From 2c9bceced3a7105665488085072bb7979cdc5257 Mon Sep 17 00:00:00 2001 From: Manikanta Pubbisetty Date: Tue, 20 Oct 2015 12:11:40 +0530 Subject: ath10k: consolidate if statements in ath10k_wmi_event_mgmt_rx This patch replaces multiple if conditional checks with a single if condition in WMI management rx handler. Found during code review. Signed-off-by: Manikanta Pubbisetty Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 7569db0f69b5..9e93ba3e916f 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2204,22 +2204,9 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) ath10k_dbg(ar, ATH10K_DBG_MGMT, "event mgmt rx status %08x\n", rx_status); - if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) { - dev_kfree_skb(skb); - return 0; - } - - if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) { - dev_kfree_skb(skb); - return 0; - } - - if (rx_status & WMI_RX_STATUS_ERR_KEY_CACHE_MISS) { - dev_kfree_skb(skb); - return 0; - } - - if (rx_status & WMI_RX_STATUS_ERR_CRC) { + if ((test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) || + (rx_status & (WMI_RX_STATUS_ERR_DECRYPT | + WMI_RX_STATUS_ERR_KEY_CACHE_MISS | WMI_RX_STATUS_ERR_CRC))) { dev_kfree_skb(skb); return 0; } -- cgit v1.2.3-59-g8ed1b From 3fab30f7e8f31a06702ee6b03a902caffa5bc724 Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Thu, 29 Oct 2015 14:27:37 +0200 Subject: ath10k: add abstraction layer for peer flags Abstraction layer for peer flags is added to fix ABI breakage. Signed-off-by: Tamizh chelvam Signed-off-by: SenthilKumar Jegadeesan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 + drivers/net/wireless/ath/ath10k/mac.c | 33 ++++++------ drivers/net/wireless/ath/ath10k/wmi-tlv.c | 19 +++++++ drivers/net/wireless/ath/ath10k/wmi-tlv.h | 18 +++++++ drivers/net/wireless/ath/ath10k/wmi.c | 60 +++++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.h | 88 +++++++++++++++++++++++++------ 6 files changed, 188 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 4a2301589902..01a417302149 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -151,6 +151,7 @@ struct ath10k_wmi { struct wmi_vdev_param_map *vdev_param; struct wmi_pdev_param_map *pdev_param; const struct wmi_ops *ops; + const struct wmi_peer_flags_map *peer_flags; u32 num_mem_chunks; u32 rx_decap_mode; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index a7411fe90cc4..2ccda0ed91fa 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1960,7 +1960,7 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar, ether_addr_copy(arg->addr, sta->addr); arg->vdev_id = arvif->vdev_id; arg->peer_aid = aid; - arg->peer_flags |= WMI_PEER_AUTH; + arg->peer_flags |= arvif->ar->wmi.peer_flags->auth; arg->peer_listen_intval = ath10k_peer_assoc_h_listen_intval(ar, vif); arg->peer_num_spatial_streams = 1; arg->peer_caps = vif->bss_conf.assoc_capability; @@ -2002,12 +2002,12 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar, /* FIXME: base on RSN IE/WPA IE is a correct idea? */ if (rsnie || wpaie) { ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__); - arg->peer_flags |= WMI_PEER_NEED_PTK_4_WAY; + arg->peer_flags |= ar->wmi.peer_flags->need_ptk_4_way; } if (wpaie) { ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__); - arg->peer_flags |= WMI_PEER_NEED_GTK_2_WAY; + arg->peer_flags |= ar->wmi.peer_flags->need_gtk_2_way; } } @@ -2104,7 +2104,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, ath10k_peer_assoc_h_vht_masked(vht_mcs_mask)) return; - arg->peer_flags |= WMI_PEER_HT; + arg->peer_flags |= ar->wmi.peer_flags->ht; arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + ht_cap->ampdu_factor)) - 1; @@ -2115,10 +2115,10 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, arg->peer_rate_caps |= WMI_RC_HT_FLAG; if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) - arg->peer_flags |= WMI_PEER_LDPC; + arg->peer_flags |= ar->wmi.peer_flags->ldbc; if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) { - arg->peer_flags |= WMI_PEER_40MHZ; + arg->peer_flags |= ar->wmi.peer_flags->bw40; arg->peer_rate_caps |= WMI_RC_CW40_FLAG; } @@ -2132,7 +2132,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) { arg->peer_rate_caps |= WMI_RC_TX_STBC_FLAG; - arg->peer_flags |= WMI_PEER_STBC; + arg->peer_flags |= ar->wmi.peer_flags->stbc; } if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) { @@ -2140,7 +2140,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT; stbc = stbc << WMI_RC_RX_STBC_FLAG_S; arg->peer_rate_caps |= stbc; - arg->peer_flags |= WMI_PEER_STBC; + arg->peer_flags |= ar->wmi.peer_flags->stbc; } if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2]) @@ -2321,10 +2321,10 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, if (ath10k_peer_assoc_h_vht_masked(vht_mcs_mask)) return; - arg->peer_flags |= WMI_PEER_VHT; + arg->peer_flags |= ar->wmi.peer_flags->vht; if (def.chan->band == IEEE80211_BAND_2GHZ) - arg->peer_flags |= WMI_PEER_VHT_2G; + arg->peer_flags |= ar->wmi.peer_flags->vht_2g; arg->peer_vht_caps = vht_cap->cap; @@ -2341,7 +2341,7 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, ampdu_factor)) - 1); if (sta->bandwidth == IEEE80211_STA_RX_BW_80) - arg->peer_flags |= WMI_PEER_80MHZ; + arg->peer_flags |= ar->wmi.peer_flags->bw80; arg->peer_vht_rates.rx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.rx_highest); @@ -2366,27 +2366,28 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar, switch (arvif->vdev_type) { case WMI_VDEV_TYPE_AP: if (sta->wme) - arg->peer_flags |= WMI_PEER_QOS; + arg->peer_flags |= arvif->ar->wmi.peer_flags->qos; if (sta->wme && sta->uapsd_queues) { - arg->peer_flags |= WMI_PEER_APSD; + arg->peer_flags |= arvif->ar->wmi.peer_flags->apsd; arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG; } break; case WMI_VDEV_TYPE_STA: if (vif->bss_conf.qos) - arg->peer_flags |= WMI_PEER_QOS; + arg->peer_flags |= arvif->ar->wmi.peer_flags->qos; break; case WMI_VDEV_TYPE_IBSS: if (sta->wme) - arg->peer_flags |= WMI_PEER_QOS; + arg->peer_flags |= arvif->ar->wmi.peer_flags->qos; break; default: break; } ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM qos %d\n", - sta->addr, !!(arg->peer_flags & WMI_PEER_QOS)); + sta->addr, !!(arg->peer_flags & + arvif->ar->wmi.peer_flags->qos)); } static bool ath10k_mac_sta_has_ofdm_only(struct ieee80211_sta *sta) diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 6fbd17b69469..3b3a27b859f3 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -3485,6 +3485,24 @@ static const struct wmi_ops wmi_tlv_ops = { .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill, }; +static const struct wmi_peer_flags_map wmi_tlv_peer_flags_map = { + .auth = WMI_TLV_PEER_AUTH, + .qos = WMI_TLV_PEER_QOS, + .need_ptk_4_way = WMI_TLV_PEER_NEED_PTK_4_WAY, + .need_gtk_2_way = WMI_TLV_PEER_NEED_GTK_2_WAY, + .apsd = WMI_TLV_PEER_APSD, + .ht = WMI_TLV_PEER_HT, + .bw40 = WMI_TLV_PEER_40MHZ, + .stbc = WMI_TLV_PEER_STBC, + .ldbc = WMI_TLV_PEER_LDPC, + .dyn_mimops = WMI_TLV_PEER_DYN_MIMOPS, + .static_mimops = WMI_TLV_PEER_STATIC_MIMOPS, + .spatial_mux = WMI_TLV_PEER_SPATIAL_MUX, + .vht = WMI_TLV_PEER_VHT, + .bw80 = WMI_TLV_PEER_80MHZ, + .pmf = WMI_TLV_PEER_PMF, +}; + /************/ /* TLV init */ /************/ @@ -3495,4 +3513,5 @@ void ath10k_wmi_tlv_attach(struct ath10k *ar) ar->wmi.vdev_param = &wmi_tlv_vdev_param_map; ar->wmi.pdev_param = &wmi_tlv_pdev_param_map; ar->wmi.ops = &wmi_tlv_ops; + ar->wmi.peer_flags = &wmi_tlv_peer_flags_map; } diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index ad655c44afdb..dd678590531a 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -527,6 +527,24 @@ enum wmi_tlv_vdev_param { WMI_TLV_VDEV_PARAM_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE, }; +enum wmi_tlv_peer_flags { + WMI_TLV_PEER_AUTH = 0x00000001, + WMI_TLV_PEER_QOS = 0x00000002, + WMI_TLV_PEER_NEED_PTK_4_WAY = 0x00000004, + WMI_TLV_PEER_NEED_GTK_2_WAY = 0x00000010, + WMI_TLV_PEER_APSD = 0x00000800, + WMI_TLV_PEER_HT = 0x00001000, + WMI_TLV_PEER_40MHZ = 0x00002000, + WMI_TLV_PEER_STBC = 0x00008000, + WMI_TLV_PEER_LDPC = 0x00010000, + WMI_TLV_PEER_DYN_MIMOPS = 0x00020000, + WMI_TLV_PEER_STATIC_MIMOPS = 0x00040000, + WMI_TLV_PEER_SPATIAL_MUX = 0x00200000, + WMI_TLV_PEER_VHT = 0x02000000, + WMI_TLV_PEER_80MHZ = 0x04000000, + WMI_TLV_PEER_PMF = 0x08000000, +}; + enum wmi_tlv_tag { WMI_TLV_TAG_LAST_RESERVED = 15, diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 9e93ba3e916f..8cd068b5c2ad 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1546,6 +1546,61 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = { .arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR, }; +static const struct wmi_peer_flags_map wmi_peer_flags_map = { + .auth = WMI_PEER_AUTH, + .qos = WMI_PEER_QOS, + .need_ptk_4_way = WMI_PEER_NEED_PTK_4_WAY, + .need_gtk_2_way = WMI_PEER_NEED_GTK_2_WAY, + .apsd = WMI_PEER_APSD, + .ht = WMI_PEER_HT, + .bw40 = WMI_PEER_40MHZ, + .stbc = WMI_PEER_STBC, + .ldbc = WMI_PEER_LDPC, + .dyn_mimops = WMI_PEER_DYN_MIMOPS, + .static_mimops = WMI_PEER_STATIC_MIMOPS, + .spatial_mux = WMI_PEER_SPATIAL_MUX, + .vht = WMI_PEER_VHT, + .bw80 = WMI_PEER_80MHZ, + .vht_2g = WMI_PEER_VHT_2G, + .pmf = WMI_PEER_PMF, +}; + +static const struct wmi_peer_flags_map wmi_10x_peer_flags_map = { + .auth = WMI_10X_PEER_AUTH, + .qos = WMI_10X_PEER_QOS, + .need_ptk_4_way = WMI_10X_PEER_NEED_PTK_4_WAY, + .need_gtk_2_way = WMI_10X_PEER_NEED_GTK_2_WAY, + .apsd = WMI_10X_PEER_APSD, + .ht = WMI_10X_PEER_HT, + .bw40 = WMI_10X_PEER_40MHZ, + .stbc = WMI_10X_PEER_STBC, + .ldbc = WMI_10X_PEER_LDPC, + .dyn_mimops = WMI_10X_PEER_DYN_MIMOPS, + .static_mimops = WMI_10X_PEER_STATIC_MIMOPS, + .spatial_mux = WMI_10X_PEER_SPATIAL_MUX, + .vht = WMI_10X_PEER_VHT, + .bw80 = WMI_10X_PEER_80MHZ, +}; + +static const struct wmi_peer_flags_map wmi_10_2_peer_flags_map = { + .auth = WMI_10_2_PEER_AUTH, + .qos = WMI_10_2_PEER_QOS, + .need_ptk_4_way = WMI_10_2_PEER_NEED_PTK_4_WAY, + .need_gtk_2_way = WMI_10_2_PEER_NEED_GTK_2_WAY, + .apsd = WMI_10_2_PEER_APSD, + .ht = WMI_10_2_PEER_HT, + .bw40 = WMI_10_2_PEER_40MHZ, + .stbc = WMI_10_2_PEER_STBC, + .ldbc = WMI_10_2_PEER_LDPC, + .dyn_mimops = WMI_10_2_PEER_DYN_MIMOPS, + .static_mimops = WMI_10_2_PEER_STATIC_MIMOPS, + .spatial_mux = WMI_10_2_PEER_SPATIAL_MUX, + .vht = WMI_10_2_PEER_VHT, + .bw80 = WMI_10_2_PEER_80MHZ, + .vht_2g = WMI_10_2_PEER_VHT_2G, + .pmf = WMI_10_2_PEER_PMF, +}; + void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch, const struct wmi_channel_arg *arg) { @@ -7554,30 +7609,35 @@ int ath10k_wmi_attach(struct ath10k *ar) ar->wmi.cmd = &wmi_10_4_cmd_map; ar->wmi.vdev_param = &wmi_10_4_vdev_param_map; ar->wmi.pdev_param = &wmi_10_4_pdev_param_map; + ar->wmi.peer_flags = &wmi_10_2_peer_flags_map; break; case ATH10K_FW_WMI_OP_VERSION_10_2_4: ar->wmi.cmd = &wmi_10_2_4_cmd_map; ar->wmi.ops = &wmi_10_2_4_ops; ar->wmi.vdev_param = &wmi_10_2_4_vdev_param_map; ar->wmi.pdev_param = &wmi_10_2_4_pdev_param_map; + ar->wmi.peer_flags = &wmi_10_2_peer_flags_map; break; case ATH10K_FW_WMI_OP_VERSION_10_2: ar->wmi.cmd = &wmi_10_2_cmd_map; ar->wmi.ops = &wmi_10_2_ops; ar->wmi.vdev_param = &wmi_10x_vdev_param_map; ar->wmi.pdev_param = &wmi_10x_pdev_param_map; + ar->wmi.peer_flags = &wmi_10_2_peer_flags_map; break; case ATH10K_FW_WMI_OP_VERSION_10_1: ar->wmi.cmd = &wmi_10x_cmd_map; ar->wmi.ops = &wmi_10_1_ops; ar->wmi.vdev_param = &wmi_10x_vdev_param_map; ar->wmi.pdev_param = &wmi_10x_pdev_param_map; + ar->wmi.peer_flags = &wmi_10x_peer_flags_map; break; case ATH10K_FW_WMI_OP_VERSION_MAIN: ar->wmi.cmd = &wmi_cmd_map; ar->wmi.ops = &wmi_ops; ar->wmi.vdev_param = &wmi_vdev_param_map; ar->wmi.pdev_param = &wmi_pdev_param_map; + ar->wmi.peer_flags = &wmi_peer_flags_map; break; case ATH10K_FW_WMI_OP_VERSION_TLV: ath10k_wmi_tlv_attach(ar); diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 72a4ef709577..96dd98c6fcef 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -5641,21 +5641,79 @@ struct wmi_peer_set_q_empty_callback_cmd { __le32 callback_enable; } __packed; -#define WMI_PEER_AUTH 0x00000001 -#define WMI_PEER_QOS 0x00000002 -#define WMI_PEER_NEED_PTK_4_WAY 0x00000004 -#define WMI_PEER_NEED_GTK_2_WAY 0x00000010 -#define WMI_PEER_APSD 0x00000800 -#define WMI_PEER_HT 0x00001000 -#define WMI_PEER_40MHZ 0x00002000 -#define WMI_PEER_STBC 0x00008000 -#define WMI_PEER_LDPC 0x00010000 -#define WMI_PEER_DYN_MIMOPS 0x00020000 -#define WMI_PEER_STATIC_MIMOPS 0x00040000 -#define WMI_PEER_SPATIAL_MUX 0x00200000 -#define WMI_PEER_VHT 0x02000000 -#define WMI_PEER_80MHZ 0x04000000 -#define WMI_PEER_VHT_2G 0x08000000 +struct wmi_peer_flags_map { + u32 auth; + u32 qos; + u32 need_ptk_4_way; + u32 need_gtk_2_way; + u32 apsd; + u32 ht; + u32 bw40; + u32 stbc; + u32 ldbc; + u32 dyn_mimops; + u32 static_mimops; + u32 spatial_mux; + u32 vht; + u32 bw80; + u32 vht_2g; + u32 pmf; +}; + +enum wmi_peer_flags { + WMI_PEER_AUTH = 0x00000001, + WMI_PEER_QOS = 0x00000002, + WMI_PEER_NEED_PTK_4_WAY = 0x00000004, + WMI_PEER_NEED_GTK_2_WAY = 0x00000010, + WMI_PEER_APSD = 0x00000800, + WMI_PEER_HT = 0x00001000, + WMI_PEER_40MHZ = 0x00002000, + WMI_PEER_STBC = 0x00008000, + WMI_PEER_LDPC = 0x00010000, + WMI_PEER_DYN_MIMOPS = 0x00020000, + WMI_PEER_STATIC_MIMOPS = 0x00040000, + WMI_PEER_SPATIAL_MUX = 0x00200000, + WMI_PEER_VHT = 0x02000000, + WMI_PEER_80MHZ = 0x04000000, + WMI_PEER_VHT_2G = 0x08000000, + WMI_PEER_PMF = 0x10000000, +}; + +enum wmi_10x_peer_flags { + WMI_10X_PEER_AUTH = 0x00000001, + WMI_10X_PEER_QOS = 0x00000002, + WMI_10X_PEER_NEED_PTK_4_WAY = 0x00000004, + WMI_10X_PEER_NEED_GTK_2_WAY = 0x00000010, + WMI_10X_PEER_APSD = 0x00000800, + WMI_10X_PEER_HT = 0x00001000, + WMI_10X_PEER_40MHZ = 0x00002000, + WMI_10X_PEER_STBC = 0x00008000, + WMI_10X_PEER_LDPC = 0x00010000, + WMI_10X_PEER_DYN_MIMOPS = 0x00020000, + WMI_10X_PEER_STATIC_MIMOPS = 0x00040000, + WMI_10X_PEER_SPATIAL_MUX = 0x00200000, + WMI_10X_PEER_VHT = 0x02000000, + WMI_10X_PEER_80MHZ = 0x04000000, +}; + +enum wmi_10_2_peer_flags { + WMI_10_2_PEER_AUTH = 0x00000001, + WMI_10_2_PEER_QOS = 0x00000002, + WMI_10_2_PEER_NEED_PTK_4_WAY = 0x00000004, + WMI_10_2_PEER_NEED_GTK_2_WAY = 0x00000010, + WMI_10_2_PEER_APSD = 0x00000800, + WMI_10_2_PEER_HT = 0x00001000, + WMI_10_2_PEER_40MHZ = 0x00002000, + WMI_10_2_PEER_STBC = 0x00008000, + WMI_10_2_PEER_LDPC = 0x00010000, + WMI_10_2_PEER_DYN_MIMOPS = 0x00020000, + WMI_10_2_PEER_STATIC_MIMOPS = 0x00040000, + WMI_10_2_PEER_SPATIAL_MUX = 0x00200000, + WMI_10_2_PEER_VHT = 0x02000000, + WMI_10_2_PEER_80MHZ = 0x04000000, + WMI_10_2_PEER_VHT_2G = 0x08000000, + WMI_10_2_PEER_PMF = 0x10000000, +}; /* * Peer rate capabilities. -- cgit v1.2.3-59-g8ed1b From 90eceb3b5fb0d0f413f475165314d4578c3a46c4 Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Thu, 29 Oct 2015 14:27:42 +0200 Subject: ath10k: set peer MFP flag in peer assoc command Set peer's management frame protection flag in peer assoc command, this setting will enable/disable encrytion of management frames in fw. Setting of this flag is based on whether MFP is enabled/disabled at STA and a firmware feature flag ATH10K_FW_FEATURE_MFP_SUPPORT. This is because only firmwares 10.1.561 and above have support for MFP. Signed-off-by: Tamizh chelvam Signed-off-by: Manikanta pubbisetty Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 1 + drivers/net/wireless/ath/ath10k/core.h | 3 +++ drivers/net/wireless/ath/ath10k/htt_tx.c | 7 +++++++ drivers/net/wireless/ath/ath10k/mac.c | 8 +++++++- 4 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index aa9bd92ac4ed..dc4fc4e8d5ef 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -167,6 +167,7 @@ static const char *const ath10k_core_fw_feature_str[] = { [ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT] = "skip-clock-init", [ATH10K_FW_FEATURE_RAW_MODE_SUPPORT] = "raw-mode", [ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA] = "adaptive-cca", + [ATH10K_FW_FEATURE_MFP_SUPPORT] = "mfp", }; static unsigned int ath10k_core_get_fw_feature_str(char *buf, diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 01a417302149..c26f84ee80ea 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -513,6 +513,9 @@ enum ath10k_fw_features { /* Firmware Supports Adaptive CCA*/ ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA = 11, + /* Firmware supports management frame protection */ + ATH10K_FW_FEATURE_MFP_SUPPORT = 12, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 16823970dbfd..bd8f264ed8cd 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -477,6 +477,13 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) msdu_id = res; + if ((ieee80211_is_action(hdr->frame_control) || + ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control)) && + ieee80211_has_protected(hdr->frame_control)) { + skb_put(msdu, IEEE80211_CCMP_MIC_LEN); + } + txdesc = ath10k_htc_alloc_skb(ar, len); if (!txdesc) { res = -ENOMEM; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 2ccda0ed91fa..a53e213387a2 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1968,6 +1968,7 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar, static void ath10k_peer_assoc_h_crypto(struct ath10k *ar, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct wmi_peer_assoc_complete_arg *arg) { struct ieee80211_bss_conf *info = &vif->bss_conf; @@ -2009,6 +2010,11 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar, ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__); arg->peer_flags |= ar->wmi.peer_flags->need_gtk_2_way; } + + if (sta->mfp && + test_bit(ATH10K_FW_FEATURE_MFP_SUPPORT, ar->fw_features)) { + arg->peer_flags |= ar->wmi.peer_flags->pmf; + } } static void ath10k_peer_assoc_h_rates(struct ath10k *ar, @@ -2480,7 +2486,7 @@ static int ath10k_peer_assoc_prepare(struct ath10k *ar, memset(arg, 0, sizeof(*arg)); ath10k_peer_assoc_h_basic(ar, vif, sta, arg); - ath10k_peer_assoc_h_crypto(ar, vif, arg); + ath10k_peer_assoc_h_crypto(ar, vif, sta, arg); ath10k_peer_assoc_h_rates(ar, vif, sta, arg); ath10k_peer_assoc_h_ht(ar, vif, sta, arg); ath10k_peer_assoc_h_vht(ar, vif, sta, arg); -- cgit v1.2.3-59-g8ed1b From 6dd46348b935043d8748ad39ef7e9275b0c53c47 Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Sat, 31 Oct 2015 11:20:32 +0200 Subject: ath10k: add thermal throttling support for 10.4 firmware This patch enables thermal throttling support for 10.4 firmware. Signed-off-by: Tamizh chelvam Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/thermal.c | 2 +- drivers/net/wireless/ath/ath10k/wmi.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c index 60fe562e3041..444b52c7e4f3 100644 --- a/drivers/net/wireless/ath/ath10k/thermal.c +++ b/drivers/net/wireless/ath/ath10k/thermal.c @@ -187,7 +187,7 @@ int ath10k_thermal_register(struct ath10k *ar) /* Do not register hwmon device when temperature reading is not * supported by firmware */ - if (ar->wmi.op_version != ATH10K_FW_WMI_OP_VERSION_10_2_4) + if (!(ar->wmi.ops->gen_pdev_get_temperature)) return 0; /* Avoid linking error on devm_hwmon_device_register_with_groups, I diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 8cd068b5c2ad..e3ce8f09f110 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -5103,6 +5103,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb) case WMI_10_4_UPDATE_STATS_EVENTID: ath10k_wmi_event_update_stats(ar, skb); break; + case WMI_10_4_PDEV_TEMPERATURE_EVENTID: + ath10k_wmi_event_temperature(ar, skb); + break; default: ath10k_warn(ar, "Unknown eventid: %d\n", id); break; @@ -7599,6 +7602,7 @@ static const struct wmi_ops wmi_10_4_ops = { /* shared with 10.2 */ .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc, .gen_request_stats = ath10k_wmi_op_gen_request_stats, + .gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature, }; int ath10k_wmi_attach(struct ath10k *ar) -- cgit v1.2.3-59-g8ed1b From e3c6225d3a2aae18b1e4bf7356660786a6595982 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 29 Oct 2015 19:11:32 +0530 Subject: ath10k: add new service defines for 10.4 No functional changes, adds new wmi service bits for 10.4 firmware to be sync with 10.4 firmware. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 96dd98c6fcef..6fb4961c1a04 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -175,6 +175,8 @@ enum wmi_service { WMI_SERVICE_AUX_SPECTRAL_INTF, WMI_SERVICE_AUX_CHAN_LOAD_INTF, WMI_SERVICE_BSS_CHANNEL_INFO_64, + WMI_SERVICE_EXT_RES_CFG_SUPPORT, + WMI_SERVICE_MESH, /* keep last */ WMI_SERVICE_MAX, @@ -286,6 +288,8 @@ enum wmi_10_4_service { WMI_10_4_SERVICE_AUX_SPECTRAL_INTF, WMI_10_4_SERVICE_AUX_CHAN_LOAD_INTF, WMI_10_4_SERVICE_BSS_CHANNEL_INFO_64, + WMI_10_4_SERVICE_EXT_RES_CFG_SUPPORT, + WMI_10_4_SERVICE_MESH, }; static inline char *wmi_service_name(int service_id) @@ -375,6 +379,8 @@ static inline char *wmi_service_name(int service_id) SVCSTR(WMI_SERVICE_AUX_SPECTRAL_INTF); SVCSTR(WMI_SERVICE_AUX_CHAN_LOAD_INTF); SVCSTR(WMI_SERVICE_BSS_CHANNEL_INFO_64); + SVCSTR(WMI_SERVICE_EXT_RES_CFG_SUPPORT); + SVCSTR(WMI_SERVICE_MESH); default: return NULL; } @@ -600,6 +606,10 @@ static inline void wmi_10_4_svc_map(const __le32 *in, unsigned long *out, WMI_SERVICE_AUX_CHAN_LOAD_INTF, len); SVCMAP(WMI_10_4_SERVICE_BSS_CHANNEL_INFO_64, WMI_SERVICE_BSS_CHANNEL_INFO_64, len); + SVCMAP(WMI_10_4_SERVICE_EXT_RES_CFG_SUPPORT, + WMI_SERVICE_EXT_RES_CFG_SUPPORT, len); + SVCMAP(WMI_10_4_SERVICE_MESH, + WMI_SERVICE_MESH, len); } #undef SVCMAP -- cgit v1.2.3-59-g8ed1b From 69d4315cc255b21c913f24662383dc8f8fc0718c Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 29 Oct 2015 19:11:33 +0530 Subject: ath10k: add new WMI cmd/event defines for 10.4 No real functionality change, add WMI command/event defines to be in sync with 10.4 firmware. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 6fb4961c1a04..9e8f7d074aaf 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -1586,6 +1586,9 @@ enum wmi_10_4_cmd_id { WMI_10_4_MU_CAL_START_CMDID, WMI_10_4_SET_CCA_PARAMS_CMDID, WMI_10_4_PDEV_BSS_CHAN_INFO_REQUEST_CMDID, + WMI_10_4_EXT_RESOURCE_CFG_CMDID, + WMI_10_4_VDEV_SET_IE_CMDID, + WMI_10_4_SET_LTEU_CONFIG_CMDID, WMI_10_4_PDEV_UTF_CMDID = WMI_10_4_END_CMDID - 1, }; @@ -1648,6 +1651,7 @@ enum wmi_10_4_event_id { WMI_10_4_PDEV_TEMPERATURE_EVENTID, WMI_10_4_PDEV_NFCAL_POWER_ALL_CHANNELS_EVENTID, WMI_10_4_PDEV_BSS_CHAN_INFO_EVENTID, + WMI_10_4_MU_REPORT_EVENTID, WMI_10_4_PDEV_UTF_EVENTID = WMI_10_4_END_EVENTID - 1, }; -- cgit v1.2.3-59-g8ed1b From 52e8ce133e955aa7ff996247a4a276bee55ac046 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 29 Oct 2015 19:11:34 +0530 Subject: ath10k: add new pdev params defines to 10.4 No functionality change, just sync to the latest pdev params that 10.4 firmware defines. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 9e8f7d074aaf..a35c91e8cc76 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -3664,6 +3664,12 @@ enum wmi_10_4_pdev_param { WMI_10_4_PDEV_PARAM_WAPI_MBSSID_OFFSET, WMI_10_4_PDEV_PARAM_ARP_SRCADDR, WMI_10_4_PDEV_PARAM_ARP_DSTADDR, + WMI_10_4_PDEV_PARAM_TXPOWER_DECR_DB, + WMI_10_4_PDEV_PARAM_RX_BATCHMODE, + WMI_10_4_PDEV_PARAM_PACKET_AGGR_DELAY, + WMI_10_4_PDEV_PARAM_ATF_OBSS_NOISE_SCH, + WMI_10_4_PDEV_PARAM_ATF_OBSS_NOISE_SCALING_FACTOR, + WMI_10_4_PDEV_PARAM_CUST_TXPOWER_SCALE, }; struct wmi_pdev_set_param_cmd { -- cgit v1.2.3-59-g8ed1b From afb0bf7f530bef214fb8db4e05502f85d72961b4 Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Fri, 30 Oct 2015 14:57:58 +0530 Subject: ath10k: add support for pktlog in QCA99X0 This patch adds pktlog support for 10.4 fw versions. Signed-off-by: Vivek Natarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt.h | 2 ++ drivers/net/wireless/ath/ath10k/htt_rx.c | 12 ++++++++++++ drivers/net/wireless/ath/ath10k/hw.h | 10 ++++++++++ drivers/net/wireless/ath/ath10k/pci.c | 11 +++++++++++ 4 files changed, 35 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 2bad50e520b5..74ccfb291fcc 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1598,5 +1598,7 @@ int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb); void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id); int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *); int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *); +void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar, + struct sk_buff *skb); #endif diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 6060dda4e910..d1dc1ba2435e 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2127,6 +2127,18 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) } EXPORT_SYMBOL(ath10k_htt_t2h_msg_handler); +void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar, + struct sk_buff *skb) +{ + struct ath10k_pktlog_10_4_hdr *hdr = + (struct ath10k_pktlog_10_4_hdr *)skb->data; + + trace_ath10k_htt_pktlog(ar, hdr->payload, + sizeof(*hdr) + __le16_to_cpu(hdr->size)); + dev_kfree_skb_any(skb); +} +EXPORT_SYMBOL(ath10k_htt_rx_pktlog_completion_handler); + static void ath10k_htt_txrx_compl_task(unsigned long ptr) { struct ath10k_htt *htt = (struct ath10k_htt *)ptr; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 39966a05c1cc..557d8d2d06aa 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -273,6 +273,16 @@ struct ath10k_pktlog_hdr { u8 payload[0]; } __packed; +struct ath10k_pktlog_10_4_hdr { + __le16 flags; + __le16 missed_cnt; + __le16 log_type; + __le16 size; + __le32 timestamp; + __le32 type_specific_data; + u8 payload[0]; +} __packed; + enum ath10k_hw_rate_ofdm { ATH10K_HW_RATE_OFDM_48M = 0, ATH10K_HW_RATE_OFDM_24M, diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 3fca200b986c..5c91a673cf80 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -111,6 +111,7 @@ static void ath10k_pci_htc_tx_cb(struct ath10k_ce_pipe *ce_state); static void ath10k_pci_htc_rx_cb(struct ath10k_ce_pipe *ce_state); static void ath10k_pci_htt_tx_cb(struct ath10k_ce_pipe *ce_state); static void ath10k_pci_htt_rx_cb(struct ath10k_ce_pipe *ce_state); +static void ath10k_pci_pktlog_rx_cb(struct ath10k_ce_pipe *ce_state); static const struct ce_attr host_ce_config_wlan[] = { /* CE0: host->target HTC control and raw streams */ @@ -189,6 +190,7 @@ static const struct ce_attr host_ce_config_wlan[] = { .src_nentries = 0, .src_sz_max = 2048, .dest_nentries = 128, + .recv_cb = ath10k_pci_pktlog_rx_cb, }, /* CE9 target autonomous qcache memcpy */ @@ -1208,6 +1210,15 @@ static void ath10k_pci_htc_rx_cb(struct ath10k_ce_pipe *ce_state) ath10k_pci_process_rx_cb(ce_state, ath10k_htc_rx_completion_handler); } +/* Called by lower (CE) layer when data is received from the Target. + * Only 10.4 firmware uses separate CE to transfer pktlog data. + */ +static void ath10k_pci_pktlog_rx_cb(struct ath10k_ce_pipe *ce_state) +{ + ath10k_pci_process_rx_cb(ce_state, + ath10k_htt_rx_pktlog_completion_handler); +} + /* Called by lower (CE) layer when a send to HTT Target completes. */ static void ath10k_pci_htt_tx_cb(struct ath10k_ce_pipe *ce_state) { -- cgit v1.2.3-59-g8ed1b From 844fa57227124c353049df02de809b3d6c9505e8 Mon Sep 17 00:00:00 2001 From: Yanbo Li Date: Sat, 31 Oct 2015 11:07:21 +0200 Subject: ath10k: debugfs file to enable Bluetooth coexistence feature As not all QCA98XX radios are not connected to Bluetooth modules, enabling the BT coex feature in firmware will have side effects if the radio's GPIO are connected with other (non-BT) HW modules. Add debugfs file to control the firmware BT coex logic and set the feature as disable by default to avoid that btcoex is accidentally enabled. To enable this feature, execute: echo 1 > /sys/kernel/debug/ieee80211/phyX/ath10k/btcoex To disable: echo 0 > /sys/kernel/debug/ieee80211/phyX/ath10k/btcoex The firmware support this feature since 10.2.4.54 on 2G-only board, dual band or 5G boards don't support this. The feature's name is WMI_SERVICE_COEX_GPIO and the btcoex file is not created if firmware doesn't support it. Signed-off-by: Yanbo Li [kvalo@qca.qualcomm.com: use btcoex filename and other smaller fixes] Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 3 ++ drivers/net/wireless/ath/ath10k/debug.c | 66 +++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.c | 5 ++- 3 files changed, 73 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index c26f84ee80ea..c16f3484dc8a 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -538,6 +538,9 @@ enum ath10k_dev_flags { /* Disable HW crypto engine */ ATH10K_FLAG_HW_CRYPTO_DISABLED, + + /* Bluetooth coexistance enabled */ + ATH10K_FLAG_BTCOEX, }; enum ath10k_cal_mode { diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 6cc1aa3449c8..145c066b5087 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -2074,6 +2074,68 @@ static const struct file_operations fops_quiet_period = { .open = simple_open }; +static ssize_t ath10k_write_btcoex(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + size_t buf_size; + bool val; + + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, ubuf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + + if (strtobool(buf, &val) != 0) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (!(test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags) ^ val)) + goto exit; + + if (val) + set_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags); + else + clear_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags); + + if (ar->state != ATH10K_STATE_ON) + goto exit; + + ath10k_info(ar, "restarting firmware due to btcoex change"); + + queue_work(ar->workqueue, &ar->restart_work); + +exit: + mutex_unlock(&ar->conf_mutex); + + return count; +} + +static ssize_t ath10k_read_btcoex(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char buf[32]; + struct ath10k *ar = file->private_data; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%d\n", + test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags)); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_btcoex = { + .read = ath10k_read_btcoex, + .write = ath10k_write_btcoex, + .open = simple_open +}; + int ath10k_debug_create(struct ath10k *ar) { ar->debug.fw_crash_data = vzalloc(sizeof(*ar->debug.fw_crash_data)); @@ -2183,6 +2245,10 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("tpc_stats", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_tpc_stats); + if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map)) + debugfs_create_file("btcoex", S_IRUGO | S_IWUSR, + ar->debug.debugfs_phy, ar, &fops_btcoex); + return 0; } diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index e3ce8f09f110..56806d94e65e 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -5476,8 +5476,11 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar) cmd = (struct wmi_init_cmd_10_2 *)buf->data; features = WMI_10_2_RX_BATCH_MODE; - if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map)) + + if (test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags) && + test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map)) features |= WMI_10_2_COEX_GPIO; + cmd->resource_config.feature_mask = __cpu_to_le32(features); memcpy(&cmd->resource_config.common, &config, sizeof(config)); -- cgit v1.2.3-59-g8ed1b From b54e16f1040adcd7341ab07ca07ee8577144e4fa Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 5 Nov 2015 11:33:58 +0530 Subject: ath10k: fix peer assoc complete WMI command for 10.4 There is an extra 4-byte member when compared to WMI 10.2 added to assoc complete command in WMI 10.4. This new member is used for 160Mhz related configuration. This WMI command mismatch between host and firmware does not cause any real issues because this new member is not used in 10.4 firmwares so far (10.4.1.00030-1). This difference in WMI command interface brings in a new wmi_ops for 10.4 gen_peer_assoc(). No noticeable functionality differences with this change can be seen with the current 10.4 firmwares, but the WMI interface has to be fixed to work with future 10.4 firmwares which may be using this new member. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 37 ++++++++++++++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/wmi.h | 5 +++++ 2 files changed, 41 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 56806d94e65e..902107934edd 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -6376,6 +6376,16 @@ ath10k_wmi_peer_assoc_fill_10_2(struct ath10k *ar, void *buf, cmd->info0 = __cpu_to_le32(info0); } +static void +ath10k_wmi_peer_assoc_fill_10_4(struct ath10k *ar, void *buf, + const struct wmi_peer_assoc_complete_arg *arg) +{ + struct wmi_10_4_peer_assoc_complete_cmd *cmd = buf; + + ath10k_wmi_peer_assoc_fill_10_2(ar, buf, arg); + cmd->peer_bw_rxnss_override = 0; +} + static int ath10k_wmi_peer_assoc_check_arg(const struct wmi_peer_assoc_complete_arg *arg) { @@ -6464,6 +6474,31 @@ ath10k_wmi_10_2_op_gen_peer_assoc(struct ath10k *ar, return skb; } +static struct sk_buff * +ath10k_wmi_10_4_op_gen_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + size_t len = sizeof(struct wmi_10_4_peer_assoc_complete_cmd); + struct sk_buff *skb; + int ret; + + ret = ath10k_wmi_peer_assoc_check_arg(arg); + if (ret) + return ERR_PTR(ret); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ath10k_wmi_peer_assoc_fill_10_4(ar, skb->data, arg); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi peer assoc vdev %d addr %pM (%s)\n", + arg->vdev_id, arg->addr, + arg->peer_reassoc ? "reassociate" : "new"); + return skb; +} + static struct sk_buff * ath10k_wmi_10_2_op_gen_pdev_get_temperature(struct ath10k *ar) { @@ -7584,6 +7619,7 @@ static const struct wmi_ops wmi_10_4_ops = { .gen_peer_delete = ath10k_wmi_op_gen_peer_delete, .gen_peer_flush = ath10k_wmi_op_gen_peer_flush, .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param, + .gen_peer_assoc = ath10k_wmi_10_4_op_gen_peer_assoc, .gen_set_psmode = ath10k_wmi_op_gen_set_psmode, .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps, .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps, @@ -7603,7 +7639,6 @@ static const struct wmi_ops wmi_10_4_ops = { .fw_stats_fill = ath10k_wmi_10_4_op_fw_stats_fill, /* shared with 10.2 */ - .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc, .gen_request_stats = ath10k_wmi_op_gen_request_stats, .gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature, }; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index a35c91e8cc76..80d3f1c1b620 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -5799,6 +5799,11 @@ struct wmi_10_2_peer_assoc_complete_cmd { __le32 info0; /* WMI_PEER_ASSOC_INFO0_ */ } __packed; +struct wmi_10_4_peer_assoc_complete_cmd { + struct wmi_10_2_peer_assoc_complete_cmd cmd; + __le32 peer_bw_rxnss_override; +} __packed; + struct wmi_peer_assoc_complete_arg { u8 addr[ETH_ALEN]; u32 vdev_id; -- cgit v1.2.3-59-g8ed1b From 8921f5f7781db8bc5f21451eb6816c2cc293c613 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 5 Nov 2015 11:33:59 +0530 Subject: ath10k: rename the helper which is used for off-channel tx Rename ath10k_mac_need_offchan_tx_work() to ath10k_mac_tx_frm_has_freq() to make it more meaningful. This helper will be used in the future change. No functionality change. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index a53e213387a2..363a99c5f83d 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3287,7 +3287,7 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, } } -static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar) +static bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar) { /* FIXME: Not really sure since when the behaviour changed. At some * point new firmware stopped requiring creation of peer entries for @@ -3295,8 +3295,8 @@ static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar) * tx credit replenishment and reliability). Assuming it's at least 3.4 * because that's when the `freq` was introduced to TX_FRM HTT command. */ - return !(ar->htt.target_version_major >= 3 && - ar->htt.target_version_minor >= 4); + return (ar->htt.target_version_major >= 3 && + ar->htt.target_version_minor >= 4); } static int ath10k_mac_tx_wmi_mgmt(struct ath10k *ar, struct sk_buff *skb) @@ -3680,7 +3680,7 @@ static void ath10k_tx(struct ieee80211_hw *hw, ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id; spin_unlock_bh(&ar->data_lock); - if (ath10k_mac_need_offchan_tx_work(ar)) { + if (!ath10k_mac_tx_frm_has_freq(ar)) { ATH10K_SKB_CB(skb)->htt.freq = 0; ATH10K_SKB_CB(skb)->htt.is_offchan = true; -- cgit v1.2.3-59-g8ed1b From d39de9919a0cded8ddb1655441cf403ad5690d6d Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 5 Nov 2015 11:34:00 +0530 Subject: ath10k: fix peerid configuration in htt tx desc for htt version < 3.4 Of a word in struct htt_data_tx_desc htt version >= 3.4 firmware uses LSB 16-bit for frequency configuration which is used for offchannel tx and MSB 16-bit is for peerid. But other firmwares using version 2.X (10.1, 10.2.2, 10.2.4 and 10.4) are using 32-bit for peerid in htt tx desc. So far no issue is found with the existing code setting peerid and freq for HTT version 2.X, this could be mainly because of 0 as frequecy (home channel) is being always passed with those firmwares. There may be issues when non-zero freq is passed with firmware using < 3.4 htt version. To be safe use target_version_major and target_version_minor along with htt-op-version before configuring peer id and freq in htt tx desc. This patch extends ath10k_mac_tx_frm_has_freq() to check for htt_op_version_tlv and uses the helper while setting peerid in htt_tx_desc. Fixes: 8d6d36243610 ("ath10k: fix offchan reliability") Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt.h | 9 +++++++-- drivers/net/wireless/ath/ath10k/htt_tx.c | 11 +++++++++-- drivers/net/wireless/ath/ath10k/mac.c | 5 +++-- drivers/net/wireless/ath/ath10k/mac.h | 1 + 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 74ccfb291fcc..2b87ed6458db 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -166,8 +166,13 @@ struct htt_data_tx_desc { __le16 len; __le16 id; __le32 frags_paddr; - __le16 peerid; - __le16 freq; + union { + __le32 peerid; + struct { + __le16 peerid; + __le16 freq; + } __packed offchan_tx; + } __packed; u8 prefetch[0]; /* start of frame, for FW classification engine */ } __packed; diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index bd8f264ed8cd..8f76b9d96486 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -688,8 +688,15 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) skb_cb->htt.txbuf->cmd_tx.len = __cpu_to_le16(msdu->len); skb_cb->htt.txbuf->cmd_tx.id = __cpu_to_le16(msdu_id); skb_cb->htt.txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr); - skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le16(HTT_INVALID_PEERID); - skb_cb->htt.txbuf->cmd_tx.freq = __cpu_to_le16(skb_cb->htt.freq); + if (ath10k_mac_tx_frm_has_freq(ar)) { + skb_cb->htt.txbuf->cmd_tx.offchan_tx.peerid = + __cpu_to_le16(HTT_INVALID_PEERID); + skb_cb->htt.txbuf->cmd_tx.offchan_tx.freq = + __cpu_to_le16(skb_cb->htt.freq); + } else { + skb_cb->htt.txbuf->cmd_tx.peerid = + __cpu_to_le32(HTT_INVALID_PEERID); + } trace_ath10k_htt_tx(ar, msdu_id, msdu->len, vdev_id, tid); ath10k_dbg(ar, ATH10K_DBG_HTT, diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 363a99c5f83d..76484a947755 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3287,7 +3287,7 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, } } -static bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar) +bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar) { /* FIXME: Not really sure since when the behaviour changed. At some * point new firmware stopped requiring creation of peer entries for @@ -3296,7 +3296,8 @@ static bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar) * because that's when the `freq` was introduced to TX_FRM HTT command. */ return (ar->htt.target_version_major >= 3 && - ar->htt.target_version_minor >= 4); + ar->htt.target_version_minor >= 4 && + ar->htt.op_version == ATH10K_FW_HTT_OP_VERSION_TLV); } static int ath10k_mac_tx_wmi_mgmt(struct ath10k *ar, struct sk_buff *skb) diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index e3cefe4c7cfd..f5048049b870 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -74,6 +74,7 @@ void ath10k_mac_tx_lock(struct ath10k *ar, int reason); void ath10k_mac_tx_unlock(struct ath10k *ar, int reason); void ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif, int reason); void ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason); +bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar); static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) { -- cgit v1.2.3-59-g8ed1b From 4b7f353b8026b43e66af475ef2266958ddcfa6eb Mon Sep 17 00:00:00 2001 From: Yanbo Li Date: Thu, 12 Nov 2015 10:36:10 -0800 Subject: ath10k: fix the wrong RX rate idx report at 11G mode The RX rate idx is not correct for 11G mode OFDM packet. Because the bitrate table start with CCK index instead of OFDM. Signed-off-by: Yanbo Li Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 2 +- drivers/net/wireless/ath/ath10k/mac.c | 5 ++++- drivers/net/wireless/ath/ath10k/mac.h | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index d1dc1ba2435e..396645b508e2 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -674,7 +674,7 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, rate &= ~RX_PPDU_START_RATE_FLAG; sband = &ar->mac.sbands[status->band]; - status->rate_idx = ath10k_mac_hw_rate_to_idx(sband, rate); + status->rate_idx = ath10k_mac_hw_rate_to_idx(sband, rate, cck); break; case HTT_RX_HT: case HTT_RX_HT_WITH_TXBF: diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 76484a947755..66378544f3c9 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -90,7 +90,7 @@ static u8 ath10k_mac_bitrate_to_rate(int bitrate) } u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, - u8 hw_rate) + u8 hw_rate, bool cck) { const struct ieee80211_rate *rate; int i; @@ -98,6 +98,9 @@ u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, for (i = 0; i < sband->n_bitrates; i++) { rate = &sband->bitrates[i]; + if (ath10k_mac_bitrate_is_cck(rate->bitrate) != cck) + continue; + if (rate->hw_value == hw_rate) return i; else if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE && diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index f5048049b870..53091588090d 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -66,7 +66,7 @@ void ath10k_mac_handle_tx_pause_vdev(struct ath10k *ar, u32 vdev_id, enum wmi_tlv_tx_pause_action action); u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, - u8 hw_rate); + u8 hw_rate, bool cck); u8 ath10k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband, u32 bitrate); -- cgit v1.2.3-59-g8ed1b From 9b1ba7b28e70a1f4066cfa3bebea24953f36feef Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Fri, 6 Nov 2015 12:50:44 +0200 Subject: wil6210: hold wil->mutex while managing vrings To prevent race when connect flow may run in parallel with the disconnect event. Scenario leading to the bug is: while running connect flow on the AP, STA sends disconnect. log follows. <7>[ 668.736269] wil6210 0000:01:00.0: wlan0: DBG[ WMI]Configure for connection CID 1 <7>[ 668.736269] wil6210 0000:01:00.0: wlan0: DBG[MISC]wil_vring_init_tx() max_mpdu_size 2048 <7>[ 668.736301] wil6210 0000:01:00.0: wlan0: DBG[MISC]wil_vring_alloc() <7>[ 668.736363] wil6210 0000:01:00.0: wlan0: DBG[MISC]vring[1024] 0xffbe8000:d962ce08 0xdb244000 <7>[ 668.736394] wil6210 0000:01:00.0: wlan0: DBG[ WMI]Head 0x00880300 -> 0x00880308 <7>[ 668.736394] wil6210 0000:01:00.0: wlan0: DBG[ WMI]WMI command 0x0821 [28] <7>[ 668.736426] DBG[ WMI]Cmd 00000000: 20 00 24 00 00 00 00 00 00 00 21 08 00 00 00 00 .$.......!..... <7>[ 668.736426] DBG[ WMI]cmd 00000000: 00 00 00 00 00 00 5f 5c 00 00 00 00 00 04 00 08 ......_\........ <7>[ 668.736457] DBG[ WMI]cmd 00000010: 01 01 00 00 00 00 00 00 00 00 ff 0f ............ <7>[ 668.736488] wil6210 0000:01:00.0: wlan0: DBG[ IRQ]Pseudo IRQ 0x00000004 <7>[ 668.736519] wil6210 0000:01:00.0: wlan0: DBG[ WMI]Handle WMI 0x1824 (reply_id 0x1821) <7>[ 668.736519] wil6210 0000:01:00.0: wlan0: DBG[ IRQ]wil6210_mask_irq_pseudo() <7>[ 668.736519] wil6210 0000:01:00.0: wlan0: DBG[ IRQ]ISR MISC 0x20000000 <7>[ 668.736551] wil6210 0000:01:00.0: wlan0: DBG[ WMI]Handle WMI 0x1003 (reply_id 0x1821) <7>[ 668.736551] wil6210 0000:01:00.0: wlan0: DBG[ WMI]Disconnect 04:ce:14:00:07:70 reason [proto 3 wmi 4] <7>[ 668.736582] wil6210 0000:01:00.0: wlan0: DBG[MISC]wil6210_disconnect() <7>[ 668.736613] wil6210 0000:01:00.0: wlan0: DBG[ IRQ]Thread IRQ <7>[ 668.736613] wil6210 0000:01:00.0: wlan0: DBG[ IRQ]Thread ISR MISC 0x20000000 <7>[ 668.736644] wil6210 0000:01:00.0: wlan0: DBG[ IRQ]MBOX event <7>[ 668.736644] wil6210 0000:01:00.0: wlan0: DBG[ WMI]Mbox head 00880330 tail 00880328 <7>[ 668.736676] wil6210 0000:01:00.0: wlan0: DBG[ WMI]Mbox evt 001a 0010 0000 00 <7>[ 668.736676] wil6210 0000:01:00.0: wlan0: DBG[ WMI]WMI event 0x1821 MID 0 @3255145 msec <7>[ 668.736707] DBG[ WMI]evt 00000000: 1a 00 10 00 00 00 00 10 00 00 21 18 69 ab 31 00 ..........!.i.1. <7>[ 668.736707] DBG[ WMI]evt 00000010: 01 01 00 00 00 00 00 00 ........ <7>[ 668.736738] wil6210 0000:01:00.0: wlan0: DBG[ WMI]queue_work -> 0 <7>[ 668.736738] wil6210 0000:01:00.0: wlan0: DBG[ WMI]wmi_recv_cmd -> 1 events queued <7>[ 668.736769] wil6210 0000:01:00.0: wlan0: DBG[ IRQ]wil6210_unmask_irq_pseudo() <7>[ 668.736832] wil6210 0000:01:00.0: wlan0: DBG[MISC]Disconnect 04:ce:14:00:07:70, CID=1, reason=3 <7>[ 668.736832] wil6210 0000:01:00.0: wlan0: DBG[MISC]wil_disconnect_cid(CID 1, status 1) <7>[ 668.736894] wil6210 0000:01:00.0: wlan0: DBG[MISC]wil_vring_fini_tx() id=1 <7>[ 668.736894] wil6210 0000:01:00.0: wlan0: DBG[MISC]free Tx vring 1 [1024] 0xffbe8000:d962ce08 0xdb244000 <7>[ 668.736957] wil6210 0000:01:00.0: wlan0: DBG[ WMI]Handle WMI 0x1821 (reply_id 0x1821) <7>[ 668.736988] wil6210 0000:01:00.0: wlan0: DBG[ WMI]Complete WMI 0x1821 <7>[ 668.737019] wil6210 0000:01:00.0: wlan0: DBG[ WMI]wmi_call(0x0821->0x1821) completed in 0 msec <3>[ 668.737019] wil6210 0000:01:00.0: wlan0: Tx config failed, status 0x01 <7>[ 668.739518] wil6210 0000:01:00.0: wlan0: DBG[MISC]wil_cfg80211_del_station(04:ce:14:00:07:70, reason=2) <7>[ 668.739550] wil6210 0000:01:00.0: wlan0: DBG[MISC]wil6210_disconnect() <7>[ 668.739550] wil6210 0000:01:00.0: wlan0: DBG[MISC]_wil6210_disconnect(bssid=04:ce:14:00:07:70, reason=2, ev-) <7>[ 668.739581] wil6210 0000:01:00.0: wlan0: DBG[MISC]Disconnect 04:ce:14:00:07:70, CID=-2, reason=2 <7>[ 668.742705] wil6210 0000:01:00.0: wlan0: DBG[MISC]free Tx vring 1 [1024] 0x (null):d962ce08 0x (null) <3>[ 668.742736] __dma_free_remap: trying to free invalid coherent area: (null) Signed-off-by: Vladimir Kondratiev Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 18 +++++++++++++----- drivers/net/wireless/ath/wil6210/txrx.c | 5 ++++- 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index bb69a5949aea..48687f128dc6 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -401,20 +401,26 @@ void wil_bcast_fini(struct wil6210_priv *wil) static void wil_connect_worker(struct work_struct *work) { - int rc; + int rc, cid, ringid; struct wil6210_priv *wil = container_of(work, struct wil6210_priv, connect_worker); struct net_device *ndev = wil_to_ndev(wil); - int cid = wil->pending_connect_cid; - int ringid = wil_find_free_vring(wil); + mutex_lock(&wil->mutex); + cid = wil->pending_connect_cid; if (cid < 0) { wil_err(wil, "No connection pending\n"); - return; + goto out; + } + ringid = wil_find_free_vring(wil); + if (ringid < 0) { + wil_err(wil, "No free vring found\n"); + goto out; } - wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); + wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n", + cid, ringid); rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0); wil->pending_connect_cid = -1; @@ -424,6 +430,8 @@ static void wil_connect_worker(struct work_struct *work) } else { wil_disconnect_cid(wil, cid, WLAN_REASON_UNSPECIFIED, true); } +out: + mutex_unlock(&wil->mutex); } int wil_priv_init(struct wil6210_priv *wil) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 3bc9bc0efbac..7887e6cfd817 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -160,6 +160,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, struct device *dev = wil_to_dev(wil); size_t sz = vring->size * sizeof(vring->va[0]); + lockdep_assert_held(&wil->mutex); if (tx) { int vring_index = vring - wil->vring_tx; @@ -749,6 +750,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__, cmd.vring_cfg.tx_sw_ring.max_mpdu_size); + lockdep_assert_held(&wil->mutex); if (vring->va) { wil_err(wil, "Tx ring [%d] already allocated\n", id); @@ -821,6 +823,7 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size) wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__, cmd.vring_cfg.tx_sw_ring.max_mpdu_size); + lockdep_assert_held(&wil->mutex); if (vring->va) { wil_err(wil, "Tx ring [%d] already allocated\n", id); @@ -872,7 +875,7 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id) struct vring *vring = &wil->vring_tx[id]; struct vring_tx_data *txdata = &wil->vring_tx_data[id]; - WARN_ON(!mutex_is_locked(&wil->mutex)); + lockdep_assert_held(&wil->mutex); if (!vring->va) return; -- cgit v1.2.3-59-g8ed1b From d4a173040a1023e9e0443ea572f4685df406cde9 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 19:49:19 +0200 Subject: adm80211: move under admtek vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 29 +- drivers/net/wireless/Makefile | 4 +- drivers/net/wireless/adm8211.c | 1993 --------------------------------- drivers/net/wireless/adm8211.h | 602 ---------- drivers/net/wireless/admtek/Kconfig | 41 + drivers/net/wireless/admtek/Makefile | 1 + drivers/net/wireless/admtek/adm8211.c | 1993 +++++++++++++++++++++++++++++++++ drivers/net/wireless/admtek/adm8211.h | 602 ++++++++++ 9 files changed, 2642 insertions(+), 2625 deletions(-) delete mode 100644 drivers/net/wireless/adm8211.c delete mode 100644 drivers/net/wireless/adm8211.h create mode 100644 drivers/net/wireless/admtek/Kconfig create mode 100644 drivers/net/wireless/admtek/Makefile create mode 100644 drivers/net/wireless/admtek/adm8211.c create mode 100644 drivers/net/wireless/admtek/adm8211.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 35fe7ae0492e..f1adb66094f1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -388,7 +388,7 @@ ADM8211 WIRELESS DRIVER L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/ S: Orphan -F: drivers/net/wireless/adm8211.* +F: drivers/net/wireless/admtek/adm8211.* ADP1653 FLASH CONTROLLER DRIVER M: Sakari Ailus diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index f9f94229bf1b..7d8f47117c4d 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -17,6 +17,8 @@ menuconfig WLAN if WLAN +source "drivers/net/wireless/admtek/Kconfig" + config PCMCIA_RAYCS tristate "Aviator/Raytheon 2.4GHz wireless support" depends on PCMCIA @@ -214,33 +216,6 @@ config USB_NET_RNDIS_WLAN If you choose to build a module, it'll be called rndis_wlan. -config ADM8211 - tristate "ADMtek ADM8211 support" - depends on MAC80211 && PCI - select CRC32 - select EEPROM_93CX6 - ---help--- - This driver is for ADM8211A, ADM8211B, and ADM8211C based cards. - These are PCI/mini-PCI/Cardbus 802.11b chips found in cards such as: - - Xterasys Cardbus XN-2411b - Blitz NetWave Point PC - TrendNet 221pc - Belkin F5D6001 - SMC 2635W - Linksys WPC11 v1 - Fiberline FL-WL-200X - 3com Office Connect (3CRSHPW796) - Corega WLPCIB-11 - SMC 2602W V2 EU - D-Link DWL-520 Revision C - - However, some of these cards have been replaced with other chips - like the RTL8180L (Xterasys Cardbus XN-2411b, Belkin F5D6001) or - the Ralink RT2400 (SMC2635W) without a model number change. - - Thanks to Infineon-ADMtek for their support of this driver. - source "drivers/net/wireless/realtek/rtl818x/Kconfig" config MAC80211_HWSIM diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 740fdd353c5d..be215f43ec1d 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -2,6 +2,8 @@ # Makefile for the Linux Wireless network device drivers. # +obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/ + obj-$(CONFIG_IPW2100) += ipw2x00/ obj-$(CONFIG_IPW2200) += ipw2x00/ @@ -35,8 +37,6 @@ obj-$(CONFIG_LIBERTAS) += libertas/ obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ -obj-$(CONFIG_ADM8211) += adm8211.o - obj-$(CONFIG_MWL8K) += mwl8k.o obj-$(CONFIG_IWLWIFI) += iwlwifi/ diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c deleted file mode 100644 index 15f057ed41ad..000000000000 --- a/drivers/net/wireless/adm8211.c +++ /dev/null @@ -1,1993 +0,0 @@ - -/* - * Linux device driver for ADMtek ADM8211 (IEEE 802.11b MAC/BBP) - * - * Copyright (c) 2003, Jouni Malinen - * Copyright (c) 2004-2007, Michael Wu - * Some parts copyright (c) 2003 by David Young - * and used with permission. - * - * Much thanks to Infineon-ADMtek for their support of this driver. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. See README and COPYING for - * more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "adm8211.h" - -MODULE_AUTHOR("Michael Wu "); -MODULE_AUTHOR("Jouni Malinen "); -MODULE_DESCRIPTION("Driver for IEEE 802.11b wireless cards based on ADMtek ADM8211"); -MODULE_SUPPORTED_DEVICE("ADM8211"); -MODULE_LICENSE("GPL"); - -static unsigned int tx_ring_size __read_mostly = 16; -static unsigned int rx_ring_size __read_mostly = 16; - -module_param(tx_ring_size, uint, 0); -module_param(rx_ring_size, uint, 0); - -static const struct pci_device_id adm8211_pci_id_table[] = { - /* ADMtek ADM8211 */ - { PCI_DEVICE(0x10B7, 0x6000) }, /* 3Com 3CRSHPW796 */ - { PCI_DEVICE(0x1200, 0x8201) }, /* ? */ - { PCI_DEVICE(0x1317, 0x8201) }, /* ADM8211A */ - { PCI_DEVICE(0x1317, 0x8211) }, /* ADM8211B/C */ - { 0 } -}; - -static struct ieee80211_rate adm8211_rates[] = { - { .bitrate = 10, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 220, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, /* XX ?? */ -}; - -static const struct ieee80211_channel adm8211_channels[] = { - { .center_freq = 2412}, - { .center_freq = 2417}, - { .center_freq = 2422}, - { .center_freq = 2427}, - { .center_freq = 2432}, - { .center_freq = 2437}, - { .center_freq = 2442}, - { .center_freq = 2447}, - { .center_freq = 2452}, - { .center_freq = 2457}, - { .center_freq = 2462}, - { .center_freq = 2467}, - { .center_freq = 2472}, - { .center_freq = 2484}, -}; - - -static void adm8211_eeprom_register_read(struct eeprom_93cx6 *eeprom) -{ - struct adm8211_priv *priv = eeprom->data; - u32 reg = ADM8211_CSR_READ(SPR); - - eeprom->reg_data_in = reg & ADM8211_SPR_SDI; - eeprom->reg_data_out = reg & ADM8211_SPR_SDO; - eeprom->reg_data_clock = reg & ADM8211_SPR_SCLK; - eeprom->reg_chip_select = reg & ADM8211_SPR_SCS; -} - -static void adm8211_eeprom_register_write(struct eeprom_93cx6 *eeprom) -{ - struct adm8211_priv *priv = eeprom->data; - u32 reg = 0x4000 | ADM8211_SPR_SRS; - - if (eeprom->reg_data_in) - reg |= ADM8211_SPR_SDI; - if (eeprom->reg_data_out) - reg |= ADM8211_SPR_SDO; - if (eeprom->reg_data_clock) - reg |= ADM8211_SPR_SCLK; - if (eeprom->reg_chip_select) - reg |= ADM8211_SPR_SCS; - - ADM8211_CSR_WRITE(SPR, reg); - ADM8211_CSR_READ(SPR); /* eeprom_delay */ -} - -static int adm8211_read_eeprom(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - unsigned int words, i; - struct ieee80211_chan_range chan_range; - u16 cr49; - struct eeprom_93cx6 eeprom = { - .data = priv, - .register_read = adm8211_eeprom_register_read, - .register_write = adm8211_eeprom_register_write - }; - - if (ADM8211_CSR_READ(CSR_TEST0) & ADM8211_CSR_TEST0_EPTYP) { - /* 256 * 16-bit = 512 bytes */ - eeprom.width = PCI_EEPROM_WIDTH_93C66; - words = 256; - } else { - /* 64 * 16-bit = 128 bytes */ - eeprom.width = PCI_EEPROM_WIDTH_93C46; - words = 64; - } - - priv->eeprom_len = words * 2; - priv->eeprom = kmalloc(priv->eeprom_len, GFP_KERNEL); - if (!priv->eeprom) - return -ENOMEM; - - eeprom_93cx6_multiread(&eeprom, 0, (__le16 *)priv->eeprom, words); - - cr49 = le16_to_cpu(priv->eeprom->cr49); - priv->rf_type = (cr49 >> 3) & 0x7; - switch (priv->rf_type) { - case ADM8211_TYPE_INTERSIL: - case ADM8211_TYPE_RFMD: - case ADM8211_TYPE_MARVEL: - case ADM8211_TYPE_AIROHA: - case ADM8211_TYPE_ADMTEK: - break; - - default: - if (priv->pdev->revision < ADM8211_REV_CA) - priv->rf_type = ADM8211_TYPE_RFMD; - else - priv->rf_type = ADM8211_TYPE_AIROHA; - - printk(KERN_WARNING "%s (adm8211): Unknown RFtype %d\n", - pci_name(priv->pdev), (cr49 >> 3) & 0x7); - } - - priv->bbp_type = cr49 & 0x7; - switch (priv->bbp_type) { - case ADM8211_TYPE_INTERSIL: - case ADM8211_TYPE_RFMD: - case ADM8211_TYPE_MARVEL: - case ADM8211_TYPE_AIROHA: - case ADM8211_TYPE_ADMTEK: - break; - default: - if (priv->pdev->revision < ADM8211_REV_CA) - priv->bbp_type = ADM8211_TYPE_RFMD; - else - priv->bbp_type = ADM8211_TYPE_ADMTEK; - - printk(KERN_WARNING "%s (adm8211): Unknown BBPtype: %d\n", - pci_name(priv->pdev), cr49 >> 3); - } - - if (priv->eeprom->country_code >= ARRAY_SIZE(cranges)) { - printk(KERN_WARNING "%s (adm8211): Invalid country code (%d)\n", - pci_name(priv->pdev), priv->eeprom->country_code); - - chan_range = cranges[2]; - } else - chan_range = cranges[priv->eeprom->country_code]; - - printk(KERN_DEBUG "%s (adm8211): Channel range: %d - %d\n", - pci_name(priv->pdev), (int)chan_range.min, (int)chan_range.max); - - BUILD_BUG_ON(sizeof(priv->channels) != sizeof(adm8211_channels)); - - memcpy(priv->channels, adm8211_channels, sizeof(priv->channels)); - priv->band.channels = priv->channels; - priv->band.n_channels = ARRAY_SIZE(adm8211_channels); - priv->band.bitrates = adm8211_rates; - priv->band.n_bitrates = ARRAY_SIZE(adm8211_rates); - - for (i = 1; i <= ARRAY_SIZE(adm8211_channels); i++) - if (i < chan_range.min || i > chan_range.max) - priv->channels[i - 1].flags |= IEEE80211_CHAN_DISABLED; - - switch (priv->eeprom->specific_bbptype) { - case ADM8211_BBP_RFMD3000: - case ADM8211_BBP_RFMD3002: - case ADM8211_BBP_ADM8011: - priv->specific_bbptype = priv->eeprom->specific_bbptype; - break; - - default: - if (priv->pdev->revision < ADM8211_REV_CA) - priv->specific_bbptype = ADM8211_BBP_RFMD3000; - else - priv->specific_bbptype = ADM8211_BBP_ADM8011; - - printk(KERN_WARNING "%s (adm8211): Unknown specific BBP: %d\n", - pci_name(priv->pdev), priv->eeprom->specific_bbptype); - } - - switch (priv->eeprom->specific_rftype) { - case ADM8211_RFMD2948: - case ADM8211_RFMD2958: - case ADM8211_RFMD2958_RF3000_CONTROL_POWER: - case ADM8211_MAX2820: - case ADM8211_AL2210L: - priv->transceiver_type = priv->eeprom->specific_rftype; - break; - - default: - if (priv->pdev->revision == ADM8211_REV_BA) - priv->transceiver_type = ADM8211_RFMD2958_RF3000_CONTROL_POWER; - else if (priv->pdev->revision == ADM8211_REV_CA) - priv->transceiver_type = ADM8211_AL2210L; - else if (priv->pdev->revision == ADM8211_REV_AB) - priv->transceiver_type = ADM8211_RFMD2948; - - printk(KERN_WARNING "%s (adm8211): Unknown transceiver: %d\n", - pci_name(priv->pdev), priv->eeprom->specific_rftype); - - break; - } - - printk(KERN_DEBUG "%s (adm8211): RFtype=%d BBPtype=%d Specific BBP=%d " - "Transceiver=%d\n", pci_name(priv->pdev), priv->rf_type, - priv->bbp_type, priv->specific_bbptype, priv->transceiver_type); - - return 0; -} - -static inline void adm8211_write_sram(struct ieee80211_hw *dev, - u32 addr, u32 data) -{ - struct adm8211_priv *priv = dev->priv; - - ADM8211_CSR_WRITE(WEPCTL, addr | ADM8211_WEPCTL_TABLE_WR | - (priv->pdev->revision < ADM8211_REV_BA ? - 0 : ADM8211_WEPCTL_SEL_WEPTABLE )); - ADM8211_CSR_READ(WEPCTL); - msleep(1); - - ADM8211_CSR_WRITE(WESK, data); - ADM8211_CSR_READ(WESK); - msleep(1); -} - -static void adm8211_write_sram_bytes(struct ieee80211_hw *dev, - unsigned int addr, u8 *buf, - unsigned int len) -{ - struct adm8211_priv *priv = dev->priv; - u32 reg = ADM8211_CSR_READ(WEPCTL); - unsigned int i; - - if (priv->pdev->revision < ADM8211_REV_BA) { - for (i = 0; i < len; i += 2) { - u16 val = buf[i] | (buf[i + 1] << 8); - adm8211_write_sram(dev, addr + i / 2, val); - } - } else { - for (i = 0; i < len; i += 4) { - u32 val = (buf[i + 0] << 0 ) | (buf[i + 1] << 8 ) | - (buf[i + 2] << 16) | (buf[i + 3] << 24); - adm8211_write_sram(dev, addr + i / 4, val); - } - } - - ADM8211_CSR_WRITE(WEPCTL, reg); -} - -static void adm8211_clear_sram(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - u32 reg = ADM8211_CSR_READ(WEPCTL); - unsigned int addr; - - for (addr = 0; addr < ADM8211_SRAM_SIZE; addr++) - adm8211_write_sram(dev, addr, 0); - - ADM8211_CSR_WRITE(WEPCTL, reg); -} - -static int adm8211_get_stats(struct ieee80211_hw *dev, - struct ieee80211_low_level_stats *stats) -{ - struct adm8211_priv *priv = dev->priv; - - memcpy(stats, &priv->stats, sizeof(*stats)); - - return 0; -} - -static void adm8211_interrupt_tci(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - unsigned int dirty_tx; - - spin_lock(&priv->lock); - - for (dirty_tx = priv->dirty_tx; priv->cur_tx - dirty_tx; dirty_tx++) { - unsigned int entry = dirty_tx % priv->tx_ring_size; - u32 status = le32_to_cpu(priv->tx_ring[entry].status); - struct ieee80211_tx_info *txi; - struct adm8211_tx_ring_info *info; - struct sk_buff *skb; - - if (status & TDES0_CONTROL_OWN || - !(status & TDES0_CONTROL_DONE)) - break; - - info = &priv->tx_buffers[entry]; - skb = info->skb; - txi = IEEE80211_SKB_CB(skb); - - /* TODO: check TDES0_STATUS_TUF and TDES0_STATUS_TRO */ - - pci_unmap_single(priv->pdev, info->mapping, - info->skb->len, PCI_DMA_TODEVICE); - - ieee80211_tx_info_clear_status(txi); - - skb_pull(skb, sizeof(struct adm8211_tx_hdr)); - memcpy(skb_push(skb, info->hdrlen), skb->cb, info->hdrlen); - if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && - !(status & TDES0_STATUS_ES)) - txi->flags |= IEEE80211_TX_STAT_ACK; - - ieee80211_tx_status_irqsafe(dev, skb); - - info->skb = NULL; - } - - if (priv->cur_tx - dirty_tx < priv->tx_ring_size - 2) - ieee80211_wake_queue(dev, 0); - - priv->dirty_tx = dirty_tx; - spin_unlock(&priv->lock); -} - - -static void adm8211_interrupt_rci(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - unsigned int entry = priv->cur_rx % priv->rx_ring_size; - u32 status; - unsigned int pktlen; - struct sk_buff *skb, *newskb; - unsigned int limit = priv->rx_ring_size; - u8 rssi, rate; - - while (!(priv->rx_ring[entry].status & cpu_to_le32(RDES0_STATUS_OWN))) { - if (!limit--) - break; - - status = le32_to_cpu(priv->rx_ring[entry].status); - rate = (status & RDES0_STATUS_RXDR) >> 12; - rssi = le32_to_cpu(priv->rx_ring[entry].length) & - RDES1_STATUS_RSSI; - - pktlen = status & RDES0_STATUS_FL; - if (pktlen > RX_PKT_SIZE) { - if (net_ratelimit()) - wiphy_debug(dev->wiphy, "frame too long (%d)\n", - pktlen); - pktlen = RX_PKT_SIZE; - } - - if (!priv->soft_rx_crc && status & RDES0_STATUS_ES) { - skb = NULL; /* old buffer will be reused */ - /* TODO: update RX error stats */ - /* TODO: check RDES0_STATUS_CRC*E */ - } else if (pktlen < RX_COPY_BREAK) { - skb = dev_alloc_skb(pktlen); - if (skb) { - pci_dma_sync_single_for_cpu( - priv->pdev, - priv->rx_buffers[entry].mapping, - pktlen, PCI_DMA_FROMDEVICE); - memcpy(skb_put(skb, pktlen), - skb_tail_pointer(priv->rx_buffers[entry].skb), - pktlen); - pci_dma_sync_single_for_device( - priv->pdev, - priv->rx_buffers[entry].mapping, - RX_PKT_SIZE, PCI_DMA_FROMDEVICE); - } - } else { - newskb = dev_alloc_skb(RX_PKT_SIZE); - if (newskb) { - skb = priv->rx_buffers[entry].skb; - skb_put(skb, pktlen); - pci_unmap_single( - priv->pdev, - priv->rx_buffers[entry].mapping, - RX_PKT_SIZE, PCI_DMA_FROMDEVICE); - priv->rx_buffers[entry].skb = newskb; - priv->rx_buffers[entry].mapping = - pci_map_single(priv->pdev, - skb_tail_pointer(newskb), - RX_PKT_SIZE, - PCI_DMA_FROMDEVICE); - } else { - skb = NULL; - /* TODO: update rx dropped stats */ - } - - priv->rx_ring[entry].buffer1 = - cpu_to_le32(priv->rx_buffers[entry].mapping); - } - - priv->rx_ring[entry].status = cpu_to_le32(RDES0_STATUS_OWN | - RDES0_STATUS_SQL); - priv->rx_ring[entry].length = - cpu_to_le32(RX_PKT_SIZE | - (entry == priv->rx_ring_size - 1 ? - RDES1_CONTROL_RER : 0)); - - if (skb) { - struct ieee80211_rx_status rx_status = {0}; - - if (priv->pdev->revision < ADM8211_REV_CA) - rx_status.signal = rssi; - else - rx_status.signal = 100 - rssi; - - rx_status.rate_idx = rate; - - rx_status.freq = adm8211_channels[priv->channel - 1].center_freq; - rx_status.band = IEEE80211_BAND_2GHZ; - - memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); - ieee80211_rx_irqsafe(dev, skb); - } - - entry = (++priv->cur_rx) % priv->rx_ring_size; - } - - /* TODO: check LPC and update stats? */ -} - - -static irqreturn_t adm8211_interrupt(int irq, void *dev_id) -{ -#define ADM8211_INT(x) \ -do { \ - if (unlikely(stsr & ADM8211_STSR_ ## x)) \ - wiphy_debug(dev->wiphy, "%s\n", #x); \ -} while (0) - - struct ieee80211_hw *dev = dev_id; - struct adm8211_priv *priv = dev->priv; - u32 stsr = ADM8211_CSR_READ(STSR); - ADM8211_CSR_WRITE(STSR, stsr); - if (stsr == 0xffffffff) - return IRQ_HANDLED; - - if (!(stsr & (ADM8211_STSR_NISS | ADM8211_STSR_AISS))) - return IRQ_HANDLED; - - if (stsr & ADM8211_STSR_RCI) - adm8211_interrupt_rci(dev); - if (stsr & ADM8211_STSR_TCI) - adm8211_interrupt_tci(dev); - - ADM8211_INT(PCF); - ADM8211_INT(BCNTC); - ADM8211_INT(GPINT); - ADM8211_INT(ATIMTC); - ADM8211_INT(TSFTF); - ADM8211_INT(TSCZ); - ADM8211_INT(SQL); - ADM8211_INT(WEPTD); - ADM8211_INT(ATIME); - ADM8211_INT(TEIS); - ADM8211_INT(FBE); - ADM8211_INT(REIS); - ADM8211_INT(GPTT); - ADM8211_INT(RPS); - ADM8211_INT(RDU); - ADM8211_INT(TUF); - ADM8211_INT(TPS); - - return IRQ_HANDLED; - -#undef ADM8211_INT -} - -#define WRITE_SYN(name,v_mask,v_shift,a_mask,a_shift,bits,prewrite,postwrite)\ -static void adm8211_rf_write_syn_ ## name (struct ieee80211_hw *dev, \ - u16 addr, u32 value) { \ - struct adm8211_priv *priv = dev->priv; \ - unsigned int i; \ - u32 reg, bitbuf; \ - \ - value &= v_mask; \ - addr &= a_mask; \ - bitbuf = (value << v_shift) | (addr << a_shift); \ - \ - ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1); \ - ADM8211_CSR_READ(SYNRF); \ - ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0); \ - ADM8211_CSR_READ(SYNRF); \ - \ - if (prewrite) { \ - ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_WRITE_SYNDATA_0); \ - ADM8211_CSR_READ(SYNRF); \ - } \ - \ - for (i = 0; i <= bits; i++) { \ - if (bitbuf & (1 << (bits - i))) \ - reg = ADM8211_SYNRF_WRITE_SYNDATA_1; \ - else \ - reg = ADM8211_SYNRF_WRITE_SYNDATA_0; \ - \ - ADM8211_CSR_WRITE(SYNRF, reg); \ - ADM8211_CSR_READ(SYNRF); \ - \ - ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1); \ - ADM8211_CSR_READ(SYNRF); \ - ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0); \ - ADM8211_CSR_READ(SYNRF); \ - } \ - \ - if (postwrite == 1) { \ - ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_0); \ - ADM8211_CSR_READ(SYNRF); \ - } \ - if (postwrite == 2) { \ - ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_1); \ - ADM8211_CSR_READ(SYNRF); \ - } \ - \ - ADM8211_CSR_WRITE(SYNRF, 0); \ - ADM8211_CSR_READ(SYNRF); \ -} - -WRITE_SYN(max2820, 0x00FFF, 0, 0x0F, 12, 15, 1, 1) -WRITE_SYN(al2210l, 0xFFFFF, 4, 0x0F, 0, 23, 1, 1) -WRITE_SYN(rfmd2958, 0x3FFFF, 0, 0x1F, 18, 23, 0, 1) -WRITE_SYN(rfmd2948, 0x0FFFF, 4, 0x0F, 0, 21, 0, 2) - -#undef WRITE_SYN - -static int adm8211_write_bbp(struct ieee80211_hw *dev, u8 addr, u8 data) -{ - struct adm8211_priv *priv = dev->priv; - unsigned int timeout; - u32 reg; - - timeout = 10; - while (timeout > 0) { - reg = ADM8211_CSR_READ(BBPCTL); - if (!(reg & (ADM8211_BBPCTL_WR | ADM8211_BBPCTL_RD))) - break; - timeout--; - msleep(2); - } - - if (timeout == 0) { - wiphy_debug(dev->wiphy, - "adm8211_write_bbp(%d,%d) failed prewrite (reg=0x%08x)\n", - addr, data, reg); - return -ETIMEDOUT; - } - - switch (priv->bbp_type) { - case ADM8211_TYPE_INTERSIL: - reg = ADM8211_BBPCTL_MMISEL; /* three wire interface */ - break; - case ADM8211_TYPE_RFMD: - reg = (0x20 << 24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP | - (0x01 << 18); - break; - case ADM8211_TYPE_ADMTEK: - reg = (0x20 << 24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP | - (0x05 << 18); - break; - } - reg |= ADM8211_BBPCTL_WR | (addr << 8) | data; - - ADM8211_CSR_WRITE(BBPCTL, reg); - - timeout = 10; - while (timeout > 0) { - reg = ADM8211_CSR_READ(BBPCTL); - if (!(reg & ADM8211_BBPCTL_WR)) - break; - timeout--; - msleep(2); - } - - if (timeout == 0) { - ADM8211_CSR_WRITE(BBPCTL, ADM8211_CSR_READ(BBPCTL) & - ~ADM8211_BBPCTL_WR); - wiphy_debug(dev->wiphy, - "adm8211_write_bbp(%d,%d) failed postwrite (reg=0x%08x)\n", - addr, data, reg); - return -ETIMEDOUT; - } - - return 0; -} - -static int adm8211_rf_set_channel(struct ieee80211_hw *dev, unsigned int chan) -{ - static const u32 adm8211_rfmd2958_reg5[] = - {0x22BD, 0x22D2, 0x22E8, 0x22FE, 0x2314, 0x232A, 0x2340, - 0x2355, 0x236B, 0x2381, 0x2397, 0x23AD, 0x23C2, 0x23F7}; - static const u32 adm8211_rfmd2958_reg6[] = - {0x05D17, 0x3A2E8, 0x2E8BA, 0x22E8B, 0x1745D, 0x0BA2E, 0x00000, - 0x345D1, 0x28BA2, 0x1D174, 0x11745, 0x05D17, 0x3A2E8, 0x11745}; - - struct adm8211_priv *priv = dev->priv; - u8 ant_power = priv->ant_power > 0x3F ? - priv->eeprom->antenna_power[chan - 1] : priv->ant_power; - u8 tx_power = priv->tx_power > 0x3F ? - priv->eeprom->tx_power[chan - 1] : priv->tx_power; - u8 lpf_cutoff = priv->lpf_cutoff == 0xFF ? - priv->eeprom->lpf_cutoff[chan - 1] : priv->lpf_cutoff; - u8 lnags_thresh = priv->lnags_threshold == 0xFF ? - priv->eeprom->lnags_threshold[chan - 1] : priv->lnags_threshold; - u32 reg; - - ADM8211_IDLE(); - - /* Program synthesizer to new channel */ - switch (priv->transceiver_type) { - case ADM8211_RFMD2958: - case ADM8211_RFMD2958_RF3000_CONTROL_POWER: - adm8211_rf_write_syn_rfmd2958(dev, 0x00, 0x04007); - adm8211_rf_write_syn_rfmd2958(dev, 0x02, 0x00033); - - adm8211_rf_write_syn_rfmd2958(dev, 0x05, - adm8211_rfmd2958_reg5[chan - 1]); - adm8211_rf_write_syn_rfmd2958(dev, 0x06, - adm8211_rfmd2958_reg6[chan - 1]); - break; - - case ADM8211_RFMD2948: - adm8211_rf_write_syn_rfmd2948(dev, SI4126_MAIN_CONF, - SI4126_MAIN_XINDIV2); - adm8211_rf_write_syn_rfmd2948(dev, SI4126_POWERDOWN, - SI4126_POWERDOWN_PDIB | - SI4126_POWERDOWN_PDRB); - adm8211_rf_write_syn_rfmd2948(dev, SI4126_PHASE_DET_GAIN, 0); - adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_N_DIV, - (chan == 14 ? - 2110 : (2033 + (chan * 5)))); - adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_N_DIV, 1496); - adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_R_DIV, 44); - adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_R_DIV, 44); - break; - - case ADM8211_MAX2820: - adm8211_rf_write_syn_max2820(dev, 0x3, - (chan == 14 ? 0x054 : (0x7 + (chan * 5)))); - break; - - case ADM8211_AL2210L: - adm8211_rf_write_syn_al2210l(dev, 0x0, - (chan == 14 ? 0x229B4 : (0x22967 + (chan * 5)))); - break; - - default: - wiphy_debug(dev->wiphy, "unsupported transceiver type %d\n", - priv->transceiver_type); - break; - } - - /* write BBP regs */ - if (priv->bbp_type == ADM8211_TYPE_RFMD) { - - /* SMC 2635W specific? adm8211b doesn't use the 2948 though.. */ - /* TODO: remove if SMC 2635W doesn't need this */ - if (priv->transceiver_type == ADM8211_RFMD2948) { - reg = ADM8211_CSR_READ(GPIO); - reg &= 0xfffc0000; - reg |= ADM8211_CSR_GPIO_EN0; - if (chan != 14) - reg |= ADM8211_CSR_GPIO_O0; - ADM8211_CSR_WRITE(GPIO, reg); - } - - if (priv->transceiver_type == ADM8211_RFMD2958) { - /* set PCNT2 */ - adm8211_rf_write_syn_rfmd2958(dev, 0x0B, 0x07100); - /* set PCNT1 P_DESIRED/MID_BIAS */ - reg = le16_to_cpu(priv->eeprom->cr49); - reg >>= 13; - reg <<= 15; - reg |= ant_power << 9; - adm8211_rf_write_syn_rfmd2958(dev, 0x0A, reg); - /* set TXRX TX_GAIN */ - adm8211_rf_write_syn_rfmd2958(dev, 0x09, 0x00050 | - (priv->pdev->revision < ADM8211_REV_CA ? tx_power : 0)); - } else { - reg = ADM8211_CSR_READ(PLCPHD); - reg &= 0xff00ffff; - reg |= tx_power << 18; - ADM8211_CSR_WRITE(PLCPHD, reg); - } - - ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | - ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST); - ADM8211_CSR_READ(SYNRF); - msleep(30); - - /* RF3000 BBP */ - if (priv->transceiver_type != ADM8211_RFMD2958) - adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, - tx_power<<2); - adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, lpf_cutoff); - adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, lnags_thresh); - adm8211_write_bbp(dev, 0x1c, priv->pdev->revision == ADM8211_REV_BA ? - priv->eeprom->cr28 : 0); - adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29); - - ADM8211_CSR_WRITE(SYNRF, 0); - - /* Nothing to do for ADMtek BBP */ - } else if (priv->bbp_type != ADM8211_TYPE_ADMTEK) - wiphy_debug(dev->wiphy, "unsupported BBP type %d\n", - priv->bbp_type); - - ADM8211_RESTORE(); - - /* update current channel for adhoc (and maybe AP mode) */ - reg = ADM8211_CSR_READ(CAP0); - reg &= ~0xF; - reg |= chan; - ADM8211_CSR_WRITE(CAP0, reg); - - return 0; -} - -static void adm8211_update_mode(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - - ADM8211_IDLE(); - - priv->soft_rx_crc = 0; - switch (priv->mode) { - case NL80211_IFTYPE_STATION: - priv->nar &= ~(ADM8211_NAR_PR | ADM8211_NAR_EA); - priv->nar |= ADM8211_NAR_ST | ADM8211_NAR_SR; - break; - case NL80211_IFTYPE_ADHOC: - priv->nar &= ~ADM8211_NAR_PR; - priv->nar |= ADM8211_NAR_EA | ADM8211_NAR_ST | ADM8211_NAR_SR; - - /* don't trust the error bits on rev 0x20 and up in adhoc */ - if (priv->pdev->revision >= ADM8211_REV_BA) - priv->soft_rx_crc = 1; - break; - case NL80211_IFTYPE_MONITOR: - priv->nar &= ~(ADM8211_NAR_EA | ADM8211_NAR_ST); - priv->nar |= ADM8211_NAR_PR | ADM8211_NAR_SR; - break; - } - - ADM8211_RESTORE(); -} - -static void adm8211_hw_init_syn(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - - switch (priv->transceiver_type) { - case ADM8211_RFMD2958: - case ADM8211_RFMD2958_RF3000_CONTROL_POWER: - /* comments taken from ADMtek vendor driver */ - - /* Reset RF2958 after power on */ - adm8211_rf_write_syn_rfmd2958(dev, 0x1F, 0x00000); - /* Initialize RF VCO Core Bias to maximum */ - adm8211_rf_write_syn_rfmd2958(dev, 0x0C, 0x3001F); - /* Initialize IF PLL */ - adm8211_rf_write_syn_rfmd2958(dev, 0x01, 0x29C03); - /* Initialize IF PLL Coarse Tuning */ - adm8211_rf_write_syn_rfmd2958(dev, 0x03, 0x1FF6F); - /* Initialize RF PLL */ - adm8211_rf_write_syn_rfmd2958(dev, 0x04, 0x29403); - /* Initialize RF PLL Coarse Tuning */ - adm8211_rf_write_syn_rfmd2958(dev, 0x07, 0x1456F); - /* Initialize TX gain and filter BW (R9) */ - adm8211_rf_write_syn_rfmd2958(dev, 0x09, - (priv->transceiver_type == ADM8211_RFMD2958 ? - 0x10050 : 0x00050)); - /* Initialize CAL register */ - adm8211_rf_write_syn_rfmd2958(dev, 0x08, 0x3FFF8); - break; - - case ADM8211_MAX2820: - adm8211_rf_write_syn_max2820(dev, 0x1, 0x01E); - adm8211_rf_write_syn_max2820(dev, 0x2, 0x001); - adm8211_rf_write_syn_max2820(dev, 0x3, 0x054); - adm8211_rf_write_syn_max2820(dev, 0x4, 0x310); - adm8211_rf_write_syn_max2820(dev, 0x5, 0x000); - break; - - case ADM8211_AL2210L: - adm8211_rf_write_syn_al2210l(dev, 0x0, 0x0196C); - adm8211_rf_write_syn_al2210l(dev, 0x1, 0x007CB); - adm8211_rf_write_syn_al2210l(dev, 0x2, 0x3582F); - adm8211_rf_write_syn_al2210l(dev, 0x3, 0x010A9); - adm8211_rf_write_syn_al2210l(dev, 0x4, 0x77280); - adm8211_rf_write_syn_al2210l(dev, 0x5, 0x45641); - adm8211_rf_write_syn_al2210l(dev, 0x6, 0xEA130); - adm8211_rf_write_syn_al2210l(dev, 0x7, 0x80000); - adm8211_rf_write_syn_al2210l(dev, 0x8, 0x7850F); - adm8211_rf_write_syn_al2210l(dev, 0x9, 0xF900C); - adm8211_rf_write_syn_al2210l(dev, 0xA, 0x00000); - adm8211_rf_write_syn_al2210l(dev, 0xB, 0x00000); - break; - - case ADM8211_RFMD2948: - default: - break; - } -} - -static int adm8211_hw_init_bbp(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - u32 reg; - - /* write addresses */ - if (priv->bbp_type == ADM8211_TYPE_INTERSIL) { - ADM8211_CSR_WRITE(MMIWA, 0x100E0C0A); - ADM8211_CSR_WRITE(MMIRD0, 0x00007C7E); - ADM8211_CSR_WRITE(MMIRD1, 0x00100000); - } else if (priv->bbp_type == ADM8211_TYPE_RFMD || - priv->bbp_type == ADM8211_TYPE_ADMTEK) { - /* check specific BBP type */ - switch (priv->specific_bbptype) { - case ADM8211_BBP_RFMD3000: - case ADM8211_BBP_RFMD3002: - ADM8211_CSR_WRITE(MMIWA, 0x00009101); - ADM8211_CSR_WRITE(MMIRD0, 0x00000301); - break; - - case ADM8211_BBP_ADM8011: - ADM8211_CSR_WRITE(MMIWA, 0x00008903); - ADM8211_CSR_WRITE(MMIRD0, 0x00001716); - - reg = ADM8211_CSR_READ(BBPCTL); - reg &= ~ADM8211_BBPCTL_TYPE; - reg |= 0x5 << 18; - ADM8211_CSR_WRITE(BBPCTL, reg); - break; - } - - switch (priv->pdev->revision) { - case ADM8211_REV_CA: - if (priv->transceiver_type == ADM8211_RFMD2958 || - priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER || - priv->transceiver_type == ADM8211_RFMD2948) - ADM8211_CSR_WRITE(SYNCTL, 0x1 << 22); - else if (priv->transceiver_type == ADM8211_MAX2820 || - priv->transceiver_type == ADM8211_AL2210L) - ADM8211_CSR_WRITE(SYNCTL, 0x3 << 22); - break; - - case ADM8211_REV_BA: - reg = ADM8211_CSR_READ(MMIRD1); - reg &= 0x0000FFFF; - reg |= 0x7e100000; - ADM8211_CSR_WRITE(MMIRD1, reg); - break; - - case ADM8211_REV_AB: - case ADM8211_REV_AF: - default: - ADM8211_CSR_WRITE(MMIRD1, 0x7e100000); - break; - } - - /* For RFMD */ - ADM8211_CSR_WRITE(MACTEST, 0x800); - } - - adm8211_hw_init_syn(dev); - - /* Set RF Power control IF pin to PE1+PHYRST# */ - ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | - ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST); - ADM8211_CSR_READ(SYNRF); - msleep(20); - - /* write BBP regs */ - if (priv->bbp_type == ADM8211_TYPE_RFMD) { - /* RF3000 BBP */ - /* another set: - * 11: c8 - * 14: 14 - * 15: 50 (chan 1..13; chan 14: d0) - * 1c: 00 - * 1d: 84 - */ - adm8211_write_bbp(dev, RF3000_CCA_CTRL, 0x80); - /* antenna selection: diversity */ - adm8211_write_bbp(dev, RF3000_DIVERSITY__RSSI, 0x80); - adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, 0x74); - adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, 0x38); - adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, 0x40); - - if (priv->eeprom->major_version < 2) { - adm8211_write_bbp(dev, 0x1c, 0x00); - adm8211_write_bbp(dev, 0x1d, 0x80); - } else { - if (priv->pdev->revision == ADM8211_REV_BA) - adm8211_write_bbp(dev, 0x1c, priv->eeprom->cr28); - else - adm8211_write_bbp(dev, 0x1c, 0x00); - - adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29); - } - } else if (priv->bbp_type == ADM8211_TYPE_ADMTEK) { - /* reset baseband */ - adm8211_write_bbp(dev, 0x00, 0xFF); - /* antenna selection: diversity */ - adm8211_write_bbp(dev, 0x07, 0x0A); - - /* TODO: find documentation for this */ - switch (priv->transceiver_type) { - case ADM8211_RFMD2958: - case ADM8211_RFMD2958_RF3000_CONTROL_POWER: - adm8211_write_bbp(dev, 0x00, 0x00); - adm8211_write_bbp(dev, 0x01, 0x00); - adm8211_write_bbp(dev, 0x02, 0x00); - adm8211_write_bbp(dev, 0x03, 0x00); - adm8211_write_bbp(dev, 0x06, 0x0f); - adm8211_write_bbp(dev, 0x09, 0x00); - adm8211_write_bbp(dev, 0x0a, 0x00); - adm8211_write_bbp(dev, 0x0b, 0x00); - adm8211_write_bbp(dev, 0x0c, 0x00); - adm8211_write_bbp(dev, 0x0f, 0xAA); - adm8211_write_bbp(dev, 0x10, 0x8c); - adm8211_write_bbp(dev, 0x11, 0x43); - adm8211_write_bbp(dev, 0x18, 0x40); - adm8211_write_bbp(dev, 0x20, 0x23); - adm8211_write_bbp(dev, 0x21, 0x02); - adm8211_write_bbp(dev, 0x22, 0x28); - adm8211_write_bbp(dev, 0x23, 0x30); - adm8211_write_bbp(dev, 0x24, 0x2d); - adm8211_write_bbp(dev, 0x28, 0x35); - adm8211_write_bbp(dev, 0x2a, 0x8c); - adm8211_write_bbp(dev, 0x2b, 0x81); - adm8211_write_bbp(dev, 0x2c, 0x44); - adm8211_write_bbp(dev, 0x2d, 0x0A); - adm8211_write_bbp(dev, 0x29, 0x40); - adm8211_write_bbp(dev, 0x60, 0x08); - adm8211_write_bbp(dev, 0x64, 0x01); - break; - - case ADM8211_MAX2820: - adm8211_write_bbp(dev, 0x00, 0x00); - adm8211_write_bbp(dev, 0x01, 0x00); - adm8211_write_bbp(dev, 0x02, 0x00); - adm8211_write_bbp(dev, 0x03, 0x00); - adm8211_write_bbp(dev, 0x06, 0x0f); - adm8211_write_bbp(dev, 0x09, 0x05); - adm8211_write_bbp(dev, 0x0a, 0x02); - adm8211_write_bbp(dev, 0x0b, 0x00); - adm8211_write_bbp(dev, 0x0c, 0x0f); - adm8211_write_bbp(dev, 0x0f, 0x55); - adm8211_write_bbp(dev, 0x10, 0x8d); - adm8211_write_bbp(dev, 0x11, 0x43); - adm8211_write_bbp(dev, 0x18, 0x4a); - adm8211_write_bbp(dev, 0x20, 0x20); - adm8211_write_bbp(dev, 0x21, 0x02); - adm8211_write_bbp(dev, 0x22, 0x23); - adm8211_write_bbp(dev, 0x23, 0x30); - adm8211_write_bbp(dev, 0x24, 0x2d); - adm8211_write_bbp(dev, 0x2a, 0x8c); - adm8211_write_bbp(dev, 0x2b, 0x81); - adm8211_write_bbp(dev, 0x2c, 0x44); - adm8211_write_bbp(dev, 0x29, 0x4a); - adm8211_write_bbp(dev, 0x60, 0x2b); - adm8211_write_bbp(dev, 0x64, 0x01); - break; - - case ADM8211_AL2210L: - adm8211_write_bbp(dev, 0x00, 0x00); - adm8211_write_bbp(dev, 0x01, 0x00); - adm8211_write_bbp(dev, 0x02, 0x00); - adm8211_write_bbp(dev, 0x03, 0x00); - adm8211_write_bbp(dev, 0x06, 0x0f); - adm8211_write_bbp(dev, 0x07, 0x05); - adm8211_write_bbp(dev, 0x08, 0x03); - adm8211_write_bbp(dev, 0x09, 0x00); - adm8211_write_bbp(dev, 0x0a, 0x00); - adm8211_write_bbp(dev, 0x0b, 0x00); - adm8211_write_bbp(dev, 0x0c, 0x10); - adm8211_write_bbp(dev, 0x0f, 0x55); - adm8211_write_bbp(dev, 0x10, 0x8d); - adm8211_write_bbp(dev, 0x11, 0x43); - adm8211_write_bbp(dev, 0x18, 0x4a); - adm8211_write_bbp(dev, 0x20, 0x20); - adm8211_write_bbp(dev, 0x21, 0x02); - adm8211_write_bbp(dev, 0x22, 0x23); - adm8211_write_bbp(dev, 0x23, 0x30); - adm8211_write_bbp(dev, 0x24, 0x2d); - adm8211_write_bbp(dev, 0x2a, 0xaa); - adm8211_write_bbp(dev, 0x2b, 0x81); - adm8211_write_bbp(dev, 0x2c, 0x44); - adm8211_write_bbp(dev, 0x29, 0xfa); - adm8211_write_bbp(dev, 0x60, 0x2d); - adm8211_write_bbp(dev, 0x64, 0x01); - break; - - case ADM8211_RFMD2948: - break; - - default: - wiphy_debug(dev->wiphy, "unsupported transceiver %d\n", - priv->transceiver_type); - break; - } - } else - wiphy_debug(dev->wiphy, "unsupported BBP %d\n", priv->bbp_type); - - ADM8211_CSR_WRITE(SYNRF, 0); - - /* Set RF CAL control source to MAC control */ - reg = ADM8211_CSR_READ(SYNCTL); - reg |= ADM8211_SYNCTL_SELCAL; - ADM8211_CSR_WRITE(SYNCTL, reg); - - return 0; -} - -/* configures hw beacons/probe responses */ -static int adm8211_set_rate(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - u32 reg; - int i = 0; - u8 rate_buf[12] = {0}; - - /* write supported rates */ - if (priv->pdev->revision != ADM8211_REV_BA) { - rate_buf[0] = ARRAY_SIZE(adm8211_rates); - for (i = 0; i < ARRAY_SIZE(adm8211_rates); i++) - rate_buf[i + 1] = (adm8211_rates[i].bitrate / 5) | 0x80; - } else { - /* workaround for rev BA specific bug */ - rate_buf[0] = 0x04; - rate_buf[1] = 0x82; - rate_buf[2] = 0x04; - rate_buf[3] = 0x0b; - rate_buf[4] = 0x16; - } - - adm8211_write_sram_bytes(dev, ADM8211_SRAM_SUPP_RATE, rate_buf, - ARRAY_SIZE(adm8211_rates) + 1); - - reg = ADM8211_CSR_READ(PLCPHD) & 0x00FFFFFF; /* keep bits 0-23 */ - reg |= 1 << 15; /* short preamble */ - reg |= 110 << 24; - ADM8211_CSR_WRITE(PLCPHD, reg); - - /* MTMLT = 512 TU (max TX MSDU lifetime) - * BCNTSIG = plcp_signal (beacon, probe resp, and atim TX rate) - * SRTYLIM = 224 (short retry limit, TX header value is default) */ - ADM8211_CSR_WRITE(TXLMT, (512 << 16) | (110 << 8) | (224 << 0)); - - return 0; -} - -static void adm8211_hw_init(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - u32 reg; - u8 cline; - - reg = ADM8211_CSR_READ(PAR); - reg |= ADM8211_PAR_MRLE | ADM8211_PAR_MRME; - reg &= ~(ADM8211_PAR_BAR | ADM8211_PAR_CAL); - - if (!pci_set_mwi(priv->pdev)) { - reg |= 0x1 << 24; - pci_read_config_byte(priv->pdev, PCI_CACHE_LINE_SIZE, &cline); - - switch (cline) { - case 0x8: - reg |= (0x1 << 14); - break; - case 0x10: - reg |= (0x2 << 14); - break; - case 0x20: - reg |= (0x3 << 14); - break; - default: - reg |= (0x0 << 14); - break; - } - } - - ADM8211_CSR_WRITE(PAR, reg); - - reg = ADM8211_CSR_READ(CSR_TEST1); - reg &= ~(0xF << 28); - reg |= (1 << 28) | (1 << 31); - ADM8211_CSR_WRITE(CSR_TEST1, reg); - - /* lose link after 4 lost beacons */ - reg = (0x04 << 21) | ADM8211_WCSR_TSFTWE | ADM8211_WCSR_LSOE; - ADM8211_CSR_WRITE(WCSR, reg); - - /* Disable APM, enable receive FIFO threshold, and set drain receive - * threshold to store-and-forward */ - reg = ADM8211_CSR_READ(CMDR); - reg &= ~(ADM8211_CMDR_APM | ADM8211_CMDR_DRT); - reg |= ADM8211_CMDR_RTE | ADM8211_CMDR_DRT_SF; - ADM8211_CSR_WRITE(CMDR, reg); - - adm8211_set_rate(dev); - - /* 4-bit values: - * PWR1UP = 8 * 2 ms - * PWR0PAPE = 8 us or 5 us - * PWR1PAPE = 1 us or 3 us - * PWR0TRSW = 5 us - * PWR1TRSW = 12 us - * PWR0PE2 = 13 us - * PWR1PE2 = 1 us - * PWR0TXPE = 8 or 6 */ - if (priv->pdev->revision < ADM8211_REV_CA) - ADM8211_CSR_WRITE(TOFS2, 0x8815cd18); - else - ADM8211_CSR_WRITE(TOFS2, 0x8535cd16); - - /* Enable store and forward for transmit */ - priv->nar = ADM8211_NAR_SF | ADM8211_NAR_PB; - ADM8211_CSR_WRITE(NAR, priv->nar); - - /* Reset RF */ - ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_RADIO); - ADM8211_CSR_READ(SYNRF); - msleep(10); - ADM8211_CSR_WRITE(SYNRF, 0); - ADM8211_CSR_READ(SYNRF); - msleep(5); - - /* Set CFP Max Duration to 0x10 TU */ - reg = ADM8211_CSR_READ(CFPP); - reg &= ~(0xffff << 8); - reg |= 0x0010 << 8; - ADM8211_CSR_WRITE(CFPP, reg); - - /* USCNT = 0x16 (number of system clocks, 22 MHz, in 1us - * TUCNT = 0x3ff - Tu counter 1024 us */ - ADM8211_CSR_WRITE(TOFS0, (0x16 << 24) | 0x3ff); - - /* SLOT=20 us, SIFS=110 cycles of 22 MHz (5 us), - * DIFS=50 us, EIFS=100 us */ - if (priv->pdev->revision < ADM8211_REV_CA) - ADM8211_CSR_WRITE(IFST, (20 << 23) | (110 << 15) | - (50 << 9) | 100); - else - ADM8211_CSR_WRITE(IFST, (20 << 23) | (24 << 15) | - (50 << 9) | 100); - - /* PCNT = 1 (MAC idle time awake/sleep, unit S) - * RMRD = 2346 * 8 + 1 us (max RX duration) */ - ADM8211_CSR_WRITE(RMD, (1 << 16) | 18769); - - /* MART=65535 us, MIRT=256 us, TSFTOFST=0 us */ - ADM8211_CSR_WRITE(RSPT, 0xffffff00); - - /* Initialize BBP (and SYN) */ - adm8211_hw_init_bbp(dev); - - /* make sure interrupts are off */ - ADM8211_CSR_WRITE(IER, 0); - - /* ACK interrupts */ - ADM8211_CSR_WRITE(STSR, ADM8211_CSR_READ(STSR)); - - /* Setup WEP (turns it off for now) */ - reg = ADM8211_CSR_READ(MACTEST); - reg &= ~(7 << 20); - ADM8211_CSR_WRITE(MACTEST, reg); - - reg = ADM8211_CSR_READ(WEPCTL); - reg &= ~ADM8211_WEPCTL_WEPENABLE; - reg |= ADM8211_WEPCTL_WEPRXBYP; - ADM8211_CSR_WRITE(WEPCTL, reg); - - /* Clear the missed-packet counter. */ - ADM8211_CSR_READ(LPC); -} - -static int adm8211_hw_reset(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - u32 reg, tmp; - int timeout = 100; - - /* Power-on issue */ - /* TODO: check if this is necessary */ - ADM8211_CSR_WRITE(FRCTL, 0); - - /* Reset the chip */ - tmp = ADM8211_CSR_READ(PAR); - ADM8211_CSR_WRITE(PAR, ADM8211_PAR_SWR); - - while ((ADM8211_CSR_READ(PAR) & ADM8211_PAR_SWR) && timeout--) - msleep(50); - - if (timeout <= 0) - return -ETIMEDOUT; - - ADM8211_CSR_WRITE(PAR, tmp); - - if (priv->pdev->revision == ADM8211_REV_BA && - (priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER || - priv->transceiver_type == ADM8211_RFMD2958)) { - reg = ADM8211_CSR_READ(CSR_TEST1); - reg |= (1 << 4) | (1 << 5); - ADM8211_CSR_WRITE(CSR_TEST1, reg); - } else if (priv->pdev->revision == ADM8211_REV_CA) { - reg = ADM8211_CSR_READ(CSR_TEST1); - reg &= ~((1 << 4) | (1 << 5)); - ADM8211_CSR_WRITE(CSR_TEST1, reg); - } - - ADM8211_CSR_WRITE(FRCTL, 0); - - reg = ADM8211_CSR_READ(CSR_TEST0); - reg |= ADM8211_CSR_TEST0_EPRLD; /* EEPROM Recall */ - ADM8211_CSR_WRITE(CSR_TEST0, reg); - - adm8211_clear_sram(dev); - - return 0; -} - -static u64 adm8211_get_tsft(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct adm8211_priv *priv = dev->priv; - u32 tsftl; - u64 tsft; - - tsftl = ADM8211_CSR_READ(TSFTL); - tsft = ADM8211_CSR_READ(TSFTH); - tsft <<= 32; - tsft |= tsftl; - - return tsft; -} - -static void adm8211_set_interval(struct ieee80211_hw *dev, - unsigned short bi, unsigned short li) -{ - struct adm8211_priv *priv = dev->priv; - u32 reg; - - /* BP (beacon interval) = data->beacon_interval - * LI (listen interval) = data->listen_interval (in beacon intervals) */ - reg = (bi << 16) | li; - ADM8211_CSR_WRITE(BPLI, reg); -} - -static void adm8211_set_bssid(struct ieee80211_hw *dev, const u8 *bssid) -{ - struct adm8211_priv *priv = dev->priv; - u32 reg; - - ADM8211_CSR_WRITE(BSSID0, le32_to_cpu(*(__le32 *)bssid)); - reg = ADM8211_CSR_READ(ABDA1); - reg &= 0x0000ffff; - reg |= (bssid[4] << 16) | (bssid[5] << 24); - ADM8211_CSR_WRITE(ABDA1, reg); -} - -static int adm8211_config(struct ieee80211_hw *dev, u32 changed) -{ - struct adm8211_priv *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; - int channel = - ieee80211_frequency_to_channel(conf->chandef.chan->center_freq); - - if (channel != priv->channel) { - priv->channel = channel; - adm8211_rf_set_channel(dev, priv->channel); - } - - return 0; -} - -static void adm8211_bss_info_changed(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf, - u32 changes) -{ - struct adm8211_priv *priv = dev->priv; - - if (!(changes & BSS_CHANGED_BSSID)) - return; - - if (!ether_addr_equal(conf->bssid, priv->bssid)) { - adm8211_set_bssid(dev, conf->bssid); - memcpy(priv->bssid, conf->bssid, ETH_ALEN); - } -} - -static u64 adm8211_prepare_multicast(struct ieee80211_hw *hw, - struct netdev_hw_addr_list *mc_list) -{ - unsigned int bit_nr; - u32 mc_filter[2]; - struct netdev_hw_addr *ha; - - mc_filter[1] = mc_filter[0] = 0; - - netdev_hw_addr_list_for_each(ha, mc_list) { - bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; - - bit_nr &= 0x3F; - mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); - } - - return mc_filter[0] | ((u64)(mc_filter[1]) << 32); -} - -static void adm8211_configure_filter(struct ieee80211_hw *dev, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - static const u8 bcast[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - struct adm8211_priv *priv = dev->priv; - unsigned int new_flags; - u32 mc_filter[2]; - - mc_filter[0] = multicast; - mc_filter[1] = multicast >> 32; - - new_flags = 0; - - if (*total_flags & FIF_ALLMULTI || multicast == ~(0ULL)) { - new_flags |= FIF_ALLMULTI; - priv->nar &= ~ADM8211_NAR_PR; - priv->nar |= ADM8211_NAR_MM; - mc_filter[1] = mc_filter[0] = ~0; - } else { - priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR); - } - - ADM8211_IDLE_RX(); - - ADM8211_CSR_WRITE(MAR0, mc_filter[0]); - ADM8211_CSR_WRITE(MAR1, mc_filter[1]); - ADM8211_CSR_READ(NAR); - - if (priv->nar & ADM8211_NAR_PR) - ieee80211_hw_set(dev, RX_INCLUDES_FCS); - else - __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, dev->flags); - - if (*total_flags & FIF_BCN_PRBRESP_PROMISC) - adm8211_set_bssid(dev, bcast); - else - adm8211_set_bssid(dev, priv->bssid); - - ADM8211_RESTORE(); - - *total_flags = new_flags; -} - -static int adm8211_add_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct adm8211_priv *priv = dev->priv; - if (priv->mode != NL80211_IFTYPE_MONITOR) - return -EOPNOTSUPP; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - priv->mode = vif->type; - break; - default: - return -EOPNOTSUPP; - } - - ADM8211_IDLE(); - - ADM8211_CSR_WRITE(PAR0, le32_to_cpu(*(__le32 *)vif->addr)); - ADM8211_CSR_WRITE(PAR1, le16_to_cpu(*(__le16 *)(vif->addr + 4))); - - adm8211_update_mode(dev); - - ADM8211_RESTORE(); - - return 0; -} - -static void adm8211_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct adm8211_priv *priv = dev->priv; - priv->mode = NL80211_IFTYPE_MONITOR; -} - -static int adm8211_init_rings(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - struct adm8211_desc *desc = NULL; - struct adm8211_rx_ring_info *rx_info; - struct adm8211_tx_ring_info *tx_info; - unsigned int i; - - for (i = 0; i < priv->rx_ring_size; i++) { - desc = &priv->rx_ring[i]; - desc->status = 0; - desc->length = cpu_to_le32(RX_PKT_SIZE); - priv->rx_buffers[i].skb = NULL; - } - /* Mark the end of RX ring; hw returns to base address after this - * descriptor */ - desc->length |= cpu_to_le32(RDES1_CONTROL_RER); - - for (i = 0; i < priv->rx_ring_size; i++) { - desc = &priv->rx_ring[i]; - rx_info = &priv->rx_buffers[i]; - - rx_info->skb = dev_alloc_skb(RX_PKT_SIZE); - if (rx_info->skb == NULL) - break; - rx_info->mapping = pci_map_single(priv->pdev, - skb_tail_pointer(rx_info->skb), - RX_PKT_SIZE, - PCI_DMA_FROMDEVICE); - desc->buffer1 = cpu_to_le32(rx_info->mapping); - desc->status = cpu_to_le32(RDES0_STATUS_OWN | RDES0_STATUS_SQL); - } - - /* Setup TX ring. TX buffers descriptors will be filled in as needed */ - for (i = 0; i < priv->tx_ring_size; i++) { - desc = &priv->tx_ring[i]; - tx_info = &priv->tx_buffers[i]; - - tx_info->skb = NULL; - tx_info->mapping = 0; - desc->status = 0; - } - desc->length = cpu_to_le32(TDES1_CONTROL_TER); - - priv->cur_rx = priv->cur_tx = priv->dirty_tx = 0; - ADM8211_CSR_WRITE(RDB, priv->rx_ring_dma); - ADM8211_CSR_WRITE(TDBD, priv->tx_ring_dma); - - return 0; -} - -static void adm8211_free_rings(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - unsigned int i; - - for (i = 0; i < priv->rx_ring_size; i++) { - if (!priv->rx_buffers[i].skb) - continue; - - pci_unmap_single( - priv->pdev, - priv->rx_buffers[i].mapping, - RX_PKT_SIZE, PCI_DMA_FROMDEVICE); - - dev_kfree_skb(priv->rx_buffers[i].skb); - } - - for (i = 0; i < priv->tx_ring_size; i++) { - if (!priv->tx_buffers[i].skb) - continue; - - pci_unmap_single(priv->pdev, - priv->tx_buffers[i].mapping, - priv->tx_buffers[i].skb->len, - PCI_DMA_TODEVICE); - - dev_kfree_skb(priv->tx_buffers[i].skb); - } -} - -static int adm8211_start(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - int retval; - - /* Power up MAC and RF chips */ - retval = adm8211_hw_reset(dev); - if (retval) { - wiphy_err(dev->wiphy, "hardware reset failed\n"); - goto fail; - } - - retval = adm8211_init_rings(dev); - if (retval) { - wiphy_err(dev->wiphy, "failed to initialize rings\n"); - goto fail; - } - - /* Init hardware */ - adm8211_hw_init(dev); - adm8211_rf_set_channel(dev, priv->channel); - - retval = request_irq(priv->pdev->irq, adm8211_interrupt, - IRQF_SHARED, "adm8211", dev); - if (retval) { - wiphy_err(dev->wiphy, "failed to register IRQ handler\n"); - goto fail; - } - - ADM8211_CSR_WRITE(IER, ADM8211_IER_NIE | ADM8211_IER_AIE | - ADM8211_IER_RCIE | ADM8211_IER_TCIE | - ADM8211_IER_TDUIE | ADM8211_IER_GPTIE); - priv->mode = NL80211_IFTYPE_MONITOR; - adm8211_update_mode(dev); - ADM8211_CSR_WRITE(RDR, 0); - - adm8211_set_interval(dev, 100, 10); - return 0; - -fail: - return retval; -} - -static void adm8211_stop(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - priv->nar = 0; - ADM8211_CSR_WRITE(NAR, 0); - ADM8211_CSR_WRITE(IER, 0); - ADM8211_CSR_READ(NAR); - - free_irq(priv->pdev->irq, dev); - - adm8211_free_rings(dev); -} - -static void adm8211_calc_durations(int *dur, int *plcp, size_t payload_len, int len, - int plcp_signal, int short_preamble) -{ - /* Alternative calculation from NetBSD: */ - -/* IEEE 802.11b durations for DSSS PHY in microseconds */ -#define IEEE80211_DUR_DS_LONG_PREAMBLE 144 -#define IEEE80211_DUR_DS_SHORT_PREAMBLE 72 -#define IEEE80211_DUR_DS_FAST_PLCPHDR 24 -#define IEEE80211_DUR_DS_SLOW_PLCPHDR 48 -#define IEEE80211_DUR_DS_SLOW_ACK 112 -#define IEEE80211_DUR_DS_FAST_ACK 56 -#define IEEE80211_DUR_DS_SLOW_CTS 112 -#define IEEE80211_DUR_DS_FAST_CTS 56 -#define IEEE80211_DUR_DS_SLOT 20 -#define IEEE80211_DUR_DS_SIFS 10 - - int remainder; - - *dur = (80 * (24 + payload_len) + plcp_signal - 1) - / plcp_signal; - - if (plcp_signal <= PLCP_SIGNAL_2M) - /* 1-2Mbps WLAN: send ACK/CTS at 1Mbps */ - *dur += 3 * (IEEE80211_DUR_DS_SIFS + - IEEE80211_DUR_DS_SHORT_PREAMBLE + - IEEE80211_DUR_DS_FAST_PLCPHDR) + - IEEE80211_DUR_DS_SLOW_CTS + IEEE80211_DUR_DS_SLOW_ACK; - else - /* 5-11Mbps WLAN: send ACK/CTS at 2Mbps */ - *dur += 3 * (IEEE80211_DUR_DS_SIFS + - IEEE80211_DUR_DS_SHORT_PREAMBLE + - IEEE80211_DUR_DS_FAST_PLCPHDR) + - IEEE80211_DUR_DS_FAST_CTS + IEEE80211_DUR_DS_FAST_ACK; - - /* lengthen duration if long preamble */ - if (!short_preamble) - *dur += 3 * (IEEE80211_DUR_DS_LONG_PREAMBLE - - IEEE80211_DUR_DS_SHORT_PREAMBLE) + - 3 * (IEEE80211_DUR_DS_SLOW_PLCPHDR - - IEEE80211_DUR_DS_FAST_PLCPHDR); - - - *plcp = (80 * len) / plcp_signal; - remainder = (80 * len) % plcp_signal; - if (plcp_signal == PLCP_SIGNAL_11M && - remainder <= 30 && remainder > 0) - *plcp = (*plcp | 0x8000) + 1; - else if (remainder) - (*plcp)++; -} - -/* Transmit skb w/adm8211_tx_hdr (802.11 header created by hardware) */ -static void adm8211_tx_raw(struct ieee80211_hw *dev, struct sk_buff *skb, - u16 plcp_signal, - size_t hdrlen) -{ - struct adm8211_priv *priv = dev->priv; - unsigned long flags; - dma_addr_t mapping; - unsigned int entry; - u32 flag; - - mapping = pci_map_single(priv->pdev, skb->data, skb->len, - PCI_DMA_TODEVICE); - - spin_lock_irqsave(&priv->lock, flags); - - if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size / 2) - flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS; - else - flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS; - - if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size - 2) - ieee80211_stop_queue(dev, 0); - - entry = priv->cur_tx % priv->tx_ring_size; - - priv->tx_buffers[entry].skb = skb; - priv->tx_buffers[entry].mapping = mapping; - priv->tx_buffers[entry].hdrlen = hdrlen; - priv->tx_ring[entry].buffer1 = cpu_to_le32(mapping); - - if (entry == priv->tx_ring_size - 1) - flag |= TDES1_CONTROL_TER; - priv->tx_ring[entry].length = cpu_to_le32(flag | skb->len); - - /* Set TX rate (SIGNAL field in PLCP PPDU format) */ - flag = TDES0_CONTROL_OWN | (plcp_signal << 20) | 8 /* ? */; - priv->tx_ring[entry].status = cpu_to_le32(flag); - - priv->cur_tx++; - - spin_unlock_irqrestore(&priv->lock, flags); - - /* Trigger transmit poll */ - ADM8211_CSR_WRITE(TDR, 0); -} - -/* Put adm8211_tx_hdr on skb and transmit */ -static void adm8211_tx(struct ieee80211_hw *dev, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct adm8211_tx_hdr *txhdr; - size_t payload_len, hdrlen; - int plcp, dur, len, plcp_signal, short_preamble; - struct ieee80211_hdr *hdr; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_rate *txrate = ieee80211_get_tx_rate(dev, info); - u8 rc_flags; - - rc_flags = info->control.rates[0].flags; - short_preamble = !!(rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE); - plcp_signal = txrate->bitrate; - - hdr = (struct ieee80211_hdr *)skb->data; - hdrlen = ieee80211_hdrlen(hdr->frame_control); - memcpy(skb->cb, skb->data, hdrlen); - hdr = (struct ieee80211_hdr *)skb->cb; - skb_pull(skb, hdrlen); - payload_len = skb->len; - - txhdr = (struct adm8211_tx_hdr *) skb_push(skb, sizeof(*txhdr)); - memset(txhdr, 0, sizeof(*txhdr)); - memcpy(txhdr->da, ieee80211_get_DA(hdr), ETH_ALEN); - txhdr->signal = plcp_signal; - txhdr->frame_body_size = cpu_to_le16(payload_len); - txhdr->frame_control = hdr->frame_control; - - len = hdrlen + payload_len + FCS_LEN; - - txhdr->frag = cpu_to_le16(0x0FFF); - adm8211_calc_durations(&dur, &plcp, payload_len, - len, plcp_signal, short_preamble); - txhdr->plcp_frag_head_len = cpu_to_le16(plcp); - txhdr->plcp_frag_tail_len = cpu_to_le16(plcp); - txhdr->dur_frag_head = cpu_to_le16(dur); - txhdr->dur_frag_tail = cpu_to_le16(dur); - - txhdr->header_control = cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER); - - if (short_preamble) - txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE); - - if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) - txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS); - - txhdr->retry_limit = info->control.rates[0].count; - - adm8211_tx_raw(dev, skb, plcp_signal, hdrlen); -} - -static int adm8211_alloc_rings(struct ieee80211_hw *dev) -{ - struct adm8211_priv *priv = dev->priv; - unsigned int ring_size; - - priv->rx_buffers = kmalloc(sizeof(*priv->rx_buffers) * priv->rx_ring_size + - sizeof(*priv->tx_buffers) * priv->tx_ring_size, GFP_KERNEL); - if (!priv->rx_buffers) - return -ENOMEM; - - priv->tx_buffers = (void *)priv->rx_buffers + - sizeof(*priv->rx_buffers) * priv->rx_ring_size; - - /* Allocate TX/RX descriptors */ - ring_size = sizeof(struct adm8211_desc) * priv->rx_ring_size + - sizeof(struct adm8211_desc) * priv->tx_ring_size; - priv->rx_ring = pci_alloc_consistent(priv->pdev, ring_size, - &priv->rx_ring_dma); - - if (!priv->rx_ring) { - kfree(priv->rx_buffers); - priv->rx_buffers = NULL; - priv->tx_buffers = NULL; - return -ENOMEM; - } - - priv->tx_ring = priv->rx_ring + priv->rx_ring_size; - priv->tx_ring_dma = priv->rx_ring_dma + - sizeof(struct adm8211_desc) * priv->rx_ring_size; - - return 0; -} - -static const struct ieee80211_ops adm8211_ops = { - .tx = adm8211_tx, - .start = adm8211_start, - .stop = adm8211_stop, - .add_interface = adm8211_add_interface, - .remove_interface = adm8211_remove_interface, - .config = adm8211_config, - .bss_info_changed = adm8211_bss_info_changed, - .prepare_multicast = adm8211_prepare_multicast, - .configure_filter = adm8211_configure_filter, - .get_stats = adm8211_get_stats, - .get_tsf = adm8211_get_tsft -}; - -static int adm8211_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct ieee80211_hw *dev; - struct adm8211_priv *priv; - unsigned long mem_addr, mem_len; - unsigned int io_addr, io_len; - int err; - u32 reg; - u8 perm_addr[ETH_ALEN]; - - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR "%s (adm8211): Cannot enable new PCI device\n", - pci_name(pdev)); - return err; - } - - io_addr = pci_resource_start(pdev, 0); - io_len = pci_resource_len(pdev, 0); - mem_addr = pci_resource_start(pdev, 1); - mem_len = pci_resource_len(pdev, 1); - if (io_len < 256 || mem_len < 1024) { - printk(KERN_ERR "%s (adm8211): Too short PCI resources\n", - pci_name(pdev)); - goto err_disable_pdev; - } - - - /* check signature */ - pci_read_config_dword(pdev, 0x80 /* CR32 */, ®); - if (reg != ADM8211_SIG1 && reg != ADM8211_SIG2) { - printk(KERN_ERR "%s (adm8211): Invalid signature (0x%x)\n", - pci_name(pdev), reg); - goto err_disable_pdev; - } - - err = pci_request_regions(pdev, "adm8211"); - if (err) { - printk(KERN_ERR "%s (adm8211): Cannot obtain PCI resources\n", - pci_name(pdev)); - return err; /* someone else grabbed it? don't disable it */ - } - - if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) || - pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { - printk(KERN_ERR "%s (adm8211): No suitable DMA available\n", - pci_name(pdev)); - goto err_free_reg; - } - - pci_set_master(pdev); - - dev = ieee80211_alloc_hw(sizeof(*priv), &adm8211_ops); - if (!dev) { - printk(KERN_ERR "%s (adm8211): ieee80211 alloc failed\n", - pci_name(pdev)); - err = -ENOMEM; - goto err_free_reg; - } - priv = dev->priv; - priv->pdev = pdev; - - spin_lock_init(&priv->lock); - - SET_IEEE80211_DEV(dev, &pdev->dev); - - pci_set_drvdata(pdev, dev); - - priv->map = pci_iomap(pdev, 1, mem_len); - if (!priv->map) - priv->map = pci_iomap(pdev, 0, io_len); - - if (!priv->map) { - printk(KERN_ERR "%s (adm8211): Cannot map device memory\n", - pci_name(pdev)); - err = -ENOMEM; - goto err_free_dev; - } - - priv->rx_ring_size = rx_ring_size; - priv->tx_ring_size = tx_ring_size; - - if (adm8211_alloc_rings(dev)) { - printk(KERN_ERR "%s (adm8211): Cannot allocate TX/RX ring\n", - pci_name(pdev)); - goto err_iounmap; - } - - *(__le32 *)perm_addr = cpu_to_le32(ADM8211_CSR_READ(PAR0)); - *(__le16 *)&perm_addr[4] = - cpu_to_le16(ADM8211_CSR_READ(PAR1) & 0xFFFF); - - if (!is_valid_ether_addr(perm_addr)) { - printk(KERN_WARNING "%s (adm8211): Invalid hwaddr in EEPROM!\n", - pci_name(pdev)); - eth_random_addr(perm_addr); - } - SET_IEEE80211_PERM_ADDR(dev, perm_addr); - - dev->extra_tx_headroom = sizeof(struct adm8211_tx_hdr); - /* dev->flags = RX_INCLUDES_FCS in promisc mode */ - ieee80211_hw_set(dev, SIGNAL_UNSPEC); - dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - - dev->max_signal = 100; /* FIXME: find better value */ - - dev->queues = 1; /* ADM8211C supports more, maybe ADM8211B too */ - - priv->retry_limit = 3; - priv->ant_power = 0x40; - priv->tx_power = 0x40; - priv->lpf_cutoff = 0xFF; - priv->lnags_threshold = 0xFF; - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - - /* Power-on issue. EEPROM won't read correctly without */ - if (pdev->revision >= ADM8211_REV_BA) { - ADM8211_CSR_WRITE(FRCTL, 0); - ADM8211_CSR_READ(FRCTL); - ADM8211_CSR_WRITE(FRCTL, 1); - ADM8211_CSR_READ(FRCTL); - msleep(100); - } - - err = adm8211_read_eeprom(dev); - if (err) { - printk(KERN_ERR "%s (adm8211): Can't alloc eeprom buffer\n", - pci_name(pdev)); - goto err_free_desc; - } - - priv->channel = 1; - - dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - - err = ieee80211_register_hw(dev); - if (err) { - printk(KERN_ERR "%s (adm8211): Cannot register device\n", - pci_name(pdev)); - goto err_free_eeprom; - } - - wiphy_info(dev->wiphy, "hwaddr %pM, Rev 0x%02x\n", - dev->wiphy->perm_addr, pdev->revision); - - return 0; - - err_free_eeprom: - kfree(priv->eeprom); - - err_free_desc: - pci_free_consistent(pdev, - sizeof(struct adm8211_desc) * priv->rx_ring_size + - sizeof(struct adm8211_desc) * priv->tx_ring_size, - priv->rx_ring, priv->rx_ring_dma); - kfree(priv->rx_buffers); - - err_iounmap: - pci_iounmap(pdev, priv->map); - - err_free_dev: - ieee80211_free_hw(dev); - - err_free_reg: - pci_release_regions(pdev); - - err_disable_pdev: - pci_disable_device(pdev); - return err; -} - - -static void adm8211_remove(struct pci_dev *pdev) -{ - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - struct adm8211_priv *priv; - - if (!dev) - return; - - ieee80211_unregister_hw(dev); - - priv = dev->priv; - - pci_free_consistent(pdev, - sizeof(struct adm8211_desc) * priv->rx_ring_size + - sizeof(struct adm8211_desc) * priv->tx_ring_size, - priv->rx_ring, priv->rx_ring_dma); - - kfree(priv->rx_buffers); - kfree(priv->eeprom); - pci_iounmap(pdev, priv->map); - pci_release_regions(pdev); - pci_disable_device(pdev); - ieee80211_free_hw(dev); -} - - -#ifdef CONFIG_PM -static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state) -{ - pci_save_state(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; -} - -static int adm8211_resume(struct pci_dev *pdev) -{ - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - return 0; -} -#endif /* CONFIG_PM */ - - -MODULE_DEVICE_TABLE(pci, adm8211_pci_id_table); - -/* TODO: implement enable_wake */ -static struct pci_driver adm8211_driver = { - .name = "adm8211", - .id_table = adm8211_pci_id_table, - .probe = adm8211_probe, - .remove = adm8211_remove, -#ifdef CONFIG_PM - .suspend = adm8211_suspend, - .resume = adm8211_resume, -#endif /* CONFIG_PM */ -}; - -module_pci_driver(adm8211_driver); diff --git a/drivers/net/wireless/adm8211.h b/drivers/net/wireless/adm8211.h deleted file mode 100644 index bbc10b1cde87..000000000000 --- a/drivers/net/wireless/adm8211.h +++ /dev/null @@ -1,602 +0,0 @@ -#ifndef ADM8211_H -#define ADM8211_H - -/* ADM8211 Registers */ - -/* CR32 (SIG) signature */ -#define ADM8211_SIG1 0x82011317 /* ADM8211A */ -#define ADM8211_SIG2 0x82111317 /* ADM8211B/ADM8211C */ - -#define ADM8211_CSR_READ(r) ioread32(&priv->map->r) -#define ADM8211_CSR_WRITE(r, val) iowrite32((val), &priv->map->r) - -/* CSR (Host Control and Status Registers) */ -struct adm8211_csr { - __le32 PAR; /* 0x00 CSR0 */ - __le32 FRCTL; /* 0x04 CSR0A */ - __le32 TDR; /* 0x08 CSR1 */ - __le32 WTDP; /* 0x0C CSR1A */ - __le32 RDR; /* 0x10 CSR2 */ - __le32 WRDP; /* 0x14 CSR2A */ - __le32 RDB; /* 0x18 CSR3 */ - __le32 TDBH; /* 0x1C CSR3A */ - __le32 TDBD; /* 0x20 CSR4 */ - __le32 TDBP; /* 0x24 CSR4A */ - __le32 STSR; /* 0x28 CSR5 */ - __le32 TDBB; /* 0x2C CSR5A */ - __le32 NAR; /* 0x30 CSR6 */ - __le32 CSR6A; /* reserved */ - __le32 IER; /* 0x38 CSR7 */ - __le32 TKIPSCEP; /* 0x3C CSR7A */ - __le32 LPC; /* 0x40 CSR8 */ - __le32 CSR_TEST1; /* 0x44 CSR8A */ - __le32 SPR; /* 0x48 CSR9 */ - __le32 CSR_TEST0; /* 0x4C CSR9A */ - __le32 WCSR; /* 0x50 CSR10 */ - __le32 WPDR; /* 0x54 CSR10A */ - __le32 GPTMR; /* 0x58 CSR11 */ - __le32 GPIO; /* 0x5C CSR11A */ - __le32 BBPCTL; /* 0x60 CSR12 */ - __le32 SYNCTL; /* 0x64 CSR12A */ - __le32 PLCPHD; /* 0x68 CSR13 */ - __le32 MMIWA; /* 0x6C CSR13A */ - __le32 MMIRD0; /* 0x70 CSR14 */ - __le32 MMIRD1; /* 0x74 CSR14A */ - __le32 TXBR; /* 0x78 CSR15 */ - __le32 SYNDATA; /* 0x7C CSR15A */ - __le32 ALCS; /* 0x80 CSR16 */ - __le32 TOFS2; /* 0x84 CSR17 */ - __le32 CMDR; /* 0x88 CSR18 */ - __le32 PCIC; /* 0x8C CSR19 */ - __le32 PMCSR; /* 0x90 CSR20 */ - __le32 PAR0; /* 0x94 CSR21 */ - __le32 PAR1; /* 0x98 CSR22 */ - __le32 MAR0; /* 0x9C CSR23 */ - __le32 MAR1; /* 0xA0 CSR24 */ - __le32 ATIMDA0; /* 0xA4 CSR25 */ - __le32 ABDA1; /* 0xA8 CSR26 */ - __le32 BSSID0; /* 0xAC CSR27 */ - __le32 TXLMT; /* 0xB0 CSR28 */ - __le32 MIBCNT; /* 0xB4 CSR29 */ - __le32 BCNT; /* 0xB8 CSR30 */ - __le32 TSFTH; /* 0xBC CSR31 */ - __le32 TSC; /* 0xC0 CSR32 */ - __le32 SYNRF; /* 0xC4 CSR33 */ - __le32 BPLI; /* 0xC8 CSR34 */ - __le32 CAP0; /* 0xCC CSR35 */ - __le32 CAP1; /* 0xD0 CSR36 */ - __le32 RMD; /* 0xD4 CSR37 */ - __le32 CFPP; /* 0xD8 CSR38 */ - __le32 TOFS0; /* 0xDC CSR39 */ - __le32 TOFS1; /* 0xE0 CSR40 */ - __le32 IFST; /* 0xE4 CSR41 */ - __le32 RSPT; /* 0xE8 CSR42 */ - __le32 TSFTL; /* 0xEC CSR43 */ - __le32 WEPCTL; /* 0xF0 CSR44 */ - __le32 WESK; /* 0xF4 CSR45 */ - __le32 WEPCNT; /* 0xF8 CSR46 */ - __le32 MACTEST; /* 0xFC CSR47 */ - __le32 FER; /* 0x100 */ - __le32 FEMR; /* 0x104 */ - __le32 FPSR; /* 0x108 */ - __le32 FFER; /* 0x10C */ -} __packed; - -/* CSR0 - PAR (PCI Address Register) */ -#define ADM8211_PAR_MWIE (1 << 24) -#define ADM8211_PAR_MRLE (1 << 23) -#define ADM8211_PAR_MRME (1 << 21) -#define ADM8211_PAR_RAP ((1 << 18) | (1 << 17)) -#define ADM8211_PAR_CAL ((1 << 15) | (1 << 14)) -#define ADM8211_PAR_PBL 0x00003f00 -#define ADM8211_PAR_BLE (1 << 7) -#define ADM8211_PAR_DSL 0x0000007c -#define ADM8211_PAR_BAR (1 << 1) -#define ADM8211_PAR_SWR (1 << 0) - -/* CSR1 - FRCTL (Frame Control Register) */ -#define ADM8211_FRCTL_PWRMGT (1 << 31) -#define ADM8211_FRCTL_MAXPSP (1 << 27) -#define ADM8211_FRCTL_DRVPRSP (1 << 26) -#define ADM8211_FRCTL_DRVBCON (1 << 25) -#define ADM8211_FRCTL_AID 0x0000ffff -#define ADM8211_FRCTL_AID_ON 0x0000c000 - -/* CSR5 - STSR (Status Register) */ -#define ADM8211_STSR_PCF (1 << 31) -#define ADM8211_STSR_BCNTC (1 << 30) -#define ADM8211_STSR_GPINT (1 << 29) -#define ADM8211_STSR_LinkOff (1 << 28) -#define ADM8211_STSR_ATIMTC (1 << 27) -#define ADM8211_STSR_TSFTF (1 << 26) -#define ADM8211_STSR_TSCZ (1 << 25) -#define ADM8211_STSR_LinkOn (1 << 24) -#define ADM8211_STSR_SQL (1 << 23) -#define ADM8211_STSR_WEPTD (1 << 22) -#define ADM8211_STSR_ATIME (1 << 21) -#define ADM8211_STSR_TBTT (1 << 20) -#define ADM8211_STSR_NISS (1 << 16) -#define ADM8211_STSR_AISS (1 << 15) -#define ADM8211_STSR_TEIS (1 << 14) -#define ADM8211_STSR_FBE (1 << 13) -#define ADM8211_STSR_REIS (1 << 12) -#define ADM8211_STSR_GPTT (1 << 11) -#define ADM8211_STSR_RPS (1 << 8) -#define ADM8211_STSR_RDU (1 << 7) -#define ADM8211_STSR_RCI (1 << 6) -#define ADM8211_STSR_TUF (1 << 5) -#define ADM8211_STSR_TRT (1 << 4) -#define ADM8211_STSR_TLT (1 << 3) -#define ADM8211_STSR_TDU (1 << 2) -#define ADM8211_STSR_TPS (1 << 1) -#define ADM8211_STSR_TCI (1 << 0) - -/* CSR6 - NAR (Network Access Register) */ -#define ADM8211_NAR_TXCF (1 << 31) -#define ADM8211_NAR_HF (1 << 30) -#define ADM8211_NAR_UTR (1 << 29) -#define ADM8211_NAR_SQ (1 << 28) -#define ADM8211_NAR_CFP (1 << 27) -#define ADM8211_NAR_SF (1 << 21) -#define ADM8211_NAR_TR ((1 << 15) | (1 << 14)) -#define ADM8211_NAR_ST (1 << 13) -#define ADM8211_NAR_OM ((1 << 11) | (1 << 10)) -#define ADM8211_NAR_MM (1 << 7) -#define ADM8211_NAR_PR (1 << 6) -#define ADM8211_NAR_EA (1 << 5) -#define ADM8211_NAR_PB (1 << 3) -#define ADM8211_NAR_STPDMA (1 << 2) -#define ADM8211_NAR_SR (1 << 1) -#define ADM8211_NAR_CTX (1 << 0) - -#define ADM8211_IDLE() \ -do { \ - if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) { \ - ADM8211_CSR_WRITE(NAR, priv->nar & \ - ~(ADM8211_NAR_SR | ADM8211_NAR_ST));\ - ADM8211_CSR_READ(NAR); \ - msleep(20); \ - } \ -} while (0) - -#define ADM8211_IDLE_RX() \ -do { \ - if (priv->nar & ADM8211_NAR_SR) { \ - ADM8211_CSR_WRITE(NAR, priv->nar & ~ADM8211_NAR_SR); \ - ADM8211_CSR_READ(NAR); \ - mdelay(20); \ - } \ -} while (0) - -#define ADM8211_RESTORE() \ -do { \ - if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) \ - ADM8211_CSR_WRITE(NAR, priv->nar); \ -} while (0) - -/* CSR7 - IER (Interrupt Enable Register) */ -#define ADM8211_IER_PCFIE (1 << 31) -#define ADM8211_IER_BCNTCIE (1 << 30) -#define ADM8211_IER_GPIE (1 << 29) -#define ADM8211_IER_LinkOffIE (1 << 28) -#define ADM8211_IER_ATIMTCIE (1 << 27) -#define ADM8211_IER_TSFTFIE (1 << 26) -#define ADM8211_IER_TSCZE (1 << 25) -#define ADM8211_IER_LinkOnIE (1 << 24) -#define ADM8211_IER_SQLIE (1 << 23) -#define ADM8211_IER_WEPIE (1 << 22) -#define ADM8211_IER_ATIMEIE (1 << 21) -#define ADM8211_IER_TBTTIE (1 << 20) -#define ADM8211_IER_NIE (1 << 16) -#define ADM8211_IER_AIE (1 << 15) -#define ADM8211_IER_TEIE (1 << 14) -#define ADM8211_IER_FBEIE (1 << 13) -#define ADM8211_IER_REIE (1 << 12) -#define ADM8211_IER_GPTIE (1 << 11) -#define ADM8211_IER_RSIE (1 << 8) -#define ADM8211_IER_RUIE (1 << 7) -#define ADM8211_IER_RCIE (1 << 6) -#define ADM8211_IER_TUIE (1 << 5) -#define ADM8211_IER_TRTIE (1 << 4) -#define ADM8211_IER_TLTTIE (1 << 3) -#define ADM8211_IER_TDUIE (1 << 2) -#define ADM8211_IER_TPSIE (1 << 1) -#define ADM8211_IER_TCIE (1 << 0) - -/* CSR9 - SPR (Serial Port Register) */ -#define ADM8211_SPR_SRS (1 << 11) -#define ADM8211_SPR_SDO (1 << 3) -#define ADM8211_SPR_SDI (1 << 2) -#define ADM8211_SPR_SCLK (1 << 1) -#define ADM8211_SPR_SCS (1 << 0) - -/* CSR9A - CSR_TEST0 */ -#define ADM8211_CSR_TEST0_EPNE (1 << 18) -#define ADM8211_CSR_TEST0_EPSNM (1 << 17) -#define ADM8211_CSR_TEST0_EPTYP (1 << 16) -#define ADM8211_CSR_TEST0_EPRLD (1 << 15) - -/* CSR10 - WCSR (Wake-up Control/Status Register) */ -#define ADM8211_WCSR_CRCT (1 << 30) -#define ADM8211_WCSR_TSFTWE (1 << 20) -#define ADM8211_WCSR_TIMWE (1 << 19) -#define ADM8211_WCSR_ATIMWE (1 << 18) -#define ADM8211_WCSR_KEYWE (1 << 17) -#define ADM8211_WCSR_MPRE (1 << 9) -#define ADM8211_WCSR_LSOE (1 << 8) -#define ADM8211_WCSR_KEYUP (1 << 6) -#define ADM8211_WCSR_TSFTW (1 << 5) -#define ADM8211_WCSR_TIMW (1 << 4) -#define ADM8211_WCSR_ATIMW (1 << 3) -#define ADM8211_WCSR_MPR (1 << 1) -#define ADM8211_WCSR_LSO (1 << 0) - -/* CSR11A - GPIO */ -#define ADM8211_CSR_GPIO_EN5 (1 << 17) -#define ADM8211_CSR_GPIO_EN4 (1 << 16) -#define ADM8211_CSR_GPIO_EN3 (1 << 15) -#define ADM8211_CSR_GPIO_EN2 (1 << 14) -#define ADM8211_CSR_GPIO_EN1 (1 << 13) -#define ADM8211_CSR_GPIO_EN0 (1 << 12) -#define ADM8211_CSR_GPIO_O5 (1 << 11) -#define ADM8211_CSR_GPIO_O4 (1 << 10) -#define ADM8211_CSR_GPIO_O3 (1 << 9) -#define ADM8211_CSR_GPIO_O2 (1 << 8) -#define ADM8211_CSR_GPIO_O1 (1 << 7) -#define ADM8211_CSR_GPIO_O0 (1 << 6) -#define ADM8211_CSR_GPIO_IN 0x0000003f - -/* CSR12 - BBPCTL (BBP Control port) */ -#define ADM8211_BBPCTL_MMISEL (1 << 31) -#define ADM8211_BBPCTL_SPICADD (0x7F << 24) -#define ADM8211_BBPCTL_RF3000 (0x20 << 24) -#define ADM8211_BBPCTL_TXCE (1 << 23) -#define ADM8211_BBPCTL_RXCE (1 << 22) -#define ADM8211_BBPCTL_CCAP (1 << 21) -#define ADM8211_BBPCTL_TYPE 0x001c0000 -#define ADM8211_BBPCTL_WR (1 << 17) -#define ADM8211_BBPCTL_RD (1 << 16) -#define ADM8211_BBPCTL_ADDR 0x0000ff00 -#define ADM8211_BBPCTL_DATA 0x000000ff - -/* CSR12A - SYNCTL (Synthesizer Control port) */ -#define ADM8211_SYNCTL_WR (1 << 31) -#define ADM8211_SYNCTL_RD (1 << 30) -#define ADM8211_SYNCTL_CS0 (1 << 29) -#define ADM8211_SYNCTL_CS1 (1 << 28) -#define ADM8211_SYNCTL_CAL (1 << 27) -#define ADM8211_SYNCTL_SELCAL (1 << 26) -#define ADM8211_SYNCTL_RFtype ((1 << 24) | (1 << 23) | (1 << 22)) -#define ADM8211_SYNCTL_RFMD (1 << 22) -#define ADM8211_SYNCTL_GENERAL (0x7 << 22) -/* SYNCTL 21:0 Data (Si4126: 18-bit data, 4-bit address) */ - -/* CSR18 - CMDR (Command Register) */ -#define ADM8211_CMDR_PM (1 << 19) -#define ADM8211_CMDR_APM (1 << 18) -#define ADM8211_CMDR_RTE (1 << 4) -#define ADM8211_CMDR_DRT ((1 << 3) | (1 << 2)) -#define ADM8211_CMDR_DRT_8DW (0x0 << 2) -#define ADM8211_CMDR_DRT_16DW (0x1 << 2) -#define ADM8211_CMDR_DRT_SF (0x2 << 2) - -/* CSR33 - SYNRF (SYNRF direct control) */ -#define ADM8211_SYNRF_SELSYN (1 << 31) -#define ADM8211_SYNRF_SELRF (1 << 30) -#define ADM8211_SYNRF_LERF (1 << 29) -#define ADM8211_SYNRF_LEIF (1 << 28) -#define ADM8211_SYNRF_SYNCLK (1 << 27) -#define ADM8211_SYNRF_SYNDATA (1 << 26) -#define ADM8211_SYNRF_PE1 (1 << 25) -#define ADM8211_SYNRF_PE2 (1 << 24) -#define ADM8211_SYNRF_PA_PE (1 << 23) -#define ADM8211_SYNRF_TR_SW (1 << 22) -#define ADM8211_SYNRF_TR_SWN (1 << 21) -#define ADM8211_SYNRF_RADIO (1 << 20) -#define ADM8211_SYNRF_CAL_EN (1 << 19) -#define ADM8211_SYNRF_PHYRST (1 << 18) - -#define ADM8211_SYNRF_IF_SELECT_0 (1 << 31) -#define ADM8211_SYNRF_IF_SELECT_1 ((1 << 31) | (1 << 28)) -#define ADM8211_SYNRF_WRITE_SYNDATA_0 (1 << 31) -#define ADM8211_SYNRF_WRITE_SYNDATA_1 ((1 << 31) | (1 << 26)) -#define ADM8211_SYNRF_WRITE_CLOCK_0 (1 << 31) -#define ADM8211_SYNRF_WRITE_CLOCK_1 ((1 << 31) | (1 << 27)) - -/* CSR44 - WEPCTL (WEP Control) */ -#define ADM8211_WEPCTL_WEPENABLE (1 << 31) -#define ADM8211_WEPCTL_WPAENABLE (1 << 30) -#define ADM8211_WEPCTL_CURRENT_TABLE (1 << 29) -#define ADM8211_WEPCTL_TABLE_WR (1 << 28) -#define ADM8211_WEPCTL_TABLE_RD (1 << 27) -#define ADM8211_WEPCTL_WEPRXBYP (1 << 25) -#define ADM8211_WEPCTL_SEL_WEPTABLE (1 << 23) -#define ADM8211_WEPCTL_ADDR (0x000001ff) - -/* CSR45 - WESK (Data Entry for Share/Individual Key) */ -#define ADM8211_WESK_DATA (0x0000ffff) - -/* FER (Function Event Register) */ -#define ADM8211_FER_INTR_EV_ENT (1 << 15) - - -/* Si4126 RF Synthesizer - Control Registers */ -#define SI4126_MAIN_CONF 0 -#define SI4126_PHASE_DET_GAIN 1 -#define SI4126_POWERDOWN 2 -#define SI4126_RF1_N_DIV 3 /* only Si4136 */ -#define SI4126_RF2_N_DIV 4 -#define SI4126_IF_N_DIV 5 -#define SI4126_RF1_R_DIV 6 /* only Si4136 */ -#define SI4126_RF2_R_DIV 7 -#define SI4126_IF_R_DIV 8 - -/* Main Configuration */ -#define SI4126_MAIN_XINDIV2 (1 << 6) -#define SI4126_MAIN_IFDIV ((1 << 11) | (1 << 10)) -/* Powerdown */ -#define SI4126_POWERDOWN_PDIB (1 << 1) -#define SI4126_POWERDOWN_PDRB (1 << 0) - - -/* RF3000 BBP - Control Port Registers */ -/* 0x00 - reserved */ -#define RF3000_MODEM_CTRL__RX_STATUS 0x01 -#define RF3000_CCA_CTRL 0x02 -#define RF3000_DIVERSITY__RSSI 0x03 -#define RF3000_RX_SIGNAL_FIELD 0x04 -#define RF3000_RX_LEN_MSB 0x05 -#define RF3000_RX_LEN_LSB 0x06 -#define RF3000_RX_SERVICE_FIELD 0x07 -#define RF3000_TX_VAR_GAIN__TX_LEN_EXT 0x11 -#define RF3000_TX_LEN_MSB 0x12 -#define RF3000_TX_LEN_LSB 0x13 -#define RF3000_LOW_GAIN_CALIB 0x14 -#define RF3000_HIGH_GAIN_CALIB 0x15 - -/* ADM8211 revisions */ -#define ADM8211_REV_AB 0x11 -#define ADM8211_REV_AF 0x15 -#define ADM8211_REV_BA 0x20 -#define ADM8211_REV_CA 0x30 - -struct adm8211_desc { - __le32 status; - __le32 length; - __le32 buffer1; - __le32 buffer2; -}; - -#define RDES0_STATUS_OWN (1 << 31) -#define RDES0_STATUS_ES (1 << 30) -#define RDES0_STATUS_SQL (1 << 29) -#define RDES0_STATUS_DE (1 << 28) -#define RDES0_STATUS_FS (1 << 27) -#define RDES0_STATUS_LS (1 << 26) -#define RDES0_STATUS_PCF (1 << 25) -#define RDES0_STATUS_SFDE (1 << 24) -#define RDES0_STATUS_SIGE (1 << 23) -#define RDES0_STATUS_CRC16E (1 << 22) -#define RDES0_STATUS_RXTOE (1 << 21) -#define RDES0_STATUS_CRC32E (1 << 20) -#define RDES0_STATUS_ICVE (1 << 19) -#define RDES0_STATUS_DA1 (1 << 17) -#define RDES0_STATUS_DA0 (1 << 16) -#define RDES0_STATUS_RXDR ((1 << 15) | (1 << 14) | (1 << 13) | (1 << 12)) -#define RDES0_STATUS_FL (0x00000fff) - -#define RDES1_CONTROL_RER (1 << 25) -#define RDES1_CONTROL_RCH (1 << 24) -#define RDES1_CONTROL_RBS2 (0x00fff000) -#define RDES1_CONTROL_RBS1 (0x00000fff) - -#define RDES1_STATUS_RSSI (0x0000007f) - - -#define TDES0_CONTROL_OWN (1 << 31) -#define TDES0_CONTROL_DONE (1 << 30) -#define TDES0_CONTROL_TXDR (0x0ff00000) - -#define TDES0_STATUS_OWN (1 << 31) -#define TDES0_STATUS_DONE (1 << 30) -#define TDES0_STATUS_ES (1 << 29) -#define TDES0_STATUS_TLT (1 << 28) -#define TDES0_STATUS_TRT (1 << 27) -#define TDES0_STATUS_TUF (1 << 26) -#define TDES0_STATUS_TRO (1 << 25) -#define TDES0_STATUS_SOFBR (1 << 24) -#define TDES0_STATUS_ACR (0x00000fff) - -#define TDES1_CONTROL_IC (1 << 31) -#define TDES1_CONTROL_LS (1 << 30) -#define TDES1_CONTROL_FS (1 << 29) -#define TDES1_CONTROL_TER (1 << 25) -#define TDES1_CONTROL_TCH (1 << 24) -#define TDES1_CONTROL_RBS2 (0x00fff000) -#define TDES1_CONTROL_RBS1 (0x00000fff) - -/* SRAM offsets */ -#define ADM8211_SRAM(x) (priv->pdev->revision < ADM8211_REV_BA ? \ - ADM8211_SRAM_A_ ## x : ADM8211_SRAM_B_ ## x) - -#define ADM8211_SRAM_INDIV_KEY 0x0000 -#define ADM8211_SRAM_A_SHARE_KEY 0x0160 -#define ADM8211_SRAM_B_SHARE_KEY 0x00c0 - -#define ADM8211_SRAM_A_SSID 0x0180 -#define ADM8211_SRAM_B_SSID 0x00d4 -#define ADM8211_SRAM_SSID ADM8211_SRAM(SSID) - -#define ADM8211_SRAM_A_SUPP_RATE 0x0191 -#define ADM8211_SRAM_B_SUPP_RATE 0x00dd -#define ADM8211_SRAM_SUPP_RATE ADM8211_SRAM(SUPP_RATE) - -#define ADM8211_SRAM_A_SIZE 0x0200 -#define ADM8211_SRAM_B_SIZE 0x01c0 -#define ADM8211_SRAM_SIZE ADM8211_SRAM(SIZE) - -struct adm8211_rx_ring_info { - struct sk_buff *skb; - dma_addr_t mapping; -}; - -struct adm8211_tx_ring_info { - struct sk_buff *skb; - dma_addr_t mapping; - size_t hdrlen; -}; - -#define PLCP_SIGNAL_1M 0x0a -#define PLCP_SIGNAL_2M 0x14 -#define PLCP_SIGNAL_5M5 0x37 -#define PLCP_SIGNAL_11M 0x6e - -struct adm8211_tx_hdr { - u8 da[6]; - u8 signal; /* PLCP signal / TX rate in 100 Kbps */ - u8 service; - __le16 frame_body_size; - __le16 frame_control; - __le16 plcp_frag_tail_len; - __le16 plcp_frag_head_len; - __le16 dur_frag_tail; - __le16 dur_frag_head; - u8 addr4[6]; - -#define ADM8211_TXHDRCTL_SHORT_PREAMBLE (1 << 0) -#define ADM8211_TXHDRCTL_MORE_FRAG (1 << 1) -#define ADM8211_TXHDRCTL_MORE_DATA (1 << 2) -#define ADM8211_TXHDRCTL_FRAG_NO (1 << 3) /* ? */ -#define ADM8211_TXHDRCTL_ENABLE_RTS (1 << 4) -#define ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE (1 << 5) -#define ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER (1 << 15) /* ? */ - __le16 header_control; - __le16 frag; - u8 reserved_0; - u8 retry_limit; - - u32 wep2key0; - u32 wep2key1; - u32 wep2key2; - u32 wep2key3; - - u8 keyid; - u8 entry_control; // huh?? - u16 reserved_1; - u32 reserved_2; -} __packed; - - -#define RX_COPY_BREAK 128 -#define RX_PKT_SIZE 2500 - -struct adm8211_eeprom { - __le16 signature; /* 0x00 */ - u8 major_version; /* 0x02 */ - u8 minor_version; /* 0x03 */ - u8 reserved_1[4]; /* 0x04 */ - u8 hwaddr[6]; /* 0x08 */ - u8 reserved_2[8]; /* 0x1E */ - __le16 cr49; /* 0x16 */ - u8 cr03; /* 0x18 */ - u8 cr28; /* 0x19 */ - u8 cr29; /* 0x1A */ - u8 country_code; /* 0x1B */ - -/* specific bbp types */ -#define ADM8211_BBP_RFMD3000 0x00 -#define ADM8211_BBP_RFMD3002 0x01 -#define ADM8211_BBP_ADM8011 0x04 - u8 specific_bbptype; /* 0x1C */ - u8 specific_rftype; /* 0x1D */ - u8 reserved_3[2]; /* 0x1E */ - __le16 device_id; /* 0x20 */ - __le16 vendor_id; /* 0x22 */ - __le16 subsystem_id; /* 0x24 */ - __le16 subsystem_vendor_id; /* 0x26 */ - u8 maxlat; /* 0x28 */ - u8 mingnt; /* 0x29 */ - __le16 cis_pointer_low; /* 0x2A */ - __le16 cis_pointer_high; /* 0x2C */ - __le16 csr18; /* 0x2E */ - u8 reserved_4[16]; /* 0x30 */ - u8 d1_pwrdara; /* 0x40 */ - u8 d0_pwrdara; /* 0x41 */ - u8 d3_pwrdara; /* 0x42 */ - u8 d2_pwrdara; /* 0x43 */ - u8 antenna_power[14]; /* 0x44 */ - __le16 cis_wordcnt; /* 0x52 */ - u8 tx_power[14]; /* 0x54 */ - u8 lpf_cutoff[14]; /* 0x62 */ - u8 lnags_threshold[14]; /* 0x70 */ - __le16 checksum; /* 0x7E */ - u8 cis_data[0]; /* 0x80, 384 bytes */ -} __packed; - -struct adm8211_priv { - struct pci_dev *pdev; - spinlock_t lock; - struct adm8211_csr __iomem *map; - struct adm8211_desc *rx_ring; - struct adm8211_desc *tx_ring; - dma_addr_t rx_ring_dma; - dma_addr_t tx_ring_dma; - struct adm8211_rx_ring_info *rx_buffers; - struct adm8211_tx_ring_info *tx_buffers; - unsigned int rx_ring_size, tx_ring_size; - unsigned int cur_tx, dirty_tx, cur_rx; - - struct ieee80211_low_level_stats stats; - struct ieee80211_supported_band band; - struct ieee80211_channel channels[14]; - int mode; - - int channel; - u8 bssid[ETH_ALEN]; - - u8 soft_rx_crc; - u8 retry_limit; - - u8 ant_power; - u8 tx_power; - u8 lpf_cutoff; - u8 lnags_threshold; - struct adm8211_eeprom *eeprom; - size_t eeprom_len; - - u32 nar; - -#define ADM8211_TYPE_INTERSIL 0x00 -#define ADM8211_TYPE_RFMD 0x01 -#define ADM8211_TYPE_MARVEL 0x02 -#define ADM8211_TYPE_AIROHA 0x03 -#define ADM8211_TYPE_ADMTEK 0x05 - unsigned int rf_type:3; - unsigned int bbp_type:3; - - u8 specific_bbptype; - enum { - ADM8211_RFMD2948 = 0x0, - ADM8211_RFMD2958 = 0x1, - ADM8211_RFMD2958_RF3000_CONTROL_POWER = 0x2, - ADM8211_MAX2820 = 0x8, - ADM8211_AL2210L = 0xC, /* Airoha */ - } transceiver_type; -}; - -struct ieee80211_chan_range { - u8 min; - u8 max; -}; - -static const struct ieee80211_chan_range cranges[] = { - {1, 11}, /* FCC */ - {1, 11}, /* IC */ - {1, 13}, /* ETSI */ - {10, 11}, /* SPAIN */ - {10, 13}, /* FRANCE */ - {14, 14}, /* MMK */ - {1, 14}, /* MMK2 */ -}; - -#endif /* ADM8211_H */ diff --git a/drivers/net/wireless/admtek/Kconfig b/drivers/net/wireless/admtek/Kconfig new file mode 100644 index 000000000000..d5a2dc728078 --- /dev/null +++ b/drivers/net/wireless/admtek/Kconfig @@ -0,0 +1,41 @@ +config WLAN_VENDOR_ADMTEK + bool "ADMtek devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_ADMTEK + +config ADM8211 + tristate "ADMtek ADM8211 support" + depends on MAC80211 && PCI + select CRC32 + select EEPROM_93CX6 + ---help--- + This driver is for ADM8211A, ADM8211B, and ADM8211C based cards. + These are PCI/mini-PCI/Cardbus 802.11b chips found in cards such as: + + Xterasys Cardbus XN-2411b + Blitz NetWave Point PC + TrendNet 221pc + Belkin F5D6001 + SMC 2635W + Linksys WPC11 v1 + Fiberline FL-WL-200X + 3com Office Connect (3CRSHPW796) + Corega WLPCIB-11 + SMC 2602W V2 EU + D-Link DWL-520 Revision C + + However, some of these cards have been replaced with other chips + like the RTL8180L (Xterasys Cardbus XN-2411b, Belkin F5D6001) or + the Ralink RT2400 (SMC2635W) without a model number change. + + Thanks to Infineon-ADMtek for their support of this driver. + +endif # WLAN_VENDOR_ADMTEK diff --git a/drivers/net/wireless/admtek/Makefile b/drivers/net/wireless/admtek/Makefile new file mode 100644 index 000000000000..9cca7e571cdd --- /dev/null +++ b/drivers/net/wireless/admtek/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ADM8211) += adm8211.o diff --git a/drivers/net/wireless/admtek/adm8211.c b/drivers/net/wireless/admtek/adm8211.c new file mode 100644 index 000000000000..15f057ed41ad --- /dev/null +++ b/drivers/net/wireless/admtek/adm8211.c @@ -0,0 +1,1993 @@ + +/* + * Linux device driver for ADMtek ADM8211 (IEEE 802.11b MAC/BBP) + * + * Copyright (c) 2003, Jouni Malinen + * Copyright (c) 2004-2007, Michael Wu + * Some parts copyright (c) 2003 by David Young + * and used with permission. + * + * Much thanks to Infineon-ADMtek for their support of this driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adm8211.h" + +MODULE_AUTHOR("Michael Wu "); +MODULE_AUTHOR("Jouni Malinen "); +MODULE_DESCRIPTION("Driver for IEEE 802.11b wireless cards based on ADMtek ADM8211"); +MODULE_SUPPORTED_DEVICE("ADM8211"); +MODULE_LICENSE("GPL"); + +static unsigned int tx_ring_size __read_mostly = 16; +static unsigned int rx_ring_size __read_mostly = 16; + +module_param(tx_ring_size, uint, 0); +module_param(rx_ring_size, uint, 0); + +static const struct pci_device_id adm8211_pci_id_table[] = { + /* ADMtek ADM8211 */ + { PCI_DEVICE(0x10B7, 0x6000) }, /* 3Com 3CRSHPW796 */ + { PCI_DEVICE(0x1200, 0x8201) }, /* ? */ + { PCI_DEVICE(0x1317, 0x8201) }, /* ADM8211A */ + { PCI_DEVICE(0x1317, 0x8211) }, /* ADM8211B/C */ + { 0 } +}; + +static struct ieee80211_rate adm8211_rates[] = { + { .bitrate = 10, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 220, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, /* XX ?? */ +}; + +static const struct ieee80211_channel adm8211_channels[] = { + { .center_freq = 2412}, + { .center_freq = 2417}, + { .center_freq = 2422}, + { .center_freq = 2427}, + { .center_freq = 2432}, + { .center_freq = 2437}, + { .center_freq = 2442}, + { .center_freq = 2447}, + { .center_freq = 2452}, + { .center_freq = 2457}, + { .center_freq = 2462}, + { .center_freq = 2467}, + { .center_freq = 2472}, + { .center_freq = 2484}, +}; + + +static void adm8211_eeprom_register_read(struct eeprom_93cx6 *eeprom) +{ + struct adm8211_priv *priv = eeprom->data; + u32 reg = ADM8211_CSR_READ(SPR); + + eeprom->reg_data_in = reg & ADM8211_SPR_SDI; + eeprom->reg_data_out = reg & ADM8211_SPR_SDO; + eeprom->reg_data_clock = reg & ADM8211_SPR_SCLK; + eeprom->reg_chip_select = reg & ADM8211_SPR_SCS; +} + +static void adm8211_eeprom_register_write(struct eeprom_93cx6 *eeprom) +{ + struct adm8211_priv *priv = eeprom->data; + u32 reg = 0x4000 | ADM8211_SPR_SRS; + + if (eeprom->reg_data_in) + reg |= ADM8211_SPR_SDI; + if (eeprom->reg_data_out) + reg |= ADM8211_SPR_SDO; + if (eeprom->reg_data_clock) + reg |= ADM8211_SPR_SCLK; + if (eeprom->reg_chip_select) + reg |= ADM8211_SPR_SCS; + + ADM8211_CSR_WRITE(SPR, reg); + ADM8211_CSR_READ(SPR); /* eeprom_delay */ +} + +static int adm8211_read_eeprom(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int words, i; + struct ieee80211_chan_range chan_range; + u16 cr49; + struct eeprom_93cx6 eeprom = { + .data = priv, + .register_read = adm8211_eeprom_register_read, + .register_write = adm8211_eeprom_register_write + }; + + if (ADM8211_CSR_READ(CSR_TEST0) & ADM8211_CSR_TEST0_EPTYP) { + /* 256 * 16-bit = 512 bytes */ + eeprom.width = PCI_EEPROM_WIDTH_93C66; + words = 256; + } else { + /* 64 * 16-bit = 128 bytes */ + eeprom.width = PCI_EEPROM_WIDTH_93C46; + words = 64; + } + + priv->eeprom_len = words * 2; + priv->eeprom = kmalloc(priv->eeprom_len, GFP_KERNEL); + if (!priv->eeprom) + return -ENOMEM; + + eeprom_93cx6_multiread(&eeprom, 0, (__le16 *)priv->eeprom, words); + + cr49 = le16_to_cpu(priv->eeprom->cr49); + priv->rf_type = (cr49 >> 3) & 0x7; + switch (priv->rf_type) { + case ADM8211_TYPE_INTERSIL: + case ADM8211_TYPE_RFMD: + case ADM8211_TYPE_MARVEL: + case ADM8211_TYPE_AIROHA: + case ADM8211_TYPE_ADMTEK: + break; + + default: + if (priv->pdev->revision < ADM8211_REV_CA) + priv->rf_type = ADM8211_TYPE_RFMD; + else + priv->rf_type = ADM8211_TYPE_AIROHA; + + printk(KERN_WARNING "%s (adm8211): Unknown RFtype %d\n", + pci_name(priv->pdev), (cr49 >> 3) & 0x7); + } + + priv->bbp_type = cr49 & 0x7; + switch (priv->bbp_type) { + case ADM8211_TYPE_INTERSIL: + case ADM8211_TYPE_RFMD: + case ADM8211_TYPE_MARVEL: + case ADM8211_TYPE_AIROHA: + case ADM8211_TYPE_ADMTEK: + break; + default: + if (priv->pdev->revision < ADM8211_REV_CA) + priv->bbp_type = ADM8211_TYPE_RFMD; + else + priv->bbp_type = ADM8211_TYPE_ADMTEK; + + printk(KERN_WARNING "%s (adm8211): Unknown BBPtype: %d\n", + pci_name(priv->pdev), cr49 >> 3); + } + + if (priv->eeprom->country_code >= ARRAY_SIZE(cranges)) { + printk(KERN_WARNING "%s (adm8211): Invalid country code (%d)\n", + pci_name(priv->pdev), priv->eeprom->country_code); + + chan_range = cranges[2]; + } else + chan_range = cranges[priv->eeprom->country_code]; + + printk(KERN_DEBUG "%s (adm8211): Channel range: %d - %d\n", + pci_name(priv->pdev), (int)chan_range.min, (int)chan_range.max); + + BUILD_BUG_ON(sizeof(priv->channels) != sizeof(adm8211_channels)); + + memcpy(priv->channels, adm8211_channels, sizeof(priv->channels)); + priv->band.channels = priv->channels; + priv->band.n_channels = ARRAY_SIZE(adm8211_channels); + priv->band.bitrates = adm8211_rates; + priv->band.n_bitrates = ARRAY_SIZE(adm8211_rates); + + for (i = 1; i <= ARRAY_SIZE(adm8211_channels); i++) + if (i < chan_range.min || i > chan_range.max) + priv->channels[i - 1].flags |= IEEE80211_CHAN_DISABLED; + + switch (priv->eeprom->specific_bbptype) { + case ADM8211_BBP_RFMD3000: + case ADM8211_BBP_RFMD3002: + case ADM8211_BBP_ADM8011: + priv->specific_bbptype = priv->eeprom->specific_bbptype; + break; + + default: + if (priv->pdev->revision < ADM8211_REV_CA) + priv->specific_bbptype = ADM8211_BBP_RFMD3000; + else + priv->specific_bbptype = ADM8211_BBP_ADM8011; + + printk(KERN_WARNING "%s (adm8211): Unknown specific BBP: %d\n", + pci_name(priv->pdev), priv->eeprom->specific_bbptype); + } + + switch (priv->eeprom->specific_rftype) { + case ADM8211_RFMD2948: + case ADM8211_RFMD2958: + case ADM8211_RFMD2958_RF3000_CONTROL_POWER: + case ADM8211_MAX2820: + case ADM8211_AL2210L: + priv->transceiver_type = priv->eeprom->specific_rftype; + break; + + default: + if (priv->pdev->revision == ADM8211_REV_BA) + priv->transceiver_type = ADM8211_RFMD2958_RF3000_CONTROL_POWER; + else if (priv->pdev->revision == ADM8211_REV_CA) + priv->transceiver_type = ADM8211_AL2210L; + else if (priv->pdev->revision == ADM8211_REV_AB) + priv->transceiver_type = ADM8211_RFMD2948; + + printk(KERN_WARNING "%s (adm8211): Unknown transceiver: %d\n", + pci_name(priv->pdev), priv->eeprom->specific_rftype); + + break; + } + + printk(KERN_DEBUG "%s (adm8211): RFtype=%d BBPtype=%d Specific BBP=%d " + "Transceiver=%d\n", pci_name(priv->pdev), priv->rf_type, + priv->bbp_type, priv->specific_bbptype, priv->transceiver_type); + + return 0; +} + +static inline void adm8211_write_sram(struct ieee80211_hw *dev, + u32 addr, u32 data) +{ + struct adm8211_priv *priv = dev->priv; + + ADM8211_CSR_WRITE(WEPCTL, addr | ADM8211_WEPCTL_TABLE_WR | + (priv->pdev->revision < ADM8211_REV_BA ? + 0 : ADM8211_WEPCTL_SEL_WEPTABLE )); + ADM8211_CSR_READ(WEPCTL); + msleep(1); + + ADM8211_CSR_WRITE(WESK, data); + ADM8211_CSR_READ(WESK); + msleep(1); +} + +static void adm8211_write_sram_bytes(struct ieee80211_hw *dev, + unsigned int addr, u8 *buf, + unsigned int len) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg = ADM8211_CSR_READ(WEPCTL); + unsigned int i; + + if (priv->pdev->revision < ADM8211_REV_BA) { + for (i = 0; i < len; i += 2) { + u16 val = buf[i] | (buf[i + 1] << 8); + adm8211_write_sram(dev, addr + i / 2, val); + } + } else { + for (i = 0; i < len; i += 4) { + u32 val = (buf[i + 0] << 0 ) | (buf[i + 1] << 8 ) | + (buf[i + 2] << 16) | (buf[i + 3] << 24); + adm8211_write_sram(dev, addr + i / 4, val); + } + } + + ADM8211_CSR_WRITE(WEPCTL, reg); +} + +static void adm8211_clear_sram(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg = ADM8211_CSR_READ(WEPCTL); + unsigned int addr; + + for (addr = 0; addr < ADM8211_SRAM_SIZE; addr++) + adm8211_write_sram(dev, addr, 0); + + ADM8211_CSR_WRITE(WEPCTL, reg); +} + +static int adm8211_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct adm8211_priv *priv = dev->priv; + + memcpy(stats, &priv->stats, sizeof(*stats)); + + return 0; +} + +static void adm8211_interrupt_tci(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int dirty_tx; + + spin_lock(&priv->lock); + + for (dirty_tx = priv->dirty_tx; priv->cur_tx - dirty_tx; dirty_tx++) { + unsigned int entry = dirty_tx % priv->tx_ring_size; + u32 status = le32_to_cpu(priv->tx_ring[entry].status); + struct ieee80211_tx_info *txi; + struct adm8211_tx_ring_info *info; + struct sk_buff *skb; + + if (status & TDES0_CONTROL_OWN || + !(status & TDES0_CONTROL_DONE)) + break; + + info = &priv->tx_buffers[entry]; + skb = info->skb; + txi = IEEE80211_SKB_CB(skb); + + /* TODO: check TDES0_STATUS_TUF and TDES0_STATUS_TRO */ + + pci_unmap_single(priv->pdev, info->mapping, + info->skb->len, PCI_DMA_TODEVICE); + + ieee80211_tx_info_clear_status(txi); + + skb_pull(skb, sizeof(struct adm8211_tx_hdr)); + memcpy(skb_push(skb, info->hdrlen), skb->cb, info->hdrlen); + if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && + !(status & TDES0_STATUS_ES)) + txi->flags |= IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(dev, skb); + + info->skb = NULL; + } + + if (priv->cur_tx - dirty_tx < priv->tx_ring_size - 2) + ieee80211_wake_queue(dev, 0); + + priv->dirty_tx = dirty_tx; + spin_unlock(&priv->lock); +} + + +static void adm8211_interrupt_rci(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int entry = priv->cur_rx % priv->rx_ring_size; + u32 status; + unsigned int pktlen; + struct sk_buff *skb, *newskb; + unsigned int limit = priv->rx_ring_size; + u8 rssi, rate; + + while (!(priv->rx_ring[entry].status & cpu_to_le32(RDES0_STATUS_OWN))) { + if (!limit--) + break; + + status = le32_to_cpu(priv->rx_ring[entry].status); + rate = (status & RDES0_STATUS_RXDR) >> 12; + rssi = le32_to_cpu(priv->rx_ring[entry].length) & + RDES1_STATUS_RSSI; + + pktlen = status & RDES0_STATUS_FL; + if (pktlen > RX_PKT_SIZE) { + if (net_ratelimit()) + wiphy_debug(dev->wiphy, "frame too long (%d)\n", + pktlen); + pktlen = RX_PKT_SIZE; + } + + if (!priv->soft_rx_crc && status & RDES0_STATUS_ES) { + skb = NULL; /* old buffer will be reused */ + /* TODO: update RX error stats */ + /* TODO: check RDES0_STATUS_CRC*E */ + } else if (pktlen < RX_COPY_BREAK) { + skb = dev_alloc_skb(pktlen); + if (skb) { + pci_dma_sync_single_for_cpu( + priv->pdev, + priv->rx_buffers[entry].mapping, + pktlen, PCI_DMA_FROMDEVICE); + memcpy(skb_put(skb, pktlen), + skb_tail_pointer(priv->rx_buffers[entry].skb), + pktlen); + pci_dma_sync_single_for_device( + priv->pdev, + priv->rx_buffers[entry].mapping, + RX_PKT_SIZE, PCI_DMA_FROMDEVICE); + } + } else { + newskb = dev_alloc_skb(RX_PKT_SIZE); + if (newskb) { + skb = priv->rx_buffers[entry].skb; + skb_put(skb, pktlen); + pci_unmap_single( + priv->pdev, + priv->rx_buffers[entry].mapping, + RX_PKT_SIZE, PCI_DMA_FROMDEVICE); + priv->rx_buffers[entry].skb = newskb; + priv->rx_buffers[entry].mapping = + pci_map_single(priv->pdev, + skb_tail_pointer(newskb), + RX_PKT_SIZE, + PCI_DMA_FROMDEVICE); + } else { + skb = NULL; + /* TODO: update rx dropped stats */ + } + + priv->rx_ring[entry].buffer1 = + cpu_to_le32(priv->rx_buffers[entry].mapping); + } + + priv->rx_ring[entry].status = cpu_to_le32(RDES0_STATUS_OWN | + RDES0_STATUS_SQL); + priv->rx_ring[entry].length = + cpu_to_le32(RX_PKT_SIZE | + (entry == priv->rx_ring_size - 1 ? + RDES1_CONTROL_RER : 0)); + + if (skb) { + struct ieee80211_rx_status rx_status = {0}; + + if (priv->pdev->revision < ADM8211_REV_CA) + rx_status.signal = rssi; + else + rx_status.signal = 100 - rssi; + + rx_status.rate_idx = rate; + + rx_status.freq = adm8211_channels[priv->channel - 1].center_freq; + rx_status.band = IEEE80211_BAND_2GHZ; + + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(dev, skb); + } + + entry = (++priv->cur_rx) % priv->rx_ring_size; + } + + /* TODO: check LPC and update stats? */ +} + + +static irqreturn_t adm8211_interrupt(int irq, void *dev_id) +{ +#define ADM8211_INT(x) \ +do { \ + if (unlikely(stsr & ADM8211_STSR_ ## x)) \ + wiphy_debug(dev->wiphy, "%s\n", #x); \ +} while (0) + + struct ieee80211_hw *dev = dev_id; + struct adm8211_priv *priv = dev->priv; + u32 stsr = ADM8211_CSR_READ(STSR); + ADM8211_CSR_WRITE(STSR, stsr); + if (stsr == 0xffffffff) + return IRQ_HANDLED; + + if (!(stsr & (ADM8211_STSR_NISS | ADM8211_STSR_AISS))) + return IRQ_HANDLED; + + if (stsr & ADM8211_STSR_RCI) + adm8211_interrupt_rci(dev); + if (stsr & ADM8211_STSR_TCI) + adm8211_interrupt_tci(dev); + + ADM8211_INT(PCF); + ADM8211_INT(BCNTC); + ADM8211_INT(GPINT); + ADM8211_INT(ATIMTC); + ADM8211_INT(TSFTF); + ADM8211_INT(TSCZ); + ADM8211_INT(SQL); + ADM8211_INT(WEPTD); + ADM8211_INT(ATIME); + ADM8211_INT(TEIS); + ADM8211_INT(FBE); + ADM8211_INT(REIS); + ADM8211_INT(GPTT); + ADM8211_INT(RPS); + ADM8211_INT(RDU); + ADM8211_INT(TUF); + ADM8211_INT(TPS); + + return IRQ_HANDLED; + +#undef ADM8211_INT +} + +#define WRITE_SYN(name,v_mask,v_shift,a_mask,a_shift,bits,prewrite,postwrite)\ +static void adm8211_rf_write_syn_ ## name (struct ieee80211_hw *dev, \ + u16 addr, u32 value) { \ + struct adm8211_priv *priv = dev->priv; \ + unsigned int i; \ + u32 reg, bitbuf; \ + \ + value &= v_mask; \ + addr &= a_mask; \ + bitbuf = (value << v_shift) | (addr << a_shift); \ + \ + ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_1); \ + ADM8211_CSR_READ(SYNRF); \ + ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_IF_SELECT_0); \ + ADM8211_CSR_READ(SYNRF); \ + \ + if (prewrite) { \ + ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_WRITE_SYNDATA_0); \ + ADM8211_CSR_READ(SYNRF); \ + } \ + \ + for (i = 0; i <= bits; i++) { \ + if (bitbuf & (1 << (bits - i))) \ + reg = ADM8211_SYNRF_WRITE_SYNDATA_1; \ + else \ + reg = ADM8211_SYNRF_WRITE_SYNDATA_0; \ + \ + ADM8211_CSR_WRITE(SYNRF, reg); \ + ADM8211_CSR_READ(SYNRF); \ + \ + ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_1); \ + ADM8211_CSR_READ(SYNRF); \ + ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_WRITE_CLOCK_0); \ + ADM8211_CSR_READ(SYNRF); \ + } \ + \ + if (postwrite == 1) { \ + ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_0); \ + ADM8211_CSR_READ(SYNRF); \ + } \ + if (postwrite == 2) { \ + ADM8211_CSR_WRITE(SYNRF, reg | ADM8211_SYNRF_IF_SELECT_1); \ + ADM8211_CSR_READ(SYNRF); \ + } \ + \ + ADM8211_CSR_WRITE(SYNRF, 0); \ + ADM8211_CSR_READ(SYNRF); \ +} + +WRITE_SYN(max2820, 0x00FFF, 0, 0x0F, 12, 15, 1, 1) +WRITE_SYN(al2210l, 0xFFFFF, 4, 0x0F, 0, 23, 1, 1) +WRITE_SYN(rfmd2958, 0x3FFFF, 0, 0x1F, 18, 23, 0, 1) +WRITE_SYN(rfmd2948, 0x0FFFF, 4, 0x0F, 0, 21, 0, 2) + +#undef WRITE_SYN + +static int adm8211_write_bbp(struct ieee80211_hw *dev, u8 addr, u8 data) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int timeout; + u32 reg; + + timeout = 10; + while (timeout > 0) { + reg = ADM8211_CSR_READ(BBPCTL); + if (!(reg & (ADM8211_BBPCTL_WR | ADM8211_BBPCTL_RD))) + break; + timeout--; + msleep(2); + } + + if (timeout == 0) { + wiphy_debug(dev->wiphy, + "adm8211_write_bbp(%d,%d) failed prewrite (reg=0x%08x)\n", + addr, data, reg); + return -ETIMEDOUT; + } + + switch (priv->bbp_type) { + case ADM8211_TYPE_INTERSIL: + reg = ADM8211_BBPCTL_MMISEL; /* three wire interface */ + break; + case ADM8211_TYPE_RFMD: + reg = (0x20 << 24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP | + (0x01 << 18); + break; + case ADM8211_TYPE_ADMTEK: + reg = (0x20 << 24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP | + (0x05 << 18); + break; + } + reg |= ADM8211_BBPCTL_WR | (addr << 8) | data; + + ADM8211_CSR_WRITE(BBPCTL, reg); + + timeout = 10; + while (timeout > 0) { + reg = ADM8211_CSR_READ(BBPCTL); + if (!(reg & ADM8211_BBPCTL_WR)) + break; + timeout--; + msleep(2); + } + + if (timeout == 0) { + ADM8211_CSR_WRITE(BBPCTL, ADM8211_CSR_READ(BBPCTL) & + ~ADM8211_BBPCTL_WR); + wiphy_debug(dev->wiphy, + "adm8211_write_bbp(%d,%d) failed postwrite (reg=0x%08x)\n", + addr, data, reg); + return -ETIMEDOUT; + } + + return 0; +} + +static int adm8211_rf_set_channel(struct ieee80211_hw *dev, unsigned int chan) +{ + static const u32 adm8211_rfmd2958_reg5[] = + {0x22BD, 0x22D2, 0x22E8, 0x22FE, 0x2314, 0x232A, 0x2340, + 0x2355, 0x236B, 0x2381, 0x2397, 0x23AD, 0x23C2, 0x23F7}; + static const u32 adm8211_rfmd2958_reg6[] = + {0x05D17, 0x3A2E8, 0x2E8BA, 0x22E8B, 0x1745D, 0x0BA2E, 0x00000, + 0x345D1, 0x28BA2, 0x1D174, 0x11745, 0x05D17, 0x3A2E8, 0x11745}; + + struct adm8211_priv *priv = dev->priv; + u8 ant_power = priv->ant_power > 0x3F ? + priv->eeprom->antenna_power[chan - 1] : priv->ant_power; + u8 tx_power = priv->tx_power > 0x3F ? + priv->eeprom->tx_power[chan - 1] : priv->tx_power; + u8 lpf_cutoff = priv->lpf_cutoff == 0xFF ? + priv->eeprom->lpf_cutoff[chan - 1] : priv->lpf_cutoff; + u8 lnags_thresh = priv->lnags_threshold == 0xFF ? + priv->eeprom->lnags_threshold[chan - 1] : priv->lnags_threshold; + u32 reg; + + ADM8211_IDLE(); + + /* Program synthesizer to new channel */ + switch (priv->transceiver_type) { + case ADM8211_RFMD2958: + case ADM8211_RFMD2958_RF3000_CONTROL_POWER: + adm8211_rf_write_syn_rfmd2958(dev, 0x00, 0x04007); + adm8211_rf_write_syn_rfmd2958(dev, 0x02, 0x00033); + + adm8211_rf_write_syn_rfmd2958(dev, 0x05, + adm8211_rfmd2958_reg5[chan - 1]); + adm8211_rf_write_syn_rfmd2958(dev, 0x06, + adm8211_rfmd2958_reg6[chan - 1]); + break; + + case ADM8211_RFMD2948: + adm8211_rf_write_syn_rfmd2948(dev, SI4126_MAIN_CONF, + SI4126_MAIN_XINDIV2); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_POWERDOWN, + SI4126_POWERDOWN_PDIB | + SI4126_POWERDOWN_PDRB); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_PHASE_DET_GAIN, 0); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_N_DIV, + (chan == 14 ? + 2110 : (2033 + (chan * 5)))); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_N_DIV, 1496); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_R_DIV, 44); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_R_DIV, 44); + break; + + case ADM8211_MAX2820: + adm8211_rf_write_syn_max2820(dev, 0x3, + (chan == 14 ? 0x054 : (0x7 + (chan * 5)))); + break; + + case ADM8211_AL2210L: + adm8211_rf_write_syn_al2210l(dev, 0x0, + (chan == 14 ? 0x229B4 : (0x22967 + (chan * 5)))); + break; + + default: + wiphy_debug(dev->wiphy, "unsupported transceiver type %d\n", + priv->transceiver_type); + break; + } + + /* write BBP regs */ + if (priv->bbp_type == ADM8211_TYPE_RFMD) { + + /* SMC 2635W specific? adm8211b doesn't use the 2948 though.. */ + /* TODO: remove if SMC 2635W doesn't need this */ + if (priv->transceiver_type == ADM8211_RFMD2948) { + reg = ADM8211_CSR_READ(GPIO); + reg &= 0xfffc0000; + reg |= ADM8211_CSR_GPIO_EN0; + if (chan != 14) + reg |= ADM8211_CSR_GPIO_O0; + ADM8211_CSR_WRITE(GPIO, reg); + } + + if (priv->transceiver_type == ADM8211_RFMD2958) { + /* set PCNT2 */ + adm8211_rf_write_syn_rfmd2958(dev, 0x0B, 0x07100); + /* set PCNT1 P_DESIRED/MID_BIAS */ + reg = le16_to_cpu(priv->eeprom->cr49); + reg >>= 13; + reg <<= 15; + reg |= ant_power << 9; + adm8211_rf_write_syn_rfmd2958(dev, 0x0A, reg); + /* set TXRX TX_GAIN */ + adm8211_rf_write_syn_rfmd2958(dev, 0x09, 0x00050 | + (priv->pdev->revision < ADM8211_REV_CA ? tx_power : 0)); + } else { + reg = ADM8211_CSR_READ(PLCPHD); + reg &= 0xff00ffff; + reg |= tx_power << 18; + ADM8211_CSR_WRITE(PLCPHD, reg); + } + + ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | + ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST); + ADM8211_CSR_READ(SYNRF); + msleep(30); + + /* RF3000 BBP */ + if (priv->transceiver_type != ADM8211_RFMD2958) + adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, + tx_power<<2); + adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, lpf_cutoff); + adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, lnags_thresh); + adm8211_write_bbp(dev, 0x1c, priv->pdev->revision == ADM8211_REV_BA ? + priv->eeprom->cr28 : 0); + adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29); + + ADM8211_CSR_WRITE(SYNRF, 0); + + /* Nothing to do for ADMtek BBP */ + } else if (priv->bbp_type != ADM8211_TYPE_ADMTEK) + wiphy_debug(dev->wiphy, "unsupported BBP type %d\n", + priv->bbp_type); + + ADM8211_RESTORE(); + + /* update current channel for adhoc (and maybe AP mode) */ + reg = ADM8211_CSR_READ(CAP0); + reg &= ~0xF; + reg |= chan; + ADM8211_CSR_WRITE(CAP0, reg); + + return 0; +} + +static void adm8211_update_mode(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + + ADM8211_IDLE(); + + priv->soft_rx_crc = 0; + switch (priv->mode) { + case NL80211_IFTYPE_STATION: + priv->nar &= ~(ADM8211_NAR_PR | ADM8211_NAR_EA); + priv->nar |= ADM8211_NAR_ST | ADM8211_NAR_SR; + break; + case NL80211_IFTYPE_ADHOC: + priv->nar &= ~ADM8211_NAR_PR; + priv->nar |= ADM8211_NAR_EA | ADM8211_NAR_ST | ADM8211_NAR_SR; + + /* don't trust the error bits on rev 0x20 and up in adhoc */ + if (priv->pdev->revision >= ADM8211_REV_BA) + priv->soft_rx_crc = 1; + break; + case NL80211_IFTYPE_MONITOR: + priv->nar &= ~(ADM8211_NAR_EA | ADM8211_NAR_ST); + priv->nar |= ADM8211_NAR_PR | ADM8211_NAR_SR; + break; + } + + ADM8211_RESTORE(); +} + +static void adm8211_hw_init_syn(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + + switch (priv->transceiver_type) { + case ADM8211_RFMD2958: + case ADM8211_RFMD2958_RF3000_CONTROL_POWER: + /* comments taken from ADMtek vendor driver */ + + /* Reset RF2958 after power on */ + adm8211_rf_write_syn_rfmd2958(dev, 0x1F, 0x00000); + /* Initialize RF VCO Core Bias to maximum */ + adm8211_rf_write_syn_rfmd2958(dev, 0x0C, 0x3001F); + /* Initialize IF PLL */ + adm8211_rf_write_syn_rfmd2958(dev, 0x01, 0x29C03); + /* Initialize IF PLL Coarse Tuning */ + adm8211_rf_write_syn_rfmd2958(dev, 0x03, 0x1FF6F); + /* Initialize RF PLL */ + adm8211_rf_write_syn_rfmd2958(dev, 0x04, 0x29403); + /* Initialize RF PLL Coarse Tuning */ + adm8211_rf_write_syn_rfmd2958(dev, 0x07, 0x1456F); + /* Initialize TX gain and filter BW (R9) */ + adm8211_rf_write_syn_rfmd2958(dev, 0x09, + (priv->transceiver_type == ADM8211_RFMD2958 ? + 0x10050 : 0x00050)); + /* Initialize CAL register */ + adm8211_rf_write_syn_rfmd2958(dev, 0x08, 0x3FFF8); + break; + + case ADM8211_MAX2820: + adm8211_rf_write_syn_max2820(dev, 0x1, 0x01E); + adm8211_rf_write_syn_max2820(dev, 0x2, 0x001); + adm8211_rf_write_syn_max2820(dev, 0x3, 0x054); + adm8211_rf_write_syn_max2820(dev, 0x4, 0x310); + adm8211_rf_write_syn_max2820(dev, 0x5, 0x000); + break; + + case ADM8211_AL2210L: + adm8211_rf_write_syn_al2210l(dev, 0x0, 0x0196C); + adm8211_rf_write_syn_al2210l(dev, 0x1, 0x007CB); + adm8211_rf_write_syn_al2210l(dev, 0x2, 0x3582F); + adm8211_rf_write_syn_al2210l(dev, 0x3, 0x010A9); + adm8211_rf_write_syn_al2210l(dev, 0x4, 0x77280); + adm8211_rf_write_syn_al2210l(dev, 0x5, 0x45641); + adm8211_rf_write_syn_al2210l(dev, 0x6, 0xEA130); + adm8211_rf_write_syn_al2210l(dev, 0x7, 0x80000); + adm8211_rf_write_syn_al2210l(dev, 0x8, 0x7850F); + adm8211_rf_write_syn_al2210l(dev, 0x9, 0xF900C); + adm8211_rf_write_syn_al2210l(dev, 0xA, 0x00000); + adm8211_rf_write_syn_al2210l(dev, 0xB, 0x00000); + break; + + case ADM8211_RFMD2948: + default: + break; + } +} + +static int adm8211_hw_init_bbp(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg; + + /* write addresses */ + if (priv->bbp_type == ADM8211_TYPE_INTERSIL) { + ADM8211_CSR_WRITE(MMIWA, 0x100E0C0A); + ADM8211_CSR_WRITE(MMIRD0, 0x00007C7E); + ADM8211_CSR_WRITE(MMIRD1, 0x00100000); + } else if (priv->bbp_type == ADM8211_TYPE_RFMD || + priv->bbp_type == ADM8211_TYPE_ADMTEK) { + /* check specific BBP type */ + switch (priv->specific_bbptype) { + case ADM8211_BBP_RFMD3000: + case ADM8211_BBP_RFMD3002: + ADM8211_CSR_WRITE(MMIWA, 0x00009101); + ADM8211_CSR_WRITE(MMIRD0, 0x00000301); + break; + + case ADM8211_BBP_ADM8011: + ADM8211_CSR_WRITE(MMIWA, 0x00008903); + ADM8211_CSR_WRITE(MMIRD0, 0x00001716); + + reg = ADM8211_CSR_READ(BBPCTL); + reg &= ~ADM8211_BBPCTL_TYPE; + reg |= 0x5 << 18; + ADM8211_CSR_WRITE(BBPCTL, reg); + break; + } + + switch (priv->pdev->revision) { + case ADM8211_REV_CA: + if (priv->transceiver_type == ADM8211_RFMD2958 || + priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER || + priv->transceiver_type == ADM8211_RFMD2948) + ADM8211_CSR_WRITE(SYNCTL, 0x1 << 22); + else if (priv->transceiver_type == ADM8211_MAX2820 || + priv->transceiver_type == ADM8211_AL2210L) + ADM8211_CSR_WRITE(SYNCTL, 0x3 << 22); + break; + + case ADM8211_REV_BA: + reg = ADM8211_CSR_READ(MMIRD1); + reg &= 0x0000FFFF; + reg |= 0x7e100000; + ADM8211_CSR_WRITE(MMIRD1, reg); + break; + + case ADM8211_REV_AB: + case ADM8211_REV_AF: + default: + ADM8211_CSR_WRITE(MMIRD1, 0x7e100000); + break; + } + + /* For RFMD */ + ADM8211_CSR_WRITE(MACTEST, 0x800); + } + + adm8211_hw_init_syn(dev); + + /* Set RF Power control IF pin to PE1+PHYRST# */ + ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_SELRF | + ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST); + ADM8211_CSR_READ(SYNRF); + msleep(20); + + /* write BBP regs */ + if (priv->bbp_type == ADM8211_TYPE_RFMD) { + /* RF3000 BBP */ + /* another set: + * 11: c8 + * 14: 14 + * 15: 50 (chan 1..13; chan 14: d0) + * 1c: 00 + * 1d: 84 + */ + adm8211_write_bbp(dev, RF3000_CCA_CTRL, 0x80); + /* antenna selection: diversity */ + adm8211_write_bbp(dev, RF3000_DIVERSITY__RSSI, 0x80); + adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, 0x74); + adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, 0x38); + adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, 0x40); + + if (priv->eeprom->major_version < 2) { + adm8211_write_bbp(dev, 0x1c, 0x00); + adm8211_write_bbp(dev, 0x1d, 0x80); + } else { + if (priv->pdev->revision == ADM8211_REV_BA) + adm8211_write_bbp(dev, 0x1c, priv->eeprom->cr28); + else + adm8211_write_bbp(dev, 0x1c, 0x00); + + adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29); + } + } else if (priv->bbp_type == ADM8211_TYPE_ADMTEK) { + /* reset baseband */ + adm8211_write_bbp(dev, 0x00, 0xFF); + /* antenna selection: diversity */ + adm8211_write_bbp(dev, 0x07, 0x0A); + + /* TODO: find documentation for this */ + switch (priv->transceiver_type) { + case ADM8211_RFMD2958: + case ADM8211_RFMD2958_RF3000_CONTROL_POWER: + adm8211_write_bbp(dev, 0x00, 0x00); + adm8211_write_bbp(dev, 0x01, 0x00); + adm8211_write_bbp(dev, 0x02, 0x00); + adm8211_write_bbp(dev, 0x03, 0x00); + adm8211_write_bbp(dev, 0x06, 0x0f); + adm8211_write_bbp(dev, 0x09, 0x00); + adm8211_write_bbp(dev, 0x0a, 0x00); + adm8211_write_bbp(dev, 0x0b, 0x00); + adm8211_write_bbp(dev, 0x0c, 0x00); + adm8211_write_bbp(dev, 0x0f, 0xAA); + adm8211_write_bbp(dev, 0x10, 0x8c); + adm8211_write_bbp(dev, 0x11, 0x43); + adm8211_write_bbp(dev, 0x18, 0x40); + adm8211_write_bbp(dev, 0x20, 0x23); + adm8211_write_bbp(dev, 0x21, 0x02); + adm8211_write_bbp(dev, 0x22, 0x28); + adm8211_write_bbp(dev, 0x23, 0x30); + adm8211_write_bbp(dev, 0x24, 0x2d); + adm8211_write_bbp(dev, 0x28, 0x35); + adm8211_write_bbp(dev, 0x2a, 0x8c); + adm8211_write_bbp(dev, 0x2b, 0x81); + adm8211_write_bbp(dev, 0x2c, 0x44); + adm8211_write_bbp(dev, 0x2d, 0x0A); + adm8211_write_bbp(dev, 0x29, 0x40); + adm8211_write_bbp(dev, 0x60, 0x08); + adm8211_write_bbp(dev, 0x64, 0x01); + break; + + case ADM8211_MAX2820: + adm8211_write_bbp(dev, 0x00, 0x00); + adm8211_write_bbp(dev, 0x01, 0x00); + adm8211_write_bbp(dev, 0x02, 0x00); + adm8211_write_bbp(dev, 0x03, 0x00); + adm8211_write_bbp(dev, 0x06, 0x0f); + adm8211_write_bbp(dev, 0x09, 0x05); + adm8211_write_bbp(dev, 0x0a, 0x02); + adm8211_write_bbp(dev, 0x0b, 0x00); + adm8211_write_bbp(dev, 0x0c, 0x0f); + adm8211_write_bbp(dev, 0x0f, 0x55); + adm8211_write_bbp(dev, 0x10, 0x8d); + adm8211_write_bbp(dev, 0x11, 0x43); + adm8211_write_bbp(dev, 0x18, 0x4a); + adm8211_write_bbp(dev, 0x20, 0x20); + adm8211_write_bbp(dev, 0x21, 0x02); + adm8211_write_bbp(dev, 0x22, 0x23); + adm8211_write_bbp(dev, 0x23, 0x30); + adm8211_write_bbp(dev, 0x24, 0x2d); + adm8211_write_bbp(dev, 0x2a, 0x8c); + adm8211_write_bbp(dev, 0x2b, 0x81); + adm8211_write_bbp(dev, 0x2c, 0x44); + adm8211_write_bbp(dev, 0x29, 0x4a); + adm8211_write_bbp(dev, 0x60, 0x2b); + adm8211_write_bbp(dev, 0x64, 0x01); + break; + + case ADM8211_AL2210L: + adm8211_write_bbp(dev, 0x00, 0x00); + adm8211_write_bbp(dev, 0x01, 0x00); + adm8211_write_bbp(dev, 0x02, 0x00); + adm8211_write_bbp(dev, 0x03, 0x00); + adm8211_write_bbp(dev, 0x06, 0x0f); + adm8211_write_bbp(dev, 0x07, 0x05); + adm8211_write_bbp(dev, 0x08, 0x03); + adm8211_write_bbp(dev, 0x09, 0x00); + adm8211_write_bbp(dev, 0x0a, 0x00); + adm8211_write_bbp(dev, 0x0b, 0x00); + adm8211_write_bbp(dev, 0x0c, 0x10); + adm8211_write_bbp(dev, 0x0f, 0x55); + adm8211_write_bbp(dev, 0x10, 0x8d); + adm8211_write_bbp(dev, 0x11, 0x43); + adm8211_write_bbp(dev, 0x18, 0x4a); + adm8211_write_bbp(dev, 0x20, 0x20); + adm8211_write_bbp(dev, 0x21, 0x02); + adm8211_write_bbp(dev, 0x22, 0x23); + adm8211_write_bbp(dev, 0x23, 0x30); + adm8211_write_bbp(dev, 0x24, 0x2d); + adm8211_write_bbp(dev, 0x2a, 0xaa); + adm8211_write_bbp(dev, 0x2b, 0x81); + adm8211_write_bbp(dev, 0x2c, 0x44); + adm8211_write_bbp(dev, 0x29, 0xfa); + adm8211_write_bbp(dev, 0x60, 0x2d); + adm8211_write_bbp(dev, 0x64, 0x01); + break; + + case ADM8211_RFMD2948: + break; + + default: + wiphy_debug(dev->wiphy, "unsupported transceiver %d\n", + priv->transceiver_type); + break; + } + } else + wiphy_debug(dev->wiphy, "unsupported BBP %d\n", priv->bbp_type); + + ADM8211_CSR_WRITE(SYNRF, 0); + + /* Set RF CAL control source to MAC control */ + reg = ADM8211_CSR_READ(SYNCTL); + reg |= ADM8211_SYNCTL_SELCAL; + ADM8211_CSR_WRITE(SYNCTL, reg); + + return 0; +} + +/* configures hw beacons/probe responses */ +static int adm8211_set_rate(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg; + int i = 0; + u8 rate_buf[12] = {0}; + + /* write supported rates */ + if (priv->pdev->revision != ADM8211_REV_BA) { + rate_buf[0] = ARRAY_SIZE(adm8211_rates); + for (i = 0; i < ARRAY_SIZE(adm8211_rates); i++) + rate_buf[i + 1] = (adm8211_rates[i].bitrate / 5) | 0x80; + } else { + /* workaround for rev BA specific bug */ + rate_buf[0] = 0x04; + rate_buf[1] = 0x82; + rate_buf[2] = 0x04; + rate_buf[3] = 0x0b; + rate_buf[4] = 0x16; + } + + adm8211_write_sram_bytes(dev, ADM8211_SRAM_SUPP_RATE, rate_buf, + ARRAY_SIZE(adm8211_rates) + 1); + + reg = ADM8211_CSR_READ(PLCPHD) & 0x00FFFFFF; /* keep bits 0-23 */ + reg |= 1 << 15; /* short preamble */ + reg |= 110 << 24; + ADM8211_CSR_WRITE(PLCPHD, reg); + + /* MTMLT = 512 TU (max TX MSDU lifetime) + * BCNTSIG = plcp_signal (beacon, probe resp, and atim TX rate) + * SRTYLIM = 224 (short retry limit, TX header value is default) */ + ADM8211_CSR_WRITE(TXLMT, (512 << 16) | (110 << 8) | (224 << 0)); + + return 0; +} + +static void adm8211_hw_init(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg; + u8 cline; + + reg = ADM8211_CSR_READ(PAR); + reg |= ADM8211_PAR_MRLE | ADM8211_PAR_MRME; + reg &= ~(ADM8211_PAR_BAR | ADM8211_PAR_CAL); + + if (!pci_set_mwi(priv->pdev)) { + reg |= 0x1 << 24; + pci_read_config_byte(priv->pdev, PCI_CACHE_LINE_SIZE, &cline); + + switch (cline) { + case 0x8: + reg |= (0x1 << 14); + break; + case 0x10: + reg |= (0x2 << 14); + break; + case 0x20: + reg |= (0x3 << 14); + break; + default: + reg |= (0x0 << 14); + break; + } + } + + ADM8211_CSR_WRITE(PAR, reg); + + reg = ADM8211_CSR_READ(CSR_TEST1); + reg &= ~(0xF << 28); + reg |= (1 << 28) | (1 << 31); + ADM8211_CSR_WRITE(CSR_TEST1, reg); + + /* lose link after 4 lost beacons */ + reg = (0x04 << 21) | ADM8211_WCSR_TSFTWE | ADM8211_WCSR_LSOE; + ADM8211_CSR_WRITE(WCSR, reg); + + /* Disable APM, enable receive FIFO threshold, and set drain receive + * threshold to store-and-forward */ + reg = ADM8211_CSR_READ(CMDR); + reg &= ~(ADM8211_CMDR_APM | ADM8211_CMDR_DRT); + reg |= ADM8211_CMDR_RTE | ADM8211_CMDR_DRT_SF; + ADM8211_CSR_WRITE(CMDR, reg); + + adm8211_set_rate(dev); + + /* 4-bit values: + * PWR1UP = 8 * 2 ms + * PWR0PAPE = 8 us or 5 us + * PWR1PAPE = 1 us or 3 us + * PWR0TRSW = 5 us + * PWR1TRSW = 12 us + * PWR0PE2 = 13 us + * PWR1PE2 = 1 us + * PWR0TXPE = 8 or 6 */ + if (priv->pdev->revision < ADM8211_REV_CA) + ADM8211_CSR_WRITE(TOFS2, 0x8815cd18); + else + ADM8211_CSR_WRITE(TOFS2, 0x8535cd16); + + /* Enable store and forward for transmit */ + priv->nar = ADM8211_NAR_SF | ADM8211_NAR_PB; + ADM8211_CSR_WRITE(NAR, priv->nar); + + /* Reset RF */ + ADM8211_CSR_WRITE(SYNRF, ADM8211_SYNRF_RADIO); + ADM8211_CSR_READ(SYNRF); + msleep(10); + ADM8211_CSR_WRITE(SYNRF, 0); + ADM8211_CSR_READ(SYNRF); + msleep(5); + + /* Set CFP Max Duration to 0x10 TU */ + reg = ADM8211_CSR_READ(CFPP); + reg &= ~(0xffff << 8); + reg |= 0x0010 << 8; + ADM8211_CSR_WRITE(CFPP, reg); + + /* USCNT = 0x16 (number of system clocks, 22 MHz, in 1us + * TUCNT = 0x3ff - Tu counter 1024 us */ + ADM8211_CSR_WRITE(TOFS0, (0x16 << 24) | 0x3ff); + + /* SLOT=20 us, SIFS=110 cycles of 22 MHz (5 us), + * DIFS=50 us, EIFS=100 us */ + if (priv->pdev->revision < ADM8211_REV_CA) + ADM8211_CSR_WRITE(IFST, (20 << 23) | (110 << 15) | + (50 << 9) | 100); + else + ADM8211_CSR_WRITE(IFST, (20 << 23) | (24 << 15) | + (50 << 9) | 100); + + /* PCNT = 1 (MAC idle time awake/sleep, unit S) + * RMRD = 2346 * 8 + 1 us (max RX duration) */ + ADM8211_CSR_WRITE(RMD, (1 << 16) | 18769); + + /* MART=65535 us, MIRT=256 us, TSFTOFST=0 us */ + ADM8211_CSR_WRITE(RSPT, 0xffffff00); + + /* Initialize BBP (and SYN) */ + adm8211_hw_init_bbp(dev); + + /* make sure interrupts are off */ + ADM8211_CSR_WRITE(IER, 0); + + /* ACK interrupts */ + ADM8211_CSR_WRITE(STSR, ADM8211_CSR_READ(STSR)); + + /* Setup WEP (turns it off for now) */ + reg = ADM8211_CSR_READ(MACTEST); + reg &= ~(7 << 20); + ADM8211_CSR_WRITE(MACTEST, reg); + + reg = ADM8211_CSR_READ(WEPCTL); + reg &= ~ADM8211_WEPCTL_WEPENABLE; + reg |= ADM8211_WEPCTL_WEPRXBYP; + ADM8211_CSR_WRITE(WEPCTL, reg); + + /* Clear the missed-packet counter. */ + ADM8211_CSR_READ(LPC); +} + +static int adm8211_hw_reset(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg, tmp; + int timeout = 100; + + /* Power-on issue */ + /* TODO: check if this is necessary */ + ADM8211_CSR_WRITE(FRCTL, 0); + + /* Reset the chip */ + tmp = ADM8211_CSR_READ(PAR); + ADM8211_CSR_WRITE(PAR, ADM8211_PAR_SWR); + + while ((ADM8211_CSR_READ(PAR) & ADM8211_PAR_SWR) && timeout--) + msleep(50); + + if (timeout <= 0) + return -ETIMEDOUT; + + ADM8211_CSR_WRITE(PAR, tmp); + + if (priv->pdev->revision == ADM8211_REV_BA && + (priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER || + priv->transceiver_type == ADM8211_RFMD2958)) { + reg = ADM8211_CSR_READ(CSR_TEST1); + reg |= (1 << 4) | (1 << 5); + ADM8211_CSR_WRITE(CSR_TEST1, reg); + } else if (priv->pdev->revision == ADM8211_REV_CA) { + reg = ADM8211_CSR_READ(CSR_TEST1); + reg &= ~((1 << 4) | (1 << 5)); + ADM8211_CSR_WRITE(CSR_TEST1, reg); + } + + ADM8211_CSR_WRITE(FRCTL, 0); + + reg = ADM8211_CSR_READ(CSR_TEST0); + reg |= ADM8211_CSR_TEST0_EPRLD; /* EEPROM Recall */ + ADM8211_CSR_WRITE(CSR_TEST0, reg); + + adm8211_clear_sram(dev); + + return 0; +} + +static u64 adm8211_get_tsft(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct adm8211_priv *priv = dev->priv; + u32 tsftl; + u64 tsft; + + tsftl = ADM8211_CSR_READ(TSFTL); + tsft = ADM8211_CSR_READ(TSFTH); + tsft <<= 32; + tsft |= tsftl; + + return tsft; +} + +static void adm8211_set_interval(struct ieee80211_hw *dev, + unsigned short bi, unsigned short li) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg; + + /* BP (beacon interval) = data->beacon_interval + * LI (listen interval) = data->listen_interval (in beacon intervals) */ + reg = (bi << 16) | li; + ADM8211_CSR_WRITE(BPLI, reg); +} + +static void adm8211_set_bssid(struct ieee80211_hw *dev, const u8 *bssid) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg; + + ADM8211_CSR_WRITE(BSSID0, le32_to_cpu(*(__le32 *)bssid)); + reg = ADM8211_CSR_READ(ABDA1); + reg &= 0x0000ffff; + reg |= (bssid[4] << 16) | (bssid[5] << 24); + ADM8211_CSR_WRITE(ABDA1, reg); +} + +static int adm8211_config(struct ieee80211_hw *dev, u32 changed) +{ + struct adm8211_priv *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + int channel = + ieee80211_frequency_to_channel(conf->chandef.chan->center_freq); + + if (channel != priv->channel) { + priv->channel = channel; + adm8211_rf_set_channel(dev, priv->channel); + } + + return 0; +} + +static void adm8211_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, + u32 changes) +{ + struct adm8211_priv *priv = dev->priv; + + if (!(changes & BSS_CHANGED_BSSID)) + return; + + if (!ether_addr_equal(conf->bssid, priv->bssid)) { + adm8211_set_bssid(dev, conf->bssid); + memcpy(priv->bssid, conf->bssid, ETH_ALEN); + } +} + +static u64 adm8211_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + unsigned int bit_nr; + u32 mc_filter[2]; + struct netdev_hw_addr *ha; + + mc_filter[1] = mc_filter[0] = 0; + + netdev_hw_addr_list_for_each(ha, mc_list) { + bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; + + bit_nr &= 0x3F; + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + } + + return mc_filter[0] | ((u64)(mc_filter[1]) << 32); +} + +static void adm8211_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + static const u8 bcast[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + struct adm8211_priv *priv = dev->priv; + unsigned int new_flags; + u32 mc_filter[2]; + + mc_filter[0] = multicast; + mc_filter[1] = multicast >> 32; + + new_flags = 0; + + if (*total_flags & FIF_ALLMULTI || multicast == ~(0ULL)) { + new_flags |= FIF_ALLMULTI; + priv->nar &= ~ADM8211_NAR_PR; + priv->nar |= ADM8211_NAR_MM; + mc_filter[1] = mc_filter[0] = ~0; + } else { + priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR); + } + + ADM8211_IDLE_RX(); + + ADM8211_CSR_WRITE(MAR0, mc_filter[0]); + ADM8211_CSR_WRITE(MAR1, mc_filter[1]); + ADM8211_CSR_READ(NAR); + + if (priv->nar & ADM8211_NAR_PR) + ieee80211_hw_set(dev, RX_INCLUDES_FCS); + else + __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, dev->flags); + + if (*total_flags & FIF_BCN_PRBRESP_PROMISC) + adm8211_set_bssid(dev, bcast); + else + adm8211_set_bssid(dev, priv->bssid); + + ADM8211_RESTORE(); + + *total_flags = new_flags; +} + +static int adm8211_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct adm8211_priv *priv = dev->priv; + if (priv->mode != NL80211_IFTYPE_MONITOR) + return -EOPNOTSUPP; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + priv->mode = vif->type; + break; + default: + return -EOPNOTSUPP; + } + + ADM8211_IDLE(); + + ADM8211_CSR_WRITE(PAR0, le32_to_cpu(*(__le32 *)vif->addr)); + ADM8211_CSR_WRITE(PAR1, le16_to_cpu(*(__le16 *)(vif->addr + 4))); + + adm8211_update_mode(dev); + + ADM8211_RESTORE(); + + return 0; +} + +static void adm8211_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct adm8211_priv *priv = dev->priv; + priv->mode = NL80211_IFTYPE_MONITOR; +} + +static int adm8211_init_rings(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + struct adm8211_desc *desc = NULL; + struct adm8211_rx_ring_info *rx_info; + struct adm8211_tx_ring_info *tx_info; + unsigned int i; + + for (i = 0; i < priv->rx_ring_size; i++) { + desc = &priv->rx_ring[i]; + desc->status = 0; + desc->length = cpu_to_le32(RX_PKT_SIZE); + priv->rx_buffers[i].skb = NULL; + } + /* Mark the end of RX ring; hw returns to base address after this + * descriptor */ + desc->length |= cpu_to_le32(RDES1_CONTROL_RER); + + for (i = 0; i < priv->rx_ring_size; i++) { + desc = &priv->rx_ring[i]; + rx_info = &priv->rx_buffers[i]; + + rx_info->skb = dev_alloc_skb(RX_PKT_SIZE); + if (rx_info->skb == NULL) + break; + rx_info->mapping = pci_map_single(priv->pdev, + skb_tail_pointer(rx_info->skb), + RX_PKT_SIZE, + PCI_DMA_FROMDEVICE); + desc->buffer1 = cpu_to_le32(rx_info->mapping); + desc->status = cpu_to_le32(RDES0_STATUS_OWN | RDES0_STATUS_SQL); + } + + /* Setup TX ring. TX buffers descriptors will be filled in as needed */ + for (i = 0; i < priv->tx_ring_size; i++) { + desc = &priv->tx_ring[i]; + tx_info = &priv->tx_buffers[i]; + + tx_info->skb = NULL; + tx_info->mapping = 0; + desc->status = 0; + } + desc->length = cpu_to_le32(TDES1_CONTROL_TER); + + priv->cur_rx = priv->cur_tx = priv->dirty_tx = 0; + ADM8211_CSR_WRITE(RDB, priv->rx_ring_dma); + ADM8211_CSR_WRITE(TDBD, priv->tx_ring_dma); + + return 0; +} + +static void adm8211_free_rings(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int i; + + for (i = 0; i < priv->rx_ring_size; i++) { + if (!priv->rx_buffers[i].skb) + continue; + + pci_unmap_single( + priv->pdev, + priv->rx_buffers[i].mapping, + RX_PKT_SIZE, PCI_DMA_FROMDEVICE); + + dev_kfree_skb(priv->rx_buffers[i].skb); + } + + for (i = 0; i < priv->tx_ring_size; i++) { + if (!priv->tx_buffers[i].skb) + continue; + + pci_unmap_single(priv->pdev, + priv->tx_buffers[i].mapping, + priv->tx_buffers[i].skb->len, + PCI_DMA_TODEVICE); + + dev_kfree_skb(priv->tx_buffers[i].skb); + } +} + +static int adm8211_start(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + int retval; + + /* Power up MAC and RF chips */ + retval = adm8211_hw_reset(dev); + if (retval) { + wiphy_err(dev->wiphy, "hardware reset failed\n"); + goto fail; + } + + retval = adm8211_init_rings(dev); + if (retval) { + wiphy_err(dev->wiphy, "failed to initialize rings\n"); + goto fail; + } + + /* Init hardware */ + adm8211_hw_init(dev); + adm8211_rf_set_channel(dev, priv->channel); + + retval = request_irq(priv->pdev->irq, adm8211_interrupt, + IRQF_SHARED, "adm8211", dev); + if (retval) { + wiphy_err(dev->wiphy, "failed to register IRQ handler\n"); + goto fail; + } + + ADM8211_CSR_WRITE(IER, ADM8211_IER_NIE | ADM8211_IER_AIE | + ADM8211_IER_RCIE | ADM8211_IER_TCIE | + ADM8211_IER_TDUIE | ADM8211_IER_GPTIE); + priv->mode = NL80211_IFTYPE_MONITOR; + adm8211_update_mode(dev); + ADM8211_CSR_WRITE(RDR, 0); + + adm8211_set_interval(dev, 100, 10); + return 0; + +fail: + return retval; +} + +static void adm8211_stop(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->nar = 0; + ADM8211_CSR_WRITE(NAR, 0); + ADM8211_CSR_WRITE(IER, 0); + ADM8211_CSR_READ(NAR); + + free_irq(priv->pdev->irq, dev); + + adm8211_free_rings(dev); +} + +static void adm8211_calc_durations(int *dur, int *plcp, size_t payload_len, int len, + int plcp_signal, int short_preamble) +{ + /* Alternative calculation from NetBSD: */ + +/* IEEE 802.11b durations for DSSS PHY in microseconds */ +#define IEEE80211_DUR_DS_LONG_PREAMBLE 144 +#define IEEE80211_DUR_DS_SHORT_PREAMBLE 72 +#define IEEE80211_DUR_DS_FAST_PLCPHDR 24 +#define IEEE80211_DUR_DS_SLOW_PLCPHDR 48 +#define IEEE80211_DUR_DS_SLOW_ACK 112 +#define IEEE80211_DUR_DS_FAST_ACK 56 +#define IEEE80211_DUR_DS_SLOW_CTS 112 +#define IEEE80211_DUR_DS_FAST_CTS 56 +#define IEEE80211_DUR_DS_SLOT 20 +#define IEEE80211_DUR_DS_SIFS 10 + + int remainder; + + *dur = (80 * (24 + payload_len) + plcp_signal - 1) + / plcp_signal; + + if (plcp_signal <= PLCP_SIGNAL_2M) + /* 1-2Mbps WLAN: send ACK/CTS at 1Mbps */ + *dur += 3 * (IEEE80211_DUR_DS_SIFS + + IEEE80211_DUR_DS_SHORT_PREAMBLE + + IEEE80211_DUR_DS_FAST_PLCPHDR) + + IEEE80211_DUR_DS_SLOW_CTS + IEEE80211_DUR_DS_SLOW_ACK; + else + /* 5-11Mbps WLAN: send ACK/CTS at 2Mbps */ + *dur += 3 * (IEEE80211_DUR_DS_SIFS + + IEEE80211_DUR_DS_SHORT_PREAMBLE + + IEEE80211_DUR_DS_FAST_PLCPHDR) + + IEEE80211_DUR_DS_FAST_CTS + IEEE80211_DUR_DS_FAST_ACK; + + /* lengthen duration if long preamble */ + if (!short_preamble) + *dur += 3 * (IEEE80211_DUR_DS_LONG_PREAMBLE - + IEEE80211_DUR_DS_SHORT_PREAMBLE) + + 3 * (IEEE80211_DUR_DS_SLOW_PLCPHDR - + IEEE80211_DUR_DS_FAST_PLCPHDR); + + + *plcp = (80 * len) / plcp_signal; + remainder = (80 * len) % plcp_signal; + if (plcp_signal == PLCP_SIGNAL_11M && + remainder <= 30 && remainder > 0) + *plcp = (*plcp | 0x8000) + 1; + else if (remainder) + (*plcp)++; +} + +/* Transmit skb w/adm8211_tx_hdr (802.11 header created by hardware) */ +static void adm8211_tx_raw(struct ieee80211_hw *dev, struct sk_buff *skb, + u16 plcp_signal, + size_t hdrlen) +{ + struct adm8211_priv *priv = dev->priv; + unsigned long flags; + dma_addr_t mapping; + unsigned int entry; + u32 flag; + + mapping = pci_map_single(priv->pdev, skb->data, skb->len, + PCI_DMA_TODEVICE); + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size / 2) + flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS; + else + flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS; + + if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size - 2) + ieee80211_stop_queue(dev, 0); + + entry = priv->cur_tx % priv->tx_ring_size; + + priv->tx_buffers[entry].skb = skb; + priv->tx_buffers[entry].mapping = mapping; + priv->tx_buffers[entry].hdrlen = hdrlen; + priv->tx_ring[entry].buffer1 = cpu_to_le32(mapping); + + if (entry == priv->tx_ring_size - 1) + flag |= TDES1_CONTROL_TER; + priv->tx_ring[entry].length = cpu_to_le32(flag | skb->len); + + /* Set TX rate (SIGNAL field in PLCP PPDU format) */ + flag = TDES0_CONTROL_OWN | (plcp_signal << 20) | 8 /* ? */; + priv->tx_ring[entry].status = cpu_to_le32(flag); + + priv->cur_tx++; + + spin_unlock_irqrestore(&priv->lock, flags); + + /* Trigger transmit poll */ + ADM8211_CSR_WRITE(TDR, 0); +} + +/* Put adm8211_tx_hdr on skb and transmit */ +static void adm8211_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct adm8211_tx_hdr *txhdr; + size_t payload_len, hdrlen; + int plcp, dur, len, plcp_signal, short_preamble; + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_rate *txrate = ieee80211_get_tx_rate(dev, info); + u8 rc_flags; + + rc_flags = info->control.rates[0].flags; + short_preamble = !!(rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE); + plcp_signal = txrate->bitrate; + + hdr = (struct ieee80211_hdr *)skb->data; + hdrlen = ieee80211_hdrlen(hdr->frame_control); + memcpy(skb->cb, skb->data, hdrlen); + hdr = (struct ieee80211_hdr *)skb->cb; + skb_pull(skb, hdrlen); + payload_len = skb->len; + + txhdr = (struct adm8211_tx_hdr *) skb_push(skb, sizeof(*txhdr)); + memset(txhdr, 0, sizeof(*txhdr)); + memcpy(txhdr->da, ieee80211_get_DA(hdr), ETH_ALEN); + txhdr->signal = plcp_signal; + txhdr->frame_body_size = cpu_to_le16(payload_len); + txhdr->frame_control = hdr->frame_control; + + len = hdrlen + payload_len + FCS_LEN; + + txhdr->frag = cpu_to_le16(0x0FFF); + adm8211_calc_durations(&dur, &plcp, payload_len, + len, plcp_signal, short_preamble); + txhdr->plcp_frag_head_len = cpu_to_le16(plcp); + txhdr->plcp_frag_tail_len = cpu_to_le16(plcp); + txhdr->dur_frag_head = cpu_to_le16(dur); + txhdr->dur_frag_tail = cpu_to_le16(dur); + + txhdr->header_control = cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER); + + if (short_preamble) + txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE); + + if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) + txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS); + + txhdr->retry_limit = info->control.rates[0].count; + + adm8211_tx_raw(dev, skb, plcp_signal, hdrlen); +} + +static int adm8211_alloc_rings(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int ring_size; + + priv->rx_buffers = kmalloc(sizeof(*priv->rx_buffers) * priv->rx_ring_size + + sizeof(*priv->tx_buffers) * priv->tx_ring_size, GFP_KERNEL); + if (!priv->rx_buffers) + return -ENOMEM; + + priv->tx_buffers = (void *)priv->rx_buffers + + sizeof(*priv->rx_buffers) * priv->rx_ring_size; + + /* Allocate TX/RX descriptors */ + ring_size = sizeof(struct adm8211_desc) * priv->rx_ring_size + + sizeof(struct adm8211_desc) * priv->tx_ring_size; + priv->rx_ring = pci_alloc_consistent(priv->pdev, ring_size, + &priv->rx_ring_dma); + + if (!priv->rx_ring) { + kfree(priv->rx_buffers); + priv->rx_buffers = NULL; + priv->tx_buffers = NULL; + return -ENOMEM; + } + + priv->tx_ring = priv->rx_ring + priv->rx_ring_size; + priv->tx_ring_dma = priv->rx_ring_dma + + sizeof(struct adm8211_desc) * priv->rx_ring_size; + + return 0; +} + +static const struct ieee80211_ops adm8211_ops = { + .tx = adm8211_tx, + .start = adm8211_start, + .stop = adm8211_stop, + .add_interface = adm8211_add_interface, + .remove_interface = adm8211_remove_interface, + .config = adm8211_config, + .bss_info_changed = adm8211_bss_info_changed, + .prepare_multicast = adm8211_prepare_multicast, + .configure_filter = adm8211_configure_filter, + .get_stats = adm8211_get_stats, + .get_tsf = adm8211_get_tsft +}; + +static int adm8211_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct ieee80211_hw *dev; + struct adm8211_priv *priv; + unsigned long mem_addr, mem_len; + unsigned int io_addr, io_len; + int err; + u32 reg; + u8 perm_addr[ETH_ALEN]; + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "%s (adm8211): Cannot enable new PCI device\n", + pci_name(pdev)); + return err; + } + + io_addr = pci_resource_start(pdev, 0); + io_len = pci_resource_len(pdev, 0); + mem_addr = pci_resource_start(pdev, 1); + mem_len = pci_resource_len(pdev, 1); + if (io_len < 256 || mem_len < 1024) { + printk(KERN_ERR "%s (adm8211): Too short PCI resources\n", + pci_name(pdev)); + goto err_disable_pdev; + } + + + /* check signature */ + pci_read_config_dword(pdev, 0x80 /* CR32 */, ®); + if (reg != ADM8211_SIG1 && reg != ADM8211_SIG2) { + printk(KERN_ERR "%s (adm8211): Invalid signature (0x%x)\n", + pci_name(pdev), reg); + goto err_disable_pdev; + } + + err = pci_request_regions(pdev, "adm8211"); + if (err) { + printk(KERN_ERR "%s (adm8211): Cannot obtain PCI resources\n", + pci_name(pdev)); + return err; /* someone else grabbed it? don't disable it */ + } + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) || + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { + printk(KERN_ERR "%s (adm8211): No suitable DMA available\n", + pci_name(pdev)); + goto err_free_reg; + } + + pci_set_master(pdev); + + dev = ieee80211_alloc_hw(sizeof(*priv), &adm8211_ops); + if (!dev) { + printk(KERN_ERR "%s (adm8211): ieee80211 alloc failed\n", + pci_name(pdev)); + err = -ENOMEM; + goto err_free_reg; + } + priv = dev->priv; + priv->pdev = pdev; + + spin_lock_init(&priv->lock); + + SET_IEEE80211_DEV(dev, &pdev->dev); + + pci_set_drvdata(pdev, dev); + + priv->map = pci_iomap(pdev, 1, mem_len); + if (!priv->map) + priv->map = pci_iomap(pdev, 0, io_len); + + if (!priv->map) { + printk(KERN_ERR "%s (adm8211): Cannot map device memory\n", + pci_name(pdev)); + err = -ENOMEM; + goto err_free_dev; + } + + priv->rx_ring_size = rx_ring_size; + priv->tx_ring_size = tx_ring_size; + + if (adm8211_alloc_rings(dev)) { + printk(KERN_ERR "%s (adm8211): Cannot allocate TX/RX ring\n", + pci_name(pdev)); + goto err_iounmap; + } + + *(__le32 *)perm_addr = cpu_to_le32(ADM8211_CSR_READ(PAR0)); + *(__le16 *)&perm_addr[4] = + cpu_to_le16(ADM8211_CSR_READ(PAR1) & 0xFFFF); + + if (!is_valid_ether_addr(perm_addr)) { + printk(KERN_WARNING "%s (adm8211): Invalid hwaddr in EEPROM!\n", + pci_name(pdev)); + eth_random_addr(perm_addr); + } + SET_IEEE80211_PERM_ADDR(dev, perm_addr); + + dev->extra_tx_headroom = sizeof(struct adm8211_tx_hdr); + /* dev->flags = RX_INCLUDES_FCS in promisc mode */ + ieee80211_hw_set(dev, SIGNAL_UNSPEC); + dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + + dev->max_signal = 100; /* FIXME: find better value */ + + dev->queues = 1; /* ADM8211C supports more, maybe ADM8211B too */ + + priv->retry_limit = 3; + priv->ant_power = 0x40; + priv->tx_power = 0x40; + priv->lpf_cutoff = 0xFF; + priv->lnags_threshold = 0xFF; + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + + /* Power-on issue. EEPROM won't read correctly without */ + if (pdev->revision >= ADM8211_REV_BA) { + ADM8211_CSR_WRITE(FRCTL, 0); + ADM8211_CSR_READ(FRCTL); + ADM8211_CSR_WRITE(FRCTL, 1); + ADM8211_CSR_READ(FRCTL); + msleep(100); + } + + err = adm8211_read_eeprom(dev); + if (err) { + printk(KERN_ERR "%s (adm8211): Can't alloc eeprom buffer\n", + pci_name(pdev)); + goto err_free_desc; + } + + priv->channel = 1; + + dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + + err = ieee80211_register_hw(dev); + if (err) { + printk(KERN_ERR "%s (adm8211): Cannot register device\n", + pci_name(pdev)); + goto err_free_eeprom; + } + + wiphy_info(dev->wiphy, "hwaddr %pM, Rev 0x%02x\n", + dev->wiphy->perm_addr, pdev->revision); + + return 0; + + err_free_eeprom: + kfree(priv->eeprom); + + err_free_desc: + pci_free_consistent(pdev, + sizeof(struct adm8211_desc) * priv->rx_ring_size + + sizeof(struct adm8211_desc) * priv->tx_ring_size, + priv->rx_ring, priv->rx_ring_dma); + kfree(priv->rx_buffers); + + err_iounmap: + pci_iounmap(pdev, priv->map); + + err_free_dev: + ieee80211_free_hw(dev); + + err_free_reg: + pci_release_regions(pdev); + + err_disable_pdev: + pci_disable_device(pdev); + return err; +} + + +static void adm8211_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + struct adm8211_priv *priv; + + if (!dev) + return; + + ieee80211_unregister_hw(dev); + + priv = dev->priv; + + pci_free_consistent(pdev, + sizeof(struct adm8211_desc) * priv->rx_ring_size + + sizeof(struct adm8211_desc) * priv->tx_ring_size, + priv->rx_ring, priv->rx_ring_dma); + + kfree(priv->rx_buffers); + kfree(priv->eeprom); + pci_iounmap(pdev, priv->map); + pci_release_regions(pdev); + pci_disable_device(pdev); + ieee80211_free_hw(dev); +} + + +#ifdef CONFIG_PM +static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state) +{ + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int adm8211_resume(struct pci_dev *pdev) +{ + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + return 0; +} +#endif /* CONFIG_PM */ + + +MODULE_DEVICE_TABLE(pci, adm8211_pci_id_table); + +/* TODO: implement enable_wake */ +static struct pci_driver adm8211_driver = { + .name = "adm8211", + .id_table = adm8211_pci_id_table, + .probe = adm8211_probe, + .remove = adm8211_remove, +#ifdef CONFIG_PM + .suspend = adm8211_suspend, + .resume = adm8211_resume, +#endif /* CONFIG_PM */ +}; + +module_pci_driver(adm8211_driver); diff --git a/drivers/net/wireless/admtek/adm8211.h b/drivers/net/wireless/admtek/adm8211.h new file mode 100644 index 000000000000..bbc10b1cde87 --- /dev/null +++ b/drivers/net/wireless/admtek/adm8211.h @@ -0,0 +1,602 @@ +#ifndef ADM8211_H +#define ADM8211_H + +/* ADM8211 Registers */ + +/* CR32 (SIG) signature */ +#define ADM8211_SIG1 0x82011317 /* ADM8211A */ +#define ADM8211_SIG2 0x82111317 /* ADM8211B/ADM8211C */ + +#define ADM8211_CSR_READ(r) ioread32(&priv->map->r) +#define ADM8211_CSR_WRITE(r, val) iowrite32((val), &priv->map->r) + +/* CSR (Host Control and Status Registers) */ +struct adm8211_csr { + __le32 PAR; /* 0x00 CSR0 */ + __le32 FRCTL; /* 0x04 CSR0A */ + __le32 TDR; /* 0x08 CSR1 */ + __le32 WTDP; /* 0x0C CSR1A */ + __le32 RDR; /* 0x10 CSR2 */ + __le32 WRDP; /* 0x14 CSR2A */ + __le32 RDB; /* 0x18 CSR3 */ + __le32 TDBH; /* 0x1C CSR3A */ + __le32 TDBD; /* 0x20 CSR4 */ + __le32 TDBP; /* 0x24 CSR4A */ + __le32 STSR; /* 0x28 CSR5 */ + __le32 TDBB; /* 0x2C CSR5A */ + __le32 NAR; /* 0x30 CSR6 */ + __le32 CSR6A; /* reserved */ + __le32 IER; /* 0x38 CSR7 */ + __le32 TKIPSCEP; /* 0x3C CSR7A */ + __le32 LPC; /* 0x40 CSR8 */ + __le32 CSR_TEST1; /* 0x44 CSR8A */ + __le32 SPR; /* 0x48 CSR9 */ + __le32 CSR_TEST0; /* 0x4C CSR9A */ + __le32 WCSR; /* 0x50 CSR10 */ + __le32 WPDR; /* 0x54 CSR10A */ + __le32 GPTMR; /* 0x58 CSR11 */ + __le32 GPIO; /* 0x5C CSR11A */ + __le32 BBPCTL; /* 0x60 CSR12 */ + __le32 SYNCTL; /* 0x64 CSR12A */ + __le32 PLCPHD; /* 0x68 CSR13 */ + __le32 MMIWA; /* 0x6C CSR13A */ + __le32 MMIRD0; /* 0x70 CSR14 */ + __le32 MMIRD1; /* 0x74 CSR14A */ + __le32 TXBR; /* 0x78 CSR15 */ + __le32 SYNDATA; /* 0x7C CSR15A */ + __le32 ALCS; /* 0x80 CSR16 */ + __le32 TOFS2; /* 0x84 CSR17 */ + __le32 CMDR; /* 0x88 CSR18 */ + __le32 PCIC; /* 0x8C CSR19 */ + __le32 PMCSR; /* 0x90 CSR20 */ + __le32 PAR0; /* 0x94 CSR21 */ + __le32 PAR1; /* 0x98 CSR22 */ + __le32 MAR0; /* 0x9C CSR23 */ + __le32 MAR1; /* 0xA0 CSR24 */ + __le32 ATIMDA0; /* 0xA4 CSR25 */ + __le32 ABDA1; /* 0xA8 CSR26 */ + __le32 BSSID0; /* 0xAC CSR27 */ + __le32 TXLMT; /* 0xB0 CSR28 */ + __le32 MIBCNT; /* 0xB4 CSR29 */ + __le32 BCNT; /* 0xB8 CSR30 */ + __le32 TSFTH; /* 0xBC CSR31 */ + __le32 TSC; /* 0xC0 CSR32 */ + __le32 SYNRF; /* 0xC4 CSR33 */ + __le32 BPLI; /* 0xC8 CSR34 */ + __le32 CAP0; /* 0xCC CSR35 */ + __le32 CAP1; /* 0xD0 CSR36 */ + __le32 RMD; /* 0xD4 CSR37 */ + __le32 CFPP; /* 0xD8 CSR38 */ + __le32 TOFS0; /* 0xDC CSR39 */ + __le32 TOFS1; /* 0xE0 CSR40 */ + __le32 IFST; /* 0xE4 CSR41 */ + __le32 RSPT; /* 0xE8 CSR42 */ + __le32 TSFTL; /* 0xEC CSR43 */ + __le32 WEPCTL; /* 0xF0 CSR44 */ + __le32 WESK; /* 0xF4 CSR45 */ + __le32 WEPCNT; /* 0xF8 CSR46 */ + __le32 MACTEST; /* 0xFC CSR47 */ + __le32 FER; /* 0x100 */ + __le32 FEMR; /* 0x104 */ + __le32 FPSR; /* 0x108 */ + __le32 FFER; /* 0x10C */ +} __packed; + +/* CSR0 - PAR (PCI Address Register) */ +#define ADM8211_PAR_MWIE (1 << 24) +#define ADM8211_PAR_MRLE (1 << 23) +#define ADM8211_PAR_MRME (1 << 21) +#define ADM8211_PAR_RAP ((1 << 18) | (1 << 17)) +#define ADM8211_PAR_CAL ((1 << 15) | (1 << 14)) +#define ADM8211_PAR_PBL 0x00003f00 +#define ADM8211_PAR_BLE (1 << 7) +#define ADM8211_PAR_DSL 0x0000007c +#define ADM8211_PAR_BAR (1 << 1) +#define ADM8211_PAR_SWR (1 << 0) + +/* CSR1 - FRCTL (Frame Control Register) */ +#define ADM8211_FRCTL_PWRMGT (1 << 31) +#define ADM8211_FRCTL_MAXPSP (1 << 27) +#define ADM8211_FRCTL_DRVPRSP (1 << 26) +#define ADM8211_FRCTL_DRVBCON (1 << 25) +#define ADM8211_FRCTL_AID 0x0000ffff +#define ADM8211_FRCTL_AID_ON 0x0000c000 + +/* CSR5 - STSR (Status Register) */ +#define ADM8211_STSR_PCF (1 << 31) +#define ADM8211_STSR_BCNTC (1 << 30) +#define ADM8211_STSR_GPINT (1 << 29) +#define ADM8211_STSR_LinkOff (1 << 28) +#define ADM8211_STSR_ATIMTC (1 << 27) +#define ADM8211_STSR_TSFTF (1 << 26) +#define ADM8211_STSR_TSCZ (1 << 25) +#define ADM8211_STSR_LinkOn (1 << 24) +#define ADM8211_STSR_SQL (1 << 23) +#define ADM8211_STSR_WEPTD (1 << 22) +#define ADM8211_STSR_ATIME (1 << 21) +#define ADM8211_STSR_TBTT (1 << 20) +#define ADM8211_STSR_NISS (1 << 16) +#define ADM8211_STSR_AISS (1 << 15) +#define ADM8211_STSR_TEIS (1 << 14) +#define ADM8211_STSR_FBE (1 << 13) +#define ADM8211_STSR_REIS (1 << 12) +#define ADM8211_STSR_GPTT (1 << 11) +#define ADM8211_STSR_RPS (1 << 8) +#define ADM8211_STSR_RDU (1 << 7) +#define ADM8211_STSR_RCI (1 << 6) +#define ADM8211_STSR_TUF (1 << 5) +#define ADM8211_STSR_TRT (1 << 4) +#define ADM8211_STSR_TLT (1 << 3) +#define ADM8211_STSR_TDU (1 << 2) +#define ADM8211_STSR_TPS (1 << 1) +#define ADM8211_STSR_TCI (1 << 0) + +/* CSR6 - NAR (Network Access Register) */ +#define ADM8211_NAR_TXCF (1 << 31) +#define ADM8211_NAR_HF (1 << 30) +#define ADM8211_NAR_UTR (1 << 29) +#define ADM8211_NAR_SQ (1 << 28) +#define ADM8211_NAR_CFP (1 << 27) +#define ADM8211_NAR_SF (1 << 21) +#define ADM8211_NAR_TR ((1 << 15) | (1 << 14)) +#define ADM8211_NAR_ST (1 << 13) +#define ADM8211_NAR_OM ((1 << 11) | (1 << 10)) +#define ADM8211_NAR_MM (1 << 7) +#define ADM8211_NAR_PR (1 << 6) +#define ADM8211_NAR_EA (1 << 5) +#define ADM8211_NAR_PB (1 << 3) +#define ADM8211_NAR_STPDMA (1 << 2) +#define ADM8211_NAR_SR (1 << 1) +#define ADM8211_NAR_CTX (1 << 0) + +#define ADM8211_IDLE() \ +do { \ + if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) { \ + ADM8211_CSR_WRITE(NAR, priv->nar & \ + ~(ADM8211_NAR_SR | ADM8211_NAR_ST));\ + ADM8211_CSR_READ(NAR); \ + msleep(20); \ + } \ +} while (0) + +#define ADM8211_IDLE_RX() \ +do { \ + if (priv->nar & ADM8211_NAR_SR) { \ + ADM8211_CSR_WRITE(NAR, priv->nar & ~ADM8211_NAR_SR); \ + ADM8211_CSR_READ(NAR); \ + mdelay(20); \ + } \ +} while (0) + +#define ADM8211_RESTORE() \ +do { \ + if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) \ + ADM8211_CSR_WRITE(NAR, priv->nar); \ +} while (0) + +/* CSR7 - IER (Interrupt Enable Register) */ +#define ADM8211_IER_PCFIE (1 << 31) +#define ADM8211_IER_BCNTCIE (1 << 30) +#define ADM8211_IER_GPIE (1 << 29) +#define ADM8211_IER_LinkOffIE (1 << 28) +#define ADM8211_IER_ATIMTCIE (1 << 27) +#define ADM8211_IER_TSFTFIE (1 << 26) +#define ADM8211_IER_TSCZE (1 << 25) +#define ADM8211_IER_LinkOnIE (1 << 24) +#define ADM8211_IER_SQLIE (1 << 23) +#define ADM8211_IER_WEPIE (1 << 22) +#define ADM8211_IER_ATIMEIE (1 << 21) +#define ADM8211_IER_TBTTIE (1 << 20) +#define ADM8211_IER_NIE (1 << 16) +#define ADM8211_IER_AIE (1 << 15) +#define ADM8211_IER_TEIE (1 << 14) +#define ADM8211_IER_FBEIE (1 << 13) +#define ADM8211_IER_REIE (1 << 12) +#define ADM8211_IER_GPTIE (1 << 11) +#define ADM8211_IER_RSIE (1 << 8) +#define ADM8211_IER_RUIE (1 << 7) +#define ADM8211_IER_RCIE (1 << 6) +#define ADM8211_IER_TUIE (1 << 5) +#define ADM8211_IER_TRTIE (1 << 4) +#define ADM8211_IER_TLTTIE (1 << 3) +#define ADM8211_IER_TDUIE (1 << 2) +#define ADM8211_IER_TPSIE (1 << 1) +#define ADM8211_IER_TCIE (1 << 0) + +/* CSR9 - SPR (Serial Port Register) */ +#define ADM8211_SPR_SRS (1 << 11) +#define ADM8211_SPR_SDO (1 << 3) +#define ADM8211_SPR_SDI (1 << 2) +#define ADM8211_SPR_SCLK (1 << 1) +#define ADM8211_SPR_SCS (1 << 0) + +/* CSR9A - CSR_TEST0 */ +#define ADM8211_CSR_TEST0_EPNE (1 << 18) +#define ADM8211_CSR_TEST0_EPSNM (1 << 17) +#define ADM8211_CSR_TEST0_EPTYP (1 << 16) +#define ADM8211_CSR_TEST0_EPRLD (1 << 15) + +/* CSR10 - WCSR (Wake-up Control/Status Register) */ +#define ADM8211_WCSR_CRCT (1 << 30) +#define ADM8211_WCSR_TSFTWE (1 << 20) +#define ADM8211_WCSR_TIMWE (1 << 19) +#define ADM8211_WCSR_ATIMWE (1 << 18) +#define ADM8211_WCSR_KEYWE (1 << 17) +#define ADM8211_WCSR_MPRE (1 << 9) +#define ADM8211_WCSR_LSOE (1 << 8) +#define ADM8211_WCSR_KEYUP (1 << 6) +#define ADM8211_WCSR_TSFTW (1 << 5) +#define ADM8211_WCSR_TIMW (1 << 4) +#define ADM8211_WCSR_ATIMW (1 << 3) +#define ADM8211_WCSR_MPR (1 << 1) +#define ADM8211_WCSR_LSO (1 << 0) + +/* CSR11A - GPIO */ +#define ADM8211_CSR_GPIO_EN5 (1 << 17) +#define ADM8211_CSR_GPIO_EN4 (1 << 16) +#define ADM8211_CSR_GPIO_EN3 (1 << 15) +#define ADM8211_CSR_GPIO_EN2 (1 << 14) +#define ADM8211_CSR_GPIO_EN1 (1 << 13) +#define ADM8211_CSR_GPIO_EN0 (1 << 12) +#define ADM8211_CSR_GPIO_O5 (1 << 11) +#define ADM8211_CSR_GPIO_O4 (1 << 10) +#define ADM8211_CSR_GPIO_O3 (1 << 9) +#define ADM8211_CSR_GPIO_O2 (1 << 8) +#define ADM8211_CSR_GPIO_O1 (1 << 7) +#define ADM8211_CSR_GPIO_O0 (1 << 6) +#define ADM8211_CSR_GPIO_IN 0x0000003f + +/* CSR12 - BBPCTL (BBP Control port) */ +#define ADM8211_BBPCTL_MMISEL (1 << 31) +#define ADM8211_BBPCTL_SPICADD (0x7F << 24) +#define ADM8211_BBPCTL_RF3000 (0x20 << 24) +#define ADM8211_BBPCTL_TXCE (1 << 23) +#define ADM8211_BBPCTL_RXCE (1 << 22) +#define ADM8211_BBPCTL_CCAP (1 << 21) +#define ADM8211_BBPCTL_TYPE 0x001c0000 +#define ADM8211_BBPCTL_WR (1 << 17) +#define ADM8211_BBPCTL_RD (1 << 16) +#define ADM8211_BBPCTL_ADDR 0x0000ff00 +#define ADM8211_BBPCTL_DATA 0x000000ff + +/* CSR12A - SYNCTL (Synthesizer Control port) */ +#define ADM8211_SYNCTL_WR (1 << 31) +#define ADM8211_SYNCTL_RD (1 << 30) +#define ADM8211_SYNCTL_CS0 (1 << 29) +#define ADM8211_SYNCTL_CS1 (1 << 28) +#define ADM8211_SYNCTL_CAL (1 << 27) +#define ADM8211_SYNCTL_SELCAL (1 << 26) +#define ADM8211_SYNCTL_RFtype ((1 << 24) | (1 << 23) | (1 << 22)) +#define ADM8211_SYNCTL_RFMD (1 << 22) +#define ADM8211_SYNCTL_GENERAL (0x7 << 22) +/* SYNCTL 21:0 Data (Si4126: 18-bit data, 4-bit address) */ + +/* CSR18 - CMDR (Command Register) */ +#define ADM8211_CMDR_PM (1 << 19) +#define ADM8211_CMDR_APM (1 << 18) +#define ADM8211_CMDR_RTE (1 << 4) +#define ADM8211_CMDR_DRT ((1 << 3) | (1 << 2)) +#define ADM8211_CMDR_DRT_8DW (0x0 << 2) +#define ADM8211_CMDR_DRT_16DW (0x1 << 2) +#define ADM8211_CMDR_DRT_SF (0x2 << 2) + +/* CSR33 - SYNRF (SYNRF direct control) */ +#define ADM8211_SYNRF_SELSYN (1 << 31) +#define ADM8211_SYNRF_SELRF (1 << 30) +#define ADM8211_SYNRF_LERF (1 << 29) +#define ADM8211_SYNRF_LEIF (1 << 28) +#define ADM8211_SYNRF_SYNCLK (1 << 27) +#define ADM8211_SYNRF_SYNDATA (1 << 26) +#define ADM8211_SYNRF_PE1 (1 << 25) +#define ADM8211_SYNRF_PE2 (1 << 24) +#define ADM8211_SYNRF_PA_PE (1 << 23) +#define ADM8211_SYNRF_TR_SW (1 << 22) +#define ADM8211_SYNRF_TR_SWN (1 << 21) +#define ADM8211_SYNRF_RADIO (1 << 20) +#define ADM8211_SYNRF_CAL_EN (1 << 19) +#define ADM8211_SYNRF_PHYRST (1 << 18) + +#define ADM8211_SYNRF_IF_SELECT_0 (1 << 31) +#define ADM8211_SYNRF_IF_SELECT_1 ((1 << 31) | (1 << 28)) +#define ADM8211_SYNRF_WRITE_SYNDATA_0 (1 << 31) +#define ADM8211_SYNRF_WRITE_SYNDATA_1 ((1 << 31) | (1 << 26)) +#define ADM8211_SYNRF_WRITE_CLOCK_0 (1 << 31) +#define ADM8211_SYNRF_WRITE_CLOCK_1 ((1 << 31) | (1 << 27)) + +/* CSR44 - WEPCTL (WEP Control) */ +#define ADM8211_WEPCTL_WEPENABLE (1 << 31) +#define ADM8211_WEPCTL_WPAENABLE (1 << 30) +#define ADM8211_WEPCTL_CURRENT_TABLE (1 << 29) +#define ADM8211_WEPCTL_TABLE_WR (1 << 28) +#define ADM8211_WEPCTL_TABLE_RD (1 << 27) +#define ADM8211_WEPCTL_WEPRXBYP (1 << 25) +#define ADM8211_WEPCTL_SEL_WEPTABLE (1 << 23) +#define ADM8211_WEPCTL_ADDR (0x000001ff) + +/* CSR45 - WESK (Data Entry for Share/Individual Key) */ +#define ADM8211_WESK_DATA (0x0000ffff) + +/* FER (Function Event Register) */ +#define ADM8211_FER_INTR_EV_ENT (1 << 15) + + +/* Si4126 RF Synthesizer - Control Registers */ +#define SI4126_MAIN_CONF 0 +#define SI4126_PHASE_DET_GAIN 1 +#define SI4126_POWERDOWN 2 +#define SI4126_RF1_N_DIV 3 /* only Si4136 */ +#define SI4126_RF2_N_DIV 4 +#define SI4126_IF_N_DIV 5 +#define SI4126_RF1_R_DIV 6 /* only Si4136 */ +#define SI4126_RF2_R_DIV 7 +#define SI4126_IF_R_DIV 8 + +/* Main Configuration */ +#define SI4126_MAIN_XINDIV2 (1 << 6) +#define SI4126_MAIN_IFDIV ((1 << 11) | (1 << 10)) +/* Powerdown */ +#define SI4126_POWERDOWN_PDIB (1 << 1) +#define SI4126_POWERDOWN_PDRB (1 << 0) + + +/* RF3000 BBP - Control Port Registers */ +/* 0x00 - reserved */ +#define RF3000_MODEM_CTRL__RX_STATUS 0x01 +#define RF3000_CCA_CTRL 0x02 +#define RF3000_DIVERSITY__RSSI 0x03 +#define RF3000_RX_SIGNAL_FIELD 0x04 +#define RF3000_RX_LEN_MSB 0x05 +#define RF3000_RX_LEN_LSB 0x06 +#define RF3000_RX_SERVICE_FIELD 0x07 +#define RF3000_TX_VAR_GAIN__TX_LEN_EXT 0x11 +#define RF3000_TX_LEN_MSB 0x12 +#define RF3000_TX_LEN_LSB 0x13 +#define RF3000_LOW_GAIN_CALIB 0x14 +#define RF3000_HIGH_GAIN_CALIB 0x15 + +/* ADM8211 revisions */ +#define ADM8211_REV_AB 0x11 +#define ADM8211_REV_AF 0x15 +#define ADM8211_REV_BA 0x20 +#define ADM8211_REV_CA 0x30 + +struct adm8211_desc { + __le32 status; + __le32 length; + __le32 buffer1; + __le32 buffer2; +}; + +#define RDES0_STATUS_OWN (1 << 31) +#define RDES0_STATUS_ES (1 << 30) +#define RDES0_STATUS_SQL (1 << 29) +#define RDES0_STATUS_DE (1 << 28) +#define RDES0_STATUS_FS (1 << 27) +#define RDES0_STATUS_LS (1 << 26) +#define RDES0_STATUS_PCF (1 << 25) +#define RDES0_STATUS_SFDE (1 << 24) +#define RDES0_STATUS_SIGE (1 << 23) +#define RDES0_STATUS_CRC16E (1 << 22) +#define RDES0_STATUS_RXTOE (1 << 21) +#define RDES0_STATUS_CRC32E (1 << 20) +#define RDES0_STATUS_ICVE (1 << 19) +#define RDES0_STATUS_DA1 (1 << 17) +#define RDES0_STATUS_DA0 (1 << 16) +#define RDES0_STATUS_RXDR ((1 << 15) | (1 << 14) | (1 << 13) | (1 << 12)) +#define RDES0_STATUS_FL (0x00000fff) + +#define RDES1_CONTROL_RER (1 << 25) +#define RDES1_CONTROL_RCH (1 << 24) +#define RDES1_CONTROL_RBS2 (0x00fff000) +#define RDES1_CONTROL_RBS1 (0x00000fff) + +#define RDES1_STATUS_RSSI (0x0000007f) + + +#define TDES0_CONTROL_OWN (1 << 31) +#define TDES0_CONTROL_DONE (1 << 30) +#define TDES0_CONTROL_TXDR (0x0ff00000) + +#define TDES0_STATUS_OWN (1 << 31) +#define TDES0_STATUS_DONE (1 << 30) +#define TDES0_STATUS_ES (1 << 29) +#define TDES0_STATUS_TLT (1 << 28) +#define TDES0_STATUS_TRT (1 << 27) +#define TDES0_STATUS_TUF (1 << 26) +#define TDES0_STATUS_TRO (1 << 25) +#define TDES0_STATUS_SOFBR (1 << 24) +#define TDES0_STATUS_ACR (0x00000fff) + +#define TDES1_CONTROL_IC (1 << 31) +#define TDES1_CONTROL_LS (1 << 30) +#define TDES1_CONTROL_FS (1 << 29) +#define TDES1_CONTROL_TER (1 << 25) +#define TDES1_CONTROL_TCH (1 << 24) +#define TDES1_CONTROL_RBS2 (0x00fff000) +#define TDES1_CONTROL_RBS1 (0x00000fff) + +/* SRAM offsets */ +#define ADM8211_SRAM(x) (priv->pdev->revision < ADM8211_REV_BA ? \ + ADM8211_SRAM_A_ ## x : ADM8211_SRAM_B_ ## x) + +#define ADM8211_SRAM_INDIV_KEY 0x0000 +#define ADM8211_SRAM_A_SHARE_KEY 0x0160 +#define ADM8211_SRAM_B_SHARE_KEY 0x00c0 + +#define ADM8211_SRAM_A_SSID 0x0180 +#define ADM8211_SRAM_B_SSID 0x00d4 +#define ADM8211_SRAM_SSID ADM8211_SRAM(SSID) + +#define ADM8211_SRAM_A_SUPP_RATE 0x0191 +#define ADM8211_SRAM_B_SUPP_RATE 0x00dd +#define ADM8211_SRAM_SUPP_RATE ADM8211_SRAM(SUPP_RATE) + +#define ADM8211_SRAM_A_SIZE 0x0200 +#define ADM8211_SRAM_B_SIZE 0x01c0 +#define ADM8211_SRAM_SIZE ADM8211_SRAM(SIZE) + +struct adm8211_rx_ring_info { + struct sk_buff *skb; + dma_addr_t mapping; +}; + +struct adm8211_tx_ring_info { + struct sk_buff *skb; + dma_addr_t mapping; + size_t hdrlen; +}; + +#define PLCP_SIGNAL_1M 0x0a +#define PLCP_SIGNAL_2M 0x14 +#define PLCP_SIGNAL_5M5 0x37 +#define PLCP_SIGNAL_11M 0x6e + +struct adm8211_tx_hdr { + u8 da[6]; + u8 signal; /* PLCP signal / TX rate in 100 Kbps */ + u8 service; + __le16 frame_body_size; + __le16 frame_control; + __le16 plcp_frag_tail_len; + __le16 plcp_frag_head_len; + __le16 dur_frag_tail; + __le16 dur_frag_head; + u8 addr4[6]; + +#define ADM8211_TXHDRCTL_SHORT_PREAMBLE (1 << 0) +#define ADM8211_TXHDRCTL_MORE_FRAG (1 << 1) +#define ADM8211_TXHDRCTL_MORE_DATA (1 << 2) +#define ADM8211_TXHDRCTL_FRAG_NO (1 << 3) /* ? */ +#define ADM8211_TXHDRCTL_ENABLE_RTS (1 << 4) +#define ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE (1 << 5) +#define ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER (1 << 15) /* ? */ + __le16 header_control; + __le16 frag; + u8 reserved_0; + u8 retry_limit; + + u32 wep2key0; + u32 wep2key1; + u32 wep2key2; + u32 wep2key3; + + u8 keyid; + u8 entry_control; // huh?? + u16 reserved_1; + u32 reserved_2; +} __packed; + + +#define RX_COPY_BREAK 128 +#define RX_PKT_SIZE 2500 + +struct adm8211_eeprom { + __le16 signature; /* 0x00 */ + u8 major_version; /* 0x02 */ + u8 minor_version; /* 0x03 */ + u8 reserved_1[4]; /* 0x04 */ + u8 hwaddr[6]; /* 0x08 */ + u8 reserved_2[8]; /* 0x1E */ + __le16 cr49; /* 0x16 */ + u8 cr03; /* 0x18 */ + u8 cr28; /* 0x19 */ + u8 cr29; /* 0x1A */ + u8 country_code; /* 0x1B */ + +/* specific bbp types */ +#define ADM8211_BBP_RFMD3000 0x00 +#define ADM8211_BBP_RFMD3002 0x01 +#define ADM8211_BBP_ADM8011 0x04 + u8 specific_bbptype; /* 0x1C */ + u8 specific_rftype; /* 0x1D */ + u8 reserved_3[2]; /* 0x1E */ + __le16 device_id; /* 0x20 */ + __le16 vendor_id; /* 0x22 */ + __le16 subsystem_id; /* 0x24 */ + __le16 subsystem_vendor_id; /* 0x26 */ + u8 maxlat; /* 0x28 */ + u8 mingnt; /* 0x29 */ + __le16 cis_pointer_low; /* 0x2A */ + __le16 cis_pointer_high; /* 0x2C */ + __le16 csr18; /* 0x2E */ + u8 reserved_4[16]; /* 0x30 */ + u8 d1_pwrdara; /* 0x40 */ + u8 d0_pwrdara; /* 0x41 */ + u8 d3_pwrdara; /* 0x42 */ + u8 d2_pwrdara; /* 0x43 */ + u8 antenna_power[14]; /* 0x44 */ + __le16 cis_wordcnt; /* 0x52 */ + u8 tx_power[14]; /* 0x54 */ + u8 lpf_cutoff[14]; /* 0x62 */ + u8 lnags_threshold[14]; /* 0x70 */ + __le16 checksum; /* 0x7E */ + u8 cis_data[0]; /* 0x80, 384 bytes */ +} __packed; + +struct adm8211_priv { + struct pci_dev *pdev; + spinlock_t lock; + struct adm8211_csr __iomem *map; + struct adm8211_desc *rx_ring; + struct adm8211_desc *tx_ring; + dma_addr_t rx_ring_dma; + dma_addr_t tx_ring_dma; + struct adm8211_rx_ring_info *rx_buffers; + struct adm8211_tx_ring_info *tx_buffers; + unsigned int rx_ring_size, tx_ring_size; + unsigned int cur_tx, dirty_tx, cur_rx; + + struct ieee80211_low_level_stats stats; + struct ieee80211_supported_band band; + struct ieee80211_channel channels[14]; + int mode; + + int channel; + u8 bssid[ETH_ALEN]; + + u8 soft_rx_crc; + u8 retry_limit; + + u8 ant_power; + u8 tx_power; + u8 lpf_cutoff; + u8 lnags_threshold; + struct adm8211_eeprom *eeprom; + size_t eeprom_len; + + u32 nar; + +#define ADM8211_TYPE_INTERSIL 0x00 +#define ADM8211_TYPE_RFMD 0x01 +#define ADM8211_TYPE_MARVEL 0x02 +#define ADM8211_TYPE_AIROHA 0x03 +#define ADM8211_TYPE_ADMTEK 0x05 + unsigned int rf_type:3; + unsigned int bbp_type:3; + + u8 specific_bbptype; + enum { + ADM8211_RFMD2948 = 0x0, + ADM8211_RFMD2958 = 0x1, + ADM8211_RFMD2958_RF3000_CONTROL_POWER = 0x2, + ADM8211_MAX2820 = 0x8, + ADM8211_AL2210L = 0xC, /* Airoha */ + } transceiver_type; +}; + +struct ieee80211_chan_range { + u8 min; + u8 max; +}; + +static const struct ieee80211_chan_range cranges[] = { + {1, 11}, /* FCC */ + {1, 11}, /* IC */ + {1, 13}, /* ETSI */ + {10, 11}, /* SPAIN */ + {10, 13}, /* FRANCE */ + {14, 14}, /* MMK */ + {1, 14}, /* MMK2 */ +}; + +#endif /* ADM8211_H */ -- cgit v1.2.3-59-g8ed1b From 59df9bb25cf8a13443b8335a7a9013817c050b88 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 4 Nov 2015 08:13:00 -0200 Subject: nxp-nci: i2c: Do not check specifically for -EREMOTEIO error Function nxp_nci_i2c_write currently assumes in case of I2C bus NACK that the NFC device is in stand-by mode and will retry the I2C transaction after a pause. This assumes that the first failed I2C transaction will wake-up the device. This is done by checking on EREMOTEIO, which is wrong. According to Documentation/i2c/fault-codes ENXIO shall be used. Unfortunately the NOACK return code is currently inconsistent across various I2C host controller drivers. So only check for the generic error case instead. This is a temporary fix. As soon as all I2C bus master drivers are fixed to consistently return 'ENXIO', then we can do the specific error check again. Signed-off-by: Fabio Estevam Signed-off-by: Samuel Ortiz --- drivers/nfc/nxp-nci/i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index df4333c7ee0f..ec359fc10b48 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -85,7 +85,7 @@ static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb) return phy->hard_fault; r = i2c_master_send(client, skb->data, skb->len); - if (r == -EREMOTEIO) { + if (r < 0) { /* Retry, chip was in standby */ usleep_range(110000, 120000); r = i2c_master_send(client, skb->data, skb->len); -- cgit v1.2.3-59-g8ed1b From 30db0ca8bc78e9ba98c6c71edcd46ff6dd21cbf6 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 19:49:19 +0200 Subject: airo: move under cisco vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- drivers/net/wireless/Kconfig | 43 +- drivers/net/wireless/Makefile | 4 +- drivers/net/wireless/airo.c | 8236 ---------------------------------- drivers/net/wireless/airo.h | 9 - drivers/net/wireless/airo_cs.c | 222 - drivers/net/wireless/cisco/Kconfig | 56 + drivers/net/wireless/cisco/Makefile | 2 + drivers/net/wireless/cisco/airo.c | 8236 ++++++++++++++++++++++++++++++++++ drivers/net/wireless/cisco/airo.h | 9 + drivers/net/wireless/cisco/airo_cs.c | 222 + 10 files changed, 8527 insertions(+), 8512 deletions(-) delete mode 100644 drivers/net/wireless/airo.c delete mode 100644 drivers/net/wireless/airo.h delete mode 100644 drivers/net/wireless/airo_cs.c create mode 100644 drivers/net/wireless/cisco/Kconfig create mode 100644 drivers/net/wireless/cisco/Makefile create mode 100644 drivers/net/wireless/cisco/airo.c create mode 100644 drivers/net/wireless/cisco/airo.h create mode 100644 drivers/net/wireless/cisco/airo_cs.c (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 7d8f47117c4d..ce5211ba1b29 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -18,6 +18,7 @@ menuconfig WLAN if WLAN source "drivers/net/wireless/admtek/Kconfig" +source "drivers/net/wireless/cisco/Kconfig" config PCMCIA_RAYCS tristate "Aviator/Raytheon 2.4GHz wireless support" @@ -53,26 +54,6 @@ config LIBERTAS_THINFIRM_USB ---help--- A driver for Marvell Libertas 8388 USB devices using thinfirm. -config AIRO - tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" - depends on CFG80211 && ISA_DMA_API && (PCI || BROKEN) - select WIRELESS_EXT - select CRYPTO - select WEXT_SPY - select WEXT_PRIV - ---help--- - This is the standard Linux driver to support Cisco/Aironet ISA and - PCI 802.11 wireless cards. - It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X - - with or without encryption) as well as card before the Cisco - acquisition (Aironet 4500, Aironet 4800, Aironet 4800B). - - This driver support both the standard Linux Wireless Extensions - and Cisco proprietary API, so both the Linux Wireless Tools and the - Cisco Linux utilities can be used to configure the card. - - The driver can be compiled as a module and will be named "airo". - config ATMEL tristate "Atmel at76c50x chipset 802.11b support" depends on CFG80211 && (PCI || PCMCIA) @@ -116,28 +97,6 @@ config AT76C50X_USB Enable support for USB Wireless devices using Atmel at76c503, at76c505 or at76c505a chips. -config AIRO_CS - tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards" - depends on CFG80211 && PCMCIA && (BROKEN || !M32R) - select WIRELESS_EXT - select WEXT_SPY - select WEXT_PRIV - select CRYPTO - select CRYPTO_AES - ---help--- - This is the standard Linux driver to support Cisco/Aironet PCMCIA - 802.11 wireless cards. This driver is the same as the Aironet - driver part of the Linux Pcmcia package. - It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X - - with or without encryption) as well as card before the Cisco - acquisition (Aironet 4500, Aironet 4800, Aironet 4800B). It also - supports OEM of Cisco such as the DELL TrueMobile 4800 and Xircom - 802.11b cards. - - This driver support both the standard Linux Wireless Extensions - and Cisco proprietary API, so both the Linux Wireless Tools and the - Cisco Linux utilities can be used to configure the card. - config PCMCIA_WL3501 tristate "Planet WL3501 PCMCIA cards" depends on CFG80211 && PCMCIA diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index be215f43ec1d..853294d84fa9 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -3,15 +3,13 @@ # obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/ +obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ obj-$(CONFIG_IPW2100) += ipw2x00/ obj-$(CONFIG_IPW2200) += ipw2x00/ obj-$(CONFIG_HERMES) += orinoco/ -obj-$(CONFIG_AIRO) += airo.o -obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o - obj-$(CONFIG_ATMEL) += atmel.o obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c deleted file mode 100644 index 17c40f06f13e..000000000000 --- a/drivers/net/wireless/airo.c +++ /dev/null @@ -1,8236 +0,0 @@ -/*====================================================================== - - Aironet driver for 4500 and 4800 series cards - - This code is released under both the GPL version 2 and BSD licenses. - Either license may be used. The respective licenses are found at - the end of this file. - - This code was developed by Benjamin Reed - including portions of which come from the Aironet PC4500 - Developer's Reference Manual and used with permission. Copyright - (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use - code in the Developer's manual was granted for this driver by - Aironet. Major code contributions were received from Javier Achirica - and Jean Tourrilhes . - Code was also integrated from the Cisco Aironet driver for Linux. - Support for MPI350 cards was added by Fabrice Bellet - . - -======================================================================*/ - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "airo.h" - -#define DRV_NAME "airo" - -#ifdef CONFIG_PCI -static const struct pci_device_id card_ids[] = { - { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID }, - { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, card_ids); - -static int airo_pci_probe(struct pci_dev *, const struct pci_device_id *); -static void airo_pci_remove(struct pci_dev *); -static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state); -static int airo_pci_resume(struct pci_dev *pdev); - -static struct pci_driver airo_driver = { - .name = DRV_NAME, - .id_table = card_ids, - .probe = airo_pci_probe, - .remove = airo_pci_remove, - .suspend = airo_pci_suspend, - .resume = airo_pci_resume, -}; -#endif /* CONFIG_PCI */ - -/* Include Wireless Extension definition and check version - Jean II */ -#include -#define WIRELESS_SPY /* enable iwspy support */ - -#define CISCO_EXT /* enable Cisco extensions */ -#ifdef CISCO_EXT -#include -#endif - -/* Hack to do some power saving */ -#define POWER_ON_DOWN - -/* As you can see this list is HUGH! - I really don't know what a lot of these counts are about, but they - are all here for completeness. If the IGNLABEL macro is put in - infront of the label, that statistic will not be included in the list - of statistics in the /proc filesystem */ - -#define IGNLABEL(comment) NULL -static const char *statsLabels[] = { - "RxOverrun", - IGNLABEL("RxPlcpCrcErr"), - IGNLABEL("RxPlcpFormatErr"), - IGNLABEL("RxPlcpLengthErr"), - "RxMacCrcErr", - "RxMacCrcOk", - "RxWepErr", - "RxWepOk", - "RetryLong", - "RetryShort", - "MaxRetries", - "NoAck", - "NoCts", - "RxAck", - "RxCts", - "TxAck", - "TxRts", - "TxCts", - "TxMc", - "TxBc", - "TxUcFrags", - "TxUcPackets", - "TxBeacon", - "RxBeacon", - "TxSinColl", - "TxMulColl", - "DefersNo", - "DefersProt", - "DefersEngy", - "DupFram", - "RxFragDisc", - "TxAged", - "RxAged", - "LostSync-MaxRetry", - "LostSync-MissedBeacons", - "LostSync-ArlExceeded", - "LostSync-Deauth", - "LostSync-Disassoced", - "LostSync-TsfTiming", - "HostTxMc", - "HostTxBc", - "HostTxUc", - "HostTxFail", - "HostRxMc", - "HostRxBc", - "HostRxUc", - "HostRxDiscard", - IGNLABEL("HmacTxMc"), - IGNLABEL("HmacTxBc"), - IGNLABEL("HmacTxUc"), - IGNLABEL("HmacTxFail"), - IGNLABEL("HmacRxMc"), - IGNLABEL("HmacRxBc"), - IGNLABEL("HmacRxUc"), - IGNLABEL("HmacRxDiscard"), - IGNLABEL("HmacRxAccepted"), - "SsidMismatch", - "ApMismatch", - "RatesMismatch", - "AuthReject", - "AuthTimeout", - "AssocReject", - "AssocTimeout", - IGNLABEL("ReasonOutsideTable"), - IGNLABEL("ReasonStatus1"), - IGNLABEL("ReasonStatus2"), - IGNLABEL("ReasonStatus3"), - IGNLABEL("ReasonStatus4"), - IGNLABEL("ReasonStatus5"), - IGNLABEL("ReasonStatus6"), - IGNLABEL("ReasonStatus7"), - IGNLABEL("ReasonStatus8"), - IGNLABEL("ReasonStatus9"), - IGNLABEL("ReasonStatus10"), - IGNLABEL("ReasonStatus11"), - IGNLABEL("ReasonStatus12"), - IGNLABEL("ReasonStatus13"), - IGNLABEL("ReasonStatus14"), - IGNLABEL("ReasonStatus15"), - IGNLABEL("ReasonStatus16"), - IGNLABEL("ReasonStatus17"), - IGNLABEL("ReasonStatus18"), - IGNLABEL("ReasonStatus19"), - "RxMan", - "TxMan", - "RxRefresh", - "TxRefresh", - "RxPoll", - "TxPoll", - "HostRetries", - "LostSync-HostReq", - "HostTxBytes", - "HostRxBytes", - "ElapsedUsec", - "ElapsedSec", - "LostSyncBetterAP", - "PrivacyMismatch", - "Jammed", - "DiscRxNotWepped", - "PhyEleMismatch", - (char*)-1 }; -#ifndef RUN_AT -#define RUN_AT(x) (jiffies+(x)) -#endif - - -/* These variables are for insmod, since it seems that the rates - can only be set in setup_card. Rates should be a comma separated - (no spaces) list of rates (up to 8). */ - -static int rates[8]; -static char *ssids[3]; - -static int io[4]; -static int irq[4]; - -static -int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at. - 0 means no limit. For old cards this was 4 */ - -static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */ -static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read - the bap, needed on some older cards and buses. */ -static int adhoc; - -static int probe = 1; - -static kuid_t proc_kuid; -static int proc_uid /* = 0 */; - -static kgid_t proc_kgid; -static int proc_gid /* = 0 */; - -static int airo_perm = 0555; - -static int proc_perm = 0644; - -MODULE_AUTHOR("Benjamin Reed"); -MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet cards. " - "Direct support for ISA/PCI/MPI cards and support for PCMCIA when used with airo_cs."); -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350"); -module_param_array(io, int, NULL, 0); -module_param_array(irq, int, NULL, 0); -module_param_array(rates, int, NULL, 0); -module_param_array(ssids, charp, NULL, 0); -module_param(auto_wep, int, 0); -MODULE_PARM_DESC(auto_wep, - "If non-zero, the driver will keep looping through the authentication options until an association is made. " - "The value of auto_wep is number of the wep keys to check. " - "A value of 2 will try using the key at index 0 and index 1."); -module_param(aux_bap, int, 0); -MODULE_PARM_DESC(aux_bap, - "If non-zero, the driver will switch into a mode that seems to work better for older cards with some older buses. " - "Before switching it checks that the switch is needed."); -module_param(maxencrypt, int, 0); -MODULE_PARM_DESC(maxencrypt, - "The maximum speed that the card can do encryption. " - "Units are in 512kbs. " - "Zero (default) means there is no limit. " - "Older cards used to be limited to 2mbs (4)."); -module_param(adhoc, int, 0); -MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode."); -module_param(probe, int, 0); -MODULE_PARM_DESC(probe, "If zero, the driver won't start the card."); - -module_param(proc_uid, int, 0); -MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to."); -module_param(proc_gid, int, 0); -MODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to."); -module_param(airo_perm, int, 0); -MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet."); -module_param(proc_perm, int, 0); -MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc"); - -/* This is a kind of sloppy hack to get this information to OUT4500 and - IN4500. I would be extremely interested in the situation where this - doesn't work though!!! */ -static int do8bitIO /* = 0 */; - -/* Return codes */ -#define SUCCESS 0 -#define ERROR -1 -#define NO_PACKET -2 - -/* Commands */ -#define NOP2 0x0000 -#define MAC_ENABLE 0x0001 -#define MAC_DISABLE 0x0002 -#define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */ -#define CMD_SOFTRESET 0x0004 -#define HOSTSLEEP 0x0005 -#define CMD_MAGIC_PKT 0x0006 -#define CMD_SETWAKEMASK 0x0007 -#define CMD_READCFG 0x0008 -#define CMD_SETMODE 0x0009 -#define CMD_ALLOCATETX 0x000a -#define CMD_TRANSMIT 0x000b -#define CMD_DEALLOCATETX 0x000c -#define NOP 0x0010 -#define CMD_WORKAROUND 0x0011 -#define CMD_ALLOCATEAUX 0x0020 -#define CMD_ACCESS 0x0021 -#define CMD_PCIBAP 0x0022 -#define CMD_PCIAUX 0x0023 -#define CMD_ALLOCBUF 0x0028 -#define CMD_GETTLV 0x0029 -#define CMD_PUTTLV 0x002a -#define CMD_DELTLV 0x002b -#define CMD_FINDNEXTTLV 0x002c -#define CMD_PSPNODES 0x0030 -#define CMD_SETCW 0x0031 -#define CMD_SETPCF 0x0032 -#define CMD_SETPHYREG 0x003e -#define CMD_TXTEST 0x003f -#define MAC_ENABLETX 0x0101 -#define CMD_LISTBSS 0x0103 -#define CMD_SAVECFG 0x0108 -#define CMD_ENABLEAUX 0x0111 -#define CMD_WRITERID 0x0121 -#define CMD_USEPSPNODES 0x0130 -#define MAC_ENABLERX 0x0201 - -/* Command errors */ -#define ERROR_QUALIF 0x00 -#define ERROR_ILLCMD 0x01 -#define ERROR_ILLFMT 0x02 -#define ERROR_INVFID 0x03 -#define ERROR_INVRID 0x04 -#define ERROR_LARGE 0x05 -#define ERROR_NDISABL 0x06 -#define ERROR_ALLOCBSY 0x07 -#define ERROR_NORD 0x0B -#define ERROR_NOWR 0x0C -#define ERROR_INVFIDTX 0x0D -#define ERROR_TESTACT 0x0E -#define ERROR_TAGNFND 0x12 -#define ERROR_DECODE 0x20 -#define ERROR_DESCUNAV 0x21 -#define ERROR_BADLEN 0x22 -#define ERROR_MODE 0x80 -#define ERROR_HOP 0x81 -#define ERROR_BINTER 0x82 -#define ERROR_RXMODE 0x83 -#define ERROR_MACADDR 0x84 -#define ERROR_RATES 0x85 -#define ERROR_ORDER 0x86 -#define ERROR_SCAN 0x87 -#define ERROR_AUTH 0x88 -#define ERROR_PSMODE 0x89 -#define ERROR_RTYPE 0x8A -#define ERROR_DIVER 0x8B -#define ERROR_SSID 0x8C -#define ERROR_APLIST 0x8D -#define ERROR_AUTOWAKE 0x8E -#define ERROR_LEAP 0x8F - -/* Registers */ -#define COMMAND 0x00 -#define PARAM0 0x02 -#define PARAM1 0x04 -#define PARAM2 0x06 -#define STATUS 0x08 -#define RESP0 0x0a -#define RESP1 0x0c -#define RESP2 0x0e -#define LINKSTAT 0x10 -#define SELECT0 0x18 -#define OFFSET0 0x1c -#define RXFID 0x20 -#define TXALLOCFID 0x22 -#define TXCOMPLFID 0x24 -#define DATA0 0x36 -#define EVSTAT 0x30 -#define EVINTEN 0x32 -#define EVACK 0x34 -#define SWS0 0x28 -#define SWS1 0x2a -#define SWS2 0x2c -#define SWS3 0x2e -#define AUXPAGE 0x3A -#define AUXOFF 0x3C -#define AUXDATA 0x3E - -#define FID_TX 1 -#define FID_RX 2 -/* Offset into aux memory for descriptors */ -#define AUX_OFFSET 0x800 -/* Size of allocated packets */ -#define PKTSIZE 1840 -#define RIDSIZE 2048 -/* Size of the transmit queue */ -#define MAXTXQ 64 - -/* BAP selectors */ -#define BAP0 0 /* Used for receiving packets */ -#define BAP1 2 /* Used for xmiting packets and working with RIDS */ - -/* Flags */ -#define COMMAND_BUSY 0x8000 - -#define BAP_BUSY 0x8000 -#define BAP_ERR 0x4000 -#define BAP_DONE 0x2000 - -#define PROMISC 0xffff -#define NOPROMISC 0x0000 - -#define EV_CMD 0x10 -#define EV_CLEARCOMMANDBUSY 0x4000 -#define EV_RX 0x01 -#define EV_TX 0x02 -#define EV_TXEXC 0x04 -#define EV_ALLOC 0x08 -#define EV_LINK 0x80 -#define EV_AWAKE 0x100 -#define EV_TXCPY 0x400 -#define EV_UNKNOWN 0x800 -#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */ -#define EV_AWAKEN 0x2000 -#define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC) - -#ifdef CHECK_UNKNOWN_INTS -#define IGNORE_INTS ( EV_CMD | EV_UNKNOWN) -#else -#define IGNORE_INTS (~STATUS_INTS) -#endif - -/* RID TYPES */ -#define RID_RW 0x20 - -/* The RIDs */ -#define RID_CAPABILITIES 0xFF00 -#define RID_APINFO 0xFF01 -#define RID_RADIOINFO 0xFF02 -#define RID_UNKNOWN3 0xFF03 -#define RID_RSSI 0xFF04 -#define RID_CONFIG 0xFF10 -#define RID_SSID 0xFF11 -#define RID_APLIST 0xFF12 -#define RID_DRVNAME 0xFF13 -#define RID_ETHERENCAP 0xFF14 -#define RID_WEP_TEMP 0xFF15 -#define RID_WEP_PERM 0xFF16 -#define RID_MODULATION 0xFF17 -#define RID_OPTIONS 0xFF18 -#define RID_ACTUALCONFIG 0xFF20 /*readonly*/ -#define RID_FACTORYCONFIG 0xFF21 -#define RID_UNKNOWN22 0xFF22 -#define RID_LEAPUSERNAME 0xFF23 -#define RID_LEAPPASSWORD 0xFF24 -#define RID_STATUS 0xFF50 -#define RID_BEACON_HST 0xFF51 -#define RID_BUSY_HST 0xFF52 -#define RID_RETRIES_HST 0xFF53 -#define RID_UNKNOWN54 0xFF54 -#define RID_UNKNOWN55 0xFF55 -#define RID_UNKNOWN56 0xFF56 -#define RID_MIC 0xFF57 -#define RID_STATS16 0xFF60 -#define RID_STATS16DELTA 0xFF61 -#define RID_STATS16DELTACLEAR 0xFF62 -#define RID_STATS 0xFF68 -#define RID_STATSDELTA 0xFF69 -#define RID_STATSDELTACLEAR 0xFF6A -#define RID_ECHOTEST_RID 0xFF70 -#define RID_ECHOTEST_RESULTS 0xFF71 -#define RID_BSSLISTFIRST 0xFF72 -#define RID_BSSLISTNEXT 0xFF73 -#define RID_WPA_BSSLISTFIRST 0xFF74 -#define RID_WPA_BSSLISTNEXT 0xFF75 - -typedef struct { - u16 cmd; - u16 parm0; - u16 parm1; - u16 parm2; -} Cmd; - -typedef struct { - u16 status; - u16 rsp0; - u16 rsp1; - u16 rsp2; -} Resp; - -/* - * Rids and endian-ness: The Rids will always be in cpu endian, since - * this all the patches from the big-endian guys end up doing that. - * so all rid access should use the read/writeXXXRid routines. - */ - -/* This structure came from an email sent to me from an engineer at - aironet for inclusion into this driver */ -typedef struct WepKeyRid WepKeyRid; -struct WepKeyRid { - __le16 len; - __le16 kindex; - u8 mac[ETH_ALEN]; - __le16 klen; - u8 key[16]; -} __packed; - -/* These structures are from the Aironet's PC4500 Developers Manual */ -typedef struct Ssid Ssid; -struct Ssid { - __le16 len; - u8 ssid[32]; -} __packed; - -typedef struct SsidRid SsidRid; -struct SsidRid { - __le16 len; - Ssid ssids[3]; -} __packed; - -typedef struct ModulationRid ModulationRid; -struct ModulationRid { - __le16 len; - __le16 modulation; -#define MOD_DEFAULT cpu_to_le16(0) -#define MOD_CCK cpu_to_le16(1) -#define MOD_MOK cpu_to_le16(2) -} __packed; - -typedef struct ConfigRid ConfigRid; -struct ConfigRid { - __le16 len; /* sizeof(ConfigRid) */ - __le16 opmode; /* operating mode */ -#define MODE_STA_IBSS cpu_to_le16(0) -#define MODE_STA_ESS cpu_to_le16(1) -#define MODE_AP cpu_to_le16(2) -#define MODE_AP_RPTR cpu_to_le16(3) -#define MODE_CFG_MASK cpu_to_le16(0xff) -#define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */ -#define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */ -#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extenstions */ -#define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */ -#define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */ -#define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */ -#define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */ -#define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */ -#define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */ - __le16 rmode; /* receive mode */ -#define RXMODE_BC_MC_ADDR cpu_to_le16(0) -#define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */ -#define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */ -#define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */ -#define RXMODE_RFMON_ANYBSS cpu_to_le16(4) -#define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */ -#define RXMODE_MASK cpu_to_le16(255) -#define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */ -#define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER) -#define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */ - __le16 fragThresh; - __le16 rtsThres; - u8 macAddr[ETH_ALEN]; - u8 rates[8]; - __le16 shortRetryLimit; - __le16 longRetryLimit; - __le16 txLifetime; /* in kusec */ - __le16 rxLifetime; /* in kusec */ - __le16 stationary; - __le16 ordering; - __le16 u16deviceType; /* for overriding device type */ - __le16 cfpRate; - __le16 cfpDuration; - __le16 _reserved1[3]; - /*---------- Scanning/Associating ----------*/ - __le16 scanMode; -#define SCANMODE_ACTIVE cpu_to_le16(0) -#define SCANMODE_PASSIVE cpu_to_le16(1) -#define SCANMODE_AIROSCAN cpu_to_le16(2) - __le16 probeDelay; /* in kusec */ - __le16 probeEnergyTimeout; /* in kusec */ - __le16 probeResponseTimeout; - __le16 beaconListenTimeout; - __le16 joinNetTimeout; - __le16 authTimeout; - __le16 authType; -#define AUTH_OPEN cpu_to_le16(0x1) -#define AUTH_ENCRYPT cpu_to_le16(0x101) -#define AUTH_SHAREDKEY cpu_to_le16(0x102) -#define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200) - __le16 associationTimeout; - __le16 specifiedApTimeout; - __le16 offlineScanInterval; - __le16 offlineScanDuration; - __le16 linkLossDelay; - __le16 maxBeaconLostTime; - __le16 refreshInterval; -#define DISABLE_REFRESH cpu_to_le16(0xFFFF) - __le16 _reserved1a[1]; - /*---------- Power save operation ----------*/ - __le16 powerSaveMode; -#define POWERSAVE_CAM cpu_to_le16(0) -#define POWERSAVE_PSP cpu_to_le16(1) -#define POWERSAVE_PSPCAM cpu_to_le16(2) - __le16 sleepForDtims; - __le16 listenInterval; - __le16 fastListenInterval; - __le16 listenDecay; - __le16 fastListenDelay; - __le16 _reserved2[2]; - /*---------- Ap/Ibss config items ----------*/ - __le16 beaconPeriod; - __le16 atimDuration; - __le16 hopPeriod; - __le16 channelSet; - __le16 channel; - __le16 dtimPeriod; - __le16 bridgeDistance; - __le16 radioID; - /*---------- Radio configuration ----------*/ - __le16 radioType; -#define RADIOTYPE_DEFAULT cpu_to_le16(0) -#define RADIOTYPE_802_11 cpu_to_le16(1) -#define RADIOTYPE_LEGACY cpu_to_le16(2) - u8 rxDiversity; - u8 txDiversity; - __le16 txPower; -#define TXPOWER_DEFAULT 0 - __le16 rssiThreshold; -#define RSSI_DEFAULT 0 - __le16 modulation; -#define PREAMBLE_AUTO cpu_to_le16(0) -#define PREAMBLE_LONG cpu_to_le16(1) -#define PREAMBLE_SHORT cpu_to_le16(2) - __le16 preamble; - __le16 homeProduct; - __le16 radioSpecific; - /*---------- Aironet Extensions ----------*/ - u8 nodeName[16]; - __le16 arlThreshold; - __le16 arlDecay; - __le16 arlDelay; - __le16 _reserved4[1]; - /*---------- Aironet Extensions ----------*/ - u8 magicAction; -#define MAGIC_ACTION_STSCHG 1 -#define MAGIC_ACTION_RESUME 2 -#define MAGIC_IGNORE_MCAST (1<<8) -#define MAGIC_IGNORE_BCAST (1<<9) -#define MAGIC_SWITCH_TO_PSP (0<<10) -#define MAGIC_STAY_IN_CAM (1<<10) - u8 magicControl; - __le16 autoWake; -} __packed; - -typedef struct StatusRid StatusRid; -struct StatusRid { - __le16 len; - u8 mac[ETH_ALEN]; - __le16 mode; - __le16 errorCode; - __le16 sigQuality; - __le16 SSIDlen; - char SSID[32]; - char apName[16]; - u8 bssid[4][ETH_ALEN]; - __le16 beaconPeriod; - __le16 dimPeriod; - __le16 atimDuration; - __le16 hopPeriod; - __le16 channelSet; - __le16 channel; - __le16 hopsToBackbone; - __le16 apTotalLoad; - __le16 generatedLoad; - __le16 accumulatedArl; - __le16 signalQuality; - __le16 currentXmitRate; - __le16 apDevExtensions; - __le16 normalizedSignalStrength; - __le16 shortPreamble; - u8 apIP[4]; - u8 noisePercent; /* Noise percent in last second */ - u8 noisedBm; /* Noise dBm in last second */ - u8 noiseAvePercent; /* Noise percent in last minute */ - u8 noiseAvedBm; /* Noise dBm in last minute */ - u8 noiseMaxPercent; /* Highest noise percent in last minute */ - u8 noiseMaxdBm; /* Highest noise dbm in last minute */ - __le16 load; - u8 carrier[4]; - __le16 assocStatus; -#define STAT_NOPACKETS 0 -#define STAT_NOCARRIERSET 10 -#define STAT_GOTCARRIERSET 11 -#define STAT_WRONGSSID 20 -#define STAT_BADCHANNEL 25 -#define STAT_BADBITRATES 30 -#define STAT_BADPRIVACY 35 -#define STAT_APFOUND 40 -#define STAT_APREJECTED 50 -#define STAT_AUTHENTICATING 60 -#define STAT_DEAUTHENTICATED 61 -#define STAT_AUTHTIMEOUT 62 -#define STAT_ASSOCIATING 70 -#define STAT_DEASSOCIATED 71 -#define STAT_ASSOCTIMEOUT 72 -#define STAT_NOTAIROAP 73 -#define STAT_ASSOCIATED 80 -#define STAT_LEAPING 90 -#define STAT_LEAPFAILED 91 -#define STAT_LEAPTIMEDOUT 92 -#define STAT_LEAPCOMPLETE 93 -} __packed; - -typedef struct StatsRid StatsRid; -struct StatsRid { - __le16 len; - __le16 spacer; - __le32 vals[100]; -} __packed; - -typedef struct APListRid APListRid; -struct APListRid { - __le16 len; - u8 ap[4][ETH_ALEN]; -} __packed; - -typedef struct CapabilityRid CapabilityRid; -struct CapabilityRid { - __le16 len; - char oui[3]; - char zero; - __le16 prodNum; - char manName[32]; - char prodName[16]; - char prodVer[8]; - char factoryAddr[ETH_ALEN]; - char aironetAddr[ETH_ALEN]; - __le16 radioType; - __le16 country; - char callid[ETH_ALEN]; - char supportedRates[8]; - char rxDiversity; - char txDiversity; - __le16 txPowerLevels[8]; - __le16 hardVer; - __le16 hardCap; - __le16 tempRange; - __le16 softVer; - __le16 softSubVer; - __le16 interfaceVer; - __le16 softCap; - __le16 bootBlockVer; - __le16 requiredHard; - __le16 extSoftCap; -} __packed; - -/* Only present on firmware >= 5.30.17 */ -typedef struct BSSListRidExtra BSSListRidExtra; -struct BSSListRidExtra { - __le16 unknown[4]; - u8 fixed[12]; /* WLAN management frame */ - u8 iep[624]; -} __packed; - -typedef struct BSSListRid BSSListRid; -struct BSSListRid { - __le16 len; - __le16 index; /* First is 0 and 0xffff means end of list */ -#define RADIO_FH 1 /* Frequency hopping radio type */ -#define RADIO_DS 2 /* Direct sequence radio type */ -#define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */ - __le16 radioType; - u8 bssid[ETH_ALEN]; /* Mac address of the BSS */ - u8 zero; - u8 ssidLen; - u8 ssid[32]; - __le16 dBm; -#define CAP_ESS cpu_to_le16(1<<0) -#define CAP_IBSS cpu_to_le16(1<<1) -#define CAP_PRIVACY cpu_to_le16(1<<4) -#define CAP_SHORTHDR cpu_to_le16(1<<5) - __le16 cap; - __le16 beaconInterval; - u8 rates[8]; /* Same as rates for config rid */ - struct { /* For frequency hopping only */ - __le16 dwell; - u8 hopSet; - u8 hopPattern; - u8 hopIndex; - u8 fill; - } fh; - __le16 dsChannel; - __le16 atimWindow; - - /* Only present on firmware >= 5.30.17 */ - BSSListRidExtra extra; -} __packed; - -typedef struct { - BSSListRid bss; - struct list_head list; -} BSSListElement; - -typedef struct tdsRssiEntry tdsRssiEntry; -struct tdsRssiEntry { - u8 rssipct; - u8 rssidBm; -} __packed; - -typedef struct tdsRssiRid tdsRssiRid; -struct tdsRssiRid { - u16 len; - tdsRssiEntry x[256]; -} __packed; - -typedef struct MICRid MICRid; -struct MICRid { - __le16 len; - __le16 state; - __le16 multicastValid; - u8 multicast[16]; - __le16 unicastValid; - u8 unicast[16]; -} __packed; - -typedef struct MICBuffer MICBuffer; -struct MICBuffer { - __be16 typelen; - - union { - u8 snap[8]; - struct { - u8 dsap; - u8 ssap; - u8 control; - u8 orgcode[3]; - u8 fieldtype[2]; - } llc; - } u; - __be32 mic; - __be32 seq; -} __packed; - -typedef struct { - u8 da[ETH_ALEN]; - u8 sa[ETH_ALEN]; -} etherHead; - -#define TXCTL_TXOK (1<<1) /* report if tx is ok */ -#define TXCTL_TXEX (1<<2) /* report if tx fails */ -#define TXCTL_802_3 (0<<3) /* 802.3 packet */ -#define TXCTL_802_11 (1<<3) /* 802.11 mac packet */ -#define TXCTL_ETHERNET (0<<4) /* payload has ethertype */ -#define TXCTL_LLC (1<<4) /* payload is llc */ -#define TXCTL_RELEASE (0<<5) /* release after completion */ -#define TXCTL_NORELEASE (1<<5) /* on completion returns to host */ - -#define BUSY_FID 0x10000 - -#ifdef CISCO_EXT -#define AIROMAGIC 0xa55a -/* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */ -#ifdef SIOCIWFIRSTPRIV -#ifdef SIOCDEVPRIVATE -#define AIROOLDIOCTL SIOCDEVPRIVATE -#define AIROOLDIDIFC AIROOLDIOCTL + 1 -#endif /* SIOCDEVPRIVATE */ -#else /* SIOCIWFIRSTPRIV */ -#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE -#endif /* SIOCIWFIRSTPRIV */ -/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably - * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root - * only and don't return the modified struct ifreq to the application which - * is usually a problem. - Jean II */ -#define AIROIOCTL SIOCIWFIRSTPRIV -#define AIROIDIFC AIROIOCTL + 1 - -/* Ioctl constants to be used in airo_ioctl.command */ - -#define AIROGCAP 0 // Capability rid -#define AIROGCFG 1 // USED A LOT -#define AIROGSLIST 2 // System ID list -#define AIROGVLIST 3 // List of specified AP's -#define AIROGDRVNAM 4 // NOTUSED -#define AIROGEHTENC 5 // NOTUSED -#define AIROGWEPKTMP 6 -#define AIROGWEPKNV 7 -#define AIROGSTAT 8 -#define AIROGSTATSC32 9 -#define AIROGSTATSD32 10 -#define AIROGMICRID 11 -#define AIROGMICSTATS 12 -#define AIROGFLAGS 13 -#define AIROGID 14 -#define AIRORRID 15 -#define AIRORSWVERSION 17 - -/* Leave gap of 40 commands after AIROGSTATSD32 for future */ - -#define AIROPCAP AIROGSTATSD32 + 40 -#define AIROPVLIST AIROPCAP + 1 -#define AIROPSLIST AIROPVLIST + 1 -#define AIROPCFG AIROPSLIST + 1 -#define AIROPSIDS AIROPCFG + 1 -#define AIROPAPLIST AIROPSIDS + 1 -#define AIROPMACON AIROPAPLIST + 1 /* Enable mac */ -#define AIROPMACOFF AIROPMACON + 1 /* Disable mac */ -#define AIROPSTCLR AIROPMACOFF + 1 -#define AIROPWEPKEY AIROPSTCLR + 1 -#define AIROPWEPKEYNV AIROPWEPKEY + 1 -#define AIROPLEAPPWD AIROPWEPKEYNV + 1 -#define AIROPLEAPUSR AIROPLEAPPWD + 1 - -/* Flash codes */ - -#define AIROFLSHRST AIROPWEPKEYNV + 40 -#define AIROFLSHGCHR AIROFLSHRST + 1 -#define AIROFLSHSTFL AIROFLSHGCHR + 1 -#define AIROFLSHPCHR AIROFLSHSTFL + 1 -#define AIROFLPUTBUF AIROFLSHPCHR + 1 -#define AIRORESTART AIROFLPUTBUF + 1 - -#define FLASHSIZE 32768 -#define AUXMEMSIZE (256 * 1024) - -typedef struct aironet_ioctl { - unsigned short command; // What to do - unsigned short len; // Len of data - unsigned short ridnum; // rid number - unsigned char __user *data; // d-data -} aironet_ioctl; - -static const char swversion[] = "2.1"; -#endif /* CISCO_EXT */ - -#define NUM_MODULES 2 -#define MIC_MSGLEN_MAX 2400 -#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX -#define AIRO_DEF_MTU 2312 - -typedef struct { - u32 size; // size - u8 enabled; // MIC enabled or not - u32 rxSuccess; // successful packets received - u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison - u32 rxNotMICed; // pkts dropped due to not being MIC'd - u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed - u32 rxWrongSequence; // pkts dropped due to sequence number violation - u32 reserve[32]; -} mic_statistics; - -typedef struct { - u32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2]; - u64 accum; // accumulated mic, reduced to u32 in final() - int position; // current position (byte offset) in message - union { - u8 d8[4]; - __be32 d32; - } part; // saves partial message word across update() calls -} emmh32_context; - -typedef struct { - emmh32_context seed; // Context - the seed - u32 rx; // Received sequence number - u32 tx; // Tx sequence number - u32 window; // Start of window - u8 valid; // Flag to say if context is valid or not - u8 key[16]; -} miccntx; - -typedef struct { - miccntx mCtx; // Multicast context - miccntx uCtx; // Unicast context -} mic_module; - -typedef struct { - unsigned int rid: 16; - unsigned int len: 15; - unsigned int valid: 1; - dma_addr_t host_addr; -} Rid; - -typedef struct { - unsigned int offset: 15; - unsigned int eoc: 1; - unsigned int len: 15; - unsigned int valid: 1; - dma_addr_t host_addr; -} TxFid; - -struct rx_hdr { - __le16 status, len; - u8 rssi[2]; - u8 rate; - u8 freq; - __le16 tmp[4]; -} __packed; - -typedef struct { - unsigned int ctl: 15; - unsigned int rdy: 1; - unsigned int len: 15; - unsigned int valid: 1; - dma_addr_t host_addr; -} RxFid; - -/* - * Host receive descriptor - */ -typedef struct { - unsigned char __iomem *card_ram_off; /* offset into card memory of the - desc */ - RxFid rx_desc; /* card receive descriptor */ - char *virtual_host_addr; /* virtual address of host receive - buffer */ - int pending; -} HostRxDesc; - -/* - * Host transmit descriptor - */ -typedef struct { - unsigned char __iomem *card_ram_off; /* offset into card memory of the - desc */ - TxFid tx_desc; /* card transmit descriptor */ - char *virtual_host_addr; /* virtual address of host receive - buffer */ - int pending; -} HostTxDesc; - -/* - * Host RID descriptor - */ -typedef struct { - unsigned char __iomem *card_ram_off; /* offset into card memory of the - descriptor */ - Rid rid_desc; /* card RID descriptor */ - char *virtual_host_addr; /* virtual address of host receive - buffer */ -} HostRidDesc; - -typedef struct { - u16 sw0; - u16 sw1; - u16 status; - u16 len; -#define HOST_SET (1 << 0) -#define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */ -#define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */ -#define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */ -#define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */ -#define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */ -#define HOST_CLR_AID (1 << 7) /* clear AID failure */ -#define HOST_RTS (1 << 9) /* Force RTS use */ -#define HOST_SHORT (1 << 10) /* Do short preamble */ - u16 ctl; - u16 aid; - u16 retries; - u16 fill; -} TxCtlHdr; - -typedef struct { - u16 ctl; - u16 duration; - char addr1[6]; - char addr2[6]; - char addr3[6]; - u16 seq; - char addr4[6]; -} WifiHdr; - - -typedef struct { - TxCtlHdr ctlhdr; - u16 fill1; - u16 fill2; - WifiHdr wifihdr; - u16 gaplen; - u16 status; -} WifiCtlHdr; - -static WifiCtlHdr wifictlhdr8023 = { - .ctlhdr = { - .ctl = HOST_DONT_RLSE, - } -}; - -// A few details needed for WEP (Wireless Equivalent Privacy) -#define MAX_KEY_SIZE 13 // 128 (?) bits -#define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP -typedef struct wep_key_t { - u16 len; - u8 key[16]; /* 40-bit and 104-bit keys */ -} wep_key_t; - -/* List of Wireless Handlers (new API) */ -static const struct iw_handler_def airo_handler_def; - -static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)"; - -struct airo_info; - -static int get_dec_u16( char *buffer, int *start, int limit ); -static void OUT4500( struct airo_info *, u16 register, u16 value ); -static unsigned short IN4500( struct airo_info *, u16 register ); -static u16 setup_card(struct airo_info*, u8 *mac, int lock); -static int enable_MAC(struct airo_info *ai, int lock); -static void disable_MAC(struct airo_info *ai, int lock); -static void enable_interrupts(struct airo_info*); -static void disable_interrupts(struct airo_info*); -static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp); -static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap); -static int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen, - int whichbap); -static int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen, - int whichbap); -static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen, - int whichbap); -static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd); -static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock); -static int PC4500_writerid(struct airo_info*, u16 rid, const void - *pBuf, int len, int lock); -static int do_writerid( struct airo_info*, u16 rid, const void *rid_data, - int len, int dummy ); -static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw); -static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket); -static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket); - -static int mpi_send_packet (struct net_device *dev); -static void mpi_unmap_card(struct pci_dev *pci); -static void mpi_receive_802_3(struct airo_info *ai); -static void mpi_receive_802_11(struct airo_info *ai); -static int waitbusy (struct airo_info *ai); - -static irqreturn_t airo_interrupt( int irq, void* dev_id); -static int airo_thread(void *data); -static void timer_func( struct net_device *dev ); -static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev); -static void airo_read_wireless_stats (struct airo_info *local); -#ifdef CISCO_EXT -static int readrids(struct net_device *dev, aironet_ioctl *comp); -static int writerids(struct net_device *dev, aironet_ioctl *comp); -static int flashcard(struct net_device *dev, aironet_ioctl *comp); -#endif /* CISCO_EXT */ -static void micinit(struct airo_info *ai); -static int micsetup(struct airo_info *ai); -static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len); -static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen); - -static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi); -static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm); - -static void airo_networks_free(struct airo_info *ai); - -struct airo_info { - struct net_device *dev; - struct list_head dev_list; - /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we - use the high bit to mark whether it is in use. */ -#define MAX_FIDS 6 -#define MPI_MAX_FIDS 1 - u32 fids[MAX_FIDS]; - ConfigRid config; - char keyindex; // Used with auto wep - char defindex; // Used with auto wep - struct proc_dir_entry *proc_entry; - spinlock_t aux_lock; -#define FLAG_RADIO_OFF 0 /* User disabling of MAC */ -#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */ -#define FLAG_RADIO_MASK 0x03 -#define FLAG_ENABLED 2 -#define FLAG_ADHOC 3 /* Needed by MIC */ -#define FLAG_MIC_CAPABLE 4 -#define FLAG_UPDATE_MULTI 5 -#define FLAG_UPDATE_UNI 6 -#define FLAG_802_11 7 -#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */ -#define FLAG_PENDING_XMIT 9 -#define FLAG_PENDING_XMIT11 10 -#define FLAG_MPI 11 -#define FLAG_REGISTERED 12 -#define FLAG_COMMIT 13 -#define FLAG_RESET 14 -#define FLAG_FLASHING 15 -#define FLAG_WPA_CAPABLE 16 - unsigned long flags; -#define JOB_DIE 0 -#define JOB_XMIT 1 -#define JOB_XMIT11 2 -#define JOB_STATS 3 -#define JOB_PROMISC 4 -#define JOB_MIC 5 -#define JOB_EVENT 6 -#define JOB_AUTOWEP 7 -#define JOB_WSTATS 8 -#define JOB_SCAN_RESULTS 9 - unsigned long jobs; - int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen, - int whichbap); - unsigned short *flash; - tdsRssiEntry *rssi; - struct task_struct *list_bss_task; - struct task_struct *airo_thread_task; - struct semaphore sem; - wait_queue_head_t thr_wait; - unsigned long expires; - struct { - struct sk_buff *skb; - int fid; - } xmit, xmit11; - struct net_device *wifidev; - struct iw_statistics wstats; // wireless stats - unsigned long scan_timeout; /* Time scan should be read */ - struct iw_spy_data spy_data; - struct iw_public_data wireless_data; - /* MIC stuff */ - struct crypto_cipher *tfm; - mic_module mod[2]; - mic_statistics micstats; - HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors - HostTxDesc txfids[MPI_MAX_FIDS]; - HostRidDesc config_desc; - unsigned long ridbus; // phys addr of config_desc - struct sk_buff_head txq;// tx queue used by mpi350 code - struct pci_dev *pci; - unsigned char __iomem *pcimem; - unsigned char __iomem *pciaux; - unsigned char *shared; - dma_addr_t shared_dma; - pm_message_t power; - SsidRid *SSID; - APListRid APList; -#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE - char proc_name[IFNAMSIZ]; - - int wep_capable; - int max_wep_idx; - int last_auth; - - /* WPA-related stuff */ - unsigned int bssListFirst; - unsigned int bssListNext; - unsigned int bssListRidLen; - - struct list_head network_list; - struct list_head network_free_list; - BSSListElement *networks; -}; - -static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen, - int whichbap) -{ - return ai->bap_read(ai, pu16Dst, bytelen, whichbap); -} - -static int setup_proc_entry( struct net_device *dev, - struct airo_info *apriv ); -static int takedown_proc_entry( struct net_device *dev, - struct airo_info *apriv ); - -static int cmdreset(struct airo_info *ai); -static int setflashmode (struct airo_info *ai); -static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime); -static int flashputbuf(struct airo_info *ai); -static int flashrestart(struct airo_info *ai,struct net_device *dev); - -#define airo_print(type, name, fmt, args...) \ - printk(type DRV_NAME "(%s): " fmt "\n", name, ##args) - -#define airo_print_info(name, fmt, args...) \ - airo_print(KERN_INFO, name, fmt, ##args) - -#define airo_print_dbg(name, fmt, args...) \ - airo_print(KERN_DEBUG, name, fmt, ##args) - -#define airo_print_warn(name, fmt, args...) \ - airo_print(KERN_WARNING, name, fmt, ##args) - -#define airo_print_err(name, fmt, args...) \ - airo_print(KERN_ERR, name, fmt, ##args) - -#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash) - -/*********************************************************************** - * MIC ROUTINES * - *********************************************************************** - */ - -static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq); -static void MoveWindow(miccntx *context, u32 micSeq); -static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, - struct crypto_cipher *tfm); -static void emmh32_init(emmh32_context *context); -static void emmh32_update(emmh32_context *context, u8 *pOctets, int len); -static void emmh32_final(emmh32_context *context, u8 digest[4]); -static int flashpchar(struct airo_info *ai,int byte,int dwelltime); - -static void age_mic_context(miccntx *cur, miccntx *old, u8 *key, int key_len, - struct crypto_cipher *tfm) -{ - /* If the current MIC context is valid and its key is the same as - * the MIC register, there's nothing to do. - */ - if (cur->valid && (memcmp(cur->key, key, key_len) == 0)) - return; - - /* Age current mic Context */ - memcpy(old, cur, sizeof(*cur)); - - /* Initialize new context */ - memcpy(cur->key, key, key_len); - cur->window = 33; /* Window always points to the middle */ - cur->rx = 0; /* Rx Sequence numbers */ - cur->tx = 0; /* Tx sequence numbers */ - cur->valid = 1; /* Key is now valid */ - - /* Give key to mic seed */ - emmh32_setseed(&cur->seed, key, key_len, tfm); -} - -/* micinit - Initialize mic seed */ - -static void micinit(struct airo_info *ai) -{ - MICRid mic_rid; - - clear_bit(JOB_MIC, &ai->jobs); - PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0); - up(&ai->sem); - - ai->micstats.enabled = (le16_to_cpu(mic_rid.state) & 0x00FF) ? 1 : 0; - if (!ai->micstats.enabled) { - /* So next time we have a valid key and mic is enabled, we will - * update the sequence number if the key is the same as before. - */ - ai->mod[0].uCtx.valid = 0; - ai->mod[0].mCtx.valid = 0; - return; - } - - if (mic_rid.multicastValid) { - age_mic_context(&ai->mod[0].mCtx, &ai->mod[1].mCtx, - mic_rid.multicast, sizeof(mic_rid.multicast), - ai->tfm); - } - - if (mic_rid.unicastValid) { - age_mic_context(&ai->mod[0].uCtx, &ai->mod[1].uCtx, - mic_rid.unicast, sizeof(mic_rid.unicast), - ai->tfm); - } -} - -/* micsetup - Get ready for business */ - -static int micsetup(struct airo_info *ai) { - int i; - - if (ai->tfm == NULL) - ai->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); - - if (IS_ERR(ai->tfm)) { - airo_print_err(ai->dev->name, "failed to load transform for AES"); - ai->tfm = NULL; - return ERROR; - } - - for (i=0; i < NUM_MODULES; i++) { - memset(&ai->mod[i].mCtx,0,sizeof(miccntx)); - memset(&ai->mod[i].uCtx,0,sizeof(miccntx)); - } - return SUCCESS; -} - -static const u8 micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02}; - -/*=========================================================================== - * Description: Mic a packet - * - * Inputs: etherHead * pointer to an 802.3 frame - * - * Returns: BOOLEAN if successful, otherwise false. - * PacketTxLen will be updated with the mic'd packets size. - * - * Caveats: It is assumed that the frame buffer will already - * be big enough to hold the largets mic message possible. - * (No memory allocation is done here). - * - * Author: sbraneky (10/15/01) - * Merciless hacks by rwilcher (1/14/02) - */ - -static int encapsulate(struct airo_info *ai ,etherHead *frame, MICBuffer *mic, int payLen) -{ - miccntx *context; - - // Determine correct context - // If not adhoc, always use unicast key - - if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1)) - context = &ai->mod[0].mCtx; - else - context = &ai->mod[0].uCtx; - - if (!context->valid) - return ERROR; - - mic->typelen = htons(payLen + 16); //Length of Mic'd packet - - memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap - - // Add Tx sequence - mic->seq = htonl(context->tx); - context->tx += 2; - - emmh32_init(&context->seed); // Mic the packet - emmh32_update(&context->seed,frame->da,ETH_ALEN * 2); // DA,SA - emmh32_update(&context->seed,(u8*)&mic->typelen,10); // Type/Length and Snap - emmh32_update(&context->seed,(u8*)&mic->seq,sizeof(mic->seq)); //SEQ - emmh32_update(&context->seed,(u8*)(frame + 1),payLen); //payload - emmh32_final(&context->seed, (u8*)&mic->mic); - - /* New Type/length ?????????? */ - mic->typelen = 0; //Let NIC know it could be an oversized packet - return SUCCESS; -} - -typedef enum { - NONE, - NOMIC, - NOMICPLUMMED, - SEQUENCE, - INCORRECTMIC, -} mic_error; - -/*=========================================================================== - * Description: Decapsulates a MIC'd packet and returns the 802.3 packet - * (removes the MIC stuff) if packet is a valid packet. - * - * Inputs: etherHead pointer to the 802.3 packet - * - * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE - * - * Author: sbraneky (10/15/01) - * Merciless hacks by rwilcher (1/14/02) - *--------------------------------------------------------------------------- - */ - -static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen) -{ - int i; - u32 micSEQ; - miccntx *context; - u8 digest[4]; - mic_error micError = NONE; - - // Check if the packet is a Mic'd packet - - if (!ai->micstats.enabled) { - //No Mic set or Mic OFF but we received a MIC'd packet. - if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) { - ai->micstats.rxMICPlummed++; - return ERROR; - } - return SUCCESS; - } - - if (ntohs(mic->typelen) == 0x888E) - return SUCCESS; - - if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) { - // Mic enabled but packet isn't Mic'd - ai->micstats.rxMICPlummed++; - return ERROR; - } - - micSEQ = ntohl(mic->seq); //store SEQ as CPU order - - //At this point we a have a mic'd packet and mic is enabled - //Now do the mic error checking. - - //Receive seq must be odd - if ( (micSEQ & 1) == 0 ) { - ai->micstats.rxWrongSequence++; - return ERROR; - } - - for (i = 0; i < NUM_MODULES; i++) { - int mcast = eth->da[0] & 1; - //Determine proper context - context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx; - - //Make sure context is valid - if (!context->valid) { - if (i == 0) - micError = NOMICPLUMMED; - continue; - } - //DeMic it - - if (!mic->typelen) - mic->typelen = htons(payLen + sizeof(MICBuffer) - 2); - - emmh32_init(&context->seed); - emmh32_update(&context->seed, eth->da, ETH_ALEN*2); - emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap)); - emmh32_update(&context->seed, (u8 *)&mic->seq,sizeof(mic->seq)); - emmh32_update(&context->seed, (u8 *)(eth + 1),payLen); - //Calculate MIC - emmh32_final(&context->seed, digest); - - if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match - //Invalid Mic - if (i == 0) - micError = INCORRECTMIC; - continue; - } - - //Check Sequence number if mics pass - if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) { - ai->micstats.rxSuccess++; - return SUCCESS; - } - if (i == 0) - micError = SEQUENCE; - } - - // Update statistics - switch (micError) { - case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break; - case SEQUENCE: ai->micstats.rxWrongSequence++; break; - case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break; - case NONE: break; - case NOMIC: break; - } - return ERROR; -} - -/*=========================================================================== - * Description: Checks the Rx Seq number to make sure it is valid - * and hasn't already been received - * - * Inputs: miccntx - mic context to check seq against - * micSeq - the Mic seq number - * - * Returns: TRUE if valid otherwise FALSE. - * - * Author: sbraneky (10/15/01) - * Merciless hacks by rwilcher (1/14/02) - *--------------------------------------------------------------------------- - */ - -static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq) -{ - u32 seq,index; - - //Allow for the ap being rebooted - if it is then use the next - //sequence number of the current sequence number - might go backwards - - if (mcast) { - if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) { - clear_bit (FLAG_UPDATE_MULTI, &ai->flags); - context->window = (micSeq > 33) ? micSeq : 33; - context->rx = 0; // Reset rx - } - } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) { - clear_bit (FLAG_UPDATE_UNI, &ai->flags); - context->window = (micSeq > 33) ? micSeq : 33; // Move window - context->rx = 0; // Reset rx - } - - //Make sequence number relative to START of window - seq = micSeq - (context->window - 33); - - //Too old of a SEQ number to check. - if ((s32)seq < 0) - return ERROR; - - if ( seq > 64 ) { - //Window is infinite forward - MoveWindow(context,micSeq); - return SUCCESS; - } - - // We are in the window. Now check the context rx bit to see if it was already sent - seq >>= 1; //divide by 2 because we only have odd numbers - index = 1 << seq; //Get an index number - - if (!(context->rx & index)) { - //micSEQ falls inside the window. - //Add seqence number to the list of received numbers. - context->rx |= index; - - MoveWindow(context,micSeq); - - return SUCCESS; - } - return ERROR; -} - -static void MoveWindow(miccntx *context, u32 micSeq) -{ - u32 shift; - - //Move window if seq greater than the middle of the window - if (micSeq > context->window) { - shift = (micSeq - context->window) >> 1; - - //Shift out old - if (shift < 32) - context->rx >>= shift; - else - context->rx = 0; - - context->window = micSeq; //Move window - } -} - -/*==============================================*/ -/*========== EMMH ROUTINES ====================*/ -/*==============================================*/ - -/* mic accumulate */ -#define MIC_ACCUM(val) \ - context->accum += (u64)(val) * context->coeff[coeff_position++]; - -static unsigned char aes_counter[16]; - -/* expand the key to fill the MMH coefficient array */ -static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, - struct crypto_cipher *tfm) -{ - /* take the keying material, expand if necessary, truncate at 16-bytes */ - /* run through AES counter mode to generate context->coeff[] */ - - int i,j; - u32 counter; - u8 *cipher, plain[16]; - - crypto_cipher_setkey(tfm, pkey, 16); - counter = 0; - for (i = 0; i < ARRAY_SIZE(context->coeff); ) { - aes_counter[15] = (u8)(counter >> 0); - aes_counter[14] = (u8)(counter >> 8); - aes_counter[13] = (u8)(counter >> 16); - aes_counter[12] = (u8)(counter >> 24); - counter++; - memcpy (plain, aes_counter, 16); - crypto_cipher_encrypt_one(tfm, plain, plain); - cipher = plain; - for (j = 0; (j < 16) && (i < ARRAY_SIZE(context->coeff)); ) { - context->coeff[i++] = ntohl(*(__be32 *)&cipher[j]); - j += 4; - } - } -} - -/* prepare for calculation of a new mic */ -static void emmh32_init(emmh32_context *context) -{ - /* prepare for new mic calculation */ - context->accum = 0; - context->position = 0; -} - -/* add some bytes to the mic calculation */ -static void emmh32_update(emmh32_context *context, u8 *pOctets, int len) -{ - int coeff_position, byte_position; - - if (len == 0) return; - - coeff_position = context->position >> 2; - - /* deal with partial 32-bit word left over from last update */ - byte_position = context->position & 3; - if (byte_position) { - /* have a partial word in part to deal with */ - do { - if (len == 0) return; - context->part.d8[byte_position++] = *pOctets++; - context->position++; - len--; - } while (byte_position < 4); - MIC_ACCUM(ntohl(context->part.d32)); - } - - /* deal with full 32-bit words */ - while (len >= 4) { - MIC_ACCUM(ntohl(*(__be32 *)pOctets)); - context->position += 4; - pOctets += 4; - len -= 4; - } - - /* deal with partial 32-bit word that will be left over from this update */ - byte_position = 0; - while (len > 0) { - context->part.d8[byte_position++] = *pOctets++; - context->position++; - len--; - } -} - -/* mask used to zero empty bytes for final partial word */ -static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L }; - -/* calculate the mic */ -static void emmh32_final(emmh32_context *context, u8 digest[4]) -{ - int coeff_position, byte_position; - u32 val; - - u64 sum, utmp; - s64 stmp; - - coeff_position = context->position >> 2; - - /* deal with partial 32-bit word left over from last update */ - byte_position = context->position & 3; - if (byte_position) { - /* have a partial word in part to deal with */ - val = ntohl(context->part.d32); - MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */ - } - - /* reduce the accumulated u64 to a 32-bit MIC */ - sum = context->accum; - stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15); - utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15); - sum = utmp & 0xffffffffLL; - if (utmp > 0x10000000fLL) - sum -= 15; - - val = (u32)sum; - digest[0] = (val>>24) & 0xFF; - digest[1] = (val>>16) & 0xFF; - digest[2] = (val>>8) & 0xFF; - digest[3] = val & 0xFF; -} - -static int readBSSListRid(struct airo_info *ai, int first, - BSSListRid *list) -{ - Cmd cmd; - Resp rsp; - - if (first == 1) { - if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN; - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd=CMD_LISTBSS; - if (down_interruptible(&ai->sem)) - return -ERESTARTSYS; - ai->list_bss_task = current; - issuecommand(ai, &cmd, &rsp); - up(&ai->sem); - /* Let the command take effect */ - schedule_timeout_uninterruptible(3 * HZ); - ai->list_bss_task = NULL; - } - return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext, - list, ai->bssListRidLen, 1); -} - -static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock) -{ - return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM, - wkr, sizeof(*wkr), lock); -} - -static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock) -{ - int rc; - rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock); - if (rc!=SUCCESS) - airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc); - if (perm) { - rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock); - if (rc!=SUCCESS) - airo_print_err(ai->dev->name, "WEP_PERM set %x", rc); - } - return rc; -} - -static int readSsidRid(struct airo_info*ai, SsidRid *ssidr) -{ - return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1); -} - -static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock) -{ - return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock); -} - -static int readConfigRid(struct airo_info *ai, int lock) -{ - int rc; - ConfigRid cfg; - - if (ai->config.len) - return SUCCESS; - - rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock); - if (rc != SUCCESS) - return rc; - - ai->config = cfg; - return SUCCESS; -} - -static inline void checkThrottle(struct airo_info *ai) -{ - int i; -/* Old hardware had a limit on encryption speed */ - if (ai->config.authType != AUTH_OPEN && maxencrypt) { - for(i=0; i<8; i++) { - if (ai->config.rates[i] > maxencrypt) { - ai->config.rates[i] = 0; - } - } - } -} - -static int writeConfigRid(struct airo_info *ai, int lock) -{ - ConfigRid cfgr; - - if (!test_bit (FLAG_COMMIT, &ai->flags)) - return SUCCESS; - - clear_bit (FLAG_COMMIT, &ai->flags); - clear_bit (FLAG_RESET, &ai->flags); - checkThrottle(ai); - cfgr = ai->config; - - if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS) - set_bit(FLAG_ADHOC, &ai->flags); - else - clear_bit(FLAG_ADHOC, &ai->flags); - - return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock); -} - -static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock) -{ - return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock); -} - -static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock) -{ - return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock); -} - -static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock) -{ - return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock); -} - -static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock) -{ - return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock); -} - -static void try_auto_wep(struct airo_info *ai) -{ - if (auto_wep && !test_bit(FLAG_RADIO_DOWN, &ai->flags)) { - ai->expires = RUN_AT(3*HZ); - wake_up_interruptible(&ai->thr_wait); - } -} - -static int airo_open(struct net_device *dev) { - struct airo_info *ai = dev->ml_priv; - int rc = 0; - - if (test_bit(FLAG_FLASHING, &ai->flags)) - return -EIO; - - /* Make sure the card is configured. - * Wireless Extensions may postpone config changes until the card - * is open (to pipeline changes and speed-up card setup). If - * those changes are not yet committed, do it now - Jean II */ - if (test_bit(FLAG_COMMIT, &ai->flags)) { - disable_MAC(ai, 1); - writeConfigRid(ai, 1); - } - - if (ai->wifidev != dev) { - clear_bit(JOB_DIE, &ai->jobs); - ai->airo_thread_task = kthread_run(airo_thread, dev, "%s", - dev->name); - if (IS_ERR(ai->airo_thread_task)) - return (int)PTR_ERR(ai->airo_thread_task); - - rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED, - dev->name, dev); - if (rc) { - airo_print_err(dev->name, - "register interrupt %d failed, rc %d", - dev->irq, rc); - set_bit(JOB_DIE, &ai->jobs); - kthread_stop(ai->airo_thread_task); - return rc; - } - - /* Power on the MAC controller (which may have been disabled) */ - clear_bit(FLAG_RADIO_DOWN, &ai->flags); - enable_interrupts(ai); - - try_auto_wep(ai); - } - enable_MAC(ai, 1); - - netif_start_queue(dev); - return 0; -} - -static netdev_tx_t mpi_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - int npacks, pending; - unsigned long flags; - struct airo_info *ai = dev->ml_priv; - - if (!skb) { - airo_print_err(dev->name, "%s: skb == NULL!",__func__); - return NETDEV_TX_OK; - } - npacks = skb_queue_len (&ai->txq); - - if (npacks >= MAXTXQ - 1) { - netif_stop_queue (dev); - if (npacks > MAXTXQ) { - dev->stats.tx_fifo_errors++; - return NETDEV_TX_BUSY; - } - skb_queue_tail (&ai->txq, skb); - return NETDEV_TX_OK; - } - - spin_lock_irqsave(&ai->aux_lock, flags); - skb_queue_tail (&ai->txq, skb); - pending = test_bit(FLAG_PENDING_XMIT, &ai->flags); - spin_unlock_irqrestore(&ai->aux_lock,flags); - netif_wake_queue (dev); - - if (pending == 0) { - set_bit(FLAG_PENDING_XMIT, &ai->flags); - mpi_send_packet (dev); - } - return NETDEV_TX_OK; -} - -/* - * @mpi_send_packet - * - * Attempt to transmit a packet. Can be called from interrupt - * or transmit . return number of packets we tried to send - */ - -static int mpi_send_packet (struct net_device *dev) -{ - struct sk_buff *skb; - unsigned char *buffer; - s16 len; - __le16 *payloadLen; - struct airo_info *ai = dev->ml_priv; - u8 *sendbuf; - - /* get a packet to send */ - - if ((skb = skb_dequeue(&ai->txq)) == NULL) { - airo_print_err(dev->name, - "%s: Dequeue'd zero in send_packet()", - __func__); - return 0; - } - - /* check min length*/ - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - buffer = skb->data; - - ai->txfids[0].tx_desc.offset = 0; - ai->txfids[0].tx_desc.valid = 1; - ai->txfids[0].tx_desc.eoc = 1; - ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr); - -/* - * Magic, the cards firmware needs a length count (2 bytes) in the host buffer - * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen - * is immediately after it. ------------------------------------------------ - * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA| - * ------------------------------------------------ - */ - - memcpy(ai->txfids[0].virtual_host_addr, - (char *)&wifictlhdr8023, sizeof(wifictlhdr8023)); - - payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr + - sizeof(wifictlhdr8023)); - sendbuf = ai->txfids[0].virtual_host_addr + - sizeof(wifictlhdr8023) + 2 ; - - /* - * Firmware automatically puts 802 header on so - * we don't need to account for it in the length - */ - if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled && - (ntohs(((__be16 *)buffer)[6]) != 0x888E)) { - MICBuffer pMic; - - if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS) - return ERROR; - - *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic)); - ai->txfids[0].tx_desc.len += sizeof(pMic); - /* copy data into airo dma buffer */ - memcpy (sendbuf, buffer, sizeof(etherHead)); - buffer += sizeof(etherHead); - sendbuf += sizeof(etherHead); - memcpy (sendbuf, &pMic, sizeof(pMic)); - sendbuf += sizeof(pMic); - memcpy (sendbuf, buffer, len - sizeof(etherHead)); - } else { - *payloadLen = cpu_to_le16(len - sizeof(etherHead)); - - dev->trans_start = jiffies; - - /* copy data into airo dma buffer */ - memcpy(sendbuf, buffer, len); - } - - memcpy_toio(ai->txfids[0].card_ram_off, - &ai->txfids[0].tx_desc, sizeof(TxFid)); - - OUT4500(ai, EVACK, 8); - - dev_kfree_skb_any(skb); - return 1; -} - -static void get_tx_error(struct airo_info *ai, s32 fid) -{ - __le16 status; - - if (fid < 0) - status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status; - else { - if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS) - return; - bap_read(ai, &status, 2, BAP0); - } - if (le16_to_cpu(status) & 2) /* Too many retries */ - ai->dev->stats.tx_aborted_errors++; - if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */ - ai->dev->stats.tx_heartbeat_errors++; - if (le16_to_cpu(status) & 8) /* Aid fail */ - { } - if (le16_to_cpu(status) & 0x10) /* MAC disabled */ - ai->dev->stats.tx_carrier_errors++; - if (le16_to_cpu(status) & 0x20) /* Association lost */ - { } - /* We produce a TXDROP event only for retry or lifetime - * exceeded, because that's the only status that really mean - * that this particular node went away. - * Other errors means that *we* screwed up. - Jean II */ - if ((le16_to_cpu(status) & 2) || - (le16_to_cpu(status) & 4)) { - union iwreq_data wrqu; - char junk[0x18]; - - /* Faster to skip over useless data than to do - * another bap_setup(). We are at offset 0x6 and - * need to go to 0x18 and read 6 bytes - Jean II */ - bap_read(ai, (__le16 *) junk, 0x18, BAP0); - - /* Copy 802.11 dest address. - * We use the 802.11 header because the frame may - * not be 802.3 or may be mangled... - * In Ad-Hoc mode, it will be the node address. - * In managed mode, it will be most likely the AP addr - * User space will figure out how to convert it to - * whatever it needs (IP address or else). - * - Jean II */ - memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN); - wrqu.addr.sa_family = ARPHRD_ETHER; - - /* Send event to user space */ - wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL); - } -} - -static void airo_end_xmit(struct net_device *dev) { - u16 status; - int i; - struct airo_info *priv = dev->ml_priv; - struct sk_buff *skb = priv->xmit.skb; - int fid = priv->xmit.fid; - u32 *fids = priv->fids; - - clear_bit(JOB_XMIT, &priv->jobs); - clear_bit(FLAG_PENDING_XMIT, &priv->flags); - status = transmit_802_3_packet (priv, fids[fid], skb->data); - up(&priv->sem); - - i = 0; - if ( status == SUCCESS ) { - dev->trans_start = jiffies; - for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++); - } else { - priv->fids[fid] &= 0xffff; - dev->stats.tx_window_errors++; - } - if (i < MAX_FIDS / 2) - netif_wake_queue(dev); - dev_kfree_skb(skb); -} - -static netdev_tx_t airo_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - s16 len; - int i, j; - struct airo_info *priv = dev->ml_priv; - u32 *fids = priv->fids; - - if ( skb == NULL ) { - airo_print_err(dev->name, "%s: skb == NULL!", __func__); - return NETDEV_TX_OK; - } - - /* Find a vacant FID */ - for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ ); - for( j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++ ); - - if ( j >= MAX_FIDS / 2 ) { - netif_stop_queue(dev); - - if (i == MAX_FIDS / 2) { - dev->stats.tx_fifo_errors++; - return NETDEV_TX_BUSY; - } - } - /* check min length*/ - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - /* Mark fid as used & save length for later */ - fids[i] |= (len << 16); - priv->xmit.skb = skb; - priv->xmit.fid = i; - if (down_trylock(&priv->sem) != 0) { - set_bit(FLAG_PENDING_XMIT, &priv->flags); - netif_stop_queue(dev); - set_bit(JOB_XMIT, &priv->jobs); - wake_up_interruptible(&priv->thr_wait); - } else - airo_end_xmit(dev); - return NETDEV_TX_OK; -} - -static void airo_end_xmit11(struct net_device *dev) { - u16 status; - int i; - struct airo_info *priv = dev->ml_priv; - struct sk_buff *skb = priv->xmit11.skb; - int fid = priv->xmit11.fid; - u32 *fids = priv->fids; - - clear_bit(JOB_XMIT11, &priv->jobs); - clear_bit(FLAG_PENDING_XMIT11, &priv->flags); - status = transmit_802_11_packet (priv, fids[fid], skb->data); - up(&priv->sem); - - i = MAX_FIDS / 2; - if ( status == SUCCESS ) { - dev->trans_start = jiffies; - for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++); - } else { - priv->fids[fid] &= 0xffff; - dev->stats.tx_window_errors++; - } - if (i < MAX_FIDS) - netif_wake_queue(dev); - dev_kfree_skb(skb); -} - -static netdev_tx_t airo_start_xmit11(struct sk_buff *skb, - struct net_device *dev) -{ - s16 len; - int i, j; - struct airo_info *priv = dev->ml_priv; - u32 *fids = priv->fids; - - if (test_bit(FLAG_MPI, &priv->flags)) { - /* Not implemented yet for MPI350 */ - netif_stop_queue(dev); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } - - if ( skb == NULL ) { - airo_print_err(dev->name, "%s: skb == NULL!", __func__); - return NETDEV_TX_OK; - } - - /* Find a vacant FID */ - for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ ); - for( j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++ ); - - if ( j >= MAX_FIDS ) { - netif_stop_queue(dev); - - if (i == MAX_FIDS) { - dev->stats.tx_fifo_errors++; - return NETDEV_TX_BUSY; - } - } - /* check min length*/ - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - /* Mark fid as used & save length for later */ - fids[i] |= (len << 16); - priv->xmit11.skb = skb; - priv->xmit11.fid = i; - if (down_trylock(&priv->sem) != 0) { - set_bit(FLAG_PENDING_XMIT11, &priv->flags); - netif_stop_queue(dev); - set_bit(JOB_XMIT11, &priv->jobs); - wake_up_interruptible(&priv->thr_wait); - } else - airo_end_xmit11(dev); - return NETDEV_TX_OK; -} - -static void airo_read_stats(struct net_device *dev) -{ - struct airo_info *ai = dev->ml_priv; - StatsRid stats_rid; - __le32 *vals = stats_rid.vals; - - clear_bit(JOB_STATS, &ai->jobs); - if (ai->power.event) { - up(&ai->sem); - return; - } - readStatsRid(ai, &stats_rid, RID_STATS, 0); - up(&ai->sem); - - dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) + - le32_to_cpu(vals[45]); - dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) + - le32_to_cpu(vals[41]); - dev->stats.rx_bytes = le32_to_cpu(vals[92]); - dev->stats.tx_bytes = le32_to_cpu(vals[91]); - dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) + - le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]); - dev->stats.tx_errors = le32_to_cpu(vals[42]) + - dev->stats.tx_fifo_errors; - dev->stats.multicast = le32_to_cpu(vals[43]); - dev->stats.collisions = le32_to_cpu(vals[89]); - - /* detailed rx_errors: */ - dev->stats.rx_length_errors = le32_to_cpu(vals[3]); - dev->stats.rx_crc_errors = le32_to_cpu(vals[4]); - dev->stats.rx_frame_errors = le32_to_cpu(vals[2]); - dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]); -} - -static struct net_device_stats *airo_get_stats(struct net_device *dev) -{ - struct airo_info *local = dev->ml_priv; - - if (!test_bit(JOB_STATS, &local->jobs)) { - /* Get stats out of the card if available */ - if (down_trylock(&local->sem) != 0) { - set_bit(JOB_STATS, &local->jobs); - wake_up_interruptible(&local->thr_wait); - } else - airo_read_stats(dev); - } - - return &dev->stats; -} - -static void airo_set_promisc(struct airo_info *ai) { - Cmd cmd; - Resp rsp; - - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd=CMD_SETMODE; - clear_bit(JOB_PROMISC, &ai->jobs); - cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC; - issuecommand(ai, &cmd, &rsp); - up(&ai->sem); -} - -static void airo_set_multicast_list(struct net_device *dev) { - struct airo_info *ai = dev->ml_priv; - - if ((dev->flags ^ ai->flags) & IFF_PROMISC) { - change_bit(FLAG_PROMISC, &ai->flags); - if (down_trylock(&ai->sem) != 0) { - set_bit(JOB_PROMISC, &ai->jobs); - wake_up_interruptible(&ai->thr_wait); - } else - airo_set_promisc(ai); - } - - if ((dev->flags&IFF_ALLMULTI) || !netdev_mc_empty(dev)) { - /* Turn on multicast. (Should be already setup...) */ - } -} - -static int airo_set_mac_address(struct net_device *dev, void *p) -{ - struct airo_info *ai = dev->ml_priv; - struct sockaddr *addr = p; - - readConfigRid(ai, 1); - memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len); - set_bit (FLAG_COMMIT, &ai->flags); - disable_MAC(ai, 1); - writeConfigRid (ai, 1); - enable_MAC(ai, 1); - memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len); - if (ai->wifidev) - memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len); - return 0; -} - -static int airo_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > 2400)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - -static LIST_HEAD(airo_devices); - -static void add_airo_dev(struct airo_info *ai) -{ - /* Upper layers already keep track of PCI devices, - * so we only need to remember our non-PCI cards. */ - if (!ai->pci) - list_add_tail(&ai->dev_list, &airo_devices); -} - -static void del_airo_dev(struct airo_info *ai) -{ - if (!ai->pci) - list_del(&ai->dev_list); -} - -static int airo_close(struct net_device *dev) { - struct airo_info *ai = dev->ml_priv; - - netif_stop_queue(dev); - - if (ai->wifidev != dev) { -#ifdef POWER_ON_DOWN - /* Shut power to the card. The idea is that the user can save - * power when he doesn't need the card with "ifconfig down". - * That's the method that is most friendly towards the network - * stack (i.e. the network stack won't try to broadcast - * anything on the interface and routes are gone. Jean II */ - set_bit(FLAG_RADIO_DOWN, &ai->flags); - disable_MAC(ai, 1); -#endif - disable_interrupts( ai ); - - free_irq(dev->irq, dev); - - set_bit(JOB_DIE, &ai->jobs); - kthread_stop(ai->airo_thread_task); - } - return 0; -} - -void stop_airo_card( struct net_device *dev, int freeres ) -{ - struct airo_info *ai = dev->ml_priv; - - set_bit(FLAG_RADIO_DOWN, &ai->flags); - disable_MAC(ai, 1); - disable_interrupts(ai); - takedown_proc_entry( dev, ai ); - if (test_bit(FLAG_REGISTERED, &ai->flags)) { - unregister_netdev( dev ); - if (ai->wifidev) { - unregister_netdev(ai->wifidev); - free_netdev(ai->wifidev); - ai->wifidev = NULL; - } - clear_bit(FLAG_REGISTERED, &ai->flags); - } - /* - * Clean out tx queue - */ - if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) { - struct sk_buff *skb = NULL; - for (;(skb = skb_dequeue(&ai->txq));) - dev_kfree_skb(skb); - } - - airo_networks_free (ai); - - kfree(ai->flash); - kfree(ai->rssi); - kfree(ai->SSID); - if (freeres) { - /* PCMCIA frees this stuff, so only for PCI and ISA */ - release_region( dev->base_addr, 64 ); - if (test_bit(FLAG_MPI, &ai->flags)) { - if (ai->pci) - mpi_unmap_card(ai->pci); - if (ai->pcimem) - iounmap(ai->pcimem); - if (ai->pciaux) - iounmap(ai->pciaux); - pci_free_consistent(ai->pci, PCI_SHARED_LEN, - ai->shared, ai->shared_dma); - } - } - crypto_free_cipher(ai->tfm); - del_airo_dev(ai); - free_netdev( dev ); -} - -EXPORT_SYMBOL(stop_airo_card); - -static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr) -{ - memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); - return ETH_ALEN; -} - -static void mpi_unmap_card(struct pci_dev *pci) -{ - unsigned long mem_start = pci_resource_start(pci, 1); - unsigned long mem_len = pci_resource_len(pci, 1); - unsigned long aux_start = pci_resource_start(pci, 2); - unsigned long aux_len = AUXMEMSIZE; - - release_mem_region(aux_start, aux_len); - release_mem_region(mem_start, mem_len); -} - -/************************************************************* - * This routine assumes that descriptors have been setup . - * Run at insmod time or after reset when the decriptors - * have been initialized . Returns 0 if all is well nz - * otherwise . Does not allocate memory but sets up card - * using previously allocated descriptors. - */ -static int mpi_init_descriptors (struct airo_info *ai) -{ - Cmd cmd; - Resp rsp; - int i; - int rc = SUCCESS; - - /* Alloc card RX descriptors */ - netif_stop_queue(ai->dev); - - memset(&rsp,0,sizeof(rsp)); - memset(&cmd,0,sizeof(cmd)); - - cmd.cmd = CMD_ALLOCATEAUX; - cmd.parm0 = FID_RX; - cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux); - cmd.parm2 = MPI_MAX_FIDS; - rc=issuecommand(ai, &cmd, &rsp); - if (rc != SUCCESS) { - airo_print_err(ai->dev->name, "Couldn't allocate RX FID"); - return rc; - } - - for (i=0; irxfids[i].card_ram_off, - &ai->rxfids[i].rx_desc, sizeof(RxFid)); - } - - /* Alloc card TX descriptors */ - - memset(&rsp,0,sizeof(rsp)); - memset(&cmd,0,sizeof(cmd)); - - cmd.cmd = CMD_ALLOCATEAUX; - cmd.parm0 = FID_TX; - cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux); - cmd.parm2 = MPI_MAX_FIDS; - - for (i=0; itxfids[i].tx_desc.valid = 1; - memcpy_toio(ai->txfids[i].card_ram_off, - &ai->txfids[i].tx_desc, sizeof(TxFid)); - } - ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */ - - rc=issuecommand(ai, &cmd, &rsp); - if (rc != SUCCESS) { - airo_print_err(ai->dev->name, "Couldn't allocate TX FID"); - return rc; - } - - /* Alloc card Rid descriptor */ - memset(&rsp,0,sizeof(rsp)); - memset(&cmd,0,sizeof(cmd)); - - cmd.cmd = CMD_ALLOCATEAUX; - cmd.parm0 = RID_RW; - cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux); - cmd.parm2 = 1; /* Magic number... */ - rc=issuecommand(ai, &cmd, &rsp); - if (rc != SUCCESS) { - airo_print_err(ai->dev->name, "Couldn't allocate RID"); - return rc; - } - - memcpy_toio(ai->config_desc.card_ram_off, - &ai->config_desc.rid_desc, sizeof(Rid)); - - return rc; -} - -/* - * We are setting up three things here: - * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid. - * 2) Map PCI memory for issuing commands. - * 3) Allocate memory (shared) to send and receive ethernet frames. - */ -static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci) -{ - unsigned long mem_start, mem_len, aux_start, aux_len; - int rc = -1; - int i; - dma_addr_t busaddroff; - unsigned char *vpackoff; - unsigned char __iomem *pciaddroff; - - mem_start = pci_resource_start(pci, 1); - mem_len = pci_resource_len(pci, 1); - aux_start = pci_resource_start(pci, 2); - aux_len = AUXMEMSIZE; - - if (!request_mem_region(mem_start, mem_len, DRV_NAME)) { - airo_print_err("", "Couldn't get region %x[%x]", - (int)mem_start, (int)mem_len); - goto out; - } - if (!request_mem_region(aux_start, aux_len, DRV_NAME)) { - airo_print_err("", "Couldn't get region %x[%x]", - (int)aux_start, (int)aux_len); - goto free_region1; - } - - ai->pcimem = ioremap(mem_start, mem_len); - if (!ai->pcimem) { - airo_print_err("", "Couldn't map region %x[%x]", - (int)mem_start, (int)mem_len); - goto free_region2; - } - ai->pciaux = ioremap(aux_start, aux_len); - if (!ai->pciaux) { - airo_print_err("", "Couldn't map region %x[%x]", - (int)aux_start, (int)aux_len); - goto free_memmap; - } - - /* Reserve PKTSIZE for each fid and 2K for the Rids */ - ai->shared = pci_alloc_consistent(pci, PCI_SHARED_LEN, &ai->shared_dma); - if (!ai->shared) { - airo_print_err("", "Couldn't alloc_consistent %d", - PCI_SHARED_LEN); - goto free_auxmap; - } - - /* - * Setup descriptor RX, TX, CONFIG - */ - busaddroff = ai->shared_dma; - pciaddroff = ai->pciaux + AUX_OFFSET; - vpackoff = ai->shared; - - /* RX descriptor setup */ - for(i = 0; i < MPI_MAX_FIDS; i++) { - ai->rxfids[i].pending = 0; - ai->rxfids[i].card_ram_off = pciaddroff; - ai->rxfids[i].virtual_host_addr = vpackoff; - ai->rxfids[i].rx_desc.host_addr = busaddroff; - ai->rxfids[i].rx_desc.valid = 1; - ai->rxfids[i].rx_desc.len = PKTSIZE; - ai->rxfids[i].rx_desc.rdy = 0; - - pciaddroff += sizeof(RxFid); - busaddroff += PKTSIZE; - vpackoff += PKTSIZE; - } - - /* TX descriptor setup */ - for(i = 0; i < MPI_MAX_FIDS; i++) { - ai->txfids[i].card_ram_off = pciaddroff; - ai->txfids[i].virtual_host_addr = vpackoff; - ai->txfids[i].tx_desc.valid = 1; - ai->txfids[i].tx_desc.host_addr = busaddroff; - memcpy(ai->txfids[i].virtual_host_addr, - &wifictlhdr8023, sizeof(wifictlhdr8023)); - - pciaddroff += sizeof(TxFid); - busaddroff += PKTSIZE; - vpackoff += PKTSIZE; - } - ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */ - - /* Rid descriptor setup */ - ai->config_desc.card_ram_off = pciaddroff; - ai->config_desc.virtual_host_addr = vpackoff; - ai->config_desc.rid_desc.host_addr = busaddroff; - ai->ridbus = busaddroff; - ai->config_desc.rid_desc.rid = 0; - ai->config_desc.rid_desc.len = RIDSIZE; - ai->config_desc.rid_desc.valid = 1; - pciaddroff += sizeof(Rid); - busaddroff += RIDSIZE; - vpackoff += RIDSIZE; - - /* Tell card about descriptors */ - if (mpi_init_descriptors (ai) != SUCCESS) - goto free_shared; - - return 0; - free_shared: - pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma); - free_auxmap: - iounmap(ai->pciaux); - free_memmap: - iounmap(ai->pcimem); - free_region2: - release_mem_region(aux_start, aux_len); - free_region1: - release_mem_region(mem_start, mem_len); - out: - return rc; -} - -static const struct header_ops airo_header_ops = { - .parse = wll_header_parse, -}; - -static const struct net_device_ops airo11_netdev_ops = { - .ndo_open = airo_open, - .ndo_stop = airo_close, - .ndo_start_xmit = airo_start_xmit11, - .ndo_get_stats = airo_get_stats, - .ndo_set_mac_address = airo_set_mac_address, - .ndo_do_ioctl = airo_ioctl, - .ndo_change_mtu = airo_change_mtu, -}; - -static void wifi_setup(struct net_device *dev) -{ - dev->netdev_ops = &airo11_netdev_ops; - dev->header_ops = &airo_header_ops; - dev->wireless_handlers = &airo_handler_def; - - dev->type = ARPHRD_IEEE80211; - dev->hard_header_len = ETH_HLEN; - dev->mtu = AIRO_DEF_MTU; - dev->addr_len = ETH_ALEN; - dev->tx_queue_len = 100; - - eth_broadcast_addr(dev->broadcast); - - dev->flags = IFF_BROADCAST|IFF_MULTICAST; -} - -static struct net_device *init_wifidev(struct airo_info *ai, - struct net_device *ethdev) -{ - int err; - struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN, - wifi_setup); - if (!dev) - return NULL; - dev->ml_priv = ethdev->ml_priv; - dev->irq = ethdev->irq; - dev->base_addr = ethdev->base_addr; - dev->wireless_data = ethdev->wireless_data; - SET_NETDEV_DEV(dev, ethdev->dev.parent); - eth_hw_addr_inherit(dev, ethdev); - err = register_netdev(dev); - if (err<0) { - free_netdev(dev); - return NULL; - } - return dev; -} - -static int reset_card( struct net_device *dev , int lock) { - struct airo_info *ai = dev->ml_priv; - - if (lock && down_interruptible(&ai->sem)) - return -1; - waitbusy (ai); - OUT4500(ai,COMMAND,CMD_SOFTRESET); - msleep(200); - waitbusy (ai); - msleep(200); - if (lock) - up(&ai->sem); - return 0; -} - -#define AIRO_MAX_NETWORK_COUNT 64 -static int airo_networks_allocate(struct airo_info *ai) -{ - if (ai->networks) - return 0; - - ai->networks = kcalloc(AIRO_MAX_NETWORK_COUNT, sizeof(BSSListElement), - GFP_KERNEL); - if (!ai->networks) { - airo_print_warn("", "Out of memory allocating beacons"); - return -ENOMEM; - } - - return 0; -} - -static void airo_networks_free(struct airo_info *ai) -{ - kfree(ai->networks); - ai->networks = NULL; -} - -static void airo_networks_initialize(struct airo_info *ai) -{ - int i; - - INIT_LIST_HEAD(&ai->network_free_list); - INIT_LIST_HEAD(&ai->network_list); - for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++) - list_add_tail(&ai->networks[i].list, - &ai->network_free_list); -} - -static const struct net_device_ops airo_netdev_ops = { - .ndo_open = airo_open, - .ndo_stop = airo_close, - .ndo_start_xmit = airo_start_xmit, - .ndo_get_stats = airo_get_stats, - .ndo_set_rx_mode = airo_set_multicast_list, - .ndo_set_mac_address = airo_set_mac_address, - .ndo_do_ioctl = airo_ioctl, - .ndo_change_mtu = airo_change_mtu, - .ndo_validate_addr = eth_validate_addr, -}; - -static const struct net_device_ops mpi_netdev_ops = { - .ndo_open = airo_open, - .ndo_stop = airo_close, - .ndo_start_xmit = mpi_start_xmit, - .ndo_get_stats = airo_get_stats, - .ndo_set_rx_mode = airo_set_multicast_list, - .ndo_set_mac_address = airo_set_mac_address, - .ndo_do_ioctl = airo_ioctl, - .ndo_change_mtu = airo_change_mtu, - .ndo_validate_addr = eth_validate_addr, -}; - - -static struct net_device *_init_airo_card( unsigned short irq, int port, - int is_pcmcia, struct pci_dev *pci, - struct device *dmdev ) -{ - struct net_device *dev; - struct airo_info *ai; - int i, rc; - CapabilityRid cap_rid; - - /* Create the network device object. */ - dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup); - if (!dev) { - airo_print_err("", "Couldn't alloc_etherdev"); - return NULL; - } - - ai = dev->ml_priv = netdev_priv(dev); - ai->wifidev = NULL; - ai->flags = 1 << FLAG_RADIO_DOWN; - ai->jobs = 0; - ai->dev = dev; - if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) { - airo_print_dbg("", "Found an MPI350 card"); - set_bit(FLAG_MPI, &ai->flags); - } - spin_lock_init(&ai->aux_lock); - sema_init(&ai->sem, 1); - ai->config.len = 0; - ai->pci = pci; - init_waitqueue_head (&ai->thr_wait); - ai->tfm = NULL; - add_airo_dev(ai); - ai->APList.len = cpu_to_le16(sizeof(struct APListRid)); - - if (airo_networks_allocate (ai)) - goto err_out_free; - airo_networks_initialize (ai); - - skb_queue_head_init (&ai->txq); - - /* The Airo-specific entries in the device structure. */ - if (test_bit(FLAG_MPI,&ai->flags)) - dev->netdev_ops = &mpi_netdev_ops; - else - dev->netdev_ops = &airo_netdev_ops; - dev->wireless_handlers = &airo_handler_def; - ai->wireless_data.spy_data = &ai->spy_data; - dev->wireless_data = &ai->wireless_data; - dev->irq = irq; - dev->base_addr = port; - dev->priv_flags &= ~IFF_TX_SKB_SHARING; - - SET_NETDEV_DEV(dev, dmdev); - - reset_card (dev, 1); - msleep(400); - - if (!is_pcmcia) { - if (!request_region(dev->base_addr, 64, DRV_NAME)) { - rc = -EBUSY; - airo_print_err(dev->name, "Couldn't request region"); - goto err_out_nets; - } - } - - if (test_bit(FLAG_MPI,&ai->flags)) { - if (mpi_map_card(ai, pci)) { - airo_print_err("", "Could not map memory"); - goto err_out_res; - } - } - - if (probe) { - if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) { - airo_print_err(dev->name, "MAC could not be enabled" ); - rc = -EIO; - goto err_out_map; - } - } else if (!test_bit(FLAG_MPI,&ai->flags)) { - ai->bap_read = fast_bap_read; - set_bit(FLAG_FLASHING, &ai->flags); - } - - strcpy(dev->name, "eth%d"); - rc = register_netdev(dev); - if (rc) { - airo_print_err(dev->name, "Couldn't register_netdev"); - goto err_out_map; - } - ai->wifidev = init_wifidev(ai, dev); - if (!ai->wifidev) - goto err_out_reg; - - rc = readCapabilityRid(ai, &cap_rid, 1); - if (rc != SUCCESS) { - rc = -EIO; - goto err_out_wifi; - } - /* WEP capability discovery */ - ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0; - ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0; - - airo_print_info(dev->name, "Firmware version %x.%x.%02d", - ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF), - (le16_to_cpu(cap_rid.softVer) & 0xFF), - le16_to_cpu(cap_rid.softSubVer)); - - /* Test for WPA support */ - /* Only firmware versions 5.30.17 or better can do WPA */ - if (le16_to_cpu(cap_rid.softVer) > 0x530 - || (le16_to_cpu(cap_rid.softVer) == 0x530 - && le16_to_cpu(cap_rid.softSubVer) >= 17)) { - airo_print_info(ai->dev->name, "WPA supported."); - - set_bit(FLAG_WPA_CAPABLE, &ai->flags); - ai->bssListFirst = RID_WPA_BSSLISTFIRST; - ai->bssListNext = RID_WPA_BSSLISTNEXT; - ai->bssListRidLen = sizeof(BSSListRid); - } else { - airo_print_info(ai->dev->name, "WPA unsupported with firmware " - "versions older than 5.30.17."); - - ai->bssListFirst = RID_BSSLISTFIRST; - ai->bssListNext = RID_BSSLISTNEXT; - ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra); - } - - set_bit(FLAG_REGISTERED,&ai->flags); - airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr); - - /* Allocate the transmit buffers */ - if (probe && !test_bit(FLAG_MPI,&ai->flags)) - for( i = 0; i < MAX_FIDS; i++ ) - ai->fids[i] = transmit_allocate(ai,AIRO_DEF_MTU,i>=MAX_FIDS/2); - - if (setup_proc_entry(dev, dev->ml_priv) < 0) - goto err_out_wifi; - - return dev; - -err_out_wifi: - unregister_netdev(ai->wifidev); - free_netdev(ai->wifidev); -err_out_reg: - unregister_netdev(dev); -err_out_map: - if (test_bit(FLAG_MPI,&ai->flags) && pci) { - pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma); - iounmap(ai->pciaux); - iounmap(ai->pcimem); - mpi_unmap_card(ai->pci); - } -err_out_res: - if (!is_pcmcia) - release_region( dev->base_addr, 64 ); -err_out_nets: - airo_networks_free(ai); -err_out_free: - del_airo_dev(ai); - free_netdev(dev); - return NULL; -} - -struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia, - struct device *dmdev) -{ - return _init_airo_card ( irq, port, is_pcmcia, NULL, dmdev); -} - -EXPORT_SYMBOL(init_airo_card); - -static int waitbusy (struct airo_info *ai) { - int delay = 0; - while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) { - udelay (10); - if ((++delay % 20) == 0) - OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); - } - return delay < 10000; -} - -int reset_airo_card( struct net_device *dev ) -{ - int i; - struct airo_info *ai = dev->ml_priv; - - if (reset_card (dev, 1)) - return -1; - - if ( setup_card(ai, dev->dev_addr, 1 ) != SUCCESS ) { - airo_print_err(dev->name, "MAC could not be enabled"); - return -1; - } - airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr); - /* Allocate the transmit buffers if needed */ - if (!test_bit(FLAG_MPI,&ai->flags)) - for( i = 0; i < MAX_FIDS; i++ ) - ai->fids[i] = transmit_allocate (ai,AIRO_DEF_MTU,i>=MAX_FIDS/2); - - enable_interrupts( ai ); - netif_wake_queue(dev); - return 0; -} - -EXPORT_SYMBOL(reset_airo_card); - -static void airo_send_event(struct net_device *dev) { - struct airo_info *ai = dev->ml_priv; - union iwreq_data wrqu; - StatusRid status_rid; - - clear_bit(JOB_EVENT, &ai->jobs); - PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0); - up(&ai->sem); - wrqu.data.length = 0; - wrqu.data.flags = 0; - memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - - /* Send event to user space */ - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); -} - -static void airo_process_scan_results (struct airo_info *ai) { - union iwreq_data wrqu; - BSSListRid bss; - int rc; - BSSListElement * loop_net; - BSSListElement * tmp_net; - - /* Blow away current list of scan results */ - list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) { - list_move_tail (&loop_net->list, &ai->network_free_list); - /* Don't blow away ->list, just BSS data */ - memset (loop_net, 0, sizeof (loop_net->bss)); - } - - /* Try to read the first entry of the scan result */ - rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0); - if((rc) || (bss.index == cpu_to_le16(0xffff))) { - /* No scan results */ - goto out; - } - - /* Read and parse all entries */ - tmp_net = NULL; - while((!rc) && (bss.index != cpu_to_le16(0xffff))) { - /* Grab a network off the free list */ - if (!list_empty(&ai->network_free_list)) { - tmp_net = list_entry(ai->network_free_list.next, - BSSListElement, list); - list_del(ai->network_free_list.next); - } - - if (tmp_net != NULL) { - memcpy(tmp_net, &bss, sizeof(tmp_net->bss)); - list_add_tail(&tmp_net->list, &ai->network_list); - tmp_net = NULL; - } - - /* Read next entry */ - rc = PC4500_readrid(ai, ai->bssListNext, - &bss, ai->bssListRidLen, 0); - } - -out: - /* write APList back (we cleared it in airo_set_scan) */ - disable_MAC(ai, 2); - writeAPListRid(ai, &ai->APList, 0); - enable_MAC(ai, 0); - - ai->scan_timeout = 0; - clear_bit(JOB_SCAN_RESULTS, &ai->jobs); - up(&ai->sem); - - /* Send an empty event to user space. - * We don't send the received data on - * the event because it would require - * us to do complex transcoding, and - * we want to minimise the work done in - * the irq handler. Use a request to - * extract the data - Jean II */ - wrqu.data.length = 0; - wrqu.data.flags = 0; - wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL); -} - -static int airo_thread(void *data) { - struct net_device *dev = data; - struct airo_info *ai = dev->ml_priv; - int locked; - - set_freezable(); - while(1) { - /* make swsusp happy with our thread */ - try_to_freeze(); - - if (test_bit(JOB_DIE, &ai->jobs)) - break; - - if (ai->jobs) { - locked = down_interruptible(&ai->sem); - } else { - wait_queue_t wait; - - init_waitqueue_entry(&wait, current); - add_wait_queue(&ai->thr_wait, &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - if (ai->jobs) - break; - if (ai->expires || ai->scan_timeout) { - if (ai->scan_timeout && - time_after_eq(jiffies,ai->scan_timeout)){ - set_bit(JOB_SCAN_RESULTS, &ai->jobs); - break; - } else if (ai->expires && - time_after_eq(jiffies,ai->expires)){ - set_bit(JOB_AUTOWEP, &ai->jobs); - break; - } - if (!kthread_should_stop() && - !freezing(current)) { - unsigned long wake_at; - if (!ai->expires || !ai->scan_timeout) { - wake_at = max(ai->expires, - ai->scan_timeout); - } else { - wake_at = min(ai->expires, - ai->scan_timeout); - } - schedule_timeout(wake_at - jiffies); - continue; - } - } else if (!kthread_should_stop() && - !freezing(current)) { - schedule(); - continue; - } - break; - } - current->state = TASK_RUNNING; - remove_wait_queue(&ai->thr_wait, &wait); - locked = 1; - } - - if (locked) - continue; - - if (test_bit(JOB_DIE, &ai->jobs)) { - up(&ai->sem); - break; - } - - if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) { - up(&ai->sem); - continue; - } - - if (test_bit(JOB_XMIT, &ai->jobs)) - airo_end_xmit(dev); - else if (test_bit(JOB_XMIT11, &ai->jobs)) - airo_end_xmit11(dev); - else if (test_bit(JOB_STATS, &ai->jobs)) - airo_read_stats(dev); - else if (test_bit(JOB_WSTATS, &ai->jobs)) - airo_read_wireless_stats(ai); - else if (test_bit(JOB_PROMISC, &ai->jobs)) - airo_set_promisc(ai); - else if (test_bit(JOB_MIC, &ai->jobs)) - micinit(ai); - else if (test_bit(JOB_EVENT, &ai->jobs)) - airo_send_event(dev); - else if (test_bit(JOB_AUTOWEP, &ai->jobs)) - timer_func(dev); - else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs)) - airo_process_scan_results(ai); - else /* Shouldn't get here, but we make sure to unlock */ - up(&ai->sem); - } - - return 0; -} - -static int header_len(__le16 ctl) -{ - u16 fc = le16_to_cpu(ctl); - switch (fc & 0xc) { - case 4: - if ((fc & 0xe0) == 0xc0) - return 10; /* one-address control packet */ - return 16; /* two-address control packet */ - case 8: - if ((fc & 0x300) == 0x300) - return 30; /* WDS packet */ - } - return 24; -} - -static void airo_handle_cisco_mic(struct airo_info *ai) -{ - if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) { - set_bit(JOB_MIC, &ai->jobs); - wake_up_interruptible(&ai->thr_wait); - } -} - -/* Airo Status codes */ -#define STAT_NOBEACON 0x8000 /* Loss of sync - missed beacons */ -#define STAT_MAXRETRIES 0x8001 /* Loss of sync - max retries */ -#define STAT_MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/ -#define STAT_FORCELOSS 0x8003 /* Loss of sync - host request */ -#define STAT_TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */ -#define STAT_DEAUTH 0x8100 /* low byte is 802.11 reason code */ -#define STAT_DISASSOC 0x8200 /* low byte is 802.11 reason code */ -#define STAT_ASSOC_FAIL 0x8400 /* low byte is 802.11 reason code */ -#define STAT_AUTH_FAIL 0x0300 /* low byte is 802.11 reason code */ -#define STAT_ASSOC 0x0400 /* Associated */ -#define STAT_REASSOC 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */ - -static void airo_print_status(const char *devname, u16 status) -{ - u8 reason = status & 0xFF; - - switch (status & 0xFF00) { - case STAT_NOBEACON: - switch (status) { - case STAT_NOBEACON: - airo_print_dbg(devname, "link lost (missed beacons)"); - break; - case STAT_MAXRETRIES: - case STAT_MAXARL: - airo_print_dbg(devname, "link lost (max retries)"); - break; - case STAT_FORCELOSS: - airo_print_dbg(devname, "link lost (local choice)"); - break; - case STAT_TSFSYNC: - airo_print_dbg(devname, "link lost (TSF sync lost)"); - break; - default: - airo_print_dbg(devname, "unknown status %x\n", status); - break; - } - break; - case STAT_DEAUTH: - airo_print_dbg(devname, "deauthenticated (reason: %d)", reason); - break; - case STAT_DISASSOC: - airo_print_dbg(devname, "disassociated (reason: %d)", reason); - break; - case STAT_ASSOC_FAIL: - airo_print_dbg(devname, "association failed (reason: %d)", - reason); - break; - case STAT_AUTH_FAIL: - airo_print_dbg(devname, "authentication failed (reason: %d)", - reason); - break; - case STAT_ASSOC: - case STAT_REASSOC: - break; - default: - airo_print_dbg(devname, "unknown status %x\n", status); - break; - } -} - -static void airo_handle_link(struct airo_info *ai) -{ - union iwreq_data wrqu; - int scan_forceloss = 0; - u16 status; - - /* Get new status and acknowledge the link change */ - status = le16_to_cpu(IN4500(ai, LINKSTAT)); - OUT4500(ai, EVACK, EV_LINK); - - if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0)) - scan_forceloss = 1; - - airo_print_status(ai->dev->name, status); - - if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) { - if (auto_wep) - ai->expires = 0; - if (ai->list_bss_task) - wake_up_process(ai->list_bss_task); - set_bit(FLAG_UPDATE_UNI, &ai->flags); - set_bit(FLAG_UPDATE_MULTI, &ai->flags); - - if (down_trylock(&ai->sem) != 0) { - set_bit(JOB_EVENT, &ai->jobs); - wake_up_interruptible(&ai->thr_wait); - } else - airo_send_event(ai->dev); - netif_carrier_on(ai->dev); - } else if (!scan_forceloss) { - if (auto_wep && !ai->expires) { - ai->expires = RUN_AT(3*HZ); - wake_up_interruptible(&ai->thr_wait); - } - - /* Send event to user space */ - eth_zero_addr(wrqu.ap_addr.sa_data); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL); - netif_carrier_off(ai->dev); - } else { - netif_carrier_off(ai->dev); - } -} - -static void airo_handle_rx(struct airo_info *ai) -{ - struct sk_buff *skb = NULL; - __le16 fc, v, *buffer, tmpbuf[4]; - u16 len, hdrlen = 0, gap, fid; - struct rx_hdr hdr; - int success = 0; - - if (test_bit(FLAG_MPI, &ai->flags)) { - if (test_bit(FLAG_802_11, &ai->flags)) - mpi_receive_802_11(ai); - else - mpi_receive_802_3(ai); - OUT4500(ai, EVACK, EV_RX); - return; - } - - fid = IN4500(ai, RXFID); - - /* Get the packet length */ - if (test_bit(FLAG_802_11, &ai->flags)) { - bap_setup (ai, fid, 4, BAP0); - bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0); - /* Bad CRC. Ignore packet */ - if (le16_to_cpu(hdr.status) & 2) - hdr.len = 0; - if (ai->wifidev == NULL) - hdr.len = 0; - } else { - bap_setup(ai, fid, 0x36, BAP0); - bap_read(ai, &hdr.len, 2, BAP0); - } - len = le16_to_cpu(hdr.len); - - if (len > AIRO_DEF_MTU) { - airo_print_err(ai->dev->name, "Bad size %d", len); - goto done; - } - if (len == 0) - goto done; - - if (test_bit(FLAG_802_11, &ai->flags)) { - bap_read(ai, &fc, sizeof (fc), BAP0); - hdrlen = header_len(fc); - } else - hdrlen = ETH_ALEN * 2; - - skb = dev_alloc_skb(len + hdrlen + 2 + 2); - if (!skb) { - ai->dev->stats.rx_dropped++; - goto done; - } - - skb_reserve(skb, 2); /* This way the IP header is aligned */ - buffer = (__le16 *) skb_put(skb, len + hdrlen); - if (test_bit(FLAG_802_11, &ai->flags)) { - buffer[0] = fc; - bap_read(ai, buffer + 1, hdrlen - 2, BAP0); - if (hdrlen == 24) - bap_read(ai, tmpbuf, 6, BAP0); - - bap_read(ai, &v, sizeof(v), BAP0); - gap = le16_to_cpu(v); - if (gap) { - if (gap <= 8) { - bap_read(ai, tmpbuf, gap, BAP0); - } else { - airo_print_err(ai->dev->name, "gaplen too " - "big. Problems will follow..."); - } - } - bap_read(ai, buffer + hdrlen/2, len, BAP0); - } else { - MICBuffer micbuf; - - bap_read(ai, buffer, ETH_ALEN * 2, BAP0); - if (ai->micstats.enabled) { - bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0); - if (ntohs(micbuf.typelen) > 0x05DC) - bap_setup(ai, fid, 0x44, BAP0); - else { - if (len <= sizeof (micbuf)) { - dev_kfree_skb_irq(skb); - goto done; - } - - len -= sizeof(micbuf); - skb_trim(skb, len + hdrlen); - } - } - - bap_read(ai, buffer + ETH_ALEN, len, BAP0); - if (decapsulate(ai, &micbuf, (etherHead*) buffer, len)) - dev_kfree_skb_irq (skb); - else - success = 1; - } - -#ifdef WIRELESS_SPY - if (success && (ai->spy_data.spy_number > 0)) { - char *sa; - struct iw_quality wstats; - - /* Prepare spy data : addr + qual */ - if (!test_bit(FLAG_802_11, &ai->flags)) { - sa = (char *) buffer + 6; - bap_setup(ai, fid, 8, BAP0); - bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0); - } else - sa = (char *) buffer + 10; - wstats.qual = hdr.rssi[0]; - if (ai->rssi) - wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm; - else - wstats.level = (hdr.rssi[1] + 321) / 2; - wstats.noise = ai->wstats.qual.noise; - wstats.updated = IW_QUAL_LEVEL_UPDATED - | IW_QUAL_QUAL_UPDATED - | IW_QUAL_DBM; - /* Update spy records */ - wireless_spy_update(ai->dev, sa, &wstats); - } -#endif /* WIRELESS_SPY */ - -done: - OUT4500(ai, EVACK, EV_RX); - - if (success) { - if (test_bit(FLAG_802_11, &ai->flags)) { - skb_reset_mac_header(skb); - skb->pkt_type = PACKET_OTHERHOST; - skb->dev = ai->wifidev; - skb->protocol = htons(ETH_P_802_2); - } else - skb->protocol = eth_type_trans(skb, ai->dev); - skb->ip_summed = CHECKSUM_NONE; - - netif_rx(skb); - } -} - -static void airo_handle_tx(struct airo_info *ai, u16 status) -{ - int i, len = 0, index = -1; - u16 fid; - - if (test_bit(FLAG_MPI, &ai->flags)) { - unsigned long flags; - - if (status & EV_TXEXC) - get_tx_error(ai, -1); - - spin_lock_irqsave(&ai->aux_lock, flags); - if (!skb_queue_empty(&ai->txq)) { - spin_unlock_irqrestore(&ai->aux_lock,flags); - mpi_send_packet(ai->dev); - } else { - clear_bit(FLAG_PENDING_XMIT, &ai->flags); - spin_unlock_irqrestore(&ai->aux_lock,flags); - netif_wake_queue(ai->dev); - } - OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC)); - return; - } - - fid = IN4500(ai, TXCOMPLFID); - - for(i = 0; i < MAX_FIDS; i++) { - if ((ai->fids[i] & 0xffff) == fid) { - len = ai->fids[i] >> 16; - index = i; - } - } - - if (index != -1) { - if (status & EV_TXEXC) - get_tx_error(ai, index); - - OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC)); - - /* Set up to be used again */ - ai->fids[index] &= 0xffff; - if (index < MAX_FIDS / 2) { - if (!test_bit(FLAG_PENDING_XMIT, &ai->flags)) - netif_wake_queue(ai->dev); - } else { - if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags)) - netif_wake_queue(ai->wifidev); - } - } else { - OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC)); - airo_print_err(ai->dev->name, "Unallocated FID was used to xmit"); - } -} - -static irqreturn_t airo_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - u16 status, savedInterrupts = 0; - struct airo_info *ai = dev->ml_priv; - int handled = 0; - - if (!netif_device_present(dev)) - return IRQ_NONE; - - for (;;) { - status = IN4500(ai, EVSTAT); - if (!(status & STATUS_INTS) || (status == 0xffff)) - break; - - handled = 1; - - if (status & EV_AWAKE) { - OUT4500(ai, EVACK, EV_AWAKE); - OUT4500(ai, EVACK, EV_AWAKE); - } - - if (!savedInterrupts) { - savedInterrupts = IN4500(ai, EVINTEN); - OUT4500(ai, EVINTEN, 0); - } - - if (status & EV_MIC) { - OUT4500(ai, EVACK, EV_MIC); - airo_handle_cisco_mic(ai); - } - - if (status & EV_LINK) { - /* Link status changed */ - airo_handle_link(ai); - } - - /* Check to see if there is something to receive */ - if (status & EV_RX) - airo_handle_rx(ai); - - /* Check to see if a packet has been transmitted */ - if (status & (EV_TX | EV_TXCPY | EV_TXEXC)) - airo_handle_tx(ai, status); - - if ( status & ~STATUS_INTS & ~IGNORE_INTS ) { - airo_print_warn(ai->dev->name, "Got weird status %x", - status & ~STATUS_INTS & ~IGNORE_INTS ); - } - } - - if (savedInterrupts) - OUT4500(ai, EVINTEN, savedInterrupts); - - return IRQ_RETVAL(handled); -} - -/* - * Routines to talk to the card - */ - -/* - * This was originally written for the 4500, hence the name - * NOTE: If use with 8bit mode and SMP bad things will happen! - * Why would some one do 8 bit IO in an SMP machine?!? - */ -static void OUT4500( struct airo_info *ai, u16 reg, u16 val ) { - if (test_bit(FLAG_MPI,&ai->flags)) - reg <<= 1; - if ( !do8bitIO ) - outw( val, ai->dev->base_addr + reg ); - else { - outb( val & 0xff, ai->dev->base_addr + reg ); - outb( val >> 8, ai->dev->base_addr + reg + 1 ); - } -} - -static u16 IN4500( struct airo_info *ai, u16 reg ) { - unsigned short rc; - - if (test_bit(FLAG_MPI,&ai->flags)) - reg <<= 1; - if ( !do8bitIO ) - rc = inw( ai->dev->base_addr + reg ); - else { - rc = inb( ai->dev->base_addr + reg ); - rc += ((int)inb( ai->dev->base_addr + reg + 1 )) << 8; - } - return rc; -} - -static int enable_MAC(struct airo_info *ai, int lock) -{ - int rc; - Cmd cmd; - Resp rsp; - - /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions - * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down" - * Note : we could try to use !netif_running(dev) in enable_MAC() - * instead of this flag, but I don't trust it *within* the - * open/close functions, and testing both flags together is - * "cheaper" - Jean II */ - if (ai->flags & FLAG_RADIO_MASK) return SUCCESS; - - if (lock && down_interruptible(&ai->sem)) - return -ERESTARTSYS; - - if (!test_bit(FLAG_ENABLED, &ai->flags)) { - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd = MAC_ENABLE; - rc = issuecommand(ai, &cmd, &rsp); - if (rc == SUCCESS) - set_bit(FLAG_ENABLED, &ai->flags); - } else - rc = SUCCESS; - - if (lock) - up(&ai->sem); - - if (rc) - airo_print_err(ai->dev->name, "Cannot enable MAC"); - else if ((rsp.status & 0xFF00) != 0) { - airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, " - "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2); - rc = ERROR; - } - return rc; -} - -static void disable_MAC( struct airo_info *ai, int lock ) { - Cmd cmd; - Resp rsp; - - if (lock == 1 && down_interruptible(&ai->sem)) - return; - - if (test_bit(FLAG_ENABLED, &ai->flags)) { - if (lock != 2) /* lock == 2 means don't disable carrier */ - netif_carrier_off(ai->dev); - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd = MAC_DISABLE; // disable in case already enabled - issuecommand(ai, &cmd, &rsp); - clear_bit(FLAG_ENABLED, &ai->flags); - } - if (lock == 1) - up(&ai->sem); -} - -static void enable_interrupts( struct airo_info *ai ) { - /* Enable the interrupts */ - OUT4500( ai, EVINTEN, STATUS_INTS ); -} - -static void disable_interrupts( struct airo_info *ai ) { - OUT4500( ai, EVINTEN, 0 ); -} - -static void mpi_receive_802_3(struct airo_info *ai) -{ - RxFid rxd; - int len = 0; - struct sk_buff *skb; - char *buffer; - int off = 0; - MICBuffer micbuf; - - memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd)); - /* Make sure we got something */ - if (rxd.rdy && rxd.valid == 0) { - len = rxd.len + 12; - if (len < 12 || len > 2048) - goto badrx; - - skb = dev_alloc_skb(len); - if (!skb) { - ai->dev->stats.rx_dropped++; - goto badrx; - } - buffer = skb_put(skb,len); - memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2); - if (ai->micstats.enabled) { - memcpy(&micbuf, - ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2, - sizeof(micbuf)); - if (ntohs(micbuf.typelen) <= 0x05DC) { - if (len <= sizeof(micbuf) + ETH_ALEN * 2) - goto badmic; - - off = sizeof(micbuf); - skb_trim (skb, len - off); - } - } - memcpy(buffer + ETH_ALEN * 2, - ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off, - len - ETH_ALEN * 2 - off); - if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) { -badmic: - dev_kfree_skb_irq (skb); - goto badrx; - } -#ifdef WIRELESS_SPY - if (ai->spy_data.spy_number > 0) { - char *sa; - struct iw_quality wstats; - /* Prepare spy data : addr + qual */ - sa = buffer + ETH_ALEN; - wstats.qual = 0; /* XXX Where do I get that info from ??? */ - wstats.level = 0; - wstats.updated = 0; - /* Update spy records */ - wireless_spy_update(ai->dev, sa, &wstats); - } -#endif /* WIRELESS_SPY */ - - skb->ip_summed = CHECKSUM_NONE; - skb->protocol = eth_type_trans(skb, ai->dev); - netif_rx(skb); - } -badrx: - if (rxd.valid == 0) { - rxd.valid = 1; - rxd.rdy = 0; - rxd.len = PKTSIZE; - memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd)); - } -} - -static void mpi_receive_802_11(struct airo_info *ai) -{ - RxFid rxd; - struct sk_buff *skb = NULL; - u16 len, hdrlen = 0; - __le16 fc; - struct rx_hdr hdr; - u16 gap; - u16 *buffer; - char *ptr = ai->rxfids[0].virtual_host_addr + 4; - - memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd)); - memcpy ((char *)&hdr, ptr, sizeof(hdr)); - ptr += sizeof(hdr); - /* Bad CRC. Ignore packet */ - if (le16_to_cpu(hdr.status) & 2) - hdr.len = 0; - if (ai->wifidev == NULL) - hdr.len = 0; - len = le16_to_cpu(hdr.len); - if (len > AIRO_DEF_MTU) { - airo_print_err(ai->dev->name, "Bad size %d", len); - goto badrx; - } - if (len == 0) - goto badrx; - - fc = get_unaligned((__le16 *)ptr); - hdrlen = header_len(fc); - - skb = dev_alloc_skb( len + hdrlen + 2 ); - if ( !skb ) { - ai->dev->stats.rx_dropped++; - goto badrx; - } - buffer = (u16*)skb_put (skb, len + hdrlen); - memcpy ((char *)buffer, ptr, hdrlen); - ptr += hdrlen; - if (hdrlen == 24) - ptr += 6; - gap = get_unaligned_le16(ptr); - ptr += sizeof(__le16); - if (gap) { - if (gap <= 8) - ptr += gap; - else - airo_print_err(ai->dev->name, - "gaplen too big. Problems will follow..."); - } - memcpy ((char *)buffer + hdrlen, ptr, len); - ptr += len; -#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ - if (ai->spy_data.spy_number > 0) { - char *sa; - struct iw_quality wstats; - /* Prepare spy data : addr + qual */ - sa = (char*)buffer + 10; - wstats.qual = hdr.rssi[0]; - if (ai->rssi) - wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm; - else - wstats.level = (hdr.rssi[1] + 321) / 2; - wstats.noise = ai->wstats.qual.noise; - wstats.updated = IW_QUAL_QUAL_UPDATED - | IW_QUAL_LEVEL_UPDATED - | IW_QUAL_DBM; - /* Update spy records */ - wireless_spy_update(ai->dev, sa, &wstats); - } -#endif /* IW_WIRELESS_SPY */ - skb_reset_mac_header(skb); - skb->pkt_type = PACKET_OTHERHOST; - skb->dev = ai->wifidev; - skb->protocol = htons(ETH_P_802_2); - skb->ip_summed = CHECKSUM_NONE; - netif_rx( skb ); - -badrx: - if (rxd.valid == 0) { - rxd.valid = 1; - rxd.rdy = 0; - rxd.len = PKTSIZE; - memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd)); - } -} - -static inline void set_auth_type(struct airo_info *local, int auth_type) -{ - local->config.authType = auth_type; - /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT). - * Used by airo_set_auth() - */ - if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT) - local->last_auth = auth_type; -} - -static u16 setup_card(struct airo_info *ai, u8 *mac, int lock) -{ - Cmd cmd; - Resp rsp; - int status; - SsidRid mySsid; - __le16 lastindex; - WepKeyRid wkr; - int rc; - - memset( &mySsid, 0, sizeof( mySsid ) ); - kfree (ai->flash); - ai->flash = NULL; - - /* The NOP is the first step in getting the card going */ - cmd.cmd = NOP; - cmd.parm0 = cmd.parm1 = cmd.parm2 = 0; - if (lock && down_interruptible(&ai->sem)) - return ERROR; - if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) { - if (lock) - up(&ai->sem); - return ERROR; - } - disable_MAC( ai, 0); - - // Let's figure out if we need to use the AUX port - if (!test_bit(FLAG_MPI,&ai->flags)) { - cmd.cmd = CMD_ENABLEAUX; - if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { - if (lock) - up(&ai->sem); - airo_print_err(ai->dev->name, "Error checking for AUX port"); - return ERROR; - } - if (!aux_bap || rsp.status & 0xff00) { - ai->bap_read = fast_bap_read; - airo_print_dbg(ai->dev->name, "Doing fast bap_reads"); - } else { - ai->bap_read = aux_bap_read; - airo_print_dbg(ai->dev->name, "Doing AUX bap_reads"); - } - } - if (lock) - up(&ai->sem); - if (ai->config.len == 0) { - int i; - tdsRssiRid rssi_rid; - CapabilityRid cap_rid; - - kfree(ai->SSID); - ai->SSID = NULL; - // general configuration (read/modify/write) - status = readConfigRid(ai, lock); - if ( status != SUCCESS ) return ERROR; - - status = readCapabilityRid(ai, &cap_rid, lock); - if ( status != SUCCESS ) return ERROR; - - status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),lock); - if ( status == SUCCESS ) { - if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL) - memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */ - } - else { - kfree(ai->rssi); - ai->rssi = NULL; - if (cap_rid.softCap & cpu_to_le16(8)) - ai->config.rmode |= RXMODE_NORMALIZED_RSSI; - else - airo_print_warn(ai->dev->name, "unknown received signal " - "level scale"); - } - ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS; - set_auth_type(ai, AUTH_OPEN); - ai->config.modulation = MOD_CCK; - - if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) && - (cap_rid.extSoftCap & cpu_to_le16(1)) && - micsetup(ai) == SUCCESS) { - ai->config.opmode |= MODE_MIC; - set_bit(FLAG_MIC_CAPABLE, &ai->flags); - } - - /* Save off the MAC */ - for( i = 0; i < ETH_ALEN; i++ ) { - mac[i] = ai->config.macAddr[i]; - } - - /* Check to see if there are any insmod configured - rates to add */ - if ( rates[0] ) { - memset(ai->config.rates,0,sizeof(ai->config.rates)); - for( i = 0; i < 8 && rates[i]; i++ ) { - ai->config.rates[i] = rates[i]; - } - } - set_bit (FLAG_COMMIT, &ai->flags); - } - - /* Setup the SSIDs if present */ - if ( ssids[0] ) { - int i; - for( i = 0; i < 3 && ssids[i]; i++ ) { - size_t len = strlen(ssids[i]); - if (len > 32) - len = 32; - mySsid.ssids[i].len = cpu_to_le16(len); - memcpy(mySsid.ssids[i].ssid, ssids[i], len); - } - mySsid.len = cpu_to_le16(sizeof(mySsid)); - } - - status = writeConfigRid(ai, lock); - if ( status != SUCCESS ) return ERROR; - - /* Set up the SSID list */ - if ( ssids[0] ) { - status = writeSsidRid(ai, &mySsid, lock); - if ( status != SUCCESS ) return ERROR; - } - - status = enable_MAC(ai, lock); - if (status != SUCCESS) - return ERROR; - - /* Grab the initial wep key, we gotta save it for auto_wep */ - rc = readWepKeyRid(ai, &wkr, 1, lock); - if (rc == SUCCESS) do { - lastindex = wkr.kindex; - if (wkr.kindex == cpu_to_le16(0xffff)) { - ai->defindex = wkr.mac[0]; - } - rc = readWepKeyRid(ai, &wkr, 0, lock); - } while(lastindex != wkr.kindex); - - try_auto_wep(ai); - - return SUCCESS; -} - -static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) { - // Im really paranoid about letting it run forever! - int max_tries = 600000; - - if (IN4500(ai, EVSTAT) & EV_CMD) - OUT4500(ai, EVACK, EV_CMD); - - OUT4500(ai, PARAM0, pCmd->parm0); - OUT4500(ai, PARAM1, pCmd->parm1); - OUT4500(ai, PARAM2, pCmd->parm2); - OUT4500(ai, COMMAND, pCmd->cmd); - - while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) { - if ((IN4500(ai, COMMAND)) == pCmd->cmd) - // PC4500 didn't notice command, try again - OUT4500(ai, COMMAND, pCmd->cmd); - if (!in_atomic() && (max_tries & 255) == 0) - schedule(); - } - - if ( max_tries == -1 ) { - airo_print_err(ai->dev->name, - "Max tries exceeded when issuing command"); - if (IN4500(ai, COMMAND) & COMMAND_BUSY) - OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); - return ERROR; - } - - // command completed - pRsp->status = IN4500(ai, STATUS); - pRsp->rsp0 = IN4500(ai, RESP0); - pRsp->rsp1 = IN4500(ai, RESP1); - pRsp->rsp2 = IN4500(ai, RESP2); - if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET) - airo_print_err(ai->dev->name, - "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x", - pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1, - pRsp->rsp2); - - // clear stuck command busy if necessary - if (IN4500(ai, COMMAND) & COMMAND_BUSY) { - OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); - } - // acknowledge processing the status/response - OUT4500(ai, EVACK, EV_CMD); - - return SUCCESS; -} - -/* Sets up the bap to start exchange data. whichbap should - * be one of the BAP0 or BAP1 defines. Locks should be held before - * calling! */ -static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap ) -{ - int timeout = 50; - int max_tries = 3; - - OUT4500(ai, SELECT0+whichbap, rid); - OUT4500(ai, OFFSET0+whichbap, offset); - while (1) { - int status = IN4500(ai, OFFSET0+whichbap); - if (status & BAP_BUSY) { - /* This isn't really a timeout, but its kinda - close */ - if (timeout--) { - continue; - } - } else if ( status & BAP_ERR ) { - /* invalid rid or offset */ - airo_print_err(ai->dev->name, "BAP error %x %d", - status, whichbap ); - return ERROR; - } else if (status & BAP_DONE) { // success - return SUCCESS; - } - if ( !(max_tries--) ) { - airo_print_err(ai->dev->name, - "BAP setup error too many retries\n"); - return ERROR; - } - // -- PC4500 missed it, try again - OUT4500(ai, SELECT0+whichbap, rid); - OUT4500(ai, OFFSET0+whichbap, offset); - timeout = 50; - } -} - -/* should only be called by aux_bap_read. This aux function and the - following use concepts not documented in the developers guide. I - got them from a patch given to my by Aironet */ -static u16 aux_setup(struct airo_info *ai, u16 page, - u16 offset, u16 *len) -{ - u16 next; - - OUT4500(ai, AUXPAGE, page); - OUT4500(ai, AUXOFF, 0); - next = IN4500(ai, AUXDATA); - *len = IN4500(ai, AUXDATA)&0xff; - if (offset != 4) OUT4500(ai, AUXOFF, offset); - return next; -} - -/* requires call to bap_setup() first */ -static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst, - int bytelen, int whichbap) -{ - u16 len; - u16 page; - u16 offset; - u16 next; - int words; - int i; - unsigned long flags; - - spin_lock_irqsave(&ai->aux_lock, flags); - page = IN4500(ai, SWS0+whichbap); - offset = IN4500(ai, SWS2+whichbap); - next = aux_setup(ai, page, offset, &len); - words = (bytelen+1)>>1; - - for (i=0; i>1) < (words-i) ? (len>>1) : (words-i); - if ( !do8bitIO ) - insw( ai->dev->base_addr+DATA0+whichbap, - pu16Dst+i,count ); - else - insb( ai->dev->base_addr+DATA0+whichbap, - pu16Dst+i, count << 1 ); - i += count; - if (iaux_lock, flags); - return SUCCESS; -} - - -/* requires call to bap_setup() first */ -static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst, - int bytelen, int whichbap) -{ - bytelen = (bytelen + 1) & (~1); // round up to even value - if ( !do8bitIO ) - insw( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1 ); - else - insb( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen ); - return SUCCESS; -} - -/* requires call to bap_setup() first */ -static int bap_write(struct airo_info *ai, const __le16 *pu16Src, - int bytelen, int whichbap) -{ - bytelen = (bytelen + 1) & (~1); // round up to even value - if ( !do8bitIO ) - outsw( ai->dev->base_addr+DATA0+whichbap, - pu16Src, bytelen>>1 ); - else - outsb( ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen ); - return SUCCESS; -} - -static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd) -{ - Cmd cmd; /* for issuing commands */ - Resp rsp; /* response from commands */ - u16 status; - - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd = accmd; - cmd.parm0 = rid; - status = issuecommand(ai, &cmd, &rsp); - if (status != 0) return status; - if ( (rsp.status & 0x7F00) != 0) { - return (accmd << 8) + (rsp.rsp0 & 0xFF); - } - return 0; -} - -/* Note, that we are using BAP1 which is also used by transmit, so - * we must get a lock. */ -static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock) -{ - u16 status; - int rc = SUCCESS; - - if (lock) { - if (down_interruptible(&ai->sem)) - return ERROR; - } - if (test_bit(FLAG_MPI,&ai->flags)) { - Cmd cmd; - Resp rsp; - - memset(&cmd, 0, sizeof(cmd)); - memset(&rsp, 0, sizeof(rsp)); - ai->config_desc.rid_desc.valid = 1; - ai->config_desc.rid_desc.len = RIDSIZE; - ai->config_desc.rid_desc.rid = 0; - ai->config_desc.rid_desc.host_addr = ai->ridbus; - - cmd.cmd = CMD_ACCESS; - cmd.parm0 = rid; - - memcpy_toio(ai->config_desc.card_ram_off, - &ai->config_desc.rid_desc, sizeof(Rid)); - - rc = issuecommand(ai, &cmd, &rsp); - - if (rsp.status & 0x7f00) - rc = rsp.rsp0; - if (!rc) - memcpy(pBuf, ai->config_desc.virtual_host_addr, len); - goto done; - } else { - if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) { - rc = status; - goto done; - } - if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) { - rc = ERROR; - goto done; - } - // read the rid length field - bap_read(ai, pBuf, 2, BAP1); - // length for remaining part of rid - len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2; - - if ( len <= 2 ) { - airo_print_err(ai->dev->name, - "Rid %x has a length of %d which is too short", - (int)rid, (int)len ); - rc = ERROR; - goto done; - } - // read remainder of the rid - rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1); - } -done: - if (lock) - up(&ai->sem); - return rc; -} - -/* Note, that we are using BAP1 which is also used by transmit, so - * make sure this isn't called when a transmit is happening */ -static int PC4500_writerid(struct airo_info *ai, u16 rid, - const void *pBuf, int len, int lock) -{ - u16 status; - int rc = SUCCESS; - - *(__le16*)pBuf = cpu_to_le16((u16)len); - - if (lock) { - if (down_interruptible(&ai->sem)) - return ERROR; - } - if (test_bit(FLAG_MPI,&ai->flags)) { - Cmd cmd; - Resp rsp; - - if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid)) - airo_print_err(ai->dev->name, - "%s: MAC should be disabled (rid=%04x)", - __func__, rid); - memset(&cmd, 0, sizeof(cmd)); - memset(&rsp, 0, sizeof(rsp)); - - ai->config_desc.rid_desc.valid = 1; - ai->config_desc.rid_desc.len = *((u16 *)pBuf); - ai->config_desc.rid_desc.rid = 0; - - cmd.cmd = CMD_WRITERID; - cmd.parm0 = rid; - - memcpy_toio(ai->config_desc.card_ram_off, - &ai->config_desc.rid_desc, sizeof(Rid)); - - if (len < 4 || len > 2047) { - airo_print_err(ai->dev->name, "%s: len=%d", __func__, len); - rc = -1; - } else { - memcpy(ai->config_desc.virtual_host_addr, - pBuf, len); - - rc = issuecommand(ai, &cmd, &rsp); - if ((rc & 0xff00) != 0) { - airo_print_err(ai->dev->name, "%s: Write rid Error %d", - __func__, rc); - airo_print_err(ai->dev->name, "%s: Cmd=%04x", - __func__, cmd.cmd); - } - - if ((rsp.status & 0x7f00)) - rc = rsp.rsp0; - } - } else { - // --- first access so that we can write the rid data - if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) { - rc = status; - goto done; - } - // --- now write the rid data - if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) { - rc = ERROR; - goto done; - } - bap_write(ai, pBuf, len, BAP1); - // ---now commit the rid data - rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS); - } -done: - if (lock) - up(&ai->sem); - return rc; -} - -/* Allocates a FID to be used for transmitting packets. We only use - one for now. */ -static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw) -{ - unsigned int loop = 3000; - Cmd cmd; - Resp rsp; - u16 txFid; - __le16 txControl; - - cmd.cmd = CMD_ALLOCATETX; - cmd.parm0 = lenPayload; - if (down_interruptible(&ai->sem)) - return ERROR; - if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { - txFid = ERROR; - goto done; - } - if ( (rsp.status & 0xFF00) != 0) { - txFid = ERROR; - goto done; - } - /* wait for the allocate event/indication - * It makes me kind of nervous that this can just sit here and spin, - * but in practice it only loops like four times. */ - while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop); - if (!loop) { - txFid = ERROR; - goto done; - } - - // get the allocated fid and acknowledge - txFid = IN4500(ai, TXALLOCFID); - OUT4500(ai, EVACK, EV_ALLOC); - - /* The CARD is pretty cool since it converts the ethernet packet - * into 802.11. Also note that we don't release the FID since we - * will be using the same one over and over again. */ - /* We only have to setup the control once since we are not - * releasing the fid. */ - if (raw) - txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11 - | TXCTL_ETHERNET | TXCTL_NORELEASE); - else - txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3 - | TXCTL_ETHERNET | TXCTL_NORELEASE); - if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS) - txFid = ERROR; - else - bap_write(ai, &txControl, sizeof(txControl), BAP1); - -done: - up(&ai->sem); - - return txFid; -} - -/* In general BAP1 is dedicated to transmiting packets. However, - since we need a BAP when accessing RIDs, we also use BAP1 for that. - Make sure the BAP1 spinlock is held when this is called. */ -static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket) -{ - __le16 payloadLen; - Cmd cmd; - Resp rsp; - int miclen = 0; - u16 txFid = len; - MICBuffer pMic; - - len >>= 16; - - if (len <= ETH_ALEN * 2) { - airo_print_warn(ai->dev->name, "Short packet %d", len); - return ERROR; - } - len -= ETH_ALEN * 2; - - if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled && - (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) { - if (encapsulate(ai,(etherHead *)pPacket,&pMic,len) != SUCCESS) - return ERROR; - miclen = sizeof(pMic); - } - // packet is destination[6], source[6], payload[len-12] - // write the payload length and dst/src/payload - if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR; - /* The hardware addresses aren't counted as part of the payload, so - * we have to subtract the 12 bytes for the addresses off */ - payloadLen = cpu_to_le16(len + miclen); - bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1); - bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1); - if (miclen) - bap_write(ai, (__le16*)&pMic, miclen, BAP1); - bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1); - // issue the transmit command - memset( &cmd, 0, sizeof( cmd ) ); - cmd.cmd = CMD_TRANSMIT; - cmd.parm0 = txFid; - if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR; - if ( (rsp.status & 0xFF00) != 0) return ERROR; - return SUCCESS; -} - -static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket) -{ - __le16 fc, payloadLen; - Cmd cmd; - Resp rsp; - int hdrlen; - static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6}; - /* padding of header to full size + le16 gaplen (6) + gaplen bytes */ - u16 txFid = len; - len >>= 16; - - fc = *(__le16*)pPacket; - hdrlen = header_len(fc); - - if (len < hdrlen) { - airo_print_warn(ai->dev->name, "Short packet %d", len); - return ERROR; - } - - /* packet is 802.11 header + payload - * write the payload length and dst/src/payload */ - if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR; - /* The 802.11 header aren't counted as part of the payload, so - * we have to subtract the header bytes off */ - payloadLen = cpu_to_le16(len-hdrlen); - bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1); - if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR; - bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1); - bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1); - - bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1); - // issue the transmit command - memset( &cmd, 0, sizeof( cmd ) ); - cmd.cmd = CMD_TRANSMIT; - cmd.parm0 = txFid; - if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR; - if ( (rsp.status & 0xFF00) != 0) return ERROR; - return SUCCESS; -} - -/* - * This is the proc_fs routines. It is a bit messier than I would - * like! Feel free to clean it up! - */ - -static ssize_t proc_read( struct file *file, - char __user *buffer, - size_t len, - loff_t *offset); - -static ssize_t proc_write( struct file *file, - const char __user *buffer, - size_t len, - loff_t *offset ); -static int proc_close( struct inode *inode, struct file *file ); - -static int proc_stats_open( struct inode *inode, struct file *file ); -static int proc_statsdelta_open( struct inode *inode, struct file *file ); -static int proc_status_open( struct inode *inode, struct file *file ); -static int proc_SSID_open( struct inode *inode, struct file *file ); -static int proc_APList_open( struct inode *inode, struct file *file ); -static int proc_BSSList_open( struct inode *inode, struct file *file ); -static int proc_config_open( struct inode *inode, struct file *file ); -static int proc_wepkey_open( struct inode *inode, struct file *file ); - -static const struct file_operations proc_statsdelta_ops = { - .owner = THIS_MODULE, - .read = proc_read, - .open = proc_statsdelta_open, - .release = proc_close, - .llseek = default_llseek, -}; - -static const struct file_operations proc_stats_ops = { - .owner = THIS_MODULE, - .read = proc_read, - .open = proc_stats_open, - .release = proc_close, - .llseek = default_llseek, -}; - -static const struct file_operations proc_status_ops = { - .owner = THIS_MODULE, - .read = proc_read, - .open = proc_status_open, - .release = proc_close, - .llseek = default_llseek, -}; - -static const struct file_operations proc_SSID_ops = { - .owner = THIS_MODULE, - .read = proc_read, - .write = proc_write, - .open = proc_SSID_open, - .release = proc_close, - .llseek = default_llseek, -}; - -static const struct file_operations proc_BSSList_ops = { - .owner = THIS_MODULE, - .read = proc_read, - .write = proc_write, - .open = proc_BSSList_open, - .release = proc_close, - .llseek = default_llseek, -}; - -static const struct file_operations proc_APList_ops = { - .owner = THIS_MODULE, - .read = proc_read, - .write = proc_write, - .open = proc_APList_open, - .release = proc_close, - .llseek = default_llseek, -}; - -static const struct file_operations proc_config_ops = { - .owner = THIS_MODULE, - .read = proc_read, - .write = proc_write, - .open = proc_config_open, - .release = proc_close, - .llseek = default_llseek, -}; - -static const struct file_operations proc_wepkey_ops = { - .owner = THIS_MODULE, - .read = proc_read, - .write = proc_write, - .open = proc_wepkey_open, - .release = proc_close, - .llseek = default_llseek, -}; - -static struct proc_dir_entry *airo_entry; - -struct proc_data { - int release_buffer; - int readlen; - char *rbuffer; - int writelen; - int maxwritelen; - char *wbuffer; - void (*on_close) (struct inode *, struct file *); -}; - -static int setup_proc_entry( struct net_device *dev, - struct airo_info *apriv ) { - struct proc_dir_entry *entry; - - /* First setup the device directory */ - strcpy(apriv->proc_name,dev->name); - apriv->proc_entry = proc_mkdir_mode(apriv->proc_name, airo_perm, - airo_entry); - if (!apriv->proc_entry) - return -ENOMEM; - proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid); - - /* Setup the StatsDelta */ - entry = proc_create_data("StatsDelta", S_IRUGO & proc_perm, - apriv->proc_entry, &proc_statsdelta_ops, dev); - if (!entry) - goto fail; - proc_set_user(entry, proc_kuid, proc_kgid); - - /* Setup the Stats */ - entry = proc_create_data("Stats", S_IRUGO & proc_perm, - apriv->proc_entry, &proc_stats_ops, dev); - if (!entry) - goto fail; - proc_set_user(entry, proc_kuid, proc_kgid); - - /* Setup the Status */ - entry = proc_create_data("Status", S_IRUGO & proc_perm, - apriv->proc_entry, &proc_status_ops, dev); - if (!entry) - goto fail; - proc_set_user(entry, proc_kuid, proc_kgid); - - /* Setup the Config */ - entry = proc_create_data("Config", proc_perm, - apriv->proc_entry, &proc_config_ops, dev); - if (!entry) - goto fail; - proc_set_user(entry, proc_kuid, proc_kgid); - - /* Setup the SSID */ - entry = proc_create_data("SSID", proc_perm, - apriv->proc_entry, &proc_SSID_ops, dev); - if (!entry) - goto fail; - proc_set_user(entry, proc_kuid, proc_kgid); - - /* Setup the APList */ - entry = proc_create_data("APList", proc_perm, - apriv->proc_entry, &proc_APList_ops, dev); - if (!entry) - goto fail; - proc_set_user(entry, proc_kuid, proc_kgid); - - /* Setup the BSSList */ - entry = proc_create_data("BSSList", proc_perm, - apriv->proc_entry, &proc_BSSList_ops, dev); - if (!entry) - goto fail; - proc_set_user(entry, proc_kuid, proc_kgid); - - /* Setup the WepKey */ - entry = proc_create_data("WepKey", proc_perm, - apriv->proc_entry, &proc_wepkey_ops, dev); - if (!entry) - goto fail; - proc_set_user(entry, proc_kuid, proc_kgid); - return 0; - -fail: - remove_proc_subtree(apriv->proc_name, airo_entry); - return -ENOMEM; -} - -static int takedown_proc_entry( struct net_device *dev, - struct airo_info *apriv ) -{ - remove_proc_subtree(apriv->proc_name, airo_entry); - return 0; -} - -/* - * What we want from the proc_fs is to be able to efficiently read - * and write the configuration. To do this, we want to read the - * configuration when the file is opened and write it when the file is - * closed. So basically we allocate a read buffer at open and fill it - * with data, and allocate a write buffer and read it at close. - */ - -/* - * The read routine is generic, it relies on the preallocated rbuffer - * to supply the data. - */ -static ssize_t proc_read( struct file *file, - char __user *buffer, - size_t len, - loff_t *offset ) -{ - struct proc_data *priv = file->private_data; - - if (!priv->rbuffer) - return -EINVAL; - - return simple_read_from_buffer(buffer, len, offset, priv->rbuffer, - priv->readlen); -} - -/* - * The write routine is generic, it fills in a preallocated rbuffer - * to supply the data. - */ -static ssize_t proc_write( struct file *file, - const char __user *buffer, - size_t len, - loff_t *offset ) -{ - ssize_t ret; - struct proc_data *priv = file->private_data; - - if (!priv->wbuffer) - return -EINVAL; - - ret = simple_write_to_buffer(priv->wbuffer, priv->maxwritelen, offset, - buffer, len); - if (ret > 0) - priv->writelen = max_t(int, priv->writelen, *offset); - - return ret; -} - -static int proc_status_open(struct inode *inode, struct file *file) -{ - struct proc_data *data; - struct net_device *dev = PDE_DATA(inode); - struct airo_info *apriv = dev->ml_priv; - CapabilityRid cap_rid; - StatusRid status_rid; - u16 mode; - int i; - - if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) - return -ENOMEM; - data = file->private_data; - if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) { - kfree (file->private_data); - return -ENOMEM; - } - - readStatusRid(apriv, &status_rid, 1); - readCapabilityRid(apriv, &cap_rid, 1); - - mode = le16_to_cpu(status_rid.mode); - - i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n", - mode & 1 ? "CFG ": "", - mode & 2 ? "ACT ": "", - mode & 0x10 ? "SYN ": "", - mode & 0x20 ? "LNK ": "", - mode & 0x40 ? "LEAP ": "", - mode & 0x80 ? "PRIV ": "", - mode & 0x100 ? "KEY ": "", - mode & 0x200 ? "WEP ": "", - mode & 0x8000 ? "ERR ": ""); - sprintf( data->rbuffer+i, "Mode: %x\n" - "Signal Strength: %d\n" - "Signal Quality: %d\n" - "SSID: %-.*s\n" - "AP: %-.16s\n" - "Freq: %d\n" - "BitRate: %dmbs\n" - "Driver Version: %s\n" - "Device: %s\nManufacturer: %s\nFirmware Version: %s\n" - "Radio type: %x\nCountry: %x\nHardware Version: %x\n" - "Software Version: %x\nSoftware Subversion: %x\n" - "Boot block version: %x\n", - le16_to_cpu(status_rid.mode), - le16_to_cpu(status_rid.normalizedSignalStrength), - le16_to_cpu(status_rid.signalQuality), - le16_to_cpu(status_rid.SSIDlen), - status_rid.SSID, - status_rid.apName, - le16_to_cpu(status_rid.channel), - le16_to_cpu(status_rid.currentXmitRate) / 2, - version, - cap_rid.prodName, - cap_rid.manName, - cap_rid.prodVer, - le16_to_cpu(cap_rid.radioType), - le16_to_cpu(cap_rid.country), - le16_to_cpu(cap_rid.hardVer), - le16_to_cpu(cap_rid.softVer), - le16_to_cpu(cap_rid.softSubVer), - le16_to_cpu(cap_rid.bootBlockVer)); - data->readlen = strlen( data->rbuffer ); - return 0; -} - -static int proc_stats_rid_open(struct inode*, struct file*, u16); -static int proc_statsdelta_open( struct inode *inode, - struct file *file ) { - if (file->f_mode&FMODE_WRITE) { - return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR); - } - return proc_stats_rid_open(inode, file, RID_STATSDELTA); -} - -static int proc_stats_open( struct inode *inode, struct file *file ) { - return proc_stats_rid_open(inode, file, RID_STATS); -} - -static int proc_stats_rid_open( struct inode *inode, - struct file *file, - u16 rid ) -{ - struct proc_data *data; - struct net_device *dev = PDE_DATA(inode); - struct airo_info *apriv = dev->ml_priv; - StatsRid stats; - int i, j; - __le32 *vals = stats.vals; - int len; - - if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) - return -ENOMEM; - data = file->private_data; - if ((data->rbuffer = kmalloc( 4096, GFP_KERNEL )) == NULL) { - kfree (file->private_data); - return -ENOMEM; - } - - readStatsRid(apriv, &stats, rid, 1); - len = le16_to_cpu(stats.len); - - j = 0; - for(i=0; statsLabels[i]!=(char *)-1 && i*44096) { - airo_print_warn(apriv->dev->name, - "Potentially disastrous buffer overflow averted!"); - break; - } - j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i], - le32_to_cpu(vals[i])); - } - if (i*4 >= len) { - airo_print_warn(apriv->dev->name, "Got a short rid"); - } - data->readlen = j; - return 0; -} - -static int get_dec_u16( char *buffer, int *start, int limit ) { - u16 value; - int valid = 0; - for (value = 0; *start < limit && buffer[*start] >= '0' && - buffer[*start] <= '9'; (*start)++) { - valid = 1; - value *= 10; - value += buffer[*start] - '0'; - } - if ( !valid ) return -1; - return value; -} - -static int airo_config_commit(struct net_device *dev, - struct iw_request_info *info, void *zwrq, - char *extra); - -static inline int sniffing_mode(struct airo_info *ai) -{ - return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >= - le16_to_cpu(RXMODE_RFMON); -} - -static void proc_config_on_close(struct inode *inode, struct file *file) -{ - struct proc_data *data = file->private_data; - struct net_device *dev = PDE_DATA(inode); - struct airo_info *ai = dev->ml_priv; - char *line; - - if ( !data->writelen ) return; - - readConfigRid(ai, 1); - set_bit (FLAG_COMMIT, &ai->flags); - - line = data->wbuffer; - while( line[0] ) { -/*** Mode processing */ - if ( !strncmp( line, "Mode: ", 6 ) ) { - line += 6; - if (sniffing_mode(ai)) - set_bit (FLAG_RESET, &ai->flags); - ai->config.rmode &= ~RXMODE_FULL_MASK; - clear_bit (FLAG_802_11, &ai->flags); - ai->config.opmode &= ~MODE_CFG_MASK; - ai->config.scanMode = SCANMODE_ACTIVE; - if ( line[0] == 'a' ) { - ai->config.opmode |= MODE_STA_IBSS; - } else { - ai->config.opmode |= MODE_STA_ESS; - if ( line[0] == 'r' ) { - ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER; - ai->config.scanMode = SCANMODE_PASSIVE; - set_bit (FLAG_802_11, &ai->flags); - } else if ( line[0] == 'y' ) { - ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER; - ai->config.scanMode = SCANMODE_PASSIVE; - set_bit (FLAG_802_11, &ai->flags); - } else if ( line[0] == 'l' ) - ai->config.rmode |= RXMODE_LANMON; - } - set_bit (FLAG_COMMIT, &ai->flags); - } - -/*** Radio status */ - else if (!strncmp(line,"Radio: ", 7)) { - line += 7; - if (!strncmp(line,"off",3)) { - set_bit (FLAG_RADIO_OFF, &ai->flags); - } else { - clear_bit (FLAG_RADIO_OFF, &ai->flags); - } - } -/*** NodeName processing */ - else if ( !strncmp( line, "NodeName: ", 10 ) ) { - int j; - - line += 10; - memset( ai->config.nodeName, 0, 16 ); -/* Do the name, assume a space between the mode and node name */ - for( j = 0; j < 16 && line[j] != '\n'; j++ ) { - ai->config.nodeName[j] = line[j]; - } - set_bit (FLAG_COMMIT, &ai->flags); - } - -/*** PowerMode processing */ - else if ( !strncmp( line, "PowerMode: ", 11 ) ) { - line += 11; - if ( !strncmp( line, "PSPCAM", 6 ) ) { - ai->config.powerSaveMode = POWERSAVE_PSPCAM; - set_bit (FLAG_COMMIT, &ai->flags); - } else if ( !strncmp( line, "PSP", 3 ) ) { - ai->config.powerSaveMode = POWERSAVE_PSP; - set_bit (FLAG_COMMIT, &ai->flags); - } else { - ai->config.powerSaveMode = POWERSAVE_CAM; - set_bit (FLAG_COMMIT, &ai->flags); - } - } else if ( !strncmp( line, "DataRates: ", 11 ) ) { - int v, i = 0, k = 0; /* i is index into line, - k is index to rates */ - - line += 11; - while((v = get_dec_u16(line, &i, 3))!=-1) { - ai->config.rates[k++] = (u8)v; - line += i + 1; - i = 0; - } - set_bit (FLAG_COMMIT, &ai->flags); - } else if ( !strncmp( line, "Channel: ", 9 ) ) { - int v, i = 0; - line += 9; - v = get_dec_u16(line, &i, i+3); - if ( v != -1 ) { - ai->config.channelSet = cpu_to_le16(v); - set_bit (FLAG_COMMIT, &ai->flags); - } - } else if ( !strncmp( line, "XmitPower: ", 11 ) ) { - int v, i = 0; - line += 11; - v = get_dec_u16(line, &i, i+3); - if ( v != -1 ) { - ai->config.txPower = cpu_to_le16(v); - set_bit (FLAG_COMMIT, &ai->flags); - } - } else if ( !strncmp( line, "WEP: ", 5 ) ) { - line += 5; - switch( line[0] ) { - case 's': - set_auth_type(ai, AUTH_SHAREDKEY); - break; - case 'e': - set_auth_type(ai, AUTH_ENCRYPT); - break; - default: - set_auth_type(ai, AUTH_OPEN); - break; - } - set_bit (FLAG_COMMIT, &ai->flags); - } else if ( !strncmp( line, "LongRetryLimit: ", 16 ) ) { - int v, i = 0; - - line += 16; - v = get_dec_u16(line, &i, 3); - v = (v<0) ? 0 : ((v>255) ? 255 : v); - ai->config.longRetryLimit = cpu_to_le16(v); - set_bit (FLAG_COMMIT, &ai->flags); - } else if ( !strncmp( line, "ShortRetryLimit: ", 17 ) ) { - int v, i = 0; - - line += 17; - v = get_dec_u16(line, &i, 3); - v = (v<0) ? 0 : ((v>255) ? 255 : v); - ai->config.shortRetryLimit = cpu_to_le16(v); - set_bit (FLAG_COMMIT, &ai->flags); - } else if ( !strncmp( line, "RTSThreshold: ", 14 ) ) { - int v, i = 0; - - line += 14; - v = get_dec_u16(line, &i, 4); - v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v); - ai->config.rtsThres = cpu_to_le16(v); - set_bit (FLAG_COMMIT, &ai->flags); - } else if ( !strncmp( line, "TXMSDULifetime: ", 16 ) ) { - int v, i = 0; - - line += 16; - v = get_dec_u16(line, &i, 5); - v = (v<0) ? 0 : v; - ai->config.txLifetime = cpu_to_le16(v); - set_bit (FLAG_COMMIT, &ai->flags); - } else if ( !strncmp( line, "RXMSDULifetime: ", 16 ) ) { - int v, i = 0; - - line += 16; - v = get_dec_u16(line, &i, 5); - v = (v<0) ? 0 : v; - ai->config.rxLifetime = cpu_to_le16(v); - set_bit (FLAG_COMMIT, &ai->flags); - } else if ( !strncmp( line, "TXDiversity: ", 13 ) ) { - ai->config.txDiversity = - (line[13]=='l') ? 1 : - ((line[13]=='r')? 2: 3); - set_bit (FLAG_COMMIT, &ai->flags); - } else if ( !strncmp( line, "RXDiversity: ", 13 ) ) { - ai->config.rxDiversity = - (line[13]=='l') ? 1 : - ((line[13]=='r')? 2: 3); - set_bit (FLAG_COMMIT, &ai->flags); - } else if ( !strncmp( line, "FragThreshold: ", 15 ) ) { - int v, i = 0; - - line += 15; - v = get_dec_u16(line, &i, 4); - v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v); - v = v & 0xfffe; /* Make sure its even */ - ai->config.fragThresh = cpu_to_le16(v); - set_bit (FLAG_COMMIT, &ai->flags); - } else if (!strncmp(line, "Modulation: ", 12)) { - line += 12; - switch(*line) { - case 'd': ai->config.modulation=MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break; - case 'c': ai->config.modulation=MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break; - case 'm': ai->config.modulation=MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break; - default: airo_print_warn(ai->dev->name, "Unknown modulation"); - } - } else if (!strncmp(line, "Preamble: ", 10)) { - line += 10; - switch(*line) { - case 'a': ai->config.preamble=PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break; - case 'l': ai->config.preamble=PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break; - case 's': ai->config.preamble=PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break; - default: airo_print_warn(ai->dev->name, "Unknown preamble"); - } - } else { - airo_print_warn(ai->dev->name, "Couldn't figure out %s", line); - } - while( line[0] && line[0] != '\n' ) line++; - if ( line[0] ) line++; - } - airo_config_commit(dev, NULL, NULL, NULL); -} - -static const char *get_rmode(__le16 mode) -{ - switch(mode & RXMODE_MASK) { - case RXMODE_RFMON: return "rfmon"; - case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon"; - case RXMODE_LANMON: return "lanmon"; - } - return "ESS"; -} - -static int proc_config_open(struct inode *inode, struct file *file) -{ - struct proc_data *data; - struct net_device *dev = PDE_DATA(inode); - struct airo_info *ai = dev->ml_priv; - int i; - __le16 mode; - - if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) - return -ENOMEM; - data = file->private_data; - if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) { - kfree (file->private_data); - return -ENOMEM; - } - if ((data->wbuffer = kzalloc( 2048, GFP_KERNEL )) == NULL) { - kfree (data->rbuffer); - kfree (file->private_data); - return -ENOMEM; - } - data->maxwritelen = 2048; - data->on_close = proc_config_on_close; - - readConfigRid(ai, 1); - - mode = ai->config.opmode & MODE_CFG_MASK; - i = sprintf( data->rbuffer, - "Mode: %s\n" - "Radio: %s\n" - "NodeName: %-16s\n" - "PowerMode: %s\n" - "DataRates: %d %d %d %d %d %d %d %d\n" - "Channel: %d\n" - "XmitPower: %d\n", - mode == MODE_STA_IBSS ? "adhoc" : - mode == MODE_STA_ESS ? get_rmode(ai->config.rmode): - mode == MODE_AP ? "AP" : - mode == MODE_AP_RPTR ? "AP RPTR" : "Error", - test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on", - ai->config.nodeName, - ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" : - ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" : - ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" : - "Error", - (int)ai->config.rates[0], - (int)ai->config.rates[1], - (int)ai->config.rates[2], - (int)ai->config.rates[3], - (int)ai->config.rates[4], - (int)ai->config.rates[5], - (int)ai->config.rates[6], - (int)ai->config.rates[7], - le16_to_cpu(ai->config.channelSet), - le16_to_cpu(ai->config.txPower) - ); - sprintf( data->rbuffer + i, - "LongRetryLimit: %d\n" - "ShortRetryLimit: %d\n" - "RTSThreshold: %d\n" - "TXMSDULifetime: %d\n" - "RXMSDULifetime: %d\n" - "TXDiversity: %s\n" - "RXDiversity: %s\n" - "FragThreshold: %d\n" - "WEP: %s\n" - "Modulation: %s\n" - "Preamble: %s\n", - le16_to_cpu(ai->config.longRetryLimit), - le16_to_cpu(ai->config.shortRetryLimit), - le16_to_cpu(ai->config.rtsThres), - le16_to_cpu(ai->config.txLifetime), - le16_to_cpu(ai->config.rxLifetime), - ai->config.txDiversity == 1 ? "left" : - ai->config.txDiversity == 2 ? "right" : "both", - ai->config.rxDiversity == 1 ? "left" : - ai->config.rxDiversity == 2 ? "right" : "both", - le16_to_cpu(ai->config.fragThresh), - ai->config.authType == AUTH_ENCRYPT ? "encrypt" : - ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open", - ai->config.modulation == MOD_DEFAULT ? "default" : - ai->config.modulation == MOD_CCK ? "cck" : - ai->config.modulation == MOD_MOK ? "mok" : "error", - ai->config.preamble == PREAMBLE_AUTO ? "auto" : - ai->config.preamble == PREAMBLE_LONG ? "long" : - ai->config.preamble == PREAMBLE_SHORT ? "short" : "error" - ); - data->readlen = strlen( data->rbuffer ); - return 0; -} - -static void proc_SSID_on_close(struct inode *inode, struct file *file) -{ - struct proc_data *data = file->private_data; - struct net_device *dev = PDE_DATA(inode); - struct airo_info *ai = dev->ml_priv; - SsidRid SSID_rid; - int i; - char *p = data->wbuffer; - char *end = p + data->writelen; - - if (!data->writelen) - return; - - *end = '\n'; /* sentinel; we have space for it */ - - memset(&SSID_rid, 0, sizeof(SSID_rid)); - - for (i = 0; i < 3 && p < end; i++) { - int j = 0; - /* copy up to 32 characters from this line */ - while (*p != '\n' && j < 32) - SSID_rid.ssids[i].ssid[j++] = *p++; - if (j == 0) - break; - SSID_rid.ssids[i].len = cpu_to_le16(j); - /* skip to the beginning of the next line */ - while (*p++ != '\n') - ; - } - if (i) - SSID_rid.len = cpu_to_le16(sizeof(SSID_rid)); - disable_MAC(ai, 1); - writeSsidRid(ai, &SSID_rid, 1); - enable_MAC(ai, 1); -} - -static void proc_APList_on_close( struct inode *inode, struct file *file ) { - struct proc_data *data = file->private_data; - struct net_device *dev = PDE_DATA(inode); - struct airo_info *ai = dev->ml_priv; - APListRid *APList_rid = &ai->APList; - int i; - - if ( !data->writelen ) return; - - memset(APList_rid, 0, sizeof(*APList_rid)); - APList_rid->len = cpu_to_le16(sizeof(*APList_rid)); - - for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) { - int j; - for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) { - switch(j%3) { - case 0: - APList_rid->ap[i][j/3]= - hex_to_bin(data->wbuffer[j+i*6*3])<<4; - break; - case 1: - APList_rid->ap[i][j/3]|= - hex_to_bin(data->wbuffer[j+i*6*3]); - break; - } - } - } - disable_MAC(ai, 1); - writeAPListRid(ai, APList_rid, 1); - enable_MAC(ai, 1); -} - -/* This function wraps PC4500_writerid with a MAC disable */ -static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data, - int len, int dummy ) { - int rc; - - disable_MAC(ai, 1); - rc = PC4500_writerid(ai, rid, rid_data, len, 1); - enable_MAC(ai, 1); - return rc; -} - -/* Returns the WEP key at the specified index, or -1 if that key does - * not exist. The buffer is assumed to be at least 16 bytes in length. - */ -static int get_wep_key(struct airo_info *ai, u16 index, char *buf, u16 buflen) -{ - WepKeyRid wkr; - int rc; - __le16 lastindex; - - rc = readWepKeyRid(ai, &wkr, 1, 1); - if (rc != SUCCESS) - return -1; - do { - lastindex = wkr.kindex; - if (le16_to_cpu(wkr.kindex) == index) { - int klen = min_t(int, buflen, le16_to_cpu(wkr.klen)); - memcpy(buf, wkr.key, klen); - return klen; - } - rc = readWepKeyRid(ai, &wkr, 0, 1); - if (rc != SUCCESS) - return -1; - } while (lastindex != wkr.kindex); - return -1; -} - -static int get_wep_tx_idx(struct airo_info *ai) -{ - WepKeyRid wkr; - int rc; - __le16 lastindex; - - rc = readWepKeyRid(ai, &wkr, 1, 1); - if (rc != SUCCESS) - return -1; - do { - lastindex = wkr.kindex; - if (wkr.kindex == cpu_to_le16(0xffff)) - return wkr.mac[0]; - rc = readWepKeyRid(ai, &wkr, 0, 1); - if (rc != SUCCESS) - return -1; - } while (lastindex != wkr.kindex); - return -1; -} - -static int set_wep_key(struct airo_info *ai, u16 index, const char *key, - u16 keylen, int perm, int lock) -{ - static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 }; - WepKeyRid wkr; - int rc; - - if (WARN_ON(keylen == 0)) - return -1; - - memset(&wkr, 0, sizeof(wkr)); - wkr.len = cpu_to_le16(sizeof(wkr)); - wkr.kindex = cpu_to_le16(index); - wkr.klen = cpu_to_le16(keylen); - memcpy(wkr.key, key, keylen); - memcpy(wkr.mac, macaddr, ETH_ALEN); - - if (perm) disable_MAC(ai, lock); - rc = writeWepKeyRid(ai, &wkr, perm, lock); - if (perm) enable_MAC(ai, lock); - return rc; -} - -static int set_wep_tx_idx(struct airo_info *ai, u16 index, int perm, int lock) -{ - WepKeyRid wkr; - int rc; - - memset(&wkr, 0, sizeof(wkr)); - wkr.len = cpu_to_le16(sizeof(wkr)); - wkr.kindex = cpu_to_le16(0xffff); - wkr.mac[0] = (char)index; - - if (perm) { - ai->defindex = (char)index; - disable_MAC(ai, lock); - } - - rc = writeWepKeyRid(ai, &wkr, perm, lock); - - if (perm) - enable_MAC(ai, lock); - return rc; -} - -static void proc_wepkey_on_close( struct inode *inode, struct file *file ) { - struct proc_data *data; - struct net_device *dev = PDE_DATA(inode); - struct airo_info *ai = dev->ml_priv; - int i, rc; - char key[16]; - u16 index = 0; - int j = 0; - - memset(key, 0, sizeof(key)); - - data = file->private_data; - if ( !data->writelen ) return; - - if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' && - (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) { - index = data->wbuffer[0] - '0'; - if (data->wbuffer[1] == '\n') { - rc = set_wep_tx_idx(ai, index, 1, 1); - if (rc < 0) { - airo_print_err(ai->dev->name, "failed to set " - "WEP transmit index to %d: %d.", - index, rc); - } - return; - } - j = 2; - } else { - airo_print_err(ai->dev->name, "WepKey passed invalid key index"); - return; - } - - for( i = 0; i < 16*3 && data->wbuffer[i+j]; i++ ) { - switch(i%3) { - case 0: - key[i/3] = hex_to_bin(data->wbuffer[i+j])<<4; - break; - case 1: - key[i/3] |= hex_to_bin(data->wbuffer[i+j]); - break; - } - } - - rc = set_wep_key(ai, index, key, i/3, 1, 1); - if (rc < 0) { - airo_print_err(ai->dev->name, "failed to set WEP key at index " - "%d: %d.", index, rc); - } -} - -static int proc_wepkey_open( struct inode *inode, struct file *file ) -{ - struct proc_data *data; - struct net_device *dev = PDE_DATA(inode); - struct airo_info *ai = dev->ml_priv; - char *ptr; - WepKeyRid wkr; - __le16 lastindex; - int j=0; - int rc; - - if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) - return -ENOMEM; - memset(&wkr, 0, sizeof(wkr)); - data = file->private_data; - if ((data->rbuffer = kzalloc( 180, GFP_KERNEL )) == NULL) { - kfree (file->private_data); - return -ENOMEM; - } - data->writelen = 0; - data->maxwritelen = 80; - if ((data->wbuffer = kzalloc( 80, GFP_KERNEL )) == NULL) { - kfree (data->rbuffer); - kfree (file->private_data); - return -ENOMEM; - } - data->on_close = proc_wepkey_on_close; - - ptr = data->rbuffer; - strcpy(ptr, "No wep keys\n"); - rc = readWepKeyRid(ai, &wkr, 1, 1); - if (rc == SUCCESS) do { - lastindex = wkr.kindex; - if (wkr.kindex == cpu_to_le16(0xffff)) { - j += sprintf(ptr+j, "Tx key = %d\n", - (int)wkr.mac[0]); - } else { - j += sprintf(ptr+j, "Key %d set with length = %d\n", - le16_to_cpu(wkr.kindex), - le16_to_cpu(wkr.klen)); - } - readWepKeyRid(ai, &wkr, 0, 1); - } while((lastindex != wkr.kindex) && (j < 180-30)); - - data->readlen = strlen( data->rbuffer ); - return 0; -} - -static int proc_SSID_open(struct inode *inode, struct file *file) -{ - struct proc_data *data; - struct net_device *dev = PDE_DATA(inode); - struct airo_info *ai = dev->ml_priv; - int i; - char *ptr; - SsidRid SSID_rid; - - if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) - return -ENOMEM; - data = file->private_data; - if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) { - kfree (file->private_data); - return -ENOMEM; - } - data->writelen = 0; - data->maxwritelen = 33*3; - /* allocate maxwritelen + 1; we'll want a sentinel */ - if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) { - kfree (data->rbuffer); - kfree (file->private_data); - return -ENOMEM; - } - data->on_close = proc_SSID_on_close; - - readSsidRid(ai, &SSID_rid); - ptr = data->rbuffer; - for (i = 0; i < 3; i++) { - int j; - size_t len = le16_to_cpu(SSID_rid.ssids[i].len); - if (!len) - break; - if (len > 32) - len = 32; - for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++) - *ptr++ = SSID_rid.ssids[i].ssid[j]; - *ptr++ = '\n'; - } - *ptr = '\0'; - data->readlen = strlen( data->rbuffer ); - return 0; -} - -static int proc_APList_open( struct inode *inode, struct file *file ) { - struct proc_data *data; - struct net_device *dev = PDE_DATA(inode); - struct airo_info *ai = dev->ml_priv; - int i; - char *ptr; - APListRid *APList_rid = &ai->APList; - - if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) - return -ENOMEM; - data = file->private_data; - if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) { - kfree (file->private_data); - return -ENOMEM; - } - data->writelen = 0; - data->maxwritelen = 4*6*3; - if ((data->wbuffer = kzalloc( data->maxwritelen, GFP_KERNEL )) == NULL) { - kfree (data->rbuffer); - kfree (file->private_data); - return -ENOMEM; - } - data->on_close = proc_APList_on_close; - - ptr = data->rbuffer; - for( i = 0; i < 4; i++ ) { -// We end when we find a zero MAC - if ( !*(int*)APList_rid->ap[i] && - !*(int*)&APList_rid->ap[i][2]) break; - ptr += sprintf(ptr, "%pM\n", APList_rid->ap[i]); - } - if (i==0) ptr += sprintf(ptr, "Not using specific APs\n"); - - *ptr = '\0'; - data->readlen = strlen( data->rbuffer ); - return 0; -} - -static int proc_BSSList_open( struct inode *inode, struct file *file ) { - struct proc_data *data; - struct net_device *dev = PDE_DATA(inode); - struct airo_info *ai = dev->ml_priv; - char *ptr; - BSSListRid BSSList_rid; - int rc; - /* If doLoseSync is not 1, we won't do a Lose Sync */ - int doLoseSync = -1; - - if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) - return -ENOMEM; - data = file->private_data; - if ((data->rbuffer = kmalloc( 1024, GFP_KERNEL )) == NULL) { - kfree (file->private_data); - return -ENOMEM; - } - data->writelen = 0; - data->maxwritelen = 0; - data->wbuffer = NULL; - data->on_close = NULL; - - if (file->f_mode & FMODE_WRITE) { - if (!(file->f_mode & FMODE_READ)) { - Cmd cmd; - Resp rsp; - - if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN; - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd=CMD_LISTBSS; - if (down_interruptible(&ai->sem)) - return -ERESTARTSYS; - issuecommand(ai, &cmd, &rsp); - up(&ai->sem); - data->readlen = 0; - return 0; - } - doLoseSync = 1; - } - ptr = data->rbuffer; - /* There is a race condition here if there are concurrent opens. - Since it is a rare condition, we'll just live with it, otherwise - we have to add a spin lock... */ - rc = readBSSListRid(ai, doLoseSync, &BSSList_rid); - while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) { - ptr += sprintf(ptr, "%pM %*s rssi = %d", - BSSList_rid.bssid, - (int)BSSList_rid.ssidLen, - BSSList_rid.ssid, - le16_to_cpu(BSSList_rid.dBm)); - ptr += sprintf(ptr, " channel = %d %s %s %s %s\n", - le16_to_cpu(BSSList_rid.dsChannel), - BSSList_rid.cap & CAP_ESS ? "ESS" : "", - BSSList_rid.cap & CAP_IBSS ? "adhoc" : "", - BSSList_rid.cap & CAP_PRIVACY ? "wep" : "", - BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : ""); - rc = readBSSListRid(ai, 0, &BSSList_rid); - } - *ptr = '\0'; - data->readlen = strlen( data->rbuffer ); - return 0; -} - -static int proc_close( struct inode *inode, struct file *file ) -{ - struct proc_data *data = file->private_data; - - if (data->on_close != NULL) - data->on_close(inode, file); - kfree(data->rbuffer); - kfree(data->wbuffer); - kfree(data); - return 0; -} - -/* Since the card doesn't automatically switch to the right WEP mode, - we will make it do it. If the card isn't associated, every secs we - will switch WEP modes to see if that will help. If the card is - associated we will check every minute to see if anything has - changed. */ -static void timer_func( struct net_device *dev ) { - struct airo_info *apriv = dev->ml_priv; - -/* We don't have a link so try changing the authtype */ - readConfigRid(apriv, 0); - disable_MAC(apriv, 0); - switch(apriv->config.authType) { - case AUTH_ENCRYPT: -/* So drop to OPEN */ - apriv->config.authType = AUTH_OPEN; - break; - case AUTH_SHAREDKEY: - if (apriv->keyindex < auto_wep) { - set_wep_tx_idx(apriv, apriv->keyindex, 0, 0); - apriv->config.authType = AUTH_SHAREDKEY; - apriv->keyindex++; - } else { - /* Drop to ENCRYPT */ - apriv->keyindex = 0; - set_wep_tx_idx(apriv, apriv->defindex, 0, 0); - apriv->config.authType = AUTH_ENCRYPT; - } - break; - default: /* We'll escalate to SHAREDKEY */ - apriv->config.authType = AUTH_SHAREDKEY; - } - set_bit (FLAG_COMMIT, &apriv->flags); - writeConfigRid(apriv, 0); - enable_MAC(apriv, 0); - up(&apriv->sem); - -/* Schedule check to see if the change worked */ - clear_bit(JOB_AUTOWEP, &apriv->jobs); - apriv->expires = RUN_AT(HZ*3); -} - -#ifdef CONFIG_PCI -static int airo_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *pent) -{ - struct net_device *dev; - - if (pci_enable_device(pdev)) - return -ENODEV; - pci_set_master(pdev); - - if (pdev->device == 0x5000 || pdev->device == 0xa504) - dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev); - else - dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev); - if (!dev) { - pci_disable_device(pdev); - return -ENODEV; - } - - pci_set_drvdata(pdev, dev); - return 0; -} - -static void airo_pci_remove(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - - airo_print_info(dev->name, "Unregistering..."); - stop_airo_card(dev, 1); - pci_disable_device(pdev); -} - -static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct airo_info *ai = dev->ml_priv; - Cmd cmd; - Resp rsp; - - if (!ai->SSID) - ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL); - if (!ai->SSID) - return -ENOMEM; - readSsidRid(ai, ai->SSID); - memset(&cmd, 0, sizeof(cmd)); - /* the lock will be released at the end of the resume callback */ - if (down_interruptible(&ai->sem)) - return -EAGAIN; - disable_MAC(ai, 0); - netif_device_detach(dev); - ai->power = state; - cmd.cmd = HOSTSLEEP; - issuecommand(ai, &cmd, &rsp); - - pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); - pci_save_state(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; -} - -static int airo_pci_resume(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct airo_info *ai = dev->ml_priv; - pci_power_t prev_state = pdev->current_state; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - pci_enable_wake(pdev, PCI_D0, 0); - - if (prev_state != PCI_D1) { - reset_card(dev, 0); - mpi_init_descriptors(ai); - setup_card(ai, dev->dev_addr, 0); - clear_bit(FLAG_RADIO_OFF, &ai->flags); - clear_bit(FLAG_PENDING_XMIT, &ai->flags); - } else { - OUT4500(ai, EVACK, EV_AWAKEN); - OUT4500(ai, EVACK, EV_AWAKEN); - msleep(100); - } - - set_bit(FLAG_COMMIT, &ai->flags); - disable_MAC(ai, 0); - msleep(200); - if (ai->SSID) { - writeSsidRid(ai, ai->SSID, 0); - kfree(ai->SSID); - ai->SSID = NULL; - } - writeAPListRid(ai, &ai->APList, 0); - writeConfigRid(ai, 0); - enable_MAC(ai, 0); - ai->power = PMSG_ON; - netif_device_attach(dev); - netif_wake_queue(dev); - enable_interrupts(ai); - up(&ai->sem); - return 0; -} -#endif - -static int __init airo_init_module( void ) -{ - int i; - - proc_kuid = make_kuid(&init_user_ns, proc_uid); - proc_kgid = make_kgid(&init_user_ns, proc_gid); - if (!uid_valid(proc_kuid) || !gid_valid(proc_kgid)) - return -EINVAL; - - airo_entry = proc_mkdir_mode("driver/aironet", airo_perm, NULL); - - if (airo_entry) - proc_set_user(airo_entry, proc_kuid, proc_kgid); - - for (i = 0; i < 4 && io[i] && irq[i]; i++) { - airo_print_info("", "Trying to configure ISA adapter at irq=%d " - "io=0x%x", irq[i], io[i] ); - if (init_airo_card( irq[i], io[i], 0, NULL )) - /* do nothing */ ; - } - -#ifdef CONFIG_PCI - airo_print_info("", "Probing for PCI adapters"); - i = pci_register_driver(&airo_driver); - airo_print_info("", "Finished probing for PCI adapters"); - - if (i) { - remove_proc_entry("driver/aironet", NULL); - return i; - } -#endif - - /* Always exit with success, as we are a library module - * as well as a driver module - */ - return 0; -} - -static void __exit airo_cleanup_module( void ) -{ - struct airo_info *ai; - while(!list_empty(&airo_devices)) { - ai = list_entry(airo_devices.next, struct airo_info, dev_list); - airo_print_info(ai->dev->name, "Unregistering..."); - stop_airo_card(ai->dev, 1); - } -#ifdef CONFIG_PCI - pci_unregister_driver(&airo_driver); -#endif - remove_proc_entry("driver/aironet", NULL); -} - -/* - * Initial Wireless Extension code for Aironet driver by : - * Jean Tourrilhes - HPL - 17 November 00 - * Conversion to new driver API by : - * Jean Tourrilhes - HPL - 26 March 02 - * Javier also did a good amount of work here, adding some new extensions - * and fixing my code. Let's just say that without him this code just - * would not work at all... - Jean II - */ - -static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi) -{ - if (!rssi_rid) - return 0; - - return (0x100 - rssi_rid[rssi].rssidBm); -} - -static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm) -{ - int i; - - if (!rssi_rid) - return 0; - - for (i = 0; i < 256; i++) - if (rssi_rid[i].rssidBm == dbm) - return rssi_rid[i].rssipct; - - return 0; -} - - -static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid) -{ - int quality = 0; - u16 sq; - - if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f)) - return 0; - - if (!(cap_rid->hardCap & cpu_to_le16(8))) - return 0; - - sq = le16_to_cpu(status_rid->signalQuality); - if (memcmp(cap_rid->prodName, "350", 3)) - if (sq > 0x20) - quality = 0; - else - quality = 0x20 - sq; - else - if (sq > 0xb0) - quality = 0; - else if (sq < 0x10) - quality = 0xa0; - else - quality = 0xb0 - sq; - return quality; -} - -#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0) -#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50); - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get protocol name - */ -static int airo_get_name(struct net_device *dev, - struct iw_request_info *info, - char *cwrq, - char *extra) -{ - strcpy(cwrq, "IEEE 802.11-DS"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set frequency - */ -static int airo_set_freq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *fwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - int rc = -EINPROGRESS; /* Call commit handler */ - - /* If setting by frequency, convert to a channel */ - if(fwrq->e == 1) { - int f = fwrq->m / 100000; - - /* Hack to fall through... */ - fwrq->e = 0; - fwrq->m = ieee80211_frequency_to_channel(f); - } - /* Setting by channel number */ - if((fwrq->m > 1000) || (fwrq->e > 0)) - rc = -EOPNOTSUPP; - else { - int channel = fwrq->m; - /* We should do a better check than that, - * based on the card capability !!! */ - if((channel < 1) || (channel > 14)) { - airo_print_dbg(dev->name, "New channel value of %d is invalid!", - fwrq->m); - rc = -EINVAL; - } else { - readConfigRid(local, 1); - /* Yes ! We can set it !!! */ - local->config.channelSet = cpu_to_le16(channel); - set_bit (FLAG_COMMIT, &local->flags); - } - } - return rc; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get frequency - */ -static int airo_get_freq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *fwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - StatusRid status_rid; /* Card status info */ - int ch; - - readConfigRid(local, 1); - if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS) - status_rid.channel = local->config.channelSet; - else - readStatusRid(local, &status_rid, 1); - - ch = le16_to_cpu(status_rid.channel); - if((ch > 0) && (ch < 15)) { - fwrq->m = 100000 * - ieee80211_channel_to_frequency(ch, IEEE80211_BAND_2GHZ); - fwrq->e = 1; - } else { - fwrq->m = ch; - fwrq->e = 0; - } - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set ESSID - */ -static int airo_set_essid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - SsidRid SSID_rid; /* SSIDs */ - - /* Reload the list of current SSID */ - readSsidRid(local, &SSID_rid); - - /* Check if we asked for `any' */ - if (dwrq->flags == 0) { - /* Just send an empty SSID list */ - memset(&SSID_rid, 0, sizeof(SSID_rid)); - } else { - unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - - /* Check the size of the string */ - if (dwrq->length > IW_ESSID_MAX_SIZE) - return -E2BIG ; - - /* Check if index is valid */ - if (index >= ARRAY_SIZE(SSID_rid.ssids)) - return -EINVAL; - - /* Set the SSID */ - memset(SSID_rid.ssids[index].ssid, 0, - sizeof(SSID_rid.ssids[index].ssid)); - memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length); - SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length); - } - SSID_rid.len = cpu_to_le16(sizeof(SSID_rid)); - /* Write it to the card */ - disable_MAC(local, 1); - writeSsidRid(local, &SSID_rid, 1); - enable_MAC(local, 1); - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get ESSID - */ -static int airo_get_essid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - StatusRid status_rid; /* Card status info */ - - readStatusRid(local, &status_rid, 1); - - /* Note : if dwrq->flags != 0, we should - * get the relevant SSID from the SSID list... */ - - /* Get the current SSID */ - memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen)); - /* If none, we may want to get the one that was set */ - - /* Push it out ! */ - dwrq->length = le16_to_cpu(status_rid.SSIDlen); - dwrq->flags = 1; /* active */ - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set AP address - */ -static int airo_set_wap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *awrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - Cmd cmd; - Resp rsp; - APListRid *APList_rid = &local->APList; - - if (awrq->sa_family != ARPHRD_ETHER) - return -EINVAL; - else if (is_broadcast_ether_addr(awrq->sa_data) || - is_zero_ether_addr(awrq->sa_data)) { - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd=CMD_LOSE_SYNC; - if (down_interruptible(&local->sem)) - return -ERESTARTSYS; - issuecommand(local, &cmd, &rsp); - up(&local->sem); - } else { - memset(APList_rid, 0, sizeof(*APList_rid)); - APList_rid->len = cpu_to_le16(sizeof(*APList_rid)); - memcpy(APList_rid->ap[0], awrq->sa_data, ETH_ALEN); - disable_MAC(local, 1); - writeAPListRid(local, APList_rid, 1); - enable_MAC(local, 1); - } - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get AP address - */ -static int airo_get_wap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *awrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - StatusRid status_rid; /* Card status info */ - - readStatusRid(local, &status_rid, 1); - - /* Tentative. This seems to work, wow, I'm lucky !!! */ - memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN); - awrq->sa_family = ARPHRD_ETHER; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Nickname - */ -static int airo_set_nick(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - - /* Check the size of the string */ - if(dwrq->length > 16) { - return -E2BIG; - } - readConfigRid(local, 1); - memset(local->config.nodeName, 0, sizeof(local->config.nodeName)); - memcpy(local->config.nodeName, extra, dwrq->length); - set_bit (FLAG_COMMIT, &local->flags); - - return -EINPROGRESS; /* Call commit handler */ -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Nickname - */ -static int airo_get_nick(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - - readConfigRid(local, 1); - strncpy(extra, local->config.nodeName, 16); - extra[16] = '\0'; - dwrq->length = strlen(extra); - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Bit-Rate - */ -static int airo_set_rate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - CapabilityRid cap_rid; /* Card capability info */ - u8 brate = 0; - int i; - - /* First : get a valid bit rate value */ - readCapabilityRid(local, &cap_rid, 1); - - /* Which type of value ? */ - if((vwrq->value < 8) && (vwrq->value >= 0)) { - /* Setting by rate index */ - /* Find value in the magic rate table */ - brate = cap_rid.supportedRates[vwrq->value]; - } else { - /* Setting by frequency value */ - u8 normvalue = (u8) (vwrq->value/500000); - - /* Check if rate is valid */ - for(i = 0 ; i < 8 ; i++) { - if(normvalue == cap_rid.supportedRates[i]) { - brate = normvalue; - break; - } - } - } - /* -1 designed the max rate (mostly auto mode) */ - if(vwrq->value == -1) { - /* Get the highest available rate */ - for(i = 0 ; i < 8 ; i++) { - if(cap_rid.supportedRates[i] == 0) - break; - } - if(i != 0) - brate = cap_rid.supportedRates[i - 1]; - } - /* Check that it is valid */ - if(brate == 0) { - return -EINVAL; - } - - readConfigRid(local, 1); - /* Now, check if we want a fixed or auto value */ - if(vwrq->fixed == 0) { - /* Fill all the rates up to this max rate */ - memset(local->config.rates, 0, 8); - for(i = 0 ; i < 8 ; i++) { - local->config.rates[i] = cap_rid.supportedRates[i]; - if(local->config.rates[i] == brate) - break; - } - } else { - /* Fixed mode */ - /* One rate, fixed */ - memset(local->config.rates, 0, 8); - local->config.rates[0] = brate; - } - set_bit (FLAG_COMMIT, &local->flags); - - return -EINPROGRESS; /* Call commit handler */ -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Bit-Rate - */ -static int airo_get_rate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - StatusRid status_rid; /* Card status info */ - - readStatusRid(local, &status_rid, 1); - - vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000; - /* If more than one rate, set auto */ - readConfigRid(local, 1); - vwrq->fixed = (local->config.rates[1] == 0); - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set RTS threshold - */ -static int airo_set_rts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - int rthr = vwrq->value; - - if(vwrq->disabled) - rthr = AIRO_DEF_MTU; - if((rthr < 0) || (rthr > AIRO_DEF_MTU)) { - return -EINVAL; - } - readConfigRid(local, 1); - local->config.rtsThres = cpu_to_le16(rthr); - set_bit (FLAG_COMMIT, &local->flags); - - return -EINPROGRESS; /* Call commit handler */ -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get RTS threshold - */ -static int airo_get_rts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - - readConfigRid(local, 1); - vwrq->value = le16_to_cpu(local->config.rtsThres); - vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU); - vwrq->fixed = 1; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Fragmentation threshold - */ -static int airo_set_frag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - int fthr = vwrq->value; - - if(vwrq->disabled) - fthr = AIRO_DEF_MTU; - if((fthr < 256) || (fthr > AIRO_DEF_MTU)) { - return -EINVAL; - } - fthr &= ~0x1; /* Get an even value - is it really needed ??? */ - readConfigRid(local, 1); - local->config.fragThresh = cpu_to_le16(fthr); - set_bit (FLAG_COMMIT, &local->flags); - - return -EINPROGRESS; /* Call commit handler */ -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Fragmentation threshold - */ -static int airo_get_frag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - - readConfigRid(local, 1); - vwrq->value = le16_to_cpu(local->config.fragThresh); - vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU); - vwrq->fixed = 1; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Mode of Operation - */ -static int airo_set_mode(struct net_device *dev, - struct iw_request_info *info, - __u32 *uwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - int reset = 0; - - readConfigRid(local, 1); - if (sniffing_mode(local)) - reset = 1; - - switch(*uwrq) { - case IW_MODE_ADHOC: - local->config.opmode &= ~MODE_CFG_MASK; - local->config.opmode |= MODE_STA_IBSS; - local->config.rmode &= ~RXMODE_FULL_MASK; - local->config.scanMode = SCANMODE_ACTIVE; - clear_bit (FLAG_802_11, &local->flags); - break; - case IW_MODE_INFRA: - local->config.opmode &= ~MODE_CFG_MASK; - local->config.opmode |= MODE_STA_ESS; - local->config.rmode &= ~RXMODE_FULL_MASK; - local->config.scanMode = SCANMODE_ACTIVE; - clear_bit (FLAG_802_11, &local->flags); - break; - case IW_MODE_MASTER: - local->config.opmode &= ~MODE_CFG_MASK; - local->config.opmode |= MODE_AP; - local->config.rmode &= ~RXMODE_FULL_MASK; - local->config.scanMode = SCANMODE_ACTIVE; - clear_bit (FLAG_802_11, &local->flags); - break; - case IW_MODE_REPEAT: - local->config.opmode &= ~MODE_CFG_MASK; - local->config.opmode |= MODE_AP_RPTR; - local->config.rmode &= ~RXMODE_FULL_MASK; - local->config.scanMode = SCANMODE_ACTIVE; - clear_bit (FLAG_802_11, &local->flags); - break; - case IW_MODE_MONITOR: - local->config.opmode &= ~MODE_CFG_MASK; - local->config.opmode |= MODE_STA_ESS; - local->config.rmode &= ~RXMODE_FULL_MASK; - local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER; - local->config.scanMode = SCANMODE_PASSIVE; - set_bit (FLAG_802_11, &local->flags); - break; - default: - return -EINVAL; - } - if (reset) - set_bit (FLAG_RESET, &local->flags); - set_bit (FLAG_COMMIT, &local->flags); - - return -EINPROGRESS; /* Call commit handler */ -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Mode of Operation - */ -static int airo_get_mode(struct net_device *dev, - struct iw_request_info *info, - __u32 *uwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - - readConfigRid(local, 1); - /* If not managed, assume it's ad-hoc */ - switch (local->config.opmode & MODE_CFG_MASK) { - case MODE_STA_ESS: - *uwrq = IW_MODE_INFRA; - break; - case MODE_AP: - *uwrq = IW_MODE_MASTER; - break; - case MODE_AP_RPTR: - *uwrq = IW_MODE_REPEAT; - break; - default: - *uwrq = IW_MODE_ADHOC; - } - - return 0; -} - -static inline int valid_index(struct airo_info *ai, int index) -{ - return (index >= 0) && (index <= ai->max_wep_idx); -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Encryption Key - */ -static int airo_set_encode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1); - __le16 currentAuthType = local->config.authType; - int rc = 0; - - if (!local->wep_capable) - return -EOPNOTSUPP; - - readConfigRid(local, 1); - - /* Basic checking: do we have a key to set ? - * Note : with the new API, it's impossible to get a NULL pointer. - * Therefore, we need to check a key size == 0 instead. - * New version of iwconfig properly set the IW_ENCODE_NOKEY flag - * when no key is present (only change flags), but older versions - * don't do it. - Jean II */ - if (dwrq->length > 0) { - wep_key_t key; - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - int current_index; - - /* Check the size of the key */ - if (dwrq->length > MAX_KEY_SIZE) { - return -EINVAL; - } - - current_index = get_wep_tx_idx(local); - if (current_index < 0) - current_index = 0; - - /* Check the index (none -> use current) */ - if (!valid_index(local, index)) - index = current_index; - - /* Set the length */ - if (dwrq->length > MIN_KEY_SIZE) - key.len = MAX_KEY_SIZE; - else - key.len = MIN_KEY_SIZE; - /* Check if the key is not marked as invalid */ - if(!(dwrq->flags & IW_ENCODE_NOKEY)) { - /* Cleanup */ - memset(key.key, 0, MAX_KEY_SIZE); - /* Copy the key in the driver */ - memcpy(key.key, extra, dwrq->length); - /* Send the key to the card */ - rc = set_wep_key(local, index, key.key, key.len, perm, 1); - if (rc < 0) { - airo_print_err(local->dev->name, "failed to set" - " WEP key at index %d: %d.", - index, rc); - return rc; - } - } - /* WE specify that if a valid key is set, encryption - * should be enabled (user may turn it off later) - * This is also how "iwconfig ethX key on" works */ - if((index == current_index) && (key.len > 0) && - (local->config.authType == AUTH_OPEN)) - set_auth_type(local, AUTH_ENCRYPT); - } else { - /* Do we want to just set the transmit key index ? */ - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - if (valid_index(local, index)) { - rc = set_wep_tx_idx(local, index, perm, 1); - if (rc < 0) { - airo_print_err(local->dev->name, "failed to set" - " WEP transmit index to %d: %d.", - index, rc); - return rc; - } - } else { - /* Don't complain if only change the mode */ - if (!(dwrq->flags & IW_ENCODE_MODE)) - return -EINVAL; - } - } - /* Read the flags */ - if (dwrq->flags & IW_ENCODE_DISABLED) - set_auth_type(local, AUTH_OPEN); /* disable encryption */ - if(dwrq->flags & IW_ENCODE_RESTRICTED) - set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ - if (dwrq->flags & IW_ENCODE_OPEN) - set_auth_type(local, AUTH_ENCRYPT); /* Only Wep */ - /* Commit the changes to flags if needed */ - if (local->config.authType != currentAuthType) - set_bit (FLAG_COMMIT, &local->flags); - return -EINPROGRESS; /* Call commit handler */ -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Encryption Key - */ -static int airo_get_encode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - int wep_key_len; - u8 buf[16]; - - if (!local->wep_capable) - return -EOPNOTSUPP; - - readConfigRid(local, 1); - - /* Check encryption mode */ - switch(local->config.authType) { - case AUTH_ENCRYPT: - dwrq->flags = IW_ENCODE_OPEN; - break; - case AUTH_SHAREDKEY: - dwrq->flags = IW_ENCODE_RESTRICTED; - break; - default: - case AUTH_OPEN: - dwrq->flags = IW_ENCODE_DISABLED; - break; - } - /* We can't return the key, so set the proper flag and return zero */ - dwrq->flags |= IW_ENCODE_NOKEY; - memset(extra, 0, 16); - - /* Which key do we want ? -1 -> tx index */ - if (!valid_index(local, index)) { - index = get_wep_tx_idx(local); - if (index < 0) - index = 0; - } - dwrq->flags |= index + 1; - - /* Copy the key to the user buffer */ - wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf)); - if (wep_key_len < 0) { - dwrq->length = 0; - } else { - dwrq->length = wep_key_len; - memcpy(extra, buf, dwrq->length); - } - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set extended Encryption parameters - */ -static int airo_set_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int perm = ( encoding->flags & IW_ENCODE_TEMP ? 0 : 1 ); - __le16 currentAuthType = local->config.authType; - int idx, key_len, alg = ext->alg, set_key = 1, rc; - wep_key_t key; - - if (!local->wep_capable) - return -EOPNOTSUPP; - - readConfigRid(local, 1); - - /* Determine and validate the key index */ - idx = encoding->flags & IW_ENCODE_INDEX; - if (idx) { - if (!valid_index(local, idx - 1)) - return -EINVAL; - idx--; - } else { - idx = get_wep_tx_idx(local); - if (idx < 0) - idx = 0; - } - - if (encoding->flags & IW_ENCODE_DISABLED) - alg = IW_ENCODE_ALG_NONE; - - if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { - /* Only set transmit key index here, actual - * key is set below if needed. - */ - rc = set_wep_tx_idx(local, idx, perm, 1); - if (rc < 0) { - airo_print_err(local->dev->name, "failed to set " - "WEP transmit index to %d: %d.", - idx, rc); - return rc; - } - set_key = ext->key_len > 0 ? 1 : 0; - } - - if (set_key) { - /* Set the requested key first */ - memset(key.key, 0, MAX_KEY_SIZE); - switch (alg) { - case IW_ENCODE_ALG_NONE: - key.len = 0; - break; - case IW_ENCODE_ALG_WEP: - if (ext->key_len > MIN_KEY_SIZE) { - key.len = MAX_KEY_SIZE; - } else if (ext->key_len > 0) { - key.len = MIN_KEY_SIZE; - } else { - return -EINVAL; - } - key_len = min (ext->key_len, key.len); - memcpy(key.key, ext->key, key_len); - break; - default: - return -EINVAL; - } - if (key.len == 0) { - rc = set_wep_tx_idx(local, idx, perm, 1); - if (rc < 0) { - airo_print_err(local->dev->name, - "failed to set WEP transmit index to %d: %d.", - idx, rc); - return rc; - } - } else { - rc = set_wep_key(local, idx, key.key, key.len, perm, 1); - if (rc < 0) { - airo_print_err(local->dev->name, - "failed to set WEP key at index %d: %d.", - idx, rc); - return rc; - } - } - } - - /* Read the flags */ - if (encoding->flags & IW_ENCODE_DISABLED) - set_auth_type(local, AUTH_OPEN); /* disable encryption */ - if(encoding->flags & IW_ENCODE_RESTRICTED) - set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ - if (encoding->flags & IW_ENCODE_OPEN) - set_auth_type(local, AUTH_ENCRYPT); - /* Commit the changes to flags if needed */ - if (local->config.authType != currentAuthType) - set_bit (FLAG_COMMIT, &local->flags); - - return -EINPROGRESS; -} - - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get extended Encryption parameters - */ -static int airo_get_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int idx, max_key_len, wep_key_len; - u8 buf[16]; - - if (!local->wep_capable) - return -EOPNOTSUPP; - - readConfigRid(local, 1); - - max_key_len = encoding->length - sizeof(*ext); - if (max_key_len < 0) - return -EINVAL; - - idx = encoding->flags & IW_ENCODE_INDEX; - if (idx) { - if (!valid_index(local, idx - 1)) - return -EINVAL; - idx--; - } else { - idx = get_wep_tx_idx(local); - if (idx < 0) - idx = 0; - } - - encoding->flags = idx + 1; - memset(ext, 0, sizeof(*ext)); - - /* Check encryption mode */ - switch(local->config.authType) { - case AUTH_ENCRYPT: - encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED; - break; - case AUTH_SHAREDKEY: - encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED; - break; - default: - case AUTH_OPEN: - encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED; - break; - } - /* We can't return the key, so set the proper flag and return zero */ - encoding->flags |= IW_ENCODE_NOKEY; - memset(extra, 0, 16); - - /* Copy the key to the user buffer */ - wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf)); - if (wep_key_len < 0) { - ext->key_len = 0; - } else { - ext->key_len = wep_key_len; - memcpy(extra, buf, ext->key_len); - } - - return 0; -} - - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set extended authentication parameters - */ -static int airo_set_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct airo_info *local = dev->ml_priv; - struct iw_param *param = &wrqu->param; - __le16 currentAuthType = local->config.authType; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_PAIRWISE: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_KEY_MGMT: - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - case IW_AUTH_PRIVACY_INVOKED: - /* - * airo does not use these parameters - */ - break; - - case IW_AUTH_DROP_UNENCRYPTED: - if (param->value) { - /* Only change auth type if unencrypted */ - if (currentAuthType == AUTH_OPEN) - set_auth_type(local, AUTH_ENCRYPT); - } else { - set_auth_type(local, AUTH_OPEN); - } - - /* Commit the changes to flags if needed */ - if (local->config.authType != currentAuthType) - set_bit (FLAG_COMMIT, &local->flags); - break; - - case IW_AUTH_80211_AUTH_ALG: { - if (param->value & IW_AUTH_ALG_SHARED_KEY) { - set_auth_type(local, AUTH_SHAREDKEY); - } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { - /* We don't know here if WEP open system or - * unencrypted mode was requested - so use the - * last mode (of these two) used last time - */ - set_auth_type(local, local->last_auth); - } else - return -EINVAL; - - /* Commit the changes to flags if needed */ - if (local->config.authType != currentAuthType) - set_bit (FLAG_COMMIT, &local->flags); - break; - } - - case IW_AUTH_WPA_ENABLED: - /* Silently accept disable of WPA */ - if (param->value > 0) - return -EOPNOTSUPP; - break; - - default: - return -EOPNOTSUPP; - } - return -EINPROGRESS; -} - - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get extended authentication parameters - */ -static int airo_get_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct airo_info *local = dev->ml_priv; - struct iw_param *param = &wrqu->param; - __le16 currentAuthType = local->config.authType; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_DROP_UNENCRYPTED: - switch (currentAuthType) { - case AUTH_SHAREDKEY: - case AUTH_ENCRYPT: - param->value = 1; - break; - default: - param->value = 0; - break; - } - break; - - case IW_AUTH_80211_AUTH_ALG: - switch (currentAuthType) { - case AUTH_SHAREDKEY: - param->value = IW_AUTH_ALG_SHARED_KEY; - break; - case AUTH_ENCRYPT: - default: - param->value = IW_AUTH_ALG_OPEN_SYSTEM; - break; - } - break; - - case IW_AUTH_WPA_ENABLED: - param->value = 0; - break; - - default: - return -EOPNOTSUPP; - } - return 0; -} - - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Tx-Power - */ -static int airo_set_txpow(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - CapabilityRid cap_rid; /* Card capability info */ - int i; - int rc = -EINVAL; - __le16 v = cpu_to_le16(vwrq->value); - - readCapabilityRid(local, &cap_rid, 1); - - if (vwrq->disabled) { - set_bit (FLAG_RADIO_OFF, &local->flags); - set_bit (FLAG_COMMIT, &local->flags); - return -EINPROGRESS; /* Call commit handler */ - } - if (vwrq->flags != IW_TXPOW_MWATT) { - return -EINVAL; - } - clear_bit (FLAG_RADIO_OFF, &local->flags); - for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++) - if (v == cap_rid.txPowerLevels[i]) { - readConfigRid(local, 1); - local->config.txPower = v; - set_bit (FLAG_COMMIT, &local->flags); - rc = -EINPROGRESS; /* Call commit handler */ - break; - } - return rc; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Tx-Power - */ -static int airo_get_txpow(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - - readConfigRid(local, 1); - vwrq->value = le16_to_cpu(local->config.txPower); - vwrq->fixed = 1; /* No power control */ - vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags); - vwrq->flags = IW_TXPOW_MWATT; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Retry limits - */ -static int airo_set_retry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - int rc = -EINVAL; - - if(vwrq->disabled) { - return -EINVAL; - } - readConfigRid(local, 1); - if(vwrq->flags & IW_RETRY_LIMIT) { - __le16 v = cpu_to_le16(vwrq->value); - if(vwrq->flags & IW_RETRY_LONG) - local->config.longRetryLimit = v; - else if (vwrq->flags & IW_RETRY_SHORT) - local->config.shortRetryLimit = v; - else { - /* No modifier : set both */ - local->config.longRetryLimit = v; - local->config.shortRetryLimit = v; - } - set_bit (FLAG_COMMIT, &local->flags); - rc = -EINPROGRESS; /* Call commit handler */ - } - if(vwrq->flags & IW_RETRY_LIFETIME) { - local->config.txLifetime = cpu_to_le16(vwrq->value / 1024); - set_bit (FLAG_COMMIT, &local->flags); - rc = -EINPROGRESS; /* Call commit handler */ - } - return rc; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Retry limits - */ -static int airo_get_retry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - - vwrq->disabled = 0; /* Can't be disabled */ - - readConfigRid(local, 1); - /* Note : by default, display the min retry number */ - if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { - vwrq->flags = IW_RETRY_LIFETIME; - vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024; - } else if((vwrq->flags & IW_RETRY_LONG)) { - vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; - vwrq->value = le16_to_cpu(local->config.longRetryLimit); - } else { - vwrq->flags = IW_RETRY_LIMIT; - vwrq->value = le16_to_cpu(local->config.shortRetryLimit); - if(local->config.shortRetryLimit != local->config.longRetryLimit) - vwrq->flags |= IW_RETRY_SHORT; - } - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get range info - */ -static int airo_get_range(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - struct iw_range *range = (struct iw_range *) extra; - CapabilityRid cap_rid; /* Card capability info */ - int i; - int k; - - readCapabilityRid(local, &cap_rid, 1); - - dwrq->length = sizeof(struct iw_range); - memset(range, 0, sizeof(*range)); - range->min_nwid = 0x0000; - range->max_nwid = 0x0000; - range->num_channels = 14; - /* Should be based on cap_rid.country to give only - * what the current card support */ - k = 0; - for(i = 0; i < 14; i++) { - range->freq[k].i = i + 1; /* List index */ - range->freq[k].m = 100000 * - ieee80211_channel_to_frequency(i + 1, IEEE80211_BAND_2GHZ); - range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */ - } - range->num_frequency = k; - - range->sensitivity = 65535; - - /* Hum... Should put the right values there */ - if (local->rssi) - range->max_qual.qual = 100; /* % */ - else - range->max_qual.qual = airo_get_max_quality(&cap_rid); - range->max_qual.level = 0x100 - 120; /* -120 dBm */ - range->max_qual.noise = 0x100 - 120; /* -120 dBm */ - - /* Experimental measurements - boundary 11/5.5 Mb/s */ - /* Note : with or without the (local->rssi), results - * are somewhat different. - Jean II */ - if (local->rssi) { - range->avg_qual.qual = 50; /* % */ - range->avg_qual.level = 0x100 - 70; /* -70 dBm */ - } else { - range->avg_qual.qual = airo_get_avg_quality(&cap_rid); - range->avg_qual.level = 0x100 - 80; /* -80 dBm */ - } - range->avg_qual.noise = 0x100 - 85; /* -85 dBm */ - - for(i = 0 ; i < 8 ; i++) { - range->bitrate[i] = cap_rid.supportedRates[i] * 500000; - if(range->bitrate[i] == 0) - break; - } - range->num_bitrates = i; - - /* Set an indication of the max TCP throughput - * in bit/s that we can expect using this interface. - * May be use for QoS stuff... Jean II */ - if(i > 2) - range->throughput = 5000 * 1000; - else - range->throughput = 1500 * 1000; - - range->min_rts = 0; - range->max_rts = AIRO_DEF_MTU; - range->min_frag = 256; - range->max_frag = AIRO_DEF_MTU; - - if(cap_rid.softCap & cpu_to_le16(2)) { - // WEP: RC4 40 bits - range->encoding_size[0] = 5; - // RC4 ~128 bits - if (cap_rid.softCap & cpu_to_le16(0x100)) { - range->encoding_size[1] = 13; - range->num_encoding_sizes = 2; - } else - range->num_encoding_sizes = 1; - range->max_encoding_tokens = - cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1; - } else { - range->num_encoding_sizes = 0; - range->max_encoding_tokens = 0; - } - range->min_pmp = 0; - range->max_pmp = 5000000; /* 5 secs */ - range->min_pmt = 0; - range->max_pmt = 65535 * 1024; /* ??? */ - range->pmp_flags = IW_POWER_PERIOD; - range->pmt_flags = IW_POWER_TIMEOUT; - range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; - - /* Transmit Power - values are in mW */ - for(i = 0 ; i < 8 ; i++) { - range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]); - if(range->txpower[i] == 0) - break; - } - range->num_txpower = i; - range->txpower_capa = IW_TXPOW_MWATT; - range->we_version_source = 19; - range->we_version_compiled = WIRELESS_EXT; - range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; - range->retry_flags = IW_RETRY_LIMIT; - range->r_time_flags = IW_RETRY_LIFETIME; - range->min_retry = 1; - range->max_retry = 65535; - range->min_r_time = 1024; - range->max_r_time = 65535 * 1024; - - /* Event capability (kernel + driver) */ - range->event_capa[0] = (IW_EVENT_CAPA_K_0 | - IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | - IW_EVENT_CAPA_MASK(SIOCGIWAP) | - IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); - range->event_capa[1] = IW_EVENT_CAPA_K_1; - range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Power Management - */ -static int airo_set_power(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - - readConfigRid(local, 1); - if (vwrq->disabled) { - if (sniffing_mode(local)) - return -EINVAL; - local->config.powerSaveMode = POWERSAVE_CAM; - local->config.rmode &= ~RXMODE_MASK; - local->config.rmode |= RXMODE_BC_MC_ADDR; - set_bit (FLAG_COMMIT, &local->flags); - return -EINPROGRESS; /* Call commit handler */ - } - if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { - local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024); - local->config.powerSaveMode = POWERSAVE_PSPCAM; - set_bit (FLAG_COMMIT, &local->flags); - } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { - local->config.fastListenInterval = - local->config.listenInterval = - cpu_to_le16((vwrq->value + 500) / 1024); - local->config.powerSaveMode = POWERSAVE_PSPCAM; - set_bit (FLAG_COMMIT, &local->flags); - } - switch (vwrq->flags & IW_POWER_MODE) { - case IW_POWER_UNICAST_R: - if (sniffing_mode(local)) - return -EINVAL; - local->config.rmode &= ~RXMODE_MASK; - local->config.rmode |= RXMODE_ADDR; - set_bit (FLAG_COMMIT, &local->flags); - break; - case IW_POWER_ALL_R: - if (sniffing_mode(local)) - return -EINVAL; - local->config.rmode &= ~RXMODE_MASK; - local->config.rmode |= RXMODE_BC_MC_ADDR; - set_bit (FLAG_COMMIT, &local->flags); - case IW_POWER_ON: - /* This is broken, fixme ;-) */ - break; - default: - return -EINVAL; - } - // Note : we may want to factor local->need_commit here - // Note2 : may also want to factor RXMODE_RFMON test - return -EINPROGRESS; /* Call commit handler */ -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Power Management - */ -static int airo_get_power(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - __le16 mode; - - readConfigRid(local, 1); - mode = local->config.powerSaveMode; - if ((vwrq->disabled = (mode == POWERSAVE_CAM))) - return 0; - if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { - vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024; - vwrq->flags = IW_POWER_TIMEOUT; - } else { - vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024; - vwrq->flags = IW_POWER_PERIOD; - } - if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR) - vwrq->flags |= IW_POWER_UNICAST_R; - else - vwrq->flags |= IW_POWER_ALL_R; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : set Sensitivity - */ -static int airo_set_sens(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - - readConfigRid(local, 1); - local->config.rssiThreshold = - cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value); - set_bit (FLAG_COMMIT, &local->flags); - - return -EINPROGRESS; /* Call commit handler */ -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get Sensitivity - */ -static int airo_get_sens(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - - readConfigRid(local, 1); - vwrq->value = le16_to_cpu(local->config.rssiThreshold); - vwrq->disabled = (vwrq->value == 0); - vwrq->fixed = 1; - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : get AP List - * Note : this is deprecated in favor of IWSCAN - */ -static int airo_get_aplist(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct airo_info *local = dev->ml_priv; - struct sockaddr *address = (struct sockaddr *) extra; - struct iw_quality *qual; - BSSListRid BSSList; - int i; - int loseSync = capable(CAP_NET_ADMIN) ? 1: -1; - - qual = kmalloc(IW_MAX_AP * sizeof(*qual), GFP_KERNEL); - if (!qual) - return -ENOMEM; - - for (i = 0; i < IW_MAX_AP; i++) { - u16 dBm; - if (readBSSListRid(local, loseSync, &BSSList)) - break; - loseSync = 0; - memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN); - address[i].sa_family = ARPHRD_ETHER; - dBm = le16_to_cpu(BSSList.dBm); - if (local->rssi) { - qual[i].level = 0x100 - dBm; - qual[i].qual = airo_dbm_to_pct(local->rssi, dBm); - qual[i].updated = IW_QUAL_QUAL_UPDATED - | IW_QUAL_LEVEL_UPDATED - | IW_QUAL_DBM; - } else { - qual[i].level = (dBm + 321) / 2; - qual[i].qual = 0; - qual[i].updated = IW_QUAL_QUAL_INVALID - | IW_QUAL_LEVEL_UPDATED - | IW_QUAL_DBM; - } - qual[i].noise = local->wstats.qual.noise; - if (BSSList.index == cpu_to_le16(0xffff)) - break; - } - if (!i) { - StatusRid status_rid; /* Card status info */ - readStatusRid(local, &status_rid, 1); - for (i = 0; - i < min(IW_MAX_AP, 4) && - (status_rid.bssid[i][0] - & status_rid.bssid[i][1] - & status_rid.bssid[i][2] - & status_rid.bssid[i][3] - & status_rid.bssid[i][4] - & status_rid.bssid[i][5])!=0xff && - (status_rid.bssid[i][0] - | status_rid.bssid[i][1] - | status_rid.bssid[i][2] - | status_rid.bssid[i][3] - | status_rid.bssid[i][4] - | status_rid.bssid[i][5]); - i++) { - memcpy(address[i].sa_data, - status_rid.bssid[i], ETH_ALEN); - address[i].sa_family = ARPHRD_ETHER; - } - } else { - dwrq->flags = 1; /* Should be define'd */ - memcpy(extra + sizeof(struct sockaddr) * i, qual, - sizeof(struct iw_quality) * i); - } - dwrq->length = i; - - kfree(qual); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : Initiate Scan - */ -static int airo_set_scan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct airo_info *ai = dev->ml_priv; - Cmd cmd; - Resp rsp; - int wake = 0; - APListRid APList_rid_empty; - - /* Note : you may have realised that, as this is a SET operation, - * this is privileged and therefore a normal user can't - * perform scanning. - * This is not an error, while the device perform scanning, - * traffic doesn't flow, so it's a perfect DoS... - * Jean II */ - if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN; - - if (down_interruptible(&ai->sem)) - return -ERESTARTSYS; - - /* If there's already a scan in progress, don't - * trigger another one. */ - if (ai->scan_timeout > 0) - goto out; - - /* Clear APList as it affects scan results */ - memset(&APList_rid_empty, 0, sizeof(APList_rid_empty)); - APList_rid_empty.len = cpu_to_le16(sizeof(APList_rid_empty)); - disable_MAC(ai, 2); - writeAPListRid(ai, &APList_rid_empty, 0); - enable_MAC(ai, 0); - - /* Initiate a scan command */ - ai->scan_timeout = RUN_AT(3*HZ); - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd=CMD_LISTBSS; - issuecommand(ai, &cmd, &rsp); - wake = 1; - -out: - up(&ai->sem); - if (wake) - wake_up_interruptible(&ai->thr_wait); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Translate scan data returned from the card to a card independent - * format that the Wireless Tools will understand - Jean II - */ -static inline char *airo_translate_scan(struct net_device *dev, - struct iw_request_info *info, - char *current_ev, - char *end_buf, - BSSListRid *bss) -{ - struct airo_info *ai = dev->ml_priv; - struct iw_event iwe; /* Temporary buffer */ - __le16 capabilities; - char * current_val; /* For rates */ - int i; - char * buf; - u16 dBm; - - /* First entry *MUST* be the AP MAC address */ - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_ADDR_LEN); - - /* Other entries will be displayed in the order we give them */ - - /* Add the ESSID */ - iwe.u.data.length = bss->ssidLen; - if(iwe.u.data.length > 32) - iwe.u.data.length = 32; - iwe.cmd = SIOCGIWESSID; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, bss->ssid); - - /* Add mode */ - iwe.cmd = SIOCGIWMODE; - capabilities = bss->cap; - if(capabilities & (CAP_ESS | CAP_IBSS)) { - if(capabilities & CAP_ESS) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_UINT_LEN); - } - - /* Add frequency */ - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = le16_to_cpu(bss->dsChannel); - iwe.u.freq.m = 100000 * - ieee80211_channel_to_frequency(iwe.u.freq.m, IEEE80211_BAND_2GHZ); - iwe.u.freq.e = 1; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_FREQ_LEN); - - dBm = le16_to_cpu(bss->dBm); - - /* Add quality statistics */ - iwe.cmd = IWEVQUAL; - if (ai->rssi) { - iwe.u.qual.level = 0x100 - dBm; - iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm); - iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED - | IW_QUAL_LEVEL_UPDATED - | IW_QUAL_DBM; - } else { - iwe.u.qual.level = (dBm + 321) / 2; - iwe.u.qual.qual = 0; - iwe.u.qual.updated = IW_QUAL_QUAL_INVALID - | IW_QUAL_LEVEL_UPDATED - | IW_QUAL_DBM; - } - iwe.u.qual.noise = ai->wstats.qual.noise; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_QUAL_LEN); - - /* Add encryption capability */ - iwe.cmd = SIOCGIWENCODE; - if(capabilities & CAP_PRIVACY) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, bss->ssid); - - /* Rate : stuffing multiple values in a single event require a bit - * more of magic - Jean II */ - current_val = current_ev + iwe_stream_lcp_len(info); - - iwe.cmd = SIOCGIWRATE; - /* Those two flags are ignored... */ - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; - /* Max 8 values */ - for(i = 0 ; i < 8 ; i++) { - /* NULL terminated */ - if(bss->rates[i] == 0) - break; - /* Bit rate given in 500 kb/s units (+ 0x80) */ - iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000); - /* Add new value to event */ - current_val = iwe_stream_add_value(info, current_ev, - current_val, end_buf, - &iwe, IW_EV_PARAM_LEN); - } - /* Check if we added any event */ - if ((current_val - current_ev) > iwe_stream_lcp_len(info)) - current_ev = current_val; - - /* Beacon interval */ - buf = kmalloc(30, GFP_KERNEL); - if (buf) { - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "bcn_int=%d", bss->beaconInterval); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, buf); - kfree(buf); - } - - /* Put WPA/RSN Information Elements into the event stream */ - if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) { - unsigned int num_null_ies = 0; - u16 length = sizeof (bss->extra.iep); - u8 *ie = (void *)&bss->extra.iep; - - while ((length >= 2) && (num_null_ies < 2)) { - if (2 + ie[1] > length) { - /* Invalid element, don't continue parsing IE */ - break; - } - - switch (ie[0]) { - case WLAN_EID_SSID: - /* Two zero-length SSID elements - * mean we're done parsing elements */ - if (!ie[1]) - num_null_ies++; - break; - - case WLAN_EID_VENDOR_SPECIFIC: - if (ie[1] >= 4 && - ie[2] == 0x00 && - ie[3] == 0x50 && - ie[4] == 0xf2 && - ie[5] == 0x01) { - iwe.cmd = IWEVGENIE; - /* 64 is an arbitrary cut-off */ - iwe.u.data.length = min(ie[1] + 2, - 64); - current_ev = iwe_stream_add_point( - info, current_ev, - end_buf, &iwe, ie); - } - break; - - case WLAN_EID_RSN: - iwe.cmd = IWEVGENIE; - /* 64 is an arbitrary cut-off */ - iwe.u.data.length = min(ie[1] + 2, 64); - current_ev = iwe_stream_add_point( - info, current_ev, end_buf, - &iwe, ie); - break; - - default: - break; - } - - length -= 2 + ie[1]; - ie += 2 + ie[1]; - } - } - return current_ev; -} - -/*------------------------------------------------------------------*/ -/* - * Wireless Handler : Read Scan Results - */ -static int airo_get_scan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct airo_info *ai = dev->ml_priv; - BSSListElement *net; - int err = 0; - char *current_ev = extra; - - /* If a scan is in-progress, return -EAGAIN */ - if (ai->scan_timeout > 0) - return -EAGAIN; - - if (down_interruptible(&ai->sem)) - return -EAGAIN; - - list_for_each_entry (net, &ai->network_list, list) { - /* Translate to WE format this entry */ - current_ev = airo_translate_scan(dev, info, current_ev, - extra + dwrq->length, - &net->bss); - - /* Check if there is space for one more entry */ - if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) { - /* Ask user space to try again with a bigger buffer */ - err = -E2BIG; - goto out; - } - } - - /* Length of data */ - dwrq->length = (current_ev - extra); - dwrq->flags = 0; /* todo */ - -out: - up(&ai->sem); - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Commit handler : called after a bunch of SET operations - */ -static int airo_config_commit(struct net_device *dev, - struct iw_request_info *info, /* NULL */ - void *zwrq, /* NULL */ - char *extra) /* NULL */ -{ - struct airo_info *local = dev->ml_priv; - - if (!test_bit (FLAG_COMMIT, &local->flags)) - return 0; - - /* Some of the "SET" function may have modified some of the - * parameters. It's now time to commit them in the card */ - disable_MAC(local, 1); - if (test_bit (FLAG_RESET, &local->flags)) { - SsidRid SSID_rid; - - readSsidRid(local, &SSID_rid); - if (test_bit(FLAG_MPI,&local->flags)) - setup_card(local, dev->dev_addr, 1 ); - else - reset_airo_card(dev); - disable_MAC(local, 1); - writeSsidRid(local, &SSID_rid, 1); - writeAPListRid(local, &local->APList, 1); - } - if (down_interruptible(&local->sem)) - return -ERESTARTSYS; - writeConfigRid(local, 0); - enable_MAC(local, 0); - if (test_bit (FLAG_RESET, &local->flags)) - airo_set_promisc(local); - else - up(&local->sem); - - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Structures to export the Wireless Handlers - */ - -static const struct iw_priv_args airo_private_args[] = { -/*{ cmd, set_args, get_args, name } */ - { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), - IW_PRIV_TYPE_BYTE | 2047, "airoioctl" }, - { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" }, -}; - -static const iw_handler airo_handler[] = -{ - (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */ - (iw_handler) airo_get_name, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) airo_set_freq, /* SIOCSIWFREQ */ - (iw_handler) airo_get_freq, /* SIOCGIWFREQ */ - (iw_handler) airo_set_mode, /* SIOCSIWMODE */ - (iw_handler) airo_get_mode, /* SIOCGIWMODE */ - (iw_handler) airo_set_sens, /* SIOCSIWSENS */ - (iw_handler) airo_get_sens, /* SIOCGIWSENS */ - (iw_handler) NULL, /* SIOCSIWRANGE */ - (iw_handler) airo_get_range, /* SIOCGIWRANGE */ - (iw_handler) NULL, /* SIOCSIWPRIV */ - (iw_handler) NULL, /* SIOCGIWPRIV */ - (iw_handler) NULL, /* SIOCSIWSTATS */ - (iw_handler) NULL, /* SIOCGIWSTATS */ - iw_handler_set_spy, /* SIOCSIWSPY */ - iw_handler_get_spy, /* SIOCGIWSPY */ - iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ - iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ - (iw_handler) airo_set_wap, /* SIOCSIWAP */ - (iw_handler) airo_get_wap, /* SIOCGIWAP */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */ - (iw_handler) airo_set_scan, /* SIOCSIWSCAN */ - (iw_handler) airo_get_scan, /* SIOCGIWSCAN */ - (iw_handler) airo_set_essid, /* SIOCSIWESSID */ - (iw_handler) airo_get_essid, /* SIOCGIWESSID */ - (iw_handler) airo_set_nick, /* SIOCSIWNICKN */ - (iw_handler) airo_get_nick, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) airo_set_rate, /* SIOCSIWRATE */ - (iw_handler) airo_get_rate, /* SIOCGIWRATE */ - (iw_handler) airo_set_rts, /* SIOCSIWRTS */ - (iw_handler) airo_get_rts, /* SIOCGIWRTS */ - (iw_handler) airo_set_frag, /* SIOCSIWFRAG */ - (iw_handler) airo_get_frag, /* SIOCGIWFRAG */ - (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */ - (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */ - (iw_handler) airo_set_retry, /* SIOCSIWRETRY */ - (iw_handler) airo_get_retry, /* SIOCGIWRETRY */ - (iw_handler) airo_set_encode, /* SIOCSIWENCODE */ - (iw_handler) airo_get_encode, /* SIOCGIWENCODE */ - (iw_handler) airo_set_power, /* SIOCSIWPOWER */ - (iw_handler) airo_get_power, /* SIOCGIWPOWER */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCSIWGENIE */ - (iw_handler) NULL, /* SIOCGIWGENIE */ - (iw_handler) airo_set_auth, /* SIOCSIWAUTH */ - (iw_handler) airo_get_auth, /* SIOCGIWAUTH */ - (iw_handler) airo_set_encodeext, /* SIOCSIWENCODEEXT */ - (iw_handler) airo_get_encodeext, /* SIOCGIWENCODEEXT */ - (iw_handler) NULL, /* SIOCSIWPMKSA */ -}; - -/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here. - * We want to force the use of the ioctl code, because those can't be - * won't work the iw_handler code (because they simultaneously read - * and write data and iw_handler can't do that). - * Note that it's perfectly legal to read/write on a single ioctl command, - * you just can't use iwpriv and need to force it via the ioctl handler. - * Jean II */ -static const iw_handler airo_private_handler[] = -{ - NULL, /* SIOCIWFIRSTPRIV */ -}; - -static const struct iw_handler_def airo_handler_def = -{ - .num_standard = ARRAY_SIZE(airo_handler), - .num_private = ARRAY_SIZE(airo_private_handler), - .num_private_args = ARRAY_SIZE(airo_private_args), - .standard = airo_handler, - .private = airo_private_handler, - .private_args = airo_private_args, - .get_wireless_stats = airo_get_wireless_stats, -}; - -/* - * This defines the configuration part of the Wireless Extensions - * Note : irq and spinlock protection will occur in the subroutines - * - * TODO : - * o Check input value more carefully and fill correct values in range - * o Test and shakeout the bugs (if any) - * - * Jean II - * - * Javier Achirica did a great job of merging code from the unnamed CISCO - * developer that added support for flashing the card. - */ -static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - int rc = 0; - struct airo_info *ai = dev->ml_priv; - - if (ai->power.event) - return 0; - - switch (cmd) { -#ifdef CISCO_EXT - case AIROIDIFC: -#ifdef AIROOLDIDIFC - case AIROOLDIDIFC: -#endif - { - int val = AIROMAGIC; - aironet_ioctl com; - if (copy_from_user(&com,rq->ifr_data,sizeof(com))) - rc = -EFAULT; - else if (copy_to_user(com.data,(char *)&val,sizeof(val))) - rc = -EFAULT; - } - break; - - case AIROIOCTL: -#ifdef AIROOLDIOCTL - case AIROOLDIOCTL: -#endif - /* Get the command struct and hand it off for evaluation by - * the proper subfunction - */ - { - aironet_ioctl com; - if (copy_from_user(&com,rq->ifr_data,sizeof(com))) { - rc = -EFAULT; - break; - } - - /* Separate R/W functions bracket legality here - */ - if ( com.command == AIRORSWVERSION ) { - if (copy_to_user(com.data, swversion, sizeof(swversion))) - rc = -EFAULT; - else - rc = 0; - } - else if ( com.command <= AIRORRID) - rc = readrids(dev,&com); - else if ( com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2) ) - rc = writerids(dev,&com); - else if ( com.command >= AIROFLSHRST && com.command <= AIRORESTART ) - rc = flashcard(dev,&com); - else - rc = -EINVAL; /* Bad command in ioctl */ - } - break; -#endif /* CISCO_EXT */ - - // All other calls are currently unsupported - default: - rc = -EOPNOTSUPP; - } - return rc; -} - -/* - * Get the Wireless stats out of the driver - * Note : irq and spinlock protection will occur in the subroutines - * - * TODO : - * o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs) - * - * Jean - */ -static void airo_read_wireless_stats(struct airo_info *local) -{ - StatusRid status_rid; - StatsRid stats_rid; - CapabilityRid cap_rid; - __le32 *vals = stats_rid.vals; - - /* Get stats out of the card */ - clear_bit(JOB_WSTATS, &local->jobs); - if (local->power.event) { - up(&local->sem); - return; - } - readCapabilityRid(local, &cap_rid, 0); - readStatusRid(local, &status_rid, 0); - readStatsRid(local, &stats_rid, RID_STATS, 0); - up(&local->sem); - - /* The status */ - local->wstats.status = le16_to_cpu(status_rid.mode); - - /* Signal quality and co */ - if (local->rssi) { - local->wstats.qual.level = - airo_rssi_to_dbm(local->rssi, - le16_to_cpu(status_rid.sigQuality)); - /* normalizedSignalStrength appears to be a percentage */ - local->wstats.qual.qual = - le16_to_cpu(status_rid.normalizedSignalStrength); - } else { - local->wstats.qual.level = - (le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2; - local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid); - } - if (le16_to_cpu(status_rid.len) >= 124) { - local->wstats.qual.noise = 0x100 - status_rid.noisedBm; - local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; - } else { - local->wstats.qual.noise = 0; - local->wstats.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID | IW_QUAL_DBM; - } - - /* Packets discarded in the wireless adapter due to wireless - * specific problems */ - local->wstats.discard.nwid = le32_to_cpu(vals[56]) + - le32_to_cpu(vals[57]) + - le32_to_cpu(vals[58]); /* SSID Mismatch */ - local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */ - local->wstats.discard.fragment = le32_to_cpu(vals[30]); - local->wstats.discard.retries = le32_to_cpu(vals[10]); - local->wstats.discard.misc = le32_to_cpu(vals[1]) + - le32_to_cpu(vals[32]); - local->wstats.miss.beacon = le32_to_cpu(vals[34]); -} - -static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev) -{ - struct airo_info *local = dev->ml_priv; - - if (!test_bit(JOB_WSTATS, &local->jobs)) { - /* Get stats out of the card if available */ - if (down_trylock(&local->sem) != 0) { - set_bit(JOB_WSTATS, &local->jobs); - wake_up_interruptible(&local->thr_wait); - } else - airo_read_wireless_stats(local); - } - - return &local->wstats; -} - -#ifdef CISCO_EXT -/* - * This just translates from driver IOCTL codes to the command codes to - * feed to the radio's host interface. Things can be added/deleted - * as needed. This represents the READ side of control I/O to - * the card - */ -static int readrids(struct net_device *dev, aironet_ioctl *comp) { - unsigned short ridcode; - unsigned char *iobuf; - int len; - struct airo_info *ai = dev->ml_priv; - - if (test_bit(FLAG_FLASHING, &ai->flags)) - return -EIO; - - switch(comp->command) - { - case AIROGCAP: ridcode = RID_CAPABILITIES; break; - case AIROGCFG: ridcode = RID_CONFIG; - if (test_bit(FLAG_COMMIT, &ai->flags)) { - disable_MAC (ai, 1); - writeConfigRid (ai, 1); - enable_MAC(ai, 1); - } - break; - case AIROGSLIST: ridcode = RID_SSID; break; - case AIROGVLIST: ridcode = RID_APLIST; break; - case AIROGDRVNAM: ridcode = RID_DRVNAME; break; - case AIROGEHTENC: ridcode = RID_ETHERENCAP; break; - case AIROGWEPKTMP: ridcode = RID_WEP_TEMP; - /* Only super-user can read WEP keys */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - break; - case AIROGWEPKNV: ridcode = RID_WEP_PERM; - /* Only super-user can read WEP keys */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - break; - case AIROGSTAT: ridcode = RID_STATUS; break; - case AIROGSTATSD32: ridcode = RID_STATSDELTA; break; - case AIROGSTATSC32: ridcode = RID_STATS; break; - case AIROGMICSTATS: - if (copy_to_user(comp->data, &ai->micstats, - min((int)comp->len,(int)sizeof(ai->micstats)))) - return -EFAULT; - return 0; - case AIRORRID: ridcode = comp->ridnum; break; - default: - return -EINVAL; - } - - if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL) - return -ENOMEM; - - PC4500_readrid(ai,ridcode,iobuf,RIDSIZE, 1); - /* get the count of bytes in the rid docs say 1st 2 bytes is it. - * then return it to the user - * 9/22/2000 Honor user given length - */ - len = comp->len; - - if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) { - kfree (iobuf); - return -EFAULT; - } - kfree (iobuf); - return 0; -} - -/* - * Danger Will Robinson write the rids here - */ - -static int writerids(struct net_device *dev, aironet_ioctl *comp) { - struct airo_info *ai = dev->ml_priv; - int ridcode; - int enabled; - static int (* writer)(struct airo_info *, u16 rid, const void *, int, int); - unsigned char *iobuf; - - /* Only super-user can write RIDs */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (test_bit(FLAG_FLASHING, &ai->flags)) - return -EIO; - - ridcode = 0; - writer = do_writerid; - - switch(comp->command) - { - case AIROPSIDS: ridcode = RID_SSID; break; - case AIROPCAP: ridcode = RID_CAPABILITIES; break; - case AIROPAPLIST: ridcode = RID_APLIST; break; - case AIROPCFG: ai->config.len = 0; - clear_bit(FLAG_COMMIT, &ai->flags); - ridcode = RID_CONFIG; break; - case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break; - case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break; - case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break; - case AIROPWEPKEY: ridcode = RID_WEP_TEMP; writer = PC4500_writerid; - break; - case AIROPLEAPUSR+1: ridcode = 0xFF2A; break; - case AIROPLEAPUSR+2: ridcode = 0xFF2B; break; - - /* this is not really a rid but a command given to the card - * same with MAC off - */ - case AIROPMACON: - if (enable_MAC(ai, 1) != 0) - return -EIO; - return 0; - - /* - * Evidently this code in the airo driver does not get a symbol - * as disable_MAC. it's probably so short the compiler does not gen one. - */ - case AIROPMACOFF: - disable_MAC(ai, 1); - return 0; - - /* This command merely clears the counts does not actually store any data - * only reads rid. But as it changes the cards state, I put it in the - * writerid routines. - */ - case AIROPSTCLR: - if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL) - return -ENOMEM; - - PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDSIZE, 1); - - enabled = ai->micstats.enabled; - memset(&ai->micstats,0,sizeof(ai->micstats)); - ai->micstats.enabled = enabled; - - if (copy_to_user(comp->data, iobuf, - min((int)comp->len, (int)RIDSIZE))) { - kfree (iobuf); - return -EFAULT; - } - kfree (iobuf); - return 0; - - default: - return -EOPNOTSUPP; /* Blarg! */ - } - if(comp->len > RIDSIZE) - return -EINVAL; - - if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL) - return -ENOMEM; - - if (copy_from_user(iobuf,comp->data,comp->len)) { - kfree (iobuf); - return -EFAULT; - } - - if (comp->command == AIROPCFG) { - ConfigRid *cfg = (ConfigRid *)iobuf; - - if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) - cfg->opmode |= MODE_MIC; - - if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS) - set_bit (FLAG_ADHOC, &ai->flags); - else - clear_bit (FLAG_ADHOC, &ai->flags); - } - - if((*writer)(ai, ridcode, iobuf,comp->len,1)) { - kfree (iobuf); - return -EIO; - } - kfree (iobuf); - return 0; -} - -/***************************************************************************** - * Ancillary flash / mod functions much black magic lurkes here * - ***************************************************************************** - */ - -/* - * Flash command switch table - */ - -static int flashcard(struct net_device *dev, aironet_ioctl *comp) { - int z; - - /* Only super-user can modify flash */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch(comp->command) - { - case AIROFLSHRST: - return cmdreset((struct airo_info *)dev->ml_priv); - - case AIROFLSHSTFL: - if (!AIRO_FLASH(dev) && - (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL) - return -ENOMEM; - return setflashmode((struct airo_info *)dev->ml_priv); - - case AIROFLSHGCHR: /* Get char from aux */ - if(comp->len != sizeof(int)) - return -EINVAL; - if (copy_from_user(&z,comp->data,comp->len)) - return -EFAULT; - return flashgchar((struct airo_info *)dev->ml_priv, z, 8000); - - case AIROFLSHPCHR: /* Send char to card. */ - if(comp->len != sizeof(int)) - return -EINVAL; - if (copy_from_user(&z,comp->data,comp->len)) - return -EFAULT; - return flashpchar((struct airo_info *)dev->ml_priv, z, 8000); - - case AIROFLPUTBUF: /* Send 32k to card */ - if (!AIRO_FLASH(dev)) - return -ENOMEM; - if(comp->len > FLASHSIZE) - return -EINVAL; - if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len)) - return -EFAULT; - - flashputbuf((struct airo_info *)dev->ml_priv); - return 0; - - case AIRORESTART: - if (flashrestart((struct airo_info *)dev->ml_priv, dev)) - return -EIO; - return 0; - } - return -EINVAL; -} - -#define FLASH_COMMAND 0x7e7e - -/* - * STEP 1) - * Disable MAC and do soft reset on - * card. - */ - -static int cmdreset(struct airo_info *ai) { - disable_MAC(ai, 1); - - if(!waitbusy (ai)){ - airo_print_info(ai->dev->name, "Waitbusy hang before RESET"); - return -EBUSY; - } - - OUT4500(ai,COMMAND,CMD_SOFTRESET); - - ssleep(1); /* WAS 600 12/7/00 */ - - if(!waitbusy (ai)){ - airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET"); - return -EBUSY; - } - return 0; -} - -/* STEP 2) - * Put the card in legendary flash - * mode - */ - -static int setflashmode (struct airo_info *ai) { - set_bit (FLAG_FLASHING, &ai->flags); - - OUT4500(ai, SWS0, FLASH_COMMAND); - OUT4500(ai, SWS1, FLASH_COMMAND); - if (probe) { - OUT4500(ai, SWS0, FLASH_COMMAND); - OUT4500(ai, COMMAND,0x10); - } else { - OUT4500(ai, SWS2, FLASH_COMMAND); - OUT4500(ai, SWS3, FLASH_COMMAND); - OUT4500(ai, COMMAND,0); - } - msleep(500); /* 500ms delay */ - - if(!waitbusy(ai)) { - clear_bit (FLAG_FLASHING, &ai->flags); - airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode"); - return -EIO; - } - return 0; -} - -/* Put character to SWS0 wait for dwelltime - * x 50us for echo . - */ - -static int flashpchar(struct airo_info *ai,int byte,int dwelltime) { - int echo; - int waittime; - - byte |= 0x8000; - - if(dwelltime == 0 ) - dwelltime = 200; - - waittime=dwelltime; - - /* Wait for busy bit d15 to go false indicating buffer empty */ - while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) { - udelay (50); - waittime -= 50; - } - - /* timeout for busy clear wait */ - if(waittime <= 0 ){ - airo_print_info(ai->dev->name, "flash putchar busywait timeout!"); - return -EBUSY; - } - - /* Port is clear now write byte and wait for it to echo back */ - do { - OUT4500(ai,SWS0,byte); - udelay(50); - dwelltime -= 50; - echo = IN4500(ai,SWS1); - } while (dwelltime >= 0 && echo != byte); - - OUT4500(ai,SWS1,0); - - return (echo == byte) ? 0 : -EIO; -} - -/* - * Get a character from the card matching matchbyte - * Step 3) - */ -static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){ - int rchar; - unsigned char rbyte=0; - - do { - rchar = IN4500(ai,SWS1); - - if(dwelltime && !(0x8000 & rchar)){ - dwelltime -= 10; - mdelay(10); - continue; - } - rbyte = 0xff & rchar; - - if( (rbyte == matchbyte) && (0x8000 & rchar) ){ - OUT4500(ai,SWS1,0); - return 0; - } - if( rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar) - break; - OUT4500(ai,SWS1,0); - - }while(dwelltime > 0); - return -EIO; -} - -/* - * Transfer 32k of firmware data from user buffer to our buffer and - * send to the card - */ - -static int flashputbuf(struct airo_info *ai){ - int nwords; - - /* Write stuff */ - if (test_bit(FLAG_MPI,&ai->flags)) - memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE); - else { - OUT4500(ai,AUXPAGE,0x100); - OUT4500(ai,AUXOFF,0); - - for(nwords=0;nwords != FLASHSIZE / 2;nwords++){ - OUT4500(ai,AUXDATA,ai->flash[nwords] & 0xffff); - } - } - OUT4500(ai,SWS0,0x8000); - - return 0; -} - -/* - * - */ -static int flashrestart(struct airo_info *ai,struct net_device *dev){ - int i,status; - - ssleep(1); /* Added 12/7/00 */ - clear_bit (FLAG_FLASHING, &ai->flags); - if (test_bit(FLAG_MPI, &ai->flags)) { - status = mpi_init_descriptors(ai); - if (status != SUCCESS) - return status; - } - status = setup_card(ai, dev->dev_addr, 1); - - if (!test_bit(FLAG_MPI,&ai->flags)) - for( i = 0; i < MAX_FIDS; i++ ) { - ai->fids[i] = transmit_allocate - ( ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2 ); - } - - ssleep(1); /* Added 12/7/00 */ - return status; -} -#endif /* CISCO_EXT */ - -/* - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - In addition: - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ - -module_init(airo_init_module); -module_exit(airo_cleanup_module); diff --git a/drivers/net/wireless/airo.h b/drivers/net/wireless/airo.h deleted file mode 100644 index e480adf86be6..000000000000 --- a/drivers/net/wireless/airo.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _AIRO_H_ -#define _AIRO_H_ - -struct net_device *init_airo_card(unsigned short irq, int port, int is_pcmcia, - struct device *dmdev); -int reset_airo_card(struct net_device *dev); -void stop_airo_card(struct net_device *dev, int freeres); - -#endif /* _AIRO_H_ */ diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c deleted file mode 100644 index d9ed22b4cc6b..000000000000 --- a/drivers/net/wireless/airo_cs.c +++ /dev/null @@ -1,222 +0,0 @@ -/*====================================================================== - - Aironet driver for 4500 and 4800 series cards - - This code is released under both the GPL version 2 and BSD licenses. - Either license may be used. The respective licenses are found at - the end of this file. - - This code was developed by Benjamin Reed - including portions of which come from the Aironet PC4500 - Developer's Reference Manual and used with permission. Copyright - (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use - code in the Developer's manual was granted for this driver by - Aironet. - - In addition this module was derived from dummy_cs. - The initial developer of dummy_cs is David A. Hinds - . Portions created by David A. Hinds - are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - -======================================================================*/ - -#ifdef __IN_PCMCIA_PACKAGE__ -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "airo.h" - - -/*====================================================================*/ - -MODULE_AUTHOR("Benjamin Reed"); -MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet " - "cards. This is the module that links the PCMCIA card " - "with the airo module."); -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340 PCMCIA cards"); - -/*====================================================================*/ - -static int airo_config(struct pcmcia_device *link); -static void airo_release(struct pcmcia_device *link); - -static void airo_detach(struct pcmcia_device *p_dev); - -struct local_info { - struct net_device *eth_dev; -}; - -static int airo_probe(struct pcmcia_device *p_dev) -{ - struct local_info *local; - - dev_dbg(&p_dev->dev, "airo_attach()\n"); - - /* Allocate space for private device-specific data */ - local = kzalloc(sizeof(*local), GFP_KERNEL); - if (!local) - return -ENOMEM; - - p_dev->priv = local; - - return airo_config(p_dev); -} /* airo_attach */ - -static void airo_detach(struct pcmcia_device *link) -{ - dev_dbg(&link->dev, "airo_detach\n"); - - airo_release(link); - - if (((struct local_info *)link->priv)->eth_dev) { - stop_airo_card(((struct local_info *)link->priv)->eth_dev, - 0); - } - ((struct local_info *)link->priv)->eth_dev = NULL; - - kfree(link->priv); -} /* airo_detach */ - -static int airo_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - if (p_dev->config_index == 0) - return -EINVAL; - - return pcmcia_request_io(p_dev); -} - - -static int airo_config(struct pcmcia_device *link) -{ - struct local_info *dev; - int ret; - - dev = link->priv; - - dev_dbg(&link->dev, "airo_config\n"); - - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | - CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; - - ret = pcmcia_loop_config(link, airo_cs_config_check, NULL); - if (ret) - goto failed; - - if (!link->irq) - goto failed; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - ((struct local_info *)link->priv)->eth_dev = - init_airo_card(link->irq, - link->resource[0]->start, 1, &link->dev); - if (!((struct local_info *)link->priv)->eth_dev) - goto failed; - - return 0; - - failed: - airo_release(link); - return -ENODEV; -} /* airo_config */ - -static void airo_release(struct pcmcia_device *link) -{ - dev_dbg(&link->dev, "airo_release\n"); - pcmcia_disable_device(link); -} - -static int airo_suspend(struct pcmcia_device *link) -{ - struct local_info *local = link->priv; - - netif_device_detach(local->eth_dev); - - return 0; -} - -static int airo_resume(struct pcmcia_device *link) -{ - struct local_info *local = link->priv; - - if (link->open) { - reset_airo_card(local->eth_dev); - netif_device_attach(local->eth_dev); - } - - return 0; -} - -static const struct pcmcia_device_id airo_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x015f, 0x000a), - PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0005), - PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0007), - PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0007), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, airo_ids); - -static struct pcmcia_driver airo_driver = { - .owner = THIS_MODULE, - .name = "airo_cs", - .probe = airo_probe, - .remove = airo_detach, - .id_table = airo_ids, - .suspend = airo_suspend, - .resume = airo_resume, -}; -module_pcmcia_driver(airo_driver); - -/* - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - In addition: - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ diff --git a/drivers/net/wireless/cisco/Kconfig b/drivers/net/wireless/cisco/Kconfig new file mode 100644 index 000000000000..b22567dff893 --- /dev/null +++ b/drivers/net/wireless/cisco/Kconfig @@ -0,0 +1,56 @@ +config WLAN_VENDOR_CISCO + bool "Cisco devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_CISCO + +config AIRO + tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" + depends on CFG80211 && ISA_DMA_API && (PCI || BROKEN) + select WIRELESS_EXT + select CRYPTO + select WEXT_SPY + select WEXT_PRIV + ---help--- + This is the standard Linux driver to support Cisco/Aironet ISA and + PCI 802.11 wireless cards. + It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X + - with or without encryption) as well as card before the Cisco + acquisition (Aironet 4500, Aironet 4800, Aironet 4800B). + + This driver support both the standard Linux Wireless Extensions + and Cisco proprietary API, so both the Linux Wireless Tools and the + Cisco Linux utilities can be used to configure the card. + + The driver can be compiled as a module and will be named "airo". + +config AIRO_CS + tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards" + depends on CFG80211 && PCMCIA && (BROKEN || !M32R) + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + select CRYPTO + select CRYPTO_AES + ---help--- + This is the standard Linux driver to support Cisco/Aironet PCMCIA + 802.11 wireless cards. This driver is the same as the Aironet + driver part of the Linux Pcmcia package. + It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X + - with or without encryption) as well as card before the Cisco + acquisition (Aironet 4500, Aironet 4800, Aironet 4800B). It also + supports OEM of Cisco such as the DELL TrueMobile 4800 and Xircom + 802.11b cards. + + This driver support both the standard Linux Wireless Extensions + and Cisco proprietary API, so both the Linux Wireless Tools and the + Cisco Linux utilities can be used to configure the card. + +endif # WLAN_VENDOR_CISCO diff --git a/drivers/net/wireless/cisco/Makefile b/drivers/net/wireless/cisco/Makefile new file mode 100644 index 000000000000..d4110b19d6ef --- /dev/null +++ b/drivers/net/wireless/cisco/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_AIRO) += airo.o +obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c new file mode 100644 index 000000000000..17c40f06f13e --- /dev/null +++ b/drivers/net/wireless/cisco/airo.c @@ -0,0 +1,8236 @@ +/*====================================================================== + + Aironet driver for 4500 and 4800 series cards + + This code is released under both the GPL version 2 and BSD licenses. + Either license may be used. The respective licenses are found at + the end of this file. + + This code was developed by Benjamin Reed + including portions of which come from the Aironet PC4500 + Developer's Reference Manual and used with permission. Copyright + (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use + code in the Developer's manual was granted for this driver by + Aironet. Major code contributions were received from Javier Achirica + and Jean Tourrilhes . + Code was also integrated from the Cisco Aironet driver for Linux. + Support for MPI350 cards was added by Fabrice Bellet + . + +======================================================================*/ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "airo.h" + +#define DRV_NAME "airo" + +#ifdef CONFIG_PCI +static const struct pci_device_id card_ids[] = { + { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, }, + { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID }, + { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, }, + { 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, }, + { 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, }, + { 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, }, + { 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, card_ids); + +static int airo_pci_probe(struct pci_dev *, const struct pci_device_id *); +static void airo_pci_remove(struct pci_dev *); +static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state); +static int airo_pci_resume(struct pci_dev *pdev); + +static struct pci_driver airo_driver = { + .name = DRV_NAME, + .id_table = card_ids, + .probe = airo_pci_probe, + .remove = airo_pci_remove, + .suspend = airo_pci_suspend, + .resume = airo_pci_resume, +}; +#endif /* CONFIG_PCI */ + +/* Include Wireless Extension definition and check version - Jean II */ +#include +#define WIRELESS_SPY /* enable iwspy support */ + +#define CISCO_EXT /* enable Cisco extensions */ +#ifdef CISCO_EXT +#include +#endif + +/* Hack to do some power saving */ +#define POWER_ON_DOWN + +/* As you can see this list is HUGH! + I really don't know what a lot of these counts are about, but they + are all here for completeness. If the IGNLABEL macro is put in + infront of the label, that statistic will not be included in the list + of statistics in the /proc filesystem */ + +#define IGNLABEL(comment) NULL +static const char *statsLabels[] = { + "RxOverrun", + IGNLABEL("RxPlcpCrcErr"), + IGNLABEL("RxPlcpFormatErr"), + IGNLABEL("RxPlcpLengthErr"), + "RxMacCrcErr", + "RxMacCrcOk", + "RxWepErr", + "RxWepOk", + "RetryLong", + "RetryShort", + "MaxRetries", + "NoAck", + "NoCts", + "RxAck", + "RxCts", + "TxAck", + "TxRts", + "TxCts", + "TxMc", + "TxBc", + "TxUcFrags", + "TxUcPackets", + "TxBeacon", + "RxBeacon", + "TxSinColl", + "TxMulColl", + "DefersNo", + "DefersProt", + "DefersEngy", + "DupFram", + "RxFragDisc", + "TxAged", + "RxAged", + "LostSync-MaxRetry", + "LostSync-MissedBeacons", + "LostSync-ArlExceeded", + "LostSync-Deauth", + "LostSync-Disassoced", + "LostSync-TsfTiming", + "HostTxMc", + "HostTxBc", + "HostTxUc", + "HostTxFail", + "HostRxMc", + "HostRxBc", + "HostRxUc", + "HostRxDiscard", + IGNLABEL("HmacTxMc"), + IGNLABEL("HmacTxBc"), + IGNLABEL("HmacTxUc"), + IGNLABEL("HmacTxFail"), + IGNLABEL("HmacRxMc"), + IGNLABEL("HmacRxBc"), + IGNLABEL("HmacRxUc"), + IGNLABEL("HmacRxDiscard"), + IGNLABEL("HmacRxAccepted"), + "SsidMismatch", + "ApMismatch", + "RatesMismatch", + "AuthReject", + "AuthTimeout", + "AssocReject", + "AssocTimeout", + IGNLABEL("ReasonOutsideTable"), + IGNLABEL("ReasonStatus1"), + IGNLABEL("ReasonStatus2"), + IGNLABEL("ReasonStatus3"), + IGNLABEL("ReasonStatus4"), + IGNLABEL("ReasonStatus5"), + IGNLABEL("ReasonStatus6"), + IGNLABEL("ReasonStatus7"), + IGNLABEL("ReasonStatus8"), + IGNLABEL("ReasonStatus9"), + IGNLABEL("ReasonStatus10"), + IGNLABEL("ReasonStatus11"), + IGNLABEL("ReasonStatus12"), + IGNLABEL("ReasonStatus13"), + IGNLABEL("ReasonStatus14"), + IGNLABEL("ReasonStatus15"), + IGNLABEL("ReasonStatus16"), + IGNLABEL("ReasonStatus17"), + IGNLABEL("ReasonStatus18"), + IGNLABEL("ReasonStatus19"), + "RxMan", + "TxMan", + "RxRefresh", + "TxRefresh", + "RxPoll", + "TxPoll", + "HostRetries", + "LostSync-HostReq", + "HostTxBytes", + "HostRxBytes", + "ElapsedUsec", + "ElapsedSec", + "LostSyncBetterAP", + "PrivacyMismatch", + "Jammed", + "DiscRxNotWepped", + "PhyEleMismatch", + (char*)-1 }; +#ifndef RUN_AT +#define RUN_AT(x) (jiffies+(x)) +#endif + + +/* These variables are for insmod, since it seems that the rates + can only be set in setup_card. Rates should be a comma separated + (no spaces) list of rates (up to 8). */ + +static int rates[8]; +static char *ssids[3]; + +static int io[4]; +static int irq[4]; + +static +int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at. + 0 means no limit. For old cards this was 4 */ + +static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */ +static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read + the bap, needed on some older cards and buses. */ +static int adhoc; + +static int probe = 1; + +static kuid_t proc_kuid; +static int proc_uid /* = 0 */; + +static kgid_t proc_kgid; +static int proc_gid /* = 0 */; + +static int airo_perm = 0555; + +static int proc_perm = 0644; + +MODULE_AUTHOR("Benjamin Reed"); +MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet cards. " + "Direct support for ISA/PCI/MPI cards and support for PCMCIA when used with airo_cs."); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340/350"); +module_param_array(io, int, NULL, 0); +module_param_array(irq, int, NULL, 0); +module_param_array(rates, int, NULL, 0); +module_param_array(ssids, charp, NULL, 0); +module_param(auto_wep, int, 0); +MODULE_PARM_DESC(auto_wep, + "If non-zero, the driver will keep looping through the authentication options until an association is made. " + "The value of auto_wep is number of the wep keys to check. " + "A value of 2 will try using the key at index 0 and index 1."); +module_param(aux_bap, int, 0); +MODULE_PARM_DESC(aux_bap, + "If non-zero, the driver will switch into a mode that seems to work better for older cards with some older buses. " + "Before switching it checks that the switch is needed."); +module_param(maxencrypt, int, 0); +MODULE_PARM_DESC(maxencrypt, + "The maximum speed that the card can do encryption. " + "Units are in 512kbs. " + "Zero (default) means there is no limit. " + "Older cards used to be limited to 2mbs (4)."); +module_param(adhoc, int, 0); +MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode."); +module_param(probe, int, 0); +MODULE_PARM_DESC(probe, "If zero, the driver won't start the card."); + +module_param(proc_uid, int, 0); +MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to."); +module_param(proc_gid, int, 0); +MODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to."); +module_param(airo_perm, int, 0); +MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet."); +module_param(proc_perm, int, 0); +MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc"); + +/* This is a kind of sloppy hack to get this information to OUT4500 and + IN4500. I would be extremely interested in the situation where this + doesn't work though!!! */ +static int do8bitIO /* = 0 */; + +/* Return codes */ +#define SUCCESS 0 +#define ERROR -1 +#define NO_PACKET -2 + +/* Commands */ +#define NOP2 0x0000 +#define MAC_ENABLE 0x0001 +#define MAC_DISABLE 0x0002 +#define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */ +#define CMD_SOFTRESET 0x0004 +#define HOSTSLEEP 0x0005 +#define CMD_MAGIC_PKT 0x0006 +#define CMD_SETWAKEMASK 0x0007 +#define CMD_READCFG 0x0008 +#define CMD_SETMODE 0x0009 +#define CMD_ALLOCATETX 0x000a +#define CMD_TRANSMIT 0x000b +#define CMD_DEALLOCATETX 0x000c +#define NOP 0x0010 +#define CMD_WORKAROUND 0x0011 +#define CMD_ALLOCATEAUX 0x0020 +#define CMD_ACCESS 0x0021 +#define CMD_PCIBAP 0x0022 +#define CMD_PCIAUX 0x0023 +#define CMD_ALLOCBUF 0x0028 +#define CMD_GETTLV 0x0029 +#define CMD_PUTTLV 0x002a +#define CMD_DELTLV 0x002b +#define CMD_FINDNEXTTLV 0x002c +#define CMD_PSPNODES 0x0030 +#define CMD_SETCW 0x0031 +#define CMD_SETPCF 0x0032 +#define CMD_SETPHYREG 0x003e +#define CMD_TXTEST 0x003f +#define MAC_ENABLETX 0x0101 +#define CMD_LISTBSS 0x0103 +#define CMD_SAVECFG 0x0108 +#define CMD_ENABLEAUX 0x0111 +#define CMD_WRITERID 0x0121 +#define CMD_USEPSPNODES 0x0130 +#define MAC_ENABLERX 0x0201 + +/* Command errors */ +#define ERROR_QUALIF 0x00 +#define ERROR_ILLCMD 0x01 +#define ERROR_ILLFMT 0x02 +#define ERROR_INVFID 0x03 +#define ERROR_INVRID 0x04 +#define ERROR_LARGE 0x05 +#define ERROR_NDISABL 0x06 +#define ERROR_ALLOCBSY 0x07 +#define ERROR_NORD 0x0B +#define ERROR_NOWR 0x0C +#define ERROR_INVFIDTX 0x0D +#define ERROR_TESTACT 0x0E +#define ERROR_TAGNFND 0x12 +#define ERROR_DECODE 0x20 +#define ERROR_DESCUNAV 0x21 +#define ERROR_BADLEN 0x22 +#define ERROR_MODE 0x80 +#define ERROR_HOP 0x81 +#define ERROR_BINTER 0x82 +#define ERROR_RXMODE 0x83 +#define ERROR_MACADDR 0x84 +#define ERROR_RATES 0x85 +#define ERROR_ORDER 0x86 +#define ERROR_SCAN 0x87 +#define ERROR_AUTH 0x88 +#define ERROR_PSMODE 0x89 +#define ERROR_RTYPE 0x8A +#define ERROR_DIVER 0x8B +#define ERROR_SSID 0x8C +#define ERROR_APLIST 0x8D +#define ERROR_AUTOWAKE 0x8E +#define ERROR_LEAP 0x8F + +/* Registers */ +#define COMMAND 0x00 +#define PARAM0 0x02 +#define PARAM1 0x04 +#define PARAM2 0x06 +#define STATUS 0x08 +#define RESP0 0x0a +#define RESP1 0x0c +#define RESP2 0x0e +#define LINKSTAT 0x10 +#define SELECT0 0x18 +#define OFFSET0 0x1c +#define RXFID 0x20 +#define TXALLOCFID 0x22 +#define TXCOMPLFID 0x24 +#define DATA0 0x36 +#define EVSTAT 0x30 +#define EVINTEN 0x32 +#define EVACK 0x34 +#define SWS0 0x28 +#define SWS1 0x2a +#define SWS2 0x2c +#define SWS3 0x2e +#define AUXPAGE 0x3A +#define AUXOFF 0x3C +#define AUXDATA 0x3E + +#define FID_TX 1 +#define FID_RX 2 +/* Offset into aux memory for descriptors */ +#define AUX_OFFSET 0x800 +/* Size of allocated packets */ +#define PKTSIZE 1840 +#define RIDSIZE 2048 +/* Size of the transmit queue */ +#define MAXTXQ 64 + +/* BAP selectors */ +#define BAP0 0 /* Used for receiving packets */ +#define BAP1 2 /* Used for xmiting packets and working with RIDS */ + +/* Flags */ +#define COMMAND_BUSY 0x8000 + +#define BAP_BUSY 0x8000 +#define BAP_ERR 0x4000 +#define BAP_DONE 0x2000 + +#define PROMISC 0xffff +#define NOPROMISC 0x0000 + +#define EV_CMD 0x10 +#define EV_CLEARCOMMANDBUSY 0x4000 +#define EV_RX 0x01 +#define EV_TX 0x02 +#define EV_TXEXC 0x04 +#define EV_ALLOC 0x08 +#define EV_LINK 0x80 +#define EV_AWAKE 0x100 +#define EV_TXCPY 0x400 +#define EV_UNKNOWN 0x800 +#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */ +#define EV_AWAKEN 0x2000 +#define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC) + +#ifdef CHECK_UNKNOWN_INTS +#define IGNORE_INTS ( EV_CMD | EV_UNKNOWN) +#else +#define IGNORE_INTS (~STATUS_INTS) +#endif + +/* RID TYPES */ +#define RID_RW 0x20 + +/* The RIDs */ +#define RID_CAPABILITIES 0xFF00 +#define RID_APINFO 0xFF01 +#define RID_RADIOINFO 0xFF02 +#define RID_UNKNOWN3 0xFF03 +#define RID_RSSI 0xFF04 +#define RID_CONFIG 0xFF10 +#define RID_SSID 0xFF11 +#define RID_APLIST 0xFF12 +#define RID_DRVNAME 0xFF13 +#define RID_ETHERENCAP 0xFF14 +#define RID_WEP_TEMP 0xFF15 +#define RID_WEP_PERM 0xFF16 +#define RID_MODULATION 0xFF17 +#define RID_OPTIONS 0xFF18 +#define RID_ACTUALCONFIG 0xFF20 /*readonly*/ +#define RID_FACTORYCONFIG 0xFF21 +#define RID_UNKNOWN22 0xFF22 +#define RID_LEAPUSERNAME 0xFF23 +#define RID_LEAPPASSWORD 0xFF24 +#define RID_STATUS 0xFF50 +#define RID_BEACON_HST 0xFF51 +#define RID_BUSY_HST 0xFF52 +#define RID_RETRIES_HST 0xFF53 +#define RID_UNKNOWN54 0xFF54 +#define RID_UNKNOWN55 0xFF55 +#define RID_UNKNOWN56 0xFF56 +#define RID_MIC 0xFF57 +#define RID_STATS16 0xFF60 +#define RID_STATS16DELTA 0xFF61 +#define RID_STATS16DELTACLEAR 0xFF62 +#define RID_STATS 0xFF68 +#define RID_STATSDELTA 0xFF69 +#define RID_STATSDELTACLEAR 0xFF6A +#define RID_ECHOTEST_RID 0xFF70 +#define RID_ECHOTEST_RESULTS 0xFF71 +#define RID_BSSLISTFIRST 0xFF72 +#define RID_BSSLISTNEXT 0xFF73 +#define RID_WPA_BSSLISTFIRST 0xFF74 +#define RID_WPA_BSSLISTNEXT 0xFF75 + +typedef struct { + u16 cmd; + u16 parm0; + u16 parm1; + u16 parm2; +} Cmd; + +typedef struct { + u16 status; + u16 rsp0; + u16 rsp1; + u16 rsp2; +} Resp; + +/* + * Rids and endian-ness: The Rids will always be in cpu endian, since + * this all the patches from the big-endian guys end up doing that. + * so all rid access should use the read/writeXXXRid routines. + */ + +/* This structure came from an email sent to me from an engineer at + aironet for inclusion into this driver */ +typedef struct WepKeyRid WepKeyRid; +struct WepKeyRid { + __le16 len; + __le16 kindex; + u8 mac[ETH_ALEN]; + __le16 klen; + u8 key[16]; +} __packed; + +/* These structures are from the Aironet's PC4500 Developers Manual */ +typedef struct Ssid Ssid; +struct Ssid { + __le16 len; + u8 ssid[32]; +} __packed; + +typedef struct SsidRid SsidRid; +struct SsidRid { + __le16 len; + Ssid ssids[3]; +} __packed; + +typedef struct ModulationRid ModulationRid; +struct ModulationRid { + __le16 len; + __le16 modulation; +#define MOD_DEFAULT cpu_to_le16(0) +#define MOD_CCK cpu_to_le16(1) +#define MOD_MOK cpu_to_le16(2) +} __packed; + +typedef struct ConfigRid ConfigRid; +struct ConfigRid { + __le16 len; /* sizeof(ConfigRid) */ + __le16 opmode; /* operating mode */ +#define MODE_STA_IBSS cpu_to_le16(0) +#define MODE_STA_ESS cpu_to_le16(1) +#define MODE_AP cpu_to_le16(2) +#define MODE_AP_RPTR cpu_to_le16(3) +#define MODE_CFG_MASK cpu_to_le16(0xff) +#define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */ +#define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */ +#define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extenstions */ +#define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */ +#define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */ +#define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */ +#define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */ +#define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */ +#define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */ + __le16 rmode; /* receive mode */ +#define RXMODE_BC_MC_ADDR cpu_to_le16(0) +#define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */ +#define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */ +#define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */ +#define RXMODE_RFMON_ANYBSS cpu_to_le16(4) +#define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */ +#define RXMODE_MASK cpu_to_le16(255) +#define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */ +#define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER) +#define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */ + __le16 fragThresh; + __le16 rtsThres; + u8 macAddr[ETH_ALEN]; + u8 rates[8]; + __le16 shortRetryLimit; + __le16 longRetryLimit; + __le16 txLifetime; /* in kusec */ + __le16 rxLifetime; /* in kusec */ + __le16 stationary; + __le16 ordering; + __le16 u16deviceType; /* for overriding device type */ + __le16 cfpRate; + __le16 cfpDuration; + __le16 _reserved1[3]; + /*---------- Scanning/Associating ----------*/ + __le16 scanMode; +#define SCANMODE_ACTIVE cpu_to_le16(0) +#define SCANMODE_PASSIVE cpu_to_le16(1) +#define SCANMODE_AIROSCAN cpu_to_le16(2) + __le16 probeDelay; /* in kusec */ + __le16 probeEnergyTimeout; /* in kusec */ + __le16 probeResponseTimeout; + __le16 beaconListenTimeout; + __le16 joinNetTimeout; + __le16 authTimeout; + __le16 authType; +#define AUTH_OPEN cpu_to_le16(0x1) +#define AUTH_ENCRYPT cpu_to_le16(0x101) +#define AUTH_SHAREDKEY cpu_to_le16(0x102) +#define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200) + __le16 associationTimeout; + __le16 specifiedApTimeout; + __le16 offlineScanInterval; + __le16 offlineScanDuration; + __le16 linkLossDelay; + __le16 maxBeaconLostTime; + __le16 refreshInterval; +#define DISABLE_REFRESH cpu_to_le16(0xFFFF) + __le16 _reserved1a[1]; + /*---------- Power save operation ----------*/ + __le16 powerSaveMode; +#define POWERSAVE_CAM cpu_to_le16(0) +#define POWERSAVE_PSP cpu_to_le16(1) +#define POWERSAVE_PSPCAM cpu_to_le16(2) + __le16 sleepForDtims; + __le16 listenInterval; + __le16 fastListenInterval; + __le16 listenDecay; + __le16 fastListenDelay; + __le16 _reserved2[2]; + /*---------- Ap/Ibss config items ----------*/ + __le16 beaconPeriod; + __le16 atimDuration; + __le16 hopPeriod; + __le16 channelSet; + __le16 channel; + __le16 dtimPeriod; + __le16 bridgeDistance; + __le16 radioID; + /*---------- Radio configuration ----------*/ + __le16 radioType; +#define RADIOTYPE_DEFAULT cpu_to_le16(0) +#define RADIOTYPE_802_11 cpu_to_le16(1) +#define RADIOTYPE_LEGACY cpu_to_le16(2) + u8 rxDiversity; + u8 txDiversity; + __le16 txPower; +#define TXPOWER_DEFAULT 0 + __le16 rssiThreshold; +#define RSSI_DEFAULT 0 + __le16 modulation; +#define PREAMBLE_AUTO cpu_to_le16(0) +#define PREAMBLE_LONG cpu_to_le16(1) +#define PREAMBLE_SHORT cpu_to_le16(2) + __le16 preamble; + __le16 homeProduct; + __le16 radioSpecific; + /*---------- Aironet Extensions ----------*/ + u8 nodeName[16]; + __le16 arlThreshold; + __le16 arlDecay; + __le16 arlDelay; + __le16 _reserved4[1]; + /*---------- Aironet Extensions ----------*/ + u8 magicAction; +#define MAGIC_ACTION_STSCHG 1 +#define MAGIC_ACTION_RESUME 2 +#define MAGIC_IGNORE_MCAST (1<<8) +#define MAGIC_IGNORE_BCAST (1<<9) +#define MAGIC_SWITCH_TO_PSP (0<<10) +#define MAGIC_STAY_IN_CAM (1<<10) + u8 magicControl; + __le16 autoWake; +} __packed; + +typedef struct StatusRid StatusRid; +struct StatusRid { + __le16 len; + u8 mac[ETH_ALEN]; + __le16 mode; + __le16 errorCode; + __le16 sigQuality; + __le16 SSIDlen; + char SSID[32]; + char apName[16]; + u8 bssid[4][ETH_ALEN]; + __le16 beaconPeriod; + __le16 dimPeriod; + __le16 atimDuration; + __le16 hopPeriod; + __le16 channelSet; + __le16 channel; + __le16 hopsToBackbone; + __le16 apTotalLoad; + __le16 generatedLoad; + __le16 accumulatedArl; + __le16 signalQuality; + __le16 currentXmitRate; + __le16 apDevExtensions; + __le16 normalizedSignalStrength; + __le16 shortPreamble; + u8 apIP[4]; + u8 noisePercent; /* Noise percent in last second */ + u8 noisedBm; /* Noise dBm in last second */ + u8 noiseAvePercent; /* Noise percent in last minute */ + u8 noiseAvedBm; /* Noise dBm in last minute */ + u8 noiseMaxPercent; /* Highest noise percent in last minute */ + u8 noiseMaxdBm; /* Highest noise dbm in last minute */ + __le16 load; + u8 carrier[4]; + __le16 assocStatus; +#define STAT_NOPACKETS 0 +#define STAT_NOCARRIERSET 10 +#define STAT_GOTCARRIERSET 11 +#define STAT_WRONGSSID 20 +#define STAT_BADCHANNEL 25 +#define STAT_BADBITRATES 30 +#define STAT_BADPRIVACY 35 +#define STAT_APFOUND 40 +#define STAT_APREJECTED 50 +#define STAT_AUTHENTICATING 60 +#define STAT_DEAUTHENTICATED 61 +#define STAT_AUTHTIMEOUT 62 +#define STAT_ASSOCIATING 70 +#define STAT_DEASSOCIATED 71 +#define STAT_ASSOCTIMEOUT 72 +#define STAT_NOTAIROAP 73 +#define STAT_ASSOCIATED 80 +#define STAT_LEAPING 90 +#define STAT_LEAPFAILED 91 +#define STAT_LEAPTIMEDOUT 92 +#define STAT_LEAPCOMPLETE 93 +} __packed; + +typedef struct StatsRid StatsRid; +struct StatsRid { + __le16 len; + __le16 spacer; + __le32 vals[100]; +} __packed; + +typedef struct APListRid APListRid; +struct APListRid { + __le16 len; + u8 ap[4][ETH_ALEN]; +} __packed; + +typedef struct CapabilityRid CapabilityRid; +struct CapabilityRid { + __le16 len; + char oui[3]; + char zero; + __le16 prodNum; + char manName[32]; + char prodName[16]; + char prodVer[8]; + char factoryAddr[ETH_ALEN]; + char aironetAddr[ETH_ALEN]; + __le16 radioType; + __le16 country; + char callid[ETH_ALEN]; + char supportedRates[8]; + char rxDiversity; + char txDiversity; + __le16 txPowerLevels[8]; + __le16 hardVer; + __le16 hardCap; + __le16 tempRange; + __le16 softVer; + __le16 softSubVer; + __le16 interfaceVer; + __le16 softCap; + __le16 bootBlockVer; + __le16 requiredHard; + __le16 extSoftCap; +} __packed; + +/* Only present on firmware >= 5.30.17 */ +typedef struct BSSListRidExtra BSSListRidExtra; +struct BSSListRidExtra { + __le16 unknown[4]; + u8 fixed[12]; /* WLAN management frame */ + u8 iep[624]; +} __packed; + +typedef struct BSSListRid BSSListRid; +struct BSSListRid { + __le16 len; + __le16 index; /* First is 0 and 0xffff means end of list */ +#define RADIO_FH 1 /* Frequency hopping radio type */ +#define RADIO_DS 2 /* Direct sequence radio type */ +#define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */ + __le16 radioType; + u8 bssid[ETH_ALEN]; /* Mac address of the BSS */ + u8 zero; + u8 ssidLen; + u8 ssid[32]; + __le16 dBm; +#define CAP_ESS cpu_to_le16(1<<0) +#define CAP_IBSS cpu_to_le16(1<<1) +#define CAP_PRIVACY cpu_to_le16(1<<4) +#define CAP_SHORTHDR cpu_to_le16(1<<5) + __le16 cap; + __le16 beaconInterval; + u8 rates[8]; /* Same as rates for config rid */ + struct { /* For frequency hopping only */ + __le16 dwell; + u8 hopSet; + u8 hopPattern; + u8 hopIndex; + u8 fill; + } fh; + __le16 dsChannel; + __le16 atimWindow; + + /* Only present on firmware >= 5.30.17 */ + BSSListRidExtra extra; +} __packed; + +typedef struct { + BSSListRid bss; + struct list_head list; +} BSSListElement; + +typedef struct tdsRssiEntry tdsRssiEntry; +struct tdsRssiEntry { + u8 rssipct; + u8 rssidBm; +} __packed; + +typedef struct tdsRssiRid tdsRssiRid; +struct tdsRssiRid { + u16 len; + tdsRssiEntry x[256]; +} __packed; + +typedef struct MICRid MICRid; +struct MICRid { + __le16 len; + __le16 state; + __le16 multicastValid; + u8 multicast[16]; + __le16 unicastValid; + u8 unicast[16]; +} __packed; + +typedef struct MICBuffer MICBuffer; +struct MICBuffer { + __be16 typelen; + + union { + u8 snap[8]; + struct { + u8 dsap; + u8 ssap; + u8 control; + u8 orgcode[3]; + u8 fieldtype[2]; + } llc; + } u; + __be32 mic; + __be32 seq; +} __packed; + +typedef struct { + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; +} etherHead; + +#define TXCTL_TXOK (1<<1) /* report if tx is ok */ +#define TXCTL_TXEX (1<<2) /* report if tx fails */ +#define TXCTL_802_3 (0<<3) /* 802.3 packet */ +#define TXCTL_802_11 (1<<3) /* 802.11 mac packet */ +#define TXCTL_ETHERNET (0<<4) /* payload has ethertype */ +#define TXCTL_LLC (1<<4) /* payload is llc */ +#define TXCTL_RELEASE (0<<5) /* release after completion */ +#define TXCTL_NORELEASE (1<<5) /* on completion returns to host */ + +#define BUSY_FID 0x10000 + +#ifdef CISCO_EXT +#define AIROMAGIC 0xa55a +/* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */ +#ifdef SIOCIWFIRSTPRIV +#ifdef SIOCDEVPRIVATE +#define AIROOLDIOCTL SIOCDEVPRIVATE +#define AIROOLDIDIFC AIROOLDIOCTL + 1 +#endif /* SIOCDEVPRIVATE */ +#else /* SIOCIWFIRSTPRIV */ +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif /* SIOCIWFIRSTPRIV */ +/* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably + * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root + * only and don't return the modified struct ifreq to the application which + * is usually a problem. - Jean II */ +#define AIROIOCTL SIOCIWFIRSTPRIV +#define AIROIDIFC AIROIOCTL + 1 + +/* Ioctl constants to be used in airo_ioctl.command */ + +#define AIROGCAP 0 // Capability rid +#define AIROGCFG 1 // USED A LOT +#define AIROGSLIST 2 // System ID list +#define AIROGVLIST 3 // List of specified AP's +#define AIROGDRVNAM 4 // NOTUSED +#define AIROGEHTENC 5 // NOTUSED +#define AIROGWEPKTMP 6 +#define AIROGWEPKNV 7 +#define AIROGSTAT 8 +#define AIROGSTATSC32 9 +#define AIROGSTATSD32 10 +#define AIROGMICRID 11 +#define AIROGMICSTATS 12 +#define AIROGFLAGS 13 +#define AIROGID 14 +#define AIRORRID 15 +#define AIRORSWVERSION 17 + +/* Leave gap of 40 commands after AIROGSTATSD32 for future */ + +#define AIROPCAP AIROGSTATSD32 + 40 +#define AIROPVLIST AIROPCAP + 1 +#define AIROPSLIST AIROPVLIST + 1 +#define AIROPCFG AIROPSLIST + 1 +#define AIROPSIDS AIROPCFG + 1 +#define AIROPAPLIST AIROPSIDS + 1 +#define AIROPMACON AIROPAPLIST + 1 /* Enable mac */ +#define AIROPMACOFF AIROPMACON + 1 /* Disable mac */ +#define AIROPSTCLR AIROPMACOFF + 1 +#define AIROPWEPKEY AIROPSTCLR + 1 +#define AIROPWEPKEYNV AIROPWEPKEY + 1 +#define AIROPLEAPPWD AIROPWEPKEYNV + 1 +#define AIROPLEAPUSR AIROPLEAPPWD + 1 + +/* Flash codes */ + +#define AIROFLSHRST AIROPWEPKEYNV + 40 +#define AIROFLSHGCHR AIROFLSHRST + 1 +#define AIROFLSHSTFL AIROFLSHGCHR + 1 +#define AIROFLSHPCHR AIROFLSHSTFL + 1 +#define AIROFLPUTBUF AIROFLSHPCHR + 1 +#define AIRORESTART AIROFLPUTBUF + 1 + +#define FLASHSIZE 32768 +#define AUXMEMSIZE (256 * 1024) + +typedef struct aironet_ioctl { + unsigned short command; // What to do + unsigned short len; // Len of data + unsigned short ridnum; // rid number + unsigned char __user *data; // d-data +} aironet_ioctl; + +static const char swversion[] = "2.1"; +#endif /* CISCO_EXT */ + +#define NUM_MODULES 2 +#define MIC_MSGLEN_MAX 2400 +#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX +#define AIRO_DEF_MTU 2312 + +typedef struct { + u32 size; // size + u8 enabled; // MIC enabled or not + u32 rxSuccess; // successful packets received + u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison + u32 rxNotMICed; // pkts dropped due to not being MIC'd + u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed + u32 rxWrongSequence; // pkts dropped due to sequence number violation + u32 reserve[32]; +} mic_statistics; + +typedef struct { + u32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2]; + u64 accum; // accumulated mic, reduced to u32 in final() + int position; // current position (byte offset) in message + union { + u8 d8[4]; + __be32 d32; + } part; // saves partial message word across update() calls +} emmh32_context; + +typedef struct { + emmh32_context seed; // Context - the seed + u32 rx; // Received sequence number + u32 tx; // Tx sequence number + u32 window; // Start of window + u8 valid; // Flag to say if context is valid or not + u8 key[16]; +} miccntx; + +typedef struct { + miccntx mCtx; // Multicast context + miccntx uCtx; // Unicast context +} mic_module; + +typedef struct { + unsigned int rid: 16; + unsigned int len: 15; + unsigned int valid: 1; + dma_addr_t host_addr; +} Rid; + +typedef struct { + unsigned int offset: 15; + unsigned int eoc: 1; + unsigned int len: 15; + unsigned int valid: 1; + dma_addr_t host_addr; +} TxFid; + +struct rx_hdr { + __le16 status, len; + u8 rssi[2]; + u8 rate; + u8 freq; + __le16 tmp[4]; +} __packed; + +typedef struct { + unsigned int ctl: 15; + unsigned int rdy: 1; + unsigned int len: 15; + unsigned int valid: 1; + dma_addr_t host_addr; +} RxFid; + +/* + * Host receive descriptor + */ +typedef struct { + unsigned char __iomem *card_ram_off; /* offset into card memory of the + desc */ + RxFid rx_desc; /* card receive descriptor */ + char *virtual_host_addr; /* virtual address of host receive + buffer */ + int pending; +} HostRxDesc; + +/* + * Host transmit descriptor + */ +typedef struct { + unsigned char __iomem *card_ram_off; /* offset into card memory of the + desc */ + TxFid tx_desc; /* card transmit descriptor */ + char *virtual_host_addr; /* virtual address of host receive + buffer */ + int pending; +} HostTxDesc; + +/* + * Host RID descriptor + */ +typedef struct { + unsigned char __iomem *card_ram_off; /* offset into card memory of the + descriptor */ + Rid rid_desc; /* card RID descriptor */ + char *virtual_host_addr; /* virtual address of host receive + buffer */ +} HostRidDesc; + +typedef struct { + u16 sw0; + u16 sw1; + u16 status; + u16 len; +#define HOST_SET (1 << 0) +#define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */ +#define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */ +#define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */ +#define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */ +#define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */ +#define HOST_CLR_AID (1 << 7) /* clear AID failure */ +#define HOST_RTS (1 << 9) /* Force RTS use */ +#define HOST_SHORT (1 << 10) /* Do short preamble */ + u16 ctl; + u16 aid; + u16 retries; + u16 fill; +} TxCtlHdr; + +typedef struct { + u16 ctl; + u16 duration; + char addr1[6]; + char addr2[6]; + char addr3[6]; + u16 seq; + char addr4[6]; +} WifiHdr; + + +typedef struct { + TxCtlHdr ctlhdr; + u16 fill1; + u16 fill2; + WifiHdr wifihdr; + u16 gaplen; + u16 status; +} WifiCtlHdr; + +static WifiCtlHdr wifictlhdr8023 = { + .ctlhdr = { + .ctl = HOST_DONT_RLSE, + } +}; + +// A few details needed for WEP (Wireless Equivalent Privacy) +#define MAX_KEY_SIZE 13 // 128 (?) bits +#define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP +typedef struct wep_key_t { + u16 len; + u8 key[16]; /* 40-bit and 104-bit keys */ +} wep_key_t; + +/* List of Wireless Handlers (new API) */ +static const struct iw_handler_def airo_handler_def; + +static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)"; + +struct airo_info; + +static int get_dec_u16( char *buffer, int *start, int limit ); +static void OUT4500( struct airo_info *, u16 register, u16 value ); +static unsigned short IN4500( struct airo_info *, u16 register ); +static u16 setup_card(struct airo_info*, u8 *mac, int lock); +static int enable_MAC(struct airo_info *ai, int lock); +static void disable_MAC(struct airo_info *ai, int lock); +static void enable_interrupts(struct airo_info*); +static void disable_interrupts(struct airo_info*); +static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp); +static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap); +static int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen, + int whichbap); +static int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen, + int whichbap); +static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen, + int whichbap); +static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd); +static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock); +static int PC4500_writerid(struct airo_info*, u16 rid, const void + *pBuf, int len, int lock); +static int do_writerid( struct airo_info*, u16 rid, const void *rid_data, + int len, int dummy ); +static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw); +static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket); +static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket); + +static int mpi_send_packet (struct net_device *dev); +static void mpi_unmap_card(struct pci_dev *pci); +static void mpi_receive_802_3(struct airo_info *ai); +static void mpi_receive_802_11(struct airo_info *ai); +static int waitbusy (struct airo_info *ai); + +static irqreturn_t airo_interrupt( int irq, void* dev_id); +static int airo_thread(void *data); +static void timer_func( struct net_device *dev ); +static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev); +static void airo_read_wireless_stats (struct airo_info *local); +#ifdef CISCO_EXT +static int readrids(struct net_device *dev, aironet_ioctl *comp); +static int writerids(struct net_device *dev, aironet_ioctl *comp); +static int flashcard(struct net_device *dev, aironet_ioctl *comp); +#endif /* CISCO_EXT */ +static void micinit(struct airo_info *ai); +static int micsetup(struct airo_info *ai); +static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len); +static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen); + +static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi); +static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm); + +static void airo_networks_free(struct airo_info *ai); + +struct airo_info { + struct net_device *dev; + struct list_head dev_list; + /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we + use the high bit to mark whether it is in use. */ +#define MAX_FIDS 6 +#define MPI_MAX_FIDS 1 + u32 fids[MAX_FIDS]; + ConfigRid config; + char keyindex; // Used with auto wep + char defindex; // Used with auto wep + struct proc_dir_entry *proc_entry; + spinlock_t aux_lock; +#define FLAG_RADIO_OFF 0 /* User disabling of MAC */ +#define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */ +#define FLAG_RADIO_MASK 0x03 +#define FLAG_ENABLED 2 +#define FLAG_ADHOC 3 /* Needed by MIC */ +#define FLAG_MIC_CAPABLE 4 +#define FLAG_UPDATE_MULTI 5 +#define FLAG_UPDATE_UNI 6 +#define FLAG_802_11 7 +#define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */ +#define FLAG_PENDING_XMIT 9 +#define FLAG_PENDING_XMIT11 10 +#define FLAG_MPI 11 +#define FLAG_REGISTERED 12 +#define FLAG_COMMIT 13 +#define FLAG_RESET 14 +#define FLAG_FLASHING 15 +#define FLAG_WPA_CAPABLE 16 + unsigned long flags; +#define JOB_DIE 0 +#define JOB_XMIT 1 +#define JOB_XMIT11 2 +#define JOB_STATS 3 +#define JOB_PROMISC 4 +#define JOB_MIC 5 +#define JOB_EVENT 6 +#define JOB_AUTOWEP 7 +#define JOB_WSTATS 8 +#define JOB_SCAN_RESULTS 9 + unsigned long jobs; + int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen, + int whichbap); + unsigned short *flash; + tdsRssiEntry *rssi; + struct task_struct *list_bss_task; + struct task_struct *airo_thread_task; + struct semaphore sem; + wait_queue_head_t thr_wait; + unsigned long expires; + struct { + struct sk_buff *skb; + int fid; + } xmit, xmit11; + struct net_device *wifidev; + struct iw_statistics wstats; // wireless stats + unsigned long scan_timeout; /* Time scan should be read */ + struct iw_spy_data spy_data; + struct iw_public_data wireless_data; + /* MIC stuff */ + struct crypto_cipher *tfm; + mic_module mod[2]; + mic_statistics micstats; + HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors + HostTxDesc txfids[MPI_MAX_FIDS]; + HostRidDesc config_desc; + unsigned long ridbus; // phys addr of config_desc + struct sk_buff_head txq;// tx queue used by mpi350 code + struct pci_dev *pci; + unsigned char __iomem *pcimem; + unsigned char __iomem *pciaux; + unsigned char *shared; + dma_addr_t shared_dma; + pm_message_t power; + SsidRid *SSID; + APListRid APList; +#define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE + char proc_name[IFNAMSIZ]; + + int wep_capable; + int max_wep_idx; + int last_auth; + + /* WPA-related stuff */ + unsigned int bssListFirst; + unsigned int bssListNext; + unsigned int bssListRidLen; + + struct list_head network_list; + struct list_head network_free_list; + BSSListElement *networks; +}; + +static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen, + int whichbap) +{ + return ai->bap_read(ai, pu16Dst, bytelen, whichbap); +} + +static int setup_proc_entry( struct net_device *dev, + struct airo_info *apriv ); +static int takedown_proc_entry( struct net_device *dev, + struct airo_info *apriv ); + +static int cmdreset(struct airo_info *ai); +static int setflashmode (struct airo_info *ai); +static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime); +static int flashputbuf(struct airo_info *ai); +static int flashrestart(struct airo_info *ai,struct net_device *dev); + +#define airo_print(type, name, fmt, args...) \ + printk(type DRV_NAME "(%s): " fmt "\n", name, ##args) + +#define airo_print_info(name, fmt, args...) \ + airo_print(KERN_INFO, name, fmt, ##args) + +#define airo_print_dbg(name, fmt, args...) \ + airo_print(KERN_DEBUG, name, fmt, ##args) + +#define airo_print_warn(name, fmt, args...) \ + airo_print(KERN_WARNING, name, fmt, ##args) + +#define airo_print_err(name, fmt, args...) \ + airo_print(KERN_ERR, name, fmt, ##args) + +#define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash) + +/*********************************************************************** + * MIC ROUTINES * + *********************************************************************** + */ + +static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq); +static void MoveWindow(miccntx *context, u32 micSeq); +static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, + struct crypto_cipher *tfm); +static void emmh32_init(emmh32_context *context); +static void emmh32_update(emmh32_context *context, u8 *pOctets, int len); +static void emmh32_final(emmh32_context *context, u8 digest[4]); +static int flashpchar(struct airo_info *ai,int byte,int dwelltime); + +static void age_mic_context(miccntx *cur, miccntx *old, u8 *key, int key_len, + struct crypto_cipher *tfm) +{ + /* If the current MIC context is valid and its key is the same as + * the MIC register, there's nothing to do. + */ + if (cur->valid && (memcmp(cur->key, key, key_len) == 0)) + return; + + /* Age current mic Context */ + memcpy(old, cur, sizeof(*cur)); + + /* Initialize new context */ + memcpy(cur->key, key, key_len); + cur->window = 33; /* Window always points to the middle */ + cur->rx = 0; /* Rx Sequence numbers */ + cur->tx = 0; /* Tx sequence numbers */ + cur->valid = 1; /* Key is now valid */ + + /* Give key to mic seed */ + emmh32_setseed(&cur->seed, key, key_len, tfm); +} + +/* micinit - Initialize mic seed */ + +static void micinit(struct airo_info *ai) +{ + MICRid mic_rid; + + clear_bit(JOB_MIC, &ai->jobs); + PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0); + up(&ai->sem); + + ai->micstats.enabled = (le16_to_cpu(mic_rid.state) & 0x00FF) ? 1 : 0; + if (!ai->micstats.enabled) { + /* So next time we have a valid key and mic is enabled, we will + * update the sequence number if the key is the same as before. + */ + ai->mod[0].uCtx.valid = 0; + ai->mod[0].mCtx.valid = 0; + return; + } + + if (mic_rid.multicastValid) { + age_mic_context(&ai->mod[0].mCtx, &ai->mod[1].mCtx, + mic_rid.multicast, sizeof(mic_rid.multicast), + ai->tfm); + } + + if (mic_rid.unicastValid) { + age_mic_context(&ai->mod[0].uCtx, &ai->mod[1].uCtx, + mic_rid.unicast, sizeof(mic_rid.unicast), + ai->tfm); + } +} + +/* micsetup - Get ready for business */ + +static int micsetup(struct airo_info *ai) { + int i; + + if (ai->tfm == NULL) + ai->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); + + if (IS_ERR(ai->tfm)) { + airo_print_err(ai->dev->name, "failed to load transform for AES"); + ai->tfm = NULL; + return ERROR; + } + + for (i=0; i < NUM_MODULES; i++) { + memset(&ai->mod[i].mCtx,0,sizeof(miccntx)); + memset(&ai->mod[i].uCtx,0,sizeof(miccntx)); + } + return SUCCESS; +} + +static const u8 micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02}; + +/*=========================================================================== + * Description: Mic a packet + * + * Inputs: etherHead * pointer to an 802.3 frame + * + * Returns: BOOLEAN if successful, otherwise false. + * PacketTxLen will be updated with the mic'd packets size. + * + * Caveats: It is assumed that the frame buffer will already + * be big enough to hold the largets mic message possible. + * (No memory allocation is done here). + * + * Author: sbraneky (10/15/01) + * Merciless hacks by rwilcher (1/14/02) + */ + +static int encapsulate(struct airo_info *ai ,etherHead *frame, MICBuffer *mic, int payLen) +{ + miccntx *context; + + // Determine correct context + // If not adhoc, always use unicast key + + if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1)) + context = &ai->mod[0].mCtx; + else + context = &ai->mod[0].uCtx; + + if (!context->valid) + return ERROR; + + mic->typelen = htons(payLen + 16); //Length of Mic'd packet + + memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap + + // Add Tx sequence + mic->seq = htonl(context->tx); + context->tx += 2; + + emmh32_init(&context->seed); // Mic the packet + emmh32_update(&context->seed,frame->da,ETH_ALEN * 2); // DA,SA + emmh32_update(&context->seed,(u8*)&mic->typelen,10); // Type/Length and Snap + emmh32_update(&context->seed,(u8*)&mic->seq,sizeof(mic->seq)); //SEQ + emmh32_update(&context->seed,(u8*)(frame + 1),payLen); //payload + emmh32_final(&context->seed, (u8*)&mic->mic); + + /* New Type/length ?????????? */ + mic->typelen = 0; //Let NIC know it could be an oversized packet + return SUCCESS; +} + +typedef enum { + NONE, + NOMIC, + NOMICPLUMMED, + SEQUENCE, + INCORRECTMIC, +} mic_error; + +/*=========================================================================== + * Description: Decapsulates a MIC'd packet and returns the 802.3 packet + * (removes the MIC stuff) if packet is a valid packet. + * + * Inputs: etherHead pointer to the 802.3 packet + * + * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE + * + * Author: sbraneky (10/15/01) + * Merciless hacks by rwilcher (1/14/02) + *--------------------------------------------------------------------------- + */ + +static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen) +{ + int i; + u32 micSEQ; + miccntx *context; + u8 digest[4]; + mic_error micError = NONE; + + // Check if the packet is a Mic'd packet + + if (!ai->micstats.enabled) { + //No Mic set or Mic OFF but we received a MIC'd packet. + if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) { + ai->micstats.rxMICPlummed++; + return ERROR; + } + return SUCCESS; + } + + if (ntohs(mic->typelen) == 0x888E) + return SUCCESS; + + if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) { + // Mic enabled but packet isn't Mic'd + ai->micstats.rxMICPlummed++; + return ERROR; + } + + micSEQ = ntohl(mic->seq); //store SEQ as CPU order + + //At this point we a have a mic'd packet and mic is enabled + //Now do the mic error checking. + + //Receive seq must be odd + if ( (micSEQ & 1) == 0 ) { + ai->micstats.rxWrongSequence++; + return ERROR; + } + + for (i = 0; i < NUM_MODULES; i++) { + int mcast = eth->da[0] & 1; + //Determine proper context + context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx; + + //Make sure context is valid + if (!context->valid) { + if (i == 0) + micError = NOMICPLUMMED; + continue; + } + //DeMic it + + if (!mic->typelen) + mic->typelen = htons(payLen + sizeof(MICBuffer) - 2); + + emmh32_init(&context->seed); + emmh32_update(&context->seed, eth->da, ETH_ALEN*2); + emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap)); + emmh32_update(&context->seed, (u8 *)&mic->seq,sizeof(mic->seq)); + emmh32_update(&context->seed, (u8 *)(eth + 1),payLen); + //Calculate MIC + emmh32_final(&context->seed, digest); + + if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match + //Invalid Mic + if (i == 0) + micError = INCORRECTMIC; + continue; + } + + //Check Sequence number if mics pass + if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) { + ai->micstats.rxSuccess++; + return SUCCESS; + } + if (i == 0) + micError = SEQUENCE; + } + + // Update statistics + switch (micError) { + case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break; + case SEQUENCE: ai->micstats.rxWrongSequence++; break; + case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break; + case NONE: break; + case NOMIC: break; + } + return ERROR; +} + +/*=========================================================================== + * Description: Checks the Rx Seq number to make sure it is valid + * and hasn't already been received + * + * Inputs: miccntx - mic context to check seq against + * micSeq - the Mic seq number + * + * Returns: TRUE if valid otherwise FALSE. + * + * Author: sbraneky (10/15/01) + * Merciless hacks by rwilcher (1/14/02) + *--------------------------------------------------------------------------- + */ + +static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq) +{ + u32 seq,index; + + //Allow for the ap being rebooted - if it is then use the next + //sequence number of the current sequence number - might go backwards + + if (mcast) { + if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) { + clear_bit (FLAG_UPDATE_MULTI, &ai->flags); + context->window = (micSeq > 33) ? micSeq : 33; + context->rx = 0; // Reset rx + } + } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) { + clear_bit (FLAG_UPDATE_UNI, &ai->flags); + context->window = (micSeq > 33) ? micSeq : 33; // Move window + context->rx = 0; // Reset rx + } + + //Make sequence number relative to START of window + seq = micSeq - (context->window - 33); + + //Too old of a SEQ number to check. + if ((s32)seq < 0) + return ERROR; + + if ( seq > 64 ) { + //Window is infinite forward + MoveWindow(context,micSeq); + return SUCCESS; + } + + // We are in the window. Now check the context rx bit to see if it was already sent + seq >>= 1; //divide by 2 because we only have odd numbers + index = 1 << seq; //Get an index number + + if (!(context->rx & index)) { + //micSEQ falls inside the window. + //Add seqence number to the list of received numbers. + context->rx |= index; + + MoveWindow(context,micSeq); + + return SUCCESS; + } + return ERROR; +} + +static void MoveWindow(miccntx *context, u32 micSeq) +{ + u32 shift; + + //Move window if seq greater than the middle of the window + if (micSeq > context->window) { + shift = (micSeq - context->window) >> 1; + + //Shift out old + if (shift < 32) + context->rx >>= shift; + else + context->rx = 0; + + context->window = micSeq; //Move window + } +} + +/*==============================================*/ +/*========== EMMH ROUTINES ====================*/ +/*==============================================*/ + +/* mic accumulate */ +#define MIC_ACCUM(val) \ + context->accum += (u64)(val) * context->coeff[coeff_position++]; + +static unsigned char aes_counter[16]; + +/* expand the key to fill the MMH coefficient array */ +static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, + struct crypto_cipher *tfm) +{ + /* take the keying material, expand if necessary, truncate at 16-bytes */ + /* run through AES counter mode to generate context->coeff[] */ + + int i,j; + u32 counter; + u8 *cipher, plain[16]; + + crypto_cipher_setkey(tfm, pkey, 16); + counter = 0; + for (i = 0; i < ARRAY_SIZE(context->coeff); ) { + aes_counter[15] = (u8)(counter >> 0); + aes_counter[14] = (u8)(counter >> 8); + aes_counter[13] = (u8)(counter >> 16); + aes_counter[12] = (u8)(counter >> 24); + counter++; + memcpy (plain, aes_counter, 16); + crypto_cipher_encrypt_one(tfm, plain, plain); + cipher = plain; + for (j = 0; (j < 16) && (i < ARRAY_SIZE(context->coeff)); ) { + context->coeff[i++] = ntohl(*(__be32 *)&cipher[j]); + j += 4; + } + } +} + +/* prepare for calculation of a new mic */ +static void emmh32_init(emmh32_context *context) +{ + /* prepare for new mic calculation */ + context->accum = 0; + context->position = 0; +} + +/* add some bytes to the mic calculation */ +static void emmh32_update(emmh32_context *context, u8 *pOctets, int len) +{ + int coeff_position, byte_position; + + if (len == 0) return; + + coeff_position = context->position >> 2; + + /* deal with partial 32-bit word left over from last update */ + byte_position = context->position & 3; + if (byte_position) { + /* have a partial word in part to deal with */ + do { + if (len == 0) return; + context->part.d8[byte_position++] = *pOctets++; + context->position++; + len--; + } while (byte_position < 4); + MIC_ACCUM(ntohl(context->part.d32)); + } + + /* deal with full 32-bit words */ + while (len >= 4) { + MIC_ACCUM(ntohl(*(__be32 *)pOctets)); + context->position += 4; + pOctets += 4; + len -= 4; + } + + /* deal with partial 32-bit word that will be left over from this update */ + byte_position = 0; + while (len > 0) { + context->part.d8[byte_position++] = *pOctets++; + context->position++; + len--; + } +} + +/* mask used to zero empty bytes for final partial word */ +static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L }; + +/* calculate the mic */ +static void emmh32_final(emmh32_context *context, u8 digest[4]) +{ + int coeff_position, byte_position; + u32 val; + + u64 sum, utmp; + s64 stmp; + + coeff_position = context->position >> 2; + + /* deal with partial 32-bit word left over from last update */ + byte_position = context->position & 3; + if (byte_position) { + /* have a partial word in part to deal with */ + val = ntohl(context->part.d32); + MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */ + } + + /* reduce the accumulated u64 to a 32-bit MIC */ + sum = context->accum; + stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15); + utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15); + sum = utmp & 0xffffffffLL; + if (utmp > 0x10000000fLL) + sum -= 15; + + val = (u32)sum; + digest[0] = (val>>24) & 0xFF; + digest[1] = (val>>16) & 0xFF; + digest[2] = (val>>8) & 0xFF; + digest[3] = val & 0xFF; +} + +static int readBSSListRid(struct airo_info *ai, int first, + BSSListRid *list) +{ + Cmd cmd; + Resp rsp; + + if (first == 1) { + if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN; + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd=CMD_LISTBSS; + if (down_interruptible(&ai->sem)) + return -ERESTARTSYS; + ai->list_bss_task = current; + issuecommand(ai, &cmd, &rsp); + up(&ai->sem); + /* Let the command take effect */ + schedule_timeout_uninterruptible(3 * HZ); + ai->list_bss_task = NULL; + } + return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext, + list, ai->bssListRidLen, 1); +} + +static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock) +{ + return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM, + wkr, sizeof(*wkr), lock); +} + +static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock) +{ + int rc; + rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock); + if (rc!=SUCCESS) + airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc); + if (perm) { + rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock); + if (rc!=SUCCESS) + airo_print_err(ai->dev->name, "WEP_PERM set %x", rc); + } + return rc; +} + +static int readSsidRid(struct airo_info*ai, SsidRid *ssidr) +{ + return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1); +} + +static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock) +{ + return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock); +} + +static int readConfigRid(struct airo_info *ai, int lock) +{ + int rc; + ConfigRid cfg; + + if (ai->config.len) + return SUCCESS; + + rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock); + if (rc != SUCCESS) + return rc; + + ai->config = cfg; + return SUCCESS; +} + +static inline void checkThrottle(struct airo_info *ai) +{ + int i; +/* Old hardware had a limit on encryption speed */ + if (ai->config.authType != AUTH_OPEN && maxencrypt) { + for(i=0; i<8; i++) { + if (ai->config.rates[i] > maxencrypt) { + ai->config.rates[i] = 0; + } + } + } +} + +static int writeConfigRid(struct airo_info *ai, int lock) +{ + ConfigRid cfgr; + + if (!test_bit (FLAG_COMMIT, &ai->flags)) + return SUCCESS; + + clear_bit (FLAG_COMMIT, &ai->flags); + clear_bit (FLAG_RESET, &ai->flags); + checkThrottle(ai); + cfgr = ai->config; + + if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS) + set_bit(FLAG_ADHOC, &ai->flags); + else + clear_bit(FLAG_ADHOC, &ai->flags); + + return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock); +} + +static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock) +{ + return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock); +} + +static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock) +{ + return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock); +} + +static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock) +{ + return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock); +} + +static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock) +{ + return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock); +} + +static void try_auto_wep(struct airo_info *ai) +{ + if (auto_wep && !test_bit(FLAG_RADIO_DOWN, &ai->flags)) { + ai->expires = RUN_AT(3*HZ); + wake_up_interruptible(&ai->thr_wait); + } +} + +static int airo_open(struct net_device *dev) { + struct airo_info *ai = dev->ml_priv; + int rc = 0; + + if (test_bit(FLAG_FLASHING, &ai->flags)) + return -EIO; + + /* Make sure the card is configured. + * Wireless Extensions may postpone config changes until the card + * is open (to pipeline changes and speed-up card setup). If + * those changes are not yet committed, do it now - Jean II */ + if (test_bit(FLAG_COMMIT, &ai->flags)) { + disable_MAC(ai, 1); + writeConfigRid(ai, 1); + } + + if (ai->wifidev != dev) { + clear_bit(JOB_DIE, &ai->jobs); + ai->airo_thread_task = kthread_run(airo_thread, dev, "%s", + dev->name); + if (IS_ERR(ai->airo_thread_task)) + return (int)PTR_ERR(ai->airo_thread_task); + + rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED, + dev->name, dev); + if (rc) { + airo_print_err(dev->name, + "register interrupt %d failed, rc %d", + dev->irq, rc); + set_bit(JOB_DIE, &ai->jobs); + kthread_stop(ai->airo_thread_task); + return rc; + } + + /* Power on the MAC controller (which may have been disabled) */ + clear_bit(FLAG_RADIO_DOWN, &ai->flags); + enable_interrupts(ai); + + try_auto_wep(ai); + } + enable_MAC(ai, 1); + + netif_start_queue(dev); + return 0; +} + +static netdev_tx_t mpi_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + int npacks, pending; + unsigned long flags; + struct airo_info *ai = dev->ml_priv; + + if (!skb) { + airo_print_err(dev->name, "%s: skb == NULL!",__func__); + return NETDEV_TX_OK; + } + npacks = skb_queue_len (&ai->txq); + + if (npacks >= MAXTXQ - 1) { + netif_stop_queue (dev); + if (npacks > MAXTXQ) { + dev->stats.tx_fifo_errors++; + return NETDEV_TX_BUSY; + } + skb_queue_tail (&ai->txq, skb); + return NETDEV_TX_OK; + } + + spin_lock_irqsave(&ai->aux_lock, flags); + skb_queue_tail (&ai->txq, skb); + pending = test_bit(FLAG_PENDING_XMIT, &ai->flags); + spin_unlock_irqrestore(&ai->aux_lock,flags); + netif_wake_queue (dev); + + if (pending == 0) { + set_bit(FLAG_PENDING_XMIT, &ai->flags); + mpi_send_packet (dev); + } + return NETDEV_TX_OK; +} + +/* + * @mpi_send_packet + * + * Attempt to transmit a packet. Can be called from interrupt + * or transmit . return number of packets we tried to send + */ + +static int mpi_send_packet (struct net_device *dev) +{ + struct sk_buff *skb; + unsigned char *buffer; + s16 len; + __le16 *payloadLen; + struct airo_info *ai = dev->ml_priv; + u8 *sendbuf; + + /* get a packet to send */ + + if ((skb = skb_dequeue(&ai->txq)) == NULL) { + airo_print_err(dev->name, + "%s: Dequeue'd zero in send_packet()", + __func__); + return 0; + } + + /* check min length*/ + len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + buffer = skb->data; + + ai->txfids[0].tx_desc.offset = 0; + ai->txfids[0].tx_desc.valid = 1; + ai->txfids[0].tx_desc.eoc = 1; + ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr); + +/* + * Magic, the cards firmware needs a length count (2 bytes) in the host buffer + * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen + * is immediately after it. ------------------------------------------------ + * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA| + * ------------------------------------------------ + */ + + memcpy(ai->txfids[0].virtual_host_addr, + (char *)&wifictlhdr8023, sizeof(wifictlhdr8023)); + + payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr + + sizeof(wifictlhdr8023)); + sendbuf = ai->txfids[0].virtual_host_addr + + sizeof(wifictlhdr8023) + 2 ; + + /* + * Firmware automatically puts 802 header on so + * we don't need to account for it in the length + */ + if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled && + (ntohs(((__be16 *)buffer)[6]) != 0x888E)) { + MICBuffer pMic; + + if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS) + return ERROR; + + *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic)); + ai->txfids[0].tx_desc.len += sizeof(pMic); + /* copy data into airo dma buffer */ + memcpy (sendbuf, buffer, sizeof(etherHead)); + buffer += sizeof(etherHead); + sendbuf += sizeof(etherHead); + memcpy (sendbuf, &pMic, sizeof(pMic)); + sendbuf += sizeof(pMic); + memcpy (sendbuf, buffer, len - sizeof(etherHead)); + } else { + *payloadLen = cpu_to_le16(len - sizeof(etherHead)); + + dev->trans_start = jiffies; + + /* copy data into airo dma buffer */ + memcpy(sendbuf, buffer, len); + } + + memcpy_toio(ai->txfids[0].card_ram_off, + &ai->txfids[0].tx_desc, sizeof(TxFid)); + + OUT4500(ai, EVACK, 8); + + dev_kfree_skb_any(skb); + return 1; +} + +static void get_tx_error(struct airo_info *ai, s32 fid) +{ + __le16 status; + + if (fid < 0) + status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status; + else { + if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS) + return; + bap_read(ai, &status, 2, BAP0); + } + if (le16_to_cpu(status) & 2) /* Too many retries */ + ai->dev->stats.tx_aborted_errors++; + if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */ + ai->dev->stats.tx_heartbeat_errors++; + if (le16_to_cpu(status) & 8) /* Aid fail */ + { } + if (le16_to_cpu(status) & 0x10) /* MAC disabled */ + ai->dev->stats.tx_carrier_errors++; + if (le16_to_cpu(status) & 0x20) /* Association lost */ + { } + /* We produce a TXDROP event only for retry or lifetime + * exceeded, because that's the only status that really mean + * that this particular node went away. + * Other errors means that *we* screwed up. - Jean II */ + if ((le16_to_cpu(status) & 2) || + (le16_to_cpu(status) & 4)) { + union iwreq_data wrqu; + char junk[0x18]; + + /* Faster to skip over useless data than to do + * another bap_setup(). We are at offset 0x6 and + * need to go to 0x18 and read 6 bytes - Jean II */ + bap_read(ai, (__le16 *) junk, 0x18, BAP0); + + /* Copy 802.11 dest address. + * We use the 802.11 header because the frame may + * not be 802.3 or may be mangled... + * In Ad-Hoc mode, it will be the node address. + * In managed mode, it will be most likely the AP addr + * User space will figure out how to convert it to + * whatever it needs (IP address or else). + * - Jean II */ + memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL); + } +} + +static void airo_end_xmit(struct net_device *dev) { + u16 status; + int i; + struct airo_info *priv = dev->ml_priv; + struct sk_buff *skb = priv->xmit.skb; + int fid = priv->xmit.fid; + u32 *fids = priv->fids; + + clear_bit(JOB_XMIT, &priv->jobs); + clear_bit(FLAG_PENDING_XMIT, &priv->flags); + status = transmit_802_3_packet (priv, fids[fid], skb->data); + up(&priv->sem); + + i = 0; + if ( status == SUCCESS ) { + dev->trans_start = jiffies; + for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++); + } else { + priv->fids[fid] &= 0xffff; + dev->stats.tx_window_errors++; + } + if (i < MAX_FIDS / 2) + netif_wake_queue(dev); + dev_kfree_skb(skb); +} + +static netdev_tx_t airo_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + s16 len; + int i, j; + struct airo_info *priv = dev->ml_priv; + u32 *fids = priv->fids; + + if ( skb == NULL ) { + airo_print_err(dev->name, "%s: skb == NULL!", __func__); + return NETDEV_TX_OK; + } + + /* Find a vacant FID */ + for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ ); + for( j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++ ); + + if ( j >= MAX_FIDS / 2 ) { + netif_stop_queue(dev); + + if (i == MAX_FIDS / 2) { + dev->stats.tx_fifo_errors++; + return NETDEV_TX_BUSY; + } + } + /* check min length*/ + len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + /* Mark fid as used & save length for later */ + fids[i] |= (len << 16); + priv->xmit.skb = skb; + priv->xmit.fid = i; + if (down_trylock(&priv->sem) != 0) { + set_bit(FLAG_PENDING_XMIT, &priv->flags); + netif_stop_queue(dev); + set_bit(JOB_XMIT, &priv->jobs); + wake_up_interruptible(&priv->thr_wait); + } else + airo_end_xmit(dev); + return NETDEV_TX_OK; +} + +static void airo_end_xmit11(struct net_device *dev) { + u16 status; + int i; + struct airo_info *priv = dev->ml_priv; + struct sk_buff *skb = priv->xmit11.skb; + int fid = priv->xmit11.fid; + u32 *fids = priv->fids; + + clear_bit(JOB_XMIT11, &priv->jobs); + clear_bit(FLAG_PENDING_XMIT11, &priv->flags); + status = transmit_802_11_packet (priv, fids[fid], skb->data); + up(&priv->sem); + + i = MAX_FIDS / 2; + if ( status == SUCCESS ) { + dev->trans_start = jiffies; + for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++); + } else { + priv->fids[fid] &= 0xffff; + dev->stats.tx_window_errors++; + } + if (i < MAX_FIDS) + netif_wake_queue(dev); + dev_kfree_skb(skb); +} + +static netdev_tx_t airo_start_xmit11(struct sk_buff *skb, + struct net_device *dev) +{ + s16 len; + int i, j; + struct airo_info *priv = dev->ml_priv; + u32 *fids = priv->fids; + + if (test_bit(FLAG_MPI, &priv->flags)) { + /* Not implemented yet for MPI350 */ + netif_stop_queue(dev); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + if ( skb == NULL ) { + airo_print_err(dev->name, "%s: skb == NULL!", __func__); + return NETDEV_TX_OK; + } + + /* Find a vacant FID */ + for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ ); + for( j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++ ); + + if ( j >= MAX_FIDS ) { + netif_stop_queue(dev); + + if (i == MAX_FIDS) { + dev->stats.tx_fifo_errors++; + return NETDEV_TX_BUSY; + } + } + /* check min length*/ + len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + /* Mark fid as used & save length for later */ + fids[i] |= (len << 16); + priv->xmit11.skb = skb; + priv->xmit11.fid = i; + if (down_trylock(&priv->sem) != 0) { + set_bit(FLAG_PENDING_XMIT11, &priv->flags); + netif_stop_queue(dev); + set_bit(JOB_XMIT11, &priv->jobs); + wake_up_interruptible(&priv->thr_wait); + } else + airo_end_xmit11(dev); + return NETDEV_TX_OK; +} + +static void airo_read_stats(struct net_device *dev) +{ + struct airo_info *ai = dev->ml_priv; + StatsRid stats_rid; + __le32 *vals = stats_rid.vals; + + clear_bit(JOB_STATS, &ai->jobs); + if (ai->power.event) { + up(&ai->sem); + return; + } + readStatsRid(ai, &stats_rid, RID_STATS, 0); + up(&ai->sem); + + dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) + + le32_to_cpu(vals[45]); + dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) + + le32_to_cpu(vals[41]); + dev->stats.rx_bytes = le32_to_cpu(vals[92]); + dev->stats.tx_bytes = le32_to_cpu(vals[91]); + dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) + + le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]); + dev->stats.tx_errors = le32_to_cpu(vals[42]) + + dev->stats.tx_fifo_errors; + dev->stats.multicast = le32_to_cpu(vals[43]); + dev->stats.collisions = le32_to_cpu(vals[89]); + + /* detailed rx_errors: */ + dev->stats.rx_length_errors = le32_to_cpu(vals[3]); + dev->stats.rx_crc_errors = le32_to_cpu(vals[4]); + dev->stats.rx_frame_errors = le32_to_cpu(vals[2]); + dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]); +} + +static struct net_device_stats *airo_get_stats(struct net_device *dev) +{ + struct airo_info *local = dev->ml_priv; + + if (!test_bit(JOB_STATS, &local->jobs)) { + /* Get stats out of the card if available */ + if (down_trylock(&local->sem) != 0) { + set_bit(JOB_STATS, &local->jobs); + wake_up_interruptible(&local->thr_wait); + } else + airo_read_stats(dev); + } + + return &dev->stats; +} + +static void airo_set_promisc(struct airo_info *ai) { + Cmd cmd; + Resp rsp; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd=CMD_SETMODE; + clear_bit(JOB_PROMISC, &ai->jobs); + cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC; + issuecommand(ai, &cmd, &rsp); + up(&ai->sem); +} + +static void airo_set_multicast_list(struct net_device *dev) { + struct airo_info *ai = dev->ml_priv; + + if ((dev->flags ^ ai->flags) & IFF_PROMISC) { + change_bit(FLAG_PROMISC, &ai->flags); + if (down_trylock(&ai->sem) != 0) { + set_bit(JOB_PROMISC, &ai->jobs); + wake_up_interruptible(&ai->thr_wait); + } else + airo_set_promisc(ai); + } + + if ((dev->flags&IFF_ALLMULTI) || !netdev_mc_empty(dev)) { + /* Turn on multicast. (Should be already setup...) */ + } +} + +static int airo_set_mac_address(struct net_device *dev, void *p) +{ + struct airo_info *ai = dev->ml_priv; + struct sockaddr *addr = p; + + readConfigRid(ai, 1); + memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len); + set_bit (FLAG_COMMIT, &ai->flags); + disable_MAC(ai, 1); + writeConfigRid (ai, 1); + enable_MAC(ai, 1); + memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len); + if (ai->wifidev) + memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len); + return 0; +} + +static int airo_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > 2400)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + +static LIST_HEAD(airo_devices); + +static void add_airo_dev(struct airo_info *ai) +{ + /* Upper layers already keep track of PCI devices, + * so we only need to remember our non-PCI cards. */ + if (!ai->pci) + list_add_tail(&ai->dev_list, &airo_devices); +} + +static void del_airo_dev(struct airo_info *ai) +{ + if (!ai->pci) + list_del(&ai->dev_list); +} + +static int airo_close(struct net_device *dev) { + struct airo_info *ai = dev->ml_priv; + + netif_stop_queue(dev); + + if (ai->wifidev != dev) { +#ifdef POWER_ON_DOWN + /* Shut power to the card. The idea is that the user can save + * power when he doesn't need the card with "ifconfig down". + * That's the method that is most friendly towards the network + * stack (i.e. the network stack won't try to broadcast + * anything on the interface and routes are gone. Jean II */ + set_bit(FLAG_RADIO_DOWN, &ai->flags); + disable_MAC(ai, 1); +#endif + disable_interrupts( ai ); + + free_irq(dev->irq, dev); + + set_bit(JOB_DIE, &ai->jobs); + kthread_stop(ai->airo_thread_task); + } + return 0; +} + +void stop_airo_card( struct net_device *dev, int freeres ) +{ + struct airo_info *ai = dev->ml_priv; + + set_bit(FLAG_RADIO_DOWN, &ai->flags); + disable_MAC(ai, 1); + disable_interrupts(ai); + takedown_proc_entry( dev, ai ); + if (test_bit(FLAG_REGISTERED, &ai->flags)) { + unregister_netdev( dev ); + if (ai->wifidev) { + unregister_netdev(ai->wifidev); + free_netdev(ai->wifidev); + ai->wifidev = NULL; + } + clear_bit(FLAG_REGISTERED, &ai->flags); + } + /* + * Clean out tx queue + */ + if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) { + struct sk_buff *skb = NULL; + for (;(skb = skb_dequeue(&ai->txq));) + dev_kfree_skb(skb); + } + + airo_networks_free (ai); + + kfree(ai->flash); + kfree(ai->rssi); + kfree(ai->SSID); + if (freeres) { + /* PCMCIA frees this stuff, so only for PCI and ISA */ + release_region( dev->base_addr, 64 ); + if (test_bit(FLAG_MPI, &ai->flags)) { + if (ai->pci) + mpi_unmap_card(ai->pci); + if (ai->pcimem) + iounmap(ai->pcimem); + if (ai->pciaux) + iounmap(ai->pciaux); + pci_free_consistent(ai->pci, PCI_SHARED_LEN, + ai->shared, ai->shared_dma); + } + } + crypto_free_cipher(ai->tfm); + del_airo_dev(ai); + free_netdev( dev ); +} + +EXPORT_SYMBOL(stop_airo_card); + +static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr) +{ + memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); + return ETH_ALEN; +} + +static void mpi_unmap_card(struct pci_dev *pci) +{ + unsigned long mem_start = pci_resource_start(pci, 1); + unsigned long mem_len = pci_resource_len(pci, 1); + unsigned long aux_start = pci_resource_start(pci, 2); + unsigned long aux_len = AUXMEMSIZE; + + release_mem_region(aux_start, aux_len); + release_mem_region(mem_start, mem_len); +} + +/************************************************************* + * This routine assumes that descriptors have been setup . + * Run at insmod time or after reset when the decriptors + * have been initialized . Returns 0 if all is well nz + * otherwise . Does not allocate memory but sets up card + * using previously allocated descriptors. + */ +static int mpi_init_descriptors (struct airo_info *ai) +{ + Cmd cmd; + Resp rsp; + int i; + int rc = SUCCESS; + + /* Alloc card RX descriptors */ + netif_stop_queue(ai->dev); + + memset(&rsp,0,sizeof(rsp)); + memset(&cmd,0,sizeof(cmd)); + + cmd.cmd = CMD_ALLOCATEAUX; + cmd.parm0 = FID_RX; + cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux); + cmd.parm2 = MPI_MAX_FIDS; + rc=issuecommand(ai, &cmd, &rsp); + if (rc != SUCCESS) { + airo_print_err(ai->dev->name, "Couldn't allocate RX FID"); + return rc; + } + + for (i=0; irxfids[i].card_ram_off, + &ai->rxfids[i].rx_desc, sizeof(RxFid)); + } + + /* Alloc card TX descriptors */ + + memset(&rsp,0,sizeof(rsp)); + memset(&cmd,0,sizeof(cmd)); + + cmd.cmd = CMD_ALLOCATEAUX; + cmd.parm0 = FID_TX; + cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux); + cmd.parm2 = MPI_MAX_FIDS; + + for (i=0; itxfids[i].tx_desc.valid = 1; + memcpy_toio(ai->txfids[i].card_ram_off, + &ai->txfids[i].tx_desc, sizeof(TxFid)); + } + ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */ + + rc=issuecommand(ai, &cmd, &rsp); + if (rc != SUCCESS) { + airo_print_err(ai->dev->name, "Couldn't allocate TX FID"); + return rc; + } + + /* Alloc card Rid descriptor */ + memset(&rsp,0,sizeof(rsp)); + memset(&cmd,0,sizeof(cmd)); + + cmd.cmd = CMD_ALLOCATEAUX; + cmd.parm0 = RID_RW; + cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux); + cmd.parm2 = 1; /* Magic number... */ + rc=issuecommand(ai, &cmd, &rsp); + if (rc != SUCCESS) { + airo_print_err(ai->dev->name, "Couldn't allocate RID"); + return rc; + } + + memcpy_toio(ai->config_desc.card_ram_off, + &ai->config_desc.rid_desc, sizeof(Rid)); + + return rc; +} + +/* + * We are setting up three things here: + * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid. + * 2) Map PCI memory for issuing commands. + * 3) Allocate memory (shared) to send and receive ethernet frames. + */ +static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci) +{ + unsigned long mem_start, mem_len, aux_start, aux_len; + int rc = -1; + int i; + dma_addr_t busaddroff; + unsigned char *vpackoff; + unsigned char __iomem *pciaddroff; + + mem_start = pci_resource_start(pci, 1); + mem_len = pci_resource_len(pci, 1); + aux_start = pci_resource_start(pci, 2); + aux_len = AUXMEMSIZE; + + if (!request_mem_region(mem_start, mem_len, DRV_NAME)) { + airo_print_err("", "Couldn't get region %x[%x]", + (int)mem_start, (int)mem_len); + goto out; + } + if (!request_mem_region(aux_start, aux_len, DRV_NAME)) { + airo_print_err("", "Couldn't get region %x[%x]", + (int)aux_start, (int)aux_len); + goto free_region1; + } + + ai->pcimem = ioremap(mem_start, mem_len); + if (!ai->pcimem) { + airo_print_err("", "Couldn't map region %x[%x]", + (int)mem_start, (int)mem_len); + goto free_region2; + } + ai->pciaux = ioremap(aux_start, aux_len); + if (!ai->pciaux) { + airo_print_err("", "Couldn't map region %x[%x]", + (int)aux_start, (int)aux_len); + goto free_memmap; + } + + /* Reserve PKTSIZE for each fid and 2K for the Rids */ + ai->shared = pci_alloc_consistent(pci, PCI_SHARED_LEN, &ai->shared_dma); + if (!ai->shared) { + airo_print_err("", "Couldn't alloc_consistent %d", + PCI_SHARED_LEN); + goto free_auxmap; + } + + /* + * Setup descriptor RX, TX, CONFIG + */ + busaddroff = ai->shared_dma; + pciaddroff = ai->pciaux + AUX_OFFSET; + vpackoff = ai->shared; + + /* RX descriptor setup */ + for(i = 0; i < MPI_MAX_FIDS; i++) { + ai->rxfids[i].pending = 0; + ai->rxfids[i].card_ram_off = pciaddroff; + ai->rxfids[i].virtual_host_addr = vpackoff; + ai->rxfids[i].rx_desc.host_addr = busaddroff; + ai->rxfids[i].rx_desc.valid = 1; + ai->rxfids[i].rx_desc.len = PKTSIZE; + ai->rxfids[i].rx_desc.rdy = 0; + + pciaddroff += sizeof(RxFid); + busaddroff += PKTSIZE; + vpackoff += PKTSIZE; + } + + /* TX descriptor setup */ + for(i = 0; i < MPI_MAX_FIDS; i++) { + ai->txfids[i].card_ram_off = pciaddroff; + ai->txfids[i].virtual_host_addr = vpackoff; + ai->txfids[i].tx_desc.valid = 1; + ai->txfids[i].tx_desc.host_addr = busaddroff; + memcpy(ai->txfids[i].virtual_host_addr, + &wifictlhdr8023, sizeof(wifictlhdr8023)); + + pciaddroff += sizeof(TxFid); + busaddroff += PKTSIZE; + vpackoff += PKTSIZE; + } + ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */ + + /* Rid descriptor setup */ + ai->config_desc.card_ram_off = pciaddroff; + ai->config_desc.virtual_host_addr = vpackoff; + ai->config_desc.rid_desc.host_addr = busaddroff; + ai->ridbus = busaddroff; + ai->config_desc.rid_desc.rid = 0; + ai->config_desc.rid_desc.len = RIDSIZE; + ai->config_desc.rid_desc.valid = 1; + pciaddroff += sizeof(Rid); + busaddroff += RIDSIZE; + vpackoff += RIDSIZE; + + /* Tell card about descriptors */ + if (mpi_init_descriptors (ai) != SUCCESS) + goto free_shared; + + return 0; + free_shared: + pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma); + free_auxmap: + iounmap(ai->pciaux); + free_memmap: + iounmap(ai->pcimem); + free_region2: + release_mem_region(aux_start, aux_len); + free_region1: + release_mem_region(mem_start, mem_len); + out: + return rc; +} + +static const struct header_ops airo_header_ops = { + .parse = wll_header_parse, +}; + +static const struct net_device_ops airo11_netdev_ops = { + .ndo_open = airo_open, + .ndo_stop = airo_close, + .ndo_start_xmit = airo_start_xmit11, + .ndo_get_stats = airo_get_stats, + .ndo_set_mac_address = airo_set_mac_address, + .ndo_do_ioctl = airo_ioctl, + .ndo_change_mtu = airo_change_mtu, +}; + +static void wifi_setup(struct net_device *dev) +{ + dev->netdev_ops = &airo11_netdev_ops; + dev->header_ops = &airo_header_ops; + dev->wireless_handlers = &airo_handler_def; + + dev->type = ARPHRD_IEEE80211; + dev->hard_header_len = ETH_HLEN; + dev->mtu = AIRO_DEF_MTU; + dev->addr_len = ETH_ALEN; + dev->tx_queue_len = 100; + + eth_broadcast_addr(dev->broadcast); + + dev->flags = IFF_BROADCAST|IFF_MULTICAST; +} + +static struct net_device *init_wifidev(struct airo_info *ai, + struct net_device *ethdev) +{ + int err; + struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN, + wifi_setup); + if (!dev) + return NULL; + dev->ml_priv = ethdev->ml_priv; + dev->irq = ethdev->irq; + dev->base_addr = ethdev->base_addr; + dev->wireless_data = ethdev->wireless_data; + SET_NETDEV_DEV(dev, ethdev->dev.parent); + eth_hw_addr_inherit(dev, ethdev); + err = register_netdev(dev); + if (err<0) { + free_netdev(dev); + return NULL; + } + return dev; +} + +static int reset_card( struct net_device *dev , int lock) { + struct airo_info *ai = dev->ml_priv; + + if (lock && down_interruptible(&ai->sem)) + return -1; + waitbusy (ai); + OUT4500(ai,COMMAND,CMD_SOFTRESET); + msleep(200); + waitbusy (ai); + msleep(200); + if (lock) + up(&ai->sem); + return 0; +} + +#define AIRO_MAX_NETWORK_COUNT 64 +static int airo_networks_allocate(struct airo_info *ai) +{ + if (ai->networks) + return 0; + + ai->networks = kcalloc(AIRO_MAX_NETWORK_COUNT, sizeof(BSSListElement), + GFP_KERNEL); + if (!ai->networks) { + airo_print_warn("", "Out of memory allocating beacons"); + return -ENOMEM; + } + + return 0; +} + +static void airo_networks_free(struct airo_info *ai) +{ + kfree(ai->networks); + ai->networks = NULL; +} + +static void airo_networks_initialize(struct airo_info *ai) +{ + int i; + + INIT_LIST_HEAD(&ai->network_free_list); + INIT_LIST_HEAD(&ai->network_list); + for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++) + list_add_tail(&ai->networks[i].list, + &ai->network_free_list); +} + +static const struct net_device_ops airo_netdev_ops = { + .ndo_open = airo_open, + .ndo_stop = airo_close, + .ndo_start_xmit = airo_start_xmit, + .ndo_get_stats = airo_get_stats, + .ndo_set_rx_mode = airo_set_multicast_list, + .ndo_set_mac_address = airo_set_mac_address, + .ndo_do_ioctl = airo_ioctl, + .ndo_change_mtu = airo_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +static const struct net_device_ops mpi_netdev_ops = { + .ndo_open = airo_open, + .ndo_stop = airo_close, + .ndo_start_xmit = mpi_start_xmit, + .ndo_get_stats = airo_get_stats, + .ndo_set_rx_mode = airo_set_multicast_list, + .ndo_set_mac_address = airo_set_mac_address, + .ndo_do_ioctl = airo_ioctl, + .ndo_change_mtu = airo_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + + +static struct net_device *_init_airo_card( unsigned short irq, int port, + int is_pcmcia, struct pci_dev *pci, + struct device *dmdev ) +{ + struct net_device *dev; + struct airo_info *ai; + int i, rc; + CapabilityRid cap_rid; + + /* Create the network device object. */ + dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup); + if (!dev) { + airo_print_err("", "Couldn't alloc_etherdev"); + return NULL; + } + + ai = dev->ml_priv = netdev_priv(dev); + ai->wifidev = NULL; + ai->flags = 1 << FLAG_RADIO_DOWN; + ai->jobs = 0; + ai->dev = dev; + if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) { + airo_print_dbg("", "Found an MPI350 card"); + set_bit(FLAG_MPI, &ai->flags); + } + spin_lock_init(&ai->aux_lock); + sema_init(&ai->sem, 1); + ai->config.len = 0; + ai->pci = pci; + init_waitqueue_head (&ai->thr_wait); + ai->tfm = NULL; + add_airo_dev(ai); + ai->APList.len = cpu_to_le16(sizeof(struct APListRid)); + + if (airo_networks_allocate (ai)) + goto err_out_free; + airo_networks_initialize (ai); + + skb_queue_head_init (&ai->txq); + + /* The Airo-specific entries in the device structure. */ + if (test_bit(FLAG_MPI,&ai->flags)) + dev->netdev_ops = &mpi_netdev_ops; + else + dev->netdev_ops = &airo_netdev_ops; + dev->wireless_handlers = &airo_handler_def; + ai->wireless_data.spy_data = &ai->spy_data; + dev->wireless_data = &ai->wireless_data; + dev->irq = irq; + dev->base_addr = port; + dev->priv_flags &= ~IFF_TX_SKB_SHARING; + + SET_NETDEV_DEV(dev, dmdev); + + reset_card (dev, 1); + msleep(400); + + if (!is_pcmcia) { + if (!request_region(dev->base_addr, 64, DRV_NAME)) { + rc = -EBUSY; + airo_print_err(dev->name, "Couldn't request region"); + goto err_out_nets; + } + } + + if (test_bit(FLAG_MPI,&ai->flags)) { + if (mpi_map_card(ai, pci)) { + airo_print_err("", "Could not map memory"); + goto err_out_res; + } + } + + if (probe) { + if (setup_card(ai, dev->dev_addr, 1) != SUCCESS) { + airo_print_err(dev->name, "MAC could not be enabled" ); + rc = -EIO; + goto err_out_map; + } + } else if (!test_bit(FLAG_MPI,&ai->flags)) { + ai->bap_read = fast_bap_read; + set_bit(FLAG_FLASHING, &ai->flags); + } + + strcpy(dev->name, "eth%d"); + rc = register_netdev(dev); + if (rc) { + airo_print_err(dev->name, "Couldn't register_netdev"); + goto err_out_map; + } + ai->wifidev = init_wifidev(ai, dev); + if (!ai->wifidev) + goto err_out_reg; + + rc = readCapabilityRid(ai, &cap_rid, 1); + if (rc != SUCCESS) { + rc = -EIO; + goto err_out_wifi; + } + /* WEP capability discovery */ + ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0; + ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0; + + airo_print_info(dev->name, "Firmware version %x.%x.%02d", + ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF), + (le16_to_cpu(cap_rid.softVer) & 0xFF), + le16_to_cpu(cap_rid.softSubVer)); + + /* Test for WPA support */ + /* Only firmware versions 5.30.17 or better can do WPA */ + if (le16_to_cpu(cap_rid.softVer) > 0x530 + || (le16_to_cpu(cap_rid.softVer) == 0x530 + && le16_to_cpu(cap_rid.softSubVer) >= 17)) { + airo_print_info(ai->dev->name, "WPA supported."); + + set_bit(FLAG_WPA_CAPABLE, &ai->flags); + ai->bssListFirst = RID_WPA_BSSLISTFIRST; + ai->bssListNext = RID_WPA_BSSLISTNEXT; + ai->bssListRidLen = sizeof(BSSListRid); + } else { + airo_print_info(ai->dev->name, "WPA unsupported with firmware " + "versions older than 5.30.17."); + + ai->bssListFirst = RID_BSSLISTFIRST; + ai->bssListNext = RID_BSSLISTNEXT; + ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra); + } + + set_bit(FLAG_REGISTERED,&ai->flags); + airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr); + + /* Allocate the transmit buffers */ + if (probe && !test_bit(FLAG_MPI,&ai->flags)) + for( i = 0; i < MAX_FIDS; i++ ) + ai->fids[i] = transmit_allocate(ai,AIRO_DEF_MTU,i>=MAX_FIDS/2); + + if (setup_proc_entry(dev, dev->ml_priv) < 0) + goto err_out_wifi; + + return dev; + +err_out_wifi: + unregister_netdev(ai->wifidev); + free_netdev(ai->wifidev); +err_out_reg: + unregister_netdev(dev); +err_out_map: + if (test_bit(FLAG_MPI,&ai->flags) && pci) { + pci_free_consistent(pci, PCI_SHARED_LEN, ai->shared, ai->shared_dma); + iounmap(ai->pciaux); + iounmap(ai->pcimem); + mpi_unmap_card(ai->pci); + } +err_out_res: + if (!is_pcmcia) + release_region( dev->base_addr, 64 ); +err_out_nets: + airo_networks_free(ai); +err_out_free: + del_airo_dev(ai); + free_netdev(dev); + return NULL; +} + +struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia, + struct device *dmdev) +{ + return _init_airo_card ( irq, port, is_pcmcia, NULL, dmdev); +} + +EXPORT_SYMBOL(init_airo_card); + +static int waitbusy (struct airo_info *ai) { + int delay = 0; + while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) { + udelay (10); + if ((++delay % 20) == 0) + OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); + } + return delay < 10000; +} + +int reset_airo_card( struct net_device *dev ) +{ + int i; + struct airo_info *ai = dev->ml_priv; + + if (reset_card (dev, 1)) + return -1; + + if ( setup_card(ai, dev->dev_addr, 1 ) != SUCCESS ) { + airo_print_err(dev->name, "MAC could not be enabled"); + return -1; + } + airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr); + /* Allocate the transmit buffers if needed */ + if (!test_bit(FLAG_MPI,&ai->flags)) + for( i = 0; i < MAX_FIDS; i++ ) + ai->fids[i] = transmit_allocate (ai,AIRO_DEF_MTU,i>=MAX_FIDS/2); + + enable_interrupts( ai ); + netif_wake_queue(dev); + return 0; +} + +EXPORT_SYMBOL(reset_airo_card); + +static void airo_send_event(struct net_device *dev) { + struct airo_info *ai = dev->ml_priv; + union iwreq_data wrqu; + StatusRid status_rid; + + clear_bit(JOB_EVENT, &ai->jobs); + PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0); + up(&ai->sem); + wrqu.data.length = 0; + wrqu.data.flags = 0; + memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +} + +static void airo_process_scan_results (struct airo_info *ai) { + union iwreq_data wrqu; + BSSListRid bss; + int rc; + BSSListElement * loop_net; + BSSListElement * tmp_net; + + /* Blow away current list of scan results */ + list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) { + list_move_tail (&loop_net->list, &ai->network_free_list); + /* Don't blow away ->list, just BSS data */ + memset (loop_net, 0, sizeof (loop_net->bss)); + } + + /* Try to read the first entry of the scan result */ + rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0); + if((rc) || (bss.index == cpu_to_le16(0xffff))) { + /* No scan results */ + goto out; + } + + /* Read and parse all entries */ + tmp_net = NULL; + while((!rc) && (bss.index != cpu_to_le16(0xffff))) { + /* Grab a network off the free list */ + if (!list_empty(&ai->network_free_list)) { + tmp_net = list_entry(ai->network_free_list.next, + BSSListElement, list); + list_del(ai->network_free_list.next); + } + + if (tmp_net != NULL) { + memcpy(tmp_net, &bss, sizeof(tmp_net->bss)); + list_add_tail(&tmp_net->list, &ai->network_list); + tmp_net = NULL; + } + + /* Read next entry */ + rc = PC4500_readrid(ai, ai->bssListNext, + &bss, ai->bssListRidLen, 0); + } + +out: + /* write APList back (we cleared it in airo_set_scan) */ + disable_MAC(ai, 2); + writeAPListRid(ai, &ai->APList, 0); + enable_MAC(ai, 0); + + ai->scan_timeout = 0; + clear_bit(JOB_SCAN_RESULTS, &ai->jobs); + up(&ai->sem); + + /* Send an empty event to user space. + * We don't send the received data on + * the event because it would require + * us to do complex transcoding, and + * we want to minimise the work done in + * the irq handler. Use a request to + * extract the data - Jean II */ + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL); +} + +static int airo_thread(void *data) { + struct net_device *dev = data; + struct airo_info *ai = dev->ml_priv; + int locked; + + set_freezable(); + while(1) { + /* make swsusp happy with our thread */ + try_to_freeze(); + + if (test_bit(JOB_DIE, &ai->jobs)) + break; + + if (ai->jobs) { + locked = down_interruptible(&ai->sem); + } else { + wait_queue_t wait; + + init_waitqueue_entry(&wait, current); + add_wait_queue(&ai->thr_wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (ai->jobs) + break; + if (ai->expires || ai->scan_timeout) { + if (ai->scan_timeout && + time_after_eq(jiffies,ai->scan_timeout)){ + set_bit(JOB_SCAN_RESULTS, &ai->jobs); + break; + } else if (ai->expires && + time_after_eq(jiffies,ai->expires)){ + set_bit(JOB_AUTOWEP, &ai->jobs); + break; + } + if (!kthread_should_stop() && + !freezing(current)) { + unsigned long wake_at; + if (!ai->expires || !ai->scan_timeout) { + wake_at = max(ai->expires, + ai->scan_timeout); + } else { + wake_at = min(ai->expires, + ai->scan_timeout); + } + schedule_timeout(wake_at - jiffies); + continue; + } + } else if (!kthread_should_stop() && + !freezing(current)) { + schedule(); + continue; + } + break; + } + current->state = TASK_RUNNING; + remove_wait_queue(&ai->thr_wait, &wait); + locked = 1; + } + + if (locked) + continue; + + if (test_bit(JOB_DIE, &ai->jobs)) { + up(&ai->sem); + break; + } + + if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) { + up(&ai->sem); + continue; + } + + if (test_bit(JOB_XMIT, &ai->jobs)) + airo_end_xmit(dev); + else if (test_bit(JOB_XMIT11, &ai->jobs)) + airo_end_xmit11(dev); + else if (test_bit(JOB_STATS, &ai->jobs)) + airo_read_stats(dev); + else if (test_bit(JOB_WSTATS, &ai->jobs)) + airo_read_wireless_stats(ai); + else if (test_bit(JOB_PROMISC, &ai->jobs)) + airo_set_promisc(ai); + else if (test_bit(JOB_MIC, &ai->jobs)) + micinit(ai); + else if (test_bit(JOB_EVENT, &ai->jobs)) + airo_send_event(dev); + else if (test_bit(JOB_AUTOWEP, &ai->jobs)) + timer_func(dev); + else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs)) + airo_process_scan_results(ai); + else /* Shouldn't get here, but we make sure to unlock */ + up(&ai->sem); + } + + return 0; +} + +static int header_len(__le16 ctl) +{ + u16 fc = le16_to_cpu(ctl); + switch (fc & 0xc) { + case 4: + if ((fc & 0xe0) == 0xc0) + return 10; /* one-address control packet */ + return 16; /* two-address control packet */ + case 8: + if ((fc & 0x300) == 0x300) + return 30; /* WDS packet */ + } + return 24; +} + +static void airo_handle_cisco_mic(struct airo_info *ai) +{ + if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) { + set_bit(JOB_MIC, &ai->jobs); + wake_up_interruptible(&ai->thr_wait); + } +} + +/* Airo Status codes */ +#define STAT_NOBEACON 0x8000 /* Loss of sync - missed beacons */ +#define STAT_MAXRETRIES 0x8001 /* Loss of sync - max retries */ +#define STAT_MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/ +#define STAT_FORCELOSS 0x8003 /* Loss of sync - host request */ +#define STAT_TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */ +#define STAT_DEAUTH 0x8100 /* low byte is 802.11 reason code */ +#define STAT_DISASSOC 0x8200 /* low byte is 802.11 reason code */ +#define STAT_ASSOC_FAIL 0x8400 /* low byte is 802.11 reason code */ +#define STAT_AUTH_FAIL 0x0300 /* low byte is 802.11 reason code */ +#define STAT_ASSOC 0x0400 /* Associated */ +#define STAT_REASSOC 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */ + +static void airo_print_status(const char *devname, u16 status) +{ + u8 reason = status & 0xFF; + + switch (status & 0xFF00) { + case STAT_NOBEACON: + switch (status) { + case STAT_NOBEACON: + airo_print_dbg(devname, "link lost (missed beacons)"); + break; + case STAT_MAXRETRIES: + case STAT_MAXARL: + airo_print_dbg(devname, "link lost (max retries)"); + break; + case STAT_FORCELOSS: + airo_print_dbg(devname, "link lost (local choice)"); + break; + case STAT_TSFSYNC: + airo_print_dbg(devname, "link lost (TSF sync lost)"); + break; + default: + airo_print_dbg(devname, "unknown status %x\n", status); + break; + } + break; + case STAT_DEAUTH: + airo_print_dbg(devname, "deauthenticated (reason: %d)", reason); + break; + case STAT_DISASSOC: + airo_print_dbg(devname, "disassociated (reason: %d)", reason); + break; + case STAT_ASSOC_FAIL: + airo_print_dbg(devname, "association failed (reason: %d)", + reason); + break; + case STAT_AUTH_FAIL: + airo_print_dbg(devname, "authentication failed (reason: %d)", + reason); + break; + case STAT_ASSOC: + case STAT_REASSOC: + break; + default: + airo_print_dbg(devname, "unknown status %x\n", status); + break; + } +} + +static void airo_handle_link(struct airo_info *ai) +{ + union iwreq_data wrqu; + int scan_forceloss = 0; + u16 status; + + /* Get new status and acknowledge the link change */ + status = le16_to_cpu(IN4500(ai, LINKSTAT)); + OUT4500(ai, EVACK, EV_LINK); + + if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0)) + scan_forceloss = 1; + + airo_print_status(ai->dev->name, status); + + if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) { + if (auto_wep) + ai->expires = 0; + if (ai->list_bss_task) + wake_up_process(ai->list_bss_task); + set_bit(FLAG_UPDATE_UNI, &ai->flags); + set_bit(FLAG_UPDATE_MULTI, &ai->flags); + + if (down_trylock(&ai->sem) != 0) { + set_bit(JOB_EVENT, &ai->jobs); + wake_up_interruptible(&ai->thr_wait); + } else + airo_send_event(ai->dev); + netif_carrier_on(ai->dev); + } else if (!scan_forceloss) { + if (auto_wep && !ai->expires) { + ai->expires = RUN_AT(3*HZ); + wake_up_interruptible(&ai->thr_wait); + } + + /* Send event to user space */ + eth_zero_addr(wrqu.ap_addr.sa_data); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL); + netif_carrier_off(ai->dev); + } else { + netif_carrier_off(ai->dev); + } +} + +static void airo_handle_rx(struct airo_info *ai) +{ + struct sk_buff *skb = NULL; + __le16 fc, v, *buffer, tmpbuf[4]; + u16 len, hdrlen = 0, gap, fid; + struct rx_hdr hdr; + int success = 0; + + if (test_bit(FLAG_MPI, &ai->flags)) { + if (test_bit(FLAG_802_11, &ai->flags)) + mpi_receive_802_11(ai); + else + mpi_receive_802_3(ai); + OUT4500(ai, EVACK, EV_RX); + return; + } + + fid = IN4500(ai, RXFID); + + /* Get the packet length */ + if (test_bit(FLAG_802_11, &ai->flags)) { + bap_setup (ai, fid, 4, BAP0); + bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0); + /* Bad CRC. Ignore packet */ + if (le16_to_cpu(hdr.status) & 2) + hdr.len = 0; + if (ai->wifidev == NULL) + hdr.len = 0; + } else { + bap_setup(ai, fid, 0x36, BAP0); + bap_read(ai, &hdr.len, 2, BAP0); + } + len = le16_to_cpu(hdr.len); + + if (len > AIRO_DEF_MTU) { + airo_print_err(ai->dev->name, "Bad size %d", len); + goto done; + } + if (len == 0) + goto done; + + if (test_bit(FLAG_802_11, &ai->flags)) { + bap_read(ai, &fc, sizeof (fc), BAP0); + hdrlen = header_len(fc); + } else + hdrlen = ETH_ALEN * 2; + + skb = dev_alloc_skb(len + hdrlen + 2 + 2); + if (!skb) { + ai->dev->stats.rx_dropped++; + goto done; + } + + skb_reserve(skb, 2); /* This way the IP header is aligned */ + buffer = (__le16 *) skb_put(skb, len + hdrlen); + if (test_bit(FLAG_802_11, &ai->flags)) { + buffer[0] = fc; + bap_read(ai, buffer + 1, hdrlen - 2, BAP0); + if (hdrlen == 24) + bap_read(ai, tmpbuf, 6, BAP0); + + bap_read(ai, &v, sizeof(v), BAP0); + gap = le16_to_cpu(v); + if (gap) { + if (gap <= 8) { + bap_read(ai, tmpbuf, gap, BAP0); + } else { + airo_print_err(ai->dev->name, "gaplen too " + "big. Problems will follow..."); + } + } + bap_read(ai, buffer + hdrlen/2, len, BAP0); + } else { + MICBuffer micbuf; + + bap_read(ai, buffer, ETH_ALEN * 2, BAP0); + if (ai->micstats.enabled) { + bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0); + if (ntohs(micbuf.typelen) > 0x05DC) + bap_setup(ai, fid, 0x44, BAP0); + else { + if (len <= sizeof (micbuf)) { + dev_kfree_skb_irq(skb); + goto done; + } + + len -= sizeof(micbuf); + skb_trim(skb, len + hdrlen); + } + } + + bap_read(ai, buffer + ETH_ALEN, len, BAP0); + if (decapsulate(ai, &micbuf, (etherHead*) buffer, len)) + dev_kfree_skb_irq (skb); + else + success = 1; + } + +#ifdef WIRELESS_SPY + if (success && (ai->spy_data.spy_number > 0)) { + char *sa; + struct iw_quality wstats; + + /* Prepare spy data : addr + qual */ + if (!test_bit(FLAG_802_11, &ai->flags)) { + sa = (char *) buffer + 6; + bap_setup(ai, fid, 8, BAP0); + bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0); + } else + sa = (char *) buffer + 10; + wstats.qual = hdr.rssi[0]; + if (ai->rssi) + wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm; + else + wstats.level = (hdr.rssi[1] + 321) / 2; + wstats.noise = ai->wstats.qual.noise; + wstats.updated = IW_QUAL_LEVEL_UPDATED + | IW_QUAL_QUAL_UPDATED + | IW_QUAL_DBM; + /* Update spy records */ + wireless_spy_update(ai->dev, sa, &wstats); + } +#endif /* WIRELESS_SPY */ + +done: + OUT4500(ai, EVACK, EV_RX); + + if (success) { + if (test_bit(FLAG_802_11, &ai->flags)) { + skb_reset_mac_header(skb); + skb->pkt_type = PACKET_OTHERHOST; + skb->dev = ai->wifidev; + skb->protocol = htons(ETH_P_802_2); + } else + skb->protocol = eth_type_trans(skb, ai->dev); + skb->ip_summed = CHECKSUM_NONE; + + netif_rx(skb); + } +} + +static void airo_handle_tx(struct airo_info *ai, u16 status) +{ + int i, len = 0, index = -1; + u16 fid; + + if (test_bit(FLAG_MPI, &ai->flags)) { + unsigned long flags; + + if (status & EV_TXEXC) + get_tx_error(ai, -1); + + spin_lock_irqsave(&ai->aux_lock, flags); + if (!skb_queue_empty(&ai->txq)) { + spin_unlock_irqrestore(&ai->aux_lock,flags); + mpi_send_packet(ai->dev); + } else { + clear_bit(FLAG_PENDING_XMIT, &ai->flags); + spin_unlock_irqrestore(&ai->aux_lock,flags); + netif_wake_queue(ai->dev); + } + OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC)); + return; + } + + fid = IN4500(ai, TXCOMPLFID); + + for(i = 0; i < MAX_FIDS; i++) { + if ((ai->fids[i] & 0xffff) == fid) { + len = ai->fids[i] >> 16; + index = i; + } + } + + if (index != -1) { + if (status & EV_TXEXC) + get_tx_error(ai, index); + + OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC)); + + /* Set up to be used again */ + ai->fids[index] &= 0xffff; + if (index < MAX_FIDS / 2) { + if (!test_bit(FLAG_PENDING_XMIT, &ai->flags)) + netif_wake_queue(ai->dev); + } else { + if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags)) + netif_wake_queue(ai->wifidev); + } + } else { + OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC)); + airo_print_err(ai->dev->name, "Unallocated FID was used to xmit"); + } +} + +static irqreturn_t airo_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + u16 status, savedInterrupts = 0; + struct airo_info *ai = dev->ml_priv; + int handled = 0; + + if (!netif_device_present(dev)) + return IRQ_NONE; + + for (;;) { + status = IN4500(ai, EVSTAT); + if (!(status & STATUS_INTS) || (status == 0xffff)) + break; + + handled = 1; + + if (status & EV_AWAKE) { + OUT4500(ai, EVACK, EV_AWAKE); + OUT4500(ai, EVACK, EV_AWAKE); + } + + if (!savedInterrupts) { + savedInterrupts = IN4500(ai, EVINTEN); + OUT4500(ai, EVINTEN, 0); + } + + if (status & EV_MIC) { + OUT4500(ai, EVACK, EV_MIC); + airo_handle_cisco_mic(ai); + } + + if (status & EV_LINK) { + /* Link status changed */ + airo_handle_link(ai); + } + + /* Check to see if there is something to receive */ + if (status & EV_RX) + airo_handle_rx(ai); + + /* Check to see if a packet has been transmitted */ + if (status & (EV_TX | EV_TXCPY | EV_TXEXC)) + airo_handle_tx(ai, status); + + if ( status & ~STATUS_INTS & ~IGNORE_INTS ) { + airo_print_warn(ai->dev->name, "Got weird status %x", + status & ~STATUS_INTS & ~IGNORE_INTS ); + } + } + + if (savedInterrupts) + OUT4500(ai, EVINTEN, savedInterrupts); + + return IRQ_RETVAL(handled); +} + +/* + * Routines to talk to the card + */ + +/* + * This was originally written for the 4500, hence the name + * NOTE: If use with 8bit mode and SMP bad things will happen! + * Why would some one do 8 bit IO in an SMP machine?!? + */ +static void OUT4500( struct airo_info *ai, u16 reg, u16 val ) { + if (test_bit(FLAG_MPI,&ai->flags)) + reg <<= 1; + if ( !do8bitIO ) + outw( val, ai->dev->base_addr + reg ); + else { + outb( val & 0xff, ai->dev->base_addr + reg ); + outb( val >> 8, ai->dev->base_addr + reg + 1 ); + } +} + +static u16 IN4500( struct airo_info *ai, u16 reg ) { + unsigned short rc; + + if (test_bit(FLAG_MPI,&ai->flags)) + reg <<= 1; + if ( !do8bitIO ) + rc = inw( ai->dev->base_addr + reg ); + else { + rc = inb( ai->dev->base_addr + reg ); + rc += ((int)inb( ai->dev->base_addr + reg + 1 )) << 8; + } + return rc; +} + +static int enable_MAC(struct airo_info *ai, int lock) +{ + int rc; + Cmd cmd; + Resp rsp; + + /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions + * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down" + * Note : we could try to use !netif_running(dev) in enable_MAC() + * instead of this flag, but I don't trust it *within* the + * open/close functions, and testing both flags together is + * "cheaper" - Jean II */ + if (ai->flags & FLAG_RADIO_MASK) return SUCCESS; + + if (lock && down_interruptible(&ai->sem)) + return -ERESTARTSYS; + + if (!test_bit(FLAG_ENABLED, &ai->flags)) { + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = MAC_ENABLE; + rc = issuecommand(ai, &cmd, &rsp); + if (rc == SUCCESS) + set_bit(FLAG_ENABLED, &ai->flags); + } else + rc = SUCCESS; + + if (lock) + up(&ai->sem); + + if (rc) + airo_print_err(ai->dev->name, "Cannot enable MAC"); + else if ((rsp.status & 0xFF00) != 0) { + airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, " + "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2); + rc = ERROR; + } + return rc; +} + +static void disable_MAC( struct airo_info *ai, int lock ) { + Cmd cmd; + Resp rsp; + + if (lock == 1 && down_interruptible(&ai->sem)) + return; + + if (test_bit(FLAG_ENABLED, &ai->flags)) { + if (lock != 2) /* lock == 2 means don't disable carrier */ + netif_carrier_off(ai->dev); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = MAC_DISABLE; // disable in case already enabled + issuecommand(ai, &cmd, &rsp); + clear_bit(FLAG_ENABLED, &ai->flags); + } + if (lock == 1) + up(&ai->sem); +} + +static void enable_interrupts( struct airo_info *ai ) { + /* Enable the interrupts */ + OUT4500( ai, EVINTEN, STATUS_INTS ); +} + +static void disable_interrupts( struct airo_info *ai ) { + OUT4500( ai, EVINTEN, 0 ); +} + +static void mpi_receive_802_3(struct airo_info *ai) +{ + RxFid rxd; + int len = 0; + struct sk_buff *skb; + char *buffer; + int off = 0; + MICBuffer micbuf; + + memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd)); + /* Make sure we got something */ + if (rxd.rdy && rxd.valid == 0) { + len = rxd.len + 12; + if (len < 12 || len > 2048) + goto badrx; + + skb = dev_alloc_skb(len); + if (!skb) { + ai->dev->stats.rx_dropped++; + goto badrx; + } + buffer = skb_put(skb,len); + memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2); + if (ai->micstats.enabled) { + memcpy(&micbuf, + ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2, + sizeof(micbuf)); + if (ntohs(micbuf.typelen) <= 0x05DC) { + if (len <= sizeof(micbuf) + ETH_ALEN * 2) + goto badmic; + + off = sizeof(micbuf); + skb_trim (skb, len - off); + } + } + memcpy(buffer + ETH_ALEN * 2, + ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off, + len - ETH_ALEN * 2 - off); + if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) { +badmic: + dev_kfree_skb_irq (skb); + goto badrx; + } +#ifdef WIRELESS_SPY + if (ai->spy_data.spy_number > 0) { + char *sa; + struct iw_quality wstats; + /* Prepare spy data : addr + qual */ + sa = buffer + ETH_ALEN; + wstats.qual = 0; /* XXX Where do I get that info from ??? */ + wstats.level = 0; + wstats.updated = 0; + /* Update spy records */ + wireless_spy_update(ai->dev, sa, &wstats); + } +#endif /* WIRELESS_SPY */ + + skb->ip_summed = CHECKSUM_NONE; + skb->protocol = eth_type_trans(skb, ai->dev); + netif_rx(skb); + } +badrx: + if (rxd.valid == 0) { + rxd.valid = 1; + rxd.rdy = 0; + rxd.len = PKTSIZE; + memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd)); + } +} + +static void mpi_receive_802_11(struct airo_info *ai) +{ + RxFid rxd; + struct sk_buff *skb = NULL; + u16 len, hdrlen = 0; + __le16 fc; + struct rx_hdr hdr; + u16 gap; + u16 *buffer; + char *ptr = ai->rxfids[0].virtual_host_addr + 4; + + memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd)); + memcpy ((char *)&hdr, ptr, sizeof(hdr)); + ptr += sizeof(hdr); + /* Bad CRC. Ignore packet */ + if (le16_to_cpu(hdr.status) & 2) + hdr.len = 0; + if (ai->wifidev == NULL) + hdr.len = 0; + len = le16_to_cpu(hdr.len); + if (len > AIRO_DEF_MTU) { + airo_print_err(ai->dev->name, "Bad size %d", len); + goto badrx; + } + if (len == 0) + goto badrx; + + fc = get_unaligned((__le16 *)ptr); + hdrlen = header_len(fc); + + skb = dev_alloc_skb( len + hdrlen + 2 ); + if ( !skb ) { + ai->dev->stats.rx_dropped++; + goto badrx; + } + buffer = (u16*)skb_put (skb, len + hdrlen); + memcpy ((char *)buffer, ptr, hdrlen); + ptr += hdrlen; + if (hdrlen == 24) + ptr += 6; + gap = get_unaligned_le16(ptr); + ptr += sizeof(__le16); + if (gap) { + if (gap <= 8) + ptr += gap; + else + airo_print_err(ai->dev->name, + "gaplen too big. Problems will follow..."); + } + memcpy ((char *)buffer + hdrlen, ptr, len); + ptr += len; +#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ + if (ai->spy_data.spy_number > 0) { + char *sa; + struct iw_quality wstats; + /* Prepare spy data : addr + qual */ + sa = (char*)buffer + 10; + wstats.qual = hdr.rssi[0]; + if (ai->rssi) + wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm; + else + wstats.level = (hdr.rssi[1] + 321) / 2; + wstats.noise = ai->wstats.qual.noise; + wstats.updated = IW_QUAL_QUAL_UPDATED + | IW_QUAL_LEVEL_UPDATED + | IW_QUAL_DBM; + /* Update spy records */ + wireless_spy_update(ai->dev, sa, &wstats); + } +#endif /* IW_WIRELESS_SPY */ + skb_reset_mac_header(skb); + skb->pkt_type = PACKET_OTHERHOST; + skb->dev = ai->wifidev; + skb->protocol = htons(ETH_P_802_2); + skb->ip_summed = CHECKSUM_NONE; + netif_rx( skb ); + +badrx: + if (rxd.valid == 0) { + rxd.valid = 1; + rxd.rdy = 0; + rxd.len = PKTSIZE; + memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd)); + } +} + +static inline void set_auth_type(struct airo_info *local, int auth_type) +{ + local->config.authType = auth_type; + /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT). + * Used by airo_set_auth() + */ + if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT) + local->last_auth = auth_type; +} + +static u16 setup_card(struct airo_info *ai, u8 *mac, int lock) +{ + Cmd cmd; + Resp rsp; + int status; + SsidRid mySsid; + __le16 lastindex; + WepKeyRid wkr; + int rc; + + memset( &mySsid, 0, sizeof( mySsid ) ); + kfree (ai->flash); + ai->flash = NULL; + + /* The NOP is the first step in getting the card going */ + cmd.cmd = NOP; + cmd.parm0 = cmd.parm1 = cmd.parm2 = 0; + if (lock && down_interruptible(&ai->sem)) + return ERROR; + if ( issuecommand( ai, &cmd, &rsp ) != SUCCESS ) { + if (lock) + up(&ai->sem); + return ERROR; + } + disable_MAC( ai, 0); + + // Let's figure out if we need to use the AUX port + if (!test_bit(FLAG_MPI,&ai->flags)) { + cmd.cmd = CMD_ENABLEAUX; + if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { + if (lock) + up(&ai->sem); + airo_print_err(ai->dev->name, "Error checking for AUX port"); + return ERROR; + } + if (!aux_bap || rsp.status & 0xff00) { + ai->bap_read = fast_bap_read; + airo_print_dbg(ai->dev->name, "Doing fast bap_reads"); + } else { + ai->bap_read = aux_bap_read; + airo_print_dbg(ai->dev->name, "Doing AUX bap_reads"); + } + } + if (lock) + up(&ai->sem); + if (ai->config.len == 0) { + int i; + tdsRssiRid rssi_rid; + CapabilityRid cap_rid; + + kfree(ai->SSID); + ai->SSID = NULL; + // general configuration (read/modify/write) + status = readConfigRid(ai, lock); + if ( status != SUCCESS ) return ERROR; + + status = readCapabilityRid(ai, &cap_rid, lock); + if ( status != SUCCESS ) return ERROR; + + status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),lock); + if ( status == SUCCESS ) { + if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL) + memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */ + } + else { + kfree(ai->rssi); + ai->rssi = NULL; + if (cap_rid.softCap & cpu_to_le16(8)) + ai->config.rmode |= RXMODE_NORMALIZED_RSSI; + else + airo_print_warn(ai->dev->name, "unknown received signal " + "level scale"); + } + ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS; + set_auth_type(ai, AUTH_OPEN); + ai->config.modulation = MOD_CCK; + + if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) && + (cap_rid.extSoftCap & cpu_to_le16(1)) && + micsetup(ai) == SUCCESS) { + ai->config.opmode |= MODE_MIC; + set_bit(FLAG_MIC_CAPABLE, &ai->flags); + } + + /* Save off the MAC */ + for( i = 0; i < ETH_ALEN; i++ ) { + mac[i] = ai->config.macAddr[i]; + } + + /* Check to see if there are any insmod configured + rates to add */ + if ( rates[0] ) { + memset(ai->config.rates,0,sizeof(ai->config.rates)); + for( i = 0; i < 8 && rates[i]; i++ ) { + ai->config.rates[i] = rates[i]; + } + } + set_bit (FLAG_COMMIT, &ai->flags); + } + + /* Setup the SSIDs if present */ + if ( ssids[0] ) { + int i; + for( i = 0; i < 3 && ssids[i]; i++ ) { + size_t len = strlen(ssids[i]); + if (len > 32) + len = 32; + mySsid.ssids[i].len = cpu_to_le16(len); + memcpy(mySsid.ssids[i].ssid, ssids[i], len); + } + mySsid.len = cpu_to_le16(sizeof(mySsid)); + } + + status = writeConfigRid(ai, lock); + if ( status != SUCCESS ) return ERROR; + + /* Set up the SSID list */ + if ( ssids[0] ) { + status = writeSsidRid(ai, &mySsid, lock); + if ( status != SUCCESS ) return ERROR; + } + + status = enable_MAC(ai, lock); + if (status != SUCCESS) + return ERROR; + + /* Grab the initial wep key, we gotta save it for auto_wep */ + rc = readWepKeyRid(ai, &wkr, 1, lock); + if (rc == SUCCESS) do { + lastindex = wkr.kindex; + if (wkr.kindex == cpu_to_le16(0xffff)) { + ai->defindex = wkr.mac[0]; + } + rc = readWepKeyRid(ai, &wkr, 0, lock); + } while(lastindex != wkr.kindex); + + try_auto_wep(ai); + + return SUCCESS; +} + +static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp) { + // Im really paranoid about letting it run forever! + int max_tries = 600000; + + if (IN4500(ai, EVSTAT) & EV_CMD) + OUT4500(ai, EVACK, EV_CMD); + + OUT4500(ai, PARAM0, pCmd->parm0); + OUT4500(ai, PARAM1, pCmd->parm1); + OUT4500(ai, PARAM2, pCmd->parm2); + OUT4500(ai, COMMAND, pCmd->cmd); + + while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) { + if ((IN4500(ai, COMMAND)) == pCmd->cmd) + // PC4500 didn't notice command, try again + OUT4500(ai, COMMAND, pCmd->cmd); + if (!in_atomic() && (max_tries & 255) == 0) + schedule(); + } + + if ( max_tries == -1 ) { + airo_print_err(ai->dev->name, + "Max tries exceeded when issuing command"); + if (IN4500(ai, COMMAND) & COMMAND_BUSY) + OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); + return ERROR; + } + + // command completed + pRsp->status = IN4500(ai, STATUS); + pRsp->rsp0 = IN4500(ai, RESP0); + pRsp->rsp1 = IN4500(ai, RESP1); + pRsp->rsp2 = IN4500(ai, RESP2); + if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET) + airo_print_err(ai->dev->name, + "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x", + pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1, + pRsp->rsp2); + + // clear stuck command busy if necessary + if (IN4500(ai, COMMAND) & COMMAND_BUSY) { + OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); + } + // acknowledge processing the status/response + OUT4500(ai, EVACK, EV_CMD); + + return SUCCESS; +} + +/* Sets up the bap to start exchange data. whichbap should + * be one of the BAP0 or BAP1 defines. Locks should be held before + * calling! */ +static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap ) +{ + int timeout = 50; + int max_tries = 3; + + OUT4500(ai, SELECT0+whichbap, rid); + OUT4500(ai, OFFSET0+whichbap, offset); + while (1) { + int status = IN4500(ai, OFFSET0+whichbap); + if (status & BAP_BUSY) { + /* This isn't really a timeout, but its kinda + close */ + if (timeout--) { + continue; + } + } else if ( status & BAP_ERR ) { + /* invalid rid or offset */ + airo_print_err(ai->dev->name, "BAP error %x %d", + status, whichbap ); + return ERROR; + } else if (status & BAP_DONE) { // success + return SUCCESS; + } + if ( !(max_tries--) ) { + airo_print_err(ai->dev->name, + "BAP setup error too many retries\n"); + return ERROR; + } + // -- PC4500 missed it, try again + OUT4500(ai, SELECT0+whichbap, rid); + OUT4500(ai, OFFSET0+whichbap, offset); + timeout = 50; + } +} + +/* should only be called by aux_bap_read. This aux function and the + following use concepts not documented in the developers guide. I + got them from a patch given to my by Aironet */ +static u16 aux_setup(struct airo_info *ai, u16 page, + u16 offset, u16 *len) +{ + u16 next; + + OUT4500(ai, AUXPAGE, page); + OUT4500(ai, AUXOFF, 0); + next = IN4500(ai, AUXDATA); + *len = IN4500(ai, AUXDATA)&0xff; + if (offset != 4) OUT4500(ai, AUXOFF, offset); + return next; +} + +/* requires call to bap_setup() first */ +static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst, + int bytelen, int whichbap) +{ + u16 len; + u16 page; + u16 offset; + u16 next; + int words; + int i; + unsigned long flags; + + spin_lock_irqsave(&ai->aux_lock, flags); + page = IN4500(ai, SWS0+whichbap); + offset = IN4500(ai, SWS2+whichbap); + next = aux_setup(ai, page, offset, &len); + words = (bytelen+1)>>1; + + for (i=0; i>1) < (words-i) ? (len>>1) : (words-i); + if ( !do8bitIO ) + insw( ai->dev->base_addr+DATA0+whichbap, + pu16Dst+i,count ); + else + insb( ai->dev->base_addr+DATA0+whichbap, + pu16Dst+i, count << 1 ); + i += count; + if (iaux_lock, flags); + return SUCCESS; +} + + +/* requires call to bap_setup() first */ +static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst, + int bytelen, int whichbap) +{ + bytelen = (bytelen + 1) & (~1); // round up to even value + if ( !do8bitIO ) + insw( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1 ); + else + insb( ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen ); + return SUCCESS; +} + +/* requires call to bap_setup() first */ +static int bap_write(struct airo_info *ai, const __le16 *pu16Src, + int bytelen, int whichbap) +{ + bytelen = (bytelen + 1) & (~1); // round up to even value + if ( !do8bitIO ) + outsw( ai->dev->base_addr+DATA0+whichbap, + pu16Src, bytelen>>1 ); + else + outsb( ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen ); + return SUCCESS; +} + +static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd) +{ + Cmd cmd; /* for issuing commands */ + Resp rsp; /* response from commands */ + u16 status; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = accmd; + cmd.parm0 = rid; + status = issuecommand(ai, &cmd, &rsp); + if (status != 0) return status; + if ( (rsp.status & 0x7F00) != 0) { + return (accmd << 8) + (rsp.rsp0 & 0xFF); + } + return 0; +} + +/* Note, that we are using BAP1 which is also used by transmit, so + * we must get a lock. */ +static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock) +{ + u16 status; + int rc = SUCCESS; + + if (lock) { + if (down_interruptible(&ai->sem)) + return ERROR; + } + if (test_bit(FLAG_MPI,&ai->flags)) { + Cmd cmd; + Resp rsp; + + memset(&cmd, 0, sizeof(cmd)); + memset(&rsp, 0, sizeof(rsp)); + ai->config_desc.rid_desc.valid = 1; + ai->config_desc.rid_desc.len = RIDSIZE; + ai->config_desc.rid_desc.rid = 0; + ai->config_desc.rid_desc.host_addr = ai->ridbus; + + cmd.cmd = CMD_ACCESS; + cmd.parm0 = rid; + + memcpy_toio(ai->config_desc.card_ram_off, + &ai->config_desc.rid_desc, sizeof(Rid)); + + rc = issuecommand(ai, &cmd, &rsp); + + if (rsp.status & 0x7f00) + rc = rsp.rsp0; + if (!rc) + memcpy(pBuf, ai->config_desc.virtual_host_addr, len); + goto done; + } else { + if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) { + rc = status; + goto done; + } + if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) { + rc = ERROR; + goto done; + } + // read the rid length field + bap_read(ai, pBuf, 2, BAP1); + // length for remaining part of rid + len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2; + + if ( len <= 2 ) { + airo_print_err(ai->dev->name, + "Rid %x has a length of %d which is too short", + (int)rid, (int)len ); + rc = ERROR; + goto done; + } + // read remainder of the rid + rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1); + } +done: + if (lock) + up(&ai->sem); + return rc; +} + +/* Note, that we are using BAP1 which is also used by transmit, so + * make sure this isn't called when a transmit is happening */ +static int PC4500_writerid(struct airo_info *ai, u16 rid, + const void *pBuf, int len, int lock) +{ + u16 status; + int rc = SUCCESS; + + *(__le16*)pBuf = cpu_to_le16((u16)len); + + if (lock) { + if (down_interruptible(&ai->sem)) + return ERROR; + } + if (test_bit(FLAG_MPI,&ai->flags)) { + Cmd cmd; + Resp rsp; + + if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid)) + airo_print_err(ai->dev->name, + "%s: MAC should be disabled (rid=%04x)", + __func__, rid); + memset(&cmd, 0, sizeof(cmd)); + memset(&rsp, 0, sizeof(rsp)); + + ai->config_desc.rid_desc.valid = 1; + ai->config_desc.rid_desc.len = *((u16 *)pBuf); + ai->config_desc.rid_desc.rid = 0; + + cmd.cmd = CMD_WRITERID; + cmd.parm0 = rid; + + memcpy_toio(ai->config_desc.card_ram_off, + &ai->config_desc.rid_desc, sizeof(Rid)); + + if (len < 4 || len > 2047) { + airo_print_err(ai->dev->name, "%s: len=%d", __func__, len); + rc = -1; + } else { + memcpy(ai->config_desc.virtual_host_addr, + pBuf, len); + + rc = issuecommand(ai, &cmd, &rsp); + if ((rc & 0xff00) != 0) { + airo_print_err(ai->dev->name, "%s: Write rid Error %d", + __func__, rc); + airo_print_err(ai->dev->name, "%s: Cmd=%04x", + __func__, cmd.cmd); + } + + if ((rsp.status & 0x7f00)) + rc = rsp.rsp0; + } + } else { + // --- first access so that we can write the rid data + if ( (status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) { + rc = status; + goto done; + } + // --- now write the rid data + if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) { + rc = ERROR; + goto done; + } + bap_write(ai, pBuf, len, BAP1); + // ---now commit the rid data + rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS); + } +done: + if (lock) + up(&ai->sem); + return rc; +} + +/* Allocates a FID to be used for transmitting packets. We only use + one for now. */ +static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw) +{ + unsigned int loop = 3000; + Cmd cmd; + Resp rsp; + u16 txFid; + __le16 txControl; + + cmd.cmd = CMD_ALLOCATETX; + cmd.parm0 = lenPayload; + if (down_interruptible(&ai->sem)) + return ERROR; + if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { + txFid = ERROR; + goto done; + } + if ( (rsp.status & 0xFF00) != 0) { + txFid = ERROR; + goto done; + } + /* wait for the allocate event/indication + * It makes me kind of nervous that this can just sit here and spin, + * but in practice it only loops like four times. */ + while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop); + if (!loop) { + txFid = ERROR; + goto done; + } + + // get the allocated fid and acknowledge + txFid = IN4500(ai, TXALLOCFID); + OUT4500(ai, EVACK, EV_ALLOC); + + /* The CARD is pretty cool since it converts the ethernet packet + * into 802.11. Also note that we don't release the FID since we + * will be using the same one over and over again. */ + /* We only have to setup the control once since we are not + * releasing the fid. */ + if (raw) + txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11 + | TXCTL_ETHERNET | TXCTL_NORELEASE); + else + txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3 + | TXCTL_ETHERNET | TXCTL_NORELEASE); + if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS) + txFid = ERROR; + else + bap_write(ai, &txControl, sizeof(txControl), BAP1); + +done: + up(&ai->sem); + + return txFid; +} + +/* In general BAP1 is dedicated to transmiting packets. However, + since we need a BAP when accessing RIDs, we also use BAP1 for that. + Make sure the BAP1 spinlock is held when this is called. */ +static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket) +{ + __le16 payloadLen; + Cmd cmd; + Resp rsp; + int miclen = 0; + u16 txFid = len; + MICBuffer pMic; + + len >>= 16; + + if (len <= ETH_ALEN * 2) { + airo_print_warn(ai->dev->name, "Short packet %d", len); + return ERROR; + } + len -= ETH_ALEN * 2; + + if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled && + (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) { + if (encapsulate(ai,(etherHead *)pPacket,&pMic,len) != SUCCESS) + return ERROR; + miclen = sizeof(pMic); + } + // packet is destination[6], source[6], payload[len-12] + // write the payload length and dst/src/payload + if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR; + /* The hardware addresses aren't counted as part of the payload, so + * we have to subtract the 12 bytes for the addresses off */ + payloadLen = cpu_to_le16(len + miclen); + bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1); + bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1); + if (miclen) + bap_write(ai, (__le16*)&pMic, miclen, BAP1); + bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1); + // issue the transmit command + memset( &cmd, 0, sizeof( cmd ) ); + cmd.cmd = CMD_TRANSMIT; + cmd.parm0 = txFid; + if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR; + if ( (rsp.status & 0xFF00) != 0) return ERROR; + return SUCCESS; +} + +static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket) +{ + __le16 fc, payloadLen; + Cmd cmd; + Resp rsp; + int hdrlen; + static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6}; + /* padding of header to full size + le16 gaplen (6) + gaplen bytes */ + u16 txFid = len; + len >>= 16; + + fc = *(__le16*)pPacket; + hdrlen = header_len(fc); + + if (len < hdrlen) { + airo_print_warn(ai->dev->name, "Short packet %d", len); + return ERROR; + } + + /* packet is 802.11 header + payload + * write the payload length and dst/src/payload */ + if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR; + /* The 802.11 header aren't counted as part of the payload, so + * we have to subtract the header bytes off */ + payloadLen = cpu_to_le16(len-hdrlen); + bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1); + if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR; + bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1); + bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1); + + bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1); + // issue the transmit command + memset( &cmd, 0, sizeof( cmd ) ); + cmd.cmd = CMD_TRANSMIT; + cmd.parm0 = txFid; + if (issuecommand(ai, &cmd, &rsp) != SUCCESS) return ERROR; + if ( (rsp.status & 0xFF00) != 0) return ERROR; + return SUCCESS; +} + +/* + * This is the proc_fs routines. It is a bit messier than I would + * like! Feel free to clean it up! + */ + +static ssize_t proc_read( struct file *file, + char __user *buffer, + size_t len, + loff_t *offset); + +static ssize_t proc_write( struct file *file, + const char __user *buffer, + size_t len, + loff_t *offset ); +static int proc_close( struct inode *inode, struct file *file ); + +static int proc_stats_open( struct inode *inode, struct file *file ); +static int proc_statsdelta_open( struct inode *inode, struct file *file ); +static int proc_status_open( struct inode *inode, struct file *file ); +static int proc_SSID_open( struct inode *inode, struct file *file ); +static int proc_APList_open( struct inode *inode, struct file *file ); +static int proc_BSSList_open( struct inode *inode, struct file *file ); +static int proc_config_open( struct inode *inode, struct file *file ); +static int proc_wepkey_open( struct inode *inode, struct file *file ); + +static const struct file_operations proc_statsdelta_ops = { + .owner = THIS_MODULE, + .read = proc_read, + .open = proc_statsdelta_open, + .release = proc_close, + .llseek = default_llseek, +}; + +static const struct file_operations proc_stats_ops = { + .owner = THIS_MODULE, + .read = proc_read, + .open = proc_stats_open, + .release = proc_close, + .llseek = default_llseek, +}; + +static const struct file_operations proc_status_ops = { + .owner = THIS_MODULE, + .read = proc_read, + .open = proc_status_open, + .release = proc_close, + .llseek = default_llseek, +}; + +static const struct file_operations proc_SSID_ops = { + .owner = THIS_MODULE, + .read = proc_read, + .write = proc_write, + .open = proc_SSID_open, + .release = proc_close, + .llseek = default_llseek, +}; + +static const struct file_operations proc_BSSList_ops = { + .owner = THIS_MODULE, + .read = proc_read, + .write = proc_write, + .open = proc_BSSList_open, + .release = proc_close, + .llseek = default_llseek, +}; + +static const struct file_operations proc_APList_ops = { + .owner = THIS_MODULE, + .read = proc_read, + .write = proc_write, + .open = proc_APList_open, + .release = proc_close, + .llseek = default_llseek, +}; + +static const struct file_operations proc_config_ops = { + .owner = THIS_MODULE, + .read = proc_read, + .write = proc_write, + .open = proc_config_open, + .release = proc_close, + .llseek = default_llseek, +}; + +static const struct file_operations proc_wepkey_ops = { + .owner = THIS_MODULE, + .read = proc_read, + .write = proc_write, + .open = proc_wepkey_open, + .release = proc_close, + .llseek = default_llseek, +}; + +static struct proc_dir_entry *airo_entry; + +struct proc_data { + int release_buffer; + int readlen; + char *rbuffer; + int writelen; + int maxwritelen; + char *wbuffer; + void (*on_close) (struct inode *, struct file *); +}; + +static int setup_proc_entry( struct net_device *dev, + struct airo_info *apriv ) { + struct proc_dir_entry *entry; + + /* First setup the device directory */ + strcpy(apriv->proc_name,dev->name); + apriv->proc_entry = proc_mkdir_mode(apriv->proc_name, airo_perm, + airo_entry); + if (!apriv->proc_entry) + return -ENOMEM; + proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid); + + /* Setup the StatsDelta */ + entry = proc_create_data("StatsDelta", S_IRUGO & proc_perm, + apriv->proc_entry, &proc_statsdelta_ops, dev); + if (!entry) + goto fail; + proc_set_user(entry, proc_kuid, proc_kgid); + + /* Setup the Stats */ + entry = proc_create_data("Stats", S_IRUGO & proc_perm, + apriv->proc_entry, &proc_stats_ops, dev); + if (!entry) + goto fail; + proc_set_user(entry, proc_kuid, proc_kgid); + + /* Setup the Status */ + entry = proc_create_data("Status", S_IRUGO & proc_perm, + apriv->proc_entry, &proc_status_ops, dev); + if (!entry) + goto fail; + proc_set_user(entry, proc_kuid, proc_kgid); + + /* Setup the Config */ + entry = proc_create_data("Config", proc_perm, + apriv->proc_entry, &proc_config_ops, dev); + if (!entry) + goto fail; + proc_set_user(entry, proc_kuid, proc_kgid); + + /* Setup the SSID */ + entry = proc_create_data("SSID", proc_perm, + apriv->proc_entry, &proc_SSID_ops, dev); + if (!entry) + goto fail; + proc_set_user(entry, proc_kuid, proc_kgid); + + /* Setup the APList */ + entry = proc_create_data("APList", proc_perm, + apriv->proc_entry, &proc_APList_ops, dev); + if (!entry) + goto fail; + proc_set_user(entry, proc_kuid, proc_kgid); + + /* Setup the BSSList */ + entry = proc_create_data("BSSList", proc_perm, + apriv->proc_entry, &proc_BSSList_ops, dev); + if (!entry) + goto fail; + proc_set_user(entry, proc_kuid, proc_kgid); + + /* Setup the WepKey */ + entry = proc_create_data("WepKey", proc_perm, + apriv->proc_entry, &proc_wepkey_ops, dev); + if (!entry) + goto fail; + proc_set_user(entry, proc_kuid, proc_kgid); + return 0; + +fail: + remove_proc_subtree(apriv->proc_name, airo_entry); + return -ENOMEM; +} + +static int takedown_proc_entry( struct net_device *dev, + struct airo_info *apriv ) +{ + remove_proc_subtree(apriv->proc_name, airo_entry); + return 0; +} + +/* + * What we want from the proc_fs is to be able to efficiently read + * and write the configuration. To do this, we want to read the + * configuration when the file is opened and write it when the file is + * closed. So basically we allocate a read buffer at open and fill it + * with data, and allocate a write buffer and read it at close. + */ + +/* + * The read routine is generic, it relies on the preallocated rbuffer + * to supply the data. + */ +static ssize_t proc_read( struct file *file, + char __user *buffer, + size_t len, + loff_t *offset ) +{ + struct proc_data *priv = file->private_data; + + if (!priv->rbuffer) + return -EINVAL; + + return simple_read_from_buffer(buffer, len, offset, priv->rbuffer, + priv->readlen); +} + +/* + * The write routine is generic, it fills in a preallocated rbuffer + * to supply the data. + */ +static ssize_t proc_write( struct file *file, + const char __user *buffer, + size_t len, + loff_t *offset ) +{ + ssize_t ret; + struct proc_data *priv = file->private_data; + + if (!priv->wbuffer) + return -EINVAL; + + ret = simple_write_to_buffer(priv->wbuffer, priv->maxwritelen, offset, + buffer, len); + if (ret > 0) + priv->writelen = max_t(int, priv->writelen, *offset); + + return ret; +} + +static int proc_status_open(struct inode *inode, struct file *file) +{ + struct proc_data *data; + struct net_device *dev = PDE_DATA(inode); + struct airo_info *apriv = dev->ml_priv; + CapabilityRid cap_rid; + StatusRid status_rid; + u16 mode; + int i; + + if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) + return -ENOMEM; + data = file->private_data; + if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) { + kfree (file->private_data); + return -ENOMEM; + } + + readStatusRid(apriv, &status_rid, 1); + readCapabilityRid(apriv, &cap_rid, 1); + + mode = le16_to_cpu(status_rid.mode); + + i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n", + mode & 1 ? "CFG ": "", + mode & 2 ? "ACT ": "", + mode & 0x10 ? "SYN ": "", + mode & 0x20 ? "LNK ": "", + mode & 0x40 ? "LEAP ": "", + mode & 0x80 ? "PRIV ": "", + mode & 0x100 ? "KEY ": "", + mode & 0x200 ? "WEP ": "", + mode & 0x8000 ? "ERR ": ""); + sprintf( data->rbuffer+i, "Mode: %x\n" + "Signal Strength: %d\n" + "Signal Quality: %d\n" + "SSID: %-.*s\n" + "AP: %-.16s\n" + "Freq: %d\n" + "BitRate: %dmbs\n" + "Driver Version: %s\n" + "Device: %s\nManufacturer: %s\nFirmware Version: %s\n" + "Radio type: %x\nCountry: %x\nHardware Version: %x\n" + "Software Version: %x\nSoftware Subversion: %x\n" + "Boot block version: %x\n", + le16_to_cpu(status_rid.mode), + le16_to_cpu(status_rid.normalizedSignalStrength), + le16_to_cpu(status_rid.signalQuality), + le16_to_cpu(status_rid.SSIDlen), + status_rid.SSID, + status_rid.apName, + le16_to_cpu(status_rid.channel), + le16_to_cpu(status_rid.currentXmitRate) / 2, + version, + cap_rid.prodName, + cap_rid.manName, + cap_rid.prodVer, + le16_to_cpu(cap_rid.radioType), + le16_to_cpu(cap_rid.country), + le16_to_cpu(cap_rid.hardVer), + le16_to_cpu(cap_rid.softVer), + le16_to_cpu(cap_rid.softSubVer), + le16_to_cpu(cap_rid.bootBlockVer)); + data->readlen = strlen( data->rbuffer ); + return 0; +} + +static int proc_stats_rid_open(struct inode*, struct file*, u16); +static int proc_statsdelta_open( struct inode *inode, + struct file *file ) { + if (file->f_mode&FMODE_WRITE) { + return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR); + } + return proc_stats_rid_open(inode, file, RID_STATSDELTA); +} + +static int proc_stats_open( struct inode *inode, struct file *file ) { + return proc_stats_rid_open(inode, file, RID_STATS); +} + +static int proc_stats_rid_open( struct inode *inode, + struct file *file, + u16 rid ) +{ + struct proc_data *data; + struct net_device *dev = PDE_DATA(inode); + struct airo_info *apriv = dev->ml_priv; + StatsRid stats; + int i, j; + __le32 *vals = stats.vals; + int len; + + if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) + return -ENOMEM; + data = file->private_data; + if ((data->rbuffer = kmalloc( 4096, GFP_KERNEL )) == NULL) { + kfree (file->private_data); + return -ENOMEM; + } + + readStatsRid(apriv, &stats, rid, 1); + len = le16_to_cpu(stats.len); + + j = 0; + for(i=0; statsLabels[i]!=(char *)-1 && i*44096) { + airo_print_warn(apriv->dev->name, + "Potentially disastrous buffer overflow averted!"); + break; + } + j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i], + le32_to_cpu(vals[i])); + } + if (i*4 >= len) { + airo_print_warn(apriv->dev->name, "Got a short rid"); + } + data->readlen = j; + return 0; +} + +static int get_dec_u16( char *buffer, int *start, int limit ) { + u16 value; + int valid = 0; + for (value = 0; *start < limit && buffer[*start] >= '0' && + buffer[*start] <= '9'; (*start)++) { + valid = 1; + value *= 10; + value += buffer[*start] - '0'; + } + if ( !valid ) return -1; + return value; +} + +static int airo_config_commit(struct net_device *dev, + struct iw_request_info *info, void *zwrq, + char *extra); + +static inline int sniffing_mode(struct airo_info *ai) +{ + return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >= + le16_to_cpu(RXMODE_RFMON); +} + +static void proc_config_on_close(struct inode *inode, struct file *file) +{ + struct proc_data *data = file->private_data; + struct net_device *dev = PDE_DATA(inode); + struct airo_info *ai = dev->ml_priv; + char *line; + + if ( !data->writelen ) return; + + readConfigRid(ai, 1); + set_bit (FLAG_COMMIT, &ai->flags); + + line = data->wbuffer; + while( line[0] ) { +/*** Mode processing */ + if ( !strncmp( line, "Mode: ", 6 ) ) { + line += 6; + if (sniffing_mode(ai)) + set_bit (FLAG_RESET, &ai->flags); + ai->config.rmode &= ~RXMODE_FULL_MASK; + clear_bit (FLAG_802_11, &ai->flags); + ai->config.opmode &= ~MODE_CFG_MASK; + ai->config.scanMode = SCANMODE_ACTIVE; + if ( line[0] == 'a' ) { + ai->config.opmode |= MODE_STA_IBSS; + } else { + ai->config.opmode |= MODE_STA_ESS; + if ( line[0] == 'r' ) { + ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER; + ai->config.scanMode = SCANMODE_PASSIVE; + set_bit (FLAG_802_11, &ai->flags); + } else if ( line[0] == 'y' ) { + ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER; + ai->config.scanMode = SCANMODE_PASSIVE; + set_bit (FLAG_802_11, &ai->flags); + } else if ( line[0] == 'l' ) + ai->config.rmode |= RXMODE_LANMON; + } + set_bit (FLAG_COMMIT, &ai->flags); + } + +/*** Radio status */ + else if (!strncmp(line,"Radio: ", 7)) { + line += 7; + if (!strncmp(line,"off",3)) { + set_bit (FLAG_RADIO_OFF, &ai->flags); + } else { + clear_bit (FLAG_RADIO_OFF, &ai->flags); + } + } +/*** NodeName processing */ + else if ( !strncmp( line, "NodeName: ", 10 ) ) { + int j; + + line += 10; + memset( ai->config.nodeName, 0, 16 ); +/* Do the name, assume a space between the mode and node name */ + for( j = 0; j < 16 && line[j] != '\n'; j++ ) { + ai->config.nodeName[j] = line[j]; + } + set_bit (FLAG_COMMIT, &ai->flags); + } + +/*** PowerMode processing */ + else if ( !strncmp( line, "PowerMode: ", 11 ) ) { + line += 11; + if ( !strncmp( line, "PSPCAM", 6 ) ) { + ai->config.powerSaveMode = POWERSAVE_PSPCAM; + set_bit (FLAG_COMMIT, &ai->flags); + } else if ( !strncmp( line, "PSP", 3 ) ) { + ai->config.powerSaveMode = POWERSAVE_PSP; + set_bit (FLAG_COMMIT, &ai->flags); + } else { + ai->config.powerSaveMode = POWERSAVE_CAM; + set_bit (FLAG_COMMIT, &ai->flags); + } + } else if ( !strncmp( line, "DataRates: ", 11 ) ) { + int v, i = 0, k = 0; /* i is index into line, + k is index to rates */ + + line += 11; + while((v = get_dec_u16(line, &i, 3))!=-1) { + ai->config.rates[k++] = (u8)v; + line += i + 1; + i = 0; + } + set_bit (FLAG_COMMIT, &ai->flags); + } else if ( !strncmp( line, "Channel: ", 9 ) ) { + int v, i = 0; + line += 9; + v = get_dec_u16(line, &i, i+3); + if ( v != -1 ) { + ai->config.channelSet = cpu_to_le16(v); + set_bit (FLAG_COMMIT, &ai->flags); + } + } else if ( !strncmp( line, "XmitPower: ", 11 ) ) { + int v, i = 0; + line += 11; + v = get_dec_u16(line, &i, i+3); + if ( v != -1 ) { + ai->config.txPower = cpu_to_le16(v); + set_bit (FLAG_COMMIT, &ai->flags); + } + } else if ( !strncmp( line, "WEP: ", 5 ) ) { + line += 5; + switch( line[0] ) { + case 's': + set_auth_type(ai, AUTH_SHAREDKEY); + break; + case 'e': + set_auth_type(ai, AUTH_ENCRYPT); + break; + default: + set_auth_type(ai, AUTH_OPEN); + break; + } + set_bit (FLAG_COMMIT, &ai->flags); + } else if ( !strncmp( line, "LongRetryLimit: ", 16 ) ) { + int v, i = 0; + + line += 16; + v = get_dec_u16(line, &i, 3); + v = (v<0) ? 0 : ((v>255) ? 255 : v); + ai->config.longRetryLimit = cpu_to_le16(v); + set_bit (FLAG_COMMIT, &ai->flags); + } else if ( !strncmp( line, "ShortRetryLimit: ", 17 ) ) { + int v, i = 0; + + line += 17; + v = get_dec_u16(line, &i, 3); + v = (v<0) ? 0 : ((v>255) ? 255 : v); + ai->config.shortRetryLimit = cpu_to_le16(v); + set_bit (FLAG_COMMIT, &ai->flags); + } else if ( !strncmp( line, "RTSThreshold: ", 14 ) ) { + int v, i = 0; + + line += 14; + v = get_dec_u16(line, &i, 4); + v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v); + ai->config.rtsThres = cpu_to_le16(v); + set_bit (FLAG_COMMIT, &ai->flags); + } else if ( !strncmp( line, "TXMSDULifetime: ", 16 ) ) { + int v, i = 0; + + line += 16; + v = get_dec_u16(line, &i, 5); + v = (v<0) ? 0 : v; + ai->config.txLifetime = cpu_to_le16(v); + set_bit (FLAG_COMMIT, &ai->flags); + } else if ( !strncmp( line, "RXMSDULifetime: ", 16 ) ) { + int v, i = 0; + + line += 16; + v = get_dec_u16(line, &i, 5); + v = (v<0) ? 0 : v; + ai->config.rxLifetime = cpu_to_le16(v); + set_bit (FLAG_COMMIT, &ai->flags); + } else if ( !strncmp( line, "TXDiversity: ", 13 ) ) { + ai->config.txDiversity = + (line[13]=='l') ? 1 : + ((line[13]=='r')? 2: 3); + set_bit (FLAG_COMMIT, &ai->flags); + } else if ( !strncmp( line, "RXDiversity: ", 13 ) ) { + ai->config.rxDiversity = + (line[13]=='l') ? 1 : + ((line[13]=='r')? 2: 3); + set_bit (FLAG_COMMIT, &ai->flags); + } else if ( !strncmp( line, "FragThreshold: ", 15 ) ) { + int v, i = 0; + + line += 15; + v = get_dec_u16(line, &i, 4); + v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v); + v = v & 0xfffe; /* Make sure its even */ + ai->config.fragThresh = cpu_to_le16(v); + set_bit (FLAG_COMMIT, &ai->flags); + } else if (!strncmp(line, "Modulation: ", 12)) { + line += 12; + switch(*line) { + case 'd': ai->config.modulation=MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break; + case 'c': ai->config.modulation=MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break; + case 'm': ai->config.modulation=MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break; + default: airo_print_warn(ai->dev->name, "Unknown modulation"); + } + } else if (!strncmp(line, "Preamble: ", 10)) { + line += 10; + switch(*line) { + case 'a': ai->config.preamble=PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break; + case 'l': ai->config.preamble=PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break; + case 's': ai->config.preamble=PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break; + default: airo_print_warn(ai->dev->name, "Unknown preamble"); + } + } else { + airo_print_warn(ai->dev->name, "Couldn't figure out %s", line); + } + while( line[0] && line[0] != '\n' ) line++; + if ( line[0] ) line++; + } + airo_config_commit(dev, NULL, NULL, NULL); +} + +static const char *get_rmode(__le16 mode) +{ + switch(mode & RXMODE_MASK) { + case RXMODE_RFMON: return "rfmon"; + case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon"; + case RXMODE_LANMON: return "lanmon"; + } + return "ESS"; +} + +static int proc_config_open(struct inode *inode, struct file *file) +{ + struct proc_data *data; + struct net_device *dev = PDE_DATA(inode); + struct airo_info *ai = dev->ml_priv; + int i; + __le16 mode; + + if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) + return -ENOMEM; + data = file->private_data; + if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) { + kfree (file->private_data); + return -ENOMEM; + } + if ((data->wbuffer = kzalloc( 2048, GFP_KERNEL )) == NULL) { + kfree (data->rbuffer); + kfree (file->private_data); + return -ENOMEM; + } + data->maxwritelen = 2048; + data->on_close = proc_config_on_close; + + readConfigRid(ai, 1); + + mode = ai->config.opmode & MODE_CFG_MASK; + i = sprintf( data->rbuffer, + "Mode: %s\n" + "Radio: %s\n" + "NodeName: %-16s\n" + "PowerMode: %s\n" + "DataRates: %d %d %d %d %d %d %d %d\n" + "Channel: %d\n" + "XmitPower: %d\n", + mode == MODE_STA_IBSS ? "adhoc" : + mode == MODE_STA_ESS ? get_rmode(ai->config.rmode): + mode == MODE_AP ? "AP" : + mode == MODE_AP_RPTR ? "AP RPTR" : "Error", + test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on", + ai->config.nodeName, + ai->config.powerSaveMode == POWERSAVE_CAM ? "CAM" : + ai->config.powerSaveMode == POWERSAVE_PSP ? "PSP" : + ai->config.powerSaveMode == POWERSAVE_PSPCAM ? "PSPCAM" : + "Error", + (int)ai->config.rates[0], + (int)ai->config.rates[1], + (int)ai->config.rates[2], + (int)ai->config.rates[3], + (int)ai->config.rates[4], + (int)ai->config.rates[5], + (int)ai->config.rates[6], + (int)ai->config.rates[7], + le16_to_cpu(ai->config.channelSet), + le16_to_cpu(ai->config.txPower) + ); + sprintf( data->rbuffer + i, + "LongRetryLimit: %d\n" + "ShortRetryLimit: %d\n" + "RTSThreshold: %d\n" + "TXMSDULifetime: %d\n" + "RXMSDULifetime: %d\n" + "TXDiversity: %s\n" + "RXDiversity: %s\n" + "FragThreshold: %d\n" + "WEP: %s\n" + "Modulation: %s\n" + "Preamble: %s\n", + le16_to_cpu(ai->config.longRetryLimit), + le16_to_cpu(ai->config.shortRetryLimit), + le16_to_cpu(ai->config.rtsThres), + le16_to_cpu(ai->config.txLifetime), + le16_to_cpu(ai->config.rxLifetime), + ai->config.txDiversity == 1 ? "left" : + ai->config.txDiversity == 2 ? "right" : "both", + ai->config.rxDiversity == 1 ? "left" : + ai->config.rxDiversity == 2 ? "right" : "both", + le16_to_cpu(ai->config.fragThresh), + ai->config.authType == AUTH_ENCRYPT ? "encrypt" : + ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open", + ai->config.modulation == MOD_DEFAULT ? "default" : + ai->config.modulation == MOD_CCK ? "cck" : + ai->config.modulation == MOD_MOK ? "mok" : "error", + ai->config.preamble == PREAMBLE_AUTO ? "auto" : + ai->config.preamble == PREAMBLE_LONG ? "long" : + ai->config.preamble == PREAMBLE_SHORT ? "short" : "error" + ); + data->readlen = strlen( data->rbuffer ); + return 0; +} + +static void proc_SSID_on_close(struct inode *inode, struct file *file) +{ + struct proc_data *data = file->private_data; + struct net_device *dev = PDE_DATA(inode); + struct airo_info *ai = dev->ml_priv; + SsidRid SSID_rid; + int i; + char *p = data->wbuffer; + char *end = p + data->writelen; + + if (!data->writelen) + return; + + *end = '\n'; /* sentinel; we have space for it */ + + memset(&SSID_rid, 0, sizeof(SSID_rid)); + + for (i = 0; i < 3 && p < end; i++) { + int j = 0; + /* copy up to 32 characters from this line */ + while (*p != '\n' && j < 32) + SSID_rid.ssids[i].ssid[j++] = *p++; + if (j == 0) + break; + SSID_rid.ssids[i].len = cpu_to_le16(j); + /* skip to the beginning of the next line */ + while (*p++ != '\n') + ; + } + if (i) + SSID_rid.len = cpu_to_le16(sizeof(SSID_rid)); + disable_MAC(ai, 1); + writeSsidRid(ai, &SSID_rid, 1); + enable_MAC(ai, 1); +} + +static void proc_APList_on_close( struct inode *inode, struct file *file ) { + struct proc_data *data = file->private_data; + struct net_device *dev = PDE_DATA(inode); + struct airo_info *ai = dev->ml_priv; + APListRid *APList_rid = &ai->APList; + int i; + + if ( !data->writelen ) return; + + memset(APList_rid, 0, sizeof(*APList_rid)); + APList_rid->len = cpu_to_le16(sizeof(*APList_rid)); + + for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) { + int j; + for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) { + switch(j%3) { + case 0: + APList_rid->ap[i][j/3]= + hex_to_bin(data->wbuffer[j+i*6*3])<<4; + break; + case 1: + APList_rid->ap[i][j/3]|= + hex_to_bin(data->wbuffer[j+i*6*3]); + break; + } + } + } + disable_MAC(ai, 1); + writeAPListRid(ai, APList_rid, 1); + enable_MAC(ai, 1); +} + +/* This function wraps PC4500_writerid with a MAC disable */ +static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data, + int len, int dummy ) { + int rc; + + disable_MAC(ai, 1); + rc = PC4500_writerid(ai, rid, rid_data, len, 1); + enable_MAC(ai, 1); + return rc; +} + +/* Returns the WEP key at the specified index, or -1 if that key does + * not exist. The buffer is assumed to be at least 16 bytes in length. + */ +static int get_wep_key(struct airo_info *ai, u16 index, char *buf, u16 buflen) +{ + WepKeyRid wkr; + int rc; + __le16 lastindex; + + rc = readWepKeyRid(ai, &wkr, 1, 1); + if (rc != SUCCESS) + return -1; + do { + lastindex = wkr.kindex; + if (le16_to_cpu(wkr.kindex) == index) { + int klen = min_t(int, buflen, le16_to_cpu(wkr.klen)); + memcpy(buf, wkr.key, klen); + return klen; + } + rc = readWepKeyRid(ai, &wkr, 0, 1); + if (rc != SUCCESS) + return -1; + } while (lastindex != wkr.kindex); + return -1; +} + +static int get_wep_tx_idx(struct airo_info *ai) +{ + WepKeyRid wkr; + int rc; + __le16 lastindex; + + rc = readWepKeyRid(ai, &wkr, 1, 1); + if (rc != SUCCESS) + return -1; + do { + lastindex = wkr.kindex; + if (wkr.kindex == cpu_to_le16(0xffff)) + return wkr.mac[0]; + rc = readWepKeyRid(ai, &wkr, 0, 1); + if (rc != SUCCESS) + return -1; + } while (lastindex != wkr.kindex); + return -1; +} + +static int set_wep_key(struct airo_info *ai, u16 index, const char *key, + u16 keylen, int perm, int lock) +{ + static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 }; + WepKeyRid wkr; + int rc; + + if (WARN_ON(keylen == 0)) + return -1; + + memset(&wkr, 0, sizeof(wkr)); + wkr.len = cpu_to_le16(sizeof(wkr)); + wkr.kindex = cpu_to_le16(index); + wkr.klen = cpu_to_le16(keylen); + memcpy(wkr.key, key, keylen); + memcpy(wkr.mac, macaddr, ETH_ALEN); + + if (perm) disable_MAC(ai, lock); + rc = writeWepKeyRid(ai, &wkr, perm, lock); + if (perm) enable_MAC(ai, lock); + return rc; +} + +static int set_wep_tx_idx(struct airo_info *ai, u16 index, int perm, int lock) +{ + WepKeyRid wkr; + int rc; + + memset(&wkr, 0, sizeof(wkr)); + wkr.len = cpu_to_le16(sizeof(wkr)); + wkr.kindex = cpu_to_le16(0xffff); + wkr.mac[0] = (char)index; + + if (perm) { + ai->defindex = (char)index; + disable_MAC(ai, lock); + } + + rc = writeWepKeyRid(ai, &wkr, perm, lock); + + if (perm) + enable_MAC(ai, lock); + return rc; +} + +static void proc_wepkey_on_close( struct inode *inode, struct file *file ) { + struct proc_data *data; + struct net_device *dev = PDE_DATA(inode); + struct airo_info *ai = dev->ml_priv; + int i, rc; + char key[16]; + u16 index = 0; + int j = 0; + + memset(key, 0, sizeof(key)); + + data = file->private_data; + if ( !data->writelen ) return; + + if (data->wbuffer[0] >= '0' && data->wbuffer[0] <= '3' && + (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) { + index = data->wbuffer[0] - '0'; + if (data->wbuffer[1] == '\n') { + rc = set_wep_tx_idx(ai, index, 1, 1); + if (rc < 0) { + airo_print_err(ai->dev->name, "failed to set " + "WEP transmit index to %d: %d.", + index, rc); + } + return; + } + j = 2; + } else { + airo_print_err(ai->dev->name, "WepKey passed invalid key index"); + return; + } + + for( i = 0; i < 16*3 && data->wbuffer[i+j]; i++ ) { + switch(i%3) { + case 0: + key[i/3] = hex_to_bin(data->wbuffer[i+j])<<4; + break; + case 1: + key[i/3] |= hex_to_bin(data->wbuffer[i+j]); + break; + } + } + + rc = set_wep_key(ai, index, key, i/3, 1, 1); + if (rc < 0) { + airo_print_err(ai->dev->name, "failed to set WEP key at index " + "%d: %d.", index, rc); + } +} + +static int proc_wepkey_open( struct inode *inode, struct file *file ) +{ + struct proc_data *data; + struct net_device *dev = PDE_DATA(inode); + struct airo_info *ai = dev->ml_priv; + char *ptr; + WepKeyRid wkr; + __le16 lastindex; + int j=0; + int rc; + + if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(&wkr, 0, sizeof(wkr)); + data = file->private_data; + if ((data->rbuffer = kzalloc( 180, GFP_KERNEL )) == NULL) { + kfree (file->private_data); + return -ENOMEM; + } + data->writelen = 0; + data->maxwritelen = 80; + if ((data->wbuffer = kzalloc( 80, GFP_KERNEL )) == NULL) { + kfree (data->rbuffer); + kfree (file->private_data); + return -ENOMEM; + } + data->on_close = proc_wepkey_on_close; + + ptr = data->rbuffer; + strcpy(ptr, "No wep keys\n"); + rc = readWepKeyRid(ai, &wkr, 1, 1); + if (rc == SUCCESS) do { + lastindex = wkr.kindex; + if (wkr.kindex == cpu_to_le16(0xffff)) { + j += sprintf(ptr+j, "Tx key = %d\n", + (int)wkr.mac[0]); + } else { + j += sprintf(ptr+j, "Key %d set with length = %d\n", + le16_to_cpu(wkr.kindex), + le16_to_cpu(wkr.klen)); + } + readWepKeyRid(ai, &wkr, 0, 1); + } while((lastindex != wkr.kindex) && (j < 180-30)); + + data->readlen = strlen( data->rbuffer ); + return 0; +} + +static int proc_SSID_open(struct inode *inode, struct file *file) +{ + struct proc_data *data; + struct net_device *dev = PDE_DATA(inode); + struct airo_info *ai = dev->ml_priv; + int i; + char *ptr; + SsidRid SSID_rid; + + if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) + return -ENOMEM; + data = file->private_data; + if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) { + kfree (file->private_data); + return -ENOMEM; + } + data->writelen = 0; + data->maxwritelen = 33*3; + /* allocate maxwritelen + 1; we'll want a sentinel */ + if ((data->wbuffer = kzalloc(33*3 + 1, GFP_KERNEL)) == NULL) { + kfree (data->rbuffer); + kfree (file->private_data); + return -ENOMEM; + } + data->on_close = proc_SSID_on_close; + + readSsidRid(ai, &SSID_rid); + ptr = data->rbuffer; + for (i = 0; i < 3; i++) { + int j; + size_t len = le16_to_cpu(SSID_rid.ssids[i].len); + if (!len) + break; + if (len > 32) + len = 32; + for (j = 0; j < len && SSID_rid.ssids[i].ssid[j]; j++) + *ptr++ = SSID_rid.ssids[i].ssid[j]; + *ptr++ = '\n'; + } + *ptr = '\0'; + data->readlen = strlen( data->rbuffer ); + return 0; +} + +static int proc_APList_open( struct inode *inode, struct file *file ) { + struct proc_data *data; + struct net_device *dev = PDE_DATA(inode); + struct airo_info *ai = dev->ml_priv; + int i; + char *ptr; + APListRid *APList_rid = &ai->APList; + + if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) + return -ENOMEM; + data = file->private_data; + if ((data->rbuffer = kmalloc( 104, GFP_KERNEL )) == NULL) { + kfree (file->private_data); + return -ENOMEM; + } + data->writelen = 0; + data->maxwritelen = 4*6*3; + if ((data->wbuffer = kzalloc( data->maxwritelen, GFP_KERNEL )) == NULL) { + kfree (data->rbuffer); + kfree (file->private_data); + return -ENOMEM; + } + data->on_close = proc_APList_on_close; + + ptr = data->rbuffer; + for( i = 0; i < 4; i++ ) { +// We end when we find a zero MAC + if ( !*(int*)APList_rid->ap[i] && + !*(int*)&APList_rid->ap[i][2]) break; + ptr += sprintf(ptr, "%pM\n", APList_rid->ap[i]); + } + if (i==0) ptr += sprintf(ptr, "Not using specific APs\n"); + + *ptr = '\0'; + data->readlen = strlen( data->rbuffer ); + return 0; +} + +static int proc_BSSList_open( struct inode *inode, struct file *file ) { + struct proc_data *data; + struct net_device *dev = PDE_DATA(inode); + struct airo_info *ai = dev->ml_priv; + char *ptr; + BSSListRid BSSList_rid; + int rc; + /* If doLoseSync is not 1, we won't do a Lose Sync */ + int doLoseSync = -1; + + if ((file->private_data = kzalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL) + return -ENOMEM; + data = file->private_data; + if ((data->rbuffer = kmalloc( 1024, GFP_KERNEL )) == NULL) { + kfree (file->private_data); + return -ENOMEM; + } + data->writelen = 0; + data->maxwritelen = 0; + data->wbuffer = NULL; + data->on_close = NULL; + + if (file->f_mode & FMODE_WRITE) { + if (!(file->f_mode & FMODE_READ)) { + Cmd cmd; + Resp rsp; + + if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN; + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd=CMD_LISTBSS; + if (down_interruptible(&ai->sem)) + return -ERESTARTSYS; + issuecommand(ai, &cmd, &rsp); + up(&ai->sem); + data->readlen = 0; + return 0; + } + doLoseSync = 1; + } + ptr = data->rbuffer; + /* There is a race condition here if there are concurrent opens. + Since it is a rare condition, we'll just live with it, otherwise + we have to add a spin lock... */ + rc = readBSSListRid(ai, doLoseSync, &BSSList_rid); + while(rc == 0 && BSSList_rid.index != cpu_to_le16(0xffff)) { + ptr += sprintf(ptr, "%pM %*s rssi = %d", + BSSList_rid.bssid, + (int)BSSList_rid.ssidLen, + BSSList_rid.ssid, + le16_to_cpu(BSSList_rid.dBm)); + ptr += sprintf(ptr, " channel = %d %s %s %s %s\n", + le16_to_cpu(BSSList_rid.dsChannel), + BSSList_rid.cap & CAP_ESS ? "ESS" : "", + BSSList_rid.cap & CAP_IBSS ? "adhoc" : "", + BSSList_rid.cap & CAP_PRIVACY ? "wep" : "", + BSSList_rid.cap & CAP_SHORTHDR ? "shorthdr" : ""); + rc = readBSSListRid(ai, 0, &BSSList_rid); + } + *ptr = '\0'; + data->readlen = strlen( data->rbuffer ); + return 0; +} + +static int proc_close( struct inode *inode, struct file *file ) +{ + struct proc_data *data = file->private_data; + + if (data->on_close != NULL) + data->on_close(inode, file); + kfree(data->rbuffer); + kfree(data->wbuffer); + kfree(data); + return 0; +} + +/* Since the card doesn't automatically switch to the right WEP mode, + we will make it do it. If the card isn't associated, every secs we + will switch WEP modes to see if that will help. If the card is + associated we will check every minute to see if anything has + changed. */ +static void timer_func( struct net_device *dev ) { + struct airo_info *apriv = dev->ml_priv; + +/* We don't have a link so try changing the authtype */ + readConfigRid(apriv, 0); + disable_MAC(apriv, 0); + switch(apriv->config.authType) { + case AUTH_ENCRYPT: +/* So drop to OPEN */ + apriv->config.authType = AUTH_OPEN; + break; + case AUTH_SHAREDKEY: + if (apriv->keyindex < auto_wep) { + set_wep_tx_idx(apriv, apriv->keyindex, 0, 0); + apriv->config.authType = AUTH_SHAREDKEY; + apriv->keyindex++; + } else { + /* Drop to ENCRYPT */ + apriv->keyindex = 0; + set_wep_tx_idx(apriv, apriv->defindex, 0, 0); + apriv->config.authType = AUTH_ENCRYPT; + } + break; + default: /* We'll escalate to SHAREDKEY */ + apriv->config.authType = AUTH_SHAREDKEY; + } + set_bit (FLAG_COMMIT, &apriv->flags); + writeConfigRid(apriv, 0); + enable_MAC(apriv, 0); + up(&apriv->sem); + +/* Schedule check to see if the change worked */ + clear_bit(JOB_AUTOWEP, &apriv->jobs); + apriv->expires = RUN_AT(HZ*3); +} + +#ifdef CONFIG_PCI +static int airo_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *pent) +{ + struct net_device *dev; + + if (pci_enable_device(pdev)) + return -ENODEV; + pci_set_master(pdev); + + if (pdev->device == 0x5000 || pdev->device == 0xa504) + dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev); + else + dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev); + if (!dev) { + pci_disable_device(pdev); + return -ENODEV; + } + + pci_set_drvdata(pdev, dev); + return 0; +} + +static void airo_pci_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + airo_print_info(dev->name, "Unregistering..."); + stop_airo_card(dev, 1); + pci_disable_device(pdev); +} + +static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct airo_info *ai = dev->ml_priv; + Cmd cmd; + Resp rsp; + + if (!ai->SSID) + ai->SSID = kmalloc(sizeof(SsidRid), GFP_KERNEL); + if (!ai->SSID) + return -ENOMEM; + readSsidRid(ai, ai->SSID); + memset(&cmd, 0, sizeof(cmd)); + /* the lock will be released at the end of the resume callback */ + if (down_interruptible(&ai->sem)) + return -EAGAIN; + disable_MAC(ai, 0); + netif_device_detach(dev); + ai->power = state; + cmd.cmd = HOSTSLEEP; + issuecommand(ai, &cmd, &rsp); + + pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int airo_pci_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct airo_info *ai = dev->ml_priv; + pci_power_t prev_state = pdev->current_state; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D0, 0); + + if (prev_state != PCI_D1) { + reset_card(dev, 0); + mpi_init_descriptors(ai); + setup_card(ai, dev->dev_addr, 0); + clear_bit(FLAG_RADIO_OFF, &ai->flags); + clear_bit(FLAG_PENDING_XMIT, &ai->flags); + } else { + OUT4500(ai, EVACK, EV_AWAKEN); + OUT4500(ai, EVACK, EV_AWAKEN); + msleep(100); + } + + set_bit(FLAG_COMMIT, &ai->flags); + disable_MAC(ai, 0); + msleep(200); + if (ai->SSID) { + writeSsidRid(ai, ai->SSID, 0); + kfree(ai->SSID); + ai->SSID = NULL; + } + writeAPListRid(ai, &ai->APList, 0); + writeConfigRid(ai, 0); + enable_MAC(ai, 0); + ai->power = PMSG_ON; + netif_device_attach(dev); + netif_wake_queue(dev); + enable_interrupts(ai); + up(&ai->sem); + return 0; +} +#endif + +static int __init airo_init_module( void ) +{ + int i; + + proc_kuid = make_kuid(&init_user_ns, proc_uid); + proc_kgid = make_kgid(&init_user_ns, proc_gid); + if (!uid_valid(proc_kuid) || !gid_valid(proc_kgid)) + return -EINVAL; + + airo_entry = proc_mkdir_mode("driver/aironet", airo_perm, NULL); + + if (airo_entry) + proc_set_user(airo_entry, proc_kuid, proc_kgid); + + for (i = 0; i < 4 && io[i] && irq[i]; i++) { + airo_print_info("", "Trying to configure ISA adapter at irq=%d " + "io=0x%x", irq[i], io[i] ); + if (init_airo_card( irq[i], io[i], 0, NULL )) + /* do nothing */ ; + } + +#ifdef CONFIG_PCI + airo_print_info("", "Probing for PCI adapters"); + i = pci_register_driver(&airo_driver); + airo_print_info("", "Finished probing for PCI adapters"); + + if (i) { + remove_proc_entry("driver/aironet", NULL); + return i; + } +#endif + + /* Always exit with success, as we are a library module + * as well as a driver module + */ + return 0; +} + +static void __exit airo_cleanup_module( void ) +{ + struct airo_info *ai; + while(!list_empty(&airo_devices)) { + ai = list_entry(airo_devices.next, struct airo_info, dev_list); + airo_print_info(ai->dev->name, "Unregistering..."); + stop_airo_card(ai->dev, 1); + } +#ifdef CONFIG_PCI + pci_unregister_driver(&airo_driver); +#endif + remove_proc_entry("driver/aironet", NULL); +} + +/* + * Initial Wireless Extension code for Aironet driver by : + * Jean Tourrilhes - HPL - 17 November 00 + * Conversion to new driver API by : + * Jean Tourrilhes - HPL - 26 March 02 + * Javier also did a good amount of work here, adding some new extensions + * and fixing my code. Let's just say that without him this code just + * would not work at all... - Jean II + */ + +static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi) +{ + if (!rssi_rid) + return 0; + + return (0x100 - rssi_rid[rssi].rssidBm); +} + +static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm) +{ + int i; + + if (!rssi_rid) + return 0; + + for (i = 0; i < 256; i++) + if (rssi_rid[i].rssidBm == dbm) + return rssi_rid[i].rssipct; + + return 0; +} + + +static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid) +{ + int quality = 0; + u16 sq; + + if ((status_rid->mode & cpu_to_le16(0x3f)) != cpu_to_le16(0x3f)) + return 0; + + if (!(cap_rid->hardCap & cpu_to_le16(8))) + return 0; + + sq = le16_to_cpu(status_rid->signalQuality); + if (memcmp(cap_rid->prodName, "350", 3)) + if (sq > 0x20) + quality = 0; + else + quality = 0x20 - sq; + else + if (sq > 0xb0) + quality = 0; + else if (sq < 0x10) + quality = 0xa0; + else + quality = 0xb0 - sq; + return quality; +} + +#define airo_get_max_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x20 : 0xa0) +#define airo_get_avg_quality(cap_rid) (memcmp((cap_rid)->prodName, "350", 3) ? 0x10 : 0x50); + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get protocol name + */ +static int airo_get_name(struct net_device *dev, + struct iw_request_info *info, + char *cwrq, + char *extra) +{ + strcpy(cwrq, "IEEE 802.11-DS"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set frequency + */ +static int airo_set_freq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + int rc = -EINPROGRESS; /* Call commit handler */ + + /* If setting by frequency, convert to a channel */ + if(fwrq->e == 1) { + int f = fwrq->m / 100000; + + /* Hack to fall through... */ + fwrq->e = 0; + fwrq->m = ieee80211_frequency_to_channel(f); + } + /* Setting by channel number */ + if((fwrq->m > 1000) || (fwrq->e > 0)) + rc = -EOPNOTSUPP; + else { + int channel = fwrq->m; + /* We should do a better check than that, + * based on the card capability !!! */ + if((channel < 1) || (channel > 14)) { + airo_print_dbg(dev->name, "New channel value of %d is invalid!", + fwrq->m); + rc = -EINVAL; + } else { + readConfigRid(local, 1); + /* Yes ! We can set it !!! */ + local->config.channelSet = cpu_to_le16(channel); + set_bit (FLAG_COMMIT, &local->flags); + } + } + return rc; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get frequency + */ +static int airo_get_freq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + StatusRid status_rid; /* Card status info */ + int ch; + + readConfigRid(local, 1); + if ((local->config.opmode & MODE_CFG_MASK) == MODE_STA_ESS) + status_rid.channel = local->config.channelSet; + else + readStatusRid(local, &status_rid, 1); + + ch = le16_to_cpu(status_rid.channel); + if((ch > 0) && (ch < 15)) { + fwrq->m = 100000 * + ieee80211_channel_to_frequency(ch, IEEE80211_BAND_2GHZ); + fwrq->e = 1; + } else { + fwrq->m = ch; + fwrq->e = 0; + } + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set ESSID + */ +static int airo_set_essid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + SsidRid SSID_rid; /* SSIDs */ + + /* Reload the list of current SSID */ + readSsidRid(local, &SSID_rid); + + /* Check if we asked for `any' */ + if (dwrq->flags == 0) { + /* Just send an empty SSID list */ + memset(&SSID_rid, 0, sizeof(SSID_rid)); + } else { + unsigned index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + /* Check the size of the string */ + if (dwrq->length > IW_ESSID_MAX_SIZE) + return -E2BIG ; + + /* Check if index is valid */ + if (index >= ARRAY_SIZE(SSID_rid.ssids)) + return -EINVAL; + + /* Set the SSID */ + memset(SSID_rid.ssids[index].ssid, 0, + sizeof(SSID_rid.ssids[index].ssid)); + memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length); + SSID_rid.ssids[index].len = cpu_to_le16(dwrq->length); + } + SSID_rid.len = cpu_to_le16(sizeof(SSID_rid)); + /* Write it to the card */ + disable_MAC(local, 1); + writeSsidRid(local, &SSID_rid, 1); + enable_MAC(local, 1); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get ESSID + */ +static int airo_get_essid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + StatusRid status_rid; /* Card status info */ + + readStatusRid(local, &status_rid, 1); + + /* Note : if dwrq->flags != 0, we should + * get the relevant SSID from the SSID list... */ + + /* Get the current SSID */ + memcpy(extra, status_rid.SSID, le16_to_cpu(status_rid.SSIDlen)); + /* If none, we may want to get the one that was set */ + + /* Push it out ! */ + dwrq->length = le16_to_cpu(status_rid.SSIDlen); + dwrq->flags = 1; /* active */ + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set AP address + */ +static int airo_set_wap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + Cmd cmd; + Resp rsp; + APListRid *APList_rid = &local->APList; + + if (awrq->sa_family != ARPHRD_ETHER) + return -EINVAL; + else if (is_broadcast_ether_addr(awrq->sa_data) || + is_zero_ether_addr(awrq->sa_data)) { + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd=CMD_LOSE_SYNC; + if (down_interruptible(&local->sem)) + return -ERESTARTSYS; + issuecommand(local, &cmd, &rsp); + up(&local->sem); + } else { + memset(APList_rid, 0, sizeof(*APList_rid)); + APList_rid->len = cpu_to_le16(sizeof(*APList_rid)); + memcpy(APList_rid->ap[0], awrq->sa_data, ETH_ALEN); + disable_MAC(local, 1); + writeAPListRid(local, APList_rid, 1); + enable_MAC(local, 1); + } + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get AP address + */ +static int airo_get_wap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + StatusRid status_rid; /* Card status info */ + + readStatusRid(local, &status_rid, 1); + + /* Tentative. This seems to work, wow, I'm lucky !!! */ + memcpy(awrq->sa_data, status_rid.bssid[0], ETH_ALEN); + awrq->sa_family = ARPHRD_ETHER; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Nickname + */ +static int airo_set_nick(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + + /* Check the size of the string */ + if(dwrq->length > 16) { + return -E2BIG; + } + readConfigRid(local, 1); + memset(local->config.nodeName, 0, sizeof(local->config.nodeName)); + memcpy(local->config.nodeName, extra, dwrq->length); + set_bit (FLAG_COMMIT, &local->flags); + + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Nickname + */ +static int airo_get_nick(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + + readConfigRid(local, 1); + strncpy(extra, local->config.nodeName, 16); + extra[16] = '\0'; + dwrq->length = strlen(extra); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Bit-Rate + */ +static int airo_set_rate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + CapabilityRid cap_rid; /* Card capability info */ + u8 brate = 0; + int i; + + /* First : get a valid bit rate value */ + readCapabilityRid(local, &cap_rid, 1); + + /* Which type of value ? */ + if((vwrq->value < 8) && (vwrq->value >= 0)) { + /* Setting by rate index */ + /* Find value in the magic rate table */ + brate = cap_rid.supportedRates[vwrq->value]; + } else { + /* Setting by frequency value */ + u8 normvalue = (u8) (vwrq->value/500000); + + /* Check if rate is valid */ + for(i = 0 ; i < 8 ; i++) { + if(normvalue == cap_rid.supportedRates[i]) { + brate = normvalue; + break; + } + } + } + /* -1 designed the max rate (mostly auto mode) */ + if(vwrq->value == -1) { + /* Get the highest available rate */ + for(i = 0 ; i < 8 ; i++) { + if(cap_rid.supportedRates[i] == 0) + break; + } + if(i != 0) + brate = cap_rid.supportedRates[i - 1]; + } + /* Check that it is valid */ + if(brate == 0) { + return -EINVAL; + } + + readConfigRid(local, 1); + /* Now, check if we want a fixed or auto value */ + if(vwrq->fixed == 0) { + /* Fill all the rates up to this max rate */ + memset(local->config.rates, 0, 8); + for(i = 0 ; i < 8 ; i++) { + local->config.rates[i] = cap_rid.supportedRates[i]; + if(local->config.rates[i] == brate) + break; + } + } else { + /* Fixed mode */ + /* One rate, fixed */ + memset(local->config.rates, 0, 8); + local->config.rates[0] = brate; + } + set_bit (FLAG_COMMIT, &local->flags); + + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Bit-Rate + */ +static int airo_get_rate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + StatusRid status_rid; /* Card status info */ + + readStatusRid(local, &status_rid, 1); + + vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000; + /* If more than one rate, set auto */ + readConfigRid(local, 1); + vwrq->fixed = (local->config.rates[1] == 0); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set RTS threshold + */ +static int airo_set_rts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + int rthr = vwrq->value; + + if(vwrq->disabled) + rthr = AIRO_DEF_MTU; + if((rthr < 0) || (rthr > AIRO_DEF_MTU)) { + return -EINVAL; + } + readConfigRid(local, 1); + local->config.rtsThres = cpu_to_le16(rthr); + set_bit (FLAG_COMMIT, &local->flags); + + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get RTS threshold + */ +static int airo_get_rts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + + readConfigRid(local, 1); + vwrq->value = le16_to_cpu(local->config.rtsThres); + vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU); + vwrq->fixed = 1; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Fragmentation threshold + */ +static int airo_set_frag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + int fthr = vwrq->value; + + if(vwrq->disabled) + fthr = AIRO_DEF_MTU; + if((fthr < 256) || (fthr > AIRO_DEF_MTU)) { + return -EINVAL; + } + fthr &= ~0x1; /* Get an even value - is it really needed ??? */ + readConfigRid(local, 1); + local->config.fragThresh = cpu_to_le16(fthr); + set_bit (FLAG_COMMIT, &local->flags); + + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Fragmentation threshold + */ +static int airo_get_frag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + + readConfigRid(local, 1); + vwrq->value = le16_to_cpu(local->config.fragThresh); + vwrq->disabled = (vwrq->value >= AIRO_DEF_MTU); + vwrq->fixed = 1; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Mode of Operation + */ +static int airo_set_mode(struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + int reset = 0; + + readConfigRid(local, 1); + if (sniffing_mode(local)) + reset = 1; + + switch(*uwrq) { + case IW_MODE_ADHOC: + local->config.opmode &= ~MODE_CFG_MASK; + local->config.opmode |= MODE_STA_IBSS; + local->config.rmode &= ~RXMODE_FULL_MASK; + local->config.scanMode = SCANMODE_ACTIVE; + clear_bit (FLAG_802_11, &local->flags); + break; + case IW_MODE_INFRA: + local->config.opmode &= ~MODE_CFG_MASK; + local->config.opmode |= MODE_STA_ESS; + local->config.rmode &= ~RXMODE_FULL_MASK; + local->config.scanMode = SCANMODE_ACTIVE; + clear_bit (FLAG_802_11, &local->flags); + break; + case IW_MODE_MASTER: + local->config.opmode &= ~MODE_CFG_MASK; + local->config.opmode |= MODE_AP; + local->config.rmode &= ~RXMODE_FULL_MASK; + local->config.scanMode = SCANMODE_ACTIVE; + clear_bit (FLAG_802_11, &local->flags); + break; + case IW_MODE_REPEAT: + local->config.opmode &= ~MODE_CFG_MASK; + local->config.opmode |= MODE_AP_RPTR; + local->config.rmode &= ~RXMODE_FULL_MASK; + local->config.scanMode = SCANMODE_ACTIVE; + clear_bit (FLAG_802_11, &local->flags); + break; + case IW_MODE_MONITOR: + local->config.opmode &= ~MODE_CFG_MASK; + local->config.opmode |= MODE_STA_ESS; + local->config.rmode &= ~RXMODE_FULL_MASK; + local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER; + local->config.scanMode = SCANMODE_PASSIVE; + set_bit (FLAG_802_11, &local->flags); + break; + default: + return -EINVAL; + } + if (reset) + set_bit (FLAG_RESET, &local->flags); + set_bit (FLAG_COMMIT, &local->flags); + + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Mode of Operation + */ +static int airo_get_mode(struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + + readConfigRid(local, 1); + /* If not managed, assume it's ad-hoc */ + switch (local->config.opmode & MODE_CFG_MASK) { + case MODE_STA_ESS: + *uwrq = IW_MODE_INFRA; + break; + case MODE_AP: + *uwrq = IW_MODE_MASTER; + break; + case MODE_AP_RPTR: + *uwrq = IW_MODE_REPEAT; + break; + default: + *uwrq = IW_MODE_ADHOC; + } + + return 0; +} + +static inline int valid_index(struct airo_info *ai, int index) +{ + return (index >= 0) && (index <= ai->max_wep_idx); +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Encryption Key + */ +static int airo_set_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + int perm = (dwrq->flags & IW_ENCODE_TEMP ? 0 : 1); + __le16 currentAuthType = local->config.authType; + int rc = 0; + + if (!local->wep_capable) + return -EOPNOTSUPP; + + readConfigRid(local, 1); + + /* Basic checking: do we have a key to set ? + * Note : with the new API, it's impossible to get a NULL pointer. + * Therefore, we need to check a key size == 0 instead. + * New version of iwconfig properly set the IW_ENCODE_NOKEY flag + * when no key is present (only change flags), but older versions + * don't do it. - Jean II */ + if (dwrq->length > 0) { + wep_key_t key; + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + int current_index; + + /* Check the size of the key */ + if (dwrq->length > MAX_KEY_SIZE) { + return -EINVAL; + } + + current_index = get_wep_tx_idx(local); + if (current_index < 0) + current_index = 0; + + /* Check the index (none -> use current) */ + if (!valid_index(local, index)) + index = current_index; + + /* Set the length */ + if (dwrq->length > MIN_KEY_SIZE) + key.len = MAX_KEY_SIZE; + else + key.len = MIN_KEY_SIZE; + /* Check if the key is not marked as invalid */ + if(!(dwrq->flags & IW_ENCODE_NOKEY)) { + /* Cleanup */ + memset(key.key, 0, MAX_KEY_SIZE); + /* Copy the key in the driver */ + memcpy(key.key, extra, dwrq->length); + /* Send the key to the card */ + rc = set_wep_key(local, index, key.key, key.len, perm, 1); + if (rc < 0) { + airo_print_err(local->dev->name, "failed to set" + " WEP key at index %d: %d.", + index, rc); + return rc; + } + } + /* WE specify that if a valid key is set, encryption + * should be enabled (user may turn it off later) + * This is also how "iwconfig ethX key on" works */ + if((index == current_index) && (key.len > 0) && + (local->config.authType == AUTH_OPEN)) + set_auth_type(local, AUTH_ENCRYPT); + } else { + /* Do we want to just set the transmit key index ? */ + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (valid_index(local, index)) { + rc = set_wep_tx_idx(local, index, perm, 1); + if (rc < 0) { + airo_print_err(local->dev->name, "failed to set" + " WEP transmit index to %d: %d.", + index, rc); + return rc; + } + } else { + /* Don't complain if only change the mode */ + if (!(dwrq->flags & IW_ENCODE_MODE)) + return -EINVAL; + } + } + /* Read the flags */ + if (dwrq->flags & IW_ENCODE_DISABLED) + set_auth_type(local, AUTH_OPEN); /* disable encryption */ + if(dwrq->flags & IW_ENCODE_RESTRICTED) + set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ + if (dwrq->flags & IW_ENCODE_OPEN) + set_auth_type(local, AUTH_ENCRYPT); /* Only Wep */ + /* Commit the changes to flags if needed */ + if (local->config.authType != currentAuthType) + set_bit (FLAG_COMMIT, &local->flags); + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Encryption Key + */ +static int airo_get_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + int wep_key_len; + u8 buf[16]; + + if (!local->wep_capable) + return -EOPNOTSUPP; + + readConfigRid(local, 1); + + /* Check encryption mode */ + switch(local->config.authType) { + case AUTH_ENCRYPT: + dwrq->flags = IW_ENCODE_OPEN; + break; + case AUTH_SHAREDKEY: + dwrq->flags = IW_ENCODE_RESTRICTED; + break; + default: + case AUTH_OPEN: + dwrq->flags = IW_ENCODE_DISABLED; + break; + } + /* We can't return the key, so set the proper flag and return zero */ + dwrq->flags |= IW_ENCODE_NOKEY; + memset(extra, 0, 16); + + /* Which key do we want ? -1 -> tx index */ + if (!valid_index(local, index)) { + index = get_wep_tx_idx(local); + if (index < 0) + index = 0; + } + dwrq->flags |= index + 1; + + /* Copy the key to the user buffer */ + wep_key_len = get_wep_key(local, index, &buf[0], sizeof(buf)); + if (wep_key_len < 0) { + dwrq->length = 0; + } else { + dwrq->length = wep_key_len; + memcpy(extra, buf, dwrq->length); + } + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set extended Encryption parameters + */ +static int airo_set_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int perm = ( encoding->flags & IW_ENCODE_TEMP ? 0 : 1 ); + __le16 currentAuthType = local->config.authType; + int idx, key_len, alg = ext->alg, set_key = 1, rc; + wep_key_t key; + + if (!local->wep_capable) + return -EOPNOTSUPP; + + readConfigRid(local, 1); + + /* Determine and validate the key index */ + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if (!valid_index(local, idx - 1)) + return -EINVAL; + idx--; + } else { + idx = get_wep_tx_idx(local); + if (idx < 0) + idx = 0; + } + + if (encoding->flags & IW_ENCODE_DISABLED) + alg = IW_ENCODE_ALG_NONE; + + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + /* Only set transmit key index here, actual + * key is set below if needed. + */ + rc = set_wep_tx_idx(local, idx, perm, 1); + if (rc < 0) { + airo_print_err(local->dev->name, "failed to set " + "WEP transmit index to %d: %d.", + idx, rc); + return rc; + } + set_key = ext->key_len > 0 ? 1 : 0; + } + + if (set_key) { + /* Set the requested key first */ + memset(key.key, 0, MAX_KEY_SIZE); + switch (alg) { + case IW_ENCODE_ALG_NONE: + key.len = 0; + break; + case IW_ENCODE_ALG_WEP: + if (ext->key_len > MIN_KEY_SIZE) { + key.len = MAX_KEY_SIZE; + } else if (ext->key_len > 0) { + key.len = MIN_KEY_SIZE; + } else { + return -EINVAL; + } + key_len = min (ext->key_len, key.len); + memcpy(key.key, ext->key, key_len); + break; + default: + return -EINVAL; + } + if (key.len == 0) { + rc = set_wep_tx_idx(local, idx, perm, 1); + if (rc < 0) { + airo_print_err(local->dev->name, + "failed to set WEP transmit index to %d: %d.", + idx, rc); + return rc; + } + } else { + rc = set_wep_key(local, idx, key.key, key.len, perm, 1); + if (rc < 0) { + airo_print_err(local->dev->name, + "failed to set WEP key at index %d: %d.", + idx, rc); + return rc; + } + } + } + + /* Read the flags */ + if (encoding->flags & IW_ENCODE_DISABLED) + set_auth_type(local, AUTH_OPEN); /* disable encryption */ + if(encoding->flags & IW_ENCODE_RESTRICTED) + set_auth_type(local, AUTH_SHAREDKEY); /* Only Both */ + if (encoding->flags & IW_ENCODE_OPEN) + set_auth_type(local, AUTH_ENCRYPT); + /* Commit the changes to flags if needed */ + if (local->config.authType != currentAuthType) + set_bit (FLAG_COMMIT, &local->flags); + + return -EINPROGRESS; +} + + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get extended Encryption parameters + */ +static int airo_get_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, max_key_len, wep_key_len; + u8 buf[16]; + + if (!local->wep_capable) + return -EOPNOTSUPP; + + readConfigRid(local, 1); + + max_key_len = encoding->length - sizeof(*ext); + if (max_key_len < 0) + return -EINVAL; + + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if (!valid_index(local, idx - 1)) + return -EINVAL; + idx--; + } else { + idx = get_wep_tx_idx(local); + if (idx < 0) + idx = 0; + } + + encoding->flags = idx + 1; + memset(ext, 0, sizeof(*ext)); + + /* Check encryption mode */ + switch(local->config.authType) { + case AUTH_ENCRYPT: + encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED; + break; + case AUTH_SHAREDKEY: + encoding->flags = IW_ENCODE_ALG_WEP | IW_ENCODE_ENABLED; + break; + default: + case AUTH_OPEN: + encoding->flags = IW_ENCODE_ALG_NONE | IW_ENCODE_DISABLED; + break; + } + /* We can't return the key, so set the proper flag and return zero */ + encoding->flags |= IW_ENCODE_NOKEY; + memset(extra, 0, 16); + + /* Copy the key to the user buffer */ + wep_key_len = get_wep_key(local, idx, &buf[0], sizeof(buf)); + if (wep_key_len < 0) { + ext->key_len = 0; + } else { + ext->key_len = wep_key_len; + memcpy(extra, buf, ext->key_len); + } + + return 0; +} + + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set extended authentication parameters + */ +static int airo_set_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct airo_info *local = dev->ml_priv; + struct iw_param *param = &wrqu->param; + __le16 currentAuthType = local->config.authType; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_PRIVACY_INVOKED: + /* + * airo does not use these parameters + */ + break; + + case IW_AUTH_DROP_UNENCRYPTED: + if (param->value) { + /* Only change auth type if unencrypted */ + if (currentAuthType == AUTH_OPEN) + set_auth_type(local, AUTH_ENCRYPT); + } else { + set_auth_type(local, AUTH_OPEN); + } + + /* Commit the changes to flags if needed */ + if (local->config.authType != currentAuthType) + set_bit (FLAG_COMMIT, &local->flags); + break; + + case IW_AUTH_80211_AUTH_ALG: { + if (param->value & IW_AUTH_ALG_SHARED_KEY) { + set_auth_type(local, AUTH_SHAREDKEY); + } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { + /* We don't know here if WEP open system or + * unencrypted mode was requested - so use the + * last mode (of these two) used last time + */ + set_auth_type(local, local->last_auth); + } else + return -EINVAL; + + /* Commit the changes to flags if needed */ + if (local->config.authType != currentAuthType) + set_bit (FLAG_COMMIT, &local->flags); + break; + } + + case IW_AUTH_WPA_ENABLED: + /* Silently accept disable of WPA */ + if (param->value > 0) + return -EOPNOTSUPP; + break; + + default: + return -EOPNOTSUPP; + } + return -EINPROGRESS; +} + + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get extended authentication parameters + */ +static int airo_get_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct airo_info *local = dev->ml_priv; + struct iw_param *param = &wrqu->param; + __le16 currentAuthType = local->config.authType; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_DROP_UNENCRYPTED: + switch (currentAuthType) { + case AUTH_SHAREDKEY: + case AUTH_ENCRYPT: + param->value = 1; + break; + default: + param->value = 0; + break; + } + break; + + case IW_AUTH_80211_AUTH_ALG: + switch (currentAuthType) { + case AUTH_SHAREDKEY: + param->value = IW_AUTH_ALG_SHARED_KEY; + break; + case AUTH_ENCRYPT: + default: + param->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + } + break; + + case IW_AUTH_WPA_ENABLED: + param->value = 0; + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Tx-Power + */ +static int airo_set_txpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + CapabilityRid cap_rid; /* Card capability info */ + int i; + int rc = -EINVAL; + __le16 v = cpu_to_le16(vwrq->value); + + readCapabilityRid(local, &cap_rid, 1); + + if (vwrq->disabled) { + set_bit (FLAG_RADIO_OFF, &local->flags); + set_bit (FLAG_COMMIT, &local->flags); + return -EINPROGRESS; /* Call commit handler */ + } + if (vwrq->flags != IW_TXPOW_MWATT) { + return -EINVAL; + } + clear_bit (FLAG_RADIO_OFF, &local->flags); + for (i = 0; i < 8 && cap_rid.txPowerLevels[i]; i++) + if (v == cap_rid.txPowerLevels[i]) { + readConfigRid(local, 1); + local->config.txPower = v; + set_bit (FLAG_COMMIT, &local->flags); + rc = -EINPROGRESS; /* Call commit handler */ + break; + } + return rc; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Tx-Power + */ +static int airo_get_txpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + + readConfigRid(local, 1); + vwrq->value = le16_to_cpu(local->config.txPower); + vwrq->fixed = 1; /* No power control */ + vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags); + vwrq->flags = IW_TXPOW_MWATT; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Retry limits + */ +static int airo_set_retry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + int rc = -EINVAL; + + if(vwrq->disabled) { + return -EINVAL; + } + readConfigRid(local, 1); + if(vwrq->flags & IW_RETRY_LIMIT) { + __le16 v = cpu_to_le16(vwrq->value); + if(vwrq->flags & IW_RETRY_LONG) + local->config.longRetryLimit = v; + else if (vwrq->flags & IW_RETRY_SHORT) + local->config.shortRetryLimit = v; + else { + /* No modifier : set both */ + local->config.longRetryLimit = v; + local->config.shortRetryLimit = v; + } + set_bit (FLAG_COMMIT, &local->flags); + rc = -EINPROGRESS; /* Call commit handler */ + } + if(vwrq->flags & IW_RETRY_LIFETIME) { + local->config.txLifetime = cpu_to_le16(vwrq->value / 1024); + set_bit (FLAG_COMMIT, &local->flags); + rc = -EINPROGRESS; /* Call commit handler */ + } + return rc; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Retry limits + */ +static int airo_get_retry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + + vwrq->disabled = 0; /* Can't be disabled */ + + readConfigRid(local, 1); + /* Note : by default, display the min retry number */ + if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { + vwrq->flags = IW_RETRY_LIFETIME; + vwrq->value = le16_to_cpu(local->config.txLifetime) * 1024; + } else if((vwrq->flags & IW_RETRY_LONG)) { + vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; + vwrq->value = le16_to_cpu(local->config.longRetryLimit); + } else { + vwrq->flags = IW_RETRY_LIMIT; + vwrq->value = le16_to_cpu(local->config.shortRetryLimit); + if(local->config.shortRetryLimit != local->config.longRetryLimit) + vwrq->flags |= IW_RETRY_SHORT; + } + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get range info + */ +static int airo_get_range(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + struct iw_range *range = (struct iw_range *) extra; + CapabilityRid cap_rid; /* Card capability info */ + int i; + int k; + + readCapabilityRid(local, &cap_rid, 1); + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(*range)); + range->min_nwid = 0x0000; + range->max_nwid = 0x0000; + range->num_channels = 14; + /* Should be based on cap_rid.country to give only + * what the current card support */ + k = 0; + for(i = 0; i < 14; i++) { + range->freq[k].i = i + 1; /* List index */ + range->freq[k].m = 100000 * + ieee80211_channel_to_frequency(i + 1, IEEE80211_BAND_2GHZ); + range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */ + } + range->num_frequency = k; + + range->sensitivity = 65535; + + /* Hum... Should put the right values there */ + if (local->rssi) + range->max_qual.qual = 100; /* % */ + else + range->max_qual.qual = airo_get_max_quality(&cap_rid); + range->max_qual.level = 0x100 - 120; /* -120 dBm */ + range->max_qual.noise = 0x100 - 120; /* -120 dBm */ + + /* Experimental measurements - boundary 11/5.5 Mb/s */ + /* Note : with or without the (local->rssi), results + * are somewhat different. - Jean II */ + if (local->rssi) { + range->avg_qual.qual = 50; /* % */ + range->avg_qual.level = 0x100 - 70; /* -70 dBm */ + } else { + range->avg_qual.qual = airo_get_avg_quality(&cap_rid); + range->avg_qual.level = 0x100 - 80; /* -80 dBm */ + } + range->avg_qual.noise = 0x100 - 85; /* -85 dBm */ + + for(i = 0 ; i < 8 ; i++) { + range->bitrate[i] = cap_rid.supportedRates[i] * 500000; + if(range->bitrate[i] == 0) + break; + } + range->num_bitrates = i; + + /* Set an indication of the max TCP throughput + * in bit/s that we can expect using this interface. + * May be use for QoS stuff... Jean II */ + if(i > 2) + range->throughput = 5000 * 1000; + else + range->throughput = 1500 * 1000; + + range->min_rts = 0; + range->max_rts = AIRO_DEF_MTU; + range->min_frag = 256; + range->max_frag = AIRO_DEF_MTU; + + if(cap_rid.softCap & cpu_to_le16(2)) { + // WEP: RC4 40 bits + range->encoding_size[0] = 5; + // RC4 ~128 bits + if (cap_rid.softCap & cpu_to_le16(0x100)) { + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + } else + range->num_encoding_sizes = 1; + range->max_encoding_tokens = + cap_rid.softCap & cpu_to_le16(0x80) ? 4 : 1; + } else { + range->num_encoding_sizes = 0; + range->max_encoding_tokens = 0; + } + range->min_pmp = 0; + range->max_pmp = 5000000; /* 5 secs */ + range->min_pmt = 0; + range->max_pmt = 65535 * 1024; /* ??? */ + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + /* Transmit Power - values are in mW */ + for(i = 0 ; i < 8 ; i++) { + range->txpower[i] = le16_to_cpu(cap_rid.txPowerLevels[i]); + if(range->txpower[i] == 0) + break; + } + range->num_txpower = i; + range->txpower_capa = IW_TXPOW_MWATT; + range->we_version_source = 19; + range->we_version_compiled = WIRELESS_EXT; + range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; + range->retry_flags = IW_RETRY_LIMIT; + range->r_time_flags = IW_RETRY_LIFETIME; + range->min_retry = 1; + range->max_retry = 65535; + range->min_r_time = 1024; + range->max_r_time = 65535 * 1024; + + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Power Management + */ +static int airo_set_power(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + + readConfigRid(local, 1); + if (vwrq->disabled) { + if (sniffing_mode(local)) + return -EINVAL; + local->config.powerSaveMode = POWERSAVE_CAM; + local->config.rmode &= ~RXMODE_MASK; + local->config.rmode |= RXMODE_BC_MC_ADDR; + set_bit (FLAG_COMMIT, &local->flags); + return -EINPROGRESS; /* Call commit handler */ + } + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + local->config.fastListenDelay = cpu_to_le16((vwrq->value + 500) / 1024); + local->config.powerSaveMode = POWERSAVE_PSPCAM; + set_bit (FLAG_COMMIT, &local->flags); + } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { + local->config.fastListenInterval = + local->config.listenInterval = + cpu_to_le16((vwrq->value + 500) / 1024); + local->config.powerSaveMode = POWERSAVE_PSPCAM; + set_bit (FLAG_COMMIT, &local->flags); + } + switch (vwrq->flags & IW_POWER_MODE) { + case IW_POWER_UNICAST_R: + if (sniffing_mode(local)) + return -EINVAL; + local->config.rmode &= ~RXMODE_MASK; + local->config.rmode |= RXMODE_ADDR; + set_bit (FLAG_COMMIT, &local->flags); + break; + case IW_POWER_ALL_R: + if (sniffing_mode(local)) + return -EINVAL; + local->config.rmode &= ~RXMODE_MASK; + local->config.rmode |= RXMODE_BC_MC_ADDR; + set_bit (FLAG_COMMIT, &local->flags); + case IW_POWER_ON: + /* This is broken, fixme ;-) */ + break; + default: + return -EINVAL; + } + // Note : we may want to factor local->need_commit here + // Note2 : may also want to factor RXMODE_RFMON test + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Power Management + */ +static int airo_get_power(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + __le16 mode; + + readConfigRid(local, 1); + mode = local->config.powerSaveMode; + if ((vwrq->disabled = (mode == POWERSAVE_CAM))) + return 0; + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + vwrq->value = le16_to_cpu(local->config.fastListenDelay) * 1024; + vwrq->flags = IW_POWER_TIMEOUT; + } else { + vwrq->value = le16_to_cpu(local->config.fastListenInterval) * 1024; + vwrq->flags = IW_POWER_PERIOD; + } + if ((local->config.rmode & RXMODE_MASK) == RXMODE_ADDR) + vwrq->flags |= IW_POWER_UNICAST_R; + else + vwrq->flags |= IW_POWER_ALL_R; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : set Sensitivity + */ +static int airo_set_sens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + + readConfigRid(local, 1); + local->config.rssiThreshold = + cpu_to_le16(vwrq->disabled ? RSSI_DEFAULT : vwrq->value); + set_bit (FLAG_COMMIT, &local->flags); + + return -EINPROGRESS; /* Call commit handler */ +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get Sensitivity + */ +static int airo_get_sens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + + readConfigRid(local, 1); + vwrq->value = le16_to_cpu(local->config.rssiThreshold); + vwrq->disabled = (vwrq->value == 0); + vwrq->fixed = 1; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : get AP List + * Note : this is deprecated in favor of IWSCAN + */ +static int airo_get_aplist(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *local = dev->ml_priv; + struct sockaddr *address = (struct sockaddr *) extra; + struct iw_quality *qual; + BSSListRid BSSList; + int i; + int loseSync = capable(CAP_NET_ADMIN) ? 1: -1; + + qual = kmalloc(IW_MAX_AP * sizeof(*qual), GFP_KERNEL); + if (!qual) + return -ENOMEM; + + for (i = 0; i < IW_MAX_AP; i++) { + u16 dBm; + if (readBSSListRid(local, loseSync, &BSSList)) + break; + loseSync = 0; + memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN); + address[i].sa_family = ARPHRD_ETHER; + dBm = le16_to_cpu(BSSList.dBm); + if (local->rssi) { + qual[i].level = 0x100 - dBm; + qual[i].qual = airo_dbm_to_pct(local->rssi, dBm); + qual[i].updated = IW_QUAL_QUAL_UPDATED + | IW_QUAL_LEVEL_UPDATED + | IW_QUAL_DBM; + } else { + qual[i].level = (dBm + 321) / 2; + qual[i].qual = 0; + qual[i].updated = IW_QUAL_QUAL_INVALID + | IW_QUAL_LEVEL_UPDATED + | IW_QUAL_DBM; + } + qual[i].noise = local->wstats.qual.noise; + if (BSSList.index == cpu_to_le16(0xffff)) + break; + } + if (!i) { + StatusRid status_rid; /* Card status info */ + readStatusRid(local, &status_rid, 1); + for (i = 0; + i < min(IW_MAX_AP, 4) && + (status_rid.bssid[i][0] + & status_rid.bssid[i][1] + & status_rid.bssid[i][2] + & status_rid.bssid[i][3] + & status_rid.bssid[i][4] + & status_rid.bssid[i][5])!=0xff && + (status_rid.bssid[i][0] + | status_rid.bssid[i][1] + | status_rid.bssid[i][2] + | status_rid.bssid[i][3] + | status_rid.bssid[i][4] + | status_rid.bssid[i][5]); + i++) { + memcpy(address[i].sa_data, + status_rid.bssid[i], ETH_ALEN); + address[i].sa_family = ARPHRD_ETHER; + } + } else { + dwrq->flags = 1; /* Should be define'd */ + memcpy(extra + sizeof(struct sockaddr) * i, qual, + sizeof(struct iw_quality) * i); + } + dwrq->length = i; + + kfree(qual); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : Initiate Scan + */ +static int airo_set_scan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *ai = dev->ml_priv; + Cmd cmd; + Resp rsp; + int wake = 0; + APListRid APList_rid_empty; + + /* Note : you may have realised that, as this is a SET operation, + * this is privileged and therefore a normal user can't + * perform scanning. + * This is not an error, while the device perform scanning, + * traffic doesn't flow, so it's a perfect DoS... + * Jean II */ + if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN; + + if (down_interruptible(&ai->sem)) + return -ERESTARTSYS; + + /* If there's already a scan in progress, don't + * trigger another one. */ + if (ai->scan_timeout > 0) + goto out; + + /* Clear APList as it affects scan results */ + memset(&APList_rid_empty, 0, sizeof(APList_rid_empty)); + APList_rid_empty.len = cpu_to_le16(sizeof(APList_rid_empty)); + disable_MAC(ai, 2); + writeAPListRid(ai, &APList_rid_empty, 0); + enable_MAC(ai, 0); + + /* Initiate a scan command */ + ai->scan_timeout = RUN_AT(3*HZ); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd=CMD_LISTBSS; + issuecommand(ai, &cmd, &rsp); + wake = 1; + +out: + up(&ai->sem); + if (wake) + wake_up_interruptible(&ai->thr_wait); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Translate scan data returned from the card to a card independent + * format that the Wireless Tools will understand - Jean II + */ +static inline char *airo_translate_scan(struct net_device *dev, + struct iw_request_info *info, + char *current_ev, + char *end_buf, + BSSListRid *bss) +{ + struct airo_info *ai = dev->ml_priv; + struct iw_event iwe; /* Temporary buffer */ + __le16 capabilities; + char * current_val; /* For rates */ + int i; + char * buf; + u16 dBm; + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); + + /* Other entries will be displayed in the order we give them */ + + /* Add the ESSID */ + iwe.u.data.length = bss->ssidLen; + if(iwe.u.data.length > 32) + iwe.u.data.length = 32; + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ssid); + + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + capabilities = bss->cap; + if(capabilities & (CAP_ESS | CAP_IBSS)) { + if(capabilities & CAP_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); + } + + /* Add frequency */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = le16_to_cpu(bss->dsChannel); + iwe.u.freq.m = 100000 * + ieee80211_channel_to_frequency(iwe.u.freq.m, IEEE80211_BAND_2GHZ); + iwe.u.freq.e = 1; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); + + dBm = le16_to_cpu(bss->dBm); + + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + if (ai->rssi) { + iwe.u.qual.level = 0x100 - dBm; + iwe.u.qual.qual = airo_dbm_to_pct(ai->rssi, dBm); + iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED + | IW_QUAL_LEVEL_UPDATED + | IW_QUAL_DBM; + } else { + iwe.u.qual.level = (dBm + 321) / 2; + iwe.u.qual.qual = 0; + iwe.u.qual.updated = IW_QUAL_QUAL_INVALID + | IW_QUAL_LEVEL_UPDATED + | IW_QUAL_DBM; + } + iwe.u.qual.noise = ai->wstats.qual.noise; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if(capabilities & CAP_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ssid); + + /* Rate : stuffing multiple values in a single event require a bit + * more of magic - Jean II */ + current_val = current_ev + iwe_stream_lcp_len(info); + + iwe.cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + /* Max 8 values */ + for(i = 0 ; i < 8 ; i++) { + /* NULL terminated */ + if(bss->rates[i] == 0) + break; + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000); + /* Add new value to event */ + current_val = iwe_stream_add_value(info, current_ev, + current_val, end_buf, + &iwe, IW_EV_PARAM_LEN); + } + /* Check if we added any event */ + if ((current_val - current_ev) > iwe_stream_lcp_len(info)) + current_ev = current_val; + + /* Beacon interval */ + buf = kmalloc(30, GFP_KERNEL); + if (buf) { + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "bcn_int=%d", bss->beaconInterval); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); + kfree(buf); + } + + /* Put WPA/RSN Information Elements into the event stream */ + if (test_bit(FLAG_WPA_CAPABLE, &ai->flags)) { + unsigned int num_null_ies = 0; + u16 length = sizeof (bss->extra.iep); + u8 *ie = (void *)&bss->extra.iep; + + while ((length >= 2) && (num_null_ies < 2)) { + if (2 + ie[1] > length) { + /* Invalid element, don't continue parsing IE */ + break; + } + + switch (ie[0]) { + case WLAN_EID_SSID: + /* Two zero-length SSID elements + * mean we're done parsing elements */ + if (!ie[1]) + num_null_ies++; + break; + + case WLAN_EID_VENDOR_SPECIFIC: + if (ie[1] >= 4 && + ie[2] == 0x00 && + ie[3] == 0x50 && + ie[4] == 0xf2 && + ie[5] == 0x01) { + iwe.cmd = IWEVGENIE; + /* 64 is an arbitrary cut-off */ + iwe.u.data.length = min(ie[1] + 2, + 64); + current_ev = iwe_stream_add_point( + info, current_ev, + end_buf, &iwe, ie); + } + break; + + case WLAN_EID_RSN: + iwe.cmd = IWEVGENIE; + /* 64 is an arbitrary cut-off */ + iwe.u.data.length = min(ie[1] + 2, 64); + current_ev = iwe_stream_add_point( + info, current_ev, end_buf, + &iwe, ie); + break; + + default: + break; + } + + length -= 2 + ie[1]; + ie += 2 + ie[1]; + } + } + return current_ev; +} + +/*------------------------------------------------------------------*/ +/* + * Wireless Handler : Read Scan Results + */ +static int airo_get_scan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct airo_info *ai = dev->ml_priv; + BSSListElement *net; + int err = 0; + char *current_ev = extra; + + /* If a scan is in-progress, return -EAGAIN */ + if (ai->scan_timeout > 0) + return -EAGAIN; + + if (down_interruptible(&ai->sem)) + return -EAGAIN; + + list_for_each_entry (net, &ai->network_list, list) { + /* Translate to WE format this entry */ + current_ev = airo_translate_scan(dev, info, current_ev, + extra + dwrq->length, + &net->bss); + + /* Check if there is space for one more entry */ + if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) { + /* Ask user space to try again with a bigger buffer */ + err = -E2BIG; + goto out; + } + } + + /* Length of data */ + dwrq->length = (current_ev - extra); + dwrq->flags = 0; /* todo */ + +out: + up(&ai->sem); + return err; +} + +/*------------------------------------------------------------------*/ +/* + * Commit handler : called after a bunch of SET operations + */ +static int airo_config_commit(struct net_device *dev, + struct iw_request_info *info, /* NULL */ + void *zwrq, /* NULL */ + char *extra) /* NULL */ +{ + struct airo_info *local = dev->ml_priv; + + if (!test_bit (FLAG_COMMIT, &local->flags)) + return 0; + + /* Some of the "SET" function may have modified some of the + * parameters. It's now time to commit them in the card */ + disable_MAC(local, 1); + if (test_bit (FLAG_RESET, &local->flags)) { + SsidRid SSID_rid; + + readSsidRid(local, &SSID_rid); + if (test_bit(FLAG_MPI,&local->flags)) + setup_card(local, dev->dev_addr, 1 ); + else + reset_airo_card(dev); + disable_MAC(local, 1); + writeSsidRid(local, &SSID_rid, 1); + writeAPListRid(local, &local->APList, 1); + } + if (down_interruptible(&local->sem)) + return -ERESTARTSYS; + writeConfigRid(local, 0); + enable_MAC(local, 0); + if (test_bit (FLAG_RESET, &local->flags)) + airo_set_promisc(local); + else + up(&local->sem); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Structures to export the Wireless Handlers + */ + +static const struct iw_priv_args airo_private_args[] = { +/*{ cmd, set_args, get_args, name } */ + { AIROIOCTL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), + IW_PRIV_TYPE_BYTE | 2047, "airoioctl" }, + { AIROIDIFC, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (aironet_ioctl), + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "airoidifc" }, +}; + +static const iw_handler airo_handler[] = +{ + (iw_handler) airo_config_commit, /* SIOCSIWCOMMIT */ + (iw_handler) airo_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) airo_set_freq, /* SIOCSIWFREQ */ + (iw_handler) airo_get_freq, /* SIOCGIWFREQ */ + (iw_handler) airo_set_mode, /* SIOCSIWMODE */ + (iw_handler) airo_get_mode, /* SIOCGIWMODE */ + (iw_handler) airo_set_sens, /* SIOCSIWSENS */ + (iw_handler) airo_get_sens, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) airo_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ + (iw_handler) airo_set_wap, /* SIOCSIWAP */ + (iw_handler) airo_get_wap, /* SIOCGIWAP */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) airo_get_aplist, /* SIOCGIWAPLIST */ + (iw_handler) airo_set_scan, /* SIOCSIWSCAN */ + (iw_handler) airo_get_scan, /* SIOCGIWSCAN */ + (iw_handler) airo_set_essid, /* SIOCSIWESSID */ + (iw_handler) airo_get_essid, /* SIOCGIWESSID */ + (iw_handler) airo_set_nick, /* SIOCSIWNICKN */ + (iw_handler) airo_get_nick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) airo_set_rate, /* SIOCSIWRATE */ + (iw_handler) airo_get_rate, /* SIOCGIWRATE */ + (iw_handler) airo_set_rts, /* SIOCSIWRTS */ + (iw_handler) airo_get_rts, /* SIOCGIWRTS */ + (iw_handler) airo_set_frag, /* SIOCSIWFRAG */ + (iw_handler) airo_get_frag, /* SIOCGIWFRAG */ + (iw_handler) airo_set_txpow, /* SIOCSIWTXPOW */ + (iw_handler) airo_get_txpow, /* SIOCGIWTXPOW */ + (iw_handler) airo_set_retry, /* SIOCSIWRETRY */ + (iw_handler) airo_get_retry, /* SIOCGIWRETRY */ + (iw_handler) airo_set_encode, /* SIOCSIWENCODE */ + (iw_handler) airo_get_encode, /* SIOCGIWENCODE */ + (iw_handler) airo_set_power, /* SIOCSIWPOWER */ + (iw_handler) airo_get_power, /* SIOCGIWPOWER */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* SIOCSIWGENIE */ + (iw_handler) NULL, /* SIOCGIWGENIE */ + (iw_handler) airo_set_auth, /* SIOCSIWAUTH */ + (iw_handler) airo_get_auth, /* SIOCGIWAUTH */ + (iw_handler) airo_set_encodeext, /* SIOCSIWENCODEEXT */ + (iw_handler) airo_get_encodeext, /* SIOCGIWENCODEEXT */ + (iw_handler) NULL, /* SIOCSIWPMKSA */ +}; + +/* Note : don't describe AIROIDIFC and AIROOLDIDIFC in here. + * We want to force the use of the ioctl code, because those can't be + * won't work the iw_handler code (because they simultaneously read + * and write data and iw_handler can't do that). + * Note that it's perfectly legal to read/write on a single ioctl command, + * you just can't use iwpriv and need to force it via the ioctl handler. + * Jean II */ +static const iw_handler airo_private_handler[] = +{ + NULL, /* SIOCIWFIRSTPRIV */ +}; + +static const struct iw_handler_def airo_handler_def = +{ + .num_standard = ARRAY_SIZE(airo_handler), + .num_private = ARRAY_SIZE(airo_private_handler), + .num_private_args = ARRAY_SIZE(airo_private_args), + .standard = airo_handler, + .private = airo_private_handler, + .private_args = airo_private_args, + .get_wireless_stats = airo_get_wireless_stats, +}; + +/* + * This defines the configuration part of the Wireless Extensions + * Note : irq and spinlock protection will occur in the subroutines + * + * TODO : + * o Check input value more carefully and fill correct values in range + * o Test and shakeout the bugs (if any) + * + * Jean II + * + * Javier Achirica did a great job of merging code from the unnamed CISCO + * developer that added support for flashing the card. + */ +static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + int rc = 0; + struct airo_info *ai = dev->ml_priv; + + if (ai->power.event) + return 0; + + switch (cmd) { +#ifdef CISCO_EXT + case AIROIDIFC: +#ifdef AIROOLDIDIFC + case AIROOLDIDIFC: +#endif + { + int val = AIROMAGIC; + aironet_ioctl com; + if (copy_from_user(&com,rq->ifr_data,sizeof(com))) + rc = -EFAULT; + else if (copy_to_user(com.data,(char *)&val,sizeof(val))) + rc = -EFAULT; + } + break; + + case AIROIOCTL: +#ifdef AIROOLDIOCTL + case AIROOLDIOCTL: +#endif + /* Get the command struct and hand it off for evaluation by + * the proper subfunction + */ + { + aironet_ioctl com; + if (copy_from_user(&com,rq->ifr_data,sizeof(com))) { + rc = -EFAULT; + break; + } + + /* Separate R/W functions bracket legality here + */ + if ( com.command == AIRORSWVERSION ) { + if (copy_to_user(com.data, swversion, sizeof(swversion))) + rc = -EFAULT; + else + rc = 0; + } + else if ( com.command <= AIRORRID) + rc = readrids(dev,&com); + else if ( com.command >= AIROPCAP && com.command <= (AIROPLEAPUSR+2) ) + rc = writerids(dev,&com); + else if ( com.command >= AIROFLSHRST && com.command <= AIRORESTART ) + rc = flashcard(dev,&com); + else + rc = -EINVAL; /* Bad command in ioctl */ + } + break; +#endif /* CISCO_EXT */ + + // All other calls are currently unsupported + default: + rc = -EOPNOTSUPP; + } + return rc; +} + +/* + * Get the Wireless stats out of the driver + * Note : irq and spinlock protection will occur in the subroutines + * + * TODO : + * o Check if work in Ad-Hoc mode (otherwise, use SPY, as in wvlan_cs) + * + * Jean + */ +static void airo_read_wireless_stats(struct airo_info *local) +{ + StatusRid status_rid; + StatsRid stats_rid; + CapabilityRid cap_rid; + __le32 *vals = stats_rid.vals; + + /* Get stats out of the card */ + clear_bit(JOB_WSTATS, &local->jobs); + if (local->power.event) { + up(&local->sem); + return; + } + readCapabilityRid(local, &cap_rid, 0); + readStatusRid(local, &status_rid, 0); + readStatsRid(local, &stats_rid, RID_STATS, 0); + up(&local->sem); + + /* The status */ + local->wstats.status = le16_to_cpu(status_rid.mode); + + /* Signal quality and co */ + if (local->rssi) { + local->wstats.qual.level = + airo_rssi_to_dbm(local->rssi, + le16_to_cpu(status_rid.sigQuality)); + /* normalizedSignalStrength appears to be a percentage */ + local->wstats.qual.qual = + le16_to_cpu(status_rid.normalizedSignalStrength); + } else { + local->wstats.qual.level = + (le16_to_cpu(status_rid.normalizedSignalStrength) + 321) / 2; + local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid); + } + if (le16_to_cpu(status_rid.len) >= 124) { + local->wstats.qual.noise = 0x100 - status_rid.noisedBm; + local->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + } else { + local->wstats.qual.noise = 0; + local->wstats.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID | IW_QUAL_DBM; + } + + /* Packets discarded in the wireless adapter due to wireless + * specific problems */ + local->wstats.discard.nwid = le32_to_cpu(vals[56]) + + le32_to_cpu(vals[57]) + + le32_to_cpu(vals[58]); /* SSID Mismatch */ + local->wstats.discard.code = le32_to_cpu(vals[6]);/* RxWepErr */ + local->wstats.discard.fragment = le32_to_cpu(vals[30]); + local->wstats.discard.retries = le32_to_cpu(vals[10]); + local->wstats.discard.misc = le32_to_cpu(vals[1]) + + le32_to_cpu(vals[32]); + local->wstats.miss.beacon = le32_to_cpu(vals[34]); +} + +static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev) +{ + struct airo_info *local = dev->ml_priv; + + if (!test_bit(JOB_WSTATS, &local->jobs)) { + /* Get stats out of the card if available */ + if (down_trylock(&local->sem) != 0) { + set_bit(JOB_WSTATS, &local->jobs); + wake_up_interruptible(&local->thr_wait); + } else + airo_read_wireless_stats(local); + } + + return &local->wstats; +} + +#ifdef CISCO_EXT +/* + * This just translates from driver IOCTL codes to the command codes to + * feed to the radio's host interface. Things can be added/deleted + * as needed. This represents the READ side of control I/O to + * the card + */ +static int readrids(struct net_device *dev, aironet_ioctl *comp) { + unsigned short ridcode; + unsigned char *iobuf; + int len; + struct airo_info *ai = dev->ml_priv; + + if (test_bit(FLAG_FLASHING, &ai->flags)) + return -EIO; + + switch(comp->command) + { + case AIROGCAP: ridcode = RID_CAPABILITIES; break; + case AIROGCFG: ridcode = RID_CONFIG; + if (test_bit(FLAG_COMMIT, &ai->flags)) { + disable_MAC (ai, 1); + writeConfigRid (ai, 1); + enable_MAC(ai, 1); + } + break; + case AIROGSLIST: ridcode = RID_SSID; break; + case AIROGVLIST: ridcode = RID_APLIST; break; + case AIROGDRVNAM: ridcode = RID_DRVNAME; break; + case AIROGEHTENC: ridcode = RID_ETHERENCAP; break; + case AIROGWEPKTMP: ridcode = RID_WEP_TEMP; + /* Only super-user can read WEP keys */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + break; + case AIROGWEPKNV: ridcode = RID_WEP_PERM; + /* Only super-user can read WEP keys */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + break; + case AIROGSTAT: ridcode = RID_STATUS; break; + case AIROGSTATSD32: ridcode = RID_STATSDELTA; break; + case AIROGSTATSC32: ridcode = RID_STATS; break; + case AIROGMICSTATS: + if (copy_to_user(comp->data, &ai->micstats, + min((int)comp->len,(int)sizeof(ai->micstats)))) + return -EFAULT; + return 0; + case AIRORRID: ridcode = comp->ridnum; break; + default: + return -EINVAL; + } + + if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL) + return -ENOMEM; + + PC4500_readrid(ai,ridcode,iobuf,RIDSIZE, 1); + /* get the count of bytes in the rid docs say 1st 2 bytes is it. + * then return it to the user + * 9/22/2000 Honor user given length + */ + len = comp->len; + + if (copy_to_user(comp->data, iobuf, min(len, (int)RIDSIZE))) { + kfree (iobuf); + return -EFAULT; + } + kfree (iobuf); + return 0; +} + +/* + * Danger Will Robinson write the rids here + */ + +static int writerids(struct net_device *dev, aironet_ioctl *comp) { + struct airo_info *ai = dev->ml_priv; + int ridcode; + int enabled; + static int (* writer)(struct airo_info *, u16 rid, const void *, int, int); + unsigned char *iobuf; + + /* Only super-user can write RIDs */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (test_bit(FLAG_FLASHING, &ai->flags)) + return -EIO; + + ridcode = 0; + writer = do_writerid; + + switch(comp->command) + { + case AIROPSIDS: ridcode = RID_SSID; break; + case AIROPCAP: ridcode = RID_CAPABILITIES; break; + case AIROPAPLIST: ridcode = RID_APLIST; break; + case AIROPCFG: ai->config.len = 0; + clear_bit(FLAG_COMMIT, &ai->flags); + ridcode = RID_CONFIG; break; + case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break; + case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break; + case AIROPLEAPPWD: ridcode = RID_LEAPPASSWORD; break; + case AIROPWEPKEY: ridcode = RID_WEP_TEMP; writer = PC4500_writerid; + break; + case AIROPLEAPUSR+1: ridcode = 0xFF2A; break; + case AIROPLEAPUSR+2: ridcode = 0xFF2B; break; + + /* this is not really a rid but a command given to the card + * same with MAC off + */ + case AIROPMACON: + if (enable_MAC(ai, 1) != 0) + return -EIO; + return 0; + + /* + * Evidently this code in the airo driver does not get a symbol + * as disable_MAC. it's probably so short the compiler does not gen one. + */ + case AIROPMACOFF: + disable_MAC(ai, 1); + return 0; + + /* This command merely clears the counts does not actually store any data + * only reads rid. But as it changes the cards state, I put it in the + * writerid routines. + */ + case AIROPSTCLR: + if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL) + return -ENOMEM; + + PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDSIZE, 1); + + enabled = ai->micstats.enabled; + memset(&ai->micstats,0,sizeof(ai->micstats)); + ai->micstats.enabled = enabled; + + if (copy_to_user(comp->data, iobuf, + min((int)comp->len, (int)RIDSIZE))) { + kfree (iobuf); + return -EFAULT; + } + kfree (iobuf); + return 0; + + default: + return -EOPNOTSUPP; /* Blarg! */ + } + if(comp->len > RIDSIZE) + return -EINVAL; + + if ((iobuf = kmalloc(RIDSIZE, GFP_KERNEL)) == NULL) + return -ENOMEM; + + if (copy_from_user(iobuf,comp->data,comp->len)) { + kfree (iobuf); + return -EFAULT; + } + + if (comp->command == AIROPCFG) { + ConfigRid *cfg = (ConfigRid *)iobuf; + + if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) + cfg->opmode |= MODE_MIC; + + if ((cfg->opmode & MODE_CFG_MASK) == MODE_STA_IBSS) + set_bit (FLAG_ADHOC, &ai->flags); + else + clear_bit (FLAG_ADHOC, &ai->flags); + } + + if((*writer)(ai, ridcode, iobuf,comp->len,1)) { + kfree (iobuf); + return -EIO; + } + kfree (iobuf); + return 0; +} + +/***************************************************************************** + * Ancillary flash / mod functions much black magic lurkes here * + ***************************************************************************** + */ + +/* + * Flash command switch table + */ + +static int flashcard(struct net_device *dev, aironet_ioctl *comp) { + int z; + + /* Only super-user can modify flash */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + switch(comp->command) + { + case AIROFLSHRST: + return cmdreset((struct airo_info *)dev->ml_priv); + + case AIROFLSHSTFL: + if (!AIRO_FLASH(dev) && + (AIRO_FLASH(dev) = kmalloc(FLASHSIZE, GFP_KERNEL)) == NULL) + return -ENOMEM; + return setflashmode((struct airo_info *)dev->ml_priv); + + case AIROFLSHGCHR: /* Get char from aux */ + if(comp->len != sizeof(int)) + return -EINVAL; + if (copy_from_user(&z,comp->data,comp->len)) + return -EFAULT; + return flashgchar((struct airo_info *)dev->ml_priv, z, 8000); + + case AIROFLSHPCHR: /* Send char to card. */ + if(comp->len != sizeof(int)) + return -EINVAL; + if (copy_from_user(&z,comp->data,comp->len)) + return -EFAULT; + return flashpchar((struct airo_info *)dev->ml_priv, z, 8000); + + case AIROFLPUTBUF: /* Send 32k to card */ + if (!AIRO_FLASH(dev)) + return -ENOMEM; + if(comp->len > FLASHSIZE) + return -EINVAL; + if (copy_from_user(AIRO_FLASH(dev), comp->data, comp->len)) + return -EFAULT; + + flashputbuf((struct airo_info *)dev->ml_priv); + return 0; + + case AIRORESTART: + if (flashrestart((struct airo_info *)dev->ml_priv, dev)) + return -EIO; + return 0; + } + return -EINVAL; +} + +#define FLASH_COMMAND 0x7e7e + +/* + * STEP 1) + * Disable MAC and do soft reset on + * card. + */ + +static int cmdreset(struct airo_info *ai) { + disable_MAC(ai, 1); + + if(!waitbusy (ai)){ + airo_print_info(ai->dev->name, "Waitbusy hang before RESET"); + return -EBUSY; + } + + OUT4500(ai,COMMAND,CMD_SOFTRESET); + + ssleep(1); /* WAS 600 12/7/00 */ + + if(!waitbusy (ai)){ + airo_print_info(ai->dev->name, "Waitbusy hang AFTER RESET"); + return -EBUSY; + } + return 0; +} + +/* STEP 2) + * Put the card in legendary flash + * mode + */ + +static int setflashmode (struct airo_info *ai) { + set_bit (FLAG_FLASHING, &ai->flags); + + OUT4500(ai, SWS0, FLASH_COMMAND); + OUT4500(ai, SWS1, FLASH_COMMAND); + if (probe) { + OUT4500(ai, SWS0, FLASH_COMMAND); + OUT4500(ai, COMMAND,0x10); + } else { + OUT4500(ai, SWS2, FLASH_COMMAND); + OUT4500(ai, SWS3, FLASH_COMMAND); + OUT4500(ai, COMMAND,0); + } + msleep(500); /* 500ms delay */ + + if(!waitbusy(ai)) { + clear_bit (FLAG_FLASHING, &ai->flags); + airo_print_info(ai->dev->name, "Waitbusy hang after setflash mode"); + return -EIO; + } + return 0; +} + +/* Put character to SWS0 wait for dwelltime + * x 50us for echo . + */ + +static int flashpchar(struct airo_info *ai,int byte,int dwelltime) { + int echo; + int waittime; + + byte |= 0x8000; + + if(dwelltime == 0 ) + dwelltime = 200; + + waittime=dwelltime; + + /* Wait for busy bit d15 to go false indicating buffer empty */ + while ((IN4500 (ai, SWS0) & 0x8000) && waittime > 0) { + udelay (50); + waittime -= 50; + } + + /* timeout for busy clear wait */ + if(waittime <= 0 ){ + airo_print_info(ai->dev->name, "flash putchar busywait timeout!"); + return -EBUSY; + } + + /* Port is clear now write byte and wait for it to echo back */ + do { + OUT4500(ai,SWS0,byte); + udelay(50); + dwelltime -= 50; + echo = IN4500(ai,SWS1); + } while (dwelltime >= 0 && echo != byte); + + OUT4500(ai,SWS1,0); + + return (echo == byte) ? 0 : -EIO; +} + +/* + * Get a character from the card matching matchbyte + * Step 3) + */ +static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){ + int rchar; + unsigned char rbyte=0; + + do { + rchar = IN4500(ai,SWS1); + + if(dwelltime && !(0x8000 & rchar)){ + dwelltime -= 10; + mdelay(10); + continue; + } + rbyte = 0xff & rchar; + + if( (rbyte == matchbyte) && (0x8000 & rchar) ){ + OUT4500(ai,SWS1,0); + return 0; + } + if( rbyte == 0x81 || rbyte == 0x82 || rbyte == 0x83 || rbyte == 0x1a || 0xffff == rchar) + break; + OUT4500(ai,SWS1,0); + + }while(dwelltime > 0); + return -EIO; +} + +/* + * Transfer 32k of firmware data from user buffer to our buffer and + * send to the card + */ + +static int flashputbuf(struct airo_info *ai){ + int nwords; + + /* Write stuff */ + if (test_bit(FLAG_MPI,&ai->flags)) + memcpy_toio(ai->pciaux + 0x8000, ai->flash, FLASHSIZE); + else { + OUT4500(ai,AUXPAGE,0x100); + OUT4500(ai,AUXOFF,0); + + for(nwords=0;nwords != FLASHSIZE / 2;nwords++){ + OUT4500(ai,AUXDATA,ai->flash[nwords] & 0xffff); + } + } + OUT4500(ai,SWS0,0x8000); + + return 0; +} + +/* + * + */ +static int flashrestart(struct airo_info *ai,struct net_device *dev){ + int i,status; + + ssleep(1); /* Added 12/7/00 */ + clear_bit (FLAG_FLASHING, &ai->flags); + if (test_bit(FLAG_MPI, &ai->flags)) { + status = mpi_init_descriptors(ai); + if (status != SUCCESS) + return status; + } + status = setup_card(ai, dev->dev_addr, 1); + + if (!test_bit(FLAG_MPI,&ai->flags)) + for( i = 0; i < MAX_FIDS; i++ ) { + ai->fids[i] = transmit_allocate + ( ai, AIRO_DEF_MTU, i >= MAX_FIDS / 2 ); + } + + ssleep(1); /* Added 12/7/00 */ + return status; +} +#endif /* CISCO_EXT */ + +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + In addition: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +module_init(airo_init_module); +module_exit(airo_cleanup_module); diff --git a/drivers/net/wireless/cisco/airo.h b/drivers/net/wireless/cisco/airo.h new file mode 100644 index 000000000000..e480adf86be6 --- /dev/null +++ b/drivers/net/wireless/cisco/airo.h @@ -0,0 +1,9 @@ +#ifndef _AIRO_H_ +#define _AIRO_H_ + +struct net_device *init_airo_card(unsigned short irq, int port, int is_pcmcia, + struct device *dmdev); +int reset_airo_card(struct net_device *dev); +void stop_airo_card(struct net_device *dev, int freeres); + +#endif /* _AIRO_H_ */ diff --git a/drivers/net/wireless/cisco/airo_cs.c b/drivers/net/wireless/cisco/airo_cs.c new file mode 100644 index 000000000000..d9ed22b4cc6b --- /dev/null +++ b/drivers/net/wireless/cisco/airo_cs.c @@ -0,0 +1,222 @@ +/*====================================================================== + + Aironet driver for 4500 and 4800 series cards + + This code is released under both the GPL version 2 and BSD licenses. + Either license may be used. The respective licenses are found at + the end of this file. + + This code was developed by Benjamin Reed + including portions of which come from the Aironet PC4500 + Developer's Reference Manual and used with permission. Copyright + (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use + code in the Developer's manual was granted for this driver by + Aironet. + + In addition this module was derived from dummy_cs. + The initial developer of dummy_cs is David A. Hinds + . Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + +======================================================================*/ + +#ifdef __IN_PCMCIA_PACKAGE__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "airo.h" + + +/*====================================================================*/ + +MODULE_AUTHOR("Benjamin Reed"); +MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet " + "cards. This is the module that links the PCMCIA card " + "with the airo module."); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340 PCMCIA cards"); + +/*====================================================================*/ + +static int airo_config(struct pcmcia_device *link); +static void airo_release(struct pcmcia_device *link); + +static void airo_detach(struct pcmcia_device *p_dev); + +struct local_info { + struct net_device *eth_dev; +}; + +static int airo_probe(struct pcmcia_device *p_dev) +{ + struct local_info *local; + + dev_dbg(&p_dev->dev, "airo_attach()\n"); + + /* Allocate space for private device-specific data */ + local = kzalloc(sizeof(*local), GFP_KERNEL); + if (!local) + return -ENOMEM; + + p_dev->priv = local; + + return airo_config(p_dev); +} /* airo_attach */ + +static void airo_detach(struct pcmcia_device *link) +{ + dev_dbg(&link->dev, "airo_detach\n"); + + airo_release(link); + + if (((struct local_info *)link->priv)->eth_dev) { + stop_airo_card(((struct local_info *)link->priv)->eth_dev, + 0); + } + ((struct local_info *)link->priv)->eth_dev = NULL; + + kfree(link->priv); +} /* airo_detach */ + +static int airo_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + if (p_dev->config_index == 0) + return -EINVAL; + + return pcmcia_request_io(p_dev); +} + + +static int airo_config(struct pcmcia_device *link) +{ + struct local_info *dev; + int ret; + + dev = link->priv; + + dev_dbg(&link->dev, "airo_config\n"); + + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | + CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; + + ret = pcmcia_loop_config(link, airo_cs_config_check, NULL); + if (ret) + goto failed; + + if (!link->irq) + goto failed; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + ((struct local_info *)link->priv)->eth_dev = + init_airo_card(link->irq, + link->resource[0]->start, 1, &link->dev); + if (!((struct local_info *)link->priv)->eth_dev) + goto failed; + + return 0; + + failed: + airo_release(link); + return -ENODEV; +} /* airo_config */ + +static void airo_release(struct pcmcia_device *link) +{ + dev_dbg(&link->dev, "airo_release\n"); + pcmcia_disable_device(link); +} + +static int airo_suspend(struct pcmcia_device *link) +{ + struct local_info *local = link->priv; + + netif_device_detach(local->eth_dev); + + return 0; +} + +static int airo_resume(struct pcmcia_device *link) +{ + struct local_info *local = link->priv; + + if (link->open) { + reset_airo_card(local->eth_dev); + netif_device_attach(local->eth_dev); + } + + return 0; +} + +static const struct pcmcia_device_id airo_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x015f, 0x000a), + PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0005), + PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0007), + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0007), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, airo_ids); + +static struct pcmcia_driver airo_driver = { + .owner = THIS_MODULE, + .name = "airo_cs", + .probe = airo_probe, + .remove = airo_detach, + .id_table = airo_ids, + .suspend = airo_suspend, + .resume = airo_resume, +}; +module_pcmcia_driver(airo_driver); + +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + In addition: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ -- cgit v1.2.3-59-g8ed1b From 30fe0f9b8c755d9aab04bb7c98ce9c7835c3bd24 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 19:49:20 +0200 Subject: atmel: move under atmel vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 44 +- drivers/net/wireless/Makefile | 7 +- drivers/net/wireless/at76c50x-usb.c | 2617 ----------------- drivers/net/wireless/at76c50x-usb.h | 466 --- drivers/net/wireless/atmel.c | 4549 ----------------------------- drivers/net/wireless/atmel.h | 43 - drivers/net/wireless/atmel/Kconfig | 57 + drivers/net/wireless/atmel/Makefile | 5 + drivers/net/wireless/atmel/at76c50x-usb.c | 2617 +++++++++++++++++ drivers/net/wireless/atmel/at76c50x-usb.h | 466 +++ drivers/net/wireless/atmel/atmel.c | 4549 +++++++++++++++++++++++++++++ drivers/net/wireless/atmel/atmel.h | 43 + drivers/net/wireless/atmel/atmel_cs.c | 286 ++ drivers/net/wireless/atmel/atmel_pci.c | 76 + drivers/net/wireless/atmel_cs.c | 286 -- drivers/net/wireless/atmel_pci.c | 76 - 17 files changed, 8102 insertions(+), 8087 deletions(-) delete mode 100644 drivers/net/wireless/at76c50x-usb.c delete mode 100644 drivers/net/wireless/at76c50x-usb.h delete mode 100644 drivers/net/wireless/atmel.c delete mode 100644 drivers/net/wireless/atmel.h create mode 100644 drivers/net/wireless/atmel/Kconfig create mode 100644 drivers/net/wireless/atmel/Makefile create mode 100644 drivers/net/wireless/atmel/at76c50x-usb.c create mode 100644 drivers/net/wireless/atmel/at76c50x-usb.h create mode 100644 drivers/net/wireless/atmel/atmel.c create mode 100644 drivers/net/wireless/atmel/atmel.h create mode 100644 drivers/net/wireless/atmel/atmel_cs.c create mode 100644 drivers/net/wireless/atmel/atmel_pci.c delete mode 100644 drivers/net/wireless/atmel_cs.c delete mode 100644 drivers/net/wireless/atmel_pci.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index f1adb66094f1..2f3ea01c101c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1992,7 +1992,7 @@ L: linux-wireless@vger.kernel.org W: http://www.thekelleys.org.uk/atmel W: http://atmelwlandriver.sourceforge.net/ S: Maintained -F: drivers/net/wireless/atmel* +F: drivers/net/wireless/atmel/atmel* ATMEL MAXTOUCH DRIVER M: Nick Dyer diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index ce5211ba1b29..275b6ed93f15 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -18,6 +18,7 @@ menuconfig WLAN if WLAN source "drivers/net/wireless/admtek/Kconfig" +source "drivers/net/wireless/atmel/Kconfig" source "drivers/net/wireless/cisco/Kconfig" config PCMCIA_RAYCS @@ -54,49 +55,6 @@ config LIBERTAS_THINFIRM_USB ---help--- A driver for Marvell Libertas 8388 USB devices using thinfirm. -config ATMEL - tristate "Atmel at76c50x chipset 802.11b support" - depends on CFG80211 && (PCI || PCMCIA) - select WIRELESS_EXT - select WEXT_PRIV - select FW_LOADER - select CRC32 - ---help--- - A driver 802.11b wireless cards based on the Atmel fast-vnet - chips. This driver supports standard Linux wireless extensions. - - Many cards based on this chipset do not have flash memory - and need their firmware loaded at start-up. If yours is - one of these, you will need to provide a firmware image - to be loaded into the card by the driver. The Atmel - firmware package can be downloaded from - - -config PCI_ATMEL - tristate "Atmel at76c506 PCI cards" - depends on ATMEL && PCI - ---help--- - Enable support for PCI and mini-PCI cards containing the - Atmel at76c506 chip. - -config PCMCIA_ATMEL - tristate "Atmel at76c502/at76c504 PCMCIA cards" - depends on ATMEL && PCMCIA - select WIRELESS_EXT - select FW_LOADER - select CRC32 - ---help--- - Enable support for PCMCIA cards containing the - Atmel at76c502 and at76c504 chips. - -config AT76C50X_USB - tristate "Atmel at76c503/at76c505/at76c505a USB cards" - depends on MAC80211 && USB - select FW_LOADER - ---help--- - Enable support for USB Wireless devices using Atmel at76c503, - at76c505 or at76c505a chips. - config PCMCIA_WL3501 tristate "Planet WL3501 PCMCIA cards" depends on CFG80211 && PCMCIA diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 853294d84fa9..87eb6b2f3227 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/ +obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ obj-$(CONFIG_IPW2100) += ipw2x00/ @@ -10,12 +11,6 @@ obj-$(CONFIG_IPW2200) += ipw2x00/ obj-$(CONFIG_HERMES) += orinoco/ -obj-$(CONFIG_ATMEL) += atmel.o -obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o -obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o - -obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o - obj-$(CONFIG_PRISM54) += prism54/ obj-$(CONFIG_HOSTAP) += hostap/ diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c deleted file mode 100644 index dab25136214a..000000000000 --- a/drivers/net/wireless/at76c50x-usb.c +++ /dev/null @@ -1,2617 +0,0 @@ -/* - * at76c503/at76c505 USB driver - * - * Copyright (c) 2002 - 2003 Oliver Kurth - * Copyright (c) 2004 Joerg Albert - * Copyright (c) 2004 Nick Jones - * Copyright (c) 2004 Balint Seeber - * Copyright (c) 2007 Guido Guenther - * Copyright (c) 2007 Kalle Valo - * Copyright (c) 2010 Sebastian Smolorz - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This file is part of the Berlios driver for WLAN USB devices based on the - * Atmel AT76C503A/505/505A. - * - * Some iw_handler code was taken from airo.c, (C) 1999 Benjamin Reed - * - * TODO list is at the wiki: - * - * http://wireless.kernel.org/en/users/Drivers/at76c50x-usb#TODO - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "at76c50x-usb.h" - -/* Version information */ -#define DRIVER_NAME "at76c50x-usb" -#define DRIVER_VERSION "0.17" -#define DRIVER_DESC "Atmel at76x USB Wireless LAN Driver" - -/* at76_debug bits */ -#define DBG_PROGRESS 0x00000001 /* authentication/accociation */ -#define DBG_BSS_TABLE 0x00000002 /* show BSS table after scans */ -#define DBG_IOCTL 0x00000004 /* ioctl calls / settings */ -#define DBG_MAC_STATE 0x00000008 /* MAC state transitions */ -#define DBG_TX_DATA 0x00000010 /* tx header */ -#define DBG_TX_DATA_CONTENT 0x00000020 /* tx content */ -#define DBG_TX_MGMT 0x00000040 /* tx management */ -#define DBG_RX_DATA 0x00000080 /* rx data header */ -#define DBG_RX_DATA_CONTENT 0x00000100 /* rx data content */ -#define DBG_RX_MGMT 0x00000200 /* rx mgmt frame headers */ -#define DBG_RX_BEACON 0x00000400 /* rx beacon */ -#define DBG_RX_CTRL 0x00000800 /* rx control */ -#define DBG_RX_MGMT_CONTENT 0x00001000 /* rx mgmt content */ -#define DBG_RX_FRAGS 0x00002000 /* rx data fragment handling */ -#define DBG_DEVSTART 0x00004000 /* fw download, device start */ -#define DBG_URB 0x00008000 /* rx urb status, ... */ -#define DBG_RX_ATMEL_HDR 0x00010000 /* Atmel-specific Rx headers */ -#define DBG_PROC_ENTRY 0x00020000 /* procedure entries/exits */ -#define DBG_PM 0x00040000 /* power management settings */ -#define DBG_BSS_MATCH 0x00080000 /* BSS match failures */ -#define DBG_PARAMS 0x00100000 /* show configured parameters */ -#define DBG_WAIT_COMPLETE 0x00200000 /* command completion */ -#define DBG_RX_FRAGS_SKB 0x00400000 /* skb header of Rx fragments */ -#define DBG_BSS_TABLE_RM 0x00800000 /* purging bss table entries */ -#define DBG_MONITOR_MODE 0x01000000 /* monitor mode */ -#define DBG_MIB 0x02000000 /* dump all MIBs on startup */ -#define DBG_MGMT_TIMER 0x04000000 /* dump mgmt_timer ops */ -#define DBG_WE_EVENTS 0x08000000 /* dump wireless events */ -#define DBG_FW 0x10000000 /* firmware download */ -#define DBG_DFU 0x20000000 /* device firmware upgrade */ -#define DBG_CMD 0x40000000 -#define DBG_MAC80211 0x80000000 - -#define DBG_DEFAULTS 0 - -/* Use our own dbg macro */ -#define at76_dbg(bits, format, arg...) \ -do { \ - if (at76_debug & (bits)) \ - printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ -} while (0) - -#define at76_dbg_dump(bits, buf, len, format, arg...) \ -do { \ - if (at76_debug & (bits)) { \ - printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ - print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); \ - } \ -} while (0) - -static uint at76_debug = DBG_DEFAULTS; - -/* Protect against concurrent firmware loading and parsing */ -static struct mutex fw_mutex; - -static struct fwentry firmwares[] = { - [0] = { "" }, - [BOARD_503_ISL3861] = { "atmel_at76c503-i3861.bin" }, - [BOARD_503_ISL3863] = { "atmel_at76c503-i3863.bin" }, - [BOARD_503] = { "atmel_at76c503-rfmd.bin" }, - [BOARD_503_ACC] = { "atmel_at76c503-rfmd-acc.bin" }, - [BOARD_505] = { "atmel_at76c505-rfmd.bin" }, - [BOARD_505_2958] = { "atmel_at76c505-rfmd2958.bin" }, - [BOARD_505A] = { "atmel_at76c505a-rfmd2958.bin" }, - [BOARD_505AMX] = { "atmel_at76c505amx-rfmd.bin" }, -}; -MODULE_FIRMWARE("atmel_at76c503-i3861.bin"); -MODULE_FIRMWARE("atmel_at76c503-i3863.bin"); -MODULE_FIRMWARE("atmel_at76c503-rfmd.bin"); -MODULE_FIRMWARE("atmel_at76c503-rfmd-acc.bin"); -MODULE_FIRMWARE("atmel_at76c505-rfmd.bin"); -MODULE_FIRMWARE("atmel_at76c505-rfmd2958.bin"); -MODULE_FIRMWARE("atmel_at76c505a-rfmd2958.bin"); -MODULE_FIRMWARE("atmel_at76c505amx-rfmd.bin"); - -#define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) - -static struct usb_device_id dev_table[] = { - /* - * at76c503-i3861 - */ - /* Generic AT76C503/3861 device */ - { USB_DEVICE(0x03eb, 0x7603), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Linksys WUSB11 v2.1/v2.6 */ - { USB_DEVICE(0x066b, 0x2211), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Netgear MA101 rev. A */ - { USB_DEVICE(0x0864, 0x4100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Tekram U300C / Allnet ALL0193 */ - { USB_DEVICE(0x0b3b, 0x1612), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* HP HN210W J7801A */ - { USB_DEVICE(0x03f0, 0x011c), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Sitecom/Z-Com/Zyxel M4Y-750 */ - { USB_DEVICE(0x0cde, 0x0001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Dynalink/Askey WLL013 (intersil) */ - { USB_DEVICE(0x069a, 0x0320), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* EZ connect 11Mpbs Wireless USB Adapter SMC2662W v1 */ - { USB_DEVICE(0x0d5c, 0xa001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* BenQ AWL300 */ - { USB_DEVICE(0x04a5, 0x9000), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Addtron AWU-120, Compex WLU11 */ - { USB_DEVICE(0x05dd, 0xff31), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Intel AP310 AnyPoint II USB */ - { USB_DEVICE(0x8086, 0x0200), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Dynalink L11U */ - { USB_DEVICE(0x0d8e, 0x7100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Arescom WL-210, FCC id 07J-GL2411USB */ - { USB_DEVICE(0x0d8e, 0x7110), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* I-O DATA WN-B11/USB */ - { USB_DEVICE(0x04bb, 0x0919), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* BT Voyager 1010 */ - { USB_DEVICE(0x069a, 0x0821), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* - * at76c503-i3863 - */ - /* Generic AT76C503/3863 device */ - { USB_DEVICE(0x03eb, 0x7604), USB_DEVICE_DATA(BOARD_503_ISL3863) }, - /* Samsung SWL-2100U */ - { USB_DEVICE(0x055d, 0xa000), USB_DEVICE_DATA(BOARD_503_ISL3863) }, - /* - * at76c503-rfmd - */ - /* Generic AT76C503/RFMD device */ - { USB_DEVICE(0x03eb, 0x7605), USB_DEVICE_DATA(BOARD_503) }, - /* Dynalink/Askey WLL013 (rfmd) */ - { USB_DEVICE(0x069a, 0x0321), USB_DEVICE_DATA(BOARD_503) }, - /* Linksys WUSB11 v2.6 */ - { USB_DEVICE(0x077b, 0x2219), USB_DEVICE_DATA(BOARD_503) }, - /* Network Everywhere NWU11B */ - { USB_DEVICE(0x077b, 0x2227), USB_DEVICE_DATA(BOARD_503) }, - /* Netgear MA101 rev. B */ - { USB_DEVICE(0x0864, 0x4102), USB_DEVICE_DATA(BOARD_503) }, - /* D-Link DWL-120 rev. E */ - { USB_DEVICE(0x2001, 0x3200), USB_DEVICE_DATA(BOARD_503) }, - /* Actiontec 802UAT1, HWU01150-01UK */ - { USB_DEVICE(0x1668, 0x7605), USB_DEVICE_DATA(BOARD_503) }, - /* AirVast W-Buddie WN210 */ - { USB_DEVICE(0x03eb, 0x4102), USB_DEVICE_DATA(BOARD_503) }, - /* Dick Smith Electronics XH1153 802.11b USB adapter */ - { USB_DEVICE(0x1371, 0x5743), USB_DEVICE_DATA(BOARD_503) }, - /* CNet CNUSB611 */ - { USB_DEVICE(0x1371, 0x0001), USB_DEVICE_DATA(BOARD_503) }, - /* FiberLine FL-WL200U */ - { USB_DEVICE(0x1371, 0x0002), USB_DEVICE_DATA(BOARD_503) }, - /* BenQ AWL400 USB stick */ - { USB_DEVICE(0x04a5, 0x9001), USB_DEVICE_DATA(BOARD_503) }, - /* 3Com 3CRSHEW696 */ - { USB_DEVICE(0x0506, 0x0a01), USB_DEVICE_DATA(BOARD_503) }, - /* Siemens Santis ADSL WLAN USB adapter WLL 013 */ - { USB_DEVICE(0x0681, 0x001b), USB_DEVICE_DATA(BOARD_503) }, - /* Belkin F5D6050, version 2 */ - { USB_DEVICE(0x050d, 0x0050), USB_DEVICE_DATA(BOARD_503) }, - /* iBlitzz, BWU613 (not *B or *SB) */ - { USB_DEVICE(0x07b8, 0xb000), USB_DEVICE_DATA(BOARD_503) }, - /* Gigabyte GN-WLBM101 */ - { USB_DEVICE(0x1044, 0x8003), USB_DEVICE_DATA(BOARD_503) }, - /* Planex GW-US11S */ - { USB_DEVICE(0x2019, 0x3220), USB_DEVICE_DATA(BOARD_503) }, - /* Internal WLAN adapter in h5[4,5]xx series iPAQs */ - { USB_DEVICE(0x049f, 0x0032), USB_DEVICE_DATA(BOARD_503) }, - /* Corega Wireless LAN USB-11 mini */ - { USB_DEVICE(0x07aa, 0x0011), USB_DEVICE_DATA(BOARD_503) }, - /* Corega Wireless LAN USB-11 mini2 */ - { USB_DEVICE(0x07aa, 0x0018), USB_DEVICE_DATA(BOARD_503) }, - /* Uniden PCW100 */ - { USB_DEVICE(0x05dd, 0xff35), USB_DEVICE_DATA(BOARD_503) }, - /* - * at76c503-rfmd-acc - */ - /* SMC2664W */ - { USB_DEVICE(0x083a, 0x3501), USB_DEVICE_DATA(BOARD_503_ACC) }, - /* Belkin F5D6050, SMC2662W v2, SMC2662W-AR */ - { USB_DEVICE(0x0d5c, 0xa002), USB_DEVICE_DATA(BOARD_503_ACC) }, - /* - * at76c505-rfmd - */ - /* Generic AT76C505/RFMD */ - { USB_DEVICE(0x03eb, 0x7606), USB_DEVICE_DATA(BOARD_505) }, - /* - * at76c505-rfmd2958 - */ - /* Generic AT76C505/RFMD, OvisLink WL-1130USB */ - { USB_DEVICE(0x03eb, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, - /* Fiberline FL-WL240U */ - { USB_DEVICE(0x1371, 0x0014), USB_DEVICE_DATA(BOARD_505_2958) }, - /* CNet CNUSB-611G */ - { USB_DEVICE(0x1371, 0x0013), USB_DEVICE_DATA(BOARD_505_2958) }, - /* Linksys WUSB11 v2.8 */ - { USB_DEVICE(0x1915, 0x2233), USB_DEVICE_DATA(BOARD_505_2958) }, - /* Xterasys XN-2122B, IBlitzz BWU613B/BWU613SB */ - { USB_DEVICE(0x12fd, 0x1001), USB_DEVICE_DATA(BOARD_505_2958) }, - /* Corega WLAN USB Stick 11 */ - { USB_DEVICE(0x07aa, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, - /* Microstar MSI Box MS6978 */ - { USB_DEVICE(0x0db0, 0x1020), USB_DEVICE_DATA(BOARD_505_2958) }, - /* - * at76c505a-rfmd2958 - */ - /* Generic AT76C505A device */ - { USB_DEVICE(0x03eb, 0x7614), USB_DEVICE_DATA(BOARD_505A) }, - /* Generic AT76C505AS device */ - { USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A) }, - /* Siemens Gigaset USB WLAN Adapter 11 */ - { USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A) }, - /* OQO Model 01+ Internal Wi-Fi */ - { USB_DEVICE(0x1557, 0x0002), USB_DEVICE_DATA(BOARD_505A) }, - /* - * at76c505amx-rfmd - */ - /* Generic AT76C505AMX device */ - { USB_DEVICE(0x03eb, 0x7615), USB_DEVICE_DATA(BOARD_505AMX) }, - { } -}; - -MODULE_DEVICE_TABLE(usb, dev_table); - -/* Supported rates of this hardware, bit 7 marks basic rates */ -static const u8 hw_rates[] = { 0x82, 0x84, 0x0b, 0x16 }; - -static const char *const preambles[] = { "long", "short", "auto" }; - -/* Firmware download */ -/* DFU states */ -#define STATE_IDLE 0x00 -#define STATE_DETACH 0x01 -#define STATE_DFU_IDLE 0x02 -#define STATE_DFU_DOWNLOAD_SYNC 0x03 -#define STATE_DFU_DOWNLOAD_BUSY 0x04 -#define STATE_DFU_DOWNLOAD_IDLE 0x05 -#define STATE_DFU_MANIFEST_SYNC 0x06 -#define STATE_DFU_MANIFEST 0x07 -#define STATE_DFU_MANIFEST_WAIT_RESET 0x08 -#define STATE_DFU_UPLOAD_IDLE 0x09 -#define STATE_DFU_ERROR 0x0a - -/* DFU commands */ -#define DFU_DETACH 0 -#define DFU_DNLOAD 1 -#define DFU_UPLOAD 2 -#define DFU_GETSTATUS 3 -#define DFU_CLRSTATUS 4 -#define DFU_GETSTATE 5 -#define DFU_ABORT 6 - -#define FW_BLOCK_SIZE 1024 - -struct dfu_status { - unsigned char status; - unsigned char poll_timeout[3]; - unsigned char state; - unsigned char string; -} __packed; - -static inline int at76_is_intersil(enum board_type board) -{ - return (board == BOARD_503_ISL3861 || board == BOARD_503_ISL3863); -} - -static inline int at76_is_503rfmd(enum board_type board) -{ - return (board == BOARD_503 || board == BOARD_503_ACC); -} - -static inline int at76_is_505a(enum board_type board) -{ - return (board == BOARD_505A || board == BOARD_505AMX); -} - -/* Load a block of the first (internal) part of the firmware */ -static int at76_load_int_fw_block(struct usb_device *udev, int blockno, - void *block, int size) -{ - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), DFU_DNLOAD, - USB_TYPE_CLASS | USB_DIR_OUT | - USB_RECIP_INTERFACE, blockno, 0, block, size, - USB_CTRL_GET_TIMEOUT); -} - -static int at76_dfu_get_status(struct usb_device *udev, - struct dfu_status *status) -{ - int ret; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATUS, - USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, - 0, 0, status, sizeof(struct dfu_status), - USB_CTRL_GET_TIMEOUT); - return ret; -} - -static int at76_dfu_get_state(struct usb_device *udev, u8 *state) -{ - int ret; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATE, - USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, - 0, 0, state, 1, USB_CTRL_GET_TIMEOUT); - return ret; -} - -/* Convert timeout from the DFU status to jiffies */ -static inline unsigned long at76_get_timeout(struct dfu_status *s) -{ - return msecs_to_jiffies((s->poll_timeout[2] << 16) - | (s->poll_timeout[1] << 8) - | (s->poll_timeout[0])); -} - -/* Load internal firmware from the buffer. If manifest_sync_timeout > 0, use - * its value in jiffies in the MANIFEST_SYNC state. */ -static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, - int manifest_sync_timeout) -{ - int ret = 0; - int need_dfu_state = 1; - int is_done = 0; - u32 dfu_timeout = 0; - int bsize = 0; - int blockno = 0; - struct dfu_status *dfu_stat_buf = NULL; - u8 *dfu_state = NULL; - u8 *block = NULL; - - at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size, - manifest_sync_timeout); - - if (!size) { - dev_err(&udev->dev, "FW buffer length invalid!\n"); - return -EINVAL; - } - - dfu_stat_buf = kmalloc(sizeof(struct dfu_status), GFP_KERNEL); - if (!dfu_stat_buf) { - ret = -ENOMEM; - goto exit; - } - - block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); - if (!block) { - ret = -ENOMEM; - goto exit; - } - - dfu_state = kmalloc(sizeof(u8), GFP_KERNEL); - if (!dfu_state) { - ret = -ENOMEM; - goto exit; - } - *dfu_state = 0; - - do { - if (need_dfu_state) { - ret = at76_dfu_get_state(udev, dfu_state); - if (ret < 0) { - dev_err(&udev->dev, - "cannot get DFU state: %d\n", ret); - goto exit; - } - need_dfu_state = 0; - } - - switch (*dfu_state) { - case STATE_DFU_DOWNLOAD_SYNC: - at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC"); - ret = at76_dfu_get_status(udev, dfu_stat_buf); - if (ret >= 0) { - *dfu_state = dfu_stat_buf->state; - dfu_timeout = at76_get_timeout(dfu_stat_buf); - need_dfu_state = 0; - } else - dev_err(&udev->dev, - "at76_dfu_get_status returned %d\n", - ret); - break; - - case STATE_DFU_DOWNLOAD_BUSY: - at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_BUSY"); - need_dfu_state = 1; - - at76_dbg(DBG_DFU, "DFU: Resetting device"); - schedule_timeout_interruptible(dfu_timeout); - break; - - case STATE_DFU_DOWNLOAD_IDLE: - at76_dbg(DBG_DFU, "DOWNLOAD..."); - /* fall through */ - case STATE_DFU_IDLE: - at76_dbg(DBG_DFU, "DFU IDLE"); - - bsize = min_t(int, size, FW_BLOCK_SIZE); - memcpy(block, buf, bsize); - at76_dbg(DBG_DFU, "int fw, size left = %5d, " - "bsize = %4d, blockno = %2d", size, bsize, - blockno); - ret = - at76_load_int_fw_block(udev, blockno, block, bsize); - buf += bsize; - size -= bsize; - blockno++; - - if (ret != bsize) - dev_err(&udev->dev, - "at76_load_int_fw_block returned %d\n", - ret); - need_dfu_state = 1; - break; - - case STATE_DFU_MANIFEST_SYNC: - at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC"); - - ret = at76_dfu_get_status(udev, dfu_stat_buf); - if (ret < 0) - break; - - *dfu_state = dfu_stat_buf->state; - dfu_timeout = at76_get_timeout(dfu_stat_buf); - need_dfu_state = 0; - - /* override the timeout from the status response, - needed for AT76C505A */ - if (manifest_sync_timeout > 0) - dfu_timeout = manifest_sync_timeout; - - at76_dbg(DBG_DFU, "DFU: Waiting for manifest phase"); - schedule_timeout_interruptible(dfu_timeout); - break; - - case STATE_DFU_MANIFEST: - at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST"); - is_done = 1; - break; - - case STATE_DFU_MANIFEST_WAIT_RESET: - at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_WAIT_RESET"); - is_done = 1; - break; - - case STATE_DFU_UPLOAD_IDLE: - at76_dbg(DBG_DFU, "STATE_DFU_UPLOAD_IDLE"); - break; - - case STATE_DFU_ERROR: - at76_dbg(DBG_DFU, "STATE_DFU_ERROR"); - ret = -EPIPE; - break; - - default: - at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", *dfu_state); - ret = -EINVAL; - break; - } - } while (!is_done && (ret >= 0)); - -exit: - kfree(dfu_state); - kfree(block); - kfree(dfu_stat_buf); - - if (ret >= 0) - ret = 0; - - return ret; -} - -/* LED trigger */ -static int tx_activity; -static void at76_ledtrig_tx_timerfunc(unsigned long data); -static DEFINE_TIMER(ledtrig_tx_timer, at76_ledtrig_tx_timerfunc, 0, 0); -DEFINE_LED_TRIGGER(ledtrig_tx); - -static void at76_ledtrig_tx_timerfunc(unsigned long data) -{ - static int tx_lastactivity; - - if (tx_lastactivity != tx_activity) { - tx_lastactivity = tx_activity; - led_trigger_event(ledtrig_tx, LED_FULL); - mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); - } else - led_trigger_event(ledtrig_tx, LED_OFF); -} - -static void at76_ledtrig_tx_activity(void) -{ - tx_activity++; - if (!timer_pending(&ledtrig_tx_timer)) - mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); -} - -static int at76_remap(struct usb_device *udev) -{ - int ret; - ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0a, - USB_TYPE_VENDOR | USB_DIR_OUT | - USB_RECIP_INTERFACE, 0, 0, NULL, 0, - USB_CTRL_GET_TIMEOUT); - if (ret < 0) - return ret; - return 0; -} - -static int at76_get_op_mode(struct usb_device *udev) -{ - int ret; - u8 saved; - u8 *op_mode; - - op_mode = kmalloc(1, GFP_NOIO); - if (!op_mode) - return -ENOMEM; - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, - USB_TYPE_VENDOR | USB_DIR_IN | - USB_RECIP_INTERFACE, 0x01, 0, op_mode, 1, - USB_CTRL_GET_TIMEOUT); - saved = *op_mode; - kfree(op_mode); - - if (ret < 0) - return ret; - else if (ret < 1) - return -EIO; - else - return saved; -} - -/* Load a block of the second ("external") part of the firmware */ -static inline int at76_load_ext_fw_block(struct usb_device *udev, int blockno, - void *block, int size) -{ - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - 0x0802, blockno, block, size, - USB_CTRL_GET_TIMEOUT); -} - -static inline int at76_get_hw_cfg(struct usb_device *udev, - union at76_hwcfg *buf, int buf_size) -{ - return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, - USB_TYPE_VENDOR | USB_DIR_IN | - USB_RECIP_INTERFACE, 0x0a02, 0, - buf, buf_size, USB_CTRL_GET_TIMEOUT); -} - -/* Intersil boards use a different "value" for GetHWConfig requests */ -static inline int at76_get_hw_cfg_intersil(struct usb_device *udev, - union at76_hwcfg *buf, int buf_size) -{ - return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, - USB_TYPE_VENDOR | USB_DIR_IN | - USB_RECIP_INTERFACE, 0x0902, 0, - buf, buf_size, USB_CTRL_GET_TIMEOUT); -} - -/* Get the hardware configuration for the adapter and put it to the appropriate - * fields of 'priv' (the GetHWConfig request and interpretation of the result - * depends on the board type) */ -static int at76_get_hw_config(struct at76_priv *priv) -{ - int ret; - union at76_hwcfg *hwcfg = kmalloc(sizeof(*hwcfg), GFP_KERNEL); - - if (!hwcfg) - return -ENOMEM; - - if (at76_is_intersil(priv->board_type)) { - ret = at76_get_hw_cfg_intersil(priv->udev, hwcfg, - sizeof(hwcfg->i)); - if (ret < 0) - goto exit; - memcpy(priv->mac_addr, hwcfg->i.mac_addr, ETH_ALEN); - priv->regulatory_domain = hwcfg->i.regulatory_domain; - } else if (at76_is_503rfmd(priv->board_type)) { - ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r3)); - if (ret < 0) - goto exit; - memcpy(priv->mac_addr, hwcfg->r3.mac_addr, ETH_ALEN); - priv->regulatory_domain = hwcfg->r3.regulatory_domain; - } else { - ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r5)); - if (ret < 0) - goto exit; - memcpy(priv->mac_addr, hwcfg->r5.mac_addr, ETH_ALEN); - priv->regulatory_domain = hwcfg->r5.regulatory_domain; - } - -exit: - kfree(hwcfg); - if (ret < 0) - wiphy_err(priv->hw->wiphy, "cannot get HW Config (error %d)\n", - ret); - - return ret; -} - -static struct reg_domain const *at76_get_reg_domain(u16 code) -{ - int i; - static struct reg_domain const fd_tab[] = { - { 0x10, "FCC (USA)", 0x7ff }, /* ch 1-11 */ - { 0x20, "IC (Canada)", 0x7ff }, /* ch 1-11 */ - { 0x30, "ETSI (most of Europe)", 0x1fff }, /* ch 1-13 */ - { 0x31, "Spain", 0x600 }, /* ch 10-11 */ - { 0x32, "France", 0x1e00 }, /* ch 10-13 */ - { 0x40, "MKK (Japan)", 0x2000 }, /* ch 14 */ - { 0x41, "MKK1 (Japan)", 0x3fff }, /* ch 1-14 */ - { 0x50, "Israel", 0x3fc }, /* ch 3-9 */ - { 0x00, "", 0xffffffff } /* ch 1-32 */ - }; - - /* Last entry is fallback for unknown domain code */ - for (i = 0; i < ARRAY_SIZE(fd_tab) - 1; i++) - if (code == fd_tab[i].code) - break; - - return &fd_tab[i]; -} - -static inline int at76_get_mib(struct usb_device *udev, u16 mib, void *buf, - int buf_size) -{ - int ret; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, - USB_TYPE_VENDOR | USB_DIR_IN | - USB_RECIP_INTERFACE, mib << 8, 0, buf, buf_size, - USB_CTRL_GET_TIMEOUT); - if (ret >= 0 && ret != buf_size) - return -EIO; - return ret; -} - -/* Return positive number for status, negative for an error */ -static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd) -{ - u8 *stat_buf; - int ret; - - stat_buf = kmalloc(40, GFP_NOIO); - if (!stat_buf) - return -ENOMEM; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x22, - USB_TYPE_VENDOR | USB_DIR_IN | - USB_RECIP_INTERFACE, cmd, 0, stat_buf, - 40, USB_CTRL_GET_TIMEOUT); - if (ret >= 0) - ret = stat_buf[5]; - kfree(stat_buf); - - return ret; -} - -#define MAKE_CMD_CASE(c) case (c): return #c -static const char *at76_get_cmd_string(u8 cmd_status) -{ - switch (cmd_status) { - MAKE_CMD_CASE(CMD_SET_MIB); - MAKE_CMD_CASE(CMD_GET_MIB); - MAKE_CMD_CASE(CMD_SCAN); - MAKE_CMD_CASE(CMD_JOIN); - MAKE_CMD_CASE(CMD_START_IBSS); - MAKE_CMD_CASE(CMD_RADIO_ON); - MAKE_CMD_CASE(CMD_RADIO_OFF); - MAKE_CMD_CASE(CMD_STARTUP); - } - - return "UNKNOWN"; -} - -static int at76_set_card_command(struct usb_device *udev, u8 cmd, void *buf, - int buf_size) -{ - int ret; - struct at76_command *cmd_buf = kmalloc(sizeof(struct at76_command) + - buf_size, GFP_KERNEL); - - if (!cmd_buf) - return -ENOMEM; - - cmd_buf->cmd = cmd; - cmd_buf->reserved = 0; - cmd_buf->size = cpu_to_le16(buf_size); - memcpy(cmd_buf->data, buf, buf_size); - - at76_dbg_dump(DBG_CMD, cmd_buf, sizeof(struct at76_command) + buf_size, - "issuing command %s (0x%02x)", - at76_get_cmd_string(cmd), cmd); - - ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - 0, 0, cmd_buf, - sizeof(struct at76_command) + buf_size, - USB_CTRL_GET_TIMEOUT); - kfree(cmd_buf); - return ret; -} - -#define MAKE_CMD_STATUS_CASE(c) case (c): return #c -static const char *at76_get_cmd_status_string(u8 cmd_status) -{ - switch (cmd_status) { - MAKE_CMD_STATUS_CASE(CMD_STATUS_IDLE); - MAKE_CMD_STATUS_CASE(CMD_STATUS_COMPLETE); - MAKE_CMD_STATUS_CASE(CMD_STATUS_UNKNOWN); - MAKE_CMD_STATUS_CASE(CMD_STATUS_INVALID_PARAMETER); - MAKE_CMD_STATUS_CASE(CMD_STATUS_FUNCTION_NOT_SUPPORTED); - MAKE_CMD_STATUS_CASE(CMD_STATUS_TIME_OUT); - MAKE_CMD_STATUS_CASE(CMD_STATUS_IN_PROGRESS); - MAKE_CMD_STATUS_CASE(CMD_STATUS_HOST_FAILURE); - MAKE_CMD_STATUS_CASE(CMD_STATUS_SCAN_FAILED); - } - - return "UNKNOWN"; -} - -/* Wait until the command is completed */ -static int at76_wait_completion(struct at76_priv *priv, int cmd) -{ - int status = 0; - unsigned long timeout = jiffies + CMD_COMPLETION_TIMEOUT; - - do { - status = at76_get_cmd_status(priv->udev, cmd); - if (status < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_cmd_status failed: %d\n", - status); - break; - } - - at76_dbg(DBG_WAIT_COMPLETE, - "%s: Waiting on cmd %d, status = %d (%s)", - wiphy_name(priv->hw->wiphy), cmd, status, - at76_get_cmd_status_string(status)); - - if (status != CMD_STATUS_IN_PROGRESS - && status != CMD_STATUS_IDLE) - break; - - schedule_timeout_interruptible(HZ / 10); /* 100 ms */ - if (time_after(jiffies, timeout)) { - wiphy_err(priv->hw->wiphy, - "completion timeout for command %d\n", cmd); - status = -ETIMEDOUT; - break; - } - } while (1); - - return status; -} - -static int at76_set_mib(struct at76_priv *priv, struct set_mib_buffer *buf) -{ - int ret; - - ret = at76_set_card_command(priv->udev, CMD_SET_MIB, buf, - offsetof(struct set_mib_buffer, - data) + buf->size); - if (ret < 0) - return ret; - - ret = at76_wait_completion(priv, CMD_SET_MIB); - if (ret != CMD_STATUS_COMPLETE) { - wiphy_info(priv->hw->wiphy, - "set_mib: at76_wait_completion failed with %d\n", - ret); - ret = -EIO; - } - - return ret; -} - -/* Return < 0 on error, == 0 if no command sent, == 1 if cmd sent */ -static int at76_set_radio(struct at76_priv *priv, int enable) -{ - int ret; - int cmd; - - if (priv->radio_on == enable) - return 0; - - cmd = enable ? CMD_RADIO_ON : CMD_RADIO_OFF; - - ret = at76_set_card_command(priv->udev, cmd, NULL, 0); - if (ret < 0) - wiphy_err(priv->hw->wiphy, - "at76_set_card_command(%d) failed: %d\n", cmd, ret); - else - ret = 1; - - priv->radio_on = enable; - return ret; -} - -/* Set current power save mode (AT76_PM_OFF/AT76_PM_ON/AT76_PM_SMART) */ -static int at76_set_pm_mode(struct at76_priv *priv) -{ - int ret = 0; - - priv->mib_buf.type = MIB_MAC_MGMT; - priv->mib_buf.size = 1; - priv->mib_buf.index = offsetof(struct mib_mac_mgmt, power_mgmt_mode); - priv->mib_buf.data.byte = priv->pm_mode; - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, "set_mib (pm_mode) failed: %d\n", - ret); - - return ret; -} - -static int at76_set_preamble(struct at76_priv *priv, u8 type) -{ - int ret = 0; - - priv->mib_buf.type = MIB_LOCAL; - priv->mib_buf.size = 1; - priv->mib_buf.index = offsetof(struct mib_local, preamble_type); - priv->mib_buf.data.byte = type; - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, "set_mib (preamble) failed: %d\n", - ret); - - return ret; -} - -static int at76_set_frag(struct at76_priv *priv, u16 size) -{ - int ret = 0; - - priv->mib_buf.type = MIB_MAC; - priv->mib_buf.size = 2; - priv->mib_buf.index = offsetof(struct mib_mac, frag_threshold); - priv->mib_buf.data.word = cpu_to_le16(size); - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, - "set_mib (frag threshold) failed: %d\n", ret); - - return ret; -} - -static int at76_set_rts(struct at76_priv *priv, u16 size) -{ - int ret = 0; - - priv->mib_buf.type = MIB_MAC; - priv->mib_buf.size = 2; - priv->mib_buf.index = offsetof(struct mib_mac, rts_threshold); - priv->mib_buf.data.word = cpu_to_le16(size); - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, "set_mib (rts) failed: %d\n", ret); - - return ret; -} - -static int at76_set_autorate_fallback(struct at76_priv *priv, int onoff) -{ - int ret = 0; - - priv->mib_buf.type = MIB_LOCAL; - priv->mib_buf.size = 1; - priv->mib_buf.index = offsetof(struct mib_local, txautorate_fallback); - priv->mib_buf.data.byte = onoff; - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, - "set_mib (autorate fallback) failed: %d\n", ret); - - return ret; -} - -static void at76_dump_mib_mac_addr(struct at76_priv *priv) -{ - int i; - int ret; - struct mib_mac_addr *m = kmalloc(sizeof(struct mib_mac_addr), - GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_MAC_ADDR, m, - sizeof(struct mib_mac_addr)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (MAC_ADDR) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %pM res 0x%x 0x%x", - wiphy_name(priv->hw->wiphy), - m->mac_addr, m->res[0], m->res[1]); - for (i = 0; i < ARRAY_SIZE(m->group_addr); i++) - at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %pM, " - "status %d", wiphy_name(priv->hw->wiphy), i, - m->group_addr[i], m->group_addr_status[i]); -exit: - kfree(m); -} - -static void at76_dump_mib_mac_wep(struct at76_priv *priv) -{ - int i; - int ret; - int key_len; - struct mib_mac_wep *m = kmalloc(sizeof(struct mib_mac_wep), GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_MAC_WEP, m, - sizeof(struct mib_mac_wep)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (MAC_WEP) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: priv_invoked %u def_key_id %u " - "key_len %u excl_unencr %u wep_icv_err %u wep_excluded %u " - "encr_level %u key %d", wiphy_name(priv->hw->wiphy), - m->privacy_invoked, m->wep_default_key_id, - m->wep_key_mapping_len, m->exclude_unencrypted, - le32_to_cpu(m->wep_icv_error_count), - le32_to_cpu(m->wep_excluded_count), m->encryption_level, - m->wep_default_key_id); - - key_len = (m->encryption_level == 1) ? - WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN; - - for (i = 0; i < WEP_KEYS; i++) - at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: key %d: %*phD", - wiphy_name(priv->hw->wiphy), i, - key_len, m->wep_default_keyvalue[i]); -exit: - kfree(m); -} - -static void at76_dump_mib_mac_mgmt(struct at76_priv *priv) -{ - int ret; - struct mib_mac_mgmt *m = kmalloc(sizeof(struct mib_mac_mgmt), - GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, m, - sizeof(struct mib_mac_mgmt)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (MAC_MGMT) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration " - "%d medium_occupancy_limit %d station_id 0x%x ATIM_window %d " - "CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d " - "current_bssid %pM current_essid %*phD current_bss_type %d " - "pm_mode %d ibss_change %d res %d " - "multi_domain_capability_implemented %d " - "international_roaming %d country_string %.3s", - wiphy_name(priv->hw->wiphy), le16_to_cpu(m->beacon_period), - le16_to_cpu(m->CFP_max_duration), - le16_to_cpu(m->medium_occupancy_limit), - le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window), - m->CFP_mode, m->privacy_option_implemented, m->DTIM_period, - m->CFP_period, m->current_bssid, - IW_ESSID_MAX_SIZE, m->current_essid, - m->current_bss_type, m->power_mgmt_mode, m->ibss_change, - m->res, m->multi_domain_capability_implemented, - m->multi_domain_capability_enabled, m->country_string); -exit: - kfree(m); -} - -static void at76_dump_mib_mac(struct at76_priv *priv) -{ - int ret; - struct mib_mac *m = kmalloc(sizeof(struct mib_mac), GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_MAC, m, sizeof(struct mib_mac)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (MAC) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB MAC: max_tx_msdu_lifetime %d " - "max_rx_lifetime %d frag_threshold %d rts_threshold %d " - "cwmin %d cwmax %d short_retry_time %d long_retry_time %d " - "scan_type %d scan_channel %d probe_delay %u " - "min_channel_time %d max_channel_time %d listen_int %d " - "desired_ssid %*phD desired_bssid %pM desired_bsstype %d", - wiphy_name(priv->hw->wiphy), - le32_to_cpu(m->max_tx_msdu_lifetime), - le32_to_cpu(m->max_rx_lifetime), - le16_to_cpu(m->frag_threshold), le16_to_cpu(m->rts_threshold), - le16_to_cpu(m->cwmin), le16_to_cpu(m->cwmax), - m->short_retry_time, m->long_retry_time, m->scan_type, - m->scan_channel, le16_to_cpu(m->probe_delay), - le16_to_cpu(m->min_channel_time), - le16_to_cpu(m->max_channel_time), - le16_to_cpu(m->listen_interval), - IW_ESSID_MAX_SIZE, m->desired_ssid, - m->desired_bssid, m->desired_bsstype); -exit: - kfree(m); -} - -static void at76_dump_mib_phy(struct at76_priv *priv) -{ - int ret; - struct mib_phy *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_PHY, m, sizeof(struct mib_phy)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (PHY) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB PHY: ed_threshold %d slot_time %d " - "sifs_time %d preamble_length %d plcp_header_length %d " - "mpdu_max_length %d cca_mode_supported %d operation_rate_set " - "0x%x 0x%x 0x%x 0x%x channel_id %d current_cca_mode %d " - "phy_type %d current_reg_domain %d", - wiphy_name(priv->hw->wiphy), le32_to_cpu(m->ed_threshold), - le16_to_cpu(m->slot_time), le16_to_cpu(m->sifs_time), - le16_to_cpu(m->preamble_length), - le16_to_cpu(m->plcp_header_length), - le16_to_cpu(m->mpdu_max_length), - le16_to_cpu(m->cca_mode_supported), m->operation_rate_set[0], - m->operation_rate_set[1], m->operation_rate_set[2], - m->operation_rate_set[3], m->channel_id, m->current_cca_mode, - m->phy_type, m->current_reg_domain); -exit: - kfree(m); -} - -static void at76_dump_mib_local(struct at76_priv *priv) -{ - int ret; - struct mib_local *m = kmalloc(sizeof(*m), GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(*m)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (LOCAL) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB LOCAL: beacon_enable %d " - "txautorate_fallback %d ssid_size %d promiscuous_mode %d " - "preamble_type %d", wiphy_name(priv->hw->wiphy), - m->beacon_enable, - m->txautorate_fallback, m->ssid_size, m->promiscuous_mode, - m->preamble_type); -exit: - kfree(m); -} - -static void at76_dump_mib_mdomain(struct at76_priv *priv) -{ - int ret; - struct mib_mdomain *m = kmalloc(sizeof(struct mib_mdomain), GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_MDOMAIN, m, - sizeof(struct mib_mdomain)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (MDOMAIN) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %*phD", - wiphy_name(priv->hw->wiphy), - (int)sizeof(m->channel_list), m->channel_list); - - at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: tx_powerlevel %*phD", - wiphy_name(priv->hw->wiphy), - (int)sizeof(m->tx_powerlevel), m->tx_powerlevel); -exit: - kfree(m); -} - -/* Enable monitor mode */ -static int at76_start_monitor(struct at76_priv *priv) -{ - struct at76_req_scan scan; - int ret; - - memset(&scan, 0, sizeof(struct at76_req_scan)); - eth_broadcast_addr(scan.bssid); - - scan.channel = priv->channel; - scan.scan_type = SCAN_TYPE_PASSIVE; - scan.international_scan = 0; - scan.min_channel_time = cpu_to_le16(priv->scan_min_time); - scan.max_channel_time = cpu_to_le16(priv->scan_max_time); - scan.probe_delay = cpu_to_le16(0); - - ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); - if (ret >= 0) - ret = at76_get_cmd_status(priv->udev, CMD_SCAN); - - return ret; -} - -/* Calculate padding from txbuf->wlength (which excludes the USB TX header), - likely to compensate a flaw in the AT76C503A USB part ... */ -static inline int at76_calc_padding(int wlen) -{ - /* add the USB TX header */ - wlen += AT76_TX_HDRLEN; - - wlen = wlen % 64; - - if (wlen < 50) - return 50 - wlen; - - if (wlen >= 61) - return 64 + 50 - wlen; - - return 0; -} - -static void at76_rx_callback(struct urb *urb) -{ - struct at76_priv *priv = urb->context; - - priv->rx_tasklet.data = (unsigned long)urb; - tasklet_schedule(&priv->rx_tasklet); -} - -static int at76_submit_rx_urb(struct at76_priv *priv) -{ - int ret; - int size; - struct sk_buff *skb = priv->rx_skb; - - if (!priv->rx_urb) { - wiphy_err(priv->hw->wiphy, "%s: priv->rx_urb is NULL\n", - __func__); - return -EFAULT; - } - - if (!skb) { - skb = dev_alloc_skb(sizeof(struct at76_rx_buffer)); - if (!skb) { - wiphy_err(priv->hw->wiphy, - "cannot allocate rx skbuff\n"); - ret = -ENOMEM; - goto exit; - } - priv->rx_skb = skb; - } else { - skb_push(skb, skb_headroom(skb)); - skb_trim(skb, 0); - } - - size = skb_tailroom(skb); - usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe, - skb_put(skb, size), size, at76_rx_callback, priv); - ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC); - if (ret < 0) { - if (ret == -ENODEV) - at76_dbg(DBG_DEVSTART, - "usb_submit_urb returned -ENODEV"); - else - wiphy_err(priv->hw->wiphy, - "rx, usb_submit_urb failed: %d\n", ret); - } - -exit: - if (ret < 0 && ret != -ENODEV) - wiphy_err(priv->hw->wiphy, - "cannot submit rx urb - please unload the driver and/or power cycle the device\n"); - - return ret; -} - -/* Download external firmware */ -static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) -{ - int ret; - int op_mode; - int blockno = 0; - int bsize; - u8 *block; - u8 *buf = fwe->extfw; - int size = fwe->extfw_size; - - if (!buf || !size) - return -ENOENT; - - op_mode = at76_get_op_mode(udev); - at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); - - if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { - dev_err(&udev->dev, "unexpected opmode %d\n", op_mode); - return -EINVAL; - } - - block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); - if (!block) - return -ENOMEM; - - at76_dbg(DBG_DEVSTART, "downloading external firmware"); - - /* for fw >= 0.100, the device needs an extra empty block */ - do { - bsize = min_t(int, size, FW_BLOCK_SIZE); - memcpy(block, buf, bsize); - at76_dbg(DBG_DEVSTART, - "ext fw, size left = %5d, bsize = %4d, blockno = %2d", - size, bsize, blockno); - ret = at76_load_ext_fw_block(udev, blockno, block, bsize); - if (ret != bsize) { - dev_err(&udev->dev, - "loading %dth firmware block failed: %d\n", - blockno, ret); - ret = -EIO; - goto exit; - } - buf += bsize; - size -= bsize; - blockno++; - } while (bsize > 0); - - if (at76_is_505a(fwe->board_type)) { - at76_dbg(DBG_DEVSTART, "200 ms delay for 505a"); - schedule_timeout_interruptible(HZ / 5 + 1); - } - -exit: - kfree(block); - if (ret < 0) - dev_err(&udev->dev, - "downloading external firmware failed: %d\n", ret); - return ret; -} - -/* Download internal firmware */ -static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe) -{ - int ret; - int need_remap = !at76_is_505a(fwe->board_type); - - ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size, - need_remap ? 0 : 2 * HZ); - - if (ret < 0) { - dev_err(&udev->dev, - "downloading internal fw failed with %d\n", ret); - goto exit; - } - - at76_dbg(DBG_DEVSTART, "sending REMAP"); - - /* no REMAP for 505A (see SF driver) */ - if (need_remap) { - ret = at76_remap(udev); - if (ret < 0) { - dev_err(&udev->dev, - "sending REMAP failed with %d\n", ret); - goto exit; - } - } - - at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds"); - schedule_timeout_interruptible(2 * HZ + 1); - usb_reset_device(udev); - -exit: - return ret; -} - -static int at76_startup_device(struct at76_priv *priv) -{ - struct at76_card_config *ccfg = &priv->card_config; - int ret; - - at76_dbg(DBG_PARAMS, - "%s param: ssid %.*s (%*phD) mode %s ch %d wep %s key %d " - "keylen %d", wiphy_name(priv->hw->wiphy), priv->essid_size, - priv->essid, IW_ESSID_MAX_SIZE, priv->essid, - priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra", - priv->channel, priv->wep_enabled ? "enabled" : "disabled", - priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]); - at76_dbg(DBG_PARAMS, - "%s param: preamble %s rts %d retry %d frag %d " - "txrate %s auth_mode %d", wiphy_name(priv->hw->wiphy), - preambles[priv->preamble_type], priv->rts_threshold, - priv->short_retry_limit, priv->frag_threshold, - priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate == - TX_RATE_2MBIT ? "2MBit" : priv->txrate == - TX_RATE_5_5MBIT ? "5.5MBit" : priv->txrate == - TX_RATE_11MBIT ? "11MBit" : priv->txrate == - TX_RATE_AUTO ? "auto" : "", priv->auth_mode); - at76_dbg(DBG_PARAMS, - "%s param: pm_mode %d pm_period %d auth_mode %s " - "scan_times %d %d scan_mode %s", - wiphy_name(priv->hw->wiphy), priv->pm_mode, priv->pm_period, - priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret", - priv->scan_min_time, priv->scan_max_time, - priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive"); - - memset(ccfg, 0, sizeof(struct at76_card_config)); - ccfg->promiscuous_mode = 0; - ccfg->short_retry_limit = priv->short_retry_limit; - - if (priv->wep_enabled) { - if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) - ccfg->encryption_type = 2; - else - ccfg->encryption_type = 1; - - /* jal: always exclude unencrypted if WEP is active */ - ccfg->exclude_unencrypted = 1; - } else { - ccfg->exclude_unencrypted = 0; - ccfg->encryption_type = 0; - } - - ccfg->rts_threshold = cpu_to_le16(priv->rts_threshold); - ccfg->fragmentation_threshold = cpu_to_le16(priv->frag_threshold); - - memcpy(ccfg->basic_rate_set, hw_rates, 4); - /* jal: really needed, we do a set_mib for autorate later ??? */ - ccfg->auto_rate_fallback = (priv->txrate == TX_RATE_AUTO ? 1 : 0); - ccfg->channel = priv->channel; - ccfg->privacy_invoked = priv->wep_enabled; - memcpy(ccfg->current_ssid, priv->essid, IW_ESSID_MAX_SIZE); - ccfg->ssid_len = priv->essid_size; - - ccfg->wep_default_key_id = priv->wep_key_id; - memcpy(ccfg->wep_default_key_value, priv->wep_keys, - sizeof(priv->wep_keys)); - - ccfg->short_preamble = priv->preamble_type; - ccfg->beacon_period = cpu_to_le16(priv->beacon_period); - - ret = at76_set_card_command(priv->udev, CMD_STARTUP, &priv->card_config, - sizeof(struct at76_card_config)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", - ret); - return ret; - } - - at76_wait_completion(priv, CMD_STARTUP); - - /* remove BSSID from previous run */ - eth_zero_addr(priv->bssid); - - priv->scanning = false; - - if (at76_set_radio(priv, 1) == 1) - at76_wait_completion(priv, CMD_RADIO_ON); - - ret = at76_set_preamble(priv, priv->preamble_type); - if (ret < 0) - return ret; - - ret = at76_set_frag(priv, priv->frag_threshold); - if (ret < 0) - return ret; - - ret = at76_set_rts(priv, priv->rts_threshold); - if (ret < 0) - return ret; - - ret = at76_set_autorate_fallback(priv, - priv->txrate == TX_RATE_AUTO ? 1 : 0); - if (ret < 0) - return ret; - - ret = at76_set_pm_mode(priv); - if (ret < 0) - return ret; - - if (at76_debug & DBG_MIB) { - at76_dump_mib_mac(priv); - at76_dump_mib_mac_addr(priv); - at76_dump_mib_mac_mgmt(priv); - at76_dump_mib_mac_wep(priv); - at76_dump_mib_mdomain(priv); - at76_dump_mib_phy(priv); - at76_dump_mib_local(priv); - } - - return 0; -} - -/* Enable or disable promiscuous mode */ -static void at76_work_set_promisc(struct work_struct *work) -{ - struct at76_priv *priv = container_of(work, struct at76_priv, - work_set_promisc); - int ret = 0; - - if (priv->device_unplugged) - return; - - mutex_lock(&priv->mtx); - - priv->mib_buf.type = MIB_LOCAL; - priv->mib_buf.size = 1; - priv->mib_buf.index = offsetof(struct mib_local, promiscuous_mode); - priv->mib_buf.data.byte = priv->promisc ? 1 : 0; - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, - "set_mib (promiscuous_mode) failed: %d\n", ret); - - mutex_unlock(&priv->mtx); -} - -/* Submit Rx urb back to the device */ -static void at76_work_submit_rx(struct work_struct *work) -{ - struct at76_priv *priv = container_of(work, struct at76_priv, - work_submit_rx); - - mutex_lock(&priv->mtx); - at76_submit_rx_urb(priv); - mutex_unlock(&priv->mtx); -} - -/* This is a workaround to make scan working: - * currently mac80211 does not process frames with no frequency - * information. - * However during scan the HW performs a sweep by itself, and we - * are unable to know where the radio is actually tuned. - * This function tries to do its best to guess this information.. - * During scan, If the current frame is a beacon or a probe response, - * the channel information is extracted from it. - * When not scanning, for other frames, or if it happens that for - * whatever reason we fail to parse beacons and probe responses, this - * function returns the priv->channel information, that should be correct - * at least when we are not scanning. - */ -static inline int at76_guess_freq(struct at76_priv *priv) -{ - size_t el_off; - const u8 *el; - int channel = priv->channel; - int len = priv->rx_skb->len; - struct ieee80211_hdr *hdr = (void *)priv->rx_skb->data; - - if (!priv->scanning) - goto exit; - - if (len < 24) - goto exit; - - if (ieee80211_is_probe_resp(hdr->frame_control)) { - el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable; - } else if (ieee80211_is_beacon(hdr->frame_control)) { - el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable); - el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable; - } else { - goto exit; - } - len -= el_off; - - el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len); - if (el && el[1] > 0) - channel = el[2]; - -exit: - return ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ); -} - -static void at76_rx_tasklet(unsigned long param) -{ - struct urb *urb = (struct urb *)param; - struct at76_priv *priv = urb->context; - struct at76_rx_buffer *buf; - struct ieee80211_rx_status rx_status = { 0 }; - - if (priv->device_unplugged) { - at76_dbg(DBG_DEVSTART, "device unplugged"); - at76_dbg(DBG_DEVSTART, "urb status %d", urb->status); - return; - } - - if (!priv->rx_skb || !priv->rx_skb->data) - return; - - buf = (struct at76_rx_buffer *)priv->rx_skb->data; - - if (urb->status != 0) { - if (urb->status != -ENOENT && urb->status != -ECONNRESET) - at76_dbg(DBG_URB, - "%s %s: - nonzero Rx bulk status received: %d", - __func__, wiphy_name(priv->hw->wiphy), - urb->status); - return; - } - - at76_dbg(DBG_RX_ATMEL_HDR, - "%s: rx frame: rate %d rssi %d noise %d link %d", - wiphy_name(priv->hw->wiphy), buf->rx_rate, buf->rssi, - buf->noise_level, buf->link_quality); - - skb_pull(priv->rx_skb, AT76_RX_HDRLEN); - skb_trim(priv->rx_skb, le16_to_cpu(buf->wlength)); - at76_dbg_dump(DBG_RX_DATA, priv->rx_skb->data, - priv->rx_skb->len, "RX: len=%d", priv->rx_skb->len); - - rx_status.signal = buf->rssi; - rx_status.flag |= RX_FLAG_DECRYPTED; - rx_status.flag |= RX_FLAG_IV_STRIPPED; - rx_status.band = IEEE80211_BAND_2GHZ; - rx_status.freq = at76_guess_freq(priv); - - at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d", - priv->rx_skb->len, priv->rx_skb->data_len); - memcpy(IEEE80211_SKB_RXCB(priv->rx_skb), &rx_status, sizeof(rx_status)); - ieee80211_rx_irqsafe(priv->hw, priv->rx_skb); - - /* Use a new skb for the next receive */ - priv->rx_skb = NULL; - - at76_submit_rx_urb(priv); -} - -/* Load firmware into kernel memory and parse it */ -static struct fwentry *at76_load_firmware(struct usb_device *udev, - enum board_type board_type) -{ - int ret; - char *str; - struct at76_fw_header *fwh; - struct fwentry *fwe = &firmwares[board_type]; - - mutex_lock(&fw_mutex); - - if (fwe->loaded) { - at76_dbg(DBG_FW, "re-using previously loaded fw"); - goto exit; - } - - at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname); - ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev); - if (ret < 0) { - dev_err(&udev->dev, "firmware %s not found!\n", - fwe->fwname); - dev_err(&udev->dev, - "you may need to download the firmware from http://developer.berlios.de/projects/at76c503a/\n"); - goto exit; - } - - at76_dbg(DBG_FW, "got it."); - fwh = (struct at76_fw_header *)(fwe->fw->data); - - if (fwe->fw->size <= sizeof(*fwh)) { - dev_err(&udev->dev, - "firmware is too short (0x%zx)\n", fwe->fw->size); - goto exit; - } - - /* CRC currently not checked */ - fwe->board_type = le32_to_cpu(fwh->board_type); - if (fwe->board_type != board_type) { - dev_err(&udev->dev, - "board type mismatch, requested %u, got %u\n", - board_type, fwe->board_type); - goto exit; - } - - fwe->fw_version.major = fwh->major; - fwe->fw_version.minor = fwh->minor; - fwe->fw_version.patch = fwh->patch; - fwe->fw_version.build = fwh->build; - - str = (char *)fwh + le32_to_cpu(fwh->str_offset); - fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset); - fwe->intfw_size = le32_to_cpu(fwh->int_fw_len); - fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset); - fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len); - - fwe->loaded = 1; - - dev_printk(KERN_DEBUG, &udev->dev, - "using firmware %s (version %d.%d.%d-%d)\n", - fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build); - - at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type, - le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len), - le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len)); - at76_dbg(DBG_DEVSTART, "firmware id %s", str); - -exit: - mutex_unlock(&fw_mutex); - - if (fwe->loaded) - return fwe; - else - return NULL; -} - -static int at76_join(struct at76_priv *priv) -{ - struct at76_req_join join; - int ret; - - memset(&join, 0, sizeof(struct at76_req_join)); - memcpy(join.essid, priv->essid, priv->essid_size); - join.essid_size = priv->essid_size; - memcpy(join.bssid, priv->bssid, ETH_ALEN); - join.bss_type = INFRASTRUCTURE_MODE; - join.channel = priv->channel; - join.timeout = cpu_to_le16(2000); - - at76_dbg(DBG_MAC80211, "%s: sending CMD_JOIN", __func__); - ret = at76_set_card_command(priv->udev, CMD_JOIN, &join, - sizeof(struct at76_req_join)); - - if (ret < 0) { - wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", - ret); - return 0; - } - - ret = at76_wait_completion(priv, CMD_JOIN); - at76_dbg(DBG_MAC80211, "%s: CMD_JOIN returned: 0x%02x", __func__, ret); - if (ret != CMD_STATUS_COMPLETE) { - wiphy_err(priv->hw->wiphy, "at76_wait_completion failed: %d\n", - ret); - return 0; - } - - at76_set_pm_mode(priv); - - return 0; -} - -static void at76_work_join_bssid(struct work_struct *work) -{ - struct at76_priv *priv = container_of(work, struct at76_priv, - work_join_bssid); - - if (priv->device_unplugged) - return; - - mutex_lock(&priv->mtx); - - if (is_valid_ether_addr(priv->bssid)) - at76_join(priv); - - mutex_unlock(&priv->mtx); -} - -static void at76_mac80211_tx_callback(struct urb *urb) -{ - struct at76_priv *priv = urb->context; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); - - at76_dbg(DBG_MAC80211, "%s()", __func__); - - switch (urb->status) { - case 0: - /* success */ - info->flags |= IEEE80211_TX_STAT_ACK; - break; - case -ENOENT: - case -ECONNRESET: - /* fail, urb has been unlinked */ - /* FIXME: add error message */ - break; - default: - at76_dbg(DBG_URB, "%s - nonzero tx status received: %d", - __func__, urb->status); - break; - } - - memset(&info->status, 0, sizeof(info->status)); - - ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); - - priv->tx_skb = NULL; - - ieee80211_wake_queues(priv->hw); -} - -static void at76_mac80211_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct at76_priv *priv = hw->priv; - struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; - int padding, submit_len, ret; - - at76_dbg(DBG_MAC80211, "%s()", __func__); - - if (priv->tx_urb->status == -EINPROGRESS) { - wiphy_err(priv->hw->wiphy, - "%s called while tx urb is pending\n", __func__); - dev_kfree_skb_any(skb); - return; - } - - /* The following code lines are important when the device is going to - * authenticate with a new bssid. The driver must send CMD_JOIN before - * an authentication frame is transmitted. For this to succeed, the - * correct bssid of the AP must be known. As mac80211 does not inform - * drivers about the bssid prior to the authentication process the - * following workaround is necessary. If the TX frame is an - * authentication frame extract the bssid and send the CMD_JOIN. */ - if (mgmt->frame_control & cpu_to_le16(IEEE80211_STYPE_AUTH)) { - if (!ether_addr_equal_64bits(priv->bssid, mgmt->bssid)) { - memcpy(priv->bssid, mgmt->bssid, ETH_ALEN); - ieee80211_queue_work(hw, &priv->work_join_bssid); - dev_kfree_skb_any(skb); - return; - } - } - - ieee80211_stop_queues(hw); - - at76_ledtrig_tx_activity(); /* tell ledtrigger we send a packet */ - - WARN_ON(priv->tx_skb != NULL); - - priv->tx_skb = skb; - padding = at76_calc_padding(skb->len); - submit_len = AT76_TX_HDRLEN + skb->len + padding; - - /* setup 'Atmel' header */ - memset(tx_buffer, 0, sizeof(*tx_buffer)); - tx_buffer->padding = padding; - tx_buffer->wlength = cpu_to_le16(skb->len); - tx_buffer->tx_rate = ieee80211_get_tx_rate(hw, info)->hw_value; - memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved)); - memcpy(tx_buffer->packet, skb->data, skb->len); - - at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr", - wiphy_name(priv->hw->wiphy), le16_to_cpu(tx_buffer->wlength), - tx_buffer->padding, tx_buffer->tx_rate); - - /* send stuff */ - at76_dbg_dump(DBG_TX_DATA_CONTENT, tx_buffer, submit_len, - "%s(): tx_buffer %d bytes:", __func__, submit_len); - usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer, - submit_len, at76_mac80211_tx_callback, priv); - ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); - if (ret) { - wiphy_err(priv->hw->wiphy, "error in tx submit urb: %d\n", ret); - if (ret == -EINVAL) - wiphy_err(priv->hw->wiphy, - "-EINVAL: tx urb %p hcpriv %p complete %p\n", - priv->tx_urb, - priv->tx_urb->hcpriv, priv->tx_urb->complete); - } -} - -static int at76_mac80211_start(struct ieee80211_hw *hw) -{ - struct at76_priv *priv = hw->priv; - int ret; - - at76_dbg(DBG_MAC80211, "%s()", __func__); - - mutex_lock(&priv->mtx); - - ret = at76_submit_rx_urb(priv); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, "open: submit_rx_urb failed: %d\n", - ret); - goto error; - } - - at76_startup_device(priv); - - at76_start_monitor(priv); - -error: - mutex_unlock(&priv->mtx); - - return 0; -} - -static void at76_mac80211_stop(struct ieee80211_hw *hw) -{ - struct at76_priv *priv = hw->priv; - - at76_dbg(DBG_MAC80211, "%s()", __func__); - - cancel_delayed_work(&priv->dwork_hw_scan); - cancel_work_sync(&priv->work_join_bssid); - cancel_work_sync(&priv->work_set_promisc); - - mutex_lock(&priv->mtx); - - if (!priv->device_unplugged) { - /* We are called by "ifconfig ethX down", not because the - * device is not available anymore. */ - at76_set_radio(priv, 0); - - /* We unlink rx_urb because at76_open() re-submits it. - * If unplugged, at76_delete_device() takes care of it. */ - usb_kill_urb(priv->rx_urb); - } - - mutex_unlock(&priv->mtx); -} - -static int at76_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct at76_priv *priv = hw->priv; - int ret = 0; - - at76_dbg(DBG_MAC80211, "%s()", __func__); - - mutex_lock(&priv->mtx); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - priv->iw_mode = IW_MODE_INFRA; - break; - default: - ret = -EOPNOTSUPP; - goto exit; - } - -exit: - mutex_unlock(&priv->mtx); - - return ret; -} - -static void at76_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - at76_dbg(DBG_MAC80211, "%s()", __func__); -} - -static void at76_dwork_hw_scan(struct work_struct *work) -{ - struct at76_priv *priv = container_of(work, struct at76_priv, - dwork_hw_scan.work); - int ret; - - if (priv->device_unplugged) - return; - - mutex_lock(&priv->mtx); - - ret = at76_get_cmd_status(priv->udev, CMD_SCAN); - at76_dbg(DBG_MAC80211, "%s: CMD_SCAN status 0x%02x", __func__, ret); - - /* FIXME: add maximum time for scan to complete */ - - if (ret != CMD_STATUS_COMPLETE) { - ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, - SCAN_POLL_INTERVAL); - mutex_unlock(&priv->mtx); - return; - } - - if (is_valid_ether_addr(priv->bssid)) - at76_join(priv); - - priv->scanning = false; - - mutex_unlock(&priv->mtx); - - ieee80211_scan_completed(priv->hw, false); - - ieee80211_wake_queues(priv->hw); -} - -static int at76_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req) -{ - struct cfg80211_scan_request *req = &hw_req->req; - struct at76_priv *priv = hw->priv; - struct at76_req_scan scan; - u8 *ssid = NULL; - int ret, len = 0; - - at76_dbg(DBG_MAC80211, "%s():", __func__); - - if (priv->device_unplugged) - return 0; - - mutex_lock(&priv->mtx); - - ieee80211_stop_queues(hw); - - memset(&scan, 0, sizeof(struct at76_req_scan)); - eth_broadcast_addr(scan.bssid); - - if (req->n_ssids) { - scan.scan_type = SCAN_TYPE_ACTIVE; - ssid = req->ssids[0].ssid; - len = req->ssids[0].ssid_len; - } else { - scan.scan_type = SCAN_TYPE_PASSIVE; - } - - if (len) { - memcpy(scan.essid, ssid, len); - scan.essid_size = len; - } - - scan.min_channel_time = cpu_to_le16(priv->scan_min_time); - scan.max_channel_time = cpu_to_le16(priv->scan_max_time); - scan.probe_delay = cpu_to_le16(priv->scan_min_time * 1000); - scan.international_scan = 0; - - at76_dbg(DBG_MAC80211, "%s: sending CMD_SCAN", __func__); - ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); - - if (ret < 0) { - wiphy_err(priv->hw->wiphy, "CMD_SCAN failed: %d\n", ret); - goto exit; - } - - priv->scanning = true; - ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, - SCAN_POLL_INTERVAL); - -exit: - mutex_unlock(&priv->mtx); - - return 0; -} - -static int at76_config(struct ieee80211_hw *hw, u32 changed) -{ - struct at76_priv *priv = hw->priv; - - at76_dbg(DBG_MAC80211, "%s(): channel %d", - __func__, hw->conf.chandef.chan->hw_value); - at76_dbg_dump(DBG_MAC80211, priv->bssid, ETH_ALEN, "bssid:"); - - mutex_lock(&priv->mtx); - - priv->channel = hw->conf.chandef.chan->hw_value; - - if (is_valid_ether_addr(priv->bssid)) - at76_join(priv); - else - at76_start_monitor(priv); - - mutex_unlock(&priv->mtx); - - return 0; -} - -static void at76_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf, - u32 changed) -{ - struct at76_priv *priv = hw->priv; - - at76_dbg(DBG_MAC80211, "%s():", __func__); - - if (!(changed & BSS_CHANGED_BSSID)) - return; - - at76_dbg_dump(DBG_MAC80211, conf->bssid, ETH_ALEN, "bssid:"); - - mutex_lock(&priv->mtx); - - memcpy(priv->bssid, conf->bssid, ETH_ALEN); - - if (is_valid_ether_addr(priv->bssid)) - /* mac80211 is joining a bss */ - at76_join(priv); - - mutex_unlock(&priv->mtx); -} - -/* must be atomic */ -static void at76_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, u64 multicast) -{ - struct at76_priv *priv = hw->priv; - int flags; - - at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x " - "total_flags=0x%08x", - __func__, changed_flags, *total_flags); - - flags = changed_flags & AT76_SUPPORTED_FILTERS; - *total_flags = AT76_SUPPORTED_FILTERS; - - /* Bail out after updating flags to prevent a WARN_ON in mac80211. */ - if (priv->device_unplugged) - return; - - /* FIXME: access to priv->promisc should be protected with - * priv->mtx, but it's impossible because this function needs to be - * atomic */ - - if (flags && !priv->promisc) { - /* mac80211 wants us to enable promiscuous mode */ - priv->promisc = 1; - } else if (!flags && priv->promisc) { - /* we need to disable promiscuous mode */ - priv->promisc = 0; - } else - return; - - ieee80211_queue_work(hw, &priv->work_set_promisc); -} - -static int at76_set_wep(struct at76_priv *priv) -{ - int ret = 0; - struct mib_mac_wep *mib_data = &priv->mib_buf.data.wep_mib; - - priv->mib_buf.type = MIB_MAC_WEP; - priv->mib_buf.size = sizeof(struct mib_mac_wep); - priv->mib_buf.index = 0; - - memset(mib_data, 0, sizeof(*mib_data)); - - if (priv->wep_enabled) { - if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) - mib_data->encryption_level = 2; - else - mib_data->encryption_level = 1; - - /* always exclude unencrypted if WEP is active */ - mib_data->exclude_unencrypted = 1; - } else { - mib_data->exclude_unencrypted = 0; - mib_data->encryption_level = 0; - } - - mib_data->privacy_invoked = priv->wep_enabled; - mib_data->wep_default_key_id = priv->wep_key_id; - memcpy(mib_data->wep_default_keyvalue, priv->wep_keys, - sizeof(priv->wep_keys)); - - ret = at76_set_mib(priv, &priv->mib_buf); - - if (ret < 0) - wiphy_err(priv->hw->wiphy, - "set_mib (wep) failed: %d\n", ret); - - return ret; -} - -static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct at76_priv *priv = hw->priv; - - int i; - - at76_dbg(DBG_MAC80211, "%s(): cmd %d key->cipher %d key->keyidx %d " - "key->keylen %d", - __func__, cmd, key->cipher, key->keyidx, key->keylen); - - if ((key->cipher != WLAN_CIPHER_SUITE_WEP40) && - (key->cipher != WLAN_CIPHER_SUITE_WEP104)) - return -EOPNOTSUPP; - - key->hw_key_idx = key->keyidx; - - mutex_lock(&priv->mtx); - - switch (cmd) { - case SET_KEY: - memcpy(priv->wep_keys[key->keyidx], key->key, key->keylen); - priv->wep_keys_len[key->keyidx] = key->keylen; - - /* FIXME: find out how to do this properly */ - priv->wep_key_id = key->keyidx; - - break; - case DISABLE_KEY: - default: - priv->wep_keys_len[key->keyidx] = 0; - break; - } - - priv->wep_enabled = 0; - - for (i = 0; i < WEP_KEYS; i++) { - if (priv->wep_keys_len[i] != 0) - priv->wep_enabled = 1; - } - - at76_set_wep(priv); - - mutex_unlock(&priv->mtx); - - return 0; -} - -static const struct ieee80211_ops at76_ops = { - .tx = at76_mac80211_tx, - .add_interface = at76_add_interface, - .remove_interface = at76_remove_interface, - .config = at76_config, - .bss_info_changed = at76_bss_info_changed, - .configure_filter = at76_configure_filter, - .start = at76_mac80211_start, - .stop = at76_mac80211_stop, - .hw_scan = at76_hw_scan, - .set_key = at76_set_key, -}; - -/* Allocate network device and initialize private data */ -static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) -{ - struct ieee80211_hw *hw; - struct at76_priv *priv; - - hw = ieee80211_alloc_hw(sizeof(struct at76_priv), &at76_ops); - if (!hw) { - printk(KERN_ERR DRIVER_NAME ": could not register" - " ieee80211_hw\n"); - return NULL; - } - - priv = hw->priv; - priv->hw = hw; - - priv->udev = udev; - - mutex_init(&priv->mtx); - INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc); - INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx); - INIT_WORK(&priv->work_join_bssid, at76_work_join_bssid); - INIT_DELAYED_WORK(&priv->dwork_hw_scan, at76_dwork_hw_scan); - - tasklet_init(&priv->rx_tasklet, at76_rx_tasklet, 0); - - priv->pm_mode = AT76_PM_OFF; - priv->pm_period = 0; - - /* unit us */ - - return priv; -} - -static int at76_alloc_urbs(struct at76_priv *priv, - struct usb_interface *interface) -{ - struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out; - int i; - int buffer_size; - struct usb_host_interface *iface_desc; - - at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); - - at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__, - interface->altsetting[0].desc.bNumEndpoints); - - ep_in = NULL; - ep_out = NULL; - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { - endpoint = &iface_desc->endpoint[i].desc; - - at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x", - __func__, i, endpoint->bEndpointAddress, - endpoint->bmAttributes); - - if (!ep_in && usb_endpoint_is_bulk_in(endpoint)) - ep_in = endpoint; - - if (!ep_out && usb_endpoint_is_bulk_out(endpoint)) - ep_out = endpoint; - } - - if (!ep_in || !ep_out) { - dev_err(&interface->dev, "bulk endpoints missing\n"); - return -ENXIO; - } - - priv->rx_pipe = usb_rcvbulkpipe(priv->udev, ep_in->bEndpointAddress); - priv->tx_pipe = usb_sndbulkpipe(priv->udev, ep_out->bEndpointAddress); - - priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!priv->rx_urb || !priv->tx_urb) { - dev_err(&interface->dev, "cannot allocate URB\n"); - return -ENOMEM; - } - - buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE; - priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!priv->bulk_out_buffer) - return -ENOMEM; - - at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); - - return 0; -} - -static struct ieee80211_rate at76_rates[] = { - { .bitrate = 10, .hw_value = TX_RATE_1MBIT, }, - { .bitrate = 20, .hw_value = TX_RATE_2MBIT, }, - { .bitrate = 55, .hw_value = TX_RATE_5_5MBIT, }, - { .bitrate = 110, .hw_value = TX_RATE_11MBIT, }, -}; - -static struct ieee80211_channel at76_channels[] = { - { .center_freq = 2412, .hw_value = 1 }, - { .center_freq = 2417, .hw_value = 2 }, - { .center_freq = 2422, .hw_value = 3 }, - { .center_freq = 2427, .hw_value = 4 }, - { .center_freq = 2432, .hw_value = 5 }, - { .center_freq = 2437, .hw_value = 6 }, - { .center_freq = 2442, .hw_value = 7 }, - { .center_freq = 2447, .hw_value = 8 }, - { .center_freq = 2452, .hw_value = 9 }, - { .center_freq = 2457, .hw_value = 10 }, - { .center_freq = 2462, .hw_value = 11 }, - { .center_freq = 2467, .hw_value = 12 }, - { .center_freq = 2472, .hw_value = 13 }, - { .center_freq = 2484, .hw_value = 14 } -}; - -static struct ieee80211_supported_band at76_supported_band = { - .channels = at76_channels, - .n_channels = ARRAY_SIZE(at76_channels), - .bitrates = at76_rates, - .n_bitrates = ARRAY_SIZE(at76_rates), -}; - -/* Register network device and initialize the hardware */ -static int at76_init_new_device(struct at76_priv *priv, - struct usb_interface *interface) -{ - struct wiphy *wiphy; - size_t len; - int ret; - - /* set up the endpoint information */ - /* check out the endpoints */ - - at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints", - interface->cur_altsetting->desc.bNumEndpoints); - - ret = at76_alloc_urbs(priv, interface); - if (ret < 0) - goto exit; - - /* MAC address */ - ret = at76_get_hw_config(priv); - if (ret < 0) { - dev_err(&interface->dev, "cannot get MAC address\n"); - goto exit; - } - - priv->domain = at76_get_reg_domain(priv->regulatory_domain); - - priv->channel = DEF_CHANNEL; - priv->iw_mode = IW_MODE_INFRA; - priv->rts_threshold = DEF_RTS_THRESHOLD; - priv->frag_threshold = DEF_FRAG_THRESHOLD; - priv->short_retry_limit = DEF_SHORT_RETRY_LIMIT; - priv->txrate = TX_RATE_AUTO; - priv->preamble_type = PREAMBLE_TYPE_LONG; - priv->beacon_period = 100; - priv->auth_mode = WLAN_AUTH_OPEN; - priv->scan_min_time = DEF_SCAN_MIN_TIME; - priv->scan_max_time = DEF_SCAN_MAX_TIME; - priv->scan_mode = SCAN_TYPE_ACTIVE; - priv->device_unplugged = 0; - - /* mac80211 initialisation */ - wiphy = priv->hw->wiphy; - priv->hw->wiphy->max_scan_ssids = 1; - priv->hw->wiphy->max_scan_ie_len = 0; - priv->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &at76_supported_band; - ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); - ieee80211_hw_set(priv->hw, SIGNAL_UNSPEC); - priv->hw->max_signal = 100; - - SET_IEEE80211_DEV(priv->hw, &interface->dev); - SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); - - len = sizeof(wiphy->fw_version); - snprintf(wiphy->fw_version, len, "%d.%d.%d-%d", - priv->fw_version.major, priv->fw_version.minor, - priv->fw_version.patch, priv->fw_version.build); - - wiphy->hw_version = priv->board_type; - - ret = ieee80211_register_hw(priv->hw); - if (ret) { - printk(KERN_ERR "cannot register mac80211 hw (status %d)!\n", - ret); - goto exit; - } - - priv->mac80211_registered = 1; - - wiphy_info(priv->hw->wiphy, "USB %s, MAC %pM, firmware %d.%d.%d-%d\n", - dev_name(&interface->dev), priv->mac_addr, - priv->fw_version.major, priv->fw_version.minor, - priv->fw_version.patch, priv->fw_version.build); - wiphy_info(priv->hw->wiphy, "regulatory domain 0x%02x: %s\n", - priv->regulatory_domain, priv->domain->name); - -exit: - return ret; -} - -static void at76_delete_device(struct at76_priv *priv) -{ - at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); - - /* The device is gone, don't bother turning it off */ - priv->device_unplugged = 1; - - tasklet_kill(&priv->rx_tasklet); - - if (priv->mac80211_registered) - ieee80211_unregister_hw(priv->hw); - - if (priv->tx_urb) { - usb_kill_urb(priv->tx_urb); - usb_free_urb(priv->tx_urb); - } - if (priv->rx_urb) { - usb_kill_urb(priv->rx_urb); - usb_free_urb(priv->rx_urb); - } - - at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __func__); - - kfree(priv->bulk_out_buffer); - - del_timer_sync(&ledtrig_tx_timer); - - kfree_skb(priv->rx_skb); - - at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/ieee80211_hw", - __func__); - ieee80211_free_hw(priv->hw); - - at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); -} - -static int at76_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - int ret; - struct at76_priv *priv; - struct fwentry *fwe; - struct usb_device *udev; - int op_mode; - int need_ext_fw = 0; - struct mib_fw_version *fwv = NULL; - int board_type = (int)id->driver_info; - - udev = usb_get_dev(interface_to_usbdev(interface)); - - fwv = kmalloc(sizeof(*fwv), GFP_KERNEL); - if (!fwv) { - ret = -ENOMEM; - goto exit; - } - - /* Load firmware into kernel memory */ - fwe = at76_load_firmware(udev, board_type); - if (!fwe) { - ret = -ENOENT; - goto exit; - } - - op_mode = at76_get_op_mode(udev); - - at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); - - /* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ??? - we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */ - - if (op_mode == OPMODE_HW_CONFIG_MODE) { - dev_err(&interface->dev, - "cannot handle a device in HW_CONFIG_MODE\n"); - ret = -EBUSY; - goto exit; - } - - if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH - && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { - /* download internal firmware part */ - dev_printk(KERN_DEBUG, &interface->dev, - "downloading internal firmware\n"); - ret = at76_load_internal_fw(udev, fwe); - if (ret < 0) { - dev_err(&interface->dev, - "error %d downloading internal firmware\n", - ret); - goto exit; - } - usb_put_dev(udev); - goto exit; - } - - /* Internal firmware already inside the device. Get firmware - * version to test if external firmware is loaded. - * This works only for newer firmware, e.g. the Intersil 0.90.x - * says "control timeout on ep0in" and subsequent - * at76_get_op_mode() fail too :-( */ - - /* if version >= 0.100.x.y or device with built-in flash we can - * query the device for the fw version */ - if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100) - || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { - ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); - if (ret < 0 || (fwv->major | fwv->minor) == 0) - need_ext_fw = 1; - } else - /* No way to check firmware version, reload to be sure */ - need_ext_fw = 1; - - if (need_ext_fw) { - dev_printk(KERN_DEBUG, &interface->dev, - "downloading external firmware\n"); - - ret = at76_load_external_fw(udev, fwe); - if (ret < 0) - goto exit; - - /* Re-check firmware version */ - ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); - if (ret < 0) { - dev_err(&interface->dev, - "error %d getting firmware version\n", ret); - goto exit; - } - } - - priv = at76_alloc_new_device(udev); - if (!priv) { - ret = -ENOMEM; - goto exit; - } - - usb_set_intfdata(interface, priv); - - memcpy(&priv->fw_version, fwv, sizeof(struct mib_fw_version)); - priv->board_type = board_type; - - ret = at76_init_new_device(priv, interface); - if (ret < 0) - at76_delete_device(priv); - -exit: - kfree(fwv); - if (ret < 0) - usb_put_dev(udev); - return ret; -} - -static void at76_disconnect(struct usb_interface *interface) -{ - struct at76_priv *priv; - - priv = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - /* Disconnect after loading internal firmware */ - if (!priv) - return; - - wiphy_info(priv->hw->wiphy, "disconnecting\n"); - at76_delete_device(priv); - usb_put_dev(priv->udev); - dev_info(&interface->dev, "disconnected\n"); -} - -/* Structure for registering this driver with the USB subsystem */ -static struct usb_driver at76_driver = { - .name = DRIVER_NAME, - .probe = at76_probe, - .disconnect = at76_disconnect, - .id_table = dev_table, - .disable_hub_initiated_lpm = 1, -}; - -static int __init at76_mod_init(void) -{ - int result; - - printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " loading\n"); - - mutex_init(&fw_mutex); - - /* register this driver with the USB subsystem */ - result = usb_register(&at76_driver); - if (result < 0) - printk(KERN_ERR DRIVER_NAME - ": usb_register failed (status %d)\n", result); - - led_trigger_register_simple("at76_usb-tx", &ledtrig_tx); - return result; -} - -static void __exit at76_mod_exit(void) -{ - int i; - - printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n"); - usb_deregister(&at76_driver); - for (i = 0; i < ARRAY_SIZE(firmwares); i++) - release_firmware(firmwares[i].fw); - led_trigger_unregister_simple(ledtrig_tx); -} - -module_param_named(debug, at76_debug, uint, 0600); -MODULE_PARM_DESC(debug, "Debugging level"); - -module_init(at76_mod_init); -module_exit(at76_mod_exit); - -MODULE_AUTHOR("Oliver Kurth "); -MODULE_AUTHOR("Joerg Albert "); -MODULE_AUTHOR("Alex "); -MODULE_AUTHOR("Nick Jones"); -MODULE_AUTHOR("Balint Seeber "); -MODULE_AUTHOR("Pavel Roskin "); -MODULE_AUTHOR("Guido Guenther "); -MODULE_AUTHOR("Kalle Valo "); -MODULE_AUTHOR("Sebastian Smolorz "); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/at76c50x-usb.h b/drivers/net/wireless/at76c50x-usb.h deleted file mode 100644 index ae03271f878e..000000000000 --- a/drivers/net/wireless/at76c50x-usb.h +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (c) 2002,2003 Oliver Kurth - * (c) 2003,2004 Joerg Albert - * (c) 2007 Guido Guenther - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This driver was based on information from the Sourceforge driver - * released and maintained by Atmel: - * - * http://sourceforge.net/projects/atmelwlandriver/ - * - * Although the code was completely re-written, - * it would have been impossible without Atmel's decision to - * release an Open Source driver (unfortunately the firmware was - * kept binary only). Thanks for that decision to Atmel! - */ - -#ifndef _AT76_USB_H -#define _AT76_USB_H - -/* Board types */ -enum board_type { - BOARD_503_ISL3861 = 1, - BOARD_503_ISL3863 = 2, - BOARD_503 = 3, - BOARD_503_ACC = 4, - BOARD_505 = 5, - BOARD_505_2958 = 6, - BOARD_505A = 7, - BOARD_505AMX = 8 -}; - -#define CMD_STATUS_IDLE 0x00 -#define CMD_STATUS_COMPLETE 0x01 -#define CMD_STATUS_UNKNOWN 0x02 -#define CMD_STATUS_INVALID_PARAMETER 0x03 -#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 -#define CMD_STATUS_TIME_OUT 0x07 -#define CMD_STATUS_IN_PROGRESS 0x08 -#define CMD_STATUS_HOST_FAILURE 0xff -#define CMD_STATUS_SCAN_FAILED 0xf0 - -/* answers to get op mode */ -#define OPMODE_NONE 0x00 -#define OPMODE_NORMAL_NIC_WITH_FLASH 0x01 -#define OPMODE_HW_CONFIG_MODE 0x02 -#define OPMODE_DFU_MODE_WITH_FLASH 0x03 -#define OPMODE_NORMAL_NIC_WITHOUT_FLASH 0x04 - -#define CMD_SET_MIB 0x01 -#define CMD_GET_MIB 0x02 -#define CMD_SCAN 0x03 -#define CMD_JOIN 0x04 -#define CMD_START_IBSS 0x05 -#define CMD_RADIO_ON 0x06 -#define CMD_RADIO_OFF 0x07 -#define CMD_STARTUP 0x0B - -#define MIB_LOCAL 0x01 -#define MIB_MAC_ADDR 0x02 -#define MIB_MAC 0x03 -#define MIB_MAC_MGMT 0x05 -#define MIB_MAC_WEP 0x06 -#define MIB_PHY 0x07 -#define MIB_FW_VERSION 0x08 -#define MIB_MDOMAIN 0x09 - -#define ADHOC_MODE 1 -#define INFRASTRUCTURE_MODE 2 - -/* values for struct mib_local, field preamble_type */ -#define PREAMBLE_TYPE_LONG 0 -#define PREAMBLE_TYPE_SHORT 1 -#define PREAMBLE_TYPE_AUTO 2 - -/* values for tx_rate */ -#define TX_RATE_1MBIT 0 -#define TX_RATE_2MBIT 1 -#define TX_RATE_5_5MBIT 2 -#define TX_RATE_11MBIT 3 -#define TX_RATE_AUTO 4 - -/* power management modes */ -#define AT76_PM_OFF 1 -#define AT76_PM_ON 2 -#define AT76_PM_SMART 3 - -struct hwcfg_r505 { - u8 cr39_values[14]; - u8 reserved1[14]; - u8 bb_cr[14]; - u8 pidvid[4]; - u8 mac_addr[ETH_ALEN]; - u8 regulatory_domain; - u8 reserved2[14]; - u8 cr15_values[14]; - u8 reserved3[3]; -} __packed; - -struct hwcfg_rfmd { - u8 cr20_values[14]; - u8 cr21_values[14]; - u8 bb_cr[14]; - u8 pidvid[4]; - u8 mac_addr[ETH_ALEN]; - u8 regulatory_domain; - u8 low_power_values[14]; - u8 normal_power_values[14]; - u8 reserved1[3]; -} __packed; - -struct hwcfg_intersil { - u8 mac_addr[ETH_ALEN]; - u8 cr31_values[14]; - u8 cr58_values[14]; - u8 pidvid[4]; - u8 regulatory_domain; - u8 reserved[1]; -} __packed; - -union at76_hwcfg { - struct hwcfg_intersil i; - struct hwcfg_rfmd r3; - struct hwcfg_r505 r5; -}; - -#define WEP_SMALL_KEY_LEN (40 / 8) -#define WEP_LARGE_KEY_LEN (104 / 8) -#define WEP_KEYS (4) - -struct at76_card_config { - u8 exclude_unencrypted; - u8 promiscuous_mode; - u8 short_retry_limit; - u8 encryption_type; - __le16 rts_threshold; - __le16 fragmentation_threshold; /* 256..2346 */ - u8 basic_rate_set[4]; - u8 auto_rate_fallback; /* 0,1 */ - u8 channel; - u8 privacy_invoked; - u8 wep_default_key_id; /* 0..3 */ - u8 current_ssid[32]; - u8 wep_default_key_value[4][WEP_LARGE_KEY_LEN]; - u8 ssid_len; - u8 short_preamble; - __le16 beacon_period; -} __packed; - -struct at76_command { - u8 cmd; - u8 reserved; - __le16 size; - u8 data[0]; -} __packed; - -/* Length of Atmel-specific Rx header before 802.11 frame */ -#define AT76_RX_HDRLEN offsetof(struct at76_rx_buffer, packet) - -struct at76_rx_buffer { - __le16 wlength; - u8 rx_rate; - u8 newbss; - u8 fragmentation; - u8 rssi; - u8 link_quality; - u8 noise_level; - __le32 rx_time; - u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; -} __packed; - -/* Length of Atmel-specific Tx header before 802.11 frame */ -#define AT76_TX_HDRLEN offsetof(struct at76_tx_buffer, packet) - -struct at76_tx_buffer { - __le16 wlength; - u8 tx_rate; - u8 padding; - u8 reserved[4]; - u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; -} __packed; - -/* defines for scan_type below */ -#define SCAN_TYPE_ACTIVE 0 -#define SCAN_TYPE_PASSIVE 1 - -struct at76_req_scan { - u8 bssid[ETH_ALEN]; - u8 essid[32]; - u8 scan_type; - u8 channel; - __le16 probe_delay; - __le16 min_channel_time; - __le16 max_channel_time; - u8 essid_size; - u8 international_scan; -} __packed; - -struct at76_req_ibss { - u8 bssid[ETH_ALEN]; - u8 essid[32]; - u8 bss_type; - u8 channel; - u8 essid_size; - u8 reserved[3]; -} __packed; - -struct at76_req_join { - u8 bssid[ETH_ALEN]; - u8 essid[32]; - u8 bss_type; - u8 channel; - __le16 timeout; - u8 essid_size; - u8 reserved; -} __packed; - -struct mib_local { - u16 reserved0; - u8 beacon_enable; - u8 txautorate_fallback; - u8 reserved1; - u8 ssid_size; - u8 promiscuous_mode; - u16 reserved2; - u8 preamble_type; - u16 reserved3; -} __packed; - -struct mib_mac_addr { - u8 mac_addr[ETH_ALEN]; - u8 res[2]; /* ??? */ - u8 group_addr[4][ETH_ALEN]; - u8 group_addr_status[4]; -} __packed; - -struct mib_mac { - __le32 max_tx_msdu_lifetime; - __le32 max_rx_lifetime; - __le16 frag_threshold; - __le16 rts_threshold; - __le16 cwmin; - __le16 cwmax; - u8 short_retry_time; - u8 long_retry_time; - u8 scan_type; /* active or passive */ - u8 scan_channel; - __le16 probe_delay; /* delay before ProbeReq in active scan, RO */ - __le16 min_channel_time; - __le16 max_channel_time; - __le16 listen_interval; - u8 desired_ssid[32]; - u8 desired_bssid[ETH_ALEN]; - u8 desired_bsstype; /* ad-hoc or infrastructure */ - u8 reserved2; -} __packed; - -struct mib_mac_mgmt { - __le16 beacon_period; - __le16 CFP_max_duration; - __le16 medium_occupancy_limit; - __le16 station_id; /* assoc id */ - __le16 ATIM_window; - u8 CFP_mode; - u8 privacy_option_implemented; - u8 DTIM_period; - u8 CFP_period; - u8 current_bssid[ETH_ALEN]; - u8 current_essid[32]; - u8 current_bss_type; - u8 power_mgmt_mode; - /* rfmd and 505 */ - u8 ibss_change; - u8 res; - u8 multi_domain_capability_implemented; - u8 multi_domain_capability_enabled; - u8 country_string[IEEE80211_COUNTRY_STRING_LEN]; - u8 reserved[3]; -} __packed; - -struct mib_mac_wep { - u8 privacy_invoked; /* 0 disable encr., 1 enable encr */ - u8 wep_default_key_id; - u8 wep_key_mapping_len; - u8 exclude_unencrypted; - __le32 wep_icv_error_count; - __le32 wep_excluded_count; - u8 wep_default_keyvalue[WEP_KEYS][WEP_LARGE_KEY_LEN]; - u8 encryption_level; /* 1 for 40bit, 2 for 104bit encryption */ -} __packed; - -struct mib_phy { - __le32 ed_threshold; - - __le16 slot_time; - __le16 sifs_time; - __le16 preamble_length; - __le16 plcp_header_length; - __le16 mpdu_max_length; - __le16 cca_mode_supported; - - u8 operation_rate_set[4]; - u8 channel_id; - u8 current_cca_mode; - u8 phy_type; - u8 current_reg_domain; -} __packed; - -struct mib_fw_version { - u8 major; - u8 minor; - u8 patch; - u8 build; -} __packed; - -struct mib_mdomain { - u8 tx_powerlevel[14]; - u8 channel_list[14]; /* 0 for invalid channels */ -} __packed; - -struct set_mib_buffer { - u8 type; - u8 size; - u8 index; - u8 reserved; - union { - u8 byte; - __le16 word; - u8 addr[ETH_ALEN]; - struct mib_mac_wep wep_mib; - } data; -} __packed; - -struct at76_fw_header { - __le32 crc; /* CRC32 of the whole image */ - __le32 board_type; /* firmware compatibility code */ - u8 build; /* firmware build number */ - u8 patch; /* firmware patch level */ - u8 minor; /* firmware minor version */ - u8 major; /* firmware major version */ - __le32 str_offset; /* offset of the copyright string */ - __le32 int_fw_offset; /* internal firmware image offset */ - __le32 int_fw_len; /* internal firmware image length */ - __le32 ext_fw_offset; /* external firmware image offset */ - __le32 ext_fw_len; /* external firmware image length */ -} __packed; - -/* a description of a regulatory domain and the allowed channels */ -struct reg_domain { - u16 code; - char const *name; - u32 channel_map; /* if bit N is set, channel (N+1) is allowed */ -}; - -/* Data for one loaded firmware file */ -struct fwentry { - const char *const fwname; - const struct firmware *fw; - int extfw_size; - int intfw_size; - /* pointer to loaded firmware, no need to free */ - u8 *extfw; /* external firmware, extfw_size bytes long */ - u8 *intfw; /* internal firmware, intfw_size bytes long */ - enum board_type board_type; /* board type */ - struct mib_fw_version fw_version; - int loaded; /* Loaded and parsed successfully */ -}; - -struct at76_priv { - struct usb_device *udev; /* USB device pointer */ - - struct sk_buff *rx_skb; /* skbuff for receiving data */ - struct sk_buff *tx_skb; /* skbuff for transmitting data */ - void *bulk_out_buffer; /* buffer for sending data */ - - struct urb *tx_urb; /* URB for sending data */ - struct urb *rx_urb; /* URB for receiving data */ - - unsigned int tx_pipe; /* bulk out pipe */ - unsigned int rx_pipe; /* bulk in pipe */ - - struct mutex mtx; /* locks this structure */ - - /* work queues */ - struct work_struct work_set_promisc; - struct work_struct work_submit_rx; - struct work_struct work_join_bssid; - struct delayed_work dwork_hw_scan; - - struct tasklet_struct rx_tasklet; - - /* the WEP stuff */ - int wep_enabled; /* 1 if WEP is enabled */ - int wep_key_id; /* key id to be used */ - u8 wep_keys[WEP_KEYS][WEP_LARGE_KEY_LEN]; /* WEP keys */ - u8 wep_keys_len[WEP_KEYS]; /* length of WEP keys */ - - int channel; - int iw_mode; - u8 bssid[ETH_ALEN]; - u8 essid[IW_ESSID_MAX_SIZE]; - int essid_size; - int radio_on; - int promisc; - - int preamble_type; /* 0 - long, 1 - short, 2 - auto */ - int auth_mode; /* authentication type: 0 open, 1 shared key */ - int txrate; /* 0,1,2,3 = 1,2,5.5,11 Mbps, 4 is auto */ - int frag_threshold; /* threshold for fragmentation of tx packets */ - int rts_threshold; /* threshold for RTS mechanism */ - int short_retry_limit; - - int scan_min_time; /* scan min channel time */ - int scan_max_time; /* scan max channel time */ - int scan_mode; /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */ - int scan_need_any; /* if set, need to scan for any ESSID */ - bool scanning; /* if set, the scan is running */ - - u16 assoc_id; /* current association ID, if associated */ - - u8 pm_mode; /* power management mode */ - u32 pm_period; /* power management period in microseconds */ - - struct reg_domain const *domain; /* reg domain description */ - - /* These fields contain HW config provided by the device (not all of - * these fields are used by all board types) */ - u8 mac_addr[ETH_ALEN]; - u8 regulatory_domain; - - struct at76_card_config card_config; - - enum board_type board_type; - struct mib_fw_version fw_version; - - unsigned int device_unplugged:1; - unsigned int netdev_registered:1; - struct set_mib_buffer mib_buf; /* global buffer for set_mib calls */ - - int beacon_period; /* period of mgmt beacons, Kus */ - - struct ieee80211_hw *hw; - int mac80211_registered; -}; - -#define AT76_SUPPORTED_FILTERS 0 - -#define SCAN_POLL_INTERVAL (HZ / 4) - -#define CMD_COMPLETION_TIMEOUT (5 * HZ) - -#define DEF_RTS_THRESHOLD 1536 -#define DEF_FRAG_THRESHOLD 1536 -#define DEF_SHORT_RETRY_LIMIT 8 -#define DEF_CHANNEL 10 -#define DEF_SCAN_MIN_TIME 10 -#define DEF_SCAN_MAX_TIME 120 - -/* the max padding size for tx in bytes (see calc_padding) */ -#define MAX_PADDING_SIZE 53 - -#endif /* _AT76_USB_H */ diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c deleted file mode 100644 index 6a1f03c271c1..000000000000 --- a/drivers/net/wireless/atmel.c +++ /dev/null @@ -1,4549 +0,0 @@ -/*** -*- linux-c -*- ********************************************************** - - Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. - - Copyright 2000-2001 ATMEL Corporation. - Copyright 2003-2004 Simon Kelley. - - This code was developed from version 2.1.1 of the Atmel drivers, - released by Atmel corp. under the GPL in December 2002. It also - includes code from the Linux aironet drivers (C) Benjamin Reed, - and the Linux PCMCIA package, (C) David Hinds and the Linux wireless - extensions, (C) Jean Tourrilhes. - - The firmware module for reading the MAC address of the card comes from - net.russotto.AtmelMACFW, written by Matthew T. Russotto and copyright - by him. net.russotto.AtmelMACFW is used under the GPL license version 2. - This file contains the module in binary form and, under the terms - of the GPL, in source form. The source is located at the end of the file. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This software is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, see - . - - For all queries about this code, please contact the current author, - Simon Kelley and not Atmel Corporation. - - Credit is due to HP UK and Cambridge Online Systems Ltd for supplying - hardware used during development of this driver. - -******************************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "atmel.h" - -#define DRIVER_MAJOR 0 -#define DRIVER_MINOR 98 - -MODULE_AUTHOR("Simon Kelley"); -MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("Atmel at76c50x wireless cards"); - -/* The name of the firmware file to be loaded - over-rides any automatic selection */ -static char *firmware = NULL; -module_param(firmware, charp, 0); - -/* table of firmware file names */ -static struct { - AtmelFWType fw_type; - const char *fw_file; - const char *fw_file_ext; -} fw_table[] = { - { ATMEL_FW_TYPE_502, "atmel_at76c502", "bin" }, - { ATMEL_FW_TYPE_502D, "atmel_at76c502d", "bin" }, - { ATMEL_FW_TYPE_502E, "atmel_at76c502e", "bin" }, - { ATMEL_FW_TYPE_502_3COM, "atmel_at76c502_3com", "bin" }, - { ATMEL_FW_TYPE_504, "atmel_at76c504", "bin" }, - { ATMEL_FW_TYPE_504_2958, "atmel_at76c504_2958", "bin" }, - { ATMEL_FW_TYPE_504A_2958, "atmel_at76c504a_2958", "bin" }, - { ATMEL_FW_TYPE_506, "atmel_at76c506", "bin" }, - { ATMEL_FW_TYPE_NONE, NULL, NULL } -}; -MODULE_FIRMWARE("atmel_at76c502-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c502.bin"); -MODULE_FIRMWARE("atmel_at76c502d-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c502d.bin"); -MODULE_FIRMWARE("atmel_at76c502e-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c502e.bin"); -MODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c502_3com.bin"); -MODULE_FIRMWARE("atmel_at76c504-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c504.bin"); -MODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c504_2958.bin"); -MODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c504a_2958.bin"); -MODULE_FIRMWARE("atmel_at76c506-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c506.bin"); - -#define MAX_SSID_LENGTH 32 -#define MGMT_JIFFIES (256 * HZ / 100) - -#define MAX_BSS_ENTRIES 64 - -/* registers */ -#define GCR 0x00 /* (SIR0) General Configuration Register */ -#define BSR 0x02 /* (SIR1) Bank Switching Select Register */ -#define AR 0x04 -#define DR 0x08 -#define MR1 0x12 /* Mirror Register 1 */ -#define MR2 0x14 /* Mirror Register 2 */ -#define MR3 0x16 /* Mirror Register 3 */ -#define MR4 0x18 /* Mirror Register 4 */ - -#define GPR1 0x0c -#define GPR2 0x0e -#define GPR3 0x10 -/* - * Constants for the GCR register. - */ -#define GCR_REMAP 0x0400 /* Remap internal SRAM to 0 */ -#define GCR_SWRES 0x0080 /* BIU reset (ARM and PAI are NOT reset) */ -#define GCR_CORES 0x0060 /* Core Reset (ARM and PAI are reset) */ -#define GCR_ENINT 0x0002 /* Enable Interrupts */ -#define GCR_ACKINT 0x0008 /* Acknowledge Interrupts */ - -#define BSS_SRAM 0x0200 /* AMBA module selection --> SRAM */ -#define BSS_IRAM 0x0100 /* AMBA module selection --> IRAM */ -/* - *Constants for the MR registers. - */ -#define MAC_INIT_COMPLETE 0x0001 /* MAC init has been completed */ -#define MAC_BOOT_COMPLETE 0x0010 /* MAC boot has been completed */ -#define MAC_INIT_OK 0x0002 /* MAC boot has been completed */ - -#define MIB_MAX_DATA_BYTES 212 -#define MIB_HEADER_SIZE 4 /* first four fields */ - -struct get_set_mib { - u8 type; - u8 size; - u8 index; - u8 reserved; - u8 data[MIB_MAX_DATA_BYTES]; -}; - -struct rx_desc { - u32 Next; - u16 MsduPos; - u16 MsduSize; - - u8 State; - u8 Status; - u8 Rate; - u8 Rssi; - u8 LinkQuality; - u8 PreambleType; - u16 Duration; - u32 RxTime; -}; - -#define RX_DESC_FLAG_VALID 0x80 -#define RX_DESC_FLAG_CONSUMED 0x40 -#define RX_DESC_FLAG_IDLE 0x00 - -#define RX_STATUS_SUCCESS 0x00 - -#define RX_DESC_MSDU_POS_OFFSET 4 -#define RX_DESC_MSDU_SIZE_OFFSET 6 -#define RX_DESC_FLAGS_OFFSET 8 -#define RX_DESC_STATUS_OFFSET 9 -#define RX_DESC_RSSI_OFFSET 11 -#define RX_DESC_LINK_QUALITY_OFFSET 12 -#define RX_DESC_PREAMBLE_TYPE_OFFSET 13 -#define RX_DESC_DURATION_OFFSET 14 -#define RX_DESC_RX_TIME_OFFSET 16 - -struct tx_desc { - u32 NextDescriptor; - u16 TxStartOfFrame; - u16 TxLength; - - u8 TxState; - u8 TxStatus; - u8 RetryCount; - - u8 TxRate; - - u8 KeyIndex; - u8 ChiperType; - u8 ChipreLength; - u8 Reserved1; - - u8 Reserved; - u8 PacketType; - u16 HostTxLength; -}; - -#define TX_DESC_NEXT_OFFSET 0 -#define TX_DESC_POS_OFFSET 4 -#define TX_DESC_SIZE_OFFSET 6 -#define TX_DESC_FLAGS_OFFSET 8 -#define TX_DESC_STATUS_OFFSET 9 -#define TX_DESC_RETRY_OFFSET 10 -#define TX_DESC_RATE_OFFSET 11 -#define TX_DESC_KEY_INDEX_OFFSET 12 -#define TX_DESC_CIPHER_TYPE_OFFSET 13 -#define TX_DESC_CIPHER_LENGTH_OFFSET 14 -#define TX_DESC_PACKET_TYPE_OFFSET 17 -#define TX_DESC_HOST_LENGTH_OFFSET 18 - -/* - * Host-MAC interface - */ - -#define TX_STATUS_SUCCESS 0x00 - -#define TX_FIRM_OWN 0x80 -#define TX_DONE 0x40 - -#define TX_ERROR 0x01 - -#define TX_PACKET_TYPE_DATA 0x01 -#define TX_PACKET_TYPE_MGMT 0x02 - -#define ISR_EMPTY 0x00 /* no bits set in ISR */ -#define ISR_TxCOMPLETE 0x01 /* packet transmitted */ -#define ISR_RxCOMPLETE 0x02 /* packet received */ -#define ISR_RxFRAMELOST 0x04 /* Rx Frame lost */ -#define ISR_FATAL_ERROR 0x08 /* Fatal error */ -#define ISR_COMMAND_COMPLETE 0x10 /* command completed */ -#define ISR_OUT_OF_RANGE 0x20 /* command completed */ -#define ISR_IBSS_MERGE 0x40 /* (4.1.2.30): IBSS merge */ -#define ISR_GENERIC_IRQ 0x80 - -#define Local_Mib_Type 0x01 -#define Mac_Address_Mib_Type 0x02 -#define Mac_Mib_Type 0x03 -#define Statistics_Mib_Type 0x04 -#define Mac_Mgmt_Mib_Type 0x05 -#define Mac_Wep_Mib_Type 0x06 -#define Phy_Mib_Type 0x07 -#define Multi_Domain_MIB 0x08 - -#define MAC_MGMT_MIB_CUR_BSSID_POS 14 -#define MAC_MIB_FRAG_THRESHOLD_POS 8 -#define MAC_MIB_RTS_THRESHOLD_POS 10 -#define MAC_MIB_SHORT_RETRY_POS 16 -#define MAC_MIB_LONG_RETRY_POS 17 -#define MAC_MIB_SHORT_RETRY_LIMIT_POS 16 -#define MAC_MGMT_MIB_BEACON_PER_POS 0 -#define MAC_MGMT_MIB_STATION_ID_POS 6 -#define MAC_MGMT_MIB_CUR_PRIVACY_POS 11 -#define MAC_MGMT_MIB_CUR_BSSID_POS 14 -#define MAC_MGMT_MIB_PS_MODE_POS 53 -#define MAC_MGMT_MIB_LISTEN_INTERVAL_POS 54 -#define MAC_MGMT_MIB_MULTI_DOMAIN_IMPLEMENTED 56 -#define MAC_MGMT_MIB_MULTI_DOMAIN_ENABLED 57 -#define PHY_MIB_CHANNEL_POS 14 -#define PHY_MIB_RATE_SET_POS 20 -#define PHY_MIB_REG_DOMAIN_POS 26 -#define LOCAL_MIB_AUTO_TX_RATE_POS 3 -#define LOCAL_MIB_SSID_SIZE 5 -#define LOCAL_MIB_TX_PROMISCUOUS_POS 6 -#define LOCAL_MIB_TX_MGMT_RATE_POS 7 -#define LOCAL_MIB_TX_CONTROL_RATE_POS 8 -#define LOCAL_MIB_PREAMBLE_TYPE 9 -#define MAC_ADDR_MIB_MAC_ADDR_POS 0 - -#define CMD_Set_MIB_Vars 0x01 -#define CMD_Get_MIB_Vars 0x02 -#define CMD_Scan 0x03 -#define CMD_Join 0x04 -#define CMD_Start 0x05 -#define CMD_EnableRadio 0x06 -#define CMD_DisableRadio 0x07 -#define CMD_SiteSurvey 0x0B - -#define CMD_STATUS_IDLE 0x00 -#define CMD_STATUS_COMPLETE 0x01 -#define CMD_STATUS_UNKNOWN 0x02 -#define CMD_STATUS_INVALID_PARAMETER 0x03 -#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 -#define CMD_STATUS_TIME_OUT 0x07 -#define CMD_STATUS_IN_PROGRESS 0x08 -#define CMD_STATUS_REJECTED_RADIO_OFF 0x09 -#define CMD_STATUS_HOST_ERROR 0xFF -#define CMD_STATUS_BUSY 0xFE - -#define CMD_BLOCK_COMMAND_OFFSET 0 -#define CMD_BLOCK_STATUS_OFFSET 1 -#define CMD_BLOCK_PARAMETERS_OFFSET 4 - -#define SCAN_OPTIONS_SITE_SURVEY 0x80 - -#define MGMT_FRAME_BODY_OFFSET 24 -#define MAX_AUTHENTICATION_RETRIES 3 -#define MAX_ASSOCIATION_RETRIES 3 - -#define AUTHENTICATION_RESPONSE_TIME_OUT 1000 - -#define MAX_WIRELESS_BODY 2316 /* mtu is 2312, CRC is 4 */ -#define LOOP_RETRY_LIMIT 500000 - -#define ACTIVE_MODE 1 -#define PS_MODE 2 - -#define MAX_ENCRYPTION_KEYS 4 -#define MAX_ENCRYPTION_KEY_SIZE 40 - -/* - * 802.11 related definitions - */ - -/* - * Regulatory Domains - */ - -#define REG_DOMAIN_FCC 0x10 /* Channels 1-11 USA */ -#define REG_DOMAIN_DOC 0x20 /* Channel 1-11 Canada */ -#define REG_DOMAIN_ETSI 0x30 /* Channel 1-13 Europe (ex Spain/France) */ -#define REG_DOMAIN_SPAIN 0x31 /* Channel 10-11 Spain */ -#define REG_DOMAIN_FRANCE 0x32 /* Channel 10-13 France */ -#define REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */ -#define REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan(MKK1) */ -#define REG_DOMAIN_ISRAEL 0x50 /* Channel 3-9 ISRAEL */ - -#define BSS_TYPE_AD_HOC 1 -#define BSS_TYPE_INFRASTRUCTURE 2 - -#define SCAN_TYPE_ACTIVE 0 -#define SCAN_TYPE_PASSIVE 1 - -#define LONG_PREAMBLE 0 -#define SHORT_PREAMBLE 1 -#define AUTO_PREAMBLE 2 - -#define DATA_FRAME_WS_HEADER_SIZE 30 - -/* promiscuous mode control */ -#define PROM_MODE_OFF 0x0 -#define PROM_MODE_UNKNOWN 0x1 -#define PROM_MODE_CRC_FAILED 0x2 -#define PROM_MODE_DUPLICATED 0x4 -#define PROM_MODE_MGMT 0x8 -#define PROM_MODE_CTRL 0x10 -#define PROM_MODE_BAD_PROTOCOL 0x20 - -#define IFACE_INT_STATUS_OFFSET 0 -#define IFACE_INT_MASK_OFFSET 1 -#define IFACE_LOCKOUT_HOST_OFFSET 2 -#define IFACE_LOCKOUT_MAC_OFFSET 3 -#define IFACE_FUNC_CTRL_OFFSET 28 -#define IFACE_MAC_STAT_OFFSET 30 -#define IFACE_GENERIC_INT_TYPE_OFFSET 32 - -#define CIPHER_SUITE_NONE 0 -#define CIPHER_SUITE_WEP_64 1 -#define CIPHER_SUITE_TKIP 2 -#define CIPHER_SUITE_AES 3 -#define CIPHER_SUITE_CCX 4 -#define CIPHER_SUITE_WEP_128 5 - -/* - * IFACE MACROS & definitions - */ - -/* - * FuncCtrl field: - */ -#define FUNC_CTRL_TxENABLE 0x10 -#define FUNC_CTRL_RxENABLE 0x20 -#define FUNC_CTRL_INIT_COMPLETE 0x01 - -/* A stub firmware image which reads the MAC address from NVRAM on the card. - For copyright information and source see the end of this file. */ -static u8 mac_reader[] = { - 0x06, 0x00, 0x00, 0xea, 0x04, 0x00, 0x00, 0xea, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xea, - 0x01, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, - 0xd3, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x21, 0xe1, 0x0e, 0x04, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, - 0x81, 0x11, 0xa0, 0xe1, 0x00, 0x10, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x1c, 0x10, 0x90, 0xe5, - 0x10, 0x10, 0xc1, 0xe3, 0x1c, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 0x08, 0x10, 0x80, 0xe5, - 0x02, 0x03, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0xb0, 0x10, 0xc0, 0xe1, 0xb4, 0x10, 0xc0, 0xe1, - 0xb8, 0x10, 0xc0, 0xe1, 0xbc, 0x10, 0xc0, 0xe1, 0x56, 0xdc, 0xa0, 0xe3, 0x21, 0x00, 0x00, 0xeb, - 0x0a, 0x00, 0xa0, 0xe3, 0x1a, 0x00, 0x00, 0xeb, 0x10, 0x00, 0x00, 0xeb, 0x07, 0x00, 0x00, 0xeb, - 0x02, 0x03, 0xa0, 0xe3, 0x02, 0x14, 0xa0, 0xe3, 0xb4, 0x10, 0xc0, 0xe1, 0x4c, 0x10, 0x9f, 0xe5, - 0xbc, 0x10, 0xc0, 0xe1, 0x10, 0x10, 0xa0, 0xe3, 0xb8, 0x10, 0xc0, 0xe1, 0xfe, 0xff, 0xff, 0xea, - 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe3, 0x02, 0x3c, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, - 0x28, 0x00, 0x9f, 0xe5, 0x37, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, - 0x00, 0x40, 0x2d, 0xe9, 0x12, 0x2e, 0xa0, 0xe3, 0x06, 0x30, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, - 0x02, 0x04, 0xa0, 0xe3, 0x2f, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, - 0x00, 0x02, 0x00, 0x02, 0x80, 0x01, 0x90, 0xe0, 0x01, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x50, 0xe2, - 0xfc, 0xff, 0xff, 0xea, 0x1e, 0xff, 0x2f, 0xe1, 0x80, 0x10, 0xa0, 0xe3, 0xf3, 0x06, 0xa0, 0xe3, - 0x00, 0x10, 0x80, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, - 0x04, 0x10, 0x80, 0xe5, 0x00, 0x10, 0x80, 0xe5, 0x0e, 0x34, 0xa0, 0xe3, 0x1c, 0x10, 0x93, 0xe5, - 0x02, 0x1a, 0x81, 0xe3, 0x1c, 0x10, 0x83, 0xe5, 0x58, 0x11, 0x9f, 0xe5, 0x30, 0x10, 0x80, 0xe5, - 0x54, 0x11, 0x9f, 0xe5, 0x34, 0x10, 0x80, 0xe5, 0x38, 0x10, 0x80, 0xe5, 0x3c, 0x10, 0x80, 0xe5, - 0x10, 0x10, 0x90, 0xe5, 0x08, 0x00, 0x90, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0xf3, 0x16, 0xa0, 0xe3, - 0x08, 0x00, 0x91, 0xe5, 0x05, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 0x10, 0x00, 0x91, 0xe5, - 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0xff, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, - 0x10, 0x00, 0x91, 0xe5, 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, - 0x10, 0x00, 0x91, 0xe5, 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, - 0xff, 0x00, 0x00, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x30, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe1, - 0x03, 0x40, 0xa0, 0xe1, 0xa2, 0x02, 0xa0, 0xe1, 0x08, 0x00, 0x00, 0xe2, 0x03, 0x00, 0x80, 0xe2, - 0xd8, 0x10, 0x9f, 0xe5, 0x00, 0x00, 0xc1, 0xe5, 0x01, 0x20, 0xc1, 0xe5, 0xe2, 0xff, 0xff, 0xeb, - 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x14, 0x00, 0xa0, 0xe3, 0xc4, 0xff, 0xff, 0xeb, - 0x04, 0x20, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1, 0x02, 0x00, 0xa0, 0xe3, 0x01, 0x00, 0x00, 0xeb, - 0x30, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x70, 0x40, 0x2d, 0xe9, 0xf3, 0x46, 0xa0, 0xe3, - 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x08, 0x00, 0x00, 0x9a, 0x8c, 0x50, 0x9f, 0xe5, - 0x03, 0x60, 0xd5, 0xe7, 0x0c, 0x60, 0x84, 0xe5, 0x10, 0x60, 0x94, 0xe5, 0x02, 0x00, 0x16, 0xe3, - 0xfc, 0xff, 0xff, 0x0a, 0x01, 0x30, 0x83, 0xe2, 0x00, 0x00, 0x53, 0xe1, 0xf7, 0xff, 0xff, 0x3a, - 0xff, 0x30, 0xa0, 0xe3, 0x0c, 0x30, 0x84, 0xe5, 0x08, 0x00, 0x94, 0xe5, 0x10, 0x00, 0x94, 0xe5, - 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x94, 0xe5, 0x00, 0x00, 0xa0, 0xe3, - 0x00, 0x00, 0x52, 0xe3, 0x0b, 0x00, 0x00, 0x9a, 0x10, 0x50, 0x94, 0xe5, 0x02, 0x00, 0x15, 0xe3, - 0xfc, 0xff, 0xff, 0x0a, 0x0c, 0x30, 0x84, 0xe5, 0x10, 0x50, 0x94, 0xe5, 0x01, 0x00, 0x15, 0xe3, - 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x50, 0x94, 0xe5, 0x01, 0x50, 0xc1, 0xe4, 0x01, 0x00, 0x80, 0xe2, - 0x02, 0x00, 0x50, 0xe1, 0xf3, 0xff, 0xff, 0x3a, 0xc8, 0x00, 0xa0, 0xe3, 0x98, 0xff, 0xff, 0xeb, - 0x70, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x0c, 0x00, 0x02, 0x01, 0x02, 0x00, 0x02, - 0x00, 0x01, 0x00, 0x02 -}; - -struct atmel_private { - void *card; /* Bus dependent structure varies for PCcard */ - int (*present_callback)(void *); /* And callback which uses it */ - char firmware_id[32]; - AtmelFWType firmware_type; - u8 *firmware; - int firmware_length; - struct timer_list management_timer; - struct net_device *dev; - struct device *sys_dev; - struct iw_statistics wstats; - spinlock_t irqlock, timerlock; /* spinlocks */ - enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type; - enum { - CARD_TYPE_PARALLEL_FLASH, - CARD_TYPE_SPI_FLASH, - CARD_TYPE_EEPROM - } card_type; - int do_rx_crc; /* If we need to CRC incoming packets */ - int probe_crc; /* set if we don't yet know */ - int crc_ok_cnt, crc_ko_cnt; /* counters for probing */ - u16 rx_desc_head; - u16 tx_desc_free, tx_desc_head, tx_desc_tail, tx_desc_previous; - u16 tx_free_mem, tx_buff_head, tx_buff_tail; - - u16 frag_seq, frag_len, frag_no; - u8 frag_source[6]; - - u8 wep_is_on, default_key, exclude_unencrypted, encryption_level; - u8 group_cipher_suite, pairwise_cipher_suite; - u8 wep_keys[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; - int wep_key_len[MAX_ENCRYPTION_KEYS]; - int use_wpa, radio_on_broken; /* firmware dependent stuff. */ - - u16 host_info_base; - struct host_info_struct { - /* NB this is matched to the hardware, don't change. */ - u8 volatile int_status; - u8 volatile int_mask; - u8 volatile lockout_host; - u8 volatile lockout_mac; - - u16 tx_buff_pos; - u16 tx_buff_size; - u16 tx_desc_pos; - u16 tx_desc_count; - - u16 rx_buff_pos; - u16 rx_buff_size; - u16 rx_desc_pos; - u16 rx_desc_count; - - u16 build_version; - u16 command_pos; - - u16 major_version; - u16 minor_version; - - u16 func_ctrl; - u16 mac_status; - u16 generic_IRQ_type; - u8 reserved[2]; - } host_info; - - enum { - STATION_STATE_SCANNING, - STATION_STATE_JOINNING, - STATION_STATE_AUTHENTICATING, - STATION_STATE_ASSOCIATING, - STATION_STATE_READY, - STATION_STATE_REASSOCIATING, - STATION_STATE_DOWN, - STATION_STATE_MGMT_ERROR - } station_state; - - int operating_mode, power_mode; - time_t last_qual; - int beacons_this_sec; - int channel; - int reg_domain, config_reg_domain; - int tx_rate; - int auto_tx_rate; - int rts_threshold; - int frag_threshold; - int long_retry, short_retry; - int preamble; - int default_beacon_period, beacon_period, listen_interval; - int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum; - int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt; - enum { - SITE_SURVEY_IDLE, - SITE_SURVEY_IN_PROGRESS, - SITE_SURVEY_COMPLETED - } site_survey_state; - unsigned long last_survey; - - int station_was_associated, station_is_associated; - int fast_scan; - - struct bss_info { - int channel; - int SSIDsize; - int RSSI; - int UsingWEP; - int preamble; - int beacon_period; - int BSStype; - u8 BSSID[6]; - u8 SSID[MAX_SSID_LENGTH]; - } BSSinfo[MAX_BSS_ENTRIES]; - int BSS_list_entries, current_BSS; - int connect_to_any_BSS; - int SSID_size, new_SSID_size; - u8 CurrentBSSID[6], BSSID[6]; - u8 SSID[MAX_SSID_LENGTH], new_SSID[MAX_SSID_LENGTH]; - u64 last_beacon_timestamp; - u8 rx_buf[MAX_WIRELESS_BODY]; -}; - -static u8 atmel_basic_rates[4] = {0x82, 0x84, 0x0b, 0x16}; - -static const struct { - int reg_domain; - int min, max; - char *name; -} channel_table[] = { { REG_DOMAIN_FCC, 1, 11, "USA" }, - { REG_DOMAIN_DOC, 1, 11, "Canada" }, - { REG_DOMAIN_ETSI, 1, 13, "Europe" }, - { REG_DOMAIN_SPAIN, 10, 11, "Spain" }, - { REG_DOMAIN_FRANCE, 10, 13, "France" }, - { REG_DOMAIN_MKK, 14, 14, "MKK" }, - { REG_DOMAIN_MKK1, 1, 14, "MKK1" }, - { REG_DOMAIN_ISRAEL, 3, 9, "Israel"} }; - -static void build_wpa_mib(struct atmel_private *priv); -static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void atmel_copy_to_card(struct net_device *dev, u16 dest, - const unsigned char *src, u16 len); -static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, - u16 src, u16 len); -static void atmel_set_gcr(struct net_device *dev, u16 mask); -static void atmel_clear_gcr(struct net_device *dev, u16 mask); -static int atmel_lock_mac(struct atmel_private *priv); -static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data); -static void atmel_command_irq(struct atmel_private *priv); -static int atmel_validate_channel(struct atmel_private *priv, int channel); -static void atmel_management_frame(struct atmel_private *priv, - struct ieee80211_hdr *header, - u16 frame_len, u8 rssi); -static void atmel_management_timer(u_long a); -static void atmel_send_command(struct atmel_private *priv, int command, - void *cmd, int cmd_size); -static int atmel_send_command_wait(struct atmel_private *priv, int command, - void *cmd, int cmd_size); -static void atmel_transmit_management_frame(struct atmel_private *priv, - struct ieee80211_hdr *header, - u8 *body, int body_len); - -static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index); -static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, - u8 data); -static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, - u16 data); -static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, - u8 *data, int data_len); -static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, - u8 *data, int data_len); -static void atmel_scan(struct atmel_private *priv, int specific_ssid); -static void atmel_join_bss(struct atmel_private *priv, int bss_index); -static void atmel_smooth_qual(struct atmel_private *priv); -static void atmel_writeAR(struct net_device *dev, u16 data); -static int probe_atmel_card(struct net_device *dev); -static int reset_atmel_card(struct net_device *dev); -static void atmel_enter_state(struct atmel_private *priv, int new_state); -int atmel_open (struct net_device *dev); - -static inline u16 atmel_hi(struct atmel_private *priv, u16 offset) -{ - return priv->host_info_base + offset; -} - -static inline u16 atmel_co(struct atmel_private *priv, u16 offset) -{ - return priv->host_info.command_pos + offset; -} - -static inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc) -{ - return priv->host_info.rx_desc_pos + (sizeof(struct rx_desc) * desc) + offset; -} - -static inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc) -{ - return priv->host_info.tx_desc_pos + (sizeof(struct tx_desc) * desc) + offset; -} - -static inline u8 atmel_read8(struct net_device *dev, u16 offset) -{ - return inb(dev->base_addr + offset); -} - -static inline void atmel_write8(struct net_device *dev, u16 offset, u8 data) -{ - outb(data, dev->base_addr + offset); -} - -static inline u16 atmel_read16(struct net_device *dev, u16 offset) -{ - return inw(dev->base_addr + offset); -} - -static inline void atmel_write16(struct net_device *dev, u16 offset, u16 data) -{ - outw(data, dev->base_addr + offset); -} - -static inline u8 atmel_rmem8(struct atmel_private *priv, u16 pos) -{ - atmel_writeAR(priv->dev, pos); - return atmel_read8(priv->dev, DR); -} - -static inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data) -{ - atmel_writeAR(priv->dev, pos); - atmel_write8(priv->dev, DR, data); -} - -static inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos) -{ - atmel_writeAR(priv->dev, pos); - return atmel_read16(priv->dev, DR); -} - -static inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data) -{ - atmel_writeAR(priv->dev, pos); - atmel_write16(priv->dev, DR, data); -} - -static const struct iw_handler_def atmel_handler_def; - -static void tx_done_irq(struct atmel_private *priv) -{ - int i; - - for (i = 0; - atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE && - i < priv->host_info.tx_desc_count; - i++) { - u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head)); - u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head)); - u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head)); - - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head), 0); - - priv->tx_free_mem += msdu_size; - priv->tx_desc_free++; - - if (priv->tx_buff_head + msdu_size > (priv->host_info.tx_buff_pos + priv->host_info.tx_buff_size)) - priv->tx_buff_head = 0; - else - priv->tx_buff_head += msdu_size; - - if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1)) - priv->tx_desc_head++ ; - else - priv->tx_desc_head = 0; - - if (type == TX_PACKET_TYPE_DATA) { - if (status == TX_STATUS_SUCCESS) - priv->dev->stats.tx_packets++; - else - priv->dev->stats.tx_errors++; - netif_wake_queue(priv->dev); - } - } -} - -static u16 find_tx_buff(struct atmel_private *priv, u16 len) -{ - u16 bottom_free = priv->host_info.tx_buff_size - priv->tx_buff_tail; - - if (priv->tx_desc_free == 3 || priv->tx_free_mem < len) - return 0; - - if (bottom_free >= len) - return priv->host_info.tx_buff_pos + priv->tx_buff_tail; - - if (priv->tx_free_mem - bottom_free >= len) { - priv->tx_buff_tail = 0; - return priv->host_info.tx_buff_pos; - } - - return 0; -} - -static void tx_update_descriptor(struct atmel_private *priv, int is_bcast, - u16 len, u16 buff, u8 type) -{ - atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, priv->tx_desc_tail), buff); - atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_tail), len); - if (!priv->use_wpa) - atmel_wmem16(priv, atmel_tx(priv, TX_DESC_HOST_LENGTH_OFFSET, priv->tx_desc_tail), len); - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_tail), type); - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RATE_OFFSET, priv->tx_desc_tail), priv->tx_rate); - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RETRY_OFFSET, priv->tx_desc_tail), 0); - if (priv->use_wpa) { - int cipher_type, cipher_length; - if (is_bcast) { - cipher_type = priv->group_cipher_suite; - if (cipher_type == CIPHER_SUITE_WEP_64 || - cipher_type == CIPHER_SUITE_WEP_128) - cipher_length = 8; - else if (cipher_type == CIPHER_SUITE_TKIP) - cipher_length = 12; - else if (priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_64 || - priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_128) { - cipher_type = priv->pairwise_cipher_suite; - cipher_length = 8; - } else { - cipher_type = CIPHER_SUITE_NONE; - cipher_length = 0; - } - } else { - cipher_type = priv->pairwise_cipher_suite; - if (cipher_type == CIPHER_SUITE_WEP_64 || - cipher_type == CIPHER_SUITE_WEP_128) - cipher_length = 8; - else if (cipher_type == CIPHER_SUITE_TKIP) - cipher_length = 12; - else if (priv->group_cipher_suite == CIPHER_SUITE_WEP_64 || - priv->group_cipher_suite == CIPHER_SUITE_WEP_128) { - cipher_type = priv->group_cipher_suite; - cipher_length = 8; - } else { - cipher_type = CIPHER_SUITE_NONE; - cipher_length = 0; - } - } - - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail), - cipher_type); - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_LENGTH_OFFSET, priv->tx_desc_tail), - cipher_length); - } - atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_tail), 0x80000000L); - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_tail), TX_FIRM_OWN); - if (priv->tx_desc_previous != priv->tx_desc_tail) - atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_previous), 0); - priv->tx_desc_previous = priv->tx_desc_tail; - if (priv->tx_desc_tail < (priv->host_info.tx_desc_count - 1)) - priv->tx_desc_tail++; - else - priv->tx_desc_tail = 0; - priv->tx_desc_free--; - priv->tx_free_mem -= len; -} - -static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) -{ - static const u8 SNAP_RFC1024[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; - struct atmel_private *priv = netdev_priv(dev); - struct ieee80211_hdr header; - unsigned long flags; - u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; - - if (priv->card && priv->present_callback && - !(*priv->present_callback)(priv->card)) { - dev->stats.tx_errors++; - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - if (priv->station_state != STATION_STATE_READY) { - dev->stats.tx_errors++; - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - /* first ensure the timer func cannot run */ - spin_lock_bh(&priv->timerlock); - /* then stop the hardware ISR */ - spin_lock_irqsave(&priv->irqlock, flags); - /* nb doing the above in the opposite order will deadlock */ - - /* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the - 12 first bytes (containing DA/SA) and put them in the appropriate - fields of the Wireless Header. Thus the packet length is then the - initial + 18 (+30-12) */ - - if (!(buff = find_tx_buff(priv, len + 18))) { - dev->stats.tx_dropped++; - spin_unlock_irqrestore(&priv->irqlock, flags); - spin_unlock_bh(&priv->timerlock); - netif_stop_queue(dev); - return NETDEV_TX_BUSY; - } - - frame_ctl = IEEE80211_FTYPE_DATA; - header.duration_id = 0; - header.seq_ctrl = 0; - if (priv->wep_is_on) - frame_ctl |= IEEE80211_FCTL_PROTECTED; - if (priv->operating_mode == IW_MODE_ADHOC) { - skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN); - memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); - memcpy(&header.addr3, priv->BSSID, ETH_ALEN); - } else { - frame_ctl |= IEEE80211_FCTL_TODS; - memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN); - memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); - skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN); - } - - if (priv->use_wpa) - memcpy(&header.addr4, SNAP_RFC1024, ETH_ALEN); - - header.frame_control = cpu_to_le16(frame_ctl); - /* Copy the wireless header into the card */ - atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE); - /* Copy the packet sans its 802.3 header addresses which have been replaced */ - atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12); - priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE; - - /* low bit of first byte of destination tells us if broadcast */ - tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA); - dev->stats.tx_bytes += len; - - spin_unlock_irqrestore(&priv->irqlock, flags); - spin_unlock_bh(&priv->timerlock); - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static void atmel_transmit_management_frame(struct atmel_private *priv, - struct ieee80211_hdr *header, - u8 *body, int body_len) -{ - u16 buff; - int len = MGMT_FRAME_BODY_OFFSET + body_len; - - if (!(buff = find_tx_buff(priv, len))) - return; - - atmel_copy_to_card(priv->dev, buff, (u8 *)header, MGMT_FRAME_BODY_OFFSET); - atmel_copy_to_card(priv->dev, buff + MGMT_FRAME_BODY_OFFSET, body, body_len); - priv->tx_buff_tail += len; - tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT); -} - -static void fast_rx_path(struct atmel_private *priv, - struct ieee80211_hdr *header, - u16 msdu_size, u16 rx_packet_loc, u32 crc) -{ - /* fast path: unfragmented packet copy directly into skbuf */ - u8 mac4[6]; - struct sk_buff *skb; - unsigned char *skbp; - - /* get the final, mac 4 header field, this tells us encapsulation */ - atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6); - msdu_size -= 6; - - if (priv->do_rx_crc) { - crc = crc32_le(crc, mac4, 6); - msdu_size -= 4; - } - - if (!(skb = dev_alloc_skb(msdu_size + 14))) { - priv->dev->stats.rx_dropped++; - return; - } - - skb_reserve(skb, 2); - skbp = skb_put(skb, msdu_size + 12); - atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size); - - if (priv->do_rx_crc) { - u32 netcrc; - crc = crc32_le(crc, skbp + 12, msdu_size); - atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + 30 + msdu_size, 4); - if ((crc ^ 0xffffffff) != netcrc) { - priv->dev->stats.rx_crc_errors++; - dev_kfree_skb(skb); - return; - } - } - - memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */ - if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) - memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN); - else - memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */ - - skb->protocol = eth_type_trans(skb, priv->dev); - skb->ip_summed = CHECKSUM_NONE; - netif_rx(skb); - priv->dev->stats.rx_bytes += 12 + msdu_size; - priv->dev->stats.rx_packets++; -} - -/* Test to see if the packet in card memory at packet_loc has a valid CRC - It doesn't matter that this is slow: it is only used to proble the first few - packets. */ -static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size) -{ - int i = msdu_size - 4; - u32 netcrc, crc = 0xffffffff; - - if (msdu_size < 4) - return 0; - - atmel_copy_to_host(priv->dev, (void *)&netcrc, packet_loc + i, 4); - - atmel_writeAR(priv->dev, packet_loc); - while (i--) { - u8 octet = atmel_read8(priv->dev, DR); - crc = crc32_le(crc, &octet, 1); - } - - return (crc ^ 0xffffffff) == netcrc; -} - -static void frag_rx_path(struct atmel_private *priv, - struct ieee80211_hdr *header, - u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, - u8 frag_no, int more_frags) -{ - u8 mac4[ETH_ALEN]; - u8 source[ETH_ALEN]; - struct sk_buff *skb; - - if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) - memcpy(source, header->addr3, ETH_ALEN); - else - memcpy(source, header->addr2, ETH_ALEN); - - rx_packet_loc += 24; /* skip header */ - - if (priv->do_rx_crc) - msdu_size -= 4; - - if (frag_no == 0) { /* first fragment */ - atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN); - msdu_size -= ETH_ALEN; - rx_packet_loc += ETH_ALEN; - - if (priv->do_rx_crc) - crc = crc32_le(crc, mac4, 6); - - priv->frag_seq = seq_no; - priv->frag_no = 1; - priv->frag_len = msdu_size; - memcpy(priv->frag_source, source, ETH_ALEN); - memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN); - memcpy(priv->rx_buf, header->addr1, ETH_ALEN); - - atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size); - - if (priv->do_rx_crc) { - u32 netcrc; - crc = crc32_le(crc, &priv->rx_buf[12], msdu_size); - atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); - if ((crc ^ 0xffffffff) != netcrc) { - priv->dev->stats.rx_crc_errors++; - eth_broadcast_addr(priv->frag_source); - } - } - - } else if (priv->frag_no == frag_no && - priv->frag_seq == seq_no && - memcmp(priv->frag_source, source, ETH_ALEN) == 0) { - - atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len], - rx_packet_loc, msdu_size); - if (priv->do_rx_crc) { - u32 netcrc; - crc = crc32_le(crc, - &priv->rx_buf[12 + priv->frag_len], - msdu_size); - atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); - if ((crc ^ 0xffffffff) != netcrc) { - priv->dev->stats.rx_crc_errors++; - eth_broadcast_addr(priv->frag_source); - more_frags = 1; /* don't send broken assembly */ - } - } - - priv->frag_len += msdu_size; - priv->frag_no++; - - if (!more_frags) { /* last one */ - eth_broadcast_addr(priv->frag_source); - if (!(skb = dev_alloc_skb(priv->frag_len + 14))) { - priv->dev->stats.rx_dropped++; - } else { - skb_reserve(skb, 2); - memcpy(skb_put(skb, priv->frag_len + 12), - priv->rx_buf, - priv->frag_len + 12); - skb->protocol = eth_type_trans(skb, priv->dev); - skb->ip_summed = CHECKSUM_NONE; - netif_rx(skb); - priv->dev->stats.rx_bytes += priv->frag_len + 12; - priv->dev->stats.rx_packets++; - } - } - } else - priv->wstats.discard.fragment++; -} - -static void rx_done_irq(struct atmel_private *priv) -{ - int i; - struct ieee80211_hdr header; - - for (i = 0; - atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID && - i < priv->host_info.rx_desc_count; - i++) { - - u16 msdu_size, rx_packet_loc, frame_ctl, seq_control; - u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head)); - u32 crc = 0xffffffff; - - if (status != RX_STATUS_SUCCESS) { - if (status == 0xc1) /* determined by experiment */ - priv->wstats.discard.nwid++; - else - priv->dev->stats.rx_errors++; - goto next; - } - - msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head)); - rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head)); - - if (msdu_size < 30) { - priv->dev->stats.rx_errors++; - goto next; - } - - /* Get header as far as end of seq_ctrl */ - atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24); - frame_ctl = le16_to_cpu(header.frame_control); - seq_control = le16_to_cpu(header.seq_ctrl); - - /* probe for CRC use here if needed once five packets have - arrived with the same crc status, we assume we know what's - happening and stop probing */ - if (priv->probe_crc) { - if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) { - priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size); - } else { - priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24); - } - if (priv->do_rx_crc) { - if (priv->crc_ok_cnt++ > 5) - priv->probe_crc = 0; - } else { - if (priv->crc_ko_cnt++ > 5) - priv->probe_crc = 0; - } - } - - /* don't CRC header when WEP in use */ - if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) { - crc = crc32_le(0xffffffff, (unsigned char *)&header, 24); - } - msdu_size -= 24; /* header */ - - if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { - int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS; - u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG; - u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4; - - if (!more_fragments && packet_fragment_no == 0) { - fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc); - } else { - frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc, - packet_sequence_no, packet_fragment_no, more_fragments); - } - } - - if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { - /* copy rest of packet into buffer */ - atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size); - - /* we use the same buffer for frag reassembly and control packets */ - eth_broadcast_addr(priv->frag_source); - - if (priv->do_rx_crc) { - /* last 4 octets is crc */ - msdu_size -= 4; - crc = crc32_le(crc, (unsigned char *)&priv->rx_buf, msdu_size); - if ((crc ^ 0xffffffff) != (*((u32 *)&priv->rx_buf[msdu_size]))) { - priv->dev->stats.rx_crc_errors++; - goto next; - } - } - - atmel_management_frame(priv, &header, msdu_size, - atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head))); - } - -next: - /* release descriptor */ - atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED); - - if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1)) - priv->rx_desc_head++; - else - priv->rx_desc_head = 0; - } -} - -static irqreturn_t service_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *) dev_id; - struct atmel_private *priv = netdev_priv(dev); - u8 isr; - int i = -1; - static const u8 irq_order[] = { - ISR_OUT_OF_RANGE, - ISR_RxCOMPLETE, - ISR_TxCOMPLETE, - ISR_RxFRAMELOST, - ISR_FATAL_ERROR, - ISR_COMMAND_COMPLETE, - ISR_IBSS_MERGE, - ISR_GENERIC_IRQ - }; - - if (priv->card && priv->present_callback && - !(*priv->present_callback)(priv->card)) - return IRQ_HANDLED; - - /* In this state upper-level code assumes it can mess with - the card unhampered by interrupts which may change register state. - Note that even though the card shouldn't generate interrupts - the inturrupt line may be shared. This allows card setup - to go on without disabling interrupts for a long time. */ - if (priv->station_state == STATION_STATE_DOWN) - return IRQ_NONE; - - atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */ - - while (1) { - if (!atmel_lock_mac(priv)) { - /* failed to contact card */ - printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); - return IRQ_HANDLED; - } - - isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); - atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); - - if (!isr) { - atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */ - return i == -1 ? IRQ_NONE : IRQ_HANDLED; - } - - atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */ - - for (i = 0; i < ARRAY_SIZE(irq_order); i++) - if (isr & irq_order[i]) - break; - - if (!atmel_lock_mac(priv)) { - /* failed to contact card */ - printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); - return IRQ_HANDLED; - } - - isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); - isr ^= irq_order[i]; - atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr); - atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); - - switch (irq_order[i]) { - - case ISR_OUT_OF_RANGE: - if (priv->operating_mode == IW_MODE_INFRA && - priv->station_state == STATION_STATE_READY) { - priv->station_is_associated = 0; - atmel_scan(priv, 1); - } - break; - - case ISR_RxFRAMELOST: - priv->wstats.discard.misc++; - /* fall through */ - case ISR_RxCOMPLETE: - rx_done_irq(priv); - break; - - case ISR_TxCOMPLETE: - tx_done_irq(priv); - break; - - case ISR_FATAL_ERROR: - printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name); - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - break; - - case ISR_COMMAND_COMPLETE: - atmel_command_irq(priv); - break; - - case ISR_IBSS_MERGE: - atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, - priv->CurrentBSSID, 6); - /* The WPA stuff cares about the current AP address */ - if (priv->use_wpa) - build_wpa_mib(priv); - break; - case ISR_GENERIC_IRQ: - printk(KERN_INFO "%s: Generic_irq received.\n", dev->name); - break; - } - } -} - -static struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* update the link quality here in case we are seeing no beacons - at all to drive the process */ - atmel_smooth_qual(priv); - - priv->wstats.status = priv->station_state; - - if (priv->operating_mode == IW_MODE_INFRA) { - if (priv->station_state != STATION_STATE_READY) { - priv->wstats.qual.qual = 0; - priv->wstats.qual.level = 0; - priv->wstats.qual.updated = (IW_QUAL_QUAL_INVALID - | IW_QUAL_LEVEL_INVALID); - } - priv->wstats.qual.noise = 0; - priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID; - } else { - /* Quality levels cannot be determined in ad-hoc mode, - because we can 'hear' more that one remote station. */ - priv->wstats.qual.qual = 0; - priv->wstats.qual.level = 0; - priv->wstats.qual.noise = 0; - priv->wstats.qual.updated = IW_QUAL_QUAL_INVALID - | IW_QUAL_LEVEL_INVALID - | IW_QUAL_NOISE_INVALID; - priv->wstats.miss.beacon = 0; - } - - return &priv->wstats; -} - -static int atmel_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > 2312)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - -static int atmel_set_mac_address(struct net_device *dev, void *p) -{ - struct sockaddr *addr = p; - - memcpy (dev->dev_addr, addr->sa_data, dev->addr_len); - return atmel_open(dev); -} - -EXPORT_SYMBOL(atmel_open); - -int atmel_open(struct net_device *dev) -{ - struct atmel_private *priv = netdev_priv(dev); - int i, channel, err; - - /* any scheduled timer is no longer needed and might screw things up.. */ - del_timer_sync(&priv->management_timer); - - /* Interrupts will not touch the card once in this state... */ - priv->station_state = STATION_STATE_DOWN; - - if (priv->new_SSID_size) { - memcpy(priv->SSID, priv->new_SSID, priv->new_SSID_size); - priv->SSID_size = priv->new_SSID_size; - priv->new_SSID_size = 0; - } - priv->BSS_list_entries = 0; - - priv->AuthenticationRequestRetryCnt = 0; - priv->AssociationRequestRetryCnt = 0; - priv->ReAssociationRequestRetryCnt = 0; - priv->CurrentAuthentTransactionSeqNum = 0x0001; - priv->ExpectedAuthentTransactionSeqNum = 0x0002; - - priv->site_survey_state = SITE_SURVEY_IDLE; - priv->station_is_associated = 0; - - err = reset_atmel_card(dev); - if (err) - return err; - - if (priv->config_reg_domain) { - priv->reg_domain = priv->config_reg_domain; - atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS, priv->reg_domain); - } else { - priv->reg_domain = atmel_get_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS); - for (i = 0; i < ARRAY_SIZE(channel_table); i++) - if (priv->reg_domain == channel_table[i].reg_domain) - break; - if (i == ARRAY_SIZE(channel_table)) { - priv->reg_domain = REG_DOMAIN_MKK1; - printk(KERN_ALERT "%s: failed to get regulatory domain: assuming MKK1.\n", dev->name); - } - } - - if ((channel = atmel_validate_channel(priv, priv->channel))) - priv->channel = channel; - - /* this moves station_state on.... */ - atmel_scan(priv, 1); - - atmel_set_gcr(priv->dev, GCR_ENINT); /* enable interrupts */ - return 0; -} - -static int atmel_close(struct net_device *dev) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* Send event to userspace that we are disassociating */ - if (priv->station_state == STATION_STATE_READY) { - union iwreq_data wrqu; - - wrqu.data.length = 0; - wrqu.data.flags = 0; - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - eth_zero_addr(wrqu.ap_addr.sa_data); - wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); - } - - atmel_enter_state(priv, STATION_STATE_DOWN); - - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(dev, GCR, 0x0060); - atmel_write16(dev, GCR, 0x0040); - return 0; -} - -static int atmel_validate_channel(struct atmel_private *priv, int channel) -{ - /* check that channel is OK, if so return zero, - else return suitable default channel */ - int i; - - for (i = 0; i < ARRAY_SIZE(channel_table); i++) - if (priv->reg_domain == channel_table[i].reg_domain) { - if (channel >= channel_table[i].min && - channel <= channel_table[i].max) - return 0; - else - return channel_table[i].min; - } - return 0; -} - -static int atmel_proc_show(struct seq_file *m, void *v) -{ - struct atmel_private *priv = m->private; - int i; - char *s, *r, *c; - - seq_printf(m, "Driver version:\t\t%d.%d\n", DRIVER_MAJOR, DRIVER_MINOR); - - if (priv->station_state != STATION_STATE_DOWN) { - seq_printf(m, - "Firmware version:\t%d.%d build %d\n" - "Firmware location:\t", - priv->host_info.major_version, - priv->host_info.minor_version, - priv->host_info.build_version); - - if (priv->card_type != CARD_TYPE_EEPROM) - seq_puts(m, "on card\n"); - else if (priv->firmware) - seq_printf(m, "%s loaded by host\n", priv->firmware_id); - else - seq_printf(m, "%s loaded by hotplug\n", priv->firmware_id); - - switch (priv->card_type) { - case CARD_TYPE_PARALLEL_FLASH: - c = "Parallel flash"; - break; - case CARD_TYPE_SPI_FLASH: - c = "SPI flash\n"; - break; - case CARD_TYPE_EEPROM: - c = "EEPROM"; - break; - default: - c = ""; - } - - r = ""; - for (i = 0; i < ARRAY_SIZE(channel_table); i++) - if (priv->reg_domain == channel_table[i].reg_domain) - r = channel_table[i].name; - - seq_printf(m, "MAC memory type:\t%s\n", c); - seq_printf(m, "Regulatory domain:\t%s\n", r); - seq_printf(m, "Host CRC checking:\t%s\n", - priv->do_rx_crc ? "On" : "Off"); - seq_printf(m, "WPA-capable firmware:\t%s\n", - priv->use_wpa ? "Yes" : "No"); - } - - switch (priv->station_state) { - case STATION_STATE_SCANNING: - s = "Scanning"; - break; - case STATION_STATE_JOINNING: - s = "Joining"; - break; - case STATION_STATE_AUTHENTICATING: - s = "Authenticating"; - break; - case STATION_STATE_ASSOCIATING: - s = "Associating"; - break; - case STATION_STATE_READY: - s = "Ready"; - break; - case STATION_STATE_REASSOCIATING: - s = "Reassociating"; - break; - case STATION_STATE_MGMT_ERROR: - s = "Management error"; - break; - case STATION_STATE_DOWN: - s = "Down"; - break; - default: - s = ""; - } - - seq_printf(m, "Current state:\t\t%s\n", s); - return 0; -} - -static int atmel_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, atmel_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations atmel_proc_fops = { - .open = atmel_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct net_device_ops atmel_netdev_ops = { - .ndo_open = atmel_open, - .ndo_stop = atmel_close, - .ndo_change_mtu = atmel_change_mtu, - .ndo_set_mac_address = atmel_set_mac_address, - .ndo_start_xmit = start_tx, - .ndo_do_ioctl = atmel_ioctl, - .ndo_validate_addr = eth_validate_addr, -}; - -struct net_device *init_atmel_card(unsigned short irq, unsigned long port, - const AtmelFWType fw_type, - struct device *sys_dev, - int (*card_present)(void *), void *card) -{ - struct net_device *dev; - struct atmel_private *priv; - int rc; - - /* Create the network device object. */ - dev = alloc_etherdev(sizeof(*priv)); - if (!dev) - return NULL; - - if (dev_alloc_name(dev, dev->name) < 0) { - printk(KERN_ERR "atmel: Couldn't get name!\n"); - goto err_out_free; - } - - priv = netdev_priv(dev); - priv->dev = dev; - priv->sys_dev = sys_dev; - priv->present_callback = card_present; - priv->card = card; - priv->firmware = NULL; - priv->firmware_id[0] = '\0'; - priv->firmware_type = fw_type; - if (firmware) /* module parameter */ - strcpy(priv->firmware_id, firmware); - priv->bus_type = card_present ? BUS_TYPE_PCCARD : BUS_TYPE_PCI; - priv->station_state = STATION_STATE_DOWN; - priv->do_rx_crc = 0; - /* For PCMCIA cards, some chips need CRC, some don't - so we have to probe. */ - if (priv->bus_type == BUS_TYPE_PCCARD) { - priv->probe_crc = 1; - priv->crc_ok_cnt = priv->crc_ko_cnt = 0; - } else - priv->probe_crc = 0; - priv->last_qual = jiffies; - priv->last_beacon_timestamp = 0; - memset(priv->frag_source, 0xff, sizeof(priv->frag_source)); - eth_zero_addr(priv->BSSID); - priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */ - priv->station_was_associated = 0; - - priv->last_survey = jiffies; - priv->preamble = LONG_PREAMBLE; - priv->operating_mode = IW_MODE_INFRA; - priv->connect_to_any_BSS = 0; - priv->config_reg_domain = 0; - priv->reg_domain = 0; - priv->tx_rate = 3; - priv->auto_tx_rate = 1; - priv->channel = 4; - priv->power_mode = 0; - priv->SSID[0] = '\0'; - priv->SSID_size = 0; - priv->new_SSID_size = 0; - priv->frag_threshold = 2346; - priv->rts_threshold = 2347; - priv->short_retry = 7; - priv->long_retry = 4; - - priv->wep_is_on = 0; - priv->default_key = 0; - priv->encryption_level = 0; - priv->exclude_unencrypted = 0; - priv->group_cipher_suite = priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; - priv->use_wpa = 0; - memset(priv->wep_keys, 0, sizeof(priv->wep_keys)); - memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); - - priv->default_beacon_period = priv->beacon_period = 100; - priv->listen_interval = 1; - - init_timer(&priv->management_timer); - spin_lock_init(&priv->irqlock); - spin_lock_init(&priv->timerlock); - priv->management_timer.function = atmel_management_timer; - priv->management_timer.data = (unsigned long) dev; - - dev->netdev_ops = &atmel_netdev_ops; - dev->wireless_handlers = &atmel_handler_def; - dev->irq = irq; - dev->base_addr = port; - - SET_NETDEV_DEV(dev, sys_dev); - - if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) { - printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc); - goto err_out_free; - } - - if (!request_region(dev->base_addr, 32, - priv->bus_type == BUS_TYPE_PCCARD ? "atmel_cs" : "atmel_pci")) { - goto err_out_irq; - } - - if (register_netdev(dev)) - goto err_out_res; - - if (!probe_atmel_card(dev)) { - unregister_netdev(dev); - goto err_out_res; - } - - netif_carrier_off(dev); - - if (!proc_create_data("driver/atmel", 0, NULL, &atmel_proc_fops, priv)) - printk(KERN_WARNING "atmel: unable to create /proc entry.\n"); - - printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %pM\n", - dev->name, DRIVER_MAJOR, DRIVER_MINOR, dev->dev_addr); - - return dev; - -err_out_res: - release_region(dev->base_addr, 32); -err_out_irq: - free_irq(dev->irq, dev); -err_out_free: - free_netdev(dev); - return NULL; -} - -EXPORT_SYMBOL(init_atmel_card); - -void stop_atmel_card(struct net_device *dev) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* put a brick on it... */ - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(dev, GCR, 0x0060); - atmel_write16(dev, GCR, 0x0040); - - del_timer_sync(&priv->management_timer); - unregister_netdev(dev); - remove_proc_entry("driver/atmel", NULL); - free_irq(dev->irq, dev); - kfree(priv->firmware); - release_region(dev->base_addr, 32); - free_netdev(dev); -} - -EXPORT_SYMBOL(stop_atmel_card); - -static int atmel_set_essid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* Check if we asked for `any' */ - if (dwrq->flags == 0) { - priv->connect_to_any_BSS = 1; - } else { - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - - priv->connect_to_any_BSS = 0; - - /* Check the size of the string */ - if (dwrq->length > MAX_SSID_LENGTH) - return -E2BIG; - if (index != 0) - return -EINVAL; - - memcpy(priv->new_SSID, extra, dwrq->length); - priv->new_SSID_size = dwrq->length; - } - - return -EINPROGRESS; -} - -static int atmel_get_essid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* Get the current SSID */ - if (priv->new_SSID_size != 0) { - memcpy(extra, priv->new_SSID, priv->new_SSID_size); - dwrq->length = priv->new_SSID_size; - } else { - memcpy(extra, priv->SSID, priv->SSID_size); - dwrq->length = priv->SSID_size; - } - - dwrq->flags = !priv->connect_to_any_BSS; /* active */ - - return 0; -} - -static int atmel_get_wap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *awrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN); - awrq->sa_family = ARPHRD_ETHER; - - return 0; -} - -static int atmel_set_encode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* Basic checking: do we have a key to set ? - * Note : with the new API, it's impossible to get a NULL pointer. - * Therefore, we need to check a key size == 0 instead. - * New version of iwconfig properly set the IW_ENCODE_NOKEY flag - * when no key is present (only change flags), but older versions - * don't do it. - Jean II */ - if (dwrq->length > 0) { - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - int current_index = priv->default_key; - /* Check the size of the key */ - if (dwrq->length > 13) { - return -EINVAL; - } - /* Check the index (none -> use current) */ - if (index < 0 || index >= 4) - index = current_index; - else - priv->default_key = index; - /* Set the length */ - if (dwrq->length > 5) - priv->wep_key_len[index] = 13; - else - if (dwrq->length > 0) - priv->wep_key_len[index] = 5; - else - /* Disable the key */ - priv->wep_key_len[index] = 0; - /* Check if the key is not marked as invalid */ - if (!(dwrq->flags & IW_ENCODE_NOKEY)) { - /* Cleanup */ - memset(priv->wep_keys[index], 0, 13); - /* Copy the key in the driver */ - memcpy(priv->wep_keys[index], extra, dwrq->length); - } - /* WE specify that if a valid key is set, encryption - * should be enabled (user may turn it off later) - * This is also how "iwconfig ethX key on" works */ - if (index == current_index && - priv->wep_key_len[index] > 0) { - priv->wep_is_on = 1; - priv->exclude_unencrypted = 1; - if (priv->wep_key_len[index] > 5) { - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; - priv->encryption_level = 2; - } else { - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; - priv->encryption_level = 1; - } - } - } else { - /* Do we want to just set the transmit key index ? */ - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - if (index >= 0 && index < 4) { - priv->default_key = index; - } else - /* Don't complain if only change the mode */ - if (!(dwrq->flags & IW_ENCODE_MODE)) - return -EINVAL; - } - /* Read the flags */ - if (dwrq->flags & IW_ENCODE_DISABLED) { - priv->wep_is_on = 0; - priv->encryption_level = 0; - priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; - } else { - priv->wep_is_on = 1; - if (priv->wep_key_len[priv->default_key] > 5) { - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; - priv->encryption_level = 2; - } else { - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; - priv->encryption_level = 1; - } - } - if (dwrq->flags & IW_ENCODE_RESTRICTED) - priv->exclude_unencrypted = 1; - if (dwrq->flags & IW_ENCODE_OPEN) - priv->exclude_unencrypted = 0; - - return -EINPROGRESS; /* Call commit handler */ -} - -static int atmel_get_encode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - - if (!priv->wep_is_on) - dwrq->flags = IW_ENCODE_DISABLED; - else { - if (priv->exclude_unencrypted) - dwrq->flags = IW_ENCODE_RESTRICTED; - else - dwrq->flags = IW_ENCODE_OPEN; - } - /* Which key do we want ? -1 -> tx index */ - if (index < 0 || index >= 4) - index = priv->default_key; - dwrq->flags |= index + 1; - /* Copy the key to the user buffer */ - dwrq->length = priv->wep_key_len[index]; - if (dwrq->length > 16) { - dwrq->length = 0; - } else { - memset(extra, 0, 16); - memcpy(extra, priv->wep_keys[index], dwrq->length); - } - - return 0; -} - -static int atmel_set_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int idx, key_len, alg = ext->alg, set_key = 1; - - /* Determine and validate the key index */ - idx = encoding->flags & IW_ENCODE_INDEX; - if (idx) { - if (idx < 1 || idx > 4) - return -EINVAL; - idx--; - } else - idx = priv->default_key; - - if (encoding->flags & IW_ENCODE_DISABLED) - alg = IW_ENCODE_ALG_NONE; - - if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { - priv->default_key = idx; - set_key = ext->key_len > 0 ? 1 : 0; - } - - if (set_key) { - /* Set the requested key first */ - switch (alg) { - case IW_ENCODE_ALG_NONE: - priv->wep_is_on = 0; - priv->encryption_level = 0; - priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; - break; - case IW_ENCODE_ALG_WEP: - if (ext->key_len > 5) { - priv->wep_key_len[idx] = 13; - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; - priv->encryption_level = 2; - } else if (ext->key_len > 0) { - priv->wep_key_len[idx] = 5; - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; - priv->encryption_level = 1; - } else { - return -EINVAL; - } - priv->wep_is_on = 1; - memset(priv->wep_keys[idx], 0, 13); - key_len = min ((int)ext->key_len, priv->wep_key_len[idx]); - memcpy(priv->wep_keys[idx], ext->key, key_len); - break; - default: - return -EINVAL; - } - } - - return -EINPROGRESS; -} - -static int atmel_get_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int idx, max_key_len; - - max_key_len = encoding->length - sizeof(*ext); - if (max_key_len < 0) - return -EINVAL; - - idx = encoding->flags & IW_ENCODE_INDEX; - if (idx) { - if (idx < 1 || idx > 4) - return -EINVAL; - idx--; - } else - idx = priv->default_key; - - encoding->flags = idx + 1; - memset(ext, 0, sizeof(*ext)); - - if (!priv->wep_is_on) { - ext->alg = IW_ENCODE_ALG_NONE; - ext->key_len = 0; - encoding->flags |= IW_ENCODE_DISABLED; - } else { - if (priv->encryption_level > 0) - ext->alg = IW_ENCODE_ALG_WEP; - else - return -EINVAL; - - ext->key_len = priv->wep_key_len[idx]; - memcpy(ext->key, priv->wep_keys[idx], ext->key_len); - encoding->flags |= IW_ENCODE_ENABLED; - } - - return 0; -} - -static int atmel_set_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - struct iw_param *param = &wrqu->param; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_PAIRWISE: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_KEY_MGMT: - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - case IW_AUTH_PRIVACY_INVOKED: - /* - * atmel does not use these parameters - */ - break; - - case IW_AUTH_DROP_UNENCRYPTED: - priv->exclude_unencrypted = param->value ? 1 : 0; - break; - - case IW_AUTH_80211_AUTH_ALG: { - if (param->value & IW_AUTH_ALG_SHARED_KEY) { - priv->exclude_unencrypted = 1; - } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { - priv->exclude_unencrypted = 0; - } else - return -EINVAL; - break; - } - - case IW_AUTH_WPA_ENABLED: - /* Silently accept disable of WPA */ - if (param->value > 0) - return -EOPNOTSUPP; - break; - - default: - return -EOPNOTSUPP; - } - return -EINPROGRESS; -} - -static int atmel_get_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - struct iw_param *param = &wrqu->param; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_DROP_UNENCRYPTED: - param->value = priv->exclude_unencrypted; - break; - - case IW_AUTH_80211_AUTH_ALG: - if (priv->exclude_unencrypted == 1) - param->value = IW_AUTH_ALG_SHARED_KEY; - else - param->value = IW_AUTH_ALG_OPEN_SYSTEM; - break; - - case IW_AUTH_WPA_ENABLED: - param->value = 0; - break; - - default: - return -EOPNOTSUPP; - } - return 0; -} - - -static int atmel_get_name(struct net_device *dev, - struct iw_request_info *info, - char *cwrq, - char *extra) -{ - strcpy(cwrq, "IEEE 802.11-DS"); - return 0; -} - -static int atmel_set_rate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - if (vwrq->fixed == 0) { - priv->tx_rate = 3; - priv->auto_tx_rate = 1; - } else { - priv->auto_tx_rate = 0; - - /* Which type of value ? */ - if ((vwrq->value < 4) && (vwrq->value >= 0)) { - /* Setting by rate index */ - priv->tx_rate = vwrq->value; - } else { - /* Setting by frequency value */ - switch (vwrq->value) { - case 1000000: - priv->tx_rate = 0; - break; - case 2000000: - priv->tx_rate = 1; - break; - case 5500000: - priv->tx_rate = 2; - break; - case 11000000: - priv->tx_rate = 3; - break; - default: - return -EINVAL; - } - } - } - - return -EINPROGRESS; -} - -static int atmel_set_mode(struct net_device *dev, - struct iw_request_info *info, - __u32 *uwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - if (*uwrq != IW_MODE_ADHOC && *uwrq != IW_MODE_INFRA) - return -EINVAL; - - priv->operating_mode = *uwrq; - return -EINPROGRESS; -} - -static int atmel_get_mode(struct net_device *dev, - struct iw_request_info *info, - __u32 *uwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - *uwrq = priv->operating_mode; - return 0; -} - -static int atmel_get_rate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - if (priv->auto_tx_rate) { - vwrq->fixed = 0; - vwrq->value = 11000000; - } else { - vwrq->fixed = 1; - switch (priv->tx_rate) { - case 0: - vwrq->value = 1000000; - break; - case 1: - vwrq->value = 2000000; - break; - case 2: - vwrq->value = 5500000; - break; - case 3: - vwrq->value = 11000000; - break; - } - } - return 0; -} - -static int atmel_set_power(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - priv->power_mode = vwrq->disabled ? 0 : 1; - return -EINPROGRESS; -} - -static int atmel_get_power(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - vwrq->disabled = priv->power_mode ? 0 : 1; - vwrq->flags = IW_POWER_ON; - return 0; -} - -static int atmel_set_retry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) { - if (vwrq->flags & IW_RETRY_LONG) - priv->long_retry = vwrq->value; - else if (vwrq->flags & IW_RETRY_SHORT) - priv->short_retry = vwrq->value; - else { - /* No modifier : set both */ - priv->long_retry = vwrq->value; - priv->short_retry = vwrq->value; - } - return -EINPROGRESS; - } - - return -EINVAL; -} - -static int atmel_get_retry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - vwrq->disabled = 0; /* Can't be disabled */ - - /* Note : by default, display the short retry number */ - if (vwrq->flags & IW_RETRY_LONG) { - vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; - vwrq->value = priv->long_retry; - } else { - vwrq->flags = IW_RETRY_LIMIT; - vwrq->value = priv->short_retry; - if (priv->long_retry != priv->short_retry) - vwrq->flags |= IW_RETRY_SHORT; - } - - return 0; -} - -static int atmel_set_rts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int rthr = vwrq->value; - - if (vwrq->disabled) - rthr = 2347; - if ((rthr < 0) || (rthr > 2347)) { - return -EINVAL; - } - priv->rts_threshold = rthr; - - return -EINPROGRESS; /* Call commit handler */ -} - -static int atmel_get_rts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - vwrq->value = priv->rts_threshold; - vwrq->disabled = (vwrq->value >= 2347); - vwrq->fixed = 1; - - return 0; -} - -static int atmel_set_frag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int fthr = vwrq->value; - - if (vwrq->disabled) - fthr = 2346; - if ((fthr < 256) || (fthr > 2346)) { - return -EINVAL; - } - fthr &= ~0x1; /* Get an even value - is it really needed ??? */ - priv->frag_threshold = fthr; - - return -EINPROGRESS; /* Call commit handler */ -} - -static int atmel_get_frag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - vwrq->value = priv->frag_threshold; - vwrq->disabled = (vwrq->value >= 2346); - vwrq->fixed = 1; - - return 0; -} - -static int atmel_set_freq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *fwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int rc = -EINPROGRESS; /* Call commit handler */ - - /* If setting by frequency, convert to a channel */ - if (fwrq->e == 1) { - int f = fwrq->m / 100000; - - /* Hack to fall through... */ - fwrq->e = 0; - fwrq->m = ieee80211_frequency_to_channel(f); - } - /* Setting by channel number */ - if ((fwrq->m > 1000) || (fwrq->e > 0)) - rc = -EOPNOTSUPP; - else { - int channel = fwrq->m; - if (atmel_validate_channel(priv, channel) == 0) { - priv->channel = channel; - } else { - rc = -EINVAL; - } - } - return rc; -} - -static int atmel_get_freq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *fwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - fwrq->m = priv->channel; - fwrq->e = 0; - return 0; -} - -static int atmel_set_scan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - unsigned long flags; - - /* Note : you may have realised that, as this is a SET operation, - * this is privileged and therefore a normal user can't - * perform scanning. - * This is not an error, while the device perform scanning, - * traffic doesn't flow, so it's a perfect DoS... - * Jean II */ - - if (priv->station_state == STATION_STATE_DOWN) - return -EAGAIN; - - /* Timeout old surveys. */ - if (time_after(jiffies, priv->last_survey + 20 * HZ)) - priv->site_survey_state = SITE_SURVEY_IDLE; - priv->last_survey = jiffies; - - /* Initiate a scan command */ - if (priv->site_survey_state == SITE_SURVEY_IN_PROGRESS) - return -EBUSY; - - del_timer_sync(&priv->management_timer); - spin_lock_irqsave(&priv->irqlock, flags); - - priv->site_survey_state = SITE_SURVEY_IN_PROGRESS; - priv->fast_scan = 0; - atmel_scan(priv, 0); - spin_unlock_irqrestore(&priv->irqlock, flags); - - return 0; -} - -static int atmel_get_scan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int i; - char *current_ev = extra; - struct iw_event iwe; - - if (priv->site_survey_state != SITE_SURVEY_COMPLETED) - return -EAGAIN; - - for (i = 0; i < priv->BSS_list_entries; i++) { - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN); - current_ev = iwe_stream_add_event(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, IW_EV_ADDR_LEN); - - iwe.u.data.length = priv->BSSinfo[i].SSIDsize; - if (iwe.u.data.length > 32) - iwe.u.data.length = 32; - iwe.cmd = SIOCGIWESSID; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, priv->BSSinfo[i].SSID); - - iwe.cmd = SIOCGIWMODE; - iwe.u.mode = priv->BSSinfo[i].BSStype; - current_ev = iwe_stream_add_event(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, IW_EV_UINT_LEN); - - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = priv->BSSinfo[i].channel; - iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, IW_EV_FREQ_LEN); - - /* Add quality statistics */ - iwe.cmd = IWEVQUAL; - iwe.u.qual.level = priv->BSSinfo[i].RSSI; - iwe.u.qual.qual = iwe.u.qual.level; - /* iwe.u.qual.noise = SOMETHING */ - current_ev = iwe_stream_add_event(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, IW_EV_QUAL_LEN); - - - iwe.cmd = SIOCGIWENCODE; - if (priv->BSSinfo[i].UsingWEP) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, NULL); - } - - /* Length of data */ - dwrq->length = (current_ev - extra); - dwrq->flags = 0; - - return 0; -} - -static int atmel_get_range(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - struct iw_range *range = (struct iw_range *) extra; - int k, i, j; - - dwrq->length = sizeof(struct iw_range); - memset(range, 0, sizeof(struct iw_range)); - range->min_nwid = 0x0000; - range->max_nwid = 0x0000; - range->num_channels = 0; - for (j = 0; j < ARRAY_SIZE(channel_table); j++) - if (priv->reg_domain == channel_table[j].reg_domain) { - range->num_channels = channel_table[j].max - channel_table[j].min + 1; - break; - } - if (range->num_channels != 0) { - for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) { - range->freq[k].i = i; /* List index */ - - /* Values in MHz -> * 10^5 * 10 */ - range->freq[k].m = 100000 * - ieee80211_channel_to_frequency(i, IEEE80211_BAND_2GHZ); - range->freq[k++].e = 1; - } - range->num_frequency = k; - } - - range->max_qual.qual = 100; - range->max_qual.level = 100; - range->max_qual.noise = 0; - range->max_qual.updated = IW_QUAL_NOISE_INVALID; - - range->avg_qual.qual = 50; - range->avg_qual.level = 50; - range->avg_qual.noise = 0; - range->avg_qual.updated = IW_QUAL_NOISE_INVALID; - - range->sensitivity = 0; - - range->bitrate[0] = 1000000; - range->bitrate[1] = 2000000; - range->bitrate[2] = 5500000; - range->bitrate[3] = 11000000; - range->num_bitrates = 4; - - range->min_rts = 0; - range->max_rts = 2347; - range->min_frag = 256; - range->max_frag = 2346; - - range->encoding_size[0] = 5; - range->encoding_size[1] = 13; - range->num_encoding_sizes = 2; - range->max_encoding_tokens = 4; - - range->pmp_flags = IW_POWER_ON; - range->pmt_flags = IW_POWER_ON; - range->pm_capa = 0; - - range->we_version_source = WIRELESS_EXT; - range->we_version_compiled = WIRELESS_EXT; - range->retry_capa = IW_RETRY_LIMIT ; - range->retry_flags = IW_RETRY_LIMIT; - range->r_time_flags = 0; - range->min_retry = 1; - range->max_retry = 65535; - - return 0; -} - -static int atmel_set_wap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *awrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int i; - static const u8 any[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - unsigned long flags; - - if (awrq->sa_family != ARPHRD_ETHER) - return -EINVAL; - - if (!memcmp(any, awrq->sa_data, 6) || - !memcmp(off, awrq->sa_data, 6)) { - del_timer_sync(&priv->management_timer); - spin_lock_irqsave(&priv->irqlock, flags); - atmel_scan(priv, 1); - spin_unlock_irqrestore(&priv->irqlock, flags); - return 0; - } - - for (i = 0; i < priv->BSS_list_entries; i++) { - if (memcmp(priv->BSSinfo[i].BSSID, awrq->sa_data, 6) == 0) { - if (!priv->wep_is_on && priv->BSSinfo[i].UsingWEP) { - return -EINVAL; - } else if (priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) { - return -EINVAL; - } else { - del_timer_sync(&priv->management_timer); - spin_lock_irqsave(&priv->irqlock, flags); - atmel_join_bss(priv, i); - spin_unlock_irqrestore(&priv->irqlock, flags); - return 0; - } - } - } - - return -EINVAL; -} - -static int atmel_config_commit(struct net_device *dev, - struct iw_request_info *info, /* NULL */ - void *zwrq, /* NULL */ - char *extra) /* NULL */ -{ - return atmel_open(dev); -} - -static const iw_handler atmel_handler[] = -{ - (iw_handler) atmel_config_commit, /* SIOCSIWCOMMIT */ - (iw_handler) atmel_get_name, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) atmel_set_freq, /* SIOCSIWFREQ */ - (iw_handler) atmel_get_freq, /* SIOCGIWFREQ */ - (iw_handler) atmel_set_mode, /* SIOCSIWMODE */ - (iw_handler) atmel_get_mode, /* SIOCGIWMODE */ - (iw_handler) NULL, /* SIOCSIWSENS */ - (iw_handler) NULL, /* SIOCGIWSENS */ - (iw_handler) NULL, /* SIOCSIWRANGE */ - (iw_handler) atmel_get_range, /* SIOCGIWRANGE */ - (iw_handler) NULL, /* SIOCSIWPRIV */ - (iw_handler) NULL, /* SIOCGIWPRIV */ - (iw_handler) NULL, /* SIOCSIWSTATS */ - (iw_handler) NULL, /* SIOCGIWSTATS */ - (iw_handler) NULL, /* SIOCSIWSPY */ - (iw_handler) NULL, /* SIOCGIWSPY */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) atmel_set_wap, /* SIOCSIWAP */ - (iw_handler) atmel_get_wap, /* SIOCGIWAP */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCGIWAPLIST */ - (iw_handler) atmel_set_scan, /* SIOCSIWSCAN */ - (iw_handler) atmel_get_scan, /* SIOCGIWSCAN */ - (iw_handler) atmel_set_essid, /* SIOCSIWESSID */ - (iw_handler) atmel_get_essid, /* SIOCGIWESSID */ - (iw_handler) NULL, /* SIOCSIWNICKN */ - (iw_handler) NULL, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) atmel_set_rate, /* SIOCSIWRATE */ - (iw_handler) atmel_get_rate, /* SIOCGIWRATE */ - (iw_handler) atmel_set_rts, /* SIOCSIWRTS */ - (iw_handler) atmel_get_rts, /* SIOCGIWRTS */ - (iw_handler) atmel_set_frag, /* SIOCSIWFRAG */ - (iw_handler) atmel_get_frag, /* SIOCGIWFRAG */ - (iw_handler) NULL, /* SIOCSIWTXPOW */ - (iw_handler) NULL, /* SIOCGIWTXPOW */ - (iw_handler) atmel_set_retry, /* SIOCSIWRETRY */ - (iw_handler) atmel_get_retry, /* SIOCGIWRETRY */ - (iw_handler) atmel_set_encode, /* SIOCSIWENCODE */ - (iw_handler) atmel_get_encode, /* SIOCGIWENCODE */ - (iw_handler) atmel_set_power, /* SIOCSIWPOWER */ - (iw_handler) atmel_get_power, /* SIOCGIWPOWER */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCSIWGENIE */ - (iw_handler) NULL, /* SIOCGIWGENIE */ - (iw_handler) atmel_set_auth, /* SIOCSIWAUTH */ - (iw_handler) atmel_get_auth, /* SIOCGIWAUTH */ - (iw_handler) atmel_set_encodeext, /* SIOCSIWENCODEEXT */ - (iw_handler) atmel_get_encodeext, /* SIOCGIWENCODEEXT */ - (iw_handler) NULL, /* SIOCSIWPMKSA */ -}; - -static const iw_handler atmel_private_handler[] = -{ - NULL, /* SIOCIWFIRSTPRIV */ -}; - -struct atmel_priv_ioctl { - char id[32]; - unsigned char __user *data; - unsigned short len; -}; - -#define ATMELFWL SIOCIWFIRSTPRIV -#define ATMELIDIFC ATMELFWL + 1 -#define ATMELRD ATMELFWL + 2 -#define ATMELMAGIC 0x51807 -#define REGDOMAINSZ 20 - -static const struct iw_priv_args atmel_private_args[] = { - { - .cmd = ATMELFWL, - .set_args = IW_PRIV_TYPE_BYTE - | IW_PRIV_SIZE_FIXED - | sizeof(struct atmel_priv_ioctl), - .get_args = IW_PRIV_TYPE_NONE, - .name = "atmelfwl" - }, { - .cmd = ATMELIDIFC, - .set_args = IW_PRIV_TYPE_NONE, - .get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - .name = "atmelidifc" - }, { - .cmd = ATMELRD, - .set_args = IW_PRIV_TYPE_CHAR | REGDOMAINSZ, - .get_args = IW_PRIV_TYPE_NONE, - .name = "regdomain" - }, -}; - -static const struct iw_handler_def atmel_handler_def = { - .num_standard = ARRAY_SIZE(atmel_handler), - .num_private = ARRAY_SIZE(atmel_private_handler), - .num_private_args = ARRAY_SIZE(atmel_private_args), - .standard = (iw_handler *) atmel_handler, - .private = (iw_handler *) atmel_private_handler, - .private_args = (struct iw_priv_args *) atmel_private_args, - .get_wireless_stats = atmel_get_wireless_stats -}; - -static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - int i, rc = 0; - struct atmel_private *priv = netdev_priv(dev); - struct atmel_priv_ioctl com; - struct iwreq *wrq = (struct iwreq *) rq; - unsigned char *new_firmware; - char domain[REGDOMAINSZ + 1]; - - switch (cmd) { - case ATMELIDIFC: - wrq->u.param.value = ATMELMAGIC; - break; - - case ATMELFWL: - if (copy_from_user(&com, rq->ifr_data, sizeof(com))) { - rc = -EFAULT; - break; - } - - if (!capable(CAP_NET_ADMIN)) { - rc = -EPERM; - break; - } - - if (!(new_firmware = kmalloc(com.len, GFP_KERNEL))) { - rc = -ENOMEM; - break; - } - - if (copy_from_user(new_firmware, com.data, com.len)) { - kfree(new_firmware); - rc = -EFAULT; - break; - } - - kfree(priv->firmware); - - priv->firmware = new_firmware; - priv->firmware_length = com.len; - strncpy(priv->firmware_id, com.id, 31); - priv->firmware_id[31] = '\0'; - break; - - case ATMELRD: - if (copy_from_user(domain, rq->ifr_data, REGDOMAINSZ)) { - rc = -EFAULT; - break; - } - - if (!capable(CAP_NET_ADMIN)) { - rc = -EPERM; - break; - } - - domain[REGDOMAINSZ] = 0; - rc = -EINVAL; - for (i = 0; i < ARRAY_SIZE(channel_table); i++) { - if (!strcasecmp(channel_table[i].name, domain)) { - priv->config_reg_domain = channel_table[i].reg_domain; - rc = 0; - } - } - - if (rc == 0 && priv->station_state != STATION_STATE_DOWN) - rc = atmel_open(dev); - break; - - default: - rc = -EOPNOTSUPP; - } - - return rc; -} - -struct auth_body { - __le16 alg; - __le16 trans_seq; - __le16 status; - u8 el_id; - u8 chall_text_len; - u8 chall_text[253]; -}; - -static void atmel_enter_state(struct atmel_private *priv, int new_state) -{ - int old_state = priv->station_state; - - if (new_state == old_state) - return; - - priv->station_state = new_state; - - if (new_state == STATION_STATE_READY) { - netif_start_queue(priv->dev); - netif_carrier_on(priv->dev); - } - - if (old_state == STATION_STATE_READY) { - netif_carrier_off(priv->dev); - if (netif_running(priv->dev)) - netif_stop_queue(priv->dev); - priv->last_beacon_timestamp = 0; - } -} - -static void atmel_scan(struct atmel_private *priv, int specific_ssid) -{ - struct { - u8 BSSID[ETH_ALEN]; - u8 SSID[MAX_SSID_LENGTH]; - u8 scan_type; - u8 channel; - __le16 BSS_type; - __le16 min_channel_time; - __le16 max_channel_time; - u8 options; - u8 SSID_size; - } cmd; - - eth_broadcast_addr(cmd.BSSID); - - if (priv->fast_scan) { - cmd.SSID_size = priv->SSID_size; - memcpy(cmd.SSID, priv->SSID, priv->SSID_size); - cmd.min_channel_time = cpu_to_le16(10); - cmd.max_channel_time = cpu_to_le16(50); - } else { - priv->BSS_list_entries = 0; - cmd.SSID_size = 0; - cmd.min_channel_time = cpu_to_le16(10); - cmd.max_channel_time = cpu_to_le16(120); - } - - cmd.options = 0; - - if (!specific_ssid) - cmd.options |= SCAN_OPTIONS_SITE_SURVEY; - - cmd.channel = (priv->channel & 0x7f); - cmd.scan_type = SCAN_TYPE_ACTIVE; - cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ? - BSS_TYPE_AD_HOC : BSS_TYPE_INFRASTRUCTURE); - - atmel_send_command(priv, CMD_Scan, &cmd, sizeof(cmd)); - - /* This must come after all hardware access to avoid being messed up - by stuff happening in interrupt context after we leave STATE_DOWN */ - atmel_enter_state(priv, STATION_STATE_SCANNING); -} - -static void join(struct atmel_private *priv, int type) -{ - struct { - u8 BSSID[6]; - u8 SSID[MAX_SSID_LENGTH]; - u8 BSS_type; /* this is a short in a scan command - weird */ - u8 channel; - __le16 timeout; - u8 SSID_size; - u8 reserved; - } cmd; - - cmd.SSID_size = priv->SSID_size; - memcpy(cmd.SSID, priv->SSID, priv->SSID_size); - memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN); - cmd.channel = (priv->channel & 0x7f); - cmd.BSS_type = type; - cmd.timeout = cpu_to_le16(2000); - - atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd)); -} - -static void start(struct atmel_private *priv, int type) -{ - struct { - u8 BSSID[6]; - u8 SSID[MAX_SSID_LENGTH]; - u8 BSS_type; - u8 channel; - u8 SSID_size; - u8 reserved[3]; - } cmd; - - cmd.SSID_size = priv->SSID_size; - memcpy(cmd.SSID, priv->SSID, priv->SSID_size); - memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN); - cmd.BSS_type = type; - cmd.channel = (priv->channel & 0x7f); - - atmel_send_command(priv, CMD_Start, &cmd, sizeof(cmd)); -} - -static void handle_beacon_probe(struct atmel_private *priv, u16 capability, - u8 channel) -{ - int rejoin = 0; - int new = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? - SHORT_PREAMBLE : LONG_PREAMBLE; - - if (priv->preamble != new) { - priv->preamble = new; - rejoin = 1; - atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, new); - } - - if (priv->channel != channel) { - priv->channel = channel; - rejoin = 1; - atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_CHANNEL_POS, channel); - } - - if (rejoin) { - priv->station_is_associated = 0; - atmel_enter_state(priv, STATION_STATE_JOINNING); - - if (priv->operating_mode == IW_MODE_INFRA) - join(priv, BSS_TYPE_INFRASTRUCTURE); - else - join(priv, BSS_TYPE_AD_HOC); - } -} - -static void send_authentication_request(struct atmel_private *priv, u16 system, - u8 *challenge, int challenge_len) -{ - struct ieee80211_hdr header; - struct auth_body auth; - - header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); - header.duration_id = cpu_to_le16(0x8000); - header.seq_ctrl = 0; - memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); - memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); - memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); - - if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1) - /* no WEP for authentication frames with TrSeqNo 1 */ - header.frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); - - auth.alg = cpu_to_le16(system); - - auth.status = 0; - auth.trans_seq = cpu_to_le16(priv->CurrentAuthentTransactionSeqNum); - priv->ExpectedAuthentTransactionSeqNum = priv->CurrentAuthentTransactionSeqNum+1; - priv->CurrentAuthentTransactionSeqNum += 2; - - if (challenge_len != 0) { - auth.el_id = 16; /* challenge_text */ - auth.chall_text_len = challenge_len; - memcpy(auth.chall_text, challenge, challenge_len); - atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 8 + challenge_len); - } else { - atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 6); - } -} - -static void send_association_request(struct atmel_private *priv, int is_reassoc) -{ - u8 *ssid_el_p; - int bodysize; - struct ieee80211_hdr header; - struct ass_req_format { - __le16 capability; - __le16 listen_interval; - u8 ap[ETH_ALEN]; /* nothing after here directly accessible */ - u8 ssid_el_id; - u8 ssid_len; - u8 ssid[MAX_SSID_LENGTH]; - u8 sup_rates_el_id; - u8 sup_rates_len; - u8 rates[4]; - } body; - - header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - (is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ)); - header.duration_id = cpu_to_le16(0x8000); - header.seq_ctrl = 0; - - memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); - memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); - memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); - - body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS); - if (priv->wep_is_on) - body.capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); - if (priv->preamble == SHORT_PREAMBLE) - body.capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); - - body.listen_interval = cpu_to_le16(priv->listen_interval * priv->beacon_period); - - /* current AP address - only in reassoc frame */ - if (is_reassoc) { - memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN); - ssid_el_p = &body.ssid_el_id; - bodysize = 18 + priv->SSID_size; - } else { - ssid_el_p = &body.ap[0]; - bodysize = 12 + priv->SSID_size; - } - - ssid_el_p[0] = WLAN_EID_SSID; - ssid_el_p[1] = priv->SSID_size; - memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size); - ssid_el_p[2 + priv->SSID_size] = WLAN_EID_SUPP_RATES; - ssid_el_p[3 + priv->SSID_size] = 4; /* len of supported rates */ - memcpy(ssid_el_p + 4 + priv->SSID_size, atmel_basic_rates, 4); - - atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize); -} - -static int is_frame_from_current_bss(struct atmel_private *priv, - struct ieee80211_hdr *header) -{ - if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) - return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0; - else - return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0; -} - -static int retrieve_bss(struct atmel_private *priv) -{ - int i; - int max_rssi = -128; - int max_index = -1; - - if (priv->BSS_list_entries == 0) - return -1; - - if (priv->connect_to_any_BSS) { - /* Select a BSS with the max-RSSI but of the same type and of - the same WEP mode and that it is not marked as 'bad' (i.e. - we had previously failed to connect to this BSS with the - settings that we currently use) */ - priv->current_BSS = 0; - for (i = 0; i < priv->BSS_list_entries; i++) { - if (priv->operating_mode == priv->BSSinfo[i].BSStype && - ((!priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) || - (priv->wep_is_on && priv->BSSinfo[i].UsingWEP)) && - !(priv->BSSinfo[i].channel & 0x80)) { - max_rssi = priv->BSSinfo[i].RSSI; - priv->current_BSS = max_index = i; - } - } - return max_index; - } - - for (i = 0; i < priv->BSS_list_entries; i++) { - if (priv->SSID_size == priv->BSSinfo[i].SSIDsize && - memcmp(priv->SSID, priv->BSSinfo[i].SSID, priv->SSID_size) == 0 && - priv->operating_mode == priv->BSSinfo[i].BSStype && - atmel_validate_channel(priv, priv->BSSinfo[i].channel) == 0) { - if (priv->BSSinfo[i].RSSI >= max_rssi) { - max_rssi = priv->BSSinfo[i].RSSI; - max_index = i; - } - } - } - return max_index; -} - -static void store_bss_info(struct atmel_private *priv, - struct ieee80211_hdr *header, u16 capability, - u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len, - u8 *ssid, int is_beacon) -{ - u8 *bss = capability & WLAN_CAPABILITY_ESS ? header->addr2 : header->addr3; - int i, index; - - for (index = -1, i = 0; i < priv->BSS_list_entries; i++) - if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0) - index = i; - - /* If we process a probe and an entry from this BSS exists - we will update the BSS entry with the info from this BSS. - If we process a beacon we will only update RSSI */ - - if (index == -1) { - if (priv->BSS_list_entries == MAX_BSS_ENTRIES) - return; - index = priv->BSS_list_entries++; - memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN); - priv->BSSinfo[index].RSSI = rssi; - } else { - if (rssi > priv->BSSinfo[index].RSSI) - priv->BSSinfo[index].RSSI = rssi; - if (is_beacon) - return; - } - - priv->BSSinfo[index].channel = channel; - priv->BSSinfo[index].beacon_period = beacon_period; - priv->BSSinfo[index].UsingWEP = capability & WLAN_CAPABILITY_PRIVACY; - memcpy(priv->BSSinfo[index].SSID, ssid, ssid_len); - priv->BSSinfo[index].SSIDsize = ssid_len; - - if (capability & WLAN_CAPABILITY_IBSS) - priv->BSSinfo[index].BSStype = IW_MODE_ADHOC; - else if (capability & WLAN_CAPABILITY_ESS) - priv->BSSinfo[index].BSStype = IW_MODE_INFRA; - - priv->BSSinfo[index].preamble = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? - SHORT_PREAMBLE : LONG_PREAMBLE; -} - -static void authenticate(struct atmel_private *priv, u16 frame_len) -{ - struct auth_body *auth = (struct auth_body *)priv->rx_buf; - u16 status = le16_to_cpu(auth->status); - u16 trans_seq_no = le16_to_cpu(auth->trans_seq); - u16 system = le16_to_cpu(auth->alg); - - if (status == WLAN_STATUS_SUCCESS && !priv->wep_is_on) { - /* no WEP */ - if (priv->station_was_associated) { - atmel_enter_state(priv, STATION_STATE_REASSOCIATING); - send_association_request(priv, 1); - return; - } else { - atmel_enter_state(priv, STATION_STATE_ASSOCIATING); - send_association_request(priv, 0); - return; - } - } - - if (status == WLAN_STATUS_SUCCESS && priv->wep_is_on) { - int should_associate = 0; - /* WEP */ - if (trans_seq_no != priv->ExpectedAuthentTransactionSeqNum) - return; - - if (system == WLAN_AUTH_OPEN) { - if (trans_seq_no == 0x0002) { - should_associate = 1; - } - } else if (system == WLAN_AUTH_SHARED_KEY) { - if (trans_seq_no == 0x0002 && - auth->el_id == WLAN_EID_CHALLENGE) { - send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len); - return; - } else if (trans_seq_no == 0x0004) { - should_associate = 1; - } - } - - if (should_associate) { - if (priv->station_was_associated) { - atmel_enter_state(priv, STATION_STATE_REASSOCIATING); - send_association_request(priv, 1); - return; - } else { - atmel_enter_state(priv, STATION_STATE_ASSOCIATING); - send_association_request(priv, 0); - return; - } - } - } - - if (status == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { - /* Flip back and forth between WEP auth modes until the max - * authentication tries has been exceeded. - */ - if (system == WLAN_AUTH_OPEN) { - priv->CurrentAuthentTransactionSeqNum = 0x001; - priv->exclude_unencrypted = 1; - send_authentication_request(priv, WLAN_AUTH_SHARED_KEY, NULL, 0); - return; - } else if (system == WLAN_AUTH_SHARED_KEY - && priv->wep_is_on) { - priv->CurrentAuthentTransactionSeqNum = 0x001; - priv->exclude_unencrypted = 0; - send_authentication_request(priv, WLAN_AUTH_OPEN, NULL, 0); - return; - } else if (priv->connect_to_any_BSS) { - int bss_index; - - priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; - - if ((bss_index = retrieve_bss(priv)) != -1) { - atmel_join_bss(priv, bss_index); - return; - } - } - } - - priv->AuthenticationRequestRetryCnt = 0; - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; -} - -static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype) -{ - struct ass_resp_format { - __le16 capability; - __le16 status; - __le16 ass_id; - u8 el_id; - u8 length; - u8 rates[4]; - } *ass_resp = (struct ass_resp_format *)priv->rx_buf; - - u16 status = le16_to_cpu(ass_resp->status); - u16 ass_id = le16_to_cpu(ass_resp->ass_id); - u16 rates_len = ass_resp->length > 4 ? 4 : ass_resp->length; - - union iwreq_data wrqu; - - if (frame_len < 8 + rates_len) - return; - - if (status == WLAN_STATUS_SUCCESS) { - if (subtype == IEEE80211_STYPE_ASSOC_RESP) - priv->AssociationRequestRetryCnt = 0; - else - priv->ReAssociationRequestRetryCnt = 0; - - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_STATION_ID_POS, ass_id & 0x3fff); - atmel_set_mib(priv, Phy_Mib_Type, - PHY_MIB_RATE_SET_POS, ass_resp->rates, rates_len); - if (priv->power_mode == 0) { - priv->listen_interval = 1; - atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); - } else { - priv->listen_interval = 2; - atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_PS_MODE_POS, PS_MODE); - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 2); - } - - priv->station_is_associated = 1; - priv->station_was_associated = 1; - atmel_enter_state(priv, STATION_STATE_READY); - - /* Send association event to userspace */ - wrqu.data.length = 0; - wrqu.data.flags = 0; - memcpy(wrqu.ap_addr.sa_data, priv->CurrentBSSID, ETH_ALEN); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); - - return; - } - - if (subtype == IEEE80211_STYPE_ASSOC_RESP && - status != WLAN_STATUS_ASSOC_DENIED_RATES && - status != WLAN_STATUS_CAPS_UNSUPPORTED && - priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - priv->AssociationRequestRetryCnt++; - send_association_request(priv, 0); - return; - } - - if (subtype == IEEE80211_STYPE_REASSOC_RESP && - status != WLAN_STATUS_ASSOC_DENIED_RATES && - status != WLAN_STATUS_CAPS_UNSUPPORTED && - priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - priv->ReAssociationRequestRetryCnt++; - send_association_request(priv, 1); - return; - } - - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - - if (priv->connect_to_any_BSS) { - int bss_index; - priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; - - if ((bss_index = retrieve_bss(priv)) != -1) - atmel_join_bss(priv, bss_index); - } -} - -static void atmel_join_bss(struct atmel_private *priv, int bss_index) -{ - struct bss_info *bss = &priv->BSSinfo[bss_index]; - - memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN); - memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize); - - /* The WPA stuff cares about the current AP address */ - if (priv->use_wpa) - build_wpa_mib(priv); - - /* When switching to AdHoc turn OFF Power Save if needed */ - - if (bss->BSStype == IW_MODE_ADHOC && - priv->operating_mode != IW_MODE_ADHOC && - priv->power_mode) { - priv->power_mode = 0; - priv->listen_interval = 1; - atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); - } - - priv->operating_mode = bss->BSStype; - priv->channel = bss->channel & 0x7f; - priv->beacon_period = bss->beacon_period; - - if (priv->preamble != bss->preamble) { - priv->preamble = bss->preamble; - atmel_set_mib8(priv, Local_Mib_Type, - LOCAL_MIB_PREAMBLE_TYPE, bss->preamble); - } - - if (!priv->wep_is_on && bss->UsingWEP) { - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - return; - } - - if (priv->wep_is_on && !bss->UsingWEP) { - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - return; - } - - atmel_enter_state(priv, STATION_STATE_JOINNING); - - if (priv->operating_mode == IW_MODE_INFRA) - join(priv, BSS_TYPE_INFRASTRUCTURE); - else - join(priv, BSS_TYPE_AD_HOC); -} - -static void restart_search(struct atmel_private *priv) -{ - int bss_index; - - if (!priv->connect_to_any_BSS) { - atmel_scan(priv, 1); - } else { - priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; - - if ((bss_index = retrieve_bss(priv)) != -1) - atmel_join_bss(priv, bss_index); - else - atmel_scan(priv, 0); - } -} - -static void smooth_rssi(struct atmel_private *priv, u8 rssi) -{ - u8 old = priv->wstats.qual.level; - u8 max_rssi = 42; /* 502-rmfd-revd max by experiment, default for now */ - - switch (priv->firmware_type) { - case ATMEL_FW_TYPE_502E: - max_rssi = 63; /* 502-rmfd-reve max by experiment */ - break; - default: - break; - } - - rssi = rssi * 100 / max_rssi; - if ((rssi + old) % 2) - priv->wstats.qual.level = (rssi + old) / 2 + 1; - else - priv->wstats.qual.level = (rssi + old) / 2; - priv->wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; - priv->wstats.qual.updated &= ~IW_QUAL_LEVEL_INVALID; -} - -static void atmel_smooth_qual(struct atmel_private *priv) -{ - unsigned long time_diff = (jiffies - priv->last_qual) / HZ; - while (time_diff--) { - priv->last_qual += HZ; - priv->wstats.qual.qual = priv->wstats.qual.qual / 2; - priv->wstats.qual.qual += - priv->beacons_this_sec * priv->beacon_period * (priv->wstats.qual.level + 100) / 4000; - priv->beacons_this_sec = 0; - } - priv->wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; - priv->wstats.qual.updated &= ~IW_QUAL_QUAL_INVALID; -} - -/* deals with incoming management frames. */ -static void atmel_management_frame(struct atmel_private *priv, - struct ieee80211_hdr *header, - u16 frame_len, u8 rssi) -{ - u16 subtype; - - subtype = le16_to_cpu(header->frame_control) & IEEE80211_FCTL_STYPE; - switch (subtype) { - case IEEE80211_STYPE_BEACON: - case IEEE80211_STYPE_PROBE_RESP: - - /* beacon frame has multiple variable-length fields - - never let an engineer loose with a data structure design. */ - { - struct beacon_format { - __le64 timestamp; - __le16 interval; - __le16 capability; - u8 ssid_el_id; - u8 ssid_length; - /* ssid here */ - u8 rates_el_id; - u8 rates_length; - /* rates here */ - u8 ds_el_id; - u8 ds_length; - /* ds here */ - } *beacon = (struct beacon_format *)priv->rx_buf; - - u8 channel, rates_length, ssid_length; - u64 timestamp = le64_to_cpu(beacon->timestamp); - u16 beacon_interval = le16_to_cpu(beacon->interval); - u16 capability = le16_to_cpu(beacon->capability); - u8 *beaconp = priv->rx_buf; - ssid_length = beacon->ssid_length; - /* this blows chunks. */ - if (frame_len < 14 || frame_len < ssid_length + 15) - return; - rates_length = beaconp[beacon->ssid_length + 15]; - if (frame_len < ssid_length + rates_length + 18) - return; - if (ssid_length > MAX_SSID_LENGTH) - return; - channel = beaconp[ssid_length + rates_length + 18]; - - if (priv->station_state == STATION_STATE_READY) { - smooth_rssi(priv, rssi); - if (is_frame_from_current_bss(priv, header)) { - priv->beacons_this_sec++; - atmel_smooth_qual(priv); - if (priv->last_beacon_timestamp) { - /* Note truncate this to 32 bits - kernel can't divide a long long */ - u32 beacon_delay = timestamp - priv->last_beacon_timestamp; - int beacons = beacon_delay / (beacon_interval * 1000); - if (beacons > 1) - priv->wstats.miss.beacon += beacons - 1; - } - priv->last_beacon_timestamp = timestamp; - handle_beacon_probe(priv, capability, channel); - } - } - - if (priv->station_state == STATION_STATE_SCANNING) - store_bss_info(priv, header, capability, - beacon_interval, channel, rssi, - ssid_length, - &beacon->rates_el_id, - subtype == IEEE80211_STYPE_BEACON); - } - break; - - case IEEE80211_STYPE_AUTH: - - if (priv->station_state == STATION_STATE_AUTHENTICATING) - authenticate(priv, frame_len); - - break; - - case IEEE80211_STYPE_ASSOC_RESP: - case IEEE80211_STYPE_REASSOC_RESP: - - if (priv->station_state == STATION_STATE_ASSOCIATING || - priv->station_state == STATION_STATE_REASSOCIATING) - associate(priv, frame_len, subtype); - - break; - - case IEEE80211_STYPE_DISASSOC: - if (priv->station_is_associated && - priv->operating_mode == IW_MODE_INFRA && - is_frame_from_current_bss(priv, header)) { - priv->station_was_associated = 0; - priv->station_is_associated = 0; - - atmel_enter_state(priv, STATION_STATE_JOINNING); - join(priv, BSS_TYPE_INFRASTRUCTURE); - } - - break; - - case IEEE80211_STYPE_DEAUTH: - if (priv->operating_mode == IW_MODE_INFRA && - is_frame_from_current_bss(priv, header)) { - priv->station_was_associated = 0; - - atmel_enter_state(priv, STATION_STATE_JOINNING); - join(priv, BSS_TYPE_INFRASTRUCTURE); - } - - break; - } -} - -/* run when timer expires */ -static void atmel_management_timer(u_long a) -{ - struct net_device *dev = (struct net_device *) a; - struct atmel_private *priv = netdev_priv(dev); - unsigned long flags; - - /* Check if the card has been yanked. */ - if (priv->card && priv->present_callback && - !(*priv->present_callback)(priv->card)) - return; - - spin_lock_irqsave(&priv->irqlock, flags); - - switch (priv->station_state) { - - case STATION_STATE_AUTHENTICATING: - if (priv->AuthenticationRequestRetryCnt >= MAX_AUTHENTICATION_RETRIES) { - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - priv->AuthenticationRequestRetryCnt = 0; - restart_search(priv); - } else { - int auth = WLAN_AUTH_OPEN; - priv->AuthenticationRequestRetryCnt++; - priv->CurrentAuthentTransactionSeqNum = 0x0001; - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - if (priv->wep_is_on && priv->exclude_unencrypted) - auth = WLAN_AUTH_SHARED_KEY; - send_authentication_request(priv, auth, NULL, 0); - } - break; - - case STATION_STATE_ASSOCIATING: - if (priv->AssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - priv->AssociationRequestRetryCnt = 0; - restart_search(priv); - } else { - priv->AssociationRequestRetryCnt++; - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - send_association_request(priv, 0); - } - break; - - case STATION_STATE_REASSOCIATING: - if (priv->ReAssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - priv->ReAssociationRequestRetryCnt = 0; - restart_search(priv); - } else { - priv->ReAssociationRequestRetryCnt++; - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - send_association_request(priv, 1); - } - break; - - default: - break; - } - - spin_unlock_irqrestore(&priv->irqlock, flags); -} - -static void atmel_command_irq(struct atmel_private *priv) -{ - u8 status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); - u8 command = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET)); - int fast_scan; - union iwreq_data wrqu; - - if (status == CMD_STATUS_IDLE || - status == CMD_STATUS_IN_PROGRESS) - return; - - switch (command) { - case CMD_Start: - if (status == CMD_STATUS_COMPLETE) { - priv->station_was_associated = priv->station_is_associated; - atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, - (u8 *)priv->CurrentBSSID, 6); - atmel_enter_state(priv, STATION_STATE_READY); - } - break; - - case CMD_Scan: - fast_scan = priv->fast_scan; - priv->fast_scan = 0; - - if (status != CMD_STATUS_COMPLETE) { - atmel_scan(priv, 1); - } else { - int bss_index = retrieve_bss(priv); - int notify_scan_complete = 1; - if (bss_index != -1) { - atmel_join_bss(priv, bss_index); - } else if (priv->operating_mode == IW_MODE_ADHOC && - priv->SSID_size != 0) { - start(priv, BSS_TYPE_AD_HOC); - } else { - priv->fast_scan = !fast_scan; - atmel_scan(priv, 1); - notify_scan_complete = 0; - } - priv->site_survey_state = SITE_SURVEY_COMPLETED; - if (notify_scan_complete) { - wrqu.data.length = 0; - wrqu.data.flags = 0; - wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); - } - } - break; - - case CMD_SiteSurvey: - priv->fast_scan = 0; - - if (status != CMD_STATUS_COMPLETE) - return; - - priv->site_survey_state = SITE_SURVEY_COMPLETED; - if (priv->station_is_associated) { - atmel_enter_state(priv, STATION_STATE_READY); - wrqu.data.length = 0; - wrqu.data.flags = 0; - wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); - } else { - atmel_scan(priv, 1); - } - break; - - case CMD_Join: - if (status == CMD_STATUS_COMPLETE) { - if (priv->operating_mode == IW_MODE_ADHOC) { - priv->station_was_associated = priv->station_is_associated; - atmel_enter_state(priv, STATION_STATE_READY); - } else { - int auth = WLAN_AUTH_OPEN; - priv->AuthenticationRequestRetryCnt = 0; - atmel_enter_state(priv, STATION_STATE_AUTHENTICATING); - - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - priv->CurrentAuthentTransactionSeqNum = 0x0001; - if (priv->wep_is_on && priv->exclude_unencrypted) - auth = WLAN_AUTH_SHARED_KEY; - send_authentication_request(priv, auth, NULL, 0); - } - return; - } - - atmel_scan(priv, 1); - } -} - -static int atmel_wakeup_firmware(struct atmel_private *priv) -{ - struct host_info_struct *iface = &priv->host_info; - u16 mr1, mr3; - int i; - - if (priv->card_type == CARD_TYPE_SPI_FLASH) - atmel_set_gcr(priv->dev, GCR_REMAP); - - /* wake up on-board processor */ - atmel_clear_gcr(priv->dev, 0x0040); - atmel_write16(priv->dev, BSR, BSS_SRAM); - - if (priv->card_type == CARD_TYPE_SPI_FLASH) - mdelay(100); - - /* and wait for it */ - for (i = LOOP_RETRY_LIMIT; i; i--) { - mr1 = atmel_read16(priv->dev, MR1); - mr3 = atmel_read16(priv->dev, MR3); - - if (mr3 & MAC_BOOT_COMPLETE) - break; - if (mr1 & MAC_BOOT_COMPLETE && - priv->bus_type == BUS_TYPE_PCCARD) - break; - } - - if (i == 0) { - printk(KERN_ALERT "%s: MAC failed to boot.\n", priv->dev->name); - return -EIO; - } - - if ((priv->host_info_base = atmel_read16(priv->dev, MR2)) == 0xffff) { - printk(KERN_ALERT "%s: card missing.\n", priv->dev->name); - return -ENODEV; - } - - /* now check for completion of MAC initialization through - the FunCtrl field of the IFACE, poll MR1 to detect completion of - MAC initialization, check completion status, set interrupt mask, - enables interrupts and calls Tx and Rx initialization functions */ - - atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), FUNC_CTRL_INIT_COMPLETE); - - for (i = LOOP_RETRY_LIMIT; i; i--) { - mr1 = atmel_read16(priv->dev, MR1); - mr3 = atmel_read16(priv->dev, MR3); - - if (mr3 & MAC_INIT_COMPLETE) - break; - if (mr1 & MAC_INIT_COMPLETE && - priv->bus_type == BUS_TYPE_PCCARD) - break; - } - - if (i == 0) { - printk(KERN_ALERT "%s: MAC failed to initialise.\n", - priv->dev->name); - return -EIO; - } - - /* Check for MAC_INIT_OK only on the register that the MAC_INIT_OK was set */ - if ((mr3 & MAC_INIT_COMPLETE) && - !(atmel_read16(priv->dev, MR3) & MAC_INIT_OK)) { - printk(KERN_ALERT "%s: MAC failed MR3 self-test.\n", priv->dev->name); - return -EIO; - } - if ((mr1 & MAC_INIT_COMPLETE) && - !(atmel_read16(priv->dev, MR1) & MAC_INIT_OK)) { - printk(KERN_ALERT "%s: MAC failed MR1 self-test.\n", priv->dev->name); - return -EIO; - } - - atmel_copy_to_host(priv->dev, (unsigned char *)iface, - priv->host_info_base, sizeof(*iface)); - - iface->tx_buff_pos = le16_to_cpu(iface->tx_buff_pos); - iface->tx_buff_size = le16_to_cpu(iface->tx_buff_size); - iface->tx_desc_pos = le16_to_cpu(iface->tx_desc_pos); - iface->tx_desc_count = le16_to_cpu(iface->tx_desc_count); - iface->rx_buff_pos = le16_to_cpu(iface->rx_buff_pos); - iface->rx_buff_size = le16_to_cpu(iface->rx_buff_size); - iface->rx_desc_pos = le16_to_cpu(iface->rx_desc_pos); - iface->rx_desc_count = le16_to_cpu(iface->rx_desc_count); - iface->build_version = le16_to_cpu(iface->build_version); - iface->command_pos = le16_to_cpu(iface->command_pos); - iface->major_version = le16_to_cpu(iface->major_version); - iface->minor_version = le16_to_cpu(iface->minor_version); - iface->func_ctrl = le16_to_cpu(iface->func_ctrl); - iface->mac_status = le16_to_cpu(iface->mac_status); - - return 0; -} - -/* determine type of memory and MAC address */ -static int probe_atmel_card(struct net_device *dev) -{ - int rc = 0; - struct atmel_private *priv = netdev_priv(dev); - - /* reset pccard */ - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(dev, GCR, 0x0060); - - atmel_write16(dev, GCR, 0x0040); - mdelay(500); - - if (atmel_read16(dev, MR2) == 0) { - /* No stored firmware so load a small stub which just - tells us the MAC address */ - int i; - priv->card_type = CARD_TYPE_EEPROM; - atmel_write16(dev, BSR, BSS_IRAM); - atmel_copy_to_card(dev, 0, mac_reader, sizeof(mac_reader)); - atmel_set_gcr(dev, GCR_REMAP); - atmel_clear_gcr(priv->dev, 0x0040); - atmel_write16(dev, BSR, BSS_SRAM); - for (i = LOOP_RETRY_LIMIT; i; i--) - if (atmel_read16(dev, MR3) & MAC_BOOT_COMPLETE) - break; - if (i == 0) { - printk(KERN_ALERT "%s: MAC failed to boot MAC address reader.\n", dev->name); - } else { - atmel_copy_to_host(dev, dev->dev_addr, atmel_read16(dev, MR2), 6); - /* got address, now squash it again until the network - interface is opened */ - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(dev, GCR, 0x0060); - atmel_write16(dev, GCR, 0x0040); - rc = 1; - } - } else if (atmel_read16(dev, MR4) == 0) { - /* Mac address easy in this case. */ - priv->card_type = CARD_TYPE_PARALLEL_FLASH; - atmel_write16(dev, BSR, 1); - atmel_copy_to_host(dev, dev->dev_addr, 0xc000, 6); - atmel_write16(dev, BSR, 0x200); - rc = 1; - } else { - /* Standard firmware in flash, boot it up and ask - for the Mac Address */ - priv->card_type = CARD_TYPE_SPI_FLASH; - if (atmel_wakeup_firmware(priv) == 0) { - atmel_get_mib(priv, Mac_Address_Mib_Type, 0, dev->dev_addr, 6); - - /* got address, now squash it again until the network - interface is opened */ - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(dev, GCR, 0x0060); - atmel_write16(dev, GCR, 0x0040); - rc = 1; - } - } - - if (rc) { - if (dev->dev_addr[0] == 0xFF) { - static const u8 default_mac[] = { - 0x00, 0x04, 0x25, 0x00, 0x00, 0x00 - }; - printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name); - memcpy(dev->dev_addr, default_mac, ETH_ALEN); - } - } - - return rc; -} - -/* Move the encyption information on the MIB structure. - This routine is for the pre-WPA firmware: later firmware has - a different format MIB and a different routine. */ -static void build_wep_mib(struct atmel_private *priv) -{ - struct { /* NB this is matched to the hardware, don't change. */ - u8 wep_is_on; - u8 default_key; /* 0..3 */ - u8 reserved; - u8 exclude_unencrypted; - - u32 WEPICV_error_count; - u32 WEP_excluded_count; - - u8 wep_keys[MAX_ENCRYPTION_KEYS][13]; - u8 encryption_level; /* 0, 1, 2 */ - u8 reserved2[3]; - } mib; - int i; - - mib.wep_is_on = priv->wep_is_on; - if (priv->wep_is_on) { - if (priv->wep_key_len[priv->default_key] > 5) - mib.encryption_level = 2; - else - mib.encryption_level = 1; - } else { - mib.encryption_level = 0; - } - - mib.default_key = priv->default_key; - mib.exclude_unencrypted = priv->exclude_unencrypted; - - for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) - memcpy(mib.wep_keys[i], priv->wep_keys[i], 13); - - atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); -} - -static void build_wpa_mib(struct atmel_private *priv) -{ - /* This is for the later (WPA enabled) firmware. */ - - struct { /* NB this is matched to the hardware, don't change. */ - u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; - u8 receiver_address[ETH_ALEN]; - u8 wep_is_on; - u8 default_key; /* 0..3 */ - u8 group_key; - u8 exclude_unencrypted; - u8 encryption_type; - u8 reserved; - - u32 WEPICV_error_count; - u32 WEP_excluded_count; - - u8 key_RSC[4][8]; - } mib; - - int i; - - mib.wep_is_on = priv->wep_is_on; - mib.exclude_unencrypted = priv->exclude_unencrypted; - memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN); - - /* zero all the keys before adding in valid ones. */ - memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value)); - - if (priv->wep_is_on) { - /* There's a comment in the Atmel code to the effect that this - is only valid when still using WEP, it may need to be set to - something to use WPA */ - memset(mib.key_RSC, 0, sizeof(mib.key_RSC)); - - mib.default_key = mib.group_key = 255; - for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) { - if (priv->wep_key_len[i] > 0) { - memcpy(mib.cipher_default_key_value[i], priv->wep_keys[i], MAX_ENCRYPTION_KEY_SIZE); - if (i == priv->default_key) { - mib.default_key = i; - mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 7; - mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->pairwise_cipher_suite; - } else { - mib.group_key = i; - priv->group_cipher_suite = priv->pairwise_cipher_suite; - mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 1; - mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->group_cipher_suite; - } - } - } - if (mib.default_key == 255) - mib.default_key = mib.group_key != 255 ? mib.group_key : 0; - if (mib.group_key == 255) - mib.group_key = mib.default_key; - - } - - atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); -} - -static int reset_atmel_card(struct net_device *dev) -{ - /* do everything necessary to wake up the hardware, including - waiting for the lightning strike and throwing the knife switch.... - - set all the Mib values which matter in the card to match - their settings in the atmel_private structure. Some of these - can be altered on the fly, but many (WEP, infrastucture or ad-hoc) - can only be changed by tearing down the world and coming back through - here. - - This routine is also responsible for initialising some - hardware-specific fields in the atmel_private structure, - including a copy of the firmware's hostinfo structure - which is the route into the rest of the firmware datastructures. */ - - struct atmel_private *priv = netdev_priv(dev); - u8 configuration; - int old_state = priv->station_state; - int err = 0; - - /* data to add to the firmware names, in priority order - this implemenents firmware versioning */ - - static char *firmware_modifier[] = { - "-wpa", - "", - NULL - }; - - /* reset pccard */ - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(priv->dev, GCR, 0x0060); - - /* stop card , disable interrupts */ - atmel_write16(priv->dev, GCR, 0x0040); - - if (priv->card_type == CARD_TYPE_EEPROM) { - /* copy in firmware if needed */ - const struct firmware *fw_entry = NULL; - const unsigned char *fw; - int len = priv->firmware_length; - if (!(fw = priv->firmware)) { - if (priv->firmware_type == ATMEL_FW_TYPE_NONE) { - if (strlen(priv->firmware_id) == 0) { - printk(KERN_INFO - "%s: card type is unknown: assuming at76c502 firmware is OK.\n", - dev->name); - printk(KERN_INFO - "%s: if not, use the firmware= module parameter.\n", - dev->name); - strcpy(priv->firmware_id, "atmel_at76c502.bin"); - } - err = request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev); - if (err != 0) { - printk(KERN_ALERT - "%s: firmware %s is missing, cannot continue.\n", - dev->name, priv->firmware_id); - return err; - } - } else { - int fw_index = 0; - int success = 0; - - /* get firmware filename entry based on firmware type ID */ - while (fw_table[fw_index].fw_type != priv->firmware_type - && fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) - fw_index++; - - /* construct the actual firmware file name */ - if (fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) { - int i; - for (i = 0; firmware_modifier[i]; i++) { - snprintf(priv->firmware_id, 32, "%s%s.%s", fw_table[fw_index].fw_file, - firmware_modifier[i], fw_table[fw_index].fw_file_ext); - priv->firmware_id[31] = '\0'; - if (request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev) == 0) { - success = 1; - break; - } - } - } - if (!success) { - printk(KERN_ALERT - "%s: firmware %s is missing, cannot start.\n", - dev->name, priv->firmware_id); - priv->firmware_id[0] = '\0'; - return -ENOENT; - } - } - - fw = fw_entry->data; - len = fw_entry->size; - } - - if (len <= 0x6000) { - atmel_write16(priv->dev, BSR, BSS_IRAM); - atmel_copy_to_card(priv->dev, 0, fw, len); - atmel_set_gcr(priv->dev, GCR_REMAP); - } else { - /* Remap */ - atmel_set_gcr(priv->dev, GCR_REMAP); - atmel_write16(priv->dev, BSR, BSS_IRAM); - atmel_copy_to_card(priv->dev, 0, fw, 0x6000); - atmel_write16(priv->dev, BSR, 0x2ff); - atmel_copy_to_card(priv->dev, 0x8000, &fw[0x6000], len - 0x6000); - } - - release_firmware(fw_entry); - } - - err = atmel_wakeup_firmware(priv); - if (err != 0) - return err; - - /* Check the version and set the correct flag for wpa stuff, - old and new firmware is incompatible. - The pre-wpa 3com firmware reports major version 5, - the wpa 3com firmware is major version 4 and doesn't need - the 3com broken-ness filter. */ - priv->use_wpa = (priv->host_info.major_version == 4); - priv->radio_on_broken = (priv->host_info.major_version == 5); - - /* unmask all irq sources */ - atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_MASK_OFFSET), 0xff); - - /* int Tx system and enable Tx */ - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, 0), 0); - atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, 0), 0x80000000L); - atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, 0), 0); - atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, 0), 0); - - priv->tx_desc_free = priv->host_info.tx_desc_count; - priv->tx_desc_head = 0; - priv->tx_desc_tail = 0; - priv->tx_desc_previous = 0; - priv->tx_free_mem = priv->host_info.tx_buff_size; - priv->tx_buff_head = 0; - priv->tx_buff_tail = 0; - - configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); - atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), - configuration | FUNC_CTRL_TxENABLE); - - /* init Rx system and enable */ - priv->rx_desc_head = 0; - - configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); - atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), - configuration | FUNC_CTRL_RxENABLE); - - if (!priv->radio_on_broken) { - if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) == - CMD_STATUS_REJECTED_RADIO_OFF) { - printk(KERN_INFO "%s: cannot turn the radio on.\n", - dev->name); - return -EIO; - } - } - - /* set up enough MIB values to run. */ - atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_AUTO_TX_RATE_POS, priv->auto_tx_rate); - atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_TX_PROMISCUOUS_POS, PROM_MODE_OFF); - atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_RTS_THRESHOLD_POS, priv->rts_threshold); - atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_FRAG_THRESHOLD_POS, priv->frag_threshold); - atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_SHORT_RETRY_POS, priv->short_retry); - atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_LONG_RETRY_POS, priv->long_retry); - atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, priv->preamble); - atmel_set_mib(priv, Mac_Address_Mib_Type, MAC_ADDR_MIB_MAC_ADDR_POS, - priv->dev->dev_addr, 6); - atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_BEACON_PER_POS, priv->default_beacon_period); - atmel_set_mib(priv, Phy_Mib_Type, PHY_MIB_RATE_SET_POS, atmel_basic_rates, 4); - atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_PRIVACY_POS, priv->wep_is_on); - if (priv->use_wpa) - build_wpa_mib(priv); - else - build_wep_mib(priv); - - if (old_state == STATION_STATE_READY) { - union iwreq_data wrqu; - - wrqu.data.length = 0; - wrqu.data.flags = 0; - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - eth_zero_addr(wrqu.ap_addr.sa_data); - wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); - } - - return 0; -} - -static void atmel_send_command(struct atmel_private *priv, int command, - void *cmd, int cmd_size) -{ - if (cmd) - atmel_copy_to_card(priv->dev, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET), - cmd, cmd_size); - - atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET), command); - atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET), 0); -} - -static int atmel_send_command_wait(struct atmel_private *priv, int command, - void *cmd, int cmd_size) -{ - int i, status; - - atmel_send_command(priv, command, cmd, cmd_size); - - for (i = 5000; i; i--) { - status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); - if (status != CMD_STATUS_IDLE && - status != CMD_STATUS_IN_PROGRESS) - break; - udelay(20); - } - - if (i == 0) { - printk(KERN_ALERT "%s: failed to contact MAC.\n", priv->dev->name); - status = CMD_STATUS_HOST_ERROR; - } else { - if (command != CMD_EnableRadio) - status = CMD_STATUS_COMPLETE; - } - - return status; -} - -static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index) -{ - struct get_set_mib m; - m.type = type; - m.size = 1; - m.index = index; - - atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + 1); - return atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE)); -} - -static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, u8 data) -{ - struct get_set_mib m; - m.type = type; - m.size = 1; - m.index = index; - m.data[0] = data; - - atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 1); -} - -static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, - u16 data) -{ - struct get_set_mib m; - m.type = type; - m.size = 2; - m.index = index; - m.data[0] = data; - m.data[1] = data >> 8; - - atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 2); -} - -static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, - u8 *data, int data_len) -{ - struct get_set_mib m; - m.type = type; - m.size = data_len; - m.index = index; - - if (data_len > MIB_MAX_DATA_BYTES) - printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); - - memcpy(m.data, data, data_len); - atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); -} - -static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, - u8 *data, int data_len) -{ - struct get_set_mib m; - m.type = type; - m.size = data_len; - m.index = index; - - if (data_len > MIB_MAX_DATA_BYTES) - printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); - - atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); - atmel_copy_to_host(priv->dev, data, - atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE), data_len); -} - -static void atmel_writeAR(struct net_device *dev, u16 data) -{ - int i; - outw(data, dev->base_addr + AR); - /* Address register appears to need some convincing..... */ - for (i = 0; data != inw(dev->base_addr + AR) && i < 10; i++) - outw(data, dev->base_addr + AR); -} - -static void atmel_copy_to_card(struct net_device *dev, u16 dest, - const unsigned char *src, u16 len) -{ - int i; - atmel_writeAR(dev, dest); - if (dest % 2) { - atmel_write8(dev, DR, *src); - src++; len--; - } - for (i = len; i > 1 ; i -= 2) { - u8 lb = *src++; - u8 hb = *src++; - atmel_write16(dev, DR, lb | (hb << 8)); - } - if (i) - atmel_write8(dev, DR, *src); -} - -static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, - u16 src, u16 len) -{ - int i; - atmel_writeAR(dev, src); - if (src % 2) { - *dest = atmel_read8(dev, DR); - dest++; len--; - } - for (i = len; i > 1 ; i -= 2) { - u16 hw = atmel_read16(dev, DR); - *dest++ = hw; - *dest++ = hw >> 8; - } - if (i) - *dest = atmel_read8(dev, DR); -} - -static void atmel_set_gcr(struct net_device *dev, u16 mask) -{ - outw(inw(dev->base_addr + GCR) | mask, dev->base_addr + GCR); -} - -static void atmel_clear_gcr(struct net_device *dev, u16 mask) -{ - outw(inw(dev->base_addr + GCR) & ~mask, dev->base_addr + GCR); -} - -static int atmel_lock_mac(struct atmel_private *priv) -{ - int i, j = 20; - retry: - for (i = 5000; i; i--) { - if (!atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) - break; - udelay(20); - } - - if (!i) - return 0; /* timed out */ - - atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 1); - if (atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) { - atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); - if (!j--) - return 0; /* timed out */ - goto retry; - } - - return 1; -} - -static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data) -{ - atmel_writeAR(priv->dev, pos); - atmel_write16(priv->dev, DR, data); /* card is little-endian */ - atmel_write16(priv->dev, DR, data >> 16); -} - -/***************************************************************************/ -/* There follows the source form of the MAC address reading firmware */ -/***************************************************************************/ -#if 0 - -/* Copyright 2003 Matthew T. Russotto */ -/* But derived from the Atmel 76C502 firmware written by Atmel and */ -/* included in "atmel wireless lan drivers" package */ -/** - This file is part of net.russotto.AtmelMACFW, hereto referred to - as AtmelMACFW - - AtmelMACFW is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 - as published by the Free Software Foundation. - - AtmelMACFW is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with AtmelMACFW; if not, see . - -****************************************************************************/ -/* This firmware should work on the 76C502 RFMD, RFMD_D, and RFMD_E */ -/* It will probably work on the 76C504 and 76C502 RFMD_3COM */ -/* It only works on SPI EEPROM versions of the card. */ - -/* This firmware initializes the SPI controller and clock, reads the MAC */ -/* address from the EEPROM into SRAM, and puts the SRAM offset of the MAC */ -/* address in MR2, and sets MR3 to 0x10 to indicate it is done */ -/* It also puts a complete copy of the EEPROM in SRAM with the offset in */ -/* MR4, for investigational purposes (maybe we can determine chip type */ -/* from that?) */ - - .org 0 - .set MRBASE, 0x8000000 - .set CPSR_INITIAL, 0xD3 /* IRQ/FIQ disabled, ARM mode, Supervisor state */ - .set CPSR_USER, 0xD1 /* IRQ/FIQ disabled, ARM mode, USER state */ - .set SRAM_BASE, 0x02000000 - .set SP_BASE, 0x0F300000 - .set UNK_BASE, 0x0F000000 /* Some internal device, but which one? */ - .set SPI_CGEN_BASE, 0x0E000000 /* Some internal device, but which one? */ - .set UNK3_BASE, 0x02014000 /* Some internal device, but which one? */ - .set STACK_BASE, 0x5600 - .set SP_SR, 0x10 - .set SP_TDRE, 2 /* status register bit -- TDR empty */ - .set SP_RDRF, 1 /* status register bit -- RDR full */ - .set SP_SWRST, 0x80 - .set SP_SPIEN, 0x1 - .set SP_CR, 0 /* control register */ - .set SP_MR, 4 /* mode register */ - .set SP_RDR, 0x08 /* Read Data Register */ - .set SP_TDR, 0x0C /* Transmit Data Register */ - .set SP_CSR0, 0x30 /* chip select registers */ - .set SP_CSR1, 0x34 - .set SP_CSR2, 0x38 - .set SP_CSR3, 0x3C - .set NVRAM_CMD_RDSR, 5 /* read status register */ - .set NVRAM_CMD_READ, 3 /* read data */ - .set NVRAM_SR_RDY, 1 /* RDY bit. This bit is inverted */ - .set SPI_8CLOCKS, 0xFF /* Writing this to the TDR doesn't do anything to the - serial output, since SO is normally high. But it - does cause 8 clock cycles and thus 8 bits to be - clocked in to the chip. See Atmel's SPI - controller (e.g. AT91M55800) timing and 4K - SPI EEPROM manuals */ - - .set NVRAM_SCRATCH, 0x02000100 /* arbitrary area for scratchpad memory */ - .set NVRAM_IMAGE, 0x02000200 - .set NVRAM_LENGTH, 0x0200 - .set MAC_ADDRESS_MIB, SRAM_BASE - .set MAC_ADDRESS_LENGTH, 6 - .set MAC_BOOT_FLAG, 0x10 - .set MR1, 0 - .set MR2, 4 - .set MR3, 8 - .set MR4, 0xC -RESET_VECTOR: - b RESET_HANDLER -UNDEF_VECTOR: - b HALT1 -SWI_VECTOR: - b HALT1 -IABORT_VECTOR: - b HALT1 -DABORT_VECTOR: -RESERVED_VECTOR: - b HALT1 -IRQ_VECTOR: - b HALT1 -FIQ_VECTOR: - b HALT1 -HALT1: b HALT1 -RESET_HANDLER: - mov r0, #CPSR_INITIAL - msr CPSR_c, r0 /* This is probably unnecessary */ - -/* I'm guessing this is initializing clock generator electronics for SPI */ - ldr r0, =SPI_CGEN_BASE - mov r1, #0 - mov r1, r1, lsl #3 - orr r1, r1, #0 - str r1, [r0] - ldr r1, [r0, #28] - bic r1, r1, #16 - str r1, [r0, #28] - mov r1, #1 - str r1, [r0, #8] - - ldr r0, =MRBASE - mov r1, #0 - strh r1, [r0, #MR1] - strh r1, [r0, #MR2] - strh r1, [r0, #MR3] - strh r1, [r0, #MR4] - - mov sp, #STACK_BASE - bl SP_INIT - mov r0, #10 - bl DELAY9 - bl GET_MAC_ADDR - bl GET_WHOLE_NVRAM - ldr r0, =MRBASE - ldr r1, =MAC_ADDRESS_MIB - strh r1, [r0, #MR2] - ldr r1, =NVRAM_IMAGE - strh r1, [r0, #MR4] - mov r1, #MAC_BOOT_FLAG - strh r1, [r0, #MR3] -HALT2: b HALT2 -.func Get_Whole_NVRAM, GET_WHOLE_NVRAM -GET_WHOLE_NVRAM: - stmdb sp!, {lr} - mov r2, #0 /* 0th bytes of NVRAM */ - mov r3, #NVRAM_LENGTH - mov r1, #0 /* not used in routine */ - ldr r0, =NVRAM_IMAGE - bl NVRAM_XFER - ldmia sp!, {lr} - bx lr -.endfunc - -.func Get_MAC_Addr, GET_MAC_ADDR -GET_MAC_ADDR: - stmdb sp!, {lr} - mov r2, #0x120 /* address of MAC Address within NVRAM */ - mov r3, #MAC_ADDRESS_LENGTH - mov r1, #0 /* not used in routine */ - ldr r0, =MAC_ADDRESS_MIB - bl NVRAM_XFER - ldmia sp!, {lr} - bx lr -.endfunc -.ltorg -.func Delay9, DELAY9 -DELAY9: - adds r0, r0, r0, LSL #3 /* r0 = r0 * 9 */ -DELAYLOOP: - beq DELAY9_done - subs r0, r0, #1 - b DELAYLOOP -DELAY9_done: - bx lr -.endfunc - -.func SP_Init, SP_INIT -SP_INIT: - mov r1, #SP_SWRST - ldr r0, =SP_BASE - str r1, [r0, #SP_CR] /* reset the SPI */ - mov r1, #0 - str r1, [r0, #SP_CR] /* release SPI from reset state */ - mov r1, #SP_SPIEN - str r1, [r0, #SP_MR] /* set the SPI to MASTER mode*/ - str r1, [r0, #SP_CR] /* enable the SPI */ - -/* My guess would be this turns on the SPI clock */ - ldr r3, =SPI_CGEN_BASE - ldr r1, [r3, #28] - orr r1, r1, #0x2000 - str r1, [r3, #28] - - ldr r1, =0x2000c01 - str r1, [r0, #SP_CSR0] - ldr r1, =0x2000201 - str r1, [r0, #SP_CSR1] - str r1, [r0, #SP_CSR2] - str r1, [r0, #SP_CSR3] - ldr r1, [r0, #SP_SR] - ldr r0, [r0, #SP_RDR] - bx lr -.endfunc -.func NVRAM_Init, NVRAM_INIT -NVRAM_INIT: - ldr r1, =SP_BASE - ldr r0, [r1, #SP_RDR] - mov r0, #NVRAM_CMD_RDSR - str r0, [r1, #SP_TDR] -SP_loop1: - ldr r0, [r1, #SP_SR] - tst r0, #SP_TDRE - beq SP_loop1 - - mov r0, #SPI_8CLOCKS - str r0, [r1, #SP_TDR] -SP_loop2: - ldr r0, [r1, #SP_SR] - tst r0, #SP_TDRE - beq SP_loop2 - - ldr r0, [r1, #SP_RDR] -SP_loop3: - ldr r0, [r1, #SP_SR] - tst r0, #SP_RDRF - beq SP_loop3 - - ldr r0, [r1, #SP_RDR] - and r0, r0, #255 - bx lr -.endfunc - -.func NVRAM_Xfer, NVRAM_XFER - /* r0 = dest address */ - /* r1 = not used */ - /* r2 = src address within NVRAM */ - /* r3 = length */ -NVRAM_XFER: - stmdb sp!, {r4, r5, lr} - mov r5, r0 /* save r0 (dest address) */ - mov r4, r3 /* save r3 (length) */ - mov r0, r2, LSR #5 /* SPI memories put A8 in the command field */ - and r0, r0, #8 - add r0, r0, #NVRAM_CMD_READ - ldr r1, =NVRAM_SCRATCH - strb r0, [r1, #0] /* save command in NVRAM_SCRATCH[0] */ - strb r2, [r1, #1] /* save low byte of source address in NVRAM_SCRATCH[1] */ -_local1: - bl NVRAM_INIT - tst r0, #NVRAM_SR_RDY - bne _local1 - mov r0, #20 - bl DELAY9 - mov r2, r4 /* length */ - mov r1, r5 /* dest address */ - mov r0, #2 /* bytes to transfer in command */ - bl NVRAM_XFER2 - ldmia sp!, {r4, r5, lr} - bx lr -.endfunc - -.func NVRAM_Xfer2, NVRAM_XFER2 -NVRAM_XFER2: - stmdb sp!, {r4, r5, r6, lr} - ldr r4, =SP_BASE - mov r3, #0 - cmp r0, #0 - bls _local2 - ldr r5, =NVRAM_SCRATCH -_local4: - ldrb r6, [r5, r3] - str r6, [r4, #SP_TDR] -_local3: - ldr r6, [r4, #SP_SR] - tst r6, #SP_TDRE - beq _local3 - add r3, r3, #1 - cmp r3, r0 /* r0 is # of bytes to send out (command+addr) */ - blo _local4 -_local2: - mov r3, #SPI_8CLOCKS - str r3, [r4, #SP_TDR] - ldr r0, [r4, #SP_RDR] -_local5: - ldr r0, [r4, #SP_SR] - tst r0, #SP_RDRF - beq _local5 - ldr r0, [r4, #SP_RDR] /* what's this byte? It's the byte read while writing the TDR -- nonsense, because the NVRAM doesn't read and write at the same time */ - mov r0, #0 - cmp r2, #0 /* r2 is # of bytes to copy in */ - bls _local6 -_local7: - ldr r5, [r4, #SP_SR] - tst r5, #SP_TDRE - beq _local7 - str r3, [r4, #SP_TDR] /* r3 has SPI_8CLOCKS */ -_local8: - ldr r5, [r4, #SP_SR] - tst r5, #SP_RDRF - beq _local8 - ldr r5, [r4, #SP_RDR] /* but didn't we read this byte above? */ - strb r5, [r1], #1 /* postindexed */ - add r0, r0, #1 - cmp r0, r2 - blo _local7 /* since we don't send another address, the NVRAM must be capable of sequential reads */ -_local6: - mov r0, #200 - bl DELAY9 - ldmia sp!, {r4, r5, r6, lr} - bx lr -#endif diff --git a/drivers/net/wireless/atmel.h b/drivers/net/wireless/atmel.h deleted file mode 100644 index 96f7318cbb04..000000000000 --- a/drivers/net/wireless/atmel.h +++ /dev/null @@ -1,43 +0,0 @@ -/*** -*- linux-c -*- ********************************************************** - - Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. - - Copyright 2005 Dan Williams and Red Hat, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This software is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, see - . - -******************************************************************************/ - -#ifndef _ATMEL_H -#define _ATMEL_H - -typedef enum { - ATMEL_FW_TYPE_NONE = 0, - ATMEL_FW_TYPE_502, - ATMEL_FW_TYPE_502D, - ATMEL_FW_TYPE_502E, - ATMEL_FW_TYPE_502_3COM, - ATMEL_FW_TYPE_504, - ATMEL_FW_TYPE_504_2958, - ATMEL_FW_TYPE_504A_2958, - ATMEL_FW_TYPE_506 -} AtmelFWType; - -struct net_device *init_atmel_card(unsigned short, unsigned long, const AtmelFWType, struct device *, - int (*present_func)(void *), void * ); -void stop_atmel_card( struct net_device *); -int atmel_open( struct net_device * ); - -#endif diff --git a/drivers/net/wireless/atmel/Kconfig b/drivers/net/wireless/atmel/Kconfig new file mode 100644 index 000000000000..a43cfd163254 --- /dev/null +++ b/drivers/net/wireless/atmel/Kconfig @@ -0,0 +1,57 @@ +config WLAN_VENDOR_ATMEL + bool "Atmel devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_ATMEL + +config ATMEL + tristate "Atmel at76c50x chipset 802.11b support" + depends on CFG80211 && (PCI || PCMCIA) + select WIRELESS_EXT + select WEXT_PRIV + select FW_LOADER + select CRC32 + ---help--- + A driver 802.11b wireless cards based on the Atmel fast-vnet + chips. This driver supports standard Linux wireless extensions. + + Many cards based on this chipset do not have flash memory + and need their firmware loaded at start-up. If yours is + one of these, you will need to provide a firmware image + to be loaded into the card by the driver. The Atmel + firmware package can be downloaded from + + +config PCI_ATMEL + tristate "Atmel at76c506 PCI cards" + depends on ATMEL && PCI + ---help--- + Enable support for PCI and mini-PCI cards containing the + Atmel at76c506 chip. + +config PCMCIA_ATMEL + tristate "Atmel at76c502/at76c504 PCMCIA cards" + depends on ATMEL && PCMCIA + select WIRELESS_EXT + select FW_LOADER + select CRC32 + ---help--- + Enable support for PCMCIA cards containing the + Atmel at76c502 and at76c504 chips. + +config AT76C50X_USB + tristate "Atmel at76c503/at76c505/at76c505a USB cards" + depends on MAC80211 && USB + select FW_LOADER + ---help--- + Enable support for USB Wireless devices using Atmel at76c503, + at76c505 or at76c505a chips. + +endif # WLAN_VENDOR_ATMEL diff --git a/drivers/net/wireless/atmel/Makefile b/drivers/net/wireless/atmel/Makefile new file mode 100644 index 000000000000..e62e345f7af6 --- /dev/null +++ b/drivers/net/wireless/atmel/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_ATMEL) += atmel.o +obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o +obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o + +obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c new file mode 100644 index 000000000000..dab25136214a --- /dev/null +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -0,0 +1,2617 @@ +/* + * at76c503/at76c505 USB driver + * + * Copyright (c) 2002 - 2003 Oliver Kurth + * Copyright (c) 2004 Joerg Albert + * Copyright (c) 2004 Nick Jones + * Copyright (c) 2004 Balint Seeber + * Copyright (c) 2007 Guido Guenther + * Copyright (c) 2007 Kalle Valo + * Copyright (c) 2010 Sebastian Smolorz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This file is part of the Berlios driver for WLAN USB devices based on the + * Atmel AT76C503A/505/505A. + * + * Some iw_handler code was taken from airo.c, (C) 1999 Benjamin Reed + * + * TODO list is at the wiki: + * + * http://wireless.kernel.org/en/users/Drivers/at76c50x-usb#TODO + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "at76c50x-usb.h" + +/* Version information */ +#define DRIVER_NAME "at76c50x-usb" +#define DRIVER_VERSION "0.17" +#define DRIVER_DESC "Atmel at76x USB Wireless LAN Driver" + +/* at76_debug bits */ +#define DBG_PROGRESS 0x00000001 /* authentication/accociation */ +#define DBG_BSS_TABLE 0x00000002 /* show BSS table after scans */ +#define DBG_IOCTL 0x00000004 /* ioctl calls / settings */ +#define DBG_MAC_STATE 0x00000008 /* MAC state transitions */ +#define DBG_TX_DATA 0x00000010 /* tx header */ +#define DBG_TX_DATA_CONTENT 0x00000020 /* tx content */ +#define DBG_TX_MGMT 0x00000040 /* tx management */ +#define DBG_RX_DATA 0x00000080 /* rx data header */ +#define DBG_RX_DATA_CONTENT 0x00000100 /* rx data content */ +#define DBG_RX_MGMT 0x00000200 /* rx mgmt frame headers */ +#define DBG_RX_BEACON 0x00000400 /* rx beacon */ +#define DBG_RX_CTRL 0x00000800 /* rx control */ +#define DBG_RX_MGMT_CONTENT 0x00001000 /* rx mgmt content */ +#define DBG_RX_FRAGS 0x00002000 /* rx data fragment handling */ +#define DBG_DEVSTART 0x00004000 /* fw download, device start */ +#define DBG_URB 0x00008000 /* rx urb status, ... */ +#define DBG_RX_ATMEL_HDR 0x00010000 /* Atmel-specific Rx headers */ +#define DBG_PROC_ENTRY 0x00020000 /* procedure entries/exits */ +#define DBG_PM 0x00040000 /* power management settings */ +#define DBG_BSS_MATCH 0x00080000 /* BSS match failures */ +#define DBG_PARAMS 0x00100000 /* show configured parameters */ +#define DBG_WAIT_COMPLETE 0x00200000 /* command completion */ +#define DBG_RX_FRAGS_SKB 0x00400000 /* skb header of Rx fragments */ +#define DBG_BSS_TABLE_RM 0x00800000 /* purging bss table entries */ +#define DBG_MONITOR_MODE 0x01000000 /* monitor mode */ +#define DBG_MIB 0x02000000 /* dump all MIBs on startup */ +#define DBG_MGMT_TIMER 0x04000000 /* dump mgmt_timer ops */ +#define DBG_WE_EVENTS 0x08000000 /* dump wireless events */ +#define DBG_FW 0x10000000 /* firmware download */ +#define DBG_DFU 0x20000000 /* device firmware upgrade */ +#define DBG_CMD 0x40000000 +#define DBG_MAC80211 0x80000000 + +#define DBG_DEFAULTS 0 + +/* Use our own dbg macro */ +#define at76_dbg(bits, format, arg...) \ +do { \ + if (at76_debug & (bits)) \ + printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ +} while (0) + +#define at76_dbg_dump(bits, buf, len, format, arg...) \ +do { \ + if (at76_debug & (bits)) { \ + printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); \ + } \ +} while (0) + +static uint at76_debug = DBG_DEFAULTS; + +/* Protect against concurrent firmware loading and parsing */ +static struct mutex fw_mutex; + +static struct fwentry firmwares[] = { + [0] = { "" }, + [BOARD_503_ISL3861] = { "atmel_at76c503-i3861.bin" }, + [BOARD_503_ISL3863] = { "atmel_at76c503-i3863.bin" }, + [BOARD_503] = { "atmel_at76c503-rfmd.bin" }, + [BOARD_503_ACC] = { "atmel_at76c503-rfmd-acc.bin" }, + [BOARD_505] = { "atmel_at76c505-rfmd.bin" }, + [BOARD_505_2958] = { "atmel_at76c505-rfmd2958.bin" }, + [BOARD_505A] = { "atmel_at76c505a-rfmd2958.bin" }, + [BOARD_505AMX] = { "atmel_at76c505amx-rfmd.bin" }, +}; +MODULE_FIRMWARE("atmel_at76c503-i3861.bin"); +MODULE_FIRMWARE("atmel_at76c503-i3863.bin"); +MODULE_FIRMWARE("atmel_at76c503-rfmd.bin"); +MODULE_FIRMWARE("atmel_at76c503-rfmd-acc.bin"); +MODULE_FIRMWARE("atmel_at76c505-rfmd.bin"); +MODULE_FIRMWARE("atmel_at76c505-rfmd2958.bin"); +MODULE_FIRMWARE("atmel_at76c505a-rfmd2958.bin"); +MODULE_FIRMWARE("atmel_at76c505amx-rfmd.bin"); + +#define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) + +static struct usb_device_id dev_table[] = { + /* + * at76c503-i3861 + */ + /* Generic AT76C503/3861 device */ + { USB_DEVICE(0x03eb, 0x7603), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Linksys WUSB11 v2.1/v2.6 */ + { USB_DEVICE(0x066b, 0x2211), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Netgear MA101 rev. A */ + { USB_DEVICE(0x0864, 0x4100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Tekram U300C / Allnet ALL0193 */ + { USB_DEVICE(0x0b3b, 0x1612), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* HP HN210W J7801A */ + { USB_DEVICE(0x03f0, 0x011c), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Sitecom/Z-Com/Zyxel M4Y-750 */ + { USB_DEVICE(0x0cde, 0x0001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Dynalink/Askey WLL013 (intersil) */ + { USB_DEVICE(0x069a, 0x0320), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* EZ connect 11Mpbs Wireless USB Adapter SMC2662W v1 */ + { USB_DEVICE(0x0d5c, 0xa001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* BenQ AWL300 */ + { USB_DEVICE(0x04a5, 0x9000), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Addtron AWU-120, Compex WLU11 */ + { USB_DEVICE(0x05dd, 0xff31), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Intel AP310 AnyPoint II USB */ + { USB_DEVICE(0x8086, 0x0200), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Dynalink L11U */ + { USB_DEVICE(0x0d8e, 0x7100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Arescom WL-210, FCC id 07J-GL2411USB */ + { USB_DEVICE(0x0d8e, 0x7110), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* I-O DATA WN-B11/USB */ + { USB_DEVICE(0x04bb, 0x0919), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* BT Voyager 1010 */ + { USB_DEVICE(0x069a, 0x0821), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* + * at76c503-i3863 + */ + /* Generic AT76C503/3863 device */ + { USB_DEVICE(0x03eb, 0x7604), USB_DEVICE_DATA(BOARD_503_ISL3863) }, + /* Samsung SWL-2100U */ + { USB_DEVICE(0x055d, 0xa000), USB_DEVICE_DATA(BOARD_503_ISL3863) }, + /* + * at76c503-rfmd + */ + /* Generic AT76C503/RFMD device */ + { USB_DEVICE(0x03eb, 0x7605), USB_DEVICE_DATA(BOARD_503) }, + /* Dynalink/Askey WLL013 (rfmd) */ + { USB_DEVICE(0x069a, 0x0321), USB_DEVICE_DATA(BOARD_503) }, + /* Linksys WUSB11 v2.6 */ + { USB_DEVICE(0x077b, 0x2219), USB_DEVICE_DATA(BOARD_503) }, + /* Network Everywhere NWU11B */ + { USB_DEVICE(0x077b, 0x2227), USB_DEVICE_DATA(BOARD_503) }, + /* Netgear MA101 rev. B */ + { USB_DEVICE(0x0864, 0x4102), USB_DEVICE_DATA(BOARD_503) }, + /* D-Link DWL-120 rev. E */ + { USB_DEVICE(0x2001, 0x3200), USB_DEVICE_DATA(BOARD_503) }, + /* Actiontec 802UAT1, HWU01150-01UK */ + { USB_DEVICE(0x1668, 0x7605), USB_DEVICE_DATA(BOARD_503) }, + /* AirVast W-Buddie WN210 */ + { USB_DEVICE(0x03eb, 0x4102), USB_DEVICE_DATA(BOARD_503) }, + /* Dick Smith Electronics XH1153 802.11b USB adapter */ + { USB_DEVICE(0x1371, 0x5743), USB_DEVICE_DATA(BOARD_503) }, + /* CNet CNUSB611 */ + { USB_DEVICE(0x1371, 0x0001), USB_DEVICE_DATA(BOARD_503) }, + /* FiberLine FL-WL200U */ + { USB_DEVICE(0x1371, 0x0002), USB_DEVICE_DATA(BOARD_503) }, + /* BenQ AWL400 USB stick */ + { USB_DEVICE(0x04a5, 0x9001), USB_DEVICE_DATA(BOARD_503) }, + /* 3Com 3CRSHEW696 */ + { USB_DEVICE(0x0506, 0x0a01), USB_DEVICE_DATA(BOARD_503) }, + /* Siemens Santis ADSL WLAN USB adapter WLL 013 */ + { USB_DEVICE(0x0681, 0x001b), USB_DEVICE_DATA(BOARD_503) }, + /* Belkin F5D6050, version 2 */ + { USB_DEVICE(0x050d, 0x0050), USB_DEVICE_DATA(BOARD_503) }, + /* iBlitzz, BWU613 (not *B or *SB) */ + { USB_DEVICE(0x07b8, 0xb000), USB_DEVICE_DATA(BOARD_503) }, + /* Gigabyte GN-WLBM101 */ + { USB_DEVICE(0x1044, 0x8003), USB_DEVICE_DATA(BOARD_503) }, + /* Planex GW-US11S */ + { USB_DEVICE(0x2019, 0x3220), USB_DEVICE_DATA(BOARD_503) }, + /* Internal WLAN adapter in h5[4,5]xx series iPAQs */ + { USB_DEVICE(0x049f, 0x0032), USB_DEVICE_DATA(BOARD_503) }, + /* Corega Wireless LAN USB-11 mini */ + { USB_DEVICE(0x07aa, 0x0011), USB_DEVICE_DATA(BOARD_503) }, + /* Corega Wireless LAN USB-11 mini2 */ + { USB_DEVICE(0x07aa, 0x0018), USB_DEVICE_DATA(BOARD_503) }, + /* Uniden PCW100 */ + { USB_DEVICE(0x05dd, 0xff35), USB_DEVICE_DATA(BOARD_503) }, + /* + * at76c503-rfmd-acc + */ + /* SMC2664W */ + { USB_DEVICE(0x083a, 0x3501), USB_DEVICE_DATA(BOARD_503_ACC) }, + /* Belkin F5D6050, SMC2662W v2, SMC2662W-AR */ + { USB_DEVICE(0x0d5c, 0xa002), USB_DEVICE_DATA(BOARD_503_ACC) }, + /* + * at76c505-rfmd + */ + /* Generic AT76C505/RFMD */ + { USB_DEVICE(0x03eb, 0x7606), USB_DEVICE_DATA(BOARD_505) }, + /* + * at76c505-rfmd2958 + */ + /* Generic AT76C505/RFMD, OvisLink WL-1130USB */ + { USB_DEVICE(0x03eb, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, + /* Fiberline FL-WL240U */ + { USB_DEVICE(0x1371, 0x0014), USB_DEVICE_DATA(BOARD_505_2958) }, + /* CNet CNUSB-611G */ + { USB_DEVICE(0x1371, 0x0013), USB_DEVICE_DATA(BOARD_505_2958) }, + /* Linksys WUSB11 v2.8 */ + { USB_DEVICE(0x1915, 0x2233), USB_DEVICE_DATA(BOARD_505_2958) }, + /* Xterasys XN-2122B, IBlitzz BWU613B/BWU613SB */ + { USB_DEVICE(0x12fd, 0x1001), USB_DEVICE_DATA(BOARD_505_2958) }, + /* Corega WLAN USB Stick 11 */ + { USB_DEVICE(0x07aa, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, + /* Microstar MSI Box MS6978 */ + { USB_DEVICE(0x0db0, 0x1020), USB_DEVICE_DATA(BOARD_505_2958) }, + /* + * at76c505a-rfmd2958 + */ + /* Generic AT76C505A device */ + { USB_DEVICE(0x03eb, 0x7614), USB_DEVICE_DATA(BOARD_505A) }, + /* Generic AT76C505AS device */ + { USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A) }, + /* Siemens Gigaset USB WLAN Adapter 11 */ + { USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A) }, + /* OQO Model 01+ Internal Wi-Fi */ + { USB_DEVICE(0x1557, 0x0002), USB_DEVICE_DATA(BOARD_505A) }, + /* + * at76c505amx-rfmd + */ + /* Generic AT76C505AMX device */ + { USB_DEVICE(0x03eb, 0x7615), USB_DEVICE_DATA(BOARD_505AMX) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, dev_table); + +/* Supported rates of this hardware, bit 7 marks basic rates */ +static const u8 hw_rates[] = { 0x82, 0x84, 0x0b, 0x16 }; + +static const char *const preambles[] = { "long", "short", "auto" }; + +/* Firmware download */ +/* DFU states */ +#define STATE_IDLE 0x00 +#define STATE_DETACH 0x01 +#define STATE_DFU_IDLE 0x02 +#define STATE_DFU_DOWNLOAD_SYNC 0x03 +#define STATE_DFU_DOWNLOAD_BUSY 0x04 +#define STATE_DFU_DOWNLOAD_IDLE 0x05 +#define STATE_DFU_MANIFEST_SYNC 0x06 +#define STATE_DFU_MANIFEST 0x07 +#define STATE_DFU_MANIFEST_WAIT_RESET 0x08 +#define STATE_DFU_UPLOAD_IDLE 0x09 +#define STATE_DFU_ERROR 0x0a + +/* DFU commands */ +#define DFU_DETACH 0 +#define DFU_DNLOAD 1 +#define DFU_UPLOAD 2 +#define DFU_GETSTATUS 3 +#define DFU_CLRSTATUS 4 +#define DFU_GETSTATE 5 +#define DFU_ABORT 6 + +#define FW_BLOCK_SIZE 1024 + +struct dfu_status { + unsigned char status; + unsigned char poll_timeout[3]; + unsigned char state; + unsigned char string; +} __packed; + +static inline int at76_is_intersil(enum board_type board) +{ + return (board == BOARD_503_ISL3861 || board == BOARD_503_ISL3863); +} + +static inline int at76_is_503rfmd(enum board_type board) +{ + return (board == BOARD_503 || board == BOARD_503_ACC); +} + +static inline int at76_is_505a(enum board_type board) +{ + return (board == BOARD_505A || board == BOARD_505AMX); +} + +/* Load a block of the first (internal) part of the firmware */ +static int at76_load_int_fw_block(struct usb_device *udev, int blockno, + void *block, int size) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), DFU_DNLOAD, + USB_TYPE_CLASS | USB_DIR_OUT | + USB_RECIP_INTERFACE, blockno, 0, block, size, + USB_CTRL_GET_TIMEOUT); +} + +static int at76_dfu_get_status(struct usb_device *udev, + struct dfu_status *status) +{ + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATUS, + USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + 0, 0, status, sizeof(struct dfu_status), + USB_CTRL_GET_TIMEOUT); + return ret; +} + +static int at76_dfu_get_state(struct usb_device *udev, u8 *state) +{ + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATE, + USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + 0, 0, state, 1, USB_CTRL_GET_TIMEOUT); + return ret; +} + +/* Convert timeout from the DFU status to jiffies */ +static inline unsigned long at76_get_timeout(struct dfu_status *s) +{ + return msecs_to_jiffies((s->poll_timeout[2] << 16) + | (s->poll_timeout[1] << 8) + | (s->poll_timeout[0])); +} + +/* Load internal firmware from the buffer. If manifest_sync_timeout > 0, use + * its value in jiffies in the MANIFEST_SYNC state. */ +static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, + int manifest_sync_timeout) +{ + int ret = 0; + int need_dfu_state = 1; + int is_done = 0; + u32 dfu_timeout = 0; + int bsize = 0; + int blockno = 0; + struct dfu_status *dfu_stat_buf = NULL; + u8 *dfu_state = NULL; + u8 *block = NULL; + + at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size, + manifest_sync_timeout); + + if (!size) { + dev_err(&udev->dev, "FW buffer length invalid!\n"); + return -EINVAL; + } + + dfu_stat_buf = kmalloc(sizeof(struct dfu_status), GFP_KERNEL); + if (!dfu_stat_buf) { + ret = -ENOMEM; + goto exit; + } + + block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); + if (!block) { + ret = -ENOMEM; + goto exit; + } + + dfu_state = kmalloc(sizeof(u8), GFP_KERNEL); + if (!dfu_state) { + ret = -ENOMEM; + goto exit; + } + *dfu_state = 0; + + do { + if (need_dfu_state) { + ret = at76_dfu_get_state(udev, dfu_state); + if (ret < 0) { + dev_err(&udev->dev, + "cannot get DFU state: %d\n", ret); + goto exit; + } + need_dfu_state = 0; + } + + switch (*dfu_state) { + case STATE_DFU_DOWNLOAD_SYNC: + at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC"); + ret = at76_dfu_get_status(udev, dfu_stat_buf); + if (ret >= 0) { + *dfu_state = dfu_stat_buf->state; + dfu_timeout = at76_get_timeout(dfu_stat_buf); + need_dfu_state = 0; + } else + dev_err(&udev->dev, + "at76_dfu_get_status returned %d\n", + ret); + break; + + case STATE_DFU_DOWNLOAD_BUSY: + at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_BUSY"); + need_dfu_state = 1; + + at76_dbg(DBG_DFU, "DFU: Resetting device"); + schedule_timeout_interruptible(dfu_timeout); + break; + + case STATE_DFU_DOWNLOAD_IDLE: + at76_dbg(DBG_DFU, "DOWNLOAD..."); + /* fall through */ + case STATE_DFU_IDLE: + at76_dbg(DBG_DFU, "DFU IDLE"); + + bsize = min_t(int, size, FW_BLOCK_SIZE); + memcpy(block, buf, bsize); + at76_dbg(DBG_DFU, "int fw, size left = %5d, " + "bsize = %4d, blockno = %2d", size, bsize, + blockno); + ret = + at76_load_int_fw_block(udev, blockno, block, bsize); + buf += bsize; + size -= bsize; + blockno++; + + if (ret != bsize) + dev_err(&udev->dev, + "at76_load_int_fw_block returned %d\n", + ret); + need_dfu_state = 1; + break; + + case STATE_DFU_MANIFEST_SYNC: + at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC"); + + ret = at76_dfu_get_status(udev, dfu_stat_buf); + if (ret < 0) + break; + + *dfu_state = dfu_stat_buf->state; + dfu_timeout = at76_get_timeout(dfu_stat_buf); + need_dfu_state = 0; + + /* override the timeout from the status response, + needed for AT76C505A */ + if (manifest_sync_timeout > 0) + dfu_timeout = manifest_sync_timeout; + + at76_dbg(DBG_DFU, "DFU: Waiting for manifest phase"); + schedule_timeout_interruptible(dfu_timeout); + break; + + case STATE_DFU_MANIFEST: + at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST"); + is_done = 1; + break; + + case STATE_DFU_MANIFEST_WAIT_RESET: + at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_WAIT_RESET"); + is_done = 1; + break; + + case STATE_DFU_UPLOAD_IDLE: + at76_dbg(DBG_DFU, "STATE_DFU_UPLOAD_IDLE"); + break; + + case STATE_DFU_ERROR: + at76_dbg(DBG_DFU, "STATE_DFU_ERROR"); + ret = -EPIPE; + break; + + default: + at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", *dfu_state); + ret = -EINVAL; + break; + } + } while (!is_done && (ret >= 0)); + +exit: + kfree(dfu_state); + kfree(block); + kfree(dfu_stat_buf); + + if (ret >= 0) + ret = 0; + + return ret; +} + +/* LED trigger */ +static int tx_activity; +static void at76_ledtrig_tx_timerfunc(unsigned long data); +static DEFINE_TIMER(ledtrig_tx_timer, at76_ledtrig_tx_timerfunc, 0, 0); +DEFINE_LED_TRIGGER(ledtrig_tx); + +static void at76_ledtrig_tx_timerfunc(unsigned long data) +{ + static int tx_lastactivity; + + if (tx_lastactivity != tx_activity) { + tx_lastactivity = tx_activity; + led_trigger_event(ledtrig_tx, LED_FULL); + mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); + } else + led_trigger_event(ledtrig_tx, LED_OFF); +} + +static void at76_ledtrig_tx_activity(void) +{ + tx_activity++; + if (!timer_pending(&ledtrig_tx_timer)) + mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); +} + +static int at76_remap(struct usb_device *udev) +{ + int ret; + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0a, + USB_TYPE_VENDOR | USB_DIR_OUT | + USB_RECIP_INTERFACE, 0, 0, NULL, 0, + USB_CTRL_GET_TIMEOUT); + if (ret < 0) + return ret; + return 0; +} + +static int at76_get_op_mode(struct usb_device *udev) +{ + int ret; + u8 saved; + u8 *op_mode; + + op_mode = kmalloc(1, GFP_NOIO); + if (!op_mode) + return -ENOMEM; + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, 0x01, 0, op_mode, 1, + USB_CTRL_GET_TIMEOUT); + saved = *op_mode; + kfree(op_mode); + + if (ret < 0) + return ret; + else if (ret < 1) + return -EIO; + else + return saved; +} + +/* Load a block of the second ("external") part of the firmware */ +static inline int at76_load_ext_fw_block(struct usb_device *udev, int blockno, + void *block, int size) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + 0x0802, blockno, block, size, + USB_CTRL_GET_TIMEOUT); +} + +static inline int at76_get_hw_cfg(struct usb_device *udev, + union at76_hwcfg *buf, int buf_size) +{ + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, 0x0a02, 0, + buf, buf_size, USB_CTRL_GET_TIMEOUT); +} + +/* Intersil boards use a different "value" for GetHWConfig requests */ +static inline int at76_get_hw_cfg_intersil(struct usb_device *udev, + union at76_hwcfg *buf, int buf_size) +{ + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, 0x0902, 0, + buf, buf_size, USB_CTRL_GET_TIMEOUT); +} + +/* Get the hardware configuration for the adapter and put it to the appropriate + * fields of 'priv' (the GetHWConfig request and interpretation of the result + * depends on the board type) */ +static int at76_get_hw_config(struct at76_priv *priv) +{ + int ret; + union at76_hwcfg *hwcfg = kmalloc(sizeof(*hwcfg), GFP_KERNEL); + + if (!hwcfg) + return -ENOMEM; + + if (at76_is_intersil(priv->board_type)) { + ret = at76_get_hw_cfg_intersil(priv->udev, hwcfg, + sizeof(hwcfg->i)); + if (ret < 0) + goto exit; + memcpy(priv->mac_addr, hwcfg->i.mac_addr, ETH_ALEN); + priv->regulatory_domain = hwcfg->i.regulatory_domain; + } else if (at76_is_503rfmd(priv->board_type)) { + ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r3)); + if (ret < 0) + goto exit; + memcpy(priv->mac_addr, hwcfg->r3.mac_addr, ETH_ALEN); + priv->regulatory_domain = hwcfg->r3.regulatory_domain; + } else { + ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r5)); + if (ret < 0) + goto exit; + memcpy(priv->mac_addr, hwcfg->r5.mac_addr, ETH_ALEN); + priv->regulatory_domain = hwcfg->r5.regulatory_domain; + } + +exit: + kfree(hwcfg); + if (ret < 0) + wiphy_err(priv->hw->wiphy, "cannot get HW Config (error %d)\n", + ret); + + return ret; +} + +static struct reg_domain const *at76_get_reg_domain(u16 code) +{ + int i; + static struct reg_domain const fd_tab[] = { + { 0x10, "FCC (USA)", 0x7ff }, /* ch 1-11 */ + { 0x20, "IC (Canada)", 0x7ff }, /* ch 1-11 */ + { 0x30, "ETSI (most of Europe)", 0x1fff }, /* ch 1-13 */ + { 0x31, "Spain", 0x600 }, /* ch 10-11 */ + { 0x32, "France", 0x1e00 }, /* ch 10-13 */ + { 0x40, "MKK (Japan)", 0x2000 }, /* ch 14 */ + { 0x41, "MKK1 (Japan)", 0x3fff }, /* ch 1-14 */ + { 0x50, "Israel", 0x3fc }, /* ch 3-9 */ + { 0x00, "", 0xffffffff } /* ch 1-32 */ + }; + + /* Last entry is fallback for unknown domain code */ + for (i = 0; i < ARRAY_SIZE(fd_tab) - 1; i++) + if (code == fd_tab[i].code) + break; + + return &fd_tab[i]; +} + +static inline int at76_get_mib(struct usb_device *udev, u16 mib, void *buf, + int buf_size) +{ + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, mib << 8, 0, buf, buf_size, + USB_CTRL_GET_TIMEOUT); + if (ret >= 0 && ret != buf_size) + return -EIO; + return ret; +} + +/* Return positive number for status, negative for an error */ +static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd) +{ + u8 *stat_buf; + int ret; + + stat_buf = kmalloc(40, GFP_NOIO); + if (!stat_buf) + return -ENOMEM; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x22, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, cmd, 0, stat_buf, + 40, USB_CTRL_GET_TIMEOUT); + if (ret >= 0) + ret = stat_buf[5]; + kfree(stat_buf); + + return ret; +} + +#define MAKE_CMD_CASE(c) case (c): return #c +static const char *at76_get_cmd_string(u8 cmd_status) +{ + switch (cmd_status) { + MAKE_CMD_CASE(CMD_SET_MIB); + MAKE_CMD_CASE(CMD_GET_MIB); + MAKE_CMD_CASE(CMD_SCAN); + MAKE_CMD_CASE(CMD_JOIN); + MAKE_CMD_CASE(CMD_START_IBSS); + MAKE_CMD_CASE(CMD_RADIO_ON); + MAKE_CMD_CASE(CMD_RADIO_OFF); + MAKE_CMD_CASE(CMD_STARTUP); + } + + return "UNKNOWN"; +} + +static int at76_set_card_command(struct usb_device *udev, u8 cmd, void *buf, + int buf_size) +{ + int ret; + struct at76_command *cmd_buf = kmalloc(sizeof(struct at76_command) + + buf_size, GFP_KERNEL); + + if (!cmd_buf) + return -ENOMEM; + + cmd_buf->cmd = cmd; + cmd_buf->reserved = 0; + cmd_buf->size = cpu_to_le16(buf_size); + memcpy(cmd_buf->data, buf, buf_size); + + at76_dbg_dump(DBG_CMD, cmd_buf, sizeof(struct at76_command) + buf_size, + "issuing command %s (0x%02x)", + at76_get_cmd_string(cmd), cmd); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + 0, 0, cmd_buf, + sizeof(struct at76_command) + buf_size, + USB_CTRL_GET_TIMEOUT); + kfree(cmd_buf); + return ret; +} + +#define MAKE_CMD_STATUS_CASE(c) case (c): return #c +static const char *at76_get_cmd_status_string(u8 cmd_status) +{ + switch (cmd_status) { + MAKE_CMD_STATUS_CASE(CMD_STATUS_IDLE); + MAKE_CMD_STATUS_CASE(CMD_STATUS_COMPLETE); + MAKE_CMD_STATUS_CASE(CMD_STATUS_UNKNOWN); + MAKE_CMD_STATUS_CASE(CMD_STATUS_INVALID_PARAMETER); + MAKE_CMD_STATUS_CASE(CMD_STATUS_FUNCTION_NOT_SUPPORTED); + MAKE_CMD_STATUS_CASE(CMD_STATUS_TIME_OUT); + MAKE_CMD_STATUS_CASE(CMD_STATUS_IN_PROGRESS); + MAKE_CMD_STATUS_CASE(CMD_STATUS_HOST_FAILURE); + MAKE_CMD_STATUS_CASE(CMD_STATUS_SCAN_FAILED); + } + + return "UNKNOWN"; +} + +/* Wait until the command is completed */ +static int at76_wait_completion(struct at76_priv *priv, int cmd) +{ + int status = 0; + unsigned long timeout = jiffies + CMD_COMPLETION_TIMEOUT; + + do { + status = at76_get_cmd_status(priv->udev, cmd); + if (status < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_cmd_status failed: %d\n", + status); + break; + } + + at76_dbg(DBG_WAIT_COMPLETE, + "%s: Waiting on cmd %d, status = %d (%s)", + wiphy_name(priv->hw->wiphy), cmd, status, + at76_get_cmd_status_string(status)); + + if (status != CMD_STATUS_IN_PROGRESS + && status != CMD_STATUS_IDLE) + break; + + schedule_timeout_interruptible(HZ / 10); /* 100 ms */ + if (time_after(jiffies, timeout)) { + wiphy_err(priv->hw->wiphy, + "completion timeout for command %d\n", cmd); + status = -ETIMEDOUT; + break; + } + } while (1); + + return status; +} + +static int at76_set_mib(struct at76_priv *priv, struct set_mib_buffer *buf) +{ + int ret; + + ret = at76_set_card_command(priv->udev, CMD_SET_MIB, buf, + offsetof(struct set_mib_buffer, + data) + buf->size); + if (ret < 0) + return ret; + + ret = at76_wait_completion(priv, CMD_SET_MIB); + if (ret != CMD_STATUS_COMPLETE) { + wiphy_info(priv->hw->wiphy, + "set_mib: at76_wait_completion failed with %d\n", + ret); + ret = -EIO; + } + + return ret; +} + +/* Return < 0 on error, == 0 if no command sent, == 1 if cmd sent */ +static int at76_set_radio(struct at76_priv *priv, int enable) +{ + int ret; + int cmd; + + if (priv->radio_on == enable) + return 0; + + cmd = enable ? CMD_RADIO_ON : CMD_RADIO_OFF; + + ret = at76_set_card_command(priv->udev, cmd, NULL, 0); + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "at76_set_card_command(%d) failed: %d\n", cmd, ret); + else + ret = 1; + + priv->radio_on = enable; + return ret; +} + +/* Set current power save mode (AT76_PM_OFF/AT76_PM_ON/AT76_PM_SMART) */ +static int at76_set_pm_mode(struct at76_priv *priv) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC_MGMT; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_mac_mgmt, power_mgmt_mode); + priv->mib_buf.data.byte = priv->pm_mode; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, "set_mib (pm_mode) failed: %d\n", + ret); + + return ret; +} + +static int at76_set_preamble(struct at76_priv *priv, u8 type) +{ + int ret = 0; + + priv->mib_buf.type = MIB_LOCAL; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_local, preamble_type); + priv->mib_buf.data.byte = type; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, "set_mib (preamble) failed: %d\n", + ret); + + return ret; +} + +static int at76_set_frag(struct at76_priv *priv, u16 size) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC; + priv->mib_buf.size = 2; + priv->mib_buf.index = offsetof(struct mib_mac, frag_threshold); + priv->mib_buf.data.word = cpu_to_le16(size); + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "set_mib (frag threshold) failed: %d\n", ret); + + return ret; +} + +static int at76_set_rts(struct at76_priv *priv, u16 size) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC; + priv->mib_buf.size = 2; + priv->mib_buf.index = offsetof(struct mib_mac, rts_threshold); + priv->mib_buf.data.word = cpu_to_le16(size); + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, "set_mib (rts) failed: %d\n", ret); + + return ret; +} + +static int at76_set_autorate_fallback(struct at76_priv *priv, int onoff) +{ + int ret = 0; + + priv->mib_buf.type = MIB_LOCAL; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_local, txautorate_fallback); + priv->mib_buf.data.byte = onoff; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "set_mib (autorate fallback) failed: %d\n", ret); + + return ret; +} + +static void at76_dump_mib_mac_addr(struct at76_priv *priv) +{ + int i; + int ret; + struct mib_mac_addr *m = kmalloc(sizeof(struct mib_mac_addr), + GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC_ADDR, m, + sizeof(struct mib_mac_addr)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (MAC_ADDR) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %pM res 0x%x 0x%x", + wiphy_name(priv->hw->wiphy), + m->mac_addr, m->res[0], m->res[1]); + for (i = 0; i < ARRAY_SIZE(m->group_addr); i++) + at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %pM, " + "status %d", wiphy_name(priv->hw->wiphy), i, + m->group_addr[i], m->group_addr_status[i]); +exit: + kfree(m); +} + +static void at76_dump_mib_mac_wep(struct at76_priv *priv) +{ + int i; + int ret; + int key_len; + struct mib_mac_wep *m = kmalloc(sizeof(struct mib_mac_wep), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC_WEP, m, + sizeof(struct mib_mac_wep)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (MAC_WEP) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: priv_invoked %u def_key_id %u " + "key_len %u excl_unencr %u wep_icv_err %u wep_excluded %u " + "encr_level %u key %d", wiphy_name(priv->hw->wiphy), + m->privacy_invoked, m->wep_default_key_id, + m->wep_key_mapping_len, m->exclude_unencrypted, + le32_to_cpu(m->wep_icv_error_count), + le32_to_cpu(m->wep_excluded_count), m->encryption_level, + m->wep_default_key_id); + + key_len = (m->encryption_level == 1) ? + WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN; + + for (i = 0; i < WEP_KEYS; i++) + at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: key %d: %*phD", + wiphy_name(priv->hw->wiphy), i, + key_len, m->wep_default_keyvalue[i]); +exit: + kfree(m); +} + +static void at76_dump_mib_mac_mgmt(struct at76_priv *priv) +{ + int ret; + struct mib_mac_mgmt *m = kmalloc(sizeof(struct mib_mac_mgmt), + GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, m, + sizeof(struct mib_mac_mgmt)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (MAC_MGMT) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration " + "%d medium_occupancy_limit %d station_id 0x%x ATIM_window %d " + "CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d " + "current_bssid %pM current_essid %*phD current_bss_type %d " + "pm_mode %d ibss_change %d res %d " + "multi_domain_capability_implemented %d " + "international_roaming %d country_string %.3s", + wiphy_name(priv->hw->wiphy), le16_to_cpu(m->beacon_period), + le16_to_cpu(m->CFP_max_duration), + le16_to_cpu(m->medium_occupancy_limit), + le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window), + m->CFP_mode, m->privacy_option_implemented, m->DTIM_period, + m->CFP_period, m->current_bssid, + IW_ESSID_MAX_SIZE, m->current_essid, + m->current_bss_type, m->power_mgmt_mode, m->ibss_change, + m->res, m->multi_domain_capability_implemented, + m->multi_domain_capability_enabled, m->country_string); +exit: + kfree(m); +} + +static void at76_dump_mib_mac(struct at76_priv *priv) +{ + int ret; + struct mib_mac *m = kmalloc(sizeof(struct mib_mac), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC, m, sizeof(struct mib_mac)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (MAC) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC: max_tx_msdu_lifetime %d " + "max_rx_lifetime %d frag_threshold %d rts_threshold %d " + "cwmin %d cwmax %d short_retry_time %d long_retry_time %d " + "scan_type %d scan_channel %d probe_delay %u " + "min_channel_time %d max_channel_time %d listen_int %d " + "desired_ssid %*phD desired_bssid %pM desired_bsstype %d", + wiphy_name(priv->hw->wiphy), + le32_to_cpu(m->max_tx_msdu_lifetime), + le32_to_cpu(m->max_rx_lifetime), + le16_to_cpu(m->frag_threshold), le16_to_cpu(m->rts_threshold), + le16_to_cpu(m->cwmin), le16_to_cpu(m->cwmax), + m->short_retry_time, m->long_retry_time, m->scan_type, + m->scan_channel, le16_to_cpu(m->probe_delay), + le16_to_cpu(m->min_channel_time), + le16_to_cpu(m->max_channel_time), + le16_to_cpu(m->listen_interval), + IW_ESSID_MAX_SIZE, m->desired_ssid, + m->desired_bssid, m->desired_bsstype); +exit: + kfree(m); +} + +static void at76_dump_mib_phy(struct at76_priv *priv) +{ + int ret; + struct mib_phy *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_PHY, m, sizeof(struct mib_phy)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (PHY) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB PHY: ed_threshold %d slot_time %d " + "sifs_time %d preamble_length %d plcp_header_length %d " + "mpdu_max_length %d cca_mode_supported %d operation_rate_set " + "0x%x 0x%x 0x%x 0x%x channel_id %d current_cca_mode %d " + "phy_type %d current_reg_domain %d", + wiphy_name(priv->hw->wiphy), le32_to_cpu(m->ed_threshold), + le16_to_cpu(m->slot_time), le16_to_cpu(m->sifs_time), + le16_to_cpu(m->preamble_length), + le16_to_cpu(m->plcp_header_length), + le16_to_cpu(m->mpdu_max_length), + le16_to_cpu(m->cca_mode_supported), m->operation_rate_set[0], + m->operation_rate_set[1], m->operation_rate_set[2], + m->operation_rate_set[3], m->channel_id, m->current_cca_mode, + m->phy_type, m->current_reg_domain); +exit: + kfree(m); +} + +static void at76_dump_mib_local(struct at76_priv *priv) +{ + int ret; + struct mib_local *m = kmalloc(sizeof(*m), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(*m)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (LOCAL) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB LOCAL: beacon_enable %d " + "txautorate_fallback %d ssid_size %d promiscuous_mode %d " + "preamble_type %d", wiphy_name(priv->hw->wiphy), + m->beacon_enable, + m->txautorate_fallback, m->ssid_size, m->promiscuous_mode, + m->preamble_type); +exit: + kfree(m); +} + +static void at76_dump_mib_mdomain(struct at76_priv *priv) +{ + int ret; + struct mib_mdomain *m = kmalloc(sizeof(struct mib_mdomain), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MDOMAIN, m, + sizeof(struct mib_mdomain)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (MDOMAIN) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %*phD", + wiphy_name(priv->hw->wiphy), + (int)sizeof(m->channel_list), m->channel_list); + + at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: tx_powerlevel %*phD", + wiphy_name(priv->hw->wiphy), + (int)sizeof(m->tx_powerlevel), m->tx_powerlevel); +exit: + kfree(m); +} + +/* Enable monitor mode */ +static int at76_start_monitor(struct at76_priv *priv) +{ + struct at76_req_scan scan; + int ret; + + memset(&scan, 0, sizeof(struct at76_req_scan)); + eth_broadcast_addr(scan.bssid); + + scan.channel = priv->channel; + scan.scan_type = SCAN_TYPE_PASSIVE; + scan.international_scan = 0; + scan.min_channel_time = cpu_to_le16(priv->scan_min_time); + scan.max_channel_time = cpu_to_le16(priv->scan_max_time); + scan.probe_delay = cpu_to_le16(0); + + ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); + if (ret >= 0) + ret = at76_get_cmd_status(priv->udev, CMD_SCAN); + + return ret; +} + +/* Calculate padding from txbuf->wlength (which excludes the USB TX header), + likely to compensate a flaw in the AT76C503A USB part ... */ +static inline int at76_calc_padding(int wlen) +{ + /* add the USB TX header */ + wlen += AT76_TX_HDRLEN; + + wlen = wlen % 64; + + if (wlen < 50) + return 50 - wlen; + + if (wlen >= 61) + return 64 + 50 - wlen; + + return 0; +} + +static void at76_rx_callback(struct urb *urb) +{ + struct at76_priv *priv = urb->context; + + priv->rx_tasklet.data = (unsigned long)urb; + tasklet_schedule(&priv->rx_tasklet); +} + +static int at76_submit_rx_urb(struct at76_priv *priv) +{ + int ret; + int size; + struct sk_buff *skb = priv->rx_skb; + + if (!priv->rx_urb) { + wiphy_err(priv->hw->wiphy, "%s: priv->rx_urb is NULL\n", + __func__); + return -EFAULT; + } + + if (!skb) { + skb = dev_alloc_skb(sizeof(struct at76_rx_buffer)); + if (!skb) { + wiphy_err(priv->hw->wiphy, + "cannot allocate rx skbuff\n"); + ret = -ENOMEM; + goto exit; + } + priv->rx_skb = skb; + } else { + skb_push(skb, skb_headroom(skb)); + skb_trim(skb, 0); + } + + size = skb_tailroom(skb); + usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe, + skb_put(skb, size), size, at76_rx_callback, priv); + ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC); + if (ret < 0) { + if (ret == -ENODEV) + at76_dbg(DBG_DEVSTART, + "usb_submit_urb returned -ENODEV"); + else + wiphy_err(priv->hw->wiphy, + "rx, usb_submit_urb failed: %d\n", ret); + } + +exit: + if (ret < 0 && ret != -ENODEV) + wiphy_err(priv->hw->wiphy, + "cannot submit rx urb - please unload the driver and/or power cycle the device\n"); + + return ret; +} + +/* Download external firmware */ +static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) +{ + int ret; + int op_mode; + int blockno = 0; + int bsize; + u8 *block; + u8 *buf = fwe->extfw; + int size = fwe->extfw_size; + + if (!buf || !size) + return -ENOENT; + + op_mode = at76_get_op_mode(udev); + at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); + + if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { + dev_err(&udev->dev, "unexpected opmode %d\n", op_mode); + return -EINVAL; + } + + block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); + if (!block) + return -ENOMEM; + + at76_dbg(DBG_DEVSTART, "downloading external firmware"); + + /* for fw >= 0.100, the device needs an extra empty block */ + do { + bsize = min_t(int, size, FW_BLOCK_SIZE); + memcpy(block, buf, bsize); + at76_dbg(DBG_DEVSTART, + "ext fw, size left = %5d, bsize = %4d, blockno = %2d", + size, bsize, blockno); + ret = at76_load_ext_fw_block(udev, blockno, block, bsize); + if (ret != bsize) { + dev_err(&udev->dev, + "loading %dth firmware block failed: %d\n", + blockno, ret); + ret = -EIO; + goto exit; + } + buf += bsize; + size -= bsize; + blockno++; + } while (bsize > 0); + + if (at76_is_505a(fwe->board_type)) { + at76_dbg(DBG_DEVSTART, "200 ms delay for 505a"); + schedule_timeout_interruptible(HZ / 5 + 1); + } + +exit: + kfree(block); + if (ret < 0) + dev_err(&udev->dev, + "downloading external firmware failed: %d\n", ret); + return ret; +} + +/* Download internal firmware */ +static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe) +{ + int ret; + int need_remap = !at76_is_505a(fwe->board_type); + + ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size, + need_remap ? 0 : 2 * HZ); + + if (ret < 0) { + dev_err(&udev->dev, + "downloading internal fw failed with %d\n", ret); + goto exit; + } + + at76_dbg(DBG_DEVSTART, "sending REMAP"); + + /* no REMAP for 505A (see SF driver) */ + if (need_remap) { + ret = at76_remap(udev); + if (ret < 0) { + dev_err(&udev->dev, + "sending REMAP failed with %d\n", ret); + goto exit; + } + } + + at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds"); + schedule_timeout_interruptible(2 * HZ + 1); + usb_reset_device(udev); + +exit: + return ret; +} + +static int at76_startup_device(struct at76_priv *priv) +{ + struct at76_card_config *ccfg = &priv->card_config; + int ret; + + at76_dbg(DBG_PARAMS, + "%s param: ssid %.*s (%*phD) mode %s ch %d wep %s key %d " + "keylen %d", wiphy_name(priv->hw->wiphy), priv->essid_size, + priv->essid, IW_ESSID_MAX_SIZE, priv->essid, + priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra", + priv->channel, priv->wep_enabled ? "enabled" : "disabled", + priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]); + at76_dbg(DBG_PARAMS, + "%s param: preamble %s rts %d retry %d frag %d " + "txrate %s auth_mode %d", wiphy_name(priv->hw->wiphy), + preambles[priv->preamble_type], priv->rts_threshold, + priv->short_retry_limit, priv->frag_threshold, + priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate == + TX_RATE_2MBIT ? "2MBit" : priv->txrate == + TX_RATE_5_5MBIT ? "5.5MBit" : priv->txrate == + TX_RATE_11MBIT ? "11MBit" : priv->txrate == + TX_RATE_AUTO ? "auto" : "", priv->auth_mode); + at76_dbg(DBG_PARAMS, + "%s param: pm_mode %d pm_period %d auth_mode %s " + "scan_times %d %d scan_mode %s", + wiphy_name(priv->hw->wiphy), priv->pm_mode, priv->pm_period, + priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret", + priv->scan_min_time, priv->scan_max_time, + priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive"); + + memset(ccfg, 0, sizeof(struct at76_card_config)); + ccfg->promiscuous_mode = 0; + ccfg->short_retry_limit = priv->short_retry_limit; + + if (priv->wep_enabled) { + if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) + ccfg->encryption_type = 2; + else + ccfg->encryption_type = 1; + + /* jal: always exclude unencrypted if WEP is active */ + ccfg->exclude_unencrypted = 1; + } else { + ccfg->exclude_unencrypted = 0; + ccfg->encryption_type = 0; + } + + ccfg->rts_threshold = cpu_to_le16(priv->rts_threshold); + ccfg->fragmentation_threshold = cpu_to_le16(priv->frag_threshold); + + memcpy(ccfg->basic_rate_set, hw_rates, 4); + /* jal: really needed, we do a set_mib for autorate later ??? */ + ccfg->auto_rate_fallback = (priv->txrate == TX_RATE_AUTO ? 1 : 0); + ccfg->channel = priv->channel; + ccfg->privacy_invoked = priv->wep_enabled; + memcpy(ccfg->current_ssid, priv->essid, IW_ESSID_MAX_SIZE); + ccfg->ssid_len = priv->essid_size; + + ccfg->wep_default_key_id = priv->wep_key_id; + memcpy(ccfg->wep_default_key_value, priv->wep_keys, + sizeof(priv->wep_keys)); + + ccfg->short_preamble = priv->preamble_type; + ccfg->beacon_period = cpu_to_le16(priv->beacon_period); + + ret = at76_set_card_command(priv->udev, CMD_STARTUP, &priv->card_config, + sizeof(struct at76_card_config)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", + ret); + return ret; + } + + at76_wait_completion(priv, CMD_STARTUP); + + /* remove BSSID from previous run */ + eth_zero_addr(priv->bssid); + + priv->scanning = false; + + if (at76_set_radio(priv, 1) == 1) + at76_wait_completion(priv, CMD_RADIO_ON); + + ret = at76_set_preamble(priv, priv->preamble_type); + if (ret < 0) + return ret; + + ret = at76_set_frag(priv, priv->frag_threshold); + if (ret < 0) + return ret; + + ret = at76_set_rts(priv, priv->rts_threshold); + if (ret < 0) + return ret; + + ret = at76_set_autorate_fallback(priv, + priv->txrate == TX_RATE_AUTO ? 1 : 0); + if (ret < 0) + return ret; + + ret = at76_set_pm_mode(priv); + if (ret < 0) + return ret; + + if (at76_debug & DBG_MIB) { + at76_dump_mib_mac(priv); + at76_dump_mib_mac_addr(priv); + at76_dump_mib_mac_mgmt(priv); + at76_dump_mib_mac_wep(priv); + at76_dump_mib_mdomain(priv); + at76_dump_mib_phy(priv); + at76_dump_mib_local(priv); + } + + return 0; +} + +/* Enable or disable promiscuous mode */ +static void at76_work_set_promisc(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_set_promisc); + int ret = 0; + + if (priv->device_unplugged) + return; + + mutex_lock(&priv->mtx); + + priv->mib_buf.type = MIB_LOCAL; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_local, promiscuous_mode); + priv->mib_buf.data.byte = priv->promisc ? 1 : 0; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "set_mib (promiscuous_mode) failed: %d\n", ret); + + mutex_unlock(&priv->mtx); +} + +/* Submit Rx urb back to the device */ +static void at76_work_submit_rx(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_submit_rx); + + mutex_lock(&priv->mtx); + at76_submit_rx_urb(priv); + mutex_unlock(&priv->mtx); +} + +/* This is a workaround to make scan working: + * currently mac80211 does not process frames with no frequency + * information. + * However during scan the HW performs a sweep by itself, and we + * are unable to know where the radio is actually tuned. + * This function tries to do its best to guess this information.. + * During scan, If the current frame is a beacon or a probe response, + * the channel information is extracted from it. + * When not scanning, for other frames, or if it happens that for + * whatever reason we fail to parse beacons and probe responses, this + * function returns the priv->channel information, that should be correct + * at least when we are not scanning. + */ +static inline int at76_guess_freq(struct at76_priv *priv) +{ + size_t el_off; + const u8 *el; + int channel = priv->channel; + int len = priv->rx_skb->len; + struct ieee80211_hdr *hdr = (void *)priv->rx_skb->data; + + if (!priv->scanning) + goto exit; + + if (len < 24) + goto exit; + + if (ieee80211_is_probe_resp(hdr->frame_control)) { + el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable; + } else if (ieee80211_is_beacon(hdr->frame_control)) { + el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable); + el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable; + } else { + goto exit; + } + len -= el_off; + + el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len); + if (el && el[1] > 0) + channel = el[2]; + +exit: + return ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ); +} + +static void at76_rx_tasklet(unsigned long param) +{ + struct urb *urb = (struct urb *)param; + struct at76_priv *priv = urb->context; + struct at76_rx_buffer *buf; + struct ieee80211_rx_status rx_status = { 0 }; + + if (priv->device_unplugged) { + at76_dbg(DBG_DEVSTART, "device unplugged"); + at76_dbg(DBG_DEVSTART, "urb status %d", urb->status); + return; + } + + if (!priv->rx_skb || !priv->rx_skb->data) + return; + + buf = (struct at76_rx_buffer *)priv->rx_skb->data; + + if (urb->status != 0) { + if (urb->status != -ENOENT && urb->status != -ECONNRESET) + at76_dbg(DBG_URB, + "%s %s: - nonzero Rx bulk status received: %d", + __func__, wiphy_name(priv->hw->wiphy), + urb->status); + return; + } + + at76_dbg(DBG_RX_ATMEL_HDR, + "%s: rx frame: rate %d rssi %d noise %d link %d", + wiphy_name(priv->hw->wiphy), buf->rx_rate, buf->rssi, + buf->noise_level, buf->link_quality); + + skb_pull(priv->rx_skb, AT76_RX_HDRLEN); + skb_trim(priv->rx_skb, le16_to_cpu(buf->wlength)); + at76_dbg_dump(DBG_RX_DATA, priv->rx_skb->data, + priv->rx_skb->len, "RX: len=%d", priv->rx_skb->len); + + rx_status.signal = buf->rssi; + rx_status.flag |= RX_FLAG_DECRYPTED; + rx_status.flag |= RX_FLAG_IV_STRIPPED; + rx_status.band = IEEE80211_BAND_2GHZ; + rx_status.freq = at76_guess_freq(priv); + + at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d", + priv->rx_skb->len, priv->rx_skb->data_len); + memcpy(IEEE80211_SKB_RXCB(priv->rx_skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(priv->hw, priv->rx_skb); + + /* Use a new skb for the next receive */ + priv->rx_skb = NULL; + + at76_submit_rx_urb(priv); +} + +/* Load firmware into kernel memory and parse it */ +static struct fwentry *at76_load_firmware(struct usb_device *udev, + enum board_type board_type) +{ + int ret; + char *str; + struct at76_fw_header *fwh; + struct fwentry *fwe = &firmwares[board_type]; + + mutex_lock(&fw_mutex); + + if (fwe->loaded) { + at76_dbg(DBG_FW, "re-using previously loaded fw"); + goto exit; + } + + at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname); + ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev); + if (ret < 0) { + dev_err(&udev->dev, "firmware %s not found!\n", + fwe->fwname); + dev_err(&udev->dev, + "you may need to download the firmware from http://developer.berlios.de/projects/at76c503a/\n"); + goto exit; + } + + at76_dbg(DBG_FW, "got it."); + fwh = (struct at76_fw_header *)(fwe->fw->data); + + if (fwe->fw->size <= sizeof(*fwh)) { + dev_err(&udev->dev, + "firmware is too short (0x%zx)\n", fwe->fw->size); + goto exit; + } + + /* CRC currently not checked */ + fwe->board_type = le32_to_cpu(fwh->board_type); + if (fwe->board_type != board_type) { + dev_err(&udev->dev, + "board type mismatch, requested %u, got %u\n", + board_type, fwe->board_type); + goto exit; + } + + fwe->fw_version.major = fwh->major; + fwe->fw_version.minor = fwh->minor; + fwe->fw_version.patch = fwh->patch; + fwe->fw_version.build = fwh->build; + + str = (char *)fwh + le32_to_cpu(fwh->str_offset); + fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset); + fwe->intfw_size = le32_to_cpu(fwh->int_fw_len); + fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset); + fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len); + + fwe->loaded = 1; + + dev_printk(KERN_DEBUG, &udev->dev, + "using firmware %s (version %d.%d.%d-%d)\n", + fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build); + + at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type, + le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len), + le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len)); + at76_dbg(DBG_DEVSTART, "firmware id %s", str); + +exit: + mutex_unlock(&fw_mutex); + + if (fwe->loaded) + return fwe; + else + return NULL; +} + +static int at76_join(struct at76_priv *priv) +{ + struct at76_req_join join; + int ret; + + memset(&join, 0, sizeof(struct at76_req_join)); + memcpy(join.essid, priv->essid, priv->essid_size); + join.essid_size = priv->essid_size; + memcpy(join.bssid, priv->bssid, ETH_ALEN); + join.bss_type = INFRASTRUCTURE_MODE; + join.channel = priv->channel; + join.timeout = cpu_to_le16(2000); + + at76_dbg(DBG_MAC80211, "%s: sending CMD_JOIN", __func__); + ret = at76_set_card_command(priv->udev, CMD_JOIN, &join, + sizeof(struct at76_req_join)); + + if (ret < 0) { + wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", + ret); + return 0; + } + + ret = at76_wait_completion(priv, CMD_JOIN); + at76_dbg(DBG_MAC80211, "%s: CMD_JOIN returned: 0x%02x", __func__, ret); + if (ret != CMD_STATUS_COMPLETE) { + wiphy_err(priv->hw->wiphy, "at76_wait_completion failed: %d\n", + ret); + return 0; + } + + at76_set_pm_mode(priv); + + return 0; +} + +static void at76_work_join_bssid(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_join_bssid); + + if (priv->device_unplugged) + return; + + mutex_lock(&priv->mtx); + + if (is_valid_ether_addr(priv->bssid)) + at76_join(priv); + + mutex_unlock(&priv->mtx); +} + +static void at76_mac80211_tx_callback(struct urb *urb) +{ + struct at76_priv *priv = urb->context; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); + + at76_dbg(DBG_MAC80211, "%s()", __func__); + + switch (urb->status) { + case 0: + /* success */ + info->flags |= IEEE80211_TX_STAT_ACK; + break; + case -ENOENT: + case -ECONNRESET: + /* fail, urb has been unlinked */ + /* FIXME: add error message */ + break; + default: + at76_dbg(DBG_URB, "%s - nonzero tx status received: %d", + __func__, urb->status); + break; + } + + memset(&info->status, 0, sizeof(info->status)); + + ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); + + priv->tx_skb = NULL; + + ieee80211_wake_queues(priv->hw); +} + +static void at76_mac80211_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct at76_priv *priv = hw->priv; + struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + int padding, submit_len, ret; + + at76_dbg(DBG_MAC80211, "%s()", __func__); + + if (priv->tx_urb->status == -EINPROGRESS) { + wiphy_err(priv->hw->wiphy, + "%s called while tx urb is pending\n", __func__); + dev_kfree_skb_any(skb); + return; + } + + /* The following code lines are important when the device is going to + * authenticate with a new bssid. The driver must send CMD_JOIN before + * an authentication frame is transmitted. For this to succeed, the + * correct bssid of the AP must be known. As mac80211 does not inform + * drivers about the bssid prior to the authentication process the + * following workaround is necessary. If the TX frame is an + * authentication frame extract the bssid and send the CMD_JOIN. */ + if (mgmt->frame_control & cpu_to_le16(IEEE80211_STYPE_AUTH)) { + if (!ether_addr_equal_64bits(priv->bssid, mgmt->bssid)) { + memcpy(priv->bssid, mgmt->bssid, ETH_ALEN); + ieee80211_queue_work(hw, &priv->work_join_bssid); + dev_kfree_skb_any(skb); + return; + } + } + + ieee80211_stop_queues(hw); + + at76_ledtrig_tx_activity(); /* tell ledtrigger we send a packet */ + + WARN_ON(priv->tx_skb != NULL); + + priv->tx_skb = skb; + padding = at76_calc_padding(skb->len); + submit_len = AT76_TX_HDRLEN + skb->len + padding; + + /* setup 'Atmel' header */ + memset(tx_buffer, 0, sizeof(*tx_buffer)); + tx_buffer->padding = padding; + tx_buffer->wlength = cpu_to_le16(skb->len); + tx_buffer->tx_rate = ieee80211_get_tx_rate(hw, info)->hw_value; + memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved)); + memcpy(tx_buffer->packet, skb->data, skb->len); + + at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr", + wiphy_name(priv->hw->wiphy), le16_to_cpu(tx_buffer->wlength), + tx_buffer->padding, tx_buffer->tx_rate); + + /* send stuff */ + at76_dbg_dump(DBG_TX_DATA_CONTENT, tx_buffer, submit_len, + "%s(): tx_buffer %d bytes:", __func__, submit_len); + usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer, + submit_len, at76_mac80211_tx_callback, priv); + ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); + if (ret) { + wiphy_err(priv->hw->wiphy, "error in tx submit urb: %d\n", ret); + if (ret == -EINVAL) + wiphy_err(priv->hw->wiphy, + "-EINVAL: tx urb %p hcpriv %p complete %p\n", + priv->tx_urb, + priv->tx_urb->hcpriv, priv->tx_urb->complete); + } +} + +static int at76_mac80211_start(struct ieee80211_hw *hw) +{ + struct at76_priv *priv = hw->priv; + int ret; + + at76_dbg(DBG_MAC80211, "%s()", __func__); + + mutex_lock(&priv->mtx); + + ret = at76_submit_rx_urb(priv); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, "open: submit_rx_urb failed: %d\n", + ret); + goto error; + } + + at76_startup_device(priv); + + at76_start_monitor(priv); + +error: + mutex_unlock(&priv->mtx); + + return 0; +} + +static void at76_mac80211_stop(struct ieee80211_hw *hw) +{ + struct at76_priv *priv = hw->priv; + + at76_dbg(DBG_MAC80211, "%s()", __func__); + + cancel_delayed_work(&priv->dwork_hw_scan); + cancel_work_sync(&priv->work_join_bssid); + cancel_work_sync(&priv->work_set_promisc); + + mutex_lock(&priv->mtx); + + if (!priv->device_unplugged) { + /* We are called by "ifconfig ethX down", not because the + * device is not available anymore. */ + at76_set_radio(priv, 0); + + /* We unlink rx_urb because at76_open() re-submits it. + * If unplugged, at76_delete_device() takes care of it. */ + usb_kill_urb(priv->rx_urb); + } + + mutex_unlock(&priv->mtx); +} + +static int at76_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct at76_priv *priv = hw->priv; + int ret = 0; + + at76_dbg(DBG_MAC80211, "%s()", __func__); + + mutex_lock(&priv->mtx); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + priv->iw_mode = IW_MODE_INFRA; + break; + default: + ret = -EOPNOTSUPP; + goto exit; + } + +exit: + mutex_unlock(&priv->mtx); + + return ret; +} + +static void at76_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + at76_dbg(DBG_MAC80211, "%s()", __func__); +} + +static void at76_dwork_hw_scan(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_hw_scan.work); + int ret; + + if (priv->device_unplugged) + return; + + mutex_lock(&priv->mtx); + + ret = at76_get_cmd_status(priv->udev, CMD_SCAN); + at76_dbg(DBG_MAC80211, "%s: CMD_SCAN status 0x%02x", __func__, ret); + + /* FIXME: add maximum time for scan to complete */ + + if (ret != CMD_STATUS_COMPLETE) { + ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, + SCAN_POLL_INTERVAL); + mutex_unlock(&priv->mtx); + return; + } + + if (is_valid_ether_addr(priv->bssid)) + at76_join(priv); + + priv->scanning = false; + + mutex_unlock(&priv->mtx); + + ieee80211_scan_completed(priv->hw, false); + + ieee80211_wake_queues(priv->hw); +} + +static int at76_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct cfg80211_scan_request *req = &hw_req->req; + struct at76_priv *priv = hw->priv; + struct at76_req_scan scan; + u8 *ssid = NULL; + int ret, len = 0; + + at76_dbg(DBG_MAC80211, "%s():", __func__); + + if (priv->device_unplugged) + return 0; + + mutex_lock(&priv->mtx); + + ieee80211_stop_queues(hw); + + memset(&scan, 0, sizeof(struct at76_req_scan)); + eth_broadcast_addr(scan.bssid); + + if (req->n_ssids) { + scan.scan_type = SCAN_TYPE_ACTIVE; + ssid = req->ssids[0].ssid; + len = req->ssids[0].ssid_len; + } else { + scan.scan_type = SCAN_TYPE_PASSIVE; + } + + if (len) { + memcpy(scan.essid, ssid, len); + scan.essid_size = len; + } + + scan.min_channel_time = cpu_to_le16(priv->scan_min_time); + scan.max_channel_time = cpu_to_le16(priv->scan_max_time); + scan.probe_delay = cpu_to_le16(priv->scan_min_time * 1000); + scan.international_scan = 0; + + at76_dbg(DBG_MAC80211, "%s: sending CMD_SCAN", __func__); + ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); + + if (ret < 0) { + wiphy_err(priv->hw->wiphy, "CMD_SCAN failed: %d\n", ret); + goto exit; + } + + priv->scanning = true; + ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, + SCAN_POLL_INTERVAL); + +exit: + mutex_unlock(&priv->mtx); + + return 0; +} + +static int at76_config(struct ieee80211_hw *hw, u32 changed) +{ + struct at76_priv *priv = hw->priv; + + at76_dbg(DBG_MAC80211, "%s(): channel %d", + __func__, hw->conf.chandef.chan->hw_value); + at76_dbg_dump(DBG_MAC80211, priv->bssid, ETH_ALEN, "bssid:"); + + mutex_lock(&priv->mtx); + + priv->channel = hw->conf.chandef.chan->hw_value; + + if (is_valid_ether_addr(priv->bssid)) + at76_join(priv); + else + at76_start_monitor(priv); + + mutex_unlock(&priv->mtx); + + return 0; +} + +static void at76_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, + u32 changed) +{ + struct at76_priv *priv = hw->priv; + + at76_dbg(DBG_MAC80211, "%s():", __func__); + + if (!(changed & BSS_CHANGED_BSSID)) + return; + + at76_dbg_dump(DBG_MAC80211, conf->bssid, ETH_ALEN, "bssid:"); + + mutex_lock(&priv->mtx); + + memcpy(priv->bssid, conf->bssid, ETH_ALEN); + + if (is_valid_ether_addr(priv->bssid)) + /* mac80211 is joining a bss */ + at76_join(priv); + + mutex_unlock(&priv->mtx); +} + +/* must be atomic */ +static void at76_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct at76_priv *priv = hw->priv; + int flags; + + at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x " + "total_flags=0x%08x", + __func__, changed_flags, *total_flags); + + flags = changed_flags & AT76_SUPPORTED_FILTERS; + *total_flags = AT76_SUPPORTED_FILTERS; + + /* Bail out after updating flags to prevent a WARN_ON in mac80211. */ + if (priv->device_unplugged) + return; + + /* FIXME: access to priv->promisc should be protected with + * priv->mtx, but it's impossible because this function needs to be + * atomic */ + + if (flags && !priv->promisc) { + /* mac80211 wants us to enable promiscuous mode */ + priv->promisc = 1; + } else if (!flags && priv->promisc) { + /* we need to disable promiscuous mode */ + priv->promisc = 0; + } else + return; + + ieee80211_queue_work(hw, &priv->work_set_promisc); +} + +static int at76_set_wep(struct at76_priv *priv) +{ + int ret = 0; + struct mib_mac_wep *mib_data = &priv->mib_buf.data.wep_mib; + + priv->mib_buf.type = MIB_MAC_WEP; + priv->mib_buf.size = sizeof(struct mib_mac_wep); + priv->mib_buf.index = 0; + + memset(mib_data, 0, sizeof(*mib_data)); + + if (priv->wep_enabled) { + if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) + mib_data->encryption_level = 2; + else + mib_data->encryption_level = 1; + + /* always exclude unencrypted if WEP is active */ + mib_data->exclude_unencrypted = 1; + } else { + mib_data->exclude_unencrypted = 0; + mib_data->encryption_level = 0; + } + + mib_data->privacy_invoked = priv->wep_enabled; + mib_data->wep_default_key_id = priv->wep_key_id; + memcpy(mib_data->wep_default_keyvalue, priv->wep_keys, + sizeof(priv->wep_keys)); + + ret = at76_set_mib(priv, &priv->mib_buf); + + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "set_mib (wep) failed: %d\n", ret); + + return ret; +} + +static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct at76_priv *priv = hw->priv; + + int i; + + at76_dbg(DBG_MAC80211, "%s(): cmd %d key->cipher %d key->keyidx %d " + "key->keylen %d", + __func__, cmd, key->cipher, key->keyidx, key->keylen); + + if ((key->cipher != WLAN_CIPHER_SUITE_WEP40) && + (key->cipher != WLAN_CIPHER_SUITE_WEP104)) + return -EOPNOTSUPP; + + key->hw_key_idx = key->keyidx; + + mutex_lock(&priv->mtx); + + switch (cmd) { + case SET_KEY: + memcpy(priv->wep_keys[key->keyidx], key->key, key->keylen); + priv->wep_keys_len[key->keyidx] = key->keylen; + + /* FIXME: find out how to do this properly */ + priv->wep_key_id = key->keyidx; + + break; + case DISABLE_KEY: + default: + priv->wep_keys_len[key->keyidx] = 0; + break; + } + + priv->wep_enabled = 0; + + for (i = 0; i < WEP_KEYS; i++) { + if (priv->wep_keys_len[i] != 0) + priv->wep_enabled = 1; + } + + at76_set_wep(priv); + + mutex_unlock(&priv->mtx); + + return 0; +} + +static const struct ieee80211_ops at76_ops = { + .tx = at76_mac80211_tx, + .add_interface = at76_add_interface, + .remove_interface = at76_remove_interface, + .config = at76_config, + .bss_info_changed = at76_bss_info_changed, + .configure_filter = at76_configure_filter, + .start = at76_mac80211_start, + .stop = at76_mac80211_stop, + .hw_scan = at76_hw_scan, + .set_key = at76_set_key, +}; + +/* Allocate network device and initialize private data */ +static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) +{ + struct ieee80211_hw *hw; + struct at76_priv *priv; + + hw = ieee80211_alloc_hw(sizeof(struct at76_priv), &at76_ops); + if (!hw) { + printk(KERN_ERR DRIVER_NAME ": could not register" + " ieee80211_hw\n"); + return NULL; + } + + priv = hw->priv; + priv->hw = hw; + + priv->udev = udev; + + mutex_init(&priv->mtx); + INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc); + INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx); + INIT_WORK(&priv->work_join_bssid, at76_work_join_bssid); + INIT_DELAYED_WORK(&priv->dwork_hw_scan, at76_dwork_hw_scan); + + tasklet_init(&priv->rx_tasklet, at76_rx_tasklet, 0); + + priv->pm_mode = AT76_PM_OFF; + priv->pm_period = 0; + + /* unit us */ + + return priv; +} + +static int at76_alloc_urbs(struct at76_priv *priv, + struct usb_interface *interface) +{ + struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out; + int i; + int buffer_size; + struct usb_host_interface *iface_desc; + + at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); + + at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__, + interface->altsetting[0].desc.bNumEndpoints); + + ep_in = NULL; + ep_out = NULL; + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; + + at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x", + __func__, i, endpoint->bEndpointAddress, + endpoint->bmAttributes); + + if (!ep_in && usb_endpoint_is_bulk_in(endpoint)) + ep_in = endpoint; + + if (!ep_out && usb_endpoint_is_bulk_out(endpoint)) + ep_out = endpoint; + } + + if (!ep_in || !ep_out) { + dev_err(&interface->dev, "bulk endpoints missing\n"); + return -ENXIO; + } + + priv->rx_pipe = usb_rcvbulkpipe(priv->udev, ep_in->bEndpointAddress); + priv->tx_pipe = usb_sndbulkpipe(priv->udev, ep_out->bEndpointAddress); + + priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!priv->rx_urb || !priv->tx_urb) { + dev_err(&interface->dev, "cannot allocate URB\n"); + return -ENOMEM; + } + + buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE; + priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!priv->bulk_out_buffer) + return -ENOMEM; + + at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); + + return 0; +} + +static struct ieee80211_rate at76_rates[] = { + { .bitrate = 10, .hw_value = TX_RATE_1MBIT, }, + { .bitrate = 20, .hw_value = TX_RATE_2MBIT, }, + { .bitrate = 55, .hw_value = TX_RATE_5_5MBIT, }, + { .bitrate = 110, .hw_value = TX_RATE_11MBIT, }, +}; + +static struct ieee80211_channel at76_channels[] = { + { .center_freq = 2412, .hw_value = 1 }, + { .center_freq = 2417, .hw_value = 2 }, + { .center_freq = 2422, .hw_value = 3 }, + { .center_freq = 2427, .hw_value = 4 }, + { .center_freq = 2432, .hw_value = 5 }, + { .center_freq = 2437, .hw_value = 6 }, + { .center_freq = 2442, .hw_value = 7 }, + { .center_freq = 2447, .hw_value = 8 }, + { .center_freq = 2452, .hw_value = 9 }, + { .center_freq = 2457, .hw_value = 10 }, + { .center_freq = 2462, .hw_value = 11 }, + { .center_freq = 2467, .hw_value = 12 }, + { .center_freq = 2472, .hw_value = 13 }, + { .center_freq = 2484, .hw_value = 14 } +}; + +static struct ieee80211_supported_band at76_supported_band = { + .channels = at76_channels, + .n_channels = ARRAY_SIZE(at76_channels), + .bitrates = at76_rates, + .n_bitrates = ARRAY_SIZE(at76_rates), +}; + +/* Register network device and initialize the hardware */ +static int at76_init_new_device(struct at76_priv *priv, + struct usb_interface *interface) +{ + struct wiphy *wiphy; + size_t len; + int ret; + + /* set up the endpoint information */ + /* check out the endpoints */ + + at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints", + interface->cur_altsetting->desc.bNumEndpoints); + + ret = at76_alloc_urbs(priv, interface); + if (ret < 0) + goto exit; + + /* MAC address */ + ret = at76_get_hw_config(priv); + if (ret < 0) { + dev_err(&interface->dev, "cannot get MAC address\n"); + goto exit; + } + + priv->domain = at76_get_reg_domain(priv->regulatory_domain); + + priv->channel = DEF_CHANNEL; + priv->iw_mode = IW_MODE_INFRA; + priv->rts_threshold = DEF_RTS_THRESHOLD; + priv->frag_threshold = DEF_FRAG_THRESHOLD; + priv->short_retry_limit = DEF_SHORT_RETRY_LIMIT; + priv->txrate = TX_RATE_AUTO; + priv->preamble_type = PREAMBLE_TYPE_LONG; + priv->beacon_period = 100; + priv->auth_mode = WLAN_AUTH_OPEN; + priv->scan_min_time = DEF_SCAN_MIN_TIME; + priv->scan_max_time = DEF_SCAN_MAX_TIME; + priv->scan_mode = SCAN_TYPE_ACTIVE; + priv->device_unplugged = 0; + + /* mac80211 initialisation */ + wiphy = priv->hw->wiphy; + priv->hw->wiphy->max_scan_ssids = 1; + priv->hw->wiphy->max_scan_ie_len = 0; + priv->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &at76_supported_band; + ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); + ieee80211_hw_set(priv->hw, SIGNAL_UNSPEC); + priv->hw->max_signal = 100; + + SET_IEEE80211_DEV(priv->hw, &interface->dev); + SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); + + len = sizeof(wiphy->fw_version); + snprintf(wiphy->fw_version, len, "%d.%d.%d-%d", + priv->fw_version.major, priv->fw_version.minor, + priv->fw_version.patch, priv->fw_version.build); + + wiphy->hw_version = priv->board_type; + + ret = ieee80211_register_hw(priv->hw); + if (ret) { + printk(KERN_ERR "cannot register mac80211 hw (status %d)!\n", + ret); + goto exit; + } + + priv->mac80211_registered = 1; + + wiphy_info(priv->hw->wiphy, "USB %s, MAC %pM, firmware %d.%d.%d-%d\n", + dev_name(&interface->dev), priv->mac_addr, + priv->fw_version.major, priv->fw_version.minor, + priv->fw_version.patch, priv->fw_version.build); + wiphy_info(priv->hw->wiphy, "regulatory domain 0x%02x: %s\n", + priv->regulatory_domain, priv->domain->name); + +exit: + return ret; +} + +static void at76_delete_device(struct at76_priv *priv) +{ + at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); + + /* The device is gone, don't bother turning it off */ + priv->device_unplugged = 1; + + tasklet_kill(&priv->rx_tasklet); + + if (priv->mac80211_registered) + ieee80211_unregister_hw(priv->hw); + + if (priv->tx_urb) { + usb_kill_urb(priv->tx_urb); + usb_free_urb(priv->tx_urb); + } + if (priv->rx_urb) { + usb_kill_urb(priv->rx_urb); + usb_free_urb(priv->rx_urb); + } + + at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __func__); + + kfree(priv->bulk_out_buffer); + + del_timer_sync(&ledtrig_tx_timer); + + kfree_skb(priv->rx_skb); + + at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/ieee80211_hw", + __func__); + ieee80211_free_hw(priv->hw); + + at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); +} + +static int at76_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int ret; + struct at76_priv *priv; + struct fwentry *fwe; + struct usb_device *udev; + int op_mode; + int need_ext_fw = 0; + struct mib_fw_version *fwv = NULL; + int board_type = (int)id->driver_info; + + udev = usb_get_dev(interface_to_usbdev(interface)); + + fwv = kmalloc(sizeof(*fwv), GFP_KERNEL); + if (!fwv) { + ret = -ENOMEM; + goto exit; + } + + /* Load firmware into kernel memory */ + fwe = at76_load_firmware(udev, board_type); + if (!fwe) { + ret = -ENOENT; + goto exit; + } + + op_mode = at76_get_op_mode(udev); + + at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); + + /* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ??? + we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */ + + if (op_mode == OPMODE_HW_CONFIG_MODE) { + dev_err(&interface->dev, + "cannot handle a device in HW_CONFIG_MODE\n"); + ret = -EBUSY; + goto exit; + } + + if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH + && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { + /* download internal firmware part */ + dev_printk(KERN_DEBUG, &interface->dev, + "downloading internal firmware\n"); + ret = at76_load_internal_fw(udev, fwe); + if (ret < 0) { + dev_err(&interface->dev, + "error %d downloading internal firmware\n", + ret); + goto exit; + } + usb_put_dev(udev); + goto exit; + } + + /* Internal firmware already inside the device. Get firmware + * version to test if external firmware is loaded. + * This works only for newer firmware, e.g. the Intersil 0.90.x + * says "control timeout on ep0in" and subsequent + * at76_get_op_mode() fail too :-( */ + + /* if version >= 0.100.x.y or device with built-in flash we can + * query the device for the fw version */ + if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100) + || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { + ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); + if (ret < 0 || (fwv->major | fwv->minor) == 0) + need_ext_fw = 1; + } else + /* No way to check firmware version, reload to be sure */ + need_ext_fw = 1; + + if (need_ext_fw) { + dev_printk(KERN_DEBUG, &interface->dev, + "downloading external firmware\n"); + + ret = at76_load_external_fw(udev, fwe); + if (ret < 0) + goto exit; + + /* Re-check firmware version */ + ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); + if (ret < 0) { + dev_err(&interface->dev, + "error %d getting firmware version\n", ret); + goto exit; + } + } + + priv = at76_alloc_new_device(udev); + if (!priv) { + ret = -ENOMEM; + goto exit; + } + + usb_set_intfdata(interface, priv); + + memcpy(&priv->fw_version, fwv, sizeof(struct mib_fw_version)); + priv->board_type = board_type; + + ret = at76_init_new_device(priv, interface); + if (ret < 0) + at76_delete_device(priv); + +exit: + kfree(fwv); + if (ret < 0) + usb_put_dev(udev); + return ret; +} + +static void at76_disconnect(struct usb_interface *interface) +{ + struct at76_priv *priv; + + priv = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + /* Disconnect after loading internal firmware */ + if (!priv) + return; + + wiphy_info(priv->hw->wiphy, "disconnecting\n"); + at76_delete_device(priv); + usb_put_dev(priv->udev); + dev_info(&interface->dev, "disconnected\n"); +} + +/* Structure for registering this driver with the USB subsystem */ +static struct usb_driver at76_driver = { + .name = DRIVER_NAME, + .probe = at76_probe, + .disconnect = at76_disconnect, + .id_table = dev_table, + .disable_hub_initiated_lpm = 1, +}; + +static int __init at76_mod_init(void) +{ + int result; + + printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " loading\n"); + + mutex_init(&fw_mutex); + + /* register this driver with the USB subsystem */ + result = usb_register(&at76_driver); + if (result < 0) + printk(KERN_ERR DRIVER_NAME + ": usb_register failed (status %d)\n", result); + + led_trigger_register_simple("at76_usb-tx", &ledtrig_tx); + return result; +} + +static void __exit at76_mod_exit(void) +{ + int i; + + printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n"); + usb_deregister(&at76_driver); + for (i = 0; i < ARRAY_SIZE(firmwares); i++) + release_firmware(firmwares[i].fw); + led_trigger_unregister_simple(ledtrig_tx); +} + +module_param_named(debug, at76_debug, uint, 0600); +MODULE_PARM_DESC(debug, "Debugging level"); + +module_init(at76_mod_init); +module_exit(at76_mod_exit); + +MODULE_AUTHOR("Oliver Kurth "); +MODULE_AUTHOR("Joerg Albert "); +MODULE_AUTHOR("Alex "); +MODULE_AUTHOR("Nick Jones"); +MODULE_AUTHOR("Balint Seeber "); +MODULE_AUTHOR("Pavel Roskin "); +MODULE_AUTHOR("Guido Guenther "); +MODULE_AUTHOR("Kalle Valo "); +MODULE_AUTHOR("Sebastian Smolorz "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/atmel/at76c50x-usb.h b/drivers/net/wireless/atmel/at76c50x-usb.h new file mode 100644 index 000000000000..ae03271f878e --- /dev/null +++ b/drivers/net/wireless/atmel/at76c50x-usb.h @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2002,2003 Oliver Kurth + * (c) 2003,2004 Joerg Albert + * (c) 2007 Guido Guenther + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This driver was based on information from the Sourceforge driver + * released and maintained by Atmel: + * + * http://sourceforge.net/projects/atmelwlandriver/ + * + * Although the code was completely re-written, + * it would have been impossible without Atmel's decision to + * release an Open Source driver (unfortunately the firmware was + * kept binary only). Thanks for that decision to Atmel! + */ + +#ifndef _AT76_USB_H +#define _AT76_USB_H + +/* Board types */ +enum board_type { + BOARD_503_ISL3861 = 1, + BOARD_503_ISL3863 = 2, + BOARD_503 = 3, + BOARD_503_ACC = 4, + BOARD_505 = 5, + BOARD_505_2958 = 6, + BOARD_505A = 7, + BOARD_505AMX = 8 +}; + +#define CMD_STATUS_IDLE 0x00 +#define CMD_STATUS_COMPLETE 0x01 +#define CMD_STATUS_UNKNOWN 0x02 +#define CMD_STATUS_INVALID_PARAMETER 0x03 +#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 +#define CMD_STATUS_TIME_OUT 0x07 +#define CMD_STATUS_IN_PROGRESS 0x08 +#define CMD_STATUS_HOST_FAILURE 0xff +#define CMD_STATUS_SCAN_FAILED 0xf0 + +/* answers to get op mode */ +#define OPMODE_NONE 0x00 +#define OPMODE_NORMAL_NIC_WITH_FLASH 0x01 +#define OPMODE_HW_CONFIG_MODE 0x02 +#define OPMODE_DFU_MODE_WITH_FLASH 0x03 +#define OPMODE_NORMAL_NIC_WITHOUT_FLASH 0x04 + +#define CMD_SET_MIB 0x01 +#define CMD_GET_MIB 0x02 +#define CMD_SCAN 0x03 +#define CMD_JOIN 0x04 +#define CMD_START_IBSS 0x05 +#define CMD_RADIO_ON 0x06 +#define CMD_RADIO_OFF 0x07 +#define CMD_STARTUP 0x0B + +#define MIB_LOCAL 0x01 +#define MIB_MAC_ADDR 0x02 +#define MIB_MAC 0x03 +#define MIB_MAC_MGMT 0x05 +#define MIB_MAC_WEP 0x06 +#define MIB_PHY 0x07 +#define MIB_FW_VERSION 0x08 +#define MIB_MDOMAIN 0x09 + +#define ADHOC_MODE 1 +#define INFRASTRUCTURE_MODE 2 + +/* values for struct mib_local, field preamble_type */ +#define PREAMBLE_TYPE_LONG 0 +#define PREAMBLE_TYPE_SHORT 1 +#define PREAMBLE_TYPE_AUTO 2 + +/* values for tx_rate */ +#define TX_RATE_1MBIT 0 +#define TX_RATE_2MBIT 1 +#define TX_RATE_5_5MBIT 2 +#define TX_RATE_11MBIT 3 +#define TX_RATE_AUTO 4 + +/* power management modes */ +#define AT76_PM_OFF 1 +#define AT76_PM_ON 2 +#define AT76_PM_SMART 3 + +struct hwcfg_r505 { + u8 cr39_values[14]; + u8 reserved1[14]; + u8 bb_cr[14]; + u8 pidvid[4]; + u8 mac_addr[ETH_ALEN]; + u8 regulatory_domain; + u8 reserved2[14]; + u8 cr15_values[14]; + u8 reserved3[3]; +} __packed; + +struct hwcfg_rfmd { + u8 cr20_values[14]; + u8 cr21_values[14]; + u8 bb_cr[14]; + u8 pidvid[4]; + u8 mac_addr[ETH_ALEN]; + u8 regulatory_domain; + u8 low_power_values[14]; + u8 normal_power_values[14]; + u8 reserved1[3]; +} __packed; + +struct hwcfg_intersil { + u8 mac_addr[ETH_ALEN]; + u8 cr31_values[14]; + u8 cr58_values[14]; + u8 pidvid[4]; + u8 regulatory_domain; + u8 reserved[1]; +} __packed; + +union at76_hwcfg { + struct hwcfg_intersil i; + struct hwcfg_rfmd r3; + struct hwcfg_r505 r5; +}; + +#define WEP_SMALL_KEY_LEN (40 / 8) +#define WEP_LARGE_KEY_LEN (104 / 8) +#define WEP_KEYS (4) + +struct at76_card_config { + u8 exclude_unencrypted; + u8 promiscuous_mode; + u8 short_retry_limit; + u8 encryption_type; + __le16 rts_threshold; + __le16 fragmentation_threshold; /* 256..2346 */ + u8 basic_rate_set[4]; + u8 auto_rate_fallback; /* 0,1 */ + u8 channel; + u8 privacy_invoked; + u8 wep_default_key_id; /* 0..3 */ + u8 current_ssid[32]; + u8 wep_default_key_value[4][WEP_LARGE_KEY_LEN]; + u8 ssid_len; + u8 short_preamble; + __le16 beacon_period; +} __packed; + +struct at76_command { + u8 cmd; + u8 reserved; + __le16 size; + u8 data[0]; +} __packed; + +/* Length of Atmel-specific Rx header before 802.11 frame */ +#define AT76_RX_HDRLEN offsetof(struct at76_rx_buffer, packet) + +struct at76_rx_buffer { + __le16 wlength; + u8 rx_rate; + u8 newbss; + u8 fragmentation; + u8 rssi; + u8 link_quality; + u8 noise_level; + __le32 rx_time; + u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; +} __packed; + +/* Length of Atmel-specific Tx header before 802.11 frame */ +#define AT76_TX_HDRLEN offsetof(struct at76_tx_buffer, packet) + +struct at76_tx_buffer { + __le16 wlength; + u8 tx_rate; + u8 padding; + u8 reserved[4]; + u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; +} __packed; + +/* defines for scan_type below */ +#define SCAN_TYPE_ACTIVE 0 +#define SCAN_TYPE_PASSIVE 1 + +struct at76_req_scan { + u8 bssid[ETH_ALEN]; + u8 essid[32]; + u8 scan_type; + u8 channel; + __le16 probe_delay; + __le16 min_channel_time; + __le16 max_channel_time; + u8 essid_size; + u8 international_scan; +} __packed; + +struct at76_req_ibss { + u8 bssid[ETH_ALEN]; + u8 essid[32]; + u8 bss_type; + u8 channel; + u8 essid_size; + u8 reserved[3]; +} __packed; + +struct at76_req_join { + u8 bssid[ETH_ALEN]; + u8 essid[32]; + u8 bss_type; + u8 channel; + __le16 timeout; + u8 essid_size; + u8 reserved; +} __packed; + +struct mib_local { + u16 reserved0; + u8 beacon_enable; + u8 txautorate_fallback; + u8 reserved1; + u8 ssid_size; + u8 promiscuous_mode; + u16 reserved2; + u8 preamble_type; + u16 reserved3; +} __packed; + +struct mib_mac_addr { + u8 mac_addr[ETH_ALEN]; + u8 res[2]; /* ??? */ + u8 group_addr[4][ETH_ALEN]; + u8 group_addr_status[4]; +} __packed; + +struct mib_mac { + __le32 max_tx_msdu_lifetime; + __le32 max_rx_lifetime; + __le16 frag_threshold; + __le16 rts_threshold; + __le16 cwmin; + __le16 cwmax; + u8 short_retry_time; + u8 long_retry_time; + u8 scan_type; /* active or passive */ + u8 scan_channel; + __le16 probe_delay; /* delay before ProbeReq in active scan, RO */ + __le16 min_channel_time; + __le16 max_channel_time; + __le16 listen_interval; + u8 desired_ssid[32]; + u8 desired_bssid[ETH_ALEN]; + u8 desired_bsstype; /* ad-hoc or infrastructure */ + u8 reserved2; +} __packed; + +struct mib_mac_mgmt { + __le16 beacon_period; + __le16 CFP_max_duration; + __le16 medium_occupancy_limit; + __le16 station_id; /* assoc id */ + __le16 ATIM_window; + u8 CFP_mode; + u8 privacy_option_implemented; + u8 DTIM_period; + u8 CFP_period; + u8 current_bssid[ETH_ALEN]; + u8 current_essid[32]; + u8 current_bss_type; + u8 power_mgmt_mode; + /* rfmd and 505 */ + u8 ibss_change; + u8 res; + u8 multi_domain_capability_implemented; + u8 multi_domain_capability_enabled; + u8 country_string[IEEE80211_COUNTRY_STRING_LEN]; + u8 reserved[3]; +} __packed; + +struct mib_mac_wep { + u8 privacy_invoked; /* 0 disable encr., 1 enable encr */ + u8 wep_default_key_id; + u8 wep_key_mapping_len; + u8 exclude_unencrypted; + __le32 wep_icv_error_count; + __le32 wep_excluded_count; + u8 wep_default_keyvalue[WEP_KEYS][WEP_LARGE_KEY_LEN]; + u8 encryption_level; /* 1 for 40bit, 2 for 104bit encryption */ +} __packed; + +struct mib_phy { + __le32 ed_threshold; + + __le16 slot_time; + __le16 sifs_time; + __le16 preamble_length; + __le16 plcp_header_length; + __le16 mpdu_max_length; + __le16 cca_mode_supported; + + u8 operation_rate_set[4]; + u8 channel_id; + u8 current_cca_mode; + u8 phy_type; + u8 current_reg_domain; +} __packed; + +struct mib_fw_version { + u8 major; + u8 minor; + u8 patch; + u8 build; +} __packed; + +struct mib_mdomain { + u8 tx_powerlevel[14]; + u8 channel_list[14]; /* 0 for invalid channels */ +} __packed; + +struct set_mib_buffer { + u8 type; + u8 size; + u8 index; + u8 reserved; + union { + u8 byte; + __le16 word; + u8 addr[ETH_ALEN]; + struct mib_mac_wep wep_mib; + } data; +} __packed; + +struct at76_fw_header { + __le32 crc; /* CRC32 of the whole image */ + __le32 board_type; /* firmware compatibility code */ + u8 build; /* firmware build number */ + u8 patch; /* firmware patch level */ + u8 minor; /* firmware minor version */ + u8 major; /* firmware major version */ + __le32 str_offset; /* offset of the copyright string */ + __le32 int_fw_offset; /* internal firmware image offset */ + __le32 int_fw_len; /* internal firmware image length */ + __le32 ext_fw_offset; /* external firmware image offset */ + __le32 ext_fw_len; /* external firmware image length */ +} __packed; + +/* a description of a regulatory domain and the allowed channels */ +struct reg_domain { + u16 code; + char const *name; + u32 channel_map; /* if bit N is set, channel (N+1) is allowed */ +}; + +/* Data for one loaded firmware file */ +struct fwentry { + const char *const fwname; + const struct firmware *fw; + int extfw_size; + int intfw_size; + /* pointer to loaded firmware, no need to free */ + u8 *extfw; /* external firmware, extfw_size bytes long */ + u8 *intfw; /* internal firmware, intfw_size bytes long */ + enum board_type board_type; /* board type */ + struct mib_fw_version fw_version; + int loaded; /* Loaded and parsed successfully */ +}; + +struct at76_priv { + struct usb_device *udev; /* USB device pointer */ + + struct sk_buff *rx_skb; /* skbuff for receiving data */ + struct sk_buff *tx_skb; /* skbuff for transmitting data */ + void *bulk_out_buffer; /* buffer for sending data */ + + struct urb *tx_urb; /* URB for sending data */ + struct urb *rx_urb; /* URB for receiving data */ + + unsigned int tx_pipe; /* bulk out pipe */ + unsigned int rx_pipe; /* bulk in pipe */ + + struct mutex mtx; /* locks this structure */ + + /* work queues */ + struct work_struct work_set_promisc; + struct work_struct work_submit_rx; + struct work_struct work_join_bssid; + struct delayed_work dwork_hw_scan; + + struct tasklet_struct rx_tasklet; + + /* the WEP stuff */ + int wep_enabled; /* 1 if WEP is enabled */ + int wep_key_id; /* key id to be used */ + u8 wep_keys[WEP_KEYS][WEP_LARGE_KEY_LEN]; /* WEP keys */ + u8 wep_keys_len[WEP_KEYS]; /* length of WEP keys */ + + int channel; + int iw_mode; + u8 bssid[ETH_ALEN]; + u8 essid[IW_ESSID_MAX_SIZE]; + int essid_size; + int radio_on; + int promisc; + + int preamble_type; /* 0 - long, 1 - short, 2 - auto */ + int auth_mode; /* authentication type: 0 open, 1 shared key */ + int txrate; /* 0,1,2,3 = 1,2,5.5,11 Mbps, 4 is auto */ + int frag_threshold; /* threshold for fragmentation of tx packets */ + int rts_threshold; /* threshold for RTS mechanism */ + int short_retry_limit; + + int scan_min_time; /* scan min channel time */ + int scan_max_time; /* scan max channel time */ + int scan_mode; /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */ + int scan_need_any; /* if set, need to scan for any ESSID */ + bool scanning; /* if set, the scan is running */ + + u16 assoc_id; /* current association ID, if associated */ + + u8 pm_mode; /* power management mode */ + u32 pm_period; /* power management period in microseconds */ + + struct reg_domain const *domain; /* reg domain description */ + + /* These fields contain HW config provided by the device (not all of + * these fields are used by all board types) */ + u8 mac_addr[ETH_ALEN]; + u8 regulatory_domain; + + struct at76_card_config card_config; + + enum board_type board_type; + struct mib_fw_version fw_version; + + unsigned int device_unplugged:1; + unsigned int netdev_registered:1; + struct set_mib_buffer mib_buf; /* global buffer for set_mib calls */ + + int beacon_period; /* period of mgmt beacons, Kus */ + + struct ieee80211_hw *hw; + int mac80211_registered; +}; + +#define AT76_SUPPORTED_FILTERS 0 + +#define SCAN_POLL_INTERVAL (HZ / 4) + +#define CMD_COMPLETION_TIMEOUT (5 * HZ) + +#define DEF_RTS_THRESHOLD 1536 +#define DEF_FRAG_THRESHOLD 1536 +#define DEF_SHORT_RETRY_LIMIT 8 +#define DEF_CHANNEL 10 +#define DEF_SCAN_MIN_TIME 10 +#define DEF_SCAN_MAX_TIME 120 + +/* the max padding size for tx in bytes (see calc_padding) */ +#define MAX_PADDING_SIZE 53 + +#endif /* _AT76_USB_H */ diff --git a/drivers/net/wireless/atmel/atmel.c b/drivers/net/wireless/atmel/atmel.c new file mode 100644 index 000000000000..6a1f03c271c1 --- /dev/null +++ b/drivers/net/wireless/atmel/atmel.c @@ -0,0 +1,4549 @@ +/*** -*- linux-c -*- ********************************************************** + + Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. + + Copyright 2000-2001 ATMEL Corporation. + Copyright 2003-2004 Simon Kelley. + + This code was developed from version 2.1.1 of the Atmel drivers, + released by Atmel corp. under the GPL in December 2002. It also + includes code from the Linux aironet drivers (C) Benjamin Reed, + and the Linux PCMCIA package, (C) David Hinds and the Linux wireless + extensions, (C) Jean Tourrilhes. + + The firmware module for reading the MAC address of the card comes from + net.russotto.AtmelMACFW, written by Matthew T. Russotto and copyright + by him. net.russotto.AtmelMACFW is used under the GPL license version 2. + This file contains the module in binary form and, under the terms + of the GPL, in source form. The source is located at the end of the file. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Atmel wireless lan drivers; if not, see + . + + For all queries about this code, please contact the current author, + Simon Kelley and not Atmel Corporation. + + Credit is due to HP UK and Cambridge Online Systems Ltd for supplying + hardware used during development of this driver. + +******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "atmel.h" + +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 98 + +MODULE_AUTHOR("Simon Kelley"); +MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("Atmel at76c50x wireless cards"); + +/* The name of the firmware file to be loaded + over-rides any automatic selection */ +static char *firmware = NULL; +module_param(firmware, charp, 0); + +/* table of firmware file names */ +static struct { + AtmelFWType fw_type; + const char *fw_file; + const char *fw_file_ext; +} fw_table[] = { + { ATMEL_FW_TYPE_502, "atmel_at76c502", "bin" }, + { ATMEL_FW_TYPE_502D, "atmel_at76c502d", "bin" }, + { ATMEL_FW_TYPE_502E, "atmel_at76c502e", "bin" }, + { ATMEL_FW_TYPE_502_3COM, "atmel_at76c502_3com", "bin" }, + { ATMEL_FW_TYPE_504, "atmel_at76c504", "bin" }, + { ATMEL_FW_TYPE_504_2958, "atmel_at76c504_2958", "bin" }, + { ATMEL_FW_TYPE_504A_2958, "atmel_at76c504a_2958", "bin" }, + { ATMEL_FW_TYPE_506, "atmel_at76c506", "bin" }, + { ATMEL_FW_TYPE_NONE, NULL, NULL } +}; +MODULE_FIRMWARE("atmel_at76c502-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502.bin"); +MODULE_FIRMWARE("atmel_at76c502d-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502d.bin"); +MODULE_FIRMWARE("atmel_at76c502e-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502e.bin"); +MODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502_3com.bin"); +MODULE_FIRMWARE("atmel_at76c504-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c504.bin"); +MODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c504_2958.bin"); +MODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c504a_2958.bin"); +MODULE_FIRMWARE("atmel_at76c506-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c506.bin"); + +#define MAX_SSID_LENGTH 32 +#define MGMT_JIFFIES (256 * HZ / 100) + +#define MAX_BSS_ENTRIES 64 + +/* registers */ +#define GCR 0x00 /* (SIR0) General Configuration Register */ +#define BSR 0x02 /* (SIR1) Bank Switching Select Register */ +#define AR 0x04 +#define DR 0x08 +#define MR1 0x12 /* Mirror Register 1 */ +#define MR2 0x14 /* Mirror Register 2 */ +#define MR3 0x16 /* Mirror Register 3 */ +#define MR4 0x18 /* Mirror Register 4 */ + +#define GPR1 0x0c +#define GPR2 0x0e +#define GPR3 0x10 +/* + * Constants for the GCR register. + */ +#define GCR_REMAP 0x0400 /* Remap internal SRAM to 0 */ +#define GCR_SWRES 0x0080 /* BIU reset (ARM and PAI are NOT reset) */ +#define GCR_CORES 0x0060 /* Core Reset (ARM and PAI are reset) */ +#define GCR_ENINT 0x0002 /* Enable Interrupts */ +#define GCR_ACKINT 0x0008 /* Acknowledge Interrupts */ + +#define BSS_SRAM 0x0200 /* AMBA module selection --> SRAM */ +#define BSS_IRAM 0x0100 /* AMBA module selection --> IRAM */ +/* + *Constants for the MR registers. + */ +#define MAC_INIT_COMPLETE 0x0001 /* MAC init has been completed */ +#define MAC_BOOT_COMPLETE 0x0010 /* MAC boot has been completed */ +#define MAC_INIT_OK 0x0002 /* MAC boot has been completed */ + +#define MIB_MAX_DATA_BYTES 212 +#define MIB_HEADER_SIZE 4 /* first four fields */ + +struct get_set_mib { + u8 type; + u8 size; + u8 index; + u8 reserved; + u8 data[MIB_MAX_DATA_BYTES]; +}; + +struct rx_desc { + u32 Next; + u16 MsduPos; + u16 MsduSize; + + u8 State; + u8 Status; + u8 Rate; + u8 Rssi; + u8 LinkQuality; + u8 PreambleType; + u16 Duration; + u32 RxTime; +}; + +#define RX_DESC_FLAG_VALID 0x80 +#define RX_DESC_FLAG_CONSUMED 0x40 +#define RX_DESC_FLAG_IDLE 0x00 + +#define RX_STATUS_SUCCESS 0x00 + +#define RX_DESC_MSDU_POS_OFFSET 4 +#define RX_DESC_MSDU_SIZE_OFFSET 6 +#define RX_DESC_FLAGS_OFFSET 8 +#define RX_DESC_STATUS_OFFSET 9 +#define RX_DESC_RSSI_OFFSET 11 +#define RX_DESC_LINK_QUALITY_OFFSET 12 +#define RX_DESC_PREAMBLE_TYPE_OFFSET 13 +#define RX_DESC_DURATION_OFFSET 14 +#define RX_DESC_RX_TIME_OFFSET 16 + +struct tx_desc { + u32 NextDescriptor; + u16 TxStartOfFrame; + u16 TxLength; + + u8 TxState; + u8 TxStatus; + u8 RetryCount; + + u8 TxRate; + + u8 KeyIndex; + u8 ChiperType; + u8 ChipreLength; + u8 Reserved1; + + u8 Reserved; + u8 PacketType; + u16 HostTxLength; +}; + +#define TX_DESC_NEXT_OFFSET 0 +#define TX_DESC_POS_OFFSET 4 +#define TX_DESC_SIZE_OFFSET 6 +#define TX_DESC_FLAGS_OFFSET 8 +#define TX_DESC_STATUS_OFFSET 9 +#define TX_DESC_RETRY_OFFSET 10 +#define TX_DESC_RATE_OFFSET 11 +#define TX_DESC_KEY_INDEX_OFFSET 12 +#define TX_DESC_CIPHER_TYPE_OFFSET 13 +#define TX_DESC_CIPHER_LENGTH_OFFSET 14 +#define TX_DESC_PACKET_TYPE_OFFSET 17 +#define TX_DESC_HOST_LENGTH_OFFSET 18 + +/* + * Host-MAC interface + */ + +#define TX_STATUS_SUCCESS 0x00 + +#define TX_FIRM_OWN 0x80 +#define TX_DONE 0x40 + +#define TX_ERROR 0x01 + +#define TX_PACKET_TYPE_DATA 0x01 +#define TX_PACKET_TYPE_MGMT 0x02 + +#define ISR_EMPTY 0x00 /* no bits set in ISR */ +#define ISR_TxCOMPLETE 0x01 /* packet transmitted */ +#define ISR_RxCOMPLETE 0x02 /* packet received */ +#define ISR_RxFRAMELOST 0x04 /* Rx Frame lost */ +#define ISR_FATAL_ERROR 0x08 /* Fatal error */ +#define ISR_COMMAND_COMPLETE 0x10 /* command completed */ +#define ISR_OUT_OF_RANGE 0x20 /* command completed */ +#define ISR_IBSS_MERGE 0x40 /* (4.1.2.30): IBSS merge */ +#define ISR_GENERIC_IRQ 0x80 + +#define Local_Mib_Type 0x01 +#define Mac_Address_Mib_Type 0x02 +#define Mac_Mib_Type 0x03 +#define Statistics_Mib_Type 0x04 +#define Mac_Mgmt_Mib_Type 0x05 +#define Mac_Wep_Mib_Type 0x06 +#define Phy_Mib_Type 0x07 +#define Multi_Domain_MIB 0x08 + +#define MAC_MGMT_MIB_CUR_BSSID_POS 14 +#define MAC_MIB_FRAG_THRESHOLD_POS 8 +#define MAC_MIB_RTS_THRESHOLD_POS 10 +#define MAC_MIB_SHORT_RETRY_POS 16 +#define MAC_MIB_LONG_RETRY_POS 17 +#define MAC_MIB_SHORT_RETRY_LIMIT_POS 16 +#define MAC_MGMT_MIB_BEACON_PER_POS 0 +#define MAC_MGMT_MIB_STATION_ID_POS 6 +#define MAC_MGMT_MIB_CUR_PRIVACY_POS 11 +#define MAC_MGMT_MIB_CUR_BSSID_POS 14 +#define MAC_MGMT_MIB_PS_MODE_POS 53 +#define MAC_MGMT_MIB_LISTEN_INTERVAL_POS 54 +#define MAC_MGMT_MIB_MULTI_DOMAIN_IMPLEMENTED 56 +#define MAC_MGMT_MIB_MULTI_DOMAIN_ENABLED 57 +#define PHY_MIB_CHANNEL_POS 14 +#define PHY_MIB_RATE_SET_POS 20 +#define PHY_MIB_REG_DOMAIN_POS 26 +#define LOCAL_MIB_AUTO_TX_RATE_POS 3 +#define LOCAL_MIB_SSID_SIZE 5 +#define LOCAL_MIB_TX_PROMISCUOUS_POS 6 +#define LOCAL_MIB_TX_MGMT_RATE_POS 7 +#define LOCAL_MIB_TX_CONTROL_RATE_POS 8 +#define LOCAL_MIB_PREAMBLE_TYPE 9 +#define MAC_ADDR_MIB_MAC_ADDR_POS 0 + +#define CMD_Set_MIB_Vars 0x01 +#define CMD_Get_MIB_Vars 0x02 +#define CMD_Scan 0x03 +#define CMD_Join 0x04 +#define CMD_Start 0x05 +#define CMD_EnableRadio 0x06 +#define CMD_DisableRadio 0x07 +#define CMD_SiteSurvey 0x0B + +#define CMD_STATUS_IDLE 0x00 +#define CMD_STATUS_COMPLETE 0x01 +#define CMD_STATUS_UNKNOWN 0x02 +#define CMD_STATUS_INVALID_PARAMETER 0x03 +#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 +#define CMD_STATUS_TIME_OUT 0x07 +#define CMD_STATUS_IN_PROGRESS 0x08 +#define CMD_STATUS_REJECTED_RADIO_OFF 0x09 +#define CMD_STATUS_HOST_ERROR 0xFF +#define CMD_STATUS_BUSY 0xFE + +#define CMD_BLOCK_COMMAND_OFFSET 0 +#define CMD_BLOCK_STATUS_OFFSET 1 +#define CMD_BLOCK_PARAMETERS_OFFSET 4 + +#define SCAN_OPTIONS_SITE_SURVEY 0x80 + +#define MGMT_FRAME_BODY_OFFSET 24 +#define MAX_AUTHENTICATION_RETRIES 3 +#define MAX_ASSOCIATION_RETRIES 3 + +#define AUTHENTICATION_RESPONSE_TIME_OUT 1000 + +#define MAX_WIRELESS_BODY 2316 /* mtu is 2312, CRC is 4 */ +#define LOOP_RETRY_LIMIT 500000 + +#define ACTIVE_MODE 1 +#define PS_MODE 2 + +#define MAX_ENCRYPTION_KEYS 4 +#define MAX_ENCRYPTION_KEY_SIZE 40 + +/* + * 802.11 related definitions + */ + +/* + * Regulatory Domains + */ + +#define REG_DOMAIN_FCC 0x10 /* Channels 1-11 USA */ +#define REG_DOMAIN_DOC 0x20 /* Channel 1-11 Canada */ +#define REG_DOMAIN_ETSI 0x30 /* Channel 1-13 Europe (ex Spain/France) */ +#define REG_DOMAIN_SPAIN 0x31 /* Channel 10-11 Spain */ +#define REG_DOMAIN_FRANCE 0x32 /* Channel 10-13 France */ +#define REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */ +#define REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan(MKK1) */ +#define REG_DOMAIN_ISRAEL 0x50 /* Channel 3-9 ISRAEL */ + +#define BSS_TYPE_AD_HOC 1 +#define BSS_TYPE_INFRASTRUCTURE 2 + +#define SCAN_TYPE_ACTIVE 0 +#define SCAN_TYPE_PASSIVE 1 + +#define LONG_PREAMBLE 0 +#define SHORT_PREAMBLE 1 +#define AUTO_PREAMBLE 2 + +#define DATA_FRAME_WS_HEADER_SIZE 30 + +/* promiscuous mode control */ +#define PROM_MODE_OFF 0x0 +#define PROM_MODE_UNKNOWN 0x1 +#define PROM_MODE_CRC_FAILED 0x2 +#define PROM_MODE_DUPLICATED 0x4 +#define PROM_MODE_MGMT 0x8 +#define PROM_MODE_CTRL 0x10 +#define PROM_MODE_BAD_PROTOCOL 0x20 + +#define IFACE_INT_STATUS_OFFSET 0 +#define IFACE_INT_MASK_OFFSET 1 +#define IFACE_LOCKOUT_HOST_OFFSET 2 +#define IFACE_LOCKOUT_MAC_OFFSET 3 +#define IFACE_FUNC_CTRL_OFFSET 28 +#define IFACE_MAC_STAT_OFFSET 30 +#define IFACE_GENERIC_INT_TYPE_OFFSET 32 + +#define CIPHER_SUITE_NONE 0 +#define CIPHER_SUITE_WEP_64 1 +#define CIPHER_SUITE_TKIP 2 +#define CIPHER_SUITE_AES 3 +#define CIPHER_SUITE_CCX 4 +#define CIPHER_SUITE_WEP_128 5 + +/* + * IFACE MACROS & definitions + */ + +/* + * FuncCtrl field: + */ +#define FUNC_CTRL_TxENABLE 0x10 +#define FUNC_CTRL_RxENABLE 0x20 +#define FUNC_CTRL_INIT_COMPLETE 0x01 + +/* A stub firmware image which reads the MAC address from NVRAM on the card. + For copyright information and source see the end of this file. */ +static u8 mac_reader[] = { + 0x06, 0x00, 0x00, 0xea, 0x04, 0x00, 0x00, 0xea, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xea, + 0x01, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, + 0xd3, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x21, 0xe1, 0x0e, 0x04, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, + 0x81, 0x11, 0xa0, 0xe1, 0x00, 0x10, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x1c, 0x10, 0x90, 0xe5, + 0x10, 0x10, 0xc1, 0xe3, 0x1c, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 0x08, 0x10, 0x80, 0xe5, + 0x02, 0x03, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0xb0, 0x10, 0xc0, 0xe1, 0xb4, 0x10, 0xc0, 0xe1, + 0xb8, 0x10, 0xc0, 0xe1, 0xbc, 0x10, 0xc0, 0xe1, 0x56, 0xdc, 0xa0, 0xe3, 0x21, 0x00, 0x00, 0xeb, + 0x0a, 0x00, 0xa0, 0xe3, 0x1a, 0x00, 0x00, 0xeb, 0x10, 0x00, 0x00, 0xeb, 0x07, 0x00, 0x00, 0xeb, + 0x02, 0x03, 0xa0, 0xe3, 0x02, 0x14, 0xa0, 0xe3, 0xb4, 0x10, 0xc0, 0xe1, 0x4c, 0x10, 0x9f, 0xe5, + 0xbc, 0x10, 0xc0, 0xe1, 0x10, 0x10, 0xa0, 0xe3, 0xb8, 0x10, 0xc0, 0xe1, 0xfe, 0xff, 0xff, 0xea, + 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe3, 0x02, 0x3c, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, + 0x28, 0x00, 0x9f, 0xe5, 0x37, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, + 0x00, 0x40, 0x2d, 0xe9, 0x12, 0x2e, 0xa0, 0xe3, 0x06, 0x30, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, + 0x02, 0x04, 0xa0, 0xe3, 0x2f, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, + 0x00, 0x02, 0x00, 0x02, 0x80, 0x01, 0x90, 0xe0, 0x01, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x50, 0xe2, + 0xfc, 0xff, 0xff, 0xea, 0x1e, 0xff, 0x2f, 0xe1, 0x80, 0x10, 0xa0, 0xe3, 0xf3, 0x06, 0xa0, 0xe3, + 0x00, 0x10, 0x80, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, + 0x04, 0x10, 0x80, 0xe5, 0x00, 0x10, 0x80, 0xe5, 0x0e, 0x34, 0xa0, 0xe3, 0x1c, 0x10, 0x93, 0xe5, + 0x02, 0x1a, 0x81, 0xe3, 0x1c, 0x10, 0x83, 0xe5, 0x58, 0x11, 0x9f, 0xe5, 0x30, 0x10, 0x80, 0xe5, + 0x54, 0x11, 0x9f, 0xe5, 0x34, 0x10, 0x80, 0xe5, 0x38, 0x10, 0x80, 0xe5, 0x3c, 0x10, 0x80, 0xe5, + 0x10, 0x10, 0x90, 0xe5, 0x08, 0x00, 0x90, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0xf3, 0x16, 0xa0, 0xe3, + 0x08, 0x00, 0x91, 0xe5, 0x05, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 0x10, 0x00, 0x91, 0xe5, + 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0xff, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, + 0x10, 0x00, 0x91, 0xe5, 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, + 0x10, 0x00, 0x91, 0xe5, 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, + 0xff, 0x00, 0x00, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x30, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe1, + 0x03, 0x40, 0xa0, 0xe1, 0xa2, 0x02, 0xa0, 0xe1, 0x08, 0x00, 0x00, 0xe2, 0x03, 0x00, 0x80, 0xe2, + 0xd8, 0x10, 0x9f, 0xe5, 0x00, 0x00, 0xc1, 0xe5, 0x01, 0x20, 0xc1, 0xe5, 0xe2, 0xff, 0xff, 0xeb, + 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x14, 0x00, 0xa0, 0xe3, 0xc4, 0xff, 0xff, 0xeb, + 0x04, 0x20, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1, 0x02, 0x00, 0xa0, 0xe3, 0x01, 0x00, 0x00, 0xeb, + 0x30, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x70, 0x40, 0x2d, 0xe9, 0xf3, 0x46, 0xa0, 0xe3, + 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x08, 0x00, 0x00, 0x9a, 0x8c, 0x50, 0x9f, 0xe5, + 0x03, 0x60, 0xd5, 0xe7, 0x0c, 0x60, 0x84, 0xe5, 0x10, 0x60, 0x94, 0xe5, 0x02, 0x00, 0x16, 0xe3, + 0xfc, 0xff, 0xff, 0x0a, 0x01, 0x30, 0x83, 0xe2, 0x00, 0x00, 0x53, 0xe1, 0xf7, 0xff, 0xff, 0x3a, + 0xff, 0x30, 0xa0, 0xe3, 0x0c, 0x30, 0x84, 0xe5, 0x08, 0x00, 0x94, 0xe5, 0x10, 0x00, 0x94, 0xe5, + 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x94, 0xe5, 0x00, 0x00, 0xa0, 0xe3, + 0x00, 0x00, 0x52, 0xe3, 0x0b, 0x00, 0x00, 0x9a, 0x10, 0x50, 0x94, 0xe5, 0x02, 0x00, 0x15, 0xe3, + 0xfc, 0xff, 0xff, 0x0a, 0x0c, 0x30, 0x84, 0xe5, 0x10, 0x50, 0x94, 0xe5, 0x01, 0x00, 0x15, 0xe3, + 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x50, 0x94, 0xe5, 0x01, 0x50, 0xc1, 0xe4, 0x01, 0x00, 0x80, 0xe2, + 0x02, 0x00, 0x50, 0xe1, 0xf3, 0xff, 0xff, 0x3a, 0xc8, 0x00, 0xa0, 0xe3, 0x98, 0xff, 0xff, 0xeb, + 0x70, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x0c, 0x00, 0x02, 0x01, 0x02, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x02 +}; + +struct atmel_private { + void *card; /* Bus dependent structure varies for PCcard */ + int (*present_callback)(void *); /* And callback which uses it */ + char firmware_id[32]; + AtmelFWType firmware_type; + u8 *firmware; + int firmware_length; + struct timer_list management_timer; + struct net_device *dev; + struct device *sys_dev; + struct iw_statistics wstats; + spinlock_t irqlock, timerlock; /* spinlocks */ + enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type; + enum { + CARD_TYPE_PARALLEL_FLASH, + CARD_TYPE_SPI_FLASH, + CARD_TYPE_EEPROM + } card_type; + int do_rx_crc; /* If we need to CRC incoming packets */ + int probe_crc; /* set if we don't yet know */ + int crc_ok_cnt, crc_ko_cnt; /* counters for probing */ + u16 rx_desc_head; + u16 tx_desc_free, tx_desc_head, tx_desc_tail, tx_desc_previous; + u16 tx_free_mem, tx_buff_head, tx_buff_tail; + + u16 frag_seq, frag_len, frag_no; + u8 frag_source[6]; + + u8 wep_is_on, default_key, exclude_unencrypted, encryption_level; + u8 group_cipher_suite, pairwise_cipher_suite; + u8 wep_keys[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; + int wep_key_len[MAX_ENCRYPTION_KEYS]; + int use_wpa, radio_on_broken; /* firmware dependent stuff. */ + + u16 host_info_base; + struct host_info_struct { + /* NB this is matched to the hardware, don't change. */ + u8 volatile int_status; + u8 volatile int_mask; + u8 volatile lockout_host; + u8 volatile lockout_mac; + + u16 tx_buff_pos; + u16 tx_buff_size; + u16 tx_desc_pos; + u16 tx_desc_count; + + u16 rx_buff_pos; + u16 rx_buff_size; + u16 rx_desc_pos; + u16 rx_desc_count; + + u16 build_version; + u16 command_pos; + + u16 major_version; + u16 minor_version; + + u16 func_ctrl; + u16 mac_status; + u16 generic_IRQ_type; + u8 reserved[2]; + } host_info; + + enum { + STATION_STATE_SCANNING, + STATION_STATE_JOINNING, + STATION_STATE_AUTHENTICATING, + STATION_STATE_ASSOCIATING, + STATION_STATE_READY, + STATION_STATE_REASSOCIATING, + STATION_STATE_DOWN, + STATION_STATE_MGMT_ERROR + } station_state; + + int operating_mode, power_mode; + time_t last_qual; + int beacons_this_sec; + int channel; + int reg_domain, config_reg_domain; + int tx_rate; + int auto_tx_rate; + int rts_threshold; + int frag_threshold; + int long_retry, short_retry; + int preamble; + int default_beacon_period, beacon_period, listen_interval; + int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum; + int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt; + enum { + SITE_SURVEY_IDLE, + SITE_SURVEY_IN_PROGRESS, + SITE_SURVEY_COMPLETED + } site_survey_state; + unsigned long last_survey; + + int station_was_associated, station_is_associated; + int fast_scan; + + struct bss_info { + int channel; + int SSIDsize; + int RSSI; + int UsingWEP; + int preamble; + int beacon_period; + int BSStype; + u8 BSSID[6]; + u8 SSID[MAX_SSID_LENGTH]; + } BSSinfo[MAX_BSS_ENTRIES]; + int BSS_list_entries, current_BSS; + int connect_to_any_BSS; + int SSID_size, new_SSID_size; + u8 CurrentBSSID[6], BSSID[6]; + u8 SSID[MAX_SSID_LENGTH], new_SSID[MAX_SSID_LENGTH]; + u64 last_beacon_timestamp; + u8 rx_buf[MAX_WIRELESS_BODY]; +}; + +static u8 atmel_basic_rates[4] = {0x82, 0x84, 0x0b, 0x16}; + +static const struct { + int reg_domain; + int min, max; + char *name; +} channel_table[] = { { REG_DOMAIN_FCC, 1, 11, "USA" }, + { REG_DOMAIN_DOC, 1, 11, "Canada" }, + { REG_DOMAIN_ETSI, 1, 13, "Europe" }, + { REG_DOMAIN_SPAIN, 10, 11, "Spain" }, + { REG_DOMAIN_FRANCE, 10, 13, "France" }, + { REG_DOMAIN_MKK, 14, 14, "MKK" }, + { REG_DOMAIN_MKK1, 1, 14, "MKK1" }, + { REG_DOMAIN_ISRAEL, 3, 9, "Israel"} }; + +static void build_wpa_mib(struct atmel_private *priv); +static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void atmel_copy_to_card(struct net_device *dev, u16 dest, + const unsigned char *src, u16 len); +static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, + u16 src, u16 len); +static void atmel_set_gcr(struct net_device *dev, u16 mask); +static void atmel_clear_gcr(struct net_device *dev, u16 mask); +static int atmel_lock_mac(struct atmel_private *priv); +static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data); +static void atmel_command_irq(struct atmel_private *priv); +static int atmel_validate_channel(struct atmel_private *priv, int channel); +static void atmel_management_frame(struct atmel_private *priv, + struct ieee80211_hdr *header, + u16 frame_len, u8 rssi); +static void atmel_management_timer(u_long a); +static void atmel_send_command(struct atmel_private *priv, int command, + void *cmd, int cmd_size); +static int atmel_send_command_wait(struct atmel_private *priv, int command, + void *cmd, int cmd_size); +static void atmel_transmit_management_frame(struct atmel_private *priv, + struct ieee80211_hdr *header, + u8 *body, int body_len); + +static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index); +static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, + u8 data); +static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, + u16 data); +static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, + u8 *data, int data_len); +static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, + u8 *data, int data_len); +static void atmel_scan(struct atmel_private *priv, int specific_ssid); +static void atmel_join_bss(struct atmel_private *priv, int bss_index); +static void atmel_smooth_qual(struct atmel_private *priv); +static void atmel_writeAR(struct net_device *dev, u16 data); +static int probe_atmel_card(struct net_device *dev); +static int reset_atmel_card(struct net_device *dev); +static void atmel_enter_state(struct atmel_private *priv, int new_state); +int atmel_open (struct net_device *dev); + +static inline u16 atmel_hi(struct atmel_private *priv, u16 offset) +{ + return priv->host_info_base + offset; +} + +static inline u16 atmel_co(struct atmel_private *priv, u16 offset) +{ + return priv->host_info.command_pos + offset; +} + +static inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc) +{ + return priv->host_info.rx_desc_pos + (sizeof(struct rx_desc) * desc) + offset; +} + +static inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc) +{ + return priv->host_info.tx_desc_pos + (sizeof(struct tx_desc) * desc) + offset; +} + +static inline u8 atmel_read8(struct net_device *dev, u16 offset) +{ + return inb(dev->base_addr + offset); +} + +static inline void atmel_write8(struct net_device *dev, u16 offset, u8 data) +{ + outb(data, dev->base_addr + offset); +} + +static inline u16 atmel_read16(struct net_device *dev, u16 offset) +{ + return inw(dev->base_addr + offset); +} + +static inline void atmel_write16(struct net_device *dev, u16 offset, u16 data) +{ + outw(data, dev->base_addr + offset); +} + +static inline u8 atmel_rmem8(struct atmel_private *priv, u16 pos) +{ + atmel_writeAR(priv->dev, pos); + return atmel_read8(priv->dev, DR); +} + +static inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data) +{ + atmel_writeAR(priv->dev, pos); + atmel_write8(priv->dev, DR, data); +} + +static inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos) +{ + atmel_writeAR(priv->dev, pos); + return atmel_read16(priv->dev, DR); +} + +static inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data) +{ + atmel_writeAR(priv->dev, pos); + atmel_write16(priv->dev, DR, data); +} + +static const struct iw_handler_def atmel_handler_def; + +static void tx_done_irq(struct atmel_private *priv) +{ + int i; + + for (i = 0; + atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE && + i < priv->host_info.tx_desc_count; + i++) { + u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head)); + u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head)); + u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head)); + + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head), 0); + + priv->tx_free_mem += msdu_size; + priv->tx_desc_free++; + + if (priv->tx_buff_head + msdu_size > (priv->host_info.tx_buff_pos + priv->host_info.tx_buff_size)) + priv->tx_buff_head = 0; + else + priv->tx_buff_head += msdu_size; + + if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1)) + priv->tx_desc_head++ ; + else + priv->tx_desc_head = 0; + + if (type == TX_PACKET_TYPE_DATA) { + if (status == TX_STATUS_SUCCESS) + priv->dev->stats.tx_packets++; + else + priv->dev->stats.tx_errors++; + netif_wake_queue(priv->dev); + } + } +} + +static u16 find_tx_buff(struct atmel_private *priv, u16 len) +{ + u16 bottom_free = priv->host_info.tx_buff_size - priv->tx_buff_tail; + + if (priv->tx_desc_free == 3 || priv->tx_free_mem < len) + return 0; + + if (bottom_free >= len) + return priv->host_info.tx_buff_pos + priv->tx_buff_tail; + + if (priv->tx_free_mem - bottom_free >= len) { + priv->tx_buff_tail = 0; + return priv->host_info.tx_buff_pos; + } + + return 0; +} + +static void tx_update_descriptor(struct atmel_private *priv, int is_bcast, + u16 len, u16 buff, u8 type) +{ + atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, priv->tx_desc_tail), buff); + atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_tail), len); + if (!priv->use_wpa) + atmel_wmem16(priv, atmel_tx(priv, TX_DESC_HOST_LENGTH_OFFSET, priv->tx_desc_tail), len); + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_tail), type); + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RATE_OFFSET, priv->tx_desc_tail), priv->tx_rate); + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RETRY_OFFSET, priv->tx_desc_tail), 0); + if (priv->use_wpa) { + int cipher_type, cipher_length; + if (is_bcast) { + cipher_type = priv->group_cipher_suite; + if (cipher_type == CIPHER_SUITE_WEP_64 || + cipher_type == CIPHER_SUITE_WEP_128) + cipher_length = 8; + else if (cipher_type == CIPHER_SUITE_TKIP) + cipher_length = 12; + else if (priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_64 || + priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_128) { + cipher_type = priv->pairwise_cipher_suite; + cipher_length = 8; + } else { + cipher_type = CIPHER_SUITE_NONE; + cipher_length = 0; + } + } else { + cipher_type = priv->pairwise_cipher_suite; + if (cipher_type == CIPHER_SUITE_WEP_64 || + cipher_type == CIPHER_SUITE_WEP_128) + cipher_length = 8; + else if (cipher_type == CIPHER_SUITE_TKIP) + cipher_length = 12; + else if (priv->group_cipher_suite == CIPHER_SUITE_WEP_64 || + priv->group_cipher_suite == CIPHER_SUITE_WEP_128) { + cipher_type = priv->group_cipher_suite; + cipher_length = 8; + } else { + cipher_type = CIPHER_SUITE_NONE; + cipher_length = 0; + } + } + + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail), + cipher_type); + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_LENGTH_OFFSET, priv->tx_desc_tail), + cipher_length); + } + atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_tail), 0x80000000L); + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_tail), TX_FIRM_OWN); + if (priv->tx_desc_previous != priv->tx_desc_tail) + atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_previous), 0); + priv->tx_desc_previous = priv->tx_desc_tail; + if (priv->tx_desc_tail < (priv->host_info.tx_desc_count - 1)) + priv->tx_desc_tail++; + else + priv->tx_desc_tail = 0; + priv->tx_desc_free--; + priv->tx_free_mem -= len; +} + +static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) +{ + static const u8 SNAP_RFC1024[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + struct atmel_private *priv = netdev_priv(dev); + struct ieee80211_hdr header; + unsigned long flags; + u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; + + if (priv->card && priv->present_callback && + !(*priv->present_callback)(priv->card)) { + dev->stats.tx_errors++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + if (priv->station_state != STATION_STATE_READY) { + dev->stats.tx_errors++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + /* first ensure the timer func cannot run */ + spin_lock_bh(&priv->timerlock); + /* then stop the hardware ISR */ + spin_lock_irqsave(&priv->irqlock, flags); + /* nb doing the above in the opposite order will deadlock */ + + /* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the + 12 first bytes (containing DA/SA) and put them in the appropriate + fields of the Wireless Header. Thus the packet length is then the + initial + 18 (+30-12) */ + + if (!(buff = find_tx_buff(priv, len + 18))) { + dev->stats.tx_dropped++; + spin_unlock_irqrestore(&priv->irqlock, flags); + spin_unlock_bh(&priv->timerlock); + netif_stop_queue(dev); + return NETDEV_TX_BUSY; + } + + frame_ctl = IEEE80211_FTYPE_DATA; + header.duration_id = 0; + header.seq_ctrl = 0; + if (priv->wep_is_on) + frame_ctl |= IEEE80211_FCTL_PROTECTED; + if (priv->operating_mode == IW_MODE_ADHOC) { + skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN); + memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); + memcpy(&header.addr3, priv->BSSID, ETH_ALEN); + } else { + frame_ctl |= IEEE80211_FCTL_TODS; + memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); + skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN); + } + + if (priv->use_wpa) + memcpy(&header.addr4, SNAP_RFC1024, ETH_ALEN); + + header.frame_control = cpu_to_le16(frame_ctl); + /* Copy the wireless header into the card */ + atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE); + /* Copy the packet sans its 802.3 header addresses which have been replaced */ + atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12); + priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE; + + /* low bit of first byte of destination tells us if broadcast */ + tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA); + dev->stats.tx_bytes += len; + + spin_unlock_irqrestore(&priv->irqlock, flags); + spin_unlock_bh(&priv->timerlock); + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +static void atmel_transmit_management_frame(struct atmel_private *priv, + struct ieee80211_hdr *header, + u8 *body, int body_len) +{ + u16 buff; + int len = MGMT_FRAME_BODY_OFFSET + body_len; + + if (!(buff = find_tx_buff(priv, len))) + return; + + atmel_copy_to_card(priv->dev, buff, (u8 *)header, MGMT_FRAME_BODY_OFFSET); + atmel_copy_to_card(priv->dev, buff + MGMT_FRAME_BODY_OFFSET, body, body_len); + priv->tx_buff_tail += len; + tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT); +} + +static void fast_rx_path(struct atmel_private *priv, + struct ieee80211_hdr *header, + u16 msdu_size, u16 rx_packet_loc, u32 crc) +{ + /* fast path: unfragmented packet copy directly into skbuf */ + u8 mac4[6]; + struct sk_buff *skb; + unsigned char *skbp; + + /* get the final, mac 4 header field, this tells us encapsulation */ + atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6); + msdu_size -= 6; + + if (priv->do_rx_crc) { + crc = crc32_le(crc, mac4, 6); + msdu_size -= 4; + } + + if (!(skb = dev_alloc_skb(msdu_size + 14))) { + priv->dev->stats.rx_dropped++; + return; + } + + skb_reserve(skb, 2); + skbp = skb_put(skb, msdu_size + 12); + atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size); + + if (priv->do_rx_crc) { + u32 netcrc; + crc = crc32_le(crc, skbp + 12, msdu_size); + atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + 30 + msdu_size, 4); + if ((crc ^ 0xffffffff) != netcrc) { + priv->dev->stats.rx_crc_errors++; + dev_kfree_skb(skb); + return; + } + } + + memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */ + if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) + memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN); + else + memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */ + + skb->protocol = eth_type_trans(skb, priv->dev); + skb->ip_summed = CHECKSUM_NONE; + netif_rx(skb); + priv->dev->stats.rx_bytes += 12 + msdu_size; + priv->dev->stats.rx_packets++; +} + +/* Test to see if the packet in card memory at packet_loc has a valid CRC + It doesn't matter that this is slow: it is only used to proble the first few + packets. */ +static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size) +{ + int i = msdu_size - 4; + u32 netcrc, crc = 0xffffffff; + + if (msdu_size < 4) + return 0; + + atmel_copy_to_host(priv->dev, (void *)&netcrc, packet_loc + i, 4); + + atmel_writeAR(priv->dev, packet_loc); + while (i--) { + u8 octet = atmel_read8(priv->dev, DR); + crc = crc32_le(crc, &octet, 1); + } + + return (crc ^ 0xffffffff) == netcrc; +} + +static void frag_rx_path(struct atmel_private *priv, + struct ieee80211_hdr *header, + u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, + u8 frag_no, int more_frags) +{ + u8 mac4[ETH_ALEN]; + u8 source[ETH_ALEN]; + struct sk_buff *skb; + + if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) + memcpy(source, header->addr3, ETH_ALEN); + else + memcpy(source, header->addr2, ETH_ALEN); + + rx_packet_loc += 24; /* skip header */ + + if (priv->do_rx_crc) + msdu_size -= 4; + + if (frag_no == 0) { /* first fragment */ + atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN); + msdu_size -= ETH_ALEN; + rx_packet_loc += ETH_ALEN; + + if (priv->do_rx_crc) + crc = crc32_le(crc, mac4, 6); + + priv->frag_seq = seq_no; + priv->frag_no = 1; + priv->frag_len = msdu_size; + memcpy(priv->frag_source, source, ETH_ALEN); + memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN); + memcpy(priv->rx_buf, header->addr1, ETH_ALEN); + + atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size); + + if (priv->do_rx_crc) { + u32 netcrc; + crc = crc32_le(crc, &priv->rx_buf[12], msdu_size); + atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); + if ((crc ^ 0xffffffff) != netcrc) { + priv->dev->stats.rx_crc_errors++; + eth_broadcast_addr(priv->frag_source); + } + } + + } else if (priv->frag_no == frag_no && + priv->frag_seq == seq_no && + memcmp(priv->frag_source, source, ETH_ALEN) == 0) { + + atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len], + rx_packet_loc, msdu_size); + if (priv->do_rx_crc) { + u32 netcrc; + crc = crc32_le(crc, + &priv->rx_buf[12 + priv->frag_len], + msdu_size); + atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); + if ((crc ^ 0xffffffff) != netcrc) { + priv->dev->stats.rx_crc_errors++; + eth_broadcast_addr(priv->frag_source); + more_frags = 1; /* don't send broken assembly */ + } + } + + priv->frag_len += msdu_size; + priv->frag_no++; + + if (!more_frags) { /* last one */ + eth_broadcast_addr(priv->frag_source); + if (!(skb = dev_alloc_skb(priv->frag_len + 14))) { + priv->dev->stats.rx_dropped++; + } else { + skb_reserve(skb, 2); + memcpy(skb_put(skb, priv->frag_len + 12), + priv->rx_buf, + priv->frag_len + 12); + skb->protocol = eth_type_trans(skb, priv->dev); + skb->ip_summed = CHECKSUM_NONE; + netif_rx(skb); + priv->dev->stats.rx_bytes += priv->frag_len + 12; + priv->dev->stats.rx_packets++; + } + } + } else + priv->wstats.discard.fragment++; +} + +static void rx_done_irq(struct atmel_private *priv) +{ + int i; + struct ieee80211_hdr header; + + for (i = 0; + atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID && + i < priv->host_info.rx_desc_count; + i++) { + + u16 msdu_size, rx_packet_loc, frame_ctl, seq_control; + u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head)); + u32 crc = 0xffffffff; + + if (status != RX_STATUS_SUCCESS) { + if (status == 0xc1) /* determined by experiment */ + priv->wstats.discard.nwid++; + else + priv->dev->stats.rx_errors++; + goto next; + } + + msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head)); + rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head)); + + if (msdu_size < 30) { + priv->dev->stats.rx_errors++; + goto next; + } + + /* Get header as far as end of seq_ctrl */ + atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24); + frame_ctl = le16_to_cpu(header.frame_control); + seq_control = le16_to_cpu(header.seq_ctrl); + + /* probe for CRC use here if needed once five packets have + arrived with the same crc status, we assume we know what's + happening and stop probing */ + if (priv->probe_crc) { + if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) { + priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size); + } else { + priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24); + } + if (priv->do_rx_crc) { + if (priv->crc_ok_cnt++ > 5) + priv->probe_crc = 0; + } else { + if (priv->crc_ko_cnt++ > 5) + priv->probe_crc = 0; + } + } + + /* don't CRC header when WEP in use */ + if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) { + crc = crc32_le(0xffffffff, (unsigned char *)&header, 24); + } + msdu_size -= 24; /* header */ + + if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { + int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS; + u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG; + u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4; + + if (!more_fragments && packet_fragment_no == 0) { + fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc); + } else { + frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc, + packet_sequence_no, packet_fragment_no, more_fragments); + } + } + + if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { + /* copy rest of packet into buffer */ + atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size); + + /* we use the same buffer for frag reassembly and control packets */ + eth_broadcast_addr(priv->frag_source); + + if (priv->do_rx_crc) { + /* last 4 octets is crc */ + msdu_size -= 4; + crc = crc32_le(crc, (unsigned char *)&priv->rx_buf, msdu_size); + if ((crc ^ 0xffffffff) != (*((u32 *)&priv->rx_buf[msdu_size]))) { + priv->dev->stats.rx_crc_errors++; + goto next; + } + } + + atmel_management_frame(priv, &header, msdu_size, + atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head))); + } + +next: + /* release descriptor */ + atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED); + + if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1)) + priv->rx_desc_head++; + else + priv->rx_desc_head = 0; + } +} + +static irqreturn_t service_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct atmel_private *priv = netdev_priv(dev); + u8 isr; + int i = -1; + static const u8 irq_order[] = { + ISR_OUT_OF_RANGE, + ISR_RxCOMPLETE, + ISR_TxCOMPLETE, + ISR_RxFRAMELOST, + ISR_FATAL_ERROR, + ISR_COMMAND_COMPLETE, + ISR_IBSS_MERGE, + ISR_GENERIC_IRQ + }; + + if (priv->card && priv->present_callback && + !(*priv->present_callback)(priv->card)) + return IRQ_HANDLED; + + /* In this state upper-level code assumes it can mess with + the card unhampered by interrupts which may change register state. + Note that even though the card shouldn't generate interrupts + the inturrupt line may be shared. This allows card setup + to go on without disabling interrupts for a long time. */ + if (priv->station_state == STATION_STATE_DOWN) + return IRQ_NONE; + + atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */ + + while (1) { + if (!atmel_lock_mac(priv)) { + /* failed to contact card */ + printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); + return IRQ_HANDLED; + } + + isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); + atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); + + if (!isr) { + atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */ + return i == -1 ? IRQ_NONE : IRQ_HANDLED; + } + + atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */ + + for (i = 0; i < ARRAY_SIZE(irq_order); i++) + if (isr & irq_order[i]) + break; + + if (!atmel_lock_mac(priv)) { + /* failed to contact card */ + printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); + return IRQ_HANDLED; + } + + isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); + isr ^= irq_order[i]; + atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr); + atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); + + switch (irq_order[i]) { + + case ISR_OUT_OF_RANGE: + if (priv->operating_mode == IW_MODE_INFRA && + priv->station_state == STATION_STATE_READY) { + priv->station_is_associated = 0; + atmel_scan(priv, 1); + } + break; + + case ISR_RxFRAMELOST: + priv->wstats.discard.misc++; + /* fall through */ + case ISR_RxCOMPLETE: + rx_done_irq(priv); + break; + + case ISR_TxCOMPLETE: + tx_done_irq(priv); + break; + + case ISR_FATAL_ERROR: + printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name); + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + break; + + case ISR_COMMAND_COMPLETE: + atmel_command_irq(priv); + break; + + case ISR_IBSS_MERGE: + atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, + priv->CurrentBSSID, 6); + /* The WPA stuff cares about the current AP address */ + if (priv->use_wpa) + build_wpa_mib(priv); + break; + case ISR_GENERIC_IRQ: + printk(KERN_INFO "%s: Generic_irq received.\n", dev->name); + break; + } + } +} + +static struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* update the link quality here in case we are seeing no beacons + at all to drive the process */ + atmel_smooth_qual(priv); + + priv->wstats.status = priv->station_state; + + if (priv->operating_mode == IW_MODE_INFRA) { + if (priv->station_state != STATION_STATE_READY) { + priv->wstats.qual.qual = 0; + priv->wstats.qual.level = 0; + priv->wstats.qual.updated = (IW_QUAL_QUAL_INVALID + | IW_QUAL_LEVEL_INVALID); + } + priv->wstats.qual.noise = 0; + priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID; + } else { + /* Quality levels cannot be determined in ad-hoc mode, + because we can 'hear' more that one remote station. */ + priv->wstats.qual.qual = 0; + priv->wstats.qual.level = 0; + priv->wstats.qual.noise = 0; + priv->wstats.qual.updated = IW_QUAL_QUAL_INVALID + | IW_QUAL_LEVEL_INVALID + | IW_QUAL_NOISE_INVALID; + priv->wstats.miss.beacon = 0; + } + + return &priv->wstats; +} + +static int atmel_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > 2312)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + +static int atmel_set_mac_address(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + + memcpy (dev->dev_addr, addr->sa_data, dev->addr_len); + return atmel_open(dev); +} + +EXPORT_SYMBOL(atmel_open); + +int atmel_open(struct net_device *dev) +{ + struct atmel_private *priv = netdev_priv(dev); + int i, channel, err; + + /* any scheduled timer is no longer needed and might screw things up.. */ + del_timer_sync(&priv->management_timer); + + /* Interrupts will not touch the card once in this state... */ + priv->station_state = STATION_STATE_DOWN; + + if (priv->new_SSID_size) { + memcpy(priv->SSID, priv->new_SSID, priv->new_SSID_size); + priv->SSID_size = priv->new_SSID_size; + priv->new_SSID_size = 0; + } + priv->BSS_list_entries = 0; + + priv->AuthenticationRequestRetryCnt = 0; + priv->AssociationRequestRetryCnt = 0; + priv->ReAssociationRequestRetryCnt = 0; + priv->CurrentAuthentTransactionSeqNum = 0x0001; + priv->ExpectedAuthentTransactionSeqNum = 0x0002; + + priv->site_survey_state = SITE_SURVEY_IDLE; + priv->station_is_associated = 0; + + err = reset_atmel_card(dev); + if (err) + return err; + + if (priv->config_reg_domain) { + priv->reg_domain = priv->config_reg_domain; + atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS, priv->reg_domain); + } else { + priv->reg_domain = atmel_get_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS); + for (i = 0; i < ARRAY_SIZE(channel_table); i++) + if (priv->reg_domain == channel_table[i].reg_domain) + break; + if (i == ARRAY_SIZE(channel_table)) { + priv->reg_domain = REG_DOMAIN_MKK1; + printk(KERN_ALERT "%s: failed to get regulatory domain: assuming MKK1.\n", dev->name); + } + } + + if ((channel = atmel_validate_channel(priv, priv->channel))) + priv->channel = channel; + + /* this moves station_state on.... */ + atmel_scan(priv, 1); + + atmel_set_gcr(priv->dev, GCR_ENINT); /* enable interrupts */ + return 0; +} + +static int atmel_close(struct net_device *dev) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* Send event to userspace that we are disassociating */ + if (priv->station_state == STATION_STATE_READY) { + union iwreq_data wrqu; + + wrqu.data.length = 0; + wrqu.data.flags = 0; + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + eth_zero_addr(wrqu.ap_addr.sa_data); + wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); + } + + atmel_enter_state(priv, STATION_STATE_DOWN); + + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(dev, GCR, 0x0060); + atmel_write16(dev, GCR, 0x0040); + return 0; +} + +static int atmel_validate_channel(struct atmel_private *priv, int channel) +{ + /* check that channel is OK, if so return zero, + else return suitable default channel */ + int i; + + for (i = 0; i < ARRAY_SIZE(channel_table); i++) + if (priv->reg_domain == channel_table[i].reg_domain) { + if (channel >= channel_table[i].min && + channel <= channel_table[i].max) + return 0; + else + return channel_table[i].min; + } + return 0; +} + +static int atmel_proc_show(struct seq_file *m, void *v) +{ + struct atmel_private *priv = m->private; + int i; + char *s, *r, *c; + + seq_printf(m, "Driver version:\t\t%d.%d\n", DRIVER_MAJOR, DRIVER_MINOR); + + if (priv->station_state != STATION_STATE_DOWN) { + seq_printf(m, + "Firmware version:\t%d.%d build %d\n" + "Firmware location:\t", + priv->host_info.major_version, + priv->host_info.minor_version, + priv->host_info.build_version); + + if (priv->card_type != CARD_TYPE_EEPROM) + seq_puts(m, "on card\n"); + else if (priv->firmware) + seq_printf(m, "%s loaded by host\n", priv->firmware_id); + else + seq_printf(m, "%s loaded by hotplug\n", priv->firmware_id); + + switch (priv->card_type) { + case CARD_TYPE_PARALLEL_FLASH: + c = "Parallel flash"; + break; + case CARD_TYPE_SPI_FLASH: + c = "SPI flash\n"; + break; + case CARD_TYPE_EEPROM: + c = "EEPROM"; + break; + default: + c = ""; + } + + r = ""; + for (i = 0; i < ARRAY_SIZE(channel_table); i++) + if (priv->reg_domain == channel_table[i].reg_domain) + r = channel_table[i].name; + + seq_printf(m, "MAC memory type:\t%s\n", c); + seq_printf(m, "Regulatory domain:\t%s\n", r); + seq_printf(m, "Host CRC checking:\t%s\n", + priv->do_rx_crc ? "On" : "Off"); + seq_printf(m, "WPA-capable firmware:\t%s\n", + priv->use_wpa ? "Yes" : "No"); + } + + switch (priv->station_state) { + case STATION_STATE_SCANNING: + s = "Scanning"; + break; + case STATION_STATE_JOINNING: + s = "Joining"; + break; + case STATION_STATE_AUTHENTICATING: + s = "Authenticating"; + break; + case STATION_STATE_ASSOCIATING: + s = "Associating"; + break; + case STATION_STATE_READY: + s = "Ready"; + break; + case STATION_STATE_REASSOCIATING: + s = "Reassociating"; + break; + case STATION_STATE_MGMT_ERROR: + s = "Management error"; + break; + case STATION_STATE_DOWN: + s = "Down"; + break; + default: + s = ""; + } + + seq_printf(m, "Current state:\t\t%s\n", s); + return 0; +} + +static int atmel_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, atmel_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations atmel_proc_fops = { + .open = atmel_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct net_device_ops atmel_netdev_ops = { + .ndo_open = atmel_open, + .ndo_stop = atmel_close, + .ndo_change_mtu = atmel_change_mtu, + .ndo_set_mac_address = atmel_set_mac_address, + .ndo_start_xmit = start_tx, + .ndo_do_ioctl = atmel_ioctl, + .ndo_validate_addr = eth_validate_addr, +}; + +struct net_device *init_atmel_card(unsigned short irq, unsigned long port, + const AtmelFWType fw_type, + struct device *sys_dev, + int (*card_present)(void *), void *card) +{ + struct net_device *dev; + struct atmel_private *priv; + int rc; + + /* Create the network device object. */ + dev = alloc_etherdev(sizeof(*priv)); + if (!dev) + return NULL; + + if (dev_alloc_name(dev, dev->name) < 0) { + printk(KERN_ERR "atmel: Couldn't get name!\n"); + goto err_out_free; + } + + priv = netdev_priv(dev); + priv->dev = dev; + priv->sys_dev = sys_dev; + priv->present_callback = card_present; + priv->card = card; + priv->firmware = NULL; + priv->firmware_id[0] = '\0'; + priv->firmware_type = fw_type; + if (firmware) /* module parameter */ + strcpy(priv->firmware_id, firmware); + priv->bus_type = card_present ? BUS_TYPE_PCCARD : BUS_TYPE_PCI; + priv->station_state = STATION_STATE_DOWN; + priv->do_rx_crc = 0; + /* For PCMCIA cards, some chips need CRC, some don't + so we have to probe. */ + if (priv->bus_type == BUS_TYPE_PCCARD) { + priv->probe_crc = 1; + priv->crc_ok_cnt = priv->crc_ko_cnt = 0; + } else + priv->probe_crc = 0; + priv->last_qual = jiffies; + priv->last_beacon_timestamp = 0; + memset(priv->frag_source, 0xff, sizeof(priv->frag_source)); + eth_zero_addr(priv->BSSID); + priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */ + priv->station_was_associated = 0; + + priv->last_survey = jiffies; + priv->preamble = LONG_PREAMBLE; + priv->operating_mode = IW_MODE_INFRA; + priv->connect_to_any_BSS = 0; + priv->config_reg_domain = 0; + priv->reg_domain = 0; + priv->tx_rate = 3; + priv->auto_tx_rate = 1; + priv->channel = 4; + priv->power_mode = 0; + priv->SSID[0] = '\0'; + priv->SSID_size = 0; + priv->new_SSID_size = 0; + priv->frag_threshold = 2346; + priv->rts_threshold = 2347; + priv->short_retry = 7; + priv->long_retry = 4; + + priv->wep_is_on = 0; + priv->default_key = 0; + priv->encryption_level = 0; + priv->exclude_unencrypted = 0; + priv->group_cipher_suite = priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; + priv->use_wpa = 0; + memset(priv->wep_keys, 0, sizeof(priv->wep_keys)); + memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); + + priv->default_beacon_period = priv->beacon_period = 100; + priv->listen_interval = 1; + + init_timer(&priv->management_timer); + spin_lock_init(&priv->irqlock); + spin_lock_init(&priv->timerlock); + priv->management_timer.function = atmel_management_timer; + priv->management_timer.data = (unsigned long) dev; + + dev->netdev_ops = &atmel_netdev_ops; + dev->wireless_handlers = &atmel_handler_def; + dev->irq = irq; + dev->base_addr = port; + + SET_NETDEV_DEV(dev, sys_dev); + + if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) { + printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc); + goto err_out_free; + } + + if (!request_region(dev->base_addr, 32, + priv->bus_type == BUS_TYPE_PCCARD ? "atmel_cs" : "atmel_pci")) { + goto err_out_irq; + } + + if (register_netdev(dev)) + goto err_out_res; + + if (!probe_atmel_card(dev)) { + unregister_netdev(dev); + goto err_out_res; + } + + netif_carrier_off(dev); + + if (!proc_create_data("driver/atmel", 0, NULL, &atmel_proc_fops, priv)) + printk(KERN_WARNING "atmel: unable to create /proc entry.\n"); + + printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %pM\n", + dev->name, DRIVER_MAJOR, DRIVER_MINOR, dev->dev_addr); + + return dev; + +err_out_res: + release_region(dev->base_addr, 32); +err_out_irq: + free_irq(dev->irq, dev); +err_out_free: + free_netdev(dev); + return NULL; +} + +EXPORT_SYMBOL(init_atmel_card); + +void stop_atmel_card(struct net_device *dev) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* put a brick on it... */ + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(dev, GCR, 0x0060); + atmel_write16(dev, GCR, 0x0040); + + del_timer_sync(&priv->management_timer); + unregister_netdev(dev); + remove_proc_entry("driver/atmel", NULL); + free_irq(dev->irq, dev); + kfree(priv->firmware); + release_region(dev->base_addr, 32); + free_netdev(dev); +} + +EXPORT_SYMBOL(stop_atmel_card); + +static int atmel_set_essid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* Check if we asked for `any' */ + if (dwrq->flags == 0) { + priv->connect_to_any_BSS = 1; + } else { + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + priv->connect_to_any_BSS = 0; + + /* Check the size of the string */ + if (dwrq->length > MAX_SSID_LENGTH) + return -E2BIG; + if (index != 0) + return -EINVAL; + + memcpy(priv->new_SSID, extra, dwrq->length); + priv->new_SSID_size = dwrq->length; + } + + return -EINPROGRESS; +} + +static int atmel_get_essid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* Get the current SSID */ + if (priv->new_SSID_size != 0) { + memcpy(extra, priv->new_SSID, priv->new_SSID_size); + dwrq->length = priv->new_SSID_size; + } else { + memcpy(extra, priv->SSID, priv->SSID_size); + dwrq->length = priv->SSID_size; + } + + dwrq->flags = !priv->connect_to_any_BSS; /* active */ + + return 0; +} + +static int atmel_get_wap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN); + awrq->sa_family = ARPHRD_ETHER; + + return 0; +} + +static int atmel_set_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* Basic checking: do we have a key to set ? + * Note : with the new API, it's impossible to get a NULL pointer. + * Therefore, we need to check a key size == 0 instead. + * New version of iwconfig properly set the IW_ENCODE_NOKEY flag + * when no key is present (only change flags), but older versions + * don't do it. - Jean II */ + if (dwrq->length > 0) { + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + int current_index = priv->default_key; + /* Check the size of the key */ + if (dwrq->length > 13) { + return -EINVAL; + } + /* Check the index (none -> use current) */ + if (index < 0 || index >= 4) + index = current_index; + else + priv->default_key = index; + /* Set the length */ + if (dwrq->length > 5) + priv->wep_key_len[index] = 13; + else + if (dwrq->length > 0) + priv->wep_key_len[index] = 5; + else + /* Disable the key */ + priv->wep_key_len[index] = 0; + /* Check if the key is not marked as invalid */ + if (!(dwrq->flags & IW_ENCODE_NOKEY)) { + /* Cleanup */ + memset(priv->wep_keys[index], 0, 13); + /* Copy the key in the driver */ + memcpy(priv->wep_keys[index], extra, dwrq->length); + } + /* WE specify that if a valid key is set, encryption + * should be enabled (user may turn it off later) + * This is also how "iwconfig ethX key on" works */ + if (index == current_index && + priv->wep_key_len[index] > 0) { + priv->wep_is_on = 1; + priv->exclude_unencrypted = 1; + if (priv->wep_key_len[index] > 5) { + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; + priv->encryption_level = 2; + } else { + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; + priv->encryption_level = 1; + } + } + } else { + /* Do we want to just set the transmit key index ? */ + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (index >= 0 && index < 4) { + priv->default_key = index; + } else + /* Don't complain if only change the mode */ + if (!(dwrq->flags & IW_ENCODE_MODE)) + return -EINVAL; + } + /* Read the flags */ + if (dwrq->flags & IW_ENCODE_DISABLED) { + priv->wep_is_on = 0; + priv->encryption_level = 0; + priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; + } else { + priv->wep_is_on = 1; + if (priv->wep_key_len[priv->default_key] > 5) { + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; + priv->encryption_level = 2; + } else { + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; + priv->encryption_level = 1; + } + } + if (dwrq->flags & IW_ENCODE_RESTRICTED) + priv->exclude_unencrypted = 1; + if (dwrq->flags & IW_ENCODE_OPEN) + priv->exclude_unencrypted = 0; + + return -EINPROGRESS; /* Call commit handler */ +} + +static int atmel_get_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + if (!priv->wep_is_on) + dwrq->flags = IW_ENCODE_DISABLED; + else { + if (priv->exclude_unencrypted) + dwrq->flags = IW_ENCODE_RESTRICTED; + else + dwrq->flags = IW_ENCODE_OPEN; + } + /* Which key do we want ? -1 -> tx index */ + if (index < 0 || index >= 4) + index = priv->default_key; + dwrq->flags |= index + 1; + /* Copy the key to the user buffer */ + dwrq->length = priv->wep_key_len[index]; + if (dwrq->length > 16) { + dwrq->length = 0; + } else { + memset(extra, 0, 16); + memcpy(extra, priv->wep_keys[index], dwrq->length); + } + + return 0; +} + +static int atmel_set_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, key_len, alg = ext->alg, set_key = 1; + + /* Determine and validate the key index */ + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if (idx < 1 || idx > 4) + return -EINVAL; + idx--; + } else + idx = priv->default_key; + + if (encoding->flags & IW_ENCODE_DISABLED) + alg = IW_ENCODE_ALG_NONE; + + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + priv->default_key = idx; + set_key = ext->key_len > 0 ? 1 : 0; + } + + if (set_key) { + /* Set the requested key first */ + switch (alg) { + case IW_ENCODE_ALG_NONE: + priv->wep_is_on = 0; + priv->encryption_level = 0; + priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; + break; + case IW_ENCODE_ALG_WEP: + if (ext->key_len > 5) { + priv->wep_key_len[idx] = 13; + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; + priv->encryption_level = 2; + } else if (ext->key_len > 0) { + priv->wep_key_len[idx] = 5; + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; + priv->encryption_level = 1; + } else { + return -EINVAL; + } + priv->wep_is_on = 1; + memset(priv->wep_keys[idx], 0, 13); + key_len = min ((int)ext->key_len, priv->wep_key_len[idx]); + memcpy(priv->wep_keys[idx], ext->key, key_len); + break; + default: + return -EINVAL; + } + } + + return -EINPROGRESS; +} + +static int atmel_get_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, max_key_len; + + max_key_len = encoding->length - sizeof(*ext); + if (max_key_len < 0) + return -EINVAL; + + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if (idx < 1 || idx > 4) + return -EINVAL; + idx--; + } else + idx = priv->default_key; + + encoding->flags = idx + 1; + memset(ext, 0, sizeof(*ext)); + + if (!priv->wep_is_on) { + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + encoding->flags |= IW_ENCODE_DISABLED; + } else { + if (priv->encryption_level > 0) + ext->alg = IW_ENCODE_ALG_WEP; + else + return -EINVAL; + + ext->key_len = priv->wep_key_len[idx]; + memcpy(ext->key, priv->wep_keys[idx], ext->key_len); + encoding->flags |= IW_ENCODE_ENABLED; + } + + return 0; +} + +static int atmel_set_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + struct iw_param *param = &wrqu->param; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_PRIVACY_INVOKED: + /* + * atmel does not use these parameters + */ + break; + + case IW_AUTH_DROP_UNENCRYPTED: + priv->exclude_unencrypted = param->value ? 1 : 0; + break; + + case IW_AUTH_80211_AUTH_ALG: { + if (param->value & IW_AUTH_ALG_SHARED_KEY) { + priv->exclude_unencrypted = 1; + } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { + priv->exclude_unencrypted = 0; + } else + return -EINVAL; + break; + } + + case IW_AUTH_WPA_ENABLED: + /* Silently accept disable of WPA */ + if (param->value > 0) + return -EOPNOTSUPP; + break; + + default: + return -EOPNOTSUPP; + } + return -EINPROGRESS; +} + +static int atmel_get_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + struct iw_param *param = &wrqu->param; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_DROP_UNENCRYPTED: + param->value = priv->exclude_unencrypted; + break; + + case IW_AUTH_80211_AUTH_ALG: + if (priv->exclude_unencrypted == 1) + param->value = IW_AUTH_ALG_SHARED_KEY; + else + param->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + + case IW_AUTH_WPA_ENABLED: + param->value = 0; + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + + +static int atmel_get_name(struct net_device *dev, + struct iw_request_info *info, + char *cwrq, + char *extra) +{ + strcpy(cwrq, "IEEE 802.11-DS"); + return 0; +} + +static int atmel_set_rate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + if (vwrq->fixed == 0) { + priv->tx_rate = 3; + priv->auto_tx_rate = 1; + } else { + priv->auto_tx_rate = 0; + + /* Which type of value ? */ + if ((vwrq->value < 4) && (vwrq->value >= 0)) { + /* Setting by rate index */ + priv->tx_rate = vwrq->value; + } else { + /* Setting by frequency value */ + switch (vwrq->value) { + case 1000000: + priv->tx_rate = 0; + break; + case 2000000: + priv->tx_rate = 1; + break; + case 5500000: + priv->tx_rate = 2; + break; + case 11000000: + priv->tx_rate = 3; + break; + default: + return -EINVAL; + } + } + } + + return -EINPROGRESS; +} + +static int atmel_set_mode(struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + if (*uwrq != IW_MODE_ADHOC && *uwrq != IW_MODE_INFRA) + return -EINVAL; + + priv->operating_mode = *uwrq; + return -EINPROGRESS; +} + +static int atmel_get_mode(struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + *uwrq = priv->operating_mode; + return 0; +} + +static int atmel_get_rate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + if (priv->auto_tx_rate) { + vwrq->fixed = 0; + vwrq->value = 11000000; + } else { + vwrq->fixed = 1; + switch (priv->tx_rate) { + case 0: + vwrq->value = 1000000; + break; + case 1: + vwrq->value = 2000000; + break; + case 2: + vwrq->value = 5500000; + break; + case 3: + vwrq->value = 11000000; + break; + } + } + return 0; +} + +static int atmel_set_power(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + priv->power_mode = vwrq->disabled ? 0 : 1; + return -EINPROGRESS; +} + +static int atmel_get_power(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + vwrq->disabled = priv->power_mode ? 0 : 1; + vwrq->flags = IW_POWER_ON; + return 0; +} + +static int atmel_set_retry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) { + if (vwrq->flags & IW_RETRY_LONG) + priv->long_retry = vwrq->value; + else if (vwrq->flags & IW_RETRY_SHORT) + priv->short_retry = vwrq->value; + else { + /* No modifier : set both */ + priv->long_retry = vwrq->value; + priv->short_retry = vwrq->value; + } + return -EINPROGRESS; + } + + return -EINVAL; +} + +static int atmel_get_retry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + vwrq->disabled = 0; /* Can't be disabled */ + + /* Note : by default, display the short retry number */ + if (vwrq->flags & IW_RETRY_LONG) { + vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; + vwrq->value = priv->long_retry; + } else { + vwrq->flags = IW_RETRY_LIMIT; + vwrq->value = priv->short_retry; + if (priv->long_retry != priv->short_retry) + vwrq->flags |= IW_RETRY_SHORT; + } + + return 0; +} + +static int atmel_set_rts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int rthr = vwrq->value; + + if (vwrq->disabled) + rthr = 2347; + if ((rthr < 0) || (rthr > 2347)) { + return -EINVAL; + } + priv->rts_threshold = rthr; + + return -EINPROGRESS; /* Call commit handler */ +} + +static int atmel_get_rts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + vwrq->value = priv->rts_threshold; + vwrq->disabled = (vwrq->value >= 2347); + vwrq->fixed = 1; + + return 0; +} + +static int atmel_set_frag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int fthr = vwrq->value; + + if (vwrq->disabled) + fthr = 2346; + if ((fthr < 256) || (fthr > 2346)) { + return -EINVAL; + } + fthr &= ~0x1; /* Get an even value - is it really needed ??? */ + priv->frag_threshold = fthr; + + return -EINPROGRESS; /* Call commit handler */ +} + +static int atmel_get_frag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + vwrq->value = priv->frag_threshold; + vwrq->disabled = (vwrq->value >= 2346); + vwrq->fixed = 1; + + return 0; +} + +static int atmel_set_freq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int rc = -EINPROGRESS; /* Call commit handler */ + + /* If setting by frequency, convert to a channel */ + if (fwrq->e == 1) { + int f = fwrq->m / 100000; + + /* Hack to fall through... */ + fwrq->e = 0; + fwrq->m = ieee80211_frequency_to_channel(f); + } + /* Setting by channel number */ + if ((fwrq->m > 1000) || (fwrq->e > 0)) + rc = -EOPNOTSUPP; + else { + int channel = fwrq->m; + if (atmel_validate_channel(priv, channel) == 0) { + priv->channel = channel; + } else { + rc = -EINVAL; + } + } + return rc; +} + +static int atmel_get_freq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + fwrq->m = priv->channel; + fwrq->e = 0; + return 0; +} + +static int atmel_set_scan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + unsigned long flags; + + /* Note : you may have realised that, as this is a SET operation, + * this is privileged and therefore a normal user can't + * perform scanning. + * This is not an error, while the device perform scanning, + * traffic doesn't flow, so it's a perfect DoS... + * Jean II */ + + if (priv->station_state == STATION_STATE_DOWN) + return -EAGAIN; + + /* Timeout old surveys. */ + if (time_after(jiffies, priv->last_survey + 20 * HZ)) + priv->site_survey_state = SITE_SURVEY_IDLE; + priv->last_survey = jiffies; + + /* Initiate a scan command */ + if (priv->site_survey_state == SITE_SURVEY_IN_PROGRESS) + return -EBUSY; + + del_timer_sync(&priv->management_timer); + spin_lock_irqsave(&priv->irqlock, flags); + + priv->site_survey_state = SITE_SURVEY_IN_PROGRESS; + priv->fast_scan = 0; + atmel_scan(priv, 0); + spin_unlock_irqrestore(&priv->irqlock, flags); + + return 0; +} + +static int atmel_get_scan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int i; + char *current_ev = extra; + struct iw_event iwe; + + if (priv->site_survey_state != SITE_SURVEY_COMPLETED) + return -EAGAIN; + + for (i = 0; i < priv->BSS_list_entries; i++) { + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN); + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_ADDR_LEN); + + iwe.u.data.length = priv->BSSinfo[i].SSIDsize; + if (iwe.u.data.length > 32) + iwe.u.data.length = 32; + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, priv->BSSinfo[i].SSID); + + iwe.cmd = SIOCGIWMODE; + iwe.u.mode = priv->BSSinfo[i].BSStype; + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_UINT_LEN); + + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = priv->BSSinfo[i].channel; + iwe.u.freq.e = 0; + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_FREQ_LEN); + + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.level = priv->BSSinfo[i].RSSI; + iwe.u.qual.qual = iwe.u.qual.level; + /* iwe.u.qual.noise = SOMETHING */ + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_QUAL_LEN); + + + iwe.cmd = SIOCGIWENCODE; + if (priv->BSSinfo[i].UsingWEP) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, NULL); + } + + /* Length of data */ + dwrq->length = (current_ev - extra); + dwrq->flags = 0; + + return 0; +} + +static int atmel_get_range(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + struct iw_range *range = (struct iw_range *) extra; + int k, i, j; + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + range->min_nwid = 0x0000; + range->max_nwid = 0x0000; + range->num_channels = 0; + for (j = 0; j < ARRAY_SIZE(channel_table); j++) + if (priv->reg_domain == channel_table[j].reg_domain) { + range->num_channels = channel_table[j].max - channel_table[j].min + 1; + break; + } + if (range->num_channels != 0) { + for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) { + range->freq[k].i = i; /* List index */ + + /* Values in MHz -> * 10^5 * 10 */ + range->freq[k].m = 100000 * + ieee80211_channel_to_frequency(i, IEEE80211_BAND_2GHZ); + range->freq[k++].e = 1; + } + range->num_frequency = k; + } + + range->max_qual.qual = 100; + range->max_qual.level = 100; + range->max_qual.noise = 0; + range->max_qual.updated = IW_QUAL_NOISE_INVALID; + + range->avg_qual.qual = 50; + range->avg_qual.level = 50; + range->avg_qual.noise = 0; + range->avg_qual.updated = IW_QUAL_NOISE_INVALID; + + range->sensitivity = 0; + + range->bitrate[0] = 1000000; + range->bitrate[1] = 2000000; + range->bitrate[2] = 5500000; + range->bitrate[3] = 11000000; + range->num_bitrates = 4; + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = 4; + + range->pmp_flags = IW_POWER_ON; + range->pmt_flags = IW_POWER_ON; + range->pm_capa = 0; + + range->we_version_source = WIRELESS_EXT; + range->we_version_compiled = WIRELESS_EXT; + range->retry_capa = IW_RETRY_LIMIT ; + range->retry_flags = IW_RETRY_LIMIT; + range->r_time_flags = 0; + range->min_retry = 1; + range->max_retry = 65535; + + return 0; +} + +static int atmel_set_wap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int i; + static const u8 any[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + unsigned long flags; + + if (awrq->sa_family != ARPHRD_ETHER) + return -EINVAL; + + if (!memcmp(any, awrq->sa_data, 6) || + !memcmp(off, awrq->sa_data, 6)) { + del_timer_sync(&priv->management_timer); + spin_lock_irqsave(&priv->irqlock, flags); + atmel_scan(priv, 1); + spin_unlock_irqrestore(&priv->irqlock, flags); + return 0; + } + + for (i = 0; i < priv->BSS_list_entries; i++) { + if (memcmp(priv->BSSinfo[i].BSSID, awrq->sa_data, 6) == 0) { + if (!priv->wep_is_on && priv->BSSinfo[i].UsingWEP) { + return -EINVAL; + } else if (priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) { + return -EINVAL; + } else { + del_timer_sync(&priv->management_timer); + spin_lock_irqsave(&priv->irqlock, flags); + atmel_join_bss(priv, i); + spin_unlock_irqrestore(&priv->irqlock, flags); + return 0; + } + } + } + + return -EINVAL; +} + +static int atmel_config_commit(struct net_device *dev, + struct iw_request_info *info, /* NULL */ + void *zwrq, /* NULL */ + char *extra) /* NULL */ +{ + return atmel_open(dev); +} + +static const iw_handler atmel_handler[] = +{ + (iw_handler) atmel_config_commit, /* SIOCSIWCOMMIT */ + (iw_handler) atmel_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) atmel_set_freq, /* SIOCSIWFREQ */ + (iw_handler) atmel_get_freq, /* SIOCGIWFREQ */ + (iw_handler) atmel_set_mode, /* SIOCSIWMODE */ + (iw_handler) atmel_get_mode, /* SIOCGIWMODE */ + (iw_handler) NULL, /* SIOCSIWSENS */ + (iw_handler) NULL, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) atmel_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) atmel_set_wap, /* SIOCSIWAP */ + (iw_handler) atmel_get_wap, /* SIOCGIWAP */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* SIOCGIWAPLIST */ + (iw_handler) atmel_set_scan, /* SIOCSIWSCAN */ + (iw_handler) atmel_get_scan, /* SIOCGIWSCAN */ + (iw_handler) atmel_set_essid, /* SIOCSIWESSID */ + (iw_handler) atmel_get_essid, /* SIOCGIWESSID */ + (iw_handler) NULL, /* SIOCSIWNICKN */ + (iw_handler) NULL, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) atmel_set_rate, /* SIOCSIWRATE */ + (iw_handler) atmel_get_rate, /* SIOCGIWRATE */ + (iw_handler) atmel_set_rts, /* SIOCSIWRTS */ + (iw_handler) atmel_get_rts, /* SIOCGIWRTS */ + (iw_handler) atmel_set_frag, /* SIOCSIWFRAG */ + (iw_handler) atmel_get_frag, /* SIOCGIWFRAG */ + (iw_handler) NULL, /* SIOCSIWTXPOW */ + (iw_handler) NULL, /* SIOCGIWTXPOW */ + (iw_handler) atmel_set_retry, /* SIOCSIWRETRY */ + (iw_handler) atmel_get_retry, /* SIOCGIWRETRY */ + (iw_handler) atmel_set_encode, /* SIOCSIWENCODE */ + (iw_handler) atmel_get_encode, /* SIOCGIWENCODE */ + (iw_handler) atmel_set_power, /* SIOCSIWPOWER */ + (iw_handler) atmel_get_power, /* SIOCGIWPOWER */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* SIOCSIWGENIE */ + (iw_handler) NULL, /* SIOCGIWGENIE */ + (iw_handler) atmel_set_auth, /* SIOCSIWAUTH */ + (iw_handler) atmel_get_auth, /* SIOCGIWAUTH */ + (iw_handler) atmel_set_encodeext, /* SIOCSIWENCODEEXT */ + (iw_handler) atmel_get_encodeext, /* SIOCGIWENCODEEXT */ + (iw_handler) NULL, /* SIOCSIWPMKSA */ +}; + +static const iw_handler atmel_private_handler[] = +{ + NULL, /* SIOCIWFIRSTPRIV */ +}; + +struct atmel_priv_ioctl { + char id[32]; + unsigned char __user *data; + unsigned short len; +}; + +#define ATMELFWL SIOCIWFIRSTPRIV +#define ATMELIDIFC ATMELFWL + 1 +#define ATMELRD ATMELFWL + 2 +#define ATMELMAGIC 0x51807 +#define REGDOMAINSZ 20 + +static const struct iw_priv_args atmel_private_args[] = { + { + .cmd = ATMELFWL, + .set_args = IW_PRIV_TYPE_BYTE + | IW_PRIV_SIZE_FIXED + | sizeof(struct atmel_priv_ioctl), + .get_args = IW_PRIV_TYPE_NONE, + .name = "atmelfwl" + }, { + .cmd = ATMELIDIFC, + .set_args = IW_PRIV_TYPE_NONE, + .get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + .name = "atmelidifc" + }, { + .cmd = ATMELRD, + .set_args = IW_PRIV_TYPE_CHAR | REGDOMAINSZ, + .get_args = IW_PRIV_TYPE_NONE, + .name = "regdomain" + }, +}; + +static const struct iw_handler_def atmel_handler_def = { + .num_standard = ARRAY_SIZE(atmel_handler), + .num_private = ARRAY_SIZE(atmel_private_handler), + .num_private_args = ARRAY_SIZE(atmel_private_args), + .standard = (iw_handler *) atmel_handler, + .private = (iw_handler *) atmel_private_handler, + .private_args = (struct iw_priv_args *) atmel_private_args, + .get_wireless_stats = atmel_get_wireless_stats +}; + +static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + int i, rc = 0; + struct atmel_private *priv = netdev_priv(dev); + struct atmel_priv_ioctl com; + struct iwreq *wrq = (struct iwreq *) rq; + unsigned char *new_firmware; + char domain[REGDOMAINSZ + 1]; + + switch (cmd) { + case ATMELIDIFC: + wrq->u.param.value = ATMELMAGIC; + break; + + case ATMELFWL: + if (copy_from_user(&com, rq->ifr_data, sizeof(com))) { + rc = -EFAULT; + break; + } + + if (!capable(CAP_NET_ADMIN)) { + rc = -EPERM; + break; + } + + if (!(new_firmware = kmalloc(com.len, GFP_KERNEL))) { + rc = -ENOMEM; + break; + } + + if (copy_from_user(new_firmware, com.data, com.len)) { + kfree(new_firmware); + rc = -EFAULT; + break; + } + + kfree(priv->firmware); + + priv->firmware = new_firmware; + priv->firmware_length = com.len; + strncpy(priv->firmware_id, com.id, 31); + priv->firmware_id[31] = '\0'; + break; + + case ATMELRD: + if (copy_from_user(domain, rq->ifr_data, REGDOMAINSZ)) { + rc = -EFAULT; + break; + } + + if (!capable(CAP_NET_ADMIN)) { + rc = -EPERM; + break; + } + + domain[REGDOMAINSZ] = 0; + rc = -EINVAL; + for (i = 0; i < ARRAY_SIZE(channel_table); i++) { + if (!strcasecmp(channel_table[i].name, domain)) { + priv->config_reg_domain = channel_table[i].reg_domain; + rc = 0; + } + } + + if (rc == 0 && priv->station_state != STATION_STATE_DOWN) + rc = atmel_open(dev); + break; + + default: + rc = -EOPNOTSUPP; + } + + return rc; +} + +struct auth_body { + __le16 alg; + __le16 trans_seq; + __le16 status; + u8 el_id; + u8 chall_text_len; + u8 chall_text[253]; +}; + +static void atmel_enter_state(struct atmel_private *priv, int new_state) +{ + int old_state = priv->station_state; + + if (new_state == old_state) + return; + + priv->station_state = new_state; + + if (new_state == STATION_STATE_READY) { + netif_start_queue(priv->dev); + netif_carrier_on(priv->dev); + } + + if (old_state == STATION_STATE_READY) { + netif_carrier_off(priv->dev); + if (netif_running(priv->dev)) + netif_stop_queue(priv->dev); + priv->last_beacon_timestamp = 0; + } +} + +static void atmel_scan(struct atmel_private *priv, int specific_ssid) +{ + struct { + u8 BSSID[ETH_ALEN]; + u8 SSID[MAX_SSID_LENGTH]; + u8 scan_type; + u8 channel; + __le16 BSS_type; + __le16 min_channel_time; + __le16 max_channel_time; + u8 options; + u8 SSID_size; + } cmd; + + eth_broadcast_addr(cmd.BSSID); + + if (priv->fast_scan) { + cmd.SSID_size = priv->SSID_size; + memcpy(cmd.SSID, priv->SSID, priv->SSID_size); + cmd.min_channel_time = cpu_to_le16(10); + cmd.max_channel_time = cpu_to_le16(50); + } else { + priv->BSS_list_entries = 0; + cmd.SSID_size = 0; + cmd.min_channel_time = cpu_to_le16(10); + cmd.max_channel_time = cpu_to_le16(120); + } + + cmd.options = 0; + + if (!specific_ssid) + cmd.options |= SCAN_OPTIONS_SITE_SURVEY; + + cmd.channel = (priv->channel & 0x7f); + cmd.scan_type = SCAN_TYPE_ACTIVE; + cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ? + BSS_TYPE_AD_HOC : BSS_TYPE_INFRASTRUCTURE); + + atmel_send_command(priv, CMD_Scan, &cmd, sizeof(cmd)); + + /* This must come after all hardware access to avoid being messed up + by stuff happening in interrupt context after we leave STATE_DOWN */ + atmel_enter_state(priv, STATION_STATE_SCANNING); +} + +static void join(struct atmel_private *priv, int type) +{ + struct { + u8 BSSID[6]; + u8 SSID[MAX_SSID_LENGTH]; + u8 BSS_type; /* this is a short in a scan command - weird */ + u8 channel; + __le16 timeout; + u8 SSID_size; + u8 reserved; + } cmd; + + cmd.SSID_size = priv->SSID_size; + memcpy(cmd.SSID, priv->SSID, priv->SSID_size); + memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN); + cmd.channel = (priv->channel & 0x7f); + cmd.BSS_type = type; + cmd.timeout = cpu_to_le16(2000); + + atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd)); +} + +static void start(struct atmel_private *priv, int type) +{ + struct { + u8 BSSID[6]; + u8 SSID[MAX_SSID_LENGTH]; + u8 BSS_type; + u8 channel; + u8 SSID_size; + u8 reserved[3]; + } cmd; + + cmd.SSID_size = priv->SSID_size; + memcpy(cmd.SSID, priv->SSID, priv->SSID_size); + memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN); + cmd.BSS_type = type; + cmd.channel = (priv->channel & 0x7f); + + atmel_send_command(priv, CMD_Start, &cmd, sizeof(cmd)); +} + +static void handle_beacon_probe(struct atmel_private *priv, u16 capability, + u8 channel) +{ + int rejoin = 0; + int new = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? + SHORT_PREAMBLE : LONG_PREAMBLE; + + if (priv->preamble != new) { + priv->preamble = new; + rejoin = 1; + atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, new); + } + + if (priv->channel != channel) { + priv->channel = channel; + rejoin = 1; + atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_CHANNEL_POS, channel); + } + + if (rejoin) { + priv->station_is_associated = 0; + atmel_enter_state(priv, STATION_STATE_JOINNING); + + if (priv->operating_mode == IW_MODE_INFRA) + join(priv, BSS_TYPE_INFRASTRUCTURE); + else + join(priv, BSS_TYPE_AD_HOC); + } +} + +static void send_authentication_request(struct atmel_private *priv, u16 system, + u8 *challenge, int challenge_len) +{ + struct ieee80211_hdr header; + struct auth_body auth; + + header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); + header.duration_id = cpu_to_le16(0x8000); + header.seq_ctrl = 0; + memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); + memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); + + if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1) + /* no WEP for authentication frames with TrSeqNo 1 */ + header.frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); + + auth.alg = cpu_to_le16(system); + + auth.status = 0; + auth.trans_seq = cpu_to_le16(priv->CurrentAuthentTransactionSeqNum); + priv->ExpectedAuthentTransactionSeqNum = priv->CurrentAuthentTransactionSeqNum+1; + priv->CurrentAuthentTransactionSeqNum += 2; + + if (challenge_len != 0) { + auth.el_id = 16; /* challenge_text */ + auth.chall_text_len = challenge_len; + memcpy(auth.chall_text, challenge, challenge_len); + atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 8 + challenge_len); + } else { + atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 6); + } +} + +static void send_association_request(struct atmel_private *priv, int is_reassoc) +{ + u8 *ssid_el_p; + int bodysize; + struct ieee80211_hdr header; + struct ass_req_format { + __le16 capability; + __le16 listen_interval; + u8 ap[ETH_ALEN]; /* nothing after here directly accessible */ + u8 ssid_el_id; + u8 ssid_len; + u8 ssid[MAX_SSID_LENGTH]; + u8 sup_rates_el_id; + u8 sup_rates_len; + u8 rates[4]; + } body; + + header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + (is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ)); + header.duration_id = cpu_to_le16(0x8000); + header.seq_ctrl = 0; + + memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); + memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); + + body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS); + if (priv->wep_is_on) + body.capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); + if (priv->preamble == SHORT_PREAMBLE) + body.capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); + + body.listen_interval = cpu_to_le16(priv->listen_interval * priv->beacon_period); + + /* current AP address - only in reassoc frame */ + if (is_reassoc) { + memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN); + ssid_el_p = &body.ssid_el_id; + bodysize = 18 + priv->SSID_size; + } else { + ssid_el_p = &body.ap[0]; + bodysize = 12 + priv->SSID_size; + } + + ssid_el_p[0] = WLAN_EID_SSID; + ssid_el_p[1] = priv->SSID_size; + memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size); + ssid_el_p[2 + priv->SSID_size] = WLAN_EID_SUPP_RATES; + ssid_el_p[3 + priv->SSID_size] = 4; /* len of supported rates */ + memcpy(ssid_el_p + 4 + priv->SSID_size, atmel_basic_rates, 4); + + atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize); +} + +static int is_frame_from_current_bss(struct atmel_private *priv, + struct ieee80211_hdr *header) +{ + if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) + return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0; + else + return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0; +} + +static int retrieve_bss(struct atmel_private *priv) +{ + int i; + int max_rssi = -128; + int max_index = -1; + + if (priv->BSS_list_entries == 0) + return -1; + + if (priv->connect_to_any_BSS) { + /* Select a BSS with the max-RSSI but of the same type and of + the same WEP mode and that it is not marked as 'bad' (i.e. + we had previously failed to connect to this BSS with the + settings that we currently use) */ + priv->current_BSS = 0; + for (i = 0; i < priv->BSS_list_entries; i++) { + if (priv->operating_mode == priv->BSSinfo[i].BSStype && + ((!priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) || + (priv->wep_is_on && priv->BSSinfo[i].UsingWEP)) && + !(priv->BSSinfo[i].channel & 0x80)) { + max_rssi = priv->BSSinfo[i].RSSI; + priv->current_BSS = max_index = i; + } + } + return max_index; + } + + for (i = 0; i < priv->BSS_list_entries; i++) { + if (priv->SSID_size == priv->BSSinfo[i].SSIDsize && + memcmp(priv->SSID, priv->BSSinfo[i].SSID, priv->SSID_size) == 0 && + priv->operating_mode == priv->BSSinfo[i].BSStype && + atmel_validate_channel(priv, priv->BSSinfo[i].channel) == 0) { + if (priv->BSSinfo[i].RSSI >= max_rssi) { + max_rssi = priv->BSSinfo[i].RSSI; + max_index = i; + } + } + } + return max_index; +} + +static void store_bss_info(struct atmel_private *priv, + struct ieee80211_hdr *header, u16 capability, + u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len, + u8 *ssid, int is_beacon) +{ + u8 *bss = capability & WLAN_CAPABILITY_ESS ? header->addr2 : header->addr3; + int i, index; + + for (index = -1, i = 0; i < priv->BSS_list_entries; i++) + if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0) + index = i; + + /* If we process a probe and an entry from this BSS exists + we will update the BSS entry with the info from this BSS. + If we process a beacon we will only update RSSI */ + + if (index == -1) { + if (priv->BSS_list_entries == MAX_BSS_ENTRIES) + return; + index = priv->BSS_list_entries++; + memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN); + priv->BSSinfo[index].RSSI = rssi; + } else { + if (rssi > priv->BSSinfo[index].RSSI) + priv->BSSinfo[index].RSSI = rssi; + if (is_beacon) + return; + } + + priv->BSSinfo[index].channel = channel; + priv->BSSinfo[index].beacon_period = beacon_period; + priv->BSSinfo[index].UsingWEP = capability & WLAN_CAPABILITY_PRIVACY; + memcpy(priv->BSSinfo[index].SSID, ssid, ssid_len); + priv->BSSinfo[index].SSIDsize = ssid_len; + + if (capability & WLAN_CAPABILITY_IBSS) + priv->BSSinfo[index].BSStype = IW_MODE_ADHOC; + else if (capability & WLAN_CAPABILITY_ESS) + priv->BSSinfo[index].BSStype = IW_MODE_INFRA; + + priv->BSSinfo[index].preamble = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? + SHORT_PREAMBLE : LONG_PREAMBLE; +} + +static void authenticate(struct atmel_private *priv, u16 frame_len) +{ + struct auth_body *auth = (struct auth_body *)priv->rx_buf; + u16 status = le16_to_cpu(auth->status); + u16 trans_seq_no = le16_to_cpu(auth->trans_seq); + u16 system = le16_to_cpu(auth->alg); + + if (status == WLAN_STATUS_SUCCESS && !priv->wep_is_on) { + /* no WEP */ + if (priv->station_was_associated) { + atmel_enter_state(priv, STATION_STATE_REASSOCIATING); + send_association_request(priv, 1); + return; + } else { + atmel_enter_state(priv, STATION_STATE_ASSOCIATING); + send_association_request(priv, 0); + return; + } + } + + if (status == WLAN_STATUS_SUCCESS && priv->wep_is_on) { + int should_associate = 0; + /* WEP */ + if (trans_seq_no != priv->ExpectedAuthentTransactionSeqNum) + return; + + if (system == WLAN_AUTH_OPEN) { + if (trans_seq_no == 0x0002) { + should_associate = 1; + } + } else if (system == WLAN_AUTH_SHARED_KEY) { + if (trans_seq_no == 0x0002 && + auth->el_id == WLAN_EID_CHALLENGE) { + send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len); + return; + } else if (trans_seq_no == 0x0004) { + should_associate = 1; + } + } + + if (should_associate) { + if (priv->station_was_associated) { + atmel_enter_state(priv, STATION_STATE_REASSOCIATING); + send_association_request(priv, 1); + return; + } else { + atmel_enter_state(priv, STATION_STATE_ASSOCIATING); + send_association_request(priv, 0); + return; + } + } + } + + if (status == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { + /* Flip back and forth between WEP auth modes until the max + * authentication tries has been exceeded. + */ + if (system == WLAN_AUTH_OPEN) { + priv->CurrentAuthentTransactionSeqNum = 0x001; + priv->exclude_unencrypted = 1; + send_authentication_request(priv, WLAN_AUTH_SHARED_KEY, NULL, 0); + return; + } else if (system == WLAN_AUTH_SHARED_KEY + && priv->wep_is_on) { + priv->CurrentAuthentTransactionSeqNum = 0x001; + priv->exclude_unencrypted = 0; + send_authentication_request(priv, WLAN_AUTH_OPEN, NULL, 0); + return; + } else if (priv->connect_to_any_BSS) { + int bss_index; + + priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; + + if ((bss_index = retrieve_bss(priv)) != -1) { + atmel_join_bss(priv, bss_index); + return; + } + } + } + + priv->AuthenticationRequestRetryCnt = 0; + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; +} + +static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype) +{ + struct ass_resp_format { + __le16 capability; + __le16 status; + __le16 ass_id; + u8 el_id; + u8 length; + u8 rates[4]; + } *ass_resp = (struct ass_resp_format *)priv->rx_buf; + + u16 status = le16_to_cpu(ass_resp->status); + u16 ass_id = le16_to_cpu(ass_resp->ass_id); + u16 rates_len = ass_resp->length > 4 ? 4 : ass_resp->length; + + union iwreq_data wrqu; + + if (frame_len < 8 + rates_len) + return; + + if (status == WLAN_STATUS_SUCCESS) { + if (subtype == IEEE80211_STYPE_ASSOC_RESP) + priv->AssociationRequestRetryCnt = 0; + else + priv->ReAssociationRequestRetryCnt = 0; + + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_STATION_ID_POS, ass_id & 0x3fff); + atmel_set_mib(priv, Phy_Mib_Type, + PHY_MIB_RATE_SET_POS, ass_resp->rates, rates_len); + if (priv->power_mode == 0) { + priv->listen_interval = 1; + atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); + } else { + priv->listen_interval = 2; + atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_PS_MODE_POS, PS_MODE); + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 2); + } + + priv->station_is_associated = 1; + priv->station_was_associated = 1; + atmel_enter_state(priv, STATION_STATE_READY); + + /* Send association event to userspace */ + wrqu.data.length = 0; + wrqu.data.flags = 0; + memcpy(wrqu.ap_addr.sa_data, priv->CurrentBSSID, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); + + return; + } + + if (subtype == IEEE80211_STYPE_ASSOC_RESP && + status != WLAN_STATUS_ASSOC_DENIED_RATES && + status != WLAN_STATUS_CAPS_UNSUPPORTED && + priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + priv->AssociationRequestRetryCnt++; + send_association_request(priv, 0); + return; + } + + if (subtype == IEEE80211_STYPE_REASSOC_RESP && + status != WLAN_STATUS_ASSOC_DENIED_RATES && + status != WLAN_STATUS_CAPS_UNSUPPORTED && + priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + priv->ReAssociationRequestRetryCnt++; + send_association_request(priv, 1); + return; + } + + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + + if (priv->connect_to_any_BSS) { + int bss_index; + priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; + + if ((bss_index = retrieve_bss(priv)) != -1) + atmel_join_bss(priv, bss_index); + } +} + +static void atmel_join_bss(struct atmel_private *priv, int bss_index) +{ + struct bss_info *bss = &priv->BSSinfo[bss_index]; + + memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN); + memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize); + + /* The WPA stuff cares about the current AP address */ + if (priv->use_wpa) + build_wpa_mib(priv); + + /* When switching to AdHoc turn OFF Power Save if needed */ + + if (bss->BSStype == IW_MODE_ADHOC && + priv->operating_mode != IW_MODE_ADHOC && + priv->power_mode) { + priv->power_mode = 0; + priv->listen_interval = 1; + atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); + } + + priv->operating_mode = bss->BSStype; + priv->channel = bss->channel & 0x7f; + priv->beacon_period = bss->beacon_period; + + if (priv->preamble != bss->preamble) { + priv->preamble = bss->preamble; + atmel_set_mib8(priv, Local_Mib_Type, + LOCAL_MIB_PREAMBLE_TYPE, bss->preamble); + } + + if (!priv->wep_is_on && bss->UsingWEP) { + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + return; + } + + if (priv->wep_is_on && !bss->UsingWEP) { + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + return; + } + + atmel_enter_state(priv, STATION_STATE_JOINNING); + + if (priv->operating_mode == IW_MODE_INFRA) + join(priv, BSS_TYPE_INFRASTRUCTURE); + else + join(priv, BSS_TYPE_AD_HOC); +} + +static void restart_search(struct atmel_private *priv) +{ + int bss_index; + + if (!priv->connect_to_any_BSS) { + atmel_scan(priv, 1); + } else { + priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; + + if ((bss_index = retrieve_bss(priv)) != -1) + atmel_join_bss(priv, bss_index); + else + atmel_scan(priv, 0); + } +} + +static void smooth_rssi(struct atmel_private *priv, u8 rssi) +{ + u8 old = priv->wstats.qual.level; + u8 max_rssi = 42; /* 502-rmfd-revd max by experiment, default for now */ + + switch (priv->firmware_type) { + case ATMEL_FW_TYPE_502E: + max_rssi = 63; /* 502-rmfd-reve max by experiment */ + break; + default: + break; + } + + rssi = rssi * 100 / max_rssi; + if ((rssi + old) % 2) + priv->wstats.qual.level = (rssi + old) / 2 + 1; + else + priv->wstats.qual.level = (rssi + old) / 2; + priv->wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; + priv->wstats.qual.updated &= ~IW_QUAL_LEVEL_INVALID; +} + +static void atmel_smooth_qual(struct atmel_private *priv) +{ + unsigned long time_diff = (jiffies - priv->last_qual) / HZ; + while (time_diff--) { + priv->last_qual += HZ; + priv->wstats.qual.qual = priv->wstats.qual.qual / 2; + priv->wstats.qual.qual += + priv->beacons_this_sec * priv->beacon_period * (priv->wstats.qual.level + 100) / 4000; + priv->beacons_this_sec = 0; + } + priv->wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; + priv->wstats.qual.updated &= ~IW_QUAL_QUAL_INVALID; +} + +/* deals with incoming management frames. */ +static void atmel_management_frame(struct atmel_private *priv, + struct ieee80211_hdr *header, + u16 frame_len, u8 rssi) +{ + u16 subtype; + + subtype = le16_to_cpu(header->frame_control) & IEEE80211_FCTL_STYPE; + switch (subtype) { + case IEEE80211_STYPE_BEACON: + case IEEE80211_STYPE_PROBE_RESP: + + /* beacon frame has multiple variable-length fields - + never let an engineer loose with a data structure design. */ + { + struct beacon_format { + __le64 timestamp; + __le16 interval; + __le16 capability; + u8 ssid_el_id; + u8 ssid_length; + /* ssid here */ + u8 rates_el_id; + u8 rates_length; + /* rates here */ + u8 ds_el_id; + u8 ds_length; + /* ds here */ + } *beacon = (struct beacon_format *)priv->rx_buf; + + u8 channel, rates_length, ssid_length; + u64 timestamp = le64_to_cpu(beacon->timestamp); + u16 beacon_interval = le16_to_cpu(beacon->interval); + u16 capability = le16_to_cpu(beacon->capability); + u8 *beaconp = priv->rx_buf; + ssid_length = beacon->ssid_length; + /* this blows chunks. */ + if (frame_len < 14 || frame_len < ssid_length + 15) + return; + rates_length = beaconp[beacon->ssid_length + 15]; + if (frame_len < ssid_length + rates_length + 18) + return; + if (ssid_length > MAX_SSID_LENGTH) + return; + channel = beaconp[ssid_length + rates_length + 18]; + + if (priv->station_state == STATION_STATE_READY) { + smooth_rssi(priv, rssi); + if (is_frame_from_current_bss(priv, header)) { + priv->beacons_this_sec++; + atmel_smooth_qual(priv); + if (priv->last_beacon_timestamp) { + /* Note truncate this to 32 bits - kernel can't divide a long long */ + u32 beacon_delay = timestamp - priv->last_beacon_timestamp; + int beacons = beacon_delay / (beacon_interval * 1000); + if (beacons > 1) + priv->wstats.miss.beacon += beacons - 1; + } + priv->last_beacon_timestamp = timestamp; + handle_beacon_probe(priv, capability, channel); + } + } + + if (priv->station_state == STATION_STATE_SCANNING) + store_bss_info(priv, header, capability, + beacon_interval, channel, rssi, + ssid_length, + &beacon->rates_el_id, + subtype == IEEE80211_STYPE_BEACON); + } + break; + + case IEEE80211_STYPE_AUTH: + + if (priv->station_state == STATION_STATE_AUTHENTICATING) + authenticate(priv, frame_len); + + break; + + case IEEE80211_STYPE_ASSOC_RESP: + case IEEE80211_STYPE_REASSOC_RESP: + + if (priv->station_state == STATION_STATE_ASSOCIATING || + priv->station_state == STATION_STATE_REASSOCIATING) + associate(priv, frame_len, subtype); + + break; + + case IEEE80211_STYPE_DISASSOC: + if (priv->station_is_associated && + priv->operating_mode == IW_MODE_INFRA && + is_frame_from_current_bss(priv, header)) { + priv->station_was_associated = 0; + priv->station_is_associated = 0; + + atmel_enter_state(priv, STATION_STATE_JOINNING); + join(priv, BSS_TYPE_INFRASTRUCTURE); + } + + break; + + case IEEE80211_STYPE_DEAUTH: + if (priv->operating_mode == IW_MODE_INFRA && + is_frame_from_current_bss(priv, header)) { + priv->station_was_associated = 0; + + atmel_enter_state(priv, STATION_STATE_JOINNING); + join(priv, BSS_TYPE_INFRASTRUCTURE); + } + + break; + } +} + +/* run when timer expires */ +static void atmel_management_timer(u_long a) +{ + struct net_device *dev = (struct net_device *) a; + struct atmel_private *priv = netdev_priv(dev); + unsigned long flags; + + /* Check if the card has been yanked. */ + if (priv->card && priv->present_callback && + !(*priv->present_callback)(priv->card)) + return; + + spin_lock_irqsave(&priv->irqlock, flags); + + switch (priv->station_state) { + + case STATION_STATE_AUTHENTICATING: + if (priv->AuthenticationRequestRetryCnt >= MAX_AUTHENTICATION_RETRIES) { + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + priv->AuthenticationRequestRetryCnt = 0; + restart_search(priv); + } else { + int auth = WLAN_AUTH_OPEN; + priv->AuthenticationRequestRetryCnt++; + priv->CurrentAuthentTransactionSeqNum = 0x0001; + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + if (priv->wep_is_on && priv->exclude_unencrypted) + auth = WLAN_AUTH_SHARED_KEY; + send_authentication_request(priv, auth, NULL, 0); + } + break; + + case STATION_STATE_ASSOCIATING: + if (priv->AssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + priv->AssociationRequestRetryCnt = 0; + restart_search(priv); + } else { + priv->AssociationRequestRetryCnt++; + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + send_association_request(priv, 0); + } + break; + + case STATION_STATE_REASSOCIATING: + if (priv->ReAssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + priv->ReAssociationRequestRetryCnt = 0; + restart_search(priv); + } else { + priv->ReAssociationRequestRetryCnt++; + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + send_association_request(priv, 1); + } + break; + + default: + break; + } + + spin_unlock_irqrestore(&priv->irqlock, flags); +} + +static void atmel_command_irq(struct atmel_private *priv) +{ + u8 status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); + u8 command = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET)); + int fast_scan; + union iwreq_data wrqu; + + if (status == CMD_STATUS_IDLE || + status == CMD_STATUS_IN_PROGRESS) + return; + + switch (command) { + case CMD_Start: + if (status == CMD_STATUS_COMPLETE) { + priv->station_was_associated = priv->station_is_associated; + atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, + (u8 *)priv->CurrentBSSID, 6); + atmel_enter_state(priv, STATION_STATE_READY); + } + break; + + case CMD_Scan: + fast_scan = priv->fast_scan; + priv->fast_scan = 0; + + if (status != CMD_STATUS_COMPLETE) { + atmel_scan(priv, 1); + } else { + int bss_index = retrieve_bss(priv); + int notify_scan_complete = 1; + if (bss_index != -1) { + atmel_join_bss(priv, bss_index); + } else if (priv->operating_mode == IW_MODE_ADHOC && + priv->SSID_size != 0) { + start(priv, BSS_TYPE_AD_HOC); + } else { + priv->fast_scan = !fast_scan; + atmel_scan(priv, 1); + notify_scan_complete = 0; + } + priv->site_survey_state = SITE_SURVEY_COMPLETED; + if (notify_scan_complete) { + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); + } + } + break; + + case CMD_SiteSurvey: + priv->fast_scan = 0; + + if (status != CMD_STATUS_COMPLETE) + return; + + priv->site_survey_state = SITE_SURVEY_COMPLETED; + if (priv->station_is_associated) { + atmel_enter_state(priv, STATION_STATE_READY); + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); + } else { + atmel_scan(priv, 1); + } + break; + + case CMD_Join: + if (status == CMD_STATUS_COMPLETE) { + if (priv->operating_mode == IW_MODE_ADHOC) { + priv->station_was_associated = priv->station_is_associated; + atmel_enter_state(priv, STATION_STATE_READY); + } else { + int auth = WLAN_AUTH_OPEN; + priv->AuthenticationRequestRetryCnt = 0; + atmel_enter_state(priv, STATION_STATE_AUTHENTICATING); + + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + priv->CurrentAuthentTransactionSeqNum = 0x0001; + if (priv->wep_is_on && priv->exclude_unencrypted) + auth = WLAN_AUTH_SHARED_KEY; + send_authentication_request(priv, auth, NULL, 0); + } + return; + } + + atmel_scan(priv, 1); + } +} + +static int atmel_wakeup_firmware(struct atmel_private *priv) +{ + struct host_info_struct *iface = &priv->host_info; + u16 mr1, mr3; + int i; + + if (priv->card_type == CARD_TYPE_SPI_FLASH) + atmel_set_gcr(priv->dev, GCR_REMAP); + + /* wake up on-board processor */ + atmel_clear_gcr(priv->dev, 0x0040); + atmel_write16(priv->dev, BSR, BSS_SRAM); + + if (priv->card_type == CARD_TYPE_SPI_FLASH) + mdelay(100); + + /* and wait for it */ + for (i = LOOP_RETRY_LIMIT; i; i--) { + mr1 = atmel_read16(priv->dev, MR1); + mr3 = atmel_read16(priv->dev, MR3); + + if (mr3 & MAC_BOOT_COMPLETE) + break; + if (mr1 & MAC_BOOT_COMPLETE && + priv->bus_type == BUS_TYPE_PCCARD) + break; + } + + if (i == 0) { + printk(KERN_ALERT "%s: MAC failed to boot.\n", priv->dev->name); + return -EIO; + } + + if ((priv->host_info_base = atmel_read16(priv->dev, MR2)) == 0xffff) { + printk(KERN_ALERT "%s: card missing.\n", priv->dev->name); + return -ENODEV; + } + + /* now check for completion of MAC initialization through + the FunCtrl field of the IFACE, poll MR1 to detect completion of + MAC initialization, check completion status, set interrupt mask, + enables interrupts and calls Tx and Rx initialization functions */ + + atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), FUNC_CTRL_INIT_COMPLETE); + + for (i = LOOP_RETRY_LIMIT; i; i--) { + mr1 = atmel_read16(priv->dev, MR1); + mr3 = atmel_read16(priv->dev, MR3); + + if (mr3 & MAC_INIT_COMPLETE) + break; + if (mr1 & MAC_INIT_COMPLETE && + priv->bus_type == BUS_TYPE_PCCARD) + break; + } + + if (i == 0) { + printk(KERN_ALERT "%s: MAC failed to initialise.\n", + priv->dev->name); + return -EIO; + } + + /* Check for MAC_INIT_OK only on the register that the MAC_INIT_OK was set */ + if ((mr3 & MAC_INIT_COMPLETE) && + !(atmel_read16(priv->dev, MR3) & MAC_INIT_OK)) { + printk(KERN_ALERT "%s: MAC failed MR3 self-test.\n", priv->dev->name); + return -EIO; + } + if ((mr1 & MAC_INIT_COMPLETE) && + !(atmel_read16(priv->dev, MR1) & MAC_INIT_OK)) { + printk(KERN_ALERT "%s: MAC failed MR1 self-test.\n", priv->dev->name); + return -EIO; + } + + atmel_copy_to_host(priv->dev, (unsigned char *)iface, + priv->host_info_base, sizeof(*iface)); + + iface->tx_buff_pos = le16_to_cpu(iface->tx_buff_pos); + iface->tx_buff_size = le16_to_cpu(iface->tx_buff_size); + iface->tx_desc_pos = le16_to_cpu(iface->tx_desc_pos); + iface->tx_desc_count = le16_to_cpu(iface->tx_desc_count); + iface->rx_buff_pos = le16_to_cpu(iface->rx_buff_pos); + iface->rx_buff_size = le16_to_cpu(iface->rx_buff_size); + iface->rx_desc_pos = le16_to_cpu(iface->rx_desc_pos); + iface->rx_desc_count = le16_to_cpu(iface->rx_desc_count); + iface->build_version = le16_to_cpu(iface->build_version); + iface->command_pos = le16_to_cpu(iface->command_pos); + iface->major_version = le16_to_cpu(iface->major_version); + iface->minor_version = le16_to_cpu(iface->minor_version); + iface->func_ctrl = le16_to_cpu(iface->func_ctrl); + iface->mac_status = le16_to_cpu(iface->mac_status); + + return 0; +} + +/* determine type of memory and MAC address */ +static int probe_atmel_card(struct net_device *dev) +{ + int rc = 0; + struct atmel_private *priv = netdev_priv(dev); + + /* reset pccard */ + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(dev, GCR, 0x0060); + + atmel_write16(dev, GCR, 0x0040); + mdelay(500); + + if (atmel_read16(dev, MR2) == 0) { + /* No stored firmware so load a small stub which just + tells us the MAC address */ + int i; + priv->card_type = CARD_TYPE_EEPROM; + atmel_write16(dev, BSR, BSS_IRAM); + atmel_copy_to_card(dev, 0, mac_reader, sizeof(mac_reader)); + atmel_set_gcr(dev, GCR_REMAP); + atmel_clear_gcr(priv->dev, 0x0040); + atmel_write16(dev, BSR, BSS_SRAM); + for (i = LOOP_RETRY_LIMIT; i; i--) + if (atmel_read16(dev, MR3) & MAC_BOOT_COMPLETE) + break; + if (i == 0) { + printk(KERN_ALERT "%s: MAC failed to boot MAC address reader.\n", dev->name); + } else { + atmel_copy_to_host(dev, dev->dev_addr, atmel_read16(dev, MR2), 6); + /* got address, now squash it again until the network + interface is opened */ + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(dev, GCR, 0x0060); + atmel_write16(dev, GCR, 0x0040); + rc = 1; + } + } else if (atmel_read16(dev, MR4) == 0) { + /* Mac address easy in this case. */ + priv->card_type = CARD_TYPE_PARALLEL_FLASH; + atmel_write16(dev, BSR, 1); + atmel_copy_to_host(dev, dev->dev_addr, 0xc000, 6); + atmel_write16(dev, BSR, 0x200); + rc = 1; + } else { + /* Standard firmware in flash, boot it up and ask + for the Mac Address */ + priv->card_type = CARD_TYPE_SPI_FLASH; + if (atmel_wakeup_firmware(priv) == 0) { + atmel_get_mib(priv, Mac_Address_Mib_Type, 0, dev->dev_addr, 6); + + /* got address, now squash it again until the network + interface is opened */ + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(dev, GCR, 0x0060); + atmel_write16(dev, GCR, 0x0040); + rc = 1; + } + } + + if (rc) { + if (dev->dev_addr[0] == 0xFF) { + static const u8 default_mac[] = { + 0x00, 0x04, 0x25, 0x00, 0x00, 0x00 + }; + printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name); + memcpy(dev->dev_addr, default_mac, ETH_ALEN); + } + } + + return rc; +} + +/* Move the encyption information on the MIB structure. + This routine is for the pre-WPA firmware: later firmware has + a different format MIB and a different routine. */ +static void build_wep_mib(struct atmel_private *priv) +{ + struct { /* NB this is matched to the hardware, don't change. */ + u8 wep_is_on; + u8 default_key; /* 0..3 */ + u8 reserved; + u8 exclude_unencrypted; + + u32 WEPICV_error_count; + u32 WEP_excluded_count; + + u8 wep_keys[MAX_ENCRYPTION_KEYS][13]; + u8 encryption_level; /* 0, 1, 2 */ + u8 reserved2[3]; + } mib; + int i; + + mib.wep_is_on = priv->wep_is_on; + if (priv->wep_is_on) { + if (priv->wep_key_len[priv->default_key] > 5) + mib.encryption_level = 2; + else + mib.encryption_level = 1; + } else { + mib.encryption_level = 0; + } + + mib.default_key = priv->default_key; + mib.exclude_unencrypted = priv->exclude_unencrypted; + + for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) + memcpy(mib.wep_keys[i], priv->wep_keys[i], 13); + + atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); +} + +static void build_wpa_mib(struct atmel_private *priv) +{ + /* This is for the later (WPA enabled) firmware. */ + + struct { /* NB this is matched to the hardware, don't change. */ + u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; + u8 receiver_address[ETH_ALEN]; + u8 wep_is_on; + u8 default_key; /* 0..3 */ + u8 group_key; + u8 exclude_unencrypted; + u8 encryption_type; + u8 reserved; + + u32 WEPICV_error_count; + u32 WEP_excluded_count; + + u8 key_RSC[4][8]; + } mib; + + int i; + + mib.wep_is_on = priv->wep_is_on; + mib.exclude_unencrypted = priv->exclude_unencrypted; + memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN); + + /* zero all the keys before adding in valid ones. */ + memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value)); + + if (priv->wep_is_on) { + /* There's a comment in the Atmel code to the effect that this + is only valid when still using WEP, it may need to be set to + something to use WPA */ + memset(mib.key_RSC, 0, sizeof(mib.key_RSC)); + + mib.default_key = mib.group_key = 255; + for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) { + if (priv->wep_key_len[i] > 0) { + memcpy(mib.cipher_default_key_value[i], priv->wep_keys[i], MAX_ENCRYPTION_KEY_SIZE); + if (i == priv->default_key) { + mib.default_key = i; + mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 7; + mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->pairwise_cipher_suite; + } else { + mib.group_key = i; + priv->group_cipher_suite = priv->pairwise_cipher_suite; + mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 1; + mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->group_cipher_suite; + } + } + } + if (mib.default_key == 255) + mib.default_key = mib.group_key != 255 ? mib.group_key : 0; + if (mib.group_key == 255) + mib.group_key = mib.default_key; + + } + + atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); +} + +static int reset_atmel_card(struct net_device *dev) +{ + /* do everything necessary to wake up the hardware, including + waiting for the lightning strike and throwing the knife switch.... + + set all the Mib values which matter in the card to match + their settings in the atmel_private structure. Some of these + can be altered on the fly, but many (WEP, infrastucture or ad-hoc) + can only be changed by tearing down the world and coming back through + here. + + This routine is also responsible for initialising some + hardware-specific fields in the atmel_private structure, + including a copy of the firmware's hostinfo structure + which is the route into the rest of the firmware datastructures. */ + + struct atmel_private *priv = netdev_priv(dev); + u8 configuration; + int old_state = priv->station_state; + int err = 0; + + /* data to add to the firmware names, in priority order + this implemenents firmware versioning */ + + static char *firmware_modifier[] = { + "-wpa", + "", + NULL + }; + + /* reset pccard */ + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(priv->dev, GCR, 0x0060); + + /* stop card , disable interrupts */ + atmel_write16(priv->dev, GCR, 0x0040); + + if (priv->card_type == CARD_TYPE_EEPROM) { + /* copy in firmware if needed */ + const struct firmware *fw_entry = NULL; + const unsigned char *fw; + int len = priv->firmware_length; + if (!(fw = priv->firmware)) { + if (priv->firmware_type == ATMEL_FW_TYPE_NONE) { + if (strlen(priv->firmware_id) == 0) { + printk(KERN_INFO + "%s: card type is unknown: assuming at76c502 firmware is OK.\n", + dev->name); + printk(KERN_INFO + "%s: if not, use the firmware= module parameter.\n", + dev->name); + strcpy(priv->firmware_id, "atmel_at76c502.bin"); + } + err = request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev); + if (err != 0) { + printk(KERN_ALERT + "%s: firmware %s is missing, cannot continue.\n", + dev->name, priv->firmware_id); + return err; + } + } else { + int fw_index = 0; + int success = 0; + + /* get firmware filename entry based on firmware type ID */ + while (fw_table[fw_index].fw_type != priv->firmware_type + && fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) + fw_index++; + + /* construct the actual firmware file name */ + if (fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) { + int i; + for (i = 0; firmware_modifier[i]; i++) { + snprintf(priv->firmware_id, 32, "%s%s.%s", fw_table[fw_index].fw_file, + firmware_modifier[i], fw_table[fw_index].fw_file_ext); + priv->firmware_id[31] = '\0'; + if (request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev) == 0) { + success = 1; + break; + } + } + } + if (!success) { + printk(KERN_ALERT + "%s: firmware %s is missing, cannot start.\n", + dev->name, priv->firmware_id); + priv->firmware_id[0] = '\0'; + return -ENOENT; + } + } + + fw = fw_entry->data; + len = fw_entry->size; + } + + if (len <= 0x6000) { + atmel_write16(priv->dev, BSR, BSS_IRAM); + atmel_copy_to_card(priv->dev, 0, fw, len); + atmel_set_gcr(priv->dev, GCR_REMAP); + } else { + /* Remap */ + atmel_set_gcr(priv->dev, GCR_REMAP); + atmel_write16(priv->dev, BSR, BSS_IRAM); + atmel_copy_to_card(priv->dev, 0, fw, 0x6000); + atmel_write16(priv->dev, BSR, 0x2ff); + atmel_copy_to_card(priv->dev, 0x8000, &fw[0x6000], len - 0x6000); + } + + release_firmware(fw_entry); + } + + err = atmel_wakeup_firmware(priv); + if (err != 0) + return err; + + /* Check the version and set the correct flag for wpa stuff, + old and new firmware is incompatible. + The pre-wpa 3com firmware reports major version 5, + the wpa 3com firmware is major version 4 and doesn't need + the 3com broken-ness filter. */ + priv->use_wpa = (priv->host_info.major_version == 4); + priv->radio_on_broken = (priv->host_info.major_version == 5); + + /* unmask all irq sources */ + atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_MASK_OFFSET), 0xff); + + /* int Tx system and enable Tx */ + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, 0), 0); + atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, 0), 0x80000000L); + atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, 0), 0); + atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, 0), 0); + + priv->tx_desc_free = priv->host_info.tx_desc_count; + priv->tx_desc_head = 0; + priv->tx_desc_tail = 0; + priv->tx_desc_previous = 0; + priv->tx_free_mem = priv->host_info.tx_buff_size; + priv->tx_buff_head = 0; + priv->tx_buff_tail = 0; + + configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); + atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), + configuration | FUNC_CTRL_TxENABLE); + + /* init Rx system and enable */ + priv->rx_desc_head = 0; + + configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); + atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), + configuration | FUNC_CTRL_RxENABLE); + + if (!priv->radio_on_broken) { + if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) == + CMD_STATUS_REJECTED_RADIO_OFF) { + printk(KERN_INFO "%s: cannot turn the radio on.\n", + dev->name); + return -EIO; + } + } + + /* set up enough MIB values to run. */ + atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_AUTO_TX_RATE_POS, priv->auto_tx_rate); + atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_TX_PROMISCUOUS_POS, PROM_MODE_OFF); + atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_RTS_THRESHOLD_POS, priv->rts_threshold); + atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_FRAG_THRESHOLD_POS, priv->frag_threshold); + atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_SHORT_RETRY_POS, priv->short_retry); + atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_LONG_RETRY_POS, priv->long_retry); + atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, priv->preamble); + atmel_set_mib(priv, Mac_Address_Mib_Type, MAC_ADDR_MIB_MAC_ADDR_POS, + priv->dev->dev_addr, 6); + atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_BEACON_PER_POS, priv->default_beacon_period); + atmel_set_mib(priv, Phy_Mib_Type, PHY_MIB_RATE_SET_POS, atmel_basic_rates, 4); + atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_PRIVACY_POS, priv->wep_is_on); + if (priv->use_wpa) + build_wpa_mib(priv); + else + build_wep_mib(priv); + + if (old_state == STATION_STATE_READY) { + union iwreq_data wrqu; + + wrqu.data.length = 0; + wrqu.data.flags = 0; + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + eth_zero_addr(wrqu.ap_addr.sa_data); + wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); + } + + return 0; +} + +static void atmel_send_command(struct atmel_private *priv, int command, + void *cmd, int cmd_size) +{ + if (cmd) + atmel_copy_to_card(priv->dev, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET), + cmd, cmd_size); + + atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET), command); + atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET), 0); +} + +static int atmel_send_command_wait(struct atmel_private *priv, int command, + void *cmd, int cmd_size) +{ + int i, status; + + atmel_send_command(priv, command, cmd, cmd_size); + + for (i = 5000; i; i--) { + status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); + if (status != CMD_STATUS_IDLE && + status != CMD_STATUS_IN_PROGRESS) + break; + udelay(20); + } + + if (i == 0) { + printk(KERN_ALERT "%s: failed to contact MAC.\n", priv->dev->name); + status = CMD_STATUS_HOST_ERROR; + } else { + if (command != CMD_EnableRadio) + status = CMD_STATUS_COMPLETE; + } + + return status; +} + +static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index) +{ + struct get_set_mib m; + m.type = type; + m.size = 1; + m.index = index; + + atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + 1); + return atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE)); +} + +static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, u8 data) +{ + struct get_set_mib m; + m.type = type; + m.size = 1; + m.index = index; + m.data[0] = data; + + atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 1); +} + +static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, + u16 data) +{ + struct get_set_mib m; + m.type = type; + m.size = 2; + m.index = index; + m.data[0] = data; + m.data[1] = data >> 8; + + atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 2); +} + +static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, + u8 *data, int data_len) +{ + struct get_set_mib m; + m.type = type; + m.size = data_len; + m.index = index; + + if (data_len > MIB_MAX_DATA_BYTES) + printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); + + memcpy(m.data, data, data_len); + atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); +} + +static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, + u8 *data, int data_len) +{ + struct get_set_mib m; + m.type = type; + m.size = data_len; + m.index = index; + + if (data_len > MIB_MAX_DATA_BYTES) + printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); + + atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); + atmel_copy_to_host(priv->dev, data, + atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE), data_len); +} + +static void atmel_writeAR(struct net_device *dev, u16 data) +{ + int i; + outw(data, dev->base_addr + AR); + /* Address register appears to need some convincing..... */ + for (i = 0; data != inw(dev->base_addr + AR) && i < 10; i++) + outw(data, dev->base_addr + AR); +} + +static void atmel_copy_to_card(struct net_device *dev, u16 dest, + const unsigned char *src, u16 len) +{ + int i; + atmel_writeAR(dev, dest); + if (dest % 2) { + atmel_write8(dev, DR, *src); + src++; len--; + } + for (i = len; i > 1 ; i -= 2) { + u8 lb = *src++; + u8 hb = *src++; + atmel_write16(dev, DR, lb | (hb << 8)); + } + if (i) + atmel_write8(dev, DR, *src); +} + +static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, + u16 src, u16 len) +{ + int i; + atmel_writeAR(dev, src); + if (src % 2) { + *dest = atmel_read8(dev, DR); + dest++; len--; + } + for (i = len; i > 1 ; i -= 2) { + u16 hw = atmel_read16(dev, DR); + *dest++ = hw; + *dest++ = hw >> 8; + } + if (i) + *dest = atmel_read8(dev, DR); +} + +static void atmel_set_gcr(struct net_device *dev, u16 mask) +{ + outw(inw(dev->base_addr + GCR) | mask, dev->base_addr + GCR); +} + +static void atmel_clear_gcr(struct net_device *dev, u16 mask) +{ + outw(inw(dev->base_addr + GCR) & ~mask, dev->base_addr + GCR); +} + +static int atmel_lock_mac(struct atmel_private *priv) +{ + int i, j = 20; + retry: + for (i = 5000; i; i--) { + if (!atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) + break; + udelay(20); + } + + if (!i) + return 0; /* timed out */ + + atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 1); + if (atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) { + atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); + if (!j--) + return 0; /* timed out */ + goto retry; + } + + return 1; +} + +static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data) +{ + atmel_writeAR(priv->dev, pos); + atmel_write16(priv->dev, DR, data); /* card is little-endian */ + atmel_write16(priv->dev, DR, data >> 16); +} + +/***************************************************************************/ +/* There follows the source form of the MAC address reading firmware */ +/***************************************************************************/ +#if 0 + +/* Copyright 2003 Matthew T. Russotto */ +/* But derived from the Atmel 76C502 firmware written by Atmel and */ +/* included in "atmel wireless lan drivers" package */ +/** + This file is part of net.russotto.AtmelMACFW, hereto referred to + as AtmelMACFW + + AtmelMACFW is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + AtmelMACFW is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with AtmelMACFW; if not, see . + +****************************************************************************/ +/* This firmware should work on the 76C502 RFMD, RFMD_D, and RFMD_E */ +/* It will probably work on the 76C504 and 76C502 RFMD_3COM */ +/* It only works on SPI EEPROM versions of the card. */ + +/* This firmware initializes the SPI controller and clock, reads the MAC */ +/* address from the EEPROM into SRAM, and puts the SRAM offset of the MAC */ +/* address in MR2, and sets MR3 to 0x10 to indicate it is done */ +/* It also puts a complete copy of the EEPROM in SRAM with the offset in */ +/* MR4, for investigational purposes (maybe we can determine chip type */ +/* from that?) */ + + .org 0 + .set MRBASE, 0x8000000 + .set CPSR_INITIAL, 0xD3 /* IRQ/FIQ disabled, ARM mode, Supervisor state */ + .set CPSR_USER, 0xD1 /* IRQ/FIQ disabled, ARM mode, USER state */ + .set SRAM_BASE, 0x02000000 + .set SP_BASE, 0x0F300000 + .set UNK_BASE, 0x0F000000 /* Some internal device, but which one? */ + .set SPI_CGEN_BASE, 0x0E000000 /* Some internal device, but which one? */ + .set UNK3_BASE, 0x02014000 /* Some internal device, but which one? */ + .set STACK_BASE, 0x5600 + .set SP_SR, 0x10 + .set SP_TDRE, 2 /* status register bit -- TDR empty */ + .set SP_RDRF, 1 /* status register bit -- RDR full */ + .set SP_SWRST, 0x80 + .set SP_SPIEN, 0x1 + .set SP_CR, 0 /* control register */ + .set SP_MR, 4 /* mode register */ + .set SP_RDR, 0x08 /* Read Data Register */ + .set SP_TDR, 0x0C /* Transmit Data Register */ + .set SP_CSR0, 0x30 /* chip select registers */ + .set SP_CSR1, 0x34 + .set SP_CSR2, 0x38 + .set SP_CSR3, 0x3C + .set NVRAM_CMD_RDSR, 5 /* read status register */ + .set NVRAM_CMD_READ, 3 /* read data */ + .set NVRAM_SR_RDY, 1 /* RDY bit. This bit is inverted */ + .set SPI_8CLOCKS, 0xFF /* Writing this to the TDR doesn't do anything to the + serial output, since SO is normally high. But it + does cause 8 clock cycles and thus 8 bits to be + clocked in to the chip. See Atmel's SPI + controller (e.g. AT91M55800) timing and 4K + SPI EEPROM manuals */ + + .set NVRAM_SCRATCH, 0x02000100 /* arbitrary area for scratchpad memory */ + .set NVRAM_IMAGE, 0x02000200 + .set NVRAM_LENGTH, 0x0200 + .set MAC_ADDRESS_MIB, SRAM_BASE + .set MAC_ADDRESS_LENGTH, 6 + .set MAC_BOOT_FLAG, 0x10 + .set MR1, 0 + .set MR2, 4 + .set MR3, 8 + .set MR4, 0xC +RESET_VECTOR: + b RESET_HANDLER +UNDEF_VECTOR: + b HALT1 +SWI_VECTOR: + b HALT1 +IABORT_VECTOR: + b HALT1 +DABORT_VECTOR: +RESERVED_VECTOR: + b HALT1 +IRQ_VECTOR: + b HALT1 +FIQ_VECTOR: + b HALT1 +HALT1: b HALT1 +RESET_HANDLER: + mov r0, #CPSR_INITIAL + msr CPSR_c, r0 /* This is probably unnecessary */ + +/* I'm guessing this is initializing clock generator electronics for SPI */ + ldr r0, =SPI_CGEN_BASE + mov r1, #0 + mov r1, r1, lsl #3 + orr r1, r1, #0 + str r1, [r0] + ldr r1, [r0, #28] + bic r1, r1, #16 + str r1, [r0, #28] + mov r1, #1 + str r1, [r0, #8] + + ldr r0, =MRBASE + mov r1, #0 + strh r1, [r0, #MR1] + strh r1, [r0, #MR2] + strh r1, [r0, #MR3] + strh r1, [r0, #MR4] + + mov sp, #STACK_BASE + bl SP_INIT + mov r0, #10 + bl DELAY9 + bl GET_MAC_ADDR + bl GET_WHOLE_NVRAM + ldr r0, =MRBASE + ldr r1, =MAC_ADDRESS_MIB + strh r1, [r0, #MR2] + ldr r1, =NVRAM_IMAGE + strh r1, [r0, #MR4] + mov r1, #MAC_BOOT_FLAG + strh r1, [r0, #MR3] +HALT2: b HALT2 +.func Get_Whole_NVRAM, GET_WHOLE_NVRAM +GET_WHOLE_NVRAM: + stmdb sp!, {lr} + mov r2, #0 /* 0th bytes of NVRAM */ + mov r3, #NVRAM_LENGTH + mov r1, #0 /* not used in routine */ + ldr r0, =NVRAM_IMAGE + bl NVRAM_XFER + ldmia sp!, {lr} + bx lr +.endfunc + +.func Get_MAC_Addr, GET_MAC_ADDR +GET_MAC_ADDR: + stmdb sp!, {lr} + mov r2, #0x120 /* address of MAC Address within NVRAM */ + mov r3, #MAC_ADDRESS_LENGTH + mov r1, #0 /* not used in routine */ + ldr r0, =MAC_ADDRESS_MIB + bl NVRAM_XFER + ldmia sp!, {lr} + bx lr +.endfunc +.ltorg +.func Delay9, DELAY9 +DELAY9: + adds r0, r0, r0, LSL #3 /* r0 = r0 * 9 */ +DELAYLOOP: + beq DELAY9_done + subs r0, r0, #1 + b DELAYLOOP +DELAY9_done: + bx lr +.endfunc + +.func SP_Init, SP_INIT +SP_INIT: + mov r1, #SP_SWRST + ldr r0, =SP_BASE + str r1, [r0, #SP_CR] /* reset the SPI */ + mov r1, #0 + str r1, [r0, #SP_CR] /* release SPI from reset state */ + mov r1, #SP_SPIEN + str r1, [r0, #SP_MR] /* set the SPI to MASTER mode*/ + str r1, [r0, #SP_CR] /* enable the SPI */ + +/* My guess would be this turns on the SPI clock */ + ldr r3, =SPI_CGEN_BASE + ldr r1, [r3, #28] + orr r1, r1, #0x2000 + str r1, [r3, #28] + + ldr r1, =0x2000c01 + str r1, [r0, #SP_CSR0] + ldr r1, =0x2000201 + str r1, [r0, #SP_CSR1] + str r1, [r0, #SP_CSR2] + str r1, [r0, #SP_CSR3] + ldr r1, [r0, #SP_SR] + ldr r0, [r0, #SP_RDR] + bx lr +.endfunc +.func NVRAM_Init, NVRAM_INIT +NVRAM_INIT: + ldr r1, =SP_BASE + ldr r0, [r1, #SP_RDR] + mov r0, #NVRAM_CMD_RDSR + str r0, [r1, #SP_TDR] +SP_loop1: + ldr r0, [r1, #SP_SR] + tst r0, #SP_TDRE + beq SP_loop1 + + mov r0, #SPI_8CLOCKS + str r0, [r1, #SP_TDR] +SP_loop2: + ldr r0, [r1, #SP_SR] + tst r0, #SP_TDRE + beq SP_loop2 + + ldr r0, [r1, #SP_RDR] +SP_loop3: + ldr r0, [r1, #SP_SR] + tst r0, #SP_RDRF + beq SP_loop3 + + ldr r0, [r1, #SP_RDR] + and r0, r0, #255 + bx lr +.endfunc + +.func NVRAM_Xfer, NVRAM_XFER + /* r0 = dest address */ + /* r1 = not used */ + /* r2 = src address within NVRAM */ + /* r3 = length */ +NVRAM_XFER: + stmdb sp!, {r4, r5, lr} + mov r5, r0 /* save r0 (dest address) */ + mov r4, r3 /* save r3 (length) */ + mov r0, r2, LSR #5 /* SPI memories put A8 in the command field */ + and r0, r0, #8 + add r0, r0, #NVRAM_CMD_READ + ldr r1, =NVRAM_SCRATCH + strb r0, [r1, #0] /* save command in NVRAM_SCRATCH[0] */ + strb r2, [r1, #1] /* save low byte of source address in NVRAM_SCRATCH[1] */ +_local1: + bl NVRAM_INIT + tst r0, #NVRAM_SR_RDY + bne _local1 + mov r0, #20 + bl DELAY9 + mov r2, r4 /* length */ + mov r1, r5 /* dest address */ + mov r0, #2 /* bytes to transfer in command */ + bl NVRAM_XFER2 + ldmia sp!, {r4, r5, lr} + bx lr +.endfunc + +.func NVRAM_Xfer2, NVRAM_XFER2 +NVRAM_XFER2: + stmdb sp!, {r4, r5, r6, lr} + ldr r4, =SP_BASE + mov r3, #0 + cmp r0, #0 + bls _local2 + ldr r5, =NVRAM_SCRATCH +_local4: + ldrb r6, [r5, r3] + str r6, [r4, #SP_TDR] +_local3: + ldr r6, [r4, #SP_SR] + tst r6, #SP_TDRE + beq _local3 + add r3, r3, #1 + cmp r3, r0 /* r0 is # of bytes to send out (command+addr) */ + blo _local4 +_local2: + mov r3, #SPI_8CLOCKS + str r3, [r4, #SP_TDR] + ldr r0, [r4, #SP_RDR] +_local5: + ldr r0, [r4, #SP_SR] + tst r0, #SP_RDRF + beq _local5 + ldr r0, [r4, #SP_RDR] /* what's this byte? It's the byte read while writing the TDR -- nonsense, because the NVRAM doesn't read and write at the same time */ + mov r0, #0 + cmp r2, #0 /* r2 is # of bytes to copy in */ + bls _local6 +_local7: + ldr r5, [r4, #SP_SR] + tst r5, #SP_TDRE + beq _local7 + str r3, [r4, #SP_TDR] /* r3 has SPI_8CLOCKS */ +_local8: + ldr r5, [r4, #SP_SR] + tst r5, #SP_RDRF + beq _local8 + ldr r5, [r4, #SP_RDR] /* but didn't we read this byte above? */ + strb r5, [r1], #1 /* postindexed */ + add r0, r0, #1 + cmp r0, r2 + blo _local7 /* since we don't send another address, the NVRAM must be capable of sequential reads */ +_local6: + mov r0, #200 + bl DELAY9 + ldmia sp!, {r4, r5, r6, lr} + bx lr +#endif diff --git a/drivers/net/wireless/atmel/atmel.h b/drivers/net/wireless/atmel/atmel.h new file mode 100644 index 000000000000..96f7318cbb04 --- /dev/null +++ b/drivers/net/wireless/atmel/atmel.h @@ -0,0 +1,43 @@ +/*** -*- linux-c -*- ********************************************************** + + Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. + + Copyright 2005 Dan Williams and Red Hat, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Atmel wireless lan drivers; if not, see + . + +******************************************************************************/ + +#ifndef _ATMEL_H +#define _ATMEL_H + +typedef enum { + ATMEL_FW_TYPE_NONE = 0, + ATMEL_FW_TYPE_502, + ATMEL_FW_TYPE_502D, + ATMEL_FW_TYPE_502E, + ATMEL_FW_TYPE_502_3COM, + ATMEL_FW_TYPE_504, + ATMEL_FW_TYPE_504_2958, + ATMEL_FW_TYPE_504A_2958, + ATMEL_FW_TYPE_506 +} AtmelFWType; + +struct net_device *init_atmel_card(unsigned short, unsigned long, const AtmelFWType, struct device *, + int (*present_func)(void *), void * ); +void stop_atmel_card( struct net_device *); +int atmel_open( struct net_device * ); + +#endif diff --git a/drivers/net/wireless/atmel/atmel_cs.c b/drivers/net/wireless/atmel/atmel_cs.c new file mode 100644 index 000000000000..7afc9c5329fb --- /dev/null +++ b/drivers/net/wireless/atmel/atmel_cs.c @@ -0,0 +1,286 @@ +/*** -*- linux-c -*- ********************************************************** + + Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. + + Copyright 2000-2001 ATMEL Corporation. + Copyright 2003 Simon Kelley. + + This code was developed from version 2.1.1 of the Atmel drivers, + released by Atmel corp. under the GPL in December 2002. It also + includes code from the Linux aironet drivers (C) Benjamin Reed, + and the Linux PCMCIA package, (C) David Hinds. + + For all queries about this code, please contact the current author, + Simon Kelley and not Atmel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Atmel wireless lan drivers; if not, see + . + +******************************************************************************/ + +#ifdef __IN_PCMCIA_PACKAGE__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "atmel.h" + + +/*====================================================================*/ + +MODULE_AUTHOR("Simon Kelley"); +MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("Atmel at76c50x PCMCIA cards"); + +/*====================================================================*/ + +static int atmel_config(struct pcmcia_device *link); +static void atmel_release(struct pcmcia_device *link); + +static void atmel_detach(struct pcmcia_device *p_dev); + +struct local_info { + struct net_device *eth_dev; +}; + +static int atmel_probe(struct pcmcia_device *p_dev) +{ + struct local_info *local; + + dev_dbg(&p_dev->dev, "atmel_attach()\n"); + + /* Allocate space for private device-specific data */ + local = kzalloc(sizeof(*local), GFP_KERNEL); + if (!local) + return -ENOMEM; + + p_dev->priv = local; + + return atmel_config(p_dev); +} /* atmel_attach */ + +static void atmel_detach(struct pcmcia_device *link) +{ + dev_dbg(&link->dev, "atmel_detach\n"); + + atmel_release(link); + + kfree(link->priv); +} + +/* Call-back function to interrogate PCMCIA-specific information + about the current existence of the card */ +static int card_present(void *arg) +{ + struct pcmcia_device *link = (struct pcmcia_device *)arg; + + if (pcmcia_dev_present(link)) + return 1; + + return 0; +} + +static int atmel_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + if (p_dev->config_index == 0) + return -EINVAL; + + return pcmcia_request_io(p_dev); +} + +static int atmel_config(struct pcmcia_device *link) +{ + struct local_info *dev; + int ret; + const struct pcmcia_device_id *did; + + dev = link->priv; + did = dev_get_drvdata(&link->dev); + + dev_dbg(&link->dev, "atmel_config\n"); + + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | + CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; + + if (pcmcia_loop_config(link, atmel_config_check, NULL)) + goto failed; + + if (!link->irq) { + dev_err(&link->dev, "atmel: cannot assign IRQ: check that CONFIG_ISA is set in kernel config."); + goto failed; + } + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + ((struct local_info *)link->priv)->eth_dev = + init_atmel_card(link->irq, + link->resource[0]->start, + did ? did->driver_info : ATMEL_FW_TYPE_NONE, + &link->dev, + card_present, + link); + if (!((struct local_info *)link->priv)->eth_dev) + goto failed; + + + return 0; + + failed: + atmel_release(link); + return -ENODEV; +} + +static void atmel_release(struct pcmcia_device *link) +{ + struct net_device *dev = ((struct local_info *)link->priv)->eth_dev; + + dev_dbg(&link->dev, "atmel_release\n"); + + if (dev) + stop_atmel_card(dev); + ((struct local_info *)link->priv)->eth_dev = NULL; + + pcmcia_disable_device(link); +} + +static int atmel_suspend(struct pcmcia_device *link) +{ + struct local_info *local = link->priv; + + netif_device_detach(local->eth_dev); + + return 0; +} + +static int atmel_resume(struct pcmcia_device *link) +{ + struct local_info *local = link->priv; + + atmel_open(local->eth_dev); + netif_device_attach(local->eth_dev); + + return 0; +} + +/*====================================================================*/ +/* We use the driver_info field to store the correct firmware type for a card. */ + +#define PCMCIA_DEVICE_MANF_CARD_INFO(manf, card, info) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \ + PCMCIA_DEV_ID_MATCH_CARD_ID, \ + .manf_id = (manf), \ + .card_id = (card), \ + .driver_info = (kernel_ulong_t)(info), } + +#define PCMCIA_DEVICE_PROD_ID12_INFO(v1, v2, vh1, vh2, info) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2, \ + .prod_id = { (v1), (v2), NULL, NULL }, \ + .prod_id_hash = { (vh1), (vh2), 0, 0 }, \ + .driver_info = (kernel_ulong_t)(info), } + +static const struct pcmcia_device_id atmel_ids[] = { + PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0620, ATMEL_FW_TYPE_502_3COM), + PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0696, ATMEL_FW_TYPE_502_3COM), + PCMCIA_DEVICE_MANF_CARD_INFO(0x01bf, 0x3302, ATMEL_FW_TYPE_502E), + PCMCIA_DEVICE_MANF_CARD_INFO(0xd601, 0x0007, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("11WAVE", "11WP611AL-E", 0x9eb2da1f, 0xc9a0d3f9, ATMEL_FW_TYPE_502E), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR", 0xabda4164, 0x41b37e1f, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_D", 0xabda4164, 0x3675d704, ATMEL_FW_TYPE_502D), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_E", 0xabda4164, 0x4172e792, ATMEL_FW_TYPE_502E), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504_R", 0xabda4164, 0x917f3d72, ATMEL_FW_TYPE_504_2958), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504", 0xabda4164, 0x5040670a, ATMEL_FW_TYPE_504), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504A", 0xabda4164, 0xe15ed87f, ATMEL_FW_TYPE_504A_2958), + PCMCIA_DEVICE_PROD_ID12_INFO("BT", "Voyager 1020 Laptop Adapter", 0xae49b86a, 0x1e957cd5, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("CNet", "CNWLC 11Mbps Wireless PC Card V-5", 0xbc477dde, 0x502fae6b, ATMEL_FW_TYPE_502E), + PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN PC Card", 0x5b878724, 0x122f1df6, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN Card S", 0x5b878724, 0x5fba533a, ATMEL_FW_TYPE_504_2958), + PCMCIA_DEVICE_PROD_ID12_INFO("OEM", "11Mbps Wireless LAN PC Card V-3", 0xfea54c90, 0x1c5b0f68, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W", 0xc4f8b18b, 0x30f38774, ATMEL_FW_TYPE_502D), + PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W-V2", 0xc4f8b18b, 0x172d1377, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("Wireless", "PC_CARD", 0xa407ecdd, 0x119f6314, ATMEL_FW_TYPE_502D), + PCMCIA_DEVICE_PROD_ID12_INFO("WLAN", "802.11b PC CARD", 0x575c516c, 0xb1f6dbc4, ATMEL_FW_TYPE_502D), + PCMCIA_DEVICE_PROD_ID12_INFO("LG", "LW2100N", 0xb474d43a, 0x6b1fec94, ATMEL_FW_TYPE_502E), + PCMCIA_DEVICE_NULL +}; + +MODULE_DEVICE_TABLE(pcmcia, atmel_ids); + +static struct pcmcia_driver atmel_driver = { + .owner = THIS_MODULE, + .name = "atmel_cs", + .probe = atmel_probe, + .remove = atmel_detach, + .id_table = atmel_ids, + .suspend = atmel_suspend, + .resume = atmel_resume, +}; +module_pcmcia_driver(atmel_driver); + +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + In addition: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ diff --git a/drivers/net/wireless/atmel/atmel_pci.c b/drivers/net/wireless/atmel/atmel_pci.c new file mode 100644 index 000000000000..bcf1f274a251 --- /dev/null +++ b/drivers/net/wireless/atmel/atmel_pci.c @@ -0,0 +1,76 @@ +/*** -*- linux-c -*- ********************************************************** + + Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. + + Copyright 2004 Simon Kelley. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Atmel wireless lan drivers; if not, see + . + +******************************************************************************/ +#include +#include +#include +#include +#include "atmel.h" + +MODULE_AUTHOR("Simon Kelley"); +MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("Atmel at76c506 PCI wireless cards"); + +static const struct pci_device_id card_ids[] = { + { 0x1114, 0x0506, PCI_ANY_ID, PCI_ANY_ID }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, card_ids); + +static int atmel_pci_probe(struct pci_dev *, const struct pci_device_id *); +static void atmel_pci_remove(struct pci_dev *); + +static struct pci_driver atmel_driver = { + .name = "atmel", + .id_table = card_ids, + .probe = atmel_pci_probe, + .remove = atmel_pci_remove, +}; + + +static int atmel_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *pent) +{ + struct net_device *dev; + + if (pci_enable_device(pdev)) + return -ENODEV; + + pci_set_master(pdev); + + dev = init_atmel_card(pdev->irq, pdev->resource[1].start, + ATMEL_FW_TYPE_506, + &pdev->dev, NULL, NULL); + if (!dev) + return -ENODEV; + + pci_set_drvdata(pdev, dev); + return 0; +} + +static void atmel_pci_remove(struct pci_dev *pdev) +{ + stop_atmel_card(pci_get_drvdata(pdev)); +} + +module_pci_driver(atmel_driver); diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c deleted file mode 100644 index 7afc9c5329fb..000000000000 --- a/drivers/net/wireless/atmel_cs.c +++ /dev/null @@ -1,286 +0,0 @@ -/*** -*- linux-c -*- ********************************************************** - - Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. - - Copyright 2000-2001 ATMEL Corporation. - Copyright 2003 Simon Kelley. - - This code was developed from version 2.1.1 of the Atmel drivers, - released by Atmel corp. under the GPL in December 2002. It also - includes code from the Linux aironet drivers (C) Benjamin Reed, - and the Linux PCMCIA package, (C) David Hinds. - - For all queries about this code, please contact the current author, - Simon Kelley and not Atmel Corporation. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This software is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, see - . - -******************************************************************************/ - -#ifdef __IN_PCMCIA_PACKAGE__ -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "atmel.h" - - -/*====================================================================*/ - -MODULE_AUTHOR("Simon Kelley"); -MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("Atmel at76c50x PCMCIA cards"); - -/*====================================================================*/ - -static int atmel_config(struct pcmcia_device *link); -static void atmel_release(struct pcmcia_device *link); - -static void atmel_detach(struct pcmcia_device *p_dev); - -struct local_info { - struct net_device *eth_dev; -}; - -static int atmel_probe(struct pcmcia_device *p_dev) -{ - struct local_info *local; - - dev_dbg(&p_dev->dev, "atmel_attach()\n"); - - /* Allocate space for private device-specific data */ - local = kzalloc(sizeof(*local), GFP_KERNEL); - if (!local) - return -ENOMEM; - - p_dev->priv = local; - - return atmel_config(p_dev); -} /* atmel_attach */ - -static void atmel_detach(struct pcmcia_device *link) -{ - dev_dbg(&link->dev, "atmel_detach\n"); - - atmel_release(link); - - kfree(link->priv); -} - -/* Call-back function to interrogate PCMCIA-specific information - about the current existence of the card */ -static int card_present(void *arg) -{ - struct pcmcia_device *link = (struct pcmcia_device *)arg; - - if (pcmcia_dev_present(link)) - return 1; - - return 0; -} - -static int atmel_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - if (p_dev->config_index == 0) - return -EINVAL; - - return pcmcia_request_io(p_dev); -} - -static int atmel_config(struct pcmcia_device *link) -{ - struct local_info *dev; - int ret; - const struct pcmcia_device_id *did; - - dev = link->priv; - did = dev_get_drvdata(&link->dev); - - dev_dbg(&link->dev, "atmel_config\n"); - - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | - CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; - - if (pcmcia_loop_config(link, atmel_config_check, NULL)) - goto failed; - - if (!link->irq) { - dev_err(&link->dev, "atmel: cannot assign IRQ: check that CONFIG_ISA is set in kernel config."); - goto failed; - } - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - ((struct local_info *)link->priv)->eth_dev = - init_atmel_card(link->irq, - link->resource[0]->start, - did ? did->driver_info : ATMEL_FW_TYPE_NONE, - &link->dev, - card_present, - link); - if (!((struct local_info *)link->priv)->eth_dev) - goto failed; - - - return 0; - - failed: - atmel_release(link); - return -ENODEV; -} - -static void atmel_release(struct pcmcia_device *link) -{ - struct net_device *dev = ((struct local_info *)link->priv)->eth_dev; - - dev_dbg(&link->dev, "atmel_release\n"); - - if (dev) - stop_atmel_card(dev); - ((struct local_info *)link->priv)->eth_dev = NULL; - - pcmcia_disable_device(link); -} - -static int atmel_suspend(struct pcmcia_device *link) -{ - struct local_info *local = link->priv; - - netif_device_detach(local->eth_dev); - - return 0; -} - -static int atmel_resume(struct pcmcia_device *link) -{ - struct local_info *local = link->priv; - - atmel_open(local->eth_dev); - netif_device_attach(local->eth_dev); - - return 0; -} - -/*====================================================================*/ -/* We use the driver_info field to store the correct firmware type for a card. */ - -#define PCMCIA_DEVICE_MANF_CARD_INFO(manf, card, info) { \ - .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \ - PCMCIA_DEV_ID_MATCH_CARD_ID, \ - .manf_id = (manf), \ - .card_id = (card), \ - .driver_info = (kernel_ulong_t)(info), } - -#define PCMCIA_DEVICE_PROD_ID12_INFO(v1, v2, vh1, vh2, info) { \ - .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ - PCMCIA_DEV_ID_MATCH_PROD_ID2, \ - .prod_id = { (v1), (v2), NULL, NULL }, \ - .prod_id_hash = { (vh1), (vh2), 0, 0 }, \ - .driver_info = (kernel_ulong_t)(info), } - -static const struct pcmcia_device_id atmel_ids[] = { - PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0620, ATMEL_FW_TYPE_502_3COM), - PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0696, ATMEL_FW_TYPE_502_3COM), - PCMCIA_DEVICE_MANF_CARD_INFO(0x01bf, 0x3302, ATMEL_FW_TYPE_502E), - PCMCIA_DEVICE_MANF_CARD_INFO(0xd601, 0x0007, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("11WAVE", "11WP611AL-E", 0x9eb2da1f, 0xc9a0d3f9, ATMEL_FW_TYPE_502E), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR", 0xabda4164, 0x41b37e1f, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_D", 0xabda4164, 0x3675d704, ATMEL_FW_TYPE_502D), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_E", 0xabda4164, 0x4172e792, ATMEL_FW_TYPE_502E), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504_R", 0xabda4164, 0x917f3d72, ATMEL_FW_TYPE_504_2958), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504", 0xabda4164, 0x5040670a, ATMEL_FW_TYPE_504), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504A", 0xabda4164, 0xe15ed87f, ATMEL_FW_TYPE_504A_2958), - PCMCIA_DEVICE_PROD_ID12_INFO("BT", "Voyager 1020 Laptop Adapter", 0xae49b86a, 0x1e957cd5, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("CNet", "CNWLC 11Mbps Wireless PC Card V-5", 0xbc477dde, 0x502fae6b, ATMEL_FW_TYPE_502E), - PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN PC Card", 0x5b878724, 0x122f1df6, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN Card S", 0x5b878724, 0x5fba533a, ATMEL_FW_TYPE_504_2958), - PCMCIA_DEVICE_PROD_ID12_INFO("OEM", "11Mbps Wireless LAN PC Card V-3", 0xfea54c90, 0x1c5b0f68, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W", 0xc4f8b18b, 0x30f38774, ATMEL_FW_TYPE_502D), - PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W-V2", 0xc4f8b18b, 0x172d1377, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("Wireless", "PC_CARD", 0xa407ecdd, 0x119f6314, ATMEL_FW_TYPE_502D), - PCMCIA_DEVICE_PROD_ID12_INFO("WLAN", "802.11b PC CARD", 0x575c516c, 0xb1f6dbc4, ATMEL_FW_TYPE_502D), - PCMCIA_DEVICE_PROD_ID12_INFO("LG", "LW2100N", 0xb474d43a, 0x6b1fec94, ATMEL_FW_TYPE_502E), - PCMCIA_DEVICE_NULL -}; - -MODULE_DEVICE_TABLE(pcmcia, atmel_ids); - -static struct pcmcia_driver atmel_driver = { - .owner = THIS_MODULE, - .name = "atmel_cs", - .probe = atmel_probe, - .remove = atmel_detach, - .id_table = atmel_ids, - .suspend = atmel_suspend, - .resume = atmel_resume, -}; -module_pcmcia_driver(atmel_driver); - -/* - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - In addition: - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ diff --git a/drivers/net/wireless/atmel_pci.c b/drivers/net/wireless/atmel_pci.c deleted file mode 100644 index bcf1f274a251..000000000000 --- a/drivers/net/wireless/atmel_pci.c +++ /dev/null @@ -1,76 +0,0 @@ -/*** -*- linux-c -*- ********************************************************** - - Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. - - Copyright 2004 Simon Kelley. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This software is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, see - . - -******************************************************************************/ -#include -#include -#include -#include -#include "atmel.h" - -MODULE_AUTHOR("Simon Kelley"); -MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("Atmel at76c506 PCI wireless cards"); - -static const struct pci_device_id card_ids[] = { - { 0x1114, 0x0506, PCI_ANY_ID, PCI_ANY_ID }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, card_ids); - -static int atmel_pci_probe(struct pci_dev *, const struct pci_device_id *); -static void atmel_pci_remove(struct pci_dev *); - -static struct pci_driver atmel_driver = { - .name = "atmel", - .id_table = card_ids, - .probe = atmel_pci_probe, - .remove = atmel_pci_remove, -}; - - -static int atmel_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *pent) -{ - struct net_device *dev; - - if (pci_enable_device(pdev)) - return -ENODEV; - - pci_set_master(pdev); - - dev = init_atmel_card(pdev->irq, pdev->resource[1].start, - ATMEL_FW_TYPE_506, - &pdev->dev, NULL, NULL); - if (!dev) - return -ENODEV; - - pci_set_drvdata(pdev, dev); - return 0; -} - -static void atmel_pci_remove(struct pci_dev *pdev) -{ - stop_atmel_card(pci_get_drvdata(pdev)); -} - -module_pci_driver(atmel_driver); -- cgit v1.2.3-59-g8ed1b From 58619b14d106e453e3924810fac16a8644b04db1 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 19:49:23 +0200 Subject: b43: move under broadcom vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 2 +- drivers/net/wireless/Makefile | 2 +- drivers/net/wireless/b43/Kconfig | 187 - drivers/net/wireless/b43/Makefile | 27 - drivers/net/wireless/b43/b43.h | 1108 -- drivers/net/wireless/b43/bus.c | 265 - drivers/net/wireless/b43/bus.h | 95 - drivers/net/wireless/b43/debugfs.c | 826 -- drivers/net/wireless/b43/debugfs.h | 111 - drivers/net/wireless/b43/dma.c | 1831 ---- drivers/net/wireless/b43/dma.h | 305 - drivers/net/wireless/b43/leds.c | 359 - drivers/net/wireless/b43/leds.h | 94 - drivers/net/wireless/b43/lo.c | 1016 -- drivers/net/wireless/b43/lo.h | 87 - drivers/net/wireless/b43/main.c | 5895 ----------- drivers/net/wireless/b43/main.h | 112 - drivers/net/wireless/b43/phy_a.c | 595 -- drivers/net/wireless/b43/phy_a.h | 126 - drivers/net/wireless/b43/phy_ac.c | 92 - drivers/net/wireless/b43/phy_ac.h | 38 - drivers/net/wireless/b43/phy_common.c | 653 -- drivers/net/wireless/b43/phy_common.h | 457 - drivers/net/wireless/b43/phy_g.c | 3055 ------ drivers/net/wireless/b43/phy_g.h | 208 - drivers/net/wireless/b43/phy_ht.c | 1153 --- drivers/net/wireless/b43/phy_ht.h | 141 - drivers/net/wireless/b43/phy_lcn.c | 855 -- drivers/net/wireless/b43/phy_lcn.h | 31 - drivers/net/wireless/b43/phy_lp.c | 2716 ----- drivers/net/wireless/b43/phy_lp.h | 912 -- drivers/net/wireless/b43/phy_n.c | 6726 ------------ drivers/net/wireless/b43/phy_n.h | 1007 -- drivers/net/wireless/b43/pio.c | 834 -- drivers/net/wireless/b43/pio.h | 165 - drivers/net/wireless/b43/ppr.c | 199 - drivers/net/wireless/b43/ppr.h | 45 - drivers/net/wireless/b43/radio_2055.c | 1335 --- drivers/net/wireless/b43/radio_2055.h | 259 - drivers/net/wireless/b43/radio_2056.c | 10318 ------------------- drivers/net/wireless/b43/radio_2056.h | 1100 -- drivers/net/wireless/b43/radio_2057.c | 637 -- drivers/net/wireless/b43/radio_2057.h | 506 - drivers/net/wireless/b43/radio_2059.c | 364 - drivers/net/wireless/b43/radio_2059.h | 60 - drivers/net/wireless/b43/rfkill.c | 70 - drivers/net/wireless/b43/rfkill.h | 11 - drivers/net/wireless/b43/sdio.c | 207 - drivers/net/wireless/b43/sdio.h | 45 - drivers/net/wireless/b43/sysfs.c | 155 - drivers/net/wireless/b43/sysfs.h | 9 - drivers/net/wireless/b43/tables.c | 466 - drivers/net/wireless/b43/tables.h | 34 - drivers/net/wireless/b43/tables_lpphy.c | 2456 ----- drivers/net/wireless/b43/tables_lpphy.h | 44 - drivers/net/wireless/b43/tables_nphy.c | 3878 ------- drivers/net/wireless/b43/tables_nphy.h | 222 - drivers/net/wireless/b43/tables_phy_ht.c | 836 -- drivers/net/wireless/b43/tables_phy_ht.h | 26 - drivers/net/wireless/b43/tables_phy_lcn.c | 724 -- drivers/net/wireless/b43/tables_phy_lcn.h | 24 - drivers/net/wireless/b43/wa.c | 634 -- drivers/net/wireless/b43/wa.h | 7 - drivers/net/wireless/b43/xmit.c | 947 -- drivers/net/wireless/b43/xmit.h | 416 - drivers/net/wireless/broadcom/Kconfig | 16 + drivers/net/wireless/broadcom/Makefile | 1 + drivers/net/wireless/broadcom/b43/Kconfig | 187 + drivers/net/wireless/broadcom/b43/Makefile | 27 + drivers/net/wireless/broadcom/b43/b43.h | 1108 ++ drivers/net/wireless/broadcom/b43/bus.c | 265 + drivers/net/wireless/broadcom/b43/bus.h | 95 + drivers/net/wireless/broadcom/b43/debugfs.c | 826 ++ drivers/net/wireless/broadcom/b43/debugfs.h | 111 + drivers/net/wireless/broadcom/b43/dma.c | 1831 ++++ drivers/net/wireless/broadcom/b43/dma.h | 305 + drivers/net/wireless/broadcom/b43/leds.c | 359 + drivers/net/wireless/broadcom/b43/leds.h | 94 + drivers/net/wireless/broadcom/b43/lo.c | 1016 ++ drivers/net/wireless/broadcom/b43/lo.h | 87 + drivers/net/wireless/broadcom/b43/main.c | 5895 +++++++++++ drivers/net/wireless/broadcom/b43/main.h | 112 + drivers/net/wireless/broadcom/b43/phy_a.c | 595 ++ drivers/net/wireless/broadcom/b43/phy_a.h | 126 + drivers/net/wireless/broadcom/b43/phy_ac.c | 92 + drivers/net/wireless/broadcom/b43/phy_ac.h | 38 + drivers/net/wireless/broadcom/b43/phy_common.c | 653 ++ drivers/net/wireless/broadcom/b43/phy_common.h | 457 + drivers/net/wireless/broadcom/b43/phy_g.c | 3055 ++++++ drivers/net/wireless/broadcom/b43/phy_g.h | 208 + drivers/net/wireless/broadcom/b43/phy_ht.c | 1153 +++ drivers/net/wireless/broadcom/b43/phy_ht.h | 141 + drivers/net/wireless/broadcom/b43/phy_lcn.c | 855 ++ drivers/net/wireless/broadcom/b43/phy_lcn.h | 31 + drivers/net/wireless/broadcom/b43/phy_lp.c | 2716 +++++ drivers/net/wireless/broadcom/b43/phy_lp.h | 912 ++ drivers/net/wireless/broadcom/b43/phy_n.c | 6726 ++++++++++++ drivers/net/wireless/broadcom/b43/phy_n.h | 1007 ++ drivers/net/wireless/broadcom/b43/pio.c | 834 ++ drivers/net/wireless/broadcom/b43/pio.h | 165 + drivers/net/wireless/broadcom/b43/ppr.c | 199 + drivers/net/wireless/broadcom/b43/ppr.h | 45 + drivers/net/wireless/broadcom/b43/radio_2055.c | 1335 +++ drivers/net/wireless/broadcom/b43/radio_2055.h | 259 + drivers/net/wireless/broadcom/b43/radio_2056.c | 10318 +++++++++++++++++++ drivers/net/wireless/broadcom/b43/radio_2056.h | 1100 ++ drivers/net/wireless/broadcom/b43/radio_2057.c | 637 ++ drivers/net/wireless/broadcom/b43/radio_2057.h | 506 + drivers/net/wireless/broadcom/b43/radio_2059.c | 364 + drivers/net/wireless/broadcom/b43/radio_2059.h | 60 + drivers/net/wireless/broadcom/b43/rfkill.c | 70 + drivers/net/wireless/broadcom/b43/rfkill.h | 11 + drivers/net/wireless/broadcom/b43/sdio.c | 207 + drivers/net/wireless/broadcom/b43/sdio.h | 45 + drivers/net/wireless/broadcom/b43/sysfs.c | 155 + drivers/net/wireless/broadcom/b43/sysfs.h | 9 + drivers/net/wireless/broadcom/b43/tables.c | 466 + drivers/net/wireless/broadcom/b43/tables.h | 34 + drivers/net/wireless/broadcom/b43/tables_lpphy.c | 2456 +++++ drivers/net/wireless/broadcom/b43/tables_lpphy.h | 44 + drivers/net/wireless/broadcom/b43/tables_nphy.c | 3878 +++++++ drivers/net/wireless/broadcom/b43/tables_nphy.h | 222 + drivers/net/wireless/broadcom/b43/tables_phy_ht.c | 836 ++ drivers/net/wireless/broadcom/b43/tables_phy_ht.h | 26 + drivers/net/wireless/broadcom/b43/tables_phy_lcn.c | 724 ++ drivers/net/wireless/broadcom/b43/tables_phy_lcn.h | 24 + drivers/net/wireless/broadcom/b43/wa.c | 634 ++ drivers/net/wireless/broadcom/b43/wa.h | 7 + drivers/net/wireless/broadcom/b43/xmit.c | 947 ++ drivers/net/wireless/broadcom/b43/xmit.h | 416 + 131 files changed, 58136 insertions(+), 58119 deletions(-) delete mode 100644 drivers/net/wireless/b43/Kconfig delete mode 100644 drivers/net/wireless/b43/Makefile delete mode 100644 drivers/net/wireless/b43/b43.h delete mode 100644 drivers/net/wireless/b43/bus.c delete mode 100644 drivers/net/wireless/b43/bus.h delete mode 100644 drivers/net/wireless/b43/debugfs.c delete mode 100644 drivers/net/wireless/b43/debugfs.h delete mode 100644 drivers/net/wireless/b43/dma.c delete mode 100644 drivers/net/wireless/b43/dma.h delete mode 100644 drivers/net/wireless/b43/leds.c delete mode 100644 drivers/net/wireless/b43/leds.h delete mode 100644 drivers/net/wireless/b43/lo.c delete mode 100644 drivers/net/wireless/b43/lo.h delete mode 100644 drivers/net/wireless/b43/main.c delete mode 100644 drivers/net/wireless/b43/main.h delete mode 100644 drivers/net/wireless/b43/phy_a.c delete mode 100644 drivers/net/wireless/b43/phy_a.h delete mode 100644 drivers/net/wireless/b43/phy_ac.c delete mode 100644 drivers/net/wireless/b43/phy_ac.h delete mode 100644 drivers/net/wireless/b43/phy_common.c delete mode 100644 drivers/net/wireless/b43/phy_common.h delete mode 100644 drivers/net/wireless/b43/phy_g.c delete mode 100644 drivers/net/wireless/b43/phy_g.h delete mode 100644 drivers/net/wireless/b43/phy_ht.c delete mode 100644 drivers/net/wireless/b43/phy_ht.h delete mode 100644 drivers/net/wireless/b43/phy_lcn.c delete mode 100644 drivers/net/wireless/b43/phy_lcn.h delete mode 100644 drivers/net/wireless/b43/phy_lp.c delete mode 100644 drivers/net/wireless/b43/phy_lp.h delete mode 100644 drivers/net/wireless/b43/phy_n.c delete mode 100644 drivers/net/wireless/b43/phy_n.h delete mode 100644 drivers/net/wireless/b43/pio.c delete mode 100644 drivers/net/wireless/b43/pio.h delete mode 100644 drivers/net/wireless/b43/ppr.c delete mode 100644 drivers/net/wireless/b43/ppr.h delete mode 100644 drivers/net/wireless/b43/radio_2055.c delete mode 100644 drivers/net/wireless/b43/radio_2055.h delete mode 100644 drivers/net/wireless/b43/radio_2056.c delete mode 100644 drivers/net/wireless/b43/radio_2056.h delete mode 100644 drivers/net/wireless/b43/radio_2057.c delete mode 100644 drivers/net/wireless/b43/radio_2057.h delete mode 100644 drivers/net/wireless/b43/radio_2059.c delete mode 100644 drivers/net/wireless/b43/radio_2059.h delete mode 100644 drivers/net/wireless/b43/rfkill.c delete mode 100644 drivers/net/wireless/b43/rfkill.h delete mode 100644 drivers/net/wireless/b43/sdio.c delete mode 100644 drivers/net/wireless/b43/sdio.h delete mode 100644 drivers/net/wireless/b43/sysfs.c delete mode 100644 drivers/net/wireless/b43/sysfs.h delete mode 100644 drivers/net/wireless/b43/tables.c delete mode 100644 drivers/net/wireless/b43/tables.h delete mode 100644 drivers/net/wireless/b43/tables_lpphy.c delete mode 100644 drivers/net/wireless/b43/tables_lpphy.h delete mode 100644 drivers/net/wireless/b43/tables_nphy.c delete mode 100644 drivers/net/wireless/b43/tables_nphy.h delete mode 100644 drivers/net/wireless/b43/tables_phy_ht.c delete mode 100644 drivers/net/wireless/b43/tables_phy_ht.h delete mode 100644 drivers/net/wireless/b43/tables_phy_lcn.c delete mode 100644 drivers/net/wireless/b43/tables_phy_lcn.h delete mode 100644 drivers/net/wireless/b43/wa.c delete mode 100644 drivers/net/wireless/b43/wa.h delete mode 100644 drivers/net/wireless/b43/xmit.c delete mode 100644 drivers/net/wireless/b43/xmit.h create mode 100644 drivers/net/wireless/broadcom/Kconfig create mode 100644 drivers/net/wireless/broadcom/Makefile create mode 100644 drivers/net/wireless/broadcom/b43/Kconfig create mode 100644 drivers/net/wireless/broadcom/b43/Makefile create mode 100644 drivers/net/wireless/broadcom/b43/b43.h create mode 100644 drivers/net/wireless/broadcom/b43/bus.c create mode 100644 drivers/net/wireless/broadcom/b43/bus.h create mode 100644 drivers/net/wireless/broadcom/b43/debugfs.c create mode 100644 drivers/net/wireless/broadcom/b43/debugfs.h create mode 100644 drivers/net/wireless/broadcom/b43/dma.c create mode 100644 drivers/net/wireless/broadcom/b43/dma.h create mode 100644 drivers/net/wireless/broadcom/b43/leds.c create mode 100644 drivers/net/wireless/broadcom/b43/leds.h create mode 100644 drivers/net/wireless/broadcom/b43/lo.c create mode 100644 drivers/net/wireless/broadcom/b43/lo.h create mode 100644 drivers/net/wireless/broadcom/b43/main.c create mode 100644 drivers/net/wireless/broadcom/b43/main.h create mode 100644 drivers/net/wireless/broadcom/b43/phy_a.c create mode 100644 drivers/net/wireless/broadcom/b43/phy_a.h create mode 100644 drivers/net/wireless/broadcom/b43/phy_ac.c create mode 100644 drivers/net/wireless/broadcom/b43/phy_ac.h create mode 100644 drivers/net/wireless/broadcom/b43/phy_common.c create mode 100644 drivers/net/wireless/broadcom/b43/phy_common.h create mode 100644 drivers/net/wireless/broadcom/b43/phy_g.c create mode 100644 drivers/net/wireless/broadcom/b43/phy_g.h create mode 100644 drivers/net/wireless/broadcom/b43/phy_ht.c create mode 100644 drivers/net/wireless/broadcom/b43/phy_ht.h create mode 100644 drivers/net/wireless/broadcom/b43/phy_lcn.c create mode 100644 drivers/net/wireless/broadcom/b43/phy_lcn.h create mode 100644 drivers/net/wireless/broadcom/b43/phy_lp.c create mode 100644 drivers/net/wireless/broadcom/b43/phy_lp.h create mode 100644 drivers/net/wireless/broadcom/b43/phy_n.c create mode 100644 drivers/net/wireless/broadcom/b43/phy_n.h create mode 100644 drivers/net/wireless/broadcom/b43/pio.c create mode 100644 drivers/net/wireless/broadcom/b43/pio.h create mode 100644 drivers/net/wireless/broadcom/b43/ppr.c create mode 100644 drivers/net/wireless/broadcom/b43/ppr.h create mode 100644 drivers/net/wireless/broadcom/b43/radio_2055.c create mode 100644 drivers/net/wireless/broadcom/b43/radio_2055.h create mode 100644 drivers/net/wireless/broadcom/b43/radio_2056.c create mode 100644 drivers/net/wireless/broadcom/b43/radio_2056.h create mode 100644 drivers/net/wireless/broadcom/b43/radio_2057.c create mode 100644 drivers/net/wireless/broadcom/b43/radio_2057.h create mode 100644 drivers/net/wireless/broadcom/b43/radio_2059.c create mode 100644 drivers/net/wireless/broadcom/b43/radio_2059.h create mode 100644 drivers/net/wireless/broadcom/b43/rfkill.c create mode 100644 drivers/net/wireless/broadcom/b43/rfkill.h create mode 100644 drivers/net/wireless/broadcom/b43/sdio.c create mode 100644 drivers/net/wireless/broadcom/b43/sdio.h create mode 100644 drivers/net/wireless/broadcom/b43/sysfs.c create mode 100644 drivers/net/wireless/broadcom/b43/sysfs.h create mode 100644 drivers/net/wireless/broadcom/b43/tables.c create mode 100644 drivers/net/wireless/broadcom/b43/tables.h create mode 100644 drivers/net/wireless/broadcom/b43/tables_lpphy.c create mode 100644 drivers/net/wireless/broadcom/b43/tables_lpphy.h create mode 100644 drivers/net/wireless/broadcom/b43/tables_nphy.c create mode 100644 drivers/net/wireless/broadcom/b43/tables_nphy.h create mode 100644 drivers/net/wireless/broadcom/b43/tables_phy_ht.c create mode 100644 drivers/net/wireless/broadcom/b43/tables_phy_ht.h create mode 100644 drivers/net/wireless/broadcom/b43/tables_phy_lcn.c create mode 100644 drivers/net/wireless/broadcom/b43/tables_phy_lcn.h create mode 100644 drivers/net/wireless/broadcom/b43/wa.c create mode 100644 drivers/net/wireless/broadcom/b43/wa.h create mode 100644 drivers/net/wireless/broadcom/b43/xmit.c create mode 100644 drivers/net/wireless/broadcom/b43/xmit.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 2f3ea01c101c..68793777fdb5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2081,7 +2081,7 @@ L: linux-wireless@vger.kernel.org L: b43-dev@lists.infradead.org W: http://wireless.kernel.org/en/users/Drivers/b43 S: Odd Fixes -F: drivers/net/wireless/b43/ +F: drivers/net/wireless/broadcom/b43/ B43LEGACY WIRELESS DRIVER M: Larry Finger diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 275b6ed93f15..0c47f2ca2207 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -19,6 +19,7 @@ if WLAN source "drivers/net/wireless/admtek/Kconfig" source "drivers/net/wireless/atmel/Kconfig" +source "drivers/net/wireless/broadcom/Kconfig" source "drivers/net/wireless/cisco/Kconfig" config PCMCIA_RAYCS @@ -158,7 +159,6 @@ config MWL8K will be called mwl8k. If unsure, say N. source "drivers/net/wireless/ath/Kconfig" -source "drivers/net/wireless/b43/Kconfig" source "drivers/net/wireless/b43legacy/Kconfig" source "drivers/net/wireless/brcm80211/Kconfig" source "drivers/net/wireless/hostap/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 87eb6b2f3227..7907674ad5b4 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/ obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/ +obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ obj-$(CONFIG_IPW2100) += ipw2x00/ @@ -14,7 +15,6 @@ obj-$(CONFIG_HERMES) += orinoco/ obj-$(CONFIG_PRISM54) += prism54/ obj-$(CONFIG_HOSTAP) += hostap/ -obj-$(CONFIG_B43) += b43/ obj-$(CONFIG_B43LEGACY) += b43legacy/ obj-$(CONFIG_ZD1211RW) += zd1211rw/ obj-$(CONFIG_WLAN) += realtek/ diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig deleted file mode 100644 index fba856032ca5..000000000000 --- a/drivers/net/wireless/b43/Kconfig +++ /dev/null @@ -1,187 +0,0 @@ -config B43 - tristate "Broadcom 43xx wireless support (mac80211 stack)" - depends on (BCMA_POSSIBLE || SSB_POSSIBLE) && MAC80211 && HAS_DMA - select BCMA if B43_BCMA - select SSB if B43_SSB - select FW_LOADER - ---help--- - b43 is a driver for the Broadcom 43xx series wireless devices. - - Check "lspci" for something like - "Broadcom Corporation BCM43XX 802.11 Wireless LAN Controller" - to determine whether you own such a device. - - This driver supports the new BCM43xx IEEE 802.11G devices, but not - the old IEEE 802.11B devices. Old devices are supported by - the b43legacy driver. - Note that this has nothing to do with the standard that your AccessPoint - supports (A, B, G or a combination). - IEEE 802.11G devices can talk to IEEE 802.11B AccessPoints. - - It is safe to include both b43 and b43legacy as the underlying glue - layer will automatically load the correct version for your device. - - This driver uses V4 firmware, which must be installed separately using - b43-fwcutter. - - This driver can be built as a module (recommended) that will be called "b43". - If unsure, say M. - -config B43_BCMA - bool - -config B43_SSB - bool - -choice - prompt "Supported bus types" - depends on B43 - default B43_BUSES_BCMA_AND_SSB - -config B43_BUSES_BCMA_AND_SSB - bool "BCMA and SSB" - depends on BCMA_POSSIBLE && SSB_POSSIBLE - select B43_BCMA - select B43_SSB - -config B43_BUSES_BCMA - bool "BCMA only" - depends on BCMA_POSSIBLE - select B43_BCMA - -config B43_BUSES_SSB - bool "SSB only" - depends on SSB_POSSIBLE - select B43_SSB - -endchoice - -# Auto-select SSB PCI-HOST support, if possible -config B43_PCI_AUTOSELECT - bool - depends on B43 && SSB_PCIHOST_POSSIBLE - select SSB_PCIHOST - select SSB_B43_PCI_BRIDGE - default y - -# Auto-select SSB PCICORE driver, if possible -config B43_PCICORE_AUTOSELECT - bool - depends on B43 && SSB_DRIVER_PCICORE_POSSIBLE - select SSB_DRIVER_PCICORE - default y - -config B43_SDIO - bool "Broadcom 43xx SDIO device support" - depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE - select SSB_SDIOHOST - ---help--- - Broadcom 43xx device support for Soft-MAC SDIO devices. - - With this config option you can drive Soft-MAC b43 cards with a - Secure Digital I/O interface. - This includes the WLAN daughter card found on the Nintendo Wii - video game console. - Note that this does not support Broadcom 43xx Full-MAC devices. - - It's safe to select Y here, even if you don't have a B43 SDIO device. - - If unsure, say N. - -#Data transfers to the device via PIO. We want it as a fallback even -# if we can do DMA. -config B43_BCMA_PIO - bool - depends on B43 && B43_BCMA - select BCMA_BLOCKIO - default y - -config B43_PIO - bool - depends on B43 && B43_SSB - select SSB_BLOCKIO - default y - -config B43_PHY_G - bool "Support for G-PHY (802.11g) devices" - depends on B43 && B43_SSB - default y - ---help--- - This PHY type can be found in the following chipsets: - PCI: BCM4306, BCM4311, BCM4318 - SoC: BCM4712, BCM5352E - -config B43_PHY_N - bool "Support for N-PHY (the main 802.11n series) devices" - depends on B43 - default y - ---help--- - This PHY type can be found in the following chipsets: - PCI: BCM4321, BCM4322, - BCM43222, BCM43224, BCM43225, - BCM43131, BCM43217, BCM43227, BCM43228 - SoC: BCM4716, BCM4717, BCM4718, BCM5356, BCM5357, BCM5358 - -config B43_PHY_LP - bool "Support for LP-PHY (low-power 802.11g) devices" - depends on B43 && B43_SSB - default y - ---help--- - The LP-PHY is a low-power PHY built into some notebooks - and embedded devices. It supports 802.11a/b/g - (802.11a support is optional, and currently disabled). - -config B43_PHY_HT - bool "Support for HT-PHY (high throughput 802.11n) devices" - depends on B43 && B43_BCMA - default y - ---help--- - This PHY type with 3x3:3 MIMO can be found in the BCM4331 PCI chipset. - -config B43_PHY_LCN - bool "Support for LCN-PHY devices (BROKEN)" - depends on B43 && BROKEN - ---help--- - Support for the LCN-PHY. - - Say N, this is BROKEN and crashes driver. - -config B43_PHY_AC - bool "Support for AC-PHY (802.11ac) devices (BROKEN)" - depends on B43 && B43_BCMA && BROKEN - ---help--- - This PHY type can be found in the following chipsets: - PCI: BCM4352, BCM4360 - - Say N, this is BROKEN and crashes driver. - -# This config option automatically enables b43 LEDS support, -# if it's possible. -config B43_LEDS - bool - depends on B43 && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = B43) - default y - -# This config option automatically enables b43 HW-RNG support, -# if the HW-RNG core is enabled. -config B43_HWRNG - bool - depends on B43 && (HW_RANDOM = y || HW_RANDOM = B43) - default y - -config B43_DEBUG - bool "Broadcom 43xx debugging" - depends on B43 - ---help--- - Broadcom 43xx debugging. - - This adds additional runtime sanity checks and statistics to the driver. - These checks and statistics might be expensive and hurt the runtime - performance of your system. - This also adds the b43 debugfs interface. - - Do not enable this, unless you are debugging the driver. - - Say N, if you are a distributor or user building a release kernel - for production use. - Only say Y, if you are debugging a problem in the b43 driver sourcecode. diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile deleted file mode 100644 index ddc4df46656f..000000000000 --- a/drivers/net/wireless/b43/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -b43-y += main.o -b43-y += bus.o -b43-$(CONFIG_B43_PHY_G) += phy_a.o phy_g.o tables.o lo.o wa.o -b43-$(CONFIG_B43_PHY_N) += tables_nphy.o -b43-$(CONFIG_B43_PHY_N) += radio_2055.o -b43-$(CONFIG_B43_PHY_N) += radio_2056.o -b43-$(CONFIG_B43_PHY_N) += radio_2057.o -b43-y += phy_common.o -b43-$(CONFIG_B43_PHY_N) += phy_n.o -b43-$(CONFIG_B43_PHY_LP) += phy_lp.o -b43-$(CONFIG_B43_PHY_LP) += tables_lpphy.o -b43-$(CONFIG_B43_PHY_HT) += phy_ht.o -b43-$(CONFIG_B43_PHY_HT) += tables_phy_ht.o -b43-$(CONFIG_B43_PHY_HT) += radio_2059.o -b43-$(CONFIG_B43_PHY_LCN) += phy_lcn.o tables_phy_lcn.o -b43-$(CONFIG_B43_PHY_AC) += phy_ac.o -b43-y += sysfs.o -b43-y += xmit.o -b43-y += dma.o -b43-y += pio.o -b43-y += rfkill.o -b43-y += ppr.o -b43-$(CONFIG_B43_LEDS) += leds.o -b43-$(CONFIG_B43_SDIO) += sdio.o -b43-$(CONFIG_B43_DEBUG) += debugfs.o - -obj-$(CONFIG_B43) += b43.o diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h deleted file mode 100644 index 036552439816..000000000000 --- a/drivers/net/wireless/b43/b43.h +++ /dev/null @@ -1,1108 +0,0 @@ -#ifndef B43_H_ -#define B43_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "debugfs.h" -#include "leds.h" -#include "rfkill.h" -#include "bus.h" -#include "lo.h" -#include "phy_common.h" - - -#ifdef CONFIG_B43_DEBUG -# define B43_DEBUG 1 -#else -# define B43_DEBUG 0 -#endif - -/* MMIO offsets */ -#define B43_MMIO_DMA0_REASON 0x20 -#define B43_MMIO_DMA0_IRQ_MASK 0x24 -#define B43_MMIO_DMA1_REASON 0x28 -#define B43_MMIO_DMA1_IRQ_MASK 0x2C -#define B43_MMIO_DMA2_REASON 0x30 -#define B43_MMIO_DMA2_IRQ_MASK 0x34 -#define B43_MMIO_DMA3_REASON 0x38 -#define B43_MMIO_DMA3_IRQ_MASK 0x3C -#define B43_MMIO_DMA4_REASON 0x40 -#define B43_MMIO_DMA4_IRQ_MASK 0x44 -#define B43_MMIO_DMA5_REASON 0x48 -#define B43_MMIO_DMA5_IRQ_MASK 0x4C -#define B43_MMIO_MACCTL 0x120 /* MAC control */ -#define B43_MMIO_MACCMD 0x124 /* MAC command */ -#define B43_MMIO_GEN_IRQ_REASON 0x128 -#define B43_MMIO_GEN_IRQ_MASK 0x12C -#define B43_MMIO_RAM_CONTROL 0x130 -#define B43_MMIO_RAM_DATA 0x134 -#define B43_MMIO_PS_STATUS 0x140 -#define B43_MMIO_RADIO_HWENABLED_HI 0x158 -#define B43_MMIO_MAC_HW_CAP 0x15C /* MAC capabilities (corerev >= 13) */ -#define B43_MMIO_SHM_CONTROL 0x160 -#define B43_MMIO_SHM_DATA 0x164 -#define B43_MMIO_SHM_DATA_UNALIGNED 0x166 -#define B43_MMIO_XMITSTAT_0 0x170 -#define B43_MMIO_XMITSTAT_1 0x174 -#define B43_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */ -#define B43_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */ -#define B43_MMIO_TSF_CFP_REP 0x188 -#define B43_MMIO_TSF_CFP_START 0x18C -#define B43_MMIO_TSF_CFP_MAXDUR 0x190 - -/* 32-bit DMA */ -#define B43_MMIO_DMA32_BASE0 0x200 -#define B43_MMIO_DMA32_BASE1 0x220 -#define B43_MMIO_DMA32_BASE2 0x240 -#define B43_MMIO_DMA32_BASE3 0x260 -#define B43_MMIO_DMA32_BASE4 0x280 -#define B43_MMIO_DMA32_BASE5 0x2A0 -/* 64-bit DMA */ -#define B43_MMIO_DMA64_BASE0 0x200 -#define B43_MMIO_DMA64_BASE1 0x240 -#define B43_MMIO_DMA64_BASE2 0x280 -#define B43_MMIO_DMA64_BASE3 0x2C0 -#define B43_MMIO_DMA64_BASE4 0x300 -#define B43_MMIO_DMA64_BASE5 0x340 - -/* PIO on core rev < 11 */ -#define B43_MMIO_PIO_BASE0 0x300 -#define B43_MMIO_PIO_BASE1 0x310 -#define B43_MMIO_PIO_BASE2 0x320 -#define B43_MMIO_PIO_BASE3 0x330 -#define B43_MMIO_PIO_BASE4 0x340 -#define B43_MMIO_PIO_BASE5 0x350 -#define B43_MMIO_PIO_BASE6 0x360 -#define B43_MMIO_PIO_BASE7 0x370 -/* PIO on core rev >= 11 */ -#define B43_MMIO_PIO11_BASE0 0x200 -#define B43_MMIO_PIO11_BASE1 0x240 -#define B43_MMIO_PIO11_BASE2 0x280 -#define B43_MMIO_PIO11_BASE3 0x2C0 -#define B43_MMIO_PIO11_BASE4 0x300 -#define B43_MMIO_PIO11_BASE5 0x340 - -#define B43_MMIO_RADIO24_CONTROL 0x3D8 /* core rev >= 24 only */ -#define B43_MMIO_RADIO24_DATA 0x3DA /* core rev >= 24 only */ -#define B43_MMIO_PHY_VER 0x3E0 -#define B43_MMIO_PHY_RADIO 0x3E2 -#define B43_MMIO_PHY0 0x3E6 -#define B43_MMIO_ANTENNA 0x3E8 -#define B43_MMIO_CHANNEL 0x3F0 -#define B43_MMIO_CHANNEL_EXT 0x3F4 -#define B43_MMIO_RADIO_CONTROL 0x3F6 -#define B43_MMIO_RADIO_DATA_HIGH 0x3F8 -#define B43_MMIO_RADIO_DATA_LOW 0x3FA -#define B43_MMIO_PHY_CONTROL 0x3FC -#define B43_MMIO_PHY_DATA 0x3FE -#define B43_MMIO_MACFILTER_CONTROL 0x420 -#define B43_MMIO_MACFILTER_DATA 0x422 -#define B43_MMIO_RCMTA_COUNT 0x43C -#define B43_MMIO_PSM_PHY_HDR 0x492 -#define B43_MMIO_RADIO_HWENABLED_LO 0x49A -#define B43_MMIO_GPIO_CONTROL 0x49C -#define B43_MMIO_GPIO_MASK 0x49E -#define B43_MMIO_TXE0_CTL 0x500 -#define B43_MMIO_TXE0_AUX 0x502 -#define B43_MMIO_TXE0_TS_LOC 0x504 -#define B43_MMIO_TXE0_TIME_OUT 0x506 -#define B43_MMIO_TXE0_WM_0 0x508 -#define B43_MMIO_TXE0_WM_1 0x50A -#define B43_MMIO_TXE0_PHYCTL 0x50C -#define B43_MMIO_TXE0_STATUS 0x50E -#define B43_MMIO_TXE0_MMPLCP0 0x510 -#define B43_MMIO_TXE0_MMPLCP1 0x512 -#define B43_MMIO_TXE0_PHYCTL1 0x514 -#define B43_MMIO_XMTFIFODEF 0x520 -#define B43_MMIO_XMTFIFO_FRAME_CNT 0x522 /* core rev>= 16 only */ -#define B43_MMIO_XMTFIFO_BYTE_CNT 0x524 /* core rev>= 16 only */ -#define B43_MMIO_XMTFIFO_HEAD 0x526 /* core rev>= 16 only */ -#define B43_MMIO_XMTFIFO_RD_PTR 0x528 /* core rev>= 16 only */ -#define B43_MMIO_XMTFIFO_WR_PTR 0x52A /* core rev>= 16 only */ -#define B43_MMIO_XMTFIFODEF1 0x52C /* core rev>= 16 only */ -#define B43_MMIO_XMTFIFOCMD 0x540 -#define B43_MMIO_XMTFIFOFLUSH 0x542 -#define B43_MMIO_XMTFIFOTHRESH 0x544 -#define B43_MMIO_XMTFIFORDY 0x546 -#define B43_MMIO_XMTFIFOPRIRDY 0x548 -#define B43_MMIO_XMTFIFORQPRI 0x54A -#define B43_MMIO_XMTTPLATETXPTR 0x54C -#define B43_MMIO_XMTTPLATEPTR 0x550 -#define B43_MMIO_SMPL_CLCT_STRPTR 0x552 /* core rev>= 22 only */ -#define B43_MMIO_SMPL_CLCT_STPPTR 0x554 /* core rev>= 22 only */ -#define B43_MMIO_SMPL_CLCT_CURPTR 0x556 /* core rev>= 22 only */ -#define B43_MMIO_XMTTPLATEDATALO 0x560 -#define B43_MMIO_XMTTPLATEDATAHI 0x562 -#define B43_MMIO_XMTSEL 0x568 -#define B43_MMIO_XMTTXCNT 0x56A -#define B43_MMIO_XMTTXSHMADDR 0x56C -#define B43_MMIO_TSF_CFP_START_LOW 0x604 -#define B43_MMIO_TSF_CFP_START_HIGH 0x606 -#define B43_MMIO_TSF_CFP_PRETBTT 0x612 -#define B43_MMIO_TSF_CLK_FRAC_LOW 0x62E -#define B43_MMIO_TSF_CLK_FRAC_HIGH 0x630 -#define B43_MMIO_TSF_0 0x632 /* core rev < 3 only */ -#define B43_MMIO_TSF_1 0x634 /* core rev < 3 only */ -#define B43_MMIO_TSF_2 0x636 /* core rev < 3 only */ -#define B43_MMIO_TSF_3 0x638 /* core rev < 3 only */ -#define B43_MMIO_RNG 0x65A -#define B43_MMIO_IFSSLOT 0x684 /* Interframe slot time */ -#define B43_MMIO_IFSCTL 0x688 /* Interframe space control */ -#define B43_MMIO_IFSSTAT 0x690 -#define B43_MMIO_IFSMEDBUSYCTL 0x692 -#define B43_MMIO_IFTXDUR 0x694 -#define B43_MMIO_IFSCTL_USE_EDCF 0x0004 -#define B43_MMIO_POWERUP_DELAY 0x6A8 -#define B43_MMIO_BTCOEX_CTL 0x6B4 /* Bluetooth Coexistence Control */ -#define B43_MMIO_BTCOEX_STAT 0x6B6 /* Bluetooth Coexistence Status */ -#define B43_MMIO_BTCOEX_TXCTL 0x6B8 /* Bluetooth Coexistence Transmit Control */ -#define B43_MMIO_WEPCTL 0x7C0 - -/* SPROM boardflags_lo values */ -#define B43_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */ -#define B43_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */ -#define B43_BFL_AIRLINEMODE 0x0004 /* implements GPIO 13 radio disable indication */ -#define B43_BFL_RSSI 0x0008 /* software calculates nrssi slope. */ -#define B43_BFL_ENETSPI 0x0010 /* has ephy roboswitch spi */ -#define B43_BFL_XTAL_NOSLOW 0x0020 /* no slow clock available */ -#define B43_BFL_CCKHIPWR 0x0040 /* can do high power CCK transmission */ -#define B43_BFL_ENETADM 0x0080 /* has ADMtek switch */ -#define B43_BFL_ENETVLAN 0x0100 /* can do vlan */ -#define B43_BFL_AFTERBURNER 0x0200 /* supports Afterburner mode */ -#define B43_BFL_NOPCI 0x0400 /* leaves PCI floating */ -#define B43_BFL_FEM 0x0800 /* supports the Front End Module */ -#define B43_BFL_EXTLNA 0x1000 /* has an external LNA */ -#define B43_BFL_HGPA 0x2000 /* had high gain PA */ -#define B43_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */ -#define B43_BFL_ALTIQ 0x8000 /* alternate I/Q settings */ - -/* SPROM boardflags_hi values */ -#define B43_BFH_NOPA 0x0001 /* has no PA */ -#define B43_BFH_RSSIINV 0x0002 /* RSSI uses positive slope (not TSSI) */ -#define B43_BFH_PAREF 0x0004 /* uses the PARef LDO */ -#define B43_BFH_3TSWITCH 0x0008 /* uses a triple throw switch shared - * with bluetooth */ -#define B43_BFH_PHASESHIFT 0x0010 /* can support phase shifter */ -#define B43_BFH_BUCKBOOST 0x0020 /* has buck/booster */ -#define B43_BFH_FEM_BT 0x0040 /* has FEM and switch to share antenna - * with bluetooth */ -#define B43_BFH_NOCBUCK 0x0080 -#define B43_BFH_PALDO 0x0200 -#define B43_BFH_EXTLNA_5GHZ 0x1000 /* has an external LNA (5GHz mode) */ - -/* SPROM boardflags2_lo values */ -#define B43_BFL2_RXBB_INT_REG_DIS 0x0001 /* external RX BB regulator present */ -#define B43_BFL2_APLL_WAR 0x0002 /* alternative A-band PLL settings implemented */ -#define B43_BFL2_TXPWRCTRL_EN 0x0004 /* permits enabling TX Power Control */ -#define B43_BFL2_2X4_DIV 0x0008 /* 2x4 diversity switch */ -#define B43_BFL2_5G_PWRGAIN 0x0010 /* supports 5G band power gain */ -#define B43_BFL2_PCIEWAR_OVR 0x0020 /* overrides ASPM and Clkreq settings */ -#define B43_BFL2_CAESERS_BRD 0x0040 /* is Caesers board (unused) */ -#define B43_BFL2_BTC3WIRE 0x0080 /* used 3-wire bluetooth coexist */ -#define B43_BFL2_SKWRKFEM_BRD 0x0100 /* 4321mcm93 uses Skyworks FEM */ -#define B43_BFL2_SPUR_WAR 0x0200 /* has a workaround for clock-harmonic spurs */ -#define B43_BFL2_GPLL_WAR 0x0400 /* altenative G-band PLL settings implemented */ -#define B43_BFL2_SINGLEANT_CCK 0x1000 -#define B43_BFL2_2G_SPUR_WAR 0x2000 - -/* SPROM boardflags2_hi values */ -#define B43_BFH2_GPLL_WAR2 0x0001 -#define B43_BFH2_IPALVLSHIFT_3P3 0x0002 -#define B43_BFH2_INTERNDET_TXIQCAL 0x0004 -#define B43_BFH2_XTALBUFOUTEN 0x0008 - -/* GPIO register offset, in both ChipCommon and PCI core. */ -#define B43_GPIO_CONTROL 0x6c - -/* SHM Routing */ -enum { - B43_SHM_UCODE, /* Microcode memory */ - B43_SHM_SHARED, /* Shared memory */ - B43_SHM_SCRATCH, /* Scratch memory */ - B43_SHM_HW, /* Internal hardware register */ - B43_SHM_RCMTA, /* Receive match transmitter address (rev >= 5 only) */ -}; -/* SHM Routing modifiers */ -#define B43_SHM_AUTOINC_R 0x0200 /* Auto-increment address on read */ -#define B43_SHM_AUTOINC_W 0x0100 /* Auto-increment address on write */ -#define B43_SHM_AUTOINC_RW (B43_SHM_AUTOINC_R | \ - B43_SHM_AUTOINC_W) - -/* Misc SHM_SHARED offsets */ -#define B43_SHM_SH_WLCOREREV 0x0016 /* 802.11 core revision */ -#define B43_SHM_SH_PCTLWDPOS 0x0008 -#define B43_SHM_SH_RXPADOFF 0x0034 /* RX Padding data offset (PIO only) */ -#define B43_SHM_SH_FWCAPA 0x0042 /* Firmware capabilities (Opensource firmware only) */ -#define B43_SHM_SH_PHYVER 0x0050 /* PHY version */ -#define B43_SHM_SH_PHYTYPE 0x0052 /* PHY type */ -#define B43_SHM_SH_ANTSWAP 0x005C /* Antenna swap threshold */ -#define B43_SHM_SH_HOSTF1 0x005E /* Hostflags 1 for ucode options */ -#define B43_SHM_SH_HOSTF2 0x0060 /* Hostflags 2 for ucode options */ -#define B43_SHM_SH_HOSTF3 0x0062 /* Hostflags 3 for ucode options */ -#define B43_SHM_SH_RFATT 0x0064 /* Current radio attenuation value */ -#define B43_SHM_SH_RADAR 0x0066 /* Radar register */ -#define B43_SHM_SH_PHYTXNOI 0x006E /* PHY noise directly after TX (lower 8bit only) */ -#define B43_SHM_SH_RFRXSP1 0x0072 /* RF RX SP Register 1 */ -#define B43_SHM_SH_HOSTF4 0x0078 /* Hostflags 4 for ucode options */ -#define B43_SHM_SH_CHAN 0x00A0 /* Current channel (low 8bit only) */ -#define B43_SHM_SH_CHAN_5GHZ 0x0100 /* Bit set, if 5 Ghz channel */ -#define B43_SHM_SH_CHAN_40MHZ 0x0200 /* Bit set, if 40 Mhz channel width */ -#define B43_SHM_SH_MACHW_L 0x00C0 /* Location where the ucode expects the MAC capabilities */ -#define B43_SHM_SH_MACHW_H 0x00C2 /* Location where the ucode expects the MAC capabilities */ -#define B43_SHM_SH_HOSTF5 0x00D4 /* Hostflags 5 for ucode options */ -#define B43_SHM_SH_BCMCFIFOID 0x0108 /* Last posted cookie to the bcast/mcast FIFO */ -/* TSSI information */ -#define B43_SHM_SH_TSSI_CCK 0x0058 /* TSSI for last 4 CCK frames (32bit) */ -#define B43_SHM_SH_TSSI_OFDM_A 0x0068 /* TSSI for last 4 OFDM frames (32bit) */ -#define B43_SHM_SH_TSSI_OFDM_G 0x0070 /* TSSI for last 4 OFDM frames (32bit) */ -#define B43_TSSI_MAX 0x7F /* Max value for one TSSI value */ -/* SHM_SHARED TX FIFO variables */ -#define B43_SHM_SH_SIZE01 0x0098 /* TX FIFO size for FIFO 0 (low) and 1 (high) */ -#define B43_SHM_SH_SIZE23 0x009A /* TX FIFO size for FIFO 2 and 3 */ -#define B43_SHM_SH_SIZE45 0x009C /* TX FIFO size for FIFO 4 and 5 */ -#define B43_SHM_SH_SIZE67 0x009E /* TX FIFO size for FIFO 6 and 7 */ -/* SHM_SHARED background noise */ -#define B43_SHM_SH_JSSI0 0x0088 /* Measure JSSI 0 */ -#define B43_SHM_SH_JSSI1 0x008A /* Measure JSSI 1 */ -#define B43_SHM_SH_JSSIAUX 0x008C /* Measure JSSI AUX */ -/* SHM_SHARED crypto engine */ -#define B43_SHM_SH_DEFAULTIV 0x003C /* Default IV location */ -#define B43_SHM_SH_NRRXTRANS 0x003E /* # of soft RX transmitter addresses (max 8) */ -#define B43_SHM_SH_KTP 0x0056 /* Key table pointer */ -#define B43_SHM_SH_TKIPTSCTTAK 0x0318 -#define B43_SHM_SH_KEYIDXBLOCK 0x05D4 /* Key index/algorithm block (v4 firmware) */ -#define B43_SHM_SH_PSM 0x05F4 /* PSM transmitter address match block (rev < 5) */ -/* SHM_SHARED WME variables */ -#define B43_SHM_SH_EDCFSTAT 0x000E /* EDCF status */ -#define B43_SHM_SH_TXFCUR 0x0030 /* TXF current index */ -#define B43_SHM_SH_EDCFQ 0x0240 /* EDCF Q info */ -/* SHM_SHARED powersave mode related */ -#define B43_SHM_SH_SLOTT 0x0010 /* Slot time */ -#define B43_SHM_SH_DTIMPER 0x0012 /* DTIM period */ -#define B43_SHM_SH_NOSLPZNATDTIM 0x004C /* NOSLPZNAT DTIM */ -/* SHM_SHARED beacon/AP variables */ -#define B43_SHM_SH_BT_BASE0 0x0068 /* Beacon template base 0 */ -#define B43_SHM_SH_BTL0 0x0018 /* Beacon template length 0 */ -#define B43_SHM_SH_BT_BASE1 0x0468 /* Beacon template base 1 */ -#define B43_SHM_SH_BTL1 0x001A /* Beacon template length 1 */ -#define B43_SHM_SH_BTSFOFF 0x001C /* Beacon TSF offset */ -#define B43_SHM_SH_TIMBPOS 0x001E /* TIM B position in beacon */ -#define B43_SHM_SH_DTIMP 0x0012 /* DTIP period */ -#define B43_SHM_SH_MCASTCOOKIE 0x00A8 /* Last bcast/mcast frame ID */ -#define B43_SHM_SH_SFFBLIM 0x0044 /* Short frame fallback retry limit */ -#define B43_SHM_SH_LFFBLIM 0x0046 /* Long frame fallback retry limit */ -#define B43_SHM_SH_BEACPHYCTL 0x0054 /* Beacon PHY TX control word (see PHY TX control) */ -#define B43_SHM_SH_EXTNPHYCTL 0x00B0 /* Extended bytes for beacon PHY control (N) */ -#define B43_SHM_SH_BCN_LI 0x00B6 /* beacon listen interval */ -/* SHM_SHARED ACK/CTS control */ -#define B43_SHM_SH_ACKCTSPHYCTL 0x0022 /* ACK/CTS PHY control word (see PHY TX control) */ -/* SHM_SHARED probe response variables */ -#define B43_SHM_SH_PRSSID 0x0160 /* Probe Response SSID */ -#define B43_SHM_SH_PRSSIDLEN 0x0048 /* Probe Response SSID length */ -#define B43_SHM_SH_PRTLEN 0x004A /* Probe Response template length */ -#define B43_SHM_SH_PRMAXTIME 0x0074 /* Probe Response max time */ -#define B43_SHM_SH_PRPHYCTL 0x0188 /* Probe Response PHY TX control word */ -/* SHM_SHARED rate tables */ -#define B43_SHM_SH_OFDMDIRECT 0x01C0 /* Pointer to OFDM direct map */ -#define B43_SHM_SH_OFDMBASIC 0x01E0 /* Pointer to OFDM basic rate map */ -#define B43_SHM_SH_CCKDIRECT 0x0200 /* Pointer to CCK direct map */ -#define B43_SHM_SH_CCKBASIC 0x0220 /* Pointer to CCK basic rate map */ -/* SHM_SHARED microcode soft registers */ -#define B43_SHM_SH_UCODEREV 0x0000 /* Microcode revision */ -#define B43_SHM_SH_UCODEPATCH 0x0002 /* Microcode patchlevel */ -#define B43_SHM_SH_UCODEDATE 0x0004 /* Microcode date */ -#define B43_SHM_SH_UCODETIME 0x0006 /* Microcode time */ -#define B43_SHM_SH_UCODESTAT 0x0040 /* Microcode debug status code */ -#define B43_SHM_SH_UCODESTAT_INVALID 0 -#define B43_SHM_SH_UCODESTAT_INIT 1 -#define B43_SHM_SH_UCODESTAT_ACTIVE 2 -#define B43_SHM_SH_UCODESTAT_SUSP 3 /* suspended */ -#define B43_SHM_SH_UCODESTAT_SLEEP 4 /* asleep (PS) */ -#define B43_SHM_SH_MAXBFRAMES 0x0080 /* Maximum number of frames in a burst */ -#define B43_SHM_SH_SPUWKUP 0x0094 /* pre-wakeup for synth PU in us */ -#define B43_SHM_SH_PRETBTT 0x0096 /* pre-TBTT in us */ -/* SHM_SHARED tx iq workarounds */ -#define B43_SHM_SH_NPHY_TXIQW0 0x0700 -#define B43_SHM_SH_NPHY_TXIQW1 0x0702 -#define B43_SHM_SH_NPHY_TXIQW2 0x0704 -#define B43_SHM_SH_NPHY_TXIQW3 0x0706 -/* SHM_SHARED tx pwr ctrl */ -#define B43_SHM_SH_NPHY_TXPWR_INDX0 0x0708 -#define B43_SHM_SH_NPHY_TXPWR_INDX1 0x070E - -/* SHM_SCRATCH offsets */ -#define B43_SHM_SC_MINCONT 0x0003 /* Minimum contention window */ -#define B43_SHM_SC_MAXCONT 0x0004 /* Maximum contention window */ -#define B43_SHM_SC_CURCONT 0x0005 /* Current contention window */ -#define B43_SHM_SC_SRLIMIT 0x0006 /* Short retry count limit */ -#define B43_SHM_SC_LRLIMIT 0x0007 /* Long retry count limit */ -#define B43_SHM_SC_DTIMC 0x0008 /* Current DTIM count */ -#define B43_SHM_SC_BTL0LEN 0x0015 /* Beacon 0 template length */ -#define B43_SHM_SC_BTL1LEN 0x0016 /* Beacon 1 template length */ -#define B43_SHM_SC_SCFB 0x0017 /* Short frame transmit count threshold for rate fallback */ -#define B43_SHM_SC_LCFB 0x0018 /* Long frame transmit count threshold for rate fallback */ - -/* Hardware Radio Enable masks */ -#define B43_MMIO_RADIO_HWENABLED_HI_MASK (1 << 16) -#define B43_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4) - -/* HostFlags. See b43_hf_read/write() */ -#define B43_HF_ANTDIVHELP 0x000000000001ULL /* ucode antenna div helper */ -#define B43_HF_SYMW 0x000000000002ULL /* G-PHY SYM workaround */ -#define B43_HF_RXPULLW 0x000000000004ULL /* RX pullup workaround */ -#define B43_HF_CCKBOOST 0x000000000008ULL /* 4dB CCK power boost (exclusive with OFDM boost) */ -#define B43_HF_BTCOEX 0x000000000010ULL /* Bluetooth coexistance */ -#define B43_HF_GDCW 0x000000000020ULL /* G-PHY DC canceller filter bw workaround */ -#define B43_HF_OFDMPABOOST 0x000000000040ULL /* Enable PA gain boost for OFDM */ -#define B43_HF_ACPR 0x000000000080ULL /* Disable for Japan, channel 14 */ -#define B43_HF_EDCF 0x000000000100ULL /* on if WME and MAC suspended */ -#define B43_HF_TSSIRPSMW 0x000000000200ULL /* TSSI reset PSM ucode workaround */ -#define B43_HF_20IN40IQW 0x000000000200ULL /* 20 in 40 MHz I/Q workaround (rev >= 13 only) */ -#define B43_HF_DSCRQ 0x000000000400ULL /* Disable slow clock request in ucode */ -#define B43_HF_ACIW 0x000000000800ULL /* ACI workaround: shift bits by 2 on PHY CRS */ -#define B43_HF_2060W 0x000000001000ULL /* 2060 radio workaround */ -#define B43_HF_RADARW 0x000000002000ULL /* Radar workaround */ -#define B43_HF_USEDEFKEYS 0x000000004000ULL /* Enable use of default keys */ -#define B43_HF_AFTERBURNER 0x000000008000ULL /* Afterburner enabled */ -#define B43_HF_BT4PRIOCOEX 0x000000010000ULL /* Bluetooth 4-priority coexistance */ -#define B43_HF_FWKUP 0x000000020000ULL /* Fast wake-up ucode */ -#define B43_HF_VCORECALC 0x000000040000ULL /* Force VCO recalculation when powering up synthpu */ -#define B43_HF_PCISCW 0x000000080000ULL /* PCI slow clock workaround */ -#define B43_HF_4318TSSI 0x000000200000ULL /* 4318 TSSI */ -#define B43_HF_FBCMCFIFO 0x000000400000ULL /* Flush bcast/mcast FIFO immediately */ -#define B43_HF_HWPCTL 0x000000800000ULL /* Enable hardwarre power control */ -#define B43_HF_BTCOEXALT 0x000001000000ULL /* Bluetooth coexistance in alternate pins */ -#define B43_HF_TXBTCHECK 0x000002000000ULL /* Bluetooth check during transmission */ -#define B43_HF_SKCFPUP 0x000004000000ULL /* Skip CFP update */ -#define B43_HF_N40W 0x000008000000ULL /* N PHY 40 MHz workaround (rev >= 13 only) */ -#define B43_HF_ANTSEL 0x000020000000ULL /* Antenna selection (for testing antenna div.) */ -#define B43_HF_BT3COEXT 0x000020000000ULL /* Bluetooth 3-wire coexistence (rev >= 13 only) */ -#define B43_HF_BTCANT 0x000040000000ULL /* Bluetooth coexistence (antenna mode) (rev >= 13 only) */ -#define B43_HF_ANTSELEN 0x000100000000ULL /* Antenna selection enabled (rev >= 13 only) */ -#define B43_HF_ANTSELMODE 0x000200000000ULL /* Antenna selection mode (rev >= 13 only) */ -#define B43_HF_MLADVW 0x001000000000ULL /* N PHY ML ADV workaround (rev >= 13 only) */ -#define B43_HF_PR45960W 0x080000000000ULL /* PR 45960 workaround (rev >= 13 only) */ - -/* Firmware capabilities field in SHM (Opensource firmware only) */ -#define B43_FWCAPA_HWCRYPTO 0x0001 -#define B43_FWCAPA_QOS 0x0002 - -/* MacFilter offsets. */ -#define B43_MACFILTER_SELF 0x0000 -#define B43_MACFILTER_BSSID 0x0003 - -/* PowerControl */ -#define B43_PCTL_IN 0xB0 -#define B43_PCTL_OUT 0xB4 -#define B43_PCTL_OUTENABLE 0xB8 -#define B43_PCTL_XTAL_POWERUP 0x40 -#define B43_PCTL_PLL_POWERDOWN 0x80 - -/* PowerControl Clock Modes */ -#define B43_PCTL_CLK_FAST 0x00 -#define B43_PCTL_CLK_SLOW 0x01 -#define B43_PCTL_CLK_DYNAMIC 0x02 - -#define B43_PCTL_FORCE_SLOW 0x0800 -#define B43_PCTL_FORCE_PLL 0x1000 -#define B43_PCTL_DYN_XTAL 0x2000 - -/* PHYVersioning */ -#define B43_PHYTYPE_A 0x00 -#define B43_PHYTYPE_B 0x01 -#define B43_PHYTYPE_G 0x02 -#define B43_PHYTYPE_N 0x04 -#define B43_PHYTYPE_LP 0x05 -#define B43_PHYTYPE_SSLPN 0x06 -#define B43_PHYTYPE_HT 0x07 -#define B43_PHYTYPE_LCN 0x08 -#define B43_PHYTYPE_LCNXN 0x09 -#define B43_PHYTYPE_LCN40 0x0a -#define B43_PHYTYPE_AC 0x0b - -/* PHYRegisters */ -#define B43_PHY_ILT_A_CTRL 0x0072 -#define B43_PHY_ILT_A_DATA1 0x0073 -#define B43_PHY_ILT_A_DATA2 0x0074 -#define B43_PHY_G_LO_CONTROL 0x0810 -#define B43_PHY_ILT_G_CTRL 0x0472 -#define B43_PHY_ILT_G_DATA1 0x0473 -#define B43_PHY_ILT_G_DATA2 0x0474 -#define B43_PHY_A_PCTL 0x007B -#define B43_PHY_G_PCTL 0x0029 -#define B43_PHY_A_CRS 0x0029 -#define B43_PHY_RADIO_BITFIELD 0x0401 -#define B43_PHY_G_CRS 0x0429 -#define B43_PHY_NRSSILT_CTRL 0x0803 -#define B43_PHY_NRSSILT_DATA 0x0804 - -/* RadioRegisters */ -#define B43_RADIOCTL_ID 0x01 - -/* MAC Control bitfield */ -#define B43_MACCTL_ENABLED 0x00000001 /* MAC Enabled */ -#define B43_MACCTL_PSM_RUN 0x00000002 /* Run Microcode */ -#define B43_MACCTL_PSM_JMP0 0x00000004 /* Microcode jump to 0 */ -#define B43_MACCTL_SHM_ENABLED 0x00000100 /* SHM Enabled */ -#define B43_MACCTL_SHM_UPPER 0x00000200 /* SHM Upper */ -#define B43_MACCTL_IHR_ENABLED 0x00000400 /* IHR Region Enabled */ -#define B43_MACCTL_PSM_DBG 0x00002000 /* Microcode debugging enabled */ -#define B43_MACCTL_GPOUTSMSK 0x0000C000 /* GPOUT Select Mask */ -#define B43_MACCTL_BE 0x00010000 /* Big Endian mode */ -#define B43_MACCTL_INFRA 0x00020000 /* Infrastructure mode */ -#define B43_MACCTL_AP 0x00040000 /* AccessPoint mode */ -#define B43_MACCTL_RADIOLOCK 0x00080000 /* Radio lock */ -#define B43_MACCTL_BEACPROMISC 0x00100000 /* Beacon Promiscuous */ -#define B43_MACCTL_KEEP_BADPLCP 0x00200000 /* Keep frames with bad PLCP */ -#define B43_MACCTL_PHY_LOCK 0x00200000 -#define B43_MACCTL_KEEP_CTL 0x00400000 /* Keep control frames */ -#define B43_MACCTL_KEEP_BAD 0x00800000 /* Keep bad frames (FCS) */ -#define B43_MACCTL_PROMISC 0x01000000 /* Promiscuous mode */ -#define B43_MACCTL_HWPS 0x02000000 /* Hardware Power Saving */ -#define B43_MACCTL_AWAKE 0x04000000 /* Device is awake */ -#define B43_MACCTL_CLOSEDNET 0x08000000 /* Closed net (no SSID bcast) */ -#define B43_MACCTL_TBTTHOLD 0x10000000 /* TBTT Hold */ -#define B43_MACCTL_DISCTXSTAT 0x20000000 /* Discard TX status */ -#define B43_MACCTL_DISCPMQ 0x40000000 /* Discard Power Management Queue */ -#define B43_MACCTL_GMODE 0x80000000 /* G Mode */ - -/* MAC Command bitfield */ -#define B43_MACCMD_BEACON0_VALID 0x00000001 /* Beacon 0 in template RAM is busy/valid */ -#define B43_MACCMD_BEACON1_VALID 0x00000002 /* Beacon 1 in template RAM is busy/valid */ -#define B43_MACCMD_DFQ_VALID 0x00000004 /* Directed frame queue valid (IBSS PS mode, ATIM) */ -#define B43_MACCMD_CCA 0x00000008 /* Clear channel assessment */ -#define B43_MACCMD_BGNOISE 0x00000010 /* Background noise */ - -/* B43_MMIO_PSM_PHY_HDR bits */ -#define B43_PSM_HDR_MAC_PHY_RESET 0x00000001 -#define B43_PSM_HDR_MAC_PHY_CLOCK_EN 0x00000002 -#define B43_PSM_HDR_MAC_PHY_FORCE_CLK 0x00000004 - -/* See BCMA_CLKCTLST_EXTRESREQ and BCMA_CLKCTLST_EXTRESST */ -#define B43_BCMA_CLKCTLST_80211_PLL_REQ 0x00000100 -#define B43_BCMA_CLKCTLST_PHY_PLL_REQ 0x00000200 -#define B43_BCMA_CLKCTLST_80211_PLL_ST 0x01000000 -#define B43_BCMA_CLKCTLST_PHY_PLL_ST 0x02000000 - -/* BCMA 802.11 core specific IO Control (BCMA_IOCTL) flags */ -#define B43_BCMA_IOCTL_PHY_CLKEN 0x00000004 /* PHY Clock Enable */ -#define B43_BCMA_IOCTL_PHY_RESET 0x00000008 /* PHY Reset */ -#define B43_BCMA_IOCTL_MACPHYCLKEN 0x00000010 /* MAC PHY Clock Control Enable */ -#define B43_BCMA_IOCTL_PLLREFSEL 0x00000020 /* PLL Frequency Reference Select */ -#define B43_BCMA_IOCTL_PHY_BW 0x000000C0 /* PHY band width and clock speed mask (N-PHY+ only?) */ -#define B43_BCMA_IOCTL_PHY_BW_10MHZ 0x00000000 /* 10 MHz bandwidth, 40 MHz PHY */ -#define B43_BCMA_IOCTL_PHY_BW_20MHZ 0x00000040 /* 20 MHz bandwidth, 80 MHz PHY */ -#define B43_BCMA_IOCTL_PHY_BW_40MHZ 0x00000080 /* 40 MHz bandwidth, 160 MHz PHY */ -#define B43_BCMA_IOCTL_PHY_BW_80MHZ 0x000000C0 /* 80 MHz bandwidth */ -#define B43_BCMA_IOCTL_DAC 0x00000300 /* Highspeed DAC mode control field */ -#define B43_BCMA_IOCTL_GMODE 0x00002000 /* G Mode Enable */ - -/* BCMA 802.11 core specific IO status (BCMA_IOST) flags */ -#define B43_BCMA_IOST_2G_PHY 0x00000001 /* 2.4G capable phy */ -#define B43_BCMA_IOST_5G_PHY 0x00000002 /* 5G capable phy */ -#define B43_BCMA_IOST_FASTCLKA 0x00000004 /* Fast Clock Available */ -#define B43_BCMA_IOST_DUALB_PHY 0x00000008 /* Dualband phy */ - -/* 802.11 core specific TM State Low (SSB_TMSLOW) flags */ -#define B43_TMSLOW_GMODE 0x20000000 /* G Mode Enable */ -#define B43_TMSLOW_PHY_BANDWIDTH 0x00C00000 /* PHY band width and clock speed mask (N-PHY only) */ -#define B43_TMSLOW_PHY_BANDWIDTH_10MHZ 0x00000000 /* 10 MHz bandwidth, 40 MHz PHY */ -#define B43_TMSLOW_PHY_BANDWIDTH_20MHZ 0x00400000 /* 20 MHz bandwidth, 80 MHz PHY */ -#define B43_TMSLOW_PHY_BANDWIDTH_40MHZ 0x00800000 /* 40 MHz bandwidth, 160 MHz PHY */ -#define B43_TMSLOW_PLLREFSEL 0x00200000 /* PLL Frequency Reference Select (rev >= 5) */ -#define B43_TMSLOW_MACPHYCLKEN 0x00100000 /* MAC PHY Clock Control Enable (rev >= 5) */ -#define B43_TMSLOW_PHYRESET 0x00080000 /* PHY Reset */ -#define B43_TMSLOW_PHYCLKEN 0x00040000 /* PHY Clock Enable */ - -/* 802.11 core specific TM State High (SSB_TMSHIGH) flags */ -#define B43_TMSHIGH_DUALBAND_PHY 0x00080000 /* Dualband PHY available */ -#define B43_TMSHIGH_FCLOCK 0x00040000 /* Fast Clock Available (rev >= 5) */ -#define B43_TMSHIGH_HAVE_5GHZ_PHY 0x00020000 /* 5 GHz PHY available (rev >= 5) */ -#define B43_TMSHIGH_HAVE_2GHZ_PHY 0x00010000 /* 2.4 GHz PHY available (rev >= 5) */ - -/* Generic-Interrupt reasons. */ -#define B43_IRQ_MAC_SUSPENDED 0x00000001 -#define B43_IRQ_BEACON 0x00000002 -#define B43_IRQ_TBTT_INDI 0x00000004 -#define B43_IRQ_BEACON_TX_OK 0x00000008 -#define B43_IRQ_BEACON_CANCEL 0x00000010 -#define B43_IRQ_ATIM_END 0x00000020 -#define B43_IRQ_PMQ 0x00000040 -#define B43_IRQ_PIO_WORKAROUND 0x00000100 -#define B43_IRQ_MAC_TXERR 0x00000200 -#define B43_IRQ_PHY_TXERR 0x00000800 -#define B43_IRQ_PMEVENT 0x00001000 -#define B43_IRQ_TIMER0 0x00002000 -#define B43_IRQ_TIMER1 0x00004000 -#define B43_IRQ_DMA 0x00008000 -#define B43_IRQ_TXFIFO_FLUSH_OK 0x00010000 -#define B43_IRQ_CCA_MEASURE_OK 0x00020000 -#define B43_IRQ_NOISESAMPLE_OK 0x00040000 -#define B43_IRQ_UCODE_DEBUG 0x08000000 -#define B43_IRQ_RFKILL 0x10000000 -#define B43_IRQ_TX_OK 0x20000000 -#define B43_IRQ_PHY_G_CHANGED 0x40000000 -#define B43_IRQ_TIMEOUT 0x80000000 - -#define B43_IRQ_ALL 0xFFFFFFFF -#define B43_IRQ_MASKTEMPLATE (B43_IRQ_TBTT_INDI | \ - B43_IRQ_ATIM_END | \ - B43_IRQ_PMQ | \ - B43_IRQ_MAC_TXERR | \ - B43_IRQ_PHY_TXERR | \ - B43_IRQ_DMA | \ - B43_IRQ_TXFIFO_FLUSH_OK | \ - B43_IRQ_NOISESAMPLE_OK | \ - B43_IRQ_UCODE_DEBUG | \ - B43_IRQ_RFKILL | \ - B43_IRQ_TX_OK) - -/* The firmware register to fetch the debug-IRQ reason from. */ -#define B43_DEBUGIRQ_REASON_REG 63 -/* Debug-IRQ reasons. */ -#define B43_DEBUGIRQ_PANIC 0 /* The firmware panic'ed */ -#define B43_DEBUGIRQ_DUMP_SHM 1 /* Dump shared SHM */ -#define B43_DEBUGIRQ_DUMP_REGS 2 /* Dump the microcode registers */ -#define B43_DEBUGIRQ_MARKER 3 /* A "marker" was thrown by the firmware. */ -#define B43_DEBUGIRQ_ACK 0xFFFF /* The host writes that to ACK the IRQ */ - -/* The firmware register that contains the "marker" line. */ -#define B43_MARKER_ID_REG 2 -#define B43_MARKER_LINE_REG 3 - -/* The firmware register to fetch the panic reason from. */ -#define B43_FWPANIC_REASON_REG 3 -/* Firmware panic reason codes */ -#define B43_FWPANIC_DIE 0 /* Firmware died. Don't auto-restart it. */ -#define B43_FWPANIC_RESTART 1 /* Firmware died. Schedule a controller reset. */ - -/* The firmware register that contains the watchdog counter. */ -#define B43_WATCHDOG_REG 1 - -/* Device specific rate values. - * The actual values defined here are (rate_in_mbps * 2). - * Some code depends on this. Don't change it. */ -#define B43_CCK_RATE_1MB 0x02 -#define B43_CCK_RATE_2MB 0x04 -#define B43_CCK_RATE_5MB 0x0B -#define B43_CCK_RATE_11MB 0x16 -#define B43_OFDM_RATE_6MB 0x0C -#define B43_OFDM_RATE_9MB 0x12 -#define B43_OFDM_RATE_12MB 0x18 -#define B43_OFDM_RATE_18MB 0x24 -#define B43_OFDM_RATE_24MB 0x30 -#define B43_OFDM_RATE_36MB 0x48 -#define B43_OFDM_RATE_48MB 0x60 -#define B43_OFDM_RATE_54MB 0x6C -/* Convert a b43 rate value to a rate in 100kbps */ -#define B43_RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) - -#define B43_DEFAULT_SHORT_RETRY_LIMIT 7 -#define B43_DEFAULT_LONG_RETRY_LIMIT 4 - -#define B43_PHY_TX_BADNESS_LIMIT 1000 - -/* Max size of a security key */ -#define B43_SEC_KEYSIZE 16 -/* Max number of group keys */ -#define B43_NR_GROUP_KEYS 4 -/* Max number of pairwise keys */ -#define B43_NR_PAIRWISE_KEYS 50 -/* Security algorithms. */ -enum { - B43_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */ - B43_SEC_ALGO_WEP40, - B43_SEC_ALGO_TKIP, - B43_SEC_ALGO_AES, - B43_SEC_ALGO_WEP104, - B43_SEC_ALGO_AES_LEGACY, -}; - -struct b43_dmaring; - -/* The firmware file header */ -#define B43_FW_TYPE_UCODE 'u' -#define B43_FW_TYPE_PCM 'p' -#define B43_FW_TYPE_IV 'i' -struct b43_fw_header { - /* File type */ - u8 type; - /* File format version */ - u8 ver; - u8 __padding[2]; - /* Size of the data. For ucode and PCM this is in bytes. - * For IV this is number-of-ivs. */ - __be32 size; -} __packed; - -/* Initial Value file format */ -#define B43_IV_OFFSET_MASK 0x7FFF -#define B43_IV_32BIT 0x8000 -struct b43_iv { - __be16 offset_size; - union { - __be16 d16; - __be32 d32; - } data __packed; -} __packed; - - -/* Data structures for DMA transmission, per 80211 core. */ -struct b43_dma { - struct b43_dmaring *tx_ring_AC_BK; /* Background */ - struct b43_dmaring *tx_ring_AC_BE; /* Best Effort */ - struct b43_dmaring *tx_ring_AC_VI; /* Video */ - struct b43_dmaring *tx_ring_AC_VO; /* Voice */ - struct b43_dmaring *tx_ring_mcast; /* Multicast */ - - struct b43_dmaring *rx_ring; - - u32 translation; /* Routing bits */ - bool translation_in_low; /* Should translation bit go into low addr? */ - bool parity; /* Check for parity */ -}; - -struct b43_pio_txqueue; -struct b43_pio_rxqueue; - -/* Data structures for PIO transmission, per 80211 core. */ -struct b43_pio { - struct b43_pio_txqueue *tx_queue_AC_BK; /* Background */ - struct b43_pio_txqueue *tx_queue_AC_BE; /* Best Effort */ - struct b43_pio_txqueue *tx_queue_AC_VI; /* Video */ - struct b43_pio_txqueue *tx_queue_AC_VO; /* Voice */ - struct b43_pio_txqueue *tx_queue_mcast; /* Multicast */ - - struct b43_pio_rxqueue *rx_queue; -}; - -/* Context information for a noise calculation (Link Quality). */ -struct b43_noise_calculation { - bool calculation_running; - u8 nr_samples; - s8 samples[8][4]; -}; - -struct b43_stats { - u8 link_noise; -}; - -struct b43_key { - /* If keyconf is NULL, this key is disabled. - * keyconf is a cookie. Don't derefenrence it outside of the set_key - * path, because b43 doesn't own it. */ - struct ieee80211_key_conf *keyconf; - u8 algorithm; -}; - -/* SHM offsets to the QOS data structures for the 4 different queues. */ -#define B43_QOS_QUEUE_NUM 4 -#define B43_QOS_PARAMS(queue) (B43_SHM_SH_EDCFQ + \ - (B43_NR_QOSPARAMS * sizeof(u16) * (queue))) -#define B43_QOS_BACKGROUND B43_QOS_PARAMS(0) -#define B43_QOS_BESTEFFORT B43_QOS_PARAMS(1) -#define B43_QOS_VIDEO B43_QOS_PARAMS(2) -#define B43_QOS_VOICE B43_QOS_PARAMS(3) - -/* QOS parameter hardware data structure offsets. */ -#define B43_NR_QOSPARAMS 16 -enum { - B43_QOSPARAM_TXOP = 0, - B43_QOSPARAM_CWMIN, - B43_QOSPARAM_CWMAX, - B43_QOSPARAM_CWCUR, - B43_QOSPARAM_AIFS, - B43_QOSPARAM_BSLOTS, - B43_QOSPARAM_REGGAP, - B43_QOSPARAM_STATUS, -}; - -/* QOS parameters for a queue. */ -struct b43_qos_params { - /* The QOS parameters */ - struct ieee80211_tx_queue_params p; -}; - -struct b43_wl; - -/* The type of the firmware file. */ -enum b43_firmware_file_type { - B43_FWTYPE_PROPRIETARY, - B43_FWTYPE_OPENSOURCE, - B43_NR_FWTYPES, -}; - -/* Context data for fetching firmware. */ -struct b43_request_fw_context { - /* The device we are requesting the fw for. */ - struct b43_wldev *dev; - /* a pointer to the firmware object */ - const struct firmware *blob; - /* The type of firmware to request. */ - enum b43_firmware_file_type req_type; - /* Error messages for each firmware type. */ - char errors[B43_NR_FWTYPES][128]; - /* Temporary buffer for storing the firmware name. */ - char fwname[64]; - /* A fatal error occurred while requesting. Firmware request - * can not continue, as any other request will also fail. */ - int fatal_failure; -}; - -/* In-memory representation of a cached microcode file. */ -struct b43_firmware_file { - const char *filename; - const struct firmware *data; - /* Type of the firmware file name. Note that this does only indicate - * the type by the firmware name. NOT the file contents. - * If you want to check for proprietary vs opensource, use (struct b43_firmware)->opensource - * instead! The (struct b43_firmware)->opensource flag is derived from the actual firmware - * binary code, not just the filename. - */ - enum b43_firmware_file_type type; -}; - -enum b43_firmware_hdr_format { - B43_FW_HDR_598, - B43_FW_HDR_410, - B43_FW_HDR_351, -}; - -/* Pointers to the firmware data and meta information about it. */ -struct b43_firmware { - /* Microcode */ - struct b43_firmware_file ucode; - /* PCM code */ - struct b43_firmware_file pcm; - /* Initial MMIO values for the firmware */ - struct b43_firmware_file initvals; - /* Initial MMIO values for the firmware, band-specific */ - struct b43_firmware_file initvals_band; - - /* Firmware revision */ - u16 rev; - /* Firmware patchlevel */ - u16 patch; - - /* Format of header used by firmware */ - enum b43_firmware_hdr_format hdr_format; - - /* Set to true, if we are using an opensource firmware. - * Use this to check for proprietary vs opensource. */ - bool opensource; - /* Set to true, if the core needs a PCM firmware, but - * we failed to load one. This is always false for - * core rev > 10, as these don't need PCM firmware. */ - bool pcm_request_failed; -}; - -enum b43_band { - B43_BAND_2G = 0, - B43_BAND_5G_LO = 1, - B43_BAND_5G_MI = 2, - B43_BAND_5G_HI = 3, -}; - -/* Device (802.11 core) initialization status. */ -enum { - B43_STAT_UNINIT = 0, /* Uninitialized. */ - B43_STAT_INITIALIZED = 1, /* Initialized, but not started, yet. */ - B43_STAT_STARTED = 2, /* Up and running. */ -}; -#define b43_status(wldev) atomic_read(&(wldev)->__init_status) -#define b43_set_status(wldev, stat) do { \ - atomic_set(&(wldev)->__init_status, (stat)); \ - smp_wmb(); \ - } while (0) - -/* Data structure for one wireless device (802.11 core) */ -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. */ - atomic_t __init_status; - - bool bad_frames_preempt; /* Use "Bad Frames Preemption" (default off) */ - bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM) */ - bool radio_hw_enable; /* saved state of radio hardware enabled state */ - bool qos_enabled; /* TRUE, if QoS is used. */ - bool hwcrypto_enabled; /* TRUE, if HW crypto acceleration is enabled. */ - bool use_pio; /* TRUE if next init should use PIO */ - - /* PHY/Radio device. */ - struct b43_phy phy; - - union { - /* DMA engines. */ - struct b43_dma dma; - /* PIO engines. */ - struct b43_pio pio; - }; - /* Use b43_using_pio_transfers() to check whether we are using - * DMA or PIO data transfers. */ - bool __using_pio_transfers; - - /* Various statistics about the physical device. */ - struct b43_stats stats; - - /* Reason code of the last interrupt. */ - u32 irq_reason; - u32 dma_reason[6]; - /* The currently active generic-interrupt mask. */ - u32 irq_mask; - - /* Link Quality calculation context. */ - struct b43_noise_calculation noisecalc; - /* if > 0 MAC is suspended. if == 0 MAC is enabled. */ - int mac_suspended; - - /* Periodic tasks */ - struct delayed_work periodic_work; - unsigned int periodic_state; - - struct work_struct restart_work; - - /* encryption/decryption */ - u16 ktp; /* Key table pointer */ - struct b43_key key[B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS]; - - /* Firmware data */ - struct b43_firmware fw; - - /* Devicelist in struct b43_wl (all 802.11 cores) */ - struct list_head list; - - /* Debugging stuff follows. */ -#ifdef CONFIG_B43_DEBUG - struct b43_dfsentry *dfsentry; - unsigned int irq_count; - unsigned int irq_bit_count[32]; - unsigned int tx_count; - unsigned int rx_count; -#endif -}; - -/* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */ -struct b43_wl { - /* Pointer to the active wireless device on this chip */ - struct b43_wldev *current_dev; - /* Pointer to the ieee80211 hardware data structure */ - struct ieee80211_hw *hw; - - /* Global driver mutex. Every operation must run with this mutex locked. */ - struct mutex mutex; - /* Hard-IRQ spinlock. This lock protects things used in the hard-IRQ - * handler, only. This basically is just the IRQ mask register. */ - spinlock_t hardirq_lock; - - /* Set this if we call ieee80211_register_hw() and check if we call - * ieee80211_unregister_hw(). */ - bool hw_registred; - - /* We can only have one operating interface (802.11 core) - * at a time. General information about this interface follows. - */ - - struct ieee80211_vif *vif; - /* The MAC address of the operating interface. */ - u8 mac_addr[ETH_ALEN]; - /* Current BSSID */ - u8 bssid[ETH_ALEN]; - /* Interface type. (NL80211_IFTYPE_XXX) */ - int if_type; - /* Is the card operating in AP, STA or IBSS mode? */ - bool operating; - /* filter flags */ - unsigned int filter_flags; - /* Stats about the wireless interface */ - struct ieee80211_low_level_stats ieee_stats; - -#ifdef CONFIG_B43_HWRNG - struct hwrng rng; - bool rng_initialized; - char rng_name[30 + 1]; -#endif /* CONFIG_B43_HWRNG */ - - bool radiotap_enabled; - bool radio_enabled; - - /* The beacon we are currently using (AP or IBSS mode). */ - struct sk_buff *current_beacon; - bool beacon0_uploaded; - bool beacon1_uploaded; - bool beacon_templates_virgin; /* Never wrote the templates? */ - struct work_struct beacon_update_trigger; - spinlock_t beacon_lock; - - /* The current QOS parameters for the 4 queues. */ - struct b43_qos_params qos_params[B43_QOS_QUEUE_NUM]; - - /* Work for adjustment of the transmission power. - * This is scheduled when we determine that the actual TX output - * power doesn't match what we want. */ - struct work_struct txpower_adjust_work; - - /* Packet transmit work */ - struct work_struct tx_work; - - /* Queue of packets to be transmitted. */ - struct sk_buff_head tx_queue[B43_QOS_QUEUE_NUM]; - - /* Flag that implement the queues stopping. */ - bool tx_queue_stopped[B43_QOS_QUEUE_NUM]; - - /* firmware loading work */ - struct work_struct firmware_load; - - /* The device LEDs. */ - struct b43_leds leds; - - /* Kmalloc'ed scratch space for PIO TX/RX. Protected by wl->mutex. */ - u8 pio_scratchspace[118] __attribute__((__aligned__(8))); - u8 pio_tailspace[4] __attribute__((__aligned__(8))); -}; - -static inline struct b43_wl *hw_to_b43_wl(struct ieee80211_hw *hw) -{ - return hw->priv; -} - -static inline struct b43_wldev *dev_to_b43_wldev(struct device *dev) -{ - struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); - return ssb_get_drvdata(ssb_dev); -} - -/* Is the device operating in a specified mode (NL80211_IFTYPE_XXX). */ -static inline int b43_is_mode(struct b43_wl *wl, int type) -{ - return (wl->operating && wl->if_type == type); -} - -/** - * b43_current_band - Returns the currently used band. - * Returns one of IEEE80211_BAND_2GHZ and IEEE80211_BAND_5GHZ. - */ -static inline enum ieee80211_band b43_current_band(struct b43_wl *wl) -{ - return wl->hw->conf.chandef.chan->band; -} - -static inline int b43_bus_may_powerdown(struct b43_wldev *wldev) -{ - return wldev->dev->bus_may_powerdown(wldev->dev); -} -static inline int b43_bus_powerup(struct b43_wldev *wldev, bool dynamic_pctl) -{ - return wldev->dev->bus_powerup(wldev->dev, dynamic_pctl); -} -static inline int b43_device_is_enabled(struct b43_wldev *wldev) -{ - return wldev->dev->device_is_enabled(wldev->dev); -} -static inline void b43_device_enable(struct b43_wldev *wldev, - u32 core_specific_flags) -{ - wldev->dev->device_enable(wldev->dev, core_specific_flags); -} -static inline void b43_device_disable(struct b43_wldev *wldev, - u32 core_specific_flags) -{ - wldev->dev->device_disable(wldev->dev, core_specific_flags); -} - -static inline u16 b43_read16(struct b43_wldev *dev, u16 offset) -{ - return dev->dev->read16(dev->dev, offset); -} - -static inline void b43_write16(struct b43_wldev *dev, u16 offset, u16 value) -{ - dev->dev->write16(dev->dev, offset, value); -} - -/* To optimize this check for flush_writes on BCM47XX_BCMA only. */ -static inline void b43_write16f(struct b43_wldev *dev, u16 offset, u16 value) -{ - b43_write16(dev, offset, value); -#if defined(CONFIG_BCM47XX_BCMA) - if (dev->dev->flush_writes) - b43_read16(dev, offset); -#endif -} - -static inline void b43_maskset16(struct b43_wldev *dev, u16 offset, u16 mask, - u16 set) -{ - b43_write16(dev, offset, (b43_read16(dev, offset) & mask) | set); -} - -static inline u32 b43_read32(struct b43_wldev *dev, u16 offset) -{ - return dev->dev->read32(dev->dev, offset); -} - -static inline void b43_write32(struct b43_wldev *dev, u16 offset, u32 value) -{ - dev->dev->write32(dev->dev, offset, value); -} - -static inline void b43_maskset32(struct b43_wldev *dev, u16 offset, u32 mask, - u32 set) -{ - b43_write32(dev, offset, (b43_read32(dev, offset) & mask) | set); -} - -static inline void b43_block_read(struct b43_wldev *dev, void *buffer, - size_t count, u16 offset, u8 reg_width) -{ - dev->dev->block_read(dev->dev, buffer, count, offset, reg_width); -} - -static inline void b43_block_write(struct b43_wldev *dev, const void *buffer, - size_t count, u16 offset, u8 reg_width) -{ - dev->dev->block_write(dev->dev, buffer, count, offset, reg_width); -} - -static inline bool b43_using_pio_transfers(struct b43_wldev *dev) -{ - return dev->__using_pio_transfers; -} - -/* Message printing */ -__printf(2, 3) void b43info(struct b43_wl *wl, const char *fmt, ...); -__printf(2, 3) void b43err(struct b43_wl *wl, const char *fmt, ...); -__printf(2, 3) void b43warn(struct b43_wl *wl, const char *fmt, ...); -__printf(2, 3) void b43dbg(struct b43_wl *wl, const char *fmt, ...); - - -/* A WARN_ON variant that vanishes when b43 debugging is disabled. - * This _also_ evaluates the arg with debugging disabled. */ -#if B43_DEBUG -# define B43_WARN_ON(x) WARN_ON(x) -#else -static inline bool __b43_warn_on_dummy(bool x) { return x; } -# define B43_WARN_ON(x) __b43_warn_on_dummy(unlikely(!!(x))) -#endif - -/* Convert an integer to a Q5.2 value */ -#define INT_TO_Q52(i) ((i) << 2) -/* Convert a Q5.2 value to an integer (precision loss!) */ -#define Q52_TO_INT(q52) ((q52) >> 2) -/* Macros for printing a value in Q5.2 format */ -#define Q52_FMT "%u.%u" -#define Q52_ARG(q52) Q52_TO_INT(q52), ((((q52) & 0x3) * 100) / 4) - -#endif /* B43_H_ */ diff --git a/drivers/net/wireless/b43/bus.c b/drivers/net/wireless/b43/bus.c deleted file mode 100644 index 17d16a391fe6..000000000000 --- a/drivers/net/wireless/b43/bus.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - - Broadcom B43 wireless driver - Bus abstraction layer - - Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#ifdef CONFIG_BCM47XX_BCMA -#include -#endif - -#include "b43.h" -#include "bus.h" - -/* BCMA */ -#ifdef CONFIG_B43_BCMA -static int b43_bus_bcma_bus_may_powerdown(struct b43_bus_dev *dev) -{ - return 0; /* bcma_bus_may_powerdown(dev->bdev->bus); */ -} -static int b43_bus_bcma_bus_powerup(struct b43_bus_dev *dev, - bool dynamic_pctl) -{ - return 0; /* bcma_bus_powerup(dev->sdev->bus, dynamic_pctl); */ -} -static int b43_bus_bcma_device_is_enabled(struct b43_bus_dev *dev) -{ - return bcma_core_is_enabled(dev->bdev); -} -static void b43_bus_bcma_device_enable(struct b43_bus_dev *dev, - u32 core_specific_flags) -{ - bcma_core_enable(dev->bdev, core_specific_flags); -} -static void b43_bus_bcma_device_disable(struct b43_bus_dev *dev, - u32 core_specific_flags) -{ - bcma_core_disable(dev->bdev, core_specific_flags); -} -static u16 b43_bus_bcma_read16(struct b43_bus_dev *dev, u16 offset) -{ - return bcma_read16(dev->bdev, offset); -} -static u32 b43_bus_bcma_read32(struct b43_bus_dev *dev, u16 offset) -{ - return bcma_read32(dev->bdev, offset); -} -static -void b43_bus_bcma_write16(struct b43_bus_dev *dev, u16 offset, u16 value) -{ - bcma_write16(dev->bdev, offset, value); -} -static -void b43_bus_bcma_write32(struct b43_bus_dev *dev, u16 offset, u32 value) -{ - bcma_write32(dev->bdev, offset, value); -} -static -void b43_bus_bcma_block_read(struct b43_bus_dev *dev, void *buffer, - size_t count, u16 offset, u8 reg_width) -{ - bcma_block_read(dev->bdev, buffer, count, offset, reg_width); -} -static -void b43_bus_bcma_block_write(struct b43_bus_dev *dev, const void *buffer, - size_t count, u16 offset, u8 reg_width) -{ - bcma_block_write(dev->bdev, buffer, count, offset, reg_width); -} - -struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core) -{ - struct b43_bus_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return NULL; - - dev->bus_type = B43_BUS_BCMA; - dev->bdev = core; - - dev->bus_may_powerdown = b43_bus_bcma_bus_may_powerdown; - dev->bus_powerup = b43_bus_bcma_bus_powerup; - dev->device_is_enabled = b43_bus_bcma_device_is_enabled; - dev->device_enable = b43_bus_bcma_device_enable; - dev->device_disable = b43_bus_bcma_device_disable; - - dev->read16 = b43_bus_bcma_read16; - dev->read32 = b43_bus_bcma_read32; - dev->write16 = b43_bus_bcma_write16; - dev->write32 = b43_bus_bcma_write32; - dev->block_read = b43_bus_bcma_block_read; - dev->block_write = b43_bus_bcma_block_write; -#ifdef CONFIG_BCM47XX_BCMA - if (b43_bus_host_is_pci(dev) && - bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA && - bcm47xx_bus.bcma.bus.chipinfo.id == BCMA_CHIP_ID_BCM4716) - dev->flush_writes = true; -#endif - - dev->dev = &core->dev; - dev->dma_dev = core->dma_dev; - dev->irq = core->irq; - - dev->board_vendor = core->bus->boardinfo.vendor; - dev->board_type = core->bus->boardinfo.type; - dev->board_rev = core->bus->sprom.board_rev; - - dev->chip_id = core->bus->chipinfo.id; - dev->chip_rev = core->bus->chipinfo.rev; - dev->chip_pkg = core->bus->chipinfo.pkg; - - dev->bus_sprom = &core->bus->sprom; - - dev->core_id = core->id.id; - dev->core_rev = core->id.rev; - - return dev; -} -#endif /* CONFIG_B43_BCMA */ - -/* SSB */ -#ifdef CONFIG_B43_SSB -static int b43_bus_ssb_bus_may_powerdown(struct b43_bus_dev *dev) -{ - return ssb_bus_may_powerdown(dev->sdev->bus); -} -static int b43_bus_ssb_bus_powerup(struct b43_bus_dev *dev, - bool dynamic_pctl) -{ - return ssb_bus_powerup(dev->sdev->bus, dynamic_pctl); -} -static int b43_bus_ssb_device_is_enabled(struct b43_bus_dev *dev) -{ - return ssb_device_is_enabled(dev->sdev); -} -static void b43_bus_ssb_device_enable(struct b43_bus_dev *dev, - u32 core_specific_flags) -{ - ssb_device_enable(dev->sdev, core_specific_flags); -} -static void b43_bus_ssb_device_disable(struct b43_bus_dev *dev, - u32 core_specific_flags) -{ - ssb_device_disable(dev->sdev, core_specific_flags); -} - -static u16 b43_bus_ssb_read16(struct b43_bus_dev *dev, u16 offset) -{ - return ssb_read16(dev->sdev, offset); -} -static u32 b43_bus_ssb_read32(struct b43_bus_dev *dev, u16 offset) -{ - return ssb_read32(dev->sdev, offset); -} -static void b43_bus_ssb_write16(struct b43_bus_dev *dev, u16 offset, u16 value) -{ - ssb_write16(dev->sdev, offset, value); -} -static void b43_bus_ssb_write32(struct b43_bus_dev *dev, u16 offset, u32 value) -{ - ssb_write32(dev->sdev, offset, value); -} -static void b43_bus_ssb_block_read(struct b43_bus_dev *dev, void *buffer, - size_t count, u16 offset, u8 reg_width) -{ - ssb_block_read(dev->sdev, buffer, count, offset, reg_width); -} -static -void b43_bus_ssb_block_write(struct b43_bus_dev *dev, const void *buffer, - size_t count, u16 offset, u8 reg_width) -{ - ssb_block_write(dev->sdev, buffer, count, offset, reg_width); -} - -struct b43_bus_dev *b43_bus_dev_ssb_init(struct ssb_device *sdev) -{ - struct b43_bus_dev *dev; - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return NULL; - - dev->bus_type = B43_BUS_SSB; - dev->sdev = sdev; - - dev->bus_may_powerdown = b43_bus_ssb_bus_may_powerdown; - dev->bus_powerup = b43_bus_ssb_bus_powerup; - dev->device_is_enabled = b43_bus_ssb_device_is_enabled; - dev->device_enable = b43_bus_ssb_device_enable; - dev->device_disable = b43_bus_ssb_device_disable; - - dev->read16 = b43_bus_ssb_read16; - dev->read32 = b43_bus_ssb_read32; - dev->write16 = b43_bus_ssb_write16; - dev->write32 = b43_bus_ssb_write32; - dev->block_read = b43_bus_ssb_block_read; - dev->block_write = b43_bus_ssb_block_write; - - dev->dev = sdev->dev; - dev->dma_dev = sdev->dma_dev; - dev->irq = sdev->irq; - - dev->board_vendor = sdev->bus->boardinfo.vendor; - dev->board_type = sdev->bus->boardinfo.type; - dev->board_rev = sdev->bus->sprom.board_rev; - - dev->chip_id = sdev->bus->chip_id; - dev->chip_rev = sdev->bus->chip_rev; - dev->chip_pkg = sdev->bus->chip_package; - - dev->bus_sprom = &sdev->bus->sprom; - - dev->core_id = sdev->id.coreid; - dev->core_rev = sdev->id.revision; - - return dev; -} -#endif /* CONFIG_B43_SSB */ - -void *b43_bus_get_wldev(struct b43_bus_dev *dev) -{ - switch (dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - return bcma_get_drvdata(dev->bdev); -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - return ssb_get_drvdata(dev->sdev); -#endif - } - return NULL; -} - -void b43_bus_set_wldev(struct b43_bus_dev *dev, void *wldev) -{ - switch (dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - bcma_set_drvdata(dev->bdev, wldev); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - ssb_set_drvdata(dev->sdev, wldev); - break; -#endif - } -} diff --git a/drivers/net/wireless/b43/bus.h b/drivers/net/wireless/b43/bus.h deleted file mode 100644 index 256c2c17939a..000000000000 --- a/drivers/net/wireless/b43/bus.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef B43_BUS_H_ -#define B43_BUS_H_ - -enum b43_bus_type { -#ifdef CONFIG_B43_BCMA - B43_BUS_BCMA, -#endif -#ifdef CONFIG_B43_SSB - B43_BUS_SSB, -#endif -}; - -struct b43_bus_dev { - enum b43_bus_type bus_type; - union { - struct bcma_device *bdev; - struct ssb_device *sdev; - }; - - int (*bus_may_powerdown)(struct b43_bus_dev *dev); - int (*bus_powerup)(struct b43_bus_dev *dev, bool dynamic_pctl); - int (*device_is_enabled)(struct b43_bus_dev *dev); - void (*device_enable)(struct b43_bus_dev *dev, - u32 core_specific_flags); - void (*device_disable)(struct b43_bus_dev *dev, - u32 core_specific_flags); - - u16 (*read16)(struct b43_bus_dev *dev, u16 offset); - u32 (*read32)(struct b43_bus_dev *dev, u16 offset); - void (*write16)(struct b43_bus_dev *dev, u16 offset, u16 value); - void (*write32)(struct b43_bus_dev *dev, u16 offset, u32 value); - void (*block_read)(struct b43_bus_dev *dev, void *buffer, - size_t count, u16 offset, u8 reg_width); - void (*block_write)(struct b43_bus_dev *dev, const void *buffer, - size_t count, u16 offset, u8 reg_width); - bool flush_writes; - - struct device *dev; - struct device *dma_dev; - unsigned int irq; - - u16 board_vendor; - u16 board_type; - u16 board_rev; - - u16 chip_id; - u8 chip_rev; - u8 chip_pkg; - - struct ssb_sprom *bus_sprom; - - u16 core_id; - u8 core_rev; -}; - -static inline bool b43_bus_host_is_pcmcia(struct b43_bus_dev *dev) -{ -#ifdef CONFIG_B43_SSB - return (dev->bus_type == B43_BUS_SSB && - dev->sdev->bus->bustype == SSB_BUSTYPE_PCMCIA); -#else - return false; -#endif -}; - -static inline bool b43_bus_host_is_pci(struct b43_bus_dev *dev) -{ -#ifdef CONFIG_B43_BCMA - if (dev->bus_type == B43_BUS_BCMA) - return (dev->bdev->bus->hosttype == BCMA_HOSTTYPE_PCI); -#endif -#ifdef CONFIG_B43_SSB - if (dev->bus_type == B43_BUS_SSB) - return (dev->sdev->bus->bustype == SSB_BUSTYPE_PCI); -#endif - return false; -} - -static inline bool b43_bus_host_is_sdio(struct b43_bus_dev *dev) -{ -#ifdef CONFIG_B43_SSB - return (dev->bus_type == B43_BUS_SSB && - dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO); -#else - return false; -#endif -} - -struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core); -struct b43_bus_dev *b43_bus_dev_ssb_init(struct ssb_device *sdev); - -void *b43_bus_get_wldev(struct b43_bus_dev *dev); -void b43_bus_set_wldev(struct b43_bus_dev *dev, void *data); - -#endif /* B43_BUS_H_ */ diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/b43/debugfs.c deleted file mode 100644 index b4bcd94aff6c..000000000000 --- a/drivers/net/wireless/b43/debugfs.c +++ /dev/null @@ -1,826 +0,0 @@ -/* - - Broadcom B43 wireless driver - - debugfs driver debugging code - - Copyright (c) 2005-2007 Michael Buesch - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include -#include -#include -#include -#include -#include - -#include "b43.h" -#include "main.h" -#include "debugfs.h" -#include "dma.h" -#include "xmit.h" - - -/* The root directory. */ -static struct dentry *rootdir; - -struct b43_debugfs_fops { - ssize_t (*read)(struct b43_wldev *dev, char *buf, size_t bufsize); - int (*write)(struct b43_wldev *dev, const char *buf, size_t count); - struct file_operations fops; - /* Offset of struct b43_dfs_file in struct b43_dfsentry */ - size_t file_struct_offset; -}; - -static inline -struct b43_dfs_file *fops_to_dfs_file(struct b43_wldev *dev, - const struct b43_debugfs_fops *dfops) -{ - void *p; - - p = dev->dfsentry; - p += dfops->file_struct_offset; - - return p; -} - - -#define fappend(fmt, x...) \ - do { \ - if (bufsize - count) \ - count += snprintf(buf + count, \ - bufsize - count, \ - fmt , ##x); \ - else \ - printk(KERN_ERR "b43: fappend overflow\n"); \ - } while (0) - - -/* The biggest address values for SHM access from the debugfs files. */ -#define B43_MAX_SHM_ROUTING 4 -#define B43_MAX_SHM_ADDR 0xFFFF - -static ssize_t shm16read__read_file(struct b43_wldev *dev, - char *buf, size_t bufsize) -{ - ssize_t count = 0; - unsigned int routing, addr; - u16 val; - - routing = dev->dfsentry->shm16read_routing_next; - addr = dev->dfsentry->shm16read_addr_next; - if ((routing > B43_MAX_SHM_ROUTING) || - (addr > B43_MAX_SHM_ADDR)) - return -EDESTADDRREQ; - - val = b43_shm_read16(dev, routing, addr); - fappend("0x%04X\n", val); - - return count; -} - -static int shm16read__write_file(struct b43_wldev *dev, - const char *buf, size_t count) -{ - unsigned int routing, addr; - int res; - - res = sscanf(buf, "0x%X 0x%X", &routing, &addr); - if (res != 2) - return -EINVAL; - if (routing > B43_MAX_SHM_ROUTING) - return -EADDRNOTAVAIL; - if (addr > B43_MAX_SHM_ADDR) - return -EADDRNOTAVAIL; - if (routing == B43_SHM_SHARED) { - if ((addr % 2) != 0) - return -EADDRNOTAVAIL; - } - - dev->dfsentry->shm16read_routing_next = routing; - dev->dfsentry->shm16read_addr_next = addr; - - return 0; -} - -static int shm16write__write_file(struct b43_wldev *dev, - const char *buf, size_t count) -{ - unsigned int routing, addr, mask, set; - u16 val; - int res; - - res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X", - &routing, &addr, &mask, &set); - if (res != 4) - return -EINVAL; - if (routing > B43_MAX_SHM_ROUTING) - return -EADDRNOTAVAIL; - if (addr > B43_MAX_SHM_ADDR) - return -EADDRNOTAVAIL; - if (routing == B43_SHM_SHARED) { - if ((addr % 2) != 0) - return -EADDRNOTAVAIL; - } - if ((mask > 0xFFFF) || (set > 0xFFFF)) - return -E2BIG; - - if (mask == 0) - val = 0; - else - val = b43_shm_read16(dev, routing, addr); - val &= mask; - val |= set; - b43_shm_write16(dev, routing, addr, val); - - return 0; -} - -static ssize_t shm32read__read_file(struct b43_wldev *dev, - char *buf, size_t bufsize) -{ - ssize_t count = 0; - unsigned int routing, addr; - u32 val; - - routing = dev->dfsentry->shm32read_routing_next; - addr = dev->dfsentry->shm32read_addr_next; - if ((routing > B43_MAX_SHM_ROUTING) || - (addr > B43_MAX_SHM_ADDR)) - return -EDESTADDRREQ; - - val = b43_shm_read32(dev, routing, addr); - fappend("0x%08X\n", val); - - return count; -} - -static int shm32read__write_file(struct b43_wldev *dev, - const char *buf, size_t count) -{ - unsigned int routing, addr; - int res; - - res = sscanf(buf, "0x%X 0x%X", &routing, &addr); - if (res != 2) - return -EINVAL; - if (routing > B43_MAX_SHM_ROUTING) - return -EADDRNOTAVAIL; - if (addr > B43_MAX_SHM_ADDR) - return -EADDRNOTAVAIL; - if (routing == B43_SHM_SHARED) { - if ((addr % 2) != 0) - return -EADDRNOTAVAIL; - } - - dev->dfsentry->shm32read_routing_next = routing; - dev->dfsentry->shm32read_addr_next = addr; - - return 0; -} - -static int shm32write__write_file(struct b43_wldev *dev, - const char *buf, size_t count) -{ - unsigned int routing, addr, mask, set; - u32 val; - int res; - - res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X", - &routing, &addr, &mask, &set); - if (res != 4) - return -EINVAL; - if (routing > B43_MAX_SHM_ROUTING) - return -EADDRNOTAVAIL; - if (addr > B43_MAX_SHM_ADDR) - return -EADDRNOTAVAIL; - if (routing == B43_SHM_SHARED) { - if ((addr % 2) != 0) - return -EADDRNOTAVAIL; - } - if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF)) - return -E2BIG; - - if (mask == 0) - val = 0; - else - val = b43_shm_read32(dev, routing, addr); - val &= mask; - val |= set; - b43_shm_write32(dev, routing, addr, val); - - return 0; -} - -/* The biggest MMIO address that we allow access to from the debugfs files. */ -#define B43_MAX_MMIO_ACCESS (0xF00 - 1) - -static ssize_t mmio16read__read_file(struct b43_wldev *dev, - char *buf, size_t bufsize) -{ - ssize_t count = 0; - unsigned int addr; - u16 val; - - addr = dev->dfsentry->mmio16read_next; - if (addr > B43_MAX_MMIO_ACCESS) - return -EDESTADDRREQ; - - val = b43_read16(dev, addr); - fappend("0x%04X\n", val); - - return count; -} - -static int mmio16read__write_file(struct b43_wldev *dev, - const char *buf, size_t count) -{ - unsigned int addr; - int res; - - res = sscanf(buf, "0x%X", &addr); - if (res != 1) - return -EINVAL; - if (addr > B43_MAX_MMIO_ACCESS) - return -EADDRNOTAVAIL; - if ((addr % 2) != 0) - return -EINVAL; - - dev->dfsentry->mmio16read_next = addr; - - return 0; -} - -static int mmio16write__write_file(struct b43_wldev *dev, - const char *buf, size_t count) -{ - unsigned int addr, mask, set; - int res; - u16 val; - - res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set); - if (res != 3) - return -EINVAL; - if (addr > B43_MAX_MMIO_ACCESS) - return -EADDRNOTAVAIL; - if ((mask > 0xFFFF) || (set > 0xFFFF)) - return -E2BIG; - if ((addr % 2) != 0) - return -EINVAL; - - if (mask == 0) - val = 0; - else - val = b43_read16(dev, addr); - val &= mask; - val |= set; - b43_write16(dev, addr, val); - - return 0; -} - -static ssize_t mmio32read__read_file(struct b43_wldev *dev, - char *buf, size_t bufsize) -{ - ssize_t count = 0; - unsigned int addr; - u32 val; - - addr = dev->dfsentry->mmio32read_next; - if (addr > B43_MAX_MMIO_ACCESS) - return -EDESTADDRREQ; - - val = b43_read32(dev, addr); - fappend("0x%08X\n", val); - - return count; -} - -static int mmio32read__write_file(struct b43_wldev *dev, - const char *buf, size_t count) -{ - unsigned int addr; - int res; - - res = sscanf(buf, "0x%X", &addr); - if (res != 1) - return -EINVAL; - if (addr > B43_MAX_MMIO_ACCESS) - return -EADDRNOTAVAIL; - if ((addr % 4) != 0) - return -EINVAL; - - dev->dfsentry->mmio32read_next = addr; - - return 0; -} - -static int mmio32write__write_file(struct b43_wldev *dev, - const char *buf, size_t count) -{ - unsigned int addr, mask, set; - int res; - u32 val; - - res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set); - if (res != 3) - return -EINVAL; - if (addr > B43_MAX_MMIO_ACCESS) - return -EADDRNOTAVAIL; - if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF)) - return -E2BIG; - if ((addr % 4) != 0) - return -EINVAL; - - if (mask == 0) - val = 0; - else - val = b43_read32(dev, addr); - val &= mask; - val |= set; - b43_write32(dev, addr, val); - - return 0; -} - -static ssize_t txstat_read_file(struct b43_wldev *dev, - char *buf, size_t bufsize) -{ - struct b43_txstatus_log *log = &dev->dfsentry->txstatlog; - ssize_t count = 0; - int i, idx; - struct b43_txstatus *stat; - - if (log->end < 0) { - fappend("Nothing transmitted, yet\n"); - goto out; - } - fappend("b43 TX status reports:\n\n" - "index | cookie | seq | phy_stat | frame_count | " - "rts_count | supp_reason | pm_indicated | " - "intermediate | for_ampdu | acked\n" "---\n"); - i = log->end + 1; - idx = 0; - while (1) { - if (i == B43_NR_LOGGED_TXSTATUS) - i = 0; - stat = &(log->log[i]); - if (stat->cookie) { - fappend("%03d | " - "0x%04X | 0x%04X | 0x%02X | " - "0x%X | 0x%X | " - "%u | %u | " - "%u | %u | %u\n", - idx, - stat->cookie, stat->seq, stat->phy_stat, - stat->frame_count, stat->rts_count, - stat->supp_reason, stat->pm_indicated, - stat->intermediate, stat->for_ampdu, - stat->acked); - idx++; - } - if (i == log->end) - break; - i++; - } -out: - - return count; -} - -static int restart_write_file(struct b43_wldev *dev, - const char *buf, size_t count) -{ - int err = 0; - - if (count > 0 && buf[0] == '1') { - b43_controller_restart(dev, "manually restarted"); - } else - err = -EINVAL; - - return err; -} - -static unsigned long calc_expire_secs(unsigned long now, - unsigned long time, - unsigned long expire) -{ - expire = time + expire; - - if (time_after(now, expire)) - return 0; /* expired */ - if (expire < now) { - /* jiffies wrapped */ - expire -= MAX_JIFFY_OFFSET; - now -= MAX_JIFFY_OFFSET; - } - B43_WARN_ON(expire < now); - - return (expire - now) / HZ; -} - -static ssize_t loctls_read_file(struct b43_wldev *dev, - char *buf, size_t bufsize) -{ - ssize_t count = 0; - struct b43_txpower_lo_control *lo; - int i, err = 0; - struct b43_lo_calib *cal; - unsigned long now = jiffies; - struct b43_phy *phy = &dev->phy; - - if (phy->type != B43_PHYTYPE_G) { - fappend("Device is not a G-PHY\n"); - err = -ENODEV; - goto out; - } - lo = phy->g->lo_control; - fappend("-- Local Oscillator calibration data --\n\n"); - fappend("HW-power-control enabled: %d\n", - dev->phy.hardware_power_control); - fappend("TX Bias: 0x%02X, TX Magn: 0x%02X (expire in %lu sec)\n", - lo->tx_bias, lo->tx_magn, - calc_expire_secs(now, lo->txctl_measured_time, - B43_LO_TXCTL_EXPIRE)); - fappend("Power Vector: 0x%08X%08X (expires in %lu sec)\n", - (unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32), - (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL), - calc_expire_secs(now, lo->pwr_vec_read_time, - B43_LO_PWRVEC_EXPIRE)); - - fappend("\nCalibrated settings:\n"); - list_for_each_entry(cal, &lo->calib_list, list) { - bool active; - - active = (b43_compare_bbatt(&cal->bbatt, &phy->g->bbatt) && - b43_compare_rfatt(&cal->rfatt, &phy->g->rfatt)); - fappend("BB(%d), RF(%d,%d) -> I=%d, Q=%d " - "(expires in %lu sec)%s\n", - cal->bbatt.att, - cal->rfatt.att, cal->rfatt.with_padmix, - cal->ctl.i, cal->ctl.q, - calc_expire_secs(now, cal->calib_time, - B43_LO_CALIB_EXPIRE), - active ? " ACTIVE" : ""); - } - - fappend("\nUsed RF attenuation values: Value(WithPadmix flag)\n"); - for (i = 0; i < lo->rfatt_list.len; i++) { - fappend("%u(%d), ", - lo->rfatt_list.list[i].att, - lo->rfatt_list.list[i].with_padmix); - } - fappend("\n"); - fappend("\nUsed Baseband attenuation values:\n"); - for (i = 0; i < lo->bbatt_list.len; i++) { - fappend("%u, ", - lo->bbatt_list.list[i].att); - } - fappend("\n"); - -out: - return err ? err : count; -} - -#undef fappend - -static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct b43_wldev *dev; - struct b43_debugfs_fops *dfops; - struct b43_dfs_file *dfile; - ssize_t uninitialized_var(ret); - char *buf; - const size_t bufsize = 1024 * 16; /* 16 kiB buffer */ - const size_t buforder = get_order(bufsize); - int err = 0; - - if (!count) - return 0; - dev = file->private_data; - if (!dev) - return -ENODEV; - - mutex_lock(&dev->wl->mutex); - if (b43_status(dev) < B43_STAT_INITIALIZED) { - err = -ENODEV; - goto out_unlock; - } - - dfops = container_of(file->f_op, struct b43_debugfs_fops, fops); - if (!dfops->read) { - err = -ENOSYS; - goto out_unlock; - } - dfile = fops_to_dfs_file(dev, dfops); - - if (!dfile->buffer) { - buf = (char *)__get_free_pages(GFP_KERNEL, buforder); - if (!buf) { - err = -ENOMEM; - goto out_unlock; - } - memset(buf, 0, bufsize); - ret = dfops->read(dev, buf, bufsize); - if (ret <= 0) { - free_pages((unsigned long)buf, buforder); - err = ret; - goto out_unlock; - } - dfile->data_len = ret; - dfile->buffer = buf; - } - - ret = simple_read_from_buffer(userbuf, count, ppos, - dfile->buffer, - dfile->data_len); - if (*ppos >= dfile->data_len) { - free_pages((unsigned long)dfile->buffer, buforder); - dfile->buffer = NULL; - dfile->data_len = 0; - } -out_unlock: - mutex_unlock(&dev->wl->mutex); - - return err ? err : ret; -} - -static ssize_t b43_debugfs_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct b43_wldev *dev; - struct b43_debugfs_fops *dfops; - char *buf; - int err = 0; - - if (!count) - return 0; - if (count > PAGE_SIZE) - return -E2BIG; - dev = file->private_data; - if (!dev) - return -ENODEV; - - mutex_lock(&dev->wl->mutex); - if (b43_status(dev) < B43_STAT_INITIALIZED) { - err = -ENODEV; - goto out_unlock; - } - - dfops = container_of(file->f_op, struct b43_debugfs_fops, fops); - if (!dfops->write) { - err = -ENOSYS; - goto out_unlock; - } - - buf = (char *)get_zeroed_page(GFP_KERNEL); - if (!buf) { - err = -ENOMEM; - goto out_unlock; - } - if (copy_from_user(buf, userbuf, count)) { - err = -EFAULT; - goto out_freepage; - } - err = dfops->write(dev, buf, count); - if (err) - goto out_freepage; - -out_freepage: - free_page((unsigned long)buf); -out_unlock: - mutex_unlock(&dev->wl->mutex); - - return err ? err : count; -} - - -#define B43_DEBUGFS_FOPS(name, _read, _write) \ - static struct b43_debugfs_fops fops_##name = { \ - .read = _read, \ - .write = _write, \ - .fops = { \ - .open = simple_open, \ - .read = b43_debugfs_read, \ - .write = b43_debugfs_write, \ - .llseek = generic_file_llseek, \ - }, \ - .file_struct_offset = offsetof(struct b43_dfsentry, \ - file_##name), \ - } - -B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file); -B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file); -B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file); -B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file); -B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file); -B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file); -B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file); -B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file); -B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL); -B43_DEBUGFS_FOPS(restart, NULL, restart_write_file); -B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL); - - -bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature) -{ - bool enabled; - - enabled = (dev->dfsentry && dev->dfsentry->dyn_debug[feature]); - if (unlikely(enabled)) { - /* Force full debugging messages, if the user enabled - * some dynamic debugging feature. */ - b43_modparam_verbose = B43_VERBOSITY_MAX; - } - - return enabled; -} - -static void b43_remove_dynamic_debug(struct b43_wldev *dev) -{ - struct b43_dfsentry *e = dev->dfsentry; - int i; - - for (i = 0; i < __B43_NR_DYNDBG; i++) - debugfs_remove(e->dyn_debug_dentries[i]); -} - -static void b43_add_dynamic_debug(struct b43_wldev *dev) -{ - struct b43_dfsentry *e = dev->dfsentry; - struct dentry *d; - -#define add_dyn_dbg(name, id, initstate) do { \ - e->dyn_debug[id] = (initstate); \ - d = debugfs_create_bool(name, 0600, e->subdir, \ - &(e->dyn_debug[id])); \ - if (!IS_ERR(d)) \ - e->dyn_debug_dentries[id] = d; \ - } while (0) - - add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, false); - add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, false); - add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, false); - add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, false); - add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, false); - add_dyn_dbg("debug_lo", B43_DBG_LO, false); - add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, false); - add_dyn_dbg("debug_keys", B43_DBG_KEYS, false); - add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, false); - -#undef add_dyn_dbg -} - -void b43_debugfs_add_device(struct b43_wldev *dev) -{ - struct b43_dfsentry *e; - struct b43_txstatus_log *log; - char devdir[16]; - - B43_WARN_ON(!dev); - e = kzalloc(sizeof(*e), GFP_KERNEL); - if (!e) { - b43err(dev->wl, "debugfs: add device OOM\n"); - return; - } - e->dev = dev; - log = &e->txstatlog; - log->log = kcalloc(B43_NR_LOGGED_TXSTATUS, - sizeof(struct b43_txstatus), GFP_KERNEL); - if (!log->log) { - b43err(dev->wl, "debugfs: add device txstatus OOM\n"); - kfree(e); - return; - } - log->end = -1; - - dev->dfsentry = e; - - snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy)); - e->subdir = debugfs_create_dir(devdir, rootdir); - if (!e->subdir || IS_ERR(e->subdir)) { - if (e->subdir == ERR_PTR(-ENODEV)) { - b43dbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not " - "enabled in kernel config\n"); - } else { - b43err(dev->wl, "debugfs: cannot create %s directory\n", - devdir); - } - dev->dfsentry = NULL; - kfree(log->log); - kfree(e); - return; - } - - e->mmio16read_next = 0xFFFF; /* invalid address */ - e->mmio32read_next = 0xFFFF; /* invalid address */ - e->shm16read_routing_next = 0xFFFFFFFF; /* invalid routing */ - e->shm16read_addr_next = 0xFFFFFFFF; /* invalid address */ - e->shm32read_routing_next = 0xFFFFFFFF; /* invalid routing */ - e->shm32read_addr_next = 0xFFFFFFFF; /* invalid address */ - -#define ADD_FILE(name, mode) \ - do { \ - struct dentry *d; \ - d = debugfs_create_file(__stringify(name), \ - mode, e->subdir, dev, \ - &fops_##name.fops); \ - e->file_##name.dentry = NULL; \ - if (!IS_ERR(d)) \ - e->file_##name.dentry = d; \ - } while (0) - - - ADD_FILE(shm16read, 0600); - ADD_FILE(shm16write, 0200); - ADD_FILE(shm32read, 0600); - ADD_FILE(shm32write, 0200); - ADD_FILE(mmio16read, 0600); - ADD_FILE(mmio16write, 0200); - ADD_FILE(mmio32read, 0600); - ADD_FILE(mmio32write, 0200); - ADD_FILE(txstat, 0400); - ADD_FILE(restart, 0200); - ADD_FILE(loctls, 0400); - -#undef ADD_FILE - - b43_add_dynamic_debug(dev); -} - -void b43_debugfs_remove_device(struct b43_wldev *dev) -{ - struct b43_dfsentry *e; - - if (!dev) - return; - e = dev->dfsentry; - if (!e) - return; - b43_remove_dynamic_debug(dev); - - debugfs_remove(e->file_shm16read.dentry); - debugfs_remove(e->file_shm16write.dentry); - debugfs_remove(e->file_shm32read.dentry); - debugfs_remove(e->file_shm32write.dentry); - debugfs_remove(e->file_mmio16read.dentry); - debugfs_remove(e->file_mmio16write.dentry); - debugfs_remove(e->file_mmio32read.dentry); - debugfs_remove(e->file_mmio32write.dentry); - debugfs_remove(e->file_txstat.dentry); - debugfs_remove(e->file_restart.dentry); - debugfs_remove(e->file_loctls.dentry); - - debugfs_remove(e->subdir); - kfree(e->txstatlog.log); - kfree(e); -} - -void b43_debugfs_log_txstat(struct b43_wldev *dev, - const struct b43_txstatus *status) -{ - struct b43_dfsentry *e = dev->dfsentry; - struct b43_txstatus_log *log; - struct b43_txstatus *cur; - int i; - - if (!e) - return; - log = &e->txstatlog; - i = log->end + 1; - if (i == B43_NR_LOGGED_TXSTATUS) - i = 0; - log->end = i; - cur = &(log->log[i]); - memcpy(cur, status, sizeof(*cur)); -} - -void b43_debugfs_init(void) -{ - rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); - if (IS_ERR(rootdir)) - rootdir = NULL; -} - -void b43_debugfs_exit(void) -{ - debugfs_remove(rootdir); -} diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/b43/debugfs.h deleted file mode 100644 index d05377745011..000000000000 --- a/drivers/net/wireless/b43/debugfs.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef B43_DEBUGFS_H_ -#define B43_DEBUGFS_H_ - -struct b43_wldev; -struct b43_txstatus; - -enum b43_dyndbg { /* Dynamic debugging features */ - B43_DBG_XMITPOWER, - B43_DBG_DMAOVERFLOW, - B43_DBG_DMAVERBOSE, - B43_DBG_PWORK_FAST, - B43_DBG_PWORK_STOP, - B43_DBG_LO, - B43_DBG_FIRMWARE, - B43_DBG_KEYS, - B43_DBG_VERBOSESTATS, - __B43_NR_DYNDBG, -}; - -#ifdef CONFIG_B43_DEBUG - -struct dentry; - -#define B43_NR_LOGGED_TXSTATUS 100 - -struct b43_txstatus_log { - /* This structure is protected by wl->mutex */ - - struct b43_txstatus *log; - int end; -}; - -struct b43_dfs_file { - struct dentry *dentry; - char *buffer; - size_t data_len; -}; - -struct b43_dfsentry { - struct b43_wldev *dev; - struct dentry *subdir; - - struct b43_dfs_file file_shm16read; - struct b43_dfs_file file_shm16write; - struct b43_dfs_file file_shm32read; - struct b43_dfs_file file_shm32write; - struct b43_dfs_file file_mmio16read; - struct b43_dfs_file file_mmio16write; - struct b43_dfs_file file_mmio32read; - struct b43_dfs_file file_mmio32write; - struct b43_dfs_file file_txstat; - struct b43_dfs_file file_txpower_g; - struct b43_dfs_file file_restart; - struct b43_dfs_file file_loctls; - - struct b43_txstatus_log txstatlog; - - /* The cached address for the next mmio16read file read */ - u16 mmio16read_next; - /* The cached address for the next mmio32read file read */ - u16 mmio32read_next; - - /* The cached address for the next shm16read file read */ - u32 shm16read_routing_next; - u32 shm16read_addr_next; - /* The cached address for the next shm32read file read */ - u32 shm32read_routing_next; - u32 shm32read_addr_next; - - /* Enabled/Disabled list for the dynamic debugging features. */ - bool dyn_debug[__B43_NR_DYNDBG]; - /* Dentries for the dynamic debugging entries. */ - struct dentry *dyn_debug_dentries[__B43_NR_DYNDBG]; -}; - -bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature); - -void b43_debugfs_init(void); -void b43_debugfs_exit(void); -void b43_debugfs_add_device(struct b43_wldev *dev); -void b43_debugfs_remove_device(struct b43_wldev *dev); -void b43_debugfs_log_txstat(struct b43_wldev *dev, - const struct b43_txstatus *status); - -#else /* CONFIG_B43_DEBUG */ - -static inline bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature) -{ - return false; -} - -static inline void b43_debugfs_init(void) -{ -} -static inline void b43_debugfs_exit(void) -{ -} -static inline void b43_debugfs_add_device(struct b43_wldev *dev) -{ -} -static inline void b43_debugfs_remove_device(struct b43_wldev *dev) -{ -} -static inline void b43_debugfs_log_txstat(struct b43_wldev *dev, - const struct b43_txstatus *status) -{ -} - -#endif /* CONFIG_B43_DEBUG */ - -#endif /* B43_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c deleted file mode 100644 index 6837064908be..000000000000 --- a/drivers/net/wireless/b43/dma.c +++ /dev/null @@ -1,1831 +0,0 @@ -/* - - Broadcom B43 wireless driver - - DMA ringbuffer and descriptor allocation/management - - Copyright (c) 2005, 2006 Michael Buesch - - Some code in this file is derived from the b44.c driver - Copyright (C) 2002 David S. Miller - Copyright (C) Pekka Pietikainen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "dma.h" -#include "main.h" -#include "debugfs.h" -#include "xmit.h" - -#include -#include -#include -#include -#include -#include -#include - - -/* Required number of TX DMA slots per TX frame. - * This currently is 2, because we put the header and the ieee80211 frame - * into separate slots. */ -#define TX_SLOTS_PER_FRAME 2 - -static u32 b43_dma_address(struct b43_dma *dma, dma_addr_t dmaaddr, - enum b43_addrtype addrtype) -{ - u32 uninitialized_var(addr); - - switch (addrtype) { - case B43_DMA_ADDR_LOW: - addr = lower_32_bits(dmaaddr); - if (dma->translation_in_low) { - addr &= ~SSB_DMA_TRANSLATION_MASK; - addr |= dma->translation; - } - break; - case B43_DMA_ADDR_HIGH: - addr = upper_32_bits(dmaaddr); - if (!dma->translation_in_low) { - addr &= ~SSB_DMA_TRANSLATION_MASK; - addr |= dma->translation; - } - break; - case B43_DMA_ADDR_EXT: - if (dma->translation_in_low) - addr = lower_32_bits(dmaaddr); - else - addr = upper_32_bits(dmaaddr); - addr &= SSB_DMA_TRANSLATION_MASK; - addr >>= SSB_DMA_TRANSLATION_SHIFT; - break; - } - - return addr; -} - -/* 32bit DMA ops. */ -static -struct b43_dmadesc_generic *op32_idx2desc(struct b43_dmaring *ring, - int slot, - struct b43_dmadesc_meta **meta) -{ - struct b43_dmadesc32 *desc; - - *meta = &(ring->meta[slot]); - desc = ring->descbase; - desc = &(desc[slot]); - - return (struct b43_dmadesc_generic *)desc; -} - -static void op32_fill_descriptor(struct b43_dmaring *ring, - struct b43_dmadesc_generic *desc, - dma_addr_t dmaaddr, u16 bufsize, - int start, int end, int irq) -{ - struct b43_dmadesc32 *descbase = ring->descbase; - int slot; - u32 ctl; - u32 addr; - u32 addrext; - - slot = (int)(&(desc->dma32) - descbase); - B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); - - addr = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW); - addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT); - - ctl = bufsize & B43_DMA32_DCTL_BYTECNT; - if (slot == ring->nr_slots - 1) - ctl |= B43_DMA32_DCTL_DTABLEEND; - if (start) - ctl |= B43_DMA32_DCTL_FRAMESTART; - if (end) - ctl |= B43_DMA32_DCTL_FRAMEEND; - if (irq) - ctl |= B43_DMA32_DCTL_IRQ; - ctl |= (addrext << B43_DMA32_DCTL_ADDREXT_SHIFT) - & B43_DMA32_DCTL_ADDREXT_MASK; - - desc->dma32.control = cpu_to_le32(ctl); - desc->dma32.address = cpu_to_le32(addr); -} - -static void op32_poke_tx(struct b43_dmaring *ring, int slot) -{ - b43_dma_write(ring, B43_DMA32_TXINDEX, - (u32) (slot * sizeof(struct b43_dmadesc32))); -} - -static void op32_tx_suspend(struct b43_dmaring *ring) -{ - b43_dma_write(ring, B43_DMA32_TXCTL, b43_dma_read(ring, B43_DMA32_TXCTL) - | B43_DMA32_TXSUSPEND); -} - -static void op32_tx_resume(struct b43_dmaring *ring) -{ - b43_dma_write(ring, B43_DMA32_TXCTL, b43_dma_read(ring, B43_DMA32_TXCTL) - & ~B43_DMA32_TXSUSPEND); -} - -static int op32_get_current_rxslot(struct b43_dmaring *ring) -{ - u32 val; - - val = b43_dma_read(ring, B43_DMA32_RXSTATUS); - val &= B43_DMA32_RXDPTR; - - return (val / sizeof(struct b43_dmadesc32)); -} - -static void op32_set_current_rxslot(struct b43_dmaring *ring, int slot) -{ - b43_dma_write(ring, B43_DMA32_RXINDEX, - (u32) (slot * sizeof(struct b43_dmadesc32))); -} - -static const struct b43_dma_ops dma32_ops = { - .idx2desc = op32_idx2desc, - .fill_descriptor = op32_fill_descriptor, - .poke_tx = op32_poke_tx, - .tx_suspend = op32_tx_suspend, - .tx_resume = op32_tx_resume, - .get_current_rxslot = op32_get_current_rxslot, - .set_current_rxslot = op32_set_current_rxslot, -}; - -/* 64bit DMA ops. */ -static -struct b43_dmadesc_generic *op64_idx2desc(struct b43_dmaring *ring, - int slot, - struct b43_dmadesc_meta **meta) -{ - struct b43_dmadesc64 *desc; - - *meta = &(ring->meta[slot]); - desc = ring->descbase; - desc = &(desc[slot]); - - return (struct b43_dmadesc_generic *)desc; -} - -static void op64_fill_descriptor(struct b43_dmaring *ring, - struct b43_dmadesc_generic *desc, - dma_addr_t dmaaddr, u16 bufsize, - int start, int end, int irq) -{ - struct b43_dmadesc64 *descbase = ring->descbase; - int slot; - u32 ctl0 = 0, ctl1 = 0; - u32 addrlo, addrhi; - u32 addrext; - - slot = (int)(&(desc->dma64) - descbase); - B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); - - addrlo = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW); - addrhi = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_HIGH); - addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT); - - if (slot == ring->nr_slots - 1) - ctl0 |= B43_DMA64_DCTL0_DTABLEEND; - if (start) - ctl0 |= B43_DMA64_DCTL0_FRAMESTART; - if (end) - ctl0 |= B43_DMA64_DCTL0_FRAMEEND; - if (irq) - ctl0 |= B43_DMA64_DCTL0_IRQ; - ctl1 |= bufsize & B43_DMA64_DCTL1_BYTECNT; - ctl1 |= (addrext << B43_DMA64_DCTL1_ADDREXT_SHIFT) - & B43_DMA64_DCTL1_ADDREXT_MASK; - - desc->dma64.control0 = cpu_to_le32(ctl0); - desc->dma64.control1 = cpu_to_le32(ctl1); - desc->dma64.address_low = cpu_to_le32(addrlo); - desc->dma64.address_high = cpu_to_le32(addrhi); -} - -static void op64_poke_tx(struct b43_dmaring *ring, int slot) -{ - b43_dma_write(ring, B43_DMA64_TXINDEX, - (u32) (slot * sizeof(struct b43_dmadesc64))); -} - -static void op64_tx_suspend(struct b43_dmaring *ring) -{ - b43_dma_write(ring, B43_DMA64_TXCTL, b43_dma_read(ring, B43_DMA64_TXCTL) - | B43_DMA64_TXSUSPEND); -} - -static void op64_tx_resume(struct b43_dmaring *ring) -{ - b43_dma_write(ring, B43_DMA64_TXCTL, b43_dma_read(ring, B43_DMA64_TXCTL) - & ~B43_DMA64_TXSUSPEND); -} - -static int op64_get_current_rxslot(struct b43_dmaring *ring) -{ - u32 val; - - val = b43_dma_read(ring, B43_DMA64_RXSTATUS); - val &= B43_DMA64_RXSTATDPTR; - - return (val / sizeof(struct b43_dmadesc64)); -} - -static void op64_set_current_rxslot(struct b43_dmaring *ring, int slot) -{ - b43_dma_write(ring, B43_DMA64_RXINDEX, - (u32) (slot * sizeof(struct b43_dmadesc64))); -} - -static const struct b43_dma_ops dma64_ops = { - .idx2desc = op64_idx2desc, - .fill_descriptor = op64_fill_descriptor, - .poke_tx = op64_poke_tx, - .tx_suspend = op64_tx_suspend, - .tx_resume = op64_tx_resume, - .get_current_rxslot = op64_get_current_rxslot, - .set_current_rxslot = op64_set_current_rxslot, -}; - -static inline int free_slots(struct b43_dmaring *ring) -{ - return (ring->nr_slots - ring->used_slots); -} - -static inline int next_slot(struct b43_dmaring *ring, int slot) -{ - B43_WARN_ON(!(slot >= -1 && slot <= ring->nr_slots - 1)); - if (slot == ring->nr_slots - 1) - return 0; - return slot + 1; -} - -static inline int prev_slot(struct b43_dmaring *ring, int slot) -{ - B43_WARN_ON(!(slot >= 0 && slot <= ring->nr_slots - 1)); - if (slot == 0) - return ring->nr_slots - 1; - return slot - 1; -} - -#ifdef CONFIG_B43_DEBUG -static void update_max_used_slots(struct b43_dmaring *ring, - int current_used_slots) -{ - if (current_used_slots <= ring->max_used_slots) - return; - ring->max_used_slots = current_used_slots; - if (b43_debug(ring->dev, B43_DBG_DMAVERBOSE)) { - b43dbg(ring->dev->wl, - "max_used_slots increased to %d on %s ring %d\n", - ring->max_used_slots, - ring->tx ? "TX" : "RX", ring->index); - } -} -#else -static inline - void update_max_used_slots(struct b43_dmaring *ring, int current_used_slots) -{ -} -#endif /* DEBUG */ - -/* Request a slot for usage. */ -static inline int request_slot(struct b43_dmaring *ring) -{ - int slot; - - B43_WARN_ON(!ring->tx); - B43_WARN_ON(ring->stopped); - B43_WARN_ON(free_slots(ring) == 0); - - slot = next_slot(ring, ring->current_slot); - ring->current_slot = slot; - ring->used_slots++; - - update_max_used_slots(ring, ring->used_slots); - - return slot; -} - -static u16 b43_dmacontroller_base(enum b43_dmatype type, int controller_idx) -{ - static const u16 map64[] = { - B43_MMIO_DMA64_BASE0, - B43_MMIO_DMA64_BASE1, - B43_MMIO_DMA64_BASE2, - B43_MMIO_DMA64_BASE3, - B43_MMIO_DMA64_BASE4, - B43_MMIO_DMA64_BASE5, - }; - static const u16 map32[] = { - B43_MMIO_DMA32_BASE0, - B43_MMIO_DMA32_BASE1, - B43_MMIO_DMA32_BASE2, - B43_MMIO_DMA32_BASE3, - B43_MMIO_DMA32_BASE4, - B43_MMIO_DMA32_BASE5, - }; - - if (type == B43_DMA_64BIT) { - B43_WARN_ON(!(controller_idx >= 0 && - controller_idx < ARRAY_SIZE(map64))); - return map64[controller_idx]; - } - B43_WARN_ON(!(controller_idx >= 0 && - controller_idx < ARRAY_SIZE(map32))); - return map32[controller_idx]; -} - -static inline - dma_addr_t map_descbuffer(struct b43_dmaring *ring, - unsigned char *buf, size_t len, int tx) -{ - dma_addr_t dmaaddr; - - if (tx) { - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, DMA_TO_DEVICE); - } else { - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, DMA_FROM_DEVICE); - } - - return dmaaddr; -} - -static inline - void unmap_descbuffer(struct b43_dmaring *ring, - dma_addr_t addr, size_t len, int tx) -{ - if (tx) { - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, DMA_TO_DEVICE); - } else { - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); - } -} - -static inline - void sync_descbuffer_for_cpu(struct b43_dmaring *ring, - dma_addr_t addr, size_t len) -{ - B43_WARN_ON(ring->tx); - dma_sync_single_for_cpu(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); -} - -static inline - void sync_descbuffer_for_device(struct b43_dmaring *ring, - dma_addr_t addr, size_t len) -{ - B43_WARN_ON(ring->tx); - dma_sync_single_for_device(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); -} - -static inline - void free_descriptor_buffer(struct b43_dmaring *ring, - struct b43_dmadesc_meta *meta) -{ - if (meta->skb) { - if (ring->tx) - ieee80211_free_txskb(ring->dev->wl->hw, meta->skb); - else - dev_kfree_skb_any(meta->skb); - meta->skb = NULL; - } -} - -static int alloc_ringmemory(struct b43_dmaring *ring) -{ - /* The specs call for 4K buffers for 30- and 32-bit DMA with 4K - * alignment and 8K buffers for 64-bit DMA with 8K alignment. - * In practice we could use smaller buffers for the latter, but the - * alignment is really important because of the hardware bug. If bit - * 0x00001000 is used in DMA address, some hardware (like BCM4331) - * copies that bit into B43_DMA64_RXSTATUS and we get false values from - * B43_DMA64_RXSTATDPTR. Let's just use 8K buffers even if we don't use - * more than 256 slots for ring. - */ - u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ? - B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE; - - ring->descbase = dma_zalloc_coherent(ring->dev->dev->dma_dev, - ring_mem_size, &(ring->dmabase), - GFP_KERNEL); - if (!ring->descbase) - return -ENOMEM; - - return 0; -} - -static void free_ringmemory(struct b43_dmaring *ring) -{ - u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ? - B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE; - dma_free_coherent(ring->dev->dev->dma_dev, ring_mem_size, - ring->descbase, ring->dmabase); -} - -/* Reset the RX DMA channel */ -static int b43_dmacontroller_rx_reset(struct b43_wldev *dev, u16 mmio_base, - enum b43_dmatype type) -{ - int i; - u32 value; - u16 offset; - - might_sleep(); - - offset = (type == B43_DMA_64BIT) ? B43_DMA64_RXCTL : B43_DMA32_RXCTL; - b43_write32(dev, mmio_base + offset, 0); - for (i = 0; i < 10; i++) { - offset = (type == B43_DMA_64BIT) ? B43_DMA64_RXSTATUS : - B43_DMA32_RXSTATUS; - value = b43_read32(dev, mmio_base + offset); - if (type == B43_DMA_64BIT) { - value &= B43_DMA64_RXSTAT; - if (value == B43_DMA64_RXSTAT_DISABLED) { - i = -1; - break; - } - } else { - value &= B43_DMA32_RXSTATE; - if (value == B43_DMA32_RXSTAT_DISABLED) { - i = -1; - break; - } - } - msleep(1); - } - if (i != -1) { - b43err(dev->wl, "DMA RX reset timed out\n"); - return -ENODEV; - } - - return 0; -} - -/* Reset the TX DMA channel */ -static int b43_dmacontroller_tx_reset(struct b43_wldev *dev, u16 mmio_base, - enum b43_dmatype type) -{ - int i; - u32 value; - u16 offset; - - might_sleep(); - - for (i = 0; i < 10; i++) { - offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXSTATUS : - B43_DMA32_TXSTATUS; - value = b43_read32(dev, mmio_base + offset); - if (type == B43_DMA_64BIT) { - value &= B43_DMA64_TXSTAT; - if (value == B43_DMA64_TXSTAT_DISABLED || - value == B43_DMA64_TXSTAT_IDLEWAIT || - value == B43_DMA64_TXSTAT_STOPPED) - break; - } else { - value &= B43_DMA32_TXSTATE; - if (value == B43_DMA32_TXSTAT_DISABLED || - value == B43_DMA32_TXSTAT_IDLEWAIT || - value == B43_DMA32_TXSTAT_STOPPED) - break; - } - msleep(1); - } - offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXCTL : B43_DMA32_TXCTL; - b43_write32(dev, mmio_base + offset, 0); - for (i = 0; i < 10; i++) { - offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXSTATUS : - B43_DMA32_TXSTATUS; - value = b43_read32(dev, mmio_base + offset); - if (type == B43_DMA_64BIT) { - value &= B43_DMA64_TXSTAT; - if (value == B43_DMA64_TXSTAT_DISABLED) { - i = -1; - break; - } - } else { - value &= B43_DMA32_TXSTATE; - if (value == B43_DMA32_TXSTAT_DISABLED) { - i = -1; - break; - } - } - msleep(1); - } - if (i != -1) { - b43err(dev->wl, "DMA TX reset timed out\n"); - return -ENODEV; - } - /* ensure the reset is completed. */ - msleep(1); - - return 0; -} - -/* Check if a DMA mapping address is invalid. */ -static bool b43_dma_mapping_error(struct b43_dmaring *ring, - dma_addr_t addr, - size_t buffersize, bool dma_to_device) -{ - if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr))) - return true; - - switch (ring->type) { - case B43_DMA_30BIT: - if ((u64)addr + buffersize > (1ULL << 30)) - goto address_error; - break; - case B43_DMA_32BIT: - if ((u64)addr + buffersize > (1ULL << 32)) - goto address_error; - break; - case B43_DMA_64BIT: - /* Currently we can't have addresses beyond - * 64bit in the kernel. */ - break; - } - - /* The address is OK. */ - return false; - -address_error: - /* We can't support this address. Unmap it again. */ - unmap_descbuffer(ring, addr, buffersize, dma_to_device); - - return true; -} - -static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb) -{ - unsigned char *f = skb->data + ring->frameoffset; - - return ((f[0] & f[1] & f[2] & f[3] & f[4] & f[5] & f[6] & f[7]) == 0xFF); -} - -static void b43_poison_rx_buffer(struct b43_dmaring *ring, struct sk_buff *skb) -{ - struct b43_rxhdr_fw4 *rxhdr; - unsigned char *frame; - - /* This poisons the RX buffer to detect DMA failures. */ - - rxhdr = (struct b43_rxhdr_fw4 *)(skb->data); - rxhdr->frame_len = 0; - - B43_WARN_ON(ring->rx_buffersize < ring->frameoffset + sizeof(struct b43_plcp_hdr6) + 2); - frame = skb->data + ring->frameoffset; - memset(frame, 0xFF, sizeof(struct b43_plcp_hdr6) + 2 /* padding */); -} - -static int setup_rx_descbuffer(struct b43_dmaring *ring, - struct b43_dmadesc_generic *desc, - struct b43_dmadesc_meta *meta, gfp_t gfp_flags) -{ - dma_addr_t dmaaddr; - struct sk_buff *skb; - - B43_WARN_ON(ring->tx); - - skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); - if (unlikely(!skb)) - return -ENOMEM; - b43_poison_rx_buffer(ring, skb); - dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); - if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { - /* ugh. try to realloc in zone_dma */ - gfp_flags |= GFP_DMA; - - dev_kfree_skb_any(skb); - - skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); - if (unlikely(!skb)) - return -ENOMEM; - b43_poison_rx_buffer(ring, skb); - dmaaddr = map_descbuffer(ring, skb->data, - ring->rx_buffersize, 0); - if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { - b43err(ring->dev->wl, "RX DMA buffer allocation failed\n"); - dev_kfree_skb_any(skb); - return -EIO; - } - } - - meta->skb = skb; - meta->dmaaddr = dmaaddr; - ring->ops->fill_descriptor(ring, desc, dmaaddr, - ring->rx_buffersize, 0, 0, 0); - - return 0; -} - -/* Allocate the initial descbuffers. - * This is used for an RX ring only. - */ -static int alloc_initial_descbuffers(struct b43_dmaring *ring) -{ - int i, err = -ENOMEM; - struct b43_dmadesc_generic *desc; - struct b43_dmadesc_meta *meta; - - for (i = 0; i < ring->nr_slots; i++) { - desc = ring->ops->idx2desc(ring, i, &meta); - - err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); - if (err) { - b43err(ring->dev->wl, - "Failed to allocate initial descbuffers\n"); - goto err_unwind; - } - } - mb(); - ring->used_slots = ring->nr_slots; - err = 0; - out: - return err; - - err_unwind: - for (i--; i >= 0; i--) { - desc = ring->ops->idx2desc(ring, i, &meta); - - unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); - dev_kfree_skb(meta->skb); - } - goto out; -} - -/* Do initial setup of the DMA controller. - * Reset the controller, write the ring busaddress - * and switch the "enable" bit on. - */ -static int dmacontroller_setup(struct b43_dmaring *ring) -{ - int err = 0; - u32 value; - u32 addrext; - bool parity = ring->dev->dma.parity; - u32 addrlo; - u32 addrhi; - - if (ring->tx) { - if (ring->type == B43_DMA_64BIT) { - u64 ringbase = (u64) (ring->dmabase); - addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); - addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); - addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH); - - value = B43_DMA64_TXENABLE; - value |= (addrext << B43_DMA64_TXADDREXT_SHIFT) - & B43_DMA64_TXADDREXT_MASK; - if (!parity) - value |= B43_DMA64_TXPARITYDISABLE; - b43_dma_write(ring, B43_DMA64_TXCTL, value); - b43_dma_write(ring, B43_DMA64_TXRINGLO, addrlo); - b43_dma_write(ring, B43_DMA64_TXRINGHI, addrhi); - } else { - u32 ringbase = (u32) (ring->dmabase); - addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); - addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); - - value = B43_DMA32_TXENABLE; - value |= (addrext << B43_DMA32_TXADDREXT_SHIFT) - & B43_DMA32_TXADDREXT_MASK; - if (!parity) - value |= B43_DMA32_TXPARITYDISABLE; - b43_dma_write(ring, B43_DMA32_TXCTL, value); - b43_dma_write(ring, B43_DMA32_TXRING, addrlo); - } - } else { - err = alloc_initial_descbuffers(ring); - if (err) - goto out; - if (ring->type == B43_DMA_64BIT) { - u64 ringbase = (u64) (ring->dmabase); - addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); - addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); - addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH); - - value = (ring->frameoffset << B43_DMA64_RXFROFF_SHIFT); - value |= B43_DMA64_RXENABLE; - value |= (addrext << B43_DMA64_RXADDREXT_SHIFT) - & B43_DMA64_RXADDREXT_MASK; - if (!parity) - value |= B43_DMA64_RXPARITYDISABLE; - b43_dma_write(ring, B43_DMA64_RXCTL, value); - b43_dma_write(ring, B43_DMA64_RXRINGLO, addrlo); - b43_dma_write(ring, B43_DMA64_RXRINGHI, addrhi); - b43_dma_write(ring, B43_DMA64_RXINDEX, ring->nr_slots * - sizeof(struct b43_dmadesc64)); - } else { - u32 ringbase = (u32) (ring->dmabase); - addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); - addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); - - value = (ring->frameoffset << B43_DMA32_RXFROFF_SHIFT); - value |= B43_DMA32_RXENABLE; - value |= (addrext << B43_DMA32_RXADDREXT_SHIFT) - & B43_DMA32_RXADDREXT_MASK; - if (!parity) - value |= B43_DMA32_RXPARITYDISABLE; - b43_dma_write(ring, B43_DMA32_RXCTL, value); - b43_dma_write(ring, B43_DMA32_RXRING, addrlo); - b43_dma_write(ring, B43_DMA32_RXINDEX, ring->nr_slots * - sizeof(struct b43_dmadesc32)); - } - } - -out: - return err; -} - -/* Shutdown the DMA controller. */ -static void dmacontroller_cleanup(struct b43_dmaring *ring) -{ - if (ring->tx) { - b43_dmacontroller_tx_reset(ring->dev, ring->mmio_base, - ring->type); - if (ring->type == B43_DMA_64BIT) { - b43_dma_write(ring, B43_DMA64_TXRINGLO, 0); - b43_dma_write(ring, B43_DMA64_TXRINGHI, 0); - } else - b43_dma_write(ring, B43_DMA32_TXRING, 0); - } else { - b43_dmacontroller_rx_reset(ring->dev, ring->mmio_base, - ring->type); - if (ring->type == B43_DMA_64BIT) { - b43_dma_write(ring, B43_DMA64_RXRINGLO, 0); - b43_dma_write(ring, B43_DMA64_RXRINGHI, 0); - } else - b43_dma_write(ring, B43_DMA32_RXRING, 0); - } -} - -static void free_all_descbuffers(struct b43_dmaring *ring) -{ - struct b43_dmadesc_meta *meta; - int i; - - if (!ring->used_slots) - return; - for (i = 0; i < ring->nr_slots; i++) { - /* get meta - ignore returned value */ - ring->ops->idx2desc(ring, i, &meta); - - if (!meta->skb || b43_dma_ptr_is_poisoned(meta->skb)) { - B43_WARN_ON(!ring->tx); - continue; - } - if (ring->tx) { - unmap_descbuffer(ring, meta->dmaaddr, - meta->skb->len, 1); - } else { - unmap_descbuffer(ring, meta->dmaaddr, - ring->rx_buffersize, 0); - } - free_descriptor_buffer(ring, meta); - } -} - -static u64 supported_dma_mask(struct b43_wldev *dev) -{ - u32 tmp; - u16 mmio_base; - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOST); - if (tmp & BCMA_IOST_DMA64) - return DMA_BIT_MASK(64); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - tmp = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); - if (tmp & SSB_TMSHIGH_DMA64) - return DMA_BIT_MASK(64); - break; -#endif - } - - mmio_base = b43_dmacontroller_base(0, 0); - b43_write32(dev, mmio_base + B43_DMA32_TXCTL, B43_DMA32_TXADDREXT_MASK); - tmp = b43_read32(dev, mmio_base + B43_DMA32_TXCTL); - if (tmp & B43_DMA32_TXADDREXT_MASK) - return DMA_BIT_MASK(32); - - return DMA_BIT_MASK(30); -} - -static enum b43_dmatype dma_mask_to_engine_type(u64 dmamask) -{ - if (dmamask == DMA_BIT_MASK(30)) - return B43_DMA_30BIT; - if (dmamask == DMA_BIT_MASK(32)) - return B43_DMA_32BIT; - if (dmamask == DMA_BIT_MASK(64)) - return B43_DMA_64BIT; - B43_WARN_ON(1); - return B43_DMA_30BIT; -} - -/* Main initialization function. */ -static -struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, - int controller_index, - int for_tx, - enum b43_dmatype type) -{ - struct b43_dmaring *ring; - int i, err; - dma_addr_t dma_test; - - ring = kzalloc(sizeof(*ring), GFP_KERNEL); - if (!ring) - goto out; - - ring->nr_slots = B43_RXRING_SLOTS; - if (for_tx) - ring->nr_slots = B43_TXRING_SLOTS; - - ring->meta = kcalloc(ring->nr_slots, sizeof(struct b43_dmadesc_meta), - GFP_KERNEL); - if (!ring->meta) - goto err_kfree_ring; - for (i = 0; i < ring->nr_slots; i++) - ring->meta->skb = B43_DMA_PTR_POISON; - - ring->type = type; - ring->dev = dev; - ring->mmio_base = b43_dmacontroller_base(type, controller_index); - ring->index = controller_index; - if (type == B43_DMA_64BIT) - ring->ops = &dma64_ops; - else - ring->ops = &dma32_ops; - if (for_tx) { - ring->tx = true; - ring->current_slot = -1; - } else { - if (ring->index == 0) { - switch (dev->fw.hdr_format) { - case B43_FW_HDR_598: - ring->rx_buffersize = B43_DMA0_RX_FW598_BUFSIZE; - ring->frameoffset = B43_DMA0_RX_FW598_FO; - break; - case B43_FW_HDR_410: - case B43_FW_HDR_351: - ring->rx_buffersize = B43_DMA0_RX_FW351_BUFSIZE; - ring->frameoffset = B43_DMA0_RX_FW351_FO; - break; - } - } else - B43_WARN_ON(1); - } -#ifdef CONFIG_B43_DEBUG - ring->last_injected_overflow = jiffies; -#endif - - if (for_tx) { - /* Assumption: B43_TXRING_SLOTS can be divided by TX_SLOTS_PER_FRAME */ - BUILD_BUG_ON(B43_TXRING_SLOTS % TX_SLOTS_PER_FRAME != 0); - - ring->txhdr_cache = kcalloc(ring->nr_slots / TX_SLOTS_PER_FRAME, - b43_txhdr_size(dev), - GFP_KERNEL); - if (!ring->txhdr_cache) - goto err_kfree_meta; - - /* test for ability to dma to txhdr_cache */ - dma_test = dma_map_single(dev->dev->dma_dev, - ring->txhdr_cache, - b43_txhdr_size(dev), - DMA_TO_DEVICE); - - if (b43_dma_mapping_error(ring, dma_test, - b43_txhdr_size(dev), 1)) { - /* ugh realloc */ - kfree(ring->txhdr_cache); - ring->txhdr_cache = kcalloc(ring->nr_slots / TX_SLOTS_PER_FRAME, - b43_txhdr_size(dev), - GFP_KERNEL | GFP_DMA); - if (!ring->txhdr_cache) - goto err_kfree_meta; - - dma_test = dma_map_single(dev->dev->dma_dev, - ring->txhdr_cache, - b43_txhdr_size(dev), - DMA_TO_DEVICE); - - if (b43_dma_mapping_error(ring, dma_test, - b43_txhdr_size(dev), 1)) { - - b43err(dev->wl, - "TXHDR DMA allocation failed\n"); - goto err_kfree_txhdr_cache; - } - } - - dma_unmap_single(dev->dev->dma_dev, - dma_test, b43_txhdr_size(dev), - DMA_TO_DEVICE); - } - - err = alloc_ringmemory(ring); - if (err) - goto err_kfree_txhdr_cache; - err = dmacontroller_setup(ring); - if (err) - goto err_free_ringmemory; - - out: - return ring; - - err_free_ringmemory: - free_ringmemory(ring); - err_kfree_txhdr_cache: - kfree(ring->txhdr_cache); - err_kfree_meta: - kfree(ring->meta); - err_kfree_ring: - kfree(ring); - ring = NULL; - goto out; -} - -#define divide(a, b) ({ \ - typeof(a) __a = a; \ - do_div(__a, b); \ - __a; \ - }) - -#define modulo(a, b) ({ \ - typeof(a) __a = a; \ - do_div(__a, b); \ - }) - -/* Main cleanup function. */ -static void b43_destroy_dmaring(struct b43_dmaring *ring, - const char *ringname) -{ - if (!ring) - return; - -#ifdef CONFIG_B43_DEBUG - { - /* Print some statistics. */ - u64 failed_packets = ring->nr_failed_tx_packets; - u64 succeed_packets = ring->nr_succeed_tx_packets; - u64 nr_packets = failed_packets + succeed_packets; - u64 permille_failed = 0, average_tries = 0; - - if (nr_packets) - permille_failed = divide(failed_packets * 1000, nr_packets); - if (nr_packets) - average_tries = divide(ring->nr_total_packet_tries * 100, nr_packets); - - b43dbg(ring->dev->wl, "DMA-%u %s: " - "Used slots %d/%d, Failed frames %llu/%llu = %llu.%01llu%%, " - "Average tries %llu.%02llu\n", - (unsigned int)(ring->type), ringname, - ring->max_used_slots, - ring->nr_slots, - (unsigned long long)failed_packets, - (unsigned long long)nr_packets, - (unsigned long long)divide(permille_failed, 10), - (unsigned long long)modulo(permille_failed, 10), - (unsigned long long)divide(average_tries, 100), - (unsigned long long)modulo(average_tries, 100)); - } -#endif /* DEBUG */ - - /* Device IRQs are disabled prior entering this function, - * so no need to take care of concurrency with rx handler stuff. - */ - dmacontroller_cleanup(ring); - free_all_descbuffers(ring); - free_ringmemory(ring); - - kfree(ring->txhdr_cache); - kfree(ring->meta); - kfree(ring); -} - -#define destroy_ring(dma, ring) do { \ - b43_destroy_dmaring((dma)->ring, __stringify(ring)); \ - (dma)->ring = NULL; \ - } while (0) - -void b43_dma_free(struct b43_wldev *dev) -{ - struct b43_dma *dma; - - if (b43_using_pio_transfers(dev)) - return; - dma = &dev->dma; - - destroy_ring(dma, rx_ring); - destroy_ring(dma, tx_ring_AC_BK); - destroy_ring(dma, tx_ring_AC_BE); - destroy_ring(dma, tx_ring_AC_VI); - destroy_ring(dma, tx_ring_AC_VO); - destroy_ring(dma, tx_ring_mcast); -} - -static int b43_dma_set_mask(struct b43_wldev *dev, u64 mask) -{ - u64 orig_mask = mask; - bool fallback = false; - int err; - - /* Try to set the DMA mask. If it fails, try falling back to a - * lower mask, as we can always also support a lower one. */ - while (1) { - err = dma_set_mask_and_coherent(dev->dev->dma_dev, mask); - if (!err) - break; - if (mask == DMA_BIT_MASK(64)) { - mask = DMA_BIT_MASK(32); - fallback = true; - continue; - } - if (mask == DMA_BIT_MASK(32)) { - mask = DMA_BIT_MASK(30); - fallback = true; - continue; - } - b43err(dev->wl, "The machine/kernel does not support " - "the required %u-bit DMA mask\n", - (unsigned int)dma_mask_to_engine_type(orig_mask)); - return -EOPNOTSUPP; - } - if (fallback) { - b43info(dev->wl, "DMA mask fallback from %u-bit to %u-bit\n", - (unsigned int)dma_mask_to_engine_type(orig_mask), - (unsigned int)dma_mask_to_engine_type(mask)); - } - - return 0; -} - -/* Some hardware with 64-bit DMA seems to be bugged and looks for translation - * bit in low address word instead of high one. - */ -static bool b43_dma_translation_in_low_word(struct b43_wldev *dev, - enum b43_dmatype type) -{ - if (type != B43_DMA_64BIT) - return true; - -#ifdef CONFIG_B43_SSB - if (dev->dev->bus_type == B43_BUS_SSB && - dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI && - !(pci_is_pcie(dev->dev->sdev->bus->host_pci) && - ssb_read32(dev->dev->sdev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64)) - return true; -#endif - return false; -} - -int b43_dma_init(struct b43_wldev *dev) -{ - struct b43_dma *dma = &dev->dma; - int err; - u64 dmamask; - enum b43_dmatype type; - - dmamask = supported_dma_mask(dev); - type = dma_mask_to_engine_type(dmamask); - err = b43_dma_set_mask(dev, dmamask); - if (err) - return err; - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - dma->translation = bcma_core_dma_translation(dev->dev->bdev); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - dma->translation = ssb_dma_translation(dev->dev->sdev); - break; -#endif - } - dma->translation_in_low = b43_dma_translation_in_low_word(dev, type); - - dma->parity = true; -#ifdef CONFIG_B43_BCMA - /* TODO: find out which SSB devices need disabling parity */ - if (dev->dev->bus_type == B43_BUS_BCMA) - dma->parity = false; -#endif - - err = -ENOMEM; - /* setup TX DMA channels. */ - dma->tx_ring_AC_BK = b43_setup_dmaring(dev, 0, 1, type); - if (!dma->tx_ring_AC_BK) - goto out; - - dma->tx_ring_AC_BE = b43_setup_dmaring(dev, 1, 1, type); - if (!dma->tx_ring_AC_BE) - goto err_destroy_bk; - - dma->tx_ring_AC_VI = b43_setup_dmaring(dev, 2, 1, type); - if (!dma->tx_ring_AC_VI) - goto err_destroy_be; - - dma->tx_ring_AC_VO = b43_setup_dmaring(dev, 3, 1, type); - if (!dma->tx_ring_AC_VO) - goto err_destroy_vi; - - dma->tx_ring_mcast = b43_setup_dmaring(dev, 4, 1, type); - if (!dma->tx_ring_mcast) - goto err_destroy_vo; - - /* setup RX DMA channel. */ - dma->rx_ring = b43_setup_dmaring(dev, 0, 0, type); - if (!dma->rx_ring) - goto err_destroy_mcast; - - /* No support for the TX status DMA ring. */ - B43_WARN_ON(dev->dev->core_rev < 5); - - b43dbg(dev->wl, "%u-bit DMA initialized\n", - (unsigned int)type); - err = 0; -out: - return err; - -err_destroy_mcast: - destroy_ring(dma, tx_ring_mcast); -err_destroy_vo: - destroy_ring(dma, tx_ring_AC_VO); -err_destroy_vi: - destroy_ring(dma, tx_ring_AC_VI); -err_destroy_be: - destroy_ring(dma, tx_ring_AC_BE); -err_destroy_bk: - destroy_ring(dma, tx_ring_AC_BK); - return err; -} - -/* Generate a cookie for the TX header. */ -static u16 generate_cookie(struct b43_dmaring *ring, int slot) -{ - u16 cookie; - - /* Use the upper 4 bits of the cookie as - * DMA controller ID and store the slot number - * in the lower 12 bits. - * Note that the cookie must never be 0, as this - * is a special value used in RX path. - * It can also not be 0xFFFF because that is special - * for multicast frames. - */ - cookie = (((u16)ring->index + 1) << 12); - B43_WARN_ON(slot & ~0x0FFF); - cookie |= (u16)slot; - - return cookie; -} - -/* Inspect a cookie and find out to which controller/slot it belongs. */ -static -struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot) -{ - struct b43_dma *dma = &dev->dma; - struct b43_dmaring *ring = NULL; - - switch (cookie & 0xF000) { - case 0x1000: - ring = dma->tx_ring_AC_BK; - break; - case 0x2000: - ring = dma->tx_ring_AC_BE; - break; - case 0x3000: - ring = dma->tx_ring_AC_VI; - break; - case 0x4000: - ring = dma->tx_ring_AC_VO; - break; - case 0x5000: - ring = dma->tx_ring_mcast; - break; - } - *slot = (cookie & 0x0FFF); - if (unlikely(!ring || *slot < 0 || *slot >= ring->nr_slots)) { - b43dbg(dev->wl, "TX-status contains " - "invalid cookie: 0x%04X\n", cookie); - return NULL; - } - - return ring; -} - -static int dma_tx_fragment(struct b43_dmaring *ring, - struct sk_buff *skb) -{ - const struct b43_dma_ops *ops = ring->ops; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct b43_private_tx_info *priv_info = b43_get_priv_tx_info(info); - u8 *header; - int slot, old_top_slot, old_used_slots; - int err; - struct b43_dmadesc_generic *desc; - struct b43_dmadesc_meta *meta; - struct b43_dmadesc_meta *meta_hdr; - u16 cookie; - size_t hdrsize = b43_txhdr_size(ring->dev); - - /* Important note: If the number of used DMA slots per TX frame - * is changed here, the TX_SLOTS_PER_FRAME definition at the top of - * the file has to be updated, too! - */ - - old_top_slot = ring->current_slot; - old_used_slots = ring->used_slots; - - /* Get a slot for the header. */ - slot = request_slot(ring); - desc = ops->idx2desc(ring, slot, &meta_hdr); - memset(meta_hdr, 0, sizeof(*meta_hdr)); - - header = &(ring->txhdr_cache[(slot / TX_SLOTS_PER_FRAME) * hdrsize]); - cookie = generate_cookie(ring, slot); - err = b43_generate_txhdr(ring->dev, header, - skb, info, cookie); - if (unlikely(err)) { - ring->current_slot = old_top_slot; - ring->used_slots = old_used_slots; - return err; - } - - meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, - hdrsize, 1); - if (b43_dma_mapping_error(ring, meta_hdr->dmaaddr, hdrsize, 1)) { - ring->current_slot = old_top_slot; - ring->used_slots = old_used_slots; - return -EIO; - } - ops->fill_descriptor(ring, desc, meta_hdr->dmaaddr, - hdrsize, 1, 0, 0); - - /* Get a slot for the payload. */ - slot = request_slot(ring); - desc = ops->idx2desc(ring, slot, &meta); - memset(meta, 0, sizeof(*meta)); - - meta->skb = skb; - meta->is_last_fragment = true; - priv_info->bouncebuffer = NULL; - - meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); - /* create a bounce buffer in zone_dma on mapping failure. */ - if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { - priv_info->bouncebuffer = kmemdup(skb->data, skb->len, - GFP_ATOMIC | GFP_DMA); - if (!priv_info->bouncebuffer) { - ring->current_slot = old_top_slot; - ring->used_slots = old_used_slots; - err = -ENOMEM; - goto out_unmap_hdr; - } - - meta->dmaaddr = map_descbuffer(ring, priv_info->bouncebuffer, skb->len, 1); - if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { - kfree(priv_info->bouncebuffer); - priv_info->bouncebuffer = NULL; - ring->current_slot = old_top_slot; - ring->used_slots = old_used_slots; - err = -EIO; - goto out_unmap_hdr; - } - } - - ops->fill_descriptor(ring, desc, meta->dmaaddr, skb->len, 0, 1, 1); - - if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { - /* Tell the firmware about the cookie of the last - * mcast frame, so it can clear the more-data bit in it. */ - b43_shm_write16(ring->dev, B43_SHM_SHARED, - B43_SHM_SH_MCASTCOOKIE, cookie); - } - /* Now transfer the whole frame. */ - wmb(); - ops->poke_tx(ring, next_slot(ring, slot)); - return 0; - -out_unmap_hdr: - unmap_descbuffer(ring, meta_hdr->dmaaddr, - hdrsize, 1); - return err; -} - -static inline int should_inject_overflow(struct b43_dmaring *ring) -{ -#ifdef CONFIG_B43_DEBUG - if (unlikely(b43_debug(ring->dev, B43_DBG_DMAOVERFLOW))) { - /* Check if we should inject another ringbuffer overflow - * to test handling of this situation in the stack. */ - unsigned long next_overflow; - - next_overflow = ring->last_injected_overflow + HZ; - if (time_after(jiffies, next_overflow)) { - ring->last_injected_overflow = jiffies; - b43dbg(ring->dev->wl, - "Injecting TX ring overflow on " - "DMA controller %d\n", ring->index); - return 1; - } - } -#endif /* CONFIG_B43_DEBUG */ - return 0; -} - -/* Static mapping of mac80211's queues (priorities) to b43 DMA rings. */ -static struct b43_dmaring *select_ring_by_priority(struct b43_wldev *dev, - u8 queue_prio) -{ - struct b43_dmaring *ring; - - if (dev->qos_enabled) { - /* 0 = highest priority */ - switch (queue_prio) { - default: - B43_WARN_ON(1); - /* fallthrough */ - case 0: - ring = dev->dma.tx_ring_AC_VO; - break; - case 1: - ring = dev->dma.tx_ring_AC_VI; - break; - case 2: - ring = dev->dma.tx_ring_AC_BE; - break; - case 3: - ring = dev->dma.tx_ring_AC_BK; - break; - } - } else - ring = dev->dma.tx_ring_AC_BE; - - return ring; -} - -int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb) -{ - struct b43_dmaring *ring; - struct ieee80211_hdr *hdr; - int err = 0; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - hdr = (struct ieee80211_hdr *)skb->data; - if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { - /* The multicast ring will be sent after the DTIM */ - ring = dev->dma.tx_ring_mcast; - /* Set the more-data bit. Ucode will clear it on - * the last frame for us. */ - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); - } else { - /* Decide by priority where to put this frame. */ - ring = select_ring_by_priority( - dev, skb_get_queue_mapping(skb)); - } - - B43_WARN_ON(!ring->tx); - - if (unlikely(ring->stopped)) { - /* We get here only because of a bug in mac80211. - * Because of a race, one packet may be queued after - * the queue is stopped, thus we got called when we shouldn't. - * For now, just refuse the transmit. */ - if (b43_debug(dev, B43_DBG_DMAVERBOSE)) - b43err(dev->wl, "Packet after queue stopped\n"); - err = -ENOSPC; - goto out; - } - - if (unlikely(WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME))) { - /* If we get here, we have a real error with the queue - * full, but queues not stopped. */ - b43err(dev->wl, "DMA queue overflow\n"); - err = -ENOSPC; - goto out; - } - - /* Assign the queue number to the ring (if not already done before) - * so TX status handling can use it. The queue to ring mapping is - * static, so we don't need to store it per frame. */ - ring->queue_prio = skb_get_queue_mapping(skb); - - err = dma_tx_fragment(ring, skb); - if (unlikely(err == -ENOKEY)) { - /* Drop this packet, as we don't have the encryption key - * anymore and must not transmit it unencrypted. */ - ieee80211_free_txskb(dev->wl->hw, skb); - err = 0; - goto out; - } - if (unlikely(err)) { - b43err(dev->wl, "DMA tx mapping failure\n"); - goto out; - } - if ((free_slots(ring) < TX_SLOTS_PER_FRAME) || - should_inject_overflow(ring)) { - /* This TX ring is full. */ - unsigned int skb_mapping = skb_get_queue_mapping(skb); - ieee80211_stop_queue(dev->wl->hw, skb_mapping); - dev->wl->tx_queue_stopped[skb_mapping] = 1; - ring->stopped = true; - if (b43_debug(dev, B43_DBG_DMAVERBOSE)) { - b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index); - } - } -out: - - return err; -} - -void b43_dma_handle_txstatus(struct b43_wldev *dev, - const struct b43_txstatus *status) -{ - const struct b43_dma_ops *ops; - struct b43_dmaring *ring; - struct b43_dmadesc_meta *meta; - static const struct b43_txstatus fake; /* filled with 0 */ - const struct b43_txstatus *txstat; - int slot, firstused; - bool frame_succeed; - int skip; - static u8 err_out1, err_out2; - - ring = parse_cookie(dev, status->cookie, &slot); - if (unlikely(!ring)) - return; - B43_WARN_ON(!ring->tx); - - /* Sanity check: TX packets are processed in-order on one ring. - * Check if the slot deduced from the cookie really is the first - * used slot. */ - firstused = ring->current_slot - ring->used_slots + 1; - if (firstused < 0) - firstused = ring->nr_slots + firstused; - - skip = 0; - if (unlikely(slot != firstused)) { - /* This possibly is a firmware bug and will result in - * malfunction, memory leaks and/or stall of DMA functionality. - */ - if (slot == next_slot(ring, next_slot(ring, firstused))) { - /* If a single header/data pair was missed, skip over - * the first two slots in an attempt to recover. - */ - slot = firstused; - skip = 2; - if (!err_out1) { - /* Report the error once. */ - b43dbg(dev->wl, - "Skip on DMA ring %d slot %d.\n", - ring->index, slot); - err_out1 = 1; - } - } else { - /* More than a single header/data pair were missed. - * Report this error once. - */ - if (!err_out2) - b43dbg(dev->wl, - "Out of order TX status report on DMA ring %d. Expected %d, but got %d\n", - ring->index, firstused, slot); - err_out2 = 1; - return; - } - } - - ops = ring->ops; - while (1) { - B43_WARN_ON(slot < 0 || slot >= ring->nr_slots); - /* get meta - ignore returned value */ - ops->idx2desc(ring, slot, &meta); - - if (b43_dma_ptr_is_poisoned(meta->skb)) { - b43dbg(dev->wl, "Poisoned TX slot %d (first=%d) " - "on ring %d\n", - slot, firstused, ring->index); - break; - } - - if (meta->skb) { - struct b43_private_tx_info *priv_info = - b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb)); - - unmap_descbuffer(ring, meta->dmaaddr, - meta->skb->len, 1); - kfree(priv_info->bouncebuffer); - priv_info->bouncebuffer = NULL; - } else { - unmap_descbuffer(ring, meta->dmaaddr, - b43_txhdr_size(dev), 1); - } - - if (meta->is_last_fragment) { - struct ieee80211_tx_info *info; - - if (unlikely(!meta->skb)) { - /* This is a scatter-gather fragment of a frame, - * so the skb pointer must not be NULL. - */ - b43dbg(dev->wl, "TX status unexpected NULL skb " - "at slot %d (first=%d) on ring %d\n", - slot, firstused, ring->index); - break; - } - - info = IEEE80211_SKB_CB(meta->skb); - - /* - * Call back to inform the ieee80211 subsystem about - * the status of the transmission. When skipping over - * a missed TX status report, use a status structure - * filled with zeros to indicate that the frame was not - * sent (frame_count 0) and not acknowledged - */ - if (unlikely(skip)) - txstat = &fake; - else - txstat = status; - - frame_succeed = b43_fill_txstatus_report(dev, info, - txstat); -#ifdef CONFIG_B43_DEBUG - if (frame_succeed) - ring->nr_succeed_tx_packets++; - else - ring->nr_failed_tx_packets++; - ring->nr_total_packet_tries += status->frame_count; -#endif /* DEBUG */ - ieee80211_tx_status(dev->wl->hw, meta->skb); - - /* skb will be freed by ieee80211_tx_status(). - * Poison our pointer. */ - meta->skb = B43_DMA_PTR_POISON; - } else { - /* No need to call free_descriptor_buffer here, as - * this is only the txhdr, which is not allocated. - */ - if (unlikely(meta->skb)) { - b43dbg(dev->wl, "TX status unexpected non-NULL skb " - "at slot %d (first=%d) on ring %d\n", - slot, firstused, ring->index); - break; - } - } - - /* Everything unmapped and free'd. So it's not used anymore. */ - ring->used_slots--; - - if (meta->is_last_fragment && !skip) { - /* This is the last scatter-gather - * fragment of the frame. We are done. */ - break; - } - slot = next_slot(ring, slot); - if (skip > 0) - --skip; - } - if (ring->stopped) { - B43_WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME); - ring->stopped = false; - } - - if (dev->wl->tx_queue_stopped[ring->queue_prio]) { - dev->wl->tx_queue_stopped[ring->queue_prio] = 0; - } else { - /* If the driver queue is running wake the corresponding - * mac80211 queue. */ - ieee80211_wake_queue(dev->wl->hw, ring->queue_prio); - if (b43_debug(dev, B43_DBG_DMAVERBOSE)) { - b43dbg(dev->wl, "Woke up TX ring %d\n", ring->index); - } - } - /* Add work to the queue. */ - ieee80211_queue_work(dev->wl->hw, &dev->wl->tx_work); -} - -static void dma_rx(struct b43_dmaring *ring, int *slot) -{ - const struct b43_dma_ops *ops = ring->ops; - struct b43_dmadesc_generic *desc; - struct b43_dmadesc_meta *meta; - struct b43_rxhdr_fw4 *rxhdr; - struct sk_buff *skb; - u16 len; - int err; - dma_addr_t dmaaddr; - - desc = ops->idx2desc(ring, *slot, &meta); - - sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); - skb = meta->skb; - - rxhdr = (struct b43_rxhdr_fw4 *)skb->data; - len = le16_to_cpu(rxhdr->frame_len); - if (len == 0) { - int i = 0; - - do { - udelay(2); - barrier(); - len = le16_to_cpu(rxhdr->frame_len); - } while (len == 0 && i++ < 5); - if (unlikely(len == 0)) { - dmaaddr = meta->dmaaddr; - goto drop_recycle_buffer; - } - } - if (unlikely(b43_rx_buffer_is_poisoned(ring, skb))) { - /* Something went wrong with the DMA. - * The device did not touch the buffer and did not overwrite the poison. */ - b43dbg(ring->dev->wl, "DMA RX: Dropping poisoned buffer.\n"); - dmaaddr = meta->dmaaddr; - goto drop_recycle_buffer; - } - if (unlikely(len + ring->frameoffset > ring->rx_buffersize)) { - /* The data did not fit into one descriptor buffer - * and is split over multiple buffers. - * This should never happen, as we try to allocate buffers - * big enough. So simply ignore this packet. - */ - int cnt = 0; - s32 tmp = len; - - while (1) { - desc = ops->idx2desc(ring, *slot, &meta); - /* recycle the descriptor buffer. */ - b43_poison_rx_buffer(ring, meta->skb); - sync_descbuffer_for_device(ring, meta->dmaaddr, - ring->rx_buffersize); - *slot = next_slot(ring, *slot); - cnt++; - tmp -= ring->rx_buffersize; - if (tmp <= 0) - break; - } - b43err(ring->dev->wl, "DMA RX buffer too small " - "(len: %u, buffer: %u, nr-dropped: %d)\n", - len, ring->rx_buffersize, cnt); - goto drop; - } - - dmaaddr = meta->dmaaddr; - err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); - if (unlikely(err)) { - b43dbg(ring->dev->wl, "DMA RX: setup_rx_descbuffer() failed\n"); - goto drop_recycle_buffer; - } - - unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); - skb_put(skb, len + ring->frameoffset); - skb_pull(skb, ring->frameoffset); - - b43_rx(ring->dev, skb, rxhdr); -drop: - return; - -drop_recycle_buffer: - /* Poison and recycle the RX buffer. */ - b43_poison_rx_buffer(ring, skb); - sync_descbuffer_for_device(ring, dmaaddr, ring->rx_buffersize); -} - -void b43_dma_handle_rx_overflow(struct b43_dmaring *ring) -{ - int current_slot, previous_slot; - - B43_WARN_ON(ring->tx); - - /* Device has filled all buffers, drop all packets and let TCP - * decrease speed. - * Decrement RX index by one will let the device to see all slots - * as free again - */ - /* - *TODO: How to increase rx_drop in mac80211? - */ - current_slot = ring->ops->get_current_rxslot(ring); - previous_slot = prev_slot(ring, current_slot); - ring->ops->set_current_rxslot(ring, previous_slot); -} - -void b43_dma_rx(struct b43_dmaring *ring) -{ - const struct b43_dma_ops *ops = ring->ops; - int slot, current_slot; - int used_slots = 0; - - B43_WARN_ON(ring->tx); - current_slot = ops->get_current_rxslot(ring); - B43_WARN_ON(!(current_slot >= 0 && current_slot < ring->nr_slots)); - - slot = ring->current_slot; - for (; slot != current_slot; slot = next_slot(ring, slot)) { - dma_rx(ring, &slot); - update_max_used_slots(ring, ++used_slots); - } - wmb(); - ops->set_current_rxslot(ring, slot); - ring->current_slot = slot; -} - -static void b43_dma_tx_suspend_ring(struct b43_dmaring *ring) -{ - B43_WARN_ON(!ring->tx); - ring->ops->tx_suspend(ring); -} - -static void b43_dma_tx_resume_ring(struct b43_dmaring *ring) -{ - B43_WARN_ON(!ring->tx); - ring->ops->tx_resume(ring); -} - -void b43_dma_tx_suspend(struct b43_wldev *dev) -{ - b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); - b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BK); - b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BE); - b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VI); - b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VO); - b43_dma_tx_suspend_ring(dev->dma.tx_ring_mcast); -} - -void b43_dma_tx_resume(struct b43_wldev *dev) -{ - b43_dma_tx_resume_ring(dev->dma.tx_ring_mcast); - b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VO); - b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VI); - b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BE); - b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BK); - b43_power_saving_ctl_bits(dev, 0); -} - -static void direct_fifo_rx(struct b43_wldev *dev, enum b43_dmatype type, - u16 mmio_base, bool enable) -{ - u32 ctl; - - if (type == B43_DMA_64BIT) { - ctl = b43_read32(dev, mmio_base + B43_DMA64_RXCTL); - ctl &= ~B43_DMA64_RXDIRECTFIFO; - if (enable) - ctl |= B43_DMA64_RXDIRECTFIFO; - b43_write32(dev, mmio_base + B43_DMA64_RXCTL, ctl); - } else { - ctl = b43_read32(dev, mmio_base + B43_DMA32_RXCTL); - ctl &= ~B43_DMA32_RXDIRECTFIFO; - if (enable) - ctl |= B43_DMA32_RXDIRECTFIFO; - b43_write32(dev, mmio_base + B43_DMA32_RXCTL, ctl); - } -} - -/* Enable/Disable Direct FIFO Receive Mode (PIO) on a RX engine. - * This is called from PIO code, so DMA structures are not available. */ -void b43_dma_direct_fifo_rx(struct b43_wldev *dev, - unsigned int engine_index, bool enable) -{ - enum b43_dmatype type; - u16 mmio_base; - - type = dma_mask_to_engine_type(supported_dma_mask(dev)); - - mmio_base = b43_dmacontroller_base(type, engine_index); - direct_fifo_rx(dev, type, mmio_base, enable); -} diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h deleted file mode 100644 index df8c8cdcbdb5..000000000000 --- a/drivers/net/wireless/b43/dma.h +++ /dev/null @@ -1,305 +0,0 @@ -#ifndef B43_DMA_H_ -#define B43_DMA_H_ - -#include - -#include "b43.h" - - -/* DMA-Interrupt reasons. */ -#define B43_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \ - | (1 << 14) | (1 << 15)) -#define B43_DMAIRQ_RDESC_UFLOW (1 << 13) -#define B43_DMAIRQ_RX_DONE (1 << 16) - -/*** 32-bit DMA Engine. ***/ - -/* 32-bit DMA controller registers. */ -#define B43_DMA32_TXCTL 0x00 -#define B43_DMA32_TXENABLE 0x00000001 -#define B43_DMA32_TXSUSPEND 0x00000002 -#define B43_DMA32_TXLOOPBACK 0x00000004 -#define B43_DMA32_TXFLUSH 0x00000010 -#define B43_DMA32_TXPARITYDISABLE 0x00000800 -#define B43_DMA32_TXADDREXT_MASK 0x00030000 -#define B43_DMA32_TXADDREXT_SHIFT 16 -#define B43_DMA32_TXRING 0x04 -#define B43_DMA32_TXINDEX 0x08 -#define B43_DMA32_TXSTATUS 0x0C -#define B43_DMA32_TXDPTR 0x00000FFF -#define B43_DMA32_TXSTATE 0x0000F000 -#define B43_DMA32_TXSTAT_DISABLED 0x00000000 -#define B43_DMA32_TXSTAT_ACTIVE 0x00001000 -#define B43_DMA32_TXSTAT_IDLEWAIT 0x00002000 -#define B43_DMA32_TXSTAT_STOPPED 0x00003000 -#define B43_DMA32_TXSTAT_SUSP 0x00004000 -#define B43_DMA32_TXERROR 0x000F0000 -#define B43_DMA32_TXERR_NOERR 0x00000000 -#define B43_DMA32_TXERR_PROT 0x00010000 -#define B43_DMA32_TXERR_UNDERRUN 0x00020000 -#define B43_DMA32_TXERR_BUFREAD 0x00030000 -#define B43_DMA32_TXERR_DESCREAD 0x00040000 -#define B43_DMA32_TXACTIVE 0xFFF00000 -#define B43_DMA32_RXCTL 0x10 -#define B43_DMA32_RXENABLE 0x00000001 -#define B43_DMA32_RXFROFF_MASK 0x000000FE -#define B43_DMA32_RXFROFF_SHIFT 1 -#define B43_DMA32_RXDIRECTFIFO 0x00000100 -#define B43_DMA32_RXPARITYDISABLE 0x00000800 -#define B43_DMA32_RXADDREXT_MASK 0x00030000 -#define B43_DMA32_RXADDREXT_SHIFT 16 -#define B43_DMA32_RXRING 0x14 -#define B43_DMA32_RXINDEX 0x18 -#define B43_DMA32_RXSTATUS 0x1C -#define B43_DMA32_RXDPTR 0x00000FFF -#define B43_DMA32_RXSTATE 0x0000F000 -#define B43_DMA32_RXSTAT_DISABLED 0x00000000 -#define B43_DMA32_RXSTAT_ACTIVE 0x00001000 -#define B43_DMA32_RXSTAT_IDLEWAIT 0x00002000 -#define B43_DMA32_RXSTAT_STOPPED 0x00003000 -#define B43_DMA32_RXERROR 0x000F0000 -#define B43_DMA32_RXERR_NOERR 0x00000000 -#define B43_DMA32_RXERR_PROT 0x00010000 -#define B43_DMA32_RXERR_OVERFLOW 0x00020000 -#define B43_DMA32_RXERR_BUFWRITE 0x00030000 -#define B43_DMA32_RXERR_DESCREAD 0x00040000 -#define B43_DMA32_RXACTIVE 0xFFF00000 - -/* 32-bit DMA descriptor. */ -struct b43_dmadesc32 { - __le32 control; - __le32 address; -} __packed; -#define B43_DMA32_DCTL_BYTECNT 0x00001FFF -#define B43_DMA32_DCTL_ADDREXT_MASK 0x00030000 -#define B43_DMA32_DCTL_ADDREXT_SHIFT 16 -#define B43_DMA32_DCTL_DTABLEEND 0x10000000 -#define B43_DMA32_DCTL_IRQ 0x20000000 -#define B43_DMA32_DCTL_FRAMEEND 0x40000000 -#define B43_DMA32_DCTL_FRAMESTART 0x80000000 - -/*** 64-bit DMA Engine. ***/ - -/* 64-bit DMA controller registers. */ -#define B43_DMA64_TXCTL 0x00 -#define B43_DMA64_TXENABLE 0x00000001 -#define B43_DMA64_TXSUSPEND 0x00000002 -#define B43_DMA64_TXLOOPBACK 0x00000004 -#define B43_DMA64_TXFLUSH 0x00000010 -#define B43_DMA64_TXPARITYDISABLE 0x00000800 -#define B43_DMA64_TXADDREXT_MASK 0x00030000 -#define B43_DMA64_TXADDREXT_SHIFT 16 -#define B43_DMA64_TXINDEX 0x04 -#define B43_DMA64_TXRINGLO 0x08 -#define B43_DMA64_TXRINGHI 0x0C -#define B43_DMA64_TXSTATUS 0x10 -#define B43_DMA64_TXSTATDPTR 0x00001FFF -#define B43_DMA64_TXSTAT 0xF0000000 -#define B43_DMA64_TXSTAT_DISABLED 0x00000000 -#define B43_DMA64_TXSTAT_ACTIVE 0x10000000 -#define B43_DMA64_TXSTAT_IDLEWAIT 0x20000000 -#define B43_DMA64_TXSTAT_STOPPED 0x30000000 -#define B43_DMA64_TXSTAT_SUSP 0x40000000 -#define B43_DMA64_TXERROR 0x14 -#define B43_DMA64_TXERRDPTR 0x0001FFFF -#define B43_DMA64_TXERR 0xF0000000 -#define B43_DMA64_TXERR_NOERR 0x00000000 -#define B43_DMA64_TXERR_PROT 0x10000000 -#define B43_DMA64_TXERR_UNDERRUN 0x20000000 -#define B43_DMA64_TXERR_TRANSFER 0x30000000 -#define B43_DMA64_TXERR_DESCREAD 0x40000000 -#define B43_DMA64_TXERR_CORE 0x50000000 -#define B43_DMA64_RXCTL 0x20 -#define B43_DMA64_RXENABLE 0x00000001 -#define B43_DMA64_RXFROFF_MASK 0x000000FE -#define B43_DMA64_RXFROFF_SHIFT 1 -#define B43_DMA64_RXDIRECTFIFO 0x00000100 -#define B43_DMA64_RXPARITYDISABLE 0x00000800 -#define B43_DMA64_RXADDREXT_MASK 0x00030000 -#define B43_DMA64_RXADDREXT_SHIFT 16 -#define B43_DMA64_RXINDEX 0x24 -#define B43_DMA64_RXRINGLO 0x28 -#define B43_DMA64_RXRINGHI 0x2C -#define B43_DMA64_RXSTATUS 0x30 -#define B43_DMA64_RXSTATDPTR 0x00001FFF -#define B43_DMA64_RXSTAT 0xF0000000 -#define B43_DMA64_RXSTAT_DISABLED 0x00000000 -#define B43_DMA64_RXSTAT_ACTIVE 0x10000000 -#define B43_DMA64_RXSTAT_IDLEWAIT 0x20000000 -#define B43_DMA64_RXSTAT_STOPPED 0x30000000 -#define B43_DMA64_RXSTAT_SUSP 0x40000000 -#define B43_DMA64_RXERROR 0x34 -#define B43_DMA64_RXERRDPTR 0x0001FFFF -#define B43_DMA64_RXERR 0xF0000000 -#define B43_DMA64_RXERR_NOERR 0x00000000 -#define B43_DMA64_RXERR_PROT 0x10000000 -#define B43_DMA64_RXERR_UNDERRUN 0x20000000 -#define B43_DMA64_RXERR_TRANSFER 0x30000000 -#define B43_DMA64_RXERR_DESCREAD 0x40000000 -#define B43_DMA64_RXERR_CORE 0x50000000 - -/* 64-bit DMA descriptor. */ -struct b43_dmadesc64 { - __le32 control0; - __le32 control1; - __le32 address_low; - __le32 address_high; -} __packed; -#define B43_DMA64_DCTL0_DTABLEEND 0x10000000 -#define B43_DMA64_DCTL0_IRQ 0x20000000 -#define B43_DMA64_DCTL0_FRAMEEND 0x40000000 -#define B43_DMA64_DCTL0_FRAMESTART 0x80000000 -#define B43_DMA64_DCTL1_BYTECNT 0x00001FFF -#define B43_DMA64_DCTL1_ADDREXT_MASK 0x00030000 -#define B43_DMA64_DCTL1_ADDREXT_SHIFT 16 - -struct b43_dmadesc_generic { - union { - struct b43_dmadesc32 dma32; - struct b43_dmadesc64 dma64; - } __packed; -} __packed; - -/* Misc DMA constants */ -#define B43_DMA32_RINGMEMSIZE 4096 -#define B43_DMA64_RINGMEMSIZE 8192 -/* Offset of frame with actual data */ -#define B43_DMA0_RX_FW598_FO 38 -#define B43_DMA0_RX_FW351_FO 30 - -/* DMA engine tuning knobs */ -#define B43_TXRING_SLOTS 256 -#define B43_RXRING_SLOTS 256 -#define B43_DMA0_RX_FW598_BUFSIZE (B43_DMA0_RX_FW598_FO + IEEE80211_MAX_FRAME_LEN) -#define B43_DMA0_RX_FW351_BUFSIZE (B43_DMA0_RX_FW351_FO + IEEE80211_MAX_FRAME_LEN) - -/* Pointer poison */ -#define B43_DMA_PTR_POISON ((void *)ERR_PTR(-ENOMEM)) -#define b43_dma_ptr_is_poisoned(ptr) (unlikely((ptr) == B43_DMA_PTR_POISON)) - - -struct sk_buff; -struct b43_private; -struct b43_txstatus; - -struct b43_dmadesc_meta { - /* The kernel DMA-able buffer. */ - struct sk_buff *skb; - /* DMA base bus-address of the descriptor buffer. */ - dma_addr_t dmaaddr; - /* ieee80211 TX status. Only used once per 802.11 frag. */ - bool is_last_fragment; -}; - -struct b43_dmaring; - -/* Lowlevel DMA operations that differ between 32bit and 64bit DMA. */ -struct b43_dma_ops { - struct b43_dmadesc_generic *(*idx2desc) (struct b43_dmaring * ring, - int slot, - struct b43_dmadesc_meta ** - meta); - void (*fill_descriptor) (struct b43_dmaring * ring, - struct b43_dmadesc_generic * desc, - dma_addr_t dmaaddr, u16 bufsize, int start, - int end, int irq); - void (*poke_tx) (struct b43_dmaring * ring, int slot); - void (*tx_suspend) (struct b43_dmaring * ring); - void (*tx_resume) (struct b43_dmaring * ring); - int (*get_current_rxslot) (struct b43_dmaring * ring); - void (*set_current_rxslot) (struct b43_dmaring * ring, int slot); -}; - -enum b43_dmatype { - B43_DMA_30BIT = 30, - B43_DMA_32BIT = 32, - B43_DMA_64BIT = 64, -}; - -enum b43_addrtype { - B43_DMA_ADDR_LOW, - B43_DMA_ADDR_HIGH, - B43_DMA_ADDR_EXT, -}; - -struct b43_dmaring { - /* Lowlevel DMA ops. */ - const struct b43_dma_ops *ops; - /* Kernel virtual base address of the ring memory. */ - void *descbase; - /* Meta data about all descriptors. */ - struct b43_dmadesc_meta *meta; - /* Cache of TX headers for each TX frame. - * This is to avoid an allocation on each TX. - * This is NULL for an RX ring. - */ - u8 *txhdr_cache; - /* (Unadjusted) DMA base bus-address of the ring memory. */ - dma_addr_t dmabase; - /* Number of descriptor slots in the ring. */ - int nr_slots; - /* Number of used descriptor slots. */ - int used_slots; - /* Currently used slot in the ring. */ - int current_slot; - /* Frameoffset in octets. */ - u32 frameoffset; - /* Descriptor buffer size. */ - u16 rx_buffersize; - /* The MMIO base register of the DMA controller. */ - u16 mmio_base; - /* DMA controller index number (0-5). */ - int index; - /* Boolean. Is this a TX ring? */ - bool tx; - /* The type of DMA engine used. */ - enum b43_dmatype type; - /* Boolean. Is this ring stopped at ieee80211 level? */ - bool stopped; - /* The QOS priority assigned to this ring. Only used for TX rings. - * This is the mac80211 "queue" value. */ - u8 queue_prio; - struct b43_wldev *dev; -#ifdef CONFIG_B43_DEBUG - /* Maximum number of used slots. */ - int max_used_slots; - /* Last time we injected a ring overflow. */ - unsigned long last_injected_overflow; - /* Statistics: Number of successfully transmitted packets */ - u64 nr_succeed_tx_packets; - /* Statistics: Number of failed TX packets */ - u64 nr_failed_tx_packets; - /* Statistics: Total number of TX plus all retries. */ - u64 nr_total_packet_tries; -#endif /* CONFIG_B43_DEBUG */ -}; - -static inline u32 b43_dma_read(struct b43_dmaring *ring, u16 offset) -{ - return b43_read32(ring->dev, ring->mmio_base + offset); -} - -static inline void b43_dma_write(struct b43_dmaring *ring, u16 offset, u32 value) -{ - b43_write32(ring->dev, ring->mmio_base + offset, value); -} - -int b43_dma_init(struct b43_wldev *dev); -void b43_dma_free(struct b43_wldev *dev); - -void b43_dma_tx_suspend(struct b43_wldev *dev); -void b43_dma_tx_resume(struct b43_wldev *dev); - -int b43_dma_tx(struct b43_wldev *dev, - struct sk_buff *skb); -void b43_dma_handle_txstatus(struct b43_wldev *dev, - const struct b43_txstatus *status); - -void b43_dma_handle_rx_overflow(struct b43_dmaring *ring); - -void b43_dma_rx(struct b43_dmaring *ring); - -void b43_dma_direct_fifo_rx(struct b43_wldev *dev, - unsigned int engine_index, bool enable); - -#endif /* B43_DMA_H_ */ diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c deleted file mode 100644 index d79ab2a227e1..000000000000 --- a/drivers/net/wireless/b43/leds.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - - Broadcom B43 wireless driver - LED control - - Copyright (c) 2005 Martin Langer , - Copyright (c) 2005 Stefano Brivio - Copyright (c) 2005-2007 Michael Buesch - Copyright (c) 2005 Danny van Dyk - Copyright (c) 2005 Andreas Jaggi - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "leds.h" -#include "rfkill.h" - - -static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index, - bool activelow) -{ - u16 ctl; - - ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); - if (activelow) - ctl &= ~(1 << led_index); - else - ctl |= (1 << led_index); - b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); -} - -static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index, - bool activelow) -{ - u16 ctl; - - ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); - if (activelow) - ctl |= (1 << led_index); - else - ctl &= ~(1 << led_index); - b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); -} - -static void b43_led_update(struct b43_wldev *dev, - struct b43_led *led) -{ - bool radio_enabled; - bool turn_on; - - if (!led->wl) - return; - - radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable); - - /* The led->state read is racy, but we don't care. In case we raced - * with the brightness_set handler, we will be called again soon - * to fixup our state. */ - if (radio_enabled) - turn_on = atomic_read(&led->state) != LED_OFF; - else - turn_on = false; - if (turn_on == led->hw_state) - return; - led->hw_state = turn_on; - - if (turn_on) - b43_led_turn_on(dev, led->index, led->activelow); - else - b43_led_turn_off(dev, led->index, led->activelow); -} - -static void b43_leds_work(struct work_struct *work) -{ - struct b43_leds *leds = container_of(work, struct b43_leds, work); - struct b43_wl *wl = container_of(leds, struct b43_wl, leds); - struct b43_wldev *dev; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED)) - goto out_unlock; - - b43_led_update(dev, &wl->leds.led_tx); - b43_led_update(dev, &wl->leds.led_rx); - b43_led_update(dev, &wl->leds.led_radio); - b43_led_update(dev, &wl->leds.led_assoc); - -out_unlock: - mutex_unlock(&wl->mutex); -} - -/* Callback from the LED subsystem. */ -static void b43_led_brightness_set(struct led_classdev *led_dev, - enum led_brightness brightness) -{ - struct b43_led *led = container_of(led_dev, struct b43_led, led_dev); - struct b43_wl *wl = led->wl; - - if (likely(!wl->leds.stop)) { - atomic_set(&led->state, brightness); - ieee80211_queue_work(wl->hw, &wl->leds.work); - } -} - -static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, - const char *name, const char *default_trigger, - u8 led_index, bool activelow) -{ - int err; - - if (led->wl) - return -EEXIST; - if (!default_trigger) - return -EINVAL; - led->wl = dev->wl; - led->index = led_index; - led->activelow = activelow; - strncpy(led->name, name, sizeof(led->name)); - atomic_set(&led->state, 0); - - led->led_dev.name = led->name; - led->led_dev.default_trigger = default_trigger; - led->led_dev.brightness_set = b43_led_brightness_set; - - err = led_classdev_register(dev->dev->dev, &led->led_dev); - if (err) { - b43warn(dev->wl, "LEDs: Failed to register %s\n", name); - led->wl = NULL; - return err; - } - - return 0; -} - -static void b43_unregister_led(struct b43_led *led) -{ - if (!led->wl) - return; - led_classdev_unregister(&led->led_dev); - led->wl = NULL; -} - -static void b43_map_led(struct b43_wldev *dev, - u8 led_index, - enum b43_led_behaviour behaviour, - bool activelow) -{ - struct ieee80211_hw *hw = dev->wl->hw; - char name[B43_LED_MAX_NAME_LEN + 1]; - - /* Map the b43 specific LED behaviour value to the - * generic LED triggers. */ - switch (behaviour) { - case B43_LED_INACTIVE: - case B43_LED_OFF: - case B43_LED_ON: - break; - case B43_LED_ACTIVITY: - case B43_LED_TRANSFER: - case B43_LED_APTRANSFER: - snprintf(name, sizeof(name), - "b43-%s::tx", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->wl->leds.led_tx, name, - ieee80211_get_tx_led_name(hw), - led_index, activelow); - snprintf(name, sizeof(name), - "b43-%s::rx", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->wl->leds.led_rx, name, - ieee80211_get_rx_led_name(hw), - led_index, activelow); - break; - case B43_LED_RADIO_ALL: - case B43_LED_RADIO_A: - case B43_LED_RADIO_B: - case B43_LED_MODE_BG: - snprintf(name, sizeof(name), - "b43-%s::radio", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->wl->leds.led_radio, name, - ieee80211_get_radio_led_name(hw), - led_index, activelow); - break; - case B43_LED_WEIRD: - case B43_LED_ASSOC: - snprintf(name, sizeof(name), - "b43-%s::assoc", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->wl->leds.led_assoc, name, - ieee80211_get_assoc_led_name(hw), - led_index, activelow); - break; - default: - b43warn(dev->wl, "LEDs: Unknown behaviour 0x%02X\n", - behaviour); - break; - } -} - -static void b43_led_get_sprominfo(struct b43_wldev *dev, - unsigned int led_index, - enum b43_led_behaviour *behaviour, - bool *activelow) -{ - u8 sprom[4]; - - sprom[0] = dev->dev->bus_sprom->gpio0; - sprom[1] = dev->dev->bus_sprom->gpio1; - sprom[2] = dev->dev->bus_sprom->gpio2; - sprom[3] = dev->dev->bus_sprom->gpio3; - - if (sprom[led_index] == 0xFF) { - /* There is no LED information in the SPROM - * for this LED. Hardcode it here. */ - *activelow = false; - switch (led_index) { - case 0: - *behaviour = B43_LED_ACTIVITY; - *activelow = true; - if (dev->dev->board_vendor == PCI_VENDOR_ID_COMPAQ) - *behaviour = B43_LED_RADIO_ALL; - break; - case 1: - *behaviour = B43_LED_RADIO_B; - if (dev->dev->board_vendor == PCI_VENDOR_ID_ASUSTEK) - *behaviour = B43_LED_ASSOC; - break; - case 2: - *behaviour = B43_LED_RADIO_A; - break; - case 3: - *behaviour = B43_LED_OFF; - break; - default: - *behaviour = B43_LED_OFF; - B43_WARN_ON(1); - return; - } - } else { - *behaviour = sprom[led_index] & B43_LED_BEHAVIOUR; - *activelow = !!(sprom[led_index] & B43_LED_ACTIVELOW); - } -} - -void b43_leds_init(struct b43_wldev *dev) -{ - struct b43_led *led; - unsigned int i; - enum b43_led_behaviour behaviour; - bool activelow; - - /* Sync the RF-kill LED state (if we have one) with radio and switch states. */ - led = &dev->wl->leds.led_radio; - if (led->wl) { - if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) { - b43_led_turn_on(dev, led->index, led->activelow); - led->hw_state = true; - atomic_set(&led->state, 1); - } else { - b43_led_turn_off(dev, led->index, led->activelow); - led->hw_state = false; - atomic_set(&led->state, 0); - } - } - - /* Initialize TX/RX/ASSOC leds */ - led = &dev->wl->leds.led_tx; - if (led->wl) { - b43_led_turn_off(dev, led->index, led->activelow); - led->hw_state = false; - atomic_set(&led->state, 0); - } - led = &dev->wl->leds.led_rx; - if (led->wl) { - b43_led_turn_off(dev, led->index, led->activelow); - led->hw_state = false; - atomic_set(&led->state, 0); - } - led = &dev->wl->leds.led_assoc; - if (led->wl) { - b43_led_turn_off(dev, led->index, led->activelow); - led->hw_state = false; - atomic_set(&led->state, 0); - } - - /* Initialize other LED states. */ - for (i = 0; i < B43_MAX_NR_LEDS; i++) { - b43_led_get_sprominfo(dev, i, &behaviour, &activelow); - switch (behaviour) { - case B43_LED_OFF: - b43_led_turn_off(dev, i, activelow); - break; - case B43_LED_ON: - b43_led_turn_on(dev, i, activelow); - break; - default: - /* Leave others as-is. */ - break; - } - } - - dev->wl->leds.stop = 0; -} - -void b43_leds_exit(struct b43_wldev *dev) -{ - struct b43_leds *leds = &dev->wl->leds; - - b43_led_turn_off(dev, leds->led_tx.index, leds->led_tx.activelow); - b43_led_turn_off(dev, leds->led_rx.index, leds->led_rx.activelow); - b43_led_turn_off(dev, leds->led_assoc.index, leds->led_assoc.activelow); - b43_led_turn_off(dev, leds->led_radio.index, leds->led_radio.activelow); -} - -void b43_leds_stop(struct b43_wldev *dev) -{ - struct b43_leds *leds = &dev->wl->leds; - - leds->stop = 1; - cancel_work_sync(&leds->work); -} - -void b43_leds_register(struct b43_wldev *dev) -{ - unsigned int i; - enum b43_led_behaviour behaviour; - bool activelow; - - INIT_WORK(&dev->wl->leds.work, b43_leds_work); - - /* Register the LEDs to the LED subsystem. */ - for (i = 0; i < B43_MAX_NR_LEDS; i++) { - b43_led_get_sprominfo(dev, i, &behaviour, &activelow); - b43_map_led(dev, i, behaviour, activelow); - } -} - -void b43_leds_unregister(struct b43_wl *wl) -{ - struct b43_leds *leds = &wl->leds; - - b43_unregister_led(&leds->led_tx); - b43_unregister_led(&leds->led_rx); - b43_unregister_led(&leds->led_assoc); - b43_unregister_led(&leds->led_radio); -} diff --git a/drivers/net/wireless/b43/leds.h b/drivers/net/wireless/b43/leds.h deleted file mode 100644 index 32b66d53cdac..000000000000 --- a/drivers/net/wireless/b43/leds.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef B43_LEDS_H_ -#define B43_LEDS_H_ - -struct b43_wl; -struct b43_wldev; - -#ifdef CONFIG_B43_LEDS - -#include -#include -#include - - -#define B43_LED_MAX_NAME_LEN 31 - -struct b43_led { - struct b43_wl *wl; - /* The LED class device */ - struct led_classdev led_dev; - /* The index number of the LED. */ - u8 index; - /* If activelow is true, the LED is ON if the - * bit is switched off. */ - bool activelow; - /* The unique name string for this LED device. */ - char name[B43_LED_MAX_NAME_LEN + 1]; - /* The current status of the LED. This is updated locklessly. */ - atomic_t state; - /* The active state in hardware. */ - bool hw_state; -}; - -struct b43_leds { - struct b43_led led_tx; - struct b43_led led_rx; - struct b43_led led_radio; - struct b43_led led_assoc; - - bool stop; - struct work_struct work; -}; - -#define B43_MAX_NR_LEDS 4 - -#define B43_LED_BEHAVIOUR 0x7F -#define B43_LED_ACTIVELOW 0x80 -/* LED behaviour values */ -enum b43_led_behaviour { - B43_LED_OFF, - B43_LED_ON, - B43_LED_ACTIVITY, - B43_LED_RADIO_ALL, - B43_LED_RADIO_A, - B43_LED_RADIO_B, - B43_LED_MODE_BG, - B43_LED_TRANSFER, - B43_LED_APTRANSFER, - B43_LED_WEIRD, //FIXME - B43_LED_ASSOC, - B43_LED_INACTIVE, -}; - -void b43_leds_register(struct b43_wldev *dev); -void b43_leds_unregister(struct b43_wl *wl); -void b43_leds_init(struct b43_wldev *dev); -void b43_leds_exit(struct b43_wldev *dev); -void b43_leds_stop(struct b43_wldev *dev); - - -#else /* CONFIG_B43_LEDS */ -/* LED support disabled */ - -struct b43_leds { - /* empty */ -}; - -static inline void b43_leds_register(struct b43_wldev *dev) -{ -} -static inline void b43_leds_unregister(struct b43_wl *wl) -{ -} -static inline void b43_leds_init(struct b43_wldev *dev) -{ -} -static inline void b43_leds_exit(struct b43_wldev *dev) -{ -} -static inline void b43_leds_stop(struct b43_wldev *dev) -{ -} -#endif /* CONFIG_B43_LEDS */ - -#endif /* B43_LEDS_H_ */ diff --git a/drivers/net/wireless/b43/lo.c b/drivers/net/wireless/b43/lo.c deleted file mode 100644 index a335f94c72ff..000000000000 --- a/drivers/net/wireless/b43/lo.c +++ /dev/null @@ -1,1016 +0,0 @@ -/* - - Broadcom B43 wireless driver - - G PHY LO (LocalOscillator) Measuring and Control routines - - Copyright (c) 2005 Martin Langer , - Copyright (c) 2005, 2006 Stefano Brivio - Copyright (c) 2005-2007 Michael Buesch - Copyright (c) 2005, 2006 Danny van Dyk - Copyright (c) 2005, 2006 Andreas Jaggi - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "lo.h" -#include "phy_g.h" -#include "main.h" - -#include -#include -#include - - -static struct b43_lo_calib *b43_find_lo_calib(struct b43_txpower_lo_control *lo, - const struct b43_bbatt *bbatt, - const struct b43_rfatt *rfatt) -{ - struct b43_lo_calib *c; - - list_for_each_entry(c, &lo->calib_list, list) { - if (!b43_compare_bbatt(&c->bbatt, bbatt)) - continue; - if (!b43_compare_rfatt(&c->rfatt, rfatt)) - continue; - return c; - } - - return NULL; -} - -/* Write the LocalOscillator Control (adjust) value-pair. */ -static void b43_lo_write(struct b43_wldev *dev, struct b43_loctl *control) -{ - struct b43_phy *phy = &dev->phy; - u16 value; - - if (B43_DEBUG) { - if (unlikely(abs(control->i) > 16 || abs(control->q) > 16)) { - b43dbg(dev->wl, "Invalid LO control pair " - "(I: %d, Q: %d)\n", control->i, control->q); - dump_stack(); - return; - } - } - B43_WARN_ON(phy->type != B43_PHYTYPE_G); - - value = (u8) (control->q); - value |= ((u8) (control->i)) << 8; - b43_phy_write(dev, B43_PHY_LO_CTL, value); -} - -static u16 lo_measure_feedthrough(struct b43_wldev *dev, - u16 lna, u16 pga, u16 trsw_rx) -{ - struct b43_phy *phy = &dev->phy; - u16 rfover; - u16 feedthrough; - - if (phy->gmode) { - lna <<= B43_PHY_RFOVERVAL_LNA_SHIFT; - pga <<= B43_PHY_RFOVERVAL_PGA_SHIFT; - - B43_WARN_ON(lna & ~B43_PHY_RFOVERVAL_LNA); - B43_WARN_ON(pga & ~B43_PHY_RFOVERVAL_PGA); -/*FIXME This assertion fails B43_WARN_ON(trsw_rx & ~(B43_PHY_RFOVERVAL_TRSWRX | - B43_PHY_RFOVERVAL_BW)); -*/ - trsw_rx &= (B43_PHY_RFOVERVAL_TRSWRX | B43_PHY_RFOVERVAL_BW); - - /* Construct the RF Override Value */ - rfover = B43_PHY_RFOVERVAL_UNK; - rfover |= pga; - rfover |= lna; - rfover |= trsw_rx; - if ((dev->dev->bus_sprom->boardflags_lo & B43_BFL_EXTLNA) - && phy->rev > 6) - rfover |= B43_PHY_RFOVERVAL_EXTLNA; - - b43_phy_write(dev, B43_PHY_PGACTL, 0xE300); - b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); - udelay(10); - rfover |= B43_PHY_RFOVERVAL_BW_LBW; - b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); - udelay(10); - rfover |= B43_PHY_RFOVERVAL_BW_LPF; - b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); - udelay(10); - b43_phy_write(dev, B43_PHY_PGACTL, 0xF300); - } else { - pga |= B43_PHY_PGACTL_UNKNOWN; - b43_phy_write(dev, B43_PHY_PGACTL, pga); - udelay(10); - pga |= B43_PHY_PGACTL_LOWBANDW; - b43_phy_write(dev, B43_PHY_PGACTL, pga); - udelay(10); - pga |= B43_PHY_PGACTL_LPF; - b43_phy_write(dev, B43_PHY_PGACTL, pga); - } - udelay(21); - feedthrough = b43_phy_read(dev, B43_PHY_LO_LEAKAGE); - - /* This is a good place to check if we need to relax a bit, - * as this is the main function called regularly - * in the LO calibration. */ - cond_resched(); - - return feedthrough; -} - -/* TXCTL Register and Value Table. - * Returns the "TXCTL Register". - * "value" is the "TXCTL Value". - * "pad_mix_gain" is the PAD Mixer Gain. - */ -static u16 lo_txctl_register_table(struct b43_wldev *dev, - u16 *value, u16 *pad_mix_gain) -{ - struct b43_phy *phy = &dev->phy; - u16 reg, v, padmix; - - if (phy->type == B43_PHYTYPE_B) { - v = 0x30; - if (phy->radio_rev <= 5) { - reg = 0x43; - padmix = 0; - } else { - reg = 0x52; - padmix = 5; - } - } else { - if (phy->rev >= 2 && phy->radio_rev == 8) { - reg = 0x43; - v = 0x10; - padmix = 2; - } else { - reg = 0x52; - v = 0x30; - padmix = 5; - } - } - if (value) - *value = v; - if (pad_mix_gain) - *pad_mix_gain = padmix; - - return reg; -} - -static void lo_measure_txctl_values(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_txpower_lo_control *lo = gphy->lo_control; - u16 reg, mask; - u16 trsw_rx, pga; - u16 radio_pctl_reg; - - static const u8 tx_bias_values[] = { - 0x09, 0x08, 0x0A, 0x01, 0x00, - 0x02, 0x05, 0x04, 0x06, - }; - static const u8 tx_magn_values[] = { - 0x70, 0x40, - }; - - if (!has_loopback_gain(phy)) { - radio_pctl_reg = 6; - trsw_rx = 2; - pga = 0; - } else { - int lb_gain; /* Loopback gain (in dB) */ - - trsw_rx = 0; - lb_gain = gphy->max_lb_gain / 2; - if (lb_gain > 10) { - radio_pctl_reg = 0; - pga = abs(10 - lb_gain) / 6; - pga = clamp_val(pga, 0, 15); - } else { - int cmp_val; - int tmp; - - pga = 0; - cmp_val = 0x24; - if ((phy->rev >= 2) && - (phy->radio_ver == 0x2050) && (phy->radio_rev == 8)) - cmp_val = 0x3C; - tmp = lb_gain; - if ((10 - lb_gain) < cmp_val) - tmp = (10 - lb_gain); - if (tmp < 0) - tmp += 6; - else - tmp += 3; - cmp_val /= 4; - tmp /= 4; - if (tmp >= cmp_val) - radio_pctl_reg = cmp_val; - else - radio_pctl_reg = tmp; - } - } - b43_radio_maskset(dev, 0x43, 0xFFF0, radio_pctl_reg); - b43_gphy_set_baseband_attenuation(dev, 2); - - reg = lo_txctl_register_table(dev, &mask, NULL); - mask = ~mask; - b43_radio_mask(dev, reg, mask); - - if (has_tx_magnification(phy)) { - int i, j; - int feedthrough; - int min_feedth = 0xFFFF; - u8 tx_magn, tx_bias; - - for (i = 0; i < ARRAY_SIZE(tx_magn_values); i++) { - tx_magn = tx_magn_values[i]; - b43_radio_maskset(dev, 0x52, 0xFF0F, tx_magn); - for (j = 0; j < ARRAY_SIZE(tx_bias_values); j++) { - tx_bias = tx_bias_values[j]; - b43_radio_maskset(dev, 0x52, 0xFFF0, tx_bias); - feedthrough = - lo_measure_feedthrough(dev, 0, pga, - trsw_rx); - if (feedthrough < min_feedth) { - lo->tx_bias = tx_bias; - lo->tx_magn = tx_magn; - min_feedth = feedthrough; - } - if (lo->tx_bias == 0) - break; - } - b43_radio_write16(dev, 0x52, - (b43_radio_read16(dev, 0x52) - & 0xFF00) | lo->tx_bias | lo-> - tx_magn); - } - } else { - lo->tx_magn = 0; - lo->tx_bias = 0; - b43_radio_mask(dev, 0x52, 0xFFF0); /* TX bias == 0 */ - } - lo->txctl_measured_time = jiffies; -} - -static void lo_read_power_vector(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_txpower_lo_control *lo = gphy->lo_control; - int i; - u64 tmp; - u64 power_vector = 0; - - for (i = 0; i < 8; i += 2) { - tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x310 + i); - power_vector |= (tmp << (i * 8)); - /* Clear the vector on the device. */ - b43_shm_write16(dev, B43_SHM_SHARED, 0x310 + i, 0); - } - if (power_vector) - lo->power_vector = power_vector; - lo->pwr_vec_read_time = jiffies; -} - -/* 802.11/LO/GPHY/MeasuringGains */ -static void lo_measure_gain_values(struct b43_wldev *dev, - s16 max_rx_gain, int use_trsw_rx) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - u16 tmp; - - if (max_rx_gain < 0) - max_rx_gain = 0; - - if (has_loopback_gain(phy)) { - int trsw_rx_gain; - - if (use_trsw_rx) { - trsw_rx_gain = gphy->trsw_rx_gain / 2; - if (max_rx_gain >= trsw_rx_gain) { - trsw_rx_gain = max_rx_gain - trsw_rx_gain; - } - } else - trsw_rx_gain = max_rx_gain; - if (trsw_rx_gain < 9) { - gphy->lna_lod_gain = 0; - } else { - gphy->lna_lod_gain = 1; - trsw_rx_gain -= 8; - } - trsw_rx_gain = clamp_val(trsw_rx_gain, 0, 0x2D); - gphy->pga_gain = trsw_rx_gain / 3; - if (gphy->pga_gain >= 5) { - gphy->pga_gain -= 5; - gphy->lna_gain = 2; - } else - gphy->lna_gain = 0; - } else { - gphy->lna_gain = 0; - gphy->trsw_rx_gain = 0x20; - if (max_rx_gain >= 0x14) { - gphy->lna_lod_gain = 1; - gphy->pga_gain = 2; - } else if (max_rx_gain >= 0x12) { - gphy->lna_lod_gain = 1; - gphy->pga_gain = 1; - } else if (max_rx_gain >= 0xF) { - gphy->lna_lod_gain = 1; - gphy->pga_gain = 0; - } else { - gphy->lna_lod_gain = 0; - gphy->pga_gain = 0; - } - } - - tmp = b43_radio_read16(dev, 0x7A); - if (gphy->lna_lod_gain == 0) - tmp &= ~0x0008; - else - tmp |= 0x0008; - b43_radio_write16(dev, 0x7A, tmp); -} - -struct lo_g_saved_values { - u8 old_channel; - - /* Core registers */ - u16 reg_3F4; - u16 reg_3E2; - - /* PHY registers */ - u16 phy_lo_mask; - u16 phy_extg_01; - u16 phy_dacctl_hwpctl; - u16 phy_dacctl; - u16 phy_cck_14; - u16 phy_hpwr_tssictl; - u16 phy_analogover; - u16 phy_analogoverval; - u16 phy_rfover; - u16 phy_rfoverval; - u16 phy_classctl; - u16 phy_cck_3E; - u16 phy_crs0; - u16 phy_pgactl; - u16 phy_cck_2A; - u16 phy_syncctl; - u16 phy_cck_30; - u16 phy_cck_06; - - /* Radio registers */ - u16 radio_43; - u16 radio_7A; - u16 radio_52; -}; - -static void lo_measure_setup(struct b43_wldev *dev, - struct lo_g_saved_values *sav) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_txpower_lo_control *lo = gphy->lo_control; - u16 tmp; - - if (b43_has_hardware_pctl(dev)) { - sav->phy_lo_mask = b43_phy_read(dev, B43_PHY_LO_MASK); - sav->phy_extg_01 = b43_phy_read(dev, B43_PHY_EXTG(0x01)); - sav->phy_dacctl_hwpctl = b43_phy_read(dev, B43_PHY_DACCTL); - sav->phy_cck_14 = b43_phy_read(dev, B43_PHY_CCK(0x14)); - sav->phy_hpwr_tssictl = b43_phy_read(dev, B43_PHY_HPWR_TSSICTL); - - b43_phy_set(dev, B43_PHY_HPWR_TSSICTL, 0x100); - b43_phy_set(dev, B43_PHY_EXTG(0x01), 0x40); - b43_phy_set(dev, B43_PHY_DACCTL, 0x40); - b43_phy_set(dev, B43_PHY_CCK(0x14), 0x200); - } - if (phy->type == B43_PHYTYPE_B && - phy->radio_ver == 0x2050 && phy->radio_rev < 6) { - b43_phy_write(dev, B43_PHY_CCK(0x16), 0x410); - b43_phy_write(dev, B43_PHY_CCK(0x17), 0x820); - } - if (phy->rev >= 2) { - sav->phy_analogover = b43_phy_read(dev, B43_PHY_ANALOGOVER); - sav->phy_analogoverval = - b43_phy_read(dev, B43_PHY_ANALOGOVERVAL); - sav->phy_rfover = b43_phy_read(dev, B43_PHY_RFOVER); - sav->phy_rfoverval = b43_phy_read(dev, B43_PHY_RFOVERVAL); - sav->phy_classctl = b43_phy_read(dev, B43_PHY_CLASSCTL); - sav->phy_cck_3E = b43_phy_read(dev, B43_PHY_CCK(0x3E)); - sav->phy_crs0 = b43_phy_read(dev, B43_PHY_CRS0); - - b43_phy_mask(dev, B43_PHY_CLASSCTL, 0xFFFC); - b43_phy_mask(dev, B43_PHY_CRS0, 0x7FFF); - b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0003); - b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFC); - if (phy->type == B43_PHYTYPE_G) { - if ((phy->rev >= 7) && - (sprom->boardflags_lo & B43_BFL_EXTLNA)) { - b43_phy_write(dev, B43_PHY_RFOVER, 0x933); - } else { - b43_phy_write(dev, B43_PHY_RFOVER, 0x133); - } - } else { - b43_phy_write(dev, B43_PHY_RFOVER, 0); - } - b43_phy_write(dev, B43_PHY_CCK(0x3E), 0); - } - sav->reg_3F4 = b43_read16(dev, 0x3F4); - sav->reg_3E2 = b43_read16(dev, 0x3E2); - sav->radio_43 = b43_radio_read16(dev, 0x43); - sav->radio_7A = b43_radio_read16(dev, 0x7A); - sav->phy_pgactl = b43_phy_read(dev, B43_PHY_PGACTL); - sav->phy_cck_2A = b43_phy_read(dev, B43_PHY_CCK(0x2A)); - sav->phy_syncctl = b43_phy_read(dev, B43_PHY_SYNCCTL); - sav->phy_dacctl = b43_phy_read(dev, B43_PHY_DACCTL); - - if (!has_tx_magnification(phy)) { - sav->radio_52 = b43_radio_read16(dev, 0x52); - sav->radio_52 &= 0x00F0; - } - if (phy->type == B43_PHYTYPE_B) { - sav->phy_cck_30 = b43_phy_read(dev, B43_PHY_CCK(0x30)); - sav->phy_cck_06 = b43_phy_read(dev, B43_PHY_CCK(0x06)); - b43_phy_write(dev, B43_PHY_CCK(0x30), 0x00FF); - b43_phy_write(dev, B43_PHY_CCK(0x06), 0x3F3F); - } else { - b43_write16(dev, 0x3E2, b43_read16(dev, 0x3E2) - | 0x8000); - } - b43_write16(dev, 0x3F4, b43_read16(dev, 0x3F4) - & 0xF000); - - tmp = - (phy->type == B43_PHYTYPE_G) ? B43_PHY_LO_MASK : B43_PHY_CCK(0x2E); - b43_phy_write(dev, tmp, 0x007F); - - tmp = sav->phy_syncctl; - b43_phy_write(dev, B43_PHY_SYNCCTL, tmp & 0xFF7F); - tmp = sav->radio_7A; - b43_radio_write16(dev, 0x007A, tmp & 0xFFF0); - - b43_phy_write(dev, B43_PHY_CCK(0x2A), 0x8A3); - if (phy->type == B43_PHYTYPE_G || - (phy->type == B43_PHYTYPE_B && - phy->radio_ver == 0x2050 && phy->radio_rev >= 6)) { - b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x1003); - } else - b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x0802); - if (phy->rev >= 2) - b43_dummy_transmission(dev, false, true); - b43_gphy_channel_switch(dev, 6, 0); - b43_radio_read16(dev, 0x51); /* dummy read */ - if (phy->type == B43_PHYTYPE_G) - b43_phy_write(dev, B43_PHY_CCK(0x2F), 0); - - /* Re-measure the txctl values, if needed. */ - if (time_before(lo->txctl_measured_time, - jiffies - B43_LO_TXCTL_EXPIRE)) - lo_measure_txctl_values(dev); - - if (phy->type == B43_PHYTYPE_G && phy->rev >= 3) { - b43_phy_write(dev, B43_PHY_LO_MASK, 0xC078); - } else { - if (phy->type == B43_PHYTYPE_B) - b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8078); - else - b43_phy_write(dev, B43_PHY_LO_MASK, 0x8078); - } -} - -static void lo_measure_restore(struct b43_wldev *dev, - struct lo_g_saved_values *sav) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - u16 tmp; - - if (phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_PGACTL, 0xE300); - tmp = (gphy->pga_gain << 8); - b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA0); - udelay(5); - b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA2); - udelay(2); - b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA3); - } else { - tmp = (gphy->pga_gain | 0xEFA0); - b43_phy_write(dev, B43_PHY_PGACTL, tmp); - } - if (phy->type == B43_PHYTYPE_G) { - if (phy->rev >= 3) - b43_phy_write(dev, B43_PHY_CCK(0x2E), 0xC078); - else - b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8078); - if (phy->rev >= 2) - b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x0202); - else - b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x0101); - } - b43_write16(dev, 0x3F4, sav->reg_3F4); - b43_phy_write(dev, B43_PHY_PGACTL, sav->phy_pgactl); - b43_phy_write(dev, B43_PHY_CCK(0x2A), sav->phy_cck_2A); - b43_phy_write(dev, B43_PHY_SYNCCTL, sav->phy_syncctl); - b43_phy_write(dev, B43_PHY_DACCTL, sav->phy_dacctl); - b43_radio_write16(dev, 0x43, sav->radio_43); - b43_radio_write16(dev, 0x7A, sav->radio_7A); - if (!has_tx_magnification(phy)) { - tmp = sav->radio_52; - b43_radio_maskset(dev, 0x52, 0xFF0F, tmp); - } - b43_write16(dev, 0x3E2, sav->reg_3E2); - if (phy->type == B43_PHYTYPE_B && - phy->radio_ver == 0x2050 && phy->radio_rev <= 5) { - b43_phy_write(dev, B43_PHY_CCK(0x30), sav->phy_cck_30); - b43_phy_write(dev, B43_PHY_CCK(0x06), sav->phy_cck_06); - } - if (phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_ANALOGOVER, sav->phy_analogover); - b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, - sav->phy_analogoverval); - b43_phy_write(dev, B43_PHY_CLASSCTL, sav->phy_classctl); - b43_phy_write(dev, B43_PHY_RFOVER, sav->phy_rfover); - b43_phy_write(dev, B43_PHY_RFOVERVAL, sav->phy_rfoverval); - b43_phy_write(dev, B43_PHY_CCK(0x3E), sav->phy_cck_3E); - b43_phy_write(dev, B43_PHY_CRS0, sav->phy_crs0); - } - if (b43_has_hardware_pctl(dev)) { - tmp = (sav->phy_lo_mask & 0xBFFF); - b43_phy_write(dev, B43_PHY_LO_MASK, tmp); - b43_phy_write(dev, B43_PHY_EXTG(0x01), sav->phy_extg_01); - b43_phy_write(dev, B43_PHY_DACCTL, sav->phy_dacctl_hwpctl); - b43_phy_write(dev, B43_PHY_CCK(0x14), sav->phy_cck_14); - b43_phy_write(dev, B43_PHY_HPWR_TSSICTL, sav->phy_hpwr_tssictl); - } - b43_gphy_channel_switch(dev, sav->old_channel, 1); -} - -struct b43_lo_g_statemachine { - int current_state; - int nr_measured; - int state_val_multiplier; - u16 lowest_feedth; - struct b43_loctl min_loctl; -}; - -/* Loop over each possible value in this state. */ -static int lo_probe_possible_loctls(struct b43_wldev *dev, - struct b43_loctl *probe_loctl, - struct b43_lo_g_statemachine *d) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_loctl test_loctl; - struct b43_loctl orig_loctl; - struct b43_loctl prev_loctl = { - .i = -100, - .q = -100, - }; - int i; - int begin, end; - int found_lower = 0; - u16 feedth; - - static const struct b43_loctl modifiers[] = { - {.i = 1,.q = 1,}, - {.i = 1,.q = 0,}, - {.i = 1,.q = -1,}, - {.i = 0,.q = -1,}, - {.i = -1,.q = -1,}, - {.i = -1,.q = 0,}, - {.i = -1,.q = 1,}, - {.i = 0,.q = 1,}, - }; - - if (d->current_state == 0) { - begin = 1; - end = 8; - } else if (d->current_state % 2 == 0) { - begin = d->current_state - 1; - end = d->current_state + 1; - } else { - begin = d->current_state - 2; - end = d->current_state + 2; - } - if (begin < 1) - begin += 8; - if (end > 8) - end -= 8; - - memcpy(&orig_loctl, probe_loctl, sizeof(struct b43_loctl)); - i = begin; - d->current_state = i; - while (1) { - B43_WARN_ON(!(i >= 1 && i <= 8)); - memcpy(&test_loctl, &orig_loctl, sizeof(struct b43_loctl)); - test_loctl.i += modifiers[i - 1].i * d->state_val_multiplier; - test_loctl.q += modifiers[i - 1].q * d->state_val_multiplier; - if ((test_loctl.i != prev_loctl.i || - test_loctl.q != prev_loctl.q) && - (abs(test_loctl.i) <= 16 && abs(test_loctl.q) <= 16)) { - b43_lo_write(dev, &test_loctl); - feedth = lo_measure_feedthrough(dev, gphy->lna_gain, - gphy->pga_gain, - gphy->trsw_rx_gain); - if (feedth < d->lowest_feedth) { - memcpy(probe_loctl, &test_loctl, - sizeof(struct b43_loctl)); - found_lower = 1; - d->lowest_feedth = feedth; - if ((d->nr_measured < 2) && - !has_loopback_gain(phy)) - break; - } - } - memcpy(&prev_loctl, &test_loctl, sizeof(prev_loctl)); - if (i == end) - break; - if (i == 8) - i = 1; - else - i++; - d->current_state = i; - } - - return found_lower; -} - -static void lo_probe_loctls_statemachine(struct b43_wldev *dev, - struct b43_loctl *loctl, - int *max_rx_gain) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_lo_g_statemachine d; - u16 feedth; - int found_lower; - struct b43_loctl probe_loctl; - int max_repeat = 1, repeat_cnt = 0; - - d.nr_measured = 0; - d.state_val_multiplier = 1; - if (has_loopback_gain(phy)) - d.state_val_multiplier = 3; - - memcpy(&d.min_loctl, loctl, sizeof(struct b43_loctl)); - if (has_loopback_gain(phy)) - max_repeat = 4; - do { - b43_lo_write(dev, &d.min_loctl); - feedth = lo_measure_feedthrough(dev, gphy->lna_gain, - gphy->pga_gain, - gphy->trsw_rx_gain); - if (feedth < 0x258) { - if (feedth >= 0x12C) - *max_rx_gain += 6; - else - *max_rx_gain += 3; - feedth = lo_measure_feedthrough(dev, gphy->lna_gain, - gphy->pga_gain, - gphy->trsw_rx_gain); - } - d.lowest_feedth = feedth; - - d.current_state = 0; - do { - B43_WARN_ON(! - (d.current_state >= 0 - && d.current_state <= 8)); - memcpy(&probe_loctl, &d.min_loctl, - sizeof(struct b43_loctl)); - found_lower = - lo_probe_possible_loctls(dev, &probe_loctl, &d); - if (!found_lower) - break; - if ((probe_loctl.i == d.min_loctl.i) && - (probe_loctl.q == d.min_loctl.q)) - break; - memcpy(&d.min_loctl, &probe_loctl, - sizeof(struct b43_loctl)); - d.nr_measured++; - } while (d.nr_measured < 24); - memcpy(loctl, &d.min_loctl, sizeof(struct b43_loctl)); - - if (has_loopback_gain(phy)) { - if (d.lowest_feedth > 0x1194) - *max_rx_gain -= 6; - else if (d.lowest_feedth < 0x5DC) - *max_rx_gain += 3; - if (repeat_cnt == 0) { - if (d.lowest_feedth <= 0x5DC) { - d.state_val_multiplier = 1; - repeat_cnt++; - } else - d.state_val_multiplier = 2; - } else if (repeat_cnt == 2) - d.state_val_multiplier = 1; - } - lo_measure_gain_values(dev, *max_rx_gain, - has_loopback_gain(phy)); - } while (++repeat_cnt < max_repeat); -} - -static -struct b43_lo_calib *b43_calibrate_lo_setting(struct b43_wldev *dev, - const struct b43_bbatt *bbatt, - const struct b43_rfatt *rfatt) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_loctl loctl = { - .i = 0, - .q = 0, - }; - int max_rx_gain; - struct b43_lo_calib *cal; - struct lo_g_saved_values uninitialized_var(saved_regs); - /* Values from the "TXCTL Register and Value Table" */ - u16 txctl_reg; - u16 txctl_value; - u16 pad_mix_gain; - - saved_regs.old_channel = phy->channel; - b43_mac_suspend(dev); - lo_measure_setup(dev, &saved_regs); - - txctl_reg = lo_txctl_register_table(dev, &txctl_value, &pad_mix_gain); - - b43_radio_maskset(dev, 0x43, 0xFFF0, rfatt->att); - b43_radio_maskset(dev, txctl_reg, ~txctl_value, (rfatt->with_padmix ? txctl_value :0)); - - max_rx_gain = rfatt->att * 2; - max_rx_gain += bbatt->att / 2; - if (rfatt->with_padmix) - max_rx_gain -= pad_mix_gain; - if (has_loopback_gain(phy)) - max_rx_gain += gphy->max_lb_gain; - lo_measure_gain_values(dev, max_rx_gain, - has_loopback_gain(phy)); - - b43_gphy_set_baseband_attenuation(dev, bbatt->att); - lo_probe_loctls_statemachine(dev, &loctl, &max_rx_gain); - - lo_measure_restore(dev, &saved_regs); - b43_mac_enable(dev); - - if (b43_debug(dev, B43_DBG_LO)) { - b43dbg(dev->wl, "LO: Calibrated for BB(%u), RF(%u,%u) " - "=> I=%d Q=%d\n", - bbatt->att, rfatt->att, rfatt->with_padmix, - loctl.i, loctl.q); - } - - cal = kmalloc(sizeof(*cal), GFP_KERNEL); - if (!cal) { - b43warn(dev->wl, "LO calib: out of memory\n"); - return NULL; - } - memcpy(&cal->bbatt, bbatt, sizeof(*bbatt)); - memcpy(&cal->rfatt, rfatt, sizeof(*rfatt)); - memcpy(&cal->ctl, &loctl, sizeof(loctl)); - cal->calib_time = jiffies; - INIT_LIST_HEAD(&cal->list); - - return cal; -} - -/* Get a calibrated LO setting for the given attenuation values. - * Might return a NULL pointer under OOM! */ -static -struct b43_lo_calib *b43_get_calib_lo_settings(struct b43_wldev *dev, - const struct b43_bbatt *bbatt, - const struct b43_rfatt *rfatt) -{ - struct b43_txpower_lo_control *lo = dev->phy.g->lo_control; - struct b43_lo_calib *c; - - c = b43_find_lo_calib(lo, bbatt, rfatt); - if (c) - return c; - /* Not in the list of calibrated LO settings. - * Calibrate it now. */ - c = b43_calibrate_lo_setting(dev, bbatt, rfatt); - if (!c) - return NULL; - list_add(&c->list, &lo->calib_list); - - return c; -} - -void b43_gphy_dc_lt_init(struct b43_wldev *dev, bool update_all) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_txpower_lo_control *lo = gphy->lo_control; - int i; - int rf_offset, bb_offset; - const struct b43_rfatt *rfatt; - const struct b43_bbatt *bbatt; - u64 power_vector; - bool table_changed = false; - - BUILD_BUG_ON(B43_DC_LT_SIZE != 32); - B43_WARN_ON(lo->rfatt_list.len * lo->bbatt_list.len > 64); - - power_vector = lo->power_vector; - if (!update_all && !power_vector) - return; /* Nothing to do. */ - - /* Suspend the MAC now to avoid continuous suspend/enable - * cycles in the loop. */ - b43_mac_suspend(dev); - - for (i = 0; i < B43_DC_LT_SIZE * 2; i++) { - struct b43_lo_calib *cal; - int idx; - u16 val; - - if (!update_all && !(power_vector & (((u64)1ULL) << i))) - continue; - /* Update the table entry for this power_vector bit. - * The table rows are RFatt entries and columns are BBatt. */ - bb_offset = i / lo->rfatt_list.len; - rf_offset = i % lo->rfatt_list.len; - bbatt = &(lo->bbatt_list.list[bb_offset]); - rfatt = &(lo->rfatt_list.list[rf_offset]); - - cal = b43_calibrate_lo_setting(dev, bbatt, rfatt); - if (!cal) { - b43warn(dev->wl, "LO: Could not " - "calibrate DC table entry\n"); - continue; - } - /*FIXME: Is Q really in the low nibble? */ - val = (u8)(cal->ctl.q); - val |= ((u8)(cal->ctl.i)) << 4; - kfree(cal); - - /* Get the index into the hardware DC LT. */ - idx = i / 2; - /* Change the table in memory. */ - if (i % 2) { - /* Change the high byte. */ - lo->dc_lt[idx] = (lo->dc_lt[idx] & 0x00FF) - | ((val & 0x00FF) << 8); - } else { - /* Change the low byte. */ - lo->dc_lt[idx] = (lo->dc_lt[idx] & 0xFF00) - | (val & 0x00FF); - } - table_changed = true; - } - if (table_changed) { - /* The table changed in memory. Update the hardware table. */ - for (i = 0; i < B43_DC_LT_SIZE; i++) - b43_phy_write(dev, 0x3A0 + i, lo->dc_lt[i]); - } - b43_mac_enable(dev); -} - -/* Fixup the RF attenuation value for the case where we are - * using the PAD mixer. */ -static inline void b43_lo_fixup_rfatt(struct b43_rfatt *rf) -{ - if (!rf->with_padmix) - return; - if ((rf->att != 1) && (rf->att != 2) && (rf->att != 3)) - rf->att = 4; -} - -void b43_lo_g_adjust(struct b43_wldev *dev) -{ - struct b43_phy_g *gphy = dev->phy.g; - struct b43_lo_calib *cal; - struct b43_rfatt rf; - - memcpy(&rf, &gphy->rfatt, sizeof(rf)); - b43_lo_fixup_rfatt(&rf); - - cal = b43_get_calib_lo_settings(dev, &gphy->bbatt, &rf); - if (!cal) - return; - b43_lo_write(dev, &cal->ctl); -} - -void b43_lo_g_adjust_to(struct b43_wldev *dev, - u16 rfatt, u16 bbatt, u16 tx_control) -{ - struct b43_rfatt rf; - struct b43_bbatt bb; - struct b43_lo_calib *cal; - - memset(&rf, 0, sizeof(rf)); - memset(&bb, 0, sizeof(bb)); - rf.att = rfatt; - bb.att = bbatt; - b43_lo_fixup_rfatt(&rf); - cal = b43_get_calib_lo_settings(dev, &bb, &rf); - if (!cal) - return; - b43_lo_write(dev, &cal->ctl); -} - -/* Periodic LO maintenance work */ -void b43_lo_g_maintenance_work(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_txpower_lo_control *lo = gphy->lo_control; - unsigned long now; - unsigned long expire; - struct b43_lo_calib *cal, *tmp; - bool current_item_expired = false; - bool hwpctl; - - if (!lo) - return; - now = jiffies; - hwpctl = b43_has_hardware_pctl(dev); - - if (hwpctl) { - /* Read the power vector and update it, if needed. */ - expire = now - B43_LO_PWRVEC_EXPIRE; - if (time_before(lo->pwr_vec_read_time, expire)) { - lo_read_power_vector(dev); - b43_gphy_dc_lt_init(dev, 0); - } - //FIXME Recalc the whole DC table from time to time? - } - - if (hwpctl) - return; - /* Search for expired LO settings. Remove them. - * Recalibrate the current setting, if expired. */ - expire = now - B43_LO_CALIB_EXPIRE; - list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) { - if (!time_before(cal->calib_time, expire)) - continue; - /* This item expired. */ - if (b43_compare_bbatt(&cal->bbatt, &gphy->bbatt) && - b43_compare_rfatt(&cal->rfatt, &gphy->rfatt)) { - B43_WARN_ON(current_item_expired); - current_item_expired = true; - } - if (b43_debug(dev, B43_DBG_LO)) { - b43dbg(dev->wl, "LO: Item BB(%u), RF(%u,%u), " - "I=%d, Q=%d expired\n", - cal->bbatt.att, cal->rfatt.att, - cal->rfatt.with_padmix, - cal->ctl.i, cal->ctl.q); - } - list_del(&cal->list); - kfree(cal); - } - if (current_item_expired || unlikely(list_empty(&lo->calib_list))) { - /* Recalibrate currently used LO setting. */ - if (b43_debug(dev, B43_DBG_LO)) - b43dbg(dev->wl, "LO: Recalibrating current LO setting\n"); - cal = b43_calibrate_lo_setting(dev, &gphy->bbatt, &gphy->rfatt); - if (cal) { - list_add(&cal->list, &lo->calib_list); - b43_lo_write(dev, &cal->ctl); - } else - b43warn(dev->wl, "Failed to recalibrate current LO setting\n"); - } -} - -void b43_lo_g_cleanup(struct b43_wldev *dev) -{ - struct b43_txpower_lo_control *lo = dev->phy.g->lo_control; - struct b43_lo_calib *cal, *tmp; - - if (!lo) - return; - list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) { - list_del(&cal->list); - kfree(cal); - } -} - -/* LO Initialization */ -void b43_lo_g_init(struct b43_wldev *dev) -{ - if (b43_has_hardware_pctl(dev)) { - lo_read_power_vector(dev); - b43_gphy_dc_lt_init(dev, 1); - } -} diff --git a/drivers/net/wireless/b43/lo.h b/drivers/net/wireless/b43/lo.h deleted file mode 100644 index 7b4df3883bc2..000000000000 --- a/drivers/net/wireless/b43/lo.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef B43_LO_H_ -#define B43_LO_H_ - -/* G-PHY Local Oscillator */ - -#include "phy_g.h" - -struct b43_wldev; - -/* Local Oscillator control value-pair. */ -struct b43_loctl { - /* Control values. */ - s8 i; - s8 q; -}; -/* Debugging: Poison value for i and q values. */ -#define B43_LOCTL_POISON 111 - -/* This struct holds calibrated LO settings for a set of - * Baseband and RF attenuation settings. */ -struct b43_lo_calib { - /* The set of attenuation values this set of LO - * control values is calibrated for. */ - struct b43_bbatt bbatt; - struct b43_rfatt rfatt; - /* The set of control values for the LO. */ - struct b43_loctl ctl; - /* The time when these settings were calibrated (in jiffies) */ - unsigned long calib_time; - /* List. */ - struct list_head list; -}; - -/* Size of the DC Lookup Table in 16bit words. */ -#define B43_DC_LT_SIZE 32 - -/* Local Oscillator calibration information */ -struct b43_txpower_lo_control { - /* Lists of RF and BB attenuation values for this device. - * Used for building hardware power control tables. */ - struct b43_rfatt_list rfatt_list; - struct b43_bbatt_list bbatt_list; - - /* The DC Lookup Table is cached in memory here. - * Note that this is only used for Hardware Power Control. */ - u16 dc_lt[B43_DC_LT_SIZE]; - - /* List of calibrated control values (struct b43_lo_calib). */ - struct list_head calib_list; - /* Last time the power vector was read (jiffies). */ - unsigned long pwr_vec_read_time; - /* Last time the txctl values were measured (jiffies). */ - unsigned long txctl_measured_time; - - /* Current TX Bias value */ - u8 tx_bias; - /* Current TX Magnification Value (if used by the device) */ - u8 tx_magn; - - /* Saved device PowerVector */ - u64 power_vector; -}; - -/* Calibration expire timeouts. - * Timeouts must be multiple of 15 seconds. To make sure - * the item really expired when the 15 second timer hits, we - * subtract two additional seconds from the timeout. */ -#define B43_LO_CALIB_EXPIRE (HZ * (30 - 2)) -#define B43_LO_PWRVEC_EXPIRE (HZ * (30 - 2)) -#define B43_LO_TXCTL_EXPIRE (HZ * (180 - 4)) - - -/* Adjust the Local Oscillator to the saved attenuation - * and txctl values. - */ -void b43_lo_g_adjust(struct b43_wldev *dev); -/* Adjust to specific values. */ -void b43_lo_g_adjust_to(struct b43_wldev *dev, - u16 rfatt, u16 bbatt, u16 tx_control); - -void b43_gphy_dc_lt_init(struct b43_wldev *dev, bool update_all); - -void b43_lo_g_maintenance_work(struct b43_wldev *dev); -void b43_lo_g_cleanup(struct b43_wldev *dev); -void b43_lo_g_init(struct b43_wldev *dev); - -#endif /* B43_LO_H_ */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c deleted file mode 100644 index ec013fbd6a81..000000000000 --- a/drivers/net/wireless/b43/main.c +++ /dev/null @@ -1,5895 +0,0 @@ -/* - - Broadcom B43 wireless driver - - Copyright (c) 2005 Martin Langer - Copyright (c) 2005 Stefano Brivio - Copyright (c) 2005-2009 Michael Buesch - Copyright (c) 2005 Danny van Dyk - Copyright (c) 2005 Andreas Jaggi - Copyright (c) 2010-2011 RafaÅ‚ MiÅ‚ecki - - SDIO support - Copyright (c) 2009 Albert Herranz - - Some parts of the code in this file are derived from the ipw2200 - driver Copyright(c) 2003 - 2004 Intel Corporation. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "b43.h" -#include "main.h" -#include "debugfs.h" -#include "phy_common.h" -#include "phy_g.h" -#include "phy_n.h" -#include "dma.h" -#include "pio.h" -#include "sysfs.h" -#include "xmit.h" -#include "lo.h" -#include "sdio.h" -#include - -MODULE_DESCRIPTION("Broadcom B43 wireless driver"); -MODULE_AUTHOR("Martin Langer"); -MODULE_AUTHOR("Stefano Brivio"); -MODULE_AUTHOR("Michael Buesch"); -MODULE_AUTHOR("Gábor Stefanik"); -MODULE_AUTHOR("RafaÅ‚ MiÅ‚ecki"); -MODULE_LICENSE("GPL"); - -MODULE_FIRMWARE("b43/ucode11.fw"); -MODULE_FIRMWARE("b43/ucode13.fw"); -MODULE_FIRMWARE("b43/ucode14.fw"); -MODULE_FIRMWARE("b43/ucode15.fw"); -MODULE_FIRMWARE("b43/ucode16_mimo.fw"); -MODULE_FIRMWARE("b43/ucode5.fw"); -MODULE_FIRMWARE("b43/ucode9.fw"); - -static int modparam_bad_frames_preempt; -module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); -MODULE_PARM_DESC(bad_frames_preempt, - "enable(1) / disable(0) Bad Frames Preemption"); - -static char modparam_fwpostfix[16]; -module_param_string(fwpostfix, modparam_fwpostfix, 16, 0444); -MODULE_PARM_DESC(fwpostfix, "Postfix for the .fw files to load."); - -static int modparam_hwpctl; -module_param_named(hwpctl, modparam_hwpctl, int, 0444); -MODULE_PARM_DESC(hwpctl, "Enable hardware-side power control (default off)"); - -static int modparam_nohwcrypt; -module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); -MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); - -static int modparam_hwtkip; -module_param_named(hwtkip, modparam_hwtkip, int, 0444); -MODULE_PARM_DESC(hwtkip, "Enable hardware tkip."); - -static int modparam_qos = 1; -module_param_named(qos, modparam_qos, int, 0444); -MODULE_PARM_DESC(qos, "Enable QOS support (default on)"); - -static int modparam_btcoex = 1; -module_param_named(btcoex, modparam_btcoex, int, 0444); -MODULE_PARM_DESC(btcoex, "Enable Bluetooth coexistence (default on)"); - -int b43_modparam_verbose = B43_VERBOSITY_DEFAULT; -module_param_named(verbose, b43_modparam_verbose, int, 0644); -MODULE_PARM_DESC(verbose, "Log message verbosity: 0=error, 1=warn, 2=info(default), 3=debug"); - -static int b43_modparam_pio = 0; -module_param_named(pio, b43_modparam_pio, int, 0644); -MODULE_PARM_DESC(pio, "Use PIO accesses by default: 0=DMA, 1=PIO"); - -static int modparam_allhwsupport = !IS_ENABLED(CONFIG_BRCMSMAC); -module_param_named(allhwsupport, modparam_allhwsupport, int, 0444); -MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if overlaps with the brcmsmac driver)"); - -#ifdef CONFIG_B43_BCMA -static const struct bcma_device_id b43_bcma_tbl[] = { - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS), - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x15, BCMA_ANY_CLASS), - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS), - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS), - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1C, BCMA_ANY_CLASS), - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS), - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1E, BCMA_ANY_CLASS), - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x28, BCMA_ANY_CLASS), - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x2A, BCMA_ANY_CLASS), - {}, -}; -MODULE_DEVICE_TABLE(bcma, b43_bcma_tbl); -#endif - -#ifdef CONFIG_B43_SSB -static const struct ssb_device_id b43_ssb_tbl[] = { - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5), - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 6), - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 7), - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 9), - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 10), - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 11), - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 12), - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 13), - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 15), - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 16), - {}, -}; -MODULE_DEVICE_TABLE(ssb, b43_ssb_tbl); -#endif - -/* Channel and ratetables are shared for all devices. - * They can't be const, because ieee80211 puts some precalculated - * data in there. This data is the same for all devices, so we don't - * get concurrency issues */ -#define RATETAB_ENT(_rateid, _flags) \ - { \ - .bitrate = B43_RATE_TO_BASE100KBPS(_rateid), \ - .hw_value = (_rateid), \ - .flags = (_flags), \ - } - -/* - * NOTE: When changing this, sync with xmit.c's - * b43_plcp_get_bitrate_idx_* functions! - */ -static struct ieee80211_rate __b43_ratetable[] = { - RATETAB_ENT(B43_CCK_RATE_1MB, 0), - RATETAB_ENT(B43_CCK_RATE_2MB, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(B43_CCK_RATE_5MB, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(B43_CCK_RATE_11MB, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(B43_OFDM_RATE_6MB, 0), - RATETAB_ENT(B43_OFDM_RATE_9MB, 0), - RATETAB_ENT(B43_OFDM_RATE_12MB, 0), - RATETAB_ENT(B43_OFDM_RATE_18MB, 0), - RATETAB_ENT(B43_OFDM_RATE_24MB, 0), - RATETAB_ENT(B43_OFDM_RATE_36MB, 0), - RATETAB_ENT(B43_OFDM_RATE_48MB, 0), - RATETAB_ENT(B43_OFDM_RATE_54MB, 0), -}; - -#define b43_a_ratetable (__b43_ratetable + 4) -#define b43_a_ratetable_size 8 -#define b43_b_ratetable (__b43_ratetable + 0) -#define b43_b_ratetable_size 4 -#define b43_g_ratetable (__b43_ratetable + 0) -#define b43_g_ratetable_size 12 - -#define CHAN2G(_channel, _freq, _flags) { \ - .band = IEEE80211_BAND_2GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_channel), \ - .flags = (_flags), \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} -static struct ieee80211_channel b43_2ghz_chantable[] = { - CHAN2G(1, 2412, 0), - CHAN2G(2, 2417, 0), - CHAN2G(3, 2422, 0), - CHAN2G(4, 2427, 0), - CHAN2G(5, 2432, 0), - CHAN2G(6, 2437, 0), - CHAN2G(7, 2442, 0), - CHAN2G(8, 2447, 0), - CHAN2G(9, 2452, 0), - CHAN2G(10, 2457, 0), - CHAN2G(11, 2462, 0), - CHAN2G(12, 2467, 0), - CHAN2G(13, 2472, 0), - CHAN2G(14, 2484, 0), -}; - -/* No support for the last 3 channels (12, 13, 14) */ -#define b43_2ghz_chantable_limited_size 11 -#undef CHAN2G - -#define CHAN4G(_channel, _flags) { \ - .band = IEEE80211_BAND_5GHZ, \ - .center_freq = 4000 + (5 * (_channel)), \ - .hw_value = (_channel), \ - .flags = (_flags), \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} -#define CHAN5G(_channel, _flags) { \ - .band = IEEE80211_BAND_5GHZ, \ - .center_freq = 5000 + (5 * (_channel)), \ - .hw_value = (_channel), \ - .flags = (_flags), \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} -static struct ieee80211_channel b43_5ghz_nphy_chantable[] = { - CHAN4G(184, 0), CHAN4G(186, 0), - CHAN4G(188, 0), CHAN4G(190, 0), - CHAN4G(192, 0), CHAN4G(194, 0), - CHAN4G(196, 0), CHAN4G(198, 0), - CHAN4G(200, 0), CHAN4G(202, 0), - CHAN4G(204, 0), CHAN4G(206, 0), - CHAN4G(208, 0), CHAN4G(210, 0), - CHAN4G(212, 0), CHAN4G(214, 0), - CHAN4G(216, 0), CHAN4G(218, 0), - CHAN4G(220, 0), CHAN4G(222, 0), - CHAN4G(224, 0), CHAN4G(226, 0), - CHAN4G(228, 0), - CHAN5G(32, 0), CHAN5G(34, 0), - CHAN5G(36, 0), CHAN5G(38, 0), - CHAN5G(40, 0), CHAN5G(42, 0), - CHAN5G(44, 0), CHAN5G(46, 0), - CHAN5G(48, 0), CHAN5G(50, 0), - CHAN5G(52, 0), CHAN5G(54, 0), - CHAN5G(56, 0), CHAN5G(58, 0), - CHAN5G(60, 0), CHAN5G(62, 0), - CHAN5G(64, 0), CHAN5G(66, 0), - CHAN5G(68, 0), CHAN5G(70, 0), - CHAN5G(72, 0), CHAN5G(74, 0), - CHAN5G(76, 0), CHAN5G(78, 0), - CHAN5G(80, 0), CHAN5G(82, 0), - CHAN5G(84, 0), CHAN5G(86, 0), - CHAN5G(88, 0), CHAN5G(90, 0), - CHAN5G(92, 0), CHAN5G(94, 0), - CHAN5G(96, 0), CHAN5G(98, 0), - CHAN5G(100, 0), CHAN5G(102, 0), - CHAN5G(104, 0), CHAN5G(106, 0), - CHAN5G(108, 0), CHAN5G(110, 0), - CHAN5G(112, 0), CHAN5G(114, 0), - CHAN5G(116, 0), CHAN5G(118, 0), - CHAN5G(120, 0), CHAN5G(122, 0), - CHAN5G(124, 0), CHAN5G(126, 0), - CHAN5G(128, 0), CHAN5G(130, 0), - CHAN5G(132, 0), CHAN5G(134, 0), - CHAN5G(136, 0), CHAN5G(138, 0), - CHAN5G(140, 0), CHAN5G(142, 0), - CHAN5G(144, 0), CHAN5G(145, 0), - CHAN5G(146, 0), CHAN5G(147, 0), - CHAN5G(148, 0), CHAN5G(149, 0), - CHAN5G(150, 0), CHAN5G(151, 0), - CHAN5G(152, 0), CHAN5G(153, 0), - CHAN5G(154, 0), CHAN5G(155, 0), - CHAN5G(156, 0), CHAN5G(157, 0), - CHAN5G(158, 0), CHAN5G(159, 0), - CHAN5G(160, 0), CHAN5G(161, 0), - CHAN5G(162, 0), CHAN5G(163, 0), - CHAN5G(164, 0), CHAN5G(165, 0), - CHAN5G(166, 0), CHAN5G(168, 0), - CHAN5G(170, 0), CHAN5G(172, 0), - CHAN5G(174, 0), CHAN5G(176, 0), - CHAN5G(178, 0), CHAN5G(180, 0), - CHAN5G(182, 0), -}; - -static struct ieee80211_channel b43_5ghz_nphy_chantable_limited[] = { - CHAN5G(36, 0), CHAN5G(40, 0), - CHAN5G(44, 0), CHAN5G(48, 0), - CHAN5G(149, 0), CHAN5G(153, 0), - CHAN5G(157, 0), CHAN5G(161, 0), - CHAN5G(165, 0), -}; - -static struct ieee80211_channel b43_5ghz_aphy_chantable[] = { - CHAN5G(34, 0), CHAN5G(36, 0), - CHAN5G(38, 0), CHAN5G(40, 0), - CHAN5G(42, 0), CHAN5G(44, 0), - CHAN5G(46, 0), CHAN5G(48, 0), - CHAN5G(52, 0), CHAN5G(56, 0), - CHAN5G(60, 0), CHAN5G(64, 0), - CHAN5G(100, 0), CHAN5G(104, 0), - CHAN5G(108, 0), CHAN5G(112, 0), - CHAN5G(116, 0), CHAN5G(120, 0), - CHAN5G(124, 0), CHAN5G(128, 0), - CHAN5G(132, 0), CHAN5G(136, 0), - CHAN5G(140, 0), CHAN5G(149, 0), - CHAN5G(153, 0), CHAN5G(157, 0), - CHAN5G(161, 0), CHAN5G(165, 0), - CHAN5G(184, 0), CHAN5G(188, 0), - CHAN5G(192, 0), CHAN5G(196, 0), - CHAN5G(200, 0), CHAN5G(204, 0), - CHAN5G(208, 0), CHAN5G(212, 0), - CHAN5G(216, 0), -}; -#undef CHAN4G -#undef CHAN5G - -static struct ieee80211_supported_band b43_band_5GHz_nphy = { - .band = IEEE80211_BAND_5GHZ, - .channels = b43_5ghz_nphy_chantable, - .n_channels = ARRAY_SIZE(b43_5ghz_nphy_chantable), - .bitrates = b43_a_ratetable, - .n_bitrates = b43_a_ratetable_size, -}; - -static struct ieee80211_supported_band b43_band_5GHz_nphy_limited = { - .band = IEEE80211_BAND_5GHZ, - .channels = b43_5ghz_nphy_chantable_limited, - .n_channels = ARRAY_SIZE(b43_5ghz_nphy_chantable_limited), - .bitrates = b43_a_ratetable, - .n_bitrates = b43_a_ratetable_size, -}; - -static struct ieee80211_supported_band b43_band_5GHz_aphy = { - .band = IEEE80211_BAND_5GHZ, - .channels = b43_5ghz_aphy_chantable, - .n_channels = ARRAY_SIZE(b43_5ghz_aphy_chantable), - .bitrates = b43_a_ratetable, - .n_bitrates = b43_a_ratetable_size, -}; - -static struct ieee80211_supported_band b43_band_2GHz = { - .band = IEEE80211_BAND_2GHZ, - .channels = b43_2ghz_chantable, - .n_channels = ARRAY_SIZE(b43_2ghz_chantable), - .bitrates = b43_g_ratetable, - .n_bitrates = b43_g_ratetable_size, -}; - -static struct ieee80211_supported_band b43_band_2ghz_limited = { - .band = IEEE80211_BAND_2GHZ, - .channels = b43_2ghz_chantable, - .n_channels = b43_2ghz_chantable_limited_size, - .bitrates = b43_g_ratetable, - .n_bitrates = b43_g_ratetable_size, -}; - -static void b43_wireless_core_exit(struct b43_wldev *dev); -static int b43_wireless_core_init(struct b43_wldev *dev); -static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev); -static int b43_wireless_core_start(struct b43_wldev *dev); -static void b43_op_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf, - u32 changed); - -static int b43_ratelimit(struct b43_wl *wl) -{ - if (!wl || !wl->current_dev) - return 1; - if (b43_status(wl->current_dev) < B43_STAT_STARTED) - return 1; - /* We are up and running. - * Ratelimit the messages to avoid DoS over the net. */ - return net_ratelimit(); -} - -void b43info(struct b43_wl *wl, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - if (b43_modparam_verbose < B43_VERBOSITY_INFO) - return; - if (!b43_ratelimit(wl)) - return; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - printk(KERN_INFO "b43-%s: %pV", - (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); - - va_end(args); -} - -void b43err(struct b43_wl *wl, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - if (b43_modparam_verbose < B43_VERBOSITY_ERROR) - return; - if (!b43_ratelimit(wl)) - return; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - printk(KERN_ERR "b43-%s ERROR: %pV", - (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); - - va_end(args); -} - -void b43warn(struct b43_wl *wl, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - if (b43_modparam_verbose < B43_VERBOSITY_WARN) - return; - if (!b43_ratelimit(wl)) - return; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - printk(KERN_WARNING "b43-%s warning: %pV", - (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); - - va_end(args); -} - -void b43dbg(struct b43_wl *wl, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - if (b43_modparam_verbose < B43_VERBOSITY_DEBUG) - return; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - printk(KERN_DEBUG "b43-%s debug: %pV", - (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); - - va_end(args); -} - -static void b43_ram_write(struct b43_wldev *dev, u16 offset, u32 val) -{ - u32 macctl; - - B43_WARN_ON(offset % 4 != 0); - - macctl = b43_read32(dev, B43_MMIO_MACCTL); - if (macctl & B43_MACCTL_BE) - val = swab32(val); - - b43_write32(dev, B43_MMIO_RAM_CONTROL, offset); - mmiowb(); - b43_write32(dev, B43_MMIO_RAM_DATA, val); -} - -static inline void b43_shm_control_word(struct b43_wldev *dev, - u16 routing, u16 offset) -{ - u32 control; - - /* "offset" is the WORD offset. */ - control = routing; - control <<= 16; - control |= offset; - b43_write32(dev, B43_MMIO_SHM_CONTROL, control); -} - -u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset) -{ - u32 ret; - - if (routing == B43_SHM_SHARED) { - B43_WARN_ON(offset & 0x0001); - if (offset & 0x0003) { - /* Unaligned access */ - b43_shm_control_word(dev, routing, offset >> 2); - ret = b43_read16(dev, B43_MMIO_SHM_DATA_UNALIGNED); - b43_shm_control_word(dev, routing, (offset >> 2) + 1); - ret |= ((u32)b43_read16(dev, B43_MMIO_SHM_DATA)) << 16; - - goto out; - } - offset >>= 2; - } - b43_shm_control_word(dev, routing, offset); - ret = b43_read32(dev, B43_MMIO_SHM_DATA); -out: - return ret; -} - -u16 b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset) -{ - u16 ret; - - if (routing == B43_SHM_SHARED) { - B43_WARN_ON(offset & 0x0001); - if (offset & 0x0003) { - /* Unaligned access */ - b43_shm_control_word(dev, routing, offset >> 2); - ret = b43_read16(dev, B43_MMIO_SHM_DATA_UNALIGNED); - - goto out; - } - offset >>= 2; - } - b43_shm_control_word(dev, routing, offset); - ret = b43_read16(dev, B43_MMIO_SHM_DATA); -out: - return ret; -} - -void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value) -{ - if (routing == B43_SHM_SHARED) { - B43_WARN_ON(offset & 0x0001); - if (offset & 0x0003) { - /* Unaligned access */ - b43_shm_control_word(dev, routing, offset >> 2); - b43_write16(dev, B43_MMIO_SHM_DATA_UNALIGNED, - value & 0xFFFF); - b43_shm_control_word(dev, routing, (offset >> 2) + 1); - b43_write16(dev, B43_MMIO_SHM_DATA, - (value >> 16) & 0xFFFF); - return; - } - offset >>= 2; - } - b43_shm_control_word(dev, routing, offset); - b43_write32(dev, B43_MMIO_SHM_DATA, value); -} - -void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value) -{ - if (routing == B43_SHM_SHARED) { - B43_WARN_ON(offset & 0x0001); - if (offset & 0x0003) { - /* Unaligned access */ - b43_shm_control_word(dev, routing, offset >> 2); - b43_write16(dev, B43_MMIO_SHM_DATA_UNALIGNED, value); - return; - } - offset >>= 2; - } - b43_shm_control_word(dev, routing, offset); - b43_write16(dev, B43_MMIO_SHM_DATA, value); -} - -/* Read HostFlags */ -u64 b43_hf_read(struct b43_wldev *dev) -{ - u64 ret; - - ret = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF3); - ret <<= 16; - ret |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF2); - ret <<= 16; - ret |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF1); - - return ret; -} - -/* Write HostFlags */ -void b43_hf_write(struct b43_wldev *dev, u64 value) -{ - u16 lo, mi, hi; - - lo = (value & 0x00000000FFFFULL); - mi = (value & 0x0000FFFF0000ULL) >> 16; - hi = (value & 0xFFFF00000000ULL) >> 32; - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF1, lo); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF2, mi); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF3, hi); -} - -/* Read the firmware capabilities bitmask (Opensource firmware only) */ -static u16 b43_fwcapa_read(struct b43_wldev *dev) -{ - B43_WARN_ON(!dev->fw.opensource); - return b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_FWCAPA); -} - -void b43_tsf_read(struct b43_wldev *dev, u64 *tsf) -{ - u32 low, high; - - B43_WARN_ON(dev->dev->core_rev < 3); - - /* The hardware guarantees us an atomic read, if we - * read the low register first. */ - low = b43_read32(dev, B43_MMIO_REV3PLUS_TSF_LOW); - high = b43_read32(dev, B43_MMIO_REV3PLUS_TSF_HIGH); - - *tsf = high; - *tsf <<= 32; - *tsf |= low; -} - -static void b43_time_lock(struct b43_wldev *dev) -{ - b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_TBTTHOLD); - /* Commit the write */ - b43_read32(dev, B43_MMIO_MACCTL); -} - -static void b43_time_unlock(struct b43_wldev *dev) -{ - b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_TBTTHOLD, 0); - /* Commit the write */ - b43_read32(dev, B43_MMIO_MACCTL); -} - -static void b43_tsf_write_locked(struct b43_wldev *dev, u64 tsf) -{ - u32 low, high; - - B43_WARN_ON(dev->dev->core_rev < 3); - - low = tsf; - high = (tsf >> 32); - /* The hardware guarantees us an atomic write, if we - * write the low register first. */ - b43_write32(dev, B43_MMIO_REV3PLUS_TSF_LOW, low); - mmiowb(); - b43_write32(dev, B43_MMIO_REV3PLUS_TSF_HIGH, high); - mmiowb(); -} - -void b43_tsf_write(struct b43_wldev *dev, u64 tsf) -{ - b43_time_lock(dev); - b43_tsf_write_locked(dev, tsf); - b43_time_unlock(dev); -} - -static -void b43_macfilter_set(struct b43_wldev *dev, u16 offset, const u8 *mac) -{ - static const u8 zero_addr[ETH_ALEN] = { 0 }; - u16 data; - - if (!mac) - mac = zero_addr; - - offset |= 0x0020; - b43_write16(dev, B43_MMIO_MACFILTER_CONTROL, offset); - - data = mac[0]; - data |= mac[1] << 8; - b43_write16(dev, B43_MMIO_MACFILTER_DATA, data); - data = mac[2]; - data |= mac[3] << 8; - b43_write16(dev, B43_MMIO_MACFILTER_DATA, data); - data = mac[4]; - data |= mac[5] << 8; - b43_write16(dev, B43_MMIO_MACFILTER_DATA, data); -} - -static void b43_write_mac_bssid_templates(struct b43_wldev *dev) -{ - const u8 *mac; - const u8 *bssid; - u8 mac_bssid[ETH_ALEN * 2]; - int i; - u32 tmp; - - bssid = dev->wl->bssid; - mac = dev->wl->mac_addr; - - b43_macfilter_set(dev, B43_MACFILTER_BSSID, bssid); - - memcpy(mac_bssid, mac, ETH_ALEN); - memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN); - - /* Write our MAC address and BSSID to template ram */ - for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) { - tmp = (u32) (mac_bssid[i + 0]); - tmp |= (u32) (mac_bssid[i + 1]) << 8; - tmp |= (u32) (mac_bssid[i + 2]) << 16; - tmp |= (u32) (mac_bssid[i + 3]) << 24; - b43_ram_write(dev, 0x20 + i, tmp); - } -} - -static void b43_upload_card_macaddress(struct b43_wldev *dev) -{ - b43_write_mac_bssid_templates(dev); - b43_macfilter_set(dev, B43_MACFILTER_SELF, dev->wl->mac_addr); -} - -static void b43_set_slot_time(struct b43_wldev *dev, u16 slot_time) -{ - /* slot_time is in usec. */ - /* This test used to exit for all but a G PHY. */ - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - return; - b43_write16(dev, B43_MMIO_IFSSLOT, 510 + slot_time); - /* Shared memory location 0x0010 is the slot time and should be - * set to slot_time; however, this register is initially 0 and changing - * the value adversely affects the transmit rate for BCM4311 - * devices. Until this behavior is unterstood, delete this step - * - * b43_shm_write16(dev, B43_SHM_SHARED, 0x0010, slot_time); - */ -} - -static void b43_short_slot_timing_enable(struct b43_wldev *dev) -{ - b43_set_slot_time(dev, 9); -} - -static void b43_short_slot_timing_disable(struct b43_wldev *dev) -{ - b43_set_slot_time(dev, 20); -} - -/* DummyTransmission function, as documented on - * http://bcm-v4.sipsolutions.net/802.11/DummyTransmission - */ -void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on) -{ - struct b43_phy *phy = &dev->phy; - unsigned int i, max_loop; - u16 value; - u32 buffer[5] = { - 0x00000000, - 0x00D40000, - 0x00000000, - 0x01000000, - 0x00000000, - }; - - if (ofdm) { - max_loop = 0x1E; - buffer[0] = 0x000201CC; - } else { - max_loop = 0xFA; - buffer[0] = 0x000B846E; - } - - for (i = 0; i < 5; i++) - b43_ram_write(dev, i * 4, buffer[i]); - - b43_write16(dev, B43_MMIO_XMTSEL, 0x0000); - - if (dev->dev->core_rev < 11) - b43_write16(dev, B43_MMIO_WEPCTL, 0x0000); - else - b43_write16(dev, B43_MMIO_WEPCTL, 0x0100); - - value = (ofdm ? 0x41 : 0x40); - b43_write16(dev, B43_MMIO_TXE0_PHYCTL, value); - if (phy->type == B43_PHYTYPE_N || phy->type == B43_PHYTYPE_LP || - phy->type == B43_PHYTYPE_LCN) - b43_write16(dev, B43_MMIO_TXE0_PHYCTL1, 0x1A02); - - b43_write16(dev, B43_MMIO_TXE0_WM_0, 0x0000); - b43_write16(dev, B43_MMIO_TXE0_WM_1, 0x0000); - - b43_write16(dev, B43_MMIO_XMTTPLATETXPTR, 0x0000); - b43_write16(dev, B43_MMIO_XMTTXCNT, 0x0014); - b43_write16(dev, B43_MMIO_XMTSEL, 0x0826); - b43_write16(dev, B43_MMIO_TXE0_CTL, 0x0000); - - if (!pa_on && phy->type == B43_PHYTYPE_N) - ; /*b43_nphy_pa_override(dev, false) */ - - switch (phy->type) { - case B43_PHYTYPE_N: - case B43_PHYTYPE_LCN: - b43_write16(dev, B43_MMIO_TXE0_AUX, 0x00D0); - break; - case B43_PHYTYPE_LP: - b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0050); - break; - default: - b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0030); - } - b43_read16(dev, B43_MMIO_TXE0_AUX); - - if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) - b43_radio_write16(dev, 0x0051, 0x0017); - for (i = 0x00; i < max_loop; i++) { - value = b43_read16(dev, B43_MMIO_TXE0_STATUS); - if (value & 0x0080) - break; - udelay(10); - } - for (i = 0x00; i < 0x0A; i++) { - value = b43_read16(dev, B43_MMIO_TXE0_STATUS); - if (value & 0x0400) - break; - udelay(10); - } - for (i = 0x00; i < 0x19; i++) { - value = b43_read16(dev, B43_MMIO_IFSSTAT); - if (!(value & 0x0100)) - break; - udelay(10); - } - if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) - b43_radio_write16(dev, 0x0051, 0x0037); -} - -static void key_write(struct b43_wldev *dev, - u8 index, u8 algorithm, const u8 *key) -{ - unsigned int i; - u32 offset; - u16 value; - u16 kidx; - - /* Key index/algo block */ - kidx = b43_kidx_to_fw(dev, index); - value = ((kidx << 4) | algorithm); - b43_shm_write16(dev, B43_SHM_SHARED, - B43_SHM_SH_KEYIDXBLOCK + (kidx * 2), value); - - /* Write the key to the Key Table Pointer offset */ - offset = dev->ktp + (index * B43_SEC_KEYSIZE); - for (i = 0; i < B43_SEC_KEYSIZE; i += 2) { - value = key[i]; - value |= (u16) (key[i + 1]) << 8; - b43_shm_write16(dev, B43_SHM_SHARED, offset + i, value); - } -} - -static void keymac_write(struct b43_wldev *dev, u8 index, const u8 *addr) -{ - u32 addrtmp[2] = { 0, 0, }; - u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; - - if (b43_new_kidx_api(dev)) - pairwise_keys_start = B43_NR_GROUP_KEYS; - - B43_WARN_ON(index < pairwise_keys_start); - /* We have four default TX keys and possibly four default RX keys. - * Physical mac 0 is mapped to physical key 4 or 8, depending - * on the firmware version. - * So we must adjust the index here. - */ - index -= pairwise_keys_start; - B43_WARN_ON(index >= B43_NR_PAIRWISE_KEYS); - - if (addr) { - addrtmp[0] = addr[0]; - addrtmp[0] |= ((u32) (addr[1]) << 8); - addrtmp[0] |= ((u32) (addr[2]) << 16); - addrtmp[0] |= ((u32) (addr[3]) << 24); - addrtmp[1] = addr[4]; - addrtmp[1] |= ((u32) (addr[5]) << 8); - } - - /* Receive match transmitter address (RCMTA) mechanism */ - b43_shm_write32(dev, B43_SHM_RCMTA, - (index * 2) + 0, addrtmp[0]); - b43_shm_write16(dev, B43_SHM_RCMTA, - (index * 2) + 1, addrtmp[1]); -} - -/* The ucode will use phase1 key with TEK key to decrypt rx packets. - * When a packet is received, the iv32 is checked. - * - if it doesn't the packet is returned without modification (and software - * decryption can be done). That's what happen when iv16 wrap. - * - if it does, the rc4 key is computed, and decryption is tried. - * Either it will success and B43_RX_MAC_DEC is returned, - * either it fails and B43_RX_MAC_DEC|B43_RX_MAC_DECERR is returned - * and the packet is not usable (it got modified by the ucode). - * So in order to never have B43_RX_MAC_DECERR, we should provide - * a iv32 and phase1key that match. Because we drop packets in case of - * B43_RX_MAC_DECERR, if we have a correct iv32 but a wrong phase1key, all - * packets will be lost without higher layer knowing (ie no resync possible - * until next wrap). - * - * NOTE : this should support 50 key like RCMTA because - * (B43_SHM_SH_KEYIDXBLOCK - B43_SHM_SH_TKIPTSCTTAK)/14 = 50 - */ -static void rx_tkip_phase1_write(struct b43_wldev *dev, u8 index, u32 iv32, - u16 *phase1key) -{ - unsigned int i; - u32 offset; - u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; - - if (!modparam_hwtkip) - return; - - if (b43_new_kidx_api(dev)) - pairwise_keys_start = B43_NR_GROUP_KEYS; - - B43_WARN_ON(index < pairwise_keys_start); - /* We have four default TX keys and possibly four default RX keys. - * Physical mac 0 is mapped to physical key 4 or 8, depending - * on the firmware version. - * So we must adjust the index here. - */ - index -= pairwise_keys_start; - B43_WARN_ON(index >= B43_NR_PAIRWISE_KEYS); - - if (b43_debug(dev, B43_DBG_KEYS)) { - b43dbg(dev->wl, "rx_tkip_phase1_write : idx 0x%x, iv32 0x%x\n", - index, iv32); - } - /* Write the key to the RX tkip shared mem */ - offset = B43_SHM_SH_TKIPTSCTTAK + index * (10 + 4); - for (i = 0; i < 10; i += 2) { - b43_shm_write16(dev, B43_SHM_SHARED, offset + i, - phase1key ? phase1key[i / 2] : 0); - } - b43_shm_write16(dev, B43_SHM_SHARED, offset + i, iv32); - b43_shm_write16(dev, B43_SHM_SHARED, offset + i + 2, iv32 >> 16); -} - -static void b43_op_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, - u32 iv32, u16 *phase1key) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - int index = keyconf->hw_key_idx; - - if (B43_WARN_ON(!modparam_hwtkip)) - return; - - /* This is only called from the RX path through mac80211, where - * our mutex is already locked. */ - B43_WARN_ON(!mutex_is_locked(&wl->mutex)); - dev = wl->current_dev; - B43_WARN_ON(!dev || b43_status(dev) < B43_STAT_INITIALIZED); - - keymac_write(dev, index, NULL); /* First zero out mac to avoid race */ - - rx_tkip_phase1_write(dev, index, iv32, phase1key); - /* only pairwise TKIP keys are supported right now */ - if (WARN_ON(!sta)) - return; - keymac_write(dev, index, sta->addr); -} - -static void do_key_write(struct b43_wldev *dev, - u8 index, u8 algorithm, - const u8 *key, size_t key_len, const u8 *mac_addr) -{ - u8 buf[B43_SEC_KEYSIZE] = { 0, }; - u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; - - if (b43_new_kidx_api(dev)) - pairwise_keys_start = B43_NR_GROUP_KEYS; - - B43_WARN_ON(index >= ARRAY_SIZE(dev->key)); - B43_WARN_ON(key_len > B43_SEC_KEYSIZE); - - if (index >= pairwise_keys_start) - keymac_write(dev, index, NULL); /* First zero out mac. */ - if (algorithm == B43_SEC_ALGO_TKIP) { - /* - * We should provide an initial iv32, phase1key pair. - * We could start with iv32=0 and compute the corresponding - * phase1key, but this means calling ieee80211_get_tkip_key - * with a fake skb (or export other tkip function). - * Because we are lazy we hope iv32 won't start with - * 0xffffffff and let's b43_op_update_tkip_key provide a - * correct pair. - */ - rx_tkip_phase1_write(dev, index, 0xffffffff, (u16*)buf); - } else if (index >= pairwise_keys_start) /* clear it */ - rx_tkip_phase1_write(dev, index, 0, NULL); - if (key) - memcpy(buf, key, key_len); - key_write(dev, index, algorithm, buf); - if (index >= pairwise_keys_start) - keymac_write(dev, index, mac_addr); - - dev->key[index].algorithm = algorithm; -} - -static int b43_key_write(struct b43_wldev *dev, - int index, u8 algorithm, - const u8 *key, size_t key_len, - const u8 *mac_addr, - struct ieee80211_key_conf *keyconf) -{ - int i; - int pairwise_keys_start; - - /* For ALG_TKIP the key is encoded as a 256-bit (32 byte) data block: - * - Temporal Encryption Key (128 bits) - * - Temporal Authenticator Tx MIC Key (64 bits) - * - Temporal Authenticator Rx MIC Key (64 bits) - * - * Hardware only store TEK - */ - if (algorithm == B43_SEC_ALGO_TKIP && key_len == 32) - key_len = 16; - if (key_len > B43_SEC_KEYSIZE) - return -EINVAL; - for (i = 0; i < ARRAY_SIZE(dev->key); i++) { - /* Check that we don't already have this key. */ - B43_WARN_ON(dev->key[i].keyconf == keyconf); - } - if (index < 0) { - /* Pairwise key. Get an empty slot for the key. */ - if (b43_new_kidx_api(dev)) - pairwise_keys_start = B43_NR_GROUP_KEYS; - else - pairwise_keys_start = B43_NR_GROUP_KEYS * 2; - for (i = pairwise_keys_start; - i < pairwise_keys_start + B43_NR_PAIRWISE_KEYS; - i++) { - B43_WARN_ON(i >= ARRAY_SIZE(dev->key)); - if (!dev->key[i].keyconf) { - /* found empty */ - index = i; - break; - } - } - if (index < 0) { - b43warn(dev->wl, "Out of hardware key memory\n"); - return -ENOSPC; - } - } else - B43_WARN_ON(index > 3); - - do_key_write(dev, index, algorithm, key, key_len, mac_addr); - if ((index <= 3) && !b43_new_kidx_api(dev)) { - /* Default RX key */ - B43_WARN_ON(mac_addr); - do_key_write(dev, index + 4, algorithm, key, key_len, NULL); - } - keyconf->hw_key_idx = index; - dev->key[index].keyconf = keyconf; - - return 0; -} - -static int b43_key_clear(struct b43_wldev *dev, int index) -{ - if (B43_WARN_ON((index < 0) || (index >= ARRAY_SIZE(dev->key)))) - return -EINVAL; - do_key_write(dev, index, B43_SEC_ALGO_NONE, - NULL, B43_SEC_KEYSIZE, NULL); - if ((index <= 3) && !b43_new_kidx_api(dev)) { - do_key_write(dev, index + 4, B43_SEC_ALGO_NONE, - NULL, B43_SEC_KEYSIZE, NULL); - } - dev->key[index].keyconf = NULL; - - return 0; -} - -static void b43_clear_keys(struct b43_wldev *dev) -{ - int i, count; - - if (b43_new_kidx_api(dev)) - count = B43_NR_GROUP_KEYS + B43_NR_PAIRWISE_KEYS; - else - count = B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS; - for (i = 0; i < count; i++) - b43_key_clear(dev, i); -} - -static void b43_dump_keymemory(struct b43_wldev *dev) -{ - unsigned int i, index, count, offset, pairwise_keys_start; - u8 mac[ETH_ALEN]; - u16 algo; - u32 rcmta0; - u16 rcmta1; - u64 hf; - struct b43_key *key; - - if (!b43_debug(dev, B43_DBG_KEYS)) - return; - - hf = b43_hf_read(dev); - b43dbg(dev->wl, "Hardware key memory dump: USEDEFKEYS=%u\n", - !!(hf & B43_HF_USEDEFKEYS)); - if (b43_new_kidx_api(dev)) { - pairwise_keys_start = B43_NR_GROUP_KEYS; - count = B43_NR_GROUP_KEYS + B43_NR_PAIRWISE_KEYS; - } else { - pairwise_keys_start = B43_NR_GROUP_KEYS * 2; - count = B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS; - } - for (index = 0; index < count; index++) { - key = &(dev->key[index]); - printk(KERN_DEBUG "Key slot %02u: %s", - index, (key->keyconf == NULL) ? " " : "*"); - offset = dev->ktp + (index * B43_SEC_KEYSIZE); - for (i = 0; i < B43_SEC_KEYSIZE; i += 2) { - u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, offset + i); - printk("%02X%02X", (tmp & 0xFF), ((tmp >> 8) & 0xFF)); - } - - algo = b43_shm_read16(dev, B43_SHM_SHARED, - B43_SHM_SH_KEYIDXBLOCK + (index * 2)); - printk(" Algo: %04X/%02X", algo, key->algorithm); - - if (index >= pairwise_keys_start) { - if (key->algorithm == B43_SEC_ALGO_TKIP) { - printk(" TKIP: "); - offset = B43_SHM_SH_TKIPTSCTTAK + (index - 4) * (10 + 4); - for (i = 0; i < 14; i += 2) { - u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, offset + i); - printk("%02X%02X", (tmp & 0xFF), ((tmp >> 8) & 0xFF)); - } - } - rcmta0 = b43_shm_read32(dev, B43_SHM_RCMTA, - ((index - pairwise_keys_start) * 2) + 0); - rcmta1 = b43_shm_read16(dev, B43_SHM_RCMTA, - ((index - pairwise_keys_start) * 2) + 1); - *((__le32 *)(&mac[0])) = cpu_to_le32(rcmta0); - *((__le16 *)(&mac[4])) = cpu_to_le16(rcmta1); - printk(" MAC: %pM", mac); - } else - printk(" DEFAULT KEY"); - printk("\n"); - } -} - -void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags) -{ - u32 macctl; - u16 ucstat; - bool hwps; - bool awake; - int i; - - B43_WARN_ON((ps_flags & B43_PS_ENABLED) && - (ps_flags & B43_PS_DISABLED)); - B43_WARN_ON((ps_flags & B43_PS_AWAKE) && (ps_flags & B43_PS_ASLEEP)); - - if (ps_flags & B43_PS_ENABLED) { - hwps = true; - } else if (ps_flags & B43_PS_DISABLED) { - hwps = false; - } else { - //TODO: If powersave is not off and FIXME is not set and we are not in adhoc - // and thus is not an AP and we are associated, set bit 25 - } - if (ps_flags & B43_PS_AWAKE) { - awake = true; - } else if (ps_flags & B43_PS_ASLEEP) { - awake = false; - } else { - //TODO: If the device is awake or this is an AP, or we are scanning, or FIXME, - // or we are associated, or FIXME, or the latest PS-Poll packet sent was - // successful, set bit26 - } - -/* FIXME: For now we force awake-on and hwps-off */ - hwps = false; - awake = true; - - macctl = b43_read32(dev, B43_MMIO_MACCTL); - if (hwps) - macctl |= B43_MACCTL_HWPS; - else - macctl &= ~B43_MACCTL_HWPS; - if (awake) - macctl |= B43_MACCTL_AWAKE; - else - macctl &= ~B43_MACCTL_AWAKE; - b43_write32(dev, B43_MMIO_MACCTL, macctl); - /* Commit write */ - b43_read32(dev, B43_MMIO_MACCTL); - if (awake && dev->dev->core_rev >= 5) { - /* Wait for the microcode to wake up. */ - for (i = 0; i < 100; i++) { - ucstat = b43_shm_read16(dev, B43_SHM_SHARED, - B43_SHM_SH_UCODESTAT); - if (ucstat != B43_SHM_SH_UCODESTAT_SLEEP) - break; - udelay(10); - } - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/BmacCorePllReset */ -void b43_wireless_core_phy_pll_reset(struct b43_wldev *dev) -{ - struct bcma_drv_cc *bcma_cc __maybe_unused; - struct ssb_chipcommon *ssb_cc __maybe_unused; - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - bcma_cc = &dev->dev->bdev->bus->drv_cc; - - bcma_cc_write32(bcma_cc, BCMA_CC_CHIPCTL_ADDR, 0); - bcma_cc_mask32(bcma_cc, BCMA_CC_CHIPCTL_DATA, ~0x4); - bcma_cc_set32(bcma_cc, BCMA_CC_CHIPCTL_DATA, 0x4); - bcma_cc_mask32(bcma_cc, BCMA_CC_CHIPCTL_DATA, ~0x4); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - ssb_cc = &dev->dev->sdev->bus->chipco; - - chipco_write32(ssb_cc, SSB_CHIPCO_CHIPCTL_ADDR, 0); - chipco_mask32(ssb_cc, SSB_CHIPCO_CHIPCTL_DATA, ~0x4); - chipco_set32(ssb_cc, SSB_CHIPCO_CHIPCTL_DATA, 0x4); - chipco_mask32(ssb_cc, SSB_CHIPCO_CHIPCTL_DATA, ~0x4); - break; -#endif - } -} - -#ifdef CONFIG_B43_BCMA -static void b43_bcma_phy_reset(struct b43_wldev *dev) -{ - u32 flags; - - /* Put PHY into reset */ - flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - flags |= B43_BCMA_IOCTL_PHY_RESET; - flags |= B43_BCMA_IOCTL_PHY_BW_20MHZ; /* Make 20 MHz def */ - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags); - udelay(2); - - b43_phy_take_out_of_reset(dev); -} - -static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode) -{ - u32 req = B43_BCMA_CLKCTLST_80211_PLL_REQ | - B43_BCMA_CLKCTLST_PHY_PLL_REQ; - u32 status = B43_BCMA_CLKCTLST_80211_PLL_ST | - B43_BCMA_CLKCTLST_PHY_PLL_ST; - u32 flags; - - flags = B43_BCMA_IOCTL_PHY_CLKEN; - if (gmode) - flags |= B43_BCMA_IOCTL_GMODE; - b43_device_enable(dev, flags); - - if (dev->phy.type == B43_PHYTYPE_AC) { - u16 tmp; - - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - tmp &= ~B43_BCMA_IOCTL_DAC; - tmp |= 0x100; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); - - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); - - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - tmp |= B43_BCMA_IOCTL_PHY_CLKEN; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); - } - - bcma_core_set_clockmode(dev->dev->bdev, BCMA_CLKMODE_FAST); - b43_bcma_phy_reset(dev); - bcma_core_pll_ctl(dev->dev->bdev, req, status, true); -} -#endif - -#ifdef CONFIG_B43_SSB -static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode) -{ - u32 flags = 0; - - if (gmode) - flags |= B43_TMSLOW_GMODE; - flags |= B43_TMSLOW_PHYCLKEN; - flags |= B43_TMSLOW_PHYRESET; - if (dev->phy.type == B43_PHYTYPE_N) - flags |= B43_TMSLOW_PHY_BANDWIDTH_20MHZ; /* Make 20 MHz def */ - b43_device_enable(dev, flags); - msleep(2); /* Wait for the PLL to turn on. */ - - b43_phy_take_out_of_reset(dev); -} -#endif - -void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode) -{ - u32 macctl; - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - b43_bcma_wireless_core_reset(dev, gmode); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - b43_ssb_wireless_core_reset(dev, gmode); - break; -#endif - } - - /* Turn Analog ON, but only if we already know the PHY-type. - * This protects against very early setup where we don't know the - * PHY-type, yet. wireless_core_reset will be called once again later, - * when we know the PHY-type. */ - if (dev->phy.ops) - dev->phy.ops->switch_analog(dev, 1); - - macctl = b43_read32(dev, B43_MMIO_MACCTL); - macctl &= ~B43_MACCTL_GMODE; - if (gmode) - macctl |= B43_MACCTL_GMODE; - macctl |= B43_MACCTL_IHR_ENABLED; - b43_write32(dev, B43_MMIO_MACCTL, macctl); -} - -static void handle_irq_transmit_status(struct b43_wldev *dev) -{ - u32 v0, v1; - u16 tmp; - struct b43_txstatus stat; - - while (1) { - v0 = b43_read32(dev, B43_MMIO_XMITSTAT_0); - if (!(v0 & 0x00000001)) - break; - v1 = b43_read32(dev, B43_MMIO_XMITSTAT_1); - - stat.cookie = (v0 >> 16); - stat.seq = (v1 & 0x0000FFFF); - stat.phy_stat = ((v1 & 0x00FF0000) >> 16); - tmp = (v0 & 0x0000FFFF); - stat.frame_count = ((tmp & 0xF000) >> 12); - stat.rts_count = ((tmp & 0x0F00) >> 8); - stat.supp_reason = ((tmp & 0x001C) >> 2); - stat.pm_indicated = !!(tmp & 0x0080); - stat.intermediate = !!(tmp & 0x0040); - stat.for_ampdu = !!(tmp & 0x0020); - stat.acked = !!(tmp & 0x0002); - - b43_handle_txstatus(dev, &stat); - } -} - -static void drain_txstatus_queue(struct b43_wldev *dev) -{ - u32 dummy; - - if (dev->dev->core_rev < 5) - return; - /* Read all entries from the microcode TXstatus FIFO - * and throw them away. - */ - while (1) { - dummy = b43_read32(dev, B43_MMIO_XMITSTAT_0); - if (!(dummy & 0x00000001)) - break; - dummy = b43_read32(dev, B43_MMIO_XMITSTAT_1); - } -} - -static u32 b43_jssi_read(struct b43_wldev *dev) -{ - u32 val = 0; - - val = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI1); - val <<= 16; - val |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI0); - - return val; -} - -static void b43_jssi_write(struct b43_wldev *dev, u32 jssi) -{ - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI0, - (jssi & 0x0000FFFF)); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI1, - (jssi & 0xFFFF0000) >> 16); -} - -static void b43_generate_noise_sample(struct b43_wldev *dev) -{ - b43_jssi_write(dev, 0x7F7F7F7F); - b43_write32(dev, B43_MMIO_MACCMD, - b43_read32(dev, B43_MMIO_MACCMD) | B43_MACCMD_BGNOISE); -} - -static void b43_calculate_link_quality(struct b43_wldev *dev) -{ - /* Top half of Link Quality calculation. */ - - if (dev->phy.type != B43_PHYTYPE_G) - return; - if (dev->noisecalc.calculation_running) - return; - dev->noisecalc.calculation_running = true; - dev->noisecalc.nr_samples = 0; - - b43_generate_noise_sample(dev); -} - -static void handle_irq_noise(struct b43_wldev *dev) -{ - struct b43_phy_g *phy = dev->phy.g; - u16 tmp; - u8 noise[4]; - u8 i, j; - s32 average; - - /* Bottom half of Link Quality calculation. */ - - if (dev->phy.type != B43_PHYTYPE_G) - return; - - /* Possible race condition: It might be possible that the user - * changed to a different channel in the meantime since we - * started the calculation. We ignore that fact, since it's - * not really that much of a problem. The background noise is - * an estimation only anyway. Slightly wrong results will get damped - * by the averaging of the 8 sample rounds. Additionally the - * value is shortlived. So it will be replaced by the next noise - * calculation round soon. */ - - B43_WARN_ON(!dev->noisecalc.calculation_running); - *((__le32 *)noise) = cpu_to_le32(b43_jssi_read(dev)); - if (noise[0] == 0x7F || noise[1] == 0x7F || - noise[2] == 0x7F || noise[3] == 0x7F) - goto generate_new; - - /* Get the noise samples. */ - B43_WARN_ON(dev->noisecalc.nr_samples >= 8); - i = dev->noisecalc.nr_samples; - noise[0] = clamp_val(noise[0], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); - noise[1] = clamp_val(noise[1], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); - noise[2] = clamp_val(noise[2], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); - noise[3] = clamp_val(noise[3], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); - dev->noisecalc.samples[i][0] = phy->nrssi_lt[noise[0]]; - dev->noisecalc.samples[i][1] = phy->nrssi_lt[noise[1]]; - dev->noisecalc.samples[i][2] = phy->nrssi_lt[noise[2]]; - dev->noisecalc.samples[i][3] = phy->nrssi_lt[noise[3]]; - dev->noisecalc.nr_samples++; - if (dev->noisecalc.nr_samples == 8) { - /* Calculate the Link Quality by the noise samples. */ - average = 0; - for (i = 0; i < 8; i++) { - for (j = 0; j < 4; j++) - average += dev->noisecalc.samples[i][j]; - } - average /= (8 * 4); - average *= 125; - average += 64; - average /= 128; - tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x40C); - tmp = (tmp / 128) & 0x1F; - if (tmp >= 8) - average += 2; - else - average -= 25; - if (tmp == 8) - average -= 72; - else - average -= 48; - - dev->stats.link_noise = average; - dev->noisecalc.calculation_running = false; - return; - } -generate_new: - b43_generate_noise_sample(dev); -} - -static void handle_irq_tbtt_indication(struct b43_wldev *dev) -{ - if (b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) { - ///TODO: PS TBTT - } else { - if (1 /*FIXME: the last PSpoll frame was sent successfully */ ) - b43_power_saving_ctl_bits(dev, 0); - } - if (b43_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) - dev->dfq_valid = true; -} - -static void handle_irq_atim_end(struct b43_wldev *dev) -{ - if (dev->dfq_valid) { - b43_write32(dev, B43_MMIO_MACCMD, - b43_read32(dev, B43_MMIO_MACCMD) - | B43_MACCMD_DFQ_VALID); - dev->dfq_valid = false; - } -} - -static void handle_irq_pmq(struct b43_wldev *dev) -{ - u32 tmp; - - //TODO: AP mode. - - while (1) { - tmp = b43_read32(dev, B43_MMIO_PS_STATUS); - if (!(tmp & 0x00000008)) - break; - } - /* 16bit write is odd, but correct. */ - b43_write16(dev, B43_MMIO_PS_STATUS, 0x0002); -} - -static void b43_write_template_common(struct b43_wldev *dev, - const u8 *data, u16 size, - u16 ram_offset, - u16 shm_size_offset, u8 rate) -{ - u32 i, tmp; - struct b43_plcp_hdr4 plcp; - - plcp.data = 0; - b43_generate_plcp_hdr(&plcp, size + FCS_LEN, rate); - b43_ram_write(dev, ram_offset, le32_to_cpu(plcp.data)); - ram_offset += sizeof(u32); - /* The PLCP is 6 bytes long, but we only wrote 4 bytes, yet. - * So leave the first two bytes of the next write blank. - */ - tmp = (u32) (data[0]) << 16; - tmp |= (u32) (data[1]) << 24; - b43_ram_write(dev, ram_offset, tmp); - ram_offset += sizeof(u32); - for (i = 2; i < size; i += sizeof(u32)) { - tmp = (u32) (data[i + 0]); - if (i + 1 < size) - tmp |= (u32) (data[i + 1]) << 8; - if (i + 2 < size) - tmp |= (u32) (data[i + 2]) << 16; - if (i + 3 < size) - tmp |= (u32) (data[i + 3]) << 24; - b43_ram_write(dev, ram_offset + i - 2, tmp); - } - b43_shm_write16(dev, B43_SHM_SHARED, shm_size_offset, - size + sizeof(struct b43_plcp_hdr6)); -} - -/* Check if the use of the antenna that ieee80211 told us to - * use is possible. This will fall back to DEFAULT. - * "antenna_nr" is the antenna identifier we got from ieee80211. */ -u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev, - u8 antenna_nr) -{ - u8 antenna_mask; - - if (antenna_nr == 0) { - /* Zero means "use default antenna". That's always OK. */ - return 0; - } - - /* Get the mask of available antennas. */ - if (dev->phy.gmode) - antenna_mask = dev->dev->bus_sprom->ant_available_bg; - else - antenna_mask = dev->dev->bus_sprom->ant_available_a; - - if (!(antenna_mask & (1 << (antenna_nr - 1)))) { - /* This antenna is not available. Fall back to default. */ - return 0; - } - - return antenna_nr; -} - -/* Convert a b43 antenna number value to the PHY TX control value. */ -static u16 b43_antenna_to_phyctl(int antenna) -{ - switch (antenna) { - case B43_ANTENNA0: - return B43_TXH_PHY_ANT0; - case B43_ANTENNA1: - return B43_TXH_PHY_ANT1; - case B43_ANTENNA2: - return B43_TXH_PHY_ANT2; - case B43_ANTENNA3: - return B43_TXH_PHY_ANT3; - case B43_ANTENNA_AUTO0: - case B43_ANTENNA_AUTO1: - return B43_TXH_PHY_ANT01AUTO; - } - B43_WARN_ON(1); - return 0; -} - -static void b43_write_beacon_template(struct b43_wldev *dev, - u16 ram_offset, - u16 shm_size_offset) -{ - unsigned int i, len, variable_len; - const struct ieee80211_mgmt *bcn; - const u8 *ie; - bool tim_found = false; - unsigned int rate; - u16 ctl; - int antenna; - struct ieee80211_tx_info *info; - unsigned long flags; - struct sk_buff *beacon_skb; - - spin_lock_irqsave(&dev->wl->beacon_lock, flags); - info = IEEE80211_SKB_CB(dev->wl->current_beacon); - rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value; - /* Clone the beacon, so it cannot go away, while we write it to hw. */ - beacon_skb = skb_clone(dev->wl->current_beacon, GFP_ATOMIC); - spin_unlock_irqrestore(&dev->wl->beacon_lock, flags); - - if (!beacon_skb) { - b43dbg(dev->wl, "Could not upload beacon. " - "Failed to clone beacon skb."); - return; - } - - bcn = (const struct ieee80211_mgmt *)(beacon_skb->data); - len = min_t(size_t, beacon_skb->len, - 0x200 - sizeof(struct b43_plcp_hdr6)); - - b43_write_template_common(dev, (const u8 *)bcn, - len, ram_offset, shm_size_offset, rate); - - /* Write the PHY TX control parameters. */ - antenna = B43_ANTENNA_DEFAULT; - antenna = b43_antenna_to_phyctl(antenna); - ctl = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL); - /* We can't send beacons with short preamble. Would get PHY errors. */ - ctl &= ~B43_TXH_PHY_SHORTPRMBL; - ctl &= ~B43_TXH_PHY_ANT; - ctl &= ~B43_TXH_PHY_ENC; - ctl |= antenna; - if (b43_is_cck_rate(rate)) - ctl |= B43_TXH_PHY_ENC_CCK; - else - ctl |= B43_TXH_PHY_ENC_OFDM; - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, ctl); - - /* Find the position of the TIM and the DTIM_period value - * and write them to SHM. */ - ie = bcn->u.beacon.variable; - variable_len = len - offsetof(struct ieee80211_mgmt, u.beacon.variable); - for (i = 0; i < variable_len - 2; ) { - uint8_t ie_id, ie_len; - - ie_id = ie[i]; - ie_len = ie[i + 1]; - if (ie_id == 5) { - u16 tim_position; - u16 dtim_period; - /* This is the TIM Information Element */ - - /* Check whether the ie_len is in the beacon data range. */ - if (variable_len < ie_len + 2 + i) - break; - /* A valid TIM is at least 4 bytes long. */ - if (ie_len < 4) - break; - tim_found = true; - - tim_position = sizeof(struct b43_plcp_hdr6); - tim_position += offsetof(struct ieee80211_mgmt, u.beacon.variable); - tim_position += i; - - dtim_period = ie[i + 3]; - - b43_shm_write16(dev, B43_SHM_SHARED, - B43_SHM_SH_TIMBPOS, tim_position); - b43_shm_write16(dev, B43_SHM_SHARED, - B43_SHM_SH_DTIMPER, dtim_period); - break; - } - i += ie_len + 2; - } - if (!tim_found) { - /* - * If ucode wants to modify TIM do it behind the beacon, this - * will happen, for example, when doing mesh networking. - */ - b43_shm_write16(dev, B43_SHM_SHARED, - B43_SHM_SH_TIMBPOS, - len + sizeof(struct b43_plcp_hdr6)); - b43_shm_write16(dev, B43_SHM_SHARED, - B43_SHM_SH_DTIMPER, 0); - } - b43dbg(dev->wl, "Updated beacon template at 0x%x\n", ram_offset); - - dev_kfree_skb_any(beacon_skb); -} - -static void b43_upload_beacon0(struct b43_wldev *dev) -{ - struct b43_wl *wl = dev->wl; - - if (wl->beacon0_uploaded) - return; - b43_write_beacon_template(dev, B43_SHM_SH_BT_BASE0, B43_SHM_SH_BTL0); - wl->beacon0_uploaded = true; -} - -static void b43_upload_beacon1(struct b43_wldev *dev) -{ - struct b43_wl *wl = dev->wl; - - if (wl->beacon1_uploaded) - return; - b43_write_beacon_template(dev, B43_SHM_SH_BT_BASE1, B43_SHM_SH_BTL1); - wl->beacon1_uploaded = true; -} - -static void handle_irq_beacon(struct b43_wldev *dev) -{ - struct b43_wl *wl = dev->wl; - u32 cmd, beacon0_valid, beacon1_valid; - - if (!b43_is_mode(wl, NL80211_IFTYPE_AP) && - !b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) && - !b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) - return; - - /* This is the bottom half of the asynchronous beacon update. */ - - /* Ignore interrupt in the future. */ - dev->irq_mask &= ~B43_IRQ_BEACON; - - cmd = b43_read32(dev, B43_MMIO_MACCMD); - beacon0_valid = (cmd & B43_MACCMD_BEACON0_VALID); - beacon1_valid = (cmd & B43_MACCMD_BEACON1_VALID); - - /* Schedule interrupt manually, if busy. */ - if (beacon0_valid && beacon1_valid) { - b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_BEACON); - dev->irq_mask |= B43_IRQ_BEACON; - return; - } - - if (unlikely(wl->beacon_templates_virgin)) { - /* We never uploaded a beacon before. - * Upload both templates now, but only mark one valid. */ - wl->beacon_templates_virgin = false; - b43_upload_beacon0(dev); - b43_upload_beacon1(dev); - cmd = b43_read32(dev, B43_MMIO_MACCMD); - cmd |= B43_MACCMD_BEACON0_VALID; - b43_write32(dev, B43_MMIO_MACCMD, cmd); - } else { - if (!beacon0_valid) { - b43_upload_beacon0(dev); - cmd = b43_read32(dev, B43_MMIO_MACCMD); - cmd |= B43_MACCMD_BEACON0_VALID; - b43_write32(dev, B43_MMIO_MACCMD, cmd); - } else if (!beacon1_valid) { - b43_upload_beacon1(dev); - cmd = b43_read32(dev, B43_MMIO_MACCMD); - cmd |= B43_MACCMD_BEACON1_VALID; - b43_write32(dev, B43_MMIO_MACCMD, cmd); - } - } -} - -static void b43_do_beacon_update_trigger_work(struct b43_wldev *dev) -{ - u32 old_irq_mask = dev->irq_mask; - - /* update beacon right away or defer to irq */ - handle_irq_beacon(dev); - if (old_irq_mask != dev->irq_mask) { - /* The handler updated the IRQ mask. */ - B43_WARN_ON(!dev->irq_mask); - if (b43_read32(dev, B43_MMIO_GEN_IRQ_MASK)) { - b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); - } else { - /* Device interrupts are currently disabled. That means - * we just ran the hardirq handler and scheduled the - * IRQ thread. The thread will write the IRQ mask when - * it finished, so there's nothing to do here. Writing - * the mask _here_ would incorrectly re-enable IRQs. */ - } - } -} - -static void b43_beacon_update_trigger_work(struct work_struct *work) -{ - struct b43_wl *wl = container_of(work, struct b43_wl, - beacon_update_trigger); - struct b43_wldev *dev; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) { - if (b43_bus_host_is_sdio(dev->dev)) { - /* wl->mutex is enough. */ - b43_do_beacon_update_trigger_work(dev); - mmiowb(); - } else { - spin_lock_irq(&wl->hardirq_lock); - b43_do_beacon_update_trigger_work(dev); - mmiowb(); - spin_unlock_irq(&wl->hardirq_lock); - } - } - mutex_unlock(&wl->mutex); -} - -/* Asynchronously update the packet templates in template RAM. */ -static void b43_update_templates(struct b43_wl *wl) -{ - struct sk_buff *beacon, *old_beacon; - unsigned long flags; - - /* This is the top half of the asynchronous beacon update. - * The bottom half is the beacon IRQ. - * Beacon update must be asynchronous to avoid sending an - * invalid beacon. This can happen for example, if the firmware - * transmits a beacon while we are updating it. */ - - /* We could modify the existing beacon and set the aid bit in - * the TIM field, but that would probably require resizing and - * moving of data within the beacon template. - * Simply request a new beacon and let mac80211 do the hard work. */ - beacon = ieee80211_beacon_get(wl->hw, wl->vif); - if (unlikely(!beacon)) - return; - - spin_lock_irqsave(&wl->beacon_lock, flags); - old_beacon = wl->current_beacon; - wl->current_beacon = beacon; - wl->beacon0_uploaded = false; - wl->beacon1_uploaded = false; - spin_unlock_irqrestore(&wl->beacon_lock, flags); - - ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger); - - if (old_beacon) - dev_kfree_skb_any(old_beacon); -} - -static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int) -{ - b43_time_lock(dev); - if (dev->dev->core_rev >= 3) { - b43_write32(dev, B43_MMIO_TSF_CFP_REP, (beacon_int << 16)); - b43_write32(dev, B43_MMIO_TSF_CFP_START, (beacon_int << 10)); - } else { - b43_write16(dev, 0x606, (beacon_int >> 6)); - b43_write16(dev, 0x610, beacon_int); - } - b43_time_unlock(dev); - b43dbg(dev->wl, "Set beacon interval to %u\n", beacon_int); -} - -static void b43_handle_firmware_panic(struct b43_wldev *dev) -{ - u16 reason; - - /* Read the register that contains the reason code for the panic. */ - reason = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_FWPANIC_REASON_REG); - b43err(dev->wl, "Whoopsy, firmware panic! Reason: %u\n", reason); - - switch (reason) { - default: - b43dbg(dev->wl, "The panic reason is unknown.\n"); - /* fallthrough */ - case B43_FWPANIC_DIE: - /* Do not restart the controller or firmware. - * The device is nonfunctional from now on. - * Restarting would result in this panic to trigger again, - * so we avoid that recursion. */ - break; - case B43_FWPANIC_RESTART: - b43_controller_restart(dev, "Microcode panic"); - break; - } -} - -static void handle_irq_ucode_debug(struct b43_wldev *dev) -{ - unsigned int i, cnt; - u16 reason, marker_id, marker_line; - __le16 *buf; - - /* The proprietary firmware doesn't have this IRQ. */ - if (!dev->fw.opensource) - return; - - /* Read the register that contains the reason code for this IRQ. */ - reason = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_DEBUGIRQ_REASON_REG); - - switch (reason) { - case B43_DEBUGIRQ_PANIC: - b43_handle_firmware_panic(dev); - break; - case B43_DEBUGIRQ_DUMP_SHM: - if (!B43_DEBUG) - break; /* Only with driver debugging enabled. */ - buf = kmalloc(4096, GFP_ATOMIC); - if (!buf) { - b43dbg(dev->wl, "SHM-dump: Failed to allocate memory\n"); - goto out; - } - for (i = 0; i < 4096; i += 2) { - u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, i); - buf[i / 2] = cpu_to_le16(tmp); - } - b43info(dev->wl, "Shared memory dump:\n"); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, - 16, 2, buf, 4096, 1); - kfree(buf); - break; - case B43_DEBUGIRQ_DUMP_REGS: - if (!B43_DEBUG) - break; /* Only with driver debugging enabled. */ - b43info(dev->wl, "Microcode register dump:\n"); - for (i = 0, cnt = 0; i < 64; i++) { - u16 tmp = b43_shm_read16(dev, B43_SHM_SCRATCH, i); - if (cnt == 0) - printk(KERN_INFO); - printk("r%02u: 0x%04X ", i, tmp); - cnt++; - if (cnt == 6) { - printk("\n"); - cnt = 0; - } - } - printk("\n"); - break; - case B43_DEBUGIRQ_MARKER: - if (!B43_DEBUG) - break; /* Only with driver debugging enabled. */ - marker_id = b43_shm_read16(dev, B43_SHM_SCRATCH, - B43_MARKER_ID_REG); - marker_line = b43_shm_read16(dev, B43_SHM_SCRATCH, - B43_MARKER_LINE_REG); - b43info(dev->wl, "The firmware just executed the MARKER(%u) " - "at line number %u\n", - marker_id, marker_line); - break; - default: - b43dbg(dev->wl, "Debug-IRQ triggered for unknown reason: %u\n", - reason); - } -out: - /* Acknowledge the debug-IRQ, so the firmware can continue. */ - b43_shm_write16(dev, B43_SHM_SCRATCH, - B43_DEBUGIRQ_REASON_REG, B43_DEBUGIRQ_ACK); -} - -static void b43_do_interrupt_thread(struct b43_wldev *dev) -{ - u32 reason; - u32 dma_reason[ARRAY_SIZE(dev->dma_reason)]; - u32 merged_dma_reason = 0; - int i; - - if (unlikely(b43_status(dev) != B43_STAT_STARTED)) - return; - - reason = dev->irq_reason; - for (i = 0; i < ARRAY_SIZE(dma_reason); i++) { - dma_reason[i] = dev->dma_reason[i]; - merged_dma_reason |= dma_reason[i]; - } - - if (unlikely(reason & B43_IRQ_MAC_TXERR)) - b43err(dev->wl, "MAC transmission error\n"); - - if (unlikely(reason & B43_IRQ_PHY_TXERR)) { - b43err(dev->wl, "PHY transmission error\n"); - rmb(); - if (unlikely(atomic_dec_and_test(&dev->phy.txerr_cnt))) { - atomic_set(&dev->phy.txerr_cnt, - B43_PHY_TX_BADNESS_LIMIT); - b43err(dev->wl, "Too many PHY TX errors, " - "restarting the controller\n"); - b43_controller_restart(dev, "PHY TX errors"); - } - } - - if (unlikely(merged_dma_reason & (B43_DMAIRQ_FATALMASK))) { - b43err(dev->wl, - "Fatal DMA error: 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X\n", - dma_reason[0], dma_reason[1], - dma_reason[2], dma_reason[3], - dma_reason[4], dma_reason[5]); - b43err(dev->wl, "This device does not support DMA " - "on your system. It will now be switched to PIO.\n"); - /* Fall back to PIO transfers if we get fatal DMA errors! */ - dev->use_pio = true; - b43_controller_restart(dev, "DMA error"); - return; - } - - if (unlikely(reason & B43_IRQ_UCODE_DEBUG)) - handle_irq_ucode_debug(dev); - if (reason & B43_IRQ_TBTT_INDI) - handle_irq_tbtt_indication(dev); - if (reason & B43_IRQ_ATIM_END) - handle_irq_atim_end(dev); - if (reason & B43_IRQ_BEACON) - handle_irq_beacon(dev); - if (reason & B43_IRQ_PMQ) - handle_irq_pmq(dev); - if (reason & B43_IRQ_TXFIFO_FLUSH_OK) - ;/* TODO */ - if (reason & B43_IRQ_NOISESAMPLE_OK) - handle_irq_noise(dev); - - /* Check the DMA reason registers for received data. */ - if (dma_reason[0] & B43_DMAIRQ_RDESC_UFLOW) { - if (B43_DEBUG) - b43warn(dev->wl, "RX descriptor underrun\n"); - b43_dma_handle_rx_overflow(dev->dma.rx_ring); - } - if (dma_reason[0] & B43_DMAIRQ_RX_DONE) { - if (b43_using_pio_transfers(dev)) - b43_pio_rx(dev->pio.rx_queue); - else - b43_dma_rx(dev->dma.rx_ring); - } - B43_WARN_ON(dma_reason[1] & B43_DMAIRQ_RX_DONE); - B43_WARN_ON(dma_reason[2] & B43_DMAIRQ_RX_DONE); - B43_WARN_ON(dma_reason[3] & B43_DMAIRQ_RX_DONE); - B43_WARN_ON(dma_reason[4] & B43_DMAIRQ_RX_DONE); - B43_WARN_ON(dma_reason[5] & B43_DMAIRQ_RX_DONE); - - if (reason & B43_IRQ_TX_OK) - handle_irq_transmit_status(dev); - - /* Re-enable interrupts on the device by restoring the current interrupt mask. */ - b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); - -#if B43_DEBUG - if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { - dev->irq_count++; - for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { - if (reason & (1 << i)) - dev->irq_bit_count[i]++; - } - } -#endif -} - -/* Interrupt thread handler. Handles device interrupts in thread context. */ -static irqreturn_t b43_interrupt_thread_handler(int irq, void *dev_id) -{ - struct b43_wldev *dev = dev_id; - - mutex_lock(&dev->wl->mutex); - b43_do_interrupt_thread(dev); - mmiowb(); - mutex_unlock(&dev->wl->mutex); - - return IRQ_HANDLED; -} - -static irqreturn_t b43_do_interrupt(struct b43_wldev *dev) -{ - u32 reason; - - /* This code runs under wl->hardirq_lock, but _only_ on non-SDIO busses. - * On SDIO, this runs under wl->mutex. */ - - reason = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); - if (reason == 0xffffffff) /* shared IRQ */ - return IRQ_NONE; - reason &= dev->irq_mask; - if (!reason) - return IRQ_NONE; - - dev->dma_reason[0] = b43_read32(dev, B43_MMIO_DMA0_REASON) - & 0x0001FC00; - dev->dma_reason[1] = b43_read32(dev, B43_MMIO_DMA1_REASON) - & 0x0000DC00; - dev->dma_reason[2] = b43_read32(dev, B43_MMIO_DMA2_REASON) - & 0x0000DC00; - dev->dma_reason[3] = b43_read32(dev, B43_MMIO_DMA3_REASON) - & 0x0001DC00; - dev->dma_reason[4] = b43_read32(dev, B43_MMIO_DMA4_REASON) - & 0x0000DC00; -/* Unused ring - dev->dma_reason[5] = b43_read32(dev, B43_MMIO_DMA5_REASON) - & 0x0000DC00; -*/ - - /* ACK the interrupt. */ - b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, reason); - b43_write32(dev, B43_MMIO_DMA0_REASON, dev->dma_reason[0]); - b43_write32(dev, B43_MMIO_DMA1_REASON, dev->dma_reason[1]); - b43_write32(dev, B43_MMIO_DMA2_REASON, dev->dma_reason[2]); - b43_write32(dev, B43_MMIO_DMA3_REASON, dev->dma_reason[3]); - b43_write32(dev, B43_MMIO_DMA4_REASON, dev->dma_reason[4]); -/* Unused ring - b43_write32(dev, B43_MMIO_DMA5_REASON, dev->dma_reason[5]); -*/ - - /* Disable IRQs on the device. The IRQ thread handler will re-enable them. */ - b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); - /* Save the reason bitmasks for the IRQ thread handler. */ - dev->irq_reason = reason; - - return IRQ_WAKE_THREAD; -} - -/* Interrupt handler top-half. This runs with interrupts disabled. */ -static irqreturn_t b43_interrupt_handler(int irq, void *dev_id) -{ - struct b43_wldev *dev = dev_id; - irqreturn_t ret; - - if (unlikely(b43_status(dev) < B43_STAT_STARTED)) - return IRQ_NONE; - - spin_lock(&dev->wl->hardirq_lock); - ret = b43_do_interrupt(dev); - mmiowb(); - spin_unlock(&dev->wl->hardirq_lock); - - return ret; -} - -/* SDIO interrupt handler. This runs in process context. */ -static void b43_sdio_interrupt_handler(struct b43_wldev *dev) -{ - struct b43_wl *wl = dev->wl; - irqreturn_t ret; - - mutex_lock(&wl->mutex); - - ret = b43_do_interrupt(dev); - if (ret == IRQ_WAKE_THREAD) - b43_do_interrupt_thread(dev); - - mutex_unlock(&wl->mutex); -} - -void b43_do_release_fw(struct b43_firmware_file *fw) -{ - release_firmware(fw->data); - fw->data = NULL; - fw->filename = NULL; -} - -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); - b43_do_release_fw(&dev->fw.initvals_band); -} - -static void b43_print_fw_helptext(struct b43_wl *wl, bool error) -{ - const char text[] = - "You must go to " \ - "http://wireless.kernel.org/en/users/Drivers/b43#devicefirmware " \ - "and download the correct firmware for this driver version. " \ - "Please carefully read all instructions on this website.\n"; - - if (error) - b43err(wl, text); - else - b43warn(wl, text); -} - -static void b43_fw_cb(const struct firmware *firmware, void *context) -{ - struct b43_request_fw_context *ctx = context; - - ctx->blob = firmware; - complete(&ctx->dev->fw_load_complete); -} - -int b43_do_request_fw(struct b43_request_fw_context *ctx, - const char *name, - struct b43_firmware_file *fw, bool async) -{ - struct b43_fw_header *hdr; - u32 size; - int err; - - if (!name) { - /* Don't fetch anything. Free possibly cached firmware. */ - /* FIXME: We should probably keep it anyway, to save some headache - * on suspend/resume with multiband devices. */ - b43_do_release_fw(fw); - return 0; - } - if (fw->filename) { - if ((fw->type == ctx->req_type) && - (strcmp(fw->filename, name) == 0)) - return 0; /* Already have this fw. */ - /* Free the cached firmware first. */ - /* FIXME: We should probably do this later after we successfully - * got the new fw. This could reduce headache with multiband devices. - * We could also redesign this to cache the firmware for all possible - * bands all the time. */ - b43_do_release_fw(fw); - } - - switch (ctx->req_type) { - case B43_FWTYPE_PROPRIETARY: - snprintf(ctx->fwname, sizeof(ctx->fwname), - "b43%s/%s.fw", - modparam_fwpostfix, name); - break; - case B43_FWTYPE_OPENSOURCE: - snprintf(ctx->fwname, sizeof(ctx->fwname), - "b43-open%s/%s.fw", - modparam_fwpostfix, name); - break; - default: - B43_WARN_ON(1); - return -ENOSYS; - } - if (async) { - /* do this part asynchronously */ - 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); - if (err < 0) { - pr_err("Unable to load firmware\n"); - return err; - } - 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 fall through here - */ - } - err = request_firmware(&ctx->blob, ctx->fwname, - ctx->dev->dev->dev); - if (err == -ENOENT) { - snprintf(ctx->errors[ctx->req_type], - sizeof(ctx->errors[ctx->req_type]), - "Firmware file \"%s\" not found\n", - ctx->fwname); - return err; - } else if (err) { - snprintf(ctx->errors[ctx->req_type], - sizeof(ctx->errors[ctx->req_type]), - "Firmware file \"%s\" request failed (err=%d)\n", - ctx->fwname, err); - return err; - } -fw_ready: - if (ctx->blob->size < sizeof(struct b43_fw_header)) - goto err_format; - hdr = (struct b43_fw_header *)(ctx->blob->data); - switch (hdr->type) { - case B43_FW_TYPE_UCODE: - case B43_FW_TYPE_PCM: - size = be32_to_cpu(hdr->size); - if (size != ctx->blob->size - sizeof(struct b43_fw_header)) - goto err_format; - /* fallthrough */ - case B43_FW_TYPE_IV: - if (hdr->ver != 1) - goto err_format; - break; - default: - goto err_format; - } - - fw->data = ctx->blob; - fw->filename = name; - fw->type = ctx->req_type; - - return 0; - -err_format: - snprintf(ctx->errors[ctx->req_type], - sizeof(ctx->errors[ctx->req_type]), - "Firmware file \"%s\" format error.\n", ctx->fwname); - release_firmware(ctx->blob); - - return -EPROTO; -} - -/* http://bcm-v4.sipsolutions.net/802.11/Init/Firmware */ -static int b43_try_request_fw(struct b43_request_fw_context *ctx) -{ - struct b43_wldev *dev = ctx->dev; - struct b43_firmware *fw = &ctx->dev->fw; - struct b43_phy *phy = &dev->phy; - const u8 rev = ctx->dev->dev->core_rev; - const char *filename; - int err; - - /* Get microcode */ - filename = NULL; - switch (rev) { - case 42: - if (phy->type == B43_PHYTYPE_AC) - filename = "ucode42"; - break; - case 40: - if (phy->type == B43_PHYTYPE_AC) - filename = "ucode40"; - break; - case 33: - if (phy->type == B43_PHYTYPE_LCN40) - filename = "ucode33_lcn40"; - break; - case 30: - if (phy->type == B43_PHYTYPE_N) - filename = "ucode30_mimo"; - break; - case 29: - if (phy->type == B43_PHYTYPE_HT) - filename = "ucode29_mimo"; - break; - case 26: - if (phy->type == B43_PHYTYPE_HT) - filename = "ucode26_mimo"; - break; - case 28: - case 25: - if (phy->type == B43_PHYTYPE_N) - filename = "ucode25_mimo"; - else if (phy->type == B43_PHYTYPE_LCN) - filename = "ucode25_lcn"; - break; - case 24: - if (phy->type == B43_PHYTYPE_LCN) - filename = "ucode24_lcn"; - break; - case 23: - if (phy->type == B43_PHYTYPE_N) - filename = "ucode16_mimo"; - break; - case 16 ... 19: - if (phy->type == B43_PHYTYPE_N) - filename = "ucode16_mimo"; - else if (phy->type == B43_PHYTYPE_LP) - filename = "ucode16_lp"; - break; - case 15: - filename = "ucode15"; - break; - case 14: - filename = "ucode14"; - break; - case 13: - filename = "ucode13"; - break; - case 11 ... 12: - filename = "ucode11"; - break; - case 5 ... 10: - filename = "ucode5"; - break; - } - if (!filename) - goto err_no_ucode; - err = b43_do_request_fw(ctx, filename, &fw->ucode, true); - if (err) - goto err_load; - - /* Get PCM code */ - if ((rev >= 5) && (rev <= 10)) - filename = "pcm5"; - else if (rev >= 11) - filename = NULL; - else - goto err_no_pcm; - fw->pcm_request_failed = false; - err = b43_do_request_fw(ctx, filename, &fw->pcm, false); - if (err == -ENOENT) { - /* We did not find a PCM file? Not fatal, but - * core rev <= 10 must do without hwcrypto then. */ - fw->pcm_request_failed = true; - } else if (err) - goto err_load; - - /* Get initvals */ - filename = NULL; - switch (dev->phy.type) { - case B43_PHYTYPE_G: - if (rev == 13) - filename = "b0g0initvals13"; - else if (rev >= 5 && rev <= 10) - filename = "b0g0initvals5"; - break; - case B43_PHYTYPE_N: - if (rev == 30) - filename = "n16initvals30"; - else if (rev == 28 || rev == 25) - filename = "n0initvals25"; - else if (rev == 24) - filename = "n0initvals24"; - else if (rev == 23) - filename = "n0initvals16"; /* What about n0initvals22? */ - else if (rev >= 16 && rev <= 18) - filename = "n0initvals16"; - else if (rev >= 11 && rev <= 12) - filename = "n0initvals11"; - break; - case B43_PHYTYPE_LP: - if (rev >= 16 && rev <= 18) - filename = "lp0initvals16"; - else if (rev == 15) - filename = "lp0initvals15"; - else if (rev == 14) - filename = "lp0initvals14"; - else if (rev == 13) - filename = "lp0initvals13"; - break; - case B43_PHYTYPE_HT: - if (rev == 29) - filename = "ht0initvals29"; - else if (rev == 26) - filename = "ht0initvals26"; - break; - case B43_PHYTYPE_LCN: - if (rev == 24) - filename = "lcn0initvals24"; - break; - case B43_PHYTYPE_LCN40: - if (rev == 33) - filename = "lcn400initvals33"; - break; - case B43_PHYTYPE_AC: - if (rev == 42) - filename = "ac1initvals42"; - else if (rev == 40) - filename = "ac0initvals40"; - break; - } - if (!filename) - goto err_no_initvals; - err = b43_do_request_fw(ctx, filename, &fw->initvals, false); - if (err) - goto err_load; - - /* Get bandswitch initvals */ - filename = NULL; - switch (dev->phy.type) { - case B43_PHYTYPE_G: - if (rev == 13) - filename = "b0g0bsinitvals13"; - else if (rev >= 5 && rev <= 10) - filename = "b0g0bsinitvals5"; - break; - case B43_PHYTYPE_N: - if (rev == 30) - filename = "n16bsinitvals30"; - else if (rev == 28 || rev == 25) - filename = "n0bsinitvals25"; - else if (rev == 24) - filename = "n0bsinitvals24"; - else if (rev == 23) - filename = "n0bsinitvals16"; /* What about n0bsinitvals22? */ - else if (rev >= 16 && rev <= 18) - filename = "n0bsinitvals16"; - else if (rev >= 11 && rev <= 12) - filename = "n0bsinitvals11"; - break; - case B43_PHYTYPE_LP: - if (rev >= 16 && rev <= 18) - filename = "lp0bsinitvals16"; - else if (rev == 15) - filename = "lp0bsinitvals15"; - else if (rev == 14) - filename = "lp0bsinitvals14"; - else if (rev == 13) - filename = "lp0bsinitvals13"; - break; - case B43_PHYTYPE_HT: - if (rev == 29) - filename = "ht0bsinitvals29"; - else if (rev == 26) - filename = "ht0bsinitvals26"; - break; - case B43_PHYTYPE_LCN: - if (rev == 24) - filename = "lcn0bsinitvals24"; - break; - case B43_PHYTYPE_LCN40: - if (rev == 33) - filename = "lcn400bsinitvals33"; - break; - case B43_PHYTYPE_AC: - if (rev == 42) - filename = "ac1bsinitvals42"; - else if (rev == 40) - filename = "ac0bsinitvals40"; - break; - } - if (!filename) - goto err_no_initvals; - err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false); - if (err) - goto err_load; - - fw->opensource = (ctx->req_type == B43_FWTYPE_OPENSOURCE); - - return 0; - -err_no_ucode: - err = ctx->fatal_failure = -EOPNOTSUPP; - b43err(dev->wl, "The driver does not know which firmware (ucode) " - "is required for your device (wl-core rev %u)\n", rev); - goto error; - -err_no_pcm: - err = ctx->fatal_failure = -EOPNOTSUPP; - b43err(dev->wl, "The driver does not know which firmware (PCM) " - "is required for your device (wl-core rev %u)\n", rev); - goto error; - -err_no_initvals: - err = ctx->fatal_failure = -EOPNOTSUPP; - b43err(dev->wl, "The driver does not know which firmware (initvals) " - "is required for your device (wl-core rev %u)\n", rev); - goto error; - -err_load: - /* We failed to load this firmware image. The error message - * already is in ctx->errors. Return and let our caller decide - * what to do. */ - goto error; - -error: - b43_release_firmware(dev); - return err; -} - -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) -{ - struct b43_wl *wl = container_of(work, - struct b43_wl, firmware_load); - struct b43_wldev *dev = wl->current_dev; - struct b43_request_fw_context *ctx; - unsigned int i; - int err; - const char *errmsg; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return; - ctx->dev = dev; - - ctx->req_type = B43_FWTYPE_PROPRIETARY; - err = b43_try_request_fw(ctx); - if (!err) - goto start_ieee80211; /* Successfully loaded it. */ - /* Was fw version known? */ - if (ctx->fatal_failure) - goto out; - - /* proprietary fw not found, try open source */ - ctx->req_type = B43_FWTYPE_OPENSOURCE; - err = b43_try_request_fw(ctx); - if (!err) - goto start_ieee80211; /* Successfully loaded it. */ - if(ctx->fatal_failure) - goto out; - - /* Could not find a usable firmware. Print the errors. */ - for (i = 0; i < B43_NR_FWTYPES; i++) { - errmsg = ctx->errors[i]; - if (strlen(errmsg)) - b43err(dev->wl, "%s", errmsg); - } - b43_print_fw_helptext(dev->wl, 1); - goto out; - -start_ieee80211: - wl->hw->queues = B43_QOS_QUEUE_NUM; - if (!modparam_qos || dev->fw.opensource) - wl->hw->queues = 1; - - err = ieee80211_register_hw(wl->hw); - if (err) - 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: - b43_one_core_detach(dev->dev); - -out: - kfree(ctx); -} - -static int b43_upload_microcode(struct b43_wldev *dev) -{ - struct wiphy *wiphy = dev->wl->hw->wiphy; - const size_t hdr_len = sizeof(struct b43_fw_header); - const __be32 *data; - unsigned int i, len; - u16 fwrev, fwpatch, fwdate, fwtime; - u32 tmp, macctl; - int err = 0; - - /* Jump the microcode PSM to offset 0 */ - macctl = b43_read32(dev, B43_MMIO_MACCTL); - B43_WARN_ON(macctl & B43_MACCTL_PSM_RUN); - macctl |= B43_MACCTL_PSM_JMP0; - b43_write32(dev, B43_MMIO_MACCTL, macctl); - /* Zero out all microcode PSM registers and shared memory. */ - for (i = 0; i < 64; i++) - b43_shm_write16(dev, B43_SHM_SCRATCH, i, 0); - for (i = 0; i < 4096; i += 2) - b43_shm_write16(dev, B43_SHM_SHARED, i, 0); - - /* Upload Microcode. */ - data = (__be32 *) (dev->fw.ucode.data->data + hdr_len); - len = (dev->fw.ucode.data->size - hdr_len) / sizeof(__be32); - b43_shm_control_word(dev, B43_SHM_UCODE | B43_SHM_AUTOINC_W, 0x0000); - for (i = 0; i < len; i++) { - b43_write32(dev, B43_MMIO_SHM_DATA, be32_to_cpu(data[i])); - udelay(10); - } - - if (dev->fw.pcm.data) { - /* Upload PCM data. */ - data = (__be32 *) (dev->fw.pcm.data->data + hdr_len); - len = (dev->fw.pcm.data->size - hdr_len) / sizeof(__be32); - b43_shm_control_word(dev, B43_SHM_HW, 0x01EA); - b43_write32(dev, B43_MMIO_SHM_DATA, 0x00004000); - /* No need for autoinc bit in SHM_HW */ - b43_shm_control_word(dev, B43_SHM_HW, 0x01EB); - for (i = 0; i < len; i++) { - b43_write32(dev, B43_MMIO_SHM_DATA, be32_to_cpu(data[i])); - udelay(10); - } - } - - b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_ALL); - - /* Start the microcode PSM */ - b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_JMP0, - B43_MACCTL_PSM_RUN); - - /* Wait for the microcode to load and respond */ - i = 0; - while (1) { - tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); - if (tmp == B43_IRQ_MAC_SUSPENDED) - break; - i++; - if (i >= 20) { - b43err(dev->wl, "Microcode not responding\n"); - b43_print_fw_helptext(dev->wl, 1); - err = -ENODEV; - goto error; - } - msleep(50); - } - b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); /* dummy read */ - - /* Get and check the revisions. */ - fwrev = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODEREV); - fwpatch = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODEPATCH); - fwdate = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODEDATE); - fwtime = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODETIME); - - if (fwrev <= 0x128) { - b43err(dev->wl, "YOUR FIRMWARE IS TOO OLD. Firmware from " - "binary drivers older than version 4.x is unsupported. " - "You must upgrade your firmware files.\n"); - b43_print_fw_helptext(dev->wl, 1); - err = -EOPNOTSUPP; - goto error; - } - dev->fw.rev = fwrev; - dev->fw.patch = fwpatch; - if (dev->fw.rev >= 598) - dev->fw.hdr_format = B43_FW_HDR_598; - else if (dev->fw.rev >= 410) - dev->fw.hdr_format = B43_FW_HDR_410; - else - dev->fw.hdr_format = B43_FW_HDR_351; - WARN_ON(dev->fw.opensource != (fwdate == 0xFFFF)); - - dev->qos_enabled = dev->wl->hw->queues > 1; - /* Default to firmware/hardware crypto acceleration. */ - dev->hwcrypto_enabled = true; - - if (dev->fw.opensource) { - u16 fwcapa; - - /* Patchlevel info is encoded in the "time" field. */ - dev->fw.patch = fwtime; - b43info(dev->wl, "Loading OpenSource firmware version %u.%u\n", - dev->fw.rev, dev->fw.patch); - - fwcapa = b43_fwcapa_read(dev); - if (!(fwcapa & B43_FWCAPA_HWCRYPTO) || dev->fw.pcm_request_failed) { - b43info(dev->wl, "Hardware crypto acceleration not supported by firmware\n"); - /* Disable hardware crypto and fall back to software crypto. */ - dev->hwcrypto_enabled = false; - } - /* adding QoS support should use an offline discovery mechanism */ - WARN(fwcapa & B43_FWCAPA_QOS, "QoS in OpenFW not supported\n"); - } else { - b43info(dev->wl, "Loading firmware version %u.%u " - "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n", - fwrev, fwpatch, - (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF, - (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, fwtime & 0x1F); - if (dev->fw.pcm_request_failed) { - b43warn(dev->wl, "No \"pcm5.fw\" firmware file found. " - "Hardware accelerated cryptography is disabled.\n"); - b43_print_fw_helptext(dev->wl, 0); - } - } - - snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "%u.%u", - dev->fw.rev, dev->fw.patch); - wiphy->hw_version = dev->dev->core_id; - - if (dev->fw.hdr_format == B43_FW_HDR_351) { - /* We're over the deadline, but we keep support for old fw - * until it turns out to be in major conflict with something new. */ - b43warn(dev->wl, "You are using an old firmware image. " - "Support for old firmware will be removed soon " - "(official deadline was July 2008).\n"); - b43_print_fw_helptext(dev->wl, 0); - } - - return 0; - -error: - /* Stop the microcode PSM. */ - b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN, - B43_MACCTL_PSM_JMP0); - - return err; -} - -static int b43_write_initvals(struct b43_wldev *dev, - const struct b43_iv *ivals, - size_t count, - size_t array_size) -{ - const struct b43_iv *iv; - u16 offset; - size_t i; - bool bit32; - - BUILD_BUG_ON(sizeof(struct b43_iv) != 6); - iv = ivals; - for (i = 0; i < count; i++) { - if (array_size < sizeof(iv->offset_size)) - goto err_format; - array_size -= sizeof(iv->offset_size); - offset = be16_to_cpu(iv->offset_size); - bit32 = !!(offset & B43_IV_32BIT); - offset &= B43_IV_OFFSET_MASK; - if (offset >= 0x1000) - goto err_format; - if (bit32) { - u32 value; - - if (array_size < sizeof(iv->data.d32)) - goto err_format; - array_size -= sizeof(iv->data.d32); - - value = get_unaligned_be32(&iv->data.d32); - b43_write32(dev, offset, value); - - iv = (const struct b43_iv *)((const uint8_t *)iv + - sizeof(__be16) + - sizeof(__be32)); - } else { - u16 value; - - if (array_size < sizeof(iv->data.d16)) - goto err_format; - array_size -= sizeof(iv->data.d16); - - value = be16_to_cpu(iv->data.d16); - b43_write16(dev, offset, value); - - iv = (const struct b43_iv *)((const uint8_t *)iv + - sizeof(__be16) + - sizeof(__be16)); - } - } - if (array_size) - goto err_format; - - return 0; - -err_format: - b43err(dev->wl, "Initial Values Firmware file-format error.\n"); - b43_print_fw_helptext(dev->wl, 1); - - return -EPROTO; -} - -static int b43_upload_initvals(struct b43_wldev *dev) -{ - const size_t hdr_len = sizeof(struct b43_fw_header); - const struct b43_fw_header *hdr; - struct b43_firmware *fw = &dev->fw; - const struct b43_iv *ivals; - size_t count; - - hdr = (const struct b43_fw_header *)(fw->initvals.data->data); - ivals = (const struct b43_iv *)(fw->initvals.data->data + hdr_len); - count = be32_to_cpu(hdr->size); - return b43_write_initvals(dev, ivals, count, - fw->initvals.data->size - hdr_len); -} - -static int b43_upload_initvals_band(struct b43_wldev *dev) -{ - const size_t hdr_len = sizeof(struct b43_fw_header); - const struct b43_fw_header *hdr; - struct b43_firmware *fw = &dev->fw; - const struct b43_iv *ivals; - size_t count; - - if (!fw->initvals_band.data) - return 0; - - hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data); - ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len); - count = be32_to_cpu(hdr->size); - return b43_write_initvals(dev, ivals, count, - fw->initvals_band.data->size - hdr_len); -} - -/* Initialize the GPIOs - * http://bcm-specs.sipsolutions.net/GPIO - */ - -#ifdef CONFIG_B43_SSB -static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev) -{ - struct ssb_bus *bus = dev->dev->sdev->bus; - -#ifdef CONFIG_SSB_DRIVER_PCICORE - return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev); -#else - return bus->chipco.dev; -#endif -} -#endif - -static int b43_gpio_init(struct b43_wldev *dev) -{ -#ifdef CONFIG_B43_SSB - struct ssb_device *gpiodev; -#endif - u32 mask, set; - - b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0); - b43_maskset16(dev, B43_MMIO_GPIO_MASK, ~0, 0xF); - - mask = 0x0000001F; - set = 0x0000000F; - if (dev->dev->chip_id == 0x4301) { - mask |= 0x0060; - set |= 0x0060; - } else if (dev->dev->chip_id == 0x5354) { - /* Don't allow overtaking buttons GPIOs */ - set &= 0x2; /* 0x2 is LED GPIO on BCM5354 */ - } - - if (0 /* FIXME: conditional unknown */ ) { - b43_write16(dev, B43_MMIO_GPIO_MASK, - b43_read16(dev, B43_MMIO_GPIO_MASK) - | 0x0100); - /* BT Coexistance Input */ - mask |= 0x0080; - set |= 0x0080; - /* BT Coexistance Out */ - mask |= 0x0100; - set |= 0x0100; - } - if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) { - /* PA is controlled by gpio 9, let ucode handle it */ - b43_write16(dev, B43_MMIO_GPIO_MASK, - b43_read16(dev, B43_MMIO_GPIO_MASK) - | 0x0200); - mask |= 0x0200; - set |= 0x0200; - } - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - bcma_chipco_gpio_control(&dev->dev->bdev->bus->drv_cc, mask, set); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - gpiodev = b43_ssb_gpio_dev(dev); - if (gpiodev) - ssb_write32(gpiodev, B43_GPIO_CONTROL, - (ssb_read32(gpiodev, B43_GPIO_CONTROL) - & ~mask) | set); - break; -#endif - } - - return 0; -} - -/* Turn off all GPIO stuff. Call this on module unload, for example. */ -static void b43_gpio_cleanup(struct b43_wldev *dev) -{ -#ifdef CONFIG_B43_SSB - struct ssb_device *gpiodev; -#endif - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - bcma_chipco_gpio_control(&dev->dev->bdev->bus->drv_cc, ~0, 0); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - gpiodev = b43_ssb_gpio_dev(dev); - if (gpiodev) - ssb_write32(gpiodev, B43_GPIO_CONTROL, 0); - break; -#endif - } -} - -/* http://bcm-specs.sipsolutions.net/EnableMac */ -void b43_mac_enable(struct b43_wldev *dev) -{ - if (b43_debug(dev, B43_DBG_FIRMWARE)) { - u16 fwstate; - - fwstate = b43_shm_read16(dev, B43_SHM_SHARED, - B43_SHM_SH_UCODESTAT); - if ((fwstate != B43_SHM_SH_UCODESTAT_SUSP) && - (fwstate != B43_SHM_SH_UCODESTAT_SLEEP)) { - b43err(dev->wl, "b43_mac_enable(): The firmware " - "should be suspended, but current state is %u\n", - fwstate); - } - } - - dev->mac_suspended--; - B43_WARN_ON(dev->mac_suspended < 0); - if (dev->mac_suspended == 0) { - b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_ENABLED); - b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, - B43_IRQ_MAC_SUSPENDED); - /* Commit writes */ - b43_read32(dev, B43_MMIO_MACCTL); - b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); - b43_power_saving_ctl_bits(dev, 0); - } -} - -/* http://bcm-specs.sipsolutions.net/SuspendMAC */ -void b43_mac_suspend(struct b43_wldev *dev) -{ - int i; - u32 tmp; - - might_sleep(); - B43_WARN_ON(dev->mac_suspended < 0); - - if (dev->mac_suspended == 0) { - b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); - b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_ENABLED, 0); - /* force pci to flush the write */ - b43_read32(dev, B43_MMIO_MACCTL); - for (i = 35; i; i--) { - tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); - if (tmp & B43_IRQ_MAC_SUSPENDED) - goto out; - udelay(10); - } - /* Hm, it seems this will take some time. Use msleep(). */ - for (i = 40; i; i--) { - tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); - if (tmp & B43_IRQ_MAC_SUSPENDED) - goto out; - msleep(1); - } - b43err(dev->wl, "MAC suspend failed\n"); - } -out: - dev->mac_suspended++; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/MacPhyClkSet */ -void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on) -{ - u32 tmp; - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - if (on) - tmp |= B43_BCMA_IOCTL_MACPHYCLKEN; - else - tmp &= ~B43_BCMA_IOCTL_MACPHYCLKEN; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); - if (on) - tmp |= B43_TMSLOW_MACPHYCLKEN; - else - tmp &= ~B43_TMSLOW_MACPHYCLKEN; - ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); - break; -#endif - } -} - -/* brcms_b_switch_macfreq */ -void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode) -{ - u16 chip_id = dev->dev->chip_id; - - if (chip_id == BCMA_CHIP_ID_BCM4331) { - switch (spurmode) { - case 2: /* 168 Mhz: 2^26/168 = 0x61862 */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x1862); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x6); - break; - case 1: /* 164 Mhz: 2^26/164 = 0x63e70 */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x3e70); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x6); - break; - default: /* 160 Mhz: 2^26/160 = 0x66666 */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x6666); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x6); - break; - } - } else if (chip_id == BCMA_CHIP_ID_BCM43131 || - chip_id == BCMA_CHIP_ID_BCM43217 || - chip_id == BCMA_CHIP_ID_BCM43222 || - chip_id == BCMA_CHIP_ID_BCM43224 || - chip_id == BCMA_CHIP_ID_BCM43225 || - chip_id == BCMA_CHIP_ID_BCM43227 || - chip_id == BCMA_CHIP_ID_BCM43228) { - switch (spurmode) { - case 2: /* 126 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); - break; - case 1: /* 123 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); - break; - default: /* 120 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); - break; - } - } else if (dev->phy.type == B43_PHYTYPE_LCN) { - switch (spurmode) { - case 1: /* 82 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); - break; - default: /* 80 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); - break; - } - } -} - -static void b43_adjust_opmode(struct b43_wldev *dev) -{ - struct b43_wl *wl = dev->wl; - u32 ctl; - u16 cfp_pretbtt; - - ctl = b43_read32(dev, B43_MMIO_MACCTL); - /* Reset status to STA infrastructure mode. */ - ctl &= ~B43_MACCTL_AP; - ctl &= ~B43_MACCTL_KEEP_CTL; - ctl &= ~B43_MACCTL_KEEP_BADPLCP; - ctl &= ~B43_MACCTL_KEEP_BAD; - ctl &= ~B43_MACCTL_PROMISC; - ctl &= ~B43_MACCTL_BEACPROMISC; - ctl |= B43_MACCTL_INFRA; - - if (b43_is_mode(wl, NL80211_IFTYPE_AP) || - b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT)) - ctl |= B43_MACCTL_AP; - else if (b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) - ctl &= ~B43_MACCTL_INFRA; - - if (wl->filter_flags & FIF_CONTROL) - ctl |= B43_MACCTL_KEEP_CTL; - if (wl->filter_flags & FIF_FCSFAIL) - ctl |= B43_MACCTL_KEEP_BAD; - if (wl->filter_flags & FIF_PLCPFAIL) - ctl |= B43_MACCTL_KEEP_BADPLCP; - if (wl->filter_flags & FIF_BCN_PRBRESP_PROMISC) - ctl |= B43_MACCTL_BEACPROMISC; - - /* Workaround: On old hardware the HW-MAC-address-filter - * doesn't work properly, so always run promisc in filter - * it in software. */ - if (dev->dev->core_rev <= 4) - ctl |= B43_MACCTL_PROMISC; - - b43_write32(dev, B43_MMIO_MACCTL, ctl); - - cfp_pretbtt = 2; - if ((ctl & B43_MACCTL_INFRA) && !(ctl & B43_MACCTL_AP)) { - if (dev->dev->chip_id == 0x4306 && - dev->dev->chip_rev == 3) - cfp_pretbtt = 100; - else - cfp_pretbtt = 50; - } - b43_write16(dev, 0x612, cfp_pretbtt); - - /* FIXME: We don't currently implement the PMQ mechanism, - * so always disable it. If we want to implement PMQ, - * we need to enable it here (clear DISCPMQ) in AP mode. - */ - if (0 /* ctl & B43_MACCTL_AP */) - b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_DISCPMQ, 0); - else - b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_DISCPMQ); -} - -static void b43_rate_memory_write(struct b43_wldev *dev, u16 rate, int is_ofdm) -{ - u16 offset; - - if (is_ofdm) { - offset = 0x480; - offset += (b43_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2; - } else { - offset = 0x4C0; - offset += (b43_plcp_get_ratecode_cck(rate) & 0x000F) * 2; - } - b43_shm_write16(dev, B43_SHM_SHARED, offset + 0x20, - b43_shm_read16(dev, B43_SHM_SHARED, offset)); -} - -static void b43_rate_memory_init(struct b43_wldev *dev) -{ - switch (dev->phy.type) { - case B43_PHYTYPE_A: - case B43_PHYTYPE_G: - case B43_PHYTYPE_N: - case B43_PHYTYPE_LP: - case B43_PHYTYPE_HT: - case B43_PHYTYPE_LCN: - b43_rate_memory_write(dev, B43_OFDM_RATE_6MB, 1); - b43_rate_memory_write(dev, B43_OFDM_RATE_9MB, 1); - b43_rate_memory_write(dev, B43_OFDM_RATE_12MB, 1); - b43_rate_memory_write(dev, B43_OFDM_RATE_18MB, 1); - b43_rate_memory_write(dev, B43_OFDM_RATE_24MB, 1); - b43_rate_memory_write(dev, B43_OFDM_RATE_36MB, 1); - b43_rate_memory_write(dev, B43_OFDM_RATE_48MB, 1); - b43_rate_memory_write(dev, B43_OFDM_RATE_54MB, 1); - if (dev->phy.type == B43_PHYTYPE_A) - break; - /* fallthrough */ - case B43_PHYTYPE_B: - b43_rate_memory_write(dev, B43_CCK_RATE_1MB, 0); - b43_rate_memory_write(dev, B43_CCK_RATE_2MB, 0); - b43_rate_memory_write(dev, B43_CCK_RATE_5MB, 0); - b43_rate_memory_write(dev, B43_CCK_RATE_11MB, 0); - break; - default: - B43_WARN_ON(1); - } -} - -/* Set the default values for the PHY TX Control Words. */ -static void b43_set_phytxctl_defaults(struct b43_wldev *dev) -{ - u16 ctl = 0; - - ctl |= B43_TXH_PHY_ENC_CCK; - ctl |= B43_TXH_PHY_ANT01AUTO; - ctl |= B43_TXH_PHY_TXPWR; - - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, ctl); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL, ctl); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL, ctl); -} - -/* Set the TX-Antenna for management frames sent by firmware. */ -static void b43_mgmtframe_txantenna(struct b43_wldev *dev, int antenna) -{ - u16 ant; - u16 tmp; - - ant = b43_antenna_to_phyctl(antenna); - - /* For ACK/CTS */ - tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL); - tmp = (tmp & ~B43_TXH_PHY_ANT) | ant; - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL, tmp); - /* For Probe Resposes */ - tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL); - tmp = (tmp & ~B43_TXH_PHY_ANT) | ant; - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL, tmp); -} - -/* This is the opposite of b43_chip_init() */ -static void b43_chip_exit(struct b43_wldev *dev) -{ - b43_phy_exit(dev); - b43_gpio_cleanup(dev); - /* firmware is released later */ -} - -/* Initialize the chip - * http://bcm-specs.sipsolutions.net/ChipInit - */ -static int b43_chip_init(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - int err; - u32 macctl; - u16 value16; - - /* Initialize the MAC control */ - macctl = B43_MACCTL_IHR_ENABLED | B43_MACCTL_SHM_ENABLED; - if (dev->phy.gmode) - macctl |= B43_MACCTL_GMODE; - macctl |= B43_MACCTL_INFRA; - b43_write32(dev, B43_MMIO_MACCTL, macctl); - - err = b43_upload_microcode(dev); - if (err) - goto out; /* firmware is released later */ - - err = b43_gpio_init(dev); - if (err) - goto out; /* firmware is released later */ - - err = b43_upload_initvals(dev); - if (err) - goto err_gpio_clean; - - err = b43_upload_initvals_band(dev); - if (err) - goto err_gpio_clean; - - /* Turn the Analog on and initialize the PHY. */ - phy->ops->switch_analog(dev, 1); - err = b43_phy_init(dev); - if (err) - goto err_gpio_clean; - - /* Disable Interference Mitigation. */ - if (phy->ops->interf_mitigation) - phy->ops->interf_mitigation(dev, B43_INTERFMODE_NONE); - - /* Select the antennae */ - if (phy->ops->set_rx_antenna) - phy->ops->set_rx_antenna(dev, B43_ANTENNA_DEFAULT); - b43_mgmtframe_txantenna(dev, B43_ANTENNA_DEFAULT); - - if (phy->type == B43_PHYTYPE_B) { - value16 = b43_read16(dev, 0x005E); - value16 |= 0x0004; - b43_write16(dev, 0x005E, value16); - } - b43_write32(dev, 0x0100, 0x01000000); - if (dev->dev->core_rev < 5) - b43_write32(dev, 0x010C, 0x01000000); - - b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_INFRA, 0); - b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_INFRA); - - /* Probe Response Timeout value */ - /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */ - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRMAXTIME, 0); - - /* Initially set the wireless operation mode. */ - b43_adjust_opmode(dev); - - if (dev->dev->core_rev < 3) { - b43_write16(dev, 0x060E, 0x0000); - b43_write16(dev, 0x0610, 0x8000); - b43_write16(dev, 0x0604, 0x0000); - b43_write16(dev, 0x0606, 0x0200); - } else { - b43_write32(dev, 0x0188, 0x80000000); - b43_write32(dev, 0x018C, 0x02000000); - } - b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, 0x00004000); - b43_write32(dev, B43_MMIO_DMA0_IRQ_MASK, 0x0001FC00); - b43_write32(dev, B43_MMIO_DMA1_IRQ_MASK, 0x0000DC00); - b43_write32(dev, B43_MMIO_DMA2_IRQ_MASK, 0x0000DC00); - b43_write32(dev, B43_MMIO_DMA3_IRQ_MASK, 0x0001DC00); - b43_write32(dev, B43_MMIO_DMA4_IRQ_MASK, 0x0000DC00); - b43_write32(dev, B43_MMIO_DMA5_IRQ_MASK, 0x0000DC00); - - b43_mac_phy_clock_set(dev, true); - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - /* FIXME: 0xE74 is quite common, but should be read from CC */ - b43_write16(dev, B43_MMIO_POWERUP_DELAY, 0xE74); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - b43_write16(dev, B43_MMIO_POWERUP_DELAY, - dev->dev->sdev->bus->chipco.fast_pwrup_delay); - break; -#endif - } - - err = 0; - b43dbg(dev->wl, "Chip initialized\n"); -out: - return err; - -err_gpio_clean: - b43_gpio_cleanup(dev); - return err; -} - -static void b43_periodic_every60sec(struct b43_wldev *dev) -{ - const struct b43_phy_operations *ops = dev->phy.ops; - - if (ops->pwork_60sec) - ops->pwork_60sec(dev); - - /* Force check the TX power emission now. */ - b43_phy_txpower_check(dev, B43_TXPWR_IGNORE_TIME); -} - -static void b43_periodic_every30sec(struct b43_wldev *dev) -{ - /* Update device statistics. */ - b43_calculate_link_quality(dev); -} - -static void b43_periodic_every15sec(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - u16 wdr; - - if (dev->fw.opensource) { - /* Check if the firmware is still alive. - * It will reset the watchdog counter to 0 in its idle loop. */ - wdr = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_WATCHDOG_REG); - if (unlikely(wdr)) { - b43err(dev->wl, "Firmware watchdog: The firmware died!\n"); - b43_controller_restart(dev, "Firmware watchdog"); - return; - } else { - b43_shm_write16(dev, B43_SHM_SCRATCH, - B43_WATCHDOG_REG, 1); - } - } - - if (phy->ops->pwork_15sec) - phy->ops->pwork_15sec(dev); - - atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); - wmb(); - -#if B43_DEBUG - if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { - unsigned int i; - - b43dbg(dev->wl, "Stats: %7u IRQs/sec, %7u TX/sec, %7u RX/sec\n", - dev->irq_count / 15, - dev->tx_count / 15, - dev->rx_count / 15); - dev->irq_count = 0; - dev->tx_count = 0; - dev->rx_count = 0; - for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { - if (dev->irq_bit_count[i]) { - b43dbg(dev->wl, "Stats: %7u IRQ-%02u/sec (0x%08X)\n", - dev->irq_bit_count[i] / 15, i, (1 << i)); - dev->irq_bit_count[i] = 0; - } - } - } -#endif -} - -static void do_periodic_work(struct b43_wldev *dev) -{ - unsigned int state; - - state = dev->periodic_state; - if (state % 4 == 0) - b43_periodic_every60sec(dev); - if (state % 2 == 0) - b43_periodic_every30sec(dev); - b43_periodic_every15sec(dev); -} - -/* Periodic work locking policy: - * The whole periodic work handler is protected by - * wl->mutex. If another lock is needed somewhere in the - * pwork callchain, it's acquired in-place, where it's needed. - */ -static void b43_periodic_work_handler(struct work_struct *work) -{ - struct b43_wldev *dev = container_of(work, struct b43_wldev, - periodic_work.work); - struct b43_wl *wl = dev->wl; - unsigned long delay; - - mutex_lock(&wl->mutex); - - if (unlikely(b43_status(dev) != B43_STAT_STARTED)) - goto out; - if (b43_debug(dev, B43_DBG_PWORK_STOP)) - goto out_requeue; - - do_periodic_work(dev); - - dev->periodic_state++; -out_requeue: - if (b43_debug(dev, B43_DBG_PWORK_FAST)) - delay = msecs_to_jiffies(50); - else - delay = round_jiffies_relative(HZ * 15); - ieee80211_queue_delayed_work(wl->hw, &dev->periodic_work, delay); -out: - mutex_unlock(&wl->mutex); -} - -static void b43_periodic_tasks_setup(struct b43_wldev *dev) -{ - struct delayed_work *work = &dev->periodic_work; - - dev->periodic_state = 0; - INIT_DELAYED_WORK(work, b43_periodic_work_handler); - ieee80211_queue_delayed_work(dev->wl->hw, work, 0); -} - -/* Check if communication with the device works correctly. */ -static int b43_validate_chipaccess(struct b43_wldev *dev) -{ - u32 v, backup0, backup4; - - backup0 = b43_shm_read32(dev, B43_SHM_SHARED, 0); - backup4 = b43_shm_read32(dev, B43_SHM_SHARED, 4); - - /* Check for read/write and endianness problems. */ - b43_shm_write32(dev, B43_SHM_SHARED, 0, 0x55AAAA55); - if (b43_shm_read32(dev, B43_SHM_SHARED, 0) != 0x55AAAA55) - goto error; - b43_shm_write32(dev, B43_SHM_SHARED, 0, 0xAA5555AA); - if (b43_shm_read32(dev, B43_SHM_SHARED, 0) != 0xAA5555AA) - goto error; - - /* Check if unaligned 32bit SHM_SHARED access works properly. - * However, don't bail out on failure, because it's noncritical. */ - b43_shm_write16(dev, B43_SHM_SHARED, 0, 0x1122); - b43_shm_write16(dev, B43_SHM_SHARED, 2, 0x3344); - b43_shm_write16(dev, B43_SHM_SHARED, 4, 0x5566); - b43_shm_write16(dev, B43_SHM_SHARED, 6, 0x7788); - if (b43_shm_read32(dev, B43_SHM_SHARED, 2) != 0x55663344) - b43warn(dev->wl, "Unaligned 32bit SHM read access is broken\n"); - b43_shm_write32(dev, B43_SHM_SHARED, 2, 0xAABBCCDD); - if (b43_shm_read16(dev, B43_SHM_SHARED, 0) != 0x1122 || - b43_shm_read16(dev, B43_SHM_SHARED, 2) != 0xCCDD || - b43_shm_read16(dev, B43_SHM_SHARED, 4) != 0xAABB || - b43_shm_read16(dev, B43_SHM_SHARED, 6) != 0x7788) - b43warn(dev->wl, "Unaligned 32bit SHM write access is broken\n"); - - b43_shm_write32(dev, B43_SHM_SHARED, 0, backup0); - b43_shm_write32(dev, B43_SHM_SHARED, 4, backup4); - - if ((dev->dev->core_rev >= 3) && (dev->dev->core_rev <= 10)) { - /* The 32bit register shadows the two 16bit registers - * with update sideeffects. Validate this. */ - b43_write16(dev, B43_MMIO_TSF_CFP_START, 0xAAAA); - b43_write32(dev, B43_MMIO_TSF_CFP_START, 0xCCCCBBBB); - if (b43_read16(dev, B43_MMIO_TSF_CFP_START_LOW) != 0xBBBB) - goto error; - if (b43_read16(dev, B43_MMIO_TSF_CFP_START_HIGH) != 0xCCCC) - goto error; - } - b43_write32(dev, B43_MMIO_TSF_CFP_START, 0); - - v = b43_read32(dev, B43_MMIO_MACCTL); - v |= B43_MACCTL_GMODE; - if (v != (B43_MACCTL_GMODE | B43_MACCTL_IHR_ENABLED)) - goto error; - - return 0; -error: - b43err(dev->wl, "Failed to validate the chipaccess\n"); - return -ENODEV; -} - -static void b43_security_init(struct b43_wldev *dev) -{ - dev->ktp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_KTP); - /* KTP is a word address, but we address SHM bytewise. - * So multiply by two. - */ - dev->ktp *= 2; - /* Number of RCMTA address slots */ - b43_write16(dev, B43_MMIO_RCMTA_COUNT, B43_NR_PAIRWISE_KEYS); - /* Clear the key memory. */ - b43_clear_keys(dev); -} - -#ifdef CONFIG_B43_HWRNG -static int b43_rng_read(struct hwrng *rng, u32 *data) -{ - struct b43_wl *wl = (struct b43_wl *)rng->priv; - struct b43_wldev *dev; - int count = -ENODEV; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (likely(dev && b43_status(dev) >= B43_STAT_INITIALIZED)) { - *data = b43_read16(dev, B43_MMIO_RNG); - count = sizeof(u16); - } - mutex_unlock(&wl->mutex); - - return count; -} -#endif /* CONFIG_B43_HWRNG */ - -static void b43_rng_exit(struct b43_wl *wl) -{ -#ifdef CONFIG_B43_HWRNG - if (wl->rng_initialized) - hwrng_unregister(&wl->rng); -#endif /* CONFIG_B43_HWRNG */ -} - -static int b43_rng_init(struct b43_wl *wl) -{ - int err = 0; - -#ifdef CONFIG_B43_HWRNG - snprintf(wl->rng_name, ARRAY_SIZE(wl->rng_name), - "%s_%s", KBUILD_MODNAME, wiphy_name(wl->hw->wiphy)); - wl->rng.name = wl->rng_name; - wl->rng.data_read = b43_rng_read; - wl->rng.priv = (unsigned long)wl; - wl->rng_initialized = true; - err = hwrng_register(&wl->rng); - if (err) { - wl->rng_initialized = false; - b43err(wl, "Failed to register the random " - "number generator (%d)\n", err); - } -#endif /* CONFIG_B43_HWRNG */ - - return err; -} - -static void b43_tx_work(struct work_struct *work) -{ - struct b43_wl *wl = container_of(work, struct b43_wl, tx_work); - struct b43_wldev *dev; - struct sk_buff *skb; - int queue_num; - int err = 0; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED)) { - mutex_unlock(&wl->mutex); - return; - } - - for (queue_num = 0; queue_num < B43_QOS_QUEUE_NUM; queue_num++) { - while (skb_queue_len(&wl->tx_queue[queue_num])) { - skb = skb_dequeue(&wl->tx_queue[queue_num]); - if (b43_using_pio_transfers(dev)) - err = b43_pio_tx(dev, skb); - else - err = b43_dma_tx(dev, skb); - if (err == -ENOSPC) { - wl->tx_queue_stopped[queue_num] = 1; - ieee80211_stop_queue(wl->hw, queue_num); - skb_queue_head(&wl->tx_queue[queue_num], skb); - break; - } - if (unlikely(err)) - ieee80211_free_txskb(wl->hw, skb); - err = 0; - } - - if (!err) - wl->tx_queue_stopped[queue_num] = 0; - } - -#if B43_DEBUG - dev->tx_count++; -#endif - mutex_unlock(&wl->mutex); -} - -static void b43_op_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - - if (unlikely(skb->len < 2 + 2 + 6)) { - /* Too short, this can't be a valid frame. */ - ieee80211_free_txskb(hw, skb); - return; - } - B43_WARN_ON(skb_shinfo(skb)->nr_frags); - - skb_queue_tail(&wl->tx_queue[skb->queue_mapping], skb); - if (!wl->tx_queue_stopped[skb->queue_mapping]) { - ieee80211_queue_work(wl->hw, &wl->tx_work); - } else { - ieee80211_stop_queue(wl->hw, skb->queue_mapping); - } -} - -static void b43_qos_params_upload(struct b43_wldev *dev, - const struct ieee80211_tx_queue_params *p, - u16 shm_offset) -{ - u16 params[B43_NR_QOSPARAMS]; - int bslots, tmp; - unsigned int i; - - if (!dev->qos_enabled) - return; - - bslots = b43_read16(dev, B43_MMIO_RNG) & p->cw_min; - - memset(¶ms, 0, sizeof(params)); - - params[B43_QOSPARAM_TXOP] = p->txop * 32; - params[B43_QOSPARAM_CWMIN] = p->cw_min; - params[B43_QOSPARAM_CWMAX] = p->cw_max; - params[B43_QOSPARAM_CWCUR] = p->cw_min; - params[B43_QOSPARAM_AIFS] = p->aifs; - params[B43_QOSPARAM_BSLOTS] = bslots; - params[B43_QOSPARAM_REGGAP] = bslots + p->aifs; - - for (i = 0; i < ARRAY_SIZE(params); i++) { - if (i == B43_QOSPARAM_STATUS) { - tmp = b43_shm_read16(dev, B43_SHM_SHARED, - shm_offset + (i * 2)); - /* Mark the parameters as updated. */ - tmp |= 0x100; - b43_shm_write16(dev, B43_SHM_SHARED, - shm_offset + (i * 2), - tmp); - } else { - b43_shm_write16(dev, B43_SHM_SHARED, - shm_offset + (i * 2), - params[i]); - } - } -} - -/* Mapping of mac80211 queue numbers to b43 QoS SHM offsets. */ -static const u16 b43_qos_shm_offsets[] = { - /* [mac80211-queue-nr] = SHM_OFFSET, */ - [0] = B43_QOS_VOICE, - [1] = B43_QOS_VIDEO, - [2] = B43_QOS_BESTEFFORT, - [3] = B43_QOS_BACKGROUND, -}; - -/* Update all QOS parameters in hardware. */ -static void b43_qos_upload_all(struct b43_wldev *dev) -{ - struct b43_wl *wl = dev->wl; - struct b43_qos_params *params; - unsigned int i; - - if (!dev->qos_enabled) - return; - - BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) != - ARRAY_SIZE(wl->qos_params)); - - b43_mac_suspend(dev); - for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) { - params = &(wl->qos_params[i]); - b43_qos_params_upload(dev, &(params->p), - b43_qos_shm_offsets[i]); - } - b43_mac_enable(dev); -} - -static void b43_qos_clear(struct b43_wl *wl) -{ - struct b43_qos_params *params; - unsigned int i; - - /* Initialize QoS parameters to sane defaults. */ - - BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) != - ARRAY_SIZE(wl->qos_params)); - - for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) { - params = &(wl->qos_params[i]); - - switch (b43_qos_shm_offsets[i]) { - case B43_QOS_VOICE: - params->p.txop = 0; - params->p.aifs = 2; - params->p.cw_min = 0x0001; - params->p.cw_max = 0x0001; - break; - case B43_QOS_VIDEO: - params->p.txop = 0; - params->p.aifs = 2; - params->p.cw_min = 0x0001; - params->p.cw_max = 0x0001; - break; - case B43_QOS_BESTEFFORT: - params->p.txop = 0; - params->p.aifs = 3; - params->p.cw_min = 0x0001; - params->p.cw_max = 0x03FF; - break; - case B43_QOS_BACKGROUND: - params->p.txop = 0; - params->p.aifs = 7; - params->p.cw_min = 0x0001; - params->p.cw_max = 0x03FF; - break; - default: - B43_WARN_ON(1); - } - } -} - -/* Initialize the core's QOS capabilities */ -static void b43_qos_init(struct b43_wldev *dev) -{ - if (!dev->qos_enabled) { - /* Disable QOS support. */ - b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_EDCF); - b43_write16(dev, B43_MMIO_IFSCTL, - b43_read16(dev, B43_MMIO_IFSCTL) - & ~B43_MMIO_IFSCTL_USE_EDCF); - b43dbg(dev->wl, "QoS disabled\n"); - return; - } - - /* Upload the current QOS parameters. */ - b43_qos_upload_all(dev); - - /* Enable QOS support. */ - b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF); - b43_write16(dev, B43_MMIO_IFSCTL, - b43_read16(dev, B43_MMIO_IFSCTL) - | B43_MMIO_IFSCTL_USE_EDCF); - b43dbg(dev->wl, "QoS enabled\n"); -} - -static int b43_op_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 _queue, - const struct ieee80211_tx_queue_params *params) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - unsigned int queue = (unsigned int)_queue; - int err = -ENODEV; - - if (queue >= ARRAY_SIZE(wl->qos_params)) { - /* Queue not available or don't support setting - * params on this queue. Return success to not - * confuse mac80211. */ - return 0; - } - BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) != - ARRAY_SIZE(wl->qos_params)); - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (unlikely(!dev || (b43_status(dev) < B43_STAT_INITIALIZED))) - goto out_unlock; - - memcpy(&(wl->qos_params[queue].p), params, sizeof(*params)); - b43_mac_suspend(dev); - b43_qos_params_upload(dev, &(wl->qos_params[queue].p), - b43_qos_shm_offsets[queue]); - b43_mac_enable(dev); - err = 0; - -out_unlock: - mutex_unlock(&wl->mutex); - - return err; -} - -static int b43_op_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - - mutex_lock(&wl->mutex); - memcpy(stats, &wl->ieee_stats, sizeof(*stats)); - mutex_unlock(&wl->mutex); - - return 0; -} - -static u64 b43_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - u64 tsf; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - - if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) - b43_tsf_read(dev, &tsf); - else - tsf = 0; - - mutex_unlock(&wl->mutex); - - return tsf; -} - -static void b43_op_set_tsf(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u64 tsf) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - - if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) - b43_tsf_write(dev, tsf); - - mutex_unlock(&wl->mutex); -} - -static const char *band_to_string(enum ieee80211_band band) -{ - switch (band) { - case IEEE80211_BAND_5GHZ: - return "5"; - case IEEE80211_BAND_2GHZ: - return "2.4"; - default: - break; - } - B43_WARN_ON(1); - return ""; -} - -/* Expects wl->mutex locked */ -static int b43_switch_band(struct b43_wldev *dev, - struct ieee80211_channel *chan) -{ - struct b43_phy *phy = &dev->phy; - bool gmode; - u32 tmp; - - switch (chan->band) { - case IEEE80211_BAND_5GHZ: - gmode = false; - break; - case IEEE80211_BAND_2GHZ: - gmode = true; - break; - default: - B43_WARN_ON(1); - return -EINVAL; - } - - if (!((gmode && phy->supports_2ghz) || - (!gmode && phy->supports_5ghz))) { - b43err(dev->wl, "This device doesn't support %s-GHz band\n", - band_to_string(chan->band)); - return -ENODEV; - } - - if (!!phy->gmode == !!gmode) { - /* This device is already running. */ - return 0; - } - - b43dbg(dev->wl, "Switching to %s GHz band\n", - band_to_string(chan->band)); - - /* Some new devices don't need disabling radio for band switching */ - if (!(phy->type == B43_PHYTYPE_N && phy->rev >= 3)) - b43_software_rfkill(dev, true); - - phy->gmode = gmode; - b43_phy_put_into_reset(dev); - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - if (gmode) - tmp |= B43_BCMA_IOCTL_GMODE; - else - tmp &= ~B43_BCMA_IOCTL_GMODE; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); - if (gmode) - tmp |= B43_TMSLOW_GMODE; - else - tmp &= ~B43_TMSLOW_GMODE; - ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); - break; -#endif - } - b43_phy_take_out_of_reset(dev); - - b43_upload_initvals_band(dev); - - b43_phy_init(dev); - - return 0; -} - -static void b43_set_beacon_listen_interval(struct b43_wldev *dev, u16 interval) -{ - interval = min_t(u16, interval, (u16)0xFF); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BCN_LI, interval); -} - -/* Write the short and long frame retry limit values. */ -static void b43_set_retry_limits(struct b43_wldev *dev, - unsigned int short_retry, - unsigned int long_retry) -{ - /* The retry limit is a 4-bit counter. Enforce this to avoid overflowing - * the chip-internal counter. */ - short_retry = min(short_retry, (unsigned int)0xF); - long_retry = min(long_retry, (unsigned int)0xF); - - b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_SRLIMIT, - short_retry); - b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_LRLIMIT, - long_retry); -} - -static int b43_op_config(struct ieee80211_hw *hw, u32 changed) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev = wl->current_dev; - struct b43_phy *phy = &dev->phy; - struct ieee80211_conf *conf = &hw->conf; - int antenna; - int err = 0; - - mutex_lock(&wl->mutex); - b43_mac_suspend(dev); - - if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) - b43_set_beacon_listen_interval(dev, conf->listen_interval); - - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - phy->chandef = &conf->chandef; - phy->channel = conf->chandef.chan->hw_value; - - /* Switch the band (if necessary). */ - err = b43_switch_band(dev, conf->chandef.chan); - if (err) - goto out_mac_enable; - - /* Switch to the requested channel. - * The firmware takes care of races with the TX handler. - */ - b43_switch_channel(dev, phy->channel); - } - - if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) - b43_set_retry_limits(dev, conf->short_frame_max_tx_count, - conf->long_frame_max_tx_count); - changed &= ~IEEE80211_CONF_CHANGE_RETRY_LIMITS; - if (!changed) - goto out_mac_enable; - - dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR); - - /* Adjust the desired TX power level. */ - if (conf->power_level != 0) { - if (conf->power_level != phy->desired_txpower) { - phy->desired_txpower = conf->power_level; - b43_phy_txpower_check(dev, B43_TXPWR_IGNORE_TIME | - B43_TXPWR_IGNORE_TSSI); - } - } - - /* Antennas for RX and management frame TX. */ - antenna = B43_ANTENNA_DEFAULT; - b43_mgmtframe_txantenna(dev, antenna); - antenna = B43_ANTENNA_DEFAULT; - if (phy->ops->set_rx_antenna) - phy->ops->set_rx_antenna(dev, antenna); - - if (wl->radio_enabled != phy->radio_on) { - if (wl->radio_enabled) { - b43_software_rfkill(dev, false); - b43info(dev->wl, "Radio turned on by software\n"); - if (!dev->radio_hw_enable) { - b43info(dev->wl, "The hardware RF-kill button " - "still turns the radio physically off. " - "Press the button to turn it on.\n"); - } - } else { - b43_software_rfkill(dev, true); - b43info(dev->wl, "Radio turned off by software\n"); - } - } - -out_mac_enable: - b43_mac_enable(dev); - mutex_unlock(&wl->mutex); - - return err; -} - -static void b43_update_basic_rates(struct b43_wldev *dev, u32 brates) -{ - struct ieee80211_supported_band *sband = - dev->wl->hw->wiphy->bands[b43_current_band(dev->wl)]; - struct ieee80211_rate *rate; - int i; - u16 basic, direct, offset, basic_offset, rateptr; - - for (i = 0; i < sband->n_bitrates; i++) { - rate = &sband->bitrates[i]; - - if (b43_is_cck_rate(rate->hw_value)) { - direct = B43_SHM_SH_CCKDIRECT; - basic = B43_SHM_SH_CCKBASIC; - offset = b43_plcp_get_ratecode_cck(rate->hw_value); - offset &= 0xF; - } else { - direct = B43_SHM_SH_OFDMDIRECT; - basic = B43_SHM_SH_OFDMBASIC; - offset = b43_plcp_get_ratecode_ofdm(rate->hw_value); - offset &= 0xF; - } - - rate = ieee80211_get_response_rate(sband, brates, rate->bitrate); - - if (b43_is_cck_rate(rate->hw_value)) { - basic_offset = b43_plcp_get_ratecode_cck(rate->hw_value); - basic_offset &= 0xF; - } else { - basic_offset = b43_plcp_get_ratecode_ofdm(rate->hw_value); - basic_offset &= 0xF; - } - - /* - * Get the pointer that we need to point to - * from the direct map - */ - rateptr = b43_shm_read16(dev, B43_SHM_SHARED, - direct + 2 * basic_offset); - /* and write it to the basic map */ - b43_shm_write16(dev, B43_SHM_SHARED, basic + 2 * offset, - rateptr); - } -} - -static void b43_op_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf, - u32 changed) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - - mutex_lock(&wl->mutex); - - dev = wl->current_dev; - if (!dev || b43_status(dev) < B43_STAT_STARTED) - goto out_unlock_mutex; - - B43_WARN_ON(wl->vif != vif); - - if (changed & BSS_CHANGED_BSSID) { - if (conf->bssid) - memcpy(wl->bssid, conf->bssid, ETH_ALEN); - else - eth_zero_addr(wl->bssid); - } - - if (b43_status(dev) >= B43_STAT_INITIALIZED) { - if (changed & BSS_CHANGED_BEACON && - (b43_is_mode(wl, NL80211_IFTYPE_AP) || - b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) || - b43_is_mode(wl, NL80211_IFTYPE_ADHOC))) - b43_update_templates(wl); - - if (changed & BSS_CHANGED_BSSID) - b43_write_mac_bssid_templates(dev); - } - - b43_mac_suspend(dev); - - /* Update templates for AP/mesh mode. */ - if (changed & BSS_CHANGED_BEACON_INT && - (b43_is_mode(wl, NL80211_IFTYPE_AP) || - b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) || - b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) && - conf->beacon_int) - b43_set_beacon_int(dev, conf->beacon_int); - - if (changed & BSS_CHANGED_BASIC_RATES) - b43_update_basic_rates(dev, conf->basic_rates); - - if (changed & BSS_CHANGED_ERP_SLOT) { - if (conf->use_short_slot) - b43_short_slot_timing_enable(dev); - else - b43_short_slot_timing_disable(dev); - } - - b43_mac_enable(dev); -out_unlock_mutex: - mutex_unlock(&wl->mutex); -} - -static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - u8 algorithm; - u8 index; - int err; - static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - - if (modparam_nohwcrypt) - return -ENOSPC; /* User disabled HW-crypto */ - - if ((vif->type == NL80211_IFTYPE_ADHOC || - vif->type == NL80211_IFTYPE_MESH_POINT) && - (key->cipher == WLAN_CIPHER_SUITE_TKIP || - key->cipher == WLAN_CIPHER_SUITE_CCMP) && - !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { - /* - * For now, disable hw crypto for the RSN IBSS group keys. This - * could be optimized in the future, but until that gets - * implemented, use of software crypto for group addressed - * frames is a acceptable to allow RSN IBSS to be used. - */ - return -EOPNOTSUPP; - } - - mutex_lock(&wl->mutex); - - dev = wl->current_dev; - err = -ENODEV; - if (!dev || b43_status(dev) < B43_STAT_INITIALIZED) - goto out_unlock; - - if (dev->fw.pcm_request_failed || !dev->hwcrypto_enabled) { - /* We don't have firmware for the crypto engine. - * Must use software-crypto. */ - err = -EOPNOTSUPP; - goto out_unlock; - } - - err = -EINVAL; - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - algorithm = B43_SEC_ALGO_WEP40; - break; - case WLAN_CIPHER_SUITE_WEP104: - algorithm = B43_SEC_ALGO_WEP104; - break; - case WLAN_CIPHER_SUITE_TKIP: - algorithm = B43_SEC_ALGO_TKIP; - break; - case WLAN_CIPHER_SUITE_CCMP: - algorithm = B43_SEC_ALGO_AES; - break; - default: - B43_WARN_ON(1); - goto out_unlock; - } - index = (u8) (key->keyidx); - if (index > 3) - goto out_unlock; - - switch (cmd) { - case SET_KEY: - if (algorithm == B43_SEC_ALGO_TKIP && - (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) || - !modparam_hwtkip)) { - /* We support only pairwise key */ - err = -EOPNOTSUPP; - goto out_unlock; - } - - if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { - if (WARN_ON(!sta)) { - err = -EOPNOTSUPP; - goto out_unlock; - } - /* Pairwise key with an assigned MAC address. */ - err = b43_key_write(dev, -1, algorithm, - key->key, key->keylen, - sta->addr, key); - } else { - /* Group key */ - err = b43_key_write(dev, index, algorithm, - key->key, key->keylen, NULL, key); - } - if (err) - goto out_unlock; - - if (algorithm == B43_SEC_ALGO_WEP40 || - algorithm == B43_SEC_ALGO_WEP104) { - b43_hf_write(dev, b43_hf_read(dev) | B43_HF_USEDEFKEYS); - } else { - b43_hf_write(dev, - b43_hf_read(dev) & ~B43_HF_USEDEFKEYS); - } - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - if (algorithm == B43_SEC_ALGO_TKIP) - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - break; - case DISABLE_KEY: { - err = b43_key_clear(dev, key->hw_key_idx); - if (err) - goto out_unlock; - break; - } - default: - B43_WARN_ON(1); - } - -out_unlock: - if (!err) { - b43dbg(wl, "%s hardware based encryption for keyidx: %d, " - "mac: %pM\n", - cmd == SET_KEY ? "Using" : "Disabling", key->keyidx, - sta ? sta->addr : bcast_addr); - b43_dump_keymemory(dev); - } - mutex_unlock(&wl->mutex); - - return err; -} - -static void b43_op_configure_filter(struct ieee80211_hw *hw, - unsigned int changed, unsigned int *fflags, - u64 multicast) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (!dev) { - *fflags = 0; - goto out_unlock; - } - - *fflags &= FIF_ALLMULTI | - FIF_FCSFAIL | - FIF_PLCPFAIL | - FIF_CONTROL | - FIF_OTHER_BSS | - FIF_BCN_PRBRESP_PROMISC; - - changed &= FIF_ALLMULTI | - FIF_FCSFAIL | - FIF_PLCPFAIL | - FIF_CONTROL | - FIF_OTHER_BSS | - FIF_BCN_PRBRESP_PROMISC; - - wl->filter_flags = *fflags; - - if (changed && b43_status(dev) >= B43_STAT_INITIALIZED) - b43_adjust_opmode(dev); - -out_unlock: - mutex_unlock(&wl->mutex); -} - -/* Locking: wl->mutex - * Returns the current dev. This might be different from the passed in dev, - * because the core might be gone away while we unlocked the mutex. */ -static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev) -{ - struct b43_wl *wl; - struct b43_wldev *orig_dev; - u32 mask; - int queue_num; - - if (!dev) - return NULL; - wl = dev->wl; -redo: - if (!dev || b43_status(dev) < B43_STAT_STARTED) - return dev; - - /* Cancel work. Unlock to avoid deadlocks. */ - mutex_unlock(&wl->mutex); - cancel_delayed_work_sync(&dev->periodic_work); - cancel_work_sync(&wl->tx_work); - b43_leds_stop(dev); - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (!dev || b43_status(dev) < B43_STAT_STARTED) { - /* Whoops, aliens ate up the device while we were unlocked. */ - return dev; - } - - /* Disable interrupts on the device. */ - b43_set_status(dev, B43_STAT_INITIALIZED); - if (b43_bus_host_is_sdio(dev->dev)) { - /* wl->mutex is locked. That is enough. */ - b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); - b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ - } else { - spin_lock_irq(&wl->hardirq_lock); - b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); - b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ - spin_unlock_irq(&wl->hardirq_lock); - } - /* Synchronize and free the interrupt handlers. Unlock to avoid deadlocks. */ - orig_dev = dev; - mutex_unlock(&wl->mutex); - if (b43_bus_host_is_sdio(dev->dev)) { - b43_sdio_free_irq(dev); - } else { - synchronize_irq(dev->dev->irq); - free_irq(dev->dev->irq, dev); - } - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (!dev) - return dev; - if (dev != orig_dev) { - if (b43_status(dev) >= B43_STAT_STARTED) - goto redo; - return dev; - } - mask = b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); - B43_WARN_ON(mask != 0xFFFFFFFF && mask); - - /* Drain all TX queues. */ - for (queue_num = 0; queue_num < B43_QOS_QUEUE_NUM; queue_num++) { - while (skb_queue_len(&wl->tx_queue[queue_num])) { - struct sk_buff *skb; - - skb = skb_dequeue(&wl->tx_queue[queue_num]); - ieee80211_free_txskb(wl->hw, skb); - } - } - - b43_mac_suspend(dev); - b43_leds_exit(dev); - b43dbg(wl, "Wireless interface stopped\n"); - - return dev; -} - -/* Locking: wl->mutex */ -static int b43_wireless_core_start(struct b43_wldev *dev) -{ - int err; - - B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED); - - drain_txstatus_queue(dev); - if (b43_bus_host_is_sdio(dev->dev)) { - err = b43_sdio_request_irq(dev, b43_sdio_interrupt_handler); - if (err) { - b43err(dev->wl, "Cannot request SDIO IRQ\n"); - goto out; - } - } else { - err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, - b43_interrupt_thread_handler, - IRQF_SHARED, KBUILD_MODNAME, dev); - if (err) { - b43err(dev->wl, "Cannot request IRQ-%d\n", - dev->dev->irq); - goto out; - } - } - - /* We are ready to run. */ - ieee80211_wake_queues(dev->wl->hw); - b43_set_status(dev, B43_STAT_STARTED); - - /* Start data flow (TX/RX). */ - b43_mac_enable(dev); - b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); - - /* Start maintenance work */ - b43_periodic_tasks_setup(dev); - - b43_leds_init(dev); - - b43dbg(dev->wl, "Wireless interface started\n"); -out: - return err; -} - -static char *b43_phy_name(struct b43_wldev *dev, u8 phy_type) -{ - switch (phy_type) { - case B43_PHYTYPE_A: - return "A"; - case B43_PHYTYPE_B: - return "B"; - case B43_PHYTYPE_G: - return "G"; - case B43_PHYTYPE_N: - return "N"; - case B43_PHYTYPE_LP: - return "LP"; - case B43_PHYTYPE_SSLPN: - return "SSLPN"; - case B43_PHYTYPE_HT: - return "HT"; - case B43_PHYTYPE_LCN: - return "LCN"; - case B43_PHYTYPE_LCNXN: - return "LCNXN"; - case B43_PHYTYPE_LCN40: - return "LCN40"; - case B43_PHYTYPE_AC: - return "AC"; - } - return "UNKNOWN"; -} - -/* Get PHY and RADIO versioning numbers */ -static int b43_phy_versioning(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - const u8 core_rev = dev->dev->core_rev; - u32 tmp; - u8 analog_type; - u8 phy_type; - u8 phy_rev; - u16 radio_manuf; - u16 radio_id; - u16 radio_rev; - u8 radio_ver; - int unsupported = 0; - - /* Get PHY versioning */ - tmp = b43_read16(dev, B43_MMIO_PHY_VER); - analog_type = (tmp & B43_PHYVER_ANALOG) >> B43_PHYVER_ANALOG_SHIFT; - phy_type = (tmp & B43_PHYVER_TYPE) >> B43_PHYVER_TYPE_SHIFT; - phy_rev = (tmp & B43_PHYVER_VERSION); - - /* LCNXN is continuation of N which run out of revisions */ - if (phy_type == B43_PHYTYPE_LCNXN) { - phy_type = B43_PHYTYPE_N; - phy_rev += 16; - } - - switch (phy_type) { -#ifdef CONFIG_B43_PHY_G - case B43_PHYTYPE_G: - if (phy_rev > 9) - unsupported = 1; - break; -#endif -#ifdef CONFIG_B43_PHY_N - case B43_PHYTYPE_N: - if (phy_rev >= 19) - unsupported = 1; - break; -#endif -#ifdef CONFIG_B43_PHY_LP - case B43_PHYTYPE_LP: - if (phy_rev > 2) - unsupported = 1; - break; -#endif -#ifdef CONFIG_B43_PHY_HT - case B43_PHYTYPE_HT: - if (phy_rev > 1) - unsupported = 1; - break; -#endif -#ifdef CONFIG_B43_PHY_LCN - case B43_PHYTYPE_LCN: - if (phy_rev > 1) - unsupported = 1; - break; -#endif -#ifdef CONFIG_B43_PHY_AC - case B43_PHYTYPE_AC: - if (phy_rev > 1) - unsupported = 1; - break; -#endif - default: - unsupported = 1; - } - if (unsupported) { - b43err(dev->wl, "FOUND UNSUPPORTED PHY (Analog %u, Type %d (%s), Revision %u)\n", - analog_type, phy_type, b43_phy_name(dev, phy_type), - phy_rev); - return -EOPNOTSUPP; - } - b43info(dev->wl, "Found PHY: Analog %u, Type %d (%s), Revision %u\n", - analog_type, phy_type, b43_phy_name(dev, phy_type), phy_rev); - - /* Get RADIO versioning */ - if (core_rev == 40 || core_rev == 42) { - radio_manuf = 0x17F; - - b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, 0); - radio_rev = b43_read16(dev, B43_MMIO_RADIO24_DATA); - - b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, 1); - radio_id = b43_read16(dev, B43_MMIO_RADIO24_DATA); - - radio_ver = 0; /* Is there version somewhere? */ - } else if (core_rev >= 24) { - u16 radio24[3]; - - for (tmp = 0; tmp < 3; tmp++) { - b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, tmp); - radio24[tmp] = b43_read16(dev, B43_MMIO_RADIO24_DATA); - } - - radio_manuf = 0x17F; - radio_id = (radio24[2] << 8) | radio24[1]; - radio_rev = (radio24[0] & 0xF); - radio_ver = (radio24[0] & 0xF0) >> 4; - } else { - if (dev->dev->chip_id == 0x4317) { - if (dev->dev->chip_rev == 0) - tmp = 0x3205017F; - else if (dev->dev->chip_rev == 1) - tmp = 0x4205017F; - else - tmp = 0x5205017F; - } else { - b43_write16f(dev, B43_MMIO_RADIO_CONTROL, - B43_RADIOCTL_ID); - tmp = b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); - b43_write16f(dev, B43_MMIO_RADIO_CONTROL, - B43_RADIOCTL_ID); - tmp |= b43_read16(dev, B43_MMIO_RADIO_DATA_HIGH) << 16; - } - radio_manuf = (tmp & 0x00000FFF); - radio_id = (tmp & 0x0FFFF000) >> 12; - radio_rev = (tmp & 0xF0000000) >> 28; - radio_ver = 0; /* Probably not available on old hw */ - } - - if (radio_manuf != 0x17F /* Broadcom */) - unsupported = 1; - switch (phy_type) { - case B43_PHYTYPE_A: - if (radio_id != 0x2060) - unsupported = 1; - if (radio_rev != 1) - unsupported = 1; - if (radio_manuf != 0x17F) - unsupported = 1; - break; - case B43_PHYTYPE_B: - if ((radio_id & 0xFFF0) != 0x2050) - unsupported = 1; - break; - case B43_PHYTYPE_G: - if (radio_id != 0x2050) - unsupported = 1; - break; - case B43_PHYTYPE_N: - if (radio_id != 0x2055 && radio_id != 0x2056 && - radio_id != 0x2057) - unsupported = 1; - if (radio_id == 0x2057 && - !(radio_rev == 9 || radio_rev == 14)) - unsupported = 1; - break; - case B43_PHYTYPE_LP: - if (radio_id != 0x2062 && radio_id != 0x2063) - unsupported = 1; - break; - case B43_PHYTYPE_HT: - if (radio_id != 0x2059) - unsupported = 1; - break; - case B43_PHYTYPE_LCN: - if (radio_id != 0x2064) - unsupported = 1; - break; - case B43_PHYTYPE_AC: - if (radio_id != 0x2069) - unsupported = 1; - break; - default: - B43_WARN_ON(1); - } - if (unsupported) { - b43err(dev->wl, - "FOUND UNSUPPORTED RADIO (Manuf 0x%X, ID 0x%X, Revision %u, Version %u)\n", - radio_manuf, radio_id, radio_rev, radio_ver); - return -EOPNOTSUPP; - } - b43info(dev->wl, - "Found Radio: Manuf 0x%X, ID 0x%X, Revision %u, Version %u\n", - radio_manuf, radio_id, radio_rev, radio_ver); - - /* FIXME: b43 treats "id" as "ver" and ignores the real "ver" */ - phy->radio_manuf = radio_manuf; - phy->radio_ver = radio_id; - phy->radio_rev = radio_rev; - - phy->analog = analog_type; - phy->type = phy_type; - phy->rev = phy_rev; - - return 0; -} - -static void setup_struct_phy_for_init(struct b43_wldev *dev, - struct b43_phy *phy) -{ - phy->hardware_power_control = !!modparam_hwpctl; - phy->next_txpwr_check_time = jiffies; - /* PHY TX errors counter. */ - atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); - -#if B43_DEBUG - phy->phy_locked = false; - phy->radio_locked = false; -#endif -} - -static void setup_struct_wldev_for_init(struct b43_wldev *dev) -{ - dev->dfq_valid = false; - - /* Assume the radio is enabled. If it's not enabled, the state will - * immediately get fixed on the first periodic work run. */ - dev->radio_hw_enable = true; - - /* Stats */ - memset(&dev->stats, 0, sizeof(dev->stats)); - - setup_struct_phy_for_init(dev, &dev->phy); - - /* IRQ related flags */ - dev->irq_reason = 0; - memset(dev->dma_reason, 0, sizeof(dev->dma_reason)); - dev->irq_mask = B43_IRQ_MASKTEMPLATE; - if (b43_modparam_verbose < B43_VERBOSITY_DEBUG) - dev->irq_mask &= ~B43_IRQ_PHY_TXERR; - - dev->mac_suspended = 1; - - /* Noise calculation context */ - memset(&dev->noisecalc, 0, sizeof(dev->noisecalc)); -} - -static void b43_bluetooth_coext_enable(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - u64 hf; - - if (!modparam_btcoex) - return; - if (!(sprom->boardflags_lo & B43_BFL_BTCOEXIST)) - return; - if (dev->phy.type != B43_PHYTYPE_B && !dev->phy.gmode) - return; - - hf = b43_hf_read(dev); - if (sprom->boardflags_lo & B43_BFL_BTCMOD) - hf |= B43_HF_BTCOEXALT; - else - hf |= B43_HF_BTCOEX; - b43_hf_write(dev, hf); -} - -static void b43_bluetooth_coext_disable(struct b43_wldev *dev) -{ - if (!modparam_btcoex) - return; - //TODO -} - -static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev) -{ - struct ssb_bus *bus; - u32 tmp; - -#ifdef CONFIG_B43_SSB - if (dev->dev->bus_type != B43_BUS_SSB) - return; -#else - return; -#endif - - bus = dev->dev->sdev->bus; - - if ((bus->chip_id == 0x4311 && bus->chip_rev == 2) || - (bus->chip_id == 0x4312)) { - tmp = ssb_read32(dev->dev->sdev, SSB_IMCFGLO); - tmp &= ~SSB_IMCFGLO_REQTO; - tmp &= ~SSB_IMCFGLO_SERTO; - tmp |= 0x3; - ssb_write32(dev->dev->sdev, SSB_IMCFGLO, tmp); - ssb_commit_settings(bus); - } -} - -static void b43_set_synth_pu_delay(struct b43_wldev *dev, bool idle) -{ - u16 pu_delay; - - /* The time value is in microseconds. */ - if (dev->phy.type == B43_PHYTYPE_A) - pu_delay = 3700; - else - pu_delay = 1050; - if (b43_is_mode(dev->wl, NL80211_IFTYPE_ADHOC) || idle) - pu_delay = 500; - if ((dev->phy.radio_ver == 0x2050) && (dev->phy.radio_rev == 8)) - pu_delay = max(pu_delay, (u16)2400); - - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_SPUWKUP, pu_delay); -} - -/* Set the TSF CFP pre-TargetBeaconTransmissionTime. */ -static void b43_set_pretbtt(struct b43_wldev *dev) -{ - u16 pretbtt; - - /* The time value is in microseconds. */ - if (b43_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) { - pretbtt = 2; - } else { - if (dev->phy.type == B43_PHYTYPE_A) - pretbtt = 120; - else - pretbtt = 250; - } - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRETBTT, pretbtt); - b43_write16(dev, B43_MMIO_TSF_CFP_PRETBTT, pretbtt); -} - -/* Shutdown a wireless core */ -/* Locking: wl->mutex */ -static void b43_wireless_core_exit(struct b43_wldev *dev) -{ - B43_WARN_ON(dev && b43_status(dev) > B43_STAT_INITIALIZED); - if (!dev || b43_status(dev) != B43_STAT_INITIALIZED) - return; - - b43_set_status(dev, B43_STAT_UNINIT); - - /* Stop the microcode PSM. */ - b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN, - B43_MACCTL_PSM_JMP0); - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - bcma_host_pci_down(dev->dev->bdev->bus); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - /* TODO */ - break; -#endif - } - - b43_dma_free(dev); - b43_pio_free(dev); - b43_chip_exit(dev); - dev->phy.ops->switch_analog(dev, 0); - if (dev->wl->current_beacon) { - dev_kfree_skb_any(dev->wl->current_beacon); - dev->wl->current_beacon = NULL; - } - - b43_device_disable(dev, 0); - b43_bus_may_powerdown(dev); -} - -/* Initialize a wireless core */ -static int b43_wireless_core_init(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - struct b43_phy *phy = &dev->phy; - int err; - u64 hf; - - B43_WARN_ON(b43_status(dev) != B43_STAT_UNINIT); - - err = b43_bus_powerup(dev, 0); - if (err) - goto out; - if (!b43_device_is_enabled(dev)) - b43_wireless_core_reset(dev, phy->gmode); - - /* Reset all data structures. */ - setup_struct_wldev_for_init(dev); - phy->ops->prepare_structs(dev); - - /* Enable IRQ routing to this device. */ - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - bcma_host_pci_irq_ctl(dev->dev->bdev->bus, - dev->dev->bdev, true); - bcma_host_pci_up(dev->dev->bdev->bus); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - ssb_pcicore_dev_irqvecs_enable(&dev->dev->sdev->bus->pcicore, - dev->dev->sdev); - break; -#endif - } - - b43_imcfglo_timeouts_workaround(dev); - b43_bluetooth_coext_disable(dev); - if (phy->ops->prepare_hardware) { - err = phy->ops->prepare_hardware(dev); - if (err) - goto err_busdown; - } - err = b43_chip_init(dev); - if (err) - goto err_busdown; - b43_shm_write16(dev, B43_SHM_SHARED, - B43_SHM_SH_WLCOREREV, dev->dev->core_rev); - hf = b43_hf_read(dev); - if (phy->type == B43_PHYTYPE_G) { - hf |= B43_HF_SYMW; - if (phy->rev == 1) - hf |= B43_HF_GDCW; - if (sprom->boardflags_lo & B43_BFL_PACTRL) - hf |= B43_HF_OFDMPABOOST; - } - if (phy->radio_ver == 0x2050) { - if (phy->radio_rev == 6) - hf |= B43_HF_4318TSSI; - if (phy->radio_rev < 6) - hf |= B43_HF_VCORECALC; - } - if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW) - hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */ -#if defined(CONFIG_B43_SSB) && defined(CONFIG_SSB_DRIVER_PCICORE) - if (dev->dev->bus_type == B43_BUS_SSB && - dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI && - dev->dev->sdev->bus->pcicore.dev->id.revision <= 10) - hf |= B43_HF_PCISCW; /* PCI slow clock workaround. */ -#endif - hf &= ~B43_HF_SKCFPUP; - b43_hf_write(dev, hf); - - /* tell the ucode MAC capabilities */ - if (dev->dev->core_rev >= 13) { - u32 mac_hw_cap = b43_read32(dev, B43_MMIO_MAC_HW_CAP); - - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_MACHW_L, - mac_hw_cap & 0xffff); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_MACHW_H, - (mac_hw_cap >> 16) & 0xffff); - } - - b43_set_retry_limits(dev, B43_DEFAULT_SHORT_RETRY_LIMIT, - B43_DEFAULT_LONG_RETRY_LIMIT); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_SFFBLIM, 3); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_LFFBLIM, 2); - - /* Disable sending probe responses from firmware. - * Setting the MaxTime to one usec will always trigger - * a timeout, so we never send any probe resp. - * A timeout of zero is infinite. */ - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRMAXTIME, 1); - - b43_rate_memory_init(dev); - b43_set_phytxctl_defaults(dev); - - /* Minimum Contention Window */ - if (phy->type == B43_PHYTYPE_B) - b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0x1F); - else - b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0xF); - /* Maximum Contention Window */ - b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF); - - /* write phytype and phyvers */ - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PHYTYPE, phy->type); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PHYVER, phy->rev); - - if (b43_bus_host_is_pcmcia(dev->dev) || - b43_bus_host_is_sdio(dev->dev)) { - dev->__using_pio_transfers = true; - err = b43_pio_init(dev); - } else if (dev->use_pio) { - b43warn(dev->wl, "Forced PIO by use_pio module parameter. " - "This should not be needed and will result in lower " - "performance.\n"); - dev->__using_pio_transfers = true; - err = b43_pio_init(dev); - } else { - dev->__using_pio_transfers = false; - err = b43_dma_init(dev); - } - if (err) - goto err_chip_exit; - b43_qos_init(dev); - b43_set_synth_pu_delay(dev, 1); - b43_bluetooth_coext_enable(dev); - - b43_bus_powerup(dev, !(sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)); - b43_upload_card_macaddress(dev); - b43_security_init(dev); - - ieee80211_wake_queues(dev->wl->hw); - - b43_set_status(dev, B43_STAT_INITIALIZED); - -out: - return err; - -err_chip_exit: - b43_chip_exit(dev); -err_busdown: - b43_bus_may_powerdown(dev); - B43_WARN_ON(b43_status(dev) != B43_STAT_UNINIT); - return err; -} - -static int b43_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - int err = -EOPNOTSUPP; - - /* TODO: allow WDS/AP devices to coexist */ - - if (vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_MESH_POINT && - vif->type != NL80211_IFTYPE_STATION && - vif->type != NL80211_IFTYPE_WDS && - vif->type != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - mutex_lock(&wl->mutex); - if (wl->operating) - goto out_mutex_unlock; - - b43dbg(wl, "Adding Interface type %d\n", vif->type); - - dev = wl->current_dev; - wl->operating = true; - wl->vif = vif; - wl->if_type = vif->type; - memcpy(wl->mac_addr, vif->addr, ETH_ALEN); - - b43_adjust_opmode(dev); - b43_set_pretbtt(dev); - b43_set_synth_pu_delay(dev, 0); - b43_upload_card_macaddress(dev); - - err = 0; - out_mutex_unlock: - mutex_unlock(&wl->mutex); - - if (err == 0) - b43_op_bss_info_changed(hw, vif, &vif->bss_conf, ~0); - - return err; -} - -static void b43_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev = wl->current_dev; - - b43dbg(wl, "Removing Interface type %d\n", vif->type); - - mutex_lock(&wl->mutex); - - B43_WARN_ON(!wl->operating); - B43_WARN_ON(wl->vif != vif); - wl->vif = NULL; - - wl->operating = false; - - b43_adjust_opmode(dev); - eth_zero_addr(wl->mac_addr); - b43_upload_card_macaddress(dev); - - mutex_unlock(&wl->mutex); -} - -static int b43_op_start(struct ieee80211_hw *hw) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev = wl->current_dev; - int did_init = 0; - int err = 0; - - /* Kill all old instance specific information to make sure - * the card won't use it in the short timeframe between start - * and mac80211 reconfiguring it. */ - eth_zero_addr(wl->bssid); - eth_zero_addr(wl->mac_addr); - wl->filter_flags = 0; - wl->radiotap_enabled = false; - b43_qos_clear(wl); - wl->beacon0_uploaded = false; - wl->beacon1_uploaded = false; - wl->beacon_templates_virgin = true; - wl->radio_enabled = true; - - mutex_lock(&wl->mutex); - - if (b43_status(dev) < B43_STAT_INITIALIZED) { - err = b43_wireless_core_init(dev); - if (err) - goto out_mutex_unlock; - did_init = 1; - } - - if (b43_status(dev) < B43_STAT_STARTED) { - err = b43_wireless_core_start(dev); - if (err) { - if (did_init) - b43_wireless_core_exit(dev); - goto out_mutex_unlock; - } - } - - /* XXX: only do if device doesn't support rfkill irq */ - wiphy_rfkill_start_polling(hw->wiphy); - - out_mutex_unlock: - mutex_unlock(&wl->mutex); - - /* - * Configuration may have been overwritten during initialization. - * Reload the configuration, but only if initialization was - * successful. Reloading the configuration after a failed init - * may hang the system. - */ - if (!err) - b43_op_config(hw, ~0); - - return err; -} - -static void b43_op_stop(struct ieee80211_hw *hw) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev = wl->current_dev; - - cancel_work_sync(&(wl->beacon_update_trigger)); - - if (!dev) - goto out; - - mutex_lock(&wl->mutex); - if (b43_status(dev) >= B43_STAT_STARTED) { - dev = b43_wireless_core_stop(dev); - if (!dev) - goto out_unlock; - } - b43_wireless_core_exit(dev); - wl->radio_enabled = false; - -out_unlock: - mutex_unlock(&wl->mutex); -out: - cancel_work_sync(&(wl->txpower_adjust_work)); -} - -static int b43_op_beacon_set_tim(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, bool set) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - - b43_update_templates(wl); - - return 0; -} - -static void b43_op_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd notify_cmd, - struct ieee80211_sta *sta) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - - B43_WARN_ON(!vif || wl->vif != vif); -} - -static void b43_op_sw_scan_start_notifier(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const u8 *mac_addr) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) { - /* Disable CFP update during scan on other channels. */ - b43_hf_write(dev, b43_hf_read(dev) | B43_HF_SKCFPUP); - } - mutex_unlock(&wl->mutex); -} - -static void b43_op_sw_scan_complete_notifier(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) { - /* Re-enable CFP update. */ - b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_SKCFPUP); - } - mutex_unlock(&wl->mutex); -} - -static int b43_op_get_survey(struct ieee80211_hw *hw, int idx, - struct survey_info *survey) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev = wl->current_dev; - struct ieee80211_conf *conf = &hw->conf; - - if (idx != 0) - return -ENOENT; - - survey->channel = conf->chandef.chan; - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = dev->stats.link_noise; - - return 0; -} - -static const struct ieee80211_ops b43_hw_ops = { - .tx = b43_op_tx, - .conf_tx = b43_op_conf_tx, - .add_interface = b43_op_add_interface, - .remove_interface = b43_op_remove_interface, - .config = b43_op_config, - .bss_info_changed = b43_op_bss_info_changed, - .configure_filter = b43_op_configure_filter, - .set_key = b43_op_set_key, - .update_tkip_key = b43_op_update_tkip_key, - .get_stats = b43_op_get_stats, - .get_tsf = b43_op_get_tsf, - .set_tsf = b43_op_set_tsf, - .start = b43_op_start, - .stop = b43_op_stop, - .set_tim = b43_op_beacon_set_tim, - .sta_notify = b43_op_sta_notify, - .sw_scan_start = b43_op_sw_scan_start_notifier, - .sw_scan_complete = b43_op_sw_scan_complete_notifier, - .get_survey = b43_op_get_survey, - .rfkill_poll = b43_rfkill_poll, -}; - -/* Hard-reset the chip. Do not call this directly. - * Use b43_controller_restart() - */ -static void b43_chip_reset(struct work_struct *work) -{ - struct b43_wldev *dev = - container_of(work, struct b43_wldev, restart_work); - struct b43_wl *wl = dev->wl; - int err = 0; - int prev_status; - - mutex_lock(&wl->mutex); - - prev_status = b43_status(dev); - /* Bring the device down... */ - if (prev_status >= B43_STAT_STARTED) { - dev = b43_wireless_core_stop(dev); - if (!dev) { - err = -ENODEV; - goto out; - } - } - if (prev_status >= B43_STAT_INITIALIZED) - b43_wireless_core_exit(dev); - - /* ...and up again. */ - if (prev_status >= B43_STAT_INITIALIZED) { - err = b43_wireless_core_init(dev); - if (err) - goto out; - } - if (prev_status >= B43_STAT_STARTED) { - err = b43_wireless_core_start(dev); - if (err) { - b43_wireless_core_exit(dev); - goto out; - } - } -out: - if (err) - wl->current_dev = NULL; /* Failed to init the dev. */ - mutex_unlock(&wl->mutex); - - if (err) { - b43err(wl, "Controller restart FAILED\n"); - return; - } - - /* reload configuration */ - b43_op_config(wl->hw, ~0); - if (wl->vif) - b43_op_bss_info_changed(wl->hw, wl->vif, &wl->vif->bss_conf, ~0); - - b43info(wl, "Controller restarted\n"); -} - -static int b43_setup_bands(struct b43_wldev *dev, - bool have_2ghz_phy, bool have_5ghz_phy) -{ - struct ieee80211_hw *hw = dev->wl->hw; - struct b43_phy *phy = &dev->phy; - bool limited_2g; - bool limited_5g; - - /* We don't support all 2 GHz channels on some devices */ - limited_2g = phy->radio_ver == 0x2057 && - (phy->radio_rev == 9 || phy->radio_rev == 14); - limited_5g = phy->radio_ver == 0x2057 && - phy->radio_rev == 9; - - if (have_2ghz_phy) - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = limited_2g ? - &b43_band_2ghz_limited : &b43_band_2GHz; - if (dev->phy.type == B43_PHYTYPE_N) { - if (have_5ghz_phy) - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = limited_5g ? - &b43_band_5GHz_nphy_limited : - &b43_band_5GHz_nphy; - } else { - if (have_5ghz_phy) - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_aphy; - } - - dev->phy.supports_2ghz = have_2ghz_phy; - dev->phy.supports_5ghz = have_5ghz_phy; - - return 0; -} - -static void b43_wireless_core_detach(struct b43_wldev *dev) -{ - /* We release firmware that late to not be required to re-request - * is all the time when we reinit the core. */ - b43_release_firmware(dev); - b43_phy_free(dev); -} - -static void b43_supported_bands(struct b43_wldev *dev, bool *have_2ghz_phy, - bool *have_5ghz_phy) -{ - u16 dev_id = 0; - -#ifdef CONFIG_B43_BCMA - if (dev->dev->bus_type == B43_BUS_BCMA && - dev->dev->bdev->bus->hosttype == BCMA_HOSTTYPE_PCI) - dev_id = dev->dev->bdev->bus->host_pci->device; -#endif -#ifdef CONFIG_B43_SSB - if (dev->dev->bus_type == B43_BUS_SSB && - dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI) - dev_id = dev->dev->sdev->bus->host_pci->device; -#endif - /* Override with SPROM value if available */ - if (dev->dev->bus_sprom->dev_id) - dev_id = dev->dev->bus_sprom->dev_id; - - /* Note: below IDs can be "virtual" (not maching e.g. real PCI ID) */ - switch (dev_id) { - case 0x4324: /* BCM4306 */ - case 0x4312: /* BCM4311 */ - case 0x4319: /* BCM4318 */ - case 0x4328: /* BCM4321 */ - case 0x432b: /* BCM4322 */ - case 0x4350: /* BCM43222 */ - case 0x4353: /* BCM43224 */ - case 0x0576: /* BCM43224 */ - case 0x435f: /* BCM6362 */ - case 0x4331: /* BCM4331 */ - case 0x4359: /* BCM43228 */ - case 0x43a0: /* BCM4360 */ - case 0x43b1: /* BCM4352 */ - /* Dual band devices */ - *have_2ghz_phy = true; - *have_5ghz_phy = true; - return; - case 0x4321: /* BCM4306 */ - /* There are 14e4:4321 PCI devs with 2.4 GHz BCM4321 (N-PHY) */ - if (dev->phy.type != B43_PHYTYPE_G) - break; - /* fall through */ - case 0x4313: /* BCM4311 */ - case 0x431a: /* BCM4318 */ - case 0x432a: /* BCM4321 */ - case 0x432d: /* BCM4322 */ - case 0x4352: /* BCM43222 */ - case 0x435a: /* BCM43228 */ - case 0x4333: /* BCM4331 */ - case 0x43a2: /* BCM4360 */ - case 0x43b3: /* BCM4352 */ - /* 5 GHz only devices */ - *have_2ghz_phy = false; - *have_5ghz_phy = true; - return; - } - - /* As a fallback, try to guess using PHY type */ - switch (dev->phy.type) { - case B43_PHYTYPE_A: - *have_2ghz_phy = false; - *have_5ghz_phy = true; - return; - case B43_PHYTYPE_G: - case B43_PHYTYPE_N: - case B43_PHYTYPE_LP: - case B43_PHYTYPE_HT: - case B43_PHYTYPE_LCN: - *have_2ghz_phy = true; - *have_5ghz_phy = false; - return; - } - - B43_WARN_ON(1); -} - -static int b43_wireless_core_attach(struct b43_wldev *dev) -{ - struct b43_wl *wl = dev->wl; - struct b43_phy *phy = &dev->phy; - int err; - u32 tmp; - bool have_2ghz_phy = false, have_5ghz_phy = false; - - /* Do NOT do any device initialization here. - * Do it in wireless_core_init() instead. - * This function is for gathering basic information about the HW, only. - * Also some structs may be set up here. But most likely you want to have - * that in core_init(), too. - */ - - err = b43_bus_powerup(dev, 0); - if (err) { - b43err(wl, "Bus powerup failed\n"); - goto out; - } - - phy->do_full_init = true; - - /* Try to guess supported bands for the first init needs */ - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOST); - have_2ghz_phy = !!(tmp & B43_BCMA_IOST_2G_PHY); - have_5ghz_phy = !!(tmp & B43_BCMA_IOST_5G_PHY); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - if (dev->dev->core_rev >= 5) { - tmp = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); - have_2ghz_phy = !!(tmp & B43_TMSHIGH_HAVE_2GHZ_PHY); - have_5ghz_phy = !!(tmp & B43_TMSHIGH_HAVE_5GHZ_PHY); - } else - B43_WARN_ON(1); - break; -#endif - } - - dev->phy.gmode = have_2ghz_phy; - b43_wireless_core_reset(dev, dev->phy.gmode); - - /* Get the PHY type. */ - err = b43_phy_versioning(dev); - if (err) - goto err_powerdown; - - /* Get real info about supported bands */ - b43_supported_bands(dev, &have_2ghz_phy, &have_5ghz_phy); - - /* We don't support 5 GHz on some PHYs yet */ - if (have_5ghz_phy) { - switch (dev->phy.type) { - case B43_PHYTYPE_A: - case B43_PHYTYPE_G: - case B43_PHYTYPE_LP: - case B43_PHYTYPE_HT: - b43warn(wl, "5 GHz band is unsupported on this PHY\n"); - have_5ghz_phy = false; - } - } - - if (!have_2ghz_phy && !have_5ghz_phy) { - b43err(wl, "b43 can't support any band on this device\n"); - err = -EOPNOTSUPP; - goto err_powerdown; - } - - err = b43_phy_allocate(dev); - if (err) - goto err_powerdown; - - dev->phy.gmode = have_2ghz_phy; - b43_wireless_core_reset(dev, dev->phy.gmode); - - err = b43_validate_chipaccess(dev); - if (err) - goto err_phy_free; - err = b43_setup_bands(dev, have_2ghz_phy, have_5ghz_phy); - if (err) - goto err_phy_free; - - /* Now set some default "current_dev" */ - if (!wl->current_dev) - wl->current_dev = dev; - INIT_WORK(&dev->restart_work, b43_chip_reset); - - dev->phy.ops->switch_analog(dev, 0); - b43_device_disable(dev, 0); - b43_bus_may_powerdown(dev); - -out: - return err; - -err_phy_free: - b43_phy_free(dev); -err_powerdown: - b43_bus_may_powerdown(dev); - return err; -} - -static void b43_one_core_detach(struct b43_bus_dev *dev) -{ - struct b43_wldev *wldev; - struct b43_wl *wl; - - /* Do not cancel ieee80211-workqueue based work here. - * See comment in b43_remove(). */ - - wldev = b43_bus_get_wldev(dev); - wl = wldev->wl; - b43_debugfs_remove_device(wldev); - b43_wireless_core_detach(wldev); - list_del(&wldev->list); - b43_bus_set_wldev(dev, NULL); - kfree(wldev); -} - -static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl) -{ - struct b43_wldev *wldev; - int err = -ENOMEM; - - wldev = kzalloc(sizeof(*wldev), GFP_KERNEL); - if (!wldev) - goto out; - - wldev->use_pio = b43_modparam_pio; - wldev->dev = dev; - wldev->wl = wl; - b43_set_status(wldev, B43_STAT_UNINIT); - wldev->bad_frames_preempt = modparam_bad_frames_preempt; - INIT_LIST_HEAD(&wldev->list); - - err = b43_wireless_core_attach(wldev); - if (err) - goto err_kfree_wldev; - - b43_bus_set_wldev(dev, wldev); - b43_debugfs_add_device(wldev); - - out: - return err; - - err_kfree_wldev: - kfree(wldev); - return err; -} - -#define IS_PDEV(pdev, _vendor, _device, _subvendor, _subdevice) ( \ - (pdev->vendor == PCI_VENDOR_ID_##_vendor) && \ - (pdev->device == _device) && \ - (pdev->subsystem_vendor == PCI_VENDOR_ID_##_subvendor) && \ - (pdev->subsystem_device == _subdevice) ) - -#ifdef CONFIG_B43_SSB -static void b43_sprom_fixup(struct ssb_bus *bus) -{ - struct pci_dev *pdev; - - /* boardflags workarounds */ - if (bus->boardinfo.vendor == SSB_BOARDVENDOR_DELL && - bus->chip_id == 0x4301 && bus->sprom.board_rev == 0x74) - bus->sprom.boardflags_lo |= B43_BFL_BTCOEXIST; - if (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE && - bus->boardinfo.type == 0x4E && bus->sprom.board_rev > 0x40) - bus->sprom.boardflags_lo |= B43_BFL_PACTRL; - if (bus->bustype == SSB_BUSTYPE_PCI) { - pdev = bus->host_pci; - if (IS_PDEV(pdev, BROADCOM, 0x4318, ASUSTEK, 0x100F) || - IS_PDEV(pdev, BROADCOM, 0x4320, DELL, 0x0003) || - IS_PDEV(pdev, BROADCOM, 0x4320, HP, 0x12f8) || - IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0015) || - IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0014) || - IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0013) || - IS_PDEV(pdev, BROADCOM, 0x4320, MOTOROLA, 0x7010)) - bus->sprom.boardflags_lo &= ~B43_BFL_BTCOEXIST; - } -} - -static void b43_wireless_exit(struct b43_bus_dev *dev, struct b43_wl *wl) -{ - struct ieee80211_hw *hw = wl->hw; - - ssb_set_devtypedata(dev->sdev, NULL); - ieee80211_free_hw(hw); -} -#endif - -static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) -{ - struct ssb_sprom *sprom = dev->bus_sprom; - struct ieee80211_hw *hw; - struct b43_wl *wl; - char chip_name[6]; - int queue_num; - - hw = ieee80211_alloc_hw(sizeof(*wl), &b43_hw_ops); - if (!hw) { - b43err(NULL, "Could not allocate ieee80211 device\n"); - return ERR_PTR(-ENOMEM); - } - wl = hw_to_b43_wl(hw); - - /* fill hw info */ - ieee80211_hw_set(hw, RX_INCLUDES_FCS); - ieee80211_hw_set(hw, SIGNAL_DBM); - - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT) | - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_WDS) | - BIT(NL80211_IFTYPE_ADHOC); - - hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - - wl->hw_registred = false; - hw->max_rates = 2; - SET_IEEE80211_DEV(hw, dev->dev); - if (is_valid_ether_addr(sprom->et1mac)) - SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac); - else - SET_IEEE80211_PERM_ADDR(hw, sprom->il0mac); - - /* Initialize struct b43_wl */ - wl->hw = hw; - mutex_init(&wl->mutex); - spin_lock_init(&wl->hardirq_lock); - spin_lock_init(&wl->beacon_lock); - INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work); - INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work); - INIT_WORK(&wl->tx_work, b43_tx_work); - - /* Initialize queues and flags. */ - for (queue_num = 0; queue_num < B43_QOS_QUEUE_NUM; queue_num++) { - skb_queue_head_init(&wl->tx_queue[queue_num]); - wl->tx_queue_stopped[queue_num] = 0; - } - - snprintf(chip_name, ARRAY_SIZE(chip_name), - (dev->chip_id > 0x9999) ? "%d" : "%04X", dev->chip_id); - b43info(wl, "Broadcom %s WLAN found (core revision %u)\n", chip_name, - dev->core_rev); - return wl; -} - -#ifdef CONFIG_B43_BCMA -static int b43_bcma_probe(struct bcma_device *core) -{ - struct b43_bus_dev *dev; - struct b43_wl *wl; - int err; - - if (!modparam_allhwsupport && - (core->id.rev == 0x17 || core->id.rev == 0x18)) { - pr_err("Support for cores revisions 0x17 and 0x18 disabled by module param allhwsupport=0. Try b43.allhwsupport=1\n"); - return -ENOTSUPP; - } - - dev = b43_bus_dev_bcma_init(core); - if (!dev) - return -ENODEV; - - wl = b43_wireless_init(dev); - if (IS_ERR(wl)) { - err = PTR_ERR(wl); - goto bcma_out; - } - - err = b43_one_core_attach(dev, wl); - if (err) - goto bcma_err_wireless_exit; - - /* setup and start work to load firmware */ - INIT_WORK(&wl->firmware_load, b43_request_firmware); - schedule_work(&wl->firmware_load); - -bcma_out: - return err; - -bcma_err_wireless_exit: - ieee80211_free_hw(wl->hw); - return err; -} - -static void b43_bcma_remove(struct bcma_device *core) -{ - struct b43_wldev *wldev = bcma_get_drvdata(core); - struct b43_wl *wl = wldev->wl; - - /* We must cancel any work here before unregistering from ieee80211, - * as the ieee80211 unreg will destroy the workqueue. */ - cancel_work_sync(&wldev->restart_work); - cancel_work_sync(&wl->firmware_load); - - B43_WARN_ON(!wl); - if (!wldev->fw.ucode.data) - return; /* NULL if firmware never loaded */ - if (wl->current_dev == wldev && wl->hw_registred) { - b43_leds_stop(wldev); - ieee80211_unregister_hw(wl->hw); - } - - b43_one_core_detach(wldev->dev); - - /* Unregister HW RNG driver */ - b43_rng_exit(wl); - - b43_leds_unregister(wl); - - ieee80211_free_hw(wl->hw); -} - -static struct bcma_driver b43_bcma_driver = { - .name = KBUILD_MODNAME, - .id_table = b43_bcma_tbl, - .probe = b43_bcma_probe, - .remove = b43_bcma_remove, -}; -#endif - -#ifdef CONFIG_B43_SSB -static -int b43_ssb_probe(struct ssb_device *sdev, const struct ssb_device_id *id) -{ - struct b43_bus_dev *dev; - struct b43_wl *wl; - int err; - - dev = b43_bus_dev_ssb_init(sdev); - if (!dev) - return -ENOMEM; - - wl = ssb_get_devtypedata(sdev); - if (wl) { - b43err(NULL, "Dual-core devices are not supported\n"); - err = -ENOTSUPP; - goto err_ssb_kfree_dev; - } - - b43_sprom_fixup(sdev->bus); - - wl = b43_wireless_init(dev); - if (IS_ERR(wl)) { - err = PTR_ERR(wl); - goto err_ssb_kfree_dev; - } - ssb_set_devtypedata(sdev, wl); - B43_WARN_ON(ssb_get_devtypedata(sdev) != wl); - - err = b43_one_core_attach(dev, wl); - if (err) - goto err_ssb_wireless_exit; - - /* setup and start work to load firmware */ - INIT_WORK(&wl->firmware_load, b43_request_firmware); - schedule_work(&wl->firmware_load); - - return err; - -err_ssb_wireless_exit: - b43_wireless_exit(dev, wl); -err_ssb_kfree_dev: - kfree(dev); - return err; -} - -static void b43_ssb_remove(struct ssb_device *sdev) -{ - struct b43_wl *wl = ssb_get_devtypedata(sdev); - struct b43_wldev *wldev = ssb_get_drvdata(sdev); - struct b43_bus_dev *dev = wldev->dev; - - /* We must cancel any work here before unregistering from ieee80211, - * as the ieee80211 unreg will destroy the workqueue. */ - cancel_work_sync(&wldev->restart_work); - cancel_work_sync(&wl->firmware_load); - - B43_WARN_ON(!wl); - if (!wldev->fw.ucode.data) - return; /* NULL if firmware never loaded */ - if (wl->current_dev == wldev && wl->hw_registred) { - b43_leds_stop(wldev); - ieee80211_unregister_hw(wl->hw); - } - - b43_one_core_detach(dev); - - /* Unregister HW RNG driver */ - b43_rng_exit(wl); - - b43_leds_unregister(wl); - b43_wireless_exit(dev, wl); -} - -static struct ssb_driver b43_ssb_driver = { - .name = KBUILD_MODNAME, - .id_table = b43_ssb_tbl, - .probe = b43_ssb_probe, - .remove = b43_ssb_remove, -}; -#endif /* CONFIG_B43_SSB */ - -/* Perform a hardware reset. This can be called from any context. */ -void b43_controller_restart(struct b43_wldev *dev, const char *reason) -{ - /* Must avoid requeueing, if we are in shutdown. */ - if (b43_status(dev) < B43_STAT_INITIALIZED) - return; - b43info(dev->wl, "Controller RESET (%s) ...\n", reason); - ieee80211_queue_work(dev->wl->hw, &dev->restart_work); -} - -static void b43_print_driverinfo(void) -{ - const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "", - *feat_leds = "", *feat_sdio = ""; - -#ifdef CONFIG_B43_PCI_AUTOSELECT - feat_pci = "P"; -#endif -#ifdef CONFIG_B43_PCMCIA - feat_pcmcia = "M"; -#endif -#ifdef CONFIG_B43_PHY_N - feat_nphy = "N"; -#endif -#ifdef CONFIG_B43_LEDS - feat_leds = "L"; -#endif -#ifdef CONFIG_B43_SDIO - feat_sdio = "S"; -#endif - printk(KERN_INFO "Broadcom 43xx driver loaded " - "[ Features: %s%s%s%s%s ]\n", - feat_pci, feat_pcmcia, feat_nphy, - feat_leds, feat_sdio); -} - -static int __init b43_init(void) -{ - int err; - - b43_debugfs_init(); - err = b43_sdio_init(); - if (err) - goto err_dfs_exit; -#ifdef CONFIG_B43_BCMA - err = bcma_driver_register(&b43_bcma_driver); - if (err) - goto err_sdio_exit; -#endif -#ifdef CONFIG_B43_SSB - err = ssb_driver_register(&b43_ssb_driver); - if (err) - goto err_bcma_driver_exit; -#endif - b43_print_driverinfo(); - - return err; - -#ifdef CONFIG_B43_SSB -err_bcma_driver_exit: -#endif -#ifdef CONFIG_B43_BCMA - bcma_driver_unregister(&b43_bcma_driver); -err_sdio_exit: -#endif - b43_sdio_exit(); -err_dfs_exit: - b43_debugfs_exit(); - return err; -} - -static void __exit b43_exit(void) -{ -#ifdef CONFIG_B43_SSB - ssb_driver_unregister(&b43_ssb_driver); -#endif -#ifdef CONFIG_B43_BCMA - bcma_driver_unregister(&b43_bcma_driver); -#endif - b43_sdio_exit(); - b43_debugfs_exit(); -} - -module_init(b43_init) -module_exit(b43_exit) diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h deleted file mode 100644 index c46430cc725c..000000000000 --- a/drivers/net/wireless/b43/main.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - - Broadcom B43 wireless driver - - Copyright (c) 2005 Martin Langer , - Stefano Brivio - Michael Buesch - Danny van Dyk - Andreas Jaggi - - Some parts of the code in this file are derived from the ipw2200 - driver Copyright(c) 2003 - 2004 Intel Corporation. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#ifndef B43_MAIN_H_ -#define B43_MAIN_H_ - -#include "b43.h" - -#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] -#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) -/* Magic helper macro to pad structures. Ignore those above. It's magic. */ -#define PAD_BYTES(nr_bytes) P4D_BYTES( __LINE__ , (nr_bytes)) - - -extern int b43_modparam_verbose; - -/* Logmessage verbosity levels. Update the b43_modparam_verbose helptext, if - * you add or remove levels. */ -enum b43_verbosity { - B43_VERBOSITY_ERROR, - B43_VERBOSITY_WARN, - B43_VERBOSITY_INFO, - B43_VERBOSITY_DEBUG, - __B43_VERBOSITY_AFTERLAST, /* keep last */ - - B43_VERBOSITY_MAX = __B43_VERBOSITY_AFTERLAST - 1, -#if B43_DEBUG - B43_VERBOSITY_DEFAULT = B43_VERBOSITY_DEBUG, -#else - B43_VERBOSITY_DEFAULT = B43_VERBOSITY_INFO, -#endif -}; - -static inline int b43_is_cck_rate(int rate) -{ - return (rate == B43_CCK_RATE_1MB || - rate == B43_CCK_RATE_2MB || - rate == B43_CCK_RATE_5MB || rate == B43_CCK_RATE_11MB); -} - -static inline int b43_is_ofdm_rate(int rate) -{ - return !b43_is_cck_rate(rate); -} - -u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev, - u8 antenna_nr); - -void b43_tsf_read(struct b43_wldev *dev, u64 * tsf); -void b43_tsf_write(struct b43_wldev *dev, u64 tsf); - -u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset); -u16 b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset); -void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value); -void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value); - -u64 b43_hf_read(struct b43_wldev *dev); -void b43_hf_write(struct b43_wldev *dev, u64 value); - -void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on); - -void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode); - -void b43_controller_restart(struct b43_wldev *dev, const char *reason); - -#define B43_PS_ENABLED (1 << 0) /* Force enable hardware power saving */ -#define B43_PS_DISABLED (1 << 1) /* Force disable hardware power saving */ -#define B43_PS_AWAKE (1 << 2) /* Force device awake */ -#define B43_PS_ASLEEP (1 << 3) /* Force device asleep */ -void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags); - -void b43_wireless_core_phy_pll_reset(struct b43_wldev *dev); - -void b43_mac_suspend(struct b43_wldev *dev); -void b43_mac_enable(struct b43_wldev *dev); -void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on); -void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode); - - -struct b43_request_fw_context; -int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, - struct b43_firmware_file *fw, bool async); -void b43_do_release_fw(struct b43_firmware_file *fw); - -#endif /* B43_MAIN_H_ */ diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/b43/phy_a.c deleted file mode 100644 index 99c036f5ecb7..000000000000 --- a/drivers/net/wireless/b43/phy_a.c +++ /dev/null @@ -1,595 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11a PHY driver - - Copyright (c) 2005 Martin Langer , - Copyright (c) 2005-2007 Stefano Brivio - Copyright (c) 2005-2008 Michael Buesch - Copyright (c) 2005, 2006 Danny van Dyk - Copyright (c) 2005, 2006 Andreas Jaggi - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include - -#include "b43.h" -#include "phy_a.h" -#include "phy_common.h" -#include "wa.h" -#include "tables.h" -#include "main.h" - - -/* Get the freq, as it has to be written to the device. */ -static inline u16 channel2freq_a(u8 channel) -{ - B43_WARN_ON(channel > 200); - - return (5000 + 5 * channel); -} - -static inline u16 freq_r3A_value(u16 frequency) -{ - u16 value; - - if (frequency < 5091) - value = 0x0040; - else if (frequency < 5321) - value = 0x0000; - else if (frequency < 5806) - value = 0x0080; - else - value = 0x0040; - - return value; -} - -#if 0 -/* This function converts a TSSI value to dBm in Q5.2 */ -static s8 b43_aphy_estimate_power_out(struct b43_wldev *dev, s8 tssi) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_a *aphy = phy->a; - s8 dbm = 0; - s32 tmp; - - tmp = (aphy->tgt_idle_tssi - aphy->cur_idle_tssi + tssi); - tmp += 0x80; - tmp = clamp_val(tmp, 0x00, 0xFF); - dbm = aphy->tssi2dbm[tmp]; - //TODO: There's a FIXME on the specs - - return dbm; -} -#endif - -static void b43_radio_set_tx_iq(struct b43_wldev *dev) -{ - static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 }; - static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A }; - u16 tmp = b43_radio_read16(dev, 0x001E); - int i, j; - - for (i = 0; i < 5; i++) { - for (j = 0; j < 5; j++) { - if (tmp == (data_high[i] << 4 | data_low[j])) { - b43_phy_write(dev, 0x0069, - (i - j) << 8 | 0x00C0); - return; - } - } - } -} - -static void aphy_channel_switch(struct b43_wldev *dev, unsigned int channel) -{ - u16 freq, r8, tmp; - - freq = channel2freq_a(channel); - - r8 = b43_radio_read16(dev, 0x0008); - b43_write16(dev, 0x03F0, freq); - b43_radio_write16(dev, 0x0008, r8); - - //TODO: write max channel TX power? to Radio 0x2D - tmp = b43_radio_read16(dev, 0x002E); - tmp &= 0x0080; - //TODO: OR tmp with the Power out estimation for this channel? - b43_radio_write16(dev, 0x002E, tmp); - - if (freq >= 4920 && freq <= 5500) { - /* - * r8 = (((freq * 15 * 0xE1FC780F) >> 32) / 29) & 0x0F; - * = (freq * 0.025862069 - */ - r8 = 3 * freq / 116; /* is equal to r8 = freq * 0.025862 */ - } - b43_radio_write16(dev, 0x0007, (r8 << 4) | r8); - b43_radio_write16(dev, 0x0020, (r8 << 4) | r8); - b43_radio_write16(dev, 0x0021, (r8 << 4) | r8); - b43_radio_maskset(dev, 0x0022, 0x000F, (r8 << 4)); - b43_radio_write16(dev, 0x002A, (r8 << 4)); - b43_radio_write16(dev, 0x002B, (r8 << 4)); - b43_radio_maskset(dev, 0x0008, 0x00F0, (r8 << 4)); - b43_radio_maskset(dev, 0x0029, 0xFF0F, 0x00B0); - b43_radio_write16(dev, 0x0035, 0x00AA); - b43_radio_write16(dev, 0x0036, 0x0085); - b43_radio_maskset(dev, 0x003A, 0xFF20, freq_r3A_value(freq)); - b43_radio_mask(dev, 0x003D, 0x00FF); - b43_radio_maskset(dev, 0x0081, 0xFF7F, 0x0080); - b43_radio_mask(dev, 0x0035, 0xFFEF); - b43_radio_maskset(dev, 0x0035, 0xFFEF, 0x0010); - b43_radio_set_tx_iq(dev); - //TODO: TSSI2dbm workaround -//FIXME b43_phy_xmitpower(dev); -} - -static void b43_radio_init2060(struct b43_wldev *dev) -{ - b43_radio_write16(dev, 0x0004, 0x00C0); - b43_radio_write16(dev, 0x0005, 0x0008); - b43_radio_write16(dev, 0x0009, 0x0040); - b43_radio_write16(dev, 0x0005, 0x00AA); - b43_radio_write16(dev, 0x0032, 0x008F); - b43_radio_write16(dev, 0x0006, 0x008F); - b43_radio_write16(dev, 0x0034, 0x008F); - b43_radio_write16(dev, 0x002C, 0x0007); - b43_radio_write16(dev, 0x0082, 0x0080); - b43_radio_write16(dev, 0x0080, 0x0000); - b43_radio_write16(dev, 0x003F, 0x00DA); - b43_radio_mask(dev, 0x0005, ~0x0008); - b43_radio_mask(dev, 0x0081, ~0x0010); - b43_radio_mask(dev, 0x0081, ~0x0020); - b43_radio_mask(dev, 0x0081, ~0x0020); - msleep(1); /* delay 400usec */ - - b43_radio_maskset(dev, 0x0081, ~0x0020, 0x0010); - msleep(1); /* delay 400usec */ - - b43_radio_maskset(dev, 0x0005, ~0x0008, 0x0008); - b43_radio_mask(dev, 0x0085, ~0x0010); - b43_radio_mask(dev, 0x0005, ~0x0008); - b43_radio_mask(dev, 0x0081, ~0x0040); - b43_radio_maskset(dev, 0x0081, ~0x0040, 0x0040); - b43_radio_write16(dev, 0x0005, - (b43_radio_read16(dev, 0x0081) & ~0x0008) | 0x0008); - b43_phy_write(dev, 0x0063, 0xDDC6); - b43_phy_write(dev, 0x0069, 0x07BE); - b43_phy_write(dev, 0x006A, 0x0000); - - aphy_channel_switch(dev, dev->phy.ops->get_default_chan(dev)); - - msleep(1); -} - -static void b43_phy_rssiagc(struct b43_wldev *dev, u8 enable) -{ - int i; - - if (dev->phy.rev < 3) { - if (enable) - for (i = 0; i < B43_TAB_RSSIAGC1_SIZE; i++) { - b43_ofdmtab_write16(dev, - B43_OFDMTAB_LNAHPFGAIN1, i, 0xFFF8); - b43_ofdmtab_write16(dev, - B43_OFDMTAB_WRSSI, i, 0xFFF8); - } - else - for (i = 0; i < B43_TAB_RSSIAGC1_SIZE; i++) { - b43_ofdmtab_write16(dev, - B43_OFDMTAB_LNAHPFGAIN1, i, b43_tab_rssiagc1[i]); - b43_ofdmtab_write16(dev, - B43_OFDMTAB_WRSSI, i, b43_tab_rssiagc1[i]); - } - } else { - if (enable) - for (i = 0; i < B43_TAB_RSSIAGC1_SIZE; i++) - b43_ofdmtab_write16(dev, - B43_OFDMTAB_WRSSI, i, 0x0820); - else - for (i = 0; i < B43_TAB_RSSIAGC2_SIZE; i++) - b43_ofdmtab_write16(dev, - B43_OFDMTAB_WRSSI, i, b43_tab_rssiagc2[i]); - } -} - -static void b43_phy_ww(struct b43_wldev *dev) -{ - u16 b, curr_s, best_s = 0xFFFF; - int i; - - b43_phy_mask(dev, B43_PHY_CRS0, ~B43_PHY_CRS0_EN); - b43_phy_set(dev, B43_PHY_OFDM(0x1B), 0x1000); - b43_phy_maskset(dev, B43_PHY_OFDM(0x82), 0xF0FF, 0x0300); - b43_radio_set(dev, 0x0009, 0x0080); - b43_radio_maskset(dev, 0x0012, 0xFFFC, 0x0002); - b43_wa_initgains(dev); - b43_phy_write(dev, B43_PHY_OFDM(0xBA), 0x3ED5); - b = b43_phy_read(dev, B43_PHY_PWRDOWN); - b43_phy_write(dev, B43_PHY_PWRDOWN, (b & 0xFFF8) | 0x0005); - b43_radio_set(dev, 0x0004, 0x0004); - for (i = 0x10; i <= 0x20; i++) { - b43_radio_write16(dev, 0x0013, i); - curr_s = b43_phy_read(dev, B43_PHY_OTABLEQ) & 0x00FF; - if (!curr_s) { - best_s = 0x0000; - break; - } else if (curr_s >= 0x0080) - curr_s = 0x0100 - curr_s; - if (curr_s < best_s) - best_s = curr_s; - } - b43_phy_write(dev, B43_PHY_PWRDOWN, b); - b43_radio_mask(dev, 0x0004, 0xFFFB); - b43_radio_write16(dev, 0x0013, best_s); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 0, 0xFFEC); - b43_phy_write(dev, B43_PHY_OFDM(0xB7), 0x1E80); - b43_phy_write(dev, B43_PHY_OFDM(0xB6), 0x1C00); - b43_phy_write(dev, B43_PHY_OFDM(0xB5), 0x0EC0); - b43_phy_write(dev, B43_PHY_OFDM(0xB2), 0x00C0); - b43_phy_write(dev, B43_PHY_OFDM(0xB9), 0x1FFF); - b43_phy_maskset(dev, B43_PHY_OFDM(0xBB), 0xF000, 0x0053); - b43_phy_maskset(dev, B43_PHY_OFDM61, 0xFE1F, 0x0120); - b43_phy_maskset(dev, B43_PHY_OFDM(0x13), 0x0FFF, 0x3000); - b43_phy_maskset(dev, B43_PHY_OFDM(0x14), 0x0FFF, 0x3000); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 6, 0x0017); - for (i = 0; i < 6; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, i, 0x000F); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0x0D, 0x000E); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0x0E, 0x0011); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0x0F, 0x0013); - b43_phy_write(dev, B43_PHY_OFDM(0x33), 0x5030); - b43_phy_set(dev, B43_PHY_CRS0, B43_PHY_CRS0_EN); -} - -static void hardware_pctl_init_aphy(struct b43_wldev *dev) -{ - //TODO -} - -void b43_phy_inita(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - /* This lowlevel A-PHY init is also called from G-PHY init. - * So we must not access phy->a, if called from G-PHY code. - */ - B43_WARN_ON((phy->type != B43_PHYTYPE_A) && - (phy->type != B43_PHYTYPE_G)); - - might_sleep(); - - if (phy->rev >= 6) { - if (phy->type == B43_PHYTYPE_A) - b43_phy_mask(dev, B43_PHY_OFDM(0x1B), ~0x1000); - if (b43_phy_read(dev, B43_PHY_ENCORE) & B43_PHY_ENCORE_EN) - b43_phy_set(dev, B43_PHY_ENCORE, 0x0010); - else - b43_phy_mask(dev, B43_PHY_ENCORE, ~0x1010); - } - - b43_wa_all(dev); - - if (phy->type == B43_PHYTYPE_A) { - if (phy->gmode && (phy->rev < 3)) - b43_phy_set(dev, 0x0034, 0x0001); - b43_phy_rssiagc(dev, 0); - - b43_phy_set(dev, B43_PHY_CRS0, B43_PHY_CRS0_EN); - - b43_radio_init2060(dev); - - if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) && - ((dev->dev->board_type == SSB_BOARD_BU4306) || - (dev->dev->board_type == SSB_BOARD_BU4309))) { - ; //TODO: A PHY LO - } - - if (phy->rev >= 3) - b43_phy_ww(dev); - - hardware_pctl_init_aphy(dev); - - //TODO: radar detection - } - - if ((phy->type == B43_PHYTYPE_G) && - (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL)) { - b43_phy_maskset(dev, B43_PHY_OFDM(0x6E), 0xE000, 0x3CF); - } -} - -/* Initialise the TSSI->dBm lookup table */ -static int b43_aphy_init_tssi2dbm_table(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_a *aphy = phy->a; - s16 pab0, pab1, pab2; - - pab0 = (s16) (dev->dev->bus_sprom->pa1b0); - pab1 = (s16) (dev->dev->bus_sprom->pa1b1); - pab2 = (s16) (dev->dev->bus_sprom->pa1b2); - - if (pab0 != 0 && pab1 != 0 && pab2 != 0 && - pab0 != -1 && pab1 != -1 && pab2 != -1) { - /* The pabX values are set in SPROM. Use them. */ - if ((s8) dev->dev->bus_sprom->itssi_a != 0 && - (s8) dev->dev->bus_sprom->itssi_a != -1) - aphy->tgt_idle_tssi = - (s8) (dev->dev->bus_sprom->itssi_a); - else - aphy->tgt_idle_tssi = 62; - aphy->tssi2dbm = b43_generate_dyn_tssi2dbm_tab(dev, pab0, - pab1, pab2); - if (!aphy->tssi2dbm) - return -ENOMEM; - } else { - /* pabX values not set in SPROM, - * but APHY needs a generated table. */ - aphy->tssi2dbm = NULL; - b43err(dev->wl, "Could not generate tssi2dBm " - "table (wrong SPROM info)!\n"); - return -ENODEV; - } - - return 0; -} - -static int b43_aphy_op_allocate(struct b43_wldev *dev) -{ - struct b43_phy_a *aphy; - int err; - - aphy = kzalloc(sizeof(*aphy), GFP_KERNEL); - if (!aphy) - return -ENOMEM; - dev->phy.a = aphy; - - err = b43_aphy_init_tssi2dbm_table(dev); - if (err) - goto err_free_aphy; - - return 0; - -err_free_aphy: - kfree(aphy); - dev->phy.a = NULL; - - return err; -} - -static void b43_aphy_op_prepare_structs(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_a *aphy = phy->a; - const void *tssi2dbm; - int tgt_idle_tssi; - - /* tssi2dbm table is constant, so it is initialized at alloc time. - * Save a copy of the pointer. */ - tssi2dbm = aphy->tssi2dbm; - tgt_idle_tssi = aphy->tgt_idle_tssi; - - /* Zero out the whole PHY structure. */ - memset(aphy, 0, sizeof(*aphy)); - - aphy->tssi2dbm = tssi2dbm; - aphy->tgt_idle_tssi = tgt_idle_tssi; - - //TODO init struct b43_phy_a - -} - -static void b43_aphy_op_free(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_a *aphy = phy->a; - - kfree(aphy->tssi2dbm); - aphy->tssi2dbm = NULL; - - kfree(aphy); - dev->phy.a = NULL; -} - -static int b43_aphy_op_init(struct b43_wldev *dev) -{ - b43_phy_inita(dev); - - return 0; -} - -static inline u16 adjust_phyreg(struct b43_wldev *dev, u16 offset) -{ - /* OFDM registers are base-registers for the A-PHY. */ - if ((offset & B43_PHYROUTE) == B43_PHYROUTE_OFDM_GPHY) { - offset &= ~B43_PHYROUTE; - offset |= B43_PHYROUTE_BASE; - } - -#if B43_DEBUG - if ((offset & B43_PHYROUTE) == B43_PHYROUTE_EXT_GPHY) { - /* Ext-G registers are only available on G-PHYs */ - b43err(dev->wl, "Invalid EXT-G PHY access at " - "0x%04X on A-PHY\n", offset); - dump_stack(); - } - if ((offset & B43_PHYROUTE) == B43_PHYROUTE_N_BMODE) { - /* N-BMODE registers are only available on N-PHYs */ - b43err(dev->wl, "Invalid N-BMODE PHY access at " - "0x%04X on A-PHY\n", offset); - dump_stack(); - } -#endif /* B43_DEBUG */ - - return offset; -} - -static u16 b43_aphy_op_read(struct b43_wldev *dev, u16 reg) -{ - reg = adjust_phyreg(dev, reg); - b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); - return b43_read16(dev, B43_MMIO_PHY_DATA); -} - -static void b43_aphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - reg = adjust_phyreg(dev, reg); - b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); - b43_write16(dev, B43_MMIO_PHY_DATA, value); -} - -static u16 b43_aphy_op_radio_read(struct b43_wldev *dev, u16 reg) -{ - /* Register 1 is a 32-bit register. */ - B43_WARN_ON(reg == 1); - /* A-PHY needs 0x40 for read access */ - reg |= 0x40; - - b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); - return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); -} - -static void b43_aphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - /* Register 1 is a 32-bit register. */ - B43_WARN_ON(reg == 1); - - b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); - b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); -} - -static bool b43_aphy_op_supports_hwpctl(struct b43_wldev *dev) -{ - return (dev->phy.rev >= 5); -} - -static void b43_aphy_op_software_rfkill(struct b43_wldev *dev, - bool blocked) -{ - struct b43_phy *phy = &dev->phy; - - if (!blocked) { - if (phy->radio_on) - return; - b43_radio_write16(dev, 0x0004, 0x00C0); - b43_radio_write16(dev, 0x0005, 0x0008); - b43_phy_mask(dev, 0x0010, 0xFFF7); - b43_phy_mask(dev, 0x0011, 0xFFF7); - b43_radio_init2060(dev); - } else { - b43_radio_write16(dev, 0x0004, 0x00FF); - b43_radio_write16(dev, 0x0005, 0x00FB); - b43_phy_set(dev, 0x0010, 0x0008); - b43_phy_set(dev, 0x0011, 0x0008); - } -} - -static int b43_aphy_op_switch_channel(struct b43_wldev *dev, - unsigned int new_channel) -{ - if (new_channel > 200) - return -EINVAL; - aphy_channel_switch(dev, new_channel); - - return 0; -} - -static unsigned int b43_aphy_op_get_default_chan(struct b43_wldev *dev) -{ - return 36; /* Default to channel 36 */ -} - -static void b43_aphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) -{//TODO - struct b43_phy *phy = &dev->phy; - u16 tmp; - int autodiv = 0; - - if (antenna == B43_ANTENNA_AUTO0 || antenna == B43_ANTENNA_AUTO1) - autodiv = 1; - - b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP); - - b43_phy_maskset(dev, B43_PHY_BBANDCFG, ~B43_PHY_BBANDCFG_RXANT, - (autodiv ? B43_ANTENNA_AUTO1 : antenna) << - B43_PHY_BBANDCFG_RXANT_SHIFT); - - if (autodiv) { - tmp = b43_phy_read(dev, B43_PHY_ANTDWELL); - if (antenna == B43_ANTENNA_AUTO1) - tmp &= ~B43_PHY_ANTDWELL_AUTODIV1; - else - tmp |= B43_PHY_ANTDWELL_AUTODIV1; - b43_phy_write(dev, B43_PHY_ANTDWELL, tmp); - } - if (phy->rev < 3) - b43_phy_maskset(dev, B43_PHY_ANTDWELL, 0xFF00, 0x24); - else { - b43_phy_set(dev, B43_PHY_OFDM61, 0x10); - if (phy->rev == 3) { - b43_phy_write(dev, B43_PHY_CLIPPWRDOWNT, 0x1D); - b43_phy_write(dev, B43_PHY_ADIVRELATED, 8); - } else { - b43_phy_write(dev, B43_PHY_CLIPPWRDOWNT, 0x3A); - b43_phy_maskset(dev, B43_PHY_ADIVRELATED, 0xFF00, 8); - } - } - - b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP); -} - -static void b43_aphy_op_adjust_txpower(struct b43_wldev *dev) -{//TODO -} - -static enum b43_txpwr_result b43_aphy_op_recalc_txpower(struct b43_wldev *dev, - bool ignore_tssi) -{//TODO - return B43_TXPWR_RES_DONE; -} - -static void b43_aphy_op_pwork_15sec(struct b43_wldev *dev) -{//TODO -} - -static void b43_aphy_op_pwork_60sec(struct b43_wldev *dev) -{//TODO -} - -static const struct b43_phy_operations b43_phyops_a = { - .allocate = b43_aphy_op_allocate, - .free = b43_aphy_op_free, - .prepare_structs = b43_aphy_op_prepare_structs, - .init = b43_aphy_op_init, - .phy_read = b43_aphy_op_read, - .phy_write = b43_aphy_op_write, - .radio_read = b43_aphy_op_radio_read, - .radio_write = b43_aphy_op_radio_write, - .supports_hwpctl = b43_aphy_op_supports_hwpctl, - .software_rfkill = b43_aphy_op_software_rfkill, - .switch_analog = b43_phyop_switch_analog_generic, - .switch_channel = b43_aphy_op_switch_channel, - .get_default_chan = b43_aphy_op_get_default_chan, - .set_rx_antenna = b43_aphy_op_set_rx_antenna, - .recalc_txpower = b43_aphy_op_recalc_txpower, - .adjust_txpower = b43_aphy_op_adjust_txpower, - .pwork_15sec = b43_aphy_op_pwork_15sec, - .pwork_60sec = b43_aphy_op_pwork_60sec, -}; diff --git a/drivers/net/wireless/b43/phy_a.h b/drivers/net/wireless/b43/phy_a.h deleted file mode 100644 index f7d0d929a374..000000000000 --- a/drivers/net/wireless/b43/phy_a.h +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef LINUX_B43_PHY_A_H_ -#define LINUX_B43_PHY_A_H_ - -#include "phy_common.h" - - -/* OFDM (A) PHY Registers */ -#define B43_PHY_VERSION_OFDM B43_PHY_OFDM(0x00) /* Versioning register for A-PHY */ -#define B43_PHY_BBANDCFG B43_PHY_OFDM(0x01) /* Baseband config */ -#define B43_PHY_BBANDCFG_RXANT 0x180 /* RX Antenna selection */ -#define B43_PHY_BBANDCFG_RXANT_SHIFT 7 -#define B43_PHY_PWRDOWN B43_PHY_OFDM(0x03) /* Powerdown */ -#define B43_PHY_CRSTHRES1_R1 B43_PHY_OFDM(0x06) /* CRS Threshold 1 (phy.rev 1 only) */ -#define B43_PHY_LNAHPFCTL B43_PHY_OFDM(0x1C) /* LNA/HPF control */ -#define B43_PHY_LPFGAINCTL B43_PHY_OFDM(0x20) /* LPF Gain control */ -#define B43_PHY_ADIVRELATED B43_PHY_OFDM(0x27) /* FIXME rename */ -#define B43_PHY_CRS0 B43_PHY_OFDM(0x29) -#define B43_PHY_CRS0_EN 0x4000 -#define B43_PHY_PEAK_COUNT B43_PHY_OFDM(0x30) -#define B43_PHY_ANTDWELL B43_PHY_OFDM(0x2B) /* Antenna dwell */ -#define B43_PHY_ANTDWELL_AUTODIV1 0x0100 /* Automatic RX diversity start antenna */ -#define B43_PHY_ENCORE B43_PHY_OFDM(0x49) /* "Encore" (RangeMax / BroadRange) */ -#define B43_PHY_ENCORE_EN 0x0200 /* Encore enable */ -#define B43_PHY_LMS B43_PHY_OFDM(0x55) -#define B43_PHY_OFDM61 B43_PHY_OFDM(0x61) /* FIXME rename */ -#define B43_PHY_OFDM61_10 0x0010 /* FIXME rename */ -#define B43_PHY_IQBAL B43_PHY_OFDM(0x69) /* I/Q balance */ -#define B43_PHY_BBTXDC_BIAS B43_PHY_OFDM(0x6B) /* Baseband TX DC bias */ -#define B43_PHY_OTABLECTL B43_PHY_OFDM(0x72) /* OFDM table control (see below) */ -#define B43_PHY_OTABLEOFF 0x03FF /* OFDM table offset (see below) */ -#define B43_PHY_OTABLENR 0xFC00 /* OFDM table number (see below) */ -#define B43_PHY_OTABLENR_SHIFT 10 -#define B43_PHY_OTABLEI B43_PHY_OFDM(0x73) /* OFDM table data I */ -#define B43_PHY_OTABLEQ B43_PHY_OFDM(0x74) /* OFDM table data Q */ -#define B43_PHY_HPWR_TSSICTL B43_PHY_OFDM(0x78) /* Hardware power TSSI control */ -#define B43_PHY_ADCCTL B43_PHY_OFDM(0x7A) /* ADC control */ -#define B43_PHY_IDLE_TSSI B43_PHY_OFDM(0x7B) -#define B43_PHY_A_TEMP_SENSE B43_PHY_OFDM(0x7C) /* A PHY temperature sense */ -#define B43_PHY_NRSSITHRES B43_PHY_OFDM(0x8A) /* NRSSI threshold */ -#define B43_PHY_ANTWRSETT B43_PHY_OFDM(0x8C) /* Antenna WR settle */ -#define B43_PHY_ANTWRSETT_ARXDIV 0x2000 /* Automatic RX diversity enabled */ -#define B43_PHY_CLIPPWRDOWNT B43_PHY_OFDM(0x93) /* Clip powerdown threshold */ -#define B43_PHY_OFDM9B B43_PHY_OFDM(0x9B) /* FIXME rename */ -#define B43_PHY_N1P1GAIN B43_PHY_OFDM(0xA0) -#define B43_PHY_P1P2GAIN B43_PHY_OFDM(0xA1) -#define B43_PHY_N1N2GAIN B43_PHY_OFDM(0xA2) -#define B43_PHY_CLIPTHRES B43_PHY_OFDM(0xA3) -#define B43_PHY_CLIPN1P2THRES B43_PHY_OFDM(0xA4) -#define B43_PHY_CCKSHIFTBITS_WA B43_PHY_OFDM(0xA5) /* CCK shiftbits workaround, FIXME rename */ -#define B43_PHY_CCKSHIFTBITS B43_PHY_OFDM(0xA7) /* FIXME rename */ -#define B43_PHY_DIVSRCHIDX B43_PHY_OFDM(0xA8) /* Divider search gain/index */ -#define B43_PHY_CLIPP2THRES B43_PHY_OFDM(0xA9) -#define B43_PHY_CLIPP3THRES B43_PHY_OFDM(0xAA) -#define B43_PHY_DIVP1P2GAIN B43_PHY_OFDM(0xAB) -#define B43_PHY_DIVSRCHGAINBACK B43_PHY_OFDM(0xAD) /* Divider search gain back */ -#define B43_PHY_DIVSRCHGAINCHNG B43_PHY_OFDM(0xAE) /* Divider search gain change */ -#define B43_PHY_CRSTHRES1 B43_PHY_OFDM(0xC0) /* CRS Threshold 1 (phy.rev >= 2 only) */ -#define B43_PHY_CRSTHRES2 B43_PHY_OFDM(0xC1) /* CRS Threshold 2 (phy.rev >= 2 only) */ -#define B43_PHY_TSSIP_LTBASE B43_PHY_OFDM(0x380) /* TSSI power lookup table base */ -#define B43_PHY_DC_LTBASE B43_PHY_OFDM(0x3A0) /* DC lookup table base */ -#define B43_PHY_GAIN_LTBASE B43_PHY_OFDM(0x3C0) /* Gain lookup table base */ - -/*** OFDM table numbers ***/ -#define B43_OFDMTAB(number, offset) (((number) << B43_PHY_OTABLENR_SHIFT) | (offset)) -#define B43_OFDMTAB_AGC1 B43_OFDMTAB(0x00, 0) -#define B43_OFDMTAB_GAIN0 B43_OFDMTAB(0x00, 0) -#define B43_OFDMTAB_GAINX B43_OFDMTAB(0x01, 0) //TODO rename -#define B43_OFDMTAB_GAIN1 B43_OFDMTAB(0x01, 4) -#define B43_OFDMTAB_AGC3 B43_OFDMTAB(0x02, 0) -#define B43_OFDMTAB_GAIN2 B43_OFDMTAB(0x02, 3) -#define B43_OFDMTAB_LNAHPFGAIN1 B43_OFDMTAB(0x03, 0) -#define B43_OFDMTAB_WRSSI B43_OFDMTAB(0x04, 0) -#define B43_OFDMTAB_LNAHPFGAIN2 B43_OFDMTAB(0x04, 0) -#define B43_OFDMTAB_NOISESCALE B43_OFDMTAB(0x05, 0) -#define B43_OFDMTAB_AGC2 B43_OFDMTAB(0x06, 0) -#define B43_OFDMTAB_ROTOR B43_OFDMTAB(0x08, 0) -#define B43_OFDMTAB_ADVRETARD B43_OFDMTAB(0x09, 0) -#define B43_OFDMTAB_DAC B43_OFDMTAB(0x0C, 0) -#define B43_OFDMTAB_DC B43_OFDMTAB(0x0E, 7) -#define B43_OFDMTAB_PWRDYN2 B43_OFDMTAB(0x0E, 12) -#define B43_OFDMTAB_LNAGAIN B43_OFDMTAB(0x0E, 13) -#define B43_OFDMTAB_UNKNOWN_0F B43_OFDMTAB(0x0F, 0) //TODO rename -#define B43_OFDMTAB_UNKNOWN_APHY B43_OFDMTAB(0x0F, 7) //TODO rename -#define B43_OFDMTAB_LPFGAIN B43_OFDMTAB(0x0F, 12) -#define B43_OFDMTAB_RSSI B43_OFDMTAB(0x10, 0) -#define B43_OFDMTAB_UNKNOWN_11 B43_OFDMTAB(0x11, 4) //TODO rename -#define B43_OFDMTAB_AGC1_R1 B43_OFDMTAB(0x13, 0) -#define B43_OFDMTAB_GAINX_R1 B43_OFDMTAB(0x14, 0) //TODO remove! -#define B43_OFDMTAB_MINSIGSQ B43_OFDMTAB(0x14, 0) -#define B43_OFDMTAB_AGC3_R1 B43_OFDMTAB(0x15, 0) -#define B43_OFDMTAB_WRSSI_R1 B43_OFDMTAB(0x15, 4) -#define B43_OFDMTAB_TSSI B43_OFDMTAB(0x15, 0) -#define B43_OFDMTAB_DACRFPABB B43_OFDMTAB(0x16, 0) -#define B43_OFDMTAB_DACOFF B43_OFDMTAB(0x17, 0) -#define B43_OFDMTAB_DCBIAS B43_OFDMTAB(0x18, 0) - -u16 b43_ofdmtab_read16(struct b43_wldev *dev, u16 table, u16 offset); -void b43_ofdmtab_write16(struct b43_wldev *dev, u16 table, - u16 offset, u16 value); -u32 b43_ofdmtab_read32(struct b43_wldev *dev, u16 table, u16 offset); -void b43_ofdmtab_write32(struct b43_wldev *dev, u16 table, - u16 offset, u32 value); - - -struct b43_phy_a { - /* Pointer to the table used to convert a - * TSSI value to dBm-Q5.2 */ - const s8 *tssi2dbm; - /* Target idle TSSI */ - int tgt_idle_tssi; - /* Current idle TSSI */ - int cur_idle_tssi;//FIXME value currently not set - - /* A-PHY TX Power control value. */ - u16 txpwr_offset; - - //TODO lots of missing stuff -}; - -/** - * b43_phy_inita - Lowlevel A-PHY init routine. - * This is _only_ used by the G-PHY code. - */ -void b43_phy_inita(struct b43_wldev *dev); - -#endif /* LINUX_B43_PHY_A_H_ */ diff --git a/drivers/net/wireless/b43/phy_ac.c b/drivers/net/wireless/b43/phy_ac.c deleted file mode 100644 index e75633d67938..000000000000 --- a/drivers/net/wireless/b43/phy_ac.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Broadcom B43 wireless driver - * IEEE 802.11ac AC-PHY support - * - * Copyright (c) 2015 RafaÅ‚ MiÅ‚ecki - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include "b43.h" -#include "phy_ac.h" - -/************************************************** - * Basic PHY ops - **************************************************/ - -static int b43_phy_ac_op_allocate(struct b43_wldev *dev) -{ - struct b43_phy_ac *phy_ac; - - phy_ac = kzalloc(sizeof(*phy_ac), GFP_KERNEL); - if (!phy_ac) - return -ENOMEM; - dev->phy.ac = phy_ac; - - return 0; -} - -static void b43_phy_ac_op_free(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_ac *phy_ac = phy->ac; - - kfree(phy_ac); - phy->ac = NULL; -} - -static void b43_phy_ac_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, - u16 set) -{ - b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); - b43_write16(dev, B43_MMIO_PHY_DATA, - (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); -} - -static u16 b43_phy_ac_op_radio_read(struct b43_wldev *dev, u16 reg) -{ - b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); - return b43_read16(dev, B43_MMIO_RADIO24_DATA); -} - -static void b43_phy_ac_op_radio_write(struct b43_wldev *dev, u16 reg, - u16 value) -{ - b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); - b43_write16(dev, B43_MMIO_RADIO24_DATA, value); -} - -static unsigned int b43_phy_ac_op_get_default_chan(struct b43_wldev *dev) -{ - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - return 11; - return 36; -} - -static enum b43_txpwr_result -b43_phy_ac_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) -{ - return B43_TXPWR_RES_DONE; -} - -static void b43_phy_ac_op_adjust_txpower(struct b43_wldev *dev) -{ -} - -/************************************************** - * PHY ops struct - **************************************************/ - -const struct b43_phy_operations b43_phyops_ac = { - .allocate = b43_phy_ac_op_allocate, - .free = b43_phy_ac_op_free, - .phy_maskset = b43_phy_ac_op_maskset, - .radio_read = b43_phy_ac_op_radio_read, - .radio_write = b43_phy_ac_op_radio_write, - .get_default_chan = b43_phy_ac_op_get_default_chan, - .recalc_txpower = b43_phy_ac_op_recalc_txpower, - .adjust_txpower = b43_phy_ac_op_adjust_txpower, -}; diff --git a/drivers/net/wireless/b43/phy_ac.h b/drivers/net/wireless/b43/phy_ac.h deleted file mode 100644 index d1ca79e0eb24..000000000000 --- a/drivers/net/wireless/b43/phy_ac.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef B43_PHY_AC_H_ -#define B43_PHY_AC_H_ - -#include "phy_common.h" - -#define B43_PHY_AC_BBCFG 0x001 -#define B43_PHY_AC_BBCFG_RSTCCA 0x4000 /* Reset CCA */ -#define B43_PHY_AC_BANDCTL 0x003 /* Band control */ -#define B43_PHY_AC_BANDCTL_5GHZ 0x0001 -#define B43_PHY_AC_TABLE_ID 0x00d -#define B43_PHY_AC_TABLE_OFFSET 0x00e -#define B43_PHY_AC_TABLE_DATA1 0x00f -#define B43_PHY_AC_TABLE_DATA2 0x010 -#define B43_PHY_AC_TABLE_DATA3 0x011 -#define B43_PHY_AC_CLASSCTL 0x140 /* Classifier control */ -#define B43_PHY_AC_CLASSCTL_CCKEN 0x0001 /* CCK enable */ -#define B43_PHY_AC_CLASSCTL_OFDMEN 0x0002 /* OFDM enable */ -#define B43_PHY_AC_CLASSCTL_WAITEDEN 0x0004 /* Waited enable */ -#define B43_PHY_AC_BW1A 0x371 -#define B43_PHY_AC_BW2 0x372 -#define B43_PHY_AC_BW3 0x373 -#define B43_PHY_AC_BW4 0x374 -#define B43_PHY_AC_BW5 0x375 -#define B43_PHY_AC_BW6 0x376 -#define B43_PHY_AC_RFCTL_CMD 0x408 -#define B43_PHY_AC_C1_CLIP 0x6d4 -#define B43_PHY_AC_C1_CLIP_DIS 0x4000 -#define B43_PHY_AC_C2_CLIP 0x8d4 -#define B43_PHY_AC_C2_CLIP_DIS 0x4000 -#define B43_PHY_AC_C3_CLIP 0xad4 -#define B43_PHY_AC_C3_CLIP_DIS 0x4000 - -struct b43_phy_ac { -}; - -extern const struct b43_phy_operations b43_phyops_ac; - -#endif /* B43_PHY_AC_H_ */ diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c deleted file mode 100644 index ec2b9c577b90..000000000000 --- a/drivers/net/wireless/b43/phy_common.c +++ /dev/null @@ -1,653 +0,0 @@ -/* - - Broadcom B43 wireless driver - Common PHY routines - - Copyright (c) 2005 Martin Langer , - Copyright (c) 2005-2007 Stefano Brivio - Copyright (c) 2005-2008 Michael Buesch - Copyright (c) 2005, 2006 Danny van Dyk - Copyright (c) 2005, 2006 Andreas Jaggi - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "phy_common.h" -#include "phy_g.h" -#include "phy_a.h" -#include "phy_n.h" -#include "phy_lp.h" -#include "phy_ht.h" -#include "phy_lcn.h" -#include "phy_ac.h" -#include "b43.h" -#include "main.h" - - -int b43_phy_allocate(struct b43_wldev *dev) -{ - struct b43_phy *phy = &(dev->phy); - int err; - - phy->ops = NULL; - - switch (phy->type) { - case B43_PHYTYPE_G: -#ifdef CONFIG_B43_PHY_G - phy->ops = &b43_phyops_g; -#endif - break; - case B43_PHYTYPE_N: -#ifdef CONFIG_B43_PHY_N - phy->ops = &b43_phyops_n; -#endif - break; - case B43_PHYTYPE_LP: -#ifdef CONFIG_B43_PHY_LP - phy->ops = &b43_phyops_lp; -#endif - break; - case B43_PHYTYPE_HT: -#ifdef CONFIG_B43_PHY_HT - phy->ops = &b43_phyops_ht; -#endif - break; - case B43_PHYTYPE_LCN: -#ifdef CONFIG_B43_PHY_LCN - phy->ops = &b43_phyops_lcn; -#endif - break; - case B43_PHYTYPE_AC: -#ifdef CONFIG_B43_PHY_AC - phy->ops = &b43_phyops_ac; -#endif - break; - } - if (B43_WARN_ON(!phy->ops)) - return -ENODEV; - - err = phy->ops->allocate(dev); - if (err) - phy->ops = NULL; - - return err; -} - -void b43_phy_free(struct b43_wldev *dev) -{ - dev->phy.ops->free(dev); - dev->phy.ops = NULL; -} - -int b43_phy_init(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - const struct b43_phy_operations *ops = phy->ops; - int err; - - /* During PHY init we need to use some channel. On the first init this - * function is called *before* b43_op_config, so our pointer is NULL. - */ - if (!phy->chandef) { - phy->chandef = &dev->wl->hw->conf.chandef; - phy->channel = phy->chandef->chan->hw_value; - } - - phy->ops->switch_analog(dev, true); - b43_software_rfkill(dev, false); - - err = ops->init(dev); - if (err) { - b43err(dev->wl, "PHY init failed\n"); - goto err_block_rf; - } - phy->do_full_init = false; - - err = b43_switch_channel(dev, phy->channel); - if (err) { - b43err(dev->wl, "PHY init: Channel switch to default failed\n"); - goto err_phy_exit; - } - - return 0; - -err_phy_exit: - phy->do_full_init = true; - if (ops->exit) - ops->exit(dev); -err_block_rf: - b43_software_rfkill(dev, true); - - return err; -} - -void b43_phy_exit(struct b43_wldev *dev) -{ - const struct b43_phy_operations *ops = dev->phy.ops; - - b43_software_rfkill(dev, true); - dev->phy.do_full_init = true; - if (ops->exit) - ops->exit(dev); -} - -bool b43_has_hardware_pctl(struct b43_wldev *dev) -{ - if (!dev->phy.hardware_power_control) - return false; - if (!dev->phy.ops->supports_hwpctl) - return false; - return dev->phy.ops->supports_hwpctl(dev); -} - -void b43_radio_lock(struct b43_wldev *dev) -{ - u32 macctl; - -#if B43_DEBUG - B43_WARN_ON(dev->phy.radio_locked); - dev->phy.radio_locked = true; -#endif - - macctl = b43_read32(dev, B43_MMIO_MACCTL); - macctl |= B43_MACCTL_RADIOLOCK; - b43_write32(dev, B43_MMIO_MACCTL, macctl); - /* Commit the write and wait for the firmware - * to finish any radio register access. */ - b43_read32(dev, B43_MMIO_MACCTL); - udelay(10); -} - -void b43_radio_unlock(struct b43_wldev *dev) -{ - u32 macctl; - -#if B43_DEBUG - B43_WARN_ON(!dev->phy.radio_locked); - dev->phy.radio_locked = false; -#endif - - /* Commit any write */ - b43_read16(dev, B43_MMIO_PHY_VER); - /* unlock */ - macctl = b43_read32(dev, B43_MMIO_MACCTL); - macctl &= ~B43_MACCTL_RADIOLOCK; - b43_write32(dev, B43_MMIO_MACCTL, macctl); -} - -void b43_phy_lock(struct b43_wldev *dev) -{ -#if B43_DEBUG - B43_WARN_ON(dev->phy.phy_locked); - dev->phy.phy_locked = true; -#endif - B43_WARN_ON(dev->dev->core_rev < 3); - - if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) - b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); -} - -void b43_phy_unlock(struct b43_wldev *dev) -{ -#if B43_DEBUG - B43_WARN_ON(!dev->phy.phy_locked); - dev->phy.phy_locked = false; -#endif - B43_WARN_ON(dev->dev->core_rev < 3); - - if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) - b43_power_saving_ctl_bits(dev, 0); -} - -static inline void assert_mac_suspended(struct b43_wldev *dev) -{ - if (!B43_DEBUG) - return; - if ((b43_status(dev) >= B43_STAT_INITIALIZED) && - (dev->mac_suspended <= 0)) { - b43dbg(dev->wl, "PHY/RADIO register access with " - "enabled MAC.\n"); - dump_stack(); - } -} - -u16 b43_radio_read(struct b43_wldev *dev, u16 reg) -{ - assert_mac_suspended(dev); - dev->phy.writes_counter = 0; - return dev->phy.ops->radio_read(dev, reg); -} - -void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - assert_mac_suspended(dev); - if (b43_bus_host_is_pci(dev->dev) && - ++dev->phy.writes_counter > B43_MAX_WRITES_IN_ROW) { - b43_read32(dev, B43_MMIO_MACCTL); - dev->phy.writes_counter = 1; - } - dev->phy.ops->radio_write(dev, reg, value); -} - -void b43_radio_mask(struct b43_wldev *dev, u16 offset, u16 mask) -{ - b43_radio_write16(dev, offset, - b43_radio_read16(dev, offset) & mask); -} - -void b43_radio_set(struct b43_wldev *dev, u16 offset, u16 set) -{ - b43_radio_write16(dev, offset, - b43_radio_read16(dev, offset) | set); -} - -void b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) -{ - b43_radio_write16(dev, offset, - (b43_radio_read16(dev, offset) & mask) | set); -} - -bool b43_radio_wait_value(struct b43_wldev *dev, u16 offset, u16 mask, - u16 value, int delay, int timeout) -{ - u16 val; - int i; - - for (i = 0; i < timeout; i += delay) { - val = b43_radio_read(dev, offset); - if ((val & mask) == value) - return true; - udelay(delay); - } - return false; -} - -u16 b43_phy_read(struct b43_wldev *dev, u16 reg) -{ - assert_mac_suspended(dev); - dev->phy.writes_counter = 0; - - if (dev->phy.ops->phy_read) - return dev->phy.ops->phy_read(dev, reg); - - b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); - return b43_read16(dev, B43_MMIO_PHY_DATA); -} - -void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - assert_mac_suspended(dev); - if (b43_bus_host_is_pci(dev->dev) && - ++dev->phy.writes_counter > B43_MAX_WRITES_IN_ROW) { - b43_read16(dev, B43_MMIO_PHY_VER); - dev->phy.writes_counter = 1; - } - - if (dev->phy.ops->phy_write) - return dev->phy.ops->phy_write(dev, reg, value); - - b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); - b43_write16(dev, B43_MMIO_PHY_DATA, value); -} - -void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg) -{ - b43_phy_write(dev, destreg, b43_phy_read(dev, srcreg)); -} - -void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask) -{ - if (dev->phy.ops->phy_maskset) { - assert_mac_suspended(dev); - dev->phy.ops->phy_maskset(dev, offset, mask, 0); - } else { - b43_phy_write(dev, offset, - b43_phy_read(dev, offset) & mask); - } -} - -void b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set) -{ - if (dev->phy.ops->phy_maskset) { - assert_mac_suspended(dev); - dev->phy.ops->phy_maskset(dev, offset, 0xFFFF, set); - } else { - b43_phy_write(dev, offset, - b43_phy_read(dev, offset) | set); - } -} - -void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) -{ - if (dev->phy.ops->phy_maskset) { - assert_mac_suspended(dev); - dev->phy.ops->phy_maskset(dev, offset, mask, set); - } else { - b43_phy_write(dev, offset, - (b43_phy_read(dev, offset) & mask) | set); - } -} - -void b43_phy_put_into_reset(struct b43_wldev *dev) -{ - u32 tmp; - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - tmp &= ~B43_BCMA_IOCTL_GMODE; - tmp |= B43_BCMA_IOCTL_PHY_RESET; - tmp |= BCMA_IOCTL_FGC; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); - udelay(1); - - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - tmp &= ~BCMA_IOCTL_FGC; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); - udelay(1); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); - tmp &= ~B43_TMSLOW_GMODE; - tmp |= B43_TMSLOW_PHYRESET; - tmp |= SSB_TMSLOW_FGC; - ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); - usleep_range(1000, 2000); - - tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); - tmp &= ~SSB_TMSLOW_FGC; - ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); - usleep_range(1000, 2000); - - break; -#endif - } -} - -void b43_phy_take_out_of_reset(struct b43_wldev *dev) -{ - u32 tmp; - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - /* Unset reset bit (with forcing clock) */ - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - tmp &= ~B43_BCMA_IOCTL_PHY_RESET; - tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN; - tmp |= BCMA_IOCTL_FGC; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); - udelay(1); - - /* Do not force clock anymore */ - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - tmp &= ~BCMA_IOCTL_FGC; - tmp |= B43_BCMA_IOCTL_PHY_CLKEN; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); - udelay(1); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - /* Unset reset bit (with forcing clock) */ - tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); - tmp &= ~B43_TMSLOW_PHYRESET; - tmp &= ~B43_TMSLOW_PHYCLKEN; - tmp |= SSB_TMSLOW_FGC; - ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); - ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ - usleep_range(1000, 2000); - - tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); - tmp &= ~SSB_TMSLOW_FGC; - tmp |= B43_TMSLOW_PHYCLKEN; - ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); - ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ - usleep_range(1000, 2000); - break; -#endif - } -} - -int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) -{ - struct b43_phy *phy = &(dev->phy); - u16 channelcookie, savedcookie; - int err; - - /* First we set the channel radio code to prevent the - * firmware from sending ghost packets. - */ - channelcookie = new_channel; - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - channelcookie |= B43_SHM_SH_CHAN_5GHZ; - /* FIXME: set 40Mhz flag if required */ - if (0) - channelcookie |= B43_SHM_SH_CHAN_40MHZ; - savedcookie = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN, channelcookie); - - /* Now try to switch the PHY hardware channel. */ - err = phy->ops->switch_channel(dev, new_channel); - if (err) - goto err_restore_cookie; - - /* Wait for the radio to tune to the channel and stabilize. */ - msleep(8); - - return 0; - -err_restore_cookie: - b43_shm_write16(dev, B43_SHM_SHARED, - B43_SHM_SH_CHAN, savedcookie); - - return err; -} - -void b43_software_rfkill(struct b43_wldev *dev, bool blocked) -{ - struct b43_phy *phy = &dev->phy; - - b43_mac_suspend(dev); - phy->ops->software_rfkill(dev, blocked); - phy->radio_on = !blocked; - b43_mac_enable(dev); -} - -/** - * b43_phy_txpower_adjust_work - TX power workqueue. - * - * Workqueue for updating the TX power parameters in hardware. - */ -void b43_phy_txpower_adjust_work(struct work_struct *work) -{ - struct b43_wl *wl = container_of(work, struct b43_wl, - txpower_adjust_work); - struct b43_wldev *dev; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - - if (likely(dev && (b43_status(dev) >= B43_STAT_STARTED))) - dev->phy.ops->adjust_txpower(dev); - - mutex_unlock(&wl->mutex); -} - -void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags) -{ - struct b43_phy *phy = &dev->phy; - unsigned long now = jiffies; - enum b43_txpwr_result result; - - if (!(flags & B43_TXPWR_IGNORE_TIME)) { - /* Check if it's time for a TXpower check. */ - if (time_before(now, phy->next_txpwr_check_time)) - return; /* Not yet */ - } - /* The next check will be needed in two seconds, or later. */ - phy->next_txpwr_check_time = round_jiffies(now + (HZ * 2)); - - if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) && - (dev->dev->board_type == SSB_BOARD_BU4306)) - return; /* No software txpower adjustment needed */ - - result = phy->ops->recalc_txpower(dev, !!(flags & B43_TXPWR_IGNORE_TSSI)); - if (result == B43_TXPWR_RES_DONE) - return; /* We are done. */ - B43_WARN_ON(result != B43_TXPWR_RES_NEED_ADJUST); - B43_WARN_ON(phy->ops->adjust_txpower == NULL); - - /* We must adjust the transmission power in hardware. - * Schedule b43_phy_txpower_adjust_work(). */ - ieee80211_queue_work(dev->wl->hw, &dev->wl->txpower_adjust_work); -} - -int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset) -{ - const bool is_ofdm = (shm_offset != B43_SHM_SH_TSSI_CCK); - unsigned int a, b, c, d; - unsigned int average; - u32 tmp; - - tmp = b43_shm_read32(dev, B43_SHM_SHARED, shm_offset); - a = tmp & 0xFF; - b = (tmp >> 8) & 0xFF; - c = (tmp >> 16) & 0xFF; - d = (tmp >> 24) & 0xFF; - if (a == 0 || a == B43_TSSI_MAX || - b == 0 || b == B43_TSSI_MAX || - c == 0 || c == B43_TSSI_MAX || - d == 0 || d == B43_TSSI_MAX) - return -ENOENT; - /* The values are OK. Clear them. */ - tmp = B43_TSSI_MAX | (B43_TSSI_MAX << 8) | - (B43_TSSI_MAX << 16) | (B43_TSSI_MAX << 24); - b43_shm_write32(dev, B43_SHM_SHARED, shm_offset, tmp); - - if (is_ofdm) { - a = (a + 32) & 0x3F; - b = (b + 32) & 0x3F; - c = (c + 32) & 0x3F; - d = (d + 32) & 0x3F; - } - - /* Get the average of the values with 0.5 added to each value. */ - average = (a + b + c + d + 2) / 4; - if (is_ofdm) { - /* Adjust for CCK-boost */ - if (b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF1) - & B43_HF_CCKBOOST) - average = (average >= 13) ? (average - 13) : 0; - } - - return average; -} - -void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on) -{ - b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4); -} - - -bool b43_is_40mhz(struct b43_wldev *dev) -{ - return dev->phy.chandef->width == NL80211_CHAN_WIDTH_40; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */ -void b43_phy_force_clock(struct b43_wldev *dev, bool force) -{ - u32 tmp; - - WARN_ON(dev->phy.type != B43_PHYTYPE_N && - dev->phy.type != B43_PHYTYPE_HT && - dev->phy.type != B43_PHYTYPE_AC); - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - if (force) - tmp |= BCMA_IOCTL_FGC; - else - tmp &= ~BCMA_IOCTL_FGC; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); - if (force) - tmp |= SSB_TMSLOW_FGC; - else - tmp &= ~SSB_TMSLOW_FGC; - ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); - break; -#endif - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/Cordic */ -struct b43_c32 b43_cordic(int theta) -{ - static const u32 arctg[] = { - 2949120, 1740967, 919879, 466945, 234379, 117304, - 58666, 29335, 14668, 7334, 3667, 1833, - 917, 458, 229, 115, 57, 29, - }; - u8 i; - s32 tmp; - s8 signx = 1; - u32 angle = 0; - struct b43_c32 ret = { .i = 39797, .q = 0, }; - - while (theta > (180 << 16)) - theta -= (360 << 16); - while (theta < -(180 << 16)) - theta += (360 << 16); - - if (theta > (90 << 16)) { - theta -= (180 << 16); - signx = -1; - } else if (theta < -(90 << 16)) { - theta += (180 << 16); - signx = -1; - } - - for (i = 0; i <= 17; i++) { - if (theta > angle) { - tmp = ret.i - (ret.q >> i); - ret.q += ret.i >> i; - ret.i = tmp; - angle += arctg[i]; - } else { - tmp = ret.i + (ret.q >> i); - ret.q -= ret.i >> i; - ret.i = tmp; - angle -= arctg[i]; - } - } - - ret.i *= signx; - ret.q *= signx; - - return ret; -} diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h deleted file mode 100644 index 78d86526799e..000000000000 --- a/drivers/net/wireless/b43/phy_common.h +++ /dev/null @@ -1,457 +0,0 @@ -#ifndef LINUX_B43_PHY_COMMON_H_ -#define LINUX_B43_PHY_COMMON_H_ - -#include -#include - -struct b43_wldev; - -/* Complex number using 2 32-bit signed integers */ -struct b43_c32 { s32 i, q; }; - -#define CORDIC_CONVERT(value) (((value) >= 0) ? \ - ((((value) >> 15) + 1) >> 1) : \ - -((((-(value)) >> 15) + 1) >> 1)) - -/* PHY register routing bits */ -#define B43_PHYROUTE 0x0C00 /* PHY register routing bits mask */ -#define B43_PHYROUTE_BASE 0x0000 /* Base registers */ -#define B43_PHYROUTE_OFDM_GPHY 0x0400 /* OFDM register routing for G-PHYs */ -#define B43_PHYROUTE_EXT_GPHY 0x0800 /* Extended G-PHY registers */ -#define B43_PHYROUTE_N_BMODE 0x0C00 /* N-PHY BMODE registers */ - -/* CCK (B-PHY) registers. */ -#define B43_PHY_CCK(reg) ((reg) | B43_PHYROUTE_BASE) -/* N-PHY registers. */ -#define B43_PHY_N(reg) ((reg) | B43_PHYROUTE_BASE) -/* N-PHY BMODE registers. */ -#define B43_PHY_N_BMODE(reg) ((reg) | B43_PHYROUTE_N_BMODE) -/* OFDM (A-PHY) registers. */ -#define B43_PHY_OFDM(reg) ((reg) | B43_PHYROUTE_OFDM_GPHY) -/* Extended G-PHY registers. */ -#define B43_PHY_EXTG(reg) ((reg) | B43_PHYROUTE_EXT_GPHY) - - -/* Masks for the PHY versioning registers. */ -#define B43_PHYVER_ANALOG 0xF000 -#define B43_PHYVER_ANALOG_SHIFT 12 -#define B43_PHYVER_TYPE 0x0F00 -#define B43_PHYVER_TYPE_SHIFT 8 -#define B43_PHYVER_VERSION 0x00FF - -/* PHY writes need to be flushed if we reach limit */ -#define B43_MAX_WRITES_IN_ROW 24 - -/** - * enum b43_interference_mitigation - Interference Mitigation mode - * - * @B43_INTERFMODE_NONE: Disabled - * @B43_INTERFMODE_NONWLAN: Non-WLAN Interference Mitigation - * @B43_INTERFMODE_MANUALWLAN: WLAN Interference Mitigation - * @B43_INTERFMODE_AUTOWLAN: Automatic WLAN Interference Mitigation - */ -enum b43_interference_mitigation { - B43_INTERFMODE_NONE, - B43_INTERFMODE_NONWLAN, - B43_INTERFMODE_MANUALWLAN, - B43_INTERFMODE_AUTOWLAN, -}; - -/* Antenna identifiers */ -enum { - B43_ANTENNA0 = 0, /* Antenna 0 */ - B43_ANTENNA1 = 1, /* Antenna 1 */ - B43_ANTENNA_AUTO0 = 2, /* Automatic, starting with antenna 0 */ - B43_ANTENNA_AUTO1 = 3, /* Automatic, starting with antenna 1 */ - B43_ANTENNA2 = 4, - B43_ANTENNA3 = 8, - - B43_ANTENNA_AUTO = B43_ANTENNA_AUTO0, - B43_ANTENNA_DEFAULT = B43_ANTENNA_AUTO, -}; - -/** - * enum b43_txpwr_result - Return value for the recalc_txpower PHY op. - * - * @B43_TXPWR_RES_NEED_ADJUST: Values changed. Hardware adjustment is needed. - * @B43_TXPWR_RES_DONE: No more work to do. Everything is done. - */ -enum b43_txpwr_result { - B43_TXPWR_RES_NEED_ADJUST, - B43_TXPWR_RES_DONE, -}; - -/** - * struct b43_phy_operations - Function pointers for PHY ops. - * - * @allocate: Allocate and initialise the PHY data structures. - * Must not be NULL. - * @free: Destroy and free the PHY data structures. - * Must not be NULL. - * - * @prepare_structs: Prepare the PHY data structures. - * The data structures allocated in @allocate are - * initialized here. - * Must not be NULL. - * @prepare_hardware: Prepare the PHY. This is called before b43_chip_init to - * do some early early PHY hardware init. - * Can be NULL, if not required. - * @init: Initialize the PHY. - * Must not be NULL. - * @exit: Shutdown the PHY. - * Can be NULL, if not required. - * - * @phy_read: Read from a PHY register. - * Must not be NULL. - * @phy_write: Write to a PHY register. - * Must not be NULL. - * @phy_maskset: Maskset a PHY register, taking shortcuts. - * If it is NULL, a generic algorithm is used. - * @radio_read: Read from a Radio register. - * Must not be NULL. - * @radio_write: Write to a Radio register. - * Must not be NULL. - * - * @supports_hwpctl: Returns a boolean whether Hardware Power Control - * is supported or not. - * If NULL, hwpctl is assumed to be never supported. - * @software_rfkill: Turn the radio ON or OFF. - * Possible state values are - * RFKILL_STATE_SOFT_BLOCKED or - * RFKILL_STATE_UNBLOCKED - * Must not be NULL. - * @switch_analog: Turn the Analog on/off. - * Must not be NULL. - * @switch_channel: Switch the radio to another channel. - * Must not be NULL. - * @get_default_chan: Just returns the default channel number. - * Must not be NULL. - * @set_rx_antenna: Set the antenna used for RX. - * Can be NULL, if not supported. - * @interf_mitigation: Switch the Interference Mitigation mode. - * Can be NULL, if not supported. - * - * @recalc_txpower: Recalculate the transmission power parameters. - * This callback has to recalculate the TX power settings, - * but does not need to write them to the hardware, yet. - * Returns enum b43_txpwr_result to indicate whether the hardware - * needs to be adjusted. - * If B43_TXPWR_NEED_ADJUST is returned, @adjust_txpower - * will be called later. - * If the parameter "ignore_tssi" is true, the TSSI values should - * be ignored and a recalculation of the power settings should be - * done even if the TSSI values did not change. - * This function may sleep, but should not. - * Must not be NULL. - * @adjust_txpower: Write the previously calculated TX power settings - * (from @recalc_txpower) to the hardware. - * This function may sleep. - * Can be NULL, if (and ONLY if) @recalc_txpower _always_ - * returns B43_TXPWR_RES_DONE. - * - * @pwork_15sec: Periodic work. Called every 15 seconds. - * Can be NULL, if not required. - * @pwork_60sec: Periodic work. Called every 60 seconds. - * Can be NULL, if not required. - */ -struct b43_phy_operations { - /* Initialisation */ - int (*allocate)(struct b43_wldev *dev); - void (*free)(struct b43_wldev *dev); - void (*prepare_structs)(struct b43_wldev *dev); - int (*prepare_hardware)(struct b43_wldev *dev); - int (*init)(struct b43_wldev *dev); - void (*exit)(struct b43_wldev *dev); - - /* Register access */ - u16 (*phy_read)(struct b43_wldev *dev, u16 reg); - void (*phy_write)(struct b43_wldev *dev, u16 reg, u16 value); - void (*phy_maskset)(struct b43_wldev *dev, u16 reg, u16 mask, u16 set); - u16 (*radio_read)(struct b43_wldev *dev, u16 reg); - void (*radio_write)(struct b43_wldev *dev, u16 reg, u16 value); - - /* Radio */ - bool (*supports_hwpctl)(struct b43_wldev *dev); - void (*software_rfkill)(struct b43_wldev *dev, bool blocked); - void (*switch_analog)(struct b43_wldev *dev, bool on); - int (*switch_channel)(struct b43_wldev *dev, unsigned int new_channel); - unsigned int (*get_default_chan)(struct b43_wldev *dev); - void (*set_rx_antenna)(struct b43_wldev *dev, int antenna); - int (*interf_mitigation)(struct b43_wldev *dev, - enum b43_interference_mitigation new_mode); - - /* Transmission power adjustment */ - enum b43_txpwr_result (*recalc_txpower)(struct b43_wldev *dev, - bool ignore_tssi); - void (*adjust_txpower)(struct b43_wldev *dev); - - /* Misc */ - void (*pwork_15sec)(struct b43_wldev *dev); - void (*pwork_60sec)(struct b43_wldev *dev); -}; - -struct b43_phy_a; -struct b43_phy_g; -struct b43_phy_n; -struct b43_phy_lp; -struct b43_phy_ht; -struct b43_phy_lcn; - -struct b43_phy { - /* Hardware operation callbacks. */ - const struct b43_phy_operations *ops; - - /* Most hardware context information is stored in the standard- - * specific data structures pointed to by the pointers below. - * Only one of them is valid (the currently enabled PHY). */ -#ifdef CONFIG_B43_DEBUG - /* No union for debug build to force NULL derefs in buggy code. */ - struct { -#else - union { -#endif - /* A-PHY specific information */ - struct b43_phy_a *a; - /* G-PHY specific information */ - struct b43_phy_g *g; - /* N-PHY specific information */ - struct b43_phy_n *n; - /* LP-PHY specific information */ - struct b43_phy_lp *lp; - /* HT-PHY specific information */ - struct b43_phy_ht *ht; - /* LCN-PHY specific information */ - struct b43_phy_lcn *lcn; - /* AC-PHY specific information */ - struct b43_phy_ac *ac; - }; - - /* Band support flags. */ - bool supports_2ghz; - bool supports_5ghz; - - /* Is GMODE (2 GHz mode) bit enabled? */ - bool gmode; - - /* After power reset full init has to be performed */ - bool do_full_init; - - /* Analog Type */ - u8 analog; - /* B43_PHYTYPE_ */ - u8 type; - /* PHY revision number. */ - u8 rev; - - /* Count writes since last read */ - u8 writes_counter; - - /* Radio versioning */ - u16 radio_manuf; /* Radio manufacturer */ - u16 radio_ver; /* Radio version */ - u8 radio_rev; /* Radio revision */ - - /* Software state of the radio */ - bool radio_on; - - /* Desired TX power level (in dBm). - * This is set by the user and adjusted in b43_phy_xmitpower(). */ - int desired_txpower; - - /* Hardware Power Control enabled? */ - bool hardware_power_control; - - /* The time (in absolute jiffies) when the next TX power output - * check is needed. */ - unsigned long next_txpwr_check_time; - - /* Current channel */ - struct cfg80211_chan_def *chandef; - unsigned int channel; - - /* PHY TX errors counter. */ - atomic_t txerr_cnt; - -#ifdef CONFIG_B43_DEBUG - /* PHY registers locked (w.r.t. firmware) */ - bool phy_locked; - /* Radio registers locked (w.r.t. firmware) */ - bool radio_locked; -#endif /* B43_DEBUG */ -}; - - -/** - * b43_phy_allocate - Allocate PHY structs - * Allocate the PHY data structures, based on the current dev->phy.type - */ -int b43_phy_allocate(struct b43_wldev *dev); - -/** - * b43_phy_free - Free PHY structs - */ -void b43_phy_free(struct b43_wldev *dev); - -/** - * b43_phy_init - Initialise the PHY - */ -int b43_phy_init(struct b43_wldev *dev); - -/** - * b43_phy_exit - Cleanup PHY - */ -void b43_phy_exit(struct b43_wldev *dev); - -/** - * b43_has_hardware_pctl - Hardware Power Control supported? - * Returns a boolean, whether hardware power control is supported. - */ -bool b43_has_hardware_pctl(struct b43_wldev *dev); - -/** - * b43_phy_read - 16bit PHY register read access - */ -u16 b43_phy_read(struct b43_wldev *dev, u16 reg); - -/** - * b43_phy_write - 16bit PHY register write access - */ -void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value); - -/** - * b43_phy_copy - copy contents of 16bit PHY register to another - */ -void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg); - -/** - * b43_phy_mask - Mask a PHY register with a mask - */ -void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask); - -/** - * b43_phy_set - OR a PHY register with a bitmap - */ -void b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set); - -/** - * b43_phy_maskset - Mask and OR a PHY register with a mask and bitmap - */ -void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set); - -/** - * b43_radio_read - 16bit Radio register read access - */ -u16 b43_radio_read(struct b43_wldev *dev, u16 reg); -#define b43_radio_read16 b43_radio_read /* DEPRECATED */ - -/** - * b43_radio_write - 16bit Radio register write access - */ -void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value); -#define b43_radio_write16 b43_radio_write /* DEPRECATED */ - -/** - * b43_radio_mask - Mask a 16bit radio register with a mask - */ -void b43_radio_mask(struct b43_wldev *dev, u16 offset, u16 mask); - -/** - * b43_radio_set - OR a 16bit radio register with a bitmap - */ -void b43_radio_set(struct b43_wldev *dev, u16 offset, u16 set); - -/** - * b43_radio_maskset - Mask and OR a radio register with a mask and bitmap - */ -void b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set); - -/** - * b43_radio_wait_value - Waits for a given value in masked register read - */ -bool b43_radio_wait_value(struct b43_wldev *dev, u16 offset, u16 mask, - u16 value, int delay, int timeout); - -/** - * b43_radio_lock - Lock firmware radio register access - */ -void b43_radio_lock(struct b43_wldev *dev); - -/** - * b43_radio_unlock - Unlock firmware radio register access - */ -void b43_radio_unlock(struct b43_wldev *dev); - -/** - * b43_phy_lock - Lock firmware PHY register access - */ -void b43_phy_lock(struct b43_wldev *dev); - -/** - * b43_phy_unlock - Unlock firmware PHY register access - */ -void b43_phy_unlock(struct b43_wldev *dev); - -void b43_phy_put_into_reset(struct b43_wldev *dev); -void b43_phy_take_out_of_reset(struct b43_wldev *dev); - -/** - * b43_switch_channel - Switch to another channel - */ -int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel); - -/** - * b43_software_rfkill - Turn the radio ON or OFF in software. - */ -void b43_software_rfkill(struct b43_wldev *dev, bool blocked); - -/** - * b43_phy_txpower_check - Check TX power output. - * - * Compare the current TX power output to the desired power emission - * and schedule an adjustment in case it mismatches. - * - * @flags: OR'ed enum b43_phy_txpower_check_flags flags. - * See the docs below. - */ -void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags); -/** - * enum b43_phy_txpower_check_flags - Flags for b43_phy_txpower_check() - * - * @B43_TXPWR_IGNORE_TIME: Ignore the schedule time and force-redo - * the check now. - * @B43_TXPWR_IGNORE_TSSI: Redo the recalculation, even if the average - * TSSI did not change. - */ -enum b43_phy_txpower_check_flags { - B43_TXPWR_IGNORE_TIME = (1 << 0), - B43_TXPWR_IGNORE_TSSI = (1 << 1), -}; - -struct work_struct; -void b43_phy_txpower_adjust_work(struct work_struct *work); - -/** - * b43_phy_shm_tssi_read - Read the average of the last 4 TSSI from SHM. - * - * @shm_offset: The SHM address to read the values from. - * - * Returns the average of the 4 TSSI values, or a negative error code. - */ -int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset); - -/** - * b43_phy_switch_analog_generic - Generic PHY operation for switching the Analog. - * - * It does the switching based on the PHY0 core register. - * Do _not_ call this directly. Only use it as a switch_analog callback - * for struct b43_phy_operations. - */ -void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on); - -bool b43_is_40mhz(struct b43_wldev *dev); - -void b43_phy_force_clock(struct b43_wldev *dev, bool force); - -struct b43_c32 b43_cordic(int theta); - -#endif /* LINUX_B43_PHY_COMMON_H_ */ diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c deleted file mode 100644 index 462310e6e88f..000000000000 --- a/drivers/net/wireless/b43/phy_g.c +++ /dev/null @@ -1,3055 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11g PHY driver - - Copyright (c) 2005 Martin Langer , - Copyright (c) 2005-2007 Stefano Brivio - Copyright (c) 2005-2008 Michael Buesch - Copyright (c) 2005, 2006 Danny van Dyk - Copyright (c) 2005, 2006 Andreas Jaggi - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "phy_g.h" -#include "phy_common.h" -#include "lo.h" -#include "main.h" - -#include -#include - - -static const s8 b43_tssi2dbm_g_table[] = { - 77, 77, 77, 76, - 76, 76, 75, 75, - 74, 74, 73, 73, - 73, 72, 72, 71, - 71, 70, 70, 69, - 68, 68, 67, 67, - 66, 65, 65, 64, - 63, 63, 62, 61, - 60, 59, 58, 57, - 56, 55, 54, 53, - 52, 50, 49, 47, - 45, 43, 40, 37, - 33, 28, 22, 14, - 5, -7, -20, -20, - -20, -20, -20, -20, - -20, -20, -20, -20, -}; - -static const u8 b43_radio_channel_codes_bg[] = { - 12, 17, 22, 27, - 32, 37, 42, 47, - 52, 57, 62, 67, - 72, 84, -}; - - -static void b43_calc_nrssi_threshold(struct b43_wldev *dev); - - -#define bitrev4(tmp) (bitrev8(tmp) >> 4) - - -/* Get the freq, as it has to be written to the device. */ -static inline u16 channel2freq_bg(u8 channel) -{ - B43_WARN_ON(!(channel >= 1 && channel <= 14)); - - return b43_radio_channel_codes_bg[channel - 1]; -} - -static void generate_rfatt_list(struct b43_wldev *dev, - struct b43_rfatt_list *list) -{ - struct b43_phy *phy = &dev->phy; - - /* APHY.rev < 5 || GPHY.rev < 6 */ - static const struct b43_rfatt rfatt_0[] = { - {.att = 3,.with_padmix = 0,}, - {.att = 1,.with_padmix = 0,}, - {.att = 5,.with_padmix = 0,}, - {.att = 7,.with_padmix = 0,}, - {.att = 9,.with_padmix = 0,}, - {.att = 2,.with_padmix = 0,}, - {.att = 0,.with_padmix = 0,}, - {.att = 4,.with_padmix = 0,}, - {.att = 6,.with_padmix = 0,}, - {.att = 8,.with_padmix = 0,}, - {.att = 1,.with_padmix = 1,}, - {.att = 2,.with_padmix = 1,}, - {.att = 3,.with_padmix = 1,}, - {.att = 4,.with_padmix = 1,}, - }; - /* Radio.rev == 8 && Radio.version == 0x2050 */ - static const struct b43_rfatt rfatt_1[] = { - {.att = 2,.with_padmix = 1,}, - {.att = 4,.with_padmix = 1,}, - {.att = 6,.with_padmix = 1,}, - {.att = 8,.with_padmix = 1,}, - {.att = 10,.with_padmix = 1,}, - {.att = 12,.with_padmix = 1,}, - {.att = 14,.with_padmix = 1,}, - }; - /* Otherwise */ - static const struct b43_rfatt rfatt_2[] = { - {.att = 0,.with_padmix = 1,}, - {.att = 2,.with_padmix = 1,}, - {.att = 4,.with_padmix = 1,}, - {.att = 6,.with_padmix = 1,}, - {.att = 8,.with_padmix = 1,}, - {.att = 9,.with_padmix = 1,}, - {.att = 9,.with_padmix = 1,}, - }; - - if (!b43_has_hardware_pctl(dev)) { - /* Software pctl */ - list->list = rfatt_0; - list->len = ARRAY_SIZE(rfatt_0); - list->min_val = 0; - list->max_val = 9; - return; - } - if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { - /* Hardware pctl */ - list->list = rfatt_1; - list->len = ARRAY_SIZE(rfatt_1); - list->min_val = 0; - list->max_val = 14; - return; - } - /* Hardware pctl */ - list->list = rfatt_2; - list->len = ARRAY_SIZE(rfatt_2); - list->min_val = 0; - list->max_val = 9; -} - -static void generate_bbatt_list(struct b43_wldev *dev, - struct b43_bbatt_list *list) -{ - static const struct b43_bbatt bbatt_0[] = { - {.att = 0,}, - {.att = 1,}, - {.att = 2,}, - {.att = 3,}, - {.att = 4,}, - {.att = 5,}, - {.att = 6,}, - {.att = 7,}, - {.att = 8,}, - }; - - list->list = bbatt_0; - list->len = ARRAY_SIZE(bbatt_0); - list->min_val = 0; - list->max_val = 8; -} - -static void b43_shm_clear_tssi(struct b43_wldev *dev) -{ - b43_shm_write16(dev, B43_SHM_SHARED, 0x0058, 0x7F7F); - b43_shm_write16(dev, B43_SHM_SHARED, 0x005a, 0x7F7F); - b43_shm_write16(dev, B43_SHM_SHARED, 0x0070, 0x7F7F); - b43_shm_write16(dev, B43_SHM_SHARED, 0x0072, 0x7F7F); -} - -/* Synthetic PU workaround */ -static void b43_synth_pu_workaround(struct b43_wldev *dev, u8 channel) -{ - struct b43_phy *phy = &dev->phy; - - might_sleep(); - - if (phy->radio_ver != 0x2050 || phy->radio_rev >= 6) { - /* We do not need the workaround. */ - return; - } - - if (channel <= 10) { - b43_write16(dev, B43_MMIO_CHANNEL, - channel2freq_bg(channel + 4)); - } else { - b43_write16(dev, B43_MMIO_CHANNEL, channel2freq_bg(1)); - } - msleep(1); - b43_write16(dev, B43_MMIO_CHANNEL, channel2freq_bg(channel)); -} - -/* Set the baseband attenuation value on chip. */ -void b43_gphy_set_baseband_attenuation(struct b43_wldev *dev, - u16 baseband_attenuation) -{ - struct b43_phy *phy = &dev->phy; - - if (phy->analog == 0) { - b43_write16(dev, B43_MMIO_PHY0, (b43_read16(dev, B43_MMIO_PHY0) - & 0xFFF0) | - baseband_attenuation); - } else if (phy->analog > 1) { - b43_phy_maskset(dev, B43_PHY_DACCTL, 0xFFC3, (baseband_attenuation << 2)); - } else { - b43_phy_maskset(dev, B43_PHY_DACCTL, 0xFF87, (baseband_attenuation << 3)); - } -} - -/* Adjust the transmission power output (G-PHY) */ -static void b43_set_txpower_g(struct b43_wldev *dev, - const struct b43_bbatt *bbatt, - const struct b43_rfatt *rfatt, u8 tx_control) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_txpower_lo_control *lo = gphy->lo_control; - u16 bb, rf; - u16 tx_bias, tx_magn; - - bb = bbatt->att; - rf = rfatt->att; - tx_bias = lo->tx_bias; - tx_magn = lo->tx_magn; - if (unlikely(tx_bias == 0xFF)) - tx_bias = 0; - - /* Save the values for later. Use memmove, because it's valid - * to pass &gphy->rfatt as rfatt pointer argument. Same for bbatt. */ - gphy->tx_control = tx_control; - memmove(&gphy->rfatt, rfatt, sizeof(*rfatt)); - gphy->rfatt.with_padmix = !!(tx_control & B43_TXCTL_TXMIX); - memmove(&gphy->bbatt, bbatt, sizeof(*bbatt)); - - if (b43_debug(dev, B43_DBG_XMITPOWER)) { - b43dbg(dev->wl, "Tuning TX-power to bbatt(%u), " - "rfatt(%u), tx_control(0x%02X), " - "tx_bias(0x%02X), tx_magn(0x%02X)\n", - bb, rf, tx_control, tx_bias, tx_magn); - } - - b43_gphy_set_baseband_attenuation(dev, bb); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_RFATT, rf); - if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { - b43_radio_write16(dev, 0x43, - (rf & 0x000F) | (tx_control & 0x0070)); - } else { - b43_radio_maskset(dev, 0x43, 0xFFF0, (rf & 0x000F)); - b43_radio_maskset(dev, 0x52, ~0x0070, (tx_control & 0x0070)); - } - if (has_tx_magnification(phy)) { - b43_radio_write16(dev, 0x52, tx_magn | tx_bias); - } else { - b43_radio_maskset(dev, 0x52, 0xFFF0, (tx_bias & 0x000F)); - } - b43_lo_g_adjust(dev); -} - -/* GPHY_TSSI_Power_Lookup_Table_Init */ -static void b43_gphy_tssi_power_lt_init(struct b43_wldev *dev) -{ - struct b43_phy_g *gphy = dev->phy.g; - int i; - u16 value; - - for (i = 0; i < 32; i++) - b43_ofdmtab_write16(dev, 0x3C20, i, gphy->tssi2dbm[i]); - for (i = 32; i < 64; i++) - b43_ofdmtab_write16(dev, 0x3C00, i - 32, gphy->tssi2dbm[i]); - for (i = 0; i < 64; i += 2) { - value = (u16) gphy->tssi2dbm[i]; - value |= ((u16) gphy->tssi2dbm[i + 1]) << 8; - b43_phy_write(dev, 0x380 + (i / 2), value); - } -} - -/* GPHY_Gain_Lookup_Table_Init */ -static void b43_gphy_gain_lt_init(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_txpower_lo_control *lo = gphy->lo_control; - u16 nr_written = 0; - u16 tmp; - u8 rf, bb; - - for (rf = 0; rf < lo->rfatt_list.len; rf++) { - for (bb = 0; bb < lo->bbatt_list.len; bb++) { - if (nr_written >= 0x40) - return; - tmp = lo->bbatt_list.list[bb].att; - tmp <<= 8; - if (phy->radio_rev == 8) - tmp |= 0x50; - else - tmp |= 0x40; - tmp |= lo->rfatt_list.list[rf].att; - b43_phy_write(dev, 0x3C0 + nr_written, tmp); - nr_written++; - } - } -} - -static void b43_set_all_gains(struct b43_wldev *dev, - s16 first, s16 second, s16 third) -{ - struct b43_phy *phy = &dev->phy; - u16 i; - u16 start = 0x08, end = 0x18; - u16 tmp; - u16 table; - - if (phy->rev <= 1) { - start = 0x10; - end = 0x20; - } - - table = B43_OFDMTAB_GAINX; - if (phy->rev <= 1) - table = B43_OFDMTAB_GAINX_R1; - for (i = 0; i < 4; i++) - b43_ofdmtab_write16(dev, table, i, first); - - for (i = start; i < end; i++) - b43_ofdmtab_write16(dev, table, i, second); - - if (third != -1) { - tmp = ((u16) third << 14) | ((u16) third << 6); - b43_phy_maskset(dev, 0x04A0, 0xBFBF, tmp); - b43_phy_maskset(dev, 0x04A1, 0xBFBF, tmp); - b43_phy_maskset(dev, 0x04A2, 0xBFBF, tmp); - } - b43_dummy_transmission(dev, false, true); -} - -static void b43_set_original_gains(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - u16 i, tmp; - u16 table; - u16 start = 0x0008, end = 0x0018; - - if (phy->rev <= 1) { - start = 0x0010; - end = 0x0020; - } - - table = B43_OFDMTAB_GAINX; - if (phy->rev <= 1) - table = B43_OFDMTAB_GAINX_R1; - for (i = 0; i < 4; i++) { - tmp = (i & 0xFFFC); - tmp |= (i & 0x0001) << 1; - tmp |= (i & 0x0002) >> 1; - - b43_ofdmtab_write16(dev, table, i, tmp); - } - - for (i = start; i < end; i++) - b43_ofdmtab_write16(dev, table, i, i - start); - - b43_phy_maskset(dev, 0x04A0, 0xBFBF, 0x4040); - b43_phy_maskset(dev, 0x04A1, 0xBFBF, 0x4040); - b43_phy_maskset(dev, 0x04A2, 0xBFBF, 0x4000); - b43_dummy_transmission(dev, false, true); -} - -/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ -static void b43_nrssi_hw_write(struct b43_wldev *dev, u16 offset, s16 val) -{ - b43_phy_write(dev, B43_PHY_NRSSILT_CTRL, offset); - b43_phy_write(dev, B43_PHY_NRSSILT_DATA, (u16) val); -} - -/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ -static s16 b43_nrssi_hw_read(struct b43_wldev *dev, u16 offset) -{ - u16 val; - - b43_phy_write(dev, B43_PHY_NRSSILT_CTRL, offset); - val = b43_phy_read(dev, B43_PHY_NRSSILT_DATA); - - return (s16) val; -} - -/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ -static void b43_nrssi_hw_update(struct b43_wldev *dev, u16 val) -{ - u16 i; - s16 tmp; - - for (i = 0; i < 64; i++) { - tmp = b43_nrssi_hw_read(dev, i); - tmp -= val; - tmp = clamp_val(tmp, -32, 31); - b43_nrssi_hw_write(dev, i, tmp); - } -} - -/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ -static void b43_nrssi_mem_update(struct b43_wldev *dev) -{ - struct b43_phy_g *gphy = dev->phy.g; - s16 i, delta; - s32 tmp; - - delta = 0x1F - gphy->nrssi[0]; - for (i = 0; i < 64; i++) { - tmp = (i - delta) * gphy->nrssislope; - tmp /= 0x10000; - tmp += 0x3A; - tmp = clamp_val(tmp, 0, 0x3F); - gphy->nrssi_lt[i] = tmp; - } -} - -static void b43_calc_nrssi_offset(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - u16 backup[20] = { 0 }; - s16 v47F; - u16 i; - u16 saved = 0xFFFF; - - backup[0] = b43_phy_read(dev, 0x0001); - backup[1] = b43_phy_read(dev, 0x0811); - backup[2] = b43_phy_read(dev, 0x0812); - if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ - backup[3] = b43_phy_read(dev, 0x0814); - backup[4] = b43_phy_read(dev, 0x0815); - } - backup[5] = b43_phy_read(dev, 0x005A); - backup[6] = b43_phy_read(dev, 0x0059); - backup[7] = b43_phy_read(dev, 0x0058); - backup[8] = b43_phy_read(dev, 0x000A); - backup[9] = b43_phy_read(dev, 0x0003); - backup[10] = b43_radio_read16(dev, 0x007A); - backup[11] = b43_radio_read16(dev, 0x0043); - - b43_phy_mask(dev, 0x0429, 0x7FFF); - b43_phy_maskset(dev, 0x0001, 0x3FFF, 0x4000); - b43_phy_set(dev, 0x0811, 0x000C); - b43_phy_maskset(dev, 0x0812, 0xFFF3, 0x0004); - b43_phy_mask(dev, 0x0802, ~(0x1 | 0x2)); - if (phy->rev >= 6) { - backup[12] = b43_phy_read(dev, 0x002E); - backup[13] = b43_phy_read(dev, 0x002F); - backup[14] = b43_phy_read(dev, 0x080F); - backup[15] = b43_phy_read(dev, 0x0810); - backup[16] = b43_phy_read(dev, 0x0801); - backup[17] = b43_phy_read(dev, 0x0060); - backup[18] = b43_phy_read(dev, 0x0014); - backup[19] = b43_phy_read(dev, 0x0478); - - b43_phy_write(dev, 0x002E, 0); - b43_phy_write(dev, 0x002F, 0); - b43_phy_write(dev, 0x080F, 0); - b43_phy_write(dev, 0x0810, 0); - b43_phy_set(dev, 0x0478, 0x0100); - b43_phy_set(dev, 0x0801, 0x0040); - b43_phy_set(dev, 0x0060, 0x0040); - b43_phy_set(dev, 0x0014, 0x0200); - } - b43_radio_set(dev, 0x007A, 0x0070); - b43_radio_set(dev, 0x007A, 0x0080); - udelay(30); - - v47F = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); - if (v47F >= 0x20) - v47F -= 0x40; - if (v47F == 31) { - for (i = 7; i >= 4; i--) { - b43_radio_write16(dev, 0x007B, i); - udelay(20); - v47F = - (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); - if (v47F >= 0x20) - v47F -= 0x40; - if (v47F < 31 && saved == 0xFFFF) - saved = i; - } - if (saved == 0xFFFF) - saved = 4; - } else { - b43_radio_mask(dev, 0x007A, 0x007F); - if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ - b43_phy_set(dev, 0x0814, 0x0001); - b43_phy_mask(dev, 0x0815, 0xFFFE); - } - b43_phy_set(dev, 0x0811, 0x000C); - b43_phy_set(dev, 0x0812, 0x000C); - b43_phy_set(dev, 0x0811, 0x0030); - b43_phy_set(dev, 0x0812, 0x0030); - b43_phy_write(dev, 0x005A, 0x0480); - b43_phy_write(dev, 0x0059, 0x0810); - b43_phy_write(dev, 0x0058, 0x000D); - if (phy->rev == 0) { - b43_phy_write(dev, 0x0003, 0x0122); - } else { - b43_phy_set(dev, 0x000A, 0x2000); - } - if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ - b43_phy_set(dev, 0x0814, 0x0004); - b43_phy_mask(dev, 0x0815, 0xFFFB); - } - b43_phy_maskset(dev, 0x0003, 0xFF9F, 0x0040); - b43_radio_set(dev, 0x007A, 0x000F); - b43_set_all_gains(dev, 3, 0, 1); - b43_radio_maskset(dev, 0x0043, 0x00F0, 0x000F); - udelay(30); - v47F = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); - if (v47F >= 0x20) - v47F -= 0x40; - if (v47F == -32) { - for (i = 0; i < 4; i++) { - b43_radio_write16(dev, 0x007B, i); - udelay(20); - v47F = - (s16) ((b43_phy_read(dev, 0x047F) >> 8) & - 0x003F); - if (v47F >= 0x20) - v47F -= 0x40; - if (v47F > -31 && saved == 0xFFFF) - saved = i; - } - if (saved == 0xFFFF) - saved = 3; - } else - saved = 0; - } - b43_radio_write16(dev, 0x007B, saved); - - if (phy->rev >= 6) { - b43_phy_write(dev, 0x002E, backup[12]); - b43_phy_write(dev, 0x002F, backup[13]); - b43_phy_write(dev, 0x080F, backup[14]); - b43_phy_write(dev, 0x0810, backup[15]); - } - if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ - b43_phy_write(dev, 0x0814, backup[3]); - b43_phy_write(dev, 0x0815, backup[4]); - } - b43_phy_write(dev, 0x005A, backup[5]); - b43_phy_write(dev, 0x0059, backup[6]); - b43_phy_write(dev, 0x0058, backup[7]); - b43_phy_write(dev, 0x000A, backup[8]); - b43_phy_write(dev, 0x0003, backup[9]); - b43_radio_write16(dev, 0x0043, backup[11]); - b43_radio_write16(dev, 0x007A, backup[10]); - b43_phy_write(dev, 0x0802, b43_phy_read(dev, 0x0802) | 0x1 | 0x2); - b43_phy_set(dev, 0x0429, 0x8000); - b43_set_original_gains(dev); - if (phy->rev >= 6) { - b43_phy_write(dev, 0x0801, backup[16]); - b43_phy_write(dev, 0x0060, backup[17]); - b43_phy_write(dev, 0x0014, backup[18]); - b43_phy_write(dev, 0x0478, backup[19]); - } - b43_phy_write(dev, 0x0001, backup[0]); - b43_phy_write(dev, 0x0812, backup[2]); - b43_phy_write(dev, 0x0811, backup[1]); -} - -static void b43_calc_nrssi_slope(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - u16 backup[18] = { 0 }; - u16 tmp; - s16 nrssi0, nrssi1; - - B43_WARN_ON(phy->type != B43_PHYTYPE_G); - - if (phy->radio_rev >= 9) - return; - if (phy->radio_rev == 8) - b43_calc_nrssi_offset(dev); - - b43_phy_mask(dev, B43_PHY_G_CRS, 0x7FFF); - b43_phy_mask(dev, 0x0802, 0xFFFC); - backup[7] = b43_read16(dev, 0x03E2); - b43_write16(dev, 0x03E2, b43_read16(dev, 0x03E2) | 0x8000); - backup[0] = b43_radio_read16(dev, 0x007A); - backup[1] = b43_radio_read16(dev, 0x0052); - backup[2] = b43_radio_read16(dev, 0x0043); - backup[3] = b43_phy_read(dev, 0x0015); - backup[4] = b43_phy_read(dev, 0x005A); - backup[5] = b43_phy_read(dev, 0x0059); - backup[6] = b43_phy_read(dev, 0x0058); - backup[8] = b43_read16(dev, 0x03E6); - backup[9] = b43_read16(dev, B43_MMIO_CHANNEL_EXT); - if (phy->rev >= 3) { - backup[10] = b43_phy_read(dev, 0x002E); - backup[11] = b43_phy_read(dev, 0x002F); - backup[12] = b43_phy_read(dev, 0x080F); - backup[13] = b43_phy_read(dev, B43_PHY_G_LO_CONTROL); - backup[14] = b43_phy_read(dev, 0x0801); - backup[15] = b43_phy_read(dev, 0x0060); - backup[16] = b43_phy_read(dev, 0x0014); - backup[17] = b43_phy_read(dev, 0x0478); - b43_phy_write(dev, 0x002E, 0); - b43_phy_write(dev, B43_PHY_G_LO_CONTROL, 0); - switch (phy->rev) { - case 4: - case 6: - case 7: - b43_phy_set(dev, 0x0478, 0x0100); - b43_phy_set(dev, 0x0801, 0x0040); - break; - case 3: - case 5: - b43_phy_mask(dev, 0x0801, 0xFFBF); - break; - } - b43_phy_set(dev, 0x0060, 0x0040); - b43_phy_set(dev, 0x0014, 0x0200); - } - b43_radio_set(dev, 0x007A, 0x0070); - b43_set_all_gains(dev, 0, 8, 0); - b43_radio_mask(dev, 0x007A, 0x00F7); - if (phy->rev >= 2) { - b43_phy_maskset(dev, 0x0811, 0xFFCF, 0x0030); - b43_phy_maskset(dev, 0x0812, 0xFFCF, 0x0010); - } - b43_radio_set(dev, 0x007A, 0x0080); - udelay(20); - - nrssi0 = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); - if (nrssi0 >= 0x0020) - nrssi0 -= 0x0040; - - b43_radio_mask(dev, 0x007A, 0x007F); - if (phy->rev >= 2) { - b43_phy_maskset(dev, 0x0003, 0xFF9F, 0x0040); - } - - b43_write16(dev, B43_MMIO_CHANNEL_EXT, - b43_read16(dev, B43_MMIO_CHANNEL_EXT) - | 0x2000); - b43_radio_set(dev, 0x007A, 0x000F); - b43_phy_write(dev, 0x0015, 0xF330); - if (phy->rev >= 2) { - b43_phy_maskset(dev, 0x0812, 0xFFCF, 0x0020); - b43_phy_maskset(dev, 0x0811, 0xFFCF, 0x0020); - } - - b43_set_all_gains(dev, 3, 0, 1); - if (phy->radio_rev == 8) { - b43_radio_write16(dev, 0x0043, 0x001F); - } else { - tmp = b43_radio_read16(dev, 0x0052) & 0xFF0F; - b43_radio_write16(dev, 0x0052, tmp | 0x0060); - tmp = b43_radio_read16(dev, 0x0043) & 0xFFF0; - b43_radio_write16(dev, 0x0043, tmp | 0x0009); - } - b43_phy_write(dev, 0x005A, 0x0480); - b43_phy_write(dev, 0x0059, 0x0810); - b43_phy_write(dev, 0x0058, 0x000D); - udelay(20); - nrssi1 = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); - if (nrssi1 >= 0x0020) - nrssi1 -= 0x0040; - if (nrssi0 == nrssi1) - gphy->nrssislope = 0x00010000; - else - gphy->nrssislope = 0x00400000 / (nrssi0 - nrssi1); - if (nrssi0 >= -4) { - gphy->nrssi[0] = nrssi1; - gphy->nrssi[1] = nrssi0; - } - if (phy->rev >= 3) { - b43_phy_write(dev, 0x002E, backup[10]); - b43_phy_write(dev, 0x002F, backup[11]); - b43_phy_write(dev, 0x080F, backup[12]); - b43_phy_write(dev, B43_PHY_G_LO_CONTROL, backup[13]); - } - if (phy->rev >= 2) { - b43_phy_mask(dev, 0x0812, 0xFFCF); - b43_phy_mask(dev, 0x0811, 0xFFCF); - } - - b43_radio_write16(dev, 0x007A, backup[0]); - b43_radio_write16(dev, 0x0052, backup[1]); - b43_radio_write16(dev, 0x0043, backup[2]); - b43_write16(dev, 0x03E2, backup[7]); - b43_write16(dev, 0x03E6, backup[8]); - b43_write16(dev, B43_MMIO_CHANNEL_EXT, backup[9]); - b43_phy_write(dev, 0x0015, backup[3]); - b43_phy_write(dev, 0x005A, backup[4]); - b43_phy_write(dev, 0x0059, backup[5]); - b43_phy_write(dev, 0x0058, backup[6]); - b43_synth_pu_workaround(dev, phy->channel); - b43_phy_set(dev, 0x0802, (0x0001 | 0x0002)); - b43_set_original_gains(dev); - b43_phy_set(dev, B43_PHY_G_CRS, 0x8000); - if (phy->rev >= 3) { - b43_phy_write(dev, 0x0801, backup[14]); - b43_phy_write(dev, 0x0060, backup[15]); - b43_phy_write(dev, 0x0014, backup[16]); - b43_phy_write(dev, 0x0478, backup[17]); - } - b43_nrssi_mem_update(dev); - b43_calc_nrssi_threshold(dev); -} - -static void b43_calc_nrssi_threshold(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - s32 a, b; - s16 tmp16; - u16 tmp_u16; - - B43_WARN_ON(phy->type != B43_PHYTYPE_G); - - if (!phy->gmode || - !(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI)) { - tmp16 = b43_nrssi_hw_read(dev, 0x20); - if (tmp16 >= 0x20) - tmp16 -= 0x40; - if (tmp16 < 3) { - b43_phy_maskset(dev, 0x048A, 0xF000, 0x09EB); - } else { - b43_phy_maskset(dev, 0x048A, 0xF000, 0x0AED); - } - } else { - if (gphy->interfmode == B43_INTERFMODE_NONWLAN) { - a = 0xE; - b = 0xA; - } else if (!gphy->aci_wlan_automatic && gphy->aci_enable) { - a = 0x13; - b = 0x12; - } else { - a = 0xE; - b = 0x11; - } - - a = a * (gphy->nrssi[1] - gphy->nrssi[0]); - a += (gphy->nrssi[0] << 6); - if (a < 32) - a += 31; - else - a += 32; - a = a >> 6; - a = clamp_val(a, -31, 31); - - b = b * (gphy->nrssi[1] - gphy->nrssi[0]); - b += (gphy->nrssi[0] << 6); - if (b < 32) - b += 31; - else - b += 32; - b = b >> 6; - b = clamp_val(b, -31, 31); - - tmp_u16 = b43_phy_read(dev, 0x048A) & 0xF000; - tmp_u16 |= ((u32) b & 0x0000003F); - tmp_u16 |= (((u32) a & 0x0000003F) << 6); - b43_phy_write(dev, 0x048A, tmp_u16); - } -} - -/* Stack implementation to save/restore values from the - * interference mitigation code. - * It is save to restore values in random order. - */ -static void _stack_save(u32 *_stackptr, size_t *stackidx, - u8 id, u16 offset, u16 value) -{ - u32 *stackptr = &(_stackptr[*stackidx]); - - B43_WARN_ON(offset & 0xF000); - B43_WARN_ON(id & 0xF0); - *stackptr = offset; - *stackptr |= ((u32) id) << 12; - *stackptr |= ((u32) value) << 16; - (*stackidx)++; - B43_WARN_ON(*stackidx >= B43_INTERFSTACK_SIZE); -} - -static u16 _stack_restore(u32 *stackptr, u8 id, u16 offset) -{ - size_t i; - - B43_WARN_ON(offset & 0xF000); - B43_WARN_ON(id & 0xF0); - for (i = 0; i < B43_INTERFSTACK_SIZE; i++, stackptr++) { - if ((*stackptr & 0x00000FFF) != offset) - continue; - if (((*stackptr & 0x0000F000) >> 12) != id) - continue; - return ((*stackptr & 0xFFFF0000) >> 16); - } - B43_WARN_ON(1); - - return 0; -} - -#define phy_stacksave(offset) \ - do { \ - _stack_save(stack, &stackidx, 0x1, (offset), \ - b43_phy_read(dev, (offset))); \ - } while (0) -#define phy_stackrestore(offset) \ - do { \ - b43_phy_write(dev, (offset), \ - _stack_restore(stack, 0x1, \ - (offset))); \ - } while (0) -#define radio_stacksave(offset) \ - do { \ - _stack_save(stack, &stackidx, 0x2, (offset), \ - b43_radio_read16(dev, (offset))); \ - } while (0) -#define radio_stackrestore(offset) \ - do { \ - b43_radio_write16(dev, (offset), \ - _stack_restore(stack, 0x2, \ - (offset))); \ - } while (0) -#define ofdmtab_stacksave(table, offset) \ - do { \ - _stack_save(stack, &stackidx, 0x3, (offset)|(table), \ - b43_ofdmtab_read16(dev, (table), (offset))); \ - } while (0) -#define ofdmtab_stackrestore(table, offset) \ - do { \ - b43_ofdmtab_write16(dev, (table), (offset), \ - _stack_restore(stack, 0x3, \ - (offset)|(table))); \ - } while (0) - -static void -b43_radio_interference_mitigation_enable(struct b43_wldev *dev, int mode) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - u16 tmp, flipped; - size_t stackidx = 0; - u32 *stack = gphy->interfstack; - - switch (mode) { - case B43_INTERFMODE_NONWLAN: - if (phy->rev != 1) { - b43_phy_set(dev, 0x042B, 0x0800); - b43_phy_mask(dev, B43_PHY_G_CRS, ~0x4000); - break; - } - radio_stacksave(0x0078); - tmp = (b43_radio_read16(dev, 0x0078) & 0x001E); - B43_WARN_ON(tmp > 15); - flipped = bitrev4(tmp); - if (flipped < 10 && flipped >= 8) - flipped = 7; - else if (flipped >= 10) - flipped -= 3; - flipped = (bitrev4(flipped) << 1) | 0x0020; - b43_radio_write16(dev, 0x0078, flipped); - - b43_calc_nrssi_threshold(dev); - - phy_stacksave(0x0406); - b43_phy_write(dev, 0x0406, 0x7E28); - - b43_phy_set(dev, 0x042B, 0x0800); - b43_phy_set(dev, B43_PHY_RADIO_BITFIELD, 0x1000); - - phy_stacksave(0x04A0); - b43_phy_maskset(dev, 0x04A0, 0xC0C0, 0x0008); - phy_stacksave(0x04A1); - b43_phy_maskset(dev, 0x04A1, 0xC0C0, 0x0605); - phy_stacksave(0x04A2); - b43_phy_maskset(dev, 0x04A2, 0xC0C0, 0x0204); - phy_stacksave(0x04A8); - b43_phy_maskset(dev, 0x04A8, 0xC0C0, 0x0803); - phy_stacksave(0x04AB); - b43_phy_maskset(dev, 0x04AB, 0xC0C0, 0x0605); - - phy_stacksave(0x04A7); - b43_phy_write(dev, 0x04A7, 0x0002); - phy_stacksave(0x04A3); - b43_phy_write(dev, 0x04A3, 0x287A); - phy_stacksave(0x04A9); - b43_phy_write(dev, 0x04A9, 0x2027); - phy_stacksave(0x0493); - b43_phy_write(dev, 0x0493, 0x32F5); - phy_stacksave(0x04AA); - b43_phy_write(dev, 0x04AA, 0x2027); - phy_stacksave(0x04AC); - b43_phy_write(dev, 0x04AC, 0x32F5); - break; - case B43_INTERFMODE_MANUALWLAN: - if (b43_phy_read(dev, 0x0033) & 0x0800) - break; - - gphy->aci_enable = true; - - phy_stacksave(B43_PHY_RADIO_BITFIELD); - phy_stacksave(B43_PHY_G_CRS); - if (phy->rev < 2) { - phy_stacksave(0x0406); - } else { - phy_stacksave(0x04C0); - phy_stacksave(0x04C1); - } - phy_stacksave(0x0033); - phy_stacksave(0x04A7); - phy_stacksave(0x04A3); - phy_stacksave(0x04A9); - phy_stacksave(0x04AA); - phy_stacksave(0x04AC); - phy_stacksave(0x0493); - phy_stacksave(0x04A1); - phy_stacksave(0x04A0); - phy_stacksave(0x04A2); - phy_stacksave(0x048A); - phy_stacksave(0x04A8); - phy_stacksave(0x04AB); - if (phy->rev == 2) { - phy_stacksave(0x04AD); - phy_stacksave(0x04AE); - } else if (phy->rev >= 3) { - phy_stacksave(0x04AD); - phy_stacksave(0x0415); - phy_stacksave(0x0416); - phy_stacksave(0x0417); - ofdmtab_stacksave(0x1A00, 0x2); - ofdmtab_stacksave(0x1A00, 0x3); - } - phy_stacksave(0x042B); - phy_stacksave(0x048C); - - b43_phy_mask(dev, B43_PHY_RADIO_BITFIELD, ~0x1000); - b43_phy_maskset(dev, B43_PHY_G_CRS, 0xFFFC, 0x0002); - - b43_phy_write(dev, 0x0033, 0x0800); - b43_phy_write(dev, 0x04A3, 0x2027); - b43_phy_write(dev, 0x04A9, 0x1CA8); - b43_phy_write(dev, 0x0493, 0x287A); - b43_phy_write(dev, 0x04AA, 0x1CA8); - b43_phy_write(dev, 0x04AC, 0x287A); - - b43_phy_maskset(dev, 0x04A0, 0xFFC0, 0x001A); - b43_phy_write(dev, 0x04A7, 0x000D); - - if (phy->rev < 2) { - b43_phy_write(dev, 0x0406, 0xFF0D); - } else if (phy->rev == 2) { - b43_phy_write(dev, 0x04C0, 0xFFFF); - b43_phy_write(dev, 0x04C1, 0x00A9); - } else { - b43_phy_write(dev, 0x04C0, 0x00C1); - b43_phy_write(dev, 0x04C1, 0x0059); - } - - b43_phy_maskset(dev, 0x04A1, 0xC0FF, 0x1800); - b43_phy_maskset(dev, 0x04A1, 0xFFC0, 0x0015); - b43_phy_maskset(dev, 0x04A8, 0xCFFF, 0x1000); - b43_phy_maskset(dev, 0x04A8, 0xF0FF, 0x0A00); - b43_phy_maskset(dev, 0x04AB, 0xCFFF, 0x1000); - b43_phy_maskset(dev, 0x04AB, 0xF0FF, 0x0800); - b43_phy_maskset(dev, 0x04AB, 0xFFCF, 0x0010); - b43_phy_maskset(dev, 0x04AB, 0xFFF0, 0x0005); - b43_phy_maskset(dev, 0x04A8, 0xFFCF, 0x0010); - b43_phy_maskset(dev, 0x04A8, 0xFFF0, 0x0006); - b43_phy_maskset(dev, 0x04A2, 0xF0FF, 0x0800); - b43_phy_maskset(dev, 0x04A0, 0xF0FF, 0x0500); - b43_phy_maskset(dev, 0x04A2, 0xFFF0, 0x000B); - - if (phy->rev >= 3) { - b43_phy_mask(dev, 0x048A, 0x7FFF); - b43_phy_maskset(dev, 0x0415, 0x8000, 0x36D8); - b43_phy_maskset(dev, 0x0416, 0x8000, 0x36D8); - b43_phy_maskset(dev, 0x0417, 0xFE00, 0x016D); - } else { - b43_phy_set(dev, 0x048A, 0x1000); - b43_phy_maskset(dev, 0x048A, 0x9FFF, 0x2000); - b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ACIW); - } - if (phy->rev >= 2) { - b43_phy_set(dev, 0x042B, 0x0800); - } - b43_phy_maskset(dev, 0x048C, 0xF0FF, 0x0200); - if (phy->rev == 2) { - b43_phy_maskset(dev, 0x04AE, 0xFF00, 0x007F); - b43_phy_maskset(dev, 0x04AD, 0x00FF, 0x1300); - } else if (phy->rev >= 6) { - b43_ofdmtab_write16(dev, 0x1A00, 0x3, 0x007F); - b43_ofdmtab_write16(dev, 0x1A00, 0x2, 0x007F); - b43_phy_mask(dev, 0x04AD, 0x00FF); - } - b43_calc_nrssi_slope(dev); - break; - default: - B43_WARN_ON(1); - } -} - -static void -b43_radio_interference_mitigation_disable(struct b43_wldev *dev, int mode) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - u32 *stack = gphy->interfstack; - - switch (mode) { - case B43_INTERFMODE_NONWLAN: - if (phy->rev != 1) { - b43_phy_mask(dev, 0x042B, ~0x0800); - b43_phy_set(dev, B43_PHY_G_CRS, 0x4000); - break; - } - radio_stackrestore(0x0078); - b43_calc_nrssi_threshold(dev); - phy_stackrestore(0x0406); - b43_phy_mask(dev, 0x042B, ~0x0800); - if (!dev->bad_frames_preempt) { - b43_phy_mask(dev, B43_PHY_RADIO_BITFIELD, ~(1 << 11)); - } - b43_phy_set(dev, B43_PHY_G_CRS, 0x4000); - phy_stackrestore(0x04A0); - phy_stackrestore(0x04A1); - phy_stackrestore(0x04A2); - phy_stackrestore(0x04A8); - phy_stackrestore(0x04AB); - phy_stackrestore(0x04A7); - phy_stackrestore(0x04A3); - phy_stackrestore(0x04A9); - phy_stackrestore(0x0493); - phy_stackrestore(0x04AA); - phy_stackrestore(0x04AC); - break; - case B43_INTERFMODE_MANUALWLAN: - if (!(b43_phy_read(dev, 0x0033) & 0x0800)) - break; - - gphy->aci_enable = false; - - phy_stackrestore(B43_PHY_RADIO_BITFIELD); - phy_stackrestore(B43_PHY_G_CRS); - phy_stackrestore(0x0033); - phy_stackrestore(0x04A3); - phy_stackrestore(0x04A9); - phy_stackrestore(0x0493); - phy_stackrestore(0x04AA); - phy_stackrestore(0x04AC); - phy_stackrestore(0x04A0); - phy_stackrestore(0x04A7); - if (phy->rev >= 2) { - phy_stackrestore(0x04C0); - phy_stackrestore(0x04C1); - } else - phy_stackrestore(0x0406); - phy_stackrestore(0x04A1); - phy_stackrestore(0x04AB); - phy_stackrestore(0x04A8); - if (phy->rev == 2) { - phy_stackrestore(0x04AD); - phy_stackrestore(0x04AE); - } else if (phy->rev >= 3) { - phy_stackrestore(0x04AD); - phy_stackrestore(0x0415); - phy_stackrestore(0x0416); - phy_stackrestore(0x0417); - ofdmtab_stackrestore(0x1A00, 0x2); - ofdmtab_stackrestore(0x1A00, 0x3); - } - phy_stackrestore(0x04A2); - phy_stackrestore(0x048A); - phy_stackrestore(0x042B); - phy_stackrestore(0x048C); - b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ACIW); - b43_calc_nrssi_slope(dev); - break; - default: - B43_WARN_ON(1); - } -} - -#undef phy_stacksave -#undef phy_stackrestore -#undef radio_stacksave -#undef radio_stackrestore -#undef ofdmtab_stacksave -#undef ofdmtab_stackrestore - -static u16 b43_radio_core_calibration_value(struct b43_wldev *dev) -{ - u16 reg, index, ret; - - static const u8 rcc_table[] = { - 0x02, 0x03, 0x01, 0x0F, - 0x06, 0x07, 0x05, 0x0F, - 0x0A, 0x0B, 0x09, 0x0F, - 0x0E, 0x0F, 0x0D, 0x0F, - }; - - reg = b43_radio_read16(dev, 0x60); - index = (reg & 0x001E) >> 1; - ret = rcc_table[index] << 1; - ret |= (reg & 0x0001); - ret |= 0x0020; - - return ret; -} - -#define LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0)) -static u16 radio2050_rfover_val(struct b43_wldev *dev, - u16 phy_register, unsigned int lpd) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct ssb_sprom *sprom = dev->dev->bus_sprom; - - if (!phy->gmode) - return 0; - - if (has_loopback_gain(phy)) { - int max_lb_gain = gphy->max_lb_gain; - u16 extlna; - u16 i; - - if (phy->radio_rev == 8) - max_lb_gain += 0x3E; - else - max_lb_gain += 0x26; - if (max_lb_gain >= 0x46) { - extlna = 0x3000; - max_lb_gain -= 0x46; - } else if (max_lb_gain >= 0x3A) { - extlna = 0x1000; - max_lb_gain -= 0x3A; - } else if (max_lb_gain >= 0x2E) { - extlna = 0x2000; - max_lb_gain -= 0x2E; - } else { - extlna = 0; - max_lb_gain -= 0x10; - } - - for (i = 0; i < 16; i++) { - max_lb_gain -= (i * 6); - if (max_lb_gain < 6) - break; - } - - if ((phy->rev < 7) || - !(sprom->boardflags_lo & B43_BFL_EXTLNA)) { - if (phy_register == B43_PHY_RFOVER) { - return 0x1B3; - } else if (phy_register == B43_PHY_RFOVERVAL) { - extlna |= (i << 8); - switch (lpd) { - case LPD(0, 1, 1): - return 0x0F92; - case LPD(0, 0, 1): - case LPD(1, 0, 1): - return (0x0092 | extlna); - case LPD(1, 0, 0): - return (0x0093 | extlna); - } - B43_WARN_ON(1); - } - B43_WARN_ON(1); - } else { - if (phy_register == B43_PHY_RFOVER) { - return 0x9B3; - } else if (phy_register == B43_PHY_RFOVERVAL) { - if (extlna) - extlna |= 0x8000; - extlna |= (i << 8); - switch (lpd) { - case LPD(0, 1, 1): - return 0x8F92; - case LPD(0, 0, 1): - return (0x8092 | extlna); - case LPD(1, 0, 1): - return (0x2092 | extlna); - case LPD(1, 0, 0): - return (0x2093 | extlna); - } - B43_WARN_ON(1); - } - B43_WARN_ON(1); - } - } else { - if ((phy->rev < 7) || - !(sprom->boardflags_lo & B43_BFL_EXTLNA)) { - if (phy_register == B43_PHY_RFOVER) { - return 0x1B3; - } else if (phy_register == B43_PHY_RFOVERVAL) { - switch (lpd) { - case LPD(0, 1, 1): - return 0x0FB2; - case LPD(0, 0, 1): - return 0x00B2; - case LPD(1, 0, 1): - return 0x30B2; - case LPD(1, 0, 0): - return 0x30B3; - } - B43_WARN_ON(1); - } - B43_WARN_ON(1); - } else { - if (phy_register == B43_PHY_RFOVER) { - return 0x9B3; - } else if (phy_register == B43_PHY_RFOVERVAL) { - switch (lpd) { - case LPD(0, 1, 1): - return 0x8FB2; - case LPD(0, 0, 1): - return 0x80B2; - case LPD(1, 0, 1): - return 0x20B2; - case LPD(1, 0, 0): - return 0x20B3; - } - B43_WARN_ON(1); - } - B43_WARN_ON(1); - } - } - return 0; -} - -struct init2050_saved_values { - /* Core registers */ - u16 reg_3EC; - u16 reg_3E6; - u16 reg_3F4; - /* Radio registers */ - u16 radio_43; - u16 radio_51; - u16 radio_52; - /* PHY registers */ - u16 phy_pgactl; - u16 phy_cck_5A; - u16 phy_cck_59; - u16 phy_cck_58; - u16 phy_cck_30; - u16 phy_rfover; - u16 phy_rfoverval; - u16 phy_analogover; - u16 phy_analogoverval; - u16 phy_crs0; - u16 phy_classctl; - u16 phy_lo_mask; - u16 phy_lo_ctl; - u16 phy_syncctl; -}; - -static u16 b43_radio_init2050(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct init2050_saved_values sav; - u16 rcc; - u16 radio78; - u16 ret; - u16 i, j; - u32 tmp1 = 0, tmp2 = 0; - - memset(&sav, 0, sizeof(sav)); /* get rid of "may be used uninitialized..." */ - - sav.radio_43 = b43_radio_read16(dev, 0x43); - sav.radio_51 = b43_radio_read16(dev, 0x51); - sav.radio_52 = b43_radio_read16(dev, 0x52); - sav.phy_pgactl = b43_phy_read(dev, B43_PHY_PGACTL); - sav.phy_cck_5A = b43_phy_read(dev, B43_PHY_CCK(0x5A)); - sav.phy_cck_59 = b43_phy_read(dev, B43_PHY_CCK(0x59)); - sav.phy_cck_58 = b43_phy_read(dev, B43_PHY_CCK(0x58)); - - if (phy->type == B43_PHYTYPE_B) { - sav.phy_cck_30 = b43_phy_read(dev, B43_PHY_CCK(0x30)); - sav.reg_3EC = b43_read16(dev, 0x3EC); - - b43_phy_write(dev, B43_PHY_CCK(0x30), 0xFF); - b43_write16(dev, 0x3EC, 0x3F3F); - } else if (phy->gmode || phy->rev >= 2) { - sav.phy_rfover = b43_phy_read(dev, B43_PHY_RFOVER); - sav.phy_rfoverval = b43_phy_read(dev, B43_PHY_RFOVERVAL); - sav.phy_analogover = b43_phy_read(dev, B43_PHY_ANALOGOVER); - sav.phy_analogoverval = - b43_phy_read(dev, B43_PHY_ANALOGOVERVAL); - sav.phy_crs0 = b43_phy_read(dev, B43_PHY_CRS0); - sav.phy_classctl = b43_phy_read(dev, B43_PHY_CLASSCTL); - - b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0003); - b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFC); - b43_phy_mask(dev, B43_PHY_CRS0, 0x7FFF); - b43_phy_mask(dev, B43_PHY_CLASSCTL, 0xFFFC); - if (has_loopback_gain(phy)) { - sav.phy_lo_mask = b43_phy_read(dev, B43_PHY_LO_MASK); - sav.phy_lo_ctl = b43_phy_read(dev, B43_PHY_LO_CTL); - - if (phy->rev >= 3) - b43_phy_write(dev, B43_PHY_LO_MASK, 0xC020); - else - b43_phy_write(dev, B43_PHY_LO_MASK, 0x8020); - b43_phy_write(dev, B43_PHY_LO_CTL, 0); - } - - b43_phy_write(dev, B43_PHY_RFOVERVAL, - radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, - LPD(0, 1, 1))); - b43_phy_write(dev, B43_PHY_RFOVER, - radio2050_rfover_val(dev, B43_PHY_RFOVER, 0)); - } - b43_write16(dev, 0x3E2, b43_read16(dev, 0x3E2) | 0x8000); - - sav.phy_syncctl = b43_phy_read(dev, B43_PHY_SYNCCTL); - b43_phy_mask(dev, B43_PHY_SYNCCTL, 0xFF7F); - sav.reg_3E6 = b43_read16(dev, 0x3E6); - sav.reg_3F4 = b43_read16(dev, 0x3F4); - - if (phy->analog == 0) { - b43_write16(dev, 0x03E6, 0x0122); - } else { - if (phy->analog >= 2) { - b43_phy_maskset(dev, B43_PHY_CCK(0x03), 0xFFBF, 0x40); - } - b43_write16(dev, B43_MMIO_CHANNEL_EXT, - (b43_read16(dev, B43_MMIO_CHANNEL_EXT) | 0x2000)); - } - - rcc = b43_radio_core_calibration_value(dev); - - if (phy->type == B43_PHYTYPE_B) - b43_radio_write16(dev, 0x78, 0x26); - if (phy->gmode || phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_RFOVERVAL, - radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, - LPD(0, 1, 1))); - } - b43_phy_write(dev, B43_PHY_PGACTL, 0xBFAF); - b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x1403); - if (phy->gmode || phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_RFOVERVAL, - radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, - LPD(0, 0, 1))); - } - b43_phy_write(dev, B43_PHY_PGACTL, 0xBFA0); - b43_radio_set(dev, 0x51, 0x0004); - if (phy->radio_rev == 8) { - b43_radio_write16(dev, 0x43, 0x1F); - } else { - b43_radio_write16(dev, 0x52, 0); - b43_radio_maskset(dev, 0x43, 0xFFF0, 0x0009); - } - b43_phy_write(dev, B43_PHY_CCK(0x58), 0); - - for (i = 0; i < 16; i++) { - b43_phy_write(dev, B43_PHY_CCK(0x5A), 0x0480); - b43_phy_write(dev, B43_PHY_CCK(0x59), 0xC810); - b43_phy_write(dev, B43_PHY_CCK(0x58), 0x000D); - if (phy->gmode || phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_RFOVERVAL, - radio2050_rfover_val(dev, - B43_PHY_RFOVERVAL, - LPD(1, 0, 1))); - } - b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); - udelay(10); - if (phy->gmode || phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_RFOVERVAL, - radio2050_rfover_val(dev, - B43_PHY_RFOVERVAL, - LPD(1, 0, 1))); - } - b43_phy_write(dev, B43_PHY_PGACTL, 0xEFB0); - udelay(10); - if (phy->gmode || phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_RFOVERVAL, - radio2050_rfover_val(dev, - B43_PHY_RFOVERVAL, - LPD(1, 0, 0))); - } - b43_phy_write(dev, B43_PHY_PGACTL, 0xFFF0); - udelay(20); - tmp1 += b43_phy_read(dev, B43_PHY_LO_LEAKAGE); - b43_phy_write(dev, B43_PHY_CCK(0x58), 0); - if (phy->gmode || phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_RFOVERVAL, - radio2050_rfover_val(dev, - B43_PHY_RFOVERVAL, - LPD(1, 0, 1))); - } - b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); - } - udelay(10); - - b43_phy_write(dev, B43_PHY_CCK(0x58), 0); - tmp1++; - tmp1 >>= 9; - - for (i = 0; i < 16; i++) { - radio78 = (bitrev4(i) << 1) | 0x0020; - b43_radio_write16(dev, 0x78, radio78); - udelay(10); - for (j = 0; j < 16; j++) { - b43_phy_write(dev, B43_PHY_CCK(0x5A), 0x0D80); - b43_phy_write(dev, B43_PHY_CCK(0x59), 0xC810); - b43_phy_write(dev, B43_PHY_CCK(0x58), 0x000D); - if (phy->gmode || phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_RFOVERVAL, - radio2050_rfover_val(dev, - B43_PHY_RFOVERVAL, - LPD(1, 0, - 1))); - } - b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); - udelay(10); - if (phy->gmode || phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_RFOVERVAL, - radio2050_rfover_val(dev, - B43_PHY_RFOVERVAL, - LPD(1, 0, - 1))); - } - b43_phy_write(dev, B43_PHY_PGACTL, 0xEFB0); - udelay(10); - if (phy->gmode || phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_RFOVERVAL, - radio2050_rfover_val(dev, - B43_PHY_RFOVERVAL, - LPD(1, 0, - 0))); - } - b43_phy_write(dev, B43_PHY_PGACTL, 0xFFF0); - udelay(10); - tmp2 += b43_phy_read(dev, B43_PHY_LO_LEAKAGE); - b43_phy_write(dev, B43_PHY_CCK(0x58), 0); - if (phy->gmode || phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_RFOVERVAL, - radio2050_rfover_val(dev, - B43_PHY_RFOVERVAL, - LPD(1, 0, - 1))); - } - b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); - } - tmp2++; - tmp2 >>= 8; - if (tmp1 < tmp2) - break; - } - - /* Restore the registers */ - b43_phy_write(dev, B43_PHY_PGACTL, sav.phy_pgactl); - b43_radio_write16(dev, 0x51, sav.radio_51); - b43_radio_write16(dev, 0x52, sav.radio_52); - b43_radio_write16(dev, 0x43, sav.radio_43); - b43_phy_write(dev, B43_PHY_CCK(0x5A), sav.phy_cck_5A); - b43_phy_write(dev, B43_PHY_CCK(0x59), sav.phy_cck_59); - b43_phy_write(dev, B43_PHY_CCK(0x58), sav.phy_cck_58); - b43_write16(dev, 0x3E6, sav.reg_3E6); - if (phy->analog != 0) - b43_write16(dev, 0x3F4, sav.reg_3F4); - b43_phy_write(dev, B43_PHY_SYNCCTL, sav.phy_syncctl); - b43_synth_pu_workaround(dev, phy->channel); - if (phy->type == B43_PHYTYPE_B) { - b43_phy_write(dev, B43_PHY_CCK(0x30), sav.phy_cck_30); - b43_write16(dev, 0x3EC, sav.reg_3EC); - } else if (phy->gmode) { - b43_write16(dev, B43_MMIO_PHY_RADIO, - b43_read16(dev, B43_MMIO_PHY_RADIO) - & 0x7FFF); - b43_phy_write(dev, B43_PHY_RFOVER, sav.phy_rfover); - b43_phy_write(dev, B43_PHY_RFOVERVAL, sav.phy_rfoverval); - b43_phy_write(dev, B43_PHY_ANALOGOVER, sav.phy_analogover); - b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, - sav.phy_analogoverval); - b43_phy_write(dev, B43_PHY_CRS0, sav.phy_crs0); - b43_phy_write(dev, B43_PHY_CLASSCTL, sav.phy_classctl); - if (has_loopback_gain(phy)) { - b43_phy_write(dev, B43_PHY_LO_MASK, sav.phy_lo_mask); - b43_phy_write(dev, B43_PHY_LO_CTL, sav.phy_lo_ctl); - } - } - if (i > 15) - ret = radio78; - else - ret = rcc; - - return ret; -} - -static void b43_phy_initb5(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - u16 offset, value; - u8 old_channel; - - if (phy->analog == 1) { - b43_radio_set(dev, 0x007A, 0x0050); - } - if ((dev->dev->board_vendor != SSB_BOARDVENDOR_BCM) && - (dev->dev->board_type != SSB_BOARD_BU4306)) { - value = 0x2120; - for (offset = 0x00A8; offset < 0x00C7; offset++) { - b43_phy_write(dev, offset, value); - value += 0x202; - } - } - b43_phy_maskset(dev, 0x0035, 0xF0FF, 0x0700); - if (phy->radio_ver == 0x2050) - b43_phy_write(dev, 0x0038, 0x0667); - - if (phy->gmode || phy->rev >= 2) { - if (phy->radio_ver == 0x2050) { - b43_radio_set(dev, 0x007A, 0x0020); - b43_radio_set(dev, 0x0051, 0x0004); - } - b43_write16(dev, B43_MMIO_PHY_RADIO, 0x0000); - - b43_phy_set(dev, 0x0802, 0x0100); - b43_phy_set(dev, 0x042B, 0x2000); - - b43_phy_write(dev, 0x001C, 0x186A); - - b43_phy_maskset(dev, 0x0013, 0x00FF, 0x1900); - b43_phy_maskset(dev, 0x0035, 0xFFC0, 0x0064); - b43_phy_maskset(dev, 0x005D, 0xFF80, 0x000A); - } - - if (dev->bad_frames_preempt) { - b43_phy_set(dev, B43_PHY_RADIO_BITFIELD, (1 << 11)); - } - - if (phy->analog == 1) { - b43_phy_write(dev, 0x0026, 0xCE00); - b43_phy_write(dev, 0x0021, 0x3763); - b43_phy_write(dev, 0x0022, 0x1BC3); - b43_phy_write(dev, 0x0023, 0x06F9); - b43_phy_write(dev, 0x0024, 0x037E); - } else - b43_phy_write(dev, 0x0026, 0xCC00); - b43_phy_write(dev, 0x0030, 0x00C6); - b43_write16(dev, 0x03EC, 0x3F22); - - if (phy->analog == 1) - b43_phy_write(dev, 0x0020, 0x3E1C); - else - b43_phy_write(dev, 0x0020, 0x301C); - - if (phy->analog == 0) - b43_write16(dev, 0x03E4, 0x3000); - - old_channel = phy->channel; - /* Force to channel 7, even if not supported. */ - b43_gphy_channel_switch(dev, 7, 0); - - if (phy->radio_ver != 0x2050) { - b43_radio_write16(dev, 0x0075, 0x0080); - b43_radio_write16(dev, 0x0079, 0x0081); - } - - b43_radio_write16(dev, 0x0050, 0x0020); - b43_radio_write16(dev, 0x0050, 0x0023); - - if (phy->radio_ver == 0x2050) { - b43_radio_write16(dev, 0x0050, 0x0020); - b43_radio_write16(dev, 0x005A, 0x0070); - } - - b43_radio_write16(dev, 0x005B, 0x007B); - b43_radio_write16(dev, 0x005C, 0x00B0); - - b43_radio_set(dev, 0x007A, 0x0007); - - b43_gphy_channel_switch(dev, old_channel, 0); - - b43_phy_write(dev, 0x0014, 0x0080); - b43_phy_write(dev, 0x0032, 0x00CA); - b43_phy_write(dev, 0x002A, 0x88A3); - - b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, gphy->tx_control); - - if (phy->radio_ver == 0x2050) - b43_radio_write16(dev, 0x005D, 0x000D); - - b43_write16(dev, 0x03E4, (b43_read16(dev, 0x03E4) & 0xFFC0) | 0x0004); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/Init/B6 */ -static void b43_phy_initb6(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - u16 offset, val; - u8 old_channel; - - b43_phy_write(dev, 0x003E, 0x817A); - b43_radio_write16(dev, 0x007A, - (b43_radio_read16(dev, 0x007A) | 0x0058)); - if (phy->radio_rev == 4 || phy->radio_rev == 5) { - b43_radio_write16(dev, 0x51, 0x37); - b43_radio_write16(dev, 0x52, 0x70); - b43_radio_write16(dev, 0x53, 0xB3); - b43_radio_write16(dev, 0x54, 0x9B); - b43_radio_write16(dev, 0x5A, 0x88); - b43_radio_write16(dev, 0x5B, 0x88); - b43_radio_write16(dev, 0x5D, 0x88); - b43_radio_write16(dev, 0x5E, 0x88); - b43_radio_write16(dev, 0x7D, 0x88); - b43_hf_write(dev, b43_hf_read(dev) - | B43_HF_TSSIRPSMW); - } - B43_WARN_ON(phy->radio_rev == 6 || phy->radio_rev == 7); /* We had code for these revs here... */ - if (phy->radio_rev == 8) { - b43_radio_write16(dev, 0x51, 0); - b43_radio_write16(dev, 0x52, 0x40); - b43_radio_write16(dev, 0x53, 0xB7); - b43_radio_write16(dev, 0x54, 0x98); - b43_radio_write16(dev, 0x5A, 0x88); - b43_radio_write16(dev, 0x5B, 0x6B); - b43_radio_write16(dev, 0x5C, 0x0F); - if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_ALTIQ) { - b43_radio_write16(dev, 0x5D, 0xFA); - b43_radio_write16(dev, 0x5E, 0xD8); - } else { - b43_radio_write16(dev, 0x5D, 0xF5); - b43_radio_write16(dev, 0x5E, 0xB8); - } - b43_radio_write16(dev, 0x0073, 0x0003); - b43_radio_write16(dev, 0x007D, 0x00A8); - b43_radio_write16(dev, 0x007C, 0x0001); - b43_radio_write16(dev, 0x007E, 0x0008); - } - val = 0x1E1F; - for (offset = 0x0088; offset < 0x0098; offset++) { - b43_phy_write(dev, offset, val); - val -= 0x0202; - } - val = 0x3E3F; - for (offset = 0x0098; offset < 0x00A8; offset++) { - b43_phy_write(dev, offset, val); - val -= 0x0202; - } - val = 0x2120; - for (offset = 0x00A8; offset < 0x00C8; offset++) { - b43_phy_write(dev, offset, (val & 0x3F3F)); - val += 0x0202; - } - if (phy->type == B43_PHYTYPE_G) { - b43_radio_set(dev, 0x007A, 0x0020); - b43_radio_set(dev, 0x0051, 0x0004); - b43_phy_set(dev, 0x0802, 0x0100); - b43_phy_set(dev, 0x042B, 0x2000); - b43_phy_write(dev, 0x5B, 0); - b43_phy_write(dev, 0x5C, 0); - } - - old_channel = phy->channel; - if (old_channel >= 8) - b43_gphy_channel_switch(dev, 1, 0); - else - b43_gphy_channel_switch(dev, 13, 0); - - b43_radio_write16(dev, 0x0050, 0x0020); - b43_radio_write16(dev, 0x0050, 0x0023); - udelay(40); - if (phy->radio_rev < 6 || phy->radio_rev == 8) { - b43_radio_write16(dev, 0x7C, (b43_radio_read16(dev, 0x7C) - | 0x0002)); - b43_radio_write16(dev, 0x50, 0x20); - } - if (phy->radio_rev <= 2) { - b43_radio_write16(dev, 0x50, 0x20); - b43_radio_write16(dev, 0x5A, 0x70); - b43_radio_write16(dev, 0x5B, 0x7B); - b43_radio_write16(dev, 0x5C, 0xB0); - } - b43_radio_maskset(dev, 0x007A, 0x00F8, 0x0007); - - b43_gphy_channel_switch(dev, old_channel, 0); - - b43_phy_write(dev, 0x0014, 0x0200); - if (phy->radio_rev >= 6) - b43_phy_write(dev, 0x2A, 0x88C2); - else - b43_phy_write(dev, 0x2A, 0x8AC0); - b43_phy_write(dev, 0x0038, 0x0668); - b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, gphy->tx_control); - if (phy->radio_rev == 4 || phy->radio_rev == 5) - b43_phy_maskset(dev, 0x5D, 0xFF80, 0x0003); - if (phy->radio_rev <= 2) - b43_radio_write16(dev, 0x005D, 0x000D); - - if (phy->analog == 4) { - b43_write16(dev, 0x3E4, 9); - b43_phy_mask(dev, 0x61, 0x0FFF); - } else { - b43_phy_maskset(dev, 0x0002, 0xFFC0, 0x0004); - } - if (phy->type == B43_PHYTYPE_B) - B43_WARN_ON(1); - else if (phy->type == B43_PHYTYPE_G) - b43_write16(dev, 0x03E6, 0x0); -} - -static void b43_calc_loopback_gain(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - u16 backup_phy[16] = { 0 }; - u16 backup_radio[3]; - u16 backup_bband; - u16 i, j, loop_i_max; - u16 trsw_rx; - u16 loop1_outer_done, loop1_inner_done; - - backup_phy[0] = b43_phy_read(dev, B43_PHY_CRS0); - backup_phy[1] = b43_phy_read(dev, B43_PHY_CCKBBANDCFG); - backup_phy[2] = b43_phy_read(dev, B43_PHY_RFOVER); - backup_phy[3] = b43_phy_read(dev, B43_PHY_RFOVERVAL); - if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ - backup_phy[4] = b43_phy_read(dev, B43_PHY_ANALOGOVER); - backup_phy[5] = b43_phy_read(dev, B43_PHY_ANALOGOVERVAL); - } - backup_phy[6] = b43_phy_read(dev, B43_PHY_CCK(0x5A)); - backup_phy[7] = b43_phy_read(dev, B43_PHY_CCK(0x59)); - backup_phy[8] = b43_phy_read(dev, B43_PHY_CCK(0x58)); - backup_phy[9] = b43_phy_read(dev, B43_PHY_CCK(0x0A)); - backup_phy[10] = b43_phy_read(dev, B43_PHY_CCK(0x03)); - backup_phy[11] = b43_phy_read(dev, B43_PHY_LO_MASK); - backup_phy[12] = b43_phy_read(dev, B43_PHY_LO_CTL); - backup_phy[13] = b43_phy_read(dev, B43_PHY_CCK(0x2B)); - backup_phy[14] = b43_phy_read(dev, B43_PHY_PGACTL); - backup_phy[15] = b43_phy_read(dev, B43_PHY_LO_LEAKAGE); - backup_bband = gphy->bbatt.att; - backup_radio[0] = b43_radio_read16(dev, 0x52); - backup_radio[1] = b43_radio_read16(dev, 0x43); - backup_radio[2] = b43_radio_read16(dev, 0x7A); - - b43_phy_mask(dev, B43_PHY_CRS0, 0x3FFF); - b43_phy_set(dev, B43_PHY_CCKBBANDCFG, 0x8000); - b43_phy_set(dev, B43_PHY_RFOVER, 0x0002); - b43_phy_mask(dev, B43_PHY_RFOVERVAL, 0xFFFD); - b43_phy_set(dev, B43_PHY_RFOVER, 0x0001); - b43_phy_mask(dev, B43_PHY_RFOVERVAL, 0xFFFE); - if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ - b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0001); - b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFE); - b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0002); - b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFD); - } - b43_phy_set(dev, B43_PHY_RFOVER, 0x000C); - b43_phy_set(dev, B43_PHY_RFOVERVAL, 0x000C); - b43_phy_set(dev, B43_PHY_RFOVER, 0x0030); - b43_phy_maskset(dev, B43_PHY_RFOVERVAL, 0xFFCF, 0x10); - - b43_phy_write(dev, B43_PHY_CCK(0x5A), 0x0780); - b43_phy_write(dev, B43_PHY_CCK(0x59), 0xC810); - b43_phy_write(dev, B43_PHY_CCK(0x58), 0x000D); - - b43_phy_set(dev, B43_PHY_CCK(0x0A), 0x2000); - if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ - b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0004); - b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFB); - } - b43_phy_maskset(dev, B43_PHY_CCK(0x03), 0xFF9F, 0x40); - - if (phy->radio_rev == 8) { - b43_radio_write16(dev, 0x43, 0x000F); - } else { - b43_radio_write16(dev, 0x52, 0); - b43_radio_maskset(dev, 0x43, 0xFFF0, 0x9); - } - b43_gphy_set_baseband_attenuation(dev, 11); - - if (phy->rev >= 3) - b43_phy_write(dev, B43_PHY_LO_MASK, 0xC020); - else - b43_phy_write(dev, B43_PHY_LO_MASK, 0x8020); - b43_phy_write(dev, B43_PHY_LO_CTL, 0); - - b43_phy_maskset(dev, B43_PHY_CCK(0x2B), 0xFFC0, 0x01); - b43_phy_maskset(dev, B43_PHY_CCK(0x2B), 0xC0FF, 0x800); - - b43_phy_set(dev, B43_PHY_RFOVER, 0x0100); - b43_phy_mask(dev, B43_PHY_RFOVERVAL, 0xCFFF); - - if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_EXTLNA) { - if (phy->rev >= 7) { - b43_phy_set(dev, B43_PHY_RFOVER, 0x0800); - b43_phy_set(dev, B43_PHY_RFOVERVAL, 0x8000); - } - } - b43_radio_mask(dev, 0x7A, 0x00F7); - - j = 0; - loop_i_max = (phy->radio_rev == 8) ? 15 : 9; - for (i = 0; i < loop_i_max; i++) { - for (j = 0; j < 16; j++) { - b43_radio_write16(dev, 0x43, i); - b43_phy_maskset(dev, B43_PHY_RFOVERVAL, 0xF0FF, (j << 8)); - b43_phy_maskset(dev, B43_PHY_PGACTL, 0x0FFF, 0xA000); - b43_phy_set(dev, B43_PHY_PGACTL, 0xF000); - udelay(20); - if (b43_phy_read(dev, B43_PHY_LO_LEAKAGE) >= 0xDFC) - goto exit_loop1; - } - } - exit_loop1: - loop1_outer_done = i; - loop1_inner_done = j; - if (j >= 8) { - b43_phy_set(dev, B43_PHY_RFOVERVAL, 0x30); - trsw_rx = 0x1B; - for (j = j - 8; j < 16; j++) { - b43_phy_maskset(dev, B43_PHY_RFOVERVAL, 0xF0FF, (j << 8)); - b43_phy_maskset(dev, B43_PHY_PGACTL, 0x0FFF, 0xA000); - b43_phy_set(dev, B43_PHY_PGACTL, 0xF000); - udelay(20); - trsw_rx -= 3; - if (b43_phy_read(dev, B43_PHY_LO_LEAKAGE) >= 0xDFC) - goto exit_loop2; - } - } else - trsw_rx = 0x18; - exit_loop2: - - if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ - b43_phy_write(dev, B43_PHY_ANALOGOVER, backup_phy[4]); - b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, backup_phy[5]); - } - b43_phy_write(dev, B43_PHY_CCK(0x5A), backup_phy[6]); - b43_phy_write(dev, B43_PHY_CCK(0x59), backup_phy[7]); - b43_phy_write(dev, B43_PHY_CCK(0x58), backup_phy[8]); - b43_phy_write(dev, B43_PHY_CCK(0x0A), backup_phy[9]); - b43_phy_write(dev, B43_PHY_CCK(0x03), backup_phy[10]); - b43_phy_write(dev, B43_PHY_LO_MASK, backup_phy[11]); - b43_phy_write(dev, B43_PHY_LO_CTL, backup_phy[12]); - b43_phy_write(dev, B43_PHY_CCK(0x2B), backup_phy[13]); - b43_phy_write(dev, B43_PHY_PGACTL, backup_phy[14]); - - b43_gphy_set_baseband_attenuation(dev, backup_bband); - - b43_radio_write16(dev, 0x52, backup_radio[0]); - b43_radio_write16(dev, 0x43, backup_radio[1]); - b43_radio_write16(dev, 0x7A, backup_radio[2]); - - b43_phy_write(dev, B43_PHY_RFOVER, backup_phy[2] | 0x0003); - udelay(10); - b43_phy_write(dev, B43_PHY_RFOVER, backup_phy[2]); - b43_phy_write(dev, B43_PHY_RFOVERVAL, backup_phy[3]); - b43_phy_write(dev, B43_PHY_CRS0, backup_phy[0]); - b43_phy_write(dev, B43_PHY_CCKBBANDCFG, backup_phy[1]); - - gphy->max_lb_gain = - ((loop1_inner_done * 6) - (loop1_outer_done * 4)) - 11; - gphy->trsw_rx_gain = trsw_rx * 2; -} - -static void b43_hardware_pctl_early_init(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - if (!b43_has_hardware_pctl(dev)) { - b43_phy_write(dev, 0x047A, 0xC111); - return; - } - - b43_phy_mask(dev, 0x0036, 0xFEFF); - b43_phy_write(dev, 0x002F, 0x0202); - b43_phy_set(dev, 0x047C, 0x0002); - b43_phy_set(dev, 0x047A, 0xF000); - if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { - b43_phy_maskset(dev, 0x047A, 0xFF0F, 0x0010); - b43_phy_set(dev, 0x005D, 0x8000); - b43_phy_maskset(dev, 0x004E, 0xFFC0, 0x0010); - b43_phy_write(dev, 0x002E, 0xC07F); - b43_phy_set(dev, 0x0036, 0x0400); - } else { - b43_phy_set(dev, 0x0036, 0x0200); - b43_phy_set(dev, 0x0036, 0x0400); - b43_phy_mask(dev, 0x005D, 0x7FFF); - b43_phy_mask(dev, 0x004F, 0xFFFE); - b43_phy_maskset(dev, 0x004E, 0xFFC0, 0x0010); - b43_phy_write(dev, 0x002E, 0xC07F); - b43_phy_maskset(dev, 0x047A, 0xFF0F, 0x0010); - } -} - -/* Hardware power control for G-PHY */ -static void b43_hardware_pctl_init_gphy(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - - if (!b43_has_hardware_pctl(dev)) { - /* No hardware power control */ - b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_HWPCTL); - return; - } - - b43_phy_maskset(dev, 0x0036, 0xFFC0, (gphy->tgt_idle_tssi - gphy->cur_idle_tssi)); - b43_phy_maskset(dev, 0x0478, 0xFF00, (gphy->tgt_idle_tssi - gphy->cur_idle_tssi)); - b43_gphy_tssi_power_lt_init(dev); - b43_gphy_gain_lt_init(dev); - b43_phy_mask(dev, 0x0060, 0xFFBF); - b43_phy_write(dev, 0x0014, 0x0000); - - B43_WARN_ON(phy->rev < 6); - b43_phy_set(dev, 0x0478, 0x0800); - b43_phy_mask(dev, 0x0478, 0xFEFF); - b43_phy_mask(dev, 0x0801, 0xFFBF); - - b43_gphy_dc_lt_init(dev, 1); - - /* Enable hardware pctl in firmware. */ - b43_hf_write(dev, b43_hf_read(dev) | B43_HF_HWPCTL); -} - -/* Initialize B/G PHY power control */ -static void b43_phy_init_pctl(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_rfatt old_rfatt; - struct b43_bbatt old_bbatt; - u8 old_tx_control = 0; - - B43_WARN_ON(phy->type != B43_PHYTYPE_G); - - if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) && - (dev->dev->board_type == SSB_BOARD_BU4306)) - return; - - b43_phy_write(dev, 0x0028, 0x8018); - - /* This does something with the Analog... */ - b43_write16(dev, B43_MMIO_PHY0, b43_read16(dev, B43_MMIO_PHY0) - & 0xFFDF); - - if (!phy->gmode) - return; - b43_hardware_pctl_early_init(dev); - if (gphy->cur_idle_tssi == 0) { - if (phy->radio_ver == 0x2050 && phy->analog == 0) { - b43_radio_maskset(dev, 0x0076, 0x00F7, 0x0084); - } else { - struct b43_rfatt rfatt; - struct b43_bbatt bbatt; - - memcpy(&old_rfatt, &gphy->rfatt, sizeof(old_rfatt)); - memcpy(&old_bbatt, &gphy->bbatt, sizeof(old_bbatt)); - old_tx_control = gphy->tx_control; - - bbatt.att = 11; - if (phy->radio_rev == 8) { - rfatt.att = 15; - rfatt.with_padmix = true; - } else { - rfatt.att = 9; - rfatt.with_padmix = false; - } - b43_set_txpower_g(dev, &bbatt, &rfatt, 0); - } - b43_dummy_transmission(dev, false, true); - gphy->cur_idle_tssi = b43_phy_read(dev, B43_PHY_ITSSI); - if (B43_DEBUG) { - /* Current-Idle-TSSI sanity check. */ - if (abs(gphy->cur_idle_tssi - gphy->tgt_idle_tssi) >= 20) { - b43dbg(dev->wl, - "!WARNING! Idle-TSSI phy->cur_idle_tssi " - "measuring failed. (cur=%d, tgt=%d). Disabling TX power " - "adjustment.\n", gphy->cur_idle_tssi, - gphy->tgt_idle_tssi); - gphy->cur_idle_tssi = 0; - } - } - if (phy->radio_ver == 0x2050 && phy->analog == 0) { - b43_radio_mask(dev, 0x0076, 0xFF7B); - } else { - b43_set_txpower_g(dev, &old_bbatt, - &old_rfatt, old_tx_control); - } - } - b43_hardware_pctl_init_gphy(dev); - b43_shm_clear_tssi(dev); -} - -static void b43_phy_initg(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - u16 tmp; - - if (phy->rev == 1) - b43_phy_initb5(dev); - else - b43_phy_initb6(dev); - - if (phy->rev >= 2 || phy->gmode) - b43_phy_inita(dev); - - if (phy->rev >= 2) { - b43_phy_write(dev, B43_PHY_ANALOGOVER, 0); - b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, 0); - } - if (phy->rev == 2) { - b43_phy_write(dev, B43_PHY_RFOVER, 0); - b43_phy_write(dev, B43_PHY_PGACTL, 0xC0); - } - if (phy->rev > 5) { - b43_phy_write(dev, B43_PHY_RFOVER, 0x400); - b43_phy_write(dev, B43_PHY_PGACTL, 0xC0); - } - if (phy->gmode || phy->rev >= 2) { - tmp = b43_phy_read(dev, B43_PHY_VERSION_OFDM); - tmp &= B43_PHYVER_VERSION; - if (tmp == 3 || tmp == 5) { - b43_phy_write(dev, B43_PHY_OFDM(0xC2), 0x1816); - b43_phy_write(dev, B43_PHY_OFDM(0xC3), 0x8006); - } - if (tmp == 5) { - b43_phy_maskset(dev, B43_PHY_OFDM(0xCC), 0x00FF, 0x1F00); - } - } - if ((phy->rev <= 2 && phy->gmode) || phy->rev >= 2) - b43_phy_write(dev, B43_PHY_OFDM(0x7E), 0x78); - if (phy->radio_rev == 8) { - b43_phy_set(dev, B43_PHY_EXTG(0x01), 0x80); - b43_phy_set(dev, B43_PHY_OFDM(0x3E), 0x4); - } - if (has_loopback_gain(phy)) - b43_calc_loopback_gain(dev); - - if (phy->radio_rev != 8) { - if (gphy->initval == 0xFFFF) - gphy->initval = b43_radio_init2050(dev); - else - b43_radio_write16(dev, 0x0078, gphy->initval); - } - b43_lo_g_init(dev); - if (has_tx_magnification(phy)) { - b43_radio_write16(dev, 0x52, - (b43_radio_read16(dev, 0x52) & 0xFF00) - | gphy->lo_control->tx_bias | gphy-> - lo_control->tx_magn); - } else { - b43_radio_maskset(dev, 0x52, 0xFFF0, gphy->lo_control->tx_bias); - } - if (phy->rev >= 6) { - b43_phy_maskset(dev, B43_PHY_CCK(0x36), 0x0FFF, (gphy->lo_control->tx_bias << 12)); - } - if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) - b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8075); - else - b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x807F); - if (phy->rev < 2) - b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x101); - else - b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x202); - if (phy->gmode || phy->rev >= 2) { - b43_lo_g_adjust(dev); - b43_phy_write(dev, B43_PHY_LO_MASK, 0x8078); - } - - if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI)) { - /* The specs state to update the NRSSI LT with - * the value 0x7FFFFFFF here. I think that is some weird - * compiler optimization in the original driver. - * Essentially, what we do here is resetting all NRSSI LT - * entries to -32 (see the clamp_val() in nrssi_hw_update()) - */ - b43_nrssi_hw_update(dev, 0xFFFF); //FIXME? - b43_calc_nrssi_threshold(dev); - } else if (phy->gmode || phy->rev >= 2) { - if (gphy->nrssi[0] == -1000) { - B43_WARN_ON(gphy->nrssi[1] != -1000); - b43_calc_nrssi_slope(dev); - } else - b43_calc_nrssi_threshold(dev); - } - if (phy->radio_rev == 8) - b43_phy_write(dev, B43_PHY_EXTG(0x05), 0x3230); - b43_phy_init_pctl(dev); - /* FIXME: The spec says in the following if, the 0 should be replaced - 'if OFDM may not be used in the current locale' - but OFDM is legal everywhere */ - if ((dev->dev->chip_id == 0x4306 - && dev->dev->chip_pkg == 2) || 0) { - b43_phy_mask(dev, B43_PHY_CRS0, 0xBFFF); - b43_phy_mask(dev, B43_PHY_OFDM(0xC3), 0x7FFF); - } -} - -void b43_gphy_channel_switch(struct b43_wldev *dev, - unsigned int channel, - bool synthetic_pu_workaround) -{ - if (synthetic_pu_workaround) - b43_synth_pu_workaround(dev, channel); - - b43_write16(dev, B43_MMIO_CHANNEL, channel2freq_bg(channel)); - - if (channel == 14) { - if (dev->dev->bus_sprom->country_code == - SSB_SPROM1CCODE_JAPAN) - b43_hf_write(dev, - b43_hf_read(dev) & ~B43_HF_ACPR); - else - b43_hf_write(dev, - b43_hf_read(dev) | B43_HF_ACPR); - b43_write16(dev, B43_MMIO_CHANNEL_EXT, - b43_read16(dev, B43_MMIO_CHANNEL_EXT) - | (1 << 11)); - } else { - b43_write16(dev, B43_MMIO_CHANNEL_EXT, - b43_read16(dev, B43_MMIO_CHANNEL_EXT) - & 0xF7BF); - } -} - -static void default_baseband_attenuation(struct b43_wldev *dev, - struct b43_bbatt *bb) -{ - struct b43_phy *phy = &dev->phy; - - if (phy->radio_ver == 0x2050 && phy->radio_rev < 6) - bb->att = 0; - else - bb->att = 2; -} - -static void default_radio_attenuation(struct b43_wldev *dev, - struct b43_rfatt *rf) -{ - struct b43_bus_dev *bdev = dev->dev; - struct b43_phy *phy = &dev->phy; - - rf->with_padmix = false; - - if (dev->dev->board_vendor == SSB_BOARDVENDOR_BCM && - dev->dev->board_type == SSB_BOARD_BCM4309G) { - if (dev->dev->board_rev < 0x43) { - rf->att = 2; - return; - } else if (dev->dev->board_rev < 0x51) { - rf->att = 3; - return; - } - } - - if (phy->type == B43_PHYTYPE_A) { - rf->att = 0x60; - return; - } - - switch (phy->radio_ver) { - case 0x2053: - switch (phy->radio_rev) { - case 1: - rf->att = 6; - return; - } - break; - case 0x2050: - switch (phy->radio_rev) { - case 0: - rf->att = 5; - return; - case 1: - if (phy->type == B43_PHYTYPE_G) { - if (bdev->board_vendor == SSB_BOARDVENDOR_BCM - && bdev->board_type == SSB_BOARD_BCM4309G - && bdev->board_rev >= 30) - rf->att = 3; - else if (bdev->board_vendor == - SSB_BOARDVENDOR_BCM - && bdev->board_type == - SSB_BOARD_BU4306) - rf->att = 3; - else - rf->att = 1; - } else { - if (bdev->board_vendor == SSB_BOARDVENDOR_BCM - && bdev->board_type == SSB_BOARD_BCM4309G - && bdev->board_rev >= 30) - rf->att = 7; - else - rf->att = 6; - } - return; - case 2: - if (phy->type == B43_PHYTYPE_G) { - if (bdev->board_vendor == SSB_BOARDVENDOR_BCM - && bdev->board_type == SSB_BOARD_BCM4309G - && bdev->board_rev >= 30) - rf->att = 3; - else if (bdev->board_vendor == - SSB_BOARDVENDOR_BCM - && bdev->board_type == - SSB_BOARD_BU4306) - rf->att = 5; - else if (bdev->chip_id == 0x4320) - rf->att = 4; - else - rf->att = 3; - } else - rf->att = 6; - return; - case 3: - rf->att = 5; - return; - case 4: - case 5: - rf->att = 1; - return; - case 6: - case 7: - rf->att = 5; - return; - case 8: - rf->att = 0xA; - rf->with_padmix = true; - return; - case 9: - default: - rf->att = 5; - return; - } - } - rf->att = 5; -} - -static u16 default_tx_control(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - if (phy->radio_ver != 0x2050) - return 0; - if (phy->radio_rev == 1) - return B43_TXCTL_PA2DB | B43_TXCTL_TXMIX; - if (phy->radio_rev < 6) - return B43_TXCTL_PA2DB; - if (phy->radio_rev == 8) - return B43_TXCTL_TXMIX; - return 0; -} - -static u8 b43_gphy_aci_detect(struct b43_wldev *dev, u8 channel) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - u8 ret = 0; - u16 saved, rssi, temp; - int i, j = 0; - - saved = b43_phy_read(dev, 0x0403); - b43_switch_channel(dev, channel); - b43_phy_write(dev, 0x0403, (saved & 0xFFF8) | 5); - if (gphy->aci_hw_rssi) - rssi = b43_phy_read(dev, 0x048A) & 0x3F; - else - rssi = saved & 0x3F; - /* clamp temp to signed 5bit */ - if (rssi > 32) - rssi -= 64; - for (i = 0; i < 100; i++) { - temp = (b43_phy_read(dev, 0x047F) >> 8) & 0x3F; - if (temp > 32) - temp -= 64; - if (temp < rssi) - j++; - if (j >= 20) - ret = 1; - } - b43_phy_write(dev, 0x0403, saved); - - return ret; -} - -static u8 b43_gphy_aci_scan(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - u8 ret[13]; - unsigned int channel = phy->channel; - unsigned int i, j, start, end; - - if (!((phy->type == B43_PHYTYPE_G) && (phy->rev > 0))) - return 0; - - b43_phy_lock(dev); - b43_radio_lock(dev); - b43_phy_mask(dev, 0x0802, 0xFFFC); - b43_phy_mask(dev, B43_PHY_G_CRS, 0x7FFF); - b43_set_all_gains(dev, 3, 8, 1); - - start = (channel - 5 > 0) ? channel - 5 : 1; - end = (channel + 5 < 14) ? channel + 5 : 13; - - for (i = start; i <= end; i++) { - if (abs(channel - i) > 2) - ret[i - 1] = b43_gphy_aci_detect(dev, i); - } - b43_switch_channel(dev, channel); - b43_phy_maskset(dev, 0x0802, 0xFFFC, 0x0003); - b43_phy_mask(dev, 0x0403, 0xFFF8); - b43_phy_set(dev, B43_PHY_G_CRS, 0x8000); - b43_set_original_gains(dev); - for (i = 0; i < 13; i++) { - if (!ret[i]) - continue; - end = (i + 5 < 13) ? i + 5 : 13; - for (j = i; j < end; j++) - ret[j] = 1; - } - b43_radio_unlock(dev); - b43_phy_unlock(dev); - - return ret[channel - 1]; -} - -static s32 b43_tssi2dbm_ad(s32 num, s32 den) -{ - if (num < 0) - return num / den; - else - return (num + den / 2) / den; -} - -static s8 b43_tssi2dbm_entry(s8 entry[], u8 index, - s16 pab0, s16 pab1, s16 pab2) -{ - s32 m1, m2, f = 256, q, delta; - s8 i = 0; - - m1 = b43_tssi2dbm_ad(16 * pab0 + index * pab1, 32); - m2 = max(b43_tssi2dbm_ad(32768 + index * pab2, 256), 1); - do { - if (i > 15) - return -EINVAL; - q = b43_tssi2dbm_ad(f * 4096 - - b43_tssi2dbm_ad(m2 * f, 16) * f, 2048); - delta = abs(q - f); - f = q; - i++; - } while (delta >= 2); - entry[index] = clamp_val(b43_tssi2dbm_ad(m1 * f, 8192), -127, 128); - return 0; -} - -u8 *b43_generate_dyn_tssi2dbm_tab(struct b43_wldev *dev, - s16 pab0, s16 pab1, s16 pab2) -{ - unsigned int i; - u8 *tab; - int err; - - tab = kmalloc(64, GFP_KERNEL); - if (!tab) { - b43err(dev->wl, "Could not allocate memory " - "for tssi2dbm table\n"); - return NULL; - } - for (i = 0; i < 64; i++) { - err = b43_tssi2dbm_entry(tab, i, pab0, pab1, pab2); - if (err) { - b43err(dev->wl, "Could not generate " - "tssi2dBm table\n"); - kfree(tab); - return NULL; - } - } - - return tab; -} - -/* Initialise the TSSI->dBm lookup table */ -static int b43_gphy_init_tssi2dbm_table(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - s16 pab0, pab1, pab2; - - pab0 = (s16) (dev->dev->bus_sprom->pa0b0); - pab1 = (s16) (dev->dev->bus_sprom->pa0b1); - pab2 = (s16) (dev->dev->bus_sprom->pa0b2); - - B43_WARN_ON((dev->dev->chip_id == 0x4301) && - (phy->radio_ver != 0x2050)); /* Not supported anymore */ - - gphy->dyn_tssi_tbl = false; - - if (pab0 != 0 && pab1 != 0 && pab2 != 0 && - pab0 != -1 && pab1 != -1 && pab2 != -1) { - /* The pabX values are set in SPROM. Use them. */ - if ((s8) dev->dev->bus_sprom->itssi_bg != 0 && - (s8) dev->dev->bus_sprom->itssi_bg != -1) { - gphy->tgt_idle_tssi = - (s8) (dev->dev->bus_sprom->itssi_bg); - } else - gphy->tgt_idle_tssi = 62; - gphy->tssi2dbm = b43_generate_dyn_tssi2dbm_tab(dev, pab0, - pab1, pab2); - if (!gphy->tssi2dbm) - return -ENOMEM; - gphy->dyn_tssi_tbl = true; - } else { - /* pabX values not set in SPROM. */ - gphy->tgt_idle_tssi = 52; - gphy->tssi2dbm = b43_tssi2dbm_g_table; - } - - return 0; -} - -static int b43_gphy_op_allocate(struct b43_wldev *dev) -{ - struct b43_phy_g *gphy; - struct b43_txpower_lo_control *lo; - int err; - - gphy = kzalloc(sizeof(*gphy), GFP_KERNEL); - if (!gphy) { - err = -ENOMEM; - goto error; - } - dev->phy.g = gphy; - - lo = kzalloc(sizeof(*lo), GFP_KERNEL); - if (!lo) { - err = -ENOMEM; - goto err_free_gphy; - } - gphy->lo_control = lo; - - err = b43_gphy_init_tssi2dbm_table(dev); - if (err) - goto err_free_lo; - - return 0; - -err_free_lo: - kfree(lo); -err_free_gphy: - kfree(gphy); -error: - return err; -} - -static void b43_gphy_op_prepare_structs(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - const void *tssi2dbm; - int tgt_idle_tssi; - struct b43_txpower_lo_control *lo; - unsigned int i; - - /* tssi2dbm table is constant, so it is initialized at alloc time. - * Save a copy of the pointer. */ - tssi2dbm = gphy->tssi2dbm; - tgt_idle_tssi = gphy->tgt_idle_tssi; - /* Save the LO pointer. */ - lo = gphy->lo_control; - - /* Zero out the whole PHY structure. */ - memset(gphy, 0, sizeof(*gphy)); - - /* Restore pointers. */ - gphy->tssi2dbm = tssi2dbm; - gphy->tgt_idle_tssi = tgt_idle_tssi; - gphy->lo_control = lo; - - memset(gphy->minlowsig, 0xFF, sizeof(gphy->minlowsig)); - - /* NRSSI */ - for (i = 0; i < ARRAY_SIZE(gphy->nrssi); i++) - gphy->nrssi[i] = -1000; - for (i = 0; i < ARRAY_SIZE(gphy->nrssi_lt); i++) - gphy->nrssi_lt[i] = i; - - gphy->lofcal = 0xFFFF; - gphy->initval = 0xFFFF; - - gphy->interfmode = B43_INTERFMODE_NONE; - - /* OFDM-table address caching. */ - gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_UNKNOWN; - - gphy->average_tssi = 0xFF; - - /* Local Osciallator structure */ - lo->tx_bias = 0xFF; - INIT_LIST_HEAD(&lo->calib_list); -} - -static void b43_gphy_op_free(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - - kfree(gphy->lo_control); - - if (gphy->dyn_tssi_tbl) - kfree(gphy->tssi2dbm); - gphy->dyn_tssi_tbl = false; - gphy->tssi2dbm = NULL; - - kfree(gphy); - dev->phy.g = NULL; -} - -static int b43_gphy_op_prepare_hardware(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - struct b43_txpower_lo_control *lo = gphy->lo_control; - - B43_WARN_ON(phy->type != B43_PHYTYPE_G); - - default_baseband_attenuation(dev, &gphy->bbatt); - default_radio_attenuation(dev, &gphy->rfatt); - gphy->tx_control = (default_tx_control(dev) << 4); - generate_rfatt_list(dev, &lo->rfatt_list); - generate_bbatt_list(dev, &lo->bbatt_list); - - /* Commit previous writes */ - b43_read32(dev, B43_MMIO_MACCTL); - - if (phy->rev == 1) { - /* Workaround: Temporarly disable gmode through the early init - * phase, as the gmode stuff is not needed for phy rev 1 */ - phy->gmode = false; - b43_wireless_core_reset(dev, 0); - b43_phy_initg(dev); - phy->gmode = true; - b43_wireless_core_reset(dev, 1); - } - - return 0; -} - -static int b43_gphy_op_init(struct b43_wldev *dev) -{ - b43_phy_initg(dev); - - return 0; -} - -static void b43_gphy_op_exit(struct b43_wldev *dev) -{ - b43_lo_g_cleanup(dev); -} - -static u16 b43_gphy_op_read(struct b43_wldev *dev, u16 reg) -{ - b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); - return b43_read16(dev, B43_MMIO_PHY_DATA); -} - -static void b43_gphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); - b43_write16(dev, B43_MMIO_PHY_DATA, value); -} - -static u16 b43_gphy_op_radio_read(struct b43_wldev *dev, u16 reg) -{ - /* Register 1 is a 32-bit register. */ - B43_WARN_ON(reg == 1); - /* G-PHY needs 0x80 for read access. */ - reg |= 0x80; - - b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); - return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); -} - -static void b43_gphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - /* Register 1 is a 32-bit register. */ - B43_WARN_ON(reg == 1); - - b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); - b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); -} - -static bool b43_gphy_op_supports_hwpctl(struct b43_wldev *dev) -{ - return (dev->phy.rev >= 6); -} - -static void b43_gphy_op_software_rfkill(struct b43_wldev *dev, - bool blocked) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - unsigned int channel; - - might_sleep(); - - if (!blocked) { - /* Turn radio ON */ - if (phy->radio_on) - return; - - b43_phy_write(dev, 0x0015, 0x8000); - b43_phy_write(dev, 0x0015, 0xCC00); - b43_phy_write(dev, 0x0015, (phy->gmode ? 0x00C0 : 0x0000)); - if (gphy->radio_off_context.valid) { - /* Restore the RFover values. */ - b43_phy_write(dev, B43_PHY_RFOVER, - gphy->radio_off_context.rfover); - b43_phy_write(dev, B43_PHY_RFOVERVAL, - gphy->radio_off_context.rfoverval); - gphy->radio_off_context.valid = false; - } - channel = phy->channel; - b43_gphy_channel_switch(dev, 6, 1); - b43_gphy_channel_switch(dev, channel, 0); - } else { - /* Turn radio OFF */ - u16 rfover, rfoverval; - - rfover = b43_phy_read(dev, B43_PHY_RFOVER); - rfoverval = b43_phy_read(dev, B43_PHY_RFOVERVAL); - gphy->radio_off_context.rfover = rfover; - gphy->radio_off_context.rfoverval = rfoverval; - gphy->radio_off_context.valid = true; - b43_phy_write(dev, B43_PHY_RFOVER, rfover | 0x008C); - b43_phy_write(dev, B43_PHY_RFOVERVAL, rfoverval & 0xFF73); - } -} - -static int b43_gphy_op_switch_channel(struct b43_wldev *dev, - unsigned int new_channel) -{ - if ((new_channel < 1) || (new_channel > 14)) - return -EINVAL; - b43_gphy_channel_switch(dev, new_channel, 0); - - return 0; -} - -static unsigned int b43_gphy_op_get_default_chan(struct b43_wldev *dev) -{ - return 1; /* Default to channel 1 */ -} - -static void b43_gphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) -{ - struct b43_phy *phy = &dev->phy; - u16 tmp; - int autodiv = 0; - - if (antenna == B43_ANTENNA_AUTO0 || antenna == B43_ANTENNA_AUTO1) - autodiv = 1; - - b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP); - - b43_phy_maskset(dev, B43_PHY_BBANDCFG, ~B43_PHY_BBANDCFG_RXANT, - (autodiv ? B43_ANTENNA_AUTO1 : antenna) << - B43_PHY_BBANDCFG_RXANT_SHIFT); - - if (autodiv) { - tmp = b43_phy_read(dev, B43_PHY_ANTDWELL); - if (antenna == B43_ANTENNA_AUTO1) - tmp &= ~B43_PHY_ANTDWELL_AUTODIV1; - else - tmp |= B43_PHY_ANTDWELL_AUTODIV1; - b43_phy_write(dev, B43_PHY_ANTDWELL, tmp); - } - - tmp = b43_phy_read(dev, B43_PHY_ANTWRSETT); - if (autodiv) - tmp |= B43_PHY_ANTWRSETT_ARXDIV; - else - tmp &= ~B43_PHY_ANTWRSETT_ARXDIV; - b43_phy_write(dev, B43_PHY_ANTWRSETT, tmp); - - if (autodiv) - b43_phy_set(dev, B43_PHY_ANTWRSETT, B43_PHY_ANTWRSETT_ARXDIV); - else { - b43_phy_mask(dev, B43_PHY_ANTWRSETT, - B43_PHY_ANTWRSETT_ARXDIV); - } - - if (phy->rev >= 2) { - b43_phy_set(dev, B43_PHY_OFDM61, B43_PHY_OFDM61_10); - b43_phy_maskset(dev, B43_PHY_DIVSRCHGAINBACK, 0xFF00, 0x15); - - if (phy->rev == 2) - b43_phy_write(dev, B43_PHY_ADIVRELATED, 8); - else - b43_phy_maskset(dev, B43_PHY_ADIVRELATED, 0xFF00, 8); - } - if (phy->rev >= 6) - b43_phy_write(dev, B43_PHY_OFDM9B, 0xDC); - - b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP); -} - -static int b43_gphy_op_interf_mitigation(struct b43_wldev *dev, - enum b43_interference_mitigation mode) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - int currentmode; - - B43_WARN_ON(phy->type != B43_PHYTYPE_G); - if ((phy->rev == 0) || (!phy->gmode)) - return -ENODEV; - - gphy->aci_wlan_automatic = false; - switch (mode) { - case B43_INTERFMODE_AUTOWLAN: - gphy->aci_wlan_automatic = true; - if (gphy->aci_enable) - mode = B43_INTERFMODE_MANUALWLAN; - else - mode = B43_INTERFMODE_NONE; - break; - case B43_INTERFMODE_NONE: - case B43_INTERFMODE_NONWLAN: - case B43_INTERFMODE_MANUALWLAN: - break; - default: - return -EINVAL; - } - - currentmode = gphy->interfmode; - if (currentmode == mode) - return 0; - if (currentmode != B43_INTERFMODE_NONE) - b43_radio_interference_mitigation_disable(dev, currentmode); - - if (mode == B43_INTERFMODE_NONE) { - gphy->aci_enable = false; - gphy->aci_hw_rssi = false; - } else - b43_radio_interference_mitigation_enable(dev, mode); - gphy->interfmode = mode; - - return 0; -} - -/* http://bcm-specs.sipsolutions.net/EstimatePowerOut - * This function converts a TSSI value to dBm in Q5.2 - */ -static s8 b43_gphy_estimate_power_out(struct b43_wldev *dev, s8 tssi) -{ - struct b43_phy_g *gphy = dev->phy.g; - s8 dbm; - s32 tmp; - - tmp = (gphy->tgt_idle_tssi - gphy->cur_idle_tssi + tssi); - tmp = clamp_val(tmp, 0x00, 0x3F); - dbm = gphy->tssi2dbm[tmp]; - - return dbm; -} - -static void b43_put_attenuation_into_ranges(struct b43_wldev *dev, - int *_bbatt, int *_rfatt) -{ - int rfatt = *_rfatt; - int bbatt = *_bbatt; - struct b43_txpower_lo_control *lo = dev->phy.g->lo_control; - - /* Get baseband and radio attenuation values into their permitted ranges. - * Radio attenuation affects power level 4 times as much as baseband. */ - - /* Range constants */ - const int rf_min = lo->rfatt_list.min_val; - const int rf_max = lo->rfatt_list.max_val; - const int bb_min = lo->bbatt_list.min_val; - const int bb_max = lo->bbatt_list.max_val; - - while (1) { - if (rfatt > rf_max && bbatt > bb_max - 4) - break; /* Can not get it into ranges */ - if (rfatt < rf_min && bbatt < bb_min + 4) - break; /* Can not get it into ranges */ - if (bbatt > bb_max && rfatt > rf_max - 1) - break; /* Can not get it into ranges */ - if (bbatt < bb_min && rfatt < rf_min + 1) - break; /* Can not get it into ranges */ - - if (bbatt > bb_max) { - bbatt -= 4; - rfatt += 1; - continue; - } - if (bbatt < bb_min) { - bbatt += 4; - rfatt -= 1; - continue; - } - if (rfatt > rf_max) { - rfatt -= 1; - bbatt += 4; - continue; - } - if (rfatt < rf_min) { - rfatt += 1; - bbatt -= 4; - continue; - } - break; - } - - *_rfatt = clamp_val(rfatt, rf_min, rf_max); - *_bbatt = clamp_val(bbatt, bb_min, bb_max); -} - -static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - int rfatt, bbatt; - u8 tx_control; - - b43_mac_suspend(dev); - - /* Calculate the new attenuation values. */ - bbatt = gphy->bbatt.att; - bbatt += gphy->bbatt_delta; - rfatt = gphy->rfatt.att; - rfatt += gphy->rfatt_delta; - - b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt); - tx_control = gphy->tx_control; - if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 2)) { - if (rfatt <= 1) { - if (tx_control == 0) { - tx_control = - B43_TXCTL_PA2DB | - B43_TXCTL_TXMIX; - rfatt += 2; - bbatt += 2; - } else if (dev->dev->bus_sprom-> - boardflags_lo & - B43_BFL_PACTRL) { - bbatt += 4 * (rfatt - 2); - rfatt = 2; - } - } else if (rfatt > 4 && tx_control) { - tx_control = 0; - if (bbatt < 3) { - rfatt -= 3; - bbatt += 2; - } else { - rfatt -= 2; - bbatt -= 2; - } - } - } - /* Save the control values */ - gphy->tx_control = tx_control; - b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt); - gphy->rfatt.att = rfatt; - gphy->bbatt.att = bbatt; - - if (b43_debug(dev, B43_DBG_XMITPOWER)) - b43dbg(dev->wl, "Adjusting TX power\n"); - - /* Adjust the hardware */ - b43_phy_lock(dev); - b43_radio_lock(dev); - b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, - gphy->tx_control); - b43_radio_unlock(dev); - b43_phy_unlock(dev); - - b43_mac_enable(dev); -} - -static enum b43_txpwr_result b43_gphy_op_recalc_txpower(struct b43_wldev *dev, - bool ignore_tssi) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - unsigned int average_tssi; - int cck_result, ofdm_result; - int estimated_pwr, desired_pwr, pwr_adjust; - int rfatt_delta, bbatt_delta; - unsigned int max_pwr; - - /* First get the average TSSI */ - cck_result = b43_phy_shm_tssi_read(dev, B43_SHM_SH_TSSI_CCK); - ofdm_result = b43_phy_shm_tssi_read(dev, B43_SHM_SH_TSSI_OFDM_G); - if ((cck_result < 0) && (ofdm_result < 0)) { - /* No TSSI information available */ - if (!ignore_tssi) - goto no_adjustment_needed; - cck_result = 0; - ofdm_result = 0; - } - if (cck_result < 0) - average_tssi = ofdm_result; - else if (ofdm_result < 0) - average_tssi = cck_result; - else - average_tssi = (cck_result + ofdm_result) / 2; - /* Merge the average with the stored value. */ - if (likely(gphy->average_tssi != 0xFF)) - average_tssi = (average_tssi + gphy->average_tssi) / 2; - gphy->average_tssi = average_tssi; - B43_WARN_ON(average_tssi >= B43_TSSI_MAX); - - /* Estimate the TX power emission based on the TSSI */ - estimated_pwr = b43_gphy_estimate_power_out(dev, average_tssi); - - B43_WARN_ON(phy->type != B43_PHYTYPE_G); - max_pwr = dev->dev->bus_sprom->maxpwr_bg; - if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) - max_pwr -= 3; /* minus 0.75 */ - if (unlikely(max_pwr >= INT_TO_Q52(30/*dBm*/))) { - b43warn(dev->wl, - "Invalid max-TX-power value in SPROM.\n"); - max_pwr = INT_TO_Q52(20); /* fake it */ - dev->dev->bus_sprom->maxpwr_bg = max_pwr; - } - - /* Get desired power (in Q5.2) */ - if (phy->desired_txpower < 0) - desired_pwr = INT_TO_Q52(0); - else - desired_pwr = INT_TO_Q52(phy->desired_txpower); - /* And limit it. max_pwr already is Q5.2 */ - desired_pwr = clamp_val(desired_pwr, 0, max_pwr); - if (b43_debug(dev, B43_DBG_XMITPOWER)) { - b43dbg(dev->wl, - "[TX power] current = " Q52_FMT - " dBm, desired = " Q52_FMT - " dBm, max = " Q52_FMT "\n", - Q52_ARG(estimated_pwr), - Q52_ARG(desired_pwr), - Q52_ARG(max_pwr)); - } - - /* Calculate the adjustment delta. */ - pwr_adjust = desired_pwr - estimated_pwr; - if (pwr_adjust == 0) - goto no_adjustment_needed; - - /* RF attenuation delta. */ - rfatt_delta = ((pwr_adjust + 7) / 8); - /* Lower attenuation => Bigger power output. Negate it. */ - rfatt_delta = -rfatt_delta; - - /* Baseband attenuation delta. */ - bbatt_delta = pwr_adjust / 2; - /* Lower attenuation => Bigger power output. Negate it. */ - bbatt_delta = -bbatt_delta; - /* RF att affects power level 4 times as much as - * Baseband attennuation. Subtract it. */ - bbatt_delta -= 4 * rfatt_delta; - -#if B43_DEBUG - if (b43_debug(dev, B43_DBG_XMITPOWER)) { - int dbm = pwr_adjust < 0 ? -pwr_adjust : pwr_adjust; - b43dbg(dev->wl, - "[TX power deltas] %s" Q52_FMT " dBm => " - "bbatt-delta = %d, rfatt-delta = %d\n", - (pwr_adjust < 0 ? "-" : ""), Q52_ARG(dbm), - bbatt_delta, rfatt_delta); - } -#endif /* DEBUG */ - - /* So do we finally need to adjust something in hardware? */ - if ((rfatt_delta == 0) && (bbatt_delta == 0)) - goto no_adjustment_needed; - - /* Save the deltas for later when we adjust the power. */ - gphy->bbatt_delta = bbatt_delta; - gphy->rfatt_delta = rfatt_delta; - - /* We need to adjust the TX power on the device. */ - return B43_TXPWR_RES_NEED_ADJUST; - -no_adjustment_needed: - return B43_TXPWR_RES_DONE; -} - -static void b43_gphy_op_pwork_15sec(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - - b43_mac_suspend(dev); - //TODO: update_aci_moving_average - if (gphy->aci_enable && gphy->aci_wlan_automatic) { - if (!gphy->aci_enable && 1 /*TODO: not scanning? */ ) { - if (0 /*TODO: bunch of conditions */ ) { - phy->ops->interf_mitigation(dev, - B43_INTERFMODE_MANUALWLAN); - } - } else if (0 /*TODO*/) { - if (/*(aci_average > 1000) &&*/ !b43_gphy_aci_scan(dev)) - phy->ops->interf_mitigation(dev, B43_INTERFMODE_NONE); - } - } else if (gphy->interfmode == B43_INTERFMODE_NONWLAN && - phy->rev == 1) { - //TODO: implement rev1 workaround - } - b43_lo_g_maintenance_work(dev); - b43_mac_enable(dev); -} - -static void b43_gphy_op_pwork_60sec(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI)) - return; - - b43_mac_suspend(dev); - b43_calc_nrssi_slope(dev); - if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 8)) { - u8 old_chan = phy->channel; - - /* VCO Calibration */ - if (old_chan >= 8) - b43_switch_channel(dev, 1); - else - b43_switch_channel(dev, 13); - b43_switch_channel(dev, old_chan); - } - b43_mac_enable(dev); -} - -const struct b43_phy_operations b43_phyops_g = { - .allocate = b43_gphy_op_allocate, - .free = b43_gphy_op_free, - .prepare_structs = b43_gphy_op_prepare_structs, - .prepare_hardware = b43_gphy_op_prepare_hardware, - .init = b43_gphy_op_init, - .exit = b43_gphy_op_exit, - .phy_read = b43_gphy_op_read, - .phy_write = b43_gphy_op_write, - .radio_read = b43_gphy_op_radio_read, - .radio_write = b43_gphy_op_radio_write, - .supports_hwpctl = b43_gphy_op_supports_hwpctl, - .software_rfkill = b43_gphy_op_software_rfkill, - .switch_analog = b43_phyop_switch_analog_generic, - .switch_channel = b43_gphy_op_switch_channel, - .get_default_chan = b43_gphy_op_get_default_chan, - .set_rx_antenna = b43_gphy_op_set_rx_antenna, - .interf_mitigation = b43_gphy_op_interf_mitigation, - .recalc_txpower = b43_gphy_op_recalc_txpower, - .adjust_txpower = b43_gphy_op_adjust_txpower, - .pwork_15sec = b43_gphy_op_pwork_15sec, - .pwork_60sec = b43_gphy_op_pwork_60sec, -}; diff --git a/drivers/net/wireless/b43/phy_g.h b/drivers/net/wireless/b43/phy_g.h deleted file mode 100644 index 5413c906a3e7..000000000000 --- a/drivers/net/wireless/b43/phy_g.h +++ /dev/null @@ -1,208 +0,0 @@ -#ifndef LINUX_B43_PHY_G_H_ -#define LINUX_B43_PHY_G_H_ - -/* OFDM PHY registers are defined in the A-PHY header. */ -#include "phy_a.h" - -/* CCK (B) PHY Registers */ -#define B43_PHY_VERSION_CCK B43_PHY_CCK(0x00) /* Versioning register for B-PHY */ -#define B43_PHY_CCKBBANDCFG B43_PHY_CCK(0x01) /* Contains antenna 0/1 control bit */ -#define B43_PHY_PGACTL B43_PHY_CCK(0x15) /* PGA control */ -#define B43_PHY_PGACTL_LPF 0x1000 /* Low pass filter (?) */ -#define B43_PHY_PGACTL_LOWBANDW 0x0040 /* Low bandwidth flag */ -#define B43_PHY_PGACTL_UNKNOWN 0xEFA0 -#define B43_PHY_FBCTL1 B43_PHY_CCK(0x18) /* Frequency bandwidth control 1 */ -#define B43_PHY_ITSSI B43_PHY_CCK(0x29) /* Idle TSSI */ -#define B43_PHY_LO_LEAKAGE B43_PHY_CCK(0x2D) /* Measured LO leakage */ -#define B43_PHY_ENERGY B43_PHY_CCK(0x33) /* Energy */ -#define B43_PHY_SYNCCTL B43_PHY_CCK(0x35) -#define B43_PHY_FBCTL2 B43_PHY_CCK(0x38) /* Frequency bandwidth control 2 */ -#define B43_PHY_DACCTL B43_PHY_CCK(0x60) /* DAC control */ -#define B43_PHY_RCCALOVER B43_PHY_CCK(0x78) /* RC calibration override */ - -/* Extended G-PHY Registers */ -#define B43_PHY_CLASSCTL B43_PHY_EXTG(0x02) /* Classify control */ -#define B43_PHY_GTABCTL B43_PHY_EXTG(0x03) /* G-PHY table control (see below) */ -#define B43_PHY_GTABOFF 0x03FF /* G-PHY table offset (see below) */ -#define B43_PHY_GTABNR 0xFC00 /* G-PHY table number (see below) */ -#define B43_PHY_GTABNR_SHIFT 10 -#define B43_PHY_GTABDATA B43_PHY_EXTG(0x04) /* G-PHY table data */ -#define B43_PHY_LO_MASK B43_PHY_EXTG(0x0F) /* Local Oscillator control mask */ -#define B43_PHY_LO_CTL B43_PHY_EXTG(0x10) /* Local Oscillator control */ -#define B43_PHY_RFOVER B43_PHY_EXTG(0x11) /* RF override */ -#define B43_PHY_RFOVERVAL B43_PHY_EXTG(0x12) /* RF override value */ -#define B43_PHY_RFOVERVAL_EXTLNA 0x8000 -#define B43_PHY_RFOVERVAL_LNA 0x7000 -#define B43_PHY_RFOVERVAL_LNA_SHIFT 12 -#define B43_PHY_RFOVERVAL_PGA 0x0F00 -#define B43_PHY_RFOVERVAL_PGA_SHIFT 8 -#define B43_PHY_RFOVERVAL_UNK 0x0010 /* Unknown, always set. */ -#define B43_PHY_RFOVERVAL_TRSWRX 0x00E0 -#define B43_PHY_RFOVERVAL_BW 0x0003 /* Bandwidth flags */ -#define B43_PHY_RFOVERVAL_BW_LPF 0x0001 /* Low Pass Filter */ -#define B43_PHY_RFOVERVAL_BW_LBW 0x0002 /* Low Bandwidth (when set), high when unset */ -#define B43_PHY_ANALOGOVER B43_PHY_EXTG(0x14) /* Analog override */ -#define B43_PHY_ANALOGOVERVAL B43_PHY_EXTG(0x15) /* Analog override value */ - - -/*** G-PHY table numbers */ -#define B43_GTAB(number, offset) (((number) << B43_PHY_GTABNR_SHIFT) | (offset)) -#define B43_GTAB_NRSSI B43_GTAB(0x00, 0) -#define B43_GTAB_TRFEMW B43_GTAB(0x0C, 0x120) -#define B43_GTAB_ORIGTR B43_GTAB(0x2E, 0x298) - -u16 b43_gtab_read(struct b43_wldev *dev, u16 table, u16 offset); -void b43_gtab_write(struct b43_wldev *dev, u16 table, u16 offset, u16 value); - - -/* Returns the boolean whether "TX Magnification" is enabled. */ -#define has_tx_magnification(phy) \ - (((phy)->rev >= 2) && \ - ((phy)->radio_ver == 0x2050) && \ - ((phy)->radio_rev == 8)) -/* Card uses the loopback gain stuff */ -#define has_loopback_gain(phy) \ - (((phy)->rev > 1) || ((phy)->gmode)) - -/* Radio Attenuation (RF Attenuation) */ -struct b43_rfatt { - u8 att; /* Attenuation value */ - bool with_padmix; /* Flag, PAD Mixer enabled. */ -}; -struct b43_rfatt_list { - /* Attenuation values list */ - const struct b43_rfatt *list; - u8 len; - /* Minimum/Maximum attenuation values */ - u8 min_val; - u8 max_val; -}; - -/* Returns true, if the values are the same. */ -static inline bool b43_compare_rfatt(const struct b43_rfatt *a, - const struct b43_rfatt *b) -{ - return ((a->att == b->att) && - (a->with_padmix == b->with_padmix)); -} - -/* Baseband Attenuation */ -struct b43_bbatt { - u8 att; /* Attenuation value */ -}; -struct b43_bbatt_list { - /* Attenuation values list */ - const struct b43_bbatt *list; - u8 len; - /* Minimum/Maximum attenuation values */ - u8 min_val; - u8 max_val; -}; - -/* Returns true, if the values are the same. */ -static inline bool b43_compare_bbatt(const struct b43_bbatt *a, - const struct b43_bbatt *b) -{ - return (a->att == b->att); -} - -/* tx_control bits. */ -#define B43_TXCTL_PA3DB 0x40 /* PA Gain 3dB */ -#define B43_TXCTL_PA2DB 0x20 /* PA Gain 2dB */ -#define B43_TXCTL_TXMIX 0x10 /* TX Mixer Gain */ - -struct b43_txpower_lo_control; - -struct b43_phy_g { - /* ACI (adjacent channel interference) flags. */ - bool aci_enable; - bool aci_wlan_automatic; - bool aci_hw_rssi; - - /* Radio switched on/off */ - bool radio_on; - struct { - /* Values saved when turning the radio off. - * They are needed when turning it on again. */ - bool valid; - u16 rfover; - u16 rfoverval; - } radio_off_context; - - u16 minlowsig[2]; - u16 minlowsigpos[2]; - - /* Pointer to the table used to convert a - * TSSI value to dBm-Q5.2 */ - const s8 *tssi2dbm; - /* tssi2dbm is kmalloc()ed. Only used for free()ing. */ - bool dyn_tssi_tbl; - /* Target idle TSSI */ - int tgt_idle_tssi; - /* Current idle TSSI */ - int cur_idle_tssi; - /* The current average TSSI. */ - u8 average_tssi; - /* Current TX power level attenuation control values */ - struct b43_bbatt bbatt; - struct b43_rfatt rfatt; - u8 tx_control; /* B43_TXCTL_XXX */ - /* The calculated attenuation deltas that are used later - * when adjusting the actual power output. */ - int bbatt_delta; - int rfatt_delta; - - /* LocalOscillator control values. */ - struct b43_txpower_lo_control *lo_control; - /* Values from b43_calc_loopback_gain() */ - s16 max_lb_gain; /* Maximum Loopback gain in hdB */ - s16 trsw_rx_gain; /* TRSW RX gain in hdB */ - s16 lna_lod_gain; /* LNA lod */ - s16 lna_gain; /* LNA */ - s16 pga_gain; /* PGA */ - - /* Current Interference Mitigation mode */ - int interfmode; - /* Stack of saved values from the Interference Mitigation code. - * Each value in the stack is laid out as follows: - * bit 0-11: offset - * bit 12-15: register ID - * bit 16-32: value - * register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT - */ -#define B43_INTERFSTACK_SIZE 26 - u32 interfstack[B43_INTERFSTACK_SIZE]; //FIXME: use a data structure - - /* Saved values from the NRSSI Slope calculation */ - s16 nrssi[2]; - s32 nrssislope; - /* In memory nrssi lookup table. */ - s8 nrssi_lt[64]; - - u16 lofcal; - - u16 initval; //FIXME rename? - - /* The device does address auto increment for the OFDM tables. - * We cache the previously used address here and omit the address - * write on the next table access, if possible. */ - u16 ofdmtab_addr; /* The address currently set in hardware. */ - enum { /* The last data flow direction. */ - B43_OFDMTAB_DIRECTION_UNKNOWN = 0, - B43_OFDMTAB_DIRECTION_READ, - B43_OFDMTAB_DIRECTION_WRITE, - } ofdmtab_addr_direction; -}; - -void b43_gphy_set_baseband_attenuation(struct b43_wldev *dev, - u16 baseband_attenuation); -void b43_gphy_channel_switch(struct b43_wldev *dev, - unsigned int channel, - bool synthetic_pu_workaround); -u8 * b43_generate_dyn_tssi2dbm_tab(struct b43_wldev *dev, - s16 pab0, s16 pab1, s16 pab2); - -struct b43_phy_operations; -extern const struct b43_phy_operations b43_phyops_g; - -#endif /* LINUX_B43_PHY_G_H_ */ diff --git a/drivers/net/wireless/b43/phy_ht.c b/drivers/net/wireless/b43/phy_ht.c deleted file mode 100644 index bd68945965d6..000000000000 --- a/drivers/net/wireless/b43/phy_ht.c +++ /dev/null @@ -1,1153 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11n HT-PHY support - - Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include - -#include "b43.h" -#include "phy_ht.h" -#include "tables_phy_ht.h" -#include "radio_2059.h" -#include "main.h" - -/* Force values to keep compatibility with wl */ -enum ht_rssi_type { - HT_RSSI_W1 = 0, - HT_RSSI_W2 = 1, - HT_RSSI_NB = 2, - HT_RSSI_IQ = 3, - HT_RSSI_TSSI_2G = 4, - HT_RSSI_TSSI_5G = 5, - HT_RSSI_TBD = 6, -}; - -/************************************************** - * Radio 2059. - **************************************************/ - -static void b43_radio_2059_channel_setup(struct b43_wldev *dev, - const struct b43_phy_ht_channeltab_e_radio2059 *e) -{ - static const u16 routing[] = { R2059_C1, R2059_C2, R2059_C3, }; - u16 r; - int core; - - b43_radio_write(dev, 0x16, e->radio_syn16); - b43_radio_write(dev, 0x17, e->radio_syn17); - b43_radio_write(dev, 0x22, e->radio_syn22); - b43_radio_write(dev, 0x25, e->radio_syn25); - b43_radio_write(dev, 0x27, e->radio_syn27); - b43_radio_write(dev, 0x28, e->radio_syn28); - b43_radio_write(dev, 0x29, e->radio_syn29); - b43_radio_write(dev, 0x2c, e->radio_syn2c); - b43_radio_write(dev, 0x2d, e->radio_syn2d); - b43_radio_write(dev, 0x37, e->radio_syn37); - b43_radio_write(dev, 0x41, e->radio_syn41); - b43_radio_write(dev, 0x43, e->radio_syn43); - b43_radio_write(dev, 0x47, e->radio_syn47); - - for (core = 0; core < 3; core++) { - r = routing[core]; - b43_radio_write(dev, r | 0x4a, e->radio_rxtx4a); - b43_radio_write(dev, r | 0x58, e->radio_rxtx58); - b43_radio_write(dev, r | 0x5a, e->radio_rxtx5a); - b43_radio_write(dev, r | 0x6a, e->radio_rxtx6a); - b43_radio_write(dev, r | 0x6d, e->radio_rxtx6d); - b43_radio_write(dev, r | 0x6e, e->radio_rxtx6e); - b43_radio_write(dev, r | 0x92, e->radio_rxtx92); - b43_radio_write(dev, r | 0x98, e->radio_rxtx98); - } - - udelay(50); - - /* Calibration */ - b43_radio_mask(dev, R2059_RFPLL_MISC_EN, ~0x1); - b43_radio_mask(dev, R2059_RFPLL_MISC_CAL_RESETN, ~0x4); - b43_radio_set(dev, R2059_RFPLL_MISC_CAL_RESETN, 0x4); - b43_radio_set(dev, R2059_RFPLL_MISC_EN, 0x1); - - udelay(300); -} - -/* Calibrate resistors in LPF of PLL? */ -static void b43_radio_2059_rcal(struct b43_wldev *dev) -{ - /* Enable */ - b43_radio_set(dev, R2059_C3 | R2059_RCAL_CONFIG, 0x1); - usleep_range(10, 20); - - b43_radio_set(dev, R2059_C3 | 0x0BF, 0x1); - b43_radio_maskset(dev, R2059_C3 | 0x19B, 0x3, 0x2); - - /* Start */ - b43_radio_set(dev, R2059_C3 | R2059_RCAL_CONFIG, 0x2); - usleep_range(100, 200); - - /* Stop */ - b43_radio_mask(dev, R2059_C3 | R2059_RCAL_CONFIG, ~0x2); - - if (!b43_radio_wait_value(dev, R2059_C3 | R2059_RCAL_STATUS, 1, 1, 100, - 1000000)) - b43err(dev->wl, "Radio 0x2059 rcal timeout\n"); - - /* Disable */ - b43_radio_mask(dev, R2059_C3 | R2059_RCAL_CONFIG, ~0x1); - - b43_radio_set(dev, 0xa, 0x60); -} - -/* Calibrate the internal RC oscillator? */ -static void b43_radio_2057_rccal(struct b43_wldev *dev) -{ - const u16 radio_values[3][2] = { - { 0x61, 0xE9 }, { 0x69, 0xD5 }, { 0x73, 0x99 }, - }; - int i; - - for (i = 0; i < 3; i++) { - b43_radio_write(dev, R2059_RCCAL_MASTER, radio_values[i][0]); - b43_radio_write(dev, R2059_RCCAL_X1, 0x6E); - b43_radio_write(dev, R2059_RCCAL_TRC0, radio_values[i][1]); - - /* Start */ - b43_radio_write(dev, R2059_RCCAL_START_R1_Q1_P1, 0x55); - - /* Wait */ - if (!b43_radio_wait_value(dev, R2059_RCCAL_DONE_OSCCAP, 2, 2, - 500, 5000000)) - b43err(dev->wl, "Radio 0x2059 rccal timeout\n"); - - /* Stop */ - b43_radio_write(dev, R2059_RCCAL_START_R1_Q1_P1, 0x15); - } - - b43_radio_mask(dev, R2059_RCCAL_MASTER, ~0x1); -} - -static void b43_radio_2059_init_pre(struct b43_wldev *dev) -{ - b43_phy_mask(dev, B43_PHY_HT_RF_CTL_CMD, ~B43_PHY_HT_RF_CTL_CMD_CHIP0_PU); - b43_phy_set(dev, B43_PHY_HT_RF_CTL_CMD, B43_PHY_HT_RF_CTL_CMD_FORCE); - b43_phy_mask(dev, B43_PHY_HT_RF_CTL_CMD, ~B43_PHY_HT_RF_CTL_CMD_FORCE); - b43_phy_set(dev, B43_PHY_HT_RF_CTL_CMD, B43_PHY_HT_RF_CTL_CMD_CHIP0_PU); -} - -static void b43_radio_2059_init(struct b43_wldev *dev) -{ - const u16 routing[] = { R2059_C1, R2059_C2, R2059_C3 }; - int i; - - /* Prepare (reset?) radio */ - b43_radio_2059_init_pre(dev); - - r2059_upload_inittabs(dev); - - for (i = 0; i < ARRAY_SIZE(routing); i++) - b43_radio_set(dev, routing[i] | 0x146, 0x3); - - /* Post init starts below */ - - b43_radio_set(dev, R2059_RFPLL_MISC_CAL_RESETN, 0x0078); - b43_radio_set(dev, R2059_XTAL_CONFIG2, 0x0080); - msleep(2); - b43_radio_mask(dev, R2059_RFPLL_MISC_CAL_RESETN, ~0x0078); - b43_radio_mask(dev, R2059_XTAL_CONFIG2, ~0x0080); - - if (1) { /* FIXME */ - b43_radio_2059_rcal(dev); - b43_radio_2057_rccal(dev); - } - - b43_radio_mask(dev, R2059_RFPLL_MASTER, ~0x0008); -} - -/************************************************** - * RF - **************************************************/ - -static void b43_phy_ht_force_rf_sequence(struct b43_wldev *dev, u16 rf_seq) -{ - u8 i; - - u16 save_seq_mode = b43_phy_read(dev, B43_PHY_HT_RF_SEQ_MODE); - b43_phy_set(dev, B43_PHY_HT_RF_SEQ_MODE, 0x3); - - b43_phy_set(dev, B43_PHY_HT_RF_SEQ_TRIG, rf_seq); - for (i = 0; i < 200; i++) { - if (!(b43_phy_read(dev, B43_PHY_HT_RF_SEQ_STATUS) & rf_seq)) { - i = 0; - break; - } - msleep(1); - } - if (i) - b43err(dev->wl, "Forcing RF sequence timeout\n"); - - b43_phy_write(dev, B43_PHY_HT_RF_SEQ_MODE, save_seq_mode); -} - -static void b43_phy_ht_pa_override(struct b43_wldev *dev, bool enable) -{ - struct b43_phy_ht *htphy = dev->phy.ht; - static const u16 regs[3] = { B43_PHY_HT_RF_CTL_INT_C1, - B43_PHY_HT_RF_CTL_INT_C2, - B43_PHY_HT_RF_CTL_INT_C3 }; - int i; - - if (enable) { - for (i = 0; i < 3; i++) - b43_phy_write(dev, regs[i], htphy->rf_ctl_int_save[i]); - } else { - for (i = 0; i < 3; i++) - htphy->rf_ctl_int_save[i] = b43_phy_read(dev, regs[i]); - /* TODO: Does 5GHz band use different value (not 0x0400)? */ - for (i = 0; i < 3; i++) - b43_phy_write(dev, regs[i], 0x0400); - } -} - -/************************************************** - * Various PHY ops - **************************************************/ - -static u16 b43_phy_ht_classifier(struct b43_wldev *dev, u16 mask, u16 val) -{ - u16 tmp; - u16 allowed = B43_PHY_HT_CLASS_CTL_CCK_EN | - B43_PHY_HT_CLASS_CTL_OFDM_EN | - B43_PHY_HT_CLASS_CTL_WAITED_EN; - - tmp = b43_phy_read(dev, B43_PHY_HT_CLASS_CTL); - tmp &= allowed; - tmp &= ~mask; - tmp |= (val & mask); - b43_phy_maskset(dev, B43_PHY_HT_CLASS_CTL, ~allowed, tmp); - - return tmp; -} - -static void b43_phy_ht_reset_cca(struct b43_wldev *dev) -{ - u16 bbcfg; - - b43_phy_force_clock(dev, true); - bbcfg = b43_phy_read(dev, B43_PHY_HT_BBCFG); - b43_phy_write(dev, B43_PHY_HT_BBCFG, bbcfg | B43_PHY_HT_BBCFG_RSTCCA); - udelay(1); - b43_phy_write(dev, B43_PHY_HT_BBCFG, bbcfg & ~B43_PHY_HT_BBCFG_RSTCCA); - b43_phy_force_clock(dev, false); - - b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RST2RX); -} - -static void b43_phy_ht_zero_extg(struct b43_wldev *dev) -{ - u8 i, j; - u16 base[] = { 0x40, 0x60, 0x80 }; - - for (i = 0; i < ARRAY_SIZE(base); i++) { - for (j = 0; j < 4; j++) - b43_phy_write(dev, B43_PHY_EXTG(base[i] + j), 0); - } - - for (i = 0; i < ARRAY_SIZE(base); i++) - b43_phy_write(dev, B43_PHY_EXTG(base[i] + 0xc), 0); -} - -/* Some unknown AFE (Analog Frondned) op */ -static void b43_phy_ht_afe_unk1(struct b43_wldev *dev) -{ - u8 i; - - static const u16 ctl_regs[3][2] = { - { B43_PHY_HT_AFE_C1_OVER, B43_PHY_HT_AFE_C1 }, - { B43_PHY_HT_AFE_C2_OVER, B43_PHY_HT_AFE_C2 }, - { B43_PHY_HT_AFE_C3_OVER, B43_PHY_HT_AFE_C3}, - }; - - for (i = 0; i < 3; i++) { - /* TODO: verify masks&sets */ - b43_phy_set(dev, ctl_regs[i][1], 0x4); - b43_phy_set(dev, ctl_regs[i][0], 0x4); - b43_phy_mask(dev, ctl_regs[i][1], ~0x1); - b43_phy_set(dev, ctl_regs[i][0], 0x1); - b43_httab_write(dev, B43_HTTAB16(8, 5 + (i * 0x10)), 0); - b43_phy_mask(dev, ctl_regs[i][0], ~0x4); - } -} - -static void b43_phy_ht_read_clip_detection(struct b43_wldev *dev, u16 *clip_st) -{ - clip_st[0] = b43_phy_read(dev, B43_PHY_HT_C1_CLIP1THRES); - clip_st[1] = b43_phy_read(dev, B43_PHY_HT_C2_CLIP1THRES); - clip_st[2] = b43_phy_read(dev, B43_PHY_HT_C3_CLIP1THRES); -} - -static void b43_phy_ht_bphy_init(struct b43_wldev *dev) -{ - unsigned int i; - u16 val; - - val = 0x1E1F; - for (i = 0; i < 16; i++) { - b43_phy_write(dev, B43_PHY_N_BMODE(0x88 + i), val); - val -= 0x202; - } - val = 0x3E3F; - for (i = 0; i < 16; i++) { - b43_phy_write(dev, B43_PHY_N_BMODE(0x98 + i), val); - val -= 0x202; - } - b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668); -} - -static void b43_phy_ht_bphy_reset(struct b43_wldev *dev, bool reset) -{ - u16 tmp; - - tmp = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); - b43_write16(dev, B43_MMIO_PSM_PHY_HDR, - tmp | B43_PSM_HDR_MAC_PHY_FORCE_CLK); - - /* Put BPHY in or take it out of the reset */ - if (reset) - b43_phy_set(dev, B43_PHY_B_BBCFG, - B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX); - else - b43_phy_mask(dev, B43_PHY_B_BBCFG, - (u16)~(B43_PHY_B_BBCFG_RSTCCA | - B43_PHY_B_BBCFG_RSTRX)); - - b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp); -} - -/************************************************** - * Samples - **************************************************/ - -static void b43_phy_ht_stop_playback(struct b43_wldev *dev) -{ - struct b43_phy_ht *phy_ht = dev->phy.ht; - u16 tmp; - int i; - - tmp = b43_phy_read(dev, B43_PHY_HT_SAMP_STAT); - if (tmp & 0x1) - b43_phy_set(dev, B43_PHY_HT_SAMP_CMD, B43_PHY_HT_SAMP_CMD_STOP); - else if (tmp & 0x2) - b43_phy_mask(dev, B43_PHY_HT_IQLOCAL_CMDGCTL, 0x7FFF); - - b43_phy_mask(dev, B43_PHY_HT_SAMP_CMD, ~0x0004); - - for (i = 0; i < 3; i++) { - if (phy_ht->bb_mult_save[i] >= 0) { - b43_httab_write(dev, B43_HTTAB16(13, 0x63 + i * 4), - phy_ht->bb_mult_save[i]); - b43_httab_write(dev, B43_HTTAB16(13, 0x67 + i * 4), - phy_ht->bb_mult_save[i]); - } - } -} - -static u16 b43_phy_ht_load_samples(struct b43_wldev *dev) -{ - int i; - u16 len = 20 << 3; - - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, 0x4400); - - for (i = 0; i < len; i++) { - b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, 0); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, 0); - } - - return len; -} - -static void b43_phy_ht_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, - u16 wait) -{ - struct b43_phy_ht *phy_ht = dev->phy.ht; - u16 save_seq_mode; - int i; - - for (i = 0; i < 3; i++) { - if (phy_ht->bb_mult_save[i] < 0) - phy_ht->bb_mult_save[i] = b43_httab_read(dev, B43_HTTAB16(13, 0x63 + i * 4)); - } - - b43_phy_write(dev, B43_PHY_HT_SAMP_DEP_CNT, samps - 1); - if (loops != 0xFFFF) - loops--; - b43_phy_write(dev, B43_PHY_HT_SAMP_LOOP_CNT, loops); - b43_phy_write(dev, B43_PHY_HT_SAMP_WAIT_CNT, wait); - - save_seq_mode = b43_phy_read(dev, B43_PHY_HT_RF_SEQ_MODE); - b43_phy_set(dev, B43_PHY_HT_RF_SEQ_MODE, - B43_PHY_HT_RF_SEQ_MODE_CA_OVER); - - /* TODO: find out mask bits! Do we need more function arguments? */ - b43_phy_mask(dev, B43_PHY_HT_SAMP_CMD, ~0); - b43_phy_mask(dev, B43_PHY_HT_SAMP_CMD, ~0); - b43_phy_mask(dev, B43_PHY_HT_IQLOCAL_CMDGCTL, ~0); - b43_phy_set(dev, B43_PHY_HT_SAMP_CMD, 0x1); - - for (i = 0; i < 100; i++) { - if (!(b43_phy_read(dev, B43_PHY_HT_RF_SEQ_STATUS) & 1)) { - i = 0; - break; - } - udelay(10); - } - if (i) - b43err(dev->wl, "run samples timeout\n"); - - b43_phy_write(dev, B43_PHY_HT_RF_SEQ_MODE, save_seq_mode); -} - -static void b43_phy_ht_tx_tone(struct b43_wldev *dev) -{ - u16 samp; - - samp = b43_phy_ht_load_samples(dev); - b43_phy_ht_run_samples(dev, samp, 0xFFFF, 0); -} - -/************************************************** - * RSSI - **************************************************/ - -static void b43_phy_ht_rssi_select(struct b43_wldev *dev, u8 core_sel, - enum ht_rssi_type rssi_type) -{ - static const u16 ctl_regs[3][2] = { - { B43_PHY_HT_AFE_C1, B43_PHY_HT_AFE_C1_OVER, }, - { B43_PHY_HT_AFE_C2, B43_PHY_HT_AFE_C2_OVER, }, - { B43_PHY_HT_AFE_C3, B43_PHY_HT_AFE_C3_OVER, }, - }; - static const u16 radio_r[] = { R2059_C1, R2059_C2, R2059_C3, }; - int core; - - if (core_sel == 0) { - b43err(dev->wl, "RSSI selection for core off not implemented yet\n"); - } else { - for (core = 0; core < 3; core++) { - /* Check if caller requested a one specific core */ - if ((core_sel == 1 && core != 0) || - (core_sel == 2 && core != 1) || - (core_sel == 3 && core != 2)) - continue; - - switch (rssi_type) { - case HT_RSSI_TSSI_2G: - b43_phy_set(dev, ctl_regs[core][0], 0x3 << 8); - b43_phy_set(dev, ctl_regs[core][0], 0x3 << 10); - b43_phy_set(dev, ctl_regs[core][1], 0x1 << 9); - b43_phy_set(dev, ctl_regs[core][1], 0x1 << 10); - - b43_radio_set(dev, R2059_C3 | 0xbf, 0x1); - b43_radio_write(dev, radio_r[core] | 0x159, - 0x11); - break; - default: - b43err(dev->wl, "RSSI selection for type %d not implemented yet\n", - rssi_type); - } - } - } -} - -static void b43_phy_ht_poll_rssi(struct b43_wldev *dev, enum ht_rssi_type type, - s32 *buf, u8 nsamp) -{ - u16 phy_regs_values[12]; - static const u16 phy_regs_to_save[] = { - B43_PHY_HT_AFE_C1, B43_PHY_HT_AFE_C1_OVER, - 0x848, 0x841, - B43_PHY_HT_AFE_C2, B43_PHY_HT_AFE_C2_OVER, - 0x868, 0x861, - B43_PHY_HT_AFE_C3, B43_PHY_HT_AFE_C3_OVER, - 0x888, 0x881, - }; - u16 tmp[3]; - int i; - - for (i = 0; i < 12; i++) - phy_regs_values[i] = b43_phy_read(dev, phy_regs_to_save[i]); - - b43_phy_ht_rssi_select(dev, 5, type); - - for (i = 0; i < 6; i++) - buf[i] = 0; - - for (i = 0; i < nsamp; i++) { - tmp[0] = b43_phy_read(dev, B43_PHY_HT_RSSI_C1); - tmp[1] = b43_phy_read(dev, B43_PHY_HT_RSSI_C2); - tmp[2] = b43_phy_read(dev, B43_PHY_HT_RSSI_C3); - - buf[0] += ((s8)((tmp[0] & 0x3F) << 2)) >> 2; - buf[1] += ((s8)(((tmp[0] >> 8) & 0x3F) << 2)) >> 2; - buf[2] += ((s8)((tmp[1] & 0x3F) << 2)) >> 2; - buf[3] += ((s8)(((tmp[1] >> 8) & 0x3F) << 2)) >> 2; - buf[4] += ((s8)((tmp[2] & 0x3F) << 2)) >> 2; - buf[5] += ((s8)(((tmp[2] >> 8) & 0x3F) << 2)) >> 2; - } - - for (i = 0; i < 12; i++) - b43_phy_write(dev, phy_regs_to_save[i], phy_regs_values[i]); -} - -/************************************************** - * Tx/Rx - **************************************************/ - -static void b43_phy_ht_tx_power_fix(struct b43_wldev *dev) -{ - int i; - - for (i = 0; i < 3; i++) { - u16 mask; - u32 tmp = b43_httab_read(dev, B43_HTTAB32(26, 0xE8)); - - if (0) /* FIXME */ - mask = 0x2 << (i * 4); - else - mask = 0; - b43_phy_mask(dev, B43_PHY_EXTG(0x108), mask); - - b43_httab_write(dev, B43_HTTAB16(7, 0x110 + i), tmp >> 16); - b43_httab_write(dev, B43_HTTAB8(13, 0x63 + (i * 4)), - tmp & 0xFF); - b43_httab_write(dev, B43_HTTAB8(13, 0x73 + (i * 4)), - tmp & 0xFF); - } -} - -static void b43_phy_ht_tx_power_ctl(struct b43_wldev *dev, bool enable) -{ - struct b43_phy_ht *phy_ht = dev->phy.ht; - u16 en_bits = B43_PHY_HT_TXPCTL_CMD_C1_COEFF | - B43_PHY_HT_TXPCTL_CMD_C1_HWPCTLEN | - B43_PHY_HT_TXPCTL_CMD_C1_PCTLEN; - static const u16 cmd_regs[3] = { B43_PHY_HT_TXPCTL_CMD_C1, - B43_PHY_HT_TXPCTL_CMD_C2, - B43_PHY_HT_TXPCTL_CMD_C3 }; - static const u16 status_regs[3] = { B43_PHY_HT_TX_PCTL_STATUS_C1, - B43_PHY_HT_TX_PCTL_STATUS_C2, - B43_PHY_HT_TX_PCTL_STATUS_C3 }; - int i; - - if (!enable) { - if (b43_phy_read(dev, B43_PHY_HT_TXPCTL_CMD_C1) & en_bits) { - /* We disable enabled TX pwr ctl, save it's state */ - for (i = 0; i < 3; i++) - phy_ht->tx_pwr_idx[i] = - b43_phy_read(dev, status_regs[i]); - } - b43_phy_mask(dev, B43_PHY_HT_TXPCTL_CMD_C1, ~en_bits); - } else { - b43_phy_set(dev, B43_PHY_HT_TXPCTL_CMD_C1, en_bits); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - for (i = 0; i < 3; i++) - b43_phy_write(dev, cmd_regs[i], 0x32); - } - - for (i = 0; i < 3; i++) - if (phy_ht->tx_pwr_idx[i] <= - B43_PHY_HT_TXPCTL_CMD_C1_INIT) - b43_phy_write(dev, cmd_regs[i], - phy_ht->tx_pwr_idx[i]); - } - - phy_ht->tx_pwr_ctl = enable; -} - -static void b43_phy_ht_tx_power_ctl_idle_tssi(struct b43_wldev *dev) -{ - struct b43_phy_ht *phy_ht = dev->phy.ht; - static const u16 base[] = { 0x840, 0x860, 0x880 }; - u16 save_regs[3][3]; - s32 rssi_buf[6]; - int core; - - for (core = 0; core < 3; core++) { - save_regs[core][1] = b43_phy_read(dev, base[core] + 6); - save_regs[core][2] = b43_phy_read(dev, base[core] + 7); - save_regs[core][0] = b43_phy_read(dev, base[core] + 0); - - b43_phy_write(dev, base[core] + 6, 0); - b43_phy_mask(dev, base[core] + 7, ~0xF); /* 0xF? Or just 0x6? */ - b43_phy_set(dev, base[core] + 0, 0x0400); - b43_phy_set(dev, base[core] + 0, 0x1000); - } - - b43_phy_ht_tx_tone(dev); - udelay(20); - b43_phy_ht_poll_rssi(dev, HT_RSSI_TSSI_2G, rssi_buf, 1); - b43_phy_ht_stop_playback(dev); - b43_phy_ht_reset_cca(dev); - - phy_ht->idle_tssi[0] = rssi_buf[0] & 0xff; - phy_ht->idle_tssi[1] = rssi_buf[2] & 0xff; - phy_ht->idle_tssi[2] = rssi_buf[4] & 0xff; - - for (core = 0; core < 3; core++) { - b43_phy_write(dev, base[core] + 0, save_regs[core][0]); - b43_phy_write(dev, base[core] + 6, save_regs[core][1]); - b43_phy_write(dev, base[core] + 7, save_regs[core][2]); - } -} - -static void b43_phy_ht_tssi_setup(struct b43_wldev *dev) -{ - static const u16 routing[] = { R2059_C1, R2059_C2, R2059_C3, }; - int core; - - /* 0x159 is probably TX_SSI_MUX or TSSIG (by comparing to N-PHY) */ - for (core = 0; core < 3; core++) { - b43_radio_set(dev, 0x8bf, 0x1); - b43_radio_write(dev, routing[core] | 0x0159, 0x0011); - } -} - -static void b43_phy_ht_tx_power_ctl_setup(struct b43_wldev *dev) -{ - struct b43_phy_ht *phy_ht = dev->phy.ht; - struct ssb_sprom *sprom = dev->dev->bus_sprom; - - u8 *idle = phy_ht->idle_tssi; - u8 target[3]; - s16 a1[3], b0[3], b1[3]; - - u16 freq = dev->phy.chandef->chan->center_freq; - int i, c; - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - for (c = 0; c < 3; c++) { - target[c] = sprom->core_pwr_info[c].maxpwr_2g; - a1[c] = sprom->core_pwr_info[c].pa_2g[0]; - b0[c] = sprom->core_pwr_info[c].pa_2g[1]; - b1[c] = sprom->core_pwr_info[c].pa_2g[2]; - } - } else if (freq >= 4900 && freq < 5100) { - for (c = 0; c < 3; c++) { - target[c] = sprom->core_pwr_info[c].maxpwr_5gl; - a1[c] = sprom->core_pwr_info[c].pa_5gl[0]; - b0[c] = sprom->core_pwr_info[c].pa_5gl[1]; - b1[c] = sprom->core_pwr_info[c].pa_5gl[2]; - } - } else if (freq >= 5100 && freq < 5500) { - for (c = 0; c < 3; c++) { - target[c] = sprom->core_pwr_info[c].maxpwr_5g; - a1[c] = sprom->core_pwr_info[c].pa_5g[0]; - b0[c] = sprom->core_pwr_info[c].pa_5g[1]; - b1[c] = sprom->core_pwr_info[c].pa_5g[2]; - } - } else if (freq >= 5500) { - for (c = 0; c < 3; c++) { - target[c] = sprom->core_pwr_info[c].maxpwr_5gh; - a1[c] = sprom->core_pwr_info[c].pa_5gh[0]; - b0[c] = sprom->core_pwr_info[c].pa_5gh[1]; - b1[c] = sprom->core_pwr_info[c].pa_5gh[2]; - } - } else { - target[0] = target[1] = target[2] = 52; - a1[0] = a1[1] = a1[2] = -424; - b0[0] = b0[1] = b0[2] = 5612; - b1[0] = b1[1] = b1[2] = -1393; - } - - b43_phy_set(dev, B43_PHY_HT_TSSIMODE, B43_PHY_HT_TSSIMODE_EN); - b43_phy_mask(dev, B43_PHY_HT_TXPCTL_CMD_C1, - ~B43_PHY_HT_TXPCTL_CMD_C1_PCTLEN & 0xFFFF); - - /* TODO: Does it depend on sprom->fem.ghz2.tssipos? */ - b43_phy_set(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI, 0x4000); - - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C1, - ~B43_PHY_HT_TXPCTL_CMD_C1_INIT, 0x19); - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C2, - ~B43_PHY_HT_TXPCTL_CMD_C2_INIT, 0x19); - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C3, - ~B43_PHY_HT_TXPCTL_CMD_C3_INIT, 0x19); - - b43_phy_set(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI, - B43_PHY_HT_TXPCTL_IDLE_TSSI_BINF); - - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI, - ~B43_PHY_HT_TXPCTL_IDLE_TSSI_C1, - idle[0] << B43_PHY_HT_TXPCTL_IDLE_TSSI_C1_SHIFT); - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI, - ~B43_PHY_HT_TXPCTL_IDLE_TSSI_C2, - idle[1] << B43_PHY_HT_TXPCTL_IDLE_TSSI_C2_SHIFT); - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI2, - ~B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3, - idle[2] << B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3_SHIFT); - - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_N, ~B43_PHY_HT_TXPCTL_N_TSSID, - 0xf0); - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_N, ~B43_PHY_HT_TXPCTL_N_NPTIL2, - 0x3 << B43_PHY_HT_TXPCTL_N_NPTIL2_SHIFT); -#if 0 - /* TODO: what to mask/set? */ - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C1, 0x800, 0) - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C1, 0x400, 0) -#endif - - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_TARG_PWR, - ~B43_PHY_HT_TXPCTL_TARG_PWR_C1, - target[0] << B43_PHY_HT_TXPCTL_TARG_PWR_C1_SHIFT); - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_TARG_PWR, - ~B43_PHY_HT_TXPCTL_TARG_PWR_C2 & 0xFFFF, - target[1] << B43_PHY_HT_TXPCTL_TARG_PWR_C2_SHIFT); - b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_TARG_PWR2, - ~B43_PHY_HT_TXPCTL_TARG_PWR2_C3, - target[2] << B43_PHY_HT_TXPCTL_TARG_PWR2_C3_SHIFT); - - for (c = 0; c < 3; c++) { - s32 num, den, pwr; - u32 regval[64]; - - for (i = 0; i < 64; i++) { - num = 8 * (16 * b0[c] + b1[c] * i); - den = 32768 + a1[c] * i; - pwr = max((4 * num + den / 2) / den, -8); - regval[i] = pwr; - } - b43_httab_write_bulk(dev, B43_HTTAB16(26 + c, 0), 64, regval); - } -} - -/************************************************** - * Channel switching ops. - **************************************************/ - -static void b43_phy_ht_spur_avoid(struct b43_wldev *dev, - struct ieee80211_channel *new_channel) -{ - struct bcma_device *core = dev->dev->bdev; - int spuravoid = 0; - - /* Check for 13 and 14 is just a guess, we don't have enough logs. */ - if (new_channel->hw_value == 13 || new_channel->hw_value == 14) - spuravoid = 1; - bcma_core_pll_ctl(core, B43_BCMA_CLKCTLST_PHY_PLL_REQ, 0, false); - bcma_pmu_spuravoid_pllupdate(&core->bus->drv_cc, spuravoid); - bcma_core_pll_ctl(core, - B43_BCMA_CLKCTLST_80211_PLL_REQ | - B43_BCMA_CLKCTLST_PHY_PLL_REQ, - B43_BCMA_CLKCTLST_80211_PLL_ST | - B43_BCMA_CLKCTLST_PHY_PLL_ST, false); - - b43_mac_switch_freq(dev, spuravoid); - - b43_wireless_core_phy_pll_reset(dev); - - if (spuravoid) - b43_phy_set(dev, B43_PHY_HT_BBCFG, B43_PHY_HT_BBCFG_RSTRX); - else - b43_phy_mask(dev, B43_PHY_HT_BBCFG, - ~B43_PHY_HT_BBCFG_RSTRX & 0xFFFF); - - b43_phy_ht_reset_cca(dev); -} - -static void b43_phy_ht_channel_setup(struct b43_wldev *dev, - const struct b43_phy_ht_channeltab_e_phy *e, - struct ieee80211_channel *new_channel) -{ - if (new_channel->band == IEEE80211_BAND_5GHZ) { - /* Switch to 2 GHz for a moment to access B-PHY regs */ - b43_phy_mask(dev, B43_PHY_HT_BANDCTL, ~B43_PHY_HT_BANDCTL_5GHZ); - - b43_phy_ht_bphy_reset(dev, true); - - /* Switch to 5 GHz */ - b43_phy_set(dev, B43_PHY_HT_BANDCTL, B43_PHY_HT_BANDCTL_5GHZ); - } else { - /* Switch to 2 GHz */ - b43_phy_mask(dev, B43_PHY_HT_BANDCTL, ~B43_PHY_HT_BANDCTL_5GHZ); - - b43_phy_ht_bphy_reset(dev, false); - } - - b43_phy_write(dev, B43_PHY_HT_BW1, e->bw1); - b43_phy_write(dev, B43_PHY_HT_BW2, e->bw2); - b43_phy_write(dev, B43_PHY_HT_BW3, e->bw3); - b43_phy_write(dev, B43_PHY_HT_BW4, e->bw4); - b43_phy_write(dev, B43_PHY_HT_BW5, e->bw5); - b43_phy_write(dev, B43_PHY_HT_BW6, e->bw6); - - if (new_channel->hw_value == 14) { - b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_OFDM_EN, 0); - b43_phy_set(dev, B43_PHY_HT_TEST, 0x0800); - } else { - b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_OFDM_EN, - B43_PHY_HT_CLASS_CTL_OFDM_EN); - if (new_channel->band == IEEE80211_BAND_2GHZ) - b43_phy_mask(dev, B43_PHY_HT_TEST, ~0x840); - } - - if (1) /* TODO: On N it's for early devices only, what about HT? */ - b43_phy_ht_tx_power_fix(dev); - - b43_phy_ht_spur_avoid(dev, new_channel); - - b43_phy_write(dev, 0x017e, 0x3830); -} - -static int b43_phy_ht_set_channel(struct b43_wldev *dev, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type) -{ - struct b43_phy *phy = &dev->phy; - - const struct b43_phy_ht_channeltab_e_radio2059 *chent_r2059 = NULL; - - if (phy->radio_ver == 0x2059) { - chent_r2059 = b43_phy_ht_get_channeltab_e_r2059(dev, - channel->center_freq); - if (!chent_r2059) - return -ESRCH; - } else { - return -ESRCH; - } - - /* TODO: In case of N-PHY some bandwidth switching goes here */ - - if (phy->radio_ver == 0x2059) { - b43_radio_2059_channel_setup(dev, chent_r2059); - b43_phy_ht_channel_setup(dev, &(chent_r2059->phy_regs), - channel); - } else { - return -ESRCH; - } - - return 0; -} - -/************************************************** - * Basic PHY ops. - **************************************************/ - -static int b43_phy_ht_op_allocate(struct b43_wldev *dev) -{ - struct b43_phy_ht *phy_ht; - - phy_ht = kzalloc(sizeof(*phy_ht), GFP_KERNEL); - if (!phy_ht) - return -ENOMEM; - dev->phy.ht = phy_ht; - - return 0; -} - -static void b43_phy_ht_op_prepare_structs(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_ht *phy_ht = phy->ht; - int i; - - memset(phy_ht, 0, sizeof(*phy_ht)); - - phy_ht->tx_pwr_ctl = true; - for (i = 0; i < 3; i++) - phy_ht->tx_pwr_idx[i] = B43_PHY_HT_TXPCTL_CMD_C1_INIT + 1; - - for (i = 0; i < 3; i++) - phy_ht->bb_mult_save[i] = -1; -} - -static int b43_phy_ht_op_init(struct b43_wldev *dev) -{ - struct b43_phy_ht *phy_ht = dev->phy.ht; - u16 tmp; - u16 clip_state[3]; - bool saved_tx_pwr_ctl; - - if (dev->dev->bus_type != B43_BUS_BCMA) { - b43err(dev->wl, "HT-PHY is supported only on BCMA bus!\n"); - return -EOPNOTSUPP; - } - - b43_phy_ht_tables_init(dev); - - b43_phy_mask(dev, 0x0be, ~0x2); - b43_phy_set(dev, 0x23f, 0x7ff); - b43_phy_set(dev, 0x240, 0x7ff); - b43_phy_set(dev, 0x241, 0x7ff); - - b43_phy_ht_zero_extg(dev); - - b43_phy_mask(dev, B43_PHY_EXTG(0), ~0x3); - - b43_phy_write(dev, B43_PHY_HT_AFE_C1_OVER, 0); - b43_phy_write(dev, B43_PHY_HT_AFE_C2_OVER, 0); - b43_phy_write(dev, B43_PHY_HT_AFE_C3_OVER, 0); - - b43_phy_write(dev, B43_PHY_EXTG(0x103), 0x20); - b43_phy_write(dev, B43_PHY_EXTG(0x101), 0x20); - b43_phy_write(dev, 0x20d, 0xb8); - b43_phy_write(dev, B43_PHY_EXTG(0x14f), 0xc8); - b43_phy_write(dev, 0x70, 0x50); - b43_phy_write(dev, 0x1ff, 0x30); - - if (0) /* TODO: condition */ - ; /* TODO: PHY op on reg 0x217 */ - - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_CCK_EN, 0); - else - b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_CCK_EN, - B43_PHY_HT_CLASS_CTL_CCK_EN); - - b43_phy_set(dev, 0xb1, 0x91); - b43_phy_write(dev, 0x32f, 0x0003); - b43_phy_write(dev, 0x077, 0x0010); - b43_phy_write(dev, 0x0b4, 0x0258); - b43_phy_mask(dev, 0x17e, ~0x4000); - - b43_phy_write(dev, 0x0b9, 0x0072); - - b43_httab_write_few(dev, B43_HTTAB16(7, 0x14e), 2, 0x010f, 0x010f); - b43_httab_write_few(dev, B43_HTTAB16(7, 0x15e), 2, 0x010f, 0x010f); - b43_httab_write_few(dev, B43_HTTAB16(7, 0x16e), 2, 0x010f, 0x010f); - - b43_phy_ht_afe_unk1(dev); - - b43_httab_write_few(dev, B43_HTTAB16(7, 0x130), 9, 0x777, 0x111, 0x111, - 0x777, 0x111, 0x111, 0x777, 0x111, 0x111); - - b43_httab_write(dev, B43_HTTAB16(7, 0x120), 0x0777); - b43_httab_write(dev, B43_HTTAB16(7, 0x124), 0x0777); - - b43_httab_write(dev, B43_HTTAB16(8, 0x00), 0x02); - b43_httab_write(dev, B43_HTTAB16(8, 0x10), 0x02); - b43_httab_write(dev, B43_HTTAB16(8, 0x20), 0x02); - - b43_httab_write_few(dev, B43_HTTAB16(8, 0x08), 4, - 0x8e, 0x96, 0x96, 0x96); - b43_httab_write_few(dev, B43_HTTAB16(8, 0x18), 4, - 0x8f, 0x9f, 0x9f, 0x9f); - b43_httab_write_few(dev, B43_HTTAB16(8, 0x28), 4, - 0x8f, 0x9f, 0x9f, 0x9f); - - b43_httab_write_few(dev, B43_HTTAB16(8, 0x0c), 4, 0x2, 0x2, 0x2, 0x2); - b43_httab_write_few(dev, B43_HTTAB16(8, 0x1c), 4, 0x2, 0x2, 0x2, 0x2); - b43_httab_write_few(dev, B43_HTTAB16(8, 0x2c), 4, 0x2, 0x2, 0x2, 0x2); - - b43_phy_maskset(dev, 0x0280, 0xff00, 0x3e); - b43_phy_maskset(dev, 0x0283, 0xff00, 0x3e); - b43_phy_maskset(dev, B43_PHY_OFDM(0x0141), 0xff00, 0x46); - b43_phy_maskset(dev, 0x0283, 0xff00, 0x40); - - b43_httab_write_few(dev, B43_HTTAB16(00, 0x8), 4, - 0x09, 0x0e, 0x13, 0x18); - b43_httab_write_few(dev, B43_HTTAB16(01, 0x8), 4, - 0x09, 0x0e, 0x13, 0x18); - /* TODO: Did wl mean 2 instead of 40? */ - b43_httab_write_few(dev, B43_HTTAB16(40, 0x8), 4, - 0x09, 0x0e, 0x13, 0x18); - - b43_phy_maskset(dev, B43_PHY_OFDM(0x24), 0x3f, 0xd); - b43_phy_maskset(dev, B43_PHY_OFDM(0x64), 0x3f, 0xd); - b43_phy_maskset(dev, B43_PHY_OFDM(0xa4), 0x3f, 0xd); - - b43_phy_set(dev, B43_PHY_EXTG(0x060), 0x1); - b43_phy_set(dev, B43_PHY_EXTG(0x064), 0x1); - b43_phy_set(dev, B43_PHY_EXTG(0x080), 0x1); - b43_phy_set(dev, B43_PHY_EXTG(0x084), 0x1); - - /* Copy some tables entries */ - tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x144)); - b43_httab_write(dev, B43_HTTAB16(7, 0x14a), tmp); - tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x154)); - b43_httab_write(dev, B43_HTTAB16(7, 0x15a), tmp); - tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x164)); - b43_httab_write(dev, B43_HTTAB16(7, 0x16a), tmp); - - /* Reset CCA */ - b43_phy_force_clock(dev, true); - tmp = b43_phy_read(dev, B43_PHY_HT_BBCFG); - b43_phy_write(dev, B43_PHY_HT_BBCFG, tmp | B43_PHY_HT_BBCFG_RSTCCA); - b43_phy_write(dev, B43_PHY_HT_BBCFG, tmp & ~B43_PHY_HT_BBCFG_RSTCCA); - b43_phy_force_clock(dev, false); - - b43_mac_phy_clock_set(dev, true); - - b43_phy_ht_pa_override(dev, false); - b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RX2TX); - b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RST2RX); - b43_phy_ht_pa_override(dev, true); - - /* TODO: Should we restore it? Or store it in global PHY info? */ - b43_phy_ht_classifier(dev, 0, 0); - b43_phy_ht_read_clip_detection(dev, clip_state); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - b43_phy_ht_bphy_init(dev); - - b43_httab_write_bulk(dev, B43_HTTAB32(0x1a, 0xc0), - B43_HTTAB_1A_C0_LATE_SIZE, b43_httab_0x1a_0xc0_late); - - saved_tx_pwr_ctl = phy_ht->tx_pwr_ctl; - b43_phy_ht_tx_power_fix(dev); - b43_phy_ht_tx_power_ctl(dev, false); - b43_phy_ht_tx_power_ctl_idle_tssi(dev); - b43_phy_ht_tx_power_ctl_setup(dev); - b43_phy_ht_tssi_setup(dev); - b43_phy_ht_tx_power_ctl(dev, saved_tx_pwr_ctl); - - return 0; -} - -static void b43_phy_ht_op_free(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_ht *phy_ht = phy->ht; - - kfree(phy_ht); - phy->ht = NULL; -} - -/* http://bcm-v4.sipsolutions.net/802.11/Radio/Switch%20Radio */ -static void b43_phy_ht_op_software_rfkill(struct b43_wldev *dev, - bool blocked) -{ - if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED) - b43err(dev->wl, "MAC not suspended\n"); - - if (blocked) { - b43_phy_mask(dev, B43_PHY_HT_RF_CTL_CMD, - ~B43_PHY_HT_RF_CTL_CMD_CHIP0_PU); - } else { - if (dev->phy.radio_ver == 0x2059) - b43_radio_2059_init(dev); - else - B43_WARN_ON(1); - - b43_switch_channel(dev, dev->phy.channel); - } -} - -static void b43_phy_ht_op_switch_analog(struct b43_wldev *dev, bool on) -{ - if (on) { - b43_phy_write(dev, B43_PHY_HT_AFE_C1, 0x00cd); - b43_phy_write(dev, B43_PHY_HT_AFE_C1_OVER, 0x0000); - b43_phy_write(dev, B43_PHY_HT_AFE_C2, 0x00cd); - b43_phy_write(dev, B43_PHY_HT_AFE_C2_OVER, 0x0000); - b43_phy_write(dev, B43_PHY_HT_AFE_C3, 0x00cd); - b43_phy_write(dev, B43_PHY_HT_AFE_C3_OVER, 0x0000); - } else { - b43_phy_write(dev, B43_PHY_HT_AFE_C1_OVER, 0x07ff); - b43_phy_write(dev, B43_PHY_HT_AFE_C1, 0x00fd); - b43_phy_write(dev, B43_PHY_HT_AFE_C2_OVER, 0x07ff); - b43_phy_write(dev, B43_PHY_HT_AFE_C2, 0x00fd); - b43_phy_write(dev, B43_PHY_HT_AFE_C3_OVER, 0x07ff); - b43_phy_write(dev, B43_PHY_HT_AFE_C3, 0x00fd); - } -} - -static int b43_phy_ht_op_switch_channel(struct b43_wldev *dev, - unsigned int new_channel) -{ - struct ieee80211_channel *channel = dev->wl->hw->conf.chandef.chan; - enum nl80211_channel_type channel_type = - cfg80211_get_chandef_type(&dev->wl->hw->conf.chandef); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if ((new_channel < 1) || (new_channel > 14)) - return -EINVAL; - } else { - return -EINVAL; - } - - return b43_phy_ht_set_channel(dev, channel, channel_type); -} - -static unsigned int b43_phy_ht_op_get_default_chan(struct b43_wldev *dev) -{ - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - return 11; - return 36; -} - -/************************************************** - * R/W ops. - **************************************************/ - -static void b43_phy_ht_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, - u16 set) -{ - b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); - b43_write16(dev, B43_MMIO_PHY_DATA, - (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); -} - -static u16 b43_phy_ht_op_radio_read(struct b43_wldev *dev, u16 reg) -{ - /* HT-PHY needs 0x200 for read access */ - reg |= 0x200; - - b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); - return b43_read16(dev, B43_MMIO_RADIO24_DATA); -} - -static void b43_phy_ht_op_radio_write(struct b43_wldev *dev, u16 reg, - u16 value) -{ - b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); - b43_write16(dev, B43_MMIO_RADIO24_DATA, value); -} - -static enum b43_txpwr_result -b43_phy_ht_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) -{ - return B43_TXPWR_RES_DONE; -} - -static void b43_phy_ht_op_adjust_txpower(struct b43_wldev *dev) -{ -} - -/************************************************** - * PHY ops struct. - **************************************************/ - -const struct b43_phy_operations b43_phyops_ht = { - .allocate = b43_phy_ht_op_allocate, - .free = b43_phy_ht_op_free, - .prepare_structs = b43_phy_ht_op_prepare_structs, - .init = b43_phy_ht_op_init, - .phy_maskset = b43_phy_ht_op_maskset, - .radio_read = b43_phy_ht_op_radio_read, - .radio_write = b43_phy_ht_op_radio_write, - .software_rfkill = b43_phy_ht_op_software_rfkill, - .switch_analog = b43_phy_ht_op_switch_analog, - .switch_channel = b43_phy_ht_op_switch_channel, - .get_default_chan = b43_phy_ht_op_get_default_chan, - .recalc_txpower = b43_phy_ht_op_recalc_txpower, - .adjust_txpower = b43_phy_ht_op_adjust_txpower, -}; diff --git a/drivers/net/wireless/b43/phy_ht.h b/drivers/net/wireless/b43/phy_ht.h deleted file mode 100644 index c086f56ce478..000000000000 --- a/drivers/net/wireless/b43/phy_ht.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef B43_PHY_HT_H_ -#define B43_PHY_HT_H_ - -#include "phy_common.h" - - -#define B43_PHY_HT_BBCFG 0x001 /* BB config */ -#define B43_PHY_HT_BBCFG_RSTCCA 0x4000 /* Reset CCA */ -#define B43_PHY_HT_BBCFG_RSTRX 0x8000 /* Reset RX */ -#define B43_PHY_HT_BANDCTL 0x009 /* Band control */ -#define B43_PHY_HT_BANDCTL_5GHZ 0x0001 /* Use the 5GHz band */ -#define B43_PHY_HT_TABLE_ADDR 0x072 /* Table address */ -#define B43_PHY_HT_TABLE_DATALO 0x073 /* Table data low */ -#define B43_PHY_HT_TABLE_DATAHI 0x074 /* Table data high */ -#define B43_PHY_HT_CLASS_CTL 0x0B0 /* Classifier control */ -#define B43_PHY_HT_CLASS_CTL_CCK_EN 0x0001 /* CCK enable */ -#define B43_PHY_HT_CLASS_CTL_OFDM_EN 0x0002 /* OFDM enable */ -#define B43_PHY_HT_CLASS_CTL_WAITED_EN 0x0004 /* Waited enable */ -#define B43_PHY_HT_IQLOCAL_CMDGCTL 0x0C2 /* I/Q LO cal command G control */ -#define B43_PHY_HT_SAMP_CMD 0x0C3 /* Sample command */ -#define B43_PHY_HT_SAMP_CMD_STOP 0x0002 /* Stop */ -#define B43_PHY_HT_SAMP_LOOP_CNT 0x0C4 /* Sample loop count */ -#define B43_PHY_HT_SAMP_WAIT_CNT 0x0C5 /* Sample wait count */ -#define B43_PHY_HT_SAMP_DEP_CNT 0x0C6 /* Sample depth count */ -#define B43_PHY_HT_SAMP_STAT 0x0C7 /* Sample status */ -#define B43_PHY_HT_EST_PWR_C1 0x118 -#define B43_PHY_HT_EST_PWR_C2 0x119 -#define B43_PHY_HT_EST_PWR_C3 0x11A -#define B43_PHY_HT_TSSIMODE 0x122 /* TSSI mode */ -#define B43_PHY_HT_TSSIMODE_EN 0x0001 /* TSSI enable */ -#define B43_PHY_HT_TSSIMODE_PDEN 0x0002 /* Power det enable */ -#define B43_PHY_HT_BW1 0x1CE -#define B43_PHY_HT_BW2 0x1CF -#define B43_PHY_HT_BW3 0x1D0 -#define B43_PHY_HT_BW4 0x1D1 -#define B43_PHY_HT_BW5 0x1D2 -#define B43_PHY_HT_BW6 0x1D3 -#define B43_PHY_HT_TXPCTL_CMD_C1 0x1E7 /* TX power control command */ -#define B43_PHY_HT_TXPCTL_CMD_C1_INIT 0x007F /* Init */ -#define B43_PHY_HT_TXPCTL_CMD_C1_COEFF 0x2000 /* Power control coefficients */ -#define B43_PHY_HT_TXPCTL_CMD_C1_HWPCTLEN 0x4000 /* Hardware TX power control enable */ -#define B43_PHY_HT_TXPCTL_CMD_C1_PCTLEN 0x8000 /* TX power control enable */ -#define B43_PHY_HT_TXPCTL_N 0x1E8 /* TX power control N num */ -#define B43_PHY_HT_TXPCTL_N_TSSID 0x00FF /* N TSSI delay */ -#define B43_PHY_HT_TXPCTL_N_TSSID_SHIFT 0 -#define B43_PHY_HT_TXPCTL_N_NPTIL2 0x0700 /* N PT integer log2 */ -#define B43_PHY_HT_TXPCTL_N_NPTIL2_SHIFT 8 -#define B43_PHY_HT_TXPCTL_IDLE_TSSI 0x1E9 /* TX power control idle TSSI */ -#define B43_PHY_HT_TXPCTL_IDLE_TSSI_C1 0x003F -#define B43_PHY_HT_TXPCTL_IDLE_TSSI_C1_SHIFT 0 -#define B43_PHY_HT_TXPCTL_IDLE_TSSI_C2 0x3F00 -#define B43_PHY_HT_TXPCTL_IDLE_TSSI_C2_SHIFT 8 -#define B43_PHY_HT_TXPCTL_IDLE_TSSI_BINF 0x8000 /* Raw TSSI offset bin format */ -#define B43_PHY_HT_TXPCTL_TARG_PWR 0x1EA /* TX power control target power */ -#define B43_PHY_HT_TXPCTL_TARG_PWR_C1 0x00FF /* Power 0 */ -#define B43_PHY_HT_TXPCTL_TARG_PWR_C1_SHIFT 0 -#define B43_PHY_HT_TXPCTL_TARG_PWR_C2 0xFF00 /* Power 1 */ -#define B43_PHY_HT_TXPCTL_TARG_PWR_C2_SHIFT 8 -#define B43_PHY_HT_TX_PCTL_STATUS_C1 0x1ED -#define B43_PHY_HT_TX_PCTL_STATUS_C2 0x1EE -#define B43_PHY_HT_TXPCTL_CMD_C2 0x222 -#define B43_PHY_HT_TXPCTL_CMD_C2_INIT 0x007F -#define B43_PHY_HT_RSSI_C1 0x219 -#define B43_PHY_HT_RSSI_C2 0x21A -#define B43_PHY_HT_RSSI_C3 0x21B - -#define B43_PHY_HT_C1_CLIP1THRES B43_PHY_OFDM(0x00E) -#define B43_PHY_HT_C2_CLIP1THRES B43_PHY_OFDM(0x04E) -#define B43_PHY_HT_C3_CLIP1THRES B43_PHY_OFDM(0x08E) - -#define B43_PHY_HT_RF_SEQ_MODE B43_PHY_EXTG(0x000) -#define B43_PHY_HT_RF_SEQ_MODE_CA_OVER 0x0001 /* Core active override */ -#define B43_PHY_HT_RF_SEQ_MODE_TR_OVER 0x0002 /* Trigger override */ -#define B43_PHY_HT_RF_SEQ_TRIG B43_PHY_EXTG(0x003) -#define B43_PHY_HT_RF_SEQ_TRIG_RX2TX 0x0001 /* RX2TX */ -#define B43_PHY_HT_RF_SEQ_TRIG_TX2RX 0x0002 /* TX2RX */ -#define B43_PHY_HT_RF_SEQ_TRIG_UPGH 0x0004 /* Update gain H */ -#define B43_PHY_HT_RF_SEQ_TRIG_UPGL 0x0008 /* Update gain L */ -#define B43_PHY_HT_RF_SEQ_TRIG_UPGU 0x0010 /* Update gain U */ -#define B43_PHY_HT_RF_SEQ_TRIG_RST2RX 0x0020 /* Reset to RX */ -#define B43_PHY_HT_RF_SEQ_STATUS B43_PHY_EXTG(0x004) -/* Values for the status are the same as for the trigger */ - -#define B43_PHY_HT_RF_CTL_CMD 0x810 -#define B43_PHY_HT_RF_CTL_CMD_FORCE 0x0001 -#define B43_PHY_HT_RF_CTL_CMD_CHIP0_PU 0x0002 - -#define B43_PHY_HT_RF_CTL_INT_C1 B43_PHY_EXTG(0x04c) -#define B43_PHY_HT_RF_CTL_INT_C2 B43_PHY_EXTG(0x06c) -#define B43_PHY_HT_RF_CTL_INT_C3 B43_PHY_EXTG(0x08c) - -#define B43_PHY_HT_AFE_C1_OVER B43_PHY_EXTG(0x110) -#define B43_PHY_HT_AFE_C1 B43_PHY_EXTG(0x111) -#define B43_PHY_HT_AFE_C2_OVER B43_PHY_EXTG(0x114) -#define B43_PHY_HT_AFE_C2 B43_PHY_EXTG(0x115) -#define B43_PHY_HT_AFE_C3_OVER B43_PHY_EXTG(0x118) -#define B43_PHY_HT_AFE_C3 B43_PHY_EXTG(0x119) - -#define B43_PHY_HT_TXPCTL_CMD_C3 B43_PHY_EXTG(0x164) -#define B43_PHY_HT_TXPCTL_CMD_C3_INIT 0x007F -#define B43_PHY_HT_TXPCTL_IDLE_TSSI2 B43_PHY_EXTG(0x165) /* TX power control idle TSSI */ -#define B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3 0x003F -#define B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3_SHIFT 0 -#define B43_PHY_HT_TXPCTL_TARG_PWR2 B43_PHY_EXTG(0x166) /* TX power control target power */ -#define B43_PHY_HT_TXPCTL_TARG_PWR2_C3 0x00FF -#define B43_PHY_HT_TXPCTL_TARG_PWR2_C3_SHIFT 0 -#define B43_PHY_HT_TX_PCTL_STATUS_C3 B43_PHY_EXTG(0x169) - -#define B43_PHY_B_BBCFG B43_PHY_N_BMODE(0x001) -#define B43_PHY_B_BBCFG_RSTCCA 0x4000 /* Reset CCA */ -#define B43_PHY_B_BBCFG_RSTRX 0x8000 /* Reset RX */ -#define B43_PHY_HT_TEST B43_PHY_N_BMODE(0x00A) - - -/* Values for PHY registers used on channel switching */ -struct b43_phy_ht_channeltab_e_phy { - u16 bw1; - u16 bw2; - u16 bw3; - u16 bw4; - u16 bw5; - u16 bw6; -}; - - -struct b43_phy_ht { - u16 rf_ctl_int_save[3]; - - bool tx_pwr_ctl; - u8 tx_pwr_idx[3]; - - s32 bb_mult_save[3]; - - u8 idle_tssi[3]; -}; - - -struct b43_phy_operations; -extern const struct b43_phy_operations b43_phyops_ht; - -#endif /* B43_PHY_HT_H_ */ diff --git a/drivers/net/wireless/b43/phy_lcn.c b/drivers/net/wireless/b43/phy_lcn.c deleted file mode 100644 index 97461ccf3e1e..000000000000 --- a/drivers/net/wireless/b43/phy_lcn.c +++ /dev/null @@ -1,855 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11n LCN-PHY support - - Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - - This file incorporates work covered by the following copyright and - permission notice: - - Copyright (c) 2010 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. -*/ - -#include - -#include "b43.h" -#include "phy_lcn.h" -#include "tables_phy_lcn.h" -#include "main.h" - -struct lcn_tx_gains { - u16 gm_gain; - u16 pga_gain; - u16 pad_gain; - u16 dac_gain; -}; - -struct lcn_tx_iir_filter { - u8 type; - u16 values[16]; -}; - -enum lcn_sense_type { - B43_SENSE_TEMP, - B43_SENSE_VBAT, -}; - -/************************************************** - * Radio 2064. - **************************************************/ - -/* wlc_lcnphy_radio_2064_channel_tune_4313 */ -static void b43_radio_2064_channel_setup(struct b43_wldev *dev) -{ - u16 save[2]; - - b43_radio_set(dev, 0x09d, 0x4); - b43_radio_write(dev, 0x09e, 0xf); - - /* Channel specific values in theory, in practice always the same */ - b43_radio_write(dev, 0x02a, 0xb); - b43_radio_maskset(dev, 0x030, ~0x3, 0xa); - b43_radio_maskset(dev, 0x091, ~0x3, 0); - b43_radio_maskset(dev, 0x038, ~0xf, 0x7); - b43_radio_maskset(dev, 0x030, ~0xc, 0x8); - b43_radio_maskset(dev, 0x05e, ~0xf, 0x8); - b43_radio_maskset(dev, 0x05e, ~0xf0, 0x80); - b43_radio_write(dev, 0x06c, 0x80); - - save[0] = b43_radio_read(dev, 0x044); - save[1] = b43_radio_read(dev, 0x12b); - - b43_radio_set(dev, 0x044, 0x7); - b43_radio_set(dev, 0x12b, 0xe); - - /* TODO */ - - b43_radio_write(dev, 0x040, 0xfb); - - b43_radio_write(dev, 0x041, 0x9a); - b43_radio_write(dev, 0x042, 0xa3); - b43_radio_write(dev, 0x043, 0x0c); - - /* TODO */ - - b43_radio_set(dev, 0x044, 0x0c); - udelay(1); - - b43_radio_write(dev, 0x044, save[0]); - b43_radio_write(dev, 0x12b, save[1]); - - if (dev->phy.rev == 1) { - /* brcmsmac uses outdated 0x3 for 0x038 */ - b43_radio_write(dev, 0x038, 0x0); - b43_radio_write(dev, 0x091, 0x7); - } -} - -/* wlc_radio_2064_init */ -static void b43_radio_2064_init(struct b43_wldev *dev) -{ - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_radio_write(dev, 0x09c, 0x0020); - b43_radio_write(dev, 0x105, 0x0008); - } else { - /* TODO */ - } - b43_radio_write(dev, 0x032, 0x0062); - b43_radio_write(dev, 0x033, 0x0019); - b43_radio_write(dev, 0x090, 0x0010); - b43_radio_write(dev, 0x010, 0x0000); - if (dev->phy.rev == 1) { - b43_radio_write(dev, 0x060, 0x007f); - b43_radio_write(dev, 0x061, 0x0072); - b43_radio_write(dev, 0x062, 0x007f); - } - b43_radio_write(dev, 0x01d, 0x0002); - b43_radio_write(dev, 0x01e, 0x0006); - - b43_phy_write(dev, 0x4ea, 0x4688); - b43_phy_maskset(dev, 0x4eb, ~0x7, 0x2); - b43_phy_mask(dev, 0x4eb, ~0x01c0); - b43_phy_maskset(dev, 0x46a, 0xff00, 0x19); - - b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x55), 0); - - b43_radio_mask(dev, 0x05b, (u16) ~0xff02); - b43_radio_set(dev, 0x004, 0x40); - b43_radio_set(dev, 0x120, 0x10); - b43_radio_set(dev, 0x078, 0x80); - b43_radio_set(dev, 0x129, 0x2); - b43_radio_set(dev, 0x057, 0x1); - b43_radio_set(dev, 0x05b, 0x2); - - /* TODO: wait for some bit to be set */ - b43_radio_read(dev, 0x05c); - - b43_radio_mask(dev, 0x05b, (u16) ~0xff02); - b43_radio_mask(dev, 0x057, (u16) ~0xff01); - - b43_phy_write(dev, 0x933, 0x2d6b); - b43_phy_write(dev, 0x934, 0x2d6b); - b43_phy_write(dev, 0x935, 0x2d6b); - b43_phy_write(dev, 0x936, 0x2d6b); - b43_phy_write(dev, 0x937, 0x016b); - - b43_radio_mask(dev, 0x057, (u16) ~0xff02); - b43_radio_write(dev, 0x0c2, 0x006f); -} - -/************************************************** - * Various PHY ops - **************************************************/ - -/* wlc_lcnphy_toggle_afe_pwdn */ -static void b43_phy_lcn_afe_set_unset(struct b43_wldev *dev) -{ - u16 afe_ctl2 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL2); - u16 afe_ctl1 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL1); - - b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 | 0x1); - b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 | 0x1); - - b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 & ~0x1); - b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 & ~0x1); - - b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2); - b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1); -} - -/* wlc_lcnphy_get_pa_gain */ -static u16 b43_phy_lcn_get_pa_gain(struct b43_wldev *dev) -{ - return (b43_phy_read(dev, 0x4fb) & 0x7f00) >> 8; -} - -/* wlc_lcnphy_set_dac_gain */ -static void b43_phy_lcn_set_dac_gain(struct b43_wldev *dev, u16 dac_gain) -{ - u16 dac_ctrl; - - dac_ctrl = b43_phy_read(dev, 0x439); - dac_ctrl = dac_ctrl & 0xc7f; - dac_ctrl = dac_ctrl | (dac_gain << 7); - b43_phy_maskset(dev, 0x439, ~0xfff, dac_ctrl); -} - -/* wlc_lcnphy_set_bbmult */ -static void b43_phy_lcn_set_bbmult(struct b43_wldev *dev, u8 m0) -{ - b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x57), m0 << 8); -} - -/* wlc_lcnphy_clear_tx_power_offsets */ -static void b43_phy_lcn_clear_tx_power_offsets(struct b43_wldev *dev) -{ - u8 i; - - if (1) { /* FIXME */ - b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x340); - for (i = 0; i < 30; i++) { - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0); - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0); - } - } - - b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x80); - for (i = 0; i < 64; i++) { - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0); - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0); - } -} - -/* wlc_lcnphy_rev0_baseband_init */ -static void b43_phy_lcn_rev0_baseband_init(struct b43_wldev *dev) -{ - b43_radio_write(dev, 0x11c, 0); - - b43_phy_write(dev, 0x43b, 0); - b43_phy_write(dev, 0x43c, 0); - b43_phy_write(dev, 0x44c, 0); - b43_phy_write(dev, 0x4e6, 0); - b43_phy_write(dev, 0x4f9, 0); - b43_phy_write(dev, 0x4b0, 0); - b43_phy_write(dev, 0x938, 0); - b43_phy_write(dev, 0x4b0, 0); - b43_phy_write(dev, 0x44e, 0); - - b43_phy_set(dev, 0x567, 0x03); - - b43_phy_set(dev, 0x44a, 0x44); - b43_phy_write(dev, 0x44a, 0x80); - - if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM)) - ; /* TODO */ - b43_phy_maskset(dev, 0x634, ~0xff, 0xc); - if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM) { - b43_phy_maskset(dev, 0x634, ~0xff, 0xa); - b43_phy_write(dev, 0x910, 0x1); - } - - b43_phy_write(dev, 0x910, 0x1); - - b43_phy_maskset(dev, 0x448, ~0x300, 0x100); - b43_phy_maskset(dev, 0x608, ~0xff, 0x17); - b43_phy_maskset(dev, 0x604, ~0x7ff, 0x3ea); -} - -/* wlc_lcnphy_bu_tweaks */ -static void b43_phy_lcn_bu_tweaks(struct b43_wldev *dev) -{ - b43_phy_set(dev, 0x805, 0x1); - - b43_phy_maskset(dev, 0x42f, ~0x7, 0x3); - b43_phy_maskset(dev, 0x030, ~0x7, 0x3); - - b43_phy_write(dev, 0x414, 0x1e10); - b43_phy_write(dev, 0x415, 0x0640); - - b43_phy_maskset(dev, 0x4df, (u16) ~0xff00, 0xf700); - - b43_phy_set(dev, 0x44a, 0x44); - b43_phy_write(dev, 0x44a, 0x80); - - b43_phy_maskset(dev, 0x434, ~0xff, 0xfd); - b43_phy_maskset(dev, 0x420, ~0xff, 0x10); - - if (dev->dev->bus_sprom->board_rev >= 0x1204) - b43_radio_set(dev, 0x09b, 0xf0); - - b43_phy_write(dev, 0x7d6, 0x0902); - - b43_phy_maskset(dev, 0x429, ~0xf, 0x9); - b43_phy_maskset(dev, 0x429, ~(0x3f << 4), 0xe << 4); - - if (dev->phy.rev == 1) { - b43_phy_maskset(dev, 0x423, ~0xff, 0x46); - b43_phy_maskset(dev, 0x411, ~0xff, 1); - b43_phy_set(dev, 0x434, 0xff); /* FIXME: update to wl */ - - /* TODO: wl operates on PHY 0x416, brcmsmac is outdated here */ - - b43_phy_maskset(dev, 0x656, ~0xf, 2); - b43_phy_set(dev, 0x44d, 4); - - b43_radio_set(dev, 0x0f7, 0x4); - b43_radio_mask(dev, 0x0f1, ~0x3); - b43_radio_maskset(dev, 0x0f2, ~0xf8, 0x90); - b43_radio_maskset(dev, 0x0f3, ~0x3, 0x2); - b43_radio_maskset(dev, 0x0f3, ~0xf0, 0xa0); - - b43_radio_set(dev, 0x11f, 0x2); - - b43_phy_lcn_clear_tx_power_offsets(dev); - - /* TODO: something more? */ - } -} - -/* wlc_lcnphy_vbat_temp_sense_setup */ -static void b43_phy_lcn_sense_setup(struct b43_wldev *dev, - enum lcn_sense_type sense_type) -{ - u8 auxpga_vmidcourse, auxpga_vmidfine, auxpga_gain; - u16 auxpga_vmid; - u8 tx_pwr_idx; - u8 i; - - u16 save_radio_regs[6][2] = { - { 0x007, 0 }, { 0x0ff, 0 }, { 0x11f, 0 }, { 0x005, 0 }, - { 0x025, 0 }, { 0x112, 0 }, - }; - u16 save_phy_regs[14][2] = { - { 0x503, 0 }, { 0x4a4, 0 }, { 0x4d0, 0 }, { 0x4d9, 0 }, - { 0x4da, 0 }, { 0x4a6, 0 }, { 0x938, 0 }, { 0x939, 0 }, - { 0x4d8, 0 }, { 0x4d0, 0 }, { 0x4d7, 0 }, { 0x4a5, 0 }, - { 0x40d, 0 }, { 0x4a2, 0 }, - }; - u16 save_radio_4a4; - - msleep(1); - - /* Save */ - for (i = 0; i < 6; i++) - save_radio_regs[i][1] = b43_radio_read(dev, - save_radio_regs[i][0]); - for (i = 0; i < 14; i++) - save_phy_regs[i][1] = b43_phy_read(dev, save_phy_regs[i][0]); - b43_mac_suspend(dev); - save_radio_4a4 = b43_radio_read(dev, 0x4a4); - /* wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); */ - tx_pwr_idx = dev->phy.lcn->tx_pwr_curr_idx; - - /* Setup */ - /* TODO: wlc_lcnphy_set_tx_pwr_by_index(pi, 127); */ - b43_radio_set(dev, 0x007, 0x1); - b43_radio_set(dev, 0x0ff, 0x10); - b43_radio_set(dev, 0x11f, 0x4); - - b43_phy_mask(dev, 0x503, ~0x1); - b43_phy_mask(dev, 0x503, ~0x4); - b43_phy_mask(dev, 0x4a4, ~0x4000); - b43_phy_mask(dev, 0x4a4, (u16) ~0x8000); - b43_phy_mask(dev, 0x4d0, ~0x20); - b43_phy_set(dev, 0x4a5, 0xff); - b43_phy_maskset(dev, 0x4a5, ~0x7000, 0x5000); - b43_phy_mask(dev, 0x4a5, ~0x700); - b43_phy_maskset(dev, 0x40d, ~0xff, 64); - b43_phy_maskset(dev, 0x40d, ~0x700, 0x600); - b43_phy_maskset(dev, 0x4a2, ~0xff, 64); - b43_phy_maskset(dev, 0x4a2, ~0x700, 0x600); - b43_phy_maskset(dev, 0x4d9, ~0x70, 0x20); - b43_phy_maskset(dev, 0x4d9, ~0x700, 0x300); - b43_phy_maskset(dev, 0x4d9, ~0x7000, 0x1000); - b43_phy_mask(dev, 0x4da, ~0x1000); - b43_phy_set(dev, 0x4da, 0x2000); - b43_phy_set(dev, 0x4a6, 0x8000); - - b43_radio_write(dev, 0x025, 0xc); - b43_radio_set(dev, 0x005, 0x8); - b43_phy_set(dev, 0x938, 0x4); - b43_phy_set(dev, 0x939, 0x4); - b43_phy_set(dev, 0x4a4, 0x1000); - - /* FIXME: don't hardcode */ - b43_lcntab_write(dev, B43_LCNTAB16(0x8, 0x6), 0x640); - - switch (sense_type) { - case B43_SENSE_TEMP: - b43_phy_set(dev, 0x4d7, 0x8); - b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x1000); - auxpga_vmidcourse = 8; - auxpga_vmidfine = 0x4; - auxpga_gain = 2; - b43_radio_set(dev, 0x082, 0x20); - break; - case B43_SENSE_VBAT: - b43_phy_set(dev, 0x4d7, 0x8); - b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x3000); - auxpga_vmidcourse = 7; - auxpga_vmidfine = 0xa; - auxpga_gain = 2; - break; - } - auxpga_vmid = (0x200 | (auxpga_vmidcourse << 4) | auxpga_vmidfine); - - b43_phy_set(dev, 0x4d8, 0x1); - b43_phy_maskset(dev, 0x4d8, ~(0x3ff << 2), auxpga_vmid << 2); - b43_phy_set(dev, 0x4d8, 0x2); - b43_phy_maskset(dev, 0x4d8, ~(0x7 << 12), auxpga_gain << 12); - b43_phy_set(dev, 0x4d0, 0x20); - b43_radio_write(dev, 0x112, 0x6); - - b43_dummy_transmission(dev, true, false); - /* Wait if not done */ - if (!(b43_phy_read(dev, 0x476) & 0x8000)) - udelay(10); - - /* Restore */ - for (i = 0; i < 6; i++) - b43_radio_write(dev, save_radio_regs[i][0], - save_radio_regs[i][1]); - for (i = 0; i < 14; i++) - b43_phy_write(dev, save_phy_regs[i][0], save_phy_regs[i][1]); - /* TODO: wlc_lcnphy_set_tx_pwr_by_index(tx_pwr_idx) */ - b43_radio_write(dev, 0x4a4, save_radio_4a4); - - b43_mac_enable(dev); - - msleep(1); -} - -static bool b43_phy_lcn_load_tx_iir_cck_filter(struct b43_wldev *dev, - u8 filter_type) -{ - int i, j; - u16 phy_regs[] = { 0x910, 0x91e, 0x91f, 0x924, 0x925, 0x926, 0x920, - 0x921, 0x927, 0x928, 0x929, 0x922, 0x923, 0x930, - 0x931, 0x932 }; - /* Table is from brcmsmac, values for type 25 were outdated, probably - * others need updating too */ - struct lcn_tx_iir_filter tx_iir_filters_cck[] = { - { 0, { 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778, - 1582, 64, 128, 64 } }, - { 1, { 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608, - 1863, 93, 167, 93 } }, - { 2, { 1, 415, 1874, 64, 128, 64, 792, 1656, 192, 384, 192, - 778, 1582, 64, 128, 64 } }, - { 3, { 1, 302, 1841, 129, 258, 129, 658, 1720, 205, 410, 205, - 754, 1760, 170, 340, 170 } }, - { 20, { 1, 360, 1884, 242, 1734, 242, 752, 1720, 205, 1845, 205, - 767, 1760, 256, 185, 256 } }, - { 21, { 1, 360, 1884, 149, 1874, 149, 752, 1720, 205, 1883, 205, - 767, 1760, 256, 273, 256 } }, - { 22, { 1, 360, 1884, 98, 1948, 98, 752, 1720, 205, 1924, 205, - 767, 1760, 256, 352, 256 } }, - { 23, { 1, 350, 1884, 116, 1966, 116, 752, 1720, 205, 2008, 205, - 767, 1760, 128, 233, 128 } }, - { 24, { 1, 325, 1884, 32, 40, 32, 756, 1720, 256, 471, 256, 766, - 1760, 256, 1881, 256 } }, - { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, 256, 471, 256, 765, - 1760, 262, 1878, 262 } }, - /* brcmsmac version { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, - * 256, 471, 256, 765, 1760, 256, 1881, 256 } }, */ - { 26, { 1, 277, 1943, 39, 117, 88, 637, 1838, 64, 192, 144, 614, - 1864, 128, 384, 288 } }, - { 27, { 1, 245, 1943, 49, 147, 110, 626, 1838, 256, 768, 576, - 613, 1864, 128, 384, 288 } }, - { 30, { 1, 302, 1841, 61, 122, 61, 658, 1720, 205, 410, 205, - 754, 1760, 170, 340, 170 } }, - }; - - for (i = 0; i < ARRAY_SIZE(tx_iir_filters_cck); i++) { - if (tx_iir_filters_cck[i].type == filter_type) { - for (j = 0; j < 16; j++) - b43_phy_write(dev, phy_regs[j], - tx_iir_filters_cck[i].values[j]); - return true; - } - } - - return false; -} - -static bool b43_phy_lcn_load_tx_iir_ofdm_filter(struct b43_wldev *dev, - u8 filter_type) -{ - int i, j; - u16 phy_regs[] = { 0x90f, 0x900, 0x901, 0x906, 0x907, 0x908, 0x902, - 0x903, 0x909, 0x90a, 0x90b, 0x904, 0x905, 0x90c, - 0x90d, 0x90e }; - struct lcn_tx_iir_filter tx_iir_filters_ofdm[] = { - { 0, { 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0, - 0x0, 0x278, 0xfea0, 0x80, 0x100, 0x80 } }, - { 1, { 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50, 750, - 0xFE2B, 212, 0xFFCE, 212 } }, - { 2, { 0, 375, 0xFF16, 37, 76, 37, 799, 0xFE74, 32, 20, 32, 748, - 0xFEF2, 128, 0xFFE2, 128 } }, - }; - - for (i = 0; i < ARRAY_SIZE(tx_iir_filters_ofdm); i++) { - if (tx_iir_filters_ofdm[i].type == filter_type) { - for (j = 0; j < 16; j++) - b43_phy_write(dev, phy_regs[j], - tx_iir_filters_ofdm[i].values[j]); - return true; - } - } - - return false; -} - -/* wlc_lcnphy_set_tx_gain_override */ -static void b43_phy_lcn_set_tx_gain_override(struct b43_wldev *dev, bool enable) -{ - b43_phy_maskset(dev, 0x4b0, ~(0x1 << 7), enable << 7); - b43_phy_maskset(dev, 0x4b0, ~(0x1 << 14), enable << 14); - b43_phy_maskset(dev, 0x43b, ~(0x1 << 6), enable << 6); -} - -/* wlc_lcnphy_set_tx_gain */ -static void b43_phy_lcn_set_tx_gain(struct b43_wldev *dev, - struct lcn_tx_gains *target_gains) -{ - u16 pa_gain = b43_phy_lcn_get_pa_gain(dev); - - b43_phy_write(dev, 0x4b5, - (target_gains->gm_gain | (target_gains->pga_gain << 8))); - b43_phy_maskset(dev, 0x4fb, ~0x7fff, - (target_gains->pad_gain | (pa_gain << 8))); - b43_phy_write(dev, 0x4fc, - (target_gains->gm_gain | (target_gains->pga_gain << 8))); - b43_phy_maskset(dev, 0x4fd, ~0x7fff, - (target_gains->pad_gain | (pa_gain << 8))); - - b43_phy_lcn_set_dac_gain(dev, target_gains->dac_gain); - b43_phy_lcn_set_tx_gain_override(dev, true); -} - -/* wlc_lcnphy_tx_pwr_ctrl_init */ -static void b43_phy_lcn_tx_pwr_ctl_init(struct b43_wldev *dev) -{ - struct lcn_tx_gains tx_gains; - u8 bbmult; - - b43_mac_suspend(dev); - - if (!dev->phy.lcn->hw_pwr_ctl_capable) { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - tx_gains.gm_gain = 4; - tx_gains.pga_gain = 12; - tx_gains.pad_gain = 12; - tx_gains.dac_gain = 0; - bbmult = 150; - } else { - tx_gains.gm_gain = 7; - tx_gains.pga_gain = 15; - tx_gains.pad_gain = 14; - tx_gains.dac_gain = 0; - bbmult = 150; - } - b43_phy_lcn_set_tx_gain(dev, &tx_gains); - b43_phy_lcn_set_bbmult(dev, bbmult); - b43_phy_lcn_sense_setup(dev, B43_SENSE_TEMP); - } else { - b43err(dev->wl, "TX power control not supported for this HW\n"); - } - - b43_mac_enable(dev); -} - -/* wlc_lcnphy_txrx_spur_avoidance_mode */ -static void b43_phy_lcn_txrx_spur_avoidance_mode(struct b43_wldev *dev, - bool enable) -{ - if (enable) { - b43_phy_write(dev, 0x942, 0x7); - b43_phy_write(dev, 0x93b, ((1 << 13) + 23)); - b43_phy_write(dev, 0x93c, ((1 << 13) + 1989)); - - b43_phy_write(dev, 0x44a, 0x084); - b43_phy_write(dev, 0x44a, 0x080); - b43_phy_write(dev, 0x6d3, 0x2222); - b43_phy_write(dev, 0x6d3, 0x2220); - } else { - b43_phy_write(dev, 0x942, 0x0); - b43_phy_write(dev, 0x93b, ((0 << 13) + 23)); - b43_phy_write(dev, 0x93c, ((0 << 13) + 1989)); - } - b43_mac_switch_freq(dev, enable); -} - -/************************************************** - * Channel switching ops. - **************************************************/ - -/* wlc_lcnphy_set_chanspec_tweaks */ -static void b43_phy_lcn_set_channel_tweaks(struct b43_wldev *dev, int channel) -{ - struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc; - - b43_phy_maskset(dev, 0x448, ~0x300, (channel == 14) ? 0x200 : 0x100); - - if (channel == 1 || channel == 2 || channel == 3 || channel == 4 || - channel == 9 || channel == 10 || channel == 11 || channel == 12) { - bcma_chipco_pll_write(cc, 0x2, 0x03000c04); - bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x0); - bcma_chipco_pll_write(cc, 0x4, 0x200005c0); - - bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400); - - b43_phy_write(dev, 0x942, 0); - - b43_phy_lcn_txrx_spur_avoidance_mode(dev, false); - b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1b00); - b43_phy_write(dev, 0x425, 0x5907); - } else { - bcma_chipco_pll_write(cc, 0x2, 0x03140c04); - bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x333333); - bcma_chipco_pll_write(cc, 0x4, 0x202c2820); - - bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400); - - b43_phy_write(dev, 0x942, 0); - - b43_phy_lcn_txrx_spur_avoidance_mode(dev, true); - b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1f00); - b43_phy_write(dev, 0x425, 0x590a); - } - - b43_phy_set(dev, 0x44a, 0x44); - b43_phy_write(dev, 0x44a, 0x80); -} - -/* wlc_phy_chanspec_set_lcnphy */ -static int b43_phy_lcn_set_channel(struct b43_wldev *dev, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type) -{ - static const u16 sfo_cfg[14][2] = { - {965, 1087}, {967, 1085}, {969, 1082}, {971, 1080}, {973, 1078}, - {975, 1076}, {977, 1073}, {979, 1071}, {981, 1069}, {983, 1067}, - {985, 1065}, {987, 1063}, {989, 1060}, {994, 1055}, - }; - - b43_phy_lcn_set_channel_tweaks(dev, channel->hw_value); - - b43_phy_set(dev, 0x44a, 0x44); - b43_phy_write(dev, 0x44a, 0x80); - - b43_radio_2064_channel_setup(dev); - mdelay(1); - - b43_phy_lcn_afe_set_unset(dev); - - b43_phy_write(dev, 0x657, sfo_cfg[channel->hw_value - 1][0]); - b43_phy_write(dev, 0x658, sfo_cfg[channel->hw_value - 1][1]); - - if (channel->hw_value == 14) { - b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (2) << 8); - b43_phy_lcn_load_tx_iir_cck_filter(dev, 3); - } else { - b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (1) << 8); - /* brcmsmac uses filter_type 2, we follow wl with 25 */ - b43_phy_lcn_load_tx_iir_cck_filter(dev, 25); - } - /* brcmsmac uses filter_type 2, we follow wl with 0 */ - b43_phy_lcn_load_tx_iir_ofdm_filter(dev, 0); - - b43_phy_maskset(dev, 0x4eb, ~(0x7 << 3), 0x1 << 3); - - return 0; -} - -/************************************************** - * Basic PHY ops. - **************************************************/ - -static int b43_phy_lcn_op_allocate(struct b43_wldev *dev) -{ - struct b43_phy_lcn *phy_lcn; - - phy_lcn = kzalloc(sizeof(*phy_lcn), GFP_KERNEL); - if (!phy_lcn) - return -ENOMEM; - dev->phy.lcn = phy_lcn; - - return 0; -} - -static void b43_phy_lcn_op_free(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_lcn *phy_lcn = phy->lcn; - - kfree(phy_lcn); - phy->lcn = NULL; -} - -static void b43_phy_lcn_op_prepare_structs(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_lcn *phy_lcn = phy->lcn; - - memset(phy_lcn, 0, sizeof(*phy_lcn)); -} - -/* wlc_phy_init_lcnphy */ -static int b43_phy_lcn_op_init(struct b43_wldev *dev) -{ - struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc; - - b43_phy_set(dev, 0x44a, 0x80); - b43_phy_mask(dev, 0x44a, 0x7f); - b43_phy_set(dev, 0x6d1, 0x80); - b43_phy_write(dev, 0x6d0, 0x7); - - b43_phy_lcn_afe_set_unset(dev); - - b43_phy_write(dev, 0x60a, 0xa0); - b43_phy_write(dev, 0x46a, 0x19); - b43_phy_maskset(dev, 0x663, 0xFF00, 0x64); - - b43_phy_lcn_tables_init(dev); - - b43_phy_lcn_rev0_baseband_init(dev); - b43_phy_lcn_bu_tweaks(dev); - - if (dev->phy.radio_ver == 0x2064) - b43_radio_2064_init(dev); - else - B43_WARN_ON(1); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - b43_phy_lcn_tx_pwr_ctl_init(dev); - - b43_switch_channel(dev, dev->phy.channel); - - bcma_chipco_regctl_maskset(cc, 0, 0xf, 0x9); - bcma_chipco_chipctl_maskset(cc, 0, 0, 0x03cddddd); - - /* TODO */ - - b43_phy_set(dev, 0x448, 0x4000); - udelay(100); - b43_phy_mask(dev, 0x448, ~0x4000); - - /* TODO */ - - return 0; -} - -static void b43_phy_lcn_op_software_rfkill(struct b43_wldev *dev, - bool blocked) -{ - if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED) - b43err(dev->wl, "MAC not suspended\n"); - - if (blocked) { - b43_phy_mask(dev, B43_PHY_LCN_RF_CTL2, ~0x7c00); - b43_phy_set(dev, B43_PHY_LCN_RF_CTL1, 0x1f00); - - b43_phy_mask(dev, B43_PHY_LCN_RF_CTL5, ~0x7f00); - b43_phy_mask(dev, B43_PHY_LCN_RF_CTL4, ~0x2); - b43_phy_set(dev, B43_PHY_LCN_RF_CTL3, 0x808); - - b43_phy_mask(dev, B43_PHY_LCN_RF_CTL7, ~0x8); - b43_phy_set(dev, B43_PHY_LCN_RF_CTL6, 0x8); - } else { - b43_phy_mask(dev, B43_PHY_LCN_RF_CTL1, ~0x1f00); - b43_phy_mask(dev, B43_PHY_LCN_RF_CTL3, ~0x808); - b43_phy_mask(dev, B43_PHY_LCN_RF_CTL6, ~0x8); - } -} - -static void b43_phy_lcn_op_switch_analog(struct b43_wldev *dev, bool on) -{ - if (on) { - b43_phy_mask(dev, B43_PHY_LCN_AFE_CTL1, ~0x7); - } else { - b43_phy_set(dev, B43_PHY_LCN_AFE_CTL2, 0x7); - b43_phy_set(dev, B43_PHY_LCN_AFE_CTL1, 0x7); - } -} - -static int b43_phy_lcn_op_switch_channel(struct b43_wldev *dev, - unsigned int new_channel) -{ - struct ieee80211_channel *channel = dev->wl->hw->conf.chandef.chan; - enum nl80211_channel_type channel_type = - cfg80211_get_chandef_type(&dev->wl->hw->conf.chandef); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if ((new_channel < 1) || (new_channel > 14)) - return -EINVAL; - } else { - return -EINVAL; - } - - return b43_phy_lcn_set_channel(dev, channel, channel_type); -} - -static unsigned int b43_phy_lcn_op_get_default_chan(struct b43_wldev *dev) -{ - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - return 1; - return 36; -} - -static enum b43_txpwr_result -b43_phy_lcn_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) -{ - return B43_TXPWR_RES_DONE; -} - -static void b43_phy_lcn_op_adjust_txpower(struct b43_wldev *dev) -{ -} - -/************************************************** - * R/W ops. - **************************************************/ - -static void b43_phy_lcn_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, - u16 set) -{ - b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); - b43_write16(dev, B43_MMIO_PHY_DATA, - (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); -} - -static u16 b43_phy_lcn_op_radio_read(struct b43_wldev *dev, u16 reg) -{ - /* LCN-PHY needs 0x200 for read access */ - reg |= 0x200; - - b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); - return b43_read16(dev, B43_MMIO_RADIO24_DATA); -} - -static void b43_phy_lcn_op_radio_write(struct b43_wldev *dev, u16 reg, - u16 value) -{ - b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); - b43_write16(dev, B43_MMIO_RADIO24_DATA, value); -} - -/************************************************** - * PHY ops struct. - **************************************************/ - -const struct b43_phy_operations b43_phyops_lcn = { - .allocate = b43_phy_lcn_op_allocate, - .free = b43_phy_lcn_op_free, - .prepare_structs = b43_phy_lcn_op_prepare_structs, - .init = b43_phy_lcn_op_init, - .phy_maskset = b43_phy_lcn_op_maskset, - .radio_read = b43_phy_lcn_op_radio_read, - .radio_write = b43_phy_lcn_op_radio_write, - .software_rfkill = b43_phy_lcn_op_software_rfkill, - .switch_analog = b43_phy_lcn_op_switch_analog, - .switch_channel = b43_phy_lcn_op_switch_channel, - .get_default_chan = b43_phy_lcn_op_get_default_chan, - .recalc_txpower = b43_phy_lcn_op_recalc_txpower, - .adjust_txpower = b43_phy_lcn_op_adjust_txpower, -}; diff --git a/drivers/net/wireless/b43/phy_lcn.h b/drivers/net/wireless/b43/phy_lcn.h deleted file mode 100644 index 6a7092e13fff..000000000000 --- a/drivers/net/wireless/b43/phy_lcn.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef B43_PHY_LCN_H_ -#define B43_PHY_LCN_H_ - -#include "phy_common.h" - - -#define B43_PHY_LCN_AFE_CTL1 B43_PHY_OFDM(0x03B) -#define B43_PHY_LCN_AFE_CTL2 B43_PHY_OFDM(0x03C) -#define B43_PHY_LCN_RF_CTL1 B43_PHY_OFDM(0x04C) -#define B43_PHY_LCN_RF_CTL2 B43_PHY_OFDM(0x04D) -#define B43_PHY_LCN_TABLE_ADDR B43_PHY_OFDM(0x055) /* Table address */ -#define B43_PHY_LCN_TABLE_DATALO B43_PHY_OFDM(0x056) /* Table data low */ -#define B43_PHY_LCN_TABLE_DATAHI B43_PHY_OFDM(0x057) /* Table data high */ -#define B43_PHY_LCN_RF_CTL3 B43_PHY_OFDM(0x0B0) -#define B43_PHY_LCN_RF_CTL4 B43_PHY_OFDM(0x0B1) -#define B43_PHY_LCN_RF_CTL5 B43_PHY_OFDM(0x0B7) -#define B43_PHY_LCN_RF_CTL6 B43_PHY_OFDM(0x0F9) -#define B43_PHY_LCN_RF_CTL7 B43_PHY_OFDM(0x0FA) - - -struct b43_phy_lcn { - bool hw_pwr_ctl; - bool hw_pwr_ctl_capable; - u8 tx_pwr_curr_idx; -}; - - -struct b43_phy_operations; -extern const struct b43_phy_operations b43_phyops_lcn; - -#endif /* B43_PHY_LCN_H_ */ diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c deleted file mode 100644 index 058a9f232050..000000000000 --- a/drivers/net/wireless/b43/phy_lp.c +++ /dev/null @@ -1,2716 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11a/g LP-PHY driver - - Copyright (c) 2008-2009 Michael Buesch - Copyright (c) 2009 Gábor Stefanik - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include - -#include "b43.h" -#include "main.h" -#include "phy_lp.h" -#include "phy_common.h" -#include "tables_lpphy.h" - - -static inline u16 channel2freq_lp(u8 channel) -{ - if (channel < 14) - return (2407 + 5 * channel); - else if (channel == 14) - return 2484; - else if (channel < 184) - return (5000 + 5 * channel); - else - return (4000 + 5 * channel); -} - -static unsigned int b43_lpphy_op_get_default_chan(struct b43_wldev *dev) -{ - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - return 1; - return 36; -} - -static int b43_lpphy_op_allocate(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy; - - lpphy = kzalloc(sizeof(*lpphy), GFP_KERNEL); - if (!lpphy) - return -ENOMEM; - dev->phy.lp = lpphy; - - return 0; -} - -static void b43_lpphy_op_prepare_structs(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_lp *lpphy = phy->lp; - - memset(lpphy, 0, sizeof(*lpphy)); - lpphy->antenna = B43_ANTENNA_DEFAULT; - - //TODO -} - -static void b43_lpphy_op_free(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - - kfree(lpphy); - dev->phy.lp = NULL; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/LP/ReadBandSrom */ -static void lpphy_read_band_sprom(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - struct b43_phy_lp *lpphy = dev->phy.lp; - u16 cckpo, maxpwr; - u32 ofdmpo; - int i; - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - lpphy->tx_isolation_med_band = sprom->tri2g; - lpphy->bx_arch = sprom->bxa2g; - lpphy->rx_pwr_offset = sprom->rxpo2g; - lpphy->rssi_vf = sprom->rssismf2g; - lpphy->rssi_vc = sprom->rssismc2g; - lpphy->rssi_gs = sprom->rssisav2g; - lpphy->txpa[0] = sprom->pa0b0; - lpphy->txpa[1] = sprom->pa0b1; - lpphy->txpa[2] = sprom->pa0b2; - maxpwr = sprom->maxpwr_bg; - lpphy->max_tx_pwr_med_band = maxpwr; - cckpo = sprom->cck2gpo; - if (cckpo) { - ofdmpo = sprom->ofdm2gpo; - for (i = 0; i < 4; i++) { - lpphy->tx_max_rate[i] = - maxpwr - (ofdmpo & 0xF) * 2; - ofdmpo >>= 4; - } - ofdmpo = sprom->ofdm2gpo; - for (i = 4; i < 15; i++) { - lpphy->tx_max_rate[i] = - maxpwr - (ofdmpo & 0xF) * 2; - ofdmpo >>= 4; - } - } else { - u8 opo = sprom->opo; - for (i = 0; i < 4; i++) - lpphy->tx_max_rate[i] = maxpwr; - for (i = 4; i < 15; i++) - lpphy->tx_max_rate[i] = maxpwr - opo; - } - } else { /* 5GHz */ - lpphy->tx_isolation_low_band = sprom->tri5gl; - lpphy->tx_isolation_med_band = sprom->tri5g; - lpphy->tx_isolation_hi_band = sprom->tri5gh; - lpphy->bx_arch = sprom->bxa5g; - lpphy->rx_pwr_offset = sprom->rxpo5g; - lpphy->rssi_vf = sprom->rssismf5g; - lpphy->rssi_vc = sprom->rssismc5g; - lpphy->rssi_gs = sprom->rssisav5g; - lpphy->txpa[0] = sprom->pa1b0; - lpphy->txpa[1] = sprom->pa1b1; - lpphy->txpa[2] = sprom->pa1b2; - lpphy->txpal[0] = sprom->pa1lob0; - lpphy->txpal[1] = sprom->pa1lob1; - lpphy->txpal[2] = sprom->pa1lob2; - lpphy->txpah[0] = sprom->pa1hib0; - lpphy->txpah[1] = sprom->pa1hib1; - lpphy->txpah[2] = sprom->pa1hib2; - maxpwr = sprom->maxpwr_al; - ofdmpo = sprom->ofdm5glpo; - lpphy->max_tx_pwr_low_band = maxpwr; - for (i = 4; i < 12; i++) { - lpphy->tx_max_ratel[i] = maxpwr - (ofdmpo & 0xF) * 2; - ofdmpo >>= 4; - } - maxpwr = sprom->maxpwr_a; - ofdmpo = sprom->ofdm5gpo; - lpphy->max_tx_pwr_med_band = maxpwr; - for (i = 4; i < 12; i++) { - lpphy->tx_max_rate[i] = maxpwr - (ofdmpo & 0xF) * 2; - ofdmpo >>= 4; - } - maxpwr = sprom->maxpwr_ah; - ofdmpo = sprom->ofdm5ghpo; - lpphy->max_tx_pwr_hi_band = maxpwr; - for (i = 4; i < 12; i++) { - lpphy->tx_max_rateh[i] = maxpwr - (ofdmpo & 0xF) * 2; - ofdmpo >>= 4; - } - } -} - -static void lpphy_adjust_gain_table(struct b43_wldev *dev, u32 freq) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - u16 temp[3]; - u16 isolation; - - B43_WARN_ON(dev->phy.rev >= 2); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - isolation = lpphy->tx_isolation_med_band; - else if (freq <= 5320) - isolation = lpphy->tx_isolation_low_band; - else if (freq <= 5700) - isolation = lpphy->tx_isolation_med_band; - else - isolation = lpphy->tx_isolation_hi_band; - - temp[0] = ((isolation - 26) / 12) << 12; - temp[1] = temp[0] + 0x1000; - temp[2] = temp[0] + 0x2000; - - b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), 3, temp); - b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), 3, temp); -} - -static void lpphy_table_init(struct b43_wldev *dev) -{ - u32 freq = channel2freq_lp(b43_lpphy_op_get_default_chan(dev)); - - if (dev->phy.rev < 2) - lpphy_rev0_1_table_init(dev); - else - lpphy_rev2plus_table_init(dev); - - lpphy_init_tx_gain_table(dev); - - if (dev->phy.rev < 2) - lpphy_adjust_gain_table(dev, freq); -} - -static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev) -{ - struct ssb_bus *bus = dev->dev->sdev->bus; - struct ssb_sprom *sprom = dev->dev->bus_sprom; - struct b43_phy_lp *lpphy = dev->phy.lp; - u16 tmp, tmp2; - - b43_phy_mask(dev, B43_LPPHY_AFE_DAC_CTL, 0xF7FF); - b43_phy_write(dev, B43_LPPHY_AFE_CTL, 0); - b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, 0); - b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, 0); - b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0); - b43_phy_set(dev, B43_LPPHY_AFE_DAC_CTL, 0x0004); - b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0x0078); - b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x5800); - b43_phy_write(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x0016); - b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_0, 0xFFF8, 0x0004); - b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5400); - b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2400); - b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100); - b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0x0006); - b43_phy_mask(dev, B43_LPPHY_RX_RADIO_CTL, 0xFFFE); - b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x0005); - b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0x0180); - b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x3C00); - b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFFF0, 0x0005); - b43_phy_maskset(dev, B43_LPPHY_GAIN_MISMATCH_LIMIT, 0xFFC0, 0x001A); - b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0xFF00, 0x00B3); - b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00); - b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB, - 0xFF00, lpphy->rx_pwr_offset); - if ((sprom->boardflags_lo & B43_BFL_FEM) && - ((b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) || - (sprom->boardflags_hi & B43_BFH_PAREF))) { - ssb_pmu_set_ldo_voltage(&bus->chipco, LDO_PAREF, 0x28); - ssb_pmu_set_ldo_paref(&bus->chipco, true); - if (dev->phy.rev == 0) { - b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT, - 0xFFCF, 0x0010); - } - b43_lptab_write(dev, B43_LPTAB16(11, 7), 60); - } else { - ssb_pmu_set_ldo_paref(&bus->chipco, false); - b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT, - 0xFFCF, 0x0020); - b43_lptab_write(dev, B43_LPTAB16(11, 7), 100); - } - tmp = lpphy->rssi_vf | lpphy->rssi_vc << 4 | 0xA000; - b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, tmp); - if (sprom->boardflags_hi & B43_BFH_RSSIINV) - b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x0AAA); - else - b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x02AA); - b43_lptab_write(dev, B43_LPTAB16(11, 1), 24); - b43_phy_maskset(dev, B43_LPPHY_RX_RADIO_CTL, - 0xFFF9, (lpphy->bx_arch << 1)); - if (dev->phy.rev == 1 && - (sprom->boardflags_hi & B43_BFH_FEM_BT)) { - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0x3F00, 0x0900); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x000A); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0400); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x000A); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0B00); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xFFC0, 0x000A); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xC0FF, 0x0900); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xFFC0, 0x000A); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xC0FF, 0x0B00); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xFFC0, 0x000A); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xC0FF, 0x0900); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xFFC0, 0x000A); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xC0FF, 0x0B00); - } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ || - (dev->dev->board_type == SSB_BOARD_BU4312) || - (dev->phy.rev == 0 && (sprom->boardflags_lo & B43_BFL_FEM))) { - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0001); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0400); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0001); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0500); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0800); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0A00); - } else if (dev->phy.rev == 1 || - (sprom->boardflags_lo & B43_BFL_FEM)) { - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0004); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0800); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0004); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0C00); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0100); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0300); - } else { - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0900); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0006); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0500); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0006); - b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0700); - } - if (dev->phy.rev == 1 && (sprom->boardflags_hi & B43_BFH_PAREF)) { - b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_5, B43_LPPHY_TR_LOOKUP_1); - b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_6, B43_LPPHY_TR_LOOKUP_2); - b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_7, B43_LPPHY_TR_LOOKUP_3); - b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_8, B43_LPPHY_TR_LOOKUP_4); - } - if ((sprom->boardflags_hi & B43_BFH_FEM_BT) && - (dev->dev->chip_id == 0x5354) && - (dev->dev->chip_pkg == SSB_CHIPPACK_BCM4712S)) { - b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0006); - b43_phy_write(dev, B43_LPPHY_GPIO_SELECT, 0x0005); - b43_phy_write(dev, B43_LPPHY_GPIO_OUTEN, 0xFFFF); - //FIXME the Broadcom driver caches & delays this HF write! - b43_hf_write(dev, b43_hf_read(dev) | B43_HF_PR45960W); - } - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x8000); - b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0040); - b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0xA400); - b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xF0FF, 0x0B00); - b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x0007); - b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFF8, 0x0003); - b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFC7, 0x0020); - b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF); - } else { /* 5GHz */ - b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0x7FFF); - b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFBF); - } - if (dev->phy.rev == 1) { - tmp = b43_phy_read(dev, B43_LPPHY_CLIPCTRTHRESH); - tmp2 = (tmp & 0x03E0) >> 5; - tmp2 |= tmp2 << 5; - b43_phy_write(dev, B43_LPPHY_4C3, tmp2); - tmp = b43_phy_read(dev, B43_LPPHY_GAINDIRECTMISMATCH); - tmp2 = (tmp & 0x1F00) >> 8; - tmp2 |= tmp2 << 5; - b43_phy_write(dev, B43_LPPHY_4C4, tmp2); - tmp = b43_phy_read(dev, B43_LPPHY_VERYLOWGAINDB); - tmp2 = tmp & 0x00FF; - tmp2 |= tmp << 8; - b43_phy_write(dev, B43_LPPHY_4C5, tmp2); - } -} - -static void lpphy_save_dig_flt_state(struct b43_wldev *dev) -{ - static const u16 addr[] = { - B43_PHY_OFDM(0xC1), - B43_PHY_OFDM(0xC2), - B43_PHY_OFDM(0xC3), - B43_PHY_OFDM(0xC4), - B43_PHY_OFDM(0xC5), - B43_PHY_OFDM(0xC6), - B43_PHY_OFDM(0xC7), - B43_PHY_OFDM(0xC8), - B43_PHY_OFDM(0xCF), - }; - - static const u16 coefs[] = { - 0xDE5E, 0xE832, 0xE331, 0x4D26, - 0x0026, 0x1420, 0x0020, 0xFE08, - 0x0008, - }; - - struct b43_phy_lp *lpphy = dev->phy.lp; - int i; - - for (i = 0; i < ARRAY_SIZE(addr); i++) { - lpphy->dig_flt_state[i] = b43_phy_read(dev, addr[i]); - b43_phy_write(dev, addr[i], coefs[i]); - } -} - -static void lpphy_restore_dig_flt_state(struct b43_wldev *dev) -{ - static const u16 addr[] = { - B43_PHY_OFDM(0xC1), - B43_PHY_OFDM(0xC2), - B43_PHY_OFDM(0xC3), - B43_PHY_OFDM(0xC4), - B43_PHY_OFDM(0xC5), - B43_PHY_OFDM(0xC6), - B43_PHY_OFDM(0xC7), - B43_PHY_OFDM(0xC8), - B43_PHY_OFDM(0xCF), - }; - - struct b43_phy_lp *lpphy = dev->phy.lp; - int i; - - for (i = 0; i < ARRAY_SIZE(addr); i++) - b43_phy_write(dev, addr[i], lpphy->dig_flt_state[i]); -} - -static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - - b43_phy_write(dev, B43_LPPHY_AFE_DAC_CTL, 0x50); - b43_phy_write(dev, B43_LPPHY_AFE_CTL, 0x8800); - b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, 0); - b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0); - b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, 0); - b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0); - b43_phy_write(dev, B43_PHY_OFDM(0xF9), 0); - b43_phy_write(dev, B43_LPPHY_TR_LOOKUP_1, 0); - b43_phy_set(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x10); - b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0xB4); - b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xF8FF, 0x200); - b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xFF00, 0x7F); - b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFF0F, 0x40); - b43_phy_maskset(dev, B43_LPPHY_PREAMBLECONFIRMTO, 0xFF00, 0x2); - b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x4000); - b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x2000); - b43_phy_set(dev, B43_PHY_OFDM(0x10A), 0x1); - if (dev->dev->board_rev >= 0x18) { - b43_lptab_write(dev, B43_LPTAB32(17, 65), 0xEC); - b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x14); - } else { - b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x10); - } - b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0xFF00, 0xF4); - b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0x00FF, 0xF100); - b43_phy_write(dev, B43_LPPHY_CLIPTHRESH, 0x48); - b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0xFF00, 0x46); - b43_phy_maskset(dev, B43_PHY_OFDM(0xE4), 0xFF00, 0x10); - b43_phy_maskset(dev, B43_LPPHY_PWR_THRESH1, 0xFFF0, 0x9); - b43_phy_mask(dev, B43_LPPHY_GAINDIRECTMISMATCH, ~0xF); - b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5500); - b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0xA0); - b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xE0FF, 0x300); - b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2A00); - if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { - b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100); - b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0xA); - } else { - b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x1E00); - b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0xD); - } - b43_phy_maskset(dev, B43_PHY_OFDM(0xFE), 0xFFE0, 0x1F); - b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0xFFE0, 0xC); - b43_phy_maskset(dev, B43_PHY_OFDM(0x100), 0xFF00, 0x19); - b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0x03FF, 0x3C00); - b43_phy_maskset(dev, B43_PHY_OFDM(0xFE), 0xFC1F, 0x3E0); - b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0xFFE0, 0xC); - b43_phy_maskset(dev, B43_PHY_OFDM(0x100), 0x00FF, 0x1900); - b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x5800); - b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x12); - b43_phy_maskset(dev, B43_LPPHY_GAINMISMATCH, 0x0FFF, 0x9000); - - if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { - b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0); - b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40); - } - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x40); - b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xF0FF, 0xB00); - b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x6); - b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0x9D00); - b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0xFF00, 0xA1); - b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF); - } else /* 5GHz */ - b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x40); - - b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0xFF00, 0xB3); - b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00); - b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB, 0xFF00, lpphy->rx_pwr_offset); - b43_phy_set(dev, B43_LPPHY_RESET_CTL, 0x44); - b43_phy_write(dev, B43_LPPHY_RESET_CTL, 0x80); - b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, 0xA954); - b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_1, - 0x2000 | ((u16)lpphy->rssi_gs << 10) | - ((u16)lpphy->rssi_vc << 4) | lpphy->rssi_vf); - - if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { - b43_phy_set(dev, B43_LPPHY_AFE_ADC_CTL_0, 0x1C); - b43_phy_maskset(dev, B43_LPPHY_AFE_CTL, 0x00FF, 0x8800); - b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_1, 0xFC3C, 0x0400); - } - - lpphy_save_dig_flt_state(dev); -} - -static void lpphy_baseband_init(struct b43_wldev *dev) -{ - lpphy_table_init(dev); - if (dev->phy.rev >= 2) - lpphy_baseband_rev2plus_init(dev); - else - lpphy_baseband_rev0_1_init(dev); -} - -struct b2062_freqdata { - u16 freq; - u8 data[6]; -}; - -/* Initialize the 2062 radio. */ -static void lpphy_2062_init(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - struct ssb_bus *bus = dev->dev->sdev->bus; - u32 crystalfreq, tmp, ref; - unsigned int i; - const struct b2062_freqdata *fd = NULL; - - static const struct b2062_freqdata freqdata_tab[] = { - { .freq = 12000, .data[0] = 6, .data[1] = 6, .data[2] = 6, - .data[3] = 6, .data[4] = 10, .data[5] = 6, }, - { .freq = 13000, .data[0] = 4, .data[1] = 4, .data[2] = 4, - .data[3] = 4, .data[4] = 11, .data[5] = 7, }, - { .freq = 14400, .data[0] = 3, .data[1] = 3, .data[2] = 3, - .data[3] = 3, .data[4] = 12, .data[5] = 7, }, - { .freq = 16200, .data[0] = 3, .data[1] = 3, .data[2] = 3, - .data[3] = 3, .data[4] = 13, .data[5] = 8, }, - { .freq = 18000, .data[0] = 2, .data[1] = 2, .data[2] = 2, - .data[3] = 2, .data[4] = 14, .data[5] = 8, }, - { .freq = 19200, .data[0] = 1, .data[1] = 1, .data[2] = 1, - .data[3] = 1, .data[4] = 14, .data[5] = 9, }, - }; - - b2062_upload_init_table(dev); - - b43_radio_write(dev, B2062_N_TX_CTL3, 0); - b43_radio_write(dev, B2062_N_TX_CTL4, 0); - b43_radio_write(dev, B2062_N_TX_CTL5, 0); - b43_radio_write(dev, B2062_N_TX_CTL6, 0); - b43_radio_write(dev, B2062_N_PDN_CTL0, 0x40); - b43_radio_write(dev, B2062_N_PDN_CTL0, 0); - b43_radio_write(dev, B2062_N_CALIB_TS, 0x10); - b43_radio_write(dev, B2062_N_CALIB_TS, 0); - if (dev->phy.rev > 0) { - b43_radio_write(dev, B2062_S_BG_CTL1, - (b43_radio_read(dev, B2062_N_COMM2) >> 1) | 0x80); - } - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - b43_radio_set(dev, B2062_N_TSSI_CTL0, 0x1); - else - b43_radio_mask(dev, B2062_N_TSSI_CTL0, ~0x1); - - /* Get the crystal freq, in Hz. */ - crystalfreq = bus->chipco.pmu.crystalfreq * 1000; - - B43_WARN_ON(!(bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU)); - B43_WARN_ON(crystalfreq == 0); - - if (crystalfreq <= 30000000) { - lpphy->pdiv = 1; - b43_radio_mask(dev, B2062_S_RFPLL_CTL1, 0xFFFB); - } else { - lpphy->pdiv = 2; - b43_radio_set(dev, B2062_S_RFPLL_CTL1, 0x4); - } - - tmp = (((800000000 * lpphy->pdiv + crystalfreq) / - (2 * crystalfreq)) - 8) & 0xFF; - b43_radio_write(dev, B2062_S_RFPLL_CTL7, tmp); - - tmp = (((100 * crystalfreq + 16000000 * lpphy->pdiv) / - (32000000 * lpphy->pdiv)) - 1) & 0xFF; - b43_radio_write(dev, B2062_S_RFPLL_CTL18, tmp); - - tmp = (((2 * crystalfreq + 1000000 * lpphy->pdiv) / - (2000000 * lpphy->pdiv)) - 1) & 0xFF; - b43_radio_write(dev, B2062_S_RFPLL_CTL19, tmp); - - ref = (1000 * lpphy->pdiv + 2 * crystalfreq) / (2000 * lpphy->pdiv); - ref &= 0xFFFF; - for (i = 0; i < ARRAY_SIZE(freqdata_tab); i++) { - if (ref < freqdata_tab[i].freq) { - fd = &freqdata_tab[i]; - break; - } - } - if (!fd) - fd = &freqdata_tab[ARRAY_SIZE(freqdata_tab) - 1]; - b43dbg(dev->wl, "b2062: Using crystal tab entry %u kHz.\n", - fd->freq); /* FIXME: Keep this printk until the code is fully debugged. */ - - b43_radio_write(dev, B2062_S_RFPLL_CTL8, - ((u16)(fd->data[1]) << 4) | fd->data[0]); - b43_radio_write(dev, B2062_S_RFPLL_CTL9, - ((u16)(fd->data[3]) << 4) | fd->data[2]); - b43_radio_write(dev, B2062_S_RFPLL_CTL10, fd->data[4]); - b43_radio_write(dev, B2062_S_RFPLL_CTL11, fd->data[5]); -} - -/* Initialize the 2063 radio. */ -static void lpphy_2063_init(struct b43_wldev *dev) -{ - b2063_upload_init_table(dev); - b43_radio_write(dev, B2063_LOGEN_SP5, 0); - b43_radio_set(dev, B2063_COMM8, 0x38); - b43_radio_write(dev, B2063_REG_SP1, 0x56); - b43_radio_mask(dev, B2063_RX_BB_CTL2, ~0x2); - b43_radio_write(dev, B2063_PA_SP7, 0); - b43_radio_write(dev, B2063_TX_RF_SP6, 0x20); - b43_radio_write(dev, B2063_TX_RF_SP9, 0x40); - if (dev->phy.rev == 2) { - b43_radio_write(dev, B2063_PA_SP3, 0xa0); - b43_radio_write(dev, B2063_PA_SP4, 0xa0); - b43_radio_write(dev, B2063_PA_SP2, 0x18); - } else { - b43_radio_write(dev, B2063_PA_SP3, 0x20); - b43_radio_write(dev, B2063_PA_SP2, 0x20); - } -} - -struct lpphy_stx_table_entry { - u16 phy_offset; - u16 phy_shift; - u16 rf_addr; - u16 rf_shift; - u16 mask; -}; - -static const struct lpphy_stx_table_entry lpphy_stx_table[] = { - { .phy_offset = 2, .phy_shift = 6, .rf_addr = 0x3d, .rf_shift = 3, .mask = 0x01, }, - { .phy_offset = 1, .phy_shift = 12, .rf_addr = 0x4c, .rf_shift = 1, .mask = 0x01, }, - { .phy_offset = 1, .phy_shift = 8, .rf_addr = 0x50, .rf_shift = 0, .mask = 0x7f, }, - { .phy_offset = 0, .phy_shift = 8, .rf_addr = 0x44, .rf_shift = 0, .mask = 0xff, }, - { .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4a, .rf_shift = 0, .mask = 0xff, }, - { .phy_offset = 0, .phy_shift = 4, .rf_addr = 0x4d, .rf_shift = 0, .mask = 0xff, }, - { .phy_offset = 1, .phy_shift = 4, .rf_addr = 0x4e, .rf_shift = 0, .mask = 0xff, }, - { .phy_offset = 0, .phy_shift = 12, .rf_addr = 0x4f, .rf_shift = 0, .mask = 0x0f, }, - { .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4f, .rf_shift = 4, .mask = 0x0f, }, - { .phy_offset = 3, .phy_shift = 0, .rf_addr = 0x49, .rf_shift = 0, .mask = 0x0f, }, - { .phy_offset = 4, .phy_shift = 3, .rf_addr = 0x46, .rf_shift = 4, .mask = 0x07, }, - { .phy_offset = 3, .phy_shift = 15, .rf_addr = 0x46, .rf_shift = 0, .mask = 0x01, }, - { .phy_offset = 4, .phy_shift = 0, .rf_addr = 0x46, .rf_shift = 1, .mask = 0x07, }, - { .phy_offset = 3, .phy_shift = 8, .rf_addr = 0x48, .rf_shift = 4, .mask = 0x07, }, - { .phy_offset = 3, .phy_shift = 11, .rf_addr = 0x48, .rf_shift = 0, .mask = 0x0f, }, - { .phy_offset = 3, .phy_shift = 4, .rf_addr = 0x49, .rf_shift = 4, .mask = 0x0f, }, - { .phy_offset = 2, .phy_shift = 15, .rf_addr = 0x45, .rf_shift = 0, .mask = 0x01, }, - { .phy_offset = 5, .phy_shift = 13, .rf_addr = 0x52, .rf_shift = 4, .mask = 0x07, }, - { .phy_offset = 6, .phy_shift = 0, .rf_addr = 0x52, .rf_shift = 7, .mask = 0x01, }, - { .phy_offset = 5, .phy_shift = 3, .rf_addr = 0x41, .rf_shift = 5, .mask = 0x07, }, - { .phy_offset = 5, .phy_shift = 6, .rf_addr = 0x41, .rf_shift = 0, .mask = 0x0f, }, - { .phy_offset = 5, .phy_shift = 10, .rf_addr = 0x42, .rf_shift = 5, .mask = 0x07, }, - { .phy_offset = 4, .phy_shift = 15, .rf_addr = 0x42, .rf_shift = 0, .mask = 0x01, }, - { .phy_offset = 5, .phy_shift = 0, .rf_addr = 0x42, .rf_shift = 1, .mask = 0x07, }, - { .phy_offset = 4, .phy_shift = 11, .rf_addr = 0x43, .rf_shift = 4, .mask = 0x0f, }, - { .phy_offset = 4, .phy_shift = 7, .rf_addr = 0x43, .rf_shift = 0, .mask = 0x0f, }, - { .phy_offset = 4, .phy_shift = 6, .rf_addr = 0x45, .rf_shift = 1, .mask = 0x01, }, - { .phy_offset = 2, .phy_shift = 7, .rf_addr = 0x40, .rf_shift = 4, .mask = 0x0f, }, - { .phy_offset = 2, .phy_shift = 11, .rf_addr = 0x40, .rf_shift = 0, .mask = 0x0f, }, -}; - -static void lpphy_sync_stx(struct b43_wldev *dev) -{ - const struct lpphy_stx_table_entry *e; - unsigned int i; - u16 tmp; - - for (i = 0; i < ARRAY_SIZE(lpphy_stx_table); i++) { - e = &lpphy_stx_table[i]; - tmp = b43_radio_read(dev, e->rf_addr); - tmp >>= e->rf_shift; - tmp <<= e->phy_shift; - b43_phy_maskset(dev, B43_PHY_OFDM(0xF2 + e->phy_offset), - ~(e->mask << e->phy_shift), tmp); - } -} - -static void lpphy_radio_init(struct b43_wldev *dev) -{ - /* The radio is attached through the 4wire bus. */ - b43_phy_set(dev, B43_LPPHY_FOURWIRE_CTL, 0x2); - udelay(1); - b43_phy_mask(dev, B43_LPPHY_FOURWIRE_CTL, 0xFFFD); - udelay(1); - - if (dev->phy.radio_ver == 0x2062) { - lpphy_2062_init(dev); - } else { - lpphy_2063_init(dev); - lpphy_sync_stx(dev); - b43_phy_write(dev, B43_PHY_OFDM(0xF0), 0x5F80); - b43_phy_write(dev, B43_PHY_OFDM(0xF1), 0); - if (dev->dev->chip_id == 0x4325) { - // TODO SSB PMU recalibration - } - } -} - -struct lpphy_iq_est { u32 iq_prod, i_pwr, q_pwr; }; - -static void lpphy_set_rc_cap(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - - u8 rc_cap = (lpphy->rc_cap & 0x1F) >> 1; - - if (dev->phy.rev == 1) //FIXME check channel 14! - rc_cap = min_t(u8, rc_cap + 5, 15); - - b43_radio_write(dev, B2062_N_RXBB_CALIB2, - max_t(u8, lpphy->rc_cap - 4, 0x80)); - b43_radio_write(dev, B2062_N_TX_CTL_A, rc_cap | 0x80); - b43_radio_write(dev, B2062_S_RXG_CNT16, - ((lpphy->rc_cap & 0x1F) >> 2) | 0x80); -} - -static u8 lpphy_get_bb_mult(struct b43_wldev *dev) -{ - return (b43_lptab_read(dev, B43_LPTAB16(0, 87)) & 0xFF00) >> 8; -} - -static void lpphy_set_bb_mult(struct b43_wldev *dev, u8 bb_mult) -{ - b43_lptab_write(dev, B43_LPTAB16(0, 87), (u16)bb_mult << 8); -} - -static void lpphy_set_deaf(struct b43_wldev *dev, bool user) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - - if (user) - lpphy->crs_usr_disable = true; - else - lpphy->crs_sys_disable = true; - b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFF1F, 0x80); -} - -static void lpphy_clear_deaf(struct b43_wldev *dev, bool user) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - - if (user) - lpphy->crs_usr_disable = false; - else - lpphy->crs_sys_disable = false; - - if (!lpphy->crs_usr_disable && !lpphy->crs_sys_disable) { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, - 0xFF1F, 0x60); - else - b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, - 0xFF1F, 0x20); - } -} - -static void lpphy_set_trsw_over(struct b43_wldev *dev, bool tx, bool rx) -{ - u16 trsw = (tx << 1) | rx; - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, trsw); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3); -} - -static void lpphy_disable_crs(struct b43_wldev *dev, bool user) -{ - lpphy_set_deaf(dev, user); - lpphy_set_trsw_over(dev, false, true); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFB); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x4); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFF7); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x10); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFDF); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFBF); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x7); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x38); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x100); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFDFF); - b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL0, 0); - b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL1, 1); - b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL2, 0x20); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFBFF); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xF7FF); - b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, 0); - b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, 0x45AF); - b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0x3FF); -} - -static void lpphy_restore_crs(struct b43_wldev *dev, bool user) -{ - lpphy_clear_deaf(dev, user); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFF80); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFC00); -} - -struct lpphy_tx_gains { u16 gm, pga, pad, dac; }; - -static void lpphy_disable_rx_gain_override(struct b43_wldev *dev) -{ - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF); - if (dev->phy.rev >= 2) { - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF); - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF); - b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7); - } - } else { - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF); - } -} - -static void lpphy_enable_rx_gain_override(struct b43_wldev *dev) -{ - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40); - if (dev->phy.rev >= 2) { - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100); - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400); - b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8); - } - } else { - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200); - } -} - -static void lpphy_disable_tx_gain_override(struct b43_wldev *dev) -{ - if (dev->phy.rev < 2) - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF); - else { - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF); - } - b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF); -} - -static void lpphy_enable_tx_gain_override(struct b43_wldev *dev) -{ - if (dev->phy.rev < 2) - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100); - else { - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x80); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x4000); - } - b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x40); -} - -static struct lpphy_tx_gains lpphy_get_tx_gains(struct b43_wldev *dev) -{ - struct lpphy_tx_gains gains; - u16 tmp; - - gains.dac = (b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0x380) >> 7; - if (dev->phy.rev < 2) { - tmp = b43_phy_read(dev, - B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL) & 0x7FF; - gains.gm = tmp & 0x0007; - gains.pga = (tmp & 0x0078) >> 3; - gains.pad = (tmp & 0x780) >> 7; - } else { - tmp = b43_phy_read(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL); - gains.pad = b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0xFF; - gains.gm = tmp & 0xFF; - gains.pga = (tmp >> 8) & 0xFF; - } - - return gains; -} - -static void lpphy_set_dac_gain(struct b43_wldev *dev, u16 dac) -{ - u16 ctl = b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0xC7F; - ctl |= dac << 7; - b43_phy_maskset(dev, B43_LPPHY_AFE_DAC_CTL, 0xF000, ctl); -} - -static u16 lpphy_get_pa_gain(struct b43_wldev *dev) -{ - return b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x7F; -} - -static void lpphy_set_pa_gain(struct b43_wldev *dev, u16 gain) -{ - b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), 0xE03F, gain << 6); - b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), 0x80FF, gain << 8); -} - -static void lpphy_set_tx_gains(struct b43_wldev *dev, - struct lpphy_tx_gains gains) -{ - u16 rf_gain, pa_gain; - - if (dev->phy.rev < 2) { - rf_gain = (gains.pad << 7) | (gains.pga << 3) | gains.gm; - b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, - 0xF800, rf_gain); - } else { - pa_gain = lpphy_get_pa_gain(dev); - b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, - (gains.pga << 8) | gains.gm); - /* - * SPEC FIXME The spec calls for (pa_gain << 8) here, but that - * conflicts with the spec for set_pa_gain! Vendor driver bug? - */ - b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), - 0x8000, gains.pad | (pa_gain << 6)); - b43_phy_write(dev, B43_PHY_OFDM(0xFC), - (gains.pga << 8) | gains.gm); - b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), - 0x8000, gains.pad | (pa_gain << 8)); - } - lpphy_set_dac_gain(dev, gains.dac); - lpphy_enable_tx_gain_override(dev); -} - -static void lpphy_rev0_1_set_rx_gain(struct b43_wldev *dev, u32 gain) -{ - u16 trsw = gain & 0x1; - u16 lna = (gain & 0xFFFC) | ((gain & 0xC) >> 2); - u16 ext_lna = (gain & 2) >> 1; - - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, - 0xFBFF, ext_lna << 10); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, - 0xF7FF, ext_lna << 11); - b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, lna); -} - -static void lpphy_rev2plus_set_rx_gain(struct b43_wldev *dev, u32 gain) -{ - u16 low_gain = gain & 0xFFFF; - u16 high_gain = (gain >> 16) & 0xF; - u16 ext_lna = (gain >> 21) & 0x1; - u16 trsw = ~(gain >> 20) & 0x1; - u16 tmp; - - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, - 0xFDFF, ext_lna << 9); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, - 0xFBFF, ext_lna << 10); - b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, low_gain); - b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF0, high_gain); - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - tmp = (gain >> 2) & 0x3; - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, - 0xE7FF, tmp<<11); - b43_phy_maskset(dev, B43_PHY_OFDM(0xE6), 0xFFE7, tmp << 3); - } -} - -static void lpphy_set_rx_gain(struct b43_wldev *dev, u32 gain) -{ - if (dev->phy.rev < 2) - lpphy_rev0_1_set_rx_gain(dev, gain); - else - lpphy_rev2plus_set_rx_gain(dev, gain); - lpphy_enable_rx_gain_override(dev); -} - -static void lpphy_set_rx_gain_by_index(struct b43_wldev *dev, u16 idx) -{ - u32 gain = b43_lptab_read(dev, B43_LPTAB16(12, idx)); - lpphy_set_rx_gain(dev, gain); -} - -static void lpphy_stop_ddfs(struct b43_wldev *dev) -{ - b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFD); - b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xFFDF); -} - -static void lpphy_run_ddfs(struct b43_wldev *dev, int i_on, int q_on, - int incr1, int incr2, int scale_idx) -{ - lpphy_stop_ddfs(dev); - b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0xFF80); - b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0x80FF); - b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0xFF80, incr1); - b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0x80FF, incr2 << 8); - b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF7, i_on << 3); - b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFEF, q_on << 4); - b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFF9F, scale_idx << 5); - b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFB); - b43_phy_set(dev, B43_LPPHY_AFE_DDFS, 0x2); - b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x20); -} - -static bool lpphy_rx_iq_est(struct b43_wldev *dev, u16 samples, u8 time, - struct lpphy_iq_est *iq_est) -{ - int i; - - b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFF7); - b43_phy_write(dev, B43_LPPHY_IQ_NUM_SMPLS_ADDR, samples); - b43_phy_maskset(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFF00, time); - b43_phy_mask(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFEFF); - b43_phy_set(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0x200); - - for (i = 0; i < 500; i++) { - if (!(b43_phy_read(dev, - B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) - break; - msleep(1); - } - - if ((b43_phy_read(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) { - b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8); - return false; - } - - iq_est->iq_prod = b43_phy_read(dev, B43_LPPHY_IQ_ACC_HI_ADDR); - iq_est->iq_prod <<= 16; - iq_est->iq_prod |= b43_phy_read(dev, B43_LPPHY_IQ_ACC_LO_ADDR); - - iq_est->i_pwr = b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_HI_ADDR); - iq_est->i_pwr <<= 16; - iq_est->i_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_LO_ADDR); - - iq_est->q_pwr = b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_HI_ADDR); - iq_est->q_pwr <<= 16; - iq_est->q_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_LO_ADDR); - - b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8); - return true; -} - -static int lpphy_loopback(struct b43_wldev *dev) -{ - struct lpphy_iq_est iq_est; - int i, index = -1; - u32 tmp; - - memset(&iq_est, 0, sizeof(iq_est)); - - lpphy_set_trsw_over(dev, true, true); - b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 1); - b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x8); - b43_radio_write(dev, B2062_N_TX_CTL_A, 0x80); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x80); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x80); - for (i = 0; i < 32; i++) { - lpphy_set_rx_gain_by_index(dev, i); - lpphy_run_ddfs(dev, 1, 1, 5, 5, 0); - if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est))) - continue; - tmp = (iq_est.i_pwr + iq_est.q_pwr) / 1000; - if ((tmp > 4000) && (tmp < 10000)) { - index = i; - break; - } - } - lpphy_stop_ddfs(dev); - return index; -} - -/* Fixed-point division algorithm using only integer math. */ -static u32 lpphy_qdiv_roundup(u32 dividend, u32 divisor, u8 precision) -{ - u32 quotient, remainder; - - if (divisor == 0) - return 0; - - quotient = dividend / divisor; - remainder = dividend % divisor; - - while (precision > 0) { - quotient <<= 1; - if (remainder << 1 >= divisor) { - quotient++; - remainder = (remainder << 1) - divisor; - } - precision--; - } - - if (remainder << 1 >= divisor) - quotient++; - - return quotient; -} - -/* Read the TX power control mode from hardware. */ -static void lpphy_read_tx_pctl_mode_from_hardware(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - u16 ctl; - - ctl = b43_phy_read(dev, B43_LPPHY_TX_PWR_CTL_CMD); - switch (ctl & B43_LPPHY_TX_PWR_CTL_CMD_MODE) { - case B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF: - lpphy->txpctl_mode = B43_LPPHY_TXPCTL_OFF; - break; - case B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW: - lpphy->txpctl_mode = B43_LPPHY_TXPCTL_SW; - break; - case B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW: - lpphy->txpctl_mode = B43_LPPHY_TXPCTL_HW; - break; - default: - lpphy->txpctl_mode = B43_LPPHY_TXPCTL_UNKNOWN; - B43_WARN_ON(1); - break; - } -} - -/* Set the TX power control mode in hardware. */ -static void lpphy_write_tx_pctl_mode_to_hardware(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - u16 ctl; - - switch (lpphy->txpctl_mode) { - case B43_LPPHY_TXPCTL_OFF: - ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF; - break; - case B43_LPPHY_TXPCTL_HW: - ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW; - break; - case B43_LPPHY_TXPCTL_SW: - ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW; - break; - default: - ctl = 0; - B43_WARN_ON(1); - } - b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, - ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF, ctl); -} - -static void lpphy_set_tx_power_control(struct b43_wldev *dev, - enum b43_lpphy_txpctl_mode mode) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - enum b43_lpphy_txpctl_mode oldmode; - - lpphy_read_tx_pctl_mode_from_hardware(dev); - oldmode = lpphy->txpctl_mode; - if (oldmode == mode) - return; - lpphy->txpctl_mode = mode; - - if (oldmode == B43_LPPHY_TXPCTL_HW) { - //TODO Update TX Power NPT - //TODO Clear all TX Power offsets - } else { - if (mode == B43_LPPHY_TXPCTL_HW) { - //TODO Recalculate target TX power - b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, - 0xFF80, lpphy->tssi_idx); - b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, - 0x8FFF, ((u16)lpphy->tssi_npt << 16)); - //TODO Set "TSSI Transmit Count" variable to total transmitted frame count - lpphy_disable_tx_gain_override(dev); - lpphy->tx_pwr_idx_over = -1; - } - } - if (dev->phy.rev >= 2) { - if (mode == B43_LPPHY_TXPCTL_HW) - b43_phy_set(dev, B43_PHY_OFDM(0xD0), 0x2); - else - b43_phy_mask(dev, B43_PHY_OFDM(0xD0), 0xFFFD); - } - lpphy_write_tx_pctl_mode_to_hardware(dev); -} - -static int b43_lpphy_op_switch_channel(struct b43_wldev *dev, - unsigned int new_channel); - -static void lpphy_rev0_1_rc_calib(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - struct lpphy_iq_est iq_est; - struct lpphy_tx_gains tx_gains; - static const u32 ideal_pwr_table[21] = { - 0x10000, 0x10557, 0x10e2d, 0x113e0, 0x10f22, 0x0ff64, - 0x0eda2, 0x0e5d4, 0x0efd1, 0x0fbe8, 0x0b7b8, 0x04b35, - 0x01a5e, 0x00a0b, 0x00444, 0x001fd, 0x000ff, 0x00088, - 0x0004c, 0x0002c, 0x0001a, - }; - bool old_txg_ovr; - u8 old_bbmult; - u16 old_rf_ovr, old_rf_ovrval, old_afe_ovr, old_afe_ovrval, - old_rf2_ovr, old_rf2_ovrval, old_phy_ctl; - enum b43_lpphy_txpctl_mode old_txpctl; - u32 normal_pwr, ideal_pwr, mean_sq_pwr, tmp = 0, mean_sq_pwr_min = 0; - int loopback, i, j, inner_sum, err; - - memset(&iq_est, 0, sizeof(iq_est)); - - err = b43_lpphy_op_switch_channel(dev, 7); - if (err) { - b43dbg(dev->wl, - "RC calib: Failed to switch to channel 7, error = %d\n", - err); - } - old_txg_ovr = !!(b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40); - old_bbmult = lpphy_get_bb_mult(dev); - if (old_txg_ovr) - tx_gains = lpphy_get_tx_gains(dev); - old_rf_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_0); - old_rf_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_VAL_0); - old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR); - old_afe_ovrval = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVRVAL); - old_rf2_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2); - old_rf2_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2_VAL); - old_phy_ctl = b43_phy_read(dev, B43_LPPHY_LP_PHY_CTL); - lpphy_read_tx_pctl_mode_from_hardware(dev); - old_txpctl = lpphy->txpctl_mode; - - lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); - lpphy_disable_crs(dev, true); - loopback = lpphy_loopback(dev); - if (loopback == -1) - goto finish; - lpphy_set_rx_gain_by_index(dev, loopback); - b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFFBF, 0x40); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFF8, 0x1); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFC7, 0x8); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F, 0xC0); - for (i = 128; i <= 159; i++) { - b43_radio_write(dev, B2062_N_RXBB_CALIB2, i); - inner_sum = 0; - for (j = 5; j <= 25; j++) { - lpphy_run_ddfs(dev, 1, 1, j, j, 0); - if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est))) - goto finish; - mean_sq_pwr = iq_est.i_pwr + iq_est.q_pwr; - if (j == 5) - tmp = mean_sq_pwr; - ideal_pwr = ((ideal_pwr_table[j-5] >> 3) + 1) >> 1; - normal_pwr = lpphy_qdiv_roundup(mean_sq_pwr, tmp, 12); - mean_sq_pwr = ideal_pwr - normal_pwr; - mean_sq_pwr *= mean_sq_pwr; - inner_sum += mean_sq_pwr; - if ((i == 128) || (inner_sum < mean_sq_pwr_min)) { - lpphy->rc_cap = i; - mean_sq_pwr_min = inner_sum; - } - } - } - lpphy_stop_ddfs(dev); - -finish: - lpphy_restore_crs(dev, true); - b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, old_rf_ovrval); - b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, old_rf_ovr); - b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVRVAL, old_afe_ovrval); - b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, old_afe_ovr); - b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, old_rf2_ovrval); - b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, old_rf2_ovr); - b43_phy_write(dev, B43_LPPHY_LP_PHY_CTL, old_phy_ctl); - - lpphy_set_bb_mult(dev, old_bbmult); - if (old_txg_ovr) { - /* - * SPEC FIXME: The specs say "get_tx_gains" here, which is - * illogical. According to lwfinger, vendor driver v4.150.10.5 - * has a Set here, while v4.174.64.19 has a Get - regression in - * the vendor driver? This should be tested this once the code - * is testable. - */ - lpphy_set_tx_gains(dev, tx_gains); - } - lpphy_set_tx_power_control(dev, old_txpctl); - if (lpphy->rc_cap) - lpphy_set_rc_cap(dev); -} - -static void lpphy_rev2plus_rc_calib(struct b43_wldev *dev) -{ - struct ssb_bus *bus = dev->dev->sdev->bus; - u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; - u8 tmp = b43_radio_read(dev, B2063_RX_BB_SP8) & 0xFF; - int i; - - b43_radio_write(dev, B2063_RX_BB_SP8, 0x0); - b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); - b43_radio_mask(dev, B2063_PLL_SP1, 0xF7); - b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C); - b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x15); - b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x70); - b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x52); - b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1); - b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7D); - - for (i = 0; i < 10000; i++) { - if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2) - break; - msleep(1); - } - - if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)) - b43_radio_write(dev, B2063_RX_BB_SP8, tmp); - - tmp = b43_radio_read(dev, B2063_TX_BB_SP3) & 0xFF; - - b43_radio_write(dev, B2063_TX_BB_SP3, 0x0); - b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); - b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C); - b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x55); - b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x76); - - if (crystal_freq == 24000000) { - b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0xFC); - b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x0); - } else { - b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x13); - b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1); - } - - b43_radio_write(dev, B2063_PA_SP7, 0x7D); - - for (i = 0; i < 10000; i++) { - if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2) - break; - msleep(1); - } - - if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)) - b43_radio_write(dev, B2063_TX_BB_SP3, tmp); - - b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); -} - -static void lpphy_calibrate_rc(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - - if (dev->phy.rev >= 2) { - lpphy_rev2plus_rc_calib(dev); - } else if (!lpphy->rc_cap) { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - lpphy_rev0_1_rc_calib(dev); - } else { - lpphy_set_rc_cap(dev); - } -} - -static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) -{ - if (dev->phy.rev >= 2) - return; // rev2+ doesn't support antenna diversity - - if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1)) - return; - - b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP); - - b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2); - b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1); - - b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP); - - dev->phy.lp->antenna = antenna; -} - -static void lpphy_set_tx_iqcc(struct b43_wldev *dev, u16 a, u16 b) -{ - u16 tmp[2]; - - tmp[0] = a; - tmp[1] = b; - b43_lptab_write_bulk(dev, B43_LPTAB16(0, 80), 2, tmp); -} - -static void lpphy_set_tx_power_by_index(struct b43_wldev *dev, u8 index) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - struct lpphy_tx_gains gains; - u32 iq_comp, tx_gain, coeff, rf_power; - - lpphy->tx_pwr_idx_over = index; - lpphy_read_tx_pctl_mode_from_hardware(dev); - if (lpphy->txpctl_mode != B43_LPPHY_TXPCTL_OFF) - lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_SW); - if (dev->phy.rev >= 2) { - iq_comp = b43_lptab_read(dev, B43_LPTAB32(7, index + 320)); - tx_gain = b43_lptab_read(dev, B43_LPTAB32(7, index + 192)); - gains.pad = (tx_gain >> 16) & 0xFF; - gains.gm = tx_gain & 0xFF; - gains.pga = (tx_gain >> 8) & 0xFF; - gains.dac = (iq_comp >> 28) & 0xFF; - lpphy_set_tx_gains(dev, gains); - } else { - iq_comp = b43_lptab_read(dev, B43_LPTAB32(10, index + 320)); - tx_gain = b43_lptab_read(dev, B43_LPTAB32(10, index + 192)); - b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, - 0xF800, (tx_gain >> 4) & 0x7FFF); - lpphy_set_dac_gain(dev, tx_gain & 0x7); - lpphy_set_pa_gain(dev, (tx_gain >> 24) & 0x7F); - } - lpphy_set_bb_mult(dev, (iq_comp >> 20) & 0xFF); - lpphy_set_tx_iqcc(dev, (iq_comp >> 10) & 0x3FF, iq_comp & 0x3FF); - if (dev->phy.rev >= 2) { - coeff = b43_lptab_read(dev, B43_LPTAB32(7, index + 448)); - } else { - coeff = b43_lptab_read(dev, B43_LPTAB32(10, index + 448)); - } - b43_lptab_write(dev, B43_LPTAB16(0, 85), coeff & 0xFFFF); - if (dev->phy.rev >= 2) { - rf_power = b43_lptab_read(dev, B43_LPTAB32(7, index + 576)); - b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, - rf_power & 0xFFFF);//SPEC FIXME mask & set != 0 - } - lpphy_enable_tx_gain_override(dev); -} - -static void lpphy_btcoex_override(struct b43_wldev *dev) -{ - b43_write16(dev, B43_MMIO_BTCOEX_CTL, 0x3); - b43_write16(dev, B43_MMIO_BTCOEX_TXCTL, 0xFF); -} - -static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev, - bool blocked) -{ - //TODO check MAC control register - if (blocked) { - if (dev->phy.rev >= 2) { - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x83FF); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00); - b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0x80FF); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xDFFF); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0808); - } else { - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xE0FF); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFCFF); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0018); - } - } else { - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xE0FF); - if (dev->phy.rev >= 2) - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xF7F7); - else - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFFE7); - } -} - -/* This was previously called lpphy_japan_filter */ -static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter! - - if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific? - b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9); - if ((dev->phy.rev == 1) && (lpphy->rc_cap)) - lpphy_set_rc_cap(dev); - } else { - b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F); - } -} - -static void lpphy_set_tssi_mux(struct b43_wldev *dev, enum tssi_mux_mode mode) -{ - if (mode != TSSI_MUX_EXT) { - b43_radio_set(dev, B2063_PA_SP1, 0x2); - b43_phy_set(dev, B43_PHY_OFDM(0xF3), 0x1000); - b43_radio_write(dev, B2063_PA_CTL10, 0x51); - if (mode == TSSI_MUX_POSTPA) { - b43_radio_mask(dev, B2063_PA_SP1, 0xFFFE); - b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFC7); - } else { - b43_radio_maskset(dev, B2063_PA_SP1, 0xFFFE, 0x1); - b43_phy_maskset(dev, B43_LPPHY_AFE_CTL_OVRVAL, - 0xFFC7, 0x20); - } - } else { - B43_WARN_ON(1); - } -} - -static void lpphy_tx_pctl_init_hw(struct b43_wldev *dev) -{ - u16 tmp; - int i; - - //SPEC TODO Call LP PHY Clear TX Power offsets - for (i = 0; i < 64; i++) { - if (dev->phy.rev >= 2) - b43_lptab_write(dev, B43_LPTAB32(7, i + 1), i); - else - b43_lptab_write(dev, B43_LPTAB32(10, i + 1), i); - } - - b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xFF00, 0xFF); - b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0x8FFF, 0x5000); - b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0xFFC0, 0x1F); - if (dev->phy.rev < 2) { - b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xEFFF); - b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xDFFF, 0x2000); - } else { - b43_phy_mask(dev, B43_PHY_OFDM(0x103), 0xFFFE); - b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFFB, 0x4); - b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFEF, 0x10); - b43_radio_maskset(dev, B2063_IQ_CALIB_CTL2, 0xF3, 0x1); - lpphy_set_tssi_mux(dev, TSSI_MUX_POSTPA); - } - b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0x7FFF, 0x8000); - b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xFF); - b43_phy_write(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xA); - b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, - ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF, - B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF); - b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xF8FF); - b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, - ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF, - B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW); - - if (dev->phy.rev < 2) { - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF, 0x1000); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xEFFF); - } else { - lpphy_set_tx_power_by_index(dev, 0x7F); - } - - b43_dummy_transmission(dev, true, true); - - tmp = b43_phy_read(dev, B43_LPPHY_TX_PWR_CTL_STAT); - if (tmp & 0x8000) { - b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, - 0xFFC0, (tmp & 0xFF) - 32); - } - - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF); - - // (SPEC?) TODO Set "Target TX frequency" variable to 0 - // SPEC FIXME "Set BB Multiplier to 0xE000" impossible - bb_mult is u8! -} - -static void lpphy_tx_pctl_init_sw(struct b43_wldev *dev) -{ - struct lpphy_tx_gains gains; - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - gains.gm = 4; - gains.pad = 12; - gains.pga = 12; - gains.dac = 0; - } else { - gains.gm = 7; - gains.pad = 14; - gains.pga = 15; - gains.dac = 0; - } - lpphy_set_tx_gains(dev, gains); - lpphy_set_bb_mult(dev, 150); -} - -/* Initialize TX power control */ -static void lpphy_tx_pctl_init(struct b43_wldev *dev) -{ - if (0/*FIXME HWPCTL capable */) { - lpphy_tx_pctl_init_hw(dev); - } else { /* This device is only software TX power control capable. */ - lpphy_tx_pctl_init_sw(dev); - } -} - -static void lpphy_pr41573_workaround(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - u32 *saved_tab; - const unsigned int saved_tab_size = 256; - enum b43_lpphy_txpctl_mode txpctl_mode; - s8 tx_pwr_idx_over; - u16 tssi_npt, tssi_idx; - - saved_tab = kcalloc(saved_tab_size, sizeof(saved_tab[0]), GFP_KERNEL); - if (!saved_tab) { - b43err(dev->wl, "PR41573 failed. Out of memory!\n"); - return; - } - - lpphy_read_tx_pctl_mode_from_hardware(dev); - txpctl_mode = lpphy->txpctl_mode; - tx_pwr_idx_over = lpphy->tx_pwr_idx_over; - tssi_npt = lpphy->tssi_npt; - tssi_idx = lpphy->tssi_idx; - - if (dev->phy.rev < 2) { - b43_lptab_read_bulk(dev, B43_LPTAB32(10, 0x140), - saved_tab_size, saved_tab); - } else { - b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140), - saved_tab_size, saved_tab); - } - //FIXME PHY reset - lpphy_table_init(dev); //FIXME is table init needed? - lpphy_baseband_init(dev); - lpphy_tx_pctl_init(dev); - b43_lpphy_op_software_rfkill(dev, false); - lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); - if (dev->phy.rev < 2) { - b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0x140), - saved_tab_size, saved_tab); - } else { - b43_lptab_write_bulk(dev, B43_LPTAB32(7, 0x140), - saved_tab_size, saved_tab); - } - b43_write16(dev, B43_MMIO_CHANNEL, lpphy->channel); - lpphy->tssi_npt = tssi_npt; - lpphy->tssi_idx = tssi_idx; - lpphy_set_analog_filter(dev, lpphy->channel); - if (tx_pwr_idx_over != -1) - lpphy_set_tx_power_by_index(dev, tx_pwr_idx_over); - if (lpphy->rc_cap) - lpphy_set_rc_cap(dev); - b43_lpphy_op_set_rx_antenna(dev, lpphy->antenna); - lpphy_set_tx_power_control(dev, txpctl_mode); - kfree(saved_tab); -} - -struct lpphy_rx_iq_comp { u8 chan; s8 c1, c0; }; - -static const struct lpphy_rx_iq_comp lpphy_5354_iq_table[] = { - { .chan = 1, .c1 = -66, .c0 = 15, }, - { .chan = 2, .c1 = -66, .c0 = 15, }, - { .chan = 3, .c1 = -66, .c0 = 15, }, - { .chan = 4, .c1 = -66, .c0 = 15, }, - { .chan = 5, .c1 = -66, .c0 = 15, }, - { .chan = 6, .c1 = -66, .c0 = 15, }, - { .chan = 7, .c1 = -66, .c0 = 14, }, - { .chan = 8, .c1 = -66, .c0 = 14, }, - { .chan = 9, .c1 = -66, .c0 = 14, }, - { .chan = 10, .c1 = -66, .c0 = 14, }, - { .chan = 11, .c1 = -66, .c0 = 14, }, - { .chan = 12, .c1 = -66, .c0 = 13, }, - { .chan = 13, .c1 = -66, .c0 = 13, }, - { .chan = 14, .c1 = -66, .c0 = 13, }, -}; - -static const struct lpphy_rx_iq_comp lpphy_rev0_1_iq_table[] = { - { .chan = 1, .c1 = -64, .c0 = 13, }, - { .chan = 2, .c1 = -64, .c0 = 13, }, - { .chan = 3, .c1 = -64, .c0 = 13, }, - { .chan = 4, .c1 = -64, .c0 = 13, }, - { .chan = 5, .c1 = -64, .c0 = 12, }, - { .chan = 6, .c1 = -64, .c0 = 12, }, - { .chan = 7, .c1 = -64, .c0 = 12, }, - { .chan = 8, .c1 = -64, .c0 = 12, }, - { .chan = 9, .c1 = -64, .c0 = 12, }, - { .chan = 10, .c1 = -64, .c0 = 11, }, - { .chan = 11, .c1 = -64, .c0 = 11, }, - { .chan = 12, .c1 = -64, .c0 = 11, }, - { .chan = 13, .c1 = -64, .c0 = 11, }, - { .chan = 14, .c1 = -64, .c0 = 10, }, - { .chan = 34, .c1 = -62, .c0 = 24, }, - { .chan = 38, .c1 = -62, .c0 = 24, }, - { .chan = 42, .c1 = -62, .c0 = 24, }, - { .chan = 46, .c1 = -62, .c0 = 23, }, - { .chan = 36, .c1 = -62, .c0 = 24, }, - { .chan = 40, .c1 = -62, .c0 = 24, }, - { .chan = 44, .c1 = -62, .c0 = 23, }, - { .chan = 48, .c1 = -62, .c0 = 23, }, - { .chan = 52, .c1 = -62, .c0 = 23, }, - { .chan = 56, .c1 = -62, .c0 = 22, }, - { .chan = 60, .c1 = -62, .c0 = 22, }, - { .chan = 64, .c1 = -62, .c0 = 22, }, - { .chan = 100, .c1 = -62, .c0 = 16, }, - { .chan = 104, .c1 = -62, .c0 = 16, }, - { .chan = 108, .c1 = -62, .c0 = 15, }, - { .chan = 112, .c1 = -62, .c0 = 14, }, - { .chan = 116, .c1 = -62, .c0 = 14, }, - { .chan = 120, .c1 = -62, .c0 = 13, }, - { .chan = 124, .c1 = -62, .c0 = 12, }, - { .chan = 128, .c1 = -62, .c0 = 12, }, - { .chan = 132, .c1 = -62, .c0 = 12, }, - { .chan = 136, .c1 = -62, .c0 = 11, }, - { .chan = 140, .c1 = -62, .c0 = 10, }, - { .chan = 149, .c1 = -61, .c0 = 9, }, - { .chan = 153, .c1 = -61, .c0 = 9, }, - { .chan = 157, .c1 = -61, .c0 = 9, }, - { .chan = 161, .c1 = -61, .c0 = 8, }, - { .chan = 165, .c1 = -61, .c0 = 8, }, - { .chan = 184, .c1 = -62, .c0 = 25, }, - { .chan = 188, .c1 = -62, .c0 = 25, }, - { .chan = 192, .c1 = -62, .c0 = 25, }, - { .chan = 196, .c1 = -62, .c0 = 25, }, - { .chan = 200, .c1 = -62, .c0 = 25, }, - { .chan = 204, .c1 = -62, .c0 = 25, }, - { .chan = 208, .c1 = -62, .c0 = 25, }, - { .chan = 212, .c1 = -62, .c0 = 25, }, - { .chan = 216, .c1 = -62, .c0 = 26, }, -}; - -static const struct lpphy_rx_iq_comp lpphy_rev2plus_iq_comp = { - .chan = 0, - .c1 = -64, - .c0 = 0, -}; - -static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples) -{ - struct lpphy_iq_est iq_est; - u16 c0, c1; - int prod, ipwr, qpwr, prod_msb, q_msb, tmp1, tmp2, tmp3, tmp4, ret; - - c1 = b43_phy_read(dev, B43_LPPHY_RX_COMP_COEFF_S); - c0 = c1 >> 8; - c1 |= 0xFF; - - b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, 0x00C0); - b43_phy_mask(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF); - - ret = lpphy_rx_iq_est(dev, samples, 32, &iq_est); - if (!ret) - goto out; - - prod = iq_est.iq_prod; - ipwr = iq_est.i_pwr; - qpwr = iq_est.q_pwr; - - if (ipwr + qpwr < 2) { - ret = 0; - goto out; - } - - prod_msb = fls(abs(prod)); - q_msb = fls(abs(qpwr)); - tmp1 = prod_msb - 20; - - if (tmp1 >= 0) { - tmp3 = ((prod << (30 - prod_msb)) + (ipwr >> (1 + tmp1))) / - (ipwr >> tmp1); - } else { - tmp3 = ((prod << (30 - prod_msb)) + (ipwr << (-1 - tmp1))) / - (ipwr << -tmp1); - } - - tmp2 = q_msb - 11; - - if (tmp2 >= 0) - tmp4 = (qpwr << (31 - q_msb)) / (ipwr >> tmp2); - else - tmp4 = (qpwr << (31 - q_msb)) / (ipwr << -tmp2); - - tmp4 -= tmp3 * tmp3; - tmp4 = -int_sqrt(tmp4); - - c0 = tmp3 >> 3; - c1 = tmp4 >> 4; - -out: - b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, c1); - b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF, c0 << 8); - return ret; -} - -static void lpphy_run_samples(struct b43_wldev *dev, u16 samples, u16 loops, - u16 wait) -{ - b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, - 0xFFC0, samples - 1); - if (loops != 0xFFFF) - loops--; - b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000, loops); - b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, 0x3F, wait << 6); - b43_phy_set(dev, B43_LPPHY_A_PHY_CTL_ADDR, 0x1); -} - -//SPEC FIXME what does a negative freq mean? -static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - u16 buf[64]; - int i, samples = 0, angle = 0; - int rotation = (((36 * freq) / 20) << 16) / 100; - struct b43_c32 sample; - - lpphy->tx_tone_freq = freq; - - if (freq) { - /* Find i for which abs(freq) integrally divides 20000 * i */ - for (i = 1; samples * abs(freq) != 20000 * i; i++) { - samples = (20000 * i) / abs(freq); - if(B43_WARN_ON(samples > 63)) - return; - } - } else { - samples = 2; - } - - for (i = 0; i < samples; i++) { - sample = b43_cordic(angle); - angle += rotation; - buf[i] = CORDIC_CONVERT((sample.i * max) & 0xFF) << 8; - buf[i] |= CORDIC_CONVERT((sample.q * max) & 0xFF); - } - - b43_lptab_write_bulk(dev, B43_LPTAB16(5, 0), samples, buf); - - lpphy_run_samples(dev, samples, 0xFFFF, 0); -} - -static void lpphy_stop_tx_tone(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - int i; - - lpphy->tx_tone_freq = 0; - - b43_phy_mask(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000); - for (i = 0; i < 31; i++) { - if (!(b43_phy_read(dev, B43_LPPHY_A_PHY_CTL_ADDR) & 0x1)) - break; - udelay(100); - } -} - - -static void lpphy_papd_cal(struct b43_wldev *dev, struct lpphy_tx_gains gains, - int mode, bool useindex, u8 index) -{ - //TODO -} - -static void lpphy_papd_cal_txpwr(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - struct lpphy_tx_gains gains, oldgains; - int old_txpctl, old_afe_ovr, old_rf, old_bbmult; - - lpphy_read_tx_pctl_mode_from_hardware(dev); - old_txpctl = lpphy->txpctl_mode; - old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40; - if (old_afe_ovr) - oldgains = lpphy_get_tx_gains(dev); - old_rf = b43_phy_read(dev, B43_LPPHY_RF_PWR_OVERRIDE) & 0xFF; - old_bbmult = lpphy_get_bb_mult(dev); - - lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); - - if (dev->dev->chip_id == 0x4325 && dev->dev->chip_rev == 0) - lpphy_papd_cal(dev, gains, 0, 1, 30); - else - lpphy_papd_cal(dev, gains, 0, 1, 65); - - if (old_afe_ovr) - lpphy_set_tx_gains(dev, oldgains); - lpphy_set_bb_mult(dev, old_bbmult); - lpphy_set_tx_power_control(dev, old_txpctl); - b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, old_rf); -} - -static int lpphy_rx_iq_cal(struct b43_wldev *dev, bool noise, bool tx, - bool rx, bool pa, struct lpphy_tx_gains *gains) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - const struct lpphy_rx_iq_comp *iqcomp = NULL; - struct lpphy_tx_gains nogains, oldgains; - u16 tmp; - int i, ret; - - memset(&nogains, 0, sizeof(nogains)); - memset(&oldgains, 0, sizeof(oldgains)); - - if (dev->dev->chip_id == 0x5354) { - for (i = 0; i < ARRAY_SIZE(lpphy_5354_iq_table); i++) { - if (lpphy_5354_iq_table[i].chan == lpphy->channel) { - iqcomp = &lpphy_5354_iq_table[i]; - } - } - } else if (dev->phy.rev >= 2) { - iqcomp = &lpphy_rev2plus_iq_comp; - } else { - for (i = 0; i < ARRAY_SIZE(lpphy_rev0_1_iq_table); i++) { - if (lpphy_rev0_1_iq_table[i].chan == lpphy->channel) { - iqcomp = &lpphy_rev0_1_iq_table[i]; - } - } - } - - if (B43_WARN_ON(!iqcomp)) - return 0; - - b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, iqcomp->c1); - b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, - 0x00FF, iqcomp->c0 << 8); - - if (noise) { - tx = true; - rx = false; - pa = false; - } - - lpphy_set_trsw_over(dev, tx, rx); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, - 0xFFF7, pa << 3); - } else { - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20); - b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, - 0xFFDF, pa << 5); - } - - tmp = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40; - - if (noise) - lpphy_set_rx_gain(dev, 0x2D5D); - else { - if (tmp) - oldgains = lpphy_get_tx_gains(dev); - if (!gains) - gains = &nogains; - lpphy_set_tx_gains(dev, *gains); - } - - b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE); - b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800); - b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800); - lpphy_set_deaf(dev, false); - if (noise) - ret = lpphy_calc_rx_iq_comp(dev, 0xFFF0); - else { - lpphy_start_tx_tone(dev, 4000, 100); - ret = lpphy_calc_rx_iq_comp(dev, 0x4000); - lpphy_stop_tx_tone(dev); - } - lpphy_clear_deaf(dev, false); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFC); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFF7); - b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFDF); - if (!noise) { - if (tmp) - lpphy_set_tx_gains(dev, oldgains); - else - lpphy_disable_tx_gain_override(dev); - } - lpphy_disable_rx_gain_override(dev); - b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE); - b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xF7FF); - return ret; -} - -static void lpphy_calibration(struct b43_wldev *dev) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - enum b43_lpphy_txpctl_mode saved_pctl_mode; - bool full_cal = false; - - if (lpphy->full_calib_chan != lpphy->channel) { - full_cal = true; - lpphy->full_calib_chan = lpphy->channel; - } - - b43_mac_suspend(dev); - - lpphy_btcoex_override(dev); - if (dev->phy.rev >= 2) - lpphy_save_dig_flt_state(dev); - lpphy_read_tx_pctl_mode_from_hardware(dev); - saved_pctl_mode = lpphy->txpctl_mode; - lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); - //TODO Perform transmit power table I/Q LO calibration - if ((dev->phy.rev == 0) && (saved_pctl_mode != B43_LPPHY_TXPCTL_OFF)) - lpphy_pr41573_workaround(dev); - if ((dev->phy.rev >= 2) && full_cal) { - lpphy_papd_cal_txpwr(dev); - } - lpphy_set_tx_power_control(dev, saved_pctl_mode); - if (dev->phy.rev >= 2) - lpphy_restore_dig_flt_state(dev); - lpphy_rx_iq_cal(dev, true, true, false, false, NULL); - - b43_mac_enable(dev); -} - -static void b43_lpphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, - u16 set) -{ - b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); - b43_write16(dev, B43_MMIO_PHY_DATA, - (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); -} - -static u16 b43_lpphy_op_radio_read(struct b43_wldev *dev, u16 reg) -{ - /* Register 1 is a 32-bit register. */ - B43_WARN_ON(reg == 1); - /* LP-PHY needs a special bit set for read access */ - if (dev->phy.rev < 2) { - if (reg != 0x4001) - reg |= 0x100; - } else - reg |= 0x200; - - b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); - return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); -} - -static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - /* Register 1 is a 32-bit register. */ - B43_WARN_ON(reg == 1); - - b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); - b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); -} - -struct b206x_channel { - u8 channel; - u16 freq; - u8 data[12]; -}; - -static const struct b206x_channel b2062_chantbl[] = { - { .channel = 1, .freq = 2412, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 2, .freq = 2417, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 3, .freq = 2422, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 4, .freq = 2427, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 5, .freq = 2432, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 6, .freq = 2437, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 7, .freq = 2442, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 8, .freq = 2447, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 9, .freq = 2452, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 10, .freq = 2457, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 11, .freq = 2462, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 12, .freq = 2467, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 13, .freq = 2472, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 14, .freq = 2484, .data[0] = 0xFF, .data[1] = 0xFF, - .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, - .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, - { .channel = 34, .freq = 5170, .data[0] = 0x00, .data[1] = 0x22, - .data[2] = 0x20, .data[3] = 0x84, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 38, .freq = 5190, .data[0] = 0x00, .data[1] = 0x11, - .data[2] = 0x10, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 42, .freq = 5210, .data[0] = 0x00, .data[1] = 0x11, - .data[2] = 0x10, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 46, .freq = 5230, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 36, .freq = 5180, .data[0] = 0x00, .data[1] = 0x11, - .data[2] = 0x20, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 40, .freq = 5200, .data[0] = 0x00, .data[1] = 0x11, - .data[2] = 0x10, .data[3] = 0x84, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 44, .freq = 5220, .data[0] = 0x00, .data[1] = 0x11, - .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 48, .freq = 5240, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 52, .freq = 5260, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 56, .freq = 5280, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 60, .freq = 5300, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x63, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 64, .freq = 5320, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x62, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 100, .freq = 5500, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x30, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 104, .freq = 5520, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 108, .freq = 5540, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 112, .freq = 5560, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 116, .freq = 5580, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x10, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 120, .freq = 5600, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 124, .freq = 5620, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 128, .freq = 5640, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 132, .freq = 5660, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 136, .freq = 5680, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 140, .freq = 5700, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 149, .freq = 5745, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 153, .freq = 5765, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 157, .freq = 5785, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 161, .freq = 5805, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 165, .freq = 5825, .data[0] = 0x00, .data[1] = 0x00, - .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 184, .freq = 4920, .data[0] = 0x55, .data[1] = 0x77, - .data[2] = 0x90, .data[3] = 0xF7, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, - { .channel = 188, .freq = 4940, .data[0] = 0x44, .data[1] = 0x77, - .data[2] = 0x80, .data[3] = 0xE7, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, - { .channel = 192, .freq = 4960, .data[0] = 0x44, .data[1] = 0x66, - .data[2] = 0x80, .data[3] = 0xE7, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, - { .channel = 196, .freq = 4980, .data[0] = 0x33, .data[1] = 0x66, - .data[2] = 0x70, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, - { .channel = 200, .freq = 5000, .data[0] = 0x22, .data[1] = 0x55, - .data[2] = 0x60, .data[3] = 0xD7, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, - { .channel = 204, .freq = 5020, .data[0] = 0x22, .data[1] = 0x55, - .data[2] = 0x60, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, - { .channel = 208, .freq = 5040, .data[0] = 0x22, .data[1] = 0x44, - .data[2] = 0x50, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, - { .channel = 212, .freq = 5060, .data[0] = 0x11, .data[1] = 0x44, - .data[2] = 0x50, .data[3] = 0xA5, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, - { .channel = 216, .freq = 5080, .data[0] = 0x00, .data[1] = 0x44, - .data[2] = 0x40, .data[3] = 0xB6, .data[4] = 0x3C, .data[5] = 0x77, - .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, -}; - -static const struct b206x_channel b2063_chantbl[] = { - { .channel = 1, .freq = 2412, .data[0] = 0x6F, .data[1] = 0x3C, - .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 2, .freq = 2417, .data[0] = 0x6F, .data[1] = 0x3C, - .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 3, .freq = 2422, .data[0] = 0x6F, .data[1] = 0x3C, - .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 4, .freq = 2427, .data[0] = 0x6F, .data[1] = 0x2C, - .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 5, .freq = 2432, .data[0] = 0x6F, .data[1] = 0x2C, - .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 6, .freq = 2437, .data[0] = 0x6F, .data[1] = 0x2C, - .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 7, .freq = 2442, .data[0] = 0x6F, .data[1] = 0x2C, - .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 8, .freq = 2447, .data[0] = 0x6F, .data[1] = 0x2C, - .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 9, .freq = 2452, .data[0] = 0x6F, .data[1] = 0x1C, - .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 10, .freq = 2457, .data[0] = 0x6F, .data[1] = 0x1C, - .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 11, .freq = 2462, .data[0] = 0x6E, .data[1] = 0x1C, - .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 12, .freq = 2467, .data[0] = 0x6E, .data[1] = 0x1C, - .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 13, .freq = 2472, .data[0] = 0x6E, .data[1] = 0x1C, - .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 14, .freq = 2484, .data[0] = 0x6E, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, - .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x80, .data[11] = 0x70, }, - { .channel = 34, .freq = 5170, .data[0] = 0x6A, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x02, .data[5] = 0x05, - .data[6] = 0x0D, .data[7] = 0x0D, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x20, .data[11] = 0x00, }, - { .channel = 36, .freq = 5180, .data[0] = 0x6A, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x05, - .data[6] = 0x0D, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x20, .data[11] = 0x00, }, - { .channel = 38, .freq = 5190, .data[0] = 0x6A, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, - .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80, - .data[10] = 0x20, .data[11] = 0x00, }, - { .channel = 40, .freq = 5200, .data[0] = 0x69, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, - .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70, - .data[10] = 0x20, .data[11] = 0x00, }, - { .channel = 42, .freq = 5210, .data[0] = 0x69, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, - .data[6] = 0x0B, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70, - .data[10] = 0x20, .data[11] = 0x00, }, - { .channel = 44, .freq = 5220, .data[0] = 0x69, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x04, - .data[6] = 0x0B, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60, - .data[10] = 0x20, .data[11] = 0x00, }, - { .channel = 46, .freq = 5230, .data[0] = 0x69, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03, - .data[6] = 0x0A, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60, - .data[10] = 0x20, .data[11] = 0x00, }, - { .channel = 48, .freq = 5240, .data[0] = 0x69, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03, - .data[6] = 0x0A, .data[7] = 0x0A, .data[8] = 0x77, .data[9] = 0x60, - .data[10] = 0x20, .data[11] = 0x00, }, - { .channel = 52, .freq = 5260, .data[0] = 0x68, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x02, - .data[6] = 0x09, .data[7] = 0x09, .data[8] = 0x77, .data[9] = 0x60, - .data[10] = 0x20, .data[11] = 0x00, }, - { .channel = 56, .freq = 5280, .data[0] = 0x68, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01, - .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, - .data[10] = 0x10, .data[11] = 0x00, }, - { .channel = 60, .freq = 5300, .data[0] = 0x68, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01, - .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, - .data[10] = 0x10, .data[11] = 0x00, }, - { .channel = 64, .freq = 5320, .data[0] = 0x67, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, - .data[10] = 0x10, .data[11] = 0x00, }, - { .channel = 100, .freq = 5500, .data[0] = 0x64, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x02, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 104, .freq = 5520, .data[0] = 0x64, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x01, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 108, .freq = 5540, .data[0] = 0x63, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x01, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 112, .freq = 5560, .data[0] = 0x63, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 116, .freq = 5580, .data[0] = 0x62, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 120, .freq = 5600, .data[0] = 0x62, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 124, .freq = 5620, .data[0] = 0x62, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 128, .freq = 5640, .data[0] = 0x61, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 132, .freq = 5660, .data[0] = 0x61, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 136, .freq = 5680, .data[0] = 0x61, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 140, .freq = 5700, .data[0] = 0x60, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 149, .freq = 5745, .data[0] = 0x60, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 153, .freq = 5765, .data[0] = 0x60, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 157, .freq = 5785, .data[0] = 0x60, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 161, .freq = 5805, .data[0] = 0x60, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 165, .freq = 5825, .data[0] = 0x60, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, - .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, - .data[10] = 0x00, .data[11] = 0x00, }, - { .channel = 184, .freq = 4920, .data[0] = 0x6E, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0E, - .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xC0, - .data[10] = 0x50, .data[11] = 0x00, }, - { .channel = 188, .freq = 4940, .data[0] = 0x6E, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0D, - .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0, - .data[10] = 0x50, .data[11] = 0x00, }, - { .channel = 192, .freq = 4960, .data[0] = 0x6E, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C, - .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0, - .data[10] = 0x50, .data[11] = 0x00, }, - { .channel = 196, .freq = 4980, .data[0] = 0x6D, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C, - .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, - .data[10] = 0x40, .data[11] = 0x00, }, - { .channel = 200, .freq = 5000, .data[0] = 0x6D, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0B, - .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, - .data[10] = 0x40, .data[11] = 0x00, }, - { .channel = 204, .freq = 5020, .data[0] = 0x6D, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0A, - .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, - .data[10] = 0x40, .data[11] = 0x00, }, - { .channel = 208, .freq = 5040, .data[0] = 0x6C, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x07, .data[5] = 0x09, - .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, - .data[10] = 0x40, .data[11] = 0x00, }, - { .channel = 212, .freq = 5060, .data[0] = 0x6C, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x06, .data[5] = 0x08, - .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, - .data[10] = 0x40, .data[11] = 0x00, }, - { .channel = 216, .freq = 5080, .data[0] = 0x6C, .data[1] = 0x0C, - .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x05, .data[5] = 0x08, - .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, - .data[10] = 0x40, .data[11] = 0x00, }, -}; - -static void lpphy_b2062_reset_pll_bias(struct b43_wldev *dev) -{ - b43_radio_write(dev, B2062_S_RFPLL_CTL2, 0xFF); - udelay(20); - if (dev->dev->chip_id == 0x5354) { - b43_radio_write(dev, B2062_N_COMM1, 4); - b43_radio_write(dev, B2062_S_RFPLL_CTL2, 4); - } else { - b43_radio_write(dev, B2062_S_RFPLL_CTL2, 0); - } - udelay(5); -} - -static void lpphy_b2062_vco_calib(struct b43_wldev *dev) -{ - b43_radio_write(dev, B2062_S_RFPLL_CTL21, 0x42); - b43_radio_write(dev, B2062_S_RFPLL_CTL21, 0x62); - udelay(200); -} - -static int lpphy_b2062_tune(struct b43_wldev *dev, - unsigned int channel) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - struct ssb_bus *bus = dev->dev->sdev->bus; - const struct b206x_channel *chandata = NULL; - u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; - u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9; - int i, err = 0; - - for (i = 0; i < ARRAY_SIZE(b2062_chantbl); i++) { - if (b2062_chantbl[i].channel == channel) { - chandata = &b2062_chantbl[i]; - break; - } - } - - if (B43_WARN_ON(!chandata)) - return -EINVAL; - - b43_radio_set(dev, B2062_S_RFPLL_CTL14, 0x04); - b43_radio_write(dev, B2062_N_LGENA_TUNE0, chandata->data[0]); - b43_radio_write(dev, B2062_N_LGENA_TUNE2, chandata->data[1]); - b43_radio_write(dev, B2062_N_LGENA_TUNE3, chandata->data[2]); - b43_radio_write(dev, B2062_N_TX_TUNE, chandata->data[3]); - b43_radio_write(dev, B2062_S_LGENG_CTL1, chandata->data[4]); - b43_radio_write(dev, B2062_N_LGENA_CTL5, chandata->data[5]); - b43_radio_write(dev, B2062_N_LGENA_CTL6, chandata->data[6]); - b43_radio_write(dev, B2062_N_TX_PGA, chandata->data[7]); - b43_radio_write(dev, B2062_N_TX_PAD, chandata->data[8]); - - tmp1 = crystal_freq / 1000; - tmp2 = lpphy->pdiv * 1000; - b43_radio_write(dev, B2062_S_RFPLL_CTL33, 0xCC); - b43_radio_write(dev, B2062_S_RFPLL_CTL34, 0x07); - lpphy_b2062_reset_pll_bias(dev); - tmp3 = tmp2 * channel2freq_lp(channel); - if (channel2freq_lp(channel) < 4000) - tmp3 *= 2; - tmp4 = 48 * tmp1; - tmp6 = tmp3 / tmp4; - tmp7 = tmp3 % tmp4; - b43_radio_write(dev, B2062_S_RFPLL_CTL26, tmp6); - tmp5 = tmp7 * 0x100; - tmp6 = tmp5 / tmp4; - tmp7 = tmp5 % tmp4; - b43_radio_write(dev, B2062_S_RFPLL_CTL27, tmp6); - tmp5 = tmp7 * 0x100; - tmp6 = tmp5 / tmp4; - tmp7 = tmp5 % tmp4; - b43_radio_write(dev, B2062_S_RFPLL_CTL28, tmp6); - tmp5 = tmp7 * 0x100; - tmp6 = tmp5 / tmp4; - tmp7 = tmp5 % tmp4; - b43_radio_write(dev, B2062_S_RFPLL_CTL29, tmp6 + ((2 * tmp7) / tmp4)); - tmp8 = b43_radio_read(dev, B2062_S_RFPLL_CTL19); - tmp9 = ((2 * tmp3 * (tmp8 + 1)) + (3 * tmp1)) / (6 * tmp1); - b43_radio_write(dev, B2062_S_RFPLL_CTL23, (tmp9 >> 8) + 16); - b43_radio_write(dev, B2062_S_RFPLL_CTL24, tmp9 & 0xFF); - - lpphy_b2062_vco_calib(dev); - if (b43_radio_read(dev, B2062_S_RFPLL_CTL3) & 0x10) { - b43_radio_write(dev, B2062_S_RFPLL_CTL33, 0xFC); - b43_radio_write(dev, B2062_S_RFPLL_CTL34, 0); - lpphy_b2062_reset_pll_bias(dev); - lpphy_b2062_vco_calib(dev); - if (b43_radio_read(dev, B2062_S_RFPLL_CTL3) & 0x10) - err = -EIO; - } - - b43_radio_mask(dev, B2062_S_RFPLL_CTL14, ~0x04); - return err; -} - -static void lpphy_b2063_vco_calib(struct b43_wldev *dev) -{ - u16 tmp; - - b43_radio_mask(dev, B2063_PLL_SP1, ~0x40); - tmp = b43_radio_read(dev, B2063_PLL_JTAG_CALNRST) & 0xF8; - b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp); - udelay(1); - b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x4); - udelay(1); - b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x6); - udelay(1); - b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x7); - udelay(300); - b43_radio_set(dev, B2063_PLL_SP1, 0x40); -} - -static int lpphy_b2063_tune(struct b43_wldev *dev, - unsigned int channel) -{ - struct ssb_bus *bus = dev->dev->sdev->bus; - - static const struct b206x_channel *chandata = NULL; - u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; - u32 freqref, vco_freq, val1, val2, val3, timeout, timeoutref, count; - u16 old_comm15, scale; - u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; - int i, div = (crystal_freq <= 26000000 ? 1 : 2); - - for (i = 0; i < ARRAY_SIZE(b2063_chantbl); i++) { - if (b2063_chantbl[i].channel == channel) { - chandata = &b2063_chantbl[i]; - break; - } - } - - if (B43_WARN_ON(!chandata)) - return -EINVAL; - - b43_radio_write(dev, B2063_LOGEN_VCOBUF1, chandata->data[0]); - b43_radio_write(dev, B2063_LOGEN_MIXER2, chandata->data[1]); - b43_radio_write(dev, B2063_LOGEN_BUF2, chandata->data[2]); - b43_radio_write(dev, B2063_LOGEN_RCCR1, chandata->data[3]); - b43_radio_write(dev, B2063_A_RX_1ST3, chandata->data[4]); - b43_radio_write(dev, B2063_A_RX_2ND1, chandata->data[5]); - b43_radio_write(dev, B2063_A_RX_2ND4, chandata->data[6]); - b43_radio_write(dev, B2063_A_RX_2ND7, chandata->data[7]); - b43_radio_write(dev, B2063_A_RX_PS6, chandata->data[8]); - b43_radio_write(dev, B2063_TX_RF_CTL2, chandata->data[9]); - b43_radio_write(dev, B2063_TX_RF_CTL5, chandata->data[10]); - b43_radio_write(dev, B2063_PA_CTL11, chandata->data[11]); - - old_comm15 = b43_radio_read(dev, B2063_COMM15); - b43_radio_set(dev, B2063_COMM15, 0x1E); - - if (chandata->freq > 4000) /* spec says 2484, but 4000 is safer */ - vco_freq = chandata->freq << 1; - else - vco_freq = chandata->freq << 2; - - freqref = crystal_freq * 3; - val1 = lpphy_qdiv_roundup(crystal_freq, 1000000, 16); - val2 = lpphy_qdiv_roundup(crystal_freq, 1000000 * div, 16); - val3 = lpphy_qdiv_roundup(vco_freq, 3, 16); - timeout = ((((8 * crystal_freq) / (div * 5000000)) + 1) >> 1) - 1; - b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB3, 0x2); - b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB6, - 0xFFF8, timeout >> 2); - b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7, - 0xFF9F,timeout << 5); - - timeoutref = ((((8 * crystal_freq) / (div * (timeout + 1))) + - 999999) / 1000000) + 1; - b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB5, timeoutref); - - count = lpphy_qdiv_roundup(val3, val2 + 16, 16); - count *= (timeout + 1) * (timeoutref + 1); - count--; - b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7, - 0xF0, count >> 8); - b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB8, count & 0xFF); - - tmp1 = ((val3 * 62500) / freqref) << 4; - tmp2 = ((val3 * 62500) % freqref) << 4; - while (tmp2 >= freqref) { - tmp1++; - tmp2 -= freqref; - } - b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG1, 0xFFE0, tmp1 >> 4); - b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFE0F, tmp1 << 4); - b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFFF0, tmp1 >> 16); - b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG3, (tmp2 >> 8) & 0xFF); - b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG4, tmp2 & 0xFF); - - b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF1, 0xB9); - b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF2, 0x88); - b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF3, 0x28); - b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF4, 0x63); - - tmp3 = ((41 * (val3 - 3000)) /1200) + 27; - tmp4 = lpphy_qdiv_roundup(132000 * tmp1, 8451, 16); - - if ((tmp4 + tmp3 - 1) / tmp3 > 60) { - scale = 1; - tmp5 = ((tmp4 + tmp3) / (tmp3 << 1)) - 8; - } else { - scale = 0; - tmp5 = ((tmp4 + (tmp3 >> 1)) / tmp3) - 8; - } - b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFC0, tmp5); - b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFBF, scale << 6); - - tmp6 = lpphy_qdiv_roundup(100 * val1, val3, 16); - tmp6 *= (tmp5 * 8) * (scale + 1); - if (tmp6 > 150) - tmp6 = 0; - - b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFE0, tmp6); - b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFDF, scale << 5); - - b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFFFB, 0x4); - if (crystal_freq > 26000000) - b43_radio_set(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0x2); - else - b43_radio_mask(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFD); - - if (val1 == 45) - b43_radio_set(dev, B2063_PLL_JTAG_PLL_VCO1, 0x2); - else - b43_radio_mask(dev, B2063_PLL_JTAG_PLL_VCO1, 0xFD); - - b43_radio_set(dev, B2063_PLL_SP2, 0x3); - udelay(1); - b43_radio_mask(dev, B2063_PLL_SP2, 0xFFFC); - lpphy_b2063_vco_calib(dev); - b43_radio_write(dev, B2063_COMM15, old_comm15); - - return 0; -} - -static int b43_lpphy_op_switch_channel(struct b43_wldev *dev, - unsigned int new_channel) -{ - struct b43_phy_lp *lpphy = dev->phy.lp; - int err; - - if (dev->phy.radio_ver == 0x2063) { - err = lpphy_b2063_tune(dev, new_channel); - if (err) - return err; - } else { - err = lpphy_b2062_tune(dev, new_channel); - if (err) - return err; - lpphy_set_analog_filter(dev, new_channel); - lpphy_adjust_gain_table(dev, channel2freq_lp(new_channel)); - } - - lpphy->channel = new_channel; - b43_write16(dev, B43_MMIO_CHANNEL, new_channel); - - return 0; -} - -static int b43_lpphy_op_init(struct b43_wldev *dev) -{ - int err; - - if (dev->dev->bus_type != B43_BUS_SSB) { - b43err(dev->wl, "LP-PHY is supported only on SSB!\n"); - return -EOPNOTSUPP; - } - - lpphy_read_band_sprom(dev); //FIXME should this be in prepare_structs? - lpphy_baseband_init(dev); - lpphy_radio_init(dev); - lpphy_calibrate_rc(dev); - err = b43_lpphy_op_switch_channel(dev, 7); - if (err) { - b43dbg(dev->wl, "Switch to channel 7 failed, error = %d.\n", - err); - } - lpphy_tx_pctl_init(dev); - lpphy_calibration(dev); - //TODO ACI init - - return 0; -} - -static void b43_lpphy_op_adjust_txpower(struct b43_wldev *dev) -{ - //TODO -} - -static enum b43_txpwr_result b43_lpphy_op_recalc_txpower(struct b43_wldev *dev, - bool ignore_tssi) -{ - //TODO - return B43_TXPWR_RES_DONE; -} - -static void b43_lpphy_op_switch_analog(struct b43_wldev *dev, bool on) -{ - if (on) { - b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xfff8); - } else { - b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0x0007); - b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x0007); - } -} - -static void b43_lpphy_op_pwork_15sec(struct b43_wldev *dev) -{ - //TODO -} - -const struct b43_phy_operations b43_phyops_lp = { - .allocate = b43_lpphy_op_allocate, - .free = b43_lpphy_op_free, - .prepare_structs = b43_lpphy_op_prepare_structs, - .init = b43_lpphy_op_init, - .phy_maskset = b43_lpphy_op_maskset, - .radio_read = b43_lpphy_op_radio_read, - .radio_write = b43_lpphy_op_radio_write, - .software_rfkill = b43_lpphy_op_software_rfkill, - .switch_analog = b43_lpphy_op_switch_analog, - .switch_channel = b43_lpphy_op_switch_channel, - .get_default_chan = b43_lpphy_op_get_default_chan, - .set_rx_antenna = b43_lpphy_op_set_rx_antenna, - .recalc_txpower = b43_lpphy_op_recalc_txpower, - .adjust_txpower = b43_lpphy_op_adjust_txpower, - .pwork_15sec = b43_lpphy_op_pwork_15sec, - .pwork_60sec = lpphy_calibration, -}; diff --git a/drivers/net/wireless/b43/phy_lp.h b/drivers/net/wireless/b43/phy_lp.h deleted file mode 100644 index 62737f700cbc..000000000000 --- a/drivers/net/wireless/b43/phy_lp.h +++ /dev/null @@ -1,912 +0,0 @@ -#ifndef LINUX_B43_PHY_LP_H_ -#define LINUX_B43_PHY_LP_H_ - -/* Definitions for the LP-PHY */ - - -/* The CCK PHY register range. */ -#define B43_LPPHY_B_VERSION B43_PHY_CCK(0x00) /* B PHY version */ -#define B43_LPPHY_B_BBCONFIG B43_PHY_CCK(0x01) /* B PHY BBConfig */ -#define B43_LPPHY_B_RX_STAT0 B43_PHY_CCK(0x04) /* B PHY RX Status0 */ -#define B43_LPPHY_B_RX_STAT1 B43_PHY_CCK(0x05) /* B PHY RX Status1 */ -#define B43_LPPHY_B_CRS_THRESH B43_PHY_CCK(0x06) /* B PHY CRS Thresh */ -#define B43_LPPHY_B_TXERROR B43_PHY_CCK(0x07) /* B PHY TxError */ -#define B43_LPPHY_B_CHANNEL B43_PHY_CCK(0x08) /* B PHY Channel */ -#define B43_LPPHY_B_WORKAROUND B43_PHY_CCK(0x09) /* B PHY workaround */ -#define B43_LPPHY_B_TEST B43_PHY_CCK(0x0A) /* B PHY Test */ -#define B43_LPPHY_B_FOURWIRE_ADDR B43_PHY_CCK(0x0B) /* B PHY Fourwire Address */ -#define B43_LPPHY_B_FOURWIRE_DATA_HI B43_PHY_CCK(0x0C) /* B PHY Fourwire Data Hi */ -#define B43_LPPHY_B_FOURWIRE_DATA_LO B43_PHY_CCK(0x0D) /* B PHY Fourwire Data Lo */ -#define B43_LPPHY_B_BIST_STAT B43_PHY_CCK(0x0E) /* B PHY Bist Status */ -#define B43_LPPHY_PA_RAMP_TX_TO B43_PHY_CCK(0x10) /* PA Ramp TX Timeout */ -#define B43_LPPHY_RF_SYNTH_DC_TIMER B43_PHY_CCK(0x11) /* RF Synth DC Timer */ -#define B43_LPPHY_PA_RAMP_TX_TIME_IN B43_PHY_CCK(0x12) /* PA ramp TX Time in */ -#define B43_LPPHY_RX_FILTER_TIME_IN B43_PHY_CCK(0x13) /* RX Filter Time in */ -#define B43_LPPHY_PLL_COEFF_S B43_PHY_CCK(0x18) /* PLL Coefficient(s) */ -#define B43_LPPHY_PLL_OUT B43_PHY_CCK(0x19) /* PLL Out */ -#define B43_LPPHY_RSSI_THRES B43_PHY_CCK(0x20) /* RSSI Threshold */ -#define B43_LPPHY_IQ_THRES_HH B43_PHY_CCK(0x21) /* IQ Threshold HH */ -#define B43_LPPHY_IQ_THRES_H B43_PHY_CCK(0x22) /* IQ Threshold H */ -#define B43_LPPHY_IQ_THRES_L B43_PHY_CCK(0x23) /* IQ Threshold L */ -#define B43_LPPHY_IQ_THRES_LL B43_PHY_CCK(0x24) /* IQ Threshold LL */ -#define B43_LPPHY_AGC_GAIN B43_PHY_CCK(0x25) /* AGC Gain */ -#define B43_LPPHY_LNA_GAIN_RANGE B43_PHY_CCK(0x26) /* LNA Gain Range */ -#define B43_LPPHY_JSSI B43_PHY_CCK(0x27) /* JSSI */ -#define B43_LPPHY_TSSI_CTL B43_PHY_CCK(0x28) /* TSSI Control */ -#define B43_LPPHY_TSSI B43_PHY_CCK(0x29) /* TSSI */ -#define B43_LPPHY_TR_LOSS B43_PHY_CCK(0x2A) /* TR Loss */ -#define B43_LPPHY_LO_LEAKAGE B43_PHY_CCK(0x2B) /* LO Leakage */ -#define B43_LPPHY_LO_RSSIACC B43_PHY_CCK(0x2C) /* LO RSSIAcc */ -#define B43_LPPHY_LO_IQ_MAG_ACC B43_PHY_CCK(0x2D) /* LO IQ Mag Acc */ -#define B43_LPPHY_TX_DCOFFSET1 B43_PHY_CCK(0x2E) /* TX DCOffset1 */ -#define B43_LPPHY_TX_DCOFFSET2 B43_PHY_CCK(0x2F) /* TX DCOffset2 */ -#define B43_LPPHY_SYNCPEAKCNT B43_PHY_CCK(0x30) /* SyncPeakCnt */ -#define B43_LPPHY_SYNCFREQ B43_PHY_CCK(0x31) /* SyncFreq */ -#define B43_LPPHY_SYNCDIVERSITYCTL B43_PHY_CCK(0x32) /* SyncDiversityControl */ -#define B43_LPPHY_PEAKENERGYL B43_PHY_CCK(0x33) /* PeakEnergyL */ -#define B43_LPPHY_PEAKENERGYH B43_PHY_CCK(0x34) /* PeakEnergyH */ -#define B43_LPPHY_SYNCCTL B43_PHY_CCK(0x35) /* SyncControl */ -#define B43_LPPHY_DSSSSTEP B43_PHY_CCK(0x38) /* DsssStep */ -#define B43_LPPHY_DSSSWARMUP B43_PHY_CCK(0x39) /* DsssWarmup */ -#define B43_LPPHY_DSSSSIGPOW B43_PHY_CCK(0x3D) /* DsssSigPow */ -#define B43_LPPHY_SFDDETECTBLOCKTIME B43_PHY_CCK(0x40) /* SfdDetectBlockTIme */ -#define B43_LPPHY_SFDTO B43_PHY_CCK(0x41) /* SFDTimeOut */ -#define B43_LPPHY_SFDCTL B43_PHY_CCK(0x42) /* SFDControl */ -#define B43_LPPHY_RXDBG B43_PHY_CCK(0x43) /* rxDebug */ -#define B43_LPPHY_RX_DELAYCOMP B43_PHY_CCK(0x44) /* RX DelayComp */ -#define B43_LPPHY_CRSDROPOUTTO B43_PHY_CCK(0x45) /* CRSDropoutTimeout */ -#define B43_LPPHY_PSEUDOSHORTTO B43_PHY_CCK(0x46) /* PseudoShortTimeout */ -#define B43_LPPHY_PR3931 B43_PHY_CCK(0x47) /* PR3931 */ -#define B43_LPPHY_DSSSCOEFF1 B43_PHY_CCK(0x48) /* DSSSCoeff1 */ -#define B43_LPPHY_DSSSCOEFF2 B43_PHY_CCK(0x49) /* DSSSCoeff2 */ -#define B43_LPPHY_CCKCOEFF1 B43_PHY_CCK(0x4A) /* CCKCoeff1 */ -#define B43_LPPHY_CCKCOEFF2 B43_PHY_CCK(0x4B) /* CCKCoeff2 */ -#define B43_LPPHY_TRCORR B43_PHY_CCK(0x4C) /* TRCorr */ -#define B43_LPPHY_ANGLESCALE B43_PHY_CCK(0x4D) /* AngleScale */ -#define B43_LPPHY_OPTIONALMODES2 B43_PHY_CCK(0x4F) /* OptionalModes2 */ -#define B43_LPPHY_CCKLMSSTEPSIZE B43_PHY_CCK(0x50) /* CCKLMSStepSize */ -#define B43_LPPHY_DFEBYPASS B43_PHY_CCK(0x51) /* DFEBypass */ -#define B43_LPPHY_CCKSTARTDELAYLONG B43_PHY_CCK(0x52) /* CCKStartDelayLong */ -#define B43_LPPHY_CCKSTARTDELAYSHORT B43_PHY_CCK(0x53) /* CCKStartDelayShort */ -#define B43_LPPHY_PPROCCHDELAY B43_PHY_CCK(0x54) /* PprocChDelay */ -#define B43_LPPHY_PPROCONOFF B43_PHY_CCK(0x55) /* PProcOnOff */ -#define B43_LPPHY_LNAGAINTWOBIT10 B43_PHY_CCK(0x5B) /* LNAGainTwoBit10 */ -#define B43_LPPHY_LNAGAINTWOBIT32 B43_PHY_CCK(0x5C) /* LNAGainTwoBit32 */ -#define B43_LPPHY_OPTIONALMODES B43_PHY_CCK(0x5D) /* OptionalModes */ -#define B43_LPPHY_B_RX_STAT2 B43_PHY_CCK(0x5E) /* B PHY RX Status2 */ -#define B43_LPPHY_B_RX_STAT3 B43_PHY_CCK(0x5F) /* B PHY RX Status3 */ -#define B43_LPPHY_PWDNDACDELAY B43_PHY_CCK(0x63) /* pwdnDacDelay */ -#define B43_LPPHY_FINEDIGIGAIN_CTL B43_PHY_CCK(0x67) /* FineDigiGain Control */ -#define B43_LPPHY_LG2GAINTBLLNA8 B43_PHY_CCK(0x68) /* Lg2GainTblLNA8 */ -#define B43_LPPHY_LG2GAINTBLLNA28 B43_PHY_CCK(0x69) /* Lg2GainTblLNA28 */ -#define B43_LPPHY_GAINTBLLNATRSW B43_PHY_CCK(0x6A) /* GainTblLNATrSw */ -#define B43_LPPHY_PEAKENERGY B43_PHY_CCK(0x6B) /* PeakEnergy */ -#define B43_LPPHY_LG2INITGAIN B43_PHY_CCK(0x6C) /* lg2InitGain */ -#define B43_LPPHY_BLANKCOUNTLNAPGA B43_PHY_CCK(0x6D) /* BlankCountLnaPga */ -#define B43_LPPHY_LNAGAINTWOBIT54 B43_PHY_CCK(0x6E) /* LNAGainTwoBit54 */ -#define B43_LPPHY_LNAGAINTWOBIT76 B43_PHY_CCK(0x6F) /* LNAGainTwoBit76 */ -#define B43_LPPHY_JSSICTL B43_PHY_CCK(0x70) /* JSSIControl */ -#define B43_LPPHY_LG2GAINTBLLNA44 B43_PHY_CCK(0x71) /* Lg2GainTblLNA44 */ -#define B43_LPPHY_LG2GAINTBLLNA62 B43_PHY_CCK(0x72) /* Lg2GainTblLNA62 */ - -/* The OFDM PHY register range. */ -#define B43_LPPHY_VERSION B43_PHY_OFDM(0x00) /* Version */ -#define B43_LPPHY_BBCONFIG B43_PHY_OFDM(0x01) /* BBConfig */ -#define B43_LPPHY_RX_STAT0 B43_PHY_OFDM(0x04) /* RX Status0 */ -#define B43_LPPHY_RX_STAT1 B43_PHY_OFDM(0x05) /* RX Status1 */ -#define B43_LPPHY_TX_ERROR B43_PHY_OFDM(0x07) /* TX Error */ -#define B43_LPPHY_CHANNEL B43_PHY_OFDM(0x08) /* Channel */ -#define B43_LPPHY_WORKAROUND B43_PHY_OFDM(0x09) /* workaround */ -#define B43_LPPHY_FOURWIRE_ADDR B43_PHY_OFDM(0x0B) /* Fourwire Address */ -#define B43_LPPHY_FOURWIREDATAHI B43_PHY_OFDM(0x0C) /* FourwireDataHi */ -#define B43_LPPHY_FOURWIREDATALO B43_PHY_OFDM(0x0D) /* FourwireDataLo */ -#define B43_LPPHY_BISTSTAT0 B43_PHY_OFDM(0x0E) /* BistStatus0 */ -#define B43_LPPHY_BISTSTAT1 B43_PHY_OFDM(0x0F) /* BistStatus1 */ -#define B43_LPPHY_CRSGAIN_CTL B43_PHY_OFDM(0x10) /* crsgain Control */ -#define B43_LPPHY_OFDMPWR_THRESH0 B43_PHY_OFDM(0x11) /* ofdmPower Thresh0 */ -#define B43_LPPHY_OFDMPWR_THRESH1 B43_PHY_OFDM(0x12) /* ofdmPower Thresh1 */ -#define B43_LPPHY_OFDMPWR_THRESH2 B43_PHY_OFDM(0x13) /* ofdmPower Thresh2 */ -#define B43_LPPHY_DSSSPWR_THRESH0 B43_PHY_OFDM(0x14) /* dsssPower Thresh0 */ -#define B43_LPPHY_DSSSPWR_THRESH1 B43_PHY_OFDM(0x15) /* dsssPower Thresh1 */ -#define B43_LPPHY_MINPWR_LEVEL B43_PHY_OFDM(0x16) /* MinPower Level */ -#define B43_LPPHY_OFDMSYNCTHRESH0 B43_PHY_OFDM(0x17) /* ofdmSyncThresh0 */ -#define B43_LPPHY_OFDMSYNCTHRESH1 B43_PHY_OFDM(0x18) /* ofdmSyncThresh1 */ -#define B43_LPPHY_FINEFREQEST B43_PHY_OFDM(0x19) /* FineFreqEst */ -#define B43_LPPHY_IDLEAFTERPKTRXTO B43_PHY_OFDM(0x1A) /* IDLEafterPktRXTimeout */ -#define B43_LPPHY_LTRN_CTL B43_PHY_OFDM(0x1B) /* LTRN Control */ -#define B43_LPPHY_DCOFFSETTRANSIENT B43_PHY_OFDM(0x1C) /* DCOffsetTransient */ -#define B43_LPPHY_PREAMBLEINTO B43_PHY_OFDM(0x1D) /* PreambleInTimeout */ -#define B43_LPPHY_PREAMBLECONFIRMTO B43_PHY_OFDM(0x1E) /* PreambleConfirmTimeout */ -#define B43_LPPHY_CLIPTHRESH B43_PHY_OFDM(0x1F) /* ClipThresh */ -#define B43_LPPHY_CLIPCTRTHRESH B43_PHY_OFDM(0x20) /* ClipCtrThresh */ -#define B43_LPPHY_OFDMSYNCTIMER_CTL B43_PHY_OFDM(0x21) /* ofdmSyncTimer Control */ -#define B43_LPPHY_WAITFORPHYSELTO B43_PHY_OFDM(0x22) /* WaitforPHYSelTimeout */ -#define B43_LPPHY_HIGAINDB B43_PHY_OFDM(0x23) /* HiGainDB */ -#define B43_LPPHY_LOWGAINDB B43_PHY_OFDM(0x24) /* LowGainDB */ -#define B43_LPPHY_VERYLOWGAINDB B43_PHY_OFDM(0x25) /* VeryLowGainDB */ -#define B43_LPPHY_GAINMISMATCH B43_PHY_OFDM(0x26) /* gainMismatch */ -#define B43_LPPHY_GAINDIRECTMISMATCH B43_PHY_OFDM(0x27) /* gaindirectMismatch */ -#define B43_LPPHY_PWR_THRESH0 B43_PHY_OFDM(0x28) /* Power Thresh0 */ -#define B43_LPPHY_PWR_THRESH1 B43_PHY_OFDM(0x29) /* Power Thresh1 */ -#define B43_LPPHY_DETECTOR_DELAY_ADJUST B43_PHY_OFDM(0x2A) /* Detector Delay Adjust */ -#define B43_LPPHY_REDUCED_DETECTOR_DELAY B43_PHY_OFDM(0x2B) /* Reduced Detector Delay */ -#define B43_LPPHY_DATA_TO B43_PHY_OFDM(0x2C) /* data Timeout */ -#define B43_LPPHY_CORRELATOR_DIS_DELAY B43_PHY_OFDM(0x2D) /* correlator Dis Delay */ -#define B43_LPPHY_DIVERSITY_GAINBACK B43_PHY_OFDM(0x2E) /* Diversity GainBack */ -#define B43_LPPHY_DSSS_CONFIRM_CNT B43_PHY_OFDM(0x2F) /* DSSS Confirm Cnt */ -#define B43_LPPHY_DC_BLANK_INT B43_PHY_OFDM(0x30) /* DC Blank Interval */ -#define B43_LPPHY_GAIN_MISMATCH_LIMIT B43_PHY_OFDM(0x31) /* gain Mismatch Limit */ -#define B43_LPPHY_CRS_ED_THRESH B43_PHY_OFDM(0x32) /* crs ed thresh */ -#define B43_LPPHY_PHASE_SHIFT_CTL B43_PHY_OFDM(0x33) /* phase shift Control */ -#define B43_LPPHY_INPUT_PWRDB B43_PHY_OFDM(0x34) /* Input PowerDB */ -#define B43_LPPHY_OFDM_SYNC_CTL B43_PHY_OFDM(0x35) /* ofdm sync Control */ -#define B43_LPPHY_AFE_ADC_CTL_0 B43_PHY_OFDM(0x36) /* Afe ADC Control 0 */ -#define B43_LPPHY_AFE_ADC_CTL_1 B43_PHY_OFDM(0x37) /* Afe ADC Control 1 */ -#define B43_LPPHY_AFE_ADC_CTL_2 B43_PHY_OFDM(0x38) /* Afe ADC Control 2 */ -#define B43_LPPHY_AFE_DAC_CTL B43_PHY_OFDM(0x39) /* Afe DAC Control */ -#define B43_LPPHY_AFE_CTL B43_PHY_OFDM(0x3A) /* Afe Control */ -#define B43_LPPHY_AFE_CTL_OVR B43_PHY_OFDM(0x3B) /* Afe Control Ovr */ -#define B43_LPPHY_AFE_CTL_OVRVAL B43_PHY_OFDM(0x3C) /* Afe Control OvrVal */ -#define B43_LPPHY_AFE_RSSI_CTL_0 B43_PHY_OFDM(0x3D) /* Afe RSSI Control 0 */ -#define B43_LPPHY_AFE_RSSI_CTL_1 B43_PHY_OFDM(0x3E) /* Afe RSSI Control 1 */ -#define B43_LPPHY_AFE_RSSI_SEL B43_PHY_OFDM(0x3F) /* Afe RSSI Sel */ -#define B43_LPPHY_RADAR_THRESH B43_PHY_OFDM(0x40) /* Radar Thresh */ -#define B43_LPPHY_RADAR_BLANK_INT B43_PHY_OFDM(0x41) /* Radar blank Interval */ -#define B43_LPPHY_RADAR_MIN_FM_INT B43_PHY_OFDM(0x42) /* Radar min fm Interval */ -#define B43_LPPHY_RADAR_GAIN_TO B43_PHY_OFDM(0x43) /* Radar gain timeout */ -#define B43_LPPHY_RADAR_PULSE_TO B43_PHY_OFDM(0x44) /* Radar pulse timeout */ -#define B43_LPPHY_RADAR_DETECT_FM_CTL B43_PHY_OFDM(0x45) /* Radar detect FM Control */ -#define B43_LPPHY_RADAR_DETECT_EN B43_PHY_OFDM(0x46) /* Radar detect En */ -#define B43_LPPHY_RADAR_RD_DATA_REG B43_PHY_OFDM(0x47) /* Radar Rd Data Reg */ -#define B43_LPPHY_LP_PHY_CTL B43_PHY_OFDM(0x48) /* LP PHY Control */ -#define B43_LPPHY_CLASSIFIER_CTL B43_PHY_OFDM(0x49) /* classifier Control */ -#define B43_LPPHY_RESET_CTL B43_PHY_OFDM(0x4A) /* reset Control */ -#define B43_LPPHY_CLKEN_CTL B43_PHY_OFDM(0x4B) /* ClkEn Control */ -#define B43_LPPHY_RF_OVERRIDE_0 B43_PHY_OFDM(0x4C) /* RF Override 0 */ -#define B43_LPPHY_RF_OVERRIDE_VAL_0 B43_PHY_OFDM(0x4D) /* RF Override Val 0 */ -#define B43_LPPHY_TR_LOOKUP_1 B43_PHY_OFDM(0x4E) /* TR Lookup 1 */ -#define B43_LPPHY_TR_LOOKUP_2 B43_PHY_OFDM(0x4F) /* TR Lookup 2 */ -#define B43_LPPHY_RSSISELLOOKUP1 B43_PHY_OFDM(0x50) /* RssiSelLookup1 */ -#define B43_LPPHY_IQLO_CAL_CMD B43_PHY_OFDM(0x51) /* iqlo Cal Cmd */ -#define B43_LPPHY_IQLO_CAL_CMD_N_NUM B43_PHY_OFDM(0x52) /* iqlo Cal Cmd N num */ -#define B43_LPPHY_IQLO_CAL_CMD_G_CTL B43_PHY_OFDM(0x53) /* iqlo Cal Cmd G control */ -#define B43_LPPHY_MACINT_DBG_REGISTER B43_PHY_OFDM(0x54) /* macint Debug Register */ -#define B43_LPPHY_TABLE_ADDR B43_PHY_OFDM(0x55) /* Table Address */ -#define B43_LPPHY_TABLEDATALO B43_PHY_OFDM(0x56) /* TabledataLo */ -#define B43_LPPHY_TABLEDATAHI B43_PHY_OFDM(0x57) /* TabledataHi */ -#define B43_LPPHY_PHY_CRS_ENABLE_ADDR B43_PHY_OFDM(0x58) /* phy CRS Enable Address */ -#define B43_LPPHY_IDLETIME_CTL B43_PHY_OFDM(0x59) /* Idletime Control */ -#define B43_LPPHY_IDLETIME_CRS_ON_LO B43_PHY_OFDM(0x5A) /* Idletime CRS On Lo */ -#define B43_LPPHY_IDLETIME_CRS_ON_HI B43_PHY_OFDM(0x5B) /* Idletime CRS On Hi */ -#define B43_LPPHY_IDLETIME_MEAS_TIME_LO B43_PHY_OFDM(0x5C) /* Idletime Meas Time Lo */ -#define B43_LPPHY_IDLETIME_MEAS_TIME_HI B43_PHY_OFDM(0x5D) /* Idletime Meas Time Hi */ -#define B43_LPPHY_RESET_LEN_OFDM_TX_ADDR B43_PHY_OFDM(0x5E) /* Reset len Ofdm TX Address */ -#define B43_LPPHY_RESET_LEN_OFDM_RX_ADDR B43_PHY_OFDM(0x5F) /* Reset len Ofdm RX Address */ -#define B43_LPPHY_REG_CRS_ENABLE B43_PHY_OFDM(0x60) /* reg crs enable */ -#define B43_LPPHY_PLCP_TMT_STR0_CTR_MIN B43_PHY_OFDM(0x61) /* PLCP Tmt Str0 Ctr Min */ -#define B43_LPPHY_PKT_FSM_RESET_LEN_VAL B43_PHY_OFDM(0x62) /* Pkt fsm Reset Len Value */ -#define B43_LPPHY_READSYM2RESET_CTL B43_PHY_OFDM(0x63) /* readsym2reset Control */ -#define B43_LPPHY_DC_FILTER_DELAY1 B43_PHY_OFDM(0x64) /* Dc filter delay1 */ -#define B43_LPPHY_PACKET_RX_ACTIVE_TO B43_PHY_OFDM(0x65) /* packet rx Active timeout */ -#define B43_LPPHY_ED_TOVAL B43_PHY_OFDM(0x66) /* ed timeoutValue */ -#define B43_LPPHY_HOLD_CRS_ON_VAL B43_PHY_OFDM(0x67) /* hold CRS On Value */ -#define B43_LPPHY_OFDM_TX_PHY_CRS_DELAY_VAL B43_PHY_OFDM(0x69) /* ofdm tx phy CRS Delay Value */ -#define B43_LPPHY_CCK_TX_PHY_CRS_DELAY_VAL B43_PHY_OFDM(0x6A) /* cck tx phy CRS Delay Value */ -#define B43_LPPHY_ED_ON_CONFIRM_TIMER_VAL B43_PHY_OFDM(0x6B) /* Ed on confirm Timer Value */ -#define B43_LPPHY_ED_OFFSET_CONFIRM_TIMER_VAL B43_PHY_OFDM(0x6C) /* Ed offset confirm Timer Value */ -#define B43_LPPHY_PHY_CRS_OFFSET_TIMER_VAL B43_PHY_OFDM(0x6D) /* phy CRS offset Timer Value */ -#define B43_LPPHY_ADC_COMPENSATION_CTL B43_PHY_OFDM(0x70) /* ADC Compensation Control */ -#define B43_LPPHY_LOG2_RBPSK_ADDR B43_PHY_OFDM(0x71) /* log2 RBPSK Address */ -#define B43_LPPHY_LOG2_RQPSK_ADDR B43_PHY_OFDM(0x72) /* log2 RQPSK Address */ -#define B43_LPPHY_LOG2_R16QAM_ADDR B43_PHY_OFDM(0x73) /* log2 R16QAM Address */ -#define B43_LPPHY_LOG2_R64QAM_ADDR B43_PHY_OFDM(0x74) /* log2 R64QAM Address */ -#define B43_LPPHY_OFFSET_BPSK_ADDR B43_PHY_OFDM(0x75) /* offset BPSK Address */ -#define B43_LPPHY_OFFSET_QPSK_ADDR B43_PHY_OFDM(0x76) /* offset QPSK Address */ -#define B43_LPPHY_OFFSET_16QAM_ADDR B43_PHY_OFDM(0x77) /* offset 16QAM Address */ -#define B43_LPPHY_OFFSET_64QAM_ADDR B43_PHY_OFDM(0x78) /* offset 64QAM Address */ -#define B43_LPPHY_ALPHA1 B43_PHY_OFDM(0x79) /* Alpha1 */ -#define B43_LPPHY_ALPHA2 B43_PHY_OFDM(0x7A) /* Alpha2 */ -#define B43_LPPHY_BETA1 B43_PHY_OFDM(0x7B) /* Beta1 */ -#define B43_LPPHY_BETA2 B43_PHY_OFDM(0x7C) /* Beta2 */ -#define B43_LPPHY_LOOP_NUM_ADDR B43_PHY_OFDM(0x7D) /* Loop Num Address */ -#define B43_LPPHY_STR_COLLMAX_SMPL_ADDR B43_PHY_OFDM(0x7E) /* Str Collmax Sample Address */ -#define B43_LPPHY_MAX_SMPL_COARSE_FINE_ADDR B43_PHY_OFDM(0x7F) /* Max Sample Coarse/Fine Address */ -#define B43_LPPHY_MAX_SMPL_COARSE_STR0CTR_ADDR B43_PHY_OFDM(0x80) /* Max Sample Coarse/Str0Ctr Address */ -#define B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR B43_PHY_OFDM(0x81) /* IQ Enable Wait Time Address */ -#define B43_LPPHY_IQ_NUM_SMPLS_ADDR B43_PHY_OFDM(0x82) /* IQ Num Samples Address */ -#define B43_LPPHY_IQ_ACC_HI_ADDR B43_PHY_OFDM(0x83) /* IQ Acc Hi Address */ -#define B43_LPPHY_IQ_ACC_LO_ADDR B43_PHY_OFDM(0x84) /* IQ Acc Lo Address */ -#define B43_LPPHY_IQ_I_PWR_ACC_HI_ADDR B43_PHY_OFDM(0x85) /* IQ I PWR Acc Hi Address */ -#define B43_LPPHY_IQ_I_PWR_ACC_LO_ADDR B43_PHY_OFDM(0x86) /* IQ I PWR Acc Lo Address */ -#define B43_LPPHY_IQ_Q_PWR_ACC_HI_ADDR B43_PHY_OFDM(0x87) /* IQ Q PWR Acc Hi Address */ -#define B43_LPPHY_IQ_Q_PWR_ACC_LO_ADDR B43_PHY_OFDM(0x88) /* IQ Q PWR Acc Lo Address */ -#define B43_LPPHY_MAXNUMSTEPS B43_PHY_OFDM(0x89) /* MaxNumsteps */ -#define B43_LPPHY_ROTORPHASE_ADDR B43_PHY_OFDM(0x8A) /* RotorPhase Address */ -#define B43_LPPHY_ADVANCEDRETARDROTOR_ADDR B43_PHY_OFDM(0x8B) /* AdvancedRetardRotor Address */ -#define B43_LPPHY_RSSIADCDELAY_CTL_ADDR B43_PHY_OFDM(0x8D) /* rssiAdcdelay Control Address */ -#define B43_LPPHY_TSSISTAT_ADDR B43_PHY_OFDM(0x8E) /* tssiStatus Address */ -#define B43_LPPHY_TEMPSENSESTAT_ADDR B43_PHY_OFDM(0x8F) /* tempsenseStatus Address */ -#define B43_LPPHY_TEMPSENSE_CTL_ADDR B43_PHY_OFDM(0x90) /* tempsense Control Address */ -#define B43_LPPHY_WRSSISTAT_ADDR B43_PHY_OFDM(0x91) /* wrssistatus Address */ -#define B43_LPPHY_MUFACTORADDR B43_PHY_OFDM(0x92) /* mufactoraddr */ -#define B43_LPPHY_SCRAMSTATE_ADDR B43_PHY_OFDM(0x93) /* scramstate Address */ -#define B43_LPPHY_TXHOLDOFFADDR B43_PHY_OFDM(0x94) /* txholdoffaddr */ -#define B43_LPPHY_PKTGAINVAL_ADDR B43_PHY_OFDM(0x95) /* pktgainval Address */ -#define B43_LPPHY_COARSEESTIM_ADDR B43_PHY_OFDM(0x96) /* Coarseestim Address */ -#define B43_LPPHY_STATE_TRANSITION_ADDR B43_PHY_OFDM(0x97) /* state Transition Address */ -#define B43_LPPHY_TRN_OFFSET_ADDR B43_PHY_OFDM(0x98) /* TRN offset Address */ -#define B43_LPPHY_NUM_ROTOR_ADDR B43_PHY_OFDM(0x99) /* Num Rotor Address */ -#define B43_LPPHY_VITERBI_OFFSET_ADDR B43_PHY_OFDM(0x9A) /* Viterbi Offset Address */ -#define B43_LPPHY_SMPL_COLLECT_WAIT_ADDR B43_PHY_OFDM(0x9B) /* Sample collect wait Address */ -#define B43_LPPHY_A_PHY_CTL_ADDR B43_PHY_OFDM(0x9C) /* A PHY Control Address */ -#define B43_LPPHY_NUM_PASS_THROUGH_ADDR B43_PHY_OFDM(0x9D) /* Num Pass Through Address */ -#define B43_LPPHY_RX_COMP_COEFF_S B43_PHY_OFDM(0x9E) /* RX Comp coefficient(s) */ -#define B43_LPPHY_CPAROTATEVAL B43_PHY_OFDM(0x9F) /* cpaRotateValue */ -#define B43_LPPHY_SMPL_PLAY_COUNT B43_PHY_OFDM(0xA0) /* Sample play count */ -#define B43_LPPHY_SMPL_PLAY_BUFFER_CTL B43_PHY_OFDM(0xA1) /* Sample play Buffer Control */ -#define B43_LPPHY_FOURWIRE_CTL B43_PHY_OFDM(0xA2) /* fourwire Control */ -#define B43_LPPHY_CPA_TAILCOUNT_VAL B43_PHY_OFDM(0xA3) /* CPA TailCount Value */ -#define B43_LPPHY_TX_PWR_CTL_CMD B43_PHY_OFDM(0xA4) /* TX Power Control Cmd */ -#define B43_LPPHY_TX_PWR_CTL_CMD_MODE 0xE000 /* TX power control mode mask */ -#define B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF 0x0000 /* TX power control is OFF */ -#define B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW 0x8000 /* TX power control is SOFTWARE */ -#define B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW 0xE000 /* TX power control is HARDWARE */ -#define B43_LPPHY_TX_PWR_CTL_NNUM B43_PHY_OFDM(0xA5) /* TX Power Control Nnum */ -#define B43_LPPHY_TX_PWR_CTL_IDLETSSI B43_PHY_OFDM(0xA6) /* TX Power Control IdleTssi */ -#define B43_LPPHY_TX_PWR_CTL_TARGETPWR B43_PHY_OFDM(0xA7) /* TX Power Control TargetPower */ -#define B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT B43_PHY_OFDM(0xA8) /* TX Power Control DeltaPower Limit */ -#define B43_LPPHY_TX_PWR_CTL_BASEINDEX B43_PHY_OFDM(0xA9) /* TX Power Control BaseIndex */ -#define B43_LPPHY_TX_PWR_CTL_PWR_INDEX B43_PHY_OFDM(0xAA) /* TX Power Control Power Index */ -#define B43_LPPHY_TX_PWR_CTL_STAT B43_PHY_OFDM(0xAB) /* TX Power Control Status */ -#define B43_LPPHY_LP_RF_SIGNAL_LUT B43_PHY_OFDM(0xAC) /* LP RF signal LUT */ -#define B43_LPPHY_RX_RADIO_CTL_FILTER_STATE B43_PHY_OFDM(0xAD) /* RX Radio Control Filter State */ -#define B43_LPPHY_RX_RADIO_CTL B43_PHY_OFDM(0xAE) /* RX Radio Control */ -#define B43_LPPHY_NRSSI_STAT_ADDR B43_PHY_OFDM(0xAF) /* NRSSI status Address */ -#define B43_LPPHY_RF_OVERRIDE_2 B43_PHY_OFDM(0xB0) /* RF override 2 */ -#define B43_LPPHY_RF_OVERRIDE_2_VAL B43_PHY_OFDM(0xB1) /* RF override 2 val */ -#define B43_LPPHY_PS_CTL_OVERRIDE_VAL0 B43_PHY_OFDM(0xB2) /* PS Control override val0 */ -#define B43_LPPHY_PS_CTL_OVERRIDE_VAL1 B43_PHY_OFDM(0xB3) /* PS Control override val1 */ -#define B43_LPPHY_PS_CTL_OVERRIDE_VAL2 B43_PHY_OFDM(0xB4) /* PS Control override val2 */ -#define B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL B43_PHY_OFDM(0xB5) /* TX gain Control override val */ -#define B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL B43_PHY_OFDM(0xB6) /* RX gain Control override val */ -#define B43_LPPHY_AFE_DDFS B43_PHY_OFDM(0xB7) /* AFE DDFS */ -#define B43_LPPHY_AFE_DDFS_POINTER_INIT B43_PHY_OFDM(0xB8) /* AFE DDFS pointer init */ -#define B43_LPPHY_AFE_DDFS_INCR_INIT B43_PHY_OFDM(0xB9) /* AFE DDFS incr init */ -#define B43_LPPHY_MRCNOISEREDUCTION B43_PHY_OFDM(0xBA) /* mrcNoiseReduction */ -#define B43_LPPHY_TR_LOOKUP_3 B43_PHY_OFDM(0xBB) /* TR Lookup 3 */ -#define B43_LPPHY_TR_LOOKUP_4 B43_PHY_OFDM(0xBC) /* TR Lookup 4 */ -#define B43_LPPHY_RADAR_FIFO_STAT B43_PHY_OFDM(0xBD) /* Radar FIFO Status */ -#define B43_LPPHY_GPIO_OUTEN B43_PHY_OFDM(0xBE) /* GPIO Out enable */ -#define B43_LPPHY_GPIO_SELECT B43_PHY_OFDM(0xBF) /* GPIO Select */ -#define B43_LPPHY_GPIO_OUT B43_PHY_OFDM(0xC0) /* GPIO Out */ -#define B43_LPPHY_4C3 B43_PHY_OFDM(0xC3) /* unknown, used during BB init */ -#define B43_LPPHY_4C4 B43_PHY_OFDM(0xC4) /* unknown, used during BB init */ -#define B43_LPPHY_4C5 B43_PHY_OFDM(0xC5) /* unknown, used during BB init */ -#define B43_LPPHY_TR_LOOKUP_5 B43_PHY_OFDM(0xC7) /* TR Lookup 5 */ -#define B43_LPPHY_TR_LOOKUP_6 B43_PHY_OFDM(0xC8) /* TR Lookup 6 */ -#define B43_LPPHY_TR_LOOKUP_7 B43_PHY_OFDM(0xC9) /* TR Lookup 7 */ -#define B43_LPPHY_TR_LOOKUP_8 B43_PHY_OFDM(0xCA) /* TR Lookup 8 */ -#define B43_LPPHY_RF_PWR_OVERRIDE B43_PHY_OFDM(0xD3) /* RF power override */ - - - -/* Radio register access decorators. */ -#define B43_LP_RADIO(radio_reg) (radio_reg) -#define B43_LP_NORTH(radio_reg) B43_LP_RADIO(radio_reg) -#define B43_LP_SOUTH(radio_reg) B43_LP_RADIO((radio_reg) | 0x4000) - - -/*** Broadcom 2062 NORTH radio registers ***/ -#define B2062_N_COMM1 B43_LP_NORTH(0x000) /* Common 01 (north) */ -#define B2062_N_COMM2 B43_LP_NORTH(0x002) /* Common 02 (north) */ -#define B2062_N_COMM3 B43_LP_NORTH(0x003) /* Common 03 (north) */ -#define B2062_N_COMM4 B43_LP_NORTH(0x004) /* Common 04 (north) */ -#define B2062_N_COMM5 B43_LP_NORTH(0x005) /* Common 05 (north) */ -#define B2062_N_COMM6 B43_LP_NORTH(0x006) /* Common 06 (north) */ -#define B2062_N_COMM7 B43_LP_NORTH(0x007) /* Common 07 (north) */ -#define B2062_N_COMM8 B43_LP_NORTH(0x008) /* Common 08 (north) */ -#define B2062_N_COMM9 B43_LP_NORTH(0x009) /* Common 09 (north) */ -#define B2062_N_COMM10 B43_LP_NORTH(0x00A) /* Common 10 (north) */ -#define B2062_N_COMM11 B43_LP_NORTH(0x00B) /* Common 11 (north) */ -#define B2062_N_COMM12 B43_LP_NORTH(0x00C) /* Common 12 (north) */ -#define B2062_N_COMM13 B43_LP_NORTH(0x00D) /* Common 13 (north) */ -#define B2062_N_COMM14 B43_LP_NORTH(0x00E) /* Common 14 (north) */ -#define B2062_N_COMM15 B43_LP_NORTH(0x00F) /* Common 15 (north) */ -#define B2062_N_PDN_CTL0 B43_LP_NORTH(0x010) /* PDN Control 0 (north) */ -#define B2062_N_PDN_CTL1 B43_LP_NORTH(0x011) /* PDN Control 1 (north) */ -#define B2062_N_PDN_CTL2 B43_LP_NORTH(0x012) /* PDN Control 2 (north) */ -#define B2062_N_PDN_CTL3 B43_LP_NORTH(0x013) /* PDN Control 3 (north) */ -#define B2062_N_PDN_CTL4 B43_LP_NORTH(0x014) /* PDN Control 4 (north) */ -#define B2062_N_GEN_CTL0 B43_LP_NORTH(0x015) /* GEN Control 0 (north) */ -#define B2062_N_IQ_CALIB B43_LP_NORTH(0x016) /* IQ Calibration (north) */ -#define B2062_N_LGENC B43_LP_NORTH(0x017) /* LGENC (north) */ -#define B2062_N_LGENA_LPF B43_LP_NORTH(0x018) /* LGENA LPF (north) */ -#define B2062_N_LGENA_BIAS0 B43_LP_NORTH(0x019) /* LGENA Bias 0 (north) */ -#define B2062_N_LGNEA_BIAS1 B43_LP_NORTH(0x01A) /* LGNEA Bias 1 (north) */ -#define B2062_N_LGENA_CTL0 B43_LP_NORTH(0x01B) /* LGENA Control 0 (north) */ -#define B2062_N_LGENA_CTL1 B43_LP_NORTH(0x01C) /* LGENA Control 1 (north) */ -#define B2062_N_LGENA_CTL2 B43_LP_NORTH(0x01D) /* LGENA Control 2 (north) */ -#define B2062_N_LGENA_TUNE0 B43_LP_NORTH(0x01E) /* LGENA Tune 0 (north) */ -#define B2062_N_LGENA_TUNE1 B43_LP_NORTH(0x01F) /* LGENA Tune 1 (north) */ -#define B2062_N_LGENA_TUNE2 B43_LP_NORTH(0x020) /* LGENA Tune 2 (north) */ -#define B2062_N_LGENA_TUNE3 B43_LP_NORTH(0x021) /* LGENA Tune 3 (north) */ -#define B2062_N_LGENA_CTL3 B43_LP_NORTH(0x022) /* LGENA Control 3 (north) */ -#define B2062_N_LGENA_CTL4 B43_LP_NORTH(0x023) /* LGENA Control 4 (north) */ -#define B2062_N_LGENA_CTL5 B43_LP_NORTH(0x024) /* LGENA Control 5 (north) */ -#define B2062_N_LGENA_CTL6 B43_LP_NORTH(0x025) /* LGENA Control 6 (north) */ -#define B2062_N_LGENA_CTL7 B43_LP_NORTH(0x026) /* LGENA Control 7 (north) */ -#define B2062_N_RXA_CTL0 B43_LP_NORTH(0x027) /* RXA Control 0 (north) */ -#define B2062_N_RXA_CTL1 B43_LP_NORTH(0x028) /* RXA Control 1 (north) */ -#define B2062_N_RXA_CTL2 B43_LP_NORTH(0x029) /* RXA Control 2 (north) */ -#define B2062_N_RXA_CTL3 B43_LP_NORTH(0x02A) /* RXA Control 3 (north) */ -#define B2062_N_RXA_CTL4 B43_LP_NORTH(0x02B) /* RXA Control 4 (north) */ -#define B2062_N_RXA_CTL5 B43_LP_NORTH(0x02C) /* RXA Control 5 (north) */ -#define B2062_N_RXA_CTL6 B43_LP_NORTH(0x02D) /* RXA Control 6 (north) */ -#define B2062_N_RXA_CTL7 B43_LP_NORTH(0x02E) /* RXA Control 7 (north) */ -#define B2062_N_RXBB_CTL0 B43_LP_NORTH(0x02F) /* RXBB Control 0 (north) */ -#define B2062_N_RXBB_CTL1 B43_LP_NORTH(0x030) /* RXBB Control 1 (north) */ -#define B2062_N_RXBB_CTL2 B43_LP_NORTH(0x031) /* RXBB Control 2 (north) */ -#define B2062_N_RXBB_GAIN0 B43_LP_NORTH(0x032) /* RXBB Gain 0 (north) */ -#define B2062_N_RXBB_GAIN1 B43_LP_NORTH(0x033) /* RXBB Gain 1 (north) */ -#define B2062_N_RXBB_GAIN2 B43_LP_NORTH(0x034) /* RXBB Gain 2 (north) */ -#define B2062_N_RXBB_GAIN3 B43_LP_NORTH(0x035) /* RXBB Gain 3 (north) */ -#define B2062_N_RXBB_RSSI0 B43_LP_NORTH(0x036) /* RXBB RSSI 0 (north) */ -#define B2062_N_RXBB_RSSI1 B43_LP_NORTH(0x037) /* RXBB RSSI 1 (north) */ -#define B2062_N_RXBB_CALIB0 B43_LP_NORTH(0x038) /* RXBB Calibration0 (north) */ -#define B2062_N_RXBB_CALIB1 B43_LP_NORTH(0x039) /* RXBB Calibration1 (north) */ -#define B2062_N_RXBB_CALIB2 B43_LP_NORTH(0x03A) /* RXBB Calibration2 (north) */ -#define B2062_N_RXBB_BIAS0 B43_LP_NORTH(0x03B) /* RXBB Bias 0 (north) */ -#define B2062_N_RXBB_BIAS1 B43_LP_NORTH(0x03C) /* RXBB Bias 1 (north) */ -#define B2062_N_RXBB_BIAS2 B43_LP_NORTH(0x03D) /* RXBB Bias 2 (north) */ -#define B2062_N_RXBB_BIAS3 B43_LP_NORTH(0x03E) /* RXBB Bias 3 (north) */ -#define B2062_N_RXBB_BIAS4 B43_LP_NORTH(0x03F) /* RXBB Bias 4 (north) */ -#define B2062_N_RXBB_BIAS5 B43_LP_NORTH(0x040) /* RXBB Bias 5 (north) */ -#define B2062_N_RXBB_RSSI2 B43_LP_NORTH(0x041) /* RXBB RSSI 2 (north) */ -#define B2062_N_RXBB_RSSI3 B43_LP_NORTH(0x042) /* RXBB RSSI 3 (north) */ -#define B2062_N_RXBB_RSSI4 B43_LP_NORTH(0x043) /* RXBB RSSI 4 (north) */ -#define B2062_N_RXBB_RSSI5 B43_LP_NORTH(0x044) /* RXBB RSSI 5 (north) */ -#define B2062_N_TX_CTL0 B43_LP_NORTH(0x045) /* TX Control 0 (north) */ -#define B2062_N_TX_CTL1 B43_LP_NORTH(0x046) /* TX Control 1 (north) */ -#define B2062_N_TX_CTL2 B43_LP_NORTH(0x047) /* TX Control 2 (north) */ -#define B2062_N_TX_CTL3 B43_LP_NORTH(0x048) /* TX Control 3 (north) */ -#define B2062_N_TX_CTL4 B43_LP_NORTH(0x049) /* TX Control 4 (north) */ -#define B2062_N_TX_CTL5 B43_LP_NORTH(0x04A) /* TX Control 5 (north) */ -#define B2062_N_TX_CTL6 B43_LP_NORTH(0x04B) /* TX Control 6 (north) */ -#define B2062_N_TX_CTL7 B43_LP_NORTH(0x04C) /* TX Control 7 (north) */ -#define B2062_N_TX_CTL8 B43_LP_NORTH(0x04D) /* TX Control 8 (north) */ -#define B2062_N_TX_CTL9 B43_LP_NORTH(0x04E) /* TX Control 9 (north) */ -#define B2062_N_TX_CTL_A B43_LP_NORTH(0x04F) /* TX Control A (north) */ -#define B2062_N_TX_GC2G B43_LP_NORTH(0x050) /* TX GC2G (north) */ -#define B2062_N_TX_GC5G B43_LP_NORTH(0x051) /* TX GC5G (north) */ -#define B2062_N_TX_TUNE B43_LP_NORTH(0x052) /* TX Tune (north) */ -#define B2062_N_TX_PAD B43_LP_NORTH(0x053) /* TX PAD (north) */ -#define B2062_N_TX_PGA B43_LP_NORTH(0x054) /* TX PGA (north) */ -#define B2062_N_TX_PADAUX B43_LP_NORTH(0x055) /* TX PADAUX (north) */ -#define B2062_N_TX_PGAAUX B43_LP_NORTH(0x056) /* TX PGAAUX (north) */ -#define B2062_N_TSSI_CTL0 B43_LP_NORTH(0x057) /* TSSI Control 0 (north) */ -#define B2062_N_TSSI_CTL1 B43_LP_NORTH(0x058) /* TSSI Control 1 (north) */ -#define B2062_N_TSSI_CTL2 B43_LP_NORTH(0x059) /* TSSI Control 2 (north) */ -#define B2062_N_IQ_CALIB_CTL0 B43_LP_NORTH(0x05A) /* IQ Calibration Control 0 (north) */ -#define B2062_N_IQ_CALIB_CTL1 B43_LP_NORTH(0x05B) /* IQ Calibration Control 1 (north) */ -#define B2062_N_IQ_CALIB_CTL2 B43_LP_NORTH(0x05C) /* IQ Calibration Control 2 (north) */ -#define B2062_N_CALIB_TS B43_LP_NORTH(0x05D) /* Calibration TS (north) */ -#define B2062_N_CALIB_CTL0 B43_LP_NORTH(0x05E) /* Calibration Control 0 (north) */ -#define B2062_N_CALIB_CTL1 B43_LP_NORTH(0x05F) /* Calibration Control 1 (north) */ -#define B2062_N_CALIB_CTL2 B43_LP_NORTH(0x060) /* Calibration Control 2 (north) */ -#define B2062_N_CALIB_CTL3 B43_LP_NORTH(0x061) /* Calibration Control 3 (north) */ -#define B2062_N_CALIB_CTL4 B43_LP_NORTH(0x062) /* Calibration Control 4 (north) */ -#define B2062_N_CALIB_DBG0 B43_LP_NORTH(0x063) /* Calibration Debug 0 (north) */ -#define B2062_N_CALIB_DBG1 B43_LP_NORTH(0x064) /* Calibration Debug 1 (north) */ -#define B2062_N_CALIB_DBG2 B43_LP_NORTH(0x065) /* Calibration Debug 2 (north) */ -#define B2062_N_CALIB_DBG3 B43_LP_NORTH(0x066) /* Calibration Debug 3 (north) */ -#define B2062_N_PSENSE_CTL0 B43_LP_NORTH(0x069) /* PSENSE Control 0 (north) */ -#define B2062_N_PSENSE_CTL1 B43_LP_NORTH(0x06A) /* PSENSE Control 1 (north) */ -#define B2062_N_PSENSE_CTL2 B43_LP_NORTH(0x06B) /* PSENSE Control 2 (north) */ -#define B2062_N_TEST_BUF0 B43_LP_NORTH(0x06C) /* TEST BUF0 (north) */ - -/*** Broadcom 2062 SOUTH radio registers ***/ -#define B2062_S_COMM1 B43_LP_SOUTH(0x000) /* Common 01 (south) */ -#define B2062_S_RADIO_ID_CODE B43_LP_SOUTH(0x001) /* Radio ID code (south) */ -#define B2062_S_COMM2 B43_LP_SOUTH(0x002) /* Common 02 (south) */ -#define B2062_S_COMM3 B43_LP_SOUTH(0x003) /* Common 03 (south) */ -#define B2062_S_COMM4 B43_LP_SOUTH(0x004) /* Common 04 (south) */ -#define B2062_S_COMM5 B43_LP_SOUTH(0x005) /* Common 05 (south) */ -#define B2062_S_COMM6 B43_LP_SOUTH(0x006) /* Common 06 (south) */ -#define B2062_S_COMM7 B43_LP_SOUTH(0x007) /* Common 07 (south) */ -#define B2062_S_COMM8 B43_LP_SOUTH(0x008) /* Common 08 (south) */ -#define B2062_S_COMM9 B43_LP_SOUTH(0x009) /* Common 09 (south) */ -#define B2062_S_COMM10 B43_LP_SOUTH(0x00A) /* Common 10 (south) */ -#define B2062_S_COMM11 B43_LP_SOUTH(0x00B) /* Common 11 (south) */ -#define B2062_S_COMM12 B43_LP_SOUTH(0x00C) /* Common 12 (south) */ -#define B2062_S_COMM13 B43_LP_SOUTH(0x00D) /* Common 13 (south) */ -#define B2062_S_COMM14 B43_LP_SOUTH(0x00E) /* Common 14 (south) */ -#define B2062_S_COMM15 B43_LP_SOUTH(0x00F) /* Common 15 (south) */ -#define B2062_S_PDS_CTL0 B43_LP_SOUTH(0x010) /* PDS Control 0 (south) */ -#define B2062_S_PDS_CTL1 B43_LP_SOUTH(0x011) /* PDS Control 1 (south) */ -#define B2062_S_PDS_CTL2 B43_LP_SOUTH(0x012) /* PDS Control 2 (south) */ -#define B2062_S_PDS_CTL3 B43_LP_SOUTH(0x013) /* PDS Control 3 (south) */ -#define B2062_S_BG_CTL0 B43_LP_SOUTH(0x014) /* BG Control 0 (south) */ -#define B2062_S_BG_CTL1 B43_LP_SOUTH(0x015) /* BG Control 1 (south) */ -#define B2062_S_BG_CTL2 B43_LP_SOUTH(0x016) /* BG Control 2 (south) */ -#define B2062_S_LGENG_CTL0 B43_LP_SOUTH(0x017) /* LGENG Control 00 (south) */ -#define B2062_S_LGENG_CTL1 B43_LP_SOUTH(0x018) /* LGENG Control 01 (south) */ -#define B2062_S_LGENG_CTL2 B43_LP_SOUTH(0x019) /* LGENG Control 02 (south) */ -#define B2062_S_LGENG_CTL3 B43_LP_SOUTH(0x01A) /* LGENG Control 03 (south) */ -#define B2062_S_LGENG_CTL4 B43_LP_SOUTH(0x01B) /* LGENG Control 04 (south) */ -#define B2062_S_LGENG_CTL5 B43_LP_SOUTH(0x01C) /* LGENG Control 05 (south) */ -#define B2062_S_LGENG_CTL6 B43_LP_SOUTH(0x01D) /* LGENG Control 06 (south) */ -#define B2062_S_LGENG_CTL7 B43_LP_SOUTH(0x01E) /* LGENG Control 07 (south) */ -#define B2062_S_LGENG_CTL8 B43_LP_SOUTH(0x01F) /* LGENG Control 08 (south) */ -#define B2062_S_LGENG_CTL9 B43_LP_SOUTH(0x020) /* LGENG Control 09 (south) */ -#define B2062_S_LGENG_CTL10 B43_LP_SOUTH(0x021) /* LGENG Control 10 (south) */ -#define B2062_S_LGENG_CTL11 B43_LP_SOUTH(0x022) /* LGENG Control 11 (south) */ -#define B2062_S_REFPLL_CTL0 B43_LP_SOUTH(0x023) /* REFPLL Control 00 (south) */ -#define B2062_S_REFPLL_CTL1 B43_LP_SOUTH(0x024) /* REFPLL Control 01 (south) */ -#define B2062_S_REFPLL_CTL2 B43_LP_SOUTH(0x025) /* REFPLL Control 02 (south) */ -#define B2062_S_REFPLL_CTL3 B43_LP_SOUTH(0x026) /* REFPLL Control 03 (south) */ -#define B2062_S_REFPLL_CTL4 B43_LP_SOUTH(0x027) /* REFPLL Control 04 (south) */ -#define B2062_S_REFPLL_CTL5 B43_LP_SOUTH(0x028) /* REFPLL Control 05 (south) */ -#define B2062_S_REFPLL_CTL6 B43_LP_SOUTH(0x029) /* REFPLL Control 06 (south) */ -#define B2062_S_REFPLL_CTL7 B43_LP_SOUTH(0x02A) /* REFPLL Control 07 (south) */ -#define B2062_S_REFPLL_CTL8 B43_LP_SOUTH(0x02B) /* REFPLL Control 08 (south) */ -#define B2062_S_REFPLL_CTL9 B43_LP_SOUTH(0x02C) /* REFPLL Control 09 (south) */ -#define B2062_S_REFPLL_CTL10 B43_LP_SOUTH(0x02D) /* REFPLL Control 10 (south) */ -#define B2062_S_REFPLL_CTL11 B43_LP_SOUTH(0x02E) /* REFPLL Control 11 (south) */ -#define B2062_S_REFPLL_CTL12 B43_LP_SOUTH(0x02F) /* REFPLL Control 12 (south) */ -#define B2062_S_REFPLL_CTL13 B43_LP_SOUTH(0x030) /* REFPLL Control 13 (south) */ -#define B2062_S_REFPLL_CTL14 B43_LP_SOUTH(0x031) /* REFPLL Control 14 (south) */ -#define B2062_S_REFPLL_CTL15 B43_LP_SOUTH(0x032) /* REFPLL Control 15 (south) */ -#define B2062_S_REFPLL_CTL16 B43_LP_SOUTH(0x033) /* REFPLL Control 16 (south) */ -#define B2062_S_RFPLL_CTL0 B43_LP_SOUTH(0x034) /* RFPLL Control 00 (south) */ -#define B2062_S_RFPLL_CTL1 B43_LP_SOUTH(0x035) /* RFPLL Control 01 (south) */ -#define B2062_S_RFPLL_CTL2 B43_LP_SOUTH(0x036) /* RFPLL Control 02 (south) */ -#define B2062_S_RFPLL_CTL3 B43_LP_SOUTH(0x037) /* RFPLL Control 03 (south) */ -#define B2062_S_RFPLL_CTL4 B43_LP_SOUTH(0x038) /* RFPLL Control 04 (south) */ -#define B2062_S_RFPLL_CTL5 B43_LP_SOUTH(0x039) /* RFPLL Control 05 (south) */ -#define B2062_S_RFPLL_CTL6 B43_LP_SOUTH(0x03A) /* RFPLL Control 06 (south) */ -#define B2062_S_RFPLL_CTL7 B43_LP_SOUTH(0x03B) /* RFPLL Control 07 (south) */ -#define B2062_S_RFPLL_CTL8 B43_LP_SOUTH(0x03C) /* RFPLL Control 08 (south) */ -#define B2062_S_RFPLL_CTL9 B43_LP_SOUTH(0x03D) /* RFPLL Control 09 (south) */ -#define B2062_S_RFPLL_CTL10 B43_LP_SOUTH(0x03E) /* RFPLL Control 10 (south) */ -#define B2062_S_RFPLL_CTL11 B43_LP_SOUTH(0x03F) /* RFPLL Control 11 (south) */ -#define B2062_S_RFPLL_CTL12 B43_LP_SOUTH(0x040) /* RFPLL Control 12 (south) */ -#define B2062_S_RFPLL_CTL13 B43_LP_SOUTH(0x041) /* RFPLL Control 13 (south) */ -#define B2062_S_RFPLL_CTL14 B43_LP_SOUTH(0x042) /* RFPLL Control 14 (south) */ -#define B2062_S_RFPLL_CTL15 B43_LP_SOUTH(0x043) /* RFPLL Control 15 (south) */ -#define B2062_S_RFPLL_CTL16 B43_LP_SOUTH(0x044) /* RFPLL Control 16 (south) */ -#define B2062_S_RFPLL_CTL17 B43_LP_SOUTH(0x045) /* RFPLL Control 17 (south) */ -#define B2062_S_RFPLL_CTL18 B43_LP_SOUTH(0x046) /* RFPLL Control 18 (south) */ -#define B2062_S_RFPLL_CTL19 B43_LP_SOUTH(0x047) /* RFPLL Control 19 (south) */ -#define B2062_S_RFPLL_CTL20 B43_LP_SOUTH(0x048) /* RFPLL Control 20 (south) */ -#define B2062_S_RFPLL_CTL21 B43_LP_SOUTH(0x049) /* RFPLL Control 21 (south) */ -#define B2062_S_RFPLL_CTL22 B43_LP_SOUTH(0x04A) /* RFPLL Control 22 (south) */ -#define B2062_S_RFPLL_CTL23 B43_LP_SOUTH(0x04B) /* RFPLL Control 23 (south) */ -#define B2062_S_RFPLL_CTL24 B43_LP_SOUTH(0x04C) /* RFPLL Control 24 (south) */ -#define B2062_S_RFPLL_CTL25 B43_LP_SOUTH(0x04D) /* RFPLL Control 25 (south) */ -#define B2062_S_RFPLL_CTL26 B43_LP_SOUTH(0x04E) /* RFPLL Control 26 (south) */ -#define B2062_S_RFPLL_CTL27 B43_LP_SOUTH(0x04F) /* RFPLL Control 27 (south) */ -#define B2062_S_RFPLL_CTL28 B43_LP_SOUTH(0x050) /* RFPLL Control 28 (south) */ -#define B2062_S_RFPLL_CTL29 B43_LP_SOUTH(0x051) /* RFPLL Control 29 (south) */ -#define B2062_S_RFPLL_CTL30 B43_LP_SOUTH(0x052) /* RFPLL Control 30 (south) */ -#define B2062_S_RFPLL_CTL31 B43_LP_SOUTH(0x053) /* RFPLL Control 31 (south) */ -#define B2062_S_RFPLL_CTL32 B43_LP_SOUTH(0x054) /* RFPLL Control 32 (south) */ -#define B2062_S_RFPLL_CTL33 B43_LP_SOUTH(0x055) /* RFPLL Control 33 (south) */ -#define B2062_S_RFPLL_CTL34 B43_LP_SOUTH(0x056) /* RFPLL Control 34 (south) */ -#define B2062_S_RXG_CNT0 B43_LP_SOUTH(0x057) /* RXG Counter 00 (south) */ -#define B2062_S_RXG_CNT1 B43_LP_SOUTH(0x058) /* RXG Counter 01 (south) */ -#define B2062_S_RXG_CNT2 B43_LP_SOUTH(0x059) /* RXG Counter 02 (south) */ -#define B2062_S_RXG_CNT3 B43_LP_SOUTH(0x05A) /* RXG Counter 03 (south) */ -#define B2062_S_RXG_CNT4 B43_LP_SOUTH(0x05B) /* RXG Counter 04 (south) */ -#define B2062_S_RXG_CNT5 B43_LP_SOUTH(0x05C) /* RXG Counter 05 (south) */ -#define B2062_S_RXG_CNT6 B43_LP_SOUTH(0x05D) /* RXG Counter 06 (south) */ -#define B2062_S_RXG_CNT7 B43_LP_SOUTH(0x05E) /* RXG Counter 07 (south) */ -#define B2062_S_RXG_CNT8 B43_LP_SOUTH(0x05F) /* RXG Counter 08 (south) */ -#define B2062_S_RXG_CNT9 B43_LP_SOUTH(0x060) /* RXG Counter 09 (south) */ -#define B2062_S_RXG_CNT10 B43_LP_SOUTH(0x061) /* RXG Counter 10 (south) */ -#define B2062_S_RXG_CNT11 B43_LP_SOUTH(0x062) /* RXG Counter 11 (south) */ -#define B2062_S_RXG_CNT12 B43_LP_SOUTH(0x063) /* RXG Counter 12 (south) */ -#define B2062_S_RXG_CNT13 B43_LP_SOUTH(0x064) /* RXG Counter 13 (south) */ -#define B2062_S_RXG_CNT14 B43_LP_SOUTH(0x065) /* RXG Counter 14 (south) */ -#define B2062_S_RXG_CNT15 B43_LP_SOUTH(0x066) /* RXG Counter 15 (south) */ -#define B2062_S_RXG_CNT16 B43_LP_SOUTH(0x067) /* RXG Counter 16 (south) */ -#define B2062_S_RXG_CNT17 B43_LP_SOUTH(0x068) /* RXG Counter 17 (south) */ - - - -/*** Broadcom 2063 radio registers ***/ -#define B2063_RADIO_ID_CODE B43_LP_RADIO(0x001) /* Radio ID code */ -#define B2063_COMM1 B43_LP_RADIO(0x000) /* Common 01 */ -#define B2063_COMM2 B43_LP_RADIO(0x002) /* Common 02 */ -#define B2063_COMM3 B43_LP_RADIO(0x003) /* Common 03 */ -#define B2063_COMM4 B43_LP_RADIO(0x004) /* Common 04 */ -#define B2063_COMM5 B43_LP_RADIO(0x005) /* Common 05 */ -#define B2063_COMM6 B43_LP_RADIO(0x006) /* Common 06 */ -#define B2063_COMM7 B43_LP_RADIO(0x007) /* Common 07 */ -#define B2063_COMM8 B43_LP_RADIO(0x008) /* Common 08 */ -#define B2063_COMM9 B43_LP_RADIO(0x009) /* Common 09 */ -#define B2063_COMM10 B43_LP_RADIO(0x00A) /* Common 10 */ -#define B2063_COMM11 B43_LP_RADIO(0x00B) /* Common 11 */ -#define B2063_COMM12 B43_LP_RADIO(0x00C) /* Common 12 */ -#define B2063_COMM13 B43_LP_RADIO(0x00D) /* Common 13 */ -#define B2063_COMM14 B43_LP_RADIO(0x00E) /* Common 14 */ -#define B2063_COMM15 B43_LP_RADIO(0x00F) /* Common 15 */ -#define B2063_COMM16 B43_LP_RADIO(0x010) /* Common 16 */ -#define B2063_COMM17 B43_LP_RADIO(0x011) /* Common 17 */ -#define B2063_COMM18 B43_LP_RADIO(0x012) /* Common 18 */ -#define B2063_COMM19 B43_LP_RADIO(0x013) /* Common 19 */ -#define B2063_COMM20 B43_LP_RADIO(0x014) /* Common 20 */ -#define B2063_COMM21 B43_LP_RADIO(0x015) /* Common 21 */ -#define B2063_COMM22 B43_LP_RADIO(0x016) /* Common 22 */ -#define B2063_COMM23 B43_LP_RADIO(0x017) /* Common 23 */ -#define B2063_COMM24 B43_LP_RADIO(0x018) /* Common 24 */ -#define B2063_PWR_SWITCH_CTL B43_LP_RADIO(0x019) /* POWER SWITCH Control */ -#define B2063_PLL_SP1 B43_LP_RADIO(0x01A) /* PLL SP 1 */ -#define B2063_PLL_SP2 B43_LP_RADIO(0x01B) /* PLL SP 2 */ -#define B2063_LOGEN_SP1 B43_LP_RADIO(0x01C) /* LOGEN SP 1 */ -#define B2063_LOGEN_SP2 B43_LP_RADIO(0x01D) /* LOGEN SP 2 */ -#define B2063_LOGEN_SP3 B43_LP_RADIO(0x01E) /* LOGEN SP 3 */ -#define B2063_LOGEN_SP4 B43_LP_RADIO(0x01F) /* LOGEN SP 4 */ -#define B2063_LOGEN_SP5 B43_LP_RADIO(0x020) /* LOGEN SP 5 */ -#define B2063_G_RX_SP1 B43_LP_RADIO(0x021) /* G RX SP 1 */ -#define B2063_G_RX_SP2 B43_LP_RADIO(0x022) /* G RX SP 2 */ -#define B2063_G_RX_SP3 B43_LP_RADIO(0x023) /* G RX SP 3 */ -#define B2063_G_RX_SP4 B43_LP_RADIO(0x024) /* G RX SP 4 */ -#define B2063_G_RX_SP5 B43_LP_RADIO(0x025) /* G RX SP 5 */ -#define B2063_G_RX_SP6 B43_LP_RADIO(0x026) /* G RX SP 6 */ -#define B2063_G_RX_SP7 B43_LP_RADIO(0x027) /* G RX SP 7 */ -#define B2063_G_RX_SP8 B43_LP_RADIO(0x028) /* G RX SP 8 */ -#define B2063_G_RX_SP9 B43_LP_RADIO(0x029) /* G RX SP 9 */ -#define B2063_G_RX_SP10 B43_LP_RADIO(0x02A) /* G RX SP 10 */ -#define B2063_G_RX_SP11 B43_LP_RADIO(0x02B) /* G RX SP 11 */ -#define B2063_A_RX_SP1 B43_LP_RADIO(0x02C) /* A RX SP 1 */ -#define B2063_A_RX_SP2 B43_LP_RADIO(0x02D) /* A RX SP 2 */ -#define B2063_A_RX_SP3 B43_LP_RADIO(0x02E) /* A RX SP 3 */ -#define B2063_A_RX_SP4 B43_LP_RADIO(0x02F) /* A RX SP 4 */ -#define B2063_A_RX_SP5 B43_LP_RADIO(0x030) /* A RX SP 5 */ -#define B2063_A_RX_SP6 B43_LP_RADIO(0x031) /* A RX SP 6 */ -#define B2063_A_RX_SP7 B43_LP_RADIO(0x032) /* A RX SP 7 */ -#define B2063_RX_BB_SP1 B43_LP_RADIO(0x033) /* RX BB SP 1 */ -#define B2063_RX_BB_SP2 B43_LP_RADIO(0x034) /* RX BB SP 2 */ -#define B2063_RX_BB_SP3 B43_LP_RADIO(0x035) /* RX BB SP 3 */ -#define B2063_RX_BB_SP4 B43_LP_RADIO(0x036) /* RX BB SP 4 */ -#define B2063_RX_BB_SP5 B43_LP_RADIO(0x037) /* RX BB SP 5 */ -#define B2063_RX_BB_SP6 B43_LP_RADIO(0x038) /* RX BB SP 6 */ -#define B2063_RX_BB_SP7 B43_LP_RADIO(0x039) /* RX BB SP 7 */ -#define B2063_RX_BB_SP8 B43_LP_RADIO(0x03A) /* RX BB SP 8 */ -#define B2063_TX_RF_SP1 B43_LP_RADIO(0x03B) /* TX RF SP 1 */ -#define B2063_TX_RF_SP2 B43_LP_RADIO(0x03C) /* TX RF SP 2 */ -#define B2063_TX_RF_SP3 B43_LP_RADIO(0x03D) /* TX RF SP 3 */ -#define B2063_TX_RF_SP4 B43_LP_RADIO(0x03E) /* TX RF SP 4 */ -#define B2063_TX_RF_SP5 B43_LP_RADIO(0x03F) /* TX RF SP 5 */ -#define B2063_TX_RF_SP6 B43_LP_RADIO(0x040) /* TX RF SP 6 */ -#define B2063_TX_RF_SP7 B43_LP_RADIO(0x041) /* TX RF SP 7 */ -#define B2063_TX_RF_SP8 B43_LP_RADIO(0x042) /* TX RF SP 8 */ -#define B2063_TX_RF_SP9 B43_LP_RADIO(0x043) /* TX RF SP 9 */ -#define B2063_TX_RF_SP10 B43_LP_RADIO(0x044) /* TX RF SP 10 */ -#define B2063_TX_RF_SP11 B43_LP_RADIO(0x045) /* TX RF SP 11 */ -#define B2063_TX_RF_SP12 B43_LP_RADIO(0x046) /* TX RF SP 12 */ -#define B2063_TX_RF_SP13 B43_LP_RADIO(0x047) /* TX RF SP 13 */ -#define B2063_TX_RF_SP14 B43_LP_RADIO(0x048) /* TX RF SP 14 */ -#define B2063_TX_RF_SP15 B43_LP_RADIO(0x049) /* TX RF SP 15 */ -#define B2063_TX_RF_SP16 B43_LP_RADIO(0x04A) /* TX RF SP 16 */ -#define B2063_TX_RF_SP17 B43_LP_RADIO(0x04B) /* TX RF SP 17 */ -#define B2063_PA_SP1 B43_LP_RADIO(0x04C) /* PA SP 1 */ -#define B2063_PA_SP2 B43_LP_RADIO(0x04D) /* PA SP 2 */ -#define B2063_PA_SP3 B43_LP_RADIO(0x04E) /* PA SP 3 */ -#define B2063_PA_SP4 B43_LP_RADIO(0x04F) /* PA SP 4 */ -#define B2063_PA_SP5 B43_LP_RADIO(0x050) /* PA SP 5 */ -#define B2063_PA_SP6 B43_LP_RADIO(0x051) /* PA SP 6 */ -#define B2063_PA_SP7 B43_LP_RADIO(0x052) /* PA SP 7 */ -#define B2063_TX_BB_SP1 B43_LP_RADIO(0x053) /* TX BB SP 1 */ -#define B2063_TX_BB_SP2 B43_LP_RADIO(0x054) /* TX BB SP 2 */ -#define B2063_TX_BB_SP3 B43_LP_RADIO(0x055) /* TX BB SP 3 */ -#define B2063_REG_SP1 B43_LP_RADIO(0x056) /* REG SP 1 */ -#define B2063_BANDGAP_CTL1 B43_LP_RADIO(0x057) /* BANDGAP Control 1 */ -#define B2063_BANDGAP_CTL2 B43_LP_RADIO(0x058) /* BANDGAP Control 2 */ -#define B2063_LPO_CTL1 B43_LP_RADIO(0x059) /* LPO Control 1 */ -#define B2063_RC_CALIB_CTL1 B43_LP_RADIO(0x05A) /* RC Calibration Control 1 */ -#define B2063_RC_CALIB_CTL2 B43_LP_RADIO(0x05B) /* RC Calibration Control 2 */ -#define B2063_RC_CALIB_CTL3 B43_LP_RADIO(0x05C) /* RC Calibration Control 3 */ -#define B2063_RC_CALIB_CTL4 B43_LP_RADIO(0x05D) /* RC Calibration Control 4 */ -#define B2063_RC_CALIB_CTL5 B43_LP_RADIO(0x05E) /* RC Calibration Control 5 */ -#define B2063_RC_CALIB_CTL6 B43_LP_RADIO(0x05F) /* RC Calibration Control 6 */ -#define B2063_RC_CALIB_CTL7 B43_LP_RADIO(0x060) /* RC Calibration Control 7 */ -#define B2063_RC_CALIB_CTL8 B43_LP_RADIO(0x061) /* RC Calibration Control 8 */ -#define B2063_RC_CALIB_CTL9 B43_LP_RADIO(0x062) /* RC Calibration Control 9 */ -#define B2063_RC_CALIB_CTL10 B43_LP_RADIO(0x063) /* RC Calibration Control 10 */ -#define B2063_PLL_JTAG_CALNRST B43_LP_RADIO(0x064) /* PLL JTAG CALNRST */ -#define B2063_PLL_JTAG_IN_PLL1 B43_LP_RADIO(0x065) /* PLL JTAG IN PLL 1 */ -#define B2063_PLL_JTAG_IN_PLL2 B43_LP_RADIO(0x066) /* PLL JTAG IN PLL 2 */ -#define B2063_PLL_JTAG_PLL_CP1 B43_LP_RADIO(0x067) /* PLL JTAG PLL CP 1 */ -#define B2063_PLL_JTAG_PLL_CP2 B43_LP_RADIO(0x068) /* PLL JTAG PLL CP 2 */ -#define B2063_PLL_JTAG_PLL_CP3 B43_LP_RADIO(0x069) /* PLL JTAG PLL CP 3 */ -#define B2063_PLL_JTAG_PLL_CP4 B43_LP_RADIO(0x06A) /* PLL JTAG PLL CP 4 */ -#define B2063_PLL_JTAG_PLL_CTL1 B43_LP_RADIO(0x06B) /* PLL JTAG PLL Control 1 */ -#define B2063_PLL_JTAG_PLL_LF1 B43_LP_RADIO(0x06C) /* PLL JTAG PLL LF 1 */ -#define B2063_PLL_JTAG_PLL_LF2 B43_LP_RADIO(0x06D) /* PLL JTAG PLL LF 2 */ -#define B2063_PLL_JTAG_PLL_LF3 B43_LP_RADIO(0x06E) /* PLL JTAG PLL LF 3 */ -#define B2063_PLL_JTAG_PLL_LF4 B43_LP_RADIO(0x06F) /* PLL JTAG PLL LF 4 */ -#define B2063_PLL_JTAG_PLL_SG1 B43_LP_RADIO(0x070) /* PLL JTAG PLL SG 1 */ -#define B2063_PLL_JTAG_PLL_SG2 B43_LP_RADIO(0x071) /* PLL JTAG PLL SG 2 */ -#define B2063_PLL_JTAG_PLL_SG3 B43_LP_RADIO(0x072) /* PLL JTAG PLL SG 3 */ -#define B2063_PLL_JTAG_PLL_SG4 B43_LP_RADIO(0x073) /* PLL JTAG PLL SG 4 */ -#define B2063_PLL_JTAG_PLL_SG5 B43_LP_RADIO(0x074) /* PLL JTAG PLL SG 5 */ -#define B2063_PLL_JTAG_PLL_VCO1 B43_LP_RADIO(0x075) /* PLL JTAG PLL VCO 1 */ -#define B2063_PLL_JTAG_PLL_VCO2 B43_LP_RADIO(0x076) /* PLL JTAG PLL VCO 2 */ -#define B2063_PLL_JTAG_PLL_VCO_CALIB1 B43_LP_RADIO(0x077) /* PLL JTAG PLL VCO Calibration 1 */ -#define B2063_PLL_JTAG_PLL_VCO_CALIB2 B43_LP_RADIO(0x078) /* PLL JTAG PLL VCO Calibration 2 */ -#define B2063_PLL_JTAG_PLL_VCO_CALIB3 B43_LP_RADIO(0x079) /* PLL JTAG PLL VCO Calibration 3 */ -#define B2063_PLL_JTAG_PLL_VCO_CALIB4 B43_LP_RADIO(0x07A) /* PLL JTAG PLL VCO Calibration 4 */ -#define B2063_PLL_JTAG_PLL_VCO_CALIB5 B43_LP_RADIO(0x07B) /* PLL JTAG PLL VCO Calibration 5 */ -#define B2063_PLL_JTAG_PLL_VCO_CALIB6 B43_LP_RADIO(0x07C) /* PLL JTAG PLL VCO Calibration 6 */ -#define B2063_PLL_JTAG_PLL_VCO_CALIB7 B43_LP_RADIO(0x07D) /* PLL JTAG PLL VCO Calibration 7 */ -#define B2063_PLL_JTAG_PLL_VCO_CALIB8 B43_LP_RADIO(0x07E) /* PLL JTAG PLL VCO Calibration 8 */ -#define B2063_PLL_JTAG_PLL_VCO_CALIB9 B43_LP_RADIO(0x07F) /* PLL JTAG PLL VCO Calibration 9 */ -#define B2063_PLL_JTAG_PLL_VCO_CALIB10 B43_LP_RADIO(0x080) /* PLL JTAG PLL VCO Calibration 10 */ -#define B2063_PLL_JTAG_PLL_XTAL_12 B43_LP_RADIO(0x081) /* PLL JTAG PLL XTAL 1 2 */ -#define B2063_PLL_JTAG_PLL_XTAL3 B43_LP_RADIO(0x082) /* PLL JTAG PLL XTAL 3 */ -#define B2063_LOGEN_ACL1 B43_LP_RADIO(0x083) /* LOGEN ACL 1 */ -#define B2063_LOGEN_ACL2 B43_LP_RADIO(0x084) /* LOGEN ACL 2 */ -#define B2063_LOGEN_ACL3 B43_LP_RADIO(0x085) /* LOGEN ACL 3 */ -#define B2063_LOGEN_ACL4 B43_LP_RADIO(0x086) /* LOGEN ACL 4 */ -#define B2063_LOGEN_ACL5 B43_LP_RADIO(0x087) /* LOGEN ACL 5 */ -#define B2063_LO_CALIB_INPUTS B43_LP_RADIO(0x088) /* LO Calibration INPUTS */ -#define B2063_LO_CALIB_CTL1 B43_LP_RADIO(0x089) /* LO Calibration Control 1 */ -#define B2063_LO_CALIB_CTL2 B43_LP_RADIO(0x08A) /* LO Calibration Control 2 */ -#define B2063_LO_CALIB_CTL3 B43_LP_RADIO(0x08B) /* LO Calibration Control 3 */ -#define B2063_LO_CALIB_WAITCNT B43_LP_RADIO(0x08C) /* LO Calibration WAITCNT */ -#define B2063_LO_CALIB_OVR1 B43_LP_RADIO(0x08D) /* LO Calibration OVR 1 */ -#define B2063_LO_CALIB_OVR2 B43_LP_RADIO(0x08E) /* LO Calibration OVR 2 */ -#define B2063_LO_CALIB_OVAL1 B43_LP_RADIO(0x08F) /* LO Calibration OVAL 1 */ -#define B2063_LO_CALIB_OVAL2 B43_LP_RADIO(0x090) /* LO Calibration OVAL 2 */ -#define B2063_LO_CALIB_OVAL3 B43_LP_RADIO(0x091) /* LO Calibration OVAL 3 */ -#define B2063_LO_CALIB_OVAL4 B43_LP_RADIO(0x092) /* LO Calibration OVAL 4 */ -#define B2063_LO_CALIB_OVAL5 B43_LP_RADIO(0x093) /* LO Calibration OVAL 5 */ -#define B2063_LO_CALIB_OVAL6 B43_LP_RADIO(0x094) /* LO Calibration OVAL 6 */ -#define B2063_LO_CALIB_OVAL7 B43_LP_RADIO(0x095) /* LO Calibration OVAL 7 */ -#define B2063_LO_CALIB_CALVLD1 B43_LP_RADIO(0x096) /* LO Calibration CALVLD 1 */ -#define B2063_LO_CALIB_CALVLD2 B43_LP_RADIO(0x097) /* LO Calibration CALVLD 2 */ -#define B2063_LO_CALIB_CVAL1 B43_LP_RADIO(0x098) /* LO Calibration CVAL 1 */ -#define B2063_LO_CALIB_CVAL2 B43_LP_RADIO(0x099) /* LO Calibration CVAL 2 */ -#define B2063_LO_CALIB_CVAL3 B43_LP_RADIO(0x09A) /* LO Calibration CVAL 3 */ -#define B2063_LO_CALIB_CVAL4 B43_LP_RADIO(0x09B) /* LO Calibration CVAL 4 */ -#define B2063_LO_CALIB_CVAL5 B43_LP_RADIO(0x09C) /* LO Calibration CVAL 5 */ -#define B2063_LO_CALIB_CVAL6 B43_LP_RADIO(0x09D) /* LO Calibration CVAL 6 */ -#define B2063_LO_CALIB_CVAL7 B43_LP_RADIO(0x09E) /* LO Calibration CVAL 7 */ -#define B2063_LOGEN_CALIB_EN B43_LP_RADIO(0x09F) /* LOGEN Calibration EN */ -#define B2063_LOGEN_PEAKDET1 B43_LP_RADIO(0x0A0) /* LOGEN PEAKDET 1 */ -#define B2063_LOGEN_RCCR1 B43_LP_RADIO(0x0A1) /* LOGEN RCCR 1 */ -#define B2063_LOGEN_VCOBUF1 B43_LP_RADIO(0x0A2) /* LOGEN VCOBUF 1 */ -#define B2063_LOGEN_MIXER1 B43_LP_RADIO(0x0A3) /* LOGEN MIXER 1 */ -#define B2063_LOGEN_MIXER2 B43_LP_RADIO(0x0A4) /* LOGEN MIXER 2 */ -#define B2063_LOGEN_BUF1 B43_LP_RADIO(0x0A5) /* LOGEN BUF 1 */ -#define B2063_LOGEN_BUF2 B43_LP_RADIO(0x0A6) /* LOGEN BUF 2 */ -#define B2063_LOGEN_DIV1 B43_LP_RADIO(0x0A7) /* LOGEN DIV 1 */ -#define B2063_LOGEN_DIV2 B43_LP_RADIO(0x0A8) /* LOGEN DIV 2 */ -#define B2063_LOGEN_DIV3 B43_LP_RADIO(0x0A9) /* LOGEN DIV 3 */ -#define B2063_LOGEN_CBUFRX1 B43_LP_RADIO(0x0AA) /* LOGEN CBUFRX 1 */ -#define B2063_LOGEN_CBUFRX2 B43_LP_RADIO(0x0AB) /* LOGEN CBUFRX 2 */ -#define B2063_LOGEN_CBUFTX1 B43_LP_RADIO(0x0AC) /* LOGEN CBUFTX 1 */ -#define B2063_LOGEN_CBUFTX2 B43_LP_RADIO(0x0AD) /* LOGEN CBUFTX 2 */ -#define B2063_LOGEN_IDAC1 B43_LP_RADIO(0x0AE) /* LOGEN IDAC 1 */ -#define B2063_LOGEN_SPARE1 B43_LP_RADIO(0x0AF) /* LOGEN SPARE 1 */ -#define B2063_LOGEN_SPARE2 B43_LP_RADIO(0x0B0) /* LOGEN SPARE 2 */ -#define B2063_LOGEN_SPARE3 B43_LP_RADIO(0x0B1) /* LOGEN SPARE 3 */ -#define B2063_G_RX_1ST1 B43_LP_RADIO(0x0B2) /* G RX 1ST 1 */ -#define B2063_G_RX_1ST2 B43_LP_RADIO(0x0B3) /* G RX 1ST 2 */ -#define B2063_G_RX_1ST3 B43_LP_RADIO(0x0B4) /* G RX 1ST 3 */ -#define B2063_G_RX_2ND1 B43_LP_RADIO(0x0B5) /* G RX 2ND 1 */ -#define B2063_G_RX_2ND2 B43_LP_RADIO(0x0B6) /* G RX 2ND 2 */ -#define B2063_G_RX_2ND3 B43_LP_RADIO(0x0B7) /* G RX 2ND 3 */ -#define B2063_G_RX_2ND4 B43_LP_RADIO(0x0B8) /* G RX 2ND 4 */ -#define B2063_G_RX_2ND5 B43_LP_RADIO(0x0B9) /* G RX 2ND 5 */ -#define B2063_G_RX_2ND6 B43_LP_RADIO(0x0BA) /* G RX 2ND 6 */ -#define B2063_G_RX_2ND7 B43_LP_RADIO(0x0BB) /* G RX 2ND 7 */ -#define B2063_G_RX_2ND8 B43_LP_RADIO(0x0BC) /* G RX 2ND 8 */ -#define B2063_G_RX_PS1 B43_LP_RADIO(0x0BD) /* G RX PS 1 */ -#define B2063_G_RX_PS2 B43_LP_RADIO(0x0BE) /* G RX PS 2 */ -#define B2063_G_RX_PS3 B43_LP_RADIO(0x0BF) /* G RX PS 3 */ -#define B2063_G_RX_PS4 B43_LP_RADIO(0x0C0) /* G RX PS 4 */ -#define B2063_G_RX_PS5 B43_LP_RADIO(0x0C1) /* G RX PS 5 */ -#define B2063_G_RX_MIX1 B43_LP_RADIO(0x0C2) /* G RX MIX 1 */ -#define B2063_G_RX_MIX2 B43_LP_RADIO(0x0C3) /* G RX MIX 2 */ -#define B2063_G_RX_MIX3 B43_LP_RADIO(0x0C4) /* G RX MIX 3 */ -#define B2063_G_RX_MIX4 B43_LP_RADIO(0x0C5) /* G RX MIX 4 */ -#define B2063_G_RX_MIX5 B43_LP_RADIO(0x0C6) /* G RX MIX 5 */ -#define B2063_G_RX_MIX6 B43_LP_RADIO(0x0C7) /* G RX MIX 6 */ -#define B2063_G_RX_MIX7 B43_LP_RADIO(0x0C8) /* G RX MIX 7 */ -#define B2063_G_RX_MIX8 B43_LP_RADIO(0x0C9) /* G RX MIX 8 */ -#define B2063_G_RX_PDET1 B43_LP_RADIO(0x0CA) /* G RX PDET 1 */ -#define B2063_G_RX_SPARES1 B43_LP_RADIO(0x0CB) /* G RX SPARES 1 */ -#define B2063_G_RX_SPARES2 B43_LP_RADIO(0x0CC) /* G RX SPARES 2 */ -#define B2063_G_RX_SPARES3 B43_LP_RADIO(0x0CD) /* G RX SPARES 3 */ -#define B2063_A_RX_1ST1 B43_LP_RADIO(0x0CE) /* A RX 1ST 1 */ -#define B2063_A_RX_1ST2 B43_LP_RADIO(0x0CF) /* A RX 1ST 2 */ -#define B2063_A_RX_1ST3 B43_LP_RADIO(0x0D0) /* A RX 1ST 3 */ -#define B2063_A_RX_1ST4 B43_LP_RADIO(0x0D1) /* A RX 1ST 4 */ -#define B2063_A_RX_1ST5 B43_LP_RADIO(0x0D2) /* A RX 1ST 5 */ -#define B2063_A_RX_2ND1 B43_LP_RADIO(0x0D3) /* A RX 2ND 1 */ -#define B2063_A_RX_2ND2 B43_LP_RADIO(0x0D4) /* A RX 2ND 2 */ -#define B2063_A_RX_2ND3 B43_LP_RADIO(0x0D5) /* A RX 2ND 3 */ -#define B2063_A_RX_2ND4 B43_LP_RADIO(0x0D6) /* A RX 2ND 4 */ -#define B2063_A_RX_2ND5 B43_LP_RADIO(0x0D7) /* A RX 2ND 5 */ -#define B2063_A_RX_2ND6 B43_LP_RADIO(0x0D8) /* A RX 2ND 6 */ -#define B2063_A_RX_2ND7 B43_LP_RADIO(0x0D9) /* A RX 2ND 7 */ -#define B2063_A_RX_PS1 B43_LP_RADIO(0x0DA) /* A RX PS 1 */ -#define B2063_A_RX_PS2 B43_LP_RADIO(0x0DB) /* A RX PS 2 */ -#define B2063_A_RX_PS3 B43_LP_RADIO(0x0DC) /* A RX PS 3 */ -#define B2063_A_RX_PS4 B43_LP_RADIO(0x0DD) /* A RX PS 4 */ -#define B2063_A_RX_PS5 B43_LP_RADIO(0x0DE) /* A RX PS 5 */ -#define B2063_A_RX_PS6 B43_LP_RADIO(0x0DF) /* A RX PS 6 */ -#define B2063_A_RX_MIX1 B43_LP_RADIO(0x0E0) /* A RX MIX 1 */ -#define B2063_A_RX_MIX2 B43_LP_RADIO(0x0E1) /* A RX MIX 2 */ -#define B2063_A_RX_MIX3 B43_LP_RADIO(0x0E2) /* A RX MIX 3 */ -#define B2063_A_RX_MIX4 B43_LP_RADIO(0x0E3) /* A RX MIX 4 */ -#define B2063_A_RX_MIX5 B43_LP_RADIO(0x0E4) /* A RX MIX 5 */ -#define B2063_A_RX_MIX6 B43_LP_RADIO(0x0E5) /* A RX MIX 6 */ -#define B2063_A_RX_MIX7 B43_LP_RADIO(0x0E6) /* A RX MIX 7 */ -#define B2063_A_RX_MIX8 B43_LP_RADIO(0x0E7) /* A RX MIX 8 */ -#define B2063_A_RX_PWRDET1 B43_LP_RADIO(0x0E8) /* A RX PWRDET 1 */ -#define B2063_A_RX_SPARE1 B43_LP_RADIO(0x0E9) /* A RX SPARE 1 */ -#define B2063_A_RX_SPARE2 B43_LP_RADIO(0x0EA) /* A RX SPARE 2 */ -#define B2063_A_RX_SPARE3 B43_LP_RADIO(0x0EB) /* A RX SPARE 3 */ -#define B2063_RX_TIA_CTL1 B43_LP_RADIO(0x0EC) /* RX TIA Control 1 */ -#define B2063_RX_TIA_CTL2 B43_LP_RADIO(0x0ED) /* RX TIA Control 2 */ -#define B2063_RX_TIA_CTL3 B43_LP_RADIO(0x0EE) /* RX TIA Control 3 */ -#define B2063_RX_TIA_CTL4 B43_LP_RADIO(0x0EF) /* RX TIA Control 4 */ -#define B2063_RX_TIA_CTL5 B43_LP_RADIO(0x0F0) /* RX TIA Control 5 */ -#define B2063_RX_TIA_CTL6 B43_LP_RADIO(0x0F1) /* RX TIA Control 6 */ -#define B2063_RX_BB_CTL1 B43_LP_RADIO(0x0F2) /* RX BB Control 1 */ -#define B2063_RX_BB_CTL2 B43_LP_RADIO(0x0F3) /* RX BB Control 2 */ -#define B2063_RX_BB_CTL3 B43_LP_RADIO(0x0F4) /* RX BB Control 3 */ -#define B2063_RX_BB_CTL4 B43_LP_RADIO(0x0F5) /* RX BB Control 4 */ -#define B2063_RX_BB_CTL5 B43_LP_RADIO(0x0F6) /* RX BB Control 5 */ -#define B2063_RX_BB_CTL6 B43_LP_RADIO(0x0F7) /* RX BB Control 6 */ -#define B2063_RX_BB_CTL7 B43_LP_RADIO(0x0F8) /* RX BB Control 7 */ -#define B2063_RX_BB_CTL8 B43_LP_RADIO(0x0F9) /* RX BB Control 8 */ -#define B2063_RX_BB_CTL9 B43_LP_RADIO(0x0FA) /* RX BB Control 9 */ -#define B2063_TX_RF_CTL1 B43_LP_RADIO(0x0FB) /* TX RF Control 1 */ -#define B2063_TX_RF_IDAC_LO_RF_I B43_LP_RADIO(0x0FC) /* TX RF IDAC LO RF I */ -#define B2063_TX_RF_IDAC_LO_RF_Q B43_LP_RADIO(0x0FD) /* TX RF IDAC LO RF Q */ -#define B2063_TX_RF_IDAC_LO_BB_I B43_LP_RADIO(0x0FE) /* TX RF IDAC LO BB I */ -#define B2063_TX_RF_IDAC_LO_BB_Q B43_LP_RADIO(0x0FF) /* TX RF IDAC LO BB Q */ -#define B2063_TX_RF_CTL2 B43_LP_RADIO(0x100) /* TX RF Control 2 */ -#define B2063_TX_RF_CTL3 B43_LP_RADIO(0x101) /* TX RF Control 3 */ -#define B2063_TX_RF_CTL4 B43_LP_RADIO(0x102) /* TX RF Control 4 */ -#define B2063_TX_RF_CTL5 B43_LP_RADIO(0x103) /* TX RF Control 5 */ -#define B2063_TX_RF_CTL6 B43_LP_RADIO(0x104) /* TX RF Control 6 */ -#define B2063_TX_RF_CTL7 B43_LP_RADIO(0x105) /* TX RF Control 7 */ -#define B2063_TX_RF_CTL8 B43_LP_RADIO(0x106) /* TX RF Control 8 */ -#define B2063_TX_RF_CTL9 B43_LP_RADIO(0x107) /* TX RF Control 9 */ -#define B2063_TX_RF_CTL10 B43_LP_RADIO(0x108) /* TX RF Control 10 */ -#define B2063_TX_RF_CTL14 B43_LP_RADIO(0x109) /* TX RF Control 14 */ -#define B2063_TX_RF_CTL15 B43_LP_RADIO(0x10A) /* TX RF Control 15 */ -#define B2063_PA_CTL1 B43_LP_RADIO(0x10B) /* PA Control 1 */ -#define B2063_PA_CTL2 B43_LP_RADIO(0x10C) /* PA Control 2 */ -#define B2063_PA_CTL3 B43_LP_RADIO(0x10D) /* PA Control 3 */ -#define B2063_PA_CTL4 B43_LP_RADIO(0x10E) /* PA Control 4 */ -#define B2063_PA_CTL5 B43_LP_RADIO(0x10F) /* PA Control 5 */ -#define B2063_PA_CTL6 B43_LP_RADIO(0x110) /* PA Control 6 */ -#define B2063_PA_CTL7 B43_LP_RADIO(0x111) /* PA Control 7 */ -#define B2063_PA_CTL8 B43_LP_RADIO(0x112) /* PA Control 8 */ -#define B2063_PA_CTL9 B43_LP_RADIO(0x113) /* PA Control 9 */ -#define B2063_PA_CTL10 B43_LP_RADIO(0x114) /* PA Control 10 */ -#define B2063_PA_CTL11 B43_LP_RADIO(0x115) /* PA Control 11 */ -#define B2063_PA_CTL12 B43_LP_RADIO(0x116) /* PA Control 12 */ -#define B2063_PA_CTL13 B43_LP_RADIO(0x117) /* PA Control 13 */ -#define B2063_TX_BB_CTL1 B43_LP_RADIO(0x118) /* TX BB Control 1 */ -#define B2063_TX_BB_CTL2 B43_LP_RADIO(0x119) /* TX BB Control 2 */ -#define B2063_TX_BB_CTL3 B43_LP_RADIO(0x11A) /* TX BB Control 3 */ -#define B2063_TX_BB_CTL4 B43_LP_RADIO(0x11B) /* TX BB Control 4 */ -#define B2063_GPIO_CTL1 B43_LP_RADIO(0x11C) /* GPIO Control 1 */ -#define B2063_VREG_CTL1 B43_LP_RADIO(0x11D) /* VREG Control 1 */ -#define B2063_AMUX_CTL1 B43_LP_RADIO(0x11E) /* AMUX Control 1 */ -#define B2063_IQ_CALIB_GVAR B43_LP_RADIO(0x11F) /* IQ Calibration GVAR */ -#define B2063_IQ_CALIB_CTL1 B43_LP_RADIO(0x120) /* IQ Calibration Control 1 */ -#define B2063_IQ_CALIB_CTL2 B43_LP_RADIO(0x121) /* IQ Calibration Control 2 */ -#define B2063_TEMPSENSE_CTL1 B43_LP_RADIO(0x122) /* TEMPSENSE Control 1 */ -#define B2063_TEMPSENSE_CTL2 B43_LP_RADIO(0x123) /* TEMPSENSE Control 2 */ -#define B2063_TX_RX_LOOPBACK1 B43_LP_RADIO(0x124) /* TX/RX LOOPBACK 1 */ -#define B2063_TX_RX_LOOPBACK2 B43_LP_RADIO(0x125) /* TX/RX LOOPBACK 2 */ -#define B2063_EXT_TSSI_CTL1 B43_LP_RADIO(0x126) /* EXT TSSI Control 1 */ -#define B2063_EXT_TSSI_CTL2 B43_LP_RADIO(0x127) /* EXT TSSI Control 2 */ -#define B2063_AFE_CTL B43_LP_RADIO(0x128) /* AFE Control */ - - - -enum b43_lpphy_txpctl_mode { - B43_LPPHY_TXPCTL_UNKNOWN = 0, - B43_LPPHY_TXPCTL_OFF, /* TX power control is OFF */ - B43_LPPHY_TXPCTL_SW, /* TX power control is set to Software */ - B43_LPPHY_TXPCTL_HW, /* TX power control is set to Hardware */ -}; - -struct b43_phy_lp { - /* Current TX power control mode. */ - enum b43_lpphy_txpctl_mode txpctl_mode; - - /* Transmit isolation medium band */ - u8 tx_isolation_med_band; - /* Transmit isolation low band */ - u8 tx_isolation_low_band; - /* Transmit isolation high band */ - u8 tx_isolation_hi_band; - - /* Max transmit power medium band */ - u16 max_tx_pwr_med_band; - /* Max transmit power low band */ - u16 max_tx_pwr_low_band; - /* Max transmit power high band */ - u16 max_tx_pwr_hi_band; - - /* FIXME What are these used for? */ - /* FIXME Is 15 the correct array size? */ - u16 tx_max_rate[15]; - u16 tx_max_ratel[15]; - u16 tx_max_rateh[15]; - - /* Transmit power arrays */ - s16 txpa[3], txpal[3], txpah[3]; - - /* Receive power offset */ - u8 rx_pwr_offset; - - /* TSSI transmit count */ - u16 tssi_tx_count; - /* TSSI index */ - u16 tssi_idx; /* FIXME initial value? */ - /* TSSI npt */ - u16 tssi_npt; /* FIXME initial value? */ - - /* Target TX frequency */ - u16 tgt_tx_freq; /* FIXME initial value? */ - - /* Transmit power index override */ - s8 tx_pwr_idx_over; /* FIXME initial value? */ - - /* RSSI vf */ - u8 rssi_vf; - /* RSSI vc */ - u8 rssi_vc; - /* RSSI gs */ - u8 rssi_gs; - - /* RC cap */ - u8 rc_cap; - /* BX arch */ - u8 bx_arch; - - /* Full calibration channel */ - u8 full_calib_chan; - - /* Transmit iqlocal best coeffs */ - bool tx_iqloc_best_coeffs_valid; - u8 tx_iqloc_best_coeffs[11]; - - /* Used for "Save/Restore Dig Filt State" */ - u16 dig_flt_state[9]; - - bool crs_usr_disable, crs_sys_disable; - - unsigned int pdiv; - - /* The channel we are tuned to */ - u8 channel; - - /* The active antenna diversity mode */ - int antenna; - - /* Frequency of the active TX tone */ - int tx_tone_freq; -}; - -enum tssi_mux_mode { - TSSI_MUX_PREPA, - TSSI_MUX_POSTPA, - TSSI_MUX_EXT, -}; - -struct b43_phy_operations; -extern const struct b43_phy_operations b43_phyops_lp; - -#endif /* LINUX_B43_PHY_LP_H_ */ diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c deleted file mode 100644 index 9f0bcf3b8414..000000000000 --- a/drivers/net/wireless/b43/phy_n.c +++ /dev/null @@ -1,6726 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11n PHY support - - Copyright (c) 2008 Michael Buesch - Copyright (c) 2010-2011 RafaÅ‚ MiÅ‚ecki - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include -#include -#include - -#include "b43.h" -#include "phy_n.h" -#include "tables_nphy.h" -#include "radio_2055.h" -#include "radio_2056.h" -#include "radio_2057.h" -#include "main.h" -#include "ppr.h" - -struct nphy_txgains { - u16 tx_lpf[2]; - u16 txgm[2]; - u16 pga[2]; - u16 pad[2]; - u16 ipa[2]; -}; - -struct nphy_iqcal_params { - u16 tx_lpf; - u16 txgm; - u16 pga; - u16 pad; - u16 ipa; - u16 cal_gain; - u16 ncorr[5]; -}; - -struct nphy_iq_est { - s32 iq0_prod; - u32 i0_pwr; - u32 q0_pwr; - s32 iq1_prod; - u32 i1_pwr; - u32 q1_pwr; -}; - -enum b43_nphy_rf_sequence { - B43_RFSEQ_RX2TX, - B43_RFSEQ_TX2RX, - B43_RFSEQ_RESET2RX, - B43_RFSEQ_UPDATE_GAINH, - B43_RFSEQ_UPDATE_GAINL, - B43_RFSEQ_UPDATE_GAINU, -}; - -enum n_rf_ctl_over_cmd { - N_RF_CTL_OVER_CMD_RXRF_PU = 0, - N_RF_CTL_OVER_CMD_RX_PU = 1, - N_RF_CTL_OVER_CMD_TX_PU = 2, - N_RF_CTL_OVER_CMD_RX_GAIN = 3, - N_RF_CTL_OVER_CMD_TX_GAIN = 4, -}; - -enum n_intc_override { - N_INTC_OVERRIDE_OFF = 0, - N_INTC_OVERRIDE_TRSW = 1, - N_INTC_OVERRIDE_PA = 2, - N_INTC_OVERRIDE_EXT_LNA_PU = 3, - N_INTC_OVERRIDE_EXT_LNA_GAIN = 4, -}; - -enum n_rssi_type { - N_RSSI_W1 = 0, - N_RSSI_W2, - N_RSSI_NB, - N_RSSI_IQ, - N_RSSI_TSSI_2G, - N_RSSI_TSSI_5G, - N_RSSI_TBD, -}; - -enum n_rail_type { - N_RAIL_I = 0, - N_RAIL_Q = 1, -}; - -static inline bool b43_nphy_ipa(struct b43_wldev *dev) -{ - enum ieee80211_band band = b43_current_band(dev->wl); - return ((dev->phy.n->ipa2g_on && band == IEEE80211_BAND_2GHZ) || - (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCoreGetState */ -static u8 b43_nphy_get_rx_core_state(struct b43_wldev *dev) -{ - return (b43_phy_read(dev, B43_NPHY_RFSEQCA) & B43_NPHY_RFSEQCA_RXEN) >> - B43_NPHY_RFSEQCA_RXEN_SHIFT; -} - -/************************************************** - * RF (just without b43_nphy_rf_ctl_intc_override) - **************************************************/ - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ForceRFSeq */ -static void b43_nphy_force_rf_sequence(struct b43_wldev *dev, - enum b43_nphy_rf_sequence seq) -{ - static const u16 trigger[] = { - [B43_RFSEQ_RX2TX] = B43_NPHY_RFSEQTR_RX2TX, - [B43_RFSEQ_TX2RX] = B43_NPHY_RFSEQTR_TX2RX, - [B43_RFSEQ_RESET2RX] = B43_NPHY_RFSEQTR_RST2RX, - [B43_RFSEQ_UPDATE_GAINH] = B43_NPHY_RFSEQTR_UPGH, - [B43_RFSEQ_UPDATE_GAINL] = B43_NPHY_RFSEQTR_UPGL, - [B43_RFSEQ_UPDATE_GAINU] = B43_NPHY_RFSEQTR_UPGU, - }; - int i; - u16 seq_mode = b43_phy_read(dev, B43_NPHY_RFSEQMODE); - - B43_WARN_ON(seq >= ARRAY_SIZE(trigger)); - - b43_phy_set(dev, B43_NPHY_RFSEQMODE, - B43_NPHY_RFSEQMODE_CAOVER | B43_NPHY_RFSEQMODE_TROVER); - b43_phy_set(dev, B43_NPHY_RFSEQTR, trigger[seq]); - for (i = 0; i < 200; i++) { - if (!(b43_phy_read(dev, B43_NPHY_RFSEQST) & trigger[seq])) - goto ok; - msleep(1); - } - b43err(dev->wl, "RF sequence status timeout\n"); -ok: - b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode); -} - -static void b43_nphy_rf_ctl_override_rev19(struct b43_wldev *dev, u16 field, - u16 value, u8 core, bool off, - u8 override_id) -{ - /* TODO */ -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverrideRev7 */ -static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field, - u16 value, u8 core, bool off, - u8 override) -{ - struct b43_phy *phy = &dev->phy; - const struct nphy_rf_control_override_rev7 *e; - u16 en_addrs[3][2] = { - { 0x0E7, 0x0EC }, { 0x342, 0x343 }, { 0x346, 0x347 } - }; - u16 en_addr; - u16 en_mask = field; - u16 val_addr; - u8 i; - - if (phy->rev >= 19 || phy->rev < 3) { - B43_WARN_ON(1); - return; - } - - /* Remember: we can get NULL! */ - e = b43_nphy_get_rf_ctl_over_rev7(dev, field, override); - - for (i = 0; i < 2; i++) { - if (override >= ARRAY_SIZE(en_addrs)) { - b43err(dev->wl, "Invalid override value %d\n", override); - return; - } - en_addr = en_addrs[override][i]; - - if (e) - val_addr = (i == 0) ? e->val_addr_core0 : e->val_addr_core1; - - if (off) { - b43_phy_mask(dev, en_addr, ~en_mask); - if (e) /* Do it safer, better than wl */ - b43_phy_mask(dev, val_addr, ~e->val_mask); - } else { - if (!core || (core & (1 << i))) { - b43_phy_set(dev, en_addr, en_mask); - if (e) - b43_phy_maskset(dev, val_addr, ~e->val_mask, (value << e->val_shift)); - } - } - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverideOneToMany */ -static void b43_nphy_rf_ctl_override_one_to_many(struct b43_wldev *dev, - enum n_rf_ctl_over_cmd cmd, - u16 value, u8 core, bool off) -{ - struct b43_phy *phy = &dev->phy; - u16 tmp; - - B43_WARN_ON(phy->rev < 7); - - switch (cmd) { - case N_RF_CTL_OVER_CMD_RXRF_PU: - b43_nphy_rf_ctl_override_rev7(dev, 0x20, value, core, off, 1); - b43_nphy_rf_ctl_override_rev7(dev, 0x10, value, core, off, 1); - b43_nphy_rf_ctl_override_rev7(dev, 0x08, value, core, off, 1); - break; - case N_RF_CTL_OVER_CMD_RX_PU: - b43_nphy_rf_ctl_override_rev7(dev, 0x4, value, core, off, 1); - b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1); - b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 1); - b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 2); - b43_nphy_rf_ctl_override_rev7(dev, 0x0800, 0, core, off, 1); - break; - case N_RF_CTL_OVER_CMD_TX_PU: - b43_nphy_rf_ctl_override_rev7(dev, 0x4, value, core, off, 0); - b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1); - b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 2); - b43_nphy_rf_ctl_override_rev7(dev, 0x0800, 1, core, off, 1); - break; - case N_RF_CTL_OVER_CMD_RX_GAIN: - tmp = value & 0xFF; - b43_nphy_rf_ctl_override_rev7(dev, 0x0800, tmp, core, off, 0); - tmp = value >> 8; - b43_nphy_rf_ctl_override_rev7(dev, 0x6000, tmp, core, off, 0); - break; - case N_RF_CTL_OVER_CMD_TX_GAIN: - tmp = value & 0x7FFF; - b43_nphy_rf_ctl_override_rev7(dev, 0x1000, tmp, core, off, 0); - tmp = value >> 14; - b43_nphy_rf_ctl_override_rev7(dev, 0x4000, tmp, core, off, 0); - break; - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverride */ -static void b43_nphy_rf_ctl_override(struct b43_wldev *dev, u16 field, - u16 value, u8 core, bool off) -{ - int i; - u8 index = fls(field); - u8 addr, en_addr, val_addr; - /* we expect only one bit set */ - B43_WARN_ON(field & (~(1 << (index - 1)))); - - if (dev->phy.rev >= 3) { - const struct nphy_rf_control_override_rev3 *rf_ctrl; - for (i = 0; i < 2; i++) { - if (index == 0 || index == 16) { - b43err(dev->wl, - "Unsupported RF Ctrl Override call\n"); - return; - } - - rf_ctrl = &tbl_rf_control_override_rev3[index - 1]; - en_addr = B43_PHY_N((i == 0) ? - rf_ctrl->en_addr0 : rf_ctrl->en_addr1); - val_addr = B43_PHY_N((i == 0) ? - rf_ctrl->val_addr0 : rf_ctrl->val_addr1); - - if (off) { - b43_phy_mask(dev, en_addr, ~(field)); - b43_phy_mask(dev, val_addr, - ~(rf_ctrl->val_mask)); - } else { - if (core == 0 || ((1 << i) & core)) { - b43_phy_set(dev, en_addr, field); - b43_phy_maskset(dev, val_addr, - ~(rf_ctrl->val_mask), - (value << rf_ctrl->val_shift)); - } - } - } - } else { - const struct nphy_rf_control_override_rev2 *rf_ctrl; - if (off) { - b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~(field)); - value = 0; - } else { - b43_phy_set(dev, B43_NPHY_RFCTL_OVER, field); - } - - for (i = 0; i < 2; i++) { - if (index <= 1 || index == 16) { - b43err(dev->wl, - "Unsupported RF Ctrl Override call\n"); - return; - } - - if (index == 2 || index == 10 || - (index >= 13 && index <= 15)) { - core = 1; - } - - rf_ctrl = &tbl_rf_control_override_rev2[index - 2]; - addr = B43_PHY_N((i == 0) ? - rf_ctrl->addr0 : rf_ctrl->addr1); - - if ((1 << i) & core) - b43_phy_maskset(dev, addr, ~(rf_ctrl->bmask), - (value << rf_ctrl->shift)); - - b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, - B43_NPHY_RFCTL_CMD_START); - udelay(1); - b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, 0xFFFE); - } - } -} - -static void b43_nphy_rf_ctl_intc_override_rev7(struct b43_wldev *dev, - enum n_intc_override intc_override, - u16 value, u8 core_sel) -{ - u16 reg, tmp, tmp2, val; - int core; - - /* TODO: What about rev19+? Revs 3+ and 7+ are a bit similar */ - - for (core = 0; core < 2; core++) { - if ((core_sel == 1 && core != 0) || - (core_sel == 2 && core != 1)) - continue; - - reg = (core == 0) ? B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2; - - switch (intc_override) { - case N_INTC_OVERRIDE_OFF: - b43_phy_write(dev, reg, 0); - b43_phy_mask(dev, 0x2ff, ~0x2000); - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); - break; - case N_INTC_OVERRIDE_TRSW: - b43_phy_maskset(dev, reg, ~0xC0, value << 6); - b43_phy_set(dev, reg, 0x400); - - b43_phy_mask(dev, 0x2ff, ~0xC000 & 0xFFFF); - b43_phy_set(dev, 0x2ff, 0x2000); - b43_phy_set(dev, 0x2ff, 0x0001); - break; - case N_INTC_OVERRIDE_PA: - tmp = 0x0030; - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - val = value << 5; - else - val = value << 4; - b43_phy_maskset(dev, reg, ~tmp, val); - b43_phy_set(dev, reg, 0x1000); - break; - case N_INTC_OVERRIDE_EXT_LNA_PU: - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - tmp = 0x0001; - tmp2 = 0x0004; - val = value; - } else { - tmp = 0x0004; - tmp2 = 0x0001; - val = value << 2; - } - b43_phy_maskset(dev, reg, ~tmp, val); - b43_phy_mask(dev, reg, ~tmp2); - break; - case N_INTC_OVERRIDE_EXT_LNA_GAIN: - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - tmp = 0x0002; - tmp2 = 0x0008; - val = value << 1; - } else { - tmp = 0x0008; - tmp2 = 0x0002; - val = value << 3; - } - b43_phy_maskset(dev, reg, ~tmp, val); - b43_phy_mask(dev, reg, ~tmp2); - break; - } - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlIntcOverride */ -static void b43_nphy_rf_ctl_intc_override(struct b43_wldev *dev, - enum n_intc_override intc_override, - u16 value, u8 core) -{ - u8 i, j; - u16 reg, tmp, val; - - if (dev->phy.rev >= 7) { - b43_nphy_rf_ctl_intc_override_rev7(dev, intc_override, value, - core); - return; - } - - B43_WARN_ON(dev->phy.rev < 3); - - for (i = 0; i < 2; i++) { - if ((core == 1 && i == 1) || (core == 2 && !i)) - continue; - - reg = (i == 0) ? - B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2; - b43_phy_set(dev, reg, 0x400); - - switch (intc_override) { - case N_INTC_OVERRIDE_OFF: - b43_phy_write(dev, reg, 0); - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); - break; - case N_INTC_OVERRIDE_TRSW: - if (!i) { - b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC1, - 0xFC3F, (value << 6)); - b43_phy_maskset(dev, B43_NPHY_TXF_40CO_B1S1, - 0xFFFE, 1); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, - B43_NPHY_RFCTL_CMD_START); - for (j = 0; j < 100; j++) { - if (!(b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_START)) { - j = 0; - break; - } - udelay(10); - } - if (j) - b43err(dev->wl, - "intc override timeout\n"); - b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1, - 0xFFFE); - } else { - b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC2, - 0xFC3F, (value << 6)); - b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, - 0xFFFE, 1); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, - B43_NPHY_RFCTL_CMD_RXTX); - for (j = 0; j < 100; j++) { - if (!(b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_RXTX)) { - j = 0; - break; - } - udelay(10); - } - if (j) - b43err(dev->wl, - "intc override timeout\n"); - b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, - 0xFFFE); - } - break; - case N_INTC_OVERRIDE_PA: - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - tmp = 0x0020; - val = value << 5; - } else { - tmp = 0x0010; - val = value << 4; - } - b43_phy_maskset(dev, reg, ~tmp, val); - break; - case N_INTC_OVERRIDE_EXT_LNA_PU: - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - tmp = 0x0001; - val = value; - } else { - tmp = 0x0004; - val = value << 2; - } - b43_phy_maskset(dev, reg, ~tmp, val); - break; - case N_INTC_OVERRIDE_EXT_LNA_GAIN: - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - tmp = 0x0002; - val = value << 1; - } else { - tmp = 0x0008; - val = value << 3; - } - b43_phy_maskset(dev, reg, ~tmp, val); - break; - } - } -} - -/************************************************** - * Various PHY ops - **************************************************/ - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */ -static void b43_nphy_write_clip_detection(struct b43_wldev *dev, - const u16 *clip_st) -{ - b43_phy_write(dev, B43_NPHY_C1_CLIP1THRES, clip_st[0]); - b43_phy_write(dev, B43_NPHY_C2_CLIP1THRES, clip_st[1]); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */ -static void b43_nphy_read_clip_detection(struct b43_wldev *dev, u16 *clip_st) -{ - clip_st[0] = b43_phy_read(dev, B43_NPHY_C1_CLIP1THRES); - clip_st[1] = b43_phy_read(dev, B43_NPHY_C2_CLIP1THRES); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/classifier */ -static u16 b43_nphy_classifier(struct b43_wldev *dev, u16 mask, u16 val) -{ - u16 tmp; - - if (dev->dev->core_rev == 16) - b43_mac_suspend(dev); - - tmp = b43_phy_read(dev, B43_NPHY_CLASSCTL); - tmp &= (B43_NPHY_CLASSCTL_CCKEN | B43_NPHY_CLASSCTL_OFDMEN | - B43_NPHY_CLASSCTL_WAITEDEN); - tmp &= ~mask; - tmp |= (val & mask); - b43_phy_maskset(dev, B43_NPHY_CLASSCTL, 0xFFF8, tmp); - - if (dev->dev->core_rev == 16) - b43_mac_enable(dev); - - return tmp; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CCA */ -static void b43_nphy_reset_cca(struct b43_wldev *dev) -{ - u16 bbcfg; - - b43_phy_force_clock(dev, 1); - bbcfg = b43_phy_read(dev, B43_NPHY_BBCFG); - b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg | B43_NPHY_BBCFG_RSTCCA); - udelay(1); - b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg & ~B43_NPHY_BBCFG_RSTCCA); - b43_phy_force_clock(dev, 0); - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/carriersearch */ -static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = phy->n; - - if (enable) { - static const u16 clip[] = { 0xFFFF, 0xFFFF }; - if (nphy->deaf_count++ == 0) { - nphy->classifier_state = b43_nphy_classifier(dev, 0, 0); - b43_nphy_classifier(dev, 0x7, - B43_NPHY_CLASSCTL_WAITEDEN); - b43_nphy_read_clip_detection(dev, nphy->clip_state); - b43_nphy_write_clip_detection(dev, clip); - } - b43_nphy_reset_cca(dev); - } else { - if (--nphy->deaf_count == 0) { - b43_nphy_classifier(dev, 0x7, nphy->classifier_state); - b43_nphy_write_clip_detection(dev, nphy->clip_state); - } - } -} - -/* http://bcm-v4.sipsolutions.net/PHY/N/Read_Lpf_Bw_Ctl */ -static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset) -{ - if (!offset) - offset = b43_is_40mhz(dev) ? 0x159 : 0x154; - return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/AdjustLnaGainTbl */ -static void b43_nphy_adjust_lna_gain_table(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy = dev->phy.n; - - u8 i; - s16 tmp; - u16 data[4]; - s16 gain[2]; - u16 minmax[2]; - static const u16 lna_gain[4] = { -2, 10, 19, 25 }; - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 1); - - if (nphy->gain_boost) { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - gain[0] = 6; - gain[1] = 6; - } else { - tmp = 40370 - 315 * dev->phy.channel; - gain[0] = ((tmp >> 13) + ((tmp >> 12) & 1)); - tmp = 23242 - 224 * dev->phy.channel; - gain[1] = ((tmp >> 13) + ((tmp >> 12) & 1)); - } - } else { - gain[0] = 0; - gain[1] = 0; - } - - for (i = 0; i < 2; i++) { - if (nphy->elna_gain_config) { - data[0] = 19 + gain[i]; - data[1] = 25 + gain[i]; - data[2] = 25 + gain[i]; - data[3] = 25 + gain[i]; - } else { - data[0] = lna_gain[0] + gain[i]; - data[1] = lna_gain[1] + gain[i]; - data[2] = lna_gain[2] + gain[i]; - data[3] = lna_gain[3] + gain[i]; - } - b43_ntab_write_bulk(dev, B43_NTAB16(i, 8), 4, data); - - minmax[i] = 23 + gain[i]; - } - - b43_phy_maskset(dev, B43_NPHY_C1_MINMAX_GAIN, ~B43_NPHY_C1_MINGAIN, - minmax[0] << B43_NPHY_C1_MINGAIN_SHIFT); - b43_phy_maskset(dev, B43_NPHY_C2_MINMAX_GAIN, ~B43_NPHY_C2_MINGAIN, - minmax[1] << B43_NPHY_C2_MINGAIN_SHIFT); - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 0); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRfSeq */ -static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd, - u8 *events, u8 *delays, u8 length) -{ - struct b43_phy_n *nphy = dev->phy.n; - u8 i; - u8 end = (dev->phy.rev >= 3) ? 0x1F : 0x0F; - u16 offset1 = cmd << 4; - u16 offset2 = offset1 + 0x80; - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, true); - - b43_ntab_write_bulk(dev, B43_NTAB8(7, offset1), length, events); - b43_ntab_write_bulk(dev, B43_NTAB8(7, offset2), length, delays); - - for (i = length; i < 16; i++) { - b43_ntab_write(dev, B43_NTAB8(7, offset1 + i), end); - b43_ntab_write(dev, B43_NTAB8(7, offset2 + i), 1); - } - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, false); -} - -/************************************************** - * Radio 0x2057 - **************************************************/ - -static void b43_radio_2057_chantab_upload(struct b43_wldev *dev, - const struct b43_nphy_chantabent_rev7 *e_r7, - const struct b43_nphy_chantabent_rev7_2g *e_r7_2g) -{ - if (e_r7_2g) { - b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7_2g->radio_vcocal_countval0); - b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7_2g->radio_vcocal_countval1); - b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7_2g->radio_rfpll_refmaster_sparextalsize); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7_2g->radio_rfpll_loopfilter_r1); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7_2g->radio_rfpll_loopfilter_c2); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7_2g->radio_rfpll_loopfilter_c1); - b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7_2g->radio_cp_kpd_idac); - b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7_2g->radio_rfpll_mmd0); - b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7_2g->radio_rfpll_mmd1); - b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7_2g->radio_vcobuf_tune); - b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7_2g->radio_logen_mx2g_tune); - b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7_2g->radio_logen_indbuf2g_tune); - b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7_2g->radio_txmix2g_tune_boost_pu_core0); - b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7_2g->radio_pad2g_tune_pus_core0); - b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7_2g->radio_lna2g_tune_core0); - b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7_2g->radio_txmix2g_tune_boost_pu_core1); - b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7_2g->radio_pad2g_tune_pus_core1); - b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7_2g->radio_lna2g_tune_core1); - - } else { - b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7->radio_vcocal_countval0); - b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7->radio_vcocal_countval1); - b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7->radio_rfpll_refmaster_sparextalsize); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7->radio_rfpll_loopfilter_r1); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7->radio_rfpll_loopfilter_c2); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7->radio_rfpll_loopfilter_c1); - b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7->radio_cp_kpd_idac); - b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7->radio_rfpll_mmd0); - b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7->radio_rfpll_mmd1); - b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7->radio_vcobuf_tune); - b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7->radio_logen_mx2g_tune); - b43_radio_write(dev, R2057_LOGEN_MX5G_TUNE, e_r7->radio_logen_mx5g_tune); - b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7->radio_logen_indbuf2g_tune); - b43_radio_write(dev, R2057_LOGEN_INDBUF5G_TUNE, e_r7->radio_logen_indbuf5g_tune); - b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7->radio_txmix2g_tune_boost_pu_core0); - b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7->radio_pad2g_tune_pus_core0); - b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE0, e_r7->radio_pga_boost_tune_core0); - b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE0, e_r7->radio_txmix5g_boost_tune_core0); - b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE0, e_r7->radio_pad5g_tune_misc_pus_core0); - b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7->radio_lna2g_tune_core0); - b43_radio_write(dev, R2057_LNA5G_TUNE_CORE0, e_r7->radio_lna5g_tune_core0); - b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7->radio_txmix2g_tune_boost_pu_core1); - b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7->radio_pad2g_tune_pus_core1); - b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE1, e_r7->radio_pga_boost_tune_core1); - b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE1, e_r7->radio_txmix5g_boost_tune_core1); - b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE1, e_r7->radio_pad5g_tune_misc_pus_core1); - b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7->radio_lna2g_tune_core1); - b43_radio_write(dev, R2057_LNA5G_TUNE_CORE1, e_r7->radio_lna5g_tune_core1); - } -} - -static void b43_radio_2057_setup(struct b43_wldev *dev, - const struct b43_nphy_chantabent_rev7 *tabent_r7, - const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g) -{ - struct b43_phy *phy = &dev->phy; - - b43_radio_2057_chantab_upload(dev, tabent_r7, tabent_r7_2g); - - switch (phy->radio_rev) { - case 0 ... 4: - case 6: - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x3f); - b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8); - } else { - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x1f); - b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8); - } - break; - case 9: /* e.g. PHY rev 16 */ - b43_radio_write(dev, R2057_LOGEN_PTAT_RESETS, 0x20); - b43_radio_write(dev, R2057_VCOBUF_IDACS, 0x18); - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - b43_radio_write(dev, R2057_LOGEN_PTAT_RESETS, 0x38); - b43_radio_write(dev, R2057_VCOBUF_IDACS, 0x0f); - - if (b43_is_40mhz(dev)) { - /* TODO */ - } else { - b43_radio_write(dev, - R2057_PAD_BIAS_FILTER_BWS_CORE0, - 0x3c); - b43_radio_write(dev, - R2057_PAD_BIAS_FILTER_BWS_CORE1, - 0x3c); - } - } - break; - case 14: /* 2 GHz only */ - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x1b); - b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x1f); - b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x1f); - break; - } - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - u16 txmix2g_tune_boost_pu = 0; - u16 pad2g_tune_pus = 0; - - if (b43_nphy_ipa(dev)) { - switch (phy->radio_rev) { - case 9: - txmix2g_tune_boost_pu = 0x0041; - /* TODO */ - break; - case 14: - txmix2g_tune_boost_pu = 0x21; - pad2g_tune_pus = 0x23; - break; - } - } - - if (txmix2g_tune_boost_pu) - b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, - txmix2g_tune_boost_pu); - if (pad2g_tune_pus) - b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, - pad2g_tune_pus); - if (txmix2g_tune_boost_pu) - b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, - txmix2g_tune_boost_pu); - if (pad2g_tune_pus) - b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, - pad2g_tune_pus); - } - - usleep_range(50, 100); - - /* VCO calibration */ - b43_radio_mask(dev, R2057_RFPLL_MISC_EN, ~0x01); - b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x04); - b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x4); - b43_radio_set(dev, R2057_RFPLL_MISC_EN, 0x01); - usleep_range(300, 600); -} - -/* Calibrate resistors in LPF of PLL? - * http://bcm-v4.sipsolutions.net/PHY/radio205x_rcal - */ -static u8 b43_radio_2057_rcal(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - u16 saved_regs_phy[12]; - u16 saved_regs_phy_rf[6]; - u16 saved_regs_radio[2] = { }; - static const u16 phy_to_store[] = { - B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2, - B43_NPHY_RFCTL_LUT_TRSW_LO1, B43_NPHY_RFCTL_LUT_TRSW_LO2, - B43_NPHY_RFCTL_RXG1, B43_NPHY_RFCTL_RXG2, - B43_NPHY_RFCTL_TXG1, B43_NPHY_RFCTL_TXG2, - B43_NPHY_REV7_RF_CTL_MISC_REG3, B43_NPHY_REV7_RF_CTL_MISC_REG4, - B43_NPHY_REV7_RF_CTL_MISC_REG5, B43_NPHY_REV7_RF_CTL_MISC_REG6, - }; - static const u16 phy_to_store_rf[] = { - B43_NPHY_REV3_RFCTL_OVER0, B43_NPHY_REV3_RFCTL_OVER1, - B43_NPHY_REV7_RF_CTL_OVER3, B43_NPHY_REV7_RF_CTL_OVER4, - B43_NPHY_REV7_RF_CTL_OVER5, B43_NPHY_REV7_RF_CTL_OVER6, - }; - u16 tmp; - int i; - - /* Save */ - for (i = 0; i < ARRAY_SIZE(phy_to_store); i++) - saved_regs_phy[i] = b43_phy_read(dev, phy_to_store[i]); - for (i = 0; i < ARRAY_SIZE(phy_to_store_rf); i++) - saved_regs_phy_rf[i] = b43_phy_read(dev, phy_to_store_rf[i]); - - /* Set */ - for (i = 0; i < ARRAY_SIZE(phy_to_store); i++) - b43_phy_write(dev, phy_to_store[i], 0); - b43_phy_write(dev, B43_NPHY_REV3_RFCTL_OVER0, 0x07ff); - b43_phy_write(dev, B43_NPHY_REV3_RFCTL_OVER1, 0x07ff); - b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x07ff); - b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER4, 0x07ff); - b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER5, 0x007f); - b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER6, 0x007f); - - switch (phy->radio_rev) { - case 5: - b43_phy_mask(dev, B43_NPHY_REV7_RF_CTL_OVER3, ~0x2); - udelay(10); - b43_radio_set(dev, R2057_IQTEST_SEL_PU, 0x1); - b43_radio_maskset(dev, R2057v7_IQTEST_SEL_PU2, ~0x2, 0x1); - break; - case 9: - b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x2); - b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_MISC_REG3, 0x2); - saved_regs_radio[0] = b43_radio_read(dev, R2057_IQTEST_SEL_PU); - b43_radio_write(dev, R2057_IQTEST_SEL_PU, 0x11); - break; - case 14: - saved_regs_radio[0] = b43_radio_read(dev, R2057_IQTEST_SEL_PU); - saved_regs_radio[1] = b43_radio_read(dev, R2057v7_IQTEST_SEL_PU2); - b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_MISC_REG3, 0x2); - b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x2); - b43_radio_write(dev, R2057v7_IQTEST_SEL_PU2, 0x2); - b43_radio_write(dev, R2057_IQTEST_SEL_PU, 0x1); - break; - } - - /* Enable */ - b43_radio_set(dev, R2057_RCAL_CONFIG, 0x1); - udelay(10); - - /* Start */ - b43_radio_set(dev, R2057_RCAL_CONFIG, 0x2); - usleep_range(100, 200); - - /* Stop */ - b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x2); - - /* Wait and check for result */ - if (!b43_radio_wait_value(dev, R2057_RCAL_STATUS, 1, 1, 100, 1000000)) { - b43err(dev->wl, "Radio 0x2057 rcal timeout\n"); - return 0; - } - tmp = b43_radio_read(dev, R2057_RCAL_STATUS) & 0x3E; - - /* Disable */ - b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x1); - - /* Restore */ - for (i = 0; i < ARRAY_SIZE(phy_to_store_rf); i++) - b43_phy_write(dev, phy_to_store_rf[i], saved_regs_phy_rf[i]); - for (i = 0; i < ARRAY_SIZE(phy_to_store); i++) - b43_phy_write(dev, phy_to_store[i], saved_regs_phy[i]); - - switch (phy->radio_rev) { - case 0 ... 4: - case 6: - b43_radio_maskset(dev, R2057_TEMPSENSE_CONFIG, ~0x3C, tmp); - b43_radio_maskset(dev, R2057_BANDGAP_RCAL_TRIM, ~0xF0, - tmp << 2); - break; - case 5: - b43_radio_mask(dev, R2057_IPA2G_CASCONV_CORE0, ~0x1); - b43_radio_mask(dev, R2057v7_IQTEST_SEL_PU2, ~0x2); - break; - case 9: - b43_radio_write(dev, R2057_IQTEST_SEL_PU, saved_regs_radio[0]); - break; - case 14: - b43_radio_write(dev, R2057_IQTEST_SEL_PU, saved_regs_radio[0]); - b43_radio_write(dev, R2057v7_IQTEST_SEL_PU2, saved_regs_radio[1]); - break; - } - - return tmp & 0x3e; -} - -/* Calibrate the internal RC oscillator? - * http://bcm-v4.sipsolutions.net/PHY/radio2057_rccal - */ -static u16 b43_radio_2057_rccal(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - bool special = (phy->radio_rev == 3 || phy->radio_rev == 4 || - phy->radio_rev == 6); - u16 tmp; - - /* Setup cal */ - if (special) { - b43_radio_write(dev, R2057_RCCAL_MASTER, 0x61); - b43_radio_write(dev, R2057_RCCAL_TRC0, 0xC0); - } else { - b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x61); - b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE9); - } - b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); - - /* Start, wait, stop */ - b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); - if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500, - 5000000)) - b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n"); - usleep_range(35, 70); - b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); - usleep_range(70, 140); - - /* Setup cal */ - if (special) { - b43_radio_write(dev, R2057_RCCAL_MASTER, 0x69); - b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0); - } else { - b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x69); - b43_radio_write(dev, R2057_RCCAL_TRC0, 0xD5); - } - b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); - - /* Start, wait, stop */ - usleep_range(35, 70); - b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); - usleep_range(70, 140); - if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500, - 5000000)) - b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n"); - usleep_range(35, 70); - b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); - usleep_range(70, 140); - - /* Setup cal */ - if (special) { - b43_radio_write(dev, R2057_RCCAL_MASTER, 0x73); - b43_radio_write(dev, R2057_RCCAL_X1, 0x28); - b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0); - } else { - b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x73); - b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); - b43_radio_write(dev, R2057_RCCAL_TRC0, 0x99); - } - - /* Start, wait, stop */ - usleep_range(35, 70); - b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); - usleep_range(70, 140); - if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500, - 5000000)) { - b43err(dev->wl, "Radio 0x2057 rcal timeout\n"); - return 0; - } - tmp = b43_radio_read(dev, R2057_RCCAL_DONE_OSCCAP); - usleep_range(35, 70); - b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); - usleep_range(70, 140); - - if (special) - b43_radio_mask(dev, R2057_RCCAL_MASTER, ~0x1); - else - b43_radio_mask(dev, R2057v7_RCCAL_MASTER, ~0x1); - - return tmp; -} - -static void b43_radio_2057_init_pre(struct b43_wldev *dev) -{ - b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_CHIP0PU); - /* Maybe wl meant to reset and set (order?) RFCTL_CMD_OEPORFORCE? */ - b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_OEPORFORCE); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_OEPORFORCE); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_CHIP0PU); -} - -static void b43_radio_2057_init_post(struct b43_wldev *dev) -{ - b43_radio_set(dev, R2057_XTALPUOVR_PINCTRL, 0x1); - - if (0) /* FIXME: Is this BCM43217 specific? */ - b43_radio_set(dev, R2057_XTALPUOVR_PINCTRL, 0x2); - - b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x78); - b43_radio_set(dev, R2057_XTAL_CONFIG2, 0x80); - mdelay(2); - b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x78); - b43_radio_mask(dev, R2057_XTAL_CONFIG2, ~0x80); - - if (dev->phy.do_full_init) { - b43_radio_2057_rcal(dev); - b43_radio_2057_rccal(dev); - } - b43_radio_mask(dev, R2057_RFPLL_MASTER, ~0x8); -} - -/* http://bcm-v4.sipsolutions.net/802.11/Radio/2057/Init */ -static void b43_radio_2057_init(struct b43_wldev *dev) -{ - b43_radio_2057_init_pre(dev); - r2057_upload_inittabs(dev); - b43_radio_2057_init_post(dev); -} - -/************************************************** - * Radio 0x2056 - **************************************************/ - -static void b43_chantab_radio_2056_upload(struct b43_wldev *dev, - const struct b43_nphy_channeltab_entry_rev3 *e) -{ - b43_radio_write(dev, B2056_SYN_PLL_VCOCAL1, e->radio_syn_pll_vcocal1); - b43_radio_write(dev, B2056_SYN_PLL_VCOCAL2, e->radio_syn_pll_vcocal2); - b43_radio_write(dev, B2056_SYN_PLL_REFDIV, e->radio_syn_pll_refdiv); - b43_radio_write(dev, B2056_SYN_PLL_MMD2, e->radio_syn_pll_mmd2); - b43_radio_write(dev, B2056_SYN_PLL_MMD1, e->radio_syn_pll_mmd1); - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, - e->radio_syn_pll_loopfilter1); - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, - e->radio_syn_pll_loopfilter2); - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER3, - e->radio_syn_pll_loopfilter3); - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, - e->radio_syn_pll_loopfilter4); - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER5, - e->radio_syn_pll_loopfilter5); - b43_radio_write(dev, B2056_SYN_RESERVED_ADDR27, - e->radio_syn_reserved_addr27); - b43_radio_write(dev, B2056_SYN_RESERVED_ADDR28, - e->radio_syn_reserved_addr28); - b43_radio_write(dev, B2056_SYN_RESERVED_ADDR29, - e->radio_syn_reserved_addr29); - b43_radio_write(dev, B2056_SYN_LOGEN_VCOBUF1, - e->radio_syn_logen_vcobuf1); - b43_radio_write(dev, B2056_SYN_LOGEN_MIXER2, e->radio_syn_logen_mixer2); - b43_radio_write(dev, B2056_SYN_LOGEN_BUF3, e->radio_syn_logen_buf3); - b43_radio_write(dev, B2056_SYN_LOGEN_BUF4, e->radio_syn_logen_buf4); - - b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAA_TUNE, - e->radio_rx0_lnaa_tune); - b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAG_TUNE, - e->radio_rx0_lnag_tune); - - b43_radio_write(dev, B2056_TX0 | B2056_TX_INTPAA_BOOST_TUNE, - e->radio_tx0_intpaa_boost_tune); - b43_radio_write(dev, B2056_TX0 | B2056_TX_INTPAG_BOOST_TUNE, - e->radio_tx0_intpag_boost_tune); - b43_radio_write(dev, B2056_TX0 | B2056_TX_PADA_BOOST_TUNE, - e->radio_tx0_pada_boost_tune); - b43_radio_write(dev, B2056_TX0 | B2056_TX_PADG_BOOST_TUNE, - e->radio_tx0_padg_boost_tune); - b43_radio_write(dev, B2056_TX0 | B2056_TX_PGAA_BOOST_TUNE, - e->radio_tx0_pgaa_boost_tune); - b43_radio_write(dev, B2056_TX0 | B2056_TX_PGAG_BOOST_TUNE, - e->radio_tx0_pgag_boost_tune); - b43_radio_write(dev, B2056_TX0 | B2056_TX_MIXA_BOOST_TUNE, - e->radio_tx0_mixa_boost_tune); - b43_radio_write(dev, B2056_TX0 | B2056_TX_MIXG_BOOST_TUNE, - e->radio_tx0_mixg_boost_tune); - - b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAA_TUNE, - e->radio_rx1_lnaa_tune); - b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAG_TUNE, - e->radio_rx1_lnag_tune); - - b43_radio_write(dev, B2056_TX1 | B2056_TX_INTPAA_BOOST_TUNE, - e->radio_tx1_intpaa_boost_tune); - b43_radio_write(dev, B2056_TX1 | B2056_TX_INTPAG_BOOST_TUNE, - e->radio_tx1_intpag_boost_tune); - b43_radio_write(dev, B2056_TX1 | B2056_TX_PADA_BOOST_TUNE, - e->radio_tx1_pada_boost_tune); - b43_radio_write(dev, B2056_TX1 | B2056_TX_PADG_BOOST_TUNE, - e->radio_tx1_padg_boost_tune); - b43_radio_write(dev, B2056_TX1 | B2056_TX_PGAA_BOOST_TUNE, - e->radio_tx1_pgaa_boost_tune); - b43_radio_write(dev, B2056_TX1 | B2056_TX_PGAG_BOOST_TUNE, - e->radio_tx1_pgag_boost_tune); - b43_radio_write(dev, B2056_TX1 | B2056_TX_MIXA_BOOST_TUNE, - e->radio_tx1_mixa_boost_tune); - b43_radio_write(dev, B2056_TX1 | B2056_TX_MIXG_BOOST_TUNE, - e->radio_tx1_mixg_boost_tune); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/Radio/2056Setup */ -static void b43_radio_2056_setup(struct b43_wldev *dev, - const struct b43_nphy_channeltab_entry_rev3 *e) -{ - struct b43_phy *phy = &dev->phy; - struct ssb_sprom *sprom = dev->dev->bus_sprom; - enum ieee80211_band band = b43_current_band(dev->wl); - u16 offset; - u8 i; - u16 bias, cbias; - u16 pag_boost, padg_boost, pgag_boost, mixg_boost; - u16 paa_boost, pada_boost, pgaa_boost, mixa_boost; - bool is_pkg_fab_smic; - - B43_WARN_ON(dev->phy.rev < 3); - - is_pkg_fab_smic = - ((dev->dev->chip_id == BCMA_CHIP_ID_BCM43224 || - dev->dev->chip_id == BCMA_CHIP_ID_BCM43225 || - dev->dev->chip_id == BCMA_CHIP_ID_BCM43421) && - dev->dev->chip_pkg == BCMA_PKG_ID_BCM43224_FAB_SMIC); - - b43_chantab_radio_2056_upload(dev, e); - b2056_upload_syn_pll_cp2(dev, band == IEEE80211_BAND_5GHZ); - - if (sprom->boardflags2_lo & B43_BFL2_GPLL_WAR && - b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F); - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1F); - if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 || - dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) { - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x14); - b43_radio_write(dev, B2056_SYN_PLL_CP2, 0); - } else { - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x0B); - b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x14); - } - } - if (sprom->boardflags2_hi & B43_BFH2_GPLL_WAR2 && - b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1f); - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1f); - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x0b); - b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x20); - } - if (sprom->boardflags2_lo & B43_BFL2_APLL_WAR && - b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F); - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1F); - b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x05); - b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x0C); - } - - if (dev->phy.n->ipa2g_on && band == IEEE80211_BAND_2GHZ) { - for (i = 0; i < 2; i++) { - offset = i ? B2056_TX1 : B2056_TX0; - if (dev->phy.rev >= 5) { - b43_radio_write(dev, - offset | B2056_TX_PADG_IDAC, 0xcc); - - if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 || - dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) { - bias = 0x40; - cbias = 0x45; - pag_boost = 0x5; - pgag_boost = 0x33; - mixg_boost = 0x55; - } else { - bias = 0x25; - cbias = 0x20; - if (is_pkg_fab_smic) { - bias = 0x2a; - cbias = 0x38; - } - pag_boost = 0x4; - pgag_boost = 0x03; - mixg_boost = 0x65; - } - padg_boost = 0x77; - - b43_radio_write(dev, - offset | B2056_TX_INTPAG_IMAIN_STAT, - bias); - b43_radio_write(dev, - offset | B2056_TX_INTPAG_IAUX_STAT, - bias); - b43_radio_write(dev, - offset | B2056_TX_INTPAG_CASCBIAS, - cbias); - b43_radio_write(dev, - offset | B2056_TX_INTPAG_BOOST_TUNE, - pag_boost); - b43_radio_write(dev, - offset | B2056_TX_PGAG_BOOST_TUNE, - pgag_boost); - b43_radio_write(dev, - offset | B2056_TX_PADG_BOOST_TUNE, - padg_boost); - b43_radio_write(dev, - offset | B2056_TX_MIXG_BOOST_TUNE, - mixg_boost); - } else { - bias = b43_is_40mhz(dev) ? 0x40 : 0x20; - b43_radio_write(dev, - offset | B2056_TX_INTPAG_IMAIN_STAT, - bias); - b43_radio_write(dev, - offset | B2056_TX_INTPAG_IAUX_STAT, - bias); - b43_radio_write(dev, - offset | B2056_TX_INTPAG_CASCBIAS, - 0x30); - } - b43_radio_write(dev, offset | B2056_TX_PA_SPARE1, 0xee); - } - } else if (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ) { - u16 freq = phy->chandef->chan->center_freq; - if (freq < 5100) { - paa_boost = 0xA; - pada_boost = 0x77; - pgaa_boost = 0xF; - mixa_boost = 0xF; - } else if (freq < 5340) { - paa_boost = 0x8; - pada_boost = 0x77; - pgaa_boost = 0xFB; - mixa_boost = 0xF; - } else if (freq < 5650) { - paa_boost = 0x0; - pada_boost = 0x77; - pgaa_boost = 0xB; - mixa_boost = 0xF; - } else { - paa_boost = 0x0; - pada_boost = 0x77; - if (freq != 5825) - pgaa_boost = -(freq - 18) / 36 + 168; - else - pgaa_boost = 6; - mixa_boost = 0xF; - } - - cbias = is_pkg_fab_smic ? 0x35 : 0x30; - - for (i = 0; i < 2; i++) { - offset = i ? B2056_TX1 : B2056_TX0; - - b43_radio_write(dev, - offset | B2056_TX_INTPAA_BOOST_TUNE, paa_boost); - b43_radio_write(dev, - offset | B2056_TX_PADA_BOOST_TUNE, pada_boost); - b43_radio_write(dev, - offset | B2056_TX_PGAA_BOOST_TUNE, pgaa_boost); - b43_radio_write(dev, - offset | B2056_TX_MIXA_BOOST_TUNE, mixa_boost); - b43_radio_write(dev, - offset | B2056_TX_TXSPARE1, 0x30); - b43_radio_write(dev, - offset | B2056_TX_PA_SPARE2, 0xee); - b43_radio_write(dev, - offset | B2056_TX_PADA_CASCBIAS, 0x03); - b43_radio_write(dev, - offset | B2056_TX_INTPAA_IAUX_STAT, 0x30); - b43_radio_write(dev, - offset | B2056_TX_INTPAA_IMAIN_STAT, 0x30); - b43_radio_write(dev, - offset | B2056_TX_INTPAA_CASCBIAS, cbias); - } - } - - udelay(50); - /* VCO calibration */ - b43_radio_write(dev, B2056_SYN_PLL_VCOCAL12, 0x00); - b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x38); - b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x18); - b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x38); - b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x39); - udelay(300); -} - -static u8 b43_radio_2056_rcal(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - u16 mast2, tmp; - - if (phy->rev != 3) - return 0; - - mast2 = b43_radio_read(dev, B2056_SYN_PLL_MAST2); - b43_radio_write(dev, B2056_SYN_PLL_MAST2, mast2 | 0x7); - - udelay(10); - b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x01); - udelay(10); - b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x09); - - if (!b43_radio_wait_value(dev, B2056_SYN_RCAL_CODE_OUT, 0x80, 0x80, 100, - 1000000)) { - b43err(dev->wl, "Radio recalibration timeout\n"); - return 0; - } - - b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x01); - tmp = b43_radio_read(dev, B2056_SYN_RCAL_CODE_OUT); - b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x00); - - b43_radio_write(dev, B2056_SYN_PLL_MAST2, mast2); - - return tmp & 0x1f; -} - -static void b43_radio_init2056_pre(struct b43_wldev *dev) -{ - b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, - ~B43_NPHY_RFCTL_CMD_CHIP0PU); - /* Maybe wl meant to reset and set (order?) RFCTL_CMD_OEPORFORCE? */ - b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, - B43_NPHY_RFCTL_CMD_OEPORFORCE); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, - ~B43_NPHY_RFCTL_CMD_OEPORFORCE); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, - B43_NPHY_RFCTL_CMD_CHIP0PU); -} - -static void b43_radio_init2056_post(struct b43_wldev *dev) -{ - b43_radio_set(dev, B2056_SYN_COM_CTRL, 0xB); - b43_radio_set(dev, B2056_SYN_COM_PU, 0x2); - b43_radio_set(dev, B2056_SYN_COM_RESET, 0x2); - msleep(1); - b43_radio_mask(dev, B2056_SYN_COM_RESET, ~0x2); - b43_radio_mask(dev, B2056_SYN_PLL_MAST2, ~0xFC); - b43_radio_mask(dev, B2056_SYN_RCCAL_CTRL0, ~0x1); - if (dev->phy.do_full_init) - b43_radio_2056_rcal(dev); -} - -/* - * Initialize a Broadcom 2056 N-radio - * http://bcm-v4.sipsolutions.net/802.11/Radio/2056/Init - */ -static void b43_radio_init2056(struct b43_wldev *dev) -{ - b43_radio_init2056_pre(dev); - b2056_upload_inittabs(dev, 0, 0); - b43_radio_init2056_post(dev); -} - -/************************************************** - * Radio 0x2055 - **************************************************/ - -static void b43_chantab_radio_upload(struct b43_wldev *dev, - const struct b43_nphy_channeltab_entry_rev2 *e) -{ - b43_radio_write(dev, B2055_PLL_REF, e->radio_pll_ref); - b43_radio_write(dev, B2055_RF_PLLMOD0, e->radio_rf_pllmod0); - b43_radio_write(dev, B2055_RF_PLLMOD1, e->radio_rf_pllmod1); - b43_radio_write(dev, B2055_VCO_CAPTAIL, e->radio_vco_captail); - b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ - - b43_radio_write(dev, B2055_VCO_CAL1, e->radio_vco_cal1); - b43_radio_write(dev, B2055_VCO_CAL2, e->radio_vco_cal2); - b43_radio_write(dev, B2055_PLL_LFC1, e->radio_pll_lfc1); - b43_radio_write(dev, B2055_PLL_LFR1, e->radio_pll_lfr1); - b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ - - b43_radio_write(dev, B2055_PLL_LFC2, e->radio_pll_lfc2); - b43_radio_write(dev, B2055_LGBUF_CENBUF, e->radio_lgbuf_cenbuf); - b43_radio_write(dev, B2055_LGEN_TUNE1, e->radio_lgen_tune1); - b43_radio_write(dev, B2055_LGEN_TUNE2, e->radio_lgen_tune2); - b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ - - b43_radio_write(dev, B2055_C1_LGBUF_ATUNE, e->radio_c1_lgbuf_atune); - b43_radio_write(dev, B2055_C1_LGBUF_GTUNE, e->radio_c1_lgbuf_gtune); - b43_radio_write(dev, B2055_C1_RX_RFR1, e->radio_c1_rx_rfr1); - b43_radio_write(dev, B2055_C1_TX_PGAPADTN, e->radio_c1_tx_pgapadtn); - b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ - - b43_radio_write(dev, B2055_C1_TX_MXBGTRIM, e->radio_c1_tx_mxbgtrim); - b43_radio_write(dev, B2055_C2_LGBUF_ATUNE, e->radio_c2_lgbuf_atune); - b43_radio_write(dev, B2055_C2_LGBUF_GTUNE, e->radio_c2_lgbuf_gtune); - b43_radio_write(dev, B2055_C2_RX_RFR1, e->radio_c2_rx_rfr1); - b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ - - b43_radio_write(dev, B2055_C2_TX_PGAPADTN, e->radio_c2_tx_pgapadtn); - b43_radio_write(dev, B2055_C2_TX_MXBGTRIM, e->radio_c2_tx_mxbgtrim); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/Radio/2055Setup */ -static void b43_radio_2055_setup(struct b43_wldev *dev, - const struct b43_nphy_channeltab_entry_rev2 *e) -{ - B43_WARN_ON(dev->phy.rev >= 3); - - b43_chantab_radio_upload(dev, e); - udelay(50); - b43_radio_write(dev, B2055_VCO_CAL10, 0x05); - b43_radio_write(dev, B2055_VCO_CAL10, 0x45); - b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ - b43_radio_write(dev, B2055_VCO_CAL10, 0x65); - udelay(300); -} - -static void b43_radio_init2055_pre(struct b43_wldev *dev) -{ - b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, - ~B43_NPHY_RFCTL_CMD_PORFORCE); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, - B43_NPHY_RFCTL_CMD_CHIP0PU | - B43_NPHY_RFCTL_CMD_OEPORFORCE); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, - B43_NPHY_RFCTL_CMD_PORFORCE); -} - -static void b43_radio_init2055_post(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy = dev->phy.n; - struct ssb_sprom *sprom = dev->dev->bus_sprom; - bool workaround = false; - - if (sprom->revision < 4) - workaround = (dev->dev->board_vendor != PCI_VENDOR_ID_BROADCOM - && dev->dev->board_type == SSB_BOARD_CB2_4321 - && dev->dev->board_rev >= 0x41); - else - workaround = - !(sprom->boardflags2_lo & B43_BFL2_RXBB_INT_REG_DIS); - - b43_radio_mask(dev, B2055_MASTER1, 0xFFF3); - if (workaround) { - b43_radio_mask(dev, B2055_C1_RX_BB_REG, 0x7F); - b43_radio_mask(dev, B2055_C2_RX_BB_REG, 0x7F); - } - b43_radio_maskset(dev, B2055_RRCCAL_NOPTSEL, 0xFFC0, 0x2C); - b43_radio_write(dev, B2055_CAL_MISC, 0x3C); - b43_radio_mask(dev, B2055_CAL_MISC, 0xFFBE); - b43_radio_set(dev, B2055_CAL_LPOCTL, 0x80); - b43_radio_set(dev, B2055_CAL_MISC, 0x1); - msleep(1); - b43_radio_set(dev, B2055_CAL_MISC, 0x40); - if (!b43_radio_wait_value(dev, B2055_CAL_COUT2, 0x80, 0x80, 10, 2000)) - b43err(dev->wl, "radio post init timeout\n"); - b43_radio_mask(dev, B2055_CAL_LPOCTL, 0xFF7F); - b43_switch_channel(dev, dev->phy.channel); - b43_radio_write(dev, B2055_C1_RX_BB_LPF, 0x9); - b43_radio_write(dev, B2055_C2_RX_BB_LPF, 0x9); - b43_radio_write(dev, B2055_C1_RX_BB_MIDACHP, 0x83); - b43_radio_write(dev, B2055_C2_RX_BB_MIDACHP, 0x83); - b43_radio_maskset(dev, B2055_C1_LNA_GAINBST, 0xFFF8, 0x6); - b43_radio_maskset(dev, B2055_C2_LNA_GAINBST, 0xFFF8, 0x6); - if (!nphy->gain_boost) { - b43_radio_set(dev, B2055_C1_RX_RFSPC1, 0x2); - b43_radio_set(dev, B2055_C2_RX_RFSPC1, 0x2); - } else { - b43_radio_mask(dev, B2055_C1_RX_RFSPC1, 0xFFFD); - b43_radio_mask(dev, B2055_C2_RX_RFSPC1, 0xFFFD); - } - udelay(2); -} - -/* - * Initialize a Broadcom 2055 N-radio - * http://bcm-v4.sipsolutions.net/802.11/Radio/2055/Init - */ -static void b43_radio_init2055(struct b43_wldev *dev) -{ - b43_radio_init2055_pre(dev); - if (b43_status(dev) < B43_STAT_INITIALIZED) { - /* Follow wl, not specs. Do not force uploading all regs */ - b2055_upload_inittab(dev, 0, 0); - } else { - bool ghz5 = b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ; - b2055_upload_inittab(dev, ghz5, 0); - } - b43_radio_init2055_post(dev); -} - -/************************************************** - * Samples - **************************************************/ - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/LoadSampleTable */ -static int b43_nphy_load_samples(struct b43_wldev *dev, - struct b43_c32 *samples, u16 len) { - struct b43_phy_n *nphy = dev->phy.n; - u16 i; - u32 *data; - - data = kzalloc(len * sizeof(u32), GFP_KERNEL); - if (!data) { - b43err(dev->wl, "allocation for samples loading failed\n"); - return -ENOMEM; - } - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 1); - - for (i = 0; i < len; i++) { - data[i] = (samples[i].i & 0x3FF << 10); - data[i] |= samples[i].q & 0x3FF; - } - b43_ntab_write_bulk(dev, B43_NTAB32(17, 0), len, data); - - kfree(data); - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 0); - return 0; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GenLoadSamples */ -static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max, - bool test) -{ - int i; - u16 bw, len, rot, angle; - struct b43_c32 *samples; - - bw = b43_is_40mhz(dev) ? 40 : 20; - len = bw << 3; - - if (test) { - if (b43_phy_read(dev, B43_NPHY_BBCFG) & B43_NPHY_BBCFG_RSTRX) - bw = 82; - else - bw = 80; - - if (b43_is_40mhz(dev)) - bw <<= 1; - - len = bw << 1; - } - - samples = kcalloc(len, sizeof(struct b43_c32), GFP_KERNEL); - if (!samples) { - b43err(dev->wl, "allocation for samples generation failed\n"); - return 0; - } - rot = (((freq * 36) / bw) << 16) / 100; - angle = 0; - - for (i = 0; i < len; i++) { - samples[i] = b43_cordic(angle); - angle += rot; - samples[i].q = CORDIC_CONVERT(samples[i].q * max); - samples[i].i = CORDIC_CONVERT(samples[i].i * max); - } - - i = b43_nphy_load_samples(dev, samples, len); - kfree(samples); - return (i < 0) ? 0 : len; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */ -static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, - u16 wait, bool iqmode, bool dac_test, - bool modify_bbmult) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - int i; - u16 seq_mode; - u32 tmp; - - b43_nphy_stay_in_carrier_search(dev, true); - - if (phy->rev >= 7) { - bool lpf_bw3, lpf_bw4; - - lpf_bw3 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER3) & 0x80; - lpf_bw4 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER4) & 0x80; - - if (lpf_bw3 || lpf_bw4) { - /* TODO */ - } else { - u16 value = b43_nphy_read_lpf_ctl(dev, 0); - if (phy->rev >= 19) - b43_nphy_rf_ctl_override_rev19(dev, 0x80, value, - 0, false, 1); - else - b43_nphy_rf_ctl_override_rev7(dev, 0x80, value, - 0, false, 1); - nphy->lpf_bw_overrode_for_sample_play = true; - } - } - - if ((nphy->bb_mult_save & 0x80000000) == 0) { - tmp = b43_ntab_read(dev, B43_NTAB16(15, 87)); - nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000; - } - - if (modify_bbmult) { - tmp = !b43_is_40mhz(dev) ? 0x6464 : 0x4747; - b43_ntab_write(dev, B43_NTAB16(15, 87), tmp); - } - - b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1)); - - if (loops != 0xFFFF) - b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, (loops - 1)); - else - b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, loops); - - b43_phy_write(dev, B43_NPHY_SAMP_WAITCNT, wait); - - seq_mode = b43_phy_read(dev, B43_NPHY_RFSEQMODE); - - b43_phy_set(dev, B43_NPHY_RFSEQMODE, B43_NPHY_RFSEQMODE_CAOVER); - if (iqmode) { - b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF); - b43_phy_set(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8000); - } else { - tmp = dac_test ? 5 : 1; - b43_phy_write(dev, B43_NPHY_SAMP_CMD, tmp); - } - for (i = 0; i < 100; i++) { - if (!(b43_phy_read(dev, B43_NPHY_RFSEQST) & 1)) { - i = 0; - break; - } - udelay(10); - } - if (i) - b43err(dev->wl, "run samples timeout\n"); - - b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode); - - b43_nphy_stay_in_carrier_search(dev, false); -} - -/************************************************** - * RSSI - **************************************************/ - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ScaleOffsetRssi */ -static void b43_nphy_scale_offset_rssi(struct b43_wldev *dev, u16 scale, - s8 offset, u8 core, - enum n_rail_type rail, - enum n_rssi_type rssi_type) -{ - u16 tmp; - bool core1or5 = (core == 1) || (core == 5); - bool core2or5 = (core == 2) || (core == 5); - - offset = clamp_val(offset, -32, 31); - tmp = ((scale & 0x3F) << 8) | (offset & 0x3F); - - switch (rssi_type) { - case N_RSSI_NB: - if (core1or5 && rail == N_RAIL_I) - b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, tmp); - if (core1or5 && rail == N_RAIL_Q) - b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, tmp); - if (core2or5 && rail == N_RAIL_I) - b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, tmp); - if (core2or5 && rail == N_RAIL_Q) - b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, tmp); - break; - case N_RSSI_W1: - if (core1or5 && rail == N_RAIL_I) - b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, tmp); - if (core1or5 && rail == N_RAIL_Q) - b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, tmp); - if (core2or5 && rail == N_RAIL_I) - b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, tmp); - if (core2or5 && rail == N_RAIL_Q) - b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, tmp); - break; - case N_RSSI_W2: - if (core1or5 && rail == N_RAIL_I) - b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, tmp); - if (core1or5 && rail == N_RAIL_Q) - b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, tmp); - if (core2or5 && rail == N_RAIL_I) - b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, tmp); - if (core2or5 && rail == N_RAIL_Q) - b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, tmp); - break; - case N_RSSI_TBD: - if (core1or5 && rail == N_RAIL_I) - b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TBD, tmp); - if (core1or5 && rail == N_RAIL_Q) - b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TBD, tmp); - if (core2or5 && rail == N_RAIL_I) - b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TBD, tmp); - if (core2or5 && rail == N_RAIL_Q) - b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TBD, tmp); - break; - case N_RSSI_IQ: - if (core1or5 && rail == N_RAIL_I) - b43_phy_write(dev, B43_NPHY_RSSIMC_0I_PWRDET, tmp); - if (core1or5 && rail == N_RAIL_Q) - b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_PWRDET, tmp); - if (core2or5 && rail == N_RAIL_I) - b43_phy_write(dev, B43_NPHY_RSSIMC_1I_PWRDET, tmp); - if (core2or5 && rail == N_RAIL_Q) - b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_PWRDET, tmp); - break; - case N_RSSI_TSSI_2G: - if (core1or5) - b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TSSI, tmp); - if (core2or5) - b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TSSI, tmp); - break; - case N_RSSI_TSSI_5G: - if (core1or5) - b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TSSI, tmp); - if (core2or5) - b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TSSI, tmp); - break; - } -} - -static void b43_nphy_rssi_select_rev19(struct b43_wldev *dev, u8 code, - enum n_rssi_type rssi_type) -{ - /* TODO */ -} - -static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code, - enum n_rssi_type rssi_type) -{ - u8 i; - u16 reg, val; - - if (code == 0) { - b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, 0xFDFF); - b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, 0xFDFF); - b43_phy_mask(dev, B43_NPHY_AFECTL_C1, 0xFCFF); - b43_phy_mask(dev, B43_NPHY_AFECTL_C2, 0xFCFF); - b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S0, 0xFFDF); - b43_phy_mask(dev, B43_NPHY_TXF_40CO_B32S1, 0xFFDF); - b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0xFFC3); - b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0xFFC3); - } else { - for (i = 0; i < 2; i++) { - if ((code == 1 && i == 1) || (code == 2 && !i)) - continue; - - reg = (i == 0) ? - B43_NPHY_AFECTL_OVER1 : B43_NPHY_AFECTL_OVER; - b43_phy_maskset(dev, reg, 0xFDFF, 0x0200); - - if (rssi_type == N_RSSI_W1 || - rssi_type == N_RSSI_W2 || - rssi_type == N_RSSI_NB) { - reg = (i == 0) ? - B43_NPHY_AFECTL_C1 : - B43_NPHY_AFECTL_C2; - b43_phy_maskset(dev, reg, 0xFCFF, 0); - - reg = (i == 0) ? - B43_NPHY_RFCTL_LUT_TRSW_UP1 : - B43_NPHY_RFCTL_LUT_TRSW_UP2; - b43_phy_maskset(dev, reg, 0xFFC3, 0); - - if (rssi_type == N_RSSI_W1) - val = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? 4 : 8; - else if (rssi_type == N_RSSI_W2) - val = 16; - else - val = 32; - b43_phy_set(dev, reg, val); - - reg = (i == 0) ? - B43_NPHY_TXF_40CO_B1S0 : - B43_NPHY_TXF_40CO_B32S1; - b43_phy_set(dev, reg, 0x0020); - } else { - if (rssi_type == N_RSSI_TBD) - val = 0x0100; - else if (rssi_type == N_RSSI_IQ) - val = 0x0200; - else - val = 0x0300; - - reg = (i == 0) ? - B43_NPHY_AFECTL_C1 : - B43_NPHY_AFECTL_C2; - - b43_phy_maskset(dev, reg, 0xFCFF, val); - b43_phy_maskset(dev, reg, 0xF3FF, val << 2); - - if (rssi_type != N_RSSI_IQ && - rssi_type != N_RSSI_TBD) { - enum ieee80211_band band = - b43_current_band(dev->wl); - - if (dev->phy.rev < 7) { - if (b43_nphy_ipa(dev)) - val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE; - else - val = 0x11; - reg = (i == 0) ? B2056_TX0 : B2056_TX1; - reg |= B2056_TX_TX_SSI_MUX; - b43_radio_write(dev, reg, val); - } - - reg = (i == 0) ? - B43_NPHY_AFECTL_OVER1 : - B43_NPHY_AFECTL_OVER; - b43_phy_set(dev, reg, 0x0200); - } - } - } - } -} - -static void b43_nphy_rev2_rssi_select(struct b43_wldev *dev, u8 code, - enum n_rssi_type rssi_type) -{ - u16 val; - bool rssi_w1_w2_nb = false; - - switch (rssi_type) { - case N_RSSI_W1: - case N_RSSI_W2: - case N_RSSI_NB: - val = 0; - rssi_w1_w2_nb = true; - break; - case N_RSSI_TBD: - val = 1; - break; - case N_RSSI_IQ: - val = 2; - break; - default: - val = 3; - } - - val = (val << 12) | (val << 14); - b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, val); - b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, val); - - if (rssi_w1_w2_nb) { - b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO1, 0xFFCF, - (rssi_type + 1) << 4); - b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO2, 0xFFCF, - (rssi_type + 1) << 4); - } - - if (code == 0) { - b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x3000); - if (rssi_w1_w2_nb) { - b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, - ~(B43_NPHY_RFCTL_CMD_RXEN | - B43_NPHY_RFCTL_CMD_CORESEL)); - b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, - ~(0x1 << 12 | - 0x1 << 5 | - 0x1 << 1 | - 0x1)); - b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, - ~B43_NPHY_RFCTL_CMD_START); - udelay(20); - b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~0x1); - } - } else { - b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x3000); - if (rssi_w1_w2_nb) { - b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, - ~(B43_NPHY_RFCTL_CMD_RXEN | - B43_NPHY_RFCTL_CMD_CORESEL), - (B43_NPHY_RFCTL_CMD_RXEN | - code << B43_NPHY_RFCTL_CMD_CORESEL_SHIFT)); - b43_phy_set(dev, B43_NPHY_RFCTL_OVER, - (0x1 << 12 | - 0x1 << 5 | - 0x1 << 1 | - 0x1)); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, - B43_NPHY_RFCTL_CMD_START); - udelay(20); - b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~0x1); - } - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSISel */ -static void b43_nphy_rssi_select(struct b43_wldev *dev, u8 code, - enum n_rssi_type type) -{ - if (dev->phy.rev >= 19) - b43_nphy_rssi_select_rev19(dev, code, type); - else if (dev->phy.rev >= 3) - b43_nphy_rev3_rssi_select(dev, code, type); - else - b43_nphy_rev2_rssi_select(dev, code, type); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRssi2055Vcm */ -static void b43_nphy_set_rssi_2055_vcm(struct b43_wldev *dev, - enum n_rssi_type rssi_type, u8 *buf) -{ - int i; - for (i = 0; i < 2; i++) { - if (rssi_type == N_RSSI_NB) { - if (i == 0) { - b43_radio_maskset(dev, B2055_C1_B0NB_RSSIVCM, - 0xFC, buf[0]); - b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5, - 0xFC, buf[1]); - } else { - b43_radio_maskset(dev, B2055_C2_B0NB_RSSIVCM, - 0xFC, buf[2 * i]); - b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5, - 0xFC, buf[2 * i + 1]); - } - } else { - if (i == 0) - b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5, - 0xF3, buf[0] << 2); - else - b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5, - 0xF3, buf[2 * i + 1] << 2); - } - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PollRssi */ -static int b43_nphy_poll_rssi(struct b43_wldev *dev, enum n_rssi_type rssi_type, - s32 *buf, u8 nsamp) -{ - int i; - int out; - u16 save_regs_phy[9]; - u16 s[2]; - - /* TODO: rev7+ is treated like rev3+, what about rev19+? */ - - if (dev->phy.rev >= 3) { - save_regs_phy[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); - save_regs_phy[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); - save_regs_phy[2] = b43_phy_read(dev, - B43_NPHY_RFCTL_LUT_TRSW_UP1); - save_regs_phy[3] = b43_phy_read(dev, - B43_NPHY_RFCTL_LUT_TRSW_UP2); - save_regs_phy[4] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); - save_regs_phy[5] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); - save_regs_phy[6] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S0); - save_regs_phy[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B32S1); - save_regs_phy[8] = 0; - } else { - save_regs_phy[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); - save_regs_phy[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); - save_regs_phy[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); - save_regs_phy[3] = b43_phy_read(dev, B43_NPHY_RFCTL_CMD); - save_regs_phy[4] = b43_phy_read(dev, B43_NPHY_RFCTL_OVER); - save_regs_phy[5] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO1); - save_regs_phy[6] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO2); - save_regs_phy[7] = 0; - save_regs_phy[8] = 0; - } - - b43_nphy_rssi_select(dev, 5, rssi_type); - - if (dev->phy.rev < 2) { - save_regs_phy[8] = b43_phy_read(dev, B43_NPHY_GPIO_SEL); - b43_phy_write(dev, B43_NPHY_GPIO_SEL, 5); - } - - for (i = 0; i < 4; i++) - buf[i] = 0; - - for (i = 0; i < nsamp; i++) { - if (dev->phy.rev < 2) { - s[0] = b43_phy_read(dev, B43_NPHY_GPIO_LOOUT); - s[1] = b43_phy_read(dev, B43_NPHY_GPIO_HIOUT); - } else { - s[0] = b43_phy_read(dev, B43_NPHY_RSSI1); - s[1] = b43_phy_read(dev, B43_NPHY_RSSI2); - } - - buf[0] += ((s8)((s[0] & 0x3F) << 2)) >> 2; - buf[1] += ((s8)(((s[0] >> 8) & 0x3F) << 2)) >> 2; - buf[2] += ((s8)((s[1] & 0x3F) << 2)) >> 2; - buf[3] += ((s8)(((s[1] >> 8) & 0x3F) << 2)) >> 2; - } - out = (buf[0] & 0xFF) << 24 | (buf[1] & 0xFF) << 16 | - (buf[2] & 0xFF) << 8 | (buf[3] & 0xFF); - - if (dev->phy.rev < 2) - b43_phy_write(dev, B43_NPHY_GPIO_SEL, save_regs_phy[8]); - - if (dev->phy.rev >= 3) { - b43_phy_write(dev, B43_NPHY_AFECTL_C1, save_regs_phy[0]); - b43_phy_write(dev, B43_NPHY_AFECTL_C2, save_regs_phy[1]); - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, - save_regs_phy[2]); - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, - save_regs_phy[3]); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, save_regs_phy[4]); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[5]); - b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, save_regs_phy[6]); - b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, save_regs_phy[7]); - } else { - b43_phy_write(dev, B43_NPHY_AFECTL_C1, save_regs_phy[0]); - b43_phy_write(dev, B43_NPHY_AFECTL_C2, save_regs_phy[1]); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[2]); - b43_phy_write(dev, B43_NPHY_RFCTL_CMD, save_regs_phy[3]); - b43_phy_write(dev, B43_NPHY_RFCTL_OVER, save_regs_phy[4]); - b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO1, save_regs_phy[5]); - b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO2, save_regs_phy[6]); - } - - return out; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */ -static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - - u16 saved_regs_phy_rfctl[2]; - u16 saved_regs_phy[22]; - u16 regs_to_store_rev3[] = { - B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER, - B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2, - B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER, - B43_NPHY_TXF_40CO_B1S0, B43_NPHY_TXF_40CO_B32S1, - B43_NPHY_RFCTL_CMD, - B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2, - B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2 - }; - u16 regs_to_store_rev7[] = { - B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER, - B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2, - B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER, - B43_NPHY_REV7_RF_CTL_OVER3, B43_NPHY_REV7_RF_CTL_OVER4, - B43_NPHY_REV7_RF_CTL_OVER5, B43_NPHY_REV7_RF_CTL_OVER6, - 0x2ff, - B43_NPHY_TXF_40CO_B1S0, B43_NPHY_TXF_40CO_B32S1, - B43_NPHY_RFCTL_CMD, - B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2, - B43_NPHY_REV7_RF_CTL_MISC_REG3, B43_NPHY_REV7_RF_CTL_MISC_REG4, - B43_NPHY_REV7_RF_CTL_MISC_REG5, B43_NPHY_REV7_RF_CTL_MISC_REG6, - B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2 - }; - u16 *regs_to_store; - int regs_amount; - - u16 class; - - u16 clip_state[2]; - u16 clip_off[2] = { 0xFFFF, 0xFFFF }; - - u8 vcm_final = 0; - s32 offset[4]; - s32 results[8][4] = { }; - s32 results_min[4] = { }; - s32 poll_results[4] = { }; - - u16 *rssical_radio_regs = NULL; - u16 *rssical_phy_regs = NULL; - - u16 r; /* routing */ - u8 rx_core_state; - int core, i, j, vcm; - - if (dev->phy.rev >= 7) { - regs_to_store = regs_to_store_rev7; - regs_amount = ARRAY_SIZE(regs_to_store_rev7); - } else { - regs_to_store = regs_to_store_rev3; - regs_amount = ARRAY_SIZE(regs_to_store_rev3); - } - BUG_ON(regs_amount > ARRAY_SIZE(saved_regs_phy)); - - class = b43_nphy_classifier(dev, 0, 0); - b43_nphy_classifier(dev, 7, 4); - b43_nphy_read_clip_detection(dev, clip_state); - b43_nphy_write_clip_detection(dev, clip_off); - - saved_regs_phy_rfctl[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); - saved_regs_phy_rfctl[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); - for (i = 0; i < regs_amount; i++) - saved_regs_phy[i] = b43_phy_read(dev, regs_to_store[i]); - - b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_OFF, 0, 7); - b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 1, 7); - - if (dev->phy.rev >= 7) { - b43_nphy_rf_ctl_override_one_to_many(dev, - N_RF_CTL_OVER_CMD_RXRF_PU, - 0, 0, false); - b43_nphy_rf_ctl_override_one_to_many(dev, - N_RF_CTL_OVER_CMD_RX_PU, - 1, 0, false); - b43_nphy_rf_ctl_override_rev7(dev, 0x80, 1, 0, false, 0); - b43_nphy_rf_ctl_override_rev7(dev, 0x40, 1, 0, false, 0); - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - b43_nphy_rf_ctl_override_rev7(dev, 0x20, 0, 0, false, - 0); - b43_nphy_rf_ctl_override_rev7(dev, 0x10, 1, 0, false, - 0); - } else { - b43_nphy_rf_ctl_override_rev7(dev, 0x10, 0, 0, false, - 0); - b43_nphy_rf_ctl_override_rev7(dev, 0x20, 1, 0, false, - 0); - } - } else { - b43_nphy_rf_ctl_override(dev, 0x1, 0, 0, false); - b43_nphy_rf_ctl_override(dev, 0x2, 1, 0, false); - b43_nphy_rf_ctl_override(dev, 0x80, 1, 0, false); - b43_nphy_rf_ctl_override(dev, 0x40, 1, 0, false); - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - b43_nphy_rf_ctl_override(dev, 0x20, 0, 0, false); - b43_nphy_rf_ctl_override(dev, 0x10, 1, 0, false); - } else { - b43_nphy_rf_ctl_override(dev, 0x10, 0, 0, false); - b43_nphy_rf_ctl_override(dev, 0x20, 1, 0, false); - } - } - - rx_core_state = b43_nphy_get_rx_core_state(dev); - for (core = 0; core < 2; core++) { - if (!(rx_core_state & (1 << core))) - continue; - r = core ? B2056_RX1 : B2056_RX0; - b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, N_RAIL_I, - N_RSSI_NB); - b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, N_RAIL_Q, - N_RSSI_NB); - - /* Grab RSSI results for every possible VCM */ - for (vcm = 0; vcm < 8; vcm++) { - if (dev->phy.rev >= 7) - b43_radio_maskset(dev, - core ? R2057_NB_MASTER_CORE1 : - R2057_NB_MASTER_CORE0, - ~R2057_VCM_MASK, vcm); - else - b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, - 0xE3, vcm << 2); - b43_nphy_poll_rssi(dev, N_RSSI_NB, results[vcm], 8); - } - - /* Find out which VCM got the best results */ - for (i = 0; i < 4; i += 2) { - s32 currd; - s32 mind = 0x100000; - s32 minpoll = 249; - u8 minvcm = 0; - if (2 * core != i) - continue; - for (vcm = 0; vcm < 8; vcm++) { - currd = results[vcm][i] * results[vcm][i] + - results[vcm][i + 1] * results[vcm][i]; - if (currd < mind) { - mind = currd; - minvcm = vcm; - } - if (results[vcm][i] < minpoll) - minpoll = results[vcm][i]; - } - vcm_final = minvcm; - results_min[i] = minpoll; - } - - /* Select the best VCM */ - if (dev->phy.rev >= 7) - b43_radio_maskset(dev, - core ? R2057_NB_MASTER_CORE1 : - R2057_NB_MASTER_CORE0, - ~R2057_VCM_MASK, vcm); - else - b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, - 0xE3, vcm_final << 2); - - for (i = 0; i < 4; i++) { - if (core != i / 2) - continue; - offset[i] = -results[vcm_final][i]; - if (offset[i] < 0) - offset[i] = -((abs(offset[i]) + 4) / 8); - else - offset[i] = (offset[i] + 4) / 8; - if (results_min[i] == 248) - offset[i] = -32; - b43_nphy_scale_offset_rssi(dev, 0, offset[i], - (i / 2 == 0) ? 1 : 2, - (i % 2 == 0) ? N_RAIL_I : N_RAIL_Q, - N_RSSI_NB); - } - } - - for (core = 0; core < 2; core++) { - if (!(rx_core_state & (1 << core))) - continue; - for (i = 0; i < 2; i++) { - b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, - N_RAIL_I, i); - b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, - N_RAIL_Q, i); - b43_nphy_poll_rssi(dev, i, poll_results, 8); - for (j = 0; j < 4; j++) { - if (j / 2 == core) { - offset[j] = 232 - poll_results[j]; - if (offset[j] < 0) - offset[j] = -(abs(offset[j] + 4) / 8); - else - offset[j] = (offset[j] + 4) / 8; - b43_nphy_scale_offset_rssi(dev, 0, - offset[2 * core], core + 1, j % 2, i); - } - } - } - } - - b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, saved_regs_phy_rfctl[0]); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, saved_regs_phy_rfctl[1]); - - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); - - b43_phy_set(dev, B43_NPHY_TXF_40CO_B1S1, 0x1); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_START); - b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1, ~0x1); - - b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1); - b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_RXTX); - b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~0x1); - - for (i = 0; i < regs_amount; i++) - b43_phy_write(dev, regs_to_store[i], saved_regs_phy[i]); - - /* Store for future configuration */ - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_2G; - rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_2G; - } else { - rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_5G; - rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G; - } - if (dev->phy.rev >= 7) { - rssical_radio_regs[0] = b43_radio_read(dev, - R2057_NB_MASTER_CORE0); - rssical_radio_regs[1] = b43_radio_read(dev, - R2057_NB_MASTER_CORE1); - } else { - rssical_radio_regs[0] = b43_radio_read(dev, B2056_RX0 | - B2056_RX_RSSI_MISC); - rssical_radio_regs[1] = b43_radio_read(dev, B2056_RX1 | - B2056_RX_RSSI_MISC); - } - rssical_phy_regs[0] = b43_phy_read(dev, B43_NPHY_RSSIMC_0I_RSSI_Z); - rssical_phy_regs[1] = b43_phy_read(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z); - rssical_phy_regs[2] = b43_phy_read(dev, B43_NPHY_RSSIMC_1I_RSSI_Z); - rssical_phy_regs[3] = b43_phy_read(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z); - rssical_phy_regs[4] = b43_phy_read(dev, B43_NPHY_RSSIMC_0I_RSSI_X); - rssical_phy_regs[5] = b43_phy_read(dev, B43_NPHY_RSSIMC_0Q_RSSI_X); - rssical_phy_regs[6] = b43_phy_read(dev, B43_NPHY_RSSIMC_1I_RSSI_X); - rssical_phy_regs[7] = b43_phy_read(dev, B43_NPHY_RSSIMC_1Q_RSSI_X); - rssical_phy_regs[8] = b43_phy_read(dev, B43_NPHY_RSSIMC_0I_RSSI_Y); - rssical_phy_regs[9] = b43_phy_read(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y); - rssical_phy_regs[10] = b43_phy_read(dev, B43_NPHY_RSSIMC_1I_RSSI_Y); - rssical_phy_regs[11] = b43_phy_read(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y); - - /* Remember for which channel we store configuration */ - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - nphy->rssical_chanspec_2G.center_freq = phy->chandef->chan->center_freq; - else - nphy->rssical_chanspec_5G.center_freq = phy->chandef->chan->center_freq; - - /* End of calibration, restore configuration */ - b43_nphy_classifier(dev, 7, class); - b43_nphy_write_clip_detection(dev, clip_state); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal */ -static void b43_nphy_rev2_rssi_cal(struct b43_wldev *dev, enum n_rssi_type type) -{ - int i, j, vcm; - u8 state[4]; - u8 code, val; - u16 class, override; - u8 regs_save_radio[2]; - u16 regs_save_phy[2]; - - s32 offset[4]; - u8 core; - u8 rail; - - u16 clip_state[2]; - u16 clip_off[2] = { 0xFFFF, 0xFFFF }; - s32 results_min[4] = { }; - u8 vcm_final[4] = { }; - s32 results[4][4] = { }; - s32 miniq[4][2] = { }; - - if (type == N_RSSI_NB) { - code = 0; - val = 6; - } else if (type == N_RSSI_W1 || type == N_RSSI_W2) { - code = 25; - val = 4; - } else { - B43_WARN_ON(1); - return; - } - - class = b43_nphy_classifier(dev, 0, 0); - b43_nphy_classifier(dev, 7, 4); - b43_nphy_read_clip_detection(dev, clip_state); - b43_nphy_write_clip_detection(dev, clip_off); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - override = 0x140; - else - override = 0x110; - - regs_save_phy[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); - regs_save_radio[0] = b43_radio_read(dev, B2055_C1_PD_RXTX); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, override); - b43_radio_write(dev, B2055_C1_PD_RXTX, val); - - regs_save_phy[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); - regs_save_radio[1] = b43_radio_read(dev, B2055_C2_PD_RXTX); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, override); - b43_radio_write(dev, B2055_C2_PD_RXTX, val); - - state[0] = b43_radio_read(dev, B2055_C1_PD_RSSIMISC) & 0x07; - state[1] = b43_radio_read(dev, B2055_C2_PD_RSSIMISC) & 0x07; - b43_radio_mask(dev, B2055_C1_PD_RSSIMISC, 0xF8); - b43_radio_mask(dev, B2055_C2_PD_RSSIMISC, 0xF8); - state[2] = b43_radio_read(dev, B2055_C1_SP_RSSI) & 0x07; - state[3] = b43_radio_read(dev, B2055_C2_SP_RSSI) & 0x07; - - b43_nphy_rssi_select(dev, 5, type); - b43_nphy_scale_offset_rssi(dev, 0, 0, 5, N_RAIL_I, type); - b43_nphy_scale_offset_rssi(dev, 0, 0, 5, N_RAIL_Q, type); - - for (vcm = 0; vcm < 4; vcm++) { - u8 tmp[4]; - for (j = 0; j < 4; j++) - tmp[j] = vcm; - if (type != N_RSSI_W2) - b43_nphy_set_rssi_2055_vcm(dev, type, tmp); - b43_nphy_poll_rssi(dev, type, results[vcm], 8); - if (type == N_RSSI_W1 || type == N_RSSI_W2) - for (j = 0; j < 2; j++) - miniq[vcm][j] = min(results[vcm][2 * j], - results[vcm][2 * j + 1]); - } - - for (i = 0; i < 4; i++) { - s32 mind = 0x100000; - u8 minvcm = 0; - s32 minpoll = 249; - s32 currd; - for (vcm = 0; vcm < 4; vcm++) { - if (type == N_RSSI_NB) - currd = abs(results[vcm][i] - code * 8); - else - currd = abs(miniq[vcm][i / 2] - code * 8); - - if (currd < mind) { - mind = currd; - minvcm = vcm; - } - - if (results[vcm][i] < minpoll) - minpoll = results[vcm][i]; - } - results_min[i] = minpoll; - vcm_final[i] = minvcm; - } - - if (type != N_RSSI_W2) - b43_nphy_set_rssi_2055_vcm(dev, type, vcm_final); - - for (i = 0; i < 4; i++) { - offset[i] = (code * 8) - results[vcm_final[i]][i]; - - if (offset[i] < 0) - offset[i] = -((abs(offset[i]) + 4) / 8); - else - offset[i] = (offset[i] + 4) / 8; - - if (results_min[i] == 248) - offset[i] = code - 32; - - core = (i / 2) ? 2 : 1; - rail = (i % 2) ? N_RAIL_Q : N_RAIL_I; - - b43_nphy_scale_offset_rssi(dev, 0, offset[i], core, rail, - type); - } - - b43_radio_maskset(dev, B2055_C1_PD_RSSIMISC, 0xF8, state[0]); - b43_radio_maskset(dev, B2055_C2_PD_RSSIMISC, 0xF8, state[1]); - - switch (state[2]) { - case 1: - b43_nphy_rssi_select(dev, 1, N_RSSI_NB); - break; - case 4: - b43_nphy_rssi_select(dev, 1, N_RSSI_W1); - break; - case 2: - b43_nphy_rssi_select(dev, 1, N_RSSI_W2); - break; - default: - b43_nphy_rssi_select(dev, 1, N_RSSI_W2); - break; - } - - switch (state[3]) { - case 1: - b43_nphy_rssi_select(dev, 2, N_RSSI_NB); - break; - case 4: - b43_nphy_rssi_select(dev, 2, N_RSSI_W1); - break; - default: - b43_nphy_rssi_select(dev, 2, N_RSSI_W2); - break; - } - - b43_nphy_rssi_select(dev, 0, type); - - b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs_save_phy[0]); - b43_radio_write(dev, B2055_C1_PD_RXTX, regs_save_radio[0]); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs_save_phy[1]); - b43_radio_write(dev, B2055_C2_PD_RXTX, regs_save_radio[1]); - - b43_nphy_classifier(dev, 7, class); - b43_nphy_write_clip_detection(dev, clip_state); - /* Specs don't say about reset here, but it makes wl and b43 dumps - identical, it really seems wl performs this */ - b43_nphy_reset_cca(dev); -} - -/* - * RSSI Calibration - * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal - */ -static void b43_nphy_rssi_cal(struct b43_wldev *dev) -{ - if (dev->phy.rev >= 19) { - /* TODO */ - } else if (dev->phy.rev >= 3) { - b43_nphy_rev3_rssi_cal(dev); - } else { - b43_nphy_rev2_rssi_cal(dev, N_RSSI_NB); - b43_nphy_rev2_rssi_cal(dev, N_RSSI_W1); - b43_nphy_rev2_rssi_cal(dev, N_RSSI_W2); - } -} - -/************************************************** - * Workarounds - **************************************************/ - -static void b43_nphy_gain_ctl_workarounds_rev19(struct b43_wldev *dev) -{ - /* TODO */ -} - -static void b43_nphy_gain_ctl_workarounds_rev7(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - switch (phy->rev) { - /* TODO */ - } -} - -static void b43_nphy_gain_ctl_workarounds_rev3(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - - bool ghz5; - bool ext_lna; - u16 rssi_gain; - struct nphy_gain_ctl_workaround_entry *e; - u8 lpf_gain[6] = { 0x00, 0x06, 0x0C, 0x12, 0x12, 0x12 }; - u8 lpf_bits[6] = { 0, 1, 2, 3, 3, 3 }; - - /* Prepare values */ - ghz5 = b43_phy_read(dev, B43_NPHY_BANDCTL) - & B43_NPHY_BANDCTL_5GHZ; - ext_lna = ghz5 ? sprom->boardflags_hi & B43_BFH_EXTLNA_5GHZ : - sprom->boardflags_lo & B43_BFL_EXTLNA; - e = b43_nphy_get_gain_ctl_workaround_ent(dev, ghz5, ext_lna); - if (ghz5 && dev->phy.rev >= 5) - rssi_gain = 0x90; - else - rssi_gain = 0x50; - - b43_phy_set(dev, B43_NPHY_RXCTL, 0x0040); - - /* Set Clip 2 detect */ - b43_phy_set(dev, B43_NPHY_C1_CGAINI, B43_NPHY_C1_CGAINI_CL2DETECT); - b43_phy_set(dev, B43_NPHY_C2_CGAINI, B43_NPHY_C2_CGAINI_CL2DETECT); - - b43_radio_write(dev, B2056_RX0 | B2056_RX_BIASPOLE_LNAG1_IDAC, - 0x17); - b43_radio_write(dev, B2056_RX1 | B2056_RX_BIASPOLE_LNAG1_IDAC, - 0x17); - b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAG2_IDAC, 0xF0); - b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAG2_IDAC, 0xF0); - b43_radio_write(dev, B2056_RX0 | B2056_RX_RSSI_POLE, 0x00); - b43_radio_write(dev, B2056_RX1 | B2056_RX_RSSI_POLE, 0x00); - b43_radio_write(dev, B2056_RX0 | B2056_RX_RSSI_GAIN, - rssi_gain); - b43_radio_write(dev, B2056_RX1 | B2056_RX_RSSI_GAIN, - rssi_gain); - b43_radio_write(dev, B2056_RX0 | B2056_RX_BIASPOLE_LNAA1_IDAC, - 0x17); - b43_radio_write(dev, B2056_RX1 | B2056_RX_BIASPOLE_LNAA1_IDAC, - 0x17); - b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAA2_IDAC, 0xFF); - b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAA2_IDAC, 0xFF); - - b43_ntab_write_bulk(dev, B43_NTAB8(0, 8), 4, e->lna1_gain); - b43_ntab_write_bulk(dev, B43_NTAB8(1, 8), 4, e->lna1_gain); - b43_ntab_write_bulk(dev, B43_NTAB8(0, 16), 4, e->lna2_gain); - b43_ntab_write_bulk(dev, B43_NTAB8(1, 16), 4, e->lna2_gain); - b43_ntab_write_bulk(dev, B43_NTAB8(0, 32), 10, e->gain_db); - b43_ntab_write_bulk(dev, B43_NTAB8(1, 32), 10, e->gain_db); - b43_ntab_write_bulk(dev, B43_NTAB8(2, 32), 10, e->gain_bits); - b43_ntab_write_bulk(dev, B43_NTAB8(3, 32), 10, e->gain_bits); - b43_ntab_write_bulk(dev, B43_NTAB8(0, 0x40), 6, lpf_gain); - b43_ntab_write_bulk(dev, B43_NTAB8(1, 0x40), 6, lpf_gain); - b43_ntab_write_bulk(dev, B43_NTAB8(2, 0x40), 6, lpf_bits); - b43_ntab_write_bulk(dev, B43_NTAB8(3, 0x40), 6, lpf_bits); - - b43_phy_write(dev, B43_NPHY_REV3_C1_INITGAIN_A, e->init_gain); - b43_phy_write(dev, B43_NPHY_REV3_C2_INITGAIN_A, e->init_gain); - - b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x106), 2, - e->rfseq_init); - - b43_phy_write(dev, B43_NPHY_REV3_C1_CLIP_HIGAIN_A, e->cliphi_gain); - b43_phy_write(dev, B43_NPHY_REV3_C2_CLIP_HIGAIN_A, e->cliphi_gain); - b43_phy_write(dev, B43_NPHY_REV3_C1_CLIP_MEDGAIN_A, e->clipmd_gain); - b43_phy_write(dev, B43_NPHY_REV3_C2_CLIP_MEDGAIN_A, e->clipmd_gain); - b43_phy_write(dev, B43_NPHY_REV3_C1_CLIP_LOGAIN_A, e->cliplo_gain); - b43_phy_write(dev, B43_NPHY_REV3_C2_CLIP_LOGAIN_A, e->cliplo_gain); - - b43_phy_maskset(dev, B43_NPHY_CRSMINPOWER0, 0xFF00, e->crsmin); - b43_phy_maskset(dev, B43_NPHY_CRSMINPOWERL0, 0xFF00, e->crsminl); - b43_phy_maskset(dev, B43_NPHY_CRSMINPOWERU0, 0xFF00, e->crsminu); - b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, e->nbclip); - b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, e->nbclip); - b43_phy_maskset(dev, B43_NPHY_C1_CLIPWBTHRES, - ~B43_NPHY_C1_CLIPWBTHRES_CLIP2, e->wlclip); - b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES, - ~B43_NPHY_C2_CLIPWBTHRES_CLIP2, e->wlclip); - b43_phy_write(dev, B43_NPHY_CCK_SHIFTB_REF, 0x809C); -} - -static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy = dev->phy.n; - - u8 i, j; - u8 code; - u16 tmp; - u8 rfseq_events[3] = { 6, 8, 7 }; - u8 rfseq_delays[3] = { 10, 30, 1 }; - - /* Set Clip 2 detect */ - b43_phy_set(dev, B43_NPHY_C1_CGAINI, B43_NPHY_C1_CGAINI_CL2DETECT); - b43_phy_set(dev, B43_NPHY_C2_CGAINI, B43_NPHY_C2_CGAINI_CL2DETECT); - - /* Set narrowband clip threshold */ - b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, 0x84); - b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, 0x84); - - if (!b43_is_40mhz(dev)) { - /* Set dwell lengths */ - b43_phy_write(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 0x002B); - b43_phy_write(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 0x002B); - b43_phy_write(dev, B43_NPHY_W1CLIP1_DWELL_LEN, 0x0009); - b43_phy_write(dev, B43_NPHY_W1CLIP2_DWELL_LEN, 0x0009); - } - - /* Set wideband clip 2 threshold */ - b43_phy_maskset(dev, B43_NPHY_C1_CLIPWBTHRES, - ~B43_NPHY_C1_CLIPWBTHRES_CLIP2, 21); - b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES, - ~B43_NPHY_C2_CLIPWBTHRES_CLIP2, 21); - - if (!b43_is_40mhz(dev)) { - b43_phy_maskset(dev, B43_NPHY_C1_CGAINI, - ~B43_NPHY_C1_CGAINI_GAINBKOFF, 0x1); - b43_phy_maskset(dev, B43_NPHY_C2_CGAINI, - ~B43_NPHY_C2_CGAINI_GAINBKOFF, 0x1); - b43_phy_maskset(dev, B43_NPHY_C1_CCK_CGAINI, - ~B43_NPHY_C1_CCK_CGAINI_GAINBKOFF, 0x1); - b43_phy_maskset(dev, B43_NPHY_C2_CCK_CGAINI, - ~B43_NPHY_C2_CCK_CGAINI_GAINBKOFF, 0x1); - } - - b43_phy_write(dev, B43_NPHY_CCK_SHIFTB_REF, 0x809C); - - if (nphy->gain_boost) { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ && - b43_is_40mhz(dev)) - code = 4; - else - code = 5; - } else { - code = b43_is_40mhz(dev) ? 6 : 7; - } - - /* Set HPVGA2 index */ - b43_phy_maskset(dev, B43_NPHY_C1_INITGAIN, ~B43_NPHY_C1_INITGAIN_HPVGA2, - code << B43_NPHY_C1_INITGAIN_HPVGA2_SHIFT); - b43_phy_maskset(dev, B43_NPHY_C2_INITGAIN, ~B43_NPHY_C2_INITGAIN_HPVGA2, - code << B43_NPHY_C2_INITGAIN_HPVGA2_SHIFT); - - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D06); - /* specs say about 2 loops, but wl does 4 */ - for (i = 0; i < 4; i++) - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, (code << 8 | 0x7C)); - - b43_nphy_adjust_lna_gain_table(dev); - - if (nphy->elna_gain_config) { - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x0808); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); - - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x0C08); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); - - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D06); - /* specs say about 2 loops, but wl does 4 */ - for (i = 0; i < 4; i++) - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, - (code << 8 | 0x74)); - } - - if (dev->phy.rev == 2) { - for (i = 0; i < 4; i++) { - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, - (0x0400 * i) + 0x0020); - for (j = 0; j < 21; j++) { - tmp = j * (i < 2 ? 3 : 1); - b43_phy_write(dev, - B43_NPHY_TABLE_DATALO, tmp); - } - } - } - - b43_nphy_set_rf_sequence(dev, 5, rfseq_events, rfseq_delays, 3); - b43_phy_maskset(dev, B43_NPHY_OVER_DGAIN1, - ~B43_NPHY_OVER_DGAIN_CCKDGECV & 0xFFFF, - 0x5A << B43_NPHY_OVER_DGAIN_CCKDGECV_SHIFT); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - b43_phy_maskset(dev, B43_PHY_N(0xC5D), 0xFF80, 4); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/WorkaroundsGainCtrl */ -static void b43_nphy_gain_ctl_workarounds(struct b43_wldev *dev) -{ - if (dev->phy.rev >= 19) - b43_nphy_gain_ctl_workarounds_rev19(dev); - else if (dev->phy.rev >= 7) - b43_nphy_gain_ctl_workarounds_rev7(dev); - else if (dev->phy.rev >= 3) - b43_nphy_gain_ctl_workarounds_rev3(dev); - else - b43_nphy_gain_ctl_workarounds_rev1_2(dev); -} - -static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - struct b43_phy *phy = &dev->phy; - - /* TX to RX */ - u8 tx2rx_events[7] = { 4, 3, 5, 2, 1, 8, 31, }; - u8 tx2rx_delays[7] = { 8, 4, 4, 4, 4, 6, 1, }; - /* RX to TX */ - u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3, - 0x1F }; - u8 rx2tx_delays_ipa[9] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 }; - - static const u16 ntab7_15e_16e[] = { 0, 0x10f, 0x10f }; - u8 ntab7_138_146[] = { 0x11, 0x11 }; - u8 ntab7_133[] = { 0x77, 0x11, 0x11 }; - - u16 lpf_ofdm_20mhz[2], lpf_ofdm_40mhz[2], lpf_11b[2]; - u16 bcap_val; - s16 bcap_val_11b[2], bcap_val_11n_20[2], bcap_val_11n_40[2]; - u16 scap_val; - s16 scap_val_11b[2], scap_val_11n_20[2], scap_val_11n_40[2]; - bool rccal_ovrd = false; - - u16 bias, conv, filt; - - u32 noise_tbl[2]; - - u32 tmp32; - u8 core; - - b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x0125); - b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x01b3); - b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x0105); - b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x016e); - b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0x00cd); - b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x0020); - - if (phy->rev == 7) { - b43_phy_set(dev, B43_NPHY_FINERX2_CGC, 0x10); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN0, 0xFF80, 0x0020); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN0, 0x80FF, 0x2700); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN1, 0xFF80, 0x002E); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN1, 0x80FF, 0x3300); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN2, 0xFF80, 0x0037); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN2, 0x80FF, 0x3A00); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN3, 0xFF80, 0x003C); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN3, 0x80FF, 0x3E00); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN4, 0xFF80, 0x003E); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN4, 0x80FF, 0x3F00); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN5, 0xFF80, 0x0040); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN5, 0x80FF, 0x4000); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN6, 0xFF80, 0x0040); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN6, 0x80FF, 0x4000); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN7, 0xFF80, 0x0040); - b43_phy_maskset(dev, B43_NPHY_FREQGAIN7, 0x80FF, 0x4000); - } - - if (phy->rev >= 16) { - b43_phy_write(dev, B43_NPHY_FORCEFRONT0, 0x7ff); - b43_phy_write(dev, B43_NPHY_FORCEFRONT1, 0x7ff); - } else if (phy->rev <= 8) { - b43_phy_write(dev, B43_NPHY_FORCEFRONT0, 0x1B0); - b43_phy_write(dev, B43_NPHY_FORCEFRONT1, 0x1B0); - } - - if (phy->rev >= 16) - b43_phy_maskset(dev, B43_NPHY_TXTAILCNT, ~0xFF, 0xa0); - else if (phy->rev >= 8) - b43_phy_maskset(dev, B43_NPHY_TXTAILCNT, ~0xFF, 0x72); - - b43_ntab_write(dev, B43_NTAB16(8, 0x00), 2); - b43_ntab_write(dev, B43_NTAB16(8, 0x10), 2); - tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0)); - tmp32 &= 0xffffff; - b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32); - b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x15d), 3, ntab7_15e_16e); - b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x16d), 3, ntab7_15e_16e); - - b43_nphy_set_rf_sequence(dev, 1, tx2rx_events, tx2rx_delays, - ARRAY_SIZE(tx2rx_events)); - if (b43_nphy_ipa(dev)) - b43_nphy_set_rf_sequence(dev, 0, rx2tx_events_ipa, - rx2tx_delays_ipa, ARRAY_SIZE(rx2tx_events_ipa)); - - b43_phy_maskset(dev, B43_NPHY_EPS_OVERRIDEI_0, 0x3FFF, 0x4000); - b43_phy_maskset(dev, B43_NPHY_EPS_OVERRIDEI_1, 0x3FFF, 0x4000); - - for (core = 0; core < 2; core++) { - lpf_ofdm_20mhz[core] = b43_nphy_read_lpf_ctl(dev, 0x154 + core * 0x10); - lpf_ofdm_40mhz[core] = b43_nphy_read_lpf_ctl(dev, 0x159 + core * 0x10); - lpf_11b[core] = b43_nphy_read_lpf_ctl(dev, 0x152 + core * 0x10); - } - - bcap_val = b43_radio_read(dev, R2057_RCCAL_BCAP_VAL); - scap_val = b43_radio_read(dev, R2057_RCCAL_SCAP_VAL); - - if (b43_nphy_ipa(dev)) { - bool ghz2 = b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ; - - switch (phy->radio_rev) { - case 5: - /* Check radio version (to be 0) by PHY rev for now */ - if (phy->rev == 8 && b43_is_40mhz(dev)) { - for (core = 0; core < 2; core++) { - scap_val_11b[core] = scap_val; - bcap_val_11b[core] = bcap_val; - scap_val_11n_20[core] = scap_val; - bcap_val_11n_20[core] = bcap_val; - scap_val_11n_40[core] = 0xc; - bcap_val_11n_40[core] = 0xc; - } - - rccal_ovrd = true; - } - if (phy->rev == 9) { - /* TODO: Radio version 1 (e.g. BCM5357B0) */ - } - break; - case 7: - case 8: - for (core = 0; core < 2; core++) { - scap_val_11b[core] = scap_val; - bcap_val_11b[core] = bcap_val; - lpf_ofdm_20mhz[core] = 4; - lpf_11b[core] = 1; - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - scap_val_11n_20[core] = 0xc; - bcap_val_11n_20[core] = 0xc; - scap_val_11n_40[core] = 0xa; - bcap_val_11n_40[core] = 0xa; - } else { - scap_val_11n_20[core] = 0x14; - bcap_val_11n_20[core] = 0x14; - scap_val_11n_40[core] = 0xf; - bcap_val_11n_40[core] = 0xf; - } - } - - rccal_ovrd = true; - break; - case 9: - for (core = 0; core < 2; core++) { - bcap_val_11b[core] = bcap_val; - scap_val_11b[core] = scap_val; - lpf_11b[core] = 1; - - if (ghz2) { - bcap_val_11n_20[core] = bcap_val + 13; - scap_val_11n_20[core] = scap_val + 15; - } else { - bcap_val_11n_20[core] = bcap_val + 14; - scap_val_11n_20[core] = scap_val + 15; - } - lpf_ofdm_20mhz[core] = 4; - - if (ghz2) { - bcap_val_11n_40[core] = bcap_val - 7; - scap_val_11n_40[core] = scap_val - 5; - } else { - bcap_val_11n_40[core] = bcap_val + 2; - scap_val_11n_40[core] = scap_val + 4; - } - lpf_ofdm_40mhz[core] = 4; - } - - rccal_ovrd = true; - break; - case 14: - for (core = 0; core < 2; core++) { - bcap_val_11b[core] = bcap_val; - scap_val_11b[core] = scap_val; - lpf_11b[core] = 1; - } - - bcap_val_11n_20[0] = bcap_val + 20; - scap_val_11n_20[0] = scap_val + 20; - lpf_ofdm_20mhz[0] = 3; - - bcap_val_11n_20[1] = bcap_val + 16; - scap_val_11n_20[1] = scap_val + 16; - lpf_ofdm_20mhz[1] = 3; - - bcap_val_11n_40[0] = bcap_val + 20; - scap_val_11n_40[0] = scap_val + 20; - lpf_ofdm_40mhz[0] = 4; - - bcap_val_11n_40[1] = bcap_val + 10; - scap_val_11n_40[1] = scap_val + 10; - lpf_ofdm_40mhz[1] = 4; - - rccal_ovrd = true; - break; - } - } else { - if (phy->radio_rev == 5) { - for (core = 0; core < 2; core++) { - lpf_ofdm_20mhz[core] = 1; - lpf_ofdm_40mhz[core] = 3; - scap_val_11b[core] = scap_val; - bcap_val_11b[core] = bcap_val; - scap_val_11n_20[core] = 0x11; - scap_val_11n_40[core] = 0x11; - bcap_val_11n_20[core] = 0x13; - bcap_val_11n_40[core] = 0x13; - } - - rccal_ovrd = true; - } - } - if (rccal_ovrd) { - u16 rx2tx_lut_20_11b[2], rx2tx_lut_20_11n[2], rx2tx_lut_40_11n[2]; - u8 rx2tx_lut_extra = 1; - - for (core = 0; core < 2; core++) { - bcap_val_11b[core] = clamp_val(bcap_val_11b[core], 0, 0x1f); - scap_val_11b[core] = clamp_val(scap_val_11b[core], 0, 0x1f); - bcap_val_11n_20[core] = clamp_val(bcap_val_11n_20[core], 0, 0x1f); - scap_val_11n_20[core] = clamp_val(scap_val_11n_20[core], 0, 0x1f); - bcap_val_11n_40[core] = clamp_val(bcap_val_11n_40[core], 0, 0x1f); - scap_val_11n_40[core] = clamp_val(scap_val_11n_40[core], 0, 0x1f); - - rx2tx_lut_20_11b[core] = (rx2tx_lut_extra << 13) | - (bcap_val_11b[core] << 8) | - (scap_val_11b[core] << 3) | - lpf_11b[core]; - rx2tx_lut_20_11n[core] = (rx2tx_lut_extra << 13) | - (bcap_val_11n_20[core] << 8) | - (scap_val_11n_20[core] << 3) | - lpf_ofdm_20mhz[core]; - rx2tx_lut_40_11n[core] = (rx2tx_lut_extra << 13) | - (bcap_val_11n_40[core] << 8) | - (scap_val_11n_40[core] << 3) | - lpf_ofdm_40mhz[core]; - } - - for (core = 0; core < 2; core++) { - b43_ntab_write(dev, B43_NTAB16(7, 0x152 + core * 16), - rx2tx_lut_20_11b[core]); - b43_ntab_write(dev, B43_NTAB16(7, 0x153 + core * 16), - rx2tx_lut_20_11n[core]); - b43_ntab_write(dev, B43_NTAB16(7, 0x154 + core * 16), - rx2tx_lut_20_11n[core]); - b43_ntab_write(dev, B43_NTAB16(7, 0x155 + core * 16), - rx2tx_lut_40_11n[core]); - b43_ntab_write(dev, B43_NTAB16(7, 0x156 + core * 16), - rx2tx_lut_40_11n[core]); - b43_ntab_write(dev, B43_NTAB16(7, 0x157 + core * 16), - rx2tx_lut_40_11n[core]); - b43_ntab_write(dev, B43_NTAB16(7, 0x158 + core * 16), - rx2tx_lut_40_11n[core]); - b43_ntab_write(dev, B43_NTAB16(7, 0x159 + core * 16), - rx2tx_lut_40_11n[core]); - } - } - - b43_phy_write(dev, 0x32F, 0x3); - - if (phy->radio_rev == 4 || phy->radio_rev == 6) - b43_nphy_rf_ctl_override_rev7(dev, 4, 1, 3, false, 0); - - if (phy->radio_rev == 3 || phy->radio_rev == 4 || phy->radio_rev == 6) { - if (sprom->revision && - sprom->boardflags2_hi & B43_BFH2_IPALVLSHIFT_3P3) { - b43_radio_write(dev, 0x5, 0x05); - b43_radio_write(dev, 0x6, 0x30); - b43_radio_write(dev, 0x7, 0x00); - b43_radio_set(dev, 0x4f, 0x1); - b43_radio_set(dev, 0xd4, 0x1); - bias = 0x1f; - conv = 0x6f; - filt = 0xaa; - } else { - bias = 0x2b; - conv = 0x7f; - filt = 0xee; - } - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - for (core = 0; core < 2; core++) { - if (core == 0) { - b43_radio_write(dev, 0x5F, bias); - b43_radio_write(dev, 0x64, conv); - b43_radio_write(dev, 0x66, filt); - } else { - b43_radio_write(dev, 0xE8, bias); - b43_radio_write(dev, 0xE9, conv); - b43_radio_write(dev, 0xEB, filt); - } - } - } - } - - if (b43_nphy_ipa(dev)) { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if (phy->radio_rev == 3 || phy->radio_rev == 4 || - phy->radio_rev == 6) { - for (core = 0; core < 2; core++) { - if (core == 0) - b43_radio_write(dev, 0x51, - 0x7f); - else - b43_radio_write(dev, 0xd6, - 0x7f); - } - } - switch (phy->radio_rev) { - case 3: - for (core = 0; core < 2; core++) { - if (core == 0) { - b43_radio_write(dev, 0x64, - 0x13); - b43_radio_write(dev, 0x5F, - 0x1F); - b43_radio_write(dev, 0x66, - 0xEE); - b43_radio_write(dev, 0x59, - 0x8A); - b43_radio_write(dev, 0x80, - 0x3E); - } else { - b43_radio_write(dev, 0x69, - 0x13); - b43_radio_write(dev, 0xE8, - 0x1F); - b43_radio_write(dev, 0xEB, - 0xEE); - b43_radio_write(dev, 0xDE, - 0x8A); - b43_radio_write(dev, 0x105, - 0x3E); - } - } - break; - case 7: - case 8: - if (!b43_is_40mhz(dev)) { - b43_radio_write(dev, 0x5F, 0x14); - b43_radio_write(dev, 0xE8, 0x12); - } else { - b43_radio_write(dev, 0x5F, 0x16); - b43_radio_write(dev, 0xE8, 0x16); - } - break; - case 14: - for (core = 0; core < 2; core++) { - int o = core ? 0x85 : 0; - - b43_radio_write(dev, o + R2057_IPA2G_CASCONV_CORE0, 0x13); - b43_radio_write(dev, o + R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, 0x21); - b43_radio_write(dev, o + R2057_IPA2G_BIAS_FILTER_CORE0, 0xff); - b43_radio_write(dev, o + R2057_PAD2G_IDACS_CORE0, 0x88); - b43_radio_write(dev, o + R2057_PAD2G_TUNE_PUS_CORE0, 0x23); - b43_radio_write(dev, o + R2057_IPA2G_IMAIN_CORE0, 0x16); - b43_radio_write(dev, o + R2057_PAD_BIAS_FILTER_BWS_CORE0, 0x3e); - b43_radio_write(dev, o + R2057_BACKUP1_CORE0, 0x10); - } - break; - } - } else { - u16 freq = phy->chandef->chan->center_freq; - if ((freq >= 5180 && freq <= 5230) || - (freq >= 5745 && freq <= 5805)) { - b43_radio_write(dev, 0x7D, 0xFF); - b43_radio_write(dev, 0xFE, 0xFF); - } - } - } else { - if (phy->radio_rev != 5) { - for (core = 0; core < 2; core++) { - if (core == 0) { - b43_radio_write(dev, 0x5c, 0x61); - b43_radio_write(dev, 0x51, 0x70); - } else { - b43_radio_write(dev, 0xe1, 0x61); - b43_radio_write(dev, 0xd6, 0x70); - } - } - } - } - - if (phy->radio_rev == 4) { - b43_ntab_write(dev, B43_NTAB16(8, 0x05), 0x20); - b43_ntab_write(dev, B43_NTAB16(8, 0x15), 0x20); - for (core = 0; core < 2; core++) { - if (core == 0) { - b43_radio_write(dev, 0x1a1, 0x00); - b43_radio_write(dev, 0x1a2, 0x3f); - b43_radio_write(dev, 0x1a6, 0x3f); - } else { - b43_radio_write(dev, 0x1a7, 0x00); - b43_radio_write(dev, 0x1ab, 0x3f); - b43_radio_write(dev, 0x1ac, 0x3f); - } - } - } else { - b43_phy_set(dev, B43_NPHY_AFECTL_C1, 0x4); - b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x4); - b43_phy_set(dev, B43_NPHY_AFECTL_C2, 0x4); - b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x4); - - b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x1); - b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x1); - b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x1); - b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x1); - b43_ntab_write(dev, B43_NTAB16(8, 0x05), 0); - b43_ntab_write(dev, B43_NTAB16(8, 0x15), 0); - - b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x4); - b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x4); - b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x4); - b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x4); - } - - b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, 0x2); - - b43_ntab_write(dev, B43_NTAB32(16, 0x100), 20); - b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x138), 2, ntab7_138_146); - b43_ntab_write(dev, B43_NTAB16(7, 0x141), 0x77); - b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x133), 3, ntab7_133); - b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x146), 2, ntab7_138_146); - b43_ntab_write(dev, B43_NTAB16(7, 0x123), 0x77); - b43_ntab_write(dev, B43_NTAB16(7, 0x12A), 0x77); - - b43_ntab_read_bulk(dev, B43_NTAB32(16, 0x02), 1, noise_tbl); - noise_tbl[1] = b43_is_40mhz(dev) ? 0x14D : 0x18D; - b43_ntab_write_bulk(dev, B43_NTAB32(16, 0x02), 2, noise_tbl); - - b43_ntab_read_bulk(dev, B43_NTAB32(16, 0x7E), 1, noise_tbl); - noise_tbl[1] = b43_is_40mhz(dev) ? 0x14D : 0x18D; - b43_ntab_write_bulk(dev, B43_NTAB32(16, 0x7E), 2, noise_tbl); - - b43_nphy_gain_ctl_workarounds(dev); - - /* TODO - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, - aux_adc_vmid_rev7_core0); - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, - aux_adc_vmid_rev7_core1); - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0C), 4, - aux_adc_gain_rev7); - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1C), 4, - aux_adc_gain_rev7); - */ -} - -static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy = dev->phy.n; - struct ssb_sprom *sprom = dev->dev->bus_sprom; - - /* TX to RX */ - u8 tx2rx_events[7] = { 0x4, 0x3, 0x5, 0x2, 0x1, 0x8, 0x1F }; - u8 tx2rx_delays[7] = { 8, 4, 4, 4, 4, 6, 1 }; - /* RX to TX */ - u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3, - 0x1F }; - u8 rx2tx_delays_ipa[9] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 }; - u8 rx2tx_events[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0x3, 0x4, 0x1F }; - u8 rx2tx_delays[9] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 }; - - u16 vmids[5][4] = { - { 0xa2, 0xb4, 0xb4, 0x89, }, /* 0 */ - { 0xb4, 0xb4, 0xb4, 0x24, }, /* 1 */ - { 0xa2, 0xb4, 0xb4, 0x74, }, /* 2 */ - { 0xa2, 0xb4, 0xb4, 0x270, }, /* 3 */ - { 0xa2, 0xb4, 0xb4, 0x00, }, /* 4 and 5 */ - }; - u16 gains[5][4] = { - { 0x02, 0x02, 0x02, 0x00, }, /* 0 */ - { 0x02, 0x02, 0x02, 0x02, }, /* 1 */ - { 0x02, 0x02, 0x02, 0x04, }, /* 2 */ - { 0x02, 0x02, 0x02, 0x00, }, /* 3 */ - { 0x02, 0x02, 0x02, 0x00, }, /* 4 and 5 */ - }; - u16 *vmid, *gain; - - u8 pdet_range; - u16 tmp16; - u32 tmp32; - - b43_phy_write(dev, B43_NPHY_FORCEFRONT0, 0x1f8); - b43_phy_write(dev, B43_NPHY_FORCEFRONT1, 0x1f8); - - tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0)); - tmp32 &= 0xffffff; - b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32); - - b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x0125); - b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x01B3); - b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x0105); - b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x016E); - b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0x00CD); - b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x0020); - - b43_phy_write(dev, B43_NPHY_REV3_C1_CLIP_LOGAIN_B, 0x000C); - b43_phy_write(dev, B43_NPHY_REV3_C2_CLIP_LOGAIN_B, 0x000C); - - /* TX to RX */ - b43_nphy_set_rf_sequence(dev, 1, tx2rx_events, tx2rx_delays, - ARRAY_SIZE(tx2rx_events)); - - /* RX to TX */ - if (b43_nphy_ipa(dev)) - b43_nphy_set_rf_sequence(dev, 0, rx2tx_events_ipa, - rx2tx_delays_ipa, ARRAY_SIZE(rx2tx_events_ipa)); - if (nphy->hw_phyrxchain != 3 && - nphy->hw_phyrxchain != nphy->hw_phytxchain) { - if (b43_nphy_ipa(dev)) { - rx2tx_delays[5] = 59; - rx2tx_delays[6] = 1; - rx2tx_events[7] = 0x1F; - } - b43_nphy_set_rf_sequence(dev, 0, rx2tx_events, rx2tx_delays, - ARRAY_SIZE(rx2tx_events)); - } - - tmp16 = (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) ? - 0x2 : 0x9C40; - b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, tmp16); - - b43_phy_maskset(dev, B43_NPHY_SGILTRNOFFSET, 0xF0FF, 0x0700); - - if (!b43_is_40mhz(dev)) { - b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D); - b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D); - } else { - b43_ntab_write(dev, B43_NTAB32(16, 3), 0x14D); - b43_ntab_write(dev, B43_NTAB32(16, 127), 0x14D); - } - - b43_nphy_gain_ctl_workarounds(dev); - - b43_ntab_write(dev, B43_NTAB16(8, 0), 2); - b43_ntab_write(dev, B43_NTAB16(8, 16), 2); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - pdet_range = sprom->fem.ghz2.pdet_range; - else - pdet_range = sprom->fem.ghz5.pdet_range; - vmid = vmids[min_t(u16, pdet_range, 4)]; - gain = gains[min_t(u16, pdet_range, 4)]; - switch (pdet_range) { - case 3: - if (!(dev->phy.rev >= 4 && - b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) - break; - /* FALL THROUGH */ - case 0: - case 1: - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid); - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid); - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain); - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain); - break; - case 2: - if (dev->phy.rev >= 6) { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - vmid[3] = 0x94; - else - vmid[3] = 0x8e; - gain[3] = 3; - } else if (dev->phy.rev == 5) { - vmid[3] = 0x84; - gain[3] = 2; - } - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid); - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid); - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain); - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain); - break; - case 4: - case 5: - if (b43_current_band(dev->wl) != IEEE80211_BAND_2GHZ) { - if (pdet_range == 4) { - vmid[3] = 0x8e; - tmp16 = 0x96; - gain[3] = 0x2; - } else { - vmid[3] = 0x89; - tmp16 = 0x89; - gain[3] = 0; - } - } else { - if (pdet_range == 4) { - vmid[3] = 0x89; - tmp16 = 0x8b; - gain[3] = 0x2; - } else { - vmid[3] = 0x74; - tmp16 = 0x70; - gain[3] = 0; - } - } - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid); - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain); - vmid[3] = tmp16; - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid); - b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain); - break; - } - - b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_MAST_BIAS, 0x00); - b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_MAST_BIAS, 0x00); - b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_MAIN, 0x06); - b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_MAIN, 0x06); - b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_AUX, 0x07); - b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_AUX, 0x07); - b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_LOB_BIAS, 0x88); - b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_LOB_BIAS, 0x88); - b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_CMFB_IDAC, 0x00); - b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_CMFB_IDAC, 0x00); - b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXG_CMFB_IDAC, 0x00); - b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXG_CMFB_IDAC, 0x00); - - /* N PHY WAR TX Chain Update with hw_phytxchain as argument */ - - if ((sprom->boardflags2_lo & B43_BFL2_APLL_WAR && - b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) || - (sprom->boardflags2_lo & B43_BFL2_GPLL_WAR && - b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) - tmp32 = 0x00088888; - else - tmp32 = 0x88888888; - b43_ntab_write(dev, B43_NTAB32(30, 1), tmp32); - b43_ntab_write(dev, B43_NTAB32(30, 2), tmp32); - b43_ntab_write(dev, B43_NTAB32(30, 3), tmp32); - - if (dev->phy.rev == 4 && - b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - b43_radio_write(dev, B2056_TX0 | B2056_TX_GMBB_IDAC, - 0x70); - b43_radio_write(dev, B2056_TX1 | B2056_TX_GMBB_IDAC, - 0x70); - } - - /* Dropped probably-always-true condition */ - b43_phy_write(dev, B43_NPHY_ED_CRS40ASSERTTHRESH0, 0x03eb); - b43_phy_write(dev, B43_NPHY_ED_CRS40ASSERTTHRESH1, 0x03eb); - b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH0, 0x0341); - b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH1, 0x0341); - b43_phy_write(dev, B43_NPHY_ED_CRS20LASSERTTHRESH0, 0x042b); - b43_phy_write(dev, B43_NPHY_ED_CRS20LASSERTTHRESH1, 0x042b); - b43_phy_write(dev, B43_NPHY_ED_CRS20LDEASSERTTHRESH0, 0x0381); - b43_phy_write(dev, B43_NPHY_ED_CRS20LDEASSERTTHRESH1, 0x0381); - b43_phy_write(dev, B43_NPHY_ED_CRS20UASSERTTHRESH0, 0x042b); - b43_phy_write(dev, B43_NPHY_ED_CRS20UASSERTTHRESH1, 0x042b); - b43_phy_write(dev, B43_NPHY_ED_CRS20UDEASSERTTHRESH0, 0x0381); - b43_phy_write(dev, B43_NPHY_ED_CRS20UDEASSERTTHRESH1, 0x0381); - - if (dev->phy.rev >= 6 && sprom->boardflags2_lo & B43_BFL2_SINGLEANT_CCK) - ; /* TODO: 0x0080000000000000 HF */ -} - -static void b43_nphy_workarounds_rev1_2(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = phy->n; - - u8 events1[7] = { 0x0, 0x1, 0x2, 0x8, 0x4, 0x5, 0x3 }; - u8 delays1[7] = { 0x8, 0x6, 0x6, 0x2, 0x4, 0x3C, 0x1 }; - - u8 events2[7] = { 0x0, 0x3, 0x5, 0x4, 0x2, 0x1, 0x8 }; - u8 delays2[7] = { 0x8, 0x6, 0x2, 0x4, 0x4, 0x6, 0x1 }; - - if (sprom->boardflags2_lo & B43_BFL2_SKWRKFEM_BRD || - dev->dev->board_type == BCMA_BOARD_TYPE_BCM943224M93) { - delays1[0] = 0x1; - delays1[5] = 0x14; - } - - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ && - nphy->band5g_pwrgain) { - b43_radio_mask(dev, B2055_C1_TX_RF_SPARE, ~0x8); - b43_radio_mask(dev, B2055_C2_TX_RF_SPARE, ~0x8); - } else { - b43_radio_set(dev, B2055_C1_TX_RF_SPARE, 0x8); - b43_radio_set(dev, B2055_C2_TX_RF_SPARE, 0x8); - } - - b43_ntab_write(dev, B43_NTAB16(8, 0x00), 0x000A); - b43_ntab_write(dev, B43_NTAB16(8, 0x10), 0x000A); - if (dev->phy.rev < 3) { - b43_ntab_write(dev, B43_NTAB16(8, 0x02), 0xCDAA); - b43_ntab_write(dev, B43_NTAB16(8, 0x12), 0xCDAA); - } - - if (dev->phy.rev < 2) { - b43_ntab_write(dev, B43_NTAB16(8, 0x08), 0x0000); - b43_ntab_write(dev, B43_NTAB16(8, 0x18), 0x0000); - b43_ntab_write(dev, B43_NTAB16(8, 0x07), 0x7AAB); - b43_ntab_write(dev, B43_NTAB16(8, 0x17), 0x7AAB); - b43_ntab_write(dev, B43_NTAB16(8, 0x06), 0x0800); - b43_ntab_write(dev, B43_NTAB16(8, 0x16), 0x0800); - } - - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8); - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301); - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8); - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301); - - b43_nphy_set_rf_sequence(dev, 0, events1, delays1, 7); - b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 7); - - b43_nphy_gain_ctl_workarounds(dev); - - if (dev->phy.rev < 2) { - if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2) - b43_hf_write(dev, b43_hf_read(dev) | - B43_HF_MLADVW); - } else if (dev->phy.rev == 2) { - b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0); - b43_phy_write(dev, B43_NPHY_CRSCHECK3, 0); - } - - if (dev->phy.rev < 2) - b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL, - ~B43_NPHY_SCRAM_SIGCTL_SCM); - - /* Set phase track alpha and beta */ - b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125); - b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3); - b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105); - b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E); - b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD); - b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20); - - if (dev->phy.rev < 3) { - b43_phy_mask(dev, B43_NPHY_PIL_DW1, - ~B43_NPHY_PIL_DW_64QAM & 0xFFFF); - b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B1, 0xB5); - b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B2, 0xA4); - b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B3, 0x00); - } - - if (dev->phy.rev == 2) - b43_phy_set(dev, B43_NPHY_FINERX2_CGC, - B43_NPHY_FINERX2_CGC_DECGC); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Workarounds */ -static void b43_nphy_workarounds(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = phy->n; - - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - b43_nphy_classifier(dev, 1, 0); - else - b43_nphy_classifier(dev, 1, 1); - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 1); - - b43_phy_set(dev, B43_NPHY_IQFLIP, - B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2); - - /* TODO: rev19+ */ - if (dev->phy.rev >= 7) - b43_nphy_workarounds_rev7plus(dev); - else if (dev->phy.rev >= 3) - b43_nphy_workarounds_rev3plus(dev); - else - b43_nphy_workarounds_rev1_2(dev); - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 0); -} - -/************************************************** - * Tx/Rx common - **************************************************/ - -/* - * Transmits a known value for LO calibration - * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TXTone - */ -static int b43_nphy_tx_tone(struct b43_wldev *dev, u32 freq, u16 max_val, - bool iqmode, bool dac_test, bool modify_bbmult) -{ - u16 samp = b43_nphy_gen_load_samples(dev, freq, max_val, dac_test); - if (samp == 0) - return -1; - b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test, - modify_bbmult); - return 0; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Chains */ -static void b43_nphy_update_txrx_chain(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy = dev->phy.n; - - bool override = false; - u16 chain = 0x33; - - if (nphy->txrx_chain == 0) { - chain = 0x11; - override = true; - } else if (nphy->txrx_chain == 1) { - chain = 0x22; - override = true; - } - - b43_phy_maskset(dev, B43_NPHY_RFSEQCA, - ~(B43_NPHY_RFSEQCA_TXEN | B43_NPHY_RFSEQCA_RXEN), - chain); - - if (override) - b43_phy_set(dev, B43_NPHY_RFSEQMODE, - B43_NPHY_RFSEQMODE_CAOVER); - else - b43_phy_mask(dev, B43_NPHY_RFSEQMODE, - ~B43_NPHY_RFSEQMODE_CAOVER); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/stop-playback */ -static void b43_nphy_stop_playback(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - u16 tmp; - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 1); - - tmp = b43_phy_read(dev, B43_NPHY_SAMP_STAT); - if (tmp & 0x1) - b43_phy_set(dev, B43_NPHY_SAMP_CMD, B43_NPHY_SAMP_CMD_STOP); - else if (tmp & 0x2) - b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF); - - b43_phy_mask(dev, B43_NPHY_SAMP_CMD, ~0x0004); - - if (nphy->bb_mult_save & 0x80000000) { - tmp = nphy->bb_mult_save & 0xFFFF; - b43_ntab_write(dev, B43_NTAB16(15, 87), tmp); - nphy->bb_mult_save = 0; - } - - if (phy->rev >= 7 && nphy->lpf_bw_overrode_for_sample_play) { - if (phy->rev >= 19) - b43_nphy_rf_ctl_override_rev19(dev, 0x80, 0, 0, true, - 1); - else - b43_nphy_rf_ctl_override_rev7(dev, 0x80, 0, 0, true, 1); - nphy->lpf_bw_overrode_for_sample_play = false; - } - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 0); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IqCalGainParams */ -static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core, - struct nphy_txgains target, - struct nphy_iqcal_params *params) -{ - struct b43_phy *phy = &dev->phy; - int i, j, indx; - u16 gain; - - if (dev->phy.rev >= 3) { - params->tx_lpf = target.tx_lpf[core]; /* Rev 7+ */ - params->txgm = target.txgm[core]; - params->pga = target.pga[core]; - params->pad = target.pad[core]; - params->ipa = target.ipa[core]; - if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 7) { - params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 3) | (params->ipa) | (params->tx_lpf << 15); - } else { - params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 4) | (params->ipa); - } - for (j = 0; j < 5; j++) - params->ncorr[j] = 0x79; - } else { - gain = (target.pad[core]) | (target.pga[core] << 4) | - (target.txgm[core] << 8); - - indx = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? - 1 : 0; - for (i = 0; i < 9; i++) - if (tbl_iqcal_gainparams[indx][i][0] == gain) - break; - i = min(i, 8); - - params->txgm = tbl_iqcal_gainparams[indx][i][1]; - params->pga = tbl_iqcal_gainparams[indx][i][2]; - params->pad = tbl_iqcal_gainparams[indx][i][3]; - params->cal_gain = (params->txgm << 7) | (params->pga << 4) | - (params->pad << 2); - for (j = 0; j < 4; j++) - params->ncorr[j] = tbl_iqcal_gainparams[indx][i][4 + j]; - } -} - -/************************************************** - * Tx and Rx - **************************************************/ - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlEnable */ -static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - u8 i; - u16 bmask, val, tmp; - enum ieee80211_band band = b43_current_band(dev->wl); - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 1); - - nphy->txpwrctrl = enable; - if (!enable) { - if (dev->phy.rev >= 3 && - (b43_phy_read(dev, B43_NPHY_TXPCTL_CMD) & - (B43_NPHY_TXPCTL_CMD_COEFF | - B43_NPHY_TXPCTL_CMD_HWPCTLEN | - B43_NPHY_TXPCTL_CMD_PCTLEN))) { - /* We disable enabled TX pwr ctl, save it's state */ - nphy->tx_pwr_idx[0] = b43_phy_read(dev, - B43_NPHY_C1_TXPCTL_STAT) & 0x7f; - nphy->tx_pwr_idx[1] = b43_phy_read(dev, - B43_NPHY_C2_TXPCTL_STAT) & 0x7f; - } - - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x6840); - for (i = 0; i < 84; i++) - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0); - - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x6C40); - for (i = 0; i < 84; i++) - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0); - - tmp = B43_NPHY_TXPCTL_CMD_COEFF | B43_NPHY_TXPCTL_CMD_HWPCTLEN; - if (dev->phy.rev >= 3) - tmp |= B43_NPHY_TXPCTL_CMD_PCTLEN; - b43_phy_mask(dev, B43_NPHY_TXPCTL_CMD, ~tmp); - - if (dev->phy.rev >= 3) { - b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x0100); - b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0100); - } else { - b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x4000); - } - - if (dev->phy.rev == 2) - b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, - ~B43_NPHY_BPHY_CTL3_SCALE, 0x53); - else if (dev->phy.rev < 2) - b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, - ~B43_NPHY_BPHY_CTL3_SCALE, 0x5A); - - if (dev->phy.rev < 2 && b43_is_40mhz(dev)) - b43_hf_write(dev, b43_hf_read(dev) | B43_HF_TSSIRPSMW); - } else { - b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84, - nphy->adj_pwr_tbl); - b43_ntab_write_bulk(dev, B43_NTAB16(27, 64), 84, - nphy->adj_pwr_tbl); - - bmask = B43_NPHY_TXPCTL_CMD_COEFF | - B43_NPHY_TXPCTL_CMD_HWPCTLEN; - /* wl does useless check for "enable" param here */ - val = B43_NPHY_TXPCTL_CMD_COEFF | B43_NPHY_TXPCTL_CMD_HWPCTLEN; - if (dev->phy.rev >= 3) { - bmask |= B43_NPHY_TXPCTL_CMD_PCTLEN; - if (val) - val |= B43_NPHY_TXPCTL_CMD_PCTLEN; - } - b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~(bmask), val); - - if (band == IEEE80211_BAND_5GHZ) { - if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 7) { - b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, - ~B43_NPHY_TXPCTL_CMD_INIT, - 0x32); - b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, - ~B43_NPHY_TXPCTL_INIT_PIDXI1, - 0x32); - } else { - b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, - ~B43_NPHY_TXPCTL_CMD_INIT, - 0x64); - if (phy->rev > 1) - b43_phy_maskset(dev, - B43_NPHY_TXPCTL_INIT, - ~B43_NPHY_TXPCTL_INIT_PIDXI1, - 0x64); - } - } - - if (dev->phy.rev >= 3) { - if (nphy->tx_pwr_idx[0] != 128 && - nphy->tx_pwr_idx[1] != 128) { - /* Recover TX pwr ctl state */ - b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, - ~B43_NPHY_TXPCTL_CMD_INIT, - nphy->tx_pwr_idx[0]); - if (dev->phy.rev > 1) - b43_phy_maskset(dev, - B43_NPHY_TXPCTL_INIT, - ~0xff, nphy->tx_pwr_idx[1]); - } - } - - if (phy->rev >= 7) { - /* TODO */ - } - - if (dev->phy.rev >= 3) { - b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x100); - b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x100); - } else { - b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x4000); - } - - if (dev->phy.rev == 2) - b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x3b); - else if (dev->phy.rev < 2) - b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x40); - - if (dev->phy.rev < 2 && b43_is_40mhz(dev)) - b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_TSSIRPSMW); - - if (b43_nphy_ipa(dev)) { - b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x4); - b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x4); - } - } - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 0); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrFix */ -static void b43_nphy_tx_power_fix(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - struct ssb_sprom *sprom = dev->dev->bus_sprom; - - u8 txpi[2], bbmult, i; - u16 tmp, radio_gain, dac_gain; - u16 freq = phy->chandef->chan->center_freq; - u32 txgain; - /* u32 gaintbl; rev3+ */ - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 1); - - /* TODO: rev19+ */ - if (dev->phy.rev >= 7) { - txpi[0] = txpi[1] = 30; - } else if (dev->phy.rev >= 3) { - txpi[0] = 40; - txpi[1] = 40; - } else if (sprom->revision < 4) { - txpi[0] = 72; - txpi[1] = 72; - } else { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - txpi[0] = sprom->txpid2g[0]; - txpi[1] = sprom->txpid2g[1]; - } else if (freq >= 4900 && freq < 5100) { - txpi[0] = sprom->txpid5gl[0]; - txpi[1] = sprom->txpid5gl[1]; - } else if (freq >= 5100 && freq < 5500) { - txpi[0] = sprom->txpid5g[0]; - txpi[1] = sprom->txpid5g[1]; - } else if (freq >= 5500) { - txpi[0] = sprom->txpid5gh[0]; - txpi[1] = sprom->txpid5gh[1]; - } else { - txpi[0] = 91; - txpi[1] = 91; - } - } - if (dev->phy.rev < 7 && - (txpi[0] < 40 || txpi[0] > 100 || txpi[1] < 40 || txpi[1] > 100)) - txpi[0] = txpi[1] = 91; - - /* - for (i = 0; i < 2; i++) { - nphy->txpwrindex[i].index_internal = txpi[i]; - nphy->txpwrindex[i].index_internal_save = txpi[i]; - } - */ - - for (i = 0; i < 2; i++) { - const u32 *table = b43_nphy_get_tx_gain_table(dev); - - if (!table) - break; - txgain = *(table + txpi[i]); - - if (dev->phy.rev >= 3) - radio_gain = (txgain >> 16) & 0x1FFFF; - else - radio_gain = (txgain >> 16) & 0x1FFF; - - if (dev->phy.rev >= 7) - dac_gain = (txgain >> 8) & 0x7; - else - dac_gain = (txgain >> 8) & 0x3F; - bbmult = txgain & 0xFF; - - if (dev->phy.rev >= 3) { - if (i == 0) - b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x0100); - else - b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0100); - } else { - b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x4000); - } - - if (i == 0) - b43_phy_write(dev, B43_NPHY_AFECTL_DACGAIN1, dac_gain); - else - b43_phy_write(dev, B43_NPHY_AFECTL_DACGAIN2, dac_gain); - - b43_ntab_write(dev, B43_NTAB16(0x7, 0x110 + i), radio_gain); - - tmp = b43_ntab_read(dev, B43_NTAB16(0xF, 0x57)); - if (i == 0) - tmp = (tmp & 0x00FF) | (bbmult << 8); - else - tmp = (tmp & 0xFF00) | bbmult; - b43_ntab_write(dev, B43_NTAB16(0xF, 0x57), tmp); - - if (b43_nphy_ipa(dev)) { - u32 tmp32; - u16 reg = (i == 0) ? - B43_NPHY_PAPD_EN0 : B43_NPHY_PAPD_EN1; - tmp32 = b43_ntab_read(dev, B43_NTAB32(26 + i, - 576 + txpi[i])); - b43_phy_maskset(dev, reg, 0xE00F, (u32) tmp32 << 4); - b43_phy_set(dev, reg, 0x4); - } - } - - b43_phy_mask(dev, B43_NPHY_BPHY_CTL2, ~B43_NPHY_BPHY_CTL2_LUT); - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 0); -} - -static void b43_nphy_ipa_internal_tssi_setup(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - u8 core; - u16 r; /* routing */ - - if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 7) { - for (core = 0; core < 2; core++) { - r = core ? 0x190 : 0x170; - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_radio_write(dev, r + 0x5, 0x5); - b43_radio_write(dev, r + 0x9, 0xE); - if (phy->rev != 5) - b43_radio_write(dev, r + 0xA, 0); - if (phy->rev != 7) - b43_radio_write(dev, r + 0xB, 1); - else - b43_radio_write(dev, r + 0xB, 0x31); - } else { - b43_radio_write(dev, r + 0x5, 0x9); - b43_radio_write(dev, r + 0x9, 0xC); - b43_radio_write(dev, r + 0xB, 0x0); - if (phy->rev != 5) - b43_radio_write(dev, r + 0xA, 1); - else - b43_radio_write(dev, r + 0xA, 0x31); - } - b43_radio_write(dev, r + 0x6, 0); - b43_radio_write(dev, r + 0x7, 0); - b43_radio_write(dev, r + 0x8, 3); - b43_radio_write(dev, r + 0xC, 0); - } - } else { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - b43_radio_write(dev, B2056_SYN_RESERVED_ADDR31, 0x128); - else - b43_radio_write(dev, B2056_SYN_RESERVED_ADDR31, 0x80); - b43_radio_write(dev, B2056_SYN_RESERVED_ADDR30, 0); - b43_radio_write(dev, B2056_SYN_GPIO_MASTER1, 0x29); - - for (core = 0; core < 2; core++) { - r = core ? B2056_TX1 : B2056_TX0; - - b43_radio_write(dev, r | B2056_TX_IQCAL_VCM_HG, 0); - b43_radio_write(dev, r | B2056_TX_IQCAL_IDAC, 0); - b43_radio_write(dev, r | B2056_TX_TSSI_VCM, 3); - b43_radio_write(dev, r | B2056_TX_TX_AMP_DET, 0); - b43_radio_write(dev, r | B2056_TX_TSSI_MISC1, 8); - b43_radio_write(dev, r | B2056_TX_TSSI_MISC2, 0); - b43_radio_write(dev, r | B2056_TX_TSSI_MISC3, 0); - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_radio_write(dev, r | B2056_TX_TX_SSI_MASTER, - 0x5); - if (phy->rev != 5) - b43_radio_write(dev, r | B2056_TX_TSSIA, - 0x00); - if (phy->rev >= 5) - b43_radio_write(dev, r | B2056_TX_TSSIG, - 0x31); - else - b43_radio_write(dev, r | B2056_TX_TSSIG, - 0x11); - b43_radio_write(dev, r | B2056_TX_TX_SSI_MUX, - 0xE); - } else { - b43_radio_write(dev, r | B2056_TX_TX_SSI_MASTER, - 0x9); - b43_radio_write(dev, r | B2056_TX_TSSIA, 0x31); - b43_radio_write(dev, r | B2056_TX_TSSIG, 0x0); - b43_radio_write(dev, r | B2056_TX_TX_SSI_MUX, - 0xC); - } - } - } -} - -/* - * Stop radio and transmit known signal. Then check received signal strength to - * get TSSI (Transmit Signal Strength Indicator). - * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlIdleTssi - */ -static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - - u32 tmp; - s32 rssi[4] = { }; - - if (phy->chandef->chan->flags & IEEE80211_CHAN_NO_IR) - return; - - if (b43_nphy_ipa(dev)) - b43_nphy_ipa_internal_tssi_setup(dev); - - if (phy->rev >= 19) - b43_nphy_rf_ctl_override_rev19(dev, 0x1000, 0, 3, false, 0); - else if (phy->rev >= 7) - b43_nphy_rf_ctl_override_rev7(dev, 0x1000, 0, 3, false, 0); - else if (phy->rev >= 3) - b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, false); - - b43_nphy_stop_playback(dev); - b43_nphy_tx_tone(dev, 4000, 0, false, false, false); - udelay(20); - tmp = b43_nphy_poll_rssi(dev, N_RSSI_TSSI_2G, rssi, 1); - b43_nphy_stop_playback(dev); - - b43_nphy_rssi_select(dev, 0, N_RSSI_W1); - - if (phy->rev >= 19) - b43_nphy_rf_ctl_override_rev19(dev, 0x1000, 0, 3, true, 0); - else if (phy->rev >= 7) - b43_nphy_rf_ctl_override_rev7(dev, 0x1000, 0, 3, true, 0); - else if (phy->rev >= 3) - b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, true); - - if (phy->rev >= 19) { - /* TODO */ - return; - } else if (phy->rev >= 3) { - nphy->pwr_ctl_info[0].idle_tssi_5g = (tmp >> 24) & 0xFF; - nphy->pwr_ctl_info[1].idle_tssi_5g = (tmp >> 8) & 0xFF; - } else { - nphy->pwr_ctl_info[0].idle_tssi_5g = (tmp >> 16) & 0xFF; - nphy->pwr_ctl_info[1].idle_tssi_5g = tmp & 0xFF; - } - nphy->pwr_ctl_info[0].idle_tssi_2g = (tmp >> 24) & 0xFF; - nphy->pwr_ctl_info[1].idle_tssi_2g = (tmp >> 8) & 0xFF; -} - -/* http://bcm-v4.sipsolutions.net/PHY/N/TxPwrLimitToTbl */ -static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy = dev->phy.n; - - u8 idx, delta; - u8 i, stf_mode; - - /* Array adj_pwr_tbl corresponds to the hardware table. It consists of - * 21 groups, each containing 4 entries. - * - * First group has entries for CCK modulation. - * The rest of groups has 1 entry per modulation (SISO, CDD, STBC, SDM). - * - * Group 0 is for CCK - * Groups 1..4 use BPSK (group per coding rate) - * Groups 5..8 use QPSK (group per coding rate) - * Groups 9..12 use 16-QAM (group per coding rate) - * Groups 13..16 use 64-QAM (group per coding rate) - * Groups 17..20 are unknown - */ - - for (i = 0; i < 4; i++) - nphy->adj_pwr_tbl[i] = nphy->tx_power_offset[i]; - - for (stf_mode = 0; stf_mode < 4; stf_mode++) { - delta = 0; - switch (stf_mode) { - case 0: - if (b43_is_40mhz(dev) && dev->phy.rev >= 5) { - idx = 68; - } else { - delta = 1; - idx = b43_is_40mhz(dev) ? 52 : 4; - } - break; - case 1: - idx = b43_is_40mhz(dev) ? 76 : 28; - break; - case 2: - idx = b43_is_40mhz(dev) ? 84 : 36; - break; - case 3: - idx = b43_is_40mhz(dev) ? 92 : 44; - break; - } - - for (i = 0; i < 20; i++) { - nphy->adj_pwr_tbl[4 + 4 * i + stf_mode] = - nphy->tx_power_offset[idx]; - if (i == 0) - idx += delta; - if (i == 14) - idx += 1 - delta; - if (i == 3 || i == 4 || i == 7 || i == 8 || i == 11 || - i == 13) - idx += 1; - } - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlSetup */ -static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - struct ssb_sprom *sprom = dev->dev->bus_sprom; - - s16 a1[2], b0[2], b1[2]; - u8 idle[2]; - u8 ppr_max; - s8 target[2]; - s32 num, den, pwr; - u32 regval[64]; - - u16 freq = phy->chandef->chan->center_freq; - u16 tmp; - u16 r; /* routing */ - u8 i, c; - - if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) { - b43_maskset32(dev, B43_MMIO_MACCTL, ~0, 0x200000); - b43_read32(dev, B43_MMIO_MACCTL); - udelay(1); - } - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, true); - - b43_phy_set(dev, B43_NPHY_TSSIMODE, B43_NPHY_TSSIMODE_EN); - if (dev->phy.rev >= 3) - b43_phy_mask(dev, B43_NPHY_TXPCTL_CMD, - ~B43_NPHY_TXPCTL_CMD_PCTLEN & 0xFFFF); - else - b43_phy_set(dev, B43_NPHY_TXPCTL_CMD, - B43_NPHY_TXPCTL_CMD_PCTLEN); - - if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) - b43_maskset32(dev, B43_MMIO_MACCTL, ~0x200000, 0); - - if (sprom->revision < 4) { - idle[0] = nphy->pwr_ctl_info[0].idle_tssi_2g; - idle[1] = nphy->pwr_ctl_info[1].idle_tssi_2g; - target[0] = target[1] = 52; - a1[0] = a1[1] = -424; - b0[0] = b0[1] = 5612; - b1[0] = b1[1] = -1393; - } else { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - for (c = 0; c < 2; c++) { - idle[c] = nphy->pwr_ctl_info[c].idle_tssi_2g; - target[c] = sprom->core_pwr_info[c].maxpwr_2g; - a1[c] = sprom->core_pwr_info[c].pa_2g[0]; - b0[c] = sprom->core_pwr_info[c].pa_2g[1]; - b1[c] = sprom->core_pwr_info[c].pa_2g[2]; - } - } else if (freq >= 4900 && freq < 5100) { - for (c = 0; c < 2; c++) { - idle[c] = nphy->pwr_ctl_info[c].idle_tssi_5g; - target[c] = sprom->core_pwr_info[c].maxpwr_5gl; - a1[c] = sprom->core_pwr_info[c].pa_5gl[0]; - b0[c] = sprom->core_pwr_info[c].pa_5gl[1]; - b1[c] = sprom->core_pwr_info[c].pa_5gl[2]; - } - } else if (freq >= 5100 && freq < 5500) { - for (c = 0; c < 2; c++) { - idle[c] = nphy->pwr_ctl_info[c].idle_tssi_5g; - target[c] = sprom->core_pwr_info[c].maxpwr_5g; - a1[c] = sprom->core_pwr_info[c].pa_5g[0]; - b0[c] = sprom->core_pwr_info[c].pa_5g[1]; - b1[c] = sprom->core_pwr_info[c].pa_5g[2]; - } - } else if (freq >= 5500) { - for (c = 0; c < 2; c++) { - idle[c] = nphy->pwr_ctl_info[c].idle_tssi_5g; - target[c] = sprom->core_pwr_info[c].maxpwr_5gh; - a1[c] = sprom->core_pwr_info[c].pa_5gh[0]; - b0[c] = sprom->core_pwr_info[c].pa_5gh[1]; - b1[c] = sprom->core_pwr_info[c].pa_5gh[2]; - } - } else { - idle[0] = nphy->pwr_ctl_info[0].idle_tssi_5g; - idle[1] = nphy->pwr_ctl_info[1].idle_tssi_5g; - target[0] = target[1] = 52; - a1[0] = a1[1] = -424; - b0[0] = b0[1] = 5612; - b1[0] = b1[1] = -1393; - } - } - - ppr_max = b43_ppr_get_max(dev, &nphy->tx_pwr_max_ppr); - if (ppr_max) { - target[0] = ppr_max; - target[1] = ppr_max; - } - - if (dev->phy.rev >= 3) { - if (sprom->fem.ghz2.tssipos) - b43_phy_set(dev, B43_NPHY_TXPCTL_ITSSI, 0x4000); - if (dev->phy.rev >= 7) { - for (c = 0; c < 2; c++) { - r = c ? 0x190 : 0x170; - if (b43_nphy_ipa(dev)) - b43_radio_write(dev, r + 0x9, (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) ? 0xE : 0xC); - } - } else { - if (b43_nphy_ipa(dev)) { - tmp = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? 0xC : 0xE; - b43_radio_write(dev, - B2056_TX0 | B2056_TX_TX_SSI_MUX, tmp); - b43_radio_write(dev, - B2056_TX1 | B2056_TX_TX_SSI_MUX, tmp); - } else { - b43_radio_write(dev, - B2056_TX0 | B2056_TX_TX_SSI_MUX, 0x11); - b43_radio_write(dev, - B2056_TX1 | B2056_TX_TX_SSI_MUX, 0x11); - } - } - } - - if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) { - b43_maskset32(dev, B43_MMIO_MACCTL, ~0, 0x200000); - b43_read32(dev, B43_MMIO_MACCTL); - udelay(1); - } - - if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 7) { - b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, - ~B43_NPHY_TXPCTL_CMD_INIT, 0x19); - b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, - ~B43_NPHY_TXPCTL_INIT_PIDXI1, 0x19); - } else { - b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, - ~B43_NPHY_TXPCTL_CMD_INIT, 0x40); - if (dev->phy.rev > 1) - b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, - ~B43_NPHY_TXPCTL_INIT_PIDXI1, 0x40); - } - - if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) - b43_maskset32(dev, B43_MMIO_MACCTL, ~0x200000, 0); - - b43_phy_write(dev, B43_NPHY_TXPCTL_N, - 0xF0 << B43_NPHY_TXPCTL_N_TSSID_SHIFT | - 3 << B43_NPHY_TXPCTL_N_NPTIL2_SHIFT); - b43_phy_write(dev, B43_NPHY_TXPCTL_ITSSI, - idle[0] << B43_NPHY_TXPCTL_ITSSI_0_SHIFT | - idle[1] << B43_NPHY_TXPCTL_ITSSI_1_SHIFT | - B43_NPHY_TXPCTL_ITSSI_BINF); - b43_phy_write(dev, B43_NPHY_TXPCTL_TPWR, - target[0] << B43_NPHY_TXPCTL_TPWR_0_SHIFT | - target[1] << B43_NPHY_TXPCTL_TPWR_1_SHIFT); - - for (c = 0; c < 2; c++) { - for (i = 0; i < 64; i++) { - num = 8 * (16 * b0[c] + b1[c] * i); - den = 32768 + a1[c] * i; - pwr = max((4 * num + den / 2) / den, -8); - if (dev->phy.rev < 3 && (i <= (31 - idle[c] + 1))) - pwr = max(pwr, target[c] + 1); - regval[i] = pwr; - } - b43_ntab_write_bulk(dev, B43_NTAB32(26 + c, 0), 64, regval); - } - - b43_nphy_tx_prepare_adjusted_power_table(dev); - b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84, nphy->adj_pwr_tbl); - b43_ntab_write_bulk(dev, B43_NTAB16(27, 64), 84, nphy->adj_pwr_tbl); - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, false); -} - -static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - const u32 *table = NULL; - u32 rfpwr_offset; - u8 pga_gain, pad_gain; - int i; - const s16 *uninitialized_var(rf_pwr_offset_table); - - table = b43_nphy_get_tx_gain_table(dev); - if (!table) - return; - - b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table); - b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table); - - if (phy->rev < 3) - return; - -#if 0 - nphy->gmval = (table[0] >> 16) & 0x7000; -#endif - - if (phy->rev >= 19) { - return; - } else if (phy->rev >= 7) { - rf_pwr_offset_table = b43_ntab_get_rf_pwr_offset_table(dev); - if (!rf_pwr_offset_table) - return; - /* TODO: Enable this once we have gains configured */ - return; - } - - for (i = 0; i < 128; i++) { - if (phy->rev >= 19) { - /* TODO */ - return; - } else if (phy->rev >= 7) { - pga_gain = (table[i] >> 24) & 0xf; - pad_gain = (table[i] >> 19) & 0x1f; - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - rfpwr_offset = rf_pwr_offset_table[pad_gain]; - else - rfpwr_offset = rf_pwr_offset_table[pga_gain]; - } else { - pga_gain = (table[i] >> 24) & 0xF; - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - rfpwr_offset = b43_ntab_papd_pga_gain_delta_ipa_2g[pga_gain]; - else - rfpwr_offset = 0; /* FIXME */ - } - - b43_ntab_write(dev, B43_NTAB32(26, 576 + i), rfpwr_offset); - b43_ntab_write(dev, B43_NTAB32(27, 576 + i), rfpwr_offset); - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PA%20override */ -static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable) -{ - struct b43_phy_n *nphy = dev->phy.n; - enum ieee80211_band band; - u16 tmp; - - if (!enable) { - nphy->rfctrl_intc1_save = b43_phy_read(dev, - B43_NPHY_RFCTL_INTC1); - nphy->rfctrl_intc2_save = b43_phy_read(dev, - B43_NPHY_RFCTL_INTC2); - band = b43_current_band(dev->wl); - if (dev->phy.rev >= 7) { - tmp = 0x1480; - } else if (dev->phy.rev >= 3) { - if (band == IEEE80211_BAND_5GHZ) - tmp = 0x600; - else - tmp = 0x480; - } else { - if (band == IEEE80211_BAND_5GHZ) - tmp = 0x180; - else - tmp = 0x120; - } - b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, tmp); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, tmp); - } else { - b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, - nphy->rfctrl_intc1_save); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, - nphy->rfctrl_intc2_save); - } -} - -/* - * TX low-pass filter bandwidth setup - * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw - */ -static void b43_nphy_tx_lpf_bw(struct b43_wldev *dev) -{ - u16 tmp; - - if (dev->phy.rev < 3 || dev->phy.rev >= 7) - return; - - if (b43_nphy_ipa(dev)) - tmp = b43_is_40mhz(dev) ? 5 : 4; - else - tmp = b43_is_40mhz(dev) ? 3 : 1; - b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2, - (tmp << 9) | (tmp << 6) | (tmp << 3) | tmp); - - if (b43_nphy_ipa(dev)) { - tmp = b43_is_40mhz(dev) ? 4 : 1; - b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S2, - (tmp << 9) | (tmp << 6) | (tmp << 3) | tmp); - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxIqEst */ -static void b43_nphy_rx_iq_est(struct b43_wldev *dev, struct nphy_iq_est *est, - u16 samps, u8 time, bool wait) -{ - int i; - u16 tmp; - - b43_phy_write(dev, B43_NPHY_IQEST_SAMCNT, samps); - b43_phy_maskset(dev, B43_NPHY_IQEST_WT, ~B43_NPHY_IQEST_WT_VAL, time); - if (wait) - b43_phy_set(dev, B43_NPHY_IQEST_CMD, B43_NPHY_IQEST_CMD_MODE); - else - b43_phy_mask(dev, B43_NPHY_IQEST_CMD, ~B43_NPHY_IQEST_CMD_MODE); - - b43_phy_set(dev, B43_NPHY_IQEST_CMD, B43_NPHY_IQEST_CMD_START); - - for (i = 1000; i; i--) { - tmp = b43_phy_read(dev, B43_NPHY_IQEST_CMD); - if (!(tmp & B43_NPHY_IQEST_CMD_START)) { - est->i0_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_IPACC_HI0) << 16) | - b43_phy_read(dev, B43_NPHY_IQEST_IPACC_LO0); - est->q0_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_QPACC_HI0) << 16) | - b43_phy_read(dev, B43_NPHY_IQEST_QPACC_LO0); - est->iq0_prod = (b43_phy_read(dev, B43_NPHY_IQEST_IQACC_HI0) << 16) | - b43_phy_read(dev, B43_NPHY_IQEST_IQACC_LO0); - - est->i1_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_IPACC_HI1) << 16) | - b43_phy_read(dev, B43_NPHY_IQEST_IPACC_LO1); - est->q1_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_QPACC_HI1) << 16) | - b43_phy_read(dev, B43_NPHY_IQEST_QPACC_LO1); - est->iq1_prod = (b43_phy_read(dev, B43_NPHY_IQEST_IQACC_HI1) << 16) | - b43_phy_read(dev, B43_NPHY_IQEST_IQACC_LO1); - return; - } - udelay(10); - } - memset(est, 0, sizeof(*est)); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxIqCoeffs */ -static void b43_nphy_rx_iq_coeffs(struct b43_wldev *dev, bool write, - struct b43_phy_n_iq_comp *pcomp) -{ - if (write) { - b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPA0, pcomp->a0); - b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPB0, pcomp->b0); - b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPA1, pcomp->a1); - b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPB1, pcomp->b1); - } else { - pcomp->a0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPA0); - pcomp->b0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPB0); - pcomp->a1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPA1); - pcomp->b1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPB1); - } -} - -#if 0 -/* Ready but not used anywhere */ -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCalPhyCleanup */ -static void b43_nphy_rx_cal_phy_cleanup(struct b43_wldev *dev, u8 core) -{ - u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; - - b43_phy_write(dev, B43_NPHY_RFSEQCA, regs[0]); - if (core == 0) { - b43_phy_write(dev, B43_NPHY_AFECTL_C1, regs[1]); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, regs[2]); - } else { - b43_phy_write(dev, B43_NPHY_AFECTL_C2, regs[1]); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[2]); - } - b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[3]); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[4]); - b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO1, regs[5]); - b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO2, regs[6]); - b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, regs[7]); - b43_phy_write(dev, B43_NPHY_RFCTL_OVER, regs[8]); - b43_phy_write(dev, B43_NPHY_PAPD_EN0, regs[9]); - b43_phy_write(dev, B43_NPHY_PAPD_EN1, regs[10]); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCalPhySetup */ -static void b43_nphy_rx_cal_phy_setup(struct b43_wldev *dev, u8 core) -{ - u8 rxval, txval; - u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; - - regs[0] = b43_phy_read(dev, B43_NPHY_RFSEQCA); - if (core == 0) { - regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); - regs[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); - } else { - regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); - regs[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); - } - regs[3] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); - regs[4] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); - regs[5] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO1); - regs[6] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO2); - regs[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S1); - regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_OVER); - regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0); - regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1); - - b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001); - b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001); - - b43_phy_maskset(dev, B43_NPHY_RFSEQCA, - ~B43_NPHY_RFSEQCA_RXDIS & 0xFFFF, - ((1 - core) << B43_NPHY_RFSEQCA_RXDIS_SHIFT)); - b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXEN, - ((1 - core) << B43_NPHY_RFSEQCA_TXEN_SHIFT)); - b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_RXEN, - (core << B43_NPHY_RFSEQCA_RXEN_SHIFT)); - b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXDIS, - (core << B43_NPHY_RFSEQCA_TXDIS_SHIFT)); - - if (core == 0) { - b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x0007); - b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x0007); - } else { - b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x0007); - b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0007); - } - - b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA, 0, 3); - b43_nphy_rf_ctl_override(dev, 8, 0, 3, false); - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); - - if (core == 0) { - rxval = 1; - txval = 8; - } else { - rxval = 4; - txval = 2; - } - b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, rxval, - core + 1); - b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, txval, - 2 - core); -} -#endif - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalcRxIqComp */ -static void b43_nphy_calc_rx_iq_comp(struct b43_wldev *dev, u8 mask) -{ - int i; - s32 iq; - u32 ii; - u32 qq; - int iq_nbits, qq_nbits; - int arsh, brsh; - u16 tmp, a, b; - - struct nphy_iq_est est; - struct b43_phy_n_iq_comp old; - struct b43_phy_n_iq_comp new = { }; - bool error = false; - - if (mask == 0) - return; - - b43_nphy_rx_iq_coeffs(dev, false, &old); - b43_nphy_rx_iq_coeffs(dev, true, &new); - b43_nphy_rx_iq_est(dev, &est, 0x4000, 32, false); - new = old; - - for (i = 0; i < 2; i++) { - if (i == 0 && (mask & 1)) { - iq = est.iq0_prod; - ii = est.i0_pwr; - qq = est.q0_pwr; - } else if (i == 1 && (mask & 2)) { - iq = est.iq1_prod; - ii = est.i1_pwr; - qq = est.q1_pwr; - } else { - continue; - } - - if (ii + qq < 2) { - error = true; - break; - } - - iq_nbits = fls(abs(iq)); - qq_nbits = fls(qq); - - arsh = iq_nbits - 20; - if (arsh >= 0) { - a = -((iq << (30 - iq_nbits)) + (ii >> (1 + arsh))); - tmp = ii >> arsh; - } else { - a = -((iq << (30 - iq_nbits)) + (ii << (-1 - arsh))); - tmp = ii << -arsh; - } - if (tmp == 0) { - error = true; - break; - } - a /= tmp; - - brsh = qq_nbits - 11; - if (brsh >= 0) { - b = (qq << (31 - qq_nbits)); - tmp = ii >> brsh; - } else { - b = (qq << (31 - qq_nbits)); - tmp = ii << -brsh; - } - if (tmp == 0) { - error = true; - break; - } - b = int_sqrt(b / tmp - a * a) - (1 << 10); - - if (i == 0 && (mask & 0x1)) { - if (dev->phy.rev >= 3) { - new.a0 = a & 0x3FF; - new.b0 = b & 0x3FF; - } else { - new.a0 = b & 0x3FF; - new.b0 = a & 0x3FF; - } - } else if (i == 1 && (mask & 0x2)) { - if (dev->phy.rev >= 3) { - new.a1 = a & 0x3FF; - new.b1 = b & 0x3FF; - } else { - new.a1 = b & 0x3FF; - new.b1 = a & 0x3FF; - } - } - } - - if (error) - new = old; - - b43_nphy_rx_iq_coeffs(dev, true, &new); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxIqWar */ -static void b43_nphy_tx_iq_workaround(struct b43_wldev *dev) -{ - u16 array[4]; - b43_ntab_read_bulk(dev, B43_NTAB16(0xF, 0x50), 4, array); - - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW0, array[0]); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW1, array[1]); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW2, array[2]); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW3, array[3]); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SpurWar */ -static void b43_nphy_spur_workaround(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy = dev->phy.n; - - u8 channel = dev->phy.channel; - int tone[2] = { 57, 58 }; - u32 noise[2] = { 0x3FF, 0x3FF }; - - B43_WARN_ON(dev->phy.rev < 3); - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 1); - - if (nphy->gband_spurwar_en) { - /* TODO: N PHY Adjust Analog Pfbw (7) */ - if (channel == 11 && b43_is_40mhz(dev)) - ; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/ - else - ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/ - /* TODO: N PHY Adjust CRS Min Power (0x1E) */ - } - - if (nphy->aband_spurwar_en) { - if (channel == 54) { - tone[0] = 0x20; - noise[0] = 0x25F; - } else if (channel == 38 || channel == 102 || channel == 118) { - if (0 /* FIXME */) { - tone[0] = 0x20; - noise[0] = 0x21F; - } else { - tone[0] = 0; - noise[0] = 0; - } - } else if (channel == 134) { - tone[0] = 0x20; - noise[0] = 0x21F; - } else if (channel == 151) { - tone[0] = 0x10; - noise[0] = 0x23F; - } else if (channel == 153 || channel == 161) { - tone[0] = 0x30; - noise[0] = 0x23F; - } else { - tone[0] = 0; - noise[0] = 0; - } - - if (!tone[0] && !noise[0]) - ; /* TODO: N PHY Adjust Min Noise Var(1, tone, noise)*/ - else - ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/ - } - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 0); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlCoefSetup */ -static void b43_nphy_tx_pwr_ctrl_coef_setup(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy = dev->phy.n; - int i, j; - u32 tmp; - u32 cur_real, cur_imag, real_part, imag_part; - - u16 buffer[7]; - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, true); - - b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer); - - for (i = 0; i < 2; i++) { - tmp = ((buffer[i * 2] & 0x3FF) << 10) | - (buffer[i * 2 + 1] & 0x3FF); - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, - (((i + 26) << 10) | 320)); - for (j = 0; j < 128; j++) { - b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, - ((tmp >> 16) & 0xFFFF)); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, - (tmp & 0xFFFF)); - } - } - - for (i = 0; i < 2; i++) { - tmp = buffer[5 + i]; - real_part = (tmp >> 8) & 0xFF; - imag_part = (tmp & 0xFF); - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, - (((i + 26) << 10) | 448)); - - if (dev->phy.rev >= 3) { - cur_real = real_part; - cur_imag = imag_part; - tmp = ((cur_real & 0xFF) << 8) | (cur_imag & 0xFF); - } - - for (j = 0; j < 128; j++) { - if (dev->phy.rev < 3) { - cur_real = (real_part * loscale[j] + 128) >> 8; - cur_imag = (imag_part * loscale[j] + 128) >> 8; - tmp = ((cur_real & 0xFF) << 8) | - (cur_imag & 0xFF); - } - b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, - ((tmp >> 16) & 0xFFFF)); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, - (tmp & 0xFFFF)); - } - } - - if (dev->phy.rev >= 3) { - b43_shm_write16(dev, B43_SHM_SHARED, - B43_SHM_SH_NPHY_TXPWR_INDX0, 0xFFFF); - b43_shm_write16(dev, B43_SHM_SHARED, - B43_SHM_SH_NPHY_TXPWR_INDX1, 0xFFFF); - } - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, false); -} - -/* - * Restore RSSI Calibration - * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreRssiCal - */ -static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy = dev->phy.n; - - u16 *rssical_radio_regs = NULL; - u16 *rssical_phy_regs = NULL; - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if (!nphy->rssical_chanspec_2G.center_freq) - return; - rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_2G; - rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_2G; - } else { - if (!nphy->rssical_chanspec_5G.center_freq) - return; - rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_5G; - rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G; - } - - if (dev->phy.rev >= 19) { - /* TODO */ - } else if (dev->phy.rev >= 7) { - b43_radio_maskset(dev, R2057_NB_MASTER_CORE0, ~R2057_VCM_MASK, - rssical_radio_regs[0]); - b43_radio_maskset(dev, R2057_NB_MASTER_CORE1, ~R2057_VCM_MASK, - rssical_radio_regs[1]); - } else { - b43_radio_maskset(dev, B2056_RX0 | B2056_RX_RSSI_MISC, 0xE3, - rssical_radio_regs[0]); - b43_radio_maskset(dev, B2056_RX1 | B2056_RX_RSSI_MISC, 0xE3, - rssical_radio_regs[1]); - } - - b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, rssical_phy_regs[0]); - b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, rssical_phy_regs[1]); - b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, rssical_phy_regs[2]); - b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, rssical_phy_regs[3]); - - b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, rssical_phy_regs[4]); - b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, rssical_phy_regs[5]); - b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, rssical_phy_regs[6]); - b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, rssical_phy_regs[7]); - - b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, rssical_phy_regs[8]); - b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, rssical_phy_regs[9]); - b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, rssical_phy_regs[10]); - b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, rssical_phy_regs[11]); -} - -static void b43_nphy_tx_cal_radio_setup_rev19(struct b43_wldev *dev) -{ - /* TODO */ -} - -static void b43_nphy_tx_cal_radio_setup_rev7(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - u16 *save = nphy->tx_rx_cal_radio_saveregs; - int core, off; - u16 r, tmp; - - for (core = 0; core < 2; core++) { - r = core ? 0x20 : 0; - off = core * 11; - - save[off + 0] = b43_radio_read(dev, r + R2057_TX0_TX_SSI_MASTER); - save[off + 1] = b43_radio_read(dev, r + R2057_TX0_IQCAL_VCM_HG); - save[off + 2] = b43_radio_read(dev, r + R2057_TX0_IQCAL_IDAC); - save[off + 3] = b43_radio_read(dev, r + R2057_TX0_TSSI_VCM); - save[off + 4] = 0; - save[off + 5] = b43_radio_read(dev, r + R2057_TX0_TX_SSI_MUX); - if (phy->radio_rev != 5) - save[off + 6] = b43_radio_read(dev, r + R2057_TX0_TSSIA); - save[off + 7] = b43_radio_read(dev, r + R2057_TX0_TSSIG); - save[off + 8] = b43_radio_read(dev, r + R2057_TX0_TSSI_MISC1); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - b43_radio_write(dev, r + R2057_TX0_TX_SSI_MASTER, 0xA); - b43_radio_write(dev, r + R2057_TX0_IQCAL_VCM_HG, 0x43); - b43_radio_write(dev, r + R2057_TX0_IQCAL_IDAC, 0x55); - b43_radio_write(dev, r + R2057_TX0_TSSI_VCM, 0); - b43_radio_write(dev, r + R2057_TX0_TSSIG, 0); - if (nphy->use_int_tx_iq_lo_cal) { - b43_radio_write(dev, r + R2057_TX0_TX_SSI_MUX, 0x4); - tmp = true ? 0x31 : 0x21; /* TODO */ - b43_radio_write(dev, r + R2057_TX0_TSSIA, tmp); - } - b43_radio_write(dev, r + R2057_TX0_TSSI_MISC1, 0x00); - } else { - b43_radio_write(dev, r + R2057_TX0_TX_SSI_MASTER, 0x6); - b43_radio_write(dev, r + R2057_TX0_IQCAL_VCM_HG, 0x43); - b43_radio_write(dev, r + R2057_TX0_IQCAL_IDAC, 0x55); - b43_radio_write(dev, r + R2057_TX0_TSSI_VCM, 0); - - if (phy->radio_rev != 5) - b43_radio_write(dev, r + R2057_TX0_TSSIA, 0); - if (nphy->use_int_tx_iq_lo_cal) { - b43_radio_write(dev, r + R2057_TX0_TX_SSI_MUX, 0x6); - tmp = true ? 0x31 : 0x21; /* TODO */ - b43_radio_write(dev, r + R2057_TX0_TSSIG, tmp); - } - b43_radio_write(dev, r + R2057_TX0_TSSI_MISC1, 0); - } - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalRadioSetup */ -static void b43_nphy_tx_cal_radio_setup(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - u16 *save = nphy->tx_rx_cal_radio_saveregs; - u16 tmp; - u8 offset, i; - - if (phy->rev >= 19) { - b43_nphy_tx_cal_radio_setup_rev19(dev); - } else if (phy->rev >= 7) { - b43_nphy_tx_cal_radio_setup_rev7(dev); - } else if (phy->rev >= 3) { - for (i = 0; i < 2; i++) { - tmp = (i == 0) ? 0x2000 : 0x3000; - offset = i * 11; - - save[offset + 0] = b43_radio_read(dev, B2055_CAL_RVARCTL); - save[offset + 1] = b43_radio_read(dev, B2055_CAL_LPOCTL); - save[offset + 2] = b43_radio_read(dev, B2055_CAL_TS); - save[offset + 3] = b43_radio_read(dev, B2055_CAL_RCCALRTS); - save[offset + 4] = b43_radio_read(dev, B2055_CAL_RCALRTS); - save[offset + 5] = b43_radio_read(dev, B2055_PADDRV); - save[offset + 6] = b43_radio_read(dev, B2055_XOCTL1); - save[offset + 7] = b43_radio_read(dev, B2055_XOCTL2); - save[offset + 8] = b43_radio_read(dev, B2055_XOREGUL); - save[offset + 9] = b43_radio_read(dev, B2055_XOMISC); - save[offset + 10] = b43_radio_read(dev, B2055_PLL_LFC1); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - b43_radio_write(dev, tmp | B2055_CAL_RVARCTL, 0x0A); - b43_radio_write(dev, tmp | B2055_CAL_LPOCTL, 0x40); - b43_radio_write(dev, tmp | B2055_CAL_TS, 0x55); - b43_radio_write(dev, tmp | B2055_CAL_RCCALRTS, 0); - b43_radio_write(dev, tmp | B2055_CAL_RCALRTS, 0); - if (nphy->ipa5g_on) { - b43_radio_write(dev, tmp | B2055_PADDRV, 4); - b43_radio_write(dev, tmp | B2055_XOCTL1, 1); - } else { - b43_radio_write(dev, tmp | B2055_PADDRV, 0); - b43_radio_write(dev, tmp | B2055_XOCTL1, 0x2F); - } - b43_radio_write(dev, tmp | B2055_XOCTL2, 0); - } else { - b43_radio_write(dev, tmp | B2055_CAL_RVARCTL, 0x06); - b43_radio_write(dev, tmp | B2055_CAL_LPOCTL, 0x40); - b43_radio_write(dev, tmp | B2055_CAL_TS, 0x55); - b43_radio_write(dev, tmp | B2055_CAL_RCCALRTS, 0); - b43_radio_write(dev, tmp | B2055_CAL_RCALRTS, 0); - b43_radio_write(dev, tmp | B2055_XOCTL1, 0); - if (nphy->ipa2g_on) { - b43_radio_write(dev, tmp | B2055_PADDRV, 6); - b43_radio_write(dev, tmp | B2055_XOCTL2, - (dev->phy.rev < 5) ? 0x11 : 0x01); - } else { - b43_radio_write(dev, tmp | B2055_PADDRV, 0); - b43_radio_write(dev, tmp | B2055_XOCTL2, 0); - } - } - b43_radio_write(dev, tmp | B2055_XOREGUL, 0); - b43_radio_write(dev, tmp | B2055_XOMISC, 0); - b43_radio_write(dev, tmp | B2055_PLL_LFC1, 0); - } - } else { - save[0] = b43_radio_read(dev, B2055_C1_TX_RF_IQCAL1); - b43_radio_write(dev, B2055_C1_TX_RF_IQCAL1, 0x29); - - save[1] = b43_radio_read(dev, B2055_C1_TX_RF_IQCAL2); - b43_radio_write(dev, B2055_C1_TX_RF_IQCAL2, 0x54); - - save[2] = b43_radio_read(dev, B2055_C2_TX_RF_IQCAL1); - b43_radio_write(dev, B2055_C2_TX_RF_IQCAL1, 0x29); - - save[3] = b43_radio_read(dev, B2055_C2_TX_RF_IQCAL2); - b43_radio_write(dev, B2055_C2_TX_RF_IQCAL2, 0x54); - - save[3] = b43_radio_read(dev, B2055_C1_PWRDET_RXTX); - save[4] = b43_radio_read(dev, B2055_C2_PWRDET_RXTX); - - if (!(b43_phy_read(dev, B43_NPHY_BANDCTL) & - B43_NPHY_BANDCTL_5GHZ)) { - b43_radio_write(dev, B2055_C1_PWRDET_RXTX, 0x04); - b43_radio_write(dev, B2055_C2_PWRDET_RXTX, 0x04); - } else { - b43_radio_write(dev, B2055_C1_PWRDET_RXTX, 0x20); - b43_radio_write(dev, B2055_C2_PWRDET_RXTX, 0x20); - } - - if (dev->phy.rev < 2) { - b43_radio_set(dev, B2055_C1_TX_BB_MXGM, 0x20); - b43_radio_set(dev, B2055_C2_TX_BB_MXGM, 0x20); - } else { - b43_radio_mask(dev, B2055_C1_TX_BB_MXGM, ~0x20); - b43_radio_mask(dev, B2055_C2_TX_BB_MXGM, ~0x20); - } - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/UpdateTxCalLadder */ -static void b43_nphy_update_tx_cal_ladder(struct b43_wldev *dev, u16 core) -{ - struct b43_phy_n *nphy = dev->phy.n; - int i; - u16 scale, entry; - - u16 tmp = nphy->txcal_bbmult; - if (core == 0) - tmp >>= 8; - tmp &= 0xff; - - for (i = 0; i < 18; i++) { - scale = (ladder_lo[i].percent * tmp) / 100; - entry = ((scale & 0xFF) << 8) | ladder_lo[i].g_env; - b43_ntab_write(dev, B43_NTAB16(15, i), entry); - - scale = (ladder_iq[i].percent * tmp) / 100; - entry = ((scale & 0xFF) << 8) | ladder_iq[i].g_env; - b43_ntab_write(dev, B43_NTAB16(15, i + 32), entry); - } -} - -static void b43_nphy_pa_set_tx_dig_filter(struct b43_wldev *dev, u16 offset, - const s16 *filter) -{ - int i; - - offset = B43_PHY_N(offset); - - for (i = 0; i < 15; i++, offset++) - b43_phy_write(dev, offset, filter[i]); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ExtPaSetTxDigiFilts */ -static void b43_nphy_ext_pa_set_tx_dig_filters(struct b43_wldev *dev) -{ - b43_nphy_pa_set_tx_dig_filter(dev, 0x2C5, - tbl_tx_filter_coef_rev4[2]); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IpaSetTxDigiFilts */ -static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev) -{ - /* B43_NPHY_TXF_20CO_S0A1, B43_NPHY_TXF_40CO_S0A1, unknown */ - static const u16 offset[] = { 0x186, 0x195, 0x2C5 }; - static const s16 dig_filter_phy_rev16[] = { - -375, 136, -407, 208, -1527, - 956, 93, 186, 93, 230, - -44, 230, 201, -191, 201, - }; - int i; - - for (i = 0; i < 3; i++) - b43_nphy_pa_set_tx_dig_filter(dev, offset[i], - tbl_tx_filter_coef_rev4[i]); - - /* Verified with BCM43227 and BCM43228 */ - if (dev->phy.rev == 16) - b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16); - - /* Verified with BCM43131 and BCM43217 */ - if (dev->phy.rev == 17) { - b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16); - b43_nphy_pa_set_tx_dig_filter(dev, 0x195, - tbl_tx_filter_coef_rev4[1]); - } - - if (b43_is_40mhz(dev)) { - b43_nphy_pa_set_tx_dig_filter(dev, 0x186, - tbl_tx_filter_coef_rev4[3]); - } else { - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - b43_nphy_pa_set_tx_dig_filter(dev, 0x186, - tbl_tx_filter_coef_rev4[5]); - if (dev->phy.channel == 14) - b43_nphy_pa_set_tx_dig_filter(dev, 0x186, - tbl_tx_filter_coef_rev4[6]); - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetTxGain */ -static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy = dev->phy.n; - - u16 curr_gain[2]; - struct nphy_txgains target; - const u32 *table = NULL; - - if (!nphy->txpwrctrl) { - int i; - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, true); - b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, curr_gain); - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, false); - - for (i = 0; i < 2; ++i) { - if (dev->phy.rev >= 7) { - target.ipa[i] = curr_gain[i] & 0x0007; - target.pad[i] = (curr_gain[i] & 0x00F8) >> 3; - target.pga[i] = (curr_gain[i] & 0x0F00) >> 8; - target.txgm[i] = (curr_gain[i] & 0x7000) >> 12; - target.tx_lpf[i] = (curr_gain[i] & 0x8000) >> 15; - } else if (dev->phy.rev >= 3) { - target.ipa[i] = curr_gain[i] & 0x000F; - target.pad[i] = (curr_gain[i] & 0x00F0) >> 4; - target.pga[i] = (curr_gain[i] & 0x0F00) >> 8; - target.txgm[i] = (curr_gain[i] & 0x7000) >> 12; - } else { - target.ipa[i] = curr_gain[i] & 0x0003; - target.pad[i] = (curr_gain[i] & 0x000C) >> 2; - target.pga[i] = (curr_gain[i] & 0x0070) >> 4; - target.txgm[i] = (curr_gain[i] & 0x0380) >> 7; - } - } - } else { - int i; - u16 index[2]; - index[0] = (b43_phy_read(dev, B43_NPHY_C1_TXPCTL_STAT) & - B43_NPHY_TXPCTL_STAT_BIDX) >> - B43_NPHY_TXPCTL_STAT_BIDX_SHIFT; - index[1] = (b43_phy_read(dev, B43_NPHY_C2_TXPCTL_STAT) & - B43_NPHY_TXPCTL_STAT_BIDX) >> - B43_NPHY_TXPCTL_STAT_BIDX_SHIFT; - - for (i = 0; i < 2; ++i) { - table = b43_nphy_get_tx_gain_table(dev); - if (!table) - break; - - if (dev->phy.rev >= 7) { - target.ipa[i] = (table[index[i]] >> 16) & 0x7; - target.pad[i] = (table[index[i]] >> 19) & 0x1F; - target.pga[i] = (table[index[i]] >> 24) & 0xF; - target.txgm[i] = (table[index[i]] >> 28) & 0x7; - target.tx_lpf[i] = (table[index[i]] >> 31) & 0x1; - } else if (dev->phy.rev >= 3) { - target.ipa[i] = (table[index[i]] >> 16) & 0xF; - target.pad[i] = (table[index[i]] >> 20) & 0xF; - target.pga[i] = (table[index[i]] >> 24) & 0xF; - target.txgm[i] = (table[index[i]] >> 28) & 0xF; - } else { - target.ipa[i] = (table[index[i]] >> 16) & 0x3; - target.pad[i] = (table[index[i]] >> 18) & 0x3; - target.pga[i] = (table[index[i]] >> 20) & 0x7; - target.txgm[i] = (table[index[i]] >> 23) & 0x7; - } - } - } - - return target; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhyCleanup */ -static void b43_nphy_tx_cal_phy_cleanup(struct b43_wldev *dev) -{ - u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; - - if (dev->phy.rev >= 3) { - b43_phy_write(dev, B43_NPHY_AFECTL_C1, regs[0]); - b43_phy_write(dev, B43_NPHY_AFECTL_C2, regs[1]); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, regs[2]); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[3]); - b43_phy_write(dev, B43_NPHY_BBCFG, regs[4]); - b43_ntab_write(dev, B43_NTAB16(8, 3), regs[5]); - b43_ntab_write(dev, B43_NTAB16(8, 19), regs[6]); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[7]); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[8]); - b43_phy_write(dev, B43_NPHY_PAPD_EN0, regs[9]); - b43_phy_write(dev, B43_NPHY_PAPD_EN1, regs[10]); - b43_nphy_reset_cca(dev); - } else { - b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, regs[0]); - b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, regs[1]); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[2]); - b43_ntab_write(dev, B43_NTAB16(8, 2), regs[3]); - b43_ntab_write(dev, B43_NTAB16(8, 18), regs[4]); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[5]); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[6]); - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhySetup */ -static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; - u16 tmp; - - regs[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); - regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); - if (dev->phy.rev >= 3) { - b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0xF0FF, 0x0A00); - b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0xF0FF, 0x0A00); - - tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); - regs[2] = tmp; - b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, tmp | 0x0600); - - tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); - regs[3] = tmp; - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp | 0x0600); - - regs[4] = b43_phy_read(dev, B43_NPHY_BBCFG); - b43_phy_mask(dev, B43_NPHY_BBCFG, - ~B43_NPHY_BBCFG_RSTRX & 0xFFFF); - - tmp = b43_ntab_read(dev, B43_NTAB16(8, 3)); - regs[5] = tmp; - b43_ntab_write(dev, B43_NTAB16(8, 3), 0); - - tmp = b43_ntab_read(dev, B43_NTAB16(8, 19)); - regs[6] = tmp; - b43_ntab_write(dev, B43_NTAB16(8, 19), 0); - regs[7] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); - regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); - - if (!nphy->use_int_tx_iq_lo_cal) - b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA, - 1, 3); - else - b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA, - 0, 3); - b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 2, 1); - b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 8, 2); - - regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0); - regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1); - b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001); - b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001); - - tmp = b43_nphy_read_lpf_ctl(dev, 0); - if (phy->rev >= 19) - b43_nphy_rf_ctl_override_rev19(dev, 0x80, tmp, 0, false, - 1); - else if (phy->rev >= 7) - b43_nphy_rf_ctl_override_rev7(dev, 0x80, tmp, 0, false, - 1); - - if (nphy->use_int_tx_iq_lo_cal && true /* FIXME */) { - if (phy->rev >= 19) { - b43_nphy_rf_ctl_override_rev19(dev, 0x8, 0, 0x3, - false, 0); - } else if (phy->rev >= 8) { - b43_nphy_rf_ctl_override_rev7(dev, 0x8, 0, 0x3, - false, 0); - } else if (phy->rev == 7) { - b43_radio_maskset(dev, R2057_OVR_REG0, 1 << 4, 1 << 4); - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - b43_radio_maskset(dev, R2057_PAD2G_TUNE_PUS_CORE0, ~1, 0); - b43_radio_maskset(dev, R2057_PAD2G_TUNE_PUS_CORE1, ~1, 0); - } else { - b43_radio_maskset(dev, R2057_IPA5G_CASCOFFV_PU_CORE0, ~1, 0); - b43_radio_maskset(dev, R2057_IPA5G_CASCOFFV_PU_CORE1, ~1, 0); - } - } - } - } else { - b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, 0xA000); - b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, 0xA000); - tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); - regs[2] = tmp; - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp | 0x3000); - tmp = b43_ntab_read(dev, B43_NTAB16(8, 2)); - regs[3] = tmp; - tmp |= 0x2000; - b43_ntab_write(dev, B43_NTAB16(8, 2), tmp); - tmp = b43_ntab_read(dev, B43_NTAB16(8, 18)); - regs[4] = tmp; - tmp |= 0x2000; - b43_ntab_write(dev, B43_NTAB16(8, 18), tmp); - regs[5] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); - regs[6] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - tmp = 0x0180; - else - tmp = 0x0120; - b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, tmp); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, tmp); - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SaveCal */ -static void b43_nphy_save_cal(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - - struct b43_phy_n_iq_comp *rxcal_coeffs = NULL; - u16 *txcal_radio_regs = NULL; - struct b43_chanspec *iqcal_chanspec; - u16 *table = NULL; - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 1); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G; - txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G; - iqcal_chanspec = &nphy->iqcal_chanspec_2G; - table = nphy->cal_cache.txcal_coeffs_2G; - } else { - rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G; - txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G; - iqcal_chanspec = &nphy->iqcal_chanspec_5G; - table = nphy->cal_cache.txcal_coeffs_5G; - } - - b43_nphy_rx_iq_coeffs(dev, false, rxcal_coeffs); - /* TODO use some definitions */ - if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 7) { - txcal_radio_regs[0] = b43_radio_read(dev, - R2057_TX0_LOFT_FINE_I); - txcal_radio_regs[1] = b43_radio_read(dev, - R2057_TX0_LOFT_FINE_Q); - txcal_radio_regs[4] = b43_radio_read(dev, - R2057_TX0_LOFT_COARSE_I); - txcal_radio_regs[5] = b43_radio_read(dev, - R2057_TX0_LOFT_COARSE_Q); - txcal_radio_regs[2] = b43_radio_read(dev, - R2057_TX1_LOFT_FINE_I); - txcal_radio_regs[3] = b43_radio_read(dev, - R2057_TX1_LOFT_FINE_Q); - txcal_radio_regs[6] = b43_radio_read(dev, - R2057_TX1_LOFT_COARSE_I); - txcal_radio_regs[7] = b43_radio_read(dev, - R2057_TX1_LOFT_COARSE_Q); - } else if (phy->rev >= 3) { - txcal_radio_regs[0] = b43_radio_read(dev, 0x2021); - txcal_radio_regs[1] = b43_radio_read(dev, 0x2022); - txcal_radio_regs[2] = b43_radio_read(dev, 0x3021); - txcal_radio_regs[3] = b43_radio_read(dev, 0x3022); - txcal_radio_regs[4] = b43_radio_read(dev, 0x2023); - txcal_radio_regs[5] = b43_radio_read(dev, 0x2024); - txcal_radio_regs[6] = b43_radio_read(dev, 0x3023); - txcal_radio_regs[7] = b43_radio_read(dev, 0x3024); - } else { - txcal_radio_regs[0] = b43_radio_read(dev, 0x8B); - txcal_radio_regs[1] = b43_radio_read(dev, 0xBA); - txcal_radio_regs[2] = b43_radio_read(dev, 0x8D); - txcal_radio_regs[3] = b43_radio_read(dev, 0xBC); - } - iqcal_chanspec->center_freq = dev->phy.chandef->chan->center_freq; - iqcal_chanspec->channel_type = - cfg80211_get_chandef_type(dev->phy.chandef); - b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 8, table); - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, 0); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */ -static void b43_nphy_restore_cal(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - - u16 coef[4]; - u16 *loft = NULL; - u16 *table = NULL; - - int i; - u16 *txcal_radio_regs = NULL; - struct b43_phy_n_iq_comp *rxcal_coeffs = NULL; - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if (!nphy->iqcal_chanspec_2G.center_freq) - return; - table = nphy->cal_cache.txcal_coeffs_2G; - loft = &nphy->cal_cache.txcal_coeffs_2G[5]; - } else { - if (!nphy->iqcal_chanspec_5G.center_freq) - return; - table = nphy->cal_cache.txcal_coeffs_5G; - loft = &nphy->cal_cache.txcal_coeffs_5G[5]; - } - - b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4, table); - - for (i = 0; i < 4; i++) { - if (dev->phy.rev >= 3) - table[i] = coef[i]; - else - coef[i] = 0; - } - - b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, coef); - b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, loft); - b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, loft); - - if (dev->phy.rev < 2) - b43_nphy_tx_iq_workaround(dev); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G; - rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G; - } else { - txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G; - rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G; - } - - /* TODO use some definitions */ - if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 7) { - b43_radio_write(dev, R2057_TX0_LOFT_FINE_I, - txcal_radio_regs[0]); - b43_radio_write(dev, R2057_TX0_LOFT_FINE_Q, - txcal_radio_regs[1]); - b43_radio_write(dev, R2057_TX0_LOFT_COARSE_I, - txcal_radio_regs[4]); - b43_radio_write(dev, R2057_TX0_LOFT_COARSE_Q, - txcal_radio_regs[5]); - b43_radio_write(dev, R2057_TX1_LOFT_FINE_I, - txcal_radio_regs[2]); - b43_radio_write(dev, R2057_TX1_LOFT_FINE_Q, - txcal_radio_regs[3]); - b43_radio_write(dev, R2057_TX1_LOFT_COARSE_I, - txcal_radio_regs[6]); - b43_radio_write(dev, R2057_TX1_LOFT_COARSE_Q, - txcal_radio_regs[7]); - } else if (phy->rev >= 3) { - b43_radio_write(dev, 0x2021, txcal_radio_regs[0]); - b43_radio_write(dev, 0x2022, txcal_radio_regs[1]); - b43_radio_write(dev, 0x3021, txcal_radio_regs[2]); - b43_radio_write(dev, 0x3022, txcal_radio_regs[3]); - b43_radio_write(dev, 0x2023, txcal_radio_regs[4]); - b43_radio_write(dev, 0x2024, txcal_radio_regs[5]); - b43_radio_write(dev, 0x3023, txcal_radio_regs[6]); - b43_radio_write(dev, 0x3024, txcal_radio_regs[7]); - } else { - b43_radio_write(dev, 0x8B, txcal_radio_regs[0]); - b43_radio_write(dev, 0xBA, txcal_radio_regs[1]); - b43_radio_write(dev, 0x8D, txcal_radio_regs[2]); - b43_radio_write(dev, 0xBC, txcal_radio_regs[3]); - } - b43_nphy_rx_iq_coeffs(dev, true, rxcal_coeffs); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalTxIqlo */ -static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, - struct nphy_txgains target, - bool full, bool mphase) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - int i; - int error = 0; - int freq; - bool avoid = false; - u8 length; - u16 tmp, core, type, count, max, numb, last = 0, cmd; - const u16 *table; - bool phy6or5x; - - u16 buffer[11]; - u16 diq_start = 0; - u16 save[2]; - u16 gain[2]; - struct nphy_iqcal_params params[2]; - bool updated[2] = { }; - - b43_nphy_stay_in_carrier_search(dev, true); - - if (dev->phy.rev >= 4) { - avoid = nphy->hang_avoid; - nphy->hang_avoid = false; - } - - b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, save); - - for (i = 0; i < 2; i++) { - b43_nphy_iq_cal_gain_params(dev, i, target, ¶ms[i]); - gain[i] = params[i].cal_gain; - } - - b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, gain); - - b43_nphy_tx_cal_radio_setup(dev); - b43_nphy_tx_cal_phy_setup(dev); - - phy6or5x = dev->phy.rev >= 6 || - (dev->phy.rev == 5 && nphy->ipa2g_on && - b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ); - if (phy6or5x) { - if (b43_is_40mhz(dev)) { - b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18, - tbl_tx_iqlo_cal_loft_ladder_40); - b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18, - tbl_tx_iqlo_cal_iqimb_ladder_40); - } else { - b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18, - tbl_tx_iqlo_cal_loft_ladder_20); - b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18, - tbl_tx_iqlo_cal_iqimb_ladder_20); - } - } - - if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 7) { - b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AD9); - } else { - b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9); - } - - if (!b43_is_40mhz(dev)) - freq = 2500; - else - freq = 5000; - - if (nphy->mphase_cal_phase_id > 2) - b43_nphy_run_samples(dev, (b43_is_40mhz(dev) ? 40 : 20) * 8, - 0xFFFF, 0, true, false, false); - else - error = b43_nphy_tx_tone(dev, freq, 250, true, false, false); - - if (error == 0) { - if (nphy->mphase_cal_phase_id > 2) { - table = nphy->mphase_txcal_bestcoeffs; - length = 11; - if (dev->phy.rev < 3) - length -= 2; - } else { - if (!full && nphy->txiqlocal_coeffsvalid) { - table = nphy->txiqlocal_bestc; - length = 11; - if (dev->phy.rev < 3) - length -= 2; - } else { - full = true; - if (dev->phy.rev >= 3) { - table = tbl_tx_iqlo_cal_startcoefs_nphyrev3; - length = B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3; - } else { - table = tbl_tx_iqlo_cal_startcoefs; - length = B43_NTAB_TX_IQLO_CAL_STARTCOEFS; - } - } - } - - b43_ntab_write_bulk(dev, B43_NTAB16(15, 64), length, table); - - if (full) { - if (dev->phy.rev >= 3) - max = B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL_REV3; - else - max = B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL; - } else { - if (dev->phy.rev >= 3) - max = B43_NTAB_TX_IQLO_CAL_CMDS_RECAL_REV3; - else - max = B43_NTAB_TX_IQLO_CAL_CMDS_RECAL; - } - - if (mphase) { - count = nphy->mphase_txcal_cmdidx; - numb = min(max, - (u16)(count + nphy->mphase_txcal_numcmds)); - } else { - count = 0; - numb = max; - } - - for (; count < numb; count++) { - if (full) { - if (dev->phy.rev >= 3) - cmd = tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[count]; - else - cmd = tbl_tx_iqlo_cal_cmds_fullcal[count]; - } else { - if (dev->phy.rev >= 3) - cmd = tbl_tx_iqlo_cal_cmds_recal_nphyrev3[count]; - else - cmd = tbl_tx_iqlo_cal_cmds_recal[count]; - } - - core = (cmd & 0x3000) >> 12; - type = (cmd & 0x0F00) >> 8; - - if (phy6or5x && updated[core] == 0) { - b43_nphy_update_tx_cal_ladder(dev, core); - updated[core] = true; - } - - tmp = (params[core].ncorr[type] << 8) | 0x66; - b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDNNUM, tmp); - - if (type == 1 || type == 3 || type == 4) { - buffer[0] = b43_ntab_read(dev, - B43_NTAB16(15, 69 + core)); - diq_start = buffer[0]; - buffer[0] = 0; - b43_ntab_write(dev, B43_NTAB16(15, 69 + core), - 0); - } - - b43_phy_write(dev, B43_NPHY_IQLOCAL_CMD, cmd); - for (i = 0; i < 2000; i++) { - tmp = b43_phy_read(dev, B43_NPHY_IQLOCAL_CMD); - if (tmp & 0xC000) - break; - udelay(10); - } - - b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, - buffer); - b43_ntab_write_bulk(dev, B43_NTAB16(15, 64), length, - buffer); - - if (type == 1 || type == 3 || type == 4) - buffer[0] = diq_start; - } - - if (mphase) - nphy->mphase_txcal_cmdidx = (numb >= max) ? 0 : numb; - - last = (dev->phy.rev < 3) ? 6 : 7; - - if (!mphase || nphy->mphase_cal_phase_id == last) { - b43_ntab_write_bulk(dev, B43_NTAB16(15, 96), 4, buffer); - b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 4, buffer); - if (dev->phy.rev < 3) { - buffer[0] = 0; - buffer[1] = 0; - buffer[2] = 0; - buffer[3] = 0; - } - b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, - buffer); - b43_ntab_read_bulk(dev, B43_NTAB16(15, 101), 2, - buffer); - b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, - buffer); - b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, - buffer); - length = 11; - if (dev->phy.rev < 3) - length -= 2; - b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, - nphy->txiqlocal_bestc); - nphy->txiqlocal_coeffsvalid = true; - nphy->txiqlocal_chanspec.center_freq = - phy->chandef->chan->center_freq; - nphy->txiqlocal_chanspec.channel_type = - cfg80211_get_chandef_type(phy->chandef); - } else { - length = 11; - if (dev->phy.rev < 3) - length -= 2; - b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, - nphy->mphase_txcal_bestcoeffs); - } - - b43_nphy_stop_playback(dev); - b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0); - } - - b43_nphy_tx_cal_phy_cleanup(dev); - b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, save); - - if (dev->phy.rev < 2 && (!mphase || nphy->mphase_cal_phase_id == last)) - b43_nphy_tx_iq_workaround(dev); - - if (dev->phy.rev >= 4) - nphy->hang_avoid = avoid; - - b43_nphy_stay_in_carrier_search(dev, false); - - return error; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ReapplyTxCalCoeffs */ -static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy = dev->phy.n; - u8 i; - u16 buffer[7]; - bool equal = true; - - if (!nphy->txiqlocal_coeffsvalid || - nphy->txiqlocal_chanspec.center_freq != dev->phy.chandef->chan->center_freq || - nphy->txiqlocal_chanspec.channel_type != cfg80211_get_chandef_type(dev->phy.chandef)) - return; - - b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer); - for (i = 0; i < 4; i++) { - if (buffer[i] != nphy->txiqlocal_bestc[i]) { - equal = false; - break; - } - } - - if (!equal) { - b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4, - nphy->txiqlocal_bestc); - for (i = 0; i < 4; i++) - buffer[i] = 0; - b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, - buffer); - b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, - &nphy->txiqlocal_bestc[5]); - b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, - &nphy->txiqlocal_bestc[5]); - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIqRev2 */ -static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev, - struct nphy_txgains target, u8 type, bool debug) -{ - struct b43_phy_n *nphy = dev->phy.n; - int i, j, index; - u8 rfctl[2]; - u8 afectl_core; - u16 tmp[6]; - u16 uninitialized_var(cur_hpf1), uninitialized_var(cur_hpf2), cur_lna; - u32 real, imag; - enum ieee80211_band band; - - u8 use; - u16 cur_hpf; - u16 lna[3] = { 3, 3, 1 }; - u16 hpf1[3] = { 7, 2, 0 }; - u16 hpf2[3] = { 2, 0, 0 }; - u32 power[3] = { }; - u16 gain_save[2]; - u16 cal_gain[2]; - struct nphy_iqcal_params cal_params[2]; - struct nphy_iq_est est; - int ret = 0; - bool playtone = true; - int desired = 13; - - b43_nphy_stay_in_carrier_search(dev, 1); - - if (dev->phy.rev < 2) - b43_nphy_reapply_tx_cal_coeffs(dev); - b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save); - for (i = 0; i < 2; i++) { - b43_nphy_iq_cal_gain_params(dev, i, target, &cal_params[i]); - cal_gain[i] = cal_params[i].cal_gain; - } - b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, cal_gain); - - for (i = 0; i < 2; i++) { - if (i == 0) { - rfctl[0] = B43_NPHY_RFCTL_INTC1; - rfctl[1] = B43_NPHY_RFCTL_INTC2; - afectl_core = B43_NPHY_AFECTL_C1; - } else { - rfctl[0] = B43_NPHY_RFCTL_INTC2; - rfctl[1] = B43_NPHY_RFCTL_INTC1; - afectl_core = B43_NPHY_AFECTL_C2; - } - - tmp[1] = b43_phy_read(dev, B43_NPHY_RFSEQCA); - tmp[2] = b43_phy_read(dev, afectl_core); - tmp[3] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); - tmp[4] = b43_phy_read(dev, rfctl[0]); - tmp[5] = b43_phy_read(dev, rfctl[1]); - - b43_phy_maskset(dev, B43_NPHY_RFSEQCA, - ~B43_NPHY_RFSEQCA_RXDIS & 0xFFFF, - ((1 - i) << B43_NPHY_RFSEQCA_RXDIS_SHIFT)); - b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXEN, - (1 - i)); - b43_phy_set(dev, afectl_core, 0x0006); - b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0006); - - band = b43_current_band(dev->wl); - - if (nphy->rxcalparams & 0xFF000000) { - if (band == IEEE80211_BAND_5GHZ) - b43_phy_write(dev, rfctl[0], 0x140); - else - b43_phy_write(dev, rfctl[0], 0x110); - } else { - if (band == IEEE80211_BAND_5GHZ) - b43_phy_write(dev, rfctl[0], 0x180); - else - b43_phy_write(dev, rfctl[0], 0x120); - } - - if (band == IEEE80211_BAND_5GHZ) - b43_phy_write(dev, rfctl[1], 0x148); - else - b43_phy_write(dev, rfctl[1], 0x114); - - if (nphy->rxcalparams & 0x10000) { - b43_radio_maskset(dev, B2055_C1_GENSPARE2, 0xFC, - (i + 1)); - b43_radio_maskset(dev, B2055_C2_GENSPARE2, 0xFC, - (2 - i)); - } - - for (j = 0; j < 4; j++) { - if (j < 3) { - cur_lna = lna[j]; - cur_hpf1 = hpf1[j]; - cur_hpf2 = hpf2[j]; - } else { - if (power[1] > 10000) { - use = 1; - cur_hpf = cur_hpf1; - index = 2; - } else { - if (power[0] > 10000) { - use = 1; - cur_hpf = cur_hpf1; - index = 1; - } else { - index = 0; - use = 2; - cur_hpf = cur_hpf2; - } - } - cur_lna = lna[index]; - cur_hpf1 = hpf1[index]; - cur_hpf2 = hpf2[index]; - cur_hpf += desired - hweight32(power[index]); - cur_hpf = clamp_val(cur_hpf, 0, 10); - if (use == 1) - cur_hpf1 = cur_hpf; - else - cur_hpf2 = cur_hpf; - } - - tmp[0] = ((cur_hpf2 << 8) | (cur_hpf1 << 4) | - (cur_lna << 2)); - b43_nphy_rf_ctl_override(dev, 0x400, tmp[0], 3, - false); - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); - b43_nphy_stop_playback(dev); - - if (playtone) { - ret = b43_nphy_tx_tone(dev, 4000, - (nphy->rxcalparams & 0xFFFF), - false, false, true); - playtone = false; - } else { - b43_nphy_run_samples(dev, 160, 0xFFFF, 0, false, - false, true); - } - - if (ret == 0) { - if (j < 3) { - b43_nphy_rx_iq_est(dev, &est, 1024, 32, - false); - if (i == 0) { - real = est.i0_pwr; - imag = est.q0_pwr; - } else { - real = est.i1_pwr; - imag = est.q1_pwr; - } - power[i] = ((real + imag) / 1024) + 1; - } else { - b43_nphy_calc_rx_iq_comp(dev, 1 << i); - } - b43_nphy_stop_playback(dev); - } - - if (ret != 0) - break; - } - - b43_radio_mask(dev, B2055_C1_GENSPARE2, 0xFC); - b43_radio_mask(dev, B2055_C2_GENSPARE2, 0xFC); - b43_phy_write(dev, rfctl[1], tmp[5]); - b43_phy_write(dev, rfctl[0], tmp[4]); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp[3]); - b43_phy_write(dev, afectl_core, tmp[2]); - b43_phy_write(dev, B43_NPHY_RFSEQCA, tmp[1]); - - if (ret != 0) - break; - } - - b43_nphy_rf_ctl_override(dev, 0x400, 0, 3, true); - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); - b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save); - - b43_nphy_stay_in_carrier_search(dev, 0); - - return ret; -} - -static int b43_nphy_rev3_cal_rx_iq(struct b43_wldev *dev, - struct nphy_txgains target, u8 type, bool debug) -{ - return -1; -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIq */ -static int b43_nphy_cal_rx_iq(struct b43_wldev *dev, - struct nphy_txgains target, u8 type, bool debug) -{ - if (dev->phy.rev >= 7) - type = 0; - - if (dev->phy.rev >= 3) - return b43_nphy_rev3_cal_rx_iq(dev, target, type, debug); - else - return b43_nphy_rev2_cal_rx_iq(dev, target, type, debug); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCoreSetState */ -static void b43_nphy_set_rx_core_state(struct b43_wldev *dev, u8 mask) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = phy->n; - /* u16 buf[16]; it's rev3+ */ - - nphy->phyrxchain = mask; - - if (0 /* FIXME clk */) - return; - - b43_mac_suspend(dev); - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, true); - - b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_RXEN, - (mask & 0x3) << B43_NPHY_RFSEQCA_RXEN_SHIFT); - - if ((mask & 0x3) != 0x3) { - b43_phy_write(dev, B43_NPHY_HPANT_SWTHRES, 1); - if (dev->phy.rev >= 3) { - /* TODO */ - } - } else { - b43_phy_write(dev, B43_NPHY_HPANT_SWTHRES, 0x1E); - if (dev->phy.rev >= 3) { - /* TODO */ - } - } - - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); - - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, false); - - b43_mac_enable(dev); -} - -static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev, - bool ignore_tssi) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - struct ieee80211_channel *channel = dev->wl->hw->conf.chandef.chan; - struct b43_ppr *ppr = &nphy->tx_pwr_max_ppr; - u8 max; /* qdBm */ - bool tx_pwr_state; - - if (nphy->tx_pwr_last_recalc_freq == channel->center_freq && - nphy->tx_pwr_last_recalc_limit == phy->desired_txpower) - return B43_TXPWR_RES_DONE; - - /* Make sure we have a clean PPR */ - b43_ppr_clear(dev, ppr); - - /* HW limitations */ - b43_ppr_load_max_from_sprom(dev, ppr, B43_BAND_2G); - - /* Regulatory & user settings */ - max = INT_TO_Q52(phy->chandef->chan->max_power); - if (phy->desired_txpower) - max = min_t(u8, max, INT_TO_Q52(phy->desired_txpower)); - b43_ppr_apply_max(dev, ppr, max); - if (b43_debug(dev, B43_DBG_XMITPOWER)) - b43dbg(dev->wl, "Calculated TX power: " Q52_FMT "\n", - Q52_ARG(b43_ppr_get_max(dev, ppr))); - - /* TODO: Enable this once we get gains working */ -#if 0 - /* Some extra gains */ - hw_gain = 6; /* N-PHY specific */ - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - hw_gain += sprom->antenna_gain.a0; - else - hw_gain += sprom->antenna_gain.a1; - b43_ppr_add(dev, ppr, -hw_gain); -#endif - - /* Make sure we didn't go too low */ - b43_ppr_apply_min(dev, ppr, INT_TO_Q52(8)); - - /* Apply */ - tx_pwr_state = nphy->txpwrctrl; - b43_mac_suspend(dev); - b43_nphy_tx_power_ctl_setup(dev); - if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) { - b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_PHY_LOCK); - b43_read32(dev, B43_MMIO_MACCTL); - udelay(1); - } - b43_nphy_tx_power_ctrl(dev, nphy->txpwrctrl); - if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) - b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PHY_LOCK, 0); - b43_mac_enable(dev); - - nphy->tx_pwr_last_recalc_freq = channel->center_freq; - nphy->tx_pwr_last_recalc_limit = phy->desired_txpower; - - return B43_TXPWR_RES_DONE; -} - -/************************************************** - * N-PHY init - **************************************************/ - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/MIMOConfig */ -static void b43_nphy_update_mimo_config(struct b43_wldev *dev, s32 preamble) -{ - u16 mimocfg = b43_phy_read(dev, B43_NPHY_MIMOCFG); - - mimocfg |= B43_NPHY_MIMOCFG_AUTO; - if (preamble == 1) - mimocfg |= B43_NPHY_MIMOCFG_GFMIX; - else - mimocfg &= ~B43_NPHY_MIMOCFG_GFMIX; - - b43_phy_write(dev, B43_NPHY_MIMOCFG, mimocfg); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BPHYInit */ -static void b43_nphy_bphy_init(struct b43_wldev *dev) -{ - unsigned int i; - u16 val; - - val = 0x1E1F; - for (i = 0; i < 16; i++) { - b43_phy_write(dev, B43_PHY_N_BMODE(0x88 + i), val); - val -= 0x202; - } - val = 0x3E3F; - for (i = 0; i < 16; i++) { - b43_phy_write(dev, B43_PHY_N_BMODE(0x98 + i), val); - val -= 0x202; - } - b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SuperSwitchInit */ -static void b43_nphy_superswitch_init(struct b43_wldev *dev, bool init) -{ - if (dev->phy.rev >= 7) - return; - - if (dev->phy.rev >= 3) { - if (!init) - return; - if (0 /* FIXME */) { - b43_ntab_write(dev, B43_NTAB16(9, 2), 0x211); - b43_ntab_write(dev, B43_NTAB16(9, 3), 0x222); - b43_ntab_write(dev, B43_NTAB16(9, 8), 0x144); - b43_ntab_write(dev, B43_NTAB16(9, 12), 0x188); - } - } else { - b43_phy_write(dev, B43_NPHY_GPIO_LOOEN, 0); - b43_phy_write(dev, B43_NPHY_GPIO_HIOEN, 0); - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - bcma_chipco_gpio_control(&dev->dev->bdev->bus->drv_cc, - 0xFC00, 0xFC00); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - ssb_chipco_gpio_control(&dev->dev->sdev->bus->chipco, - 0xFC00, 0xFC00); - break; -#endif - } - - b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0); - b43_maskset16(dev, B43_MMIO_GPIO_MASK, ~0, 0xFC00); - b43_maskset16(dev, B43_MMIO_GPIO_CONTROL, (~0xFC00 & 0xFFFF), - 0); - - if (init) { - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8); - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301); - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8); - b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301); - } - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/Init/N */ -static int b43_phy_initn(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = phy->n; - u8 tx_pwr_state; - struct nphy_txgains target; - u16 tmp; - enum ieee80211_band tmp2; - bool do_rssi_cal; - - u16 clip[2]; - bool do_cal = false; - - if ((dev->phy.rev >= 3) && - (sprom->boardflags_lo & B43_BFL_EXTLNA) && - (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) { - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - bcma_cc_set32(&dev->dev->bdev->bus->drv_cc, - BCMA_CC_CHIPCTL, 0x40); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - chipco_set32(&dev->dev->sdev->bus->chipco, - SSB_CHIPCO_CHIPCTL, 0x40); - break; -#endif - } - } - nphy->use_int_tx_iq_lo_cal = b43_nphy_ipa(dev) || - phy->rev >= 7 || - (phy->rev >= 5 && - sprom->boardflags2_hi & B43_BFH2_INTERNDET_TXIQCAL); - nphy->deaf_count = 0; - b43_nphy_tables_init(dev); - nphy->crsminpwr_adjusted = false; - nphy->noisevars_adjusted = false; - - /* Clear all overrides */ - if (dev->phy.rev >= 3) { - b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, 0); - b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); - if (phy->rev >= 7) { - b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0); - b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER4, 0); - b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER5, 0); - b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER6, 0); - } - if (phy->rev >= 19) { - /* TODO */ - } - - b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, 0); - b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, 0); - } else { - b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); - } - b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, 0); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, 0); - if (dev->phy.rev < 6) { - b43_phy_write(dev, B43_NPHY_RFCTL_INTC3, 0); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC4, 0); - } - b43_phy_mask(dev, B43_NPHY_RFSEQMODE, - ~(B43_NPHY_RFSEQMODE_CAOVER | - B43_NPHY_RFSEQMODE_TROVER)); - if (dev->phy.rev >= 3) - b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, 0); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, 0); - - if (dev->phy.rev <= 2) { - tmp = (dev->phy.rev == 2) ? 0x3B : 0x40; - b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, - ~B43_NPHY_BPHY_CTL3_SCALE, - tmp << B43_NPHY_BPHY_CTL3_SCALE_SHIFT); - } - b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_20M, 0x20); - b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_40M, 0x20); - - if (sprom->boardflags2_lo & B43_BFL2_SKWRKFEM_BRD || - (dev->dev->board_vendor == PCI_VENDOR_ID_APPLE && - dev->dev->board_type == BCMA_BOARD_TYPE_BCM943224M93)) - b43_phy_write(dev, B43_NPHY_TXREALFD, 0xA0); - else - b43_phy_write(dev, B43_NPHY_TXREALFD, 0xB8); - b43_phy_write(dev, B43_NPHY_MIMO_CRSTXEXT, 0xC8); - b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x50); - b43_phy_write(dev, B43_NPHY_TXRIFS_FRDEL, 0x30); - - if (phy->rev < 8) - b43_nphy_update_mimo_config(dev, nphy->preamble_override); - - b43_nphy_update_txrx_chain(dev); - - if (phy->rev < 2) { - b43_phy_write(dev, B43_NPHY_DUP40_GFBL, 0xAA8); - b43_phy_write(dev, B43_NPHY_DUP40_BL, 0x9A4); - } - - tmp2 = b43_current_band(dev->wl); - if (b43_nphy_ipa(dev)) { - b43_phy_set(dev, B43_NPHY_PAPD_EN0, 0x1); - b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ0, 0x007F, - nphy->papd_epsilon_offset[0] << 7); - b43_phy_set(dev, B43_NPHY_PAPD_EN1, 0x1); - b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ1, 0x007F, - nphy->papd_epsilon_offset[1] << 7); - b43_nphy_int_pa_set_tx_dig_filters(dev); - } else if (phy->rev >= 5) { - b43_nphy_ext_pa_set_tx_dig_filters(dev); - } - - b43_nphy_workarounds(dev); - - /* Reset CCA, in init code it differs a little from standard way */ - b43_phy_force_clock(dev, 1); - tmp = b43_phy_read(dev, B43_NPHY_BBCFG); - b43_phy_write(dev, B43_NPHY_BBCFG, tmp | B43_NPHY_BBCFG_RSTCCA); - b43_phy_write(dev, B43_NPHY_BBCFG, tmp & ~B43_NPHY_BBCFG_RSTCCA); - b43_phy_force_clock(dev, 0); - - b43_mac_phy_clock_set(dev, true); - - if (phy->rev < 7) { - b43_nphy_pa_override(dev, false); - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); - b43_nphy_pa_override(dev, true); - } - - b43_nphy_classifier(dev, 0, 0); - b43_nphy_read_clip_detection(dev, clip); - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - b43_nphy_bphy_init(dev); - - tx_pwr_state = nphy->txpwrctrl; - b43_nphy_tx_power_ctrl(dev, false); - b43_nphy_tx_power_fix(dev); - b43_nphy_tx_power_ctl_idle_tssi(dev); - b43_nphy_tx_power_ctl_setup(dev); - b43_nphy_tx_gain_table_upload(dev); - - if (nphy->phyrxchain != 3) - b43_nphy_set_rx_core_state(dev, nphy->phyrxchain); - if (nphy->mphase_cal_phase_id > 0) - ;/* TODO PHY Periodic Calibration Multi-Phase Restart */ - - do_rssi_cal = false; - if (phy->rev >= 3) { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - do_rssi_cal = !nphy->rssical_chanspec_2G.center_freq; - else - do_rssi_cal = !nphy->rssical_chanspec_5G.center_freq; - - if (do_rssi_cal) - b43_nphy_rssi_cal(dev); - else - b43_nphy_restore_rssi_cal(dev); - } else { - b43_nphy_rssi_cal(dev); - } - - if (!((nphy->measure_hold & 0x6) != 0)) { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - do_cal = !nphy->iqcal_chanspec_2G.center_freq; - else - do_cal = !nphy->iqcal_chanspec_5G.center_freq; - - if (nphy->mute) - do_cal = false; - - if (do_cal) { - target = b43_nphy_get_tx_gains(dev); - - if (nphy->antsel_type == 2) - b43_nphy_superswitch_init(dev, true); - if (nphy->perical != 2) { - b43_nphy_rssi_cal(dev); - if (phy->rev >= 3) { - nphy->cal_orig_pwr_idx[0] = - nphy->txpwrindex[0].index_internal; - nphy->cal_orig_pwr_idx[1] = - nphy->txpwrindex[1].index_internal; - /* TODO N PHY Pre Calibrate TX Gain */ - target = b43_nphy_get_tx_gains(dev); - } - if (!b43_nphy_cal_tx_iq_lo(dev, target, true, false)) - if (b43_nphy_cal_rx_iq(dev, target, 2, 0) == 0) - b43_nphy_save_cal(dev); - } else if (nphy->mphase_cal_phase_id == 0) - ;/* N PHY Periodic Calibration with arg 3 */ - } else { - b43_nphy_restore_cal(dev); - } - } - - b43_nphy_tx_pwr_ctrl_coef_setup(dev); - b43_nphy_tx_power_ctrl(dev, tx_pwr_state); - b43_phy_write(dev, B43_NPHY_TXMACIF_HOLDOFF, 0x0015); - b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320); - if (phy->rev >= 3 && phy->rev <= 6) - b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0032); - b43_nphy_tx_lpf_bw(dev); - if (phy->rev >= 3) - b43_nphy_spur_workaround(dev); - - return 0; -} - -/************************************************** - * Channel switching ops. - **************************************************/ - -static void b43_chantab_phy_upload(struct b43_wldev *dev, - const struct b43_phy_n_sfo_cfg *e) -{ - b43_phy_write(dev, B43_NPHY_BW1A, e->phy_bw1a); - b43_phy_write(dev, B43_NPHY_BW2, e->phy_bw2); - b43_phy_write(dev, B43_NPHY_BW3, e->phy_bw3); - b43_phy_write(dev, B43_NPHY_BW4, e->phy_bw4); - b43_phy_write(dev, B43_NPHY_BW5, e->phy_bw5); - b43_phy_write(dev, B43_NPHY_BW6, e->phy_bw6); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PmuSpurAvoid */ -static void b43_nphy_pmu_spur_avoid(struct b43_wldev *dev, bool avoid) -{ - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - bcma_pmu_spuravoid_pllupdate(&dev->dev->bdev->bus->drv_cc, - avoid); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - ssb_pmu_spuravoid_pllupdate(&dev->dev->sdev->bus->chipco, - avoid); - break; -#endif - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ChanspecSetup */ -static void b43_nphy_channel_setup(struct b43_wldev *dev, - const struct b43_phy_n_sfo_cfg *e, - struct ieee80211_channel *new_channel) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = dev->phy.n; - int ch = new_channel->hw_value; - u16 tmp16; - - if (new_channel->band == IEEE80211_BAND_5GHZ) { - /* Switch to 2 GHz for a moment to access B43_PHY_B_BBCFG */ - b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ); - - tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); - b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4); - /* Put BPHY in the reset */ - b43_phy_set(dev, B43_PHY_B_BBCFG, - B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX); - b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16); - b43_phy_set(dev, B43_NPHY_BANDCTL, B43_NPHY_BANDCTL_5GHZ); - } else if (new_channel->band == IEEE80211_BAND_2GHZ) { - b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ); - tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); - b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4); - /* Take BPHY out of the reset */ - b43_phy_mask(dev, B43_PHY_B_BBCFG, - (u16)~(B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX)); - b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16); - } - - b43_chantab_phy_upload(dev, e); - - if (new_channel->hw_value == 14) { - b43_nphy_classifier(dev, 2, 0); - b43_phy_set(dev, B43_PHY_B_TEST, 0x0800); - } else { - b43_nphy_classifier(dev, 2, 2); - if (new_channel->band == IEEE80211_BAND_2GHZ) - b43_phy_mask(dev, B43_PHY_B_TEST, ~0x840); - } - - if (!nphy->txpwrctrl) - b43_nphy_tx_power_fix(dev); - - if (dev->phy.rev < 3) - b43_nphy_adjust_lna_gain_table(dev); - - b43_nphy_tx_lpf_bw(dev); - - if (dev->phy.rev >= 3 && - dev->phy.n->spur_avoid != B43_SPUR_AVOID_DISABLE) { - u8 spuravoid = 0; - - if (dev->phy.n->spur_avoid == B43_SPUR_AVOID_FORCE) { - spuravoid = 1; - } else if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 18) { - /* TODO */ - } else if (phy->rev >= 17) { - /* TODO: Off for channels 1-11, but check 12-14! */ - } else if (phy->rev >= 16) { - /* TODO: Off for 2 GHz, but check 5 GHz! */ - } else if (phy->rev >= 7) { - if (!b43_is_40mhz(dev)) { /* 20MHz */ - if (ch == 13 || ch == 14 || ch == 153) - spuravoid = 1; - } else { /* 40 MHz */ - if (ch == 54) - spuravoid = 1; - } - } else { - if (!b43_is_40mhz(dev)) { /* 20MHz */ - if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14) - spuravoid = 1; - } else { /* 40MHz */ - if (nphy->aband_spurwar_en && - (ch == 38 || ch == 102 || ch == 118)) - spuravoid = dev->dev->chip_id == 0x4716; - } - } - - b43_nphy_pmu_spur_avoid(dev, spuravoid); - - b43_mac_switch_freq(dev, spuravoid); - - if (dev->phy.rev == 3 || dev->phy.rev == 4) - b43_wireless_core_phy_pll_reset(dev); - - if (spuravoid) - b43_phy_set(dev, B43_NPHY_BBCFG, B43_NPHY_BBCFG_RSTRX); - else - b43_phy_mask(dev, B43_NPHY_BBCFG, - ~B43_NPHY_BBCFG_RSTRX & 0xFFFF); - - b43_nphy_reset_cca(dev); - - /* wl sets useless phy_isspuravoid here */ - } - - b43_phy_write(dev, B43_NPHY_NDATAT_DUP40, 0x3830); - - if (phy->rev >= 3) - b43_nphy_spur_workaround(dev); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetChanspec */ -static int b43_nphy_set_channel(struct b43_wldev *dev, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type) -{ - struct b43_phy *phy = &dev->phy; - - const struct b43_nphy_channeltab_entry_rev2 *tabent_r2 = NULL; - const struct b43_nphy_channeltab_entry_rev3 *tabent_r3 = NULL; - const struct b43_nphy_chantabent_rev7 *tabent_r7 = NULL; - const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g = NULL; - - u8 tmp; - - if (phy->rev >= 19) { - return -ESRCH; - /* TODO */ - } else if (phy->rev >= 7) { - r2057_get_chantabent_rev7(dev, channel->center_freq, - &tabent_r7, &tabent_r7_2g); - if (!tabent_r7 && !tabent_r7_2g) - return -ESRCH; - } else if (phy->rev >= 3) { - tabent_r3 = b43_nphy_get_chantabent_rev3(dev, - channel->center_freq); - if (!tabent_r3) - return -ESRCH; - } else { - tabent_r2 = b43_nphy_get_chantabent_rev2(dev, - channel->hw_value); - if (!tabent_r2) - return -ESRCH; - } - - /* Channel is set later in common code, but we need to set it on our - own to let this function's subcalls work properly. */ - phy->channel = channel->hw_value; - -#if 0 - if (b43_channel_type_is_40mhz(phy->channel_type) != - b43_channel_type_is_40mhz(channel_type)) - ; /* TODO: BMAC BW Set (channel_type) */ -#endif - - if (channel_type == NL80211_CHAN_HT40PLUS) { - b43_phy_set(dev, B43_NPHY_RXCTL, B43_NPHY_RXCTL_BSELU20); - if (phy->rev >= 7) - b43_phy_set(dev, 0x310, 0x8000); - } else if (channel_type == NL80211_CHAN_HT40MINUS) { - b43_phy_mask(dev, B43_NPHY_RXCTL, ~B43_NPHY_RXCTL_BSELU20); - if (phy->rev >= 7) - b43_phy_mask(dev, 0x310, (u16)~0x8000); - } - - if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 7) { - const struct b43_phy_n_sfo_cfg *phy_regs = tabent_r7 ? - &(tabent_r7->phy_regs) : &(tabent_r7_2g->phy_regs); - - if (phy->radio_rev <= 4 || phy->radio_rev == 6) { - tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 2 : 0; - b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE0, ~2, tmp); - b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE1, ~2, tmp); - } - - b43_radio_2057_setup(dev, tabent_r7, tabent_r7_2g); - b43_nphy_channel_setup(dev, phy_regs, channel); - } else if (phy->rev >= 3) { - tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 4 : 0; - b43_radio_maskset(dev, 0x08, 0xFFFB, tmp); - b43_radio_2056_setup(dev, tabent_r3); - b43_nphy_channel_setup(dev, &(tabent_r3->phy_regs), channel); - } else { - tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 0x0020 : 0x0050; - b43_radio_maskset(dev, B2055_MASTER1, 0xFF8F, tmp); - b43_radio_2055_setup(dev, tabent_r2); - b43_nphy_channel_setup(dev, &(tabent_r2->phy_regs), channel); - } - - return 0; -} - -/************************************************** - * Basic PHY ops. - **************************************************/ - -static int b43_nphy_op_allocate(struct b43_wldev *dev) -{ - struct b43_phy_n *nphy; - - nphy = kzalloc(sizeof(*nphy), GFP_KERNEL); - if (!nphy) - return -ENOMEM; - - dev->phy.n = nphy; - - return 0; -} - -static void b43_nphy_op_prepare_structs(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = phy->n; - struct ssb_sprom *sprom = dev->dev->bus_sprom; - - memset(nphy, 0, sizeof(*nphy)); - - nphy->hang_avoid = (phy->rev == 3 || phy->rev == 4); - nphy->spur_avoid = (phy->rev >= 3) ? - B43_SPUR_AVOID_AUTO : B43_SPUR_AVOID_DISABLE; - nphy->gain_boost = true; /* this way we follow wl, assume it is true */ - nphy->txrx_chain = 2; /* sth different than 0 and 1 for now */ - nphy->phyrxchain = 3; /* to avoid b43_nphy_set_rx_core_state like wl */ - nphy->perical = 2; /* avoid additional rssi cal on init (like wl) */ - /* 128 can mean disabled-by-default state of TX pwr ctl. Max value is - * 0x7f == 127 and we check for 128 when restoring TX pwr ctl. */ - nphy->tx_pwr_idx[0] = 128; - nphy->tx_pwr_idx[1] = 128; - - /* Hardware TX power control and 5GHz power gain */ - nphy->txpwrctrl = false; - nphy->pwg_gain_5ghz = false; - if (dev->phy.rev >= 3 || - (dev->dev->board_vendor == PCI_VENDOR_ID_APPLE && - (dev->dev->core_rev == 11 || dev->dev->core_rev == 12))) { - nphy->txpwrctrl = true; - nphy->pwg_gain_5ghz = true; - } else if (sprom->revision >= 4) { - if (dev->phy.rev >= 2 && - (sprom->boardflags2_lo & B43_BFL2_TXPWRCTRL_EN)) { - nphy->txpwrctrl = true; -#ifdef CONFIG_B43_SSB - if (dev->dev->bus_type == B43_BUS_SSB && - dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI) { - struct pci_dev *pdev = - dev->dev->sdev->bus->host_pci; - if (pdev->device == 0x4328 || - pdev->device == 0x432a) - nphy->pwg_gain_5ghz = true; - } -#endif - } else if (sprom->boardflags2_lo & B43_BFL2_5G_PWRGAIN) { - nphy->pwg_gain_5ghz = true; - } - } - - if (dev->phy.rev >= 3) { - nphy->ipa2g_on = sprom->fem.ghz2.extpa_gain == 2; - nphy->ipa5g_on = sprom->fem.ghz5.extpa_gain == 2; - } -} - -static void b43_nphy_op_free(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_n *nphy = phy->n; - - kfree(nphy); - phy->n = NULL; -} - -static int b43_nphy_op_init(struct b43_wldev *dev) -{ - return b43_phy_initn(dev); -} - -static inline void check_phyreg(struct b43_wldev *dev, u16 offset) -{ -#if B43_DEBUG - if ((offset & B43_PHYROUTE) == B43_PHYROUTE_OFDM_GPHY) { - /* OFDM registers are onnly available on A/G-PHYs */ - b43err(dev->wl, "Invalid OFDM PHY access at " - "0x%04X on N-PHY\n", offset); - dump_stack(); - } - if ((offset & B43_PHYROUTE) == B43_PHYROUTE_EXT_GPHY) { - /* Ext-G registers are only available on G-PHYs */ - b43err(dev->wl, "Invalid EXT-G PHY access at " - "0x%04X on N-PHY\n", offset); - dump_stack(); - } -#endif /* B43_DEBUG */ -} - -static void b43_nphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, - u16 set) -{ - check_phyreg(dev, reg); - b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); - b43_maskset16(dev, B43_MMIO_PHY_DATA, mask, set); - dev->phy.writes_counter = 1; -} - -static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg) -{ - /* Register 1 is a 32-bit register. */ - B43_WARN_ON(dev->phy.rev < 7 && reg == 1); - - if (dev->phy.rev >= 7) - reg |= 0x200; /* Radio 0x2057 */ - else - reg |= 0x100; - - b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); - return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); -} - -static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) -{ - /* Register 1 is a 32-bit register. */ - B43_WARN_ON(dev->phy.rev < 7 && reg == 1); - - b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); - b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); -} - -/* http://bcm-v4.sipsolutions.net/802.11/Radio/Switch%20Radio */ -static void b43_nphy_op_software_rfkill(struct b43_wldev *dev, - bool blocked) -{ - struct b43_phy *phy = &dev->phy; - - if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED) - b43err(dev->wl, "MAC not suspended\n"); - - if (blocked) { - if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 8) { - b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, - ~B43_NPHY_RFCTL_CMD_CHIP0PU); - } else if (phy->rev >= 7) { - /* Nothing needed */ - } else if (phy->rev >= 3) { - b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, - ~B43_NPHY_RFCTL_CMD_CHIP0PU); - - b43_radio_mask(dev, 0x09, ~0x2); - - b43_radio_write(dev, 0x204D, 0); - b43_radio_write(dev, 0x2053, 0); - b43_radio_write(dev, 0x2058, 0); - b43_radio_write(dev, 0x205E, 0); - b43_radio_mask(dev, 0x2062, ~0xF0); - b43_radio_write(dev, 0x2064, 0); - - b43_radio_write(dev, 0x304D, 0); - b43_radio_write(dev, 0x3053, 0); - b43_radio_write(dev, 0x3058, 0); - b43_radio_write(dev, 0x305E, 0); - b43_radio_mask(dev, 0x3062, ~0xF0); - b43_radio_write(dev, 0x3064, 0); - } - } else { - if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 7) { - if (!dev->phy.radio_on) - b43_radio_2057_init(dev); - b43_switch_channel(dev, dev->phy.channel); - } else if (phy->rev >= 3) { - if (!dev->phy.radio_on) - b43_radio_init2056(dev); - b43_switch_channel(dev, dev->phy.channel); - } else { - b43_radio_init2055(dev); - } - } -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/Anacore */ -static void b43_nphy_op_switch_analog(struct b43_wldev *dev, bool on) -{ - struct b43_phy *phy = &dev->phy; - u16 override = on ? 0x0 : 0x7FFF; - u16 core = on ? 0xD : 0x00FD; - - if (phy->rev >= 19) { - /* TODO */ - } else if (phy->rev >= 3) { - if (on) { - b43_phy_write(dev, B43_NPHY_AFECTL_C1, core); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, override); - b43_phy_write(dev, B43_NPHY_AFECTL_C2, core); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override); - } else { - b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, override); - b43_phy_write(dev, B43_NPHY_AFECTL_C1, core); - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override); - b43_phy_write(dev, B43_NPHY_AFECTL_C2, core); - } - } else { - b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override); - } -} - -static int b43_nphy_op_switch_channel(struct b43_wldev *dev, - unsigned int new_channel) -{ - struct ieee80211_channel *channel = dev->wl->hw->conf.chandef.chan; - enum nl80211_channel_type channel_type = - cfg80211_get_chandef_type(&dev->wl->hw->conf.chandef); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if ((new_channel < 1) || (new_channel > 14)) - return -EINVAL; - } else { - if (new_channel > 200) - return -EINVAL; - } - - return b43_nphy_set_channel(dev, channel, channel_type); -} - -static unsigned int b43_nphy_op_get_default_chan(struct b43_wldev *dev) -{ - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - return 1; - return 36; -} - -const struct b43_phy_operations b43_phyops_n = { - .allocate = b43_nphy_op_allocate, - .free = b43_nphy_op_free, - .prepare_structs = b43_nphy_op_prepare_structs, - .init = b43_nphy_op_init, - .phy_maskset = b43_nphy_op_maskset, - .radio_read = b43_nphy_op_radio_read, - .radio_write = b43_nphy_op_radio_write, - .software_rfkill = b43_nphy_op_software_rfkill, - .switch_analog = b43_nphy_op_switch_analog, - .switch_channel = b43_nphy_op_switch_channel, - .get_default_chan = b43_nphy_op_get_default_chan, - .recalc_txpower = b43_nphy_op_recalc_txpower, -}; diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/b43/phy_n.h deleted file mode 100644 index a6da2c31a99c..000000000000 --- a/drivers/net/wireless/b43/phy_n.h +++ /dev/null @@ -1,1007 +0,0 @@ -#ifndef B43_NPHY_H_ -#define B43_NPHY_H_ - -#include "phy_common.h" -#include "ppr.h" - - -/* N-PHY registers. */ - -#define B43_NPHY_BBCFG B43_PHY_N(0x001) /* BB config */ -#define B43_NPHY_BBCFG_RSTCCA 0x4000 /* Reset CCA */ -#define B43_NPHY_BBCFG_RSTRX 0x8000 /* Reset RX */ -#define B43_NPHY_CHANNEL B43_PHY_N(0x005) /* Channel */ -#define B43_NPHY_TXERR B43_PHY_N(0x007) /* TX error */ -#define B43_NPHY_BANDCTL B43_PHY_N(0x009) /* Band control */ -#define B43_NPHY_BANDCTL_5GHZ 0x0001 /* Use the 5GHz band */ -#define B43_NPHY_4WI_ADDR B43_PHY_N(0x00B) /* Four-wire bus address */ -#define B43_NPHY_4WI_DATAHI B43_PHY_N(0x00C) /* Four-wire bus data high */ -#define B43_NPHY_4WI_DATALO B43_PHY_N(0x00D) /* Four-wire bus data low */ -#define B43_NPHY_BIST_STAT0 B43_PHY_N(0x00E) /* Built-in self test status 0 */ -#define B43_NPHY_BIST_STAT1 B43_PHY_N(0x00F) /* Built-in self test status 1 */ - -#define B43_NPHY_C1_DESPWR B43_PHY_N(0x018) /* Core 1 desired power */ -#define B43_NPHY_C1_CCK_DESPWR B43_PHY_N(0x019) /* Core 1 CCK desired power */ -#define B43_NPHY_C1_BCLIPBKOFF B43_PHY_N(0x01A) /* Core 1 barely clip backoff */ -#define B43_NPHY_C1_CCK_BCLIPBKOFF B43_PHY_N(0x01B) /* Core 1 CCK barely clip backoff */ -#define B43_NPHY_C1_CGAINI B43_PHY_N(0x01C) /* Core 1 compute gain info */ -#define B43_NPHY_C1_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ -#define B43_NPHY_C1_CGAINI_GAINBKOFF_SHIFT 0 -#define B43_NPHY_C1_CGAINI_CLIPGBKOFF 0x03E0 /* Clip gain backoff */ -#define B43_NPHY_C1_CGAINI_CLIPGBKOFF_SHIFT 5 -#define B43_NPHY_C1_CGAINI_GAINSTEP 0x1C00 /* Gain step */ -#define B43_NPHY_C1_CGAINI_GAINSTEP_SHIFT 10 -#define B43_NPHY_C1_CGAINI_CL2DETECT 0x2000 /* Clip 2 detect mask */ -#define B43_NPHY_C1_CCK_CGAINI B43_PHY_N(0x01D) /* Core 1 CCK compute gain info */ -#define B43_NPHY_C1_CCK_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ -#define B43_NPHY_C1_CCK_CGAINI_CLIPGBKOFF 0x01E0 /* CCK barely clip gain backoff */ -#define B43_NPHY_C1_MINMAX_GAIN B43_PHY_N(0x01E) /* Core 1 min/max gain */ -#define B43_NPHY_C1_MINGAIN 0x00FF /* Minimum gain */ -#define B43_NPHY_C1_MINGAIN_SHIFT 0 -#define B43_NPHY_C1_MAXGAIN 0xFF00 /* Maximum gain */ -#define B43_NPHY_C1_MAXGAIN_SHIFT 8 -#define B43_NPHY_C1_CCK_MINMAX_GAIN B43_PHY_N(0x01F) /* Core 1 CCK min/max gain */ -#define B43_NPHY_C1_CCK_MINGAIN 0x00FF /* Minimum gain */ -#define B43_NPHY_C1_CCK_MINGAIN_SHIFT 0 -#define B43_NPHY_C1_CCK_MAXGAIN 0xFF00 /* Maximum gain */ -#define B43_NPHY_C1_CCK_MAXGAIN_SHIFT 8 -#define B43_NPHY_C1_INITGAIN B43_PHY_N(0x020) /* Core 1 initial gain code */ -#define B43_NPHY_C1_INITGAIN_EXTLNA 0x0001 /* External LNA index */ -#define B43_NPHY_C1_INITGAIN_LNA 0x0006 /* LNA index */ -#define B43_NPHY_C1_INITGAIN_LNAIDX_SHIFT 1 -#define B43_NPHY_C1_INITGAIN_HPVGA1 0x0078 /* HPVGA1 index */ -#define B43_NPHY_C1_INITGAIN_HPVGA1_SHIFT 3 -#define B43_NPHY_C1_INITGAIN_HPVGA2 0x0F80 /* HPVGA2 index */ -#define B43_NPHY_C1_INITGAIN_HPVGA2_SHIFT 7 -#define B43_NPHY_C1_INITGAIN_TRRX 0x1000 /* TR RX index */ -#define B43_NPHY_C1_INITGAIN_TRTX 0x2000 /* TR TX index */ -#define B43_NPHY_REV3_C1_INITGAIN_A B43_PHY_N(0x020) -#define B43_NPHY_C1_CLIP1_HIGAIN B43_PHY_N(0x021) /* Core 1 clip1 high gain code */ -#define B43_NPHY_REV3_C1_INITGAIN_B B43_PHY_N(0x021) -#define B43_NPHY_C1_CLIP1_MEDGAIN B43_PHY_N(0x022) /* Core 1 clip1 medium gain code */ -#define B43_NPHY_REV3_C1_CLIP_HIGAIN_A B43_PHY_N(0x022) -#define B43_NPHY_C1_CLIP1_LOGAIN B43_PHY_N(0x023) /* Core 1 clip1 low gain code */ -#define B43_NPHY_REV3_C1_CLIP_HIGAIN_B B43_PHY_N(0x023) -#define B43_NPHY_C1_CLIP2_GAIN B43_PHY_N(0x024) /* Core 1 clip2 gain code */ -#define B43_NPHY_REV3_C1_CLIP_MEDGAIN_A B43_PHY_N(0x024) -#define B43_NPHY_C1_FILTERGAIN B43_PHY_N(0x025) /* Core 1 filter gain */ -#define B43_NPHY_C1_LPF_QHPF_BW B43_PHY_N(0x026) /* Core 1 LPF Q HP F bandwidth */ -#define B43_NPHY_C1_CLIPWBTHRES B43_PHY_N(0x027) /* Core 1 clip wideband threshold */ -#define B43_NPHY_C1_CLIPWBTHRES_CLIP2 0x003F /* Clip 2 */ -#define B43_NPHY_C1_CLIPWBTHRES_CLIP2_SHIFT 0 -#define B43_NPHY_C1_CLIPWBTHRES_CLIP1 0x0FC0 /* Clip 1 */ -#define B43_NPHY_C1_CLIPWBTHRES_CLIP1_SHIFT 6 -#define B43_NPHY_C1_W1THRES B43_PHY_N(0x028) /* Core 1 W1 threshold */ -#define B43_NPHY_C1_EDTHRES B43_PHY_N(0x029) /* Core 1 ED threshold */ -#define B43_NPHY_C1_SMSIGTHRES B43_PHY_N(0x02A) /* Core 1 small sig threshold */ -#define B43_NPHY_C1_NBCLIPTHRES B43_PHY_N(0x02B) /* Core 1 NB clip threshold */ -#define B43_NPHY_C1_CLIP1THRES B43_PHY_N(0x02C) /* Core 1 clip1 threshold */ -#define B43_NPHY_C1_CLIP2THRES B43_PHY_N(0x02D) /* Core 1 clip2 threshold */ - -#define B43_NPHY_C2_DESPWR B43_PHY_N(0x02E) /* Core 2 desired power */ -#define B43_NPHY_C2_CCK_DESPWR B43_PHY_N(0x02F) /* Core 2 CCK desired power */ -#define B43_NPHY_C2_BCLIPBKOFF B43_PHY_N(0x030) /* Core 2 barely clip backoff */ -#define B43_NPHY_C2_CCK_BCLIPBKOFF B43_PHY_N(0x031) /* Core 2 CCK barely clip backoff */ -#define B43_NPHY_C2_CGAINI B43_PHY_N(0x032) /* Core 2 compute gain info */ -#define B43_NPHY_C2_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ -#define B43_NPHY_C2_CGAINI_GAINBKOFF_SHIFT 0 -#define B43_NPHY_C2_CGAINI_CLIPGBKOFF 0x03E0 /* Clip gain backoff */ -#define B43_NPHY_C2_CGAINI_CLIPGBKOFF_SHIFT 5 -#define B43_NPHY_C2_CGAINI_GAINSTEP 0x1C00 /* Gain step */ -#define B43_NPHY_C2_CGAINI_GAINSTEP_SHIFT 10 -#define B43_NPHY_C2_CGAINI_CL2DETECT 0x2000 /* Clip 2 detect mask */ -#define B43_NPHY_C2_CCK_CGAINI B43_PHY_N(0x033) /* Core 2 CCK compute gain info */ -#define B43_NPHY_C2_CCK_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ -#define B43_NPHY_C2_CCK_CGAINI_CLIPGBKOFF 0x01E0 /* CCK barely clip gain backoff */ -#define B43_NPHY_C2_MINMAX_GAIN B43_PHY_N(0x034) /* Core 2 min/max gain */ -#define B43_NPHY_C2_MINGAIN 0x00FF /* Minimum gain */ -#define B43_NPHY_C2_MINGAIN_SHIFT 0 -#define B43_NPHY_C2_MAXGAIN 0xFF00 /* Maximum gain */ -#define B43_NPHY_C2_MAXGAIN_SHIFT 8 -#define B43_NPHY_C2_CCK_MINMAX_GAIN B43_PHY_N(0x035) /* Core 2 CCK min/max gain */ -#define B43_NPHY_C2_CCK_MINGAIN 0x00FF /* Minimum gain */ -#define B43_NPHY_C2_CCK_MINGAIN_SHIFT 0 -#define B43_NPHY_C2_CCK_MAXGAIN 0xFF00 /* Maximum gain */ -#define B43_NPHY_C2_CCK_MAXGAIN_SHIFT 8 -#define B43_NPHY_C2_INITGAIN B43_PHY_N(0x036) /* Core 2 initial gain code */ -#define B43_NPHY_C2_INITGAIN_EXTLNA 0x0001 /* External LNA index */ -#define B43_NPHY_C2_INITGAIN_LNA 0x0006 /* LNA index */ -#define B43_NPHY_C2_INITGAIN_LNAIDX_SHIFT 1 -#define B43_NPHY_C2_INITGAIN_HPVGA1 0x0078 /* HPVGA1 index */ -#define B43_NPHY_C2_INITGAIN_HPVGA1_SHIFT 3 -#define B43_NPHY_C2_INITGAIN_HPVGA2 0x0F80 /* HPVGA2 index */ -#define B43_NPHY_C2_INITGAIN_HPVGA2_SHIFT 7 -#define B43_NPHY_C2_INITGAIN_TRRX 0x1000 /* TR RX index */ -#define B43_NPHY_C2_INITGAIN_TRTX 0x2000 /* TR TX index */ -#define B43_NPHY_REV3_C1_CLIP_MEDGAIN_B B43_PHY_N(0x036) -#define B43_NPHY_C2_CLIP1_HIGAIN B43_PHY_N(0x037) /* Core 2 clip1 high gain code */ -#define B43_NPHY_REV3_C1_CLIP_LOGAIN_A B43_PHY_N(0x037) -#define B43_NPHY_C2_CLIP1_MEDGAIN B43_PHY_N(0x038) /* Core 2 clip1 medium gain code */ -#define B43_NPHY_REV3_C1_CLIP_LOGAIN_B B43_PHY_N(0x038) -#define B43_NPHY_C2_CLIP1_LOGAIN B43_PHY_N(0x039) /* Core 2 clip1 low gain code */ -#define B43_NPHY_REV3_C1_CLIP2_GAIN_A B43_PHY_N(0x039) -#define B43_NPHY_C2_CLIP2_GAIN B43_PHY_N(0x03A) /* Core 2 clip2 gain code */ -#define B43_NPHY_REV3_C1_CLIP2_GAIN_B B43_PHY_N(0x03A) -#define B43_NPHY_C2_FILTERGAIN B43_PHY_N(0x03B) /* Core 2 filter gain */ -#define B43_NPHY_C2_LPF_QHPF_BW B43_PHY_N(0x03C) /* Core 2 LPF Q HP F bandwidth */ -#define B43_NPHY_C2_CLIPWBTHRES B43_PHY_N(0x03D) /* Core 2 clip wideband threshold */ -#define B43_NPHY_C2_CLIPWBTHRES_CLIP2 0x003F /* Clip 2 */ -#define B43_NPHY_C2_CLIPWBTHRES_CLIP2_SHIFT 0 -#define B43_NPHY_C2_CLIPWBTHRES_CLIP1 0x0FC0 /* Clip 1 */ -#define B43_NPHY_C2_CLIPWBTHRES_CLIP1_SHIFT 6 -#define B43_NPHY_C2_W1THRES B43_PHY_N(0x03E) /* Core 2 W1 threshold */ -#define B43_NPHY_C2_EDTHRES B43_PHY_N(0x03F) /* Core 2 ED threshold */ -#define B43_NPHY_C2_SMSIGTHRES B43_PHY_N(0x040) /* Core 2 small sig threshold */ -#define B43_NPHY_C2_NBCLIPTHRES B43_PHY_N(0x041) /* Core 2 NB clip threshold */ -#define B43_NPHY_C2_CLIP1THRES B43_PHY_N(0x042) /* Core 2 clip1 threshold */ -#define B43_NPHY_C2_CLIP2THRES B43_PHY_N(0x043) /* Core 2 clip2 threshold */ - -#define B43_NPHY_CRS_THRES1 B43_PHY_N(0x044) /* CRS threshold 1 */ -#define B43_NPHY_CRS_THRES2 B43_PHY_N(0x045) /* CRS threshold 2 */ -#define B43_NPHY_CRS_THRES3 B43_PHY_N(0x046) /* CRS threshold 3 */ -#define B43_NPHY_CRSCTL B43_PHY_N(0x047) /* CRS control */ -#define B43_NPHY_DCFADDR B43_PHY_N(0x048) /* DC filter address */ -#define B43_NPHY_RXF20_NUM0 B43_PHY_N(0x049) /* RX filter 20 numerator 0 */ -#define B43_NPHY_RXF20_NUM1 B43_PHY_N(0x04A) /* RX filter 20 numerator 1 */ -#define B43_NPHY_RXF20_NUM2 B43_PHY_N(0x04B) /* RX filter 20 numerator 2 */ -#define B43_NPHY_RXF20_DENOM0 B43_PHY_N(0x04C) /* RX filter 20 denominator 0 */ -#define B43_NPHY_RXF20_DENOM1 B43_PHY_N(0x04D) /* RX filter 20 denominator 1 */ -#define B43_NPHY_RXF20_NUM10 B43_PHY_N(0x04E) /* RX filter 20 numerator 10 */ -#define B43_NPHY_RXF20_NUM11 B43_PHY_N(0x04F) /* RX filter 20 numerator 11 */ -#define B43_NPHY_RXF20_NUM12 B43_PHY_N(0x050) /* RX filter 20 numerator 12 */ -#define B43_NPHY_RXF20_DENOM10 B43_PHY_N(0x051) /* RX filter 20 denominator 10 */ -#define B43_NPHY_RXF20_DENOM11 B43_PHY_N(0x052) /* RX filter 20 denominator 11 */ -#define B43_NPHY_RXF40_NUM0 B43_PHY_N(0x053) /* RX filter 40 numerator 0 */ -#define B43_NPHY_RXF40_NUM1 B43_PHY_N(0x054) /* RX filter 40 numerator 1 */ -#define B43_NPHY_RXF40_NUM2 B43_PHY_N(0x055) /* RX filter 40 numerator 2 */ -#define B43_NPHY_RXF40_DENOM0 B43_PHY_N(0x056) /* RX filter 40 denominator 0 */ -#define B43_NPHY_RXF40_DENOM1 B43_PHY_N(0x057) /* RX filter 40 denominator 1 */ -#define B43_NPHY_RXF40_NUM10 B43_PHY_N(0x058) /* RX filter 40 numerator 10 */ -#define B43_NPHY_RXF40_NUM11 B43_PHY_N(0x059) /* RX filter 40 numerator 11 */ -#define B43_NPHY_RXF40_NUM12 B43_PHY_N(0x05A) /* RX filter 40 numerator 12 */ -#define B43_NPHY_RXF40_DENOM10 B43_PHY_N(0x05B) /* RX filter 40 denominator 10 */ -#define B43_NPHY_RXF40_DENOM11 B43_PHY_N(0x05C) /* RX filter 40 denominator 11 */ -#define B43_NPHY_PPROC_RSTLEN B43_PHY_N(0x060) /* Packet processing reset length */ -#define B43_NPHY_INITCARR_DLEN B43_PHY_N(0x061) /* Initial carrier detection length */ -#define B43_NPHY_CLIP1CARR_DLEN B43_PHY_N(0x062) /* Clip1 carrier detection length */ -#define B43_NPHY_CLIP2CARR_DLEN B43_PHY_N(0x063) /* Clip2 carrier detection length */ -#define B43_NPHY_INITGAIN_SLEN B43_PHY_N(0x064) /* Initial gain settle length */ -#define B43_NPHY_CLIP1GAIN_SLEN B43_PHY_N(0x065) /* Clip1 gain settle length */ -#define B43_NPHY_CLIP2GAIN_SLEN B43_PHY_N(0x066) /* Clip2 gain settle length */ -#define B43_NPHY_PACKGAIN_SLEN B43_PHY_N(0x067) /* Packet gain settle length */ -#define B43_NPHY_CARRSRC_TLEN B43_PHY_N(0x068) /* Carrier search timeout length */ -#define B43_NPHY_TISRC_TLEN B43_PHY_N(0x069) /* Timing search timeout length */ -#define B43_NPHY_ENDROP_TLEN B43_PHY_N(0x06A) /* Energy drop timeout length */ -#define B43_NPHY_CLIP1_NBDWELL_LEN B43_PHY_N(0x06B) /* Clip1 NB dwell length */ -#define B43_NPHY_CLIP2_NBDWELL_LEN B43_PHY_N(0x06C) /* Clip2 NB dwell length */ -#define B43_NPHY_W1CLIP1_DWELL_LEN B43_PHY_N(0x06D) /* W1 clip1 dwell length */ -#define B43_NPHY_W1CLIP2_DWELL_LEN B43_PHY_N(0x06E) /* W1 clip2 dwell length */ -#define B43_NPHY_W2CLIP1_DWELL_LEN B43_PHY_N(0x06F) /* W2 clip1 dwell length */ -#define B43_NPHY_PLOAD_CSENSE_EXTLEN B43_PHY_N(0x070) /* Payload carrier sense extension length */ -#define B43_NPHY_EDROP_CSENSE_EXTLEN B43_PHY_N(0x071) /* Energy drop carrier sense extension length */ -#define B43_NPHY_TABLE_ADDR B43_PHY_N(0x072) /* Table address */ -#define B43_NPHY_TABLE_DATALO B43_PHY_N(0x073) /* Table data low */ -#define B43_NPHY_TABLE_DATAHI B43_PHY_N(0x074) /* Table data high */ -#define B43_NPHY_WWISE_LENIDX B43_PHY_N(0x075) /* WWiSE length index */ -#define B43_NPHY_TGNSYNC_LENIDX B43_PHY_N(0x076) /* TGNsync length index */ -#define B43_NPHY_TXMACIF_HOLDOFF B43_PHY_N(0x077) /* TX MAC IF Hold off */ -#define B43_NPHY_RFCTL_CMD B43_PHY_N(0x078) /* RF control (command) */ -#define B43_NPHY_RFCTL_CMD_START 0x0001 /* Start sequence */ -#define B43_NPHY_RFCTL_CMD_RXTX 0x0002 /* RX/TX */ -#define B43_NPHY_RFCTL_CMD_CORESEL 0x0038 /* Core select */ -#define B43_NPHY_RFCTL_CMD_CORESEL_SHIFT 3 -#define B43_NPHY_RFCTL_CMD_PORFORCE 0x0040 /* POR force */ -#define B43_NPHY_RFCTL_CMD_OEPORFORCE 0x0080 /* OE POR force */ -#define B43_NPHY_RFCTL_CMD_RXEN 0x0100 /* RX enable */ -#define B43_NPHY_RFCTL_CMD_TXEN 0x0200 /* TX enable */ -#define B43_NPHY_RFCTL_CMD_CHIP0PU 0x0400 /* Chip0 PU */ -#define B43_NPHY_RFCTL_CMD_EN 0x0800 /* Radio enabled */ -#define B43_NPHY_RFCTL_CMD_SEQENCORE 0xF000 /* Seq en core */ -#define B43_NPHY_RFCTL_CMD_SEQENCORE_SHIFT 12 -#define B43_NPHY_RFCTL_RSSIO1 B43_PHY_N(0x07A) /* RF control (RSSI others 1) */ -#define B43_NPHY_RFCTL_RSSIO1_RXPD 0x0001 /* RX PD */ -#define B43_NPHY_RFCTL_RSSIO1_TXPD 0x0002 /* TX PD */ -#define B43_NPHY_RFCTL_RSSIO1_PAPD 0x0004 /* PA PD */ -#define B43_NPHY_RFCTL_RSSIO1_RSSICTL 0x0030 /* RSSI control */ -#define B43_NPHY_RFCTL_RSSIO1_LPFBW 0x00C0 /* LPF bandwidth */ -#define B43_NPHY_RFCTL_RSSIO1_HPFBWHI 0x0100 /* HPF bandwidth high */ -#define B43_NPHY_RFCTL_RSSIO1_HIQDISCO 0x0200 /* HIQ dis core */ -#define B43_NPHY_RFCTL_RXG1 B43_PHY_N(0x07B) /* RF control (RX gain 1) */ -#define B43_NPHY_RFCTL_TXG1 B43_PHY_N(0x07C) /* RF control (TX gain 1) */ -#define B43_NPHY_RFCTL_RSSIO2 B43_PHY_N(0x07D) /* RF control (RSSI others 2) */ -#define B43_NPHY_RFCTL_RSSIO2_RXPD 0x0001 /* RX PD */ -#define B43_NPHY_RFCTL_RSSIO2_TXPD 0x0002 /* TX PD */ -#define B43_NPHY_RFCTL_RSSIO2_PAPD 0x0004 /* PA PD */ -#define B43_NPHY_RFCTL_RSSIO2_RSSICTL 0x0030 /* RSSI control */ -#define B43_NPHY_RFCTL_RSSIO2_LPFBW 0x00C0 /* LPF bandwidth */ -#define B43_NPHY_RFCTL_RSSIO2_HPFBWHI 0x0100 /* HPF bandwidth high */ -#define B43_NPHY_RFCTL_RSSIO2_HIQDISCO 0x0200 /* HIQ dis core */ -#define B43_NPHY_RFCTL_RXG2 B43_PHY_N(0x07E) /* RF control (RX gain 2) */ -#define B43_NPHY_RFCTL_TXG2 B43_PHY_N(0x07F) /* RF control (TX gain 2) */ -#define B43_NPHY_RFCTL_RSSIO3 B43_PHY_N(0x080) /* RF control (RSSI others 3) */ -#define B43_NPHY_RFCTL_RSSIO3_RXPD 0x0001 /* RX PD */ -#define B43_NPHY_RFCTL_RSSIO3_TXPD 0x0002 /* TX PD */ -#define B43_NPHY_RFCTL_RSSIO3_PAPD 0x0004 /* PA PD */ -#define B43_NPHY_RFCTL_RSSIO3_RSSICTL 0x0030 /* RSSI control */ -#define B43_NPHY_RFCTL_RSSIO3_LPFBW 0x00C0 /* LPF bandwidth */ -#define B43_NPHY_RFCTL_RSSIO3_HPFBWHI 0x0100 /* HPF bandwidth high */ -#define B43_NPHY_RFCTL_RSSIO3_HIQDISCO 0x0200 /* HIQ dis core */ -#define B43_NPHY_RFCTL_RXG3 B43_PHY_N(0x081) /* RF control (RX gain 3) */ -#define B43_NPHY_RFCTL_TXG3 B43_PHY_N(0x082) /* RF control (TX gain 3) */ -#define B43_NPHY_RFCTL_RSSIO4 B43_PHY_N(0x083) /* RF control (RSSI others 4) */ -#define B43_NPHY_RFCTL_RSSIO4_RXPD 0x0001 /* RX PD */ -#define B43_NPHY_RFCTL_RSSIO4_TXPD 0x0002 /* TX PD */ -#define B43_NPHY_RFCTL_RSSIO4_PAPD 0x0004 /* PA PD */ -#define B43_NPHY_RFCTL_RSSIO4_RSSICTL 0x0030 /* RSSI control */ -#define B43_NPHY_RFCTL_RSSIO4_LPFBW 0x00C0 /* LPF bandwidth */ -#define B43_NPHY_RFCTL_RSSIO4_HPFBWHI 0x0100 /* HPF bandwidth high */ -#define B43_NPHY_RFCTL_RSSIO4_HIQDISCO 0x0200 /* HIQ dis core */ -#define B43_NPHY_RFCTL_RXG4 B43_PHY_N(0x084) /* RF control (RX gain 4) */ -#define B43_NPHY_RFCTL_TXG4 B43_PHY_N(0x085) /* RF control (TX gain 4) */ -#define B43_NPHY_C1_TXIQ_COMP_OFF B43_PHY_N(0x087) /* Core 1 TX I/Q comp offset */ -#define B43_NPHY_C2_TXIQ_COMP_OFF B43_PHY_N(0x088) /* Core 2 TX I/Q comp offset */ -#define B43_NPHY_C1_TXCTL B43_PHY_N(0x08B) /* Core 1 TX control */ -#define B43_NPHY_C2_TXCTL B43_PHY_N(0x08C) /* Core 2 TX control */ -#define B43_NPHY_AFECTL_OVER1 B43_PHY_N(0x08F) /* AFE control override 1 */ -#define B43_NPHY_SCRAM_SIGCTL B43_PHY_N(0x090) /* Scram signal control */ -#define B43_NPHY_SCRAM_SIGCTL_INITST 0x007F /* Initial state value */ -#define B43_NPHY_SCRAM_SIGCTL_INITST_SHIFT 0 -#define B43_NPHY_SCRAM_SIGCTL_SCM 0x0080 /* Scram control mode */ -#define B43_NPHY_SCRAM_SIGCTL_SICE 0x0100 /* Scram index control enable */ -#define B43_NPHY_SCRAM_SIGCTL_START 0xFE00 /* Scram start bit */ -#define B43_NPHY_SCRAM_SIGCTL_START_SHIFT 9 -#define B43_NPHY_RFCTL_INTC1 B43_PHY_N(0x091) /* RF control (intc 1) */ -#define B43_NPHY_RFCTL_INTC2 B43_PHY_N(0x092) /* RF control (intc 2) */ -#define B43_NPHY_RFCTL_INTC3 B43_PHY_N(0x093) /* RF control (intc 3) */ -#define B43_NPHY_RFCTL_INTC4 B43_PHY_N(0x094) /* RF control (intc 4) */ -#define B43_NPHY_NRDTO_WWISE B43_PHY_N(0x095) /* # datatones WWiSE */ -#define B43_NPHY_NRDTO_TGNSYNC B43_PHY_N(0x096) /* # datatones TGNsync */ -#define B43_NPHY_SIGFMOD_WWISE B43_PHY_N(0x097) /* Signal field mod WWiSE */ -#define B43_NPHY_LEG_SIGFMOD_11N B43_PHY_N(0x098) /* Legacy signal field mod 11n */ -#define B43_NPHY_HT_SIGFMOD_11N B43_PHY_N(0x099) /* HT signal field mod 11n */ -#define B43_NPHY_C1_RXIQ_COMPA0 B43_PHY_N(0x09A) /* Core 1 RX I/Q comp A0 */ -#define B43_NPHY_C1_RXIQ_COMPB0 B43_PHY_N(0x09B) /* Core 1 RX I/Q comp B0 */ -#define B43_NPHY_C2_RXIQ_COMPA1 B43_PHY_N(0x09C) /* Core 2 RX I/Q comp A1 */ -#define B43_NPHY_C2_RXIQ_COMPB1 B43_PHY_N(0x09D) /* Core 2 RX I/Q comp B1 */ -#define B43_NPHY_RXCTL B43_PHY_N(0x0A0) /* RX control */ -#define B43_NPHY_RXCTL_BSELU20 0x0010 /* Band select upper 20 */ -#define B43_NPHY_RXCTL_RIFSEN 0x0080 /* RIFS enable */ -#define B43_NPHY_RFSEQMODE B43_PHY_N(0x0A1) /* RF seq mode */ -#define B43_NPHY_RFSEQMODE_CAOVER 0x0001 /* Core active override */ -#define B43_NPHY_RFSEQMODE_TROVER 0x0002 /* Trigger override */ -#define B43_NPHY_RFSEQCA B43_PHY_N(0x0A2) /* RF seq core active */ -#define B43_NPHY_RFSEQCA_TXEN 0x000F /* TX enable */ -#define B43_NPHY_RFSEQCA_TXEN_SHIFT 0 -#define B43_NPHY_RFSEQCA_RXEN 0x00F0 /* RX enable */ -#define B43_NPHY_RFSEQCA_RXEN_SHIFT 4 -#define B43_NPHY_RFSEQCA_TXDIS 0x0F00 /* TX disable */ -#define B43_NPHY_RFSEQCA_TXDIS_SHIFT 8 -#define B43_NPHY_RFSEQCA_RXDIS 0xF000 /* RX disable */ -#define B43_NPHY_RFSEQCA_RXDIS_SHIFT 12 -#define B43_NPHY_RFSEQTR B43_PHY_N(0x0A3) /* RF seq trigger */ -#define B43_NPHY_RFSEQTR_RX2TX 0x0001 /* RX2TX */ -#define B43_NPHY_RFSEQTR_TX2RX 0x0002 /* TX2RX */ -#define B43_NPHY_RFSEQTR_UPGH 0x0004 /* Update gain H */ -#define B43_NPHY_RFSEQTR_UPGL 0x0008 /* Update gain L */ -#define B43_NPHY_RFSEQTR_UPGU 0x0010 /* Update gain U */ -#define B43_NPHY_RFSEQTR_RST2RX 0x0020 /* Reset to RX */ -#define B43_NPHY_RFSEQST B43_PHY_N(0x0A4) /* RF seq status. Values same as trigger. */ -#define B43_NPHY_AFECTL_OVER B43_PHY_N(0x0A5) /* AFE control override */ -#define B43_NPHY_AFECTL_C1 B43_PHY_N(0x0A6) /* AFE control core 1 */ -#define B43_NPHY_AFECTL_C2 B43_PHY_N(0x0A7) /* AFE control core 2 */ -#define B43_NPHY_AFECTL_C3 B43_PHY_N(0x0A8) /* AFE control core 3 */ -#define B43_NPHY_AFECTL_C4 B43_PHY_N(0x0A9) /* AFE control core 4 */ -#define B43_NPHY_AFECTL_DACGAIN1 B43_PHY_N(0x0AA) /* AFE control DAC gain 1 */ -#define B43_NPHY_AFECTL_DACGAIN2 B43_PHY_N(0x0AB) /* AFE control DAC gain 2 */ -#define B43_NPHY_AFECTL_DACGAIN3 B43_PHY_N(0x0AC) /* AFE control DAC gain 3 */ -#define B43_NPHY_AFECTL_DACGAIN4 B43_PHY_N(0x0AD) /* AFE control DAC gain 4 */ -#define B43_NPHY_STR_ADDR1 B43_PHY_N(0x0AE) /* STR address 1 */ -#define B43_NPHY_STR_ADDR2 B43_PHY_N(0x0AF) /* STR address 2 */ -#define B43_NPHY_CLASSCTL B43_PHY_N(0x0B0) /* Classifier control */ -#define B43_NPHY_CLASSCTL_CCKEN 0x0001 /* CCK enable */ -#define B43_NPHY_CLASSCTL_OFDMEN 0x0002 /* OFDM enable */ -#define B43_NPHY_CLASSCTL_WAITEDEN 0x0004 /* Waited enable */ -#define B43_NPHY_IQFLIP B43_PHY_N(0x0B1) /* I/Q flip */ -#define B43_NPHY_IQFLIP_ADC1 0x0001 /* ADC1 */ -#define B43_NPHY_IQFLIP_ADC2 0x0010 /* ADC2 */ -#define B43_NPHY_SISO_SNR_THRES B43_PHY_N(0x0B2) /* SISO SNR threshold */ -#define B43_NPHY_SIGMA_N_MULT B43_PHY_N(0x0B3) /* Sigma N multiplier */ -#define B43_NPHY_TXMACDELAY B43_PHY_N(0x0B4) /* TX MAC delay */ -#define B43_NPHY_TXFRAMEDELAY B43_PHY_N(0x0B5) /* TX frame delay */ -#define B43_NPHY_MLPARM B43_PHY_N(0x0B6) /* ML parameters */ -#define B43_NPHY_MLCTL B43_PHY_N(0x0B7) /* ML control */ -#define B43_NPHY_WWISE_20NCYCDAT B43_PHY_N(0x0B8) /* WWiSE 20 N cyc data */ -#define B43_NPHY_WWISE_40NCYCDAT B43_PHY_N(0x0B9) /* WWiSE 40 N cyc data */ -#define B43_NPHY_TGNSYNC_20NCYCDAT B43_PHY_N(0x0BA) /* TGNsync 20 N cyc data */ -#define B43_NPHY_TGNSYNC_40NCYCDAT B43_PHY_N(0x0BB) /* TGNsync 40 N cyc data */ -#define B43_NPHY_INITSWIZP B43_PHY_N(0x0BC) /* Initial swizzle pattern */ -#define B43_NPHY_TXTAILCNT B43_PHY_N(0x0BD) /* TX tail count value */ -#define B43_NPHY_BPHY_CTL1 B43_PHY_N(0x0BE) /* B PHY control 1 */ -#define B43_NPHY_BPHY_CTL2 B43_PHY_N(0x0BF) /* B PHY control 2 */ -#define B43_NPHY_BPHY_CTL2_LUT 0x001F /* LUT index */ -#define B43_NPHY_BPHY_CTL2_LUT_SHIFT 0 -#define B43_NPHY_BPHY_CTL2_MACDEL 0x7FE0 /* MAC delay */ -#define B43_NPHY_BPHY_CTL2_MACDEL_SHIFT 5 -#define B43_NPHY_IQLOCAL_CMD B43_PHY_N(0x0C0) /* I/Q LO cal command */ -#define B43_NPHY_IQLOCAL_CMD_EN 0x8000 -#define B43_NPHY_IQLOCAL_CMDNNUM B43_PHY_N(0x0C1) /* I/Q LO cal command N num */ -#define B43_NPHY_IQLOCAL_CMDGCTL B43_PHY_N(0x0C2) /* I/Q LO cal command G control */ -#define B43_NPHY_SAMP_CMD B43_PHY_N(0x0C3) /* Sample command */ -#define B43_NPHY_SAMP_CMD_STOP 0x0002 /* Stop */ -#define B43_NPHY_SAMP_LOOPCNT B43_PHY_N(0x0C4) /* Sample loop count */ -#define B43_NPHY_SAMP_WAITCNT B43_PHY_N(0x0C5) /* Sample wait count */ -#define B43_NPHY_SAMP_DEPCNT B43_PHY_N(0x0C6) /* Sample depth count */ -#define B43_NPHY_SAMP_STAT B43_PHY_N(0x0C7) /* Sample status */ -#define B43_NPHY_GPIO_LOOEN B43_PHY_N(0x0C8) /* GPIO low out enable */ -#define B43_NPHY_GPIO_HIOEN B43_PHY_N(0x0C9) /* GPIO high out enable */ -#define B43_NPHY_GPIO_SEL B43_PHY_N(0x0CA) /* GPIO select */ -#define B43_NPHY_GPIO_CLKCTL B43_PHY_N(0x0CB) /* GPIO clock control */ -#define B43_NPHY_TXF_20CO_AS0 B43_PHY_N(0x0CC) /* TX filter 20 coeff A stage 0 */ -#define B43_NPHY_TXF_20CO_AS1 B43_PHY_N(0x0CD) /* TX filter 20 coeff A stage 1 */ -#define B43_NPHY_TXF_20CO_AS2 B43_PHY_N(0x0CE) /* TX filter 20 coeff A stage 2 */ -#define B43_NPHY_TXF_20CO_B32S0 B43_PHY_N(0x0CF) /* TX filter 20 coeff B32 stage 0 */ -#define B43_NPHY_TXF_20CO_B1S0 B43_PHY_N(0x0D0) /* TX filter 20 coeff B1 stage 0 */ -#define B43_NPHY_TXF_20CO_B32S1 B43_PHY_N(0x0D1) /* TX filter 20 coeff B32 stage 1 */ -#define B43_NPHY_TXF_20CO_B1S1 B43_PHY_N(0x0D2) /* TX filter 20 coeff B1 stage 1 */ -#define B43_NPHY_TXF_20CO_B32S2 B43_PHY_N(0x0D3) /* TX filter 20 coeff B32 stage 2 */ -#define B43_NPHY_TXF_20CO_B1S2 B43_PHY_N(0x0D4) /* TX filter 20 coeff B1 stage 2 */ -#define B43_NPHY_SIGFLDTOL B43_PHY_N(0x0D5) /* Signal fld tolerance */ -#define B43_NPHY_TXSERFLD B43_PHY_N(0x0D6) /* TX service field */ -#define B43_NPHY_AFESEQ_RX2TX_PUD B43_PHY_N(0x0D7) /* AFE seq RX2TX power up/down delay */ -#define B43_NPHY_AFESEQ_TX2RX_PUD B43_PHY_N(0x0D8) /* AFE seq TX2RX power up/down delay */ -#define B43_NPHY_TGNSYNC_SCRAMI0 B43_PHY_N(0x0D9) /* TGNsync scram init 0 */ -#define B43_NPHY_TGNSYNC_SCRAMI1 B43_PHY_N(0x0DA) /* TGNsync scram init 1 */ -#define B43_NPHY_INITSWIZPATTLEG B43_PHY_N(0x0DB) /* Initial swizzle pattern leg */ -#define B43_NPHY_BPHY_CTL3 B43_PHY_N(0x0DC) /* B PHY control 3 */ -#define B43_NPHY_BPHY_CTL3_SCALE 0x00FF /* Scale */ -#define B43_NPHY_BPHY_CTL3_SCALE_SHIFT 0 -#define B43_NPHY_BPHY_CTL3_FSC 0xFF00 /* Frame start count value */ -#define B43_NPHY_BPHY_CTL3_FSC_SHIFT 8 -#define B43_NPHY_BPHY_CTL4 B43_PHY_N(0x0DD) /* B PHY control 4 */ -#define B43_NPHY_C1_TXBBMULT B43_PHY_N(0x0DE) /* Core 1 TX BB multiplier */ -#define B43_NPHY_C2_TXBBMULT B43_PHY_N(0x0DF) /* Core 2 TX BB multiplier */ -#define B43_NPHY_TXF_40CO_AS0 B43_PHY_N(0x0E1) /* TX filter 40 coeff A stage 0 */ -#define B43_NPHY_TXF_40CO_AS1 B43_PHY_N(0x0E2) /* TX filter 40 coeff A stage 1 */ -#define B43_NPHY_TXF_40CO_AS2 B43_PHY_N(0x0E3) /* TX filter 40 coeff A stage 2 */ -#define B43_NPHY_TXF_40CO_B32S0 B43_PHY_N(0x0E4) /* TX filter 40 coeff B32 stage 0 */ -#define B43_NPHY_TXF_40CO_B1S0 B43_PHY_N(0x0E5) /* TX filter 40 coeff B1 stage 0 */ -#define B43_NPHY_TXF_40CO_B32S1 B43_PHY_N(0x0E6) /* TX filter 40 coeff B32 stage 1 */ -#define B43_NPHY_TXF_40CO_B1S1 B43_PHY_N(0x0E7) /* TX filter 40 coeff B1 stage 1 */ -#define B43_NPHY_REV3_RFCTL_OVER0 B43_PHY_N(0x0E7) -#define B43_NPHY_TXF_40CO_B32S2 B43_PHY_N(0x0E8) /* TX filter 40 coeff B32 stage 2 */ -#define B43_NPHY_TXF_40CO_B1S2 B43_PHY_N(0x0E9) /* TX filter 40 coeff B1 stage 2 */ -#define B43_NPHY_BIST_STAT2 B43_PHY_N(0x0EA) /* BIST status 2 */ -#define B43_NPHY_BIST_STAT3 B43_PHY_N(0x0EB) /* BIST status 3 */ -#define B43_NPHY_RFCTL_OVER B43_PHY_N(0x0EC) /* RF control override */ -#define B43_NPHY_REV3_RFCTL_OVER1 B43_PHY_N(0x0EC) -#define B43_NPHY_MIMOCFG B43_PHY_N(0x0ED) /* MIMO config */ -#define B43_NPHY_MIMOCFG_GFMIX 0x0004 /* Greenfield or mixed mode */ -#define B43_NPHY_MIMOCFG_AUTO 0x0100 /* Greenfield/mixed mode auto */ -#define B43_NPHY_RADAR_BLNKCTL B43_PHY_N(0x0EE) /* Radar blank control */ -#define B43_NPHY_A0RADAR_FIFOCTL B43_PHY_N(0x0EF) /* Antenna 0 radar FIFO control */ -#define B43_NPHY_A1RADAR_FIFOCTL B43_PHY_N(0x0F0) /* Antenna 1 radar FIFO control */ -#define B43_NPHY_A0RADAR_FIFODAT B43_PHY_N(0x0F1) /* Antenna 0 radar FIFO data */ -#define B43_NPHY_A1RADAR_FIFODAT B43_PHY_N(0x0F2) /* Antenna 1 radar FIFO data */ -#define B43_NPHY_RADAR_THRES0 B43_PHY_N(0x0F3) /* Radar threshold 0 */ -#define B43_NPHY_RADAR_THRES1 B43_PHY_N(0x0F4) /* Radar threshold 1 */ -#define B43_NPHY_RADAR_THRES0R B43_PHY_N(0x0F5) /* Radar threshold 0R */ -#define B43_NPHY_RADAR_THRES1R B43_PHY_N(0x0F6) /* Radar threshold 1R */ -#define B43_NPHY_CSEN_20IN40_DLEN B43_PHY_N(0x0F7) /* Carrier sense 20 in 40 dwell length */ -#define B43_NPHY_RFCTL_LUT_TRSW_LO1 B43_PHY_N(0x0F8) /* RF control LUT TRSW lower 1 */ -#define B43_NPHY_RFCTL_LUT_TRSW_UP1 B43_PHY_N(0x0F9) /* RF control LUT TRSW upper 1 */ -#define B43_NPHY_RFCTL_LUT_TRSW_LO2 B43_PHY_N(0x0FA) /* RF control LUT TRSW lower 2 */ -#define B43_NPHY_RFCTL_LUT_TRSW_UP2 B43_PHY_N(0x0FB) /* RF control LUT TRSW upper 2 */ -#define B43_NPHY_RFCTL_LUT_TRSW_LO3 B43_PHY_N(0x0FC) /* RF control LUT TRSW lower 3 */ -#define B43_NPHY_RFCTL_LUT_TRSW_UP3 B43_PHY_N(0x0FD) /* RF control LUT TRSW upper 3 */ -#define B43_NPHY_RFCTL_LUT_TRSW_LO4 B43_PHY_N(0x0FE) /* RF control LUT TRSW lower 4 */ -#define B43_NPHY_RFCTL_LUT_TRSW_UP4 B43_PHY_N(0x0FF) /* RF control LUT TRSW upper 4 */ -#define B43_NPHY_RFCTL_LUT_LNAPA1 B43_PHY_N(0x100) /* RF control LUT LNA PA 1 */ -#define B43_NPHY_RFCTL_LUT_LNAPA2 B43_PHY_N(0x101) /* RF control LUT LNA PA 2 */ -#define B43_NPHY_RFCTL_LUT_LNAPA3 B43_PHY_N(0x102) /* RF control LUT LNA PA 3 */ -#define B43_NPHY_RFCTL_LUT_LNAPA4 B43_PHY_N(0x103) /* RF control LUT LNA PA 4 */ -#define B43_NPHY_TGNSYNC_CRCM0 B43_PHY_N(0x104) /* TGNsync CRC mask 0 */ -#define B43_NPHY_TGNSYNC_CRCM1 B43_PHY_N(0x105) /* TGNsync CRC mask 1 */ -#define B43_NPHY_TGNSYNC_CRCM2 B43_PHY_N(0x106) /* TGNsync CRC mask 2 */ -#define B43_NPHY_TGNSYNC_CRCM3 B43_PHY_N(0x107) /* TGNsync CRC mask 3 */ -#define B43_NPHY_TGNSYNC_CRCM4 B43_PHY_N(0x108) /* TGNsync CRC mask 4 */ -#define B43_NPHY_CRCPOLY B43_PHY_N(0x109) /* CRC polynomial */ -#define B43_NPHY_SIGCNT B43_PHY_N(0x10A) /* # sig count */ -#define B43_NPHY_SIGSTARTBIT_CTL B43_PHY_N(0x10B) /* Sig start bit control */ -#define B43_NPHY_CRCPOLY_ORDER B43_PHY_N(0x10C) /* CRC polynomial order */ -#define B43_NPHY_RFCTL_CST0 B43_PHY_N(0x10D) /* RF control core swap table 0 */ -#define B43_NPHY_RFCTL_CST1 B43_PHY_N(0x10E) /* RF control core swap table 1 */ -#define B43_NPHY_RFCTL_CST2O B43_PHY_N(0x10F) /* RF control core swap table 2 + others */ -#define B43_NPHY_BPHY_CTL5 B43_PHY_N(0x111) /* B PHY control 5 */ -#define B43_NPHY_RFSEQ_LPFBW B43_PHY_N(0x112) /* RF seq LPF bandwidth */ -#define B43_NPHY_TSSIBIAS1 B43_PHY_N(0x114) /* TSSI bias val 1 */ -#define B43_NPHY_TSSIBIAS2 B43_PHY_N(0x115) /* TSSI bias val 2 */ -#define B43_NPHY_TSSIBIAS_BIAS 0x00FF /* Bias */ -#define B43_NPHY_TSSIBIAS_BIAS_SHIFT 0 -#define B43_NPHY_TSSIBIAS_VAL 0xFF00 /* Value */ -#define B43_NPHY_TSSIBIAS_VAL_SHIFT 8 -#define B43_NPHY_ESTPWR1 B43_PHY_N(0x118) /* Estimated power 1 */ -#define B43_NPHY_ESTPWR2 B43_PHY_N(0x119) /* Estimated power 2 */ -#define B43_NPHY_ESTPWR_PWR 0x00FF /* Estimated power */ -#define B43_NPHY_ESTPWR_PWR_SHIFT 0 -#define B43_NPHY_ESTPWR_VALID 0x0100 /* Estimated power valid */ -#define B43_NPHY_TSSI_MAXTXFDT B43_PHY_N(0x11C) /* TSSI max TX frame delay time */ -#define B43_NPHY_TSSI_MAXTXFDT_VAL 0x00FF /* max TX frame delay time */ -#define B43_NPHY_TSSI_MAXTXFDT_VAL_SHIFT 0 -#define B43_NPHY_TSSI_MAXTDT B43_PHY_N(0x11D) /* TSSI max TSSI delay time */ -#define B43_NPHY_TSSI_MAXTDT_VAL 0x00FF /* max TSSI delay time */ -#define B43_NPHY_TSSI_MAXTDT_VAL_SHIFT 0 -#define B43_NPHY_ITSSI1 B43_PHY_N(0x11E) /* TSSI idle 1 */ -#define B43_NPHY_ITSSI2 B43_PHY_N(0x11F) /* TSSI idle 2 */ -#define B43_NPHY_ITSSI_VAL 0x00FF /* Idle TSSI */ -#define B43_NPHY_ITSSI_VAL_SHIFT 0 -#define B43_NPHY_TSSIMODE B43_PHY_N(0x122) /* TSSI mode */ -#define B43_NPHY_TSSIMODE_EN 0x0001 /* TSSI enable */ -#define B43_NPHY_TSSIMODE_PDEN 0x0002 /* Power det enable */ -#define B43_NPHY_RXMACIFM B43_PHY_N(0x123) /* RX Macif mode */ -#define B43_NPHY_CRSIT_COCNT_LO B43_PHY_N(0x124) /* CRS idle time CRS-on count (low) */ -#define B43_NPHY_CRSIT_COCNT_HI B43_PHY_N(0x125) /* CRS idle time CRS-on count (high) */ -#define B43_NPHY_CRSIT_MTCNT_LO B43_PHY_N(0x126) /* CRS idle time measure time count (low) */ -#define B43_NPHY_CRSIT_MTCNT_HI B43_PHY_N(0x127) /* CRS idle time measure time count (high) */ -#define B43_NPHY_SAMTWC B43_PHY_N(0x128) /* Sample tail wait count */ -#define B43_NPHY_IQEST_CMD B43_PHY_N(0x129) /* I/Q estimate command */ -#define B43_NPHY_IQEST_CMD_START 0x0001 /* Start */ -#define B43_NPHY_IQEST_CMD_MODE 0x0002 /* Mode */ -#define B43_NPHY_IQEST_WT B43_PHY_N(0x12A) /* I/Q estimate wait time */ -#define B43_NPHY_IQEST_WT_VAL 0x00FF /* Wait time */ -#define B43_NPHY_IQEST_WT_VAL_SHIFT 0 -#define B43_NPHY_IQEST_SAMCNT B43_PHY_N(0x12B) /* I/Q estimate sample count */ -#define B43_NPHY_IQEST_IQACC_LO0 B43_PHY_N(0x12C) /* I/Q estimate I/Q acc lo 0 */ -#define B43_NPHY_IQEST_IQACC_HI0 B43_PHY_N(0x12D) /* I/Q estimate I/Q acc hi 0 */ -#define B43_NPHY_IQEST_IPACC_LO0 B43_PHY_N(0x12E) /* I/Q estimate I power acc lo 0 */ -#define B43_NPHY_IQEST_IPACC_HI0 B43_PHY_N(0x12F) /* I/Q estimate I power acc hi 0 */ -#define B43_NPHY_IQEST_QPACC_LO0 B43_PHY_N(0x130) /* I/Q estimate Q power acc lo 0 */ -#define B43_NPHY_IQEST_QPACC_HI0 B43_PHY_N(0x131) /* I/Q estimate Q power acc hi 0 */ -#define B43_NPHY_IQEST_IQACC_LO1 B43_PHY_N(0x134) /* I/Q estimate I/Q acc lo 1 */ -#define B43_NPHY_IQEST_IQACC_HI1 B43_PHY_N(0x135) /* I/Q estimate I/Q acc hi 1 */ -#define B43_NPHY_IQEST_IPACC_LO1 B43_PHY_N(0x136) /* I/Q estimate I power acc lo 1 */ -#define B43_NPHY_IQEST_IPACC_HI1 B43_PHY_N(0x137) /* I/Q estimate I power acc hi 1 */ -#define B43_NPHY_IQEST_QPACC_LO1 B43_PHY_N(0x138) /* I/Q estimate Q power acc lo 1 */ -#define B43_NPHY_IQEST_QPACC_HI1 B43_PHY_N(0x139) /* I/Q estimate Q power acc hi 1 */ -#define B43_NPHY_MIMO_CRSTXEXT B43_PHY_N(0x13A) /* MIMO PHY CRS TX extension */ -#define B43_NPHY_PWRDET1 B43_PHY_N(0x13B) /* Power det 1 */ -#define B43_NPHY_PWRDET2 B43_PHY_N(0x13C) /* Power det 2 */ -#define B43_NPHY_MAXRSSI_DTIME B43_PHY_N(0x13F) /* RSSI max RSSI delay time */ -#define B43_NPHY_PIL_DW0 B43_PHY_N(0x141) /* Pilot data weight 0 */ -#define B43_NPHY_PIL_DW1 B43_PHY_N(0x142) /* Pilot data weight 1 */ -#define B43_NPHY_PIL_DW2 B43_PHY_N(0x143) /* Pilot data weight 2 */ -#define B43_NPHY_PIL_DW_BPSK 0x000F /* BPSK */ -#define B43_NPHY_PIL_DW_BPSK_SHIFT 0 -#define B43_NPHY_PIL_DW_QPSK 0x00F0 /* QPSK */ -#define B43_NPHY_PIL_DW_QPSK_SHIFT 4 -#define B43_NPHY_PIL_DW_16QAM 0x0F00 /* 16-QAM */ -#define B43_NPHY_PIL_DW_16QAM_SHIFT 8 -#define B43_NPHY_PIL_DW_64QAM 0xF000 /* 64-QAM */ -#define B43_NPHY_PIL_DW_64QAM_SHIFT 12 -#define B43_NPHY_FMDEM_CFG B43_PHY_N(0x144) /* FM demodulation config */ -#define B43_NPHY_PHASETR_A0 B43_PHY_N(0x145) /* Phase track alpha 0 */ -#define B43_NPHY_PHASETR_A1 B43_PHY_N(0x146) /* Phase track alpha 1 */ -#define B43_NPHY_PHASETR_A2 B43_PHY_N(0x147) /* Phase track alpha 2 */ -#define B43_NPHY_PHASETR_B0 B43_PHY_N(0x148) /* Phase track beta 0 */ -#define B43_NPHY_PHASETR_B1 B43_PHY_N(0x149) /* Phase track beta 1 */ -#define B43_NPHY_PHASETR_B2 B43_PHY_N(0x14A) /* Phase track beta 2 */ -#define B43_NPHY_PHASETR_CHG0 B43_PHY_N(0x14B) /* Phase track change 0 */ -#define B43_NPHY_PHASETR_CHG1 B43_PHY_N(0x14C) /* Phase track change 1 */ -#define B43_NPHY_PHASETW_OFF B43_PHY_N(0x14D) /* Phase track offset */ -#define B43_NPHY_RFCTL_DBG B43_PHY_N(0x14E) /* RF control debug */ -#define B43_NPHY_CCK_SHIFTB_REF B43_PHY_N(0x150) /* CCK shiftbits reference var */ -#define B43_NPHY_OVER_DGAIN0 B43_PHY_N(0x152) /* Override digital gain 0 */ -#define B43_NPHY_OVER_DGAIN1 B43_PHY_N(0x153) /* Override digital gain 1 */ -#define B43_NPHY_OVER_DGAIN_FDGV 0x0007 /* Force digital gain value */ -#define B43_NPHY_OVER_DGAIN_FDGV_SHIFT 0 -#define B43_NPHY_OVER_DGAIN_FDGEN 0x0008 /* Force digital gain enable */ -#define B43_NPHY_OVER_DGAIN_CCKDGECV 0xFF00 /* CCK digital gain enable count value */ -#define B43_NPHY_OVER_DGAIN_CCKDGECV_SHIFT 8 -#define B43_NPHY_BIST_STAT4 B43_PHY_N(0x156) /* BIST status 4 */ -#define B43_NPHY_RADAR_MAL B43_PHY_N(0x157) /* Radar MA length */ -#define B43_NPHY_RADAR_SRCCTL B43_PHY_N(0x158) /* Radar search control */ -#define B43_NPHY_VLD_DTSIG B43_PHY_N(0x159) /* VLD data tones sig */ -#define B43_NPHY_VLD_DTDAT B43_PHY_N(0x15A) /* VLD data tones data */ -#define B43_NPHY_C1_BPHY_RXIQCA0 B43_PHY_N(0x15B) /* Core 1 B PHY RX I/Q comp A0 */ -#define B43_NPHY_C1_BPHY_RXIQCB0 B43_PHY_N(0x15C) /* Core 1 B PHY RX I/Q comp B0 */ -#define B43_NPHY_C2_BPHY_RXIQCA1 B43_PHY_N(0x15D) /* Core 2 B PHY RX I/Q comp A1 */ -#define B43_NPHY_C2_BPHY_RXIQCB1 B43_PHY_N(0x15E) /* Core 2 B PHY RX I/Q comp B1 */ -#define B43_NPHY_FREQGAIN0 B43_PHY_N(0x160) /* Frequency gain 0 */ -#define B43_NPHY_FREQGAIN1 B43_PHY_N(0x161) /* Frequency gain 1 */ -#define B43_NPHY_FREQGAIN2 B43_PHY_N(0x162) /* Frequency gain 2 */ -#define B43_NPHY_FREQGAIN3 B43_PHY_N(0x163) /* Frequency gain 3 */ -#define B43_NPHY_FREQGAIN4 B43_PHY_N(0x164) /* Frequency gain 4 */ -#define B43_NPHY_FREQGAIN5 B43_PHY_N(0x165) /* Frequency gain 5 */ -#define B43_NPHY_FREQGAIN6 B43_PHY_N(0x166) /* Frequency gain 6 */ -#define B43_NPHY_FREQGAIN7 B43_PHY_N(0x167) /* Frequency gain 7 */ -#define B43_NPHY_FREQGAIN_BYPASS B43_PHY_N(0x168) /* Frequency gain bypass */ -#define B43_NPHY_TRLOSS B43_PHY_N(0x169) /* TR loss value */ -#define B43_NPHY_C1_ADCCLIP B43_PHY_N(0x16A) /* Core 1 ADC clip */ -#define B43_NPHY_C2_ADCCLIP B43_PHY_N(0x16B) /* Core 2 ADC clip */ -#define B43_NPHY_LTRN_OFFGAIN B43_PHY_N(0x16F) /* LTRN offset gain */ -#define B43_NPHY_LTRN_OFF B43_PHY_N(0x170) /* LTRN offset */ -#define B43_NPHY_NRDATAT_WWISE20SIG B43_PHY_N(0x171) /* # data tones WWiSE 20 sig */ -#define B43_NPHY_NRDATAT_WWISE40SIG B43_PHY_N(0x172) /* # data tones WWiSE 40 sig */ -#define B43_NPHY_NRDATAT_TGNSYNC20SIG B43_PHY_N(0x173) /* # data tones TGNsync 20 sig */ -#define B43_NPHY_NRDATAT_TGNSYNC40SIG B43_PHY_N(0x174) /* # data tones TGNsync 40 sig */ -#define B43_NPHY_WWISE_CRCM0 B43_PHY_N(0x175) /* WWiSE CRC mask 0 */ -#define B43_NPHY_WWISE_CRCM1 B43_PHY_N(0x176) /* WWiSE CRC mask 1 */ -#define B43_NPHY_WWISE_CRCM2 B43_PHY_N(0x177) /* WWiSE CRC mask 2 */ -#define B43_NPHY_WWISE_CRCM3 B43_PHY_N(0x178) /* WWiSE CRC mask 3 */ -#define B43_NPHY_WWISE_CRCM4 B43_PHY_N(0x179) /* WWiSE CRC mask 4 */ -#define B43_NPHY_CHANEST_CDDSH B43_PHY_N(0x17A) /* Channel estimate CDD shift */ -#define B43_NPHY_HTAGC_WCNT B43_PHY_N(0x17B) /* HT ADC wait counters */ -#define B43_NPHY_SQPARM B43_PHY_N(0x17C) /* SQ params */ -#define B43_NPHY_MCSDUP6M B43_PHY_N(0x17D) /* MCS dup 6M */ -#define B43_NPHY_NDATAT_DUP40 B43_PHY_N(0x17E) /* # data tones dup 40 */ -#define B43_NPHY_DUP40_TGNSYNC_CYCD B43_PHY_N(0x17F) /* Dup40 TGNsync cycle data */ -#define B43_NPHY_DUP40_GFBL B43_PHY_N(0x180) /* Dup40 GF format BL address */ -#define B43_NPHY_DUP40_BL B43_PHY_N(0x181) /* Dup40 format BL address */ -#define B43_NPHY_LEGDUP_FTA B43_PHY_N(0x182) /* Legacy dup frm table address */ -#define B43_NPHY_PACPROC_DBG B43_PHY_N(0x183) /* Packet processing debug */ -#define B43_NPHY_PIL_CYC1 B43_PHY_N(0x184) /* Pilot cycle counter 1 */ -#define B43_NPHY_PIL_CYC2 B43_PHY_N(0x185) /* Pilot cycle counter 2 */ -#define B43_NPHY_TXF_20CO_S0A1 B43_PHY_N(0x186) /* TX filter 20 coeff stage 0 A1 */ -#define B43_NPHY_TXF_20CO_S0A2 B43_PHY_N(0x187) /* TX filter 20 coeff stage 0 A2 */ -#define B43_NPHY_TXF_20CO_S1A1 B43_PHY_N(0x188) /* TX filter 20 coeff stage 1 A1 */ -#define B43_NPHY_TXF_20CO_S1A2 B43_PHY_N(0x189) /* TX filter 20 coeff stage 1 A2 */ -#define B43_NPHY_TXF_20CO_S2A1 B43_PHY_N(0x18A) /* TX filter 20 coeff stage 2 A1 */ -#define B43_NPHY_TXF_20CO_S2A2 B43_PHY_N(0x18B) /* TX filter 20 coeff stage 2 A2 */ -#define B43_NPHY_TXF_20CO_S0B1 B43_PHY_N(0x18C) /* TX filter 20 coeff stage 0 B1 */ -#define B43_NPHY_TXF_20CO_S0B2 B43_PHY_N(0x18D) /* TX filter 20 coeff stage 0 B2 */ -#define B43_NPHY_TXF_20CO_S0B3 B43_PHY_N(0x18E) /* TX filter 20 coeff stage 0 B3 */ -#define B43_NPHY_TXF_20CO_S1B1 B43_PHY_N(0x18F) /* TX filter 20 coeff stage 1 B1 */ -#define B43_NPHY_TXF_20CO_S1B2 B43_PHY_N(0x190) /* TX filter 20 coeff stage 1 B2 */ -#define B43_NPHY_TXF_20CO_S1B3 B43_PHY_N(0x191) /* TX filter 20 coeff stage 1 B3 */ -#define B43_NPHY_TXF_20CO_S2B1 B43_PHY_N(0x192) /* TX filter 20 coeff stage 2 B1 */ -#define B43_NPHY_TXF_20CO_S2B2 B43_PHY_N(0x193) /* TX filter 20 coeff stage 2 B2 */ -#define B43_NPHY_TXF_20CO_S2B3 B43_PHY_N(0x194) /* TX filter 20 coeff stage 2 B3 */ -#define B43_NPHY_TXF_40CO_S0A1 B43_PHY_N(0x195) /* TX filter 40 coeff stage 0 A1 */ -#define B43_NPHY_TXF_40CO_S0A2 B43_PHY_N(0x196) /* TX filter 40 coeff stage 0 A2 */ -#define B43_NPHY_TXF_40CO_S1A1 B43_PHY_N(0x197) /* TX filter 40 coeff stage 1 A1 */ -#define B43_NPHY_TXF_40CO_S1A2 B43_PHY_N(0x198) /* TX filter 40 coeff stage 1 A2 */ -#define B43_NPHY_TXF_40CO_S2A1 B43_PHY_N(0x199) /* TX filter 40 coeff stage 2 A1 */ -#define B43_NPHY_TXF_40CO_S2A2 B43_PHY_N(0x19A) /* TX filter 40 coeff stage 2 A2 */ -#define B43_NPHY_TXF_40CO_S0B1 B43_PHY_N(0x19B) /* TX filter 40 coeff stage 0 B1 */ -#define B43_NPHY_TXF_40CO_S0B2 B43_PHY_N(0x19C) /* TX filter 40 coeff stage 0 B2 */ -#define B43_NPHY_TXF_40CO_S0B3 B43_PHY_N(0x19D) /* TX filter 40 coeff stage 0 B3 */ -#define B43_NPHY_TXF_40CO_S1B1 B43_PHY_N(0x19E) /* TX filter 40 coeff stage 1 B1 */ -#define B43_NPHY_TXF_40CO_S1B2 B43_PHY_N(0x19F) /* TX filter 40 coeff stage 1 B2 */ -#define B43_NPHY_TXF_40CO_S1B3 B43_PHY_N(0x1A0) /* TX filter 40 coeff stage 1 B3 */ -#define B43_NPHY_TXF_40CO_S2B1 B43_PHY_N(0x1A1) /* TX filter 40 coeff stage 2 B1 */ -#define B43_NPHY_TXF_40CO_S2B2 B43_PHY_N(0x1A2) /* TX filter 40 coeff stage 2 B2 */ -#define B43_NPHY_TXF_40CO_S2B3 B43_PHY_N(0x1A3) /* TX filter 40 coeff stage 2 B3 */ -#define B43_NPHY_RSSIMC_0I_RSSI_X B43_PHY_N(0x1A4) /* RSSI multiplication coefficient 0 I RSSI X */ -#define B43_NPHY_RSSIMC_0I_RSSI_Y B43_PHY_N(0x1A5) /* RSSI multiplication coefficient 0 I RSSI Y */ -#define B43_NPHY_RSSIMC_0I_RSSI_Z B43_PHY_N(0x1A6) /* RSSI multiplication coefficient 0 I RSSI Z */ -#define B43_NPHY_RSSIMC_0I_TBD B43_PHY_N(0x1A7) /* RSSI multiplication coefficient 0 I TBD */ -#define B43_NPHY_RSSIMC_0I_PWRDET B43_PHY_N(0x1A8) /* RSSI multiplication coefficient 0 I power det */ -#define B43_NPHY_RSSIMC_0I_TSSI B43_PHY_N(0x1A9) /* RSSI multiplication coefficient 0 I TSSI */ -#define B43_NPHY_RSSIMC_0Q_RSSI_X B43_PHY_N(0x1AA) /* RSSI multiplication coefficient 0 Q RSSI X */ -#define B43_NPHY_RSSIMC_0Q_RSSI_Y B43_PHY_N(0x1AB) /* RSSI multiplication coefficient 0 Q RSSI Y */ -#define B43_NPHY_RSSIMC_0Q_RSSI_Z B43_PHY_N(0x1AC) /* RSSI multiplication coefficient 0 Q RSSI Z */ -#define B43_NPHY_RSSIMC_0Q_TBD B43_PHY_N(0x1AD) /* RSSI multiplication coefficient 0 Q TBD */ -#define B43_NPHY_RSSIMC_0Q_PWRDET B43_PHY_N(0x1AE) /* RSSI multiplication coefficient 0 Q power det */ -#define B43_NPHY_RSSIMC_0Q_TSSI B43_PHY_N(0x1AF) /* RSSI multiplication coefficient 0 Q TSSI */ -#define B43_NPHY_RSSIMC_1I_RSSI_X B43_PHY_N(0x1B0) /* RSSI multiplication coefficient 1 I RSSI X */ -#define B43_NPHY_RSSIMC_1I_RSSI_Y B43_PHY_N(0x1B1) /* RSSI multiplication coefficient 1 I RSSI Y */ -#define B43_NPHY_RSSIMC_1I_RSSI_Z B43_PHY_N(0x1B2) /* RSSI multiplication coefficient 1 I RSSI Z */ -#define B43_NPHY_RSSIMC_1I_TBD B43_PHY_N(0x1B3) /* RSSI multiplication coefficient 1 I TBD */ -#define B43_NPHY_RSSIMC_1I_PWRDET B43_PHY_N(0x1B4) /* RSSI multiplication coefficient 1 I power det */ -#define B43_NPHY_RSSIMC_1I_TSSI B43_PHY_N(0x1B5) /* RSSI multiplication coefficient 1 I TSSI */ -#define B43_NPHY_RSSIMC_1Q_RSSI_X B43_PHY_N(0x1B6) /* RSSI multiplication coefficient 1 Q RSSI X */ -#define B43_NPHY_RSSIMC_1Q_RSSI_Y B43_PHY_N(0x1B7) /* RSSI multiplication coefficient 1 Q RSSI Y */ -#define B43_NPHY_RSSIMC_1Q_RSSI_Z B43_PHY_N(0x1B8) /* RSSI multiplication coefficient 1 Q RSSI Z */ -#define B43_NPHY_RSSIMC_1Q_TBD B43_PHY_N(0x1B9) /* RSSI multiplication coefficient 1 Q TBD */ -#define B43_NPHY_RSSIMC_1Q_PWRDET B43_PHY_N(0x1BA) /* RSSI multiplication coefficient 1 Q power det */ -#define B43_NPHY_RSSIMC_1Q_TSSI B43_PHY_N(0x1BB) /* RSSI multiplication coefficient 1 Q TSSI */ -#define B43_NPHY_SAMC_WCNT B43_PHY_N(0x1BC) /* Sample collect wait counter */ -#define B43_NPHY_PTHROUGH_CNT B43_PHY_N(0x1BD) /* Pass-through counter */ -#define B43_NPHY_LTRN_OFF_G20L B43_PHY_N(0x1C4) /* LTRN offset gain 20L */ -#define B43_NPHY_LTRN_OFF_20L B43_PHY_N(0x1C5) /* LTRN offset 20L */ -#define B43_NPHY_LTRN_OFF_G20U B43_PHY_N(0x1C6) /* LTRN offset gain 20U */ -#define B43_NPHY_LTRN_OFF_20U B43_PHY_N(0x1C7) /* LTRN offset 20U */ -#define B43_NPHY_DSSSCCK_GAINSL B43_PHY_N(0x1C8) /* DSSS/CCK gain settle length */ -#define B43_NPHY_GPIO_LOOUT B43_PHY_N(0x1C9) /* GPIO low out */ -#define B43_NPHY_GPIO_HIOUT B43_PHY_N(0x1CA) /* GPIO high out */ -#define B43_NPHY_CRS_CHECK B43_PHY_N(0x1CB) /* CRS check */ -#define B43_NPHY_ML_LOGSS_RAT B43_PHY_N(0x1CC) /* ML/logss ratio */ -#define B43_NPHY_DUPSCALE B43_PHY_N(0x1CD) /* Dup scale */ -#define B43_NPHY_BW1A B43_PHY_N(0x1CE) /* BW 1A */ -#define B43_NPHY_BW2 B43_PHY_N(0x1CF) /* BW 2 */ -#define B43_NPHY_BW3 B43_PHY_N(0x1D0) /* BW 3 */ -#define B43_NPHY_BW4 B43_PHY_N(0x1D1) /* BW 4 */ -#define B43_NPHY_BW5 B43_PHY_N(0x1D2) /* BW 5 */ -#define B43_NPHY_BW6 B43_PHY_N(0x1D3) /* BW 6 */ -#define B43_NPHY_COALEN0 B43_PHY_N(0x1D4) /* Coarse length 0 */ -#define B43_NPHY_COALEN1 B43_PHY_N(0x1D5) /* Coarse length 1 */ -#define B43_NPHY_CRSTHRES_1U B43_PHY_N(0x1D6) /* CRS threshold 1 U */ -#define B43_NPHY_CRSTHRES_2U B43_PHY_N(0x1D7) /* CRS threshold 2 U */ -#define B43_NPHY_CRSTHRES_3U B43_PHY_N(0x1D8) /* CRS threshold 3 U */ -#define B43_NPHY_CRSCTL_U B43_PHY_N(0x1D9) /* CRS control U */ -#define B43_NPHY_CRSTHRES_1L B43_PHY_N(0x1DA) /* CRS threshold 1 L */ -#define B43_NPHY_CRSTHRES_2L B43_PHY_N(0x1DB) /* CRS threshold 2 L */ -#define B43_NPHY_CRSTHRES_3L B43_PHY_N(0x1DC) /* CRS threshold 3 L */ -#define B43_NPHY_CRSCTL_L B43_PHY_N(0x1DD) /* CRS control L */ -#define B43_NPHY_STRA_1U B43_PHY_N(0x1DE) /* STR address 1 U */ -#define B43_NPHY_STRA_2U B43_PHY_N(0x1DF) /* STR address 2 U */ -#define B43_NPHY_STRA_1L B43_PHY_N(0x1E0) /* STR address 1 L */ -#define B43_NPHY_STRA_2L B43_PHY_N(0x1E1) /* STR address 2 L */ -#define B43_NPHY_CRSCHECK1 B43_PHY_N(0x1E2) /* CRS check 1 */ -#define B43_NPHY_CRSCHECK2 B43_PHY_N(0x1E3) /* CRS check 2 */ -#define B43_NPHY_CRSCHECK3 B43_PHY_N(0x1E4) /* CRS check 3 */ -#define B43_NPHY_JMPSTP0 B43_PHY_N(0x1E5) /* Jump step 0 */ -#define B43_NPHY_JMPSTP1 B43_PHY_N(0x1E6) /* Jump step 1 */ -#define B43_NPHY_TXPCTL_CMD B43_PHY_N(0x1E7) /* TX power control command */ -#define B43_NPHY_TXPCTL_CMD_INIT 0x007F /* Init */ -#define B43_NPHY_TXPCTL_CMD_INIT_SHIFT 0 -#define B43_NPHY_TXPCTL_CMD_COEFF 0x2000 /* Power control coefficients */ -#define B43_NPHY_TXPCTL_CMD_HWPCTLEN 0x4000 /* Hardware TX power control enable */ -#define B43_NPHY_TXPCTL_CMD_PCTLEN 0x8000 /* TX power control enable */ -#define B43_NPHY_TXPCTL_N B43_PHY_N(0x1E8) /* TX power control N num */ -#define B43_NPHY_TXPCTL_N_TSSID 0x00FF /* N TSSI delay */ -#define B43_NPHY_TXPCTL_N_TSSID_SHIFT 0 -#define B43_NPHY_TXPCTL_N_NPTIL2 0x0700 /* N PT integer log2 */ -#define B43_NPHY_TXPCTL_N_NPTIL2_SHIFT 8 -#define B43_NPHY_TXPCTL_ITSSI B43_PHY_N(0x1E9) /* TX power control idle TSSI */ -#define B43_NPHY_TXPCTL_ITSSI_0 0x003F /* Idle TSSI 0 */ -#define B43_NPHY_TXPCTL_ITSSI_0_SHIFT 0 -#define B43_NPHY_TXPCTL_ITSSI_1 0x3F00 /* Idle TSSI 1 */ -#define B43_NPHY_TXPCTL_ITSSI_1_SHIFT 8 -#define B43_NPHY_TXPCTL_ITSSI_BINF 0x8000 /* Raw TSSI offset bin format */ -#define B43_NPHY_TXPCTL_TPWR B43_PHY_N(0x1EA) /* TX power control target power */ -#define B43_NPHY_TXPCTL_TPWR_0 0x00FF /* Power 0 */ -#define B43_NPHY_TXPCTL_TPWR_0_SHIFT 0 -#define B43_NPHY_TXPCTL_TPWR_1 0xFF00 /* Power 1 */ -#define B43_NPHY_TXPCTL_TPWR_1_SHIFT 8 -#define B43_NPHY_TXPCTL_BIDX B43_PHY_N(0x1EB) /* TX power control base index */ -#define B43_NPHY_TXPCTL_BIDX_0 0x007F /* uC base index 0 */ -#define B43_NPHY_TXPCTL_BIDX_0_SHIFT 0 -#define B43_NPHY_TXPCTL_BIDX_1 0x7F00 /* uC base index 1 */ -#define B43_NPHY_TXPCTL_BIDX_1_SHIFT 8 -#define B43_NPHY_TXPCTL_BIDX_LOAD 0x8000 /* Load base index */ -#define B43_NPHY_TXPCTL_PIDX B43_PHY_N(0x1EC) /* TX power control power index */ -#define B43_NPHY_TXPCTL_PIDX_0 0x007F /* uC power index 0 */ -#define B43_NPHY_TXPCTL_PIDX_0_SHIFT 0 -#define B43_NPHY_TXPCTL_PIDX_1 0x7F00 /* uC power index 1 */ -#define B43_NPHY_TXPCTL_PIDX_1_SHIFT 8 -#define B43_NPHY_C1_TXPCTL_STAT B43_PHY_N(0x1ED) /* Core 1 TX power control status */ -#define B43_NPHY_C2_TXPCTL_STAT B43_PHY_N(0x1EE) /* Core 2 TX power control status */ -#define B43_NPHY_TXPCTL_STAT_EST 0x00FF /* Estimated power */ -#define B43_NPHY_TXPCTL_STAT_EST_SHIFT 0 -#define B43_NPHY_TXPCTL_STAT_BIDX 0x7F00 /* Base index */ -#define B43_NPHY_TXPCTL_STAT_BIDX_SHIFT 8 -#define B43_NPHY_TXPCTL_STAT_ESTVALID 0x8000 /* Estimated power valid */ -#define B43_NPHY_SMALLSGS_LEN B43_PHY_N(0x1EF) /* Small sig gain settle length */ -#define B43_NPHY_PHYSTAT_GAIN0 B43_PHY_N(0x1F0) /* PHY stats gain info 0 */ -#define B43_NPHY_PHYSTAT_GAIN1 B43_PHY_N(0x1F1) /* PHY stats gain info 1 */ -#define B43_NPHY_PHYSTAT_FREQEST B43_PHY_N(0x1F2) /* PHY stats frequency estimate */ -#define B43_NPHY_PHYSTAT_ADVRET B43_PHY_N(0x1F3) /* PHY stats ADV retard */ -#define B43_NPHY_PHYLB_MODE B43_PHY_N(0x1F4) /* PHY loopback mode */ -#define B43_NPHY_TONE_MIDX20_1 B43_PHY_N(0x1F5) /* Tone map index 20/1 */ -#define B43_NPHY_TONE_MIDX20_2 B43_PHY_N(0x1F6) /* Tone map index 20/2 */ -#define B43_NPHY_TONE_MIDX20_3 B43_PHY_N(0x1F7) /* Tone map index 20/3 */ -#define B43_NPHY_TONE_MIDX40_1 B43_PHY_N(0x1F8) /* Tone map index 40/1 */ -#define B43_NPHY_TONE_MIDX40_2 B43_PHY_N(0x1F9) /* Tone map index 40/2 */ -#define B43_NPHY_TONE_MIDX40_3 B43_PHY_N(0x1FA) /* Tone map index 40/3 */ -#define B43_NPHY_TONE_MIDX40_4 B43_PHY_N(0x1FB) /* Tone map index 40/4 */ -#define B43_NPHY_PILTONE_MIDX1 B43_PHY_N(0x1FC) /* Pilot tone map index 1 */ -#define B43_NPHY_PILTONE_MIDX2 B43_PHY_N(0x1FD) /* Pilot tone map index 2 */ -#define B43_NPHY_PILTONE_MIDX3 B43_PHY_N(0x1FE) /* Pilot tone map index 3 */ -#define B43_NPHY_TXRIFS_FRDEL B43_PHY_N(0x1FF) /* TX RIFS frame delay */ -#define B43_NPHY_AFESEQ_RX2TX_PUD_40M B43_PHY_N(0x200) /* AFE seq rx2tx power up/down delay 40M */ -#define B43_NPHY_AFESEQ_TX2RX_PUD_40M B43_PHY_N(0x201) /* AFE seq tx2rx power up/down delay 40M */ -#define B43_NPHY_AFESEQ_RX2TX_PUD_20M B43_PHY_N(0x202) /* AFE seq rx2tx power up/down delay 20M */ -#define B43_NPHY_AFESEQ_TX2RX_PUD_20M B43_PHY_N(0x203) /* AFE seq tx2rx power up/down delay 20M */ -#define B43_NPHY_RX_SIGCTL B43_PHY_N(0x204) /* RX signal control */ -#define B43_NPHY_RXPIL_CYCNT0 B43_PHY_N(0x205) /* RX pilot cycle counter 0 */ -#define B43_NPHY_RXPIL_CYCNT1 B43_PHY_N(0x206) /* RX pilot cycle counter 1 */ -#define B43_NPHY_RXPIL_CYCNT2 B43_PHY_N(0x207) /* RX pilot cycle counter 2 */ -#define B43_NPHY_AFESEQ_RX2TX_PUD_10M B43_PHY_N(0x208) /* AFE seq rx2tx power up/down delay 10M */ -#define B43_NPHY_AFESEQ_TX2RX_PUD_10M B43_PHY_N(0x209) /* AFE seq tx2rx power up/down delay 10M */ -#define B43_NPHY_DSSSCCK_CRSEXTL B43_PHY_N(0x20A) /* DSSS/CCK CRS extension length */ -#define B43_NPHY_ML_LOGSS_RATSLOPE B43_PHY_N(0x20B) /* ML/logss ratio slope */ -#define B43_NPHY_RIFS_SRCTL B43_PHY_N(0x20C) /* RIFS search timeout length */ -#define B43_NPHY_TXREALFD B43_PHY_N(0x20D) /* TX real frame delay */ -#define B43_NPHY_HPANT_SWTHRES B43_PHY_N(0x20E) /* High power antenna switch threshold */ -#define B43_NPHY_EDCRS_ASSTHRES0 B43_PHY_N(0x210) /* ED CRS assert threshold 0 */ -#define B43_NPHY_EDCRS_ASSTHRES1 B43_PHY_N(0x211) /* ED CRS assert threshold 1 */ -#define B43_NPHY_EDCRS_DEASSTHRES0 B43_PHY_N(0x212) /* ED CRS deassert threshold 0 */ -#define B43_NPHY_EDCRS_DEASSTHRES1 B43_PHY_N(0x213) /* ED CRS deassert threshold 1 */ -#define B43_NPHY_STR_WTIME20U B43_PHY_N(0x214) /* STR wait time 20U */ -#define B43_NPHY_STR_WTIME20L B43_PHY_N(0x215) /* STR wait time 20L */ -#define B43_NPHY_TONE_MIDX657M B43_PHY_N(0x216) /* Tone map index 657M */ -#define B43_NPHY_HTSIGTONES B43_PHY_N(0x217) /* HT signal tones */ -#define B43_NPHY_RSSI1 B43_PHY_N(0x219) /* RSSI value 1 */ -#define B43_NPHY_RSSI2 B43_PHY_N(0x21A) /* RSSI value 2 */ -#define B43_NPHY_CHAN_ESTHANG B43_PHY_N(0x21D) /* Channel estimate hang */ -#define B43_NPHY_FINERX2_CGC B43_PHY_N(0x221) /* Fine RX 2 clock gate control */ -#define B43_NPHY_FINERX2_CGC_DECGC 0x0008 /* Decode gated clocks */ -#define B43_NPHY_TXPCTL_INIT B43_PHY_N(0x222) /* TX power control init */ -#define B43_NPHY_TXPCTL_INIT_PIDXI1 0x00FF /* Power index init 1 */ -#define B43_NPHY_TXPCTL_INIT_PIDXI1_SHIFT 0 -#define B43_NPHY_ED_CRSEN B43_PHY_N(0x223) -#define B43_NPHY_ED_CRS40ASSERTTHRESH0 B43_PHY_N(0x224) -#define B43_NPHY_ED_CRS40ASSERTTHRESH1 B43_PHY_N(0x225) -#define B43_NPHY_ED_CRS40DEASSERTTHRESH0 B43_PHY_N(0x226) -#define B43_NPHY_ED_CRS40DEASSERTTHRESH1 B43_PHY_N(0x227) -#define B43_NPHY_ED_CRS20LASSERTTHRESH0 B43_PHY_N(0x228) -#define B43_NPHY_ED_CRS20LASSERTTHRESH1 B43_PHY_N(0x229) -#define B43_NPHY_ED_CRS20LDEASSERTTHRESH0 B43_PHY_N(0x22A) -#define B43_NPHY_ED_CRS20LDEASSERTTHRESH1 B43_PHY_N(0x22B) -#define B43_NPHY_ED_CRS20UASSERTTHRESH0 B43_PHY_N(0x22C) -#define B43_NPHY_ED_CRS20UASSERTTHRESH1 B43_PHY_N(0x22D) -#define B43_NPHY_ED_CRS20UDEASSERTTHRESH0 B43_PHY_N(0x22E) -#define B43_NPHY_ED_CRS20UDEASSERTTHRESH1 B43_PHY_N(0x22F) -#define B43_NPHY_ED_CRS B43_PHY_N(0x230) -#define B43_NPHY_TIMEOUTEN B43_PHY_N(0x231) -#define B43_NPHY_OFDMPAYDECODETIMEOUTLEN B43_PHY_N(0x232) -#define B43_NPHY_CCKPAYDECODETIMEOUTLEN B43_PHY_N(0x233) -#define B43_NPHY_NONPAYDECODETIMEOUTLEN B43_PHY_N(0x234) -#define B43_NPHY_TIMEOUTSTATUS B43_PHY_N(0x235) -#define B43_NPHY_RFCTRLCORE0GPIO0 B43_PHY_N(0x236) -#define B43_NPHY_RFCTRLCORE0GPIO1 B43_PHY_N(0x237) -#define B43_NPHY_RFCTRLCORE0GPIO2 B43_PHY_N(0x238) -#define B43_NPHY_RFCTRLCORE0GPIO3 B43_PHY_N(0x239) -#define B43_NPHY_RFCTRLCORE1GPIO0 B43_PHY_N(0x23A) -#define B43_NPHY_RFCTRLCORE1GPIO1 B43_PHY_N(0x23B) -#define B43_NPHY_RFCTRLCORE1GPIO2 B43_PHY_N(0x23C) -#define B43_NPHY_RFCTRLCORE1GPIO3 B43_PHY_N(0x23D) -#define B43_NPHY_BPHYTESTCONTROL B43_PHY_N(0x23E) -/* REV3+ */ -#define B43_NPHY_FORCEFRONT0 B43_PHY_N(0x23F) -#define B43_NPHY_FORCEFRONT1 B43_PHY_N(0x240) -#define B43_NPHY_NORMVARHYSTTH B43_PHY_N(0x241) -#define B43_NPHY_TXCCKERROR B43_PHY_N(0x242) -#define B43_NPHY_AFESEQINITDACGAIN B43_PHY_N(0x243) -#define B43_NPHY_TXANTSWLUT B43_PHY_N(0x244) -#define B43_NPHY_CORECONFIG B43_PHY_N(0x245) -#define B43_NPHY_ANTENNADIVDWELLTIME B43_PHY_N(0x246) -#define B43_NPHY_ANTENNACCKDIVDWELLTIME B43_PHY_N(0x247) -#define B43_NPHY_ANTENNADIVBACKOFFGAIN B43_PHY_N(0x248) -#define B43_NPHY_ANTENNADIVMINGAIN B43_PHY_N(0x249) -#define B43_NPHY_BRDSEL_NORMVARHYSTTH B43_PHY_N(0x24A) -#define B43_NPHY_RXANTSWITCHCTRL B43_PHY_N(0x24B) -#define B43_NPHY_ENERGYDROPTIMEOUTLEN2 B43_PHY_N(0x24C) -#define B43_NPHY_ML_LOG_TXEVM0 B43_PHY_N(0x250) -#define B43_NPHY_ML_LOG_TXEVM1 B43_PHY_N(0x251) -#define B43_NPHY_ML_LOG_TXEVM2 B43_PHY_N(0x252) -#define B43_NPHY_ML_LOG_TXEVM3 B43_PHY_N(0x253) -#define B43_NPHY_ML_LOG_TXEVM4 B43_PHY_N(0x254) -#define B43_NPHY_ML_LOG_TXEVM5 B43_PHY_N(0x255) -#define B43_NPHY_ML_LOG_TXEVM6 B43_PHY_N(0x256) -#define B43_NPHY_ML_LOG_TXEVM7 B43_PHY_N(0x257) -#define B43_NPHY_ML_SCALE_TWEAK B43_PHY_N(0x258) -#define B43_NPHY_MLUA B43_PHY_N(0x259) -#define B43_NPHY_ZFUA B43_PHY_N(0x25A) -#define B43_NPHY_CHANUPSYM01 B43_PHY_N(0x25B) -#define B43_NPHY_CHANUPSYM2 B43_PHY_N(0x25C) -#define B43_NPHY_RXSTRNFILT20NUM00 B43_PHY_N(0x25D) -#define B43_NPHY_RXSTRNFILT20NUM01 B43_PHY_N(0x25E) -#define B43_NPHY_RXSTRNFILT20NUM02 B43_PHY_N(0x25F) -#define B43_NPHY_RXSTRNFILT20DEN00 B43_PHY_N(0x260) -#define B43_NPHY_RXSTRNFILT20DEN01 B43_PHY_N(0x261) -#define B43_NPHY_RXSTRNFILT20NUM10 B43_PHY_N(0x262) -#define B43_NPHY_RXSTRNFILT20NUM11 B43_PHY_N(0x263) -#define B43_NPHY_RXSTRNFILT20NUM12 B43_PHY_N(0x264) -#define B43_NPHY_RXSTRNFILT20DEN10 B43_PHY_N(0x265) -#define B43_NPHY_RXSTRNFILT20DEN11 B43_PHY_N(0x266) -#define B43_NPHY_RXSTRNFILT40NUM00 B43_PHY_N(0x267) -#define B43_NPHY_RXSTRNFILT40NUM01 B43_PHY_N(0x268) -#define B43_NPHY_RXSTRNFILT40NUM02 B43_PHY_N(0x269) -#define B43_NPHY_RXSTRNFILT40DEN00 B43_PHY_N(0x26A) -#define B43_NPHY_RXSTRNFILT40DEN01 B43_PHY_N(0x26B) -#define B43_NPHY_RXSTRNFILT40NUM10 B43_PHY_N(0x26C) -#define B43_NPHY_RXSTRNFILT40NUM11 B43_PHY_N(0x26D) -#define B43_NPHY_RXSTRNFILT40NUM12 B43_PHY_N(0x26E) -#define B43_NPHY_RXSTRNFILT40DEN10 B43_PHY_N(0x26F) -#define B43_NPHY_RXSTRNFILT40DEN11 B43_PHY_N(0x270) -#define B43_NPHY_CRSHIGHPOWTHRESHOLD1 B43_PHY_N(0x271) -#define B43_NPHY_CRSHIGHPOWTHRESHOLD2 B43_PHY_N(0x272) -#define B43_NPHY_CRSHIGHLOWPOWTHRESHOLD B43_PHY_N(0x273) -#define B43_NPHY_CRSHIGHPOWTHRESHOLD1L B43_PHY_N(0x274) -#define B43_NPHY_CRSHIGHPOWTHRESHOLD2L B43_PHY_N(0x275) -#define B43_NPHY_CRSHIGHLOWPOWTHRESHOLDL B43_PHY_N(0x276) -#define B43_NPHY_CRSHIGHPOWTHRESHOLD1U B43_PHY_N(0x277) -#define B43_NPHY_CRSHIGHPOWTHRESHOLD2U B43_PHY_N(0x278) -#define B43_NPHY_CRSHIGHLOWPOWTHRESHOLDU B43_PHY_N(0x279) -#define B43_NPHY_CRSACIDETECTTHRESH B43_PHY_N(0x27A) -#define B43_NPHY_CRSACIDETECTTHRESHL B43_PHY_N(0x27B) -#define B43_NPHY_CRSACIDETECTTHRESHU B43_PHY_N(0x27C) -#define B43_NPHY_CRSMINPOWER0 B43_PHY_N(0x27D) -#define B43_NPHY_CRSMINPOWER1 B43_PHY_N(0x27E) -#define B43_NPHY_CRSMINPOWER2 B43_PHY_N(0x27F) -#define B43_NPHY_CRSMINPOWERL0 B43_PHY_N(0x280) -#define B43_NPHY_CRSMINPOWERL1 B43_PHY_N(0x281) -#define B43_NPHY_CRSMINPOWERL2 B43_PHY_N(0x282) -#define B43_NPHY_CRSMINPOWERU0 B43_PHY_N(0x283) -#define B43_NPHY_CRSMINPOWERU1 B43_PHY_N(0x284) -#define B43_NPHY_CRSMINPOWERU2 B43_PHY_N(0x285) -#define B43_NPHY_STRPARAM B43_PHY_N(0x286) -#define B43_NPHY_STRPARAML B43_PHY_N(0x287) -#define B43_NPHY_STRPARAMU B43_PHY_N(0x288) -#define B43_NPHY_BPHYCRSMINPOWER0 B43_PHY_N(0x289) -#define B43_NPHY_BPHYCRSMINPOWER1 B43_PHY_N(0x28A) -#define B43_NPHY_BPHYCRSMINPOWER2 B43_PHY_N(0x28B) -#define B43_NPHY_BPHYFILTDEN0COEF B43_PHY_N(0x28C) -#define B43_NPHY_BPHYFILTDEN1COEF B43_PHY_N(0x28D) -#define B43_NPHY_BPHYFILTDEN2COEF B43_PHY_N(0x28E) -#define B43_NPHY_BPHYFILTNUM0COEF B43_PHY_N(0x28F) -#define B43_NPHY_BPHYFILTNUM1COEF B43_PHY_N(0x290) -#define B43_NPHY_BPHYFILTNUM2COEF B43_PHY_N(0x291) -#define B43_NPHY_BPHYFILTNUM01COEF2 B43_PHY_N(0x292) -#define B43_NPHY_BPHYFILTBYPASS B43_PHY_N(0x293) -#define B43_NPHY_SGILTRNOFFSET B43_PHY_N(0x294) -#define B43_NPHY_RADAR_T2_MIN B43_PHY_N(0x295) -#define B43_NPHY_TXPWRCTRLDAMPING B43_PHY_N(0x296) -#define B43_NPHY_PAPD_EN0 B43_PHY_N(0x297) /* PAPD Enable0 TBD */ -#define B43_NPHY_EPS_TABLE_ADJ0 B43_PHY_N(0x298) /* EPS Table Adj0 TBD */ -#define B43_NPHY_EPS_OVERRIDEI_0 B43_PHY_N(0x299) -#define B43_NPHY_EPS_OVERRIDEQ_0 B43_PHY_N(0x29A) -#define B43_NPHY_PAPD_EN1 B43_PHY_N(0x29B) /* PAPD Enable1 TBD */ -#define B43_NPHY_EPS_TABLE_ADJ1 B43_PHY_N(0x29C) /* EPS Table Adj1 TBD */ -#define B43_NPHY_EPS_OVERRIDEI_1 B43_PHY_N(0x29D) -#define B43_NPHY_EPS_OVERRIDEQ_1 B43_PHY_N(0x29E) -#define B43_NPHY_PAPD_CAL_ADDRESS B43_PHY_N(0x29F) -#define B43_NPHY_PAPD_CAL_YREFEPSILON B43_PHY_N(0x2A0) -#define B43_NPHY_PAPD_CAL_SETTLE B43_PHY_N(0x2A1) -#define B43_NPHY_PAPD_CAL_CORRELATE B43_PHY_N(0x2A2) -#define B43_NPHY_PAPD_CAL_SHIFTS0 B43_PHY_N(0x2A3) -#define B43_NPHY_PAPD_CAL_SHIFTS1 B43_PHY_N(0x2A4) -#define B43_NPHY_SAMPLE_START_ADDR B43_PHY_N(0x2A5) -#define B43_NPHY_RADAR_ADC_TO_DBM B43_PHY_N(0x2A6) -#define B43_NPHY_REV3_C2_INITGAIN_A B43_PHY_N(0x2A7) -#define B43_NPHY_REV3_C2_INITGAIN_B B43_PHY_N(0x2A8) -#define B43_NPHY_REV3_C2_CLIP_HIGAIN_A B43_PHY_N(0x2A9) -#define B43_NPHY_REV3_C2_CLIP_HIGAIN_B B43_PHY_N(0x2AA) -#define B43_NPHY_REV3_C2_CLIP_MEDGAIN_A B43_PHY_N(0x2AB) -#define B43_NPHY_REV3_C2_CLIP_MEDGAIN_B B43_PHY_N(0x2AC) -#define B43_NPHY_REV3_C2_CLIP_LOGAIN_A B43_PHY_N(0x2AD) -#define B43_NPHY_REV3_C2_CLIP_LOGAIN_B B43_PHY_N(0x2AE) -#define B43_NPHY_REV3_C2_CLIP2_GAIN_A B43_PHY_N(0x2AF) -#define B43_NPHY_REV3_C2_CLIP2_GAIN_B B43_PHY_N(0x2B0) - -#define B43_NPHY_REV7_RF_CTL_MISC_REG3 B43_PHY_N(0x340) -#define B43_NPHY_REV7_RF_CTL_MISC_REG4 B43_PHY_N(0x341) -#define B43_NPHY_REV7_RF_CTL_OVER3 B43_PHY_N(0x342) -#define B43_NPHY_REV7_RF_CTL_OVER4 B43_PHY_N(0x343) -#define B43_NPHY_REV7_RF_CTL_MISC_REG5 B43_PHY_N(0x344) -#define B43_NPHY_REV7_RF_CTL_MISC_REG6 B43_PHY_N(0x345) -#define B43_NPHY_REV7_RF_CTL_OVER5 B43_PHY_N(0x346) -#define B43_NPHY_REV7_RF_CTL_OVER6 B43_PHY_N(0x347) - -#define B43_PHY_B_BBCFG B43_PHY_N_BMODE(0x001) /* BB config */ -#define B43_PHY_B_BBCFG_RSTCCA 0x4000 /* Reset CCA */ -#define B43_PHY_B_BBCFG_RSTRX 0x8000 /* Reset RX */ -#define B43_PHY_B_TEST B43_PHY_N_BMODE(0x00A) - -struct b43_wldev; - -enum b43_nphy_spur_avoid { - B43_SPUR_AVOID_DISABLE, - B43_SPUR_AVOID_AUTO, - B43_SPUR_AVOID_FORCE, -}; - -struct b43_chanspec { - u16 center_freq; - enum nl80211_channel_type channel_type; -}; - -struct b43_phy_n_iq_comp { - s16 a0; - s16 b0; - s16 a1; - s16 b1; -}; - -struct b43_phy_n_rssical_cache { - u16 rssical_radio_regs_2G[2]; - u16 rssical_phy_regs_2G[12]; - - u16 rssical_radio_regs_5G[2]; - u16 rssical_phy_regs_5G[12]; -}; - -struct b43_phy_n_cal_cache { - u16 txcal_radio_regs_2G[8]; - u16 txcal_coeffs_2G[8]; - struct b43_phy_n_iq_comp rxcal_coeffs_2G; - - u16 txcal_radio_regs_5G[8]; - u16 txcal_coeffs_5G[8]; - struct b43_phy_n_iq_comp rxcal_coeffs_5G; -}; - -struct b43_phy_n_txpwrindex { - s8 index; - s8 index_internal; - s8 index_internal_save; - u16 AfectrlOverride; - u16 AfeCtrlDacGain; - u16 rad_gain; - u8 bbmult; - u16 iqcomp_a; - u16 iqcomp_b; - u16 locomp; -}; - -struct b43_phy_n_pwr_ctl_info { - u8 idle_tssi_2g; - u8 idle_tssi_5g; -}; - -struct b43_phy_n { - u8 antsel_type; - u8 cal_orig_pwr_idx[2]; - u8 measure_hold; - u8 phyrxchain; - u8 hw_phyrxchain; - u8 hw_phytxchain; - u8 perical; - u32 deaf_count; - u32 rxcalparams; - bool hang_avoid; - bool mute; - u16 papd_epsilon_offset[2]; - s32 preamble_override; - u32 bb_mult_save; - - bool gain_boost; - bool elna_gain_config; - bool band5g_pwrgain; - bool use_int_tx_iq_lo_cal; - bool lpf_bw_overrode_for_sample_play; - - u8 mphase_cal_phase_id; - u16 mphase_txcal_cmdidx; - u16 mphase_txcal_numcmds; - u16 mphase_txcal_bestcoeffs[11]; - - bool txpwrctrl; - bool pwg_gain_5ghz; - u8 tx_pwr_idx[2]; - s8 tx_power_offset[101]; - u16 adj_pwr_tbl[84]; - u16 txcal_bbmult; - u16 txiqlocal_bestc[11]; - bool txiqlocal_coeffsvalid; - struct b43_phy_n_txpwrindex txpwrindex[2]; - struct b43_phy_n_pwr_ctl_info pwr_ctl_info[2]; - struct b43_chanspec txiqlocal_chanspec; - struct b43_ppr tx_pwr_max_ppr; - u16 tx_pwr_last_recalc_freq; - int tx_pwr_last_recalc_limit; - - u8 txrx_chain; - u16 tx_rx_cal_phy_saveregs[11]; - u16 tx_rx_cal_radio_saveregs[22]; - - u16 rfctrl_intc1_save; - u16 rfctrl_intc2_save; - - u16 classifier_state; - u16 clip_state[2]; - - enum b43_nphy_spur_avoid spur_avoid; - bool aband_spurwar_en; - bool gband_spurwar_en; - - bool ipa2g_on; - struct b43_chanspec iqcal_chanspec_2G; - struct b43_chanspec rssical_chanspec_2G; - - bool ipa5g_on; - struct b43_chanspec iqcal_chanspec_5G; - struct b43_chanspec rssical_chanspec_5G; - - struct b43_phy_n_rssical_cache rssical_cache; - struct b43_phy_n_cal_cache cal_cache; - bool crsminpwr_adjusted; - bool noisevars_adjusted; -}; - - -struct b43_phy_operations; -extern const struct b43_phy_operations b43_phyops_n; - -#endif /* B43_NPHY_H_ */ diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c deleted file mode 100644 index a4ff5e2a42b9..000000000000 --- a/drivers/net/wireless/b43/pio.c +++ /dev/null @@ -1,834 +0,0 @@ -/* - - Broadcom B43 wireless driver - - PIO data transfer - - Copyright (c) 2005-2008 Michael Buesch - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "pio.h" -#include "dma.h" -#include "main.h" -#include "xmit.h" - -#include -#include -#include - - -static u16 generate_cookie(struct b43_pio_txqueue *q, - struct b43_pio_txpacket *pack) -{ - u16 cookie; - - /* Use the upper 4 bits of the cookie as - * PIO controller ID and store the packet index number - * in the lower 12 bits. - * Note that the cookie must never be 0, as this - * is a special value used in RX path. - * It can also not be 0xFFFF because that is special - * for multicast frames. - */ - cookie = (((u16)q->index + 1) << 12); - cookie |= pack->index; - - return cookie; -} - -static -struct b43_pio_txqueue *parse_cookie(struct b43_wldev *dev, - u16 cookie, - struct b43_pio_txpacket **pack) -{ - struct b43_pio *pio = &dev->pio; - struct b43_pio_txqueue *q = NULL; - unsigned int pack_index; - - switch (cookie & 0xF000) { - case 0x1000: - q = pio->tx_queue_AC_BK; - break; - case 0x2000: - q = pio->tx_queue_AC_BE; - break; - case 0x3000: - q = pio->tx_queue_AC_VI; - break; - case 0x4000: - q = pio->tx_queue_AC_VO; - break; - case 0x5000: - q = pio->tx_queue_mcast; - break; - } - if (B43_WARN_ON(!q)) - return NULL; - pack_index = (cookie & 0x0FFF); - if (B43_WARN_ON(pack_index >= ARRAY_SIZE(q->packets))) - return NULL; - *pack = &q->packets[pack_index]; - - return q; -} - -static u16 index_to_pioqueue_base(struct b43_wldev *dev, - unsigned int index) -{ - static const u16 bases[] = { - B43_MMIO_PIO_BASE0, - B43_MMIO_PIO_BASE1, - B43_MMIO_PIO_BASE2, - B43_MMIO_PIO_BASE3, - B43_MMIO_PIO_BASE4, - B43_MMIO_PIO_BASE5, - B43_MMIO_PIO_BASE6, - B43_MMIO_PIO_BASE7, - }; - static const u16 bases_rev11[] = { - B43_MMIO_PIO11_BASE0, - B43_MMIO_PIO11_BASE1, - B43_MMIO_PIO11_BASE2, - B43_MMIO_PIO11_BASE3, - B43_MMIO_PIO11_BASE4, - B43_MMIO_PIO11_BASE5, - }; - - if (dev->dev->core_rev >= 11) { - B43_WARN_ON(index >= ARRAY_SIZE(bases_rev11)); - return bases_rev11[index]; - } - B43_WARN_ON(index >= ARRAY_SIZE(bases)); - return bases[index]; -} - -static u16 pio_txqueue_offset(struct b43_wldev *dev) -{ - if (dev->dev->core_rev >= 11) - return 0x18; - return 0; -} - -static u16 pio_rxqueue_offset(struct b43_wldev *dev) -{ - if (dev->dev->core_rev >= 11) - return 0x38; - return 8; -} - -static struct b43_pio_txqueue *b43_setup_pioqueue_tx(struct b43_wldev *dev, - unsigned int index) -{ - struct b43_pio_txqueue *q; - struct b43_pio_txpacket *p; - unsigned int i; - - q = kzalloc(sizeof(*q), GFP_KERNEL); - if (!q) - return NULL; - q->dev = dev; - q->rev = dev->dev->core_rev; - q->mmio_base = index_to_pioqueue_base(dev, index) + - pio_txqueue_offset(dev); - q->index = index; - - q->free_packet_slots = B43_PIO_MAX_NR_TXPACKETS; - if (q->rev >= 8) { - q->buffer_size = 1920; //FIXME this constant is wrong. - } else { - q->buffer_size = b43_piotx_read16(q, B43_PIO_TXQBUFSIZE); - q->buffer_size -= 80; - } - - INIT_LIST_HEAD(&q->packets_list); - for (i = 0; i < ARRAY_SIZE(q->packets); i++) { - p = &(q->packets[i]); - INIT_LIST_HEAD(&p->list); - p->index = i; - p->queue = q; - list_add(&p->list, &q->packets_list); - } - - return q; -} - -static struct b43_pio_rxqueue *b43_setup_pioqueue_rx(struct b43_wldev *dev, - unsigned int index) -{ - struct b43_pio_rxqueue *q; - - q = kzalloc(sizeof(*q), GFP_KERNEL); - if (!q) - return NULL; - q->dev = dev; - q->rev = dev->dev->core_rev; - q->mmio_base = index_to_pioqueue_base(dev, index) + - pio_rxqueue_offset(dev); - - /* Enable Direct FIFO RX (PIO) on the engine. */ - b43_dma_direct_fifo_rx(dev, index, 1); - - return q; -} - -static void b43_pio_cancel_tx_packets(struct b43_pio_txqueue *q) -{ - struct b43_pio_txpacket *pack; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(q->packets); i++) { - pack = &(q->packets[i]); - if (pack->skb) { - ieee80211_free_txskb(q->dev->wl->hw, pack->skb); - pack->skb = NULL; - } - } -} - -static void b43_destroy_pioqueue_tx(struct b43_pio_txqueue *q, - const char *name) -{ - if (!q) - return; - b43_pio_cancel_tx_packets(q); - kfree(q); -} - -static void b43_destroy_pioqueue_rx(struct b43_pio_rxqueue *q, - const char *name) -{ - if (!q) - return; - kfree(q); -} - -#define destroy_queue_tx(pio, queue) do { \ - b43_destroy_pioqueue_tx((pio)->queue, __stringify(queue)); \ - (pio)->queue = NULL; \ - } while (0) - -#define destroy_queue_rx(pio, queue) do { \ - b43_destroy_pioqueue_rx((pio)->queue, __stringify(queue)); \ - (pio)->queue = NULL; \ - } while (0) - -void b43_pio_free(struct b43_wldev *dev) -{ - struct b43_pio *pio; - - if (!b43_using_pio_transfers(dev)) - return; - pio = &dev->pio; - - destroy_queue_rx(pio, rx_queue); - destroy_queue_tx(pio, tx_queue_mcast); - destroy_queue_tx(pio, tx_queue_AC_VO); - destroy_queue_tx(pio, tx_queue_AC_VI); - destroy_queue_tx(pio, tx_queue_AC_BE); - destroy_queue_tx(pio, tx_queue_AC_BK); -} - -int b43_pio_init(struct b43_wldev *dev) -{ - struct b43_pio *pio = &dev->pio; - int err = -ENOMEM; - - b43_write32(dev, B43_MMIO_MACCTL, b43_read32(dev, B43_MMIO_MACCTL) - & ~B43_MACCTL_BE); - b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_RXPADOFF, 0); - - pio->tx_queue_AC_BK = b43_setup_pioqueue_tx(dev, 0); - if (!pio->tx_queue_AC_BK) - goto out; - - pio->tx_queue_AC_BE = b43_setup_pioqueue_tx(dev, 1); - if (!pio->tx_queue_AC_BE) - goto err_destroy_bk; - - pio->tx_queue_AC_VI = b43_setup_pioqueue_tx(dev, 2); - if (!pio->tx_queue_AC_VI) - goto err_destroy_be; - - pio->tx_queue_AC_VO = b43_setup_pioqueue_tx(dev, 3); - if (!pio->tx_queue_AC_VO) - goto err_destroy_vi; - - pio->tx_queue_mcast = b43_setup_pioqueue_tx(dev, 4); - if (!pio->tx_queue_mcast) - goto err_destroy_vo; - - pio->rx_queue = b43_setup_pioqueue_rx(dev, 0); - if (!pio->rx_queue) - goto err_destroy_mcast; - - b43dbg(dev->wl, "PIO initialized\n"); - err = 0; -out: - return err; - -err_destroy_mcast: - destroy_queue_tx(pio, tx_queue_mcast); -err_destroy_vo: - destroy_queue_tx(pio, tx_queue_AC_VO); -err_destroy_vi: - destroy_queue_tx(pio, tx_queue_AC_VI); -err_destroy_be: - destroy_queue_tx(pio, tx_queue_AC_BE); -err_destroy_bk: - destroy_queue_tx(pio, tx_queue_AC_BK); - return err; -} - -/* Static mapping of mac80211's queues (priorities) to b43 PIO queues. */ -static struct b43_pio_txqueue *select_queue_by_priority(struct b43_wldev *dev, - u8 queue_prio) -{ - struct b43_pio_txqueue *q; - - if (dev->qos_enabled) { - /* 0 = highest priority */ - switch (queue_prio) { - default: - B43_WARN_ON(1); - /* fallthrough */ - case 0: - q = dev->pio.tx_queue_AC_VO; - break; - case 1: - q = dev->pio.tx_queue_AC_VI; - break; - case 2: - q = dev->pio.tx_queue_AC_BE; - break; - case 3: - q = dev->pio.tx_queue_AC_BK; - break; - } - } else - q = dev->pio.tx_queue_AC_BE; - - return q; -} - -static u16 tx_write_2byte_queue(struct b43_pio_txqueue *q, - u16 ctl, - const void *_data, - unsigned int data_len) -{ - struct b43_wldev *dev = q->dev; - struct b43_wl *wl = dev->wl; - const u8 *data = _data; - - ctl |= B43_PIO_TXCTL_WRITELO | B43_PIO_TXCTL_WRITEHI; - b43_piotx_write16(q, B43_PIO_TXCTL, ctl); - - b43_block_write(dev, data, (data_len & ~1), - q->mmio_base + B43_PIO_TXDATA, - sizeof(u16)); - if (data_len & 1) { - u8 *tail = wl->pio_tailspace; - BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 2); - - /* Write the last byte. */ - ctl &= ~B43_PIO_TXCTL_WRITEHI; - b43_piotx_write16(q, B43_PIO_TXCTL, ctl); - tail[0] = data[data_len - 1]; - tail[1] = 0; - b43_block_write(dev, tail, 2, - q->mmio_base + B43_PIO_TXDATA, - sizeof(u16)); - } - - return ctl; -} - -static void pio_tx_frame_2byte_queue(struct b43_pio_txpacket *pack, - const u8 *hdr, unsigned int hdrlen) -{ - struct b43_pio_txqueue *q = pack->queue; - const char *frame = pack->skb->data; - unsigned int frame_len = pack->skb->len; - u16 ctl; - - ctl = b43_piotx_read16(q, B43_PIO_TXCTL); - ctl |= B43_PIO_TXCTL_FREADY; - ctl &= ~B43_PIO_TXCTL_EOF; - - /* Transfer the header data. */ - ctl = tx_write_2byte_queue(q, ctl, hdr, hdrlen); - /* Transfer the frame data. */ - ctl = tx_write_2byte_queue(q, ctl, frame, frame_len); - - ctl |= B43_PIO_TXCTL_EOF; - b43_piotx_write16(q, B43_PIO_TXCTL, ctl); -} - -static u32 tx_write_4byte_queue(struct b43_pio_txqueue *q, - u32 ctl, - const void *_data, - unsigned int data_len) -{ - struct b43_wldev *dev = q->dev; - struct b43_wl *wl = dev->wl; - const u8 *data = _data; - - ctl |= B43_PIO8_TXCTL_0_7 | B43_PIO8_TXCTL_8_15 | - B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_24_31; - b43_piotx_write32(q, B43_PIO8_TXCTL, ctl); - - b43_block_write(dev, data, (data_len & ~3), - q->mmio_base + B43_PIO8_TXDATA, - sizeof(u32)); - if (data_len & 3) { - u8 *tail = wl->pio_tailspace; - BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 4); - - memset(tail, 0, 4); - /* Write the last few bytes. */ - ctl &= ~(B43_PIO8_TXCTL_8_15 | B43_PIO8_TXCTL_16_23 | - B43_PIO8_TXCTL_24_31); - switch (data_len & 3) { - case 3: - ctl |= B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_8_15; - tail[0] = data[data_len - 3]; - tail[1] = data[data_len - 2]; - tail[2] = data[data_len - 1]; - break; - case 2: - ctl |= B43_PIO8_TXCTL_8_15; - tail[0] = data[data_len - 2]; - tail[1] = data[data_len - 1]; - break; - case 1: - tail[0] = data[data_len - 1]; - break; - } - b43_piotx_write32(q, B43_PIO8_TXCTL, ctl); - b43_block_write(dev, tail, 4, - q->mmio_base + B43_PIO8_TXDATA, - sizeof(u32)); - } - - return ctl; -} - -static void pio_tx_frame_4byte_queue(struct b43_pio_txpacket *pack, - const u8 *hdr, unsigned int hdrlen) -{ - struct b43_pio_txqueue *q = pack->queue; - const char *frame = pack->skb->data; - unsigned int frame_len = pack->skb->len; - u32 ctl; - - ctl = b43_piotx_read32(q, B43_PIO8_TXCTL); - ctl |= B43_PIO8_TXCTL_FREADY; - ctl &= ~B43_PIO8_TXCTL_EOF; - - /* Transfer the header data. */ - ctl = tx_write_4byte_queue(q, ctl, hdr, hdrlen); - /* Transfer the frame data. */ - ctl = tx_write_4byte_queue(q, ctl, frame, frame_len); - - ctl |= B43_PIO8_TXCTL_EOF; - b43_piotx_write32(q, B43_PIO_TXCTL, ctl); -} - -static int pio_tx_frame(struct b43_pio_txqueue *q, - struct sk_buff *skb) -{ - struct b43_wldev *dev = q->dev; - struct b43_wl *wl = dev->wl; - struct b43_pio_txpacket *pack; - u16 cookie; - int err; - unsigned int hdrlen; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct b43_txhdr *txhdr = (struct b43_txhdr *)wl->pio_scratchspace; - - B43_WARN_ON(list_empty(&q->packets_list)); - pack = list_entry(q->packets_list.next, - struct b43_pio_txpacket, list); - - cookie = generate_cookie(q, pack); - hdrlen = b43_txhdr_size(dev); - BUILD_BUG_ON(sizeof(wl->pio_scratchspace) < sizeof(struct b43_txhdr)); - B43_WARN_ON(sizeof(wl->pio_scratchspace) < hdrlen); - err = b43_generate_txhdr(dev, (u8 *)txhdr, skb, - info, cookie); - if (err) - return err; - - if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { - /* Tell the firmware about the cookie of the last - * mcast frame, so it can clear the more-data bit in it. */ - b43_shm_write16(dev, B43_SHM_SHARED, - B43_SHM_SH_MCASTCOOKIE, cookie); - } - - pack->skb = skb; - if (q->rev >= 8) - pio_tx_frame_4byte_queue(pack, (const u8 *)txhdr, hdrlen); - else - pio_tx_frame_2byte_queue(pack, (const u8 *)txhdr, hdrlen); - - /* Remove it from the list of available packet slots. - * It will be put back when we receive the status report. */ - list_del(&pack->list); - - /* Update the queue statistics. */ - q->buffer_used += roundup(skb->len + hdrlen, 4); - q->free_packet_slots -= 1; - - return 0; -} - -int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb) -{ - struct b43_pio_txqueue *q; - struct ieee80211_hdr *hdr; - unsigned int hdrlen, total_len; - int err = 0; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - hdr = (struct ieee80211_hdr *)skb->data; - - if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { - /* The multicast queue will be sent after the DTIM. */ - q = dev->pio.tx_queue_mcast; - /* Set the frame More-Data bit. Ucode will clear it - * for us on the last frame. */ - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); - } else { - /* Decide by priority where to put this frame. */ - q = select_queue_by_priority(dev, skb_get_queue_mapping(skb)); - } - - hdrlen = b43_txhdr_size(dev); - total_len = roundup(skb->len + hdrlen, 4); - - if (unlikely(total_len > q->buffer_size)) { - err = -ENOBUFS; - b43dbg(dev->wl, "PIO: TX packet longer than queue.\n"); - goto out; - } - if (unlikely(q->free_packet_slots == 0)) { - err = -ENOBUFS; - b43warn(dev->wl, "PIO: TX packet overflow.\n"); - goto out; - } - B43_WARN_ON(q->buffer_used > q->buffer_size); - - if (total_len > (q->buffer_size - q->buffer_used)) { - /* Not enough memory on the queue. */ - err = -EBUSY; - ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb)); - q->stopped = true; - goto out; - } - - /* Assign the queue number to the ring (if not already done before) - * so TX status handling can use it. The mac80211-queue to b43-queue - * mapping is static, so we don't need to store it per frame. */ - q->queue_prio = skb_get_queue_mapping(skb); - - err = pio_tx_frame(q, skb); - if (unlikely(err == -ENOKEY)) { - /* Drop this packet, as we don't have the encryption key - * anymore and must not transmit it unencrypted. */ - ieee80211_free_txskb(dev->wl->hw, skb); - err = 0; - goto out; - } - if (unlikely(err)) { - b43err(dev->wl, "PIO transmission failure\n"); - goto out; - } - - B43_WARN_ON(q->buffer_used > q->buffer_size); - if (((q->buffer_size - q->buffer_used) < roundup(2 + 2 + 6, 4)) || - (q->free_packet_slots == 0)) { - /* The queue is full. */ - ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb)); - q->stopped = true; - } - -out: - return err; -} - -void b43_pio_handle_txstatus(struct b43_wldev *dev, - const struct b43_txstatus *status) -{ - struct b43_pio_txqueue *q; - struct b43_pio_txpacket *pack = NULL; - unsigned int total_len; - struct ieee80211_tx_info *info; - - q = parse_cookie(dev, status->cookie, &pack); - if (unlikely(!q)) - return; - B43_WARN_ON(!pack); - - info = IEEE80211_SKB_CB(pack->skb); - - b43_fill_txstatus_report(dev, info, status); - - total_len = pack->skb->len + b43_txhdr_size(dev); - total_len = roundup(total_len, 4); - q->buffer_used -= total_len; - q->free_packet_slots += 1; - - ieee80211_tx_status(dev->wl->hw, pack->skb); - pack->skb = NULL; - list_add(&pack->list, &q->packets_list); - - if (q->stopped) { - ieee80211_wake_queue(dev->wl->hw, q->queue_prio); - q->stopped = false; - } -} - -/* Returns whether we should fetch another frame. */ -static bool pio_rx_frame(struct b43_pio_rxqueue *q) -{ - struct b43_wldev *dev = q->dev; - struct b43_wl *wl = dev->wl; - u16 len; - u32 macstat = 0; - unsigned int i, padding; - struct sk_buff *skb; - const char *err_msg = NULL; - struct b43_rxhdr_fw4 *rxhdr = - (struct b43_rxhdr_fw4 *)wl->pio_scratchspace; - size_t rxhdr_size = sizeof(*rxhdr); - - BUILD_BUG_ON(sizeof(wl->pio_scratchspace) < sizeof(*rxhdr)); - switch (dev->fw.hdr_format) { - case B43_FW_HDR_410: - case B43_FW_HDR_351: - rxhdr_size -= sizeof(rxhdr->format_598) - - sizeof(rxhdr->format_351); - break; - case B43_FW_HDR_598: - break; - } - memset(rxhdr, 0, rxhdr_size); - - /* Check if we have data and wait for it to get ready. */ - if (q->rev >= 8) { - u32 ctl; - - ctl = b43_piorx_read32(q, B43_PIO8_RXCTL); - if (!(ctl & B43_PIO8_RXCTL_FRAMERDY)) - return false; - b43_piorx_write32(q, B43_PIO8_RXCTL, - B43_PIO8_RXCTL_FRAMERDY); - for (i = 0; i < 10; i++) { - ctl = b43_piorx_read32(q, B43_PIO8_RXCTL); - if (ctl & B43_PIO8_RXCTL_DATARDY) - goto data_ready; - udelay(10); - } - } else { - u16 ctl; - - ctl = b43_piorx_read16(q, B43_PIO_RXCTL); - if (!(ctl & B43_PIO_RXCTL_FRAMERDY)) - return false; - b43_piorx_write16(q, B43_PIO_RXCTL, - B43_PIO_RXCTL_FRAMERDY); - for (i = 0; i < 10; i++) { - ctl = b43_piorx_read16(q, B43_PIO_RXCTL); - if (ctl & B43_PIO_RXCTL_DATARDY) - goto data_ready; - udelay(10); - } - } - b43dbg(q->dev->wl, "PIO RX timed out\n"); - return true; -data_ready: - - /* Get the preamble (RX header) */ - if (q->rev >= 8) { - b43_block_read(dev, rxhdr, rxhdr_size, - q->mmio_base + B43_PIO8_RXDATA, - sizeof(u32)); - } else { - b43_block_read(dev, rxhdr, rxhdr_size, - q->mmio_base + B43_PIO_RXDATA, - sizeof(u16)); - } - /* Sanity checks. */ - len = le16_to_cpu(rxhdr->frame_len); - if (unlikely(len > 0x700)) { - err_msg = "len > 0x700"; - goto rx_error; - } - if (unlikely(len == 0)) { - err_msg = "len == 0"; - goto rx_error; - } - - switch (dev->fw.hdr_format) { - case B43_FW_HDR_598: - macstat = le32_to_cpu(rxhdr->format_598.mac_status); - break; - case B43_FW_HDR_410: - case B43_FW_HDR_351: - macstat = le32_to_cpu(rxhdr->format_351.mac_status); - break; - } - if (macstat & B43_RX_MAC_FCSERR) { - if (!(q->dev->wl->filter_flags & FIF_FCSFAIL)) { - /* Drop frames with failed FCS. */ - err_msg = "Frame FCS error"; - goto rx_error; - } - } - - /* We always pad 2 bytes, as that's what upstream code expects - * due to the RX-header being 30 bytes. In case the frame is - * unaligned, we pad another 2 bytes. */ - padding = (macstat & B43_RX_MAC_PADDING) ? 2 : 0; - skb = dev_alloc_skb(len + padding + 2); - if (unlikely(!skb)) { - err_msg = "Out of memory"; - goto rx_error; - } - skb_reserve(skb, 2); - skb_put(skb, len + padding); - if (q->rev >= 8) { - b43_block_read(dev, skb->data + padding, (len & ~3), - q->mmio_base + B43_PIO8_RXDATA, - sizeof(u32)); - if (len & 3) { - u8 *tail = wl->pio_tailspace; - BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 4); - - /* Read the last few bytes. */ - b43_block_read(dev, tail, 4, - q->mmio_base + B43_PIO8_RXDATA, - sizeof(u32)); - switch (len & 3) { - case 3: - skb->data[len + padding - 3] = tail[0]; - skb->data[len + padding - 2] = tail[1]; - skb->data[len + padding - 1] = tail[2]; - break; - case 2: - skb->data[len + padding - 2] = tail[0]; - skb->data[len + padding - 1] = tail[1]; - break; - case 1: - skb->data[len + padding - 1] = tail[0]; - break; - } - } - } else { - b43_block_read(dev, skb->data + padding, (len & ~1), - q->mmio_base + B43_PIO_RXDATA, - sizeof(u16)); - if (len & 1) { - u8 *tail = wl->pio_tailspace; - BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 2); - - /* Read the last byte. */ - b43_block_read(dev, tail, 2, - q->mmio_base + B43_PIO_RXDATA, - sizeof(u16)); - skb->data[len + padding - 1] = tail[0]; - } - } - - b43_rx(q->dev, skb, rxhdr); - - return true; - -rx_error: - if (err_msg) - b43dbg(q->dev->wl, "PIO RX error: %s\n", err_msg); - if (q->rev >= 8) - b43_piorx_write32(q, B43_PIO8_RXCTL, B43_PIO8_RXCTL_DATARDY); - else - b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_DATARDY); - - return true; -} - -void b43_pio_rx(struct b43_pio_rxqueue *q) -{ - unsigned int count = 0; - bool stop; - - while (1) { - stop = (pio_rx_frame(q) == 0); - if (stop) - break; - cond_resched(); - if (WARN_ON_ONCE(++count > 10000)) - break; - } -} - -static void b43_pio_tx_suspend_queue(struct b43_pio_txqueue *q) -{ - if (q->rev >= 8) { - b43_piotx_write32(q, B43_PIO8_TXCTL, - b43_piotx_read32(q, B43_PIO8_TXCTL) - | B43_PIO8_TXCTL_SUSPREQ); - } else { - b43_piotx_write16(q, B43_PIO_TXCTL, - b43_piotx_read16(q, B43_PIO_TXCTL) - | B43_PIO_TXCTL_SUSPREQ); - } -} - -static void b43_pio_tx_resume_queue(struct b43_pio_txqueue *q) -{ - if (q->rev >= 8) { - b43_piotx_write32(q, B43_PIO8_TXCTL, - b43_piotx_read32(q, B43_PIO8_TXCTL) - & ~B43_PIO8_TXCTL_SUSPREQ); - } else { - b43_piotx_write16(q, B43_PIO_TXCTL, - b43_piotx_read16(q, B43_PIO_TXCTL) - & ~B43_PIO_TXCTL_SUSPREQ); - } -} - -void b43_pio_tx_suspend(struct b43_wldev *dev) -{ - b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); - b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_BK); - b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_BE); - b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_VI); - b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_VO); - b43_pio_tx_suspend_queue(dev->pio.tx_queue_mcast); -} - -void b43_pio_tx_resume(struct b43_wldev *dev) -{ - b43_pio_tx_resume_queue(dev->pio.tx_queue_mcast); - b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_VO); - b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_VI); - b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_BE); - b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_BK); - b43_power_saving_ctl_bits(dev, 0); -} diff --git a/drivers/net/wireless/b43/pio.h b/drivers/net/wireless/b43/pio.h deleted file mode 100644 index 1e516147424f..000000000000 --- a/drivers/net/wireless/b43/pio.h +++ /dev/null @@ -1,165 +0,0 @@ -#ifndef B43_PIO_H_ -#define B43_PIO_H_ - -#include "b43.h" - -#include -#include -#include -#include - - -/*** Registers for PIO queues up to revision 7. ***/ -/* TX queue. */ -#define B43_PIO_TXCTL 0x00 -#define B43_PIO_TXCTL_WRITELO 0x0001 -#define B43_PIO_TXCTL_WRITEHI 0x0002 -#define B43_PIO_TXCTL_EOF 0x0004 -#define B43_PIO_TXCTL_FREADY 0x0008 -#define B43_PIO_TXCTL_FLUSHREQ 0x0020 -#define B43_PIO_TXCTL_FLUSHPEND 0x0040 -#define B43_PIO_TXCTL_SUSPREQ 0x0080 -#define B43_PIO_TXCTL_QSUSP 0x0100 -#define B43_PIO_TXCTL_COMMCNT 0xFC00 -#define B43_PIO_TXCTL_COMMCNT_SHIFT 10 -#define B43_PIO_TXDATA 0x02 -#define B43_PIO_TXQBUFSIZE 0x04 -/* RX queue. */ -#define B43_PIO_RXCTL 0x00 -#define B43_PIO_RXCTL_FRAMERDY 0x0001 -#define B43_PIO_RXCTL_DATARDY 0x0002 -#define B43_PIO_RXDATA 0x02 - -/*** Registers for PIO queues revision 8 and later. ***/ -/* TX queue */ -#define B43_PIO8_TXCTL 0x00 -#define B43_PIO8_TXCTL_0_7 0x00000001 -#define B43_PIO8_TXCTL_8_15 0x00000002 -#define B43_PIO8_TXCTL_16_23 0x00000004 -#define B43_PIO8_TXCTL_24_31 0x00000008 -#define B43_PIO8_TXCTL_EOF 0x00000010 -#define B43_PIO8_TXCTL_FREADY 0x00000080 -#define B43_PIO8_TXCTL_SUSPREQ 0x00000100 -#define B43_PIO8_TXCTL_QSUSP 0x00000200 -#define B43_PIO8_TXCTL_FLUSHREQ 0x00000400 -#define B43_PIO8_TXCTL_FLUSHPEND 0x00000800 -#define B43_PIO8_TXDATA 0x04 -/* RX queue */ -#define B43_PIO8_RXCTL 0x00 -#define B43_PIO8_RXCTL_FRAMERDY 0x00000001 -#define B43_PIO8_RXCTL_DATARDY 0x00000002 -#define B43_PIO8_RXDATA 0x04 - - -/* The maximum number of TX-packets the HW can handle. */ -#define B43_PIO_MAX_NR_TXPACKETS 32 - - -struct b43_pio_txpacket { - /* Pointer to the TX queue we belong to. */ - struct b43_pio_txqueue *queue; - /* The TX data packet. */ - struct sk_buff *skb; - /* Index in the (struct b43_pio_txqueue)->packets array. */ - u8 index; - - struct list_head list; -}; - -struct b43_pio_txqueue { - struct b43_wldev *dev; - u16 mmio_base; - - /* The device queue buffer size in bytes. */ - u16 buffer_size; - /* The number of used bytes in the device queue buffer. */ - u16 buffer_used; - /* The number of packets that can still get queued. - * This is decremented on queueing a packet and incremented - * after receiving the transmit status. */ - u16 free_packet_slots; - - /* True, if the mac80211 queue was stopped due to overflow at TX. */ - bool stopped; - /* Our b43 queue index number */ - u8 index; - /* The mac80211 QoS queue priority. */ - u8 queue_prio; - - /* Buffer for TX packet meta data. */ - struct b43_pio_txpacket packets[B43_PIO_MAX_NR_TXPACKETS]; - struct list_head packets_list; - - /* Shortcut to the 802.11 core revision. This is to - * avoid horrible pointer dereferencing in the fastpaths. */ - u8 rev; -}; - -struct b43_pio_rxqueue { - struct b43_wldev *dev; - u16 mmio_base; - - /* Shortcut to the 802.11 core revision. This is to - * avoid horrible pointer dereferencing in the fastpaths. */ - u8 rev; -}; - - -static inline u16 b43_piotx_read16(struct b43_pio_txqueue *q, u16 offset) -{ - return b43_read16(q->dev, q->mmio_base + offset); -} - -static inline u32 b43_piotx_read32(struct b43_pio_txqueue *q, u16 offset) -{ - return b43_read32(q->dev, q->mmio_base + offset); -} - -static inline void b43_piotx_write16(struct b43_pio_txqueue *q, - u16 offset, u16 value) -{ - b43_write16(q->dev, q->mmio_base + offset, value); -} - -static inline void b43_piotx_write32(struct b43_pio_txqueue *q, - u16 offset, u32 value) -{ - b43_write32(q->dev, q->mmio_base + offset, value); -} - - -static inline u16 b43_piorx_read16(struct b43_pio_rxqueue *q, u16 offset) -{ - return b43_read16(q->dev, q->mmio_base + offset); -} - -static inline u32 b43_piorx_read32(struct b43_pio_rxqueue *q, u16 offset) -{ - return b43_read32(q->dev, q->mmio_base + offset); -} - -static inline void b43_piorx_write16(struct b43_pio_rxqueue *q, - u16 offset, u16 value) -{ - b43_write16(q->dev, q->mmio_base + offset, value); -} - -static inline void b43_piorx_write32(struct b43_pio_rxqueue *q, - u16 offset, u32 value) -{ - b43_write32(q->dev, q->mmio_base + offset, value); -} - - -int b43_pio_init(struct b43_wldev *dev); -void b43_pio_free(struct b43_wldev *dev); - -int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb); -void b43_pio_handle_txstatus(struct b43_wldev *dev, - const struct b43_txstatus *status); -void b43_pio_rx(struct b43_pio_rxqueue *q); - -void b43_pio_tx_suspend(struct b43_wldev *dev); -void b43_pio_tx_resume(struct b43_wldev *dev); - -#endif /* B43_PIO_H_ */ diff --git a/drivers/net/wireless/b43/ppr.c b/drivers/net/wireless/b43/ppr.c deleted file mode 100644 index 9a770279c415..000000000000 --- a/drivers/net/wireless/b43/ppr.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Broadcom B43 wireless driver - * PPR (Power Per Rate) management - * - * Copyright (c) 2014 RafaÅ‚ MiÅ‚ecki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "ppr.h" -#include "b43.h" - -#define ppr_for_each_entry(ppr, i, entry) \ - for (i = 0, entry = &(ppr)->__all_rates[i]; \ - i < B43_PPR_RATES_NUM; \ - i++, entry++) - -void b43_ppr_clear(struct b43_wldev *dev, struct b43_ppr *ppr) -{ - memset(ppr, 0, sizeof(*ppr)); - - /* Compile-time PPR check */ - BUILD_BUG_ON(sizeof(struct b43_ppr) != B43_PPR_RATES_NUM * sizeof(u8)); -} - -void b43_ppr_add(struct b43_wldev *dev, struct b43_ppr *ppr, int diff) -{ - int i; - u8 *rate; - - ppr_for_each_entry(ppr, i, rate) { - *rate = clamp_val(*rate + diff, 0, 127); - } -} - -void b43_ppr_apply_max(struct b43_wldev *dev, struct b43_ppr *ppr, u8 max) -{ - int i; - u8 *rate; - - ppr_for_each_entry(ppr, i, rate) { - *rate = min(*rate, max); - } -} - -void b43_ppr_apply_min(struct b43_wldev *dev, struct b43_ppr *ppr, u8 min) -{ - int i; - u8 *rate; - - ppr_for_each_entry(ppr, i, rate) { - *rate = max(*rate, min); - } -} - -u8 b43_ppr_get_max(struct b43_wldev *dev, struct b43_ppr *ppr) -{ - u8 res = 0; - int i; - u8 *rate; - - ppr_for_each_entry(ppr, i, rate) { - res = max(*rate, res); - } - - return res; -} - -bool b43_ppr_load_max_from_sprom(struct b43_wldev *dev, struct b43_ppr *ppr, - enum b43_band band) -{ - struct b43_ppr_rates *rates = &ppr->rates; - struct ssb_sprom *sprom = dev->dev->bus_sprom; - struct b43_phy *phy = &dev->phy; - u8 maxpwr, off; - u32 sprom_ofdm_po; - u16 *sprom_mcs_po; - u8 extra_cdd_po, extra_stbc_po; - int i; - - switch (band) { - case B43_BAND_2G: - maxpwr = min(sprom->core_pwr_info[0].maxpwr_2g, - sprom->core_pwr_info[1].maxpwr_2g); - sprom_ofdm_po = sprom->ofdm2gpo; - sprom_mcs_po = sprom->mcs2gpo; - extra_cdd_po = (sprom->cddpo >> 0) & 0xf; - extra_stbc_po = (sprom->stbcpo >> 0) & 0xf; - break; - case B43_BAND_5G_LO: - maxpwr = min(sprom->core_pwr_info[0].maxpwr_5gl, - sprom->core_pwr_info[1].maxpwr_5gl); - sprom_ofdm_po = sprom->ofdm5glpo; - sprom_mcs_po = sprom->mcs5glpo; - extra_cdd_po = (sprom->cddpo >> 8) & 0xf; - extra_stbc_po = (sprom->stbcpo >> 8) & 0xf; - break; - case B43_BAND_5G_MI: - maxpwr = min(sprom->core_pwr_info[0].maxpwr_5g, - sprom->core_pwr_info[1].maxpwr_5g); - sprom_ofdm_po = sprom->ofdm5gpo; - sprom_mcs_po = sprom->mcs5gpo; - extra_cdd_po = (sprom->cddpo >> 4) & 0xf; - extra_stbc_po = (sprom->stbcpo >> 4) & 0xf; - break; - case B43_BAND_5G_HI: - maxpwr = min(sprom->core_pwr_info[0].maxpwr_5gh, - sprom->core_pwr_info[1].maxpwr_5gh); - sprom_ofdm_po = sprom->ofdm5ghpo; - sprom_mcs_po = sprom->mcs5ghpo; - extra_cdd_po = (sprom->cddpo >> 12) & 0xf; - extra_stbc_po = (sprom->stbcpo >> 12) & 0xf; - break; - default: - WARN_ON_ONCE(1); - return false; - } - - if (band == B43_BAND_2G) { - for (i = 0; i < 4; i++) { - off = ((sprom->cck2gpo >> (i * 4)) & 0xf) * 2; - rates->cck[i] = maxpwr - off; - } - } - - /* OFDM */ - for (i = 0; i < 8; i++) { - off = ((sprom_ofdm_po >> (i * 4)) & 0xf) * 2; - rates->ofdm[i] = maxpwr - off; - } - - /* MCS 20 SISO */ - rates->mcs_20[0] = rates->ofdm[0]; - rates->mcs_20[1] = rates->ofdm[2]; - rates->mcs_20[2] = rates->ofdm[3]; - rates->mcs_20[3] = rates->ofdm[4]; - rates->mcs_20[4] = rates->ofdm[5]; - rates->mcs_20[5] = rates->ofdm[6]; - rates->mcs_20[6] = rates->ofdm[7]; - rates->mcs_20[7] = rates->ofdm[7]; - - /* MCS 20 CDD */ - for (i = 0; i < 4; i++) { - off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2; - rates->mcs_20_cdd[i] = maxpwr - off; - if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) - rates->mcs_20_cdd[i] -= extra_cdd_po; - } - for (i = 0; i < 4; i++) { - off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2; - rates->mcs_20_cdd[4 + i] = maxpwr - off; - if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) - rates->mcs_20_cdd[4 + i] -= extra_cdd_po; - } - - /* OFDM 20 CDD */ - rates->ofdm_20_cdd[0] = rates->mcs_20_cdd[0]; - rates->ofdm_20_cdd[1] = rates->mcs_20_cdd[0]; - rates->ofdm_20_cdd[2] = rates->mcs_20_cdd[1]; - rates->ofdm_20_cdd[3] = rates->mcs_20_cdd[2]; - rates->ofdm_20_cdd[4] = rates->mcs_20_cdd[3]; - rates->ofdm_20_cdd[5] = rates->mcs_20_cdd[4]; - rates->ofdm_20_cdd[6] = rates->mcs_20_cdd[5]; - rates->ofdm_20_cdd[7] = rates->mcs_20_cdd[6]; - - /* MCS 20 STBC */ - for (i = 0; i < 4; i++) { - off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2; - rates->mcs_20_stbc[i] = maxpwr - off; - if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) - rates->mcs_20_stbc[i] -= extra_stbc_po; - } - for (i = 0; i < 4; i++) { - off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2; - rates->mcs_20_stbc[4 + i] = maxpwr - off; - if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) - rates->mcs_20_stbc[4 + i] -= extra_stbc_po; - } - - /* MCS 20 SDM */ - for (i = 0; i < 4; i++) { - off = ((sprom_mcs_po[2] >> (i * 4)) & 0xf) * 2; - rates->mcs_20_sdm[i] = maxpwr - off; - } - for (i = 0; i < 4; i++) { - off = ((sprom_mcs_po[3] >> (i * 4)) & 0xf) * 2; - rates->mcs_20_sdm[4 + i] = maxpwr - off; - } - - return true; -} diff --git a/drivers/net/wireless/b43/ppr.h b/drivers/net/wireless/b43/ppr.h deleted file mode 100644 index 24d7447e9f01..000000000000 --- a/drivers/net/wireless/b43/ppr.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef LINUX_B43_PPR_H_ -#define LINUX_B43_PPR_H_ - -#include - -#define B43_PPR_CCK_RATES_NUM 4 -#define B43_PPR_OFDM_RATES_NUM 8 -#define B43_PPR_MCS_RATES_NUM 8 - -#define B43_PPR_RATES_NUM (B43_PPR_CCK_RATES_NUM + \ - B43_PPR_OFDM_RATES_NUM * 2 + \ - B43_PPR_MCS_RATES_NUM * 4) - -struct b43_ppr_rates { - u8 cck[B43_PPR_CCK_RATES_NUM]; - u8 ofdm[B43_PPR_OFDM_RATES_NUM]; - u8 ofdm_20_cdd[B43_PPR_OFDM_RATES_NUM]; - u8 mcs_20[B43_PPR_MCS_RATES_NUM]; /* SISO */ - u8 mcs_20_cdd[B43_PPR_MCS_RATES_NUM]; - u8 mcs_20_stbc[B43_PPR_MCS_RATES_NUM]; - u8 mcs_20_sdm[B43_PPR_MCS_RATES_NUM]; -}; - -struct b43_ppr { - /* All powers are in qdbm (Q5.2) */ - union { - u8 __all_rates[B43_PPR_RATES_NUM]; - struct b43_ppr_rates rates; - }; -}; - -struct b43_wldev; -enum b43_band; - -void b43_ppr_clear(struct b43_wldev *dev, struct b43_ppr *ppr); - -void b43_ppr_add(struct b43_wldev *dev, struct b43_ppr *ppr, int diff); -void b43_ppr_apply_max(struct b43_wldev *dev, struct b43_ppr *ppr, u8 max); -void b43_ppr_apply_min(struct b43_wldev *dev, struct b43_ppr *ppr, u8 min); -u8 b43_ppr_get_max(struct b43_wldev *dev, struct b43_ppr *ppr); - -bool b43_ppr_load_max_from_sprom(struct b43_wldev *dev, struct b43_ppr *ppr, - enum b43_band band); - -#endif /* LINUX_B43_PPR_H_ */ diff --git a/drivers/net/wireless/b43/radio_2055.c b/drivers/net/wireless/b43/radio_2055.c deleted file mode 100644 index 5289a18ddd8c..000000000000 --- a/drivers/net/wireless/b43/radio_2055.c +++ /dev/null @@ -1,1335 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11n PHY and radio device data tables - - Copyright (c) 2008 Michael Buesch - Copyright (c) 2010 RafaÅ‚ MiÅ‚ecki - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "radio_2055.h" -#include "phy_common.h" - -struct b2055_inittab_entry { - /* Value to write if we use the 5GHz band. */ - u16 ghz5; - /* Value to write if we use the 2.4GHz band. */ - u16 ghz2; - /* Flags */ - u8 flags; -#define B2055_INITTAB_ENTRY_OK 0x01 -#define B2055_INITTAB_UPLOAD 0x02 -}; -#define UPLOAD .flags = B2055_INITTAB_ENTRY_OK | B2055_INITTAB_UPLOAD -#define NOUPLOAD .flags = B2055_INITTAB_ENTRY_OK - -static const struct b2055_inittab_entry b2055_inittab [] = { - [B2055_SP_PINPD] = { .ghz5 = 0x0080, .ghz2 = 0x0080, NOUPLOAD, }, - [B2055_C1_SP_RSSI] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C1_SP_PDMISC] = { .ghz5 = 0x0027, .ghz2 = 0x0027, NOUPLOAD, }, - [B2055_C2_SP_RSSI] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C2_SP_PDMISC] = { .ghz5 = 0x0027, .ghz2 = 0x0027, NOUPLOAD, }, - [B2055_C1_SP_RXGC1] = { .ghz5 = 0x007F, .ghz2 = 0x007F, UPLOAD, }, - [B2055_C1_SP_RXGC2] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2055_C2_SP_RXGC1] = { .ghz5 = 0x007F, .ghz2 = 0x007F, UPLOAD, }, - [B2055_C2_SP_RXGC2] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2055_C1_SP_LPFBWSEL] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2055_C2_SP_LPFBWSEL] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2055_C1_SP_TXGC1] = { .ghz5 = 0x004F, .ghz2 = 0x004F, UPLOAD, }, - [B2055_C1_SP_TXGC2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, - [B2055_C2_SP_TXGC1] = { .ghz5 = 0x004F, .ghz2 = 0x004F, UPLOAD, }, - [B2055_C2_SP_TXGC2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, - [B2055_MASTER1] = { .ghz5 = 0x00D0, .ghz2 = 0x00D0, NOUPLOAD, }, - [B2055_MASTER2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2055_PD_LGEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_PD_PLLTS] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, - [B2055_C1_PD_LGBUF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C1_PD_TX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C1_PD_RXTX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C1_PD_RSSIMISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C2_PD_LGBUF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C2_PD_TX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C2_PD_RXTX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C2_PD_RSSIMISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_PWRDET_LGEN] = { .ghz5 = 0x00C0, .ghz2 = 0x00C0, NOUPLOAD, }, - [B2055_C1_PWRDET_LGBUF] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, - [B2055_C1_PWRDET_RXTX] = { .ghz5 = 0x00C0, .ghz2 = 0x00C0, NOUPLOAD, }, - [B2055_C2_PWRDET_LGBUF] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, - [B2055_C2_PWRDET_RXTX] = { .ghz5 = 0x00C0, .ghz2 = 0x00C0, NOUPLOAD, }, - [B2055_RRCCAL_CS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_RRCCAL_NOPTSEL] = { .ghz5 = 0x002C, .ghz2 = 0x002C, NOUPLOAD, }, - [B2055_CAL_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_CAL_COUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_CAL_COUT2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_CAL_CVARCTL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_CAL_RVARCTL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_CAL_LPOCTL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_CAL_TS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_CAL_RCCALRTS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_CAL_RCALRTS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_PADDRV] = { .ghz5 = 0x00A4, .ghz2 = 0x00A4, NOUPLOAD, }, - [B2055_XOCTL1] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, - [B2055_XOCTL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_XOREGUL] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, - [B2055_XOMISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_PLL_LFC1] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, - [B2055_PLL_CALVTH] = { .ghz5 = 0x0087, .ghz2 = 0x0087, NOUPLOAD, }, - [B2055_PLL_LFC2] = { .ghz5 = 0x0009, .ghz2 = 0x0009, NOUPLOAD, }, - [B2055_PLL_REF] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2055_PLL_LFR1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2055_PLL_PFDCP] = { .ghz5 = 0x0018, .ghz2 = 0x0018, UPLOAD, }, - [B2055_PLL_IDAC_CPOPAMP] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2055_PLL_CPREG] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, - [B2055_PLL_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2055_RF_PLLMOD0] = { .ghz5 = 0x009E, .ghz2 = 0x009E, NOUPLOAD, }, - [B2055_RF_PLLMOD1] = { .ghz5 = 0x0009, .ghz2 = 0x0009, NOUPLOAD, }, - [B2055_RF_MMDIDAC1] = { .ghz5 = 0x00C8, .ghz2 = 0x00C8, UPLOAD, }, - [B2055_RF_MMDIDAC0] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2055_RF_MMDSP] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_VCO_CAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_VCO_CAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_VCO_CAL3] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2055_VCO_CAL4] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2055_VCO_CAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, - [B2055_VCO_CAL6] = { .ghz5 = 0x003E, .ghz2 = 0x003E, NOUPLOAD, }, - [B2055_VCO_CAL7] = { .ghz5 = 0x003E, .ghz2 = 0x003E, NOUPLOAD, }, - [B2055_VCO_CAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2055_VCO_CAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2055_VCO_CAL10] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2055_VCO_CAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2055_VCO_CAL12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_VCO_CAL13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_VCO_CAL14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_VCO_CAL15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_VCO_CAL16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_VCO_KVCO] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2055_VCO_CAPTAIL] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2055_VCO_IDACVCO] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2055_VCO_REG] = { .ghz5 = 0x0084, .ghz2 = 0x0084, UPLOAD, }, - [B2055_PLL_RFVTH] = { .ghz5 = 0x00C3, .ghz2 = 0x00C3, NOUPLOAD, }, - [B2055_LGBUF_CENBUF] = { .ghz5 = 0x008F, .ghz2 = 0x008F, NOUPLOAD, }, - [B2055_LGEN_TUNE1] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, - [B2055_LGEN_TUNE2] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, - [B2055_LGEN_IDAC1] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2055_LGEN_IDAC2] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2055_LGEN_BIASC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_LGEN_BIASIDAC] = { .ghz5 = 0x00CC, .ghz2 = 0x00CC, NOUPLOAD, }, - [B2055_LGEN_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2055_LGEN_DIV] = { .ghz5 = 0x0080, .ghz2 = 0x0080, NOUPLOAD, }, - [B2055_LGEN_SPARE2] = { .ghz5 = 0x0080, .ghz2 = 0x0080, NOUPLOAD, }, - [B2055_C1_LGBUF_ATUNE] = { .ghz5 = 0x00F8, .ghz2 = 0x00F8, NOUPLOAD, }, - [B2055_C1_LGBUF_GTUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2055_C1_LGBUF_DIV] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2055_C1_LGBUF_AIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0008, UPLOAD, }, - [B2055_C1_LGBUF_GIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2055_C1_LGBUF_IDACFO] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C1_LGBUF_SPARE] = { .ghz5 = 0x0001, .ghz2 = 0x0001, UPLOAD, }, - [B2055_C1_RX_RFSPC1] = { .ghz5 = 0x008A, .ghz2 = 0x008A, NOUPLOAD, }, - [B2055_C1_RX_RFR1] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2055_C1_RX_RFR2] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, - [B2055_C1_RX_RFRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2055_C1_RX_BB_BLCMP] = { .ghz5 = 0x00A0, .ghz2 = 0x00A0, NOUPLOAD, }, - [B2055_C1_RX_BB_LPF] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, - [B2055_C1_RX_BB_MIDACHP] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, - [B2055_C1_RX_BB_VGA1IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, - [B2055_C1_RX_BB_VGA2IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, - [B2055_C1_RX_BB_VGA3IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, - [B2055_C1_RX_BB_BUFOCTL] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, - [B2055_C1_RX_BB_RCCALCTL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2055_C1_RX_BB_RSSICTL1] = { .ghz5 = 0x006A, .ghz2 = 0x006A, UPLOAD, }, - [B2055_C1_RX_BB_RSSICTL2] = { .ghz5 = 0x00AB, .ghz2 = 0x00AB, UPLOAD, }, - [B2055_C1_RX_BB_RSSICTL3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, UPLOAD, }, - [B2055_C1_RX_BB_RSSICTL4] = { .ghz5 = 0x00C1, .ghz2 = 0x00C1, UPLOAD, }, - [B2055_C1_RX_BB_RSSICTL5] = { .ghz5 = 0x00AA, .ghz2 = 0x00AA, UPLOAD, }, - [B2055_C1_RX_BB_REG] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, - [B2055_C1_RX_BB_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C1_RX_TXBBRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2055_C1_TX_RF_SPGA] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2055_C1_TX_RF_SPAD] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2055_C1_TX_RF_CNTPGA1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2055_C1_TX_RF_CNTPAD1] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2055_C1_TX_RF_PGAIDAC] = { .ghz5 = 0x0097, .ghz2 = 0x0097, UPLOAD, }, - [B2055_C1_TX_PGAPADTN] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2055_C1_TX_PADIDAC1] = { .ghz5 = 0x0014, .ghz2 = 0x0014, UPLOAD, }, - [B2055_C1_TX_PADIDAC2] = { .ghz5 = 0x0033, .ghz2 = 0x0033, NOUPLOAD, }, - [B2055_C1_TX_MXBGTRIM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2055_C1_TX_RF_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2055_C1_TX_RF_PADTSSI1] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, - [B2055_C1_TX_RF_PADTSSI2] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, - [B2055_C1_TX_RF_SPARE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, - [B2055_C1_TX_RF_IQCAL1] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, - [B2055_C1_TX_RF_IQCAL2] = { .ghz5 = 0x00A4, .ghz2 = 0x00A4, NOUPLOAD, }, - [B2055_C1_TXBB_RCCAL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2055_C1_TXBB_LPF1] = { .ghz5 = 0x0028, .ghz2 = 0x0028, NOUPLOAD, }, - [B2055_C1_TX_VOSCNCL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C1_TX_LPF_MXGMIDAC] = { .ghz5 = 0x004A, .ghz2 = 0x004A, NOUPLOAD, }, - [B2055_C1_TX_BB_MXGM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C2_LGBUF_ATUNE] = { .ghz5 = 0x00F8, .ghz2 = 0x00F8, NOUPLOAD, }, - [B2055_C2_LGBUF_GTUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2055_C2_LGBUF_DIV] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2055_C2_LGBUF_AIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0008, UPLOAD, }, - [B2055_C2_LGBUF_GIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2055_C2_LGBUF_IDACFO] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C2_LGBUF_SPARE] = { .ghz5 = 0x0001, .ghz2 = 0x0001, UPLOAD, }, - [B2055_C2_RX_RFSPC1] = { .ghz5 = 0x008A, .ghz2 = 0x008A, NOUPLOAD, }, - [B2055_C2_RX_RFR1] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2055_C2_RX_RFR2] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, - [B2055_C2_RX_RFRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2055_C2_RX_BB_BLCMP] = { .ghz5 = 0x00A0, .ghz2 = 0x00A0, NOUPLOAD, }, - [B2055_C2_RX_BB_LPF] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, - [B2055_C2_RX_BB_MIDACHP] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, - [B2055_C2_RX_BB_VGA1IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, - [B2055_C2_RX_BB_VGA2IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, - [B2055_C2_RX_BB_VGA3IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, - [B2055_C2_RX_BB_BUFOCTL] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, - [B2055_C2_RX_BB_RCCALCTL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2055_C2_RX_BB_RSSICTL1] = { .ghz5 = 0x006A, .ghz2 = 0x006A, UPLOAD, }, - [B2055_C2_RX_BB_RSSICTL2] = { .ghz5 = 0x00AB, .ghz2 = 0x00AB, UPLOAD, }, - [B2055_C2_RX_BB_RSSICTL3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, UPLOAD, }, - [B2055_C2_RX_BB_RSSICTL4] = { .ghz5 = 0x00C1, .ghz2 = 0x00C1, UPLOAD, }, - [B2055_C2_RX_BB_RSSICTL5] = { .ghz5 = 0x00AA, .ghz2 = 0x00AA, UPLOAD, }, - [B2055_C2_RX_BB_REG] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, - [B2055_C2_RX_BB_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C2_RX_TXBBRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2055_C2_TX_RF_SPGA] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2055_C2_TX_RF_SPAD] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2055_C2_TX_RF_CNTPGA1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2055_C2_TX_RF_CNTPAD1] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2055_C2_TX_RF_PGAIDAC] = { .ghz5 = 0x0097, .ghz2 = 0x0097, UPLOAD, }, - [B2055_C2_TX_PGAPADTN] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2055_C2_TX_PADIDAC1] = { .ghz5 = 0x0014, .ghz2 = 0x0014, UPLOAD, }, - [B2055_C2_TX_PADIDAC2] = { .ghz5 = 0x0033, .ghz2 = 0x0033, NOUPLOAD, }, - [B2055_C2_TX_MXBGTRIM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2055_C2_TX_RF_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2055_C2_TX_RF_PADTSSI1] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, - [B2055_C2_TX_RF_PADTSSI2] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, - [B2055_C2_TX_RF_SPARE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, - [B2055_C2_TX_RF_IQCAL1] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, - [B2055_C2_TX_RF_IQCAL2] = { .ghz5 = 0x00A4, .ghz2 = 0x00A4, NOUPLOAD, }, - [B2055_C2_TXBB_RCCAL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2055_C2_TXBB_LPF1] = { .ghz5 = 0x0028, .ghz2 = 0x0028, NOUPLOAD, }, - [B2055_C2_TX_VOSCNCL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C2_TX_LPF_MXGMIDAC] = { .ghz5 = 0x004A, .ghz2 = 0x004A, NOUPLOAD, }, - [B2055_C2_TX_BB_MXGM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_PRG_GCHP21] = { .ghz5 = 0x0071, .ghz2 = 0x0071, NOUPLOAD, }, - [B2055_PRG_GCHP22] = { .ghz5 = 0x0072, .ghz2 = 0x0072, NOUPLOAD, }, - [B2055_PRG_GCHP23] = { .ghz5 = 0x0073, .ghz2 = 0x0073, NOUPLOAD, }, - [B2055_PRG_GCHP24] = { .ghz5 = 0x0074, .ghz2 = 0x0074, NOUPLOAD, }, - [B2055_PRG_GCHP25] = { .ghz5 = 0x0075, .ghz2 = 0x0075, NOUPLOAD, }, - [B2055_PRG_GCHP26] = { .ghz5 = 0x0076, .ghz2 = 0x0076, NOUPLOAD, }, - [B2055_PRG_GCHP27] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2055_PRG_GCHP28] = { .ghz5 = 0x0078, .ghz2 = 0x0078, NOUPLOAD, }, - [B2055_PRG_GCHP29] = { .ghz5 = 0x0079, .ghz2 = 0x0079, NOUPLOAD, }, - [B2055_PRG_GCHP30] = { .ghz5 = 0x007A, .ghz2 = 0x007A, NOUPLOAD, }, - [0xC7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xC8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xC9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xCA] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xCB] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xCC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C1_LNA_GAINBST] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xCE] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [0xCF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xD0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xD1] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2055_C1_B0NB_RSSIVCM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [0xD3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xD4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xD5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C1_GENSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xD7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xD8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C2_LNA_GAINBST] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xDA] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [0xDB] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xDC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xDD] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2055_C2_B0NB_RSSIVCM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [0xDF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xE0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [0xE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2055_C2_GENSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -#define RADIOREGS(r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, \ - r12, r13, r14, r15, r16, r17, r18, r19, r20, r21) \ - .radio_pll_ref = r0, \ - .radio_rf_pllmod0 = r1, \ - .radio_rf_pllmod1 = r2, \ - .radio_vco_captail = r3, \ - .radio_vco_cal1 = r4, \ - .radio_vco_cal2 = r5, \ - .radio_pll_lfc1 = r6, \ - .radio_pll_lfr1 = r7, \ - .radio_pll_lfc2 = r8, \ - .radio_lgbuf_cenbuf = r9, \ - .radio_lgen_tune1 = r10, \ - .radio_lgen_tune2 = r11, \ - .radio_c1_lgbuf_atune = r12, \ - .radio_c1_lgbuf_gtune = r13, \ - .radio_c1_rx_rfr1 = r14, \ - .radio_c1_tx_pgapadtn = r15, \ - .radio_c1_tx_mxbgtrim = r16, \ - .radio_c2_lgbuf_atune = r17, \ - .radio_c2_lgbuf_gtune = r18, \ - .radio_c2_rx_rfr1 = r19, \ - .radio_c2_tx_pgapadtn = r20, \ - .radio_c2_tx_mxbgtrim = r21 - -#define PHYREGS(r0, r1, r2, r3, r4, r5) \ - .phy_regs.phy_bw1a = r0, \ - .phy_regs.phy_bw2 = r1, \ - .phy_regs.phy_bw3 = r2, \ - .phy_regs.phy_bw4 = r3, \ - .phy_regs.phy_bw5 = r4, \ - .phy_regs.phy_bw6 = r5 - -static const struct b43_nphy_channeltab_entry_rev2 b43_nphy_channeltab_rev2[] = { - { .channel = 184, - .freq = 4920, /* MHz */ - .unk2 = 3280, - RADIOREGS(0x71, 0xEC, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07B4, 0x07B0, 0x07AC, 0x0214, 0x0215, 0x0216), - }, - { .channel = 186, - .freq = 4930, /* MHz */ - .unk2 = 3287, - RADIOREGS(0x71, 0xED, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07B8, 0x07B4, 0x07B0, 0x0213, 0x0214, 0x0215), - }, - { .channel = 188, - .freq = 4940, /* MHz */ - .unk2 = 3293, - RADIOREGS(0x71, 0xEE, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07BC, 0x07B8, 0x07B4, 0x0212, 0x0213, 0x0214), - }, - { .channel = 190, - .freq = 4950, /* MHz */ - .unk2 = 3300, - RADIOREGS(0x71, 0xEF, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07C0, 0x07BC, 0x07B8, 0x0211, 0x0212, 0x0213), - }, - { .channel = 192, - .freq = 4960, /* MHz */ - .unk2 = 3307, - RADIOREGS(0x71, 0xF0, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07C4, 0x07C0, 0x07BC, 0x020F, 0x0211, 0x0212), - }, - { .channel = 194, - .freq = 4970, /* MHz */ - .unk2 = 3313, - RADIOREGS(0x71, 0xF1, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07C8, 0x07C4, 0x07C0, 0x020E, 0x020F, 0x0211), - }, - { .channel = 196, - .freq = 4980, /* MHz */ - .unk2 = 3320, - RADIOREGS(0x71, 0xF2, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07CC, 0x07C8, 0x07C4, 0x020D, 0x020E, 0x020F), - }, - { .channel = 198, - .freq = 4990, /* MHz */ - .unk2 = 3327, - RADIOREGS(0x71, 0xF3, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07D0, 0x07CC, 0x07C8, 0x020C, 0x020D, 0x020E), - }, - { .channel = 200, - .freq = 5000, /* MHz */ - .unk2 = 3333, - RADIOREGS(0x71, 0xF4, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07D4, 0x07D0, 0x07CC, 0x020B, 0x020C, 0x020D), - }, - { .channel = 202, - .freq = 5010, /* MHz */ - .unk2 = 3340, - RADIOREGS(0x71, 0xF5, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07D8, 0x07D4, 0x07D0, 0x020A, 0x020B, 0x020C), - }, - { .channel = 204, - .freq = 5020, /* MHz */ - .unk2 = 3347, - RADIOREGS(0x71, 0xF6, 0x01, 0x0E, 0xF7, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07DC, 0x07D8, 0x07D4, 0x0209, 0x020A, 0x020B), - }, - { .channel = 206, - .freq = 5030, /* MHz */ - .unk2 = 3353, - RADIOREGS(0x71, 0xF7, 0x01, 0x0E, 0xF7, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07E0, 0x07DC, 0x07D8, 0x0208, 0x0209, 0x020A), - }, - { .channel = 208, - .freq = 5040, /* MHz */ - .unk2 = 3360, - RADIOREGS(0x71, 0xF8, 0x01, 0x0D, 0xEF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07E4, 0x07E0, 0x07DC, 0x0207, 0x0208, 0x0209), - }, - { .channel = 210, - .freq = 5050, /* MHz */ - .unk2 = 3367, - RADIOREGS(0x71, 0xF9, 0x01, 0x0D, 0xEF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, - 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), - PHYREGS(0x07E8, 0x07E4, 0x07E0, 0x0206, 0x0207, 0x0208), - }, - { .channel = 212, - .freq = 5060, /* MHz */ - .unk2 = 3373, - RADIOREGS(0x71, 0xFA, 0x01, 0x0D, 0xE6, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, - 0x8E, 0xFF, 0x00, 0x0E, 0x0F, 0x8E), - PHYREGS(0x07EC, 0x07E8, 0x07E4, 0x0205, 0x0206, 0x0207), - }, - { .channel = 214, - .freq = 5070, /* MHz */ - .unk2 = 3380, - RADIOREGS(0x71, 0xFB, 0x01, 0x0D, 0xE6, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, - 0x8E, 0xFF, 0x00, 0x0E, 0x0F, 0x8E), - PHYREGS(0x07F0, 0x07EC, 0x07E8, 0x0204, 0x0205, 0x0206), - }, - { .channel = 216, - .freq = 5080, /* MHz */ - .unk2 = 3387, - RADIOREGS(0x71, 0xFC, 0x01, 0x0D, 0xDE, 0x01, 0x04, 0x0A, - 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, - 0x8D, 0xEE, 0x00, 0x0E, 0x0F, 0x8D), - PHYREGS(0x07F4, 0x07F0, 0x07EC, 0x0203, 0x0204, 0x0205), - }, - { .channel = 218, - .freq = 5090, /* MHz */ - .unk2 = 3393, - RADIOREGS(0x71, 0xFD, 0x01, 0x0D, 0xDE, 0x01, 0x04, 0x0A, - 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, - 0x8D, 0xEE, 0x00, 0x0E, 0x0F, 0x8D), - PHYREGS(0x07F8, 0x07F4, 0x07F0, 0x0202, 0x0203, 0x0204), - }, - { .channel = 220, - .freq = 5100, /* MHz */ - .unk2 = 3400, - RADIOREGS(0x71, 0xFE, 0x01, 0x0C, 0xD6, 0x01, 0x04, 0x0A, - 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, - 0x8D, 0xEE, 0x00, 0x0D, 0x0F, 0x8D), - PHYREGS(0x07FC, 0x07F8, 0x07F4, 0x0201, 0x0202, 0x0203), - }, - { .channel = 222, - .freq = 5110, /* MHz */ - .unk2 = 3407, - RADIOREGS(0x71, 0xFF, 0x01, 0x0C, 0xD6, 0x01, 0x04, 0x0A, - 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, - 0x8D, 0xEE, 0x00, 0x0D, 0x0F, 0x8D), - PHYREGS(0x0800, 0x07FC, 0x07F8, 0x0200, 0x0201, 0x0202), - }, - { .channel = 224, - .freq = 5120, /* MHz */ - .unk2 = 3413, - RADIOREGS(0x71, 0x00, 0x02, 0x0C, 0xCE, 0x01, 0x04, 0x0A, - 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, - 0x8C, 0xDD, 0x00, 0x0D, 0x0F, 0x8C), - PHYREGS(0x0804, 0x0800, 0x07FC, 0x01FF, 0x0200, 0x0201), - }, - { .channel = 226, - .freq = 5130, /* MHz */ - .unk2 = 3420, - RADIOREGS(0x71, 0x01, 0x02, 0x0C, 0xCE, 0x01, 0x04, 0x0A, - 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, - 0x8C, 0xDD, 0x00, 0x0D, 0x0F, 0x8C), - PHYREGS(0x0808, 0x0804, 0x0800, 0x01FE, 0x01FF, 0x0200), - }, - { .channel = 228, - .freq = 5140, /* MHz */ - .unk2 = 3427, - RADIOREGS(0x71, 0x02, 0x02, 0x0C, 0xC6, 0x01, 0x04, 0x0A, - 0x00, 0x8D, 0x99, 0x99, 0xDD, 0x00, 0x0C, 0x0E, - 0x8B, 0xDD, 0x00, 0x0C, 0x0E, 0x8B), - PHYREGS(0x080C, 0x0808, 0x0804, 0x01FD, 0x01FE, 0x01FF), - }, - { .channel = 32, - .freq = 5160, /* MHz */ - .unk2 = 3440, - RADIOREGS(0x71, 0x04, 0x02, 0x0B, 0xBE, 0x01, 0x04, 0x0A, - 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, - 0x8A, 0xCC, 0x00, 0x0B, 0x0D, 0x8A), - PHYREGS(0x0814, 0x0810, 0x080C, 0x01FB, 0x01FC, 0x01FD), - }, - { .channel = 34, - .freq = 5170, /* MHz */ - .unk2 = 3447, - RADIOREGS(0x71, 0x05, 0x02, 0x0B, 0xBE, 0x01, 0x04, 0x0A, - 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, - 0x8A, 0xCC, 0x00, 0x0B, 0x0D, 0x8A), - PHYREGS(0x0818, 0x0814, 0x0810, 0x01FA, 0x01FB, 0x01FC), - }, - { .channel = 36, - .freq = 5180, /* MHz */ - .unk2 = 3453, - RADIOREGS(0x71, 0x06, 0x02, 0x0B, 0xB6, 0x01, 0x04, 0x0A, - 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, - 0x89, 0xCC, 0x00, 0x0B, 0x0C, 0x89), - PHYREGS(0x081C, 0x0818, 0x0814, 0x01F9, 0x01FA, 0x01FB), - }, - { .channel = 38, - .freq = 5190, /* MHz */ - .unk2 = 3460, - RADIOREGS(0x71, 0x07, 0x02, 0x0B, 0xB6, 0x01, 0x04, 0x0A, - 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, - 0x89, 0xCC, 0x00, 0x0B, 0x0C, 0x89), - PHYREGS(0x0820, 0x081C, 0x0818, 0x01F8, 0x01F9, 0x01FA), - }, - { .channel = 40, - .freq = 5200, /* MHz */ - .unk2 = 3467, - RADIOREGS(0x71, 0x08, 0x02, 0x0B, 0xAF, 0x01, 0x04, 0x0A, - 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, - 0x89, 0xBB, 0x00, 0x0A, 0x0B, 0x89), - PHYREGS(0x0824, 0x0820, 0x081C, 0x01F7, 0x01F8, 0x01F9), - }, - { .channel = 42, - .freq = 5210, /* MHz */ - .unk2 = 3473, - RADIOREGS(0x71, 0x09, 0x02, 0x0B, 0xAF, 0x01, 0x04, 0x0A, - 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, - 0x89, 0xBB, 0x00, 0x0A, 0x0B, 0x89), - PHYREGS(0x0828, 0x0824, 0x0820, 0x01F6, 0x01F7, 0x01F8), - }, - { .channel = 44, - .freq = 5220, /* MHz */ - .unk2 = 3480, - RADIOREGS(0x71, 0x0A, 0x02, 0x0A, 0xA7, 0x01, 0x04, 0x0A, - 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, - 0x88, 0xBB, 0x00, 0x09, 0x0A, 0x88), - PHYREGS(0x082C, 0x0828, 0x0824, 0x01F5, 0x01F6, 0x01F7), - }, - { .channel = 46, - .freq = 5230, /* MHz */ - .unk2 = 3487, - RADIOREGS(0x71, 0x0B, 0x02, 0x0A, 0xA7, 0x01, 0x04, 0x0A, - 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, - 0x88, 0xBB, 0x00, 0x09, 0x0A, 0x88), - PHYREGS(0x0830, 0x082C, 0x0828, 0x01F4, 0x01F5, 0x01F6), - }, - { .channel = 48, - .freq = 5240, /* MHz */ - .unk2 = 3493, - RADIOREGS(0x71, 0x0C, 0x02, 0x0A, 0xA0, 0x01, 0x04, 0x0A, - 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, - 0x87, 0xAA, 0x00, 0x09, 0x0A, 0x87), - PHYREGS(0x0834, 0x0830, 0x082C, 0x01F3, 0x01F4, 0x01F5), - }, - { .channel = 50, - .freq = 5250, /* MHz */ - .unk2 = 3500, - RADIOREGS(0x71, 0x0D, 0x02, 0x0A, 0xA0, 0x01, 0x04, 0x0A, - 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, - 0x87, 0xAA, 0x00, 0x09, 0x0A, 0x87), - PHYREGS(0x0838, 0x0834, 0x0830, 0x01F2, 0x01F3, 0x01F4), - }, - { .channel = 52, - .freq = 5260, /* MHz */ - .unk2 = 3507, - RADIOREGS(0x71, 0x0E, 0x02, 0x0A, 0x98, 0x01, 0x04, 0x0A, - 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, - 0x87, 0xAA, 0x00, 0x08, 0x09, 0x87), - PHYREGS(0x083C, 0x0838, 0x0834, 0x01F1, 0x01F2, 0x01F3), - }, - { .channel = 54, - .freq = 5270, /* MHz */ - .unk2 = 3513, - RADIOREGS(0x71, 0x0F, 0x02, 0x0A, 0x98, 0x01, 0x04, 0x0A, - 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, - 0x87, 0xAA, 0x00, 0x08, 0x09, 0x87), - PHYREGS(0x0840, 0x083C, 0x0838, 0x01F0, 0x01F1, 0x01F2), - }, - { .channel = 56, - .freq = 5280, /* MHz */ - .unk2 = 3520, - RADIOREGS(0x71, 0x10, 0x02, 0x09, 0x91, 0x01, 0x04, 0x0A, - 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, - 0x86, 0x99, 0x00, 0x08, 0x08, 0x86), - PHYREGS(0x0844, 0x0840, 0x083C, 0x01F0, 0x01F0, 0x01F1), - }, - { .channel = 58, - .freq = 5290, /* MHz */ - .unk2 = 3527, - RADIOREGS(0x71, 0x11, 0x02, 0x09, 0x91, 0x01, 0x04, 0x0A, - 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, - 0x86, 0x99, 0x00, 0x08, 0x08, 0x86), - PHYREGS(0x0848, 0x0844, 0x0840, 0x01EF, 0x01F0, 0x01F0), - }, - { .channel = 60, - .freq = 5300, /* MHz */ - .unk2 = 3533, - RADIOREGS(0x71, 0x12, 0x02, 0x09, 0x8A, 0x01, 0x04, 0x0A, - 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, - 0x85, 0x99, 0x00, 0x08, 0x07, 0x85), - PHYREGS(0x084C, 0x0848, 0x0844, 0x01EE, 0x01EF, 0x01F0), - }, - { .channel = 62, - .freq = 5310, /* MHz */ - .unk2 = 3540, - RADIOREGS(0x71, 0x13, 0x02, 0x09, 0x8A, 0x01, 0x04, 0x0A, - 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, - 0x85, 0x99, 0x00, 0x08, 0x07, 0x85), - PHYREGS(0x0850, 0x084C, 0x0848, 0x01ED, 0x01EE, 0x01EF), - }, - { .channel = 64, - .freq = 5320, /* MHz */ - .unk2 = 3547, - RADIOREGS(0x71, 0x14, 0x02, 0x09, 0x83, 0x01, 0x04, 0x0A, - 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, - 0x84, 0x88, 0x00, 0x07, 0x07, 0x84), - PHYREGS(0x0854, 0x0850, 0x084C, 0x01EC, 0x01ED, 0x01EE), - }, - { .channel = 66, - .freq = 5330, /* MHz */ - .unk2 = 3553, - RADIOREGS(0x71, 0x15, 0x02, 0x09, 0x83, 0x01, 0x04, 0x0A, - 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, - 0x84, 0x88, 0x00, 0x07, 0x07, 0x84), - PHYREGS(0x0858, 0x0854, 0x0850, 0x01EB, 0x01EC, 0x01ED), - }, - { .channel = 68, - .freq = 5340, /* MHz */ - .unk2 = 3560, - RADIOREGS(0x71, 0x16, 0x02, 0x08, 0x7C, 0x01, 0x04, 0x0A, - 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, - 0x84, 0x88, 0x00, 0x07, 0x06, 0x84), - PHYREGS(0x085C, 0x0858, 0x0854, 0x01EA, 0x01EB, 0x01EC), - }, - { .channel = 70, - .freq = 5350, /* MHz */ - .unk2 = 3567, - RADIOREGS(0x71, 0x17, 0x02, 0x08, 0x7C, 0x01, 0x04, 0x0A, - 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, - 0x84, 0x88, 0x00, 0x07, 0x06, 0x84), - PHYREGS(0x0860, 0x085C, 0x0858, 0x01E9, 0x01EA, 0x01EB), - }, - { .channel = 72, - .freq = 5360, /* MHz */ - .unk2 = 3573, - RADIOREGS(0x71, 0x18, 0x02, 0x08, 0x75, 0x01, 0x04, 0x0A, - 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, - 0x83, 0x77, 0x00, 0x06, 0x05, 0x83), - PHYREGS(0x0864, 0x0860, 0x085C, 0x01E8, 0x01E9, 0x01EA), - }, - { .channel = 74, - .freq = 5370, /* MHz */ - .unk2 = 3580, - RADIOREGS(0x71, 0x19, 0x02, 0x08, 0x75, 0x01, 0x04, 0x0A, - 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, - 0x83, 0x77, 0x00, 0x06, 0x05, 0x83), - PHYREGS(0x0868, 0x0864, 0x0860, 0x01E7, 0x01E8, 0x01E9), - }, - { .channel = 76, - .freq = 5380, /* MHz */ - .unk2 = 3587, - RADIOREGS(0x71, 0x1A, 0x02, 0x08, 0x6E, 0x01, 0x04, 0x0A, - 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, - 0x82, 0x77, 0x00, 0x06, 0x04, 0x82), - PHYREGS(0x086C, 0x0868, 0x0864, 0x01E6, 0x01E7, 0x01E8), - }, - { .channel = 78, - .freq = 5390, /* MHz */ - .unk2 = 3593, - RADIOREGS(0x71, 0x1B, 0x02, 0x08, 0x6E, 0x01, 0x04, 0x0A, - 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, - 0x82, 0x77, 0x00, 0x06, 0x04, 0x82), - PHYREGS(0x0870, 0x086C, 0x0868, 0x01E5, 0x01E6, 0x01E7), - }, - { .channel = 80, - .freq = 5400, /* MHz */ - .unk2 = 3600, - RADIOREGS(0x71, 0x1C, 0x02, 0x07, 0x67, 0x01, 0x04, 0x0A, - 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, - 0x81, 0x66, 0x00, 0x05, 0x04, 0x81), - PHYREGS(0x0874, 0x0870, 0x086C, 0x01E5, 0x01E5, 0x01E6), - }, - { .channel = 82, - .freq = 5410, /* MHz */ - .unk2 = 3607, - RADIOREGS(0x71, 0x1D, 0x02, 0x07, 0x67, 0x01, 0x04, 0x0A, - 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, - 0x81, 0x66, 0x00, 0x05, 0x04, 0x81), - PHYREGS(0x0878, 0x0874, 0x0870, 0x01E4, 0x01E5, 0x01E5), - }, - { .channel = 84, - .freq = 5420, /* MHz */ - .unk2 = 3613, - RADIOREGS(0x71, 0x1E, 0x02, 0x07, 0x61, 0x01, 0x04, 0x0A, - 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, - 0x80, 0x66, 0x00, 0x05, 0x03, 0x80), - PHYREGS(0x087C, 0x0878, 0x0874, 0x01E3, 0x01E4, 0x01E5), - }, - { .channel = 86, - .freq = 5430, /* MHz */ - .unk2 = 3620, - RADIOREGS(0x71, 0x1F, 0x02, 0x07, 0x61, 0x01, 0x04, 0x0A, - 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, - 0x80, 0x66, 0x00, 0x05, 0x03, 0x80), - PHYREGS(0x0880, 0x087C, 0x0878, 0x01E2, 0x01E3, 0x01E4), - }, - { .channel = 88, - .freq = 5440, /* MHz */ - .unk2 = 3627, - RADIOREGS(0x71, 0x20, 0x02, 0x07, 0x5A, 0x01, 0x04, 0x0A, - 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, - 0x80, 0x55, 0x00, 0x04, 0x02, 0x80), - PHYREGS(0x0884, 0x0880, 0x087C, 0x01E1, 0x01E2, 0x01E3), - }, - { .channel = 90, - .freq = 5450, /* MHz */ - .unk2 = 3633, - RADIOREGS(0x71, 0x21, 0x02, 0x07, 0x5A, 0x01, 0x04, 0x0A, - 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, - 0x80, 0x55, 0x00, 0x04, 0x02, 0x80), - PHYREGS(0x0888, 0x0884, 0x0880, 0x01E0, 0x01E1, 0x01E2), - }, - { .channel = 92, - .freq = 5460, /* MHz */ - .unk2 = 3640, - RADIOREGS(0x71, 0x22, 0x02, 0x06, 0x53, 0x01, 0x04, 0x0A, - 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, - 0x80, 0x55, 0x00, 0x04, 0x01, 0x80), - PHYREGS(0x088C, 0x0888, 0x0884, 0x01DF, 0x01E0, 0x01E1), - }, - { .channel = 94, - .freq = 5470, /* MHz */ - .unk2 = 3647, - RADIOREGS(0x71, 0x23, 0x02, 0x06, 0x53, 0x01, 0x04, 0x0A, - 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, - 0x80, 0x55, 0x00, 0x04, 0x01, 0x80), - PHYREGS(0x0890, 0x088C, 0x0888, 0x01DE, 0x01DF, 0x01E0), - }, - { .channel = 96, - .freq = 5480, /* MHz */ - .unk2 = 3653, - RADIOREGS(0x71, 0x24, 0x02, 0x06, 0x4D, 0x01, 0x04, 0x0A, - 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, - 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), - PHYREGS(0x0894, 0x0890, 0x088C, 0x01DD, 0x01DE, 0x01DF), - }, - { .channel = 98, - .freq = 5490, /* MHz */ - .unk2 = 3660, - RADIOREGS(0x71, 0x25, 0x02, 0x06, 0x4D, 0x01, 0x04, 0x0A, - 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, - 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), - PHYREGS(0x0898, 0x0894, 0x0890, 0x01DD, 0x01DD, 0x01DE), - }, - { .channel = 100, - .freq = 5500, /* MHz */ - .unk2 = 3667, - RADIOREGS(0x71, 0x26, 0x02, 0x06, 0x47, 0x01, 0x04, 0x0A, - 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, - 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), - PHYREGS(0x089C, 0x0898, 0x0894, 0x01DC, 0x01DD, 0x01DD), - }, - { .channel = 102, - .freq = 5510, /* MHz */ - .unk2 = 3673, - RADIOREGS(0x71, 0x27, 0x02, 0x06, 0x47, 0x01, 0x04, 0x0A, - 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, - 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), - PHYREGS(0x08A0, 0x089C, 0x0898, 0x01DB, 0x01DC, 0x01DD), - }, - { .channel = 104, - .freq = 5520, /* MHz */ - .unk2 = 3680, - RADIOREGS(0x71, 0x28, 0x02, 0x05, 0x40, 0x01, 0x04, 0x0A, - 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, - 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), - PHYREGS(0x08A4, 0x08A0, 0x089C, 0x01DA, 0x01DB, 0x01DC), - }, - { .channel = 106, - .freq = 5530, /* MHz */ - .unk2 = 3687, - RADIOREGS(0x71, 0x29, 0x02, 0x05, 0x40, 0x01, 0x04, 0x0A, - 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, - 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), - PHYREGS(0x08A8, 0x08A4, 0x08A0, 0x01D9, 0x01DA, 0x01DB), - }, - { .channel = 108, - .freq = 5540, /* MHz */ - .unk2 = 3693, - RADIOREGS(0x71, 0x2A, 0x02, 0x05, 0x3A, 0x01, 0x04, 0x0A, - 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, - 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), - PHYREGS(0x08AC, 0x08A8, 0x08A4, 0x01D8, 0x01D9, 0x01DA), - }, - { .channel = 110, - .freq = 5550, /* MHz */ - .unk2 = 3700, - RADIOREGS(0x71, 0x2B, 0x02, 0x05, 0x3A, 0x01, 0x04, 0x0A, - 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, - 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), - PHYREGS(0x08B0, 0x08AC, 0x08A8, 0x01D7, 0x01D8, 0x01D9), - }, - { .channel = 112, - .freq = 5560, /* MHz */ - .unk2 = 3707, - RADIOREGS(0x71, 0x2C, 0x02, 0x05, 0x34, 0x01, 0x04, 0x0A, - 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, - 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), - PHYREGS(0x08B4, 0x08B0, 0x08AC, 0x01D7, 0x01D7, 0x01D8), - }, - { .channel = 114, - .freq = 5570, /* MHz */ - .unk2 = 3713, - RADIOREGS(0x71, 0x2D, 0x02, 0x05, 0x34, 0x01, 0x04, 0x0A, - 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, - 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), - PHYREGS(0x08B8, 0x08B4, 0x08B0, 0x01D6, 0x01D7, 0x01D7), - }, - { .channel = 116, - .freq = 5580, /* MHz */ - .unk2 = 3720, - RADIOREGS(0x71, 0x2E, 0x02, 0x04, 0x2E, 0x01, 0x04, 0x0A, - 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, - 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), - PHYREGS(0x08BC, 0x08B8, 0x08B4, 0x01D5, 0x01D6, 0x01D7), - }, - { .channel = 118, - .freq = 5590, /* MHz */ - .unk2 = 3727, - RADIOREGS(0x71, 0x2F, 0x02, 0x04, 0x2E, 0x01, 0x04, 0x0A, - 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, - 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), - PHYREGS(0x08C0, 0x08BC, 0x08B8, 0x01D4, 0x01D5, 0x01D6), - }, - { .channel = 120, - .freq = 5600, /* MHz */ - .unk2 = 3733, - RADIOREGS(0x71, 0x30, 0x02, 0x04, 0x28, 0x01, 0x04, 0x0A, - 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, - 0x80, 0x11, 0x00, 0x01, 0x00, 0x80), - PHYREGS(0x08C4, 0x08C0, 0x08BC, 0x01D3, 0x01D4, 0x01D5), - }, - { .channel = 122, - .freq = 5610, /* MHz */ - .unk2 = 3740, - RADIOREGS(0x71, 0x31, 0x02, 0x04, 0x28, 0x01, 0x04, 0x0A, - 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, - 0x80, 0x11, 0x00, 0x01, 0x00, 0x80), - PHYREGS(0x08C8, 0x08C4, 0x08C0, 0x01D2, 0x01D3, 0x01D4), - }, - { .channel = 124, - .freq = 5620, /* MHz */ - .unk2 = 3747, - RADIOREGS(0x71, 0x32, 0x02, 0x04, 0x21, 0x01, 0x04, 0x0A, - 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x80, 0x11, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08CC, 0x08C8, 0x08C4, 0x01D2, 0x01D2, 0x01D3), - }, - { .channel = 126, - .freq = 5630, /* MHz */ - .unk2 = 3753, - RADIOREGS(0x71, 0x33, 0x02, 0x04, 0x21, 0x01, 0x04, 0x0A, - 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x80, 0x11, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08D0, 0x08CC, 0x08C8, 0x01D1, 0x01D2, 0x01D2), - }, - { .channel = 128, - .freq = 5640, /* MHz */ - .unk2 = 3760, - RADIOREGS(0x71, 0x34, 0x02, 0x03, 0x1C, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08D4, 0x08D0, 0x08CC, 0x01D0, 0x01D1, 0x01D2), - }, - { .channel = 130, - .freq = 5650, /* MHz */ - .unk2 = 3767, - RADIOREGS(0x71, 0x35, 0x02, 0x03, 0x1C, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08D8, 0x08D4, 0x08D0, 0x01CF, 0x01D0, 0x01D1), - }, - { .channel = 132, - .freq = 5660, /* MHz */ - .unk2 = 3773, - RADIOREGS(0x71, 0x36, 0x02, 0x03, 0x16, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08DC, 0x08D8, 0x08D4, 0x01CE, 0x01CF, 0x01D0), - }, - { .channel = 134, - .freq = 5670, /* MHz */ - .unk2 = 3780, - RADIOREGS(0x71, 0x37, 0x02, 0x03, 0x16, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08E0, 0x08DC, 0x08D8, 0x01CE, 0x01CE, 0x01CF), - }, - { .channel = 136, - .freq = 5680, /* MHz */ - .unk2 = 3787, - RADIOREGS(0x71, 0x38, 0x02, 0x03, 0x10, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08E4, 0x08E0, 0x08DC, 0x01CD, 0x01CE, 0x01CE), - }, - { .channel = 138, - .freq = 5690, /* MHz */ - .unk2 = 3793, - RADIOREGS(0x71, 0x39, 0x02, 0x03, 0x10, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08E8, 0x08E4, 0x08E0, 0x01CC, 0x01CD, 0x01CE), - }, - { .channel = 140, - .freq = 5700, /* MHz */ - .unk2 = 3800, - RADIOREGS(0x71, 0x3A, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08EC, 0x08E8, 0x08E4, 0x01CB, 0x01CC, 0x01CD), - }, - { .channel = 142, - .freq = 5710, /* MHz */ - .unk2 = 3807, - RADIOREGS(0x71, 0x3B, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08F0, 0x08EC, 0x08E8, 0x01CA, 0x01CB, 0x01CC), - }, - { .channel = 144, - .freq = 5720, /* MHz */ - .unk2 = 3813, - RADIOREGS(0x71, 0x3C, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08F4, 0x08F0, 0x08EC, 0x01C9, 0x01CA, 0x01CB), - }, - { .channel = 145, - .freq = 5725, /* MHz */ - .unk2 = 3817, - RADIOREGS(0x72, 0x79, 0x04, 0x02, 0x03, 0x01, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08F6, 0x08F2, 0x08EE, 0x01C9, 0x01CA, 0x01CB), - }, - { .channel = 146, - .freq = 5730, /* MHz */ - .unk2 = 3820, - RADIOREGS(0x71, 0x3D, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08F8, 0x08F4, 0x08F0, 0x01C9, 0x01C9, 0x01CA), - }, - { .channel = 147, - .freq = 5735, /* MHz */ - .unk2 = 3823, - RADIOREGS(0x72, 0x7B, 0x04, 0x02, 0x03, 0x01, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08FA, 0x08F6, 0x08F2, 0x01C8, 0x01C9, 0x01CA), - }, - { .channel = 148, - .freq = 5740, /* MHz */ - .unk2 = 3827, - RADIOREGS(0x71, 0x3E, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08FC, 0x08F8, 0x08F4, 0x01C8, 0x01C9, 0x01C9), - }, - { .channel = 149, - .freq = 5745, /* MHz */ - .unk2 = 3830, - RADIOREGS(0x72, 0x7D, 0x04, 0x02, 0xFE, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x08FE, 0x08FA, 0x08F6, 0x01C8, 0x01C8, 0x01C9), - }, - { .channel = 150, - .freq = 5750, /* MHz */ - .unk2 = 3833, - RADIOREGS(0x71, 0x3F, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0900, 0x08FC, 0x08F8, 0x01C7, 0x01C8, 0x01C9), - }, - { .channel = 151, - .freq = 5755, /* MHz */ - .unk2 = 3837, - RADIOREGS(0x72, 0x7F, 0x04, 0x02, 0xFE, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0902, 0x08FE, 0x08FA, 0x01C7, 0x01C8, 0x01C8), - }, - { .channel = 152, - .freq = 5760, /* MHz */ - .unk2 = 3840, - RADIOREGS(0x71, 0x40, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0904, 0x0900, 0x08FC, 0x01C6, 0x01C7, 0x01C8), - }, - { .channel = 153, - .freq = 5765, /* MHz */ - .unk2 = 3843, - RADIOREGS(0x72, 0x81, 0x04, 0x02, 0xF8, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0906, 0x0902, 0x08FE, 0x01C6, 0x01C7, 0x01C8), - }, - { .channel = 154, - .freq = 5770, /* MHz */ - .unk2 = 3847, - RADIOREGS(0x71, 0x41, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0908, 0x0904, 0x0900, 0x01C6, 0x01C6, 0x01C7), - }, - { .channel = 155, - .freq = 5775, /* MHz */ - .unk2 = 3850, - RADIOREGS(0x72, 0x83, 0x04, 0x02, 0xF8, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x090A, 0x0906, 0x0902, 0x01C5, 0x01C6, 0x01C7), - }, - { .channel = 156, - .freq = 5780, /* MHz */ - .unk2 = 3853, - RADIOREGS(0x71, 0x42, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x090C, 0x0908, 0x0904, 0x01C5, 0x01C6, 0x01C6), - }, - { .channel = 157, - .freq = 5785, /* MHz */ - .unk2 = 3857, - RADIOREGS(0x72, 0x85, 0x04, 0x02, 0xF2, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x090E, 0x090A, 0x0906, 0x01C4, 0x01C5, 0x01C6), - }, - { .channel = 158, - .freq = 5790, /* MHz */ - .unk2 = 3860, - RADIOREGS(0x71, 0x43, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0910, 0x090C, 0x0908, 0x01C4, 0x01C5, 0x01C6), - }, - { .channel = 159, - .freq = 5795, /* MHz */ - .unk2 = 3863, - RADIOREGS(0x72, 0x87, 0x04, 0x02, 0xF2, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0912, 0x090E, 0x090A, 0x01C4, 0x01C4, 0x01C5), - }, - { .channel = 160, - .freq = 5800, /* MHz */ - .unk2 = 3867, - RADIOREGS(0x71, 0x44, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0914, 0x0910, 0x090C, 0x01C3, 0x01C4, 0x01C5), - }, - { .channel = 161, - .freq = 5805, /* MHz */ - .unk2 = 3870, - RADIOREGS(0x72, 0x89, 0x04, 0x01, 0xED, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0916, 0x0912, 0x090E, 0x01C3, 0x01C4, 0x01C4), - }, - { .channel = 162, - .freq = 5810, /* MHz */ - .unk2 = 3873, - RADIOREGS(0x71, 0x45, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0918, 0x0914, 0x0910, 0x01C2, 0x01C3, 0x01C4), - }, - { .channel = 163, - .freq = 5815, /* MHz */ - .unk2 = 3877, - RADIOREGS(0x72, 0x8B, 0x04, 0x01, 0xED, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x091A, 0x0916, 0x0912, 0x01C2, 0x01C3, 0x01C4), - }, - { .channel = 164, - .freq = 5820, /* MHz */ - .unk2 = 3880, - RADIOREGS(0x71, 0x46, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x091C, 0x0918, 0x0914, 0x01C2, 0x01C2, 0x01C3), - }, - { .channel = 165, - .freq = 5825, /* MHz */ - .unk2 = 3883, - RADIOREGS(0x72, 0x8D, 0x04, 0x01, 0xED, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x091E, 0x091A, 0x0916, 0x01C1, 0x01C2, 0x01C3), - }, - { .channel = 166, - .freq = 5830, /* MHz */ - .unk2 = 3887, - RADIOREGS(0x71, 0x47, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0920, 0x091C, 0x0918, 0x01C1, 0x01C2, 0x01C2), - }, - { .channel = 168, - .freq = 5840, /* MHz */ - .unk2 = 3893, - RADIOREGS(0x71, 0x48, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0924, 0x0920, 0x091C, 0x01C0, 0x01C1, 0x01C2), - }, - { .channel = 170, - .freq = 5850, /* MHz */ - .unk2 = 3900, - RADIOREGS(0x71, 0x49, 0x02, 0x01, 0xE0, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0928, 0x0924, 0x0920, 0x01BF, 0x01C0, 0x01C1), - }, - { .channel = 172, - .freq = 5860, /* MHz */ - .unk2 = 3907, - RADIOREGS(0x71, 0x4A, 0x02, 0x01, 0xDE, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x092C, 0x0928, 0x0924, 0x01BF, 0x01BF, 0x01C0), - }, - { .channel = 174, - .freq = 5870, /* MHz */ - .unk2 = 3913, - RADIOREGS(0x71, 0x4B, 0x02, 0x00, 0xDB, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0930, 0x092C, 0x0928, 0x01BE, 0x01BF, 0x01BF), - }, - { .channel = 176, - .freq = 5880, /* MHz */ - .unk2 = 3920, - RADIOREGS(0x71, 0x4C, 0x02, 0x00, 0xD8, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0934, 0x0930, 0x092C, 0x01BD, 0x01BE, 0x01BF), - }, - { .channel = 178, - .freq = 5890, /* MHz */ - .unk2 = 3927, - RADIOREGS(0x71, 0x4D, 0x02, 0x00, 0xD6, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0938, 0x0934, 0x0930, 0x01BC, 0x01BD, 0x01BE), - }, - { .channel = 180, - .freq = 5900, /* MHz */ - .unk2 = 3933, - RADIOREGS(0x71, 0x4E, 0x02, 0x00, 0xD3, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x093C, 0x0938, 0x0934, 0x01BC, 0x01BC, 0x01BD), - }, - { .channel = 182, - .freq = 5910, /* MHz */ - .unk2 = 3940, - RADIOREGS(0x71, 0x4F, 0x02, 0x00, 0xD6, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), - PHYREGS(0x0940, 0x093C, 0x0938, 0x01BB, 0x01BC, 0x01BC), - }, - { .channel = 1, - .freq = 2412, /* MHz */ - .unk2 = 3216, - RADIOREGS(0x73, 0x6C, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0D, 0x0C, - 0x80, 0xFF, 0x88, 0x0D, 0x0C, 0x80), - PHYREGS(0x03C9, 0x03C5, 0x03C1, 0x043A, 0x043F, 0x0443), - }, - { .channel = 2, - .freq = 2417, /* MHz */ - .unk2 = 3223, - RADIOREGS(0x73, 0x71, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0B, - 0x80, 0xFF, 0x88, 0x0C, 0x0B, 0x80), - PHYREGS(0x03CB, 0x03C7, 0x03C3, 0x0438, 0x043D, 0x0441), - }, - { .channel = 3, - .freq = 2422, /* MHz */ - .unk2 = 3229, - RADIOREGS(0x73, 0x76, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, - 0x80, 0xFF, 0x88, 0x0C, 0x0A, 0x80), - PHYREGS(0x03CD, 0x03C9, 0x03C5, 0x0436, 0x043A, 0x043F), - }, - { .channel = 4, - .freq = 2427, /* MHz */ - .unk2 = 3236, - RADIOREGS(0x73, 0x7B, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, - 0x80, 0xFF, 0x88, 0x0C, 0x0A, 0x80), - PHYREGS(0x03CF, 0x03CB, 0x03C7, 0x0434, 0x0438, 0x043D), - }, - { .channel = 5, - .freq = 2432, /* MHz */ - .unk2 = 3243, - RADIOREGS(0x73, 0x80, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x09, - 0x80, 0xFF, 0x88, 0x0C, 0x09, 0x80), - PHYREGS(0x03D1, 0x03CD, 0x03C9, 0x0431, 0x0436, 0x043A), - }, - { .channel = 6, - .freq = 2437, /* MHz */ - .unk2 = 3249, - RADIOREGS(0x73, 0x85, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0B, 0x08, - 0x80, 0xFF, 0x88, 0x0B, 0x08, 0x80), - PHYREGS(0x03D3, 0x03CF, 0x03CB, 0x042F, 0x0434, 0x0438), - }, - { .channel = 7, - .freq = 2442, /* MHz */ - .unk2 = 3256, - RADIOREGS(0x73, 0x8A, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x07, - 0x80, 0xFF, 0x88, 0x0A, 0x07, 0x80), - PHYREGS(0x03D5, 0x03D1, 0x03CD, 0x042D, 0x0431, 0x0436), - }, - { .channel = 8, - .freq = 2447, /* MHz */ - .unk2 = 3263, - RADIOREGS(0x73, 0x8F, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x06, - 0x80, 0xFF, 0x88, 0x0A, 0x06, 0x80), - PHYREGS(0x03D7, 0x03D3, 0x03CF, 0x042B, 0x042F, 0x0434), - }, - { .channel = 9, - .freq = 2452, /* MHz */ - .unk2 = 3269, - RADIOREGS(0x73, 0x94, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x09, 0x06, - 0x80, 0xFF, 0x88, 0x09, 0x06, 0x80), - PHYREGS(0x03D9, 0x03D5, 0x03D1, 0x0429, 0x042D, 0x0431), - }, - { .channel = 10, - .freq = 2457, /* MHz */ - .unk2 = 3276, - RADIOREGS(0x73, 0x99, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x05, - 0x80, 0xFF, 0x88, 0x08, 0x05, 0x80), - PHYREGS(0x03DB, 0x03D7, 0x03D3, 0x0427, 0x042B, 0x042F), - }, - { .channel = 11, - .freq = 2462, /* MHz */ - .unk2 = 3283, - RADIOREGS(0x73, 0x9E, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x04, - 0x80, 0xFF, 0x88, 0x08, 0x04, 0x80), - PHYREGS(0x03DD, 0x03D9, 0x03D5, 0x0424, 0x0429, 0x042D), - }, - { .channel = 12, - .freq = 2467, /* MHz */ - .unk2 = 3289, - RADIOREGS(0x73, 0xA3, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x03, - 0x80, 0xFF, 0x88, 0x08, 0x03, 0x80), - PHYREGS(0x03DF, 0x03DB, 0x03D7, 0x0422, 0x0427, 0x042B), - }, - { .channel = 13, - .freq = 2472, /* MHz */ - .unk2 = 3296, - RADIOREGS(0x73, 0xA8, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x03, - 0x80, 0xFF, 0x88, 0x07, 0x03, 0x80), - PHYREGS(0x03E1, 0x03DD, 0x03D9, 0x0420, 0x0424, 0x0429), - }, - { .channel = 14, - .freq = 2484, /* MHz */ - .unk2 = 3312, - RADIOREGS(0x73, 0xB4, 0x09, 0x0F, 0xFF, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x01, - 0x80, 0xFF, 0x88, 0x07, 0x01, 0x80), - PHYREGS(0x03E6, 0x03E2, 0x03DE, 0x041B, 0x041F, 0x0424), - }, -}; - -void b2055_upload_inittab(struct b43_wldev *dev, - bool ghz5, bool ignore_uploadflag) -{ - const struct b2055_inittab_entry *e; - unsigned int i, writes = 0; - u16 value; - - for (i = 0; i < ARRAY_SIZE(b2055_inittab); i++) { - e = &(b2055_inittab[i]); - if (!(e->flags & B2055_INITTAB_ENTRY_OK)) - continue; - if ((e->flags & B2055_INITTAB_UPLOAD) || ignore_uploadflag) { - if (ghz5) - value = e->ghz5; - else - value = e->ghz2; - b43_radio_write16(dev, i, value); - if (++writes % 4 == 0) - b43_read32(dev, B43_MMIO_MACCTL); /* flush */ - } - } -} - -const struct b43_nphy_channeltab_entry_rev2 * -b43_nphy_get_chantabent_rev2(struct b43_wldev *dev, u8 channel) -{ - const struct b43_nphy_channeltab_entry_rev2 *e; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(b43_nphy_channeltab_rev2); i++) { - e = &(b43_nphy_channeltab_rev2[i]); - if (e->channel == channel) - return e; - } - - return NULL; -} diff --git a/drivers/net/wireless/b43/radio_2055.h b/drivers/net/wireless/b43/radio_2055.h deleted file mode 100644 index 67f96122f8d8..000000000000 --- a/drivers/net/wireless/b43/radio_2055.h +++ /dev/null @@ -1,259 +0,0 @@ -#ifndef B43_RADIO_2055_H_ -#define B43_RADIO_2055_H_ - -#include - -#include "tables_nphy.h" - -#define B2055_GEN_SPARE 0x00 /* GEN spare */ -#define B2055_SP_PINPD 0x02 /* SP PIN PD */ -#define B2055_C1_SP_RSSI 0x03 /* SP RSSI Core 1 */ -#define B2055_C1_SP_PDMISC 0x04 /* SP PD MISC Core 1 */ -#define B2055_C2_SP_RSSI 0x05 /* SP RSSI Core 2 */ -#define B2055_C2_SP_PDMISC 0x06 /* SP PD MISC Core 2 */ -#define B2055_C1_SP_RXGC1 0x07 /* SP RX GC1 Core 1 */ -#define B2055_C1_SP_RXGC2 0x08 /* SP RX GC2 Core 1 */ -#define B2055_C2_SP_RXGC1 0x09 /* SP RX GC1 Core 2 */ -#define B2055_C2_SP_RXGC2 0x0A /* SP RX GC2 Core 2 */ -#define B2055_C1_SP_LPFBWSEL 0x0B /* SP LPF BW select Core 1 */ -#define B2055_C2_SP_LPFBWSEL 0x0C /* SP LPF BW select Core 2 */ -#define B2055_C1_SP_TXGC1 0x0D /* SP TX GC1 Core 1 */ -#define B2055_C1_SP_TXGC2 0x0E /* SP TX GC2 Core 1 */ -#define B2055_C2_SP_TXGC1 0x0F /* SP TX GC1 Core 2 */ -#define B2055_C2_SP_TXGC2 0x10 /* SP TX GC2 Core 2 */ -#define B2055_MASTER1 0x11 /* Master control 1 */ -#define B2055_MASTER2 0x12 /* Master control 2 */ -#define B2055_PD_LGEN 0x13 /* PD LGEN */ -#define B2055_PD_PLLTS 0x14 /* PD PLL TS */ -#define B2055_C1_PD_LGBUF 0x15 /* PD Core 1 LGBUF */ -#define B2055_C1_PD_TX 0x16 /* PD Core 1 TX */ -#define B2055_C1_PD_RXTX 0x17 /* PD Core 1 RXTX */ -#define B2055_C1_PD_RSSIMISC 0x18 /* PD Core 1 RSSI MISC */ -#define B2055_C2_PD_LGBUF 0x19 /* PD Core 2 LGBUF */ -#define B2055_C2_PD_TX 0x1A /* PD Core 2 TX */ -#define B2055_C2_PD_RXTX 0x1B /* PD Core 2 RXTX */ -#define B2055_C2_PD_RSSIMISC 0x1C /* PD Core 2 RSSI MISC */ -#define B2055_PWRDET_LGEN 0x1D /* PWRDET LGEN */ -#define B2055_C1_PWRDET_LGBUF 0x1E /* PWRDET LGBUF Core 1 */ -#define B2055_C1_PWRDET_RXTX 0x1F /* PWRDET RXTX Core 1 */ -#define B2055_C2_PWRDET_LGBUF 0x20 /* PWRDET LGBUF Core 2 */ -#define B2055_C2_PWRDET_RXTX 0x21 /* PWRDET RXTX Core 2 */ -#define B2055_RRCCAL_CS 0x22 /* RRCCAL Control spare */ -#define B2055_RRCCAL_NOPTSEL 0x23 /* RRCCAL N OPT SEL */ -#define B2055_CAL_MISC 0x24 /* CAL MISC */ -#define B2055_CAL_COUT 0x25 /* CAL Counter out */ -#define B2055_CAL_COUT2 0x26 /* CAL Counter out 2 */ -#define B2055_CAL_CVARCTL 0x27 /* CAL CVAR Control */ -#define B2055_CAL_RVARCTL 0x28 /* CAL RVAR Control */ -#define B2055_CAL_LPOCTL 0x29 /* CAL LPO Control */ -#define B2055_CAL_TS 0x2A /* CAL TS */ -#define B2055_CAL_RCCALRTS 0x2B /* CAL RCCAL READ TS */ -#define B2055_CAL_RCALRTS 0x2C /* CAL RCAL READ TS */ -#define B2055_PADDRV 0x2D /* PAD driver */ -#define B2055_XOCTL1 0x2E /* XO Control 1 */ -#define B2055_XOCTL2 0x2F /* XO Control 2 */ -#define B2055_XOREGUL 0x30 /* XO Regulator */ -#define B2055_XOMISC 0x31 /* XO misc */ -#define B2055_PLL_LFC1 0x32 /* PLL LF C1 */ -#define B2055_PLL_CALVTH 0x33 /* PLL CAL VTH */ -#define B2055_PLL_LFC2 0x34 /* PLL LF C2 */ -#define B2055_PLL_REF 0x35 /* PLL reference */ -#define B2055_PLL_LFR1 0x36 /* PLL LF R1 */ -#define B2055_PLL_PFDCP 0x37 /* PLL PFD CP */ -#define B2055_PLL_IDAC_CPOPAMP 0x38 /* PLL IDAC CPOPAMP */ -#define B2055_PLL_CPREG 0x39 /* PLL CP Regulator */ -#define B2055_PLL_RCAL 0x3A /* PLL RCAL */ -#define B2055_RF_PLLMOD0 0x3B /* RF PLL MOD0 */ -#define B2055_RF_PLLMOD1 0x3C /* RF PLL MOD1 */ -#define B2055_RF_MMDIDAC1 0x3D /* RF MMD IDAC 1 */ -#define B2055_RF_MMDIDAC0 0x3E /* RF MMD IDAC 0 */ -#define B2055_RF_MMDSP 0x3F /* RF MMD spare */ -#define B2055_VCO_CAL1 0x40 /* VCO cal 1 */ -#define B2055_VCO_CAL2 0x41 /* VCO cal 2 */ -#define B2055_VCO_CAL3 0x42 /* VCO cal 3 */ -#define B2055_VCO_CAL4 0x43 /* VCO cal 4 */ -#define B2055_VCO_CAL5 0x44 /* VCO cal 5 */ -#define B2055_VCO_CAL6 0x45 /* VCO cal 6 */ -#define B2055_VCO_CAL7 0x46 /* VCO cal 7 */ -#define B2055_VCO_CAL8 0x47 /* VCO cal 8 */ -#define B2055_VCO_CAL9 0x48 /* VCO cal 9 */ -#define B2055_VCO_CAL10 0x49 /* VCO cal 10 */ -#define B2055_VCO_CAL11 0x4A /* VCO cal 11 */ -#define B2055_VCO_CAL12 0x4B /* VCO cal 12 */ -#define B2055_VCO_CAL13 0x4C /* VCO cal 13 */ -#define B2055_VCO_CAL14 0x4D /* VCO cal 14 */ -#define B2055_VCO_CAL15 0x4E /* VCO cal 15 */ -#define B2055_VCO_CAL16 0x4F /* VCO cal 16 */ -#define B2055_VCO_KVCO 0x50 /* VCO KVCO */ -#define B2055_VCO_CAPTAIL 0x51 /* VCO CAP TAIL */ -#define B2055_VCO_IDACVCO 0x52 /* VCO IDAC VCO */ -#define B2055_VCO_REG 0x53 /* VCO Regulator */ -#define B2055_PLL_RFVTH 0x54 /* PLL RF VTH */ -#define B2055_LGBUF_CENBUF 0x55 /* LGBUF CEN BUF */ -#define B2055_LGEN_TUNE1 0x56 /* LGEN tune 1 */ -#define B2055_LGEN_TUNE2 0x57 /* LGEN tune 2 */ -#define B2055_LGEN_IDAC1 0x58 /* LGEN IDAC 1 */ -#define B2055_LGEN_IDAC2 0x59 /* LGEN IDAC 2 */ -#define B2055_LGEN_BIASC 0x5A /* LGEN BIAS counter */ -#define B2055_LGEN_BIASIDAC 0x5B /* LGEN BIAS IDAC */ -#define B2055_LGEN_RCAL 0x5C /* LGEN RCAL */ -#define B2055_LGEN_DIV 0x5D /* LGEN div */ -#define B2055_LGEN_SPARE2 0x5E /* LGEN spare 2 */ -#define B2055_C1_LGBUF_ATUNE 0x5F /* Core 1 LGBUF A tune */ -#define B2055_C1_LGBUF_GTUNE 0x60 /* Core 1 LGBUF G tune */ -#define B2055_C1_LGBUF_DIV 0x61 /* Core 1 LGBUF div */ -#define B2055_C1_LGBUF_AIDAC 0x62 /* Core 1 LGBUF A IDAC */ -#define B2055_C1_LGBUF_GIDAC 0x63 /* Core 1 LGBUF G IDAC */ -#define B2055_C1_LGBUF_IDACFO 0x64 /* Core 1 LGBUF IDAC filter override */ -#define B2055_C1_LGBUF_SPARE 0x65 /* Core 1 LGBUF spare */ -#define B2055_C1_RX_RFSPC1 0x66 /* Core 1 RX RF SPC1 */ -#define B2055_C1_RX_RFR1 0x67 /* Core 1 RX RF reg 1 */ -#define B2055_C1_RX_RFR2 0x68 /* Core 1 RX RF reg 2 */ -#define B2055_C1_RX_RFRCAL 0x69 /* Core 1 RX RF RCAL */ -#define B2055_C1_RX_BB_BLCMP 0x6A /* Core 1 RX Baseband BUFI LPF CMP */ -#define B2055_C1_RX_BB_LPF 0x6B /* Core 1 RX Baseband LPF */ -#define B2055_C1_RX_BB_MIDACHP 0x6C /* Core 1 RX Baseband MIDAC High-pass */ -#define B2055_C1_RX_BB_VGA1IDAC 0x6D /* Core 1 RX Baseband VGA1 IDAC */ -#define B2055_C1_RX_BB_VGA2IDAC 0x6E /* Core 1 RX Baseband VGA2 IDAC */ -#define B2055_C1_RX_BB_VGA3IDAC 0x6F /* Core 1 RX Baseband VGA3 IDAC */ -#define B2055_C1_RX_BB_BUFOCTL 0x70 /* Core 1 RX Baseband BUFO Control */ -#define B2055_C1_RX_BB_RCCALCTL 0x71 /* Core 1 RX Baseband RCCAL Control */ -#define B2055_C1_RX_BB_RSSICTL1 0x72 /* Core 1 RX Baseband RSSI Control 1 */ -#define B2055_C1_RX_BB_RSSICTL2 0x73 /* Core 1 RX Baseband RSSI Control 2 */ -#define B2055_C1_RX_BB_RSSICTL3 0x74 /* Core 1 RX Baseband RSSI Control 3 */ -#define B2055_C1_RX_BB_RSSICTL4 0x75 /* Core 1 RX Baseband RSSI Control 4 */ -#define B2055_C1_RX_BB_RSSICTL5 0x76 /* Core 1 RX Baseband RSSI Control 5 */ -#define B2055_C1_RX_BB_REG 0x77 /* Core 1 RX Baseband Regulator */ -#define B2055_C1_RX_BB_SPARE1 0x78 /* Core 1 RX Baseband spare 1 */ -#define B2055_C1_RX_TXBBRCAL 0x79 /* Core 1 RX TX BB RCAL */ -#define B2055_C1_TX_RF_SPGA 0x7A /* Core 1 TX RF SGM PGA */ -#define B2055_C1_TX_RF_SPAD 0x7B /* Core 1 TX RF SGM PAD */ -#define B2055_C1_TX_RF_CNTPGA1 0x7C /* Core 1 TX RF counter PGA 1 */ -#define B2055_C1_TX_RF_CNTPAD1 0x7D /* Core 1 TX RF counter PAD 1 */ -#define B2055_C1_TX_RF_PGAIDAC 0x7E /* Core 1 TX RF PGA IDAC */ -#define B2055_C1_TX_PGAPADTN 0x7F /* Core 1 TX PGA PAD TN */ -#define B2055_C1_TX_PADIDAC1 0x80 /* Core 1 TX PAD IDAC 1 */ -#define B2055_C1_TX_PADIDAC2 0x81 /* Core 1 TX PAD IDAC 2 */ -#define B2055_C1_TX_MXBGTRIM 0x82 /* Core 1 TX MX B/G TRIM */ -#define B2055_C1_TX_RF_RCAL 0x83 /* Core 1 TX RF RCAL */ -#define B2055_C1_TX_RF_PADTSSI1 0x84 /* Core 1 TX RF PAD TSSI1 */ -#define B2055_C1_TX_RF_PADTSSI2 0x85 /* Core 1 TX RF PAD TSSI2 */ -#define B2055_C1_TX_RF_SPARE 0x86 /* Core 1 TX RF spare */ -#define B2055_C1_TX_RF_IQCAL1 0x87 /* Core 1 TX RF I/Q CAL 1 */ -#define B2055_C1_TX_RF_IQCAL2 0x88 /* Core 1 TX RF I/Q CAL 2 */ -#define B2055_C1_TXBB_RCCAL 0x89 /* Core 1 TXBB RC CAL Control */ -#define B2055_C1_TXBB_LPF1 0x8A /* Core 1 TXBB LPF 1 */ -#define B2055_C1_TX_VOSCNCL 0x8B /* Core 1 TX VOS CNCL */ -#define B2055_C1_TX_LPF_MXGMIDAC 0x8C /* Core 1 TX LPF MXGM IDAC */ -#define B2055_C1_TX_BB_MXGM 0x8D /* Core 1 TX BB MXGM */ -#define B2055_C2_LGBUF_ATUNE 0x8E /* Core 2 LGBUF A tune */ -#define B2055_C2_LGBUF_GTUNE 0x8F /* Core 2 LGBUF G tune */ -#define B2055_C2_LGBUF_DIV 0x90 /* Core 2 LGBUF div */ -#define B2055_C2_LGBUF_AIDAC 0x91 /* Core 2 LGBUF A IDAC */ -#define B2055_C2_LGBUF_GIDAC 0x92 /* Core 2 LGBUF G IDAC */ -#define B2055_C2_LGBUF_IDACFO 0x93 /* Core 2 LGBUF IDAC filter override */ -#define B2055_C2_LGBUF_SPARE 0x94 /* Core 2 LGBUF spare */ -#define B2055_C2_RX_RFSPC1 0x95 /* Core 2 RX RF SPC1 */ -#define B2055_C2_RX_RFR1 0x96 /* Core 2 RX RF reg 1 */ -#define B2055_C2_RX_RFR2 0x97 /* Core 2 RX RF reg 2 */ -#define B2055_C2_RX_RFRCAL 0x98 /* Core 2 RX RF RCAL */ -#define B2055_C2_RX_BB_BLCMP 0x99 /* Core 2 RX Baseband BUFI LPF CMP */ -#define B2055_C2_RX_BB_LPF 0x9A /* Core 2 RX Baseband LPF */ -#define B2055_C2_RX_BB_MIDACHP 0x9B /* Core 2 RX Baseband MIDAC High-pass */ -#define B2055_C2_RX_BB_VGA1IDAC 0x9C /* Core 2 RX Baseband VGA1 IDAC */ -#define B2055_C2_RX_BB_VGA2IDAC 0x9D /* Core 2 RX Baseband VGA2 IDAC */ -#define B2055_C2_RX_BB_VGA3IDAC 0x9E /* Core 2 RX Baseband VGA3 IDAC */ -#define B2055_C2_RX_BB_BUFOCTL 0x9F /* Core 2 RX Baseband BUFO Control */ -#define B2055_C2_RX_BB_RCCALCTL 0xA0 /* Core 2 RX Baseband RCCAL Control */ -#define B2055_C2_RX_BB_RSSICTL1 0xA1 /* Core 2 RX Baseband RSSI Control 1 */ -#define B2055_C2_RX_BB_RSSICTL2 0xA2 /* Core 2 RX Baseband RSSI Control 2 */ -#define B2055_C2_RX_BB_RSSICTL3 0xA3 /* Core 2 RX Baseband RSSI Control 3 */ -#define B2055_C2_RX_BB_RSSICTL4 0xA4 /* Core 2 RX Baseband RSSI Control 4 */ -#define B2055_C2_RX_BB_RSSICTL5 0xA5 /* Core 2 RX Baseband RSSI Control 5 */ -#define B2055_C2_RX_BB_REG 0xA6 /* Core 2 RX Baseband Regulator */ -#define B2055_C2_RX_BB_SPARE1 0xA7 /* Core 2 RX Baseband spare 1 */ -#define B2055_C2_RX_TXBBRCAL 0xA8 /* Core 2 RX TX BB RCAL */ -#define B2055_C2_TX_RF_SPGA 0xA9 /* Core 2 TX RF SGM PGA */ -#define B2055_C2_TX_RF_SPAD 0xAA /* Core 2 TX RF SGM PAD */ -#define B2055_C2_TX_RF_CNTPGA1 0xAB /* Core 2 TX RF counter PGA 1 */ -#define B2055_C2_TX_RF_CNTPAD1 0xAC /* Core 2 TX RF counter PAD 1 */ -#define B2055_C2_TX_RF_PGAIDAC 0xAD /* Core 2 TX RF PGA IDAC */ -#define B2055_C2_TX_PGAPADTN 0xAE /* Core 2 TX PGA PAD TN */ -#define B2055_C2_TX_PADIDAC1 0xAF /* Core 2 TX PAD IDAC 1 */ -#define B2055_C2_TX_PADIDAC2 0xB0 /* Core 2 TX PAD IDAC 2 */ -#define B2055_C2_TX_MXBGTRIM 0xB1 /* Core 2 TX MX B/G TRIM */ -#define B2055_C2_TX_RF_RCAL 0xB2 /* Core 2 TX RF RCAL */ -#define B2055_C2_TX_RF_PADTSSI1 0xB3 /* Core 2 TX RF PAD TSSI1 */ -#define B2055_C2_TX_RF_PADTSSI2 0xB4 /* Core 2 TX RF PAD TSSI2 */ -#define B2055_C2_TX_RF_SPARE 0xB5 /* Core 2 TX RF spare */ -#define B2055_C2_TX_RF_IQCAL1 0xB6 /* Core 2 TX RF I/Q CAL 1 */ -#define B2055_C2_TX_RF_IQCAL2 0xB7 /* Core 2 TX RF I/Q CAL 2 */ -#define B2055_C2_TXBB_RCCAL 0xB8 /* Core 2 TXBB RC CAL Control */ -#define B2055_C2_TXBB_LPF1 0xB9 /* Core 2 TXBB LPF 1 */ -#define B2055_C2_TX_VOSCNCL 0xBA /* Core 2 TX VOS CNCL */ -#define B2055_C2_TX_LPF_MXGMIDAC 0xBB /* Core 2 TX LPF MXGM IDAC */ -#define B2055_C2_TX_BB_MXGM 0xBC /* Core 2 TX BB MXGM */ -#define B2055_PRG_GCHP21 0xBD /* PRG GC HPVGA23 21 */ -#define B2055_PRG_GCHP22 0xBE /* PRG GC HPVGA23 22 */ -#define B2055_PRG_GCHP23 0xBF /* PRG GC HPVGA23 23 */ -#define B2055_PRG_GCHP24 0xC0 /* PRG GC HPVGA23 24 */ -#define B2055_PRG_GCHP25 0xC1 /* PRG GC HPVGA23 25 */ -#define B2055_PRG_GCHP26 0xC2 /* PRG GC HPVGA23 26 */ -#define B2055_PRG_GCHP27 0xC3 /* PRG GC HPVGA23 27 */ -#define B2055_PRG_GCHP28 0xC4 /* PRG GC HPVGA23 28 */ -#define B2055_PRG_GCHP29 0xC5 /* PRG GC HPVGA23 29 */ -#define B2055_PRG_GCHP30 0xC6 /* PRG GC HPVGA23 30 */ -#define B2055_C1_LNA_GAINBST 0xCD /* Core 1 LNA GAINBST */ -#define B2055_C1_B0NB_RSSIVCM 0xD2 /* Core 1 B0 narrow-band RSSI VCM */ -#define B2055_C1_GENSPARE2 0xD6 /* Core 1 GEN spare 2 */ -#define B2055_C2_LNA_GAINBST 0xD9 /* Core 2 LNA GAINBST */ -#define B2055_C2_B0NB_RSSIVCM 0xDE /* Core 2 B0 narrow-band RSSI VCM */ -#define B2055_C2_GENSPARE2 0xE2 /* Core 2 GEN spare 2 */ - -struct b43_nphy_channeltab_entry_rev2 { - /* The channel number */ - u8 channel; - /* The channel frequency in MHz */ - u16 freq; - /* An unknown value */ - u16 unk2; - /* Radio register values on channelswitch */ - u8 radio_pll_ref; - u8 radio_rf_pllmod0; - u8 radio_rf_pllmod1; - u8 radio_vco_captail; - u8 radio_vco_cal1; - u8 radio_vco_cal2; - u8 radio_pll_lfc1; - u8 radio_pll_lfr1; - u8 radio_pll_lfc2; - u8 radio_lgbuf_cenbuf; - u8 radio_lgen_tune1; - u8 radio_lgen_tune2; - u8 radio_c1_lgbuf_atune; - u8 radio_c1_lgbuf_gtune; - u8 radio_c1_rx_rfr1; - u8 radio_c1_tx_pgapadtn; - u8 radio_c1_tx_mxbgtrim; - u8 radio_c2_lgbuf_atune; - u8 radio_c2_lgbuf_gtune; - u8 radio_c2_rx_rfr1; - u8 radio_c2_tx_pgapadtn; - u8 radio_c2_tx_mxbgtrim; - /* PHY register values on channelswitch */ - struct b43_phy_n_sfo_cfg phy_regs; -}; - -/* Upload the default register value table. - * If "ghz5" is true, we upload the 5Ghz table. Otherwise the 2.4Ghz - * table is uploaded. If "ignore_uploadflag" is true, we upload any value - * and ignore the "UPLOAD" flag. */ -void b2055_upload_inittab(struct b43_wldev *dev, - bool ghz5, bool ignore_uploadflag); - -/* Get the NPHY Channel Switch Table entry for a channel. - * Returns NULL on failure to find an entry. */ -const struct b43_nphy_channeltab_entry_rev2 * -b43_nphy_get_chantabent_rev2(struct b43_wldev *dev, u8 channel); - -#endif /* B43_RADIO_2055_H_ */ diff --git a/drivers/net/wireless/b43/radio_2056.c b/drivers/net/wireless/b43/radio_2056.c deleted file mode 100644 index 2ce25607c60d..000000000000 --- a/drivers/net/wireless/b43/radio_2056.c +++ /dev/null @@ -1,10318 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11n 2056 radio device data tables - - Copyright (c) 2010 RafaÅ‚ MiÅ‚ecki - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "radio_2056.h" -#include "phy_common.h" - -struct b2056_inittab_entry { - /* Value to write if we use the 5GHz band. */ - u16 ghz5; - /* Value to write if we use the 2.4GHz band. */ - u16 ghz2; - /* Flags */ - u8 flags; -}; -#define B2056_INITTAB_ENTRY_OK 0x01 -#define B2056_INITTAB_UPLOAD 0x02 -#define UPLOAD .flags = B2056_INITTAB_ENTRY_OK | B2056_INITTAB_UPLOAD -#define NOUPLOAD .flags = B2056_INITTAB_ENTRY_OK - -struct b2056_inittabs_pts { - const struct b2056_inittab_entry *syn; - unsigned int syn_length; - const struct b2056_inittab_entry *tx; - unsigned int tx_length; - const struct b2056_inittab_entry *rx; - unsigned int rx_length; -}; - -static const struct b2056_inittab_entry b2056_inittab_phy_rev3_syn[] = { - [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, - [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, - [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, - [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, - [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, - [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_phy_rev3_tx[] = { - [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, - [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_TX_PA_SPARE2] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x002d, .ghz2 = 0x002d, NOUPLOAD, }, - [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, - [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, - [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0074, .ghz2 = 0x0074, UPLOAD, }, - [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, - [B2056_TX_TXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_phy_rev3_rx[] = { - [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0099, .ghz2 = 0x0099, NOUPLOAD, }, - [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0044, .ghz2 = 0x0044, UPLOAD, }, - [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, - [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0099, .ghz2 = 0x0099, NOUPLOAD, }, - [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_RX_TIA_IMISC] = { .ghz5 = 0x0057, .ghz2 = 0x0057, NOUPLOAD, }, - [B2056_RX_TIA_QMISC] = { .ghz5 = 0x0057, .ghz2 = 0x0057, NOUPLOAD, }, - [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, - [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, - [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, - [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, - [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_phy_rev4_syn[] = { - [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, - [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, - [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, - [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, - [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, - [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_phy_rev4_tx[] = { - [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, - [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_TX_PA_SPARE2] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x002d, .ghz2 = 0x002d, NOUPLOAD, }, - [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, - [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, - [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0072, .ghz2 = 0x0072, UPLOAD, }, - [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, - [B2056_TX_TXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_phy_rev4_rx[] = { - [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0044, .ghz2 = 0x0044, UPLOAD, }, - [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, - [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x002f, .ghz2 = 0x002f, UPLOAD, }, - [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, - [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, - [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, - [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev5_syn[] = { - [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, - [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, - [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, - [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, - [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, - [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev5_tx[] = { - [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, - [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_TX_PA_SPARE2] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x002d, .ghz2 = 0x002d, NOUPLOAD, }, - [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, - [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, - [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, - [B2056_TX_TXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, - [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, - [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0072, .ghz2 = 0x0072, UPLOAD, }, - [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0073, .ghz2 = 0x0073, UPLOAD, }, - [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0074, .ghz2 = 0x0074, UPLOAD, }, - [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev5_rx[] = { - [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, - [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, - [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, - [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, - [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, - [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev6_syn[] = { - [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, - [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_CP2] = { .ghz5 = 0x003f, .ghz2 = 0x003f, UPLOAD, }, - [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x002b, .ghz2 = 0x002b, UPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, - [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, - [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, - [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, - [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev6_tx[] = { - [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, - [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, - [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, - [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, - [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, - [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, - [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, - [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, - [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev6_rx[] = { - [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, - [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, - [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, - [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, - [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, - [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE3] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, - [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_syn[] = { - [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, - [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, - [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, - [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, - [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, - [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_tx[] = { - [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, - [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, - [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, - [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, - [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, - [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, - [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, - [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, - [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, - [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, - [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0072, .ghz2 = 0x0072, UPLOAD, }, - [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0073, .ghz2 = 0x0073, UPLOAD, }, - [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0074, .ghz2 = 0x0074, UPLOAD, }, - [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_rx[] = { - [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, - [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, - [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, - [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, - [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, - [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev8_syn[] = { - [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, - [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, - [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, - [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, - [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, - [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, - [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, - [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, - [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev8_tx[] = { - [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, - [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, - [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, - [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, - [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, - [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, - [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, - [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, - [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, - [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, - [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, - [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, - [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev8_rx[] = { - [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, - [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, - [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, - [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, - [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, - [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, - [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, - [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, - [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, - [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, - [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, - [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, - [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, - [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, - [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, - [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, - [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, - [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, - [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, - [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, - [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, - [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, - [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, - [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, - [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE3] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, - [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, - [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev11_syn[] = { - [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, - [B2056_SYN_PLL_CP2] = { .ghz5 = 0x003f, .ghz2 = 0x003f, UPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, - [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x002b, .ghz2 = 0x002b, UPLOAD, }, - [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, - [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev11_tx[] = { - [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, - [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, - [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, - [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, - [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, - [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, -}; - -static const struct b2056_inittab_entry b2056_inittab_radio_rev11_rx[] = { - [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, - [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, - [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, - [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, - [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, - [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, - [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, - [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, - [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, - [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, - [B2056_RX_RXSPARE3] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, -}; - -#define INITTABSPTS(prefix) \ - static const struct b2056_inittabs_pts prefix = { \ - .syn = prefix##_syn, \ - .syn_length = ARRAY_SIZE(prefix##_syn), \ - .tx = prefix##_tx, \ - .tx_length = ARRAY_SIZE(prefix##_tx), \ - .rx = prefix##_rx, \ - .rx_length = ARRAY_SIZE(prefix##_rx), \ - } - -INITTABSPTS(b2056_inittab_phy_rev3); -INITTABSPTS(b2056_inittab_phy_rev4); -INITTABSPTS(b2056_inittab_radio_rev5); -INITTABSPTS(b2056_inittab_radio_rev6); -INITTABSPTS(b2056_inittab_radio_rev7_9); -INITTABSPTS(b2056_inittab_radio_rev8); -INITTABSPTS(b2056_inittab_radio_rev11); - -#define RADIOREGS3(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ - r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ - r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, \ - r30, r31, r32, r33, r34, r35, r36) \ - .radio_syn_pll_vcocal1 = r00, \ - .radio_syn_pll_vcocal2 = r01, \ - .radio_syn_pll_refdiv = r02, \ - .radio_syn_pll_mmd2 = r03, \ - .radio_syn_pll_mmd1 = r04, \ - .radio_syn_pll_loopfilter1 = r05, \ - .radio_syn_pll_loopfilter2 = r06, \ - .radio_syn_pll_loopfilter3 = r07, \ - .radio_syn_pll_loopfilter4 = r08, \ - .radio_syn_pll_loopfilter5 = r09, \ - .radio_syn_reserved_addr27 = r10, \ - .radio_syn_reserved_addr28 = r11, \ - .radio_syn_reserved_addr29 = r12, \ - .radio_syn_logen_vcobuf1 = r13, \ - .radio_syn_logen_mixer2 = r14, \ - .radio_syn_logen_buf3 = r15, \ - .radio_syn_logen_buf4 = r16, \ - .radio_rx0_lnaa_tune = r17, \ - .radio_rx0_lnag_tune = r18, \ - .radio_tx0_intpaa_boost_tune = r19, \ - .radio_tx0_intpag_boost_tune = r20, \ - .radio_tx0_pada_boost_tune = r21, \ - .radio_tx0_padg_boost_tune = r22, \ - .radio_tx0_pgaa_boost_tune = r23, \ - .radio_tx0_pgag_boost_tune = r24, \ - .radio_tx0_mixa_boost_tune = r25, \ - .radio_tx0_mixg_boost_tune = r26, \ - .radio_rx1_lnaa_tune = r27, \ - .radio_rx1_lnag_tune = r28, \ - .radio_tx1_intpaa_boost_tune = r29, \ - .radio_tx1_intpag_boost_tune = r30, \ - .radio_tx1_pada_boost_tune = r31, \ - .radio_tx1_padg_boost_tune = r32, \ - .radio_tx1_pgaa_boost_tune = r33, \ - .radio_tx1_pgag_boost_tune = r34, \ - .radio_tx1_mixa_boost_tune = r35, \ - .radio_tx1_mixg_boost_tune = r36 - -#define PHYREGS(r0, r1, r2, r3, r4, r5) \ - .phy_regs.phy_bw1a = r0, \ - .phy_regs.phy_bw2 = r1, \ - .phy_regs.phy_bw3 = r2, \ - .phy_regs.phy_bw4 = r3, \ - .phy_regs.phy_bw5 = r4, \ - .phy_regs.phy_bw6 = r5 - -/* http://bcm-v4.sipsolutions.net/802.11/Radio/2056/ChannelTable */ -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_phy_rev3[] = { - { .freq = 4920, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), - }, - { .freq = 4930, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), - }, - { .freq = 4940, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), - }, - { .freq = 4950, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), - }, - { .freq = 4960, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), - }, - { .freq = 4970, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), - }, - { .freq = 4980, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), - }, - { .freq = 4990, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), - }, - { .freq = 5000, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), - }, - { .freq = 5010, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), - }, - { .freq = 5020, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), - }, - { .freq = 5030, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), - }, - { .freq = 5040, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), - }, - { .freq = 5050, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), - }, - { .freq = 5060, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), - }, - { .freq = 5070, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), - }, - { .freq = 5080, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), - }, - { .freq = 5090, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), - }, - { .freq = 5100, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00), - PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), - }, - { .freq = 5110, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00), - PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), - }, - { .freq = 5120, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00), - PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), - }, - { .freq = 5130, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00), - PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), - }, - { .freq = 5140, - RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00), - PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), - }, - { .freq = 5160, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00), - PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), - }, - { .freq = 5170, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00), - PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), - }, - { .freq = 5180, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00), - PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), - }, - { .freq = 5190, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00), - PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), - }, - { .freq = 5200, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xef, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0xef, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00), - PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), - }, - { .freq = 5210, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00), - PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), - }, - { .freq = 5220, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00), - PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), - }, - { .freq = 5230, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00), - PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), - }, - { .freq = 5240, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00), - PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), - }, - { .freq = 5250, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00), - PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), - }, - { .freq = 5260, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00), - PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), - }, - { .freq = 5270, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, - 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00), - PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), - }, - { .freq = 5280, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, - 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00), - PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), - }, - { .freq = 5290, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, - 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00), - PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), - }, - { .freq = 5300, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfc, 0x00), - PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), - }, - { .freq = 5310, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00), - PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), - }, - { .freq = 5320, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00), - PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), - }, - { .freq = 5330, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00), - PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), - }, - { .freq = 5340, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00), - PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), - }, - { .freq = 5350, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00), - PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), - }, - { .freq = 5360, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00), - PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), - }, - { .freq = 5370, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00), - PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), - }, - { .freq = 5380, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00), - PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), - }, - { .freq = 5390, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x8f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00), - PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), - }, - { .freq = 5400, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00), - PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), - }, - { .freq = 5410, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00), - PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), - }, - { .freq = 5420, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00), - PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), - }, - { .freq = 5430, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00), - PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), - }, - { .freq = 5440, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x7e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x7e, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00), - PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), - }, - { .freq = 5450, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x7d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x7d, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00), - PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), - }, - { .freq = 5460, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xf8, 0x00), - PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), - }, - { .freq = 5470, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xf8, 0x00), - PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), - }, - { .freq = 5480, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x5d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xf8, 0x00), - PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), - }, - { .freq = 5490, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x5c, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xf8, 0x00), - PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), - }, - { .freq = 5500, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x5c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), - }, - { .freq = 5510, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), - }, - { .freq = 5520, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), - }, - { .freq = 5530, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), - }, - { .freq = 5540, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), - }, - { .freq = 5550, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), - }, - { .freq = 5560, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x2b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x2b, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), - }, - { .freq = 5570, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x2a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x2a, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), - }, - { .freq = 5580, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), - }, - { .freq = 5590, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), - }, - { .freq = 5600, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), - }, - { .freq = 5610, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), - }, - { .freq = 5620, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), - }, - { .freq = 5630, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), - }, - { .freq = 5640, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), - }, - { .freq = 5650, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00), - PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), - }, - { .freq = 5660, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf6, 0x00), - PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), - }, - { .freq = 5670, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf6, 0x00), - PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), - }, - { .freq = 5680, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf6, 0x00), - PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), - }, - { .freq = 5690, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf6, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf6, 0x00), - PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), - }, - { .freq = 5700, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf6, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf6, 0x00), - PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), - }, - { .freq = 5710, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), - }, - { .freq = 5720, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5725, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5730, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), - }, - { .freq = 5735, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), - }, - { .freq = 5740, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), - }, - { .freq = 5745, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), - }, - { .freq = 5750, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), - }, - { .freq = 5755, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), - }, - { .freq = 5760, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5765, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5770, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), - }, - { .freq = 5775, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), - }, - { .freq = 5780, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), - }, - { .freq = 5785, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, - 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5790, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, - 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5795, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, - 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), - }, - { .freq = 5800, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), - }, - { .freq = 5805, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), - }, - { .freq = 5810, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5815, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5820, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), - }, - { .freq = 5825, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), - }, - { .freq = 5830, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), - }, - { .freq = 5840, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), - }, - { .freq = 5850, - RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00), - PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), - }, - { .freq = 5860, - RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf2, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf2, 0x00), - PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), - }, - { .freq = 5870, - RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf2, 0x00), - PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), - }, - { .freq = 5880, - RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf2, 0x00), - PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), - }, - { .freq = 5890, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf2, 0x00), - PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), - }, - { .freq = 5900, - RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, - 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x05, 0x00, 0xf2, 0x00), - PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), - }, - { .freq = 5910, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, - 0x00, 0xf2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x05, 0x00, 0xf2, 0x00), - PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), - }, - { .freq = 2412, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f), - PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), - }, - { .freq = 2417, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f), - PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), - }, - { .freq = 2422, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f), - PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), - }, - { .freq = 2427, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xfd, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x00, 0xfd, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f), - PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), - }, - { .freq = 2432, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xfb, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x00, 0xfb, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f), - PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), - }, - { .freq = 2437, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xfa, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f), - PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), - }, - { .freq = 2442, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf8, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f), - PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), - }, - { .freq = 2447, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf7, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x00, 0xf7, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f), - PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), - }, - { .freq = 2452, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf6, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x00, 0xf6, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f), - PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), - }, - { .freq = 2457, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf5, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0d, 0x00, 0xf5, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0d), - PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), - }, - { .freq = 2462, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0d, 0x00, 0xf4, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0d), - PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), - }, - { .freq = 2467, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf3, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0d, 0x00, 0xf3, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0d), - PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), - }, - { .freq = 2472, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0d, 0x00, 0xf2, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0d), - PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), - }, - { .freq = 2484, - RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf0, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0d, 0x00, 0xf0, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0d), - PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), - }, -}; - -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_phy_rev4[] = { - { .freq = 4920, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), - }, - { .freq = 4930, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), - }, - { .freq = 4940, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), - }, - { .freq = 4950, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), - }, - { .freq = 4960, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), - }, - { .freq = 4970, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), - }, - { .freq = 4980, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), - }, - { .freq = 4990, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), - }, - { .freq = 5000, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), - }, - { .freq = 5010, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), - }, - { .freq = 5020, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), - }, - { .freq = 5030, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), - }, - { .freq = 5040, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), - }, - { .freq = 5050, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), - }, - { .freq = 5060, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), - }, - { .freq = 5070, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), - }, - { .freq = 5080, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), - }, - { .freq = 5090, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00), - PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), - }, - { .freq = 5100, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00), - PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), - }, - { .freq = 5110, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00), - PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), - }, - { .freq = 5120, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00), - PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), - }, - { .freq = 5130, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00), - PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), - }, - { .freq = 5140, - RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00), - PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), - }, - { .freq = 5160, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00), - PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), - }, - { .freq = 5170, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00), - PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), - }, - { .freq = 5180, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00), - PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), - }, - { .freq = 5190, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00), - PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), - }, - { .freq = 5200, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xef, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0xef, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00), - PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), - }, - { .freq = 5210, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00), - PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), - }, - { .freq = 5220, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00), - PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), - }, - { .freq = 5230, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00), - PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), - }, - { .freq = 5240, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00), - PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), - }, - { .freq = 5250, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00), - PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), - }, - { .freq = 5260, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00), - PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), - }, - { .freq = 5270, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, - 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00), - PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), - }, - { .freq = 5280, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, - 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00), - PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), - }, - { .freq = 5290, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, - 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00), - PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), - }, - { .freq = 5300, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00), - PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), - }, - { .freq = 5310, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00), - PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), - }, - { .freq = 5320, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00), - PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), - }, - { .freq = 5330, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00), - PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), - }, - { .freq = 5340, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00), - PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), - }, - { .freq = 5350, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00), - PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), - }, - { .freq = 5360, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00), - PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), - }, - { .freq = 5370, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00), - PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), - }, - { .freq = 5380, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00), - PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), - }, - { .freq = 5390, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x8f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00), - PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), - }, - { .freq = 5400, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00), - PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), - }, - { .freq = 5410, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00), - PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), - }, - { .freq = 5420, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00), - PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), - }, - { .freq = 5430, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00), - PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), - }, - { .freq = 5440, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x7e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x7e, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00), - PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), - }, - { .freq = 5450, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x7d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x7d, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00), - PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), - }, - { .freq = 5460, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00), - PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), - }, - { .freq = 5470, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00), - PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), - }, - { .freq = 5480, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x5d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00), - PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), - }, - { .freq = 5490, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x5c, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00), - PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), - }, - { .freq = 5500, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x5c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x5c, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00), - PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), - }, - { .freq = 5510, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00), - PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), - }, - { .freq = 5520, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00), - PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), - }, - { .freq = 5530, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00), - PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), - }, - { .freq = 5540, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00), - PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), - }, - { .freq = 5550, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00), - PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), - }, - { .freq = 5560, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x2b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x2b, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00), - PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), - }, - { .freq = 5570, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x2a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x2a, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00), - PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), - }, - { .freq = 5580, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00), - PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), - }, - { .freq = 5590, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00), - PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), - }, - { .freq = 5600, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x1a, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x1a, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00), - PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), - }, - { .freq = 5610, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00), - PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), - }, - { .freq = 5620, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00), - PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), - }, - { .freq = 5630, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00), - PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), - }, - { .freq = 5640, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00), - PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), - }, - { .freq = 5650, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00), - PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), - }, - { .freq = 5660, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00), - PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), - }, - { .freq = 5670, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00), - PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), - }, - { .freq = 5680, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00), - PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), - }, - { .freq = 5690, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x07, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x07, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00), - PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), - }, - { .freq = 5700, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), - }, - { .freq = 5710, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), - }, - { .freq = 5720, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5725, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5730, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), - }, - { .freq = 5735, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), - }, - { .freq = 5740, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), - }, - { .freq = 5745, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), - }, - { .freq = 5750, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), - }, - { .freq = 5755, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), - }, - { .freq = 5760, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5765, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5770, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), - }, - { .freq = 5775, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), - }, - { .freq = 5780, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), - }, - { .freq = 5785, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, - 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5790, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, - 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5795, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, - 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00), - PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), - }, - { .freq = 5800, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), - }, - { .freq = 5805, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), - }, - { .freq = 5810, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5815, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5820, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), - }, - { .freq = 5825, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), - }, - { .freq = 5830, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), - }, - { .freq = 5840, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), - }, - { .freq = 5850, - RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), - }, - { .freq = 5860, - RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), - }, - { .freq = 5870, - RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), - }, - { .freq = 5880, - RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), - }, - { .freq = 5890, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00), - PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), - }, - { .freq = 5900, - RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf0, 0x00), - PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), - }, - { .freq = 5910, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf0, 0x00), - PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), - }, - { .freq = 2412, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), - }, - { .freq = 2417, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), - }, - { .freq = 2422, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), - }, - { .freq = 2427, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xfd, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xfd, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), - }, - { .freq = 2432, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xfb, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xfb, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), - }, - { .freq = 2437, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xfa, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), - }, - { .freq = 2442, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf8, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xf8, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), - }, - { .freq = 2447, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf7, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xf7, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), - }, - { .freq = 2452, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf6, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xf6, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), - }, - { .freq = 2457, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf5, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xf5, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), - }, - { .freq = 2462, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xf4, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), - }, - { .freq = 2467, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf3, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xf3, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), - }, - { .freq = 2472, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xf2, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), - }, - { .freq = 2484, - RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x00, 0xf0, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e), - PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), - }, -}; - -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev5[] = { - { .freq = 4920, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), - }, - { .freq = 4930, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), - }, - { .freq = 4940, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), - }, - { .freq = 4950, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), - }, - { .freq = 4960, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), - }, - { .freq = 4970, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), - }, - { .freq = 4980, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), - }, - { .freq = 4990, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), - }, - { .freq = 5000, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), - }, - { .freq = 5010, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), - }, - { .freq = 5020, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), - }, - { .freq = 5030, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), - }, - { .freq = 5040, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), - }, - { .freq = 5050, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), - }, - { .freq = 5060, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), - }, - { .freq = 5070, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), - }, - { .freq = 5080, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), - }, - { .freq = 5090, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), - }, - { .freq = 5100, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), - }, - { .freq = 5110, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), - }, - { .freq = 5120, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), - }, - { .freq = 5130, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, - 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), - }, - { .freq = 5140, - RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, - 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), - }, - { .freq = 5160, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), - }, - { .freq = 5170, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), - }, - { .freq = 5180, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), - }, - { .freq = 5190, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), - }, - { .freq = 5200, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), - }, - { .freq = 5210, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), - }, - { .freq = 5220, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), - }, - { .freq = 5230, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x6e, 0x00), - PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), - }, - { .freq = 5240, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x6d, 0x00), - PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), - }, - { .freq = 5250, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x6d, 0x00), - PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), - }, - { .freq = 5260, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x6d, 0x00), - PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), - }, - { .freq = 5270, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, - 0xff, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), - }, - { .freq = 5280, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, - 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), - }, - { .freq = 5290, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, - 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), - }, - { .freq = 5300, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), - }, - { .freq = 5310, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), - }, - { .freq = 5320, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), - }, - { .freq = 5330, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6b, 0x00), - PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), - }, - { .freq = 5340, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6b, 0x00), - PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), - }, - { .freq = 5350, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x6b, 0x00), - PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), - }, - { .freq = 5360, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x6b, 0x00), - PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), - }, - { .freq = 5370, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x5b, 0x00), - PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), - }, - { .freq = 5380, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x5a, 0x00), - PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), - }, - { .freq = 5390, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, - 0xff, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x5a, 0x00), - PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), - }, - { .freq = 5400, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x5a, 0x00), - PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), - }, - { .freq = 5410, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x5a, 0x00), - PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), - }, - { .freq = 5420, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x5a, 0x00), - PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), - }, - { .freq = 5430, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xc8, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x59, 0x00), - PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), - }, - { .freq = 5440, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x59, 0x00), - PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), - }, - { .freq = 5450, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x59, 0x00), - PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), - }, - { .freq = 5460, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x69, 0x00), - PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), - }, - { .freq = 5470, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x69, 0x00), - PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), - }, - { .freq = 5480, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), - }, - { .freq = 5490, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, - 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), - }, - { .freq = 5500, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x78, 0x00), - PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), - }, - { .freq = 5510, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x78, 0x00), - PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), - }, - { .freq = 5520, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x78, 0x00), - PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), - }, - { .freq = 5530, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x78, 0x00), - PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), - }, - { .freq = 5540, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x77, 0x00), - PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), - }, - { .freq = 5550, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x77, 0x00), - PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), - }, - { .freq = 5560, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x77, 0x00), - PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), - }, - { .freq = 5570, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x76, 0x00), - PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), - }, - { .freq = 5580, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x76, 0x00), - PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), - }, - { .freq = 5590, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, - 0x84, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x76, 0x00), - PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), - }, - { .freq = 5600, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x76, 0x00), - PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), - }, - { .freq = 5610, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x76, 0x00), - PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), - }, - { .freq = 5620, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x76, 0x00), - PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), - }, - { .freq = 5630, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x76, 0x00), - PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), - }, - { .freq = 5640, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x75, 0x00), - PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), - }, - { .freq = 5650, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x75, 0x00), - PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), - }, - { .freq = 5660, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x75, 0x00), - PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), - }, - { .freq = 5670, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x74, 0x00), - PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), - }, - { .freq = 5680, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x74, 0x00), - PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), - }, - { .freq = 5690, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, - 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x74, 0x00), - PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), - }, - { .freq = 5700, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x74, 0x00), - PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), - }, - { .freq = 5710, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x74, 0x00), - PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), - }, - { .freq = 5720, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x74, 0x00), - PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5725, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x74, 0x00), - PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5730, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x84, 0x00), - PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), - }, - { .freq = 5735, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x83, 0x00), - PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), - }, - { .freq = 5740, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x83, 0x00), - PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), - }, - { .freq = 5745, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x83, 0x00), - PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), - }, - { .freq = 5750, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x83, 0x00), - PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), - }, - { .freq = 5755, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x83, 0x00), - PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), - }, - { .freq = 5760, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x83, 0x00), - PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5765, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5770, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), - }, - { .freq = 5775, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), - }, - { .freq = 5780, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, - 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), - }, - { .freq = 5785, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, - 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5790, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, - 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5795, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, - 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), - }, - { .freq = 5800, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), - }, - { .freq = 5805, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), - }, - { .freq = 5810, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5815, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5820, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), - }, - { .freq = 5825, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x82, 0x00), - PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), - }, - { .freq = 5830, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x72, 0x00), - PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), - }, - { .freq = 5840, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x72, 0x00), - PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), - }, - { .freq = 5850, - RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x72, 0x00), - PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), - }, - { .freq = 5860, - RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x72, 0x00), - PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), - }, - { .freq = 5870, - RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x71, 0x00), - PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), - }, - { .freq = 5880, - RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x71, 0x00), - PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), - }, - { .freq = 5890, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x71, 0x00), - PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), - }, - { .freq = 5900, - RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x71, 0x00), - PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), - }, - { .freq = 5910, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x71, 0x00), - PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), - }, - { .freq = 2412, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0b, 0x00, 0x1f, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0b), - PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), - }, - { .freq = 2417, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0a, 0x00, 0x1f, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0a), - PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), - }, - { .freq = 2422, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0a), - PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), - }, - { .freq = 2427, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x0d, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a), - PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), - }, - { .freq = 2432, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a), - PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), - }, - { .freq = 2437, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a), - PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), - }, - { .freq = 2442, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a), - PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), - }, - { .freq = 2447, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x09, 0x00, 0x08, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x09), - PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), - }, - { .freq = 2452, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x07, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x09, 0x00, 0x07, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x09), - PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), - }, - { .freq = 2457, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x09), - PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), - }, - { .freq = 2462, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x09, 0x00, 0x05, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x09), - PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), - }, - { .freq = 2467, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08), - PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), - }, - { .freq = 2472, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08), - PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), - }, - { .freq = 2484, - RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08), - PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), - }, -}; - -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev6[] = { - { .freq = 4920, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), - }, - { .freq = 4930, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), - }, - { .freq = 4940, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), - }, - { .freq = 4950, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), - }, - { .freq = 4960, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), - }, - { .freq = 4970, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), - }, - { .freq = 4980, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), - }, - { .freq = 4990, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), - }, - { .freq = 5000, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), - }, - { .freq = 5010, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), - }, - { .freq = 5020, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), - }, - { .freq = 5030, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), - }, - { .freq = 5040, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), - }, - { .freq = 5050, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), - }, - { .freq = 5060, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), - }, - { .freq = 5070, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), - }, - { .freq = 5080, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), - }, - { .freq = 5090, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), - }, - { .freq = 5100, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), - }, - { .freq = 5110, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), - }, - { .freq = 5120, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), - }, - { .freq = 5130, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), - }, - { .freq = 5140, - RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), - }, - { .freq = 5160, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), - }, - { .freq = 5170, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), - }, - { .freq = 5180, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), - }, - { .freq = 5190, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), - }, - { .freq = 5200, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), - }, - { .freq = 5210, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), - }, - { .freq = 5220, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), - }, - { .freq = 5230, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), - }, - { .freq = 5240, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), - }, - { .freq = 5250, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), - }, - { .freq = 5260, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, - 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), - }, - { .freq = 5270, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, - 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), - }, - { .freq = 5280, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), - }, - { .freq = 5290, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), - }, - { .freq = 5300, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), - }, - { .freq = 5310, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), - }, - { .freq = 5320, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), - }, - { .freq = 5330, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), - }, - { .freq = 5340, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), - }, - { .freq = 5350, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), - }, - { .freq = 5360, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), - }, - { .freq = 5370, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), - }, - { .freq = 5380, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), - }, - { .freq = 5390, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), - }, - { .freq = 5400, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), - }, - { .freq = 5410, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), - }, - { .freq = 5420, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), - }, - { .freq = 5430, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, - 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), - }, - { .freq = 5440, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), - }, - { .freq = 5450, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), - }, - { .freq = 5460, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), - }, - { .freq = 5470, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), - }, - { .freq = 5480, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), - }, - { .freq = 5490, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), - }, - { .freq = 5500, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), - }, - { .freq = 5510, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), - }, - { .freq = 5520, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), - }, - { .freq = 5530, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, - 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), - }, - { .freq = 5540, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, - 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), - }, - { .freq = 5550, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), - }, - { .freq = 5560, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), - }, - { .freq = 5570, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), - }, - { .freq = 5580, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, - 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), - }, - { .freq = 5590, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, - 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), - }, - { .freq = 5600, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), - }, - { .freq = 5610, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), - }, - { .freq = 5620, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), - }, - { .freq = 5630, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), - }, - { .freq = 5640, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), - }, - { .freq = 5650, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), - }, - { .freq = 5660, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), - }, - { .freq = 5670, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), - }, - { .freq = 5680, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), - }, - { .freq = 5690, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), - }, - { .freq = 5700, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), - }, - { .freq = 5710, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), - }, - { .freq = 5720, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5725, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5730, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), - }, - { .freq = 5735, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00), - PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), - }, - { .freq = 5740, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00), - PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), - }, - { .freq = 5745, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00), - PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), - }, - { .freq = 5750, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6d, 0x00), - PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), - }, - { .freq = 5755, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00), - PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), - }, - { .freq = 5760, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, - 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00), - PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5765, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00), - PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5770, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), - }, - { .freq = 5775, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), - }, - { .freq = 5780, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), - }, - { .freq = 5785, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5790, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5795, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), - }, - { .freq = 5800, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), - }, - { .freq = 5805, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), - }, - { .freq = 5810, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5815, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5820, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), - }, - { .freq = 5825, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00), - PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), - }, - { .freq = 5830, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00), - PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), - }, - { .freq = 5840, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00), - PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), - }, - { .freq = 5850, - RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00), - PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), - }, - { .freq = 5860, - RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00), - PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), - }, - { .freq = 5870, - RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), - }, - { .freq = 5880, - RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), - }, - { .freq = 5890, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), - }, - { .freq = 5900, - RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), - }, - { .freq = 5910, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), - }, - { .freq = 2412, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a), - PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), - }, - { .freq = 2417, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a), - PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), - }, - { .freq = 2422, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a), - PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), - }, - { .freq = 2427, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x57, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), - }, - { .freq = 2432, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x56, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), - }, - { .freq = 2437, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x46, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), - }, - { .freq = 2442, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x45, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), - }, - { .freq = 2447, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09), - PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), - }, - { .freq = 2452, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x00, 0x23, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09), - PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), - }, - { .freq = 2457, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x00, 0x12, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09), - PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), - }, - { .freq = 2462, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), - }, - { .freq = 2467, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), - }, - { .freq = 2472, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), - }, - { .freq = 2484, - RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), - }, -}; - -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev7_9[] = { - { .freq = 4920, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), - }, - { .freq = 4930, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), - }, - { .freq = 4940, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), - }, - { .freq = 4950, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), - }, - { .freq = 4960, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), - }, - { .freq = 4970, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), - }, - { .freq = 4980, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), - }, - { .freq = 4990, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), - }, - { .freq = 5000, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), - }, - { .freq = 5010, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), - }, - { .freq = 5020, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), - }, - { .freq = 5030, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), - }, - { .freq = 5040, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), - }, - { .freq = 5050, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), - }, - { .freq = 5060, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), - }, - { .freq = 5070, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), - }, - { .freq = 5080, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), - }, - { .freq = 5090, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), - }, - { .freq = 5100, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), - }, - { .freq = 5110, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), - }, - { .freq = 5120, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), - }, - { .freq = 5130, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, - 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), - }, - { .freq = 5140, - RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, - 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), - }, - { .freq = 5160, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), - }, - { .freq = 5170, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), - }, - { .freq = 5180, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), - }, - { .freq = 5190, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), - }, - { .freq = 5200, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), - }, - { .freq = 5210, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), - }, - { .freq = 5220, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xfe, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x6e, 0x00), - PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), - }, - { .freq = 5230, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xee, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x6e, 0x00), - PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), - }, - { .freq = 5240, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xee, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x6d, 0x00), - PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), - }, - { .freq = 5250, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xed, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x6d, 0x00), - PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), - }, - { .freq = 5260, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, - 0xed, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x6d, 0x00), - PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), - }, - { .freq = 5270, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, - 0xed, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), - }, - { .freq = 5280, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), - }, - { .freq = 5290, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), - }, - { .freq = 5300, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), - }, - { .freq = 5310, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), - }, - { .freq = 5320, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdb, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6c, 0x00), - PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), - }, - { .freq = 5330, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xcb, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6b, 0x00), - PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), - }, - { .freq = 5340, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xca, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x6b, 0x00), - PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), - }, - { .freq = 5350, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xca, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x6b, 0x00), - PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), - }, - { .freq = 5360, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x6b, 0x00), - PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), - }, - { .freq = 5370, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x7b, 0x00), - PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), - }, - { .freq = 5380, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x7a, 0x00), - PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), - }, - { .freq = 5390, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x7a, 0x00), - PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), - }, - { .freq = 5400, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x7a, 0x00), - PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), - }, - { .freq = 5410, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb7, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x7a, 0x00), - PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), - }, - { .freq = 5420, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xa7, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x7a, 0x00), - PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), - }, - { .freq = 5430, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, - 0xa6, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x79, 0x00), - PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), - }, - { .freq = 5440, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0xa6, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x79, 0x00), - PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), - }, - { .freq = 5450, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x79, 0x00), - PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), - }, - { .freq = 5460, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x79, 0x00), - PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), - }, - { .freq = 5470, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x94, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x79, 0x00), - PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), - }, - { .freq = 5480, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x78, 0x00), - PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), - }, - { .freq = 5490, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x83, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x78, 0x00), - PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), - }, - { .freq = 5500, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x78, 0x00), - PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), - }, - { .freq = 5510, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x78, 0x00), - PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), - }, - { .freq = 5520, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x72, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x78, 0x00), - PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), - }, - { .freq = 5530, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, - 0x72, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x78, 0x00), - PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), - }, - { .freq = 5540, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, - 0x71, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x77, 0x00), - PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), - }, - { .freq = 5550, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x77, 0x00), - PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), - }, - { .freq = 5560, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x77, 0x00), - PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), - }, - { .freq = 5570, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x76, 0x00), - PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), - }, - { .freq = 5580, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, - 0x60, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x86, 0x00), - PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), - }, - { .freq = 5590, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, - 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x86, 0x00), - PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), - }, - { .freq = 5600, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x86, 0x00), - PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), - }, - { .freq = 5610, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x86, 0x00), - PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), - }, - { .freq = 5620, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x86, 0x00), - PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), - }, - { .freq = 5630, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x86, 0x00), - PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), - }, - { .freq = 5640, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x85, 0x00), - PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), - }, - { .freq = 5650, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x85, 0x00), - PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), - }, - { .freq = 5660, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x85, 0x00), - PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), - }, - { .freq = 5670, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x84, 0x00), - PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), - }, - { .freq = 5680, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x84, 0x00), - PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), - }, - { .freq = 5690, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00), - PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), - }, - { .freq = 5700, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00), - PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), - }, - { .freq = 5710, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00), - PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), - }, - { .freq = 5720, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00), - PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5725, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00), - PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5730, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00), - PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), - }, - { .freq = 5735, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00), - PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), - }, - { .freq = 5740, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00), - PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), - }, - { .freq = 5745, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00), - PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), - }, - { .freq = 5750, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00), - PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), - }, - { .freq = 5755, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x10, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00), - PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), - }, - { .freq = 5760, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, - 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00), - PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5765, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, - 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5770, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), - }, - { .freq = 5775, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), - }, - { .freq = 5780, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), - }, - { .freq = 5785, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5790, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5795, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), - }, - { .freq = 5800, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), - }, - { .freq = 5805, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), - }, - { .freq = 5810, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5815, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5820, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), - }, - { .freq = 5825, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), - }, - { .freq = 5830, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), - }, - { .freq = 5840, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), - }, - { .freq = 5850, - RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), - }, - { .freq = 5860, - RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00), - PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), - }, - { .freq = 5870, - RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00), - PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), - }, - { .freq = 5880, - RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00), - PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), - }, - { .freq = 5890, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00), - PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), - }, - { .freq = 5900, - RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00), - PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), - }, - { .freq = 5910, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00), - PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), - }, - { .freq = 2412, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0b, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0b), - PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), - }, - { .freq = 2417, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0a), - PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), - }, - { .freq = 2422, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0a), - PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), - }, - { .freq = 2427, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a), - PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), - }, - { .freq = 2432, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a), - PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), - }, - { .freq = 2437, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a), - PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), - }, - { .freq = 2442, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x66, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a), - PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), - }, - { .freq = 2447, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x09), - PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), - }, - { .freq = 2452, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x09), - PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), - }, - { .freq = 2457, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x09), - PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), - }, - { .freq = 2462, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x09), - PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), - }, - { .freq = 2467, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x00, 0x22, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08), - PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), - }, - { .freq = 2472, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08), - PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), - }, - { .freq = 2484, - RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08), - PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), - }, -}; - -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev8[] = { - { .freq = 4920, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), - }, - { .freq = 4930, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), - }, - { .freq = 4940, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), - }, - { .freq = 4950, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), - }, - { .freq = 4960, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), - }, - { .freq = 4970, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), - }, - { .freq = 4980, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), - }, - { .freq = 4990, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), - }, - { .freq = 5000, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), - }, - { .freq = 5010, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), - }, - { .freq = 5020, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), - }, - { .freq = 5030, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), - }, - { .freq = 5040, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), - }, - { .freq = 5050, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), - }, - { .freq = 5060, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), - }, - { .freq = 5070, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), - }, - { .freq = 5080, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), - }, - { .freq = 5090, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), - }, - { .freq = 5100, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), - }, - { .freq = 5110, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), - }, - { .freq = 5120, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), - }, - { .freq = 5130, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), - }, - { .freq = 5140, - RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), - }, - { .freq = 5160, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), - }, - { .freq = 5170, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), - }, - { .freq = 5180, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), - }, - { .freq = 5190, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), - }, - { .freq = 5200, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), - }, - { .freq = 5210, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), - }, - { .freq = 5220, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), - }, - { .freq = 5230, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), - }, - { .freq = 5240, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), - }, - { .freq = 5250, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), - }, - { .freq = 5260, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, - 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), - }, - { .freq = 5270, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, - 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), - }, - { .freq = 5280, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), - }, - { .freq = 5290, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), - }, - { .freq = 5300, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), - }, - { .freq = 5310, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), - }, - { .freq = 5320, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), - }, - { .freq = 5330, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), - }, - { .freq = 5340, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), - }, - { .freq = 5350, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), - }, - { .freq = 5360, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), - }, - { .freq = 5370, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), - }, - { .freq = 5380, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), - }, - { .freq = 5390, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), - }, - { .freq = 5400, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), - }, - { .freq = 5410, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), - }, - { .freq = 5420, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), - }, - { .freq = 5430, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, - 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), - }, - { .freq = 5440, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), - }, - { .freq = 5450, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), - }, - { .freq = 5460, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), - }, - { .freq = 5470, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), - }, - { .freq = 5480, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), - }, - { .freq = 5490, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), - }, - { .freq = 5500, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), - }, - { .freq = 5510, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), - }, - { .freq = 5520, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), - }, - { .freq = 5530, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, - 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), - }, - { .freq = 5540, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, - 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), - }, - { .freq = 5550, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), - }, - { .freq = 5560, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), - }, - { .freq = 5570, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), - }, - { .freq = 5580, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, - 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), - }, - { .freq = 5590, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, - 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), - }, - { .freq = 5600, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), - }, - { .freq = 5610, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), - }, - { .freq = 5620, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), - }, - { .freq = 5630, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), - }, - { .freq = 5640, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), - }, - { .freq = 5650, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), - }, - { .freq = 5660, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), - }, - { .freq = 5670, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), - }, - { .freq = 5680, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), - }, - { .freq = 5690, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), - }, - { .freq = 5700, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), - }, - { .freq = 5710, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), - }, - { .freq = 5720, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5725, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), - }, - { .freq = 5730, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), - }, - { .freq = 5735, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00), - PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), - }, - { .freq = 5740, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00), - PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), - }, - { .freq = 5745, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00), - PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), - }, - { .freq = 5750, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6d, 0x00), - PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), - }, - { .freq = 5755, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00), - PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), - }, - { .freq = 5760, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, - 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00), - PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5765, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00), - PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), - }, - { .freq = 5770, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), - }, - { .freq = 5775, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), - }, - { .freq = 5780, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), - }, - { .freq = 5785, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5790, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), - }, - { .freq = 5795, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), - }, - { .freq = 5800, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), - }, - { .freq = 5805, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), - }, - { .freq = 5810, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5815, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), - }, - { .freq = 5820, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), - }, - { .freq = 5825, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, - 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00), - PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), - }, - { .freq = 5830, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00), - PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), - }, - { .freq = 5840, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00), - PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), - }, - { .freq = 5850, - RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00), - PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), - }, - { .freq = 5860, - RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00), - PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), - }, - { .freq = 5870, - RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), - }, - { .freq = 5880, - RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), - }, - { .freq = 5890, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), - }, - { .freq = 5900, - RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), - }, - { .freq = 5910, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), - }, - { .freq = 2412, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a), - PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), - }, - { .freq = 2417, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a), - PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), - }, - { .freq = 2422, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a), - PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), - }, - { .freq = 2427, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), - }, - { .freq = 2432, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), - }, - { .freq = 2437, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), - }, - { .freq = 2442, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), - }, - { .freq = 2447, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09), - PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), - }, - { .freq = 2452, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09), - PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), - }, - { .freq = 2457, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09), - PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), - }, - { .freq = 2462, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), - }, - { .freq = 2467, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), - }, - { .freq = 2472, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), - }, - { .freq = 2484, - RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, - 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), - }, -}; - -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev11[] = { - { - .freq = 4920, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), - }, - { - .freq = 4930, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), - }, - { - .freq = 4940, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), - }, - { - .freq = 4950, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), - }, - { - .freq = 4960, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), - }, - { - .freq = 4970, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), - }, - { - .freq = 4980, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), - }, - { - .freq = 4990, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), - }, - { - .freq = 5000, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), - }, - { - .freq = 5010, - RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), - }, - { - .freq = 5020, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), - }, - { - .freq = 5030, - RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), - }, - { - .freq = 5040, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), - }, - { - .freq = 5050, - RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), - }, - { - .freq = 5060, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), - }, - { - .freq = 5070, - RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), - }, - { - .freq = 5080, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), - }, - { - .freq = 5090, - RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), - }, - { - .freq = 5100, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), - }, - { - .freq = 5110, - RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), - }, - { - .freq = 5120, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), - }, - { - .freq = 5130, - RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), - }, - { - .freq = 5140, - RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00), - PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), - }, - { - .freq = 5160, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), - }, - { - .freq = 5170, - RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), - }, - { - .freq = 5180, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00), - PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), - }, - { - .freq = 5190, - RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), - }, - { - .freq = 5200, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), - }, - { - .freq = 5210, - RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, - 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), - }, - { - .freq = 5220, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), - }, - { - .freq = 5230, - RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), - }, - { - .freq = 5240, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), - }, - { - .freq = 5250, - RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, - 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), - }, - { - .freq = 5260, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, - 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00), - PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), - }, - { - .freq = 5270, - RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, - 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), - }, - { - .freq = 5280, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), - }, - { - .freq = 5290, - RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), - }, - { - .freq = 5300, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), - }, - { - .freq = 5310, - RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), - }, - { - .freq = 5320, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, - 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00), - PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), - }, - { - .freq = 5330, - RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), - }, - { - .freq = 5340, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, - 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), - }, - { - .freq = 5350, - RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00), - PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), - }, - { - .freq = 5360, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), - }, - { - .freq = 5370, - RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, - 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), - }, - { - .freq = 5380, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), - }, - { - .freq = 5390, - RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), - }, - { - .freq = 5400, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), - }, - { - .freq = 5410, - RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), - }, - { - .freq = 5420, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, - 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), - }, - { - .freq = 5430, - RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, - 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00), - PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), - }, - { - .freq = 5440, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), - }, - { - .freq = 5450, - RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), - }, - { - .freq = 5460, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), - }, - { - .freq = 5470, - RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, - 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), - }, - { - .freq = 5480, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), - }, - { - .freq = 5490, - RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), - }, - { - .freq = 5500, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), - }, - { - .freq = 5510, - RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), - }, - { - .freq = 5520, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, - 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), - }, - { - .freq = 5530, - RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, - 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), - }, - { - .freq = 5540, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, - 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), - }, - { - .freq = 5550, - RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), - }, - { - .freq = 5560, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), - }, - { - .freq = 5570, - RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, - 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00), - PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), - }, - { - .freq = 5580, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, - 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), - }, - { - .freq = 5590, - RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, - 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), - }, - { - .freq = 5600, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), - }, - { - .freq = 5610, - RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00), - PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), - }, - { - .freq = 5620, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, - 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), - }, - { - .freq = 5630, - RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), - }, - { - .freq = 5640, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), - }, - { - .freq = 5650, - RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00), - PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), - }, - { - .freq = 5660, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), - }, - { - .freq = 5670, - RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, - 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), - }, - { - .freq = 5680, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), - }, - { - .freq = 5690, - RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00), - PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), - }, - { - .freq = 5700, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), - }, - { - .freq = 5710, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), - }, - { - .freq = 5720, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), - }, - { - .freq = 5725, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x05, 0x05, 0x02, - 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), - }, - { - .freq = 5730, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00), - PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), - }, - { - .freq = 5735, - RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x05, 0x05, 0x02, - 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00), - PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), - }, - { - .freq = 5740, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00), - PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), - }, - { - .freq = 5745, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x05, 0x05, 0x02, - 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00), - PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), - }, - { - .freq = 5750, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6d, 0x00), - PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), - }, - { - .freq = 5755, - RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x05, 0x05, 0x02, - 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, - 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00), - PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), - }, - { - .freq = 5760, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, - 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00), - PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), - }, - { - .freq = 5765, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x05, 0x05, 0x02, - 0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00), - PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), - }, - { - .freq = 5770, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), - }, - { - .freq = 5775, - RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x05, 0x05, 0x02, - 0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), - }, - { - .freq = 5780, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, - 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), - }, - { - .freq = 5785, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x05, 0x05, 0x02, - 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), - }, - { - .freq = 5790, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), - }, - { - .freq = 5795, - RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x05, 0x05, 0x02, - 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), - }, - { - .freq = 5800, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00), - PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), - }, - { - .freq = 5805, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x05, 0x05, 0x02, - 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), - }, - { - .freq = 5810, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), - }, - { - .freq = 5815, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x05, 0x05, 0x02, - 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), - }, - { - .freq = 5820, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00), - PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), - }, - { - .freq = 5825, - RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x05, 0x05, 0x02, - 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00), - PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), - }, - { - .freq = 5830, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00), - PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), - }, - { - .freq = 5840, - RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00), - PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), - }, - { - .freq = 5850, - RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00), - PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), - }, - { - .freq = 5860, - RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00), - PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), - }, - { - .freq = 5870, - RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), - }, - { - .freq = 5880, - RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), - }, - { - .freq = 5890, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), - }, - { - .freq = 5900, - RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), - }, - { - .freq = 5910, - RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x02, - 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00), - PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), - }, - { - .freq = 2412, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a), - PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), - }, - { - .freq = 2417, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a), - PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), - }, - { - .freq = 2422, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a), - PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), - }, - { - .freq = 2427, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), - }, - { - .freq = 2432, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), - }, - { - .freq = 2437, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), - }, - { - .freq = 2442, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a), - PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), - }, - { - .freq = 2447, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09), - PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), - }, - { - .freq = 2452, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09), - PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), - }, - { - .freq = 2457, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09), - PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), - }, - { - .freq = 2462, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), - }, - { - .freq = 2467, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), - }, - { - .freq = 2472, - RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), - }, - { - .freq = 2484, - RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x06, 0x06, 0x04, - 0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09), - PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), - }, -}; - -static const struct b2056_inittabs_pts -*b43_nphy_get_inittabs_rev3(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - switch (dev->phy.rev) { - case 3: - return &b2056_inittab_phy_rev3; - case 4: - return &b2056_inittab_phy_rev4; - default: - switch (phy->radio_rev) { - case 5: - return &b2056_inittab_radio_rev5; - case 6: - return &b2056_inittab_radio_rev6; - case 7: - case 9: - return &b2056_inittab_radio_rev7_9; - case 8: - return &b2056_inittab_radio_rev8; - case 11: - return &b2056_inittab_radio_rev11; - } - } - - return NULL; -} - -static void b2056_upload_inittab(struct b43_wldev *dev, bool ghz5, - bool ignore_uploadflag, u16 routing, - const struct b2056_inittab_entry *e, - unsigned int length) -{ - unsigned int i; - u16 value; - - for (i = 0; i < length; i++, e++) { - if (!(e->flags & B2056_INITTAB_ENTRY_OK)) - continue; - if ((e->flags & B2056_INITTAB_UPLOAD) || ignore_uploadflag) { - if (ghz5) - value = e->ghz5; - else - value = e->ghz2; - b43_radio_write(dev, routing | i, value); - } - } -} - -void b2056_upload_inittabs(struct b43_wldev *dev, - bool ghz5, bool ignore_uploadflag) -{ - const struct b2056_inittabs_pts *pts; - - pts = b43_nphy_get_inittabs_rev3(dev); - if (!pts) { - B43_WARN_ON(1); - return; - } - - b2056_upload_inittab(dev, ghz5, ignore_uploadflag, - B2056_SYN, pts->syn, pts->syn_length); - b2056_upload_inittab(dev, ghz5, ignore_uploadflag, - B2056_TX0, pts->tx, pts->tx_length); - b2056_upload_inittab(dev, ghz5, ignore_uploadflag, - B2056_TX1, pts->tx, pts->tx_length); - b2056_upload_inittab(dev, ghz5, ignore_uploadflag, - B2056_RX0, pts->rx, pts->rx_length); - b2056_upload_inittab(dev, ghz5, ignore_uploadflag, - B2056_RX1, pts->rx, pts->rx_length); -} - -void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5) -{ - const struct b2056_inittabs_pts *pts; - const struct b2056_inittab_entry *e; - - pts = b43_nphy_get_inittabs_rev3(dev); - if (!pts) { - B43_WARN_ON(1); - return; - } - - e = &pts->syn[B2056_SYN_PLL_CP2]; - - b43_radio_write(dev, B2056_SYN_PLL_CP2, ghz5 ? e->ghz5 : e->ghz2); -} - -const struct b43_nphy_channeltab_entry_rev3 * -b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq) -{ - struct b43_phy *phy = &dev->phy; - const struct b43_nphy_channeltab_entry_rev3 *e; - unsigned int length, i; - - switch (phy->rev) { - case 3: - e = b43_nphy_channeltab_phy_rev3; - length = ARRAY_SIZE(b43_nphy_channeltab_phy_rev3); - break; - case 4: - e = b43_nphy_channeltab_phy_rev4; - length = ARRAY_SIZE(b43_nphy_channeltab_phy_rev4); - break; - default: - switch (phy->radio_rev) { - case 5: - e = b43_nphy_channeltab_radio_rev5; - length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev5); - break; - case 6: - e = b43_nphy_channeltab_radio_rev6; - length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev6); - break; - case 7: - case 9: - e = b43_nphy_channeltab_radio_rev7_9; - length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev7_9); - break; - case 8: - e = b43_nphy_channeltab_radio_rev8; - length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev8); - break; - case 11: - e = b43_nphy_channeltab_radio_rev11; - length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev11); - break; - default: - B43_WARN_ON(1); - return NULL; - } - } - - for (i = 0; i < length; i++, e++) { - if (e->freq == freq) - return e; - } - - return NULL; -} diff --git a/drivers/net/wireless/b43/radio_2056.h b/drivers/net/wireless/b43/radio_2056.h deleted file mode 100644 index 5b86673459fa..000000000000 --- a/drivers/net/wireless/b43/radio_2056.h +++ /dev/null @@ -1,1100 +0,0 @@ -#ifndef B43_RADIO_2056_H_ -#define B43_RADIO_2056_H_ - -#include - -#include "tables_nphy.h" - -#define B2056_SYN (0x0 << 12) -#define B2056_TX0 (0x2 << 12) -#define B2056_TX1 (0x3 << 12) -#define B2056_RX0 (0x6 << 12) -#define B2056_RX1 (0x7 << 12) -#define B2056_ALLTX (0xE << 12) -#define B2056_ALLRX (0xF << 12) - -#define B2056_SYN_RESERVED_ADDR0 0x00 -#define B2056_SYN_IDCODE 0x01 -#define B2056_SYN_RESERVED_ADDR2 0x02 -#define B2056_SYN_RESERVED_ADDR3 0x03 -#define B2056_SYN_RESERVED_ADDR4 0x04 -#define B2056_SYN_RESERVED_ADDR5 0x05 -#define B2056_SYN_RESERVED_ADDR6 0x06 -#define B2056_SYN_RESERVED_ADDR7 0x07 -#define B2056_SYN_COM_CTRL 0x08 -#define B2056_SYN_COM_PU 0x09 -#define B2056_SYN_COM_OVR 0x0A -#define B2056_SYN_COM_RESET 0x0B -#define B2056_SYN_COM_RCAL 0x0C -#define B2056_SYN_COM_RC_RXLPF 0x0D -#define B2056_SYN_COM_RC_TXLPF 0x0E -#define B2056_SYN_COM_RC_RXHPF 0x0F -#define B2056_SYN_RESERVED_ADDR16 0x10 -#define B2056_SYN_RESERVED_ADDR17 0x11 -#define B2056_SYN_RESERVED_ADDR18 0x12 -#define B2056_SYN_RESERVED_ADDR19 0x13 -#define B2056_SYN_RESERVED_ADDR20 0x14 -#define B2056_SYN_RESERVED_ADDR21 0x15 -#define B2056_SYN_RESERVED_ADDR22 0x16 -#define B2056_SYN_RESERVED_ADDR23 0x17 -#define B2056_SYN_RESERVED_ADDR24 0x18 -#define B2056_SYN_RESERVED_ADDR25 0x19 -#define B2056_SYN_RESERVED_ADDR26 0x1A -#define B2056_SYN_RESERVED_ADDR27 0x1B -#define B2056_SYN_RESERVED_ADDR28 0x1C -#define B2056_SYN_RESERVED_ADDR29 0x1D -#define B2056_SYN_RESERVED_ADDR30 0x1E -#define B2056_SYN_RESERVED_ADDR31 0x1F -#define B2056_SYN_GPIO_MASTER1 0x20 -#define B2056_SYN_GPIO_MASTER2 0x21 -#define B2056_SYN_TOPBIAS_MASTER 0x22 -#define B2056_SYN_TOPBIAS_RCAL 0x23 -#define B2056_SYN_AFEREG 0x24 -#define B2056_SYN_TEMPPROCSENSE 0x25 -#define B2056_SYN_TEMPPROCSENSEIDAC 0x26 -#define B2056_SYN_TEMPPROCSENSERCAL 0x27 -#define B2056_SYN_LPO 0x28 -#define B2056_SYN_VDDCAL_MASTER 0x29 -#define B2056_SYN_VDDCAL_IDAC 0x2A -#define B2056_SYN_VDDCAL_STATUS 0x2B -#define B2056_SYN_RCAL_MASTER 0x2C -#define B2056_SYN_RCAL_CODE_OUT 0x2D -#define B2056_SYN_RCCAL_CTRL0 0x2E -#define B2056_SYN_RCCAL_CTRL1 0x2F -#define B2056_SYN_RCCAL_CTRL2 0x30 -#define B2056_SYN_RCCAL_CTRL3 0x31 -#define B2056_SYN_RCCAL_CTRL4 0x32 -#define B2056_SYN_RCCAL_CTRL5 0x33 -#define B2056_SYN_RCCAL_CTRL6 0x34 -#define B2056_SYN_RCCAL_CTRL7 0x35 -#define B2056_SYN_RCCAL_CTRL8 0x36 -#define B2056_SYN_RCCAL_CTRL9 0x37 -#define B2056_SYN_RCCAL_CTRL10 0x38 -#define B2056_SYN_RCCAL_CTRL11 0x39 -#define B2056_SYN_ZCAL_SPARE1 0x3A -#define B2056_SYN_ZCAL_SPARE2 0x3B -#define B2056_SYN_PLL_MAST1 0x3C -#define B2056_SYN_PLL_MAST2 0x3D -#define B2056_SYN_PLL_MAST3 0x3E -#define B2056_SYN_PLL_BIAS_RESET 0x3F -#define B2056_SYN_PLL_XTAL0 0x40 -#define B2056_SYN_PLL_XTAL1 0x41 -#define B2056_SYN_PLL_XTAL3 0x42 -#define B2056_SYN_PLL_XTAL4 0x43 -#define B2056_SYN_PLL_XTAL5 0x44 -#define B2056_SYN_PLL_XTAL6 0x45 -#define B2056_SYN_PLL_REFDIV 0x46 -#define B2056_SYN_PLL_PFD 0x47 -#define B2056_SYN_PLL_CP1 0x48 -#define B2056_SYN_PLL_CP2 0x49 -#define B2056_SYN_PLL_CP3 0x4A -#define B2056_SYN_PLL_LOOPFILTER1 0x4B -#define B2056_SYN_PLL_LOOPFILTER2 0x4C -#define B2056_SYN_PLL_LOOPFILTER3 0x4D -#define B2056_SYN_PLL_LOOPFILTER4 0x4E -#define B2056_SYN_PLL_LOOPFILTER5 0x4F -#define B2056_SYN_PLL_MMD1 0x50 -#define B2056_SYN_PLL_MMD2 0x51 -#define B2056_SYN_PLL_VCO1 0x52 -#define B2056_SYN_PLL_VCO2 0x53 -#define B2056_SYN_PLL_MONITOR1 0x54 -#define B2056_SYN_PLL_MONITOR2 0x55 -#define B2056_SYN_PLL_VCOCAL1 0x56 -#define B2056_SYN_PLL_VCOCAL2 0x57 -#define B2056_SYN_PLL_VCOCAL4 0x58 -#define B2056_SYN_PLL_VCOCAL5 0x59 -#define B2056_SYN_PLL_VCOCAL6 0x5A -#define B2056_SYN_PLL_VCOCAL7 0x5B -#define B2056_SYN_PLL_VCOCAL8 0x5C -#define B2056_SYN_PLL_VCOCAL9 0x5D -#define B2056_SYN_PLL_VCOCAL10 0x5E -#define B2056_SYN_PLL_VCOCAL11 0x5F -#define B2056_SYN_PLL_VCOCAL12 0x60 -#define B2056_SYN_PLL_VCOCAL13 0x61 -#define B2056_SYN_PLL_VREG 0x62 -#define B2056_SYN_PLL_STATUS1 0x63 -#define B2056_SYN_PLL_STATUS2 0x64 -#define B2056_SYN_PLL_STATUS3 0x65 -#define B2056_SYN_LOGEN_PU0 0x66 -#define B2056_SYN_LOGEN_PU1 0x67 -#define B2056_SYN_LOGEN_PU2 0x68 -#define B2056_SYN_LOGEN_PU3 0x69 -#define B2056_SYN_LOGEN_PU5 0x6A -#define B2056_SYN_LOGEN_PU6 0x6B -#define B2056_SYN_LOGEN_PU7 0x6C -#define B2056_SYN_LOGEN_PU8 0x6D -#define B2056_SYN_LOGEN_BIAS_RESET 0x6E -#define B2056_SYN_LOGEN_RCCR1 0x6F -#define B2056_SYN_LOGEN_VCOBUF1 0x70 -#define B2056_SYN_LOGEN_MIXER1 0x71 -#define B2056_SYN_LOGEN_MIXER2 0x72 -#define B2056_SYN_LOGEN_BUF1 0x73 -#define B2056_SYN_LOGENBUF2 0x74 -#define B2056_SYN_LOGEN_BUF3 0x75 -#define B2056_SYN_LOGEN_BUF4 0x76 -#define B2056_SYN_LOGEN_DIV1 0x77 -#define B2056_SYN_LOGEN_DIV2 0x78 -#define B2056_SYN_LOGEN_DIV3 0x79 -#define B2056_SYN_LOGEN_ACL1 0x7A -#define B2056_SYN_LOGEN_ACL2 0x7B -#define B2056_SYN_LOGEN_ACL3 0x7C -#define B2056_SYN_LOGEN_ACL4 0x7D -#define B2056_SYN_LOGEN_ACL5 0x7E -#define B2056_SYN_LOGEN_ACL6 0x7F -#define B2056_SYN_LOGEN_ACLOUT 0x80 -#define B2056_SYN_LOGEN_ACLCAL1 0x81 -#define B2056_SYN_LOGEN_ACLCAL2 0x82 -#define B2056_SYN_LOGEN_ACLCAL3 0x83 -#define B2056_SYN_CALEN 0x84 -#define B2056_SYN_LOGEN_PEAKDET1 0x85 -#define B2056_SYN_LOGEN_CORE_ACL_OVR 0x86 -#define B2056_SYN_LOGEN_RX_DIFF_ACL_OVR 0x87 -#define B2056_SYN_LOGEN_TX_DIFF_ACL_OVR 0x88 -#define B2056_SYN_LOGEN_RX_CMOS_ACL_OVR 0x89 -#define B2056_SYN_LOGEN_TX_CMOS_ACL_OVR 0x8A -#define B2056_SYN_LOGEN_VCOBUF2 0x8B -#define B2056_SYN_LOGEN_MIXER3 0x8C -#define B2056_SYN_LOGEN_BUF5 0x8D -#define B2056_SYN_LOGEN_BUF6 0x8E -#define B2056_SYN_LOGEN_CBUFRX1 0x8F -#define B2056_SYN_LOGEN_CBUFRX2 0x90 -#define B2056_SYN_LOGEN_CBUFRX3 0x91 -#define B2056_SYN_LOGEN_CBUFRX4 0x92 -#define B2056_SYN_LOGEN_CBUFTX1 0x93 -#define B2056_SYN_LOGEN_CBUFTX2 0x94 -#define B2056_SYN_LOGEN_CBUFTX3 0x95 -#define B2056_SYN_LOGEN_CBUFTX4 0x96 -#define B2056_SYN_LOGEN_CMOSRX1 0x97 -#define B2056_SYN_LOGEN_CMOSRX2 0x98 -#define B2056_SYN_LOGEN_CMOSRX3 0x99 -#define B2056_SYN_LOGEN_CMOSRX4 0x9A -#define B2056_SYN_LOGEN_CMOSTX1 0x9B -#define B2056_SYN_LOGEN_CMOSTX2 0x9C -#define B2056_SYN_LOGEN_CMOSTX3 0x9D -#define B2056_SYN_LOGEN_CMOSTX4 0x9E -#define B2056_SYN_LOGEN_VCOBUF2_OVRVAL 0x9F -#define B2056_SYN_LOGEN_MIXER3_OVRVAL 0xA0 -#define B2056_SYN_LOGEN_BUF5_OVRVAL 0xA1 -#define B2056_SYN_LOGEN_BUF6_OVRVAL 0xA2 -#define B2056_SYN_LOGEN_CBUFRX1_OVRVAL 0xA3 -#define B2056_SYN_LOGEN_CBUFRX2_OVRVAL 0xA4 -#define B2056_SYN_LOGEN_CBUFRX3_OVRVAL 0xA5 -#define B2056_SYN_LOGEN_CBUFRX4_OVRVAL 0xA6 -#define B2056_SYN_LOGEN_CBUFTX1_OVRVAL 0xA7 -#define B2056_SYN_LOGEN_CBUFTX2_OVRVAL 0xA8 -#define B2056_SYN_LOGEN_CBUFTX3_OVRVAL 0xA9 -#define B2056_SYN_LOGEN_CBUFTX4_OVRVAL 0xAA -#define B2056_SYN_LOGEN_CMOSRX1_OVRVAL 0xAB -#define B2056_SYN_LOGEN_CMOSRX2_OVRVAL 0xAC -#define B2056_SYN_LOGEN_CMOSRX3_OVRVAL 0xAD -#define B2056_SYN_LOGEN_CMOSRX4_OVRVAL 0xAE -#define B2056_SYN_LOGEN_CMOSTX1_OVRVAL 0xAF -#define B2056_SYN_LOGEN_CMOSTX2_OVRVAL 0xB0 -#define B2056_SYN_LOGEN_CMOSTX3_OVRVAL 0xB1 -#define B2056_SYN_LOGEN_CMOSTX4_OVRVAL 0xB2 -#define B2056_SYN_LOGEN_ACL_WAITCNT 0xB3 -#define B2056_SYN_LOGEN_CORE_CALVALID 0xB4 -#define B2056_SYN_LOGEN_RX_CMOS_CALVALID 0xB5 -#define B2056_SYN_LOGEN_TX_CMOS_VALID 0xB6 - -#define B2056_TX_RESERVED_ADDR0 0x00 -#define B2056_TX_IDCODE 0x01 -#define B2056_TX_RESERVED_ADDR2 0x02 -#define B2056_TX_RESERVED_ADDR3 0x03 -#define B2056_TX_RESERVED_ADDR4 0x04 -#define B2056_TX_RESERVED_ADDR5 0x05 -#define B2056_TX_RESERVED_ADDR6 0x06 -#define B2056_TX_RESERVED_ADDR7 0x07 -#define B2056_TX_COM_CTRL 0x08 -#define B2056_TX_COM_PU 0x09 -#define B2056_TX_COM_OVR 0x0A -#define B2056_TX_COM_RESET 0x0B -#define B2056_TX_COM_RCAL 0x0C -#define B2056_TX_COM_RC_RXLPF 0x0D -#define B2056_TX_COM_RC_TXLPF 0x0E -#define B2056_TX_COM_RC_RXHPF 0x0F -#define B2056_TX_RESERVED_ADDR16 0x10 -#define B2056_TX_RESERVED_ADDR17 0x11 -#define B2056_TX_RESERVED_ADDR18 0x12 -#define B2056_TX_RESERVED_ADDR19 0x13 -#define B2056_TX_RESERVED_ADDR20 0x14 -#define B2056_TX_RESERVED_ADDR21 0x15 -#define B2056_TX_RESERVED_ADDR22 0x16 -#define B2056_TX_RESERVED_ADDR23 0x17 -#define B2056_TX_RESERVED_ADDR24 0x18 -#define B2056_TX_RESERVED_ADDR25 0x19 -#define B2056_TX_RESERVED_ADDR26 0x1A -#define B2056_TX_RESERVED_ADDR27 0x1B -#define B2056_TX_RESERVED_ADDR28 0x1C -#define B2056_TX_RESERVED_ADDR29 0x1D -#define B2056_TX_RESERVED_ADDR30 0x1E -#define B2056_TX_RESERVED_ADDR31 0x1F -#define B2056_TX_IQCAL_GAIN_BW 0x20 -#define B2056_TX_LOFT_FINE_I 0x21 -#define B2056_TX_LOFT_FINE_Q 0x22 -#define B2056_TX_LOFT_COARSE_I 0x23 -#define B2056_TX_LOFT_COARSE_Q 0x24 -#define B2056_TX_TX_COM_MASTER1 0x25 -#define B2056_TX_TX_COM_MASTER2 0x26 -#define B2056_TX_RXIQCAL_TXMUX 0x27 -#define B2056_TX_TX_SSI_MASTER 0x28 -#define B2056_TX_IQCAL_VCM_HG 0x29 -#define B2056_TX_IQCAL_IDAC 0x2A -#define B2056_TX_TSSI_VCM 0x2B -#define B2056_TX_TX_AMP_DET 0x2C -#define B2056_TX_TX_SSI_MUX 0x2D -#define B2056_TX_TSSIA 0x2E -#define B2056_TX_TSSIG 0x2F -#define B2056_TX_TSSI_MISC1 0x30 -#define B2056_TX_TSSI_MISC2 0x31 -#define B2056_TX_TSSI_MISC3 0x32 -#define B2056_TX_PA_SPARE1 0x33 -#define B2056_TX_PA_SPARE2 0x34 -#define B2056_TX_INTPAA_MASTER 0x35 -#define B2056_TX_INTPAA_GAIN 0x36 -#define B2056_TX_INTPAA_BOOST_TUNE 0x37 -#define B2056_TX_INTPAA_IAUX_STAT 0x38 -#define B2056_TX_INTPAA_IAUX_DYN 0x39 -#define B2056_TX_INTPAA_IMAIN_STAT 0x3A -#define B2056_TX_INTPAA_IMAIN_DYN 0x3B -#define B2056_TX_INTPAA_CASCBIAS 0x3C -#define B2056_TX_INTPAA_PASLOPE 0x3D -#define B2056_TX_INTPAA_PA_MISC 0x3E -#define B2056_TX_INTPAG_MASTER 0x3F -#define B2056_TX_INTPAG_GAIN 0x40 -#define B2056_TX_INTPAG_BOOST_TUNE 0x41 -#define B2056_TX_INTPAG_IAUX_STAT 0x42 -#define B2056_TX_INTPAG_IAUX_DYN 0x43 -#define B2056_TX_INTPAG_IMAIN_STAT 0x44 -#define B2056_TX_INTPAG_IMAIN_DYN 0x45 -#define B2056_TX_INTPAG_CASCBIAS 0x46 -#define B2056_TX_INTPAG_PASLOPE 0x47 -#define B2056_TX_INTPAG_PA_MISC 0x48 -#define B2056_TX_PADA_MASTER 0x49 -#define B2056_TX_PADA_IDAC 0x4A -#define B2056_TX_PADA_CASCBIAS 0x4B -#define B2056_TX_PADA_GAIN 0x4C -#define B2056_TX_PADA_BOOST_TUNE 0x4D -#define B2056_TX_PADA_SLOPE 0x4E -#define B2056_TX_PADG_MASTER 0x4F -#define B2056_TX_PADG_IDAC 0x50 -#define B2056_TX_PADG_CASCBIAS 0x51 -#define B2056_TX_PADG_GAIN 0x52 -#define B2056_TX_PADG_BOOST_TUNE 0x53 -#define B2056_TX_PADG_SLOPE 0x54 -#define B2056_TX_PGAA_MASTER 0x55 -#define B2056_TX_PGAA_IDAC 0x56 -#define B2056_TX_PGAA_GAIN 0x57 -#define B2056_TX_PGAA_BOOST_TUNE 0x58 -#define B2056_TX_PGAA_SLOPE 0x59 -#define B2056_TX_PGAA_MISC 0x5A -#define B2056_TX_PGAG_MASTER 0x5B -#define B2056_TX_PGAG_IDAC 0x5C -#define B2056_TX_PGAG_GAIN 0x5D -#define B2056_TX_PGAG_BOOST_TUNE 0x5E -#define B2056_TX_PGAG_SLOPE 0x5F -#define B2056_TX_PGAG_MISC 0x60 -#define B2056_TX_MIXA_MASTER 0x61 -#define B2056_TX_MIXA_BOOST_TUNE 0x62 -#define B2056_TX_MIXG 0x63 -#define B2056_TX_MIXG_BOOST_TUNE 0x64 -#define B2056_TX_BB_GM_MASTER 0x65 -#define B2056_TX_GMBB_GM 0x66 -#define B2056_TX_GMBB_IDAC 0x67 -#define B2056_TX_TXLPF_MASTER 0x68 -#define B2056_TX_TXLPF_RCCAL 0x69 -#define B2056_TX_TXLPF_RCCAL_OFF0 0x6A -#define B2056_TX_TXLPF_RCCAL_OFF1 0x6B -#define B2056_TX_TXLPF_RCCAL_OFF2 0x6C -#define B2056_TX_TXLPF_RCCAL_OFF3 0x6D -#define B2056_TX_TXLPF_RCCAL_OFF4 0x6E -#define B2056_TX_TXLPF_RCCAL_OFF5 0x6F -#define B2056_TX_TXLPF_RCCAL_OFF6 0x70 -#define B2056_TX_TXLPF_BW 0x71 -#define B2056_TX_TXLPF_GAIN 0x72 -#define B2056_TX_TXLPF_IDAC 0x73 -#define B2056_TX_TXLPF_IDAC_0 0x74 -#define B2056_TX_TXLPF_IDAC_1 0x75 -#define B2056_TX_TXLPF_IDAC_2 0x76 -#define B2056_TX_TXLPF_IDAC_3 0x77 -#define B2056_TX_TXLPF_IDAC_4 0x78 -#define B2056_TX_TXLPF_IDAC_5 0x79 -#define B2056_TX_TXLPF_IDAC_6 0x7A -#define B2056_TX_TXLPF_OPAMP_IDAC 0x7B -#define B2056_TX_TXLPF_MISC 0x7C -#define B2056_TX_TXSPARE1 0x7D -#define B2056_TX_TXSPARE2 0x7E -#define B2056_TX_TXSPARE3 0x7F -#define B2056_TX_TXSPARE4 0x80 -#define B2056_TX_TXSPARE5 0x81 -#define B2056_TX_TXSPARE6 0x82 -#define B2056_TX_TXSPARE7 0x83 -#define B2056_TX_TXSPARE8 0x84 -#define B2056_TX_TXSPARE9 0x85 -#define B2056_TX_TXSPARE10 0x86 -#define B2056_TX_TXSPARE11 0x87 -#define B2056_TX_TXSPARE12 0x88 -#define B2056_TX_TXSPARE13 0x89 -#define B2056_TX_TXSPARE14 0x8A -#define B2056_TX_TXSPARE15 0x8B -#define B2056_TX_TXSPARE16 0x8C -#define B2056_TX_STATUS_INTPA_GAIN 0x8D -#define B2056_TX_STATUS_PAD_GAIN 0x8E -#define B2056_TX_STATUS_PGA_GAIN 0x8F -#define B2056_TX_STATUS_GM_TXLPF_GAIN 0x90 -#define B2056_TX_STATUS_TXLPF_BW 0x91 -#define B2056_TX_STATUS_TXLPF_RC 0x92 -#define B2056_TX_GMBB_IDAC0 0x93 -#define B2056_TX_GMBB_IDAC1 0x94 -#define B2056_TX_GMBB_IDAC2 0x95 -#define B2056_TX_GMBB_IDAC3 0x96 -#define B2056_TX_GMBB_IDAC4 0x97 -#define B2056_TX_GMBB_IDAC5 0x98 -#define B2056_TX_GMBB_IDAC6 0x99 -#define B2056_TX_GMBB_IDAC7 0x9A - -#define B2056_RX_RESERVED_ADDR0 0x00 -#define B2056_RX_IDCODE 0x01 -#define B2056_RX_RESERVED_ADDR2 0x02 -#define B2056_RX_RESERVED_ADDR3 0x03 -#define B2056_RX_RESERVED_ADDR4 0x04 -#define B2056_RX_RESERVED_ADDR5 0x05 -#define B2056_RX_RESERVED_ADDR6 0x06 -#define B2056_RX_RESERVED_ADDR7 0x07 -#define B2056_RX_COM_CTRL 0x08 -#define B2056_RX_COM_PU 0x09 -#define B2056_RX_COM_OVR 0x0A -#define B2056_RX_COM_RESET 0x0B -#define B2056_RX_COM_RCAL 0x0C -#define B2056_RX_COM_RC_RXLPF 0x0D -#define B2056_RX_COM_RC_TXLPF 0x0E -#define B2056_RX_COM_RC_RXHPF 0x0F -#define B2056_RX_RESERVED_ADDR16 0x10 -#define B2056_RX_RESERVED_ADDR17 0x11 -#define B2056_RX_RESERVED_ADDR18 0x12 -#define B2056_RX_RESERVED_ADDR19 0x13 -#define B2056_RX_RESERVED_ADDR20 0x14 -#define B2056_RX_RESERVED_ADDR21 0x15 -#define B2056_RX_RESERVED_ADDR22 0x16 -#define B2056_RX_RESERVED_ADDR23 0x17 -#define B2056_RX_RESERVED_ADDR24 0x18 -#define B2056_RX_RESERVED_ADDR25 0x19 -#define B2056_RX_RESERVED_ADDR26 0x1A -#define B2056_RX_RESERVED_ADDR27 0x1B -#define B2056_RX_RESERVED_ADDR28 0x1C -#define B2056_RX_RESERVED_ADDR29 0x1D -#define B2056_RX_RESERVED_ADDR30 0x1E -#define B2056_RX_RESERVED_ADDR31 0x1F -#define B2056_RX_RXIQCAL_RXMUX 0x20 -#define B2056_RX_RSSI_PU 0x21 -#define B2056_RX_RSSI_SEL 0x22 -#define B2056_RX_RSSI_GAIN 0x23 -#define B2056_RX_RSSI_NB_IDAC 0x24 -#define B2056_RX_RSSI_WB2I_IDAC_1 0x25 -#define B2056_RX_RSSI_WB2I_IDAC_2 0x26 -#define B2056_RX_RSSI_WB2Q_IDAC_1 0x27 -#define B2056_RX_RSSI_WB2Q_IDAC_2 0x28 -#define B2056_RX_RSSI_POLE 0x29 -#define B2056_RX_RSSI_WB1_IDAC 0x2A -#define B2056_RX_RSSI_MISC 0x2B -#define B2056_RX_LNAA_MASTER 0x2C -#define B2056_RX_LNAA_TUNE 0x2D -#define B2056_RX_LNAA_GAIN 0x2E -#define B2056_RX_LNA_A_SLOPE 0x2F -#define B2056_RX_BIASPOLE_LNAA1_IDAC 0x30 -#define B2056_RX_LNAA2_IDAC 0x31 -#define B2056_RX_LNA1A_MISC 0x32 -#define B2056_RX_LNAG_MASTER 0x33 -#define B2056_RX_LNAG_TUNE 0x34 -#define B2056_RX_LNAG_GAIN 0x35 -#define B2056_RX_LNA_G_SLOPE 0x36 -#define B2056_RX_BIASPOLE_LNAG1_IDAC 0x37 -#define B2056_RX_LNAG2_IDAC 0x38 -#define B2056_RX_LNA1G_MISC 0x39 -#define B2056_RX_MIXA_MASTER 0x3A -#define B2056_RX_MIXA_VCM 0x3B -#define B2056_RX_MIXA_CTRLPTAT 0x3C -#define B2056_RX_MIXA_LOB_BIAS 0x3D -#define B2056_RX_MIXA_CORE_IDAC 0x3E -#define B2056_RX_MIXA_CMFB_IDAC 0x3F -#define B2056_RX_MIXA_BIAS_AUX 0x40 -#define B2056_RX_MIXA_BIAS_MAIN 0x41 -#define B2056_RX_MIXA_BIAS_MISC 0x42 -#define B2056_RX_MIXA_MAST_BIAS 0x43 -#define B2056_RX_MIXG_MASTER 0x44 -#define B2056_RX_MIXG_VCM 0x45 -#define B2056_RX_MIXG_CTRLPTAT 0x46 -#define B2056_RX_MIXG_LOB_BIAS 0x47 -#define B2056_RX_MIXG_CORE_IDAC 0x48 -#define B2056_RX_MIXG_CMFB_IDAC 0x49 -#define B2056_RX_MIXG_BIAS_AUX 0x4A -#define B2056_RX_MIXG_BIAS_MAIN 0x4B -#define B2056_RX_MIXG_BIAS_MISC 0x4C -#define B2056_RX_MIXG_MAST_BIAS 0x4D -#define B2056_RX_TIA_MASTER 0x4E -#define B2056_RX_TIA_IOPAMP 0x4F -#define B2056_RX_TIA_QOPAMP 0x50 -#define B2056_RX_TIA_IMISC 0x51 -#define B2056_RX_TIA_QMISC 0x52 -#define B2056_RX_TIA_GAIN 0x53 -#define B2056_RX_TIA_SPARE1 0x54 -#define B2056_RX_TIA_SPARE2 0x55 -#define B2056_RX_BB_LPF_MASTER 0x56 -#define B2056_RX_AACI_MASTER 0x57 -#define B2056_RX_RXLPF_IDAC 0x58 -#define B2056_RX_RXLPF_OPAMPBIAS_LOWQ 0x59 -#define B2056_RX_RXLPF_OPAMPBIAS_HIGHQ 0x5A -#define B2056_RX_RXLPF_BIAS_DCCANCEL 0x5B -#define B2056_RX_RXLPF_OUTVCM 0x5C -#define B2056_RX_RXLPF_INVCM_BODY 0x5D -#define B2056_RX_RXLPF_CC_OP 0x5E -#define B2056_RX_RXLPF_GAIN 0x5F -#define B2056_RX_RXLPF_Q_BW 0x60 -#define B2056_RX_RXLPF_HP_CORNER_BW 0x61 -#define B2056_RX_RXLPF_RCCAL_HPC 0x62 -#define B2056_RX_RXHPF_OFF0 0x63 -#define B2056_RX_RXHPF_OFF1 0x64 -#define B2056_RX_RXHPF_OFF2 0x65 -#define B2056_RX_RXHPF_OFF3 0x66 -#define B2056_RX_RXHPF_OFF4 0x67 -#define B2056_RX_RXHPF_OFF5 0x68 -#define B2056_RX_RXHPF_OFF6 0x69 -#define B2056_RX_RXHPF_OFF7 0x6A -#define B2056_RX_RXLPF_RCCAL_LPC 0x6B -#define B2056_RX_RXLPF_OFF_0 0x6C -#define B2056_RX_RXLPF_OFF_1 0x6D -#define B2056_RX_RXLPF_OFF_2 0x6E -#define B2056_RX_RXLPF_OFF_3 0x6F -#define B2056_RX_RXLPF_OFF_4 0x70 -#define B2056_RX_UNUSED 0x71 -#define B2056_RX_VGA_MASTER 0x72 -#define B2056_RX_VGA_BIAS 0x73 -#define B2056_RX_VGA_BIAS_DCCANCEL 0x74 -#define B2056_RX_VGA_GAIN 0x75 -#define B2056_RX_VGA_HP_CORNER_BW 0x76 -#define B2056_RX_VGABUF_BIAS 0x77 -#define B2056_RX_VGABUF_GAIN_BW 0x78 -#define B2056_RX_TXFBMIX_A 0x79 -#define B2056_RX_TXFBMIX_G 0x7A -#define B2056_RX_RXSPARE1 0x7B -#define B2056_RX_RXSPARE2 0x7C -#define B2056_RX_RXSPARE3 0x7D -#define B2056_RX_RXSPARE4 0x7E -#define B2056_RX_RXSPARE5 0x7F -#define B2056_RX_RXSPARE6 0x80 -#define B2056_RX_RXSPARE7 0x81 -#define B2056_RX_RXSPARE8 0x82 -#define B2056_RX_RXSPARE9 0x83 -#define B2056_RX_RXSPARE10 0x84 -#define B2056_RX_RXSPARE11 0x85 -#define B2056_RX_RXSPARE12 0x86 -#define B2056_RX_RXSPARE13 0x87 -#define B2056_RX_RXSPARE14 0x88 -#define B2056_RX_RXSPARE15 0x89 -#define B2056_RX_RXSPARE16 0x8A -#define B2056_RX_STATUS_LNAA_GAIN 0x8B -#define B2056_RX_STATUS_LNAG_GAIN 0x8C -#define B2056_RX_STATUS_MIXTIA_GAIN 0x8D -#define B2056_RX_STATUS_RXLPF_GAIN 0x8E -#define B2056_RX_STATUS_VGA_BUF_GAIN 0x8F -#define B2056_RX_STATUS_RXLPF_Q 0x90 -#define B2056_RX_STATUS_RXLPF_BUF_BW 0x91 -#define B2056_RX_STATUS_RXLPF_VGA_HPC 0x92 -#define B2056_RX_STATUS_RXLPF_RC 0x93 -#define B2056_RX_STATUS_HPC_RC 0x94 - -#define B2056_LNA1_A_PU 0x01 -#define B2056_LNA2_A_PU 0x02 -#define B2056_LNA1_G_PU 0x01 -#define B2056_LNA2_G_PU 0x02 -#define B2056_MIXA_PU_I 0x01 -#define B2056_MIXA_PU_Q 0x02 -#define B2056_MIXA_PU_GM 0x10 -#define B2056_MIXG_PU_I 0x01 -#define B2056_MIXG_PU_Q 0x02 -#define B2056_MIXG_PU_GM 0x10 -#define B2056_TIA_PU 0x01 -#define B2056_BB_LPF_PU 0x20 -#define B2056_W1_PU 0x02 -#define B2056_W2_PU 0x04 -#define B2056_NB_PU 0x08 -#define B2056_RSSI_W1_SEL 0x02 -#define B2056_RSSI_W2_SEL 0x04 -#define B2056_RSSI_NB_SEL 0x08 -#define B2056_VCM_MASK 0x1C -#define B2056_RSSI_VCM_SHIFT 0x02 - -#define B2056_SYN (0x0 << 12) -#define B2056_TX0 (0x2 << 12) -#define B2056_TX1 (0x3 << 12) -#define B2056_RX0 (0x6 << 12) -#define B2056_RX1 (0x7 << 12) -#define B2056_ALLTX (0xE << 12) -#define B2056_ALLRX (0xF << 12) - -#define B2056_SYN_RESERVED_ADDR0 0x00 -#define B2056_SYN_IDCODE 0x01 -#define B2056_SYN_RESERVED_ADDR2 0x02 -#define B2056_SYN_RESERVED_ADDR3 0x03 -#define B2056_SYN_RESERVED_ADDR4 0x04 -#define B2056_SYN_RESERVED_ADDR5 0x05 -#define B2056_SYN_RESERVED_ADDR6 0x06 -#define B2056_SYN_RESERVED_ADDR7 0x07 -#define B2056_SYN_COM_CTRL 0x08 -#define B2056_SYN_COM_PU 0x09 -#define B2056_SYN_COM_OVR 0x0A -#define B2056_SYN_COM_RESET 0x0B -#define B2056_SYN_COM_RCAL 0x0C -#define B2056_SYN_COM_RC_RXLPF 0x0D -#define B2056_SYN_COM_RC_TXLPF 0x0E -#define B2056_SYN_COM_RC_RXHPF 0x0F -#define B2056_SYN_RESERVED_ADDR16 0x10 -#define B2056_SYN_RESERVED_ADDR17 0x11 -#define B2056_SYN_RESERVED_ADDR18 0x12 -#define B2056_SYN_RESERVED_ADDR19 0x13 -#define B2056_SYN_RESERVED_ADDR20 0x14 -#define B2056_SYN_RESERVED_ADDR21 0x15 -#define B2056_SYN_RESERVED_ADDR22 0x16 -#define B2056_SYN_RESERVED_ADDR23 0x17 -#define B2056_SYN_RESERVED_ADDR24 0x18 -#define B2056_SYN_RESERVED_ADDR25 0x19 -#define B2056_SYN_RESERVED_ADDR26 0x1A -#define B2056_SYN_RESERVED_ADDR27 0x1B -#define B2056_SYN_RESERVED_ADDR28 0x1C -#define B2056_SYN_RESERVED_ADDR29 0x1D -#define B2056_SYN_RESERVED_ADDR30 0x1E -#define B2056_SYN_RESERVED_ADDR31 0x1F -#define B2056_SYN_GPIO_MASTER1 0x20 -#define B2056_SYN_GPIO_MASTER2 0x21 -#define B2056_SYN_TOPBIAS_MASTER 0x22 -#define B2056_SYN_TOPBIAS_RCAL 0x23 -#define B2056_SYN_AFEREG 0x24 -#define B2056_SYN_TEMPPROCSENSE 0x25 -#define B2056_SYN_TEMPPROCSENSEIDAC 0x26 -#define B2056_SYN_TEMPPROCSENSERCAL 0x27 -#define B2056_SYN_LPO 0x28 -#define B2056_SYN_VDDCAL_MASTER 0x29 -#define B2056_SYN_VDDCAL_IDAC 0x2A -#define B2056_SYN_VDDCAL_STATUS 0x2B -#define B2056_SYN_RCAL_MASTER 0x2C -#define B2056_SYN_RCAL_CODE_OUT 0x2D -#define B2056_SYN_RCCAL_CTRL0 0x2E -#define B2056_SYN_RCCAL_CTRL1 0x2F -#define B2056_SYN_RCCAL_CTRL2 0x30 -#define B2056_SYN_RCCAL_CTRL3 0x31 -#define B2056_SYN_RCCAL_CTRL4 0x32 -#define B2056_SYN_RCCAL_CTRL5 0x33 -#define B2056_SYN_RCCAL_CTRL6 0x34 -#define B2056_SYN_RCCAL_CTRL7 0x35 -#define B2056_SYN_RCCAL_CTRL8 0x36 -#define B2056_SYN_RCCAL_CTRL9 0x37 -#define B2056_SYN_RCCAL_CTRL10 0x38 -#define B2056_SYN_RCCAL_CTRL11 0x39 -#define B2056_SYN_ZCAL_SPARE1 0x3A -#define B2056_SYN_ZCAL_SPARE2 0x3B -#define B2056_SYN_PLL_MAST1 0x3C -#define B2056_SYN_PLL_MAST2 0x3D -#define B2056_SYN_PLL_MAST3 0x3E -#define B2056_SYN_PLL_BIAS_RESET 0x3F -#define B2056_SYN_PLL_XTAL0 0x40 -#define B2056_SYN_PLL_XTAL1 0x41 -#define B2056_SYN_PLL_XTAL3 0x42 -#define B2056_SYN_PLL_XTAL4 0x43 -#define B2056_SYN_PLL_XTAL5 0x44 -#define B2056_SYN_PLL_XTAL6 0x45 -#define B2056_SYN_PLL_REFDIV 0x46 -#define B2056_SYN_PLL_PFD 0x47 -#define B2056_SYN_PLL_CP1 0x48 -#define B2056_SYN_PLL_CP2 0x49 -#define B2056_SYN_PLL_CP3 0x4A -#define B2056_SYN_PLL_LOOPFILTER1 0x4B -#define B2056_SYN_PLL_LOOPFILTER2 0x4C -#define B2056_SYN_PLL_LOOPFILTER3 0x4D -#define B2056_SYN_PLL_LOOPFILTER4 0x4E -#define B2056_SYN_PLL_LOOPFILTER5 0x4F -#define B2056_SYN_PLL_MMD1 0x50 -#define B2056_SYN_PLL_MMD2 0x51 -#define B2056_SYN_PLL_VCO1 0x52 -#define B2056_SYN_PLL_VCO2 0x53 -#define B2056_SYN_PLL_MONITOR1 0x54 -#define B2056_SYN_PLL_MONITOR2 0x55 -#define B2056_SYN_PLL_VCOCAL1 0x56 -#define B2056_SYN_PLL_VCOCAL2 0x57 -#define B2056_SYN_PLL_VCOCAL4 0x58 -#define B2056_SYN_PLL_VCOCAL5 0x59 -#define B2056_SYN_PLL_VCOCAL6 0x5A -#define B2056_SYN_PLL_VCOCAL7 0x5B -#define B2056_SYN_PLL_VCOCAL8 0x5C -#define B2056_SYN_PLL_VCOCAL9 0x5D -#define B2056_SYN_PLL_VCOCAL10 0x5E -#define B2056_SYN_PLL_VCOCAL11 0x5F -#define B2056_SYN_PLL_VCOCAL12 0x60 -#define B2056_SYN_PLL_VCOCAL13 0x61 -#define B2056_SYN_PLL_VREG 0x62 -#define B2056_SYN_PLL_STATUS1 0x63 -#define B2056_SYN_PLL_STATUS2 0x64 -#define B2056_SYN_PLL_STATUS3 0x65 -#define B2056_SYN_LOGEN_PU0 0x66 -#define B2056_SYN_LOGEN_PU1 0x67 -#define B2056_SYN_LOGEN_PU2 0x68 -#define B2056_SYN_LOGEN_PU3 0x69 -#define B2056_SYN_LOGEN_PU5 0x6A -#define B2056_SYN_LOGEN_PU6 0x6B -#define B2056_SYN_LOGEN_PU7 0x6C -#define B2056_SYN_LOGEN_PU8 0x6D -#define B2056_SYN_LOGEN_BIAS_RESET 0x6E -#define B2056_SYN_LOGEN_RCCR1 0x6F -#define B2056_SYN_LOGEN_VCOBUF1 0x70 -#define B2056_SYN_LOGEN_MIXER1 0x71 -#define B2056_SYN_LOGEN_MIXER2 0x72 -#define B2056_SYN_LOGEN_BUF1 0x73 -#define B2056_SYN_LOGENBUF2 0x74 -#define B2056_SYN_LOGEN_BUF3 0x75 -#define B2056_SYN_LOGEN_BUF4 0x76 -#define B2056_SYN_LOGEN_DIV1 0x77 -#define B2056_SYN_LOGEN_DIV2 0x78 -#define B2056_SYN_LOGEN_DIV3 0x79 -#define B2056_SYN_LOGEN_ACL1 0x7A -#define B2056_SYN_LOGEN_ACL2 0x7B -#define B2056_SYN_LOGEN_ACL3 0x7C -#define B2056_SYN_LOGEN_ACL4 0x7D -#define B2056_SYN_LOGEN_ACL5 0x7E -#define B2056_SYN_LOGEN_ACL6 0x7F -#define B2056_SYN_LOGEN_ACLOUT 0x80 -#define B2056_SYN_LOGEN_ACLCAL1 0x81 -#define B2056_SYN_LOGEN_ACLCAL2 0x82 -#define B2056_SYN_LOGEN_ACLCAL3 0x83 -#define B2056_SYN_CALEN 0x84 -#define B2056_SYN_LOGEN_PEAKDET1 0x85 -#define B2056_SYN_LOGEN_CORE_ACL_OVR 0x86 -#define B2056_SYN_LOGEN_RX_DIFF_ACL_OVR 0x87 -#define B2056_SYN_LOGEN_TX_DIFF_ACL_OVR 0x88 -#define B2056_SYN_LOGEN_RX_CMOS_ACL_OVR 0x89 -#define B2056_SYN_LOGEN_TX_CMOS_ACL_OVR 0x8A -#define B2056_SYN_LOGEN_VCOBUF2 0x8B -#define B2056_SYN_LOGEN_MIXER3 0x8C -#define B2056_SYN_LOGEN_BUF5 0x8D -#define B2056_SYN_LOGEN_BUF6 0x8E -#define B2056_SYN_LOGEN_CBUFRX1 0x8F -#define B2056_SYN_LOGEN_CBUFRX2 0x90 -#define B2056_SYN_LOGEN_CBUFRX3 0x91 -#define B2056_SYN_LOGEN_CBUFRX4 0x92 -#define B2056_SYN_LOGEN_CBUFTX1 0x93 -#define B2056_SYN_LOGEN_CBUFTX2 0x94 -#define B2056_SYN_LOGEN_CBUFTX3 0x95 -#define B2056_SYN_LOGEN_CBUFTX4 0x96 -#define B2056_SYN_LOGEN_CMOSRX1 0x97 -#define B2056_SYN_LOGEN_CMOSRX2 0x98 -#define B2056_SYN_LOGEN_CMOSRX3 0x99 -#define B2056_SYN_LOGEN_CMOSRX4 0x9A -#define B2056_SYN_LOGEN_CMOSTX1 0x9B -#define B2056_SYN_LOGEN_CMOSTX2 0x9C -#define B2056_SYN_LOGEN_CMOSTX3 0x9D -#define B2056_SYN_LOGEN_CMOSTX4 0x9E -#define B2056_SYN_LOGEN_VCOBUF2_OVRVAL 0x9F -#define B2056_SYN_LOGEN_MIXER3_OVRVAL 0xA0 -#define B2056_SYN_LOGEN_BUF5_OVRVAL 0xA1 -#define B2056_SYN_LOGEN_BUF6_OVRVAL 0xA2 -#define B2056_SYN_LOGEN_CBUFRX1_OVRVAL 0xA3 -#define B2056_SYN_LOGEN_CBUFRX2_OVRVAL 0xA4 -#define B2056_SYN_LOGEN_CBUFRX3_OVRVAL 0xA5 -#define B2056_SYN_LOGEN_CBUFRX4_OVRVAL 0xA6 -#define B2056_SYN_LOGEN_CBUFTX1_OVRVAL 0xA7 -#define B2056_SYN_LOGEN_CBUFTX2_OVRVAL 0xA8 -#define B2056_SYN_LOGEN_CBUFTX3_OVRVAL 0xA9 -#define B2056_SYN_LOGEN_CBUFTX4_OVRVAL 0xAA -#define B2056_SYN_LOGEN_CMOSRX1_OVRVAL 0xAB -#define B2056_SYN_LOGEN_CMOSRX2_OVRVAL 0xAC -#define B2056_SYN_LOGEN_CMOSRX3_OVRVAL 0xAD -#define B2056_SYN_LOGEN_CMOSRX4_OVRVAL 0xAE -#define B2056_SYN_LOGEN_CMOSTX1_OVRVAL 0xAF -#define B2056_SYN_LOGEN_CMOSTX2_OVRVAL 0xB0 -#define B2056_SYN_LOGEN_CMOSTX3_OVRVAL 0xB1 -#define B2056_SYN_LOGEN_CMOSTX4_OVRVAL 0xB2 -#define B2056_SYN_LOGEN_ACL_WAITCNT 0xB3 -#define B2056_SYN_LOGEN_CORE_CALVALID 0xB4 -#define B2056_SYN_LOGEN_RX_CMOS_CALVALID 0xB5 -#define B2056_SYN_LOGEN_TX_CMOS_VALID 0xB6 - -#define B2056_TX_RESERVED_ADDR0 0x00 -#define B2056_TX_IDCODE 0x01 -#define B2056_TX_RESERVED_ADDR2 0x02 -#define B2056_TX_RESERVED_ADDR3 0x03 -#define B2056_TX_RESERVED_ADDR4 0x04 -#define B2056_TX_RESERVED_ADDR5 0x05 -#define B2056_TX_RESERVED_ADDR6 0x06 -#define B2056_TX_RESERVED_ADDR7 0x07 -#define B2056_TX_COM_CTRL 0x08 -#define B2056_TX_COM_PU 0x09 -#define B2056_TX_COM_OVR 0x0A -#define B2056_TX_COM_RESET 0x0B -#define B2056_TX_COM_RCAL 0x0C -#define B2056_TX_COM_RC_RXLPF 0x0D -#define B2056_TX_COM_RC_TXLPF 0x0E -#define B2056_TX_COM_RC_RXHPF 0x0F -#define B2056_TX_RESERVED_ADDR16 0x10 -#define B2056_TX_RESERVED_ADDR17 0x11 -#define B2056_TX_RESERVED_ADDR18 0x12 -#define B2056_TX_RESERVED_ADDR19 0x13 -#define B2056_TX_RESERVED_ADDR20 0x14 -#define B2056_TX_RESERVED_ADDR21 0x15 -#define B2056_TX_RESERVED_ADDR22 0x16 -#define B2056_TX_RESERVED_ADDR23 0x17 -#define B2056_TX_RESERVED_ADDR24 0x18 -#define B2056_TX_RESERVED_ADDR25 0x19 -#define B2056_TX_RESERVED_ADDR26 0x1A -#define B2056_TX_RESERVED_ADDR27 0x1B -#define B2056_TX_RESERVED_ADDR28 0x1C -#define B2056_TX_RESERVED_ADDR29 0x1D -#define B2056_TX_RESERVED_ADDR30 0x1E -#define B2056_TX_RESERVED_ADDR31 0x1F -#define B2056_TX_IQCAL_GAIN_BW 0x20 -#define B2056_TX_LOFT_FINE_I 0x21 -#define B2056_TX_LOFT_FINE_Q 0x22 -#define B2056_TX_LOFT_COARSE_I 0x23 -#define B2056_TX_LOFT_COARSE_Q 0x24 -#define B2056_TX_TX_COM_MASTER1 0x25 -#define B2056_TX_TX_COM_MASTER2 0x26 -#define B2056_TX_RXIQCAL_TXMUX 0x27 -#define B2056_TX_TX_SSI_MASTER 0x28 -#define B2056_TX_IQCAL_VCM_HG 0x29 -#define B2056_TX_IQCAL_IDAC 0x2A -#define B2056_TX_TSSI_VCM 0x2B -#define B2056_TX_TX_AMP_DET 0x2C -#define B2056_TX_TX_SSI_MUX 0x2D -#define B2056_TX_TSSIA 0x2E -#define B2056_TX_TSSIG 0x2F -#define B2056_TX_TSSI_MISC1 0x30 -#define B2056_TX_TSSI_MISC2 0x31 -#define B2056_TX_TSSI_MISC3 0x32 -#define B2056_TX_PA_SPARE1 0x33 -#define B2056_TX_PA_SPARE2 0x34 -#define B2056_TX_INTPAA_MASTER 0x35 -#define B2056_TX_INTPAA_GAIN 0x36 -#define B2056_TX_INTPAA_BOOST_TUNE 0x37 -#define B2056_TX_INTPAA_IAUX_STAT 0x38 -#define B2056_TX_INTPAA_IAUX_DYN 0x39 -#define B2056_TX_INTPAA_IMAIN_STAT 0x3A -#define B2056_TX_INTPAA_IMAIN_DYN 0x3B -#define B2056_TX_INTPAA_CASCBIAS 0x3C -#define B2056_TX_INTPAA_PASLOPE 0x3D -#define B2056_TX_INTPAA_PA_MISC 0x3E -#define B2056_TX_INTPAG_MASTER 0x3F -#define B2056_TX_INTPAG_GAIN 0x40 -#define B2056_TX_INTPAG_BOOST_TUNE 0x41 -#define B2056_TX_INTPAG_IAUX_STAT 0x42 -#define B2056_TX_INTPAG_IAUX_DYN 0x43 -#define B2056_TX_INTPAG_IMAIN_STAT 0x44 -#define B2056_TX_INTPAG_IMAIN_DYN 0x45 -#define B2056_TX_INTPAG_CASCBIAS 0x46 -#define B2056_TX_INTPAG_PASLOPE 0x47 -#define B2056_TX_INTPAG_PA_MISC 0x48 -#define B2056_TX_PADA_MASTER 0x49 -#define B2056_TX_PADA_IDAC 0x4A -#define B2056_TX_PADA_CASCBIAS 0x4B -#define B2056_TX_PADA_GAIN 0x4C -#define B2056_TX_PADA_BOOST_TUNE 0x4D -#define B2056_TX_PADA_SLOPE 0x4E -#define B2056_TX_PADG_MASTER 0x4F -#define B2056_TX_PADG_IDAC 0x50 -#define B2056_TX_PADG_CASCBIAS 0x51 -#define B2056_TX_PADG_GAIN 0x52 -#define B2056_TX_PADG_BOOST_TUNE 0x53 -#define B2056_TX_PADG_SLOPE 0x54 -#define B2056_TX_PGAA_MASTER 0x55 -#define B2056_TX_PGAA_IDAC 0x56 -#define B2056_TX_PGAA_GAIN 0x57 -#define B2056_TX_PGAA_BOOST_TUNE 0x58 -#define B2056_TX_PGAA_SLOPE 0x59 -#define B2056_TX_PGAA_MISC 0x5A -#define B2056_TX_PGAG_MASTER 0x5B -#define B2056_TX_PGAG_IDAC 0x5C -#define B2056_TX_PGAG_GAIN 0x5D -#define B2056_TX_PGAG_BOOST_TUNE 0x5E -#define B2056_TX_PGAG_SLOPE 0x5F -#define B2056_TX_PGAG_MISC 0x60 -#define B2056_TX_MIXA_MASTER 0x61 -#define B2056_TX_MIXA_BOOST_TUNE 0x62 -#define B2056_TX_MIXG 0x63 -#define B2056_TX_MIXG_BOOST_TUNE 0x64 -#define B2056_TX_BB_GM_MASTER 0x65 -#define B2056_TX_GMBB_GM 0x66 -#define B2056_TX_GMBB_IDAC 0x67 -#define B2056_TX_TXLPF_MASTER 0x68 -#define B2056_TX_TXLPF_RCCAL 0x69 -#define B2056_TX_TXLPF_RCCAL_OFF0 0x6A -#define B2056_TX_TXLPF_RCCAL_OFF1 0x6B -#define B2056_TX_TXLPF_RCCAL_OFF2 0x6C -#define B2056_TX_TXLPF_RCCAL_OFF3 0x6D -#define B2056_TX_TXLPF_RCCAL_OFF4 0x6E -#define B2056_TX_TXLPF_RCCAL_OFF5 0x6F -#define B2056_TX_TXLPF_RCCAL_OFF6 0x70 -#define B2056_TX_TXLPF_BW 0x71 -#define B2056_TX_TXLPF_GAIN 0x72 -#define B2056_TX_TXLPF_IDAC 0x73 -#define B2056_TX_TXLPF_IDAC_0 0x74 -#define B2056_TX_TXLPF_IDAC_1 0x75 -#define B2056_TX_TXLPF_IDAC_2 0x76 -#define B2056_TX_TXLPF_IDAC_3 0x77 -#define B2056_TX_TXLPF_IDAC_4 0x78 -#define B2056_TX_TXLPF_IDAC_5 0x79 -#define B2056_TX_TXLPF_IDAC_6 0x7A -#define B2056_TX_TXLPF_OPAMP_IDAC 0x7B -#define B2056_TX_TXLPF_MISC 0x7C -#define B2056_TX_TXSPARE1 0x7D -#define B2056_TX_TXSPARE2 0x7E -#define B2056_TX_TXSPARE3 0x7F -#define B2056_TX_TXSPARE4 0x80 -#define B2056_TX_TXSPARE5 0x81 -#define B2056_TX_TXSPARE6 0x82 -#define B2056_TX_TXSPARE7 0x83 -#define B2056_TX_TXSPARE8 0x84 -#define B2056_TX_TXSPARE9 0x85 -#define B2056_TX_TXSPARE10 0x86 -#define B2056_TX_TXSPARE11 0x87 -#define B2056_TX_TXSPARE12 0x88 -#define B2056_TX_TXSPARE13 0x89 -#define B2056_TX_TXSPARE14 0x8A -#define B2056_TX_TXSPARE15 0x8B -#define B2056_TX_TXSPARE16 0x8C -#define B2056_TX_STATUS_INTPA_GAIN 0x8D -#define B2056_TX_STATUS_PAD_GAIN 0x8E -#define B2056_TX_STATUS_PGA_GAIN 0x8F -#define B2056_TX_STATUS_GM_TXLPF_GAIN 0x90 -#define B2056_TX_STATUS_TXLPF_BW 0x91 -#define B2056_TX_STATUS_TXLPF_RC 0x92 -#define B2056_TX_GMBB_IDAC0 0x93 -#define B2056_TX_GMBB_IDAC1 0x94 -#define B2056_TX_GMBB_IDAC2 0x95 -#define B2056_TX_GMBB_IDAC3 0x96 -#define B2056_TX_GMBB_IDAC4 0x97 -#define B2056_TX_GMBB_IDAC5 0x98 -#define B2056_TX_GMBB_IDAC6 0x99 -#define B2056_TX_GMBB_IDAC7 0x9A - -#define B2056_RX_RESERVED_ADDR0 0x00 -#define B2056_RX_IDCODE 0x01 -#define B2056_RX_RESERVED_ADDR2 0x02 -#define B2056_RX_RESERVED_ADDR3 0x03 -#define B2056_RX_RESERVED_ADDR4 0x04 -#define B2056_RX_RESERVED_ADDR5 0x05 -#define B2056_RX_RESERVED_ADDR6 0x06 -#define B2056_RX_RESERVED_ADDR7 0x07 -#define B2056_RX_COM_CTRL 0x08 -#define B2056_RX_COM_PU 0x09 -#define B2056_RX_COM_OVR 0x0A -#define B2056_RX_COM_RESET 0x0B -#define B2056_RX_COM_RCAL 0x0C -#define B2056_RX_COM_RC_RXLPF 0x0D -#define B2056_RX_COM_RC_TXLPF 0x0E -#define B2056_RX_COM_RC_RXHPF 0x0F -#define B2056_RX_RESERVED_ADDR16 0x10 -#define B2056_RX_RESERVED_ADDR17 0x11 -#define B2056_RX_RESERVED_ADDR18 0x12 -#define B2056_RX_RESERVED_ADDR19 0x13 -#define B2056_RX_RESERVED_ADDR20 0x14 -#define B2056_RX_RESERVED_ADDR21 0x15 -#define B2056_RX_RESERVED_ADDR22 0x16 -#define B2056_RX_RESERVED_ADDR23 0x17 -#define B2056_RX_RESERVED_ADDR24 0x18 -#define B2056_RX_RESERVED_ADDR25 0x19 -#define B2056_RX_RESERVED_ADDR26 0x1A -#define B2056_RX_RESERVED_ADDR27 0x1B -#define B2056_RX_RESERVED_ADDR28 0x1C -#define B2056_RX_RESERVED_ADDR29 0x1D -#define B2056_RX_RESERVED_ADDR30 0x1E -#define B2056_RX_RESERVED_ADDR31 0x1F -#define B2056_RX_RXIQCAL_RXMUX 0x20 -#define B2056_RX_RSSI_PU 0x21 -#define B2056_RX_RSSI_SEL 0x22 -#define B2056_RX_RSSI_GAIN 0x23 -#define B2056_RX_RSSI_NB_IDAC 0x24 -#define B2056_RX_RSSI_WB2I_IDAC_1 0x25 -#define B2056_RX_RSSI_WB2I_IDAC_2 0x26 -#define B2056_RX_RSSI_WB2Q_IDAC_1 0x27 -#define B2056_RX_RSSI_WB2Q_IDAC_2 0x28 -#define B2056_RX_RSSI_POLE 0x29 -#define B2056_RX_RSSI_WB1_IDAC 0x2A -#define B2056_RX_RSSI_MISC 0x2B -#define B2056_RX_LNAA_MASTER 0x2C -#define B2056_RX_LNAA_TUNE 0x2D -#define B2056_RX_LNAA_GAIN 0x2E -#define B2056_RX_LNA_A_SLOPE 0x2F -#define B2056_RX_BIASPOLE_LNAA1_IDAC 0x30 -#define B2056_RX_LNAA2_IDAC 0x31 -#define B2056_RX_LNA1A_MISC 0x32 -#define B2056_RX_LNAG_MASTER 0x33 -#define B2056_RX_LNAG_TUNE 0x34 -#define B2056_RX_LNAG_GAIN 0x35 -#define B2056_RX_LNA_G_SLOPE 0x36 -#define B2056_RX_BIASPOLE_LNAG1_IDAC 0x37 -#define B2056_RX_LNAG2_IDAC 0x38 -#define B2056_RX_LNA1G_MISC 0x39 -#define B2056_RX_MIXA_MASTER 0x3A -#define B2056_RX_MIXA_VCM 0x3B -#define B2056_RX_MIXA_CTRLPTAT 0x3C -#define B2056_RX_MIXA_LOB_BIAS 0x3D -#define B2056_RX_MIXA_CORE_IDAC 0x3E -#define B2056_RX_MIXA_CMFB_IDAC 0x3F -#define B2056_RX_MIXA_BIAS_AUX 0x40 -#define B2056_RX_MIXA_BIAS_MAIN 0x41 -#define B2056_RX_MIXA_BIAS_MISC 0x42 -#define B2056_RX_MIXA_MAST_BIAS 0x43 -#define B2056_RX_MIXG_MASTER 0x44 -#define B2056_RX_MIXG_VCM 0x45 -#define B2056_RX_MIXG_CTRLPTAT 0x46 -#define B2056_RX_MIXG_LOB_BIAS 0x47 -#define B2056_RX_MIXG_CORE_IDAC 0x48 -#define B2056_RX_MIXG_CMFB_IDAC 0x49 -#define B2056_RX_MIXG_BIAS_AUX 0x4A -#define B2056_RX_MIXG_BIAS_MAIN 0x4B -#define B2056_RX_MIXG_BIAS_MISC 0x4C -#define B2056_RX_MIXG_MAST_BIAS 0x4D -#define B2056_RX_TIA_MASTER 0x4E -#define B2056_RX_TIA_IOPAMP 0x4F -#define B2056_RX_TIA_QOPAMP 0x50 -#define B2056_RX_TIA_IMISC 0x51 -#define B2056_RX_TIA_QMISC 0x52 -#define B2056_RX_TIA_GAIN 0x53 -#define B2056_RX_TIA_SPARE1 0x54 -#define B2056_RX_TIA_SPARE2 0x55 -#define B2056_RX_BB_LPF_MASTER 0x56 -#define B2056_RX_AACI_MASTER 0x57 -#define B2056_RX_RXLPF_IDAC 0x58 -#define B2056_RX_RXLPF_OPAMPBIAS_LOWQ 0x59 -#define B2056_RX_RXLPF_OPAMPBIAS_HIGHQ 0x5A -#define B2056_RX_RXLPF_BIAS_DCCANCEL 0x5B -#define B2056_RX_RXLPF_OUTVCM 0x5C -#define B2056_RX_RXLPF_INVCM_BODY 0x5D -#define B2056_RX_RXLPF_CC_OP 0x5E -#define B2056_RX_RXLPF_GAIN 0x5F -#define B2056_RX_RXLPF_Q_BW 0x60 -#define B2056_RX_RXLPF_HP_CORNER_BW 0x61 -#define B2056_RX_RXLPF_RCCAL_HPC 0x62 -#define B2056_RX_RXHPF_OFF0 0x63 -#define B2056_RX_RXHPF_OFF1 0x64 -#define B2056_RX_RXHPF_OFF2 0x65 -#define B2056_RX_RXHPF_OFF3 0x66 -#define B2056_RX_RXHPF_OFF4 0x67 -#define B2056_RX_RXHPF_OFF5 0x68 -#define B2056_RX_RXHPF_OFF6 0x69 -#define B2056_RX_RXHPF_OFF7 0x6A -#define B2056_RX_RXLPF_RCCAL_LPC 0x6B -#define B2056_RX_RXLPF_OFF_0 0x6C -#define B2056_RX_RXLPF_OFF_1 0x6D -#define B2056_RX_RXLPF_OFF_2 0x6E -#define B2056_RX_RXLPF_OFF_3 0x6F -#define B2056_RX_RXLPF_OFF_4 0x70 -#define B2056_RX_UNUSED 0x71 -#define B2056_RX_VGA_MASTER 0x72 -#define B2056_RX_VGA_BIAS 0x73 -#define B2056_RX_VGA_BIAS_DCCANCEL 0x74 -#define B2056_RX_VGA_GAIN 0x75 -#define B2056_RX_VGA_HP_CORNER_BW 0x76 -#define B2056_RX_VGABUF_BIAS 0x77 -#define B2056_RX_VGABUF_GAIN_BW 0x78 -#define B2056_RX_TXFBMIX_A 0x79 -#define B2056_RX_TXFBMIX_G 0x7A -#define B2056_RX_RXSPARE1 0x7B -#define B2056_RX_RXSPARE2 0x7C -#define B2056_RX_RXSPARE3 0x7D -#define B2056_RX_RXSPARE4 0x7E -#define B2056_RX_RXSPARE5 0x7F -#define B2056_RX_RXSPARE6 0x80 -#define B2056_RX_RXSPARE7 0x81 -#define B2056_RX_RXSPARE8 0x82 -#define B2056_RX_RXSPARE9 0x83 -#define B2056_RX_RXSPARE10 0x84 -#define B2056_RX_RXSPARE11 0x85 -#define B2056_RX_RXSPARE12 0x86 -#define B2056_RX_RXSPARE13 0x87 -#define B2056_RX_RXSPARE14 0x88 -#define B2056_RX_RXSPARE15 0x89 -#define B2056_RX_RXSPARE16 0x8A -#define B2056_RX_STATUS_LNAA_GAIN 0x8B -#define B2056_RX_STATUS_LNAG_GAIN 0x8C -#define B2056_RX_STATUS_MIXTIA_GAIN 0x8D -#define B2056_RX_STATUS_RXLPF_GAIN 0x8E -#define B2056_RX_STATUS_VGA_BUF_GAIN 0x8F -#define B2056_RX_STATUS_RXLPF_Q 0x90 -#define B2056_RX_STATUS_RXLPF_BUF_BW 0x91 -#define B2056_RX_STATUS_RXLPF_VGA_HPC 0x92 -#define B2056_RX_STATUS_RXLPF_RC 0x93 -#define B2056_RX_STATUS_HPC_RC 0x94 - -#define B2056_LNA1_A_PU 0x01 -#define B2056_LNA2_A_PU 0x02 -#define B2056_LNA1_G_PU 0x01 -#define B2056_LNA2_G_PU 0x02 -#define B2056_MIXA_PU_I 0x01 -#define B2056_MIXA_PU_Q 0x02 -#define B2056_MIXA_PU_GM 0x10 -#define B2056_MIXG_PU_I 0x01 -#define B2056_MIXG_PU_Q 0x02 -#define B2056_MIXG_PU_GM 0x10 -#define B2056_TIA_PU 0x01 -#define B2056_BB_LPF_PU 0x20 -#define B2056_W1_PU 0x02 -#define B2056_W2_PU 0x04 -#define B2056_NB_PU 0x08 -#define B2056_RSSI_W1_SEL 0x02 -#define B2056_RSSI_W2_SEL 0x04 -#define B2056_RSSI_NB_SEL 0x08 -#define B2056_VCM_MASK 0x1C -#define B2056_RSSI_VCM_SHIFT 0x02 - -struct b43_nphy_channeltab_entry_rev3 { - /* The channel frequency in MHz */ - u16 freq; - /* Radio register values on channelswitch */ - u8 radio_syn_pll_vcocal1; - u8 radio_syn_pll_vcocal2; - u8 radio_syn_pll_refdiv; - u8 radio_syn_pll_mmd2; - u8 radio_syn_pll_mmd1; - u8 radio_syn_pll_loopfilter1; - u8 radio_syn_pll_loopfilter2; - u8 radio_syn_pll_loopfilter3; - u8 radio_syn_pll_loopfilter4; - u8 radio_syn_pll_loopfilter5; - u8 radio_syn_reserved_addr27; - u8 radio_syn_reserved_addr28; - u8 radio_syn_reserved_addr29; - u8 radio_syn_logen_vcobuf1; - u8 radio_syn_logen_mixer2; - u8 radio_syn_logen_buf3; - u8 radio_syn_logen_buf4; - u8 radio_rx0_lnaa_tune; - u8 radio_rx0_lnag_tune; - u8 radio_tx0_intpaa_boost_tune; - u8 radio_tx0_intpag_boost_tune; - u8 radio_tx0_pada_boost_tune; - u8 radio_tx0_padg_boost_tune; - u8 radio_tx0_pgaa_boost_tune; - u8 radio_tx0_pgag_boost_tune; - u8 radio_tx0_mixa_boost_tune; - u8 radio_tx0_mixg_boost_tune; - u8 radio_rx1_lnaa_tune; - u8 radio_rx1_lnag_tune; - u8 radio_tx1_intpaa_boost_tune; - u8 radio_tx1_intpag_boost_tune; - u8 radio_tx1_pada_boost_tune; - u8 radio_tx1_padg_boost_tune; - u8 radio_tx1_pgaa_boost_tune; - u8 radio_tx1_pgag_boost_tune; - u8 radio_tx1_mixa_boost_tune; - u8 radio_tx1_mixg_boost_tune; - /* PHY register values on channelswitch */ - struct b43_phy_n_sfo_cfg phy_regs; -}; - -void b2056_upload_inittabs(struct b43_wldev *dev, - bool ghz5, bool ignore_uploadflag); -void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5); - -/* Get the NPHY Channel Switch Table entry for a channel. - * Returns NULL on failure to find an entry. */ -const struct b43_nphy_channeltab_entry_rev3 * -b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq); - -#endif /* B43_RADIO_2056_H_ */ diff --git a/drivers/net/wireless/b43/radio_2057.c b/drivers/net/wireless/b43/radio_2057.c deleted file mode 100644 index ff1e026a61a1..000000000000 --- a/drivers/net/wireless/b43/radio_2057.c +++ /dev/null @@ -1,637 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11n 2057 radio device data tables - - Copyright (c) 2010 RafaÅ‚ MiÅ‚ecki - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "radio_2057.h" -#include "phy_common.h" - -static u16 r2057_rev4_init[][2] = { - { 0x0E, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, - { 0x35, 0x26 }, { 0x3C, 0xff }, { 0x3D, 0xff }, { 0x3E, 0xff }, - { 0x3F, 0xff }, { 0x62, 0x33 }, { 0x8A, 0xf0 }, { 0x8B, 0x10 }, - { 0x8C, 0xf0 }, { 0x91, 0x3f }, { 0x92, 0x36 }, { 0xA4, 0x8c }, - { 0xA8, 0x55 }, { 0xAF, 0x01 }, { 0x10F, 0xf0 }, { 0x110, 0x10 }, - { 0x111, 0xf0 }, { 0x116, 0x3f }, { 0x117, 0x36 }, { 0x129, 0x8c }, - { 0x12D, 0x55 }, { 0x134, 0x01 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, - { 0x160, 0x00 }, { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, - { 0x169, 0x02 }, { 0x16A, 0x00 }, { 0x16B, 0x00 }, { 0x16C, 0x00 }, - { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, - { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, -}; - -static u16 r2057_rev5_init[][2] = { - { 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 }, - { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, - { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, - { 0x64, 0x0f }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, - { 0xA1, 0x20 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, { 0xE1, 0x20 }, - { 0xE8, 0x0f }, { 0xE9, 0x0f }, { 0x106, 0x01 }, { 0x116, 0x3f }, - { 0x117, 0x36 }, { 0x126, 0x20 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, - { 0x160, 0x00 }, { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, - { 0x16A, 0x00 }, { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, - { 0x1A5, 0x00 }, { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, - { 0x1AC, 0x00 }, { 0x1B7, 0x0c }, { 0x1C1, 0x01 }, { 0x1C2, 0x80 }, -}; - -static u16 r2057_rev5a_init[][2] = { - { 0x00, 0x15 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 }, - { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, - { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, - { 0x64, 0x0f }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, - { 0xC9, 0x01 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, { 0xE1, 0x20 }, - { 0xE8, 0x0f }, { 0xE9, 0x0f }, { 0x106, 0x01 }, { 0x116, 0x3f }, - { 0x117, 0x36 }, { 0x126, 0x20 }, { 0x14E, 0x01 }, { 0x15E, 0x00 }, - { 0x15F, 0x00 }, { 0x160, 0x00 }, { 0x161, 0x00 }, { 0x162, 0x00 }, - { 0x163, 0x00 }, { 0x16A, 0x00 }, { 0x16B, 0x00 }, { 0x16C, 0x00 }, - { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, - { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, { 0x1B7, 0x0c }, { 0x1C1, 0x01 }, - { 0x1C2, 0x80 }, -}; - -static u16 r2057_rev7_init[][2] = { - { 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 }, - { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, - { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x13 }, - { 0x66, 0xee }, { 0x6E, 0x58 }, { 0x75, 0x13 }, { 0x7B, 0x13 }, - { 0x7C, 0x14 }, { 0x7D, 0xee }, { 0x81, 0x01 }, { 0x91, 0x3f }, - { 0x92, 0x36 }, { 0xA1, 0x20 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, - { 0xE1, 0x20 }, { 0xE8, 0x0f }, { 0xE9, 0x13 }, { 0xEB, 0xee }, - { 0xF3, 0x58 }, { 0xFA, 0x13 }, { 0x100, 0x13 }, { 0x101, 0x14 }, - { 0x102, 0xee }, { 0x106, 0x01 }, { 0x116, 0x3f }, { 0x117, 0x36 }, - { 0x126, 0x20 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, { 0x160, 0x00 }, - { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, { 0x16A, 0x00 }, - { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, - { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, - { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, -}; - -/* TODO: Which devices should use it? -static u16 r2057_rev8_init[][2] = { - { 0x00, 0x08 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 }, - { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, - { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f }, - { 0x6E, 0x58 }, { 0x75, 0x13 }, { 0x7B, 0x13 }, { 0x7C, 0x0f }, - { 0x7D, 0xee }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, - { 0xA1, 0x20 }, { 0xC9, 0x01 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, - { 0xE1, 0x20 }, { 0xE8, 0x0f }, { 0xE9, 0x0f }, { 0xF3, 0x58 }, - { 0xFA, 0x13 }, { 0x100, 0x13 }, { 0x101, 0x0f }, { 0x102, 0xee }, - { 0x106, 0x01 }, { 0x116, 0x3f }, { 0x117, 0x36 }, { 0x126, 0x20 }, - { 0x14E, 0x01 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, { 0x160, 0x00 }, - { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, { 0x16A, 0x00 }, - { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, - { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, - { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, -}; -*/ - -/* Extracted from MMIO dump of 6.30.223.141 */ -static u16 r2057_rev9_init[][2] = { - { 0x27, 0x1f }, { 0x28, 0x0a }, { 0x29, 0x2f }, { 0x42, 0x1f }, - { 0x48, 0x3f }, { 0x5c, 0x41 }, { 0x63, 0x14 }, { 0x64, 0x12 }, - { 0x66, 0xff }, { 0x74, 0xa3 }, { 0x7b, 0x14 }, { 0x7c, 0x14 }, - { 0x7d, 0xee }, { 0x86, 0xc0 }, { 0xc4, 0x10 }, { 0xc9, 0x01 }, - { 0xe1, 0x41 }, { 0xe8, 0x14 }, { 0xe9, 0x12 }, { 0xeb, 0xff }, - { 0xf5, 0x0a }, { 0xf8, 0x09 }, { 0xf9, 0xa3 }, { 0x100, 0x14 }, - { 0x101, 0x10 }, { 0x102, 0xee }, { 0x10b, 0xc0 }, { 0x149, 0x10 }, - { 0x14e, 0x01 }, { 0x1b7, 0x05 }, { 0x1c2, 0xa0 }, -}; - -/* Extracted from MMIO dump of 6.30.223.248 */ -static u16 r2057_rev14_init[][2] = { - { 0x011, 0xfc }, { 0x030, 0x24 }, { 0x040, 0x1c }, { 0x082, 0x08 }, - { 0x0b4, 0x44 }, { 0x0c8, 0x01 }, { 0x0c9, 0x01 }, { 0x107, 0x08 }, - { 0x14d, 0x01 }, { 0x14e, 0x01 }, { 0x1af, 0x40 }, { 0x1b0, 0x40 }, - { 0x1cc, 0x01 }, { 0x1cf, 0x10 }, { 0x1d0, 0x0f }, { 0x1d3, 0x10 }, - { 0x1d4, 0x0f }, -}; - -#define RADIOREGS7(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ - r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ - r20, r21, r22, r23, r24, r25, r26, r27) \ - .radio_vcocal_countval0 = r00, \ - .radio_vcocal_countval1 = r01, \ - .radio_rfpll_refmaster_sparextalsize = r02, \ - .radio_rfpll_loopfilter_r1 = r03, \ - .radio_rfpll_loopfilter_c2 = r04, \ - .radio_rfpll_loopfilter_c1 = r05, \ - .radio_cp_kpd_idac = r06, \ - .radio_rfpll_mmd0 = r07, \ - .radio_rfpll_mmd1 = r08, \ - .radio_vcobuf_tune = r09, \ - .radio_logen_mx2g_tune = r10, \ - .radio_logen_mx5g_tune = r11, \ - .radio_logen_indbuf2g_tune = r12, \ - .radio_logen_indbuf5g_tune = r13, \ - .radio_txmix2g_tune_boost_pu_core0 = r14, \ - .radio_pad2g_tune_pus_core0 = r15, \ - .radio_pga_boost_tune_core0 = r16, \ - .radio_txmix5g_boost_tune_core0 = r17, \ - .radio_pad5g_tune_misc_pus_core0 = r18, \ - .radio_lna2g_tune_core0 = r19, \ - .radio_lna5g_tune_core0 = r20, \ - .radio_txmix2g_tune_boost_pu_core1 = r21, \ - .radio_pad2g_tune_pus_core1 = r22, \ - .radio_pga_boost_tune_core1 = r23, \ - .radio_txmix5g_boost_tune_core1 = r24, \ - .radio_pad5g_tune_misc_pus_core1 = r25, \ - .radio_lna2g_tune_core1 = r26, \ - .radio_lna5g_tune_core1 = r27 - -#define RADIOREGS7_2G(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ - r10, r11, r12, r13, r14, r15, r16, r17) \ - .radio_vcocal_countval0 = r00, \ - .radio_vcocal_countval1 = r01, \ - .radio_rfpll_refmaster_sparextalsize = r02, \ - .radio_rfpll_loopfilter_r1 = r03, \ - .radio_rfpll_loopfilter_c2 = r04, \ - .radio_rfpll_loopfilter_c1 = r05, \ - .radio_cp_kpd_idac = r06, \ - .radio_rfpll_mmd0 = r07, \ - .radio_rfpll_mmd1 = r08, \ - .radio_vcobuf_tune = r09, \ - .radio_logen_mx2g_tune = r10, \ - .radio_logen_indbuf2g_tune = r11, \ - .radio_txmix2g_tune_boost_pu_core0 = r12, \ - .radio_pad2g_tune_pus_core0 = r13, \ - .radio_lna2g_tune_core0 = r14, \ - .radio_txmix2g_tune_boost_pu_core1 = r15, \ - .radio_pad2g_tune_pus_core1 = r16, \ - .radio_lna2g_tune_core1 = r17 - -#define PHYREGS(r0, r1, r2, r3, r4, r5) \ - .phy_regs.phy_bw1a = r0, \ - .phy_regs.phy_bw2 = r1, \ - .phy_regs.phy_bw3 = r2, \ - .phy_regs.phy_bw4 = r3, \ - .phy_regs.phy_bw5 = r4, \ - .phy_regs.phy_bw6 = r5 - -/* Copied from brcmsmac (5.75.11): chan_info_nphyrev8_2057_rev5 */ -static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev8_radio_rev5[] = { - { - .freq = 2412, - RADIOREGS7_2G(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, - 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, - 0x03, 0xff), - PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), - }, - { - .freq = 2417, - RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, - 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, - 0x03, 0xff), - PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), - }, - { - .freq = 2422, - RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, - 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xef, 0x61, - 0x03, 0xef), - PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), - }, - { - .freq = 2427, - RADIOREGS7_2G(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, - 0x09, 0x0c, 0x08, 0x0e, 0x61, 0x03, 0xdf, 0x61, - 0x03, 0xdf), - PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), - }, - { - .freq = 2432, - RADIOREGS7_2G(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, - 0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xcf, 0x61, - 0x03, 0xcf), - PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), - }, - { - .freq = 2437, - RADIOREGS7_2G(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, - 0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xbf, 0x61, - 0x03, 0xbf), - PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), - }, - { - .freq = 2442, - RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, - 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0xaf, 0x61, - 0x03, 0xaf), - PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), - }, - { - .freq = 2447, - RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, - 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x9f, 0x61, - 0x03, 0x9f), - PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), - }, - { - .freq = 2452, - RADIOREGS7_2G(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, - 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x8f, 0x61, - 0x03, 0x8f), - PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), - }, - { - .freq = 2457, - RADIOREGS7_2G(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, - 0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x7f, 0x61, - 0x03, 0x7f), - PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), - }, - { - .freq = 2462, - RADIOREGS7_2G(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, - 0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x6f, 0x61, - 0x03, 0x6f), - PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), - }, - { - .freq = 2467, - RADIOREGS7_2G(0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, - 0x09, 0x0b, 0x06, 0x0c, 0x61, 0x03, 0x5f, 0x61, - 0x03, 0x5f), - PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), - }, - { - .freq = 2472, - RADIOREGS7_2G(0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, - 0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x4f, 0x61, - 0x03, 0x4f), - PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), - }, - { - .freq = 2484, - RADIOREGS7_2G(0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, - 0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x3f, 0x61, - 0x03, 0x3f), - PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), - } -}; - -/* Extracted from MMIO dump of 6.30.223.248 */ -static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev17_radio_rev14[] = { - { - .freq = 2412, - RADIOREGS7_2G(0x48, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x6c, - 0x09, 0x0d, 0x09, 0x03, 0x21, 0x53, 0xff, 0x21, - 0x53, 0xff), - PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), - }, - { - .freq = 2417, - RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x71, - 0x09, 0x0d, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, - 0x53, 0xff), - PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), - }, - { - .freq = 2422, - RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x76, - 0x09, 0x0d, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, - 0x53, 0xff), - PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), - }, - { - .freq = 2427, - RADIOREGS7_2G(0x52, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x7b, - 0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, - 0x53, 0xff), - PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), - }, - { - .freq = 2432, - RADIOREGS7_2G(0x55, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x80, - 0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, - 0x53, 0xff), - PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), - }, - { - .freq = 2437, - RADIOREGS7_2G(0x58, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x85, - 0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, - 0x53, 0xff), - PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), - }, - { - .freq = 2442, - RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x8a, - 0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21, - 0x43, 0xff), - PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), - }, - { - .freq = 2447, - RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x8f, - 0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21, - 0x43, 0xff), - PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), - }, - { - .freq = 2452, - RADIOREGS7_2G(0x62, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x94, - 0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21, - 0x43, 0xff), - PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), - }, - { - .freq = 2457, - RADIOREGS7_2G(0x66, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x99, - 0x09, 0x0b, 0x07, 0x03, 0x21, 0x43, 0xff, 0x21, - 0x43, 0xff), - PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), - }, - { - .freq = 2462, - RADIOREGS7_2G(0x69, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x9e, - 0x09, 0x0b, 0x07, 0x03, 0x01, 0x43, 0xff, 0x01, - 0x43, 0xff), - PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), - }, -}; - -/* Extracted from MMIO dump of 6.30.223.141 */ -static const struct b43_nphy_chantabent_rev7 b43_nphy_chantab_phy_rev16_radio_rev9[] = { - { - .freq = 2412, - RADIOREGS7(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, - 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x41, 0x63, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, - 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), - }, - { - .freq = 2417, - RADIOREGS7(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, - 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x41, 0x63, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, - 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), - }, - { - .freq = 2422, - RADIOREGS7(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, - 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x41, 0x63, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, - 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), - }, - { - .freq = 2427, - RADIOREGS7(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, - 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x41, 0x63, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, - 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), - }, - { - .freq = 2432, - RADIOREGS7(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, - 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x41, 0x63, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, - 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), - }, - { - .freq = 2437, - RADIOREGS7(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, - 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x41, 0x63, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, - 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), - }, - { - .freq = 2442, - RADIOREGS7(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, - 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, - 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), - }, - { - .freq = 2447, - RADIOREGS7(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, - 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, - 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), - }, - { - .freq = 2452, - RADIOREGS7(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, - 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, - 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), - }, - { - .freq = 2457, - RADIOREGS7(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, - 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x41, 0x63, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, - 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), - }, - { - .freq = 2462, - RADIOREGS7(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, - 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x41, 0x63, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, - 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), - }, - { - .freq = 5180, - RADIOREGS7(0xbe, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x06, - 0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00, - 0x9f, 0x2f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x4f, - 0x3a, 0x83, 0x00, 0xfc), - PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), - }, - { - .freq = 5200, - RADIOREGS7(0xc5, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x08, - 0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00, - 0x7f, 0x2f, 0x83, 0x00, 0xf8, 0x00, 0x00, 0x4c, - 0x4a, 0x83, 0x00, 0xf8), - PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), - }, - { - .freq = 5220, - RADIOREGS7(0xcc, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0a, - 0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00, - 0x6d, 0x3d, 0x83, 0x00, 0xf8, 0x00, 0x00, 0x2d, - 0x2a, 0x73, 0x00, 0xf8), - PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), - }, - { - .freq = 5240, - RADIOREGS7(0xd2, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0c, - 0x02, 0x0d, 0x00, 0x0d, 0x00, 0x8d, 0x00, 0x00, - 0x4d, 0x1c, 0x73, 0x00, 0xf8, 0x00, 0x00, 0x4d, - 0x2b, 0x73, 0x00, 0xf8), - PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), - }, - { - .freq = 5745, - RADIOREGS7(0x7b, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x7d, - 0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, - 0x08, 0x03, 0x03, 0x00, 0x30, 0x00, 0x00, 0x06, - 0x02, 0x03, 0x00, 0x30), - PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), - }, - { - .freq = 5765, - RADIOREGS7(0x81, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x81, - 0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, - 0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, - 0x02, 0x03, 0x00, 0x00), - PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), - }, - { - .freq = 5785, - RADIOREGS7(0x88, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x85, - 0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, - 0x08, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, - 0x21, 0x03, 0x00, 0x00), - PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), - }, - { - .freq = 5805, - RADIOREGS7(0x8f, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x89, - 0x04, 0x07, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, - 0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x03, 0x00, 0x00), - PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), - }, - { - .freq = 5825, - RADIOREGS7(0x95, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x8d, - 0x04, 0x07, 0x00, 0x05, 0x00, 0x03, 0x00, 0x00, - 0x05, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x03, 0x00, 0x00), - PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), - }, -}; - -void r2057_upload_inittabs(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - u16 *table = NULL; - u16 size, i; - - switch (phy->rev) { - case 7: - table = r2057_rev4_init[0]; - size = ARRAY_SIZE(r2057_rev4_init); - break; - case 8: - if (phy->radio_rev == 5) { - table = r2057_rev5_init[0]; - size = ARRAY_SIZE(r2057_rev5_init); - } else if (phy->radio_rev == 7) { - table = r2057_rev7_init[0]; - size = ARRAY_SIZE(r2057_rev7_init); - } - break; - case 9: - if (phy->radio_rev == 5) { - table = r2057_rev5a_init[0]; - size = ARRAY_SIZE(r2057_rev5a_init); - } - break; - case 16: - if (phy->radio_rev == 9) { - table = r2057_rev9_init[0]; - size = ARRAY_SIZE(r2057_rev9_init); - } - break; - case 17: - if (phy->radio_rev == 14) { - table = r2057_rev14_init[0]; - size = ARRAY_SIZE(r2057_rev14_init); - } - break; - } - - B43_WARN_ON(!table); - - if (table) { - for (i = 0; i < size; i++, table += 2) - b43_radio_write(dev, table[0], table[1]); - } -} - -void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, - const struct b43_nphy_chantabent_rev7 **tabent_r7, - const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g) -{ - struct b43_phy *phy = &dev->phy; - const struct b43_nphy_chantabent_rev7 *e_r7 = NULL; - const struct b43_nphy_chantabent_rev7_2g *e_r7_2g = NULL; - unsigned int len, i; - - *tabent_r7 = NULL; - *tabent_r7_2g = NULL; - - switch (phy->rev) { - case 8: - if (phy->radio_rev == 5) { - e_r7_2g = b43_nphy_chantab_phy_rev8_radio_rev5; - len = ARRAY_SIZE(b43_nphy_chantab_phy_rev8_radio_rev5); - } - break; - case 16: - if (phy->radio_rev == 9) { - e_r7 = b43_nphy_chantab_phy_rev16_radio_rev9; - len = ARRAY_SIZE(b43_nphy_chantab_phy_rev16_radio_rev9); - } - break; - case 17: - if (phy->radio_rev == 14) { - e_r7_2g = b43_nphy_chantab_phy_rev17_radio_rev14; - len = ARRAY_SIZE(b43_nphy_chantab_phy_rev17_radio_rev14); - } - break; - default: - break; - } - - if (e_r7) { - for (i = 0; i < len; i++, e_r7++) { - if (e_r7->freq == freq) { - *tabent_r7 = e_r7; - return; - } - } - } else if (e_r7_2g) { - for (i = 0; i < len; i++, e_r7_2g++) { - if (e_r7_2g->freq == freq) { - *tabent_r7_2g = e_r7_2g; - return; - } - } - } else { - B43_WARN_ON(1); - } -} diff --git a/drivers/net/wireless/b43/radio_2057.h b/drivers/net/wireless/b43/radio_2057.h deleted file mode 100644 index 220d080238ff..000000000000 --- a/drivers/net/wireless/b43/radio_2057.h +++ /dev/null @@ -1,506 +0,0 @@ -#ifndef B43_RADIO_2057_H_ -#define B43_RADIO_2057_H_ - -#include - -#include "tables_nphy.h" - -#define R2057_DACBUF_VINCM_CORE0 0x000 -#define R2057_IDCODE 0x001 -#define R2057_RCCAL_MASTER 0x002 -#define R2057_RCCAL_CAP_SIZE 0x003 -#define R2057_RCAL_CONFIG 0x004 -#define R2057_GPAIO_CONFIG 0x005 -#define R2057_GPAIO_SEL1 0x006 -#define R2057_GPAIO_SEL0 0x007 -#define R2057_CLPO_CONFIG 0x008 -#define R2057_BANDGAP_CONFIG 0x009 -#define R2057_BANDGAP_RCAL_TRIM 0x00a -#define R2057_AFEREG_CONFIG 0x00b -#define R2057_TEMPSENSE_CONFIG 0x00c -#define R2057_XTAL_CONFIG1 0x00d -#define R2057_XTAL_ICORE_SIZE 0x00e -#define R2057_XTAL_BUF_SIZE 0x00f -#define R2057_XTAL_PULLCAP_SIZE 0x010 -#define R2057_RFPLL_MASTER 0x011 -#define R2057_VCOMONITOR_VTH_L 0x012 -#define R2057_VCOMONITOR_VTH_H 0x013 -#define R2057_VCOCAL_BIASRESET_RFPLLREG_VOUT 0x014 -#define R2057_VCO_VARCSIZE_IDAC 0x015 -#define R2057_VCOCAL_COUNTVAL0 0x016 -#define R2057_VCOCAL_COUNTVAL1 0x017 -#define R2057_VCOCAL_INTCLK_COUNT 0x018 -#define R2057_VCOCAL_MASTER 0x019 -#define R2057_VCOCAL_NUMCAPCHANGE 0x01a -#define R2057_VCOCAL_WINSIZE 0x01b -#define R2057_VCOCAL_DELAY_AFTER_REFRESH 0x01c -#define R2057_VCOCAL_DELAY_AFTER_CLOSELOOP 0x01d -#define R2057_VCOCAL_DELAY_AFTER_OPENLOOP 0x01e -#define R2057_VCOCAL_DELAY_BEFORE_OPENLOOP 0x01f -#define R2057_VCO_FORCECAPEN_FORCECAP1 0x020 -#define R2057_VCO_FORCECAP0 0x021 -#define R2057_RFPLL_REFMASTER_SPAREXTALSIZE 0x022 -#define R2057_RFPLL_PFD_RESET_PW 0x023 -#define R2057_RFPLL_LOOPFILTER_R2 0x024 -#define R2057_RFPLL_LOOPFILTER_R1 0x025 -#define R2057_RFPLL_LOOPFILTER_C3 0x026 -#define R2057_RFPLL_LOOPFILTER_C2 0x027 -#define R2057_RFPLL_LOOPFILTER_C1 0x028 -#define R2057_CP_KPD_IDAC 0x029 -#define R2057_RFPLL_IDACS 0x02a -#define R2057_RFPLL_MISC_EN 0x02b -#define R2057_RFPLL_MMD0 0x02c -#define R2057_RFPLL_MMD1 0x02d -#define R2057_RFPLL_MISC_CAL_RESETN 0x02e -#define R2057_JTAGXTAL_SIZE_CPBIAS_FILTRES 0x02f -#define R2057_VCO_ALCREF_BBPLLXTAL_SIZE 0x030 -#define R2057_VCOCAL_READCAP0 0x031 -#define R2057_VCOCAL_READCAP1 0x032 -#define R2057_VCOCAL_STATUS 0x033 -#define R2057_LOGEN_PUS 0x034 -#define R2057_LOGEN_PTAT_RESETS 0x035 -#define R2057_VCOBUF_IDACS 0x036 -#define R2057_VCOBUF_TUNE 0x037 -#define R2057_CMOSBUF_TX2GQ_IDACS 0x038 -#define R2057_CMOSBUF_TX2GI_IDACS 0x039 -#define R2057_CMOSBUF_TX5GQ_IDACS 0x03a -#define R2057_CMOSBUF_TX5GI_IDACS 0x03b -#define R2057_CMOSBUF_RX2GQ_IDACS 0x03c -#define R2057_CMOSBUF_RX2GI_IDACS 0x03d -#define R2057_CMOSBUF_RX5GQ_IDACS 0x03e -#define R2057_CMOSBUF_RX5GI_IDACS 0x03f -#define R2057_LOGEN_MX2G_IDACS 0x040 -#define R2057_LOGEN_MX2G_TUNE 0x041 -#define R2057_LOGEN_MX5G_IDACS 0x042 -#define R2057_LOGEN_MX5G_TUNE 0x043 -#define R2057_LOGEN_MX5G_RCCR 0x044 -#define R2057_LOGEN_INDBUF2G_IDAC 0x045 -#define R2057_LOGEN_INDBUF2G_IBOOST 0x046 -#define R2057_LOGEN_INDBUF2G_TUNE 0x047 -#define R2057_LOGEN_INDBUF5G_IDAC 0x048 -#define R2057_LOGEN_INDBUF5G_IBOOST 0x049 -#define R2057_LOGEN_INDBUF5G_TUNE 0x04a -#define R2057_CMOSBUF_TX_RCCR 0x04b -#define R2057_CMOSBUF_RX_RCCR 0x04c -#define R2057_LOGEN_SEL_PKDET 0x04d -#define R2057_CMOSBUF_SHAREIQ_PTAT 0x04e - -/* MISC core 0 */ -#define R2057_RXTXBIAS_CONFIG_CORE0 0x04f -#define R2057_TXGM_TXRF_PUS_CORE0 0x050 -#define R2057_TXGM_IDAC_BLEED_CORE0 0x051 -#define R2057_TXGM_GAIN_CORE0 0x056 -#define R2057_TXGM2G_PKDET_PUS_CORE0 0x057 -#define R2057_PAD2G_PTATS_CORE0 0x058 -#define R2057_PAD2G_IDACS_CORE0 0x059 -#define R2057_PAD2G_BOOST_PU_CORE0 0x05a -#define R2057_PAD2G_CASCV_GAIN_CORE0 0x05b -#define R2057_TXMIX2G_TUNE_BOOST_PU_CORE0 0x05c -#define R2057_TXMIX2G_LODC_CORE0 0x05d -#define R2057_PAD2G_TUNE_PUS_CORE0 0x05e -#define R2057_IPA2G_GAIN_CORE0 0x05f -#define R2057_TSSI2G_SPARE1_CORE0 0x060 -#define R2057_TSSI2G_SPARE2_CORE0 0x061 -#define R2057_IPA2G_TUNEV_CASCV_PTAT_CORE0 0x062 -#define R2057_IPA2G_IMAIN_CORE0 0x063 -#define R2057_IPA2G_CASCONV_CORE0 0x064 -#define R2057_IPA2G_CASCOFFV_CORE0 0x065 -#define R2057_IPA2G_BIAS_FILTER_CORE0 0x066 -#define R2057_TX5G_PKDET_CORE0 0x069 -#define R2057_PGA_PTAT_TXGM5G_PU_CORE0 0x06a -#define R2057_PAD5G_PTATS1_CORE0 0x06b -#define R2057_PAD5G_CLASS_PTATS2_CORE0 0x06c -#define R2057_PGA_BOOSTPTAT_IMAIN_CORE0 0x06d -#define R2057_PAD5G_CASCV_IMAIN_CORE0 0x06e -#define R2057_TXMIX5G_IBOOST_PAD_IAUX_CORE0 0x06f -#define R2057_PGA_BOOST_TUNE_CORE0 0x070 -#define R2057_PGA_GAIN_CORE0 0x071 -#define R2057_PAD5G_CASCOFFV_GAIN_PUS_CORE0 0x072 -#define R2057_TXMIX5G_BOOST_TUNE_CORE0 0x073 -#define R2057_PAD5G_TUNE_MISC_PUS_CORE0 0x074 -#define R2057_IPA5G_IAUX_CORE0 0x075 -#define R2057_IPA5G_GAIN_CORE0 0x076 -#define R2057_TSSI5G_SPARE1_CORE0 0x077 -#define R2057_TSSI5G_SPARE2_CORE0 0x078 -#define R2057_IPA5G_CASCOFFV_PU_CORE0 0x079 -#define R2057_IPA5G_PTAT_CORE0 0x07a -#define R2057_IPA5G_IMAIN_CORE0 0x07b -#define R2057_IPA5G_CASCONV_CORE0 0x07c -#define R2057_IPA5G_BIAS_FILTER_CORE0 0x07d -#define R2057_PAD_BIAS_FILTER_BWS_CORE0 0x080 -#define R2057_TR2G_CONFIG1_CORE0_NU 0x081 -#define R2057_TR2G_CONFIG2_CORE0_NU 0x082 -#define R2057_LNA5G_RFEN_CORE0 0x083 -#define R2057_TR5G_CONFIG2_CORE0_NU 0x084 -#define R2057_RXRFBIAS_IBOOST_PU_CORE0 0x085 -#define R2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE0 0x086 -#define R2057_RXGM_CMFBITAIL_AUXPTAT_CORE0 0x087 -#define R2057_RXMIX_ICORE_RXGM_IAUX_CORE0 0x088 -#define R2057_RXMIX_CMFBITAIL_PU_CORE0 0x089 -#define R2057_LNA2_IMAIN_PTAT_PU_CORE0 0x08a -#define R2057_LNA2_IAUX_PTAT_CORE0 0x08b -#define R2057_LNA1_IMAIN_PTAT_PU_CORE0 0x08c -#define R2057_LNA15G_INPUT_MATCH_TUNE_CORE0 0x08d -#define R2057_RXRFBIAS_BANDSEL_CORE0 0x08e -#define R2057_TIA_CONFIG_CORE0 0x08f -#define R2057_TIA_IQGAIN_CORE0 0x090 -#define R2057_TIA_IBIAS2_CORE0 0x091 -#define R2057_TIA_IBIAS1_CORE0 0x092 -#define R2057_TIA_SPARE_Q_CORE0 0x093 -#define R2057_TIA_SPARE_I_CORE0 0x094 -#define R2057_RXMIX2G_PUS_CORE0 0x095 -#define R2057_RXMIX2G_VCMREFS_CORE0 0x096 -#define R2057_RXMIX2G_LODC_QI_CORE0 0x097 -#define R2057_W12G_BW_LNA2G_PUS_CORE0 0x098 -#define R2057_LNA2G_GAIN_CORE0 0x099 -#define R2057_LNA2G_TUNE_CORE0 0x09a -#define R2057_RXMIX5G_PUS_CORE0 0x09b -#define R2057_RXMIX5G_VCMREFS_CORE0 0x09c -#define R2057_RXMIX5G_LODC_QI_CORE0 0x09d -#define R2057_W15G_BW_LNA5G_PUS_CORE0 0x09e -#define R2057_LNA5G_GAIN_CORE0 0x09f -#define R2057_LNA5G_TUNE_CORE0 0x0a0 -#define R2057_LPFSEL_TXRX_RXBB_PUS_CORE0 0x0a1 -#define R2057_RXBB_BIAS_MASTER_CORE0 0x0a2 -#define R2057_RXBB_VGABUF_IDACS_CORE0 0x0a3 -#define R2057_LPF_VCMREF_TXBUF_VCMREF_CORE0 0x0a4 -#define R2057_TXBUF_VINCM_CORE0 0x0a5 -#define R2057_TXBUF_IDACS_CORE0 0x0a6 -#define R2057_LPF_RESP_RXBUF_BW_CORE0 0x0a7 -#define R2057_RXBB_CC_CORE0 0x0a8 -#define R2057_RXBB_SPARE3_CORE0 0x0a9 -#define R2057_RXBB_RCCAL_HPC_CORE0 0x0aa -#define R2057_LPF_IDACS_CORE0 0x0ab -#define R2057_LPFBYP_DCLOOP_BYP_IDAC_CORE0 0x0ac -#define R2057_TXBUF_GAIN_CORE0 0x0ad -#define R2057_AFELOOPBACK_AACI_RESP_CORE0 0x0ae -#define R2057_RXBUF_DEGEN_CORE0 0x0af -#define R2057_RXBB_SPARE2_CORE0 0x0b0 -#define R2057_RXBB_SPARE1_CORE0 0x0b1 -#define R2057_RSSI_MASTER_CORE0 0x0b2 -#define R2057_W2_MASTER_CORE0 0x0b3 -#define R2057_NB_MASTER_CORE0 0x0b4 -#define R2057_W2_IDACS0_Q_CORE0 0x0b5 -#define R2057_W2_IDACS1_Q_CORE0 0x0b6 -#define R2057_W2_IDACS0_I_CORE0 0x0b7 -#define R2057_W2_IDACS1_I_CORE0 0x0b8 -#define R2057_RSSI_GPAIOSEL_W1_IDACS_CORE0 0x0b9 -#define R2057_NB_IDACS_Q_CORE0 0x0ba -#define R2057_NB_IDACS_I_CORE0 0x0bb -#define R2057_BACKUP4_CORE0 0x0c1 -#define R2057_BACKUP3_CORE0 0x0c2 -#define R2057_BACKUP2_CORE0 0x0c3 -#define R2057_BACKUP1_CORE0 0x0c4 -#define R2057_SPARE16_CORE0 0x0c5 -#define R2057_SPARE15_CORE0 0x0c6 -#define R2057_SPARE14_CORE0 0x0c7 -#define R2057_SPARE13_CORE0 0x0c8 -#define R2057_SPARE12_CORE0 0x0c9 -#define R2057_SPARE11_CORE0 0x0ca -#define R2057_TX2G_BIAS_RESETS_CORE0 0x0cb -#define R2057_TX5G_BIAS_RESETS_CORE0 0x0cc -#define R2057_IQTEST_SEL_PU 0x0cd -#define R2057_XTAL_CONFIG2 0x0ce -#define R2057_BUFS_MISC_LPFBW_CORE0 0x0cf -#define R2057_TXLPF_RCCAL_CORE0 0x0d0 -#define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE0 0x0d1 -#define R2057_LPF_GAIN_CORE0 0x0d2 -#define R2057_DACBUF_IDACS_BW_CORE0 0x0d3 - -/* MISC core 1 */ -#define R2057_RXTXBIAS_CONFIG_CORE1 0x0d4 -#define R2057_TXGM_TXRF_PUS_CORE1 0x0d5 -#define R2057_TXGM_IDAC_BLEED_CORE1 0x0d6 -#define R2057_TXGM_GAIN_CORE1 0x0db -#define R2057_TXGM2G_PKDET_PUS_CORE1 0x0dc -#define R2057_PAD2G_PTATS_CORE1 0x0dd -#define R2057_PAD2G_IDACS_CORE1 0x0de -#define R2057_PAD2G_BOOST_PU_CORE1 0x0df -#define R2057_PAD2G_CASCV_GAIN_CORE1 0x0e0 -#define R2057_TXMIX2G_TUNE_BOOST_PU_CORE1 0x0e1 -#define R2057_TXMIX2G_LODC_CORE1 0x0e2 -#define R2057_PAD2G_TUNE_PUS_CORE1 0x0e3 -#define R2057_IPA2G_GAIN_CORE1 0x0e4 -#define R2057_TSSI2G_SPARE1_CORE1 0x0e5 -#define R2057_TSSI2G_SPARE2_CORE1 0x0e6 -#define R2057_IPA2G_TUNEV_CASCV_PTAT_CORE1 0x0e7 -#define R2057_IPA2G_IMAIN_CORE1 0x0e8 -#define R2057_IPA2G_CASCONV_CORE1 0x0e9 -#define R2057_IPA2G_CASCOFFV_CORE1 0x0ea -#define R2057_IPA2G_BIAS_FILTER_CORE1 0x0eb -#define R2057_TX5G_PKDET_CORE1 0x0ee -#define R2057_PGA_PTAT_TXGM5G_PU_CORE1 0x0ef -#define R2057_PAD5G_PTATS1_CORE1 0x0f0 -#define R2057_PAD5G_CLASS_PTATS2_CORE1 0x0f1 -#define R2057_PGA_BOOSTPTAT_IMAIN_CORE1 0x0f2 -#define R2057_PAD5G_CASCV_IMAIN_CORE1 0x0f3 -#define R2057_TXMIX5G_IBOOST_PAD_IAUX_CORE1 0x0f4 -#define R2057_PGA_BOOST_TUNE_CORE1 0x0f5 -#define R2057_PGA_GAIN_CORE1 0x0f6 -#define R2057_PAD5G_CASCOFFV_GAIN_PUS_CORE1 0x0f7 -#define R2057_TXMIX5G_BOOST_TUNE_CORE1 0x0f8 -#define R2057_PAD5G_TUNE_MISC_PUS_CORE1 0x0f9 -#define R2057_IPA5G_IAUX_CORE1 0x0fa -#define R2057_IPA5G_GAIN_CORE1 0x0fb -#define R2057_TSSI5G_SPARE1_CORE1 0x0fc -#define R2057_TSSI5G_SPARE2_CORE1 0x0fd -#define R2057_IPA5G_CASCOFFV_PU_CORE1 0x0fe -#define R2057_IPA5G_PTAT_CORE1 0x0ff -#define R2057_IPA5G_IMAIN_CORE1 0x100 -#define R2057_IPA5G_CASCONV_CORE1 0x101 -#define R2057_IPA5G_BIAS_FILTER_CORE1 0x102 -#define R2057_PAD_BIAS_FILTER_BWS_CORE1 0x105 -#define R2057_TR2G_CONFIG1_CORE1_NU 0x106 -#define R2057_TR2G_CONFIG2_CORE1_NU 0x107 -#define R2057_LNA5G_RFEN_CORE1 0x108 -#define R2057_TR5G_CONFIG2_CORE1_NU 0x109 -#define R2057_RXRFBIAS_IBOOST_PU_CORE1 0x10a -#define R2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE1 0x10b -#define R2057_RXGM_CMFBITAIL_AUXPTAT_CORE1 0x10c -#define R2057_RXMIX_ICORE_RXGM_IAUX_CORE1 0x10d -#define R2057_RXMIX_CMFBITAIL_PU_CORE1 0x10e -#define R2057_LNA2_IMAIN_PTAT_PU_CORE1 0x10f -#define R2057_LNA2_IAUX_PTAT_CORE1 0x110 -#define R2057_LNA1_IMAIN_PTAT_PU_CORE1 0x111 -#define R2057_LNA15G_INPUT_MATCH_TUNE_CORE1 0x112 -#define R2057_RXRFBIAS_BANDSEL_CORE1 0x113 -#define R2057_TIA_CONFIG_CORE1 0x114 -#define R2057_TIA_IQGAIN_CORE1 0x115 -#define R2057_TIA_IBIAS2_CORE1 0x116 -#define R2057_TIA_IBIAS1_CORE1 0x117 -#define R2057_TIA_SPARE_Q_CORE1 0x118 -#define R2057_TIA_SPARE_I_CORE1 0x119 -#define R2057_RXMIX2G_PUS_CORE1 0x11a -#define R2057_RXMIX2G_VCMREFS_CORE1 0x11b -#define R2057_RXMIX2G_LODC_QI_CORE1 0x11c -#define R2057_W12G_BW_LNA2G_PUS_CORE1 0x11d -#define R2057_LNA2G_GAIN_CORE1 0x11e -#define R2057_LNA2G_TUNE_CORE1 0x11f -#define R2057_RXMIX5G_PUS_CORE1 0x120 -#define R2057_RXMIX5G_VCMREFS_CORE1 0x121 -#define R2057_RXMIX5G_LODC_QI_CORE1 0x122 -#define R2057_W15G_BW_LNA5G_PUS_CORE1 0x123 -#define R2057_LNA5G_GAIN_CORE1 0x124 -#define R2057_LNA5G_TUNE_CORE1 0x125 -#define R2057_LPFSEL_TXRX_RXBB_PUS_CORE1 0x126 -#define R2057_RXBB_BIAS_MASTER_CORE1 0x127 -#define R2057_RXBB_VGABUF_IDACS_CORE1 0x128 -#define R2057_LPF_VCMREF_TXBUF_VCMREF_CORE1 0x129 -#define R2057_TXBUF_VINCM_CORE1 0x12a -#define R2057_TXBUF_IDACS_CORE1 0x12b -#define R2057_LPF_RESP_RXBUF_BW_CORE1 0x12c -#define R2057_RXBB_CC_CORE1 0x12d -#define R2057_RXBB_SPARE3_CORE1 0x12e -#define R2057_RXBB_RCCAL_HPC_CORE1 0x12f -#define R2057_LPF_IDACS_CORE1 0x130 -#define R2057_LPFBYP_DCLOOP_BYP_IDAC_CORE1 0x131 -#define R2057_TXBUF_GAIN_CORE1 0x132 -#define R2057_AFELOOPBACK_AACI_RESP_CORE1 0x133 -#define R2057_RXBUF_DEGEN_CORE1 0x134 -#define R2057_RXBB_SPARE2_CORE1 0x135 -#define R2057_RXBB_SPARE1_CORE1 0x136 -#define R2057_RSSI_MASTER_CORE1 0x137 -#define R2057_W2_MASTER_CORE1 0x138 -#define R2057_NB_MASTER_CORE1 0x139 -#define R2057_W2_IDACS0_Q_CORE1 0x13a -#define R2057_W2_IDACS1_Q_CORE1 0x13b -#define R2057_W2_IDACS0_I_CORE1 0x13c -#define R2057_W2_IDACS1_I_CORE1 0x13d -#define R2057_RSSI_GPAIOSEL_W1_IDACS_CORE1 0x13e -#define R2057_NB_IDACS_Q_CORE1 0x13f -#define R2057_NB_IDACS_I_CORE1 0x140 -#define R2057_BACKUP4_CORE1 0x146 -#define R2057_BACKUP3_CORE1 0x147 -#define R2057_BACKUP2_CORE1 0x148 -#define R2057_BACKUP1_CORE1 0x149 -#define R2057_SPARE16_CORE1 0x14a -#define R2057_SPARE15_CORE1 0x14b -#define R2057_SPARE14_CORE1 0x14c -#define R2057_SPARE13_CORE1 0x14d -#define R2057_SPARE12_CORE1 0x14e -#define R2057_SPARE11_CORE1 0x14f -#define R2057_TX2G_BIAS_RESETS_CORE1 0x150 -#define R2057_TX5G_BIAS_RESETS_CORE1 0x151 -#define R2057_SPARE8_CORE1 0x152 -#define R2057_SPARE7_CORE1 0x153 -#define R2057_BUFS_MISC_LPFBW_CORE1 0x154 -#define R2057_TXLPF_RCCAL_CORE1 0x155 -#define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE1 0x156 -#define R2057_LPF_GAIN_CORE1 0x157 -#define R2057_DACBUF_IDACS_BW_CORE1 0x158 - -#define R2057_DACBUF_VINCM_CORE1 0x159 -#define R2057_RCCAL_START_R1_Q1_P1 0x15a -#define R2057_RCCAL_X1 0x15b -#define R2057_RCCAL_TRC0 0x15c -#define R2057_RCCAL_TRC1 0x15d -#define R2057_RCCAL_DONE_OSCCAP 0x15e -#define R2057_RCCAL_N0_0 0x15f -#define R2057_RCCAL_N0_1 0x160 -#define R2057_RCCAL_N1_0 0x161 -#define R2057_RCCAL_N1_1 0x162 -#define R2057_RCAL_STATUS 0x163 -#define R2057_XTALPUOVR_PINCTRL 0x164 -#define R2057_OVR_REG0 0x165 -#define R2057_OVR_REG1 0x166 -#define R2057_OVR_REG2 0x167 -#define R2057_OVR_REG3 0x168 -#define R2057_OVR_REG4 0x169 -#define R2057_RCCAL_SCAP_VAL 0x16a -#define R2057_RCCAL_BCAP_VAL 0x16b -#define R2057_RCCAL_HPC_VAL 0x16c -#define R2057_RCCAL_OVERRIDES 0x16d - -/* TX core 0 */ -#define R2057_TX0_IQCAL_GAIN_BW 0x170 -#define R2057_TX0_LOFT_FINE_I 0x171 -#define R2057_TX0_LOFT_FINE_Q 0x172 -#define R2057_TX0_LOFT_COARSE_I 0x173 -#define R2057_TX0_LOFT_COARSE_Q 0x174 -#define R2057_TX0_TX_SSI_MASTER 0x175 -#define R2057_TX0_IQCAL_VCM_HG 0x176 -#define R2057_TX0_IQCAL_IDAC 0x177 -#define R2057_TX0_TSSI_VCM 0x178 -#define R2057_TX0_TX_SSI_MUX 0x179 -#define R2057_TX0_TSSIA 0x17a -#define R2057_TX0_TSSIG 0x17b -#define R2057_TX0_TSSI_MISC1 0x17c -#define R2057_TX0_TXRXCOUPLE_2G_ATTEN 0x17d -#define R2057_TX0_TXRXCOUPLE_2G_PWRUP 0x17e -#define R2057_TX0_TXRXCOUPLE_5G_ATTEN 0x17f -#define R2057_TX0_TXRXCOUPLE_5G_PWRUP 0x180 - -/* TX core 1 */ -#define R2057_TX1_IQCAL_GAIN_BW 0x190 -#define R2057_TX1_LOFT_FINE_I 0x191 -#define R2057_TX1_LOFT_FINE_Q 0x192 -#define R2057_TX1_LOFT_COARSE_I 0x193 -#define R2057_TX1_LOFT_COARSE_Q 0x194 -#define R2057_TX1_TX_SSI_MASTER 0x195 -#define R2057_TX1_IQCAL_VCM_HG 0x196 -#define R2057_TX1_IQCAL_IDAC 0x197 -#define R2057_TX1_TSSI_VCM 0x198 -#define R2057_TX1_TX_SSI_MUX 0x199 -#define R2057_TX1_TSSIA 0x19a -#define R2057_TX1_TSSIG 0x19b -#define R2057_TX1_TSSI_MISC1 0x19c -#define R2057_TX1_TXRXCOUPLE_2G_ATTEN 0x19d -#define R2057_TX1_TXRXCOUPLE_2G_PWRUP 0x19e -#define R2057_TX1_TXRXCOUPLE_5G_ATTEN 0x19f -#define R2057_TX1_TXRXCOUPLE_5G_PWRUP 0x1a0 - -#define R2057_AFE_VCM_CAL_MASTER_CORE0 0x1a1 -#define R2057_AFE_SET_VCM_I_CORE0 0x1a2 -#define R2057_AFE_SET_VCM_Q_CORE0 0x1a3 -#define R2057_AFE_STATUS_VCM_IQADC_CORE0 0x1a4 -#define R2057_AFE_STATUS_VCM_I_CORE0 0x1a5 -#define R2057_AFE_STATUS_VCM_Q_CORE0 0x1a6 -#define R2057_AFE_VCM_CAL_MASTER_CORE1 0x1a7 -#define R2057_AFE_SET_VCM_I_CORE1 0x1a8 -#define R2057_AFE_SET_VCM_Q_CORE1 0x1a9 -#define R2057_AFE_STATUS_VCM_IQADC_CORE1 0x1aa -#define R2057_AFE_STATUS_VCM_I_CORE1 0x1ab -#define R2057_AFE_STATUS_VCM_Q_CORE1 0x1ac - -#define R2057v7_DACBUF_VINCM_CORE0 0x1ad -#define R2057v7_RCCAL_MASTER 0x1ae -#define R2057v7_TR2G_CONFIG3_CORE0_NU 0x1af -#define R2057v7_TR2G_CONFIG3_CORE1_NU 0x1b0 -#define R2057v7_LOGEN_PUS1 0x1b1 -#define R2057v7_OVR_REG5 0x1b2 -#define R2057v7_OVR_REG6 0x1b3 -#define R2057v7_OVR_REG7 0x1b4 -#define R2057v7_OVR_REG8 0x1b5 -#define R2057v7_OVR_REG9 0x1b6 -#define R2057v7_OVR_REG10 0x1b7 -#define R2057v7_OVR_REG11 0x1b8 -#define R2057v7_OVR_REG12 0x1b9 -#define R2057v7_OVR_REG13 0x1ba -#define R2057v7_OVR_REG14 0x1bb -#define R2057v7_OVR_REG15 0x1bc -#define R2057v7_OVR_REG16 0x1bd -#define R2057v7_OVR_REG1 0x1be -#define R2057v7_OVR_REG18 0x1bf -#define R2057v7_OVR_REG19 0x1c0 -#define R2057v7_OVR_REG20 0x1c1 -#define R2057v7_OVR_REG21 0x1c2 -#define R2057v7_OVR_REG2 0x1c3 -#define R2057v7_OVR_REG23 0x1c4 -#define R2057v7_OVR_REG24 0x1c5 -#define R2057v7_OVR_REG25 0x1c6 -#define R2057v7_OVR_REG26 0x1c7 -#define R2057v7_OVR_REG27 0x1c8 -#define R2057v7_OVR_REG28 0x1c9 -#define R2057v7_IQTEST_SEL_PU2 0x1ca - -#define R2057_VCM_MASK 0x7 - -struct b43_nphy_chantabent_rev7 { - /* The channel frequency in MHz */ - u16 freq; - /* Radio regs values on channelswitch */ - u8 radio_vcocal_countval0; - u8 radio_vcocal_countval1; - u8 radio_rfpll_refmaster_sparextalsize; - u8 radio_rfpll_loopfilter_r1; - u8 radio_rfpll_loopfilter_c2; - u8 radio_rfpll_loopfilter_c1; - u8 radio_cp_kpd_idac; - u8 radio_rfpll_mmd0; - u8 radio_rfpll_mmd1; - u8 radio_vcobuf_tune; - u8 radio_logen_mx2g_tune; - u8 radio_logen_mx5g_tune; - u8 radio_logen_indbuf2g_tune; - u8 radio_logen_indbuf5g_tune; - u8 radio_txmix2g_tune_boost_pu_core0; - u8 radio_pad2g_tune_pus_core0; - u8 radio_pga_boost_tune_core0; - u8 radio_txmix5g_boost_tune_core0; - u8 radio_pad5g_tune_misc_pus_core0; - u8 radio_lna2g_tune_core0; - u8 radio_lna5g_tune_core0; - u8 radio_txmix2g_tune_boost_pu_core1; - u8 radio_pad2g_tune_pus_core1; - u8 radio_pga_boost_tune_core1; - u8 radio_txmix5g_boost_tune_core1; - u8 radio_pad5g_tune_misc_pus_core1; - u8 radio_lna2g_tune_core1; - u8 radio_lna5g_tune_core1; - /* PHY res values on channelswitch */ - struct b43_phy_n_sfo_cfg phy_regs; -}; - -struct b43_nphy_chantabent_rev7_2g { - /* The channel frequency in MHz */ - u16 freq; - /* Radio regs values on channelswitch */ - u8 radio_vcocal_countval0; - u8 radio_vcocal_countval1; - u8 radio_rfpll_refmaster_sparextalsize; - u8 radio_rfpll_loopfilter_r1; - u8 radio_rfpll_loopfilter_c2; - u8 radio_rfpll_loopfilter_c1; - u8 radio_cp_kpd_idac; - u8 radio_rfpll_mmd0; - u8 radio_rfpll_mmd1; - u8 radio_vcobuf_tune; - u8 radio_logen_mx2g_tune; - u8 radio_logen_indbuf2g_tune; - u8 radio_txmix2g_tune_boost_pu_core0; - u8 radio_pad2g_tune_pus_core0; - u8 radio_lna2g_tune_core0; - u8 radio_txmix2g_tune_boost_pu_core1; - u8 radio_pad2g_tune_pus_core1; - u8 radio_lna2g_tune_core1; - /* PHY regs values on channelswitch */ - struct b43_phy_n_sfo_cfg phy_regs; -}; - -void r2057_upload_inittabs(struct b43_wldev *dev); - -void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, - const struct b43_nphy_chantabent_rev7 **tabent_r7, - const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g); - -#endif /* B43_RADIO_2057_H_ */ diff --git a/drivers/net/wireless/b43/radio_2059.c b/drivers/net/wireless/b43/radio_2059.c deleted file mode 100644 index a3cf9efd7e21..000000000000 --- a/drivers/net/wireless/b43/radio_2059.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11n 2059 radio device data tables - - Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "radio_2059.h" - -/* Extracted from MMIO dump of 6.30.223.141 */ -static u16 r2059_phy_rev1_init[][2] = { - { 0x051, 0x70 }, { 0x05a, 0x03 }, { 0x079, 0x01 }, { 0x082, 0x70 }, - { 0x083, 0x00 }, { 0x084, 0x70 }, { 0x09a, 0x7f }, { 0x0b6, 0x10 }, - { 0x188, 0x05 }, -}; - -#define RADIOREGS(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ - r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ - r20) \ - .radio_syn16 = r00, \ - .radio_syn17 = r01, \ - .radio_syn22 = r02, \ - .radio_syn25 = r03, \ - .radio_syn27 = r04, \ - .radio_syn28 = r05, \ - .radio_syn29 = r06, \ - .radio_syn2c = r07, \ - .radio_syn2d = r08, \ - .radio_syn37 = r09, \ - .radio_syn41 = r10, \ - .radio_syn43 = r11, \ - .radio_syn47 = r12, \ - .radio_rxtx4a = r13, \ - .radio_rxtx58 = r14, \ - .radio_rxtx5a = r15, \ - .radio_rxtx6a = r16, \ - .radio_rxtx6d = r17, \ - .radio_rxtx6e = r18, \ - .radio_rxtx92 = r19, \ - .radio_rxtx98 = r20 - -#define PHYREGS(r0, r1, r2, r3, r4, r5) \ - .phy_regs.bw1 = r0, \ - .phy_regs.bw2 = r1, \ - .phy_regs.bw3 = r2, \ - .phy_regs.bw4 = r3, \ - .phy_regs.bw5 = r4, \ - .phy_regs.bw6 = r5 - -/* Extracted from MMIO dump of 6.30.223.141 - * TODO: Values for channels 12 & 13 are outdated (from some old 5.x driver)! - */ -static const struct b43_phy_ht_channeltab_e_radio2059 b43_phy_ht_channeltab_radio2059[] = { - { - .freq = 2412, - RADIOREGS(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, - 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, - 0x00, 0x00, 0x00, 0xd0, 0x00), - PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), - }, - { - .freq = 2417, - RADIOREGS(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, - 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, - 0x00, 0x00, 0x00, 0xd0, 0x00), - PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), - }, - { - .freq = 2422, - RADIOREGS(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, - 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, - 0x00, 0x00, 0x00, 0xd0, 0x00), - PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), - }, - { - .freq = 2427, - RADIOREGS(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, - 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, - 0x00, 0x00, 0x00, 0xa0, 0x00), - PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), - }, - { - .freq = 2432, - RADIOREGS(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, - 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, - 0x00, 0x00, 0x00, 0xa0, 0x00), - PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), - }, - { - .freq = 2437, - RADIOREGS(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, - 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, - 0x00, 0x00, 0x00, 0xa0, 0x00), - PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), - }, - { - .freq = 2442, - RADIOREGS(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, - 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, - 0x00, 0x00, 0x00, 0x80, 0x00), - PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), - }, - { - .freq = 2447, - RADIOREGS(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, - 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, - 0x00, 0x00, 0x00, 0x80, 0x00), - PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), - }, - { - .freq = 2452, - RADIOREGS(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, - 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, - 0x00, 0x00, 0x00, 0x80, 0x00), - PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), - }, - { - .freq = 2457, - RADIOREGS(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, - 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, - 0x00, 0x00, 0x00, 0x60, 0x00), - PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), - }, - { - .freq = 2462, - RADIOREGS(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, - 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, - 0x00, 0x00, 0x00, 0x60, 0x00), - PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), - }, - { .freq = 2467, - RADIOREGS(0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, - 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x03, - 0x00, 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), - }, - { .freq = 2472, - RADIOREGS(0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, - 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x03, - 0x00, 0x00, 0x00, 0xf0, 0x00), - PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), - }, - { - .freq = 5180, - RADIOREGS(0xbe, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x06, - 0x02, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, - 0x0f, 0x4f, 0xa3, 0x00, 0xfc), - PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), - }, - { - .freq = 5200, - RADIOREGS(0xc5, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x08, - 0x02, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, - 0x0f, 0x4f, 0x93, 0x00, 0xfb), - PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), - }, - { - .freq = 5220, - RADIOREGS(0xcc, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0a, - 0x02, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, - 0x0f, 0x4f, 0x93, 0x00, 0xea), - PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), - }, - { - .freq = 5240, - RADIOREGS(0xd2, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0c, - 0x02, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, - 0x0f, 0x4f, 0x93, 0x00, 0xda), - PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), - }, - { - .freq = 5260, - RADIOREGS(0xd9, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0e, - 0x02, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x00, - 0x0f, 0x4f, 0x93, 0x00, 0xca), - PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), - }, - { - .freq = 5280, - RADIOREGS(0xe0, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x10, - 0x02, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x00, - 0x0f, 0x4f, 0x93, 0x00, 0xb9), - PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), - }, - { - .freq = 5300, - RADIOREGS(0xe6, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x12, - 0x02, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x00, - 0x0f, 0x4c, 0x83, 0x00, 0xb8), - PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), - }, - { - .freq = 5320, - RADIOREGS(0xed, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x14, - 0x02, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x00, - 0x0f, 0x4c, 0x83, 0x00, 0xa8), - PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), - }, - { - .freq = 5500, - RADIOREGS(0x29, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x26, - 0x02, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, - 0x0a, 0x46, 0x43, 0x00, 0x75), - PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), - }, - { - .freq = 5520, - RADIOREGS(0x30, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x28, - 0x02, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, - 0x0a, 0x46, 0x43, 0x00, 0x75), - PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), - }, - { - .freq = 5540, - RADIOREGS(0x36, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x2a, - 0x02, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, - 0x0a, 0x46, 0x43, 0x00, 0x75), - PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), - }, - { - .freq = 5560, - RADIOREGS(0x3d, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x2c, - 0x02, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, - 0x0a, 0x46, 0x43, 0x00, 0x75), - PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), - }, - { - .freq = 5580, - RADIOREGS(0x44, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x2e, - 0x02, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, - 0x0a, 0x46, 0x43, 0x00, 0x74), - PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), - }, - { - .freq = 5600, - RADIOREGS(0x4a, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x30, - 0x02, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, - 0x09, 0x44, 0x23, 0x00, 0x54), - PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), - }, - { - .freq = 5620, - RADIOREGS(0x51, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x32, - 0x02, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, - 0x09, 0x44, 0x23, 0x00, 0x54), - PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), - }, - { - .freq = 5640, - RADIOREGS(0x58, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x34, - 0x02, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, - 0x09, 0x44, 0x23, 0x00, 0x43), - PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), - }, - { - .freq = 5660, - RADIOREGS(0x5e, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x36, - 0x02, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, - 0x09, 0x43, 0x23, 0x00, 0x43), - PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), - }, - { - .freq = 5680, - RADIOREGS(0x65, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x38, - 0x02, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, - 0x09, 0x42, 0x23, 0x00, 0x43), - PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), - }, - { - .freq = 5700, - RADIOREGS(0x6c, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x3a, - 0x02, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, - 0x08, 0x42, 0x13, 0x00, 0x32), - PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), - }, - { - .freq = 5745, - RADIOREGS(0x7b, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x7d, - 0x04, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, - 0x08, 0x42, 0x13, 0x00, 0x21), - PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), - }, - { - .freq = 5765, - RADIOREGS(0x81, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x81, - 0x04, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, - 0x08, 0x42, 0x13, 0x00, 0x11), - PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), - }, - { - .freq = 5785, - RADIOREGS(0x88, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x85, - 0x04, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, - 0x08, 0x42, 0x13, 0x00, 0x00), - PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), - }, - { - .freq = 5805, - RADIOREGS(0x8f, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x89, - 0x04, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, - 0x06, 0x41, 0x03, 0x00, 0x00), - PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), - }, - { - .freq = 5825, - RADIOREGS(0x95, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x8d, - 0x04, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, - 0x06, 0x41, 0x03, 0x00, 0x00), - PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), - }, -}; - -void r2059_upload_inittabs(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - u16 *table = NULL; - u16 size, i; - - switch (phy->rev) { - case 1: - table = r2059_phy_rev1_init[0]; - size = ARRAY_SIZE(r2059_phy_rev1_init); - break; - default: - B43_WARN_ON(1); - return; - } - - for (i = 0; i < size; i++, table += 2) - b43_radio_write(dev, R2059_ALL | table[0], table[1]); -} - -const struct b43_phy_ht_channeltab_e_radio2059 -*b43_phy_ht_get_channeltab_e_r2059(struct b43_wldev *dev, u16 freq) -{ - const struct b43_phy_ht_channeltab_e_radio2059 *e; - unsigned int i; - - e = b43_phy_ht_channeltab_radio2059; - for (i = 0; i < ARRAY_SIZE(b43_phy_ht_channeltab_radio2059); i++, e++) { - if (e->freq == freq) - return e; - } - - return NULL; -} diff --git a/drivers/net/wireless/b43/radio_2059.h b/drivers/net/wireless/b43/radio_2059.h deleted file mode 100644 index 9e22fb60588b..000000000000 --- a/drivers/net/wireless/b43/radio_2059.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef B43_RADIO_2059_H_ -#define B43_RADIO_2059_H_ - -#include - -#include "phy_ht.h" - -#define R2059_C1 0x000 -#define R2059_C2 0x400 -#define R2059_C3 0x800 -#define R2059_ALL 0xC00 - -#define R2059_RCAL_CONFIG 0x004 -#define R2059_RFPLL_MASTER 0x011 -#define R2059_RFPLL_MISC_EN 0x02b -#define R2059_RFPLL_MISC_CAL_RESETN 0x02e -#define R2059_XTAL_CONFIG2 0x0c0 -#define R2059_RCCAL_START_R1_Q1_P1 0x13c -#define R2059_RCCAL_X1 0x13d -#define R2059_RCCAL_TRC0 0x13e -#define R2059_RCCAL_DONE_OSCCAP 0x140 -#define R2059_RCAL_STATUS 0x145 -#define R2059_RCCAL_MASTER 0x17f - -/* Values for various registers uploaded on channel switching */ -struct b43_phy_ht_channeltab_e_radio2059 { - /* The channel frequency in MHz */ - u16 freq; - /* Values for radio registers */ - u8 radio_syn16; - u8 radio_syn17; - u8 radio_syn22; - u8 radio_syn25; - u8 radio_syn27; - u8 radio_syn28; - u8 radio_syn29; - u8 radio_syn2c; - u8 radio_syn2d; - u8 radio_syn37; - u8 radio_syn41; - u8 radio_syn43; - u8 radio_syn47; - u8 radio_rxtx4a; - u8 radio_rxtx58; - u8 radio_rxtx5a; - u8 radio_rxtx6a; - u8 radio_rxtx6d; - u8 radio_rxtx6e; - u8 radio_rxtx92; - u8 radio_rxtx98; - /* Values for PHY registers */ - struct b43_phy_ht_channeltab_e_phy phy_regs; -}; - -void r2059_upload_inittabs(struct b43_wldev *dev); - -const struct b43_phy_ht_channeltab_e_radio2059 -*b43_phy_ht_get_channeltab_e_r2059(struct b43_wldev *dev, u16 freq); - -#endif /* B43_RADIO_2059_H_ */ diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/b43/rfkill.c deleted file mode 100644 index 70c2fcedd1bb..000000000000 --- a/drivers/net/wireless/b43/rfkill.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - - Broadcom B43 wireless driver - RFKILL support - - Copyright (c) 2007 Michael Buesch - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" - - -/* Returns TRUE, if the radio is enabled in hardware. */ -bool b43_is_hw_radio_enabled(struct b43_wldev *dev) -{ - return !(b43_read32(dev, B43_MMIO_RADIO_HWENABLED_HI) - & B43_MMIO_RADIO_HWENABLED_HI_MASK); -} - -/* The poll callback for the hardware button. */ -void b43_rfkill_poll(struct ieee80211_hw *hw) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev = wl->current_dev; - bool enabled; - bool brought_up = false; - - mutex_lock(&wl->mutex); - if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) { - if (b43_bus_powerup(dev, 0)) { - mutex_unlock(&wl->mutex); - return; - } - b43_device_enable(dev, 0); - brought_up = true; - } - - enabled = b43_is_hw_radio_enabled(dev); - - if (unlikely(enabled != dev->radio_hw_enable)) { - dev->radio_hw_enable = enabled; - b43info(wl, "Radio hardware status changed to %s\n", - enabled ? "ENABLED" : "DISABLED"); - wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); - if (enabled != dev->phy.radio_on) - b43_software_rfkill(dev, !enabled); - } - - if (brought_up) { - b43_device_disable(dev, 0); - b43_bus_may_powerdown(dev); - } - - mutex_unlock(&wl->mutex); -} diff --git a/drivers/net/wireless/b43/rfkill.h b/drivers/net/wireless/b43/rfkill.h deleted file mode 100644 index f046c3ca0519..000000000000 --- a/drivers/net/wireless/b43/rfkill.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef B43_RFKILL_H_ -#define B43_RFKILL_H_ - -struct ieee80211_hw; -struct b43_wldev; - -void b43_rfkill_poll(struct ieee80211_hw *hw); - -bool b43_is_hw_radio_enabled(struct b43_wldev *dev); - -#endif /* B43_RFKILL_H_ */ diff --git a/drivers/net/wireless/b43/sdio.c b/drivers/net/wireless/b43/sdio.c deleted file mode 100644 index 59a521800694..000000000000 --- a/drivers/net/wireless/b43/sdio.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Broadcom B43 wireless driver - * - * SDIO over Sonics Silicon Backplane bus glue for b43. - * - * Copyright (C) 2009 Albert Herranz - * Copyright (C) 2009 Michael Buesch - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -#include "sdio.h" -#include "b43.h" - - -#define HNBU_CHIPID 0x01 /* vendor & device id */ - -#define B43_SDIO_BLOCK_SIZE 64 /* rx fifo max size in bytes */ - - -static const struct b43_sdio_quirk { - u16 vendor; - u16 device; - unsigned int quirks; -} b43_sdio_quirks[] = { - { 0x14E4, 0x4318, SSB_QUIRK_SDIO_READ_AFTER_WRITE32, }, - { }, -}; - - -static unsigned int b43_sdio_get_quirks(u16 vendor, u16 device) -{ - const struct b43_sdio_quirk *q; - - for (q = b43_sdio_quirks; q->quirks; q++) { - if (vendor == q->vendor && device == q->device) - return q->quirks; - } - - return 0; -} - -static void b43_sdio_interrupt_dispatcher(struct sdio_func *func) -{ - struct b43_sdio *sdio = sdio_get_drvdata(func); - struct b43_wldev *dev = sdio->irq_handler_opaque; - - if (unlikely(b43_status(dev) < B43_STAT_STARTED)) - return; - - sdio_release_host(func); - sdio->irq_handler(dev); - sdio_claim_host(func); -} - -int b43_sdio_request_irq(struct b43_wldev *dev, - void (*handler)(struct b43_wldev *dev)) -{ - struct ssb_bus *bus = dev->dev->sdev->bus; - struct sdio_func *func = bus->host_sdio; - struct b43_sdio *sdio = sdio_get_drvdata(func); - int err; - - sdio->irq_handler_opaque = dev; - sdio->irq_handler = handler; - sdio_claim_host(func); - err = sdio_claim_irq(func, b43_sdio_interrupt_dispatcher); - sdio_release_host(func); - - return err; -} - -void b43_sdio_free_irq(struct b43_wldev *dev) -{ - struct ssb_bus *bus = dev->dev->sdev->bus; - struct sdio_func *func = bus->host_sdio; - struct b43_sdio *sdio = sdio_get_drvdata(func); - - sdio_claim_host(func); - sdio_release_irq(func); - sdio_release_host(func); - sdio->irq_handler_opaque = NULL; - sdio->irq_handler = NULL; -} - -static int b43_sdio_probe(struct sdio_func *func, - const struct sdio_device_id *id) -{ - struct b43_sdio *sdio; - struct sdio_func_tuple *tuple; - u16 vendor = 0, device = 0; - int error; - - /* Look for the card chip identifier. */ - tuple = func->tuples; - while (tuple) { - switch (tuple->code) { - case 0x80: - switch (tuple->data[0]) { - case HNBU_CHIPID: - if (tuple->size != 5) - break; - vendor = tuple->data[1] | (tuple->data[2]<<8); - device = tuple->data[3] | (tuple->data[4]<<8); - dev_info(&func->dev, "Chip ID %04x:%04x\n", - vendor, device); - break; - default: - break; - } - break; - default: - break; - } - tuple = tuple->next; - } - if (!vendor || !device) { - error = -ENODEV; - goto out; - } - - sdio_claim_host(func); - error = sdio_set_block_size(func, B43_SDIO_BLOCK_SIZE); - if (error) { - dev_err(&func->dev, "failed to set block size to %u bytes," - " error %d\n", B43_SDIO_BLOCK_SIZE, error); - goto err_release_host; - } - error = sdio_enable_func(func); - if (error) { - dev_err(&func->dev, "failed to enable func, error %d\n", error); - goto err_release_host; - } - sdio_release_host(func); - - sdio = kzalloc(sizeof(*sdio), GFP_KERNEL); - if (!sdio) { - error = -ENOMEM; - dev_err(&func->dev, "failed to allocate ssb bus\n"); - goto err_disable_func; - } - error = ssb_bus_sdiobus_register(&sdio->ssb, func, - b43_sdio_get_quirks(vendor, device)); - if (error) { - dev_err(&func->dev, "failed to register ssb sdio bus," - " error %d\n", error); - goto err_free_ssb; - } - sdio_set_drvdata(func, sdio); - - return 0; - -err_free_ssb: - kfree(sdio); -err_disable_func: - sdio_claim_host(func); - sdio_disable_func(func); -err_release_host: - sdio_release_host(func); -out: - return error; -} - -static void b43_sdio_remove(struct sdio_func *func) -{ - struct b43_sdio *sdio = sdio_get_drvdata(func); - - ssb_bus_unregister(&sdio->ssb); - sdio_claim_host(func); - sdio_disable_func(func); - sdio_release_host(func); - kfree(sdio); - sdio_set_drvdata(func, NULL); -} - -static const struct sdio_device_id b43_sdio_ids[] = { - { SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */ - { SDIO_DEVICE(0x0092, 0x0004) }, /* C-guys, Inc. EW-CG1102GC */ - { }, -}; - -static struct sdio_driver b43_sdio_driver = { - .name = "b43-sdio", - .id_table = b43_sdio_ids, - .probe = b43_sdio_probe, - .remove = b43_sdio_remove, -}; - -int b43_sdio_init(void) -{ - return sdio_register_driver(&b43_sdio_driver); -} - -void b43_sdio_exit(void) -{ - sdio_unregister_driver(&b43_sdio_driver); -} diff --git a/drivers/net/wireless/b43/sdio.h b/drivers/net/wireless/b43/sdio.h deleted file mode 100644 index 1e93926f388f..000000000000 --- a/drivers/net/wireless/b43/sdio.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef B43_SDIO_H_ -#define B43_SDIO_H_ - -#include - -struct b43_wldev; - - -#ifdef CONFIG_B43_SDIO - -struct b43_sdio { - struct ssb_bus ssb; - void *irq_handler_opaque; - void (*irq_handler)(struct b43_wldev *dev); -}; - -int b43_sdio_request_irq(struct b43_wldev *dev, - void (*handler)(struct b43_wldev *dev)); -void b43_sdio_free_irq(struct b43_wldev *dev); - -int b43_sdio_init(void); -void b43_sdio_exit(void); - - -#else /* CONFIG_B43_SDIO */ - - -static inline int b43_sdio_request_irq(struct b43_wldev *dev, - void (*handler)(struct b43_wldev *dev)) -{ - return -ENODEV; -} -static inline void b43_sdio_free_irq(struct b43_wldev *dev) -{ -} -static inline int b43_sdio_init(void) -{ - return 0; -} -static inline void b43_sdio_exit(void) -{ -} - -#endif /* CONFIG_B43_SDIO */ -#endif /* B43_SDIO_H_ */ diff --git a/drivers/net/wireless/b43/sysfs.c b/drivers/net/wireless/b43/sysfs.c deleted file mode 100644 index 3190493bd07f..000000000000 --- a/drivers/net/wireless/b43/sysfs.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - - Broadcom B43 wireless driver - - SYSFS support routines - - Copyright (c) 2006 Michael Buesch - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include -#include - -#include "b43.h" -#include "sysfs.h" -#include "main.h" -#include "phy_common.h" - -#define GENERIC_FILESIZE 64 - -static int get_integer(const char *buf, size_t count) -{ - char tmp[10 + 1] = { 0 }; - int ret = -EINVAL; - - if (count == 0) - goto out; - count = min_t(size_t, count, 10); - memcpy(tmp, buf, count); - ret = simple_strtol(tmp, NULL, 10); - out: - return ret; -} - -static ssize_t b43_attr_interfmode_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct b43_wldev *wldev = dev_to_b43_wldev(dev); - ssize_t count = 0; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - mutex_lock(&wldev->wl->mutex); - - if (wldev->phy.type != B43_PHYTYPE_G) { - mutex_unlock(&wldev->wl->mutex); - return -ENOSYS; - } - - switch (wldev->phy.g->interfmode) { - case B43_INTERFMODE_NONE: - count = - snprintf(buf, PAGE_SIZE, - "0 (No Interference Mitigation)\n"); - break; - case B43_INTERFMODE_NONWLAN: - count = - snprintf(buf, PAGE_SIZE, - "1 (Non-WLAN Interference Mitigation)\n"); - break; - case B43_INTERFMODE_MANUALWLAN: - count = - snprintf(buf, PAGE_SIZE, - "2 (WLAN Interference Mitigation)\n"); - break; - default: - B43_WARN_ON(1); - } - - mutex_unlock(&wldev->wl->mutex); - - return count; -} - -static ssize_t b43_attr_interfmode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct b43_wldev *wldev = dev_to_b43_wldev(dev); - int err; - int mode; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - mode = get_integer(buf, count); - switch (mode) { - case 0: - mode = B43_INTERFMODE_NONE; - break; - case 1: - mode = B43_INTERFMODE_NONWLAN; - break; - case 2: - mode = B43_INTERFMODE_MANUALWLAN; - break; - case 3: - mode = B43_INTERFMODE_AUTOWLAN; - break; - default: - return -EINVAL; - } - - mutex_lock(&wldev->wl->mutex); - - if (wldev->phy.ops->interf_mitigation) { - err = wldev->phy.ops->interf_mitigation(wldev, mode); - if (err) { - b43err(wldev->wl, "Interference Mitigation not " - "supported by device\n"); - } - } else - err = -ENOSYS; - - mmiowb(); - mutex_unlock(&wldev->wl->mutex); - - return err ? err : count; -} - -static DEVICE_ATTR(interference, 0644, - b43_attr_interfmode_show, b43_attr_interfmode_store); - -int b43_sysfs_register(struct b43_wldev *wldev) -{ - struct device *dev = wldev->dev->dev; - - B43_WARN_ON(b43_status(wldev) != B43_STAT_INITIALIZED); - - return device_create_file(dev, &dev_attr_interference); -} - -void b43_sysfs_unregister(struct b43_wldev *wldev) -{ - struct device *dev = wldev->dev->dev; - - device_remove_file(dev, &dev_attr_interference); -} diff --git a/drivers/net/wireless/b43/sysfs.h b/drivers/net/wireless/b43/sysfs.h deleted file mode 100644 index 12bda9ef1a85..000000000000 --- a/drivers/net/wireless/b43/sysfs.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef B43_SYSFS_H_ -#define B43_SYSFS_H_ - -struct b43_wldev; - -int b43_sysfs_register(struct b43_wldev *dev); -void b43_sysfs_unregister(struct b43_wldev *dev); - -#endif /* B43_SYSFS_H_ */ diff --git a/drivers/net/wireless/b43/tables.c b/drivers/net/wireless/b43/tables.c deleted file mode 100644 index ea288df8aee9..000000000000 --- a/drivers/net/wireless/b43/tables.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - - Broadcom B43 wireless driver - - Copyright (c) 2005 Martin Langer , - Copyright (c) 2005-2007 Stefano Brivio - Copyright (c) 2006, 2006 Michael Buesch - Copyright (c) 2005 Danny van Dyk - Copyright (c) 2005 Andreas Jaggi - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "tables.h" -#include "phy_g.h" - - -const u32 b43_tab_rotor[] = { - 0xFEB93FFD, 0xFEC63FFD, /* 0 */ - 0xFED23FFD, 0xFEDF3FFD, - 0xFEEC3FFE, 0xFEF83FFE, - 0xFF053FFE, 0xFF113FFE, - 0xFF1E3FFE, 0xFF2A3FFF, /* 8 */ - 0xFF373FFF, 0xFF443FFF, - 0xFF503FFF, 0xFF5D3FFF, - 0xFF693FFF, 0xFF763FFF, - 0xFF824000, 0xFF8F4000, /* 16 */ - 0xFF9B4000, 0xFFA84000, - 0xFFB54000, 0xFFC14000, - 0xFFCE4000, 0xFFDA4000, - 0xFFE74000, 0xFFF34000, /* 24 */ - 0x00004000, 0x000D4000, - 0x00194000, 0x00264000, - 0x00324000, 0x003F4000, - 0x004B4000, 0x00584000, /* 32 */ - 0x00654000, 0x00714000, - 0x007E4000, 0x008A3FFF, - 0x00973FFF, 0x00A33FFF, - 0x00B03FFF, 0x00BC3FFF, /* 40 */ - 0x00C93FFF, 0x00D63FFF, - 0x00E23FFE, 0x00EF3FFE, - 0x00FB3FFE, 0x01083FFE, - 0x01143FFE, 0x01213FFD, /* 48 */ - 0x012E3FFD, 0x013A3FFD, - 0x01473FFD, -}; - -const u32 b43_tab_retard[] = { - 0xDB93CB87, 0xD666CF64, /* 0 */ - 0xD1FDD358, 0xCDA6D826, - 0xCA38DD9F, 0xC729E2B4, - 0xC469E88E, 0xC26AEE2B, - 0xC0DEF46C, 0xC073FA62, /* 8 */ - 0xC01D00D5, 0xC0760743, - 0xC1560D1E, 0xC2E51369, - 0xC4ED18FF, 0xC7AC1ED7, - 0xCB2823B2, 0xCEFA28D9, /* 16 */ - 0xD2F62D3F, 0xD7BB3197, - 0xDCE53568, 0xE1FE3875, - 0xE7D13B35, 0xED663D35, - 0xF39B3EC4, 0xF98E3FA7, /* 24 */ - 0x00004000, 0x06723FA7, - 0x0C653EC4, 0x129A3D35, - 0x182F3B35, 0x1E023875, - 0x231B3568, 0x28453197, /* 32 */ - 0x2D0A2D3F, 0x310628D9, - 0x34D823B2, 0x38541ED7, - 0x3B1318FF, 0x3D1B1369, - 0x3EAA0D1E, 0x3F8A0743, /* 40 */ - 0x3FE300D5, 0x3F8DFA62, - 0x3F22F46C, 0x3D96EE2B, - 0x3B97E88E, 0x38D7E2B4, - 0x35C8DD9F, 0x325AD826, /* 48 */ - 0x2E03D358, 0x299ACF64, - 0x246DCB87, -}; - -const u16 b43_tab_finefreqa[] = { - 0x0082, 0x0082, 0x0102, 0x0182, /* 0 */ - 0x0202, 0x0282, 0x0302, 0x0382, - 0x0402, 0x0482, 0x0502, 0x0582, - 0x05E2, 0x0662, 0x06E2, 0x0762, - 0x07E2, 0x0842, 0x08C2, 0x0942, /* 16 */ - 0x09C2, 0x0A22, 0x0AA2, 0x0B02, - 0x0B82, 0x0BE2, 0x0C62, 0x0CC2, - 0x0D42, 0x0DA2, 0x0E02, 0x0E62, - 0x0EE2, 0x0F42, 0x0FA2, 0x1002, /* 32 */ - 0x1062, 0x10C2, 0x1122, 0x1182, - 0x11E2, 0x1242, 0x12A2, 0x12E2, - 0x1342, 0x13A2, 0x1402, 0x1442, - 0x14A2, 0x14E2, 0x1542, 0x1582, /* 48 */ - 0x15E2, 0x1622, 0x1662, 0x16C1, - 0x1701, 0x1741, 0x1781, 0x17E1, - 0x1821, 0x1861, 0x18A1, 0x18E1, - 0x1921, 0x1961, 0x19A1, 0x19E1, /* 64 */ - 0x1A21, 0x1A61, 0x1AA1, 0x1AC1, - 0x1B01, 0x1B41, 0x1B81, 0x1BA1, - 0x1BE1, 0x1C21, 0x1C41, 0x1C81, - 0x1CA1, 0x1CE1, 0x1D01, 0x1D41, /* 80 */ - 0x1D61, 0x1DA1, 0x1DC1, 0x1E01, - 0x1E21, 0x1E61, 0x1E81, 0x1EA1, - 0x1EE1, 0x1F01, 0x1F21, 0x1F41, - 0x1F81, 0x1FA1, 0x1FC1, 0x1FE1, /* 96 */ - 0x2001, 0x2041, 0x2061, 0x2081, - 0x20A1, 0x20C1, 0x20E1, 0x2101, - 0x2121, 0x2141, 0x2161, 0x2181, - 0x21A1, 0x21C1, 0x21E1, 0x2201, /* 112 */ - 0x2221, 0x2241, 0x2261, 0x2281, - 0x22A1, 0x22C1, 0x22C1, 0x22E1, - 0x2301, 0x2321, 0x2341, 0x2361, - 0x2361, 0x2381, 0x23A1, 0x23C1, /* 128 */ - 0x23E1, 0x23E1, 0x2401, 0x2421, - 0x2441, 0x2441, 0x2461, 0x2481, - 0x2481, 0x24A1, 0x24C1, 0x24C1, - 0x24E1, 0x2501, 0x2501, 0x2521, /* 144 */ - 0x2541, 0x2541, 0x2561, 0x2561, - 0x2581, 0x25A1, 0x25A1, 0x25C1, - 0x25C1, 0x25E1, 0x2601, 0x2601, - 0x2621, 0x2621, 0x2641, 0x2641, /* 160 */ - 0x2661, 0x2661, 0x2681, 0x2681, - 0x26A1, 0x26A1, 0x26C1, 0x26C1, - 0x26E1, 0x26E1, 0x2701, 0x2701, - 0x2721, 0x2721, 0x2740, 0x2740, /* 176 */ - 0x2760, 0x2760, 0x2780, 0x2780, - 0x2780, 0x27A0, 0x27A0, 0x27C0, - 0x27C0, 0x27E0, 0x27E0, 0x27E0, - 0x2800, 0x2800, 0x2820, 0x2820, /* 192 */ - 0x2820, 0x2840, 0x2840, 0x2840, - 0x2860, 0x2860, 0x2880, 0x2880, - 0x2880, 0x28A0, 0x28A0, 0x28A0, - 0x28C0, 0x28C0, 0x28C0, 0x28E0, /* 208 */ - 0x28E0, 0x28E0, 0x2900, 0x2900, - 0x2900, 0x2920, 0x2920, 0x2920, - 0x2940, 0x2940, 0x2940, 0x2960, - 0x2960, 0x2960, 0x2960, 0x2980, /* 224 */ - 0x2980, 0x2980, 0x29A0, 0x29A0, - 0x29A0, 0x29A0, 0x29C0, 0x29C0, - 0x29C0, 0x29E0, 0x29E0, 0x29E0, - 0x29E0, 0x2A00, 0x2A00, 0x2A00, /* 240 */ - 0x2A00, 0x2A20, 0x2A20, 0x2A20, - 0x2A20, 0x2A40, 0x2A40, 0x2A40, - 0x2A40, 0x2A60, 0x2A60, 0x2A60, -}; - -const u16 b43_tab_finefreqg[] = { - 0x0089, 0x02E9, 0x0409, 0x04E9, /* 0 */ - 0x05A9, 0x0669, 0x0709, 0x0789, - 0x0829, 0x08A9, 0x0929, 0x0989, - 0x0A09, 0x0A69, 0x0AC9, 0x0B29, - 0x0BA9, 0x0BE9, 0x0C49, 0x0CA9, /* 16 */ - 0x0D09, 0x0D69, 0x0DA9, 0x0E09, - 0x0E69, 0x0EA9, 0x0F09, 0x0F49, - 0x0FA9, 0x0FE9, 0x1029, 0x1089, - 0x10C9, 0x1109, 0x1169, 0x11A9, /* 32 */ - 0x11E9, 0x1229, 0x1289, 0x12C9, - 0x1309, 0x1349, 0x1389, 0x13C9, - 0x1409, 0x1449, 0x14A9, 0x14E9, - 0x1529, 0x1569, 0x15A9, 0x15E9, /* 48 */ - 0x1629, 0x1669, 0x16A9, 0x16E8, - 0x1728, 0x1768, 0x17A8, 0x17E8, - 0x1828, 0x1868, 0x18A8, 0x18E8, - 0x1928, 0x1968, 0x19A8, 0x19E8, /* 64 */ - 0x1A28, 0x1A68, 0x1AA8, 0x1AE8, - 0x1B28, 0x1B68, 0x1BA8, 0x1BE8, - 0x1C28, 0x1C68, 0x1CA8, 0x1CE8, - 0x1D28, 0x1D68, 0x1DC8, 0x1E08, /* 80 */ - 0x1E48, 0x1E88, 0x1EC8, 0x1F08, - 0x1F48, 0x1F88, 0x1FE8, 0x2028, - 0x2068, 0x20A8, 0x2108, 0x2148, - 0x2188, 0x21C8, 0x2228, 0x2268, /* 96 */ - 0x22C8, 0x2308, 0x2348, 0x23A8, - 0x23E8, 0x2448, 0x24A8, 0x24E8, - 0x2548, 0x25A8, 0x2608, 0x2668, - 0x26C8, 0x2728, 0x2787, 0x27E7, /* 112 */ - 0x2847, 0x28C7, 0x2947, 0x29A7, - 0x2A27, 0x2AC7, 0x2B47, 0x2BE7, - 0x2CA7, 0x2D67, 0x2E47, 0x2F67, - 0x3247, 0x3526, 0x3646, 0x3726, /* 128 */ - 0x3806, 0x38A6, 0x3946, 0x39E6, - 0x3A66, 0x3AE6, 0x3B66, 0x3BC6, - 0x3C45, 0x3CA5, 0x3D05, 0x3D85, - 0x3DE5, 0x3E45, 0x3EA5, 0x3EE5, /* 144 */ - 0x3F45, 0x3FA5, 0x4005, 0x4045, - 0x40A5, 0x40E5, 0x4145, 0x4185, - 0x41E5, 0x4225, 0x4265, 0x42C5, - 0x4305, 0x4345, 0x43A5, 0x43E5, /* 160 */ - 0x4424, 0x4464, 0x44C4, 0x4504, - 0x4544, 0x4584, 0x45C4, 0x4604, - 0x4644, 0x46A4, 0x46E4, 0x4724, - 0x4764, 0x47A4, 0x47E4, 0x4824, /* 176 */ - 0x4864, 0x48A4, 0x48E4, 0x4924, - 0x4964, 0x49A4, 0x49E4, 0x4A24, - 0x4A64, 0x4AA4, 0x4AE4, 0x4B23, - 0x4B63, 0x4BA3, 0x4BE3, 0x4C23, /* 192 */ - 0x4C63, 0x4CA3, 0x4CE3, 0x4D23, - 0x4D63, 0x4DA3, 0x4DE3, 0x4E23, - 0x4E63, 0x4EA3, 0x4EE3, 0x4F23, - 0x4F63, 0x4FC3, 0x5003, 0x5043, /* 208 */ - 0x5083, 0x50C3, 0x5103, 0x5143, - 0x5183, 0x51E2, 0x5222, 0x5262, - 0x52A2, 0x52E2, 0x5342, 0x5382, - 0x53C2, 0x5402, 0x5462, 0x54A2, /* 224 */ - 0x5502, 0x5542, 0x55A2, 0x55E2, - 0x5642, 0x5682, 0x56E2, 0x5722, - 0x5782, 0x57E1, 0x5841, 0x58A1, - 0x5901, 0x5961, 0x59C1, 0x5A21, /* 240 */ - 0x5AA1, 0x5B01, 0x5B81, 0x5BE1, - 0x5C61, 0x5D01, 0x5D80, 0x5E20, - 0x5EE0, 0x5FA0, 0x6080, 0x61C0, -}; - -const u16 b43_tab_noisea2[] = { - 0x0001, 0x0001, 0x0001, 0xFFFE, - 0xFFFE, 0x3FFF, 0x1000, 0x0393, -}; - -const u16 b43_tab_noisea3[] = { - 0x5E5E, 0x5E5E, 0x5E5E, 0x3F48, - 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, -}; - -const u16 b43_tab_noiseg1[] = { - 0x013C, 0x01F5, 0x031A, 0x0631, - 0x0001, 0x0001, 0x0001, 0x0001, -}; - -const u16 b43_tab_noiseg2[] = { - 0x5484, 0x3C40, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, -}; - -const u16 b43_tab_noisescalea2[] = { - 0x6767, 0x6767, 0x6767, 0x6767, /* 0 */ - 0x6767, 0x6767, 0x6767, 0x6767, - 0x6767, 0x6767, 0x6767, 0x6767, - 0x6767, 0x6700, 0x6767, 0x6767, - 0x6767, 0x6767, 0x6767, 0x6767, /* 16 */ - 0x6767, 0x6767, 0x6767, 0x6767, - 0x6767, 0x6767, 0x0067, -}; - -const u16 b43_tab_noisescalea3[] = { - 0x2323, 0x2323, 0x2323, 0x2323, /* 0 */ - 0x2323, 0x2323, 0x2323, 0x2323, - 0x2323, 0x2323, 0x2323, 0x2323, - 0x2323, 0x2300, 0x2323, 0x2323, - 0x2323, 0x2323, 0x2323, 0x2323, /* 16 */ - 0x2323, 0x2323, 0x2323, 0x2323, - 0x2323, 0x2323, 0x0023, -}; - -const u16 b43_tab_noisescaleg1[] = { - 0x6C77, 0x5162, 0x3B40, 0x3335, /* 0 */ - 0x2F2D, 0x2A2A, 0x2527, 0x1F21, - 0x1A1D, 0x1719, 0x1616, 0x1414, - 0x1414, 0x1400, 0x1414, 0x1614, - 0x1716, 0x1A19, 0x1F1D, 0x2521, /* 16 */ - 0x2A27, 0x2F2A, 0x332D, 0x3B35, - 0x5140, 0x6C62, 0x0077, -}; - -const u16 b43_tab_noisescaleg2[] = { - 0xD8DD, 0xCBD4, 0xBCC0, 0xB6B7, /* 0 */ - 0xB2B0, 0xADAD, 0xA7A9, 0x9FA1, - 0x969B, 0x9195, 0x8F8F, 0x8A8A, - 0x8A8A, 0x8A00, 0x8A8A, 0x8F8A, - 0x918F, 0x9695, 0x9F9B, 0xA7A1, /* 16 */ - 0xADA9, 0xB2AD, 0xB6B0, 0xBCB7, - 0xCBC0, 0xD8D4, 0x00DD, -}; - -const u16 b43_tab_noisescaleg3[] = { - 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 0 */ - 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, - 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, - 0xA4A4, 0xA400, 0xA4A4, 0xA4A4, - 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 16 */ - 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, - 0xA4A4, 0xA4A4, 0x00A4, -}; - -const u16 b43_tab_sigmasqr1[] = { - 0x007A, 0x0075, 0x0071, 0x006C, /* 0 */ - 0x0067, 0x0063, 0x005E, 0x0059, - 0x0054, 0x0050, 0x004B, 0x0046, - 0x0042, 0x003D, 0x003D, 0x003D, - 0x003D, 0x003D, 0x003D, 0x003D, /* 16 */ - 0x003D, 0x003D, 0x003D, 0x003D, - 0x003D, 0x003D, 0x0000, 0x003D, - 0x003D, 0x003D, 0x003D, 0x003D, - 0x003D, 0x003D, 0x003D, 0x003D, /* 32 */ - 0x003D, 0x003D, 0x003D, 0x003D, - 0x0042, 0x0046, 0x004B, 0x0050, - 0x0054, 0x0059, 0x005E, 0x0063, - 0x0067, 0x006C, 0x0071, 0x0075, /* 48 */ - 0x007A, -}; - -const u16 b43_tab_sigmasqr2[] = { - 0x00DE, 0x00DC, 0x00DA, 0x00D8, /* 0 */ - 0x00D6, 0x00D4, 0x00D2, 0x00CF, - 0x00CD, 0x00CA, 0x00C7, 0x00C4, - 0x00C1, 0x00BE, 0x00BE, 0x00BE, - 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 16 */ - 0x00BE, 0x00BE, 0x00BE, 0x00BE, - 0x00BE, 0x00BE, 0x0000, 0x00BE, - 0x00BE, 0x00BE, 0x00BE, 0x00BE, - 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 32 */ - 0x00BE, 0x00BE, 0x00BE, 0x00BE, - 0x00C1, 0x00C4, 0x00C7, 0x00CA, - 0x00CD, 0x00CF, 0x00D2, 0x00D4, - 0x00D6, 0x00D8, 0x00DA, 0x00DC, /* 48 */ - 0x00DE, -}; - -const u16 b43_tab_rssiagc1[] = { - 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, /* 0 */ - 0xFFF8, 0xFFF9, 0xFFFC, 0xFFFE, - 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, - 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, -}; - -const u16 b43_tab_rssiagc2[] = { - 0x0820, 0x0820, 0x0920, 0x0C38, /* 0 */ - 0x0820, 0x0820, 0x0820, 0x0820, - 0x0820, 0x0820, 0x0920, 0x0A38, - 0x0820, 0x0820, 0x0820, 0x0820, - 0x0820, 0x0820, 0x0920, 0x0A38, /* 16 */ - 0x0820, 0x0820, 0x0820, 0x0820, - 0x0820, 0x0820, 0x0920, 0x0A38, - 0x0820, 0x0820, 0x0820, 0x0820, - 0x0820, 0x0820, 0x0920, 0x0A38, /* 32 */ - 0x0820, 0x0820, 0x0820, 0x0820, - 0x0820, 0x0820, 0x0920, 0x0A38, - 0x0820, 0x0820, 0x0820, 0x0820, -}; - -static inline void assert_sizes(void) -{ - BUILD_BUG_ON(B43_TAB_ROTOR_SIZE != ARRAY_SIZE(b43_tab_rotor)); - BUILD_BUG_ON(B43_TAB_RETARD_SIZE != ARRAY_SIZE(b43_tab_retard)); - BUILD_BUG_ON(B43_TAB_FINEFREQA_SIZE != ARRAY_SIZE(b43_tab_finefreqa)); - BUILD_BUG_ON(B43_TAB_FINEFREQG_SIZE != ARRAY_SIZE(b43_tab_finefreqg)); - BUILD_BUG_ON(B43_TAB_NOISEA2_SIZE != ARRAY_SIZE(b43_tab_noisea2)); - BUILD_BUG_ON(B43_TAB_NOISEA3_SIZE != ARRAY_SIZE(b43_tab_noisea3)); - BUILD_BUG_ON(B43_TAB_NOISEG1_SIZE != ARRAY_SIZE(b43_tab_noiseg1)); - BUILD_BUG_ON(B43_TAB_NOISEG2_SIZE != ARRAY_SIZE(b43_tab_noiseg2)); - BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != - ARRAY_SIZE(b43_tab_noisescalea2)); - BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != - ARRAY_SIZE(b43_tab_noisescalea3)); - BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != - ARRAY_SIZE(b43_tab_noisescaleg1)); - BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != - ARRAY_SIZE(b43_tab_noisescaleg2)); - BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != - ARRAY_SIZE(b43_tab_noisescaleg3)); - BUILD_BUG_ON(B43_TAB_SIGMASQR_SIZE != ARRAY_SIZE(b43_tab_sigmasqr1)); - BUILD_BUG_ON(B43_TAB_SIGMASQR_SIZE != ARRAY_SIZE(b43_tab_sigmasqr2)); - BUILD_BUG_ON(B43_TAB_RSSIAGC1_SIZE != ARRAY_SIZE(b43_tab_rssiagc1)); - BUILD_BUG_ON(B43_TAB_RSSIAGC2_SIZE != ARRAY_SIZE(b43_tab_rssiagc2)); -} - -u16 b43_ofdmtab_read16(struct b43_wldev *dev, u16 table, u16 offset) -{ - struct b43_phy_g *gphy = dev->phy.g; - u16 addr; - - addr = table + offset; - if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_READ) || - (addr - 1 != gphy->ofdmtab_addr)) { - /* The hardware has a different address in memory. Update it. */ - b43_phy_write(dev, B43_PHY_OTABLECTL, addr); - gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_READ; - } - gphy->ofdmtab_addr = addr; - - return b43_phy_read(dev, B43_PHY_OTABLEI); - - /* Some compiletime assertions... */ - assert_sizes(); -} - -void b43_ofdmtab_write16(struct b43_wldev *dev, u16 table, - u16 offset, u16 value) -{ - struct b43_phy_g *gphy = dev->phy.g; - u16 addr; - - addr = table + offset; - if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_WRITE) || - (addr - 1 != gphy->ofdmtab_addr)) { - /* The hardware has a different address in memory. Update it. */ - b43_phy_write(dev, B43_PHY_OTABLECTL, addr); - gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_WRITE; - } - gphy->ofdmtab_addr = addr; - b43_phy_write(dev, B43_PHY_OTABLEI, value); -} - -u32 b43_ofdmtab_read32(struct b43_wldev *dev, u16 table, u16 offset) -{ - struct b43_phy_g *gphy = dev->phy.g; - u32 ret; - u16 addr; - - addr = table + offset; - if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_READ) || - (addr - 1 != gphy->ofdmtab_addr)) { - /* The hardware has a different address in memory. Update it. */ - b43_phy_write(dev, B43_PHY_OTABLECTL, addr); - gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_READ; - } - gphy->ofdmtab_addr = addr; - ret = b43_phy_read(dev, B43_PHY_OTABLEQ); - ret <<= 16; - ret |= b43_phy_read(dev, B43_PHY_OTABLEI); - - return ret; -} - -void b43_ofdmtab_write32(struct b43_wldev *dev, u16 table, - u16 offset, u32 value) -{ - struct b43_phy_g *gphy = dev->phy.g; - u16 addr; - - addr = table + offset; - if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_WRITE) || - (addr - 1 != gphy->ofdmtab_addr)) { - /* The hardware has a different address in memory. Update it. */ - b43_phy_write(dev, B43_PHY_OTABLECTL, addr); - gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_WRITE; - } - gphy->ofdmtab_addr = addr; - - b43_phy_write(dev, B43_PHY_OTABLEI, value); - b43_phy_write(dev, B43_PHY_OTABLEQ, (value >> 16)); -} - -u16 b43_gtab_read(struct b43_wldev *dev, u16 table, u16 offset) -{ - b43_phy_write(dev, B43_PHY_GTABCTL, table + offset); - return b43_phy_read(dev, B43_PHY_GTABDATA); -} - -void b43_gtab_write(struct b43_wldev *dev, u16 table, u16 offset, u16 value) -{ - b43_phy_write(dev, B43_PHY_GTABCTL, table + offset); - b43_phy_write(dev, B43_PHY_GTABDATA, value); -} diff --git a/drivers/net/wireless/b43/tables.h b/drivers/net/wireless/b43/tables.h deleted file mode 100644 index 80e73c7cbac5..000000000000 --- a/drivers/net/wireless/b43/tables.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef B43_TABLES_H_ -#define B43_TABLES_H_ - -#define B43_TAB_ROTOR_SIZE 53 -extern const u32 b43_tab_rotor[]; -#define B43_TAB_RETARD_SIZE 53 -extern const u32 b43_tab_retard[]; -#define B43_TAB_FINEFREQA_SIZE 256 -extern const u16 b43_tab_finefreqa[]; -#define B43_TAB_FINEFREQG_SIZE 256 -extern const u16 b43_tab_finefreqg[]; -#define B43_TAB_NOISEA2_SIZE 8 -extern const u16 b43_tab_noisea2[]; -#define B43_TAB_NOISEA3_SIZE 8 -extern const u16 b43_tab_noisea3[]; -#define B43_TAB_NOISEG1_SIZE 8 -extern const u16 b43_tab_noiseg1[]; -#define B43_TAB_NOISEG2_SIZE 8 -extern const u16 b43_tab_noiseg2[]; -#define B43_TAB_NOISESCALE_SIZE 27 -extern const u16 b43_tab_noisescalea2[]; -extern const u16 b43_tab_noisescalea3[]; -extern const u16 b43_tab_noisescaleg1[]; -extern const u16 b43_tab_noisescaleg2[]; -extern const u16 b43_tab_noisescaleg3[]; -#define B43_TAB_SIGMASQR_SIZE 53 -extern const u16 b43_tab_sigmasqr1[]; -extern const u16 b43_tab_sigmasqr2[]; -#define B43_TAB_RSSIAGC1_SIZE 16 -extern const u16 b43_tab_rssiagc1[]; -#define B43_TAB_RSSIAGC2_SIZE 48 -extern const u16 b43_tab_rssiagc2[]; - -#endif /* B43_TABLES_H_ */ diff --git a/drivers/net/wireless/b43/tables_lpphy.c b/drivers/net/wireless/b43/tables_lpphy.c deleted file mode 100644 index cff187c5616d..000000000000 --- a/drivers/net/wireless/b43/tables_lpphy.c +++ /dev/null @@ -1,2456 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11a/g LP-PHY and radio device data tables - - Copyright (c) 2009 Michael Buesch - Copyright (c) 2009 Gábor Stefanik - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "tables_lpphy.h" -#include "phy_common.h" -#include "phy_lp.h" - - -/* Entry of the 2062/2063 radio init table */ -struct b206x_init_tab_entry { - u16 offset; - u16 value_a; - u16 value_g; - u8 flags; -}; -#define B206X_FLAG_A 0x01 /* Flag: Init in A mode */ -#define B206X_FLAG_G 0x02 /* Flag: Init in G mode */ - -static const struct b206x_init_tab_entry b2062_init_tab[] = { - /* { .offset = B2062_N_COMM1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = 0x0001, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_COMM4, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_N_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_COMM15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_PDN_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_PDN_CTL1, .value_a = 0x0000, .value_g = 0x00CA, .flags = B206X_FLAG_G, }, - /* { .offset = B2062_N_PDN_CTL2, .value_a = 0x0018, .value_g = 0x0018, .flags = 0, }, */ - { .offset = B2062_N_PDN_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_N_PDN_CTL4, .value_a = 0x0015, .value_g = 0x002A, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_N_GEN_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_IQ_CALIB, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - { .offset = B2062_N_LGENC, .value_a = 0x00DB, .value_g = 0x00FF, .flags = B206X_FLAG_A, }, - /* { .offset = B2062_N_LGENA_LPF, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - /* { .offset = B2062_N_LGENA_BIAS0, .value_a = 0x0041, .value_g = 0x0041, .flags = 0, }, */ - /* { .offset = B2062_N_LGNEA_BIAS1, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ - /* { .offset = B2062_N_LGENA_CTL0, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ - /* { .offset = B2062_N_LGENA_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_LGENA_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_LGENA_TUNE0, .value_a = 0x00DD, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_N_LGENA_TUNE1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_LGENA_TUNE2, .value_a = 0x00DD, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_N_LGENA_TUNE3, .value_a = 0x0077, .value_g = 0x00B5, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_N_LGENA_CTL3, .value_a = 0x0000, .value_g = 0x00FF, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_N_LGENA_CTL4, .value_a = 0x001F, .value_g = 0x001F, .flags = 0, }, */ - /* { .offset = B2062_N_LGENA_CTL5, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ - /* { .offset = B2062_N_LGENA_CTL6, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ - { .offset = B2062_N_LGENA_CTL7, .value_a = 0x0033, .value_g = 0x0033, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_N_RXA_CTL0, .value_a = 0x0009, .value_g = 0x0009, .flags = 0, }, */ - { .offset = B2062_N_RXA_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, - /* { .offset = B2062_N_RXA_CTL2, .value_a = 0x0018, .value_g = 0x0018, .flags = 0, }, */ - /* { .offset = B2062_N_RXA_CTL3, .value_a = 0x0027, .value_g = 0x0027, .flags = 0, }, */ - /* { .offset = B2062_N_RXA_CTL4, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ - /* { .offset = B2062_N_RXA_CTL5, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ - /* { .offset = B2062_N_RXA_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_RXA_CTL7, .value_a = 0x0008, .value_g = 0x0008, .flags = 0, }, */ - { .offset = B2062_N_RXBB_CTL0, .value_a = 0x0082, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_N_RXBB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_GAIN0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_RXBB_GAIN1, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_N_RXBB_GAIN2, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_N_RXBB_GAIN3, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_RSSI0, .value_a = 0x0043, .value_g = 0x0043, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_RSSI1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_CALIB0, .value_a = 0x0010, .value_g = 0x0010, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_CALIB1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_CALIB2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_BIAS0, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_BIAS1, .value_a = 0x002A, .value_g = 0x002A, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_BIAS2, .value_a = 0x00AA, .value_g = 0x00AA, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_BIAS3, .value_a = 0x0021, .value_g = 0x0021, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_BIAS4, .value_a = 0x00AA, .value_g = 0x00AA, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_BIAS5, .value_a = 0x0022, .value_g = 0x0022, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_RSSI2, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_RSSI3, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_RSSI4, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - /* { .offset = B2062_N_RXBB_RSSI5, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ - /* { .offset = B2062_N_TX_CTL0, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - /* { .offset = B2062_N_TX_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_TX_CTL2, .value_a = 0x0084, .value_g = 0x0084, .flags = 0, }, */ - /* { .offset = B2062_N_TX_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_TX_CTL4, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_N_TX_CTL5, .value_a = 0x0002, .value_g = 0x0002, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_N_TX_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_TX_CTL7, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ - /* { .offset = B2062_N_TX_CTL8, .value_a = 0x0082, .value_g = 0x0082, .flags = 0, }, */ - /* { .offset = B2062_N_TX_CTL9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_TX_CTL_A, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_TX_GC2G, .value_a = 0x00FF, .value_g = 0x00FF, .flags = 0, }, */ - /* { .offset = B2062_N_TX_GC5G, .value_a = 0x00FF, .value_g = 0x00FF, .flags = 0, }, */ - { .offset = B2062_N_TX_TUNE, .value_a = 0x0088, .value_g = 0x001B, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_N_TX_PAD, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ - /* { .offset = B2062_N_TX_PGA, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ - /* { .offset = B2062_N_TX_PADAUX, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2062_N_TX_PGAAUX, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2062_N_TSSI_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_TSSI_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_TSSI_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_IQ_CALIB_CTL0, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2062_N_IQ_CALIB_CTL1, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ - /* { .offset = B2062_N_IQ_CALIB_CTL2, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ - /* { .offset = B2062_N_CALIB_TS, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_CALIB_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_CALIB_CTL1, .value_a = 0x0015, .value_g = 0x0015, .flags = 0, }, */ - /* { .offset = B2062_N_CALIB_CTL2, .value_a = 0x000F, .value_g = 0x000F, .flags = 0, }, */ - /* { .offset = B2062_N_CALIB_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_CALIB_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_CALIB_DBG0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_CALIB_DBG1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_CALIB_DBG2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_CALIB_DBG3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_PSENSE_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_PSENSE_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_PSENSE_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_N_TEST_BUF0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RADIO_ID_CODE, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_COMM4, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_S_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_COMM15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_PDS_CTL0, .value_a = 0x00FF, .value_g = 0x00FF, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_S_PDS_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_PDS_CTL2, .value_a = 0x008E, .value_g = 0x008E, .flags = 0, }, */ - /* { .offset = B2062_S_PDS_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_BG_CTL0, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ - /* { .offset = B2062_S_BG_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_BG_CTL2, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ - { .offset = B2062_S_LGENG_CTL0, .value_a = 0x00F8, .value_g = 0x00D8, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_LGENG_CTL1, .value_a = 0x003C, .value_g = 0x0024, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_S_LGENG_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_LGENG_CTL3, .value_a = 0x0041, .value_g = 0x0041, .flags = 0, }, */ - /* { .offset = B2062_S_LGENG_CTL4, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ - /* { .offset = B2062_S_LGENG_CTL5, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2062_S_LGENG_CTL6, .value_a = 0x0022, .value_g = 0x0022, .flags = 0, }, */ - /* { .offset = B2062_S_LGENG_CTL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_LGENG_CTL8, .value_a = 0x0088, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_S_LGENG_CTL9, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ - { .offset = B2062_S_LGENG_CTL10, .value_a = 0x0088, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_S_LGENG_CTL11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL1, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL2, .value_a = 0x00AF, .value_g = 0x00AF, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL3, .value_a = 0x0012, .value_g = 0x0012, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL4, .value_a = 0x000B, .value_g = 0x000B, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL5, .value_a = 0x005F, .value_g = 0x005F, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL7, .value_a = 0x0040, .value_g = 0x0040, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL8, .value_a = 0x0052, .value_g = 0x0052, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL9, .value_a = 0x0026, .value_g = 0x0026, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL10, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL11, .value_a = 0x0036, .value_g = 0x0036, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL12, .value_a = 0x0057, .value_g = 0x0057, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL13, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL14, .value_a = 0x0075, .value_g = 0x0075, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL15, .value_a = 0x00B4, .value_g = 0x00B4, .flags = 0, }, */ - /* { .offset = B2062_S_REFPLL_CTL16, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL0, .value_a = 0x0098, .value_g = 0x0098, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL1, .value_a = 0x0010, .value_g = 0x0010, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_S_RFPLL_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RFPLL_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RFPLL_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL5, .value_a = 0x0043, .value_g = 0x0043, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL6, .value_a = 0x0047, .value_g = 0x0047, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL7, .value_a = 0x000C, .value_g = 0x000C, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL8, .value_a = 0x0011, .value_g = 0x0011, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL9, .value_a = 0x0011, .value_g = 0x0011, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL10, .value_a = 0x000E, .value_g = 0x000E, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL11, .value_a = 0x0008, .value_g = 0x0008, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL12, .value_a = 0x0033, .value_g = 0x0033, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL13, .value_a = 0x000A, .value_g = 0x000A, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL14, .value_a = 0x0006, .value_g = 0x0006, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_S_RFPLL_CTL15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RFPLL_CTL16, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RFPLL_CTL17, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL18, .value_a = 0x003E, .value_g = 0x003E, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL19, .value_a = 0x0013, .value_g = 0x0013, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_S_RFPLL_CTL20, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL21, .value_a = 0x0062, .value_g = 0x0062, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL22, .value_a = 0x0007, .value_g = 0x0007, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL23, .value_a = 0x0016, .value_g = 0x0016, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL24, .value_a = 0x005C, .value_g = 0x005C, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL25, .value_a = 0x0095, .value_g = 0x0095, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_S_RFPLL_CTL26, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RFPLL_CTL27, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RFPLL_CTL28, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RFPLL_CTL29, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL30, .value_a = 0x00A0, .value_g = 0x00A0, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL31, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_S_RFPLL_CTL32, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL33, .value_a = 0x00CC, .value_g = 0x00CC, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL34, .value_a = 0x0007, .value_g = 0x0007, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2062_S_RXG_CNT0, .value_a = 0x0010, .value_g = 0x0010, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT5, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT6, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT7, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ - { .offset = B2062_S_RXG_CNT8, .value_a = 0x000F, .value_g = 0x000F, .flags = B206X_FLAG_A, }, - /* { .offset = B2062_S_RXG_CNT9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT10, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT11, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT12, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT13, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT14, .value_a = 0x00A0, .value_g = 0x00A0, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT15, .value_a = 0x0004, .value_g = 0x0004, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT16, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2062_S_RXG_CNT17, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ -}; - -static const struct b206x_init_tab_entry b2063_init_tab[] = { - { .offset = B2063_COMM1, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, - /* { .offset = B2063_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_COMM4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_COMM8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_COMM9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_COMM10, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A, }, - /* { .offset = B2063_COMM11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_COMM12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_COMM13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_COMM14, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ - /* { .offset = B2063_COMM15, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ - { .offset = B2063_COMM16, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, - { .offset = B2063_COMM17, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, - { .offset = B2063_COMM18, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, - { .offset = B2063_COMM19, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, - { .offset = B2063_COMM20, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, - { .offset = B2063_COMM21, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, - { .offset = B2063_COMM22, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, - { .offset = B2063_COMM23, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, - { .offset = B2063_COMM24, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, - /* { .offset = B2063_PWR_SWITCH_CTL, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ - /* { .offset = B2063_PLL_SP1, .value_a = 0x003f, .value_g = 0x003f, .flags = 0, }, */ - /* { .offset = B2063_PLL_SP2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_LOGEN_SP1, .value_a = 0x00e8, .value_g = 0x00d4, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2063_LOGEN_SP2, .value_a = 0x00a7, .value_g = 0x0053, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_LOGEN_SP3, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ - { .offset = B2063_LOGEN_SP4, .value_a = 0x00f0, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_LOGEN_SP5, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - { .offset = B2063_G_RX_SP1, .value_a = 0x001f, .value_g = 0x005e, .flags = B206X_FLAG_G, }, - { .offset = B2063_G_RX_SP2, .value_a = 0x007f, .value_g = 0x007e, .flags = B206X_FLAG_G, }, - { .offset = B2063_G_RX_SP3, .value_a = 0x0030, .value_g = 0x00f0, .flags = B206X_FLAG_G, }, - /* { .offset = B2063_G_RX_SP4, .value_a = 0x0035, .value_g = 0x0035, .flags = 0, }, */ - /* { .offset = B2063_G_RX_SP5, .value_a = 0x003f, .value_g = 0x003f, .flags = 0, }, */ - /* { .offset = B2063_G_RX_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_G_RX_SP7, .value_a = 0x007f, .value_g = 0x007f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_G_RX_SP8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_SP9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_G_RX_SP10, .value_a = 0x000c, .value_g = 0x000c, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_G_RX_SP11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_A_RX_SP1, .value_a = 0x003c, .value_g = 0x003f, .flags = B206X_FLAG_A, }, - { .offset = B2063_A_RX_SP2, .value_a = 0x00fc, .value_g = 0x00fe, .flags = B206X_FLAG_A, }, - /* { .offset = B2063_A_RX_SP3, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ - /* { .offset = B2063_A_RX_SP4, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ - /* { .offset = B2063_A_RX_SP5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_A_RX_SP7, .value_a = 0x0008, .value_g = 0x0008, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_RX_BB_SP1, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ - /* { .offset = B2063_RX_BB_SP2, .value_a = 0x0022, .value_g = 0x0022, .flags = 0, }, */ - /* { .offset = B2063_RX_BB_SP3, .value_a = 0x00a8, .value_g = 0x00a8, .flags = 0, }, */ - { .offset = B2063_RX_BB_SP4, .value_a = 0x0060, .value_g = 0x0060, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_RX_BB_SP5, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ - /* { .offset = B2063_RX_BB_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_RX_BB_SP7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_RX_BB_SP8, .value_a = 0x0030, .value_g = 0x0030, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_TX_RF_SP1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP2, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ - { .offset = B2063_TX_RF_SP3, .value_a = 0x000c, .value_g = 0x000b, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2063_TX_RF_SP4, .value_a = 0x0010, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_TX_RF_SP5, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP6, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP7, .value_a = 0x0068, .value_g = 0x0068, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP8, .value_a = 0x0068, .value_g = 0x0068, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP9, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP10, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP11, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP12, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP13, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP14, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP15, .value_a = 0x00c0, .value_g = 0x00c0, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP16, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_SP17, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ - { .offset = B2063_PA_SP1, .value_a = 0x003d, .value_g = 0x00fd, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_PA_SP2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ - /* { .offset = B2063_PA_SP3, .value_a = 0x0096, .value_g = 0x0096, .flags = 0, }, */ - /* { .offset = B2063_PA_SP4, .value_a = 0x005a, .value_g = 0x005a, .flags = 0, }, */ - /* { .offset = B2063_PA_SP5, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ - /* { .offset = B2063_PA_SP6, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ - /* { .offset = B2063_PA_SP7, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - { .offset = B2063_TX_BB_SP1, .value_a = 0x0002, .value_g = 0x0002, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_TX_BB_SP2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_TX_BB_SP3, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ - /* { .offset = B2063_REG_SP1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_BANDGAP_CTL1, .value_a = 0x0056, .value_g = 0x0056, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_BANDGAP_CTL2, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ - /* { .offset = B2063_LPO_CTL1, .value_a = 0x000e, .value_g = 0x000e, .flags = 0, }, */ - /* { .offset = B2063_RC_CALIB_CTL1, .value_a = 0x007e, .value_g = 0x007e, .flags = 0, }, */ - /* { .offset = B2063_RC_CALIB_CTL2, .value_a = 0x0015, .value_g = 0x0015, .flags = 0, }, */ - /* { .offset = B2063_RC_CALIB_CTL3, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ - /* { .offset = B2063_RC_CALIB_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_RC_CALIB_CTL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_RC_CALIB_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_RC_CALIB_CTL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_RC_CALIB_CTL8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_RC_CALIB_CTL9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_RC_CALIB_CTL10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_CALNRST, .value_a = 0x0004, .value_g = 0x0004, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_IN_PLL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_IN_PLL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_CP1, .value_a = 0x00cf, .value_g = 0x00cf, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_CP2, .value_a = 0x0059, .value_g = 0x0059, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_CP3, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_CP4, .value_a = 0x0042, .value_g = 0x0042, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_LF1, .value_a = 0x00db, .value_g = 0x00db, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_LF2, .value_a = 0x0094, .value_g = 0x0094, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_LF3, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_LF4, .value_a = 0x0063, .value_g = 0x0063, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_SG1, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_SG2, .value_a = 0x00d3, .value_g = 0x00d3, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_SG3, .value_a = 0x00b1, .value_g = 0x00b1, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_SG4, .value_a = 0x003b, .value_g = 0x003b, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_SG5, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_VCO1, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ - { .offset = B2063_PLL_JTAG_PLL_VCO2, .value_a = 0x00f7, .value_g = 0x00f7, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB3, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB5, .value_a = 0x0009, .value_g = 0x0009, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB6, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB7, .value_a = 0x0016, .value_g = 0x0016, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB8, .value_a = 0x006b, .value_g = 0x006b, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB10, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_XTAL_12, .value_a = 0x0004, .value_g = 0x0004, .flags = 0, }, */ - /* { .offset = B2063_PLL_JTAG_PLL_XTAL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_ACL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_ACL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_ACL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_ACL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_ACL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_INPUTS, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_WAITCNT, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_OVR1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_OVR2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_OVAL1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_OVAL2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_OVAL3, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_OVAL4, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_OVAL5, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_OVAL6, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_OVAL7, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CALVLD1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CALVLD2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CVAL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CVAL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CVAL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CVAL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CVAL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CVAL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LO_CALIB_CVAL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_CALIB_EN, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_PEAKDET1, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_RCCR1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_VCOBUF1, .value_a = 0x0060, .value_g = 0x0060, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_MIXER1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_MIXER2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_BUF1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_BUF2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_DIV1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_DIV2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_DIV3, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_CBUFRX1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_CBUFRX2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_CBUFTX1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_CBUFTX2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_IDAC1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_SPARE1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_SPARE2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_LOGEN_SPARE3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_1ST1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2063_G_RX_1ST2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_1ST3, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ - /* { .offset = B2063_G_RX_2ND1, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ - /* { .offset = B2063_G_RX_2ND2, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ - /* { .offset = B2063_G_RX_2ND3, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2063_G_RX_2ND4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_2ND5, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2063_G_RX_2ND6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_2ND7, .value_a = 0x0035, .value_g = 0x0035, .flags = 0, }, */ - /* { .offset = B2063_G_RX_2ND8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_PS1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2063_G_RX_PS2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_PS3, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2063_G_RX_PS4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_PS5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_MIX1, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ - /* { .offset = B2063_G_RX_MIX2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_G_RX_MIX3, .value_a = 0x0071, .value_g = 0x0071, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2063_G_RX_MIX4, .value_a = 0x0071, .value_g = 0x0071, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_G_RX_MIX5, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ - /* { .offset = B2063_G_RX_MIX6, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ - /* { .offset = B2063_G_RX_MIX7, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ - /* { .offset = B2063_G_RX_MIX8, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - /* { .offset = B2063_G_RX_PDET1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_SPARES1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_SPARES2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_G_RX_SPARES3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_1ST1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_A_RX_1ST2, .value_a = 0x00f0, .value_g = 0x0030, .flags = B206X_FLAG_A, }, - /* { .offset = B2063_A_RX_1ST3, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ - /* { .offset = B2063_A_RX_1ST4, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2063_A_RX_1ST5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_2ND1, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ - /* { .offset = B2063_A_RX_2ND2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_2ND3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_2ND4, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ - /* { .offset = B2063_A_RX_2ND5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_2ND6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_2ND7, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ - /* { .offset = B2063_A_RX_PS1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_PS2, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2063_A_RX_PS3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_PS4, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ - /* { .offset = B2063_A_RX_PS5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_A_RX_PS6, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_A_RX_MIX1, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ - /* { .offset = B2063_A_RX_MIX2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_MIX3, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ - { .offset = B2063_A_RX_MIX4, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2063_A_RX_MIX5, .value_a = 0x000f, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - { .offset = B2063_A_RX_MIX6, .value_a = 0x000f, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_A_RX_MIX7, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ - /* { .offset = B2063_A_RX_MIX8, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - /* { .offset = B2063_A_RX_PWRDET1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_SPARE1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_SPARE2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_A_RX_SPARE3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_RX_TIA_CTL1, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_RX_TIA_CTL2, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ - { .offset = B2063_RX_TIA_CTL3, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_RX_TIA_CTL4, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ - /* { .offset = B2063_RX_TIA_CTL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_RX_TIA_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_RX_BB_CTL1, .value_a = 0x0074, .value_g = 0x0074, .flags = 0, }, */ - { .offset = B2063_RX_BB_CTL2, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_RX_BB_CTL3, .value_a = 0x00a2, .value_g = 0x00a2, .flags = 0, }, */ - /* { .offset = B2063_RX_BB_CTL4, .value_a = 0x00aa, .value_g = 0x00aa, .flags = 0, }, */ - /* { .offset = B2063_RX_BB_CTL5, .value_a = 0x0024, .value_g = 0x0024, .flags = 0, }, */ - /* { .offset = B2063_RX_BB_CTL6, .value_a = 0x00a9, .value_g = 0x00a9, .flags = 0, }, */ - /* { .offset = B2063_RX_BB_CTL7, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ - /* { .offset = B2063_RX_BB_CTL8, .value_a = 0x0010, .value_g = 0x0010, .flags = 0, }, */ - /* { .offset = B2063_RX_BB_CTL9, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL1, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_IDAC_LO_RF_I, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_IDAC_LO_RF_Q, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_IDAC_LO_BB_I, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_IDAC_LO_BB_Q, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL2, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL3, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL4, .value_a = 0x00b8, .value_g = 0x00b8, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL5, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL6, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL7, .value_a = 0x0078, .value_g = 0x0078, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL8, .value_a = 0x00c0, .value_g = 0x00c0, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL9, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_TX_RF_CTL15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_PA_CTL1, .value_a = 0x0000, .value_g = 0x0004, .flags = B206X_FLAG_A, }, - /* { .offset = B2063_PA_CTL2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ - /* { .offset = B2063_PA_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PA_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PA_CTL5, .value_a = 0x0096, .value_g = 0x0096, .flags = 0, }, */ - /* { .offset = B2063_PA_CTL6, .value_a = 0x0077, .value_g = 0x0077, .flags = 0, }, */ - /* { .offset = B2063_PA_CTL7, .value_a = 0x005a, .value_g = 0x005a, .flags = 0, }, */ - /* { .offset = B2063_PA_CTL8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PA_CTL9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PA_CTL10, .value_a = 0x0021, .value_g = 0x0021, .flags = 0, }, */ - /* { .offset = B2063_PA_CTL11, .value_a = 0x0070, .value_g = 0x0070, .flags = 0, }, */ - /* { .offset = B2063_PA_CTL12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_PA_CTL13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_TX_BB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_TX_BB_CTL2, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ - /* { .offset = B2063_TX_BB_CTL3, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ - /* { .offset = B2063_TX_BB_CTL4, .value_a = 0x000b, .value_g = 0x000b, .flags = 0, }, */ - /* { .offset = B2063_GPIO_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2063_VREG_CTL1, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, - /* { .offset = B2063_AMUX_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_IQ_CALIB_GVAR, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ - /* { .offset = B2063_IQ_CALIB_CTL1, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ - /* { .offset = B2063_IQ_CALIB_CTL2, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ - /* { .offset = B2063_TEMPSENSE_CTL1, .value_a = 0x0046, .value_g = 0x0046, .flags = 0, }, */ - /* { .offset = B2063_TEMPSENSE_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_TX_RX_LOOPBACK1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_TX_RX_LOOPBACK2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - /* { .offset = B2063_EXT_TSSI_CTL1, .value_a = 0x0021, .value_g = 0x0021, .flags = 0, }, */ - /* { .offset = B2063_EXT_TSSI_CTL2, .value_a = 0x0023, .value_g = 0x0023, .flags = 0, }, */ - /* { .offset = B2063_AFE_CTL , .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ -}; - -void b2062_upload_init_table(struct b43_wldev *dev) -{ - const struct b206x_init_tab_entry *e; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(b2062_init_tab); i++) { - e = &b2062_init_tab[i]; - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if (!(e->flags & B206X_FLAG_G)) - continue; - b43_radio_write(dev, e->offset, e->value_g); - } else { - if (!(e->flags & B206X_FLAG_A)) - continue; - b43_radio_write(dev, e->offset, e->value_a); - } - } -} - -void b2063_upload_init_table(struct b43_wldev *dev) -{ - const struct b206x_init_tab_entry *e; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(b2063_init_tab); i++) { - e = &b2063_init_tab[i]; - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if (!(e->flags & B206X_FLAG_G)) - continue; - b43_radio_write(dev, e->offset, e->value_g); - } else { - if (!(e->flags & B206X_FLAG_A)) - continue; - b43_radio_write(dev, e->offset, e->value_a); - } - } -} - -u32 b43_lptab_read(struct b43_wldev *dev, u32 offset) -{ - u32 type, value; - - type = offset & B43_LPTAB_TYPEMASK; - offset &= ~B43_LPTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - switch (type) { - case B43_LPTAB_8BIT: - b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_LPPHY_TABLEDATALO) & 0xFF; - break; - case B43_LPTAB_16BIT: - b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_LPPHY_TABLEDATALO); - break; - case B43_LPTAB_32BIT: - b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_LPPHY_TABLEDATAHI); - value <<= 16; - value |= b43_phy_read(dev, B43_LPPHY_TABLEDATALO); - break; - default: - B43_WARN_ON(1); - value = 0; - } - - return value; -} - -void b43_lptab_read_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, void *_data) -{ - u32 type; - u8 *data = _data; - unsigned int i; - - type = offset & B43_LPTAB_TYPEMASK; - offset &= ~B43_LPTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); - - for (i = 0; i < nr_elements; i++) { - switch (type) { - case B43_LPTAB_8BIT: - *data = b43_phy_read(dev, B43_LPPHY_TABLEDATALO) & 0xFF; - data++; - break; - case B43_LPTAB_16BIT: - *((u16 *)data) = b43_phy_read(dev, B43_LPPHY_TABLEDATALO); - data += 2; - break; - case B43_LPTAB_32BIT: - *((u32 *)data) = b43_phy_read(dev, B43_LPPHY_TABLEDATAHI); - *((u32 *)data) <<= 16; - *((u32 *)data) |= b43_phy_read(dev, B43_LPPHY_TABLEDATALO); - data += 4; - break; - default: - B43_WARN_ON(1); - } - } -} - -void b43_lptab_write(struct b43_wldev *dev, u32 offset, u32 value) -{ - u32 type; - - type = offset & B43_LPTAB_TYPEMASK; - offset &= ~B43_LPTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - switch (type) { - case B43_LPTAB_8BIT: - B43_WARN_ON(value & ~0xFF); - b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); - b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); - break; - case B43_LPTAB_16BIT: - B43_WARN_ON(value & ~0xFFFF); - b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); - b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); - break; - case B43_LPTAB_32BIT: - b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); - b43_phy_write(dev, B43_LPPHY_TABLEDATAHI, value >> 16); - b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); - break; - default: - B43_WARN_ON(1); - } -} - -void b43_lptab_write_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, const void *_data) -{ - u32 type, value; - const u8 *data = _data; - unsigned int i; - - type = offset & B43_LPTAB_TYPEMASK; - offset &= ~B43_LPTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); - - for (i = 0; i < nr_elements; i++) { - switch (type) { - case B43_LPTAB_8BIT: - value = *data; - data++; - B43_WARN_ON(value & ~0xFF); - b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); - break; - case B43_LPTAB_16BIT: - value = *((u16 *)data); - data += 2; - B43_WARN_ON(value & ~0xFFFF); - b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); - break; - case B43_LPTAB_32BIT: - value = *((u32 *)data); - data += 4; - b43_phy_write(dev, B43_LPPHY_TABLEDATAHI, value >> 16); - b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); - break; - default: - B43_WARN_ON(1); - } - } -} - -static const u8 lpphy_min_sig_sq_table[] = { - 0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xcf, 0xcd, - 0xca, 0xc7, 0xc4, 0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0x00, - 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, - 0xbe, 0xbe, 0xbe, 0xbe, 0xc1, 0xc4, 0xc7, 0xca, 0xcd, - 0xcf, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, -}; - -static const u16 lpphy_rev01_noise_scale_table[] = { - 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, - 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa400, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, - 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0x00a4, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4c00, 0x2d36, - 0x0000, 0x0000, 0x4c00, 0x2d36, -}; - -static const u16 lpphy_rev2plus_noise_scale_table[] = { - 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, - 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, - 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x0000, - 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, - 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, - 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, - 0x00a4, -}; - -static const u16 lpphy_crs_gain_nft_table[] = { - 0x0366, 0x036a, 0x036f, 0x0364, 0x0367, 0x036d, 0x0374, 0x037f, 0x036f, - 0x037b, 0x038a, 0x0378, 0x0367, 0x036d, 0x0375, 0x0381, 0x0374, 0x0381, - 0x0392, 0x03a9, 0x03c4, 0x03e1, 0x0001, 0x001f, 0x0040, 0x005e, 0x007f, - 0x009e, 0x00bd, 0x00dd, 0x00fd, 0x011d, 0x013d, -}; - -static const u16 lpphy_rev01_filter_control_table[] = { - 0xa0fc, 0x10fc, 0x10db, 0x20b7, 0xff93, 0x10bf, 0x109b, 0x2077, 0xff53, - 0x0127, -}; - -static const u32 lpphy_rev2plus_filter_control_table[] = { - 0x000141fc, 0x000021fc, 0x000021b7, 0x0000416f, 0x0001ff27, 0x0000217f, - 0x00002137, 0x000040ef, 0x0001fea7, 0x0000024f, -}; - -static const u32 lpphy_rev01_ps_control_table[] = { - 0x00010000, 0x000000a0, 0x00040000, 0x00000048, 0x08080101, 0x00000080, - 0x08080101, 0x00000040, 0x08080101, 0x000000c0, 0x08a81501, 0x000000c0, - 0x0fe8fd01, 0x000000c0, 0x08300105, 0x000000c0, 0x08080201, 0x000000c0, - 0x08280205, 0x000000c0, 0xe80802fe, 0x000000c7, 0x28080206, 0x000000c0, - 0x08080202, 0x000000c0, 0x0ba87602, 0x000000c0, 0x1068013d, 0x000000c0, - 0x10280105, 0x000000c0, 0x08880102, 0x000000c0, 0x08280106, 0x000000c0, - 0xe80801fd, 0x000000c7, 0xa8080115, 0x000000c0, -}; - -static const u32 lpphy_rev2plus_ps_control_table[] = { - 0x00e38e08, 0x00e08e38, 0x00000000, 0x00000000, 0x00000000, 0x00002080, - 0x00006180, 0x00003002, 0x00000040, 0x00002042, 0x00180047, 0x00080043, - 0x00000041, 0x000020c1, 0x00046006, 0x00042002, 0x00040000, 0x00002003, - 0x00180006, 0x00080002, -}; - -static const u8 lpphy_pll_fraction_table[] = { - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, -}; - -static const u16 lpphy_iqlo_cal_table[] = { - 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, - 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600, - 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, - 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const u16 lpphy_rev0_ofdm_cck_gain_table[] = { - 0x0001, 0x0001, 0x0001, 0x0001, 0x1001, 0x2001, 0x3001, 0x4001, 0x5001, - 0x6001, 0x7001, 0x7011, 0x7021, 0x2035, 0x2045, 0x2055, 0x2065, 0x2075, - 0x006d, 0x007d, 0x014d, 0x015d, 0x115d, 0x035d, 0x135d, 0x055d, 0x155d, - 0x0d5d, 0x1d5d, 0x2d5d, 0x555d, 0x655d, 0x755d, -}; - -static const u16 lpphy_rev1_ofdm_cck_gain_table[] = { - 0x5000, 0x6000, 0x7000, 0x0001, 0x1001, 0x2001, 0x3001, 0x4001, 0x5001, - 0x6001, 0x7001, 0x7011, 0x7021, 0x2035, 0x2045, 0x2055, 0x2065, 0x2075, - 0x006d, 0x007d, 0x014d, 0x015d, 0x115d, 0x035d, 0x135d, 0x055d, 0x155d, - 0x0d5d, 0x1d5d, 0x2d5d, 0x555d, 0x655d, 0x755d, -}; - -static const u16 lpphy_gain_delta_table[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const u32 lpphy_tx_power_control_table[] = { - 0x00000050, 0x0000004f, 0x0000004e, 0x0000004d, 0x0000004c, 0x0000004b, - 0x0000004a, 0x00000049, 0x00000048, 0x00000047, 0x00000046, 0x00000045, - 0x00000044, 0x00000043, 0x00000042, 0x00000041, 0x00000040, 0x0000003f, - 0x0000003e, 0x0000003d, 0x0000003c, 0x0000003b, 0x0000003a, 0x00000039, - 0x00000038, 0x00000037, 0x00000036, 0x00000035, 0x00000034, 0x00000033, - 0x00000032, 0x00000031, 0x00000030, 0x0000002f, 0x0000002e, 0x0000002d, - 0x0000002c, 0x0000002b, 0x0000002a, 0x00000029, 0x00000028, 0x00000027, - 0x00000026, 0x00000025, 0x00000024, 0x00000023, 0x00000022, 0x00000021, - 0x00000020, 0x0000001f, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001b, - 0x0000001a, 0x00000019, 0x00000018, 0x00000017, 0x00000016, 0x00000015, - 0x00000014, 0x00000013, 0x00000012, 0x00000011, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x000075a0, 0x000075a0, 0x000075a1, 0x000075a1, 0x000075a2, 0x000075a2, - 0x000075a3, 0x000075a3, 0x000074b0, 0x000074b0, 0x000074b1, 0x000074b1, - 0x000074b2, 0x000074b2, 0x000074b3, 0x000074b3, 0x00006d20, 0x00006d20, - 0x00006d21, 0x00006d21, 0x00006d22, 0x00006d22, 0x00006d23, 0x00006d23, - 0x00004660, 0x00004660, 0x00004661, 0x00004661, 0x00004662, 0x00004662, - 0x00004663, 0x00004663, 0x00003e60, 0x00003e60, 0x00003e61, 0x00003e61, - 0x00003e62, 0x00003e62, 0x00003e63, 0x00003e63, 0x00003660, 0x00003660, - 0x00003661, 0x00003661, 0x00003662, 0x00003662, 0x00003663, 0x00003663, - 0x00002e60, 0x00002e60, 0x00002e61, 0x00002e61, 0x00002e62, 0x00002e62, - 0x00002e63, 0x00002e63, 0x00002660, 0x00002660, 0x00002661, 0x00002661, - 0x00002662, 0x00002662, 0x00002663, 0x00002663, 0x000025e0, 0x000025e0, - 0x000025e1, 0x000025e1, 0x000025e2, 0x000025e2, 0x000025e3, 0x000025e3, - 0x00001de0, 0x00001de0, 0x00001de1, 0x00001de1, 0x00001de2, 0x00001de2, - 0x00001de3, 0x00001de3, 0x00001d60, 0x00001d60, 0x00001d61, 0x00001d61, - 0x00001d62, 0x00001d62, 0x00001d63, 0x00001d63, 0x00001560, 0x00001560, - 0x00001561, 0x00001561, 0x00001562, 0x00001562, 0x00001563, 0x00001563, - 0x00000d60, 0x00000d60, 0x00000d61, 0x00000d61, 0x00000d62, 0x00000d62, - 0x00000d63, 0x00000d63, 0x00000ce0, 0x00000ce0, 0x00000ce1, 0x00000ce1, - 0x00000ce2, 0x00000ce2, 0x00000ce3, 0x00000ce3, 0x00000e10, 0x00000e10, - 0x00000e11, 0x00000e11, 0x00000e12, 0x00000e12, 0x00000e13, 0x00000e13, - 0x00000bf0, 0x00000bf0, 0x00000bf1, 0x00000bf1, 0x00000bf2, 0x00000bf2, - 0x00000bf3, 0x00000bf3, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, - 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x000000ff, 0x000002fc, - 0x0000fa08, 0x00000305, 0x00000206, 0x00000304, 0x0000fb04, 0x0000fcff, - 0x000005fb, 0x0000fd01, 0x00000401, 0x00000006, 0x0000ff03, 0x000007fc, - 0x0000fc08, 0x00000203, 0x0000fffb, 0x00000600, 0x0000fa01, 0x0000fc03, - 0x0000fe06, 0x0000fe00, 0x00000102, 0x000007fd, 0x000004fb, 0x000006ff, - 0x000004fd, 0x0000fdfa, 0x000007fb, 0x0000fdfa, 0x0000fa06, 0x00000500, - 0x0000f902, 0x000007fa, 0x0000fafa, 0x00000500, 0x000007fa, 0x00000700, - 0x00000305, 0x000004ff, 0x00000801, 0x00000503, 0x000005f9, 0x00000404, - 0x0000fb08, 0x000005fd, 0x00000501, 0x00000405, 0x0000fb03, 0x000007fc, - 0x00000403, 0x00000303, 0x00000402, 0x0000faff, 0x0000fe05, 0x000005fd, - 0x0000fe01, 0x000007fa, 0x00000202, 0x00000504, 0x00000102, 0x000008fe, - 0x0000fa04, 0x0000fafc, 0x0000fe08, 0x000000f9, 0x000002fa, 0x000003fe, - 0x00000304, 0x000004f9, 0x00000100, 0x0000fd06, 0x000008fc, 0x00000701, - 0x00000504, 0x0000fdfe, 0x0000fdfc, 0x000003fe, 0x00000704, 0x000002fc, - 0x000004f9, 0x0000fdfd, 0x0000fa07, 0x00000205, 0x000003fd, 0x000005fb, - 0x000004f9, 0x00000804, 0x0000fc06, 0x0000fcf9, 0x00000100, 0x0000fe05, - 0x00000408, 0x0000fb02, 0x00000304, 0x000006fe, 0x000004fa, 0x00000305, - 0x000008fc, 0x00000102, 0x000001fd, 0x000004fc, 0x0000fe03, 0x00000701, - 0x000001fb, 0x000001f9, 0x00000206, 0x000006fd, 0x00000508, 0x00000700, - 0x00000304, 0x000005fe, 0x000005ff, 0x0000fa04, 0x00000303, 0x0000fefb, - 0x000007f9, 0x0000fefc, 0x000004fd, 0x000005fc, 0x0000fffd, 0x0000fc08, - 0x0000fbf9, 0x0000fd07, 0x000008fb, 0x0000fe02, 0x000006fb, 0x00000702, -}; - -static const u32 lpphy_gain_idx_table[] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x10000001, 0x00000000, 0x20000082, 0x00000000, 0x40000104, 0x00000000, - 0x60004207, 0x00000001, 0x7000838a, 0x00000001, 0xd021050d, 0x00000001, - 0xe041c683, 0x00000001, 0x50828805, 0x00000000, 0x80e34288, 0x00000000, - 0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000, 0x12064711, 0x00000001, - 0xb0a18612, 0x00000010, 0xe1024794, 0x00000010, 0x11630915, 0x00000011, - 0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018, 0xf1e50da0, 0x00000018, - 0x22468e21, 0x00000019, 0x4286d023, 0x00000019, 0xa347d0a4, 0x00000019, - 0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019, 0x0408d329, 0x0000001a, - 0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a, 0x54aa152c, 0x0000001a, - 0x64ca55ad, 0x0000001a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x10000001, 0x00000000, 0x20000082, 0x00000000, - 0x40000104, 0x00000000, 0x60004207, 0x00000001, 0x7000838a, 0x00000001, - 0xd021050d, 0x00000001, 0xe041c683, 0x00000001, 0x50828805, 0x00000000, - 0x80e34288, 0x00000000, 0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000, - 0x12064711, 0x00000001, 0xb0a18612, 0x00000010, 0xe1024794, 0x00000010, - 0x11630915, 0x00000011, 0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018, - 0xf1e50da0, 0x00000018, 0x22468e21, 0x00000019, 0x4286d023, 0x00000019, - 0xa347d0a4, 0x00000019, 0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019, - 0x0408d329, 0x0000001a, 0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a, - 0x54aa152c, 0x0000001a, 0x64ca55ad, 0x0000001a, -}; - -static const u16 lpphy_aux_gain_idx_table[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0001, 0x0002, 0x0004, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0016, -}; - -static const u32 lpphy_gain_value_table[] = { - 0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004, - 0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a, - 0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006, - 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000, - 0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f, - 0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000009, 0x000000f1, - 0x00000000, 0x00000000, -}; - -static const u16 lpphy_gain_table[] = { - 0x0000, 0x0400, 0x0800, 0x0802, 0x0804, 0x0806, 0x0807, 0x0808, 0x080a, - 0x080b, 0x080c, 0x080e, 0x080f, 0x0810, 0x0812, 0x0813, 0x0814, 0x0816, - 0x0817, 0x081a, 0x081b, 0x081f, 0x0820, 0x0824, 0x0830, 0x0834, 0x0837, - 0x083b, 0x083f, 0x0840, 0x0844, 0x0857, 0x085b, 0x085f, 0x08d7, 0x08db, - 0x08df, 0x0957, 0x095b, 0x095f, 0x0b57, 0x0b5b, 0x0b5f, 0x0f5f, 0x135f, - 0x175f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const u32 lpphy_a0_gain_idx_table[] = { - 0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065, - 0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f, - 0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca, - 0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e, - 0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193, - 0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a, - 0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325, - 0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471, - 0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646, - 0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd, - 0x00036963, 0x000339f2, 0x00030a89, 0x0002db28, -}; - -static const u16 lpphy_a0_aux_gain_idx_table[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0002, 0x0014, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0014, -}; - -static const u32 lpphy_a0_gain_value_table[] = { - 0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004, - 0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a, - 0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006, - 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000, - 0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f, - 0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000f, 0x000000f7, - 0x00000000, 0x00000000, -}; - -static const u16 lpphy_a0_gain_table[] = { - 0x0000, 0x0002, 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, - 0x000e, 0x000f, 0x0010, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x001a, - 0x001b, 0x001f, 0x0020, 0x0024, 0x0030, 0x0034, 0x0037, 0x003b, 0x003f, - 0x0040, 0x0044, 0x0057, 0x005b, 0x005f, 0x00d7, 0x00db, 0x00df, 0x0157, - 0x015b, 0x015f, 0x0357, 0x035b, 0x035f, 0x075f, 0x0b5f, 0x0f5f, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const u16 lpphy_sw_control_table[] = { - 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0128, - 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0009, 0x0009, - 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0018, 0x0018, 0x0018, - 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0128, 0x0128, 0x0009, 0x0009, - 0x0028, 0x0028, 0x0028, 0x0028, 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, - 0x0028, 0x0028, 0x0028, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, - 0x0009, 0x0009, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, - 0x0018, -}; - -static const u8 lpphy_hf_table[] = { - 0x4b, 0x36, 0x24, 0x18, 0x49, 0x34, 0x23, 0x17, 0x48, - 0x33, 0x23, 0x17, 0x48, 0x33, 0x23, 0x17, -}; - -static const u32 lpphy_papd_eps_table[] = { - 0x00000000, 0x00013ffc, 0x0001dff3, 0x0001bff0, 0x00023fe9, 0x00021fdf, - 0x00028fdf, 0x00033fd2, 0x00039fcb, 0x00043fc7, 0x0004efc2, 0x00055fb5, - 0x0005cfb0, 0x00063fa8, 0x00068fa3, 0x00071f98, 0x0007ef92, 0x00084f8b, - 0x0008df82, 0x00097f77, 0x0009df69, 0x000a3f62, 0x000adf57, 0x000b6f4c, - 0x000bff41, 0x000c9f39, 0x000cff30, 0x000dbf27, 0x000e4f1e, 0x000edf16, - 0x000f7f13, 0x00102f11, 0x00110f10, 0x0011df11, 0x0012ef15, 0x00143f1c, - 0x00158f27, 0x00172f35, 0x00193f47, 0x001baf5f, 0x001e6f7e, 0x0021cfa4, - 0x0025bfd2, 0x002a2008, 0x002fb047, 0x00360090, 0x003d40e0, 0x0045c135, - 0x004fb189, 0x005ae1d7, 0x0067221d, 0x0075025a, 0x007ff291, 0x007ff2bf, - 0x007ff2e3, 0x007ff2ff, 0x007ff315, 0x007ff329, 0x007ff33f, 0x007ff356, - 0x007ff36e, 0x007ff39c, 0x007ff441, 0x007ff506, -}; - -static const u32 lpphy_papd_mult_table[] = { - 0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065, - 0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f, - 0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca, - 0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e, - 0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193, - 0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a, - 0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325, - 0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471, - 0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646, - 0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd, - 0x00036963, 0x000339f2, 0x00030a89, 0x0002db28, -}; - -static struct lpphy_tx_gain_table_entry lpphy_rev0_nopa_tx_gain_table[] = { - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 152, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 147, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 143, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 139, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 135, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 131, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 128, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 124, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 121, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 117, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 114, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 111, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 107, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 104, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 101, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 99, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 96, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 93, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 90, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 88, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 85, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 83, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 81, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 78, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 76, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 74, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 56, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, -}; - -static struct lpphy_tx_gain_table_entry lpphy_rev0_2ghz_tx_gain_table[] = { - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 71, }, - { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 69, }, - { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 67, }, - { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 65, }, - { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 58, }, - { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 65, }, - { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 67, }, - { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 65, }, - { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 65, }, - { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 58, }, - { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 57, }, - { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 83, }, - { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 81, }, - { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 78, }, - { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 76, }, - { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 74, }, - { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 72, }, -}; - -static struct lpphy_tx_gain_table_entry lpphy_rev0_5ghz_tx_gain_table[] = { - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 99, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 96, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 93, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 55, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 56, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 55, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 56, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 73, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 56, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, -}; - -static struct lpphy_tx_gain_table_entry lpphy_rev1_nopa_tx_gain_table[] = { - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 152, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 147, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 143, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 139, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 135, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 131, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 128, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 124, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 121, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 117, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 114, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 111, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 107, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 104, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 101, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 99, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 96, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 93, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 90, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 88, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 85, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 83, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 81, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 78, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 76, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 74, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 56, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, -}; - -static struct lpphy_tx_gain_table_entry lpphy_rev1_2ghz_tx_gain_table[] = { - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 61, }, - { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 72, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 70, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 68, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 66, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 64, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 62, }, - { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 60, }, -}; - -static struct lpphy_tx_gain_table_entry lpphy_rev1_5ghz_tx_gain_table[] = { - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 99, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 96, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 93, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 55, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 56, }, - { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 55, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 56, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 73, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 56, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 58, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, - { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 57, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 62, }, - { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, -}; - -static struct lpphy_tx_gain_table_entry lpphy_rev2_nopa_tx_gain_table[] = { - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 152, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 147, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 143, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 139, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 135, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 131, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 128, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 124, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 121, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 117, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 114, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 111, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 107, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 104, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 101, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 99, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 96, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 93, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 90, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 88, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 85, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 83, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 81, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 78, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 76, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 74, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 72, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 70, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 68, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 66, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 197, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 192, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 186, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 181, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 176, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 171, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 166, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 161, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 157, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 152, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 148, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 144, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 140, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 136, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 132, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 128, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 124, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 121, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 117, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 114, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 111, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 108, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 105, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 102, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 99, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 96, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 93, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 91, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 88, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 86, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 83, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 81, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 79, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 76, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 74, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 72, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 70, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 68, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 66, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 64, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 248, .pad = 64, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 248, .pad = 62, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 241, .pad = 62, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 241, .pad = 60, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 234, .pad = 60, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 234, .pad = 59, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 227, .pad = 59, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 227, .pad = 57, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 221, .pad = 57, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 221, .pad = 55, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 215, .pad = 55, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 215, .pad = 54, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 208, .pad = 54, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 208, .pad = 52, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 203, .pad = 52, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 203, .pad = 51, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 197, .pad = 51, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 197, .pad = 49, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 191, .pad = 49, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 191, .pad = 48, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 186, .pad = 48, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 186, .pad = 47, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 181, .pad = 47, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 181, .pad = 45, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 175, .pad = 45, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 175, .pad = 44, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 170, .pad = 44, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 170, .pad = 43, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 166, .pad = 43, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 166, .pad = 42, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 161, .pad = 42, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 161, .pad = 40, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 156, .pad = 40, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 156, .pad = 39, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 152, .pad = 39, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 152, .pad = 38, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 148, .pad = 38, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 148, .pad = 37, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 143, .pad = 37, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 143, .pad = 36, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 139, .pad = 36, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 139, .pad = 35, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 135, .pad = 35, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 135, .pad = 34, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 132, .pad = 34, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 132, .pad = 33, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 128, .pad = 33, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 128, .pad = 32, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 124, .pad = 32, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 124, .pad = 31, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 121, .pad = 31, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 121, .pad = 30, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 117, .pad = 30, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 117, .pad = 29, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 114, .pad = 29, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 114, .pad = 29, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 111, .pad = 29, .dac = 0, .bb_mult = 64, }, -}; - -static struct lpphy_tx_gain_table_entry lpphy_rev2_2ghz_tx_gain_table[] = { - { .gm = 7, .pga = 99, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 96, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 93, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 90, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 88, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 85, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 83, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 81, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 78, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 76, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 74, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 72, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 70, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 68, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 66, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 62, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 62, .pad = 248, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 60, .pad = 248, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 60, .pad = 241, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 59, .pad = 241, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 59, .pad = 234, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 57, .pad = 234, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 57, .pad = 227, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 55, .pad = 227, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 55, .pad = 221, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 54, .pad = 221, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 54, .pad = 215, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 52, .pad = 215, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 52, .pad = 208, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 51, .pad = 208, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 51, .pad = 203, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 49, .pad = 203, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 49, .pad = 197, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 48, .pad = 197, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 48, .pad = 191, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 47, .pad = 191, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 47, .pad = 186, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 45, .pad = 186, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 45, .pad = 181, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 44, .pad = 181, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 44, .pad = 175, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 43, .pad = 175, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 43, .pad = 170, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 42, .pad = 170, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 42, .pad = 166, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 40, .pad = 166, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 40, .pad = 161, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 39, .pad = 161, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 39, .pad = 156, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 38, .pad = 156, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 38, .pad = 152, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 37, .pad = 152, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 37, .pad = 148, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 36, .pad = 148, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 36, .pad = 143, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 35, .pad = 143, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 35, .pad = 139, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 34, .pad = 139, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 34, .pad = 135, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 33, .pad = 135, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 33, .pad = 132, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 32, .pad = 132, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 32, .pad = 128, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 31, .pad = 128, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 31, .pad = 124, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 30, .pad = 124, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 30, .pad = 121, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 29, .pad = 121, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 29, .pad = 117, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 29, .pad = 117, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 29, .pad = 114, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 28, .pad = 114, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 28, .pad = 111, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 27, .pad = 111, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 27, .pad = 108, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 26, .pad = 108, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 26, .pad = 104, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 25, .pad = 104, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 25, .pad = 102, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 25, .pad = 102, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 25, .pad = 99, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 24, .pad = 99, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 24, .pad = 96, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 23, .pad = 96, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 23, .pad = 93, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 23, .pad = 93, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 23, .pad = 90, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 22, .pad = 90, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 22, .pad = 88, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 21, .pad = 88, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 21, .pad = 85, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 21, .pad = 85, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 21, .pad = 83, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 20, .pad = 83, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 20, .pad = 81, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 20, .pad = 81, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 20, .pad = 78, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 19, .pad = 78, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 19, .pad = 76, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 19, .pad = 76, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 19, .pad = 74, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 18, .pad = 74, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 18, .pad = 72, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 18, .pad = 72, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 18, .pad = 70, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 17, .pad = 70, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 17, .pad = 68, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 17, .pad = 68, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 17, .pad = 66, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 16, .pad = 66, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 16, .pad = 64, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 16, .pad = 64, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 16, .pad = 62, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 62, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 60, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 60, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 15, .pad = 59, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 14, .pad = 59, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 14, .pad = 57, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 14, .pad = 57, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 14, .pad = 55, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 14, .pad = 55, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 14, .pad = 54, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 13, .pad = 54, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 13, .pad = 52, .dac = 0, .bb_mult = 64, }, - { .gm = 7, .pga = 13, .pad = 52, .dac = 0, .bb_mult = 64, }, -}; - -static struct lpphy_tx_gain_table_entry lpphy_rev2_5ghz_tx_gain_table[] = { - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 152, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 147, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 143, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 139, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 135, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 131, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 128, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 124, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 121, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 117, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 114, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 111, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 107, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 104, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 101, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 99, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 96, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 93, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 90, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 88, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 85, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 83, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 81, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 78, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 76, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 74, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 72, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 70, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 68, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 66, }, - { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 248, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 241, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 234, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 227, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 221, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 215, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 208, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 197, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 191, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 186, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 181, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 175, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 170, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 166, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 161, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 156, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 152, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 148, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 143, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 139, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 135, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 132, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 128, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 124, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 121, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 117, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 114, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 111, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 108, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 104, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 102, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 99, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 96, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 93, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 90, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 88, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 85, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 83, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 81, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 78, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 76, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 74, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 72, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 70, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 68, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 66, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 64, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 64, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 255, .pad = 62, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 248, .pad = 62, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 248, .pad = 60, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 241, .pad = 60, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 241, .pad = 59, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 234, .pad = 59, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 234, .pad = 57, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 227, .pad = 57, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 227, .pad = 55, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 221, .pad = 55, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 221, .pad = 54, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 215, .pad = 54, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 215, .pad = 52, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 208, .pad = 52, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 208, .pad = 51, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 203, .pad = 51, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 203, .pad = 49, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 197, .pad = 49, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 197, .pad = 48, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 191, .pad = 48, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 191, .pad = 47, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 186, .pad = 47, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 186, .pad = 45, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 181, .pad = 45, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 181, .pad = 44, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 175, .pad = 44, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 175, .pad = 43, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 170, .pad = 43, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 170, .pad = 42, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 166, .pad = 42, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 166, .pad = 40, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 161, .pad = 40, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 161, .pad = 39, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 156, .pad = 39, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 156, .pad = 38, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 152, .pad = 38, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 152, .pad = 37, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 148, .pad = 37, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 148, .pad = 36, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 143, .pad = 36, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 143, .pad = 35, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 139, .pad = 35, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 139, .pad = 34, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 135, .pad = 34, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 135, .pad = 33, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 132, .pad = 33, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 132, .pad = 32, .dac = 0, .bb_mult = 64, }, - { .gm = 255, .pga = 128, .pad = 32, .dac = 0, .bb_mult = 64, }, -}; - -void lpphy_rev0_1_table_init(struct b43_wldev *dev) -{ - B43_WARN_ON(dev->phy.rev >= 2); - - b43_lptab_write_bulk(dev, B43_LPTAB8(2, 0), - ARRAY_SIZE(lpphy_min_sig_sq_table), lpphy_min_sig_sq_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(1, 0), - ARRAY_SIZE(lpphy_rev01_noise_scale_table), lpphy_rev01_noise_scale_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), - ARRAY_SIZE(lpphy_crs_gain_nft_table), lpphy_crs_gain_nft_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(8, 0), - ARRAY_SIZE(lpphy_rev01_filter_control_table), lpphy_rev01_filter_control_table); - b43_lptab_write_bulk(dev, B43_LPTAB32(9, 0), - ARRAY_SIZE(lpphy_rev01_ps_control_table), lpphy_rev01_ps_control_table); - b43_lptab_write_bulk(dev, B43_LPTAB8(6, 0), - ARRAY_SIZE(lpphy_pll_fraction_table), lpphy_pll_fraction_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(0, 0), - ARRAY_SIZE(lpphy_iqlo_cal_table), lpphy_iqlo_cal_table); - if (dev->phy.rev == 0) { - b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), - ARRAY_SIZE(lpphy_rev0_ofdm_cck_gain_table), lpphy_rev0_ofdm_cck_gain_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), - ARRAY_SIZE(lpphy_rev0_ofdm_cck_gain_table), lpphy_rev0_ofdm_cck_gain_table); - } else { - b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), - ARRAY_SIZE(lpphy_rev1_ofdm_cck_gain_table), lpphy_rev1_ofdm_cck_gain_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), - ARRAY_SIZE(lpphy_rev1_ofdm_cck_gain_table), lpphy_rev1_ofdm_cck_gain_table); -} - b43_lptab_write_bulk(dev, B43_LPTAB16(15, 0), - ARRAY_SIZE(lpphy_gain_delta_table), lpphy_gain_delta_table); - b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0), - ARRAY_SIZE(lpphy_tx_power_control_table), lpphy_tx_power_control_table); -} - -void lpphy_rev2plus_table_init(struct b43_wldev *dev) -{ - int i; - - B43_WARN_ON(dev->phy.rev < 2); - - for (i = 0; i < 704; i++) - b43_lptab_write(dev, B43_LPTAB32(7, i), 0); - - b43_lptab_write_bulk(dev, B43_LPTAB8(2, 0), - ARRAY_SIZE(lpphy_min_sig_sq_table), lpphy_min_sig_sq_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(1, 0), - ARRAY_SIZE(lpphy_rev2plus_noise_scale_table), lpphy_rev2plus_noise_scale_table); - b43_lptab_write_bulk(dev, B43_LPTAB32(11, 0), - ARRAY_SIZE(lpphy_rev2plus_filter_control_table), lpphy_rev2plus_filter_control_table); - b43_lptab_write_bulk(dev, B43_LPTAB32(12, 0), - ARRAY_SIZE(lpphy_rev2plus_ps_control_table), lpphy_rev2plus_ps_control_table); - b43_lptab_write_bulk(dev, B43_LPTAB32(13, 0), - ARRAY_SIZE(lpphy_gain_idx_table), lpphy_gain_idx_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), - ARRAY_SIZE(lpphy_aux_gain_idx_table), lpphy_aux_gain_idx_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(15, 0), - ARRAY_SIZE(lpphy_sw_control_table), lpphy_sw_control_table); - b43_lptab_write_bulk(dev, B43_LPTAB8(16, 0), - ARRAY_SIZE(lpphy_hf_table), lpphy_hf_table); - b43_lptab_write_bulk(dev, B43_LPTAB32(17, 0), - ARRAY_SIZE(lpphy_gain_value_table), lpphy_gain_value_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(18, 0), - ARRAY_SIZE(lpphy_gain_table), lpphy_gain_table); - b43_lptab_write_bulk(dev, B43_LPTAB8(6, 0), - ARRAY_SIZE(lpphy_pll_fraction_table), lpphy_pll_fraction_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(0, 0), - ARRAY_SIZE(lpphy_iqlo_cal_table), lpphy_iqlo_cal_table); - b43_lptab_write_bulk(dev, B43_LPTAB32(9, 0), - ARRAY_SIZE(lpphy_papd_eps_table), lpphy_papd_eps_table); - b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0), - ARRAY_SIZE(lpphy_papd_mult_table), lpphy_papd_mult_table); - - if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { - b43_lptab_write_bulk(dev, B43_LPTAB32(13, 0), - ARRAY_SIZE(lpphy_a0_gain_idx_table), lpphy_a0_gain_idx_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), - ARRAY_SIZE(lpphy_a0_aux_gain_idx_table), lpphy_a0_aux_gain_idx_table); - b43_lptab_write_bulk(dev, B43_LPTAB32(17, 0), - ARRAY_SIZE(lpphy_a0_gain_value_table), lpphy_a0_gain_value_table); - b43_lptab_write_bulk(dev, B43_LPTAB16(18, 0), - ARRAY_SIZE(lpphy_a0_gain_table), lpphy_a0_gain_table); - } -} - -static void lpphy_rev0_1_write_gain_table(struct b43_wldev *dev, int offset, - struct lpphy_tx_gain_table_entry data) -{ - u32 tmp; - - B43_WARN_ON(dev->phy.rev >= 2); - - tmp = data.pad << 11; - tmp |= data.pga << 7; - tmp |= data.gm << 4; - tmp |= data.dac; - b43_lptab_write(dev, B43_LPTAB32(10, 0xC0 + offset), tmp); - tmp = data.bb_mult << 20; - b43_lptab_write(dev, B43_LPTAB32(10, 0x140 + offset), tmp); -} - -static void lpphy_rev2plus_write_gain_table(struct b43_wldev *dev, int offset, - struct lpphy_tx_gain_table_entry data) -{ - u32 tmp; - - B43_WARN_ON(dev->phy.rev < 2); - - tmp = data.pad << 16; - tmp |= data.pga << 8; - tmp |= data.gm; - if (dev->phy.rev >= 3) { - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - tmp |= 0x10 << 24; - else - tmp |= 0x70 << 24; - } else { - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - tmp |= 0x14 << 24; - else - tmp |= 0x7F << 24; - } - b43_lptab_write(dev, B43_LPTAB32(7, 0xC0 + offset), tmp); - tmp = data.bb_mult << 20; - tmp |= data.dac << 28; - b43_lptab_write(dev, B43_LPTAB32(7, 0x140 + offset), tmp); -} - -void lpphy_write_gain_table(struct b43_wldev *dev, int offset, - struct lpphy_tx_gain_table_entry data) -{ - if (dev->phy.rev >= 2) - lpphy_rev2plus_write_gain_table(dev, offset, data); - else - lpphy_rev0_1_write_gain_table(dev, offset, data); -} - -void lpphy_write_gain_table_bulk(struct b43_wldev *dev, int offset, int count, - struct lpphy_tx_gain_table_entry *table) -{ - int i; - - for (i = offset; i < count; i++) - lpphy_write_gain_table(dev, i, table[i]); -} - -void lpphy_init_tx_gain_table(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - - switch (dev->phy.rev) { - case 0: - if ((sprom->boardflags_hi & B43_BFH_NOPA) || - (sprom->boardflags_lo & B43_BFL_HGPA)) - lpphy_write_gain_table_bulk(dev, 0, 128, - lpphy_rev0_nopa_tx_gain_table); - else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - lpphy_write_gain_table_bulk(dev, 0, 128, - lpphy_rev0_2ghz_tx_gain_table); - else - lpphy_write_gain_table_bulk(dev, 0, 128, - lpphy_rev0_5ghz_tx_gain_table); - break; - case 1: - if ((sprom->boardflags_hi & B43_BFH_NOPA) || - (sprom->boardflags_lo & B43_BFL_HGPA)) - lpphy_write_gain_table_bulk(dev, 0, 128, - lpphy_rev1_nopa_tx_gain_table); - else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - lpphy_write_gain_table_bulk(dev, 0, 128, - lpphy_rev1_2ghz_tx_gain_table); - else - lpphy_write_gain_table_bulk(dev, 0, 128, - lpphy_rev1_5ghz_tx_gain_table); - break; - default: - if (sprom->boardflags_hi & B43_BFH_NOPA) - lpphy_write_gain_table_bulk(dev, 0, 128, - lpphy_rev2_nopa_tx_gain_table); - else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - lpphy_write_gain_table_bulk(dev, 0, 128, - lpphy_rev2_2ghz_tx_gain_table); - else - lpphy_write_gain_table_bulk(dev, 0, 128, - lpphy_rev2_5ghz_tx_gain_table); - } -} diff --git a/drivers/net/wireless/b43/tables_lpphy.h b/drivers/net/wireless/b43/tables_lpphy.h deleted file mode 100644 index 84f1d265f657..000000000000 --- a/drivers/net/wireless/b43/tables_lpphy.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef B43_TABLES_LPPHY_H_ -#define B43_TABLES_LPPHY_H_ - - -#define B43_LPTAB_TYPEMASK 0xF0000000 -#define B43_LPTAB_8BIT 0x10000000 -#define B43_LPTAB_16BIT 0x20000000 -#define B43_LPTAB_32BIT 0x30000000 -#define B43_LPTAB8(table, offset) (((table) << 10) | (offset) | B43_LPTAB_8BIT) -#define B43_LPTAB16(table, offset) (((table) << 10) | (offset) | B43_LPTAB_16BIT) -#define B43_LPTAB32(table, offset) (((table) << 10) | (offset) | B43_LPTAB_32BIT) - -/* Table definitions */ -#define B43_LPTAB_TXPWR_R2PLUS B43_LPTAB32(0x07, 0) /* TX power lookup table (rev >= 2) */ -#define B43_LPTAB_TXPWR_R0_1 B43_LPTAB32(0xA0, 0) /* TX power lookup table (rev < 2) */ - -u32 b43_lptab_read(struct b43_wldev *dev, u32 offset); -void b43_lptab_write(struct b43_wldev *dev, u32 offset, u32 value); - -/* Bulk table access. Note that these functions return the bulk data in - * host endianness! The returned data is _not_ a bytearray, but an array - * consisting of nr_elements of the data type. */ -void b43_lptab_read_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, void *data); -void b43_lptab_write_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, const void *data); - -void b2062_upload_init_table(struct b43_wldev *dev); -void b2063_upload_init_table(struct b43_wldev *dev); - -struct lpphy_tx_gain_table_entry { - u8 gm, pga, pad, dac, bb_mult; -}; - -void lpphy_write_gain_table(struct b43_wldev *dev, int offset, - struct lpphy_tx_gain_table_entry data); -void lpphy_write_gain_table_bulk(struct b43_wldev *dev, int offset, int count, - struct lpphy_tx_gain_table_entry *table); - -void lpphy_rev0_1_table_init(struct b43_wldev *dev); -void lpphy_rev2plus_table_init(struct b43_wldev *dev); -void lpphy_init_tx_gain_table(struct b43_wldev *dev); - -#endif /* B43_TABLES_LPPHY_H_ */ diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c deleted file mode 100644 index b2f0d245bcf3..000000000000 --- a/drivers/net/wireless/b43/tables_nphy.c +++ /dev/null @@ -1,3878 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11n PHY data tables - - Copyright (c) 2008 Michael Buesch - Copyright (c) 2010 RafaÅ‚ MiÅ‚ecki - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "tables_nphy.h" -#include "phy_common.h" -#include "phy_n.h" - -static const u8 b43_ntab_adjustpower0[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static const u8 b43_ntab_adjustpower1[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static const u16 b43_ntab_bdi[] = { - 0x0070, 0x0126, 0x012C, 0x0246, 0x048D, 0x04D2, -}; - -static const u32 b43_ntab_channelest[] = { - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, -}; - -static const u8 b43_ntab_estimatepowerlt0[] = { - 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, - 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, - 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, - 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, - 0x30, 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, - 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, - 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, - 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, -}; - -static const u8 b43_ntab_estimatepowerlt1[] = { - 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, - 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, - 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, - 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, - 0x30, 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, - 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, - 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, - 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, -}; - -static const u8 b43_ntab_framelookup[] = { - 0x02, 0x04, 0x14, 0x14, 0x03, 0x05, 0x16, 0x16, - 0x0A, 0x0C, 0x1C, 0x1C, 0x0B, 0x0D, 0x1E, 0x1E, - 0x06, 0x08, 0x18, 0x18, 0x07, 0x09, 0x1A, 0x1A, - 0x0E, 0x10, 0x20, 0x28, 0x0F, 0x11, 0x22, 0x2A, -}; - -static const u32 b43_ntab_framestruct[] = { - 0x08004A04, 0x00100000, 0x01000A05, 0x00100020, - 0x09804506, 0x00100030, 0x09804507, 0x00100030, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x08004A0C, 0x00100004, 0x01000A0D, 0x00100024, - 0x0980450E, 0x00100034, 0x0980450F, 0x00100034, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000A04, 0x00100000, 0x11008A05, 0x00100020, - 0x1980C506, 0x00100030, 0x21810506, 0x00100030, - 0x21810506, 0x00100030, 0x01800504, 0x00100030, - 0x11808505, 0x00100030, 0x29814507, 0x01100030, - 0x00000A04, 0x00100000, 0x11008A05, 0x00100020, - 0x21810506, 0x00100030, 0x21810506, 0x00100030, - 0x29814507, 0x01100030, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000A0C, 0x00100008, 0x11008A0D, 0x00100028, - 0x1980C50E, 0x00100038, 0x2181050E, 0x00100038, - 0x2181050E, 0x00100038, 0x0180050C, 0x00100038, - 0x1180850D, 0x00100038, 0x2981450F, 0x01100038, - 0x00000A0C, 0x00100008, 0x11008A0D, 0x00100028, - 0x2181050E, 0x00100038, 0x2181050E, 0x00100038, - 0x2981450F, 0x01100038, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x08004A04, 0x00100000, 0x01000A05, 0x00100020, - 0x1980C506, 0x00100030, 0x1980C506, 0x00100030, - 0x11808504, 0x00100030, 0x3981CA05, 0x00100030, - 0x29814507, 0x01100030, 0x00000000, 0x00000000, - 0x10008A04, 0x00100000, 0x3981CA05, 0x00100030, - 0x1980C506, 0x00100030, 0x29814507, 0x01100030, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x08004A0C, 0x00100008, 0x01000A0D, 0x00100028, - 0x1980C50E, 0x00100038, 0x1980C50E, 0x00100038, - 0x1180850C, 0x00100038, 0x3981CA0D, 0x00100038, - 0x2981450F, 0x01100038, 0x00000000, 0x00000000, - 0x10008A0C, 0x00100008, 0x3981CA0D, 0x00100038, - 0x1980C50E, 0x00100038, 0x2981450F, 0x01100038, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x40021404, 0x00100000, 0x02001405, 0x00100040, - 0x0B004A06, 0x01900060, 0x13008A06, 0x01900060, - 0x13008A06, 0x01900060, 0x43020A04, 0x00100060, - 0x1B00CA05, 0x00100060, 0x23010A07, 0x01500060, - 0x40021404, 0x00100000, 0x1A00D405, 0x00100040, - 0x13008A06, 0x01900060, 0x13008A06, 0x01900060, - 0x23010A07, 0x01500060, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140C, 0x00100010, 0x0200140D, 0x00100050, - 0x0B004A0E, 0x01900070, 0x13008A0E, 0x01900070, - 0x13008A0E, 0x01900070, 0x43020A0C, 0x00100070, - 0x1B00CA0D, 0x00100070, 0x23010A0F, 0x01500070, - 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, - 0x13008A0E, 0x01900070, 0x13008A0E, 0x01900070, - 0x23010A0F, 0x01500070, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x50029404, 0x00100000, 0x32019405, 0x00100040, - 0x0B004A06, 0x01900060, 0x0B004A06, 0x01900060, - 0x5B02CA04, 0x00100060, 0x3B01D405, 0x00100060, - 0x23010A07, 0x01500060, 0x00000000, 0x00000000, - 0x5802D404, 0x00100000, 0x3B01D405, 0x00100060, - 0x0B004A06, 0x01900060, 0x23010A07, 0x01500060, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x5002940C, 0x00100010, 0x3201940D, 0x00100050, - 0x0B004A0E, 0x01900070, 0x0B004A0E, 0x01900070, - 0x5B02CA0C, 0x00100070, 0x3B01D40D, 0x00100070, - 0x23010A0F, 0x01500070, 0x00000000, 0x00000000, - 0x5802D40C, 0x00100010, 0x3B01D40D, 0x00100070, - 0x0B004A0E, 0x01900070, 0x23010A0F, 0x01500070, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x40021404, 0x000F4800, 0x62031405, 0x00100040, - 0x53028A06, 0x01900060, 0x53028A07, 0x01900060, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140C, 0x000F4808, 0x6203140D, 0x00100048, - 0x53028A0E, 0x01900068, 0x53028A0F, 0x01900068, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000A0C, 0x00100004, 0x11008A0D, 0x00100024, - 0x1980C50E, 0x00100034, 0x2181050E, 0x00100034, - 0x2181050E, 0x00100034, 0x0180050C, 0x00100038, - 0x1180850D, 0x00100038, 0x1181850D, 0x00100038, - 0x2981450F, 0x01100038, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000A0C, 0x00100008, 0x11008A0D, 0x00100028, - 0x2181050E, 0x00100038, 0x2181050E, 0x00100038, - 0x1181850D, 0x00100038, 0x2981450F, 0x01100038, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x08004A04, 0x00100000, 0x01000A05, 0x00100020, - 0x0180C506, 0x00100030, 0x0180C506, 0x00100030, - 0x2180C50C, 0x00100030, 0x49820A0D, 0x0016A130, - 0x41824A0D, 0x0016A130, 0x2981450F, 0x01100030, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x2000CA0C, 0x00100000, 0x49820A0D, 0x0016A130, - 0x1980C50E, 0x00100030, 0x41824A0D, 0x0016A130, - 0x2981450F, 0x01100030, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140C, 0x00100008, 0x0200140D, 0x00100048, - 0x0B004A0E, 0x01900068, 0x13008A0E, 0x01900068, - 0x13008A0E, 0x01900068, 0x43020A0C, 0x00100070, - 0x1B00CA0D, 0x00100070, 0x1B014A0D, 0x00100070, - 0x23010A0F, 0x01500070, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, - 0x13008A0E, 0x01900070, 0x13008A0E, 0x01900070, - 0x1B014A0D, 0x00100070, 0x23010A0F, 0x01500070, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x50029404, 0x00100000, 0x32019405, 0x00100040, - 0x03004A06, 0x01900060, 0x03004A06, 0x01900060, - 0x6B030A0C, 0x00100060, 0x4B02140D, 0x0016A160, - 0x4302540D, 0x0016A160, 0x23010A0F, 0x01500060, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x6B03140C, 0x00100060, 0x4B02140D, 0x0016A160, - 0x0B004A0E, 0x01900060, 0x4302540D, 0x0016A160, - 0x23010A0F, 0x01500060, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x40021404, 0x00100000, 0x1A00D405, 0x00100040, - 0x53028A06, 0x01900060, 0x5B02CA06, 0x01900060, - 0x5B02CA06, 0x01900060, 0x43020A04, 0x00100060, - 0x1B00CA05, 0x00100060, 0x53028A07, 0x0190C060, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, - 0x53028A0E, 0x01900070, 0x5B02CA0E, 0x01900070, - 0x5B02CA0E, 0x01900070, 0x43020A0C, 0x00100070, - 0x1B00CA0D, 0x00100070, 0x53028A0F, 0x0190C070, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x40021404, 0x00100000, 0x1A00D405, 0x00100040, - 0x5B02CA06, 0x01900060, 0x5B02CA06, 0x01900060, - 0x53028A07, 0x0190C060, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, - 0x5B02CA0E, 0x01900070, 0x5B02CA0E, 0x01900070, - 0x53028A0F, 0x0190C070, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 b43_ntab_gainctl0[] = { - 0x03CC2B44, 0x03CC2B42, 0x03CC2B40, 0x03CC2B3E, - 0x03CC2B3D, 0x03CC2B3B, 0x03C82B44, 0x03C82B42, - 0x03C82B40, 0x03C82B3E, 0x03C82B3D, 0x03C82B3B, - 0x03C82B39, 0x03C82B38, 0x03C82B36, 0x03C82B34, - 0x03C42B44, 0x03C42B42, 0x03C42B40, 0x03C42B3E, - 0x03C42B3D, 0x03C42B3B, 0x03C42B39, 0x03C42B38, - 0x03C42B36, 0x03C42B34, 0x03C42B33, 0x03C42B32, - 0x03C42B30, 0x03C42B2F, 0x03C42B2D, 0x03C02B44, - 0x03C02B42, 0x03C02B40, 0x03C02B3E, 0x03C02B3D, - 0x03C02B3B, 0x03C02B39, 0x03C02B38, 0x03C02B36, - 0x03C02B34, 0x03B02B44, 0x03B02B42, 0x03B02B40, - 0x03B02B3E, 0x03B02B3D, 0x03B02B3B, 0x03B02B39, - 0x03B02B38, 0x03B02B36, 0x03B02B34, 0x03B02B33, - 0x03B02B32, 0x03B02B30, 0x03B02B2F, 0x03B02B2D, - 0x03A02B44, 0x03A02B42, 0x03A02B40, 0x03A02B3E, - 0x03A02B3D, 0x03A02B3B, 0x03A02B39, 0x03A02B38, - 0x03A02B36, 0x03A02B34, 0x03902B44, 0x03902B42, - 0x03902B40, 0x03902B3E, 0x03902B3D, 0x03902B3B, - 0x03902B39, 0x03902B38, 0x03902B36, 0x03902B34, - 0x03902B33, 0x03902B32, 0x03902B30, 0x03802B44, - 0x03802B42, 0x03802B40, 0x03802B3E, 0x03802B3D, - 0x03802B3B, 0x03802B39, 0x03802B38, 0x03802B36, - 0x03802B34, 0x03802B33, 0x03802B32, 0x03802B30, - 0x03802B2F, 0x03802B2D, 0x03802B2C, 0x03802B2B, - 0x03802B2A, 0x03802B29, 0x03802B27, 0x03802B26, - 0x03802B25, 0x03802B24, 0x03802B23, 0x03802B22, - 0x03802B21, 0x03802B20, 0x03802B1F, 0x03802B1E, - 0x03802B1E, 0x03802B1D, 0x03802B1C, 0x03802B1B, - 0x03802B1A, 0x03802B1A, 0x03802B19, 0x03802B18, - 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, - 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, - 0x03802B18, 0x03802B18, 0x03802B18, 0x00002B00, -}; - -static const u32 b43_ntab_gainctl1[] = { - 0x03CC2B44, 0x03CC2B42, 0x03CC2B40, 0x03CC2B3E, - 0x03CC2B3D, 0x03CC2B3B, 0x03C82B44, 0x03C82B42, - 0x03C82B40, 0x03C82B3E, 0x03C82B3D, 0x03C82B3B, - 0x03C82B39, 0x03C82B38, 0x03C82B36, 0x03C82B34, - 0x03C42B44, 0x03C42B42, 0x03C42B40, 0x03C42B3E, - 0x03C42B3D, 0x03C42B3B, 0x03C42B39, 0x03C42B38, - 0x03C42B36, 0x03C42B34, 0x03C42B33, 0x03C42B32, - 0x03C42B30, 0x03C42B2F, 0x03C42B2D, 0x03C02B44, - 0x03C02B42, 0x03C02B40, 0x03C02B3E, 0x03C02B3D, - 0x03C02B3B, 0x03C02B39, 0x03C02B38, 0x03C02B36, - 0x03C02B34, 0x03B02B44, 0x03B02B42, 0x03B02B40, - 0x03B02B3E, 0x03B02B3D, 0x03B02B3B, 0x03B02B39, - 0x03B02B38, 0x03B02B36, 0x03B02B34, 0x03B02B33, - 0x03B02B32, 0x03B02B30, 0x03B02B2F, 0x03B02B2D, - 0x03A02B44, 0x03A02B42, 0x03A02B40, 0x03A02B3E, - 0x03A02B3D, 0x03A02B3B, 0x03A02B39, 0x03A02B38, - 0x03A02B36, 0x03A02B34, 0x03902B44, 0x03902B42, - 0x03902B40, 0x03902B3E, 0x03902B3D, 0x03902B3B, - 0x03902B39, 0x03902B38, 0x03902B36, 0x03902B34, - 0x03902B33, 0x03902B32, 0x03902B30, 0x03802B44, - 0x03802B42, 0x03802B40, 0x03802B3E, 0x03802B3D, - 0x03802B3B, 0x03802B39, 0x03802B38, 0x03802B36, - 0x03802B34, 0x03802B33, 0x03802B32, 0x03802B30, - 0x03802B2F, 0x03802B2D, 0x03802B2C, 0x03802B2B, - 0x03802B2A, 0x03802B29, 0x03802B27, 0x03802B26, - 0x03802B25, 0x03802B24, 0x03802B23, 0x03802B22, - 0x03802B21, 0x03802B20, 0x03802B1F, 0x03802B1E, - 0x03802B1E, 0x03802B1D, 0x03802B1C, 0x03802B1B, - 0x03802B1A, 0x03802B1A, 0x03802B19, 0x03802B18, - 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, - 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, - 0x03802B18, 0x03802B18, 0x03802B18, 0x00002B00, -}; - -static const u32 b43_ntab_intlevel[] = { - 0x00802070, 0x0671188D, 0x0A60192C, 0x0A300E46, - 0x00C1188D, 0x080024D2, 0x00000070, -}; - -static const u32 b43_ntab_iqlt0[] = { - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, -}; - -static const u32 b43_ntab_iqlt1[] = { - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, - 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, -}; - -static const u16 b43_ntab_loftlt0[] = { - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, -}; - -static const u16 b43_ntab_loftlt1[] = { - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, - 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, - 0x0002, 0x0103, -}; - -static const u8 b43_ntab_mcs[] = { - 0x00, 0x08, 0x0A, 0x10, 0x12, 0x19, 0x1A, 0x1C, - 0x40, 0x48, 0x4A, 0x50, 0x52, 0x59, 0x5A, 0x5C, - 0x80, 0x88, 0x8A, 0x90, 0x92, 0x99, 0x9A, 0x9C, - 0xC0, 0xC8, 0xCA, 0xD0, 0xD2, 0xD9, 0xDA, 0xDC, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x04, 0x08, 0x09, 0x0A, 0x0C, - 0x10, 0x11, 0x12, 0x14, 0x18, 0x19, 0x1A, 0x1C, - 0x20, 0x21, 0x22, 0x24, 0x40, 0x41, 0x42, 0x44, - 0x48, 0x49, 0x4A, 0x4C, 0x50, 0x51, 0x52, 0x54, - 0x58, 0x59, 0x5A, 0x5C, 0x60, 0x61, 0x62, 0x64, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static const u32 b43_ntab_noisevar10[] = { - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, -}; - -static const u32 b43_ntab_noisevar11[] = { - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, - 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, -}; - -static const u16 b43_ntab_pilot[] = { - 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, - 0xFF08, 0xFF08, 0x80D5, 0x80D5, 0x80D5, 0x80D5, - 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0xFF0A, 0xFF82, - 0xFFA0, 0xFF28, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFF82, 0xFFA0, 0xFF28, 0xFF0A, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xF83F, 0xFA1F, 0xFA97, 0xFAB5, - 0xF2BD, 0xF0BF, 0xFFFF, 0xFFFF, 0xF017, 0xF815, - 0xF215, 0xF095, 0xF035, 0xF01D, 0xFFFF, 0xFFFF, - 0xFF08, 0xFF02, 0xFF80, 0xFF20, 0xFF08, 0xFF02, - 0xFF80, 0xFF20, 0xF01F, 0xF817, 0xFA15, 0xF295, - 0xF0B5, 0xF03D, 0xFFFF, 0xFFFF, 0xF82A, 0xFA0A, - 0xFA82, 0xFAA0, 0xF2A8, 0xF0AA, 0xFFFF, 0xFFFF, - 0xF002, 0xF800, 0xF200, 0xF080, 0xF020, 0xF008, - 0xFFFF, 0xFFFF, 0xF00A, 0xF802, 0xFA00, 0xF280, - 0xF0A0, 0xF028, 0xFFFF, 0xFFFF, -}; - -static const u32 b43_ntab_pilotlt[] = { - 0x76540123, 0x62407351, 0x76543201, 0x76540213, - 0x76540123, 0x76430521, -}; - -static const u32 b43_ntab_tdi20a0[] = { - 0x00091226, 0x000A1429, 0x000B56AD, 0x000C58B0, - 0x000D5AB3, 0x000E9CB6, 0x000F9EBA, 0x0000C13D, - 0x00020301, 0x00030504, 0x00040708, 0x0005090B, - 0x00064B8E, 0x00095291, 0x000A5494, 0x000B9718, - 0x000C9927, 0x000D9B2A, 0x000EDD2E, 0x000FDF31, - 0x000101B4, 0x000243B7, 0x000345BB, 0x000447BE, - 0x00058982, 0x00068C05, 0x00099309, 0x000A950C, - 0x000BD78F, 0x000CD992, 0x000DDB96, 0x000F1D99, - 0x00005FA8, 0x0001422C, 0x0002842F, 0x00038632, - 0x00048835, 0x0005CA38, 0x0006CCBC, 0x0009D3BF, - 0x000B1603, 0x000C1806, 0x000D1A0A, 0x000E1C0D, - 0x000F5E10, 0x00008093, 0x00018297, 0x0002C49A, - 0x0003C680, 0x0004C880, 0x00060B00, 0x00070D00, - 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 b43_ntab_tdi20a1[] = { - 0x00014B26, 0x00028D29, 0x000393AD, 0x00049630, - 0x0005D833, 0x0006DA36, 0x00099C3A, 0x000A9E3D, - 0x000BC081, 0x000CC284, 0x000DC488, 0x000F068B, - 0x0000488E, 0x00018B91, 0x0002D214, 0x0003D418, - 0x0004D6A7, 0x000618AA, 0x00071AAE, 0x0009DCB1, - 0x000B1EB4, 0x000C0137, 0x000D033B, 0x000E053E, - 0x000F4702, 0x00008905, 0x00020C09, 0x0003128C, - 0x0004148F, 0x00051712, 0x00065916, 0x00091B19, - 0x000A1D28, 0x000B5F2C, 0x000C41AF, 0x000D43B2, - 0x000E85B5, 0x000F87B8, 0x0000C9BC, 0x00024CBF, - 0x00035303, 0x00045506, 0x0005978A, 0x0006998D, - 0x00095B90, 0x000A5D93, 0x000B9F97, 0x000C821A, - 0x000D8400, 0x000EC600, 0x000FC800, 0x00010A00, - 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 b43_ntab_tdi40a0[] = { - 0x0011A346, 0x00136CCF, 0x0014F5D9, 0x001641E2, - 0x0017CB6B, 0x00195475, 0x001B2383, 0x001CAD0C, - 0x001E7616, 0x0000821F, 0x00020BA8, 0x0003D4B2, - 0x00056447, 0x00072DD0, 0x0008B6DA, 0x000A02E3, - 0x000B8C6C, 0x000D15F6, 0x0011E484, 0x0013AE0D, - 0x00153717, 0x00168320, 0x00180CA9, 0x00199633, - 0x001B6548, 0x001CEED1, 0x001EB7DB, 0x0000C3E4, - 0x00024D6D, 0x000416F7, 0x0005A585, 0x00076F0F, - 0x0008F818, 0x000A4421, 0x000BCDAB, 0x000D9734, - 0x00122649, 0x0013EFD2, 0x001578DC, 0x0016C4E5, - 0x00184E6E, 0x001A17F8, 0x001BA686, 0x001D3010, - 0x001EF999, 0x00010522, 0x00028EAC, 0x00045835, - 0x0005E74A, 0x0007B0D3, 0x00093A5D, 0x000A85E6, - 0x000C0F6F, 0x000DD8F9, 0x00126787, 0x00143111, - 0x0015BA9A, 0x00170623, 0x00188FAD, 0x001A5936, - 0x001BE84B, 0x001DB1D4, 0x001F3B5E, 0x000146E7, - 0x00031070, 0x000499FA, 0x00062888, 0x0007F212, - 0x00097B9B, 0x000AC7A4, 0x000C50AE, 0x000E1A37, - 0x0012A94C, 0x001472D5, 0x0015FC5F, 0x00174868, - 0x0018D171, 0x001A9AFB, 0x001C2989, 0x001DF313, - 0x001F7C9C, 0x000188A5, 0x000351AF, 0x0004DB38, - 0x0006AA4D, 0x000833D7, 0x0009BD60, 0x000B0969, - 0x000C9273, 0x000E5BFC, 0x00132A8A, 0x0014B414, - 0x00163D9D, 0x001789A6, 0x001912B0, 0x001ADC39, - 0x001C6BCE, 0x001E34D8, 0x001FBE61, 0x0001CA6A, - 0x00039374, 0x00051CFD, 0x0006EC0B, 0x00087515, - 0x0009FE9E, 0x000B4AA7, 0x000CD3B1, 0x000E9D3A, - 0x00000000, 0x00000000, -}; - -static const u32 b43_ntab_tdi40a1[] = { - 0x001EDB36, 0x000129CA, 0x0002B353, 0x00047CDD, - 0x0005C8E6, 0x000791EF, 0x00091BF9, 0x000AAA07, - 0x000C3391, 0x000DFD1A, 0x00120923, 0x0013D22D, - 0x00155C37, 0x0016EACB, 0x00187454, 0x001A3DDE, - 0x001B89E7, 0x001D12F0, 0x001F1CFA, 0x00016B88, - 0x00033492, 0x0004BE1B, 0x00060A24, 0x0007D32E, - 0x00095D38, 0x000AEC4C, 0x000C7555, 0x000E3EDF, - 0x00124AE8, 0x001413F1, 0x0015A37B, 0x00172C89, - 0x0018B593, 0x001A419C, 0x001BCB25, 0x001D942F, - 0x001F63B9, 0x0001AD4D, 0x00037657, 0x0004C260, - 0x00068BE9, 0x000814F3, 0x0009A47C, 0x000B2D8A, - 0x000CB694, 0x000E429D, 0x00128C26, 0x001455B0, - 0x0015E4BA, 0x00176E4E, 0x0018F758, 0x001A8361, - 0x001C0CEA, 0x001DD674, 0x001FA57D, 0x0001EE8B, - 0x0003B795, 0x0005039E, 0x0006CD27, 0x000856B1, - 0x0009E5C6, 0x000B6F4F, 0x000CF859, 0x000E8462, - 0x00130DEB, 0x00149775, 0x00162603, 0x0017AF8C, - 0x00193896, 0x001AC49F, 0x001C4E28, 0x001E17B2, - 0x0000A6C7, 0x00023050, 0x0003F9DA, 0x00054563, - 0x00070EEC, 0x00089876, 0x000A2704, 0x000BB08D, - 0x000D3A17, 0x001185A0, 0x00134F29, 0x0014D8B3, - 0x001667C8, 0x0017F151, 0x00197ADB, 0x001B0664, - 0x001C8FED, 0x001E5977, 0x0000E805, 0x0002718F, - 0x00043B18, 0x000586A1, 0x0007502B, 0x0008D9B4, - 0x000A68C9, 0x000BF252, 0x000DBBDC, 0x0011C7E5, - 0x001390EE, 0x00151A78, 0x0016A906, 0x00183290, - 0x0019BC19, 0x001B4822, 0x001CD12C, 0x001E9AB5, - 0x00000000, 0x00000000, -}; - -static const u32 b43_ntab_tdtrn[] = { - 0x061C061C, 0x0050EE68, 0xF592FE36, 0xFE5212F6, - 0x00000C38, 0xFE5212F6, 0xF592FE36, 0x0050EE68, - 0x061C061C, 0xEE680050, 0xFE36F592, 0x12F6FE52, - 0x0C380000, 0x12F6FE52, 0xFE36F592, 0xEE680050, - 0x061C061C, 0x0050EE68, 0xF592FE36, 0xFE5212F6, - 0x00000C38, 0xFE5212F6, 0xF592FE36, 0x0050EE68, - 0x061C061C, 0xEE680050, 0xFE36F592, 0x12F6FE52, - 0x0C380000, 0x12F6FE52, 0xFE36F592, 0xEE680050, - 0x05E305E3, 0x004DEF0C, 0xF5F3FE47, 0xFE611246, - 0x00000BC7, 0xFE611246, 0xF5F3FE47, 0x004DEF0C, - 0x05E305E3, 0xEF0C004D, 0xFE47F5F3, 0x1246FE61, - 0x0BC70000, 0x1246FE61, 0xFE47F5F3, 0xEF0C004D, - 0x05E305E3, 0x004DEF0C, 0xF5F3FE47, 0xFE611246, - 0x00000BC7, 0xFE611246, 0xF5F3FE47, 0x004DEF0C, - 0x05E305E3, 0xEF0C004D, 0xFE47F5F3, 0x1246FE61, - 0x0BC70000, 0x1246FE61, 0xFE47F5F3, 0xEF0C004D, - 0xFA58FA58, 0xF895043B, 0xFF4C09C0, 0xFBC6FFA8, - 0xFB84F384, 0x0798F6F9, 0x05760122, 0x058409F6, - 0x0B500000, 0x05B7F542, 0x08860432, 0x06DDFEE7, - 0xFB84F384, 0xF9D90664, 0xF7E8025C, 0x00FFF7BD, - 0x05A805A8, 0xF7BD00FF, 0x025CF7E8, 0x0664F9D9, - 0xF384FB84, 0xFEE706DD, 0x04320886, 0xF54205B7, - 0x00000B50, 0x09F60584, 0x01220576, 0xF6F90798, - 0xF384FB84, 0xFFA8FBC6, 0x09C0FF4C, 0x043BF895, - 0x02D402D4, 0x07DE0270, 0xFC96079C, 0xF90AFE94, - 0xFE00FF2C, 0x02D4065D, 0x092A0096, 0x0014FBB8, - 0xFD2CFD2C, 0x076AFB3C, 0x0096F752, 0xF991FD87, - 0xFB2C0200, 0xFEB8F960, 0x08E0FC96, 0x049802A8, - 0xFD2CFD2C, 0x02A80498, 0xFC9608E0, 0xF960FEB8, - 0x0200FB2C, 0xFD87F991, 0xF7520096, 0xFB3C076A, - 0xFD2CFD2C, 0xFBB80014, 0x0096092A, 0x065D02D4, - 0xFF2CFE00, 0xFE94F90A, 0x079CFC96, 0x027007DE, - 0x02D402D4, 0x027007DE, 0x079CFC96, 0xFE94F90A, - 0xFF2CFE00, 0x065D02D4, 0x0096092A, 0xFBB80014, - 0xFD2CFD2C, 0xFB3C076A, 0xF7520096, 0xFD87F991, - 0x0200FB2C, 0xF960FEB8, 0xFC9608E0, 0x02A80498, - 0xFD2CFD2C, 0x049802A8, 0x08E0FC96, 0xFEB8F960, - 0xFB2C0200, 0xF991FD87, 0x0096F752, 0x076AFB3C, - 0xFD2CFD2C, 0x0014FBB8, 0x092A0096, 0x02D4065D, - 0xFE00FF2C, 0xF90AFE94, 0xFC96079C, 0x07DE0270, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x062A0000, 0xFEFA0759, 0x08B80908, 0xF396FC2D, - 0xF9D6045C, 0xFC4EF608, 0xF748F596, 0x07B207BF, - 0x062A062A, 0xF84EF841, 0xF748F596, 0x03B209F8, - 0xF9D6045C, 0x0C6A03D3, 0x08B80908, 0x0106F8A7, - 0x062A0000, 0xFEFAF8A7, 0x08B8F6F8, 0xF39603D3, - 0xF9D6FBA4, 0xFC4E09F8, 0xF7480A6A, 0x07B2F841, - 0x062AF9D6, 0xF84E07BF, 0xF7480A6A, 0x03B2F608, - 0xF9D6FBA4, 0x0C6AFC2D, 0x08B8F6F8, 0x01060759, - 0x062A0000, 0xFEFA0759, 0x08B80908, 0xF396FC2D, - 0xF9D6045C, 0xFC4EF608, 0xF748F596, 0x07B207BF, - 0x062A062A, 0xF84EF841, 0xF748F596, 0x03B209F8, - 0xF9D6045C, 0x0C6A03D3, 0x08B80908, 0x0106F8A7, - 0x062A0000, 0xFEFAF8A7, 0x08B8F6F8, 0xF39603D3, - 0xF9D6FBA4, 0xFC4E09F8, 0xF7480A6A, 0x07B2F841, - 0x062AF9D6, 0xF84E07BF, 0xF7480A6A, 0x03B2F608, - 0xF9D6FBA4, 0x0C6AFC2D, 0x08B8F6F8, 0x01060759, - 0x061C061C, 0xFF30009D, 0xFFB21141, 0xFD87FB54, - 0xF65DFE59, 0x02EEF99E, 0x0166F03C, 0xFFF809B6, - 0x000008A4, 0x000AF42B, 0x00EFF577, 0xFA840BF2, - 0xFC02FF51, 0x08260F67, 0xFFF0036F, 0x0842F9C3, - 0x00000000, 0x063DF7BE, 0xFC910010, 0xF099F7DA, - 0x00AF03FE, 0xF40E057C, 0x0A89FF11, 0x0BD5FFF6, - 0xF75C0000, 0xF64A0008, 0x0FC4FE9A, 0x0662FD12, - 0x01A709A3, 0x04AC0279, 0xEEBF004E, 0xFF6300D0, - 0xF9E4F9E4, 0x00D0FF63, 0x004EEEBF, 0x027904AC, - 0x09A301A7, 0xFD120662, 0xFE9A0FC4, 0x0008F64A, - 0x0000F75C, 0xFFF60BD5, 0xFF110A89, 0x057CF40E, - 0x03FE00AF, 0xF7DAF099, 0x0010FC91, 0xF7BE063D, - 0x00000000, 0xF9C30842, 0x036FFFF0, 0x0F670826, - 0xFF51FC02, 0x0BF2FA84, 0xF57700EF, 0xF42B000A, - 0x08A40000, 0x09B6FFF8, 0xF03C0166, 0xF99E02EE, - 0xFE59F65D, 0xFB54FD87, 0x1141FFB2, 0x009DFF30, - 0x05E30000, 0xFF060705, 0x085408A0, 0xF425FC59, - 0xFA1D042A, 0xFC78F67A, 0xF7ACF60E, 0x075A0766, - 0x05E305E3, 0xF8A6F89A, 0xF7ACF60E, 0x03880986, - 0xFA1D042A, 0x0BDB03A7, 0x085408A0, 0x00FAF8FB, - 0x05E30000, 0xFF06F8FB, 0x0854F760, 0xF42503A7, - 0xFA1DFBD6, 0xFC780986, 0xF7AC09F2, 0x075AF89A, - 0x05E3FA1D, 0xF8A60766, 0xF7AC09F2, 0x0388F67A, - 0xFA1DFBD6, 0x0BDBFC59, 0x0854F760, 0x00FA0705, - 0x05E30000, 0xFF060705, 0x085408A0, 0xF425FC59, - 0xFA1D042A, 0xFC78F67A, 0xF7ACF60E, 0x075A0766, - 0x05E305E3, 0xF8A6F89A, 0xF7ACF60E, 0x03880986, - 0xFA1D042A, 0x0BDB03A7, 0x085408A0, 0x00FAF8FB, - 0x05E30000, 0xFF06F8FB, 0x0854F760, 0xF42503A7, - 0xFA1DFBD6, 0xFC780986, 0xF7AC09F2, 0x075AF89A, - 0x05E3FA1D, 0xF8A60766, 0xF7AC09F2, 0x0388F67A, - 0xFA1DFBD6, 0x0BDBFC59, 0x0854F760, 0x00FA0705, - 0xFA58FA58, 0xF8F0FE00, 0x0448073D, 0xFDC9FE46, - 0xF9910258, 0x089D0407, 0xFD5CF71A, 0x02AFFDE0, - 0x083E0496, 0xFF5A0740, 0xFF7AFD97, 0x00FE01F1, - 0x0009082E, 0xFA94FF75, 0xFECDF8EA, 0xFFB0F693, - 0xFD2CFA58, 0x0433FF16, 0xFBA405DD, 0xFA610341, - 0x06A606CB, 0x0039FD2D, 0x0677FA97, 0x01FA05E0, - 0xF896003E, 0x075A068B, 0x012CFC3E, 0xFA23F98D, - 0xFC7CFD43, 0xFF90FC0D, 0x01C10982, 0x00C601D6, - 0xFD2CFD2C, 0x01D600C6, 0x098201C1, 0xFC0DFF90, - 0xFD43FC7C, 0xF98DFA23, 0xFC3E012C, 0x068B075A, - 0x003EF896, 0x05E001FA, 0xFA970677, 0xFD2D0039, - 0x06CB06A6, 0x0341FA61, 0x05DDFBA4, 0xFF160433, - 0xFA58FD2C, 0xF693FFB0, 0xF8EAFECD, 0xFF75FA94, - 0x082E0009, 0x01F100FE, 0xFD97FF7A, 0x0740FF5A, - 0x0496083E, 0xFDE002AF, 0xF71AFD5C, 0x0407089D, - 0x0258F991, 0xFE46FDC9, 0x073D0448, 0xFE00F8F0, - 0xFD2CFD2C, 0xFCE00500, 0xFC09FDDC, 0xFE680157, - 0x04C70571, 0xFC3AFF21, 0xFCD70228, 0x056D0277, - 0x0200FE00, 0x0022F927, 0xFE3C032B, 0xFC44FF3C, - 0x03E9FBDB, 0x04570313, 0x04C9FF5C, 0x000D03B8, - 0xFA580000, 0xFBE900D2, 0xF9D0FE0B, 0x0125FDF9, - 0x042501BF, 0x0328FA2B, 0xFFA902F0, 0xFA250157, - 0x0200FE00, 0x03740438, 0xFF0405FD, 0x030CFE52, - 0x0037FB39, 0xFF6904C5, 0x04F8FD23, 0xFD31FC1B, - 0xFD2CFD2C, 0xFC1BFD31, 0xFD2304F8, 0x04C5FF69, - 0xFB390037, 0xFE52030C, 0x05FDFF04, 0x04380374, - 0xFE000200, 0x0157FA25, 0x02F0FFA9, 0xFA2B0328, - 0x01BF0425, 0xFDF90125, 0xFE0BF9D0, 0x00D2FBE9, - 0x0000FA58, 0x03B8000D, 0xFF5C04C9, 0x03130457, - 0xFBDB03E9, 0xFF3CFC44, 0x032BFE3C, 0xF9270022, - 0xFE000200, 0x0277056D, 0x0228FCD7, 0xFF21FC3A, - 0x057104C7, 0x0157FE68, 0xFDDCFC09, 0x0500FCE0, - 0xFD2CFD2C, 0x0500FCE0, 0xFDDCFC09, 0x0157FE68, - 0x057104C7, 0xFF21FC3A, 0x0228FCD7, 0x0277056D, - 0xFE000200, 0xF9270022, 0x032BFE3C, 0xFF3CFC44, - 0xFBDB03E9, 0x03130457, 0xFF5C04C9, 0x03B8000D, - 0x0000FA58, 0x00D2FBE9, 0xFE0BF9D0, 0xFDF90125, - 0x01BF0425, 0xFA2B0328, 0x02F0FFA9, 0x0157FA25, - 0xFE000200, 0x04380374, 0x05FDFF04, 0xFE52030C, - 0xFB390037, 0x04C5FF69, 0xFD2304F8, 0xFC1BFD31, - 0xFD2CFD2C, 0xFD31FC1B, 0x04F8FD23, 0xFF6904C5, - 0x0037FB39, 0x030CFE52, 0xFF0405FD, 0x03740438, - 0x0200FE00, 0xFA250157, 0xFFA902F0, 0x0328FA2B, - 0x042501BF, 0x0125FDF9, 0xF9D0FE0B, 0xFBE900D2, - 0xFA580000, 0x000D03B8, 0x04C9FF5C, 0x04570313, - 0x03E9FBDB, 0xFC44FF3C, 0xFE3C032B, 0x0022F927, - 0x0200FE00, 0x056D0277, 0xFCD70228, 0xFC3AFF21, - 0x04C70571, 0xFE680157, 0xFC09FDDC, 0xFCE00500, - 0x05A80000, 0xFF1006BE, 0x0800084A, 0xF49CFC7E, - 0xFA580400, 0xFC9CF6DA, 0xF800F672, 0x0710071C, - 0x05A805A8, 0xF8F0F8E4, 0xF800F672, 0x03640926, - 0xFA580400, 0x0B640382, 0x0800084A, 0x00F0F942, - 0x05A80000, 0xFF10F942, 0x0800F7B6, 0xF49C0382, - 0xFA58FC00, 0xFC9C0926, 0xF800098E, 0x0710F8E4, - 0x05A8FA58, 0xF8F0071C, 0xF800098E, 0x0364F6DA, - 0xFA58FC00, 0x0B64FC7E, 0x0800F7B6, 0x00F006BE, - 0x05A80000, 0xFF1006BE, 0x0800084A, 0xF49CFC7E, - 0xFA580400, 0xFC9CF6DA, 0xF800F672, 0x0710071C, - 0x05A805A8, 0xF8F0F8E4, 0xF800F672, 0x03640926, - 0xFA580400, 0x0B640382, 0x0800084A, 0x00F0F942, - 0x05A80000, 0xFF10F942, 0x0800F7B6, 0xF49C0382, - 0xFA58FC00, 0xFC9C0926, 0xF800098E, 0x0710F8E4, - 0x05A8FA58, 0xF8F0071C, 0xF800098E, 0x0364F6DA, - 0xFA58FC00, 0x0B64FC7E, 0x0800F7B6, 0x00F006BE, -}; - -static const u32 b43_ntab_tmap[] = { - 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, - 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0xF1111110, 0x11111111, 0x11F11111, 0x00000111, - 0x11000000, 0x1111F111, 0x11111111, 0x111111F1, - 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x000AA888, - 0x88880000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, - 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, - 0xA2222220, 0x22222222, 0x22C22222, 0x00000222, - 0x22000000, 0x2222A222, 0x22222222, 0x222222A2, - 0xF1111110, 0x11111111, 0x11F11111, 0x00011111, - 0x11110000, 0x1111F111, 0x11111111, 0x111111F1, - 0xA8AA88A0, 0xA88888A8, 0xA8A8A88A, 0x00088AAA, - 0xAAAA0000, 0xA8A8AA88, 0xA88AAAAA, 0xAAAA8A8A, - 0xAAA8AAA0, 0x8AAA8AAA, 0xAA8A8A8A, 0x000AAA88, - 0x8AAA0000, 0xAAA8A888, 0x8AA88A8A, 0x8A88A888, - 0x08080A00, 0x0A08080A, 0x080A0A08, 0x00080808, - 0x080A0000, 0x080A0808, 0x080A0808, 0x0A0A0A08, - 0xA0A0A0A0, 0x80A0A080, 0x8080A0A0, 0x00008080, - 0x80A00000, 0x80A080A0, 0xA080A0A0, 0x8080A0A0, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x99999000, 0x9B9B99BB, 0x9BB99999, 0x9999B9B9, - 0x9B99BB90, 0x9BBBBB9B, 0x9B9B9BB9, 0x00000999, - 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00AAA888, - 0x22000000, 0x2222B222, 0x22222222, 0x222222B2, - 0xB2222220, 0x22222222, 0x22D22222, 0x00000222, - 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, - 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, - 0x33000000, 0x3333B333, 0x33333333, 0x333333B3, - 0xB3333330, 0x33333333, 0x33D33333, 0x00000333, - 0x22000000, 0x2222A222, 0x22222222, 0x222222A2, - 0xA2222220, 0x22222222, 0x22C22222, 0x00000222, - 0x99B99B00, 0x9B9B99BB, 0x9BB99999, 0x9999B9B9, - 0x9B99BB99, 0x9BBBBB9B, 0x9B9B9BB9, 0x00000999, - 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0x8A88AA88, 0x8AAAAA8A, 0x8A8A8AA8, 0x08AAA888, - 0x22222200, 0x2222F222, 0x22222222, 0x222222F2, - 0x22222222, 0x22222222, 0x22F22222, 0x00000222, - 0x11000000, 0x1111F111, 0x11111111, 0x11111111, - 0xF1111111, 0x11111111, 0x11F11111, 0x01111111, - 0xBB9BB900, 0xB9B9BB99, 0xB99BBBBB, 0xBBBB9B9B, - 0xB9BB99BB, 0xB99999B9, 0xB9B9B99B, 0x00000BBB, - 0xAA000000, 0xA8A8AA88, 0xA88AAAAA, 0xAAAA8A8A, - 0xA8AA88AA, 0xA88888A8, 0xA8A8A88A, 0x0A888AAA, - 0xAA000000, 0xA8A8AA88, 0xA88AAAAA, 0xAAAA8A8A, - 0xA8AA88A0, 0xA88888A8, 0xA8A8A88A, 0x00000AAA, - 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, - 0xBBBBBB00, 0x999BBBBB, 0x9BB99B9B, 0xB9B9B9BB, - 0xB9B99BBB, 0xB9B9B9BB, 0xB9BB9B99, 0x00000999, - 0x8A000000, 0xAA88A888, 0xA88888AA, 0xA88A8A88, - 0xA88AA88A, 0x88A8AAAA, 0xA8AA8AAA, 0x0888A88A, - 0x0B0B0B00, 0x090B0B0B, 0x0B090B0B, 0x0909090B, - 0x09090B0B, 0x09090B0B, 0x09090B09, 0x00000909, - 0x0A000000, 0x0A080808, 0x080A080A, 0x080A0A08, - 0x080A080A, 0x0808080A, 0x0A0A0A08, 0x0808080A, - 0xB0B0B000, 0x9090B0B0, 0x90B09090, 0xB0B0B090, - 0xB0B090B0, 0x90B0B0B0, 0xB0B09090, 0x00000090, - 0x80000000, 0xA080A080, 0xA08080A0, 0xA0808080, - 0xA080A080, 0x80A0A0A0, 0xA0A080A0, 0x00A0A0A0, - 0x22000000, 0x2222F222, 0x22222222, 0x222222F2, - 0xF2222220, 0x22222222, 0x22F22222, 0x00000222, - 0x11000000, 0x1111F111, 0x11111111, 0x111111F1, - 0xF1111110, 0x11111111, 0x11F11111, 0x00000111, - 0x33000000, 0x3333F333, 0x33333333, 0x333333F3, - 0xF3333330, 0x33333333, 0x33F33333, 0x00000333, - 0x22000000, 0x2222F222, 0x22222222, 0x222222F2, - 0xF2222220, 0x22222222, 0x22F22222, 0x00000222, - 0x99000000, 0x9B9B99BB, 0x9BB99999, 0x9999B9B9, - 0x9B99BB90, 0x9BBBBB9B, 0x9B9B9BB9, 0x00000999, - 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, - 0x88888000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, - 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00AAA888, - 0x88A88A00, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0x8A88AA88, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, - 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0x8A88AA88, 0x8AAAAA8A, 0x8A8A8AA8, 0x08AAA888, - 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, - 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, - 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, - 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, - 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, - 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, - 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -/* static tables, PHY revision >= 3 */ -static const u32 b43_ntab_framestruct_r3[] = { - 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, - 0x09804506, 0x00100030, 0x09804507, 0x00100030, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x08004a0c, 0x00100004, 0x01000a0d, 0x00100024, - 0x0980450e, 0x00100034, 0x0980450f, 0x00100034, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, - 0x1980c506, 0x00100030, 0x21810506, 0x00100030, - 0x21810506, 0x00100030, 0x01800504, 0x00100030, - 0x11808505, 0x00100030, 0x29814507, 0x01100030, - 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, - 0x21810506, 0x00100030, 0x21810506, 0x00100030, - 0x29814507, 0x01100030, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, - 0x1980c50e, 0x00100038, 0x2181050e, 0x00100038, - 0x2181050e, 0x00100038, 0x0180050c, 0x00100038, - 0x1180850d, 0x00100038, 0x2981450f, 0x01100038, - 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, - 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, - 0x2981450f, 0x01100038, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, - 0x1980c506, 0x00100030, 0x1980c506, 0x00100030, - 0x11808504, 0x00100030, 0x3981ca05, 0x00100030, - 0x29814507, 0x01100030, 0x00000000, 0x00000000, - 0x10008a04, 0x00100000, 0x3981ca05, 0x00100030, - 0x1980c506, 0x00100030, 0x29814507, 0x01100030, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x08004a0c, 0x00100008, 0x01000a0d, 0x00100028, - 0x1980c50e, 0x00100038, 0x1980c50e, 0x00100038, - 0x1180850c, 0x00100038, 0x3981ca0d, 0x00100038, - 0x2981450f, 0x01100038, 0x00000000, 0x00000000, - 0x10008a0c, 0x00100008, 0x3981ca0d, 0x00100038, - 0x1980c50e, 0x00100038, 0x2981450f, 0x01100038, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x40021404, 0x00100000, 0x02001405, 0x00100040, - 0x0b004a06, 0x01900060, 0x13008a06, 0x01900060, - 0x13008a06, 0x01900060, 0x43020a04, 0x00100060, - 0x1b00ca05, 0x00100060, 0x23010a07, 0x01500060, - 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, - 0x13008a06, 0x01900060, 0x13008a06, 0x01900060, - 0x23010a07, 0x01500060, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140c, 0x00100010, 0x0200140d, 0x00100050, - 0x0b004a0e, 0x01900070, 0x13008a0e, 0x01900070, - 0x13008a0e, 0x01900070, 0x43020a0c, 0x00100070, - 0x1b00ca0d, 0x00100070, 0x23010a0f, 0x01500070, - 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, - 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, - 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x50029404, 0x00100000, 0x32019405, 0x00100040, - 0x0b004a06, 0x01900060, 0x0b004a06, 0x01900060, - 0x5b02ca04, 0x00100060, 0x3b01d405, 0x00100060, - 0x23010a07, 0x01500060, 0x00000000, 0x00000000, - 0x5802d404, 0x00100000, 0x3b01d405, 0x00100060, - 0x0b004a06, 0x01900060, 0x23010a07, 0x01500060, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x5002940c, 0x00100010, 0x3201940d, 0x00100050, - 0x0b004a0e, 0x01900070, 0x0b004a0e, 0x01900070, - 0x5b02ca0c, 0x00100070, 0x3b01d40d, 0x00100070, - 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, - 0x5802d40c, 0x00100010, 0x3b01d40d, 0x00100070, - 0x0b004a0e, 0x01900070, 0x23010a0f, 0x01500070, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x40021404, 0x000f4800, 0x62031405, 0x00100040, - 0x53028a06, 0x01900060, 0x53028a07, 0x01900060, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140c, 0x000f4808, 0x6203140d, 0x00100048, - 0x53028a0e, 0x01900068, 0x53028a0f, 0x01900068, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000a0c, 0x00100004, 0x11008a0d, 0x00100024, - 0x1980c50e, 0x00100034, 0x2181050e, 0x00100034, - 0x2181050e, 0x00100034, 0x0180050c, 0x00100038, - 0x1180850d, 0x00100038, 0x1181850d, 0x00100038, - 0x2981450f, 0x01100038, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, - 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, - 0x1181850d, 0x00100038, 0x2981450f, 0x01100038, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, - 0x0180c506, 0x00100030, 0x0180c506, 0x00100030, - 0x2180c50c, 0x00100030, 0x49820a0d, 0x0016a130, - 0x41824a0d, 0x0016a130, 0x2981450f, 0x01100030, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x2000ca0c, 0x00100000, 0x49820a0d, 0x0016a130, - 0x1980c50e, 0x00100030, 0x41824a0d, 0x0016a130, - 0x2981450f, 0x01100030, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140c, 0x00100008, 0x0200140d, 0x00100048, - 0x0b004a0e, 0x01900068, 0x13008a0e, 0x01900068, - 0x13008a0e, 0x01900068, 0x43020a0c, 0x00100070, - 0x1b00ca0d, 0x00100070, 0x1b014a0d, 0x00100070, - 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, - 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, - 0x1b014a0d, 0x00100070, 0x23010a0f, 0x01500070, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x50029404, 0x00100000, 0x32019405, 0x00100040, - 0x03004a06, 0x01900060, 0x03004a06, 0x01900060, - 0x6b030a0c, 0x00100060, 0x4b02140d, 0x0016a160, - 0x4302540d, 0x0016a160, 0x23010a0f, 0x01500060, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x6b03140c, 0x00100060, 0x4b02140d, 0x0016a160, - 0x0b004a0e, 0x01900060, 0x4302540d, 0x0016a160, - 0x23010a0f, 0x01500060, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, - 0x53028a06, 0x01900060, 0x5b02ca06, 0x01900060, - 0x5b02ca06, 0x01900060, 0x43020a04, 0x00100060, - 0x1b00ca05, 0x00100060, 0x53028a07, 0x0190c060, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, - 0x53028a0e, 0x01900070, 0x5b02ca0e, 0x01900070, - 0x5b02ca0e, 0x01900070, 0x43020a0c, 0x00100070, - 0x1b00ca0d, 0x00100070, 0x53028a0f, 0x0190c070, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, - 0x5b02ca06, 0x01900060, 0x5b02ca06, 0x01900060, - 0x53028a07, 0x0190c060, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, - 0x5b02ca0e, 0x01900070, 0x5b02ca0e, 0x01900070, - 0x53028a0f, 0x0190c070, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -static const u16 b43_ntab_pilot_r3[] = { - 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, - 0xff08, 0xff08, 0x80d5, 0x80d5, 0x80d5, 0x80d5, - 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0xff0a, 0xff82, - 0xffa0, 0xff28, 0xffff, 0xffff, 0xffff, 0xffff, - 0xff82, 0xffa0, 0xff28, 0xff0a, 0xffff, 0xffff, - 0xffff, 0xffff, 0xf83f, 0xfa1f, 0xfa97, 0xfab5, - 0xf2bd, 0xf0bf, 0xffff, 0xffff, 0xf017, 0xf815, - 0xf215, 0xf095, 0xf035, 0xf01d, 0xffff, 0xffff, - 0xff08, 0xff02, 0xff80, 0xff20, 0xff08, 0xff02, - 0xff80, 0xff20, 0xf01f, 0xf817, 0xfa15, 0xf295, - 0xf0b5, 0xf03d, 0xffff, 0xffff, 0xf82a, 0xfa0a, - 0xfa82, 0xfaa0, 0xf2a8, 0xf0aa, 0xffff, 0xffff, - 0xf002, 0xf800, 0xf200, 0xf080, 0xf020, 0xf008, - 0xffff, 0xffff, 0xf00a, 0xf802, 0xfa00, 0xf280, - 0xf0a0, 0xf028, 0xffff, 0xffff, -}; - -static const u32 b43_ntab_tmap_r3[] = { - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, - 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, - 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, - 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, - 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, - 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, - 0xf1111110, 0x11111111, 0x11f11111, 0x00011111, - 0x11110000, 0x1111f111, 0x11111111, 0x111111f1, - 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa, - 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, - 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88, - 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888, - 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808, - 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08, - 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080, - 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, - 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, - 0x22000000, 0x2222b222, 0x22222222, 0x222222b2, - 0xb2222220, 0x22222222, 0x22d22222, 0x00000222, - 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, - 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, - 0x33000000, 0x3333b333, 0x33333333, 0x333333b3, - 0xb3333330, 0x33333333, 0x33d33333, 0x00000333, - 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, - 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, - 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, - 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, - 0x22222200, 0x2222f222, 0x22222222, 0x222222f2, - 0x22222222, 0x22222222, 0x22f22222, 0x00000222, - 0x11000000, 0x1111f111, 0x11111111, 0x11111111, - 0xf1111111, 0x11111111, 0x11f11111, 0x01111111, - 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b, - 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb, - 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, - 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa, - 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, - 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb, - 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999, - 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88, - 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a, - 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b, - 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909, - 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08, - 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a, - 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090, - 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090, - 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080, - 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0, - 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, - 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, - 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, - 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, - 0x33000000, 0x3333f333, 0x33333333, 0x333333f3, - 0xf3333330, 0x33333333, 0x33f33333, 0x00000333, - 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, - 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, - 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, - 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, - 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, - 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, - 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, - 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, - 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 b43_ntab_intlevel_r3[] = { - 0x00802070, 0x0671188d, 0x0a60192c, 0x0a300e46, - 0x00c1188d, 0x080024d2, 0x00000070, -}; - -static const u32 b43_ntab_tdtrn_r3[] = { - 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, - 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, - 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, - 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, - 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, - 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, - 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, - 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, - 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, - 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, - 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, - 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, - 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, - 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, - 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, - 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, - 0xfa58fa58, 0xf895043b, 0xff4c09c0, 0xfbc6ffa8, - 0xfb84f384, 0x0798f6f9, 0x05760122, 0x058409f6, - 0x0b500000, 0x05b7f542, 0x08860432, 0x06ddfee7, - 0xfb84f384, 0xf9d90664, 0xf7e8025c, 0x00fff7bd, - 0x05a805a8, 0xf7bd00ff, 0x025cf7e8, 0x0664f9d9, - 0xf384fb84, 0xfee706dd, 0x04320886, 0xf54205b7, - 0x00000b50, 0x09f60584, 0x01220576, 0xf6f90798, - 0xf384fb84, 0xffa8fbc6, 0x09c0ff4c, 0x043bf895, - 0x02d402d4, 0x07de0270, 0xfc96079c, 0xf90afe94, - 0xfe00ff2c, 0x02d4065d, 0x092a0096, 0x0014fbb8, - 0xfd2cfd2c, 0x076afb3c, 0x0096f752, 0xf991fd87, - 0xfb2c0200, 0xfeb8f960, 0x08e0fc96, 0x049802a8, - 0xfd2cfd2c, 0x02a80498, 0xfc9608e0, 0xf960feb8, - 0x0200fb2c, 0xfd87f991, 0xf7520096, 0xfb3c076a, - 0xfd2cfd2c, 0xfbb80014, 0x0096092a, 0x065d02d4, - 0xff2cfe00, 0xfe94f90a, 0x079cfc96, 0x027007de, - 0x02d402d4, 0x027007de, 0x079cfc96, 0xfe94f90a, - 0xff2cfe00, 0x065d02d4, 0x0096092a, 0xfbb80014, - 0xfd2cfd2c, 0xfb3c076a, 0xf7520096, 0xfd87f991, - 0x0200fb2c, 0xf960feb8, 0xfc9608e0, 0x02a80498, - 0xfd2cfd2c, 0x049802a8, 0x08e0fc96, 0xfeb8f960, - 0xfb2c0200, 0xf991fd87, 0x0096f752, 0x076afb3c, - 0xfd2cfd2c, 0x0014fbb8, 0x092a0096, 0x02d4065d, - 0xfe00ff2c, 0xf90afe94, 0xfc96079c, 0x07de0270, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, - 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, - 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, - 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, - 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, - 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, - 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, - 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, - 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, - 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, - 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, - 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, - 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, - 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, - 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, - 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, - 0x061c061c, 0xff30009d, 0xffb21141, 0xfd87fb54, - 0xf65dfe59, 0x02eef99e, 0x0166f03c, 0xfff809b6, - 0x000008a4, 0x000af42b, 0x00eff577, 0xfa840bf2, - 0xfc02ff51, 0x08260f67, 0xfff0036f, 0x0842f9c3, - 0x00000000, 0x063df7be, 0xfc910010, 0xf099f7da, - 0x00af03fe, 0xf40e057c, 0x0a89ff11, 0x0bd5fff6, - 0xf75c0000, 0xf64a0008, 0x0fc4fe9a, 0x0662fd12, - 0x01a709a3, 0x04ac0279, 0xeebf004e, 0xff6300d0, - 0xf9e4f9e4, 0x00d0ff63, 0x004eeebf, 0x027904ac, - 0x09a301a7, 0xfd120662, 0xfe9a0fc4, 0x0008f64a, - 0x0000f75c, 0xfff60bd5, 0xff110a89, 0x057cf40e, - 0x03fe00af, 0xf7daf099, 0x0010fc91, 0xf7be063d, - 0x00000000, 0xf9c30842, 0x036ffff0, 0x0f670826, - 0xff51fc02, 0x0bf2fa84, 0xf57700ef, 0xf42b000a, - 0x08a40000, 0x09b6fff8, 0xf03c0166, 0xf99e02ee, - 0xfe59f65d, 0xfb54fd87, 0x1141ffb2, 0x009dff30, - 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, - 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, - 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, - 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, - 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, - 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, - 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, - 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, - 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, - 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, - 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, - 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, - 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, - 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, - 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, - 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, - 0xfa58fa58, 0xf8f0fe00, 0x0448073d, 0xfdc9fe46, - 0xf9910258, 0x089d0407, 0xfd5cf71a, 0x02affde0, - 0x083e0496, 0xff5a0740, 0xff7afd97, 0x00fe01f1, - 0x0009082e, 0xfa94ff75, 0xfecdf8ea, 0xffb0f693, - 0xfd2cfa58, 0x0433ff16, 0xfba405dd, 0xfa610341, - 0x06a606cb, 0x0039fd2d, 0x0677fa97, 0x01fa05e0, - 0xf896003e, 0x075a068b, 0x012cfc3e, 0xfa23f98d, - 0xfc7cfd43, 0xff90fc0d, 0x01c10982, 0x00c601d6, - 0xfd2cfd2c, 0x01d600c6, 0x098201c1, 0xfc0dff90, - 0xfd43fc7c, 0xf98dfa23, 0xfc3e012c, 0x068b075a, - 0x003ef896, 0x05e001fa, 0xfa970677, 0xfd2d0039, - 0x06cb06a6, 0x0341fa61, 0x05ddfba4, 0xff160433, - 0xfa58fd2c, 0xf693ffb0, 0xf8eafecd, 0xff75fa94, - 0x082e0009, 0x01f100fe, 0xfd97ff7a, 0x0740ff5a, - 0x0496083e, 0xfde002af, 0xf71afd5c, 0x0407089d, - 0x0258f991, 0xfe46fdc9, 0x073d0448, 0xfe00f8f0, - 0xfd2cfd2c, 0xfce00500, 0xfc09fddc, 0xfe680157, - 0x04c70571, 0xfc3aff21, 0xfcd70228, 0x056d0277, - 0x0200fe00, 0x0022f927, 0xfe3c032b, 0xfc44ff3c, - 0x03e9fbdb, 0x04570313, 0x04c9ff5c, 0x000d03b8, - 0xfa580000, 0xfbe900d2, 0xf9d0fe0b, 0x0125fdf9, - 0x042501bf, 0x0328fa2b, 0xffa902f0, 0xfa250157, - 0x0200fe00, 0x03740438, 0xff0405fd, 0x030cfe52, - 0x0037fb39, 0xff6904c5, 0x04f8fd23, 0xfd31fc1b, - 0xfd2cfd2c, 0xfc1bfd31, 0xfd2304f8, 0x04c5ff69, - 0xfb390037, 0xfe52030c, 0x05fdff04, 0x04380374, - 0xfe000200, 0x0157fa25, 0x02f0ffa9, 0xfa2b0328, - 0x01bf0425, 0xfdf90125, 0xfe0bf9d0, 0x00d2fbe9, - 0x0000fa58, 0x03b8000d, 0xff5c04c9, 0x03130457, - 0xfbdb03e9, 0xff3cfc44, 0x032bfe3c, 0xf9270022, - 0xfe000200, 0x0277056d, 0x0228fcd7, 0xff21fc3a, - 0x057104c7, 0x0157fe68, 0xfddcfc09, 0x0500fce0, - 0xfd2cfd2c, 0x0500fce0, 0xfddcfc09, 0x0157fe68, - 0x057104c7, 0xff21fc3a, 0x0228fcd7, 0x0277056d, - 0xfe000200, 0xf9270022, 0x032bfe3c, 0xff3cfc44, - 0xfbdb03e9, 0x03130457, 0xff5c04c9, 0x03b8000d, - 0x0000fa58, 0x00d2fbe9, 0xfe0bf9d0, 0xfdf90125, - 0x01bf0425, 0xfa2b0328, 0x02f0ffa9, 0x0157fa25, - 0xfe000200, 0x04380374, 0x05fdff04, 0xfe52030c, - 0xfb390037, 0x04c5ff69, 0xfd2304f8, 0xfc1bfd31, - 0xfd2cfd2c, 0xfd31fc1b, 0x04f8fd23, 0xff6904c5, - 0x0037fb39, 0x030cfe52, 0xff0405fd, 0x03740438, - 0x0200fe00, 0xfa250157, 0xffa902f0, 0x0328fa2b, - 0x042501bf, 0x0125fdf9, 0xf9d0fe0b, 0xfbe900d2, - 0xfa580000, 0x000d03b8, 0x04c9ff5c, 0x04570313, - 0x03e9fbdb, 0xfc44ff3c, 0xfe3c032b, 0x0022f927, - 0x0200fe00, 0x056d0277, 0xfcd70228, 0xfc3aff21, - 0x04c70571, 0xfe680157, 0xfc09fddc, 0xfce00500, - 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, - 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, - 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, - 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, - 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, - 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, - 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, - 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, - 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, - 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, - 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, - 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, - 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, - 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, - 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, - 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, -}; - -static const u32 b43_ntab_noisevar_r3[] = { - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, -}; - -static const u16 b43_ntab_mcs_r3[] = { - 0x0000, 0x0008, 0x000a, 0x0010, 0x0012, 0x0019, - 0x001a, 0x001c, 0x0080, 0x0088, 0x008a, 0x0090, - 0x0092, 0x0099, 0x009a, 0x009c, 0x0100, 0x0108, - 0x010a, 0x0110, 0x0112, 0x0119, 0x011a, 0x011c, - 0x0180, 0x0188, 0x018a, 0x0190, 0x0192, 0x0199, - 0x019a, 0x019c, 0x0000, 0x0098, 0x00a0, 0x00a8, - 0x009a, 0x00a2, 0x00aa, 0x0120, 0x0128, 0x0128, - 0x0130, 0x0138, 0x0138, 0x0140, 0x0122, 0x012a, - 0x012a, 0x0132, 0x013a, 0x013a, 0x0142, 0x01a8, - 0x01b0, 0x01b8, 0x01b0, 0x01b8, 0x01c0, 0x01c8, - 0x01c0, 0x01c8, 0x01d0, 0x01d0, 0x01d8, 0x01aa, - 0x01b2, 0x01ba, 0x01b2, 0x01ba, 0x01c2, 0x01ca, - 0x01c2, 0x01ca, 0x01d2, 0x01d2, 0x01da, 0x0001, - 0x0002, 0x0004, 0x0009, 0x000c, 0x0011, 0x0014, - 0x0018, 0x0020, 0x0021, 0x0022, 0x0024, 0x0081, - 0x0082, 0x0084, 0x0089, 0x008c, 0x0091, 0x0094, - 0x0098, 0x00a0, 0x00a1, 0x00a2, 0x00a4, 0x0007, - 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, - 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, - 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, - 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, - 0x0007, 0x0007, -}; - -static const u32 b43_ntab_tdi20a0_r3[] = { - 0x00091226, 0x000a1429, 0x000b56ad, 0x000c58b0, - 0x000d5ab3, 0x000e9cb6, 0x000f9eba, 0x0000c13d, - 0x00020301, 0x00030504, 0x00040708, 0x0005090b, - 0x00064b8e, 0x00095291, 0x000a5494, 0x000b9718, - 0x000c9927, 0x000d9b2a, 0x000edd2e, 0x000fdf31, - 0x000101b4, 0x000243b7, 0x000345bb, 0x000447be, - 0x00058982, 0x00068c05, 0x00099309, 0x000a950c, - 0x000bd78f, 0x000cd992, 0x000ddb96, 0x000f1d99, - 0x00005fa8, 0x0001422c, 0x0002842f, 0x00038632, - 0x00048835, 0x0005ca38, 0x0006ccbc, 0x0009d3bf, - 0x000b1603, 0x000c1806, 0x000d1a0a, 0x000e1c0d, - 0x000f5e10, 0x00008093, 0x00018297, 0x0002c49a, - 0x0003c680, 0x0004c880, 0x00060b00, 0x00070d00, - 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 b43_ntab_tdi20a1_r3[] = { - 0x00014b26, 0x00028d29, 0x000393ad, 0x00049630, - 0x0005d833, 0x0006da36, 0x00099c3a, 0x000a9e3d, - 0x000bc081, 0x000cc284, 0x000dc488, 0x000f068b, - 0x0000488e, 0x00018b91, 0x0002d214, 0x0003d418, - 0x0004d6a7, 0x000618aa, 0x00071aae, 0x0009dcb1, - 0x000b1eb4, 0x000c0137, 0x000d033b, 0x000e053e, - 0x000f4702, 0x00008905, 0x00020c09, 0x0003128c, - 0x0004148f, 0x00051712, 0x00065916, 0x00091b19, - 0x000a1d28, 0x000b5f2c, 0x000c41af, 0x000d43b2, - 0x000e85b5, 0x000f87b8, 0x0000c9bc, 0x00024cbf, - 0x00035303, 0x00045506, 0x0005978a, 0x0006998d, - 0x00095b90, 0x000a5d93, 0x000b9f97, 0x000c821a, - 0x000d8400, 0x000ec600, 0x000fc800, 0x00010a00, - 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 b43_ntab_tdi40a0_r3[] = { - 0x0011a346, 0x00136ccf, 0x0014f5d9, 0x001641e2, - 0x0017cb6b, 0x00195475, 0x001b2383, 0x001cad0c, - 0x001e7616, 0x0000821f, 0x00020ba8, 0x0003d4b2, - 0x00056447, 0x00072dd0, 0x0008b6da, 0x000a02e3, - 0x000b8c6c, 0x000d15f6, 0x0011e484, 0x0013ae0d, - 0x00153717, 0x00168320, 0x00180ca9, 0x00199633, - 0x001b6548, 0x001ceed1, 0x001eb7db, 0x0000c3e4, - 0x00024d6d, 0x000416f7, 0x0005a585, 0x00076f0f, - 0x0008f818, 0x000a4421, 0x000bcdab, 0x000d9734, - 0x00122649, 0x0013efd2, 0x001578dc, 0x0016c4e5, - 0x00184e6e, 0x001a17f8, 0x001ba686, 0x001d3010, - 0x001ef999, 0x00010522, 0x00028eac, 0x00045835, - 0x0005e74a, 0x0007b0d3, 0x00093a5d, 0x000a85e6, - 0x000c0f6f, 0x000dd8f9, 0x00126787, 0x00143111, - 0x0015ba9a, 0x00170623, 0x00188fad, 0x001a5936, - 0x001be84b, 0x001db1d4, 0x001f3b5e, 0x000146e7, - 0x00031070, 0x000499fa, 0x00062888, 0x0007f212, - 0x00097b9b, 0x000ac7a4, 0x000c50ae, 0x000e1a37, - 0x0012a94c, 0x001472d5, 0x0015fc5f, 0x00174868, - 0x0018d171, 0x001a9afb, 0x001c2989, 0x001df313, - 0x001f7c9c, 0x000188a5, 0x000351af, 0x0004db38, - 0x0006aa4d, 0x000833d7, 0x0009bd60, 0x000b0969, - 0x000c9273, 0x000e5bfc, 0x00132a8a, 0x0014b414, - 0x00163d9d, 0x001789a6, 0x001912b0, 0x001adc39, - 0x001c6bce, 0x001e34d8, 0x001fbe61, 0x0001ca6a, - 0x00039374, 0x00051cfd, 0x0006ec0b, 0x00087515, - 0x0009fe9e, 0x000b4aa7, 0x000cd3b1, 0x000e9d3a, - 0x00000000, 0x00000000, -}; - -static const u32 b43_ntab_tdi40a1_r3[] = { - 0x001edb36, 0x000129ca, 0x0002b353, 0x00047cdd, - 0x0005c8e6, 0x000791ef, 0x00091bf9, 0x000aaa07, - 0x000c3391, 0x000dfd1a, 0x00120923, 0x0013d22d, - 0x00155c37, 0x0016eacb, 0x00187454, 0x001a3dde, - 0x001b89e7, 0x001d12f0, 0x001f1cfa, 0x00016b88, - 0x00033492, 0x0004be1b, 0x00060a24, 0x0007d32e, - 0x00095d38, 0x000aec4c, 0x000c7555, 0x000e3edf, - 0x00124ae8, 0x001413f1, 0x0015a37b, 0x00172c89, - 0x0018b593, 0x001a419c, 0x001bcb25, 0x001d942f, - 0x001f63b9, 0x0001ad4d, 0x00037657, 0x0004c260, - 0x00068be9, 0x000814f3, 0x0009a47c, 0x000b2d8a, - 0x000cb694, 0x000e429d, 0x00128c26, 0x001455b0, - 0x0015e4ba, 0x00176e4e, 0x0018f758, 0x001a8361, - 0x001c0cea, 0x001dd674, 0x001fa57d, 0x0001ee8b, - 0x0003b795, 0x0005039e, 0x0006cd27, 0x000856b1, - 0x0009e5c6, 0x000b6f4f, 0x000cf859, 0x000e8462, - 0x00130deb, 0x00149775, 0x00162603, 0x0017af8c, - 0x00193896, 0x001ac49f, 0x001c4e28, 0x001e17b2, - 0x0000a6c7, 0x00023050, 0x0003f9da, 0x00054563, - 0x00070eec, 0x00089876, 0x000a2704, 0x000bb08d, - 0x000d3a17, 0x001185a0, 0x00134f29, 0x0014d8b3, - 0x001667c8, 0x0017f151, 0x00197adb, 0x001b0664, - 0x001c8fed, 0x001e5977, 0x0000e805, 0x0002718f, - 0x00043b18, 0x000586a1, 0x0007502b, 0x0008d9b4, - 0x000a68c9, 0x000bf252, 0x000dbbdc, 0x0011c7e5, - 0x001390ee, 0x00151a78, 0x0016a906, 0x00183290, - 0x0019bc19, 0x001b4822, 0x001cd12c, 0x001e9ab5, - 0x00000000, 0x00000000, -}; - -static const u32 b43_ntab_pilotlt_r3[] = { - 0x76540213, 0x62407351, 0x76543210, 0x76540213, - 0x76540213, 0x76430521, -}; - -static const u32 b43_ntab_channelest_r3[] = { - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x44444444, 0x44444444, 0x44444444, 0x44444444, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, - 0x10101010, 0x10101010, 0x10101010, 0x10101010, -}; - -static const u8 b43_ntab_framelookup_r3[] = { - 0x02, 0x04, 0x14, 0x14, 0x03, 0x05, 0x16, 0x16, - 0x0a, 0x0c, 0x1c, 0x1c, 0x0b, 0x0d, 0x1e, 0x1e, - 0x06, 0x08, 0x18, 0x18, 0x07, 0x09, 0x1a, 0x1a, - 0x0e, 0x10, 0x20, 0x28, 0x0f, 0x11, 0x22, 0x2a, -}; - -static const u8 b43_ntab_estimatepowerlt0_r3[] = { - 0x55, 0x54, 0x54, 0x53, 0x52, 0x52, 0x51, 0x51, - 0x50, 0x4f, 0x4f, 0x4e, 0x4e, 0x4d, 0x4c, 0x4c, - 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x47, 0x46, 0x46, - 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x40, 0x3f, - 0x3e, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x37, 0x36, - 0x35, 0x33, 0x32, 0x31, 0x2f, 0x2e, 0x2c, 0x2b, - 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1a, - 0x18, 0x15, 0x12, 0x0e, 0x0b, 0x07, 0x02, 0xfd, -}; - -static const u8 b43_ntab_estimatepowerlt1_r3[] = { - 0x55, 0x54, 0x54, 0x53, 0x52, 0x52, 0x51, 0x51, - 0x50, 0x4f, 0x4f, 0x4e, 0x4e, 0x4d, 0x4c, 0x4c, - 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x47, 0x46, 0x46, - 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x40, 0x3f, - 0x3e, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x37, 0x36, - 0x35, 0x33, 0x32, 0x31, 0x2f, 0x2e, 0x2c, 0x2b, - 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1a, - 0x18, 0x15, 0x12, 0x0e, 0x0b, 0x07, 0x02, 0xfd, -}; - -static const u8 b43_ntab_adjustpower0_r3[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static const u8 b43_ntab_adjustpower1_r3[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static const u32 b43_ntab_gainctl0_r3[] = { - 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, - 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, - 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, - 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, - 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, - 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, - 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, - 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, - 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, - 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, - 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, - 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, - 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, - 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, - 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, - 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, - 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, - 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, - 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, - 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, - 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, - 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, - 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, - 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, - 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, - 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, - 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, - 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, - 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, - 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, - 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, - 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, -}; - -static const u32 b43_ntab_gainctl1_r3[] = { - 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, - 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, - 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, - 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, - 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, - 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, - 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, - 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, - 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, - 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, - 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, - 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, - 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, - 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, - 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, - 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, - 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, - 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, - 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, - 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, - 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, - 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, - 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, - 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, - 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, - 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, - 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, - 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, - 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, - 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, - 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, - 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, -}; - -static const u32 b43_ntab_iqlt0_r3[] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 b43_ntab_iqlt1_r3[] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -static const u16 b43_ntab_loftlt0_r3[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, -}; - -static const u16 b43_ntab_loftlt1_r3[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, -}; - -/* volatile tables, PHY revision >= 3 */ - -/* indexed by antswctl2g */ -static const u16 b43_ntab_antswctl_r3[4][32] = { - { - 0x0082, 0x0082, 0x0211, 0x0222, 0x0328, - 0x0000, 0x0000, 0x0000, 0x0144, 0x0000, - 0x0000, 0x0000, 0x0188, 0x0000, 0x0000, - 0x0000, 0x0082, 0x0082, 0x0211, 0x0222, - 0x0328, 0x0000, 0x0000, 0x0000, 0x0144, - 0x0000, 0x0000, 0x0000, 0x0188, 0x0000, - 0x0000, 0x0000, - }, - { - 0x0022, 0x0022, 0x0011, 0x0022, 0x0022, - 0x0000, 0x0000, 0x0000, 0x0011, 0x0000, - 0x0000, 0x0000, 0x0022, 0x0000, 0x0000, - 0x0000, 0x0022, 0x0022, 0x0011, 0x0022, - 0x0022, 0x0000, 0x0000, 0x0000, 0x0011, - 0x0000, 0x0000, 0x0000, 0x0022, 0x0000, - 0x0000, 0x0000, - }, - { - 0x0088, 0x0088, 0x0044, 0x0088, 0x0088, - 0x0000, 0x0000, 0x0000, 0x0044, 0x0000, - 0x0000, 0x0000, 0x0088, 0x0000, 0x0000, - 0x0000, 0x0088, 0x0088, 0x0044, 0x0088, - 0x0088, 0x0000, 0x0000, 0x0000, 0x0044, - 0x0000, 0x0000, 0x0000, 0x0088, 0x0000, - 0x0000, 0x0000, - }, - { - 0x0022, 0x0022, 0x0011, 0x0022, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0011, 0x0000, - 0x0000, 0x0000, 0x0022, 0x0000, 0x0000, - 0x03cc, 0x0022, 0x0022, 0x0011, 0x0022, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0011, - 0x0000, 0x0000, 0x0000, 0x0022, 0x0000, - 0x0000, 0x03cc, - } -}; - -/* static tables, PHY revision >= 7 */ - -/* Copied from brcmsmac (5.75.11) */ -static const u32 b43_ntab_tmap_r7[] = { - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, - 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, - 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, - 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, - 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, - 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, - 0xf1111110, 0x11111111, 0x11f11111, 0x00011111, - 0x11110000, 0x1111f111, 0x11111111, 0x111111f1, - 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa, - 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, - 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88, - 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888, - 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808, - 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08, - 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080, - 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, - 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, - 0x22000000, 0x2222b222, 0x22222222, 0x222222b2, - 0xb2222220, 0x22222222, 0x22d22222, 0x00000222, - 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, - 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, - 0x33000000, 0x3333b333, 0x33333333, 0x333333b3, - 0xb3333330, 0x33333333, 0x33d33333, 0x00000333, - 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, - 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, - 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, - 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, - 0x22222200, 0x2222f222, 0x22222222, 0x222222f2, - 0x22222222, 0x22222222, 0x22f22222, 0x00000222, - 0x11000000, 0x1111f111, 0x11111111, 0x11111111, - 0xf1111111, 0x11111111, 0x11f11111, 0x01111111, - 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b, - 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb, - 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, - 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa, - 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, - 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb, - 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999, - 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88, - 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a, - 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b, - 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909, - 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08, - 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a, - 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090, - 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090, - 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080, - 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0, - 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, - 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, - 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, - 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, - 0x33000000, 0x3333f333, 0x33333333, 0x333333f3, - 0xf3333330, 0x33333333, 0x33f33333, 0x00000333, - 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, - 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, - 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, - 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, - 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, - 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, - 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, - 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, - 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, - 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, - 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -/* Extracted from MMIO dump of 6.30.223.141 */ -static const u32 b43_ntab_noisevar_r7[] = { - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, - 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, -}; - -/************************************************** - * TX gain tables - **************************************************/ - -static const u32 b43_ntab_tx_gain_rev0_1_2[] = { - 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, - 0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44, - 0x03c82a42, 0x03c82944, 0x03c82942, 0x03c82844, - 0x03c82842, 0x03c42b44, 0x03c42b42, 0x03c42a44, - 0x03c42a42, 0x03c42944, 0x03c42942, 0x03c42844, - 0x03c42842, 0x03c42744, 0x03c42742, 0x03c42644, - 0x03c42642, 0x03c42544, 0x03c42542, 0x03c42444, - 0x03c42442, 0x03c02b44, 0x03c02b42, 0x03c02a44, - 0x03c02a42, 0x03c02944, 0x03c02942, 0x03c02844, - 0x03c02842, 0x03c02744, 0x03c02742, 0x03b02b44, - 0x03b02b42, 0x03b02a44, 0x03b02a42, 0x03b02944, - 0x03b02942, 0x03b02844, 0x03b02842, 0x03b02744, - 0x03b02742, 0x03b02644, 0x03b02642, 0x03b02544, - 0x03b02542, 0x03a02b44, 0x03a02b42, 0x03a02a44, - 0x03a02a42, 0x03a02944, 0x03a02942, 0x03a02844, - 0x03a02842, 0x03a02744, 0x03a02742, 0x03902b44, - 0x03902b42, 0x03902a44, 0x03902a42, 0x03902944, - 0x03902942, 0x03902844, 0x03902842, 0x03902744, - 0x03902742, 0x03902644, 0x03902642, 0x03902544, - 0x03902542, 0x03802b44, 0x03802b42, 0x03802a44, - 0x03802a42, 0x03802944, 0x03802942, 0x03802844, - 0x03802842, 0x03802744, 0x03802742, 0x03802644, - 0x03802642, 0x03802544, 0x03802542, 0x03802444, - 0x03802442, 0x03802344, 0x03802342, 0x03802244, - 0x03802242, 0x03802144, 0x03802142, 0x03802044, - 0x03802042, 0x03801f44, 0x03801f42, 0x03801e44, - 0x03801e42, 0x03801d44, 0x03801d42, 0x03801c44, - 0x03801c42, 0x03801b44, 0x03801b42, 0x03801a44, - 0x03801a42, 0x03801944, 0x03801942, 0x03801844, - 0x03801842, 0x03801744, 0x03801742, 0x03801644, - 0x03801642, 0x03801544, 0x03801542, 0x03801444, - 0x03801442, 0x03801344, 0x03801342, 0x00002b00, -}; - -/* EPA 2 GHz */ - -static const u32 b43_ntab_tx_gain_epa_rev3_2g[] = { - 0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e, - 0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037, - 0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e, - 0x1e41003c, 0x1e41003b, 0x1e410039, 0x1e410037, - 0x1d410044, 0x1d410042, 0x1d410040, 0x1d41003e, - 0x1d41003c, 0x1d41003b, 0x1d410039, 0x1d410037, - 0x1c410044, 0x1c410042, 0x1c410040, 0x1c41003e, - 0x1c41003c, 0x1c41003b, 0x1c410039, 0x1c410037, - 0x1b410044, 0x1b410042, 0x1b410040, 0x1b41003e, - 0x1b41003c, 0x1b41003b, 0x1b410039, 0x1b410037, - 0x1a410044, 0x1a410042, 0x1a410040, 0x1a41003e, - 0x1a41003c, 0x1a41003b, 0x1a410039, 0x1a410037, - 0x19410044, 0x19410042, 0x19410040, 0x1941003e, - 0x1941003c, 0x1941003b, 0x19410039, 0x19410037, - 0x18410044, 0x18410042, 0x18410040, 0x1841003e, - 0x1841003c, 0x1841003b, 0x18410039, 0x18410037, - 0x17410044, 0x17410042, 0x17410040, 0x1741003e, - 0x1741003c, 0x1741003b, 0x17410039, 0x17410037, - 0x16410044, 0x16410042, 0x16410040, 0x1641003e, - 0x1641003c, 0x1641003b, 0x16410039, 0x16410037, - 0x15410044, 0x15410042, 0x15410040, 0x1541003e, - 0x1541003c, 0x1541003b, 0x15410039, 0x15410037, - 0x14410044, 0x14410042, 0x14410040, 0x1441003e, - 0x1441003c, 0x1441003b, 0x14410039, 0x14410037, - 0x13410044, 0x13410042, 0x13410040, 0x1341003e, - 0x1341003c, 0x1341003b, 0x13410039, 0x13410037, - 0x12410044, 0x12410042, 0x12410040, 0x1241003e, - 0x1241003c, 0x1241003b, 0x12410039, 0x12410037, - 0x11410044, 0x11410042, 0x11410040, 0x1141003e, - 0x1141003c, 0x1141003b, 0x11410039, 0x11410037, - 0x10410044, 0x10410042, 0x10410040, 0x1041003e, - 0x1041003c, 0x1041003b, 0x10410039, 0x10410037, -}; - -static const u32 b43_ntab_tx_gain_epa_rev3_hi_pwr_2g[] = { - 0x0f410044, 0x0f410042, 0x0f410040, 0x0f41003e, - 0x0f41003c, 0x0f41003b, 0x0f410039, 0x0f410037, - 0x0e410044, 0x0e410042, 0x0e410040, 0x0e41003e, - 0x0e41003c, 0x0e41003b, 0x0e410039, 0x0e410037, - 0x0d410044, 0x0d410042, 0x0d410040, 0x0d41003e, - 0x0d41003c, 0x0d41003b, 0x0d410039, 0x0d410037, - 0x0c410044, 0x0c410042, 0x0c410040, 0x0c41003e, - 0x0c41003c, 0x0c41003b, 0x0c410039, 0x0c410037, - 0x0b410044, 0x0b410042, 0x0b410040, 0x0b41003e, - 0x0b41003c, 0x0b41003b, 0x0b410039, 0x0b410037, - 0x0a410044, 0x0a410042, 0x0a410040, 0x0a41003e, - 0x0a41003c, 0x0a41003b, 0x0a410039, 0x0a410037, - 0x09410044, 0x09410042, 0x09410040, 0x0941003e, - 0x0941003c, 0x0941003b, 0x09410039, 0x09410037, - 0x08410044, 0x08410042, 0x08410040, 0x0841003e, - 0x0841003c, 0x0841003b, 0x08410039, 0x08410037, - 0x07410044, 0x07410042, 0x07410040, 0x0741003e, - 0x0741003c, 0x0741003b, 0x07410039, 0x07410037, - 0x06410044, 0x06410042, 0x06410040, 0x0641003e, - 0x0641003c, 0x0641003b, 0x06410039, 0x06410037, - 0x05410044, 0x05410042, 0x05410040, 0x0541003e, - 0x0541003c, 0x0541003b, 0x05410039, 0x05410037, - 0x04410044, 0x04410042, 0x04410040, 0x0441003e, - 0x0441003c, 0x0441003b, 0x04410039, 0x04410037, - 0x03410044, 0x03410042, 0x03410040, 0x0341003e, - 0x0341003c, 0x0341003b, 0x03410039, 0x03410037, - 0x02410044, 0x02410042, 0x02410040, 0x0241003e, - 0x0241003c, 0x0241003b, 0x02410039, 0x02410037, - 0x01410044, 0x01410042, 0x01410040, 0x0141003e, - 0x0141003c, 0x0141003b, 0x01410039, 0x01410037, - 0x00410044, 0x00410042, 0x00410040, 0x0041003e, - 0x0041003c, 0x0041003b, 0x00410039, 0x00410037 -}; - -/* EPA 5 GHz */ - -static const u32 b43_ntab_tx_gain_epa_rev3_5g[] = { - 0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e, - 0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037, - 0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e, - 0xcef7003c, 0xcef7003b, 0xcef70039, 0xcef70037, - 0xcdf70044, 0xcdf70042, 0xcdf70040, 0xcdf7003e, - 0xcdf7003c, 0xcdf7003b, 0xcdf70039, 0xcdf70037, - 0xccf70044, 0xccf70042, 0xccf70040, 0xccf7003e, - 0xccf7003c, 0xccf7003b, 0xccf70039, 0xccf70037, - 0xcbf70044, 0xcbf70042, 0xcbf70040, 0xcbf7003e, - 0xcbf7003c, 0xcbf7003b, 0xcbf70039, 0xcbf70037, - 0xcaf70044, 0xcaf70042, 0xcaf70040, 0xcaf7003e, - 0xcaf7003c, 0xcaf7003b, 0xcaf70039, 0xcaf70037, - 0xc9f70044, 0xc9f70042, 0xc9f70040, 0xc9f7003e, - 0xc9f7003c, 0xc9f7003b, 0xc9f70039, 0xc9f70037, - 0xc8f70044, 0xc8f70042, 0xc8f70040, 0xc8f7003e, - 0xc8f7003c, 0xc8f7003b, 0xc8f70039, 0xc8f70037, - 0xc7f70044, 0xc7f70042, 0xc7f70040, 0xc7f7003e, - 0xc7f7003c, 0xc7f7003b, 0xc7f70039, 0xc7f70037, - 0xc6f70044, 0xc6f70042, 0xc6f70040, 0xc6f7003e, - 0xc6f7003c, 0xc6f7003b, 0xc6f70039, 0xc6f70037, - 0xc5f70044, 0xc5f70042, 0xc5f70040, 0xc5f7003e, - 0xc5f7003c, 0xc5f7003b, 0xc5f70039, 0xc5f70037, - 0xc4f70044, 0xc4f70042, 0xc4f70040, 0xc4f7003e, - 0xc4f7003c, 0xc4f7003b, 0xc4f70039, 0xc4f70037, - 0xc3f70044, 0xc3f70042, 0xc3f70040, 0xc3f7003e, - 0xc3f7003c, 0xc3f7003b, 0xc3f70039, 0xc3f70037, - 0xc2f70044, 0xc2f70042, 0xc2f70040, 0xc2f7003e, - 0xc2f7003c, 0xc2f7003b, 0xc2f70039, 0xc2f70037, - 0xc1f70044, 0xc1f70042, 0xc1f70040, 0xc1f7003e, - 0xc1f7003c, 0xc1f7003b, 0xc1f70039, 0xc1f70037, - 0xc0f70044, 0xc0f70042, 0xc0f70040, 0xc0f7003e, - 0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037, -}; - -static const u32 b43_ntab_tx_gain_epa_rev4_5g[] = { - 0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e, - 0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037, - 0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e, - 0x2ef2003c, 0x2ef2003b, 0x2ef20039, 0x2ef20037, - 0x2df20044, 0x2df20042, 0x2df20040, 0x2df2003e, - 0x2df2003c, 0x2df2003b, 0x2df20039, 0x2df20037, - 0x2cf20044, 0x2cf20042, 0x2cf20040, 0x2cf2003e, - 0x2cf2003c, 0x2cf2003b, 0x2cf20039, 0x2cf20037, - 0x2bf20044, 0x2bf20042, 0x2bf20040, 0x2bf2003e, - 0x2bf2003c, 0x2bf2003b, 0x2bf20039, 0x2bf20037, - 0x2af20044, 0x2af20042, 0x2af20040, 0x2af2003e, - 0x2af2003c, 0x2af2003b, 0x2af20039, 0x2af20037, - 0x29f20044, 0x29f20042, 0x29f20040, 0x29f2003e, - 0x29f2003c, 0x29f2003b, 0x29f20039, 0x29f20037, - 0x28f20044, 0x28f20042, 0x28f20040, 0x28f2003e, - 0x28f2003c, 0x28f2003b, 0x28f20039, 0x28f20037, - 0x27f20044, 0x27f20042, 0x27f20040, 0x27f2003e, - 0x27f2003c, 0x27f2003b, 0x27f20039, 0x27f20037, - 0x26f20044, 0x26f20042, 0x26f20040, 0x26f2003e, - 0x26f2003c, 0x26f2003b, 0x26f20039, 0x26f20037, - 0x25f20044, 0x25f20042, 0x25f20040, 0x25f2003e, - 0x25f2003c, 0x25f2003b, 0x25f20039, 0x25f20037, - 0x24f20044, 0x24f20042, 0x24f20040, 0x24f2003e, - 0x24f2003c, 0x24f2003b, 0x24f20039, 0x24f20038, - 0x23f20041, 0x23f20040, 0x23f2003f, 0x23f2003e, - 0x23f2003c, 0x23f2003b, 0x23f20039, 0x23f20037, - 0x22f20044, 0x22f20042, 0x22f20040, 0x22f2003e, - 0x22f2003c, 0x22f2003b, 0x22f20039, 0x22f20037, - 0x21f20044, 0x21f20042, 0x21f20040, 0x21f2003e, - 0x21f2003c, 0x21f2003b, 0x21f20039, 0x21f20037, - 0x20d20043, 0x20d20041, 0x20d2003e, 0x20d2003c, - 0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034, -}; - -static const u32 b43_ntab_tx_gain_epa_rev4_hi_pwr_5g[] = { - 0x2ff10044, 0x2ff10042, 0x2ff10040, 0x2ff1003e, - 0x2ff1003c, 0x2ff1003b, 0x2ff10039, 0x2ff10037, - 0x2ef10044, 0x2ef10042, 0x2ef10040, 0x2ef1003e, - 0x2ef1003c, 0x2ef1003b, 0x2ef10039, 0x2ef10037, - 0x2df10044, 0x2df10042, 0x2df10040, 0x2df1003e, - 0x2df1003c, 0x2df1003b, 0x2df10039, 0x2df10037, - 0x2cf10044, 0x2cf10042, 0x2cf10040, 0x2cf1003e, - 0x2cf1003c, 0x2cf1003b, 0x2cf10039, 0x2cf10037, - 0x2bf10044, 0x2bf10042, 0x2bf10040, 0x2bf1003e, - 0x2bf1003c, 0x2bf1003b, 0x2bf10039, 0x2bf10037, - 0x2af10044, 0x2af10042, 0x2af10040, 0x2af1003e, - 0x2af1003c, 0x2af1003b, 0x2af10039, 0x2af10037, - 0x29f10044, 0x29f10042, 0x29f10040, 0x29f1003e, - 0x29f1003c, 0x29f1003b, 0x29f10039, 0x29f10037, - 0x28f10044, 0x28f10042, 0x28f10040, 0x28f1003e, - 0x28f1003c, 0x28f1003b, 0x28f10039, 0x28f10037, - 0x27f10044, 0x27f10042, 0x27f10040, 0x27f1003e, - 0x27f1003c, 0x27f1003b, 0x27f10039, 0x27f10037, - 0x26f10044, 0x26f10042, 0x26f10040, 0x26f1003e, - 0x26f1003c, 0x26f1003b, 0x26f10039, 0x26f10037, - 0x25f10044, 0x25f10042, 0x25f10040, 0x25f1003e, - 0x25f1003c, 0x25f1003b, 0x25f10039, 0x25f10037, - 0x24f10044, 0x24f10042, 0x24f10040, 0x24f1003e, - 0x24f1003c, 0x24f1003b, 0x24f10039, 0x24f10038, - 0x23f10041, 0x23f10040, 0x23f1003f, 0x23f1003e, - 0x23f1003c, 0x23f1003b, 0x23f10039, 0x23f10037, - 0x22f10044, 0x22f10042, 0x22f10040, 0x22f1003e, - 0x22f1003c, 0x22f1003b, 0x22f10039, 0x22f10037, - 0x21f10044, 0x21f10042, 0x21f10040, 0x21f1003e, - 0x21f1003c, 0x21f1003b, 0x21f10039, 0x21f10037, - 0x20d10043, 0x20d10041, 0x20d1003e, 0x20d1003c, - 0x20d1003a, 0x20d10038, 0x20d10036, 0x20d10034 -}; - -static const u32 b43_ntab_tx_gain_epa_rev5_5g[] = { - 0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044, - 0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c, - 0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e, - 0x0e62003c, 0x0e62003d, 0x0e62003b, 0x0e62003a, - 0x0d620043, 0x0d620041, 0x0d620040, 0x0d62003e, - 0x0d62003d, 0x0d62003c, 0x0d62003b, 0x0d62003a, - 0x0c620041, 0x0c620040, 0x0c62003f, 0x0c62003e, - 0x0c62003c, 0x0c62003b, 0x0c620039, 0x0c620037, - 0x0b620046, 0x0b620044, 0x0b620042, 0x0b620040, - 0x0b62003e, 0x0b62003c, 0x0b62003b, 0x0b62003a, - 0x0a620041, 0x0a620040, 0x0a62003e, 0x0a62003c, - 0x0a62003b, 0x0a62003a, 0x0a620039, 0x0a620038, - 0x0962003e, 0x0962003d, 0x0962003c, 0x0962003b, - 0x09620039, 0x09620037, 0x09620035, 0x09620033, - 0x08620044, 0x08620042, 0x08620040, 0x0862003e, - 0x0862003c, 0x0862003b, 0x0862003a, 0x08620039, - 0x07620043, 0x07620042, 0x07620040, 0x0762003f, - 0x0762003d, 0x0762003b, 0x0762003a, 0x07620039, - 0x0662003e, 0x0662003d, 0x0662003c, 0x0662003b, - 0x06620039, 0x06620037, 0x06620035, 0x06620033, - 0x05620046, 0x05620044, 0x05620042, 0x05620040, - 0x0562003e, 0x0562003c, 0x0562003b, 0x05620039, - 0x04620044, 0x04620042, 0x04620040, 0x0462003e, - 0x0462003c, 0x0462003b, 0x04620039, 0x04620038, - 0x0362003c, 0x0362003b, 0x0362003a, 0x03620039, - 0x03620038, 0x03620037, 0x03620035, 0x03620033, - 0x0262004c, 0x0262004a, 0x02620048, 0x02620047, - 0x02620046, 0x02620044, 0x02620043, 0x02620042, - 0x0162004a, 0x01620048, 0x01620046, 0x01620044, - 0x01620043, 0x01620042, 0x01620041, 0x01620040, - 0x00620042, 0x00620040, 0x0062003e, 0x0062003c, - 0x0062003b, 0x00620039, 0x00620037, 0x00620035, -}; - -/* IPA 2 GHz */ - -static const u32 b43_ntab_tx_gain_ipa_rev3_2g[] = { - 0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029, - 0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025, - 0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029, - 0x5ef70028, 0x5ef70027, 0x5ef70026, 0x5ef70025, - 0x5df7002d, 0x5df7002b, 0x5df7002a, 0x5df70029, - 0x5df70028, 0x5df70027, 0x5df70026, 0x5df70025, - 0x5cf7002d, 0x5cf7002b, 0x5cf7002a, 0x5cf70029, - 0x5cf70028, 0x5cf70027, 0x5cf70026, 0x5cf70025, - 0x5bf7002d, 0x5bf7002b, 0x5bf7002a, 0x5bf70029, - 0x5bf70028, 0x5bf70027, 0x5bf70026, 0x5bf70025, - 0x5af7002d, 0x5af7002b, 0x5af7002a, 0x5af70029, - 0x5af70028, 0x5af70027, 0x5af70026, 0x5af70025, - 0x59f7002d, 0x59f7002b, 0x59f7002a, 0x59f70029, - 0x59f70028, 0x59f70027, 0x59f70026, 0x59f70025, - 0x58f7002d, 0x58f7002b, 0x58f7002a, 0x58f70029, - 0x58f70028, 0x58f70027, 0x58f70026, 0x58f70025, - 0x57f7002d, 0x57f7002b, 0x57f7002a, 0x57f70029, - 0x57f70028, 0x57f70027, 0x57f70026, 0x57f70025, - 0x56f7002d, 0x56f7002b, 0x56f7002a, 0x56f70029, - 0x56f70028, 0x56f70027, 0x56f70026, 0x56f70025, - 0x55f7002d, 0x55f7002b, 0x55f7002a, 0x55f70029, - 0x55f70028, 0x55f70027, 0x55f70026, 0x55f70025, - 0x54f7002d, 0x54f7002b, 0x54f7002a, 0x54f70029, - 0x54f70028, 0x54f70027, 0x54f70026, 0x54f70025, - 0x53f7002d, 0x53f7002b, 0x53f7002a, 0x53f70029, - 0x53f70028, 0x53f70027, 0x53f70026, 0x53f70025, - 0x52f7002d, 0x52f7002b, 0x52f7002a, 0x52f70029, - 0x52f70028, 0x52f70027, 0x52f70026, 0x52f70025, - 0x51f7002d, 0x51f7002b, 0x51f7002a, 0x51f70029, - 0x51f70028, 0x51f70027, 0x51f70026, 0x51f70025, - 0x50f7002d, 0x50f7002b, 0x50f7002a, 0x50f70029, - 0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025, -}; - -static const u32 b43_ntab_tx_gain_ipa_rev5_2g[] = { - 0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029, - 0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025, - 0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029, - 0x1ef70028, 0x1ef70027, 0x1ef70026, 0x1ef70025, - 0x1df7002d, 0x1df7002b, 0x1df7002a, 0x1df70029, - 0x1df70028, 0x1df70027, 0x1df70026, 0x1df70025, - 0x1cf7002d, 0x1cf7002b, 0x1cf7002a, 0x1cf70029, - 0x1cf70028, 0x1cf70027, 0x1cf70026, 0x1cf70025, - 0x1bf7002d, 0x1bf7002b, 0x1bf7002a, 0x1bf70029, - 0x1bf70028, 0x1bf70027, 0x1bf70026, 0x1bf70025, - 0x1af7002d, 0x1af7002b, 0x1af7002a, 0x1af70029, - 0x1af70028, 0x1af70027, 0x1af70026, 0x1af70025, - 0x19f7002d, 0x19f7002b, 0x19f7002a, 0x19f70029, - 0x19f70028, 0x19f70027, 0x19f70026, 0x19f70025, - 0x18f7002d, 0x18f7002b, 0x18f7002a, 0x18f70029, - 0x18f70028, 0x18f70027, 0x18f70026, 0x18f70025, - 0x17f7002d, 0x17f7002b, 0x17f7002a, 0x17f70029, - 0x17f70028, 0x17f70027, 0x17f70026, 0x17f70025, - 0x16f7002d, 0x16f7002b, 0x16f7002a, 0x16f70029, - 0x16f70028, 0x16f70027, 0x16f70026, 0x16f70025, - 0x15f7002d, 0x15f7002b, 0x15f7002a, 0x15f70029, - 0x15f70028, 0x15f70027, 0x15f70026, 0x15f70025, - 0x14f7002d, 0x14f7002b, 0x14f7002a, 0x14f70029, - 0x14f70028, 0x14f70027, 0x14f70026, 0x14f70025, - 0x13f7002d, 0x13f7002b, 0x13f7002a, 0x13f70029, - 0x13f70028, 0x13f70027, 0x13f70026, 0x13f70025, - 0x12f7002d, 0x12f7002b, 0x12f7002a, 0x12f70029, - 0x12f70028, 0x12f70027, 0x12f70026, 0x12f70025, - 0x11f7002d, 0x11f7002b, 0x11f7002a, 0x11f70029, - 0x11f70028, 0x11f70027, 0x11f70026, 0x11f70025, - 0x10f7002d, 0x10f7002b, 0x10f7002a, 0x10f70029, - 0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025, -}; - -static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = { - 0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029, - 0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025, - 0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029, - 0x0ef70028, 0x0ef70027, 0x0ef70026, 0x0ef70025, - 0x0df7002d, 0x0df7002b, 0x0df7002a, 0x0df70029, - 0x0df70028, 0x0df70027, 0x0df70026, 0x0df70025, - 0x0cf7002d, 0x0cf7002b, 0x0cf7002a, 0x0cf70029, - 0x0cf70028, 0x0cf70027, 0x0cf70026, 0x0cf70025, - 0x0bf7002d, 0x0bf7002b, 0x0bf7002a, 0x0bf70029, - 0x0bf70028, 0x0bf70027, 0x0bf70026, 0x0bf70025, - 0x0af7002d, 0x0af7002b, 0x0af7002a, 0x0af70029, - 0x0af70028, 0x0af70027, 0x0af70026, 0x0af70025, - 0x09f7002d, 0x09f7002b, 0x09f7002a, 0x09f70029, - 0x09f70028, 0x09f70027, 0x09f70026, 0x09f70025, - 0x08f7002d, 0x08f7002b, 0x08f7002a, 0x08f70029, - 0x08f70028, 0x08f70027, 0x08f70026, 0x08f70025, - 0x07f7002d, 0x07f7002b, 0x07f7002a, 0x07f70029, - 0x07f70028, 0x07f70027, 0x07f70026, 0x07f70025, - 0x06f7002d, 0x06f7002b, 0x06f7002a, 0x06f70029, - 0x06f70028, 0x06f70027, 0x06f70026, 0x06f70025, - 0x05f7002d, 0x05f7002b, 0x05f7002a, 0x05f70029, - 0x05f70028, 0x05f70027, 0x05f70026, 0x05f70025, - 0x04f7002d, 0x04f7002b, 0x04f7002a, 0x04f70029, - 0x04f70028, 0x04f70027, 0x04f70026, 0x04f70025, - 0x03f7002d, 0x03f7002b, 0x03f7002a, 0x03f70029, - 0x03f70028, 0x03f70027, 0x03f70026, 0x03f70025, - 0x02f7002d, 0x02f7002b, 0x02f7002a, 0x02f70029, - 0x02f70028, 0x02f70027, 0x02f70026, 0x02f70025, - 0x01f7002d, 0x01f7002b, 0x01f7002a, 0x01f70029, - 0x01f70028, 0x01f70027, 0x01f70026, 0x01f70025, - 0x00f7002d, 0x00f7002b, 0x00f7002a, 0x00f70029, - 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025, -}; - -/* Copied from brcmsmac (5.75.11): nphy_tpc_txgain_ipa_2g_2057rev5 */ -static const u32 b43_ntab_tx_gain_ipa_2057_rev5_2g[] = { - 0x30ff0031, 0x30e70031, 0x30e7002e, 0x30cf002e, - 0x30bf002e, 0x30af002e, 0x309f002f, 0x307f0033, - 0x307f0031, 0x307f002e, 0x3077002e, 0x306f002e, - 0x3067002e, 0x305f002f, 0x30570030, 0x3057002d, - 0x304f002e, 0x30470031, 0x3047002e, 0x3047002c, - 0x30470029, 0x303f002c, 0x303f0029, 0x3037002d, - 0x3037002a, 0x30370028, 0x302f002c, 0x302f002a, - 0x302f0028, 0x302f0026, 0x3027002c, 0x30270029, - 0x30270027, 0x30270025, 0x30270023, 0x301f002c, - 0x301f002a, 0x301f0028, 0x301f0025, 0x301f0024, - 0x301f0022, 0x301f001f, 0x3017002d, 0x3017002b, - 0x30170028, 0x30170026, 0x30170024, 0x30170022, - 0x30170020, 0x3017001e, 0x3017001d, 0x3017001b, - 0x3017001a, 0x30170018, 0x30170017, 0x30170015, - 0x300f002c, 0x300f0029, 0x300f0027, 0x300f0024, - 0x300f0022, 0x300f0021, 0x300f001f, 0x300f001d, - 0x300f001b, 0x300f001a, 0x300f0018, 0x300f0017, - 0x300f0016, 0x300f0015, 0x300f0115, 0x300f0215, - 0x300f0315, 0x300f0415, 0x300f0515, 0x300f0615, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, -}; - -/* Extracted from MMIO dump of 6.30.223.141 */ -static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = { - 0x60ff0031, 0x60e7002c, 0x60cf002a, 0x60c70029, - 0x60b70029, 0x60a70029, 0x609f002a, 0x6097002b, - 0x6087002e, 0x60770031, 0x606f0032, 0x60670034, - 0x60670031, 0x605f0033, 0x605f0031, 0x60570033, - 0x60570030, 0x6057002d, 0x6057002b, 0x604f002d, - 0x604f002b, 0x604f0029, 0x604f0026, 0x60470029, - 0x60470027, 0x603f0029, 0x603f0027, 0x603f0025, - 0x60370029, 0x60370027, 0x60370024, 0x602f002a, - 0x602f0028, 0x602f0026, 0x602f0024, 0x6027002a, - 0x60270028, 0x60270026, 0x60270024, 0x60270022, - 0x601f002b, 0x601f0029, 0x601f0027, 0x601f0024, - 0x601f0022, 0x601f0020, 0x601f001f, 0x601f001d, - 0x60170029, 0x60170027, 0x60170025, 0x60170023, - 0x60170021, 0x6017001f, 0x6017001d, 0x6017001c, - 0x6017001a, 0x60170018, 0x60170018, 0x60170016, - 0x60170015, 0x600f0029, 0x600f0027, 0x600f0025, - 0x600f0023, 0x600f0021, 0x600f001f, 0x600f001d, - 0x600f001c, 0x600f001a, 0x600f0019, 0x600f0018, - 0x600f0016, 0x600f0015, 0x600f0115, 0x600f0215, - 0x600f0315, 0x600f0415, 0x600f0515, 0x600f0615, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, - 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, -}; - -/* Extracted from MMIO dump of 6.30.223.248 */ -static const u32 b43_ntab_tx_gain_ipa_2057_rev14_2g[] = { - 0x50df002e, 0x50cf002d, 0x50bf002c, 0x50b7002b, - 0x50af002a, 0x50a70029, 0x509f0029, 0x50970028, - 0x508f0027, 0x50870027, 0x507f0027, 0x50770027, - 0x506f0027, 0x50670027, 0x505f0028, 0x50570029, - 0x504f002b, 0x5047002e, 0x5047002b, 0x50470029, - 0x503f002c, 0x503f0029, 0x5037002c, 0x5037002a, - 0x50370028, 0x502f002d, 0x502f002b, 0x502f0028, - 0x502f0026, 0x5027002d, 0x5027002a, 0x50270028, - 0x50270026, 0x50270024, 0x501f002e, 0x501f002b, - 0x501f0029, 0x501f0027, 0x501f0024, 0x501f0022, - 0x501f0020, 0x501f001f, 0x5017002c, 0x50170029, - 0x50170027, 0x50170024, 0x50170022, 0x50170021, - 0x5017001f, 0x5017001d, 0x5017001b, 0x5017001a, - 0x50170018, 0x50170017, 0x50170015, 0x500f002c, - 0x500f002a, 0x500f0027, 0x500f0025, 0x500f0023, - 0x500f0022, 0x500f001f, 0x500f001e, 0x500f001c, - 0x500f001a, 0x500f0019, 0x500f0018, 0x500f0016, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, - 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, -}; - -/* IPA 2 5Hz */ - -static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = { - 0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031, - 0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b, - 0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027, - 0x7ff70026, 0x7ff70024, 0x7ff70023, 0x7ff70022, - 0x7ef70028, 0x7ef70027, 0x7ef70026, 0x7ef70025, - 0x7ef70024, 0x7ef70023, 0x7df70028, 0x7df70027, - 0x7df70026, 0x7df70025, 0x7df70024, 0x7df70023, - 0x7df70022, 0x7cf70029, 0x7cf70028, 0x7cf70027, - 0x7cf70026, 0x7cf70025, 0x7cf70023, 0x7cf70022, - 0x7bf70029, 0x7bf70028, 0x7bf70026, 0x7bf70025, - 0x7bf70024, 0x7bf70023, 0x7bf70022, 0x7bf70021, - 0x7af70029, 0x7af70028, 0x7af70027, 0x7af70026, - 0x7af70025, 0x7af70024, 0x7af70023, 0x7af70022, - 0x79f70029, 0x79f70028, 0x79f70027, 0x79f70026, - 0x79f70025, 0x79f70024, 0x79f70023, 0x79f70022, - 0x78f70029, 0x78f70028, 0x78f70027, 0x78f70026, - 0x78f70025, 0x78f70024, 0x78f70023, 0x78f70022, - 0x77f70029, 0x77f70028, 0x77f70027, 0x77f70026, - 0x77f70025, 0x77f70024, 0x77f70023, 0x77f70022, - 0x76f70029, 0x76f70028, 0x76f70027, 0x76f70026, - 0x76f70024, 0x76f70023, 0x76f70022, 0x76f70021, - 0x75f70029, 0x75f70028, 0x75f70027, 0x75f70026, - 0x75f70025, 0x75f70024, 0x75f70023, 0x74f70029, - 0x74f70028, 0x74f70026, 0x74f70025, 0x74f70024, - 0x74f70023, 0x74f70022, 0x73f70029, 0x73f70027, - 0x73f70026, 0x73f70025, 0x73f70024, 0x73f70023, - 0x73f70022, 0x72f70028, 0x72f70027, 0x72f70026, - 0x72f70025, 0x72f70024, 0x72f70023, 0x72f70022, - 0x71f70028, 0x71f70027, 0x71f70026, 0x71f70025, - 0x71f70024, 0x71f70023, 0x70f70028, 0x70f70027, - 0x70f70026, 0x70f70024, 0x70f70023, 0x70f70022, - 0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f, -}; - -/* Extracted from MMIO dump of 6.30.223.141 */ -static const u32 b43_ntab_tx_gain_ipa_2057_rev9_5g[] = { - 0x7f7f0053, 0x7f7f004b, 0x7f7f0044, 0x7f7f003f, - 0x7f7f0039, 0x7f7f0035, 0x7f7f0032, 0x7f7f0030, - 0x7f7f002d, 0x7e7f0030, 0x7e7f002d, 0x7d7f0032, - 0x7d7f002f, 0x7d7f002c, 0x7c7f0032, 0x7c7f0030, - 0x7c7f002d, 0x7b7f0030, 0x7b7f002e, 0x7b7f002b, - 0x7a7f0032, 0x7a7f0030, 0x7a7f002d, 0x7a7f002b, - 0x797f0030, 0x797f002e, 0x797f002b, 0x797f0029, - 0x787f0030, 0x787f002d, 0x787f002b, 0x777f0032, - 0x777f0030, 0x777f002d, 0x777f002b, 0x767f0031, - 0x767f002f, 0x767f002c, 0x767f002a, 0x757f0031, - 0x757f002f, 0x757f002c, 0x757f002a, 0x747f0030, - 0x747f002d, 0x747f002b, 0x737f0032, 0x737f002f, - 0x737f002c, 0x737f002a, 0x727f0030, 0x727f002d, - 0x727f002b, 0x727f0029, 0x717f0030, 0x717f002d, - 0x717f002b, 0x707f0031, 0x707f002f, 0x707f002c, - 0x707f002a, 0x707f0027, 0x707f0025, 0x707f0023, - 0x707f0021, 0x707f001f, 0x707f001d, 0x707f001c, - 0x707f001a, 0x707f0019, 0x707f0017, 0x707f0016, - 0x707f0015, 0x707f0014, 0x707f0012, 0x707f0012, - 0x707f0011, 0x707f0010, 0x707f000f, 0x707f000e, - 0x707f000d, 0x707f000d, 0x707f000c, 0x707f000b, - 0x707f000a, 0x707f000a, 0x707f0009, 0x707f0008, - 0x707f0008, 0x707f0008, 0x707f0008, 0x707f0007, - 0x707f0007, 0x707f0006, 0x707f0006, 0x707f0006, - 0x707f0005, 0x707f0005, 0x707f0005, 0x707f0004, - 0x707f0004, 0x707f0004, 0x707f0003, 0x707f0003, - 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, - 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, - 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, - 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, - 0x707f0002, 0x707f0001, 0x707f0001, 0x707f0001, - 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001, -}; - -const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[] = { - -114, -108, -98, -91, -84, -78, -70, -62, - -54, -46, -39, -31, -23, -15, -8, 0 -}; - -/* Extracted from MMIO dump of 6.30.223.248 - * Entries: 0, 15, 17, 21, 24, 26, 27, 29, 30 were guessed - */ -static const s16 b43_ntab_rf_pwr_offset_2057_rev9_2g[] = { - -133, -133, -107, -92, -81, - -73, -66, -61, -56, -52, - -48, -44, -41, -37, -34, - -31, -28, -25, -22, -19, - -17, -14, -12, -10, -9, - -7, -5, -4, -3, -2, - -1, 0, -}; - -/* Extracted from MMIO dump of 6.30.223.248 */ -static const s16 b43_ntab_rf_pwr_offset_2057_rev9_5g[] = { - -101, -94, -86, -79, -72, - -65, -57, -50, -42, -35, - -28, -21, -16, -9, -4, - 0, -}; - -/* Extracted from MMIO dump of 6.30.223.248 - * Entries: 0, 26, 28, 29, 30, 31 were guessed - */ -static const s16 b43_ntab_rf_pwr_offset_2057_rev14_2g[] = { - -111, -111, -111, -84, -70, - -59, -52, -45, -40, -36, - -32, -29, -26, -23, -21, - -18, -16, -15, -13, -11, - -10, -8, -7, -6, -5, - -4, -4, -3, -3, -2, - -2, -1, -}; - -const u16 tbl_iqcal_gainparams[2][9][8] = { - { - { 0x000, 0, 0, 2, 0x69, 0x69, 0x69, 0x69 }, - { 0x700, 7, 0, 0, 0x69, 0x69, 0x69, 0x69 }, - { 0x710, 7, 1, 0, 0x68, 0x68, 0x68, 0x68 }, - { 0x720, 7, 2, 0, 0x67, 0x67, 0x67, 0x67 }, - { 0x730, 7, 3, 0, 0x66, 0x66, 0x66, 0x66 }, - { 0x740, 7, 4, 0, 0x65, 0x65, 0x65, 0x65 }, - { 0x741, 7, 4, 1, 0x65, 0x65, 0x65, 0x65 }, - { 0x742, 7, 4, 2, 0x65, 0x65, 0x65, 0x65 }, - { 0x743, 7, 4, 3, 0x65, 0x65, 0x65, 0x65 } - }, - { - { 0x000, 7, 0, 0, 0x79, 0x79, 0x79, 0x79 }, - { 0x700, 7, 0, 0, 0x79, 0x79, 0x79, 0x79 }, - { 0x710, 7, 1, 0, 0x79, 0x79, 0x79, 0x79 }, - { 0x720, 7, 2, 0, 0x78, 0x78, 0x78, 0x78 }, - { 0x730, 7, 3, 0, 0x78, 0x78, 0x78, 0x78 }, - { 0x740, 7, 4, 0, 0x78, 0x78, 0x78, 0x78 }, - { 0x741, 7, 4, 1, 0x78, 0x78, 0x78, 0x78 }, - { 0x742, 7, 4, 2, 0x78, 0x78, 0x78, 0x78 }, - { 0x743, 7, 4, 3, 0x78, 0x78, 0x78, 0x78 } - } -}; - -const struct nphy_txiqcal_ladder ladder_lo[] = { - { 3, 0 }, - { 4, 0 }, - { 6, 0 }, - { 9, 0 }, - { 13, 0 }, - { 18, 0 }, - { 25, 0 }, - { 25, 1 }, - { 25, 2 }, - { 25, 3 }, - { 25, 4 }, - { 25, 5 }, - { 25, 6 }, - { 25, 7 }, - { 35, 7 }, - { 50, 7 }, - { 71, 7 }, - { 100, 7 } -}; - -const struct nphy_txiqcal_ladder ladder_iq[] = { - { 3, 0 }, - { 4, 0 }, - { 6, 0 }, - { 9, 0 }, - { 13, 0 }, - { 18, 0 }, - { 25, 0 }, - { 35, 0 }, - { 50, 0 }, - { 71, 0 }, - { 100, 0 }, - { 100, 1 }, - { 100, 2 }, - { 100, 3 }, - { 100, 4 }, - { 100, 5 }, - { 100, 6 }, - { 100, 7 } -}; - -const u16 loscale[] = { - 256, 256, 271, 271, - 287, 256, 256, 271, - 271, 287, 287, 304, - 304, 256, 256, 271, - 271, 287, 287, 304, - 304, 322, 322, 341, - 341, 362, 362, 383, - 383, 256, 256, 271, - 271, 287, 287, 304, - 304, 322, 322, 256, - 256, 271, 271, 287, - 287, 304, 304, 322, - 322, 341, 341, 362, - 362, 256, 256, 271, - 271, 287, 287, 304, - 304, 322, 322, 256, - 256, 271, 271, 287, - 287, 304, 304, 322, - 322, 341, 341, 362, - 362, 256, 256, 271, - 271, 287, 287, 304, - 304, 322, 322, 341, - 341, 362, 362, 383, - 383, 406, 406, 430, - 430, 455, 455, 482, - 482, 511, 511, 541, - 541, 573, 573, 607, - 607, 643, 643, 681, - 681, 722, 722, 764, - 764, 810, 810, 858, - 858, 908, 908, 962, - 962, 1019, 1019, 256 -}; - -const u16 tbl_tx_iqlo_cal_loft_ladder_40[] = { - 0x0200, 0x0300, 0x0400, 0x0700, - 0x0900, 0x0c00, 0x1200, 0x1201, - 0x1202, 0x1203, 0x1204, 0x1205, - 0x1206, 0x1207, 0x1907, 0x2307, - 0x3207, 0x4707 -}; - -const u16 tbl_tx_iqlo_cal_loft_ladder_20[] = { - 0x0300, 0x0500, 0x0700, 0x0900, - 0x0d00, 0x1100, 0x1900, 0x1901, - 0x1902, 0x1903, 0x1904, 0x1905, - 0x1906, 0x1907, 0x2407, 0x3207, - 0x4607, 0x6407 -}; - -const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = { - 0x0100, 0x0200, 0x0400, 0x0700, - 0x0900, 0x0c00, 0x1200, 0x1900, - 0x2300, 0x3200, 0x4700, 0x4701, - 0x4702, 0x4703, 0x4704, 0x4705, - 0x4706, 0x4707 -}; - -const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = { - 0x0200, 0x0300, 0x0600, 0x0900, - 0x0d00, 0x1100, 0x1900, 0x2400, - 0x3200, 0x4600, 0x6400, 0x6401, - 0x6402, 0x6403, 0x6404, 0x6405, - 0x6406, 0x6407 -}; - -const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3] = { }; - -const u16 tbl_tx_iqlo_cal_startcoefs[B43_NTAB_TX_IQLO_CAL_STARTCOEFS] = { }; - -const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = { - 0x8423, 0x8323, 0x8073, 0x8256, - 0x8045, 0x8223, 0x9423, 0x9323, - 0x9073, 0x9256, 0x9045, 0x9223 -}; - -const u16 tbl_tx_iqlo_cal_cmds_recal[] = { - 0x8101, 0x8253, 0x8053, 0x8234, - 0x8034, 0x9101, 0x9253, 0x9053, - 0x9234, 0x9034 -}; - -const u16 tbl_tx_iqlo_cal_cmds_fullcal[] = { - 0x8123, 0x8264, 0x8086, 0x8245, - 0x8056, 0x9123, 0x9264, 0x9086, - 0x9245, 0x9056 -}; - -const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = { - 0x8434, 0x8334, 0x8084, 0x8267, - 0x8056, 0x8234, 0x9434, 0x9334, - 0x9084, 0x9267, 0x9056, 0x9234 -}; - -const s16 tbl_tx_filter_coef_rev4[7][15] = { - { -377, 137, -407, 208, -1527, - 956, 93, 186, 93, 230, - -44, 230, 201, -191, 201 }, - { -77, 20, -98, 49, -93, - 60, 56, 111, 56, 26, - -5, 26, 34, -32, 34 }, - { -360, 164, -376, 164, -1533, - 576, 308, -314, 308, 121, - -73, 121, 91, 124, 91 }, - { -295, 200, -363, 142, -1391, - 826, 151, 301, 151, 151, - 301, 151, 602, -752, 602 }, - { -92, 58, -96, 49, -104, - 44, 17, 35, 17, 12, - 25, 12, 13, 27, 13 }, - { -375, 136, -399, 209, -1479, - 949, 130, 260, 130, 230, - -44, 230, 201, -191, 201 }, - { 0xed9, 0xc8, 0xe95, 0x8e, 0xa91, - 0x33a, 0x97, 0x12d, 0x97, 0x97, - 0x12d, 0x97, 0x25a, 0xd10, 0x25a } -}; - -/* addr0, addr1, bmask, shift */ -const struct nphy_rf_control_override_rev2 tbl_rf_control_override_rev2[] = { - { 0x78, 0x78, 0x0038, 3 }, /* for field == 0x0002 (fls == 2) */ - { 0x7A, 0x7D, 0x0001, 0 }, /* for field == 0x0004 (fls == 3) */ - { 0x7A, 0x7D, 0x0002, 1 }, /* for field == 0x0008 (fls == 4) */ - { 0x7A, 0x7D, 0x0004, 2 }, /* for field == 0x0010 (fls == 5) */ - { 0x7A, 0x7D, 0x0030, 4 }, /* for field == 0x0020 (fls == 6) */ - { 0x7A, 0x7D, 0x00C0, 6 }, /* for field == 0x0040 (fls == 7) */ - { 0x7A, 0x7D, 0x0100, 8 }, /* for field == 0x0080 (fls == 8) */ - { 0x7A, 0x7D, 0x0200, 9 }, /* for field == 0x0100 (fls == 9) */ - { 0x78, 0x78, 0x0004, 2 }, /* for field == 0x0200 (fls == 10) */ - { 0x7B, 0x7E, 0x01FF, 0 }, /* for field == 0x0400 (fls == 11) */ - { 0x7C, 0x7F, 0x01FF, 0 }, /* for field == 0x0800 (fls == 12) */ - { 0x78, 0x78, 0x0100, 8 }, /* for field == 0x1000 (fls == 13) */ - { 0x78, 0x78, 0x0200, 9 }, /* for field == 0x2000 (fls == 14) */ - { 0x78, 0x78, 0xF000, 12 } /* for field == 0x4000 (fls == 15) */ -}; - -/* val_mask, val_shift, en_addr0, val_addr0, en_addr1, val_addr1 */ -const struct nphy_rf_control_override_rev3 tbl_rf_control_override_rev3[] = { - { 0x8000, 15, 0xE5, 0xF9, 0xE6, 0xFB }, /* field == 0x0001 (fls 1) */ - { 0x0001, 0, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0002 (fls 2) */ - { 0x0002, 1, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0004 (fls 3) */ - { 0x0004, 2, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0008 (fls 4) */ - { 0x0010, 4, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0010 (fls 5) */ - { 0x0020, 5, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0020 (fls 6) */ - { 0x0040, 6, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0040 (fls 7) */ - { 0x0080, 7, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0080 (fls 8) */ - { 0x0100, 8, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0100 (fls 9) */ - { 0x0007, 0, 0xE7, 0xF8, 0xEC, 0xFA }, /* field == 0x0200 (fls 10) */ - { 0x0070, 4, 0xE7, 0xF8, 0xEC, 0xFA }, /* field == 0x0400 (fls 11) */ - { 0xE000, 13, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0800 (fls 12) */ - { 0xFFFF, 0, 0xE7, 0x7B, 0xEC, 0x7E }, /* field == 0x1000 (fls 13) */ - { 0xFFFF, 0, 0xE7, 0x7C, 0xEC, 0x7F }, /* field == 0x2000 (fls 14) */ - { 0x00C0, 6, 0xE7, 0xF9, 0xEC, 0xFB } /* field == 0x4000 (fls 15) */ -}; - -/* field, val_addr_core0, val_addr_core1, val_mask, val_shift */ -static const struct nphy_rf_control_override_rev7 - tbl_rf_control_override_rev7_over0[] = { - { 0x0004, 0x07A, 0x07D, 0x0002, 1 }, - { 0x0008, 0x07A, 0x07D, 0x0004, 2 }, - { 0x0010, 0x07A, 0x07D, 0x0010, 4 }, - { 0x0020, 0x07A, 0x07D, 0x0020, 5 }, - { 0x0040, 0x07A, 0x07D, 0x0040, 6 }, - { 0x0080, 0x07A, 0x07D, 0x0080, 7 }, - { 0x0400, 0x0F8, 0x0FA, 0x0070, 4 }, - { 0x0800, 0x07B, 0x07E, 0xFFFF, 0 }, - { 0x1000, 0x07C, 0x07F, 0xFFFF, 0 }, - { 0x6000, 0x348, 0x349, 0x00FF, 0 }, - { 0x2000, 0x348, 0x349, 0x000F, 0 }, -}; - -/* field, val_addr_core0, val_addr_core1, val_mask, val_shift */ -static const struct nphy_rf_control_override_rev7 - tbl_rf_control_override_rev7_over1[] = { - { 0x0002, 0x340, 0x341, 0x0002, 1 }, - { 0x0008, 0x340, 0x341, 0x0008, 3 }, - { 0x0020, 0x340, 0x341, 0x0020, 5 }, - { 0x0010, 0x340, 0x341, 0x0010, 4 }, - { 0x0004, 0x340, 0x341, 0x0004, 2 }, - { 0x0080, 0x340, 0x341, 0x0700, 8 }, - { 0x0800, 0x340, 0x341, 0x4000, 14 }, - { 0x0400, 0x340, 0x341, 0x2000, 13 }, - { 0x0200, 0x340, 0x341, 0x0800, 12 }, - { 0x0100, 0x340, 0x341, 0x0100, 11 }, - { 0x0040, 0x340, 0x341, 0x0040, 6 }, - { 0x0001, 0x340, 0x341, 0x0001, 0 }, -}; - -/* field, val_addr_core0, val_addr_core1, val_mask, val_shift */ -static const struct nphy_rf_control_override_rev7 - tbl_rf_control_override_rev7_over2[] = { - { 0x0008, 0x344, 0x345, 0x0008, 3 }, - { 0x0002, 0x344, 0x345, 0x0002, 1 }, - { 0x0001, 0x344, 0x345, 0x0001, 0 }, - { 0x0004, 0x344, 0x345, 0x0004, 2 }, - { 0x0010, 0x344, 0x345, 0x0010, 4 }, -}; - -static struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_wa_phy6_radio11_ghz2 = { - { 10, 14, 19, 27 }, - { -5, 6, 10, 15 }, - { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, - { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, - 0x427E, - { 0x413F, 0x413F, 0x413F, 0x413F }, - 0x007E, 0x0066, 0x1074, - 0x18, 0x18, 0x18, - 0x01D0, 0x5, -}; -static struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_workaround[2][4] = { - { /* 2GHz */ - { /* PHY rev 3 */ - { 7, 11, 16, 23 }, - { -5, 6, 10, 14 }, - { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, - { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, - 0x627E, - { 0x613F, 0x613F, 0x613F, 0x613F }, - 0x107E, 0x0066, 0x0074, - 0x18, 0x18, 0x18, - 0x020D, 0x5, - }, - { /* PHY rev 4 */ - { 8, 12, 17, 25 }, - { -5, 6, 10, 14 }, - { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, - { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, - 0x527E, - { 0x513F, 0x513F, 0x513F, 0x513F }, - 0x007E, 0x0066, 0x0074, - 0x18, 0x18, 0x18, - 0x01A1, 0x5, - }, - { /* PHY rev 5 */ - { 9, 13, 18, 26 }, - { -3, 7, 11, 16 }, - { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, - { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, - 0x427E, /* invalid for external LNA! */ - { 0x413F, 0x413F, 0x413F, 0x413F }, /* invalid for external LNA! */ - 0x1076, 0x0066, 0x0000, /* low is invalid (the last one) */ - 0x18, 0x18, 0x18, - 0x01D0, 0x9, - }, - { /* PHY rev 6+ */ - { 8, 13, 18, 25 }, - { -5, 6, 10, 14 }, - { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, - { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, - 0x527E, /* invalid for external LNA! */ - { 0x513F, 0x513F, 0x513F, 0x513F }, /* invalid for external LNA! */ - 0x007E, 0x0066, 0x0000, /* low is invalid (the last one) */ - 0x18, 0x18, 0x18, - 0x01D0, 0x5, - }, - }, - { /* 5GHz */ - { /* PHY rev 3 */ - { 7, 11, 17, 23 }, - { -6, 2, 6, 10 }, - { 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13 }, - { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - 0x52DE, - { 0x516F, 0x516F, 0x516F, 0x516F }, - 0x00DE, 0x00CA, 0x00CC, - 0x1E, 0x1E, 0x1E, - 0x01A1, 25, - }, - { /* PHY rev 4 */ - { 8, 12, 18, 23 }, - { -5, 2, 6, 10 }, - { 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD }, - { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, - 0x629E, - { 0x614F, 0x614F, 0x614F, 0x614F }, - 0x029E, 0x1084, 0x0086, - 0x24, 0x24, 0x24, - 0x0107, 25, - }, - { /* PHY rev 5 */ - { 6, 10, 16, 21 }, - { -7, 0, 4, 8 }, - { 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD }, - { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, - 0x729E, - { 0x714F, 0x714F, 0x714F, 0x714F }, - 0x029E, 0x2084, 0x2086, - 0x24, 0x24, 0x24, - 0x00A9, 25, - }, - { /* PHY rev 6+ */ - { 6, 10, 16, 21 }, - { -7, 0, 4, 8 }, - { 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD }, - { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, - 0x729E, - { 0x714F, 0x714F, 0x714F, 0x714F }, - 0x029E, 0x2084, 0x2086, - 0x24, 0x24, 0x24, /* low is invalid for radio rev 11! */ - 0x00F0, 25, - }, - }, -}; - -static inline void assert_ntab_array_sizes(void) -{ -#undef check -#define check(table, size) \ - BUILD_BUG_ON(ARRAY_SIZE(b43_ntab_##table) != B43_NTAB_##size##_SIZE) - - check(adjustpower0, C0_ADJPLT); - check(adjustpower1, C1_ADJPLT); - check(bdi, BDI); - check(channelest, CHANEST); - check(estimatepowerlt0, C0_ESTPLT); - check(estimatepowerlt1, C1_ESTPLT); - check(framelookup, FRAMELT); - check(framestruct, FRAMESTRUCT); - check(gainctl0, C0_GAINCTL); - check(gainctl1, C1_GAINCTL); - check(intlevel, INTLEVEL); - check(iqlt0, C0_IQLT); - check(iqlt1, C1_IQLT); - check(loftlt0, C0_LOFEEDTH); - check(loftlt1, C1_LOFEEDTH); - check(mcs, MCS); - check(noisevar10, NOISEVAR10); - check(noisevar11, NOISEVAR11); - check(pilot, PILOT); - check(pilotlt, PILOTLT); - check(tdi20a0, TDI20A0); - check(tdi20a1, TDI20A1); - check(tdi40a0, TDI40A0); - check(tdi40a1, TDI40A1); - check(tdtrn, TDTRN); - check(tmap, TMAP); - -#undef check -} - -u32 b43_ntab_read(struct b43_wldev *dev, u32 offset) -{ - u32 type, value; - - type = offset & B43_NTAB_TYPEMASK; - offset &= ~B43_NTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - switch (type) { - case B43_NTAB_8BIT: - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_NPHY_TABLE_DATALO) & 0xFF; - break; - case B43_NTAB_16BIT: - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); - break; - case B43_NTAB_32BIT: - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); - value |= b43_phy_read(dev, B43_NPHY_TABLE_DATAHI) << 16; - break; - default: - B43_WARN_ON(1); - value = 0; - } - - return value; -} - -void b43_ntab_read_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, void *_data) -{ - u32 type; - u8 *data = _data; - unsigned int i; - - type = offset & B43_NTAB_TYPEMASK; - offset &= ~B43_NTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); - - for (i = 0; i < nr_elements; i++) { - /* Auto increment broken + caching issue on BCM43224? */ - if (dev->dev->chip_id == 43224 && dev->dev->chip_rev == 1) { - b43_phy_read(dev, B43_NPHY_TABLE_DATALO); - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset + i); - } - - switch (type) { - case B43_NTAB_8BIT: - *data = b43_phy_read(dev, B43_NPHY_TABLE_DATALO) & 0xFF; - data++; - break; - case B43_NTAB_16BIT: - *((u16 *)data) = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); - data += 2; - break; - case B43_NTAB_32BIT: - *((u32 *)data) = - b43_phy_read(dev, B43_NPHY_TABLE_DATALO); - *((u32 *)data) |= - b43_phy_read(dev, B43_NPHY_TABLE_DATAHI) << 16; - data += 4; - break; - default: - B43_WARN_ON(1); - } - } -} - -void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value) -{ - u32 type; - - type = offset & B43_NTAB_TYPEMASK; - offset &= 0xFFFF; - - switch (type) { - case B43_NTAB_8BIT: - B43_WARN_ON(value & ~0xFF); - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); - break; - case B43_NTAB_16BIT: - B43_WARN_ON(value & ~0xFFFF); - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); - break; - case B43_NTAB_32BIT: - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); - b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, value >> 16); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value & 0xFFFF); - break; - default: - B43_WARN_ON(1); - } - - return; - - /* Some compiletime assertions... */ - assert_ntab_array_sizes(); -} - -void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, const void *_data) -{ - u32 type, value; - const u8 *data = _data; - unsigned int i; - - type = offset & B43_NTAB_TYPEMASK; - offset &= ~B43_NTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); - - for (i = 0; i < nr_elements; i++) { - /* Auto increment broken + caching issue on BCM43224? */ - if ((offset >> 10) == 9 && dev->dev->chip_id == 43224 && - dev->dev->chip_rev == 1) { - b43_phy_read(dev, B43_NPHY_TABLE_DATALO); - b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset + i); - } - - switch (type) { - case B43_NTAB_8BIT: - value = *data; - data++; - B43_WARN_ON(value & ~0xFF); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); - break; - case B43_NTAB_16BIT: - value = *((u16 *)data); - data += 2; - B43_WARN_ON(value & ~0xFFFF); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); - break; - case B43_NTAB_32BIT: - value = *((u32 *)data); - data += 4; - b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, value >> 16); - b43_phy_write(dev, B43_NPHY_TABLE_DATALO, - value & 0xFFFF); - break; - default: - B43_WARN_ON(1); - } - } -} - -#define ntab_upload(dev, offset, data) do { \ - b43_ntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ - } while (0) - -static void b43_nphy_tables_init_shared_lut(struct b43_wldev *dev) -{ - ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3); - ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3); - ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); - ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); - ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); - ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); - ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); - ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); - ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); - ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); -} - -static void b43_nphy_tables_init_rev7_volatile(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - u8 antswlut; - int core, offset, i; - - const int antswlut0_offsets[] = { 0, 4, 8, }; /* Offsets for values */ - const u8 antswlut0_values[][3] = { - { 0x2, 0x12, 0x8 }, /* Core 0 */ - { 0x2, 0x18, 0x2 }, /* Core 1 */ - }; - - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - antswlut = sprom->fem.ghz5.antswlut; - else - antswlut = sprom->fem.ghz2.antswlut; - - switch (antswlut) { - case 0: - for (core = 0; core < 2; core++) { - for (i = 0; i < ARRAY_SIZE(antswlut0_values[0]); i++) { - offset = core ? 0x20 : 0x00; - offset += antswlut0_offsets[i]; - b43_ntab_write(dev, B43_NTAB8(9, offset), - antswlut0_values[core][i]); - } - } - break; - default: - b43err(dev->wl, "Unsupported antswlut: %d\n", antswlut); - break; - } -} - -static void b43_nphy_tables_init_rev16(struct b43_wldev *dev) -{ - /* Static tables */ - if (dev->phy.do_full_init) { - ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7); - b43_nphy_tables_init_shared_lut(dev); - } - - /* Volatile tables */ - b43_nphy_tables_init_rev7_volatile(dev); -} - -static void b43_nphy_tables_init_rev7(struct b43_wldev *dev) -{ - /* Static tables */ - if (dev->phy.do_full_init) { - ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); - ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); - ntab_upload(dev, B43_NTAB_TMAP_R7, b43_ntab_tmap_r7); - ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); - ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); - ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7); - ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); - ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); - ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); - ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); - ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); - ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); - ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); - ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); - b43_nphy_tables_init_shared_lut(dev); - } - - /* Volatile tables */ - b43_nphy_tables_init_rev7_volatile(dev); -} - -static void b43_nphy_tables_init_rev3(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - u8 antswlut; - - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) - antswlut = sprom->fem.ghz5.antswlut; - else - antswlut = sprom->fem.ghz2.antswlut; - - /* Static tables */ - if (dev->phy.do_full_init) { - ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); - ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); - ntab_upload(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3); - ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); - ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); - ntab_upload(dev, B43_NTAB_NOISEVAR_R3, b43_ntab_noisevar_r3); - ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); - ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); - ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); - ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); - ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); - ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); - ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); - ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); - b43_nphy_tables_init_shared_lut(dev); - } - - /* Volatile tables */ - if (antswlut < ARRAY_SIZE(b43_ntab_antswctl_r3)) - ntab_upload(dev, B43_NTAB_ANT_SW_CTL_R3, - b43_ntab_antswctl_r3[antswlut]); - else - B43_WARN_ON(1); -} - -static void b43_nphy_tables_init_rev0(struct b43_wldev *dev) -{ - /* Static tables */ - if (dev->phy.do_full_init) { - ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct); - ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup); - ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap); - ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn); - ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel); - ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot); - ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0); - ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1); - ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0); - ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1); - ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest); - ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs); - ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10); - ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11); - } - - /* Volatile tables */ - ntab_upload(dev, B43_NTAB_BDI, b43_ntab_bdi); - ntab_upload(dev, B43_NTAB_PILOTLT, b43_ntab_pilotlt); - ntab_upload(dev, B43_NTAB_C0_GAINCTL, b43_ntab_gainctl0); - ntab_upload(dev, B43_NTAB_C1_GAINCTL, b43_ntab_gainctl1); - ntab_upload(dev, B43_NTAB_C0_ESTPLT, b43_ntab_estimatepowerlt0); - ntab_upload(dev, B43_NTAB_C1_ESTPLT, b43_ntab_estimatepowerlt1); - ntab_upload(dev, B43_NTAB_C0_ADJPLT, b43_ntab_adjustpower0); - ntab_upload(dev, B43_NTAB_C1_ADJPLT, b43_ntab_adjustpower1); - ntab_upload(dev, B43_NTAB_C0_IQLT, b43_ntab_iqlt0); - ntab_upload(dev, B43_NTAB_C1_IQLT, b43_ntab_iqlt1); - ntab_upload(dev, B43_NTAB_C0_LOFEEDTH, b43_ntab_loftlt0); - ntab_upload(dev, B43_NTAB_C1_LOFEEDTH, b43_ntab_loftlt1); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/InitTables */ -void b43_nphy_tables_init(struct b43_wldev *dev) -{ - if (dev->phy.rev >= 16) - b43_nphy_tables_init_rev16(dev); - else if (dev->phy.rev >= 7) - b43_nphy_tables_init_rev7(dev); - else if (dev->phy.rev >= 3) - b43_nphy_tables_init_rev3(dev); - else - b43_nphy_tables_init_rev0(dev); -} - -/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */ -static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - switch (phy->rev) { - case 17: - if (phy->radio_rev == 14) - return b43_ntab_tx_gain_ipa_2057_rev14_2g; - break; - case 16: - if (phy->radio_rev == 9) - return b43_ntab_tx_gain_ipa_2057_rev9_2g; - break; - case 8: - if (phy->radio_rev == 5) - return b43_ntab_tx_gain_ipa_2057_rev5_2g; - break; - case 6: - if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) - return b43_ntab_tx_gain_ipa_rev5_2g; - return b43_ntab_tx_gain_ipa_rev6_2g; - case 5: - return b43_ntab_tx_gain_ipa_rev5_2g; - case 4: - case 3: - return b43_ntab_tx_gain_ipa_rev3_2g; - } - - b43err(dev->wl, - "No 2GHz IPA gain table available for this device\n"); - return NULL; - } else { - switch (phy->rev) { - case 16: - if (phy->radio_rev == 9) - return b43_ntab_tx_gain_ipa_2057_rev9_5g; - break; - case 3 ... 6: - return b43_ntab_tx_gain_ipa_rev3_5g; - } - - b43err(dev->wl, - "No 5GHz IPA gain table available for this device\n"); - return NULL; - } -} - -const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - enum ieee80211_band band = b43_current_band(dev->wl); - struct ssb_sprom *sprom = dev->dev->bus_sprom; - - if (dev->phy.rev < 3) - return b43_ntab_tx_gain_rev0_1_2; - - /* rev 3+ */ - if ((dev->phy.n->ipa2g_on && band == IEEE80211_BAND_2GHZ) || - (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)) { - return b43_nphy_get_ipa_gain_table(dev); - } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - switch (phy->rev) { - case 6: - case 5: - return b43_ntab_tx_gain_epa_rev5_5g; - case 4: - return sprom->fem.ghz5.extpa_gain == 3 ? - b43_ntab_tx_gain_epa_rev4_5g : - b43_ntab_tx_gain_epa_rev4_hi_pwr_5g; - case 3: - return b43_ntab_tx_gain_epa_rev3_5g; - default: - b43err(dev->wl, - "No 5GHz EPA gain table available for this device\n"); - return NULL; - } - } else { - switch (phy->rev) { - case 6: - case 5: - if (sprom->fem.ghz2.extpa_gain == 3) - return b43_ntab_tx_gain_epa_rev3_hi_pwr_2g; - /* fall through */ - case 4: - case 3: - return b43_ntab_tx_gain_epa_rev3_2g; - default: - b43err(dev->wl, - "No 2GHz EPA gain table available for this device\n"); - return NULL; - } - } -} - -const s16 *b43_ntab_get_rf_pwr_offset_table(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - switch (phy->rev) { - case 17: - if (phy->radio_rev == 14) - return b43_ntab_rf_pwr_offset_2057_rev14_2g; - break; - case 16: - if (phy->radio_rev == 9) - return b43_ntab_rf_pwr_offset_2057_rev9_2g; - break; - } - - b43err(dev->wl, - "No 2GHz RF power table available for this device\n"); - return NULL; - } else { - switch (phy->rev) { - case 16: - if (phy->radio_rev == 9) - return b43_ntab_rf_pwr_offset_2057_rev9_5g; - break; - } - - b43err(dev->wl, - "No 5GHz RF power table available for this device\n"); - return NULL; - } -} - -struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( - struct b43_wldev *dev, bool ghz5, bool ext_lna) -{ - struct b43_phy *phy = &dev->phy; - struct nphy_gain_ctl_workaround_entry *e; - u8 phy_idx; - - if (!ghz5 && dev->phy.rev >= 6 && dev->phy.radio_rev == 11) - return &nphy_gain_ctl_wa_phy6_radio11_ghz2; - - B43_WARN_ON(dev->phy.rev < 3); - if (dev->phy.rev >= 6) - phy_idx = 3; - else if (dev->phy.rev == 5) - phy_idx = 2; - else if (dev->phy.rev == 4) - phy_idx = 1; - else - phy_idx = 0; - e = &nphy_gain_ctl_workaround[ghz5][phy_idx]; - - /* Some workarounds to the workarounds... */ - if (!ghz5) { - u8 tr_iso = dev->dev->bus_sprom->fem.ghz2.tr_iso; - - if (tr_iso > 7) - tr_iso = 3; - - if (phy->rev >= 6) { - static const int gain_data[] = { 0x106a, 0x106c, 0x1074, - 0x107c, 0x007e, 0x107e, - 0x207e, 0x307e, }; - - e->cliplo_gain = gain_data[tr_iso]; - } else if (phy->rev == 5) { - static const int gain_data[] = { 0x0062, 0x0064, 0x006a, - 0x106a, 0x106c, 0x1074, - 0x107c, 0x207c, }; - - e->cliplo_gain = gain_data[tr_iso]; - } - - if (phy->rev >= 5 && ext_lna) { - e->rfseq_init[0] &= ~0x4000; - e->rfseq_init[1] &= ~0x4000; - e->rfseq_init[2] &= ~0x4000; - e->rfseq_init[3] &= ~0x4000; - e->init_gain &= ~0x4000; - } - } else { - if (phy->rev >= 6) { - if (phy->radio_rev == 11 && !b43_is_40mhz(dev)) - e->crsminu = 0x2d; - } else if (phy->rev == 4 && ext_lna) { - e->rfseq_init[0] &= ~0x4000; - e->rfseq_init[1] &= ~0x4000; - e->rfseq_init[2] &= ~0x4000; - e->rfseq_init[3] &= ~0x4000; - e->init_gain &= ~0x4000; - e->rfseq_init[0] |= 0x1000; - e->rfseq_init[1] |= 0x1000; - e->rfseq_init[2] |= 0x1000; - e->rfseq_init[3] |= 0x1000; - e->init_gain |= 0x1000; - } - } - - return e; -} - -const struct nphy_rf_control_override_rev7 *b43_nphy_get_rf_ctl_over_rev7( - struct b43_wldev *dev, u16 field, u8 override) -{ - const struct nphy_rf_control_override_rev7 *e; - u8 size, i; - - switch (override) { - case 0: - e = tbl_rf_control_override_rev7_over0; - size = ARRAY_SIZE(tbl_rf_control_override_rev7_over0); - break; - case 1: - e = tbl_rf_control_override_rev7_over1; - size = ARRAY_SIZE(tbl_rf_control_override_rev7_over1); - break; - case 2: - e = tbl_rf_control_override_rev7_over2; - size = ARRAY_SIZE(tbl_rf_control_override_rev7_over2); - break; - default: - b43err(dev->wl, "Invalid override value %d\n", override); - return NULL; - } - - for (i = 0; i < size; i++) { - if (e[i].field == field) - return &e[i]; - } - - return NULL; -} diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/b43/tables_nphy.h deleted file mode 100644 index b51f386db02f..000000000000 --- a/drivers/net/wireless/b43/tables_nphy.h +++ /dev/null @@ -1,222 +0,0 @@ -#ifndef B43_TABLES_NPHY_H_ -#define B43_TABLES_NPHY_H_ - -#include - -struct b43_phy_n_sfo_cfg { - u16 phy_bw1a; - u16 phy_bw2; - u16 phy_bw3; - u16 phy_bw4; - u16 phy_bw5; - u16 phy_bw6; -}; - -struct b43_wldev; - -struct nphy_txiqcal_ladder { - u8 percent; - u8 g_env; -}; - -struct nphy_rf_control_override_rev2 { - u8 addr0; - u8 addr1; - u16 bmask; - u8 shift; -}; - -struct nphy_rf_control_override_rev3 { - u16 val_mask; - u8 val_shift; - u8 en_addr0; - u8 val_addr0; - u8 en_addr1; - u8 val_addr1; -}; - -struct nphy_rf_control_override_rev7 { - u16 field; - u16 val_addr_core0; - u16 val_addr_core1; - u16 val_mask; - u8 val_shift; -}; - -struct nphy_gain_ctl_workaround_entry { - s8 lna1_gain[4]; - s8 lna2_gain[4]; - u8 gain_db[10]; - u8 gain_bits[10]; - - u16 init_gain; - u16 rfseq_init[4]; - - u16 cliphi_gain; - u16 clipmd_gain; - u16 cliplo_gain; - - u16 crsmin; - u16 crsminl; - u16 crsminu; - - u16 nbclip; - u16 wlclip; -}; - -/* Get entry with workaround values for gain ctl. Does not return NULL. */ -struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( - struct b43_wldev *dev, bool ghz5, bool ext_lna); - - -/* The N-PHY tables. */ -#define B43_NTAB_TYPEMASK 0xF0000000 -#define B43_NTAB_8BIT 0x10000000 -#define B43_NTAB_16BIT 0x20000000 -#define B43_NTAB_32BIT 0x30000000 -#define B43_NTAB8(table, offset) (((table) << 10) | (offset) | B43_NTAB_8BIT) -#define B43_NTAB16(table, offset) (((table) << 10) | (offset) | B43_NTAB_16BIT) -#define B43_NTAB32(table, offset) (((table) << 10) | (offset) | B43_NTAB_32BIT) - -/* Static N-PHY tables */ -#define B43_NTAB_FRAMESTRUCT B43_NTAB32(0x0A, 0x000) /* Frame Struct Table */ -#define B43_NTAB_FRAMESTRUCT_SIZE 832 -#define B43_NTAB_FRAMELT B43_NTAB8 (0x18, 0x000) /* Frame Lookup Table */ -#define B43_NTAB_FRAMELT_SIZE 32 -#define B43_NTAB_TMAP B43_NTAB32(0x0C, 0x000) /* T Map Table */ -#define B43_NTAB_TMAP_SIZE 448 -#define B43_NTAB_TDTRN B43_NTAB32(0x0E, 0x000) /* TDTRN Table */ -#define B43_NTAB_TDTRN_SIZE 704 -#define B43_NTAB_INTLEVEL B43_NTAB32(0x0D, 0x000) /* Int Level Table */ -#define B43_NTAB_INTLEVEL_SIZE 7 -#define B43_NTAB_PILOT B43_NTAB16(0x0B, 0x000) /* Pilot Table */ -#define B43_NTAB_PILOT_SIZE 88 -#define B43_NTAB_PILOTLT B43_NTAB32(0x14, 0x000) /* Pilot Lookup Table */ -#define B43_NTAB_PILOTLT_SIZE 6 -#define B43_NTAB_TDI20A0 B43_NTAB32(0x13, 0x080) /* TDI Table 20 Antenna 0 */ -#define B43_NTAB_TDI20A0_SIZE 55 -#define B43_NTAB_TDI20A1 B43_NTAB32(0x13, 0x100) /* TDI Table 20 Antenna 1 */ -#define B43_NTAB_TDI20A1_SIZE 55 -#define B43_NTAB_TDI40A0 B43_NTAB32(0x13, 0x280) /* TDI Table 40 Antenna 0 */ -#define B43_NTAB_TDI40A0_SIZE 110 -#define B43_NTAB_TDI40A1 B43_NTAB32(0x13, 0x300) /* TDI Table 40 Antenna 1 */ -#define B43_NTAB_TDI40A1_SIZE 110 -#define B43_NTAB_BDI B43_NTAB16(0x15, 0x000) /* BDI Table */ -#define B43_NTAB_BDI_SIZE 6 -#define B43_NTAB_CHANEST B43_NTAB32(0x16, 0x000) /* Channel Estimate Table */ -#define B43_NTAB_CHANEST_SIZE 96 -#define B43_NTAB_MCS B43_NTAB8 (0x12, 0x000) /* MCS Table */ -#define B43_NTAB_MCS_SIZE 128 - -/* Volatile N-PHY tables */ -#define B43_NTAB_NOISEVAR10 B43_NTAB32(0x10, 0x000) /* Noise Var Table 10 */ -#define B43_NTAB_NOISEVAR10_SIZE 256 -#define B43_NTAB_NOISEVAR11 B43_NTAB32(0x10, 0x080) /* Noise Var Table 11 */ -#define B43_NTAB_NOISEVAR11_SIZE 256 -#define B43_NTAB_C0_ESTPLT B43_NTAB8 (0x1A, 0x000) /* Estimate Power Lookup Table Core 0 */ -#define B43_NTAB_C0_ESTPLT_SIZE 64 -#define B43_NTAB_C0_ADJPLT B43_NTAB8 (0x1A, 0x040) /* Adjust Power Lookup Table Core 0 */ -#define B43_NTAB_C0_ADJPLT_SIZE 128 -#define B43_NTAB_C0_GAINCTL B43_NTAB32(0x1A, 0x0C0) /* Gain Control Lookup Table Core 0 */ -#define B43_NTAB_C0_GAINCTL_SIZE 128 -#define B43_NTAB_C0_IQLT B43_NTAB32(0x1A, 0x140) /* IQ Lookup Table Core 0 */ -#define B43_NTAB_C0_IQLT_SIZE 128 -#define B43_NTAB_C0_LOFEEDTH B43_NTAB16(0x1A, 0x1C0) /* Local Oscillator Feed Through Lookup Table Core 0 */ -#define B43_NTAB_C0_LOFEEDTH_SIZE 128 -#define B43_NTAB_C1_ESTPLT B43_NTAB8 (0x1B, 0x000) /* Estimate Power Lookup Table Core 1 */ -#define B43_NTAB_C1_ESTPLT_SIZE 64 -#define B43_NTAB_C1_ADJPLT B43_NTAB8 (0x1B, 0x040) /* Adjust Power Lookup Table Core 1 */ -#define B43_NTAB_C1_ADJPLT_SIZE 128 -#define B43_NTAB_C1_GAINCTL B43_NTAB32(0x1B, 0x0C0) /* Gain Control Lookup Table Core 1 */ -#define B43_NTAB_C1_GAINCTL_SIZE 128 -#define B43_NTAB_C1_IQLT B43_NTAB32(0x1B, 0x140) /* IQ Lookup Table Core 1 */ -#define B43_NTAB_C1_IQLT_SIZE 128 -#define B43_NTAB_C1_LOFEEDTH B43_NTAB16(0x1B, 0x1C0) /* Local Oscillator Feed Through Lookup Table Core 1 */ -#define B43_NTAB_C1_LOFEEDTH_SIZE 128 - -/* Volatile N-PHY tables, PHY revision >= 3 */ -#define B43_NTAB_ANT_SW_CTL_R3 B43_NTAB16( 9, 0) /* antenna software control */ - -/* Static N-PHY tables, PHY revision >= 3 */ -#define B43_NTAB_FRAMESTRUCT_R3 B43_NTAB32(10, 0) /* frame struct */ -#define B43_NTAB_PILOT_R3 B43_NTAB16(11, 0) /* pilot */ -#define B43_NTAB_TMAP_R3 B43_NTAB32(12, 0) /* TM AP */ -#define B43_NTAB_INTLEVEL_R3 B43_NTAB32(13, 0) /* INT LV */ -#define B43_NTAB_TDTRN_R3 B43_NTAB32(14, 0) /* TD TRN */ -#define B43_NTAB_NOISEVAR_R3 B43_NTAB32(16, 0) /* noise variance */ -#define B43_NTAB_MCS_R3 B43_NTAB16(18, 0) /* MCS */ -#define B43_NTAB_TDI20A0_R3 B43_NTAB32(19, 128) /* TDI 20/0 */ -#define B43_NTAB_TDI20A1_R3 B43_NTAB32(19, 256) /* TDI 20/1 */ -#define B43_NTAB_TDI40A0_R3 B43_NTAB32(19, 640) /* TDI 40/0 */ -#define B43_NTAB_TDI40A1_R3 B43_NTAB32(19, 768) /* TDI 40/1 */ -#define B43_NTAB_PILOTLT_R3 B43_NTAB32(20, 0) /* PLT lookup */ -#define B43_NTAB_CHANEST_R3 B43_NTAB32(22, 0) /* channel estimate */ -#define B43_NTAB_FRAMELT_R3 B43_NTAB8(24, 0) /* frame lookup */ -#define B43_NTAB_C0_ESTPLT_R3 B43_NTAB8(26, 0) /* estimated power lookup 0 */ -#define B43_NTAB_C0_ADJPLT_R3 B43_NTAB8(26, 64) /* adjusted power lookup 0 */ -#define B43_NTAB_C0_GAINCTL_R3 B43_NTAB32(26, 192) /* gain control lookup 0 */ -#define B43_NTAB_C0_IQLT_R3 B43_NTAB32(26, 320) /* I/Q lookup 0 */ -#define B43_NTAB_C0_LOFEEDTH_R3 B43_NTAB16(26, 448) /* Local Oscillator Feed Through lookup 0 */ -#define B43_NTAB_C0_PAPD_COMP_R3 B43_NTAB16(26, 576) -#define B43_NTAB_C1_ESTPLT_R3 B43_NTAB8(27, 0) /* estimated power lookup 1 */ -#define B43_NTAB_C1_ADJPLT_R3 B43_NTAB8(27, 64) /* adjusted power lookup 1 */ -#define B43_NTAB_C1_GAINCTL_R3 B43_NTAB32(27, 192) /* gain control lookup 1 */ -#define B43_NTAB_C1_IQLT_R3 B43_NTAB32(27, 320) /* I/Q lookup 1 */ -#define B43_NTAB_C1_LOFEEDTH_R3 B43_NTAB16(27, 448) /* Local Oscillator Feed Through lookup 1 */ -#define B43_NTAB_C1_PAPD_COMP_R3 B43_NTAB16(27, 576) - -/* Static N-PHY tables, PHY revision >= 7 */ -#define B43_NTAB_TMAP_R7 B43_NTAB32(12, 0) /* TM AP */ -#define B43_NTAB_NOISEVAR_R7 B43_NTAB32(16, 0) /* noise variance */ - -#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE 18 -#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE 18 -#define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE 18 -#define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_20_SIZE 18 -#define B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3 11 -#define B43_NTAB_TX_IQLO_CAL_STARTCOEFS 9 -#define B43_NTAB_TX_IQLO_CAL_CMDS_RECAL_REV3 12 -#define B43_NTAB_TX_IQLO_CAL_CMDS_RECAL 10 -#define B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL 10 -#define B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL_REV3 12 - -u32 b43_ntab_read(struct b43_wldev *dev, u32 offset); -void b43_ntab_read_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, void *_data); -void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value); -void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, const void *_data); - -void b43_nphy_tables_init(struct b43_wldev *dev); - -const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev); - -const s16 *b43_ntab_get_rf_pwr_offset_table(struct b43_wldev *dev); - -extern const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[]; - -extern const u16 tbl_iqcal_gainparams[2][9][8]; -extern const struct nphy_txiqcal_ladder ladder_lo[]; -extern const struct nphy_txiqcal_ladder ladder_iq[]; -extern const u16 loscale[]; - -extern const u16 tbl_tx_iqlo_cal_loft_ladder_40[]; -extern const u16 tbl_tx_iqlo_cal_loft_ladder_20[]; -extern const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[]; -extern const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[]; -extern const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[]; -extern const u16 tbl_tx_iqlo_cal_startcoefs[]; -extern const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[]; -extern const u16 tbl_tx_iqlo_cal_cmds_recal[]; -extern const u16 tbl_tx_iqlo_cal_cmds_fullcal[]; -extern const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[]; -extern const s16 tbl_tx_filter_coef_rev4[7][15]; - -extern const struct nphy_rf_control_override_rev2 - tbl_rf_control_override_rev2[]; -extern const struct nphy_rf_control_override_rev3 - tbl_rf_control_override_rev3[]; -const struct nphy_rf_control_override_rev7 *b43_nphy_get_rf_ctl_over_rev7( - struct b43_wldev *dev, u16 field, u8 override); - -#endif /* B43_TABLES_NPHY_H_ */ diff --git a/drivers/net/wireless/b43/tables_phy_ht.c b/drivers/net/wireless/b43/tables_phy_ht.c deleted file mode 100644 index 176c49d74ef4..000000000000 --- a/drivers/net/wireless/b43/tables_phy_ht.c +++ /dev/null @@ -1,836 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11n HT-PHY data tables - - Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "tables_phy_ht.h" -#include "phy_common.h" -#include "phy_ht.h" - -static const u16 b43_httab_0x12[] = { - 0x0000, 0x0008, 0x000a, 0x0010, 0x0012, 0x0019, - 0x001a, 0x001c, 0x0080, 0x0088, 0x008a, 0x0090, - 0x0092, 0x0099, 0x009a, 0x009c, 0x0100, 0x0108, - 0x010a, 0x0110, 0x0112, 0x0119, 0x011a, 0x011c, - 0x0180, 0x0188, 0x018a, 0x0190, 0x0192, 0x0199, - 0x019a, 0x019c, 0x0000, 0x0098, 0x00a0, 0x00a8, - 0x009a, 0x00a2, 0x00aa, 0x0120, 0x0128, 0x0128, - 0x0130, 0x0138, 0x0138, 0x0140, 0x0122, 0x012a, - 0x012a, 0x0132, 0x013a, 0x013a, 0x0142, 0x01a8, - 0x01b0, 0x01b8, 0x01b0, 0x01b8, 0x01c0, 0x01c8, - 0x01c0, 0x01c8, 0x01d0, 0x01d0, 0x01d8, 0x01aa, - 0x01b2, 0x01ba, 0x01b2, 0x01ba, 0x01c2, 0x01ca, - 0x01c2, 0x01ca, 0x01d2, 0x01d2, 0x01da, 0x0001, - 0x0002, 0x0004, 0x0009, 0x000c, 0x0011, 0x0014, - 0x0018, 0x0020, 0x0021, 0x0022, 0x0024, 0x0081, - 0x0082, 0x0084, 0x0089, 0x008c, 0x0091, 0x0094, - 0x0098, 0x00a0, 0x00a1, 0x00a2, 0x00a4, 0x0007, - 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, - 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, - 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, - 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, - 0x0007, 0x0007, -}; - -static const u16 b43_httab_0x27[] = { - 0x0009, 0x000e, 0x0011, 0x0014, 0x0017, 0x001a, - 0x001d, 0x0020, 0x0009, 0x000e, 0x0011, 0x0014, - 0x0017, 0x001a, 0x001d, 0x0020, 0x0009, 0x000e, - 0x0011, 0x0014, 0x0017, 0x001a, 0x001d, 0x0020, - 0x0009, 0x000e, 0x0011, 0x0014, 0x0017, 0x001a, - 0x001d, 0x0020, -}; - -static const u16 b43_httab_0x26[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, -}; - -static const u32 b43_httab_0x25[] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 b43_httab_0x2f[] = { - 0x00035700, 0x0002cc9a, 0x00026666, 0x0001581f, - 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, - 0x0001581f, 0x0001581f, 0x0001581f, 0x00035700, - 0x0002cc9a, 0x00026666, 0x0001581f, 0x0001581f, - 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, - 0x0001581f, 0x0001581f, -}; - -static const u16 b43_httab_0x1a[] = { - 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052, - 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e, - 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a, - 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046, - 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040, - 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a, - 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033, - 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b, - 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, - 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e, - 0x000b, 0x0007, 0x0002, 0x00fd, -}; - -static const u16 b43_httab_0x1b[] = { - 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052, - 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e, - 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a, - 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046, - 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040, - 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a, - 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033, - 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b, - 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, - 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e, - 0x000b, 0x0007, 0x0002, 0x00fd, -}; - -static const u16 b43_httab_0x1c[] = { - 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052, - 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e, - 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a, - 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046, - 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040, - 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a, - 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033, - 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b, - 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, - 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e, - 0x000b, 0x0007, 0x0002, 0x00fd, -}; - -static const u32 b43_httab_0x1a_0xc0[] = { - 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, - 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, - 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, - 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, - 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, - 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, - 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, - 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, - 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, - 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, - 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, - 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, - 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, - 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, - 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, - 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, - 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, - 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, - 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, - 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, - 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, - 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, - 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, - 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, - 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, - 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, - 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, - 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, - 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, - 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, - 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, - 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, -}; - -static const u32 b43_httab_0x1a_0x140[] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 b43_httab_0x1b_0x140[] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -static const u32 b43_httab_0x1c_0x140[] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -static const u16 b43_httab_0x1a_0x1c0[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, -}; - -static const u16 b43_httab_0x1b_0x1c0[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, -}; - -static const u16 b43_httab_0x1c_0x1c0[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, -}; - -static const u16 b43_httab_0x1a_0x240[] = { - 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, - 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, - 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, - 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, - 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, - 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, - 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e, - 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, - 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, - 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc, - 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, - 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, -}; - -static const u16 b43_httab_0x1b_0x240[] = { - 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, - 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, - 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, - 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, - 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, - 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, - 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e, - 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, - 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, - 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc, - 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, - 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, -}; - -static const u16 b43_httab_0x1c_0x240[] = { - 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, - 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, - 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, - 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, - 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, - 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, - 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e, - 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, - 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, - 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc, - 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, - 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, - 0x01d6, 0x01d6, -}; - -static const u32 b43_httab_0x1f[] = { - 0x00000000, 0x00000000, 0x00016023, 0x00006028, - 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037, - 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d, - 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9, - 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9, - 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6, - 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c, - 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b, - 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15, - 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9, - 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87, - 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3, - 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa, - 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff, - 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff, - 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff, -}; - -static const u32 b43_httab_0x21[] = { - 0x00000000, 0x00000000, 0x00016023, 0x00006028, - 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037, - 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d, - 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9, - 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9, - 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6, - 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c, - 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b, - 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15, - 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9, - 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87, - 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3, - 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa, - 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff, - 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff, - 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff, -}; - -static const u32 b43_httab_0x23[] = { - 0x00000000, 0x00000000, 0x00016023, 0x00006028, - 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037, - 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d, - 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9, - 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9, - 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6, - 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c, - 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b, - 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15, - 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9, - 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87, - 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3, - 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa, - 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff, - 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff, - 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff, -}; - -static const u32 b43_httab_0x20[] = { - 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035, - 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043, - 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, - 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, - 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, - 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, - 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, - 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, - 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e, - 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, - 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212, - 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b, - 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348, - 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422, - 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533, - 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c, -}; - -static const u32 b43_httab_0x22[] = { - 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035, - 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043, - 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, - 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, - 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, - 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, - 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, - 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, - 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e, - 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, - 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212, - 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b, - 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348, - 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422, - 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533, - 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c, -}; - -static const u32 b43_httab_0x24[] = { - 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035, - 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043, - 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, - 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, - 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, - 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, - 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, - 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, - 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e, - 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, - 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212, - 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b, - 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348, - 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422, - 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533, - 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c, -}; - -/* Some late-init table */ -const u32 b43_httab_0x1a_0xc0_late[] = { - 0x10f90040, 0x10e10040, 0x10e1003c, 0x10c9003d, - 0x10b9003c, 0x10a9003d, 0x10a1003c, 0x1099003b, - 0x1091003b, 0x1089003a, 0x1081003a, 0x10790039, - 0x10710039, 0x1069003a, 0x1061003b, 0x1059003d, - 0x1051003f, 0x10490042, 0x1049003e, 0x1049003b, - 0x1041003e, 0x1041003b, 0x1039003e, 0x1039003b, - 0x10390038, 0x10390035, 0x1031003a, 0x10310036, - 0x10310033, 0x1029003a, 0x10290037, 0x10290034, - 0x10290031, 0x10210039, 0x10210036, 0x10210033, - 0x10210030, 0x1019003c, 0x10190039, 0x10190036, - 0x10190033, 0x10190030, 0x1019002d, 0x1019002b, - 0x10190028, 0x1011003a, 0x10110036, 0x10110033, - 0x10110030, 0x1011002e, 0x1011002b, 0x10110029, - 0x10110027, 0x10110024, 0x10110022, 0x10110020, - 0x1011001f, 0x1011001d, 0x1009003a, 0x10090037, - 0x10090034, 0x10090031, 0x1009002e, 0x1009002c, - 0x10090029, 0x10090027, 0x10090025, 0x10090023, - 0x10090021, 0x1009001f, 0x1009001d, 0x1009001b, - 0x1009001a, 0x10090018, 0x10090017, 0x10090016, - 0x10090015, 0x10090013, 0x10090012, 0x10090011, - 0x10090010, 0x1009000f, 0x1009000f, 0x1009000e, - 0x1009000d, 0x1009000c, 0x1009000c, 0x1009000b, - 0x1009000a, 0x1009000a, 0x10090009, 0x10090009, - 0x10090008, 0x10090008, 0x10090007, 0x10090007, - 0x10090007, 0x10090006, 0x10090006, 0x10090005, - 0x10090005, 0x10090005, 0x10090005, 0x10090004, - 0x10090004, 0x10090004, 0x10090004, 0x10090003, - 0x10090003, 0x10090003, 0x10090003, 0x10090003, - 0x10090003, 0x10090002, 0x10090002, 0x10090002, - 0x10090002, 0x10090002, 0x10090002, 0x10090002, - 0x10090002, 0x10090002, 0x10090001, 0x10090001, - 0x10090001, 0x10090001, 0x10090001, 0x10090001, -}; - -/************************************************** - * R/W ops. - **************************************************/ - -u32 b43_httab_read(struct b43_wldev *dev, u32 offset) -{ - u32 type, value; - - type = offset & B43_HTTAB_TYPEMASK; - offset &= ~B43_HTTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - switch (type) { - case B43_HTTAB_8BIT: - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO) & 0xFF; - break; - case B43_HTTAB_16BIT: - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); - break; - case B43_HTTAB_32BIT: - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATAHI); - value <<= 16; - value |= b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); - break; - default: - B43_WARN_ON(1); - value = 0; - } - - return value; -} - -void b43_httab_read_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, void *_data) -{ - u32 type; - u8 *data = _data; - unsigned int i; - - type = offset & B43_HTTAB_TYPEMASK; - offset &= ~B43_HTTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); - - for (i = 0; i < nr_elements; i++) { - switch (type) { - case B43_HTTAB_8BIT: - *data = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO) & 0xFF; - data++; - break; - case B43_HTTAB_16BIT: - *((u16 *)data) = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); - data += 2; - break; - case B43_HTTAB_32BIT: - *((u32 *)data) = b43_phy_read(dev, B43_PHY_HT_TABLE_DATAHI); - *((u32 *)data) <<= 16; - *((u32 *)data) |= b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); - data += 4; - break; - default: - B43_WARN_ON(1); - } - } -} - -void b43_httab_write(struct b43_wldev *dev, u32 offset, u32 value) -{ - u32 type; - - type = offset & B43_HTTAB_TYPEMASK; - offset &= 0xFFFF; - - switch (type) { - case B43_HTTAB_8BIT: - B43_WARN_ON(value & ~0xFF); - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); - break; - case B43_HTTAB_16BIT: - B43_WARN_ON(value & ~0xFFFF); - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); - break; - case B43_HTTAB_32BIT: - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, value >> 16); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value & 0xFFFF); - break; - default: - B43_WARN_ON(1); - } - - return; -} - -void b43_httab_write_few(struct b43_wldev *dev, u32 offset, size_t num, ...) -{ - va_list args; - u32 type, value; - unsigned int i; - - type = offset & B43_HTTAB_TYPEMASK; - offset &= 0xFFFF; - - va_start(args, num); - switch (type) { - case B43_HTTAB_8BIT: - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); - for (i = 0; i < num; i++) { - value = va_arg(args, int); - B43_WARN_ON(value & ~0xFF); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); - } - break; - case B43_HTTAB_16BIT: - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); - for (i = 0; i < num; i++) { - value = va_arg(args, int); - B43_WARN_ON(value & ~0xFFFF); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); - } - break; - case B43_HTTAB_32BIT: - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); - for (i = 0; i < num; i++) { - value = va_arg(args, int); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, - value >> 16); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, - value & 0xFFFF); - } - break; - default: - B43_WARN_ON(1); - } - va_end(args); - - return; -} - -void b43_httab_write_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, const void *_data) -{ - u32 type, value; - const u8 *data = _data; - unsigned int i; - - type = offset & B43_HTTAB_TYPEMASK; - offset &= ~B43_HTTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); - - for (i = 0; i < nr_elements; i++) { - switch (type) { - case B43_HTTAB_8BIT: - value = *data; - data++; - B43_WARN_ON(value & ~0xFF); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); - break; - case B43_HTTAB_16BIT: - value = *((u16 *)data); - data += 2; - B43_WARN_ON(value & ~0xFFFF); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); - break; - case B43_HTTAB_32BIT: - value = *((u32 *)data); - data += 4; - b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, value >> 16); - b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, - value & 0xFFFF); - break; - default: - B43_WARN_ON(1); - } - } -} - -/************************************************** - * Tables ops. - **************************************************/ - -#define httab_upload(dev, offset, data) do { \ - b43_httab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ - } while (0) -void b43_phy_ht_tables_init(struct b43_wldev *dev) -{ - BUILD_BUG_ON(ARRAY_SIZE(b43_httab_0x1a_0xc0_late) != - B43_HTTAB_1A_C0_LATE_SIZE); - - httab_upload(dev, B43_HTTAB16(0x12, 0), b43_httab_0x12); - httab_upload(dev, B43_HTTAB16(0x27, 0), b43_httab_0x27); - httab_upload(dev, B43_HTTAB16(0x26, 0), b43_httab_0x26); - httab_upload(dev, B43_HTTAB32(0x25, 0), b43_httab_0x25); - httab_upload(dev, B43_HTTAB32(0x2f, 0), b43_httab_0x2f); - httab_upload(dev, B43_HTTAB16(0x1a, 0), b43_httab_0x1a); - httab_upload(dev, B43_HTTAB16(0x1b, 0), b43_httab_0x1b); - httab_upload(dev, B43_HTTAB16(0x1c, 0), b43_httab_0x1c); - httab_upload(dev, B43_HTTAB32(0x1a, 0x0c0), b43_httab_0x1a_0xc0); - httab_upload(dev, B43_HTTAB32(0x1a, 0x140), b43_httab_0x1a_0x140); - httab_upload(dev, B43_HTTAB32(0x1b, 0x140), b43_httab_0x1b_0x140); - httab_upload(dev, B43_HTTAB32(0x1c, 0x140), b43_httab_0x1c_0x140); - httab_upload(dev, B43_HTTAB16(0x1a, 0x1c0), b43_httab_0x1a_0x1c0); - httab_upload(dev, B43_HTTAB16(0x1b, 0x1c0), b43_httab_0x1b_0x1c0); - httab_upload(dev, B43_HTTAB16(0x1c, 0x1c0), b43_httab_0x1c_0x1c0); - httab_upload(dev, B43_HTTAB16(0x1a, 0x240), b43_httab_0x1a_0x240); - httab_upload(dev, B43_HTTAB16(0x1b, 0x240), b43_httab_0x1b_0x240); - httab_upload(dev, B43_HTTAB16(0x1c, 0x240), b43_httab_0x1c_0x240); - httab_upload(dev, B43_HTTAB32(0x1f, 0), b43_httab_0x1f); - httab_upload(dev, B43_HTTAB32(0x21, 0), b43_httab_0x21); - httab_upload(dev, B43_HTTAB32(0x23, 0), b43_httab_0x23); - httab_upload(dev, B43_HTTAB32(0x20, 0), b43_httab_0x20); - httab_upload(dev, B43_HTTAB32(0x22, 0), b43_httab_0x22); - httab_upload(dev, B43_HTTAB32(0x24, 0), b43_httab_0x24); -} diff --git a/drivers/net/wireless/b43/tables_phy_ht.h b/drivers/net/wireless/b43/tables_phy_ht.h deleted file mode 100644 index 1b5ef2bc770c..000000000000 --- a/drivers/net/wireless/b43/tables_phy_ht.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef B43_TABLES_PHY_HT_H_ -#define B43_TABLES_PHY_HT_H_ - -/* The HT-PHY tables. */ -#define B43_HTTAB_TYPEMASK 0xF0000000 -#define B43_HTTAB_8BIT 0x10000000 -#define B43_HTTAB_16BIT 0x20000000 -#define B43_HTTAB_32BIT 0x30000000 -#define B43_HTTAB8(table, offset) (((table) << 10) | (offset) | B43_HTTAB_8BIT) -#define B43_HTTAB16(table, offset) (((table) << 10) | (offset) | B43_HTTAB_16BIT) -#define B43_HTTAB32(table, offset) (((table) << 10) | (offset) | B43_HTTAB_32BIT) - -u32 b43_httab_read(struct b43_wldev *dev, u32 offset); -void b43_httab_read_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, void *_data); -void b43_httab_write(struct b43_wldev *dev, u32 offset, u32 value); -void b43_httab_write_few(struct b43_wldev *dev, u32 offset, size_t num, ...); -void b43_httab_write_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, const void *_data); - -void b43_phy_ht_tables_init(struct b43_wldev *dev); - -#define B43_HTTAB_1A_C0_LATE_SIZE 128 -extern const u32 b43_httab_0x1a_0xc0_late[]; - -#endif /* B43_TABLES_PHY_HT_H_ */ diff --git a/drivers/net/wireless/b43/tables_phy_lcn.c b/drivers/net/wireless/b43/tables_phy_lcn.c deleted file mode 100644 index e347b8d80ea4..000000000000 --- a/drivers/net/wireless/b43/tables_phy_lcn.c +++ /dev/null @@ -1,724 +0,0 @@ -/* - - Broadcom B43 wireless driver - IEEE 802.11n LCN-PHY data tables - - Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "tables_phy_lcn.h" -#include "phy_common.h" -#include "phy_lcn.h" - -struct b43_lcntab_tx_gain_tbl_entry { - u8 gm; - u8 pga; - u8 pad; - u8 dac; - u8 bb_mult; -}; - -/************************************************** - * Static tables. - **************************************************/ - -static const u16 b43_lcntab_0x02[] = { - 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, - 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, - 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, - 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, - 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, - 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, - 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, - 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, - 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, - 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, - 0x014d, 0x014d, 0x014d, 0x014d, -}; - -static const u16 b43_lcntab_0x01[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const u32 b43_lcntab_0x0b[] = { - 0x000141f8, 0x000021f8, 0x000021fb, 0x000041fb, - 0x0001fedb, 0x0000217b, 0x00002133, 0x000040eb, - 0x0001fea3, 0x0000024b, -}; - -static const u32 b43_lcntab_0x0c[] = { - 0x00100001, 0x00200010, 0x00300001, 0x00400010, - 0x00500022, 0x00600122, 0x00700222, 0x00800322, - 0x00900422, 0x00a00522, 0x00b00622, 0x00c00722, - 0x00d00822, 0x00f00922, 0x00100a22, 0x00200b22, - 0x00300c22, 0x00400d22, 0x00500e22, 0x00600f22, -}; - -static const u32 b43_lcntab_0x0d[] = { - 0x00000000, 0x00000000, 0x10000000, 0x00000000, - 0x20000000, 0x00000000, 0x30000000, 0x00000000, - 0x40000000, 0x00000000, 0x50000000, 0x00000000, - 0x60000000, 0x00000000, 0x70000000, 0x00000000, - 0x80000000, 0x00000000, 0x90000000, 0x00000008, - 0xa0000000, 0x00000008, 0xb0000000, 0x00000008, - 0xc0000000, 0x00000008, 0xd0000000, 0x00000008, - 0xe0000000, 0x00000008, 0xf0000000, 0x00000008, - 0x00000000, 0x00000009, 0x10000000, 0x00000009, - 0x20000000, 0x00000019, 0x30000000, 0x00000019, - 0x40000000, 0x00000019, 0x50000000, 0x00000019, - 0x60000000, 0x00000019, 0x70000000, 0x00000019, - 0x80000000, 0x00000019, 0x90000000, 0x00000019, - 0xa0000000, 0x00000019, 0xb0000000, 0x00000019, - 0xc0000000, 0x00000019, 0xd0000000, 0x00000019, - 0xe0000000, 0x00000019, 0xf0000000, 0x00000019, - 0x00000000, 0x0000001a, 0x10000000, 0x0000001a, - 0x20000000, 0x0000001a, 0x30000000, 0x0000001a, - 0x40000000, 0x0000001a, 0x50000000, 0x00000002, - 0x60000000, 0x00000002, 0x70000000, 0x00000002, - 0x80000000, 0x00000002, 0x90000000, 0x00000002, - 0xa0000000, 0x00000002, 0xb0000000, 0x00000002, - 0xc0000000, 0x0000000a, 0xd0000000, 0x0000000a, - 0xe0000000, 0x0000000a, 0xf0000000, 0x0000000a, - 0x00000000, 0x0000000b, 0x10000000, 0x0000000b, - 0x20000000, 0x0000000b, 0x30000000, 0x0000000b, - 0x40000000, 0x0000000b, 0x50000000, 0x0000001b, - 0x60000000, 0x0000001b, 0x70000000, 0x0000001b, - 0x80000000, 0x0000001b, 0x90000000, 0x0000001b, - 0xa0000000, 0x0000001b, 0xb0000000, 0x0000001b, - 0xc0000000, 0x0000001b, 0xd0000000, 0x0000001b, - 0xe0000000, 0x0000001b, 0xf0000000, 0x0000001b, - 0x00000000, 0x0000001c, 0x10000000, 0x0000001c, - 0x20000000, 0x0000001c, 0x30000000, 0x0000001c, - 0x40000000, 0x0000001c, 0x50000000, 0x0000001c, - 0x60000000, 0x0000001c, 0x70000000, 0x0000001c, - 0x80000000, 0x0000001c, 0x90000000, 0x0000001c, -}; - -static const u16 b43_lcntab_0x0e[] = { - 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, - 0x0407, 0x0408, 0x0409, 0x040a, 0x058b, 0x058c, - 0x058d, 0x058e, 0x058f, 0x0090, 0x0091, 0x0092, - 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, 0x0198, - 0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x019e, - 0x019f, 0x01a0, 0x01a1, 0x01a2, 0x01a3, 0x01a4, - 0x01a5, 0x0000, -}; - -static const u16 b43_lcntab_0x0f[] = { - 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, - 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, - 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, - 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, - 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, - 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, - 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, - 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, - 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, - 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, - 0x000a, 0x0009, 0x0006, 0x0005, -}; - -static const u16 b43_lcntab_0x10[] = { - 0x005f, 0x0036, 0x0029, 0x001f, 0x005f, 0x0036, - 0x0029, 0x001f, 0x005f, 0x0036, 0x0029, 0x001f, - 0x005f, 0x0036, 0x0029, 0x001f, -}; - -static const u16 b43_lcntab_0x11[] = { - 0x0009, 0x000f, 0x0014, 0x0018, 0x00fe, 0x0007, - 0x000b, 0x000f, 0x00fb, 0x00fe, 0x0001, 0x0005, - 0x0008, 0x000b, 0x000e, 0x0011, 0x0014, 0x0017, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0003, 0x0006, 0x0009, 0x000c, 0x000f, - 0x0012, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, - 0x0006, 0x0009, 0x000c, 0x000f, 0x0012, 0x0015, - 0x0018, 0x001b, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0003, 0x00eb, 0x0000, 0x0000, -}; - -static const u32 b43_lcntab_0x12[] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000004, 0x00000000, 0x00000004, 0x00000008, - 0x00000001, 0x00000005, 0x00000009, 0x0000000d, - 0x0000004d, 0x0000008d, 0x0000000d, 0x0000004d, - 0x0000008d, 0x000000cd, 0x0000004f, 0x0000008f, - 0x000000cf, 0x000000d3, 0x00000113, 0x00000513, - 0x00000913, 0x00000953, 0x00000d53, 0x00001153, - 0x00001193, 0x00005193, 0x00009193, 0x0000d193, - 0x00011193, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000004, - 0x00000000, 0x00000004, 0x00000008, 0x00000001, - 0x00000005, 0x00000009, 0x0000000d, 0x0000004d, - 0x0000008d, 0x0000000d, 0x0000004d, 0x0000008d, - 0x000000cd, 0x0000004f, 0x0000008f, 0x000000cf, - 0x000000d3, 0x00000113, 0x00000513, 0x00000913, - 0x00000953, 0x00000d53, 0x00001153, 0x00005153, - 0x00009153, 0x0000d153, 0x00011153, 0x00015153, - 0x00019153, 0x0001d153, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, -}; - -static const u16 b43_lcntab_0x14[] = { - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0002, 0x0003, 0x0001, 0x0003, 0x0002, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0003, - 0x0001, 0x0003, 0x0002, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, -}; - -static const u16 b43_lcntab_0x17[] = { - 0x001a, 0x0034, 0x004e, 0x0068, 0x009c, 0x00d0, - 0x00ea, 0x0104, 0x0034, 0x0068, 0x009c, 0x00d0, - 0x0138, 0x01a0, 0x01d4, 0x0208, 0x004e, 0x009c, - 0x00ea, 0x0138, 0x01d4, 0x0270, 0x02be, 0x030c, - 0x0068, 0x00d0, 0x0138, 0x01a0, 0x0270, 0x0340, - 0x03a8, 0x0410, 0x0018, 0x009c, 0x00d0, 0x0104, - 0x00ea, 0x0138, 0x0186, 0x00d0, 0x0104, 0x0104, - 0x0138, 0x016c, 0x016c, 0x01a0, 0x0138, 0x0186, - 0x0186, 0x01d4, 0x0222, 0x0222, 0x0270, 0x0104, - 0x0138, 0x016c, 0x0138, 0x016c, 0x01a0, 0x01d4, - 0x01a0, 0x01d4, 0x0208, 0x0208, 0x023c, 0x0186, - 0x01d4, 0x0222, 0x01d4, 0x0222, 0x0270, 0x02be, - 0x0270, 0x02be, 0x030c, 0x030c, 0x035a, 0x0036, - 0x006c, 0x00a2, 0x00d8, 0x0144, 0x01b0, 0x01e6, - 0x021c, 0x006c, 0x00d8, 0x0144, 0x01b0, 0x0288, - 0x0360, 0x03cc, 0x0438, 0x00a2, 0x0144, 0x01e6, - 0x0288, 0x03cc, 0x0510, 0x05b2, 0x0654, 0x00d8, - 0x01b0, 0x0288, 0x0360, 0x0510, 0x06c0, 0x0798, - 0x0870, 0x0018, 0x0144, 0x01b0, 0x021c, 0x01e6, - 0x0288, 0x032a, 0x01b0, 0x021c, 0x021c, 0x0288, - 0x02f4, 0x02f4, 0x0360, 0x0288, 0x032a, 0x032a, - 0x03cc, 0x046e, 0x046e, 0x0510, 0x021c, 0x0288, - 0x02f4, 0x0288, 0x02f4, 0x0360, 0x03cc, 0x0360, - 0x03cc, 0x0438, 0x0438, 0x04a4, 0x032a, 0x03cc, - 0x046e, 0x03cc, 0x046e, 0x0510, 0x05b2, 0x0510, - 0x05b2, 0x0654, 0x0654, 0x06f6, -}; - -static const u16 b43_lcntab_0x00[] = { - 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, - 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, - 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600, - 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, - 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, - 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -}; - -static const u32 b43_lcntab_0x18[] = { - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, - 0x00080000, 0x00080000, 0x00080000, 0x00080000, -}; - -/************************************************** - * TX gain. - **************************************************/ - -static const struct b43_lcntab_tx_gain_tbl_entry - b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0[B43_LCNTAB_TX_GAIN_SIZE] = { - { 0x03, 0x00, 0x1f, 0x0, 0x48 }, - { 0x03, 0x00, 0x1f, 0x0, 0x46 }, - { 0x03, 0x00, 0x1f, 0x0, 0x44 }, - { 0x03, 0x00, 0x1e, 0x0, 0x43 }, - { 0x03, 0x00, 0x1d, 0x0, 0x44 }, - { 0x03, 0x00, 0x1c, 0x0, 0x44 }, - { 0x03, 0x00, 0x1b, 0x0, 0x45 }, - { 0x03, 0x00, 0x1a, 0x0, 0x46 }, - { 0x03, 0x00, 0x19, 0x0, 0x46 }, - { 0x03, 0x00, 0x18, 0x0, 0x47 }, - { 0x03, 0x00, 0x17, 0x0, 0x48 }, - { 0x03, 0x00, 0x17, 0x0, 0x46 }, - { 0x03, 0x00, 0x16, 0x0, 0x47 }, - { 0x03, 0x00, 0x15, 0x0, 0x48 }, - { 0x03, 0x00, 0x15, 0x0, 0x46 }, - { 0x03, 0x00, 0x15, 0x0, 0x44 }, - { 0x03, 0x00, 0x15, 0x0, 0x42 }, - { 0x03, 0x00, 0x15, 0x0, 0x40 }, - { 0x03, 0x00, 0x15, 0x0, 0x3f }, - { 0x03, 0x00, 0x14, 0x0, 0x40 }, - { 0x03, 0x00, 0x13, 0x0, 0x41 }, - { 0x03, 0x00, 0x13, 0x0, 0x40 }, - { 0x03, 0x00, 0x12, 0x0, 0x41 }, - { 0x03, 0x00, 0x12, 0x0, 0x40 }, - { 0x03, 0x00, 0x11, 0x0, 0x41 }, - { 0x03, 0x00, 0x11, 0x0, 0x40 }, - { 0x03, 0x00, 0x10, 0x0, 0x41 }, - { 0x03, 0x00, 0x10, 0x0, 0x40 }, - { 0x03, 0x00, 0x10, 0x0, 0x3e }, - { 0x03, 0x00, 0x10, 0x0, 0x3c }, - { 0x03, 0x00, 0x10, 0x0, 0x3a }, - { 0x03, 0x00, 0x0f, 0x0, 0x3d }, - { 0x03, 0x00, 0x0f, 0x0, 0x3b }, - { 0x03, 0x00, 0x0e, 0x0, 0x3d }, - { 0x03, 0x00, 0x0e, 0x0, 0x3c }, - { 0x03, 0x00, 0x0e, 0x0, 0x3a }, - { 0x03, 0x00, 0x0d, 0x0, 0x3c }, - { 0x03, 0x00, 0x0d, 0x0, 0x3b }, - { 0x03, 0x00, 0x0c, 0x0, 0x3e }, - { 0x03, 0x00, 0x0c, 0x0, 0x3c }, - { 0x03, 0x00, 0x0c, 0x0, 0x3a }, - { 0x03, 0x00, 0x0b, 0x0, 0x3e }, - { 0x03, 0x00, 0x0b, 0x0, 0x3c }, - { 0x03, 0x00, 0x0b, 0x0, 0x3b }, - { 0x03, 0x00, 0x0b, 0x0, 0x39 }, - { 0x03, 0x00, 0x0a, 0x0, 0x3d }, - { 0x03, 0x00, 0x0a, 0x0, 0x3b }, - { 0x03, 0x00, 0x0a, 0x0, 0x39 }, - { 0x03, 0x00, 0x09, 0x0, 0x3e }, - { 0x03, 0x00, 0x09, 0x0, 0x3c }, - { 0x03, 0x00, 0x09, 0x0, 0x3a }, - { 0x03, 0x00, 0x09, 0x0, 0x39 }, - { 0x03, 0x00, 0x08, 0x0, 0x3e }, - { 0x03, 0x00, 0x08, 0x0, 0x3c }, - { 0x03, 0x00, 0x08, 0x0, 0x3a }, - { 0x03, 0x00, 0x08, 0x0, 0x39 }, - { 0x03, 0x00, 0x08, 0x0, 0x37 }, - { 0x03, 0x00, 0x07, 0x0, 0x3d }, - { 0x03, 0x00, 0x07, 0x0, 0x3c }, - { 0x03, 0x00, 0x07, 0x0, 0x3a }, - { 0x03, 0x00, 0x07, 0x0, 0x38 }, - { 0x03, 0x00, 0x07, 0x0, 0x37 }, - { 0x03, 0x00, 0x06, 0x0, 0x3e }, - { 0x03, 0x00, 0x06, 0x0, 0x3c }, - { 0x03, 0x00, 0x06, 0x0, 0x3a }, - { 0x03, 0x00, 0x06, 0x0, 0x39 }, - { 0x03, 0x00, 0x06, 0x0, 0x37 }, - { 0x03, 0x00, 0x06, 0x0, 0x36 }, - { 0x03, 0x00, 0x06, 0x0, 0x34 }, - { 0x03, 0x00, 0x05, 0x0, 0x3d }, - { 0x03, 0x00, 0x05, 0x0, 0x3b }, - { 0x03, 0x00, 0x05, 0x0, 0x39 }, - { 0x03, 0x00, 0x05, 0x0, 0x38 }, - { 0x03, 0x00, 0x05, 0x0, 0x36 }, - { 0x03, 0x00, 0x05, 0x0, 0x35 }, - { 0x03, 0x00, 0x05, 0x0, 0x33 }, - { 0x03, 0x00, 0x04, 0x0, 0x3e }, - { 0x03, 0x00, 0x04, 0x0, 0x3c }, - { 0x03, 0x00, 0x04, 0x0, 0x3a }, - { 0x03, 0x00, 0x04, 0x0, 0x39 }, - { 0x03, 0x00, 0x04, 0x0, 0x37 }, - { 0x03, 0x00, 0x04, 0x0, 0x36 }, - { 0x03, 0x00, 0x04, 0x0, 0x34 }, - { 0x03, 0x00, 0x04, 0x0, 0x33 }, - { 0x03, 0x00, 0x04, 0x0, 0x31 }, - { 0x03, 0x00, 0x04, 0x0, 0x30 }, - { 0x03, 0x00, 0x04, 0x0, 0x2e }, - { 0x03, 0x00, 0x03, 0x0, 0x3c }, - { 0x03, 0x00, 0x03, 0x0, 0x3a }, - { 0x03, 0x00, 0x03, 0x0, 0x39 }, - { 0x03, 0x00, 0x03, 0x0, 0x37 }, - { 0x03, 0x00, 0x03, 0x0, 0x36 }, - { 0x03, 0x00, 0x03, 0x0, 0x34 }, - { 0x03, 0x00, 0x03, 0x0, 0x33 }, - { 0x03, 0x00, 0x03, 0x0, 0x31 }, - { 0x03, 0x00, 0x03, 0x0, 0x30 }, - { 0x03, 0x00, 0x03, 0x0, 0x2e }, - { 0x03, 0x00, 0x03, 0x0, 0x2d }, - { 0x03, 0x00, 0x03, 0x0, 0x2c }, - { 0x03, 0x00, 0x03, 0x0, 0x2b }, - { 0x03, 0x00, 0x03, 0x0, 0x29 }, - { 0x03, 0x00, 0x02, 0x0, 0x3d }, - { 0x03, 0x00, 0x02, 0x0, 0x3b }, - { 0x03, 0x00, 0x02, 0x0, 0x39 }, - { 0x03, 0x00, 0x02, 0x0, 0x38 }, - { 0x03, 0x00, 0x02, 0x0, 0x36 }, - { 0x03, 0x00, 0x02, 0x0, 0x35 }, - { 0x03, 0x00, 0x02, 0x0, 0x33 }, - { 0x03, 0x00, 0x02, 0x0, 0x32 }, - { 0x03, 0x00, 0x02, 0x0, 0x30 }, - { 0x03, 0x00, 0x02, 0x0, 0x2f }, - { 0x03, 0x00, 0x02, 0x0, 0x2e }, - { 0x03, 0x00, 0x02, 0x0, 0x2c }, - { 0x03, 0x00, 0x02, 0x0, 0x2b }, - { 0x03, 0x00, 0x02, 0x0, 0x2a }, - { 0x03, 0x00, 0x02, 0x0, 0x29 }, - { 0x03, 0x00, 0x02, 0x0, 0x27 }, - { 0x03, 0x00, 0x02, 0x0, 0x26 }, - { 0x03, 0x00, 0x02, 0x0, 0x25 }, - { 0x03, 0x00, 0x02, 0x0, 0x24 }, - { 0x03, 0x00, 0x02, 0x0, 0x23 }, - { 0x03, 0x00, 0x02, 0x0, 0x22 }, - { 0x03, 0x00, 0x02, 0x0, 0x21 }, - { 0x03, 0x00, 0x02, 0x0, 0x20 }, - { 0x03, 0x00, 0x01, 0x0, 0x3f }, - { 0x03, 0x00, 0x01, 0x0, 0x3d }, - { 0x03, 0x00, 0x01, 0x0, 0x3b }, - { 0x03, 0x00, 0x01, 0x0, 0x39 }, -}; - -/************************************************** - * SW control. - **************************************************/ - -static const u16 b43_lcntab_sw_ctl_4313_epa_rev0[] = { - 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, - 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, - 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, - 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, - 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, - 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, - 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, - 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, - 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, - 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, - 0x0002, 0x0008, 0x0004, 0x0001, -}; - -/************************************************** - * R/W ops. - **************************************************/ - -u32 b43_lcntab_read(struct b43_wldev *dev, u32 offset) -{ - u32 type, value; - - type = offset & B43_LCNTAB_TYPEMASK; - offset &= ~B43_LCNTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - switch (type) { - case B43_LCNTAB_8BIT: - b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO) & 0xFF; - break; - case B43_LCNTAB_16BIT: - b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO); - break; - case B43_LCNTAB_32BIT: - b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); - value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO); - value |= (b43_phy_read(dev, B43_PHY_LCN_TABLE_DATAHI) << 16); - break; - default: - B43_WARN_ON(1); - value = 0; - } - - return value; -} - -void b43_lcntab_read_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, void *_data) -{ - u32 type; - u8 *data = _data; - unsigned int i; - - type = offset & B43_LCNTAB_TYPEMASK; - offset &= ~B43_LCNTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); - - for (i = 0; i < nr_elements; i++) { - switch (type) { - case B43_LCNTAB_8BIT: - *data = b43_phy_read(dev, - B43_PHY_LCN_TABLE_DATALO) & 0xFF; - data++; - break; - case B43_LCNTAB_16BIT: - *((u16 *)data) = b43_phy_read(dev, - B43_PHY_LCN_TABLE_DATALO); - data += 2; - break; - case B43_LCNTAB_32BIT: - *((u32 *)data) = b43_phy_read(dev, - B43_PHY_LCN_TABLE_DATALO); - *((u32 *)data) |= (b43_phy_read(dev, - B43_PHY_LCN_TABLE_DATAHI) << 16); - data += 4; - break; - default: - B43_WARN_ON(1); - } - } -} - -void b43_lcntab_write(struct b43_wldev *dev, u32 offset, u32 value) -{ - u32 type; - - type = offset & B43_LCNTAB_TYPEMASK; - offset &= 0xFFFF; - - switch (type) { - case B43_LCNTAB_8BIT: - B43_WARN_ON(value & ~0xFF); - b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); - break; - case B43_LCNTAB_16BIT: - B43_WARN_ON(value & ~0xFFFF); - b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); - break; - case B43_LCNTAB_32BIT: - b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, value >> 16); - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value & 0xFFFF); - break; - default: - B43_WARN_ON(1); - } - - return; -} - -void b43_lcntab_write_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, const void *_data) -{ - u32 type, value; - const u8 *data = _data; - unsigned int i; - - type = offset & B43_LCNTAB_TYPEMASK; - offset &= ~B43_LCNTAB_TYPEMASK; - B43_WARN_ON(offset > 0xFFFF); - - b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); - - for (i = 0; i < nr_elements; i++) { - switch (type) { - case B43_LCNTAB_8BIT: - value = *data; - data++; - B43_WARN_ON(value & ~0xFF); - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); - break; - case B43_LCNTAB_16BIT: - value = *((u16 *)data); - data += 2; - B43_WARN_ON(value & ~0xFFFF); - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); - break; - case B43_LCNTAB_32BIT: - value = *((u32 *)data); - data += 4; - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, - value >> 16); - b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, - value & 0xFFFF); - break; - default: - B43_WARN_ON(1); - } - } -} - -/************************************************** - * Tables ops. - **************************************************/ - -#define lcntab_upload(dev, offset, data) do { \ - b43_lcntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ - } while (0) -static void b43_phy_lcn_upload_static_tables(struct b43_wldev *dev) -{ - lcntab_upload(dev, B43_LCNTAB16(0x02, 0), b43_lcntab_0x02); - lcntab_upload(dev, B43_LCNTAB16(0x01, 0), b43_lcntab_0x01); - lcntab_upload(dev, B43_LCNTAB32(0x0b, 0), b43_lcntab_0x0b); - lcntab_upload(dev, B43_LCNTAB32(0x0c, 0), b43_lcntab_0x0c); - lcntab_upload(dev, B43_LCNTAB32(0x0d, 0), b43_lcntab_0x0d); - lcntab_upload(dev, B43_LCNTAB16(0x0e, 0), b43_lcntab_0x0e); - lcntab_upload(dev, B43_LCNTAB16(0x0f, 0), b43_lcntab_0x0f); - lcntab_upload(dev, B43_LCNTAB16(0x10, 0), b43_lcntab_0x10); - lcntab_upload(dev, B43_LCNTAB16(0x11, 0), b43_lcntab_0x11); - lcntab_upload(dev, B43_LCNTAB32(0x12, 0), b43_lcntab_0x12); - lcntab_upload(dev, B43_LCNTAB16(0x14, 0), b43_lcntab_0x14); - lcntab_upload(dev, B43_LCNTAB16(0x17, 0), b43_lcntab_0x17); - lcntab_upload(dev, B43_LCNTAB16(0x00, 0), b43_lcntab_0x00); - lcntab_upload(dev, B43_LCNTAB32(0x18, 0), b43_lcntab_0x18); -} - -static void b43_phy_lcn_load_tx_gain_tab(struct b43_wldev *dev, - const struct b43_lcntab_tx_gain_tbl_entry *gain_table) -{ - u32 i; - u32 val; - - u16 pa_gain = 0x70; - if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM) - pa_gain = 0x10; - - for (i = 0; i < B43_LCNTAB_TX_GAIN_SIZE; i++) { - val = ((pa_gain << 24) | - (gain_table[i].pad << 16) | - (gain_table[i].pga << 8) | - gain_table[i].gm); - b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0xc0 + i), val); - - /* brcmsmac doesn't maskset, we follow newer wl here */ - val = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x140 + i)); - val &= 0x000fffff; - val |= ((gain_table[i].dac << 28) | - (gain_table[i].bb_mult << 20)); - b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x140 + i), val); - } -} - -/* wlc_lcnphy_load_rfpower */ -static void b43_phy_lcn_load_rfpower(struct b43_wldev *dev) -{ - u32 bbmult, rfgain; - u8 i; - - for (i = 0; i < 128; i++) { - bbmult = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x140 + i)); - bbmult >>= 20; - rfgain = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0xc0 + i)); - - /* TODO: calculate value for 0x240 + i table offset - * b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x240 + i), val); - */ - } -} - -/* Not implemented in brcmsmac, noticed in wl in MMIO dump */ -static void b43_phy_lcn_rewrite_rfpower_table(struct b43_wldev *dev) -{ - int i; - u32 tmp; - for (i = 0; i < 128; i++) { - tmp = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x240 + i)); - b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x240 + i), tmp); - } -} - -/* wlc_lcnphy_clear_papd_comptable */ -static void b43_phy_lcn_clean_papd_comp_table(struct b43_wldev *dev) -{ - u8 i; - - for (i = 0; i < 0x80; i++) - b43_lcntab_write(dev, B43_LCNTAB32(0x18, i), 0x80000); -} - -/* wlc_lcnphy_tbl_init */ -void b43_phy_lcn_tables_init(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - - b43_phy_lcn_upload_static_tables(dev); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if (sprom->boardflags_lo & B43_BFL_FEM) - b43_phy_lcn_load_tx_gain_tab(dev, - b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0); - else - b43err(dev->wl, - "TX gain table unknown for this card\n"); - } - - if (sprom->boardflags_lo & B43_BFL_FEM && - !(sprom->boardflags_hi & B43_BFH_FEM_BT)) - b43_lcntab_write_bulk(dev, B43_LCNTAB16(0xf, 0), - ARRAY_SIZE(b43_lcntab_sw_ctl_4313_epa_rev0), - b43_lcntab_sw_ctl_4313_epa_rev0); - else - b43err(dev->wl, "SW ctl table is unknown for this card\n"); - - b43_phy_lcn_load_rfpower(dev); - b43_phy_lcn_rewrite_rfpower_table(dev); - b43_phy_lcn_clean_papd_comp_table(dev); -} diff --git a/drivers/net/wireless/b43/tables_phy_lcn.h b/drivers/net/wireless/b43/tables_phy_lcn.h deleted file mode 100644 index caff9db6831f..000000000000 --- a/drivers/net/wireless/b43/tables_phy_lcn.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef B43_TABLES_PHY_LCN_H_ -#define B43_TABLES_PHY_LCN_H_ - -/* The LCN-PHY tables. */ -#define B43_LCNTAB_TYPEMASK 0xF0000000 -#define B43_LCNTAB_8BIT 0x10000000 -#define B43_LCNTAB_16BIT 0x20000000 -#define B43_LCNTAB_32BIT 0x30000000 -#define B43_LCNTAB8(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_8BIT) -#define B43_LCNTAB16(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_16BIT) -#define B43_LCNTAB32(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_32BIT) - -#define B43_LCNTAB_TX_GAIN_SIZE 128 - -u32 b43_lcntab_read(struct b43_wldev *dev, u32 offset); -void b43_lcntab_read_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, void *_data); -void b43_lcntab_write(struct b43_wldev *dev, u32 offset, u32 value); -void b43_lcntab_write_bulk(struct b43_wldev *dev, u32 offset, - unsigned int nr_elements, const void *_data); - -void b43_phy_lcn_tables_init(struct b43_wldev *dev); - -#endif /* B43_TABLES_PHY_LCN_H_ */ diff --git a/drivers/net/wireless/b43/wa.c b/drivers/net/wireless/b43/wa.c deleted file mode 100644 index c218c08fb2f5..000000000000 --- a/drivers/net/wireless/b43/wa.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - - Broadcom B43 wireless driver - - PHY workarounds. - - Copyright (c) 2005-2007 Stefano Brivio - Copyright (c) 2005-2007 Michael Buesch - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43.h" -#include "main.h" -#include "tables.h" -#include "phy_common.h" -#include "wa.h" - -static void b43_wa_papd(struct b43_wldev *dev) -{ - u16 backup; - - backup = b43_ofdmtab_read16(dev, B43_OFDMTAB_PWRDYN2, 0); - b43_ofdmtab_write16(dev, B43_OFDMTAB_PWRDYN2, 0, 7); - b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_APHY, 0, 0); - b43_dummy_transmission(dev, true, true); - b43_ofdmtab_write16(dev, B43_OFDMTAB_PWRDYN2, 0, backup); -} - -static void b43_wa_auxclipthr(struct b43_wldev *dev) -{ - b43_phy_write(dev, B43_PHY_OFDM(0x8E), 0x3800); -} - -static void b43_wa_afcdac(struct b43_wldev *dev) -{ - b43_phy_write(dev, 0x0035, 0x03FF); - b43_phy_write(dev, 0x0036, 0x0400); -} - -static void b43_wa_txdc_offset(struct b43_wldev *dev) -{ - b43_ofdmtab_write16(dev, B43_OFDMTAB_DC, 0, 0x0051); -} - -void b43_wa_initgains(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - b43_phy_write(dev, B43_PHY_LNAHPFCTL, 0x1FF9); - b43_phy_mask(dev, B43_PHY_LPFGAINCTL, 0xFF0F); - if (phy->rev <= 2) - b43_ofdmtab_write16(dev, B43_OFDMTAB_LPFGAIN, 0, 0x1FBF); - b43_radio_write16(dev, 0x0002, 0x1FBF); - - b43_phy_write(dev, 0x0024, 0x4680); - b43_phy_write(dev, 0x0020, 0x0003); - b43_phy_write(dev, 0x001D, 0x0F40); - b43_phy_write(dev, 0x001F, 0x1C00); - if (phy->rev <= 3) - b43_phy_maskset(dev, 0x002A, 0x00FF, 0x0400); - else if (phy->rev == 5) { - b43_phy_maskset(dev, 0x002A, 0x00FF, 0x1A00); - b43_phy_write(dev, 0x00CC, 0x2121); - } - if (phy->rev >= 3) - b43_phy_write(dev, 0x00BA, 0x3ED5); -} - -static void b43_wa_divider(struct b43_wldev *dev) -{ - b43_phy_mask(dev, 0x002B, ~0x0100); - b43_phy_write(dev, 0x008E, 0x58C1); -} - -static void b43_wa_gt(struct b43_wldev *dev) /* Gain table. */ -{ - if (dev->phy.rev <= 2) { - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 0, 15); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 1, 31); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 2, 42); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 3, 48); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 4, 58); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 0, 19); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 1, 19); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 2, 19); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 3, 19); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 4, 21); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 5, 21); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 6, 25); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN1, 0, 3); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN1, 1, 3); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN1, 2, 7); - } else { - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 0, 19); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 1, 19); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 2, 19); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 3, 19); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 4, 21); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 5, 21); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 6, 25); - } -} - -static void b43_wa_rssi_lt(struct b43_wldev *dev) /* RSSI lookup table */ -{ - int i; - - if (0 /* FIXME: For APHY.rev=2 this might be needed */) { - for (i = 0; i < 8; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_RSSI, i, i + 8); - for (i = 8; i < 16; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_RSSI, i, i - 8); - } else { - for (i = 0; i < 64; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_RSSI, i, i); - } -} - -static void b43_wa_analog(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - u16 ofdmrev; - - ofdmrev = b43_phy_read(dev, B43_PHY_VERSION_OFDM) & B43_PHYVER_VERSION; - if (ofdmrev > 2) { - if (phy->type == B43_PHYTYPE_A) - b43_phy_write(dev, B43_PHY_PWRDOWN, 0x1808); - else - b43_phy_write(dev, B43_PHY_PWRDOWN, 0x1000); - } else { - b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 3, 0x1044); - b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 4, 0x7201); - b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 6, 0x0040); - } -} - -static void b43_wa_dac(struct b43_wldev *dev) -{ - if (dev->phy.analog == 1) - b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 1, - (b43_ofdmtab_read16(dev, B43_OFDMTAB_DAC, 1) & ~0x0034) | 0x0008); - else - b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 1, - (b43_ofdmtab_read16(dev, B43_OFDMTAB_DAC, 1) & ~0x0078) | 0x0010); -} - -static void b43_wa_fft(struct b43_wldev *dev) /* Fine frequency table */ -{ - int i; - - if (dev->phy.type == B43_PHYTYPE_A) - for (i = 0; i < B43_TAB_FINEFREQA_SIZE; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_DACRFPABB, i, b43_tab_finefreqa[i]); - else - for (i = 0; i < B43_TAB_FINEFREQG_SIZE; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_DACRFPABB, i, b43_tab_finefreqg[i]); -} - -static void b43_wa_nft(struct b43_wldev *dev) /* Noise figure table */ -{ - struct b43_phy *phy = &dev->phy; - int i; - - if (phy->type == B43_PHYTYPE_A) { - if (phy->rev == 2) - for (i = 0; i < B43_TAB_NOISEA2_SIZE; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noisea2[i]); - else - for (i = 0; i < B43_TAB_NOISEA3_SIZE; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noisea3[i]); - } else { - if (phy->rev == 1) - for (i = 0; i < B43_TAB_NOISEG1_SIZE; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noiseg1[i]); - else - for (i = 0; i < B43_TAB_NOISEG2_SIZE; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noiseg2[i]); - } -} - -static void b43_wa_rt(struct b43_wldev *dev) /* Rotor table */ -{ - int i; - - for (i = 0; i < B43_TAB_ROTOR_SIZE; i++) - b43_ofdmtab_write32(dev, B43_OFDMTAB_ROTOR, i, b43_tab_rotor[i]); -} - -static void b43_write_null_nst(struct b43_wldev *dev) -{ - int i; - - for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE, i, 0); -} - -static void b43_write_nst(struct b43_wldev *dev, const u16 *nst) -{ - int i; - - for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++) - b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE, i, nst[i]); -} - -static void b43_wa_nst(struct b43_wldev *dev) /* Noise scale table */ -{ - struct b43_phy *phy = &dev->phy; - - if (phy->type == B43_PHYTYPE_A) { - if (phy->rev <= 1) - b43_write_null_nst(dev); - else if (phy->rev == 2) - b43_write_nst(dev, b43_tab_noisescalea2); - else if (phy->rev == 3) - b43_write_nst(dev, b43_tab_noisescalea3); - else - b43_write_nst(dev, b43_tab_noisescaleg3); - } else { - if (phy->rev >= 6) { - if (b43_phy_read(dev, B43_PHY_ENCORE) & B43_PHY_ENCORE_EN) - b43_write_nst(dev, b43_tab_noisescaleg3); - else - b43_write_nst(dev, b43_tab_noisescaleg2); - } else { - b43_write_nst(dev, b43_tab_noisescaleg1); - } - } -} - -static void b43_wa_art(struct b43_wldev *dev) /* ADV retard table */ -{ - int i; - - for (i = 0; i < B43_TAB_RETARD_SIZE; i++) - b43_ofdmtab_write32(dev, B43_OFDMTAB_ADVRETARD, - i, b43_tab_retard[i]); -} - -static void b43_wa_txlna_gain(struct b43_wldev *dev) -{ - b43_ofdmtab_write16(dev, B43_OFDMTAB_DC, 13, 0x0000); -} - -static void b43_wa_crs_reset(struct b43_wldev *dev) -{ - b43_phy_write(dev, 0x002C, 0x0064); -} - -static void b43_wa_2060txlna_gain(struct b43_wldev *dev) -{ - b43_hf_write(dev, b43_hf_read(dev) | - B43_HF_2060W); -} - -static void b43_wa_lms(struct b43_wldev *dev) -{ - b43_phy_maskset(dev, 0x0055, 0xFFC0, 0x0004); -} - -static void b43_wa_mixedsignal(struct b43_wldev *dev) -{ - b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 1, 3); -} - -static void b43_wa_msst(struct b43_wldev *dev) /* Min sigma square table */ -{ - struct b43_phy *phy = &dev->phy; - int i; - const u16 *tab; - - if (phy->type == B43_PHYTYPE_A) { - tab = b43_tab_sigmasqr1; - } else if (phy->type == B43_PHYTYPE_G) { - tab = b43_tab_sigmasqr2; - } else { - B43_WARN_ON(1); - return; - } - - for (i = 0; i < B43_TAB_SIGMASQR_SIZE; i++) { - b43_ofdmtab_write16(dev, B43_OFDMTAB_MINSIGSQ, - i, tab[i]); - } -} - -static void b43_wa_iqadc(struct b43_wldev *dev) -{ - if (dev->phy.analog == 4) - b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 0, - b43_ofdmtab_read16(dev, B43_OFDMTAB_DAC, 0) & ~0xF000); -} - -static void b43_wa_crs_ed(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - if (phy->rev == 1) { - b43_phy_write(dev, B43_PHY_CRSTHRES1_R1, 0x4F19); - } else if (phy->rev == 2) { - b43_phy_write(dev, B43_PHY_CRSTHRES1, 0x1861); - b43_phy_write(dev, B43_PHY_CRSTHRES2, 0x0271); - b43_phy_set(dev, B43_PHY_ANTDWELL, 0x0800); - } else { - b43_phy_write(dev, B43_PHY_CRSTHRES1, 0x0098); - b43_phy_write(dev, B43_PHY_CRSTHRES2, 0x0070); - b43_phy_write(dev, B43_PHY_OFDM(0xC9), 0x0080); - b43_phy_set(dev, B43_PHY_ANTDWELL, 0x0800); - } -} - -static void b43_wa_crs_thr(struct b43_wldev *dev) -{ - b43_phy_maskset(dev, B43_PHY_CRS0, ~0x03C0, 0xD000); -} - -static void b43_wa_crs_blank(struct b43_wldev *dev) -{ - b43_phy_write(dev, B43_PHY_OFDM(0x2C), 0x005A); -} - -static void b43_wa_cck_shiftbits(struct b43_wldev *dev) -{ - b43_phy_write(dev, B43_PHY_CCKSHIFTBITS, 0x0026); -} - -static void b43_wa_wrssi_offset(struct b43_wldev *dev) -{ - int i; - - if (dev->phy.rev == 1) { - for (i = 0; i < 16; i++) { - b43_ofdmtab_write16(dev, B43_OFDMTAB_WRSSI_R1, - i, 0x0020); - } - } else { - for (i = 0; i < 32; i++) { - b43_ofdmtab_write16(dev, B43_OFDMTAB_WRSSI, - i, 0x0820); - } - } -} - -static void b43_wa_txpuoff_rxpuon(struct b43_wldev *dev) -{ - b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_0F, 2, 15); - b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_0F, 3, 20); -} - -static void b43_wa_altagc(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - if (phy->rev == 1) { - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 0, 254); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 1, 13); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 2, 19); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 3, 25); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 0, 0x2710); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 1, 0x9B83); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 2, 0x9B83); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 3, 0x0F8D); - b43_phy_write(dev, B43_PHY_LMS, 4); - } else { - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0, 254); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 1, 13); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 2, 19); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 3, 25); - } - - b43_phy_maskset(dev, B43_PHY_CCKSHIFTBITS_WA, 0x00FF, 0x5700); - b43_phy_maskset(dev, B43_PHY_OFDM(0x1A), ~0x007F, 0x000F); - b43_phy_maskset(dev, B43_PHY_OFDM(0x1A), ~0x3F80, 0x2B80); - b43_phy_maskset(dev, B43_PHY_ANTWRSETT, 0xF0FF, 0x0300); - b43_radio_set(dev, 0x7A, 0x0008); - b43_phy_maskset(dev, B43_PHY_N1P1GAIN, ~0x000F, 0x0008); - b43_phy_maskset(dev, B43_PHY_P1P2GAIN, ~0x0F00, 0x0600); - b43_phy_maskset(dev, B43_PHY_N1N2GAIN, ~0x0F00, 0x0700); - b43_phy_maskset(dev, B43_PHY_N1P1GAIN, ~0x0F00, 0x0100); - if (phy->rev == 1) { - b43_phy_maskset(dev, B43_PHY_N1N2GAIN, ~0x000F, 0x0007); - } - b43_phy_maskset(dev, B43_PHY_OFDM(0x88), ~0x00FF, 0x001C); - b43_phy_maskset(dev, B43_PHY_OFDM(0x88), ~0x3F00, 0x0200); - b43_phy_maskset(dev, B43_PHY_OFDM(0x96), ~0x00FF, 0x001C); - b43_phy_maskset(dev, B43_PHY_OFDM(0x89), ~0x00FF, 0x0020); - b43_phy_maskset(dev, B43_PHY_OFDM(0x89), ~0x3F00, 0x0200); - b43_phy_maskset(dev, B43_PHY_OFDM(0x82), ~0x00FF, 0x002E); - b43_phy_maskset(dev, B43_PHY_OFDM(0x96), 0x00FF, 0x1A00); - b43_phy_maskset(dev, B43_PHY_OFDM(0x81), ~0x00FF, 0x0028); - b43_phy_maskset(dev, B43_PHY_OFDM(0x81), 0x00FF, 0x2C00); - if (phy->rev == 1) { - b43_phy_write(dev, B43_PHY_PEAK_COUNT, 0x092B); - b43_phy_maskset(dev, B43_PHY_OFDM(0x1B), ~0x001E, 0x0002); - } else { - b43_phy_mask(dev, B43_PHY_OFDM(0x1B), ~0x001E); - b43_phy_write(dev, B43_PHY_OFDM(0x1F), 0x287A); - b43_phy_maskset(dev, B43_PHY_LPFGAINCTL, ~0x000F, 0x0004); - if (phy->rev >= 6) { - b43_phy_write(dev, B43_PHY_OFDM(0x22), 0x287A); - b43_phy_maskset(dev, B43_PHY_LPFGAINCTL, 0x0FFF, 0x3000); - } - } - b43_phy_maskset(dev, B43_PHY_DIVSRCHIDX, 0x8080, 0x7874); - b43_phy_write(dev, B43_PHY_OFDM(0x8E), 0x1C00); - if (phy->rev == 1) { - b43_phy_maskset(dev, B43_PHY_DIVP1P2GAIN, ~0x0F00, 0x0600); - b43_phy_write(dev, B43_PHY_OFDM(0x8B), 0x005E); - b43_phy_maskset(dev, B43_PHY_ANTWRSETT, ~0x00FF, 0x001E); - b43_phy_write(dev, B43_PHY_OFDM(0x8D), 0x0002); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 0, 0); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 1, 7); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 2, 16); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 3, 28); - } else { - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 0, 0); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 1, 7); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 2, 16); - b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 3, 28); - } - if (phy->rev >= 6) { - b43_phy_mask(dev, B43_PHY_OFDM(0x26), ~0x0003); - b43_phy_mask(dev, B43_PHY_OFDM(0x26), ~0x1000); - } - b43_phy_read(dev, B43_PHY_VERSION_OFDM); /* Dummy read */ -} - -static void b43_wa_tr_ltov(struct b43_wldev *dev) /* TR Lookup Table Original Values */ -{ - b43_gtab_write(dev, B43_GTAB_ORIGTR, 0, 0x7654); -} - -static void b43_wa_cpll_nonpilot(struct b43_wldev *dev) -{ - b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_11, 0, 0); - b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_11, 1, 0); -} - -static void b43_wa_rssi_adc(struct b43_wldev *dev) -{ - if (dev->phy.analog == 4) - b43_phy_write(dev, 0x00DC, 0x7454); -} - -static void b43_wa_boards_a(struct b43_wldev *dev) -{ - if (dev->dev->board_vendor == SSB_BOARDVENDOR_BCM && - dev->dev->board_type == SSB_BOARD_BU4306 && - dev->dev->board_rev < 0x30) { - b43_phy_write(dev, 0x0010, 0xE000); - b43_phy_write(dev, 0x0013, 0x0140); - b43_phy_write(dev, 0x0014, 0x0280); - } else { - if (dev->dev->board_type == SSB_BOARD_MP4318 && - dev->dev->board_rev < 0x20) { - b43_phy_write(dev, 0x0013, 0x0210); - b43_phy_write(dev, 0x0014, 0x0840); - } else { - b43_phy_write(dev, 0x0013, 0x0140); - b43_phy_write(dev, 0x0014, 0x0280); - } - if (dev->phy.rev <= 4) - b43_phy_write(dev, 0x0010, 0xE000); - else - b43_phy_write(dev, 0x0010, 0x2000); - b43_ofdmtab_write16(dev, B43_OFDMTAB_DC, 1, 0x0039); - b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_APHY, 7, 0x0040); - } -} - -static void b43_wa_boards_g(struct b43_wldev *dev) -{ - struct ssb_sprom *sprom = dev->dev->bus_sprom; - struct b43_phy *phy = &dev->phy; - - if (dev->dev->board_vendor != SSB_BOARDVENDOR_BCM || - dev->dev->board_type != SSB_BOARD_BU4306 || - dev->dev->board_rev != 0x17) { - if (phy->rev < 2) { - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX_R1, 1, 0x0002); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX_R1, 2, 0x0001); - } else { - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 1, 0x0002); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 2, 0x0001); - if ((sprom->boardflags_lo & B43_BFL_EXTLNA) && - (phy->rev >= 7)) { - b43_phy_mask(dev, B43_PHY_EXTG(0x11), 0xF7FF); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0020, 0x0001); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0021, 0x0001); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0022, 0x0001); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0023, 0x0000); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0000, 0x0000); - b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0003, 0x0002); - } - } - } - if (sprom->boardflags_lo & B43_BFL_FEM) { - b43_phy_write(dev, B43_PHY_GTABCTL, 0x3120); - b43_phy_write(dev, B43_PHY_GTABDATA, 0xC480); - } -} - -void b43_wa_all(struct b43_wldev *dev) -{ - struct b43_phy *phy = &dev->phy; - - if (phy->type == B43_PHYTYPE_A) { - switch (phy->rev) { - case 2: - b43_wa_papd(dev); - b43_wa_auxclipthr(dev); - b43_wa_afcdac(dev); - b43_wa_txdc_offset(dev); - b43_wa_initgains(dev); - b43_wa_divider(dev); - b43_wa_gt(dev); - b43_wa_rssi_lt(dev); - b43_wa_analog(dev); - b43_wa_dac(dev); - b43_wa_fft(dev); - b43_wa_nft(dev); - b43_wa_rt(dev); - b43_wa_nst(dev); - b43_wa_art(dev); - b43_wa_txlna_gain(dev); - b43_wa_crs_reset(dev); - b43_wa_2060txlna_gain(dev); - b43_wa_lms(dev); - break; - case 3: - b43_wa_papd(dev); - b43_wa_mixedsignal(dev); - b43_wa_rssi_lt(dev); - b43_wa_txdc_offset(dev); - b43_wa_initgains(dev); - b43_wa_dac(dev); - b43_wa_nft(dev); - b43_wa_nst(dev); - b43_wa_msst(dev); - b43_wa_analog(dev); - b43_wa_gt(dev); - b43_wa_txpuoff_rxpuon(dev); - b43_wa_txlna_gain(dev); - break; - case 5: - b43_wa_iqadc(dev); - case 6: - b43_wa_papd(dev); - b43_wa_rssi_lt(dev); - b43_wa_txdc_offset(dev); - b43_wa_initgains(dev); - b43_wa_dac(dev); - b43_wa_nft(dev); - b43_wa_nst(dev); - b43_wa_msst(dev); - b43_wa_analog(dev); - b43_wa_gt(dev); - b43_wa_txpuoff_rxpuon(dev); - b43_wa_txlna_gain(dev); - break; - case 7: - b43_wa_iqadc(dev); - b43_wa_papd(dev); - b43_wa_rssi_lt(dev); - b43_wa_txdc_offset(dev); - b43_wa_initgains(dev); - b43_wa_dac(dev); - b43_wa_nft(dev); - b43_wa_nst(dev); - b43_wa_msst(dev); - b43_wa_analog(dev); - b43_wa_gt(dev); - b43_wa_txpuoff_rxpuon(dev); - b43_wa_txlna_gain(dev); - b43_wa_rssi_adc(dev); - default: - B43_WARN_ON(1); - } - b43_wa_boards_a(dev); - } else if (phy->type == B43_PHYTYPE_G) { - switch (phy->rev) { - case 1://XXX review rev1 - b43_wa_crs_ed(dev); - b43_wa_crs_thr(dev); - b43_wa_crs_blank(dev); - b43_wa_cck_shiftbits(dev); - b43_wa_fft(dev); - b43_wa_nft(dev); - b43_wa_rt(dev); - b43_wa_nst(dev); - b43_wa_art(dev); - b43_wa_wrssi_offset(dev); - b43_wa_altagc(dev); - break; - case 2: - case 6: - case 7: - case 8: - case 9: - b43_wa_tr_ltov(dev); - b43_wa_crs_ed(dev); - b43_wa_rssi_lt(dev); - b43_wa_nft(dev); - b43_wa_nst(dev); - b43_wa_msst(dev); - b43_wa_wrssi_offset(dev); - b43_wa_altagc(dev); - b43_wa_analog(dev); - b43_wa_txpuoff_rxpuon(dev); - break; - default: - B43_WARN_ON(1); - } - b43_wa_boards_g(dev); - } else { /* No N PHY support so far, LP PHY is in phy_lp.c */ - B43_WARN_ON(1); - } - - b43_wa_cpll_nonpilot(dev); -} diff --git a/drivers/net/wireless/b43/wa.h b/drivers/net/wireless/b43/wa.h deleted file mode 100644 index e163c5e56e81..000000000000 --- a/drivers/net/wireless/b43/wa.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef B43_WA_H_ -#define B43_WA_H_ - -void b43_wa_initgains(struct b43_wldev *dev); -void b43_wa_all(struct b43_wldev *dev); - -#endif /* B43_WA_H_ */ diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c deleted file mode 100644 index 426dc13c44cd..000000000000 --- a/drivers/net/wireless/b43/xmit.c +++ /dev/null @@ -1,947 +0,0 @@ -/* - - Broadcom B43 wireless driver - - Transmission (TX/RX) related functions. - - Copyright (C) 2005 Martin Langer - Copyright (C) 2005 Stefano Brivio - Copyright (C) 2005, 2006 Michael Buesch - Copyright (C) 2005 Danny van Dyk - Copyright (C) 2005 Andreas Jaggi - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "xmit.h" -#include "phy_common.h" -#include "dma.h" -#include "pio.h" - -static const struct b43_tx_legacy_rate_phy_ctl_entry b43_tx_legacy_rate_phy_ctl[] = { - { B43_CCK_RATE_1MB, 0x0, 0x0 }, - { B43_CCK_RATE_2MB, 0x0, 0x1 }, - { B43_CCK_RATE_5MB, 0x0, 0x2 }, - { B43_CCK_RATE_11MB, 0x0, 0x3 }, - { B43_OFDM_RATE_6MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_BPSK }, - { B43_OFDM_RATE_9MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_BPSK }, - { B43_OFDM_RATE_12MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_QPSK }, - { B43_OFDM_RATE_18MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QPSK }, - { B43_OFDM_RATE_24MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_QAM16 }, - { B43_OFDM_RATE_36MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QAM16 }, - { B43_OFDM_RATE_48MB, B43_TXH_PHY1_CRATE_2_3, B43_TXH_PHY1_MODUL_QAM64 }, - { B43_OFDM_RATE_54MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QAM64 }, -}; - -static const struct b43_tx_legacy_rate_phy_ctl_entry * -b43_tx_legacy_rate_phy_ctl_ent(u8 bitrate) -{ - const struct b43_tx_legacy_rate_phy_ctl_entry *e; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(b43_tx_legacy_rate_phy_ctl); i++) { - e = &(b43_tx_legacy_rate_phy_ctl[i]); - if (e->bitrate == bitrate) - return e; - } - - B43_WARN_ON(1); - return NULL; -} - -/* Extract the bitrate index out of a CCK PLCP header. */ -static int b43_plcp_get_bitrate_idx_cck(struct b43_plcp_hdr6 *plcp) -{ - switch (plcp->raw[0]) { - case 0x0A: - return 0; - case 0x14: - return 1; - case 0x37: - return 2; - case 0x6E: - return 3; - } - return -1; -} - -/* Extract the bitrate index out of an OFDM PLCP header. */ -static int b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool ghz5) -{ - /* For 2 GHz band first OFDM rate is at index 4, see main.c */ - int base = ghz5 ? 0 : 4; - - switch (plcp->raw[0] & 0xF) { - case 0xB: - return base + 0; - case 0xF: - return base + 1; - case 0xA: - return base + 2; - case 0xE: - return base + 3; - case 0x9: - return base + 4; - case 0xD: - return base + 5; - case 0x8: - return base + 6; - case 0xC: - return base + 7; - } - return -1; -} - -u8 b43_plcp_get_ratecode_cck(const u8 bitrate) -{ - switch (bitrate) { - case B43_CCK_RATE_1MB: - return 0x0A; - case B43_CCK_RATE_2MB: - return 0x14; - case B43_CCK_RATE_5MB: - return 0x37; - case B43_CCK_RATE_11MB: - return 0x6E; - } - B43_WARN_ON(1); - return 0; -} - -u8 b43_plcp_get_ratecode_ofdm(const u8 bitrate) -{ - switch (bitrate) { - case B43_OFDM_RATE_6MB: - return 0xB; - case B43_OFDM_RATE_9MB: - return 0xF; - case B43_OFDM_RATE_12MB: - return 0xA; - case B43_OFDM_RATE_18MB: - return 0xE; - case B43_OFDM_RATE_24MB: - return 0x9; - case B43_OFDM_RATE_36MB: - return 0xD; - case B43_OFDM_RATE_48MB: - return 0x8; - case B43_OFDM_RATE_54MB: - return 0xC; - } - B43_WARN_ON(1); - return 0; -} - -void b43_generate_plcp_hdr(struct b43_plcp_hdr4 *plcp, - const u16 octets, const u8 bitrate) -{ - __u8 *raw = plcp->raw; - - if (b43_is_ofdm_rate(bitrate)) { - u32 d; - - d = b43_plcp_get_ratecode_ofdm(bitrate); - B43_WARN_ON(octets & 0xF000); - d |= (octets << 5); - plcp->data = cpu_to_le32(d); - } else { - u32 plen; - - plen = octets * 16 / bitrate; - if ((octets * 16 % bitrate) > 0) { - plen++; - if ((bitrate == B43_CCK_RATE_11MB) - && ((octets * 8 % 11) < 4)) { - raw[1] = 0x84; - } else - raw[1] = 0x04; - } else - raw[1] = 0x04; - plcp->data |= cpu_to_le32(plen << 16); - raw[0] = b43_plcp_get_ratecode_cck(bitrate); - } -} - -/* TODO: verify if needed for SSLPN or LCN */ -static u16 b43_generate_tx_phy_ctl1(struct b43_wldev *dev, u8 bitrate) -{ - const struct b43_phy *phy = &dev->phy; - const struct b43_tx_legacy_rate_phy_ctl_entry *e; - u16 control = 0; - u16 bw; - - if (phy->type == B43_PHYTYPE_LP) - bw = B43_TXH_PHY1_BW_20; - else /* FIXME */ - bw = B43_TXH_PHY1_BW_20; - - if (0) { /* FIXME: MIMO */ - } else if (b43_is_cck_rate(bitrate) && phy->type != B43_PHYTYPE_LP) { - control = bw; - } else { - control = bw; - e = b43_tx_legacy_rate_phy_ctl_ent(bitrate); - if (e) { - control |= e->coding_rate; - control |= e->modulation; - } - control |= B43_TXH_PHY1_MODE_SISO; - } - - return control; -} - -static u8 b43_calc_fallback_rate(u8 bitrate) -{ - switch (bitrate) { - case B43_CCK_RATE_1MB: - return B43_CCK_RATE_1MB; - case B43_CCK_RATE_2MB: - return B43_CCK_RATE_1MB; - case B43_CCK_RATE_5MB: - return B43_CCK_RATE_2MB; - case B43_CCK_RATE_11MB: - return B43_CCK_RATE_5MB; - case B43_OFDM_RATE_6MB: - return B43_CCK_RATE_5MB; - case B43_OFDM_RATE_9MB: - return B43_OFDM_RATE_6MB; - case B43_OFDM_RATE_12MB: - return B43_OFDM_RATE_9MB; - case B43_OFDM_RATE_18MB: - return B43_OFDM_RATE_12MB; - case B43_OFDM_RATE_24MB: - return B43_OFDM_RATE_18MB; - case B43_OFDM_RATE_36MB: - return B43_OFDM_RATE_24MB; - case B43_OFDM_RATE_48MB: - return B43_OFDM_RATE_36MB; - case B43_OFDM_RATE_54MB: - return B43_OFDM_RATE_48MB; - } - B43_WARN_ON(1); - return 0; -} - -/* Generate a TX data header. */ -int b43_generate_txhdr(struct b43_wldev *dev, - u8 *_txhdr, - struct sk_buff *skb_frag, - struct ieee80211_tx_info *info, - u16 cookie) -{ - const unsigned char *fragment_data = skb_frag->data; - unsigned int fragment_len = skb_frag->len; - struct b43_txhdr *txhdr = (struct b43_txhdr *)_txhdr; - const struct b43_phy *phy = &dev->phy; - const struct ieee80211_hdr *wlhdr = - (const struct ieee80211_hdr *)fragment_data; - int use_encryption = !!info->control.hw_key; - __le16 fctl = wlhdr->frame_control; - struct ieee80211_rate *fbrate; - u8 rate, rate_fb; - int rate_ofdm, rate_fb_ofdm; - unsigned int plcp_fragment_len; - u32 mac_ctl = 0; - u16 phy_ctl = 0; - bool fill_phy_ctl1 = (phy->type == B43_PHYTYPE_LP || - phy->type == B43_PHYTYPE_N || - phy->type == B43_PHYTYPE_HT); - u8 extra_ft = 0; - struct ieee80211_rate *txrate; - struct ieee80211_tx_rate *rates; - - memset(txhdr, 0, sizeof(*txhdr)); - - txrate = ieee80211_get_tx_rate(dev->wl->hw, info); - rate = txrate ? txrate->hw_value : B43_CCK_RATE_1MB; - rate_ofdm = b43_is_ofdm_rate(rate); - fbrate = ieee80211_get_alt_retry_rate(dev->wl->hw, info, 0) ? : txrate; - rate_fb = fbrate->hw_value; - rate_fb_ofdm = b43_is_ofdm_rate(rate_fb); - - if (rate_ofdm) - txhdr->phy_rate = b43_plcp_get_ratecode_ofdm(rate); - else - txhdr->phy_rate = b43_plcp_get_ratecode_cck(rate); - txhdr->mac_frame_ctl = wlhdr->frame_control; - memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN); - - /* Calculate duration for fallback rate */ - if ((rate_fb == rate) || - (wlhdr->duration_id & cpu_to_le16(0x8000)) || - (wlhdr->duration_id == cpu_to_le16(0))) { - /* If the fallback rate equals the normal rate or the - * dur_id field contains an AID, CFP magic or 0, - * use the original dur_id field. */ - txhdr->dur_fb = wlhdr->duration_id; - } else { - txhdr->dur_fb = ieee80211_generic_frame_duration( - dev->wl->hw, info->control.vif, info->band, - fragment_len, fbrate); - } - - plcp_fragment_len = fragment_len + FCS_LEN; - if (use_encryption) { - u8 key_idx = info->control.hw_key->hw_key_idx; - struct b43_key *key; - int wlhdr_len; - size_t iv_len; - - B43_WARN_ON(key_idx >= ARRAY_SIZE(dev->key)); - key = &(dev->key[key_idx]); - - if (unlikely(!key->keyconf)) { - /* This key is invalid. This might only happen - * in a short timeframe after machine resume before - * we were able to reconfigure keys. - * Drop this packet completely. Do not transmit it - * unencrypted to avoid leaking information. */ - return -ENOKEY; - } - - /* Hardware appends ICV. */ - plcp_fragment_len += info->control.hw_key->icv_len; - - key_idx = b43_kidx_to_fw(dev, key_idx); - mac_ctl |= (key_idx << B43_TXH_MAC_KEYIDX_SHIFT) & - B43_TXH_MAC_KEYIDX; - mac_ctl |= (key->algorithm << B43_TXH_MAC_KEYALG_SHIFT) & - B43_TXH_MAC_KEYALG; - wlhdr_len = ieee80211_hdrlen(fctl); - if (key->algorithm == B43_SEC_ALGO_TKIP) { - u16 phase1key[5]; - int i; - /* we give the phase1key and iv16 here, the key is stored in - * shm. With that the hardware can do phase 2 and encryption. - */ - ieee80211_get_tkip_p1k(info->control.hw_key, skb_frag, phase1key); - /* phase1key is in host endian. Copy to little-endian txhdr->iv. */ - for (i = 0; i < 5; i++) { - txhdr->iv[i * 2 + 0] = phase1key[i]; - txhdr->iv[i * 2 + 1] = phase1key[i] >> 8; - } - /* iv16 */ - memcpy(txhdr->iv + 10, ((u8 *) wlhdr) + wlhdr_len, 3); - } else { - iv_len = min_t(size_t, info->control.hw_key->iv_len, - ARRAY_SIZE(txhdr->iv)); - memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len); - } - } - switch (dev->fw.hdr_format) { - case B43_FW_HDR_598: - b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_598.plcp), - plcp_fragment_len, rate); - break; - case B43_FW_HDR_351: - b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_351.plcp), - plcp_fragment_len, rate); - break; - case B43_FW_HDR_410: - b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_410.plcp), - plcp_fragment_len, rate); - break; - } - b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->plcp_fb), - plcp_fragment_len, rate_fb); - - /* Extra Frame Types */ - if (rate_fb_ofdm) - extra_ft |= B43_TXH_EFT_FB_OFDM; - else - extra_ft |= B43_TXH_EFT_FB_CCK; - - /* Set channel radio code. Note that the micrcode ORs 0x100 to - * this value before comparing it to the value in SHM, if this - * is a 5Ghz packet. - */ - txhdr->chan_radio_code = phy->channel; - - /* PHY TX Control word */ - if (rate_ofdm) - phy_ctl |= B43_TXH_PHY_ENC_OFDM; - else - phy_ctl |= B43_TXH_PHY_ENC_CCK; - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) - phy_ctl |= B43_TXH_PHY_SHORTPRMBL; - - switch (b43_ieee80211_antenna_sanitize(dev, 0)) { - case 0: /* Default */ - phy_ctl |= B43_TXH_PHY_ANT01AUTO; - break; - case 1: /* Antenna 0 */ - phy_ctl |= B43_TXH_PHY_ANT0; - break; - case 2: /* Antenna 1 */ - phy_ctl |= B43_TXH_PHY_ANT1; - break; - case 3: /* Antenna 2 */ - phy_ctl |= B43_TXH_PHY_ANT2; - break; - case 4: /* Antenna 3 */ - phy_ctl |= B43_TXH_PHY_ANT3; - break; - default: - B43_WARN_ON(1); - } - - rates = info->control.rates; - /* MAC control */ - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) - mac_ctl |= B43_TXH_MAC_ACK; - /* use hardware sequence counter as the non-TID counter */ - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) - mac_ctl |= B43_TXH_MAC_HWSEQ; - if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) - mac_ctl |= B43_TXH_MAC_STMSDU; - if (!phy->gmode) - mac_ctl |= B43_TXH_MAC_5GHZ; - - /* Overwrite rates[0].count to make the retry calculation - * in the tx status easier. need the actual retry limit to - * detect whether the fallback rate was used. - */ - if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || - (rates[0].count <= dev->wl->hw->conf.long_frame_max_tx_count)) { - rates[0].count = dev->wl->hw->conf.long_frame_max_tx_count; - mac_ctl |= B43_TXH_MAC_LONGFRAME; - } else { - rates[0].count = dev->wl->hw->conf.short_frame_max_tx_count; - } - - /* Generate the RTS or CTS-to-self frame */ - if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || - (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) { - unsigned int len; - struct ieee80211_hdr *uninitialized_var(hdr); - int rts_rate, rts_rate_fb; - int rts_rate_ofdm, rts_rate_fb_ofdm; - struct b43_plcp_hdr6 *uninitialized_var(plcp); - struct ieee80211_rate *rts_cts_rate; - - rts_cts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info); - - rts_rate = rts_cts_rate ? rts_cts_rate->hw_value : B43_CCK_RATE_1MB; - rts_rate_ofdm = b43_is_ofdm_rate(rts_rate); - rts_rate_fb = b43_calc_fallback_rate(rts_rate); - rts_rate_fb_ofdm = b43_is_ofdm_rate(rts_rate_fb); - - if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - struct ieee80211_cts *uninitialized_var(cts); - - switch (dev->fw.hdr_format) { - case B43_FW_HDR_598: - cts = (struct ieee80211_cts *) - (txhdr->format_598.rts_frame); - break; - case B43_FW_HDR_351: - cts = (struct ieee80211_cts *) - (txhdr->format_351.rts_frame); - break; - case B43_FW_HDR_410: - cts = (struct ieee80211_cts *) - (txhdr->format_410.rts_frame); - break; - } - ieee80211_ctstoself_get(dev->wl->hw, info->control.vif, - fragment_data, fragment_len, - info, cts); - mac_ctl |= B43_TXH_MAC_SENDCTS; - len = sizeof(struct ieee80211_cts); - } else { - struct ieee80211_rts *uninitialized_var(rts); - - switch (dev->fw.hdr_format) { - case B43_FW_HDR_598: - rts = (struct ieee80211_rts *) - (txhdr->format_598.rts_frame); - break; - case B43_FW_HDR_351: - rts = (struct ieee80211_rts *) - (txhdr->format_351.rts_frame); - break; - case B43_FW_HDR_410: - rts = (struct ieee80211_rts *) - (txhdr->format_410.rts_frame); - break; - } - ieee80211_rts_get(dev->wl->hw, info->control.vif, - fragment_data, fragment_len, - info, rts); - mac_ctl |= B43_TXH_MAC_SENDRTS; - len = sizeof(struct ieee80211_rts); - } - len += FCS_LEN; - - /* Generate the PLCP headers for the RTS/CTS frame */ - switch (dev->fw.hdr_format) { - case B43_FW_HDR_598: - plcp = &txhdr->format_598.rts_plcp; - break; - case B43_FW_HDR_351: - plcp = &txhdr->format_351.rts_plcp; - break; - case B43_FW_HDR_410: - plcp = &txhdr->format_410.rts_plcp; - break; - } - b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)plcp, - len, rts_rate); - plcp = &txhdr->rts_plcp_fb; - b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)plcp, - len, rts_rate_fb); - - switch (dev->fw.hdr_format) { - case B43_FW_HDR_598: - hdr = (struct ieee80211_hdr *) - (&txhdr->format_598.rts_frame); - break; - case B43_FW_HDR_351: - hdr = (struct ieee80211_hdr *) - (&txhdr->format_351.rts_frame); - break; - case B43_FW_HDR_410: - hdr = (struct ieee80211_hdr *) - (&txhdr->format_410.rts_frame); - break; - } - txhdr->rts_dur_fb = hdr->duration_id; - - if (rts_rate_ofdm) { - extra_ft |= B43_TXH_EFT_RTS_OFDM; - txhdr->phy_rate_rts = - b43_plcp_get_ratecode_ofdm(rts_rate); - } else { - extra_ft |= B43_TXH_EFT_RTS_CCK; - txhdr->phy_rate_rts = - b43_plcp_get_ratecode_cck(rts_rate); - } - if (rts_rate_fb_ofdm) - extra_ft |= B43_TXH_EFT_RTSFB_OFDM; - else - extra_ft |= B43_TXH_EFT_RTSFB_CCK; - - if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS && - fill_phy_ctl1) { - txhdr->phy_ctl1_rts = cpu_to_le16( - b43_generate_tx_phy_ctl1(dev, rts_rate)); - txhdr->phy_ctl1_rts_fb = cpu_to_le16( - b43_generate_tx_phy_ctl1(dev, rts_rate_fb)); - } - } - - /* Magic cookie */ - switch (dev->fw.hdr_format) { - case B43_FW_HDR_598: - txhdr->format_598.cookie = cpu_to_le16(cookie); - break; - case B43_FW_HDR_351: - txhdr->format_351.cookie = cpu_to_le16(cookie); - break; - case B43_FW_HDR_410: - txhdr->format_410.cookie = cpu_to_le16(cookie); - break; - } - - if (fill_phy_ctl1) { - txhdr->phy_ctl1 = - cpu_to_le16(b43_generate_tx_phy_ctl1(dev, rate)); - txhdr->phy_ctl1_fb = - cpu_to_le16(b43_generate_tx_phy_ctl1(dev, rate_fb)); - } - - /* Apply the bitfields */ - txhdr->mac_ctl = cpu_to_le32(mac_ctl); - txhdr->phy_ctl = cpu_to_le16(phy_ctl); - txhdr->extra_ft = extra_ft; - - return 0; -} - -static s8 b43_rssi_postprocess(struct b43_wldev *dev, - u8 in_rssi, int ofdm, - int adjust_2053, int adjust_2050) -{ - struct b43_phy *phy = &dev->phy; - struct b43_phy_g *gphy = phy->g; - s32 tmp; - - switch (phy->radio_ver) { - case 0x2050: - if (ofdm) { - tmp = in_rssi; - if (tmp > 127) - tmp -= 256; - tmp *= 73; - tmp /= 64; - if (adjust_2050) - tmp += 25; - else - tmp -= 3; - } else { - if (dev->dev->bus_sprom-> - boardflags_lo & B43_BFL_RSSI) { - if (in_rssi > 63) - in_rssi = 63; - B43_WARN_ON(phy->type != B43_PHYTYPE_G); - tmp = gphy->nrssi_lt[in_rssi]; - tmp = 31 - tmp; - tmp *= -131; - tmp /= 128; - tmp -= 57; - } else { - tmp = in_rssi; - tmp = 31 - tmp; - tmp *= -149; - tmp /= 128; - tmp -= 68; - } - if (phy->type == B43_PHYTYPE_G && adjust_2050) - tmp += 25; - } - break; - case 0x2060: - if (in_rssi > 127) - tmp = in_rssi - 256; - else - tmp = in_rssi; - break; - default: - tmp = in_rssi; - tmp -= 11; - tmp *= 103; - tmp /= 64; - if (adjust_2053) - tmp -= 109; - else - tmp -= 83; - } - - return (s8) tmp; -} - -//TODO -#if 0 -static s8 b43_rssinoise_postprocess(struct b43_wldev *dev, u8 in_rssi) -{ - struct b43_phy *phy = &dev->phy; - s8 ret; - - if (phy->type == B43_PHYTYPE_A) { - //TODO: Incomplete specs. - ret = 0; - } else - ret = b43_rssi_postprocess(dev, in_rssi, 0, 1, 1); - - return ret; -} -#endif - -void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) -{ - struct ieee80211_rx_status status; - struct b43_plcp_hdr6 *plcp; - struct ieee80211_hdr *wlhdr; - const struct b43_rxhdr_fw4 *rxhdr = _rxhdr; - __le16 fctl; - u16 phystat0, phystat3; - u16 uninitialized_var(chanstat), uninitialized_var(mactime); - u32 uninitialized_var(macstat); - u16 chanid; - u16 phytype; - int padding, rate_idx; - - memset(&status, 0, sizeof(status)); - - /* Get metadata about the frame from the header. */ - phystat0 = le16_to_cpu(rxhdr->phy_status0); - phystat3 = le16_to_cpu(rxhdr->phy_status3); - switch (dev->fw.hdr_format) { - case B43_FW_HDR_598: - macstat = le32_to_cpu(rxhdr->format_598.mac_status); - mactime = le16_to_cpu(rxhdr->format_598.mac_time); - chanstat = le16_to_cpu(rxhdr->format_598.channel); - break; - case B43_FW_HDR_410: - case B43_FW_HDR_351: - macstat = le32_to_cpu(rxhdr->format_351.mac_status); - mactime = le16_to_cpu(rxhdr->format_351.mac_time); - chanstat = le16_to_cpu(rxhdr->format_351.channel); - break; - } - phytype = chanstat & B43_RX_CHAN_PHYTYPE; - - if (unlikely(macstat & B43_RX_MAC_FCSERR)) { - dev->wl->ieee_stats.dot11FCSErrorCount++; - status.flag |= RX_FLAG_FAILED_FCS_CRC; - } - if (unlikely(phystat0 & (B43_RX_PHYST0_PLCPHCF | B43_RX_PHYST0_PLCPFV))) - status.flag |= RX_FLAG_FAILED_PLCP_CRC; - if (phystat0 & B43_RX_PHYST0_SHORTPRMBL) - status.flag |= RX_FLAG_SHORTPRE; - if (macstat & B43_RX_MAC_DECERR) { - /* Decryption with the given key failed. - * Drop the packet. We also won't be able to decrypt it with - * the key in software. */ - goto drop; - } - - /* Skip PLCP and padding */ - padding = (macstat & B43_RX_MAC_PADDING) ? 2 : 0; - if (unlikely(skb->len < (sizeof(struct b43_plcp_hdr6) + padding))) { - b43dbg(dev->wl, "RX: Packet size underrun (1)\n"); - goto drop; - } - plcp = (struct b43_plcp_hdr6 *)(skb->data + padding); - skb_pull(skb, sizeof(struct b43_plcp_hdr6) + padding); - /* The skb contains the Wireless Header + payload data now */ - if (unlikely(skb->len < (2 + 2 + 6 /*minimum hdr */ + FCS_LEN))) { - b43dbg(dev->wl, "RX: Packet size underrun (2)\n"); - goto drop; - } - wlhdr = (struct ieee80211_hdr *)(skb->data); - fctl = wlhdr->frame_control; - - if (macstat & B43_RX_MAC_DEC) { - unsigned int keyidx; - int wlhdr_len; - - keyidx = ((macstat & B43_RX_MAC_KEYIDX) - >> B43_RX_MAC_KEYIDX_SHIFT); - /* We must adjust the key index here. We want the "physical" - * key index, but the ucode passed it slightly different. - */ - keyidx = b43_kidx_to_raw(dev, keyidx); - B43_WARN_ON(keyidx >= ARRAY_SIZE(dev->key)); - - if (dev->key[keyidx].algorithm != B43_SEC_ALGO_NONE) { - wlhdr_len = ieee80211_hdrlen(fctl); - if (unlikely(skb->len < (wlhdr_len + 3))) { - b43dbg(dev->wl, - "RX: Packet size underrun (3)\n"); - goto drop; - } - status.flag |= RX_FLAG_DECRYPTED; - } - } - - /* Link quality statistics */ - switch (chanstat & B43_RX_CHAN_PHYTYPE) { - case B43_PHYTYPE_HT: - /* TODO: is max the right choice? */ - status.signal = max_t(__s8, - max(rxhdr->phy_ht_power0, rxhdr->phy_ht_power1), - rxhdr->phy_ht_power2); - break; - case B43_PHYTYPE_N: - /* Broadcom has code for min and avg, but always uses max */ - if (rxhdr->power0 == 16 || rxhdr->power0 == 32) - status.signal = max(rxhdr->power1, rxhdr->power2); - else - status.signal = max(rxhdr->power0, rxhdr->power1); - break; - case B43_PHYTYPE_A: - case B43_PHYTYPE_B: - case B43_PHYTYPE_G: - case B43_PHYTYPE_LP: - status.signal = b43_rssi_postprocess(dev, rxhdr->jssi, - (phystat0 & B43_RX_PHYST0_OFDM), - (phystat0 & B43_RX_PHYST0_GAINCTL), - (phystat3 & B43_RX_PHYST3_TRSTATE)); - break; - } - - if (phystat0 & B43_RX_PHYST0_OFDM) - rate_idx = b43_plcp_get_bitrate_idx_ofdm(plcp, - !!(chanstat & B43_RX_CHAN_5GHZ)); - else - rate_idx = b43_plcp_get_bitrate_idx_cck(plcp); - if (unlikely(rate_idx == -1)) { - /* PLCP seems to be corrupted. - * Drop the frame, if we are not interested in corrupted frames. */ - if (!(dev->wl->filter_flags & FIF_PLCPFAIL)) - goto drop; - } - status.rate_idx = rate_idx; - status.antenna = !!(phystat0 & B43_RX_PHYST0_ANT); - - /* - * All frames on monitor interfaces and beacons always need a full - * 64-bit timestamp. Monitor interfaces need it for diagnostic - * purposes and beacons for IBSS merging. - * This code assumes we get to process the packet within 16 bits - * of timestamp, i.e. about 65 milliseconds after the PHY received - * the first symbol. - */ - if (ieee80211_is_beacon(fctl) || dev->wl->radiotap_enabled) { - u16 low_mactime_now; - - b43_tsf_read(dev, &status.mactime); - low_mactime_now = status.mactime; - status.mactime = status.mactime & ~0xFFFFULL; - status.mactime += mactime; - if (low_mactime_now <= mactime) - status.mactime -= 0x10000; - status.flag |= RX_FLAG_MACTIME_START; - } - - chanid = (chanstat & B43_RX_CHAN_ID) >> B43_RX_CHAN_ID_SHIFT; - switch (chanstat & B43_RX_CHAN_PHYTYPE) { - case B43_PHYTYPE_A: - status.band = IEEE80211_BAND_5GHZ; - B43_WARN_ON(1); - /* FIXME: We don't really know which value the "chanid" contains. - * So the following assignment might be wrong. */ - status.freq = - ieee80211_channel_to_frequency(chanid, status.band); - break; - case B43_PHYTYPE_G: - status.band = IEEE80211_BAND_2GHZ; - /* Somewhere between 478.104 and 508.1084 firmware for G-PHY - * has been modified to be compatible with N-PHY and others. - */ - if (dev->fw.rev >= 508) - status.freq = ieee80211_channel_to_frequency(chanid, status.band); - else - status.freq = chanid + 2400; - break; - case B43_PHYTYPE_N: - case B43_PHYTYPE_LP: - case B43_PHYTYPE_HT: - /* chanid is the SHM channel cookie. Which is the plain - * channel number in b43. */ - if (chanstat & B43_RX_CHAN_5GHZ) - status.band = IEEE80211_BAND_5GHZ; - else - status.band = IEEE80211_BAND_2GHZ; - status.freq = - ieee80211_channel_to_frequency(chanid, status.band); - break; - default: - B43_WARN_ON(1); - goto drop; - } - - memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx_ni(dev->wl->hw, skb); - -#if B43_DEBUG - dev->rx_count++; -#endif - return; -drop: - dev_kfree_skb_any(skb); -} - -void b43_handle_txstatus(struct b43_wldev *dev, - const struct b43_txstatus *status) -{ - b43_debugfs_log_txstat(dev, status); - - if (status->intermediate) - return; - if (status->for_ampdu) - return; - if (!status->acked) - dev->wl->ieee_stats.dot11ACKFailureCount++; - if (status->rts_count) { - if (status->rts_count == 0xF) //FIXME - dev->wl->ieee_stats.dot11RTSFailureCount++; - else - dev->wl->ieee_stats.dot11RTSSuccessCount++; - } - - if (b43_using_pio_transfers(dev)) - b43_pio_handle_txstatus(dev, status); - else - b43_dma_handle_txstatus(dev, status); - - b43_phy_txpower_check(dev, 0); -} - -/* Fill out the mac80211 TXstatus report based on the b43-specific - * txstatus report data. This returns a boolean whether the frame was - * successfully transmitted. */ -bool b43_fill_txstatus_report(struct b43_wldev *dev, - struct ieee80211_tx_info *report, - const struct b43_txstatus *status) -{ - bool frame_success = true; - int retry_limit; - - /* preserve the confiured retry limit before clearing the status - * The xmit function has overwritten the rc's value with the actual - * retry limit done by the hardware */ - retry_limit = report->status.rates[0].count; - ieee80211_tx_info_clear_status(report); - - if (status->acked) { - /* The frame was ACKed. */ - report->flags |= IEEE80211_TX_STAT_ACK; - } else { - /* The frame was not ACKed... */ - if (!(report->flags & IEEE80211_TX_CTL_NO_ACK)) { - /* ...but we expected an ACK. */ - frame_success = false; - } - } - if (status->frame_count == 0) { - /* The frame was not transmitted at all. */ - report->status.rates[0].count = 0; - } else if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) { - /* - * If the short retries (RTS, not data frame) have exceeded - * the limit, the hw will not have tried the selected rate, - * but will have used the fallback rate instead. - * Don't let the rate control count attempts for the selected - * rate in this case, otherwise the statistics will be off. - */ - report->status.rates[0].count = 0; - report->status.rates[1].count = status->frame_count; - } else { - if (status->frame_count > retry_limit) { - report->status.rates[0].count = retry_limit; - report->status.rates[1].count = status->frame_count - - retry_limit; - - } else { - report->status.rates[0].count = status->frame_count; - report->status.rates[1].idx = -1; - } - } - - return frame_success; -} - -/* Stop any TX operation on the device (suspend the hardware queues) */ -void b43_tx_suspend(struct b43_wldev *dev) -{ - if (b43_using_pio_transfers(dev)) - b43_pio_tx_suspend(dev); - else - b43_dma_tx_suspend(dev); -} - -/* Resume any TX operation on the device (resume the hardware queues) */ -void b43_tx_resume(struct b43_wldev *dev) -{ - if (b43_using_pio_transfers(dev)) - b43_pio_tx_resume(dev); - else - b43_dma_tx_resume(dev); -} diff --git a/drivers/net/wireless/b43/xmit.h b/drivers/net/wireless/b43/xmit.h deleted file mode 100644 index ba6115308068..000000000000 --- a/drivers/net/wireless/b43/xmit.h +++ /dev/null @@ -1,416 +0,0 @@ -#ifndef B43_XMIT_H_ -#define B43_XMIT_H_ - -#include "main.h" -#include - - -#define _b43_declare_plcp_hdr(size) \ - struct b43_plcp_hdr##size { \ - union { \ - __le32 data; \ - __u8 raw[size]; \ - } __packed; \ - } __packed - -/* struct b43_plcp_hdr4 */ -_b43_declare_plcp_hdr(4); -/* struct b43_plcp_hdr6 */ -_b43_declare_plcp_hdr(6); - -#undef _b43_declare_plcp_hdr - -/* TX header for v4 firmware */ -struct b43_txhdr { - __le32 mac_ctl; /* MAC TX control */ - __le16 mac_frame_ctl; /* Copy of the FrameControl field */ - __le16 tx_fes_time_norm; /* TX FES Time Normal */ - __le16 phy_ctl; /* PHY TX control */ - __le16 phy_ctl1; /* PHY TX control word 1 */ - __le16 phy_ctl1_fb; /* PHY TX control word 1 for fallback rates */ - __le16 phy_ctl1_rts; /* PHY TX control word 1 RTS */ - __le16 phy_ctl1_rts_fb; /* PHY TX control word 1 RTS for fallback rates */ - __u8 phy_rate; /* PHY rate */ - __u8 phy_rate_rts; /* PHY rate for RTS/CTS */ - __u8 extra_ft; /* Extra Frame Types */ - __u8 chan_radio_code; /* Channel Radio Code */ - __u8 iv[16]; /* Encryption IV */ - __u8 tx_receiver[6]; /* TX Frame Receiver address */ - __le16 tx_fes_time_fb; /* TX FES Time Fallback */ - struct b43_plcp_hdr6 rts_plcp_fb; /* RTS fallback PLCP header */ - __le16 rts_dur_fb; /* RTS fallback duration */ - struct b43_plcp_hdr6 plcp_fb; /* Fallback PLCP header */ - __le16 dur_fb; /* Fallback duration */ - __le16 mimo_modelen; /* MIMO mode length */ - __le16 mimo_ratelen_fb; /* MIMO fallback rate length */ - __le32 timeout; /* Timeout */ - - union { - /* Tested with 598.314, 644.1001 and 666.2 */ - struct { - __le16 mimo_antenna; /* MIMO antenna select */ - __le16 preload_size; /* Preload size */ - PAD_BYTES(2); - __le16 cookie; /* TX frame cookie */ - __le16 tx_status; /* TX status */ - __le16 max_n_mpdus; - __le16 max_a_bytes_mrt; - __le16 max_a_bytes_fbr; - __le16 min_m_bytes; - struct b43_plcp_hdr6 rts_plcp; /* RTS PLCP header */ - __u8 rts_frame[16]; /* The RTS frame (if used) */ - PAD_BYTES(2); - struct b43_plcp_hdr6 plcp; /* Main PLCP header */ - } format_598 __packed; - - /* Tested with 410.2160, 478.104 and 508.* */ - struct { - __le16 mimo_antenna; /* MIMO antenna select */ - __le16 preload_size; /* Preload size */ - PAD_BYTES(2); - __le16 cookie; /* TX frame cookie */ - __le16 tx_status; /* TX status */ - struct b43_plcp_hdr6 rts_plcp; /* RTS PLCP header */ - __u8 rts_frame[16]; /* The RTS frame (if used) */ - PAD_BYTES(2); - struct b43_plcp_hdr6 plcp; /* Main PLCP header */ - } format_410 __packed; - - /* Tested with 351.126 */ - struct { - PAD_BYTES(2); - __le16 cookie; /* TX frame cookie */ - __le16 tx_status; /* TX status */ - struct b43_plcp_hdr6 rts_plcp; /* RTS PLCP header */ - __u8 rts_frame[16]; /* The RTS frame (if used) */ - PAD_BYTES(2); - struct b43_plcp_hdr6 plcp; /* Main PLCP header */ - } format_351 __packed; - - } __packed; -} __packed; - -struct b43_tx_legacy_rate_phy_ctl_entry { - u8 bitrate; - u16 coding_rate; - u16 modulation; -}; - -/* MAC TX control */ -#define B43_TXH_MAC_RTS_FB_SHORTPRMBL 0x80000000 /* RTS fallback preamble */ -#define B43_TXH_MAC_RTS_SHORTPRMBL 0x40000000 /* RTS main rate preamble */ -#define B43_TXH_MAC_FB_SHORTPRMBL 0x20000000 /* Main fallback preamble */ -#define B43_TXH_MAC_USEFBR 0x10000000 /* Use fallback rate for this AMPDU */ -#define B43_TXH_MAC_KEYIDX 0x0FF00000 /* Security key index */ -#define B43_TXH_MAC_KEYIDX_SHIFT 20 -#define B43_TXH_MAC_ALT_TXPWR 0x00080000 /* Use alternate txpwr defined at loc. M_ALT_TXPWR_IDX */ -#define B43_TXH_MAC_KEYALG 0x00070000 /* Security key algorithm */ -#define B43_TXH_MAC_KEYALG_SHIFT 16 -#define B43_TXH_MAC_AMIC 0x00008000 /* AMIC */ -#define B43_TXH_MAC_RIFS 0x00004000 /* Use RIFS */ -#define B43_TXH_MAC_LIFETIME 0x00002000 /* Lifetime */ -#define B43_TXH_MAC_FRAMEBURST 0x00001000 /* Frameburst */ -#define B43_TXH_MAC_SENDCTS 0x00000800 /* Send CTS-to-self */ -#define B43_TXH_MAC_AMPDU 0x00000600 /* AMPDU status */ -#define B43_TXH_MAC_AMPDU_MPDU 0x00000000 /* Regular MPDU, not an AMPDU */ -#define B43_TXH_MAC_AMPDU_FIRST 0x00000200 /* First MPDU or AMPDU */ -#define B43_TXH_MAC_AMPDU_INTER 0x00000400 /* Intermediate MPDU or AMPDU */ -#define B43_TXH_MAC_AMPDU_LAST 0x00000600 /* Last (or only) MPDU of AMPDU */ -#define B43_TXH_MAC_40MHZ 0x00000100 /* Use 40 MHz bandwidth */ -#define B43_TXH_MAC_5GHZ 0x00000080 /* 5GHz band */ -#define B43_TXH_MAC_DFCS 0x00000040 /* DFCS */ -#define B43_TXH_MAC_IGNPMQ 0x00000020 /* Ignore PMQ */ -#define B43_TXH_MAC_HWSEQ 0x00000010 /* Use Hardware Sequence Number */ -#define B43_TXH_MAC_STMSDU 0x00000008 /* Start MSDU */ -#define B43_TXH_MAC_SENDRTS 0x00000004 /* Send RTS */ -#define B43_TXH_MAC_LONGFRAME 0x00000002 /* Long frame */ -#define B43_TXH_MAC_ACK 0x00000001 /* Immediate ACK */ - -/* Extra Frame Types */ -#define B43_TXH_EFT_FB 0x03 /* Data frame fallback encoding */ -#define B43_TXH_EFT_FB_CCK 0x00 /* CCK */ -#define B43_TXH_EFT_FB_OFDM 0x01 /* OFDM */ -#define B43_TXH_EFT_FB_HT 0x02 /* HT */ -#define B43_TXH_EFT_FB_VHT 0x03 /* VHT */ -#define B43_TXH_EFT_RTS 0x0C /* RTS/CTS encoding */ -#define B43_TXH_EFT_RTS_CCK 0x00 /* CCK */ -#define B43_TXH_EFT_RTS_OFDM 0x04 /* OFDM */ -#define B43_TXH_EFT_RTS_HT 0x08 /* HT */ -#define B43_TXH_EFT_RTS_VHT 0x0C /* VHT */ -#define B43_TXH_EFT_RTSFB 0x30 /* RTS/CTS fallback encoding */ -#define B43_TXH_EFT_RTSFB_CCK 0x00 /* CCK */ -#define B43_TXH_EFT_RTSFB_OFDM 0x10 /* OFDM */ -#define B43_TXH_EFT_RTSFB_HT 0x20 /* HT */ -#define B43_TXH_EFT_RTSFB_VHT 0x30 /* VHT */ - -/* PHY TX control word */ -#define B43_TXH_PHY_ENC 0x0003 /* Data frame encoding */ -#define B43_TXH_PHY_ENC_CCK 0x0000 /* CCK */ -#define B43_TXH_PHY_ENC_OFDM 0x0001 /* OFDM */ -#define B43_TXH_PHY_ENC_HT 0x0002 /* HT */ -#define B43_TXH_PHY_ENC_VHT 0x0003 /* VHT */ -#define B43_TXH_PHY_SHORTPRMBL 0x0010 /* Use short preamble */ -#define B43_TXH_PHY_ANT 0x03C0 /* Antenna selection */ -#define B43_TXH_PHY_ANT0 0x0000 /* Use antenna 0 */ -#define B43_TXH_PHY_ANT1 0x0040 /* Use antenna 1 */ -#define B43_TXH_PHY_ANT01AUTO 0x00C0 /* Use antenna 0/1 auto */ -#define B43_TXH_PHY_ANT2 0x0100 /* Use antenna 2 */ -#define B43_TXH_PHY_ANT3 0x0200 /* Use antenna 3 */ -#define B43_TXH_PHY_TXPWR 0xFC00 /* TX power */ -#define B43_TXH_PHY_TXPWR_SHIFT 10 - -/* PHY TX control word 1 */ -#define B43_TXH_PHY1_BW 0x0007 /* Bandwidth */ -#define B43_TXH_PHY1_BW_10 0x0000 /* 10 MHz */ -#define B43_TXH_PHY1_BW_10U 0x0001 /* 10 MHz upper */ -#define B43_TXH_PHY1_BW_20 0x0002 /* 20 MHz */ -#define B43_TXH_PHY1_BW_20U 0x0003 /* 20 MHz upper */ -#define B43_TXH_PHY1_BW_40 0x0004 /* 40 MHz */ -#define B43_TXH_PHY1_BW_40DUP 0x0005 /* 40 MHz duplicate */ -#define B43_TXH_PHY1_MODE 0x0038 /* Mode */ -#define B43_TXH_PHY1_MODE_SISO 0x0000 /* SISO */ -#define B43_TXH_PHY1_MODE_CDD 0x0008 /* CDD */ -#define B43_TXH_PHY1_MODE_STBC 0x0010 /* STBC */ -#define B43_TXH_PHY1_MODE_SDM 0x0018 /* SDM */ -#define B43_TXH_PHY1_CRATE 0x0700 /* Coding rate */ -#define B43_TXH_PHY1_CRATE_1_2 0x0000 /* 1/2 */ -#define B43_TXH_PHY1_CRATE_2_3 0x0100 /* 2/3 */ -#define B43_TXH_PHY1_CRATE_3_4 0x0200 /* 3/4 */ -#define B43_TXH_PHY1_CRATE_4_5 0x0300 /* 4/5 */ -#define B43_TXH_PHY1_CRATE_5_6 0x0400 /* 5/6 */ -#define B43_TXH_PHY1_CRATE_7_8 0x0600 /* 7/8 */ -#define B43_TXH_PHY1_MODUL 0x3800 /* Modulation scheme */ -#define B43_TXH_PHY1_MODUL_BPSK 0x0000 /* BPSK */ -#define B43_TXH_PHY1_MODUL_QPSK 0x0800 /* QPSK */ -#define B43_TXH_PHY1_MODUL_QAM16 0x1000 /* QAM16 */ -#define B43_TXH_PHY1_MODUL_QAM64 0x1800 /* QAM64 */ -#define B43_TXH_PHY1_MODUL_QAM256 0x2000 /* QAM256 */ - - -static inline -size_t b43_txhdr_size(struct b43_wldev *dev) -{ - switch (dev->fw.hdr_format) { - case B43_FW_HDR_598: - return 112 + sizeof(struct b43_plcp_hdr6); - case B43_FW_HDR_410: - return 104 + sizeof(struct b43_plcp_hdr6); - case B43_FW_HDR_351: - return 100 + sizeof(struct b43_plcp_hdr6); - } - return 0; -} - - -int b43_generate_txhdr(struct b43_wldev *dev, - u8 * txhdr, - struct sk_buff *skb_frag, - struct ieee80211_tx_info *txctl, u16 cookie); - -/* Transmit Status */ -struct b43_txstatus { - u16 cookie; /* The cookie from the txhdr */ - u16 seq; /* Sequence number */ - u8 phy_stat; /* PHY TX status */ - u8 frame_count; /* Frame transmit count */ - u8 rts_count; /* RTS transmit count */ - u8 supp_reason; /* Suppression reason */ - /* flags */ - u8 pm_indicated; /* PM mode indicated to AP */ - u8 intermediate; /* Intermediate status notification (not final) */ - u8 for_ampdu; /* Status is for an AMPDU (afterburner) */ - u8 acked; /* Wireless ACK received */ -}; - -/* txstatus supp_reason values */ -enum { - B43_TXST_SUPP_NONE, /* Not suppressed */ - B43_TXST_SUPP_PMQ, /* Suppressed due to PMQ entry */ - B43_TXST_SUPP_FLUSH, /* Suppressed due to flush request */ - B43_TXST_SUPP_PREV, /* Previous fragment failed */ - B43_TXST_SUPP_CHAN, /* Channel mismatch */ - B43_TXST_SUPP_LIFE, /* Lifetime expired */ - B43_TXST_SUPP_UNDER, /* Buffer underflow */ - B43_TXST_SUPP_ABNACK, /* Afterburner NACK */ -}; - -/* Receive header for v4 firmware. */ -struct b43_rxhdr_fw4 { - __le16 frame_len; /* Frame length */ - PAD_BYTES(2); - __le16 phy_status0; /* PHY RX Status 0 */ - union { - /* RSSI for A/B/G-PHYs */ - struct { - __u8 jssi; /* PHY RX Status 1: JSSI */ - __u8 sig_qual; /* PHY RX Status 1: Signal Quality */ - } __packed; - - /* RSSI for N-PHYs */ - struct { - __s8 power0; /* PHY RX Status 1: Power 0 */ - __s8 power1; /* PHY RX Status 1: Power 1 */ - } __packed; - } __packed; - union { - /* HT-PHY */ - struct { - PAD_BYTES(1); - __s8 phy_ht_power0; - } __packed; - - /* RSSI for N-PHYs */ - struct { - __s8 power2; - PAD_BYTES(1); - } __packed; - - __le16 phy_status2; /* PHY RX Status 2 */ - } __packed; - union { - /* HT-PHY */ - struct { - __s8 phy_ht_power1; - __s8 phy_ht_power2; - } __packed; - - __le16 phy_status3; /* PHY RX Status 3 */ - } __packed; - union { - /* Tested with 598.314, 644.1001 and 666.2 */ - struct { - __le16 phy_status4; /* PHY RX Status 4 */ - __le16 phy_status5; /* PHY RX Status 5 */ - __le32 mac_status; /* MAC RX status */ - __le16 mac_time; - __le16 channel; - } format_598 __packed; - - /* Tested with 351.126, 410.2160, 478.104 and 508.* */ - struct { - __le32 mac_status; /* MAC RX status */ - __le16 mac_time; - __le16 channel; - } format_351 __packed; - } __packed; -} __packed; - -/* PHY RX Status 0 */ -#define B43_RX_PHYST0_GAINCTL 0x4000 /* Gain Control */ -#define B43_RX_PHYST0_PLCPHCF 0x0200 -#define B43_RX_PHYST0_PLCPFV 0x0100 -#define B43_RX_PHYST0_SHORTPRMBL 0x0080 /* Received with Short Preamble */ -#define B43_RX_PHYST0_LCRS 0x0040 -#define B43_RX_PHYST0_ANT 0x0020 /* Antenna */ -#define B43_RX_PHYST0_UNSRATE 0x0010 -#define B43_RX_PHYST0_CLIP 0x000C -#define B43_RX_PHYST0_CLIP_SHIFT 2 -#define B43_RX_PHYST0_FTYPE 0x0003 /* Frame type */ -#define B43_RX_PHYST0_CCK 0x0000 /* Frame type: CCK */ -#define B43_RX_PHYST0_OFDM 0x0001 /* Frame type: OFDM */ -#define B43_RX_PHYST0_PRE_N 0x0002 /* Pre-standard N-PHY frame */ -#define B43_RX_PHYST0_STD_N 0x0003 /* Standard N-PHY frame */ - -/* PHY RX Status 2 */ -#define B43_RX_PHYST2_LNAG 0xC000 /* LNA Gain */ -#define B43_RX_PHYST2_LNAG_SHIFT 14 -#define B43_RX_PHYST2_PNAG 0x3C00 /* PNA Gain */ -#define B43_RX_PHYST2_PNAG_SHIFT 10 -#define B43_RX_PHYST2_FOFF 0x03FF /* F offset */ - -/* PHY RX Status 3 */ -#define B43_RX_PHYST3_DIGG 0x1800 /* DIG Gain */ -#define B43_RX_PHYST3_DIGG_SHIFT 11 -#define B43_RX_PHYST3_TRSTATE 0x0400 /* TR state */ - -/* MAC RX Status */ -#define B43_RX_MAC_RXST_VALID 0x01000000 /* PHY RXST valid */ -#define B43_RX_MAC_TKIP_MICERR 0x00100000 /* TKIP MIC error */ -#define B43_RX_MAC_TKIP_MICATT 0x00080000 /* TKIP MIC attempted */ -#define B43_RX_MAC_AGGTYPE 0x00060000 /* Aggregation type */ -#define B43_RX_MAC_AGGTYPE_SHIFT 17 -#define B43_RX_MAC_AMSDU 0x00010000 /* A-MSDU mask */ -#define B43_RX_MAC_BEACONSENT 0x00008000 /* Beacon sent flag */ -#define B43_RX_MAC_KEYIDX 0x000007E0 /* Key index */ -#define B43_RX_MAC_KEYIDX_SHIFT 5 -#define B43_RX_MAC_DECERR 0x00000010 /* Decrypt error */ -#define B43_RX_MAC_DEC 0x00000008 /* Decryption attempted */ -#define B43_RX_MAC_PADDING 0x00000004 /* Pad bytes present */ -#define B43_RX_MAC_RESP 0x00000002 /* Response frame transmitted */ -#define B43_RX_MAC_FCSERR 0x00000001 /* FCS error */ - -/* RX channel */ -#define B43_RX_CHAN_40MHZ 0x1000 /* 40 Mhz channel width */ -#define B43_RX_CHAN_5GHZ 0x0800 /* 5 Ghz band */ -#define B43_RX_CHAN_ID 0x07F8 /* Channel ID */ -#define B43_RX_CHAN_ID_SHIFT 3 -#define B43_RX_CHAN_PHYTYPE 0x0007 /* PHY type */ - - -u8 b43_plcp_get_ratecode_cck(const u8 bitrate); -u8 b43_plcp_get_ratecode_ofdm(const u8 bitrate); - -void b43_generate_plcp_hdr(struct b43_plcp_hdr4 *plcp, - const u16 octets, const u8 bitrate); - -void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr); - -void b43_handle_txstatus(struct b43_wldev *dev, - const struct b43_txstatus *status); -bool b43_fill_txstatus_report(struct b43_wldev *dev, - struct ieee80211_tx_info *report, - const struct b43_txstatus *status); - -void b43_tx_suspend(struct b43_wldev *dev); -void b43_tx_resume(struct b43_wldev *dev); - - -/* Helper functions for converting the key-table index from "firmware-format" - * to "raw-format" and back. The firmware API changed for this at some revision. - * We need to account for that here. */ -static inline int b43_new_kidx_api(struct b43_wldev *dev) -{ - /* FIXME: Not sure the change was at rev 351 */ - return (dev->fw.rev >= 351); -} -static inline u8 b43_kidx_to_fw(struct b43_wldev *dev, u8 raw_kidx) -{ - u8 firmware_kidx; - if (b43_new_kidx_api(dev)) { - firmware_kidx = raw_kidx; - } else { - if (raw_kidx >= 4) /* Is per STA key? */ - firmware_kidx = raw_kidx - 4; - else - firmware_kidx = raw_kidx; /* TX default key */ - } - return firmware_kidx; -} -static inline u8 b43_kidx_to_raw(struct b43_wldev *dev, u8 firmware_kidx) -{ - u8 raw_kidx; - if (b43_new_kidx_api(dev)) - raw_kidx = firmware_kidx; - else - raw_kidx = firmware_kidx + 4; /* RX default keys or per STA keys */ - return raw_kidx; -} - -/* struct b43_private_tx_info - TX info private to b43. - * The structure is placed in (struct ieee80211_tx_info *)->rate_driver_data - * - * @bouncebuffer: DMA Bouncebuffer (if used) - */ -struct b43_private_tx_info { - void *bouncebuffer; -}; - -static inline struct b43_private_tx_info * -b43_get_priv_tx_info(struct ieee80211_tx_info *info) -{ - BUILD_BUG_ON(sizeof(struct b43_private_tx_info) > - sizeof(info->rate_driver_data)); - return (struct b43_private_tx_info *)info->rate_driver_data; -} - -#endif /* B43_XMIT_H_ */ diff --git a/drivers/net/wireless/broadcom/Kconfig b/drivers/net/wireless/broadcom/Kconfig new file mode 100644 index 000000000000..0ba8119d2cf2 --- /dev/null +++ b/drivers/net/wireless/broadcom/Kconfig @@ -0,0 +1,16 @@ +config WLAN_VENDOR_BROADCOM + bool "Broadcom devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_BROADCOM + +source "drivers/net/wireless/broadcom/b43/Kconfig" + +endif # WLAN_VENDOR_BROADCOM diff --git a/drivers/net/wireless/broadcom/Makefile b/drivers/net/wireless/broadcom/Makefile new file mode 100644 index 000000000000..0140a810107c --- /dev/null +++ b/drivers/net/wireless/broadcom/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_B43) += b43/ diff --git a/drivers/net/wireless/broadcom/b43/Kconfig b/drivers/net/wireless/broadcom/b43/Kconfig new file mode 100644 index 000000000000..fba856032ca5 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/Kconfig @@ -0,0 +1,187 @@ +config B43 + tristate "Broadcom 43xx wireless support (mac80211 stack)" + depends on (BCMA_POSSIBLE || SSB_POSSIBLE) && MAC80211 && HAS_DMA + select BCMA if B43_BCMA + select SSB if B43_SSB + select FW_LOADER + ---help--- + b43 is a driver for the Broadcom 43xx series wireless devices. + + Check "lspci" for something like + "Broadcom Corporation BCM43XX 802.11 Wireless LAN Controller" + to determine whether you own such a device. + + This driver supports the new BCM43xx IEEE 802.11G devices, but not + the old IEEE 802.11B devices. Old devices are supported by + the b43legacy driver. + Note that this has nothing to do with the standard that your AccessPoint + supports (A, B, G or a combination). + IEEE 802.11G devices can talk to IEEE 802.11B AccessPoints. + + It is safe to include both b43 and b43legacy as the underlying glue + layer will automatically load the correct version for your device. + + This driver uses V4 firmware, which must be installed separately using + b43-fwcutter. + + This driver can be built as a module (recommended) that will be called "b43". + If unsure, say M. + +config B43_BCMA + bool + +config B43_SSB + bool + +choice + prompt "Supported bus types" + depends on B43 + default B43_BUSES_BCMA_AND_SSB + +config B43_BUSES_BCMA_AND_SSB + bool "BCMA and SSB" + depends on BCMA_POSSIBLE && SSB_POSSIBLE + select B43_BCMA + select B43_SSB + +config B43_BUSES_BCMA + bool "BCMA only" + depends on BCMA_POSSIBLE + select B43_BCMA + +config B43_BUSES_SSB + bool "SSB only" + depends on SSB_POSSIBLE + select B43_SSB + +endchoice + +# Auto-select SSB PCI-HOST support, if possible +config B43_PCI_AUTOSELECT + bool + depends on B43 && SSB_PCIHOST_POSSIBLE + select SSB_PCIHOST + select SSB_B43_PCI_BRIDGE + default y + +# Auto-select SSB PCICORE driver, if possible +config B43_PCICORE_AUTOSELECT + bool + depends on B43 && SSB_DRIVER_PCICORE_POSSIBLE + select SSB_DRIVER_PCICORE + default y + +config B43_SDIO + bool "Broadcom 43xx SDIO device support" + depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE + select SSB_SDIOHOST + ---help--- + Broadcom 43xx device support for Soft-MAC SDIO devices. + + With this config option you can drive Soft-MAC b43 cards with a + Secure Digital I/O interface. + This includes the WLAN daughter card found on the Nintendo Wii + video game console. + Note that this does not support Broadcom 43xx Full-MAC devices. + + It's safe to select Y here, even if you don't have a B43 SDIO device. + + If unsure, say N. + +#Data transfers to the device via PIO. We want it as a fallback even +# if we can do DMA. +config B43_BCMA_PIO + bool + depends on B43 && B43_BCMA + select BCMA_BLOCKIO + default y + +config B43_PIO + bool + depends on B43 && B43_SSB + select SSB_BLOCKIO + default y + +config B43_PHY_G + bool "Support for G-PHY (802.11g) devices" + depends on B43 && B43_SSB + default y + ---help--- + This PHY type can be found in the following chipsets: + PCI: BCM4306, BCM4311, BCM4318 + SoC: BCM4712, BCM5352E + +config B43_PHY_N + bool "Support for N-PHY (the main 802.11n series) devices" + depends on B43 + default y + ---help--- + This PHY type can be found in the following chipsets: + PCI: BCM4321, BCM4322, + BCM43222, BCM43224, BCM43225, + BCM43131, BCM43217, BCM43227, BCM43228 + SoC: BCM4716, BCM4717, BCM4718, BCM5356, BCM5357, BCM5358 + +config B43_PHY_LP + bool "Support for LP-PHY (low-power 802.11g) devices" + depends on B43 && B43_SSB + default y + ---help--- + The LP-PHY is a low-power PHY built into some notebooks + and embedded devices. It supports 802.11a/b/g + (802.11a support is optional, and currently disabled). + +config B43_PHY_HT + bool "Support for HT-PHY (high throughput 802.11n) devices" + depends on B43 && B43_BCMA + default y + ---help--- + This PHY type with 3x3:3 MIMO can be found in the BCM4331 PCI chipset. + +config B43_PHY_LCN + bool "Support for LCN-PHY devices (BROKEN)" + depends on B43 && BROKEN + ---help--- + Support for the LCN-PHY. + + Say N, this is BROKEN and crashes driver. + +config B43_PHY_AC + bool "Support for AC-PHY (802.11ac) devices (BROKEN)" + depends on B43 && B43_BCMA && BROKEN + ---help--- + This PHY type can be found in the following chipsets: + PCI: BCM4352, BCM4360 + + Say N, this is BROKEN and crashes driver. + +# This config option automatically enables b43 LEDS support, +# if it's possible. +config B43_LEDS + bool + depends on B43 && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = B43) + default y + +# This config option automatically enables b43 HW-RNG support, +# if the HW-RNG core is enabled. +config B43_HWRNG + bool + depends on B43 && (HW_RANDOM = y || HW_RANDOM = B43) + default y + +config B43_DEBUG + bool "Broadcom 43xx debugging" + depends on B43 + ---help--- + Broadcom 43xx debugging. + + This adds additional runtime sanity checks and statistics to the driver. + These checks and statistics might be expensive and hurt the runtime + performance of your system. + This also adds the b43 debugfs interface. + + Do not enable this, unless you are debugging the driver. + + Say N, if you are a distributor or user building a release kernel + for production use. + Only say Y, if you are debugging a problem in the b43 driver sourcecode. diff --git a/drivers/net/wireless/broadcom/b43/Makefile b/drivers/net/wireless/broadcom/b43/Makefile new file mode 100644 index 000000000000..ddc4df46656f --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/Makefile @@ -0,0 +1,27 @@ +b43-y += main.o +b43-y += bus.o +b43-$(CONFIG_B43_PHY_G) += phy_a.o phy_g.o tables.o lo.o wa.o +b43-$(CONFIG_B43_PHY_N) += tables_nphy.o +b43-$(CONFIG_B43_PHY_N) += radio_2055.o +b43-$(CONFIG_B43_PHY_N) += radio_2056.o +b43-$(CONFIG_B43_PHY_N) += radio_2057.o +b43-y += phy_common.o +b43-$(CONFIG_B43_PHY_N) += phy_n.o +b43-$(CONFIG_B43_PHY_LP) += phy_lp.o +b43-$(CONFIG_B43_PHY_LP) += tables_lpphy.o +b43-$(CONFIG_B43_PHY_HT) += phy_ht.o +b43-$(CONFIG_B43_PHY_HT) += tables_phy_ht.o +b43-$(CONFIG_B43_PHY_HT) += radio_2059.o +b43-$(CONFIG_B43_PHY_LCN) += phy_lcn.o tables_phy_lcn.o +b43-$(CONFIG_B43_PHY_AC) += phy_ac.o +b43-y += sysfs.o +b43-y += xmit.o +b43-y += dma.o +b43-y += pio.o +b43-y += rfkill.o +b43-y += ppr.o +b43-$(CONFIG_B43_LEDS) += leds.o +b43-$(CONFIG_B43_SDIO) += sdio.o +b43-$(CONFIG_B43_DEBUG) += debugfs.o + +obj-$(CONFIG_B43) += b43.o diff --git a/drivers/net/wireless/broadcom/b43/b43.h b/drivers/net/wireless/broadcom/b43/b43.h new file mode 100644 index 000000000000..036552439816 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/b43.h @@ -0,0 +1,1108 @@ +#ifndef B43_H_ +#define B43_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debugfs.h" +#include "leds.h" +#include "rfkill.h" +#include "bus.h" +#include "lo.h" +#include "phy_common.h" + + +#ifdef CONFIG_B43_DEBUG +# define B43_DEBUG 1 +#else +# define B43_DEBUG 0 +#endif + +/* MMIO offsets */ +#define B43_MMIO_DMA0_REASON 0x20 +#define B43_MMIO_DMA0_IRQ_MASK 0x24 +#define B43_MMIO_DMA1_REASON 0x28 +#define B43_MMIO_DMA1_IRQ_MASK 0x2C +#define B43_MMIO_DMA2_REASON 0x30 +#define B43_MMIO_DMA2_IRQ_MASK 0x34 +#define B43_MMIO_DMA3_REASON 0x38 +#define B43_MMIO_DMA3_IRQ_MASK 0x3C +#define B43_MMIO_DMA4_REASON 0x40 +#define B43_MMIO_DMA4_IRQ_MASK 0x44 +#define B43_MMIO_DMA5_REASON 0x48 +#define B43_MMIO_DMA5_IRQ_MASK 0x4C +#define B43_MMIO_MACCTL 0x120 /* MAC control */ +#define B43_MMIO_MACCMD 0x124 /* MAC command */ +#define B43_MMIO_GEN_IRQ_REASON 0x128 +#define B43_MMIO_GEN_IRQ_MASK 0x12C +#define B43_MMIO_RAM_CONTROL 0x130 +#define B43_MMIO_RAM_DATA 0x134 +#define B43_MMIO_PS_STATUS 0x140 +#define B43_MMIO_RADIO_HWENABLED_HI 0x158 +#define B43_MMIO_MAC_HW_CAP 0x15C /* MAC capabilities (corerev >= 13) */ +#define B43_MMIO_SHM_CONTROL 0x160 +#define B43_MMIO_SHM_DATA 0x164 +#define B43_MMIO_SHM_DATA_UNALIGNED 0x166 +#define B43_MMIO_XMITSTAT_0 0x170 +#define B43_MMIO_XMITSTAT_1 0x174 +#define B43_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */ +#define B43_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */ +#define B43_MMIO_TSF_CFP_REP 0x188 +#define B43_MMIO_TSF_CFP_START 0x18C +#define B43_MMIO_TSF_CFP_MAXDUR 0x190 + +/* 32-bit DMA */ +#define B43_MMIO_DMA32_BASE0 0x200 +#define B43_MMIO_DMA32_BASE1 0x220 +#define B43_MMIO_DMA32_BASE2 0x240 +#define B43_MMIO_DMA32_BASE3 0x260 +#define B43_MMIO_DMA32_BASE4 0x280 +#define B43_MMIO_DMA32_BASE5 0x2A0 +/* 64-bit DMA */ +#define B43_MMIO_DMA64_BASE0 0x200 +#define B43_MMIO_DMA64_BASE1 0x240 +#define B43_MMIO_DMA64_BASE2 0x280 +#define B43_MMIO_DMA64_BASE3 0x2C0 +#define B43_MMIO_DMA64_BASE4 0x300 +#define B43_MMIO_DMA64_BASE5 0x340 + +/* PIO on core rev < 11 */ +#define B43_MMIO_PIO_BASE0 0x300 +#define B43_MMIO_PIO_BASE1 0x310 +#define B43_MMIO_PIO_BASE2 0x320 +#define B43_MMIO_PIO_BASE3 0x330 +#define B43_MMIO_PIO_BASE4 0x340 +#define B43_MMIO_PIO_BASE5 0x350 +#define B43_MMIO_PIO_BASE6 0x360 +#define B43_MMIO_PIO_BASE7 0x370 +/* PIO on core rev >= 11 */ +#define B43_MMIO_PIO11_BASE0 0x200 +#define B43_MMIO_PIO11_BASE1 0x240 +#define B43_MMIO_PIO11_BASE2 0x280 +#define B43_MMIO_PIO11_BASE3 0x2C0 +#define B43_MMIO_PIO11_BASE4 0x300 +#define B43_MMIO_PIO11_BASE5 0x340 + +#define B43_MMIO_RADIO24_CONTROL 0x3D8 /* core rev >= 24 only */ +#define B43_MMIO_RADIO24_DATA 0x3DA /* core rev >= 24 only */ +#define B43_MMIO_PHY_VER 0x3E0 +#define B43_MMIO_PHY_RADIO 0x3E2 +#define B43_MMIO_PHY0 0x3E6 +#define B43_MMIO_ANTENNA 0x3E8 +#define B43_MMIO_CHANNEL 0x3F0 +#define B43_MMIO_CHANNEL_EXT 0x3F4 +#define B43_MMIO_RADIO_CONTROL 0x3F6 +#define B43_MMIO_RADIO_DATA_HIGH 0x3F8 +#define B43_MMIO_RADIO_DATA_LOW 0x3FA +#define B43_MMIO_PHY_CONTROL 0x3FC +#define B43_MMIO_PHY_DATA 0x3FE +#define B43_MMIO_MACFILTER_CONTROL 0x420 +#define B43_MMIO_MACFILTER_DATA 0x422 +#define B43_MMIO_RCMTA_COUNT 0x43C +#define B43_MMIO_PSM_PHY_HDR 0x492 +#define B43_MMIO_RADIO_HWENABLED_LO 0x49A +#define B43_MMIO_GPIO_CONTROL 0x49C +#define B43_MMIO_GPIO_MASK 0x49E +#define B43_MMIO_TXE0_CTL 0x500 +#define B43_MMIO_TXE0_AUX 0x502 +#define B43_MMIO_TXE0_TS_LOC 0x504 +#define B43_MMIO_TXE0_TIME_OUT 0x506 +#define B43_MMIO_TXE0_WM_0 0x508 +#define B43_MMIO_TXE0_WM_1 0x50A +#define B43_MMIO_TXE0_PHYCTL 0x50C +#define B43_MMIO_TXE0_STATUS 0x50E +#define B43_MMIO_TXE0_MMPLCP0 0x510 +#define B43_MMIO_TXE0_MMPLCP1 0x512 +#define B43_MMIO_TXE0_PHYCTL1 0x514 +#define B43_MMIO_XMTFIFODEF 0x520 +#define B43_MMIO_XMTFIFO_FRAME_CNT 0x522 /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFO_BYTE_CNT 0x524 /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFO_HEAD 0x526 /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFO_RD_PTR 0x528 /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFO_WR_PTR 0x52A /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFODEF1 0x52C /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFOCMD 0x540 +#define B43_MMIO_XMTFIFOFLUSH 0x542 +#define B43_MMIO_XMTFIFOTHRESH 0x544 +#define B43_MMIO_XMTFIFORDY 0x546 +#define B43_MMIO_XMTFIFOPRIRDY 0x548 +#define B43_MMIO_XMTFIFORQPRI 0x54A +#define B43_MMIO_XMTTPLATETXPTR 0x54C +#define B43_MMIO_XMTTPLATEPTR 0x550 +#define B43_MMIO_SMPL_CLCT_STRPTR 0x552 /* core rev>= 22 only */ +#define B43_MMIO_SMPL_CLCT_STPPTR 0x554 /* core rev>= 22 only */ +#define B43_MMIO_SMPL_CLCT_CURPTR 0x556 /* core rev>= 22 only */ +#define B43_MMIO_XMTTPLATEDATALO 0x560 +#define B43_MMIO_XMTTPLATEDATAHI 0x562 +#define B43_MMIO_XMTSEL 0x568 +#define B43_MMIO_XMTTXCNT 0x56A +#define B43_MMIO_XMTTXSHMADDR 0x56C +#define B43_MMIO_TSF_CFP_START_LOW 0x604 +#define B43_MMIO_TSF_CFP_START_HIGH 0x606 +#define B43_MMIO_TSF_CFP_PRETBTT 0x612 +#define B43_MMIO_TSF_CLK_FRAC_LOW 0x62E +#define B43_MMIO_TSF_CLK_FRAC_HIGH 0x630 +#define B43_MMIO_TSF_0 0x632 /* core rev < 3 only */ +#define B43_MMIO_TSF_1 0x634 /* core rev < 3 only */ +#define B43_MMIO_TSF_2 0x636 /* core rev < 3 only */ +#define B43_MMIO_TSF_3 0x638 /* core rev < 3 only */ +#define B43_MMIO_RNG 0x65A +#define B43_MMIO_IFSSLOT 0x684 /* Interframe slot time */ +#define B43_MMIO_IFSCTL 0x688 /* Interframe space control */ +#define B43_MMIO_IFSSTAT 0x690 +#define B43_MMIO_IFSMEDBUSYCTL 0x692 +#define B43_MMIO_IFTXDUR 0x694 +#define B43_MMIO_IFSCTL_USE_EDCF 0x0004 +#define B43_MMIO_POWERUP_DELAY 0x6A8 +#define B43_MMIO_BTCOEX_CTL 0x6B4 /* Bluetooth Coexistence Control */ +#define B43_MMIO_BTCOEX_STAT 0x6B6 /* Bluetooth Coexistence Status */ +#define B43_MMIO_BTCOEX_TXCTL 0x6B8 /* Bluetooth Coexistence Transmit Control */ +#define B43_MMIO_WEPCTL 0x7C0 + +/* SPROM boardflags_lo values */ +#define B43_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */ +#define B43_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */ +#define B43_BFL_AIRLINEMODE 0x0004 /* implements GPIO 13 radio disable indication */ +#define B43_BFL_RSSI 0x0008 /* software calculates nrssi slope. */ +#define B43_BFL_ENETSPI 0x0010 /* has ephy roboswitch spi */ +#define B43_BFL_XTAL_NOSLOW 0x0020 /* no slow clock available */ +#define B43_BFL_CCKHIPWR 0x0040 /* can do high power CCK transmission */ +#define B43_BFL_ENETADM 0x0080 /* has ADMtek switch */ +#define B43_BFL_ENETVLAN 0x0100 /* can do vlan */ +#define B43_BFL_AFTERBURNER 0x0200 /* supports Afterburner mode */ +#define B43_BFL_NOPCI 0x0400 /* leaves PCI floating */ +#define B43_BFL_FEM 0x0800 /* supports the Front End Module */ +#define B43_BFL_EXTLNA 0x1000 /* has an external LNA */ +#define B43_BFL_HGPA 0x2000 /* had high gain PA */ +#define B43_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */ +#define B43_BFL_ALTIQ 0x8000 /* alternate I/Q settings */ + +/* SPROM boardflags_hi values */ +#define B43_BFH_NOPA 0x0001 /* has no PA */ +#define B43_BFH_RSSIINV 0x0002 /* RSSI uses positive slope (not TSSI) */ +#define B43_BFH_PAREF 0x0004 /* uses the PARef LDO */ +#define B43_BFH_3TSWITCH 0x0008 /* uses a triple throw switch shared + * with bluetooth */ +#define B43_BFH_PHASESHIFT 0x0010 /* can support phase shifter */ +#define B43_BFH_BUCKBOOST 0x0020 /* has buck/booster */ +#define B43_BFH_FEM_BT 0x0040 /* has FEM and switch to share antenna + * with bluetooth */ +#define B43_BFH_NOCBUCK 0x0080 +#define B43_BFH_PALDO 0x0200 +#define B43_BFH_EXTLNA_5GHZ 0x1000 /* has an external LNA (5GHz mode) */ + +/* SPROM boardflags2_lo values */ +#define B43_BFL2_RXBB_INT_REG_DIS 0x0001 /* external RX BB regulator present */ +#define B43_BFL2_APLL_WAR 0x0002 /* alternative A-band PLL settings implemented */ +#define B43_BFL2_TXPWRCTRL_EN 0x0004 /* permits enabling TX Power Control */ +#define B43_BFL2_2X4_DIV 0x0008 /* 2x4 diversity switch */ +#define B43_BFL2_5G_PWRGAIN 0x0010 /* supports 5G band power gain */ +#define B43_BFL2_PCIEWAR_OVR 0x0020 /* overrides ASPM and Clkreq settings */ +#define B43_BFL2_CAESERS_BRD 0x0040 /* is Caesers board (unused) */ +#define B43_BFL2_BTC3WIRE 0x0080 /* used 3-wire bluetooth coexist */ +#define B43_BFL2_SKWRKFEM_BRD 0x0100 /* 4321mcm93 uses Skyworks FEM */ +#define B43_BFL2_SPUR_WAR 0x0200 /* has a workaround for clock-harmonic spurs */ +#define B43_BFL2_GPLL_WAR 0x0400 /* altenative G-band PLL settings implemented */ +#define B43_BFL2_SINGLEANT_CCK 0x1000 +#define B43_BFL2_2G_SPUR_WAR 0x2000 + +/* SPROM boardflags2_hi values */ +#define B43_BFH2_GPLL_WAR2 0x0001 +#define B43_BFH2_IPALVLSHIFT_3P3 0x0002 +#define B43_BFH2_INTERNDET_TXIQCAL 0x0004 +#define B43_BFH2_XTALBUFOUTEN 0x0008 + +/* GPIO register offset, in both ChipCommon and PCI core. */ +#define B43_GPIO_CONTROL 0x6c + +/* SHM Routing */ +enum { + B43_SHM_UCODE, /* Microcode memory */ + B43_SHM_SHARED, /* Shared memory */ + B43_SHM_SCRATCH, /* Scratch memory */ + B43_SHM_HW, /* Internal hardware register */ + B43_SHM_RCMTA, /* Receive match transmitter address (rev >= 5 only) */ +}; +/* SHM Routing modifiers */ +#define B43_SHM_AUTOINC_R 0x0200 /* Auto-increment address on read */ +#define B43_SHM_AUTOINC_W 0x0100 /* Auto-increment address on write */ +#define B43_SHM_AUTOINC_RW (B43_SHM_AUTOINC_R | \ + B43_SHM_AUTOINC_W) + +/* Misc SHM_SHARED offsets */ +#define B43_SHM_SH_WLCOREREV 0x0016 /* 802.11 core revision */ +#define B43_SHM_SH_PCTLWDPOS 0x0008 +#define B43_SHM_SH_RXPADOFF 0x0034 /* RX Padding data offset (PIO only) */ +#define B43_SHM_SH_FWCAPA 0x0042 /* Firmware capabilities (Opensource firmware only) */ +#define B43_SHM_SH_PHYVER 0x0050 /* PHY version */ +#define B43_SHM_SH_PHYTYPE 0x0052 /* PHY type */ +#define B43_SHM_SH_ANTSWAP 0x005C /* Antenna swap threshold */ +#define B43_SHM_SH_HOSTF1 0x005E /* Hostflags 1 for ucode options */ +#define B43_SHM_SH_HOSTF2 0x0060 /* Hostflags 2 for ucode options */ +#define B43_SHM_SH_HOSTF3 0x0062 /* Hostflags 3 for ucode options */ +#define B43_SHM_SH_RFATT 0x0064 /* Current radio attenuation value */ +#define B43_SHM_SH_RADAR 0x0066 /* Radar register */ +#define B43_SHM_SH_PHYTXNOI 0x006E /* PHY noise directly after TX (lower 8bit only) */ +#define B43_SHM_SH_RFRXSP1 0x0072 /* RF RX SP Register 1 */ +#define B43_SHM_SH_HOSTF4 0x0078 /* Hostflags 4 for ucode options */ +#define B43_SHM_SH_CHAN 0x00A0 /* Current channel (low 8bit only) */ +#define B43_SHM_SH_CHAN_5GHZ 0x0100 /* Bit set, if 5 Ghz channel */ +#define B43_SHM_SH_CHAN_40MHZ 0x0200 /* Bit set, if 40 Mhz channel width */ +#define B43_SHM_SH_MACHW_L 0x00C0 /* Location where the ucode expects the MAC capabilities */ +#define B43_SHM_SH_MACHW_H 0x00C2 /* Location where the ucode expects the MAC capabilities */ +#define B43_SHM_SH_HOSTF5 0x00D4 /* Hostflags 5 for ucode options */ +#define B43_SHM_SH_BCMCFIFOID 0x0108 /* Last posted cookie to the bcast/mcast FIFO */ +/* TSSI information */ +#define B43_SHM_SH_TSSI_CCK 0x0058 /* TSSI for last 4 CCK frames (32bit) */ +#define B43_SHM_SH_TSSI_OFDM_A 0x0068 /* TSSI for last 4 OFDM frames (32bit) */ +#define B43_SHM_SH_TSSI_OFDM_G 0x0070 /* TSSI for last 4 OFDM frames (32bit) */ +#define B43_TSSI_MAX 0x7F /* Max value for one TSSI value */ +/* SHM_SHARED TX FIFO variables */ +#define B43_SHM_SH_SIZE01 0x0098 /* TX FIFO size for FIFO 0 (low) and 1 (high) */ +#define B43_SHM_SH_SIZE23 0x009A /* TX FIFO size for FIFO 2 and 3 */ +#define B43_SHM_SH_SIZE45 0x009C /* TX FIFO size for FIFO 4 and 5 */ +#define B43_SHM_SH_SIZE67 0x009E /* TX FIFO size for FIFO 6 and 7 */ +/* SHM_SHARED background noise */ +#define B43_SHM_SH_JSSI0 0x0088 /* Measure JSSI 0 */ +#define B43_SHM_SH_JSSI1 0x008A /* Measure JSSI 1 */ +#define B43_SHM_SH_JSSIAUX 0x008C /* Measure JSSI AUX */ +/* SHM_SHARED crypto engine */ +#define B43_SHM_SH_DEFAULTIV 0x003C /* Default IV location */ +#define B43_SHM_SH_NRRXTRANS 0x003E /* # of soft RX transmitter addresses (max 8) */ +#define B43_SHM_SH_KTP 0x0056 /* Key table pointer */ +#define B43_SHM_SH_TKIPTSCTTAK 0x0318 +#define B43_SHM_SH_KEYIDXBLOCK 0x05D4 /* Key index/algorithm block (v4 firmware) */ +#define B43_SHM_SH_PSM 0x05F4 /* PSM transmitter address match block (rev < 5) */ +/* SHM_SHARED WME variables */ +#define B43_SHM_SH_EDCFSTAT 0x000E /* EDCF status */ +#define B43_SHM_SH_TXFCUR 0x0030 /* TXF current index */ +#define B43_SHM_SH_EDCFQ 0x0240 /* EDCF Q info */ +/* SHM_SHARED powersave mode related */ +#define B43_SHM_SH_SLOTT 0x0010 /* Slot time */ +#define B43_SHM_SH_DTIMPER 0x0012 /* DTIM period */ +#define B43_SHM_SH_NOSLPZNATDTIM 0x004C /* NOSLPZNAT DTIM */ +/* SHM_SHARED beacon/AP variables */ +#define B43_SHM_SH_BT_BASE0 0x0068 /* Beacon template base 0 */ +#define B43_SHM_SH_BTL0 0x0018 /* Beacon template length 0 */ +#define B43_SHM_SH_BT_BASE1 0x0468 /* Beacon template base 1 */ +#define B43_SHM_SH_BTL1 0x001A /* Beacon template length 1 */ +#define B43_SHM_SH_BTSFOFF 0x001C /* Beacon TSF offset */ +#define B43_SHM_SH_TIMBPOS 0x001E /* TIM B position in beacon */ +#define B43_SHM_SH_DTIMP 0x0012 /* DTIP period */ +#define B43_SHM_SH_MCASTCOOKIE 0x00A8 /* Last bcast/mcast frame ID */ +#define B43_SHM_SH_SFFBLIM 0x0044 /* Short frame fallback retry limit */ +#define B43_SHM_SH_LFFBLIM 0x0046 /* Long frame fallback retry limit */ +#define B43_SHM_SH_BEACPHYCTL 0x0054 /* Beacon PHY TX control word (see PHY TX control) */ +#define B43_SHM_SH_EXTNPHYCTL 0x00B0 /* Extended bytes for beacon PHY control (N) */ +#define B43_SHM_SH_BCN_LI 0x00B6 /* beacon listen interval */ +/* SHM_SHARED ACK/CTS control */ +#define B43_SHM_SH_ACKCTSPHYCTL 0x0022 /* ACK/CTS PHY control word (see PHY TX control) */ +/* SHM_SHARED probe response variables */ +#define B43_SHM_SH_PRSSID 0x0160 /* Probe Response SSID */ +#define B43_SHM_SH_PRSSIDLEN 0x0048 /* Probe Response SSID length */ +#define B43_SHM_SH_PRTLEN 0x004A /* Probe Response template length */ +#define B43_SHM_SH_PRMAXTIME 0x0074 /* Probe Response max time */ +#define B43_SHM_SH_PRPHYCTL 0x0188 /* Probe Response PHY TX control word */ +/* SHM_SHARED rate tables */ +#define B43_SHM_SH_OFDMDIRECT 0x01C0 /* Pointer to OFDM direct map */ +#define B43_SHM_SH_OFDMBASIC 0x01E0 /* Pointer to OFDM basic rate map */ +#define B43_SHM_SH_CCKDIRECT 0x0200 /* Pointer to CCK direct map */ +#define B43_SHM_SH_CCKBASIC 0x0220 /* Pointer to CCK basic rate map */ +/* SHM_SHARED microcode soft registers */ +#define B43_SHM_SH_UCODEREV 0x0000 /* Microcode revision */ +#define B43_SHM_SH_UCODEPATCH 0x0002 /* Microcode patchlevel */ +#define B43_SHM_SH_UCODEDATE 0x0004 /* Microcode date */ +#define B43_SHM_SH_UCODETIME 0x0006 /* Microcode time */ +#define B43_SHM_SH_UCODESTAT 0x0040 /* Microcode debug status code */ +#define B43_SHM_SH_UCODESTAT_INVALID 0 +#define B43_SHM_SH_UCODESTAT_INIT 1 +#define B43_SHM_SH_UCODESTAT_ACTIVE 2 +#define B43_SHM_SH_UCODESTAT_SUSP 3 /* suspended */ +#define B43_SHM_SH_UCODESTAT_SLEEP 4 /* asleep (PS) */ +#define B43_SHM_SH_MAXBFRAMES 0x0080 /* Maximum number of frames in a burst */ +#define B43_SHM_SH_SPUWKUP 0x0094 /* pre-wakeup for synth PU in us */ +#define B43_SHM_SH_PRETBTT 0x0096 /* pre-TBTT in us */ +/* SHM_SHARED tx iq workarounds */ +#define B43_SHM_SH_NPHY_TXIQW0 0x0700 +#define B43_SHM_SH_NPHY_TXIQW1 0x0702 +#define B43_SHM_SH_NPHY_TXIQW2 0x0704 +#define B43_SHM_SH_NPHY_TXIQW3 0x0706 +/* SHM_SHARED tx pwr ctrl */ +#define B43_SHM_SH_NPHY_TXPWR_INDX0 0x0708 +#define B43_SHM_SH_NPHY_TXPWR_INDX1 0x070E + +/* SHM_SCRATCH offsets */ +#define B43_SHM_SC_MINCONT 0x0003 /* Minimum contention window */ +#define B43_SHM_SC_MAXCONT 0x0004 /* Maximum contention window */ +#define B43_SHM_SC_CURCONT 0x0005 /* Current contention window */ +#define B43_SHM_SC_SRLIMIT 0x0006 /* Short retry count limit */ +#define B43_SHM_SC_LRLIMIT 0x0007 /* Long retry count limit */ +#define B43_SHM_SC_DTIMC 0x0008 /* Current DTIM count */ +#define B43_SHM_SC_BTL0LEN 0x0015 /* Beacon 0 template length */ +#define B43_SHM_SC_BTL1LEN 0x0016 /* Beacon 1 template length */ +#define B43_SHM_SC_SCFB 0x0017 /* Short frame transmit count threshold for rate fallback */ +#define B43_SHM_SC_LCFB 0x0018 /* Long frame transmit count threshold for rate fallback */ + +/* Hardware Radio Enable masks */ +#define B43_MMIO_RADIO_HWENABLED_HI_MASK (1 << 16) +#define B43_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4) + +/* HostFlags. See b43_hf_read/write() */ +#define B43_HF_ANTDIVHELP 0x000000000001ULL /* ucode antenna div helper */ +#define B43_HF_SYMW 0x000000000002ULL /* G-PHY SYM workaround */ +#define B43_HF_RXPULLW 0x000000000004ULL /* RX pullup workaround */ +#define B43_HF_CCKBOOST 0x000000000008ULL /* 4dB CCK power boost (exclusive with OFDM boost) */ +#define B43_HF_BTCOEX 0x000000000010ULL /* Bluetooth coexistance */ +#define B43_HF_GDCW 0x000000000020ULL /* G-PHY DC canceller filter bw workaround */ +#define B43_HF_OFDMPABOOST 0x000000000040ULL /* Enable PA gain boost for OFDM */ +#define B43_HF_ACPR 0x000000000080ULL /* Disable for Japan, channel 14 */ +#define B43_HF_EDCF 0x000000000100ULL /* on if WME and MAC suspended */ +#define B43_HF_TSSIRPSMW 0x000000000200ULL /* TSSI reset PSM ucode workaround */ +#define B43_HF_20IN40IQW 0x000000000200ULL /* 20 in 40 MHz I/Q workaround (rev >= 13 only) */ +#define B43_HF_DSCRQ 0x000000000400ULL /* Disable slow clock request in ucode */ +#define B43_HF_ACIW 0x000000000800ULL /* ACI workaround: shift bits by 2 on PHY CRS */ +#define B43_HF_2060W 0x000000001000ULL /* 2060 radio workaround */ +#define B43_HF_RADARW 0x000000002000ULL /* Radar workaround */ +#define B43_HF_USEDEFKEYS 0x000000004000ULL /* Enable use of default keys */ +#define B43_HF_AFTERBURNER 0x000000008000ULL /* Afterburner enabled */ +#define B43_HF_BT4PRIOCOEX 0x000000010000ULL /* Bluetooth 4-priority coexistance */ +#define B43_HF_FWKUP 0x000000020000ULL /* Fast wake-up ucode */ +#define B43_HF_VCORECALC 0x000000040000ULL /* Force VCO recalculation when powering up synthpu */ +#define B43_HF_PCISCW 0x000000080000ULL /* PCI slow clock workaround */ +#define B43_HF_4318TSSI 0x000000200000ULL /* 4318 TSSI */ +#define B43_HF_FBCMCFIFO 0x000000400000ULL /* Flush bcast/mcast FIFO immediately */ +#define B43_HF_HWPCTL 0x000000800000ULL /* Enable hardwarre power control */ +#define B43_HF_BTCOEXALT 0x000001000000ULL /* Bluetooth coexistance in alternate pins */ +#define B43_HF_TXBTCHECK 0x000002000000ULL /* Bluetooth check during transmission */ +#define B43_HF_SKCFPUP 0x000004000000ULL /* Skip CFP update */ +#define B43_HF_N40W 0x000008000000ULL /* N PHY 40 MHz workaround (rev >= 13 only) */ +#define B43_HF_ANTSEL 0x000020000000ULL /* Antenna selection (for testing antenna div.) */ +#define B43_HF_BT3COEXT 0x000020000000ULL /* Bluetooth 3-wire coexistence (rev >= 13 only) */ +#define B43_HF_BTCANT 0x000040000000ULL /* Bluetooth coexistence (antenna mode) (rev >= 13 only) */ +#define B43_HF_ANTSELEN 0x000100000000ULL /* Antenna selection enabled (rev >= 13 only) */ +#define B43_HF_ANTSELMODE 0x000200000000ULL /* Antenna selection mode (rev >= 13 only) */ +#define B43_HF_MLADVW 0x001000000000ULL /* N PHY ML ADV workaround (rev >= 13 only) */ +#define B43_HF_PR45960W 0x080000000000ULL /* PR 45960 workaround (rev >= 13 only) */ + +/* Firmware capabilities field in SHM (Opensource firmware only) */ +#define B43_FWCAPA_HWCRYPTO 0x0001 +#define B43_FWCAPA_QOS 0x0002 + +/* MacFilter offsets. */ +#define B43_MACFILTER_SELF 0x0000 +#define B43_MACFILTER_BSSID 0x0003 + +/* PowerControl */ +#define B43_PCTL_IN 0xB0 +#define B43_PCTL_OUT 0xB4 +#define B43_PCTL_OUTENABLE 0xB8 +#define B43_PCTL_XTAL_POWERUP 0x40 +#define B43_PCTL_PLL_POWERDOWN 0x80 + +/* PowerControl Clock Modes */ +#define B43_PCTL_CLK_FAST 0x00 +#define B43_PCTL_CLK_SLOW 0x01 +#define B43_PCTL_CLK_DYNAMIC 0x02 + +#define B43_PCTL_FORCE_SLOW 0x0800 +#define B43_PCTL_FORCE_PLL 0x1000 +#define B43_PCTL_DYN_XTAL 0x2000 + +/* PHYVersioning */ +#define B43_PHYTYPE_A 0x00 +#define B43_PHYTYPE_B 0x01 +#define B43_PHYTYPE_G 0x02 +#define B43_PHYTYPE_N 0x04 +#define B43_PHYTYPE_LP 0x05 +#define B43_PHYTYPE_SSLPN 0x06 +#define B43_PHYTYPE_HT 0x07 +#define B43_PHYTYPE_LCN 0x08 +#define B43_PHYTYPE_LCNXN 0x09 +#define B43_PHYTYPE_LCN40 0x0a +#define B43_PHYTYPE_AC 0x0b + +/* PHYRegisters */ +#define B43_PHY_ILT_A_CTRL 0x0072 +#define B43_PHY_ILT_A_DATA1 0x0073 +#define B43_PHY_ILT_A_DATA2 0x0074 +#define B43_PHY_G_LO_CONTROL 0x0810 +#define B43_PHY_ILT_G_CTRL 0x0472 +#define B43_PHY_ILT_G_DATA1 0x0473 +#define B43_PHY_ILT_G_DATA2 0x0474 +#define B43_PHY_A_PCTL 0x007B +#define B43_PHY_G_PCTL 0x0029 +#define B43_PHY_A_CRS 0x0029 +#define B43_PHY_RADIO_BITFIELD 0x0401 +#define B43_PHY_G_CRS 0x0429 +#define B43_PHY_NRSSILT_CTRL 0x0803 +#define B43_PHY_NRSSILT_DATA 0x0804 + +/* RadioRegisters */ +#define B43_RADIOCTL_ID 0x01 + +/* MAC Control bitfield */ +#define B43_MACCTL_ENABLED 0x00000001 /* MAC Enabled */ +#define B43_MACCTL_PSM_RUN 0x00000002 /* Run Microcode */ +#define B43_MACCTL_PSM_JMP0 0x00000004 /* Microcode jump to 0 */ +#define B43_MACCTL_SHM_ENABLED 0x00000100 /* SHM Enabled */ +#define B43_MACCTL_SHM_UPPER 0x00000200 /* SHM Upper */ +#define B43_MACCTL_IHR_ENABLED 0x00000400 /* IHR Region Enabled */ +#define B43_MACCTL_PSM_DBG 0x00002000 /* Microcode debugging enabled */ +#define B43_MACCTL_GPOUTSMSK 0x0000C000 /* GPOUT Select Mask */ +#define B43_MACCTL_BE 0x00010000 /* Big Endian mode */ +#define B43_MACCTL_INFRA 0x00020000 /* Infrastructure mode */ +#define B43_MACCTL_AP 0x00040000 /* AccessPoint mode */ +#define B43_MACCTL_RADIOLOCK 0x00080000 /* Radio lock */ +#define B43_MACCTL_BEACPROMISC 0x00100000 /* Beacon Promiscuous */ +#define B43_MACCTL_KEEP_BADPLCP 0x00200000 /* Keep frames with bad PLCP */ +#define B43_MACCTL_PHY_LOCK 0x00200000 +#define B43_MACCTL_KEEP_CTL 0x00400000 /* Keep control frames */ +#define B43_MACCTL_KEEP_BAD 0x00800000 /* Keep bad frames (FCS) */ +#define B43_MACCTL_PROMISC 0x01000000 /* Promiscuous mode */ +#define B43_MACCTL_HWPS 0x02000000 /* Hardware Power Saving */ +#define B43_MACCTL_AWAKE 0x04000000 /* Device is awake */ +#define B43_MACCTL_CLOSEDNET 0x08000000 /* Closed net (no SSID bcast) */ +#define B43_MACCTL_TBTTHOLD 0x10000000 /* TBTT Hold */ +#define B43_MACCTL_DISCTXSTAT 0x20000000 /* Discard TX status */ +#define B43_MACCTL_DISCPMQ 0x40000000 /* Discard Power Management Queue */ +#define B43_MACCTL_GMODE 0x80000000 /* G Mode */ + +/* MAC Command bitfield */ +#define B43_MACCMD_BEACON0_VALID 0x00000001 /* Beacon 0 in template RAM is busy/valid */ +#define B43_MACCMD_BEACON1_VALID 0x00000002 /* Beacon 1 in template RAM is busy/valid */ +#define B43_MACCMD_DFQ_VALID 0x00000004 /* Directed frame queue valid (IBSS PS mode, ATIM) */ +#define B43_MACCMD_CCA 0x00000008 /* Clear channel assessment */ +#define B43_MACCMD_BGNOISE 0x00000010 /* Background noise */ + +/* B43_MMIO_PSM_PHY_HDR bits */ +#define B43_PSM_HDR_MAC_PHY_RESET 0x00000001 +#define B43_PSM_HDR_MAC_PHY_CLOCK_EN 0x00000002 +#define B43_PSM_HDR_MAC_PHY_FORCE_CLK 0x00000004 + +/* See BCMA_CLKCTLST_EXTRESREQ and BCMA_CLKCTLST_EXTRESST */ +#define B43_BCMA_CLKCTLST_80211_PLL_REQ 0x00000100 +#define B43_BCMA_CLKCTLST_PHY_PLL_REQ 0x00000200 +#define B43_BCMA_CLKCTLST_80211_PLL_ST 0x01000000 +#define B43_BCMA_CLKCTLST_PHY_PLL_ST 0x02000000 + +/* BCMA 802.11 core specific IO Control (BCMA_IOCTL) flags */ +#define B43_BCMA_IOCTL_PHY_CLKEN 0x00000004 /* PHY Clock Enable */ +#define B43_BCMA_IOCTL_PHY_RESET 0x00000008 /* PHY Reset */ +#define B43_BCMA_IOCTL_MACPHYCLKEN 0x00000010 /* MAC PHY Clock Control Enable */ +#define B43_BCMA_IOCTL_PLLREFSEL 0x00000020 /* PLL Frequency Reference Select */ +#define B43_BCMA_IOCTL_PHY_BW 0x000000C0 /* PHY band width and clock speed mask (N-PHY+ only?) */ +#define B43_BCMA_IOCTL_PHY_BW_10MHZ 0x00000000 /* 10 MHz bandwidth, 40 MHz PHY */ +#define B43_BCMA_IOCTL_PHY_BW_20MHZ 0x00000040 /* 20 MHz bandwidth, 80 MHz PHY */ +#define B43_BCMA_IOCTL_PHY_BW_40MHZ 0x00000080 /* 40 MHz bandwidth, 160 MHz PHY */ +#define B43_BCMA_IOCTL_PHY_BW_80MHZ 0x000000C0 /* 80 MHz bandwidth */ +#define B43_BCMA_IOCTL_DAC 0x00000300 /* Highspeed DAC mode control field */ +#define B43_BCMA_IOCTL_GMODE 0x00002000 /* G Mode Enable */ + +/* BCMA 802.11 core specific IO status (BCMA_IOST) flags */ +#define B43_BCMA_IOST_2G_PHY 0x00000001 /* 2.4G capable phy */ +#define B43_BCMA_IOST_5G_PHY 0x00000002 /* 5G capable phy */ +#define B43_BCMA_IOST_FASTCLKA 0x00000004 /* Fast Clock Available */ +#define B43_BCMA_IOST_DUALB_PHY 0x00000008 /* Dualband phy */ + +/* 802.11 core specific TM State Low (SSB_TMSLOW) flags */ +#define B43_TMSLOW_GMODE 0x20000000 /* G Mode Enable */ +#define B43_TMSLOW_PHY_BANDWIDTH 0x00C00000 /* PHY band width and clock speed mask (N-PHY only) */ +#define B43_TMSLOW_PHY_BANDWIDTH_10MHZ 0x00000000 /* 10 MHz bandwidth, 40 MHz PHY */ +#define B43_TMSLOW_PHY_BANDWIDTH_20MHZ 0x00400000 /* 20 MHz bandwidth, 80 MHz PHY */ +#define B43_TMSLOW_PHY_BANDWIDTH_40MHZ 0x00800000 /* 40 MHz bandwidth, 160 MHz PHY */ +#define B43_TMSLOW_PLLREFSEL 0x00200000 /* PLL Frequency Reference Select (rev >= 5) */ +#define B43_TMSLOW_MACPHYCLKEN 0x00100000 /* MAC PHY Clock Control Enable (rev >= 5) */ +#define B43_TMSLOW_PHYRESET 0x00080000 /* PHY Reset */ +#define B43_TMSLOW_PHYCLKEN 0x00040000 /* PHY Clock Enable */ + +/* 802.11 core specific TM State High (SSB_TMSHIGH) flags */ +#define B43_TMSHIGH_DUALBAND_PHY 0x00080000 /* Dualband PHY available */ +#define B43_TMSHIGH_FCLOCK 0x00040000 /* Fast Clock Available (rev >= 5) */ +#define B43_TMSHIGH_HAVE_5GHZ_PHY 0x00020000 /* 5 GHz PHY available (rev >= 5) */ +#define B43_TMSHIGH_HAVE_2GHZ_PHY 0x00010000 /* 2.4 GHz PHY available (rev >= 5) */ + +/* Generic-Interrupt reasons. */ +#define B43_IRQ_MAC_SUSPENDED 0x00000001 +#define B43_IRQ_BEACON 0x00000002 +#define B43_IRQ_TBTT_INDI 0x00000004 +#define B43_IRQ_BEACON_TX_OK 0x00000008 +#define B43_IRQ_BEACON_CANCEL 0x00000010 +#define B43_IRQ_ATIM_END 0x00000020 +#define B43_IRQ_PMQ 0x00000040 +#define B43_IRQ_PIO_WORKAROUND 0x00000100 +#define B43_IRQ_MAC_TXERR 0x00000200 +#define B43_IRQ_PHY_TXERR 0x00000800 +#define B43_IRQ_PMEVENT 0x00001000 +#define B43_IRQ_TIMER0 0x00002000 +#define B43_IRQ_TIMER1 0x00004000 +#define B43_IRQ_DMA 0x00008000 +#define B43_IRQ_TXFIFO_FLUSH_OK 0x00010000 +#define B43_IRQ_CCA_MEASURE_OK 0x00020000 +#define B43_IRQ_NOISESAMPLE_OK 0x00040000 +#define B43_IRQ_UCODE_DEBUG 0x08000000 +#define B43_IRQ_RFKILL 0x10000000 +#define B43_IRQ_TX_OK 0x20000000 +#define B43_IRQ_PHY_G_CHANGED 0x40000000 +#define B43_IRQ_TIMEOUT 0x80000000 + +#define B43_IRQ_ALL 0xFFFFFFFF +#define B43_IRQ_MASKTEMPLATE (B43_IRQ_TBTT_INDI | \ + B43_IRQ_ATIM_END | \ + B43_IRQ_PMQ | \ + B43_IRQ_MAC_TXERR | \ + B43_IRQ_PHY_TXERR | \ + B43_IRQ_DMA | \ + B43_IRQ_TXFIFO_FLUSH_OK | \ + B43_IRQ_NOISESAMPLE_OK | \ + B43_IRQ_UCODE_DEBUG | \ + B43_IRQ_RFKILL | \ + B43_IRQ_TX_OK) + +/* The firmware register to fetch the debug-IRQ reason from. */ +#define B43_DEBUGIRQ_REASON_REG 63 +/* Debug-IRQ reasons. */ +#define B43_DEBUGIRQ_PANIC 0 /* The firmware panic'ed */ +#define B43_DEBUGIRQ_DUMP_SHM 1 /* Dump shared SHM */ +#define B43_DEBUGIRQ_DUMP_REGS 2 /* Dump the microcode registers */ +#define B43_DEBUGIRQ_MARKER 3 /* A "marker" was thrown by the firmware. */ +#define B43_DEBUGIRQ_ACK 0xFFFF /* The host writes that to ACK the IRQ */ + +/* The firmware register that contains the "marker" line. */ +#define B43_MARKER_ID_REG 2 +#define B43_MARKER_LINE_REG 3 + +/* The firmware register to fetch the panic reason from. */ +#define B43_FWPANIC_REASON_REG 3 +/* Firmware panic reason codes */ +#define B43_FWPANIC_DIE 0 /* Firmware died. Don't auto-restart it. */ +#define B43_FWPANIC_RESTART 1 /* Firmware died. Schedule a controller reset. */ + +/* The firmware register that contains the watchdog counter. */ +#define B43_WATCHDOG_REG 1 + +/* Device specific rate values. + * The actual values defined here are (rate_in_mbps * 2). + * Some code depends on this. Don't change it. */ +#define B43_CCK_RATE_1MB 0x02 +#define B43_CCK_RATE_2MB 0x04 +#define B43_CCK_RATE_5MB 0x0B +#define B43_CCK_RATE_11MB 0x16 +#define B43_OFDM_RATE_6MB 0x0C +#define B43_OFDM_RATE_9MB 0x12 +#define B43_OFDM_RATE_12MB 0x18 +#define B43_OFDM_RATE_18MB 0x24 +#define B43_OFDM_RATE_24MB 0x30 +#define B43_OFDM_RATE_36MB 0x48 +#define B43_OFDM_RATE_48MB 0x60 +#define B43_OFDM_RATE_54MB 0x6C +/* Convert a b43 rate value to a rate in 100kbps */ +#define B43_RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) + +#define B43_DEFAULT_SHORT_RETRY_LIMIT 7 +#define B43_DEFAULT_LONG_RETRY_LIMIT 4 + +#define B43_PHY_TX_BADNESS_LIMIT 1000 + +/* Max size of a security key */ +#define B43_SEC_KEYSIZE 16 +/* Max number of group keys */ +#define B43_NR_GROUP_KEYS 4 +/* Max number of pairwise keys */ +#define B43_NR_PAIRWISE_KEYS 50 +/* Security algorithms. */ +enum { + B43_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */ + B43_SEC_ALGO_WEP40, + B43_SEC_ALGO_TKIP, + B43_SEC_ALGO_AES, + B43_SEC_ALGO_WEP104, + B43_SEC_ALGO_AES_LEGACY, +}; + +struct b43_dmaring; + +/* The firmware file header */ +#define B43_FW_TYPE_UCODE 'u' +#define B43_FW_TYPE_PCM 'p' +#define B43_FW_TYPE_IV 'i' +struct b43_fw_header { + /* File type */ + u8 type; + /* File format version */ + u8 ver; + u8 __padding[2]; + /* Size of the data. For ucode and PCM this is in bytes. + * For IV this is number-of-ivs. */ + __be32 size; +} __packed; + +/* Initial Value file format */ +#define B43_IV_OFFSET_MASK 0x7FFF +#define B43_IV_32BIT 0x8000 +struct b43_iv { + __be16 offset_size; + union { + __be16 d16; + __be32 d32; + } data __packed; +} __packed; + + +/* Data structures for DMA transmission, per 80211 core. */ +struct b43_dma { + struct b43_dmaring *tx_ring_AC_BK; /* Background */ + struct b43_dmaring *tx_ring_AC_BE; /* Best Effort */ + struct b43_dmaring *tx_ring_AC_VI; /* Video */ + struct b43_dmaring *tx_ring_AC_VO; /* Voice */ + struct b43_dmaring *tx_ring_mcast; /* Multicast */ + + struct b43_dmaring *rx_ring; + + u32 translation; /* Routing bits */ + bool translation_in_low; /* Should translation bit go into low addr? */ + bool parity; /* Check for parity */ +}; + +struct b43_pio_txqueue; +struct b43_pio_rxqueue; + +/* Data structures for PIO transmission, per 80211 core. */ +struct b43_pio { + struct b43_pio_txqueue *tx_queue_AC_BK; /* Background */ + struct b43_pio_txqueue *tx_queue_AC_BE; /* Best Effort */ + struct b43_pio_txqueue *tx_queue_AC_VI; /* Video */ + struct b43_pio_txqueue *tx_queue_AC_VO; /* Voice */ + struct b43_pio_txqueue *tx_queue_mcast; /* Multicast */ + + struct b43_pio_rxqueue *rx_queue; +}; + +/* Context information for a noise calculation (Link Quality). */ +struct b43_noise_calculation { + bool calculation_running; + u8 nr_samples; + s8 samples[8][4]; +}; + +struct b43_stats { + u8 link_noise; +}; + +struct b43_key { + /* If keyconf is NULL, this key is disabled. + * keyconf is a cookie. Don't derefenrence it outside of the set_key + * path, because b43 doesn't own it. */ + struct ieee80211_key_conf *keyconf; + u8 algorithm; +}; + +/* SHM offsets to the QOS data structures for the 4 different queues. */ +#define B43_QOS_QUEUE_NUM 4 +#define B43_QOS_PARAMS(queue) (B43_SHM_SH_EDCFQ + \ + (B43_NR_QOSPARAMS * sizeof(u16) * (queue))) +#define B43_QOS_BACKGROUND B43_QOS_PARAMS(0) +#define B43_QOS_BESTEFFORT B43_QOS_PARAMS(1) +#define B43_QOS_VIDEO B43_QOS_PARAMS(2) +#define B43_QOS_VOICE B43_QOS_PARAMS(3) + +/* QOS parameter hardware data structure offsets. */ +#define B43_NR_QOSPARAMS 16 +enum { + B43_QOSPARAM_TXOP = 0, + B43_QOSPARAM_CWMIN, + B43_QOSPARAM_CWMAX, + B43_QOSPARAM_CWCUR, + B43_QOSPARAM_AIFS, + B43_QOSPARAM_BSLOTS, + B43_QOSPARAM_REGGAP, + B43_QOSPARAM_STATUS, +}; + +/* QOS parameters for a queue. */ +struct b43_qos_params { + /* The QOS parameters */ + struct ieee80211_tx_queue_params p; +}; + +struct b43_wl; + +/* The type of the firmware file. */ +enum b43_firmware_file_type { + B43_FWTYPE_PROPRIETARY, + B43_FWTYPE_OPENSOURCE, + B43_NR_FWTYPES, +}; + +/* Context data for fetching firmware. */ +struct b43_request_fw_context { + /* The device we are requesting the fw for. */ + struct b43_wldev *dev; + /* a pointer to the firmware object */ + const struct firmware *blob; + /* The type of firmware to request. */ + enum b43_firmware_file_type req_type; + /* Error messages for each firmware type. */ + char errors[B43_NR_FWTYPES][128]; + /* Temporary buffer for storing the firmware name. */ + char fwname[64]; + /* A fatal error occurred while requesting. Firmware request + * can not continue, as any other request will also fail. */ + int fatal_failure; +}; + +/* In-memory representation of a cached microcode file. */ +struct b43_firmware_file { + const char *filename; + const struct firmware *data; + /* Type of the firmware file name. Note that this does only indicate + * the type by the firmware name. NOT the file contents. + * If you want to check for proprietary vs opensource, use (struct b43_firmware)->opensource + * instead! The (struct b43_firmware)->opensource flag is derived from the actual firmware + * binary code, not just the filename. + */ + enum b43_firmware_file_type type; +}; + +enum b43_firmware_hdr_format { + B43_FW_HDR_598, + B43_FW_HDR_410, + B43_FW_HDR_351, +}; + +/* Pointers to the firmware data and meta information about it. */ +struct b43_firmware { + /* Microcode */ + struct b43_firmware_file ucode; + /* PCM code */ + struct b43_firmware_file pcm; + /* Initial MMIO values for the firmware */ + struct b43_firmware_file initvals; + /* Initial MMIO values for the firmware, band-specific */ + struct b43_firmware_file initvals_band; + + /* Firmware revision */ + u16 rev; + /* Firmware patchlevel */ + u16 patch; + + /* Format of header used by firmware */ + enum b43_firmware_hdr_format hdr_format; + + /* Set to true, if we are using an opensource firmware. + * Use this to check for proprietary vs opensource. */ + bool opensource; + /* Set to true, if the core needs a PCM firmware, but + * we failed to load one. This is always false for + * core rev > 10, as these don't need PCM firmware. */ + bool pcm_request_failed; +}; + +enum b43_band { + B43_BAND_2G = 0, + B43_BAND_5G_LO = 1, + B43_BAND_5G_MI = 2, + B43_BAND_5G_HI = 3, +}; + +/* Device (802.11 core) initialization status. */ +enum { + B43_STAT_UNINIT = 0, /* Uninitialized. */ + B43_STAT_INITIALIZED = 1, /* Initialized, but not started, yet. */ + B43_STAT_STARTED = 2, /* Up and running. */ +}; +#define b43_status(wldev) atomic_read(&(wldev)->__init_status) +#define b43_set_status(wldev, stat) do { \ + atomic_set(&(wldev)->__init_status, (stat)); \ + smp_wmb(); \ + } while (0) + +/* Data structure for one wireless device (802.11 core) */ +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. */ + atomic_t __init_status; + + bool bad_frames_preempt; /* Use "Bad Frames Preemption" (default off) */ + bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM) */ + bool radio_hw_enable; /* saved state of radio hardware enabled state */ + bool qos_enabled; /* TRUE, if QoS is used. */ + bool hwcrypto_enabled; /* TRUE, if HW crypto acceleration is enabled. */ + bool use_pio; /* TRUE if next init should use PIO */ + + /* PHY/Radio device. */ + struct b43_phy phy; + + union { + /* DMA engines. */ + struct b43_dma dma; + /* PIO engines. */ + struct b43_pio pio; + }; + /* Use b43_using_pio_transfers() to check whether we are using + * DMA or PIO data transfers. */ + bool __using_pio_transfers; + + /* Various statistics about the physical device. */ + struct b43_stats stats; + + /* Reason code of the last interrupt. */ + u32 irq_reason; + u32 dma_reason[6]; + /* The currently active generic-interrupt mask. */ + u32 irq_mask; + + /* Link Quality calculation context. */ + struct b43_noise_calculation noisecalc; + /* if > 0 MAC is suspended. if == 0 MAC is enabled. */ + int mac_suspended; + + /* Periodic tasks */ + struct delayed_work periodic_work; + unsigned int periodic_state; + + struct work_struct restart_work; + + /* encryption/decryption */ + u16 ktp; /* Key table pointer */ + struct b43_key key[B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS]; + + /* Firmware data */ + struct b43_firmware fw; + + /* Devicelist in struct b43_wl (all 802.11 cores) */ + struct list_head list; + + /* Debugging stuff follows. */ +#ifdef CONFIG_B43_DEBUG + struct b43_dfsentry *dfsentry; + unsigned int irq_count; + unsigned int irq_bit_count[32]; + unsigned int tx_count; + unsigned int rx_count; +#endif +}; + +/* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */ +struct b43_wl { + /* Pointer to the active wireless device on this chip */ + struct b43_wldev *current_dev; + /* Pointer to the ieee80211 hardware data structure */ + struct ieee80211_hw *hw; + + /* Global driver mutex. Every operation must run with this mutex locked. */ + struct mutex mutex; + /* Hard-IRQ spinlock. This lock protects things used in the hard-IRQ + * handler, only. This basically is just the IRQ mask register. */ + spinlock_t hardirq_lock; + + /* Set this if we call ieee80211_register_hw() and check if we call + * ieee80211_unregister_hw(). */ + bool hw_registred; + + /* We can only have one operating interface (802.11 core) + * at a time. General information about this interface follows. + */ + + struct ieee80211_vif *vif; + /* The MAC address of the operating interface. */ + u8 mac_addr[ETH_ALEN]; + /* Current BSSID */ + u8 bssid[ETH_ALEN]; + /* Interface type. (NL80211_IFTYPE_XXX) */ + int if_type; + /* Is the card operating in AP, STA or IBSS mode? */ + bool operating; + /* filter flags */ + unsigned int filter_flags; + /* Stats about the wireless interface */ + struct ieee80211_low_level_stats ieee_stats; + +#ifdef CONFIG_B43_HWRNG + struct hwrng rng; + bool rng_initialized; + char rng_name[30 + 1]; +#endif /* CONFIG_B43_HWRNG */ + + bool radiotap_enabled; + bool radio_enabled; + + /* The beacon we are currently using (AP or IBSS mode). */ + struct sk_buff *current_beacon; + bool beacon0_uploaded; + bool beacon1_uploaded; + bool beacon_templates_virgin; /* Never wrote the templates? */ + struct work_struct beacon_update_trigger; + spinlock_t beacon_lock; + + /* The current QOS parameters for the 4 queues. */ + struct b43_qos_params qos_params[B43_QOS_QUEUE_NUM]; + + /* Work for adjustment of the transmission power. + * This is scheduled when we determine that the actual TX output + * power doesn't match what we want. */ + struct work_struct txpower_adjust_work; + + /* Packet transmit work */ + struct work_struct tx_work; + + /* Queue of packets to be transmitted. */ + struct sk_buff_head tx_queue[B43_QOS_QUEUE_NUM]; + + /* Flag that implement the queues stopping. */ + bool tx_queue_stopped[B43_QOS_QUEUE_NUM]; + + /* firmware loading work */ + struct work_struct firmware_load; + + /* The device LEDs. */ + struct b43_leds leds; + + /* Kmalloc'ed scratch space for PIO TX/RX. Protected by wl->mutex. */ + u8 pio_scratchspace[118] __attribute__((__aligned__(8))); + u8 pio_tailspace[4] __attribute__((__aligned__(8))); +}; + +static inline struct b43_wl *hw_to_b43_wl(struct ieee80211_hw *hw) +{ + return hw->priv; +} + +static inline struct b43_wldev *dev_to_b43_wldev(struct device *dev) +{ + struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); + return ssb_get_drvdata(ssb_dev); +} + +/* Is the device operating in a specified mode (NL80211_IFTYPE_XXX). */ +static inline int b43_is_mode(struct b43_wl *wl, int type) +{ + return (wl->operating && wl->if_type == type); +} + +/** + * b43_current_band - Returns the currently used band. + * Returns one of IEEE80211_BAND_2GHZ and IEEE80211_BAND_5GHZ. + */ +static inline enum ieee80211_band b43_current_band(struct b43_wl *wl) +{ + return wl->hw->conf.chandef.chan->band; +} + +static inline int b43_bus_may_powerdown(struct b43_wldev *wldev) +{ + return wldev->dev->bus_may_powerdown(wldev->dev); +} +static inline int b43_bus_powerup(struct b43_wldev *wldev, bool dynamic_pctl) +{ + return wldev->dev->bus_powerup(wldev->dev, dynamic_pctl); +} +static inline int b43_device_is_enabled(struct b43_wldev *wldev) +{ + return wldev->dev->device_is_enabled(wldev->dev); +} +static inline void b43_device_enable(struct b43_wldev *wldev, + u32 core_specific_flags) +{ + wldev->dev->device_enable(wldev->dev, core_specific_flags); +} +static inline void b43_device_disable(struct b43_wldev *wldev, + u32 core_specific_flags) +{ + wldev->dev->device_disable(wldev->dev, core_specific_flags); +} + +static inline u16 b43_read16(struct b43_wldev *dev, u16 offset) +{ + return dev->dev->read16(dev->dev, offset); +} + +static inline void b43_write16(struct b43_wldev *dev, u16 offset, u16 value) +{ + dev->dev->write16(dev->dev, offset, value); +} + +/* To optimize this check for flush_writes on BCM47XX_BCMA only. */ +static inline void b43_write16f(struct b43_wldev *dev, u16 offset, u16 value) +{ + b43_write16(dev, offset, value); +#if defined(CONFIG_BCM47XX_BCMA) + if (dev->dev->flush_writes) + b43_read16(dev, offset); +#endif +} + +static inline void b43_maskset16(struct b43_wldev *dev, u16 offset, u16 mask, + u16 set) +{ + b43_write16(dev, offset, (b43_read16(dev, offset) & mask) | set); +} + +static inline u32 b43_read32(struct b43_wldev *dev, u16 offset) +{ + return dev->dev->read32(dev->dev, offset); +} + +static inline void b43_write32(struct b43_wldev *dev, u16 offset, u32 value) +{ + dev->dev->write32(dev->dev, offset, value); +} + +static inline void b43_maskset32(struct b43_wldev *dev, u16 offset, u32 mask, + u32 set) +{ + b43_write32(dev, offset, (b43_read32(dev, offset) & mask) | set); +} + +static inline void b43_block_read(struct b43_wldev *dev, void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + dev->dev->block_read(dev->dev, buffer, count, offset, reg_width); +} + +static inline void b43_block_write(struct b43_wldev *dev, const void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + dev->dev->block_write(dev->dev, buffer, count, offset, reg_width); +} + +static inline bool b43_using_pio_transfers(struct b43_wldev *dev) +{ + return dev->__using_pio_transfers; +} + +/* Message printing */ +__printf(2, 3) void b43info(struct b43_wl *wl, const char *fmt, ...); +__printf(2, 3) void b43err(struct b43_wl *wl, const char *fmt, ...); +__printf(2, 3) void b43warn(struct b43_wl *wl, const char *fmt, ...); +__printf(2, 3) void b43dbg(struct b43_wl *wl, const char *fmt, ...); + + +/* A WARN_ON variant that vanishes when b43 debugging is disabled. + * This _also_ evaluates the arg with debugging disabled. */ +#if B43_DEBUG +# define B43_WARN_ON(x) WARN_ON(x) +#else +static inline bool __b43_warn_on_dummy(bool x) { return x; } +# define B43_WARN_ON(x) __b43_warn_on_dummy(unlikely(!!(x))) +#endif + +/* Convert an integer to a Q5.2 value */ +#define INT_TO_Q52(i) ((i) << 2) +/* Convert a Q5.2 value to an integer (precision loss!) */ +#define Q52_TO_INT(q52) ((q52) >> 2) +/* Macros for printing a value in Q5.2 format */ +#define Q52_FMT "%u.%u" +#define Q52_ARG(q52) Q52_TO_INT(q52), ((((q52) & 0x3) * 100) / 4) + +#endif /* B43_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/bus.c b/drivers/net/wireless/broadcom/b43/bus.c new file mode 100644 index 000000000000..17d16a391fe6 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/bus.c @@ -0,0 +1,265 @@ +/* + + Broadcom B43 wireless driver + Bus abstraction layer + + Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifdef CONFIG_BCM47XX_BCMA +#include +#endif + +#include "b43.h" +#include "bus.h" + +/* BCMA */ +#ifdef CONFIG_B43_BCMA +static int b43_bus_bcma_bus_may_powerdown(struct b43_bus_dev *dev) +{ + return 0; /* bcma_bus_may_powerdown(dev->bdev->bus); */ +} +static int b43_bus_bcma_bus_powerup(struct b43_bus_dev *dev, + bool dynamic_pctl) +{ + return 0; /* bcma_bus_powerup(dev->sdev->bus, dynamic_pctl); */ +} +static int b43_bus_bcma_device_is_enabled(struct b43_bus_dev *dev) +{ + return bcma_core_is_enabled(dev->bdev); +} +static void b43_bus_bcma_device_enable(struct b43_bus_dev *dev, + u32 core_specific_flags) +{ + bcma_core_enable(dev->bdev, core_specific_flags); +} +static void b43_bus_bcma_device_disable(struct b43_bus_dev *dev, + u32 core_specific_flags) +{ + bcma_core_disable(dev->bdev, core_specific_flags); +} +static u16 b43_bus_bcma_read16(struct b43_bus_dev *dev, u16 offset) +{ + return bcma_read16(dev->bdev, offset); +} +static u32 b43_bus_bcma_read32(struct b43_bus_dev *dev, u16 offset) +{ + return bcma_read32(dev->bdev, offset); +} +static +void b43_bus_bcma_write16(struct b43_bus_dev *dev, u16 offset, u16 value) +{ + bcma_write16(dev->bdev, offset, value); +} +static +void b43_bus_bcma_write32(struct b43_bus_dev *dev, u16 offset, u32 value) +{ + bcma_write32(dev->bdev, offset, value); +} +static +void b43_bus_bcma_block_read(struct b43_bus_dev *dev, void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + bcma_block_read(dev->bdev, buffer, count, offset, reg_width); +} +static +void b43_bus_bcma_block_write(struct b43_bus_dev *dev, const void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + bcma_block_write(dev->bdev, buffer, count, offset, reg_width); +} + +struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core) +{ + struct b43_bus_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + + dev->bus_type = B43_BUS_BCMA; + dev->bdev = core; + + dev->bus_may_powerdown = b43_bus_bcma_bus_may_powerdown; + dev->bus_powerup = b43_bus_bcma_bus_powerup; + dev->device_is_enabled = b43_bus_bcma_device_is_enabled; + dev->device_enable = b43_bus_bcma_device_enable; + dev->device_disable = b43_bus_bcma_device_disable; + + dev->read16 = b43_bus_bcma_read16; + dev->read32 = b43_bus_bcma_read32; + dev->write16 = b43_bus_bcma_write16; + dev->write32 = b43_bus_bcma_write32; + dev->block_read = b43_bus_bcma_block_read; + dev->block_write = b43_bus_bcma_block_write; +#ifdef CONFIG_BCM47XX_BCMA + if (b43_bus_host_is_pci(dev) && + bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA && + bcm47xx_bus.bcma.bus.chipinfo.id == BCMA_CHIP_ID_BCM4716) + dev->flush_writes = true; +#endif + + dev->dev = &core->dev; + dev->dma_dev = core->dma_dev; + dev->irq = core->irq; + + dev->board_vendor = core->bus->boardinfo.vendor; + dev->board_type = core->bus->boardinfo.type; + dev->board_rev = core->bus->sprom.board_rev; + + dev->chip_id = core->bus->chipinfo.id; + dev->chip_rev = core->bus->chipinfo.rev; + dev->chip_pkg = core->bus->chipinfo.pkg; + + dev->bus_sprom = &core->bus->sprom; + + dev->core_id = core->id.id; + dev->core_rev = core->id.rev; + + return dev; +} +#endif /* CONFIG_B43_BCMA */ + +/* SSB */ +#ifdef CONFIG_B43_SSB +static int b43_bus_ssb_bus_may_powerdown(struct b43_bus_dev *dev) +{ + return ssb_bus_may_powerdown(dev->sdev->bus); +} +static int b43_bus_ssb_bus_powerup(struct b43_bus_dev *dev, + bool dynamic_pctl) +{ + return ssb_bus_powerup(dev->sdev->bus, dynamic_pctl); +} +static int b43_bus_ssb_device_is_enabled(struct b43_bus_dev *dev) +{ + return ssb_device_is_enabled(dev->sdev); +} +static void b43_bus_ssb_device_enable(struct b43_bus_dev *dev, + u32 core_specific_flags) +{ + ssb_device_enable(dev->sdev, core_specific_flags); +} +static void b43_bus_ssb_device_disable(struct b43_bus_dev *dev, + u32 core_specific_flags) +{ + ssb_device_disable(dev->sdev, core_specific_flags); +} + +static u16 b43_bus_ssb_read16(struct b43_bus_dev *dev, u16 offset) +{ + return ssb_read16(dev->sdev, offset); +} +static u32 b43_bus_ssb_read32(struct b43_bus_dev *dev, u16 offset) +{ + return ssb_read32(dev->sdev, offset); +} +static void b43_bus_ssb_write16(struct b43_bus_dev *dev, u16 offset, u16 value) +{ + ssb_write16(dev->sdev, offset, value); +} +static void b43_bus_ssb_write32(struct b43_bus_dev *dev, u16 offset, u32 value) +{ + ssb_write32(dev->sdev, offset, value); +} +static void b43_bus_ssb_block_read(struct b43_bus_dev *dev, void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + ssb_block_read(dev->sdev, buffer, count, offset, reg_width); +} +static +void b43_bus_ssb_block_write(struct b43_bus_dev *dev, const void *buffer, + size_t count, u16 offset, u8 reg_width) +{ + ssb_block_write(dev->sdev, buffer, count, offset, reg_width); +} + +struct b43_bus_dev *b43_bus_dev_ssb_init(struct ssb_device *sdev) +{ + struct b43_bus_dev *dev; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + + dev->bus_type = B43_BUS_SSB; + dev->sdev = sdev; + + dev->bus_may_powerdown = b43_bus_ssb_bus_may_powerdown; + dev->bus_powerup = b43_bus_ssb_bus_powerup; + dev->device_is_enabled = b43_bus_ssb_device_is_enabled; + dev->device_enable = b43_bus_ssb_device_enable; + dev->device_disable = b43_bus_ssb_device_disable; + + dev->read16 = b43_bus_ssb_read16; + dev->read32 = b43_bus_ssb_read32; + dev->write16 = b43_bus_ssb_write16; + dev->write32 = b43_bus_ssb_write32; + dev->block_read = b43_bus_ssb_block_read; + dev->block_write = b43_bus_ssb_block_write; + + dev->dev = sdev->dev; + dev->dma_dev = sdev->dma_dev; + dev->irq = sdev->irq; + + dev->board_vendor = sdev->bus->boardinfo.vendor; + dev->board_type = sdev->bus->boardinfo.type; + dev->board_rev = sdev->bus->sprom.board_rev; + + dev->chip_id = sdev->bus->chip_id; + dev->chip_rev = sdev->bus->chip_rev; + dev->chip_pkg = sdev->bus->chip_package; + + dev->bus_sprom = &sdev->bus->sprom; + + dev->core_id = sdev->id.coreid; + dev->core_rev = sdev->id.revision; + + return dev; +} +#endif /* CONFIG_B43_SSB */ + +void *b43_bus_get_wldev(struct b43_bus_dev *dev) +{ + switch (dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + return bcma_get_drvdata(dev->bdev); +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + return ssb_get_drvdata(dev->sdev); +#endif + } + return NULL; +} + +void b43_bus_set_wldev(struct b43_bus_dev *dev, void *wldev) +{ + switch (dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + bcma_set_drvdata(dev->bdev, wldev); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + ssb_set_drvdata(dev->sdev, wldev); + break; +#endif + } +} diff --git a/drivers/net/wireless/broadcom/b43/bus.h b/drivers/net/wireless/broadcom/b43/bus.h new file mode 100644 index 000000000000..256c2c17939a --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/bus.h @@ -0,0 +1,95 @@ +#ifndef B43_BUS_H_ +#define B43_BUS_H_ + +enum b43_bus_type { +#ifdef CONFIG_B43_BCMA + B43_BUS_BCMA, +#endif +#ifdef CONFIG_B43_SSB + B43_BUS_SSB, +#endif +}; + +struct b43_bus_dev { + enum b43_bus_type bus_type; + union { + struct bcma_device *bdev; + struct ssb_device *sdev; + }; + + int (*bus_may_powerdown)(struct b43_bus_dev *dev); + int (*bus_powerup)(struct b43_bus_dev *dev, bool dynamic_pctl); + int (*device_is_enabled)(struct b43_bus_dev *dev); + void (*device_enable)(struct b43_bus_dev *dev, + u32 core_specific_flags); + void (*device_disable)(struct b43_bus_dev *dev, + u32 core_specific_flags); + + u16 (*read16)(struct b43_bus_dev *dev, u16 offset); + u32 (*read32)(struct b43_bus_dev *dev, u16 offset); + void (*write16)(struct b43_bus_dev *dev, u16 offset, u16 value); + void (*write32)(struct b43_bus_dev *dev, u16 offset, u32 value); + void (*block_read)(struct b43_bus_dev *dev, void *buffer, + size_t count, u16 offset, u8 reg_width); + void (*block_write)(struct b43_bus_dev *dev, const void *buffer, + size_t count, u16 offset, u8 reg_width); + bool flush_writes; + + struct device *dev; + struct device *dma_dev; + unsigned int irq; + + u16 board_vendor; + u16 board_type; + u16 board_rev; + + u16 chip_id; + u8 chip_rev; + u8 chip_pkg; + + struct ssb_sprom *bus_sprom; + + u16 core_id; + u8 core_rev; +}; + +static inline bool b43_bus_host_is_pcmcia(struct b43_bus_dev *dev) +{ +#ifdef CONFIG_B43_SSB + return (dev->bus_type == B43_BUS_SSB && + dev->sdev->bus->bustype == SSB_BUSTYPE_PCMCIA); +#else + return false; +#endif +}; + +static inline bool b43_bus_host_is_pci(struct b43_bus_dev *dev) +{ +#ifdef CONFIG_B43_BCMA + if (dev->bus_type == B43_BUS_BCMA) + return (dev->bdev->bus->hosttype == BCMA_HOSTTYPE_PCI); +#endif +#ifdef CONFIG_B43_SSB + if (dev->bus_type == B43_BUS_SSB) + return (dev->sdev->bus->bustype == SSB_BUSTYPE_PCI); +#endif + return false; +} + +static inline bool b43_bus_host_is_sdio(struct b43_bus_dev *dev) +{ +#ifdef CONFIG_B43_SSB + return (dev->bus_type == B43_BUS_SSB && + dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO); +#else + return false; +#endif +} + +struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core); +struct b43_bus_dev *b43_bus_dev_ssb_init(struct ssb_device *sdev); + +void *b43_bus_get_wldev(struct b43_bus_dev *dev); +void b43_bus_set_wldev(struct b43_bus_dev *dev, void *data); + +#endif /* B43_BUS_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/debugfs.c b/drivers/net/wireless/broadcom/b43/debugfs.c new file mode 100644 index 000000000000..b4bcd94aff6c --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/debugfs.c @@ -0,0 +1,826 @@ +/* + + Broadcom B43 wireless driver + + debugfs driver debugging code + + Copyright (c) 2005-2007 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include +#include +#include +#include +#include +#include + +#include "b43.h" +#include "main.h" +#include "debugfs.h" +#include "dma.h" +#include "xmit.h" + + +/* The root directory. */ +static struct dentry *rootdir; + +struct b43_debugfs_fops { + ssize_t (*read)(struct b43_wldev *dev, char *buf, size_t bufsize); + int (*write)(struct b43_wldev *dev, const char *buf, size_t count); + struct file_operations fops; + /* Offset of struct b43_dfs_file in struct b43_dfsentry */ + size_t file_struct_offset; +}; + +static inline +struct b43_dfs_file *fops_to_dfs_file(struct b43_wldev *dev, + const struct b43_debugfs_fops *dfops) +{ + void *p; + + p = dev->dfsentry; + p += dfops->file_struct_offset; + + return p; +} + + +#define fappend(fmt, x...) \ + do { \ + if (bufsize - count) \ + count += snprintf(buf + count, \ + bufsize - count, \ + fmt , ##x); \ + else \ + printk(KERN_ERR "b43: fappend overflow\n"); \ + } while (0) + + +/* The biggest address values for SHM access from the debugfs files. */ +#define B43_MAX_SHM_ROUTING 4 +#define B43_MAX_SHM_ADDR 0xFFFF + +static ssize_t shm16read__read_file(struct b43_wldev *dev, + char *buf, size_t bufsize) +{ + ssize_t count = 0; + unsigned int routing, addr; + u16 val; + + routing = dev->dfsentry->shm16read_routing_next; + addr = dev->dfsentry->shm16read_addr_next; + if ((routing > B43_MAX_SHM_ROUTING) || + (addr > B43_MAX_SHM_ADDR)) + return -EDESTADDRREQ; + + val = b43_shm_read16(dev, routing, addr); + fappend("0x%04X\n", val); + + return count; +} + +static int shm16read__write_file(struct b43_wldev *dev, + const char *buf, size_t count) +{ + unsigned int routing, addr; + int res; + + res = sscanf(buf, "0x%X 0x%X", &routing, &addr); + if (res != 2) + return -EINVAL; + if (routing > B43_MAX_SHM_ROUTING) + return -EADDRNOTAVAIL; + if (addr > B43_MAX_SHM_ADDR) + return -EADDRNOTAVAIL; + if (routing == B43_SHM_SHARED) { + if ((addr % 2) != 0) + return -EADDRNOTAVAIL; + } + + dev->dfsentry->shm16read_routing_next = routing; + dev->dfsentry->shm16read_addr_next = addr; + + return 0; +} + +static int shm16write__write_file(struct b43_wldev *dev, + const char *buf, size_t count) +{ + unsigned int routing, addr, mask, set; + u16 val; + int res; + + res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X", + &routing, &addr, &mask, &set); + if (res != 4) + return -EINVAL; + if (routing > B43_MAX_SHM_ROUTING) + return -EADDRNOTAVAIL; + if (addr > B43_MAX_SHM_ADDR) + return -EADDRNOTAVAIL; + if (routing == B43_SHM_SHARED) { + if ((addr % 2) != 0) + return -EADDRNOTAVAIL; + } + if ((mask > 0xFFFF) || (set > 0xFFFF)) + return -E2BIG; + + if (mask == 0) + val = 0; + else + val = b43_shm_read16(dev, routing, addr); + val &= mask; + val |= set; + b43_shm_write16(dev, routing, addr, val); + + return 0; +} + +static ssize_t shm32read__read_file(struct b43_wldev *dev, + char *buf, size_t bufsize) +{ + ssize_t count = 0; + unsigned int routing, addr; + u32 val; + + routing = dev->dfsentry->shm32read_routing_next; + addr = dev->dfsentry->shm32read_addr_next; + if ((routing > B43_MAX_SHM_ROUTING) || + (addr > B43_MAX_SHM_ADDR)) + return -EDESTADDRREQ; + + val = b43_shm_read32(dev, routing, addr); + fappend("0x%08X\n", val); + + return count; +} + +static int shm32read__write_file(struct b43_wldev *dev, + const char *buf, size_t count) +{ + unsigned int routing, addr; + int res; + + res = sscanf(buf, "0x%X 0x%X", &routing, &addr); + if (res != 2) + return -EINVAL; + if (routing > B43_MAX_SHM_ROUTING) + return -EADDRNOTAVAIL; + if (addr > B43_MAX_SHM_ADDR) + return -EADDRNOTAVAIL; + if (routing == B43_SHM_SHARED) { + if ((addr % 2) != 0) + return -EADDRNOTAVAIL; + } + + dev->dfsentry->shm32read_routing_next = routing; + dev->dfsentry->shm32read_addr_next = addr; + + return 0; +} + +static int shm32write__write_file(struct b43_wldev *dev, + const char *buf, size_t count) +{ + unsigned int routing, addr, mask, set; + u32 val; + int res; + + res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X", + &routing, &addr, &mask, &set); + if (res != 4) + return -EINVAL; + if (routing > B43_MAX_SHM_ROUTING) + return -EADDRNOTAVAIL; + if (addr > B43_MAX_SHM_ADDR) + return -EADDRNOTAVAIL; + if (routing == B43_SHM_SHARED) { + if ((addr % 2) != 0) + return -EADDRNOTAVAIL; + } + if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF)) + return -E2BIG; + + if (mask == 0) + val = 0; + else + val = b43_shm_read32(dev, routing, addr); + val &= mask; + val |= set; + b43_shm_write32(dev, routing, addr, val); + + return 0; +} + +/* The biggest MMIO address that we allow access to from the debugfs files. */ +#define B43_MAX_MMIO_ACCESS (0xF00 - 1) + +static ssize_t mmio16read__read_file(struct b43_wldev *dev, + char *buf, size_t bufsize) +{ + ssize_t count = 0; + unsigned int addr; + u16 val; + + addr = dev->dfsentry->mmio16read_next; + if (addr > B43_MAX_MMIO_ACCESS) + return -EDESTADDRREQ; + + val = b43_read16(dev, addr); + fappend("0x%04X\n", val); + + return count; +} + +static int mmio16read__write_file(struct b43_wldev *dev, + const char *buf, size_t count) +{ + unsigned int addr; + int res; + + res = sscanf(buf, "0x%X", &addr); + if (res != 1) + return -EINVAL; + if (addr > B43_MAX_MMIO_ACCESS) + return -EADDRNOTAVAIL; + if ((addr % 2) != 0) + return -EINVAL; + + dev->dfsentry->mmio16read_next = addr; + + return 0; +} + +static int mmio16write__write_file(struct b43_wldev *dev, + const char *buf, size_t count) +{ + unsigned int addr, mask, set; + int res; + u16 val; + + res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set); + if (res != 3) + return -EINVAL; + if (addr > B43_MAX_MMIO_ACCESS) + return -EADDRNOTAVAIL; + if ((mask > 0xFFFF) || (set > 0xFFFF)) + return -E2BIG; + if ((addr % 2) != 0) + return -EINVAL; + + if (mask == 0) + val = 0; + else + val = b43_read16(dev, addr); + val &= mask; + val |= set; + b43_write16(dev, addr, val); + + return 0; +} + +static ssize_t mmio32read__read_file(struct b43_wldev *dev, + char *buf, size_t bufsize) +{ + ssize_t count = 0; + unsigned int addr; + u32 val; + + addr = dev->dfsentry->mmio32read_next; + if (addr > B43_MAX_MMIO_ACCESS) + return -EDESTADDRREQ; + + val = b43_read32(dev, addr); + fappend("0x%08X\n", val); + + return count; +} + +static int mmio32read__write_file(struct b43_wldev *dev, + const char *buf, size_t count) +{ + unsigned int addr; + int res; + + res = sscanf(buf, "0x%X", &addr); + if (res != 1) + return -EINVAL; + if (addr > B43_MAX_MMIO_ACCESS) + return -EADDRNOTAVAIL; + if ((addr % 4) != 0) + return -EINVAL; + + dev->dfsentry->mmio32read_next = addr; + + return 0; +} + +static int mmio32write__write_file(struct b43_wldev *dev, + const char *buf, size_t count) +{ + unsigned int addr, mask, set; + int res; + u32 val; + + res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set); + if (res != 3) + return -EINVAL; + if (addr > B43_MAX_MMIO_ACCESS) + return -EADDRNOTAVAIL; + if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF)) + return -E2BIG; + if ((addr % 4) != 0) + return -EINVAL; + + if (mask == 0) + val = 0; + else + val = b43_read32(dev, addr); + val &= mask; + val |= set; + b43_write32(dev, addr, val); + + return 0; +} + +static ssize_t txstat_read_file(struct b43_wldev *dev, + char *buf, size_t bufsize) +{ + struct b43_txstatus_log *log = &dev->dfsentry->txstatlog; + ssize_t count = 0; + int i, idx; + struct b43_txstatus *stat; + + if (log->end < 0) { + fappend("Nothing transmitted, yet\n"); + goto out; + } + fappend("b43 TX status reports:\n\n" + "index | cookie | seq | phy_stat | frame_count | " + "rts_count | supp_reason | pm_indicated | " + "intermediate | for_ampdu | acked\n" "---\n"); + i = log->end + 1; + idx = 0; + while (1) { + if (i == B43_NR_LOGGED_TXSTATUS) + i = 0; + stat = &(log->log[i]); + if (stat->cookie) { + fappend("%03d | " + "0x%04X | 0x%04X | 0x%02X | " + "0x%X | 0x%X | " + "%u | %u | " + "%u | %u | %u\n", + idx, + stat->cookie, stat->seq, stat->phy_stat, + stat->frame_count, stat->rts_count, + stat->supp_reason, stat->pm_indicated, + stat->intermediate, stat->for_ampdu, + stat->acked); + idx++; + } + if (i == log->end) + break; + i++; + } +out: + + return count; +} + +static int restart_write_file(struct b43_wldev *dev, + const char *buf, size_t count) +{ + int err = 0; + + if (count > 0 && buf[0] == '1') { + b43_controller_restart(dev, "manually restarted"); + } else + err = -EINVAL; + + return err; +} + +static unsigned long calc_expire_secs(unsigned long now, + unsigned long time, + unsigned long expire) +{ + expire = time + expire; + + if (time_after(now, expire)) + return 0; /* expired */ + if (expire < now) { + /* jiffies wrapped */ + expire -= MAX_JIFFY_OFFSET; + now -= MAX_JIFFY_OFFSET; + } + B43_WARN_ON(expire < now); + + return (expire - now) / HZ; +} + +static ssize_t loctls_read_file(struct b43_wldev *dev, + char *buf, size_t bufsize) +{ + ssize_t count = 0; + struct b43_txpower_lo_control *lo; + int i, err = 0; + struct b43_lo_calib *cal; + unsigned long now = jiffies; + struct b43_phy *phy = &dev->phy; + + if (phy->type != B43_PHYTYPE_G) { + fappend("Device is not a G-PHY\n"); + err = -ENODEV; + goto out; + } + lo = phy->g->lo_control; + fappend("-- Local Oscillator calibration data --\n\n"); + fappend("HW-power-control enabled: %d\n", + dev->phy.hardware_power_control); + fappend("TX Bias: 0x%02X, TX Magn: 0x%02X (expire in %lu sec)\n", + lo->tx_bias, lo->tx_magn, + calc_expire_secs(now, lo->txctl_measured_time, + B43_LO_TXCTL_EXPIRE)); + fappend("Power Vector: 0x%08X%08X (expires in %lu sec)\n", + (unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32), + (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL), + calc_expire_secs(now, lo->pwr_vec_read_time, + B43_LO_PWRVEC_EXPIRE)); + + fappend("\nCalibrated settings:\n"); + list_for_each_entry(cal, &lo->calib_list, list) { + bool active; + + active = (b43_compare_bbatt(&cal->bbatt, &phy->g->bbatt) && + b43_compare_rfatt(&cal->rfatt, &phy->g->rfatt)); + fappend("BB(%d), RF(%d,%d) -> I=%d, Q=%d " + "(expires in %lu sec)%s\n", + cal->bbatt.att, + cal->rfatt.att, cal->rfatt.with_padmix, + cal->ctl.i, cal->ctl.q, + calc_expire_secs(now, cal->calib_time, + B43_LO_CALIB_EXPIRE), + active ? " ACTIVE" : ""); + } + + fappend("\nUsed RF attenuation values: Value(WithPadmix flag)\n"); + for (i = 0; i < lo->rfatt_list.len; i++) { + fappend("%u(%d), ", + lo->rfatt_list.list[i].att, + lo->rfatt_list.list[i].with_padmix); + } + fappend("\n"); + fappend("\nUsed Baseband attenuation values:\n"); + for (i = 0; i < lo->bbatt_list.len; i++) { + fappend("%u, ", + lo->bbatt_list.list[i].att); + } + fappend("\n"); + +out: + return err ? err : count; +} + +#undef fappend + +static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct b43_wldev *dev; + struct b43_debugfs_fops *dfops; + struct b43_dfs_file *dfile; + ssize_t uninitialized_var(ret); + char *buf; + const size_t bufsize = 1024 * 16; /* 16 kiB buffer */ + const size_t buforder = get_order(bufsize); + int err = 0; + + if (!count) + return 0; + dev = file->private_data; + if (!dev) + return -ENODEV; + + mutex_lock(&dev->wl->mutex); + if (b43_status(dev) < B43_STAT_INITIALIZED) { + err = -ENODEV; + goto out_unlock; + } + + dfops = container_of(file->f_op, struct b43_debugfs_fops, fops); + if (!dfops->read) { + err = -ENOSYS; + goto out_unlock; + } + dfile = fops_to_dfs_file(dev, dfops); + + if (!dfile->buffer) { + buf = (char *)__get_free_pages(GFP_KERNEL, buforder); + if (!buf) { + err = -ENOMEM; + goto out_unlock; + } + memset(buf, 0, bufsize); + ret = dfops->read(dev, buf, bufsize); + if (ret <= 0) { + free_pages((unsigned long)buf, buforder); + err = ret; + goto out_unlock; + } + dfile->data_len = ret; + dfile->buffer = buf; + } + + ret = simple_read_from_buffer(userbuf, count, ppos, + dfile->buffer, + dfile->data_len); + if (*ppos >= dfile->data_len) { + free_pages((unsigned long)dfile->buffer, buforder); + dfile->buffer = NULL; + dfile->data_len = 0; + } +out_unlock: + mutex_unlock(&dev->wl->mutex); + + return err ? err : ret; +} + +static ssize_t b43_debugfs_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct b43_wldev *dev; + struct b43_debugfs_fops *dfops; + char *buf; + int err = 0; + + if (!count) + return 0; + if (count > PAGE_SIZE) + return -E2BIG; + dev = file->private_data; + if (!dev) + return -ENODEV; + + mutex_lock(&dev->wl->mutex); + if (b43_status(dev) < B43_STAT_INITIALIZED) { + err = -ENODEV; + goto out_unlock; + } + + dfops = container_of(file->f_op, struct b43_debugfs_fops, fops); + if (!dfops->write) { + err = -ENOSYS; + goto out_unlock; + } + + buf = (char *)get_zeroed_page(GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto out_unlock; + } + if (copy_from_user(buf, userbuf, count)) { + err = -EFAULT; + goto out_freepage; + } + err = dfops->write(dev, buf, count); + if (err) + goto out_freepage; + +out_freepage: + free_page((unsigned long)buf); +out_unlock: + mutex_unlock(&dev->wl->mutex); + + return err ? err : count; +} + + +#define B43_DEBUGFS_FOPS(name, _read, _write) \ + static struct b43_debugfs_fops fops_##name = { \ + .read = _read, \ + .write = _write, \ + .fops = { \ + .open = simple_open, \ + .read = b43_debugfs_read, \ + .write = b43_debugfs_write, \ + .llseek = generic_file_llseek, \ + }, \ + .file_struct_offset = offsetof(struct b43_dfsentry, \ + file_##name), \ + } + +B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file); +B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file); +B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file); +B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file); +B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file); +B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file); +B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file); +B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file); +B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL); +B43_DEBUGFS_FOPS(restart, NULL, restart_write_file); +B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL); + + +bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature) +{ + bool enabled; + + enabled = (dev->dfsentry && dev->dfsentry->dyn_debug[feature]); + if (unlikely(enabled)) { + /* Force full debugging messages, if the user enabled + * some dynamic debugging feature. */ + b43_modparam_verbose = B43_VERBOSITY_MAX; + } + + return enabled; +} + +static void b43_remove_dynamic_debug(struct b43_wldev *dev) +{ + struct b43_dfsentry *e = dev->dfsentry; + int i; + + for (i = 0; i < __B43_NR_DYNDBG; i++) + debugfs_remove(e->dyn_debug_dentries[i]); +} + +static void b43_add_dynamic_debug(struct b43_wldev *dev) +{ + struct b43_dfsentry *e = dev->dfsentry; + struct dentry *d; + +#define add_dyn_dbg(name, id, initstate) do { \ + e->dyn_debug[id] = (initstate); \ + d = debugfs_create_bool(name, 0600, e->subdir, \ + &(e->dyn_debug[id])); \ + if (!IS_ERR(d)) \ + e->dyn_debug_dentries[id] = d; \ + } while (0) + + add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, false); + add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, false); + add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, false); + add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, false); + add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, false); + add_dyn_dbg("debug_lo", B43_DBG_LO, false); + add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, false); + add_dyn_dbg("debug_keys", B43_DBG_KEYS, false); + add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, false); + +#undef add_dyn_dbg +} + +void b43_debugfs_add_device(struct b43_wldev *dev) +{ + struct b43_dfsentry *e; + struct b43_txstatus_log *log; + char devdir[16]; + + B43_WARN_ON(!dev); + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (!e) { + b43err(dev->wl, "debugfs: add device OOM\n"); + return; + } + e->dev = dev; + log = &e->txstatlog; + log->log = kcalloc(B43_NR_LOGGED_TXSTATUS, + sizeof(struct b43_txstatus), GFP_KERNEL); + if (!log->log) { + b43err(dev->wl, "debugfs: add device txstatus OOM\n"); + kfree(e); + return; + } + log->end = -1; + + dev->dfsentry = e; + + snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy)); + e->subdir = debugfs_create_dir(devdir, rootdir); + if (!e->subdir || IS_ERR(e->subdir)) { + if (e->subdir == ERR_PTR(-ENODEV)) { + b43dbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not " + "enabled in kernel config\n"); + } else { + b43err(dev->wl, "debugfs: cannot create %s directory\n", + devdir); + } + dev->dfsentry = NULL; + kfree(log->log); + kfree(e); + return; + } + + e->mmio16read_next = 0xFFFF; /* invalid address */ + e->mmio32read_next = 0xFFFF; /* invalid address */ + e->shm16read_routing_next = 0xFFFFFFFF; /* invalid routing */ + e->shm16read_addr_next = 0xFFFFFFFF; /* invalid address */ + e->shm32read_routing_next = 0xFFFFFFFF; /* invalid routing */ + e->shm32read_addr_next = 0xFFFFFFFF; /* invalid address */ + +#define ADD_FILE(name, mode) \ + do { \ + struct dentry *d; \ + d = debugfs_create_file(__stringify(name), \ + mode, e->subdir, dev, \ + &fops_##name.fops); \ + e->file_##name.dentry = NULL; \ + if (!IS_ERR(d)) \ + e->file_##name.dentry = d; \ + } while (0) + + + ADD_FILE(shm16read, 0600); + ADD_FILE(shm16write, 0200); + ADD_FILE(shm32read, 0600); + ADD_FILE(shm32write, 0200); + ADD_FILE(mmio16read, 0600); + ADD_FILE(mmio16write, 0200); + ADD_FILE(mmio32read, 0600); + ADD_FILE(mmio32write, 0200); + ADD_FILE(txstat, 0400); + ADD_FILE(restart, 0200); + ADD_FILE(loctls, 0400); + +#undef ADD_FILE + + b43_add_dynamic_debug(dev); +} + +void b43_debugfs_remove_device(struct b43_wldev *dev) +{ + struct b43_dfsentry *e; + + if (!dev) + return; + e = dev->dfsentry; + if (!e) + return; + b43_remove_dynamic_debug(dev); + + debugfs_remove(e->file_shm16read.dentry); + debugfs_remove(e->file_shm16write.dentry); + debugfs_remove(e->file_shm32read.dentry); + debugfs_remove(e->file_shm32write.dentry); + debugfs_remove(e->file_mmio16read.dentry); + debugfs_remove(e->file_mmio16write.dentry); + debugfs_remove(e->file_mmio32read.dentry); + debugfs_remove(e->file_mmio32write.dentry); + debugfs_remove(e->file_txstat.dentry); + debugfs_remove(e->file_restart.dentry); + debugfs_remove(e->file_loctls.dentry); + + debugfs_remove(e->subdir); + kfree(e->txstatlog.log); + kfree(e); +} + +void b43_debugfs_log_txstat(struct b43_wldev *dev, + const struct b43_txstatus *status) +{ + struct b43_dfsentry *e = dev->dfsentry; + struct b43_txstatus_log *log; + struct b43_txstatus *cur; + int i; + + if (!e) + return; + log = &e->txstatlog; + i = log->end + 1; + if (i == B43_NR_LOGGED_TXSTATUS) + i = 0; + log->end = i; + cur = &(log->log[i]); + memcpy(cur, status, sizeof(*cur)); +} + +void b43_debugfs_init(void) +{ + rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (IS_ERR(rootdir)) + rootdir = NULL; +} + +void b43_debugfs_exit(void) +{ + debugfs_remove(rootdir); +} diff --git a/drivers/net/wireless/broadcom/b43/debugfs.h b/drivers/net/wireless/broadcom/b43/debugfs.h new file mode 100644 index 000000000000..d05377745011 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/debugfs.h @@ -0,0 +1,111 @@ +#ifndef B43_DEBUGFS_H_ +#define B43_DEBUGFS_H_ + +struct b43_wldev; +struct b43_txstatus; + +enum b43_dyndbg { /* Dynamic debugging features */ + B43_DBG_XMITPOWER, + B43_DBG_DMAOVERFLOW, + B43_DBG_DMAVERBOSE, + B43_DBG_PWORK_FAST, + B43_DBG_PWORK_STOP, + B43_DBG_LO, + B43_DBG_FIRMWARE, + B43_DBG_KEYS, + B43_DBG_VERBOSESTATS, + __B43_NR_DYNDBG, +}; + +#ifdef CONFIG_B43_DEBUG + +struct dentry; + +#define B43_NR_LOGGED_TXSTATUS 100 + +struct b43_txstatus_log { + /* This structure is protected by wl->mutex */ + + struct b43_txstatus *log; + int end; +}; + +struct b43_dfs_file { + struct dentry *dentry; + char *buffer; + size_t data_len; +}; + +struct b43_dfsentry { + struct b43_wldev *dev; + struct dentry *subdir; + + struct b43_dfs_file file_shm16read; + struct b43_dfs_file file_shm16write; + struct b43_dfs_file file_shm32read; + struct b43_dfs_file file_shm32write; + struct b43_dfs_file file_mmio16read; + struct b43_dfs_file file_mmio16write; + struct b43_dfs_file file_mmio32read; + struct b43_dfs_file file_mmio32write; + struct b43_dfs_file file_txstat; + struct b43_dfs_file file_txpower_g; + struct b43_dfs_file file_restart; + struct b43_dfs_file file_loctls; + + struct b43_txstatus_log txstatlog; + + /* The cached address for the next mmio16read file read */ + u16 mmio16read_next; + /* The cached address for the next mmio32read file read */ + u16 mmio32read_next; + + /* The cached address for the next shm16read file read */ + u32 shm16read_routing_next; + u32 shm16read_addr_next; + /* The cached address for the next shm32read file read */ + u32 shm32read_routing_next; + u32 shm32read_addr_next; + + /* Enabled/Disabled list for the dynamic debugging features. */ + bool dyn_debug[__B43_NR_DYNDBG]; + /* Dentries for the dynamic debugging entries. */ + struct dentry *dyn_debug_dentries[__B43_NR_DYNDBG]; +}; + +bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature); + +void b43_debugfs_init(void); +void b43_debugfs_exit(void); +void b43_debugfs_add_device(struct b43_wldev *dev); +void b43_debugfs_remove_device(struct b43_wldev *dev); +void b43_debugfs_log_txstat(struct b43_wldev *dev, + const struct b43_txstatus *status); + +#else /* CONFIG_B43_DEBUG */ + +static inline bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature) +{ + return false; +} + +static inline void b43_debugfs_init(void) +{ +} +static inline void b43_debugfs_exit(void) +{ +} +static inline void b43_debugfs_add_device(struct b43_wldev *dev) +{ +} +static inline void b43_debugfs_remove_device(struct b43_wldev *dev) +{ +} +static inline void b43_debugfs_log_txstat(struct b43_wldev *dev, + const struct b43_txstatus *status) +{ +} + +#endif /* CONFIG_B43_DEBUG */ + +#endif /* B43_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/dma.c b/drivers/net/wireless/broadcom/b43/dma.c new file mode 100644 index 000000000000..6837064908be --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/dma.c @@ -0,0 +1,1831 @@ +/* + + Broadcom B43 wireless driver + + DMA ringbuffer and descriptor allocation/management + + Copyright (c) 2005, 2006 Michael Buesch + + Some code in this file is derived from the b44.c driver + Copyright (C) 2002 David S. Miller + Copyright (C) Pekka Pietikainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "dma.h" +#include "main.h" +#include "debugfs.h" +#include "xmit.h" + +#include +#include +#include +#include +#include +#include +#include + + +/* Required number of TX DMA slots per TX frame. + * This currently is 2, because we put the header and the ieee80211 frame + * into separate slots. */ +#define TX_SLOTS_PER_FRAME 2 + +static u32 b43_dma_address(struct b43_dma *dma, dma_addr_t dmaaddr, + enum b43_addrtype addrtype) +{ + u32 uninitialized_var(addr); + + switch (addrtype) { + case B43_DMA_ADDR_LOW: + addr = lower_32_bits(dmaaddr); + if (dma->translation_in_low) { + addr &= ~SSB_DMA_TRANSLATION_MASK; + addr |= dma->translation; + } + break; + case B43_DMA_ADDR_HIGH: + addr = upper_32_bits(dmaaddr); + if (!dma->translation_in_low) { + addr &= ~SSB_DMA_TRANSLATION_MASK; + addr |= dma->translation; + } + break; + case B43_DMA_ADDR_EXT: + if (dma->translation_in_low) + addr = lower_32_bits(dmaaddr); + else + addr = upper_32_bits(dmaaddr); + addr &= SSB_DMA_TRANSLATION_MASK; + addr >>= SSB_DMA_TRANSLATION_SHIFT; + break; + } + + return addr; +} + +/* 32bit DMA ops. */ +static +struct b43_dmadesc_generic *op32_idx2desc(struct b43_dmaring *ring, + int slot, + struct b43_dmadesc_meta **meta) +{ + struct b43_dmadesc32 *desc; + + *meta = &(ring->meta[slot]); + desc = ring->descbase; + desc = &(desc[slot]); + + return (struct b43_dmadesc_generic *)desc; +} + +static void op32_fill_descriptor(struct b43_dmaring *ring, + struct b43_dmadesc_generic *desc, + dma_addr_t dmaaddr, u16 bufsize, + int start, int end, int irq) +{ + struct b43_dmadesc32 *descbase = ring->descbase; + int slot; + u32 ctl; + u32 addr; + u32 addrext; + + slot = (int)(&(desc->dma32) - descbase); + B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); + + addr = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW); + addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT); + + ctl = bufsize & B43_DMA32_DCTL_BYTECNT; + if (slot == ring->nr_slots - 1) + ctl |= B43_DMA32_DCTL_DTABLEEND; + if (start) + ctl |= B43_DMA32_DCTL_FRAMESTART; + if (end) + ctl |= B43_DMA32_DCTL_FRAMEEND; + if (irq) + ctl |= B43_DMA32_DCTL_IRQ; + ctl |= (addrext << B43_DMA32_DCTL_ADDREXT_SHIFT) + & B43_DMA32_DCTL_ADDREXT_MASK; + + desc->dma32.control = cpu_to_le32(ctl); + desc->dma32.address = cpu_to_le32(addr); +} + +static void op32_poke_tx(struct b43_dmaring *ring, int slot) +{ + b43_dma_write(ring, B43_DMA32_TXINDEX, + (u32) (slot * sizeof(struct b43_dmadesc32))); +} + +static void op32_tx_suspend(struct b43_dmaring *ring) +{ + b43_dma_write(ring, B43_DMA32_TXCTL, b43_dma_read(ring, B43_DMA32_TXCTL) + | B43_DMA32_TXSUSPEND); +} + +static void op32_tx_resume(struct b43_dmaring *ring) +{ + b43_dma_write(ring, B43_DMA32_TXCTL, b43_dma_read(ring, B43_DMA32_TXCTL) + & ~B43_DMA32_TXSUSPEND); +} + +static int op32_get_current_rxslot(struct b43_dmaring *ring) +{ + u32 val; + + val = b43_dma_read(ring, B43_DMA32_RXSTATUS); + val &= B43_DMA32_RXDPTR; + + return (val / sizeof(struct b43_dmadesc32)); +} + +static void op32_set_current_rxslot(struct b43_dmaring *ring, int slot) +{ + b43_dma_write(ring, B43_DMA32_RXINDEX, + (u32) (slot * sizeof(struct b43_dmadesc32))); +} + +static const struct b43_dma_ops dma32_ops = { + .idx2desc = op32_idx2desc, + .fill_descriptor = op32_fill_descriptor, + .poke_tx = op32_poke_tx, + .tx_suspend = op32_tx_suspend, + .tx_resume = op32_tx_resume, + .get_current_rxslot = op32_get_current_rxslot, + .set_current_rxslot = op32_set_current_rxslot, +}; + +/* 64bit DMA ops. */ +static +struct b43_dmadesc_generic *op64_idx2desc(struct b43_dmaring *ring, + int slot, + struct b43_dmadesc_meta **meta) +{ + struct b43_dmadesc64 *desc; + + *meta = &(ring->meta[slot]); + desc = ring->descbase; + desc = &(desc[slot]); + + return (struct b43_dmadesc_generic *)desc; +} + +static void op64_fill_descriptor(struct b43_dmaring *ring, + struct b43_dmadesc_generic *desc, + dma_addr_t dmaaddr, u16 bufsize, + int start, int end, int irq) +{ + struct b43_dmadesc64 *descbase = ring->descbase; + int slot; + u32 ctl0 = 0, ctl1 = 0; + u32 addrlo, addrhi; + u32 addrext; + + slot = (int)(&(desc->dma64) - descbase); + B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); + + addrlo = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW); + addrhi = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_HIGH); + addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT); + + if (slot == ring->nr_slots - 1) + ctl0 |= B43_DMA64_DCTL0_DTABLEEND; + if (start) + ctl0 |= B43_DMA64_DCTL0_FRAMESTART; + if (end) + ctl0 |= B43_DMA64_DCTL0_FRAMEEND; + if (irq) + ctl0 |= B43_DMA64_DCTL0_IRQ; + ctl1 |= bufsize & B43_DMA64_DCTL1_BYTECNT; + ctl1 |= (addrext << B43_DMA64_DCTL1_ADDREXT_SHIFT) + & B43_DMA64_DCTL1_ADDREXT_MASK; + + desc->dma64.control0 = cpu_to_le32(ctl0); + desc->dma64.control1 = cpu_to_le32(ctl1); + desc->dma64.address_low = cpu_to_le32(addrlo); + desc->dma64.address_high = cpu_to_le32(addrhi); +} + +static void op64_poke_tx(struct b43_dmaring *ring, int slot) +{ + b43_dma_write(ring, B43_DMA64_TXINDEX, + (u32) (slot * sizeof(struct b43_dmadesc64))); +} + +static void op64_tx_suspend(struct b43_dmaring *ring) +{ + b43_dma_write(ring, B43_DMA64_TXCTL, b43_dma_read(ring, B43_DMA64_TXCTL) + | B43_DMA64_TXSUSPEND); +} + +static void op64_tx_resume(struct b43_dmaring *ring) +{ + b43_dma_write(ring, B43_DMA64_TXCTL, b43_dma_read(ring, B43_DMA64_TXCTL) + & ~B43_DMA64_TXSUSPEND); +} + +static int op64_get_current_rxslot(struct b43_dmaring *ring) +{ + u32 val; + + val = b43_dma_read(ring, B43_DMA64_RXSTATUS); + val &= B43_DMA64_RXSTATDPTR; + + return (val / sizeof(struct b43_dmadesc64)); +} + +static void op64_set_current_rxslot(struct b43_dmaring *ring, int slot) +{ + b43_dma_write(ring, B43_DMA64_RXINDEX, + (u32) (slot * sizeof(struct b43_dmadesc64))); +} + +static const struct b43_dma_ops dma64_ops = { + .idx2desc = op64_idx2desc, + .fill_descriptor = op64_fill_descriptor, + .poke_tx = op64_poke_tx, + .tx_suspend = op64_tx_suspend, + .tx_resume = op64_tx_resume, + .get_current_rxslot = op64_get_current_rxslot, + .set_current_rxslot = op64_set_current_rxslot, +}; + +static inline int free_slots(struct b43_dmaring *ring) +{ + return (ring->nr_slots - ring->used_slots); +} + +static inline int next_slot(struct b43_dmaring *ring, int slot) +{ + B43_WARN_ON(!(slot >= -1 && slot <= ring->nr_slots - 1)); + if (slot == ring->nr_slots - 1) + return 0; + return slot + 1; +} + +static inline int prev_slot(struct b43_dmaring *ring, int slot) +{ + B43_WARN_ON(!(slot >= 0 && slot <= ring->nr_slots - 1)); + if (slot == 0) + return ring->nr_slots - 1; + return slot - 1; +} + +#ifdef CONFIG_B43_DEBUG +static void update_max_used_slots(struct b43_dmaring *ring, + int current_used_slots) +{ + if (current_used_slots <= ring->max_used_slots) + return; + ring->max_used_slots = current_used_slots; + if (b43_debug(ring->dev, B43_DBG_DMAVERBOSE)) { + b43dbg(ring->dev->wl, + "max_used_slots increased to %d on %s ring %d\n", + ring->max_used_slots, + ring->tx ? "TX" : "RX", ring->index); + } +} +#else +static inline + void update_max_used_slots(struct b43_dmaring *ring, int current_used_slots) +{ +} +#endif /* DEBUG */ + +/* Request a slot for usage. */ +static inline int request_slot(struct b43_dmaring *ring) +{ + int slot; + + B43_WARN_ON(!ring->tx); + B43_WARN_ON(ring->stopped); + B43_WARN_ON(free_slots(ring) == 0); + + slot = next_slot(ring, ring->current_slot); + ring->current_slot = slot; + ring->used_slots++; + + update_max_used_slots(ring, ring->used_slots); + + return slot; +} + +static u16 b43_dmacontroller_base(enum b43_dmatype type, int controller_idx) +{ + static const u16 map64[] = { + B43_MMIO_DMA64_BASE0, + B43_MMIO_DMA64_BASE1, + B43_MMIO_DMA64_BASE2, + B43_MMIO_DMA64_BASE3, + B43_MMIO_DMA64_BASE4, + B43_MMIO_DMA64_BASE5, + }; + static const u16 map32[] = { + B43_MMIO_DMA32_BASE0, + B43_MMIO_DMA32_BASE1, + B43_MMIO_DMA32_BASE2, + B43_MMIO_DMA32_BASE3, + B43_MMIO_DMA32_BASE4, + B43_MMIO_DMA32_BASE5, + }; + + if (type == B43_DMA_64BIT) { + B43_WARN_ON(!(controller_idx >= 0 && + controller_idx < ARRAY_SIZE(map64))); + return map64[controller_idx]; + } + B43_WARN_ON(!(controller_idx >= 0 && + controller_idx < ARRAY_SIZE(map32))); + return map32[controller_idx]; +} + +static inline + dma_addr_t map_descbuffer(struct b43_dmaring *ring, + unsigned char *buf, size_t len, int tx) +{ + dma_addr_t dmaaddr; + + if (tx) { + dmaaddr = dma_map_single(ring->dev->dev->dma_dev, + buf, len, DMA_TO_DEVICE); + } else { + dmaaddr = dma_map_single(ring->dev->dev->dma_dev, + buf, len, DMA_FROM_DEVICE); + } + + return dmaaddr; +} + +static inline + void unmap_descbuffer(struct b43_dmaring *ring, + dma_addr_t addr, size_t len, int tx) +{ + if (tx) { + dma_unmap_single(ring->dev->dev->dma_dev, + addr, len, DMA_TO_DEVICE); + } else { + dma_unmap_single(ring->dev->dev->dma_dev, + addr, len, DMA_FROM_DEVICE); + } +} + +static inline + void sync_descbuffer_for_cpu(struct b43_dmaring *ring, + dma_addr_t addr, size_t len) +{ + B43_WARN_ON(ring->tx); + dma_sync_single_for_cpu(ring->dev->dev->dma_dev, + addr, len, DMA_FROM_DEVICE); +} + +static inline + void sync_descbuffer_for_device(struct b43_dmaring *ring, + dma_addr_t addr, size_t len) +{ + B43_WARN_ON(ring->tx); + dma_sync_single_for_device(ring->dev->dev->dma_dev, + addr, len, DMA_FROM_DEVICE); +} + +static inline + void free_descriptor_buffer(struct b43_dmaring *ring, + struct b43_dmadesc_meta *meta) +{ + if (meta->skb) { + if (ring->tx) + ieee80211_free_txskb(ring->dev->wl->hw, meta->skb); + else + dev_kfree_skb_any(meta->skb); + meta->skb = NULL; + } +} + +static int alloc_ringmemory(struct b43_dmaring *ring) +{ + /* The specs call for 4K buffers for 30- and 32-bit DMA with 4K + * alignment and 8K buffers for 64-bit DMA with 8K alignment. + * In practice we could use smaller buffers for the latter, but the + * alignment is really important because of the hardware bug. If bit + * 0x00001000 is used in DMA address, some hardware (like BCM4331) + * copies that bit into B43_DMA64_RXSTATUS and we get false values from + * B43_DMA64_RXSTATDPTR. Let's just use 8K buffers even if we don't use + * more than 256 slots for ring. + */ + u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ? + B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE; + + ring->descbase = dma_zalloc_coherent(ring->dev->dev->dma_dev, + ring_mem_size, &(ring->dmabase), + GFP_KERNEL); + if (!ring->descbase) + return -ENOMEM; + + return 0; +} + +static void free_ringmemory(struct b43_dmaring *ring) +{ + u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ? + B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE; + dma_free_coherent(ring->dev->dev->dma_dev, ring_mem_size, + ring->descbase, ring->dmabase); +} + +/* Reset the RX DMA channel */ +static int b43_dmacontroller_rx_reset(struct b43_wldev *dev, u16 mmio_base, + enum b43_dmatype type) +{ + int i; + u32 value; + u16 offset; + + might_sleep(); + + offset = (type == B43_DMA_64BIT) ? B43_DMA64_RXCTL : B43_DMA32_RXCTL; + b43_write32(dev, mmio_base + offset, 0); + for (i = 0; i < 10; i++) { + offset = (type == B43_DMA_64BIT) ? B43_DMA64_RXSTATUS : + B43_DMA32_RXSTATUS; + value = b43_read32(dev, mmio_base + offset); + if (type == B43_DMA_64BIT) { + value &= B43_DMA64_RXSTAT; + if (value == B43_DMA64_RXSTAT_DISABLED) { + i = -1; + break; + } + } else { + value &= B43_DMA32_RXSTATE; + if (value == B43_DMA32_RXSTAT_DISABLED) { + i = -1; + break; + } + } + msleep(1); + } + if (i != -1) { + b43err(dev->wl, "DMA RX reset timed out\n"); + return -ENODEV; + } + + return 0; +} + +/* Reset the TX DMA channel */ +static int b43_dmacontroller_tx_reset(struct b43_wldev *dev, u16 mmio_base, + enum b43_dmatype type) +{ + int i; + u32 value; + u16 offset; + + might_sleep(); + + for (i = 0; i < 10; i++) { + offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXSTATUS : + B43_DMA32_TXSTATUS; + value = b43_read32(dev, mmio_base + offset); + if (type == B43_DMA_64BIT) { + value &= B43_DMA64_TXSTAT; + if (value == B43_DMA64_TXSTAT_DISABLED || + value == B43_DMA64_TXSTAT_IDLEWAIT || + value == B43_DMA64_TXSTAT_STOPPED) + break; + } else { + value &= B43_DMA32_TXSTATE; + if (value == B43_DMA32_TXSTAT_DISABLED || + value == B43_DMA32_TXSTAT_IDLEWAIT || + value == B43_DMA32_TXSTAT_STOPPED) + break; + } + msleep(1); + } + offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXCTL : B43_DMA32_TXCTL; + b43_write32(dev, mmio_base + offset, 0); + for (i = 0; i < 10; i++) { + offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXSTATUS : + B43_DMA32_TXSTATUS; + value = b43_read32(dev, mmio_base + offset); + if (type == B43_DMA_64BIT) { + value &= B43_DMA64_TXSTAT; + if (value == B43_DMA64_TXSTAT_DISABLED) { + i = -1; + break; + } + } else { + value &= B43_DMA32_TXSTATE; + if (value == B43_DMA32_TXSTAT_DISABLED) { + i = -1; + break; + } + } + msleep(1); + } + if (i != -1) { + b43err(dev->wl, "DMA TX reset timed out\n"); + return -ENODEV; + } + /* ensure the reset is completed. */ + msleep(1); + + return 0; +} + +/* Check if a DMA mapping address is invalid. */ +static bool b43_dma_mapping_error(struct b43_dmaring *ring, + dma_addr_t addr, + size_t buffersize, bool dma_to_device) +{ + if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr))) + return true; + + switch (ring->type) { + case B43_DMA_30BIT: + if ((u64)addr + buffersize > (1ULL << 30)) + goto address_error; + break; + case B43_DMA_32BIT: + if ((u64)addr + buffersize > (1ULL << 32)) + goto address_error; + break; + case B43_DMA_64BIT: + /* Currently we can't have addresses beyond + * 64bit in the kernel. */ + break; + } + + /* The address is OK. */ + return false; + +address_error: + /* We can't support this address. Unmap it again. */ + unmap_descbuffer(ring, addr, buffersize, dma_to_device); + + return true; +} + +static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb) +{ + unsigned char *f = skb->data + ring->frameoffset; + + return ((f[0] & f[1] & f[2] & f[3] & f[4] & f[5] & f[6] & f[7]) == 0xFF); +} + +static void b43_poison_rx_buffer(struct b43_dmaring *ring, struct sk_buff *skb) +{ + struct b43_rxhdr_fw4 *rxhdr; + unsigned char *frame; + + /* This poisons the RX buffer to detect DMA failures. */ + + rxhdr = (struct b43_rxhdr_fw4 *)(skb->data); + rxhdr->frame_len = 0; + + B43_WARN_ON(ring->rx_buffersize < ring->frameoffset + sizeof(struct b43_plcp_hdr6) + 2); + frame = skb->data + ring->frameoffset; + memset(frame, 0xFF, sizeof(struct b43_plcp_hdr6) + 2 /* padding */); +} + +static int setup_rx_descbuffer(struct b43_dmaring *ring, + struct b43_dmadesc_generic *desc, + struct b43_dmadesc_meta *meta, gfp_t gfp_flags) +{ + dma_addr_t dmaaddr; + struct sk_buff *skb; + + B43_WARN_ON(ring->tx); + + skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); + if (unlikely(!skb)) + return -ENOMEM; + b43_poison_rx_buffer(ring, skb); + dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); + if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { + /* ugh. try to realloc in zone_dma */ + gfp_flags |= GFP_DMA; + + dev_kfree_skb_any(skb); + + skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); + if (unlikely(!skb)) + return -ENOMEM; + b43_poison_rx_buffer(ring, skb); + dmaaddr = map_descbuffer(ring, skb->data, + ring->rx_buffersize, 0); + if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { + b43err(ring->dev->wl, "RX DMA buffer allocation failed\n"); + dev_kfree_skb_any(skb); + return -EIO; + } + } + + meta->skb = skb; + meta->dmaaddr = dmaaddr; + ring->ops->fill_descriptor(ring, desc, dmaaddr, + ring->rx_buffersize, 0, 0, 0); + + return 0; +} + +/* Allocate the initial descbuffers. + * This is used for an RX ring only. + */ +static int alloc_initial_descbuffers(struct b43_dmaring *ring) +{ + int i, err = -ENOMEM; + struct b43_dmadesc_generic *desc; + struct b43_dmadesc_meta *meta; + + for (i = 0; i < ring->nr_slots; i++) { + desc = ring->ops->idx2desc(ring, i, &meta); + + err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); + if (err) { + b43err(ring->dev->wl, + "Failed to allocate initial descbuffers\n"); + goto err_unwind; + } + } + mb(); + ring->used_slots = ring->nr_slots; + err = 0; + out: + return err; + + err_unwind: + for (i--; i >= 0; i--) { + desc = ring->ops->idx2desc(ring, i, &meta); + + unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); + dev_kfree_skb(meta->skb); + } + goto out; +} + +/* Do initial setup of the DMA controller. + * Reset the controller, write the ring busaddress + * and switch the "enable" bit on. + */ +static int dmacontroller_setup(struct b43_dmaring *ring) +{ + int err = 0; + u32 value; + u32 addrext; + bool parity = ring->dev->dma.parity; + u32 addrlo; + u32 addrhi; + + if (ring->tx) { + if (ring->type == B43_DMA_64BIT) { + u64 ringbase = (u64) (ring->dmabase); + addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); + addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); + addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH); + + value = B43_DMA64_TXENABLE; + value |= (addrext << B43_DMA64_TXADDREXT_SHIFT) + & B43_DMA64_TXADDREXT_MASK; + if (!parity) + value |= B43_DMA64_TXPARITYDISABLE; + b43_dma_write(ring, B43_DMA64_TXCTL, value); + b43_dma_write(ring, B43_DMA64_TXRINGLO, addrlo); + b43_dma_write(ring, B43_DMA64_TXRINGHI, addrhi); + } else { + u32 ringbase = (u32) (ring->dmabase); + addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); + addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); + + value = B43_DMA32_TXENABLE; + value |= (addrext << B43_DMA32_TXADDREXT_SHIFT) + & B43_DMA32_TXADDREXT_MASK; + if (!parity) + value |= B43_DMA32_TXPARITYDISABLE; + b43_dma_write(ring, B43_DMA32_TXCTL, value); + b43_dma_write(ring, B43_DMA32_TXRING, addrlo); + } + } else { + err = alloc_initial_descbuffers(ring); + if (err) + goto out; + if (ring->type == B43_DMA_64BIT) { + u64 ringbase = (u64) (ring->dmabase); + addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); + addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); + addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH); + + value = (ring->frameoffset << B43_DMA64_RXFROFF_SHIFT); + value |= B43_DMA64_RXENABLE; + value |= (addrext << B43_DMA64_RXADDREXT_SHIFT) + & B43_DMA64_RXADDREXT_MASK; + if (!parity) + value |= B43_DMA64_RXPARITYDISABLE; + b43_dma_write(ring, B43_DMA64_RXCTL, value); + b43_dma_write(ring, B43_DMA64_RXRINGLO, addrlo); + b43_dma_write(ring, B43_DMA64_RXRINGHI, addrhi); + b43_dma_write(ring, B43_DMA64_RXINDEX, ring->nr_slots * + sizeof(struct b43_dmadesc64)); + } else { + u32 ringbase = (u32) (ring->dmabase); + addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); + addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); + + value = (ring->frameoffset << B43_DMA32_RXFROFF_SHIFT); + value |= B43_DMA32_RXENABLE; + value |= (addrext << B43_DMA32_RXADDREXT_SHIFT) + & B43_DMA32_RXADDREXT_MASK; + if (!parity) + value |= B43_DMA32_RXPARITYDISABLE; + b43_dma_write(ring, B43_DMA32_RXCTL, value); + b43_dma_write(ring, B43_DMA32_RXRING, addrlo); + b43_dma_write(ring, B43_DMA32_RXINDEX, ring->nr_slots * + sizeof(struct b43_dmadesc32)); + } + } + +out: + return err; +} + +/* Shutdown the DMA controller. */ +static void dmacontroller_cleanup(struct b43_dmaring *ring) +{ + if (ring->tx) { + b43_dmacontroller_tx_reset(ring->dev, ring->mmio_base, + ring->type); + if (ring->type == B43_DMA_64BIT) { + b43_dma_write(ring, B43_DMA64_TXRINGLO, 0); + b43_dma_write(ring, B43_DMA64_TXRINGHI, 0); + } else + b43_dma_write(ring, B43_DMA32_TXRING, 0); + } else { + b43_dmacontroller_rx_reset(ring->dev, ring->mmio_base, + ring->type); + if (ring->type == B43_DMA_64BIT) { + b43_dma_write(ring, B43_DMA64_RXRINGLO, 0); + b43_dma_write(ring, B43_DMA64_RXRINGHI, 0); + } else + b43_dma_write(ring, B43_DMA32_RXRING, 0); + } +} + +static void free_all_descbuffers(struct b43_dmaring *ring) +{ + struct b43_dmadesc_meta *meta; + int i; + + if (!ring->used_slots) + return; + for (i = 0; i < ring->nr_slots; i++) { + /* get meta - ignore returned value */ + ring->ops->idx2desc(ring, i, &meta); + + if (!meta->skb || b43_dma_ptr_is_poisoned(meta->skb)) { + B43_WARN_ON(!ring->tx); + continue; + } + if (ring->tx) { + unmap_descbuffer(ring, meta->dmaaddr, + meta->skb->len, 1); + } else { + unmap_descbuffer(ring, meta->dmaaddr, + ring->rx_buffersize, 0); + } + free_descriptor_buffer(ring, meta); + } +} + +static u64 supported_dma_mask(struct b43_wldev *dev) +{ + u32 tmp; + u16 mmio_base; + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOST); + if (tmp & BCMA_IOST_DMA64) + return DMA_BIT_MASK(64); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + tmp = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); + if (tmp & SSB_TMSHIGH_DMA64) + return DMA_BIT_MASK(64); + break; +#endif + } + + mmio_base = b43_dmacontroller_base(0, 0); + b43_write32(dev, mmio_base + B43_DMA32_TXCTL, B43_DMA32_TXADDREXT_MASK); + tmp = b43_read32(dev, mmio_base + B43_DMA32_TXCTL); + if (tmp & B43_DMA32_TXADDREXT_MASK) + return DMA_BIT_MASK(32); + + return DMA_BIT_MASK(30); +} + +static enum b43_dmatype dma_mask_to_engine_type(u64 dmamask) +{ + if (dmamask == DMA_BIT_MASK(30)) + return B43_DMA_30BIT; + if (dmamask == DMA_BIT_MASK(32)) + return B43_DMA_32BIT; + if (dmamask == DMA_BIT_MASK(64)) + return B43_DMA_64BIT; + B43_WARN_ON(1); + return B43_DMA_30BIT; +} + +/* Main initialization function. */ +static +struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, + int controller_index, + int for_tx, + enum b43_dmatype type) +{ + struct b43_dmaring *ring; + int i, err; + dma_addr_t dma_test; + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) + goto out; + + ring->nr_slots = B43_RXRING_SLOTS; + if (for_tx) + ring->nr_slots = B43_TXRING_SLOTS; + + ring->meta = kcalloc(ring->nr_slots, sizeof(struct b43_dmadesc_meta), + GFP_KERNEL); + if (!ring->meta) + goto err_kfree_ring; + for (i = 0; i < ring->nr_slots; i++) + ring->meta->skb = B43_DMA_PTR_POISON; + + ring->type = type; + ring->dev = dev; + ring->mmio_base = b43_dmacontroller_base(type, controller_index); + ring->index = controller_index; + if (type == B43_DMA_64BIT) + ring->ops = &dma64_ops; + else + ring->ops = &dma32_ops; + if (for_tx) { + ring->tx = true; + ring->current_slot = -1; + } else { + if (ring->index == 0) { + switch (dev->fw.hdr_format) { + case B43_FW_HDR_598: + ring->rx_buffersize = B43_DMA0_RX_FW598_BUFSIZE; + ring->frameoffset = B43_DMA0_RX_FW598_FO; + break; + case B43_FW_HDR_410: + case B43_FW_HDR_351: + ring->rx_buffersize = B43_DMA0_RX_FW351_BUFSIZE; + ring->frameoffset = B43_DMA0_RX_FW351_FO; + break; + } + } else + B43_WARN_ON(1); + } +#ifdef CONFIG_B43_DEBUG + ring->last_injected_overflow = jiffies; +#endif + + if (for_tx) { + /* Assumption: B43_TXRING_SLOTS can be divided by TX_SLOTS_PER_FRAME */ + BUILD_BUG_ON(B43_TXRING_SLOTS % TX_SLOTS_PER_FRAME != 0); + + ring->txhdr_cache = kcalloc(ring->nr_slots / TX_SLOTS_PER_FRAME, + b43_txhdr_size(dev), + GFP_KERNEL); + if (!ring->txhdr_cache) + goto err_kfree_meta; + + /* test for ability to dma to txhdr_cache */ + dma_test = dma_map_single(dev->dev->dma_dev, + ring->txhdr_cache, + b43_txhdr_size(dev), + DMA_TO_DEVICE); + + if (b43_dma_mapping_error(ring, dma_test, + b43_txhdr_size(dev), 1)) { + /* ugh realloc */ + kfree(ring->txhdr_cache); + ring->txhdr_cache = kcalloc(ring->nr_slots / TX_SLOTS_PER_FRAME, + b43_txhdr_size(dev), + GFP_KERNEL | GFP_DMA); + if (!ring->txhdr_cache) + goto err_kfree_meta; + + dma_test = dma_map_single(dev->dev->dma_dev, + ring->txhdr_cache, + b43_txhdr_size(dev), + DMA_TO_DEVICE); + + if (b43_dma_mapping_error(ring, dma_test, + b43_txhdr_size(dev), 1)) { + + b43err(dev->wl, + "TXHDR DMA allocation failed\n"); + goto err_kfree_txhdr_cache; + } + } + + dma_unmap_single(dev->dev->dma_dev, + dma_test, b43_txhdr_size(dev), + DMA_TO_DEVICE); + } + + err = alloc_ringmemory(ring); + if (err) + goto err_kfree_txhdr_cache; + err = dmacontroller_setup(ring); + if (err) + goto err_free_ringmemory; + + out: + return ring; + + err_free_ringmemory: + free_ringmemory(ring); + err_kfree_txhdr_cache: + kfree(ring->txhdr_cache); + err_kfree_meta: + kfree(ring->meta); + err_kfree_ring: + kfree(ring); + ring = NULL; + goto out; +} + +#define divide(a, b) ({ \ + typeof(a) __a = a; \ + do_div(__a, b); \ + __a; \ + }) + +#define modulo(a, b) ({ \ + typeof(a) __a = a; \ + do_div(__a, b); \ + }) + +/* Main cleanup function. */ +static void b43_destroy_dmaring(struct b43_dmaring *ring, + const char *ringname) +{ + if (!ring) + return; + +#ifdef CONFIG_B43_DEBUG + { + /* Print some statistics. */ + u64 failed_packets = ring->nr_failed_tx_packets; + u64 succeed_packets = ring->nr_succeed_tx_packets; + u64 nr_packets = failed_packets + succeed_packets; + u64 permille_failed = 0, average_tries = 0; + + if (nr_packets) + permille_failed = divide(failed_packets * 1000, nr_packets); + if (nr_packets) + average_tries = divide(ring->nr_total_packet_tries * 100, nr_packets); + + b43dbg(ring->dev->wl, "DMA-%u %s: " + "Used slots %d/%d, Failed frames %llu/%llu = %llu.%01llu%%, " + "Average tries %llu.%02llu\n", + (unsigned int)(ring->type), ringname, + ring->max_used_slots, + ring->nr_slots, + (unsigned long long)failed_packets, + (unsigned long long)nr_packets, + (unsigned long long)divide(permille_failed, 10), + (unsigned long long)modulo(permille_failed, 10), + (unsigned long long)divide(average_tries, 100), + (unsigned long long)modulo(average_tries, 100)); + } +#endif /* DEBUG */ + + /* Device IRQs are disabled prior entering this function, + * so no need to take care of concurrency with rx handler stuff. + */ + dmacontroller_cleanup(ring); + free_all_descbuffers(ring); + free_ringmemory(ring); + + kfree(ring->txhdr_cache); + kfree(ring->meta); + kfree(ring); +} + +#define destroy_ring(dma, ring) do { \ + b43_destroy_dmaring((dma)->ring, __stringify(ring)); \ + (dma)->ring = NULL; \ + } while (0) + +void b43_dma_free(struct b43_wldev *dev) +{ + struct b43_dma *dma; + + if (b43_using_pio_transfers(dev)) + return; + dma = &dev->dma; + + destroy_ring(dma, rx_ring); + destroy_ring(dma, tx_ring_AC_BK); + destroy_ring(dma, tx_ring_AC_BE); + destroy_ring(dma, tx_ring_AC_VI); + destroy_ring(dma, tx_ring_AC_VO); + destroy_ring(dma, tx_ring_mcast); +} + +static int b43_dma_set_mask(struct b43_wldev *dev, u64 mask) +{ + u64 orig_mask = mask; + bool fallback = false; + int err; + + /* Try to set the DMA mask. If it fails, try falling back to a + * lower mask, as we can always also support a lower one. */ + while (1) { + err = dma_set_mask_and_coherent(dev->dev->dma_dev, mask); + if (!err) + break; + if (mask == DMA_BIT_MASK(64)) { + mask = DMA_BIT_MASK(32); + fallback = true; + continue; + } + if (mask == DMA_BIT_MASK(32)) { + mask = DMA_BIT_MASK(30); + fallback = true; + continue; + } + b43err(dev->wl, "The machine/kernel does not support " + "the required %u-bit DMA mask\n", + (unsigned int)dma_mask_to_engine_type(orig_mask)); + return -EOPNOTSUPP; + } + if (fallback) { + b43info(dev->wl, "DMA mask fallback from %u-bit to %u-bit\n", + (unsigned int)dma_mask_to_engine_type(orig_mask), + (unsigned int)dma_mask_to_engine_type(mask)); + } + + return 0; +} + +/* Some hardware with 64-bit DMA seems to be bugged and looks for translation + * bit in low address word instead of high one. + */ +static bool b43_dma_translation_in_low_word(struct b43_wldev *dev, + enum b43_dmatype type) +{ + if (type != B43_DMA_64BIT) + return true; + +#ifdef CONFIG_B43_SSB + if (dev->dev->bus_type == B43_BUS_SSB && + dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI && + !(pci_is_pcie(dev->dev->sdev->bus->host_pci) && + ssb_read32(dev->dev->sdev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64)) + return true; +#endif + return false; +} + +int b43_dma_init(struct b43_wldev *dev) +{ + struct b43_dma *dma = &dev->dma; + int err; + u64 dmamask; + enum b43_dmatype type; + + dmamask = supported_dma_mask(dev); + type = dma_mask_to_engine_type(dmamask); + err = b43_dma_set_mask(dev, dmamask); + if (err) + return err; + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + dma->translation = bcma_core_dma_translation(dev->dev->bdev); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + dma->translation = ssb_dma_translation(dev->dev->sdev); + break; +#endif + } + dma->translation_in_low = b43_dma_translation_in_low_word(dev, type); + + dma->parity = true; +#ifdef CONFIG_B43_BCMA + /* TODO: find out which SSB devices need disabling parity */ + if (dev->dev->bus_type == B43_BUS_BCMA) + dma->parity = false; +#endif + + err = -ENOMEM; + /* setup TX DMA channels. */ + dma->tx_ring_AC_BK = b43_setup_dmaring(dev, 0, 1, type); + if (!dma->tx_ring_AC_BK) + goto out; + + dma->tx_ring_AC_BE = b43_setup_dmaring(dev, 1, 1, type); + if (!dma->tx_ring_AC_BE) + goto err_destroy_bk; + + dma->tx_ring_AC_VI = b43_setup_dmaring(dev, 2, 1, type); + if (!dma->tx_ring_AC_VI) + goto err_destroy_be; + + dma->tx_ring_AC_VO = b43_setup_dmaring(dev, 3, 1, type); + if (!dma->tx_ring_AC_VO) + goto err_destroy_vi; + + dma->tx_ring_mcast = b43_setup_dmaring(dev, 4, 1, type); + if (!dma->tx_ring_mcast) + goto err_destroy_vo; + + /* setup RX DMA channel. */ + dma->rx_ring = b43_setup_dmaring(dev, 0, 0, type); + if (!dma->rx_ring) + goto err_destroy_mcast; + + /* No support for the TX status DMA ring. */ + B43_WARN_ON(dev->dev->core_rev < 5); + + b43dbg(dev->wl, "%u-bit DMA initialized\n", + (unsigned int)type); + err = 0; +out: + return err; + +err_destroy_mcast: + destroy_ring(dma, tx_ring_mcast); +err_destroy_vo: + destroy_ring(dma, tx_ring_AC_VO); +err_destroy_vi: + destroy_ring(dma, tx_ring_AC_VI); +err_destroy_be: + destroy_ring(dma, tx_ring_AC_BE); +err_destroy_bk: + destroy_ring(dma, tx_ring_AC_BK); + return err; +} + +/* Generate a cookie for the TX header. */ +static u16 generate_cookie(struct b43_dmaring *ring, int slot) +{ + u16 cookie; + + /* Use the upper 4 bits of the cookie as + * DMA controller ID and store the slot number + * in the lower 12 bits. + * Note that the cookie must never be 0, as this + * is a special value used in RX path. + * It can also not be 0xFFFF because that is special + * for multicast frames. + */ + cookie = (((u16)ring->index + 1) << 12); + B43_WARN_ON(slot & ~0x0FFF); + cookie |= (u16)slot; + + return cookie; +} + +/* Inspect a cookie and find out to which controller/slot it belongs. */ +static +struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot) +{ + struct b43_dma *dma = &dev->dma; + struct b43_dmaring *ring = NULL; + + switch (cookie & 0xF000) { + case 0x1000: + ring = dma->tx_ring_AC_BK; + break; + case 0x2000: + ring = dma->tx_ring_AC_BE; + break; + case 0x3000: + ring = dma->tx_ring_AC_VI; + break; + case 0x4000: + ring = dma->tx_ring_AC_VO; + break; + case 0x5000: + ring = dma->tx_ring_mcast; + break; + } + *slot = (cookie & 0x0FFF); + if (unlikely(!ring || *slot < 0 || *slot >= ring->nr_slots)) { + b43dbg(dev->wl, "TX-status contains " + "invalid cookie: 0x%04X\n", cookie); + return NULL; + } + + return ring; +} + +static int dma_tx_fragment(struct b43_dmaring *ring, + struct sk_buff *skb) +{ + const struct b43_dma_ops *ops = ring->ops; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct b43_private_tx_info *priv_info = b43_get_priv_tx_info(info); + u8 *header; + int slot, old_top_slot, old_used_slots; + int err; + struct b43_dmadesc_generic *desc; + struct b43_dmadesc_meta *meta; + struct b43_dmadesc_meta *meta_hdr; + u16 cookie; + size_t hdrsize = b43_txhdr_size(ring->dev); + + /* Important note: If the number of used DMA slots per TX frame + * is changed here, the TX_SLOTS_PER_FRAME definition at the top of + * the file has to be updated, too! + */ + + old_top_slot = ring->current_slot; + old_used_slots = ring->used_slots; + + /* Get a slot for the header. */ + slot = request_slot(ring); + desc = ops->idx2desc(ring, slot, &meta_hdr); + memset(meta_hdr, 0, sizeof(*meta_hdr)); + + header = &(ring->txhdr_cache[(slot / TX_SLOTS_PER_FRAME) * hdrsize]); + cookie = generate_cookie(ring, slot); + err = b43_generate_txhdr(ring->dev, header, + skb, info, cookie); + if (unlikely(err)) { + ring->current_slot = old_top_slot; + ring->used_slots = old_used_slots; + return err; + } + + meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, + hdrsize, 1); + if (b43_dma_mapping_error(ring, meta_hdr->dmaaddr, hdrsize, 1)) { + ring->current_slot = old_top_slot; + ring->used_slots = old_used_slots; + return -EIO; + } + ops->fill_descriptor(ring, desc, meta_hdr->dmaaddr, + hdrsize, 1, 0, 0); + + /* Get a slot for the payload. */ + slot = request_slot(ring); + desc = ops->idx2desc(ring, slot, &meta); + memset(meta, 0, sizeof(*meta)); + + meta->skb = skb; + meta->is_last_fragment = true; + priv_info->bouncebuffer = NULL; + + meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); + /* create a bounce buffer in zone_dma on mapping failure. */ + if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { + priv_info->bouncebuffer = kmemdup(skb->data, skb->len, + GFP_ATOMIC | GFP_DMA); + if (!priv_info->bouncebuffer) { + ring->current_slot = old_top_slot; + ring->used_slots = old_used_slots; + err = -ENOMEM; + goto out_unmap_hdr; + } + + meta->dmaaddr = map_descbuffer(ring, priv_info->bouncebuffer, skb->len, 1); + if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { + kfree(priv_info->bouncebuffer); + priv_info->bouncebuffer = NULL; + ring->current_slot = old_top_slot; + ring->used_slots = old_used_slots; + err = -EIO; + goto out_unmap_hdr; + } + } + + ops->fill_descriptor(ring, desc, meta->dmaaddr, skb->len, 0, 1, 1); + + if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { + /* Tell the firmware about the cookie of the last + * mcast frame, so it can clear the more-data bit in it. */ + b43_shm_write16(ring->dev, B43_SHM_SHARED, + B43_SHM_SH_MCASTCOOKIE, cookie); + } + /* Now transfer the whole frame. */ + wmb(); + ops->poke_tx(ring, next_slot(ring, slot)); + return 0; + +out_unmap_hdr: + unmap_descbuffer(ring, meta_hdr->dmaaddr, + hdrsize, 1); + return err; +} + +static inline int should_inject_overflow(struct b43_dmaring *ring) +{ +#ifdef CONFIG_B43_DEBUG + if (unlikely(b43_debug(ring->dev, B43_DBG_DMAOVERFLOW))) { + /* Check if we should inject another ringbuffer overflow + * to test handling of this situation in the stack. */ + unsigned long next_overflow; + + next_overflow = ring->last_injected_overflow + HZ; + if (time_after(jiffies, next_overflow)) { + ring->last_injected_overflow = jiffies; + b43dbg(ring->dev->wl, + "Injecting TX ring overflow on " + "DMA controller %d\n", ring->index); + return 1; + } + } +#endif /* CONFIG_B43_DEBUG */ + return 0; +} + +/* Static mapping of mac80211's queues (priorities) to b43 DMA rings. */ +static struct b43_dmaring *select_ring_by_priority(struct b43_wldev *dev, + u8 queue_prio) +{ + struct b43_dmaring *ring; + + if (dev->qos_enabled) { + /* 0 = highest priority */ + switch (queue_prio) { + default: + B43_WARN_ON(1); + /* fallthrough */ + case 0: + ring = dev->dma.tx_ring_AC_VO; + break; + case 1: + ring = dev->dma.tx_ring_AC_VI; + break; + case 2: + ring = dev->dma.tx_ring_AC_BE; + break; + case 3: + ring = dev->dma.tx_ring_AC_BK; + break; + } + } else + ring = dev->dma.tx_ring_AC_BE; + + return ring; +} + +int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb) +{ + struct b43_dmaring *ring; + struct ieee80211_hdr *hdr; + int err = 0; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + hdr = (struct ieee80211_hdr *)skb->data; + if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { + /* The multicast ring will be sent after the DTIM */ + ring = dev->dma.tx_ring_mcast; + /* Set the more-data bit. Ucode will clear it on + * the last frame for us. */ + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } else { + /* Decide by priority where to put this frame. */ + ring = select_ring_by_priority( + dev, skb_get_queue_mapping(skb)); + } + + B43_WARN_ON(!ring->tx); + + if (unlikely(ring->stopped)) { + /* We get here only because of a bug in mac80211. + * Because of a race, one packet may be queued after + * the queue is stopped, thus we got called when we shouldn't. + * For now, just refuse the transmit. */ + if (b43_debug(dev, B43_DBG_DMAVERBOSE)) + b43err(dev->wl, "Packet after queue stopped\n"); + err = -ENOSPC; + goto out; + } + + if (unlikely(WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME))) { + /* If we get here, we have a real error with the queue + * full, but queues not stopped. */ + b43err(dev->wl, "DMA queue overflow\n"); + err = -ENOSPC; + goto out; + } + + /* Assign the queue number to the ring (if not already done before) + * so TX status handling can use it. The queue to ring mapping is + * static, so we don't need to store it per frame. */ + ring->queue_prio = skb_get_queue_mapping(skb); + + err = dma_tx_fragment(ring, skb); + if (unlikely(err == -ENOKEY)) { + /* Drop this packet, as we don't have the encryption key + * anymore and must not transmit it unencrypted. */ + ieee80211_free_txskb(dev->wl->hw, skb); + err = 0; + goto out; + } + if (unlikely(err)) { + b43err(dev->wl, "DMA tx mapping failure\n"); + goto out; + } + if ((free_slots(ring) < TX_SLOTS_PER_FRAME) || + should_inject_overflow(ring)) { + /* This TX ring is full. */ + unsigned int skb_mapping = skb_get_queue_mapping(skb); + ieee80211_stop_queue(dev->wl->hw, skb_mapping); + dev->wl->tx_queue_stopped[skb_mapping] = 1; + ring->stopped = true; + if (b43_debug(dev, B43_DBG_DMAVERBOSE)) { + b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index); + } + } +out: + + return err; +} + +void b43_dma_handle_txstatus(struct b43_wldev *dev, + const struct b43_txstatus *status) +{ + const struct b43_dma_ops *ops; + struct b43_dmaring *ring; + struct b43_dmadesc_meta *meta; + static const struct b43_txstatus fake; /* filled with 0 */ + const struct b43_txstatus *txstat; + int slot, firstused; + bool frame_succeed; + int skip; + static u8 err_out1, err_out2; + + ring = parse_cookie(dev, status->cookie, &slot); + if (unlikely(!ring)) + return; + B43_WARN_ON(!ring->tx); + + /* Sanity check: TX packets are processed in-order on one ring. + * Check if the slot deduced from the cookie really is the first + * used slot. */ + firstused = ring->current_slot - ring->used_slots + 1; + if (firstused < 0) + firstused = ring->nr_slots + firstused; + + skip = 0; + if (unlikely(slot != firstused)) { + /* This possibly is a firmware bug and will result in + * malfunction, memory leaks and/or stall of DMA functionality. + */ + if (slot == next_slot(ring, next_slot(ring, firstused))) { + /* If a single header/data pair was missed, skip over + * the first two slots in an attempt to recover. + */ + slot = firstused; + skip = 2; + if (!err_out1) { + /* Report the error once. */ + b43dbg(dev->wl, + "Skip on DMA ring %d slot %d.\n", + ring->index, slot); + err_out1 = 1; + } + } else { + /* More than a single header/data pair were missed. + * Report this error once. + */ + if (!err_out2) + b43dbg(dev->wl, + "Out of order TX status report on DMA ring %d. Expected %d, but got %d\n", + ring->index, firstused, slot); + err_out2 = 1; + return; + } + } + + ops = ring->ops; + while (1) { + B43_WARN_ON(slot < 0 || slot >= ring->nr_slots); + /* get meta - ignore returned value */ + ops->idx2desc(ring, slot, &meta); + + if (b43_dma_ptr_is_poisoned(meta->skb)) { + b43dbg(dev->wl, "Poisoned TX slot %d (first=%d) " + "on ring %d\n", + slot, firstused, ring->index); + break; + } + + if (meta->skb) { + struct b43_private_tx_info *priv_info = + b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb)); + + unmap_descbuffer(ring, meta->dmaaddr, + meta->skb->len, 1); + kfree(priv_info->bouncebuffer); + priv_info->bouncebuffer = NULL; + } else { + unmap_descbuffer(ring, meta->dmaaddr, + b43_txhdr_size(dev), 1); + } + + if (meta->is_last_fragment) { + struct ieee80211_tx_info *info; + + if (unlikely(!meta->skb)) { + /* This is a scatter-gather fragment of a frame, + * so the skb pointer must not be NULL. + */ + b43dbg(dev->wl, "TX status unexpected NULL skb " + "at slot %d (first=%d) on ring %d\n", + slot, firstused, ring->index); + break; + } + + info = IEEE80211_SKB_CB(meta->skb); + + /* + * Call back to inform the ieee80211 subsystem about + * the status of the transmission. When skipping over + * a missed TX status report, use a status structure + * filled with zeros to indicate that the frame was not + * sent (frame_count 0) and not acknowledged + */ + if (unlikely(skip)) + txstat = &fake; + else + txstat = status; + + frame_succeed = b43_fill_txstatus_report(dev, info, + txstat); +#ifdef CONFIG_B43_DEBUG + if (frame_succeed) + ring->nr_succeed_tx_packets++; + else + ring->nr_failed_tx_packets++; + ring->nr_total_packet_tries += status->frame_count; +#endif /* DEBUG */ + ieee80211_tx_status(dev->wl->hw, meta->skb); + + /* skb will be freed by ieee80211_tx_status(). + * Poison our pointer. */ + meta->skb = B43_DMA_PTR_POISON; + } else { + /* No need to call free_descriptor_buffer here, as + * this is only the txhdr, which is not allocated. + */ + if (unlikely(meta->skb)) { + b43dbg(dev->wl, "TX status unexpected non-NULL skb " + "at slot %d (first=%d) on ring %d\n", + slot, firstused, ring->index); + break; + } + } + + /* Everything unmapped and free'd. So it's not used anymore. */ + ring->used_slots--; + + if (meta->is_last_fragment && !skip) { + /* This is the last scatter-gather + * fragment of the frame. We are done. */ + break; + } + slot = next_slot(ring, slot); + if (skip > 0) + --skip; + } + if (ring->stopped) { + B43_WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME); + ring->stopped = false; + } + + if (dev->wl->tx_queue_stopped[ring->queue_prio]) { + dev->wl->tx_queue_stopped[ring->queue_prio] = 0; + } else { + /* If the driver queue is running wake the corresponding + * mac80211 queue. */ + ieee80211_wake_queue(dev->wl->hw, ring->queue_prio); + if (b43_debug(dev, B43_DBG_DMAVERBOSE)) { + b43dbg(dev->wl, "Woke up TX ring %d\n", ring->index); + } + } + /* Add work to the queue. */ + ieee80211_queue_work(dev->wl->hw, &dev->wl->tx_work); +} + +static void dma_rx(struct b43_dmaring *ring, int *slot) +{ + const struct b43_dma_ops *ops = ring->ops; + struct b43_dmadesc_generic *desc; + struct b43_dmadesc_meta *meta; + struct b43_rxhdr_fw4 *rxhdr; + struct sk_buff *skb; + u16 len; + int err; + dma_addr_t dmaaddr; + + desc = ops->idx2desc(ring, *slot, &meta); + + sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); + skb = meta->skb; + + rxhdr = (struct b43_rxhdr_fw4 *)skb->data; + len = le16_to_cpu(rxhdr->frame_len); + if (len == 0) { + int i = 0; + + do { + udelay(2); + barrier(); + len = le16_to_cpu(rxhdr->frame_len); + } while (len == 0 && i++ < 5); + if (unlikely(len == 0)) { + dmaaddr = meta->dmaaddr; + goto drop_recycle_buffer; + } + } + if (unlikely(b43_rx_buffer_is_poisoned(ring, skb))) { + /* Something went wrong with the DMA. + * The device did not touch the buffer and did not overwrite the poison. */ + b43dbg(ring->dev->wl, "DMA RX: Dropping poisoned buffer.\n"); + dmaaddr = meta->dmaaddr; + goto drop_recycle_buffer; + } + if (unlikely(len + ring->frameoffset > ring->rx_buffersize)) { + /* The data did not fit into one descriptor buffer + * and is split over multiple buffers. + * This should never happen, as we try to allocate buffers + * big enough. So simply ignore this packet. + */ + int cnt = 0; + s32 tmp = len; + + while (1) { + desc = ops->idx2desc(ring, *slot, &meta); + /* recycle the descriptor buffer. */ + b43_poison_rx_buffer(ring, meta->skb); + sync_descbuffer_for_device(ring, meta->dmaaddr, + ring->rx_buffersize); + *slot = next_slot(ring, *slot); + cnt++; + tmp -= ring->rx_buffersize; + if (tmp <= 0) + break; + } + b43err(ring->dev->wl, "DMA RX buffer too small " + "(len: %u, buffer: %u, nr-dropped: %d)\n", + len, ring->rx_buffersize, cnt); + goto drop; + } + + dmaaddr = meta->dmaaddr; + err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); + if (unlikely(err)) { + b43dbg(ring->dev->wl, "DMA RX: setup_rx_descbuffer() failed\n"); + goto drop_recycle_buffer; + } + + unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); + skb_put(skb, len + ring->frameoffset); + skb_pull(skb, ring->frameoffset); + + b43_rx(ring->dev, skb, rxhdr); +drop: + return; + +drop_recycle_buffer: + /* Poison and recycle the RX buffer. */ + b43_poison_rx_buffer(ring, skb); + sync_descbuffer_for_device(ring, dmaaddr, ring->rx_buffersize); +} + +void b43_dma_handle_rx_overflow(struct b43_dmaring *ring) +{ + int current_slot, previous_slot; + + B43_WARN_ON(ring->tx); + + /* Device has filled all buffers, drop all packets and let TCP + * decrease speed. + * Decrement RX index by one will let the device to see all slots + * as free again + */ + /* + *TODO: How to increase rx_drop in mac80211? + */ + current_slot = ring->ops->get_current_rxslot(ring); + previous_slot = prev_slot(ring, current_slot); + ring->ops->set_current_rxslot(ring, previous_slot); +} + +void b43_dma_rx(struct b43_dmaring *ring) +{ + const struct b43_dma_ops *ops = ring->ops; + int slot, current_slot; + int used_slots = 0; + + B43_WARN_ON(ring->tx); + current_slot = ops->get_current_rxslot(ring); + B43_WARN_ON(!(current_slot >= 0 && current_slot < ring->nr_slots)); + + slot = ring->current_slot; + for (; slot != current_slot; slot = next_slot(ring, slot)) { + dma_rx(ring, &slot); + update_max_used_slots(ring, ++used_slots); + } + wmb(); + ops->set_current_rxslot(ring, slot); + ring->current_slot = slot; +} + +static void b43_dma_tx_suspend_ring(struct b43_dmaring *ring) +{ + B43_WARN_ON(!ring->tx); + ring->ops->tx_suspend(ring); +} + +static void b43_dma_tx_resume_ring(struct b43_dmaring *ring) +{ + B43_WARN_ON(!ring->tx); + ring->ops->tx_resume(ring); +} + +void b43_dma_tx_suspend(struct b43_wldev *dev) +{ + b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); + b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BK); + b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BE); + b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VI); + b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VO); + b43_dma_tx_suspend_ring(dev->dma.tx_ring_mcast); +} + +void b43_dma_tx_resume(struct b43_wldev *dev) +{ + b43_dma_tx_resume_ring(dev->dma.tx_ring_mcast); + b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VO); + b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VI); + b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BE); + b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BK); + b43_power_saving_ctl_bits(dev, 0); +} + +static void direct_fifo_rx(struct b43_wldev *dev, enum b43_dmatype type, + u16 mmio_base, bool enable) +{ + u32 ctl; + + if (type == B43_DMA_64BIT) { + ctl = b43_read32(dev, mmio_base + B43_DMA64_RXCTL); + ctl &= ~B43_DMA64_RXDIRECTFIFO; + if (enable) + ctl |= B43_DMA64_RXDIRECTFIFO; + b43_write32(dev, mmio_base + B43_DMA64_RXCTL, ctl); + } else { + ctl = b43_read32(dev, mmio_base + B43_DMA32_RXCTL); + ctl &= ~B43_DMA32_RXDIRECTFIFO; + if (enable) + ctl |= B43_DMA32_RXDIRECTFIFO; + b43_write32(dev, mmio_base + B43_DMA32_RXCTL, ctl); + } +} + +/* Enable/Disable Direct FIFO Receive Mode (PIO) on a RX engine. + * This is called from PIO code, so DMA structures are not available. */ +void b43_dma_direct_fifo_rx(struct b43_wldev *dev, + unsigned int engine_index, bool enable) +{ + enum b43_dmatype type; + u16 mmio_base; + + type = dma_mask_to_engine_type(supported_dma_mask(dev)); + + mmio_base = b43_dmacontroller_base(type, engine_index); + direct_fifo_rx(dev, type, mmio_base, enable); +} diff --git a/drivers/net/wireless/broadcom/b43/dma.h b/drivers/net/wireless/broadcom/b43/dma.h new file mode 100644 index 000000000000..df8c8cdcbdb5 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/dma.h @@ -0,0 +1,305 @@ +#ifndef B43_DMA_H_ +#define B43_DMA_H_ + +#include + +#include "b43.h" + + +/* DMA-Interrupt reasons. */ +#define B43_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \ + | (1 << 14) | (1 << 15)) +#define B43_DMAIRQ_RDESC_UFLOW (1 << 13) +#define B43_DMAIRQ_RX_DONE (1 << 16) + +/*** 32-bit DMA Engine. ***/ + +/* 32-bit DMA controller registers. */ +#define B43_DMA32_TXCTL 0x00 +#define B43_DMA32_TXENABLE 0x00000001 +#define B43_DMA32_TXSUSPEND 0x00000002 +#define B43_DMA32_TXLOOPBACK 0x00000004 +#define B43_DMA32_TXFLUSH 0x00000010 +#define B43_DMA32_TXPARITYDISABLE 0x00000800 +#define B43_DMA32_TXADDREXT_MASK 0x00030000 +#define B43_DMA32_TXADDREXT_SHIFT 16 +#define B43_DMA32_TXRING 0x04 +#define B43_DMA32_TXINDEX 0x08 +#define B43_DMA32_TXSTATUS 0x0C +#define B43_DMA32_TXDPTR 0x00000FFF +#define B43_DMA32_TXSTATE 0x0000F000 +#define B43_DMA32_TXSTAT_DISABLED 0x00000000 +#define B43_DMA32_TXSTAT_ACTIVE 0x00001000 +#define B43_DMA32_TXSTAT_IDLEWAIT 0x00002000 +#define B43_DMA32_TXSTAT_STOPPED 0x00003000 +#define B43_DMA32_TXSTAT_SUSP 0x00004000 +#define B43_DMA32_TXERROR 0x000F0000 +#define B43_DMA32_TXERR_NOERR 0x00000000 +#define B43_DMA32_TXERR_PROT 0x00010000 +#define B43_DMA32_TXERR_UNDERRUN 0x00020000 +#define B43_DMA32_TXERR_BUFREAD 0x00030000 +#define B43_DMA32_TXERR_DESCREAD 0x00040000 +#define B43_DMA32_TXACTIVE 0xFFF00000 +#define B43_DMA32_RXCTL 0x10 +#define B43_DMA32_RXENABLE 0x00000001 +#define B43_DMA32_RXFROFF_MASK 0x000000FE +#define B43_DMA32_RXFROFF_SHIFT 1 +#define B43_DMA32_RXDIRECTFIFO 0x00000100 +#define B43_DMA32_RXPARITYDISABLE 0x00000800 +#define B43_DMA32_RXADDREXT_MASK 0x00030000 +#define B43_DMA32_RXADDREXT_SHIFT 16 +#define B43_DMA32_RXRING 0x14 +#define B43_DMA32_RXINDEX 0x18 +#define B43_DMA32_RXSTATUS 0x1C +#define B43_DMA32_RXDPTR 0x00000FFF +#define B43_DMA32_RXSTATE 0x0000F000 +#define B43_DMA32_RXSTAT_DISABLED 0x00000000 +#define B43_DMA32_RXSTAT_ACTIVE 0x00001000 +#define B43_DMA32_RXSTAT_IDLEWAIT 0x00002000 +#define B43_DMA32_RXSTAT_STOPPED 0x00003000 +#define B43_DMA32_RXERROR 0x000F0000 +#define B43_DMA32_RXERR_NOERR 0x00000000 +#define B43_DMA32_RXERR_PROT 0x00010000 +#define B43_DMA32_RXERR_OVERFLOW 0x00020000 +#define B43_DMA32_RXERR_BUFWRITE 0x00030000 +#define B43_DMA32_RXERR_DESCREAD 0x00040000 +#define B43_DMA32_RXACTIVE 0xFFF00000 + +/* 32-bit DMA descriptor. */ +struct b43_dmadesc32 { + __le32 control; + __le32 address; +} __packed; +#define B43_DMA32_DCTL_BYTECNT 0x00001FFF +#define B43_DMA32_DCTL_ADDREXT_MASK 0x00030000 +#define B43_DMA32_DCTL_ADDREXT_SHIFT 16 +#define B43_DMA32_DCTL_DTABLEEND 0x10000000 +#define B43_DMA32_DCTL_IRQ 0x20000000 +#define B43_DMA32_DCTL_FRAMEEND 0x40000000 +#define B43_DMA32_DCTL_FRAMESTART 0x80000000 + +/*** 64-bit DMA Engine. ***/ + +/* 64-bit DMA controller registers. */ +#define B43_DMA64_TXCTL 0x00 +#define B43_DMA64_TXENABLE 0x00000001 +#define B43_DMA64_TXSUSPEND 0x00000002 +#define B43_DMA64_TXLOOPBACK 0x00000004 +#define B43_DMA64_TXFLUSH 0x00000010 +#define B43_DMA64_TXPARITYDISABLE 0x00000800 +#define B43_DMA64_TXADDREXT_MASK 0x00030000 +#define B43_DMA64_TXADDREXT_SHIFT 16 +#define B43_DMA64_TXINDEX 0x04 +#define B43_DMA64_TXRINGLO 0x08 +#define B43_DMA64_TXRINGHI 0x0C +#define B43_DMA64_TXSTATUS 0x10 +#define B43_DMA64_TXSTATDPTR 0x00001FFF +#define B43_DMA64_TXSTAT 0xF0000000 +#define B43_DMA64_TXSTAT_DISABLED 0x00000000 +#define B43_DMA64_TXSTAT_ACTIVE 0x10000000 +#define B43_DMA64_TXSTAT_IDLEWAIT 0x20000000 +#define B43_DMA64_TXSTAT_STOPPED 0x30000000 +#define B43_DMA64_TXSTAT_SUSP 0x40000000 +#define B43_DMA64_TXERROR 0x14 +#define B43_DMA64_TXERRDPTR 0x0001FFFF +#define B43_DMA64_TXERR 0xF0000000 +#define B43_DMA64_TXERR_NOERR 0x00000000 +#define B43_DMA64_TXERR_PROT 0x10000000 +#define B43_DMA64_TXERR_UNDERRUN 0x20000000 +#define B43_DMA64_TXERR_TRANSFER 0x30000000 +#define B43_DMA64_TXERR_DESCREAD 0x40000000 +#define B43_DMA64_TXERR_CORE 0x50000000 +#define B43_DMA64_RXCTL 0x20 +#define B43_DMA64_RXENABLE 0x00000001 +#define B43_DMA64_RXFROFF_MASK 0x000000FE +#define B43_DMA64_RXFROFF_SHIFT 1 +#define B43_DMA64_RXDIRECTFIFO 0x00000100 +#define B43_DMA64_RXPARITYDISABLE 0x00000800 +#define B43_DMA64_RXADDREXT_MASK 0x00030000 +#define B43_DMA64_RXADDREXT_SHIFT 16 +#define B43_DMA64_RXINDEX 0x24 +#define B43_DMA64_RXRINGLO 0x28 +#define B43_DMA64_RXRINGHI 0x2C +#define B43_DMA64_RXSTATUS 0x30 +#define B43_DMA64_RXSTATDPTR 0x00001FFF +#define B43_DMA64_RXSTAT 0xF0000000 +#define B43_DMA64_RXSTAT_DISABLED 0x00000000 +#define B43_DMA64_RXSTAT_ACTIVE 0x10000000 +#define B43_DMA64_RXSTAT_IDLEWAIT 0x20000000 +#define B43_DMA64_RXSTAT_STOPPED 0x30000000 +#define B43_DMA64_RXSTAT_SUSP 0x40000000 +#define B43_DMA64_RXERROR 0x34 +#define B43_DMA64_RXERRDPTR 0x0001FFFF +#define B43_DMA64_RXERR 0xF0000000 +#define B43_DMA64_RXERR_NOERR 0x00000000 +#define B43_DMA64_RXERR_PROT 0x10000000 +#define B43_DMA64_RXERR_UNDERRUN 0x20000000 +#define B43_DMA64_RXERR_TRANSFER 0x30000000 +#define B43_DMA64_RXERR_DESCREAD 0x40000000 +#define B43_DMA64_RXERR_CORE 0x50000000 + +/* 64-bit DMA descriptor. */ +struct b43_dmadesc64 { + __le32 control0; + __le32 control1; + __le32 address_low; + __le32 address_high; +} __packed; +#define B43_DMA64_DCTL0_DTABLEEND 0x10000000 +#define B43_DMA64_DCTL0_IRQ 0x20000000 +#define B43_DMA64_DCTL0_FRAMEEND 0x40000000 +#define B43_DMA64_DCTL0_FRAMESTART 0x80000000 +#define B43_DMA64_DCTL1_BYTECNT 0x00001FFF +#define B43_DMA64_DCTL1_ADDREXT_MASK 0x00030000 +#define B43_DMA64_DCTL1_ADDREXT_SHIFT 16 + +struct b43_dmadesc_generic { + union { + struct b43_dmadesc32 dma32; + struct b43_dmadesc64 dma64; + } __packed; +} __packed; + +/* Misc DMA constants */ +#define B43_DMA32_RINGMEMSIZE 4096 +#define B43_DMA64_RINGMEMSIZE 8192 +/* Offset of frame with actual data */ +#define B43_DMA0_RX_FW598_FO 38 +#define B43_DMA0_RX_FW351_FO 30 + +/* DMA engine tuning knobs */ +#define B43_TXRING_SLOTS 256 +#define B43_RXRING_SLOTS 256 +#define B43_DMA0_RX_FW598_BUFSIZE (B43_DMA0_RX_FW598_FO + IEEE80211_MAX_FRAME_LEN) +#define B43_DMA0_RX_FW351_BUFSIZE (B43_DMA0_RX_FW351_FO + IEEE80211_MAX_FRAME_LEN) + +/* Pointer poison */ +#define B43_DMA_PTR_POISON ((void *)ERR_PTR(-ENOMEM)) +#define b43_dma_ptr_is_poisoned(ptr) (unlikely((ptr) == B43_DMA_PTR_POISON)) + + +struct sk_buff; +struct b43_private; +struct b43_txstatus; + +struct b43_dmadesc_meta { + /* The kernel DMA-able buffer. */ + struct sk_buff *skb; + /* DMA base bus-address of the descriptor buffer. */ + dma_addr_t dmaaddr; + /* ieee80211 TX status. Only used once per 802.11 frag. */ + bool is_last_fragment; +}; + +struct b43_dmaring; + +/* Lowlevel DMA operations that differ between 32bit and 64bit DMA. */ +struct b43_dma_ops { + struct b43_dmadesc_generic *(*idx2desc) (struct b43_dmaring * ring, + int slot, + struct b43_dmadesc_meta ** + meta); + void (*fill_descriptor) (struct b43_dmaring * ring, + struct b43_dmadesc_generic * desc, + dma_addr_t dmaaddr, u16 bufsize, int start, + int end, int irq); + void (*poke_tx) (struct b43_dmaring * ring, int slot); + void (*tx_suspend) (struct b43_dmaring * ring); + void (*tx_resume) (struct b43_dmaring * ring); + int (*get_current_rxslot) (struct b43_dmaring * ring); + void (*set_current_rxslot) (struct b43_dmaring * ring, int slot); +}; + +enum b43_dmatype { + B43_DMA_30BIT = 30, + B43_DMA_32BIT = 32, + B43_DMA_64BIT = 64, +}; + +enum b43_addrtype { + B43_DMA_ADDR_LOW, + B43_DMA_ADDR_HIGH, + B43_DMA_ADDR_EXT, +}; + +struct b43_dmaring { + /* Lowlevel DMA ops. */ + const struct b43_dma_ops *ops; + /* Kernel virtual base address of the ring memory. */ + void *descbase; + /* Meta data about all descriptors. */ + struct b43_dmadesc_meta *meta; + /* Cache of TX headers for each TX frame. + * This is to avoid an allocation on each TX. + * This is NULL for an RX ring. + */ + u8 *txhdr_cache; + /* (Unadjusted) DMA base bus-address of the ring memory. */ + dma_addr_t dmabase; + /* Number of descriptor slots in the ring. */ + int nr_slots; + /* Number of used descriptor slots. */ + int used_slots; + /* Currently used slot in the ring. */ + int current_slot; + /* Frameoffset in octets. */ + u32 frameoffset; + /* Descriptor buffer size. */ + u16 rx_buffersize; + /* The MMIO base register of the DMA controller. */ + u16 mmio_base; + /* DMA controller index number (0-5). */ + int index; + /* Boolean. Is this a TX ring? */ + bool tx; + /* The type of DMA engine used. */ + enum b43_dmatype type; + /* Boolean. Is this ring stopped at ieee80211 level? */ + bool stopped; + /* The QOS priority assigned to this ring. Only used for TX rings. + * This is the mac80211 "queue" value. */ + u8 queue_prio; + struct b43_wldev *dev; +#ifdef CONFIG_B43_DEBUG + /* Maximum number of used slots. */ + int max_used_slots; + /* Last time we injected a ring overflow. */ + unsigned long last_injected_overflow; + /* Statistics: Number of successfully transmitted packets */ + u64 nr_succeed_tx_packets; + /* Statistics: Number of failed TX packets */ + u64 nr_failed_tx_packets; + /* Statistics: Total number of TX plus all retries. */ + u64 nr_total_packet_tries; +#endif /* CONFIG_B43_DEBUG */ +}; + +static inline u32 b43_dma_read(struct b43_dmaring *ring, u16 offset) +{ + return b43_read32(ring->dev, ring->mmio_base + offset); +} + +static inline void b43_dma_write(struct b43_dmaring *ring, u16 offset, u32 value) +{ + b43_write32(ring->dev, ring->mmio_base + offset, value); +} + +int b43_dma_init(struct b43_wldev *dev); +void b43_dma_free(struct b43_wldev *dev); + +void b43_dma_tx_suspend(struct b43_wldev *dev); +void b43_dma_tx_resume(struct b43_wldev *dev); + +int b43_dma_tx(struct b43_wldev *dev, + struct sk_buff *skb); +void b43_dma_handle_txstatus(struct b43_wldev *dev, + const struct b43_txstatus *status); + +void b43_dma_handle_rx_overflow(struct b43_dmaring *ring); + +void b43_dma_rx(struct b43_dmaring *ring); + +void b43_dma_direct_fifo_rx(struct b43_wldev *dev, + unsigned int engine_index, bool enable); + +#endif /* B43_DMA_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/leds.c b/drivers/net/wireless/broadcom/b43/leds.c new file mode 100644 index 000000000000..d79ab2a227e1 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/leds.c @@ -0,0 +1,359 @@ +/* + + Broadcom B43 wireless driver + LED control + + Copyright (c) 2005 Martin Langer , + Copyright (c) 2005 Stefano Brivio + Copyright (c) 2005-2007 Michael Buesch + Copyright (c) 2005 Danny van Dyk + Copyright (c) 2005 Andreas Jaggi + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "leds.h" +#include "rfkill.h" + + +static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index, + bool activelow) +{ + u16 ctl; + + ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); + if (activelow) + ctl &= ~(1 << led_index); + else + ctl |= (1 << led_index); + b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); +} + +static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index, + bool activelow) +{ + u16 ctl; + + ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); + if (activelow) + ctl |= (1 << led_index); + else + ctl &= ~(1 << led_index); + b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); +} + +static void b43_led_update(struct b43_wldev *dev, + struct b43_led *led) +{ + bool radio_enabled; + bool turn_on; + + if (!led->wl) + return; + + radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable); + + /* The led->state read is racy, but we don't care. In case we raced + * with the brightness_set handler, we will be called again soon + * to fixup our state. */ + if (radio_enabled) + turn_on = atomic_read(&led->state) != LED_OFF; + else + turn_on = false; + if (turn_on == led->hw_state) + return; + led->hw_state = turn_on; + + if (turn_on) + b43_led_turn_on(dev, led->index, led->activelow); + else + b43_led_turn_off(dev, led->index, led->activelow); +} + +static void b43_leds_work(struct work_struct *work) +{ + struct b43_leds *leds = container_of(work, struct b43_leds, work); + struct b43_wl *wl = container_of(leds, struct b43_wl, leds); + struct b43_wldev *dev; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED)) + goto out_unlock; + + b43_led_update(dev, &wl->leds.led_tx); + b43_led_update(dev, &wl->leds.led_rx); + b43_led_update(dev, &wl->leds.led_radio); + b43_led_update(dev, &wl->leds.led_assoc); + +out_unlock: + mutex_unlock(&wl->mutex); +} + +/* Callback from the LED subsystem. */ +static void b43_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct b43_led *led = container_of(led_dev, struct b43_led, led_dev); + struct b43_wl *wl = led->wl; + + if (likely(!wl->leds.stop)) { + atomic_set(&led->state, brightness); + ieee80211_queue_work(wl->hw, &wl->leds.work); + } +} + +static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, + const char *name, const char *default_trigger, + u8 led_index, bool activelow) +{ + int err; + + if (led->wl) + return -EEXIST; + if (!default_trigger) + return -EINVAL; + led->wl = dev->wl; + led->index = led_index; + led->activelow = activelow; + strncpy(led->name, name, sizeof(led->name)); + atomic_set(&led->state, 0); + + led->led_dev.name = led->name; + led->led_dev.default_trigger = default_trigger; + led->led_dev.brightness_set = b43_led_brightness_set; + + err = led_classdev_register(dev->dev->dev, &led->led_dev); + if (err) { + b43warn(dev->wl, "LEDs: Failed to register %s\n", name); + led->wl = NULL; + return err; + } + + return 0; +} + +static void b43_unregister_led(struct b43_led *led) +{ + if (!led->wl) + return; + led_classdev_unregister(&led->led_dev); + led->wl = NULL; +} + +static void b43_map_led(struct b43_wldev *dev, + u8 led_index, + enum b43_led_behaviour behaviour, + bool activelow) +{ + struct ieee80211_hw *hw = dev->wl->hw; + char name[B43_LED_MAX_NAME_LEN + 1]; + + /* Map the b43 specific LED behaviour value to the + * generic LED triggers. */ + switch (behaviour) { + case B43_LED_INACTIVE: + case B43_LED_OFF: + case B43_LED_ON: + break; + case B43_LED_ACTIVITY: + case B43_LED_TRANSFER: + case B43_LED_APTRANSFER: + snprintf(name, sizeof(name), + "b43-%s::tx", wiphy_name(hw->wiphy)); + b43_register_led(dev, &dev->wl->leds.led_tx, name, + ieee80211_get_tx_led_name(hw), + led_index, activelow); + snprintf(name, sizeof(name), + "b43-%s::rx", wiphy_name(hw->wiphy)); + b43_register_led(dev, &dev->wl->leds.led_rx, name, + ieee80211_get_rx_led_name(hw), + led_index, activelow); + break; + case B43_LED_RADIO_ALL: + case B43_LED_RADIO_A: + case B43_LED_RADIO_B: + case B43_LED_MODE_BG: + snprintf(name, sizeof(name), + "b43-%s::radio", wiphy_name(hw->wiphy)); + b43_register_led(dev, &dev->wl->leds.led_radio, name, + ieee80211_get_radio_led_name(hw), + led_index, activelow); + break; + case B43_LED_WEIRD: + case B43_LED_ASSOC: + snprintf(name, sizeof(name), + "b43-%s::assoc", wiphy_name(hw->wiphy)); + b43_register_led(dev, &dev->wl->leds.led_assoc, name, + ieee80211_get_assoc_led_name(hw), + led_index, activelow); + break; + default: + b43warn(dev->wl, "LEDs: Unknown behaviour 0x%02X\n", + behaviour); + break; + } +} + +static void b43_led_get_sprominfo(struct b43_wldev *dev, + unsigned int led_index, + enum b43_led_behaviour *behaviour, + bool *activelow) +{ + u8 sprom[4]; + + sprom[0] = dev->dev->bus_sprom->gpio0; + sprom[1] = dev->dev->bus_sprom->gpio1; + sprom[2] = dev->dev->bus_sprom->gpio2; + sprom[3] = dev->dev->bus_sprom->gpio3; + + if (sprom[led_index] == 0xFF) { + /* There is no LED information in the SPROM + * for this LED. Hardcode it here. */ + *activelow = false; + switch (led_index) { + case 0: + *behaviour = B43_LED_ACTIVITY; + *activelow = true; + if (dev->dev->board_vendor == PCI_VENDOR_ID_COMPAQ) + *behaviour = B43_LED_RADIO_ALL; + break; + case 1: + *behaviour = B43_LED_RADIO_B; + if (dev->dev->board_vendor == PCI_VENDOR_ID_ASUSTEK) + *behaviour = B43_LED_ASSOC; + break; + case 2: + *behaviour = B43_LED_RADIO_A; + break; + case 3: + *behaviour = B43_LED_OFF; + break; + default: + *behaviour = B43_LED_OFF; + B43_WARN_ON(1); + return; + } + } else { + *behaviour = sprom[led_index] & B43_LED_BEHAVIOUR; + *activelow = !!(sprom[led_index] & B43_LED_ACTIVELOW); + } +} + +void b43_leds_init(struct b43_wldev *dev) +{ + struct b43_led *led; + unsigned int i; + enum b43_led_behaviour behaviour; + bool activelow; + + /* Sync the RF-kill LED state (if we have one) with radio and switch states. */ + led = &dev->wl->leds.led_radio; + if (led->wl) { + if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) { + b43_led_turn_on(dev, led->index, led->activelow); + led->hw_state = true; + atomic_set(&led->state, 1); + } else { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = false; + atomic_set(&led->state, 0); + } + } + + /* Initialize TX/RX/ASSOC leds */ + led = &dev->wl->leds.led_tx; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = false; + atomic_set(&led->state, 0); + } + led = &dev->wl->leds.led_rx; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = false; + atomic_set(&led->state, 0); + } + led = &dev->wl->leds.led_assoc; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = false; + atomic_set(&led->state, 0); + } + + /* Initialize other LED states. */ + for (i = 0; i < B43_MAX_NR_LEDS; i++) { + b43_led_get_sprominfo(dev, i, &behaviour, &activelow); + switch (behaviour) { + case B43_LED_OFF: + b43_led_turn_off(dev, i, activelow); + break; + case B43_LED_ON: + b43_led_turn_on(dev, i, activelow); + break; + default: + /* Leave others as-is. */ + break; + } + } + + dev->wl->leds.stop = 0; +} + +void b43_leds_exit(struct b43_wldev *dev) +{ + struct b43_leds *leds = &dev->wl->leds; + + b43_led_turn_off(dev, leds->led_tx.index, leds->led_tx.activelow); + b43_led_turn_off(dev, leds->led_rx.index, leds->led_rx.activelow); + b43_led_turn_off(dev, leds->led_assoc.index, leds->led_assoc.activelow); + b43_led_turn_off(dev, leds->led_radio.index, leds->led_radio.activelow); +} + +void b43_leds_stop(struct b43_wldev *dev) +{ + struct b43_leds *leds = &dev->wl->leds; + + leds->stop = 1; + cancel_work_sync(&leds->work); +} + +void b43_leds_register(struct b43_wldev *dev) +{ + unsigned int i; + enum b43_led_behaviour behaviour; + bool activelow; + + INIT_WORK(&dev->wl->leds.work, b43_leds_work); + + /* Register the LEDs to the LED subsystem. */ + for (i = 0; i < B43_MAX_NR_LEDS; i++) { + b43_led_get_sprominfo(dev, i, &behaviour, &activelow); + b43_map_led(dev, i, behaviour, activelow); + } +} + +void b43_leds_unregister(struct b43_wl *wl) +{ + struct b43_leds *leds = &wl->leds; + + b43_unregister_led(&leds->led_tx); + b43_unregister_led(&leds->led_rx); + b43_unregister_led(&leds->led_assoc); + b43_unregister_led(&leds->led_radio); +} diff --git a/drivers/net/wireless/broadcom/b43/leds.h b/drivers/net/wireless/broadcom/b43/leds.h new file mode 100644 index 000000000000..32b66d53cdac --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/leds.h @@ -0,0 +1,94 @@ +#ifndef B43_LEDS_H_ +#define B43_LEDS_H_ + +struct b43_wl; +struct b43_wldev; + +#ifdef CONFIG_B43_LEDS + +#include +#include +#include + + +#define B43_LED_MAX_NAME_LEN 31 + +struct b43_led { + struct b43_wl *wl; + /* The LED class device */ + struct led_classdev led_dev; + /* The index number of the LED. */ + u8 index; + /* If activelow is true, the LED is ON if the + * bit is switched off. */ + bool activelow; + /* The unique name string for this LED device. */ + char name[B43_LED_MAX_NAME_LEN + 1]; + /* The current status of the LED. This is updated locklessly. */ + atomic_t state; + /* The active state in hardware. */ + bool hw_state; +}; + +struct b43_leds { + struct b43_led led_tx; + struct b43_led led_rx; + struct b43_led led_radio; + struct b43_led led_assoc; + + bool stop; + struct work_struct work; +}; + +#define B43_MAX_NR_LEDS 4 + +#define B43_LED_BEHAVIOUR 0x7F +#define B43_LED_ACTIVELOW 0x80 +/* LED behaviour values */ +enum b43_led_behaviour { + B43_LED_OFF, + B43_LED_ON, + B43_LED_ACTIVITY, + B43_LED_RADIO_ALL, + B43_LED_RADIO_A, + B43_LED_RADIO_B, + B43_LED_MODE_BG, + B43_LED_TRANSFER, + B43_LED_APTRANSFER, + B43_LED_WEIRD, //FIXME + B43_LED_ASSOC, + B43_LED_INACTIVE, +}; + +void b43_leds_register(struct b43_wldev *dev); +void b43_leds_unregister(struct b43_wl *wl); +void b43_leds_init(struct b43_wldev *dev); +void b43_leds_exit(struct b43_wldev *dev); +void b43_leds_stop(struct b43_wldev *dev); + + +#else /* CONFIG_B43_LEDS */ +/* LED support disabled */ + +struct b43_leds { + /* empty */ +}; + +static inline void b43_leds_register(struct b43_wldev *dev) +{ +} +static inline void b43_leds_unregister(struct b43_wl *wl) +{ +} +static inline void b43_leds_init(struct b43_wldev *dev) +{ +} +static inline void b43_leds_exit(struct b43_wldev *dev) +{ +} +static inline void b43_leds_stop(struct b43_wldev *dev) +{ +} +#endif /* CONFIG_B43_LEDS */ + +#endif /* B43_LEDS_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/lo.c b/drivers/net/wireless/broadcom/b43/lo.c new file mode 100644 index 000000000000..a335f94c72ff --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/lo.c @@ -0,0 +1,1016 @@ +/* + + Broadcom B43 wireless driver + + G PHY LO (LocalOscillator) Measuring and Control routines + + Copyright (c) 2005 Martin Langer , + Copyright (c) 2005, 2006 Stefano Brivio + Copyright (c) 2005-2007 Michael Buesch + Copyright (c) 2005, 2006 Danny van Dyk + Copyright (c) 2005, 2006 Andreas Jaggi + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "lo.h" +#include "phy_g.h" +#include "main.h" + +#include +#include +#include + + +static struct b43_lo_calib *b43_find_lo_calib(struct b43_txpower_lo_control *lo, + const struct b43_bbatt *bbatt, + const struct b43_rfatt *rfatt) +{ + struct b43_lo_calib *c; + + list_for_each_entry(c, &lo->calib_list, list) { + if (!b43_compare_bbatt(&c->bbatt, bbatt)) + continue; + if (!b43_compare_rfatt(&c->rfatt, rfatt)) + continue; + return c; + } + + return NULL; +} + +/* Write the LocalOscillator Control (adjust) value-pair. */ +static void b43_lo_write(struct b43_wldev *dev, struct b43_loctl *control) +{ + struct b43_phy *phy = &dev->phy; + u16 value; + + if (B43_DEBUG) { + if (unlikely(abs(control->i) > 16 || abs(control->q) > 16)) { + b43dbg(dev->wl, "Invalid LO control pair " + "(I: %d, Q: %d)\n", control->i, control->q); + dump_stack(); + return; + } + } + B43_WARN_ON(phy->type != B43_PHYTYPE_G); + + value = (u8) (control->q); + value |= ((u8) (control->i)) << 8; + b43_phy_write(dev, B43_PHY_LO_CTL, value); +} + +static u16 lo_measure_feedthrough(struct b43_wldev *dev, + u16 lna, u16 pga, u16 trsw_rx) +{ + struct b43_phy *phy = &dev->phy; + u16 rfover; + u16 feedthrough; + + if (phy->gmode) { + lna <<= B43_PHY_RFOVERVAL_LNA_SHIFT; + pga <<= B43_PHY_RFOVERVAL_PGA_SHIFT; + + B43_WARN_ON(lna & ~B43_PHY_RFOVERVAL_LNA); + B43_WARN_ON(pga & ~B43_PHY_RFOVERVAL_PGA); +/*FIXME This assertion fails B43_WARN_ON(trsw_rx & ~(B43_PHY_RFOVERVAL_TRSWRX | + B43_PHY_RFOVERVAL_BW)); +*/ + trsw_rx &= (B43_PHY_RFOVERVAL_TRSWRX | B43_PHY_RFOVERVAL_BW); + + /* Construct the RF Override Value */ + rfover = B43_PHY_RFOVERVAL_UNK; + rfover |= pga; + rfover |= lna; + rfover |= trsw_rx; + if ((dev->dev->bus_sprom->boardflags_lo & B43_BFL_EXTLNA) + && phy->rev > 6) + rfover |= B43_PHY_RFOVERVAL_EXTLNA; + + b43_phy_write(dev, B43_PHY_PGACTL, 0xE300); + b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); + udelay(10); + rfover |= B43_PHY_RFOVERVAL_BW_LBW; + b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); + udelay(10); + rfover |= B43_PHY_RFOVERVAL_BW_LPF; + b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); + udelay(10); + b43_phy_write(dev, B43_PHY_PGACTL, 0xF300); + } else { + pga |= B43_PHY_PGACTL_UNKNOWN; + b43_phy_write(dev, B43_PHY_PGACTL, pga); + udelay(10); + pga |= B43_PHY_PGACTL_LOWBANDW; + b43_phy_write(dev, B43_PHY_PGACTL, pga); + udelay(10); + pga |= B43_PHY_PGACTL_LPF; + b43_phy_write(dev, B43_PHY_PGACTL, pga); + } + udelay(21); + feedthrough = b43_phy_read(dev, B43_PHY_LO_LEAKAGE); + + /* This is a good place to check if we need to relax a bit, + * as this is the main function called regularly + * in the LO calibration. */ + cond_resched(); + + return feedthrough; +} + +/* TXCTL Register and Value Table. + * Returns the "TXCTL Register". + * "value" is the "TXCTL Value". + * "pad_mix_gain" is the PAD Mixer Gain. + */ +static u16 lo_txctl_register_table(struct b43_wldev *dev, + u16 *value, u16 *pad_mix_gain) +{ + struct b43_phy *phy = &dev->phy; + u16 reg, v, padmix; + + if (phy->type == B43_PHYTYPE_B) { + v = 0x30; + if (phy->radio_rev <= 5) { + reg = 0x43; + padmix = 0; + } else { + reg = 0x52; + padmix = 5; + } + } else { + if (phy->rev >= 2 && phy->radio_rev == 8) { + reg = 0x43; + v = 0x10; + padmix = 2; + } else { + reg = 0x52; + v = 0x30; + padmix = 5; + } + } + if (value) + *value = v; + if (pad_mix_gain) + *pad_mix_gain = padmix; + + return reg; +} + +static void lo_measure_txctl_values(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_txpower_lo_control *lo = gphy->lo_control; + u16 reg, mask; + u16 trsw_rx, pga; + u16 radio_pctl_reg; + + static const u8 tx_bias_values[] = { + 0x09, 0x08, 0x0A, 0x01, 0x00, + 0x02, 0x05, 0x04, 0x06, + }; + static const u8 tx_magn_values[] = { + 0x70, 0x40, + }; + + if (!has_loopback_gain(phy)) { + radio_pctl_reg = 6; + trsw_rx = 2; + pga = 0; + } else { + int lb_gain; /* Loopback gain (in dB) */ + + trsw_rx = 0; + lb_gain = gphy->max_lb_gain / 2; + if (lb_gain > 10) { + radio_pctl_reg = 0; + pga = abs(10 - lb_gain) / 6; + pga = clamp_val(pga, 0, 15); + } else { + int cmp_val; + int tmp; + + pga = 0; + cmp_val = 0x24; + if ((phy->rev >= 2) && + (phy->radio_ver == 0x2050) && (phy->radio_rev == 8)) + cmp_val = 0x3C; + tmp = lb_gain; + if ((10 - lb_gain) < cmp_val) + tmp = (10 - lb_gain); + if (tmp < 0) + tmp += 6; + else + tmp += 3; + cmp_val /= 4; + tmp /= 4; + if (tmp >= cmp_val) + radio_pctl_reg = cmp_val; + else + radio_pctl_reg = tmp; + } + } + b43_radio_maskset(dev, 0x43, 0xFFF0, radio_pctl_reg); + b43_gphy_set_baseband_attenuation(dev, 2); + + reg = lo_txctl_register_table(dev, &mask, NULL); + mask = ~mask; + b43_radio_mask(dev, reg, mask); + + if (has_tx_magnification(phy)) { + int i, j; + int feedthrough; + int min_feedth = 0xFFFF; + u8 tx_magn, tx_bias; + + for (i = 0; i < ARRAY_SIZE(tx_magn_values); i++) { + tx_magn = tx_magn_values[i]; + b43_radio_maskset(dev, 0x52, 0xFF0F, tx_magn); + for (j = 0; j < ARRAY_SIZE(tx_bias_values); j++) { + tx_bias = tx_bias_values[j]; + b43_radio_maskset(dev, 0x52, 0xFFF0, tx_bias); + feedthrough = + lo_measure_feedthrough(dev, 0, pga, + trsw_rx); + if (feedthrough < min_feedth) { + lo->tx_bias = tx_bias; + lo->tx_magn = tx_magn; + min_feedth = feedthrough; + } + if (lo->tx_bias == 0) + break; + } + b43_radio_write16(dev, 0x52, + (b43_radio_read16(dev, 0x52) + & 0xFF00) | lo->tx_bias | lo-> + tx_magn); + } + } else { + lo->tx_magn = 0; + lo->tx_bias = 0; + b43_radio_mask(dev, 0x52, 0xFFF0); /* TX bias == 0 */ + } + lo->txctl_measured_time = jiffies; +} + +static void lo_read_power_vector(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_txpower_lo_control *lo = gphy->lo_control; + int i; + u64 tmp; + u64 power_vector = 0; + + for (i = 0; i < 8; i += 2) { + tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x310 + i); + power_vector |= (tmp << (i * 8)); + /* Clear the vector on the device. */ + b43_shm_write16(dev, B43_SHM_SHARED, 0x310 + i, 0); + } + if (power_vector) + lo->power_vector = power_vector; + lo->pwr_vec_read_time = jiffies; +} + +/* 802.11/LO/GPHY/MeasuringGains */ +static void lo_measure_gain_values(struct b43_wldev *dev, + s16 max_rx_gain, int use_trsw_rx) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + u16 tmp; + + if (max_rx_gain < 0) + max_rx_gain = 0; + + if (has_loopback_gain(phy)) { + int trsw_rx_gain; + + if (use_trsw_rx) { + trsw_rx_gain = gphy->trsw_rx_gain / 2; + if (max_rx_gain >= trsw_rx_gain) { + trsw_rx_gain = max_rx_gain - trsw_rx_gain; + } + } else + trsw_rx_gain = max_rx_gain; + if (trsw_rx_gain < 9) { + gphy->lna_lod_gain = 0; + } else { + gphy->lna_lod_gain = 1; + trsw_rx_gain -= 8; + } + trsw_rx_gain = clamp_val(trsw_rx_gain, 0, 0x2D); + gphy->pga_gain = trsw_rx_gain / 3; + if (gphy->pga_gain >= 5) { + gphy->pga_gain -= 5; + gphy->lna_gain = 2; + } else + gphy->lna_gain = 0; + } else { + gphy->lna_gain = 0; + gphy->trsw_rx_gain = 0x20; + if (max_rx_gain >= 0x14) { + gphy->lna_lod_gain = 1; + gphy->pga_gain = 2; + } else if (max_rx_gain >= 0x12) { + gphy->lna_lod_gain = 1; + gphy->pga_gain = 1; + } else if (max_rx_gain >= 0xF) { + gphy->lna_lod_gain = 1; + gphy->pga_gain = 0; + } else { + gphy->lna_lod_gain = 0; + gphy->pga_gain = 0; + } + } + + tmp = b43_radio_read16(dev, 0x7A); + if (gphy->lna_lod_gain == 0) + tmp &= ~0x0008; + else + tmp |= 0x0008; + b43_radio_write16(dev, 0x7A, tmp); +} + +struct lo_g_saved_values { + u8 old_channel; + + /* Core registers */ + u16 reg_3F4; + u16 reg_3E2; + + /* PHY registers */ + u16 phy_lo_mask; + u16 phy_extg_01; + u16 phy_dacctl_hwpctl; + u16 phy_dacctl; + u16 phy_cck_14; + u16 phy_hpwr_tssictl; + u16 phy_analogover; + u16 phy_analogoverval; + u16 phy_rfover; + u16 phy_rfoverval; + u16 phy_classctl; + u16 phy_cck_3E; + u16 phy_crs0; + u16 phy_pgactl; + u16 phy_cck_2A; + u16 phy_syncctl; + u16 phy_cck_30; + u16 phy_cck_06; + + /* Radio registers */ + u16 radio_43; + u16 radio_7A; + u16 radio_52; +}; + +static void lo_measure_setup(struct b43_wldev *dev, + struct lo_g_saved_values *sav) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_txpower_lo_control *lo = gphy->lo_control; + u16 tmp; + + if (b43_has_hardware_pctl(dev)) { + sav->phy_lo_mask = b43_phy_read(dev, B43_PHY_LO_MASK); + sav->phy_extg_01 = b43_phy_read(dev, B43_PHY_EXTG(0x01)); + sav->phy_dacctl_hwpctl = b43_phy_read(dev, B43_PHY_DACCTL); + sav->phy_cck_14 = b43_phy_read(dev, B43_PHY_CCK(0x14)); + sav->phy_hpwr_tssictl = b43_phy_read(dev, B43_PHY_HPWR_TSSICTL); + + b43_phy_set(dev, B43_PHY_HPWR_TSSICTL, 0x100); + b43_phy_set(dev, B43_PHY_EXTG(0x01), 0x40); + b43_phy_set(dev, B43_PHY_DACCTL, 0x40); + b43_phy_set(dev, B43_PHY_CCK(0x14), 0x200); + } + if (phy->type == B43_PHYTYPE_B && + phy->radio_ver == 0x2050 && phy->radio_rev < 6) { + b43_phy_write(dev, B43_PHY_CCK(0x16), 0x410); + b43_phy_write(dev, B43_PHY_CCK(0x17), 0x820); + } + if (phy->rev >= 2) { + sav->phy_analogover = b43_phy_read(dev, B43_PHY_ANALOGOVER); + sav->phy_analogoverval = + b43_phy_read(dev, B43_PHY_ANALOGOVERVAL); + sav->phy_rfover = b43_phy_read(dev, B43_PHY_RFOVER); + sav->phy_rfoverval = b43_phy_read(dev, B43_PHY_RFOVERVAL); + sav->phy_classctl = b43_phy_read(dev, B43_PHY_CLASSCTL); + sav->phy_cck_3E = b43_phy_read(dev, B43_PHY_CCK(0x3E)); + sav->phy_crs0 = b43_phy_read(dev, B43_PHY_CRS0); + + b43_phy_mask(dev, B43_PHY_CLASSCTL, 0xFFFC); + b43_phy_mask(dev, B43_PHY_CRS0, 0x7FFF); + b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0003); + b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFC); + if (phy->type == B43_PHYTYPE_G) { + if ((phy->rev >= 7) && + (sprom->boardflags_lo & B43_BFL_EXTLNA)) { + b43_phy_write(dev, B43_PHY_RFOVER, 0x933); + } else { + b43_phy_write(dev, B43_PHY_RFOVER, 0x133); + } + } else { + b43_phy_write(dev, B43_PHY_RFOVER, 0); + } + b43_phy_write(dev, B43_PHY_CCK(0x3E), 0); + } + sav->reg_3F4 = b43_read16(dev, 0x3F4); + sav->reg_3E2 = b43_read16(dev, 0x3E2); + sav->radio_43 = b43_radio_read16(dev, 0x43); + sav->radio_7A = b43_radio_read16(dev, 0x7A); + sav->phy_pgactl = b43_phy_read(dev, B43_PHY_PGACTL); + sav->phy_cck_2A = b43_phy_read(dev, B43_PHY_CCK(0x2A)); + sav->phy_syncctl = b43_phy_read(dev, B43_PHY_SYNCCTL); + sav->phy_dacctl = b43_phy_read(dev, B43_PHY_DACCTL); + + if (!has_tx_magnification(phy)) { + sav->radio_52 = b43_radio_read16(dev, 0x52); + sav->radio_52 &= 0x00F0; + } + if (phy->type == B43_PHYTYPE_B) { + sav->phy_cck_30 = b43_phy_read(dev, B43_PHY_CCK(0x30)); + sav->phy_cck_06 = b43_phy_read(dev, B43_PHY_CCK(0x06)); + b43_phy_write(dev, B43_PHY_CCK(0x30), 0x00FF); + b43_phy_write(dev, B43_PHY_CCK(0x06), 0x3F3F); + } else { + b43_write16(dev, 0x3E2, b43_read16(dev, 0x3E2) + | 0x8000); + } + b43_write16(dev, 0x3F4, b43_read16(dev, 0x3F4) + & 0xF000); + + tmp = + (phy->type == B43_PHYTYPE_G) ? B43_PHY_LO_MASK : B43_PHY_CCK(0x2E); + b43_phy_write(dev, tmp, 0x007F); + + tmp = sav->phy_syncctl; + b43_phy_write(dev, B43_PHY_SYNCCTL, tmp & 0xFF7F); + tmp = sav->radio_7A; + b43_radio_write16(dev, 0x007A, tmp & 0xFFF0); + + b43_phy_write(dev, B43_PHY_CCK(0x2A), 0x8A3); + if (phy->type == B43_PHYTYPE_G || + (phy->type == B43_PHYTYPE_B && + phy->radio_ver == 0x2050 && phy->radio_rev >= 6)) { + b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x1003); + } else + b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x0802); + if (phy->rev >= 2) + b43_dummy_transmission(dev, false, true); + b43_gphy_channel_switch(dev, 6, 0); + b43_radio_read16(dev, 0x51); /* dummy read */ + if (phy->type == B43_PHYTYPE_G) + b43_phy_write(dev, B43_PHY_CCK(0x2F), 0); + + /* Re-measure the txctl values, if needed. */ + if (time_before(lo->txctl_measured_time, + jiffies - B43_LO_TXCTL_EXPIRE)) + lo_measure_txctl_values(dev); + + if (phy->type == B43_PHYTYPE_G && phy->rev >= 3) { + b43_phy_write(dev, B43_PHY_LO_MASK, 0xC078); + } else { + if (phy->type == B43_PHYTYPE_B) + b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8078); + else + b43_phy_write(dev, B43_PHY_LO_MASK, 0x8078); + } +} + +static void lo_measure_restore(struct b43_wldev *dev, + struct lo_g_saved_values *sav) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + u16 tmp; + + if (phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_PGACTL, 0xE300); + tmp = (gphy->pga_gain << 8); + b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA0); + udelay(5); + b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA2); + udelay(2); + b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA3); + } else { + tmp = (gphy->pga_gain | 0xEFA0); + b43_phy_write(dev, B43_PHY_PGACTL, tmp); + } + if (phy->type == B43_PHYTYPE_G) { + if (phy->rev >= 3) + b43_phy_write(dev, B43_PHY_CCK(0x2E), 0xC078); + else + b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8078); + if (phy->rev >= 2) + b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x0202); + else + b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x0101); + } + b43_write16(dev, 0x3F4, sav->reg_3F4); + b43_phy_write(dev, B43_PHY_PGACTL, sav->phy_pgactl); + b43_phy_write(dev, B43_PHY_CCK(0x2A), sav->phy_cck_2A); + b43_phy_write(dev, B43_PHY_SYNCCTL, sav->phy_syncctl); + b43_phy_write(dev, B43_PHY_DACCTL, sav->phy_dacctl); + b43_radio_write16(dev, 0x43, sav->radio_43); + b43_radio_write16(dev, 0x7A, sav->radio_7A); + if (!has_tx_magnification(phy)) { + tmp = sav->radio_52; + b43_radio_maskset(dev, 0x52, 0xFF0F, tmp); + } + b43_write16(dev, 0x3E2, sav->reg_3E2); + if (phy->type == B43_PHYTYPE_B && + phy->radio_ver == 0x2050 && phy->radio_rev <= 5) { + b43_phy_write(dev, B43_PHY_CCK(0x30), sav->phy_cck_30); + b43_phy_write(dev, B43_PHY_CCK(0x06), sav->phy_cck_06); + } + if (phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_ANALOGOVER, sav->phy_analogover); + b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, + sav->phy_analogoverval); + b43_phy_write(dev, B43_PHY_CLASSCTL, sav->phy_classctl); + b43_phy_write(dev, B43_PHY_RFOVER, sav->phy_rfover); + b43_phy_write(dev, B43_PHY_RFOVERVAL, sav->phy_rfoverval); + b43_phy_write(dev, B43_PHY_CCK(0x3E), sav->phy_cck_3E); + b43_phy_write(dev, B43_PHY_CRS0, sav->phy_crs0); + } + if (b43_has_hardware_pctl(dev)) { + tmp = (sav->phy_lo_mask & 0xBFFF); + b43_phy_write(dev, B43_PHY_LO_MASK, tmp); + b43_phy_write(dev, B43_PHY_EXTG(0x01), sav->phy_extg_01); + b43_phy_write(dev, B43_PHY_DACCTL, sav->phy_dacctl_hwpctl); + b43_phy_write(dev, B43_PHY_CCK(0x14), sav->phy_cck_14); + b43_phy_write(dev, B43_PHY_HPWR_TSSICTL, sav->phy_hpwr_tssictl); + } + b43_gphy_channel_switch(dev, sav->old_channel, 1); +} + +struct b43_lo_g_statemachine { + int current_state; + int nr_measured; + int state_val_multiplier; + u16 lowest_feedth; + struct b43_loctl min_loctl; +}; + +/* Loop over each possible value in this state. */ +static int lo_probe_possible_loctls(struct b43_wldev *dev, + struct b43_loctl *probe_loctl, + struct b43_lo_g_statemachine *d) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_loctl test_loctl; + struct b43_loctl orig_loctl; + struct b43_loctl prev_loctl = { + .i = -100, + .q = -100, + }; + int i; + int begin, end; + int found_lower = 0; + u16 feedth; + + static const struct b43_loctl modifiers[] = { + {.i = 1,.q = 1,}, + {.i = 1,.q = 0,}, + {.i = 1,.q = -1,}, + {.i = 0,.q = -1,}, + {.i = -1,.q = -1,}, + {.i = -1,.q = 0,}, + {.i = -1,.q = 1,}, + {.i = 0,.q = 1,}, + }; + + if (d->current_state == 0) { + begin = 1; + end = 8; + } else if (d->current_state % 2 == 0) { + begin = d->current_state - 1; + end = d->current_state + 1; + } else { + begin = d->current_state - 2; + end = d->current_state + 2; + } + if (begin < 1) + begin += 8; + if (end > 8) + end -= 8; + + memcpy(&orig_loctl, probe_loctl, sizeof(struct b43_loctl)); + i = begin; + d->current_state = i; + while (1) { + B43_WARN_ON(!(i >= 1 && i <= 8)); + memcpy(&test_loctl, &orig_loctl, sizeof(struct b43_loctl)); + test_loctl.i += modifiers[i - 1].i * d->state_val_multiplier; + test_loctl.q += modifiers[i - 1].q * d->state_val_multiplier; + if ((test_loctl.i != prev_loctl.i || + test_loctl.q != prev_loctl.q) && + (abs(test_loctl.i) <= 16 && abs(test_loctl.q) <= 16)) { + b43_lo_write(dev, &test_loctl); + feedth = lo_measure_feedthrough(dev, gphy->lna_gain, + gphy->pga_gain, + gphy->trsw_rx_gain); + if (feedth < d->lowest_feedth) { + memcpy(probe_loctl, &test_loctl, + sizeof(struct b43_loctl)); + found_lower = 1; + d->lowest_feedth = feedth; + if ((d->nr_measured < 2) && + !has_loopback_gain(phy)) + break; + } + } + memcpy(&prev_loctl, &test_loctl, sizeof(prev_loctl)); + if (i == end) + break; + if (i == 8) + i = 1; + else + i++; + d->current_state = i; + } + + return found_lower; +} + +static void lo_probe_loctls_statemachine(struct b43_wldev *dev, + struct b43_loctl *loctl, + int *max_rx_gain) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_lo_g_statemachine d; + u16 feedth; + int found_lower; + struct b43_loctl probe_loctl; + int max_repeat = 1, repeat_cnt = 0; + + d.nr_measured = 0; + d.state_val_multiplier = 1; + if (has_loopback_gain(phy)) + d.state_val_multiplier = 3; + + memcpy(&d.min_loctl, loctl, sizeof(struct b43_loctl)); + if (has_loopback_gain(phy)) + max_repeat = 4; + do { + b43_lo_write(dev, &d.min_loctl); + feedth = lo_measure_feedthrough(dev, gphy->lna_gain, + gphy->pga_gain, + gphy->trsw_rx_gain); + if (feedth < 0x258) { + if (feedth >= 0x12C) + *max_rx_gain += 6; + else + *max_rx_gain += 3; + feedth = lo_measure_feedthrough(dev, gphy->lna_gain, + gphy->pga_gain, + gphy->trsw_rx_gain); + } + d.lowest_feedth = feedth; + + d.current_state = 0; + do { + B43_WARN_ON(! + (d.current_state >= 0 + && d.current_state <= 8)); + memcpy(&probe_loctl, &d.min_loctl, + sizeof(struct b43_loctl)); + found_lower = + lo_probe_possible_loctls(dev, &probe_loctl, &d); + if (!found_lower) + break; + if ((probe_loctl.i == d.min_loctl.i) && + (probe_loctl.q == d.min_loctl.q)) + break; + memcpy(&d.min_loctl, &probe_loctl, + sizeof(struct b43_loctl)); + d.nr_measured++; + } while (d.nr_measured < 24); + memcpy(loctl, &d.min_loctl, sizeof(struct b43_loctl)); + + if (has_loopback_gain(phy)) { + if (d.lowest_feedth > 0x1194) + *max_rx_gain -= 6; + else if (d.lowest_feedth < 0x5DC) + *max_rx_gain += 3; + if (repeat_cnt == 0) { + if (d.lowest_feedth <= 0x5DC) { + d.state_val_multiplier = 1; + repeat_cnt++; + } else + d.state_val_multiplier = 2; + } else if (repeat_cnt == 2) + d.state_val_multiplier = 1; + } + lo_measure_gain_values(dev, *max_rx_gain, + has_loopback_gain(phy)); + } while (++repeat_cnt < max_repeat); +} + +static +struct b43_lo_calib *b43_calibrate_lo_setting(struct b43_wldev *dev, + const struct b43_bbatt *bbatt, + const struct b43_rfatt *rfatt) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_loctl loctl = { + .i = 0, + .q = 0, + }; + int max_rx_gain; + struct b43_lo_calib *cal; + struct lo_g_saved_values uninitialized_var(saved_regs); + /* Values from the "TXCTL Register and Value Table" */ + u16 txctl_reg; + u16 txctl_value; + u16 pad_mix_gain; + + saved_regs.old_channel = phy->channel; + b43_mac_suspend(dev); + lo_measure_setup(dev, &saved_regs); + + txctl_reg = lo_txctl_register_table(dev, &txctl_value, &pad_mix_gain); + + b43_radio_maskset(dev, 0x43, 0xFFF0, rfatt->att); + b43_radio_maskset(dev, txctl_reg, ~txctl_value, (rfatt->with_padmix ? txctl_value :0)); + + max_rx_gain = rfatt->att * 2; + max_rx_gain += bbatt->att / 2; + if (rfatt->with_padmix) + max_rx_gain -= pad_mix_gain; + if (has_loopback_gain(phy)) + max_rx_gain += gphy->max_lb_gain; + lo_measure_gain_values(dev, max_rx_gain, + has_loopback_gain(phy)); + + b43_gphy_set_baseband_attenuation(dev, bbatt->att); + lo_probe_loctls_statemachine(dev, &loctl, &max_rx_gain); + + lo_measure_restore(dev, &saved_regs); + b43_mac_enable(dev); + + if (b43_debug(dev, B43_DBG_LO)) { + b43dbg(dev->wl, "LO: Calibrated for BB(%u), RF(%u,%u) " + "=> I=%d Q=%d\n", + bbatt->att, rfatt->att, rfatt->with_padmix, + loctl.i, loctl.q); + } + + cal = kmalloc(sizeof(*cal), GFP_KERNEL); + if (!cal) { + b43warn(dev->wl, "LO calib: out of memory\n"); + return NULL; + } + memcpy(&cal->bbatt, bbatt, sizeof(*bbatt)); + memcpy(&cal->rfatt, rfatt, sizeof(*rfatt)); + memcpy(&cal->ctl, &loctl, sizeof(loctl)); + cal->calib_time = jiffies; + INIT_LIST_HEAD(&cal->list); + + return cal; +} + +/* Get a calibrated LO setting for the given attenuation values. + * Might return a NULL pointer under OOM! */ +static +struct b43_lo_calib *b43_get_calib_lo_settings(struct b43_wldev *dev, + const struct b43_bbatt *bbatt, + const struct b43_rfatt *rfatt) +{ + struct b43_txpower_lo_control *lo = dev->phy.g->lo_control; + struct b43_lo_calib *c; + + c = b43_find_lo_calib(lo, bbatt, rfatt); + if (c) + return c; + /* Not in the list of calibrated LO settings. + * Calibrate it now. */ + c = b43_calibrate_lo_setting(dev, bbatt, rfatt); + if (!c) + return NULL; + list_add(&c->list, &lo->calib_list); + + return c; +} + +void b43_gphy_dc_lt_init(struct b43_wldev *dev, bool update_all) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_txpower_lo_control *lo = gphy->lo_control; + int i; + int rf_offset, bb_offset; + const struct b43_rfatt *rfatt; + const struct b43_bbatt *bbatt; + u64 power_vector; + bool table_changed = false; + + BUILD_BUG_ON(B43_DC_LT_SIZE != 32); + B43_WARN_ON(lo->rfatt_list.len * lo->bbatt_list.len > 64); + + power_vector = lo->power_vector; + if (!update_all && !power_vector) + return; /* Nothing to do. */ + + /* Suspend the MAC now to avoid continuous suspend/enable + * cycles in the loop. */ + b43_mac_suspend(dev); + + for (i = 0; i < B43_DC_LT_SIZE * 2; i++) { + struct b43_lo_calib *cal; + int idx; + u16 val; + + if (!update_all && !(power_vector & (((u64)1ULL) << i))) + continue; + /* Update the table entry for this power_vector bit. + * The table rows are RFatt entries and columns are BBatt. */ + bb_offset = i / lo->rfatt_list.len; + rf_offset = i % lo->rfatt_list.len; + bbatt = &(lo->bbatt_list.list[bb_offset]); + rfatt = &(lo->rfatt_list.list[rf_offset]); + + cal = b43_calibrate_lo_setting(dev, bbatt, rfatt); + if (!cal) { + b43warn(dev->wl, "LO: Could not " + "calibrate DC table entry\n"); + continue; + } + /*FIXME: Is Q really in the low nibble? */ + val = (u8)(cal->ctl.q); + val |= ((u8)(cal->ctl.i)) << 4; + kfree(cal); + + /* Get the index into the hardware DC LT. */ + idx = i / 2; + /* Change the table in memory. */ + if (i % 2) { + /* Change the high byte. */ + lo->dc_lt[idx] = (lo->dc_lt[idx] & 0x00FF) + | ((val & 0x00FF) << 8); + } else { + /* Change the low byte. */ + lo->dc_lt[idx] = (lo->dc_lt[idx] & 0xFF00) + | (val & 0x00FF); + } + table_changed = true; + } + if (table_changed) { + /* The table changed in memory. Update the hardware table. */ + for (i = 0; i < B43_DC_LT_SIZE; i++) + b43_phy_write(dev, 0x3A0 + i, lo->dc_lt[i]); + } + b43_mac_enable(dev); +} + +/* Fixup the RF attenuation value for the case where we are + * using the PAD mixer. */ +static inline void b43_lo_fixup_rfatt(struct b43_rfatt *rf) +{ + if (!rf->with_padmix) + return; + if ((rf->att != 1) && (rf->att != 2) && (rf->att != 3)) + rf->att = 4; +} + +void b43_lo_g_adjust(struct b43_wldev *dev) +{ + struct b43_phy_g *gphy = dev->phy.g; + struct b43_lo_calib *cal; + struct b43_rfatt rf; + + memcpy(&rf, &gphy->rfatt, sizeof(rf)); + b43_lo_fixup_rfatt(&rf); + + cal = b43_get_calib_lo_settings(dev, &gphy->bbatt, &rf); + if (!cal) + return; + b43_lo_write(dev, &cal->ctl); +} + +void b43_lo_g_adjust_to(struct b43_wldev *dev, + u16 rfatt, u16 bbatt, u16 tx_control) +{ + struct b43_rfatt rf; + struct b43_bbatt bb; + struct b43_lo_calib *cal; + + memset(&rf, 0, sizeof(rf)); + memset(&bb, 0, sizeof(bb)); + rf.att = rfatt; + bb.att = bbatt; + b43_lo_fixup_rfatt(&rf); + cal = b43_get_calib_lo_settings(dev, &bb, &rf); + if (!cal) + return; + b43_lo_write(dev, &cal->ctl); +} + +/* Periodic LO maintenance work */ +void b43_lo_g_maintenance_work(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_txpower_lo_control *lo = gphy->lo_control; + unsigned long now; + unsigned long expire; + struct b43_lo_calib *cal, *tmp; + bool current_item_expired = false; + bool hwpctl; + + if (!lo) + return; + now = jiffies; + hwpctl = b43_has_hardware_pctl(dev); + + if (hwpctl) { + /* Read the power vector and update it, if needed. */ + expire = now - B43_LO_PWRVEC_EXPIRE; + if (time_before(lo->pwr_vec_read_time, expire)) { + lo_read_power_vector(dev); + b43_gphy_dc_lt_init(dev, 0); + } + //FIXME Recalc the whole DC table from time to time? + } + + if (hwpctl) + return; + /* Search for expired LO settings. Remove them. + * Recalibrate the current setting, if expired. */ + expire = now - B43_LO_CALIB_EXPIRE; + list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) { + if (!time_before(cal->calib_time, expire)) + continue; + /* This item expired. */ + if (b43_compare_bbatt(&cal->bbatt, &gphy->bbatt) && + b43_compare_rfatt(&cal->rfatt, &gphy->rfatt)) { + B43_WARN_ON(current_item_expired); + current_item_expired = true; + } + if (b43_debug(dev, B43_DBG_LO)) { + b43dbg(dev->wl, "LO: Item BB(%u), RF(%u,%u), " + "I=%d, Q=%d expired\n", + cal->bbatt.att, cal->rfatt.att, + cal->rfatt.with_padmix, + cal->ctl.i, cal->ctl.q); + } + list_del(&cal->list); + kfree(cal); + } + if (current_item_expired || unlikely(list_empty(&lo->calib_list))) { + /* Recalibrate currently used LO setting. */ + if (b43_debug(dev, B43_DBG_LO)) + b43dbg(dev->wl, "LO: Recalibrating current LO setting\n"); + cal = b43_calibrate_lo_setting(dev, &gphy->bbatt, &gphy->rfatt); + if (cal) { + list_add(&cal->list, &lo->calib_list); + b43_lo_write(dev, &cal->ctl); + } else + b43warn(dev->wl, "Failed to recalibrate current LO setting\n"); + } +} + +void b43_lo_g_cleanup(struct b43_wldev *dev) +{ + struct b43_txpower_lo_control *lo = dev->phy.g->lo_control; + struct b43_lo_calib *cal, *tmp; + + if (!lo) + return; + list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) { + list_del(&cal->list); + kfree(cal); + } +} + +/* LO Initialization */ +void b43_lo_g_init(struct b43_wldev *dev) +{ + if (b43_has_hardware_pctl(dev)) { + lo_read_power_vector(dev); + b43_gphy_dc_lt_init(dev, 1); + } +} diff --git a/drivers/net/wireless/broadcom/b43/lo.h b/drivers/net/wireless/broadcom/b43/lo.h new file mode 100644 index 000000000000..7b4df3883bc2 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/lo.h @@ -0,0 +1,87 @@ +#ifndef B43_LO_H_ +#define B43_LO_H_ + +/* G-PHY Local Oscillator */ + +#include "phy_g.h" + +struct b43_wldev; + +/* Local Oscillator control value-pair. */ +struct b43_loctl { + /* Control values. */ + s8 i; + s8 q; +}; +/* Debugging: Poison value for i and q values. */ +#define B43_LOCTL_POISON 111 + +/* This struct holds calibrated LO settings for a set of + * Baseband and RF attenuation settings. */ +struct b43_lo_calib { + /* The set of attenuation values this set of LO + * control values is calibrated for. */ + struct b43_bbatt bbatt; + struct b43_rfatt rfatt; + /* The set of control values for the LO. */ + struct b43_loctl ctl; + /* The time when these settings were calibrated (in jiffies) */ + unsigned long calib_time; + /* List. */ + struct list_head list; +}; + +/* Size of the DC Lookup Table in 16bit words. */ +#define B43_DC_LT_SIZE 32 + +/* Local Oscillator calibration information */ +struct b43_txpower_lo_control { + /* Lists of RF and BB attenuation values for this device. + * Used for building hardware power control tables. */ + struct b43_rfatt_list rfatt_list; + struct b43_bbatt_list bbatt_list; + + /* The DC Lookup Table is cached in memory here. + * Note that this is only used for Hardware Power Control. */ + u16 dc_lt[B43_DC_LT_SIZE]; + + /* List of calibrated control values (struct b43_lo_calib). */ + struct list_head calib_list; + /* Last time the power vector was read (jiffies). */ + unsigned long pwr_vec_read_time; + /* Last time the txctl values were measured (jiffies). */ + unsigned long txctl_measured_time; + + /* Current TX Bias value */ + u8 tx_bias; + /* Current TX Magnification Value (if used by the device) */ + u8 tx_magn; + + /* Saved device PowerVector */ + u64 power_vector; +}; + +/* Calibration expire timeouts. + * Timeouts must be multiple of 15 seconds. To make sure + * the item really expired when the 15 second timer hits, we + * subtract two additional seconds from the timeout. */ +#define B43_LO_CALIB_EXPIRE (HZ * (30 - 2)) +#define B43_LO_PWRVEC_EXPIRE (HZ * (30 - 2)) +#define B43_LO_TXCTL_EXPIRE (HZ * (180 - 4)) + + +/* Adjust the Local Oscillator to the saved attenuation + * and txctl values. + */ +void b43_lo_g_adjust(struct b43_wldev *dev); +/* Adjust to specific values. */ +void b43_lo_g_adjust_to(struct b43_wldev *dev, + u16 rfatt, u16 bbatt, u16 tx_control); + +void b43_gphy_dc_lt_init(struct b43_wldev *dev, bool update_all); + +void b43_lo_g_maintenance_work(struct b43_wldev *dev); +void b43_lo_g_cleanup(struct b43_wldev *dev); +void b43_lo_g_init(struct b43_wldev *dev); + +#endif /* B43_LO_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c new file mode 100644 index 000000000000..ec013fbd6a81 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -0,0 +1,5895 @@ +/* + + Broadcom B43 wireless driver + + Copyright (c) 2005 Martin Langer + Copyright (c) 2005 Stefano Brivio + Copyright (c) 2005-2009 Michael Buesch + Copyright (c) 2005 Danny van Dyk + Copyright (c) 2005 Andreas Jaggi + Copyright (c) 2010-2011 RafaÅ‚ MiÅ‚ecki + + SDIO support + Copyright (c) 2009 Albert Herranz + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "b43.h" +#include "main.h" +#include "debugfs.h" +#include "phy_common.h" +#include "phy_g.h" +#include "phy_n.h" +#include "dma.h" +#include "pio.h" +#include "sysfs.h" +#include "xmit.h" +#include "lo.h" +#include "sdio.h" +#include + +MODULE_DESCRIPTION("Broadcom B43 wireless driver"); +MODULE_AUTHOR("Martin Langer"); +MODULE_AUTHOR("Stefano Brivio"); +MODULE_AUTHOR("Michael Buesch"); +MODULE_AUTHOR("Gábor Stefanik"); +MODULE_AUTHOR("RafaÅ‚ MiÅ‚ecki"); +MODULE_LICENSE("GPL"); + +MODULE_FIRMWARE("b43/ucode11.fw"); +MODULE_FIRMWARE("b43/ucode13.fw"); +MODULE_FIRMWARE("b43/ucode14.fw"); +MODULE_FIRMWARE("b43/ucode15.fw"); +MODULE_FIRMWARE("b43/ucode16_mimo.fw"); +MODULE_FIRMWARE("b43/ucode5.fw"); +MODULE_FIRMWARE("b43/ucode9.fw"); + +static int modparam_bad_frames_preempt; +module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); +MODULE_PARM_DESC(bad_frames_preempt, + "enable(1) / disable(0) Bad Frames Preemption"); + +static char modparam_fwpostfix[16]; +module_param_string(fwpostfix, modparam_fwpostfix, 16, 0444); +MODULE_PARM_DESC(fwpostfix, "Postfix for the .fw files to load."); + +static int modparam_hwpctl; +module_param_named(hwpctl, modparam_hwpctl, int, 0444); +MODULE_PARM_DESC(hwpctl, "Enable hardware-side power control (default off)"); + +static int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +static int modparam_hwtkip; +module_param_named(hwtkip, modparam_hwtkip, int, 0444); +MODULE_PARM_DESC(hwtkip, "Enable hardware tkip."); + +static int modparam_qos = 1; +module_param_named(qos, modparam_qos, int, 0444); +MODULE_PARM_DESC(qos, "Enable QOS support (default on)"); + +static int modparam_btcoex = 1; +module_param_named(btcoex, modparam_btcoex, int, 0444); +MODULE_PARM_DESC(btcoex, "Enable Bluetooth coexistence (default on)"); + +int b43_modparam_verbose = B43_VERBOSITY_DEFAULT; +module_param_named(verbose, b43_modparam_verbose, int, 0644); +MODULE_PARM_DESC(verbose, "Log message verbosity: 0=error, 1=warn, 2=info(default), 3=debug"); + +static int b43_modparam_pio = 0; +module_param_named(pio, b43_modparam_pio, int, 0644); +MODULE_PARM_DESC(pio, "Use PIO accesses by default: 0=DMA, 1=PIO"); + +static int modparam_allhwsupport = !IS_ENABLED(CONFIG_BRCMSMAC); +module_param_named(allhwsupport, modparam_allhwsupport, int, 0444); +MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if overlaps with the brcmsmac driver)"); + +#ifdef CONFIG_B43_BCMA +static const struct bcma_device_id b43_bcma_tbl[] = { + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x15, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1C, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1E, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x28, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x2A, BCMA_ANY_CLASS), + {}, +}; +MODULE_DEVICE_TABLE(bcma, b43_bcma_tbl); +#endif + +#ifdef CONFIG_B43_SSB +static const struct ssb_device_id b43_ssb_tbl[] = { + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 6), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 7), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 9), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 10), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 11), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 12), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 13), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 15), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 16), + {}, +}; +MODULE_DEVICE_TABLE(ssb, b43_ssb_tbl); +#endif + +/* Channel and ratetables are shared for all devices. + * They can't be const, because ieee80211 puts some precalculated + * data in there. This data is the same for all devices, so we don't + * get concurrency issues */ +#define RATETAB_ENT(_rateid, _flags) \ + { \ + .bitrate = B43_RATE_TO_BASE100KBPS(_rateid), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +/* + * NOTE: When changing this, sync with xmit.c's + * b43_plcp_get_bitrate_idx_* functions! + */ +static struct ieee80211_rate __b43_ratetable[] = { + RATETAB_ENT(B43_CCK_RATE_1MB, 0), + RATETAB_ENT(B43_CCK_RATE_2MB, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(B43_CCK_RATE_5MB, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(B43_CCK_RATE_11MB, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(B43_OFDM_RATE_6MB, 0), + RATETAB_ENT(B43_OFDM_RATE_9MB, 0), + RATETAB_ENT(B43_OFDM_RATE_12MB, 0), + RATETAB_ENT(B43_OFDM_RATE_18MB, 0), + RATETAB_ENT(B43_OFDM_RATE_24MB, 0), + RATETAB_ENT(B43_OFDM_RATE_36MB, 0), + RATETAB_ENT(B43_OFDM_RATE_48MB, 0), + RATETAB_ENT(B43_OFDM_RATE_54MB, 0), +}; + +#define b43_a_ratetable (__b43_ratetable + 4) +#define b43_a_ratetable_size 8 +#define b43_b_ratetable (__b43_ratetable + 0) +#define b43_b_ratetable_size 4 +#define b43_g_ratetable (__b43_ratetable + 0) +#define b43_g_ratetable_size 12 + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} +static struct ieee80211_channel b43_2ghz_chantable[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +/* No support for the last 3 channels (12, 13, 14) */ +#define b43_2ghz_chantable_limited_size 11 +#undef CHAN2G + +#define CHAN4G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 4000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} +#define CHAN5G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} +static struct ieee80211_channel b43_5ghz_nphy_chantable[] = { + CHAN4G(184, 0), CHAN4G(186, 0), + CHAN4G(188, 0), CHAN4G(190, 0), + CHAN4G(192, 0), CHAN4G(194, 0), + CHAN4G(196, 0), CHAN4G(198, 0), + CHAN4G(200, 0), CHAN4G(202, 0), + CHAN4G(204, 0), CHAN4G(206, 0), + CHAN4G(208, 0), CHAN4G(210, 0), + CHAN4G(212, 0), CHAN4G(214, 0), + CHAN4G(216, 0), CHAN4G(218, 0), + CHAN4G(220, 0), CHAN4G(222, 0), + CHAN4G(224, 0), CHAN4G(226, 0), + CHAN4G(228, 0), + CHAN5G(32, 0), CHAN5G(34, 0), + CHAN5G(36, 0), CHAN5G(38, 0), + CHAN5G(40, 0), CHAN5G(42, 0), + CHAN5G(44, 0), CHAN5G(46, 0), + CHAN5G(48, 0), CHAN5G(50, 0), + CHAN5G(52, 0), CHAN5G(54, 0), + CHAN5G(56, 0), CHAN5G(58, 0), + CHAN5G(60, 0), CHAN5G(62, 0), + CHAN5G(64, 0), CHAN5G(66, 0), + CHAN5G(68, 0), CHAN5G(70, 0), + CHAN5G(72, 0), CHAN5G(74, 0), + CHAN5G(76, 0), CHAN5G(78, 0), + CHAN5G(80, 0), CHAN5G(82, 0), + CHAN5G(84, 0), CHAN5G(86, 0), + CHAN5G(88, 0), CHAN5G(90, 0), + CHAN5G(92, 0), CHAN5G(94, 0), + CHAN5G(96, 0), CHAN5G(98, 0), + CHAN5G(100, 0), CHAN5G(102, 0), + CHAN5G(104, 0), CHAN5G(106, 0), + CHAN5G(108, 0), CHAN5G(110, 0), + CHAN5G(112, 0), CHAN5G(114, 0), + CHAN5G(116, 0), CHAN5G(118, 0), + CHAN5G(120, 0), CHAN5G(122, 0), + CHAN5G(124, 0), CHAN5G(126, 0), + CHAN5G(128, 0), CHAN5G(130, 0), + CHAN5G(132, 0), CHAN5G(134, 0), + CHAN5G(136, 0), CHAN5G(138, 0), + CHAN5G(140, 0), CHAN5G(142, 0), + CHAN5G(144, 0), CHAN5G(145, 0), + CHAN5G(146, 0), CHAN5G(147, 0), + CHAN5G(148, 0), CHAN5G(149, 0), + CHAN5G(150, 0), CHAN5G(151, 0), + CHAN5G(152, 0), CHAN5G(153, 0), + CHAN5G(154, 0), CHAN5G(155, 0), + CHAN5G(156, 0), CHAN5G(157, 0), + CHAN5G(158, 0), CHAN5G(159, 0), + CHAN5G(160, 0), CHAN5G(161, 0), + CHAN5G(162, 0), CHAN5G(163, 0), + CHAN5G(164, 0), CHAN5G(165, 0), + CHAN5G(166, 0), CHAN5G(168, 0), + CHAN5G(170, 0), CHAN5G(172, 0), + CHAN5G(174, 0), CHAN5G(176, 0), + CHAN5G(178, 0), CHAN5G(180, 0), + CHAN5G(182, 0), +}; + +static struct ieee80211_channel b43_5ghz_nphy_chantable_limited[] = { + CHAN5G(36, 0), CHAN5G(40, 0), + CHAN5G(44, 0), CHAN5G(48, 0), + CHAN5G(149, 0), CHAN5G(153, 0), + CHAN5G(157, 0), CHAN5G(161, 0), + CHAN5G(165, 0), +}; + +static struct ieee80211_channel b43_5ghz_aphy_chantable[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(149, 0), + CHAN5G(153, 0), CHAN5G(157, 0), + CHAN5G(161, 0), CHAN5G(165, 0), + CHAN5G(184, 0), CHAN5G(188, 0), + CHAN5G(192, 0), CHAN5G(196, 0), + CHAN5G(200, 0), CHAN5G(204, 0), + CHAN5G(208, 0), CHAN5G(212, 0), + CHAN5G(216, 0), +}; +#undef CHAN4G +#undef CHAN5G + +static struct ieee80211_supported_band b43_band_5GHz_nphy = { + .band = IEEE80211_BAND_5GHZ, + .channels = b43_5ghz_nphy_chantable, + .n_channels = ARRAY_SIZE(b43_5ghz_nphy_chantable), + .bitrates = b43_a_ratetable, + .n_bitrates = b43_a_ratetable_size, +}; + +static struct ieee80211_supported_band b43_band_5GHz_nphy_limited = { + .band = IEEE80211_BAND_5GHZ, + .channels = b43_5ghz_nphy_chantable_limited, + .n_channels = ARRAY_SIZE(b43_5ghz_nphy_chantable_limited), + .bitrates = b43_a_ratetable, + .n_bitrates = b43_a_ratetable_size, +}; + +static struct ieee80211_supported_band b43_band_5GHz_aphy = { + .band = IEEE80211_BAND_5GHZ, + .channels = b43_5ghz_aphy_chantable, + .n_channels = ARRAY_SIZE(b43_5ghz_aphy_chantable), + .bitrates = b43_a_ratetable, + .n_bitrates = b43_a_ratetable_size, +}; + +static struct ieee80211_supported_band b43_band_2GHz = { + .band = IEEE80211_BAND_2GHZ, + .channels = b43_2ghz_chantable, + .n_channels = ARRAY_SIZE(b43_2ghz_chantable), + .bitrates = b43_g_ratetable, + .n_bitrates = b43_g_ratetable_size, +}; + +static struct ieee80211_supported_band b43_band_2ghz_limited = { + .band = IEEE80211_BAND_2GHZ, + .channels = b43_2ghz_chantable, + .n_channels = b43_2ghz_chantable_limited_size, + .bitrates = b43_g_ratetable, + .n_bitrates = b43_g_ratetable_size, +}; + +static void b43_wireless_core_exit(struct b43_wldev *dev); +static int b43_wireless_core_init(struct b43_wldev *dev); +static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev); +static int b43_wireless_core_start(struct b43_wldev *dev); +static void b43_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, + u32 changed); + +static int b43_ratelimit(struct b43_wl *wl) +{ + if (!wl || !wl->current_dev) + return 1; + if (b43_status(wl->current_dev) < B43_STAT_STARTED) + return 1; + /* We are up and running. + * Ratelimit the messages to avoid DoS over the net. */ + return net_ratelimit(); +} + +void b43info(struct b43_wl *wl, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (b43_modparam_verbose < B43_VERBOSITY_INFO) + return; + if (!b43_ratelimit(wl)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + printk(KERN_INFO "b43-%s: %pV", + (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); + + va_end(args); +} + +void b43err(struct b43_wl *wl, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (b43_modparam_verbose < B43_VERBOSITY_ERROR) + return; + if (!b43_ratelimit(wl)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + printk(KERN_ERR "b43-%s ERROR: %pV", + (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); + + va_end(args); +} + +void b43warn(struct b43_wl *wl, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (b43_modparam_verbose < B43_VERBOSITY_WARN) + return; + if (!b43_ratelimit(wl)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + printk(KERN_WARNING "b43-%s warning: %pV", + (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); + + va_end(args); +} + +void b43dbg(struct b43_wl *wl, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (b43_modparam_verbose < B43_VERBOSITY_DEBUG) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + printk(KERN_DEBUG "b43-%s debug: %pV", + (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); + + va_end(args); +} + +static void b43_ram_write(struct b43_wldev *dev, u16 offset, u32 val) +{ + u32 macctl; + + B43_WARN_ON(offset % 4 != 0); + + macctl = b43_read32(dev, B43_MMIO_MACCTL); + if (macctl & B43_MACCTL_BE) + val = swab32(val); + + b43_write32(dev, B43_MMIO_RAM_CONTROL, offset); + mmiowb(); + b43_write32(dev, B43_MMIO_RAM_DATA, val); +} + +static inline void b43_shm_control_word(struct b43_wldev *dev, + u16 routing, u16 offset) +{ + u32 control; + + /* "offset" is the WORD offset. */ + control = routing; + control <<= 16; + control |= offset; + b43_write32(dev, B43_MMIO_SHM_CONTROL, control); +} + +u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset) +{ + u32 ret; + + if (routing == B43_SHM_SHARED) { + B43_WARN_ON(offset & 0x0001); + if (offset & 0x0003) { + /* Unaligned access */ + b43_shm_control_word(dev, routing, offset >> 2); + ret = b43_read16(dev, B43_MMIO_SHM_DATA_UNALIGNED); + b43_shm_control_word(dev, routing, (offset >> 2) + 1); + ret |= ((u32)b43_read16(dev, B43_MMIO_SHM_DATA)) << 16; + + goto out; + } + offset >>= 2; + } + b43_shm_control_word(dev, routing, offset); + ret = b43_read32(dev, B43_MMIO_SHM_DATA); +out: + return ret; +} + +u16 b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset) +{ + u16 ret; + + if (routing == B43_SHM_SHARED) { + B43_WARN_ON(offset & 0x0001); + if (offset & 0x0003) { + /* Unaligned access */ + b43_shm_control_word(dev, routing, offset >> 2); + ret = b43_read16(dev, B43_MMIO_SHM_DATA_UNALIGNED); + + goto out; + } + offset >>= 2; + } + b43_shm_control_word(dev, routing, offset); + ret = b43_read16(dev, B43_MMIO_SHM_DATA); +out: + return ret; +} + +void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value) +{ + if (routing == B43_SHM_SHARED) { + B43_WARN_ON(offset & 0x0001); + if (offset & 0x0003) { + /* Unaligned access */ + b43_shm_control_word(dev, routing, offset >> 2); + b43_write16(dev, B43_MMIO_SHM_DATA_UNALIGNED, + value & 0xFFFF); + b43_shm_control_word(dev, routing, (offset >> 2) + 1); + b43_write16(dev, B43_MMIO_SHM_DATA, + (value >> 16) & 0xFFFF); + return; + } + offset >>= 2; + } + b43_shm_control_word(dev, routing, offset); + b43_write32(dev, B43_MMIO_SHM_DATA, value); +} + +void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value) +{ + if (routing == B43_SHM_SHARED) { + B43_WARN_ON(offset & 0x0001); + if (offset & 0x0003) { + /* Unaligned access */ + b43_shm_control_word(dev, routing, offset >> 2); + b43_write16(dev, B43_MMIO_SHM_DATA_UNALIGNED, value); + return; + } + offset >>= 2; + } + b43_shm_control_word(dev, routing, offset); + b43_write16(dev, B43_MMIO_SHM_DATA, value); +} + +/* Read HostFlags */ +u64 b43_hf_read(struct b43_wldev *dev) +{ + u64 ret; + + ret = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF3); + ret <<= 16; + ret |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF2); + ret <<= 16; + ret |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF1); + + return ret; +} + +/* Write HostFlags */ +void b43_hf_write(struct b43_wldev *dev, u64 value) +{ + u16 lo, mi, hi; + + lo = (value & 0x00000000FFFFULL); + mi = (value & 0x0000FFFF0000ULL) >> 16; + hi = (value & 0xFFFF00000000ULL) >> 32; + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF1, lo); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF2, mi); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF3, hi); +} + +/* Read the firmware capabilities bitmask (Opensource firmware only) */ +static u16 b43_fwcapa_read(struct b43_wldev *dev) +{ + B43_WARN_ON(!dev->fw.opensource); + return b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_FWCAPA); +} + +void b43_tsf_read(struct b43_wldev *dev, u64 *tsf) +{ + u32 low, high; + + B43_WARN_ON(dev->dev->core_rev < 3); + + /* The hardware guarantees us an atomic read, if we + * read the low register first. */ + low = b43_read32(dev, B43_MMIO_REV3PLUS_TSF_LOW); + high = b43_read32(dev, B43_MMIO_REV3PLUS_TSF_HIGH); + + *tsf = high; + *tsf <<= 32; + *tsf |= low; +} + +static void b43_time_lock(struct b43_wldev *dev) +{ + b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_TBTTHOLD); + /* Commit the write */ + b43_read32(dev, B43_MMIO_MACCTL); +} + +static void b43_time_unlock(struct b43_wldev *dev) +{ + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_TBTTHOLD, 0); + /* Commit the write */ + b43_read32(dev, B43_MMIO_MACCTL); +} + +static void b43_tsf_write_locked(struct b43_wldev *dev, u64 tsf) +{ + u32 low, high; + + B43_WARN_ON(dev->dev->core_rev < 3); + + low = tsf; + high = (tsf >> 32); + /* The hardware guarantees us an atomic write, if we + * write the low register first. */ + b43_write32(dev, B43_MMIO_REV3PLUS_TSF_LOW, low); + mmiowb(); + b43_write32(dev, B43_MMIO_REV3PLUS_TSF_HIGH, high); + mmiowb(); +} + +void b43_tsf_write(struct b43_wldev *dev, u64 tsf) +{ + b43_time_lock(dev); + b43_tsf_write_locked(dev, tsf); + b43_time_unlock(dev); +} + +static +void b43_macfilter_set(struct b43_wldev *dev, u16 offset, const u8 *mac) +{ + static const u8 zero_addr[ETH_ALEN] = { 0 }; + u16 data; + + if (!mac) + mac = zero_addr; + + offset |= 0x0020; + b43_write16(dev, B43_MMIO_MACFILTER_CONTROL, offset); + + data = mac[0]; + data |= mac[1] << 8; + b43_write16(dev, B43_MMIO_MACFILTER_DATA, data); + data = mac[2]; + data |= mac[3] << 8; + b43_write16(dev, B43_MMIO_MACFILTER_DATA, data); + data = mac[4]; + data |= mac[5] << 8; + b43_write16(dev, B43_MMIO_MACFILTER_DATA, data); +} + +static void b43_write_mac_bssid_templates(struct b43_wldev *dev) +{ + const u8 *mac; + const u8 *bssid; + u8 mac_bssid[ETH_ALEN * 2]; + int i; + u32 tmp; + + bssid = dev->wl->bssid; + mac = dev->wl->mac_addr; + + b43_macfilter_set(dev, B43_MACFILTER_BSSID, bssid); + + memcpy(mac_bssid, mac, ETH_ALEN); + memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN); + + /* Write our MAC address and BSSID to template ram */ + for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) { + tmp = (u32) (mac_bssid[i + 0]); + tmp |= (u32) (mac_bssid[i + 1]) << 8; + tmp |= (u32) (mac_bssid[i + 2]) << 16; + tmp |= (u32) (mac_bssid[i + 3]) << 24; + b43_ram_write(dev, 0x20 + i, tmp); + } +} + +static void b43_upload_card_macaddress(struct b43_wldev *dev) +{ + b43_write_mac_bssid_templates(dev); + b43_macfilter_set(dev, B43_MACFILTER_SELF, dev->wl->mac_addr); +} + +static void b43_set_slot_time(struct b43_wldev *dev, u16 slot_time) +{ + /* slot_time is in usec. */ + /* This test used to exit for all but a G PHY. */ + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + return; + b43_write16(dev, B43_MMIO_IFSSLOT, 510 + slot_time); + /* Shared memory location 0x0010 is the slot time and should be + * set to slot_time; however, this register is initially 0 and changing + * the value adversely affects the transmit rate for BCM4311 + * devices. Until this behavior is unterstood, delete this step + * + * b43_shm_write16(dev, B43_SHM_SHARED, 0x0010, slot_time); + */ +} + +static void b43_short_slot_timing_enable(struct b43_wldev *dev) +{ + b43_set_slot_time(dev, 9); +} + +static void b43_short_slot_timing_disable(struct b43_wldev *dev) +{ + b43_set_slot_time(dev, 20); +} + +/* DummyTransmission function, as documented on + * http://bcm-v4.sipsolutions.net/802.11/DummyTransmission + */ +void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on) +{ + struct b43_phy *phy = &dev->phy; + unsigned int i, max_loop; + u16 value; + u32 buffer[5] = { + 0x00000000, + 0x00D40000, + 0x00000000, + 0x01000000, + 0x00000000, + }; + + if (ofdm) { + max_loop = 0x1E; + buffer[0] = 0x000201CC; + } else { + max_loop = 0xFA; + buffer[0] = 0x000B846E; + } + + for (i = 0; i < 5; i++) + b43_ram_write(dev, i * 4, buffer[i]); + + b43_write16(dev, B43_MMIO_XMTSEL, 0x0000); + + if (dev->dev->core_rev < 11) + b43_write16(dev, B43_MMIO_WEPCTL, 0x0000); + else + b43_write16(dev, B43_MMIO_WEPCTL, 0x0100); + + value = (ofdm ? 0x41 : 0x40); + b43_write16(dev, B43_MMIO_TXE0_PHYCTL, value); + if (phy->type == B43_PHYTYPE_N || phy->type == B43_PHYTYPE_LP || + phy->type == B43_PHYTYPE_LCN) + b43_write16(dev, B43_MMIO_TXE0_PHYCTL1, 0x1A02); + + b43_write16(dev, B43_MMIO_TXE0_WM_0, 0x0000); + b43_write16(dev, B43_MMIO_TXE0_WM_1, 0x0000); + + b43_write16(dev, B43_MMIO_XMTTPLATETXPTR, 0x0000); + b43_write16(dev, B43_MMIO_XMTTXCNT, 0x0014); + b43_write16(dev, B43_MMIO_XMTSEL, 0x0826); + b43_write16(dev, B43_MMIO_TXE0_CTL, 0x0000); + + if (!pa_on && phy->type == B43_PHYTYPE_N) + ; /*b43_nphy_pa_override(dev, false) */ + + switch (phy->type) { + case B43_PHYTYPE_N: + case B43_PHYTYPE_LCN: + b43_write16(dev, B43_MMIO_TXE0_AUX, 0x00D0); + break; + case B43_PHYTYPE_LP: + b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0050); + break; + default: + b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0030); + } + b43_read16(dev, B43_MMIO_TXE0_AUX); + + if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) + b43_radio_write16(dev, 0x0051, 0x0017); + for (i = 0x00; i < max_loop; i++) { + value = b43_read16(dev, B43_MMIO_TXE0_STATUS); + if (value & 0x0080) + break; + udelay(10); + } + for (i = 0x00; i < 0x0A; i++) { + value = b43_read16(dev, B43_MMIO_TXE0_STATUS); + if (value & 0x0400) + break; + udelay(10); + } + for (i = 0x00; i < 0x19; i++) { + value = b43_read16(dev, B43_MMIO_IFSSTAT); + if (!(value & 0x0100)) + break; + udelay(10); + } + if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) + b43_radio_write16(dev, 0x0051, 0x0037); +} + +static void key_write(struct b43_wldev *dev, + u8 index, u8 algorithm, const u8 *key) +{ + unsigned int i; + u32 offset; + u16 value; + u16 kidx; + + /* Key index/algo block */ + kidx = b43_kidx_to_fw(dev, index); + value = ((kidx << 4) | algorithm); + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_KEYIDXBLOCK + (kidx * 2), value); + + /* Write the key to the Key Table Pointer offset */ + offset = dev->ktp + (index * B43_SEC_KEYSIZE); + for (i = 0; i < B43_SEC_KEYSIZE; i += 2) { + value = key[i]; + value |= (u16) (key[i + 1]) << 8; + b43_shm_write16(dev, B43_SHM_SHARED, offset + i, value); + } +} + +static void keymac_write(struct b43_wldev *dev, u8 index, const u8 *addr) +{ + u32 addrtmp[2] = { 0, 0, }; + u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; + + if (b43_new_kidx_api(dev)) + pairwise_keys_start = B43_NR_GROUP_KEYS; + + B43_WARN_ON(index < pairwise_keys_start); + /* We have four default TX keys and possibly four default RX keys. + * Physical mac 0 is mapped to physical key 4 or 8, depending + * on the firmware version. + * So we must adjust the index here. + */ + index -= pairwise_keys_start; + B43_WARN_ON(index >= B43_NR_PAIRWISE_KEYS); + + if (addr) { + addrtmp[0] = addr[0]; + addrtmp[0] |= ((u32) (addr[1]) << 8); + addrtmp[0] |= ((u32) (addr[2]) << 16); + addrtmp[0] |= ((u32) (addr[3]) << 24); + addrtmp[1] = addr[4]; + addrtmp[1] |= ((u32) (addr[5]) << 8); + } + + /* Receive match transmitter address (RCMTA) mechanism */ + b43_shm_write32(dev, B43_SHM_RCMTA, + (index * 2) + 0, addrtmp[0]); + b43_shm_write16(dev, B43_SHM_RCMTA, + (index * 2) + 1, addrtmp[1]); +} + +/* The ucode will use phase1 key with TEK key to decrypt rx packets. + * When a packet is received, the iv32 is checked. + * - if it doesn't the packet is returned without modification (and software + * decryption can be done). That's what happen when iv16 wrap. + * - if it does, the rc4 key is computed, and decryption is tried. + * Either it will success and B43_RX_MAC_DEC is returned, + * either it fails and B43_RX_MAC_DEC|B43_RX_MAC_DECERR is returned + * and the packet is not usable (it got modified by the ucode). + * So in order to never have B43_RX_MAC_DECERR, we should provide + * a iv32 and phase1key that match. Because we drop packets in case of + * B43_RX_MAC_DECERR, if we have a correct iv32 but a wrong phase1key, all + * packets will be lost without higher layer knowing (ie no resync possible + * until next wrap). + * + * NOTE : this should support 50 key like RCMTA because + * (B43_SHM_SH_KEYIDXBLOCK - B43_SHM_SH_TKIPTSCTTAK)/14 = 50 + */ +static void rx_tkip_phase1_write(struct b43_wldev *dev, u8 index, u32 iv32, + u16 *phase1key) +{ + unsigned int i; + u32 offset; + u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; + + if (!modparam_hwtkip) + return; + + if (b43_new_kidx_api(dev)) + pairwise_keys_start = B43_NR_GROUP_KEYS; + + B43_WARN_ON(index < pairwise_keys_start); + /* We have four default TX keys and possibly four default RX keys. + * Physical mac 0 is mapped to physical key 4 or 8, depending + * on the firmware version. + * So we must adjust the index here. + */ + index -= pairwise_keys_start; + B43_WARN_ON(index >= B43_NR_PAIRWISE_KEYS); + + if (b43_debug(dev, B43_DBG_KEYS)) { + b43dbg(dev->wl, "rx_tkip_phase1_write : idx 0x%x, iv32 0x%x\n", + index, iv32); + } + /* Write the key to the RX tkip shared mem */ + offset = B43_SHM_SH_TKIPTSCTTAK + index * (10 + 4); + for (i = 0; i < 10; i += 2) { + b43_shm_write16(dev, B43_SHM_SHARED, offset + i, + phase1key ? phase1key[i / 2] : 0); + } + b43_shm_write16(dev, B43_SHM_SHARED, offset + i, iv32); + b43_shm_write16(dev, B43_SHM_SHARED, offset + i + 2, iv32 >> 16); +} + +static void b43_op_update_tkip_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, + u32 iv32, u16 *phase1key) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev; + int index = keyconf->hw_key_idx; + + if (B43_WARN_ON(!modparam_hwtkip)) + return; + + /* This is only called from the RX path through mac80211, where + * our mutex is already locked. */ + B43_WARN_ON(!mutex_is_locked(&wl->mutex)); + dev = wl->current_dev; + B43_WARN_ON(!dev || b43_status(dev) < B43_STAT_INITIALIZED); + + keymac_write(dev, index, NULL); /* First zero out mac to avoid race */ + + rx_tkip_phase1_write(dev, index, iv32, phase1key); + /* only pairwise TKIP keys are supported right now */ + if (WARN_ON(!sta)) + return; + keymac_write(dev, index, sta->addr); +} + +static void do_key_write(struct b43_wldev *dev, + u8 index, u8 algorithm, + const u8 *key, size_t key_len, const u8 *mac_addr) +{ + u8 buf[B43_SEC_KEYSIZE] = { 0, }; + u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; + + if (b43_new_kidx_api(dev)) + pairwise_keys_start = B43_NR_GROUP_KEYS; + + B43_WARN_ON(index >= ARRAY_SIZE(dev->key)); + B43_WARN_ON(key_len > B43_SEC_KEYSIZE); + + if (index >= pairwise_keys_start) + keymac_write(dev, index, NULL); /* First zero out mac. */ + if (algorithm == B43_SEC_ALGO_TKIP) { + /* + * We should provide an initial iv32, phase1key pair. + * We could start with iv32=0 and compute the corresponding + * phase1key, but this means calling ieee80211_get_tkip_key + * with a fake skb (or export other tkip function). + * Because we are lazy we hope iv32 won't start with + * 0xffffffff and let's b43_op_update_tkip_key provide a + * correct pair. + */ + rx_tkip_phase1_write(dev, index, 0xffffffff, (u16*)buf); + } else if (index >= pairwise_keys_start) /* clear it */ + rx_tkip_phase1_write(dev, index, 0, NULL); + if (key) + memcpy(buf, key, key_len); + key_write(dev, index, algorithm, buf); + if (index >= pairwise_keys_start) + keymac_write(dev, index, mac_addr); + + dev->key[index].algorithm = algorithm; +} + +static int b43_key_write(struct b43_wldev *dev, + int index, u8 algorithm, + const u8 *key, size_t key_len, + const u8 *mac_addr, + struct ieee80211_key_conf *keyconf) +{ + int i; + int pairwise_keys_start; + + /* For ALG_TKIP the key is encoded as a 256-bit (32 byte) data block: + * - Temporal Encryption Key (128 bits) + * - Temporal Authenticator Tx MIC Key (64 bits) + * - Temporal Authenticator Rx MIC Key (64 bits) + * + * Hardware only store TEK + */ + if (algorithm == B43_SEC_ALGO_TKIP && key_len == 32) + key_len = 16; + if (key_len > B43_SEC_KEYSIZE) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(dev->key); i++) { + /* Check that we don't already have this key. */ + B43_WARN_ON(dev->key[i].keyconf == keyconf); + } + if (index < 0) { + /* Pairwise key. Get an empty slot for the key. */ + if (b43_new_kidx_api(dev)) + pairwise_keys_start = B43_NR_GROUP_KEYS; + else + pairwise_keys_start = B43_NR_GROUP_KEYS * 2; + for (i = pairwise_keys_start; + i < pairwise_keys_start + B43_NR_PAIRWISE_KEYS; + i++) { + B43_WARN_ON(i >= ARRAY_SIZE(dev->key)); + if (!dev->key[i].keyconf) { + /* found empty */ + index = i; + break; + } + } + if (index < 0) { + b43warn(dev->wl, "Out of hardware key memory\n"); + return -ENOSPC; + } + } else + B43_WARN_ON(index > 3); + + do_key_write(dev, index, algorithm, key, key_len, mac_addr); + if ((index <= 3) && !b43_new_kidx_api(dev)) { + /* Default RX key */ + B43_WARN_ON(mac_addr); + do_key_write(dev, index + 4, algorithm, key, key_len, NULL); + } + keyconf->hw_key_idx = index; + dev->key[index].keyconf = keyconf; + + return 0; +} + +static int b43_key_clear(struct b43_wldev *dev, int index) +{ + if (B43_WARN_ON((index < 0) || (index >= ARRAY_SIZE(dev->key)))) + return -EINVAL; + do_key_write(dev, index, B43_SEC_ALGO_NONE, + NULL, B43_SEC_KEYSIZE, NULL); + if ((index <= 3) && !b43_new_kidx_api(dev)) { + do_key_write(dev, index + 4, B43_SEC_ALGO_NONE, + NULL, B43_SEC_KEYSIZE, NULL); + } + dev->key[index].keyconf = NULL; + + return 0; +} + +static void b43_clear_keys(struct b43_wldev *dev) +{ + int i, count; + + if (b43_new_kidx_api(dev)) + count = B43_NR_GROUP_KEYS + B43_NR_PAIRWISE_KEYS; + else + count = B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS; + for (i = 0; i < count; i++) + b43_key_clear(dev, i); +} + +static void b43_dump_keymemory(struct b43_wldev *dev) +{ + unsigned int i, index, count, offset, pairwise_keys_start; + u8 mac[ETH_ALEN]; + u16 algo; + u32 rcmta0; + u16 rcmta1; + u64 hf; + struct b43_key *key; + + if (!b43_debug(dev, B43_DBG_KEYS)) + return; + + hf = b43_hf_read(dev); + b43dbg(dev->wl, "Hardware key memory dump: USEDEFKEYS=%u\n", + !!(hf & B43_HF_USEDEFKEYS)); + if (b43_new_kidx_api(dev)) { + pairwise_keys_start = B43_NR_GROUP_KEYS; + count = B43_NR_GROUP_KEYS + B43_NR_PAIRWISE_KEYS; + } else { + pairwise_keys_start = B43_NR_GROUP_KEYS * 2; + count = B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS; + } + for (index = 0; index < count; index++) { + key = &(dev->key[index]); + printk(KERN_DEBUG "Key slot %02u: %s", + index, (key->keyconf == NULL) ? " " : "*"); + offset = dev->ktp + (index * B43_SEC_KEYSIZE); + for (i = 0; i < B43_SEC_KEYSIZE; i += 2) { + u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, offset + i); + printk("%02X%02X", (tmp & 0xFF), ((tmp >> 8) & 0xFF)); + } + + algo = b43_shm_read16(dev, B43_SHM_SHARED, + B43_SHM_SH_KEYIDXBLOCK + (index * 2)); + printk(" Algo: %04X/%02X", algo, key->algorithm); + + if (index >= pairwise_keys_start) { + if (key->algorithm == B43_SEC_ALGO_TKIP) { + printk(" TKIP: "); + offset = B43_SHM_SH_TKIPTSCTTAK + (index - 4) * (10 + 4); + for (i = 0; i < 14; i += 2) { + u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, offset + i); + printk("%02X%02X", (tmp & 0xFF), ((tmp >> 8) & 0xFF)); + } + } + rcmta0 = b43_shm_read32(dev, B43_SHM_RCMTA, + ((index - pairwise_keys_start) * 2) + 0); + rcmta1 = b43_shm_read16(dev, B43_SHM_RCMTA, + ((index - pairwise_keys_start) * 2) + 1); + *((__le32 *)(&mac[0])) = cpu_to_le32(rcmta0); + *((__le16 *)(&mac[4])) = cpu_to_le16(rcmta1); + printk(" MAC: %pM", mac); + } else + printk(" DEFAULT KEY"); + printk("\n"); + } +} + +void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags) +{ + u32 macctl; + u16 ucstat; + bool hwps; + bool awake; + int i; + + B43_WARN_ON((ps_flags & B43_PS_ENABLED) && + (ps_flags & B43_PS_DISABLED)); + B43_WARN_ON((ps_flags & B43_PS_AWAKE) && (ps_flags & B43_PS_ASLEEP)); + + if (ps_flags & B43_PS_ENABLED) { + hwps = true; + } else if (ps_flags & B43_PS_DISABLED) { + hwps = false; + } else { + //TODO: If powersave is not off and FIXME is not set and we are not in adhoc + // and thus is not an AP and we are associated, set bit 25 + } + if (ps_flags & B43_PS_AWAKE) { + awake = true; + } else if (ps_flags & B43_PS_ASLEEP) { + awake = false; + } else { + //TODO: If the device is awake or this is an AP, or we are scanning, or FIXME, + // or we are associated, or FIXME, or the latest PS-Poll packet sent was + // successful, set bit26 + } + +/* FIXME: For now we force awake-on and hwps-off */ + hwps = false; + awake = true; + + macctl = b43_read32(dev, B43_MMIO_MACCTL); + if (hwps) + macctl |= B43_MACCTL_HWPS; + else + macctl &= ~B43_MACCTL_HWPS; + if (awake) + macctl |= B43_MACCTL_AWAKE; + else + macctl &= ~B43_MACCTL_AWAKE; + b43_write32(dev, B43_MMIO_MACCTL, macctl); + /* Commit write */ + b43_read32(dev, B43_MMIO_MACCTL); + if (awake && dev->dev->core_rev >= 5) { + /* Wait for the microcode to wake up. */ + for (i = 0; i < 100; i++) { + ucstat = b43_shm_read16(dev, B43_SHM_SHARED, + B43_SHM_SH_UCODESTAT); + if (ucstat != B43_SHM_SH_UCODESTAT_SLEEP) + break; + udelay(10); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/BmacCorePllReset */ +void b43_wireless_core_phy_pll_reset(struct b43_wldev *dev) +{ + struct bcma_drv_cc *bcma_cc __maybe_unused; + struct ssb_chipcommon *ssb_cc __maybe_unused; + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + bcma_cc = &dev->dev->bdev->bus->drv_cc; + + bcma_cc_write32(bcma_cc, BCMA_CC_CHIPCTL_ADDR, 0); + bcma_cc_mask32(bcma_cc, BCMA_CC_CHIPCTL_DATA, ~0x4); + bcma_cc_set32(bcma_cc, BCMA_CC_CHIPCTL_DATA, 0x4); + bcma_cc_mask32(bcma_cc, BCMA_CC_CHIPCTL_DATA, ~0x4); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + ssb_cc = &dev->dev->sdev->bus->chipco; + + chipco_write32(ssb_cc, SSB_CHIPCO_CHIPCTL_ADDR, 0); + chipco_mask32(ssb_cc, SSB_CHIPCO_CHIPCTL_DATA, ~0x4); + chipco_set32(ssb_cc, SSB_CHIPCO_CHIPCTL_DATA, 0x4); + chipco_mask32(ssb_cc, SSB_CHIPCO_CHIPCTL_DATA, ~0x4); + break; +#endif + } +} + +#ifdef CONFIG_B43_BCMA +static void b43_bcma_phy_reset(struct b43_wldev *dev) +{ + u32 flags; + + /* Put PHY into reset */ + flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + flags |= B43_BCMA_IOCTL_PHY_RESET; + flags |= B43_BCMA_IOCTL_PHY_BW_20MHZ; /* Make 20 MHz def */ + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags); + udelay(2); + + b43_phy_take_out_of_reset(dev); +} + +static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode) +{ + u32 req = B43_BCMA_CLKCTLST_80211_PLL_REQ | + B43_BCMA_CLKCTLST_PHY_PLL_REQ; + u32 status = B43_BCMA_CLKCTLST_80211_PLL_ST | + B43_BCMA_CLKCTLST_PHY_PLL_ST; + u32 flags; + + flags = B43_BCMA_IOCTL_PHY_CLKEN; + if (gmode) + flags |= B43_BCMA_IOCTL_GMODE; + b43_device_enable(dev, flags); + + if (dev->phy.type == B43_PHYTYPE_AC) { + u16 tmp; + + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~B43_BCMA_IOCTL_DAC; + tmp |= 0x100; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp |= B43_BCMA_IOCTL_PHY_CLKEN; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + } + + bcma_core_set_clockmode(dev->dev->bdev, BCMA_CLKMODE_FAST); + b43_bcma_phy_reset(dev); + bcma_core_pll_ctl(dev->dev->bdev, req, status, true); +} +#endif + +#ifdef CONFIG_B43_SSB +static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode) +{ + u32 flags = 0; + + if (gmode) + flags |= B43_TMSLOW_GMODE; + flags |= B43_TMSLOW_PHYCLKEN; + flags |= B43_TMSLOW_PHYRESET; + if (dev->phy.type == B43_PHYTYPE_N) + flags |= B43_TMSLOW_PHY_BANDWIDTH_20MHZ; /* Make 20 MHz def */ + b43_device_enable(dev, flags); + msleep(2); /* Wait for the PLL to turn on. */ + + b43_phy_take_out_of_reset(dev); +} +#endif + +void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode) +{ + u32 macctl; + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + b43_bcma_wireless_core_reset(dev, gmode); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + b43_ssb_wireless_core_reset(dev, gmode); + break; +#endif + } + + /* Turn Analog ON, but only if we already know the PHY-type. + * This protects against very early setup where we don't know the + * PHY-type, yet. wireless_core_reset will be called once again later, + * when we know the PHY-type. */ + if (dev->phy.ops) + dev->phy.ops->switch_analog(dev, 1); + + macctl = b43_read32(dev, B43_MMIO_MACCTL); + macctl &= ~B43_MACCTL_GMODE; + if (gmode) + macctl |= B43_MACCTL_GMODE; + macctl |= B43_MACCTL_IHR_ENABLED; + b43_write32(dev, B43_MMIO_MACCTL, macctl); +} + +static void handle_irq_transmit_status(struct b43_wldev *dev) +{ + u32 v0, v1; + u16 tmp; + struct b43_txstatus stat; + + while (1) { + v0 = b43_read32(dev, B43_MMIO_XMITSTAT_0); + if (!(v0 & 0x00000001)) + break; + v1 = b43_read32(dev, B43_MMIO_XMITSTAT_1); + + stat.cookie = (v0 >> 16); + stat.seq = (v1 & 0x0000FFFF); + stat.phy_stat = ((v1 & 0x00FF0000) >> 16); + tmp = (v0 & 0x0000FFFF); + stat.frame_count = ((tmp & 0xF000) >> 12); + stat.rts_count = ((tmp & 0x0F00) >> 8); + stat.supp_reason = ((tmp & 0x001C) >> 2); + stat.pm_indicated = !!(tmp & 0x0080); + stat.intermediate = !!(tmp & 0x0040); + stat.for_ampdu = !!(tmp & 0x0020); + stat.acked = !!(tmp & 0x0002); + + b43_handle_txstatus(dev, &stat); + } +} + +static void drain_txstatus_queue(struct b43_wldev *dev) +{ + u32 dummy; + + if (dev->dev->core_rev < 5) + return; + /* Read all entries from the microcode TXstatus FIFO + * and throw them away. + */ + while (1) { + dummy = b43_read32(dev, B43_MMIO_XMITSTAT_0); + if (!(dummy & 0x00000001)) + break; + dummy = b43_read32(dev, B43_MMIO_XMITSTAT_1); + } +} + +static u32 b43_jssi_read(struct b43_wldev *dev) +{ + u32 val = 0; + + val = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI1); + val <<= 16; + val |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI0); + + return val; +} + +static void b43_jssi_write(struct b43_wldev *dev, u32 jssi) +{ + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI0, + (jssi & 0x0000FFFF)); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_JSSI1, + (jssi & 0xFFFF0000) >> 16); +} + +static void b43_generate_noise_sample(struct b43_wldev *dev) +{ + b43_jssi_write(dev, 0x7F7F7F7F); + b43_write32(dev, B43_MMIO_MACCMD, + b43_read32(dev, B43_MMIO_MACCMD) | B43_MACCMD_BGNOISE); +} + +static void b43_calculate_link_quality(struct b43_wldev *dev) +{ + /* Top half of Link Quality calculation. */ + + if (dev->phy.type != B43_PHYTYPE_G) + return; + if (dev->noisecalc.calculation_running) + return; + dev->noisecalc.calculation_running = true; + dev->noisecalc.nr_samples = 0; + + b43_generate_noise_sample(dev); +} + +static void handle_irq_noise(struct b43_wldev *dev) +{ + struct b43_phy_g *phy = dev->phy.g; + u16 tmp; + u8 noise[4]; + u8 i, j; + s32 average; + + /* Bottom half of Link Quality calculation. */ + + if (dev->phy.type != B43_PHYTYPE_G) + return; + + /* Possible race condition: It might be possible that the user + * changed to a different channel in the meantime since we + * started the calculation. We ignore that fact, since it's + * not really that much of a problem. The background noise is + * an estimation only anyway. Slightly wrong results will get damped + * by the averaging of the 8 sample rounds. Additionally the + * value is shortlived. So it will be replaced by the next noise + * calculation round soon. */ + + B43_WARN_ON(!dev->noisecalc.calculation_running); + *((__le32 *)noise) = cpu_to_le32(b43_jssi_read(dev)); + if (noise[0] == 0x7F || noise[1] == 0x7F || + noise[2] == 0x7F || noise[3] == 0x7F) + goto generate_new; + + /* Get the noise samples. */ + B43_WARN_ON(dev->noisecalc.nr_samples >= 8); + i = dev->noisecalc.nr_samples; + noise[0] = clamp_val(noise[0], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + noise[1] = clamp_val(noise[1], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + noise[2] = clamp_val(noise[2], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + noise[3] = clamp_val(noise[3], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + dev->noisecalc.samples[i][0] = phy->nrssi_lt[noise[0]]; + dev->noisecalc.samples[i][1] = phy->nrssi_lt[noise[1]]; + dev->noisecalc.samples[i][2] = phy->nrssi_lt[noise[2]]; + dev->noisecalc.samples[i][3] = phy->nrssi_lt[noise[3]]; + dev->noisecalc.nr_samples++; + if (dev->noisecalc.nr_samples == 8) { + /* Calculate the Link Quality by the noise samples. */ + average = 0; + for (i = 0; i < 8; i++) { + for (j = 0; j < 4; j++) + average += dev->noisecalc.samples[i][j]; + } + average /= (8 * 4); + average *= 125; + average += 64; + average /= 128; + tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x40C); + tmp = (tmp / 128) & 0x1F; + if (tmp >= 8) + average += 2; + else + average -= 25; + if (tmp == 8) + average -= 72; + else + average -= 48; + + dev->stats.link_noise = average; + dev->noisecalc.calculation_running = false; + return; + } +generate_new: + b43_generate_noise_sample(dev); +} + +static void handle_irq_tbtt_indication(struct b43_wldev *dev) +{ + if (b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) { + ///TODO: PS TBTT + } else { + if (1 /*FIXME: the last PSpoll frame was sent successfully */ ) + b43_power_saving_ctl_bits(dev, 0); + } + if (b43_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) + dev->dfq_valid = true; +} + +static void handle_irq_atim_end(struct b43_wldev *dev) +{ + if (dev->dfq_valid) { + b43_write32(dev, B43_MMIO_MACCMD, + b43_read32(dev, B43_MMIO_MACCMD) + | B43_MACCMD_DFQ_VALID); + dev->dfq_valid = false; + } +} + +static void handle_irq_pmq(struct b43_wldev *dev) +{ + u32 tmp; + + //TODO: AP mode. + + while (1) { + tmp = b43_read32(dev, B43_MMIO_PS_STATUS); + if (!(tmp & 0x00000008)) + break; + } + /* 16bit write is odd, but correct. */ + b43_write16(dev, B43_MMIO_PS_STATUS, 0x0002); +} + +static void b43_write_template_common(struct b43_wldev *dev, + const u8 *data, u16 size, + u16 ram_offset, + u16 shm_size_offset, u8 rate) +{ + u32 i, tmp; + struct b43_plcp_hdr4 plcp; + + plcp.data = 0; + b43_generate_plcp_hdr(&plcp, size + FCS_LEN, rate); + b43_ram_write(dev, ram_offset, le32_to_cpu(plcp.data)); + ram_offset += sizeof(u32); + /* The PLCP is 6 bytes long, but we only wrote 4 bytes, yet. + * So leave the first two bytes of the next write blank. + */ + tmp = (u32) (data[0]) << 16; + tmp |= (u32) (data[1]) << 24; + b43_ram_write(dev, ram_offset, tmp); + ram_offset += sizeof(u32); + for (i = 2; i < size; i += sizeof(u32)) { + tmp = (u32) (data[i + 0]); + if (i + 1 < size) + tmp |= (u32) (data[i + 1]) << 8; + if (i + 2 < size) + tmp |= (u32) (data[i + 2]) << 16; + if (i + 3 < size) + tmp |= (u32) (data[i + 3]) << 24; + b43_ram_write(dev, ram_offset + i - 2, tmp); + } + b43_shm_write16(dev, B43_SHM_SHARED, shm_size_offset, + size + sizeof(struct b43_plcp_hdr6)); +} + +/* Check if the use of the antenna that ieee80211 told us to + * use is possible. This will fall back to DEFAULT. + * "antenna_nr" is the antenna identifier we got from ieee80211. */ +u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev, + u8 antenna_nr) +{ + u8 antenna_mask; + + if (antenna_nr == 0) { + /* Zero means "use default antenna". That's always OK. */ + return 0; + } + + /* Get the mask of available antennas. */ + if (dev->phy.gmode) + antenna_mask = dev->dev->bus_sprom->ant_available_bg; + else + antenna_mask = dev->dev->bus_sprom->ant_available_a; + + if (!(antenna_mask & (1 << (antenna_nr - 1)))) { + /* This antenna is not available. Fall back to default. */ + return 0; + } + + return antenna_nr; +} + +/* Convert a b43 antenna number value to the PHY TX control value. */ +static u16 b43_antenna_to_phyctl(int antenna) +{ + switch (antenna) { + case B43_ANTENNA0: + return B43_TXH_PHY_ANT0; + case B43_ANTENNA1: + return B43_TXH_PHY_ANT1; + case B43_ANTENNA2: + return B43_TXH_PHY_ANT2; + case B43_ANTENNA3: + return B43_TXH_PHY_ANT3; + case B43_ANTENNA_AUTO0: + case B43_ANTENNA_AUTO1: + return B43_TXH_PHY_ANT01AUTO; + } + B43_WARN_ON(1); + return 0; +} + +static void b43_write_beacon_template(struct b43_wldev *dev, + u16 ram_offset, + u16 shm_size_offset) +{ + unsigned int i, len, variable_len; + const struct ieee80211_mgmt *bcn; + const u8 *ie; + bool tim_found = false; + unsigned int rate; + u16 ctl; + int antenna; + struct ieee80211_tx_info *info; + unsigned long flags; + struct sk_buff *beacon_skb; + + spin_lock_irqsave(&dev->wl->beacon_lock, flags); + info = IEEE80211_SKB_CB(dev->wl->current_beacon); + rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value; + /* Clone the beacon, so it cannot go away, while we write it to hw. */ + beacon_skb = skb_clone(dev->wl->current_beacon, GFP_ATOMIC); + spin_unlock_irqrestore(&dev->wl->beacon_lock, flags); + + if (!beacon_skb) { + b43dbg(dev->wl, "Could not upload beacon. " + "Failed to clone beacon skb."); + return; + } + + bcn = (const struct ieee80211_mgmt *)(beacon_skb->data); + len = min_t(size_t, beacon_skb->len, + 0x200 - sizeof(struct b43_plcp_hdr6)); + + b43_write_template_common(dev, (const u8 *)bcn, + len, ram_offset, shm_size_offset, rate); + + /* Write the PHY TX control parameters. */ + antenna = B43_ANTENNA_DEFAULT; + antenna = b43_antenna_to_phyctl(antenna); + ctl = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL); + /* We can't send beacons with short preamble. Would get PHY errors. */ + ctl &= ~B43_TXH_PHY_SHORTPRMBL; + ctl &= ~B43_TXH_PHY_ANT; + ctl &= ~B43_TXH_PHY_ENC; + ctl |= antenna; + if (b43_is_cck_rate(rate)) + ctl |= B43_TXH_PHY_ENC_CCK; + else + ctl |= B43_TXH_PHY_ENC_OFDM; + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, ctl); + + /* Find the position of the TIM and the DTIM_period value + * and write them to SHM. */ + ie = bcn->u.beacon.variable; + variable_len = len - offsetof(struct ieee80211_mgmt, u.beacon.variable); + for (i = 0; i < variable_len - 2; ) { + uint8_t ie_id, ie_len; + + ie_id = ie[i]; + ie_len = ie[i + 1]; + if (ie_id == 5) { + u16 tim_position; + u16 dtim_period; + /* This is the TIM Information Element */ + + /* Check whether the ie_len is in the beacon data range. */ + if (variable_len < ie_len + 2 + i) + break; + /* A valid TIM is at least 4 bytes long. */ + if (ie_len < 4) + break; + tim_found = true; + + tim_position = sizeof(struct b43_plcp_hdr6); + tim_position += offsetof(struct ieee80211_mgmt, u.beacon.variable); + tim_position += i; + + dtim_period = ie[i + 3]; + + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_TIMBPOS, tim_position); + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_DTIMPER, dtim_period); + break; + } + i += ie_len + 2; + } + if (!tim_found) { + /* + * If ucode wants to modify TIM do it behind the beacon, this + * will happen, for example, when doing mesh networking. + */ + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_TIMBPOS, + len + sizeof(struct b43_plcp_hdr6)); + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_DTIMPER, 0); + } + b43dbg(dev->wl, "Updated beacon template at 0x%x\n", ram_offset); + + dev_kfree_skb_any(beacon_skb); +} + +static void b43_upload_beacon0(struct b43_wldev *dev) +{ + struct b43_wl *wl = dev->wl; + + if (wl->beacon0_uploaded) + return; + b43_write_beacon_template(dev, B43_SHM_SH_BT_BASE0, B43_SHM_SH_BTL0); + wl->beacon0_uploaded = true; +} + +static void b43_upload_beacon1(struct b43_wldev *dev) +{ + struct b43_wl *wl = dev->wl; + + if (wl->beacon1_uploaded) + return; + b43_write_beacon_template(dev, B43_SHM_SH_BT_BASE1, B43_SHM_SH_BTL1); + wl->beacon1_uploaded = true; +} + +static void handle_irq_beacon(struct b43_wldev *dev) +{ + struct b43_wl *wl = dev->wl; + u32 cmd, beacon0_valid, beacon1_valid; + + if (!b43_is_mode(wl, NL80211_IFTYPE_AP) && + !b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) && + !b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) + return; + + /* This is the bottom half of the asynchronous beacon update. */ + + /* Ignore interrupt in the future. */ + dev->irq_mask &= ~B43_IRQ_BEACON; + + cmd = b43_read32(dev, B43_MMIO_MACCMD); + beacon0_valid = (cmd & B43_MACCMD_BEACON0_VALID); + beacon1_valid = (cmd & B43_MACCMD_BEACON1_VALID); + + /* Schedule interrupt manually, if busy. */ + if (beacon0_valid && beacon1_valid) { + b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_BEACON); + dev->irq_mask |= B43_IRQ_BEACON; + return; + } + + if (unlikely(wl->beacon_templates_virgin)) { + /* We never uploaded a beacon before. + * Upload both templates now, but only mark one valid. */ + wl->beacon_templates_virgin = false; + b43_upload_beacon0(dev); + b43_upload_beacon1(dev); + cmd = b43_read32(dev, B43_MMIO_MACCMD); + cmd |= B43_MACCMD_BEACON0_VALID; + b43_write32(dev, B43_MMIO_MACCMD, cmd); + } else { + if (!beacon0_valid) { + b43_upload_beacon0(dev); + cmd = b43_read32(dev, B43_MMIO_MACCMD); + cmd |= B43_MACCMD_BEACON0_VALID; + b43_write32(dev, B43_MMIO_MACCMD, cmd); + } else if (!beacon1_valid) { + b43_upload_beacon1(dev); + cmd = b43_read32(dev, B43_MMIO_MACCMD); + cmd |= B43_MACCMD_BEACON1_VALID; + b43_write32(dev, B43_MMIO_MACCMD, cmd); + } + } +} + +static void b43_do_beacon_update_trigger_work(struct b43_wldev *dev) +{ + u32 old_irq_mask = dev->irq_mask; + + /* update beacon right away or defer to irq */ + handle_irq_beacon(dev); + if (old_irq_mask != dev->irq_mask) { + /* The handler updated the IRQ mask. */ + B43_WARN_ON(!dev->irq_mask); + if (b43_read32(dev, B43_MMIO_GEN_IRQ_MASK)) { + b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); + } else { + /* Device interrupts are currently disabled. That means + * we just ran the hardirq handler and scheduled the + * IRQ thread. The thread will write the IRQ mask when + * it finished, so there's nothing to do here. Writing + * the mask _here_ would incorrectly re-enable IRQs. */ + } + } +} + +static void b43_beacon_update_trigger_work(struct work_struct *work) +{ + struct b43_wl *wl = container_of(work, struct b43_wl, + beacon_update_trigger); + struct b43_wldev *dev; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) { + if (b43_bus_host_is_sdio(dev->dev)) { + /* wl->mutex is enough. */ + b43_do_beacon_update_trigger_work(dev); + mmiowb(); + } else { + spin_lock_irq(&wl->hardirq_lock); + b43_do_beacon_update_trigger_work(dev); + mmiowb(); + spin_unlock_irq(&wl->hardirq_lock); + } + } + mutex_unlock(&wl->mutex); +} + +/* Asynchronously update the packet templates in template RAM. */ +static void b43_update_templates(struct b43_wl *wl) +{ + struct sk_buff *beacon, *old_beacon; + unsigned long flags; + + /* This is the top half of the asynchronous beacon update. + * The bottom half is the beacon IRQ. + * Beacon update must be asynchronous to avoid sending an + * invalid beacon. This can happen for example, if the firmware + * transmits a beacon while we are updating it. */ + + /* We could modify the existing beacon and set the aid bit in + * the TIM field, but that would probably require resizing and + * moving of data within the beacon template. + * Simply request a new beacon and let mac80211 do the hard work. */ + beacon = ieee80211_beacon_get(wl->hw, wl->vif); + if (unlikely(!beacon)) + return; + + spin_lock_irqsave(&wl->beacon_lock, flags); + old_beacon = wl->current_beacon; + wl->current_beacon = beacon; + wl->beacon0_uploaded = false; + wl->beacon1_uploaded = false; + spin_unlock_irqrestore(&wl->beacon_lock, flags); + + ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger); + + if (old_beacon) + dev_kfree_skb_any(old_beacon); +} + +static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int) +{ + b43_time_lock(dev); + if (dev->dev->core_rev >= 3) { + b43_write32(dev, B43_MMIO_TSF_CFP_REP, (beacon_int << 16)); + b43_write32(dev, B43_MMIO_TSF_CFP_START, (beacon_int << 10)); + } else { + b43_write16(dev, 0x606, (beacon_int >> 6)); + b43_write16(dev, 0x610, beacon_int); + } + b43_time_unlock(dev); + b43dbg(dev->wl, "Set beacon interval to %u\n", beacon_int); +} + +static void b43_handle_firmware_panic(struct b43_wldev *dev) +{ + u16 reason; + + /* Read the register that contains the reason code for the panic. */ + reason = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_FWPANIC_REASON_REG); + b43err(dev->wl, "Whoopsy, firmware panic! Reason: %u\n", reason); + + switch (reason) { + default: + b43dbg(dev->wl, "The panic reason is unknown.\n"); + /* fallthrough */ + case B43_FWPANIC_DIE: + /* Do not restart the controller or firmware. + * The device is nonfunctional from now on. + * Restarting would result in this panic to trigger again, + * so we avoid that recursion. */ + break; + case B43_FWPANIC_RESTART: + b43_controller_restart(dev, "Microcode panic"); + break; + } +} + +static void handle_irq_ucode_debug(struct b43_wldev *dev) +{ + unsigned int i, cnt; + u16 reason, marker_id, marker_line; + __le16 *buf; + + /* The proprietary firmware doesn't have this IRQ. */ + if (!dev->fw.opensource) + return; + + /* Read the register that contains the reason code for this IRQ. */ + reason = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_DEBUGIRQ_REASON_REG); + + switch (reason) { + case B43_DEBUGIRQ_PANIC: + b43_handle_firmware_panic(dev); + break; + case B43_DEBUGIRQ_DUMP_SHM: + if (!B43_DEBUG) + break; /* Only with driver debugging enabled. */ + buf = kmalloc(4096, GFP_ATOMIC); + if (!buf) { + b43dbg(dev->wl, "SHM-dump: Failed to allocate memory\n"); + goto out; + } + for (i = 0; i < 4096; i += 2) { + u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, i); + buf[i / 2] = cpu_to_le16(tmp); + } + b43info(dev->wl, "Shared memory dump:\n"); + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, + 16, 2, buf, 4096, 1); + kfree(buf); + break; + case B43_DEBUGIRQ_DUMP_REGS: + if (!B43_DEBUG) + break; /* Only with driver debugging enabled. */ + b43info(dev->wl, "Microcode register dump:\n"); + for (i = 0, cnt = 0; i < 64; i++) { + u16 tmp = b43_shm_read16(dev, B43_SHM_SCRATCH, i); + if (cnt == 0) + printk(KERN_INFO); + printk("r%02u: 0x%04X ", i, tmp); + cnt++; + if (cnt == 6) { + printk("\n"); + cnt = 0; + } + } + printk("\n"); + break; + case B43_DEBUGIRQ_MARKER: + if (!B43_DEBUG) + break; /* Only with driver debugging enabled. */ + marker_id = b43_shm_read16(dev, B43_SHM_SCRATCH, + B43_MARKER_ID_REG); + marker_line = b43_shm_read16(dev, B43_SHM_SCRATCH, + B43_MARKER_LINE_REG); + b43info(dev->wl, "The firmware just executed the MARKER(%u) " + "at line number %u\n", + marker_id, marker_line); + break; + default: + b43dbg(dev->wl, "Debug-IRQ triggered for unknown reason: %u\n", + reason); + } +out: + /* Acknowledge the debug-IRQ, so the firmware can continue. */ + b43_shm_write16(dev, B43_SHM_SCRATCH, + B43_DEBUGIRQ_REASON_REG, B43_DEBUGIRQ_ACK); +} + +static void b43_do_interrupt_thread(struct b43_wldev *dev) +{ + u32 reason; + u32 dma_reason[ARRAY_SIZE(dev->dma_reason)]; + u32 merged_dma_reason = 0; + int i; + + if (unlikely(b43_status(dev) != B43_STAT_STARTED)) + return; + + reason = dev->irq_reason; + for (i = 0; i < ARRAY_SIZE(dma_reason); i++) { + dma_reason[i] = dev->dma_reason[i]; + merged_dma_reason |= dma_reason[i]; + } + + if (unlikely(reason & B43_IRQ_MAC_TXERR)) + b43err(dev->wl, "MAC transmission error\n"); + + if (unlikely(reason & B43_IRQ_PHY_TXERR)) { + b43err(dev->wl, "PHY transmission error\n"); + rmb(); + if (unlikely(atomic_dec_and_test(&dev->phy.txerr_cnt))) { + atomic_set(&dev->phy.txerr_cnt, + B43_PHY_TX_BADNESS_LIMIT); + b43err(dev->wl, "Too many PHY TX errors, " + "restarting the controller\n"); + b43_controller_restart(dev, "PHY TX errors"); + } + } + + if (unlikely(merged_dma_reason & (B43_DMAIRQ_FATALMASK))) { + b43err(dev->wl, + "Fatal DMA error: 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X\n", + dma_reason[0], dma_reason[1], + dma_reason[2], dma_reason[3], + dma_reason[4], dma_reason[5]); + b43err(dev->wl, "This device does not support DMA " + "on your system. It will now be switched to PIO.\n"); + /* Fall back to PIO transfers if we get fatal DMA errors! */ + dev->use_pio = true; + b43_controller_restart(dev, "DMA error"); + return; + } + + if (unlikely(reason & B43_IRQ_UCODE_DEBUG)) + handle_irq_ucode_debug(dev); + if (reason & B43_IRQ_TBTT_INDI) + handle_irq_tbtt_indication(dev); + if (reason & B43_IRQ_ATIM_END) + handle_irq_atim_end(dev); + if (reason & B43_IRQ_BEACON) + handle_irq_beacon(dev); + if (reason & B43_IRQ_PMQ) + handle_irq_pmq(dev); + if (reason & B43_IRQ_TXFIFO_FLUSH_OK) + ;/* TODO */ + if (reason & B43_IRQ_NOISESAMPLE_OK) + handle_irq_noise(dev); + + /* Check the DMA reason registers for received data. */ + if (dma_reason[0] & B43_DMAIRQ_RDESC_UFLOW) { + if (B43_DEBUG) + b43warn(dev->wl, "RX descriptor underrun\n"); + b43_dma_handle_rx_overflow(dev->dma.rx_ring); + } + if (dma_reason[0] & B43_DMAIRQ_RX_DONE) { + if (b43_using_pio_transfers(dev)) + b43_pio_rx(dev->pio.rx_queue); + else + b43_dma_rx(dev->dma.rx_ring); + } + B43_WARN_ON(dma_reason[1] & B43_DMAIRQ_RX_DONE); + B43_WARN_ON(dma_reason[2] & B43_DMAIRQ_RX_DONE); + B43_WARN_ON(dma_reason[3] & B43_DMAIRQ_RX_DONE); + B43_WARN_ON(dma_reason[4] & B43_DMAIRQ_RX_DONE); + B43_WARN_ON(dma_reason[5] & B43_DMAIRQ_RX_DONE); + + if (reason & B43_IRQ_TX_OK) + handle_irq_transmit_status(dev); + + /* Re-enable interrupts on the device by restoring the current interrupt mask. */ + b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); + +#if B43_DEBUG + if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { + dev->irq_count++; + for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { + if (reason & (1 << i)) + dev->irq_bit_count[i]++; + } + } +#endif +} + +/* Interrupt thread handler. Handles device interrupts in thread context. */ +static irqreturn_t b43_interrupt_thread_handler(int irq, void *dev_id) +{ + struct b43_wldev *dev = dev_id; + + mutex_lock(&dev->wl->mutex); + b43_do_interrupt_thread(dev); + mmiowb(); + mutex_unlock(&dev->wl->mutex); + + return IRQ_HANDLED; +} + +static irqreturn_t b43_do_interrupt(struct b43_wldev *dev) +{ + u32 reason; + + /* This code runs under wl->hardirq_lock, but _only_ on non-SDIO busses. + * On SDIO, this runs under wl->mutex. */ + + reason = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); + if (reason == 0xffffffff) /* shared IRQ */ + return IRQ_NONE; + reason &= dev->irq_mask; + if (!reason) + return IRQ_NONE; + + dev->dma_reason[0] = b43_read32(dev, B43_MMIO_DMA0_REASON) + & 0x0001FC00; + dev->dma_reason[1] = b43_read32(dev, B43_MMIO_DMA1_REASON) + & 0x0000DC00; + dev->dma_reason[2] = b43_read32(dev, B43_MMIO_DMA2_REASON) + & 0x0000DC00; + dev->dma_reason[3] = b43_read32(dev, B43_MMIO_DMA3_REASON) + & 0x0001DC00; + dev->dma_reason[4] = b43_read32(dev, B43_MMIO_DMA4_REASON) + & 0x0000DC00; +/* Unused ring + dev->dma_reason[5] = b43_read32(dev, B43_MMIO_DMA5_REASON) + & 0x0000DC00; +*/ + + /* ACK the interrupt. */ + b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, reason); + b43_write32(dev, B43_MMIO_DMA0_REASON, dev->dma_reason[0]); + b43_write32(dev, B43_MMIO_DMA1_REASON, dev->dma_reason[1]); + b43_write32(dev, B43_MMIO_DMA2_REASON, dev->dma_reason[2]); + b43_write32(dev, B43_MMIO_DMA3_REASON, dev->dma_reason[3]); + b43_write32(dev, B43_MMIO_DMA4_REASON, dev->dma_reason[4]); +/* Unused ring + b43_write32(dev, B43_MMIO_DMA5_REASON, dev->dma_reason[5]); +*/ + + /* Disable IRQs on the device. The IRQ thread handler will re-enable them. */ + b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); + /* Save the reason bitmasks for the IRQ thread handler. */ + dev->irq_reason = reason; + + return IRQ_WAKE_THREAD; +} + +/* Interrupt handler top-half. This runs with interrupts disabled. */ +static irqreturn_t b43_interrupt_handler(int irq, void *dev_id) +{ + struct b43_wldev *dev = dev_id; + irqreturn_t ret; + + if (unlikely(b43_status(dev) < B43_STAT_STARTED)) + return IRQ_NONE; + + spin_lock(&dev->wl->hardirq_lock); + ret = b43_do_interrupt(dev); + mmiowb(); + spin_unlock(&dev->wl->hardirq_lock); + + return ret; +} + +/* SDIO interrupt handler. This runs in process context. */ +static void b43_sdio_interrupt_handler(struct b43_wldev *dev) +{ + struct b43_wl *wl = dev->wl; + irqreturn_t ret; + + mutex_lock(&wl->mutex); + + ret = b43_do_interrupt(dev); + if (ret == IRQ_WAKE_THREAD) + b43_do_interrupt_thread(dev); + + mutex_unlock(&wl->mutex); +} + +void b43_do_release_fw(struct b43_firmware_file *fw) +{ + release_firmware(fw->data); + fw->data = NULL; + fw->filename = NULL; +} + +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); + b43_do_release_fw(&dev->fw.initvals_band); +} + +static void b43_print_fw_helptext(struct b43_wl *wl, bool error) +{ + const char text[] = + "You must go to " \ + "http://wireless.kernel.org/en/users/Drivers/b43#devicefirmware " \ + "and download the correct firmware for this driver version. " \ + "Please carefully read all instructions on this website.\n"; + + if (error) + b43err(wl, text); + else + b43warn(wl, text); +} + +static void b43_fw_cb(const struct firmware *firmware, void *context) +{ + struct b43_request_fw_context *ctx = context; + + ctx->blob = firmware; + complete(&ctx->dev->fw_load_complete); +} + +int b43_do_request_fw(struct b43_request_fw_context *ctx, + const char *name, + struct b43_firmware_file *fw, bool async) +{ + struct b43_fw_header *hdr; + u32 size; + int err; + + if (!name) { + /* Don't fetch anything. Free possibly cached firmware. */ + /* FIXME: We should probably keep it anyway, to save some headache + * on suspend/resume with multiband devices. */ + b43_do_release_fw(fw); + return 0; + } + if (fw->filename) { + if ((fw->type == ctx->req_type) && + (strcmp(fw->filename, name) == 0)) + return 0; /* Already have this fw. */ + /* Free the cached firmware first. */ + /* FIXME: We should probably do this later after we successfully + * got the new fw. This could reduce headache with multiband devices. + * We could also redesign this to cache the firmware for all possible + * bands all the time. */ + b43_do_release_fw(fw); + } + + switch (ctx->req_type) { + case B43_FWTYPE_PROPRIETARY: + snprintf(ctx->fwname, sizeof(ctx->fwname), + "b43%s/%s.fw", + modparam_fwpostfix, name); + break; + case B43_FWTYPE_OPENSOURCE: + snprintf(ctx->fwname, sizeof(ctx->fwname), + "b43-open%s/%s.fw", + modparam_fwpostfix, name); + break; + default: + B43_WARN_ON(1); + return -ENOSYS; + } + if (async) { + /* do this part asynchronously */ + 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); + if (err < 0) { + pr_err("Unable to load firmware\n"); + return err; + } + 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 fall through here + */ + } + err = request_firmware(&ctx->blob, ctx->fwname, + ctx->dev->dev->dev); + if (err == -ENOENT) { + snprintf(ctx->errors[ctx->req_type], + sizeof(ctx->errors[ctx->req_type]), + "Firmware file \"%s\" not found\n", + ctx->fwname); + return err; + } else if (err) { + snprintf(ctx->errors[ctx->req_type], + sizeof(ctx->errors[ctx->req_type]), + "Firmware file \"%s\" request failed (err=%d)\n", + ctx->fwname, err); + return err; + } +fw_ready: + if (ctx->blob->size < sizeof(struct b43_fw_header)) + goto err_format; + hdr = (struct b43_fw_header *)(ctx->blob->data); + switch (hdr->type) { + case B43_FW_TYPE_UCODE: + case B43_FW_TYPE_PCM: + size = be32_to_cpu(hdr->size); + if (size != ctx->blob->size - sizeof(struct b43_fw_header)) + goto err_format; + /* fallthrough */ + case B43_FW_TYPE_IV: + if (hdr->ver != 1) + goto err_format; + break; + default: + goto err_format; + } + + fw->data = ctx->blob; + fw->filename = name; + fw->type = ctx->req_type; + + return 0; + +err_format: + snprintf(ctx->errors[ctx->req_type], + sizeof(ctx->errors[ctx->req_type]), + "Firmware file \"%s\" format error.\n", ctx->fwname); + release_firmware(ctx->blob); + + return -EPROTO; +} + +/* http://bcm-v4.sipsolutions.net/802.11/Init/Firmware */ +static int b43_try_request_fw(struct b43_request_fw_context *ctx) +{ + struct b43_wldev *dev = ctx->dev; + struct b43_firmware *fw = &ctx->dev->fw; + struct b43_phy *phy = &dev->phy; + const u8 rev = ctx->dev->dev->core_rev; + const char *filename; + int err; + + /* Get microcode */ + filename = NULL; + switch (rev) { + case 42: + if (phy->type == B43_PHYTYPE_AC) + filename = "ucode42"; + break; + case 40: + if (phy->type == B43_PHYTYPE_AC) + filename = "ucode40"; + break; + case 33: + if (phy->type == B43_PHYTYPE_LCN40) + filename = "ucode33_lcn40"; + break; + case 30: + if (phy->type == B43_PHYTYPE_N) + filename = "ucode30_mimo"; + break; + case 29: + if (phy->type == B43_PHYTYPE_HT) + filename = "ucode29_mimo"; + break; + case 26: + if (phy->type == B43_PHYTYPE_HT) + filename = "ucode26_mimo"; + break; + case 28: + case 25: + if (phy->type == B43_PHYTYPE_N) + filename = "ucode25_mimo"; + else if (phy->type == B43_PHYTYPE_LCN) + filename = "ucode25_lcn"; + break; + case 24: + if (phy->type == B43_PHYTYPE_LCN) + filename = "ucode24_lcn"; + break; + case 23: + if (phy->type == B43_PHYTYPE_N) + filename = "ucode16_mimo"; + break; + case 16 ... 19: + if (phy->type == B43_PHYTYPE_N) + filename = "ucode16_mimo"; + else if (phy->type == B43_PHYTYPE_LP) + filename = "ucode16_lp"; + break; + case 15: + filename = "ucode15"; + break; + case 14: + filename = "ucode14"; + break; + case 13: + filename = "ucode13"; + break; + case 11 ... 12: + filename = "ucode11"; + break; + case 5 ... 10: + filename = "ucode5"; + break; + } + if (!filename) + goto err_no_ucode; + err = b43_do_request_fw(ctx, filename, &fw->ucode, true); + if (err) + goto err_load; + + /* Get PCM code */ + if ((rev >= 5) && (rev <= 10)) + filename = "pcm5"; + else if (rev >= 11) + filename = NULL; + else + goto err_no_pcm; + fw->pcm_request_failed = false; + err = b43_do_request_fw(ctx, filename, &fw->pcm, false); + if (err == -ENOENT) { + /* We did not find a PCM file? Not fatal, but + * core rev <= 10 must do without hwcrypto then. */ + fw->pcm_request_failed = true; + } else if (err) + goto err_load; + + /* Get initvals */ + filename = NULL; + switch (dev->phy.type) { + case B43_PHYTYPE_G: + if (rev == 13) + filename = "b0g0initvals13"; + else if (rev >= 5 && rev <= 10) + filename = "b0g0initvals5"; + break; + case B43_PHYTYPE_N: + if (rev == 30) + filename = "n16initvals30"; + else if (rev == 28 || rev == 25) + filename = "n0initvals25"; + else if (rev == 24) + filename = "n0initvals24"; + else if (rev == 23) + filename = "n0initvals16"; /* What about n0initvals22? */ + else if (rev >= 16 && rev <= 18) + filename = "n0initvals16"; + else if (rev >= 11 && rev <= 12) + filename = "n0initvals11"; + break; + case B43_PHYTYPE_LP: + if (rev >= 16 && rev <= 18) + filename = "lp0initvals16"; + else if (rev == 15) + filename = "lp0initvals15"; + else if (rev == 14) + filename = "lp0initvals14"; + else if (rev == 13) + filename = "lp0initvals13"; + break; + case B43_PHYTYPE_HT: + if (rev == 29) + filename = "ht0initvals29"; + else if (rev == 26) + filename = "ht0initvals26"; + break; + case B43_PHYTYPE_LCN: + if (rev == 24) + filename = "lcn0initvals24"; + break; + case B43_PHYTYPE_LCN40: + if (rev == 33) + filename = "lcn400initvals33"; + break; + case B43_PHYTYPE_AC: + if (rev == 42) + filename = "ac1initvals42"; + else if (rev == 40) + filename = "ac0initvals40"; + break; + } + if (!filename) + goto err_no_initvals; + err = b43_do_request_fw(ctx, filename, &fw->initvals, false); + if (err) + goto err_load; + + /* Get bandswitch initvals */ + filename = NULL; + switch (dev->phy.type) { + case B43_PHYTYPE_G: + if (rev == 13) + filename = "b0g0bsinitvals13"; + else if (rev >= 5 && rev <= 10) + filename = "b0g0bsinitvals5"; + break; + case B43_PHYTYPE_N: + if (rev == 30) + filename = "n16bsinitvals30"; + else if (rev == 28 || rev == 25) + filename = "n0bsinitvals25"; + else if (rev == 24) + filename = "n0bsinitvals24"; + else if (rev == 23) + filename = "n0bsinitvals16"; /* What about n0bsinitvals22? */ + else if (rev >= 16 && rev <= 18) + filename = "n0bsinitvals16"; + else if (rev >= 11 && rev <= 12) + filename = "n0bsinitvals11"; + break; + case B43_PHYTYPE_LP: + if (rev >= 16 && rev <= 18) + filename = "lp0bsinitvals16"; + else if (rev == 15) + filename = "lp0bsinitvals15"; + else if (rev == 14) + filename = "lp0bsinitvals14"; + else if (rev == 13) + filename = "lp0bsinitvals13"; + break; + case B43_PHYTYPE_HT: + if (rev == 29) + filename = "ht0bsinitvals29"; + else if (rev == 26) + filename = "ht0bsinitvals26"; + break; + case B43_PHYTYPE_LCN: + if (rev == 24) + filename = "lcn0bsinitvals24"; + break; + case B43_PHYTYPE_LCN40: + if (rev == 33) + filename = "lcn400bsinitvals33"; + break; + case B43_PHYTYPE_AC: + if (rev == 42) + filename = "ac1bsinitvals42"; + else if (rev == 40) + filename = "ac0bsinitvals40"; + break; + } + if (!filename) + goto err_no_initvals; + err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false); + if (err) + goto err_load; + + fw->opensource = (ctx->req_type == B43_FWTYPE_OPENSOURCE); + + return 0; + +err_no_ucode: + err = ctx->fatal_failure = -EOPNOTSUPP; + b43err(dev->wl, "The driver does not know which firmware (ucode) " + "is required for your device (wl-core rev %u)\n", rev); + goto error; + +err_no_pcm: + err = ctx->fatal_failure = -EOPNOTSUPP; + b43err(dev->wl, "The driver does not know which firmware (PCM) " + "is required for your device (wl-core rev %u)\n", rev); + goto error; + +err_no_initvals: + err = ctx->fatal_failure = -EOPNOTSUPP; + b43err(dev->wl, "The driver does not know which firmware (initvals) " + "is required for your device (wl-core rev %u)\n", rev); + goto error; + +err_load: + /* We failed to load this firmware image. The error message + * already is in ctx->errors. Return and let our caller decide + * what to do. */ + goto error; + +error: + b43_release_firmware(dev); + return err; +} + +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) +{ + struct b43_wl *wl = container_of(work, + struct b43_wl, firmware_load); + struct b43_wldev *dev = wl->current_dev; + struct b43_request_fw_context *ctx; + unsigned int i; + int err; + const char *errmsg; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return; + ctx->dev = dev; + + ctx->req_type = B43_FWTYPE_PROPRIETARY; + err = b43_try_request_fw(ctx); + if (!err) + goto start_ieee80211; /* Successfully loaded it. */ + /* Was fw version known? */ + if (ctx->fatal_failure) + goto out; + + /* proprietary fw not found, try open source */ + ctx->req_type = B43_FWTYPE_OPENSOURCE; + err = b43_try_request_fw(ctx); + if (!err) + goto start_ieee80211; /* Successfully loaded it. */ + if(ctx->fatal_failure) + goto out; + + /* Could not find a usable firmware. Print the errors. */ + for (i = 0; i < B43_NR_FWTYPES; i++) { + errmsg = ctx->errors[i]; + if (strlen(errmsg)) + b43err(dev->wl, "%s", errmsg); + } + b43_print_fw_helptext(dev->wl, 1); + goto out; + +start_ieee80211: + wl->hw->queues = B43_QOS_QUEUE_NUM; + if (!modparam_qos || dev->fw.opensource) + wl->hw->queues = 1; + + err = ieee80211_register_hw(wl->hw); + if (err) + 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: + b43_one_core_detach(dev->dev); + +out: + kfree(ctx); +} + +static int b43_upload_microcode(struct b43_wldev *dev) +{ + struct wiphy *wiphy = dev->wl->hw->wiphy; + const size_t hdr_len = sizeof(struct b43_fw_header); + const __be32 *data; + unsigned int i, len; + u16 fwrev, fwpatch, fwdate, fwtime; + u32 tmp, macctl; + int err = 0; + + /* Jump the microcode PSM to offset 0 */ + macctl = b43_read32(dev, B43_MMIO_MACCTL); + B43_WARN_ON(macctl & B43_MACCTL_PSM_RUN); + macctl |= B43_MACCTL_PSM_JMP0; + b43_write32(dev, B43_MMIO_MACCTL, macctl); + /* Zero out all microcode PSM registers and shared memory. */ + for (i = 0; i < 64; i++) + b43_shm_write16(dev, B43_SHM_SCRATCH, i, 0); + for (i = 0; i < 4096; i += 2) + b43_shm_write16(dev, B43_SHM_SHARED, i, 0); + + /* Upload Microcode. */ + data = (__be32 *) (dev->fw.ucode.data->data + hdr_len); + len = (dev->fw.ucode.data->size - hdr_len) / sizeof(__be32); + b43_shm_control_word(dev, B43_SHM_UCODE | B43_SHM_AUTOINC_W, 0x0000); + for (i = 0; i < len; i++) { + b43_write32(dev, B43_MMIO_SHM_DATA, be32_to_cpu(data[i])); + udelay(10); + } + + if (dev->fw.pcm.data) { + /* Upload PCM data. */ + data = (__be32 *) (dev->fw.pcm.data->data + hdr_len); + len = (dev->fw.pcm.data->size - hdr_len) / sizeof(__be32); + b43_shm_control_word(dev, B43_SHM_HW, 0x01EA); + b43_write32(dev, B43_MMIO_SHM_DATA, 0x00004000); + /* No need for autoinc bit in SHM_HW */ + b43_shm_control_word(dev, B43_SHM_HW, 0x01EB); + for (i = 0; i < len; i++) { + b43_write32(dev, B43_MMIO_SHM_DATA, be32_to_cpu(data[i])); + udelay(10); + } + } + + b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_ALL); + + /* Start the microcode PSM */ + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_JMP0, + B43_MACCTL_PSM_RUN); + + /* Wait for the microcode to load and respond */ + i = 0; + while (1) { + tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); + if (tmp == B43_IRQ_MAC_SUSPENDED) + break; + i++; + if (i >= 20) { + b43err(dev->wl, "Microcode not responding\n"); + b43_print_fw_helptext(dev->wl, 1); + err = -ENODEV; + goto error; + } + msleep(50); + } + b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); /* dummy read */ + + /* Get and check the revisions. */ + fwrev = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODEREV); + fwpatch = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODEPATCH); + fwdate = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODEDATE); + fwtime = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_UCODETIME); + + if (fwrev <= 0x128) { + b43err(dev->wl, "YOUR FIRMWARE IS TOO OLD. Firmware from " + "binary drivers older than version 4.x is unsupported. " + "You must upgrade your firmware files.\n"); + b43_print_fw_helptext(dev->wl, 1); + err = -EOPNOTSUPP; + goto error; + } + dev->fw.rev = fwrev; + dev->fw.patch = fwpatch; + if (dev->fw.rev >= 598) + dev->fw.hdr_format = B43_FW_HDR_598; + else if (dev->fw.rev >= 410) + dev->fw.hdr_format = B43_FW_HDR_410; + else + dev->fw.hdr_format = B43_FW_HDR_351; + WARN_ON(dev->fw.opensource != (fwdate == 0xFFFF)); + + dev->qos_enabled = dev->wl->hw->queues > 1; + /* Default to firmware/hardware crypto acceleration. */ + dev->hwcrypto_enabled = true; + + if (dev->fw.opensource) { + u16 fwcapa; + + /* Patchlevel info is encoded in the "time" field. */ + dev->fw.patch = fwtime; + b43info(dev->wl, "Loading OpenSource firmware version %u.%u\n", + dev->fw.rev, dev->fw.patch); + + fwcapa = b43_fwcapa_read(dev); + if (!(fwcapa & B43_FWCAPA_HWCRYPTO) || dev->fw.pcm_request_failed) { + b43info(dev->wl, "Hardware crypto acceleration not supported by firmware\n"); + /* Disable hardware crypto and fall back to software crypto. */ + dev->hwcrypto_enabled = false; + } + /* adding QoS support should use an offline discovery mechanism */ + WARN(fwcapa & B43_FWCAPA_QOS, "QoS in OpenFW not supported\n"); + } else { + b43info(dev->wl, "Loading firmware version %u.%u " + "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n", + fwrev, fwpatch, + (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF, + (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, fwtime & 0x1F); + if (dev->fw.pcm_request_failed) { + b43warn(dev->wl, "No \"pcm5.fw\" firmware file found. " + "Hardware accelerated cryptography is disabled.\n"); + b43_print_fw_helptext(dev->wl, 0); + } + } + + snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "%u.%u", + dev->fw.rev, dev->fw.patch); + wiphy->hw_version = dev->dev->core_id; + + if (dev->fw.hdr_format == B43_FW_HDR_351) { + /* We're over the deadline, but we keep support for old fw + * until it turns out to be in major conflict with something new. */ + b43warn(dev->wl, "You are using an old firmware image. " + "Support for old firmware will be removed soon " + "(official deadline was July 2008).\n"); + b43_print_fw_helptext(dev->wl, 0); + } + + return 0; + +error: + /* Stop the microcode PSM. */ + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN, + B43_MACCTL_PSM_JMP0); + + return err; +} + +static int b43_write_initvals(struct b43_wldev *dev, + const struct b43_iv *ivals, + size_t count, + size_t array_size) +{ + const struct b43_iv *iv; + u16 offset; + size_t i; + bool bit32; + + BUILD_BUG_ON(sizeof(struct b43_iv) != 6); + iv = ivals; + for (i = 0; i < count; i++) { + if (array_size < sizeof(iv->offset_size)) + goto err_format; + array_size -= sizeof(iv->offset_size); + offset = be16_to_cpu(iv->offset_size); + bit32 = !!(offset & B43_IV_32BIT); + offset &= B43_IV_OFFSET_MASK; + if (offset >= 0x1000) + goto err_format; + if (bit32) { + u32 value; + + if (array_size < sizeof(iv->data.d32)) + goto err_format; + array_size -= sizeof(iv->data.d32); + + value = get_unaligned_be32(&iv->data.d32); + b43_write32(dev, offset, value); + + iv = (const struct b43_iv *)((const uint8_t *)iv + + sizeof(__be16) + + sizeof(__be32)); + } else { + u16 value; + + if (array_size < sizeof(iv->data.d16)) + goto err_format; + array_size -= sizeof(iv->data.d16); + + value = be16_to_cpu(iv->data.d16); + b43_write16(dev, offset, value); + + iv = (const struct b43_iv *)((const uint8_t *)iv + + sizeof(__be16) + + sizeof(__be16)); + } + } + if (array_size) + goto err_format; + + return 0; + +err_format: + b43err(dev->wl, "Initial Values Firmware file-format error.\n"); + b43_print_fw_helptext(dev->wl, 1); + + return -EPROTO; +} + +static int b43_upload_initvals(struct b43_wldev *dev) +{ + const size_t hdr_len = sizeof(struct b43_fw_header); + const struct b43_fw_header *hdr; + struct b43_firmware *fw = &dev->fw; + const struct b43_iv *ivals; + size_t count; + + hdr = (const struct b43_fw_header *)(fw->initvals.data->data); + ivals = (const struct b43_iv *)(fw->initvals.data->data + hdr_len); + count = be32_to_cpu(hdr->size); + return b43_write_initvals(dev, ivals, count, + fw->initvals.data->size - hdr_len); +} + +static int b43_upload_initvals_band(struct b43_wldev *dev) +{ + const size_t hdr_len = sizeof(struct b43_fw_header); + const struct b43_fw_header *hdr; + struct b43_firmware *fw = &dev->fw; + const struct b43_iv *ivals; + size_t count; + + if (!fw->initvals_band.data) + return 0; + + hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data); + ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len); + count = be32_to_cpu(hdr->size); + return b43_write_initvals(dev, ivals, count, + fw->initvals_band.data->size - hdr_len); +} + +/* Initialize the GPIOs + * http://bcm-specs.sipsolutions.net/GPIO + */ + +#ifdef CONFIG_B43_SSB +static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->sdev->bus; + +#ifdef CONFIG_SSB_DRIVER_PCICORE + return (bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev); +#else + return bus->chipco.dev; +#endif +} +#endif + +static int b43_gpio_init(struct b43_wldev *dev) +{ +#ifdef CONFIG_B43_SSB + struct ssb_device *gpiodev; +#endif + u32 mask, set; + + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0); + b43_maskset16(dev, B43_MMIO_GPIO_MASK, ~0, 0xF); + + mask = 0x0000001F; + set = 0x0000000F; + if (dev->dev->chip_id == 0x4301) { + mask |= 0x0060; + set |= 0x0060; + } else if (dev->dev->chip_id == 0x5354) { + /* Don't allow overtaking buttons GPIOs */ + set &= 0x2; /* 0x2 is LED GPIO on BCM5354 */ + } + + if (0 /* FIXME: conditional unknown */ ) { + b43_write16(dev, B43_MMIO_GPIO_MASK, + b43_read16(dev, B43_MMIO_GPIO_MASK) + | 0x0100); + /* BT Coexistance Input */ + mask |= 0x0080; + set |= 0x0080; + /* BT Coexistance Out */ + mask |= 0x0100; + set |= 0x0100; + } + if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) { + /* PA is controlled by gpio 9, let ucode handle it */ + b43_write16(dev, B43_MMIO_GPIO_MASK, + b43_read16(dev, B43_MMIO_GPIO_MASK) + | 0x0200); + mask |= 0x0200; + set |= 0x0200; + } + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + bcma_chipco_gpio_control(&dev->dev->bdev->bus->drv_cc, mask, set); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + gpiodev = b43_ssb_gpio_dev(dev); + if (gpiodev) + ssb_write32(gpiodev, B43_GPIO_CONTROL, + (ssb_read32(gpiodev, B43_GPIO_CONTROL) + & ~mask) | set); + break; +#endif + } + + return 0; +} + +/* Turn off all GPIO stuff. Call this on module unload, for example. */ +static void b43_gpio_cleanup(struct b43_wldev *dev) +{ +#ifdef CONFIG_B43_SSB + struct ssb_device *gpiodev; +#endif + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + bcma_chipco_gpio_control(&dev->dev->bdev->bus->drv_cc, ~0, 0); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + gpiodev = b43_ssb_gpio_dev(dev); + if (gpiodev) + ssb_write32(gpiodev, B43_GPIO_CONTROL, 0); + break; +#endif + } +} + +/* http://bcm-specs.sipsolutions.net/EnableMac */ +void b43_mac_enable(struct b43_wldev *dev) +{ + if (b43_debug(dev, B43_DBG_FIRMWARE)) { + u16 fwstate; + + fwstate = b43_shm_read16(dev, B43_SHM_SHARED, + B43_SHM_SH_UCODESTAT); + if ((fwstate != B43_SHM_SH_UCODESTAT_SUSP) && + (fwstate != B43_SHM_SH_UCODESTAT_SLEEP)) { + b43err(dev->wl, "b43_mac_enable(): The firmware " + "should be suspended, but current state is %u\n", + fwstate); + } + } + + dev->mac_suspended--; + B43_WARN_ON(dev->mac_suspended < 0); + if (dev->mac_suspended == 0) { + b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_ENABLED); + b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, + B43_IRQ_MAC_SUSPENDED); + /* Commit writes */ + b43_read32(dev, B43_MMIO_MACCTL); + b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); + b43_power_saving_ctl_bits(dev, 0); + } +} + +/* http://bcm-specs.sipsolutions.net/SuspendMAC */ +void b43_mac_suspend(struct b43_wldev *dev) +{ + int i; + u32 tmp; + + might_sleep(); + B43_WARN_ON(dev->mac_suspended < 0); + + if (dev->mac_suspended == 0) { + b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_ENABLED, 0); + /* force pci to flush the write */ + b43_read32(dev, B43_MMIO_MACCTL); + for (i = 35; i; i--) { + tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); + if (tmp & B43_IRQ_MAC_SUSPENDED) + goto out; + udelay(10); + } + /* Hm, it seems this will take some time. Use msleep(). */ + for (i = 40; i; i--) { + tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); + if (tmp & B43_IRQ_MAC_SUSPENDED) + goto out; + msleep(1); + } + b43err(dev->wl, "MAC suspend failed\n"); + } +out: + dev->mac_suspended++; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/MacPhyClkSet */ +void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on) +{ + u32 tmp; + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + if (on) + tmp |= B43_BCMA_IOCTL_MACPHYCLKEN; + else + tmp &= ~B43_BCMA_IOCTL_MACPHYCLKEN; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + if (on) + tmp |= B43_TMSLOW_MACPHYCLKEN; + else + tmp &= ~B43_TMSLOW_MACPHYCLKEN; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + break; +#endif + } +} + +/* brcms_b_switch_macfreq */ +void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode) +{ + u16 chip_id = dev->dev->chip_id; + + if (chip_id == BCMA_CHIP_ID_BCM4331) { + switch (spurmode) { + case 2: /* 168 Mhz: 2^26/168 = 0x61862 */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x1862); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x6); + break; + case 1: /* 164 Mhz: 2^26/164 = 0x63e70 */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x3e70); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x6); + break; + default: /* 160 Mhz: 2^26/160 = 0x66666 */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x6666); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x6); + break; + } + } else if (chip_id == BCMA_CHIP_ID_BCM43131 || + chip_id == BCMA_CHIP_ID_BCM43217 || + chip_id == BCMA_CHIP_ID_BCM43222 || + chip_id == BCMA_CHIP_ID_BCM43224 || + chip_id == BCMA_CHIP_ID_BCM43225 || + chip_id == BCMA_CHIP_ID_BCM43227 || + chip_id == BCMA_CHIP_ID_BCM43228) { + switch (spurmode) { + case 2: /* 126 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + case 1: /* 123 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + default: /* 120 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + } + } else if (dev->phy.type == B43_PHYTYPE_LCN) { + switch (spurmode) { + case 1: /* 82 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); + break; + default: /* 80 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); + break; + } + } +} + +static void b43_adjust_opmode(struct b43_wldev *dev) +{ + struct b43_wl *wl = dev->wl; + u32 ctl; + u16 cfp_pretbtt; + + ctl = b43_read32(dev, B43_MMIO_MACCTL); + /* Reset status to STA infrastructure mode. */ + ctl &= ~B43_MACCTL_AP; + ctl &= ~B43_MACCTL_KEEP_CTL; + ctl &= ~B43_MACCTL_KEEP_BADPLCP; + ctl &= ~B43_MACCTL_KEEP_BAD; + ctl &= ~B43_MACCTL_PROMISC; + ctl &= ~B43_MACCTL_BEACPROMISC; + ctl |= B43_MACCTL_INFRA; + + if (b43_is_mode(wl, NL80211_IFTYPE_AP) || + b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT)) + ctl |= B43_MACCTL_AP; + else if (b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) + ctl &= ~B43_MACCTL_INFRA; + + if (wl->filter_flags & FIF_CONTROL) + ctl |= B43_MACCTL_KEEP_CTL; + if (wl->filter_flags & FIF_FCSFAIL) + ctl |= B43_MACCTL_KEEP_BAD; + if (wl->filter_flags & FIF_PLCPFAIL) + ctl |= B43_MACCTL_KEEP_BADPLCP; + if (wl->filter_flags & FIF_BCN_PRBRESP_PROMISC) + ctl |= B43_MACCTL_BEACPROMISC; + + /* Workaround: On old hardware the HW-MAC-address-filter + * doesn't work properly, so always run promisc in filter + * it in software. */ + if (dev->dev->core_rev <= 4) + ctl |= B43_MACCTL_PROMISC; + + b43_write32(dev, B43_MMIO_MACCTL, ctl); + + cfp_pretbtt = 2; + if ((ctl & B43_MACCTL_INFRA) && !(ctl & B43_MACCTL_AP)) { + if (dev->dev->chip_id == 0x4306 && + dev->dev->chip_rev == 3) + cfp_pretbtt = 100; + else + cfp_pretbtt = 50; + } + b43_write16(dev, 0x612, cfp_pretbtt); + + /* FIXME: We don't currently implement the PMQ mechanism, + * so always disable it. If we want to implement PMQ, + * we need to enable it here (clear DISCPMQ) in AP mode. + */ + if (0 /* ctl & B43_MACCTL_AP */) + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_DISCPMQ, 0); + else + b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_DISCPMQ); +} + +static void b43_rate_memory_write(struct b43_wldev *dev, u16 rate, int is_ofdm) +{ + u16 offset; + + if (is_ofdm) { + offset = 0x480; + offset += (b43_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2; + } else { + offset = 0x4C0; + offset += (b43_plcp_get_ratecode_cck(rate) & 0x000F) * 2; + } + b43_shm_write16(dev, B43_SHM_SHARED, offset + 0x20, + b43_shm_read16(dev, B43_SHM_SHARED, offset)); +} + +static void b43_rate_memory_init(struct b43_wldev *dev) +{ + switch (dev->phy.type) { + case B43_PHYTYPE_A: + case B43_PHYTYPE_G: + case B43_PHYTYPE_N: + case B43_PHYTYPE_LP: + case B43_PHYTYPE_HT: + case B43_PHYTYPE_LCN: + b43_rate_memory_write(dev, B43_OFDM_RATE_6MB, 1); + b43_rate_memory_write(dev, B43_OFDM_RATE_9MB, 1); + b43_rate_memory_write(dev, B43_OFDM_RATE_12MB, 1); + b43_rate_memory_write(dev, B43_OFDM_RATE_18MB, 1); + b43_rate_memory_write(dev, B43_OFDM_RATE_24MB, 1); + b43_rate_memory_write(dev, B43_OFDM_RATE_36MB, 1); + b43_rate_memory_write(dev, B43_OFDM_RATE_48MB, 1); + b43_rate_memory_write(dev, B43_OFDM_RATE_54MB, 1); + if (dev->phy.type == B43_PHYTYPE_A) + break; + /* fallthrough */ + case B43_PHYTYPE_B: + b43_rate_memory_write(dev, B43_CCK_RATE_1MB, 0); + b43_rate_memory_write(dev, B43_CCK_RATE_2MB, 0); + b43_rate_memory_write(dev, B43_CCK_RATE_5MB, 0); + b43_rate_memory_write(dev, B43_CCK_RATE_11MB, 0); + break; + default: + B43_WARN_ON(1); + } +} + +/* Set the default values for the PHY TX Control Words. */ +static void b43_set_phytxctl_defaults(struct b43_wldev *dev) +{ + u16 ctl = 0; + + ctl |= B43_TXH_PHY_ENC_CCK; + ctl |= B43_TXH_PHY_ANT01AUTO; + ctl |= B43_TXH_PHY_TXPWR; + + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, ctl); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL, ctl); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL, ctl); +} + +/* Set the TX-Antenna for management frames sent by firmware. */ +static void b43_mgmtframe_txantenna(struct b43_wldev *dev, int antenna) +{ + u16 ant; + u16 tmp; + + ant = b43_antenna_to_phyctl(antenna); + + /* For ACK/CTS */ + tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL); + tmp = (tmp & ~B43_TXH_PHY_ANT) | ant; + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL, tmp); + /* For Probe Resposes */ + tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL); + tmp = (tmp & ~B43_TXH_PHY_ANT) | ant; + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL, tmp); +} + +/* This is the opposite of b43_chip_init() */ +static void b43_chip_exit(struct b43_wldev *dev) +{ + b43_phy_exit(dev); + b43_gpio_cleanup(dev); + /* firmware is released later */ +} + +/* Initialize the chip + * http://bcm-specs.sipsolutions.net/ChipInit + */ +static int b43_chip_init(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + int err; + u32 macctl; + u16 value16; + + /* Initialize the MAC control */ + macctl = B43_MACCTL_IHR_ENABLED | B43_MACCTL_SHM_ENABLED; + if (dev->phy.gmode) + macctl |= B43_MACCTL_GMODE; + macctl |= B43_MACCTL_INFRA; + b43_write32(dev, B43_MMIO_MACCTL, macctl); + + err = b43_upload_microcode(dev); + if (err) + goto out; /* firmware is released later */ + + err = b43_gpio_init(dev); + if (err) + goto out; /* firmware is released later */ + + err = b43_upload_initvals(dev); + if (err) + goto err_gpio_clean; + + err = b43_upload_initvals_band(dev); + if (err) + goto err_gpio_clean; + + /* Turn the Analog on and initialize the PHY. */ + phy->ops->switch_analog(dev, 1); + err = b43_phy_init(dev); + if (err) + goto err_gpio_clean; + + /* Disable Interference Mitigation. */ + if (phy->ops->interf_mitigation) + phy->ops->interf_mitigation(dev, B43_INTERFMODE_NONE); + + /* Select the antennae */ + if (phy->ops->set_rx_antenna) + phy->ops->set_rx_antenna(dev, B43_ANTENNA_DEFAULT); + b43_mgmtframe_txantenna(dev, B43_ANTENNA_DEFAULT); + + if (phy->type == B43_PHYTYPE_B) { + value16 = b43_read16(dev, 0x005E); + value16 |= 0x0004; + b43_write16(dev, 0x005E, value16); + } + b43_write32(dev, 0x0100, 0x01000000); + if (dev->dev->core_rev < 5) + b43_write32(dev, 0x010C, 0x01000000); + + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_INFRA, 0); + b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_INFRA); + + /* Probe Response Timeout value */ + /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */ + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRMAXTIME, 0); + + /* Initially set the wireless operation mode. */ + b43_adjust_opmode(dev); + + if (dev->dev->core_rev < 3) { + b43_write16(dev, 0x060E, 0x0000); + b43_write16(dev, 0x0610, 0x8000); + b43_write16(dev, 0x0604, 0x0000); + b43_write16(dev, 0x0606, 0x0200); + } else { + b43_write32(dev, 0x0188, 0x80000000); + b43_write32(dev, 0x018C, 0x02000000); + } + b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, 0x00004000); + b43_write32(dev, B43_MMIO_DMA0_IRQ_MASK, 0x0001FC00); + b43_write32(dev, B43_MMIO_DMA1_IRQ_MASK, 0x0000DC00); + b43_write32(dev, B43_MMIO_DMA2_IRQ_MASK, 0x0000DC00); + b43_write32(dev, B43_MMIO_DMA3_IRQ_MASK, 0x0001DC00); + b43_write32(dev, B43_MMIO_DMA4_IRQ_MASK, 0x0000DC00); + b43_write32(dev, B43_MMIO_DMA5_IRQ_MASK, 0x0000DC00); + + b43_mac_phy_clock_set(dev, true); + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + /* FIXME: 0xE74 is quite common, but should be read from CC */ + b43_write16(dev, B43_MMIO_POWERUP_DELAY, 0xE74); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + b43_write16(dev, B43_MMIO_POWERUP_DELAY, + dev->dev->sdev->bus->chipco.fast_pwrup_delay); + break; +#endif + } + + err = 0; + b43dbg(dev->wl, "Chip initialized\n"); +out: + return err; + +err_gpio_clean: + b43_gpio_cleanup(dev); + return err; +} + +static void b43_periodic_every60sec(struct b43_wldev *dev) +{ + const struct b43_phy_operations *ops = dev->phy.ops; + + if (ops->pwork_60sec) + ops->pwork_60sec(dev); + + /* Force check the TX power emission now. */ + b43_phy_txpower_check(dev, B43_TXPWR_IGNORE_TIME); +} + +static void b43_periodic_every30sec(struct b43_wldev *dev) +{ + /* Update device statistics. */ + b43_calculate_link_quality(dev); +} + +static void b43_periodic_every15sec(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + u16 wdr; + + if (dev->fw.opensource) { + /* Check if the firmware is still alive. + * It will reset the watchdog counter to 0 in its idle loop. */ + wdr = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_WATCHDOG_REG); + if (unlikely(wdr)) { + b43err(dev->wl, "Firmware watchdog: The firmware died!\n"); + b43_controller_restart(dev, "Firmware watchdog"); + return; + } else { + b43_shm_write16(dev, B43_SHM_SCRATCH, + B43_WATCHDOG_REG, 1); + } + } + + if (phy->ops->pwork_15sec) + phy->ops->pwork_15sec(dev); + + atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); + wmb(); + +#if B43_DEBUG + if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { + unsigned int i; + + b43dbg(dev->wl, "Stats: %7u IRQs/sec, %7u TX/sec, %7u RX/sec\n", + dev->irq_count / 15, + dev->tx_count / 15, + dev->rx_count / 15); + dev->irq_count = 0; + dev->tx_count = 0; + dev->rx_count = 0; + for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { + if (dev->irq_bit_count[i]) { + b43dbg(dev->wl, "Stats: %7u IRQ-%02u/sec (0x%08X)\n", + dev->irq_bit_count[i] / 15, i, (1 << i)); + dev->irq_bit_count[i] = 0; + } + } + } +#endif +} + +static void do_periodic_work(struct b43_wldev *dev) +{ + unsigned int state; + + state = dev->periodic_state; + if (state % 4 == 0) + b43_periodic_every60sec(dev); + if (state % 2 == 0) + b43_periodic_every30sec(dev); + b43_periodic_every15sec(dev); +} + +/* Periodic work locking policy: + * The whole periodic work handler is protected by + * wl->mutex. If another lock is needed somewhere in the + * pwork callchain, it's acquired in-place, where it's needed. + */ +static void b43_periodic_work_handler(struct work_struct *work) +{ + struct b43_wldev *dev = container_of(work, struct b43_wldev, + periodic_work.work); + struct b43_wl *wl = dev->wl; + unsigned long delay; + + mutex_lock(&wl->mutex); + + if (unlikely(b43_status(dev) != B43_STAT_STARTED)) + goto out; + if (b43_debug(dev, B43_DBG_PWORK_STOP)) + goto out_requeue; + + do_periodic_work(dev); + + dev->periodic_state++; +out_requeue: + if (b43_debug(dev, B43_DBG_PWORK_FAST)) + delay = msecs_to_jiffies(50); + else + delay = round_jiffies_relative(HZ * 15); + ieee80211_queue_delayed_work(wl->hw, &dev->periodic_work, delay); +out: + mutex_unlock(&wl->mutex); +} + +static void b43_periodic_tasks_setup(struct b43_wldev *dev) +{ + struct delayed_work *work = &dev->periodic_work; + + dev->periodic_state = 0; + INIT_DELAYED_WORK(work, b43_periodic_work_handler); + ieee80211_queue_delayed_work(dev->wl->hw, work, 0); +} + +/* Check if communication with the device works correctly. */ +static int b43_validate_chipaccess(struct b43_wldev *dev) +{ + u32 v, backup0, backup4; + + backup0 = b43_shm_read32(dev, B43_SHM_SHARED, 0); + backup4 = b43_shm_read32(dev, B43_SHM_SHARED, 4); + + /* Check for read/write and endianness problems. */ + b43_shm_write32(dev, B43_SHM_SHARED, 0, 0x55AAAA55); + if (b43_shm_read32(dev, B43_SHM_SHARED, 0) != 0x55AAAA55) + goto error; + b43_shm_write32(dev, B43_SHM_SHARED, 0, 0xAA5555AA); + if (b43_shm_read32(dev, B43_SHM_SHARED, 0) != 0xAA5555AA) + goto error; + + /* Check if unaligned 32bit SHM_SHARED access works properly. + * However, don't bail out on failure, because it's noncritical. */ + b43_shm_write16(dev, B43_SHM_SHARED, 0, 0x1122); + b43_shm_write16(dev, B43_SHM_SHARED, 2, 0x3344); + b43_shm_write16(dev, B43_SHM_SHARED, 4, 0x5566); + b43_shm_write16(dev, B43_SHM_SHARED, 6, 0x7788); + if (b43_shm_read32(dev, B43_SHM_SHARED, 2) != 0x55663344) + b43warn(dev->wl, "Unaligned 32bit SHM read access is broken\n"); + b43_shm_write32(dev, B43_SHM_SHARED, 2, 0xAABBCCDD); + if (b43_shm_read16(dev, B43_SHM_SHARED, 0) != 0x1122 || + b43_shm_read16(dev, B43_SHM_SHARED, 2) != 0xCCDD || + b43_shm_read16(dev, B43_SHM_SHARED, 4) != 0xAABB || + b43_shm_read16(dev, B43_SHM_SHARED, 6) != 0x7788) + b43warn(dev->wl, "Unaligned 32bit SHM write access is broken\n"); + + b43_shm_write32(dev, B43_SHM_SHARED, 0, backup0); + b43_shm_write32(dev, B43_SHM_SHARED, 4, backup4); + + if ((dev->dev->core_rev >= 3) && (dev->dev->core_rev <= 10)) { + /* The 32bit register shadows the two 16bit registers + * with update sideeffects. Validate this. */ + b43_write16(dev, B43_MMIO_TSF_CFP_START, 0xAAAA); + b43_write32(dev, B43_MMIO_TSF_CFP_START, 0xCCCCBBBB); + if (b43_read16(dev, B43_MMIO_TSF_CFP_START_LOW) != 0xBBBB) + goto error; + if (b43_read16(dev, B43_MMIO_TSF_CFP_START_HIGH) != 0xCCCC) + goto error; + } + b43_write32(dev, B43_MMIO_TSF_CFP_START, 0); + + v = b43_read32(dev, B43_MMIO_MACCTL); + v |= B43_MACCTL_GMODE; + if (v != (B43_MACCTL_GMODE | B43_MACCTL_IHR_ENABLED)) + goto error; + + return 0; +error: + b43err(dev->wl, "Failed to validate the chipaccess\n"); + return -ENODEV; +} + +static void b43_security_init(struct b43_wldev *dev) +{ + dev->ktp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_KTP); + /* KTP is a word address, but we address SHM bytewise. + * So multiply by two. + */ + dev->ktp *= 2; + /* Number of RCMTA address slots */ + b43_write16(dev, B43_MMIO_RCMTA_COUNT, B43_NR_PAIRWISE_KEYS); + /* Clear the key memory. */ + b43_clear_keys(dev); +} + +#ifdef CONFIG_B43_HWRNG +static int b43_rng_read(struct hwrng *rng, u32 *data) +{ + struct b43_wl *wl = (struct b43_wl *)rng->priv; + struct b43_wldev *dev; + int count = -ENODEV; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (likely(dev && b43_status(dev) >= B43_STAT_INITIALIZED)) { + *data = b43_read16(dev, B43_MMIO_RNG); + count = sizeof(u16); + } + mutex_unlock(&wl->mutex); + + return count; +} +#endif /* CONFIG_B43_HWRNG */ + +static void b43_rng_exit(struct b43_wl *wl) +{ +#ifdef CONFIG_B43_HWRNG + if (wl->rng_initialized) + hwrng_unregister(&wl->rng); +#endif /* CONFIG_B43_HWRNG */ +} + +static int b43_rng_init(struct b43_wl *wl) +{ + int err = 0; + +#ifdef CONFIG_B43_HWRNG + snprintf(wl->rng_name, ARRAY_SIZE(wl->rng_name), + "%s_%s", KBUILD_MODNAME, wiphy_name(wl->hw->wiphy)); + wl->rng.name = wl->rng_name; + wl->rng.data_read = b43_rng_read; + wl->rng.priv = (unsigned long)wl; + wl->rng_initialized = true; + err = hwrng_register(&wl->rng); + if (err) { + wl->rng_initialized = false; + b43err(wl, "Failed to register the random " + "number generator (%d)\n", err); + } +#endif /* CONFIG_B43_HWRNG */ + + return err; +} + +static void b43_tx_work(struct work_struct *work) +{ + struct b43_wl *wl = container_of(work, struct b43_wl, tx_work); + struct b43_wldev *dev; + struct sk_buff *skb; + int queue_num; + int err = 0; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED)) { + mutex_unlock(&wl->mutex); + return; + } + + for (queue_num = 0; queue_num < B43_QOS_QUEUE_NUM; queue_num++) { + while (skb_queue_len(&wl->tx_queue[queue_num])) { + skb = skb_dequeue(&wl->tx_queue[queue_num]); + if (b43_using_pio_transfers(dev)) + err = b43_pio_tx(dev, skb); + else + err = b43_dma_tx(dev, skb); + if (err == -ENOSPC) { + wl->tx_queue_stopped[queue_num] = 1; + ieee80211_stop_queue(wl->hw, queue_num); + skb_queue_head(&wl->tx_queue[queue_num], skb); + break; + } + if (unlikely(err)) + ieee80211_free_txskb(wl->hw, skb); + err = 0; + } + + if (!err) + wl->tx_queue_stopped[queue_num] = 0; + } + +#if B43_DEBUG + dev->tx_count++; +#endif + mutex_unlock(&wl->mutex); +} + +static void b43_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + + if (unlikely(skb->len < 2 + 2 + 6)) { + /* Too short, this can't be a valid frame. */ + ieee80211_free_txskb(hw, skb); + return; + } + B43_WARN_ON(skb_shinfo(skb)->nr_frags); + + skb_queue_tail(&wl->tx_queue[skb->queue_mapping], skb); + if (!wl->tx_queue_stopped[skb->queue_mapping]) { + ieee80211_queue_work(wl->hw, &wl->tx_work); + } else { + ieee80211_stop_queue(wl->hw, skb->queue_mapping); + } +} + +static void b43_qos_params_upload(struct b43_wldev *dev, + const struct ieee80211_tx_queue_params *p, + u16 shm_offset) +{ + u16 params[B43_NR_QOSPARAMS]; + int bslots, tmp; + unsigned int i; + + if (!dev->qos_enabled) + return; + + bslots = b43_read16(dev, B43_MMIO_RNG) & p->cw_min; + + memset(¶ms, 0, sizeof(params)); + + params[B43_QOSPARAM_TXOP] = p->txop * 32; + params[B43_QOSPARAM_CWMIN] = p->cw_min; + params[B43_QOSPARAM_CWMAX] = p->cw_max; + params[B43_QOSPARAM_CWCUR] = p->cw_min; + params[B43_QOSPARAM_AIFS] = p->aifs; + params[B43_QOSPARAM_BSLOTS] = bslots; + params[B43_QOSPARAM_REGGAP] = bslots + p->aifs; + + for (i = 0; i < ARRAY_SIZE(params); i++) { + if (i == B43_QOSPARAM_STATUS) { + tmp = b43_shm_read16(dev, B43_SHM_SHARED, + shm_offset + (i * 2)); + /* Mark the parameters as updated. */ + tmp |= 0x100; + b43_shm_write16(dev, B43_SHM_SHARED, + shm_offset + (i * 2), + tmp); + } else { + b43_shm_write16(dev, B43_SHM_SHARED, + shm_offset + (i * 2), + params[i]); + } + } +} + +/* Mapping of mac80211 queue numbers to b43 QoS SHM offsets. */ +static const u16 b43_qos_shm_offsets[] = { + /* [mac80211-queue-nr] = SHM_OFFSET, */ + [0] = B43_QOS_VOICE, + [1] = B43_QOS_VIDEO, + [2] = B43_QOS_BESTEFFORT, + [3] = B43_QOS_BACKGROUND, +}; + +/* Update all QOS parameters in hardware. */ +static void b43_qos_upload_all(struct b43_wldev *dev) +{ + struct b43_wl *wl = dev->wl; + struct b43_qos_params *params; + unsigned int i; + + if (!dev->qos_enabled) + return; + + BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) != + ARRAY_SIZE(wl->qos_params)); + + b43_mac_suspend(dev); + for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) { + params = &(wl->qos_params[i]); + b43_qos_params_upload(dev, &(params->p), + b43_qos_shm_offsets[i]); + } + b43_mac_enable(dev); +} + +static void b43_qos_clear(struct b43_wl *wl) +{ + struct b43_qos_params *params; + unsigned int i; + + /* Initialize QoS parameters to sane defaults. */ + + BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) != + ARRAY_SIZE(wl->qos_params)); + + for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) { + params = &(wl->qos_params[i]); + + switch (b43_qos_shm_offsets[i]) { + case B43_QOS_VOICE: + params->p.txop = 0; + params->p.aifs = 2; + params->p.cw_min = 0x0001; + params->p.cw_max = 0x0001; + break; + case B43_QOS_VIDEO: + params->p.txop = 0; + params->p.aifs = 2; + params->p.cw_min = 0x0001; + params->p.cw_max = 0x0001; + break; + case B43_QOS_BESTEFFORT: + params->p.txop = 0; + params->p.aifs = 3; + params->p.cw_min = 0x0001; + params->p.cw_max = 0x03FF; + break; + case B43_QOS_BACKGROUND: + params->p.txop = 0; + params->p.aifs = 7; + params->p.cw_min = 0x0001; + params->p.cw_max = 0x03FF; + break; + default: + B43_WARN_ON(1); + } + } +} + +/* Initialize the core's QOS capabilities */ +static void b43_qos_init(struct b43_wldev *dev) +{ + if (!dev->qos_enabled) { + /* Disable QOS support. */ + b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_EDCF); + b43_write16(dev, B43_MMIO_IFSCTL, + b43_read16(dev, B43_MMIO_IFSCTL) + & ~B43_MMIO_IFSCTL_USE_EDCF); + b43dbg(dev->wl, "QoS disabled\n"); + return; + } + + /* Upload the current QOS parameters. */ + b43_qos_upload_all(dev); + + /* Enable QOS support. */ + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF); + b43_write16(dev, B43_MMIO_IFSCTL, + b43_read16(dev, B43_MMIO_IFSCTL) + | B43_MMIO_IFSCTL_USE_EDCF); + b43dbg(dev->wl, "QoS enabled\n"); +} + +static int b43_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 _queue, + const struct ieee80211_tx_queue_params *params) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev; + unsigned int queue = (unsigned int)_queue; + int err = -ENODEV; + + if (queue >= ARRAY_SIZE(wl->qos_params)) { + /* Queue not available or don't support setting + * params on this queue. Return success to not + * confuse mac80211. */ + return 0; + } + BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) != + ARRAY_SIZE(wl->qos_params)); + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (unlikely(!dev || (b43_status(dev) < B43_STAT_INITIALIZED))) + goto out_unlock; + + memcpy(&(wl->qos_params[queue].p), params, sizeof(*params)); + b43_mac_suspend(dev); + b43_qos_params_upload(dev, &(wl->qos_params[queue].p), + b43_qos_shm_offsets[queue]); + b43_mac_enable(dev); + err = 0; + +out_unlock: + mutex_unlock(&wl->mutex); + + return err; +} + +static int b43_op_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + + mutex_lock(&wl->mutex); + memcpy(stats, &wl->ieee_stats, sizeof(*stats)); + mutex_unlock(&wl->mutex); + + return 0; +} + +static u64 b43_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev; + u64 tsf; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + + if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) + b43_tsf_read(dev, &tsf); + else + tsf = 0; + + mutex_unlock(&wl->mutex); + + return tsf; +} + +static void b43_op_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u64 tsf) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + + if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) + b43_tsf_write(dev, tsf); + + mutex_unlock(&wl->mutex); +} + +static const char *band_to_string(enum ieee80211_band band) +{ + switch (band) { + case IEEE80211_BAND_5GHZ: + return "5"; + case IEEE80211_BAND_2GHZ: + return "2.4"; + default: + break; + } + B43_WARN_ON(1); + return ""; +} + +/* Expects wl->mutex locked */ +static int b43_switch_band(struct b43_wldev *dev, + struct ieee80211_channel *chan) +{ + struct b43_phy *phy = &dev->phy; + bool gmode; + u32 tmp; + + switch (chan->band) { + case IEEE80211_BAND_5GHZ: + gmode = false; + break; + case IEEE80211_BAND_2GHZ: + gmode = true; + break; + default: + B43_WARN_ON(1); + return -EINVAL; + } + + if (!((gmode && phy->supports_2ghz) || + (!gmode && phy->supports_5ghz))) { + b43err(dev->wl, "This device doesn't support %s-GHz band\n", + band_to_string(chan->band)); + return -ENODEV; + } + + if (!!phy->gmode == !!gmode) { + /* This device is already running. */ + return 0; + } + + b43dbg(dev->wl, "Switching to %s GHz band\n", + band_to_string(chan->band)); + + /* Some new devices don't need disabling radio for band switching */ + if (!(phy->type == B43_PHYTYPE_N && phy->rev >= 3)) + b43_software_rfkill(dev, true); + + phy->gmode = gmode; + b43_phy_put_into_reset(dev); + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + if (gmode) + tmp |= B43_BCMA_IOCTL_GMODE; + else + tmp &= ~B43_BCMA_IOCTL_GMODE; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + if (gmode) + tmp |= B43_TMSLOW_GMODE; + else + tmp &= ~B43_TMSLOW_GMODE; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + break; +#endif + } + b43_phy_take_out_of_reset(dev); + + b43_upload_initvals_band(dev); + + b43_phy_init(dev); + + return 0; +} + +static void b43_set_beacon_listen_interval(struct b43_wldev *dev, u16 interval) +{ + interval = min_t(u16, interval, (u16)0xFF); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BCN_LI, interval); +} + +/* Write the short and long frame retry limit values. */ +static void b43_set_retry_limits(struct b43_wldev *dev, + unsigned int short_retry, + unsigned int long_retry) +{ + /* The retry limit is a 4-bit counter. Enforce this to avoid overflowing + * the chip-internal counter. */ + short_retry = min(short_retry, (unsigned int)0xF); + long_retry = min(long_retry, (unsigned int)0xF); + + b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_SRLIMIT, + short_retry); + b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_LRLIMIT, + long_retry); +} + +static int b43_op_config(struct ieee80211_hw *hw, u32 changed) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev = wl->current_dev; + struct b43_phy *phy = &dev->phy; + struct ieee80211_conf *conf = &hw->conf; + int antenna; + int err = 0; + + mutex_lock(&wl->mutex); + b43_mac_suspend(dev); + + if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) + b43_set_beacon_listen_interval(dev, conf->listen_interval); + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + phy->chandef = &conf->chandef; + phy->channel = conf->chandef.chan->hw_value; + + /* Switch the band (if necessary). */ + err = b43_switch_band(dev, conf->chandef.chan); + if (err) + goto out_mac_enable; + + /* Switch to the requested channel. + * The firmware takes care of races with the TX handler. + */ + b43_switch_channel(dev, phy->channel); + } + + if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + b43_set_retry_limits(dev, conf->short_frame_max_tx_count, + conf->long_frame_max_tx_count); + changed &= ~IEEE80211_CONF_CHANGE_RETRY_LIMITS; + if (!changed) + goto out_mac_enable; + + dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR); + + /* Adjust the desired TX power level. */ + if (conf->power_level != 0) { + if (conf->power_level != phy->desired_txpower) { + phy->desired_txpower = conf->power_level; + b43_phy_txpower_check(dev, B43_TXPWR_IGNORE_TIME | + B43_TXPWR_IGNORE_TSSI); + } + } + + /* Antennas for RX and management frame TX. */ + antenna = B43_ANTENNA_DEFAULT; + b43_mgmtframe_txantenna(dev, antenna); + antenna = B43_ANTENNA_DEFAULT; + if (phy->ops->set_rx_antenna) + phy->ops->set_rx_antenna(dev, antenna); + + if (wl->radio_enabled != phy->radio_on) { + if (wl->radio_enabled) { + b43_software_rfkill(dev, false); + b43info(dev->wl, "Radio turned on by software\n"); + if (!dev->radio_hw_enable) { + b43info(dev->wl, "The hardware RF-kill button " + "still turns the radio physically off. " + "Press the button to turn it on.\n"); + } + } else { + b43_software_rfkill(dev, true); + b43info(dev->wl, "Radio turned off by software\n"); + } + } + +out_mac_enable: + b43_mac_enable(dev); + mutex_unlock(&wl->mutex); + + return err; +} + +static void b43_update_basic_rates(struct b43_wldev *dev, u32 brates) +{ + struct ieee80211_supported_band *sband = + dev->wl->hw->wiphy->bands[b43_current_band(dev->wl)]; + struct ieee80211_rate *rate; + int i; + u16 basic, direct, offset, basic_offset, rateptr; + + for (i = 0; i < sband->n_bitrates; i++) { + rate = &sband->bitrates[i]; + + if (b43_is_cck_rate(rate->hw_value)) { + direct = B43_SHM_SH_CCKDIRECT; + basic = B43_SHM_SH_CCKBASIC; + offset = b43_plcp_get_ratecode_cck(rate->hw_value); + offset &= 0xF; + } else { + direct = B43_SHM_SH_OFDMDIRECT; + basic = B43_SHM_SH_OFDMBASIC; + offset = b43_plcp_get_ratecode_ofdm(rate->hw_value); + offset &= 0xF; + } + + rate = ieee80211_get_response_rate(sband, brates, rate->bitrate); + + if (b43_is_cck_rate(rate->hw_value)) { + basic_offset = b43_plcp_get_ratecode_cck(rate->hw_value); + basic_offset &= 0xF; + } else { + basic_offset = b43_plcp_get_ratecode_ofdm(rate->hw_value); + basic_offset &= 0xF; + } + + /* + * Get the pointer that we need to point to + * from the direct map + */ + rateptr = b43_shm_read16(dev, B43_SHM_SHARED, + direct + 2 * basic_offset); + /* and write it to the basic map */ + b43_shm_write16(dev, B43_SHM_SHARED, basic + 2 * offset, + rateptr); + } +} + +static void b43_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, + u32 changed) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev; + + mutex_lock(&wl->mutex); + + dev = wl->current_dev; + if (!dev || b43_status(dev) < B43_STAT_STARTED) + goto out_unlock_mutex; + + B43_WARN_ON(wl->vif != vif); + + if (changed & BSS_CHANGED_BSSID) { + if (conf->bssid) + memcpy(wl->bssid, conf->bssid, ETH_ALEN); + else + eth_zero_addr(wl->bssid); + } + + if (b43_status(dev) >= B43_STAT_INITIALIZED) { + if (changed & BSS_CHANGED_BEACON && + (b43_is_mode(wl, NL80211_IFTYPE_AP) || + b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) || + b43_is_mode(wl, NL80211_IFTYPE_ADHOC))) + b43_update_templates(wl); + + if (changed & BSS_CHANGED_BSSID) + b43_write_mac_bssid_templates(dev); + } + + b43_mac_suspend(dev); + + /* Update templates for AP/mesh mode. */ + if (changed & BSS_CHANGED_BEACON_INT && + (b43_is_mode(wl, NL80211_IFTYPE_AP) || + b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) || + b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) && + conf->beacon_int) + b43_set_beacon_int(dev, conf->beacon_int); + + if (changed & BSS_CHANGED_BASIC_RATES) + b43_update_basic_rates(dev, conf->basic_rates); + + if (changed & BSS_CHANGED_ERP_SLOT) { + if (conf->use_short_slot) + b43_short_slot_timing_enable(dev); + else + b43_short_slot_timing_disable(dev); + } + + b43_mac_enable(dev); +out_unlock_mutex: + mutex_unlock(&wl->mutex); +} + +static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev; + u8 algorithm; + u8 index; + int err; + static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + if (modparam_nohwcrypt) + return -ENOSPC; /* User disabled HW-crypto */ + + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + /* + * For now, disable hw crypto for the RSN IBSS group keys. This + * could be optimized in the future, but until that gets + * implemented, use of software crypto for group addressed + * frames is a acceptable to allow RSN IBSS to be used. + */ + return -EOPNOTSUPP; + } + + mutex_lock(&wl->mutex); + + dev = wl->current_dev; + err = -ENODEV; + if (!dev || b43_status(dev) < B43_STAT_INITIALIZED) + goto out_unlock; + + if (dev->fw.pcm_request_failed || !dev->hwcrypto_enabled) { + /* We don't have firmware for the crypto engine. + * Must use software-crypto. */ + err = -EOPNOTSUPP; + goto out_unlock; + } + + err = -EINVAL; + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + algorithm = B43_SEC_ALGO_WEP40; + break; + case WLAN_CIPHER_SUITE_WEP104: + algorithm = B43_SEC_ALGO_WEP104; + break; + case WLAN_CIPHER_SUITE_TKIP: + algorithm = B43_SEC_ALGO_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + algorithm = B43_SEC_ALGO_AES; + break; + default: + B43_WARN_ON(1); + goto out_unlock; + } + index = (u8) (key->keyidx); + if (index > 3) + goto out_unlock; + + switch (cmd) { + case SET_KEY: + if (algorithm == B43_SEC_ALGO_TKIP && + (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) || + !modparam_hwtkip)) { + /* We support only pairwise key */ + err = -EOPNOTSUPP; + goto out_unlock; + } + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { + if (WARN_ON(!sta)) { + err = -EOPNOTSUPP; + goto out_unlock; + } + /* Pairwise key with an assigned MAC address. */ + err = b43_key_write(dev, -1, algorithm, + key->key, key->keylen, + sta->addr, key); + } else { + /* Group key */ + err = b43_key_write(dev, index, algorithm, + key->key, key->keylen, NULL, key); + } + if (err) + goto out_unlock; + + if (algorithm == B43_SEC_ALGO_WEP40 || + algorithm == B43_SEC_ALGO_WEP104) { + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_USEDEFKEYS); + } else { + b43_hf_write(dev, + b43_hf_read(dev) & ~B43_HF_USEDEFKEYS); + } + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + if (algorithm == B43_SEC_ALGO_TKIP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + break; + case DISABLE_KEY: { + err = b43_key_clear(dev, key->hw_key_idx); + if (err) + goto out_unlock; + break; + } + default: + B43_WARN_ON(1); + } + +out_unlock: + if (!err) { + b43dbg(wl, "%s hardware based encryption for keyidx: %d, " + "mac: %pM\n", + cmd == SET_KEY ? "Using" : "Disabling", key->keyidx, + sta ? sta->addr : bcast_addr); + b43_dump_keymemory(dev); + } + mutex_unlock(&wl->mutex); + + return err; +} + +static void b43_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed, unsigned int *fflags, + u64 multicast) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (!dev) { + *fflags = 0; + goto out_unlock; + } + + *fflags &= FIF_ALLMULTI | + FIF_FCSFAIL | + FIF_PLCPFAIL | + FIF_CONTROL | + FIF_OTHER_BSS | + FIF_BCN_PRBRESP_PROMISC; + + changed &= FIF_ALLMULTI | + FIF_FCSFAIL | + FIF_PLCPFAIL | + FIF_CONTROL | + FIF_OTHER_BSS | + FIF_BCN_PRBRESP_PROMISC; + + wl->filter_flags = *fflags; + + if (changed && b43_status(dev) >= B43_STAT_INITIALIZED) + b43_adjust_opmode(dev); + +out_unlock: + mutex_unlock(&wl->mutex); +} + +/* Locking: wl->mutex + * Returns the current dev. This might be different from the passed in dev, + * because the core might be gone away while we unlocked the mutex. */ +static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev) +{ + struct b43_wl *wl; + struct b43_wldev *orig_dev; + u32 mask; + int queue_num; + + if (!dev) + return NULL; + wl = dev->wl; +redo: + if (!dev || b43_status(dev) < B43_STAT_STARTED) + return dev; + + /* Cancel work. Unlock to avoid deadlocks. */ + mutex_unlock(&wl->mutex); + cancel_delayed_work_sync(&dev->periodic_work); + cancel_work_sync(&wl->tx_work); + b43_leds_stop(dev); + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (!dev || b43_status(dev) < B43_STAT_STARTED) { + /* Whoops, aliens ate up the device while we were unlocked. */ + return dev; + } + + /* Disable interrupts on the device. */ + b43_set_status(dev, B43_STAT_INITIALIZED); + if (b43_bus_host_is_sdio(dev->dev)) { + /* wl->mutex is locked. That is enough. */ + b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); + b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ + } else { + spin_lock_irq(&wl->hardirq_lock); + b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); + b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ + spin_unlock_irq(&wl->hardirq_lock); + } + /* Synchronize and free the interrupt handlers. Unlock to avoid deadlocks. */ + orig_dev = dev; + mutex_unlock(&wl->mutex); + if (b43_bus_host_is_sdio(dev->dev)) { + b43_sdio_free_irq(dev); + } else { + synchronize_irq(dev->dev->irq); + free_irq(dev->dev->irq, dev); + } + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (!dev) + return dev; + if (dev != orig_dev) { + if (b43_status(dev) >= B43_STAT_STARTED) + goto redo; + return dev; + } + mask = b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); + B43_WARN_ON(mask != 0xFFFFFFFF && mask); + + /* Drain all TX queues. */ + for (queue_num = 0; queue_num < B43_QOS_QUEUE_NUM; queue_num++) { + while (skb_queue_len(&wl->tx_queue[queue_num])) { + struct sk_buff *skb; + + skb = skb_dequeue(&wl->tx_queue[queue_num]); + ieee80211_free_txskb(wl->hw, skb); + } + } + + b43_mac_suspend(dev); + b43_leds_exit(dev); + b43dbg(wl, "Wireless interface stopped\n"); + + return dev; +} + +/* Locking: wl->mutex */ +static int b43_wireless_core_start(struct b43_wldev *dev) +{ + int err; + + B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED); + + drain_txstatus_queue(dev); + if (b43_bus_host_is_sdio(dev->dev)) { + err = b43_sdio_request_irq(dev, b43_sdio_interrupt_handler); + if (err) { + b43err(dev->wl, "Cannot request SDIO IRQ\n"); + goto out; + } + } else { + err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, + b43_interrupt_thread_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (err) { + b43err(dev->wl, "Cannot request IRQ-%d\n", + dev->dev->irq); + goto out; + } + } + + /* We are ready to run. */ + ieee80211_wake_queues(dev->wl->hw); + b43_set_status(dev, B43_STAT_STARTED); + + /* Start data flow (TX/RX). */ + b43_mac_enable(dev); + b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); + + /* Start maintenance work */ + b43_periodic_tasks_setup(dev); + + b43_leds_init(dev); + + b43dbg(dev->wl, "Wireless interface started\n"); +out: + return err; +} + +static char *b43_phy_name(struct b43_wldev *dev, u8 phy_type) +{ + switch (phy_type) { + case B43_PHYTYPE_A: + return "A"; + case B43_PHYTYPE_B: + return "B"; + case B43_PHYTYPE_G: + return "G"; + case B43_PHYTYPE_N: + return "N"; + case B43_PHYTYPE_LP: + return "LP"; + case B43_PHYTYPE_SSLPN: + return "SSLPN"; + case B43_PHYTYPE_HT: + return "HT"; + case B43_PHYTYPE_LCN: + return "LCN"; + case B43_PHYTYPE_LCNXN: + return "LCNXN"; + case B43_PHYTYPE_LCN40: + return "LCN40"; + case B43_PHYTYPE_AC: + return "AC"; + } + return "UNKNOWN"; +} + +/* Get PHY and RADIO versioning numbers */ +static int b43_phy_versioning(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + const u8 core_rev = dev->dev->core_rev; + u32 tmp; + u8 analog_type; + u8 phy_type; + u8 phy_rev; + u16 radio_manuf; + u16 radio_id; + u16 radio_rev; + u8 radio_ver; + int unsupported = 0; + + /* Get PHY versioning */ + tmp = b43_read16(dev, B43_MMIO_PHY_VER); + analog_type = (tmp & B43_PHYVER_ANALOG) >> B43_PHYVER_ANALOG_SHIFT; + phy_type = (tmp & B43_PHYVER_TYPE) >> B43_PHYVER_TYPE_SHIFT; + phy_rev = (tmp & B43_PHYVER_VERSION); + + /* LCNXN is continuation of N which run out of revisions */ + if (phy_type == B43_PHYTYPE_LCNXN) { + phy_type = B43_PHYTYPE_N; + phy_rev += 16; + } + + switch (phy_type) { +#ifdef CONFIG_B43_PHY_G + case B43_PHYTYPE_G: + if (phy_rev > 9) + unsupported = 1; + break; +#endif +#ifdef CONFIG_B43_PHY_N + case B43_PHYTYPE_N: + if (phy_rev >= 19) + unsupported = 1; + break; +#endif +#ifdef CONFIG_B43_PHY_LP + case B43_PHYTYPE_LP: + if (phy_rev > 2) + unsupported = 1; + break; +#endif +#ifdef CONFIG_B43_PHY_HT + case B43_PHYTYPE_HT: + if (phy_rev > 1) + unsupported = 1; + break; +#endif +#ifdef CONFIG_B43_PHY_LCN + case B43_PHYTYPE_LCN: + if (phy_rev > 1) + unsupported = 1; + break; +#endif +#ifdef CONFIG_B43_PHY_AC + case B43_PHYTYPE_AC: + if (phy_rev > 1) + unsupported = 1; + break; +#endif + default: + unsupported = 1; + } + if (unsupported) { + b43err(dev->wl, "FOUND UNSUPPORTED PHY (Analog %u, Type %d (%s), Revision %u)\n", + analog_type, phy_type, b43_phy_name(dev, phy_type), + phy_rev); + return -EOPNOTSUPP; + } + b43info(dev->wl, "Found PHY: Analog %u, Type %d (%s), Revision %u\n", + analog_type, phy_type, b43_phy_name(dev, phy_type), phy_rev); + + /* Get RADIO versioning */ + if (core_rev == 40 || core_rev == 42) { + radio_manuf = 0x17F; + + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, 0); + radio_rev = b43_read16(dev, B43_MMIO_RADIO24_DATA); + + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, 1); + radio_id = b43_read16(dev, B43_MMIO_RADIO24_DATA); + + radio_ver = 0; /* Is there version somewhere? */ + } else if (core_rev >= 24) { + u16 radio24[3]; + + for (tmp = 0; tmp < 3; tmp++) { + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, tmp); + radio24[tmp] = b43_read16(dev, B43_MMIO_RADIO24_DATA); + } + + radio_manuf = 0x17F; + radio_id = (radio24[2] << 8) | radio24[1]; + radio_rev = (radio24[0] & 0xF); + radio_ver = (radio24[0] & 0xF0) >> 4; + } else { + if (dev->dev->chip_id == 0x4317) { + if (dev->dev->chip_rev == 0) + tmp = 0x3205017F; + else if (dev->dev->chip_rev == 1) + tmp = 0x4205017F; + else + tmp = 0x5205017F; + } else { + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, + B43_RADIOCTL_ID); + tmp = b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, + B43_RADIOCTL_ID); + tmp |= b43_read16(dev, B43_MMIO_RADIO_DATA_HIGH) << 16; + } + radio_manuf = (tmp & 0x00000FFF); + radio_id = (tmp & 0x0FFFF000) >> 12; + radio_rev = (tmp & 0xF0000000) >> 28; + radio_ver = 0; /* Probably not available on old hw */ + } + + if (radio_manuf != 0x17F /* Broadcom */) + unsupported = 1; + switch (phy_type) { + case B43_PHYTYPE_A: + if (radio_id != 0x2060) + unsupported = 1; + if (radio_rev != 1) + unsupported = 1; + if (radio_manuf != 0x17F) + unsupported = 1; + break; + case B43_PHYTYPE_B: + if ((radio_id & 0xFFF0) != 0x2050) + unsupported = 1; + break; + case B43_PHYTYPE_G: + if (radio_id != 0x2050) + unsupported = 1; + break; + case B43_PHYTYPE_N: + if (radio_id != 0x2055 && radio_id != 0x2056 && + radio_id != 0x2057) + unsupported = 1; + if (radio_id == 0x2057 && + !(radio_rev == 9 || radio_rev == 14)) + unsupported = 1; + break; + case B43_PHYTYPE_LP: + if (radio_id != 0x2062 && radio_id != 0x2063) + unsupported = 1; + break; + case B43_PHYTYPE_HT: + if (radio_id != 0x2059) + unsupported = 1; + break; + case B43_PHYTYPE_LCN: + if (radio_id != 0x2064) + unsupported = 1; + break; + case B43_PHYTYPE_AC: + if (radio_id != 0x2069) + unsupported = 1; + break; + default: + B43_WARN_ON(1); + } + if (unsupported) { + b43err(dev->wl, + "FOUND UNSUPPORTED RADIO (Manuf 0x%X, ID 0x%X, Revision %u, Version %u)\n", + radio_manuf, radio_id, radio_rev, radio_ver); + return -EOPNOTSUPP; + } + b43info(dev->wl, + "Found Radio: Manuf 0x%X, ID 0x%X, Revision %u, Version %u\n", + radio_manuf, radio_id, radio_rev, radio_ver); + + /* FIXME: b43 treats "id" as "ver" and ignores the real "ver" */ + phy->radio_manuf = radio_manuf; + phy->radio_ver = radio_id; + phy->radio_rev = radio_rev; + + phy->analog = analog_type; + phy->type = phy_type; + phy->rev = phy_rev; + + return 0; +} + +static void setup_struct_phy_for_init(struct b43_wldev *dev, + struct b43_phy *phy) +{ + phy->hardware_power_control = !!modparam_hwpctl; + phy->next_txpwr_check_time = jiffies; + /* PHY TX errors counter. */ + atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); + +#if B43_DEBUG + phy->phy_locked = false; + phy->radio_locked = false; +#endif +} + +static void setup_struct_wldev_for_init(struct b43_wldev *dev) +{ + dev->dfq_valid = false; + + /* Assume the radio is enabled. If it's not enabled, the state will + * immediately get fixed on the first periodic work run. */ + dev->radio_hw_enable = true; + + /* Stats */ + memset(&dev->stats, 0, sizeof(dev->stats)); + + setup_struct_phy_for_init(dev, &dev->phy); + + /* IRQ related flags */ + dev->irq_reason = 0; + memset(dev->dma_reason, 0, sizeof(dev->dma_reason)); + dev->irq_mask = B43_IRQ_MASKTEMPLATE; + if (b43_modparam_verbose < B43_VERBOSITY_DEBUG) + dev->irq_mask &= ~B43_IRQ_PHY_TXERR; + + dev->mac_suspended = 1; + + /* Noise calculation context */ + memset(&dev->noisecalc, 0, sizeof(dev->noisecalc)); +} + +static void b43_bluetooth_coext_enable(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + u64 hf; + + if (!modparam_btcoex) + return; + if (!(sprom->boardflags_lo & B43_BFL_BTCOEXIST)) + return; + if (dev->phy.type != B43_PHYTYPE_B && !dev->phy.gmode) + return; + + hf = b43_hf_read(dev); + if (sprom->boardflags_lo & B43_BFL_BTCMOD) + hf |= B43_HF_BTCOEXALT; + else + hf |= B43_HF_BTCOEX; + b43_hf_write(dev, hf); +} + +static void b43_bluetooth_coext_disable(struct b43_wldev *dev) +{ + if (!modparam_btcoex) + return; + //TODO +} + +static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev) +{ + struct ssb_bus *bus; + u32 tmp; + +#ifdef CONFIG_B43_SSB + if (dev->dev->bus_type != B43_BUS_SSB) + return; +#else + return; +#endif + + bus = dev->dev->sdev->bus; + + if ((bus->chip_id == 0x4311 && bus->chip_rev == 2) || + (bus->chip_id == 0x4312)) { + tmp = ssb_read32(dev->dev->sdev, SSB_IMCFGLO); + tmp &= ~SSB_IMCFGLO_REQTO; + tmp &= ~SSB_IMCFGLO_SERTO; + tmp |= 0x3; + ssb_write32(dev->dev->sdev, SSB_IMCFGLO, tmp); + ssb_commit_settings(bus); + } +} + +static void b43_set_synth_pu_delay(struct b43_wldev *dev, bool idle) +{ + u16 pu_delay; + + /* The time value is in microseconds. */ + if (dev->phy.type == B43_PHYTYPE_A) + pu_delay = 3700; + else + pu_delay = 1050; + if (b43_is_mode(dev->wl, NL80211_IFTYPE_ADHOC) || idle) + pu_delay = 500; + if ((dev->phy.radio_ver == 0x2050) && (dev->phy.radio_rev == 8)) + pu_delay = max(pu_delay, (u16)2400); + + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_SPUWKUP, pu_delay); +} + +/* Set the TSF CFP pre-TargetBeaconTransmissionTime. */ +static void b43_set_pretbtt(struct b43_wldev *dev) +{ + u16 pretbtt; + + /* The time value is in microseconds. */ + if (b43_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) { + pretbtt = 2; + } else { + if (dev->phy.type == B43_PHYTYPE_A) + pretbtt = 120; + else + pretbtt = 250; + } + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRETBTT, pretbtt); + b43_write16(dev, B43_MMIO_TSF_CFP_PRETBTT, pretbtt); +} + +/* Shutdown a wireless core */ +/* Locking: wl->mutex */ +static void b43_wireless_core_exit(struct b43_wldev *dev) +{ + B43_WARN_ON(dev && b43_status(dev) > B43_STAT_INITIALIZED); + if (!dev || b43_status(dev) != B43_STAT_INITIALIZED) + return; + + b43_set_status(dev, B43_STAT_UNINIT); + + /* Stop the microcode PSM. */ + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN, + B43_MACCTL_PSM_JMP0); + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + bcma_host_pci_down(dev->dev->bdev->bus); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + /* TODO */ + break; +#endif + } + + b43_dma_free(dev); + b43_pio_free(dev); + b43_chip_exit(dev); + dev->phy.ops->switch_analog(dev, 0); + if (dev->wl->current_beacon) { + dev_kfree_skb_any(dev->wl->current_beacon); + dev->wl->current_beacon = NULL; + } + + b43_device_disable(dev, 0); + b43_bus_may_powerdown(dev); +} + +/* Initialize a wireless core */ +static int b43_wireless_core_init(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + struct b43_phy *phy = &dev->phy; + int err; + u64 hf; + + B43_WARN_ON(b43_status(dev) != B43_STAT_UNINIT); + + err = b43_bus_powerup(dev, 0); + if (err) + goto out; + if (!b43_device_is_enabled(dev)) + b43_wireless_core_reset(dev, phy->gmode); + + /* Reset all data structures. */ + setup_struct_wldev_for_init(dev); + phy->ops->prepare_structs(dev); + + /* Enable IRQ routing to this device. */ + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + bcma_host_pci_irq_ctl(dev->dev->bdev->bus, + dev->dev->bdev, true); + bcma_host_pci_up(dev->dev->bdev->bus); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + ssb_pcicore_dev_irqvecs_enable(&dev->dev->sdev->bus->pcicore, + dev->dev->sdev); + break; +#endif + } + + b43_imcfglo_timeouts_workaround(dev); + b43_bluetooth_coext_disable(dev); + if (phy->ops->prepare_hardware) { + err = phy->ops->prepare_hardware(dev); + if (err) + goto err_busdown; + } + err = b43_chip_init(dev); + if (err) + goto err_busdown; + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_WLCOREREV, dev->dev->core_rev); + hf = b43_hf_read(dev); + if (phy->type == B43_PHYTYPE_G) { + hf |= B43_HF_SYMW; + if (phy->rev == 1) + hf |= B43_HF_GDCW; + if (sprom->boardflags_lo & B43_BFL_PACTRL) + hf |= B43_HF_OFDMPABOOST; + } + if (phy->radio_ver == 0x2050) { + if (phy->radio_rev == 6) + hf |= B43_HF_4318TSSI; + if (phy->radio_rev < 6) + hf |= B43_HF_VCORECALC; + } + if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW) + hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */ +#if defined(CONFIG_B43_SSB) && defined(CONFIG_SSB_DRIVER_PCICORE) + if (dev->dev->bus_type == B43_BUS_SSB && + dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI && + dev->dev->sdev->bus->pcicore.dev->id.revision <= 10) + hf |= B43_HF_PCISCW; /* PCI slow clock workaround. */ +#endif + hf &= ~B43_HF_SKCFPUP; + b43_hf_write(dev, hf); + + /* tell the ucode MAC capabilities */ + if (dev->dev->core_rev >= 13) { + u32 mac_hw_cap = b43_read32(dev, B43_MMIO_MAC_HW_CAP); + + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_MACHW_L, + mac_hw_cap & 0xffff); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_MACHW_H, + (mac_hw_cap >> 16) & 0xffff); + } + + b43_set_retry_limits(dev, B43_DEFAULT_SHORT_RETRY_LIMIT, + B43_DEFAULT_LONG_RETRY_LIMIT); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_SFFBLIM, 3); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_LFFBLIM, 2); + + /* Disable sending probe responses from firmware. + * Setting the MaxTime to one usec will always trigger + * a timeout, so we never send any probe resp. + * A timeout of zero is infinite. */ + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRMAXTIME, 1); + + b43_rate_memory_init(dev); + b43_set_phytxctl_defaults(dev); + + /* Minimum Contention Window */ + if (phy->type == B43_PHYTYPE_B) + b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0x1F); + else + b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0xF); + /* Maximum Contention Window */ + b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF); + + /* write phytype and phyvers */ + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PHYTYPE, phy->type); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PHYVER, phy->rev); + + if (b43_bus_host_is_pcmcia(dev->dev) || + b43_bus_host_is_sdio(dev->dev)) { + dev->__using_pio_transfers = true; + err = b43_pio_init(dev); + } else if (dev->use_pio) { + b43warn(dev->wl, "Forced PIO by use_pio module parameter. " + "This should not be needed and will result in lower " + "performance.\n"); + dev->__using_pio_transfers = true; + err = b43_pio_init(dev); + } else { + dev->__using_pio_transfers = false; + err = b43_dma_init(dev); + } + if (err) + goto err_chip_exit; + b43_qos_init(dev); + b43_set_synth_pu_delay(dev, 1); + b43_bluetooth_coext_enable(dev); + + b43_bus_powerup(dev, !(sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)); + b43_upload_card_macaddress(dev); + b43_security_init(dev); + + ieee80211_wake_queues(dev->wl->hw); + + b43_set_status(dev, B43_STAT_INITIALIZED); + +out: + return err; + +err_chip_exit: + b43_chip_exit(dev); +err_busdown: + b43_bus_may_powerdown(dev); + B43_WARN_ON(b43_status(dev) != B43_STAT_UNINIT); + return err; +} + +static int b43_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev; + int err = -EOPNOTSUPP; + + /* TODO: allow WDS/AP devices to coexist */ + + if (vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_MESH_POINT && + vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_WDS && + vif->type != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + + mutex_lock(&wl->mutex); + if (wl->operating) + goto out_mutex_unlock; + + b43dbg(wl, "Adding Interface type %d\n", vif->type); + + dev = wl->current_dev; + wl->operating = true; + wl->vif = vif; + wl->if_type = vif->type; + memcpy(wl->mac_addr, vif->addr, ETH_ALEN); + + b43_adjust_opmode(dev); + b43_set_pretbtt(dev); + b43_set_synth_pu_delay(dev, 0); + b43_upload_card_macaddress(dev); + + err = 0; + out_mutex_unlock: + mutex_unlock(&wl->mutex); + + if (err == 0) + b43_op_bss_info_changed(hw, vif, &vif->bss_conf, ~0); + + return err; +} + +static void b43_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev = wl->current_dev; + + b43dbg(wl, "Removing Interface type %d\n", vif->type); + + mutex_lock(&wl->mutex); + + B43_WARN_ON(!wl->operating); + B43_WARN_ON(wl->vif != vif); + wl->vif = NULL; + + wl->operating = false; + + b43_adjust_opmode(dev); + eth_zero_addr(wl->mac_addr); + b43_upload_card_macaddress(dev); + + mutex_unlock(&wl->mutex); +} + +static int b43_op_start(struct ieee80211_hw *hw) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev = wl->current_dev; + int did_init = 0; + int err = 0; + + /* Kill all old instance specific information to make sure + * the card won't use it in the short timeframe between start + * and mac80211 reconfiguring it. */ + eth_zero_addr(wl->bssid); + eth_zero_addr(wl->mac_addr); + wl->filter_flags = 0; + wl->radiotap_enabled = false; + b43_qos_clear(wl); + wl->beacon0_uploaded = false; + wl->beacon1_uploaded = false; + wl->beacon_templates_virgin = true; + wl->radio_enabled = true; + + mutex_lock(&wl->mutex); + + if (b43_status(dev) < B43_STAT_INITIALIZED) { + err = b43_wireless_core_init(dev); + if (err) + goto out_mutex_unlock; + did_init = 1; + } + + if (b43_status(dev) < B43_STAT_STARTED) { + err = b43_wireless_core_start(dev); + if (err) { + if (did_init) + b43_wireless_core_exit(dev); + goto out_mutex_unlock; + } + } + + /* XXX: only do if device doesn't support rfkill irq */ + wiphy_rfkill_start_polling(hw->wiphy); + + out_mutex_unlock: + mutex_unlock(&wl->mutex); + + /* + * Configuration may have been overwritten during initialization. + * Reload the configuration, but only if initialization was + * successful. Reloading the configuration after a failed init + * may hang the system. + */ + if (!err) + b43_op_config(hw, ~0); + + return err; +} + +static void b43_op_stop(struct ieee80211_hw *hw) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev = wl->current_dev; + + cancel_work_sync(&(wl->beacon_update_trigger)); + + if (!dev) + goto out; + + mutex_lock(&wl->mutex); + if (b43_status(dev) >= B43_STAT_STARTED) { + dev = b43_wireless_core_stop(dev); + if (!dev) + goto out_unlock; + } + b43_wireless_core_exit(dev); + wl->radio_enabled = false; + +out_unlock: + mutex_unlock(&wl->mutex); +out: + cancel_work_sync(&(wl->txpower_adjust_work)); +} + +static int b43_op_beacon_set_tim(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, bool set) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + + b43_update_templates(wl); + + return 0; +} + +static void b43_op_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + + B43_WARN_ON(!vif || wl->vif != vif); +} + +static void b43_op_sw_scan_start_notifier(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) { + /* Disable CFP update during scan on other channels. */ + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_SKCFPUP); + } + mutex_unlock(&wl->mutex); +} + +static void b43_op_sw_scan_complete_notifier(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) { + /* Re-enable CFP update. */ + b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_SKCFPUP); + } + mutex_unlock(&wl->mutex); +} + +static int b43_op_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev = wl->current_dev; + struct ieee80211_conf *conf = &hw->conf; + + if (idx != 0) + return -ENOENT; + + survey->channel = conf->chandef.chan; + survey->filled = SURVEY_INFO_NOISE_DBM; + survey->noise = dev->stats.link_noise; + + return 0; +} + +static const struct ieee80211_ops b43_hw_ops = { + .tx = b43_op_tx, + .conf_tx = b43_op_conf_tx, + .add_interface = b43_op_add_interface, + .remove_interface = b43_op_remove_interface, + .config = b43_op_config, + .bss_info_changed = b43_op_bss_info_changed, + .configure_filter = b43_op_configure_filter, + .set_key = b43_op_set_key, + .update_tkip_key = b43_op_update_tkip_key, + .get_stats = b43_op_get_stats, + .get_tsf = b43_op_get_tsf, + .set_tsf = b43_op_set_tsf, + .start = b43_op_start, + .stop = b43_op_stop, + .set_tim = b43_op_beacon_set_tim, + .sta_notify = b43_op_sta_notify, + .sw_scan_start = b43_op_sw_scan_start_notifier, + .sw_scan_complete = b43_op_sw_scan_complete_notifier, + .get_survey = b43_op_get_survey, + .rfkill_poll = b43_rfkill_poll, +}; + +/* Hard-reset the chip. Do not call this directly. + * Use b43_controller_restart() + */ +static void b43_chip_reset(struct work_struct *work) +{ + struct b43_wldev *dev = + container_of(work, struct b43_wldev, restart_work); + struct b43_wl *wl = dev->wl; + int err = 0; + int prev_status; + + mutex_lock(&wl->mutex); + + prev_status = b43_status(dev); + /* Bring the device down... */ + if (prev_status >= B43_STAT_STARTED) { + dev = b43_wireless_core_stop(dev); + if (!dev) { + err = -ENODEV; + goto out; + } + } + if (prev_status >= B43_STAT_INITIALIZED) + b43_wireless_core_exit(dev); + + /* ...and up again. */ + if (prev_status >= B43_STAT_INITIALIZED) { + err = b43_wireless_core_init(dev); + if (err) + goto out; + } + if (prev_status >= B43_STAT_STARTED) { + err = b43_wireless_core_start(dev); + if (err) { + b43_wireless_core_exit(dev); + goto out; + } + } +out: + if (err) + wl->current_dev = NULL; /* Failed to init the dev. */ + mutex_unlock(&wl->mutex); + + if (err) { + b43err(wl, "Controller restart FAILED\n"); + return; + } + + /* reload configuration */ + b43_op_config(wl->hw, ~0); + if (wl->vif) + b43_op_bss_info_changed(wl->hw, wl->vif, &wl->vif->bss_conf, ~0); + + b43info(wl, "Controller restarted\n"); +} + +static int b43_setup_bands(struct b43_wldev *dev, + bool have_2ghz_phy, bool have_5ghz_phy) +{ + struct ieee80211_hw *hw = dev->wl->hw; + struct b43_phy *phy = &dev->phy; + bool limited_2g; + bool limited_5g; + + /* We don't support all 2 GHz channels on some devices */ + limited_2g = phy->radio_ver == 0x2057 && + (phy->radio_rev == 9 || phy->radio_rev == 14); + limited_5g = phy->radio_ver == 0x2057 && + phy->radio_rev == 9; + + if (have_2ghz_phy) + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = limited_2g ? + &b43_band_2ghz_limited : &b43_band_2GHz; + if (dev->phy.type == B43_PHYTYPE_N) { + if (have_5ghz_phy) + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = limited_5g ? + &b43_band_5GHz_nphy_limited : + &b43_band_5GHz_nphy; + } else { + if (have_5ghz_phy) + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_aphy; + } + + dev->phy.supports_2ghz = have_2ghz_phy; + dev->phy.supports_5ghz = have_5ghz_phy; + + return 0; +} + +static void b43_wireless_core_detach(struct b43_wldev *dev) +{ + /* We release firmware that late to not be required to re-request + * is all the time when we reinit the core. */ + b43_release_firmware(dev); + b43_phy_free(dev); +} + +static void b43_supported_bands(struct b43_wldev *dev, bool *have_2ghz_phy, + bool *have_5ghz_phy) +{ + u16 dev_id = 0; + +#ifdef CONFIG_B43_BCMA + if (dev->dev->bus_type == B43_BUS_BCMA && + dev->dev->bdev->bus->hosttype == BCMA_HOSTTYPE_PCI) + dev_id = dev->dev->bdev->bus->host_pci->device; +#endif +#ifdef CONFIG_B43_SSB + if (dev->dev->bus_type == B43_BUS_SSB && + dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI) + dev_id = dev->dev->sdev->bus->host_pci->device; +#endif + /* Override with SPROM value if available */ + if (dev->dev->bus_sprom->dev_id) + dev_id = dev->dev->bus_sprom->dev_id; + + /* Note: below IDs can be "virtual" (not maching e.g. real PCI ID) */ + switch (dev_id) { + case 0x4324: /* BCM4306 */ + case 0x4312: /* BCM4311 */ + case 0x4319: /* BCM4318 */ + case 0x4328: /* BCM4321 */ + case 0x432b: /* BCM4322 */ + case 0x4350: /* BCM43222 */ + case 0x4353: /* BCM43224 */ + case 0x0576: /* BCM43224 */ + case 0x435f: /* BCM6362 */ + case 0x4331: /* BCM4331 */ + case 0x4359: /* BCM43228 */ + case 0x43a0: /* BCM4360 */ + case 0x43b1: /* BCM4352 */ + /* Dual band devices */ + *have_2ghz_phy = true; + *have_5ghz_phy = true; + return; + case 0x4321: /* BCM4306 */ + /* There are 14e4:4321 PCI devs with 2.4 GHz BCM4321 (N-PHY) */ + if (dev->phy.type != B43_PHYTYPE_G) + break; + /* fall through */ + case 0x4313: /* BCM4311 */ + case 0x431a: /* BCM4318 */ + case 0x432a: /* BCM4321 */ + case 0x432d: /* BCM4322 */ + case 0x4352: /* BCM43222 */ + case 0x435a: /* BCM43228 */ + case 0x4333: /* BCM4331 */ + case 0x43a2: /* BCM4360 */ + case 0x43b3: /* BCM4352 */ + /* 5 GHz only devices */ + *have_2ghz_phy = false; + *have_5ghz_phy = true; + return; + } + + /* As a fallback, try to guess using PHY type */ + switch (dev->phy.type) { + case B43_PHYTYPE_A: + *have_2ghz_phy = false; + *have_5ghz_phy = true; + return; + case B43_PHYTYPE_G: + case B43_PHYTYPE_N: + case B43_PHYTYPE_LP: + case B43_PHYTYPE_HT: + case B43_PHYTYPE_LCN: + *have_2ghz_phy = true; + *have_5ghz_phy = false; + return; + } + + B43_WARN_ON(1); +} + +static int b43_wireless_core_attach(struct b43_wldev *dev) +{ + struct b43_wl *wl = dev->wl; + struct b43_phy *phy = &dev->phy; + int err; + u32 tmp; + bool have_2ghz_phy = false, have_5ghz_phy = false; + + /* Do NOT do any device initialization here. + * Do it in wireless_core_init() instead. + * This function is for gathering basic information about the HW, only. + * Also some structs may be set up here. But most likely you want to have + * that in core_init(), too. + */ + + err = b43_bus_powerup(dev, 0); + if (err) { + b43err(wl, "Bus powerup failed\n"); + goto out; + } + + phy->do_full_init = true; + + /* Try to guess supported bands for the first init needs */ + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOST); + have_2ghz_phy = !!(tmp & B43_BCMA_IOST_2G_PHY); + have_5ghz_phy = !!(tmp & B43_BCMA_IOST_5G_PHY); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + if (dev->dev->core_rev >= 5) { + tmp = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); + have_2ghz_phy = !!(tmp & B43_TMSHIGH_HAVE_2GHZ_PHY); + have_5ghz_phy = !!(tmp & B43_TMSHIGH_HAVE_5GHZ_PHY); + } else + B43_WARN_ON(1); + break; +#endif + } + + dev->phy.gmode = have_2ghz_phy; + b43_wireless_core_reset(dev, dev->phy.gmode); + + /* Get the PHY type. */ + err = b43_phy_versioning(dev); + if (err) + goto err_powerdown; + + /* Get real info about supported bands */ + b43_supported_bands(dev, &have_2ghz_phy, &have_5ghz_phy); + + /* We don't support 5 GHz on some PHYs yet */ + if (have_5ghz_phy) { + switch (dev->phy.type) { + case B43_PHYTYPE_A: + case B43_PHYTYPE_G: + case B43_PHYTYPE_LP: + case B43_PHYTYPE_HT: + b43warn(wl, "5 GHz band is unsupported on this PHY\n"); + have_5ghz_phy = false; + } + } + + if (!have_2ghz_phy && !have_5ghz_phy) { + b43err(wl, "b43 can't support any band on this device\n"); + err = -EOPNOTSUPP; + goto err_powerdown; + } + + err = b43_phy_allocate(dev); + if (err) + goto err_powerdown; + + dev->phy.gmode = have_2ghz_phy; + b43_wireless_core_reset(dev, dev->phy.gmode); + + err = b43_validate_chipaccess(dev); + if (err) + goto err_phy_free; + err = b43_setup_bands(dev, have_2ghz_phy, have_5ghz_phy); + if (err) + goto err_phy_free; + + /* Now set some default "current_dev" */ + if (!wl->current_dev) + wl->current_dev = dev; + INIT_WORK(&dev->restart_work, b43_chip_reset); + + dev->phy.ops->switch_analog(dev, 0); + b43_device_disable(dev, 0); + b43_bus_may_powerdown(dev); + +out: + return err; + +err_phy_free: + b43_phy_free(dev); +err_powerdown: + b43_bus_may_powerdown(dev); + return err; +} + +static void b43_one_core_detach(struct b43_bus_dev *dev) +{ + struct b43_wldev *wldev; + struct b43_wl *wl; + + /* Do not cancel ieee80211-workqueue based work here. + * See comment in b43_remove(). */ + + wldev = b43_bus_get_wldev(dev); + wl = wldev->wl; + b43_debugfs_remove_device(wldev); + b43_wireless_core_detach(wldev); + list_del(&wldev->list); + b43_bus_set_wldev(dev, NULL); + kfree(wldev); +} + +static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl) +{ + struct b43_wldev *wldev; + int err = -ENOMEM; + + wldev = kzalloc(sizeof(*wldev), GFP_KERNEL); + if (!wldev) + goto out; + + wldev->use_pio = b43_modparam_pio; + wldev->dev = dev; + wldev->wl = wl; + b43_set_status(wldev, B43_STAT_UNINIT); + wldev->bad_frames_preempt = modparam_bad_frames_preempt; + INIT_LIST_HEAD(&wldev->list); + + err = b43_wireless_core_attach(wldev); + if (err) + goto err_kfree_wldev; + + b43_bus_set_wldev(dev, wldev); + b43_debugfs_add_device(wldev); + + out: + return err; + + err_kfree_wldev: + kfree(wldev); + return err; +} + +#define IS_PDEV(pdev, _vendor, _device, _subvendor, _subdevice) ( \ + (pdev->vendor == PCI_VENDOR_ID_##_vendor) && \ + (pdev->device == _device) && \ + (pdev->subsystem_vendor == PCI_VENDOR_ID_##_subvendor) && \ + (pdev->subsystem_device == _subdevice) ) + +#ifdef CONFIG_B43_SSB +static void b43_sprom_fixup(struct ssb_bus *bus) +{ + struct pci_dev *pdev; + + /* boardflags workarounds */ + if (bus->boardinfo.vendor == SSB_BOARDVENDOR_DELL && + bus->chip_id == 0x4301 && bus->sprom.board_rev == 0x74) + bus->sprom.boardflags_lo |= B43_BFL_BTCOEXIST; + if (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE && + bus->boardinfo.type == 0x4E && bus->sprom.board_rev > 0x40) + bus->sprom.boardflags_lo |= B43_BFL_PACTRL; + if (bus->bustype == SSB_BUSTYPE_PCI) { + pdev = bus->host_pci; + if (IS_PDEV(pdev, BROADCOM, 0x4318, ASUSTEK, 0x100F) || + IS_PDEV(pdev, BROADCOM, 0x4320, DELL, 0x0003) || + IS_PDEV(pdev, BROADCOM, 0x4320, HP, 0x12f8) || + IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0015) || + IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0014) || + IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0013) || + IS_PDEV(pdev, BROADCOM, 0x4320, MOTOROLA, 0x7010)) + bus->sprom.boardflags_lo &= ~B43_BFL_BTCOEXIST; + } +} + +static void b43_wireless_exit(struct b43_bus_dev *dev, struct b43_wl *wl) +{ + struct ieee80211_hw *hw = wl->hw; + + ssb_set_devtypedata(dev->sdev, NULL); + ieee80211_free_hw(hw); +} +#endif + +static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) +{ + struct ssb_sprom *sprom = dev->bus_sprom; + struct ieee80211_hw *hw; + struct b43_wl *wl; + char chip_name[6]; + int queue_num; + + hw = ieee80211_alloc_hw(sizeof(*wl), &b43_hw_ops); + if (!hw) { + b43err(NULL, "Could not allocate ieee80211 device\n"); + return ERR_PTR(-ENOMEM); + } + wl = hw_to_b43_wl(hw); + + /* fill hw info */ + ieee80211_hw_set(hw, RX_INCLUDES_FCS); + ieee80211_hw_set(hw, SIGNAL_DBM); + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_ADHOC); + + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + + wl->hw_registred = false; + hw->max_rates = 2; + SET_IEEE80211_DEV(hw, dev->dev); + if (is_valid_ether_addr(sprom->et1mac)) + SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac); + else + SET_IEEE80211_PERM_ADDR(hw, sprom->il0mac); + + /* Initialize struct b43_wl */ + wl->hw = hw; + mutex_init(&wl->mutex); + spin_lock_init(&wl->hardirq_lock); + spin_lock_init(&wl->beacon_lock); + INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work); + INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work); + INIT_WORK(&wl->tx_work, b43_tx_work); + + /* Initialize queues and flags. */ + for (queue_num = 0; queue_num < B43_QOS_QUEUE_NUM; queue_num++) { + skb_queue_head_init(&wl->tx_queue[queue_num]); + wl->tx_queue_stopped[queue_num] = 0; + } + + snprintf(chip_name, ARRAY_SIZE(chip_name), + (dev->chip_id > 0x9999) ? "%d" : "%04X", dev->chip_id); + b43info(wl, "Broadcom %s WLAN found (core revision %u)\n", chip_name, + dev->core_rev); + return wl; +} + +#ifdef CONFIG_B43_BCMA +static int b43_bcma_probe(struct bcma_device *core) +{ + struct b43_bus_dev *dev; + struct b43_wl *wl; + int err; + + if (!modparam_allhwsupport && + (core->id.rev == 0x17 || core->id.rev == 0x18)) { + pr_err("Support for cores revisions 0x17 and 0x18 disabled by module param allhwsupport=0. Try b43.allhwsupport=1\n"); + return -ENOTSUPP; + } + + dev = b43_bus_dev_bcma_init(core); + if (!dev) + return -ENODEV; + + wl = b43_wireless_init(dev); + if (IS_ERR(wl)) { + err = PTR_ERR(wl); + goto bcma_out; + } + + err = b43_one_core_attach(dev, wl); + if (err) + goto bcma_err_wireless_exit; + + /* setup and start work to load firmware */ + INIT_WORK(&wl->firmware_load, b43_request_firmware); + schedule_work(&wl->firmware_load); + +bcma_out: + return err; + +bcma_err_wireless_exit: + ieee80211_free_hw(wl->hw); + return err; +} + +static void b43_bcma_remove(struct bcma_device *core) +{ + struct b43_wldev *wldev = bcma_get_drvdata(core); + struct b43_wl *wl = wldev->wl; + + /* We must cancel any work here before unregistering from ieee80211, + * as the ieee80211 unreg will destroy the workqueue. */ + cancel_work_sync(&wldev->restart_work); + cancel_work_sync(&wl->firmware_load); + + B43_WARN_ON(!wl); + if (!wldev->fw.ucode.data) + return; /* NULL if firmware never loaded */ + if (wl->current_dev == wldev && wl->hw_registred) { + b43_leds_stop(wldev); + ieee80211_unregister_hw(wl->hw); + } + + b43_one_core_detach(wldev->dev); + + /* Unregister HW RNG driver */ + b43_rng_exit(wl); + + b43_leds_unregister(wl); + + ieee80211_free_hw(wl->hw); +} + +static struct bcma_driver b43_bcma_driver = { + .name = KBUILD_MODNAME, + .id_table = b43_bcma_tbl, + .probe = b43_bcma_probe, + .remove = b43_bcma_remove, +}; +#endif + +#ifdef CONFIG_B43_SSB +static +int b43_ssb_probe(struct ssb_device *sdev, const struct ssb_device_id *id) +{ + struct b43_bus_dev *dev; + struct b43_wl *wl; + int err; + + dev = b43_bus_dev_ssb_init(sdev); + if (!dev) + return -ENOMEM; + + wl = ssb_get_devtypedata(sdev); + if (wl) { + b43err(NULL, "Dual-core devices are not supported\n"); + err = -ENOTSUPP; + goto err_ssb_kfree_dev; + } + + b43_sprom_fixup(sdev->bus); + + wl = b43_wireless_init(dev); + if (IS_ERR(wl)) { + err = PTR_ERR(wl); + goto err_ssb_kfree_dev; + } + ssb_set_devtypedata(sdev, wl); + B43_WARN_ON(ssb_get_devtypedata(sdev) != wl); + + err = b43_one_core_attach(dev, wl); + if (err) + goto err_ssb_wireless_exit; + + /* setup and start work to load firmware */ + INIT_WORK(&wl->firmware_load, b43_request_firmware); + schedule_work(&wl->firmware_load); + + return err; + +err_ssb_wireless_exit: + b43_wireless_exit(dev, wl); +err_ssb_kfree_dev: + kfree(dev); + return err; +} + +static void b43_ssb_remove(struct ssb_device *sdev) +{ + struct b43_wl *wl = ssb_get_devtypedata(sdev); + struct b43_wldev *wldev = ssb_get_drvdata(sdev); + struct b43_bus_dev *dev = wldev->dev; + + /* We must cancel any work here before unregistering from ieee80211, + * as the ieee80211 unreg will destroy the workqueue. */ + cancel_work_sync(&wldev->restart_work); + cancel_work_sync(&wl->firmware_load); + + B43_WARN_ON(!wl); + if (!wldev->fw.ucode.data) + return; /* NULL if firmware never loaded */ + if (wl->current_dev == wldev && wl->hw_registred) { + b43_leds_stop(wldev); + ieee80211_unregister_hw(wl->hw); + } + + b43_one_core_detach(dev); + + /* Unregister HW RNG driver */ + b43_rng_exit(wl); + + b43_leds_unregister(wl); + b43_wireless_exit(dev, wl); +} + +static struct ssb_driver b43_ssb_driver = { + .name = KBUILD_MODNAME, + .id_table = b43_ssb_tbl, + .probe = b43_ssb_probe, + .remove = b43_ssb_remove, +}; +#endif /* CONFIG_B43_SSB */ + +/* Perform a hardware reset. This can be called from any context. */ +void b43_controller_restart(struct b43_wldev *dev, const char *reason) +{ + /* Must avoid requeueing, if we are in shutdown. */ + if (b43_status(dev) < B43_STAT_INITIALIZED) + return; + b43info(dev->wl, "Controller RESET (%s) ...\n", reason); + ieee80211_queue_work(dev->wl->hw, &dev->restart_work); +} + +static void b43_print_driverinfo(void) +{ + const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "", + *feat_leds = "", *feat_sdio = ""; + +#ifdef CONFIG_B43_PCI_AUTOSELECT + feat_pci = "P"; +#endif +#ifdef CONFIG_B43_PCMCIA + feat_pcmcia = "M"; +#endif +#ifdef CONFIG_B43_PHY_N + feat_nphy = "N"; +#endif +#ifdef CONFIG_B43_LEDS + feat_leds = "L"; +#endif +#ifdef CONFIG_B43_SDIO + feat_sdio = "S"; +#endif + printk(KERN_INFO "Broadcom 43xx driver loaded " + "[ Features: %s%s%s%s%s ]\n", + feat_pci, feat_pcmcia, feat_nphy, + feat_leds, feat_sdio); +} + +static int __init b43_init(void) +{ + int err; + + b43_debugfs_init(); + err = b43_sdio_init(); + if (err) + goto err_dfs_exit; +#ifdef CONFIG_B43_BCMA + err = bcma_driver_register(&b43_bcma_driver); + if (err) + goto err_sdio_exit; +#endif +#ifdef CONFIG_B43_SSB + err = ssb_driver_register(&b43_ssb_driver); + if (err) + goto err_bcma_driver_exit; +#endif + b43_print_driverinfo(); + + return err; + +#ifdef CONFIG_B43_SSB +err_bcma_driver_exit: +#endif +#ifdef CONFIG_B43_BCMA + bcma_driver_unregister(&b43_bcma_driver); +err_sdio_exit: +#endif + b43_sdio_exit(); +err_dfs_exit: + b43_debugfs_exit(); + return err; +} + +static void __exit b43_exit(void) +{ +#ifdef CONFIG_B43_SSB + ssb_driver_unregister(&b43_ssb_driver); +#endif +#ifdef CONFIG_B43_BCMA + bcma_driver_unregister(&b43_bcma_driver); +#endif + b43_sdio_exit(); + b43_debugfs_exit(); +} + +module_init(b43_init) +module_exit(b43_exit) diff --git a/drivers/net/wireless/broadcom/b43/main.h b/drivers/net/wireless/broadcom/b43/main.h new file mode 100644 index 000000000000..c46430cc725c --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/main.h @@ -0,0 +1,112 @@ +/* + + Broadcom B43 wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef B43_MAIN_H_ +#define B43_MAIN_H_ + +#include "b43.h" + +#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] +#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) +/* Magic helper macro to pad structures. Ignore those above. It's magic. */ +#define PAD_BYTES(nr_bytes) P4D_BYTES( __LINE__ , (nr_bytes)) + + +extern int b43_modparam_verbose; + +/* Logmessage verbosity levels. Update the b43_modparam_verbose helptext, if + * you add or remove levels. */ +enum b43_verbosity { + B43_VERBOSITY_ERROR, + B43_VERBOSITY_WARN, + B43_VERBOSITY_INFO, + B43_VERBOSITY_DEBUG, + __B43_VERBOSITY_AFTERLAST, /* keep last */ + + B43_VERBOSITY_MAX = __B43_VERBOSITY_AFTERLAST - 1, +#if B43_DEBUG + B43_VERBOSITY_DEFAULT = B43_VERBOSITY_DEBUG, +#else + B43_VERBOSITY_DEFAULT = B43_VERBOSITY_INFO, +#endif +}; + +static inline int b43_is_cck_rate(int rate) +{ + return (rate == B43_CCK_RATE_1MB || + rate == B43_CCK_RATE_2MB || + rate == B43_CCK_RATE_5MB || rate == B43_CCK_RATE_11MB); +} + +static inline int b43_is_ofdm_rate(int rate) +{ + return !b43_is_cck_rate(rate); +} + +u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev, + u8 antenna_nr); + +void b43_tsf_read(struct b43_wldev *dev, u64 * tsf); +void b43_tsf_write(struct b43_wldev *dev, u64 tsf); + +u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset); +u16 b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset); +void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value); +void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value); + +u64 b43_hf_read(struct b43_wldev *dev); +void b43_hf_write(struct b43_wldev *dev, u64 value); + +void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on); + +void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode); + +void b43_controller_restart(struct b43_wldev *dev, const char *reason); + +#define B43_PS_ENABLED (1 << 0) /* Force enable hardware power saving */ +#define B43_PS_DISABLED (1 << 1) /* Force disable hardware power saving */ +#define B43_PS_AWAKE (1 << 2) /* Force device awake */ +#define B43_PS_ASLEEP (1 << 3) /* Force device asleep */ +void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags); + +void b43_wireless_core_phy_pll_reset(struct b43_wldev *dev); + +void b43_mac_suspend(struct b43_wldev *dev); +void b43_mac_enable(struct b43_wldev *dev); +void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on); +void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode); + + +struct b43_request_fw_context; +int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, + struct b43_firmware_file *fw, bool async); +void b43_do_release_fw(struct b43_firmware_file *fw); + +#endif /* B43_MAIN_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/phy_a.c b/drivers/net/wireless/broadcom/b43/phy_a.c new file mode 100644 index 000000000000..99c036f5ecb7 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_a.c @@ -0,0 +1,595 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11a PHY driver + + Copyright (c) 2005 Martin Langer , + Copyright (c) 2005-2007 Stefano Brivio + Copyright (c) 2005-2008 Michael Buesch + Copyright (c) 2005, 2006 Danny van Dyk + Copyright (c) 2005, 2006 Andreas Jaggi + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include + +#include "b43.h" +#include "phy_a.h" +#include "phy_common.h" +#include "wa.h" +#include "tables.h" +#include "main.h" + + +/* Get the freq, as it has to be written to the device. */ +static inline u16 channel2freq_a(u8 channel) +{ + B43_WARN_ON(channel > 200); + + return (5000 + 5 * channel); +} + +static inline u16 freq_r3A_value(u16 frequency) +{ + u16 value; + + if (frequency < 5091) + value = 0x0040; + else if (frequency < 5321) + value = 0x0000; + else if (frequency < 5806) + value = 0x0080; + else + value = 0x0040; + + return value; +} + +#if 0 +/* This function converts a TSSI value to dBm in Q5.2 */ +static s8 b43_aphy_estimate_power_out(struct b43_wldev *dev, s8 tssi) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_a *aphy = phy->a; + s8 dbm = 0; + s32 tmp; + + tmp = (aphy->tgt_idle_tssi - aphy->cur_idle_tssi + tssi); + tmp += 0x80; + tmp = clamp_val(tmp, 0x00, 0xFF); + dbm = aphy->tssi2dbm[tmp]; + //TODO: There's a FIXME on the specs + + return dbm; +} +#endif + +static void b43_radio_set_tx_iq(struct b43_wldev *dev) +{ + static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 }; + static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A }; + u16 tmp = b43_radio_read16(dev, 0x001E); + int i, j; + + for (i = 0; i < 5; i++) { + for (j = 0; j < 5; j++) { + if (tmp == (data_high[i] << 4 | data_low[j])) { + b43_phy_write(dev, 0x0069, + (i - j) << 8 | 0x00C0); + return; + } + } + } +} + +static void aphy_channel_switch(struct b43_wldev *dev, unsigned int channel) +{ + u16 freq, r8, tmp; + + freq = channel2freq_a(channel); + + r8 = b43_radio_read16(dev, 0x0008); + b43_write16(dev, 0x03F0, freq); + b43_radio_write16(dev, 0x0008, r8); + + //TODO: write max channel TX power? to Radio 0x2D + tmp = b43_radio_read16(dev, 0x002E); + tmp &= 0x0080; + //TODO: OR tmp with the Power out estimation for this channel? + b43_radio_write16(dev, 0x002E, tmp); + + if (freq >= 4920 && freq <= 5500) { + /* + * r8 = (((freq * 15 * 0xE1FC780F) >> 32) / 29) & 0x0F; + * = (freq * 0.025862069 + */ + r8 = 3 * freq / 116; /* is equal to r8 = freq * 0.025862 */ + } + b43_radio_write16(dev, 0x0007, (r8 << 4) | r8); + b43_radio_write16(dev, 0x0020, (r8 << 4) | r8); + b43_radio_write16(dev, 0x0021, (r8 << 4) | r8); + b43_radio_maskset(dev, 0x0022, 0x000F, (r8 << 4)); + b43_radio_write16(dev, 0x002A, (r8 << 4)); + b43_radio_write16(dev, 0x002B, (r8 << 4)); + b43_radio_maskset(dev, 0x0008, 0x00F0, (r8 << 4)); + b43_radio_maskset(dev, 0x0029, 0xFF0F, 0x00B0); + b43_radio_write16(dev, 0x0035, 0x00AA); + b43_radio_write16(dev, 0x0036, 0x0085); + b43_radio_maskset(dev, 0x003A, 0xFF20, freq_r3A_value(freq)); + b43_radio_mask(dev, 0x003D, 0x00FF); + b43_radio_maskset(dev, 0x0081, 0xFF7F, 0x0080); + b43_radio_mask(dev, 0x0035, 0xFFEF); + b43_radio_maskset(dev, 0x0035, 0xFFEF, 0x0010); + b43_radio_set_tx_iq(dev); + //TODO: TSSI2dbm workaround +//FIXME b43_phy_xmitpower(dev); +} + +static void b43_radio_init2060(struct b43_wldev *dev) +{ + b43_radio_write16(dev, 0x0004, 0x00C0); + b43_radio_write16(dev, 0x0005, 0x0008); + b43_radio_write16(dev, 0x0009, 0x0040); + b43_radio_write16(dev, 0x0005, 0x00AA); + b43_radio_write16(dev, 0x0032, 0x008F); + b43_radio_write16(dev, 0x0006, 0x008F); + b43_radio_write16(dev, 0x0034, 0x008F); + b43_radio_write16(dev, 0x002C, 0x0007); + b43_radio_write16(dev, 0x0082, 0x0080); + b43_radio_write16(dev, 0x0080, 0x0000); + b43_radio_write16(dev, 0x003F, 0x00DA); + b43_radio_mask(dev, 0x0005, ~0x0008); + b43_radio_mask(dev, 0x0081, ~0x0010); + b43_radio_mask(dev, 0x0081, ~0x0020); + b43_radio_mask(dev, 0x0081, ~0x0020); + msleep(1); /* delay 400usec */ + + b43_radio_maskset(dev, 0x0081, ~0x0020, 0x0010); + msleep(1); /* delay 400usec */ + + b43_radio_maskset(dev, 0x0005, ~0x0008, 0x0008); + b43_radio_mask(dev, 0x0085, ~0x0010); + b43_radio_mask(dev, 0x0005, ~0x0008); + b43_radio_mask(dev, 0x0081, ~0x0040); + b43_radio_maskset(dev, 0x0081, ~0x0040, 0x0040); + b43_radio_write16(dev, 0x0005, + (b43_radio_read16(dev, 0x0081) & ~0x0008) | 0x0008); + b43_phy_write(dev, 0x0063, 0xDDC6); + b43_phy_write(dev, 0x0069, 0x07BE); + b43_phy_write(dev, 0x006A, 0x0000); + + aphy_channel_switch(dev, dev->phy.ops->get_default_chan(dev)); + + msleep(1); +} + +static void b43_phy_rssiagc(struct b43_wldev *dev, u8 enable) +{ + int i; + + if (dev->phy.rev < 3) { + if (enable) + for (i = 0; i < B43_TAB_RSSIAGC1_SIZE; i++) { + b43_ofdmtab_write16(dev, + B43_OFDMTAB_LNAHPFGAIN1, i, 0xFFF8); + b43_ofdmtab_write16(dev, + B43_OFDMTAB_WRSSI, i, 0xFFF8); + } + else + for (i = 0; i < B43_TAB_RSSIAGC1_SIZE; i++) { + b43_ofdmtab_write16(dev, + B43_OFDMTAB_LNAHPFGAIN1, i, b43_tab_rssiagc1[i]); + b43_ofdmtab_write16(dev, + B43_OFDMTAB_WRSSI, i, b43_tab_rssiagc1[i]); + } + } else { + if (enable) + for (i = 0; i < B43_TAB_RSSIAGC1_SIZE; i++) + b43_ofdmtab_write16(dev, + B43_OFDMTAB_WRSSI, i, 0x0820); + else + for (i = 0; i < B43_TAB_RSSIAGC2_SIZE; i++) + b43_ofdmtab_write16(dev, + B43_OFDMTAB_WRSSI, i, b43_tab_rssiagc2[i]); + } +} + +static void b43_phy_ww(struct b43_wldev *dev) +{ + u16 b, curr_s, best_s = 0xFFFF; + int i; + + b43_phy_mask(dev, B43_PHY_CRS0, ~B43_PHY_CRS0_EN); + b43_phy_set(dev, B43_PHY_OFDM(0x1B), 0x1000); + b43_phy_maskset(dev, B43_PHY_OFDM(0x82), 0xF0FF, 0x0300); + b43_radio_set(dev, 0x0009, 0x0080); + b43_radio_maskset(dev, 0x0012, 0xFFFC, 0x0002); + b43_wa_initgains(dev); + b43_phy_write(dev, B43_PHY_OFDM(0xBA), 0x3ED5); + b = b43_phy_read(dev, B43_PHY_PWRDOWN); + b43_phy_write(dev, B43_PHY_PWRDOWN, (b & 0xFFF8) | 0x0005); + b43_radio_set(dev, 0x0004, 0x0004); + for (i = 0x10; i <= 0x20; i++) { + b43_radio_write16(dev, 0x0013, i); + curr_s = b43_phy_read(dev, B43_PHY_OTABLEQ) & 0x00FF; + if (!curr_s) { + best_s = 0x0000; + break; + } else if (curr_s >= 0x0080) + curr_s = 0x0100 - curr_s; + if (curr_s < best_s) + best_s = curr_s; + } + b43_phy_write(dev, B43_PHY_PWRDOWN, b); + b43_radio_mask(dev, 0x0004, 0xFFFB); + b43_radio_write16(dev, 0x0013, best_s); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 0, 0xFFEC); + b43_phy_write(dev, B43_PHY_OFDM(0xB7), 0x1E80); + b43_phy_write(dev, B43_PHY_OFDM(0xB6), 0x1C00); + b43_phy_write(dev, B43_PHY_OFDM(0xB5), 0x0EC0); + b43_phy_write(dev, B43_PHY_OFDM(0xB2), 0x00C0); + b43_phy_write(dev, B43_PHY_OFDM(0xB9), 0x1FFF); + b43_phy_maskset(dev, B43_PHY_OFDM(0xBB), 0xF000, 0x0053); + b43_phy_maskset(dev, B43_PHY_OFDM61, 0xFE1F, 0x0120); + b43_phy_maskset(dev, B43_PHY_OFDM(0x13), 0x0FFF, 0x3000); + b43_phy_maskset(dev, B43_PHY_OFDM(0x14), 0x0FFF, 0x3000); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 6, 0x0017); + for (i = 0; i < 6; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, i, 0x000F); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0x0D, 0x000E); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0x0E, 0x0011); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0x0F, 0x0013); + b43_phy_write(dev, B43_PHY_OFDM(0x33), 0x5030); + b43_phy_set(dev, B43_PHY_CRS0, B43_PHY_CRS0_EN); +} + +static void hardware_pctl_init_aphy(struct b43_wldev *dev) +{ + //TODO +} + +void b43_phy_inita(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + /* This lowlevel A-PHY init is also called from G-PHY init. + * So we must not access phy->a, if called from G-PHY code. + */ + B43_WARN_ON((phy->type != B43_PHYTYPE_A) && + (phy->type != B43_PHYTYPE_G)); + + might_sleep(); + + if (phy->rev >= 6) { + if (phy->type == B43_PHYTYPE_A) + b43_phy_mask(dev, B43_PHY_OFDM(0x1B), ~0x1000); + if (b43_phy_read(dev, B43_PHY_ENCORE) & B43_PHY_ENCORE_EN) + b43_phy_set(dev, B43_PHY_ENCORE, 0x0010); + else + b43_phy_mask(dev, B43_PHY_ENCORE, ~0x1010); + } + + b43_wa_all(dev); + + if (phy->type == B43_PHYTYPE_A) { + if (phy->gmode && (phy->rev < 3)) + b43_phy_set(dev, 0x0034, 0x0001); + b43_phy_rssiagc(dev, 0); + + b43_phy_set(dev, B43_PHY_CRS0, B43_PHY_CRS0_EN); + + b43_radio_init2060(dev); + + if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) && + ((dev->dev->board_type == SSB_BOARD_BU4306) || + (dev->dev->board_type == SSB_BOARD_BU4309))) { + ; //TODO: A PHY LO + } + + if (phy->rev >= 3) + b43_phy_ww(dev); + + hardware_pctl_init_aphy(dev); + + //TODO: radar detection + } + + if ((phy->type == B43_PHYTYPE_G) && + (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL)) { + b43_phy_maskset(dev, B43_PHY_OFDM(0x6E), 0xE000, 0x3CF); + } +} + +/* Initialise the TSSI->dBm lookup table */ +static int b43_aphy_init_tssi2dbm_table(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_a *aphy = phy->a; + s16 pab0, pab1, pab2; + + pab0 = (s16) (dev->dev->bus_sprom->pa1b0); + pab1 = (s16) (dev->dev->bus_sprom->pa1b1); + pab2 = (s16) (dev->dev->bus_sprom->pa1b2); + + if (pab0 != 0 && pab1 != 0 && pab2 != 0 && + pab0 != -1 && pab1 != -1 && pab2 != -1) { + /* The pabX values are set in SPROM. Use them. */ + if ((s8) dev->dev->bus_sprom->itssi_a != 0 && + (s8) dev->dev->bus_sprom->itssi_a != -1) + aphy->tgt_idle_tssi = + (s8) (dev->dev->bus_sprom->itssi_a); + else + aphy->tgt_idle_tssi = 62; + aphy->tssi2dbm = b43_generate_dyn_tssi2dbm_tab(dev, pab0, + pab1, pab2); + if (!aphy->tssi2dbm) + return -ENOMEM; + } else { + /* pabX values not set in SPROM, + * but APHY needs a generated table. */ + aphy->tssi2dbm = NULL; + b43err(dev->wl, "Could not generate tssi2dBm " + "table (wrong SPROM info)!\n"); + return -ENODEV; + } + + return 0; +} + +static int b43_aphy_op_allocate(struct b43_wldev *dev) +{ + struct b43_phy_a *aphy; + int err; + + aphy = kzalloc(sizeof(*aphy), GFP_KERNEL); + if (!aphy) + return -ENOMEM; + dev->phy.a = aphy; + + err = b43_aphy_init_tssi2dbm_table(dev); + if (err) + goto err_free_aphy; + + return 0; + +err_free_aphy: + kfree(aphy); + dev->phy.a = NULL; + + return err; +} + +static void b43_aphy_op_prepare_structs(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_a *aphy = phy->a; + const void *tssi2dbm; + int tgt_idle_tssi; + + /* tssi2dbm table is constant, so it is initialized at alloc time. + * Save a copy of the pointer. */ + tssi2dbm = aphy->tssi2dbm; + tgt_idle_tssi = aphy->tgt_idle_tssi; + + /* Zero out the whole PHY structure. */ + memset(aphy, 0, sizeof(*aphy)); + + aphy->tssi2dbm = tssi2dbm; + aphy->tgt_idle_tssi = tgt_idle_tssi; + + //TODO init struct b43_phy_a + +} + +static void b43_aphy_op_free(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_a *aphy = phy->a; + + kfree(aphy->tssi2dbm); + aphy->tssi2dbm = NULL; + + kfree(aphy); + dev->phy.a = NULL; +} + +static int b43_aphy_op_init(struct b43_wldev *dev) +{ + b43_phy_inita(dev); + + return 0; +} + +static inline u16 adjust_phyreg(struct b43_wldev *dev, u16 offset) +{ + /* OFDM registers are base-registers for the A-PHY. */ + if ((offset & B43_PHYROUTE) == B43_PHYROUTE_OFDM_GPHY) { + offset &= ~B43_PHYROUTE; + offset |= B43_PHYROUTE_BASE; + } + +#if B43_DEBUG + if ((offset & B43_PHYROUTE) == B43_PHYROUTE_EXT_GPHY) { + /* Ext-G registers are only available on G-PHYs */ + b43err(dev->wl, "Invalid EXT-G PHY access at " + "0x%04X on A-PHY\n", offset); + dump_stack(); + } + if ((offset & B43_PHYROUTE) == B43_PHYROUTE_N_BMODE) { + /* N-BMODE registers are only available on N-PHYs */ + b43err(dev->wl, "Invalid N-BMODE PHY access at " + "0x%04X on A-PHY\n", offset); + dump_stack(); + } +#endif /* B43_DEBUG */ + + return offset; +} + +static u16 b43_aphy_op_read(struct b43_wldev *dev, u16 reg) +{ + reg = adjust_phyreg(dev, reg); + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + return b43_read16(dev, B43_MMIO_PHY_DATA); +} + +static void b43_aphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) +{ + reg = adjust_phyreg(dev, reg); + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16(dev, B43_MMIO_PHY_DATA, value); +} + +static u16 b43_aphy_op_radio_read(struct b43_wldev *dev, u16 reg) +{ + /* Register 1 is a 32-bit register. */ + B43_WARN_ON(reg == 1); + /* A-PHY needs 0x40 for read access */ + reg |= 0x40; + + b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); + return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); +} + +static void b43_aphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) +{ + /* Register 1 is a 32-bit register. */ + B43_WARN_ON(reg == 1); + + b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); + b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); +} + +static bool b43_aphy_op_supports_hwpctl(struct b43_wldev *dev) +{ + return (dev->phy.rev >= 5); +} + +static void b43_aphy_op_software_rfkill(struct b43_wldev *dev, + bool blocked) +{ + struct b43_phy *phy = &dev->phy; + + if (!blocked) { + if (phy->radio_on) + return; + b43_radio_write16(dev, 0x0004, 0x00C0); + b43_radio_write16(dev, 0x0005, 0x0008); + b43_phy_mask(dev, 0x0010, 0xFFF7); + b43_phy_mask(dev, 0x0011, 0xFFF7); + b43_radio_init2060(dev); + } else { + b43_radio_write16(dev, 0x0004, 0x00FF); + b43_radio_write16(dev, 0x0005, 0x00FB); + b43_phy_set(dev, 0x0010, 0x0008); + b43_phy_set(dev, 0x0011, 0x0008); + } +} + +static int b43_aphy_op_switch_channel(struct b43_wldev *dev, + unsigned int new_channel) +{ + if (new_channel > 200) + return -EINVAL; + aphy_channel_switch(dev, new_channel); + + return 0; +} + +static unsigned int b43_aphy_op_get_default_chan(struct b43_wldev *dev) +{ + return 36; /* Default to channel 36 */ +} + +static void b43_aphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) +{//TODO + struct b43_phy *phy = &dev->phy; + u16 tmp; + int autodiv = 0; + + if (antenna == B43_ANTENNA_AUTO0 || antenna == B43_ANTENNA_AUTO1) + autodiv = 1; + + b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP); + + b43_phy_maskset(dev, B43_PHY_BBANDCFG, ~B43_PHY_BBANDCFG_RXANT, + (autodiv ? B43_ANTENNA_AUTO1 : antenna) << + B43_PHY_BBANDCFG_RXANT_SHIFT); + + if (autodiv) { + tmp = b43_phy_read(dev, B43_PHY_ANTDWELL); + if (antenna == B43_ANTENNA_AUTO1) + tmp &= ~B43_PHY_ANTDWELL_AUTODIV1; + else + tmp |= B43_PHY_ANTDWELL_AUTODIV1; + b43_phy_write(dev, B43_PHY_ANTDWELL, tmp); + } + if (phy->rev < 3) + b43_phy_maskset(dev, B43_PHY_ANTDWELL, 0xFF00, 0x24); + else { + b43_phy_set(dev, B43_PHY_OFDM61, 0x10); + if (phy->rev == 3) { + b43_phy_write(dev, B43_PHY_CLIPPWRDOWNT, 0x1D); + b43_phy_write(dev, B43_PHY_ADIVRELATED, 8); + } else { + b43_phy_write(dev, B43_PHY_CLIPPWRDOWNT, 0x3A); + b43_phy_maskset(dev, B43_PHY_ADIVRELATED, 0xFF00, 8); + } + } + + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP); +} + +static void b43_aphy_op_adjust_txpower(struct b43_wldev *dev) +{//TODO +} + +static enum b43_txpwr_result b43_aphy_op_recalc_txpower(struct b43_wldev *dev, + bool ignore_tssi) +{//TODO + return B43_TXPWR_RES_DONE; +} + +static void b43_aphy_op_pwork_15sec(struct b43_wldev *dev) +{//TODO +} + +static void b43_aphy_op_pwork_60sec(struct b43_wldev *dev) +{//TODO +} + +static const struct b43_phy_operations b43_phyops_a = { + .allocate = b43_aphy_op_allocate, + .free = b43_aphy_op_free, + .prepare_structs = b43_aphy_op_prepare_structs, + .init = b43_aphy_op_init, + .phy_read = b43_aphy_op_read, + .phy_write = b43_aphy_op_write, + .radio_read = b43_aphy_op_radio_read, + .radio_write = b43_aphy_op_radio_write, + .supports_hwpctl = b43_aphy_op_supports_hwpctl, + .software_rfkill = b43_aphy_op_software_rfkill, + .switch_analog = b43_phyop_switch_analog_generic, + .switch_channel = b43_aphy_op_switch_channel, + .get_default_chan = b43_aphy_op_get_default_chan, + .set_rx_antenna = b43_aphy_op_set_rx_antenna, + .recalc_txpower = b43_aphy_op_recalc_txpower, + .adjust_txpower = b43_aphy_op_adjust_txpower, + .pwork_15sec = b43_aphy_op_pwork_15sec, + .pwork_60sec = b43_aphy_op_pwork_60sec, +}; diff --git a/drivers/net/wireless/broadcom/b43/phy_a.h b/drivers/net/wireless/broadcom/b43/phy_a.h new file mode 100644 index 000000000000..f7d0d929a374 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_a.h @@ -0,0 +1,126 @@ +#ifndef LINUX_B43_PHY_A_H_ +#define LINUX_B43_PHY_A_H_ + +#include "phy_common.h" + + +/* OFDM (A) PHY Registers */ +#define B43_PHY_VERSION_OFDM B43_PHY_OFDM(0x00) /* Versioning register for A-PHY */ +#define B43_PHY_BBANDCFG B43_PHY_OFDM(0x01) /* Baseband config */ +#define B43_PHY_BBANDCFG_RXANT 0x180 /* RX Antenna selection */ +#define B43_PHY_BBANDCFG_RXANT_SHIFT 7 +#define B43_PHY_PWRDOWN B43_PHY_OFDM(0x03) /* Powerdown */ +#define B43_PHY_CRSTHRES1_R1 B43_PHY_OFDM(0x06) /* CRS Threshold 1 (phy.rev 1 only) */ +#define B43_PHY_LNAHPFCTL B43_PHY_OFDM(0x1C) /* LNA/HPF control */ +#define B43_PHY_LPFGAINCTL B43_PHY_OFDM(0x20) /* LPF Gain control */ +#define B43_PHY_ADIVRELATED B43_PHY_OFDM(0x27) /* FIXME rename */ +#define B43_PHY_CRS0 B43_PHY_OFDM(0x29) +#define B43_PHY_CRS0_EN 0x4000 +#define B43_PHY_PEAK_COUNT B43_PHY_OFDM(0x30) +#define B43_PHY_ANTDWELL B43_PHY_OFDM(0x2B) /* Antenna dwell */ +#define B43_PHY_ANTDWELL_AUTODIV1 0x0100 /* Automatic RX diversity start antenna */ +#define B43_PHY_ENCORE B43_PHY_OFDM(0x49) /* "Encore" (RangeMax / BroadRange) */ +#define B43_PHY_ENCORE_EN 0x0200 /* Encore enable */ +#define B43_PHY_LMS B43_PHY_OFDM(0x55) +#define B43_PHY_OFDM61 B43_PHY_OFDM(0x61) /* FIXME rename */ +#define B43_PHY_OFDM61_10 0x0010 /* FIXME rename */ +#define B43_PHY_IQBAL B43_PHY_OFDM(0x69) /* I/Q balance */ +#define B43_PHY_BBTXDC_BIAS B43_PHY_OFDM(0x6B) /* Baseband TX DC bias */ +#define B43_PHY_OTABLECTL B43_PHY_OFDM(0x72) /* OFDM table control (see below) */ +#define B43_PHY_OTABLEOFF 0x03FF /* OFDM table offset (see below) */ +#define B43_PHY_OTABLENR 0xFC00 /* OFDM table number (see below) */ +#define B43_PHY_OTABLENR_SHIFT 10 +#define B43_PHY_OTABLEI B43_PHY_OFDM(0x73) /* OFDM table data I */ +#define B43_PHY_OTABLEQ B43_PHY_OFDM(0x74) /* OFDM table data Q */ +#define B43_PHY_HPWR_TSSICTL B43_PHY_OFDM(0x78) /* Hardware power TSSI control */ +#define B43_PHY_ADCCTL B43_PHY_OFDM(0x7A) /* ADC control */ +#define B43_PHY_IDLE_TSSI B43_PHY_OFDM(0x7B) +#define B43_PHY_A_TEMP_SENSE B43_PHY_OFDM(0x7C) /* A PHY temperature sense */ +#define B43_PHY_NRSSITHRES B43_PHY_OFDM(0x8A) /* NRSSI threshold */ +#define B43_PHY_ANTWRSETT B43_PHY_OFDM(0x8C) /* Antenna WR settle */ +#define B43_PHY_ANTWRSETT_ARXDIV 0x2000 /* Automatic RX diversity enabled */ +#define B43_PHY_CLIPPWRDOWNT B43_PHY_OFDM(0x93) /* Clip powerdown threshold */ +#define B43_PHY_OFDM9B B43_PHY_OFDM(0x9B) /* FIXME rename */ +#define B43_PHY_N1P1GAIN B43_PHY_OFDM(0xA0) +#define B43_PHY_P1P2GAIN B43_PHY_OFDM(0xA1) +#define B43_PHY_N1N2GAIN B43_PHY_OFDM(0xA2) +#define B43_PHY_CLIPTHRES B43_PHY_OFDM(0xA3) +#define B43_PHY_CLIPN1P2THRES B43_PHY_OFDM(0xA4) +#define B43_PHY_CCKSHIFTBITS_WA B43_PHY_OFDM(0xA5) /* CCK shiftbits workaround, FIXME rename */ +#define B43_PHY_CCKSHIFTBITS B43_PHY_OFDM(0xA7) /* FIXME rename */ +#define B43_PHY_DIVSRCHIDX B43_PHY_OFDM(0xA8) /* Divider search gain/index */ +#define B43_PHY_CLIPP2THRES B43_PHY_OFDM(0xA9) +#define B43_PHY_CLIPP3THRES B43_PHY_OFDM(0xAA) +#define B43_PHY_DIVP1P2GAIN B43_PHY_OFDM(0xAB) +#define B43_PHY_DIVSRCHGAINBACK B43_PHY_OFDM(0xAD) /* Divider search gain back */ +#define B43_PHY_DIVSRCHGAINCHNG B43_PHY_OFDM(0xAE) /* Divider search gain change */ +#define B43_PHY_CRSTHRES1 B43_PHY_OFDM(0xC0) /* CRS Threshold 1 (phy.rev >= 2 only) */ +#define B43_PHY_CRSTHRES2 B43_PHY_OFDM(0xC1) /* CRS Threshold 2 (phy.rev >= 2 only) */ +#define B43_PHY_TSSIP_LTBASE B43_PHY_OFDM(0x380) /* TSSI power lookup table base */ +#define B43_PHY_DC_LTBASE B43_PHY_OFDM(0x3A0) /* DC lookup table base */ +#define B43_PHY_GAIN_LTBASE B43_PHY_OFDM(0x3C0) /* Gain lookup table base */ + +/*** OFDM table numbers ***/ +#define B43_OFDMTAB(number, offset) (((number) << B43_PHY_OTABLENR_SHIFT) | (offset)) +#define B43_OFDMTAB_AGC1 B43_OFDMTAB(0x00, 0) +#define B43_OFDMTAB_GAIN0 B43_OFDMTAB(0x00, 0) +#define B43_OFDMTAB_GAINX B43_OFDMTAB(0x01, 0) //TODO rename +#define B43_OFDMTAB_GAIN1 B43_OFDMTAB(0x01, 4) +#define B43_OFDMTAB_AGC3 B43_OFDMTAB(0x02, 0) +#define B43_OFDMTAB_GAIN2 B43_OFDMTAB(0x02, 3) +#define B43_OFDMTAB_LNAHPFGAIN1 B43_OFDMTAB(0x03, 0) +#define B43_OFDMTAB_WRSSI B43_OFDMTAB(0x04, 0) +#define B43_OFDMTAB_LNAHPFGAIN2 B43_OFDMTAB(0x04, 0) +#define B43_OFDMTAB_NOISESCALE B43_OFDMTAB(0x05, 0) +#define B43_OFDMTAB_AGC2 B43_OFDMTAB(0x06, 0) +#define B43_OFDMTAB_ROTOR B43_OFDMTAB(0x08, 0) +#define B43_OFDMTAB_ADVRETARD B43_OFDMTAB(0x09, 0) +#define B43_OFDMTAB_DAC B43_OFDMTAB(0x0C, 0) +#define B43_OFDMTAB_DC B43_OFDMTAB(0x0E, 7) +#define B43_OFDMTAB_PWRDYN2 B43_OFDMTAB(0x0E, 12) +#define B43_OFDMTAB_LNAGAIN B43_OFDMTAB(0x0E, 13) +#define B43_OFDMTAB_UNKNOWN_0F B43_OFDMTAB(0x0F, 0) //TODO rename +#define B43_OFDMTAB_UNKNOWN_APHY B43_OFDMTAB(0x0F, 7) //TODO rename +#define B43_OFDMTAB_LPFGAIN B43_OFDMTAB(0x0F, 12) +#define B43_OFDMTAB_RSSI B43_OFDMTAB(0x10, 0) +#define B43_OFDMTAB_UNKNOWN_11 B43_OFDMTAB(0x11, 4) //TODO rename +#define B43_OFDMTAB_AGC1_R1 B43_OFDMTAB(0x13, 0) +#define B43_OFDMTAB_GAINX_R1 B43_OFDMTAB(0x14, 0) //TODO remove! +#define B43_OFDMTAB_MINSIGSQ B43_OFDMTAB(0x14, 0) +#define B43_OFDMTAB_AGC3_R1 B43_OFDMTAB(0x15, 0) +#define B43_OFDMTAB_WRSSI_R1 B43_OFDMTAB(0x15, 4) +#define B43_OFDMTAB_TSSI B43_OFDMTAB(0x15, 0) +#define B43_OFDMTAB_DACRFPABB B43_OFDMTAB(0x16, 0) +#define B43_OFDMTAB_DACOFF B43_OFDMTAB(0x17, 0) +#define B43_OFDMTAB_DCBIAS B43_OFDMTAB(0x18, 0) + +u16 b43_ofdmtab_read16(struct b43_wldev *dev, u16 table, u16 offset); +void b43_ofdmtab_write16(struct b43_wldev *dev, u16 table, + u16 offset, u16 value); +u32 b43_ofdmtab_read32(struct b43_wldev *dev, u16 table, u16 offset); +void b43_ofdmtab_write32(struct b43_wldev *dev, u16 table, + u16 offset, u32 value); + + +struct b43_phy_a { + /* Pointer to the table used to convert a + * TSSI value to dBm-Q5.2 */ + const s8 *tssi2dbm; + /* Target idle TSSI */ + int tgt_idle_tssi; + /* Current idle TSSI */ + int cur_idle_tssi;//FIXME value currently not set + + /* A-PHY TX Power control value. */ + u16 txpwr_offset; + + //TODO lots of missing stuff +}; + +/** + * b43_phy_inita - Lowlevel A-PHY init routine. + * This is _only_ used by the G-PHY code. + */ +void b43_phy_inita(struct b43_wldev *dev); + +#endif /* LINUX_B43_PHY_A_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/phy_ac.c b/drivers/net/wireless/broadcom/b43/phy_ac.c new file mode 100644 index 000000000000..e75633d67938 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_ac.c @@ -0,0 +1,92 @@ +/* + * Broadcom B43 wireless driver + * IEEE 802.11ac AC-PHY support + * + * Copyright (c) 2015 RafaÅ‚ MiÅ‚ecki + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include "b43.h" +#include "phy_ac.h" + +/************************************************** + * Basic PHY ops + **************************************************/ + +static int b43_phy_ac_op_allocate(struct b43_wldev *dev) +{ + struct b43_phy_ac *phy_ac; + + phy_ac = kzalloc(sizeof(*phy_ac), GFP_KERNEL); + if (!phy_ac) + return -ENOMEM; + dev->phy.ac = phy_ac; + + return 0; +} + +static void b43_phy_ac_op_free(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_ac *phy_ac = phy->ac; + + kfree(phy_ac); + phy->ac = NULL; +} + +static void b43_phy_ac_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, + u16 set) +{ + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16(dev, B43_MMIO_PHY_DATA, + (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); +} + +static u16 b43_phy_ac_op_radio_read(struct b43_wldev *dev, u16 reg) +{ + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); + return b43_read16(dev, B43_MMIO_RADIO24_DATA); +} + +static void b43_phy_ac_op_radio_write(struct b43_wldev *dev, u16 reg, + u16 value) +{ + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); + b43_write16(dev, B43_MMIO_RADIO24_DATA, value); +} + +static unsigned int b43_phy_ac_op_get_default_chan(struct b43_wldev *dev) +{ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + return 11; + return 36; +} + +static enum b43_txpwr_result +b43_phy_ac_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) +{ + return B43_TXPWR_RES_DONE; +} + +static void b43_phy_ac_op_adjust_txpower(struct b43_wldev *dev) +{ +} + +/************************************************** + * PHY ops struct + **************************************************/ + +const struct b43_phy_operations b43_phyops_ac = { + .allocate = b43_phy_ac_op_allocate, + .free = b43_phy_ac_op_free, + .phy_maskset = b43_phy_ac_op_maskset, + .radio_read = b43_phy_ac_op_radio_read, + .radio_write = b43_phy_ac_op_radio_write, + .get_default_chan = b43_phy_ac_op_get_default_chan, + .recalc_txpower = b43_phy_ac_op_recalc_txpower, + .adjust_txpower = b43_phy_ac_op_adjust_txpower, +}; diff --git a/drivers/net/wireless/broadcom/b43/phy_ac.h b/drivers/net/wireless/broadcom/b43/phy_ac.h new file mode 100644 index 000000000000..d1ca79e0eb24 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_ac.h @@ -0,0 +1,38 @@ +#ifndef B43_PHY_AC_H_ +#define B43_PHY_AC_H_ + +#include "phy_common.h" + +#define B43_PHY_AC_BBCFG 0x001 +#define B43_PHY_AC_BBCFG_RSTCCA 0x4000 /* Reset CCA */ +#define B43_PHY_AC_BANDCTL 0x003 /* Band control */ +#define B43_PHY_AC_BANDCTL_5GHZ 0x0001 +#define B43_PHY_AC_TABLE_ID 0x00d +#define B43_PHY_AC_TABLE_OFFSET 0x00e +#define B43_PHY_AC_TABLE_DATA1 0x00f +#define B43_PHY_AC_TABLE_DATA2 0x010 +#define B43_PHY_AC_TABLE_DATA3 0x011 +#define B43_PHY_AC_CLASSCTL 0x140 /* Classifier control */ +#define B43_PHY_AC_CLASSCTL_CCKEN 0x0001 /* CCK enable */ +#define B43_PHY_AC_CLASSCTL_OFDMEN 0x0002 /* OFDM enable */ +#define B43_PHY_AC_CLASSCTL_WAITEDEN 0x0004 /* Waited enable */ +#define B43_PHY_AC_BW1A 0x371 +#define B43_PHY_AC_BW2 0x372 +#define B43_PHY_AC_BW3 0x373 +#define B43_PHY_AC_BW4 0x374 +#define B43_PHY_AC_BW5 0x375 +#define B43_PHY_AC_BW6 0x376 +#define B43_PHY_AC_RFCTL_CMD 0x408 +#define B43_PHY_AC_C1_CLIP 0x6d4 +#define B43_PHY_AC_C1_CLIP_DIS 0x4000 +#define B43_PHY_AC_C2_CLIP 0x8d4 +#define B43_PHY_AC_C2_CLIP_DIS 0x4000 +#define B43_PHY_AC_C3_CLIP 0xad4 +#define B43_PHY_AC_C3_CLIP_DIS 0x4000 + +struct b43_phy_ac { +}; + +extern const struct b43_phy_operations b43_phyops_ac; + +#endif /* B43_PHY_AC_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/phy_common.c b/drivers/net/wireless/broadcom/b43/phy_common.c new file mode 100644 index 000000000000..ec2b9c577b90 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_common.c @@ -0,0 +1,653 @@ +/* + + Broadcom B43 wireless driver + Common PHY routines + + Copyright (c) 2005 Martin Langer , + Copyright (c) 2005-2007 Stefano Brivio + Copyright (c) 2005-2008 Michael Buesch + Copyright (c) 2005, 2006 Danny van Dyk + Copyright (c) 2005, 2006 Andreas Jaggi + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "phy_common.h" +#include "phy_g.h" +#include "phy_a.h" +#include "phy_n.h" +#include "phy_lp.h" +#include "phy_ht.h" +#include "phy_lcn.h" +#include "phy_ac.h" +#include "b43.h" +#include "main.h" + + +int b43_phy_allocate(struct b43_wldev *dev) +{ + struct b43_phy *phy = &(dev->phy); + int err; + + phy->ops = NULL; + + switch (phy->type) { + case B43_PHYTYPE_G: +#ifdef CONFIG_B43_PHY_G + phy->ops = &b43_phyops_g; +#endif + break; + case B43_PHYTYPE_N: +#ifdef CONFIG_B43_PHY_N + phy->ops = &b43_phyops_n; +#endif + break; + case B43_PHYTYPE_LP: +#ifdef CONFIG_B43_PHY_LP + phy->ops = &b43_phyops_lp; +#endif + break; + case B43_PHYTYPE_HT: +#ifdef CONFIG_B43_PHY_HT + phy->ops = &b43_phyops_ht; +#endif + break; + case B43_PHYTYPE_LCN: +#ifdef CONFIG_B43_PHY_LCN + phy->ops = &b43_phyops_lcn; +#endif + break; + case B43_PHYTYPE_AC: +#ifdef CONFIG_B43_PHY_AC + phy->ops = &b43_phyops_ac; +#endif + break; + } + if (B43_WARN_ON(!phy->ops)) + return -ENODEV; + + err = phy->ops->allocate(dev); + if (err) + phy->ops = NULL; + + return err; +} + +void b43_phy_free(struct b43_wldev *dev) +{ + dev->phy.ops->free(dev); + dev->phy.ops = NULL; +} + +int b43_phy_init(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + const struct b43_phy_operations *ops = phy->ops; + int err; + + /* During PHY init we need to use some channel. On the first init this + * function is called *before* b43_op_config, so our pointer is NULL. + */ + if (!phy->chandef) { + phy->chandef = &dev->wl->hw->conf.chandef; + phy->channel = phy->chandef->chan->hw_value; + } + + phy->ops->switch_analog(dev, true); + b43_software_rfkill(dev, false); + + err = ops->init(dev); + if (err) { + b43err(dev->wl, "PHY init failed\n"); + goto err_block_rf; + } + phy->do_full_init = false; + + err = b43_switch_channel(dev, phy->channel); + if (err) { + b43err(dev->wl, "PHY init: Channel switch to default failed\n"); + goto err_phy_exit; + } + + return 0; + +err_phy_exit: + phy->do_full_init = true; + if (ops->exit) + ops->exit(dev); +err_block_rf: + b43_software_rfkill(dev, true); + + return err; +} + +void b43_phy_exit(struct b43_wldev *dev) +{ + const struct b43_phy_operations *ops = dev->phy.ops; + + b43_software_rfkill(dev, true); + dev->phy.do_full_init = true; + if (ops->exit) + ops->exit(dev); +} + +bool b43_has_hardware_pctl(struct b43_wldev *dev) +{ + if (!dev->phy.hardware_power_control) + return false; + if (!dev->phy.ops->supports_hwpctl) + return false; + return dev->phy.ops->supports_hwpctl(dev); +} + +void b43_radio_lock(struct b43_wldev *dev) +{ + u32 macctl; + +#if B43_DEBUG + B43_WARN_ON(dev->phy.radio_locked); + dev->phy.radio_locked = true; +#endif + + macctl = b43_read32(dev, B43_MMIO_MACCTL); + macctl |= B43_MACCTL_RADIOLOCK; + b43_write32(dev, B43_MMIO_MACCTL, macctl); + /* Commit the write and wait for the firmware + * to finish any radio register access. */ + b43_read32(dev, B43_MMIO_MACCTL); + udelay(10); +} + +void b43_radio_unlock(struct b43_wldev *dev) +{ + u32 macctl; + +#if B43_DEBUG + B43_WARN_ON(!dev->phy.radio_locked); + dev->phy.radio_locked = false; +#endif + + /* Commit any write */ + b43_read16(dev, B43_MMIO_PHY_VER); + /* unlock */ + macctl = b43_read32(dev, B43_MMIO_MACCTL); + macctl &= ~B43_MACCTL_RADIOLOCK; + b43_write32(dev, B43_MMIO_MACCTL, macctl); +} + +void b43_phy_lock(struct b43_wldev *dev) +{ +#if B43_DEBUG + B43_WARN_ON(dev->phy.phy_locked); + dev->phy.phy_locked = true; +#endif + B43_WARN_ON(dev->dev->core_rev < 3); + + if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) + b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); +} + +void b43_phy_unlock(struct b43_wldev *dev) +{ +#if B43_DEBUG + B43_WARN_ON(!dev->phy.phy_locked); + dev->phy.phy_locked = false; +#endif + B43_WARN_ON(dev->dev->core_rev < 3); + + if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) + b43_power_saving_ctl_bits(dev, 0); +} + +static inline void assert_mac_suspended(struct b43_wldev *dev) +{ + if (!B43_DEBUG) + return; + if ((b43_status(dev) >= B43_STAT_INITIALIZED) && + (dev->mac_suspended <= 0)) { + b43dbg(dev->wl, "PHY/RADIO register access with " + "enabled MAC.\n"); + dump_stack(); + } +} + +u16 b43_radio_read(struct b43_wldev *dev, u16 reg) +{ + assert_mac_suspended(dev); + dev->phy.writes_counter = 0; + return dev->phy.ops->radio_read(dev, reg); +} + +void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value) +{ + assert_mac_suspended(dev); + if (b43_bus_host_is_pci(dev->dev) && + ++dev->phy.writes_counter > B43_MAX_WRITES_IN_ROW) { + b43_read32(dev, B43_MMIO_MACCTL); + dev->phy.writes_counter = 1; + } + dev->phy.ops->radio_write(dev, reg, value); +} + +void b43_radio_mask(struct b43_wldev *dev, u16 offset, u16 mask) +{ + b43_radio_write16(dev, offset, + b43_radio_read16(dev, offset) & mask); +} + +void b43_radio_set(struct b43_wldev *dev, u16 offset, u16 set) +{ + b43_radio_write16(dev, offset, + b43_radio_read16(dev, offset) | set); +} + +void b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) +{ + b43_radio_write16(dev, offset, + (b43_radio_read16(dev, offset) & mask) | set); +} + +bool b43_radio_wait_value(struct b43_wldev *dev, u16 offset, u16 mask, + u16 value, int delay, int timeout) +{ + u16 val; + int i; + + for (i = 0; i < timeout; i += delay) { + val = b43_radio_read(dev, offset); + if ((val & mask) == value) + return true; + udelay(delay); + } + return false; +} + +u16 b43_phy_read(struct b43_wldev *dev, u16 reg) +{ + assert_mac_suspended(dev); + dev->phy.writes_counter = 0; + + if (dev->phy.ops->phy_read) + return dev->phy.ops->phy_read(dev, reg); + + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + return b43_read16(dev, B43_MMIO_PHY_DATA); +} + +void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value) +{ + assert_mac_suspended(dev); + if (b43_bus_host_is_pci(dev->dev) && + ++dev->phy.writes_counter > B43_MAX_WRITES_IN_ROW) { + b43_read16(dev, B43_MMIO_PHY_VER); + dev->phy.writes_counter = 1; + } + + if (dev->phy.ops->phy_write) + return dev->phy.ops->phy_write(dev, reg, value); + + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16(dev, B43_MMIO_PHY_DATA, value); +} + +void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg) +{ + b43_phy_write(dev, destreg, b43_phy_read(dev, srcreg)); +} + +void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask) +{ + if (dev->phy.ops->phy_maskset) { + assert_mac_suspended(dev); + dev->phy.ops->phy_maskset(dev, offset, mask, 0); + } else { + b43_phy_write(dev, offset, + b43_phy_read(dev, offset) & mask); + } +} + +void b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set) +{ + if (dev->phy.ops->phy_maskset) { + assert_mac_suspended(dev); + dev->phy.ops->phy_maskset(dev, offset, 0xFFFF, set); + } else { + b43_phy_write(dev, offset, + b43_phy_read(dev, offset) | set); + } +} + +void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) +{ + if (dev->phy.ops->phy_maskset) { + assert_mac_suspended(dev); + dev->phy.ops->phy_maskset(dev, offset, mask, set); + } else { + b43_phy_write(dev, offset, + (b43_phy_read(dev, offset) & mask) | set); + } +} + +void b43_phy_put_into_reset(struct b43_wldev *dev) +{ + u32 tmp; + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~B43_BCMA_IOCTL_GMODE; + tmp |= B43_BCMA_IOCTL_PHY_RESET; + tmp |= BCMA_IOCTL_FGC; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~BCMA_IOCTL_FGC; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~B43_TMSLOW_GMODE; + tmp |= B43_TMSLOW_PHYRESET; + tmp |= SSB_TMSLOW_FGC; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + usleep_range(1000, 2000); + + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~SSB_TMSLOW_FGC; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + usleep_range(1000, 2000); + + break; +#endif + } +} + +void b43_phy_take_out_of_reset(struct b43_wldev *dev) +{ + u32 tmp; + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + /* Unset reset bit (with forcing clock) */ + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~B43_BCMA_IOCTL_PHY_RESET; + tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN; + tmp |= BCMA_IOCTL_FGC; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + + /* Do not force clock anymore */ + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~BCMA_IOCTL_FGC; + tmp |= B43_BCMA_IOCTL_PHY_CLKEN; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + /* Unset reset bit (with forcing clock) */ + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~B43_TMSLOW_PHYRESET; + tmp &= ~B43_TMSLOW_PHYCLKEN; + tmp |= SSB_TMSLOW_FGC; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ + usleep_range(1000, 2000); + + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~SSB_TMSLOW_FGC; + tmp |= B43_TMSLOW_PHYCLKEN; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ + usleep_range(1000, 2000); + break; +#endif + } +} + +int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) +{ + struct b43_phy *phy = &(dev->phy); + u16 channelcookie, savedcookie; + int err; + + /* First we set the channel radio code to prevent the + * firmware from sending ghost packets. + */ + channelcookie = new_channel; + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + channelcookie |= B43_SHM_SH_CHAN_5GHZ; + /* FIXME: set 40Mhz flag if required */ + if (0) + channelcookie |= B43_SHM_SH_CHAN_40MHZ; + savedcookie = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN, channelcookie); + + /* Now try to switch the PHY hardware channel. */ + err = phy->ops->switch_channel(dev, new_channel); + if (err) + goto err_restore_cookie; + + /* Wait for the radio to tune to the channel and stabilize. */ + msleep(8); + + return 0; + +err_restore_cookie: + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_CHAN, savedcookie); + + return err; +} + +void b43_software_rfkill(struct b43_wldev *dev, bool blocked) +{ + struct b43_phy *phy = &dev->phy; + + b43_mac_suspend(dev); + phy->ops->software_rfkill(dev, blocked); + phy->radio_on = !blocked; + b43_mac_enable(dev); +} + +/** + * b43_phy_txpower_adjust_work - TX power workqueue. + * + * Workqueue for updating the TX power parameters in hardware. + */ +void b43_phy_txpower_adjust_work(struct work_struct *work) +{ + struct b43_wl *wl = container_of(work, struct b43_wl, + txpower_adjust_work); + struct b43_wldev *dev; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + + if (likely(dev && (b43_status(dev) >= B43_STAT_STARTED))) + dev->phy.ops->adjust_txpower(dev); + + mutex_unlock(&wl->mutex); +} + +void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags) +{ + struct b43_phy *phy = &dev->phy; + unsigned long now = jiffies; + enum b43_txpwr_result result; + + if (!(flags & B43_TXPWR_IGNORE_TIME)) { + /* Check if it's time for a TXpower check. */ + if (time_before(now, phy->next_txpwr_check_time)) + return; /* Not yet */ + } + /* The next check will be needed in two seconds, or later. */ + phy->next_txpwr_check_time = round_jiffies(now + (HZ * 2)); + + if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) && + (dev->dev->board_type == SSB_BOARD_BU4306)) + return; /* No software txpower adjustment needed */ + + result = phy->ops->recalc_txpower(dev, !!(flags & B43_TXPWR_IGNORE_TSSI)); + if (result == B43_TXPWR_RES_DONE) + return; /* We are done. */ + B43_WARN_ON(result != B43_TXPWR_RES_NEED_ADJUST); + B43_WARN_ON(phy->ops->adjust_txpower == NULL); + + /* We must adjust the transmission power in hardware. + * Schedule b43_phy_txpower_adjust_work(). */ + ieee80211_queue_work(dev->wl->hw, &dev->wl->txpower_adjust_work); +} + +int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset) +{ + const bool is_ofdm = (shm_offset != B43_SHM_SH_TSSI_CCK); + unsigned int a, b, c, d; + unsigned int average; + u32 tmp; + + tmp = b43_shm_read32(dev, B43_SHM_SHARED, shm_offset); + a = tmp & 0xFF; + b = (tmp >> 8) & 0xFF; + c = (tmp >> 16) & 0xFF; + d = (tmp >> 24) & 0xFF; + if (a == 0 || a == B43_TSSI_MAX || + b == 0 || b == B43_TSSI_MAX || + c == 0 || c == B43_TSSI_MAX || + d == 0 || d == B43_TSSI_MAX) + return -ENOENT; + /* The values are OK. Clear them. */ + tmp = B43_TSSI_MAX | (B43_TSSI_MAX << 8) | + (B43_TSSI_MAX << 16) | (B43_TSSI_MAX << 24); + b43_shm_write32(dev, B43_SHM_SHARED, shm_offset, tmp); + + if (is_ofdm) { + a = (a + 32) & 0x3F; + b = (b + 32) & 0x3F; + c = (c + 32) & 0x3F; + d = (d + 32) & 0x3F; + } + + /* Get the average of the values with 0.5 added to each value. */ + average = (a + b + c + d + 2) / 4; + if (is_ofdm) { + /* Adjust for CCK-boost */ + if (b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTF1) + & B43_HF_CCKBOOST) + average = (average >= 13) ? (average - 13) : 0; + } + + return average; +} + +void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on) +{ + b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4); +} + + +bool b43_is_40mhz(struct b43_wldev *dev) +{ + return dev->phy.chandef->width == NL80211_CHAN_WIDTH_40; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */ +void b43_phy_force_clock(struct b43_wldev *dev, bool force) +{ + u32 tmp; + + WARN_ON(dev->phy.type != B43_PHYTYPE_N && + dev->phy.type != B43_PHYTYPE_HT && + dev->phy.type != B43_PHYTYPE_AC); + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + if (force) + tmp |= BCMA_IOCTL_FGC; + else + tmp &= ~BCMA_IOCTL_FGC; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + if (force) + tmp |= SSB_TMSLOW_FGC; + else + tmp &= ~SSB_TMSLOW_FGC; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + break; +#endif + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/Cordic */ +struct b43_c32 b43_cordic(int theta) +{ + static const u32 arctg[] = { + 2949120, 1740967, 919879, 466945, 234379, 117304, + 58666, 29335, 14668, 7334, 3667, 1833, + 917, 458, 229, 115, 57, 29, + }; + u8 i; + s32 tmp; + s8 signx = 1; + u32 angle = 0; + struct b43_c32 ret = { .i = 39797, .q = 0, }; + + while (theta > (180 << 16)) + theta -= (360 << 16); + while (theta < -(180 << 16)) + theta += (360 << 16); + + if (theta > (90 << 16)) { + theta -= (180 << 16); + signx = -1; + } else if (theta < -(90 << 16)) { + theta += (180 << 16); + signx = -1; + } + + for (i = 0; i <= 17; i++) { + if (theta > angle) { + tmp = ret.i - (ret.q >> i); + ret.q += ret.i >> i; + ret.i = tmp; + angle += arctg[i]; + } else { + tmp = ret.i + (ret.q >> i); + ret.q -= ret.i >> i; + ret.i = tmp; + angle -= arctg[i]; + } + } + + ret.i *= signx; + ret.q *= signx; + + return ret; +} diff --git a/drivers/net/wireless/broadcom/b43/phy_common.h b/drivers/net/wireless/broadcom/b43/phy_common.h new file mode 100644 index 000000000000..78d86526799e --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_common.h @@ -0,0 +1,457 @@ +#ifndef LINUX_B43_PHY_COMMON_H_ +#define LINUX_B43_PHY_COMMON_H_ + +#include +#include + +struct b43_wldev; + +/* Complex number using 2 32-bit signed integers */ +struct b43_c32 { s32 i, q; }; + +#define CORDIC_CONVERT(value) (((value) >= 0) ? \ + ((((value) >> 15) + 1) >> 1) : \ + -((((-(value)) >> 15) + 1) >> 1)) + +/* PHY register routing bits */ +#define B43_PHYROUTE 0x0C00 /* PHY register routing bits mask */ +#define B43_PHYROUTE_BASE 0x0000 /* Base registers */ +#define B43_PHYROUTE_OFDM_GPHY 0x0400 /* OFDM register routing for G-PHYs */ +#define B43_PHYROUTE_EXT_GPHY 0x0800 /* Extended G-PHY registers */ +#define B43_PHYROUTE_N_BMODE 0x0C00 /* N-PHY BMODE registers */ + +/* CCK (B-PHY) registers. */ +#define B43_PHY_CCK(reg) ((reg) | B43_PHYROUTE_BASE) +/* N-PHY registers. */ +#define B43_PHY_N(reg) ((reg) | B43_PHYROUTE_BASE) +/* N-PHY BMODE registers. */ +#define B43_PHY_N_BMODE(reg) ((reg) | B43_PHYROUTE_N_BMODE) +/* OFDM (A-PHY) registers. */ +#define B43_PHY_OFDM(reg) ((reg) | B43_PHYROUTE_OFDM_GPHY) +/* Extended G-PHY registers. */ +#define B43_PHY_EXTG(reg) ((reg) | B43_PHYROUTE_EXT_GPHY) + + +/* Masks for the PHY versioning registers. */ +#define B43_PHYVER_ANALOG 0xF000 +#define B43_PHYVER_ANALOG_SHIFT 12 +#define B43_PHYVER_TYPE 0x0F00 +#define B43_PHYVER_TYPE_SHIFT 8 +#define B43_PHYVER_VERSION 0x00FF + +/* PHY writes need to be flushed if we reach limit */ +#define B43_MAX_WRITES_IN_ROW 24 + +/** + * enum b43_interference_mitigation - Interference Mitigation mode + * + * @B43_INTERFMODE_NONE: Disabled + * @B43_INTERFMODE_NONWLAN: Non-WLAN Interference Mitigation + * @B43_INTERFMODE_MANUALWLAN: WLAN Interference Mitigation + * @B43_INTERFMODE_AUTOWLAN: Automatic WLAN Interference Mitigation + */ +enum b43_interference_mitigation { + B43_INTERFMODE_NONE, + B43_INTERFMODE_NONWLAN, + B43_INTERFMODE_MANUALWLAN, + B43_INTERFMODE_AUTOWLAN, +}; + +/* Antenna identifiers */ +enum { + B43_ANTENNA0 = 0, /* Antenna 0 */ + B43_ANTENNA1 = 1, /* Antenna 1 */ + B43_ANTENNA_AUTO0 = 2, /* Automatic, starting with antenna 0 */ + B43_ANTENNA_AUTO1 = 3, /* Automatic, starting with antenna 1 */ + B43_ANTENNA2 = 4, + B43_ANTENNA3 = 8, + + B43_ANTENNA_AUTO = B43_ANTENNA_AUTO0, + B43_ANTENNA_DEFAULT = B43_ANTENNA_AUTO, +}; + +/** + * enum b43_txpwr_result - Return value for the recalc_txpower PHY op. + * + * @B43_TXPWR_RES_NEED_ADJUST: Values changed. Hardware adjustment is needed. + * @B43_TXPWR_RES_DONE: No more work to do. Everything is done. + */ +enum b43_txpwr_result { + B43_TXPWR_RES_NEED_ADJUST, + B43_TXPWR_RES_DONE, +}; + +/** + * struct b43_phy_operations - Function pointers for PHY ops. + * + * @allocate: Allocate and initialise the PHY data structures. + * Must not be NULL. + * @free: Destroy and free the PHY data structures. + * Must not be NULL. + * + * @prepare_structs: Prepare the PHY data structures. + * The data structures allocated in @allocate are + * initialized here. + * Must not be NULL. + * @prepare_hardware: Prepare the PHY. This is called before b43_chip_init to + * do some early early PHY hardware init. + * Can be NULL, if not required. + * @init: Initialize the PHY. + * Must not be NULL. + * @exit: Shutdown the PHY. + * Can be NULL, if not required. + * + * @phy_read: Read from a PHY register. + * Must not be NULL. + * @phy_write: Write to a PHY register. + * Must not be NULL. + * @phy_maskset: Maskset a PHY register, taking shortcuts. + * If it is NULL, a generic algorithm is used. + * @radio_read: Read from a Radio register. + * Must not be NULL. + * @radio_write: Write to a Radio register. + * Must not be NULL. + * + * @supports_hwpctl: Returns a boolean whether Hardware Power Control + * is supported or not. + * If NULL, hwpctl is assumed to be never supported. + * @software_rfkill: Turn the radio ON or OFF. + * Possible state values are + * RFKILL_STATE_SOFT_BLOCKED or + * RFKILL_STATE_UNBLOCKED + * Must not be NULL. + * @switch_analog: Turn the Analog on/off. + * Must not be NULL. + * @switch_channel: Switch the radio to another channel. + * Must not be NULL. + * @get_default_chan: Just returns the default channel number. + * Must not be NULL. + * @set_rx_antenna: Set the antenna used for RX. + * Can be NULL, if not supported. + * @interf_mitigation: Switch the Interference Mitigation mode. + * Can be NULL, if not supported. + * + * @recalc_txpower: Recalculate the transmission power parameters. + * This callback has to recalculate the TX power settings, + * but does not need to write them to the hardware, yet. + * Returns enum b43_txpwr_result to indicate whether the hardware + * needs to be adjusted. + * If B43_TXPWR_NEED_ADJUST is returned, @adjust_txpower + * will be called later. + * If the parameter "ignore_tssi" is true, the TSSI values should + * be ignored and a recalculation of the power settings should be + * done even if the TSSI values did not change. + * This function may sleep, but should not. + * Must not be NULL. + * @adjust_txpower: Write the previously calculated TX power settings + * (from @recalc_txpower) to the hardware. + * This function may sleep. + * Can be NULL, if (and ONLY if) @recalc_txpower _always_ + * returns B43_TXPWR_RES_DONE. + * + * @pwork_15sec: Periodic work. Called every 15 seconds. + * Can be NULL, if not required. + * @pwork_60sec: Periodic work. Called every 60 seconds. + * Can be NULL, if not required. + */ +struct b43_phy_operations { + /* Initialisation */ + int (*allocate)(struct b43_wldev *dev); + void (*free)(struct b43_wldev *dev); + void (*prepare_structs)(struct b43_wldev *dev); + int (*prepare_hardware)(struct b43_wldev *dev); + int (*init)(struct b43_wldev *dev); + void (*exit)(struct b43_wldev *dev); + + /* Register access */ + u16 (*phy_read)(struct b43_wldev *dev, u16 reg); + void (*phy_write)(struct b43_wldev *dev, u16 reg, u16 value); + void (*phy_maskset)(struct b43_wldev *dev, u16 reg, u16 mask, u16 set); + u16 (*radio_read)(struct b43_wldev *dev, u16 reg); + void (*radio_write)(struct b43_wldev *dev, u16 reg, u16 value); + + /* Radio */ + bool (*supports_hwpctl)(struct b43_wldev *dev); + void (*software_rfkill)(struct b43_wldev *dev, bool blocked); + void (*switch_analog)(struct b43_wldev *dev, bool on); + int (*switch_channel)(struct b43_wldev *dev, unsigned int new_channel); + unsigned int (*get_default_chan)(struct b43_wldev *dev); + void (*set_rx_antenna)(struct b43_wldev *dev, int antenna); + int (*interf_mitigation)(struct b43_wldev *dev, + enum b43_interference_mitigation new_mode); + + /* Transmission power adjustment */ + enum b43_txpwr_result (*recalc_txpower)(struct b43_wldev *dev, + bool ignore_tssi); + void (*adjust_txpower)(struct b43_wldev *dev); + + /* Misc */ + void (*pwork_15sec)(struct b43_wldev *dev); + void (*pwork_60sec)(struct b43_wldev *dev); +}; + +struct b43_phy_a; +struct b43_phy_g; +struct b43_phy_n; +struct b43_phy_lp; +struct b43_phy_ht; +struct b43_phy_lcn; + +struct b43_phy { + /* Hardware operation callbacks. */ + const struct b43_phy_operations *ops; + + /* Most hardware context information is stored in the standard- + * specific data structures pointed to by the pointers below. + * Only one of them is valid (the currently enabled PHY). */ +#ifdef CONFIG_B43_DEBUG + /* No union for debug build to force NULL derefs in buggy code. */ + struct { +#else + union { +#endif + /* A-PHY specific information */ + struct b43_phy_a *a; + /* G-PHY specific information */ + struct b43_phy_g *g; + /* N-PHY specific information */ + struct b43_phy_n *n; + /* LP-PHY specific information */ + struct b43_phy_lp *lp; + /* HT-PHY specific information */ + struct b43_phy_ht *ht; + /* LCN-PHY specific information */ + struct b43_phy_lcn *lcn; + /* AC-PHY specific information */ + struct b43_phy_ac *ac; + }; + + /* Band support flags. */ + bool supports_2ghz; + bool supports_5ghz; + + /* Is GMODE (2 GHz mode) bit enabled? */ + bool gmode; + + /* After power reset full init has to be performed */ + bool do_full_init; + + /* Analog Type */ + u8 analog; + /* B43_PHYTYPE_ */ + u8 type; + /* PHY revision number. */ + u8 rev; + + /* Count writes since last read */ + u8 writes_counter; + + /* Radio versioning */ + u16 radio_manuf; /* Radio manufacturer */ + u16 radio_ver; /* Radio version */ + u8 radio_rev; /* Radio revision */ + + /* Software state of the radio */ + bool radio_on; + + /* Desired TX power level (in dBm). + * This is set by the user and adjusted in b43_phy_xmitpower(). */ + int desired_txpower; + + /* Hardware Power Control enabled? */ + bool hardware_power_control; + + /* The time (in absolute jiffies) when the next TX power output + * check is needed. */ + unsigned long next_txpwr_check_time; + + /* Current channel */ + struct cfg80211_chan_def *chandef; + unsigned int channel; + + /* PHY TX errors counter. */ + atomic_t txerr_cnt; + +#ifdef CONFIG_B43_DEBUG + /* PHY registers locked (w.r.t. firmware) */ + bool phy_locked; + /* Radio registers locked (w.r.t. firmware) */ + bool radio_locked; +#endif /* B43_DEBUG */ +}; + + +/** + * b43_phy_allocate - Allocate PHY structs + * Allocate the PHY data structures, based on the current dev->phy.type + */ +int b43_phy_allocate(struct b43_wldev *dev); + +/** + * b43_phy_free - Free PHY structs + */ +void b43_phy_free(struct b43_wldev *dev); + +/** + * b43_phy_init - Initialise the PHY + */ +int b43_phy_init(struct b43_wldev *dev); + +/** + * b43_phy_exit - Cleanup PHY + */ +void b43_phy_exit(struct b43_wldev *dev); + +/** + * b43_has_hardware_pctl - Hardware Power Control supported? + * Returns a boolean, whether hardware power control is supported. + */ +bool b43_has_hardware_pctl(struct b43_wldev *dev); + +/** + * b43_phy_read - 16bit PHY register read access + */ +u16 b43_phy_read(struct b43_wldev *dev, u16 reg); + +/** + * b43_phy_write - 16bit PHY register write access + */ +void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value); + +/** + * b43_phy_copy - copy contents of 16bit PHY register to another + */ +void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg); + +/** + * b43_phy_mask - Mask a PHY register with a mask + */ +void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask); + +/** + * b43_phy_set - OR a PHY register with a bitmap + */ +void b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set); + +/** + * b43_phy_maskset - Mask and OR a PHY register with a mask and bitmap + */ +void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set); + +/** + * b43_radio_read - 16bit Radio register read access + */ +u16 b43_radio_read(struct b43_wldev *dev, u16 reg); +#define b43_radio_read16 b43_radio_read /* DEPRECATED */ + +/** + * b43_radio_write - 16bit Radio register write access + */ +void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value); +#define b43_radio_write16 b43_radio_write /* DEPRECATED */ + +/** + * b43_radio_mask - Mask a 16bit radio register with a mask + */ +void b43_radio_mask(struct b43_wldev *dev, u16 offset, u16 mask); + +/** + * b43_radio_set - OR a 16bit radio register with a bitmap + */ +void b43_radio_set(struct b43_wldev *dev, u16 offset, u16 set); + +/** + * b43_radio_maskset - Mask and OR a radio register with a mask and bitmap + */ +void b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set); + +/** + * b43_radio_wait_value - Waits for a given value in masked register read + */ +bool b43_radio_wait_value(struct b43_wldev *dev, u16 offset, u16 mask, + u16 value, int delay, int timeout); + +/** + * b43_radio_lock - Lock firmware radio register access + */ +void b43_radio_lock(struct b43_wldev *dev); + +/** + * b43_radio_unlock - Unlock firmware radio register access + */ +void b43_radio_unlock(struct b43_wldev *dev); + +/** + * b43_phy_lock - Lock firmware PHY register access + */ +void b43_phy_lock(struct b43_wldev *dev); + +/** + * b43_phy_unlock - Unlock firmware PHY register access + */ +void b43_phy_unlock(struct b43_wldev *dev); + +void b43_phy_put_into_reset(struct b43_wldev *dev); +void b43_phy_take_out_of_reset(struct b43_wldev *dev); + +/** + * b43_switch_channel - Switch to another channel + */ +int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel); + +/** + * b43_software_rfkill - Turn the radio ON or OFF in software. + */ +void b43_software_rfkill(struct b43_wldev *dev, bool blocked); + +/** + * b43_phy_txpower_check - Check TX power output. + * + * Compare the current TX power output to the desired power emission + * and schedule an adjustment in case it mismatches. + * + * @flags: OR'ed enum b43_phy_txpower_check_flags flags. + * See the docs below. + */ +void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags); +/** + * enum b43_phy_txpower_check_flags - Flags for b43_phy_txpower_check() + * + * @B43_TXPWR_IGNORE_TIME: Ignore the schedule time and force-redo + * the check now. + * @B43_TXPWR_IGNORE_TSSI: Redo the recalculation, even if the average + * TSSI did not change. + */ +enum b43_phy_txpower_check_flags { + B43_TXPWR_IGNORE_TIME = (1 << 0), + B43_TXPWR_IGNORE_TSSI = (1 << 1), +}; + +struct work_struct; +void b43_phy_txpower_adjust_work(struct work_struct *work); + +/** + * b43_phy_shm_tssi_read - Read the average of the last 4 TSSI from SHM. + * + * @shm_offset: The SHM address to read the values from. + * + * Returns the average of the 4 TSSI values, or a negative error code. + */ +int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset); + +/** + * b43_phy_switch_analog_generic - Generic PHY operation for switching the Analog. + * + * It does the switching based on the PHY0 core register. + * Do _not_ call this directly. Only use it as a switch_analog callback + * for struct b43_phy_operations. + */ +void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on); + +bool b43_is_40mhz(struct b43_wldev *dev); + +void b43_phy_force_clock(struct b43_wldev *dev, bool force); + +struct b43_c32 b43_cordic(int theta); + +#endif /* LINUX_B43_PHY_COMMON_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/phy_g.c b/drivers/net/wireless/broadcom/b43/phy_g.c new file mode 100644 index 000000000000..462310e6e88f --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_g.c @@ -0,0 +1,3055 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11g PHY driver + + Copyright (c) 2005 Martin Langer , + Copyright (c) 2005-2007 Stefano Brivio + Copyright (c) 2005-2008 Michael Buesch + Copyright (c) 2005, 2006 Danny van Dyk + Copyright (c) 2005, 2006 Andreas Jaggi + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "phy_g.h" +#include "phy_common.h" +#include "lo.h" +#include "main.h" + +#include +#include + + +static const s8 b43_tssi2dbm_g_table[] = { + 77, 77, 77, 76, + 76, 76, 75, 75, + 74, 74, 73, 73, + 73, 72, 72, 71, + 71, 70, 70, 69, + 68, 68, 67, 67, + 66, 65, 65, 64, + 63, 63, 62, 61, + 60, 59, 58, 57, + 56, 55, 54, 53, + 52, 50, 49, 47, + 45, 43, 40, 37, + 33, 28, 22, 14, + 5, -7, -20, -20, + -20, -20, -20, -20, + -20, -20, -20, -20, +}; + +static const u8 b43_radio_channel_codes_bg[] = { + 12, 17, 22, 27, + 32, 37, 42, 47, + 52, 57, 62, 67, + 72, 84, +}; + + +static void b43_calc_nrssi_threshold(struct b43_wldev *dev); + + +#define bitrev4(tmp) (bitrev8(tmp) >> 4) + + +/* Get the freq, as it has to be written to the device. */ +static inline u16 channel2freq_bg(u8 channel) +{ + B43_WARN_ON(!(channel >= 1 && channel <= 14)); + + return b43_radio_channel_codes_bg[channel - 1]; +} + +static void generate_rfatt_list(struct b43_wldev *dev, + struct b43_rfatt_list *list) +{ + struct b43_phy *phy = &dev->phy; + + /* APHY.rev < 5 || GPHY.rev < 6 */ + static const struct b43_rfatt rfatt_0[] = { + {.att = 3,.with_padmix = 0,}, + {.att = 1,.with_padmix = 0,}, + {.att = 5,.with_padmix = 0,}, + {.att = 7,.with_padmix = 0,}, + {.att = 9,.with_padmix = 0,}, + {.att = 2,.with_padmix = 0,}, + {.att = 0,.with_padmix = 0,}, + {.att = 4,.with_padmix = 0,}, + {.att = 6,.with_padmix = 0,}, + {.att = 8,.with_padmix = 0,}, + {.att = 1,.with_padmix = 1,}, + {.att = 2,.with_padmix = 1,}, + {.att = 3,.with_padmix = 1,}, + {.att = 4,.with_padmix = 1,}, + }; + /* Radio.rev == 8 && Radio.version == 0x2050 */ + static const struct b43_rfatt rfatt_1[] = { + {.att = 2,.with_padmix = 1,}, + {.att = 4,.with_padmix = 1,}, + {.att = 6,.with_padmix = 1,}, + {.att = 8,.with_padmix = 1,}, + {.att = 10,.with_padmix = 1,}, + {.att = 12,.with_padmix = 1,}, + {.att = 14,.with_padmix = 1,}, + }; + /* Otherwise */ + static const struct b43_rfatt rfatt_2[] = { + {.att = 0,.with_padmix = 1,}, + {.att = 2,.with_padmix = 1,}, + {.att = 4,.with_padmix = 1,}, + {.att = 6,.with_padmix = 1,}, + {.att = 8,.with_padmix = 1,}, + {.att = 9,.with_padmix = 1,}, + {.att = 9,.with_padmix = 1,}, + }; + + if (!b43_has_hardware_pctl(dev)) { + /* Software pctl */ + list->list = rfatt_0; + list->len = ARRAY_SIZE(rfatt_0); + list->min_val = 0; + list->max_val = 9; + return; + } + if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { + /* Hardware pctl */ + list->list = rfatt_1; + list->len = ARRAY_SIZE(rfatt_1); + list->min_val = 0; + list->max_val = 14; + return; + } + /* Hardware pctl */ + list->list = rfatt_2; + list->len = ARRAY_SIZE(rfatt_2); + list->min_val = 0; + list->max_val = 9; +} + +static void generate_bbatt_list(struct b43_wldev *dev, + struct b43_bbatt_list *list) +{ + static const struct b43_bbatt bbatt_0[] = { + {.att = 0,}, + {.att = 1,}, + {.att = 2,}, + {.att = 3,}, + {.att = 4,}, + {.att = 5,}, + {.att = 6,}, + {.att = 7,}, + {.att = 8,}, + }; + + list->list = bbatt_0; + list->len = ARRAY_SIZE(bbatt_0); + list->min_val = 0; + list->max_val = 8; +} + +static void b43_shm_clear_tssi(struct b43_wldev *dev) +{ + b43_shm_write16(dev, B43_SHM_SHARED, 0x0058, 0x7F7F); + b43_shm_write16(dev, B43_SHM_SHARED, 0x005a, 0x7F7F); + b43_shm_write16(dev, B43_SHM_SHARED, 0x0070, 0x7F7F); + b43_shm_write16(dev, B43_SHM_SHARED, 0x0072, 0x7F7F); +} + +/* Synthetic PU workaround */ +static void b43_synth_pu_workaround(struct b43_wldev *dev, u8 channel) +{ + struct b43_phy *phy = &dev->phy; + + might_sleep(); + + if (phy->radio_ver != 0x2050 || phy->radio_rev >= 6) { + /* We do not need the workaround. */ + return; + } + + if (channel <= 10) { + b43_write16(dev, B43_MMIO_CHANNEL, + channel2freq_bg(channel + 4)); + } else { + b43_write16(dev, B43_MMIO_CHANNEL, channel2freq_bg(1)); + } + msleep(1); + b43_write16(dev, B43_MMIO_CHANNEL, channel2freq_bg(channel)); +} + +/* Set the baseband attenuation value on chip. */ +void b43_gphy_set_baseband_attenuation(struct b43_wldev *dev, + u16 baseband_attenuation) +{ + struct b43_phy *phy = &dev->phy; + + if (phy->analog == 0) { + b43_write16(dev, B43_MMIO_PHY0, (b43_read16(dev, B43_MMIO_PHY0) + & 0xFFF0) | + baseband_attenuation); + } else if (phy->analog > 1) { + b43_phy_maskset(dev, B43_PHY_DACCTL, 0xFFC3, (baseband_attenuation << 2)); + } else { + b43_phy_maskset(dev, B43_PHY_DACCTL, 0xFF87, (baseband_attenuation << 3)); + } +} + +/* Adjust the transmission power output (G-PHY) */ +static void b43_set_txpower_g(struct b43_wldev *dev, + const struct b43_bbatt *bbatt, + const struct b43_rfatt *rfatt, u8 tx_control) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_txpower_lo_control *lo = gphy->lo_control; + u16 bb, rf; + u16 tx_bias, tx_magn; + + bb = bbatt->att; + rf = rfatt->att; + tx_bias = lo->tx_bias; + tx_magn = lo->tx_magn; + if (unlikely(tx_bias == 0xFF)) + tx_bias = 0; + + /* Save the values for later. Use memmove, because it's valid + * to pass &gphy->rfatt as rfatt pointer argument. Same for bbatt. */ + gphy->tx_control = tx_control; + memmove(&gphy->rfatt, rfatt, sizeof(*rfatt)); + gphy->rfatt.with_padmix = !!(tx_control & B43_TXCTL_TXMIX); + memmove(&gphy->bbatt, bbatt, sizeof(*bbatt)); + + if (b43_debug(dev, B43_DBG_XMITPOWER)) { + b43dbg(dev->wl, "Tuning TX-power to bbatt(%u), " + "rfatt(%u), tx_control(0x%02X), " + "tx_bias(0x%02X), tx_magn(0x%02X)\n", + bb, rf, tx_control, tx_bias, tx_magn); + } + + b43_gphy_set_baseband_attenuation(dev, bb); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_RFATT, rf); + if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { + b43_radio_write16(dev, 0x43, + (rf & 0x000F) | (tx_control & 0x0070)); + } else { + b43_radio_maskset(dev, 0x43, 0xFFF0, (rf & 0x000F)); + b43_radio_maskset(dev, 0x52, ~0x0070, (tx_control & 0x0070)); + } + if (has_tx_magnification(phy)) { + b43_radio_write16(dev, 0x52, tx_magn | tx_bias); + } else { + b43_radio_maskset(dev, 0x52, 0xFFF0, (tx_bias & 0x000F)); + } + b43_lo_g_adjust(dev); +} + +/* GPHY_TSSI_Power_Lookup_Table_Init */ +static void b43_gphy_tssi_power_lt_init(struct b43_wldev *dev) +{ + struct b43_phy_g *gphy = dev->phy.g; + int i; + u16 value; + + for (i = 0; i < 32; i++) + b43_ofdmtab_write16(dev, 0x3C20, i, gphy->tssi2dbm[i]); + for (i = 32; i < 64; i++) + b43_ofdmtab_write16(dev, 0x3C00, i - 32, gphy->tssi2dbm[i]); + for (i = 0; i < 64; i += 2) { + value = (u16) gphy->tssi2dbm[i]; + value |= ((u16) gphy->tssi2dbm[i + 1]) << 8; + b43_phy_write(dev, 0x380 + (i / 2), value); + } +} + +/* GPHY_Gain_Lookup_Table_Init */ +static void b43_gphy_gain_lt_init(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_txpower_lo_control *lo = gphy->lo_control; + u16 nr_written = 0; + u16 tmp; + u8 rf, bb; + + for (rf = 0; rf < lo->rfatt_list.len; rf++) { + for (bb = 0; bb < lo->bbatt_list.len; bb++) { + if (nr_written >= 0x40) + return; + tmp = lo->bbatt_list.list[bb].att; + tmp <<= 8; + if (phy->radio_rev == 8) + tmp |= 0x50; + else + tmp |= 0x40; + tmp |= lo->rfatt_list.list[rf].att; + b43_phy_write(dev, 0x3C0 + nr_written, tmp); + nr_written++; + } + } +} + +static void b43_set_all_gains(struct b43_wldev *dev, + s16 first, s16 second, s16 third) +{ + struct b43_phy *phy = &dev->phy; + u16 i; + u16 start = 0x08, end = 0x18; + u16 tmp; + u16 table; + + if (phy->rev <= 1) { + start = 0x10; + end = 0x20; + } + + table = B43_OFDMTAB_GAINX; + if (phy->rev <= 1) + table = B43_OFDMTAB_GAINX_R1; + for (i = 0; i < 4; i++) + b43_ofdmtab_write16(dev, table, i, first); + + for (i = start; i < end; i++) + b43_ofdmtab_write16(dev, table, i, second); + + if (third != -1) { + tmp = ((u16) third << 14) | ((u16) third << 6); + b43_phy_maskset(dev, 0x04A0, 0xBFBF, tmp); + b43_phy_maskset(dev, 0x04A1, 0xBFBF, tmp); + b43_phy_maskset(dev, 0x04A2, 0xBFBF, tmp); + } + b43_dummy_transmission(dev, false, true); +} + +static void b43_set_original_gains(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + u16 i, tmp; + u16 table; + u16 start = 0x0008, end = 0x0018; + + if (phy->rev <= 1) { + start = 0x0010; + end = 0x0020; + } + + table = B43_OFDMTAB_GAINX; + if (phy->rev <= 1) + table = B43_OFDMTAB_GAINX_R1; + for (i = 0; i < 4; i++) { + tmp = (i & 0xFFFC); + tmp |= (i & 0x0001) << 1; + tmp |= (i & 0x0002) >> 1; + + b43_ofdmtab_write16(dev, table, i, tmp); + } + + for (i = start; i < end; i++) + b43_ofdmtab_write16(dev, table, i, i - start); + + b43_phy_maskset(dev, 0x04A0, 0xBFBF, 0x4040); + b43_phy_maskset(dev, 0x04A1, 0xBFBF, 0x4040); + b43_phy_maskset(dev, 0x04A2, 0xBFBF, 0x4000); + b43_dummy_transmission(dev, false, true); +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +static void b43_nrssi_hw_write(struct b43_wldev *dev, u16 offset, s16 val) +{ + b43_phy_write(dev, B43_PHY_NRSSILT_CTRL, offset); + b43_phy_write(dev, B43_PHY_NRSSILT_DATA, (u16) val); +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +static s16 b43_nrssi_hw_read(struct b43_wldev *dev, u16 offset) +{ + u16 val; + + b43_phy_write(dev, B43_PHY_NRSSILT_CTRL, offset); + val = b43_phy_read(dev, B43_PHY_NRSSILT_DATA); + + return (s16) val; +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +static void b43_nrssi_hw_update(struct b43_wldev *dev, u16 val) +{ + u16 i; + s16 tmp; + + for (i = 0; i < 64; i++) { + tmp = b43_nrssi_hw_read(dev, i); + tmp -= val; + tmp = clamp_val(tmp, -32, 31); + b43_nrssi_hw_write(dev, i, tmp); + } +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +static void b43_nrssi_mem_update(struct b43_wldev *dev) +{ + struct b43_phy_g *gphy = dev->phy.g; + s16 i, delta; + s32 tmp; + + delta = 0x1F - gphy->nrssi[0]; + for (i = 0; i < 64; i++) { + tmp = (i - delta) * gphy->nrssislope; + tmp /= 0x10000; + tmp += 0x3A; + tmp = clamp_val(tmp, 0, 0x3F); + gphy->nrssi_lt[i] = tmp; + } +} + +static void b43_calc_nrssi_offset(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + u16 backup[20] = { 0 }; + s16 v47F; + u16 i; + u16 saved = 0xFFFF; + + backup[0] = b43_phy_read(dev, 0x0001); + backup[1] = b43_phy_read(dev, 0x0811); + backup[2] = b43_phy_read(dev, 0x0812); + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + backup[3] = b43_phy_read(dev, 0x0814); + backup[4] = b43_phy_read(dev, 0x0815); + } + backup[5] = b43_phy_read(dev, 0x005A); + backup[6] = b43_phy_read(dev, 0x0059); + backup[7] = b43_phy_read(dev, 0x0058); + backup[8] = b43_phy_read(dev, 0x000A); + backup[9] = b43_phy_read(dev, 0x0003); + backup[10] = b43_radio_read16(dev, 0x007A); + backup[11] = b43_radio_read16(dev, 0x0043); + + b43_phy_mask(dev, 0x0429, 0x7FFF); + b43_phy_maskset(dev, 0x0001, 0x3FFF, 0x4000); + b43_phy_set(dev, 0x0811, 0x000C); + b43_phy_maskset(dev, 0x0812, 0xFFF3, 0x0004); + b43_phy_mask(dev, 0x0802, ~(0x1 | 0x2)); + if (phy->rev >= 6) { + backup[12] = b43_phy_read(dev, 0x002E); + backup[13] = b43_phy_read(dev, 0x002F); + backup[14] = b43_phy_read(dev, 0x080F); + backup[15] = b43_phy_read(dev, 0x0810); + backup[16] = b43_phy_read(dev, 0x0801); + backup[17] = b43_phy_read(dev, 0x0060); + backup[18] = b43_phy_read(dev, 0x0014); + backup[19] = b43_phy_read(dev, 0x0478); + + b43_phy_write(dev, 0x002E, 0); + b43_phy_write(dev, 0x002F, 0); + b43_phy_write(dev, 0x080F, 0); + b43_phy_write(dev, 0x0810, 0); + b43_phy_set(dev, 0x0478, 0x0100); + b43_phy_set(dev, 0x0801, 0x0040); + b43_phy_set(dev, 0x0060, 0x0040); + b43_phy_set(dev, 0x0014, 0x0200); + } + b43_radio_set(dev, 0x007A, 0x0070); + b43_radio_set(dev, 0x007A, 0x0080); + udelay(30); + + v47F = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F == 31) { + for (i = 7; i >= 4; i--) { + b43_radio_write16(dev, 0x007B, i); + udelay(20); + v47F = + (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F < 31 && saved == 0xFFFF) + saved = i; + } + if (saved == 0xFFFF) + saved = 4; + } else { + b43_radio_mask(dev, 0x007A, 0x007F); + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + b43_phy_set(dev, 0x0814, 0x0001); + b43_phy_mask(dev, 0x0815, 0xFFFE); + } + b43_phy_set(dev, 0x0811, 0x000C); + b43_phy_set(dev, 0x0812, 0x000C); + b43_phy_set(dev, 0x0811, 0x0030); + b43_phy_set(dev, 0x0812, 0x0030); + b43_phy_write(dev, 0x005A, 0x0480); + b43_phy_write(dev, 0x0059, 0x0810); + b43_phy_write(dev, 0x0058, 0x000D); + if (phy->rev == 0) { + b43_phy_write(dev, 0x0003, 0x0122); + } else { + b43_phy_set(dev, 0x000A, 0x2000); + } + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + b43_phy_set(dev, 0x0814, 0x0004); + b43_phy_mask(dev, 0x0815, 0xFFFB); + } + b43_phy_maskset(dev, 0x0003, 0xFF9F, 0x0040); + b43_radio_set(dev, 0x007A, 0x000F); + b43_set_all_gains(dev, 3, 0, 1); + b43_radio_maskset(dev, 0x0043, 0x00F0, 0x000F); + udelay(30); + v47F = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F == -32) { + for (i = 0; i < 4; i++) { + b43_radio_write16(dev, 0x007B, i); + udelay(20); + v47F = + (s16) ((b43_phy_read(dev, 0x047F) >> 8) & + 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F > -31 && saved == 0xFFFF) + saved = i; + } + if (saved == 0xFFFF) + saved = 3; + } else + saved = 0; + } + b43_radio_write16(dev, 0x007B, saved); + + if (phy->rev >= 6) { + b43_phy_write(dev, 0x002E, backup[12]); + b43_phy_write(dev, 0x002F, backup[13]); + b43_phy_write(dev, 0x080F, backup[14]); + b43_phy_write(dev, 0x0810, backup[15]); + } + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + b43_phy_write(dev, 0x0814, backup[3]); + b43_phy_write(dev, 0x0815, backup[4]); + } + b43_phy_write(dev, 0x005A, backup[5]); + b43_phy_write(dev, 0x0059, backup[6]); + b43_phy_write(dev, 0x0058, backup[7]); + b43_phy_write(dev, 0x000A, backup[8]); + b43_phy_write(dev, 0x0003, backup[9]); + b43_radio_write16(dev, 0x0043, backup[11]); + b43_radio_write16(dev, 0x007A, backup[10]); + b43_phy_write(dev, 0x0802, b43_phy_read(dev, 0x0802) | 0x1 | 0x2); + b43_phy_set(dev, 0x0429, 0x8000); + b43_set_original_gains(dev); + if (phy->rev >= 6) { + b43_phy_write(dev, 0x0801, backup[16]); + b43_phy_write(dev, 0x0060, backup[17]); + b43_phy_write(dev, 0x0014, backup[18]); + b43_phy_write(dev, 0x0478, backup[19]); + } + b43_phy_write(dev, 0x0001, backup[0]); + b43_phy_write(dev, 0x0812, backup[2]); + b43_phy_write(dev, 0x0811, backup[1]); +} + +static void b43_calc_nrssi_slope(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + u16 backup[18] = { 0 }; + u16 tmp; + s16 nrssi0, nrssi1; + + B43_WARN_ON(phy->type != B43_PHYTYPE_G); + + if (phy->radio_rev >= 9) + return; + if (phy->radio_rev == 8) + b43_calc_nrssi_offset(dev); + + b43_phy_mask(dev, B43_PHY_G_CRS, 0x7FFF); + b43_phy_mask(dev, 0x0802, 0xFFFC); + backup[7] = b43_read16(dev, 0x03E2); + b43_write16(dev, 0x03E2, b43_read16(dev, 0x03E2) | 0x8000); + backup[0] = b43_radio_read16(dev, 0x007A); + backup[1] = b43_radio_read16(dev, 0x0052); + backup[2] = b43_radio_read16(dev, 0x0043); + backup[3] = b43_phy_read(dev, 0x0015); + backup[4] = b43_phy_read(dev, 0x005A); + backup[5] = b43_phy_read(dev, 0x0059); + backup[6] = b43_phy_read(dev, 0x0058); + backup[8] = b43_read16(dev, 0x03E6); + backup[9] = b43_read16(dev, B43_MMIO_CHANNEL_EXT); + if (phy->rev >= 3) { + backup[10] = b43_phy_read(dev, 0x002E); + backup[11] = b43_phy_read(dev, 0x002F); + backup[12] = b43_phy_read(dev, 0x080F); + backup[13] = b43_phy_read(dev, B43_PHY_G_LO_CONTROL); + backup[14] = b43_phy_read(dev, 0x0801); + backup[15] = b43_phy_read(dev, 0x0060); + backup[16] = b43_phy_read(dev, 0x0014); + backup[17] = b43_phy_read(dev, 0x0478); + b43_phy_write(dev, 0x002E, 0); + b43_phy_write(dev, B43_PHY_G_LO_CONTROL, 0); + switch (phy->rev) { + case 4: + case 6: + case 7: + b43_phy_set(dev, 0x0478, 0x0100); + b43_phy_set(dev, 0x0801, 0x0040); + break; + case 3: + case 5: + b43_phy_mask(dev, 0x0801, 0xFFBF); + break; + } + b43_phy_set(dev, 0x0060, 0x0040); + b43_phy_set(dev, 0x0014, 0x0200); + } + b43_radio_set(dev, 0x007A, 0x0070); + b43_set_all_gains(dev, 0, 8, 0); + b43_radio_mask(dev, 0x007A, 0x00F7); + if (phy->rev >= 2) { + b43_phy_maskset(dev, 0x0811, 0xFFCF, 0x0030); + b43_phy_maskset(dev, 0x0812, 0xFFCF, 0x0010); + } + b43_radio_set(dev, 0x007A, 0x0080); + udelay(20); + + nrssi0 = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (nrssi0 >= 0x0020) + nrssi0 -= 0x0040; + + b43_radio_mask(dev, 0x007A, 0x007F); + if (phy->rev >= 2) { + b43_phy_maskset(dev, 0x0003, 0xFF9F, 0x0040); + } + + b43_write16(dev, B43_MMIO_CHANNEL_EXT, + b43_read16(dev, B43_MMIO_CHANNEL_EXT) + | 0x2000); + b43_radio_set(dev, 0x007A, 0x000F); + b43_phy_write(dev, 0x0015, 0xF330); + if (phy->rev >= 2) { + b43_phy_maskset(dev, 0x0812, 0xFFCF, 0x0020); + b43_phy_maskset(dev, 0x0811, 0xFFCF, 0x0020); + } + + b43_set_all_gains(dev, 3, 0, 1); + if (phy->radio_rev == 8) { + b43_radio_write16(dev, 0x0043, 0x001F); + } else { + tmp = b43_radio_read16(dev, 0x0052) & 0xFF0F; + b43_radio_write16(dev, 0x0052, tmp | 0x0060); + tmp = b43_radio_read16(dev, 0x0043) & 0xFFF0; + b43_radio_write16(dev, 0x0043, tmp | 0x0009); + } + b43_phy_write(dev, 0x005A, 0x0480); + b43_phy_write(dev, 0x0059, 0x0810); + b43_phy_write(dev, 0x0058, 0x000D); + udelay(20); + nrssi1 = (s16) ((b43_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (nrssi1 >= 0x0020) + nrssi1 -= 0x0040; + if (nrssi0 == nrssi1) + gphy->nrssislope = 0x00010000; + else + gphy->nrssislope = 0x00400000 / (nrssi0 - nrssi1); + if (nrssi0 >= -4) { + gphy->nrssi[0] = nrssi1; + gphy->nrssi[1] = nrssi0; + } + if (phy->rev >= 3) { + b43_phy_write(dev, 0x002E, backup[10]); + b43_phy_write(dev, 0x002F, backup[11]); + b43_phy_write(dev, 0x080F, backup[12]); + b43_phy_write(dev, B43_PHY_G_LO_CONTROL, backup[13]); + } + if (phy->rev >= 2) { + b43_phy_mask(dev, 0x0812, 0xFFCF); + b43_phy_mask(dev, 0x0811, 0xFFCF); + } + + b43_radio_write16(dev, 0x007A, backup[0]); + b43_radio_write16(dev, 0x0052, backup[1]); + b43_radio_write16(dev, 0x0043, backup[2]); + b43_write16(dev, 0x03E2, backup[7]); + b43_write16(dev, 0x03E6, backup[8]); + b43_write16(dev, B43_MMIO_CHANNEL_EXT, backup[9]); + b43_phy_write(dev, 0x0015, backup[3]); + b43_phy_write(dev, 0x005A, backup[4]); + b43_phy_write(dev, 0x0059, backup[5]); + b43_phy_write(dev, 0x0058, backup[6]); + b43_synth_pu_workaround(dev, phy->channel); + b43_phy_set(dev, 0x0802, (0x0001 | 0x0002)); + b43_set_original_gains(dev); + b43_phy_set(dev, B43_PHY_G_CRS, 0x8000); + if (phy->rev >= 3) { + b43_phy_write(dev, 0x0801, backup[14]); + b43_phy_write(dev, 0x0060, backup[15]); + b43_phy_write(dev, 0x0014, backup[16]); + b43_phy_write(dev, 0x0478, backup[17]); + } + b43_nrssi_mem_update(dev); + b43_calc_nrssi_threshold(dev); +} + +static void b43_calc_nrssi_threshold(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + s32 a, b; + s16 tmp16; + u16 tmp_u16; + + B43_WARN_ON(phy->type != B43_PHYTYPE_G); + + if (!phy->gmode || + !(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI)) { + tmp16 = b43_nrssi_hw_read(dev, 0x20); + if (tmp16 >= 0x20) + tmp16 -= 0x40; + if (tmp16 < 3) { + b43_phy_maskset(dev, 0x048A, 0xF000, 0x09EB); + } else { + b43_phy_maskset(dev, 0x048A, 0xF000, 0x0AED); + } + } else { + if (gphy->interfmode == B43_INTERFMODE_NONWLAN) { + a = 0xE; + b = 0xA; + } else if (!gphy->aci_wlan_automatic && gphy->aci_enable) { + a = 0x13; + b = 0x12; + } else { + a = 0xE; + b = 0x11; + } + + a = a * (gphy->nrssi[1] - gphy->nrssi[0]); + a += (gphy->nrssi[0] << 6); + if (a < 32) + a += 31; + else + a += 32; + a = a >> 6; + a = clamp_val(a, -31, 31); + + b = b * (gphy->nrssi[1] - gphy->nrssi[0]); + b += (gphy->nrssi[0] << 6); + if (b < 32) + b += 31; + else + b += 32; + b = b >> 6; + b = clamp_val(b, -31, 31); + + tmp_u16 = b43_phy_read(dev, 0x048A) & 0xF000; + tmp_u16 |= ((u32) b & 0x0000003F); + tmp_u16 |= (((u32) a & 0x0000003F) << 6); + b43_phy_write(dev, 0x048A, tmp_u16); + } +} + +/* Stack implementation to save/restore values from the + * interference mitigation code. + * It is save to restore values in random order. + */ +static void _stack_save(u32 *_stackptr, size_t *stackidx, + u8 id, u16 offset, u16 value) +{ + u32 *stackptr = &(_stackptr[*stackidx]); + + B43_WARN_ON(offset & 0xF000); + B43_WARN_ON(id & 0xF0); + *stackptr = offset; + *stackptr |= ((u32) id) << 12; + *stackptr |= ((u32) value) << 16; + (*stackidx)++; + B43_WARN_ON(*stackidx >= B43_INTERFSTACK_SIZE); +} + +static u16 _stack_restore(u32 *stackptr, u8 id, u16 offset) +{ + size_t i; + + B43_WARN_ON(offset & 0xF000); + B43_WARN_ON(id & 0xF0); + for (i = 0; i < B43_INTERFSTACK_SIZE; i++, stackptr++) { + if ((*stackptr & 0x00000FFF) != offset) + continue; + if (((*stackptr & 0x0000F000) >> 12) != id) + continue; + return ((*stackptr & 0xFFFF0000) >> 16); + } + B43_WARN_ON(1); + + return 0; +} + +#define phy_stacksave(offset) \ + do { \ + _stack_save(stack, &stackidx, 0x1, (offset), \ + b43_phy_read(dev, (offset))); \ + } while (0) +#define phy_stackrestore(offset) \ + do { \ + b43_phy_write(dev, (offset), \ + _stack_restore(stack, 0x1, \ + (offset))); \ + } while (0) +#define radio_stacksave(offset) \ + do { \ + _stack_save(stack, &stackidx, 0x2, (offset), \ + b43_radio_read16(dev, (offset))); \ + } while (0) +#define radio_stackrestore(offset) \ + do { \ + b43_radio_write16(dev, (offset), \ + _stack_restore(stack, 0x2, \ + (offset))); \ + } while (0) +#define ofdmtab_stacksave(table, offset) \ + do { \ + _stack_save(stack, &stackidx, 0x3, (offset)|(table), \ + b43_ofdmtab_read16(dev, (table), (offset))); \ + } while (0) +#define ofdmtab_stackrestore(table, offset) \ + do { \ + b43_ofdmtab_write16(dev, (table), (offset), \ + _stack_restore(stack, 0x3, \ + (offset)|(table))); \ + } while (0) + +static void +b43_radio_interference_mitigation_enable(struct b43_wldev *dev, int mode) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + u16 tmp, flipped; + size_t stackidx = 0; + u32 *stack = gphy->interfstack; + + switch (mode) { + case B43_INTERFMODE_NONWLAN: + if (phy->rev != 1) { + b43_phy_set(dev, 0x042B, 0x0800); + b43_phy_mask(dev, B43_PHY_G_CRS, ~0x4000); + break; + } + radio_stacksave(0x0078); + tmp = (b43_radio_read16(dev, 0x0078) & 0x001E); + B43_WARN_ON(tmp > 15); + flipped = bitrev4(tmp); + if (flipped < 10 && flipped >= 8) + flipped = 7; + else if (flipped >= 10) + flipped -= 3; + flipped = (bitrev4(flipped) << 1) | 0x0020; + b43_radio_write16(dev, 0x0078, flipped); + + b43_calc_nrssi_threshold(dev); + + phy_stacksave(0x0406); + b43_phy_write(dev, 0x0406, 0x7E28); + + b43_phy_set(dev, 0x042B, 0x0800); + b43_phy_set(dev, B43_PHY_RADIO_BITFIELD, 0x1000); + + phy_stacksave(0x04A0); + b43_phy_maskset(dev, 0x04A0, 0xC0C0, 0x0008); + phy_stacksave(0x04A1); + b43_phy_maskset(dev, 0x04A1, 0xC0C0, 0x0605); + phy_stacksave(0x04A2); + b43_phy_maskset(dev, 0x04A2, 0xC0C0, 0x0204); + phy_stacksave(0x04A8); + b43_phy_maskset(dev, 0x04A8, 0xC0C0, 0x0803); + phy_stacksave(0x04AB); + b43_phy_maskset(dev, 0x04AB, 0xC0C0, 0x0605); + + phy_stacksave(0x04A7); + b43_phy_write(dev, 0x04A7, 0x0002); + phy_stacksave(0x04A3); + b43_phy_write(dev, 0x04A3, 0x287A); + phy_stacksave(0x04A9); + b43_phy_write(dev, 0x04A9, 0x2027); + phy_stacksave(0x0493); + b43_phy_write(dev, 0x0493, 0x32F5); + phy_stacksave(0x04AA); + b43_phy_write(dev, 0x04AA, 0x2027); + phy_stacksave(0x04AC); + b43_phy_write(dev, 0x04AC, 0x32F5); + break; + case B43_INTERFMODE_MANUALWLAN: + if (b43_phy_read(dev, 0x0033) & 0x0800) + break; + + gphy->aci_enable = true; + + phy_stacksave(B43_PHY_RADIO_BITFIELD); + phy_stacksave(B43_PHY_G_CRS); + if (phy->rev < 2) { + phy_stacksave(0x0406); + } else { + phy_stacksave(0x04C0); + phy_stacksave(0x04C1); + } + phy_stacksave(0x0033); + phy_stacksave(0x04A7); + phy_stacksave(0x04A3); + phy_stacksave(0x04A9); + phy_stacksave(0x04AA); + phy_stacksave(0x04AC); + phy_stacksave(0x0493); + phy_stacksave(0x04A1); + phy_stacksave(0x04A0); + phy_stacksave(0x04A2); + phy_stacksave(0x048A); + phy_stacksave(0x04A8); + phy_stacksave(0x04AB); + if (phy->rev == 2) { + phy_stacksave(0x04AD); + phy_stacksave(0x04AE); + } else if (phy->rev >= 3) { + phy_stacksave(0x04AD); + phy_stacksave(0x0415); + phy_stacksave(0x0416); + phy_stacksave(0x0417); + ofdmtab_stacksave(0x1A00, 0x2); + ofdmtab_stacksave(0x1A00, 0x3); + } + phy_stacksave(0x042B); + phy_stacksave(0x048C); + + b43_phy_mask(dev, B43_PHY_RADIO_BITFIELD, ~0x1000); + b43_phy_maskset(dev, B43_PHY_G_CRS, 0xFFFC, 0x0002); + + b43_phy_write(dev, 0x0033, 0x0800); + b43_phy_write(dev, 0x04A3, 0x2027); + b43_phy_write(dev, 0x04A9, 0x1CA8); + b43_phy_write(dev, 0x0493, 0x287A); + b43_phy_write(dev, 0x04AA, 0x1CA8); + b43_phy_write(dev, 0x04AC, 0x287A); + + b43_phy_maskset(dev, 0x04A0, 0xFFC0, 0x001A); + b43_phy_write(dev, 0x04A7, 0x000D); + + if (phy->rev < 2) { + b43_phy_write(dev, 0x0406, 0xFF0D); + } else if (phy->rev == 2) { + b43_phy_write(dev, 0x04C0, 0xFFFF); + b43_phy_write(dev, 0x04C1, 0x00A9); + } else { + b43_phy_write(dev, 0x04C0, 0x00C1); + b43_phy_write(dev, 0x04C1, 0x0059); + } + + b43_phy_maskset(dev, 0x04A1, 0xC0FF, 0x1800); + b43_phy_maskset(dev, 0x04A1, 0xFFC0, 0x0015); + b43_phy_maskset(dev, 0x04A8, 0xCFFF, 0x1000); + b43_phy_maskset(dev, 0x04A8, 0xF0FF, 0x0A00); + b43_phy_maskset(dev, 0x04AB, 0xCFFF, 0x1000); + b43_phy_maskset(dev, 0x04AB, 0xF0FF, 0x0800); + b43_phy_maskset(dev, 0x04AB, 0xFFCF, 0x0010); + b43_phy_maskset(dev, 0x04AB, 0xFFF0, 0x0005); + b43_phy_maskset(dev, 0x04A8, 0xFFCF, 0x0010); + b43_phy_maskset(dev, 0x04A8, 0xFFF0, 0x0006); + b43_phy_maskset(dev, 0x04A2, 0xF0FF, 0x0800); + b43_phy_maskset(dev, 0x04A0, 0xF0FF, 0x0500); + b43_phy_maskset(dev, 0x04A2, 0xFFF0, 0x000B); + + if (phy->rev >= 3) { + b43_phy_mask(dev, 0x048A, 0x7FFF); + b43_phy_maskset(dev, 0x0415, 0x8000, 0x36D8); + b43_phy_maskset(dev, 0x0416, 0x8000, 0x36D8); + b43_phy_maskset(dev, 0x0417, 0xFE00, 0x016D); + } else { + b43_phy_set(dev, 0x048A, 0x1000); + b43_phy_maskset(dev, 0x048A, 0x9FFF, 0x2000); + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ACIW); + } + if (phy->rev >= 2) { + b43_phy_set(dev, 0x042B, 0x0800); + } + b43_phy_maskset(dev, 0x048C, 0xF0FF, 0x0200); + if (phy->rev == 2) { + b43_phy_maskset(dev, 0x04AE, 0xFF00, 0x007F); + b43_phy_maskset(dev, 0x04AD, 0x00FF, 0x1300); + } else if (phy->rev >= 6) { + b43_ofdmtab_write16(dev, 0x1A00, 0x3, 0x007F); + b43_ofdmtab_write16(dev, 0x1A00, 0x2, 0x007F); + b43_phy_mask(dev, 0x04AD, 0x00FF); + } + b43_calc_nrssi_slope(dev); + break; + default: + B43_WARN_ON(1); + } +} + +static void +b43_radio_interference_mitigation_disable(struct b43_wldev *dev, int mode) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + u32 *stack = gphy->interfstack; + + switch (mode) { + case B43_INTERFMODE_NONWLAN: + if (phy->rev != 1) { + b43_phy_mask(dev, 0x042B, ~0x0800); + b43_phy_set(dev, B43_PHY_G_CRS, 0x4000); + break; + } + radio_stackrestore(0x0078); + b43_calc_nrssi_threshold(dev); + phy_stackrestore(0x0406); + b43_phy_mask(dev, 0x042B, ~0x0800); + if (!dev->bad_frames_preempt) { + b43_phy_mask(dev, B43_PHY_RADIO_BITFIELD, ~(1 << 11)); + } + b43_phy_set(dev, B43_PHY_G_CRS, 0x4000); + phy_stackrestore(0x04A0); + phy_stackrestore(0x04A1); + phy_stackrestore(0x04A2); + phy_stackrestore(0x04A8); + phy_stackrestore(0x04AB); + phy_stackrestore(0x04A7); + phy_stackrestore(0x04A3); + phy_stackrestore(0x04A9); + phy_stackrestore(0x0493); + phy_stackrestore(0x04AA); + phy_stackrestore(0x04AC); + break; + case B43_INTERFMODE_MANUALWLAN: + if (!(b43_phy_read(dev, 0x0033) & 0x0800)) + break; + + gphy->aci_enable = false; + + phy_stackrestore(B43_PHY_RADIO_BITFIELD); + phy_stackrestore(B43_PHY_G_CRS); + phy_stackrestore(0x0033); + phy_stackrestore(0x04A3); + phy_stackrestore(0x04A9); + phy_stackrestore(0x0493); + phy_stackrestore(0x04AA); + phy_stackrestore(0x04AC); + phy_stackrestore(0x04A0); + phy_stackrestore(0x04A7); + if (phy->rev >= 2) { + phy_stackrestore(0x04C0); + phy_stackrestore(0x04C1); + } else + phy_stackrestore(0x0406); + phy_stackrestore(0x04A1); + phy_stackrestore(0x04AB); + phy_stackrestore(0x04A8); + if (phy->rev == 2) { + phy_stackrestore(0x04AD); + phy_stackrestore(0x04AE); + } else if (phy->rev >= 3) { + phy_stackrestore(0x04AD); + phy_stackrestore(0x0415); + phy_stackrestore(0x0416); + phy_stackrestore(0x0417); + ofdmtab_stackrestore(0x1A00, 0x2); + ofdmtab_stackrestore(0x1A00, 0x3); + } + phy_stackrestore(0x04A2); + phy_stackrestore(0x048A); + phy_stackrestore(0x042B); + phy_stackrestore(0x048C); + b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ACIW); + b43_calc_nrssi_slope(dev); + break; + default: + B43_WARN_ON(1); + } +} + +#undef phy_stacksave +#undef phy_stackrestore +#undef radio_stacksave +#undef radio_stackrestore +#undef ofdmtab_stacksave +#undef ofdmtab_stackrestore + +static u16 b43_radio_core_calibration_value(struct b43_wldev *dev) +{ + u16 reg, index, ret; + + static const u8 rcc_table[] = { + 0x02, 0x03, 0x01, 0x0F, + 0x06, 0x07, 0x05, 0x0F, + 0x0A, 0x0B, 0x09, 0x0F, + 0x0E, 0x0F, 0x0D, 0x0F, + }; + + reg = b43_radio_read16(dev, 0x60); + index = (reg & 0x001E) >> 1; + ret = rcc_table[index] << 1; + ret |= (reg & 0x0001); + ret |= 0x0020; + + return ret; +} + +#define LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0)) +static u16 radio2050_rfover_val(struct b43_wldev *dev, + u16 phy_register, unsigned int lpd) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct ssb_sprom *sprom = dev->dev->bus_sprom; + + if (!phy->gmode) + return 0; + + if (has_loopback_gain(phy)) { + int max_lb_gain = gphy->max_lb_gain; + u16 extlna; + u16 i; + + if (phy->radio_rev == 8) + max_lb_gain += 0x3E; + else + max_lb_gain += 0x26; + if (max_lb_gain >= 0x46) { + extlna = 0x3000; + max_lb_gain -= 0x46; + } else if (max_lb_gain >= 0x3A) { + extlna = 0x1000; + max_lb_gain -= 0x3A; + } else if (max_lb_gain >= 0x2E) { + extlna = 0x2000; + max_lb_gain -= 0x2E; + } else { + extlna = 0; + max_lb_gain -= 0x10; + } + + for (i = 0; i < 16; i++) { + max_lb_gain -= (i * 6); + if (max_lb_gain < 6) + break; + } + + if ((phy->rev < 7) || + !(sprom->boardflags_lo & B43_BFL_EXTLNA)) { + if (phy_register == B43_PHY_RFOVER) { + return 0x1B3; + } else if (phy_register == B43_PHY_RFOVERVAL) { + extlna |= (i << 8); + switch (lpd) { + case LPD(0, 1, 1): + return 0x0F92; + case LPD(0, 0, 1): + case LPD(1, 0, 1): + return (0x0092 | extlna); + case LPD(1, 0, 0): + return (0x0093 | extlna); + } + B43_WARN_ON(1); + } + B43_WARN_ON(1); + } else { + if (phy_register == B43_PHY_RFOVER) { + return 0x9B3; + } else if (phy_register == B43_PHY_RFOVERVAL) { + if (extlna) + extlna |= 0x8000; + extlna |= (i << 8); + switch (lpd) { + case LPD(0, 1, 1): + return 0x8F92; + case LPD(0, 0, 1): + return (0x8092 | extlna); + case LPD(1, 0, 1): + return (0x2092 | extlna); + case LPD(1, 0, 0): + return (0x2093 | extlna); + } + B43_WARN_ON(1); + } + B43_WARN_ON(1); + } + } else { + if ((phy->rev < 7) || + !(sprom->boardflags_lo & B43_BFL_EXTLNA)) { + if (phy_register == B43_PHY_RFOVER) { + return 0x1B3; + } else if (phy_register == B43_PHY_RFOVERVAL) { + switch (lpd) { + case LPD(0, 1, 1): + return 0x0FB2; + case LPD(0, 0, 1): + return 0x00B2; + case LPD(1, 0, 1): + return 0x30B2; + case LPD(1, 0, 0): + return 0x30B3; + } + B43_WARN_ON(1); + } + B43_WARN_ON(1); + } else { + if (phy_register == B43_PHY_RFOVER) { + return 0x9B3; + } else if (phy_register == B43_PHY_RFOVERVAL) { + switch (lpd) { + case LPD(0, 1, 1): + return 0x8FB2; + case LPD(0, 0, 1): + return 0x80B2; + case LPD(1, 0, 1): + return 0x20B2; + case LPD(1, 0, 0): + return 0x20B3; + } + B43_WARN_ON(1); + } + B43_WARN_ON(1); + } + } + return 0; +} + +struct init2050_saved_values { + /* Core registers */ + u16 reg_3EC; + u16 reg_3E6; + u16 reg_3F4; + /* Radio registers */ + u16 radio_43; + u16 radio_51; + u16 radio_52; + /* PHY registers */ + u16 phy_pgactl; + u16 phy_cck_5A; + u16 phy_cck_59; + u16 phy_cck_58; + u16 phy_cck_30; + u16 phy_rfover; + u16 phy_rfoverval; + u16 phy_analogover; + u16 phy_analogoverval; + u16 phy_crs0; + u16 phy_classctl; + u16 phy_lo_mask; + u16 phy_lo_ctl; + u16 phy_syncctl; +}; + +static u16 b43_radio_init2050(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct init2050_saved_values sav; + u16 rcc; + u16 radio78; + u16 ret; + u16 i, j; + u32 tmp1 = 0, tmp2 = 0; + + memset(&sav, 0, sizeof(sav)); /* get rid of "may be used uninitialized..." */ + + sav.radio_43 = b43_radio_read16(dev, 0x43); + sav.radio_51 = b43_radio_read16(dev, 0x51); + sav.radio_52 = b43_radio_read16(dev, 0x52); + sav.phy_pgactl = b43_phy_read(dev, B43_PHY_PGACTL); + sav.phy_cck_5A = b43_phy_read(dev, B43_PHY_CCK(0x5A)); + sav.phy_cck_59 = b43_phy_read(dev, B43_PHY_CCK(0x59)); + sav.phy_cck_58 = b43_phy_read(dev, B43_PHY_CCK(0x58)); + + if (phy->type == B43_PHYTYPE_B) { + sav.phy_cck_30 = b43_phy_read(dev, B43_PHY_CCK(0x30)); + sav.reg_3EC = b43_read16(dev, 0x3EC); + + b43_phy_write(dev, B43_PHY_CCK(0x30), 0xFF); + b43_write16(dev, 0x3EC, 0x3F3F); + } else if (phy->gmode || phy->rev >= 2) { + sav.phy_rfover = b43_phy_read(dev, B43_PHY_RFOVER); + sav.phy_rfoverval = b43_phy_read(dev, B43_PHY_RFOVERVAL); + sav.phy_analogover = b43_phy_read(dev, B43_PHY_ANALOGOVER); + sav.phy_analogoverval = + b43_phy_read(dev, B43_PHY_ANALOGOVERVAL); + sav.phy_crs0 = b43_phy_read(dev, B43_PHY_CRS0); + sav.phy_classctl = b43_phy_read(dev, B43_PHY_CLASSCTL); + + b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0003); + b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFC); + b43_phy_mask(dev, B43_PHY_CRS0, 0x7FFF); + b43_phy_mask(dev, B43_PHY_CLASSCTL, 0xFFFC); + if (has_loopback_gain(phy)) { + sav.phy_lo_mask = b43_phy_read(dev, B43_PHY_LO_MASK); + sav.phy_lo_ctl = b43_phy_read(dev, B43_PHY_LO_CTL); + + if (phy->rev >= 3) + b43_phy_write(dev, B43_PHY_LO_MASK, 0xC020); + else + b43_phy_write(dev, B43_PHY_LO_MASK, 0x8020); + b43_phy_write(dev, B43_PHY_LO_CTL, 0); + } + + b43_phy_write(dev, B43_PHY_RFOVERVAL, + radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, + LPD(0, 1, 1))); + b43_phy_write(dev, B43_PHY_RFOVER, + radio2050_rfover_val(dev, B43_PHY_RFOVER, 0)); + } + b43_write16(dev, 0x3E2, b43_read16(dev, 0x3E2) | 0x8000); + + sav.phy_syncctl = b43_phy_read(dev, B43_PHY_SYNCCTL); + b43_phy_mask(dev, B43_PHY_SYNCCTL, 0xFF7F); + sav.reg_3E6 = b43_read16(dev, 0x3E6); + sav.reg_3F4 = b43_read16(dev, 0x3F4); + + if (phy->analog == 0) { + b43_write16(dev, 0x03E6, 0x0122); + } else { + if (phy->analog >= 2) { + b43_phy_maskset(dev, B43_PHY_CCK(0x03), 0xFFBF, 0x40); + } + b43_write16(dev, B43_MMIO_CHANNEL_EXT, + (b43_read16(dev, B43_MMIO_CHANNEL_EXT) | 0x2000)); + } + + rcc = b43_radio_core_calibration_value(dev); + + if (phy->type == B43_PHYTYPE_B) + b43_radio_write16(dev, 0x78, 0x26); + if (phy->gmode || phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_RFOVERVAL, + radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, + LPD(0, 1, 1))); + } + b43_phy_write(dev, B43_PHY_PGACTL, 0xBFAF); + b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x1403); + if (phy->gmode || phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_RFOVERVAL, + radio2050_rfover_val(dev, B43_PHY_RFOVERVAL, + LPD(0, 0, 1))); + } + b43_phy_write(dev, B43_PHY_PGACTL, 0xBFA0); + b43_radio_set(dev, 0x51, 0x0004); + if (phy->radio_rev == 8) { + b43_radio_write16(dev, 0x43, 0x1F); + } else { + b43_radio_write16(dev, 0x52, 0); + b43_radio_maskset(dev, 0x43, 0xFFF0, 0x0009); + } + b43_phy_write(dev, B43_PHY_CCK(0x58), 0); + + for (i = 0; i < 16; i++) { + b43_phy_write(dev, B43_PHY_CCK(0x5A), 0x0480); + b43_phy_write(dev, B43_PHY_CCK(0x59), 0xC810); + b43_phy_write(dev, B43_PHY_CCK(0x58), 0x000D); + if (phy->gmode || phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_RFOVERVAL, + radio2050_rfover_val(dev, + B43_PHY_RFOVERVAL, + LPD(1, 0, 1))); + } + b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); + udelay(10); + if (phy->gmode || phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_RFOVERVAL, + radio2050_rfover_val(dev, + B43_PHY_RFOVERVAL, + LPD(1, 0, 1))); + } + b43_phy_write(dev, B43_PHY_PGACTL, 0xEFB0); + udelay(10); + if (phy->gmode || phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_RFOVERVAL, + radio2050_rfover_val(dev, + B43_PHY_RFOVERVAL, + LPD(1, 0, 0))); + } + b43_phy_write(dev, B43_PHY_PGACTL, 0xFFF0); + udelay(20); + tmp1 += b43_phy_read(dev, B43_PHY_LO_LEAKAGE); + b43_phy_write(dev, B43_PHY_CCK(0x58), 0); + if (phy->gmode || phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_RFOVERVAL, + radio2050_rfover_val(dev, + B43_PHY_RFOVERVAL, + LPD(1, 0, 1))); + } + b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); + } + udelay(10); + + b43_phy_write(dev, B43_PHY_CCK(0x58), 0); + tmp1++; + tmp1 >>= 9; + + for (i = 0; i < 16; i++) { + radio78 = (bitrev4(i) << 1) | 0x0020; + b43_radio_write16(dev, 0x78, radio78); + udelay(10); + for (j = 0; j < 16; j++) { + b43_phy_write(dev, B43_PHY_CCK(0x5A), 0x0D80); + b43_phy_write(dev, B43_PHY_CCK(0x59), 0xC810); + b43_phy_write(dev, B43_PHY_CCK(0x58), 0x000D); + if (phy->gmode || phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_RFOVERVAL, + radio2050_rfover_val(dev, + B43_PHY_RFOVERVAL, + LPD(1, 0, + 1))); + } + b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); + udelay(10); + if (phy->gmode || phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_RFOVERVAL, + radio2050_rfover_val(dev, + B43_PHY_RFOVERVAL, + LPD(1, 0, + 1))); + } + b43_phy_write(dev, B43_PHY_PGACTL, 0xEFB0); + udelay(10); + if (phy->gmode || phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_RFOVERVAL, + radio2050_rfover_val(dev, + B43_PHY_RFOVERVAL, + LPD(1, 0, + 0))); + } + b43_phy_write(dev, B43_PHY_PGACTL, 0xFFF0); + udelay(10); + tmp2 += b43_phy_read(dev, B43_PHY_LO_LEAKAGE); + b43_phy_write(dev, B43_PHY_CCK(0x58), 0); + if (phy->gmode || phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_RFOVERVAL, + radio2050_rfover_val(dev, + B43_PHY_RFOVERVAL, + LPD(1, 0, + 1))); + } + b43_phy_write(dev, B43_PHY_PGACTL, 0xAFB0); + } + tmp2++; + tmp2 >>= 8; + if (tmp1 < tmp2) + break; + } + + /* Restore the registers */ + b43_phy_write(dev, B43_PHY_PGACTL, sav.phy_pgactl); + b43_radio_write16(dev, 0x51, sav.radio_51); + b43_radio_write16(dev, 0x52, sav.radio_52); + b43_radio_write16(dev, 0x43, sav.radio_43); + b43_phy_write(dev, B43_PHY_CCK(0x5A), sav.phy_cck_5A); + b43_phy_write(dev, B43_PHY_CCK(0x59), sav.phy_cck_59); + b43_phy_write(dev, B43_PHY_CCK(0x58), sav.phy_cck_58); + b43_write16(dev, 0x3E6, sav.reg_3E6); + if (phy->analog != 0) + b43_write16(dev, 0x3F4, sav.reg_3F4); + b43_phy_write(dev, B43_PHY_SYNCCTL, sav.phy_syncctl); + b43_synth_pu_workaround(dev, phy->channel); + if (phy->type == B43_PHYTYPE_B) { + b43_phy_write(dev, B43_PHY_CCK(0x30), sav.phy_cck_30); + b43_write16(dev, 0x3EC, sav.reg_3EC); + } else if (phy->gmode) { + b43_write16(dev, B43_MMIO_PHY_RADIO, + b43_read16(dev, B43_MMIO_PHY_RADIO) + & 0x7FFF); + b43_phy_write(dev, B43_PHY_RFOVER, sav.phy_rfover); + b43_phy_write(dev, B43_PHY_RFOVERVAL, sav.phy_rfoverval); + b43_phy_write(dev, B43_PHY_ANALOGOVER, sav.phy_analogover); + b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, + sav.phy_analogoverval); + b43_phy_write(dev, B43_PHY_CRS0, sav.phy_crs0); + b43_phy_write(dev, B43_PHY_CLASSCTL, sav.phy_classctl); + if (has_loopback_gain(phy)) { + b43_phy_write(dev, B43_PHY_LO_MASK, sav.phy_lo_mask); + b43_phy_write(dev, B43_PHY_LO_CTL, sav.phy_lo_ctl); + } + } + if (i > 15) + ret = radio78; + else + ret = rcc; + + return ret; +} + +static void b43_phy_initb5(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + u16 offset, value; + u8 old_channel; + + if (phy->analog == 1) { + b43_radio_set(dev, 0x007A, 0x0050); + } + if ((dev->dev->board_vendor != SSB_BOARDVENDOR_BCM) && + (dev->dev->board_type != SSB_BOARD_BU4306)) { + value = 0x2120; + for (offset = 0x00A8; offset < 0x00C7; offset++) { + b43_phy_write(dev, offset, value); + value += 0x202; + } + } + b43_phy_maskset(dev, 0x0035, 0xF0FF, 0x0700); + if (phy->radio_ver == 0x2050) + b43_phy_write(dev, 0x0038, 0x0667); + + if (phy->gmode || phy->rev >= 2) { + if (phy->radio_ver == 0x2050) { + b43_radio_set(dev, 0x007A, 0x0020); + b43_radio_set(dev, 0x0051, 0x0004); + } + b43_write16(dev, B43_MMIO_PHY_RADIO, 0x0000); + + b43_phy_set(dev, 0x0802, 0x0100); + b43_phy_set(dev, 0x042B, 0x2000); + + b43_phy_write(dev, 0x001C, 0x186A); + + b43_phy_maskset(dev, 0x0013, 0x00FF, 0x1900); + b43_phy_maskset(dev, 0x0035, 0xFFC0, 0x0064); + b43_phy_maskset(dev, 0x005D, 0xFF80, 0x000A); + } + + if (dev->bad_frames_preempt) { + b43_phy_set(dev, B43_PHY_RADIO_BITFIELD, (1 << 11)); + } + + if (phy->analog == 1) { + b43_phy_write(dev, 0x0026, 0xCE00); + b43_phy_write(dev, 0x0021, 0x3763); + b43_phy_write(dev, 0x0022, 0x1BC3); + b43_phy_write(dev, 0x0023, 0x06F9); + b43_phy_write(dev, 0x0024, 0x037E); + } else + b43_phy_write(dev, 0x0026, 0xCC00); + b43_phy_write(dev, 0x0030, 0x00C6); + b43_write16(dev, 0x03EC, 0x3F22); + + if (phy->analog == 1) + b43_phy_write(dev, 0x0020, 0x3E1C); + else + b43_phy_write(dev, 0x0020, 0x301C); + + if (phy->analog == 0) + b43_write16(dev, 0x03E4, 0x3000); + + old_channel = phy->channel; + /* Force to channel 7, even if not supported. */ + b43_gphy_channel_switch(dev, 7, 0); + + if (phy->radio_ver != 0x2050) { + b43_radio_write16(dev, 0x0075, 0x0080); + b43_radio_write16(dev, 0x0079, 0x0081); + } + + b43_radio_write16(dev, 0x0050, 0x0020); + b43_radio_write16(dev, 0x0050, 0x0023); + + if (phy->radio_ver == 0x2050) { + b43_radio_write16(dev, 0x0050, 0x0020); + b43_radio_write16(dev, 0x005A, 0x0070); + } + + b43_radio_write16(dev, 0x005B, 0x007B); + b43_radio_write16(dev, 0x005C, 0x00B0); + + b43_radio_set(dev, 0x007A, 0x0007); + + b43_gphy_channel_switch(dev, old_channel, 0); + + b43_phy_write(dev, 0x0014, 0x0080); + b43_phy_write(dev, 0x0032, 0x00CA); + b43_phy_write(dev, 0x002A, 0x88A3); + + b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, gphy->tx_control); + + if (phy->radio_ver == 0x2050) + b43_radio_write16(dev, 0x005D, 0x000D); + + b43_write16(dev, 0x03E4, (b43_read16(dev, 0x03E4) & 0xFFC0) | 0x0004); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/Init/B6 */ +static void b43_phy_initb6(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + u16 offset, val; + u8 old_channel; + + b43_phy_write(dev, 0x003E, 0x817A); + b43_radio_write16(dev, 0x007A, + (b43_radio_read16(dev, 0x007A) | 0x0058)); + if (phy->radio_rev == 4 || phy->radio_rev == 5) { + b43_radio_write16(dev, 0x51, 0x37); + b43_radio_write16(dev, 0x52, 0x70); + b43_radio_write16(dev, 0x53, 0xB3); + b43_radio_write16(dev, 0x54, 0x9B); + b43_radio_write16(dev, 0x5A, 0x88); + b43_radio_write16(dev, 0x5B, 0x88); + b43_radio_write16(dev, 0x5D, 0x88); + b43_radio_write16(dev, 0x5E, 0x88); + b43_radio_write16(dev, 0x7D, 0x88); + b43_hf_write(dev, b43_hf_read(dev) + | B43_HF_TSSIRPSMW); + } + B43_WARN_ON(phy->radio_rev == 6 || phy->radio_rev == 7); /* We had code for these revs here... */ + if (phy->radio_rev == 8) { + b43_radio_write16(dev, 0x51, 0); + b43_radio_write16(dev, 0x52, 0x40); + b43_radio_write16(dev, 0x53, 0xB7); + b43_radio_write16(dev, 0x54, 0x98); + b43_radio_write16(dev, 0x5A, 0x88); + b43_radio_write16(dev, 0x5B, 0x6B); + b43_radio_write16(dev, 0x5C, 0x0F); + if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_ALTIQ) { + b43_radio_write16(dev, 0x5D, 0xFA); + b43_radio_write16(dev, 0x5E, 0xD8); + } else { + b43_radio_write16(dev, 0x5D, 0xF5); + b43_radio_write16(dev, 0x5E, 0xB8); + } + b43_radio_write16(dev, 0x0073, 0x0003); + b43_radio_write16(dev, 0x007D, 0x00A8); + b43_radio_write16(dev, 0x007C, 0x0001); + b43_radio_write16(dev, 0x007E, 0x0008); + } + val = 0x1E1F; + for (offset = 0x0088; offset < 0x0098; offset++) { + b43_phy_write(dev, offset, val); + val -= 0x0202; + } + val = 0x3E3F; + for (offset = 0x0098; offset < 0x00A8; offset++) { + b43_phy_write(dev, offset, val); + val -= 0x0202; + } + val = 0x2120; + for (offset = 0x00A8; offset < 0x00C8; offset++) { + b43_phy_write(dev, offset, (val & 0x3F3F)); + val += 0x0202; + } + if (phy->type == B43_PHYTYPE_G) { + b43_radio_set(dev, 0x007A, 0x0020); + b43_radio_set(dev, 0x0051, 0x0004); + b43_phy_set(dev, 0x0802, 0x0100); + b43_phy_set(dev, 0x042B, 0x2000); + b43_phy_write(dev, 0x5B, 0); + b43_phy_write(dev, 0x5C, 0); + } + + old_channel = phy->channel; + if (old_channel >= 8) + b43_gphy_channel_switch(dev, 1, 0); + else + b43_gphy_channel_switch(dev, 13, 0); + + b43_radio_write16(dev, 0x0050, 0x0020); + b43_radio_write16(dev, 0x0050, 0x0023); + udelay(40); + if (phy->radio_rev < 6 || phy->radio_rev == 8) { + b43_radio_write16(dev, 0x7C, (b43_radio_read16(dev, 0x7C) + | 0x0002)); + b43_radio_write16(dev, 0x50, 0x20); + } + if (phy->radio_rev <= 2) { + b43_radio_write16(dev, 0x50, 0x20); + b43_radio_write16(dev, 0x5A, 0x70); + b43_radio_write16(dev, 0x5B, 0x7B); + b43_radio_write16(dev, 0x5C, 0xB0); + } + b43_radio_maskset(dev, 0x007A, 0x00F8, 0x0007); + + b43_gphy_channel_switch(dev, old_channel, 0); + + b43_phy_write(dev, 0x0014, 0x0200); + if (phy->radio_rev >= 6) + b43_phy_write(dev, 0x2A, 0x88C2); + else + b43_phy_write(dev, 0x2A, 0x8AC0); + b43_phy_write(dev, 0x0038, 0x0668); + b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, gphy->tx_control); + if (phy->radio_rev == 4 || phy->radio_rev == 5) + b43_phy_maskset(dev, 0x5D, 0xFF80, 0x0003); + if (phy->radio_rev <= 2) + b43_radio_write16(dev, 0x005D, 0x000D); + + if (phy->analog == 4) { + b43_write16(dev, 0x3E4, 9); + b43_phy_mask(dev, 0x61, 0x0FFF); + } else { + b43_phy_maskset(dev, 0x0002, 0xFFC0, 0x0004); + } + if (phy->type == B43_PHYTYPE_B) + B43_WARN_ON(1); + else if (phy->type == B43_PHYTYPE_G) + b43_write16(dev, 0x03E6, 0x0); +} + +static void b43_calc_loopback_gain(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + u16 backup_phy[16] = { 0 }; + u16 backup_radio[3]; + u16 backup_bband; + u16 i, j, loop_i_max; + u16 trsw_rx; + u16 loop1_outer_done, loop1_inner_done; + + backup_phy[0] = b43_phy_read(dev, B43_PHY_CRS0); + backup_phy[1] = b43_phy_read(dev, B43_PHY_CCKBBANDCFG); + backup_phy[2] = b43_phy_read(dev, B43_PHY_RFOVER); + backup_phy[3] = b43_phy_read(dev, B43_PHY_RFOVERVAL); + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + backup_phy[4] = b43_phy_read(dev, B43_PHY_ANALOGOVER); + backup_phy[5] = b43_phy_read(dev, B43_PHY_ANALOGOVERVAL); + } + backup_phy[6] = b43_phy_read(dev, B43_PHY_CCK(0x5A)); + backup_phy[7] = b43_phy_read(dev, B43_PHY_CCK(0x59)); + backup_phy[8] = b43_phy_read(dev, B43_PHY_CCK(0x58)); + backup_phy[9] = b43_phy_read(dev, B43_PHY_CCK(0x0A)); + backup_phy[10] = b43_phy_read(dev, B43_PHY_CCK(0x03)); + backup_phy[11] = b43_phy_read(dev, B43_PHY_LO_MASK); + backup_phy[12] = b43_phy_read(dev, B43_PHY_LO_CTL); + backup_phy[13] = b43_phy_read(dev, B43_PHY_CCK(0x2B)); + backup_phy[14] = b43_phy_read(dev, B43_PHY_PGACTL); + backup_phy[15] = b43_phy_read(dev, B43_PHY_LO_LEAKAGE); + backup_bband = gphy->bbatt.att; + backup_radio[0] = b43_radio_read16(dev, 0x52); + backup_radio[1] = b43_radio_read16(dev, 0x43); + backup_radio[2] = b43_radio_read16(dev, 0x7A); + + b43_phy_mask(dev, B43_PHY_CRS0, 0x3FFF); + b43_phy_set(dev, B43_PHY_CCKBBANDCFG, 0x8000); + b43_phy_set(dev, B43_PHY_RFOVER, 0x0002); + b43_phy_mask(dev, B43_PHY_RFOVERVAL, 0xFFFD); + b43_phy_set(dev, B43_PHY_RFOVER, 0x0001); + b43_phy_mask(dev, B43_PHY_RFOVERVAL, 0xFFFE); + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0001); + b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFE); + b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0002); + b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFD); + } + b43_phy_set(dev, B43_PHY_RFOVER, 0x000C); + b43_phy_set(dev, B43_PHY_RFOVERVAL, 0x000C); + b43_phy_set(dev, B43_PHY_RFOVER, 0x0030); + b43_phy_maskset(dev, B43_PHY_RFOVERVAL, 0xFFCF, 0x10); + + b43_phy_write(dev, B43_PHY_CCK(0x5A), 0x0780); + b43_phy_write(dev, B43_PHY_CCK(0x59), 0xC810); + b43_phy_write(dev, B43_PHY_CCK(0x58), 0x000D); + + b43_phy_set(dev, B43_PHY_CCK(0x0A), 0x2000); + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0004); + b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFB); + } + b43_phy_maskset(dev, B43_PHY_CCK(0x03), 0xFF9F, 0x40); + + if (phy->radio_rev == 8) { + b43_radio_write16(dev, 0x43, 0x000F); + } else { + b43_radio_write16(dev, 0x52, 0); + b43_radio_maskset(dev, 0x43, 0xFFF0, 0x9); + } + b43_gphy_set_baseband_attenuation(dev, 11); + + if (phy->rev >= 3) + b43_phy_write(dev, B43_PHY_LO_MASK, 0xC020); + else + b43_phy_write(dev, B43_PHY_LO_MASK, 0x8020); + b43_phy_write(dev, B43_PHY_LO_CTL, 0); + + b43_phy_maskset(dev, B43_PHY_CCK(0x2B), 0xFFC0, 0x01); + b43_phy_maskset(dev, B43_PHY_CCK(0x2B), 0xC0FF, 0x800); + + b43_phy_set(dev, B43_PHY_RFOVER, 0x0100); + b43_phy_mask(dev, B43_PHY_RFOVERVAL, 0xCFFF); + + if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_EXTLNA) { + if (phy->rev >= 7) { + b43_phy_set(dev, B43_PHY_RFOVER, 0x0800); + b43_phy_set(dev, B43_PHY_RFOVERVAL, 0x8000); + } + } + b43_radio_mask(dev, 0x7A, 0x00F7); + + j = 0; + loop_i_max = (phy->radio_rev == 8) ? 15 : 9; + for (i = 0; i < loop_i_max; i++) { + for (j = 0; j < 16; j++) { + b43_radio_write16(dev, 0x43, i); + b43_phy_maskset(dev, B43_PHY_RFOVERVAL, 0xF0FF, (j << 8)); + b43_phy_maskset(dev, B43_PHY_PGACTL, 0x0FFF, 0xA000); + b43_phy_set(dev, B43_PHY_PGACTL, 0xF000); + udelay(20); + if (b43_phy_read(dev, B43_PHY_LO_LEAKAGE) >= 0xDFC) + goto exit_loop1; + } + } + exit_loop1: + loop1_outer_done = i; + loop1_inner_done = j; + if (j >= 8) { + b43_phy_set(dev, B43_PHY_RFOVERVAL, 0x30); + trsw_rx = 0x1B; + for (j = j - 8; j < 16; j++) { + b43_phy_maskset(dev, B43_PHY_RFOVERVAL, 0xF0FF, (j << 8)); + b43_phy_maskset(dev, B43_PHY_PGACTL, 0x0FFF, 0xA000); + b43_phy_set(dev, B43_PHY_PGACTL, 0xF000); + udelay(20); + trsw_rx -= 3; + if (b43_phy_read(dev, B43_PHY_LO_LEAKAGE) >= 0xDFC) + goto exit_loop2; + } + } else + trsw_rx = 0x18; + exit_loop2: + + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + b43_phy_write(dev, B43_PHY_ANALOGOVER, backup_phy[4]); + b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, backup_phy[5]); + } + b43_phy_write(dev, B43_PHY_CCK(0x5A), backup_phy[6]); + b43_phy_write(dev, B43_PHY_CCK(0x59), backup_phy[7]); + b43_phy_write(dev, B43_PHY_CCK(0x58), backup_phy[8]); + b43_phy_write(dev, B43_PHY_CCK(0x0A), backup_phy[9]); + b43_phy_write(dev, B43_PHY_CCK(0x03), backup_phy[10]); + b43_phy_write(dev, B43_PHY_LO_MASK, backup_phy[11]); + b43_phy_write(dev, B43_PHY_LO_CTL, backup_phy[12]); + b43_phy_write(dev, B43_PHY_CCK(0x2B), backup_phy[13]); + b43_phy_write(dev, B43_PHY_PGACTL, backup_phy[14]); + + b43_gphy_set_baseband_attenuation(dev, backup_bband); + + b43_radio_write16(dev, 0x52, backup_radio[0]); + b43_radio_write16(dev, 0x43, backup_radio[1]); + b43_radio_write16(dev, 0x7A, backup_radio[2]); + + b43_phy_write(dev, B43_PHY_RFOVER, backup_phy[2] | 0x0003); + udelay(10); + b43_phy_write(dev, B43_PHY_RFOVER, backup_phy[2]); + b43_phy_write(dev, B43_PHY_RFOVERVAL, backup_phy[3]); + b43_phy_write(dev, B43_PHY_CRS0, backup_phy[0]); + b43_phy_write(dev, B43_PHY_CCKBBANDCFG, backup_phy[1]); + + gphy->max_lb_gain = + ((loop1_inner_done * 6) - (loop1_outer_done * 4)) - 11; + gphy->trsw_rx_gain = trsw_rx * 2; +} + +static void b43_hardware_pctl_early_init(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + if (!b43_has_hardware_pctl(dev)) { + b43_phy_write(dev, 0x047A, 0xC111); + return; + } + + b43_phy_mask(dev, 0x0036, 0xFEFF); + b43_phy_write(dev, 0x002F, 0x0202); + b43_phy_set(dev, 0x047C, 0x0002); + b43_phy_set(dev, 0x047A, 0xF000); + if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { + b43_phy_maskset(dev, 0x047A, 0xFF0F, 0x0010); + b43_phy_set(dev, 0x005D, 0x8000); + b43_phy_maskset(dev, 0x004E, 0xFFC0, 0x0010); + b43_phy_write(dev, 0x002E, 0xC07F); + b43_phy_set(dev, 0x0036, 0x0400); + } else { + b43_phy_set(dev, 0x0036, 0x0200); + b43_phy_set(dev, 0x0036, 0x0400); + b43_phy_mask(dev, 0x005D, 0x7FFF); + b43_phy_mask(dev, 0x004F, 0xFFFE); + b43_phy_maskset(dev, 0x004E, 0xFFC0, 0x0010); + b43_phy_write(dev, 0x002E, 0xC07F); + b43_phy_maskset(dev, 0x047A, 0xFF0F, 0x0010); + } +} + +/* Hardware power control for G-PHY */ +static void b43_hardware_pctl_init_gphy(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + + if (!b43_has_hardware_pctl(dev)) { + /* No hardware power control */ + b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_HWPCTL); + return; + } + + b43_phy_maskset(dev, 0x0036, 0xFFC0, (gphy->tgt_idle_tssi - gphy->cur_idle_tssi)); + b43_phy_maskset(dev, 0x0478, 0xFF00, (gphy->tgt_idle_tssi - gphy->cur_idle_tssi)); + b43_gphy_tssi_power_lt_init(dev); + b43_gphy_gain_lt_init(dev); + b43_phy_mask(dev, 0x0060, 0xFFBF); + b43_phy_write(dev, 0x0014, 0x0000); + + B43_WARN_ON(phy->rev < 6); + b43_phy_set(dev, 0x0478, 0x0800); + b43_phy_mask(dev, 0x0478, 0xFEFF); + b43_phy_mask(dev, 0x0801, 0xFFBF); + + b43_gphy_dc_lt_init(dev, 1); + + /* Enable hardware pctl in firmware. */ + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_HWPCTL); +} + +/* Initialize B/G PHY power control */ +static void b43_phy_init_pctl(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_rfatt old_rfatt; + struct b43_bbatt old_bbatt; + u8 old_tx_control = 0; + + B43_WARN_ON(phy->type != B43_PHYTYPE_G); + + if ((dev->dev->board_vendor == SSB_BOARDVENDOR_BCM) && + (dev->dev->board_type == SSB_BOARD_BU4306)) + return; + + b43_phy_write(dev, 0x0028, 0x8018); + + /* This does something with the Analog... */ + b43_write16(dev, B43_MMIO_PHY0, b43_read16(dev, B43_MMIO_PHY0) + & 0xFFDF); + + if (!phy->gmode) + return; + b43_hardware_pctl_early_init(dev); + if (gphy->cur_idle_tssi == 0) { + if (phy->radio_ver == 0x2050 && phy->analog == 0) { + b43_radio_maskset(dev, 0x0076, 0x00F7, 0x0084); + } else { + struct b43_rfatt rfatt; + struct b43_bbatt bbatt; + + memcpy(&old_rfatt, &gphy->rfatt, sizeof(old_rfatt)); + memcpy(&old_bbatt, &gphy->bbatt, sizeof(old_bbatt)); + old_tx_control = gphy->tx_control; + + bbatt.att = 11; + if (phy->radio_rev == 8) { + rfatt.att = 15; + rfatt.with_padmix = true; + } else { + rfatt.att = 9; + rfatt.with_padmix = false; + } + b43_set_txpower_g(dev, &bbatt, &rfatt, 0); + } + b43_dummy_transmission(dev, false, true); + gphy->cur_idle_tssi = b43_phy_read(dev, B43_PHY_ITSSI); + if (B43_DEBUG) { + /* Current-Idle-TSSI sanity check. */ + if (abs(gphy->cur_idle_tssi - gphy->tgt_idle_tssi) >= 20) { + b43dbg(dev->wl, + "!WARNING! Idle-TSSI phy->cur_idle_tssi " + "measuring failed. (cur=%d, tgt=%d). Disabling TX power " + "adjustment.\n", gphy->cur_idle_tssi, + gphy->tgt_idle_tssi); + gphy->cur_idle_tssi = 0; + } + } + if (phy->radio_ver == 0x2050 && phy->analog == 0) { + b43_radio_mask(dev, 0x0076, 0xFF7B); + } else { + b43_set_txpower_g(dev, &old_bbatt, + &old_rfatt, old_tx_control); + } + } + b43_hardware_pctl_init_gphy(dev); + b43_shm_clear_tssi(dev); +} + +static void b43_phy_initg(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + u16 tmp; + + if (phy->rev == 1) + b43_phy_initb5(dev); + else + b43_phy_initb6(dev); + + if (phy->rev >= 2 || phy->gmode) + b43_phy_inita(dev); + + if (phy->rev >= 2) { + b43_phy_write(dev, B43_PHY_ANALOGOVER, 0); + b43_phy_write(dev, B43_PHY_ANALOGOVERVAL, 0); + } + if (phy->rev == 2) { + b43_phy_write(dev, B43_PHY_RFOVER, 0); + b43_phy_write(dev, B43_PHY_PGACTL, 0xC0); + } + if (phy->rev > 5) { + b43_phy_write(dev, B43_PHY_RFOVER, 0x400); + b43_phy_write(dev, B43_PHY_PGACTL, 0xC0); + } + if (phy->gmode || phy->rev >= 2) { + tmp = b43_phy_read(dev, B43_PHY_VERSION_OFDM); + tmp &= B43_PHYVER_VERSION; + if (tmp == 3 || tmp == 5) { + b43_phy_write(dev, B43_PHY_OFDM(0xC2), 0x1816); + b43_phy_write(dev, B43_PHY_OFDM(0xC3), 0x8006); + } + if (tmp == 5) { + b43_phy_maskset(dev, B43_PHY_OFDM(0xCC), 0x00FF, 0x1F00); + } + } + if ((phy->rev <= 2 && phy->gmode) || phy->rev >= 2) + b43_phy_write(dev, B43_PHY_OFDM(0x7E), 0x78); + if (phy->radio_rev == 8) { + b43_phy_set(dev, B43_PHY_EXTG(0x01), 0x80); + b43_phy_set(dev, B43_PHY_OFDM(0x3E), 0x4); + } + if (has_loopback_gain(phy)) + b43_calc_loopback_gain(dev); + + if (phy->radio_rev != 8) { + if (gphy->initval == 0xFFFF) + gphy->initval = b43_radio_init2050(dev); + else + b43_radio_write16(dev, 0x0078, gphy->initval); + } + b43_lo_g_init(dev); + if (has_tx_magnification(phy)) { + b43_radio_write16(dev, 0x52, + (b43_radio_read16(dev, 0x52) & 0xFF00) + | gphy->lo_control->tx_bias | gphy-> + lo_control->tx_magn); + } else { + b43_radio_maskset(dev, 0x52, 0xFFF0, gphy->lo_control->tx_bias); + } + if (phy->rev >= 6) { + b43_phy_maskset(dev, B43_PHY_CCK(0x36), 0x0FFF, (gphy->lo_control->tx_bias << 12)); + } + if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) + b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8075); + else + b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x807F); + if (phy->rev < 2) + b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x101); + else + b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x202); + if (phy->gmode || phy->rev >= 2) { + b43_lo_g_adjust(dev); + b43_phy_write(dev, B43_PHY_LO_MASK, 0x8078); + } + + if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI)) { + /* The specs state to update the NRSSI LT with + * the value 0x7FFFFFFF here. I think that is some weird + * compiler optimization in the original driver. + * Essentially, what we do here is resetting all NRSSI LT + * entries to -32 (see the clamp_val() in nrssi_hw_update()) + */ + b43_nrssi_hw_update(dev, 0xFFFF); //FIXME? + b43_calc_nrssi_threshold(dev); + } else if (phy->gmode || phy->rev >= 2) { + if (gphy->nrssi[0] == -1000) { + B43_WARN_ON(gphy->nrssi[1] != -1000); + b43_calc_nrssi_slope(dev); + } else + b43_calc_nrssi_threshold(dev); + } + if (phy->radio_rev == 8) + b43_phy_write(dev, B43_PHY_EXTG(0x05), 0x3230); + b43_phy_init_pctl(dev); + /* FIXME: The spec says in the following if, the 0 should be replaced + 'if OFDM may not be used in the current locale' + but OFDM is legal everywhere */ + if ((dev->dev->chip_id == 0x4306 + && dev->dev->chip_pkg == 2) || 0) { + b43_phy_mask(dev, B43_PHY_CRS0, 0xBFFF); + b43_phy_mask(dev, B43_PHY_OFDM(0xC3), 0x7FFF); + } +} + +void b43_gphy_channel_switch(struct b43_wldev *dev, + unsigned int channel, + bool synthetic_pu_workaround) +{ + if (synthetic_pu_workaround) + b43_synth_pu_workaround(dev, channel); + + b43_write16(dev, B43_MMIO_CHANNEL, channel2freq_bg(channel)); + + if (channel == 14) { + if (dev->dev->bus_sprom->country_code == + SSB_SPROM1CCODE_JAPAN) + b43_hf_write(dev, + b43_hf_read(dev) & ~B43_HF_ACPR); + else + b43_hf_write(dev, + b43_hf_read(dev) | B43_HF_ACPR); + b43_write16(dev, B43_MMIO_CHANNEL_EXT, + b43_read16(dev, B43_MMIO_CHANNEL_EXT) + | (1 << 11)); + } else { + b43_write16(dev, B43_MMIO_CHANNEL_EXT, + b43_read16(dev, B43_MMIO_CHANNEL_EXT) + & 0xF7BF); + } +} + +static void default_baseband_attenuation(struct b43_wldev *dev, + struct b43_bbatt *bb) +{ + struct b43_phy *phy = &dev->phy; + + if (phy->radio_ver == 0x2050 && phy->radio_rev < 6) + bb->att = 0; + else + bb->att = 2; +} + +static void default_radio_attenuation(struct b43_wldev *dev, + struct b43_rfatt *rf) +{ + struct b43_bus_dev *bdev = dev->dev; + struct b43_phy *phy = &dev->phy; + + rf->with_padmix = false; + + if (dev->dev->board_vendor == SSB_BOARDVENDOR_BCM && + dev->dev->board_type == SSB_BOARD_BCM4309G) { + if (dev->dev->board_rev < 0x43) { + rf->att = 2; + return; + } else if (dev->dev->board_rev < 0x51) { + rf->att = 3; + return; + } + } + + if (phy->type == B43_PHYTYPE_A) { + rf->att = 0x60; + return; + } + + switch (phy->radio_ver) { + case 0x2053: + switch (phy->radio_rev) { + case 1: + rf->att = 6; + return; + } + break; + case 0x2050: + switch (phy->radio_rev) { + case 0: + rf->att = 5; + return; + case 1: + if (phy->type == B43_PHYTYPE_G) { + if (bdev->board_vendor == SSB_BOARDVENDOR_BCM + && bdev->board_type == SSB_BOARD_BCM4309G + && bdev->board_rev >= 30) + rf->att = 3; + else if (bdev->board_vendor == + SSB_BOARDVENDOR_BCM + && bdev->board_type == + SSB_BOARD_BU4306) + rf->att = 3; + else + rf->att = 1; + } else { + if (bdev->board_vendor == SSB_BOARDVENDOR_BCM + && bdev->board_type == SSB_BOARD_BCM4309G + && bdev->board_rev >= 30) + rf->att = 7; + else + rf->att = 6; + } + return; + case 2: + if (phy->type == B43_PHYTYPE_G) { + if (bdev->board_vendor == SSB_BOARDVENDOR_BCM + && bdev->board_type == SSB_BOARD_BCM4309G + && bdev->board_rev >= 30) + rf->att = 3; + else if (bdev->board_vendor == + SSB_BOARDVENDOR_BCM + && bdev->board_type == + SSB_BOARD_BU4306) + rf->att = 5; + else if (bdev->chip_id == 0x4320) + rf->att = 4; + else + rf->att = 3; + } else + rf->att = 6; + return; + case 3: + rf->att = 5; + return; + case 4: + case 5: + rf->att = 1; + return; + case 6: + case 7: + rf->att = 5; + return; + case 8: + rf->att = 0xA; + rf->with_padmix = true; + return; + case 9: + default: + rf->att = 5; + return; + } + } + rf->att = 5; +} + +static u16 default_tx_control(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + if (phy->radio_ver != 0x2050) + return 0; + if (phy->radio_rev == 1) + return B43_TXCTL_PA2DB | B43_TXCTL_TXMIX; + if (phy->radio_rev < 6) + return B43_TXCTL_PA2DB; + if (phy->radio_rev == 8) + return B43_TXCTL_TXMIX; + return 0; +} + +static u8 b43_gphy_aci_detect(struct b43_wldev *dev, u8 channel) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + u8 ret = 0; + u16 saved, rssi, temp; + int i, j = 0; + + saved = b43_phy_read(dev, 0x0403); + b43_switch_channel(dev, channel); + b43_phy_write(dev, 0x0403, (saved & 0xFFF8) | 5); + if (gphy->aci_hw_rssi) + rssi = b43_phy_read(dev, 0x048A) & 0x3F; + else + rssi = saved & 0x3F; + /* clamp temp to signed 5bit */ + if (rssi > 32) + rssi -= 64; + for (i = 0; i < 100; i++) { + temp = (b43_phy_read(dev, 0x047F) >> 8) & 0x3F; + if (temp > 32) + temp -= 64; + if (temp < rssi) + j++; + if (j >= 20) + ret = 1; + } + b43_phy_write(dev, 0x0403, saved); + + return ret; +} + +static u8 b43_gphy_aci_scan(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + u8 ret[13]; + unsigned int channel = phy->channel; + unsigned int i, j, start, end; + + if (!((phy->type == B43_PHYTYPE_G) && (phy->rev > 0))) + return 0; + + b43_phy_lock(dev); + b43_radio_lock(dev); + b43_phy_mask(dev, 0x0802, 0xFFFC); + b43_phy_mask(dev, B43_PHY_G_CRS, 0x7FFF); + b43_set_all_gains(dev, 3, 8, 1); + + start = (channel - 5 > 0) ? channel - 5 : 1; + end = (channel + 5 < 14) ? channel + 5 : 13; + + for (i = start; i <= end; i++) { + if (abs(channel - i) > 2) + ret[i - 1] = b43_gphy_aci_detect(dev, i); + } + b43_switch_channel(dev, channel); + b43_phy_maskset(dev, 0x0802, 0xFFFC, 0x0003); + b43_phy_mask(dev, 0x0403, 0xFFF8); + b43_phy_set(dev, B43_PHY_G_CRS, 0x8000); + b43_set_original_gains(dev); + for (i = 0; i < 13; i++) { + if (!ret[i]) + continue; + end = (i + 5 < 13) ? i + 5 : 13; + for (j = i; j < end; j++) + ret[j] = 1; + } + b43_radio_unlock(dev); + b43_phy_unlock(dev); + + return ret[channel - 1]; +} + +static s32 b43_tssi2dbm_ad(s32 num, s32 den) +{ + if (num < 0) + return num / den; + else + return (num + den / 2) / den; +} + +static s8 b43_tssi2dbm_entry(s8 entry[], u8 index, + s16 pab0, s16 pab1, s16 pab2) +{ + s32 m1, m2, f = 256, q, delta; + s8 i = 0; + + m1 = b43_tssi2dbm_ad(16 * pab0 + index * pab1, 32); + m2 = max(b43_tssi2dbm_ad(32768 + index * pab2, 256), 1); + do { + if (i > 15) + return -EINVAL; + q = b43_tssi2dbm_ad(f * 4096 - + b43_tssi2dbm_ad(m2 * f, 16) * f, 2048); + delta = abs(q - f); + f = q; + i++; + } while (delta >= 2); + entry[index] = clamp_val(b43_tssi2dbm_ad(m1 * f, 8192), -127, 128); + return 0; +} + +u8 *b43_generate_dyn_tssi2dbm_tab(struct b43_wldev *dev, + s16 pab0, s16 pab1, s16 pab2) +{ + unsigned int i; + u8 *tab; + int err; + + tab = kmalloc(64, GFP_KERNEL); + if (!tab) { + b43err(dev->wl, "Could not allocate memory " + "for tssi2dbm table\n"); + return NULL; + } + for (i = 0; i < 64; i++) { + err = b43_tssi2dbm_entry(tab, i, pab0, pab1, pab2); + if (err) { + b43err(dev->wl, "Could not generate " + "tssi2dBm table\n"); + kfree(tab); + return NULL; + } + } + + return tab; +} + +/* Initialise the TSSI->dBm lookup table */ +static int b43_gphy_init_tssi2dbm_table(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + s16 pab0, pab1, pab2; + + pab0 = (s16) (dev->dev->bus_sprom->pa0b0); + pab1 = (s16) (dev->dev->bus_sprom->pa0b1); + pab2 = (s16) (dev->dev->bus_sprom->pa0b2); + + B43_WARN_ON((dev->dev->chip_id == 0x4301) && + (phy->radio_ver != 0x2050)); /* Not supported anymore */ + + gphy->dyn_tssi_tbl = false; + + if (pab0 != 0 && pab1 != 0 && pab2 != 0 && + pab0 != -1 && pab1 != -1 && pab2 != -1) { + /* The pabX values are set in SPROM. Use them. */ + if ((s8) dev->dev->bus_sprom->itssi_bg != 0 && + (s8) dev->dev->bus_sprom->itssi_bg != -1) { + gphy->tgt_idle_tssi = + (s8) (dev->dev->bus_sprom->itssi_bg); + } else + gphy->tgt_idle_tssi = 62; + gphy->tssi2dbm = b43_generate_dyn_tssi2dbm_tab(dev, pab0, + pab1, pab2); + if (!gphy->tssi2dbm) + return -ENOMEM; + gphy->dyn_tssi_tbl = true; + } else { + /* pabX values not set in SPROM. */ + gphy->tgt_idle_tssi = 52; + gphy->tssi2dbm = b43_tssi2dbm_g_table; + } + + return 0; +} + +static int b43_gphy_op_allocate(struct b43_wldev *dev) +{ + struct b43_phy_g *gphy; + struct b43_txpower_lo_control *lo; + int err; + + gphy = kzalloc(sizeof(*gphy), GFP_KERNEL); + if (!gphy) { + err = -ENOMEM; + goto error; + } + dev->phy.g = gphy; + + lo = kzalloc(sizeof(*lo), GFP_KERNEL); + if (!lo) { + err = -ENOMEM; + goto err_free_gphy; + } + gphy->lo_control = lo; + + err = b43_gphy_init_tssi2dbm_table(dev); + if (err) + goto err_free_lo; + + return 0; + +err_free_lo: + kfree(lo); +err_free_gphy: + kfree(gphy); +error: + return err; +} + +static void b43_gphy_op_prepare_structs(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + const void *tssi2dbm; + int tgt_idle_tssi; + struct b43_txpower_lo_control *lo; + unsigned int i; + + /* tssi2dbm table is constant, so it is initialized at alloc time. + * Save a copy of the pointer. */ + tssi2dbm = gphy->tssi2dbm; + tgt_idle_tssi = gphy->tgt_idle_tssi; + /* Save the LO pointer. */ + lo = gphy->lo_control; + + /* Zero out the whole PHY structure. */ + memset(gphy, 0, sizeof(*gphy)); + + /* Restore pointers. */ + gphy->tssi2dbm = tssi2dbm; + gphy->tgt_idle_tssi = tgt_idle_tssi; + gphy->lo_control = lo; + + memset(gphy->minlowsig, 0xFF, sizeof(gphy->minlowsig)); + + /* NRSSI */ + for (i = 0; i < ARRAY_SIZE(gphy->nrssi); i++) + gphy->nrssi[i] = -1000; + for (i = 0; i < ARRAY_SIZE(gphy->nrssi_lt); i++) + gphy->nrssi_lt[i] = i; + + gphy->lofcal = 0xFFFF; + gphy->initval = 0xFFFF; + + gphy->interfmode = B43_INTERFMODE_NONE; + + /* OFDM-table address caching. */ + gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_UNKNOWN; + + gphy->average_tssi = 0xFF; + + /* Local Osciallator structure */ + lo->tx_bias = 0xFF; + INIT_LIST_HEAD(&lo->calib_list); +} + +static void b43_gphy_op_free(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + + kfree(gphy->lo_control); + + if (gphy->dyn_tssi_tbl) + kfree(gphy->tssi2dbm); + gphy->dyn_tssi_tbl = false; + gphy->tssi2dbm = NULL; + + kfree(gphy); + dev->phy.g = NULL; +} + +static int b43_gphy_op_prepare_hardware(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + struct b43_txpower_lo_control *lo = gphy->lo_control; + + B43_WARN_ON(phy->type != B43_PHYTYPE_G); + + default_baseband_attenuation(dev, &gphy->bbatt); + default_radio_attenuation(dev, &gphy->rfatt); + gphy->tx_control = (default_tx_control(dev) << 4); + generate_rfatt_list(dev, &lo->rfatt_list); + generate_bbatt_list(dev, &lo->bbatt_list); + + /* Commit previous writes */ + b43_read32(dev, B43_MMIO_MACCTL); + + if (phy->rev == 1) { + /* Workaround: Temporarly disable gmode through the early init + * phase, as the gmode stuff is not needed for phy rev 1 */ + phy->gmode = false; + b43_wireless_core_reset(dev, 0); + b43_phy_initg(dev); + phy->gmode = true; + b43_wireless_core_reset(dev, 1); + } + + return 0; +} + +static int b43_gphy_op_init(struct b43_wldev *dev) +{ + b43_phy_initg(dev); + + return 0; +} + +static void b43_gphy_op_exit(struct b43_wldev *dev) +{ + b43_lo_g_cleanup(dev); +} + +static u16 b43_gphy_op_read(struct b43_wldev *dev, u16 reg) +{ + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + return b43_read16(dev, B43_MMIO_PHY_DATA); +} + +static void b43_gphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) +{ + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16(dev, B43_MMIO_PHY_DATA, value); +} + +static u16 b43_gphy_op_radio_read(struct b43_wldev *dev, u16 reg) +{ + /* Register 1 is a 32-bit register. */ + B43_WARN_ON(reg == 1); + /* G-PHY needs 0x80 for read access. */ + reg |= 0x80; + + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); + return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); +} + +static void b43_gphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) +{ + /* Register 1 is a 32-bit register. */ + B43_WARN_ON(reg == 1); + + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); + b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); +} + +static bool b43_gphy_op_supports_hwpctl(struct b43_wldev *dev) +{ + return (dev->phy.rev >= 6); +} + +static void b43_gphy_op_software_rfkill(struct b43_wldev *dev, + bool blocked) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + unsigned int channel; + + might_sleep(); + + if (!blocked) { + /* Turn radio ON */ + if (phy->radio_on) + return; + + b43_phy_write(dev, 0x0015, 0x8000); + b43_phy_write(dev, 0x0015, 0xCC00); + b43_phy_write(dev, 0x0015, (phy->gmode ? 0x00C0 : 0x0000)); + if (gphy->radio_off_context.valid) { + /* Restore the RFover values. */ + b43_phy_write(dev, B43_PHY_RFOVER, + gphy->radio_off_context.rfover); + b43_phy_write(dev, B43_PHY_RFOVERVAL, + gphy->radio_off_context.rfoverval); + gphy->radio_off_context.valid = false; + } + channel = phy->channel; + b43_gphy_channel_switch(dev, 6, 1); + b43_gphy_channel_switch(dev, channel, 0); + } else { + /* Turn radio OFF */ + u16 rfover, rfoverval; + + rfover = b43_phy_read(dev, B43_PHY_RFOVER); + rfoverval = b43_phy_read(dev, B43_PHY_RFOVERVAL); + gphy->radio_off_context.rfover = rfover; + gphy->radio_off_context.rfoverval = rfoverval; + gphy->radio_off_context.valid = true; + b43_phy_write(dev, B43_PHY_RFOVER, rfover | 0x008C); + b43_phy_write(dev, B43_PHY_RFOVERVAL, rfoverval & 0xFF73); + } +} + +static int b43_gphy_op_switch_channel(struct b43_wldev *dev, + unsigned int new_channel) +{ + if ((new_channel < 1) || (new_channel > 14)) + return -EINVAL; + b43_gphy_channel_switch(dev, new_channel, 0); + + return 0; +} + +static unsigned int b43_gphy_op_get_default_chan(struct b43_wldev *dev) +{ + return 1; /* Default to channel 1 */ +} + +static void b43_gphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) +{ + struct b43_phy *phy = &dev->phy; + u16 tmp; + int autodiv = 0; + + if (antenna == B43_ANTENNA_AUTO0 || antenna == B43_ANTENNA_AUTO1) + autodiv = 1; + + b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP); + + b43_phy_maskset(dev, B43_PHY_BBANDCFG, ~B43_PHY_BBANDCFG_RXANT, + (autodiv ? B43_ANTENNA_AUTO1 : antenna) << + B43_PHY_BBANDCFG_RXANT_SHIFT); + + if (autodiv) { + tmp = b43_phy_read(dev, B43_PHY_ANTDWELL); + if (antenna == B43_ANTENNA_AUTO1) + tmp &= ~B43_PHY_ANTDWELL_AUTODIV1; + else + tmp |= B43_PHY_ANTDWELL_AUTODIV1; + b43_phy_write(dev, B43_PHY_ANTDWELL, tmp); + } + + tmp = b43_phy_read(dev, B43_PHY_ANTWRSETT); + if (autodiv) + tmp |= B43_PHY_ANTWRSETT_ARXDIV; + else + tmp &= ~B43_PHY_ANTWRSETT_ARXDIV; + b43_phy_write(dev, B43_PHY_ANTWRSETT, tmp); + + if (autodiv) + b43_phy_set(dev, B43_PHY_ANTWRSETT, B43_PHY_ANTWRSETT_ARXDIV); + else { + b43_phy_mask(dev, B43_PHY_ANTWRSETT, + B43_PHY_ANTWRSETT_ARXDIV); + } + + if (phy->rev >= 2) { + b43_phy_set(dev, B43_PHY_OFDM61, B43_PHY_OFDM61_10); + b43_phy_maskset(dev, B43_PHY_DIVSRCHGAINBACK, 0xFF00, 0x15); + + if (phy->rev == 2) + b43_phy_write(dev, B43_PHY_ADIVRELATED, 8); + else + b43_phy_maskset(dev, B43_PHY_ADIVRELATED, 0xFF00, 8); + } + if (phy->rev >= 6) + b43_phy_write(dev, B43_PHY_OFDM9B, 0xDC); + + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP); +} + +static int b43_gphy_op_interf_mitigation(struct b43_wldev *dev, + enum b43_interference_mitigation mode) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + int currentmode; + + B43_WARN_ON(phy->type != B43_PHYTYPE_G); + if ((phy->rev == 0) || (!phy->gmode)) + return -ENODEV; + + gphy->aci_wlan_automatic = false; + switch (mode) { + case B43_INTERFMODE_AUTOWLAN: + gphy->aci_wlan_automatic = true; + if (gphy->aci_enable) + mode = B43_INTERFMODE_MANUALWLAN; + else + mode = B43_INTERFMODE_NONE; + break; + case B43_INTERFMODE_NONE: + case B43_INTERFMODE_NONWLAN: + case B43_INTERFMODE_MANUALWLAN: + break; + default: + return -EINVAL; + } + + currentmode = gphy->interfmode; + if (currentmode == mode) + return 0; + if (currentmode != B43_INTERFMODE_NONE) + b43_radio_interference_mitigation_disable(dev, currentmode); + + if (mode == B43_INTERFMODE_NONE) { + gphy->aci_enable = false; + gphy->aci_hw_rssi = false; + } else + b43_radio_interference_mitigation_enable(dev, mode); + gphy->interfmode = mode; + + return 0; +} + +/* http://bcm-specs.sipsolutions.net/EstimatePowerOut + * This function converts a TSSI value to dBm in Q5.2 + */ +static s8 b43_gphy_estimate_power_out(struct b43_wldev *dev, s8 tssi) +{ + struct b43_phy_g *gphy = dev->phy.g; + s8 dbm; + s32 tmp; + + tmp = (gphy->tgt_idle_tssi - gphy->cur_idle_tssi + tssi); + tmp = clamp_val(tmp, 0x00, 0x3F); + dbm = gphy->tssi2dbm[tmp]; + + return dbm; +} + +static void b43_put_attenuation_into_ranges(struct b43_wldev *dev, + int *_bbatt, int *_rfatt) +{ + int rfatt = *_rfatt; + int bbatt = *_bbatt; + struct b43_txpower_lo_control *lo = dev->phy.g->lo_control; + + /* Get baseband and radio attenuation values into their permitted ranges. + * Radio attenuation affects power level 4 times as much as baseband. */ + + /* Range constants */ + const int rf_min = lo->rfatt_list.min_val; + const int rf_max = lo->rfatt_list.max_val; + const int bb_min = lo->bbatt_list.min_val; + const int bb_max = lo->bbatt_list.max_val; + + while (1) { + if (rfatt > rf_max && bbatt > bb_max - 4) + break; /* Can not get it into ranges */ + if (rfatt < rf_min && bbatt < bb_min + 4) + break; /* Can not get it into ranges */ + if (bbatt > bb_max && rfatt > rf_max - 1) + break; /* Can not get it into ranges */ + if (bbatt < bb_min && rfatt < rf_min + 1) + break; /* Can not get it into ranges */ + + if (bbatt > bb_max) { + bbatt -= 4; + rfatt += 1; + continue; + } + if (bbatt < bb_min) { + bbatt += 4; + rfatt -= 1; + continue; + } + if (rfatt > rf_max) { + rfatt -= 1; + bbatt += 4; + continue; + } + if (rfatt < rf_min) { + rfatt += 1; + bbatt -= 4; + continue; + } + break; + } + + *_rfatt = clamp_val(rfatt, rf_min, rf_max); + *_bbatt = clamp_val(bbatt, bb_min, bb_max); +} + +static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + int rfatt, bbatt; + u8 tx_control; + + b43_mac_suspend(dev); + + /* Calculate the new attenuation values. */ + bbatt = gphy->bbatt.att; + bbatt += gphy->bbatt_delta; + rfatt = gphy->rfatt.att; + rfatt += gphy->rfatt_delta; + + b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt); + tx_control = gphy->tx_control; + if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 2)) { + if (rfatt <= 1) { + if (tx_control == 0) { + tx_control = + B43_TXCTL_PA2DB | + B43_TXCTL_TXMIX; + rfatt += 2; + bbatt += 2; + } else if (dev->dev->bus_sprom-> + boardflags_lo & + B43_BFL_PACTRL) { + bbatt += 4 * (rfatt - 2); + rfatt = 2; + } + } else if (rfatt > 4 && tx_control) { + tx_control = 0; + if (bbatt < 3) { + rfatt -= 3; + bbatt += 2; + } else { + rfatt -= 2; + bbatt -= 2; + } + } + } + /* Save the control values */ + gphy->tx_control = tx_control; + b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt); + gphy->rfatt.att = rfatt; + gphy->bbatt.att = bbatt; + + if (b43_debug(dev, B43_DBG_XMITPOWER)) + b43dbg(dev->wl, "Adjusting TX power\n"); + + /* Adjust the hardware */ + b43_phy_lock(dev); + b43_radio_lock(dev); + b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, + gphy->tx_control); + b43_radio_unlock(dev); + b43_phy_unlock(dev); + + b43_mac_enable(dev); +} + +static enum b43_txpwr_result b43_gphy_op_recalc_txpower(struct b43_wldev *dev, + bool ignore_tssi) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + unsigned int average_tssi; + int cck_result, ofdm_result; + int estimated_pwr, desired_pwr, pwr_adjust; + int rfatt_delta, bbatt_delta; + unsigned int max_pwr; + + /* First get the average TSSI */ + cck_result = b43_phy_shm_tssi_read(dev, B43_SHM_SH_TSSI_CCK); + ofdm_result = b43_phy_shm_tssi_read(dev, B43_SHM_SH_TSSI_OFDM_G); + if ((cck_result < 0) && (ofdm_result < 0)) { + /* No TSSI information available */ + if (!ignore_tssi) + goto no_adjustment_needed; + cck_result = 0; + ofdm_result = 0; + } + if (cck_result < 0) + average_tssi = ofdm_result; + else if (ofdm_result < 0) + average_tssi = cck_result; + else + average_tssi = (cck_result + ofdm_result) / 2; + /* Merge the average with the stored value. */ + if (likely(gphy->average_tssi != 0xFF)) + average_tssi = (average_tssi + gphy->average_tssi) / 2; + gphy->average_tssi = average_tssi; + B43_WARN_ON(average_tssi >= B43_TSSI_MAX); + + /* Estimate the TX power emission based on the TSSI */ + estimated_pwr = b43_gphy_estimate_power_out(dev, average_tssi); + + B43_WARN_ON(phy->type != B43_PHYTYPE_G); + max_pwr = dev->dev->bus_sprom->maxpwr_bg; + if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) + max_pwr -= 3; /* minus 0.75 */ + if (unlikely(max_pwr >= INT_TO_Q52(30/*dBm*/))) { + b43warn(dev->wl, + "Invalid max-TX-power value in SPROM.\n"); + max_pwr = INT_TO_Q52(20); /* fake it */ + dev->dev->bus_sprom->maxpwr_bg = max_pwr; + } + + /* Get desired power (in Q5.2) */ + if (phy->desired_txpower < 0) + desired_pwr = INT_TO_Q52(0); + else + desired_pwr = INT_TO_Q52(phy->desired_txpower); + /* And limit it. max_pwr already is Q5.2 */ + desired_pwr = clamp_val(desired_pwr, 0, max_pwr); + if (b43_debug(dev, B43_DBG_XMITPOWER)) { + b43dbg(dev->wl, + "[TX power] current = " Q52_FMT + " dBm, desired = " Q52_FMT + " dBm, max = " Q52_FMT "\n", + Q52_ARG(estimated_pwr), + Q52_ARG(desired_pwr), + Q52_ARG(max_pwr)); + } + + /* Calculate the adjustment delta. */ + pwr_adjust = desired_pwr - estimated_pwr; + if (pwr_adjust == 0) + goto no_adjustment_needed; + + /* RF attenuation delta. */ + rfatt_delta = ((pwr_adjust + 7) / 8); + /* Lower attenuation => Bigger power output. Negate it. */ + rfatt_delta = -rfatt_delta; + + /* Baseband attenuation delta. */ + bbatt_delta = pwr_adjust / 2; + /* Lower attenuation => Bigger power output. Negate it. */ + bbatt_delta = -bbatt_delta; + /* RF att affects power level 4 times as much as + * Baseband attennuation. Subtract it. */ + bbatt_delta -= 4 * rfatt_delta; + +#if B43_DEBUG + if (b43_debug(dev, B43_DBG_XMITPOWER)) { + int dbm = pwr_adjust < 0 ? -pwr_adjust : pwr_adjust; + b43dbg(dev->wl, + "[TX power deltas] %s" Q52_FMT " dBm => " + "bbatt-delta = %d, rfatt-delta = %d\n", + (pwr_adjust < 0 ? "-" : ""), Q52_ARG(dbm), + bbatt_delta, rfatt_delta); + } +#endif /* DEBUG */ + + /* So do we finally need to adjust something in hardware? */ + if ((rfatt_delta == 0) && (bbatt_delta == 0)) + goto no_adjustment_needed; + + /* Save the deltas for later when we adjust the power. */ + gphy->bbatt_delta = bbatt_delta; + gphy->rfatt_delta = rfatt_delta; + + /* We need to adjust the TX power on the device. */ + return B43_TXPWR_RES_NEED_ADJUST; + +no_adjustment_needed: + return B43_TXPWR_RES_DONE; +} + +static void b43_gphy_op_pwork_15sec(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + + b43_mac_suspend(dev); + //TODO: update_aci_moving_average + if (gphy->aci_enable && gphy->aci_wlan_automatic) { + if (!gphy->aci_enable && 1 /*TODO: not scanning? */ ) { + if (0 /*TODO: bunch of conditions */ ) { + phy->ops->interf_mitigation(dev, + B43_INTERFMODE_MANUALWLAN); + } + } else if (0 /*TODO*/) { + if (/*(aci_average > 1000) &&*/ !b43_gphy_aci_scan(dev)) + phy->ops->interf_mitigation(dev, B43_INTERFMODE_NONE); + } + } else if (gphy->interfmode == B43_INTERFMODE_NONWLAN && + phy->rev == 1) { + //TODO: implement rev1 workaround + } + b43_lo_g_maintenance_work(dev); + b43_mac_enable(dev); +} + +static void b43_gphy_op_pwork_60sec(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_RSSI)) + return; + + b43_mac_suspend(dev); + b43_calc_nrssi_slope(dev); + if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 8)) { + u8 old_chan = phy->channel; + + /* VCO Calibration */ + if (old_chan >= 8) + b43_switch_channel(dev, 1); + else + b43_switch_channel(dev, 13); + b43_switch_channel(dev, old_chan); + } + b43_mac_enable(dev); +} + +const struct b43_phy_operations b43_phyops_g = { + .allocate = b43_gphy_op_allocate, + .free = b43_gphy_op_free, + .prepare_structs = b43_gphy_op_prepare_structs, + .prepare_hardware = b43_gphy_op_prepare_hardware, + .init = b43_gphy_op_init, + .exit = b43_gphy_op_exit, + .phy_read = b43_gphy_op_read, + .phy_write = b43_gphy_op_write, + .radio_read = b43_gphy_op_radio_read, + .radio_write = b43_gphy_op_radio_write, + .supports_hwpctl = b43_gphy_op_supports_hwpctl, + .software_rfkill = b43_gphy_op_software_rfkill, + .switch_analog = b43_phyop_switch_analog_generic, + .switch_channel = b43_gphy_op_switch_channel, + .get_default_chan = b43_gphy_op_get_default_chan, + .set_rx_antenna = b43_gphy_op_set_rx_antenna, + .interf_mitigation = b43_gphy_op_interf_mitigation, + .recalc_txpower = b43_gphy_op_recalc_txpower, + .adjust_txpower = b43_gphy_op_adjust_txpower, + .pwork_15sec = b43_gphy_op_pwork_15sec, + .pwork_60sec = b43_gphy_op_pwork_60sec, +}; diff --git a/drivers/net/wireless/broadcom/b43/phy_g.h b/drivers/net/wireless/broadcom/b43/phy_g.h new file mode 100644 index 000000000000..5413c906a3e7 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_g.h @@ -0,0 +1,208 @@ +#ifndef LINUX_B43_PHY_G_H_ +#define LINUX_B43_PHY_G_H_ + +/* OFDM PHY registers are defined in the A-PHY header. */ +#include "phy_a.h" + +/* CCK (B) PHY Registers */ +#define B43_PHY_VERSION_CCK B43_PHY_CCK(0x00) /* Versioning register for B-PHY */ +#define B43_PHY_CCKBBANDCFG B43_PHY_CCK(0x01) /* Contains antenna 0/1 control bit */ +#define B43_PHY_PGACTL B43_PHY_CCK(0x15) /* PGA control */ +#define B43_PHY_PGACTL_LPF 0x1000 /* Low pass filter (?) */ +#define B43_PHY_PGACTL_LOWBANDW 0x0040 /* Low bandwidth flag */ +#define B43_PHY_PGACTL_UNKNOWN 0xEFA0 +#define B43_PHY_FBCTL1 B43_PHY_CCK(0x18) /* Frequency bandwidth control 1 */ +#define B43_PHY_ITSSI B43_PHY_CCK(0x29) /* Idle TSSI */ +#define B43_PHY_LO_LEAKAGE B43_PHY_CCK(0x2D) /* Measured LO leakage */ +#define B43_PHY_ENERGY B43_PHY_CCK(0x33) /* Energy */ +#define B43_PHY_SYNCCTL B43_PHY_CCK(0x35) +#define B43_PHY_FBCTL2 B43_PHY_CCK(0x38) /* Frequency bandwidth control 2 */ +#define B43_PHY_DACCTL B43_PHY_CCK(0x60) /* DAC control */ +#define B43_PHY_RCCALOVER B43_PHY_CCK(0x78) /* RC calibration override */ + +/* Extended G-PHY Registers */ +#define B43_PHY_CLASSCTL B43_PHY_EXTG(0x02) /* Classify control */ +#define B43_PHY_GTABCTL B43_PHY_EXTG(0x03) /* G-PHY table control (see below) */ +#define B43_PHY_GTABOFF 0x03FF /* G-PHY table offset (see below) */ +#define B43_PHY_GTABNR 0xFC00 /* G-PHY table number (see below) */ +#define B43_PHY_GTABNR_SHIFT 10 +#define B43_PHY_GTABDATA B43_PHY_EXTG(0x04) /* G-PHY table data */ +#define B43_PHY_LO_MASK B43_PHY_EXTG(0x0F) /* Local Oscillator control mask */ +#define B43_PHY_LO_CTL B43_PHY_EXTG(0x10) /* Local Oscillator control */ +#define B43_PHY_RFOVER B43_PHY_EXTG(0x11) /* RF override */ +#define B43_PHY_RFOVERVAL B43_PHY_EXTG(0x12) /* RF override value */ +#define B43_PHY_RFOVERVAL_EXTLNA 0x8000 +#define B43_PHY_RFOVERVAL_LNA 0x7000 +#define B43_PHY_RFOVERVAL_LNA_SHIFT 12 +#define B43_PHY_RFOVERVAL_PGA 0x0F00 +#define B43_PHY_RFOVERVAL_PGA_SHIFT 8 +#define B43_PHY_RFOVERVAL_UNK 0x0010 /* Unknown, always set. */ +#define B43_PHY_RFOVERVAL_TRSWRX 0x00E0 +#define B43_PHY_RFOVERVAL_BW 0x0003 /* Bandwidth flags */ +#define B43_PHY_RFOVERVAL_BW_LPF 0x0001 /* Low Pass Filter */ +#define B43_PHY_RFOVERVAL_BW_LBW 0x0002 /* Low Bandwidth (when set), high when unset */ +#define B43_PHY_ANALOGOVER B43_PHY_EXTG(0x14) /* Analog override */ +#define B43_PHY_ANALOGOVERVAL B43_PHY_EXTG(0x15) /* Analog override value */ + + +/*** G-PHY table numbers */ +#define B43_GTAB(number, offset) (((number) << B43_PHY_GTABNR_SHIFT) | (offset)) +#define B43_GTAB_NRSSI B43_GTAB(0x00, 0) +#define B43_GTAB_TRFEMW B43_GTAB(0x0C, 0x120) +#define B43_GTAB_ORIGTR B43_GTAB(0x2E, 0x298) + +u16 b43_gtab_read(struct b43_wldev *dev, u16 table, u16 offset); +void b43_gtab_write(struct b43_wldev *dev, u16 table, u16 offset, u16 value); + + +/* Returns the boolean whether "TX Magnification" is enabled. */ +#define has_tx_magnification(phy) \ + (((phy)->rev >= 2) && \ + ((phy)->radio_ver == 0x2050) && \ + ((phy)->radio_rev == 8)) +/* Card uses the loopback gain stuff */ +#define has_loopback_gain(phy) \ + (((phy)->rev > 1) || ((phy)->gmode)) + +/* Radio Attenuation (RF Attenuation) */ +struct b43_rfatt { + u8 att; /* Attenuation value */ + bool with_padmix; /* Flag, PAD Mixer enabled. */ +}; +struct b43_rfatt_list { + /* Attenuation values list */ + const struct b43_rfatt *list; + u8 len; + /* Minimum/Maximum attenuation values */ + u8 min_val; + u8 max_val; +}; + +/* Returns true, if the values are the same. */ +static inline bool b43_compare_rfatt(const struct b43_rfatt *a, + const struct b43_rfatt *b) +{ + return ((a->att == b->att) && + (a->with_padmix == b->with_padmix)); +} + +/* Baseband Attenuation */ +struct b43_bbatt { + u8 att; /* Attenuation value */ +}; +struct b43_bbatt_list { + /* Attenuation values list */ + const struct b43_bbatt *list; + u8 len; + /* Minimum/Maximum attenuation values */ + u8 min_val; + u8 max_val; +}; + +/* Returns true, if the values are the same. */ +static inline bool b43_compare_bbatt(const struct b43_bbatt *a, + const struct b43_bbatt *b) +{ + return (a->att == b->att); +} + +/* tx_control bits. */ +#define B43_TXCTL_PA3DB 0x40 /* PA Gain 3dB */ +#define B43_TXCTL_PA2DB 0x20 /* PA Gain 2dB */ +#define B43_TXCTL_TXMIX 0x10 /* TX Mixer Gain */ + +struct b43_txpower_lo_control; + +struct b43_phy_g { + /* ACI (adjacent channel interference) flags. */ + bool aci_enable; + bool aci_wlan_automatic; + bool aci_hw_rssi; + + /* Radio switched on/off */ + bool radio_on; + struct { + /* Values saved when turning the radio off. + * They are needed when turning it on again. */ + bool valid; + u16 rfover; + u16 rfoverval; + } radio_off_context; + + u16 minlowsig[2]; + u16 minlowsigpos[2]; + + /* Pointer to the table used to convert a + * TSSI value to dBm-Q5.2 */ + const s8 *tssi2dbm; + /* tssi2dbm is kmalloc()ed. Only used for free()ing. */ + bool dyn_tssi_tbl; + /* Target idle TSSI */ + int tgt_idle_tssi; + /* Current idle TSSI */ + int cur_idle_tssi; + /* The current average TSSI. */ + u8 average_tssi; + /* Current TX power level attenuation control values */ + struct b43_bbatt bbatt; + struct b43_rfatt rfatt; + u8 tx_control; /* B43_TXCTL_XXX */ + /* The calculated attenuation deltas that are used later + * when adjusting the actual power output. */ + int bbatt_delta; + int rfatt_delta; + + /* LocalOscillator control values. */ + struct b43_txpower_lo_control *lo_control; + /* Values from b43_calc_loopback_gain() */ + s16 max_lb_gain; /* Maximum Loopback gain in hdB */ + s16 trsw_rx_gain; /* TRSW RX gain in hdB */ + s16 lna_lod_gain; /* LNA lod */ + s16 lna_gain; /* LNA */ + s16 pga_gain; /* PGA */ + + /* Current Interference Mitigation mode */ + int interfmode; + /* Stack of saved values from the Interference Mitigation code. + * Each value in the stack is laid out as follows: + * bit 0-11: offset + * bit 12-15: register ID + * bit 16-32: value + * register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT + */ +#define B43_INTERFSTACK_SIZE 26 + u32 interfstack[B43_INTERFSTACK_SIZE]; //FIXME: use a data structure + + /* Saved values from the NRSSI Slope calculation */ + s16 nrssi[2]; + s32 nrssislope; + /* In memory nrssi lookup table. */ + s8 nrssi_lt[64]; + + u16 lofcal; + + u16 initval; //FIXME rename? + + /* The device does address auto increment for the OFDM tables. + * We cache the previously used address here and omit the address + * write on the next table access, if possible. */ + u16 ofdmtab_addr; /* The address currently set in hardware. */ + enum { /* The last data flow direction. */ + B43_OFDMTAB_DIRECTION_UNKNOWN = 0, + B43_OFDMTAB_DIRECTION_READ, + B43_OFDMTAB_DIRECTION_WRITE, + } ofdmtab_addr_direction; +}; + +void b43_gphy_set_baseband_attenuation(struct b43_wldev *dev, + u16 baseband_attenuation); +void b43_gphy_channel_switch(struct b43_wldev *dev, + unsigned int channel, + bool synthetic_pu_workaround); +u8 * b43_generate_dyn_tssi2dbm_tab(struct b43_wldev *dev, + s16 pab0, s16 pab1, s16 pab2); + +struct b43_phy_operations; +extern const struct b43_phy_operations b43_phyops_g; + +#endif /* LINUX_B43_PHY_G_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/phy_ht.c b/drivers/net/wireless/broadcom/b43/phy_ht.c new file mode 100644 index 000000000000..bd68945965d6 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_ht.c @@ -0,0 +1,1153 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11n HT-PHY support + + Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include + +#include "b43.h" +#include "phy_ht.h" +#include "tables_phy_ht.h" +#include "radio_2059.h" +#include "main.h" + +/* Force values to keep compatibility with wl */ +enum ht_rssi_type { + HT_RSSI_W1 = 0, + HT_RSSI_W2 = 1, + HT_RSSI_NB = 2, + HT_RSSI_IQ = 3, + HT_RSSI_TSSI_2G = 4, + HT_RSSI_TSSI_5G = 5, + HT_RSSI_TBD = 6, +}; + +/************************************************** + * Radio 2059. + **************************************************/ + +static void b43_radio_2059_channel_setup(struct b43_wldev *dev, + const struct b43_phy_ht_channeltab_e_radio2059 *e) +{ + static const u16 routing[] = { R2059_C1, R2059_C2, R2059_C3, }; + u16 r; + int core; + + b43_radio_write(dev, 0x16, e->radio_syn16); + b43_radio_write(dev, 0x17, e->radio_syn17); + b43_radio_write(dev, 0x22, e->radio_syn22); + b43_radio_write(dev, 0x25, e->radio_syn25); + b43_radio_write(dev, 0x27, e->radio_syn27); + b43_radio_write(dev, 0x28, e->radio_syn28); + b43_radio_write(dev, 0x29, e->radio_syn29); + b43_radio_write(dev, 0x2c, e->radio_syn2c); + b43_radio_write(dev, 0x2d, e->radio_syn2d); + b43_radio_write(dev, 0x37, e->radio_syn37); + b43_radio_write(dev, 0x41, e->radio_syn41); + b43_radio_write(dev, 0x43, e->radio_syn43); + b43_radio_write(dev, 0x47, e->radio_syn47); + + for (core = 0; core < 3; core++) { + r = routing[core]; + b43_radio_write(dev, r | 0x4a, e->radio_rxtx4a); + b43_radio_write(dev, r | 0x58, e->radio_rxtx58); + b43_radio_write(dev, r | 0x5a, e->radio_rxtx5a); + b43_radio_write(dev, r | 0x6a, e->radio_rxtx6a); + b43_radio_write(dev, r | 0x6d, e->radio_rxtx6d); + b43_radio_write(dev, r | 0x6e, e->radio_rxtx6e); + b43_radio_write(dev, r | 0x92, e->radio_rxtx92); + b43_radio_write(dev, r | 0x98, e->radio_rxtx98); + } + + udelay(50); + + /* Calibration */ + b43_radio_mask(dev, R2059_RFPLL_MISC_EN, ~0x1); + b43_radio_mask(dev, R2059_RFPLL_MISC_CAL_RESETN, ~0x4); + b43_radio_set(dev, R2059_RFPLL_MISC_CAL_RESETN, 0x4); + b43_radio_set(dev, R2059_RFPLL_MISC_EN, 0x1); + + udelay(300); +} + +/* Calibrate resistors in LPF of PLL? */ +static void b43_radio_2059_rcal(struct b43_wldev *dev) +{ + /* Enable */ + b43_radio_set(dev, R2059_C3 | R2059_RCAL_CONFIG, 0x1); + usleep_range(10, 20); + + b43_radio_set(dev, R2059_C3 | 0x0BF, 0x1); + b43_radio_maskset(dev, R2059_C3 | 0x19B, 0x3, 0x2); + + /* Start */ + b43_radio_set(dev, R2059_C3 | R2059_RCAL_CONFIG, 0x2); + usleep_range(100, 200); + + /* Stop */ + b43_radio_mask(dev, R2059_C3 | R2059_RCAL_CONFIG, ~0x2); + + if (!b43_radio_wait_value(dev, R2059_C3 | R2059_RCAL_STATUS, 1, 1, 100, + 1000000)) + b43err(dev->wl, "Radio 0x2059 rcal timeout\n"); + + /* Disable */ + b43_radio_mask(dev, R2059_C3 | R2059_RCAL_CONFIG, ~0x1); + + b43_radio_set(dev, 0xa, 0x60); +} + +/* Calibrate the internal RC oscillator? */ +static void b43_radio_2057_rccal(struct b43_wldev *dev) +{ + const u16 radio_values[3][2] = { + { 0x61, 0xE9 }, { 0x69, 0xD5 }, { 0x73, 0x99 }, + }; + int i; + + for (i = 0; i < 3; i++) { + b43_radio_write(dev, R2059_RCCAL_MASTER, radio_values[i][0]); + b43_radio_write(dev, R2059_RCCAL_X1, 0x6E); + b43_radio_write(dev, R2059_RCCAL_TRC0, radio_values[i][1]); + + /* Start */ + b43_radio_write(dev, R2059_RCCAL_START_R1_Q1_P1, 0x55); + + /* Wait */ + if (!b43_radio_wait_value(dev, R2059_RCCAL_DONE_OSCCAP, 2, 2, + 500, 5000000)) + b43err(dev->wl, "Radio 0x2059 rccal timeout\n"); + + /* Stop */ + b43_radio_write(dev, R2059_RCCAL_START_R1_Q1_P1, 0x15); + } + + b43_radio_mask(dev, R2059_RCCAL_MASTER, ~0x1); +} + +static void b43_radio_2059_init_pre(struct b43_wldev *dev) +{ + b43_phy_mask(dev, B43_PHY_HT_RF_CTL_CMD, ~B43_PHY_HT_RF_CTL_CMD_CHIP0_PU); + b43_phy_set(dev, B43_PHY_HT_RF_CTL_CMD, B43_PHY_HT_RF_CTL_CMD_FORCE); + b43_phy_mask(dev, B43_PHY_HT_RF_CTL_CMD, ~B43_PHY_HT_RF_CTL_CMD_FORCE); + b43_phy_set(dev, B43_PHY_HT_RF_CTL_CMD, B43_PHY_HT_RF_CTL_CMD_CHIP0_PU); +} + +static void b43_radio_2059_init(struct b43_wldev *dev) +{ + const u16 routing[] = { R2059_C1, R2059_C2, R2059_C3 }; + int i; + + /* Prepare (reset?) radio */ + b43_radio_2059_init_pre(dev); + + r2059_upload_inittabs(dev); + + for (i = 0; i < ARRAY_SIZE(routing); i++) + b43_radio_set(dev, routing[i] | 0x146, 0x3); + + /* Post init starts below */ + + b43_radio_set(dev, R2059_RFPLL_MISC_CAL_RESETN, 0x0078); + b43_radio_set(dev, R2059_XTAL_CONFIG2, 0x0080); + msleep(2); + b43_radio_mask(dev, R2059_RFPLL_MISC_CAL_RESETN, ~0x0078); + b43_radio_mask(dev, R2059_XTAL_CONFIG2, ~0x0080); + + if (1) { /* FIXME */ + b43_radio_2059_rcal(dev); + b43_radio_2057_rccal(dev); + } + + b43_radio_mask(dev, R2059_RFPLL_MASTER, ~0x0008); +} + +/************************************************** + * RF + **************************************************/ + +static void b43_phy_ht_force_rf_sequence(struct b43_wldev *dev, u16 rf_seq) +{ + u8 i; + + u16 save_seq_mode = b43_phy_read(dev, B43_PHY_HT_RF_SEQ_MODE); + b43_phy_set(dev, B43_PHY_HT_RF_SEQ_MODE, 0x3); + + b43_phy_set(dev, B43_PHY_HT_RF_SEQ_TRIG, rf_seq); + for (i = 0; i < 200; i++) { + if (!(b43_phy_read(dev, B43_PHY_HT_RF_SEQ_STATUS) & rf_seq)) { + i = 0; + break; + } + msleep(1); + } + if (i) + b43err(dev->wl, "Forcing RF sequence timeout\n"); + + b43_phy_write(dev, B43_PHY_HT_RF_SEQ_MODE, save_seq_mode); +} + +static void b43_phy_ht_pa_override(struct b43_wldev *dev, bool enable) +{ + struct b43_phy_ht *htphy = dev->phy.ht; + static const u16 regs[3] = { B43_PHY_HT_RF_CTL_INT_C1, + B43_PHY_HT_RF_CTL_INT_C2, + B43_PHY_HT_RF_CTL_INT_C3 }; + int i; + + if (enable) { + for (i = 0; i < 3; i++) + b43_phy_write(dev, regs[i], htphy->rf_ctl_int_save[i]); + } else { + for (i = 0; i < 3; i++) + htphy->rf_ctl_int_save[i] = b43_phy_read(dev, regs[i]); + /* TODO: Does 5GHz band use different value (not 0x0400)? */ + for (i = 0; i < 3; i++) + b43_phy_write(dev, regs[i], 0x0400); + } +} + +/************************************************** + * Various PHY ops + **************************************************/ + +static u16 b43_phy_ht_classifier(struct b43_wldev *dev, u16 mask, u16 val) +{ + u16 tmp; + u16 allowed = B43_PHY_HT_CLASS_CTL_CCK_EN | + B43_PHY_HT_CLASS_CTL_OFDM_EN | + B43_PHY_HT_CLASS_CTL_WAITED_EN; + + tmp = b43_phy_read(dev, B43_PHY_HT_CLASS_CTL); + tmp &= allowed; + tmp &= ~mask; + tmp |= (val & mask); + b43_phy_maskset(dev, B43_PHY_HT_CLASS_CTL, ~allowed, tmp); + + return tmp; +} + +static void b43_phy_ht_reset_cca(struct b43_wldev *dev) +{ + u16 bbcfg; + + b43_phy_force_clock(dev, true); + bbcfg = b43_phy_read(dev, B43_PHY_HT_BBCFG); + b43_phy_write(dev, B43_PHY_HT_BBCFG, bbcfg | B43_PHY_HT_BBCFG_RSTCCA); + udelay(1); + b43_phy_write(dev, B43_PHY_HT_BBCFG, bbcfg & ~B43_PHY_HT_BBCFG_RSTCCA); + b43_phy_force_clock(dev, false); + + b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RST2RX); +} + +static void b43_phy_ht_zero_extg(struct b43_wldev *dev) +{ + u8 i, j; + u16 base[] = { 0x40, 0x60, 0x80 }; + + for (i = 0; i < ARRAY_SIZE(base); i++) { + for (j = 0; j < 4; j++) + b43_phy_write(dev, B43_PHY_EXTG(base[i] + j), 0); + } + + for (i = 0; i < ARRAY_SIZE(base); i++) + b43_phy_write(dev, B43_PHY_EXTG(base[i] + 0xc), 0); +} + +/* Some unknown AFE (Analog Frondned) op */ +static void b43_phy_ht_afe_unk1(struct b43_wldev *dev) +{ + u8 i; + + static const u16 ctl_regs[3][2] = { + { B43_PHY_HT_AFE_C1_OVER, B43_PHY_HT_AFE_C1 }, + { B43_PHY_HT_AFE_C2_OVER, B43_PHY_HT_AFE_C2 }, + { B43_PHY_HT_AFE_C3_OVER, B43_PHY_HT_AFE_C3}, + }; + + for (i = 0; i < 3; i++) { + /* TODO: verify masks&sets */ + b43_phy_set(dev, ctl_regs[i][1], 0x4); + b43_phy_set(dev, ctl_regs[i][0], 0x4); + b43_phy_mask(dev, ctl_regs[i][1], ~0x1); + b43_phy_set(dev, ctl_regs[i][0], 0x1); + b43_httab_write(dev, B43_HTTAB16(8, 5 + (i * 0x10)), 0); + b43_phy_mask(dev, ctl_regs[i][0], ~0x4); + } +} + +static void b43_phy_ht_read_clip_detection(struct b43_wldev *dev, u16 *clip_st) +{ + clip_st[0] = b43_phy_read(dev, B43_PHY_HT_C1_CLIP1THRES); + clip_st[1] = b43_phy_read(dev, B43_PHY_HT_C2_CLIP1THRES); + clip_st[2] = b43_phy_read(dev, B43_PHY_HT_C3_CLIP1THRES); +} + +static void b43_phy_ht_bphy_init(struct b43_wldev *dev) +{ + unsigned int i; + u16 val; + + val = 0x1E1F; + for (i = 0; i < 16; i++) { + b43_phy_write(dev, B43_PHY_N_BMODE(0x88 + i), val); + val -= 0x202; + } + val = 0x3E3F; + for (i = 0; i < 16; i++) { + b43_phy_write(dev, B43_PHY_N_BMODE(0x98 + i), val); + val -= 0x202; + } + b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668); +} + +static void b43_phy_ht_bphy_reset(struct b43_wldev *dev, bool reset) +{ + u16 tmp; + + tmp = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, + tmp | B43_PSM_HDR_MAC_PHY_FORCE_CLK); + + /* Put BPHY in or take it out of the reset */ + if (reset) + b43_phy_set(dev, B43_PHY_B_BBCFG, + B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX); + else + b43_phy_mask(dev, B43_PHY_B_BBCFG, + (u16)~(B43_PHY_B_BBCFG_RSTCCA | + B43_PHY_B_BBCFG_RSTRX)); + + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp); +} + +/************************************************** + * Samples + **************************************************/ + +static void b43_phy_ht_stop_playback(struct b43_wldev *dev) +{ + struct b43_phy_ht *phy_ht = dev->phy.ht; + u16 tmp; + int i; + + tmp = b43_phy_read(dev, B43_PHY_HT_SAMP_STAT); + if (tmp & 0x1) + b43_phy_set(dev, B43_PHY_HT_SAMP_CMD, B43_PHY_HT_SAMP_CMD_STOP); + else if (tmp & 0x2) + b43_phy_mask(dev, B43_PHY_HT_IQLOCAL_CMDGCTL, 0x7FFF); + + b43_phy_mask(dev, B43_PHY_HT_SAMP_CMD, ~0x0004); + + for (i = 0; i < 3; i++) { + if (phy_ht->bb_mult_save[i] >= 0) { + b43_httab_write(dev, B43_HTTAB16(13, 0x63 + i * 4), + phy_ht->bb_mult_save[i]); + b43_httab_write(dev, B43_HTTAB16(13, 0x67 + i * 4), + phy_ht->bb_mult_save[i]); + } + } +} + +static u16 b43_phy_ht_load_samples(struct b43_wldev *dev) +{ + int i; + u16 len = 20 << 3; + + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, 0x4400); + + for (i = 0; i < len; i++) { + b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, 0); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, 0); + } + + return len; +} + +static void b43_phy_ht_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, + u16 wait) +{ + struct b43_phy_ht *phy_ht = dev->phy.ht; + u16 save_seq_mode; + int i; + + for (i = 0; i < 3; i++) { + if (phy_ht->bb_mult_save[i] < 0) + phy_ht->bb_mult_save[i] = b43_httab_read(dev, B43_HTTAB16(13, 0x63 + i * 4)); + } + + b43_phy_write(dev, B43_PHY_HT_SAMP_DEP_CNT, samps - 1); + if (loops != 0xFFFF) + loops--; + b43_phy_write(dev, B43_PHY_HT_SAMP_LOOP_CNT, loops); + b43_phy_write(dev, B43_PHY_HT_SAMP_WAIT_CNT, wait); + + save_seq_mode = b43_phy_read(dev, B43_PHY_HT_RF_SEQ_MODE); + b43_phy_set(dev, B43_PHY_HT_RF_SEQ_MODE, + B43_PHY_HT_RF_SEQ_MODE_CA_OVER); + + /* TODO: find out mask bits! Do we need more function arguments? */ + b43_phy_mask(dev, B43_PHY_HT_SAMP_CMD, ~0); + b43_phy_mask(dev, B43_PHY_HT_SAMP_CMD, ~0); + b43_phy_mask(dev, B43_PHY_HT_IQLOCAL_CMDGCTL, ~0); + b43_phy_set(dev, B43_PHY_HT_SAMP_CMD, 0x1); + + for (i = 0; i < 100; i++) { + if (!(b43_phy_read(dev, B43_PHY_HT_RF_SEQ_STATUS) & 1)) { + i = 0; + break; + } + udelay(10); + } + if (i) + b43err(dev->wl, "run samples timeout\n"); + + b43_phy_write(dev, B43_PHY_HT_RF_SEQ_MODE, save_seq_mode); +} + +static void b43_phy_ht_tx_tone(struct b43_wldev *dev) +{ + u16 samp; + + samp = b43_phy_ht_load_samples(dev); + b43_phy_ht_run_samples(dev, samp, 0xFFFF, 0); +} + +/************************************************** + * RSSI + **************************************************/ + +static void b43_phy_ht_rssi_select(struct b43_wldev *dev, u8 core_sel, + enum ht_rssi_type rssi_type) +{ + static const u16 ctl_regs[3][2] = { + { B43_PHY_HT_AFE_C1, B43_PHY_HT_AFE_C1_OVER, }, + { B43_PHY_HT_AFE_C2, B43_PHY_HT_AFE_C2_OVER, }, + { B43_PHY_HT_AFE_C3, B43_PHY_HT_AFE_C3_OVER, }, + }; + static const u16 radio_r[] = { R2059_C1, R2059_C2, R2059_C3, }; + int core; + + if (core_sel == 0) { + b43err(dev->wl, "RSSI selection for core off not implemented yet\n"); + } else { + for (core = 0; core < 3; core++) { + /* Check if caller requested a one specific core */ + if ((core_sel == 1 && core != 0) || + (core_sel == 2 && core != 1) || + (core_sel == 3 && core != 2)) + continue; + + switch (rssi_type) { + case HT_RSSI_TSSI_2G: + b43_phy_set(dev, ctl_regs[core][0], 0x3 << 8); + b43_phy_set(dev, ctl_regs[core][0], 0x3 << 10); + b43_phy_set(dev, ctl_regs[core][1], 0x1 << 9); + b43_phy_set(dev, ctl_regs[core][1], 0x1 << 10); + + b43_radio_set(dev, R2059_C3 | 0xbf, 0x1); + b43_radio_write(dev, radio_r[core] | 0x159, + 0x11); + break; + default: + b43err(dev->wl, "RSSI selection for type %d not implemented yet\n", + rssi_type); + } + } + } +} + +static void b43_phy_ht_poll_rssi(struct b43_wldev *dev, enum ht_rssi_type type, + s32 *buf, u8 nsamp) +{ + u16 phy_regs_values[12]; + static const u16 phy_regs_to_save[] = { + B43_PHY_HT_AFE_C1, B43_PHY_HT_AFE_C1_OVER, + 0x848, 0x841, + B43_PHY_HT_AFE_C2, B43_PHY_HT_AFE_C2_OVER, + 0x868, 0x861, + B43_PHY_HT_AFE_C3, B43_PHY_HT_AFE_C3_OVER, + 0x888, 0x881, + }; + u16 tmp[3]; + int i; + + for (i = 0; i < 12; i++) + phy_regs_values[i] = b43_phy_read(dev, phy_regs_to_save[i]); + + b43_phy_ht_rssi_select(dev, 5, type); + + for (i = 0; i < 6; i++) + buf[i] = 0; + + for (i = 0; i < nsamp; i++) { + tmp[0] = b43_phy_read(dev, B43_PHY_HT_RSSI_C1); + tmp[1] = b43_phy_read(dev, B43_PHY_HT_RSSI_C2); + tmp[2] = b43_phy_read(dev, B43_PHY_HT_RSSI_C3); + + buf[0] += ((s8)((tmp[0] & 0x3F) << 2)) >> 2; + buf[1] += ((s8)(((tmp[0] >> 8) & 0x3F) << 2)) >> 2; + buf[2] += ((s8)((tmp[1] & 0x3F) << 2)) >> 2; + buf[3] += ((s8)(((tmp[1] >> 8) & 0x3F) << 2)) >> 2; + buf[4] += ((s8)((tmp[2] & 0x3F) << 2)) >> 2; + buf[5] += ((s8)(((tmp[2] >> 8) & 0x3F) << 2)) >> 2; + } + + for (i = 0; i < 12; i++) + b43_phy_write(dev, phy_regs_to_save[i], phy_regs_values[i]); +} + +/************************************************** + * Tx/Rx + **************************************************/ + +static void b43_phy_ht_tx_power_fix(struct b43_wldev *dev) +{ + int i; + + for (i = 0; i < 3; i++) { + u16 mask; + u32 tmp = b43_httab_read(dev, B43_HTTAB32(26, 0xE8)); + + if (0) /* FIXME */ + mask = 0x2 << (i * 4); + else + mask = 0; + b43_phy_mask(dev, B43_PHY_EXTG(0x108), mask); + + b43_httab_write(dev, B43_HTTAB16(7, 0x110 + i), tmp >> 16); + b43_httab_write(dev, B43_HTTAB8(13, 0x63 + (i * 4)), + tmp & 0xFF); + b43_httab_write(dev, B43_HTTAB8(13, 0x73 + (i * 4)), + tmp & 0xFF); + } +} + +static void b43_phy_ht_tx_power_ctl(struct b43_wldev *dev, bool enable) +{ + struct b43_phy_ht *phy_ht = dev->phy.ht; + u16 en_bits = B43_PHY_HT_TXPCTL_CMD_C1_COEFF | + B43_PHY_HT_TXPCTL_CMD_C1_HWPCTLEN | + B43_PHY_HT_TXPCTL_CMD_C1_PCTLEN; + static const u16 cmd_regs[3] = { B43_PHY_HT_TXPCTL_CMD_C1, + B43_PHY_HT_TXPCTL_CMD_C2, + B43_PHY_HT_TXPCTL_CMD_C3 }; + static const u16 status_regs[3] = { B43_PHY_HT_TX_PCTL_STATUS_C1, + B43_PHY_HT_TX_PCTL_STATUS_C2, + B43_PHY_HT_TX_PCTL_STATUS_C3 }; + int i; + + if (!enable) { + if (b43_phy_read(dev, B43_PHY_HT_TXPCTL_CMD_C1) & en_bits) { + /* We disable enabled TX pwr ctl, save it's state */ + for (i = 0; i < 3; i++) + phy_ht->tx_pwr_idx[i] = + b43_phy_read(dev, status_regs[i]); + } + b43_phy_mask(dev, B43_PHY_HT_TXPCTL_CMD_C1, ~en_bits); + } else { + b43_phy_set(dev, B43_PHY_HT_TXPCTL_CMD_C1, en_bits); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + for (i = 0; i < 3; i++) + b43_phy_write(dev, cmd_regs[i], 0x32); + } + + for (i = 0; i < 3; i++) + if (phy_ht->tx_pwr_idx[i] <= + B43_PHY_HT_TXPCTL_CMD_C1_INIT) + b43_phy_write(dev, cmd_regs[i], + phy_ht->tx_pwr_idx[i]); + } + + phy_ht->tx_pwr_ctl = enable; +} + +static void b43_phy_ht_tx_power_ctl_idle_tssi(struct b43_wldev *dev) +{ + struct b43_phy_ht *phy_ht = dev->phy.ht; + static const u16 base[] = { 0x840, 0x860, 0x880 }; + u16 save_regs[3][3]; + s32 rssi_buf[6]; + int core; + + for (core = 0; core < 3; core++) { + save_regs[core][1] = b43_phy_read(dev, base[core] + 6); + save_regs[core][2] = b43_phy_read(dev, base[core] + 7); + save_regs[core][0] = b43_phy_read(dev, base[core] + 0); + + b43_phy_write(dev, base[core] + 6, 0); + b43_phy_mask(dev, base[core] + 7, ~0xF); /* 0xF? Or just 0x6? */ + b43_phy_set(dev, base[core] + 0, 0x0400); + b43_phy_set(dev, base[core] + 0, 0x1000); + } + + b43_phy_ht_tx_tone(dev); + udelay(20); + b43_phy_ht_poll_rssi(dev, HT_RSSI_TSSI_2G, rssi_buf, 1); + b43_phy_ht_stop_playback(dev); + b43_phy_ht_reset_cca(dev); + + phy_ht->idle_tssi[0] = rssi_buf[0] & 0xff; + phy_ht->idle_tssi[1] = rssi_buf[2] & 0xff; + phy_ht->idle_tssi[2] = rssi_buf[4] & 0xff; + + for (core = 0; core < 3; core++) { + b43_phy_write(dev, base[core] + 0, save_regs[core][0]); + b43_phy_write(dev, base[core] + 6, save_regs[core][1]); + b43_phy_write(dev, base[core] + 7, save_regs[core][2]); + } +} + +static void b43_phy_ht_tssi_setup(struct b43_wldev *dev) +{ + static const u16 routing[] = { R2059_C1, R2059_C2, R2059_C3, }; + int core; + + /* 0x159 is probably TX_SSI_MUX or TSSIG (by comparing to N-PHY) */ + for (core = 0; core < 3; core++) { + b43_radio_set(dev, 0x8bf, 0x1); + b43_radio_write(dev, routing[core] | 0x0159, 0x0011); + } +} + +static void b43_phy_ht_tx_power_ctl_setup(struct b43_wldev *dev) +{ + struct b43_phy_ht *phy_ht = dev->phy.ht; + struct ssb_sprom *sprom = dev->dev->bus_sprom; + + u8 *idle = phy_ht->idle_tssi; + u8 target[3]; + s16 a1[3], b0[3], b1[3]; + + u16 freq = dev->phy.chandef->chan->center_freq; + int i, c; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + for (c = 0; c < 3; c++) { + target[c] = sprom->core_pwr_info[c].maxpwr_2g; + a1[c] = sprom->core_pwr_info[c].pa_2g[0]; + b0[c] = sprom->core_pwr_info[c].pa_2g[1]; + b1[c] = sprom->core_pwr_info[c].pa_2g[2]; + } + } else if (freq >= 4900 && freq < 5100) { + for (c = 0; c < 3; c++) { + target[c] = sprom->core_pwr_info[c].maxpwr_5gl; + a1[c] = sprom->core_pwr_info[c].pa_5gl[0]; + b0[c] = sprom->core_pwr_info[c].pa_5gl[1]; + b1[c] = sprom->core_pwr_info[c].pa_5gl[2]; + } + } else if (freq >= 5100 && freq < 5500) { + for (c = 0; c < 3; c++) { + target[c] = sprom->core_pwr_info[c].maxpwr_5g; + a1[c] = sprom->core_pwr_info[c].pa_5g[0]; + b0[c] = sprom->core_pwr_info[c].pa_5g[1]; + b1[c] = sprom->core_pwr_info[c].pa_5g[2]; + } + } else if (freq >= 5500) { + for (c = 0; c < 3; c++) { + target[c] = sprom->core_pwr_info[c].maxpwr_5gh; + a1[c] = sprom->core_pwr_info[c].pa_5gh[0]; + b0[c] = sprom->core_pwr_info[c].pa_5gh[1]; + b1[c] = sprom->core_pwr_info[c].pa_5gh[2]; + } + } else { + target[0] = target[1] = target[2] = 52; + a1[0] = a1[1] = a1[2] = -424; + b0[0] = b0[1] = b0[2] = 5612; + b1[0] = b1[1] = b1[2] = -1393; + } + + b43_phy_set(dev, B43_PHY_HT_TSSIMODE, B43_PHY_HT_TSSIMODE_EN); + b43_phy_mask(dev, B43_PHY_HT_TXPCTL_CMD_C1, + ~B43_PHY_HT_TXPCTL_CMD_C1_PCTLEN & 0xFFFF); + + /* TODO: Does it depend on sprom->fem.ghz2.tssipos? */ + b43_phy_set(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI, 0x4000); + + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C1, + ~B43_PHY_HT_TXPCTL_CMD_C1_INIT, 0x19); + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C2, + ~B43_PHY_HT_TXPCTL_CMD_C2_INIT, 0x19); + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C3, + ~B43_PHY_HT_TXPCTL_CMD_C3_INIT, 0x19); + + b43_phy_set(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI, + B43_PHY_HT_TXPCTL_IDLE_TSSI_BINF); + + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI, + ~B43_PHY_HT_TXPCTL_IDLE_TSSI_C1, + idle[0] << B43_PHY_HT_TXPCTL_IDLE_TSSI_C1_SHIFT); + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI, + ~B43_PHY_HT_TXPCTL_IDLE_TSSI_C2, + idle[1] << B43_PHY_HT_TXPCTL_IDLE_TSSI_C2_SHIFT); + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_IDLE_TSSI2, + ~B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3, + idle[2] << B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3_SHIFT); + + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_N, ~B43_PHY_HT_TXPCTL_N_TSSID, + 0xf0); + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_N, ~B43_PHY_HT_TXPCTL_N_NPTIL2, + 0x3 << B43_PHY_HT_TXPCTL_N_NPTIL2_SHIFT); +#if 0 + /* TODO: what to mask/set? */ + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C1, 0x800, 0) + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_CMD_C1, 0x400, 0) +#endif + + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_TARG_PWR, + ~B43_PHY_HT_TXPCTL_TARG_PWR_C1, + target[0] << B43_PHY_HT_TXPCTL_TARG_PWR_C1_SHIFT); + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_TARG_PWR, + ~B43_PHY_HT_TXPCTL_TARG_PWR_C2 & 0xFFFF, + target[1] << B43_PHY_HT_TXPCTL_TARG_PWR_C2_SHIFT); + b43_phy_maskset(dev, B43_PHY_HT_TXPCTL_TARG_PWR2, + ~B43_PHY_HT_TXPCTL_TARG_PWR2_C3, + target[2] << B43_PHY_HT_TXPCTL_TARG_PWR2_C3_SHIFT); + + for (c = 0; c < 3; c++) { + s32 num, den, pwr; + u32 regval[64]; + + for (i = 0; i < 64; i++) { + num = 8 * (16 * b0[c] + b1[c] * i); + den = 32768 + a1[c] * i; + pwr = max((4 * num + den / 2) / den, -8); + regval[i] = pwr; + } + b43_httab_write_bulk(dev, B43_HTTAB16(26 + c, 0), 64, regval); + } +} + +/************************************************** + * Channel switching ops. + **************************************************/ + +static void b43_phy_ht_spur_avoid(struct b43_wldev *dev, + struct ieee80211_channel *new_channel) +{ + struct bcma_device *core = dev->dev->bdev; + int spuravoid = 0; + + /* Check for 13 and 14 is just a guess, we don't have enough logs. */ + if (new_channel->hw_value == 13 || new_channel->hw_value == 14) + spuravoid = 1; + bcma_core_pll_ctl(core, B43_BCMA_CLKCTLST_PHY_PLL_REQ, 0, false); + bcma_pmu_spuravoid_pllupdate(&core->bus->drv_cc, spuravoid); + bcma_core_pll_ctl(core, + B43_BCMA_CLKCTLST_80211_PLL_REQ | + B43_BCMA_CLKCTLST_PHY_PLL_REQ, + B43_BCMA_CLKCTLST_80211_PLL_ST | + B43_BCMA_CLKCTLST_PHY_PLL_ST, false); + + b43_mac_switch_freq(dev, spuravoid); + + b43_wireless_core_phy_pll_reset(dev); + + if (spuravoid) + b43_phy_set(dev, B43_PHY_HT_BBCFG, B43_PHY_HT_BBCFG_RSTRX); + else + b43_phy_mask(dev, B43_PHY_HT_BBCFG, + ~B43_PHY_HT_BBCFG_RSTRX & 0xFFFF); + + b43_phy_ht_reset_cca(dev); +} + +static void b43_phy_ht_channel_setup(struct b43_wldev *dev, + const struct b43_phy_ht_channeltab_e_phy *e, + struct ieee80211_channel *new_channel) +{ + if (new_channel->band == IEEE80211_BAND_5GHZ) { + /* Switch to 2 GHz for a moment to access B-PHY regs */ + b43_phy_mask(dev, B43_PHY_HT_BANDCTL, ~B43_PHY_HT_BANDCTL_5GHZ); + + b43_phy_ht_bphy_reset(dev, true); + + /* Switch to 5 GHz */ + b43_phy_set(dev, B43_PHY_HT_BANDCTL, B43_PHY_HT_BANDCTL_5GHZ); + } else { + /* Switch to 2 GHz */ + b43_phy_mask(dev, B43_PHY_HT_BANDCTL, ~B43_PHY_HT_BANDCTL_5GHZ); + + b43_phy_ht_bphy_reset(dev, false); + } + + b43_phy_write(dev, B43_PHY_HT_BW1, e->bw1); + b43_phy_write(dev, B43_PHY_HT_BW2, e->bw2); + b43_phy_write(dev, B43_PHY_HT_BW3, e->bw3); + b43_phy_write(dev, B43_PHY_HT_BW4, e->bw4); + b43_phy_write(dev, B43_PHY_HT_BW5, e->bw5); + b43_phy_write(dev, B43_PHY_HT_BW6, e->bw6); + + if (new_channel->hw_value == 14) { + b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_OFDM_EN, 0); + b43_phy_set(dev, B43_PHY_HT_TEST, 0x0800); + } else { + b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_OFDM_EN, + B43_PHY_HT_CLASS_CTL_OFDM_EN); + if (new_channel->band == IEEE80211_BAND_2GHZ) + b43_phy_mask(dev, B43_PHY_HT_TEST, ~0x840); + } + + if (1) /* TODO: On N it's for early devices only, what about HT? */ + b43_phy_ht_tx_power_fix(dev); + + b43_phy_ht_spur_avoid(dev, new_channel); + + b43_phy_write(dev, 0x017e, 0x3830); +} + +static int b43_phy_ht_set_channel(struct b43_wldev *dev, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type) +{ + struct b43_phy *phy = &dev->phy; + + const struct b43_phy_ht_channeltab_e_radio2059 *chent_r2059 = NULL; + + if (phy->radio_ver == 0x2059) { + chent_r2059 = b43_phy_ht_get_channeltab_e_r2059(dev, + channel->center_freq); + if (!chent_r2059) + return -ESRCH; + } else { + return -ESRCH; + } + + /* TODO: In case of N-PHY some bandwidth switching goes here */ + + if (phy->radio_ver == 0x2059) { + b43_radio_2059_channel_setup(dev, chent_r2059); + b43_phy_ht_channel_setup(dev, &(chent_r2059->phy_regs), + channel); + } else { + return -ESRCH; + } + + return 0; +} + +/************************************************** + * Basic PHY ops. + **************************************************/ + +static int b43_phy_ht_op_allocate(struct b43_wldev *dev) +{ + struct b43_phy_ht *phy_ht; + + phy_ht = kzalloc(sizeof(*phy_ht), GFP_KERNEL); + if (!phy_ht) + return -ENOMEM; + dev->phy.ht = phy_ht; + + return 0; +} + +static void b43_phy_ht_op_prepare_structs(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_ht *phy_ht = phy->ht; + int i; + + memset(phy_ht, 0, sizeof(*phy_ht)); + + phy_ht->tx_pwr_ctl = true; + for (i = 0; i < 3; i++) + phy_ht->tx_pwr_idx[i] = B43_PHY_HT_TXPCTL_CMD_C1_INIT + 1; + + for (i = 0; i < 3; i++) + phy_ht->bb_mult_save[i] = -1; +} + +static int b43_phy_ht_op_init(struct b43_wldev *dev) +{ + struct b43_phy_ht *phy_ht = dev->phy.ht; + u16 tmp; + u16 clip_state[3]; + bool saved_tx_pwr_ctl; + + if (dev->dev->bus_type != B43_BUS_BCMA) { + b43err(dev->wl, "HT-PHY is supported only on BCMA bus!\n"); + return -EOPNOTSUPP; + } + + b43_phy_ht_tables_init(dev); + + b43_phy_mask(dev, 0x0be, ~0x2); + b43_phy_set(dev, 0x23f, 0x7ff); + b43_phy_set(dev, 0x240, 0x7ff); + b43_phy_set(dev, 0x241, 0x7ff); + + b43_phy_ht_zero_extg(dev); + + b43_phy_mask(dev, B43_PHY_EXTG(0), ~0x3); + + b43_phy_write(dev, B43_PHY_HT_AFE_C1_OVER, 0); + b43_phy_write(dev, B43_PHY_HT_AFE_C2_OVER, 0); + b43_phy_write(dev, B43_PHY_HT_AFE_C3_OVER, 0); + + b43_phy_write(dev, B43_PHY_EXTG(0x103), 0x20); + b43_phy_write(dev, B43_PHY_EXTG(0x101), 0x20); + b43_phy_write(dev, 0x20d, 0xb8); + b43_phy_write(dev, B43_PHY_EXTG(0x14f), 0xc8); + b43_phy_write(dev, 0x70, 0x50); + b43_phy_write(dev, 0x1ff, 0x30); + + if (0) /* TODO: condition */ + ; /* TODO: PHY op on reg 0x217 */ + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_CCK_EN, 0); + else + b43_phy_ht_classifier(dev, B43_PHY_HT_CLASS_CTL_CCK_EN, + B43_PHY_HT_CLASS_CTL_CCK_EN); + + b43_phy_set(dev, 0xb1, 0x91); + b43_phy_write(dev, 0x32f, 0x0003); + b43_phy_write(dev, 0x077, 0x0010); + b43_phy_write(dev, 0x0b4, 0x0258); + b43_phy_mask(dev, 0x17e, ~0x4000); + + b43_phy_write(dev, 0x0b9, 0x0072); + + b43_httab_write_few(dev, B43_HTTAB16(7, 0x14e), 2, 0x010f, 0x010f); + b43_httab_write_few(dev, B43_HTTAB16(7, 0x15e), 2, 0x010f, 0x010f); + b43_httab_write_few(dev, B43_HTTAB16(7, 0x16e), 2, 0x010f, 0x010f); + + b43_phy_ht_afe_unk1(dev); + + b43_httab_write_few(dev, B43_HTTAB16(7, 0x130), 9, 0x777, 0x111, 0x111, + 0x777, 0x111, 0x111, 0x777, 0x111, 0x111); + + b43_httab_write(dev, B43_HTTAB16(7, 0x120), 0x0777); + b43_httab_write(dev, B43_HTTAB16(7, 0x124), 0x0777); + + b43_httab_write(dev, B43_HTTAB16(8, 0x00), 0x02); + b43_httab_write(dev, B43_HTTAB16(8, 0x10), 0x02); + b43_httab_write(dev, B43_HTTAB16(8, 0x20), 0x02); + + b43_httab_write_few(dev, B43_HTTAB16(8, 0x08), 4, + 0x8e, 0x96, 0x96, 0x96); + b43_httab_write_few(dev, B43_HTTAB16(8, 0x18), 4, + 0x8f, 0x9f, 0x9f, 0x9f); + b43_httab_write_few(dev, B43_HTTAB16(8, 0x28), 4, + 0x8f, 0x9f, 0x9f, 0x9f); + + b43_httab_write_few(dev, B43_HTTAB16(8, 0x0c), 4, 0x2, 0x2, 0x2, 0x2); + b43_httab_write_few(dev, B43_HTTAB16(8, 0x1c), 4, 0x2, 0x2, 0x2, 0x2); + b43_httab_write_few(dev, B43_HTTAB16(8, 0x2c), 4, 0x2, 0x2, 0x2, 0x2); + + b43_phy_maskset(dev, 0x0280, 0xff00, 0x3e); + b43_phy_maskset(dev, 0x0283, 0xff00, 0x3e); + b43_phy_maskset(dev, B43_PHY_OFDM(0x0141), 0xff00, 0x46); + b43_phy_maskset(dev, 0x0283, 0xff00, 0x40); + + b43_httab_write_few(dev, B43_HTTAB16(00, 0x8), 4, + 0x09, 0x0e, 0x13, 0x18); + b43_httab_write_few(dev, B43_HTTAB16(01, 0x8), 4, + 0x09, 0x0e, 0x13, 0x18); + /* TODO: Did wl mean 2 instead of 40? */ + b43_httab_write_few(dev, B43_HTTAB16(40, 0x8), 4, + 0x09, 0x0e, 0x13, 0x18); + + b43_phy_maskset(dev, B43_PHY_OFDM(0x24), 0x3f, 0xd); + b43_phy_maskset(dev, B43_PHY_OFDM(0x64), 0x3f, 0xd); + b43_phy_maskset(dev, B43_PHY_OFDM(0xa4), 0x3f, 0xd); + + b43_phy_set(dev, B43_PHY_EXTG(0x060), 0x1); + b43_phy_set(dev, B43_PHY_EXTG(0x064), 0x1); + b43_phy_set(dev, B43_PHY_EXTG(0x080), 0x1); + b43_phy_set(dev, B43_PHY_EXTG(0x084), 0x1); + + /* Copy some tables entries */ + tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x144)); + b43_httab_write(dev, B43_HTTAB16(7, 0x14a), tmp); + tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x154)); + b43_httab_write(dev, B43_HTTAB16(7, 0x15a), tmp); + tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x164)); + b43_httab_write(dev, B43_HTTAB16(7, 0x16a), tmp); + + /* Reset CCA */ + b43_phy_force_clock(dev, true); + tmp = b43_phy_read(dev, B43_PHY_HT_BBCFG); + b43_phy_write(dev, B43_PHY_HT_BBCFG, tmp | B43_PHY_HT_BBCFG_RSTCCA); + b43_phy_write(dev, B43_PHY_HT_BBCFG, tmp & ~B43_PHY_HT_BBCFG_RSTCCA); + b43_phy_force_clock(dev, false); + + b43_mac_phy_clock_set(dev, true); + + b43_phy_ht_pa_override(dev, false); + b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RX2TX); + b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RST2RX); + b43_phy_ht_pa_override(dev, true); + + /* TODO: Should we restore it? Or store it in global PHY info? */ + b43_phy_ht_classifier(dev, 0, 0); + b43_phy_ht_read_clip_detection(dev, clip_state); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_phy_ht_bphy_init(dev); + + b43_httab_write_bulk(dev, B43_HTTAB32(0x1a, 0xc0), + B43_HTTAB_1A_C0_LATE_SIZE, b43_httab_0x1a_0xc0_late); + + saved_tx_pwr_ctl = phy_ht->tx_pwr_ctl; + b43_phy_ht_tx_power_fix(dev); + b43_phy_ht_tx_power_ctl(dev, false); + b43_phy_ht_tx_power_ctl_idle_tssi(dev); + b43_phy_ht_tx_power_ctl_setup(dev); + b43_phy_ht_tssi_setup(dev); + b43_phy_ht_tx_power_ctl(dev, saved_tx_pwr_ctl); + + return 0; +} + +static void b43_phy_ht_op_free(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_ht *phy_ht = phy->ht; + + kfree(phy_ht); + phy->ht = NULL; +} + +/* http://bcm-v4.sipsolutions.net/802.11/Radio/Switch%20Radio */ +static void b43_phy_ht_op_software_rfkill(struct b43_wldev *dev, + bool blocked) +{ + if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED) + b43err(dev->wl, "MAC not suspended\n"); + + if (blocked) { + b43_phy_mask(dev, B43_PHY_HT_RF_CTL_CMD, + ~B43_PHY_HT_RF_CTL_CMD_CHIP0_PU); + } else { + if (dev->phy.radio_ver == 0x2059) + b43_radio_2059_init(dev); + else + B43_WARN_ON(1); + + b43_switch_channel(dev, dev->phy.channel); + } +} + +static void b43_phy_ht_op_switch_analog(struct b43_wldev *dev, bool on) +{ + if (on) { + b43_phy_write(dev, B43_PHY_HT_AFE_C1, 0x00cd); + b43_phy_write(dev, B43_PHY_HT_AFE_C1_OVER, 0x0000); + b43_phy_write(dev, B43_PHY_HT_AFE_C2, 0x00cd); + b43_phy_write(dev, B43_PHY_HT_AFE_C2_OVER, 0x0000); + b43_phy_write(dev, B43_PHY_HT_AFE_C3, 0x00cd); + b43_phy_write(dev, B43_PHY_HT_AFE_C3_OVER, 0x0000); + } else { + b43_phy_write(dev, B43_PHY_HT_AFE_C1_OVER, 0x07ff); + b43_phy_write(dev, B43_PHY_HT_AFE_C1, 0x00fd); + b43_phy_write(dev, B43_PHY_HT_AFE_C2_OVER, 0x07ff); + b43_phy_write(dev, B43_PHY_HT_AFE_C2, 0x00fd); + b43_phy_write(dev, B43_PHY_HT_AFE_C3_OVER, 0x07ff); + b43_phy_write(dev, B43_PHY_HT_AFE_C3, 0x00fd); + } +} + +static int b43_phy_ht_op_switch_channel(struct b43_wldev *dev, + unsigned int new_channel) +{ + struct ieee80211_channel *channel = dev->wl->hw->conf.chandef.chan; + enum nl80211_channel_type channel_type = + cfg80211_get_chandef_type(&dev->wl->hw->conf.chandef); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if ((new_channel < 1) || (new_channel > 14)) + return -EINVAL; + } else { + return -EINVAL; + } + + return b43_phy_ht_set_channel(dev, channel, channel_type); +} + +static unsigned int b43_phy_ht_op_get_default_chan(struct b43_wldev *dev) +{ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + return 11; + return 36; +} + +/************************************************** + * R/W ops. + **************************************************/ + +static void b43_phy_ht_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, + u16 set) +{ + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16(dev, B43_MMIO_PHY_DATA, + (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); +} + +static u16 b43_phy_ht_op_radio_read(struct b43_wldev *dev, u16 reg) +{ + /* HT-PHY needs 0x200 for read access */ + reg |= 0x200; + + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); + return b43_read16(dev, B43_MMIO_RADIO24_DATA); +} + +static void b43_phy_ht_op_radio_write(struct b43_wldev *dev, u16 reg, + u16 value) +{ + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); + b43_write16(dev, B43_MMIO_RADIO24_DATA, value); +} + +static enum b43_txpwr_result +b43_phy_ht_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) +{ + return B43_TXPWR_RES_DONE; +} + +static void b43_phy_ht_op_adjust_txpower(struct b43_wldev *dev) +{ +} + +/************************************************** + * PHY ops struct. + **************************************************/ + +const struct b43_phy_operations b43_phyops_ht = { + .allocate = b43_phy_ht_op_allocate, + .free = b43_phy_ht_op_free, + .prepare_structs = b43_phy_ht_op_prepare_structs, + .init = b43_phy_ht_op_init, + .phy_maskset = b43_phy_ht_op_maskset, + .radio_read = b43_phy_ht_op_radio_read, + .radio_write = b43_phy_ht_op_radio_write, + .software_rfkill = b43_phy_ht_op_software_rfkill, + .switch_analog = b43_phy_ht_op_switch_analog, + .switch_channel = b43_phy_ht_op_switch_channel, + .get_default_chan = b43_phy_ht_op_get_default_chan, + .recalc_txpower = b43_phy_ht_op_recalc_txpower, + .adjust_txpower = b43_phy_ht_op_adjust_txpower, +}; diff --git a/drivers/net/wireless/broadcom/b43/phy_ht.h b/drivers/net/wireless/broadcom/b43/phy_ht.h new file mode 100644 index 000000000000..c086f56ce478 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_ht.h @@ -0,0 +1,141 @@ +#ifndef B43_PHY_HT_H_ +#define B43_PHY_HT_H_ + +#include "phy_common.h" + + +#define B43_PHY_HT_BBCFG 0x001 /* BB config */ +#define B43_PHY_HT_BBCFG_RSTCCA 0x4000 /* Reset CCA */ +#define B43_PHY_HT_BBCFG_RSTRX 0x8000 /* Reset RX */ +#define B43_PHY_HT_BANDCTL 0x009 /* Band control */ +#define B43_PHY_HT_BANDCTL_5GHZ 0x0001 /* Use the 5GHz band */ +#define B43_PHY_HT_TABLE_ADDR 0x072 /* Table address */ +#define B43_PHY_HT_TABLE_DATALO 0x073 /* Table data low */ +#define B43_PHY_HT_TABLE_DATAHI 0x074 /* Table data high */ +#define B43_PHY_HT_CLASS_CTL 0x0B0 /* Classifier control */ +#define B43_PHY_HT_CLASS_CTL_CCK_EN 0x0001 /* CCK enable */ +#define B43_PHY_HT_CLASS_CTL_OFDM_EN 0x0002 /* OFDM enable */ +#define B43_PHY_HT_CLASS_CTL_WAITED_EN 0x0004 /* Waited enable */ +#define B43_PHY_HT_IQLOCAL_CMDGCTL 0x0C2 /* I/Q LO cal command G control */ +#define B43_PHY_HT_SAMP_CMD 0x0C3 /* Sample command */ +#define B43_PHY_HT_SAMP_CMD_STOP 0x0002 /* Stop */ +#define B43_PHY_HT_SAMP_LOOP_CNT 0x0C4 /* Sample loop count */ +#define B43_PHY_HT_SAMP_WAIT_CNT 0x0C5 /* Sample wait count */ +#define B43_PHY_HT_SAMP_DEP_CNT 0x0C6 /* Sample depth count */ +#define B43_PHY_HT_SAMP_STAT 0x0C7 /* Sample status */ +#define B43_PHY_HT_EST_PWR_C1 0x118 +#define B43_PHY_HT_EST_PWR_C2 0x119 +#define B43_PHY_HT_EST_PWR_C3 0x11A +#define B43_PHY_HT_TSSIMODE 0x122 /* TSSI mode */ +#define B43_PHY_HT_TSSIMODE_EN 0x0001 /* TSSI enable */ +#define B43_PHY_HT_TSSIMODE_PDEN 0x0002 /* Power det enable */ +#define B43_PHY_HT_BW1 0x1CE +#define B43_PHY_HT_BW2 0x1CF +#define B43_PHY_HT_BW3 0x1D0 +#define B43_PHY_HT_BW4 0x1D1 +#define B43_PHY_HT_BW5 0x1D2 +#define B43_PHY_HT_BW6 0x1D3 +#define B43_PHY_HT_TXPCTL_CMD_C1 0x1E7 /* TX power control command */ +#define B43_PHY_HT_TXPCTL_CMD_C1_INIT 0x007F /* Init */ +#define B43_PHY_HT_TXPCTL_CMD_C1_COEFF 0x2000 /* Power control coefficients */ +#define B43_PHY_HT_TXPCTL_CMD_C1_HWPCTLEN 0x4000 /* Hardware TX power control enable */ +#define B43_PHY_HT_TXPCTL_CMD_C1_PCTLEN 0x8000 /* TX power control enable */ +#define B43_PHY_HT_TXPCTL_N 0x1E8 /* TX power control N num */ +#define B43_PHY_HT_TXPCTL_N_TSSID 0x00FF /* N TSSI delay */ +#define B43_PHY_HT_TXPCTL_N_TSSID_SHIFT 0 +#define B43_PHY_HT_TXPCTL_N_NPTIL2 0x0700 /* N PT integer log2 */ +#define B43_PHY_HT_TXPCTL_N_NPTIL2_SHIFT 8 +#define B43_PHY_HT_TXPCTL_IDLE_TSSI 0x1E9 /* TX power control idle TSSI */ +#define B43_PHY_HT_TXPCTL_IDLE_TSSI_C1 0x003F +#define B43_PHY_HT_TXPCTL_IDLE_TSSI_C1_SHIFT 0 +#define B43_PHY_HT_TXPCTL_IDLE_TSSI_C2 0x3F00 +#define B43_PHY_HT_TXPCTL_IDLE_TSSI_C2_SHIFT 8 +#define B43_PHY_HT_TXPCTL_IDLE_TSSI_BINF 0x8000 /* Raw TSSI offset bin format */ +#define B43_PHY_HT_TXPCTL_TARG_PWR 0x1EA /* TX power control target power */ +#define B43_PHY_HT_TXPCTL_TARG_PWR_C1 0x00FF /* Power 0 */ +#define B43_PHY_HT_TXPCTL_TARG_PWR_C1_SHIFT 0 +#define B43_PHY_HT_TXPCTL_TARG_PWR_C2 0xFF00 /* Power 1 */ +#define B43_PHY_HT_TXPCTL_TARG_PWR_C2_SHIFT 8 +#define B43_PHY_HT_TX_PCTL_STATUS_C1 0x1ED +#define B43_PHY_HT_TX_PCTL_STATUS_C2 0x1EE +#define B43_PHY_HT_TXPCTL_CMD_C2 0x222 +#define B43_PHY_HT_TXPCTL_CMD_C2_INIT 0x007F +#define B43_PHY_HT_RSSI_C1 0x219 +#define B43_PHY_HT_RSSI_C2 0x21A +#define B43_PHY_HT_RSSI_C3 0x21B + +#define B43_PHY_HT_C1_CLIP1THRES B43_PHY_OFDM(0x00E) +#define B43_PHY_HT_C2_CLIP1THRES B43_PHY_OFDM(0x04E) +#define B43_PHY_HT_C3_CLIP1THRES B43_PHY_OFDM(0x08E) + +#define B43_PHY_HT_RF_SEQ_MODE B43_PHY_EXTG(0x000) +#define B43_PHY_HT_RF_SEQ_MODE_CA_OVER 0x0001 /* Core active override */ +#define B43_PHY_HT_RF_SEQ_MODE_TR_OVER 0x0002 /* Trigger override */ +#define B43_PHY_HT_RF_SEQ_TRIG B43_PHY_EXTG(0x003) +#define B43_PHY_HT_RF_SEQ_TRIG_RX2TX 0x0001 /* RX2TX */ +#define B43_PHY_HT_RF_SEQ_TRIG_TX2RX 0x0002 /* TX2RX */ +#define B43_PHY_HT_RF_SEQ_TRIG_UPGH 0x0004 /* Update gain H */ +#define B43_PHY_HT_RF_SEQ_TRIG_UPGL 0x0008 /* Update gain L */ +#define B43_PHY_HT_RF_SEQ_TRIG_UPGU 0x0010 /* Update gain U */ +#define B43_PHY_HT_RF_SEQ_TRIG_RST2RX 0x0020 /* Reset to RX */ +#define B43_PHY_HT_RF_SEQ_STATUS B43_PHY_EXTG(0x004) +/* Values for the status are the same as for the trigger */ + +#define B43_PHY_HT_RF_CTL_CMD 0x810 +#define B43_PHY_HT_RF_CTL_CMD_FORCE 0x0001 +#define B43_PHY_HT_RF_CTL_CMD_CHIP0_PU 0x0002 + +#define B43_PHY_HT_RF_CTL_INT_C1 B43_PHY_EXTG(0x04c) +#define B43_PHY_HT_RF_CTL_INT_C2 B43_PHY_EXTG(0x06c) +#define B43_PHY_HT_RF_CTL_INT_C3 B43_PHY_EXTG(0x08c) + +#define B43_PHY_HT_AFE_C1_OVER B43_PHY_EXTG(0x110) +#define B43_PHY_HT_AFE_C1 B43_PHY_EXTG(0x111) +#define B43_PHY_HT_AFE_C2_OVER B43_PHY_EXTG(0x114) +#define B43_PHY_HT_AFE_C2 B43_PHY_EXTG(0x115) +#define B43_PHY_HT_AFE_C3_OVER B43_PHY_EXTG(0x118) +#define B43_PHY_HT_AFE_C3 B43_PHY_EXTG(0x119) + +#define B43_PHY_HT_TXPCTL_CMD_C3 B43_PHY_EXTG(0x164) +#define B43_PHY_HT_TXPCTL_CMD_C3_INIT 0x007F +#define B43_PHY_HT_TXPCTL_IDLE_TSSI2 B43_PHY_EXTG(0x165) /* TX power control idle TSSI */ +#define B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3 0x003F +#define B43_PHY_HT_TXPCTL_IDLE_TSSI2_C3_SHIFT 0 +#define B43_PHY_HT_TXPCTL_TARG_PWR2 B43_PHY_EXTG(0x166) /* TX power control target power */ +#define B43_PHY_HT_TXPCTL_TARG_PWR2_C3 0x00FF +#define B43_PHY_HT_TXPCTL_TARG_PWR2_C3_SHIFT 0 +#define B43_PHY_HT_TX_PCTL_STATUS_C3 B43_PHY_EXTG(0x169) + +#define B43_PHY_B_BBCFG B43_PHY_N_BMODE(0x001) +#define B43_PHY_B_BBCFG_RSTCCA 0x4000 /* Reset CCA */ +#define B43_PHY_B_BBCFG_RSTRX 0x8000 /* Reset RX */ +#define B43_PHY_HT_TEST B43_PHY_N_BMODE(0x00A) + + +/* Values for PHY registers used on channel switching */ +struct b43_phy_ht_channeltab_e_phy { + u16 bw1; + u16 bw2; + u16 bw3; + u16 bw4; + u16 bw5; + u16 bw6; +}; + + +struct b43_phy_ht { + u16 rf_ctl_int_save[3]; + + bool tx_pwr_ctl; + u8 tx_pwr_idx[3]; + + s32 bb_mult_save[3]; + + u8 idle_tssi[3]; +}; + + +struct b43_phy_operations; +extern const struct b43_phy_operations b43_phyops_ht; + +#endif /* B43_PHY_HT_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/phy_lcn.c b/drivers/net/wireless/broadcom/b43/phy_lcn.c new file mode 100644 index 000000000000..97461ccf3e1e --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_lcn.c @@ -0,0 +1,855 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11n LCN-PHY support + + Copyright (c) 2011 RafaÅ‚ MiÅ‚ecki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + + This file incorporates work covered by the following copyright and + permission notice: + + Copyright (c) 2010 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. +*/ + +#include + +#include "b43.h" +#include "phy_lcn.h" +#include "tables_phy_lcn.h" +#include "main.h" + +struct lcn_tx_gains { + u16 gm_gain; + u16 pga_gain; + u16 pad_gain; + u16 dac_gain; +}; + +struct lcn_tx_iir_filter { + u8 type; + u16 values[16]; +}; + +enum lcn_sense_type { + B43_SENSE_TEMP, + B43_SENSE_VBAT, +}; + +/************************************************** + * Radio 2064. + **************************************************/ + +/* wlc_lcnphy_radio_2064_channel_tune_4313 */ +static void b43_radio_2064_channel_setup(struct b43_wldev *dev) +{ + u16 save[2]; + + b43_radio_set(dev, 0x09d, 0x4); + b43_radio_write(dev, 0x09e, 0xf); + + /* Channel specific values in theory, in practice always the same */ + b43_radio_write(dev, 0x02a, 0xb); + b43_radio_maskset(dev, 0x030, ~0x3, 0xa); + b43_radio_maskset(dev, 0x091, ~0x3, 0); + b43_radio_maskset(dev, 0x038, ~0xf, 0x7); + b43_radio_maskset(dev, 0x030, ~0xc, 0x8); + b43_radio_maskset(dev, 0x05e, ~0xf, 0x8); + b43_radio_maskset(dev, 0x05e, ~0xf0, 0x80); + b43_radio_write(dev, 0x06c, 0x80); + + save[0] = b43_radio_read(dev, 0x044); + save[1] = b43_radio_read(dev, 0x12b); + + b43_radio_set(dev, 0x044, 0x7); + b43_radio_set(dev, 0x12b, 0xe); + + /* TODO */ + + b43_radio_write(dev, 0x040, 0xfb); + + b43_radio_write(dev, 0x041, 0x9a); + b43_radio_write(dev, 0x042, 0xa3); + b43_radio_write(dev, 0x043, 0x0c); + + /* TODO */ + + b43_radio_set(dev, 0x044, 0x0c); + udelay(1); + + b43_radio_write(dev, 0x044, save[0]); + b43_radio_write(dev, 0x12b, save[1]); + + if (dev->phy.rev == 1) { + /* brcmsmac uses outdated 0x3 for 0x038 */ + b43_radio_write(dev, 0x038, 0x0); + b43_radio_write(dev, 0x091, 0x7); + } +} + +/* wlc_radio_2064_init */ +static void b43_radio_2064_init(struct b43_wldev *dev) +{ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_radio_write(dev, 0x09c, 0x0020); + b43_radio_write(dev, 0x105, 0x0008); + } else { + /* TODO */ + } + b43_radio_write(dev, 0x032, 0x0062); + b43_radio_write(dev, 0x033, 0x0019); + b43_radio_write(dev, 0x090, 0x0010); + b43_radio_write(dev, 0x010, 0x0000); + if (dev->phy.rev == 1) { + b43_radio_write(dev, 0x060, 0x007f); + b43_radio_write(dev, 0x061, 0x0072); + b43_radio_write(dev, 0x062, 0x007f); + } + b43_radio_write(dev, 0x01d, 0x0002); + b43_radio_write(dev, 0x01e, 0x0006); + + b43_phy_write(dev, 0x4ea, 0x4688); + b43_phy_maskset(dev, 0x4eb, ~0x7, 0x2); + b43_phy_mask(dev, 0x4eb, ~0x01c0); + b43_phy_maskset(dev, 0x46a, 0xff00, 0x19); + + b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x55), 0); + + b43_radio_mask(dev, 0x05b, (u16) ~0xff02); + b43_radio_set(dev, 0x004, 0x40); + b43_radio_set(dev, 0x120, 0x10); + b43_radio_set(dev, 0x078, 0x80); + b43_radio_set(dev, 0x129, 0x2); + b43_radio_set(dev, 0x057, 0x1); + b43_radio_set(dev, 0x05b, 0x2); + + /* TODO: wait for some bit to be set */ + b43_radio_read(dev, 0x05c); + + b43_radio_mask(dev, 0x05b, (u16) ~0xff02); + b43_radio_mask(dev, 0x057, (u16) ~0xff01); + + b43_phy_write(dev, 0x933, 0x2d6b); + b43_phy_write(dev, 0x934, 0x2d6b); + b43_phy_write(dev, 0x935, 0x2d6b); + b43_phy_write(dev, 0x936, 0x2d6b); + b43_phy_write(dev, 0x937, 0x016b); + + b43_radio_mask(dev, 0x057, (u16) ~0xff02); + b43_radio_write(dev, 0x0c2, 0x006f); +} + +/************************************************** + * Various PHY ops + **************************************************/ + +/* wlc_lcnphy_toggle_afe_pwdn */ +static void b43_phy_lcn_afe_set_unset(struct b43_wldev *dev) +{ + u16 afe_ctl2 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL2); + u16 afe_ctl1 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL1); + + b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 | 0x1); + b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 | 0x1); + + b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 & ~0x1); + b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 & ~0x1); + + b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2); + b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1); +} + +/* wlc_lcnphy_get_pa_gain */ +static u16 b43_phy_lcn_get_pa_gain(struct b43_wldev *dev) +{ + return (b43_phy_read(dev, 0x4fb) & 0x7f00) >> 8; +} + +/* wlc_lcnphy_set_dac_gain */ +static void b43_phy_lcn_set_dac_gain(struct b43_wldev *dev, u16 dac_gain) +{ + u16 dac_ctrl; + + dac_ctrl = b43_phy_read(dev, 0x439); + dac_ctrl = dac_ctrl & 0xc7f; + dac_ctrl = dac_ctrl | (dac_gain << 7); + b43_phy_maskset(dev, 0x439, ~0xfff, dac_ctrl); +} + +/* wlc_lcnphy_set_bbmult */ +static void b43_phy_lcn_set_bbmult(struct b43_wldev *dev, u8 m0) +{ + b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x57), m0 << 8); +} + +/* wlc_lcnphy_clear_tx_power_offsets */ +static void b43_phy_lcn_clear_tx_power_offsets(struct b43_wldev *dev) +{ + u8 i; + + if (1) { /* FIXME */ + b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x340); + for (i = 0; i < 30; i++) { + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0); + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0); + } + } + + b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x80); + for (i = 0; i < 64; i++) { + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0); + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0); + } +} + +/* wlc_lcnphy_rev0_baseband_init */ +static void b43_phy_lcn_rev0_baseband_init(struct b43_wldev *dev) +{ + b43_radio_write(dev, 0x11c, 0); + + b43_phy_write(dev, 0x43b, 0); + b43_phy_write(dev, 0x43c, 0); + b43_phy_write(dev, 0x44c, 0); + b43_phy_write(dev, 0x4e6, 0); + b43_phy_write(dev, 0x4f9, 0); + b43_phy_write(dev, 0x4b0, 0); + b43_phy_write(dev, 0x938, 0); + b43_phy_write(dev, 0x4b0, 0); + b43_phy_write(dev, 0x44e, 0); + + b43_phy_set(dev, 0x567, 0x03); + + b43_phy_set(dev, 0x44a, 0x44); + b43_phy_write(dev, 0x44a, 0x80); + + if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM)) + ; /* TODO */ + b43_phy_maskset(dev, 0x634, ~0xff, 0xc); + if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM) { + b43_phy_maskset(dev, 0x634, ~0xff, 0xa); + b43_phy_write(dev, 0x910, 0x1); + } + + b43_phy_write(dev, 0x910, 0x1); + + b43_phy_maskset(dev, 0x448, ~0x300, 0x100); + b43_phy_maskset(dev, 0x608, ~0xff, 0x17); + b43_phy_maskset(dev, 0x604, ~0x7ff, 0x3ea); +} + +/* wlc_lcnphy_bu_tweaks */ +static void b43_phy_lcn_bu_tweaks(struct b43_wldev *dev) +{ + b43_phy_set(dev, 0x805, 0x1); + + b43_phy_maskset(dev, 0x42f, ~0x7, 0x3); + b43_phy_maskset(dev, 0x030, ~0x7, 0x3); + + b43_phy_write(dev, 0x414, 0x1e10); + b43_phy_write(dev, 0x415, 0x0640); + + b43_phy_maskset(dev, 0x4df, (u16) ~0xff00, 0xf700); + + b43_phy_set(dev, 0x44a, 0x44); + b43_phy_write(dev, 0x44a, 0x80); + + b43_phy_maskset(dev, 0x434, ~0xff, 0xfd); + b43_phy_maskset(dev, 0x420, ~0xff, 0x10); + + if (dev->dev->bus_sprom->board_rev >= 0x1204) + b43_radio_set(dev, 0x09b, 0xf0); + + b43_phy_write(dev, 0x7d6, 0x0902); + + b43_phy_maskset(dev, 0x429, ~0xf, 0x9); + b43_phy_maskset(dev, 0x429, ~(0x3f << 4), 0xe << 4); + + if (dev->phy.rev == 1) { + b43_phy_maskset(dev, 0x423, ~0xff, 0x46); + b43_phy_maskset(dev, 0x411, ~0xff, 1); + b43_phy_set(dev, 0x434, 0xff); /* FIXME: update to wl */ + + /* TODO: wl operates on PHY 0x416, brcmsmac is outdated here */ + + b43_phy_maskset(dev, 0x656, ~0xf, 2); + b43_phy_set(dev, 0x44d, 4); + + b43_radio_set(dev, 0x0f7, 0x4); + b43_radio_mask(dev, 0x0f1, ~0x3); + b43_radio_maskset(dev, 0x0f2, ~0xf8, 0x90); + b43_radio_maskset(dev, 0x0f3, ~0x3, 0x2); + b43_radio_maskset(dev, 0x0f3, ~0xf0, 0xa0); + + b43_radio_set(dev, 0x11f, 0x2); + + b43_phy_lcn_clear_tx_power_offsets(dev); + + /* TODO: something more? */ + } +} + +/* wlc_lcnphy_vbat_temp_sense_setup */ +static void b43_phy_lcn_sense_setup(struct b43_wldev *dev, + enum lcn_sense_type sense_type) +{ + u8 auxpga_vmidcourse, auxpga_vmidfine, auxpga_gain; + u16 auxpga_vmid; + u8 tx_pwr_idx; + u8 i; + + u16 save_radio_regs[6][2] = { + { 0x007, 0 }, { 0x0ff, 0 }, { 0x11f, 0 }, { 0x005, 0 }, + { 0x025, 0 }, { 0x112, 0 }, + }; + u16 save_phy_regs[14][2] = { + { 0x503, 0 }, { 0x4a4, 0 }, { 0x4d0, 0 }, { 0x4d9, 0 }, + { 0x4da, 0 }, { 0x4a6, 0 }, { 0x938, 0 }, { 0x939, 0 }, + { 0x4d8, 0 }, { 0x4d0, 0 }, { 0x4d7, 0 }, { 0x4a5, 0 }, + { 0x40d, 0 }, { 0x4a2, 0 }, + }; + u16 save_radio_4a4; + + msleep(1); + + /* Save */ + for (i = 0; i < 6; i++) + save_radio_regs[i][1] = b43_radio_read(dev, + save_radio_regs[i][0]); + for (i = 0; i < 14; i++) + save_phy_regs[i][1] = b43_phy_read(dev, save_phy_regs[i][0]); + b43_mac_suspend(dev); + save_radio_4a4 = b43_radio_read(dev, 0x4a4); + /* wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); */ + tx_pwr_idx = dev->phy.lcn->tx_pwr_curr_idx; + + /* Setup */ + /* TODO: wlc_lcnphy_set_tx_pwr_by_index(pi, 127); */ + b43_radio_set(dev, 0x007, 0x1); + b43_radio_set(dev, 0x0ff, 0x10); + b43_radio_set(dev, 0x11f, 0x4); + + b43_phy_mask(dev, 0x503, ~0x1); + b43_phy_mask(dev, 0x503, ~0x4); + b43_phy_mask(dev, 0x4a4, ~0x4000); + b43_phy_mask(dev, 0x4a4, (u16) ~0x8000); + b43_phy_mask(dev, 0x4d0, ~0x20); + b43_phy_set(dev, 0x4a5, 0xff); + b43_phy_maskset(dev, 0x4a5, ~0x7000, 0x5000); + b43_phy_mask(dev, 0x4a5, ~0x700); + b43_phy_maskset(dev, 0x40d, ~0xff, 64); + b43_phy_maskset(dev, 0x40d, ~0x700, 0x600); + b43_phy_maskset(dev, 0x4a2, ~0xff, 64); + b43_phy_maskset(dev, 0x4a2, ~0x700, 0x600); + b43_phy_maskset(dev, 0x4d9, ~0x70, 0x20); + b43_phy_maskset(dev, 0x4d9, ~0x700, 0x300); + b43_phy_maskset(dev, 0x4d9, ~0x7000, 0x1000); + b43_phy_mask(dev, 0x4da, ~0x1000); + b43_phy_set(dev, 0x4da, 0x2000); + b43_phy_set(dev, 0x4a6, 0x8000); + + b43_radio_write(dev, 0x025, 0xc); + b43_radio_set(dev, 0x005, 0x8); + b43_phy_set(dev, 0x938, 0x4); + b43_phy_set(dev, 0x939, 0x4); + b43_phy_set(dev, 0x4a4, 0x1000); + + /* FIXME: don't hardcode */ + b43_lcntab_write(dev, B43_LCNTAB16(0x8, 0x6), 0x640); + + switch (sense_type) { + case B43_SENSE_TEMP: + b43_phy_set(dev, 0x4d7, 0x8); + b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x1000); + auxpga_vmidcourse = 8; + auxpga_vmidfine = 0x4; + auxpga_gain = 2; + b43_radio_set(dev, 0x082, 0x20); + break; + case B43_SENSE_VBAT: + b43_phy_set(dev, 0x4d7, 0x8); + b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x3000); + auxpga_vmidcourse = 7; + auxpga_vmidfine = 0xa; + auxpga_gain = 2; + break; + } + auxpga_vmid = (0x200 | (auxpga_vmidcourse << 4) | auxpga_vmidfine); + + b43_phy_set(dev, 0x4d8, 0x1); + b43_phy_maskset(dev, 0x4d8, ~(0x3ff << 2), auxpga_vmid << 2); + b43_phy_set(dev, 0x4d8, 0x2); + b43_phy_maskset(dev, 0x4d8, ~(0x7 << 12), auxpga_gain << 12); + b43_phy_set(dev, 0x4d0, 0x20); + b43_radio_write(dev, 0x112, 0x6); + + b43_dummy_transmission(dev, true, false); + /* Wait if not done */ + if (!(b43_phy_read(dev, 0x476) & 0x8000)) + udelay(10); + + /* Restore */ + for (i = 0; i < 6; i++) + b43_radio_write(dev, save_radio_regs[i][0], + save_radio_regs[i][1]); + for (i = 0; i < 14; i++) + b43_phy_write(dev, save_phy_regs[i][0], save_phy_regs[i][1]); + /* TODO: wlc_lcnphy_set_tx_pwr_by_index(tx_pwr_idx) */ + b43_radio_write(dev, 0x4a4, save_radio_4a4); + + b43_mac_enable(dev); + + msleep(1); +} + +static bool b43_phy_lcn_load_tx_iir_cck_filter(struct b43_wldev *dev, + u8 filter_type) +{ + int i, j; + u16 phy_regs[] = { 0x910, 0x91e, 0x91f, 0x924, 0x925, 0x926, 0x920, + 0x921, 0x927, 0x928, 0x929, 0x922, 0x923, 0x930, + 0x931, 0x932 }; + /* Table is from brcmsmac, values for type 25 were outdated, probably + * others need updating too */ + struct lcn_tx_iir_filter tx_iir_filters_cck[] = { + { 0, { 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778, + 1582, 64, 128, 64 } }, + { 1, { 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608, + 1863, 93, 167, 93 } }, + { 2, { 1, 415, 1874, 64, 128, 64, 792, 1656, 192, 384, 192, + 778, 1582, 64, 128, 64 } }, + { 3, { 1, 302, 1841, 129, 258, 129, 658, 1720, 205, 410, 205, + 754, 1760, 170, 340, 170 } }, + { 20, { 1, 360, 1884, 242, 1734, 242, 752, 1720, 205, 1845, 205, + 767, 1760, 256, 185, 256 } }, + { 21, { 1, 360, 1884, 149, 1874, 149, 752, 1720, 205, 1883, 205, + 767, 1760, 256, 273, 256 } }, + { 22, { 1, 360, 1884, 98, 1948, 98, 752, 1720, 205, 1924, 205, + 767, 1760, 256, 352, 256 } }, + { 23, { 1, 350, 1884, 116, 1966, 116, 752, 1720, 205, 2008, 205, + 767, 1760, 128, 233, 128 } }, + { 24, { 1, 325, 1884, 32, 40, 32, 756, 1720, 256, 471, 256, 766, + 1760, 256, 1881, 256 } }, + { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, 256, 471, 256, 765, + 1760, 262, 1878, 262 } }, + /* brcmsmac version { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, + * 256, 471, 256, 765, 1760, 256, 1881, 256 } }, */ + { 26, { 1, 277, 1943, 39, 117, 88, 637, 1838, 64, 192, 144, 614, + 1864, 128, 384, 288 } }, + { 27, { 1, 245, 1943, 49, 147, 110, 626, 1838, 256, 768, 576, + 613, 1864, 128, 384, 288 } }, + { 30, { 1, 302, 1841, 61, 122, 61, 658, 1720, 205, 410, 205, + 754, 1760, 170, 340, 170 } }, + }; + + for (i = 0; i < ARRAY_SIZE(tx_iir_filters_cck); i++) { + if (tx_iir_filters_cck[i].type == filter_type) { + for (j = 0; j < 16; j++) + b43_phy_write(dev, phy_regs[j], + tx_iir_filters_cck[i].values[j]); + return true; + } + } + + return false; +} + +static bool b43_phy_lcn_load_tx_iir_ofdm_filter(struct b43_wldev *dev, + u8 filter_type) +{ + int i, j; + u16 phy_regs[] = { 0x90f, 0x900, 0x901, 0x906, 0x907, 0x908, 0x902, + 0x903, 0x909, 0x90a, 0x90b, 0x904, 0x905, 0x90c, + 0x90d, 0x90e }; + struct lcn_tx_iir_filter tx_iir_filters_ofdm[] = { + { 0, { 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0, + 0x0, 0x278, 0xfea0, 0x80, 0x100, 0x80 } }, + { 1, { 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50, 750, + 0xFE2B, 212, 0xFFCE, 212 } }, + { 2, { 0, 375, 0xFF16, 37, 76, 37, 799, 0xFE74, 32, 20, 32, 748, + 0xFEF2, 128, 0xFFE2, 128 } }, + }; + + for (i = 0; i < ARRAY_SIZE(tx_iir_filters_ofdm); i++) { + if (tx_iir_filters_ofdm[i].type == filter_type) { + for (j = 0; j < 16; j++) + b43_phy_write(dev, phy_regs[j], + tx_iir_filters_ofdm[i].values[j]); + return true; + } + } + + return false; +} + +/* wlc_lcnphy_set_tx_gain_override */ +static void b43_phy_lcn_set_tx_gain_override(struct b43_wldev *dev, bool enable) +{ + b43_phy_maskset(dev, 0x4b0, ~(0x1 << 7), enable << 7); + b43_phy_maskset(dev, 0x4b0, ~(0x1 << 14), enable << 14); + b43_phy_maskset(dev, 0x43b, ~(0x1 << 6), enable << 6); +} + +/* wlc_lcnphy_set_tx_gain */ +static void b43_phy_lcn_set_tx_gain(struct b43_wldev *dev, + struct lcn_tx_gains *target_gains) +{ + u16 pa_gain = b43_phy_lcn_get_pa_gain(dev); + + b43_phy_write(dev, 0x4b5, + (target_gains->gm_gain | (target_gains->pga_gain << 8))); + b43_phy_maskset(dev, 0x4fb, ~0x7fff, + (target_gains->pad_gain | (pa_gain << 8))); + b43_phy_write(dev, 0x4fc, + (target_gains->gm_gain | (target_gains->pga_gain << 8))); + b43_phy_maskset(dev, 0x4fd, ~0x7fff, + (target_gains->pad_gain | (pa_gain << 8))); + + b43_phy_lcn_set_dac_gain(dev, target_gains->dac_gain); + b43_phy_lcn_set_tx_gain_override(dev, true); +} + +/* wlc_lcnphy_tx_pwr_ctrl_init */ +static void b43_phy_lcn_tx_pwr_ctl_init(struct b43_wldev *dev) +{ + struct lcn_tx_gains tx_gains; + u8 bbmult; + + b43_mac_suspend(dev); + + if (!dev->phy.lcn->hw_pwr_ctl_capable) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + tx_gains.gm_gain = 4; + tx_gains.pga_gain = 12; + tx_gains.pad_gain = 12; + tx_gains.dac_gain = 0; + bbmult = 150; + } else { + tx_gains.gm_gain = 7; + tx_gains.pga_gain = 15; + tx_gains.pad_gain = 14; + tx_gains.dac_gain = 0; + bbmult = 150; + } + b43_phy_lcn_set_tx_gain(dev, &tx_gains); + b43_phy_lcn_set_bbmult(dev, bbmult); + b43_phy_lcn_sense_setup(dev, B43_SENSE_TEMP); + } else { + b43err(dev->wl, "TX power control not supported for this HW\n"); + } + + b43_mac_enable(dev); +} + +/* wlc_lcnphy_txrx_spur_avoidance_mode */ +static void b43_phy_lcn_txrx_spur_avoidance_mode(struct b43_wldev *dev, + bool enable) +{ + if (enable) { + b43_phy_write(dev, 0x942, 0x7); + b43_phy_write(dev, 0x93b, ((1 << 13) + 23)); + b43_phy_write(dev, 0x93c, ((1 << 13) + 1989)); + + b43_phy_write(dev, 0x44a, 0x084); + b43_phy_write(dev, 0x44a, 0x080); + b43_phy_write(dev, 0x6d3, 0x2222); + b43_phy_write(dev, 0x6d3, 0x2220); + } else { + b43_phy_write(dev, 0x942, 0x0); + b43_phy_write(dev, 0x93b, ((0 << 13) + 23)); + b43_phy_write(dev, 0x93c, ((0 << 13) + 1989)); + } + b43_mac_switch_freq(dev, enable); +} + +/************************************************** + * Channel switching ops. + **************************************************/ + +/* wlc_lcnphy_set_chanspec_tweaks */ +static void b43_phy_lcn_set_channel_tweaks(struct b43_wldev *dev, int channel) +{ + struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc; + + b43_phy_maskset(dev, 0x448, ~0x300, (channel == 14) ? 0x200 : 0x100); + + if (channel == 1 || channel == 2 || channel == 3 || channel == 4 || + channel == 9 || channel == 10 || channel == 11 || channel == 12) { + bcma_chipco_pll_write(cc, 0x2, 0x03000c04); + bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x0); + bcma_chipco_pll_write(cc, 0x4, 0x200005c0); + + bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400); + + b43_phy_write(dev, 0x942, 0); + + b43_phy_lcn_txrx_spur_avoidance_mode(dev, false); + b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1b00); + b43_phy_write(dev, 0x425, 0x5907); + } else { + bcma_chipco_pll_write(cc, 0x2, 0x03140c04); + bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x333333); + bcma_chipco_pll_write(cc, 0x4, 0x202c2820); + + bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400); + + b43_phy_write(dev, 0x942, 0); + + b43_phy_lcn_txrx_spur_avoidance_mode(dev, true); + b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1f00); + b43_phy_write(dev, 0x425, 0x590a); + } + + b43_phy_set(dev, 0x44a, 0x44); + b43_phy_write(dev, 0x44a, 0x80); +} + +/* wlc_phy_chanspec_set_lcnphy */ +static int b43_phy_lcn_set_channel(struct b43_wldev *dev, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type) +{ + static const u16 sfo_cfg[14][2] = { + {965, 1087}, {967, 1085}, {969, 1082}, {971, 1080}, {973, 1078}, + {975, 1076}, {977, 1073}, {979, 1071}, {981, 1069}, {983, 1067}, + {985, 1065}, {987, 1063}, {989, 1060}, {994, 1055}, + }; + + b43_phy_lcn_set_channel_tweaks(dev, channel->hw_value); + + b43_phy_set(dev, 0x44a, 0x44); + b43_phy_write(dev, 0x44a, 0x80); + + b43_radio_2064_channel_setup(dev); + mdelay(1); + + b43_phy_lcn_afe_set_unset(dev); + + b43_phy_write(dev, 0x657, sfo_cfg[channel->hw_value - 1][0]); + b43_phy_write(dev, 0x658, sfo_cfg[channel->hw_value - 1][1]); + + if (channel->hw_value == 14) { + b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (2) << 8); + b43_phy_lcn_load_tx_iir_cck_filter(dev, 3); + } else { + b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (1) << 8); + /* brcmsmac uses filter_type 2, we follow wl with 25 */ + b43_phy_lcn_load_tx_iir_cck_filter(dev, 25); + } + /* brcmsmac uses filter_type 2, we follow wl with 0 */ + b43_phy_lcn_load_tx_iir_ofdm_filter(dev, 0); + + b43_phy_maskset(dev, 0x4eb, ~(0x7 << 3), 0x1 << 3); + + return 0; +} + +/************************************************** + * Basic PHY ops. + **************************************************/ + +static int b43_phy_lcn_op_allocate(struct b43_wldev *dev) +{ + struct b43_phy_lcn *phy_lcn; + + phy_lcn = kzalloc(sizeof(*phy_lcn), GFP_KERNEL); + if (!phy_lcn) + return -ENOMEM; + dev->phy.lcn = phy_lcn; + + return 0; +} + +static void b43_phy_lcn_op_free(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_lcn *phy_lcn = phy->lcn; + + kfree(phy_lcn); + phy->lcn = NULL; +} + +static void b43_phy_lcn_op_prepare_structs(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_lcn *phy_lcn = phy->lcn; + + memset(phy_lcn, 0, sizeof(*phy_lcn)); +} + +/* wlc_phy_init_lcnphy */ +static int b43_phy_lcn_op_init(struct b43_wldev *dev) +{ + struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc; + + b43_phy_set(dev, 0x44a, 0x80); + b43_phy_mask(dev, 0x44a, 0x7f); + b43_phy_set(dev, 0x6d1, 0x80); + b43_phy_write(dev, 0x6d0, 0x7); + + b43_phy_lcn_afe_set_unset(dev); + + b43_phy_write(dev, 0x60a, 0xa0); + b43_phy_write(dev, 0x46a, 0x19); + b43_phy_maskset(dev, 0x663, 0xFF00, 0x64); + + b43_phy_lcn_tables_init(dev); + + b43_phy_lcn_rev0_baseband_init(dev); + b43_phy_lcn_bu_tweaks(dev); + + if (dev->phy.radio_ver == 0x2064) + b43_radio_2064_init(dev); + else + B43_WARN_ON(1); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_phy_lcn_tx_pwr_ctl_init(dev); + + b43_switch_channel(dev, dev->phy.channel); + + bcma_chipco_regctl_maskset(cc, 0, 0xf, 0x9); + bcma_chipco_chipctl_maskset(cc, 0, 0, 0x03cddddd); + + /* TODO */ + + b43_phy_set(dev, 0x448, 0x4000); + udelay(100); + b43_phy_mask(dev, 0x448, ~0x4000); + + /* TODO */ + + return 0; +} + +static void b43_phy_lcn_op_software_rfkill(struct b43_wldev *dev, + bool blocked) +{ + if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED) + b43err(dev->wl, "MAC not suspended\n"); + + if (blocked) { + b43_phy_mask(dev, B43_PHY_LCN_RF_CTL2, ~0x7c00); + b43_phy_set(dev, B43_PHY_LCN_RF_CTL1, 0x1f00); + + b43_phy_mask(dev, B43_PHY_LCN_RF_CTL5, ~0x7f00); + b43_phy_mask(dev, B43_PHY_LCN_RF_CTL4, ~0x2); + b43_phy_set(dev, B43_PHY_LCN_RF_CTL3, 0x808); + + b43_phy_mask(dev, B43_PHY_LCN_RF_CTL7, ~0x8); + b43_phy_set(dev, B43_PHY_LCN_RF_CTL6, 0x8); + } else { + b43_phy_mask(dev, B43_PHY_LCN_RF_CTL1, ~0x1f00); + b43_phy_mask(dev, B43_PHY_LCN_RF_CTL3, ~0x808); + b43_phy_mask(dev, B43_PHY_LCN_RF_CTL6, ~0x8); + } +} + +static void b43_phy_lcn_op_switch_analog(struct b43_wldev *dev, bool on) +{ + if (on) { + b43_phy_mask(dev, B43_PHY_LCN_AFE_CTL1, ~0x7); + } else { + b43_phy_set(dev, B43_PHY_LCN_AFE_CTL2, 0x7); + b43_phy_set(dev, B43_PHY_LCN_AFE_CTL1, 0x7); + } +} + +static int b43_phy_lcn_op_switch_channel(struct b43_wldev *dev, + unsigned int new_channel) +{ + struct ieee80211_channel *channel = dev->wl->hw->conf.chandef.chan; + enum nl80211_channel_type channel_type = + cfg80211_get_chandef_type(&dev->wl->hw->conf.chandef); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if ((new_channel < 1) || (new_channel > 14)) + return -EINVAL; + } else { + return -EINVAL; + } + + return b43_phy_lcn_set_channel(dev, channel, channel_type); +} + +static unsigned int b43_phy_lcn_op_get_default_chan(struct b43_wldev *dev) +{ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + return 1; + return 36; +} + +static enum b43_txpwr_result +b43_phy_lcn_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) +{ + return B43_TXPWR_RES_DONE; +} + +static void b43_phy_lcn_op_adjust_txpower(struct b43_wldev *dev) +{ +} + +/************************************************** + * R/W ops. + **************************************************/ + +static void b43_phy_lcn_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, + u16 set) +{ + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16(dev, B43_MMIO_PHY_DATA, + (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); +} + +static u16 b43_phy_lcn_op_radio_read(struct b43_wldev *dev, u16 reg) +{ + /* LCN-PHY needs 0x200 for read access */ + reg |= 0x200; + + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); + return b43_read16(dev, B43_MMIO_RADIO24_DATA); +} + +static void b43_phy_lcn_op_radio_write(struct b43_wldev *dev, u16 reg, + u16 value) +{ + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); + b43_write16(dev, B43_MMIO_RADIO24_DATA, value); +} + +/************************************************** + * PHY ops struct. + **************************************************/ + +const struct b43_phy_operations b43_phyops_lcn = { + .allocate = b43_phy_lcn_op_allocate, + .free = b43_phy_lcn_op_free, + .prepare_structs = b43_phy_lcn_op_prepare_structs, + .init = b43_phy_lcn_op_init, + .phy_maskset = b43_phy_lcn_op_maskset, + .radio_read = b43_phy_lcn_op_radio_read, + .radio_write = b43_phy_lcn_op_radio_write, + .software_rfkill = b43_phy_lcn_op_software_rfkill, + .switch_analog = b43_phy_lcn_op_switch_analog, + .switch_channel = b43_phy_lcn_op_switch_channel, + .get_default_chan = b43_phy_lcn_op_get_default_chan, + .recalc_txpower = b43_phy_lcn_op_recalc_txpower, + .adjust_txpower = b43_phy_lcn_op_adjust_txpower, +}; diff --git a/drivers/net/wireless/broadcom/b43/phy_lcn.h b/drivers/net/wireless/broadcom/b43/phy_lcn.h new file mode 100644 index 000000000000..6a7092e13fff --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_lcn.h @@ -0,0 +1,31 @@ +#ifndef B43_PHY_LCN_H_ +#define B43_PHY_LCN_H_ + +#include "phy_common.h" + + +#define B43_PHY_LCN_AFE_CTL1 B43_PHY_OFDM(0x03B) +#define B43_PHY_LCN_AFE_CTL2 B43_PHY_OFDM(0x03C) +#define B43_PHY_LCN_RF_CTL1 B43_PHY_OFDM(0x04C) +#define B43_PHY_LCN_RF_CTL2 B43_PHY_OFDM(0x04D) +#define B43_PHY_LCN_TABLE_ADDR B43_PHY_OFDM(0x055) /* Table address */ +#define B43_PHY_LCN_TABLE_DATALO B43_PHY_OFDM(0x056) /* Table data low */ +#define B43_PHY_LCN_TABLE_DATAHI B43_PHY_OFDM(0x057) /* Table data high */ +#define B43_PHY_LCN_RF_CTL3 B43_PHY_OFDM(0x0B0) +#define B43_PHY_LCN_RF_CTL4 B43_PHY_OFDM(0x0B1) +#define B43_PHY_LCN_RF_CTL5 B43_PHY_OFDM(0x0B7) +#define B43_PHY_LCN_RF_CTL6 B43_PHY_OFDM(0x0F9) +#define B43_PHY_LCN_RF_CTL7 B43_PHY_OFDM(0x0FA) + + +struct b43_phy_lcn { + bool hw_pwr_ctl; + bool hw_pwr_ctl_capable; + u8 tx_pwr_curr_idx; +}; + + +struct b43_phy_operations; +extern const struct b43_phy_operations b43_phyops_lcn; + +#endif /* B43_PHY_LCN_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/phy_lp.c b/drivers/net/wireless/broadcom/b43/phy_lp.c new file mode 100644 index 000000000000..058a9f232050 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_lp.c @@ -0,0 +1,2716 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11a/g LP-PHY driver + + Copyright (c) 2008-2009 Michael Buesch + Copyright (c) 2009 Gábor Stefanik + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include + +#include "b43.h" +#include "main.h" +#include "phy_lp.h" +#include "phy_common.h" +#include "tables_lpphy.h" + + +static inline u16 channel2freq_lp(u8 channel) +{ + if (channel < 14) + return (2407 + 5 * channel); + else if (channel == 14) + return 2484; + else if (channel < 184) + return (5000 + 5 * channel); + else + return (4000 + 5 * channel); +} + +static unsigned int b43_lpphy_op_get_default_chan(struct b43_wldev *dev) +{ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + return 1; + return 36; +} + +static int b43_lpphy_op_allocate(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy; + + lpphy = kzalloc(sizeof(*lpphy), GFP_KERNEL); + if (!lpphy) + return -ENOMEM; + dev->phy.lp = lpphy; + + return 0; +} + +static void b43_lpphy_op_prepare_structs(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_lp *lpphy = phy->lp; + + memset(lpphy, 0, sizeof(*lpphy)); + lpphy->antenna = B43_ANTENNA_DEFAULT; + + //TODO +} + +static void b43_lpphy_op_free(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + + kfree(lpphy); + dev->phy.lp = NULL; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/LP/ReadBandSrom */ +static void lpphy_read_band_sprom(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + struct b43_phy_lp *lpphy = dev->phy.lp; + u16 cckpo, maxpwr; + u32 ofdmpo; + int i; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + lpphy->tx_isolation_med_band = sprom->tri2g; + lpphy->bx_arch = sprom->bxa2g; + lpphy->rx_pwr_offset = sprom->rxpo2g; + lpphy->rssi_vf = sprom->rssismf2g; + lpphy->rssi_vc = sprom->rssismc2g; + lpphy->rssi_gs = sprom->rssisav2g; + lpphy->txpa[0] = sprom->pa0b0; + lpphy->txpa[1] = sprom->pa0b1; + lpphy->txpa[2] = sprom->pa0b2; + maxpwr = sprom->maxpwr_bg; + lpphy->max_tx_pwr_med_band = maxpwr; + cckpo = sprom->cck2gpo; + if (cckpo) { + ofdmpo = sprom->ofdm2gpo; + for (i = 0; i < 4; i++) { + lpphy->tx_max_rate[i] = + maxpwr - (ofdmpo & 0xF) * 2; + ofdmpo >>= 4; + } + ofdmpo = sprom->ofdm2gpo; + for (i = 4; i < 15; i++) { + lpphy->tx_max_rate[i] = + maxpwr - (ofdmpo & 0xF) * 2; + ofdmpo >>= 4; + } + } else { + u8 opo = sprom->opo; + for (i = 0; i < 4; i++) + lpphy->tx_max_rate[i] = maxpwr; + for (i = 4; i < 15; i++) + lpphy->tx_max_rate[i] = maxpwr - opo; + } + } else { /* 5GHz */ + lpphy->tx_isolation_low_band = sprom->tri5gl; + lpphy->tx_isolation_med_band = sprom->tri5g; + lpphy->tx_isolation_hi_band = sprom->tri5gh; + lpphy->bx_arch = sprom->bxa5g; + lpphy->rx_pwr_offset = sprom->rxpo5g; + lpphy->rssi_vf = sprom->rssismf5g; + lpphy->rssi_vc = sprom->rssismc5g; + lpphy->rssi_gs = sprom->rssisav5g; + lpphy->txpa[0] = sprom->pa1b0; + lpphy->txpa[1] = sprom->pa1b1; + lpphy->txpa[2] = sprom->pa1b2; + lpphy->txpal[0] = sprom->pa1lob0; + lpphy->txpal[1] = sprom->pa1lob1; + lpphy->txpal[2] = sprom->pa1lob2; + lpphy->txpah[0] = sprom->pa1hib0; + lpphy->txpah[1] = sprom->pa1hib1; + lpphy->txpah[2] = sprom->pa1hib2; + maxpwr = sprom->maxpwr_al; + ofdmpo = sprom->ofdm5glpo; + lpphy->max_tx_pwr_low_band = maxpwr; + for (i = 4; i < 12; i++) { + lpphy->tx_max_ratel[i] = maxpwr - (ofdmpo & 0xF) * 2; + ofdmpo >>= 4; + } + maxpwr = sprom->maxpwr_a; + ofdmpo = sprom->ofdm5gpo; + lpphy->max_tx_pwr_med_band = maxpwr; + for (i = 4; i < 12; i++) { + lpphy->tx_max_rate[i] = maxpwr - (ofdmpo & 0xF) * 2; + ofdmpo >>= 4; + } + maxpwr = sprom->maxpwr_ah; + ofdmpo = sprom->ofdm5ghpo; + lpphy->max_tx_pwr_hi_band = maxpwr; + for (i = 4; i < 12; i++) { + lpphy->tx_max_rateh[i] = maxpwr - (ofdmpo & 0xF) * 2; + ofdmpo >>= 4; + } + } +} + +static void lpphy_adjust_gain_table(struct b43_wldev *dev, u32 freq) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + u16 temp[3]; + u16 isolation; + + B43_WARN_ON(dev->phy.rev >= 2); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + isolation = lpphy->tx_isolation_med_band; + else if (freq <= 5320) + isolation = lpphy->tx_isolation_low_band; + else if (freq <= 5700) + isolation = lpphy->tx_isolation_med_band; + else + isolation = lpphy->tx_isolation_hi_band; + + temp[0] = ((isolation - 26) / 12) << 12; + temp[1] = temp[0] + 0x1000; + temp[2] = temp[0] + 0x2000; + + b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), 3, temp); + b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), 3, temp); +} + +static void lpphy_table_init(struct b43_wldev *dev) +{ + u32 freq = channel2freq_lp(b43_lpphy_op_get_default_chan(dev)); + + if (dev->phy.rev < 2) + lpphy_rev0_1_table_init(dev); + else + lpphy_rev2plus_table_init(dev); + + lpphy_init_tx_gain_table(dev); + + if (dev->phy.rev < 2) + lpphy_adjust_gain_table(dev, freq); +} + +static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->sdev->bus; + struct ssb_sprom *sprom = dev->dev->bus_sprom; + struct b43_phy_lp *lpphy = dev->phy.lp; + u16 tmp, tmp2; + + b43_phy_mask(dev, B43_LPPHY_AFE_DAC_CTL, 0xF7FF); + b43_phy_write(dev, B43_LPPHY_AFE_CTL, 0); + b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, 0); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, 0); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0); + b43_phy_set(dev, B43_LPPHY_AFE_DAC_CTL, 0x0004); + b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0x0078); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x5800); + b43_phy_write(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x0016); + b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_0, 0xFFF8, 0x0004); + b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5400); + b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2400); + b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100); + b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0x0006); + b43_phy_mask(dev, B43_LPPHY_RX_RADIO_CTL, 0xFFFE); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x0005); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0x0180); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x3C00); + b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFFF0, 0x0005); + b43_phy_maskset(dev, B43_LPPHY_GAIN_MISMATCH_LIMIT, 0xFFC0, 0x001A); + b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0xFF00, 0x00B3); + b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00); + b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB, + 0xFF00, lpphy->rx_pwr_offset); + if ((sprom->boardflags_lo & B43_BFL_FEM) && + ((b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) || + (sprom->boardflags_hi & B43_BFH_PAREF))) { + ssb_pmu_set_ldo_voltage(&bus->chipco, LDO_PAREF, 0x28); + ssb_pmu_set_ldo_paref(&bus->chipco, true); + if (dev->phy.rev == 0) { + b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT, + 0xFFCF, 0x0010); + } + b43_lptab_write(dev, B43_LPTAB16(11, 7), 60); + } else { + ssb_pmu_set_ldo_paref(&bus->chipco, false); + b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT, + 0xFFCF, 0x0020); + b43_lptab_write(dev, B43_LPTAB16(11, 7), 100); + } + tmp = lpphy->rssi_vf | lpphy->rssi_vc << 4 | 0xA000; + b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, tmp); + if (sprom->boardflags_hi & B43_BFH_RSSIINV) + b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x0AAA); + else + b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x02AA); + b43_lptab_write(dev, B43_LPTAB16(11, 1), 24); + b43_phy_maskset(dev, B43_LPPHY_RX_RADIO_CTL, + 0xFFF9, (lpphy->bx_arch << 1)); + if (dev->phy.rev == 1 && + (sprom->boardflags_hi & B43_BFH_FEM_BT)) { + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0x3F00, 0x0900); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0400); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0B00); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xC0FF, 0x0900); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xC0FF, 0x0B00); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xC0FF, 0x0900); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xC0FF, 0x0B00); + } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ || + (dev->dev->board_type == SSB_BOARD_BU4312) || + (dev->phy.rev == 0 && (sprom->boardflags_lo & B43_BFL_FEM))) { + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0001); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0400); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0001); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0500); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0800); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0A00); + } else if (dev->phy.rev == 1 || + (sprom->boardflags_lo & B43_BFL_FEM)) { + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0004); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0800); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0004); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0C00); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0100); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0300); + } else { + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0900); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0006); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0500); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0006); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0700); + } + if (dev->phy.rev == 1 && (sprom->boardflags_hi & B43_BFH_PAREF)) { + b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_5, B43_LPPHY_TR_LOOKUP_1); + b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_6, B43_LPPHY_TR_LOOKUP_2); + b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_7, B43_LPPHY_TR_LOOKUP_3); + b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_8, B43_LPPHY_TR_LOOKUP_4); + } + if ((sprom->boardflags_hi & B43_BFH_FEM_BT) && + (dev->dev->chip_id == 0x5354) && + (dev->dev->chip_pkg == SSB_CHIPPACK_BCM4712S)) { + b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0006); + b43_phy_write(dev, B43_LPPHY_GPIO_SELECT, 0x0005); + b43_phy_write(dev, B43_LPPHY_GPIO_OUTEN, 0xFFFF); + //FIXME the Broadcom driver caches & delays this HF write! + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_PR45960W); + } + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x8000); + b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0040); + b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0xA400); + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xF0FF, 0x0B00); + b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x0007); + b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFF8, 0x0003); + b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFC7, 0x0020); + b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF); + } else { /* 5GHz */ + b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0x7FFF); + b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFBF); + } + if (dev->phy.rev == 1) { + tmp = b43_phy_read(dev, B43_LPPHY_CLIPCTRTHRESH); + tmp2 = (tmp & 0x03E0) >> 5; + tmp2 |= tmp2 << 5; + b43_phy_write(dev, B43_LPPHY_4C3, tmp2); + tmp = b43_phy_read(dev, B43_LPPHY_GAINDIRECTMISMATCH); + tmp2 = (tmp & 0x1F00) >> 8; + tmp2 |= tmp2 << 5; + b43_phy_write(dev, B43_LPPHY_4C4, tmp2); + tmp = b43_phy_read(dev, B43_LPPHY_VERYLOWGAINDB); + tmp2 = tmp & 0x00FF; + tmp2 |= tmp << 8; + b43_phy_write(dev, B43_LPPHY_4C5, tmp2); + } +} + +static void lpphy_save_dig_flt_state(struct b43_wldev *dev) +{ + static const u16 addr[] = { + B43_PHY_OFDM(0xC1), + B43_PHY_OFDM(0xC2), + B43_PHY_OFDM(0xC3), + B43_PHY_OFDM(0xC4), + B43_PHY_OFDM(0xC5), + B43_PHY_OFDM(0xC6), + B43_PHY_OFDM(0xC7), + B43_PHY_OFDM(0xC8), + B43_PHY_OFDM(0xCF), + }; + + static const u16 coefs[] = { + 0xDE5E, 0xE832, 0xE331, 0x4D26, + 0x0026, 0x1420, 0x0020, 0xFE08, + 0x0008, + }; + + struct b43_phy_lp *lpphy = dev->phy.lp; + int i; + + for (i = 0; i < ARRAY_SIZE(addr); i++) { + lpphy->dig_flt_state[i] = b43_phy_read(dev, addr[i]); + b43_phy_write(dev, addr[i], coefs[i]); + } +} + +static void lpphy_restore_dig_flt_state(struct b43_wldev *dev) +{ + static const u16 addr[] = { + B43_PHY_OFDM(0xC1), + B43_PHY_OFDM(0xC2), + B43_PHY_OFDM(0xC3), + B43_PHY_OFDM(0xC4), + B43_PHY_OFDM(0xC5), + B43_PHY_OFDM(0xC6), + B43_PHY_OFDM(0xC7), + B43_PHY_OFDM(0xC8), + B43_PHY_OFDM(0xCF), + }; + + struct b43_phy_lp *lpphy = dev->phy.lp; + int i; + + for (i = 0; i < ARRAY_SIZE(addr); i++) + b43_phy_write(dev, addr[i], lpphy->dig_flt_state[i]); +} + +static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + + b43_phy_write(dev, B43_LPPHY_AFE_DAC_CTL, 0x50); + b43_phy_write(dev, B43_LPPHY_AFE_CTL, 0x8800); + b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, 0); + b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, 0); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0); + b43_phy_write(dev, B43_PHY_OFDM(0xF9), 0); + b43_phy_write(dev, B43_LPPHY_TR_LOOKUP_1, 0); + b43_phy_set(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x10); + b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0xB4); + b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xF8FF, 0x200); + b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xFF00, 0x7F); + b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFF0F, 0x40); + b43_phy_maskset(dev, B43_LPPHY_PREAMBLECONFIRMTO, 0xFF00, 0x2); + b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x4000); + b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x2000); + b43_phy_set(dev, B43_PHY_OFDM(0x10A), 0x1); + if (dev->dev->board_rev >= 0x18) { + b43_lptab_write(dev, B43_LPTAB32(17, 65), 0xEC); + b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x14); + } else { + b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x10); + } + b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0xFF00, 0xF4); + b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0x00FF, 0xF100); + b43_phy_write(dev, B43_LPPHY_CLIPTHRESH, 0x48); + b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0xFF00, 0x46); + b43_phy_maskset(dev, B43_PHY_OFDM(0xE4), 0xFF00, 0x10); + b43_phy_maskset(dev, B43_LPPHY_PWR_THRESH1, 0xFFF0, 0x9); + b43_phy_mask(dev, B43_LPPHY_GAINDIRECTMISMATCH, ~0xF); + b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5500); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0xA0); + b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xE0FF, 0x300); + b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2A00); + if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { + b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100); + b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0xA); + } else { + b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x1E00); + b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0xD); + } + b43_phy_maskset(dev, B43_PHY_OFDM(0xFE), 0xFFE0, 0x1F); + b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0xFFE0, 0xC); + b43_phy_maskset(dev, B43_PHY_OFDM(0x100), 0xFF00, 0x19); + b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0x03FF, 0x3C00); + b43_phy_maskset(dev, B43_PHY_OFDM(0xFE), 0xFC1F, 0x3E0); + b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0xFFE0, 0xC); + b43_phy_maskset(dev, B43_PHY_OFDM(0x100), 0x00FF, 0x1900); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x5800); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x12); + b43_phy_maskset(dev, B43_LPPHY_GAINMISMATCH, 0x0FFF, 0x9000); + + if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { + b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0); + b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40); + } + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x40); + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xF0FF, 0xB00); + b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x6); + b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0x9D00); + b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0xFF00, 0xA1); + b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF); + } else /* 5GHz */ + b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x40); + + b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0xFF00, 0xB3); + b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00); + b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB, 0xFF00, lpphy->rx_pwr_offset); + b43_phy_set(dev, B43_LPPHY_RESET_CTL, 0x44); + b43_phy_write(dev, B43_LPPHY_RESET_CTL, 0x80); + b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, 0xA954); + b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_1, + 0x2000 | ((u16)lpphy->rssi_gs << 10) | + ((u16)lpphy->rssi_vc << 4) | lpphy->rssi_vf); + + if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { + b43_phy_set(dev, B43_LPPHY_AFE_ADC_CTL_0, 0x1C); + b43_phy_maskset(dev, B43_LPPHY_AFE_CTL, 0x00FF, 0x8800); + b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_1, 0xFC3C, 0x0400); + } + + lpphy_save_dig_flt_state(dev); +} + +static void lpphy_baseband_init(struct b43_wldev *dev) +{ + lpphy_table_init(dev); + if (dev->phy.rev >= 2) + lpphy_baseband_rev2plus_init(dev); + else + lpphy_baseband_rev0_1_init(dev); +} + +struct b2062_freqdata { + u16 freq; + u8 data[6]; +}; + +/* Initialize the 2062 radio. */ +static void lpphy_2062_init(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + struct ssb_bus *bus = dev->dev->sdev->bus; + u32 crystalfreq, tmp, ref; + unsigned int i; + const struct b2062_freqdata *fd = NULL; + + static const struct b2062_freqdata freqdata_tab[] = { + { .freq = 12000, .data[0] = 6, .data[1] = 6, .data[2] = 6, + .data[3] = 6, .data[4] = 10, .data[5] = 6, }, + { .freq = 13000, .data[0] = 4, .data[1] = 4, .data[2] = 4, + .data[3] = 4, .data[4] = 11, .data[5] = 7, }, + { .freq = 14400, .data[0] = 3, .data[1] = 3, .data[2] = 3, + .data[3] = 3, .data[4] = 12, .data[5] = 7, }, + { .freq = 16200, .data[0] = 3, .data[1] = 3, .data[2] = 3, + .data[3] = 3, .data[4] = 13, .data[5] = 8, }, + { .freq = 18000, .data[0] = 2, .data[1] = 2, .data[2] = 2, + .data[3] = 2, .data[4] = 14, .data[5] = 8, }, + { .freq = 19200, .data[0] = 1, .data[1] = 1, .data[2] = 1, + .data[3] = 1, .data[4] = 14, .data[5] = 9, }, + }; + + b2062_upload_init_table(dev); + + b43_radio_write(dev, B2062_N_TX_CTL3, 0); + b43_radio_write(dev, B2062_N_TX_CTL4, 0); + b43_radio_write(dev, B2062_N_TX_CTL5, 0); + b43_radio_write(dev, B2062_N_TX_CTL6, 0); + b43_radio_write(dev, B2062_N_PDN_CTL0, 0x40); + b43_radio_write(dev, B2062_N_PDN_CTL0, 0); + b43_radio_write(dev, B2062_N_CALIB_TS, 0x10); + b43_radio_write(dev, B2062_N_CALIB_TS, 0); + if (dev->phy.rev > 0) { + b43_radio_write(dev, B2062_S_BG_CTL1, + (b43_radio_read(dev, B2062_N_COMM2) >> 1) | 0x80); + } + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_radio_set(dev, B2062_N_TSSI_CTL0, 0x1); + else + b43_radio_mask(dev, B2062_N_TSSI_CTL0, ~0x1); + + /* Get the crystal freq, in Hz. */ + crystalfreq = bus->chipco.pmu.crystalfreq * 1000; + + B43_WARN_ON(!(bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU)); + B43_WARN_ON(crystalfreq == 0); + + if (crystalfreq <= 30000000) { + lpphy->pdiv = 1; + b43_radio_mask(dev, B2062_S_RFPLL_CTL1, 0xFFFB); + } else { + lpphy->pdiv = 2; + b43_radio_set(dev, B2062_S_RFPLL_CTL1, 0x4); + } + + tmp = (((800000000 * lpphy->pdiv + crystalfreq) / + (2 * crystalfreq)) - 8) & 0xFF; + b43_radio_write(dev, B2062_S_RFPLL_CTL7, tmp); + + tmp = (((100 * crystalfreq + 16000000 * lpphy->pdiv) / + (32000000 * lpphy->pdiv)) - 1) & 0xFF; + b43_radio_write(dev, B2062_S_RFPLL_CTL18, tmp); + + tmp = (((2 * crystalfreq + 1000000 * lpphy->pdiv) / + (2000000 * lpphy->pdiv)) - 1) & 0xFF; + b43_radio_write(dev, B2062_S_RFPLL_CTL19, tmp); + + ref = (1000 * lpphy->pdiv + 2 * crystalfreq) / (2000 * lpphy->pdiv); + ref &= 0xFFFF; + for (i = 0; i < ARRAY_SIZE(freqdata_tab); i++) { + if (ref < freqdata_tab[i].freq) { + fd = &freqdata_tab[i]; + break; + } + } + if (!fd) + fd = &freqdata_tab[ARRAY_SIZE(freqdata_tab) - 1]; + b43dbg(dev->wl, "b2062: Using crystal tab entry %u kHz.\n", + fd->freq); /* FIXME: Keep this printk until the code is fully debugged. */ + + b43_radio_write(dev, B2062_S_RFPLL_CTL8, + ((u16)(fd->data[1]) << 4) | fd->data[0]); + b43_radio_write(dev, B2062_S_RFPLL_CTL9, + ((u16)(fd->data[3]) << 4) | fd->data[2]); + b43_radio_write(dev, B2062_S_RFPLL_CTL10, fd->data[4]); + b43_radio_write(dev, B2062_S_RFPLL_CTL11, fd->data[5]); +} + +/* Initialize the 2063 radio. */ +static void lpphy_2063_init(struct b43_wldev *dev) +{ + b2063_upload_init_table(dev); + b43_radio_write(dev, B2063_LOGEN_SP5, 0); + b43_radio_set(dev, B2063_COMM8, 0x38); + b43_radio_write(dev, B2063_REG_SP1, 0x56); + b43_radio_mask(dev, B2063_RX_BB_CTL2, ~0x2); + b43_radio_write(dev, B2063_PA_SP7, 0); + b43_radio_write(dev, B2063_TX_RF_SP6, 0x20); + b43_radio_write(dev, B2063_TX_RF_SP9, 0x40); + if (dev->phy.rev == 2) { + b43_radio_write(dev, B2063_PA_SP3, 0xa0); + b43_radio_write(dev, B2063_PA_SP4, 0xa0); + b43_radio_write(dev, B2063_PA_SP2, 0x18); + } else { + b43_radio_write(dev, B2063_PA_SP3, 0x20); + b43_radio_write(dev, B2063_PA_SP2, 0x20); + } +} + +struct lpphy_stx_table_entry { + u16 phy_offset; + u16 phy_shift; + u16 rf_addr; + u16 rf_shift; + u16 mask; +}; + +static const struct lpphy_stx_table_entry lpphy_stx_table[] = { + { .phy_offset = 2, .phy_shift = 6, .rf_addr = 0x3d, .rf_shift = 3, .mask = 0x01, }, + { .phy_offset = 1, .phy_shift = 12, .rf_addr = 0x4c, .rf_shift = 1, .mask = 0x01, }, + { .phy_offset = 1, .phy_shift = 8, .rf_addr = 0x50, .rf_shift = 0, .mask = 0x7f, }, + { .phy_offset = 0, .phy_shift = 8, .rf_addr = 0x44, .rf_shift = 0, .mask = 0xff, }, + { .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4a, .rf_shift = 0, .mask = 0xff, }, + { .phy_offset = 0, .phy_shift = 4, .rf_addr = 0x4d, .rf_shift = 0, .mask = 0xff, }, + { .phy_offset = 1, .phy_shift = 4, .rf_addr = 0x4e, .rf_shift = 0, .mask = 0xff, }, + { .phy_offset = 0, .phy_shift = 12, .rf_addr = 0x4f, .rf_shift = 0, .mask = 0x0f, }, + { .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4f, .rf_shift = 4, .mask = 0x0f, }, + { .phy_offset = 3, .phy_shift = 0, .rf_addr = 0x49, .rf_shift = 0, .mask = 0x0f, }, + { .phy_offset = 4, .phy_shift = 3, .rf_addr = 0x46, .rf_shift = 4, .mask = 0x07, }, + { .phy_offset = 3, .phy_shift = 15, .rf_addr = 0x46, .rf_shift = 0, .mask = 0x01, }, + { .phy_offset = 4, .phy_shift = 0, .rf_addr = 0x46, .rf_shift = 1, .mask = 0x07, }, + { .phy_offset = 3, .phy_shift = 8, .rf_addr = 0x48, .rf_shift = 4, .mask = 0x07, }, + { .phy_offset = 3, .phy_shift = 11, .rf_addr = 0x48, .rf_shift = 0, .mask = 0x0f, }, + { .phy_offset = 3, .phy_shift = 4, .rf_addr = 0x49, .rf_shift = 4, .mask = 0x0f, }, + { .phy_offset = 2, .phy_shift = 15, .rf_addr = 0x45, .rf_shift = 0, .mask = 0x01, }, + { .phy_offset = 5, .phy_shift = 13, .rf_addr = 0x52, .rf_shift = 4, .mask = 0x07, }, + { .phy_offset = 6, .phy_shift = 0, .rf_addr = 0x52, .rf_shift = 7, .mask = 0x01, }, + { .phy_offset = 5, .phy_shift = 3, .rf_addr = 0x41, .rf_shift = 5, .mask = 0x07, }, + { .phy_offset = 5, .phy_shift = 6, .rf_addr = 0x41, .rf_shift = 0, .mask = 0x0f, }, + { .phy_offset = 5, .phy_shift = 10, .rf_addr = 0x42, .rf_shift = 5, .mask = 0x07, }, + { .phy_offset = 4, .phy_shift = 15, .rf_addr = 0x42, .rf_shift = 0, .mask = 0x01, }, + { .phy_offset = 5, .phy_shift = 0, .rf_addr = 0x42, .rf_shift = 1, .mask = 0x07, }, + { .phy_offset = 4, .phy_shift = 11, .rf_addr = 0x43, .rf_shift = 4, .mask = 0x0f, }, + { .phy_offset = 4, .phy_shift = 7, .rf_addr = 0x43, .rf_shift = 0, .mask = 0x0f, }, + { .phy_offset = 4, .phy_shift = 6, .rf_addr = 0x45, .rf_shift = 1, .mask = 0x01, }, + { .phy_offset = 2, .phy_shift = 7, .rf_addr = 0x40, .rf_shift = 4, .mask = 0x0f, }, + { .phy_offset = 2, .phy_shift = 11, .rf_addr = 0x40, .rf_shift = 0, .mask = 0x0f, }, +}; + +static void lpphy_sync_stx(struct b43_wldev *dev) +{ + const struct lpphy_stx_table_entry *e; + unsigned int i; + u16 tmp; + + for (i = 0; i < ARRAY_SIZE(lpphy_stx_table); i++) { + e = &lpphy_stx_table[i]; + tmp = b43_radio_read(dev, e->rf_addr); + tmp >>= e->rf_shift; + tmp <<= e->phy_shift; + b43_phy_maskset(dev, B43_PHY_OFDM(0xF2 + e->phy_offset), + ~(e->mask << e->phy_shift), tmp); + } +} + +static void lpphy_radio_init(struct b43_wldev *dev) +{ + /* The radio is attached through the 4wire bus. */ + b43_phy_set(dev, B43_LPPHY_FOURWIRE_CTL, 0x2); + udelay(1); + b43_phy_mask(dev, B43_LPPHY_FOURWIRE_CTL, 0xFFFD); + udelay(1); + + if (dev->phy.radio_ver == 0x2062) { + lpphy_2062_init(dev); + } else { + lpphy_2063_init(dev); + lpphy_sync_stx(dev); + b43_phy_write(dev, B43_PHY_OFDM(0xF0), 0x5F80); + b43_phy_write(dev, B43_PHY_OFDM(0xF1), 0); + if (dev->dev->chip_id == 0x4325) { + // TODO SSB PMU recalibration + } + } +} + +struct lpphy_iq_est { u32 iq_prod, i_pwr, q_pwr; }; + +static void lpphy_set_rc_cap(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + + u8 rc_cap = (lpphy->rc_cap & 0x1F) >> 1; + + if (dev->phy.rev == 1) //FIXME check channel 14! + rc_cap = min_t(u8, rc_cap + 5, 15); + + b43_radio_write(dev, B2062_N_RXBB_CALIB2, + max_t(u8, lpphy->rc_cap - 4, 0x80)); + b43_radio_write(dev, B2062_N_TX_CTL_A, rc_cap | 0x80); + b43_radio_write(dev, B2062_S_RXG_CNT16, + ((lpphy->rc_cap & 0x1F) >> 2) | 0x80); +} + +static u8 lpphy_get_bb_mult(struct b43_wldev *dev) +{ + return (b43_lptab_read(dev, B43_LPTAB16(0, 87)) & 0xFF00) >> 8; +} + +static void lpphy_set_bb_mult(struct b43_wldev *dev, u8 bb_mult) +{ + b43_lptab_write(dev, B43_LPTAB16(0, 87), (u16)bb_mult << 8); +} + +static void lpphy_set_deaf(struct b43_wldev *dev, bool user) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + + if (user) + lpphy->crs_usr_disable = true; + else + lpphy->crs_sys_disable = true; + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFF1F, 0x80); +} + +static void lpphy_clear_deaf(struct b43_wldev *dev, bool user) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + + if (user) + lpphy->crs_usr_disable = false; + else + lpphy->crs_sys_disable = false; + + if (!lpphy->crs_usr_disable && !lpphy->crs_sys_disable) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, + 0xFF1F, 0x60); + else + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, + 0xFF1F, 0x20); + } +} + +static void lpphy_set_trsw_over(struct b43_wldev *dev, bool tx, bool rx) +{ + u16 trsw = (tx << 1) | rx; + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, trsw); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3); +} + +static void lpphy_disable_crs(struct b43_wldev *dev, bool user) +{ + lpphy_set_deaf(dev, user); + lpphy_set_trsw_over(dev, false, true); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFB); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x4); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFF7); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x10); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFDF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFBF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x7); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x38); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x100); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFDFF); + b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL0, 0); + b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL1, 1); + b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL2, 0x20); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFBFF); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xF7FF); + b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, 0); + b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, 0x45AF); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0x3FF); +} + +static void lpphy_restore_crs(struct b43_wldev *dev, bool user) +{ + lpphy_clear_deaf(dev, user); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFF80); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFC00); +} + +struct lpphy_tx_gains { u16 gm, pga, pad, dac; }; + +static void lpphy_disable_rx_gain_override(struct b43_wldev *dev) +{ + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF); + if (dev->phy.rev >= 2) { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF); + b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7); + } + } else { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF); + } +} + +static void lpphy_enable_rx_gain_override(struct b43_wldev *dev) +{ + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40); + if (dev->phy.rev >= 2) { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400); + b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8); + } + } else { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200); + } +} + +static void lpphy_disable_tx_gain_override(struct b43_wldev *dev) +{ + if (dev->phy.rev < 2) + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF); + else { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF); + } + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF); +} + +static void lpphy_enable_tx_gain_override(struct b43_wldev *dev) +{ + if (dev->phy.rev < 2) + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100); + else { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x80); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x4000); + } + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x40); +} + +static struct lpphy_tx_gains lpphy_get_tx_gains(struct b43_wldev *dev) +{ + struct lpphy_tx_gains gains; + u16 tmp; + + gains.dac = (b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0x380) >> 7; + if (dev->phy.rev < 2) { + tmp = b43_phy_read(dev, + B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL) & 0x7FF; + gains.gm = tmp & 0x0007; + gains.pga = (tmp & 0x0078) >> 3; + gains.pad = (tmp & 0x780) >> 7; + } else { + tmp = b43_phy_read(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL); + gains.pad = b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0xFF; + gains.gm = tmp & 0xFF; + gains.pga = (tmp >> 8) & 0xFF; + } + + return gains; +} + +static void lpphy_set_dac_gain(struct b43_wldev *dev, u16 dac) +{ + u16 ctl = b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0xC7F; + ctl |= dac << 7; + b43_phy_maskset(dev, B43_LPPHY_AFE_DAC_CTL, 0xF000, ctl); +} + +static u16 lpphy_get_pa_gain(struct b43_wldev *dev) +{ + return b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x7F; +} + +static void lpphy_set_pa_gain(struct b43_wldev *dev, u16 gain) +{ + b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), 0xE03F, gain << 6); + b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), 0x80FF, gain << 8); +} + +static void lpphy_set_tx_gains(struct b43_wldev *dev, + struct lpphy_tx_gains gains) +{ + u16 rf_gain, pa_gain; + + if (dev->phy.rev < 2) { + rf_gain = (gains.pad << 7) | (gains.pga << 3) | gains.gm; + b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, + 0xF800, rf_gain); + } else { + pa_gain = lpphy_get_pa_gain(dev); + b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, + (gains.pga << 8) | gains.gm); + /* + * SPEC FIXME The spec calls for (pa_gain << 8) here, but that + * conflicts with the spec for set_pa_gain! Vendor driver bug? + */ + b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), + 0x8000, gains.pad | (pa_gain << 6)); + b43_phy_write(dev, B43_PHY_OFDM(0xFC), + (gains.pga << 8) | gains.gm); + b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), + 0x8000, gains.pad | (pa_gain << 8)); + } + lpphy_set_dac_gain(dev, gains.dac); + lpphy_enable_tx_gain_override(dev); +} + +static void lpphy_rev0_1_set_rx_gain(struct b43_wldev *dev, u32 gain) +{ + u16 trsw = gain & 0x1; + u16 lna = (gain & 0xFFFC) | ((gain & 0xC) >> 2); + u16 ext_lna = (gain & 2) >> 1; + + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, + 0xFBFF, ext_lna << 10); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, + 0xF7FF, ext_lna << 11); + b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, lna); +} + +static void lpphy_rev2plus_set_rx_gain(struct b43_wldev *dev, u32 gain) +{ + u16 low_gain = gain & 0xFFFF; + u16 high_gain = (gain >> 16) & 0xF; + u16 ext_lna = (gain >> 21) & 0x1; + u16 trsw = ~(gain >> 20) & 0x1; + u16 tmp; + + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, + 0xFDFF, ext_lna << 9); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, + 0xFBFF, ext_lna << 10); + b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, low_gain); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF0, high_gain); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + tmp = (gain >> 2) & 0x3; + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, + 0xE7FF, tmp<<11); + b43_phy_maskset(dev, B43_PHY_OFDM(0xE6), 0xFFE7, tmp << 3); + } +} + +static void lpphy_set_rx_gain(struct b43_wldev *dev, u32 gain) +{ + if (dev->phy.rev < 2) + lpphy_rev0_1_set_rx_gain(dev, gain); + else + lpphy_rev2plus_set_rx_gain(dev, gain); + lpphy_enable_rx_gain_override(dev); +} + +static void lpphy_set_rx_gain_by_index(struct b43_wldev *dev, u16 idx) +{ + u32 gain = b43_lptab_read(dev, B43_LPTAB16(12, idx)); + lpphy_set_rx_gain(dev, gain); +} + +static void lpphy_stop_ddfs(struct b43_wldev *dev) +{ + b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFD); + b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xFFDF); +} + +static void lpphy_run_ddfs(struct b43_wldev *dev, int i_on, int q_on, + int incr1, int incr2, int scale_idx) +{ + lpphy_stop_ddfs(dev); + b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0xFF80); + b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0x80FF); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0xFF80, incr1); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0x80FF, incr2 << 8); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF7, i_on << 3); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFEF, q_on << 4); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFF9F, scale_idx << 5); + b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFB); + b43_phy_set(dev, B43_LPPHY_AFE_DDFS, 0x2); + b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x20); +} + +static bool lpphy_rx_iq_est(struct b43_wldev *dev, u16 samples, u8 time, + struct lpphy_iq_est *iq_est) +{ + int i; + + b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFF7); + b43_phy_write(dev, B43_LPPHY_IQ_NUM_SMPLS_ADDR, samples); + b43_phy_maskset(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFF00, time); + b43_phy_mask(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFEFF); + b43_phy_set(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0x200); + + for (i = 0; i < 500; i++) { + if (!(b43_phy_read(dev, + B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) + break; + msleep(1); + } + + if ((b43_phy_read(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) { + b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8); + return false; + } + + iq_est->iq_prod = b43_phy_read(dev, B43_LPPHY_IQ_ACC_HI_ADDR); + iq_est->iq_prod <<= 16; + iq_est->iq_prod |= b43_phy_read(dev, B43_LPPHY_IQ_ACC_LO_ADDR); + + iq_est->i_pwr = b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_HI_ADDR); + iq_est->i_pwr <<= 16; + iq_est->i_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_LO_ADDR); + + iq_est->q_pwr = b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_HI_ADDR); + iq_est->q_pwr <<= 16; + iq_est->q_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_LO_ADDR); + + b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8); + return true; +} + +static int lpphy_loopback(struct b43_wldev *dev) +{ + struct lpphy_iq_est iq_est; + int i, index = -1; + u32 tmp; + + memset(&iq_est, 0, sizeof(iq_est)); + + lpphy_set_trsw_over(dev, true, true); + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 1); + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x8); + b43_radio_write(dev, B2062_N_TX_CTL_A, 0x80); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x80); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x80); + for (i = 0; i < 32; i++) { + lpphy_set_rx_gain_by_index(dev, i); + lpphy_run_ddfs(dev, 1, 1, 5, 5, 0); + if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est))) + continue; + tmp = (iq_est.i_pwr + iq_est.q_pwr) / 1000; + if ((tmp > 4000) && (tmp < 10000)) { + index = i; + break; + } + } + lpphy_stop_ddfs(dev); + return index; +} + +/* Fixed-point division algorithm using only integer math. */ +static u32 lpphy_qdiv_roundup(u32 dividend, u32 divisor, u8 precision) +{ + u32 quotient, remainder; + + if (divisor == 0) + return 0; + + quotient = dividend / divisor; + remainder = dividend % divisor; + + while (precision > 0) { + quotient <<= 1; + if (remainder << 1 >= divisor) { + quotient++; + remainder = (remainder << 1) - divisor; + } + precision--; + } + + if (remainder << 1 >= divisor) + quotient++; + + return quotient; +} + +/* Read the TX power control mode from hardware. */ +static void lpphy_read_tx_pctl_mode_from_hardware(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + u16 ctl; + + ctl = b43_phy_read(dev, B43_LPPHY_TX_PWR_CTL_CMD); + switch (ctl & B43_LPPHY_TX_PWR_CTL_CMD_MODE) { + case B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF: + lpphy->txpctl_mode = B43_LPPHY_TXPCTL_OFF; + break; + case B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW: + lpphy->txpctl_mode = B43_LPPHY_TXPCTL_SW; + break; + case B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW: + lpphy->txpctl_mode = B43_LPPHY_TXPCTL_HW; + break; + default: + lpphy->txpctl_mode = B43_LPPHY_TXPCTL_UNKNOWN; + B43_WARN_ON(1); + break; + } +} + +/* Set the TX power control mode in hardware. */ +static void lpphy_write_tx_pctl_mode_to_hardware(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + u16 ctl; + + switch (lpphy->txpctl_mode) { + case B43_LPPHY_TXPCTL_OFF: + ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF; + break; + case B43_LPPHY_TXPCTL_HW: + ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW; + break; + case B43_LPPHY_TXPCTL_SW: + ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW; + break; + default: + ctl = 0; + B43_WARN_ON(1); + } + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, + ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF, ctl); +} + +static void lpphy_set_tx_power_control(struct b43_wldev *dev, + enum b43_lpphy_txpctl_mode mode) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + enum b43_lpphy_txpctl_mode oldmode; + + lpphy_read_tx_pctl_mode_from_hardware(dev); + oldmode = lpphy->txpctl_mode; + if (oldmode == mode) + return; + lpphy->txpctl_mode = mode; + + if (oldmode == B43_LPPHY_TXPCTL_HW) { + //TODO Update TX Power NPT + //TODO Clear all TX Power offsets + } else { + if (mode == B43_LPPHY_TXPCTL_HW) { + //TODO Recalculate target TX power + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, + 0xFF80, lpphy->tssi_idx); + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, + 0x8FFF, ((u16)lpphy->tssi_npt << 16)); + //TODO Set "TSSI Transmit Count" variable to total transmitted frame count + lpphy_disable_tx_gain_override(dev); + lpphy->tx_pwr_idx_over = -1; + } + } + if (dev->phy.rev >= 2) { + if (mode == B43_LPPHY_TXPCTL_HW) + b43_phy_set(dev, B43_PHY_OFDM(0xD0), 0x2); + else + b43_phy_mask(dev, B43_PHY_OFDM(0xD0), 0xFFFD); + } + lpphy_write_tx_pctl_mode_to_hardware(dev); +} + +static int b43_lpphy_op_switch_channel(struct b43_wldev *dev, + unsigned int new_channel); + +static void lpphy_rev0_1_rc_calib(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + struct lpphy_iq_est iq_est; + struct lpphy_tx_gains tx_gains; + static const u32 ideal_pwr_table[21] = { + 0x10000, 0x10557, 0x10e2d, 0x113e0, 0x10f22, 0x0ff64, + 0x0eda2, 0x0e5d4, 0x0efd1, 0x0fbe8, 0x0b7b8, 0x04b35, + 0x01a5e, 0x00a0b, 0x00444, 0x001fd, 0x000ff, 0x00088, + 0x0004c, 0x0002c, 0x0001a, + }; + bool old_txg_ovr; + u8 old_bbmult; + u16 old_rf_ovr, old_rf_ovrval, old_afe_ovr, old_afe_ovrval, + old_rf2_ovr, old_rf2_ovrval, old_phy_ctl; + enum b43_lpphy_txpctl_mode old_txpctl; + u32 normal_pwr, ideal_pwr, mean_sq_pwr, tmp = 0, mean_sq_pwr_min = 0; + int loopback, i, j, inner_sum, err; + + memset(&iq_est, 0, sizeof(iq_est)); + + err = b43_lpphy_op_switch_channel(dev, 7); + if (err) { + b43dbg(dev->wl, + "RC calib: Failed to switch to channel 7, error = %d\n", + err); + } + old_txg_ovr = !!(b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40); + old_bbmult = lpphy_get_bb_mult(dev); + if (old_txg_ovr) + tx_gains = lpphy_get_tx_gains(dev); + old_rf_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_0); + old_rf_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_VAL_0); + old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR); + old_afe_ovrval = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVRVAL); + old_rf2_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2); + old_rf2_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2_VAL); + old_phy_ctl = b43_phy_read(dev, B43_LPPHY_LP_PHY_CTL); + lpphy_read_tx_pctl_mode_from_hardware(dev); + old_txpctl = lpphy->txpctl_mode; + + lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); + lpphy_disable_crs(dev, true); + loopback = lpphy_loopback(dev); + if (loopback == -1) + goto finish; + lpphy_set_rx_gain_by_index(dev, loopback); + b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFFBF, 0x40); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFF8, 0x1); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFC7, 0x8); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F, 0xC0); + for (i = 128; i <= 159; i++) { + b43_radio_write(dev, B2062_N_RXBB_CALIB2, i); + inner_sum = 0; + for (j = 5; j <= 25; j++) { + lpphy_run_ddfs(dev, 1, 1, j, j, 0); + if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est))) + goto finish; + mean_sq_pwr = iq_est.i_pwr + iq_est.q_pwr; + if (j == 5) + tmp = mean_sq_pwr; + ideal_pwr = ((ideal_pwr_table[j-5] >> 3) + 1) >> 1; + normal_pwr = lpphy_qdiv_roundup(mean_sq_pwr, tmp, 12); + mean_sq_pwr = ideal_pwr - normal_pwr; + mean_sq_pwr *= mean_sq_pwr; + inner_sum += mean_sq_pwr; + if ((i == 128) || (inner_sum < mean_sq_pwr_min)) { + lpphy->rc_cap = i; + mean_sq_pwr_min = inner_sum; + } + } + } + lpphy_stop_ddfs(dev); + +finish: + lpphy_restore_crs(dev, true); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, old_rf_ovrval); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, old_rf_ovr); + b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVRVAL, old_afe_ovrval); + b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, old_afe_ovr); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, old_rf2_ovrval); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, old_rf2_ovr); + b43_phy_write(dev, B43_LPPHY_LP_PHY_CTL, old_phy_ctl); + + lpphy_set_bb_mult(dev, old_bbmult); + if (old_txg_ovr) { + /* + * SPEC FIXME: The specs say "get_tx_gains" here, which is + * illogical. According to lwfinger, vendor driver v4.150.10.5 + * has a Set here, while v4.174.64.19 has a Get - regression in + * the vendor driver? This should be tested this once the code + * is testable. + */ + lpphy_set_tx_gains(dev, tx_gains); + } + lpphy_set_tx_power_control(dev, old_txpctl); + if (lpphy->rc_cap) + lpphy_set_rc_cap(dev); +} + +static void lpphy_rev2plus_rc_calib(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->sdev->bus; + u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; + u8 tmp = b43_radio_read(dev, B2063_RX_BB_SP8) & 0xFF; + int i; + + b43_radio_write(dev, B2063_RX_BB_SP8, 0x0); + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); + b43_radio_mask(dev, B2063_PLL_SP1, 0xF7); + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C); + b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x15); + b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x70); + b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x52); + b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1); + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7D); + + for (i = 0; i < 10000; i++) { + if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2) + break; + msleep(1); + } + + if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)) + b43_radio_write(dev, B2063_RX_BB_SP8, tmp); + + tmp = b43_radio_read(dev, B2063_TX_BB_SP3) & 0xFF; + + b43_radio_write(dev, B2063_TX_BB_SP3, 0x0); + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C); + b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x55); + b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x76); + + if (crystal_freq == 24000000) { + b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0xFC); + b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x0); + } else { + b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x13); + b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1); + } + + b43_radio_write(dev, B2063_PA_SP7, 0x7D); + + for (i = 0; i < 10000; i++) { + if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2) + break; + msleep(1); + } + + if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)) + b43_radio_write(dev, B2063_TX_BB_SP3, tmp); + + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); +} + +static void lpphy_calibrate_rc(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + + if (dev->phy.rev >= 2) { + lpphy_rev2plus_rc_calib(dev); + } else if (!lpphy->rc_cap) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + lpphy_rev0_1_rc_calib(dev); + } else { + lpphy_set_rc_cap(dev); + } +} + +static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) +{ + if (dev->phy.rev >= 2) + return; // rev2+ doesn't support antenna diversity + + if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1)) + return; + + b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP); + + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2); + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1); + + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP); + + dev->phy.lp->antenna = antenna; +} + +static void lpphy_set_tx_iqcc(struct b43_wldev *dev, u16 a, u16 b) +{ + u16 tmp[2]; + + tmp[0] = a; + tmp[1] = b; + b43_lptab_write_bulk(dev, B43_LPTAB16(0, 80), 2, tmp); +} + +static void lpphy_set_tx_power_by_index(struct b43_wldev *dev, u8 index) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + struct lpphy_tx_gains gains; + u32 iq_comp, tx_gain, coeff, rf_power; + + lpphy->tx_pwr_idx_over = index; + lpphy_read_tx_pctl_mode_from_hardware(dev); + if (lpphy->txpctl_mode != B43_LPPHY_TXPCTL_OFF) + lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_SW); + if (dev->phy.rev >= 2) { + iq_comp = b43_lptab_read(dev, B43_LPTAB32(7, index + 320)); + tx_gain = b43_lptab_read(dev, B43_LPTAB32(7, index + 192)); + gains.pad = (tx_gain >> 16) & 0xFF; + gains.gm = tx_gain & 0xFF; + gains.pga = (tx_gain >> 8) & 0xFF; + gains.dac = (iq_comp >> 28) & 0xFF; + lpphy_set_tx_gains(dev, gains); + } else { + iq_comp = b43_lptab_read(dev, B43_LPTAB32(10, index + 320)); + tx_gain = b43_lptab_read(dev, B43_LPTAB32(10, index + 192)); + b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, + 0xF800, (tx_gain >> 4) & 0x7FFF); + lpphy_set_dac_gain(dev, tx_gain & 0x7); + lpphy_set_pa_gain(dev, (tx_gain >> 24) & 0x7F); + } + lpphy_set_bb_mult(dev, (iq_comp >> 20) & 0xFF); + lpphy_set_tx_iqcc(dev, (iq_comp >> 10) & 0x3FF, iq_comp & 0x3FF); + if (dev->phy.rev >= 2) { + coeff = b43_lptab_read(dev, B43_LPTAB32(7, index + 448)); + } else { + coeff = b43_lptab_read(dev, B43_LPTAB32(10, index + 448)); + } + b43_lptab_write(dev, B43_LPTAB16(0, 85), coeff & 0xFFFF); + if (dev->phy.rev >= 2) { + rf_power = b43_lptab_read(dev, B43_LPTAB32(7, index + 576)); + b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, + rf_power & 0xFFFF);//SPEC FIXME mask & set != 0 + } + lpphy_enable_tx_gain_override(dev); +} + +static void lpphy_btcoex_override(struct b43_wldev *dev) +{ + b43_write16(dev, B43_MMIO_BTCOEX_CTL, 0x3); + b43_write16(dev, B43_MMIO_BTCOEX_TXCTL, 0xFF); +} + +static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev, + bool blocked) +{ + //TODO check MAC control register + if (blocked) { + if (dev->phy.rev >= 2) { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x83FF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00); + b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0x80FF); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xDFFF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0808); + } else { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xE0FF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFCFF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0018); + } + } else { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xE0FF); + if (dev->phy.rev >= 2) + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xF7F7); + else + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFFE7); + } +} + +/* This was previously called lpphy_japan_filter */ +static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter! + + if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific? + b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9); + if ((dev->phy.rev == 1) && (lpphy->rc_cap)) + lpphy_set_rc_cap(dev); + } else { + b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F); + } +} + +static void lpphy_set_tssi_mux(struct b43_wldev *dev, enum tssi_mux_mode mode) +{ + if (mode != TSSI_MUX_EXT) { + b43_radio_set(dev, B2063_PA_SP1, 0x2); + b43_phy_set(dev, B43_PHY_OFDM(0xF3), 0x1000); + b43_radio_write(dev, B2063_PA_CTL10, 0x51); + if (mode == TSSI_MUX_POSTPA) { + b43_radio_mask(dev, B2063_PA_SP1, 0xFFFE); + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFC7); + } else { + b43_radio_maskset(dev, B2063_PA_SP1, 0xFFFE, 0x1); + b43_phy_maskset(dev, B43_LPPHY_AFE_CTL_OVRVAL, + 0xFFC7, 0x20); + } + } else { + B43_WARN_ON(1); + } +} + +static void lpphy_tx_pctl_init_hw(struct b43_wldev *dev) +{ + u16 tmp; + int i; + + //SPEC TODO Call LP PHY Clear TX Power offsets + for (i = 0; i < 64; i++) { + if (dev->phy.rev >= 2) + b43_lptab_write(dev, B43_LPTAB32(7, i + 1), i); + else + b43_lptab_write(dev, B43_LPTAB32(10, i + 1), i); + } + + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xFF00, 0xFF); + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0x8FFF, 0x5000); + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0xFFC0, 0x1F); + if (dev->phy.rev < 2) { + b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xEFFF); + b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xDFFF, 0x2000); + } else { + b43_phy_mask(dev, B43_PHY_OFDM(0x103), 0xFFFE); + b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFFB, 0x4); + b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFEF, 0x10); + b43_radio_maskset(dev, B2063_IQ_CALIB_CTL2, 0xF3, 0x1); + lpphy_set_tssi_mux(dev, TSSI_MUX_POSTPA); + } + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0x7FFF, 0x8000); + b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xFF); + b43_phy_write(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xA); + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, + ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF, + B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF); + b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xF8FF); + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, + ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF, + B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW); + + if (dev->phy.rev < 2) { + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF, 0x1000); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xEFFF); + } else { + lpphy_set_tx_power_by_index(dev, 0x7F); + } + + b43_dummy_transmission(dev, true, true); + + tmp = b43_phy_read(dev, B43_LPPHY_TX_PWR_CTL_STAT); + if (tmp & 0x8000) { + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, + 0xFFC0, (tmp & 0xFF) - 32); + } + + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF); + + // (SPEC?) TODO Set "Target TX frequency" variable to 0 + // SPEC FIXME "Set BB Multiplier to 0xE000" impossible - bb_mult is u8! +} + +static void lpphy_tx_pctl_init_sw(struct b43_wldev *dev) +{ + struct lpphy_tx_gains gains; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + gains.gm = 4; + gains.pad = 12; + gains.pga = 12; + gains.dac = 0; + } else { + gains.gm = 7; + gains.pad = 14; + gains.pga = 15; + gains.dac = 0; + } + lpphy_set_tx_gains(dev, gains); + lpphy_set_bb_mult(dev, 150); +} + +/* Initialize TX power control */ +static void lpphy_tx_pctl_init(struct b43_wldev *dev) +{ + if (0/*FIXME HWPCTL capable */) { + lpphy_tx_pctl_init_hw(dev); + } else { /* This device is only software TX power control capable. */ + lpphy_tx_pctl_init_sw(dev); + } +} + +static void lpphy_pr41573_workaround(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + u32 *saved_tab; + const unsigned int saved_tab_size = 256; + enum b43_lpphy_txpctl_mode txpctl_mode; + s8 tx_pwr_idx_over; + u16 tssi_npt, tssi_idx; + + saved_tab = kcalloc(saved_tab_size, sizeof(saved_tab[0]), GFP_KERNEL); + if (!saved_tab) { + b43err(dev->wl, "PR41573 failed. Out of memory!\n"); + return; + } + + lpphy_read_tx_pctl_mode_from_hardware(dev); + txpctl_mode = lpphy->txpctl_mode; + tx_pwr_idx_over = lpphy->tx_pwr_idx_over; + tssi_npt = lpphy->tssi_npt; + tssi_idx = lpphy->tssi_idx; + + if (dev->phy.rev < 2) { + b43_lptab_read_bulk(dev, B43_LPTAB32(10, 0x140), + saved_tab_size, saved_tab); + } else { + b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140), + saved_tab_size, saved_tab); + } + //FIXME PHY reset + lpphy_table_init(dev); //FIXME is table init needed? + lpphy_baseband_init(dev); + lpphy_tx_pctl_init(dev); + b43_lpphy_op_software_rfkill(dev, false); + lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); + if (dev->phy.rev < 2) { + b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0x140), + saved_tab_size, saved_tab); + } else { + b43_lptab_write_bulk(dev, B43_LPTAB32(7, 0x140), + saved_tab_size, saved_tab); + } + b43_write16(dev, B43_MMIO_CHANNEL, lpphy->channel); + lpphy->tssi_npt = tssi_npt; + lpphy->tssi_idx = tssi_idx; + lpphy_set_analog_filter(dev, lpphy->channel); + if (tx_pwr_idx_over != -1) + lpphy_set_tx_power_by_index(dev, tx_pwr_idx_over); + if (lpphy->rc_cap) + lpphy_set_rc_cap(dev); + b43_lpphy_op_set_rx_antenna(dev, lpphy->antenna); + lpphy_set_tx_power_control(dev, txpctl_mode); + kfree(saved_tab); +} + +struct lpphy_rx_iq_comp { u8 chan; s8 c1, c0; }; + +static const struct lpphy_rx_iq_comp lpphy_5354_iq_table[] = { + { .chan = 1, .c1 = -66, .c0 = 15, }, + { .chan = 2, .c1 = -66, .c0 = 15, }, + { .chan = 3, .c1 = -66, .c0 = 15, }, + { .chan = 4, .c1 = -66, .c0 = 15, }, + { .chan = 5, .c1 = -66, .c0 = 15, }, + { .chan = 6, .c1 = -66, .c0 = 15, }, + { .chan = 7, .c1 = -66, .c0 = 14, }, + { .chan = 8, .c1 = -66, .c0 = 14, }, + { .chan = 9, .c1 = -66, .c0 = 14, }, + { .chan = 10, .c1 = -66, .c0 = 14, }, + { .chan = 11, .c1 = -66, .c0 = 14, }, + { .chan = 12, .c1 = -66, .c0 = 13, }, + { .chan = 13, .c1 = -66, .c0 = 13, }, + { .chan = 14, .c1 = -66, .c0 = 13, }, +}; + +static const struct lpphy_rx_iq_comp lpphy_rev0_1_iq_table[] = { + { .chan = 1, .c1 = -64, .c0 = 13, }, + { .chan = 2, .c1 = -64, .c0 = 13, }, + { .chan = 3, .c1 = -64, .c0 = 13, }, + { .chan = 4, .c1 = -64, .c0 = 13, }, + { .chan = 5, .c1 = -64, .c0 = 12, }, + { .chan = 6, .c1 = -64, .c0 = 12, }, + { .chan = 7, .c1 = -64, .c0 = 12, }, + { .chan = 8, .c1 = -64, .c0 = 12, }, + { .chan = 9, .c1 = -64, .c0 = 12, }, + { .chan = 10, .c1 = -64, .c0 = 11, }, + { .chan = 11, .c1 = -64, .c0 = 11, }, + { .chan = 12, .c1 = -64, .c0 = 11, }, + { .chan = 13, .c1 = -64, .c0 = 11, }, + { .chan = 14, .c1 = -64, .c0 = 10, }, + { .chan = 34, .c1 = -62, .c0 = 24, }, + { .chan = 38, .c1 = -62, .c0 = 24, }, + { .chan = 42, .c1 = -62, .c0 = 24, }, + { .chan = 46, .c1 = -62, .c0 = 23, }, + { .chan = 36, .c1 = -62, .c0 = 24, }, + { .chan = 40, .c1 = -62, .c0 = 24, }, + { .chan = 44, .c1 = -62, .c0 = 23, }, + { .chan = 48, .c1 = -62, .c0 = 23, }, + { .chan = 52, .c1 = -62, .c0 = 23, }, + { .chan = 56, .c1 = -62, .c0 = 22, }, + { .chan = 60, .c1 = -62, .c0 = 22, }, + { .chan = 64, .c1 = -62, .c0 = 22, }, + { .chan = 100, .c1 = -62, .c0 = 16, }, + { .chan = 104, .c1 = -62, .c0 = 16, }, + { .chan = 108, .c1 = -62, .c0 = 15, }, + { .chan = 112, .c1 = -62, .c0 = 14, }, + { .chan = 116, .c1 = -62, .c0 = 14, }, + { .chan = 120, .c1 = -62, .c0 = 13, }, + { .chan = 124, .c1 = -62, .c0 = 12, }, + { .chan = 128, .c1 = -62, .c0 = 12, }, + { .chan = 132, .c1 = -62, .c0 = 12, }, + { .chan = 136, .c1 = -62, .c0 = 11, }, + { .chan = 140, .c1 = -62, .c0 = 10, }, + { .chan = 149, .c1 = -61, .c0 = 9, }, + { .chan = 153, .c1 = -61, .c0 = 9, }, + { .chan = 157, .c1 = -61, .c0 = 9, }, + { .chan = 161, .c1 = -61, .c0 = 8, }, + { .chan = 165, .c1 = -61, .c0 = 8, }, + { .chan = 184, .c1 = -62, .c0 = 25, }, + { .chan = 188, .c1 = -62, .c0 = 25, }, + { .chan = 192, .c1 = -62, .c0 = 25, }, + { .chan = 196, .c1 = -62, .c0 = 25, }, + { .chan = 200, .c1 = -62, .c0 = 25, }, + { .chan = 204, .c1 = -62, .c0 = 25, }, + { .chan = 208, .c1 = -62, .c0 = 25, }, + { .chan = 212, .c1 = -62, .c0 = 25, }, + { .chan = 216, .c1 = -62, .c0 = 26, }, +}; + +static const struct lpphy_rx_iq_comp lpphy_rev2plus_iq_comp = { + .chan = 0, + .c1 = -64, + .c0 = 0, +}; + +static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples) +{ + struct lpphy_iq_est iq_est; + u16 c0, c1; + int prod, ipwr, qpwr, prod_msb, q_msb, tmp1, tmp2, tmp3, tmp4, ret; + + c1 = b43_phy_read(dev, B43_LPPHY_RX_COMP_COEFF_S); + c0 = c1 >> 8; + c1 |= 0xFF; + + b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, 0x00C0); + b43_phy_mask(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF); + + ret = lpphy_rx_iq_est(dev, samples, 32, &iq_est); + if (!ret) + goto out; + + prod = iq_est.iq_prod; + ipwr = iq_est.i_pwr; + qpwr = iq_est.q_pwr; + + if (ipwr + qpwr < 2) { + ret = 0; + goto out; + } + + prod_msb = fls(abs(prod)); + q_msb = fls(abs(qpwr)); + tmp1 = prod_msb - 20; + + if (tmp1 >= 0) { + tmp3 = ((prod << (30 - prod_msb)) + (ipwr >> (1 + tmp1))) / + (ipwr >> tmp1); + } else { + tmp3 = ((prod << (30 - prod_msb)) + (ipwr << (-1 - tmp1))) / + (ipwr << -tmp1); + } + + tmp2 = q_msb - 11; + + if (tmp2 >= 0) + tmp4 = (qpwr << (31 - q_msb)) / (ipwr >> tmp2); + else + tmp4 = (qpwr << (31 - q_msb)) / (ipwr << -tmp2); + + tmp4 -= tmp3 * tmp3; + tmp4 = -int_sqrt(tmp4); + + c0 = tmp3 >> 3; + c1 = tmp4 >> 4; + +out: + b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, c1); + b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF, c0 << 8); + return ret; +} + +static void lpphy_run_samples(struct b43_wldev *dev, u16 samples, u16 loops, + u16 wait) +{ + b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, + 0xFFC0, samples - 1); + if (loops != 0xFFFF) + loops--; + b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000, loops); + b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, 0x3F, wait << 6); + b43_phy_set(dev, B43_LPPHY_A_PHY_CTL_ADDR, 0x1); +} + +//SPEC FIXME what does a negative freq mean? +static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + u16 buf[64]; + int i, samples = 0, angle = 0; + int rotation = (((36 * freq) / 20) << 16) / 100; + struct b43_c32 sample; + + lpphy->tx_tone_freq = freq; + + if (freq) { + /* Find i for which abs(freq) integrally divides 20000 * i */ + for (i = 1; samples * abs(freq) != 20000 * i; i++) { + samples = (20000 * i) / abs(freq); + if(B43_WARN_ON(samples > 63)) + return; + } + } else { + samples = 2; + } + + for (i = 0; i < samples; i++) { + sample = b43_cordic(angle); + angle += rotation; + buf[i] = CORDIC_CONVERT((sample.i * max) & 0xFF) << 8; + buf[i] |= CORDIC_CONVERT((sample.q * max) & 0xFF); + } + + b43_lptab_write_bulk(dev, B43_LPTAB16(5, 0), samples, buf); + + lpphy_run_samples(dev, samples, 0xFFFF, 0); +} + +static void lpphy_stop_tx_tone(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + int i; + + lpphy->tx_tone_freq = 0; + + b43_phy_mask(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000); + for (i = 0; i < 31; i++) { + if (!(b43_phy_read(dev, B43_LPPHY_A_PHY_CTL_ADDR) & 0x1)) + break; + udelay(100); + } +} + + +static void lpphy_papd_cal(struct b43_wldev *dev, struct lpphy_tx_gains gains, + int mode, bool useindex, u8 index) +{ + //TODO +} + +static void lpphy_papd_cal_txpwr(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + struct lpphy_tx_gains gains, oldgains; + int old_txpctl, old_afe_ovr, old_rf, old_bbmult; + + lpphy_read_tx_pctl_mode_from_hardware(dev); + old_txpctl = lpphy->txpctl_mode; + old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40; + if (old_afe_ovr) + oldgains = lpphy_get_tx_gains(dev); + old_rf = b43_phy_read(dev, B43_LPPHY_RF_PWR_OVERRIDE) & 0xFF; + old_bbmult = lpphy_get_bb_mult(dev); + + lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); + + if (dev->dev->chip_id == 0x4325 && dev->dev->chip_rev == 0) + lpphy_papd_cal(dev, gains, 0, 1, 30); + else + lpphy_papd_cal(dev, gains, 0, 1, 65); + + if (old_afe_ovr) + lpphy_set_tx_gains(dev, oldgains); + lpphy_set_bb_mult(dev, old_bbmult); + lpphy_set_tx_power_control(dev, old_txpctl); + b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, old_rf); +} + +static int lpphy_rx_iq_cal(struct b43_wldev *dev, bool noise, bool tx, + bool rx, bool pa, struct lpphy_tx_gains *gains) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + const struct lpphy_rx_iq_comp *iqcomp = NULL; + struct lpphy_tx_gains nogains, oldgains; + u16 tmp; + int i, ret; + + memset(&nogains, 0, sizeof(nogains)); + memset(&oldgains, 0, sizeof(oldgains)); + + if (dev->dev->chip_id == 0x5354) { + for (i = 0; i < ARRAY_SIZE(lpphy_5354_iq_table); i++) { + if (lpphy_5354_iq_table[i].chan == lpphy->channel) { + iqcomp = &lpphy_5354_iq_table[i]; + } + } + } else if (dev->phy.rev >= 2) { + iqcomp = &lpphy_rev2plus_iq_comp; + } else { + for (i = 0; i < ARRAY_SIZE(lpphy_rev0_1_iq_table); i++) { + if (lpphy_rev0_1_iq_table[i].chan == lpphy->channel) { + iqcomp = &lpphy_rev0_1_iq_table[i]; + } + } + } + + if (B43_WARN_ON(!iqcomp)) + return 0; + + b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, iqcomp->c1); + b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, + 0x00FF, iqcomp->c0 << 8); + + if (noise) { + tx = true; + rx = false; + pa = false; + } + + lpphy_set_trsw_over(dev, tx, rx); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, + 0xFFF7, pa << 3); + } else { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, + 0xFFDF, pa << 5); + } + + tmp = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40; + + if (noise) + lpphy_set_rx_gain(dev, 0x2D5D); + else { + if (tmp) + oldgains = lpphy_get_tx_gains(dev); + if (!gains) + gains = &nogains; + lpphy_set_tx_gains(dev, *gains); + } + + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE); + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800); + lpphy_set_deaf(dev, false); + if (noise) + ret = lpphy_calc_rx_iq_comp(dev, 0xFFF0); + else { + lpphy_start_tx_tone(dev, 4000, 100); + ret = lpphy_calc_rx_iq_comp(dev, 0x4000); + lpphy_stop_tx_tone(dev); + } + lpphy_clear_deaf(dev, false); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFC); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFF7); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFDF); + if (!noise) { + if (tmp) + lpphy_set_tx_gains(dev, oldgains); + else + lpphy_disable_tx_gain_override(dev); + } + lpphy_disable_rx_gain_override(dev); + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE); + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xF7FF); + return ret; +} + +static void lpphy_calibration(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + enum b43_lpphy_txpctl_mode saved_pctl_mode; + bool full_cal = false; + + if (lpphy->full_calib_chan != lpphy->channel) { + full_cal = true; + lpphy->full_calib_chan = lpphy->channel; + } + + b43_mac_suspend(dev); + + lpphy_btcoex_override(dev); + if (dev->phy.rev >= 2) + lpphy_save_dig_flt_state(dev); + lpphy_read_tx_pctl_mode_from_hardware(dev); + saved_pctl_mode = lpphy->txpctl_mode; + lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); + //TODO Perform transmit power table I/Q LO calibration + if ((dev->phy.rev == 0) && (saved_pctl_mode != B43_LPPHY_TXPCTL_OFF)) + lpphy_pr41573_workaround(dev); + if ((dev->phy.rev >= 2) && full_cal) { + lpphy_papd_cal_txpwr(dev); + } + lpphy_set_tx_power_control(dev, saved_pctl_mode); + if (dev->phy.rev >= 2) + lpphy_restore_dig_flt_state(dev); + lpphy_rx_iq_cal(dev, true, true, false, false, NULL); + + b43_mac_enable(dev); +} + +static void b43_lpphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, + u16 set) +{ + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16(dev, B43_MMIO_PHY_DATA, + (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); +} + +static u16 b43_lpphy_op_radio_read(struct b43_wldev *dev, u16 reg) +{ + /* Register 1 is a 32-bit register. */ + B43_WARN_ON(reg == 1); + /* LP-PHY needs a special bit set for read access */ + if (dev->phy.rev < 2) { + if (reg != 0x4001) + reg |= 0x100; + } else + reg |= 0x200; + + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); + return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); +} + +static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) +{ + /* Register 1 is a 32-bit register. */ + B43_WARN_ON(reg == 1); + + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); + b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); +} + +struct b206x_channel { + u8 channel; + u16 freq; + u8 data[12]; +}; + +static const struct b206x_channel b2062_chantbl[] = { + { .channel = 1, .freq = 2412, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 2, .freq = 2417, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 3, .freq = 2422, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 4, .freq = 2427, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 5, .freq = 2432, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 6, .freq = 2437, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 7, .freq = 2442, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 8, .freq = 2447, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 9, .freq = 2452, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 10, .freq = 2457, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 11, .freq = 2462, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 12, .freq = 2467, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 13, .freq = 2472, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 14, .freq = 2484, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 34, .freq = 5170, .data[0] = 0x00, .data[1] = 0x22, + .data[2] = 0x20, .data[3] = 0x84, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 38, .freq = 5190, .data[0] = 0x00, .data[1] = 0x11, + .data[2] = 0x10, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 42, .freq = 5210, .data[0] = 0x00, .data[1] = 0x11, + .data[2] = 0x10, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 46, .freq = 5230, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 36, .freq = 5180, .data[0] = 0x00, .data[1] = 0x11, + .data[2] = 0x20, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 40, .freq = 5200, .data[0] = 0x00, .data[1] = 0x11, + .data[2] = 0x10, .data[3] = 0x84, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 44, .freq = 5220, .data[0] = 0x00, .data[1] = 0x11, + .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 48, .freq = 5240, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 52, .freq = 5260, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 56, .freq = 5280, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 60, .freq = 5300, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x63, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 64, .freq = 5320, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x62, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 100, .freq = 5500, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x30, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 104, .freq = 5520, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 108, .freq = 5540, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 112, .freq = 5560, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 116, .freq = 5580, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x10, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 120, .freq = 5600, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 124, .freq = 5620, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 128, .freq = 5640, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 132, .freq = 5660, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 136, .freq = 5680, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 140, .freq = 5700, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 149, .freq = 5745, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 153, .freq = 5765, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 157, .freq = 5785, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 161, .freq = 5805, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 165, .freq = 5825, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 184, .freq = 4920, .data[0] = 0x55, .data[1] = 0x77, + .data[2] = 0x90, .data[3] = 0xF7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 188, .freq = 4940, .data[0] = 0x44, .data[1] = 0x77, + .data[2] = 0x80, .data[3] = 0xE7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 192, .freq = 4960, .data[0] = 0x44, .data[1] = 0x66, + .data[2] = 0x80, .data[3] = 0xE7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 196, .freq = 4980, .data[0] = 0x33, .data[1] = 0x66, + .data[2] = 0x70, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 200, .freq = 5000, .data[0] = 0x22, .data[1] = 0x55, + .data[2] = 0x60, .data[3] = 0xD7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 204, .freq = 5020, .data[0] = 0x22, .data[1] = 0x55, + .data[2] = 0x60, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 208, .freq = 5040, .data[0] = 0x22, .data[1] = 0x44, + .data[2] = 0x50, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 212, .freq = 5060, .data[0] = 0x11, .data[1] = 0x44, + .data[2] = 0x50, .data[3] = 0xA5, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 216, .freq = 5080, .data[0] = 0x00, .data[1] = 0x44, + .data[2] = 0x40, .data[3] = 0xB6, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, +}; + +static const struct b206x_channel b2063_chantbl[] = { + { .channel = 1, .freq = 2412, .data[0] = 0x6F, .data[1] = 0x3C, + .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 2, .freq = 2417, .data[0] = 0x6F, .data[1] = 0x3C, + .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 3, .freq = 2422, .data[0] = 0x6F, .data[1] = 0x3C, + .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 4, .freq = 2427, .data[0] = 0x6F, .data[1] = 0x2C, + .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 5, .freq = 2432, .data[0] = 0x6F, .data[1] = 0x2C, + .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 6, .freq = 2437, .data[0] = 0x6F, .data[1] = 0x2C, + .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 7, .freq = 2442, .data[0] = 0x6F, .data[1] = 0x2C, + .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 8, .freq = 2447, .data[0] = 0x6F, .data[1] = 0x2C, + .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 9, .freq = 2452, .data[0] = 0x6F, .data[1] = 0x1C, + .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 10, .freq = 2457, .data[0] = 0x6F, .data[1] = 0x1C, + .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 11, .freq = 2462, .data[0] = 0x6E, .data[1] = 0x1C, + .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 12, .freq = 2467, .data[0] = 0x6E, .data[1] = 0x1C, + .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 13, .freq = 2472, .data[0] = 0x6E, .data[1] = 0x1C, + .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 14, .freq = 2484, .data[0] = 0x6E, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 34, .freq = 5170, .data[0] = 0x6A, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x02, .data[5] = 0x05, + .data[6] = 0x0D, .data[7] = 0x0D, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 36, .freq = 5180, .data[0] = 0x6A, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x05, + .data[6] = 0x0D, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 38, .freq = 5190, .data[0] = 0x6A, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, + .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 40, .freq = 5200, .data[0] = 0x69, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, + .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 42, .freq = 5210, .data[0] = 0x69, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, + .data[6] = 0x0B, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 44, .freq = 5220, .data[0] = 0x69, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x04, + .data[6] = 0x0B, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 46, .freq = 5230, .data[0] = 0x69, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03, + .data[6] = 0x0A, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 48, .freq = 5240, .data[0] = 0x69, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03, + .data[6] = 0x0A, .data[7] = 0x0A, .data[8] = 0x77, .data[9] = 0x60, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 52, .freq = 5260, .data[0] = 0x68, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x02, + .data[6] = 0x09, .data[7] = 0x09, .data[8] = 0x77, .data[9] = 0x60, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 56, .freq = 5280, .data[0] = 0x68, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01, + .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, + .data[10] = 0x10, .data[11] = 0x00, }, + { .channel = 60, .freq = 5300, .data[0] = 0x68, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01, + .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, + .data[10] = 0x10, .data[11] = 0x00, }, + { .channel = 64, .freq = 5320, .data[0] = 0x67, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, + .data[10] = 0x10, .data[11] = 0x00, }, + { .channel = 100, .freq = 5500, .data[0] = 0x64, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x02, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 104, .freq = 5520, .data[0] = 0x64, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x01, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 108, .freq = 5540, .data[0] = 0x63, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x01, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 112, .freq = 5560, .data[0] = 0x63, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 116, .freq = 5580, .data[0] = 0x62, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 120, .freq = 5600, .data[0] = 0x62, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 124, .freq = 5620, .data[0] = 0x62, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 128, .freq = 5640, .data[0] = 0x61, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 132, .freq = 5660, .data[0] = 0x61, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 136, .freq = 5680, .data[0] = 0x61, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 140, .freq = 5700, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 149, .freq = 5745, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 153, .freq = 5765, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 157, .freq = 5785, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 161, .freq = 5805, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 165, .freq = 5825, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 184, .freq = 4920, .data[0] = 0x6E, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0E, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xC0, + .data[10] = 0x50, .data[11] = 0x00, }, + { .channel = 188, .freq = 4940, .data[0] = 0x6E, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0D, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0, + .data[10] = 0x50, .data[11] = 0x00, }, + { .channel = 192, .freq = 4960, .data[0] = 0x6E, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0, + .data[10] = 0x50, .data[11] = 0x00, }, + { .channel = 196, .freq = 4980, .data[0] = 0x6D, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, + .data[10] = 0x40, .data[11] = 0x00, }, + { .channel = 200, .freq = 5000, .data[0] = 0x6D, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0B, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, + .data[10] = 0x40, .data[11] = 0x00, }, + { .channel = 204, .freq = 5020, .data[0] = 0x6D, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0A, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, + .data[10] = 0x40, .data[11] = 0x00, }, + { .channel = 208, .freq = 5040, .data[0] = 0x6C, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x07, .data[5] = 0x09, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, + .data[10] = 0x40, .data[11] = 0x00, }, + { .channel = 212, .freq = 5060, .data[0] = 0x6C, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x06, .data[5] = 0x08, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, + .data[10] = 0x40, .data[11] = 0x00, }, + { .channel = 216, .freq = 5080, .data[0] = 0x6C, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x05, .data[5] = 0x08, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, + .data[10] = 0x40, .data[11] = 0x00, }, +}; + +static void lpphy_b2062_reset_pll_bias(struct b43_wldev *dev) +{ + b43_radio_write(dev, B2062_S_RFPLL_CTL2, 0xFF); + udelay(20); + if (dev->dev->chip_id == 0x5354) { + b43_radio_write(dev, B2062_N_COMM1, 4); + b43_radio_write(dev, B2062_S_RFPLL_CTL2, 4); + } else { + b43_radio_write(dev, B2062_S_RFPLL_CTL2, 0); + } + udelay(5); +} + +static void lpphy_b2062_vco_calib(struct b43_wldev *dev) +{ + b43_radio_write(dev, B2062_S_RFPLL_CTL21, 0x42); + b43_radio_write(dev, B2062_S_RFPLL_CTL21, 0x62); + udelay(200); +} + +static int lpphy_b2062_tune(struct b43_wldev *dev, + unsigned int channel) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + struct ssb_bus *bus = dev->dev->sdev->bus; + const struct b206x_channel *chandata = NULL; + u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; + u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9; + int i, err = 0; + + for (i = 0; i < ARRAY_SIZE(b2062_chantbl); i++) { + if (b2062_chantbl[i].channel == channel) { + chandata = &b2062_chantbl[i]; + break; + } + } + + if (B43_WARN_ON(!chandata)) + return -EINVAL; + + b43_radio_set(dev, B2062_S_RFPLL_CTL14, 0x04); + b43_radio_write(dev, B2062_N_LGENA_TUNE0, chandata->data[0]); + b43_radio_write(dev, B2062_N_LGENA_TUNE2, chandata->data[1]); + b43_radio_write(dev, B2062_N_LGENA_TUNE3, chandata->data[2]); + b43_radio_write(dev, B2062_N_TX_TUNE, chandata->data[3]); + b43_radio_write(dev, B2062_S_LGENG_CTL1, chandata->data[4]); + b43_radio_write(dev, B2062_N_LGENA_CTL5, chandata->data[5]); + b43_radio_write(dev, B2062_N_LGENA_CTL6, chandata->data[6]); + b43_radio_write(dev, B2062_N_TX_PGA, chandata->data[7]); + b43_radio_write(dev, B2062_N_TX_PAD, chandata->data[8]); + + tmp1 = crystal_freq / 1000; + tmp2 = lpphy->pdiv * 1000; + b43_radio_write(dev, B2062_S_RFPLL_CTL33, 0xCC); + b43_radio_write(dev, B2062_S_RFPLL_CTL34, 0x07); + lpphy_b2062_reset_pll_bias(dev); + tmp3 = tmp2 * channel2freq_lp(channel); + if (channel2freq_lp(channel) < 4000) + tmp3 *= 2; + tmp4 = 48 * tmp1; + tmp6 = tmp3 / tmp4; + tmp7 = tmp3 % tmp4; + b43_radio_write(dev, B2062_S_RFPLL_CTL26, tmp6); + tmp5 = tmp7 * 0x100; + tmp6 = tmp5 / tmp4; + tmp7 = tmp5 % tmp4; + b43_radio_write(dev, B2062_S_RFPLL_CTL27, tmp6); + tmp5 = tmp7 * 0x100; + tmp6 = tmp5 / tmp4; + tmp7 = tmp5 % tmp4; + b43_radio_write(dev, B2062_S_RFPLL_CTL28, tmp6); + tmp5 = tmp7 * 0x100; + tmp6 = tmp5 / tmp4; + tmp7 = tmp5 % tmp4; + b43_radio_write(dev, B2062_S_RFPLL_CTL29, tmp6 + ((2 * tmp7) / tmp4)); + tmp8 = b43_radio_read(dev, B2062_S_RFPLL_CTL19); + tmp9 = ((2 * tmp3 * (tmp8 + 1)) + (3 * tmp1)) / (6 * tmp1); + b43_radio_write(dev, B2062_S_RFPLL_CTL23, (tmp9 >> 8) + 16); + b43_radio_write(dev, B2062_S_RFPLL_CTL24, tmp9 & 0xFF); + + lpphy_b2062_vco_calib(dev); + if (b43_radio_read(dev, B2062_S_RFPLL_CTL3) & 0x10) { + b43_radio_write(dev, B2062_S_RFPLL_CTL33, 0xFC); + b43_radio_write(dev, B2062_S_RFPLL_CTL34, 0); + lpphy_b2062_reset_pll_bias(dev); + lpphy_b2062_vco_calib(dev); + if (b43_radio_read(dev, B2062_S_RFPLL_CTL3) & 0x10) + err = -EIO; + } + + b43_radio_mask(dev, B2062_S_RFPLL_CTL14, ~0x04); + return err; +} + +static void lpphy_b2063_vco_calib(struct b43_wldev *dev) +{ + u16 tmp; + + b43_radio_mask(dev, B2063_PLL_SP1, ~0x40); + tmp = b43_radio_read(dev, B2063_PLL_JTAG_CALNRST) & 0xF8; + b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp); + udelay(1); + b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x4); + udelay(1); + b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x6); + udelay(1); + b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x7); + udelay(300); + b43_radio_set(dev, B2063_PLL_SP1, 0x40); +} + +static int lpphy_b2063_tune(struct b43_wldev *dev, + unsigned int channel) +{ + struct ssb_bus *bus = dev->dev->sdev->bus; + + static const struct b206x_channel *chandata = NULL; + u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; + u32 freqref, vco_freq, val1, val2, val3, timeout, timeoutref, count; + u16 old_comm15, scale; + u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + int i, div = (crystal_freq <= 26000000 ? 1 : 2); + + for (i = 0; i < ARRAY_SIZE(b2063_chantbl); i++) { + if (b2063_chantbl[i].channel == channel) { + chandata = &b2063_chantbl[i]; + break; + } + } + + if (B43_WARN_ON(!chandata)) + return -EINVAL; + + b43_radio_write(dev, B2063_LOGEN_VCOBUF1, chandata->data[0]); + b43_radio_write(dev, B2063_LOGEN_MIXER2, chandata->data[1]); + b43_radio_write(dev, B2063_LOGEN_BUF2, chandata->data[2]); + b43_radio_write(dev, B2063_LOGEN_RCCR1, chandata->data[3]); + b43_radio_write(dev, B2063_A_RX_1ST3, chandata->data[4]); + b43_radio_write(dev, B2063_A_RX_2ND1, chandata->data[5]); + b43_radio_write(dev, B2063_A_RX_2ND4, chandata->data[6]); + b43_radio_write(dev, B2063_A_RX_2ND7, chandata->data[7]); + b43_radio_write(dev, B2063_A_RX_PS6, chandata->data[8]); + b43_radio_write(dev, B2063_TX_RF_CTL2, chandata->data[9]); + b43_radio_write(dev, B2063_TX_RF_CTL5, chandata->data[10]); + b43_radio_write(dev, B2063_PA_CTL11, chandata->data[11]); + + old_comm15 = b43_radio_read(dev, B2063_COMM15); + b43_radio_set(dev, B2063_COMM15, 0x1E); + + if (chandata->freq > 4000) /* spec says 2484, but 4000 is safer */ + vco_freq = chandata->freq << 1; + else + vco_freq = chandata->freq << 2; + + freqref = crystal_freq * 3; + val1 = lpphy_qdiv_roundup(crystal_freq, 1000000, 16); + val2 = lpphy_qdiv_roundup(crystal_freq, 1000000 * div, 16); + val3 = lpphy_qdiv_roundup(vco_freq, 3, 16); + timeout = ((((8 * crystal_freq) / (div * 5000000)) + 1) >> 1) - 1; + b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB3, 0x2); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB6, + 0xFFF8, timeout >> 2); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7, + 0xFF9F,timeout << 5); + + timeoutref = ((((8 * crystal_freq) / (div * (timeout + 1))) + + 999999) / 1000000) + 1; + b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB5, timeoutref); + + count = lpphy_qdiv_roundup(val3, val2 + 16, 16); + count *= (timeout + 1) * (timeoutref + 1); + count--; + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7, + 0xF0, count >> 8); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB8, count & 0xFF); + + tmp1 = ((val3 * 62500) / freqref) << 4; + tmp2 = ((val3 * 62500) % freqref) << 4; + while (tmp2 >= freqref) { + tmp1++; + tmp2 -= freqref; + } + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG1, 0xFFE0, tmp1 >> 4); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFE0F, tmp1 << 4); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFFF0, tmp1 >> 16); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG3, (tmp2 >> 8) & 0xFF); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG4, tmp2 & 0xFF); + + b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF1, 0xB9); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF2, 0x88); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF3, 0x28); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF4, 0x63); + + tmp3 = ((41 * (val3 - 3000)) /1200) + 27; + tmp4 = lpphy_qdiv_roundup(132000 * tmp1, 8451, 16); + + if ((tmp4 + tmp3 - 1) / tmp3 > 60) { + scale = 1; + tmp5 = ((tmp4 + tmp3) / (tmp3 << 1)) - 8; + } else { + scale = 0; + tmp5 = ((tmp4 + (tmp3 >> 1)) / tmp3) - 8; + } + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFC0, tmp5); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFBF, scale << 6); + + tmp6 = lpphy_qdiv_roundup(100 * val1, val3, 16); + tmp6 *= (tmp5 * 8) * (scale + 1); + if (tmp6 > 150) + tmp6 = 0; + + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFE0, tmp6); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFDF, scale << 5); + + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFFFB, 0x4); + if (crystal_freq > 26000000) + b43_radio_set(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0x2); + else + b43_radio_mask(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFD); + + if (val1 == 45) + b43_radio_set(dev, B2063_PLL_JTAG_PLL_VCO1, 0x2); + else + b43_radio_mask(dev, B2063_PLL_JTAG_PLL_VCO1, 0xFD); + + b43_radio_set(dev, B2063_PLL_SP2, 0x3); + udelay(1); + b43_radio_mask(dev, B2063_PLL_SP2, 0xFFFC); + lpphy_b2063_vco_calib(dev); + b43_radio_write(dev, B2063_COMM15, old_comm15); + + return 0; +} + +static int b43_lpphy_op_switch_channel(struct b43_wldev *dev, + unsigned int new_channel) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + int err; + + if (dev->phy.radio_ver == 0x2063) { + err = lpphy_b2063_tune(dev, new_channel); + if (err) + return err; + } else { + err = lpphy_b2062_tune(dev, new_channel); + if (err) + return err; + lpphy_set_analog_filter(dev, new_channel); + lpphy_adjust_gain_table(dev, channel2freq_lp(new_channel)); + } + + lpphy->channel = new_channel; + b43_write16(dev, B43_MMIO_CHANNEL, new_channel); + + return 0; +} + +static int b43_lpphy_op_init(struct b43_wldev *dev) +{ + int err; + + if (dev->dev->bus_type != B43_BUS_SSB) { + b43err(dev->wl, "LP-PHY is supported only on SSB!\n"); + return -EOPNOTSUPP; + } + + lpphy_read_band_sprom(dev); //FIXME should this be in prepare_structs? + lpphy_baseband_init(dev); + lpphy_radio_init(dev); + lpphy_calibrate_rc(dev); + err = b43_lpphy_op_switch_channel(dev, 7); + if (err) { + b43dbg(dev->wl, "Switch to channel 7 failed, error = %d.\n", + err); + } + lpphy_tx_pctl_init(dev); + lpphy_calibration(dev); + //TODO ACI init + + return 0; +} + +static void b43_lpphy_op_adjust_txpower(struct b43_wldev *dev) +{ + //TODO +} + +static enum b43_txpwr_result b43_lpphy_op_recalc_txpower(struct b43_wldev *dev, + bool ignore_tssi) +{ + //TODO + return B43_TXPWR_RES_DONE; +} + +static void b43_lpphy_op_switch_analog(struct b43_wldev *dev, bool on) +{ + if (on) { + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xfff8); + } else { + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0x0007); + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x0007); + } +} + +static void b43_lpphy_op_pwork_15sec(struct b43_wldev *dev) +{ + //TODO +} + +const struct b43_phy_operations b43_phyops_lp = { + .allocate = b43_lpphy_op_allocate, + .free = b43_lpphy_op_free, + .prepare_structs = b43_lpphy_op_prepare_structs, + .init = b43_lpphy_op_init, + .phy_maskset = b43_lpphy_op_maskset, + .radio_read = b43_lpphy_op_radio_read, + .radio_write = b43_lpphy_op_radio_write, + .software_rfkill = b43_lpphy_op_software_rfkill, + .switch_analog = b43_lpphy_op_switch_analog, + .switch_channel = b43_lpphy_op_switch_channel, + .get_default_chan = b43_lpphy_op_get_default_chan, + .set_rx_antenna = b43_lpphy_op_set_rx_antenna, + .recalc_txpower = b43_lpphy_op_recalc_txpower, + .adjust_txpower = b43_lpphy_op_adjust_txpower, + .pwork_15sec = b43_lpphy_op_pwork_15sec, + .pwork_60sec = lpphy_calibration, +}; diff --git a/drivers/net/wireless/broadcom/b43/phy_lp.h b/drivers/net/wireless/broadcom/b43/phy_lp.h new file mode 100644 index 000000000000..62737f700cbc --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_lp.h @@ -0,0 +1,912 @@ +#ifndef LINUX_B43_PHY_LP_H_ +#define LINUX_B43_PHY_LP_H_ + +/* Definitions for the LP-PHY */ + + +/* The CCK PHY register range. */ +#define B43_LPPHY_B_VERSION B43_PHY_CCK(0x00) /* B PHY version */ +#define B43_LPPHY_B_BBCONFIG B43_PHY_CCK(0x01) /* B PHY BBConfig */ +#define B43_LPPHY_B_RX_STAT0 B43_PHY_CCK(0x04) /* B PHY RX Status0 */ +#define B43_LPPHY_B_RX_STAT1 B43_PHY_CCK(0x05) /* B PHY RX Status1 */ +#define B43_LPPHY_B_CRS_THRESH B43_PHY_CCK(0x06) /* B PHY CRS Thresh */ +#define B43_LPPHY_B_TXERROR B43_PHY_CCK(0x07) /* B PHY TxError */ +#define B43_LPPHY_B_CHANNEL B43_PHY_CCK(0x08) /* B PHY Channel */ +#define B43_LPPHY_B_WORKAROUND B43_PHY_CCK(0x09) /* B PHY workaround */ +#define B43_LPPHY_B_TEST B43_PHY_CCK(0x0A) /* B PHY Test */ +#define B43_LPPHY_B_FOURWIRE_ADDR B43_PHY_CCK(0x0B) /* B PHY Fourwire Address */ +#define B43_LPPHY_B_FOURWIRE_DATA_HI B43_PHY_CCK(0x0C) /* B PHY Fourwire Data Hi */ +#define B43_LPPHY_B_FOURWIRE_DATA_LO B43_PHY_CCK(0x0D) /* B PHY Fourwire Data Lo */ +#define B43_LPPHY_B_BIST_STAT B43_PHY_CCK(0x0E) /* B PHY Bist Status */ +#define B43_LPPHY_PA_RAMP_TX_TO B43_PHY_CCK(0x10) /* PA Ramp TX Timeout */ +#define B43_LPPHY_RF_SYNTH_DC_TIMER B43_PHY_CCK(0x11) /* RF Synth DC Timer */ +#define B43_LPPHY_PA_RAMP_TX_TIME_IN B43_PHY_CCK(0x12) /* PA ramp TX Time in */ +#define B43_LPPHY_RX_FILTER_TIME_IN B43_PHY_CCK(0x13) /* RX Filter Time in */ +#define B43_LPPHY_PLL_COEFF_S B43_PHY_CCK(0x18) /* PLL Coefficient(s) */ +#define B43_LPPHY_PLL_OUT B43_PHY_CCK(0x19) /* PLL Out */ +#define B43_LPPHY_RSSI_THRES B43_PHY_CCK(0x20) /* RSSI Threshold */ +#define B43_LPPHY_IQ_THRES_HH B43_PHY_CCK(0x21) /* IQ Threshold HH */ +#define B43_LPPHY_IQ_THRES_H B43_PHY_CCK(0x22) /* IQ Threshold H */ +#define B43_LPPHY_IQ_THRES_L B43_PHY_CCK(0x23) /* IQ Threshold L */ +#define B43_LPPHY_IQ_THRES_LL B43_PHY_CCK(0x24) /* IQ Threshold LL */ +#define B43_LPPHY_AGC_GAIN B43_PHY_CCK(0x25) /* AGC Gain */ +#define B43_LPPHY_LNA_GAIN_RANGE B43_PHY_CCK(0x26) /* LNA Gain Range */ +#define B43_LPPHY_JSSI B43_PHY_CCK(0x27) /* JSSI */ +#define B43_LPPHY_TSSI_CTL B43_PHY_CCK(0x28) /* TSSI Control */ +#define B43_LPPHY_TSSI B43_PHY_CCK(0x29) /* TSSI */ +#define B43_LPPHY_TR_LOSS B43_PHY_CCK(0x2A) /* TR Loss */ +#define B43_LPPHY_LO_LEAKAGE B43_PHY_CCK(0x2B) /* LO Leakage */ +#define B43_LPPHY_LO_RSSIACC B43_PHY_CCK(0x2C) /* LO RSSIAcc */ +#define B43_LPPHY_LO_IQ_MAG_ACC B43_PHY_CCK(0x2D) /* LO IQ Mag Acc */ +#define B43_LPPHY_TX_DCOFFSET1 B43_PHY_CCK(0x2E) /* TX DCOffset1 */ +#define B43_LPPHY_TX_DCOFFSET2 B43_PHY_CCK(0x2F) /* TX DCOffset2 */ +#define B43_LPPHY_SYNCPEAKCNT B43_PHY_CCK(0x30) /* SyncPeakCnt */ +#define B43_LPPHY_SYNCFREQ B43_PHY_CCK(0x31) /* SyncFreq */ +#define B43_LPPHY_SYNCDIVERSITYCTL B43_PHY_CCK(0x32) /* SyncDiversityControl */ +#define B43_LPPHY_PEAKENERGYL B43_PHY_CCK(0x33) /* PeakEnergyL */ +#define B43_LPPHY_PEAKENERGYH B43_PHY_CCK(0x34) /* PeakEnergyH */ +#define B43_LPPHY_SYNCCTL B43_PHY_CCK(0x35) /* SyncControl */ +#define B43_LPPHY_DSSSSTEP B43_PHY_CCK(0x38) /* DsssStep */ +#define B43_LPPHY_DSSSWARMUP B43_PHY_CCK(0x39) /* DsssWarmup */ +#define B43_LPPHY_DSSSSIGPOW B43_PHY_CCK(0x3D) /* DsssSigPow */ +#define B43_LPPHY_SFDDETECTBLOCKTIME B43_PHY_CCK(0x40) /* SfdDetectBlockTIme */ +#define B43_LPPHY_SFDTO B43_PHY_CCK(0x41) /* SFDTimeOut */ +#define B43_LPPHY_SFDCTL B43_PHY_CCK(0x42) /* SFDControl */ +#define B43_LPPHY_RXDBG B43_PHY_CCK(0x43) /* rxDebug */ +#define B43_LPPHY_RX_DELAYCOMP B43_PHY_CCK(0x44) /* RX DelayComp */ +#define B43_LPPHY_CRSDROPOUTTO B43_PHY_CCK(0x45) /* CRSDropoutTimeout */ +#define B43_LPPHY_PSEUDOSHORTTO B43_PHY_CCK(0x46) /* PseudoShortTimeout */ +#define B43_LPPHY_PR3931 B43_PHY_CCK(0x47) /* PR3931 */ +#define B43_LPPHY_DSSSCOEFF1 B43_PHY_CCK(0x48) /* DSSSCoeff1 */ +#define B43_LPPHY_DSSSCOEFF2 B43_PHY_CCK(0x49) /* DSSSCoeff2 */ +#define B43_LPPHY_CCKCOEFF1 B43_PHY_CCK(0x4A) /* CCKCoeff1 */ +#define B43_LPPHY_CCKCOEFF2 B43_PHY_CCK(0x4B) /* CCKCoeff2 */ +#define B43_LPPHY_TRCORR B43_PHY_CCK(0x4C) /* TRCorr */ +#define B43_LPPHY_ANGLESCALE B43_PHY_CCK(0x4D) /* AngleScale */ +#define B43_LPPHY_OPTIONALMODES2 B43_PHY_CCK(0x4F) /* OptionalModes2 */ +#define B43_LPPHY_CCKLMSSTEPSIZE B43_PHY_CCK(0x50) /* CCKLMSStepSize */ +#define B43_LPPHY_DFEBYPASS B43_PHY_CCK(0x51) /* DFEBypass */ +#define B43_LPPHY_CCKSTARTDELAYLONG B43_PHY_CCK(0x52) /* CCKStartDelayLong */ +#define B43_LPPHY_CCKSTARTDELAYSHORT B43_PHY_CCK(0x53) /* CCKStartDelayShort */ +#define B43_LPPHY_PPROCCHDELAY B43_PHY_CCK(0x54) /* PprocChDelay */ +#define B43_LPPHY_PPROCONOFF B43_PHY_CCK(0x55) /* PProcOnOff */ +#define B43_LPPHY_LNAGAINTWOBIT10 B43_PHY_CCK(0x5B) /* LNAGainTwoBit10 */ +#define B43_LPPHY_LNAGAINTWOBIT32 B43_PHY_CCK(0x5C) /* LNAGainTwoBit32 */ +#define B43_LPPHY_OPTIONALMODES B43_PHY_CCK(0x5D) /* OptionalModes */ +#define B43_LPPHY_B_RX_STAT2 B43_PHY_CCK(0x5E) /* B PHY RX Status2 */ +#define B43_LPPHY_B_RX_STAT3 B43_PHY_CCK(0x5F) /* B PHY RX Status3 */ +#define B43_LPPHY_PWDNDACDELAY B43_PHY_CCK(0x63) /* pwdnDacDelay */ +#define B43_LPPHY_FINEDIGIGAIN_CTL B43_PHY_CCK(0x67) /* FineDigiGain Control */ +#define B43_LPPHY_LG2GAINTBLLNA8 B43_PHY_CCK(0x68) /* Lg2GainTblLNA8 */ +#define B43_LPPHY_LG2GAINTBLLNA28 B43_PHY_CCK(0x69) /* Lg2GainTblLNA28 */ +#define B43_LPPHY_GAINTBLLNATRSW B43_PHY_CCK(0x6A) /* GainTblLNATrSw */ +#define B43_LPPHY_PEAKENERGY B43_PHY_CCK(0x6B) /* PeakEnergy */ +#define B43_LPPHY_LG2INITGAIN B43_PHY_CCK(0x6C) /* lg2InitGain */ +#define B43_LPPHY_BLANKCOUNTLNAPGA B43_PHY_CCK(0x6D) /* BlankCountLnaPga */ +#define B43_LPPHY_LNAGAINTWOBIT54 B43_PHY_CCK(0x6E) /* LNAGainTwoBit54 */ +#define B43_LPPHY_LNAGAINTWOBIT76 B43_PHY_CCK(0x6F) /* LNAGainTwoBit76 */ +#define B43_LPPHY_JSSICTL B43_PHY_CCK(0x70) /* JSSIControl */ +#define B43_LPPHY_LG2GAINTBLLNA44 B43_PHY_CCK(0x71) /* Lg2GainTblLNA44 */ +#define B43_LPPHY_LG2GAINTBLLNA62 B43_PHY_CCK(0x72) /* Lg2GainTblLNA62 */ + +/* The OFDM PHY register range. */ +#define B43_LPPHY_VERSION B43_PHY_OFDM(0x00) /* Version */ +#define B43_LPPHY_BBCONFIG B43_PHY_OFDM(0x01) /* BBConfig */ +#define B43_LPPHY_RX_STAT0 B43_PHY_OFDM(0x04) /* RX Status0 */ +#define B43_LPPHY_RX_STAT1 B43_PHY_OFDM(0x05) /* RX Status1 */ +#define B43_LPPHY_TX_ERROR B43_PHY_OFDM(0x07) /* TX Error */ +#define B43_LPPHY_CHANNEL B43_PHY_OFDM(0x08) /* Channel */ +#define B43_LPPHY_WORKAROUND B43_PHY_OFDM(0x09) /* workaround */ +#define B43_LPPHY_FOURWIRE_ADDR B43_PHY_OFDM(0x0B) /* Fourwire Address */ +#define B43_LPPHY_FOURWIREDATAHI B43_PHY_OFDM(0x0C) /* FourwireDataHi */ +#define B43_LPPHY_FOURWIREDATALO B43_PHY_OFDM(0x0D) /* FourwireDataLo */ +#define B43_LPPHY_BISTSTAT0 B43_PHY_OFDM(0x0E) /* BistStatus0 */ +#define B43_LPPHY_BISTSTAT1 B43_PHY_OFDM(0x0F) /* BistStatus1 */ +#define B43_LPPHY_CRSGAIN_CTL B43_PHY_OFDM(0x10) /* crsgain Control */ +#define B43_LPPHY_OFDMPWR_THRESH0 B43_PHY_OFDM(0x11) /* ofdmPower Thresh0 */ +#define B43_LPPHY_OFDMPWR_THRESH1 B43_PHY_OFDM(0x12) /* ofdmPower Thresh1 */ +#define B43_LPPHY_OFDMPWR_THRESH2 B43_PHY_OFDM(0x13) /* ofdmPower Thresh2 */ +#define B43_LPPHY_DSSSPWR_THRESH0 B43_PHY_OFDM(0x14) /* dsssPower Thresh0 */ +#define B43_LPPHY_DSSSPWR_THRESH1 B43_PHY_OFDM(0x15) /* dsssPower Thresh1 */ +#define B43_LPPHY_MINPWR_LEVEL B43_PHY_OFDM(0x16) /* MinPower Level */ +#define B43_LPPHY_OFDMSYNCTHRESH0 B43_PHY_OFDM(0x17) /* ofdmSyncThresh0 */ +#define B43_LPPHY_OFDMSYNCTHRESH1 B43_PHY_OFDM(0x18) /* ofdmSyncThresh1 */ +#define B43_LPPHY_FINEFREQEST B43_PHY_OFDM(0x19) /* FineFreqEst */ +#define B43_LPPHY_IDLEAFTERPKTRXTO B43_PHY_OFDM(0x1A) /* IDLEafterPktRXTimeout */ +#define B43_LPPHY_LTRN_CTL B43_PHY_OFDM(0x1B) /* LTRN Control */ +#define B43_LPPHY_DCOFFSETTRANSIENT B43_PHY_OFDM(0x1C) /* DCOffsetTransient */ +#define B43_LPPHY_PREAMBLEINTO B43_PHY_OFDM(0x1D) /* PreambleInTimeout */ +#define B43_LPPHY_PREAMBLECONFIRMTO B43_PHY_OFDM(0x1E) /* PreambleConfirmTimeout */ +#define B43_LPPHY_CLIPTHRESH B43_PHY_OFDM(0x1F) /* ClipThresh */ +#define B43_LPPHY_CLIPCTRTHRESH B43_PHY_OFDM(0x20) /* ClipCtrThresh */ +#define B43_LPPHY_OFDMSYNCTIMER_CTL B43_PHY_OFDM(0x21) /* ofdmSyncTimer Control */ +#define B43_LPPHY_WAITFORPHYSELTO B43_PHY_OFDM(0x22) /* WaitforPHYSelTimeout */ +#define B43_LPPHY_HIGAINDB B43_PHY_OFDM(0x23) /* HiGainDB */ +#define B43_LPPHY_LOWGAINDB B43_PHY_OFDM(0x24) /* LowGainDB */ +#define B43_LPPHY_VERYLOWGAINDB B43_PHY_OFDM(0x25) /* VeryLowGainDB */ +#define B43_LPPHY_GAINMISMATCH B43_PHY_OFDM(0x26) /* gainMismatch */ +#define B43_LPPHY_GAINDIRECTMISMATCH B43_PHY_OFDM(0x27) /* gaindirectMismatch */ +#define B43_LPPHY_PWR_THRESH0 B43_PHY_OFDM(0x28) /* Power Thresh0 */ +#define B43_LPPHY_PWR_THRESH1 B43_PHY_OFDM(0x29) /* Power Thresh1 */ +#define B43_LPPHY_DETECTOR_DELAY_ADJUST B43_PHY_OFDM(0x2A) /* Detector Delay Adjust */ +#define B43_LPPHY_REDUCED_DETECTOR_DELAY B43_PHY_OFDM(0x2B) /* Reduced Detector Delay */ +#define B43_LPPHY_DATA_TO B43_PHY_OFDM(0x2C) /* data Timeout */ +#define B43_LPPHY_CORRELATOR_DIS_DELAY B43_PHY_OFDM(0x2D) /* correlator Dis Delay */ +#define B43_LPPHY_DIVERSITY_GAINBACK B43_PHY_OFDM(0x2E) /* Diversity GainBack */ +#define B43_LPPHY_DSSS_CONFIRM_CNT B43_PHY_OFDM(0x2F) /* DSSS Confirm Cnt */ +#define B43_LPPHY_DC_BLANK_INT B43_PHY_OFDM(0x30) /* DC Blank Interval */ +#define B43_LPPHY_GAIN_MISMATCH_LIMIT B43_PHY_OFDM(0x31) /* gain Mismatch Limit */ +#define B43_LPPHY_CRS_ED_THRESH B43_PHY_OFDM(0x32) /* crs ed thresh */ +#define B43_LPPHY_PHASE_SHIFT_CTL B43_PHY_OFDM(0x33) /* phase shift Control */ +#define B43_LPPHY_INPUT_PWRDB B43_PHY_OFDM(0x34) /* Input PowerDB */ +#define B43_LPPHY_OFDM_SYNC_CTL B43_PHY_OFDM(0x35) /* ofdm sync Control */ +#define B43_LPPHY_AFE_ADC_CTL_0 B43_PHY_OFDM(0x36) /* Afe ADC Control 0 */ +#define B43_LPPHY_AFE_ADC_CTL_1 B43_PHY_OFDM(0x37) /* Afe ADC Control 1 */ +#define B43_LPPHY_AFE_ADC_CTL_2 B43_PHY_OFDM(0x38) /* Afe ADC Control 2 */ +#define B43_LPPHY_AFE_DAC_CTL B43_PHY_OFDM(0x39) /* Afe DAC Control */ +#define B43_LPPHY_AFE_CTL B43_PHY_OFDM(0x3A) /* Afe Control */ +#define B43_LPPHY_AFE_CTL_OVR B43_PHY_OFDM(0x3B) /* Afe Control Ovr */ +#define B43_LPPHY_AFE_CTL_OVRVAL B43_PHY_OFDM(0x3C) /* Afe Control OvrVal */ +#define B43_LPPHY_AFE_RSSI_CTL_0 B43_PHY_OFDM(0x3D) /* Afe RSSI Control 0 */ +#define B43_LPPHY_AFE_RSSI_CTL_1 B43_PHY_OFDM(0x3E) /* Afe RSSI Control 1 */ +#define B43_LPPHY_AFE_RSSI_SEL B43_PHY_OFDM(0x3F) /* Afe RSSI Sel */ +#define B43_LPPHY_RADAR_THRESH B43_PHY_OFDM(0x40) /* Radar Thresh */ +#define B43_LPPHY_RADAR_BLANK_INT B43_PHY_OFDM(0x41) /* Radar blank Interval */ +#define B43_LPPHY_RADAR_MIN_FM_INT B43_PHY_OFDM(0x42) /* Radar min fm Interval */ +#define B43_LPPHY_RADAR_GAIN_TO B43_PHY_OFDM(0x43) /* Radar gain timeout */ +#define B43_LPPHY_RADAR_PULSE_TO B43_PHY_OFDM(0x44) /* Radar pulse timeout */ +#define B43_LPPHY_RADAR_DETECT_FM_CTL B43_PHY_OFDM(0x45) /* Radar detect FM Control */ +#define B43_LPPHY_RADAR_DETECT_EN B43_PHY_OFDM(0x46) /* Radar detect En */ +#define B43_LPPHY_RADAR_RD_DATA_REG B43_PHY_OFDM(0x47) /* Radar Rd Data Reg */ +#define B43_LPPHY_LP_PHY_CTL B43_PHY_OFDM(0x48) /* LP PHY Control */ +#define B43_LPPHY_CLASSIFIER_CTL B43_PHY_OFDM(0x49) /* classifier Control */ +#define B43_LPPHY_RESET_CTL B43_PHY_OFDM(0x4A) /* reset Control */ +#define B43_LPPHY_CLKEN_CTL B43_PHY_OFDM(0x4B) /* ClkEn Control */ +#define B43_LPPHY_RF_OVERRIDE_0 B43_PHY_OFDM(0x4C) /* RF Override 0 */ +#define B43_LPPHY_RF_OVERRIDE_VAL_0 B43_PHY_OFDM(0x4D) /* RF Override Val 0 */ +#define B43_LPPHY_TR_LOOKUP_1 B43_PHY_OFDM(0x4E) /* TR Lookup 1 */ +#define B43_LPPHY_TR_LOOKUP_2 B43_PHY_OFDM(0x4F) /* TR Lookup 2 */ +#define B43_LPPHY_RSSISELLOOKUP1 B43_PHY_OFDM(0x50) /* RssiSelLookup1 */ +#define B43_LPPHY_IQLO_CAL_CMD B43_PHY_OFDM(0x51) /* iqlo Cal Cmd */ +#define B43_LPPHY_IQLO_CAL_CMD_N_NUM B43_PHY_OFDM(0x52) /* iqlo Cal Cmd N num */ +#define B43_LPPHY_IQLO_CAL_CMD_G_CTL B43_PHY_OFDM(0x53) /* iqlo Cal Cmd G control */ +#define B43_LPPHY_MACINT_DBG_REGISTER B43_PHY_OFDM(0x54) /* macint Debug Register */ +#define B43_LPPHY_TABLE_ADDR B43_PHY_OFDM(0x55) /* Table Address */ +#define B43_LPPHY_TABLEDATALO B43_PHY_OFDM(0x56) /* TabledataLo */ +#define B43_LPPHY_TABLEDATAHI B43_PHY_OFDM(0x57) /* TabledataHi */ +#define B43_LPPHY_PHY_CRS_ENABLE_ADDR B43_PHY_OFDM(0x58) /* phy CRS Enable Address */ +#define B43_LPPHY_IDLETIME_CTL B43_PHY_OFDM(0x59) /* Idletime Control */ +#define B43_LPPHY_IDLETIME_CRS_ON_LO B43_PHY_OFDM(0x5A) /* Idletime CRS On Lo */ +#define B43_LPPHY_IDLETIME_CRS_ON_HI B43_PHY_OFDM(0x5B) /* Idletime CRS On Hi */ +#define B43_LPPHY_IDLETIME_MEAS_TIME_LO B43_PHY_OFDM(0x5C) /* Idletime Meas Time Lo */ +#define B43_LPPHY_IDLETIME_MEAS_TIME_HI B43_PHY_OFDM(0x5D) /* Idletime Meas Time Hi */ +#define B43_LPPHY_RESET_LEN_OFDM_TX_ADDR B43_PHY_OFDM(0x5E) /* Reset len Ofdm TX Address */ +#define B43_LPPHY_RESET_LEN_OFDM_RX_ADDR B43_PHY_OFDM(0x5F) /* Reset len Ofdm RX Address */ +#define B43_LPPHY_REG_CRS_ENABLE B43_PHY_OFDM(0x60) /* reg crs enable */ +#define B43_LPPHY_PLCP_TMT_STR0_CTR_MIN B43_PHY_OFDM(0x61) /* PLCP Tmt Str0 Ctr Min */ +#define B43_LPPHY_PKT_FSM_RESET_LEN_VAL B43_PHY_OFDM(0x62) /* Pkt fsm Reset Len Value */ +#define B43_LPPHY_READSYM2RESET_CTL B43_PHY_OFDM(0x63) /* readsym2reset Control */ +#define B43_LPPHY_DC_FILTER_DELAY1 B43_PHY_OFDM(0x64) /* Dc filter delay1 */ +#define B43_LPPHY_PACKET_RX_ACTIVE_TO B43_PHY_OFDM(0x65) /* packet rx Active timeout */ +#define B43_LPPHY_ED_TOVAL B43_PHY_OFDM(0x66) /* ed timeoutValue */ +#define B43_LPPHY_HOLD_CRS_ON_VAL B43_PHY_OFDM(0x67) /* hold CRS On Value */ +#define B43_LPPHY_OFDM_TX_PHY_CRS_DELAY_VAL B43_PHY_OFDM(0x69) /* ofdm tx phy CRS Delay Value */ +#define B43_LPPHY_CCK_TX_PHY_CRS_DELAY_VAL B43_PHY_OFDM(0x6A) /* cck tx phy CRS Delay Value */ +#define B43_LPPHY_ED_ON_CONFIRM_TIMER_VAL B43_PHY_OFDM(0x6B) /* Ed on confirm Timer Value */ +#define B43_LPPHY_ED_OFFSET_CONFIRM_TIMER_VAL B43_PHY_OFDM(0x6C) /* Ed offset confirm Timer Value */ +#define B43_LPPHY_PHY_CRS_OFFSET_TIMER_VAL B43_PHY_OFDM(0x6D) /* phy CRS offset Timer Value */ +#define B43_LPPHY_ADC_COMPENSATION_CTL B43_PHY_OFDM(0x70) /* ADC Compensation Control */ +#define B43_LPPHY_LOG2_RBPSK_ADDR B43_PHY_OFDM(0x71) /* log2 RBPSK Address */ +#define B43_LPPHY_LOG2_RQPSK_ADDR B43_PHY_OFDM(0x72) /* log2 RQPSK Address */ +#define B43_LPPHY_LOG2_R16QAM_ADDR B43_PHY_OFDM(0x73) /* log2 R16QAM Address */ +#define B43_LPPHY_LOG2_R64QAM_ADDR B43_PHY_OFDM(0x74) /* log2 R64QAM Address */ +#define B43_LPPHY_OFFSET_BPSK_ADDR B43_PHY_OFDM(0x75) /* offset BPSK Address */ +#define B43_LPPHY_OFFSET_QPSK_ADDR B43_PHY_OFDM(0x76) /* offset QPSK Address */ +#define B43_LPPHY_OFFSET_16QAM_ADDR B43_PHY_OFDM(0x77) /* offset 16QAM Address */ +#define B43_LPPHY_OFFSET_64QAM_ADDR B43_PHY_OFDM(0x78) /* offset 64QAM Address */ +#define B43_LPPHY_ALPHA1 B43_PHY_OFDM(0x79) /* Alpha1 */ +#define B43_LPPHY_ALPHA2 B43_PHY_OFDM(0x7A) /* Alpha2 */ +#define B43_LPPHY_BETA1 B43_PHY_OFDM(0x7B) /* Beta1 */ +#define B43_LPPHY_BETA2 B43_PHY_OFDM(0x7C) /* Beta2 */ +#define B43_LPPHY_LOOP_NUM_ADDR B43_PHY_OFDM(0x7D) /* Loop Num Address */ +#define B43_LPPHY_STR_COLLMAX_SMPL_ADDR B43_PHY_OFDM(0x7E) /* Str Collmax Sample Address */ +#define B43_LPPHY_MAX_SMPL_COARSE_FINE_ADDR B43_PHY_OFDM(0x7F) /* Max Sample Coarse/Fine Address */ +#define B43_LPPHY_MAX_SMPL_COARSE_STR0CTR_ADDR B43_PHY_OFDM(0x80) /* Max Sample Coarse/Str0Ctr Address */ +#define B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR B43_PHY_OFDM(0x81) /* IQ Enable Wait Time Address */ +#define B43_LPPHY_IQ_NUM_SMPLS_ADDR B43_PHY_OFDM(0x82) /* IQ Num Samples Address */ +#define B43_LPPHY_IQ_ACC_HI_ADDR B43_PHY_OFDM(0x83) /* IQ Acc Hi Address */ +#define B43_LPPHY_IQ_ACC_LO_ADDR B43_PHY_OFDM(0x84) /* IQ Acc Lo Address */ +#define B43_LPPHY_IQ_I_PWR_ACC_HI_ADDR B43_PHY_OFDM(0x85) /* IQ I PWR Acc Hi Address */ +#define B43_LPPHY_IQ_I_PWR_ACC_LO_ADDR B43_PHY_OFDM(0x86) /* IQ I PWR Acc Lo Address */ +#define B43_LPPHY_IQ_Q_PWR_ACC_HI_ADDR B43_PHY_OFDM(0x87) /* IQ Q PWR Acc Hi Address */ +#define B43_LPPHY_IQ_Q_PWR_ACC_LO_ADDR B43_PHY_OFDM(0x88) /* IQ Q PWR Acc Lo Address */ +#define B43_LPPHY_MAXNUMSTEPS B43_PHY_OFDM(0x89) /* MaxNumsteps */ +#define B43_LPPHY_ROTORPHASE_ADDR B43_PHY_OFDM(0x8A) /* RotorPhase Address */ +#define B43_LPPHY_ADVANCEDRETARDROTOR_ADDR B43_PHY_OFDM(0x8B) /* AdvancedRetardRotor Address */ +#define B43_LPPHY_RSSIADCDELAY_CTL_ADDR B43_PHY_OFDM(0x8D) /* rssiAdcdelay Control Address */ +#define B43_LPPHY_TSSISTAT_ADDR B43_PHY_OFDM(0x8E) /* tssiStatus Address */ +#define B43_LPPHY_TEMPSENSESTAT_ADDR B43_PHY_OFDM(0x8F) /* tempsenseStatus Address */ +#define B43_LPPHY_TEMPSENSE_CTL_ADDR B43_PHY_OFDM(0x90) /* tempsense Control Address */ +#define B43_LPPHY_WRSSISTAT_ADDR B43_PHY_OFDM(0x91) /* wrssistatus Address */ +#define B43_LPPHY_MUFACTORADDR B43_PHY_OFDM(0x92) /* mufactoraddr */ +#define B43_LPPHY_SCRAMSTATE_ADDR B43_PHY_OFDM(0x93) /* scramstate Address */ +#define B43_LPPHY_TXHOLDOFFADDR B43_PHY_OFDM(0x94) /* txholdoffaddr */ +#define B43_LPPHY_PKTGAINVAL_ADDR B43_PHY_OFDM(0x95) /* pktgainval Address */ +#define B43_LPPHY_COARSEESTIM_ADDR B43_PHY_OFDM(0x96) /* Coarseestim Address */ +#define B43_LPPHY_STATE_TRANSITION_ADDR B43_PHY_OFDM(0x97) /* state Transition Address */ +#define B43_LPPHY_TRN_OFFSET_ADDR B43_PHY_OFDM(0x98) /* TRN offset Address */ +#define B43_LPPHY_NUM_ROTOR_ADDR B43_PHY_OFDM(0x99) /* Num Rotor Address */ +#define B43_LPPHY_VITERBI_OFFSET_ADDR B43_PHY_OFDM(0x9A) /* Viterbi Offset Address */ +#define B43_LPPHY_SMPL_COLLECT_WAIT_ADDR B43_PHY_OFDM(0x9B) /* Sample collect wait Address */ +#define B43_LPPHY_A_PHY_CTL_ADDR B43_PHY_OFDM(0x9C) /* A PHY Control Address */ +#define B43_LPPHY_NUM_PASS_THROUGH_ADDR B43_PHY_OFDM(0x9D) /* Num Pass Through Address */ +#define B43_LPPHY_RX_COMP_COEFF_S B43_PHY_OFDM(0x9E) /* RX Comp coefficient(s) */ +#define B43_LPPHY_CPAROTATEVAL B43_PHY_OFDM(0x9F) /* cpaRotateValue */ +#define B43_LPPHY_SMPL_PLAY_COUNT B43_PHY_OFDM(0xA0) /* Sample play count */ +#define B43_LPPHY_SMPL_PLAY_BUFFER_CTL B43_PHY_OFDM(0xA1) /* Sample play Buffer Control */ +#define B43_LPPHY_FOURWIRE_CTL B43_PHY_OFDM(0xA2) /* fourwire Control */ +#define B43_LPPHY_CPA_TAILCOUNT_VAL B43_PHY_OFDM(0xA3) /* CPA TailCount Value */ +#define B43_LPPHY_TX_PWR_CTL_CMD B43_PHY_OFDM(0xA4) /* TX Power Control Cmd */ +#define B43_LPPHY_TX_PWR_CTL_CMD_MODE 0xE000 /* TX power control mode mask */ +#define B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF 0x0000 /* TX power control is OFF */ +#define B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW 0x8000 /* TX power control is SOFTWARE */ +#define B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW 0xE000 /* TX power control is HARDWARE */ +#define B43_LPPHY_TX_PWR_CTL_NNUM B43_PHY_OFDM(0xA5) /* TX Power Control Nnum */ +#define B43_LPPHY_TX_PWR_CTL_IDLETSSI B43_PHY_OFDM(0xA6) /* TX Power Control IdleTssi */ +#define B43_LPPHY_TX_PWR_CTL_TARGETPWR B43_PHY_OFDM(0xA7) /* TX Power Control TargetPower */ +#define B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT B43_PHY_OFDM(0xA8) /* TX Power Control DeltaPower Limit */ +#define B43_LPPHY_TX_PWR_CTL_BASEINDEX B43_PHY_OFDM(0xA9) /* TX Power Control BaseIndex */ +#define B43_LPPHY_TX_PWR_CTL_PWR_INDEX B43_PHY_OFDM(0xAA) /* TX Power Control Power Index */ +#define B43_LPPHY_TX_PWR_CTL_STAT B43_PHY_OFDM(0xAB) /* TX Power Control Status */ +#define B43_LPPHY_LP_RF_SIGNAL_LUT B43_PHY_OFDM(0xAC) /* LP RF signal LUT */ +#define B43_LPPHY_RX_RADIO_CTL_FILTER_STATE B43_PHY_OFDM(0xAD) /* RX Radio Control Filter State */ +#define B43_LPPHY_RX_RADIO_CTL B43_PHY_OFDM(0xAE) /* RX Radio Control */ +#define B43_LPPHY_NRSSI_STAT_ADDR B43_PHY_OFDM(0xAF) /* NRSSI status Address */ +#define B43_LPPHY_RF_OVERRIDE_2 B43_PHY_OFDM(0xB0) /* RF override 2 */ +#define B43_LPPHY_RF_OVERRIDE_2_VAL B43_PHY_OFDM(0xB1) /* RF override 2 val */ +#define B43_LPPHY_PS_CTL_OVERRIDE_VAL0 B43_PHY_OFDM(0xB2) /* PS Control override val0 */ +#define B43_LPPHY_PS_CTL_OVERRIDE_VAL1 B43_PHY_OFDM(0xB3) /* PS Control override val1 */ +#define B43_LPPHY_PS_CTL_OVERRIDE_VAL2 B43_PHY_OFDM(0xB4) /* PS Control override val2 */ +#define B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL B43_PHY_OFDM(0xB5) /* TX gain Control override val */ +#define B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL B43_PHY_OFDM(0xB6) /* RX gain Control override val */ +#define B43_LPPHY_AFE_DDFS B43_PHY_OFDM(0xB7) /* AFE DDFS */ +#define B43_LPPHY_AFE_DDFS_POINTER_INIT B43_PHY_OFDM(0xB8) /* AFE DDFS pointer init */ +#define B43_LPPHY_AFE_DDFS_INCR_INIT B43_PHY_OFDM(0xB9) /* AFE DDFS incr init */ +#define B43_LPPHY_MRCNOISEREDUCTION B43_PHY_OFDM(0xBA) /* mrcNoiseReduction */ +#define B43_LPPHY_TR_LOOKUP_3 B43_PHY_OFDM(0xBB) /* TR Lookup 3 */ +#define B43_LPPHY_TR_LOOKUP_4 B43_PHY_OFDM(0xBC) /* TR Lookup 4 */ +#define B43_LPPHY_RADAR_FIFO_STAT B43_PHY_OFDM(0xBD) /* Radar FIFO Status */ +#define B43_LPPHY_GPIO_OUTEN B43_PHY_OFDM(0xBE) /* GPIO Out enable */ +#define B43_LPPHY_GPIO_SELECT B43_PHY_OFDM(0xBF) /* GPIO Select */ +#define B43_LPPHY_GPIO_OUT B43_PHY_OFDM(0xC0) /* GPIO Out */ +#define B43_LPPHY_4C3 B43_PHY_OFDM(0xC3) /* unknown, used during BB init */ +#define B43_LPPHY_4C4 B43_PHY_OFDM(0xC4) /* unknown, used during BB init */ +#define B43_LPPHY_4C5 B43_PHY_OFDM(0xC5) /* unknown, used during BB init */ +#define B43_LPPHY_TR_LOOKUP_5 B43_PHY_OFDM(0xC7) /* TR Lookup 5 */ +#define B43_LPPHY_TR_LOOKUP_6 B43_PHY_OFDM(0xC8) /* TR Lookup 6 */ +#define B43_LPPHY_TR_LOOKUP_7 B43_PHY_OFDM(0xC9) /* TR Lookup 7 */ +#define B43_LPPHY_TR_LOOKUP_8 B43_PHY_OFDM(0xCA) /* TR Lookup 8 */ +#define B43_LPPHY_RF_PWR_OVERRIDE B43_PHY_OFDM(0xD3) /* RF power override */ + + + +/* Radio register access decorators. */ +#define B43_LP_RADIO(radio_reg) (radio_reg) +#define B43_LP_NORTH(radio_reg) B43_LP_RADIO(radio_reg) +#define B43_LP_SOUTH(radio_reg) B43_LP_RADIO((radio_reg) | 0x4000) + + +/*** Broadcom 2062 NORTH radio registers ***/ +#define B2062_N_COMM1 B43_LP_NORTH(0x000) /* Common 01 (north) */ +#define B2062_N_COMM2 B43_LP_NORTH(0x002) /* Common 02 (north) */ +#define B2062_N_COMM3 B43_LP_NORTH(0x003) /* Common 03 (north) */ +#define B2062_N_COMM4 B43_LP_NORTH(0x004) /* Common 04 (north) */ +#define B2062_N_COMM5 B43_LP_NORTH(0x005) /* Common 05 (north) */ +#define B2062_N_COMM6 B43_LP_NORTH(0x006) /* Common 06 (north) */ +#define B2062_N_COMM7 B43_LP_NORTH(0x007) /* Common 07 (north) */ +#define B2062_N_COMM8 B43_LP_NORTH(0x008) /* Common 08 (north) */ +#define B2062_N_COMM9 B43_LP_NORTH(0x009) /* Common 09 (north) */ +#define B2062_N_COMM10 B43_LP_NORTH(0x00A) /* Common 10 (north) */ +#define B2062_N_COMM11 B43_LP_NORTH(0x00B) /* Common 11 (north) */ +#define B2062_N_COMM12 B43_LP_NORTH(0x00C) /* Common 12 (north) */ +#define B2062_N_COMM13 B43_LP_NORTH(0x00D) /* Common 13 (north) */ +#define B2062_N_COMM14 B43_LP_NORTH(0x00E) /* Common 14 (north) */ +#define B2062_N_COMM15 B43_LP_NORTH(0x00F) /* Common 15 (north) */ +#define B2062_N_PDN_CTL0 B43_LP_NORTH(0x010) /* PDN Control 0 (north) */ +#define B2062_N_PDN_CTL1 B43_LP_NORTH(0x011) /* PDN Control 1 (north) */ +#define B2062_N_PDN_CTL2 B43_LP_NORTH(0x012) /* PDN Control 2 (north) */ +#define B2062_N_PDN_CTL3 B43_LP_NORTH(0x013) /* PDN Control 3 (north) */ +#define B2062_N_PDN_CTL4 B43_LP_NORTH(0x014) /* PDN Control 4 (north) */ +#define B2062_N_GEN_CTL0 B43_LP_NORTH(0x015) /* GEN Control 0 (north) */ +#define B2062_N_IQ_CALIB B43_LP_NORTH(0x016) /* IQ Calibration (north) */ +#define B2062_N_LGENC B43_LP_NORTH(0x017) /* LGENC (north) */ +#define B2062_N_LGENA_LPF B43_LP_NORTH(0x018) /* LGENA LPF (north) */ +#define B2062_N_LGENA_BIAS0 B43_LP_NORTH(0x019) /* LGENA Bias 0 (north) */ +#define B2062_N_LGNEA_BIAS1 B43_LP_NORTH(0x01A) /* LGNEA Bias 1 (north) */ +#define B2062_N_LGENA_CTL0 B43_LP_NORTH(0x01B) /* LGENA Control 0 (north) */ +#define B2062_N_LGENA_CTL1 B43_LP_NORTH(0x01C) /* LGENA Control 1 (north) */ +#define B2062_N_LGENA_CTL2 B43_LP_NORTH(0x01D) /* LGENA Control 2 (north) */ +#define B2062_N_LGENA_TUNE0 B43_LP_NORTH(0x01E) /* LGENA Tune 0 (north) */ +#define B2062_N_LGENA_TUNE1 B43_LP_NORTH(0x01F) /* LGENA Tune 1 (north) */ +#define B2062_N_LGENA_TUNE2 B43_LP_NORTH(0x020) /* LGENA Tune 2 (north) */ +#define B2062_N_LGENA_TUNE3 B43_LP_NORTH(0x021) /* LGENA Tune 3 (north) */ +#define B2062_N_LGENA_CTL3 B43_LP_NORTH(0x022) /* LGENA Control 3 (north) */ +#define B2062_N_LGENA_CTL4 B43_LP_NORTH(0x023) /* LGENA Control 4 (north) */ +#define B2062_N_LGENA_CTL5 B43_LP_NORTH(0x024) /* LGENA Control 5 (north) */ +#define B2062_N_LGENA_CTL6 B43_LP_NORTH(0x025) /* LGENA Control 6 (north) */ +#define B2062_N_LGENA_CTL7 B43_LP_NORTH(0x026) /* LGENA Control 7 (north) */ +#define B2062_N_RXA_CTL0 B43_LP_NORTH(0x027) /* RXA Control 0 (north) */ +#define B2062_N_RXA_CTL1 B43_LP_NORTH(0x028) /* RXA Control 1 (north) */ +#define B2062_N_RXA_CTL2 B43_LP_NORTH(0x029) /* RXA Control 2 (north) */ +#define B2062_N_RXA_CTL3 B43_LP_NORTH(0x02A) /* RXA Control 3 (north) */ +#define B2062_N_RXA_CTL4 B43_LP_NORTH(0x02B) /* RXA Control 4 (north) */ +#define B2062_N_RXA_CTL5 B43_LP_NORTH(0x02C) /* RXA Control 5 (north) */ +#define B2062_N_RXA_CTL6 B43_LP_NORTH(0x02D) /* RXA Control 6 (north) */ +#define B2062_N_RXA_CTL7 B43_LP_NORTH(0x02E) /* RXA Control 7 (north) */ +#define B2062_N_RXBB_CTL0 B43_LP_NORTH(0x02F) /* RXBB Control 0 (north) */ +#define B2062_N_RXBB_CTL1 B43_LP_NORTH(0x030) /* RXBB Control 1 (north) */ +#define B2062_N_RXBB_CTL2 B43_LP_NORTH(0x031) /* RXBB Control 2 (north) */ +#define B2062_N_RXBB_GAIN0 B43_LP_NORTH(0x032) /* RXBB Gain 0 (north) */ +#define B2062_N_RXBB_GAIN1 B43_LP_NORTH(0x033) /* RXBB Gain 1 (north) */ +#define B2062_N_RXBB_GAIN2 B43_LP_NORTH(0x034) /* RXBB Gain 2 (north) */ +#define B2062_N_RXBB_GAIN3 B43_LP_NORTH(0x035) /* RXBB Gain 3 (north) */ +#define B2062_N_RXBB_RSSI0 B43_LP_NORTH(0x036) /* RXBB RSSI 0 (north) */ +#define B2062_N_RXBB_RSSI1 B43_LP_NORTH(0x037) /* RXBB RSSI 1 (north) */ +#define B2062_N_RXBB_CALIB0 B43_LP_NORTH(0x038) /* RXBB Calibration0 (north) */ +#define B2062_N_RXBB_CALIB1 B43_LP_NORTH(0x039) /* RXBB Calibration1 (north) */ +#define B2062_N_RXBB_CALIB2 B43_LP_NORTH(0x03A) /* RXBB Calibration2 (north) */ +#define B2062_N_RXBB_BIAS0 B43_LP_NORTH(0x03B) /* RXBB Bias 0 (north) */ +#define B2062_N_RXBB_BIAS1 B43_LP_NORTH(0x03C) /* RXBB Bias 1 (north) */ +#define B2062_N_RXBB_BIAS2 B43_LP_NORTH(0x03D) /* RXBB Bias 2 (north) */ +#define B2062_N_RXBB_BIAS3 B43_LP_NORTH(0x03E) /* RXBB Bias 3 (north) */ +#define B2062_N_RXBB_BIAS4 B43_LP_NORTH(0x03F) /* RXBB Bias 4 (north) */ +#define B2062_N_RXBB_BIAS5 B43_LP_NORTH(0x040) /* RXBB Bias 5 (north) */ +#define B2062_N_RXBB_RSSI2 B43_LP_NORTH(0x041) /* RXBB RSSI 2 (north) */ +#define B2062_N_RXBB_RSSI3 B43_LP_NORTH(0x042) /* RXBB RSSI 3 (north) */ +#define B2062_N_RXBB_RSSI4 B43_LP_NORTH(0x043) /* RXBB RSSI 4 (north) */ +#define B2062_N_RXBB_RSSI5 B43_LP_NORTH(0x044) /* RXBB RSSI 5 (north) */ +#define B2062_N_TX_CTL0 B43_LP_NORTH(0x045) /* TX Control 0 (north) */ +#define B2062_N_TX_CTL1 B43_LP_NORTH(0x046) /* TX Control 1 (north) */ +#define B2062_N_TX_CTL2 B43_LP_NORTH(0x047) /* TX Control 2 (north) */ +#define B2062_N_TX_CTL3 B43_LP_NORTH(0x048) /* TX Control 3 (north) */ +#define B2062_N_TX_CTL4 B43_LP_NORTH(0x049) /* TX Control 4 (north) */ +#define B2062_N_TX_CTL5 B43_LP_NORTH(0x04A) /* TX Control 5 (north) */ +#define B2062_N_TX_CTL6 B43_LP_NORTH(0x04B) /* TX Control 6 (north) */ +#define B2062_N_TX_CTL7 B43_LP_NORTH(0x04C) /* TX Control 7 (north) */ +#define B2062_N_TX_CTL8 B43_LP_NORTH(0x04D) /* TX Control 8 (north) */ +#define B2062_N_TX_CTL9 B43_LP_NORTH(0x04E) /* TX Control 9 (north) */ +#define B2062_N_TX_CTL_A B43_LP_NORTH(0x04F) /* TX Control A (north) */ +#define B2062_N_TX_GC2G B43_LP_NORTH(0x050) /* TX GC2G (north) */ +#define B2062_N_TX_GC5G B43_LP_NORTH(0x051) /* TX GC5G (north) */ +#define B2062_N_TX_TUNE B43_LP_NORTH(0x052) /* TX Tune (north) */ +#define B2062_N_TX_PAD B43_LP_NORTH(0x053) /* TX PAD (north) */ +#define B2062_N_TX_PGA B43_LP_NORTH(0x054) /* TX PGA (north) */ +#define B2062_N_TX_PADAUX B43_LP_NORTH(0x055) /* TX PADAUX (north) */ +#define B2062_N_TX_PGAAUX B43_LP_NORTH(0x056) /* TX PGAAUX (north) */ +#define B2062_N_TSSI_CTL0 B43_LP_NORTH(0x057) /* TSSI Control 0 (north) */ +#define B2062_N_TSSI_CTL1 B43_LP_NORTH(0x058) /* TSSI Control 1 (north) */ +#define B2062_N_TSSI_CTL2 B43_LP_NORTH(0x059) /* TSSI Control 2 (north) */ +#define B2062_N_IQ_CALIB_CTL0 B43_LP_NORTH(0x05A) /* IQ Calibration Control 0 (north) */ +#define B2062_N_IQ_CALIB_CTL1 B43_LP_NORTH(0x05B) /* IQ Calibration Control 1 (north) */ +#define B2062_N_IQ_CALIB_CTL2 B43_LP_NORTH(0x05C) /* IQ Calibration Control 2 (north) */ +#define B2062_N_CALIB_TS B43_LP_NORTH(0x05D) /* Calibration TS (north) */ +#define B2062_N_CALIB_CTL0 B43_LP_NORTH(0x05E) /* Calibration Control 0 (north) */ +#define B2062_N_CALIB_CTL1 B43_LP_NORTH(0x05F) /* Calibration Control 1 (north) */ +#define B2062_N_CALIB_CTL2 B43_LP_NORTH(0x060) /* Calibration Control 2 (north) */ +#define B2062_N_CALIB_CTL3 B43_LP_NORTH(0x061) /* Calibration Control 3 (north) */ +#define B2062_N_CALIB_CTL4 B43_LP_NORTH(0x062) /* Calibration Control 4 (north) */ +#define B2062_N_CALIB_DBG0 B43_LP_NORTH(0x063) /* Calibration Debug 0 (north) */ +#define B2062_N_CALIB_DBG1 B43_LP_NORTH(0x064) /* Calibration Debug 1 (north) */ +#define B2062_N_CALIB_DBG2 B43_LP_NORTH(0x065) /* Calibration Debug 2 (north) */ +#define B2062_N_CALIB_DBG3 B43_LP_NORTH(0x066) /* Calibration Debug 3 (north) */ +#define B2062_N_PSENSE_CTL0 B43_LP_NORTH(0x069) /* PSENSE Control 0 (north) */ +#define B2062_N_PSENSE_CTL1 B43_LP_NORTH(0x06A) /* PSENSE Control 1 (north) */ +#define B2062_N_PSENSE_CTL2 B43_LP_NORTH(0x06B) /* PSENSE Control 2 (north) */ +#define B2062_N_TEST_BUF0 B43_LP_NORTH(0x06C) /* TEST BUF0 (north) */ + +/*** Broadcom 2062 SOUTH radio registers ***/ +#define B2062_S_COMM1 B43_LP_SOUTH(0x000) /* Common 01 (south) */ +#define B2062_S_RADIO_ID_CODE B43_LP_SOUTH(0x001) /* Radio ID code (south) */ +#define B2062_S_COMM2 B43_LP_SOUTH(0x002) /* Common 02 (south) */ +#define B2062_S_COMM3 B43_LP_SOUTH(0x003) /* Common 03 (south) */ +#define B2062_S_COMM4 B43_LP_SOUTH(0x004) /* Common 04 (south) */ +#define B2062_S_COMM5 B43_LP_SOUTH(0x005) /* Common 05 (south) */ +#define B2062_S_COMM6 B43_LP_SOUTH(0x006) /* Common 06 (south) */ +#define B2062_S_COMM7 B43_LP_SOUTH(0x007) /* Common 07 (south) */ +#define B2062_S_COMM8 B43_LP_SOUTH(0x008) /* Common 08 (south) */ +#define B2062_S_COMM9 B43_LP_SOUTH(0x009) /* Common 09 (south) */ +#define B2062_S_COMM10 B43_LP_SOUTH(0x00A) /* Common 10 (south) */ +#define B2062_S_COMM11 B43_LP_SOUTH(0x00B) /* Common 11 (south) */ +#define B2062_S_COMM12 B43_LP_SOUTH(0x00C) /* Common 12 (south) */ +#define B2062_S_COMM13 B43_LP_SOUTH(0x00D) /* Common 13 (south) */ +#define B2062_S_COMM14 B43_LP_SOUTH(0x00E) /* Common 14 (south) */ +#define B2062_S_COMM15 B43_LP_SOUTH(0x00F) /* Common 15 (south) */ +#define B2062_S_PDS_CTL0 B43_LP_SOUTH(0x010) /* PDS Control 0 (south) */ +#define B2062_S_PDS_CTL1 B43_LP_SOUTH(0x011) /* PDS Control 1 (south) */ +#define B2062_S_PDS_CTL2 B43_LP_SOUTH(0x012) /* PDS Control 2 (south) */ +#define B2062_S_PDS_CTL3 B43_LP_SOUTH(0x013) /* PDS Control 3 (south) */ +#define B2062_S_BG_CTL0 B43_LP_SOUTH(0x014) /* BG Control 0 (south) */ +#define B2062_S_BG_CTL1 B43_LP_SOUTH(0x015) /* BG Control 1 (south) */ +#define B2062_S_BG_CTL2 B43_LP_SOUTH(0x016) /* BG Control 2 (south) */ +#define B2062_S_LGENG_CTL0 B43_LP_SOUTH(0x017) /* LGENG Control 00 (south) */ +#define B2062_S_LGENG_CTL1 B43_LP_SOUTH(0x018) /* LGENG Control 01 (south) */ +#define B2062_S_LGENG_CTL2 B43_LP_SOUTH(0x019) /* LGENG Control 02 (south) */ +#define B2062_S_LGENG_CTL3 B43_LP_SOUTH(0x01A) /* LGENG Control 03 (south) */ +#define B2062_S_LGENG_CTL4 B43_LP_SOUTH(0x01B) /* LGENG Control 04 (south) */ +#define B2062_S_LGENG_CTL5 B43_LP_SOUTH(0x01C) /* LGENG Control 05 (south) */ +#define B2062_S_LGENG_CTL6 B43_LP_SOUTH(0x01D) /* LGENG Control 06 (south) */ +#define B2062_S_LGENG_CTL7 B43_LP_SOUTH(0x01E) /* LGENG Control 07 (south) */ +#define B2062_S_LGENG_CTL8 B43_LP_SOUTH(0x01F) /* LGENG Control 08 (south) */ +#define B2062_S_LGENG_CTL9 B43_LP_SOUTH(0x020) /* LGENG Control 09 (south) */ +#define B2062_S_LGENG_CTL10 B43_LP_SOUTH(0x021) /* LGENG Control 10 (south) */ +#define B2062_S_LGENG_CTL11 B43_LP_SOUTH(0x022) /* LGENG Control 11 (south) */ +#define B2062_S_REFPLL_CTL0 B43_LP_SOUTH(0x023) /* REFPLL Control 00 (south) */ +#define B2062_S_REFPLL_CTL1 B43_LP_SOUTH(0x024) /* REFPLL Control 01 (south) */ +#define B2062_S_REFPLL_CTL2 B43_LP_SOUTH(0x025) /* REFPLL Control 02 (south) */ +#define B2062_S_REFPLL_CTL3 B43_LP_SOUTH(0x026) /* REFPLL Control 03 (south) */ +#define B2062_S_REFPLL_CTL4 B43_LP_SOUTH(0x027) /* REFPLL Control 04 (south) */ +#define B2062_S_REFPLL_CTL5 B43_LP_SOUTH(0x028) /* REFPLL Control 05 (south) */ +#define B2062_S_REFPLL_CTL6 B43_LP_SOUTH(0x029) /* REFPLL Control 06 (south) */ +#define B2062_S_REFPLL_CTL7 B43_LP_SOUTH(0x02A) /* REFPLL Control 07 (south) */ +#define B2062_S_REFPLL_CTL8 B43_LP_SOUTH(0x02B) /* REFPLL Control 08 (south) */ +#define B2062_S_REFPLL_CTL9 B43_LP_SOUTH(0x02C) /* REFPLL Control 09 (south) */ +#define B2062_S_REFPLL_CTL10 B43_LP_SOUTH(0x02D) /* REFPLL Control 10 (south) */ +#define B2062_S_REFPLL_CTL11 B43_LP_SOUTH(0x02E) /* REFPLL Control 11 (south) */ +#define B2062_S_REFPLL_CTL12 B43_LP_SOUTH(0x02F) /* REFPLL Control 12 (south) */ +#define B2062_S_REFPLL_CTL13 B43_LP_SOUTH(0x030) /* REFPLL Control 13 (south) */ +#define B2062_S_REFPLL_CTL14 B43_LP_SOUTH(0x031) /* REFPLL Control 14 (south) */ +#define B2062_S_REFPLL_CTL15 B43_LP_SOUTH(0x032) /* REFPLL Control 15 (south) */ +#define B2062_S_REFPLL_CTL16 B43_LP_SOUTH(0x033) /* REFPLL Control 16 (south) */ +#define B2062_S_RFPLL_CTL0 B43_LP_SOUTH(0x034) /* RFPLL Control 00 (south) */ +#define B2062_S_RFPLL_CTL1 B43_LP_SOUTH(0x035) /* RFPLL Control 01 (south) */ +#define B2062_S_RFPLL_CTL2 B43_LP_SOUTH(0x036) /* RFPLL Control 02 (south) */ +#define B2062_S_RFPLL_CTL3 B43_LP_SOUTH(0x037) /* RFPLL Control 03 (south) */ +#define B2062_S_RFPLL_CTL4 B43_LP_SOUTH(0x038) /* RFPLL Control 04 (south) */ +#define B2062_S_RFPLL_CTL5 B43_LP_SOUTH(0x039) /* RFPLL Control 05 (south) */ +#define B2062_S_RFPLL_CTL6 B43_LP_SOUTH(0x03A) /* RFPLL Control 06 (south) */ +#define B2062_S_RFPLL_CTL7 B43_LP_SOUTH(0x03B) /* RFPLL Control 07 (south) */ +#define B2062_S_RFPLL_CTL8 B43_LP_SOUTH(0x03C) /* RFPLL Control 08 (south) */ +#define B2062_S_RFPLL_CTL9 B43_LP_SOUTH(0x03D) /* RFPLL Control 09 (south) */ +#define B2062_S_RFPLL_CTL10 B43_LP_SOUTH(0x03E) /* RFPLL Control 10 (south) */ +#define B2062_S_RFPLL_CTL11 B43_LP_SOUTH(0x03F) /* RFPLL Control 11 (south) */ +#define B2062_S_RFPLL_CTL12 B43_LP_SOUTH(0x040) /* RFPLL Control 12 (south) */ +#define B2062_S_RFPLL_CTL13 B43_LP_SOUTH(0x041) /* RFPLL Control 13 (south) */ +#define B2062_S_RFPLL_CTL14 B43_LP_SOUTH(0x042) /* RFPLL Control 14 (south) */ +#define B2062_S_RFPLL_CTL15 B43_LP_SOUTH(0x043) /* RFPLL Control 15 (south) */ +#define B2062_S_RFPLL_CTL16 B43_LP_SOUTH(0x044) /* RFPLL Control 16 (south) */ +#define B2062_S_RFPLL_CTL17 B43_LP_SOUTH(0x045) /* RFPLL Control 17 (south) */ +#define B2062_S_RFPLL_CTL18 B43_LP_SOUTH(0x046) /* RFPLL Control 18 (south) */ +#define B2062_S_RFPLL_CTL19 B43_LP_SOUTH(0x047) /* RFPLL Control 19 (south) */ +#define B2062_S_RFPLL_CTL20 B43_LP_SOUTH(0x048) /* RFPLL Control 20 (south) */ +#define B2062_S_RFPLL_CTL21 B43_LP_SOUTH(0x049) /* RFPLL Control 21 (south) */ +#define B2062_S_RFPLL_CTL22 B43_LP_SOUTH(0x04A) /* RFPLL Control 22 (south) */ +#define B2062_S_RFPLL_CTL23 B43_LP_SOUTH(0x04B) /* RFPLL Control 23 (south) */ +#define B2062_S_RFPLL_CTL24 B43_LP_SOUTH(0x04C) /* RFPLL Control 24 (south) */ +#define B2062_S_RFPLL_CTL25 B43_LP_SOUTH(0x04D) /* RFPLL Control 25 (south) */ +#define B2062_S_RFPLL_CTL26 B43_LP_SOUTH(0x04E) /* RFPLL Control 26 (south) */ +#define B2062_S_RFPLL_CTL27 B43_LP_SOUTH(0x04F) /* RFPLL Control 27 (south) */ +#define B2062_S_RFPLL_CTL28 B43_LP_SOUTH(0x050) /* RFPLL Control 28 (south) */ +#define B2062_S_RFPLL_CTL29 B43_LP_SOUTH(0x051) /* RFPLL Control 29 (south) */ +#define B2062_S_RFPLL_CTL30 B43_LP_SOUTH(0x052) /* RFPLL Control 30 (south) */ +#define B2062_S_RFPLL_CTL31 B43_LP_SOUTH(0x053) /* RFPLL Control 31 (south) */ +#define B2062_S_RFPLL_CTL32 B43_LP_SOUTH(0x054) /* RFPLL Control 32 (south) */ +#define B2062_S_RFPLL_CTL33 B43_LP_SOUTH(0x055) /* RFPLL Control 33 (south) */ +#define B2062_S_RFPLL_CTL34 B43_LP_SOUTH(0x056) /* RFPLL Control 34 (south) */ +#define B2062_S_RXG_CNT0 B43_LP_SOUTH(0x057) /* RXG Counter 00 (south) */ +#define B2062_S_RXG_CNT1 B43_LP_SOUTH(0x058) /* RXG Counter 01 (south) */ +#define B2062_S_RXG_CNT2 B43_LP_SOUTH(0x059) /* RXG Counter 02 (south) */ +#define B2062_S_RXG_CNT3 B43_LP_SOUTH(0x05A) /* RXG Counter 03 (south) */ +#define B2062_S_RXG_CNT4 B43_LP_SOUTH(0x05B) /* RXG Counter 04 (south) */ +#define B2062_S_RXG_CNT5 B43_LP_SOUTH(0x05C) /* RXG Counter 05 (south) */ +#define B2062_S_RXG_CNT6 B43_LP_SOUTH(0x05D) /* RXG Counter 06 (south) */ +#define B2062_S_RXG_CNT7 B43_LP_SOUTH(0x05E) /* RXG Counter 07 (south) */ +#define B2062_S_RXG_CNT8 B43_LP_SOUTH(0x05F) /* RXG Counter 08 (south) */ +#define B2062_S_RXG_CNT9 B43_LP_SOUTH(0x060) /* RXG Counter 09 (south) */ +#define B2062_S_RXG_CNT10 B43_LP_SOUTH(0x061) /* RXG Counter 10 (south) */ +#define B2062_S_RXG_CNT11 B43_LP_SOUTH(0x062) /* RXG Counter 11 (south) */ +#define B2062_S_RXG_CNT12 B43_LP_SOUTH(0x063) /* RXG Counter 12 (south) */ +#define B2062_S_RXG_CNT13 B43_LP_SOUTH(0x064) /* RXG Counter 13 (south) */ +#define B2062_S_RXG_CNT14 B43_LP_SOUTH(0x065) /* RXG Counter 14 (south) */ +#define B2062_S_RXG_CNT15 B43_LP_SOUTH(0x066) /* RXG Counter 15 (south) */ +#define B2062_S_RXG_CNT16 B43_LP_SOUTH(0x067) /* RXG Counter 16 (south) */ +#define B2062_S_RXG_CNT17 B43_LP_SOUTH(0x068) /* RXG Counter 17 (south) */ + + + +/*** Broadcom 2063 radio registers ***/ +#define B2063_RADIO_ID_CODE B43_LP_RADIO(0x001) /* Radio ID code */ +#define B2063_COMM1 B43_LP_RADIO(0x000) /* Common 01 */ +#define B2063_COMM2 B43_LP_RADIO(0x002) /* Common 02 */ +#define B2063_COMM3 B43_LP_RADIO(0x003) /* Common 03 */ +#define B2063_COMM4 B43_LP_RADIO(0x004) /* Common 04 */ +#define B2063_COMM5 B43_LP_RADIO(0x005) /* Common 05 */ +#define B2063_COMM6 B43_LP_RADIO(0x006) /* Common 06 */ +#define B2063_COMM7 B43_LP_RADIO(0x007) /* Common 07 */ +#define B2063_COMM8 B43_LP_RADIO(0x008) /* Common 08 */ +#define B2063_COMM9 B43_LP_RADIO(0x009) /* Common 09 */ +#define B2063_COMM10 B43_LP_RADIO(0x00A) /* Common 10 */ +#define B2063_COMM11 B43_LP_RADIO(0x00B) /* Common 11 */ +#define B2063_COMM12 B43_LP_RADIO(0x00C) /* Common 12 */ +#define B2063_COMM13 B43_LP_RADIO(0x00D) /* Common 13 */ +#define B2063_COMM14 B43_LP_RADIO(0x00E) /* Common 14 */ +#define B2063_COMM15 B43_LP_RADIO(0x00F) /* Common 15 */ +#define B2063_COMM16 B43_LP_RADIO(0x010) /* Common 16 */ +#define B2063_COMM17 B43_LP_RADIO(0x011) /* Common 17 */ +#define B2063_COMM18 B43_LP_RADIO(0x012) /* Common 18 */ +#define B2063_COMM19 B43_LP_RADIO(0x013) /* Common 19 */ +#define B2063_COMM20 B43_LP_RADIO(0x014) /* Common 20 */ +#define B2063_COMM21 B43_LP_RADIO(0x015) /* Common 21 */ +#define B2063_COMM22 B43_LP_RADIO(0x016) /* Common 22 */ +#define B2063_COMM23 B43_LP_RADIO(0x017) /* Common 23 */ +#define B2063_COMM24 B43_LP_RADIO(0x018) /* Common 24 */ +#define B2063_PWR_SWITCH_CTL B43_LP_RADIO(0x019) /* POWER SWITCH Control */ +#define B2063_PLL_SP1 B43_LP_RADIO(0x01A) /* PLL SP 1 */ +#define B2063_PLL_SP2 B43_LP_RADIO(0x01B) /* PLL SP 2 */ +#define B2063_LOGEN_SP1 B43_LP_RADIO(0x01C) /* LOGEN SP 1 */ +#define B2063_LOGEN_SP2 B43_LP_RADIO(0x01D) /* LOGEN SP 2 */ +#define B2063_LOGEN_SP3 B43_LP_RADIO(0x01E) /* LOGEN SP 3 */ +#define B2063_LOGEN_SP4 B43_LP_RADIO(0x01F) /* LOGEN SP 4 */ +#define B2063_LOGEN_SP5 B43_LP_RADIO(0x020) /* LOGEN SP 5 */ +#define B2063_G_RX_SP1 B43_LP_RADIO(0x021) /* G RX SP 1 */ +#define B2063_G_RX_SP2 B43_LP_RADIO(0x022) /* G RX SP 2 */ +#define B2063_G_RX_SP3 B43_LP_RADIO(0x023) /* G RX SP 3 */ +#define B2063_G_RX_SP4 B43_LP_RADIO(0x024) /* G RX SP 4 */ +#define B2063_G_RX_SP5 B43_LP_RADIO(0x025) /* G RX SP 5 */ +#define B2063_G_RX_SP6 B43_LP_RADIO(0x026) /* G RX SP 6 */ +#define B2063_G_RX_SP7 B43_LP_RADIO(0x027) /* G RX SP 7 */ +#define B2063_G_RX_SP8 B43_LP_RADIO(0x028) /* G RX SP 8 */ +#define B2063_G_RX_SP9 B43_LP_RADIO(0x029) /* G RX SP 9 */ +#define B2063_G_RX_SP10 B43_LP_RADIO(0x02A) /* G RX SP 10 */ +#define B2063_G_RX_SP11 B43_LP_RADIO(0x02B) /* G RX SP 11 */ +#define B2063_A_RX_SP1 B43_LP_RADIO(0x02C) /* A RX SP 1 */ +#define B2063_A_RX_SP2 B43_LP_RADIO(0x02D) /* A RX SP 2 */ +#define B2063_A_RX_SP3 B43_LP_RADIO(0x02E) /* A RX SP 3 */ +#define B2063_A_RX_SP4 B43_LP_RADIO(0x02F) /* A RX SP 4 */ +#define B2063_A_RX_SP5 B43_LP_RADIO(0x030) /* A RX SP 5 */ +#define B2063_A_RX_SP6 B43_LP_RADIO(0x031) /* A RX SP 6 */ +#define B2063_A_RX_SP7 B43_LP_RADIO(0x032) /* A RX SP 7 */ +#define B2063_RX_BB_SP1 B43_LP_RADIO(0x033) /* RX BB SP 1 */ +#define B2063_RX_BB_SP2 B43_LP_RADIO(0x034) /* RX BB SP 2 */ +#define B2063_RX_BB_SP3 B43_LP_RADIO(0x035) /* RX BB SP 3 */ +#define B2063_RX_BB_SP4 B43_LP_RADIO(0x036) /* RX BB SP 4 */ +#define B2063_RX_BB_SP5 B43_LP_RADIO(0x037) /* RX BB SP 5 */ +#define B2063_RX_BB_SP6 B43_LP_RADIO(0x038) /* RX BB SP 6 */ +#define B2063_RX_BB_SP7 B43_LP_RADIO(0x039) /* RX BB SP 7 */ +#define B2063_RX_BB_SP8 B43_LP_RADIO(0x03A) /* RX BB SP 8 */ +#define B2063_TX_RF_SP1 B43_LP_RADIO(0x03B) /* TX RF SP 1 */ +#define B2063_TX_RF_SP2 B43_LP_RADIO(0x03C) /* TX RF SP 2 */ +#define B2063_TX_RF_SP3 B43_LP_RADIO(0x03D) /* TX RF SP 3 */ +#define B2063_TX_RF_SP4 B43_LP_RADIO(0x03E) /* TX RF SP 4 */ +#define B2063_TX_RF_SP5 B43_LP_RADIO(0x03F) /* TX RF SP 5 */ +#define B2063_TX_RF_SP6 B43_LP_RADIO(0x040) /* TX RF SP 6 */ +#define B2063_TX_RF_SP7 B43_LP_RADIO(0x041) /* TX RF SP 7 */ +#define B2063_TX_RF_SP8 B43_LP_RADIO(0x042) /* TX RF SP 8 */ +#define B2063_TX_RF_SP9 B43_LP_RADIO(0x043) /* TX RF SP 9 */ +#define B2063_TX_RF_SP10 B43_LP_RADIO(0x044) /* TX RF SP 10 */ +#define B2063_TX_RF_SP11 B43_LP_RADIO(0x045) /* TX RF SP 11 */ +#define B2063_TX_RF_SP12 B43_LP_RADIO(0x046) /* TX RF SP 12 */ +#define B2063_TX_RF_SP13 B43_LP_RADIO(0x047) /* TX RF SP 13 */ +#define B2063_TX_RF_SP14 B43_LP_RADIO(0x048) /* TX RF SP 14 */ +#define B2063_TX_RF_SP15 B43_LP_RADIO(0x049) /* TX RF SP 15 */ +#define B2063_TX_RF_SP16 B43_LP_RADIO(0x04A) /* TX RF SP 16 */ +#define B2063_TX_RF_SP17 B43_LP_RADIO(0x04B) /* TX RF SP 17 */ +#define B2063_PA_SP1 B43_LP_RADIO(0x04C) /* PA SP 1 */ +#define B2063_PA_SP2 B43_LP_RADIO(0x04D) /* PA SP 2 */ +#define B2063_PA_SP3 B43_LP_RADIO(0x04E) /* PA SP 3 */ +#define B2063_PA_SP4 B43_LP_RADIO(0x04F) /* PA SP 4 */ +#define B2063_PA_SP5 B43_LP_RADIO(0x050) /* PA SP 5 */ +#define B2063_PA_SP6 B43_LP_RADIO(0x051) /* PA SP 6 */ +#define B2063_PA_SP7 B43_LP_RADIO(0x052) /* PA SP 7 */ +#define B2063_TX_BB_SP1 B43_LP_RADIO(0x053) /* TX BB SP 1 */ +#define B2063_TX_BB_SP2 B43_LP_RADIO(0x054) /* TX BB SP 2 */ +#define B2063_TX_BB_SP3 B43_LP_RADIO(0x055) /* TX BB SP 3 */ +#define B2063_REG_SP1 B43_LP_RADIO(0x056) /* REG SP 1 */ +#define B2063_BANDGAP_CTL1 B43_LP_RADIO(0x057) /* BANDGAP Control 1 */ +#define B2063_BANDGAP_CTL2 B43_LP_RADIO(0x058) /* BANDGAP Control 2 */ +#define B2063_LPO_CTL1 B43_LP_RADIO(0x059) /* LPO Control 1 */ +#define B2063_RC_CALIB_CTL1 B43_LP_RADIO(0x05A) /* RC Calibration Control 1 */ +#define B2063_RC_CALIB_CTL2 B43_LP_RADIO(0x05B) /* RC Calibration Control 2 */ +#define B2063_RC_CALIB_CTL3 B43_LP_RADIO(0x05C) /* RC Calibration Control 3 */ +#define B2063_RC_CALIB_CTL4 B43_LP_RADIO(0x05D) /* RC Calibration Control 4 */ +#define B2063_RC_CALIB_CTL5 B43_LP_RADIO(0x05E) /* RC Calibration Control 5 */ +#define B2063_RC_CALIB_CTL6 B43_LP_RADIO(0x05F) /* RC Calibration Control 6 */ +#define B2063_RC_CALIB_CTL7 B43_LP_RADIO(0x060) /* RC Calibration Control 7 */ +#define B2063_RC_CALIB_CTL8 B43_LP_RADIO(0x061) /* RC Calibration Control 8 */ +#define B2063_RC_CALIB_CTL9 B43_LP_RADIO(0x062) /* RC Calibration Control 9 */ +#define B2063_RC_CALIB_CTL10 B43_LP_RADIO(0x063) /* RC Calibration Control 10 */ +#define B2063_PLL_JTAG_CALNRST B43_LP_RADIO(0x064) /* PLL JTAG CALNRST */ +#define B2063_PLL_JTAG_IN_PLL1 B43_LP_RADIO(0x065) /* PLL JTAG IN PLL 1 */ +#define B2063_PLL_JTAG_IN_PLL2 B43_LP_RADIO(0x066) /* PLL JTAG IN PLL 2 */ +#define B2063_PLL_JTAG_PLL_CP1 B43_LP_RADIO(0x067) /* PLL JTAG PLL CP 1 */ +#define B2063_PLL_JTAG_PLL_CP2 B43_LP_RADIO(0x068) /* PLL JTAG PLL CP 2 */ +#define B2063_PLL_JTAG_PLL_CP3 B43_LP_RADIO(0x069) /* PLL JTAG PLL CP 3 */ +#define B2063_PLL_JTAG_PLL_CP4 B43_LP_RADIO(0x06A) /* PLL JTAG PLL CP 4 */ +#define B2063_PLL_JTAG_PLL_CTL1 B43_LP_RADIO(0x06B) /* PLL JTAG PLL Control 1 */ +#define B2063_PLL_JTAG_PLL_LF1 B43_LP_RADIO(0x06C) /* PLL JTAG PLL LF 1 */ +#define B2063_PLL_JTAG_PLL_LF2 B43_LP_RADIO(0x06D) /* PLL JTAG PLL LF 2 */ +#define B2063_PLL_JTAG_PLL_LF3 B43_LP_RADIO(0x06E) /* PLL JTAG PLL LF 3 */ +#define B2063_PLL_JTAG_PLL_LF4 B43_LP_RADIO(0x06F) /* PLL JTAG PLL LF 4 */ +#define B2063_PLL_JTAG_PLL_SG1 B43_LP_RADIO(0x070) /* PLL JTAG PLL SG 1 */ +#define B2063_PLL_JTAG_PLL_SG2 B43_LP_RADIO(0x071) /* PLL JTAG PLL SG 2 */ +#define B2063_PLL_JTAG_PLL_SG3 B43_LP_RADIO(0x072) /* PLL JTAG PLL SG 3 */ +#define B2063_PLL_JTAG_PLL_SG4 B43_LP_RADIO(0x073) /* PLL JTAG PLL SG 4 */ +#define B2063_PLL_JTAG_PLL_SG5 B43_LP_RADIO(0x074) /* PLL JTAG PLL SG 5 */ +#define B2063_PLL_JTAG_PLL_VCO1 B43_LP_RADIO(0x075) /* PLL JTAG PLL VCO 1 */ +#define B2063_PLL_JTAG_PLL_VCO2 B43_LP_RADIO(0x076) /* PLL JTAG PLL VCO 2 */ +#define B2063_PLL_JTAG_PLL_VCO_CALIB1 B43_LP_RADIO(0x077) /* PLL JTAG PLL VCO Calibration 1 */ +#define B2063_PLL_JTAG_PLL_VCO_CALIB2 B43_LP_RADIO(0x078) /* PLL JTAG PLL VCO Calibration 2 */ +#define B2063_PLL_JTAG_PLL_VCO_CALIB3 B43_LP_RADIO(0x079) /* PLL JTAG PLL VCO Calibration 3 */ +#define B2063_PLL_JTAG_PLL_VCO_CALIB4 B43_LP_RADIO(0x07A) /* PLL JTAG PLL VCO Calibration 4 */ +#define B2063_PLL_JTAG_PLL_VCO_CALIB5 B43_LP_RADIO(0x07B) /* PLL JTAG PLL VCO Calibration 5 */ +#define B2063_PLL_JTAG_PLL_VCO_CALIB6 B43_LP_RADIO(0x07C) /* PLL JTAG PLL VCO Calibration 6 */ +#define B2063_PLL_JTAG_PLL_VCO_CALIB7 B43_LP_RADIO(0x07D) /* PLL JTAG PLL VCO Calibration 7 */ +#define B2063_PLL_JTAG_PLL_VCO_CALIB8 B43_LP_RADIO(0x07E) /* PLL JTAG PLL VCO Calibration 8 */ +#define B2063_PLL_JTAG_PLL_VCO_CALIB9 B43_LP_RADIO(0x07F) /* PLL JTAG PLL VCO Calibration 9 */ +#define B2063_PLL_JTAG_PLL_VCO_CALIB10 B43_LP_RADIO(0x080) /* PLL JTAG PLL VCO Calibration 10 */ +#define B2063_PLL_JTAG_PLL_XTAL_12 B43_LP_RADIO(0x081) /* PLL JTAG PLL XTAL 1 2 */ +#define B2063_PLL_JTAG_PLL_XTAL3 B43_LP_RADIO(0x082) /* PLL JTAG PLL XTAL 3 */ +#define B2063_LOGEN_ACL1 B43_LP_RADIO(0x083) /* LOGEN ACL 1 */ +#define B2063_LOGEN_ACL2 B43_LP_RADIO(0x084) /* LOGEN ACL 2 */ +#define B2063_LOGEN_ACL3 B43_LP_RADIO(0x085) /* LOGEN ACL 3 */ +#define B2063_LOGEN_ACL4 B43_LP_RADIO(0x086) /* LOGEN ACL 4 */ +#define B2063_LOGEN_ACL5 B43_LP_RADIO(0x087) /* LOGEN ACL 5 */ +#define B2063_LO_CALIB_INPUTS B43_LP_RADIO(0x088) /* LO Calibration INPUTS */ +#define B2063_LO_CALIB_CTL1 B43_LP_RADIO(0x089) /* LO Calibration Control 1 */ +#define B2063_LO_CALIB_CTL2 B43_LP_RADIO(0x08A) /* LO Calibration Control 2 */ +#define B2063_LO_CALIB_CTL3 B43_LP_RADIO(0x08B) /* LO Calibration Control 3 */ +#define B2063_LO_CALIB_WAITCNT B43_LP_RADIO(0x08C) /* LO Calibration WAITCNT */ +#define B2063_LO_CALIB_OVR1 B43_LP_RADIO(0x08D) /* LO Calibration OVR 1 */ +#define B2063_LO_CALIB_OVR2 B43_LP_RADIO(0x08E) /* LO Calibration OVR 2 */ +#define B2063_LO_CALIB_OVAL1 B43_LP_RADIO(0x08F) /* LO Calibration OVAL 1 */ +#define B2063_LO_CALIB_OVAL2 B43_LP_RADIO(0x090) /* LO Calibration OVAL 2 */ +#define B2063_LO_CALIB_OVAL3 B43_LP_RADIO(0x091) /* LO Calibration OVAL 3 */ +#define B2063_LO_CALIB_OVAL4 B43_LP_RADIO(0x092) /* LO Calibration OVAL 4 */ +#define B2063_LO_CALIB_OVAL5 B43_LP_RADIO(0x093) /* LO Calibration OVAL 5 */ +#define B2063_LO_CALIB_OVAL6 B43_LP_RADIO(0x094) /* LO Calibration OVAL 6 */ +#define B2063_LO_CALIB_OVAL7 B43_LP_RADIO(0x095) /* LO Calibration OVAL 7 */ +#define B2063_LO_CALIB_CALVLD1 B43_LP_RADIO(0x096) /* LO Calibration CALVLD 1 */ +#define B2063_LO_CALIB_CALVLD2 B43_LP_RADIO(0x097) /* LO Calibration CALVLD 2 */ +#define B2063_LO_CALIB_CVAL1 B43_LP_RADIO(0x098) /* LO Calibration CVAL 1 */ +#define B2063_LO_CALIB_CVAL2 B43_LP_RADIO(0x099) /* LO Calibration CVAL 2 */ +#define B2063_LO_CALIB_CVAL3 B43_LP_RADIO(0x09A) /* LO Calibration CVAL 3 */ +#define B2063_LO_CALIB_CVAL4 B43_LP_RADIO(0x09B) /* LO Calibration CVAL 4 */ +#define B2063_LO_CALIB_CVAL5 B43_LP_RADIO(0x09C) /* LO Calibration CVAL 5 */ +#define B2063_LO_CALIB_CVAL6 B43_LP_RADIO(0x09D) /* LO Calibration CVAL 6 */ +#define B2063_LO_CALIB_CVAL7 B43_LP_RADIO(0x09E) /* LO Calibration CVAL 7 */ +#define B2063_LOGEN_CALIB_EN B43_LP_RADIO(0x09F) /* LOGEN Calibration EN */ +#define B2063_LOGEN_PEAKDET1 B43_LP_RADIO(0x0A0) /* LOGEN PEAKDET 1 */ +#define B2063_LOGEN_RCCR1 B43_LP_RADIO(0x0A1) /* LOGEN RCCR 1 */ +#define B2063_LOGEN_VCOBUF1 B43_LP_RADIO(0x0A2) /* LOGEN VCOBUF 1 */ +#define B2063_LOGEN_MIXER1 B43_LP_RADIO(0x0A3) /* LOGEN MIXER 1 */ +#define B2063_LOGEN_MIXER2 B43_LP_RADIO(0x0A4) /* LOGEN MIXER 2 */ +#define B2063_LOGEN_BUF1 B43_LP_RADIO(0x0A5) /* LOGEN BUF 1 */ +#define B2063_LOGEN_BUF2 B43_LP_RADIO(0x0A6) /* LOGEN BUF 2 */ +#define B2063_LOGEN_DIV1 B43_LP_RADIO(0x0A7) /* LOGEN DIV 1 */ +#define B2063_LOGEN_DIV2 B43_LP_RADIO(0x0A8) /* LOGEN DIV 2 */ +#define B2063_LOGEN_DIV3 B43_LP_RADIO(0x0A9) /* LOGEN DIV 3 */ +#define B2063_LOGEN_CBUFRX1 B43_LP_RADIO(0x0AA) /* LOGEN CBUFRX 1 */ +#define B2063_LOGEN_CBUFRX2 B43_LP_RADIO(0x0AB) /* LOGEN CBUFRX 2 */ +#define B2063_LOGEN_CBUFTX1 B43_LP_RADIO(0x0AC) /* LOGEN CBUFTX 1 */ +#define B2063_LOGEN_CBUFTX2 B43_LP_RADIO(0x0AD) /* LOGEN CBUFTX 2 */ +#define B2063_LOGEN_IDAC1 B43_LP_RADIO(0x0AE) /* LOGEN IDAC 1 */ +#define B2063_LOGEN_SPARE1 B43_LP_RADIO(0x0AF) /* LOGEN SPARE 1 */ +#define B2063_LOGEN_SPARE2 B43_LP_RADIO(0x0B0) /* LOGEN SPARE 2 */ +#define B2063_LOGEN_SPARE3 B43_LP_RADIO(0x0B1) /* LOGEN SPARE 3 */ +#define B2063_G_RX_1ST1 B43_LP_RADIO(0x0B2) /* G RX 1ST 1 */ +#define B2063_G_RX_1ST2 B43_LP_RADIO(0x0B3) /* G RX 1ST 2 */ +#define B2063_G_RX_1ST3 B43_LP_RADIO(0x0B4) /* G RX 1ST 3 */ +#define B2063_G_RX_2ND1 B43_LP_RADIO(0x0B5) /* G RX 2ND 1 */ +#define B2063_G_RX_2ND2 B43_LP_RADIO(0x0B6) /* G RX 2ND 2 */ +#define B2063_G_RX_2ND3 B43_LP_RADIO(0x0B7) /* G RX 2ND 3 */ +#define B2063_G_RX_2ND4 B43_LP_RADIO(0x0B8) /* G RX 2ND 4 */ +#define B2063_G_RX_2ND5 B43_LP_RADIO(0x0B9) /* G RX 2ND 5 */ +#define B2063_G_RX_2ND6 B43_LP_RADIO(0x0BA) /* G RX 2ND 6 */ +#define B2063_G_RX_2ND7 B43_LP_RADIO(0x0BB) /* G RX 2ND 7 */ +#define B2063_G_RX_2ND8 B43_LP_RADIO(0x0BC) /* G RX 2ND 8 */ +#define B2063_G_RX_PS1 B43_LP_RADIO(0x0BD) /* G RX PS 1 */ +#define B2063_G_RX_PS2 B43_LP_RADIO(0x0BE) /* G RX PS 2 */ +#define B2063_G_RX_PS3 B43_LP_RADIO(0x0BF) /* G RX PS 3 */ +#define B2063_G_RX_PS4 B43_LP_RADIO(0x0C0) /* G RX PS 4 */ +#define B2063_G_RX_PS5 B43_LP_RADIO(0x0C1) /* G RX PS 5 */ +#define B2063_G_RX_MIX1 B43_LP_RADIO(0x0C2) /* G RX MIX 1 */ +#define B2063_G_RX_MIX2 B43_LP_RADIO(0x0C3) /* G RX MIX 2 */ +#define B2063_G_RX_MIX3 B43_LP_RADIO(0x0C4) /* G RX MIX 3 */ +#define B2063_G_RX_MIX4 B43_LP_RADIO(0x0C5) /* G RX MIX 4 */ +#define B2063_G_RX_MIX5 B43_LP_RADIO(0x0C6) /* G RX MIX 5 */ +#define B2063_G_RX_MIX6 B43_LP_RADIO(0x0C7) /* G RX MIX 6 */ +#define B2063_G_RX_MIX7 B43_LP_RADIO(0x0C8) /* G RX MIX 7 */ +#define B2063_G_RX_MIX8 B43_LP_RADIO(0x0C9) /* G RX MIX 8 */ +#define B2063_G_RX_PDET1 B43_LP_RADIO(0x0CA) /* G RX PDET 1 */ +#define B2063_G_RX_SPARES1 B43_LP_RADIO(0x0CB) /* G RX SPARES 1 */ +#define B2063_G_RX_SPARES2 B43_LP_RADIO(0x0CC) /* G RX SPARES 2 */ +#define B2063_G_RX_SPARES3 B43_LP_RADIO(0x0CD) /* G RX SPARES 3 */ +#define B2063_A_RX_1ST1 B43_LP_RADIO(0x0CE) /* A RX 1ST 1 */ +#define B2063_A_RX_1ST2 B43_LP_RADIO(0x0CF) /* A RX 1ST 2 */ +#define B2063_A_RX_1ST3 B43_LP_RADIO(0x0D0) /* A RX 1ST 3 */ +#define B2063_A_RX_1ST4 B43_LP_RADIO(0x0D1) /* A RX 1ST 4 */ +#define B2063_A_RX_1ST5 B43_LP_RADIO(0x0D2) /* A RX 1ST 5 */ +#define B2063_A_RX_2ND1 B43_LP_RADIO(0x0D3) /* A RX 2ND 1 */ +#define B2063_A_RX_2ND2 B43_LP_RADIO(0x0D4) /* A RX 2ND 2 */ +#define B2063_A_RX_2ND3 B43_LP_RADIO(0x0D5) /* A RX 2ND 3 */ +#define B2063_A_RX_2ND4 B43_LP_RADIO(0x0D6) /* A RX 2ND 4 */ +#define B2063_A_RX_2ND5 B43_LP_RADIO(0x0D7) /* A RX 2ND 5 */ +#define B2063_A_RX_2ND6 B43_LP_RADIO(0x0D8) /* A RX 2ND 6 */ +#define B2063_A_RX_2ND7 B43_LP_RADIO(0x0D9) /* A RX 2ND 7 */ +#define B2063_A_RX_PS1 B43_LP_RADIO(0x0DA) /* A RX PS 1 */ +#define B2063_A_RX_PS2 B43_LP_RADIO(0x0DB) /* A RX PS 2 */ +#define B2063_A_RX_PS3 B43_LP_RADIO(0x0DC) /* A RX PS 3 */ +#define B2063_A_RX_PS4 B43_LP_RADIO(0x0DD) /* A RX PS 4 */ +#define B2063_A_RX_PS5 B43_LP_RADIO(0x0DE) /* A RX PS 5 */ +#define B2063_A_RX_PS6 B43_LP_RADIO(0x0DF) /* A RX PS 6 */ +#define B2063_A_RX_MIX1 B43_LP_RADIO(0x0E0) /* A RX MIX 1 */ +#define B2063_A_RX_MIX2 B43_LP_RADIO(0x0E1) /* A RX MIX 2 */ +#define B2063_A_RX_MIX3 B43_LP_RADIO(0x0E2) /* A RX MIX 3 */ +#define B2063_A_RX_MIX4 B43_LP_RADIO(0x0E3) /* A RX MIX 4 */ +#define B2063_A_RX_MIX5 B43_LP_RADIO(0x0E4) /* A RX MIX 5 */ +#define B2063_A_RX_MIX6 B43_LP_RADIO(0x0E5) /* A RX MIX 6 */ +#define B2063_A_RX_MIX7 B43_LP_RADIO(0x0E6) /* A RX MIX 7 */ +#define B2063_A_RX_MIX8 B43_LP_RADIO(0x0E7) /* A RX MIX 8 */ +#define B2063_A_RX_PWRDET1 B43_LP_RADIO(0x0E8) /* A RX PWRDET 1 */ +#define B2063_A_RX_SPARE1 B43_LP_RADIO(0x0E9) /* A RX SPARE 1 */ +#define B2063_A_RX_SPARE2 B43_LP_RADIO(0x0EA) /* A RX SPARE 2 */ +#define B2063_A_RX_SPARE3 B43_LP_RADIO(0x0EB) /* A RX SPARE 3 */ +#define B2063_RX_TIA_CTL1 B43_LP_RADIO(0x0EC) /* RX TIA Control 1 */ +#define B2063_RX_TIA_CTL2 B43_LP_RADIO(0x0ED) /* RX TIA Control 2 */ +#define B2063_RX_TIA_CTL3 B43_LP_RADIO(0x0EE) /* RX TIA Control 3 */ +#define B2063_RX_TIA_CTL4 B43_LP_RADIO(0x0EF) /* RX TIA Control 4 */ +#define B2063_RX_TIA_CTL5 B43_LP_RADIO(0x0F0) /* RX TIA Control 5 */ +#define B2063_RX_TIA_CTL6 B43_LP_RADIO(0x0F1) /* RX TIA Control 6 */ +#define B2063_RX_BB_CTL1 B43_LP_RADIO(0x0F2) /* RX BB Control 1 */ +#define B2063_RX_BB_CTL2 B43_LP_RADIO(0x0F3) /* RX BB Control 2 */ +#define B2063_RX_BB_CTL3 B43_LP_RADIO(0x0F4) /* RX BB Control 3 */ +#define B2063_RX_BB_CTL4 B43_LP_RADIO(0x0F5) /* RX BB Control 4 */ +#define B2063_RX_BB_CTL5 B43_LP_RADIO(0x0F6) /* RX BB Control 5 */ +#define B2063_RX_BB_CTL6 B43_LP_RADIO(0x0F7) /* RX BB Control 6 */ +#define B2063_RX_BB_CTL7 B43_LP_RADIO(0x0F8) /* RX BB Control 7 */ +#define B2063_RX_BB_CTL8 B43_LP_RADIO(0x0F9) /* RX BB Control 8 */ +#define B2063_RX_BB_CTL9 B43_LP_RADIO(0x0FA) /* RX BB Control 9 */ +#define B2063_TX_RF_CTL1 B43_LP_RADIO(0x0FB) /* TX RF Control 1 */ +#define B2063_TX_RF_IDAC_LO_RF_I B43_LP_RADIO(0x0FC) /* TX RF IDAC LO RF I */ +#define B2063_TX_RF_IDAC_LO_RF_Q B43_LP_RADIO(0x0FD) /* TX RF IDAC LO RF Q */ +#define B2063_TX_RF_IDAC_LO_BB_I B43_LP_RADIO(0x0FE) /* TX RF IDAC LO BB I */ +#define B2063_TX_RF_IDAC_LO_BB_Q B43_LP_RADIO(0x0FF) /* TX RF IDAC LO BB Q */ +#define B2063_TX_RF_CTL2 B43_LP_RADIO(0x100) /* TX RF Control 2 */ +#define B2063_TX_RF_CTL3 B43_LP_RADIO(0x101) /* TX RF Control 3 */ +#define B2063_TX_RF_CTL4 B43_LP_RADIO(0x102) /* TX RF Control 4 */ +#define B2063_TX_RF_CTL5 B43_LP_RADIO(0x103) /* TX RF Control 5 */ +#define B2063_TX_RF_CTL6 B43_LP_RADIO(0x104) /* TX RF Control 6 */ +#define B2063_TX_RF_CTL7 B43_LP_RADIO(0x105) /* TX RF Control 7 */ +#define B2063_TX_RF_CTL8 B43_LP_RADIO(0x106) /* TX RF Control 8 */ +#define B2063_TX_RF_CTL9 B43_LP_RADIO(0x107) /* TX RF Control 9 */ +#define B2063_TX_RF_CTL10 B43_LP_RADIO(0x108) /* TX RF Control 10 */ +#define B2063_TX_RF_CTL14 B43_LP_RADIO(0x109) /* TX RF Control 14 */ +#define B2063_TX_RF_CTL15 B43_LP_RADIO(0x10A) /* TX RF Control 15 */ +#define B2063_PA_CTL1 B43_LP_RADIO(0x10B) /* PA Control 1 */ +#define B2063_PA_CTL2 B43_LP_RADIO(0x10C) /* PA Control 2 */ +#define B2063_PA_CTL3 B43_LP_RADIO(0x10D) /* PA Control 3 */ +#define B2063_PA_CTL4 B43_LP_RADIO(0x10E) /* PA Control 4 */ +#define B2063_PA_CTL5 B43_LP_RADIO(0x10F) /* PA Control 5 */ +#define B2063_PA_CTL6 B43_LP_RADIO(0x110) /* PA Control 6 */ +#define B2063_PA_CTL7 B43_LP_RADIO(0x111) /* PA Control 7 */ +#define B2063_PA_CTL8 B43_LP_RADIO(0x112) /* PA Control 8 */ +#define B2063_PA_CTL9 B43_LP_RADIO(0x113) /* PA Control 9 */ +#define B2063_PA_CTL10 B43_LP_RADIO(0x114) /* PA Control 10 */ +#define B2063_PA_CTL11 B43_LP_RADIO(0x115) /* PA Control 11 */ +#define B2063_PA_CTL12 B43_LP_RADIO(0x116) /* PA Control 12 */ +#define B2063_PA_CTL13 B43_LP_RADIO(0x117) /* PA Control 13 */ +#define B2063_TX_BB_CTL1 B43_LP_RADIO(0x118) /* TX BB Control 1 */ +#define B2063_TX_BB_CTL2 B43_LP_RADIO(0x119) /* TX BB Control 2 */ +#define B2063_TX_BB_CTL3 B43_LP_RADIO(0x11A) /* TX BB Control 3 */ +#define B2063_TX_BB_CTL4 B43_LP_RADIO(0x11B) /* TX BB Control 4 */ +#define B2063_GPIO_CTL1 B43_LP_RADIO(0x11C) /* GPIO Control 1 */ +#define B2063_VREG_CTL1 B43_LP_RADIO(0x11D) /* VREG Control 1 */ +#define B2063_AMUX_CTL1 B43_LP_RADIO(0x11E) /* AMUX Control 1 */ +#define B2063_IQ_CALIB_GVAR B43_LP_RADIO(0x11F) /* IQ Calibration GVAR */ +#define B2063_IQ_CALIB_CTL1 B43_LP_RADIO(0x120) /* IQ Calibration Control 1 */ +#define B2063_IQ_CALIB_CTL2 B43_LP_RADIO(0x121) /* IQ Calibration Control 2 */ +#define B2063_TEMPSENSE_CTL1 B43_LP_RADIO(0x122) /* TEMPSENSE Control 1 */ +#define B2063_TEMPSENSE_CTL2 B43_LP_RADIO(0x123) /* TEMPSENSE Control 2 */ +#define B2063_TX_RX_LOOPBACK1 B43_LP_RADIO(0x124) /* TX/RX LOOPBACK 1 */ +#define B2063_TX_RX_LOOPBACK2 B43_LP_RADIO(0x125) /* TX/RX LOOPBACK 2 */ +#define B2063_EXT_TSSI_CTL1 B43_LP_RADIO(0x126) /* EXT TSSI Control 1 */ +#define B2063_EXT_TSSI_CTL2 B43_LP_RADIO(0x127) /* EXT TSSI Control 2 */ +#define B2063_AFE_CTL B43_LP_RADIO(0x128) /* AFE Control */ + + + +enum b43_lpphy_txpctl_mode { + B43_LPPHY_TXPCTL_UNKNOWN = 0, + B43_LPPHY_TXPCTL_OFF, /* TX power control is OFF */ + B43_LPPHY_TXPCTL_SW, /* TX power control is set to Software */ + B43_LPPHY_TXPCTL_HW, /* TX power control is set to Hardware */ +}; + +struct b43_phy_lp { + /* Current TX power control mode. */ + enum b43_lpphy_txpctl_mode txpctl_mode; + + /* Transmit isolation medium band */ + u8 tx_isolation_med_band; + /* Transmit isolation low band */ + u8 tx_isolation_low_band; + /* Transmit isolation high band */ + u8 tx_isolation_hi_band; + + /* Max transmit power medium band */ + u16 max_tx_pwr_med_band; + /* Max transmit power low band */ + u16 max_tx_pwr_low_band; + /* Max transmit power high band */ + u16 max_tx_pwr_hi_band; + + /* FIXME What are these used for? */ + /* FIXME Is 15 the correct array size? */ + u16 tx_max_rate[15]; + u16 tx_max_ratel[15]; + u16 tx_max_rateh[15]; + + /* Transmit power arrays */ + s16 txpa[3], txpal[3], txpah[3]; + + /* Receive power offset */ + u8 rx_pwr_offset; + + /* TSSI transmit count */ + u16 tssi_tx_count; + /* TSSI index */ + u16 tssi_idx; /* FIXME initial value? */ + /* TSSI npt */ + u16 tssi_npt; /* FIXME initial value? */ + + /* Target TX frequency */ + u16 tgt_tx_freq; /* FIXME initial value? */ + + /* Transmit power index override */ + s8 tx_pwr_idx_over; /* FIXME initial value? */ + + /* RSSI vf */ + u8 rssi_vf; + /* RSSI vc */ + u8 rssi_vc; + /* RSSI gs */ + u8 rssi_gs; + + /* RC cap */ + u8 rc_cap; + /* BX arch */ + u8 bx_arch; + + /* Full calibration channel */ + u8 full_calib_chan; + + /* Transmit iqlocal best coeffs */ + bool tx_iqloc_best_coeffs_valid; + u8 tx_iqloc_best_coeffs[11]; + + /* Used for "Save/Restore Dig Filt State" */ + u16 dig_flt_state[9]; + + bool crs_usr_disable, crs_sys_disable; + + unsigned int pdiv; + + /* The channel we are tuned to */ + u8 channel; + + /* The active antenna diversity mode */ + int antenna; + + /* Frequency of the active TX tone */ + int tx_tone_freq; +}; + +enum tssi_mux_mode { + TSSI_MUX_PREPA, + TSSI_MUX_POSTPA, + TSSI_MUX_EXT, +}; + +struct b43_phy_operations; +extern const struct b43_phy_operations b43_phyops_lp; + +#endif /* LINUX_B43_PHY_LP_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/phy_n.c b/drivers/net/wireless/broadcom/b43/phy_n.c new file mode 100644 index 000000000000..9f0bcf3b8414 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_n.c @@ -0,0 +1,6726 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11n PHY support + + Copyright (c) 2008 Michael Buesch + Copyright (c) 2010-2011 RafaÅ‚ MiÅ‚ecki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include +#include +#include + +#include "b43.h" +#include "phy_n.h" +#include "tables_nphy.h" +#include "radio_2055.h" +#include "radio_2056.h" +#include "radio_2057.h" +#include "main.h" +#include "ppr.h" + +struct nphy_txgains { + u16 tx_lpf[2]; + u16 txgm[2]; + u16 pga[2]; + u16 pad[2]; + u16 ipa[2]; +}; + +struct nphy_iqcal_params { + u16 tx_lpf; + u16 txgm; + u16 pga; + u16 pad; + u16 ipa; + u16 cal_gain; + u16 ncorr[5]; +}; + +struct nphy_iq_est { + s32 iq0_prod; + u32 i0_pwr; + u32 q0_pwr; + s32 iq1_prod; + u32 i1_pwr; + u32 q1_pwr; +}; + +enum b43_nphy_rf_sequence { + B43_RFSEQ_RX2TX, + B43_RFSEQ_TX2RX, + B43_RFSEQ_RESET2RX, + B43_RFSEQ_UPDATE_GAINH, + B43_RFSEQ_UPDATE_GAINL, + B43_RFSEQ_UPDATE_GAINU, +}; + +enum n_rf_ctl_over_cmd { + N_RF_CTL_OVER_CMD_RXRF_PU = 0, + N_RF_CTL_OVER_CMD_RX_PU = 1, + N_RF_CTL_OVER_CMD_TX_PU = 2, + N_RF_CTL_OVER_CMD_RX_GAIN = 3, + N_RF_CTL_OVER_CMD_TX_GAIN = 4, +}; + +enum n_intc_override { + N_INTC_OVERRIDE_OFF = 0, + N_INTC_OVERRIDE_TRSW = 1, + N_INTC_OVERRIDE_PA = 2, + N_INTC_OVERRIDE_EXT_LNA_PU = 3, + N_INTC_OVERRIDE_EXT_LNA_GAIN = 4, +}; + +enum n_rssi_type { + N_RSSI_W1 = 0, + N_RSSI_W2, + N_RSSI_NB, + N_RSSI_IQ, + N_RSSI_TSSI_2G, + N_RSSI_TSSI_5G, + N_RSSI_TBD, +}; + +enum n_rail_type { + N_RAIL_I = 0, + N_RAIL_Q = 1, +}; + +static inline bool b43_nphy_ipa(struct b43_wldev *dev) +{ + enum ieee80211_band band = b43_current_band(dev->wl); + return ((dev->phy.n->ipa2g_on && band == IEEE80211_BAND_2GHZ) || + (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCoreGetState */ +static u8 b43_nphy_get_rx_core_state(struct b43_wldev *dev) +{ + return (b43_phy_read(dev, B43_NPHY_RFSEQCA) & B43_NPHY_RFSEQCA_RXEN) >> + B43_NPHY_RFSEQCA_RXEN_SHIFT; +} + +/************************************************** + * RF (just without b43_nphy_rf_ctl_intc_override) + **************************************************/ + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ForceRFSeq */ +static void b43_nphy_force_rf_sequence(struct b43_wldev *dev, + enum b43_nphy_rf_sequence seq) +{ + static const u16 trigger[] = { + [B43_RFSEQ_RX2TX] = B43_NPHY_RFSEQTR_RX2TX, + [B43_RFSEQ_TX2RX] = B43_NPHY_RFSEQTR_TX2RX, + [B43_RFSEQ_RESET2RX] = B43_NPHY_RFSEQTR_RST2RX, + [B43_RFSEQ_UPDATE_GAINH] = B43_NPHY_RFSEQTR_UPGH, + [B43_RFSEQ_UPDATE_GAINL] = B43_NPHY_RFSEQTR_UPGL, + [B43_RFSEQ_UPDATE_GAINU] = B43_NPHY_RFSEQTR_UPGU, + }; + int i; + u16 seq_mode = b43_phy_read(dev, B43_NPHY_RFSEQMODE); + + B43_WARN_ON(seq >= ARRAY_SIZE(trigger)); + + b43_phy_set(dev, B43_NPHY_RFSEQMODE, + B43_NPHY_RFSEQMODE_CAOVER | B43_NPHY_RFSEQMODE_TROVER); + b43_phy_set(dev, B43_NPHY_RFSEQTR, trigger[seq]); + for (i = 0; i < 200; i++) { + if (!(b43_phy_read(dev, B43_NPHY_RFSEQST) & trigger[seq])) + goto ok; + msleep(1); + } + b43err(dev->wl, "RF sequence status timeout\n"); +ok: + b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode); +} + +static void b43_nphy_rf_ctl_override_rev19(struct b43_wldev *dev, u16 field, + u16 value, u8 core, bool off, + u8 override_id) +{ + /* TODO */ +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverrideRev7 */ +static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field, + u16 value, u8 core, bool off, + u8 override) +{ + struct b43_phy *phy = &dev->phy; + const struct nphy_rf_control_override_rev7 *e; + u16 en_addrs[3][2] = { + { 0x0E7, 0x0EC }, { 0x342, 0x343 }, { 0x346, 0x347 } + }; + u16 en_addr; + u16 en_mask = field; + u16 val_addr; + u8 i; + + if (phy->rev >= 19 || phy->rev < 3) { + B43_WARN_ON(1); + return; + } + + /* Remember: we can get NULL! */ + e = b43_nphy_get_rf_ctl_over_rev7(dev, field, override); + + for (i = 0; i < 2; i++) { + if (override >= ARRAY_SIZE(en_addrs)) { + b43err(dev->wl, "Invalid override value %d\n", override); + return; + } + en_addr = en_addrs[override][i]; + + if (e) + val_addr = (i == 0) ? e->val_addr_core0 : e->val_addr_core1; + + if (off) { + b43_phy_mask(dev, en_addr, ~en_mask); + if (e) /* Do it safer, better than wl */ + b43_phy_mask(dev, val_addr, ~e->val_mask); + } else { + if (!core || (core & (1 << i))) { + b43_phy_set(dev, en_addr, en_mask); + if (e) + b43_phy_maskset(dev, val_addr, ~e->val_mask, (value << e->val_shift)); + } + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverideOneToMany */ +static void b43_nphy_rf_ctl_override_one_to_many(struct b43_wldev *dev, + enum n_rf_ctl_over_cmd cmd, + u16 value, u8 core, bool off) +{ + struct b43_phy *phy = &dev->phy; + u16 tmp; + + B43_WARN_ON(phy->rev < 7); + + switch (cmd) { + case N_RF_CTL_OVER_CMD_RXRF_PU: + b43_nphy_rf_ctl_override_rev7(dev, 0x20, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x10, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x08, value, core, off, 1); + break; + case N_RF_CTL_OVER_CMD_RX_PU: + b43_nphy_rf_ctl_override_rev7(dev, 0x4, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 2); + b43_nphy_rf_ctl_override_rev7(dev, 0x0800, 0, core, off, 1); + break; + case N_RF_CTL_OVER_CMD_TX_PU: + b43_nphy_rf_ctl_override_rev7(dev, 0x4, value, core, off, 0); + b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 2); + b43_nphy_rf_ctl_override_rev7(dev, 0x0800, 1, core, off, 1); + break; + case N_RF_CTL_OVER_CMD_RX_GAIN: + tmp = value & 0xFF; + b43_nphy_rf_ctl_override_rev7(dev, 0x0800, tmp, core, off, 0); + tmp = value >> 8; + b43_nphy_rf_ctl_override_rev7(dev, 0x6000, tmp, core, off, 0); + break; + case N_RF_CTL_OVER_CMD_TX_GAIN: + tmp = value & 0x7FFF; + b43_nphy_rf_ctl_override_rev7(dev, 0x1000, tmp, core, off, 0); + tmp = value >> 14; + b43_nphy_rf_ctl_override_rev7(dev, 0x4000, tmp, core, off, 0); + break; + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverride */ +static void b43_nphy_rf_ctl_override(struct b43_wldev *dev, u16 field, + u16 value, u8 core, bool off) +{ + int i; + u8 index = fls(field); + u8 addr, en_addr, val_addr; + /* we expect only one bit set */ + B43_WARN_ON(field & (~(1 << (index - 1)))); + + if (dev->phy.rev >= 3) { + const struct nphy_rf_control_override_rev3 *rf_ctrl; + for (i = 0; i < 2; i++) { + if (index == 0 || index == 16) { + b43err(dev->wl, + "Unsupported RF Ctrl Override call\n"); + return; + } + + rf_ctrl = &tbl_rf_control_override_rev3[index - 1]; + en_addr = B43_PHY_N((i == 0) ? + rf_ctrl->en_addr0 : rf_ctrl->en_addr1); + val_addr = B43_PHY_N((i == 0) ? + rf_ctrl->val_addr0 : rf_ctrl->val_addr1); + + if (off) { + b43_phy_mask(dev, en_addr, ~(field)); + b43_phy_mask(dev, val_addr, + ~(rf_ctrl->val_mask)); + } else { + if (core == 0 || ((1 << i) & core)) { + b43_phy_set(dev, en_addr, field); + b43_phy_maskset(dev, val_addr, + ~(rf_ctrl->val_mask), + (value << rf_ctrl->val_shift)); + } + } + } + } else { + const struct nphy_rf_control_override_rev2 *rf_ctrl; + if (off) { + b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~(field)); + value = 0; + } else { + b43_phy_set(dev, B43_NPHY_RFCTL_OVER, field); + } + + for (i = 0; i < 2; i++) { + if (index <= 1 || index == 16) { + b43err(dev->wl, + "Unsupported RF Ctrl Override call\n"); + return; + } + + if (index == 2 || index == 10 || + (index >= 13 && index <= 15)) { + core = 1; + } + + rf_ctrl = &tbl_rf_control_override_rev2[index - 2]; + addr = B43_PHY_N((i == 0) ? + rf_ctrl->addr0 : rf_ctrl->addr1); + + if ((1 << i) & core) + b43_phy_maskset(dev, addr, ~(rf_ctrl->bmask), + (value << rf_ctrl->shift)); + + b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_CMD_START); + udelay(1); + b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, 0xFFFE); + } + } +} + +static void b43_nphy_rf_ctl_intc_override_rev7(struct b43_wldev *dev, + enum n_intc_override intc_override, + u16 value, u8 core_sel) +{ + u16 reg, tmp, tmp2, val; + int core; + + /* TODO: What about rev19+? Revs 3+ and 7+ are a bit similar */ + + for (core = 0; core < 2; core++) { + if ((core_sel == 1 && core != 0) || + (core_sel == 2 && core != 1)) + continue; + + reg = (core == 0) ? B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2; + + switch (intc_override) { + case N_INTC_OVERRIDE_OFF: + b43_phy_write(dev, reg, 0); + b43_phy_mask(dev, 0x2ff, ~0x2000); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + break; + case N_INTC_OVERRIDE_TRSW: + b43_phy_maskset(dev, reg, ~0xC0, value << 6); + b43_phy_set(dev, reg, 0x400); + + b43_phy_mask(dev, 0x2ff, ~0xC000 & 0xFFFF); + b43_phy_set(dev, 0x2ff, 0x2000); + b43_phy_set(dev, 0x2ff, 0x0001); + break; + case N_INTC_OVERRIDE_PA: + tmp = 0x0030; + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + val = value << 5; + else + val = value << 4; + b43_phy_maskset(dev, reg, ~tmp, val); + b43_phy_set(dev, reg, 0x1000); + break; + case N_INTC_OVERRIDE_EXT_LNA_PU: + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + tmp = 0x0001; + tmp2 = 0x0004; + val = value; + } else { + tmp = 0x0004; + tmp2 = 0x0001; + val = value << 2; + } + b43_phy_maskset(dev, reg, ~tmp, val); + b43_phy_mask(dev, reg, ~tmp2); + break; + case N_INTC_OVERRIDE_EXT_LNA_GAIN: + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + tmp = 0x0002; + tmp2 = 0x0008; + val = value << 1; + } else { + tmp = 0x0008; + tmp2 = 0x0002; + val = value << 3; + } + b43_phy_maskset(dev, reg, ~tmp, val); + b43_phy_mask(dev, reg, ~tmp2); + break; + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlIntcOverride */ +static void b43_nphy_rf_ctl_intc_override(struct b43_wldev *dev, + enum n_intc_override intc_override, + u16 value, u8 core) +{ + u8 i, j; + u16 reg, tmp, val; + + if (dev->phy.rev >= 7) { + b43_nphy_rf_ctl_intc_override_rev7(dev, intc_override, value, + core); + return; + } + + B43_WARN_ON(dev->phy.rev < 3); + + for (i = 0; i < 2; i++) { + if ((core == 1 && i == 1) || (core == 2 && !i)) + continue; + + reg = (i == 0) ? + B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2; + b43_phy_set(dev, reg, 0x400); + + switch (intc_override) { + case N_INTC_OVERRIDE_OFF: + b43_phy_write(dev, reg, 0); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + break; + case N_INTC_OVERRIDE_TRSW: + if (!i) { + b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC1, + 0xFC3F, (value << 6)); + b43_phy_maskset(dev, B43_NPHY_TXF_40CO_B1S1, + 0xFFFE, 1); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_CMD_START); + for (j = 0; j < 100; j++) { + if (!(b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_START)) { + j = 0; + break; + } + udelay(10); + } + if (j) + b43err(dev->wl, + "intc override timeout\n"); + b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1, + 0xFFFE); + } else { + b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC2, + 0xFC3F, (value << 6)); + b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, + 0xFFFE, 1); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_CMD_RXTX); + for (j = 0; j < 100; j++) { + if (!(b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_RXTX)) { + j = 0; + break; + } + udelay(10); + } + if (j) + b43err(dev->wl, + "intc override timeout\n"); + b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, + 0xFFFE); + } + break; + case N_INTC_OVERRIDE_PA: + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + tmp = 0x0020; + val = value << 5; + } else { + tmp = 0x0010; + val = value << 4; + } + b43_phy_maskset(dev, reg, ~tmp, val); + break; + case N_INTC_OVERRIDE_EXT_LNA_PU: + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + tmp = 0x0001; + val = value; + } else { + tmp = 0x0004; + val = value << 2; + } + b43_phy_maskset(dev, reg, ~tmp, val); + break; + case N_INTC_OVERRIDE_EXT_LNA_GAIN: + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + tmp = 0x0002; + val = value << 1; + } else { + tmp = 0x0008; + val = value << 3; + } + b43_phy_maskset(dev, reg, ~tmp, val); + break; + } + } +} + +/************************************************** + * Various PHY ops + **************************************************/ + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */ +static void b43_nphy_write_clip_detection(struct b43_wldev *dev, + const u16 *clip_st) +{ + b43_phy_write(dev, B43_NPHY_C1_CLIP1THRES, clip_st[0]); + b43_phy_write(dev, B43_NPHY_C2_CLIP1THRES, clip_st[1]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */ +static void b43_nphy_read_clip_detection(struct b43_wldev *dev, u16 *clip_st) +{ + clip_st[0] = b43_phy_read(dev, B43_NPHY_C1_CLIP1THRES); + clip_st[1] = b43_phy_read(dev, B43_NPHY_C2_CLIP1THRES); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/classifier */ +static u16 b43_nphy_classifier(struct b43_wldev *dev, u16 mask, u16 val) +{ + u16 tmp; + + if (dev->dev->core_rev == 16) + b43_mac_suspend(dev); + + tmp = b43_phy_read(dev, B43_NPHY_CLASSCTL); + tmp &= (B43_NPHY_CLASSCTL_CCKEN | B43_NPHY_CLASSCTL_OFDMEN | + B43_NPHY_CLASSCTL_WAITEDEN); + tmp &= ~mask; + tmp |= (val & mask); + b43_phy_maskset(dev, B43_NPHY_CLASSCTL, 0xFFF8, tmp); + + if (dev->dev->core_rev == 16) + b43_mac_enable(dev); + + return tmp; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CCA */ +static void b43_nphy_reset_cca(struct b43_wldev *dev) +{ + u16 bbcfg; + + b43_phy_force_clock(dev, 1); + bbcfg = b43_phy_read(dev, B43_NPHY_BBCFG); + b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg | B43_NPHY_BBCFG_RSTCCA); + udelay(1); + b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg & ~B43_NPHY_BBCFG_RSTCCA); + b43_phy_force_clock(dev, 0); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/carriersearch */ +static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = phy->n; + + if (enable) { + static const u16 clip[] = { 0xFFFF, 0xFFFF }; + if (nphy->deaf_count++ == 0) { + nphy->classifier_state = b43_nphy_classifier(dev, 0, 0); + b43_nphy_classifier(dev, 0x7, + B43_NPHY_CLASSCTL_WAITEDEN); + b43_nphy_read_clip_detection(dev, nphy->clip_state); + b43_nphy_write_clip_detection(dev, clip); + } + b43_nphy_reset_cca(dev); + } else { + if (--nphy->deaf_count == 0) { + b43_nphy_classifier(dev, 0x7, nphy->classifier_state); + b43_nphy_write_clip_detection(dev, nphy->clip_state); + } + } +} + +/* http://bcm-v4.sipsolutions.net/PHY/N/Read_Lpf_Bw_Ctl */ +static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset) +{ + if (!offset) + offset = b43_is_40mhz(dev) ? 0x159 : 0x154; + return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/AdjustLnaGainTbl */ +static void b43_nphy_adjust_lna_gain_table(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u8 i; + s16 tmp; + u16 data[4]; + s16 gain[2]; + u16 minmax[2]; + static const u16 lna_gain[4] = { -2, 10, 19, 25 }; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + if (nphy->gain_boost) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + gain[0] = 6; + gain[1] = 6; + } else { + tmp = 40370 - 315 * dev->phy.channel; + gain[0] = ((tmp >> 13) + ((tmp >> 12) & 1)); + tmp = 23242 - 224 * dev->phy.channel; + gain[1] = ((tmp >> 13) + ((tmp >> 12) & 1)); + } + } else { + gain[0] = 0; + gain[1] = 0; + } + + for (i = 0; i < 2; i++) { + if (nphy->elna_gain_config) { + data[0] = 19 + gain[i]; + data[1] = 25 + gain[i]; + data[2] = 25 + gain[i]; + data[3] = 25 + gain[i]; + } else { + data[0] = lna_gain[0] + gain[i]; + data[1] = lna_gain[1] + gain[i]; + data[2] = lna_gain[2] + gain[i]; + data[3] = lna_gain[3] + gain[i]; + } + b43_ntab_write_bulk(dev, B43_NTAB16(i, 8), 4, data); + + minmax[i] = 23 + gain[i]; + } + + b43_phy_maskset(dev, B43_NPHY_C1_MINMAX_GAIN, ~B43_NPHY_C1_MINGAIN, + minmax[0] << B43_NPHY_C1_MINGAIN_SHIFT); + b43_phy_maskset(dev, B43_NPHY_C2_MINMAX_GAIN, ~B43_NPHY_C2_MINGAIN, + minmax[1] << B43_NPHY_C2_MINGAIN_SHIFT); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRfSeq */ +static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd, + u8 *events, u8 *delays, u8 length) +{ + struct b43_phy_n *nphy = dev->phy.n; + u8 i; + u8 end = (dev->phy.rev >= 3) ? 0x1F : 0x0F; + u16 offset1 = cmd << 4; + u16 offset2 = offset1 + 0x80; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, true); + + b43_ntab_write_bulk(dev, B43_NTAB8(7, offset1), length, events); + b43_ntab_write_bulk(dev, B43_NTAB8(7, offset2), length, delays); + + for (i = length; i < 16; i++) { + b43_ntab_write(dev, B43_NTAB8(7, offset1 + i), end); + b43_ntab_write(dev, B43_NTAB8(7, offset2 + i), 1); + } + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, false); +} + +/************************************************** + * Radio 0x2057 + **************************************************/ + +static void b43_radio_2057_chantab_upload(struct b43_wldev *dev, + const struct b43_nphy_chantabent_rev7 *e_r7, + const struct b43_nphy_chantabent_rev7_2g *e_r7_2g) +{ + if (e_r7_2g) { + b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7_2g->radio_vcocal_countval0); + b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7_2g->radio_vcocal_countval1); + b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7_2g->radio_rfpll_refmaster_sparextalsize); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7_2g->radio_rfpll_loopfilter_r1); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7_2g->radio_rfpll_loopfilter_c2); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7_2g->radio_rfpll_loopfilter_c1); + b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7_2g->radio_cp_kpd_idac); + b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7_2g->radio_rfpll_mmd0); + b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7_2g->radio_rfpll_mmd1); + b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7_2g->radio_vcobuf_tune); + b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7_2g->radio_logen_mx2g_tune); + b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7_2g->radio_logen_indbuf2g_tune); + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7_2g->radio_txmix2g_tune_boost_pu_core0); + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7_2g->radio_pad2g_tune_pus_core0); + b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7_2g->radio_lna2g_tune_core0); + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7_2g->radio_txmix2g_tune_boost_pu_core1); + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7_2g->radio_pad2g_tune_pus_core1); + b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7_2g->radio_lna2g_tune_core1); + + } else { + b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7->radio_vcocal_countval0); + b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7->radio_vcocal_countval1); + b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7->radio_rfpll_refmaster_sparextalsize); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7->radio_rfpll_loopfilter_r1); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7->radio_rfpll_loopfilter_c2); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7->radio_rfpll_loopfilter_c1); + b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7->radio_cp_kpd_idac); + b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7->radio_rfpll_mmd0); + b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7->radio_rfpll_mmd1); + b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7->radio_vcobuf_tune); + b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7->radio_logen_mx2g_tune); + b43_radio_write(dev, R2057_LOGEN_MX5G_TUNE, e_r7->radio_logen_mx5g_tune); + b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7->radio_logen_indbuf2g_tune); + b43_radio_write(dev, R2057_LOGEN_INDBUF5G_TUNE, e_r7->radio_logen_indbuf5g_tune); + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7->radio_txmix2g_tune_boost_pu_core0); + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7->radio_pad2g_tune_pus_core0); + b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE0, e_r7->radio_pga_boost_tune_core0); + b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE0, e_r7->radio_txmix5g_boost_tune_core0); + b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE0, e_r7->radio_pad5g_tune_misc_pus_core0); + b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7->radio_lna2g_tune_core0); + b43_radio_write(dev, R2057_LNA5G_TUNE_CORE0, e_r7->radio_lna5g_tune_core0); + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7->radio_txmix2g_tune_boost_pu_core1); + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7->radio_pad2g_tune_pus_core1); + b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE1, e_r7->radio_pga_boost_tune_core1); + b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE1, e_r7->radio_txmix5g_boost_tune_core1); + b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE1, e_r7->radio_pad5g_tune_misc_pus_core1); + b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7->radio_lna2g_tune_core1); + b43_radio_write(dev, R2057_LNA5G_TUNE_CORE1, e_r7->radio_lna5g_tune_core1); + } +} + +static void b43_radio_2057_setup(struct b43_wldev *dev, + const struct b43_nphy_chantabent_rev7 *tabent_r7, + const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g) +{ + struct b43_phy *phy = &dev->phy; + + b43_radio_2057_chantab_upload(dev, tabent_r7, tabent_r7_2g); + + switch (phy->radio_rev) { + case 0 ... 4: + case 6: + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x3f); + b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8); + } else { + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x1f); + b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8); + } + break; + case 9: /* e.g. PHY rev 16 */ + b43_radio_write(dev, R2057_LOGEN_PTAT_RESETS, 0x20); + b43_radio_write(dev, R2057_VCOBUF_IDACS, 0x18); + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_radio_write(dev, R2057_LOGEN_PTAT_RESETS, 0x38); + b43_radio_write(dev, R2057_VCOBUF_IDACS, 0x0f); + + if (b43_is_40mhz(dev)) { + /* TODO */ + } else { + b43_radio_write(dev, + R2057_PAD_BIAS_FILTER_BWS_CORE0, + 0x3c); + b43_radio_write(dev, + R2057_PAD_BIAS_FILTER_BWS_CORE1, + 0x3c); + } + } + break; + case 14: /* 2 GHz only */ + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x1b); + b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x1f); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x1f); + break; + } + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + u16 txmix2g_tune_boost_pu = 0; + u16 pad2g_tune_pus = 0; + + if (b43_nphy_ipa(dev)) { + switch (phy->radio_rev) { + case 9: + txmix2g_tune_boost_pu = 0x0041; + /* TODO */ + break; + case 14: + txmix2g_tune_boost_pu = 0x21; + pad2g_tune_pus = 0x23; + break; + } + } + + if (txmix2g_tune_boost_pu) + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, + txmix2g_tune_boost_pu); + if (pad2g_tune_pus) + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, + pad2g_tune_pus); + if (txmix2g_tune_boost_pu) + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, + txmix2g_tune_boost_pu); + if (pad2g_tune_pus) + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, + pad2g_tune_pus); + } + + usleep_range(50, 100); + + /* VCO calibration */ + b43_radio_mask(dev, R2057_RFPLL_MISC_EN, ~0x01); + b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x04); + b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x4); + b43_radio_set(dev, R2057_RFPLL_MISC_EN, 0x01); + usleep_range(300, 600); +} + +/* Calibrate resistors in LPF of PLL? + * http://bcm-v4.sipsolutions.net/PHY/radio205x_rcal + */ +static u8 b43_radio_2057_rcal(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + u16 saved_regs_phy[12]; + u16 saved_regs_phy_rf[6]; + u16 saved_regs_radio[2] = { }; + static const u16 phy_to_store[] = { + B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2, + B43_NPHY_RFCTL_LUT_TRSW_LO1, B43_NPHY_RFCTL_LUT_TRSW_LO2, + B43_NPHY_RFCTL_RXG1, B43_NPHY_RFCTL_RXG2, + B43_NPHY_RFCTL_TXG1, B43_NPHY_RFCTL_TXG2, + B43_NPHY_REV7_RF_CTL_MISC_REG3, B43_NPHY_REV7_RF_CTL_MISC_REG4, + B43_NPHY_REV7_RF_CTL_MISC_REG5, B43_NPHY_REV7_RF_CTL_MISC_REG6, + }; + static const u16 phy_to_store_rf[] = { + B43_NPHY_REV3_RFCTL_OVER0, B43_NPHY_REV3_RFCTL_OVER1, + B43_NPHY_REV7_RF_CTL_OVER3, B43_NPHY_REV7_RF_CTL_OVER4, + B43_NPHY_REV7_RF_CTL_OVER5, B43_NPHY_REV7_RF_CTL_OVER6, + }; + u16 tmp; + int i; + + /* Save */ + for (i = 0; i < ARRAY_SIZE(phy_to_store); i++) + saved_regs_phy[i] = b43_phy_read(dev, phy_to_store[i]); + for (i = 0; i < ARRAY_SIZE(phy_to_store_rf); i++) + saved_regs_phy_rf[i] = b43_phy_read(dev, phy_to_store_rf[i]); + + /* Set */ + for (i = 0; i < ARRAY_SIZE(phy_to_store); i++) + b43_phy_write(dev, phy_to_store[i], 0); + b43_phy_write(dev, B43_NPHY_REV3_RFCTL_OVER0, 0x07ff); + b43_phy_write(dev, B43_NPHY_REV3_RFCTL_OVER1, 0x07ff); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x07ff); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER4, 0x07ff); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER5, 0x007f); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER6, 0x007f); + + switch (phy->radio_rev) { + case 5: + b43_phy_mask(dev, B43_NPHY_REV7_RF_CTL_OVER3, ~0x2); + udelay(10); + b43_radio_set(dev, R2057_IQTEST_SEL_PU, 0x1); + b43_radio_maskset(dev, R2057v7_IQTEST_SEL_PU2, ~0x2, 0x1); + break; + case 9: + b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x2); + b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_MISC_REG3, 0x2); + saved_regs_radio[0] = b43_radio_read(dev, R2057_IQTEST_SEL_PU); + b43_radio_write(dev, R2057_IQTEST_SEL_PU, 0x11); + break; + case 14: + saved_regs_radio[0] = b43_radio_read(dev, R2057_IQTEST_SEL_PU); + saved_regs_radio[1] = b43_radio_read(dev, R2057v7_IQTEST_SEL_PU2); + b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_MISC_REG3, 0x2); + b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x2); + b43_radio_write(dev, R2057v7_IQTEST_SEL_PU2, 0x2); + b43_radio_write(dev, R2057_IQTEST_SEL_PU, 0x1); + break; + } + + /* Enable */ + b43_radio_set(dev, R2057_RCAL_CONFIG, 0x1); + udelay(10); + + /* Start */ + b43_radio_set(dev, R2057_RCAL_CONFIG, 0x2); + usleep_range(100, 200); + + /* Stop */ + b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x2); + + /* Wait and check for result */ + if (!b43_radio_wait_value(dev, R2057_RCAL_STATUS, 1, 1, 100, 1000000)) { + b43err(dev->wl, "Radio 0x2057 rcal timeout\n"); + return 0; + } + tmp = b43_radio_read(dev, R2057_RCAL_STATUS) & 0x3E; + + /* Disable */ + b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x1); + + /* Restore */ + for (i = 0; i < ARRAY_SIZE(phy_to_store_rf); i++) + b43_phy_write(dev, phy_to_store_rf[i], saved_regs_phy_rf[i]); + for (i = 0; i < ARRAY_SIZE(phy_to_store); i++) + b43_phy_write(dev, phy_to_store[i], saved_regs_phy[i]); + + switch (phy->radio_rev) { + case 0 ... 4: + case 6: + b43_radio_maskset(dev, R2057_TEMPSENSE_CONFIG, ~0x3C, tmp); + b43_radio_maskset(dev, R2057_BANDGAP_RCAL_TRIM, ~0xF0, + tmp << 2); + break; + case 5: + b43_radio_mask(dev, R2057_IPA2G_CASCONV_CORE0, ~0x1); + b43_radio_mask(dev, R2057v7_IQTEST_SEL_PU2, ~0x2); + break; + case 9: + b43_radio_write(dev, R2057_IQTEST_SEL_PU, saved_regs_radio[0]); + break; + case 14: + b43_radio_write(dev, R2057_IQTEST_SEL_PU, saved_regs_radio[0]); + b43_radio_write(dev, R2057v7_IQTEST_SEL_PU2, saved_regs_radio[1]); + break; + } + + return tmp & 0x3e; +} + +/* Calibrate the internal RC oscillator? + * http://bcm-v4.sipsolutions.net/PHY/radio2057_rccal + */ +static u16 b43_radio_2057_rccal(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + bool special = (phy->radio_rev == 3 || phy->radio_rev == 4 || + phy->radio_rev == 6); + u16 tmp; + + /* Setup cal */ + if (special) { + b43_radio_write(dev, R2057_RCCAL_MASTER, 0x61); + b43_radio_write(dev, R2057_RCCAL_TRC0, 0xC0); + } else { + b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x61); + b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE9); + } + b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); + + /* Start, wait, stop */ + b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); + if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500, + 5000000)) + b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n"); + usleep_range(35, 70); + b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); + usleep_range(70, 140); + + /* Setup cal */ + if (special) { + b43_radio_write(dev, R2057_RCCAL_MASTER, 0x69); + b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0); + } else { + b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x69); + b43_radio_write(dev, R2057_RCCAL_TRC0, 0xD5); + } + b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); + + /* Start, wait, stop */ + usleep_range(35, 70); + b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); + usleep_range(70, 140); + if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500, + 5000000)) + b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n"); + usleep_range(35, 70); + b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); + usleep_range(70, 140); + + /* Setup cal */ + if (special) { + b43_radio_write(dev, R2057_RCCAL_MASTER, 0x73); + b43_radio_write(dev, R2057_RCCAL_X1, 0x28); + b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0); + } else { + b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x73); + b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); + b43_radio_write(dev, R2057_RCCAL_TRC0, 0x99); + } + + /* Start, wait, stop */ + usleep_range(35, 70); + b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55); + usleep_range(70, 140); + if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500, + 5000000)) { + b43err(dev->wl, "Radio 0x2057 rcal timeout\n"); + return 0; + } + tmp = b43_radio_read(dev, R2057_RCCAL_DONE_OSCCAP); + usleep_range(35, 70); + b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15); + usleep_range(70, 140); + + if (special) + b43_radio_mask(dev, R2057_RCCAL_MASTER, ~0x1); + else + b43_radio_mask(dev, R2057v7_RCCAL_MASTER, ~0x1); + + return tmp; +} + +static void b43_radio_2057_init_pre(struct b43_wldev *dev) +{ + b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_CHIP0PU); + /* Maybe wl meant to reset and set (order?) RFCTL_CMD_OEPORFORCE? */ + b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_OEPORFORCE); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_OEPORFORCE); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_CHIP0PU); +} + +static void b43_radio_2057_init_post(struct b43_wldev *dev) +{ + b43_radio_set(dev, R2057_XTALPUOVR_PINCTRL, 0x1); + + if (0) /* FIXME: Is this BCM43217 specific? */ + b43_radio_set(dev, R2057_XTALPUOVR_PINCTRL, 0x2); + + b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x78); + b43_radio_set(dev, R2057_XTAL_CONFIG2, 0x80); + mdelay(2); + b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x78); + b43_radio_mask(dev, R2057_XTAL_CONFIG2, ~0x80); + + if (dev->phy.do_full_init) { + b43_radio_2057_rcal(dev); + b43_radio_2057_rccal(dev); + } + b43_radio_mask(dev, R2057_RFPLL_MASTER, ~0x8); +} + +/* http://bcm-v4.sipsolutions.net/802.11/Radio/2057/Init */ +static void b43_radio_2057_init(struct b43_wldev *dev) +{ + b43_radio_2057_init_pre(dev); + r2057_upload_inittabs(dev); + b43_radio_2057_init_post(dev); +} + +/************************************************** + * Radio 0x2056 + **************************************************/ + +static void b43_chantab_radio_2056_upload(struct b43_wldev *dev, + const struct b43_nphy_channeltab_entry_rev3 *e) +{ + b43_radio_write(dev, B2056_SYN_PLL_VCOCAL1, e->radio_syn_pll_vcocal1); + b43_radio_write(dev, B2056_SYN_PLL_VCOCAL2, e->radio_syn_pll_vcocal2); + b43_radio_write(dev, B2056_SYN_PLL_REFDIV, e->radio_syn_pll_refdiv); + b43_radio_write(dev, B2056_SYN_PLL_MMD2, e->radio_syn_pll_mmd2); + b43_radio_write(dev, B2056_SYN_PLL_MMD1, e->radio_syn_pll_mmd1); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, + e->radio_syn_pll_loopfilter1); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, + e->radio_syn_pll_loopfilter2); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER3, + e->radio_syn_pll_loopfilter3); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, + e->radio_syn_pll_loopfilter4); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER5, + e->radio_syn_pll_loopfilter5); + b43_radio_write(dev, B2056_SYN_RESERVED_ADDR27, + e->radio_syn_reserved_addr27); + b43_radio_write(dev, B2056_SYN_RESERVED_ADDR28, + e->radio_syn_reserved_addr28); + b43_radio_write(dev, B2056_SYN_RESERVED_ADDR29, + e->radio_syn_reserved_addr29); + b43_radio_write(dev, B2056_SYN_LOGEN_VCOBUF1, + e->radio_syn_logen_vcobuf1); + b43_radio_write(dev, B2056_SYN_LOGEN_MIXER2, e->radio_syn_logen_mixer2); + b43_radio_write(dev, B2056_SYN_LOGEN_BUF3, e->radio_syn_logen_buf3); + b43_radio_write(dev, B2056_SYN_LOGEN_BUF4, e->radio_syn_logen_buf4); + + b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAA_TUNE, + e->radio_rx0_lnaa_tune); + b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAG_TUNE, + e->radio_rx0_lnag_tune); + + b43_radio_write(dev, B2056_TX0 | B2056_TX_INTPAA_BOOST_TUNE, + e->radio_tx0_intpaa_boost_tune); + b43_radio_write(dev, B2056_TX0 | B2056_TX_INTPAG_BOOST_TUNE, + e->radio_tx0_intpag_boost_tune); + b43_radio_write(dev, B2056_TX0 | B2056_TX_PADA_BOOST_TUNE, + e->radio_tx0_pada_boost_tune); + b43_radio_write(dev, B2056_TX0 | B2056_TX_PADG_BOOST_TUNE, + e->radio_tx0_padg_boost_tune); + b43_radio_write(dev, B2056_TX0 | B2056_TX_PGAA_BOOST_TUNE, + e->radio_tx0_pgaa_boost_tune); + b43_radio_write(dev, B2056_TX0 | B2056_TX_PGAG_BOOST_TUNE, + e->radio_tx0_pgag_boost_tune); + b43_radio_write(dev, B2056_TX0 | B2056_TX_MIXA_BOOST_TUNE, + e->radio_tx0_mixa_boost_tune); + b43_radio_write(dev, B2056_TX0 | B2056_TX_MIXG_BOOST_TUNE, + e->radio_tx0_mixg_boost_tune); + + b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAA_TUNE, + e->radio_rx1_lnaa_tune); + b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAG_TUNE, + e->radio_rx1_lnag_tune); + + b43_radio_write(dev, B2056_TX1 | B2056_TX_INTPAA_BOOST_TUNE, + e->radio_tx1_intpaa_boost_tune); + b43_radio_write(dev, B2056_TX1 | B2056_TX_INTPAG_BOOST_TUNE, + e->radio_tx1_intpag_boost_tune); + b43_radio_write(dev, B2056_TX1 | B2056_TX_PADA_BOOST_TUNE, + e->radio_tx1_pada_boost_tune); + b43_radio_write(dev, B2056_TX1 | B2056_TX_PADG_BOOST_TUNE, + e->radio_tx1_padg_boost_tune); + b43_radio_write(dev, B2056_TX1 | B2056_TX_PGAA_BOOST_TUNE, + e->radio_tx1_pgaa_boost_tune); + b43_radio_write(dev, B2056_TX1 | B2056_TX_PGAG_BOOST_TUNE, + e->radio_tx1_pgag_boost_tune); + b43_radio_write(dev, B2056_TX1 | B2056_TX_MIXA_BOOST_TUNE, + e->radio_tx1_mixa_boost_tune); + b43_radio_write(dev, B2056_TX1 | B2056_TX_MIXG_BOOST_TUNE, + e->radio_tx1_mixg_boost_tune); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/Radio/2056Setup */ +static void b43_radio_2056_setup(struct b43_wldev *dev, + const struct b43_nphy_channeltab_entry_rev3 *e) +{ + struct b43_phy *phy = &dev->phy; + struct ssb_sprom *sprom = dev->dev->bus_sprom; + enum ieee80211_band band = b43_current_band(dev->wl); + u16 offset; + u8 i; + u16 bias, cbias; + u16 pag_boost, padg_boost, pgag_boost, mixg_boost; + u16 paa_boost, pada_boost, pgaa_boost, mixa_boost; + bool is_pkg_fab_smic; + + B43_WARN_ON(dev->phy.rev < 3); + + is_pkg_fab_smic = + ((dev->dev->chip_id == BCMA_CHIP_ID_BCM43224 || + dev->dev->chip_id == BCMA_CHIP_ID_BCM43225 || + dev->dev->chip_id == BCMA_CHIP_ID_BCM43421) && + dev->dev->chip_pkg == BCMA_PKG_ID_BCM43224_FAB_SMIC); + + b43_chantab_radio_2056_upload(dev, e); + b2056_upload_syn_pll_cp2(dev, band == IEEE80211_BAND_5GHZ); + + if (sprom->boardflags2_lo & B43_BFL2_GPLL_WAR && + b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1F); + if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 || + dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) { + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x14); + b43_radio_write(dev, B2056_SYN_PLL_CP2, 0); + } else { + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x0B); + b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x14); + } + } + if (sprom->boardflags2_hi & B43_BFH2_GPLL_WAR2 && + b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1f); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1f); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x0b); + b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x20); + } + if (sprom->boardflags2_lo & B43_BFL2_APLL_WAR && + b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1F); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x05); + b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x0C); + } + + if (dev->phy.n->ipa2g_on && band == IEEE80211_BAND_2GHZ) { + for (i = 0; i < 2; i++) { + offset = i ? B2056_TX1 : B2056_TX0; + if (dev->phy.rev >= 5) { + b43_radio_write(dev, + offset | B2056_TX_PADG_IDAC, 0xcc); + + if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 || + dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) { + bias = 0x40; + cbias = 0x45; + pag_boost = 0x5; + pgag_boost = 0x33; + mixg_boost = 0x55; + } else { + bias = 0x25; + cbias = 0x20; + if (is_pkg_fab_smic) { + bias = 0x2a; + cbias = 0x38; + } + pag_boost = 0x4; + pgag_boost = 0x03; + mixg_boost = 0x65; + } + padg_boost = 0x77; + + b43_radio_write(dev, + offset | B2056_TX_INTPAG_IMAIN_STAT, + bias); + b43_radio_write(dev, + offset | B2056_TX_INTPAG_IAUX_STAT, + bias); + b43_radio_write(dev, + offset | B2056_TX_INTPAG_CASCBIAS, + cbias); + b43_radio_write(dev, + offset | B2056_TX_INTPAG_BOOST_TUNE, + pag_boost); + b43_radio_write(dev, + offset | B2056_TX_PGAG_BOOST_TUNE, + pgag_boost); + b43_radio_write(dev, + offset | B2056_TX_PADG_BOOST_TUNE, + padg_boost); + b43_radio_write(dev, + offset | B2056_TX_MIXG_BOOST_TUNE, + mixg_boost); + } else { + bias = b43_is_40mhz(dev) ? 0x40 : 0x20; + b43_radio_write(dev, + offset | B2056_TX_INTPAG_IMAIN_STAT, + bias); + b43_radio_write(dev, + offset | B2056_TX_INTPAG_IAUX_STAT, + bias); + b43_radio_write(dev, + offset | B2056_TX_INTPAG_CASCBIAS, + 0x30); + } + b43_radio_write(dev, offset | B2056_TX_PA_SPARE1, 0xee); + } + } else if (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ) { + u16 freq = phy->chandef->chan->center_freq; + if (freq < 5100) { + paa_boost = 0xA; + pada_boost = 0x77; + pgaa_boost = 0xF; + mixa_boost = 0xF; + } else if (freq < 5340) { + paa_boost = 0x8; + pada_boost = 0x77; + pgaa_boost = 0xFB; + mixa_boost = 0xF; + } else if (freq < 5650) { + paa_boost = 0x0; + pada_boost = 0x77; + pgaa_boost = 0xB; + mixa_boost = 0xF; + } else { + paa_boost = 0x0; + pada_boost = 0x77; + if (freq != 5825) + pgaa_boost = -(freq - 18) / 36 + 168; + else + pgaa_boost = 6; + mixa_boost = 0xF; + } + + cbias = is_pkg_fab_smic ? 0x35 : 0x30; + + for (i = 0; i < 2; i++) { + offset = i ? B2056_TX1 : B2056_TX0; + + b43_radio_write(dev, + offset | B2056_TX_INTPAA_BOOST_TUNE, paa_boost); + b43_radio_write(dev, + offset | B2056_TX_PADA_BOOST_TUNE, pada_boost); + b43_radio_write(dev, + offset | B2056_TX_PGAA_BOOST_TUNE, pgaa_boost); + b43_radio_write(dev, + offset | B2056_TX_MIXA_BOOST_TUNE, mixa_boost); + b43_radio_write(dev, + offset | B2056_TX_TXSPARE1, 0x30); + b43_radio_write(dev, + offset | B2056_TX_PA_SPARE2, 0xee); + b43_radio_write(dev, + offset | B2056_TX_PADA_CASCBIAS, 0x03); + b43_radio_write(dev, + offset | B2056_TX_INTPAA_IAUX_STAT, 0x30); + b43_radio_write(dev, + offset | B2056_TX_INTPAA_IMAIN_STAT, 0x30); + b43_radio_write(dev, + offset | B2056_TX_INTPAA_CASCBIAS, cbias); + } + } + + udelay(50); + /* VCO calibration */ + b43_radio_write(dev, B2056_SYN_PLL_VCOCAL12, 0x00); + b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x38); + b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x18); + b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x38); + b43_radio_write(dev, B2056_TX_INTPAA_PA_MISC, 0x39); + udelay(300); +} + +static u8 b43_radio_2056_rcal(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + u16 mast2, tmp; + + if (phy->rev != 3) + return 0; + + mast2 = b43_radio_read(dev, B2056_SYN_PLL_MAST2); + b43_radio_write(dev, B2056_SYN_PLL_MAST2, mast2 | 0x7); + + udelay(10); + b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x01); + udelay(10); + b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x09); + + if (!b43_radio_wait_value(dev, B2056_SYN_RCAL_CODE_OUT, 0x80, 0x80, 100, + 1000000)) { + b43err(dev->wl, "Radio recalibration timeout\n"); + return 0; + } + + b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x01); + tmp = b43_radio_read(dev, B2056_SYN_RCAL_CODE_OUT); + b43_radio_write(dev, B2056_SYN_RCAL_MASTER, 0x00); + + b43_radio_write(dev, B2056_SYN_PLL_MAST2, mast2); + + return tmp & 0x1f; +} + +static void b43_radio_init2056_pre(struct b43_wldev *dev) +{ + b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, + ~B43_NPHY_RFCTL_CMD_CHIP0PU); + /* Maybe wl meant to reset and set (order?) RFCTL_CMD_OEPORFORCE? */ + b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_CMD_OEPORFORCE); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, + ~B43_NPHY_RFCTL_CMD_OEPORFORCE); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_CMD_CHIP0PU); +} + +static void b43_radio_init2056_post(struct b43_wldev *dev) +{ + b43_radio_set(dev, B2056_SYN_COM_CTRL, 0xB); + b43_radio_set(dev, B2056_SYN_COM_PU, 0x2); + b43_radio_set(dev, B2056_SYN_COM_RESET, 0x2); + msleep(1); + b43_radio_mask(dev, B2056_SYN_COM_RESET, ~0x2); + b43_radio_mask(dev, B2056_SYN_PLL_MAST2, ~0xFC); + b43_radio_mask(dev, B2056_SYN_RCCAL_CTRL0, ~0x1); + if (dev->phy.do_full_init) + b43_radio_2056_rcal(dev); +} + +/* + * Initialize a Broadcom 2056 N-radio + * http://bcm-v4.sipsolutions.net/802.11/Radio/2056/Init + */ +static void b43_radio_init2056(struct b43_wldev *dev) +{ + b43_radio_init2056_pre(dev); + b2056_upload_inittabs(dev, 0, 0); + b43_radio_init2056_post(dev); +} + +/************************************************** + * Radio 0x2055 + **************************************************/ + +static void b43_chantab_radio_upload(struct b43_wldev *dev, + const struct b43_nphy_channeltab_entry_rev2 *e) +{ + b43_radio_write(dev, B2055_PLL_REF, e->radio_pll_ref); + b43_radio_write(dev, B2055_RF_PLLMOD0, e->radio_rf_pllmod0); + b43_radio_write(dev, B2055_RF_PLLMOD1, e->radio_rf_pllmod1); + b43_radio_write(dev, B2055_VCO_CAPTAIL, e->radio_vco_captail); + b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ + + b43_radio_write(dev, B2055_VCO_CAL1, e->radio_vco_cal1); + b43_radio_write(dev, B2055_VCO_CAL2, e->radio_vco_cal2); + b43_radio_write(dev, B2055_PLL_LFC1, e->radio_pll_lfc1); + b43_radio_write(dev, B2055_PLL_LFR1, e->radio_pll_lfr1); + b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ + + b43_radio_write(dev, B2055_PLL_LFC2, e->radio_pll_lfc2); + b43_radio_write(dev, B2055_LGBUF_CENBUF, e->radio_lgbuf_cenbuf); + b43_radio_write(dev, B2055_LGEN_TUNE1, e->radio_lgen_tune1); + b43_radio_write(dev, B2055_LGEN_TUNE2, e->radio_lgen_tune2); + b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ + + b43_radio_write(dev, B2055_C1_LGBUF_ATUNE, e->radio_c1_lgbuf_atune); + b43_radio_write(dev, B2055_C1_LGBUF_GTUNE, e->radio_c1_lgbuf_gtune); + b43_radio_write(dev, B2055_C1_RX_RFR1, e->radio_c1_rx_rfr1); + b43_radio_write(dev, B2055_C1_TX_PGAPADTN, e->radio_c1_tx_pgapadtn); + b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ + + b43_radio_write(dev, B2055_C1_TX_MXBGTRIM, e->radio_c1_tx_mxbgtrim); + b43_radio_write(dev, B2055_C2_LGBUF_ATUNE, e->radio_c2_lgbuf_atune); + b43_radio_write(dev, B2055_C2_LGBUF_GTUNE, e->radio_c2_lgbuf_gtune); + b43_radio_write(dev, B2055_C2_RX_RFR1, e->radio_c2_rx_rfr1); + b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ + + b43_radio_write(dev, B2055_C2_TX_PGAPADTN, e->radio_c2_tx_pgapadtn); + b43_radio_write(dev, B2055_C2_TX_MXBGTRIM, e->radio_c2_tx_mxbgtrim); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/Radio/2055Setup */ +static void b43_radio_2055_setup(struct b43_wldev *dev, + const struct b43_nphy_channeltab_entry_rev2 *e) +{ + B43_WARN_ON(dev->phy.rev >= 3); + + b43_chantab_radio_upload(dev, e); + udelay(50); + b43_radio_write(dev, B2055_VCO_CAL10, 0x05); + b43_radio_write(dev, B2055_VCO_CAL10, 0x45); + b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */ + b43_radio_write(dev, B2055_VCO_CAL10, 0x65); + udelay(300); +} + +static void b43_radio_init2055_pre(struct b43_wldev *dev) +{ + b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, + ~B43_NPHY_RFCTL_CMD_PORFORCE); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_CMD_CHIP0PU | + B43_NPHY_RFCTL_CMD_OEPORFORCE); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_CMD_PORFORCE); +} + +static void b43_radio_init2055_post(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + struct ssb_sprom *sprom = dev->dev->bus_sprom; + bool workaround = false; + + if (sprom->revision < 4) + workaround = (dev->dev->board_vendor != PCI_VENDOR_ID_BROADCOM + && dev->dev->board_type == SSB_BOARD_CB2_4321 + && dev->dev->board_rev >= 0x41); + else + workaround = + !(sprom->boardflags2_lo & B43_BFL2_RXBB_INT_REG_DIS); + + b43_radio_mask(dev, B2055_MASTER1, 0xFFF3); + if (workaround) { + b43_radio_mask(dev, B2055_C1_RX_BB_REG, 0x7F); + b43_radio_mask(dev, B2055_C2_RX_BB_REG, 0x7F); + } + b43_radio_maskset(dev, B2055_RRCCAL_NOPTSEL, 0xFFC0, 0x2C); + b43_radio_write(dev, B2055_CAL_MISC, 0x3C); + b43_radio_mask(dev, B2055_CAL_MISC, 0xFFBE); + b43_radio_set(dev, B2055_CAL_LPOCTL, 0x80); + b43_radio_set(dev, B2055_CAL_MISC, 0x1); + msleep(1); + b43_radio_set(dev, B2055_CAL_MISC, 0x40); + if (!b43_radio_wait_value(dev, B2055_CAL_COUT2, 0x80, 0x80, 10, 2000)) + b43err(dev->wl, "radio post init timeout\n"); + b43_radio_mask(dev, B2055_CAL_LPOCTL, 0xFF7F); + b43_switch_channel(dev, dev->phy.channel); + b43_radio_write(dev, B2055_C1_RX_BB_LPF, 0x9); + b43_radio_write(dev, B2055_C2_RX_BB_LPF, 0x9); + b43_radio_write(dev, B2055_C1_RX_BB_MIDACHP, 0x83); + b43_radio_write(dev, B2055_C2_RX_BB_MIDACHP, 0x83); + b43_radio_maskset(dev, B2055_C1_LNA_GAINBST, 0xFFF8, 0x6); + b43_radio_maskset(dev, B2055_C2_LNA_GAINBST, 0xFFF8, 0x6); + if (!nphy->gain_boost) { + b43_radio_set(dev, B2055_C1_RX_RFSPC1, 0x2); + b43_radio_set(dev, B2055_C2_RX_RFSPC1, 0x2); + } else { + b43_radio_mask(dev, B2055_C1_RX_RFSPC1, 0xFFFD); + b43_radio_mask(dev, B2055_C2_RX_RFSPC1, 0xFFFD); + } + udelay(2); +} + +/* + * Initialize a Broadcom 2055 N-radio + * http://bcm-v4.sipsolutions.net/802.11/Radio/2055/Init + */ +static void b43_radio_init2055(struct b43_wldev *dev) +{ + b43_radio_init2055_pre(dev); + if (b43_status(dev) < B43_STAT_INITIALIZED) { + /* Follow wl, not specs. Do not force uploading all regs */ + b2055_upload_inittab(dev, 0, 0); + } else { + bool ghz5 = b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ; + b2055_upload_inittab(dev, ghz5, 0); + } + b43_radio_init2055_post(dev); +} + +/************************************************** + * Samples + **************************************************/ + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/LoadSampleTable */ +static int b43_nphy_load_samples(struct b43_wldev *dev, + struct b43_c32 *samples, u16 len) { + struct b43_phy_n *nphy = dev->phy.n; + u16 i; + u32 *data; + + data = kzalloc(len * sizeof(u32), GFP_KERNEL); + if (!data) { + b43err(dev->wl, "allocation for samples loading failed\n"); + return -ENOMEM; + } + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + for (i = 0; i < len; i++) { + data[i] = (samples[i].i & 0x3FF << 10); + data[i] |= samples[i].q & 0x3FF; + } + b43_ntab_write_bulk(dev, B43_NTAB32(17, 0), len, data); + + kfree(data); + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); + return 0; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GenLoadSamples */ +static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max, + bool test) +{ + int i; + u16 bw, len, rot, angle; + struct b43_c32 *samples; + + bw = b43_is_40mhz(dev) ? 40 : 20; + len = bw << 3; + + if (test) { + if (b43_phy_read(dev, B43_NPHY_BBCFG) & B43_NPHY_BBCFG_RSTRX) + bw = 82; + else + bw = 80; + + if (b43_is_40mhz(dev)) + bw <<= 1; + + len = bw << 1; + } + + samples = kcalloc(len, sizeof(struct b43_c32), GFP_KERNEL); + if (!samples) { + b43err(dev->wl, "allocation for samples generation failed\n"); + return 0; + } + rot = (((freq * 36) / bw) << 16) / 100; + angle = 0; + + for (i = 0; i < len; i++) { + samples[i] = b43_cordic(angle); + angle += rot; + samples[i].q = CORDIC_CONVERT(samples[i].q * max); + samples[i].i = CORDIC_CONVERT(samples[i].i * max); + } + + i = b43_nphy_load_samples(dev, samples, len); + kfree(samples); + return (i < 0) ? 0 : len; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */ +static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, + u16 wait, bool iqmode, bool dac_test, + bool modify_bbmult) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + int i; + u16 seq_mode; + u32 tmp; + + b43_nphy_stay_in_carrier_search(dev, true); + + if (phy->rev >= 7) { + bool lpf_bw3, lpf_bw4; + + lpf_bw3 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER3) & 0x80; + lpf_bw4 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER4) & 0x80; + + if (lpf_bw3 || lpf_bw4) { + /* TODO */ + } else { + u16 value = b43_nphy_read_lpf_ctl(dev, 0); + if (phy->rev >= 19) + b43_nphy_rf_ctl_override_rev19(dev, 0x80, value, + 0, false, 1); + else + b43_nphy_rf_ctl_override_rev7(dev, 0x80, value, + 0, false, 1); + nphy->lpf_bw_overrode_for_sample_play = true; + } + } + + if ((nphy->bb_mult_save & 0x80000000) == 0) { + tmp = b43_ntab_read(dev, B43_NTAB16(15, 87)); + nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000; + } + + if (modify_bbmult) { + tmp = !b43_is_40mhz(dev) ? 0x6464 : 0x4747; + b43_ntab_write(dev, B43_NTAB16(15, 87), tmp); + } + + b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1)); + + if (loops != 0xFFFF) + b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, (loops - 1)); + else + b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, loops); + + b43_phy_write(dev, B43_NPHY_SAMP_WAITCNT, wait); + + seq_mode = b43_phy_read(dev, B43_NPHY_RFSEQMODE); + + b43_phy_set(dev, B43_NPHY_RFSEQMODE, B43_NPHY_RFSEQMODE_CAOVER); + if (iqmode) { + b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF); + b43_phy_set(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8000); + } else { + tmp = dac_test ? 5 : 1; + b43_phy_write(dev, B43_NPHY_SAMP_CMD, tmp); + } + for (i = 0; i < 100; i++) { + if (!(b43_phy_read(dev, B43_NPHY_RFSEQST) & 1)) { + i = 0; + break; + } + udelay(10); + } + if (i) + b43err(dev->wl, "run samples timeout\n"); + + b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode); + + b43_nphy_stay_in_carrier_search(dev, false); +} + +/************************************************** + * RSSI + **************************************************/ + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ScaleOffsetRssi */ +static void b43_nphy_scale_offset_rssi(struct b43_wldev *dev, u16 scale, + s8 offset, u8 core, + enum n_rail_type rail, + enum n_rssi_type rssi_type) +{ + u16 tmp; + bool core1or5 = (core == 1) || (core == 5); + bool core2or5 = (core == 2) || (core == 5); + + offset = clamp_val(offset, -32, 31); + tmp = ((scale & 0x3F) << 8) | (offset & 0x3F); + + switch (rssi_type) { + case N_RSSI_NB: + if (core1or5 && rail == N_RAIL_I) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, tmp); + if (core1or5 && rail == N_RAIL_Q) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, tmp); + if (core2or5 && rail == N_RAIL_I) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, tmp); + if (core2or5 && rail == N_RAIL_Q) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, tmp); + break; + case N_RSSI_W1: + if (core1or5 && rail == N_RAIL_I) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, tmp); + if (core1or5 && rail == N_RAIL_Q) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, tmp); + if (core2or5 && rail == N_RAIL_I) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, tmp); + if (core2or5 && rail == N_RAIL_Q) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, tmp); + break; + case N_RSSI_W2: + if (core1or5 && rail == N_RAIL_I) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, tmp); + if (core1or5 && rail == N_RAIL_Q) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, tmp); + if (core2or5 && rail == N_RAIL_I) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, tmp); + if (core2or5 && rail == N_RAIL_Q) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, tmp); + break; + case N_RSSI_TBD: + if (core1or5 && rail == N_RAIL_I) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TBD, tmp); + if (core1or5 && rail == N_RAIL_Q) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TBD, tmp); + if (core2or5 && rail == N_RAIL_I) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TBD, tmp); + if (core2or5 && rail == N_RAIL_Q) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TBD, tmp); + break; + case N_RSSI_IQ: + if (core1or5 && rail == N_RAIL_I) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_PWRDET, tmp); + if (core1or5 && rail == N_RAIL_Q) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_PWRDET, tmp); + if (core2or5 && rail == N_RAIL_I) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_PWRDET, tmp); + if (core2or5 && rail == N_RAIL_Q) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_PWRDET, tmp); + break; + case N_RSSI_TSSI_2G: + if (core1or5) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TSSI, tmp); + if (core2or5) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TSSI, tmp); + break; + case N_RSSI_TSSI_5G: + if (core1or5) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TSSI, tmp); + if (core2or5) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TSSI, tmp); + break; + } +} + +static void b43_nphy_rssi_select_rev19(struct b43_wldev *dev, u8 code, + enum n_rssi_type rssi_type) +{ + /* TODO */ +} + +static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code, + enum n_rssi_type rssi_type) +{ + u8 i; + u16 reg, val; + + if (code == 0) { + b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, 0xFDFF); + b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, 0xFDFF); + b43_phy_mask(dev, B43_NPHY_AFECTL_C1, 0xFCFF); + b43_phy_mask(dev, B43_NPHY_AFECTL_C2, 0xFCFF); + b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S0, 0xFFDF); + b43_phy_mask(dev, B43_NPHY_TXF_40CO_B32S1, 0xFFDF); + b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0xFFC3); + b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0xFFC3); + } else { + for (i = 0; i < 2; i++) { + if ((code == 1 && i == 1) || (code == 2 && !i)) + continue; + + reg = (i == 0) ? + B43_NPHY_AFECTL_OVER1 : B43_NPHY_AFECTL_OVER; + b43_phy_maskset(dev, reg, 0xFDFF, 0x0200); + + if (rssi_type == N_RSSI_W1 || + rssi_type == N_RSSI_W2 || + rssi_type == N_RSSI_NB) { + reg = (i == 0) ? + B43_NPHY_AFECTL_C1 : + B43_NPHY_AFECTL_C2; + b43_phy_maskset(dev, reg, 0xFCFF, 0); + + reg = (i == 0) ? + B43_NPHY_RFCTL_LUT_TRSW_UP1 : + B43_NPHY_RFCTL_LUT_TRSW_UP2; + b43_phy_maskset(dev, reg, 0xFFC3, 0); + + if (rssi_type == N_RSSI_W1) + val = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? 4 : 8; + else if (rssi_type == N_RSSI_W2) + val = 16; + else + val = 32; + b43_phy_set(dev, reg, val); + + reg = (i == 0) ? + B43_NPHY_TXF_40CO_B1S0 : + B43_NPHY_TXF_40CO_B32S1; + b43_phy_set(dev, reg, 0x0020); + } else { + if (rssi_type == N_RSSI_TBD) + val = 0x0100; + else if (rssi_type == N_RSSI_IQ) + val = 0x0200; + else + val = 0x0300; + + reg = (i == 0) ? + B43_NPHY_AFECTL_C1 : + B43_NPHY_AFECTL_C2; + + b43_phy_maskset(dev, reg, 0xFCFF, val); + b43_phy_maskset(dev, reg, 0xF3FF, val << 2); + + if (rssi_type != N_RSSI_IQ && + rssi_type != N_RSSI_TBD) { + enum ieee80211_band band = + b43_current_band(dev->wl); + + if (dev->phy.rev < 7) { + if (b43_nphy_ipa(dev)) + val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE; + else + val = 0x11; + reg = (i == 0) ? B2056_TX0 : B2056_TX1; + reg |= B2056_TX_TX_SSI_MUX; + b43_radio_write(dev, reg, val); + } + + reg = (i == 0) ? + B43_NPHY_AFECTL_OVER1 : + B43_NPHY_AFECTL_OVER; + b43_phy_set(dev, reg, 0x0200); + } + } + } + } +} + +static void b43_nphy_rev2_rssi_select(struct b43_wldev *dev, u8 code, + enum n_rssi_type rssi_type) +{ + u16 val; + bool rssi_w1_w2_nb = false; + + switch (rssi_type) { + case N_RSSI_W1: + case N_RSSI_W2: + case N_RSSI_NB: + val = 0; + rssi_w1_w2_nb = true; + break; + case N_RSSI_TBD: + val = 1; + break; + case N_RSSI_IQ: + val = 2; + break; + default: + val = 3; + } + + val = (val << 12) | (val << 14); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, val); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, val); + + if (rssi_w1_w2_nb) { + b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO1, 0xFFCF, + (rssi_type + 1) << 4); + b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO2, 0xFFCF, + (rssi_type + 1) << 4); + } + + if (code == 0) { + b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x3000); + if (rssi_w1_w2_nb) { + b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, + ~(B43_NPHY_RFCTL_CMD_RXEN | + B43_NPHY_RFCTL_CMD_CORESEL)); + b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, + ~(0x1 << 12 | + 0x1 << 5 | + 0x1 << 1 | + 0x1)); + b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, + ~B43_NPHY_RFCTL_CMD_START); + udelay(20); + b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~0x1); + } + } else { + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x3000); + if (rssi_w1_w2_nb) { + b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, + ~(B43_NPHY_RFCTL_CMD_RXEN | + B43_NPHY_RFCTL_CMD_CORESEL), + (B43_NPHY_RFCTL_CMD_RXEN | + code << B43_NPHY_RFCTL_CMD_CORESEL_SHIFT)); + b43_phy_set(dev, B43_NPHY_RFCTL_OVER, + (0x1 << 12 | + 0x1 << 5 | + 0x1 << 1 | + 0x1)); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_CMD_START); + udelay(20); + b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~0x1); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSISel */ +static void b43_nphy_rssi_select(struct b43_wldev *dev, u8 code, + enum n_rssi_type type) +{ + if (dev->phy.rev >= 19) + b43_nphy_rssi_select_rev19(dev, code, type); + else if (dev->phy.rev >= 3) + b43_nphy_rev3_rssi_select(dev, code, type); + else + b43_nphy_rev2_rssi_select(dev, code, type); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRssi2055Vcm */ +static void b43_nphy_set_rssi_2055_vcm(struct b43_wldev *dev, + enum n_rssi_type rssi_type, u8 *buf) +{ + int i; + for (i = 0; i < 2; i++) { + if (rssi_type == N_RSSI_NB) { + if (i == 0) { + b43_radio_maskset(dev, B2055_C1_B0NB_RSSIVCM, + 0xFC, buf[0]); + b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5, + 0xFC, buf[1]); + } else { + b43_radio_maskset(dev, B2055_C2_B0NB_RSSIVCM, + 0xFC, buf[2 * i]); + b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5, + 0xFC, buf[2 * i + 1]); + } + } else { + if (i == 0) + b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5, + 0xF3, buf[0] << 2); + else + b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5, + 0xF3, buf[2 * i + 1] << 2); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PollRssi */ +static int b43_nphy_poll_rssi(struct b43_wldev *dev, enum n_rssi_type rssi_type, + s32 *buf, u8 nsamp) +{ + int i; + int out; + u16 save_regs_phy[9]; + u16 s[2]; + + /* TODO: rev7+ is treated like rev3+, what about rev19+? */ + + if (dev->phy.rev >= 3) { + save_regs_phy[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); + save_regs_phy[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); + save_regs_phy[2] = b43_phy_read(dev, + B43_NPHY_RFCTL_LUT_TRSW_UP1); + save_regs_phy[3] = b43_phy_read(dev, + B43_NPHY_RFCTL_LUT_TRSW_UP2); + save_regs_phy[4] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); + save_regs_phy[5] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + save_regs_phy[6] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S0); + save_regs_phy[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B32S1); + save_regs_phy[8] = 0; + } else { + save_regs_phy[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); + save_regs_phy[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); + save_regs_phy[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + save_regs_phy[3] = b43_phy_read(dev, B43_NPHY_RFCTL_CMD); + save_regs_phy[4] = b43_phy_read(dev, B43_NPHY_RFCTL_OVER); + save_regs_phy[5] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO1); + save_regs_phy[6] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO2); + save_regs_phy[7] = 0; + save_regs_phy[8] = 0; + } + + b43_nphy_rssi_select(dev, 5, rssi_type); + + if (dev->phy.rev < 2) { + save_regs_phy[8] = b43_phy_read(dev, B43_NPHY_GPIO_SEL); + b43_phy_write(dev, B43_NPHY_GPIO_SEL, 5); + } + + for (i = 0; i < 4; i++) + buf[i] = 0; + + for (i = 0; i < nsamp; i++) { + if (dev->phy.rev < 2) { + s[0] = b43_phy_read(dev, B43_NPHY_GPIO_LOOUT); + s[1] = b43_phy_read(dev, B43_NPHY_GPIO_HIOUT); + } else { + s[0] = b43_phy_read(dev, B43_NPHY_RSSI1); + s[1] = b43_phy_read(dev, B43_NPHY_RSSI2); + } + + buf[0] += ((s8)((s[0] & 0x3F) << 2)) >> 2; + buf[1] += ((s8)(((s[0] >> 8) & 0x3F) << 2)) >> 2; + buf[2] += ((s8)((s[1] & 0x3F) << 2)) >> 2; + buf[3] += ((s8)(((s[1] >> 8) & 0x3F) << 2)) >> 2; + } + out = (buf[0] & 0xFF) << 24 | (buf[1] & 0xFF) << 16 | + (buf[2] & 0xFF) << 8 | (buf[3] & 0xFF); + + if (dev->phy.rev < 2) + b43_phy_write(dev, B43_NPHY_GPIO_SEL, save_regs_phy[8]); + + if (dev->phy.rev >= 3) { + b43_phy_write(dev, B43_NPHY_AFECTL_C1, save_regs_phy[0]); + b43_phy_write(dev, B43_NPHY_AFECTL_C2, save_regs_phy[1]); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, + save_regs_phy[2]); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, + save_regs_phy[3]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, save_regs_phy[4]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[5]); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, save_regs_phy[6]); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, save_regs_phy[7]); + } else { + b43_phy_write(dev, B43_NPHY_AFECTL_C1, save_regs_phy[0]); + b43_phy_write(dev, B43_NPHY_AFECTL_C2, save_regs_phy[1]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[2]); + b43_phy_write(dev, B43_NPHY_RFCTL_CMD, save_regs_phy[3]); + b43_phy_write(dev, B43_NPHY_RFCTL_OVER, save_regs_phy[4]); + b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO1, save_regs_phy[5]); + b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO2, save_regs_phy[6]); + } + + return out; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */ +static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + + u16 saved_regs_phy_rfctl[2]; + u16 saved_regs_phy[22]; + u16 regs_to_store_rev3[] = { + B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER, + B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2, + B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER, + B43_NPHY_TXF_40CO_B1S0, B43_NPHY_TXF_40CO_B32S1, + B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2, + B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2 + }; + u16 regs_to_store_rev7[] = { + B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER, + B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2, + B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER, + B43_NPHY_REV7_RF_CTL_OVER3, B43_NPHY_REV7_RF_CTL_OVER4, + B43_NPHY_REV7_RF_CTL_OVER5, B43_NPHY_REV7_RF_CTL_OVER6, + 0x2ff, + B43_NPHY_TXF_40CO_B1S0, B43_NPHY_TXF_40CO_B32S1, + B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2, + B43_NPHY_REV7_RF_CTL_MISC_REG3, B43_NPHY_REV7_RF_CTL_MISC_REG4, + B43_NPHY_REV7_RF_CTL_MISC_REG5, B43_NPHY_REV7_RF_CTL_MISC_REG6, + B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2 + }; + u16 *regs_to_store; + int regs_amount; + + u16 class; + + u16 clip_state[2]; + u16 clip_off[2] = { 0xFFFF, 0xFFFF }; + + u8 vcm_final = 0; + s32 offset[4]; + s32 results[8][4] = { }; + s32 results_min[4] = { }; + s32 poll_results[4] = { }; + + u16 *rssical_radio_regs = NULL; + u16 *rssical_phy_regs = NULL; + + u16 r; /* routing */ + u8 rx_core_state; + int core, i, j, vcm; + + if (dev->phy.rev >= 7) { + regs_to_store = regs_to_store_rev7; + regs_amount = ARRAY_SIZE(regs_to_store_rev7); + } else { + regs_to_store = regs_to_store_rev3; + regs_amount = ARRAY_SIZE(regs_to_store_rev3); + } + BUG_ON(regs_amount > ARRAY_SIZE(saved_regs_phy)); + + class = b43_nphy_classifier(dev, 0, 0); + b43_nphy_classifier(dev, 7, 4); + b43_nphy_read_clip_detection(dev, clip_state); + b43_nphy_write_clip_detection(dev, clip_off); + + saved_regs_phy_rfctl[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); + saved_regs_phy_rfctl[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); + for (i = 0; i < regs_amount; i++) + saved_regs_phy[i] = b43_phy_read(dev, regs_to_store[i]); + + b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_OFF, 0, 7); + b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 1, 7); + + if (dev->phy.rev >= 7) { + b43_nphy_rf_ctl_override_one_to_many(dev, + N_RF_CTL_OVER_CMD_RXRF_PU, + 0, 0, false); + b43_nphy_rf_ctl_override_one_to_many(dev, + N_RF_CTL_OVER_CMD_RX_PU, + 1, 0, false); + b43_nphy_rf_ctl_override_rev7(dev, 0x80, 1, 0, false, 0); + b43_nphy_rf_ctl_override_rev7(dev, 0x40, 1, 0, false, 0); + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_nphy_rf_ctl_override_rev7(dev, 0x20, 0, 0, false, + 0); + b43_nphy_rf_ctl_override_rev7(dev, 0x10, 1, 0, false, + 0); + } else { + b43_nphy_rf_ctl_override_rev7(dev, 0x10, 0, 0, false, + 0); + b43_nphy_rf_ctl_override_rev7(dev, 0x20, 1, 0, false, + 0); + } + } else { + b43_nphy_rf_ctl_override(dev, 0x1, 0, 0, false); + b43_nphy_rf_ctl_override(dev, 0x2, 1, 0, false); + b43_nphy_rf_ctl_override(dev, 0x80, 1, 0, false); + b43_nphy_rf_ctl_override(dev, 0x40, 1, 0, false); + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_nphy_rf_ctl_override(dev, 0x20, 0, 0, false); + b43_nphy_rf_ctl_override(dev, 0x10, 1, 0, false); + } else { + b43_nphy_rf_ctl_override(dev, 0x10, 0, 0, false); + b43_nphy_rf_ctl_override(dev, 0x20, 1, 0, false); + } + } + + rx_core_state = b43_nphy_get_rx_core_state(dev); + for (core = 0; core < 2; core++) { + if (!(rx_core_state & (1 << core))) + continue; + r = core ? B2056_RX1 : B2056_RX0; + b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, N_RAIL_I, + N_RSSI_NB); + b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, N_RAIL_Q, + N_RSSI_NB); + + /* Grab RSSI results for every possible VCM */ + for (vcm = 0; vcm < 8; vcm++) { + if (dev->phy.rev >= 7) + b43_radio_maskset(dev, + core ? R2057_NB_MASTER_CORE1 : + R2057_NB_MASTER_CORE0, + ~R2057_VCM_MASK, vcm); + else + b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, + 0xE3, vcm << 2); + b43_nphy_poll_rssi(dev, N_RSSI_NB, results[vcm], 8); + } + + /* Find out which VCM got the best results */ + for (i = 0; i < 4; i += 2) { + s32 currd; + s32 mind = 0x100000; + s32 minpoll = 249; + u8 minvcm = 0; + if (2 * core != i) + continue; + for (vcm = 0; vcm < 8; vcm++) { + currd = results[vcm][i] * results[vcm][i] + + results[vcm][i + 1] * results[vcm][i]; + if (currd < mind) { + mind = currd; + minvcm = vcm; + } + if (results[vcm][i] < minpoll) + minpoll = results[vcm][i]; + } + vcm_final = minvcm; + results_min[i] = minpoll; + } + + /* Select the best VCM */ + if (dev->phy.rev >= 7) + b43_radio_maskset(dev, + core ? R2057_NB_MASTER_CORE1 : + R2057_NB_MASTER_CORE0, + ~R2057_VCM_MASK, vcm); + else + b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, + 0xE3, vcm_final << 2); + + for (i = 0; i < 4; i++) { + if (core != i / 2) + continue; + offset[i] = -results[vcm_final][i]; + if (offset[i] < 0) + offset[i] = -((abs(offset[i]) + 4) / 8); + else + offset[i] = (offset[i] + 4) / 8; + if (results_min[i] == 248) + offset[i] = -32; + b43_nphy_scale_offset_rssi(dev, 0, offset[i], + (i / 2 == 0) ? 1 : 2, + (i % 2 == 0) ? N_RAIL_I : N_RAIL_Q, + N_RSSI_NB); + } + } + + for (core = 0; core < 2; core++) { + if (!(rx_core_state & (1 << core))) + continue; + for (i = 0; i < 2; i++) { + b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, + N_RAIL_I, i); + b43_nphy_scale_offset_rssi(dev, 0, 0, core + 1, + N_RAIL_Q, i); + b43_nphy_poll_rssi(dev, i, poll_results, 8); + for (j = 0; j < 4; j++) { + if (j / 2 == core) { + offset[j] = 232 - poll_results[j]; + if (offset[j] < 0) + offset[j] = -(abs(offset[j] + 4) / 8); + else + offset[j] = (offset[j] + 4) / 8; + b43_nphy_scale_offset_rssi(dev, 0, + offset[2 * core], core + 1, j % 2, i); + } + } + } + } + + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, saved_regs_phy_rfctl[0]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, saved_regs_phy_rfctl[1]); + + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + + b43_phy_set(dev, B43_NPHY_TXF_40CO_B1S1, 0x1); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_START); + b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1, ~0x1); + + b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1); + b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_RXTX); + b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~0x1); + + for (i = 0; i < regs_amount; i++) + b43_phy_write(dev, regs_to_store[i], saved_regs_phy[i]); + + /* Store for future configuration */ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_2G; + rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_2G; + } else { + rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_5G; + rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G; + } + if (dev->phy.rev >= 7) { + rssical_radio_regs[0] = b43_radio_read(dev, + R2057_NB_MASTER_CORE0); + rssical_radio_regs[1] = b43_radio_read(dev, + R2057_NB_MASTER_CORE1); + } else { + rssical_radio_regs[0] = b43_radio_read(dev, B2056_RX0 | + B2056_RX_RSSI_MISC); + rssical_radio_regs[1] = b43_radio_read(dev, B2056_RX1 | + B2056_RX_RSSI_MISC); + } + rssical_phy_regs[0] = b43_phy_read(dev, B43_NPHY_RSSIMC_0I_RSSI_Z); + rssical_phy_regs[1] = b43_phy_read(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z); + rssical_phy_regs[2] = b43_phy_read(dev, B43_NPHY_RSSIMC_1I_RSSI_Z); + rssical_phy_regs[3] = b43_phy_read(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z); + rssical_phy_regs[4] = b43_phy_read(dev, B43_NPHY_RSSIMC_0I_RSSI_X); + rssical_phy_regs[5] = b43_phy_read(dev, B43_NPHY_RSSIMC_0Q_RSSI_X); + rssical_phy_regs[6] = b43_phy_read(dev, B43_NPHY_RSSIMC_1I_RSSI_X); + rssical_phy_regs[7] = b43_phy_read(dev, B43_NPHY_RSSIMC_1Q_RSSI_X); + rssical_phy_regs[8] = b43_phy_read(dev, B43_NPHY_RSSIMC_0I_RSSI_Y); + rssical_phy_regs[9] = b43_phy_read(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y); + rssical_phy_regs[10] = b43_phy_read(dev, B43_NPHY_RSSIMC_1I_RSSI_Y); + rssical_phy_regs[11] = b43_phy_read(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y); + + /* Remember for which channel we store configuration */ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + nphy->rssical_chanspec_2G.center_freq = phy->chandef->chan->center_freq; + else + nphy->rssical_chanspec_5G.center_freq = phy->chandef->chan->center_freq; + + /* End of calibration, restore configuration */ + b43_nphy_classifier(dev, 7, class); + b43_nphy_write_clip_detection(dev, clip_state); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal */ +static void b43_nphy_rev2_rssi_cal(struct b43_wldev *dev, enum n_rssi_type type) +{ + int i, j, vcm; + u8 state[4]; + u8 code, val; + u16 class, override; + u8 regs_save_radio[2]; + u16 regs_save_phy[2]; + + s32 offset[4]; + u8 core; + u8 rail; + + u16 clip_state[2]; + u16 clip_off[2] = { 0xFFFF, 0xFFFF }; + s32 results_min[4] = { }; + u8 vcm_final[4] = { }; + s32 results[4][4] = { }; + s32 miniq[4][2] = { }; + + if (type == N_RSSI_NB) { + code = 0; + val = 6; + } else if (type == N_RSSI_W1 || type == N_RSSI_W2) { + code = 25; + val = 4; + } else { + B43_WARN_ON(1); + return; + } + + class = b43_nphy_classifier(dev, 0, 0); + b43_nphy_classifier(dev, 7, 4); + b43_nphy_read_clip_detection(dev, clip_state); + b43_nphy_write_clip_detection(dev, clip_off); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + override = 0x140; + else + override = 0x110; + + regs_save_phy[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); + regs_save_radio[0] = b43_radio_read(dev, B2055_C1_PD_RXTX); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, override); + b43_radio_write(dev, B2055_C1_PD_RXTX, val); + + regs_save_phy[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); + regs_save_radio[1] = b43_radio_read(dev, B2055_C2_PD_RXTX); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, override); + b43_radio_write(dev, B2055_C2_PD_RXTX, val); + + state[0] = b43_radio_read(dev, B2055_C1_PD_RSSIMISC) & 0x07; + state[1] = b43_radio_read(dev, B2055_C2_PD_RSSIMISC) & 0x07; + b43_radio_mask(dev, B2055_C1_PD_RSSIMISC, 0xF8); + b43_radio_mask(dev, B2055_C2_PD_RSSIMISC, 0xF8); + state[2] = b43_radio_read(dev, B2055_C1_SP_RSSI) & 0x07; + state[3] = b43_radio_read(dev, B2055_C2_SP_RSSI) & 0x07; + + b43_nphy_rssi_select(dev, 5, type); + b43_nphy_scale_offset_rssi(dev, 0, 0, 5, N_RAIL_I, type); + b43_nphy_scale_offset_rssi(dev, 0, 0, 5, N_RAIL_Q, type); + + for (vcm = 0; vcm < 4; vcm++) { + u8 tmp[4]; + for (j = 0; j < 4; j++) + tmp[j] = vcm; + if (type != N_RSSI_W2) + b43_nphy_set_rssi_2055_vcm(dev, type, tmp); + b43_nphy_poll_rssi(dev, type, results[vcm], 8); + if (type == N_RSSI_W1 || type == N_RSSI_W2) + for (j = 0; j < 2; j++) + miniq[vcm][j] = min(results[vcm][2 * j], + results[vcm][2 * j + 1]); + } + + for (i = 0; i < 4; i++) { + s32 mind = 0x100000; + u8 minvcm = 0; + s32 minpoll = 249; + s32 currd; + for (vcm = 0; vcm < 4; vcm++) { + if (type == N_RSSI_NB) + currd = abs(results[vcm][i] - code * 8); + else + currd = abs(miniq[vcm][i / 2] - code * 8); + + if (currd < mind) { + mind = currd; + minvcm = vcm; + } + + if (results[vcm][i] < minpoll) + minpoll = results[vcm][i]; + } + results_min[i] = minpoll; + vcm_final[i] = minvcm; + } + + if (type != N_RSSI_W2) + b43_nphy_set_rssi_2055_vcm(dev, type, vcm_final); + + for (i = 0; i < 4; i++) { + offset[i] = (code * 8) - results[vcm_final[i]][i]; + + if (offset[i] < 0) + offset[i] = -((abs(offset[i]) + 4) / 8); + else + offset[i] = (offset[i] + 4) / 8; + + if (results_min[i] == 248) + offset[i] = code - 32; + + core = (i / 2) ? 2 : 1; + rail = (i % 2) ? N_RAIL_Q : N_RAIL_I; + + b43_nphy_scale_offset_rssi(dev, 0, offset[i], core, rail, + type); + } + + b43_radio_maskset(dev, B2055_C1_PD_RSSIMISC, 0xF8, state[0]); + b43_radio_maskset(dev, B2055_C2_PD_RSSIMISC, 0xF8, state[1]); + + switch (state[2]) { + case 1: + b43_nphy_rssi_select(dev, 1, N_RSSI_NB); + break; + case 4: + b43_nphy_rssi_select(dev, 1, N_RSSI_W1); + break; + case 2: + b43_nphy_rssi_select(dev, 1, N_RSSI_W2); + break; + default: + b43_nphy_rssi_select(dev, 1, N_RSSI_W2); + break; + } + + switch (state[3]) { + case 1: + b43_nphy_rssi_select(dev, 2, N_RSSI_NB); + break; + case 4: + b43_nphy_rssi_select(dev, 2, N_RSSI_W1); + break; + default: + b43_nphy_rssi_select(dev, 2, N_RSSI_W2); + break; + } + + b43_nphy_rssi_select(dev, 0, type); + + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs_save_phy[0]); + b43_radio_write(dev, B2055_C1_PD_RXTX, regs_save_radio[0]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs_save_phy[1]); + b43_radio_write(dev, B2055_C2_PD_RXTX, regs_save_radio[1]); + + b43_nphy_classifier(dev, 7, class); + b43_nphy_write_clip_detection(dev, clip_state); + /* Specs don't say about reset here, but it makes wl and b43 dumps + identical, it really seems wl performs this */ + b43_nphy_reset_cca(dev); +} + +/* + * RSSI Calibration + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal + */ +static void b43_nphy_rssi_cal(struct b43_wldev *dev) +{ + if (dev->phy.rev >= 19) { + /* TODO */ + } else if (dev->phy.rev >= 3) { + b43_nphy_rev3_rssi_cal(dev); + } else { + b43_nphy_rev2_rssi_cal(dev, N_RSSI_NB); + b43_nphy_rev2_rssi_cal(dev, N_RSSI_W1); + b43_nphy_rev2_rssi_cal(dev, N_RSSI_W2); + } +} + +/************************************************** + * Workarounds + **************************************************/ + +static void b43_nphy_gain_ctl_workarounds_rev19(struct b43_wldev *dev) +{ + /* TODO */ +} + +static void b43_nphy_gain_ctl_workarounds_rev7(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + switch (phy->rev) { + /* TODO */ + } +} + +static void b43_nphy_gain_ctl_workarounds_rev3(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + + bool ghz5; + bool ext_lna; + u16 rssi_gain; + struct nphy_gain_ctl_workaround_entry *e; + u8 lpf_gain[6] = { 0x00, 0x06, 0x0C, 0x12, 0x12, 0x12 }; + u8 lpf_bits[6] = { 0, 1, 2, 3, 3, 3 }; + + /* Prepare values */ + ghz5 = b43_phy_read(dev, B43_NPHY_BANDCTL) + & B43_NPHY_BANDCTL_5GHZ; + ext_lna = ghz5 ? sprom->boardflags_hi & B43_BFH_EXTLNA_5GHZ : + sprom->boardflags_lo & B43_BFL_EXTLNA; + e = b43_nphy_get_gain_ctl_workaround_ent(dev, ghz5, ext_lna); + if (ghz5 && dev->phy.rev >= 5) + rssi_gain = 0x90; + else + rssi_gain = 0x50; + + b43_phy_set(dev, B43_NPHY_RXCTL, 0x0040); + + /* Set Clip 2 detect */ + b43_phy_set(dev, B43_NPHY_C1_CGAINI, B43_NPHY_C1_CGAINI_CL2DETECT); + b43_phy_set(dev, B43_NPHY_C2_CGAINI, B43_NPHY_C2_CGAINI_CL2DETECT); + + b43_radio_write(dev, B2056_RX0 | B2056_RX_BIASPOLE_LNAG1_IDAC, + 0x17); + b43_radio_write(dev, B2056_RX1 | B2056_RX_BIASPOLE_LNAG1_IDAC, + 0x17); + b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAG2_IDAC, 0xF0); + b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAG2_IDAC, 0xF0); + b43_radio_write(dev, B2056_RX0 | B2056_RX_RSSI_POLE, 0x00); + b43_radio_write(dev, B2056_RX1 | B2056_RX_RSSI_POLE, 0x00); + b43_radio_write(dev, B2056_RX0 | B2056_RX_RSSI_GAIN, + rssi_gain); + b43_radio_write(dev, B2056_RX1 | B2056_RX_RSSI_GAIN, + rssi_gain); + b43_radio_write(dev, B2056_RX0 | B2056_RX_BIASPOLE_LNAA1_IDAC, + 0x17); + b43_radio_write(dev, B2056_RX1 | B2056_RX_BIASPOLE_LNAA1_IDAC, + 0x17); + b43_radio_write(dev, B2056_RX0 | B2056_RX_LNAA2_IDAC, 0xFF); + b43_radio_write(dev, B2056_RX1 | B2056_RX_LNAA2_IDAC, 0xFF); + + b43_ntab_write_bulk(dev, B43_NTAB8(0, 8), 4, e->lna1_gain); + b43_ntab_write_bulk(dev, B43_NTAB8(1, 8), 4, e->lna1_gain); + b43_ntab_write_bulk(dev, B43_NTAB8(0, 16), 4, e->lna2_gain); + b43_ntab_write_bulk(dev, B43_NTAB8(1, 16), 4, e->lna2_gain); + b43_ntab_write_bulk(dev, B43_NTAB8(0, 32), 10, e->gain_db); + b43_ntab_write_bulk(dev, B43_NTAB8(1, 32), 10, e->gain_db); + b43_ntab_write_bulk(dev, B43_NTAB8(2, 32), 10, e->gain_bits); + b43_ntab_write_bulk(dev, B43_NTAB8(3, 32), 10, e->gain_bits); + b43_ntab_write_bulk(dev, B43_NTAB8(0, 0x40), 6, lpf_gain); + b43_ntab_write_bulk(dev, B43_NTAB8(1, 0x40), 6, lpf_gain); + b43_ntab_write_bulk(dev, B43_NTAB8(2, 0x40), 6, lpf_bits); + b43_ntab_write_bulk(dev, B43_NTAB8(3, 0x40), 6, lpf_bits); + + b43_phy_write(dev, B43_NPHY_REV3_C1_INITGAIN_A, e->init_gain); + b43_phy_write(dev, B43_NPHY_REV3_C2_INITGAIN_A, e->init_gain); + + b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x106), 2, + e->rfseq_init); + + b43_phy_write(dev, B43_NPHY_REV3_C1_CLIP_HIGAIN_A, e->cliphi_gain); + b43_phy_write(dev, B43_NPHY_REV3_C2_CLIP_HIGAIN_A, e->cliphi_gain); + b43_phy_write(dev, B43_NPHY_REV3_C1_CLIP_MEDGAIN_A, e->clipmd_gain); + b43_phy_write(dev, B43_NPHY_REV3_C2_CLIP_MEDGAIN_A, e->clipmd_gain); + b43_phy_write(dev, B43_NPHY_REV3_C1_CLIP_LOGAIN_A, e->cliplo_gain); + b43_phy_write(dev, B43_NPHY_REV3_C2_CLIP_LOGAIN_A, e->cliplo_gain); + + b43_phy_maskset(dev, B43_NPHY_CRSMINPOWER0, 0xFF00, e->crsmin); + b43_phy_maskset(dev, B43_NPHY_CRSMINPOWERL0, 0xFF00, e->crsminl); + b43_phy_maskset(dev, B43_NPHY_CRSMINPOWERU0, 0xFF00, e->crsminu); + b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, e->nbclip); + b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, e->nbclip); + b43_phy_maskset(dev, B43_NPHY_C1_CLIPWBTHRES, + ~B43_NPHY_C1_CLIPWBTHRES_CLIP2, e->wlclip); + b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES, + ~B43_NPHY_C2_CLIPWBTHRES_CLIP2, e->wlclip); + b43_phy_write(dev, B43_NPHY_CCK_SHIFTB_REF, 0x809C); +} + +static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u8 i, j; + u8 code; + u16 tmp; + u8 rfseq_events[3] = { 6, 8, 7 }; + u8 rfseq_delays[3] = { 10, 30, 1 }; + + /* Set Clip 2 detect */ + b43_phy_set(dev, B43_NPHY_C1_CGAINI, B43_NPHY_C1_CGAINI_CL2DETECT); + b43_phy_set(dev, B43_NPHY_C2_CGAINI, B43_NPHY_C2_CGAINI_CL2DETECT); + + /* Set narrowband clip threshold */ + b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, 0x84); + b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, 0x84); + + if (!b43_is_40mhz(dev)) { + /* Set dwell lengths */ + b43_phy_write(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 0x002B); + b43_phy_write(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 0x002B); + b43_phy_write(dev, B43_NPHY_W1CLIP1_DWELL_LEN, 0x0009); + b43_phy_write(dev, B43_NPHY_W1CLIP2_DWELL_LEN, 0x0009); + } + + /* Set wideband clip 2 threshold */ + b43_phy_maskset(dev, B43_NPHY_C1_CLIPWBTHRES, + ~B43_NPHY_C1_CLIPWBTHRES_CLIP2, 21); + b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES, + ~B43_NPHY_C2_CLIPWBTHRES_CLIP2, 21); + + if (!b43_is_40mhz(dev)) { + b43_phy_maskset(dev, B43_NPHY_C1_CGAINI, + ~B43_NPHY_C1_CGAINI_GAINBKOFF, 0x1); + b43_phy_maskset(dev, B43_NPHY_C2_CGAINI, + ~B43_NPHY_C2_CGAINI_GAINBKOFF, 0x1); + b43_phy_maskset(dev, B43_NPHY_C1_CCK_CGAINI, + ~B43_NPHY_C1_CCK_CGAINI_GAINBKOFF, 0x1); + b43_phy_maskset(dev, B43_NPHY_C2_CCK_CGAINI, + ~B43_NPHY_C2_CCK_CGAINI_GAINBKOFF, 0x1); + } + + b43_phy_write(dev, B43_NPHY_CCK_SHIFTB_REF, 0x809C); + + if (nphy->gain_boost) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ && + b43_is_40mhz(dev)) + code = 4; + else + code = 5; + } else { + code = b43_is_40mhz(dev) ? 6 : 7; + } + + /* Set HPVGA2 index */ + b43_phy_maskset(dev, B43_NPHY_C1_INITGAIN, ~B43_NPHY_C1_INITGAIN_HPVGA2, + code << B43_NPHY_C1_INITGAIN_HPVGA2_SHIFT); + b43_phy_maskset(dev, B43_NPHY_C2_INITGAIN, ~B43_NPHY_C2_INITGAIN_HPVGA2, + code << B43_NPHY_C2_INITGAIN_HPVGA2_SHIFT); + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D06); + /* specs say about 2 loops, but wl does 4 */ + for (i = 0; i < 4; i++) + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, (code << 8 | 0x7C)); + + b43_nphy_adjust_lna_gain_table(dev); + + if (nphy->elna_gain_config) { + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x0808); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x0C08); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1); + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D06); + /* specs say about 2 loops, but wl does 4 */ + for (i = 0; i < 4; i++) + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + (code << 8 | 0x74)); + } + + if (dev->phy.rev == 2) { + for (i = 0; i < 4; i++) { + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, + (0x0400 * i) + 0x0020); + for (j = 0; j < 21; j++) { + tmp = j * (i < 2 ? 3 : 1); + b43_phy_write(dev, + B43_NPHY_TABLE_DATALO, tmp); + } + } + } + + b43_nphy_set_rf_sequence(dev, 5, rfseq_events, rfseq_delays, 3); + b43_phy_maskset(dev, B43_NPHY_OVER_DGAIN1, + ~B43_NPHY_OVER_DGAIN_CCKDGECV & 0xFFFF, + 0x5A << B43_NPHY_OVER_DGAIN_CCKDGECV_SHIFT); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_phy_maskset(dev, B43_PHY_N(0xC5D), 0xFF80, 4); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/WorkaroundsGainCtrl */ +static void b43_nphy_gain_ctl_workarounds(struct b43_wldev *dev) +{ + if (dev->phy.rev >= 19) + b43_nphy_gain_ctl_workarounds_rev19(dev); + else if (dev->phy.rev >= 7) + b43_nphy_gain_ctl_workarounds_rev7(dev); + else if (dev->phy.rev >= 3) + b43_nphy_gain_ctl_workarounds_rev3(dev); + else + b43_nphy_gain_ctl_workarounds_rev1_2(dev); +} + +static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + struct b43_phy *phy = &dev->phy; + + /* TX to RX */ + u8 tx2rx_events[7] = { 4, 3, 5, 2, 1, 8, 31, }; + u8 tx2rx_delays[7] = { 8, 4, 4, 4, 4, 6, 1, }; + /* RX to TX */ + u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3, + 0x1F }; + u8 rx2tx_delays_ipa[9] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 }; + + static const u16 ntab7_15e_16e[] = { 0, 0x10f, 0x10f }; + u8 ntab7_138_146[] = { 0x11, 0x11 }; + u8 ntab7_133[] = { 0x77, 0x11, 0x11 }; + + u16 lpf_ofdm_20mhz[2], lpf_ofdm_40mhz[2], lpf_11b[2]; + u16 bcap_val; + s16 bcap_val_11b[2], bcap_val_11n_20[2], bcap_val_11n_40[2]; + u16 scap_val; + s16 scap_val_11b[2], scap_val_11n_20[2], scap_val_11n_40[2]; + bool rccal_ovrd = false; + + u16 bias, conv, filt; + + u32 noise_tbl[2]; + + u32 tmp32; + u8 core; + + b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x0125); + b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x01b3); + b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x0105); + b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x016e); + b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0x00cd); + b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x0020); + + if (phy->rev == 7) { + b43_phy_set(dev, B43_NPHY_FINERX2_CGC, 0x10); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN0, 0xFF80, 0x0020); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN0, 0x80FF, 0x2700); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN1, 0xFF80, 0x002E); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN1, 0x80FF, 0x3300); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN2, 0xFF80, 0x0037); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN2, 0x80FF, 0x3A00); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN3, 0xFF80, 0x003C); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN3, 0x80FF, 0x3E00); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN4, 0xFF80, 0x003E); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN4, 0x80FF, 0x3F00); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN5, 0xFF80, 0x0040); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN5, 0x80FF, 0x4000); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN6, 0xFF80, 0x0040); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN6, 0x80FF, 0x4000); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN7, 0xFF80, 0x0040); + b43_phy_maskset(dev, B43_NPHY_FREQGAIN7, 0x80FF, 0x4000); + } + + if (phy->rev >= 16) { + b43_phy_write(dev, B43_NPHY_FORCEFRONT0, 0x7ff); + b43_phy_write(dev, B43_NPHY_FORCEFRONT1, 0x7ff); + } else if (phy->rev <= 8) { + b43_phy_write(dev, B43_NPHY_FORCEFRONT0, 0x1B0); + b43_phy_write(dev, B43_NPHY_FORCEFRONT1, 0x1B0); + } + + if (phy->rev >= 16) + b43_phy_maskset(dev, B43_NPHY_TXTAILCNT, ~0xFF, 0xa0); + else if (phy->rev >= 8) + b43_phy_maskset(dev, B43_NPHY_TXTAILCNT, ~0xFF, 0x72); + + b43_ntab_write(dev, B43_NTAB16(8, 0x00), 2); + b43_ntab_write(dev, B43_NTAB16(8, 0x10), 2); + tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0)); + tmp32 &= 0xffffff; + b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32); + b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x15d), 3, ntab7_15e_16e); + b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x16d), 3, ntab7_15e_16e); + + b43_nphy_set_rf_sequence(dev, 1, tx2rx_events, tx2rx_delays, + ARRAY_SIZE(tx2rx_events)); + if (b43_nphy_ipa(dev)) + b43_nphy_set_rf_sequence(dev, 0, rx2tx_events_ipa, + rx2tx_delays_ipa, ARRAY_SIZE(rx2tx_events_ipa)); + + b43_phy_maskset(dev, B43_NPHY_EPS_OVERRIDEI_0, 0x3FFF, 0x4000); + b43_phy_maskset(dev, B43_NPHY_EPS_OVERRIDEI_1, 0x3FFF, 0x4000); + + for (core = 0; core < 2; core++) { + lpf_ofdm_20mhz[core] = b43_nphy_read_lpf_ctl(dev, 0x154 + core * 0x10); + lpf_ofdm_40mhz[core] = b43_nphy_read_lpf_ctl(dev, 0x159 + core * 0x10); + lpf_11b[core] = b43_nphy_read_lpf_ctl(dev, 0x152 + core * 0x10); + } + + bcap_val = b43_radio_read(dev, R2057_RCCAL_BCAP_VAL); + scap_val = b43_radio_read(dev, R2057_RCCAL_SCAP_VAL); + + if (b43_nphy_ipa(dev)) { + bool ghz2 = b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ; + + switch (phy->radio_rev) { + case 5: + /* Check radio version (to be 0) by PHY rev for now */ + if (phy->rev == 8 && b43_is_40mhz(dev)) { + for (core = 0; core < 2; core++) { + scap_val_11b[core] = scap_val; + bcap_val_11b[core] = bcap_val; + scap_val_11n_20[core] = scap_val; + bcap_val_11n_20[core] = bcap_val; + scap_val_11n_40[core] = 0xc; + bcap_val_11n_40[core] = 0xc; + } + + rccal_ovrd = true; + } + if (phy->rev == 9) { + /* TODO: Radio version 1 (e.g. BCM5357B0) */ + } + break; + case 7: + case 8: + for (core = 0; core < 2; core++) { + scap_val_11b[core] = scap_val; + bcap_val_11b[core] = bcap_val; + lpf_ofdm_20mhz[core] = 4; + lpf_11b[core] = 1; + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + scap_val_11n_20[core] = 0xc; + bcap_val_11n_20[core] = 0xc; + scap_val_11n_40[core] = 0xa; + bcap_val_11n_40[core] = 0xa; + } else { + scap_val_11n_20[core] = 0x14; + bcap_val_11n_20[core] = 0x14; + scap_val_11n_40[core] = 0xf; + bcap_val_11n_40[core] = 0xf; + } + } + + rccal_ovrd = true; + break; + case 9: + for (core = 0; core < 2; core++) { + bcap_val_11b[core] = bcap_val; + scap_val_11b[core] = scap_val; + lpf_11b[core] = 1; + + if (ghz2) { + bcap_val_11n_20[core] = bcap_val + 13; + scap_val_11n_20[core] = scap_val + 15; + } else { + bcap_val_11n_20[core] = bcap_val + 14; + scap_val_11n_20[core] = scap_val + 15; + } + lpf_ofdm_20mhz[core] = 4; + + if (ghz2) { + bcap_val_11n_40[core] = bcap_val - 7; + scap_val_11n_40[core] = scap_val - 5; + } else { + bcap_val_11n_40[core] = bcap_val + 2; + scap_val_11n_40[core] = scap_val + 4; + } + lpf_ofdm_40mhz[core] = 4; + } + + rccal_ovrd = true; + break; + case 14: + for (core = 0; core < 2; core++) { + bcap_val_11b[core] = bcap_val; + scap_val_11b[core] = scap_val; + lpf_11b[core] = 1; + } + + bcap_val_11n_20[0] = bcap_val + 20; + scap_val_11n_20[0] = scap_val + 20; + lpf_ofdm_20mhz[0] = 3; + + bcap_val_11n_20[1] = bcap_val + 16; + scap_val_11n_20[1] = scap_val + 16; + lpf_ofdm_20mhz[1] = 3; + + bcap_val_11n_40[0] = bcap_val + 20; + scap_val_11n_40[0] = scap_val + 20; + lpf_ofdm_40mhz[0] = 4; + + bcap_val_11n_40[1] = bcap_val + 10; + scap_val_11n_40[1] = scap_val + 10; + lpf_ofdm_40mhz[1] = 4; + + rccal_ovrd = true; + break; + } + } else { + if (phy->radio_rev == 5) { + for (core = 0; core < 2; core++) { + lpf_ofdm_20mhz[core] = 1; + lpf_ofdm_40mhz[core] = 3; + scap_val_11b[core] = scap_val; + bcap_val_11b[core] = bcap_val; + scap_val_11n_20[core] = 0x11; + scap_val_11n_40[core] = 0x11; + bcap_val_11n_20[core] = 0x13; + bcap_val_11n_40[core] = 0x13; + } + + rccal_ovrd = true; + } + } + if (rccal_ovrd) { + u16 rx2tx_lut_20_11b[2], rx2tx_lut_20_11n[2], rx2tx_lut_40_11n[2]; + u8 rx2tx_lut_extra = 1; + + for (core = 0; core < 2; core++) { + bcap_val_11b[core] = clamp_val(bcap_val_11b[core], 0, 0x1f); + scap_val_11b[core] = clamp_val(scap_val_11b[core], 0, 0x1f); + bcap_val_11n_20[core] = clamp_val(bcap_val_11n_20[core], 0, 0x1f); + scap_val_11n_20[core] = clamp_val(scap_val_11n_20[core], 0, 0x1f); + bcap_val_11n_40[core] = clamp_val(bcap_val_11n_40[core], 0, 0x1f); + scap_val_11n_40[core] = clamp_val(scap_val_11n_40[core], 0, 0x1f); + + rx2tx_lut_20_11b[core] = (rx2tx_lut_extra << 13) | + (bcap_val_11b[core] << 8) | + (scap_val_11b[core] << 3) | + lpf_11b[core]; + rx2tx_lut_20_11n[core] = (rx2tx_lut_extra << 13) | + (bcap_val_11n_20[core] << 8) | + (scap_val_11n_20[core] << 3) | + lpf_ofdm_20mhz[core]; + rx2tx_lut_40_11n[core] = (rx2tx_lut_extra << 13) | + (bcap_val_11n_40[core] << 8) | + (scap_val_11n_40[core] << 3) | + lpf_ofdm_40mhz[core]; + } + + for (core = 0; core < 2; core++) { + b43_ntab_write(dev, B43_NTAB16(7, 0x152 + core * 16), + rx2tx_lut_20_11b[core]); + b43_ntab_write(dev, B43_NTAB16(7, 0x153 + core * 16), + rx2tx_lut_20_11n[core]); + b43_ntab_write(dev, B43_NTAB16(7, 0x154 + core * 16), + rx2tx_lut_20_11n[core]); + b43_ntab_write(dev, B43_NTAB16(7, 0x155 + core * 16), + rx2tx_lut_40_11n[core]); + b43_ntab_write(dev, B43_NTAB16(7, 0x156 + core * 16), + rx2tx_lut_40_11n[core]); + b43_ntab_write(dev, B43_NTAB16(7, 0x157 + core * 16), + rx2tx_lut_40_11n[core]); + b43_ntab_write(dev, B43_NTAB16(7, 0x158 + core * 16), + rx2tx_lut_40_11n[core]); + b43_ntab_write(dev, B43_NTAB16(7, 0x159 + core * 16), + rx2tx_lut_40_11n[core]); + } + } + + b43_phy_write(dev, 0x32F, 0x3); + + if (phy->radio_rev == 4 || phy->radio_rev == 6) + b43_nphy_rf_ctl_override_rev7(dev, 4, 1, 3, false, 0); + + if (phy->radio_rev == 3 || phy->radio_rev == 4 || phy->radio_rev == 6) { + if (sprom->revision && + sprom->boardflags2_hi & B43_BFH2_IPALVLSHIFT_3P3) { + b43_radio_write(dev, 0x5, 0x05); + b43_radio_write(dev, 0x6, 0x30); + b43_radio_write(dev, 0x7, 0x00); + b43_radio_set(dev, 0x4f, 0x1); + b43_radio_set(dev, 0xd4, 0x1); + bias = 0x1f; + conv = 0x6f; + filt = 0xaa; + } else { + bias = 0x2b; + conv = 0x7f; + filt = 0xee; + } + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + for (core = 0; core < 2; core++) { + if (core == 0) { + b43_radio_write(dev, 0x5F, bias); + b43_radio_write(dev, 0x64, conv); + b43_radio_write(dev, 0x66, filt); + } else { + b43_radio_write(dev, 0xE8, bias); + b43_radio_write(dev, 0xE9, conv); + b43_radio_write(dev, 0xEB, filt); + } + } + } + } + + if (b43_nphy_ipa(dev)) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (phy->radio_rev == 3 || phy->radio_rev == 4 || + phy->radio_rev == 6) { + for (core = 0; core < 2; core++) { + if (core == 0) + b43_radio_write(dev, 0x51, + 0x7f); + else + b43_radio_write(dev, 0xd6, + 0x7f); + } + } + switch (phy->radio_rev) { + case 3: + for (core = 0; core < 2; core++) { + if (core == 0) { + b43_radio_write(dev, 0x64, + 0x13); + b43_radio_write(dev, 0x5F, + 0x1F); + b43_radio_write(dev, 0x66, + 0xEE); + b43_radio_write(dev, 0x59, + 0x8A); + b43_radio_write(dev, 0x80, + 0x3E); + } else { + b43_radio_write(dev, 0x69, + 0x13); + b43_radio_write(dev, 0xE8, + 0x1F); + b43_radio_write(dev, 0xEB, + 0xEE); + b43_radio_write(dev, 0xDE, + 0x8A); + b43_radio_write(dev, 0x105, + 0x3E); + } + } + break; + case 7: + case 8: + if (!b43_is_40mhz(dev)) { + b43_radio_write(dev, 0x5F, 0x14); + b43_radio_write(dev, 0xE8, 0x12); + } else { + b43_radio_write(dev, 0x5F, 0x16); + b43_radio_write(dev, 0xE8, 0x16); + } + break; + case 14: + for (core = 0; core < 2; core++) { + int o = core ? 0x85 : 0; + + b43_radio_write(dev, o + R2057_IPA2G_CASCONV_CORE0, 0x13); + b43_radio_write(dev, o + R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, 0x21); + b43_radio_write(dev, o + R2057_IPA2G_BIAS_FILTER_CORE0, 0xff); + b43_radio_write(dev, o + R2057_PAD2G_IDACS_CORE0, 0x88); + b43_radio_write(dev, o + R2057_PAD2G_TUNE_PUS_CORE0, 0x23); + b43_radio_write(dev, o + R2057_IPA2G_IMAIN_CORE0, 0x16); + b43_radio_write(dev, o + R2057_PAD_BIAS_FILTER_BWS_CORE0, 0x3e); + b43_radio_write(dev, o + R2057_BACKUP1_CORE0, 0x10); + } + break; + } + } else { + u16 freq = phy->chandef->chan->center_freq; + if ((freq >= 5180 && freq <= 5230) || + (freq >= 5745 && freq <= 5805)) { + b43_radio_write(dev, 0x7D, 0xFF); + b43_radio_write(dev, 0xFE, 0xFF); + } + } + } else { + if (phy->radio_rev != 5) { + for (core = 0; core < 2; core++) { + if (core == 0) { + b43_radio_write(dev, 0x5c, 0x61); + b43_radio_write(dev, 0x51, 0x70); + } else { + b43_radio_write(dev, 0xe1, 0x61); + b43_radio_write(dev, 0xd6, 0x70); + } + } + } + } + + if (phy->radio_rev == 4) { + b43_ntab_write(dev, B43_NTAB16(8, 0x05), 0x20); + b43_ntab_write(dev, B43_NTAB16(8, 0x15), 0x20); + for (core = 0; core < 2; core++) { + if (core == 0) { + b43_radio_write(dev, 0x1a1, 0x00); + b43_radio_write(dev, 0x1a2, 0x3f); + b43_radio_write(dev, 0x1a6, 0x3f); + } else { + b43_radio_write(dev, 0x1a7, 0x00); + b43_radio_write(dev, 0x1ab, 0x3f); + b43_radio_write(dev, 0x1ac, 0x3f); + } + } + } else { + b43_phy_set(dev, B43_NPHY_AFECTL_C1, 0x4); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x4); + b43_phy_set(dev, B43_NPHY_AFECTL_C2, 0x4); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x4); + + b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x1); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x1); + b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x1); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x1); + b43_ntab_write(dev, B43_NTAB16(8, 0x05), 0); + b43_ntab_write(dev, B43_NTAB16(8, 0x15), 0); + + b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x4); + b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x4); + b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x4); + b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x4); + } + + b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, 0x2); + + b43_ntab_write(dev, B43_NTAB32(16, 0x100), 20); + b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x138), 2, ntab7_138_146); + b43_ntab_write(dev, B43_NTAB16(7, 0x141), 0x77); + b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x133), 3, ntab7_133); + b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x146), 2, ntab7_138_146); + b43_ntab_write(dev, B43_NTAB16(7, 0x123), 0x77); + b43_ntab_write(dev, B43_NTAB16(7, 0x12A), 0x77); + + b43_ntab_read_bulk(dev, B43_NTAB32(16, 0x02), 1, noise_tbl); + noise_tbl[1] = b43_is_40mhz(dev) ? 0x14D : 0x18D; + b43_ntab_write_bulk(dev, B43_NTAB32(16, 0x02), 2, noise_tbl); + + b43_ntab_read_bulk(dev, B43_NTAB32(16, 0x7E), 1, noise_tbl); + noise_tbl[1] = b43_is_40mhz(dev) ? 0x14D : 0x18D; + b43_ntab_write_bulk(dev, B43_NTAB32(16, 0x7E), 2, noise_tbl); + + b43_nphy_gain_ctl_workarounds(dev); + + /* TODO + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, + aux_adc_vmid_rev7_core0); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, + aux_adc_vmid_rev7_core1); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0C), 4, + aux_adc_gain_rev7); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1C), 4, + aux_adc_gain_rev7); + */ +} + +static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + struct ssb_sprom *sprom = dev->dev->bus_sprom; + + /* TX to RX */ + u8 tx2rx_events[7] = { 0x4, 0x3, 0x5, 0x2, 0x1, 0x8, 0x1F }; + u8 tx2rx_delays[7] = { 8, 4, 4, 4, 4, 6, 1 }; + /* RX to TX */ + u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3, + 0x1F }; + u8 rx2tx_delays_ipa[9] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 }; + u8 rx2tx_events[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0x3, 0x4, 0x1F }; + u8 rx2tx_delays[9] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 }; + + u16 vmids[5][4] = { + { 0xa2, 0xb4, 0xb4, 0x89, }, /* 0 */ + { 0xb4, 0xb4, 0xb4, 0x24, }, /* 1 */ + { 0xa2, 0xb4, 0xb4, 0x74, }, /* 2 */ + { 0xa2, 0xb4, 0xb4, 0x270, }, /* 3 */ + { 0xa2, 0xb4, 0xb4, 0x00, }, /* 4 and 5 */ + }; + u16 gains[5][4] = { + { 0x02, 0x02, 0x02, 0x00, }, /* 0 */ + { 0x02, 0x02, 0x02, 0x02, }, /* 1 */ + { 0x02, 0x02, 0x02, 0x04, }, /* 2 */ + { 0x02, 0x02, 0x02, 0x00, }, /* 3 */ + { 0x02, 0x02, 0x02, 0x00, }, /* 4 and 5 */ + }; + u16 *vmid, *gain; + + u8 pdet_range; + u16 tmp16; + u32 tmp32; + + b43_phy_write(dev, B43_NPHY_FORCEFRONT0, 0x1f8); + b43_phy_write(dev, B43_NPHY_FORCEFRONT1, 0x1f8); + + tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0)); + tmp32 &= 0xffffff; + b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32); + + b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x0125); + b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x01B3); + b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x0105); + b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x016E); + b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0x00CD); + b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x0020); + + b43_phy_write(dev, B43_NPHY_REV3_C1_CLIP_LOGAIN_B, 0x000C); + b43_phy_write(dev, B43_NPHY_REV3_C2_CLIP_LOGAIN_B, 0x000C); + + /* TX to RX */ + b43_nphy_set_rf_sequence(dev, 1, tx2rx_events, tx2rx_delays, + ARRAY_SIZE(tx2rx_events)); + + /* RX to TX */ + if (b43_nphy_ipa(dev)) + b43_nphy_set_rf_sequence(dev, 0, rx2tx_events_ipa, + rx2tx_delays_ipa, ARRAY_SIZE(rx2tx_events_ipa)); + if (nphy->hw_phyrxchain != 3 && + nphy->hw_phyrxchain != nphy->hw_phytxchain) { + if (b43_nphy_ipa(dev)) { + rx2tx_delays[5] = 59; + rx2tx_delays[6] = 1; + rx2tx_events[7] = 0x1F; + } + b43_nphy_set_rf_sequence(dev, 0, rx2tx_events, rx2tx_delays, + ARRAY_SIZE(rx2tx_events)); + } + + tmp16 = (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) ? + 0x2 : 0x9C40; + b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, tmp16); + + b43_phy_maskset(dev, B43_NPHY_SGILTRNOFFSET, 0xF0FF, 0x0700); + + if (!b43_is_40mhz(dev)) { + b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D); + b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D); + } else { + b43_ntab_write(dev, B43_NTAB32(16, 3), 0x14D); + b43_ntab_write(dev, B43_NTAB32(16, 127), 0x14D); + } + + b43_nphy_gain_ctl_workarounds(dev); + + b43_ntab_write(dev, B43_NTAB16(8, 0), 2); + b43_ntab_write(dev, B43_NTAB16(8, 16), 2); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + pdet_range = sprom->fem.ghz2.pdet_range; + else + pdet_range = sprom->fem.ghz5.pdet_range; + vmid = vmids[min_t(u16, pdet_range, 4)]; + gain = gains[min_t(u16, pdet_range, 4)]; + switch (pdet_range) { + case 3: + if (!(dev->phy.rev >= 4 && + b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) + break; + /* FALL THROUGH */ + case 0: + case 1: + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain); + break; + case 2: + if (dev->phy.rev >= 6) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + vmid[3] = 0x94; + else + vmid[3] = 0x8e; + gain[3] = 3; + } else if (dev->phy.rev == 5) { + vmid[3] = 0x84; + gain[3] = 2; + } + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain); + break; + case 4: + case 5: + if (b43_current_band(dev->wl) != IEEE80211_BAND_2GHZ) { + if (pdet_range == 4) { + vmid[3] = 0x8e; + tmp16 = 0x96; + gain[3] = 0x2; + } else { + vmid[3] = 0x89; + tmp16 = 0x89; + gain[3] = 0; + } + } else { + if (pdet_range == 4) { + vmid[3] = 0x89; + tmp16 = 0x8b; + gain[3] = 0x2; + } else { + vmid[3] = 0x74; + tmp16 = 0x70; + gain[3] = 0; + } + } + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain); + vmid[3] = tmp16; + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain); + break; + } + + b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_MAST_BIAS, 0x00); + b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_MAST_BIAS, 0x00); + b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_MAIN, 0x06); + b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_MAIN, 0x06); + b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_AUX, 0x07); + b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_AUX, 0x07); + b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_LOB_BIAS, 0x88); + b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_LOB_BIAS, 0x88); + b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_CMFB_IDAC, 0x00); + b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_CMFB_IDAC, 0x00); + b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXG_CMFB_IDAC, 0x00); + b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXG_CMFB_IDAC, 0x00); + + /* N PHY WAR TX Chain Update with hw_phytxchain as argument */ + + if ((sprom->boardflags2_lo & B43_BFL2_APLL_WAR && + b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) || + (sprom->boardflags2_lo & B43_BFL2_GPLL_WAR && + b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) + tmp32 = 0x00088888; + else + tmp32 = 0x88888888; + b43_ntab_write(dev, B43_NTAB32(30, 1), tmp32); + b43_ntab_write(dev, B43_NTAB32(30, 2), tmp32); + b43_ntab_write(dev, B43_NTAB32(30, 3), tmp32); + + if (dev->phy.rev == 4 && + b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_radio_write(dev, B2056_TX0 | B2056_TX_GMBB_IDAC, + 0x70); + b43_radio_write(dev, B2056_TX1 | B2056_TX_GMBB_IDAC, + 0x70); + } + + /* Dropped probably-always-true condition */ + b43_phy_write(dev, B43_NPHY_ED_CRS40ASSERTTHRESH0, 0x03eb); + b43_phy_write(dev, B43_NPHY_ED_CRS40ASSERTTHRESH1, 0x03eb); + b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH0, 0x0341); + b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH1, 0x0341); + b43_phy_write(dev, B43_NPHY_ED_CRS20LASSERTTHRESH0, 0x042b); + b43_phy_write(dev, B43_NPHY_ED_CRS20LASSERTTHRESH1, 0x042b); + b43_phy_write(dev, B43_NPHY_ED_CRS20LDEASSERTTHRESH0, 0x0381); + b43_phy_write(dev, B43_NPHY_ED_CRS20LDEASSERTTHRESH1, 0x0381); + b43_phy_write(dev, B43_NPHY_ED_CRS20UASSERTTHRESH0, 0x042b); + b43_phy_write(dev, B43_NPHY_ED_CRS20UASSERTTHRESH1, 0x042b); + b43_phy_write(dev, B43_NPHY_ED_CRS20UDEASSERTTHRESH0, 0x0381); + b43_phy_write(dev, B43_NPHY_ED_CRS20UDEASSERTTHRESH1, 0x0381); + + if (dev->phy.rev >= 6 && sprom->boardflags2_lo & B43_BFL2_SINGLEANT_CCK) + ; /* TODO: 0x0080000000000000 HF */ +} + +static void b43_nphy_workarounds_rev1_2(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = phy->n; + + u8 events1[7] = { 0x0, 0x1, 0x2, 0x8, 0x4, 0x5, 0x3 }; + u8 delays1[7] = { 0x8, 0x6, 0x6, 0x2, 0x4, 0x3C, 0x1 }; + + u8 events2[7] = { 0x0, 0x3, 0x5, 0x4, 0x2, 0x1, 0x8 }; + u8 delays2[7] = { 0x8, 0x6, 0x2, 0x4, 0x4, 0x6, 0x1 }; + + if (sprom->boardflags2_lo & B43_BFL2_SKWRKFEM_BRD || + dev->dev->board_type == BCMA_BOARD_TYPE_BCM943224M93) { + delays1[0] = 0x1; + delays1[5] = 0x14; + } + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ && + nphy->band5g_pwrgain) { + b43_radio_mask(dev, B2055_C1_TX_RF_SPARE, ~0x8); + b43_radio_mask(dev, B2055_C2_TX_RF_SPARE, ~0x8); + } else { + b43_radio_set(dev, B2055_C1_TX_RF_SPARE, 0x8); + b43_radio_set(dev, B2055_C2_TX_RF_SPARE, 0x8); + } + + b43_ntab_write(dev, B43_NTAB16(8, 0x00), 0x000A); + b43_ntab_write(dev, B43_NTAB16(8, 0x10), 0x000A); + if (dev->phy.rev < 3) { + b43_ntab_write(dev, B43_NTAB16(8, 0x02), 0xCDAA); + b43_ntab_write(dev, B43_NTAB16(8, 0x12), 0xCDAA); + } + + if (dev->phy.rev < 2) { + b43_ntab_write(dev, B43_NTAB16(8, 0x08), 0x0000); + b43_ntab_write(dev, B43_NTAB16(8, 0x18), 0x0000); + b43_ntab_write(dev, B43_NTAB16(8, 0x07), 0x7AAB); + b43_ntab_write(dev, B43_NTAB16(8, 0x17), 0x7AAB); + b43_ntab_write(dev, B43_NTAB16(8, 0x06), 0x0800); + b43_ntab_write(dev, B43_NTAB16(8, 0x16), 0x0800); + } + + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301); + + b43_nphy_set_rf_sequence(dev, 0, events1, delays1, 7); + b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 7); + + b43_nphy_gain_ctl_workarounds(dev); + + if (dev->phy.rev < 2) { + if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2) + b43_hf_write(dev, b43_hf_read(dev) | + B43_HF_MLADVW); + } else if (dev->phy.rev == 2) { + b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0); + b43_phy_write(dev, B43_NPHY_CRSCHECK3, 0); + } + + if (dev->phy.rev < 2) + b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL, + ~B43_NPHY_SCRAM_SIGCTL_SCM); + + /* Set phase track alpha and beta */ + b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125); + b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3); + b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105); + b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E); + b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD); + b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20); + + if (dev->phy.rev < 3) { + b43_phy_mask(dev, B43_NPHY_PIL_DW1, + ~B43_NPHY_PIL_DW_64QAM & 0xFFFF); + b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B1, 0xB5); + b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B2, 0xA4); + b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B3, 0x00); + } + + if (dev->phy.rev == 2) + b43_phy_set(dev, B43_NPHY_FINERX2_CGC, + B43_NPHY_FINERX2_CGC_DECGC); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Workarounds */ +static void b43_nphy_workarounds(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = phy->n; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + b43_nphy_classifier(dev, 1, 0); + else + b43_nphy_classifier(dev, 1, 1); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + b43_phy_set(dev, B43_NPHY_IQFLIP, + B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2); + + /* TODO: rev19+ */ + if (dev->phy.rev >= 7) + b43_nphy_workarounds_rev7plus(dev); + else if (dev->phy.rev >= 3) + b43_nphy_workarounds_rev3plus(dev); + else + b43_nphy_workarounds_rev1_2(dev); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); +} + +/************************************************** + * Tx/Rx common + **************************************************/ + +/* + * Transmits a known value for LO calibration + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TXTone + */ +static int b43_nphy_tx_tone(struct b43_wldev *dev, u32 freq, u16 max_val, + bool iqmode, bool dac_test, bool modify_bbmult) +{ + u16 samp = b43_nphy_gen_load_samples(dev, freq, max_val, dac_test); + if (samp == 0) + return -1; + b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test, + modify_bbmult); + return 0; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Chains */ +static void b43_nphy_update_txrx_chain(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + bool override = false; + u16 chain = 0x33; + + if (nphy->txrx_chain == 0) { + chain = 0x11; + override = true; + } else if (nphy->txrx_chain == 1) { + chain = 0x22; + override = true; + } + + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, + ~(B43_NPHY_RFSEQCA_TXEN | B43_NPHY_RFSEQCA_RXEN), + chain); + + if (override) + b43_phy_set(dev, B43_NPHY_RFSEQMODE, + B43_NPHY_RFSEQMODE_CAOVER); + else + b43_phy_mask(dev, B43_NPHY_RFSEQMODE, + ~B43_NPHY_RFSEQMODE_CAOVER); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/stop-playback */ +static void b43_nphy_stop_playback(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + u16 tmp; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + tmp = b43_phy_read(dev, B43_NPHY_SAMP_STAT); + if (tmp & 0x1) + b43_phy_set(dev, B43_NPHY_SAMP_CMD, B43_NPHY_SAMP_CMD_STOP); + else if (tmp & 0x2) + b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF); + + b43_phy_mask(dev, B43_NPHY_SAMP_CMD, ~0x0004); + + if (nphy->bb_mult_save & 0x80000000) { + tmp = nphy->bb_mult_save & 0xFFFF; + b43_ntab_write(dev, B43_NTAB16(15, 87), tmp); + nphy->bb_mult_save = 0; + } + + if (phy->rev >= 7 && nphy->lpf_bw_overrode_for_sample_play) { + if (phy->rev >= 19) + b43_nphy_rf_ctl_override_rev19(dev, 0x80, 0, 0, true, + 1); + else + b43_nphy_rf_ctl_override_rev7(dev, 0x80, 0, 0, true, 1); + nphy->lpf_bw_overrode_for_sample_play = false; + } + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IqCalGainParams */ +static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core, + struct nphy_txgains target, + struct nphy_iqcal_params *params) +{ + struct b43_phy *phy = &dev->phy; + int i, j, indx; + u16 gain; + + if (dev->phy.rev >= 3) { + params->tx_lpf = target.tx_lpf[core]; /* Rev 7+ */ + params->txgm = target.txgm[core]; + params->pga = target.pga[core]; + params->pad = target.pad[core]; + params->ipa = target.ipa[core]; + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 3) | (params->ipa) | (params->tx_lpf << 15); + } else { + params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 4) | (params->ipa); + } + for (j = 0; j < 5; j++) + params->ncorr[j] = 0x79; + } else { + gain = (target.pad[core]) | (target.pga[core] << 4) | + (target.txgm[core] << 8); + + indx = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? + 1 : 0; + for (i = 0; i < 9; i++) + if (tbl_iqcal_gainparams[indx][i][0] == gain) + break; + i = min(i, 8); + + params->txgm = tbl_iqcal_gainparams[indx][i][1]; + params->pga = tbl_iqcal_gainparams[indx][i][2]; + params->pad = tbl_iqcal_gainparams[indx][i][3]; + params->cal_gain = (params->txgm << 7) | (params->pga << 4) | + (params->pad << 2); + for (j = 0; j < 4; j++) + params->ncorr[j] = tbl_iqcal_gainparams[indx][i][4 + j]; + } +} + +/************************************************** + * Tx and Rx + **************************************************/ + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlEnable */ +static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + u8 i; + u16 bmask, val, tmp; + enum ieee80211_band band = b43_current_band(dev->wl); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + nphy->txpwrctrl = enable; + if (!enable) { + if (dev->phy.rev >= 3 && + (b43_phy_read(dev, B43_NPHY_TXPCTL_CMD) & + (B43_NPHY_TXPCTL_CMD_COEFF | + B43_NPHY_TXPCTL_CMD_HWPCTLEN | + B43_NPHY_TXPCTL_CMD_PCTLEN))) { + /* We disable enabled TX pwr ctl, save it's state */ + nphy->tx_pwr_idx[0] = b43_phy_read(dev, + B43_NPHY_C1_TXPCTL_STAT) & 0x7f; + nphy->tx_pwr_idx[1] = b43_phy_read(dev, + B43_NPHY_C2_TXPCTL_STAT) & 0x7f; + } + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x6840); + for (i = 0; i < 84; i++) + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0); + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x6C40); + for (i = 0; i < 84; i++) + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0); + + tmp = B43_NPHY_TXPCTL_CMD_COEFF | B43_NPHY_TXPCTL_CMD_HWPCTLEN; + if (dev->phy.rev >= 3) + tmp |= B43_NPHY_TXPCTL_CMD_PCTLEN; + b43_phy_mask(dev, B43_NPHY_TXPCTL_CMD, ~tmp); + + if (dev->phy.rev >= 3) { + b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x0100); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0100); + } else { + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x4000); + } + + if (dev->phy.rev == 2) + b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, + ~B43_NPHY_BPHY_CTL3_SCALE, 0x53); + else if (dev->phy.rev < 2) + b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, + ~B43_NPHY_BPHY_CTL3_SCALE, 0x5A); + + if (dev->phy.rev < 2 && b43_is_40mhz(dev)) + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_TSSIRPSMW); + } else { + b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84, + nphy->adj_pwr_tbl); + b43_ntab_write_bulk(dev, B43_NTAB16(27, 64), 84, + nphy->adj_pwr_tbl); + + bmask = B43_NPHY_TXPCTL_CMD_COEFF | + B43_NPHY_TXPCTL_CMD_HWPCTLEN; + /* wl does useless check for "enable" param here */ + val = B43_NPHY_TXPCTL_CMD_COEFF | B43_NPHY_TXPCTL_CMD_HWPCTLEN; + if (dev->phy.rev >= 3) { + bmask |= B43_NPHY_TXPCTL_CMD_PCTLEN; + if (val) + val |= B43_NPHY_TXPCTL_CMD_PCTLEN; + } + b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~(bmask), val); + + if (band == IEEE80211_BAND_5GHZ) { + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, + ~B43_NPHY_TXPCTL_CMD_INIT, + 0x32); + b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, + ~B43_NPHY_TXPCTL_INIT_PIDXI1, + 0x32); + } else { + b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, + ~B43_NPHY_TXPCTL_CMD_INIT, + 0x64); + if (phy->rev > 1) + b43_phy_maskset(dev, + B43_NPHY_TXPCTL_INIT, + ~B43_NPHY_TXPCTL_INIT_PIDXI1, + 0x64); + } + } + + if (dev->phy.rev >= 3) { + if (nphy->tx_pwr_idx[0] != 128 && + nphy->tx_pwr_idx[1] != 128) { + /* Recover TX pwr ctl state */ + b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, + ~B43_NPHY_TXPCTL_CMD_INIT, + nphy->tx_pwr_idx[0]); + if (dev->phy.rev > 1) + b43_phy_maskset(dev, + B43_NPHY_TXPCTL_INIT, + ~0xff, nphy->tx_pwr_idx[1]); + } + } + + if (phy->rev >= 7) { + /* TODO */ + } + + if (dev->phy.rev >= 3) { + b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x100); + b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x100); + } else { + b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x4000); + } + + if (dev->phy.rev == 2) + b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x3b); + else if (dev->phy.rev < 2) + b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x40); + + if (dev->phy.rev < 2 && b43_is_40mhz(dev)) + b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_TSSIRPSMW); + + if (b43_nphy_ipa(dev)) { + b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x4); + b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x4); + } + } + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrFix */ +static void b43_nphy_tx_power_fix(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + struct ssb_sprom *sprom = dev->dev->bus_sprom; + + u8 txpi[2], bbmult, i; + u16 tmp, radio_gain, dac_gain; + u16 freq = phy->chandef->chan->center_freq; + u32 txgain; + /* u32 gaintbl; rev3+ */ + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + /* TODO: rev19+ */ + if (dev->phy.rev >= 7) { + txpi[0] = txpi[1] = 30; + } else if (dev->phy.rev >= 3) { + txpi[0] = 40; + txpi[1] = 40; + } else if (sprom->revision < 4) { + txpi[0] = 72; + txpi[1] = 72; + } else { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + txpi[0] = sprom->txpid2g[0]; + txpi[1] = sprom->txpid2g[1]; + } else if (freq >= 4900 && freq < 5100) { + txpi[0] = sprom->txpid5gl[0]; + txpi[1] = sprom->txpid5gl[1]; + } else if (freq >= 5100 && freq < 5500) { + txpi[0] = sprom->txpid5g[0]; + txpi[1] = sprom->txpid5g[1]; + } else if (freq >= 5500) { + txpi[0] = sprom->txpid5gh[0]; + txpi[1] = sprom->txpid5gh[1]; + } else { + txpi[0] = 91; + txpi[1] = 91; + } + } + if (dev->phy.rev < 7 && + (txpi[0] < 40 || txpi[0] > 100 || txpi[1] < 40 || txpi[1] > 100)) + txpi[0] = txpi[1] = 91; + + /* + for (i = 0; i < 2; i++) { + nphy->txpwrindex[i].index_internal = txpi[i]; + nphy->txpwrindex[i].index_internal_save = txpi[i]; + } + */ + + for (i = 0; i < 2; i++) { + const u32 *table = b43_nphy_get_tx_gain_table(dev); + + if (!table) + break; + txgain = *(table + txpi[i]); + + if (dev->phy.rev >= 3) + radio_gain = (txgain >> 16) & 0x1FFFF; + else + radio_gain = (txgain >> 16) & 0x1FFF; + + if (dev->phy.rev >= 7) + dac_gain = (txgain >> 8) & 0x7; + else + dac_gain = (txgain >> 8) & 0x3F; + bbmult = txgain & 0xFF; + + if (dev->phy.rev >= 3) { + if (i == 0) + b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x0100); + else + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0100); + } else { + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x4000); + } + + if (i == 0) + b43_phy_write(dev, B43_NPHY_AFECTL_DACGAIN1, dac_gain); + else + b43_phy_write(dev, B43_NPHY_AFECTL_DACGAIN2, dac_gain); + + b43_ntab_write(dev, B43_NTAB16(0x7, 0x110 + i), radio_gain); + + tmp = b43_ntab_read(dev, B43_NTAB16(0xF, 0x57)); + if (i == 0) + tmp = (tmp & 0x00FF) | (bbmult << 8); + else + tmp = (tmp & 0xFF00) | bbmult; + b43_ntab_write(dev, B43_NTAB16(0xF, 0x57), tmp); + + if (b43_nphy_ipa(dev)) { + u32 tmp32; + u16 reg = (i == 0) ? + B43_NPHY_PAPD_EN0 : B43_NPHY_PAPD_EN1; + tmp32 = b43_ntab_read(dev, B43_NTAB32(26 + i, + 576 + txpi[i])); + b43_phy_maskset(dev, reg, 0xE00F, (u32) tmp32 << 4); + b43_phy_set(dev, reg, 0x4); + } + } + + b43_phy_mask(dev, B43_NPHY_BPHY_CTL2, ~B43_NPHY_BPHY_CTL2_LUT); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); +} + +static void b43_nphy_ipa_internal_tssi_setup(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + u8 core; + u16 r; /* routing */ + + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + for (core = 0; core < 2; core++) { + r = core ? 0x190 : 0x170; + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_radio_write(dev, r + 0x5, 0x5); + b43_radio_write(dev, r + 0x9, 0xE); + if (phy->rev != 5) + b43_radio_write(dev, r + 0xA, 0); + if (phy->rev != 7) + b43_radio_write(dev, r + 0xB, 1); + else + b43_radio_write(dev, r + 0xB, 0x31); + } else { + b43_radio_write(dev, r + 0x5, 0x9); + b43_radio_write(dev, r + 0x9, 0xC); + b43_radio_write(dev, r + 0xB, 0x0); + if (phy->rev != 5) + b43_radio_write(dev, r + 0xA, 1); + else + b43_radio_write(dev, r + 0xA, 0x31); + } + b43_radio_write(dev, r + 0x6, 0); + b43_radio_write(dev, r + 0x7, 0); + b43_radio_write(dev, r + 0x8, 3); + b43_radio_write(dev, r + 0xC, 0); + } + } else { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_radio_write(dev, B2056_SYN_RESERVED_ADDR31, 0x128); + else + b43_radio_write(dev, B2056_SYN_RESERVED_ADDR31, 0x80); + b43_radio_write(dev, B2056_SYN_RESERVED_ADDR30, 0); + b43_radio_write(dev, B2056_SYN_GPIO_MASTER1, 0x29); + + for (core = 0; core < 2; core++) { + r = core ? B2056_TX1 : B2056_TX0; + + b43_radio_write(dev, r | B2056_TX_IQCAL_VCM_HG, 0); + b43_radio_write(dev, r | B2056_TX_IQCAL_IDAC, 0); + b43_radio_write(dev, r | B2056_TX_TSSI_VCM, 3); + b43_radio_write(dev, r | B2056_TX_TX_AMP_DET, 0); + b43_radio_write(dev, r | B2056_TX_TSSI_MISC1, 8); + b43_radio_write(dev, r | B2056_TX_TSSI_MISC2, 0); + b43_radio_write(dev, r | B2056_TX_TSSI_MISC3, 0); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_radio_write(dev, r | B2056_TX_TX_SSI_MASTER, + 0x5); + if (phy->rev != 5) + b43_radio_write(dev, r | B2056_TX_TSSIA, + 0x00); + if (phy->rev >= 5) + b43_radio_write(dev, r | B2056_TX_TSSIG, + 0x31); + else + b43_radio_write(dev, r | B2056_TX_TSSIG, + 0x11); + b43_radio_write(dev, r | B2056_TX_TX_SSI_MUX, + 0xE); + } else { + b43_radio_write(dev, r | B2056_TX_TX_SSI_MASTER, + 0x9); + b43_radio_write(dev, r | B2056_TX_TSSIA, 0x31); + b43_radio_write(dev, r | B2056_TX_TSSIG, 0x0); + b43_radio_write(dev, r | B2056_TX_TX_SSI_MUX, + 0xC); + } + } + } +} + +/* + * Stop radio and transmit known signal. Then check received signal strength to + * get TSSI (Transmit Signal Strength Indicator). + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlIdleTssi + */ +static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + + u32 tmp; + s32 rssi[4] = { }; + + if (phy->chandef->chan->flags & IEEE80211_CHAN_NO_IR) + return; + + if (b43_nphy_ipa(dev)) + b43_nphy_ipa_internal_tssi_setup(dev); + + if (phy->rev >= 19) + b43_nphy_rf_ctl_override_rev19(dev, 0x1000, 0, 3, false, 0); + else if (phy->rev >= 7) + b43_nphy_rf_ctl_override_rev7(dev, 0x1000, 0, 3, false, 0); + else if (phy->rev >= 3) + b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, false); + + b43_nphy_stop_playback(dev); + b43_nphy_tx_tone(dev, 4000, 0, false, false, false); + udelay(20); + tmp = b43_nphy_poll_rssi(dev, N_RSSI_TSSI_2G, rssi, 1); + b43_nphy_stop_playback(dev); + + b43_nphy_rssi_select(dev, 0, N_RSSI_W1); + + if (phy->rev >= 19) + b43_nphy_rf_ctl_override_rev19(dev, 0x1000, 0, 3, true, 0); + else if (phy->rev >= 7) + b43_nphy_rf_ctl_override_rev7(dev, 0x1000, 0, 3, true, 0); + else if (phy->rev >= 3) + b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, true); + + if (phy->rev >= 19) { + /* TODO */ + return; + } else if (phy->rev >= 3) { + nphy->pwr_ctl_info[0].idle_tssi_5g = (tmp >> 24) & 0xFF; + nphy->pwr_ctl_info[1].idle_tssi_5g = (tmp >> 8) & 0xFF; + } else { + nphy->pwr_ctl_info[0].idle_tssi_5g = (tmp >> 16) & 0xFF; + nphy->pwr_ctl_info[1].idle_tssi_5g = tmp & 0xFF; + } + nphy->pwr_ctl_info[0].idle_tssi_2g = (tmp >> 24) & 0xFF; + nphy->pwr_ctl_info[1].idle_tssi_2g = (tmp >> 8) & 0xFF; +} + +/* http://bcm-v4.sipsolutions.net/PHY/N/TxPwrLimitToTbl */ +static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u8 idx, delta; + u8 i, stf_mode; + + /* Array adj_pwr_tbl corresponds to the hardware table. It consists of + * 21 groups, each containing 4 entries. + * + * First group has entries for CCK modulation. + * The rest of groups has 1 entry per modulation (SISO, CDD, STBC, SDM). + * + * Group 0 is for CCK + * Groups 1..4 use BPSK (group per coding rate) + * Groups 5..8 use QPSK (group per coding rate) + * Groups 9..12 use 16-QAM (group per coding rate) + * Groups 13..16 use 64-QAM (group per coding rate) + * Groups 17..20 are unknown + */ + + for (i = 0; i < 4; i++) + nphy->adj_pwr_tbl[i] = nphy->tx_power_offset[i]; + + for (stf_mode = 0; stf_mode < 4; stf_mode++) { + delta = 0; + switch (stf_mode) { + case 0: + if (b43_is_40mhz(dev) && dev->phy.rev >= 5) { + idx = 68; + } else { + delta = 1; + idx = b43_is_40mhz(dev) ? 52 : 4; + } + break; + case 1: + idx = b43_is_40mhz(dev) ? 76 : 28; + break; + case 2: + idx = b43_is_40mhz(dev) ? 84 : 36; + break; + case 3: + idx = b43_is_40mhz(dev) ? 92 : 44; + break; + } + + for (i = 0; i < 20; i++) { + nphy->adj_pwr_tbl[4 + 4 * i + stf_mode] = + nphy->tx_power_offset[idx]; + if (i == 0) + idx += delta; + if (i == 14) + idx += 1 - delta; + if (i == 3 || i == 4 || i == 7 || i == 8 || i == 11 || + i == 13) + idx += 1; + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlSetup */ +static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + struct ssb_sprom *sprom = dev->dev->bus_sprom; + + s16 a1[2], b0[2], b1[2]; + u8 idle[2]; + u8 ppr_max; + s8 target[2]; + s32 num, den, pwr; + u32 regval[64]; + + u16 freq = phy->chandef->chan->center_freq; + u16 tmp; + u16 r; /* routing */ + u8 i, c; + + if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) { + b43_maskset32(dev, B43_MMIO_MACCTL, ~0, 0x200000); + b43_read32(dev, B43_MMIO_MACCTL); + udelay(1); + } + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, true); + + b43_phy_set(dev, B43_NPHY_TSSIMODE, B43_NPHY_TSSIMODE_EN); + if (dev->phy.rev >= 3) + b43_phy_mask(dev, B43_NPHY_TXPCTL_CMD, + ~B43_NPHY_TXPCTL_CMD_PCTLEN & 0xFFFF); + else + b43_phy_set(dev, B43_NPHY_TXPCTL_CMD, + B43_NPHY_TXPCTL_CMD_PCTLEN); + + if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) + b43_maskset32(dev, B43_MMIO_MACCTL, ~0x200000, 0); + + if (sprom->revision < 4) { + idle[0] = nphy->pwr_ctl_info[0].idle_tssi_2g; + idle[1] = nphy->pwr_ctl_info[1].idle_tssi_2g; + target[0] = target[1] = 52; + a1[0] = a1[1] = -424; + b0[0] = b0[1] = 5612; + b1[0] = b1[1] = -1393; + } else { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + for (c = 0; c < 2; c++) { + idle[c] = nphy->pwr_ctl_info[c].idle_tssi_2g; + target[c] = sprom->core_pwr_info[c].maxpwr_2g; + a1[c] = sprom->core_pwr_info[c].pa_2g[0]; + b0[c] = sprom->core_pwr_info[c].pa_2g[1]; + b1[c] = sprom->core_pwr_info[c].pa_2g[2]; + } + } else if (freq >= 4900 && freq < 5100) { + for (c = 0; c < 2; c++) { + idle[c] = nphy->pwr_ctl_info[c].idle_tssi_5g; + target[c] = sprom->core_pwr_info[c].maxpwr_5gl; + a1[c] = sprom->core_pwr_info[c].pa_5gl[0]; + b0[c] = sprom->core_pwr_info[c].pa_5gl[1]; + b1[c] = sprom->core_pwr_info[c].pa_5gl[2]; + } + } else if (freq >= 5100 && freq < 5500) { + for (c = 0; c < 2; c++) { + idle[c] = nphy->pwr_ctl_info[c].idle_tssi_5g; + target[c] = sprom->core_pwr_info[c].maxpwr_5g; + a1[c] = sprom->core_pwr_info[c].pa_5g[0]; + b0[c] = sprom->core_pwr_info[c].pa_5g[1]; + b1[c] = sprom->core_pwr_info[c].pa_5g[2]; + } + } else if (freq >= 5500) { + for (c = 0; c < 2; c++) { + idle[c] = nphy->pwr_ctl_info[c].idle_tssi_5g; + target[c] = sprom->core_pwr_info[c].maxpwr_5gh; + a1[c] = sprom->core_pwr_info[c].pa_5gh[0]; + b0[c] = sprom->core_pwr_info[c].pa_5gh[1]; + b1[c] = sprom->core_pwr_info[c].pa_5gh[2]; + } + } else { + idle[0] = nphy->pwr_ctl_info[0].idle_tssi_5g; + idle[1] = nphy->pwr_ctl_info[1].idle_tssi_5g; + target[0] = target[1] = 52; + a1[0] = a1[1] = -424; + b0[0] = b0[1] = 5612; + b1[0] = b1[1] = -1393; + } + } + + ppr_max = b43_ppr_get_max(dev, &nphy->tx_pwr_max_ppr); + if (ppr_max) { + target[0] = ppr_max; + target[1] = ppr_max; + } + + if (dev->phy.rev >= 3) { + if (sprom->fem.ghz2.tssipos) + b43_phy_set(dev, B43_NPHY_TXPCTL_ITSSI, 0x4000); + if (dev->phy.rev >= 7) { + for (c = 0; c < 2; c++) { + r = c ? 0x190 : 0x170; + if (b43_nphy_ipa(dev)) + b43_radio_write(dev, r + 0x9, (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) ? 0xE : 0xC); + } + } else { + if (b43_nphy_ipa(dev)) { + tmp = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? 0xC : 0xE; + b43_radio_write(dev, + B2056_TX0 | B2056_TX_TX_SSI_MUX, tmp); + b43_radio_write(dev, + B2056_TX1 | B2056_TX_TX_SSI_MUX, tmp); + } else { + b43_radio_write(dev, + B2056_TX0 | B2056_TX_TX_SSI_MUX, 0x11); + b43_radio_write(dev, + B2056_TX1 | B2056_TX_TX_SSI_MUX, 0x11); + } + } + } + + if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) { + b43_maskset32(dev, B43_MMIO_MACCTL, ~0, 0x200000); + b43_read32(dev, B43_MMIO_MACCTL); + udelay(1); + } + + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, + ~B43_NPHY_TXPCTL_CMD_INIT, 0x19); + b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, + ~B43_NPHY_TXPCTL_INIT_PIDXI1, 0x19); + } else { + b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, + ~B43_NPHY_TXPCTL_CMD_INIT, 0x40); + if (dev->phy.rev > 1) + b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, + ~B43_NPHY_TXPCTL_INIT_PIDXI1, 0x40); + } + + if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) + b43_maskset32(dev, B43_MMIO_MACCTL, ~0x200000, 0); + + b43_phy_write(dev, B43_NPHY_TXPCTL_N, + 0xF0 << B43_NPHY_TXPCTL_N_TSSID_SHIFT | + 3 << B43_NPHY_TXPCTL_N_NPTIL2_SHIFT); + b43_phy_write(dev, B43_NPHY_TXPCTL_ITSSI, + idle[0] << B43_NPHY_TXPCTL_ITSSI_0_SHIFT | + idle[1] << B43_NPHY_TXPCTL_ITSSI_1_SHIFT | + B43_NPHY_TXPCTL_ITSSI_BINF); + b43_phy_write(dev, B43_NPHY_TXPCTL_TPWR, + target[0] << B43_NPHY_TXPCTL_TPWR_0_SHIFT | + target[1] << B43_NPHY_TXPCTL_TPWR_1_SHIFT); + + for (c = 0; c < 2; c++) { + for (i = 0; i < 64; i++) { + num = 8 * (16 * b0[c] + b1[c] * i); + den = 32768 + a1[c] * i; + pwr = max((4 * num + den / 2) / den, -8); + if (dev->phy.rev < 3 && (i <= (31 - idle[c] + 1))) + pwr = max(pwr, target[c] + 1); + regval[i] = pwr; + } + b43_ntab_write_bulk(dev, B43_NTAB32(26 + c, 0), 64, regval); + } + + b43_nphy_tx_prepare_adjusted_power_table(dev); + b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84, nphy->adj_pwr_tbl); + b43_ntab_write_bulk(dev, B43_NTAB16(27, 64), 84, nphy->adj_pwr_tbl); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, false); +} + +static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + const u32 *table = NULL; + u32 rfpwr_offset; + u8 pga_gain, pad_gain; + int i; + const s16 *uninitialized_var(rf_pwr_offset_table); + + table = b43_nphy_get_tx_gain_table(dev); + if (!table) + return; + + b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table); + b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table); + + if (phy->rev < 3) + return; + +#if 0 + nphy->gmval = (table[0] >> 16) & 0x7000; +#endif + + if (phy->rev >= 19) { + return; + } else if (phy->rev >= 7) { + rf_pwr_offset_table = b43_ntab_get_rf_pwr_offset_table(dev); + if (!rf_pwr_offset_table) + return; + /* TODO: Enable this once we have gains configured */ + return; + } + + for (i = 0; i < 128; i++) { + if (phy->rev >= 19) { + /* TODO */ + return; + } else if (phy->rev >= 7) { + pga_gain = (table[i] >> 24) & 0xf; + pad_gain = (table[i] >> 19) & 0x1f; + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + rfpwr_offset = rf_pwr_offset_table[pad_gain]; + else + rfpwr_offset = rf_pwr_offset_table[pga_gain]; + } else { + pga_gain = (table[i] >> 24) & 0xF; + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + rfpwr_offset = b43_ntab_papd_pga_gain_delta_ipa_2g[pga_gain]; + else + rfpwr_offset = 0; /* FIXME */ + } + + b43_ntab_write(dev, B43_NTAB32(26, 576 + i), rfpwr_offset); + b43_ntab_write(dev, B43_NTAB32(27, 576 + i), rfpwr_offset); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PA%20override */ +static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable) +{ + struct b43_phy_n *nphy = dev->phy.n; + enum ieee80211_band band; + u16 tmp; + + if (!enable) { + nphy->rfctrl_intc1_save = b43_phy_read(dev, + B43_NPHY_RFCTL_INTC1); + nphy->rfctrl_intc2_save = b43_phy_read(dev, + B43_NPHY_RFCTL_INTC2); + band = b43_current_band(dev->wl); + if (dev->phy.rev >= 7) { + tmp = 0x1480; + } else if (dev->phy.rev >= 3) { + if (band == IEEE80211_BAND_5GHZ) + tmp = 0x600; + else + tmp = 0x480; + } else { + if (band == IEEE80211_BAND_5GHZ) + tmp = 0x180; + else + tmp = 0x120; + } + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, tmp); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, tmp); + } else { + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, + nphy->rfctrl_intc1_save); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, + nphy->rfctrl_intc2_save); + } +} + +/* + * TX low-pass filter bandwidth setup + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw + */ +static void b43_nphy_tx_lpf_bw(struct b43_wldev *dev) +{ + u16 tmp; + + if (dev->phy.rev < 3 || dev->phy.rev >= 7) + return; + + if (b43_nphy_ipa(dev)) + tmp = b43_is_40mhz(dev) ? 5 : 4; + else + tmp = b43_is_40mhz(dev) ? 3 : 1; + b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2, + (tmp << 9) | (tmp << 6) | (tmp << 3) | tmp); + + if (b43_nphy_ipa(dev)) { + tmp = b43_is_40mhz(dev) ? 4 : 1; + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S2, + (tmp << 9) | (tmp << 6) | (tmp << 3) | tmp); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxIqEst */ +static void b43_nphy_rx_iq_est(struct b43_wldev *dev, struct nphy_iq_est *est, + u16 samps, u8 time, bool wait) +{ + int i; + u16 tmp; + + b43_phy_write(dev, B43_NPHY_IQEST_SAMCNT, samps); + b43_phy_maskset(dev, B43_NPHY_IQEST_WT, ~B43_NPHY_IQEST_WT_VAL, time); + if (wait) + b43_phy_set(dev, B43_NPHY_IQEST_CMD, B43_NPHY_IQEST_CMD_MODE); + else + b43_phy_mask(dev, B43_NPHY_IQEST_CMD, ~B43_NPHY_IQEST_CMD_MODE); + + b43_phy_set(dev, B43_NPHY_IQEST_CMD, B43_NPHY_IQEST_CMD_START); + + for (i = 1000; i; i--) { + tmp = b43_phy_read(dev, B43_NPHY_IQEST_CMD); + if (!(tmp & B43_NPHY_IQEST_CMD_START)) { + est->i0_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_IPACC_HI0) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IPACC_LO0); + est->q0_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_QPACC_HI0) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_QPACC_LO0); + est->iq0_prod = (b43_phy_read(dev, B43_NPHY_IQEST_IQACC_HI0) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IQACC_LO0); + + est->i1_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_IPACC_HI1) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IPACC_LO1); + est->q1_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_QPACC_HI1) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_QPACC_LO1); + est->iq1_prod = (b43_phy_read(dev, B43_NPHY_IQEST_IQACC_HI1) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IQACC_LO1); + return; + } + udelay(10); + } + memset(est, 0, sizeof(*est)); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxIqCoeffs */ +static void b43_nphy_rx_iq_coeffs(struct b43_wldev *dev, bool write, + struct b43_phy_n_iq_comp *pcomp) +{ + if (write) { + b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPA0, pcomp->a0); + b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPB0, pcomp->b0); + b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPA1, pcomp->a1); + b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPB1, pcomp->b1); + } else { + pcomp->a0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPA0); + pcomp->b0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPB0); + pcomp->a1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPA1); + pcomp->b1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPB1); + } +} + +#if 0 +/* Ready but not used anywhere */ +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCalPhyCleanup */ +static void b43_nphy_rx_cal_phy_cleanup(struct b43_wldev *dev, u8 core) +{ + u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; + + b43_phy_write(dev, B43_NPHY_RFSEQCA, regs[0]); + if (core == 0) { + b43_phy_write(dev, B43_NPHY_AFECTL_C1, regs[1]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, regs[2]); + } else { + b43_phy_write(dev, B43_NPHY_AFECTL_C2, regs[1]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[2]); + } + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[3]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[4]); + b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO1, regs[5]); + b43_phy_write(dev, B43_NPHY_RFCTL_RSSIO2, regs[6]); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, regs[7]); + b43_phy_write(dev, B43_NPHY_RFCTL_OVER, regs[8]); + b43_phy_write(dev, B43_NPHY_PAPD_EN0, regs[9]); + b43_phy_write(dev, B43_NPHY_PAPD_EN1, regs[10]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCalPhySetup */ +static void b43_nphy_rx_cal_phy_setup(struct b43_wldev *dev, u8 core) +{ + u8 rxval, txval; + u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; + + regs[0] = b43_phy_read(dev, B43_NPHY_RFSEQCA); + if (core == 0) { + regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); + regs[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); + } else { + regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); + regs[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + } + regs[3] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); + regs[4] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); + regs[5] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO1); + regs[6] = b43_phy_read(dev, B43_NPHY_RFCTL_RSSIO2); + regs[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S1); + regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_OVER); + regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0); + regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1); + + b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001); + b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001); + + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, + ~B43_NPHY_RFSEQCA_RXDIS & 0xFFFF, + ((1 - core) << B43_NPHY_RFSEQCA_RXDIS_SHIFT)); + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXEN, + ((1 - core) << B43_NPHY_RFSEQCA_TXEN_SHIFT)); + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_RXEN, + (core << B43_NPHY_RFSEQCA_RXEN_SHIFT)); + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXDIS, + (core << B43_NPHY_RFSEQCA_TXDIS_SHIFT)); + + if (core == 0) { + b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x0007); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x0007); + } else { + b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x0007); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0007); + } + + b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA, 0, 3); + b43_nphy_rf_ctl_override(dev, 8, 0, 3, false); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); + + if (core == 0) { + rxval = 1; + txval = 8; + } else { + rxval = 4; + txval = 2; + } + b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, rxval, + core + 1); + b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, txval, + 2 - core); +} +#endif + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalcRxIqComp */ +static void b43_nphy_calc_rx_iq_comp(struct b43_wldev *dev, u8 mask) +{ + int i; + s32 iq; + u32 ii; + u32 qq; + int iq_nbits, qq_nbits; + int arsh, brsh; + u16 tmp, a, b; + + struct nphy_iq_est est; + struct b43_phy_n_iq_comp old; + struct b43_phy_n_iq_comp new = { }; + bool error = false; + + if (mask == 0) + return; + + b43_nphy_rx_iq_coeffs(dev, false, &old); + b43_nphy_rx_iq_coeffs(dev, true, &new); + b43_nphy_rx_iq_est(dev, &est, 0x4000, 32, false); + new = old; + + for (i = 0; i < 2; i++) { + if (i == 0 && (mask & 1)) { + iq = est.iq0_prod; + ii = est.i0_pwr; + qq = est.q0_pwr; + } else if (i == 1 && (mask & 2)) { + iq = est.iq1_prod; + ii = est.i1_pwr; + qq = est.q1_pwr; + } else { + continue; + } + + if (ii + qq < 2) { + error = true; + break; + } + + iq_nbits = fls(abs(iq)); + qq_nbits = fls(qq); + + arsh = iq_nbits - 20; + if (arsh >= 0) { + a = -((iq << (30 - iq_nbits)) + (ii >> (1 + arsh))); + tmp = ii >> arsh; + } else { + a = -((iq << (30 - iq_nbits)) + (ii << (-1 - arsh))); + tmp = ii << -arsh; + } + if (tmp == 0) { + error = true; + break; + } + a /= tmp; + + brsh = qq_nbits - 11; + if (brsh >= 0) { + b = (qq << (31 - qq_nbits)); + tmp = ii >> brsh; + } else { + b = (qq << (31 - qq_nbits)); + tmp = ii << -brsh; + } + if (tmp == 0) { + error = true; + break; + } + b = int_sqrt(b / tmp - a * a) - (1 << 10); + + if (i == 0 && (mask & 0x1)) { + if (dev->phy.rev >= 3) { + new.a0 = a & 0x3FF; + new.b0 = b & 0x3FF; + } else { + new.a0 = b & 0x3FF; + new.b0 = a & 0x3FF; + } + } else if (i == 1 && (mask & 0x2)) { + if (dev->phy.rev >= 3) { + new.a1 = a & 0x3FF; + new.b1 = b & 0x3FF; + } else { + new.a1 = b & 0x3FF; + new.b1 = a & 0x3FF; + } + } + } + + if (error) + new = old; + + b43_nphy_rx_iq_coeffs(dev, true, &new); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxIqWar */ +static void b43_nphy_tx_iq_workaround(struct b43_wldev *dev) +{ + u16 array[4]; + b43_ntab_read_bulk(dev, B43_NTAB16(0xF, 0x50), 4, array); + + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW0, array[0]); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW1, array[1]); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW2, array[2]); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW3, array[3]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SpurWar */ +static void b43_nphy_spur_workaround(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u8 channel = dev->phy.channel; + int tone[2] = { 57, 58 }; + u32 noise[2] = { 0x3FF, 0x3FF }; + + B43_WARN_ON(dev->phy.rev < 3); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + if (nphy->gband_spurwar_en) { + /* TODO: N PHY Adjust Analog Pfbw (7) */ + if (channel == 11 && b43_is_40mhz(dev)) + ; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/ + else + ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/ + /* TODO: N PHY Adjust CRS Min Power (0x1E) */ + } + + if (nphy->aband_spurwar_en) { + if (channel == 54) { + tone[0] = 0x20; + noise[0] = 0x25F; + } else if (channel == 38 || channel == 102 || channel == 118) { + if (0 /* FIXME */) { + tone[0] = 0x20; + noise[0] = 0x21F; + } else { + tone[0] = 0; + noise[0] = 0; + } + } else if (channel == 134) { + tone[0] = 0x20; + noise[0] = 0x21F; + } else if (channel == 151) { + tone[0] = 0x10; + noise[0] = 0x23F; + } else if (channel == 153 || channel == 161) { + tone[0] = 0x30; + noise[0] = 0x23F; + } else { + tone[0] = 0; + noise[0] = 0; + } + + if (!tone[0] && !noise[0]) + ; /* TODO: N PHY Adjust Min Noise Var(1, tone, noise)*/ + else + ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/ + } + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlCoefSetup */ +static void b43_nphy_tx_pwr_ctrl_coef_setup(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i, j; + u32 tmp; + u32 cur_real, cur_imag, real_part, imag_part; + + u16 buffer[7]; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, true); + + b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer); + + for (i = 0; i < 2; i++) { + tmp = ((buffer[i * 2] & 0x3FF) << 10) | + (buffer[i * 2 + 1] & 0x3FF); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, + (((i + 26) << 10) | 320)); + for (j = 0; j < 128; j++) { + b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, + ((tmp >> 16) & 0xFFFF)); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + (tmp & 0xFFFF)); + } + } + + for (i = 0; i < 2; i++) { + tmp = buffer[5 + i]; + real_part = (tmp >> 8) & 0xFF; + imag_part = (tmp & 0xFF); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, + (((i + 26) << 10) | 448)); + + if (dev->phy.rev >= 3) { + cur_real = real_part; + cur_imag = imag_part; + tmp = ((cur_real & 0xFF) << 8) | (cur_imag & 0xFF); + } + + for (j = 0; j < 128; j++) { + if (dev->phy.rev < 3) { + cur_real = (real_part * loscale[j] + 128) >> 8; + cur_imag = (imag_part * loscale[j] + 128) >> 8; + tmp = ((cur_real & 0xFF) << 8) | + (cur_imag & 0xFF); + } + b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, + ((tmp >> 16) & 0xFFFF)); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + (tmp & 0xFFFF)); + } + } + + if (dev->phy.rev >= 3) { + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_NPHY_TXPWR_INDX0, 0xFFFF); + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_NPHY_TXPWR_INDX1, 0xFFFF); + } + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, false); +} + +/* + * Restore RSSI Calibration + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreRssiCal + */ +static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u16 *rssical_radio_regs = NULL; + u16 *rssical_phy_regs = NULL; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (!nphy->rssical_chanspec_2G.center_freq) + return; + rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_2G; + rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_2G; + } else { + if (!nphy->rssical_chanspec_5G.center_freq) + return; + rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_5G; + rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G; + } + + if (dev->phy.rev >= 19) { + /* TODO */ + } else if (dev->phy.rev >= 7) { + b43_radio_maskset(dev, R2057_NB_MASTER_CORE0, ~R2057_VCM_MASK, + rssical_radio_regs[0]); + b43_radio_maskset(dev, R2057_NB_MASTER_CORE1, ~R2057_VCM_MASK, + rssical_radio_regs[1]); + } else { + b43_radio_maskset(dev, B2056_RX0 | B2056_RX_RSSI_MISC, 0xE3, + rssical_radio_regs[0]); + b43_radio_maskset(dev, B2056_RX1 | B2056_RX_RSSI_MISC, 0xE3, + rssical_radio_regs[1]); + } + + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, rssical_phy_regs[0]); + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, rssical_phy_regs[1]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, rssical_phy_regs[2]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, rssical_phy_regs[3]); + + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, rssical_phy_regs[4]); + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, rssical_phy_regs[5]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, rssical_phy_regs[6]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, rssical_phy_regs[7]); + + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, rssical_phy_regs[8]); + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, rssical_phy_regs[9]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, rssical_phy_regs[10]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, rssical_phy_regs[11]); +} + +static void b43_nphy_tx_cal_radio_setup_rev19(struct b43_wldev *dev) +{ + /* TODO */ +} + +static void b43_nphy_tx_cal_radio_setup_rev7(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + u16 *save = nphy->tx_rx_cal_radio_saveregs; + int core, off; + u16 r, tmp; + + for (core = 0; core < 2; core++) { + r = core ? 0x20 : 0; + off = core * 11; + + save[off + 0] = b43_radio_read(dev, r + R2057_TX0_TX_SSI_MASTER); + save[off + 1] = b43_radio_read(dev, r + R2057_TX0_IQCAL_VCM_HG); + save[off + 2] = b43_radio_read(dev, r + R2057_TX0_IQCAL_IDAC); + save[off + 3] = b43_radio_read(dev, r + R2057_TX0_TSSI_VCM); + save[off + 4] = 0; + save[off + 5] = b43_radio_read(dev, r + R2057_TX0_TX_SSI_MUX); + if (phy->radio_rev != 5) + save[off + 6] = b43_radio_read(dev, r + R2057_TX0_TSSIA); + save[off + 7] = b43_radio_read(dev, r + R2057_TX0_TSSIG); + save[off + 8] = b43_radio_read(dev, r + R2057_TX0_TSSI_MISC1); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_radio_write(dev, r + R2057_TX0_TX_SSI_MASTER, 0xA); + b43_radio_write(dev, r + R2057_TX0_IQCAL_VCM_HG, 0x43); + b43_radio_write(dev, r + R2057_TX0_IQCAL_IDAC, 0x55); + b43_radio_write(dev, r + R2057_TX0_TSSI_VCM, 0); + b43_radio_write(dev, r + R2057_TX0_TSSIG, 0); + if (nphy->use_int_tx_iq_lo_cal) { + b43_radio_write(dev, r + R2057_TX0_TX_SSI_MUX, 0x4); + tmp = true ? 0x31 : 0x21; /* TODO */ + b43_radio_write(dev, r + R2057_TX0_TSSIA, tmp); + } + b43_radio_write(dev, r + R2057_TX0_TSSI_MISC1, 0x00); + } else { + b43_radio_write(dev, r + R2057_TX0_TX_SSI_MASTER, 0x6); + b43_radio_write(dev, r + R2057_TX0_IQCAL_VCM_HG, 0x43); + b43_radio_write(dev, r + R2057_TX0_IQCAL_IDAC, 0x55); + b43_radio_write(dev, r + R2057_TX0_TSSI_VCM, 0); + + if (phy->radio_rev != 5) + b43_radio_write(dev, r + R2057_TX0_TSSIA, 0); + if (nphy->use_int_tx_iq_lo_cal) { + b43_radio_write(dev, r + R2057_TX0_TX_SSI_MUX, 0x6); + tmp = true ? 0x31 : 0x21; /* TODO */ + b43_radio_write(dev, r + R2057_TX0_TSSIG, tmp); + } + b43_radio_write(dev, r + R2057_TX0_TSSI_MISC1, 0); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalRadioSetup */ +static void b43_nphy_tx_cal_radio_setup(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + u16 *save = nphy->tx_rx_cal_radio_saveregs; + u16 tmp; + u8 offset, i; + + if (phy->rev >= 19) { + b43_nphy_tx_cal_radio_setup_rev19(dev); + } else if (phy->rev >= 7) { + b43_nphy_tx_cal_radio_setup_rev7(dev); + } else if (phy->rev >= 3) { + for (i = 0; i < 2; i++) { + tmp = (i == 0) ? 0x2000 : 0x3000; + offset = i * 11; + + save[offset + 0] = b43_radio_read(dev, B2055_CAL_RVARCTL); + save[offset + 1] = b43_radio_read(dev, B2055_CAL_LPOCTL); + save[offset + 2] = b43_radio_read(dev, B2055_CAL_TS); + save[offset + 3] = b43_radio_read(dev, B2055_CAL_RCCALRTS); + save[offset + 4] = b43_radio_read(dev, B2055_CAL_RCALRTS); + save[offset + 5] = b43_radio_read(dev, B2055_PADDRV); + save[offset + 6] = b43_radio_read(dev, B2055_XOCTL1); + save[offset + 7] = b43_radio_read(dev, B2055_XOCTL2); + save[offset + 8] = b43_radio_read(dev, B2055_XOREGUL); + save[offset + 9] = b43_radio_read(dev, B2055_XOMISC); + save[offset + 10] = b43_radio_read(dev, B2055_PLL_LFC1); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_radio_write(dev, tmp | B2055_CAL_RVARCTL, 0x0A); + b43_radio_write(dev, tmp | B2055_CAL_LPOCTL, 0x40); + b43_radio_write(dev, tmp | B2055_CAL_TS, 0x55); + b43_radio_write(dev, tmp | B2055_CAL_RCCALRTS, 0); + b43_radio_write(dev, tmp | B2055_CAL_RCALRTS, 0); + if (nphy->ipa5g_on) { + b43_radio_write(dev, tmp | B2055_PADDRV, 4); + b43_radio_write(dev, tmp | B2055_XOCTL1, 1); + } else { + b43_radio_write(dev, tmp | B2055_PADDRV, 0); + b43_radio_write(dev, tmp | B2055_XOCTL1, 0x2F); + } + b43_radio_write(dev, tmp | B2055_XOCTL2, 0); + } else { + b43_radio_write(dev, tmp | B2055_CAL_RVARCTL, 0x06); + b43_radio_write(dev, tmp | B2055_CAL_LPOCTL, 0x40); + b43_radio_write(dev, tmp | B2055_CAL_TS, 0x55); + b43_radio_write(dev, tmp | B2055_CAL_RCCALRTS, 0); + b43_radio_write(dev, tmp | B2055_CAL_RCALRTS, 0); + b43_radio_write(dev, tmp | B2055_XOCTL1, 0); + if (nphy->ipa2g_on) { + b43_radio_write(dev, tmp | B2055_PADDRV, 6); + b43_radio_write(dev, tmp | B2055_XOCTL2, + (dev->phy.rev < 5) ? 0x11 : 0x01); + } else { + b43_radio_write(dev, tmp | B2055_PADDRV, 0); + b43_radio_write(dev, tmp | B2055_XOCTL2, 0); + } + } + b43_radio_write(dev, tmp | B2055_XOREGUL, 0); + b43_radio_write(dev, tmp | B2055_XOMISC, 0); + b43_radio_write(dev, tmp | B2055_PLL_LFC1, 0); + } + } else { + save[0] = b43_radio_read(dev, B2055_C1_TX_RF_IQCAL1); + b43_radio_write(dev, B2055_C1_TX_RF_IQCAL1, 0x29); + + save[1] = b43_radio_read(dev, B2055_C1_TX_RF_IQCAL2); + b43_radio_write(dev, B2055_C1_TX_RF_IQCAL2, 0x54); + + save[2] = b43_radio_read(dev, B2055_C2_TX_RF_IQCAL1); + b43_radio_write(dev, B2055_C2_TX_RF_IQCAL1, 0x29); + + save[3] = b43_radio_read(dev, B2055_C2_TX_RF_IQCAL2); + b43_radio_write(dev, B2055_C2_TX_RF_IQCAL2, 0x54); + + save[3] = b43_radio_read(dev, B2055_C1_PWRDET_RXTX); + save[4] = b43_radio_read(dev, B2055_C2_PWRDET_RXTX); + + if (!(b43_phy_read(dev, B43_NPHY_BANDCTL) & + B43_NPHY_BANDCTL_5GHZ)) { + b43_radio_write(dev, B2055_C1_PWRDET_RXTX, 0x04); + b43_radio_write(dev, B2055_C2_PWRDET_RXTX, 0x04); + } else { + b43_radio_write(dev, B2055_C1_PWRDET_RXTX, 0x20); + b43_radio_write(dev, B2055_C2_PWRDET_RXTX, 0x20); + } + + if (dev->phy.rev < 2) { + b43_radio_set(dev, B2055_C1_TX_BB_MXGM, 0x20); + b43_radio_set(dev, B2055_C2_TX_BB_MXGM, 0x20); + } else { + b43_radio_mask(dev, B2055_C1_TX_BB_MXGM, ~0x20); + b43_radio_mask(dev, B2055_C2_TX_BB_MXGM, ~0x20); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/UpdateTxCalLadder */ +static void b43_nphy_update_tx_cal_ladder(struct b43_wldev *dev, u16 core) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i; + u16 scale, entry; + + u16 tmp = nphy->txcal_bbmult; + if (core == 0) + tmp >>= 8; + tmp &= 0xff; + + for (i = 0; i < 18; i++) { + scale = (ladder_lo[i].percent * tmp) / 100; + entry = ((scale & 0xFF) << 8) | ladder_lo[i].g_env; + b43_ntab_write(dev, B43_NTAB16(15, i), entry); + + scale = (ladder_iq[i].percent * tmp) / 100; + entry = ((scale & 0xFF) << 8) | ladder_iq[i].g_env; + b43_ntab_write(dev, B43_NTAB16(15, i + 32), entry); + } +} + +static void b43_nphy_pa_set_tx_dig_filter(struct b43_wldev *dev, u16 offset, + const s16 *filter) +{ + int i; + + offset = B43_PHY_N(offset); + + for (i = 0; i < 15; i++, offset++) + b43_phy_write(dev, offset, filter[i]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ExtPaSetTxDigiFilts */ +static void b43_nphy_ext_pa_set_tx_dig_filters(struct b43_wldev *dev) +{ + b43_nphy_pa_set_tx_dig_filter(dev, 0x2C5, + tbl_tx_filter_coef_rev4[2]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IpaSetTxDigiFilts */ +static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev) +{ + /* B43_NPHY_TXF_20CO_S0A1, B43_NPHY_TXF_40CO_S0A1, unknown */ + static const u16 offset[] = { 0x186, 0x195, 0x2C5 }; + static const s16 dig_filter_phy_rev16[] = { + -375, 136, -407, 208, -1527, + 956, 93, 186, 93, 230, + -44, 230, 201, -191, 201, + }; + int i; + + for (i = 0; i < 3; i++) + b43_nphy_pa_set_tx_dig_filter(dev, offset[i], + tbl_tx_filter_coef_rev4[i]); + + /* Verified with BCM43227 and BCM43228 */ + if (dev->phy.rev == 16) + b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16); + + /* Verified with BCM43131 and BCM43217 */ + if (dev->phy.rev == 17) { + b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16); + b43_nphy_pa_set_tx_dig_filter(dev, 0x195, + tbl_tx_filter_coef_rev4[1]); + } + + if (b43_is_40mhz(dev)) { + b43_nphy_pa_set_tx_dig_filter(dev, 0x186, + tbl_tx_filter_coef_rev4[3]); + } else { + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + b43_nphy_pa_set_tx_dig_filter(dev, 0x186, + tbl_tx_filter_coef_rev4[5]); + if (dev->phy.channel == 14) + b43_nphy_pa_set_tx_dig_filter(dev, 0x186, + tbl_tx_filter_coef_rev4[6]); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetTxGain */ +static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u16 curr_gain[2]; + struct nphy_txgains target; + const u32 *table = NULL; + + if (!nphy->txpwrctrl) { + int i; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, true); + b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, curr_gain); + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, false); + + for (i = 0; i < 2; ++i) { + if (dev->phy.rev >= 7) { + target.ipa[i] = curr_gain[i] & 0x0007; + target.pad[i] = (curr_gain[i] & 0x00F8) >> 3; + target.pga[i] = (curr_gain[i] & 0x0F00) >> 8; + target.txgm[i] = (curr_gain[i] & 0x7000) >> 12; + target.tx_lpf[i] = (curr_gain[i] & 0x8000) >> 15; + } else if (dev->phy.rev >= 3) { + target.ipa[i] = curr_gain[i] & 0x000F; + target.pad[i] = (curr_gain[i] & 0x00F0) >> 4; + target.pga[i] = (curr_gain[i] & 0x0F00) >> 8; + target.txgm[i] = (curr_gain[i] & 0x7000) >> 12; + } else { + target.ipa[i] = curr_gain[i] & 0x0003; + target.pad[i] = (curr_gain[i] & 0x000C) >> 2; + target.pga[i] = (curr_gain[i] & 0x0070) >> 4; + target.txgm[i] = (curr_gain[i] & 0x0380) >> 7; + } + } + } else { + int i; + u16 index[2]; + index[0] = (b43_phy_read(dev, B43_NPHY_C1_TXPCTL_STAT) & + B43_NPHY_TXPCTL_STAT_BIDX) >> + B43_NPHY_TXPCTL_STAT_BIDX_SHIFT; + index[1] = (b43_phy_read(dev, B43_NPHY_C2_TXPCTL_STAT) & + B43_NPHY_TXPCTL_STAT_BIDX) >> + B43_NPHY_TXPCTL_STAT_BIDX_SHIFT; + + for (i = 0; i < 2; ++i) { + table = b43_nphy_get_tx_gain_table(dev); + if (!table) + break; + + if (dev->phy.rev >= 7) { + target.ipa[i] = (table[index[i]] >> 16) & 0x7; + target.pad[i] = (table[index[i]] >> 19) & 0x1F; + target.pga[i] = (table[index[i]] >> 24) & 0xF; + target.txgm[i] = (table[index[i]] >> 28) & 0x7; + target.tx_lpf[i] = (table[index[i]] >> 31) & 0x1; + } else if (dev->phy.rev >= 3) { + target.ipa[i] = (table[index[i]] >> 16) & 0xF; + target.pad[i] = (table[index[i]] >> 20) & 0xF; + target.pga[i] = (table[index[i]] >> 24) & 0xF; + target.txgm[i] = (table[index[i]] >> 28) & 0xF; + } else { + target.ipa[i] = (table[index[i]] >> 16) & 0x3; + target.pad[i] = (table[index[i]] >> 18) & 0x3; + target.pga[i] = (table[index[i]] >> 20) & 0x7; + target.txgm[i] = (table[index[i]] >> 23) & 0x7; + } + } + } + + return target; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhyCleanup */ +static void b43_nphy_tx_cal_phy_cleanup(struct b43_wldev *dev) +{ + u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; + + if (dev->phy.rev >= 3) { + b43_phy_write(dev, B43_NPHY_AFECTL_C1, regs[0]); + b43_phy_write(dev, B43_NPHY_AFECTL_C2, regs[1]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, regs[2]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[3]); + b43_phy_write(dev, B43_NPHY_BBCFG, regs[4]); + b43_ntab_write(dev, B43_NTAB16(8, 3), regs[5]); + b43_ntab_write(dev, B43_NTAB16(8, 19), regs[6]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[7]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[8]); + b43_phy_write(dev, B43_NPHY_PAPD_EN0, regs[9]); + b43_phy_write(dev, B43_NPHY_PAPD_EN1, regs[10]); + b43_nphy_reset_cca(dev); + } else { + b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, regs[0]); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, regs[1]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, regs[2]); + b43_ntab_write(dev, B43_NTAB16(8, 2), regs[3]); + b43_ntab_write(dev, B43_NTAB16(8, 18), regs[4]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs[5]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs[6]); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhySetup */ +static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; + u16 tmp; + + regs[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); + regs[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); + if (dev->phy.rev >= 3) { + b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0xF0FF, 0x0A00); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0xF0FF, 0x0A00); + + tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); + regs[2] = tmp; + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, tmp | 0x0600); + + tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + regs[3] = tmp; + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp | 0x0600); + + regs[4] = b43_phy_read(dev, B43_NPHY_BBCFG); + b43_phy_mask(dev, B43_NPHY_BBCFG, + ~B43_NPHY_BBCFG_RSTRX & 0xFFFF); + + tmp = b43_ntab_read(dev, B43_NTAB16(8, 3)); + regs[5] = tmp; + b43_ntab_write(dev, B43_NTAB16(8, 3), 0); + + tmp = b43_ntab_read(dev, B43_NTAB16(8, 19)); + regs[6] = tmp; + b43_ntab_write(dev, B43_NTAB16(8, 19), 0); + regs[7] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); + regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); + + if (!nphy->use_int_tx_iq_lo_cal) + b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA, + 1, 3); + else + b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA, + 0, 3); + b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 2, 1); + b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 8, 2); + + regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0); + regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1); + b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001); + b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001); + + tmp = b43_nphy_read_lpf_ctl(dev, 0); + if (phy->rev >= 19) + b43_nphy_rf_ctl_override_rev19(dev, 0x80, tmp, 0, false, + 1); + else if (phy->rev >= 7) + b43_nphy_rf_ctl_override_rev7(dev, 0x80, tmp, 0, false, + 1); + + if (nphy->use_int_tx_iq_lo_cal && true /* FIXME */) { + if (phy->rev >= 19) { + b43_nphy_rf_ctl_override_rev19(dev, 0x8, 0, 0x3, + false, 0); + } else if (phy->rev >= 8) { + b43_nphy_rf_ctl_override_rev7(dev, 0x8, 0, 0x3, + false, 0); + } else if (phy->rev == 7) { + b43_radio_maskset(dev, R2057_OVR_REG0, 1 << 4, 1 << 4); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_radio_maskset(dev, R2057_PAD2G_TUNE_PUS_CORE0, ~1, 0); + b43_radio_maskset(dev, R2057_PAD2G_TUNE_PUS_CORE1, ~1, 0); + } else { + b43_radio_maskset(dev, R2057_IPA5G_CASCOFFV_PU_CORE0, ~1, 0); + b43_radio_maskset(dev, R2057_IPA5G_CASCOFFV_PU_CORE1, ~1, 0); + } + } + } + } else { + b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, 0xA000); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, 0xA000); + tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + regs[2] = tmp; + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp | 0x3000); + tmp = b43_ntab_read(dev, B43_NTAB16(8, 2)); + regs[3] = tmp; + tmp |= 0x2000; + b43_ntab_write(dev, B43_NTAB16(8, 2), tmp); + tmp = b43_ntab_read(dev, B43_NTAB16(8, 18)); + regs[4] = tmp; + tmp |= 0x2000; + b43_ntab_write(dev, B43_NTAB16(8, 18), tmp); + regs[5] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); + regs[6] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + tmp = 0x0180; + else + tmp = 0x0120; + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, tmp); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, tmp); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SaveCal */ +static void b43_nphy_save_cal(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + + struct b43_phy_n_iq_comp *rxcal_coeffs = NULL; + u16 *txcal_radio_regs = NULL; + struct b43_chanspec *iqcal_chanspec; + u16 *table = NULL; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 1); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G; + txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G; + iqcal_chanspec = &nphy->iqcal_chanspec_2G; + table = nphy->cal_cache.txcal_coeffs_2G; + } else { + rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G; + txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G; + iqcal_chanspec = &nphy->iqcal_chanspec_5G; + table = nphy->cal_cache.txcal_coeffs_5G; + } + + b43_nphy_rx_iq_coeffs(dev, false, rxcal_coeffs); + /* TODO use some definitions */ + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + txcal_radio_regs[0] = b43_radio_read(dev, + R2057_TX0_LOFT_FINE_I); + txcal_radio_regs[1] = b43_radio_read(dev, + R2057_TX0_LOFT_FINE_Q); + txcal_radio_regs[4] = b43_radio_read(dev, + R2057_TX0_LOFT_COARSE_I); + txcal_radio_regs[5] = b43_radio_read(dev, + R2057_TX0_LOFT_COARSE_Q); + txcal_radio_regs[2] = b43_radio_read(dev, + R2057_TX1_LOFT_FINE_I); + txcal_radio_regs[3] = b43_radio_read(dev, + R2057_TX1_LOFT_FINE_Q); + txcal_radio_regs[6] = b43_radio_read(dev, + R2057_TX1_LOFT_COARSE_I); + txcal_radio_regs[7] = b43_radio_read(dev, + R2057_TX1_LOFT_COARSE_Q); + } else if (phy->rev >= 3) { + txcal_radio_regs[0] = b43_radio_read(dev, 0x2021); + txcal_radio_regs[1] = b43_radio_read(dev, 0x2022); + txcal_radio_regs[2] = b43_radio_read(dev, 0x3021); + txcal_radio_regs[3] = b43_radio_read(dev, 0x3022); + txcal_radio_regs[4] = b43_radio_read(dev, 0x2023); + txcal_radio_regs[5] = b43_radio_read(dev, 0x2024); + txcal_radio_regs[6] = b43_radio_read(dev, 0x3023); + txcal_radio_regs[7] = b43_radio_read(dev, 0x3024); + } else { + txcal_radio_regs[0] = b43_radio_read(dev, 0x8B); + txcal_radio_regs[1] = b43_radio_read(dev, 0xBA); + txcal_radio_regs[2] = b43_radio_read(dev, 0x8D); + txcal_radio_regs[3] = b43_radio_read(dev, 0xBC); + } + iqcal_chanspec->center_freq = dev->phy.chandef->chan->center_freq; + iqcal_chanspec->channel_type = + cfg80211_get_chandef_type(dev->phy.chandef); + b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 8, table); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, 0); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */ +static void b43_nphy_restore_cal(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + + u16 coef[4]; + u16 *loft = NULL; + u16 *table = NULL; + + int i; + u16 *txcal_radio_regs = NULL; + struct b43_phy_n_iq_comp *rxcal_coeffs = NULL; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (!nphy->iqcal_chanspec_2G.center_freq) + return; + table = nphy->cal_cache.txcal_coeffs_2G; + loft = &nphy->cal_cache.txcal_coeffs_2G[5]; + } else { + if (!nphy->iqcal_chanspec_5G.center_freq) + return; + table = nphy->cal_cache.txcal_coeffs_5G; + loft = &nphy->cal_cache.txcal_coeffs_5G[5]; + } + + b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4, table); + + for (i = 0; i < 4; i++) { + if (dev->phy.rev >= 3) + table[i] = coef[i]; + else + coef[i] = 0; + } + + b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, coef); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, loft); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, loft); + + if (dev->phy.rev < 2) + b43_nphy_tx_iq_workaround(dev); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G; + rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G; + } else { + txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G; + rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G; + } + + /* TODO use some definitions */ + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + b43_radio_write(dev, R2057_TX0_LOFT_FINE_I, + txcal_radio_regs[0]); + b43_radio_write(dev, R2057_TX0_LOFT_FINE_Q, + txcal_radio_regs[1]); + b43_radio_write(dev, R2057_TX0_LOFT_COARSE_I, + txcal_radio_regs[4]); + b43_radio_write(dev, R2057_TX0_LOFT_COARSE_Q, + txcal_radio_regs[5]); + b43_radio_write(dev, R2057_TX1_LOFT_FINE_I, + txcal_radio_regs[2]); + b43_radio_write(dev, R2057_TX1_LOFT_FINE_Q, + txcal_radio_regs[3]); + b43_radio_write(dev, R2057_TX1_LOFT_COARSE_I, + txcal_radio_regs[6]); + b43_radio_write(dev, R2057_TX1_LOFT_COARSE_Q, + txcal_radio_regs[7]); + } else if (phy->rev >= 3) { + b43_radio_write(dev, 0x2021, txcal_radio_regs[0]); + b43_radio_write(dev, 0x2022, txcal_radio_regs[1]); + b43_radio_write(dev, 0x3021, txcal_radio_regs[2]); + b43_radio_write(dev, 0x3022, txcal_radio_regs[3]); + b43_radio_write(dev, 0x2023, txcal_radio_regs[4]); + b43_radio_write(dev, 0x2024, txcal_radio_regs[5]); + b43_radio_write(dev, 0x3023, txcal_radio_regs[6]); + b43_radio_write(dev, 0x3024, txcal_radio_regs[7]); + } else { + b43_radio_write(dev, 0x8B, txcal_radio_regs[0]); + b43_radio_write(dev, 0xBA, txcal_radio_regs[1]); + b43_radio_write(dev, 0x8D, txcal_radio_regs[2]); + b43_radio_write(dev, 0xBC, txcal_radio_regs[3]); + } + b43_nphy_rx_iq_coeffs(dev, true, rxcal_coeffs); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalTxIqlo */ +static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, + struct nphy_txgains target, + bool full, bool mphase) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + int i; + int error = 0; + int freq; + bool avoid = false; + u8 length; + u16 tmp, core, type, count, max, numb, last = 0, cmd; + const u16 *table; + bool phy6or5x; + + u16 buffer[11]; + u16 diq_start = 0; + u16 save[2]; + u16 gain[2]; + struct nphy_iqcal_params params[2]; + bool updated[2] = { }; + + b43_nphy_stay_in_carrier_search(dev, true); + + if (dev->phy.rev >= 4) { + avoid = nphy->hang_avoid; + nphy->hang_avoid = false; + } + + b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, save); + + for (i = 0; i < 2; i++) { + b43_nphy_iq_cal_gain_params(dev, i, target, ¶ms[i]); + gain[i] = params[i].cal_gain; + } + + b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, gain); + + b43_nphy_tx_cal_radio_setup(dev); + b43_nphy_tx_cal_phy_setup(dev); + + phy6or5x = dev->phy.rev >= 6 || + (dev->phy.rev == 5 && nphy->ipa2g_on && + b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ); + if (phy6or5x) { + if (b43_is_40mhz(dev)) { + b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18, + tbl_tx_iqlo_cal_loft_ladder_40); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18, + tbl_tx_iqlo_cal_iqimb_ladder_40); + } else { + b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18, + tbl_tx_iqlo_cal_loft_ladder_20); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18, + tbl_tx_iqlo_cal_iqimb_ladder_20); + } + } + + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AD9); + } else { + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9); + } + + if (!b43_is_40mhz(dev)) + freq = 2500; + else + freq = 5000; + + if (nphy->mphase_cal_phase_id > 2) + b43_nphy_run_samples(dev, (b43_is_40mhz(dev) ? 40 : 20) * 8, + 0xFFFF, 0, true, false, false); + else + error = b43_nphy_tx_tone(dev, freq, 250, true, false, false); + + if (error == 0) { + if (nphy->mphase_cal_phase_id > 2) { + table = nphy->mphase_txcal_bestcoeffs; + length = 11; + if (dev->phy.rev < 3) + length -= 2; + } else { + if (!full && nphy->txiqlocal_coeffsvalid) { + table = nphy->txiqlocal_bestc; + length = 11; + if (dev->phy.rev < 3) + length -= 2; + } else { + full = true; + if (dev->phy.rev >= 3) { + table = tbl_tx_iqlo_cal_startcoefs_nphyrev3; + length = B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3; + } else { + table = tbl_tx_iqlo_cal_startcoefs; + length = B43_NTAB_TX_IQLO_CAL_STARTCOEFS; + } + } + } + + b43_ntab_write_bulk(dev, B43_NTAB16(15, 64), length, table); + + if (full) { + if (dev->phy.rev >= 3) + max = B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL_REV3; + else + max = B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL; + } else { + if (dev->phy.rev >= 3) + max = B43_NTAB_TX_IQLO_CAL_CMDS_RECAL_REV3; + else + max = B43_NTAB_TX_IQLO_CAL_CMDS_RECAL; + } + + if (mphase) { + count = nphy->mphase_txcal_cmdidx; + numb = min(max, + (u16)(count + nphy->mphase_txcal_numcmds)); + } else { + count = 0; + numb = max; + } + + for (; count < numb; count++) { + if (full) { + if (dev->phy.rev >= 3) + cmd = tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[count]; + else + cmd = tbl_tx_iqlo_cal_cmds_fullcal[count]; + } else { + if (dev->phy.rev >= 3) + cmd = tbl_tx_iqlo_cal_cmds_recal_nphyrev3[count]; + else + cmd = tbl_tx_iqlo_cal_cmds_recal[count]; + } + + core = (cmd & 0x3000) >> 12; + type = (cmd & 0x0F00) >> 8; + + if (phy6or5x && updated[core] == 0) { + b43_nphy_update_tx_cal_ladder(dev, core); + updated[core] = true; + } + + tmp = (params[core].ncorr[type] << 8) | 0x66; + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDNNUM, tmp); + + if (type == 1 || type == 3 || type == 4) { + buffer[0] = b43_ntab_read(dev, + B43_NTAB16(15, 69 + core)); + diq_start = buffer[0]; + buffer[0] = 0; + b43_ntab_write(dev, B43_NTAB16(15, 69 + core), + 0); + } + + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMD, cmd); + for (i = 0; i < 2000; i++) { + tmp = b43_phy_read(dev, B43_NPHY_IQLOCAL_CMD); + if (tmp & 0xC000) + break; + udelay(10); + } + + b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, + buffer); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 64), length, + buffer); + + if (type == 1 || type == 3 || type == 4) + buffer[0] = diq_start; + } + + if (mphase) + nphy->mphase_txcal_cmdidx = (numb >= max) ? 0 : numb; + + last = (dev->phy.rev < 3) ? 6 : 7; + + if (!mphase || nphy->mphase_cal_phase_id == last) { + b43_ntab_write_bulk(dev, B43_NTAB16(15, 96), 4, buffer); + b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 4, buffer); + if (dev->phy.rev < 3) { + buffer[0] = 0; + buffer[1] = 0; + buffer[2] = 0; + buffer[3] = 0; + } + b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, + buffer); + b43_ntab_read_bulk(dev, B43_NTAB16(15, 101), 2, + buffer); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, + buffer); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, + buffer); + length = 11; + if (dev->phy.rev < 3) + length -= 2; + b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, + nphy->txiqlocal_bestc); + nphy->txiqlocal_coeffsvalid = true; + nphy->txiqlocal_chanspec.center_freq = + phy->chandef->chan->center_freq; + nphy->txiqlocal_chanspec.channel_type = + cfg80211_get_chandef_type(phy->chandef); + } else { + length = 11; + if (dev->phy.rev < 3) + length -= 2; + b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length, + nphy->mphase_txcal_bestcoeffs); + } + + b43_nphy_stop_playback(dev); + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0); + } + + b43_nphy_tx_cal_phy_cleanup(dev); + b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, save); + + if (dev->phy.rev < 2 && (!mphase || nphy->mphase_cal_phase_id == last)) + b43_nphy_tx_iq_workaround(dev); + + if (dev->phy.rev >= 4) + nphy->hang_avoid = avoid; + + b43_nphy_stay_in_carrier_search(dev, false); + + return error; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ReapplyTxCalCoeffs */ +static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + u8 i; + u16 buffer[7]; + bool equal = true; + + if (!nphy->txiqlocal_coeffsvalid || + nphy->txiqlocal_chanspec.center_freq != dev->phy.chandef->chan->center_freq || + nphy->txiqlocal_chanspec.channel_type != cfg80211_get_chandef_type(dev->phy.chandef)) + return; + + b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer); + for (i = 0; i < 4; i++) { + if (buffer[i] != nphy->txiqlocal_bestc[i]) { + equal = false; + break; + } + } + + if (!equal) { + b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4, + nphy->txiqlocal_bestc); + for (i = 0; i < 4; i++) + buffer[i] = 0; + b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4, + buffer); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2, + &nphy->txiqlocal_bestc[5]); + b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2, + &nphy->txiqlocal_bestc[5]); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIqRev2 */ +static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev, + struct nphy_txgains target, u8 type, bool debug) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i, j, index; + u8 rfctl[2]; + u8 afectl_core; + u16 tmp[6]; + u16 uninitialized_var(cur_hpf1), uninitialized_var(cur_hpf2), cur_lna; + u32 real, imag; + enum ieee80211_band band; + + u8 use; + u16 cur_hpf; + u16 lna[3] = { 3, 3, 1 }; + u16 hpf1[3] = { 7, 2, 0 }; + u16 hpf2[3] = { 2, 0, 0 }; + u32 power[3] = { }; + u16 gain_save[2]; + u16 cal_gain[2]; + struct nphy_iqcal_params cal_params[2]; + struct nphy_iq_est est; + int ret = 0; + bool playtone = true; + int desired = 13; + + b43_nphy_stay_in_carrier_search(dev, 1); + + if (dev->phy.rev < 2) + b43_nphy_reapply_tx_cal_coeffs(dev); + b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save); + for (i = 0; i < 2; i++) { + b43_nphy_iq_cal_gain_params(dev, i, target, &cal_params[i]); + cal_gain[i] = cal_params[i].cal_gain; + } + b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, cal_gain); + + for (i = 0; i < 2; i++) { + if (i == 0) { + rfctl[0] = B43_NPHY_RFCTL_INTC1; + rfctl[1] = B43_NPHY_RFCTL_INTC2; + afectl_core = B43_NPHY_AFECTL_C1; + } else { + rfctl[0] = B43_NPHY_RFCTL_INTC2; + rfctl[1] = B43_NPHY_RFCTL_INTC1; + afectl_core = B43_NPHY_AFECTL_C2; + } + + tmp[1] = b43_phy_read(dev, B43_NPHY_RFSEQCA); + tmp[2] = b43_phy_read(dev, afectl_core); + tmp[3] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + tmp[4] = b43_phy_read(dev, rfctl[0]); + tmp[5] = b43_phy_read(dev, rfctl[1]); + + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, + ~B43_NPHY_RFSEQCA_RXDIS & 0xFFFF, + ((1 - i) << B43_NPHY_RFSEQCA_RXDIS_SHIFT)); + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXEN, + (1 - i)); + b43_phy_set(dev, afectl_core, 0x0006); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0006); + + band = b43_current_band(dev->wl); + + if (nphy->rxcalparams & 0xFF000000) { + if (band == IEEE80211_BAND_5GHZ) + b43_phy_write(dev, rfctl[0], 0x140); + else + b43_phy_write(dev, rfctl[0], 0x110); + } else { + if (band == IEEE80211_BAND_5GHZ) + b43_phy_write(dev, rfctl[0], 0x180); + else + b43_phy_write(dev, rfctl[0], 0x120); + } + + if (band == IEEE80211_BAND_5GHZ) + b43_phy_write(dev, rfctl[1], 0x148); + else + b43_phy_write(dev, rfctl[1], 0x114); + + if (nphy->rxcalparams & 0x10000) { + b43_radio_maskset(dev, B2055_C1_GENSPARE2, 0xFC, + (i + 1)); + b43_radio_maskset(dev, B2055_C2_GENSPARE2, 0xFC, + (2 - i)); + } + + for (j = 0; j < 4; j++) { + if (j < 3) { + cur_lna = lna[j]; + cur_hpf1 = hpf1[j]; + cur_hpf2 = hpf2[j]; + } else { + if (power[1] > 10000) { + use = 1; + cur_hpf = cur_hpf1; + index = 2; + } else { + if (power[0] > 10000) { + use = 1; + cur_hpf = cur_hpf1; + index = 1; + } else { + index = 0; + use = 2; + cur_hpf = cur_hpf2; + } + } + cur_lna = lna[index]; + cur_hpf1 = hpf1[index]; + cur_hpf2 = hpf2[index]; + cur_hpf += desired - hweight32(power[index]); + cur_hpf = clamp_val(cur_hpf, 0, 10); + if (use == 1) + cur_hpf1 = cur_hpf; + else + cur_hpf2 = cur_hpf; + } + + tmp[0] = ((cur_hpf2 << 8) | (cur_hpf1 << 4) | + (cur_lna << 2)); + b43_nphy_rf_ctl_override(dev, 0x400, tmp[0], 3, + false); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + b43_nphy_stop_playback(dev); + + if (playtone) { + ret = b43_nphy_tx_tone(dev, 4000, + (nphy->rxcalparams & 0xFFFF), + false, false, true); + playtone = false; + } else { + b43_nphy_run_samples(dev, 160, 0xFFFF, 0, false, + false, true); + } + + if (ret == 0) { + if (j < 3) { + b43_nphy_rx_iq_est(dev, &est, 1024, 32, + false); + if (i == 0) { + real = est.i0_pwr; + imag = est.q0_pwr; + } else { + real = est.i1_pwr; + imag = est.q1_pwr; + } + power[i] = ((real + imag) / 1024) + 1; + } else { + b43_nphy_calc_rx_iq_comp(dev, 1 << i); + } + b43_nphy_stop_playback(dev); + } + + if (ret != 0) + break; + } + + b43_radio_mask(dev, B2055_C1_GENSPARE2, 0xFC); + b43_radio_mask(dev, B2055_C2_GENSPARE2, 0xFC); + b43_phy_write(dev, rfctl[1], tmp[5]); + b43_phy_write(dev, rfctl[0], tmp[4]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp[3]); + b43_phy_write(dev, afectl_core, tmp[2]); + b43_phy_write(dev, B43_NPHY_RFSEQCA, tmp[1]); + + if (ret != 0) + break; + } + + b43_nphy_rf_ctl_override(dev, 0x400, 0, 3, true); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save); + + b43_nphy_stay_in_carrier_search(dev, 0); + + return ret; +} + +static int b43_nphy_rev3_cal_rx_iq(struct b43_wldev *dev, + struct nphy_txgains target, u8 type, bool debug) +{ + return -1; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIq */ +static int b43_nphy_cal_rx_iq(struct b43_wldev *dev, + struct nphy_txgains target, u8 type, bool debug) +{ + if (dev->phy.rev >= 7) + type = 0; + + if (dev->phy.rev >= 3) + return b43_nphy_rev3_cal_rx_iq(dev, target, type, debug); + else + return b43_nphy_rev2_cal_rx_iq(dev, target, type, debug); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxCoreSetState */ +static void b43_nphy_set_rx_core_state(struct b43_wldev *dev, u8 mask) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = phy->n; + /* u16 buf[16]; it's rev3+ */ + + nphy->phyrxchain = mask; + + if (0 /* FIXME clk */) + return; + + b43_mac_suspend(dev); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, true); + + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_RXEN, + (mask & 0x3) << B43_NPHY_RFSEQCA_RXEN_SHIFT); + + if ((mask & 0x3) != 0x3) { + b43_phy_write(dev, B43_NPHY_HPANT_SWTHRES, 1); + if (dev->phy.rev >= 3) { + /* TODO */ + } + } else { + b43_phy_write(dev, B43_NPHY_HPANT_SWTHRES, 0x1E); + if (dev->phy.rev >= 3) { + /* TODO */ + } + } + + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, false); + + b43_mac_enable(dev); +} + +static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev, + bool ignore_tssi) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + struct ieee80211_channel *channel = dev->wl->hw->conf.chandef.chan; + struct b43_ppr *ppr = &nphy->tx_pwr_max_ppr; + u8 max; /* qdBm */ + bool tx_pwr_state; + + if (nphy->tx_pwr_last_recalc_freq == channel->center_freq && + nphy->tx_pwr_last_recalc_limit == phy->desired_txpower) + return B43_TXPWR_RES_DONE; + + /* Make sure we have a clean PPR */ + b43_ppr_clear(dev, ppr); + + /* HW limitations */ + b43_ppr_load_max_from_sprom(dev, ppr, B43_BAND_2G); + + /* Regulatory & user settings */ + max = INT_TO_Q52(phy->chandef->chan->max_power); + if (phy->desired_txpower) + max = min_t(u8, max, INT_TO_Q52(phy->desired_txpower)); + b43_ppr_apply_max(dev, ppr, max); + if (b43_debug(dev, B43_DBG_XMITPOWER)) + b43dbg(dev->wl, "Calculated TX power: " Q52_FMT "\n", + Q52_ARG(b43_ppr_get_max(dev, ppr))); + + /* TODO: Enable this once we get gains working */ +#if 0 + /* Some extra gains */ + hw_gain = 6; /* N-PHY specific */ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + hw_gain += sprom->antenna_gain.a0; + else + hw_gain += sprom->antenna_gain.a1; + b43_ppr_add(dev, ppr, -hw_gain); +#endif + + /* Make sure we didn't go too low */ + b43_ppr_apply_min(dev, ppr, INT_TO_Q52(8)); + + /* Apply */ + tx_pwr_state = nphy->txpwrctrl; + b43_mac_suspend(dev); + b43_nphy_tx_power_ctl_setup(dev); + if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) { + b43_maskset32(dev, B43_MMIO_MACCTL, ~0, B43_MACCTL_PHY_LOCK); + b43_read32(dev, B43_MMIO_MACCTL); + udelay(1); + } + b43_nphy_tx_power_ctrl(dev, nphy->txpwrctrl); + if (dev->dev->core_rev == 11 || dev->dev->core_rev == 12) + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PHY_LOCK, 0); + b43_mac_enable(dev); + + nphy->tx_pwr_last_recalc_freq = channel->center_freq; + nphy->tx_pwr_last_recalc_limit = phy->desired_txpower; + + return B43_TXPWR_RES_DONE; +} + +/************************************************** + * N-PHY init + **************************************************/ + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/MIMOConfig */ +static void b43_nphy_update_mimo_config(struct b43_wldev *dev, s32 preamble) +{ + u16 mimocfg = b43_phy_read(dev, B43_NPHY_MIMOCFG); + + mimocfg |= B43_NPHY_MIMOCFG_AUTO; + if (preamble == 1) + mimocfg |= B43_NPHY_MIMOCFG_GFMIX; + else + mimocfg &= ~B43_NPHY_MIMOCFG_GFMIX; + + b43_phy_write(dev, B43_NPHY_MIMOCFG, mimocfg); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BPHYInit */ +static void b43_nphy_bphy_init(struct b43_wldev *dev) +{ + unsigned int i; + u16 val; + + val = 0x1E1F; + for (i = 0; i < 16; i++) { + b43_phy_write(dev, B43_PHY_N_BMODE(0x88 + i), val); + val -= 0x202; + } + val = 0x3E3F; + for (i = 0; i < 16; i++) { + b43_phy_write(dev, B43_PHY_N_BMODE(0x98 + i), val); + val -= 0x202; + } + b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SuperSwitchInit */ +static void b43_nphy_superswitch_init(struct b43_wldev *dev, bool init) +{ + if (dev->phy.rev >= 7) + return; + + if (dev->phy.rev >= 3) { + if (!init) + return; + if (0 /* FIXME */) { + b43_ntab_write(dev, B43_NTAB16(9, 2), 0x211); + b43_ntab_write(dev, B43_NTAB16(9, 3), 0x222); + b43_ntab_write(dev, B43_NTAB16(9, 8), 0x144); + b43_ntab_write(dev, B43_NTAB16(9, 12), 0x188); + } + } else { + b43_phy_write(dev, B43_NPHY_GPIO_LOOEN, 0); + b43_phy_write(dev, B43_NPHY_GPIO_HIOEN, 0); + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + bcma_chipco_gpio_control(&dev->dev->bdev->bus->drv_cc, + 0xFC00, 0xFC00); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + ssb_chipco_gpio_control(&dev->dev->sdev->bus->chipco, + 0xFC00, 0xFC00); + break; +#endif + } + + b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0); + b43_maskset16(dev, B43_MMIO_GPIO_MASK, ~0, 0xFC00); + b43_maskset16(dev, B43_MMIO_GPIO_CONTROL, (~0xFC00 & 0xFFFF), + 0); + + if (init) { + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/Init/N */ +static int b43_phy_initn(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = phy->n; + u8 tx_pwr_state; + struct nphy_txgains target; + u16 tmp; + enum ieee80211_band tmp2; + bool do_rssi_cal; + + u16 clip[2]; + bool do_cal = false; + + if ((dev->phy.rev >= 3) && + (sprom->boardflags_lo & B43_BFL_EXTLNA) && + (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) { + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + bcma_cc_set32(&dev->dev->bdev->bus->drv_cc, + BCMA_CC_CHIPCTL, 0x40); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + chipco_set32(&dev->dev->sdev->bus->chipco, + SSB_CHIPCO_CHIPCTL, 0x40); + break; +#endif + } + } + nphy->use_int_tx_iq_lo_cal = b43_nphy_ipa(dev) || + phy->rev >= 7 || + (phy->rev >= 5 && + sprom->boardflags2_hi & B43_BFH2_INTERNDET_TXIQCAL); + nphy->deaf_count = 0; + b43_nphy_tables_init(dev); + nphy->crsminpwr_adjusted = false; + nphy->noisevars_adjusted = false; + + /* Clear all overrides */ + if (dev->phy.rev >= 3) { + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, 0); + b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); + if (phy->rev >= 7) { + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER4, 0); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER5, 0); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER6, 0); + } + if (phy->rev >= 19) { + /* TODO */ + } + + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, 0); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, 0); + } else { + b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); + } + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, 0); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, 0); + if (dev->phy.rev < 6) { + b43_phy_write(dev, B43_NPHY_RFCTL_INTC3, 0); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC4, 0); + } + b43_phy_mask(dev, B43_NPHY_RFSEQMODE, + ~(B43_NPHY_RFSEQMODE_CAOVER | + B43_NPHY_RFSEQMODE_TROVER)); + if (dev->phy.rev >= 3) + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, 0); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, 0); + + if (dev->phy.rev <= 2) { + tmp = (dev->phy.rev == 2) ? 0x3B : 0x40; + b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, + ~B43_NPHY_BPHY_CTL3_SCALE, + tmp << B43_NPHY_BPHY_CTL3_SCALE_SHIFT); + } + b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_20M, 0x20); + b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_40M, 0x20); + + if (sprom->boardflags2_lo & B43_BFL2_SKWRKFEM_BRD || + (dev->dev->board_vendor == PCI_VENDOR_ID_APPLE && + dev->dev->board_type == BCMA_BOARD_TYPE_BCM943224M93)) + b43_phy_write(dev, B43_NPHY_TXREALFD, 0xA0); + else + b43_phy_write(dev, B43_NPHY_TXREALFD, 0xB8); + b43_phy_write(dev, B43_NPHY_MIMO_CRSTXEXT, 0xC8); + b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x50); + b43_phy_write(dev, B43_NPHY_TXRIFS_FRDEL, 0x30); + + if (phy->rev < 8) + b43_nphy_update_mimo_config(dev, nphy->preamble_override); + + b43_nphy_update_txrx_chain(dev); + + if (phy->rev < 2) { + b43_phy_write(dev, B43_NPHY_DUP40_GFBL, 0xAA8); + b43_phy_write(dev, B43_NPHY_DUP40_BL, 0x9A4); + } + + tmp2 = b43_current_band(dev->wl); + if (b43_nphy_ipa(dev)) { + b43_phy_set(dev, B43_NPHY_PAPD_EN0, 0x1); + b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ0, 0x007F, + nphy->papd_epsilon_offset[0] << 7); + b43_phy_set(dev, B43_NPHY_PAPD_EN1, 0x1); + b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ1, 0x007F, + nphy->papd_epsilon_offset[1] << 7); + b43_nphy_int_pa_set_tx_dig_filters(dev); + } else if (phy->rev >= 5) { + b43_nphy_ext_pa_set_tx_dig_filters(dev); + } + + b43_nphy_workarounds(dev); + + /* Reset CCA, in init code it differs a little from standard way */ + b43_phy_force_clock(dev, 1); + tmp = b43_phy_read(dev, B43_NPHY_BBCFG); + b43_phy_write(dev, B43_NPHY_BBCFG, tmp | B43_NPHY_BBCFG_RSTCCA); + b43_phy_write(dev, B43_NPHY_BBCFG, tmp & ~B43_NPHY_BBCFG_RSTCCA); + b43_phy_force_clock(dev, 0); + + b43_mac_phy_clock_set(dev, true); + + if (phy->rev < 7) { + b43_nphy_pa_override(dev, false); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + b43_nphy_pa_override(dev, true); + } + + b43_nphy_classifier(dev, 0, 0); + b43_nphy_read_clip_detection(dev, clip); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_nphy_bphy_init(dev); + + tx_pwr_state = nphy->txpwrctrl; + b43_nphy_tx_power_ctrl(dev, false); + b43_nphy_tx_power_fix(dev); + b43_nphy_tx_power_ctl_idle_tssi(dev); + b43_nphy_tx_power_ctl_setup(dev); + b43_nphy_tx_gain_table_upload(dev); + + if (nphy->phyrxchain != 3) + b43_nphy_set_rx_core_state(dev, nphy->phyrxchain); + if (nphy->mphase_cal_phase_id > 0) + ;/* TODO PHY Periodic Calibration Multi-Phase Restart */ + + do_rssi_cal = false; + if (phy->rev >= 3) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + do_rssi_cal = !nphy->rssical_chanspec_2G.center_freq; + else + do_rssi_cal = !nphy->rssical_chanspec_5G.center_freq; + + if (do_rssi_cal) + b43_nphy_rssi_cal(dev); + else + b43_nphy_restore_rssi_cal(dev); + } else { + b43_nphy_rssi_cal(dev); + } + + if (!((nphy->measure_hold & 0x6) != 0)) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + do_cal = !nphy->iqcal_chanspec_2G.center_freq; + else + do_cal = !nphy->iqcal_chanspec_5G.center_freq; + + if (nphy->mute) + do_cal = false; + + if (do_cal) { + target = b43_nphy_get_tx_gains(dev); + + if (nphy->antsel_type == 2) + b43_nphy_superswitch_init(dev, true); + if (nphy->perical != 2) { + b43_nphy_rssi_cal(dev); + if (phy->rev >= 3) { + nphy->cal_orig_pwr_idx[0] = + nphy->txpwrindex[0].index_internal; + nphy->cal_orig_pwr_idx[1] = + nphy->txpwrindex[1].index_internal; + /* TODO N PHY Pre Calibrate TX Gain */ + target = b43_nphy_get_tx_gains(dev); + } + if (!b43_nphy_cal_tx_iq_lo(dev, target, true, false)) + if (b43_nphy_cal_rx_iq(dev, target, 2, 0) == 0) + b43_nphy_save_cal(dev); + } else if (nphy->mphase_cal_phase_id == 0) + ;/* N PHY Periodic Calibration with arg 3 */ + } else { + b43_nphy_restore_cal(dev); + } + } + + b43_nphy_tx_pwr_ctrl_coef_setup(dev); + b43_nphy_tx_power_ctrl(dev, tx_pwr_state); + b43_phy_write(dev, B43_NPHY_TXMACIF_HOLDOFF, 0x0015); + b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320); + if (phy->rev >= 3 && phy->rev <= 6) + b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0032); + b43_nphy_tx_lpf_bw(dev); + if (phy->rev >= 3) + b43_nphy_spur_workaround(dev); + + return 0; +} + +/************************************************** + * Channel switching ops. + **************************************************/ + +static void b43_chantab_phy_upload(struct b43_wldev *dev, + const struct b43_phy_n_sfo_cfg *e) +{ + b43_phy_write(dev, B43_NPHY_BW1A, e->phy_bw1a); + b43_phy_write(dev, B43_NPHY_BW2, e->phy_bw2); + b43_phy_write(dev, B43_NPHY_BW3, e->phy_bw3); + b43_phy_write(dev, B43_NPHY_BW4, e->phy_bw4); + b43_phy_write(dev, B43_NPHY_BW5, e->phy_bw5); + b43_phy_write(dev, B43_NPHY_BW6, e->phy_bw6); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PmuSpurAvoid */ +static void b43_nphy_pmu_spur_avoid(struct b43_wldev *dev, bool avoid) +{ + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + bcma_pmu_spuravoid_pllupdate(&dev->dev->bdev->bus->drv_cc, + avoid); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + ssb_pmu_spuravoid_pllupdate(&dev->dev->sdev->bus->chipco, + avoid); + break; +#endif + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ChanspecSetup */ +static void b43_nphy_channel_setup(struct b43_wldev *dev, + const struct b43_phy_n_sfo_cfg *e, + struct ieee80211_channel *new_channel) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + int ch = new_channel->hw_value; + u16 tmp16; + + if (new_channel->band == IEEE80211_BAND_5GHZ) { + /* Switch to 2 GHz for a moment to access B43_PHY_B_BBCFG */ + b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ); + + tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4); + /* Put BPHY in the reset */ + b43_phy_set(dev, B43_PHY_B_BBCFG, + B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16); + b43_phy_set(dev, B43_NPHY_BANDCTL, B43_NPHY_BANDCTL_5GHZ); + } else if (new_channel->band == IEEE80211_BAND_2GHZ) { + b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ); + tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4); + /* Take BPHY out of the reset */ + b43_phy_mask(dev, B43_PHY_B_BBCFG, + (u16)~(B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX)); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16); + } + + b43_chantab_phy_upload(dev, e); + + if (new_channel->hw_value == 14) { + b43_nphy_classifier(dev, 2, 0); + b43_phy_set(dev, B43_PHY_B_TEST, 0x0800); + } else { + b43_nphy_classifier(dev, 2, 2); + if (new_channel->band == IEEE80211_BAND_2GHZ) + b43_phy_mask(dev, B43_PHY_B_TEST, ~0x840); + } + + if (!nphy->txpwrctrl) + b43_nphy_tx_power_fix(dev); + + if (dev->phy.rev < 3) + b43_nphy_adjust_lna_gain_table(dev); + + b43_nphy_tx_lpf_bw(dev); + + if (dev->phy.rev >= 3 && + dev->phy.n->spur_avoid != B43_SPUR_AVOID_DISABLE) { + u8 spuravoid = 0; + + if (dev->phy.n->spur_avoid == B43_SPUR_AVOID_FORCE) { + spuravoid = 1; + } else if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 18) { + /* TODO */ + } else if (phy->rev >= 17) { + /* TODO: Off for channels 1-11, but check 12-14! */ + } else if (phy->rev >= 16) { + /* TODO: Off for 2 GHz, but check 5 GHz! */ + } else if (phy->rev >= 7) { + if (!b43_is_40mhz(dev)) { /* 20MHz */ + if (ch == 13 || ch == 14 || ch == 153) + spuravoid = 1; + } else { /* 40 MHz */ + if (ch == 54) + spuravoid = 1; + } + } else { + if (!b43_is_40mhz(dev)) { /* 20MHz */ + if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14) + spuravoid = 1; + } else { /* 40MHz */ + if (nphy->aband_spurwar_en && + (ch == 38 || ch == 102 || ch == 118)) + spuravoid = dev->dev->chip_id == 0x4716; + } + } + + b43_nphy_pmu_spur_avoid(dev, spuravoid); + + b43_mac_switch_freq(dev, spuravoid); + + if (dev->phy.rev == 3 || dev->phy.rev == 4) + b43_wireless_core_phy_pll_reset(dev); + + if (spuravoid) + b43_phy_set(dev, B43_NPHY_BBCFG, B43_NPHY_BBCFG_RSTRX); + else + b43_phy_mask(dev, B43_NPHY_BBCFG, + ~B43_NPHY_BBCFG_RSTRX & 0xFFFF); + + b43_nphy_reset_cca(dev); + + /* wl sets useless phy_isspuravoid here */ + } + + b43_phy_write(dev, B43_NPHY_NDATAT_DUP40, 0x3830); + + if (phy->rev >= 3) + b43_nphy_spur_workaround(dev); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetChanspec */ +static int b43_nphy_set_channel(struct b43_wldev *dev, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type) +{ + struct b43_phy *phy = &dev->phy; + + const struct b43_nphy_channeltab_entry_rev2 *tabent_r2 = NULL; + const struct b43_nphy_channeltab_entry_rev3 *tabent_r3 = NULL; + const struct b43_nphy_chantabent_rev7 *tabent_r7 = NULL; + const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g = NULL; + + u8 tmp; + + if (phy->rev >= 19) { + return -ESRCH; + /* TODO */ + } else if (phy->rev >= 7) { + r2057_get_chantabent_rev7(dev, channel->center_freq, + &tabent_r7, &tabent_r7_2g); + if (!tabent_r7 && !tabent_r7_2g) + return -ESRCH; + } else if (phy->rev >= 3) { + tabent_r3 = b43_nphy_get_chantabent_rev3(dev, + channel->center_freq); + if (!tabent_r3) + return -ESRCH; + } else { + tabent_r2 = b43_nphy_get_chantabent_rev2(dev, + channel->hw_value); + if (!tabent_r2) + return -ESRCH; + } + + /* Channel is set later in common code, but we need to set it on our + own to let this function's subcalls work properly. */ + phy->channel = channel->hw_value; + +#if 0 + if (b43_channel_type_is_40mhz(phy->channel_type) != + b43_channel_type_is_40mhz(channel_type)) + ; /* TODO: BMAC BW Set (channel_type) */ +#endif + + if (channel_type == NL80211_CHAN_HT40PLUS) { + b43_phy_set(dev, B43_NPHY_RXCTL, B43_NPHY_RXCTL_BSELU20); + if (phy->rev >= 7) + b43_phy_set(dev, 0x310, 0x8000); + } else if (channel_type == NL80211_CHAN_HT40MINUS) { + b43_phy_mask(dev, B43_NPHY_RXCTL, ~B43_NPHY_RXCTL_BSELU20); + if (phy->rev >= 7) + b43_phy_mask(dev, 0x310, (u16)~0x8000); + } + + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + const struct b43_phy_n_sfo_cfg *phy_regs = tabent_r7 ? + &(tabent_r7->phy_regs) : &(tabent_r7_2g->phy_regs); + + if (phy->radio_rev <= 4 || phy->radio_rev == 6) { + tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 2 : 0; + b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE0, ~2, tmp); + b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE1, ~2, tmp); + } + + b43_radio_2057_setup(dev, tabent_r7, tabent_r7_2g); + b43_nphy_channel_setup(dev, phy_regs, channel); + } else if (phy->rev >= 3) { + tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 4 : 0; + b43_radio_maskset(dev, 0x08, 0xFFFB, tmp); + b43_radio_2056_setup(dev, tabent_r3); + b43_nphy_channel_setup(dev, &(tabent_r3->phy_regs), channel); + } else { + tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 0x0020 : 0x0050; + b43_radio_maskset(dev, B2055_MASTER1, 0xFF8F, tmp); + b43_radio_2055_setup(dev, tabent_r2); + b43_nphy_channel_setup(dev, &(tabent_r2->phy_regs), channel); + } + + return 0; +} + +/************************************************** + * Basic PHY ops. + **************************************************/ + +static int b43_nphy_op_allocate(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy; + + nphy = kzalloc(sizeof(*nphy), GFP_KERNEL); + if (!nphy) + return -ENOMEM; + + dev->phy.n = nphy; + + return 0; +} + +static void b43_nphy_op_prepare_structs(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = phy->n; + struct ssb_sprom *sprom = dev->dev->bus_sprom; + + memset(nphy, 0, sizeof(*nphy)); + + nphy->hang_avoid = (phy->rev == 3 || phy->rev == 4); + nphy->spur_avoid = (phy->rev >= 3) ? + B43_SPUR_AVOID_AUTO : B43_SPUR_AVOID_DISABLE; + nphy->gain_boost = true; /* this way we follow wl, assume it is true */ + nphy->txrx_chain = 2; /* sth different than 0 and 1 for now */ + nphy->phyrxchain = 3; /* to avoid b43_nphy_set_rx_core_state like wl */ + nphy->perical = 2; /* avoid additional rssi cal on init (like wl) */ + /* 128 can mean disabled-by-default state of TX pwr ctl. Max value is + * 0x7f == 127 and we check for 128 when restoring TX pwr ctl. */ + nphy->tx_pwr_idx[0] = 128; + nphy->tx_pwr_idx[1] = 128; + + /* Hardware TX power control and 5GHz power gain */ + nphy->txpwrctrl = false; + nphy->pwg_gain_5ghz = false; + if (dev->phy.rev >= 3 || + (dev->dev->board_vendor == PCI_VENDOR_ID_APPLE && + (dev->dev->core_rev == 11 || dev->dev->core_rev == 12))) { + nphy->txpwrctrl = true; + nphy->pwg_gain_5ghz = true; + } else if (sprom->revision >= 4) { + if (dev->phy.rev >= 2 && + (sprom->boardflags2_lo & B43_BFL2_TXPWRCTRL_EN)) { + nphy->txpwrctrl = true; +#ifdef CONFIG_B43_SSB + if (dev->dev->bus_type == B43_BUS_SSB && + dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI) { + struct pci_dev *pdev = + dev->dev->sdev->bus->host_pci; + if (pdev->device == 0x4328 || + pdev->device == 0x432a) + nphy->pwg_gain_5ghz = true; + } +#endif + } else if (sprom->boardflags2_lo & B43_BFL2_5G_PWRGAIN) { + nphy->pwg_gain_5ghz = true; + } + } + + if (dev->phy.rev >= 3) { + nphy->ipa2g_on = sprom->fem.ghz2.extpa_gain == 2; + nphy->ipa5g_on = sprom->fem.ghz5.extpa_gain == 2; + } +} + +static void b43_nphy_op_free(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = phy->n; + + kfree(nphy); + phy->n = NULL; +} + +static int b43_nphy_op_init(struct b43_wldev *dev) +{ + return b43_phy_initn(dev); +} + +static inline void check_phyreg(struct b43_wldev *dev, u16 offset) +{ +#if B43_DEBUG + if ((offset & B43_PHYROUTE) == B43_PHYROUTE_OFDM_GPHY) { + /* OFDM registers are onnly available on A/G-PHYs */ + b43err(dev->wl, "Invalid OFDM PHY access at " + "0x%04X on N-PHY\n", offset); + dump_stack(); + } + if ((offset & B43_PHYROUTE) == B43_PHYROUTE_EXT_GPHY) { + /* Ext-G registers are only available on G-PHYs */ + b43err(dev->wl, "Invalid EXT-G PHY access at " + "0x%04X on N-PHY\n", offset); + dump_stack(); + } +#endif /* B43_DEBUG */ +} + +static void b43_nphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, + u16 set) +{ + check_phyreg(dev, reg); + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + b43_maskset16(dev, B43_MMIO_PHY_DATA, mask, set); + dev->phy.writes_counter = 1; +} + +static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg) +{ + /* Register 1 is a 32-bit register. */ + B43_WARN_ON(dev->phy.rev < 7 && reg == 1); + + if (dev->phy.rev >= 7) + reg |= 0x200; /* Radio 0x2057 */ + else + reg |= 0x100; + + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); + return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); +} + +static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) +{ + /* Register 1 is a 32-bit register. */ + B43_WARN_ON(dev->phy.rev < 7 && reg == 1); + + b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg); + b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); +} + +/* http://bcm-v4.sipsolutions.net/802.11/Radio/Switch%20Radio */ +static void b43_nphy_op_software_rfkill(struct b43_wldev *dev, + bool blocked) +{ + struct b43_phy *phy = &dev->phy; + + if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED) + b43err(dev->wl, "MAC not suspended\n"); + + if (blocked) { + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 8) { + b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, + ~B43_NPHY_RFCTL_CMD_CHIP0PU); + } else if (phy->rev >= 7) { + /* Nothing needed */ + } else if (phy->rev >= 3) { + b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, + ~B43_NPHY_RFCTL_CMD_CHIP0PU); + + b43_radio_mask(dev, 0x09, ~0x2); + + b43_radio_write(dev, 0x204D, 0); + b43_radio_write(dev, 0x2053, 0); + b43_radio_write(dev, 0x2058, 0); + b43_radio_write(dev, 0x205E, 0); + b43_radio_mask(dev, 0x2062, ~0xF0); + b43_radio_write(dev, 0x2064, 0); + + b43_radio_write(dev, 0x304D, 0); + b43_radio_write(dev, 0x3053, 0); + b43_radio_write(dev, 0x3058, 0); + b43_radio_write(dev, 0x305E, 0); + b43_radio_mask(dev, 0x3062, ~0xF0); + b43_radio_write(dev, 0x3064, 0); + } + } else { + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + if (!dev->phy.radio_on) + b43_radio_2057_init(dev); + b43_switch_channel(dev, dev->phy.channel); + } else if (phy->rev >= 3) { + if (!dev->phy.radio_on) + b43_radio_init2056(dev); + b43_switch_channel(dev, dev->phy.channel); + } else { + b43_radio_init2055(dev); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/Anacore */ +static void b43_nphy_op_switch_analog(struct b43_wldev *dev, bool on) +{ + struct b43_phy *phy = &dev->phy; + u16 override = on ? 0x0 : 0x7FFF; + u16 core = on ? 0xD : 0x00FD; + + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 3) { + if (on) { + b43_phy_write(dev, B43_NPHY_AFECTL_C1, core); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, override); + b43_phy_write(dev, B43_NPHY_AFECTL_C2, core); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override); + } else { + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, override); + b43_phy_write(dev, B43_NPHY_AFECTL_C1, core); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override); + b43_phy_write(dev, B43_NPHY_AFECTL_C2, core); + } + } else { + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, override); + } +} + +static int b43_nphy_op_switch_channel(struct b43_wldev *dev, + unsigned int new_channel) +{ + struct ieee80211_channel *channel = dev->wl->hw->conf.chandef.chan; + enum nl80211_channel_type channel_type = + cfg80211_get_chandef_type(&dev->wl->hw->conf.chandef); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if ((new_channel < 1) || (new_channel > 14)) + return -EINVAL; + } else { + if (new_channel > 200) + return -EINVAL; + } + + return b43_nphy_set_channel(dev, channel, channel_type); +} + +static unsigned int b43_nphy_op_get_default_chan(struct b43_wldev *dev) +{ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + return 1; + return 36; +} + +const struct b43_phy_operations b43_phyops_n = { + .allocate = b43_nphy_op_allocate, + .free = b43_nphy_op_free, + .prepare_structs = b43_nphy_op_prepare_structs, + .init = b43_nphy_op_init, + .phy_maskset = b43_nphy_op_maskset, + .radio_read = b43_nphy_op_radio_read, + .radio_write = b43_nphy_op_radio_write, + .software_rfkill = b43_nphy_op_software_rfkill, + .switch_analog = b43_nphy_op_switch_analog, + .switch_channel = b43_nphy_op_switch_channel, + .get_default_chan = b43_nphy_op_get_default_chan, + .recalc_txpower = b43_nphy_op_recalc_txpower, +}; diff --git a/drivers/net/wireless/broadcom/b43/phy_n.h b/drivers/net/wireless/broadcom/b43/phy_n.h new file mode 100644 index 000000000000..a6da2c31a99c --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/phy_n.h @@ -0,0 +1,1007 @@ +#ifndef B43_NPHY_H_ +#define B43_NPHY_H_ + +#include "phy_common.h" +#include "ppr.h" + + +/* N-PHY registers. */ + +#define B43_NPHY_BBCFG B43_PHY_N(0x001) /* BB config */ +#define B43_NPHY_BBCFG_RSTCCA 0x4000 /* Reset CCA */ +#define B43_NPHY_BBCFG_RSTRX 0x8000 /* Reset RX */ +#define B43_NPHY_CHANNEL B43_PHY_N(0x005) /* Channel */ +#define B43_NPHY_TXERR B43_PHY_N(0x007) /* TX error */ +#define B43_NPHY_BANDCTL B43_PHY_N(0x009) /* Band control */ +#define B43_NPHY_BANDCTL_5GHZ 0x0001 /* Use the 5GHz band */ +#define B43_NPHY_4WI_ADDR B43_PHY_N(0x00B) /* Four-wire bus address */ +#define B43_NPHY_4WI_DATAHI B43_PHY_N(0x00C) /* Four-wire bus data high */ +#define B43_NPHY_4WI_DATALO B43_PHY_N(0x00D) /* Four-wire bus data low */ +#define B43_NPHY_BIST_STAT0 B43_PHY_N(0x00E) /* Built-in self test status 0 */ +#define B43_NPHY_BIST_STAT1 B43_PHY_N(0x00F) /* Built-in self test status 1 */ + +#define B43_NPHY_C1_DESPWR B43_PHY_N(0x018) /* Core 1 desired power */ +#define B43_NPHY_C1_CCK_DESPWR B43_PHY_N(0x019) /* Core 1 CCK desired power */ +#define B43_NPHY_C1_BCLIPBKOFF B43_PHY_N(0x01A) /* Core 1 barely clip backoff */ +#define B43_NPHY_C1_CCK_BCLIPBKOFF B43_PHY_N(0x01B) /* Core 1 CCK barely clip backoff */ +#define B43_NPHY_C1_CGAINI B43_PHY_N(0x01C) /* Core 1 compute gain info */ +#define B43_NPHY_C1_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ +#define B43_NPHY_C1_CGAINI_GAINBKOFF_SHIFT 0 +#define B43_NPHY_C1_CGAINI_CLIPGBKOFF 0x03E0 /* Clip gain backoff */ +#define B43_NPHY_C1_CGAINI_CLIPGBKOFF_SHIFT 5 +#define B43_NPHY_C1_CGAINI_GAINSTEP 0x1C00 /* Gain step */ +#define B43_NPHY_C1_CGAINI_GAINSTEP_SHIFT 10 +#define B43_NPHY_C1_CGAINI_CL2DETECT 0x2000 /* Clip 2 detect mask */ +#define B43_NPHY_C1_CCK_CGAINI B43_PHY_N(0x01D) /* Core 1 CCK compute gain info */ +#define B43_NPHY_C1_CCK_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ +#define B43_NPHY_C1_CCK_CGAINI_CLIPGBKOFF 0x01E0 /* CCK barely clip gain backoff */ +#define B43_NPHY_C1_MINMAX_GAIN B43_PHY_N(0x01E) /* Core 1 min/max gain */ +#define B43_NPHY_C1_MINGAIN 0x00FF /* Minimum gain */ +#define B43_NPHY_C1_MINGAIN_SHIFT 0 +#define B43_NPHY_C1_MAXGAIN 0xFF00 /* Maximum gain */ +#define B43_NPHY_C1_MAXGAIN_SHIFT 8 +#define B43_NPHY_C1_CCK_MINMAX_GAIN B43_PHY_N(0x01F) /* Core 1 CCK min/max gain */ +#define B43_NPHY_C1_CCK_MINGAIN 0x00FF /* Minimum gain */ +#define B43_NPHY_C1_CCK_MINGAIN_SHIFT 0 +#define B43_NPHY_C1_CCK_MAXGAIN 0xFF00 /* Maximum gain */ +#define B43_NPHY_C1_CCK_MAXGAIN_SHIFT 8 +#define B43_NPHY_C1_INITGAIN B43_PHY_N(0x020) /* Core 1 initial gain code */ +#define B43_NPHY_C1_INITGAIN_EXTLNA 0x0001 /* External LNA index */ +#define B43_NPHY_C1_INITGAIN_LNA 0x0006 /* LNA index */ +#define B43_NPHY_C1_INITGAIN_LNAIDX_SHIFT 1 +#define B43_NPHY_C1_INITGAIN_HPVGA1 0x0078 /* HPVGA1 index */ +#define B43_NPHY_C1_INITGAIN_HPVGA1_SHIFT 3 +#define B43_NPHY_C1_INITGAIN_HPVGA2 0x0F80 /* HPVGA2 index */ +#define B43_NPHY_C1_INITGAIN_HPVGA2_SHIFT 7 +#define B43_NPHY_C1_INITGAIN_TRRX 0x1000 /* TR RX index */ +#define B43_NPHY_C1_INITGAIN_TRTX 0x2000 /* TR TX index */ +#define B43_NPHY_REV3_C1_INITGAIN_A B43_PHY_N(0x020) +#define B43_NPHY_C1_CLIP1_HIGAIN B43_PHY_N(0x021) /* Core 1 clip1 high gain code */ +#define B43_NPHY_REV3_C1_INITGAIN_B B43_PHY_N(0x021) +#define B43_NPHY_C1_CLIP1_MEDGAIN B43_PHY_N(0x022) /* Core 1 clip1 medium gain code */ +#define B43_NPHY_REV3_C1_CLIP_HIGAIN_A B43_PHY_N(0x022) +#define B43_NPHY_C1_CLIP1_LOGAIN B43_PHY_N(0x023) /* Core 1 clip1 low gain code */ +#define B43_NPHY_REV3_C1_CLIP_HIGAIN_B B43_PHY_N(0x023) +#define B43_NPHY_C1_CLIP2_GAIN B43_PHY_N(0x024) /* Core 1 clip2 gain code */ +#define B43_NPHY_REV3_C1_CLIP_MEDGAIN_A B43_PHY_N(0x024) +#define B43_NPHY_C1_FILTERGAIN B43_PHY_N(0x025) /* Core 1 filter gain */ +#define B43_NPHY_C1_LPF_QHPF_BW B43_PHY_N(0x026) /* Core 1 LPF Q HP F bandwidth */ +#define B43_NPHY_C1_CLIPWBTHRES B43_PHY_N(0x027) /* Core 1 clip wideband threshold */ +#define B43_NPHY_C1_CLIPWBTHRES_CLIP2 0x003F /* Clip 2 */ +#define B43_NPHY_C1_CLIPWBTHRES_CLIP2_SHIFT 0 +#define B43_NPHY_C1_CLIPWBTHRES_CLIP1 0x0FC0 /* Clip 1 */ +#define B43_NPHY_C1_CLIPWBTHRES_CLIP1_SHIFT 6 +#define B43_NPHY_C1_W1THRES B43_PHY_N(0x028) /* Core 1 W1 threshold */ +#define B43_NPHY_C1_EDTHRES B43_PHY_N(0x029) /* Core 1 ED threshold */ +#define B43_NPHY_C1_SMSIGTHRES B43_PHY_N(0x02A) /* Core 1 small sig threshold */ +#define B43_NPHY_C1_NBCLIPTHRES B43_PHY_N(0x02B) /* Core 1 NB clip threshold */ +#define B43_NPHY_C1_CLIP1THRES B43_PHY_N(0x02C) /* Core 1 clip1 threshold */ +#define B43_NPHY_C1_CLIP2THRES B43_PHY_N(0x02D) /* Core 1 clip2 threshold */ + +#define B43_NPHY_C2_DESPWR B43_PHY_N(0x02E) /* Core 2 desired power */ +#define B43_NPHY_C2_CCK_DESPWR B43_PHY_N(0x02F) /* Core 2 CCK desired power */ +#define B43_NPHY_C2_BCLIPBKOFF B43_PHY_N(0x030) /* Core 2 barely clip backoff */ +#define B43_NPHY_C2_CCK_BCLIPBKOFF B43_PHY_N(0x031) /* Core 2 CCK barely clip backoff */ +#define B43_NPHY_C2_CGAINI B43_PHY_N(0x032) /* Core 2 compute gain info */ +#define B43_NPHY_C2_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ +#define B43_NPHY_C2_CGAINI_GAINBKOFF_SHIFT 0 +#define B43_NPHY_C2_CGAINI_CLIPGBKOFF 0x03E0 /* Clip gain backoff */ +#define B43_NPHY_C2_CGAINI_CLIPGBKOFF_SHIFT 5 +#define B43_NPHY_C2_CGAINI_GAINSTEP 0x1C00 /* Gain step */ +#define B43_NPHY_C2_CGAINI_GAINSTEP_SHIFT 10 +#define B43_NPHY_C2_CGAINI_CL2DETECT 0x2000 /* Clip 2 detect mask */ +#define B43_NPHY_C2_CCK_CGAINI B43_PHY_N(0x033) /* Core 2 CCK compute gain info */ +#define B43_NPHY_C2_CCK_CGAINI_GAINBKOFF 0x001F /* Gain backoff */ +#define B43_NPHY_C2_CCK_CGAINI_CLIPGBKOFF 0x01E0 /* CCK barely clip gain backoff */ +#define B43_NPHY_C2_MINMAX_GAIN B43_PHY_N(0x034) /* Core 2 min/max gain */ +#define B43_NPHY_C2_MINGAIN 0x00FF /* Minimum gain */ +#define B43_NPHY_C2_MINGAIN_SHIFT 0 +#define B43_NPHY_C2_MAXGAIN 0xFF00 /* Maximum gain */ +#define B43_NPHY_C2_MAXGAIN_SHIFT 8 +#define B43_NPHY_C2_CCK_MINMAX_GAIN B43_PHY_N(0x035) /* Core 2 CCK min/max gain */ +#define B43_NPHY_C2_CCK_MINGAIN 0x00FF /* Minimum gain */ +#define B43_NPHY_C2_CCK_MINGAIN_SHIFT 0 +#define B43_NPHY_C2_CCK_MAXGAIN 0xFF00 /* Maximum gain */ +#define B43_NPHY_C2_CCK_MAXGAIN_SHIFT 8 +#define B43_NPHY_C2_INITGAIN B43_PHY_N(0x036) /* Core 2 initial gain code */ +#define B43_NPHY_C2_INITGAIN_EXTLNA 0x0001 /* External LNA index */ +#define B43_NPHY_C2_INITGAIN_LNA 0x0006 /* LNA index */ +#define B43_NPHY_C2_INITGAIN_LNAIDX_SHIFT 1 +#define B43_NPHY_C2_INITGAIN_HPVGA1 0x0078 /* HPVGA1 index */ +#define B43_NPHY_C2_INITGAIN_HPVGA1_SHIFT 3 +#define B43_NPHY_C2_INITGAIN_HPVGA2 0x0F80 /* HPVGA2 index */ +#define B43_NPHY_C2_INITGAIN_HPVGA2_SHIFT 7 +#define B43_NPHY_C2_INITGAIN_TRRX 0x1000 /* TR RX index */ +#define B43_NPHY_C2_INITGAIN_TRTX 0x2000 /* TR TX index */ +#define B43_NPHY_REV3_C1_CLIP_MEDGAIN_B B43_PHY_N(0x036) +#define B43_NPHY_C2_CLIP1_HIGAIN B43_PHY_N(0x037) /* Core 2 clip1 high gain code */ +#define B43_NPHY_REV3_C1_CLIP_LOGAIN_A B43_PHY_N(0x037) +#define B43_NPHY_C2_CLIP1_MEDGAIN B43_PHY_N(0x038) /* Core 2 clip1 medium gain code */ +#define B43_NPHY_REV3_C1_CLIP_LOGAIN_B B43_PHY_N(0x038) +#define B43_NPHY_C2_CLIP1_LOGAIN B43_PHY_N(0x039) /* Core 2 clip1 low gain code */ +#define B43_NPHY_REV3_C1_CLIP2_GAIN_A B43_PHY_N(0x039) +#define B43_NPHY_C2_CLIP2_GAIN B43_PHY_N(0x03A) /* Core 2 clip2 gain code */ +#define B43_NPHY_REV3_C1_CLIP2_GAIN_B B43_PHY_N(0x03A) +#define B43_NPHY_C2_FILTERGAIN B43_PHY_N(0x03B) /* Core 2 filter gain */ +#define B43_NPHY_C2_LPF_QHPF_BW B43_PHY_N(0x03C) /* Core 2 LPF Q HP F bandwidth */ +#define B43_NPHY_C2_CLIPWBTHRES B43_PHY_N(0x03D) /* Core 2 clip wideband threshold */ +#define B43_NPHY_C2_CLIPWBTHRES_CLIP2 0x003F /* Clip 2 */ +#define B43_NPHY_C2_CLIPWBTHRES_CLIP2_SHIFT 0 +#define B43_NPHY_C2_CLIPWBTHRES_CLIP1 0x0FC0 /* Clip 1 */ +#define B43_NPHY_C2_CLIPWBTHRES_CLIP1_SHIFT 6 +#define B43_NPHY_C2_W1THRES B43_PHY_N(0x03E) /* Core 2 W1 threshold */ +#define B43_NPHY_C2_EDTHRES B43_PHY_N(0x03F) /* Core 2 ED threshold */ +#define B43_NPHY_C2_SMSIGTHRES B43_PHY_N(0x040) /* Core 2 small sig threshold */ +#define B43_NPHY_C2_NBCLIPTHRES B43_PHY_N(0x041) /* Core 2 NB clip threshold */ +#define B43_NPHY_C2_CLIP1THRES B43_PHY_N(0x042) /* Core 2 clip1 threshold */ +#define B43_NPHY_C2_CLIP2THRES B43_PHY_N(0x043) /* Core 2 clip2 threshold */ + +#define B43_NPHY_CRS_THRES1 B43_PHY_N(0x044) /* CRS threshold 1 */ +#define B43_NPHY_CRS_THRES2 B43_PHY_N(0x045) /* CRS threshold 2 */ +#define B43_NPHY_CRS_THRES3 B43_PHY_N(0x046) /* CRS threshold 3 */ +#define B43_NPHY_CRSCTL B43_PHY_N(0x047) /* CRS control */ +#define B43_NPHY_DCFADDR B43_PHY_N(0x048) /* DC filter address */ +#define B43_NPHY_RXF20_NUM0 B43_PHY_N(0x049) /* RX filter 20 numerator 0 */ +#define B43_NPHY_RXF20_NUM1 B43_PHY_N(0x04A) /* RX filter 20 numerator 1 */ +#define B43_NPHY_RXF20_NUM2 B43_PHY_N(0x04B) /* RX filter 20 numerator 2 */ +#define B43_NPHY_RXF20_DENOM0 B43_PHY_N(0x04C) /* RX filter 20 denominator 0 */ +#define B43_NPHY_RXF20_DENOM1 B43_PHY_N(0x04D) /* RX filter 20 denominator 1 */ +#define B43_NPHY_RXF20_NUM10 B43_PHY_N(0x04E) /* RX filter 20 numerator 10 */ +#define B43_NPHY_RXF20_NUM11 B43_PHY_N(0x04F) /* RX filter 20 numerator 11 */ +#define B43_NPHY_RXF20_NUM12 B43_PHY_N(0x050) /* RX filter 20 numerator 12 */ +#define B43_NPHY_RXF20_DENOM10 B43_PHY_N(0x051) /* RX filter 20 denominator 10 */ +#define B43_NPHY_RXF20_DENOM11 B43_PHY_N(0x052) /* RX filter 20 denominator 11 */ +#define B43_NPHY_RXF40_NUM0 B43_PHY_N(0x053) /* RX filter 40 numerator 0 */ +#define B43_NPHY_RXF40_NUM1 B43_PHY_N(0x054) /* RX filter 40 numerator 1 */ +#define B43_NPHY_RXF40_NUM2 B43_PHY_N(0x055) /* RX filter 40 numerator 2 */ +#define B43_NPHY_RXF40_DENOM0 B43_PHY_N(0x056) /* RX filter 40 denominator 0 */ +#define B43_NPHY_RXF40_DENOM1 B43_PHY_N(0x057) /* RX filter 40 denominator 1 */ +#define B43_NPHY_RXF40_NUM10 B43_PHY_N(0x058) /* RX filter 40 numerator 10 */ +#define B43_NPHY_RXF40_NUM11 B43_PHY_N(0x059) /* RX filter 40 numerator 11 */ +#define B43_NPHY_RXF40_NUM12 B43_PHY_N(0x05A) /* RX filter 40 numerator 12 */ +#define B43_NPHY_RXF40_DENOM10 B43_PHY_N(0x05B) /* RX filter 40 denominator 10 */ +#define B43_NPHY_RXF40_DENOM11 B43_PHY_N(0x05C) /* RX filter 40 denominator 11 */ +#define B43_NPHY_PPROC_RSTLEN B43_PHY_N(0x060) /* Packet processing reset length */ +#define B43_NPHY_INITCARR_DLEN B43_PHY_N(0x061) /* Initial carrier detection length */ +#define B43_NPHY_CLIP1CARR_DLEN B43_PHY_N(0x062) /* Clip1 carrier detection length */ +#define B43_NPHY_CLIP2CARR_DLEN B43_PHY_N(0x063) /* Clip2 carrier detection length */ +#define B43_NPHY_INITGAIN_SLEN B43_PHY_N(0x064) /* Initial gain settle length */ +#define B43_NPHY_CLIP1GAIN_SLEN B43_PHY_N(0x065) /* Clip1 gain settle length */ +#define B43_NPHY_CLIP2GAIN_SLEN B43_PHY_N(0x066) /* Clip2 gain settle length */ +#define B43_NPHY_PACKGAIN_SLEN B43_PHY_N(0x067) /* Packet gain settle length */ +#define B43_NPHY_CARRSRC_TLEN B43_PHY_N(0x068) /* Carrier search timeout length */ +#define B43_NPHY_TISRC_TLEN B43_PHY_N(0x069) /* Timing search timeout length */ +#define B43_NPHY_ENDROP_TLEN B43_PHY_N(0x06A) /* Energy drop timeout length */ +#define B43_NPHY_CLIP1_NBDWELL_LEN B43_PHY_N(0x06B) /* Clip1 NB dwell length */ +#define B43_NPHY_CLIP2_NBDWELL_LEN B43_PHY_N(0x06C) /* Clip2 NB dwell length */ +#define B43_NPHY_W1CLIP1_DWELL_LEN B43_PHY_N(0x06D) /* W1 clip1 dwell length */ +#define B43_NPHY_W1CLIP2_DWELL_LEN B43_PHY_N(0x06E) /* W1 clip2 dwell length */ +#define B43_NPHY_W2CLIP1_DWELL_LEN B43_PHY_N(0x06F) /* W2 clip1 dwell length */ +#define B43_NPHY_PLOAD_CSENSE_EXTLEN B43_PHY_N(0x070) /* Payload carrier sense extension length */ +#define B43_NPHY_EDROP_CSENSE_EXTLEN B43_PHY_N(0x071) /* Energy drop carrier sense extension length */ +#define B43_NPHY_TABLE_ADDR B43_PHY_N(0x072) /* Table address */ +#define B43_NPHY_TABLE_DATALO B43_PHY_N(0x073) /* Table data low */ +#define B43_NPHY_TABLE_DATAHI B43_PHY_N(0x074) /* Table data high */ +#define B43_NPHY_WWISE_LENIDX B43_PHY_N(0x075) /* WWiSE length index */ +#define B43_NPHY_TGNSYNC_LENIDX B43_PHY_N(0x076) /* TGNsync length index */ +#define B43_NPHY_TXMACIF_HOLDOFF B43_PHY_N(0x077) /* TX MAC IF Hold off */ +#define B43_NPHY_RFCTL_CMD B43_PHY_N(0x078) /* RF control (command) */ +#define B43_NPHY_RFCTL_CMD_START 0x0001 /* Start sequence */ +#define B43_NPHY_RFCTL_CMD_RXTX 0x0002 /* RX/TX */ +#define B43_NPHY_RFCTL_CMD_CORESEL 0x0038 /* Core select */ +#define B43_NPHY_RFCTL_CMD_CORESEL_SHIFT 3 +#define B43_NPHY_RFCTL_CMD_PORFORCE 0x0040 /* POR force */ +#define B43_NPHY_RFCTL_CMD_OEPORFORCE 0x0080 /* OE POR force */ +#define B43_NPHY_RFCTL_CMD_RXEN 0x0100 /* RX enable */ +#define B43_NPHY_RFCTL_CMD_TXEN 0x0200 /* TX enable */ +#define B43_NPHY_RFCTL_CMD_CHIP0PU 0x0400 /* Chip0 PU */ +#define B43_NPHY_RFCTL_CMD_EN 0x0800 /* Radio enabled */ +#define B43_NPHY_RFCTL_CMD_SEQENCORE 0xF000 /* Seq en core */ +#define B43_NPHY_RFCTL_CMD_SEQENCORE_SHIFT 12 +#define B43_NPHY_RFCTL_RSSIO1 B43_PHY_N(0x07A) /* RF control (RSSI others 1) */ +#define B43_NPHY_RFCTL_RSSIO1_RXPD 0x0001 /* RX PD */ +#define B43_NPHY_RFCTL_RSSIO1_TXPD 0x0002 /* TX PD */ +#define B43_NPHY_RFCTL_RSSIO1_PAPD 0x0004 /* PA PD */ +#define B43_NPHY_RFCTL_RSSIO1_RSSICTL 0x0030 /* RSSI control */ +#define B43_NPHY_RFCTL_RSSIO1_LPFBW 0x00C0 /* LPF bandwidth */ +#define B43_NPHY_RFCTL_RSSIO1_HPFBWHI 0x0100 /* HPF bandwidth high */ +#define B43_NPHY_RFCTL_RSSIO1_HIQDISCO 0x0200 /* HIQ dis core */ +#define B43_NPHY_RFCTL_RXG1 B43_PHY_N(0x07B) /* RF control (RX gain 1) */ +#define B43_NPHY_RFCTL_TXG1 B43_PHY_N(0x07C) /* RF control (TX gain 1) */ +#define B43_NPHY_RFCTL_RSSIO2 B43_PHY_N(0x07D) /* RF control (RSSI others 2) */ +#define B43_NPHY_RFCTL_RSSIO2_RXPD 0x0001 /* RX PD */ +#define B43_NPHY_RFCTL_RSSIO2_TXPD 0x0002 /* TX PD */ +#define B43_NPHY_RFCTL_RSSIO2_PAPD 0x0004 /* PA PD */ +#define B43_NPHY_RFCTL_RSSIO2_RSSICTL 0x0030 /* RSSI control */ +#define B43_NPHY_RFCTL_RSSIO2_LPFBW 0x00C0 /* LPF bandwidth */ +#define B43_NPHY_RFCTL_RSSIO2_HPFBWHI 0x0100 /* HPF bandwidth high */ +#define B43_NPHY_RFCTL_RSSIO2_HIQDISCO 0x0200 /* HIQ dis core */ +#define B43_NPHY_RFCTL_RXG2 B43_PHY_N(0x07E) /* RF control (RX gain 2) */ +#define B43_NPHY_RFCTL_TXG2 B43_PHY_N(0x07F) /* RF control (TX gain 2) */ +#define B43_NPHY_RFCTL_RSSIO3 B43_PHY_N(0x080) /* RF control (RSSI others 3) */ +#define B43_NPHY_RFCTL_RSSIO3_RXPD 0x0001 /* RX PD */ +#define B43_NPHY_RFCTL_RSSIO3_TXPD 0x0002 /* TX PD */ +#define B43_NPHY_RFCTL_RSSIO3_PAPD 0x0004 /* PA PD */ +#define B43_NPHY_RFCTL_RSSIO3_RSSICTL 0x0030 /* RSSI control */ +#define B43_NPHY_RFCTL_RSSIO3_LPFBW 0x00C0 /* LPF bandwidth */ +#define B43_NPHY_RFCTL_RSSIO3_HPFBWHI 0x0100 /* HPF bandwidth high */ +#define B43_NPHY_RFCTL_RSSIO3_HIQDISCO 0x0200 /* HIQ dis core */ +#define B43_NPHY_RFCTL_RXG3 B43_PHY_N(0x081) /* RF control (RX gain 3) */ +#define B43_NPHY_RFCTL_TXG3 B43_PHY_N(0x082) /* RF control (TX gain 3) */ +#define B43_NPHY_RFCTL_RSSIO4 B43_PHY_N(0x083) /* RF control (RSSI others 4) */ +#define B43_NPHY_RFCTL_RSSIO4_RXPD 0x0001 /* RX PD */ +#define B43_NPHY_RFCTL_RSSIO4_TXPD 0x0002 /* TX PD */ +#define B43_NPHY_RFCTL_RSSIO4_PAPD 0x0004 /* PA PD */ +#define B43_NPHY_RFCTL_RSSIO4_RSSICTL 0x0030 /* RSSI control */ +#define B43_NPHY_RFCTL_RSSIO4_LPFBW 0x00C0 /* LPF bandwidth */ +#define B43_NPHY_RFCTL_RSSIO4_HPFBWHI 0x0100 /* HPF bandwidth high */ +#define B43_NPHY_RFCTL_RSSIO4_HIQDISCO 0x0200 /* HIQ dis core */ +#define B43_NPHY_RFCTL_RXG4 B43_PHY_N(0x084) /* RF control (RX gain 4) */ +#define B43_NPHY_RFCTL_TXG4 B43_PHY_N(0x085) /* RF control (TX gain 4) */ +#define B43_NPHY_C1_TXIQ_COMP_OFF B43_PHY_N(0x087) /* Core 1 TX I/Q comp offset */ +#define B43_NPHY_C2_TXIQ_COMP_OFF B43_PHY_N(0x088) /* Core 2 TX I/Q comp offset */ +#define B43_NPHY_C1_TXCTL B43_PHY_N(0x08B) /* Core 1 TX control */ +#define B43_NPHY_C2_TXCTL B43_PHY_N(0x08C) /* Core 2 TX control */ +#define B43_NPHY_AFECTL_OVER1 B43_PHY_N(0x08F) /* AFE control override 1 */ +#define B43_NPHY_SCRAM_SIGCTL B43_PHY_N(0x090) /* Scram signal control */ +#define B43_NPHY_SCRAM_SIGCTL_INITST 0x007F /* Initial state value */ +#define B43_NPHY_SCRAM_SIGCTL_INITST_SHIFT 0 +#define B43_NPHY_SCRAM_SIGCTL_SCM 0x0080 /* Scram control mode */ +#define B43_NPHY_SCRAM_SIGCTL_SICE 0x0100 /* Scram index control enable */ +#define B43_NPHY_SCRAM_SIGCTL_START 0xFE00 /* Scram start bit */ +#define B43_NPHY_SCRAM_SIGCTL_START_SHIFT 9 +#define B43_NPHY_RFCTL_INTC1 B43_PHY_N(0x091) /* RF control (intc 1) */ +#define B43_NPHY_RFCTL_INTC2 B43_PHY_N(0x092) /* RF control (intc 2) */ +#define B43_NPHY_RFCTL_INTC3 B43_PHY_N(0x093) /* RF control (intc 3) */ +#define B43_NPHY_RFCTL_INTC4 B43_PHY_N(0x094) /* RF control (intc 4) */ +#define B43_NPHY_NRDTO_WWISE B43_PHY_N(0x095) /* # datatones WWiSE */ +#define B43_NPHY_NRDTO_TGNSYNC B43_PHY_N(0x096) /* # datatones TGNsync */ +#define B43_NPHY_SIGFMOD_WWISE B43_PHY_N(0x097) /* Signal field mod WWiSE */ +#define B43_NPHY_LEG_SIGFMOD_11N B43_PHY_N(0x098) /* Legacy signal field mod 11n */ +#define B43_NPHY_HT_SIGFMOD_11N B43_PHY_N(0x099) /* HT signal field mod 11n */ +#define B43_NPHY_C1_RXIQ_COMPA0 B43_PHY_N(0x09A) /* Core 1 RX I/Q comp A0 */ +#define B43_NPHY_C1_RXIQ_COMPB0 B43_PHY_N(0x09B) /* Core 1 RX I/Q comp B0 */ +#define B43_NPHY_C2_RXIQ_COMPA1 B43_PHY_N(0x09C) /* Core 2 RX I/Q comp A1 */ +#define B43_NPHY_C2_RXIQ_COMPB1 B43_PHY_N(0x09D) /* Core 2 RX I/Q comp B1 */ +#define B43_NPHY_RXCTL B43_PHY_N(0x0A0) /* RX control */ +#define B43_NPHY_RXCTL_BSELU20 0x0010 /* Band select upper 20 */ +#define B43_NPHY_RXCTL_RIFSEN 0x0080 /* RIFS enable */ +#define B43_NPHY_RFSEQMODE B43_PHY_N(0x0A1) /* RF seq mode */ +#define B43_NPHY_RFSEQMODE_CAOVER 0x0001 /* Core active override */ +#define B43_NPHY_RFSEQMODE_TROVER 0x0002 /* Trigger override */ +#define B43_NPHY_RFSEQCA B43_PHY_N(0x0A2) /* RF seq core active */ +#define B43_NPHY_RFSEQCA_TXEN 0x000F /* TX enable */ +#define B43_NPHY_RFSEQCA_TXEN_SHIFT 0 +#define B43_NPHY_RFSEQCA_RXEN 0x00F0 /* RX enable */ +#define B43_NPHY_RFSEQCA_RXEN_SHIFT 4 +#define B43_NPHY_RFSEQCA_TXDIS 0x0F00 /* TX disable */ +#define B43_NPHY_RFSEQCA_TXDIS_SHIFT 8 +#define B43_NPHY_RFSEQCA_RXDIS 0xF000 /* RX disable */ +#define B43_NPHY_RFSEQCA_RXDIS_SHIFT 12 +#define B43_NPHY_RFSEQTR B43_PHY_N(0x0A3) /* RF seq trigger */ +#define B43_NPHY_RFSEQTR_RX2TX 0x0001 /* RX2TX */ +#define B43_NPHY_RFSEQTR_TX2RX 0x0002 /* TX2RX */ +#define B43_NPHY_RFSEQTR_UPGH 0x0004 /* Update gain H */ +#define B43_NPHY_RFSEQTR_UPGL 0x0008 /* Update gain L */ +#define B43_NPHY_RFSEQTR_UPGU 0x0010 /* Update gain U */ +#define B43_NPHY_RFSEQTR_RST2RX 0x0020 /* Reset to RX */ +#define B43_NPHY_RFSEQST B43_PHY_N(0x0A4) /* RF seq status. Values same as trigger. */ +#define B43_NPHY_AFECTL_OVER B43_PHY_N(0x0A5) /* AFE control override */ +#define B43_NPHY_AFECTL_C1 B43_PHY_N(0x0A6) /* AFE control core 1 */ +#define B43_NPHY_AFECTL_C2 B43_PHY_N(0x0A7) /* AFE control core 2 */ +#define B43_NPHY_AFECTL_C3 B43_PHY_N(0x0A8) /* AFE control core 3 */ +#define B43_NPHY_AFECTL_C4 B43_PHY_N(0x0A9) /* AFE control core 4 */ +#define B43_NPHY_AFECTL_DACGAIN1 B43_PHY_N(0x0AA) /* AFE control DAC gain 1 */ +#define B43_NPHY_AFECTL_DACGAIN2 B43_PHY_N(0x0AB) /* AFE control DAC gain 2 */ +#define B43_NPHY_AFECTL_DACGAIN3 B43_PHY_N(0x0AC) /* AFE control DAC gain 3 */ +#define B43_NPHY_AFECTL_DACGAIN4 B43_PHY_N(0x0AD) /* AFE control DAC gain 4 */ +#define B43_NPHY_STR_ADDR1 B43_PHY_N(0x0AE) /* STR address 1 */ +#define B43_NPHY_STR_ADDR2 B43_PHY_N(0x0AF) /* STR address 2 */ +#define B43_NPHY_CLASSCTL B43_PHY_N(0x0B0) /* Classifier control */ +#define B43_NPHY_CLASSCTL_CCKEN 0x0001 /* CCK enable */ +#define B43_NPHY_CLASSCTL_OFDMEN 0x0002 /* OFDM enable */ +#define B43_NPHY_CLASSCTL_WAITEDEN 0x0004 /* Waited enable */ +#define B43_NPHY_IQFLIP B43_PHY_N(0x0B1) /* I/Q flip */ +#define B43_NPHY_IQFLIP_ADC1 0x0001 /* ADC1 */ +#define B43_NPHY_IQFLIP_ADC2 0x0010 /* ADC2 */ +#define B43_NPHY_SISO_SNR_THRES B43_PHY_N(0x0B2) /* SISO SNR threshold */ +#define B43_NPHY_SIGMA_N_MULT B43_PHY_N(0x0B3) /* Sigma N multiplier */ +#define B43_NPHY_TXMACDELAY B43_PHY_N(0x0B4) /* TX MAC delay */ +#define B43_NPHY_TXFRAMEDELAY B43_PHY_N(0x0B5) /* TX frame delay */ +#define B43_NPHY_MLPARM B43_PHY_N(0x0B6) /* ML parameters */ +#define B43_NPHY_MLCTL B43_PHY_N(0x0B7) /* ML control */ +#define B43_NPHY_WWISE_20NCYCDAT B43_PHY_N(0x0B8) /* WWiSE 20 N cyc data */ +#define B43_NPHY_WWISE_40NCYCDAT B43_PHY_N(0x0B9) /* WWiSE 40 N cyc data */ +#define B43_NPHY_TGNSYNC_20NCYCDAT B43_PHY_N(0x0BA) /* TGNsync 20 N cyc data */ +#define B43_NPHY_TGNSYNC_40NCYCDAT B43_PHY_N(0x0BB) /* TGNsync 40 N cyc data */ +#define B43_NPHY_INITSWIZP B43_PHY_N(0x0BC) /* Initial swizzle pattern */ +#define B43_NPHY_TXTAILCNT B43_PHY_N(0x0BD) /* TX tail count value */ +#define B43_NPHY_BPHY_CTL1 B43_PHY_N(0x0BE) /* B PHY control 1 */ +#define B43_NPHY_BPHY_CTL2 B43_PHY_N(0x0BF) /* B PHY control 2 */ +#define B43_NPHY_BPHY_CTL2_LUT 0x001F /* LUT index */ +#define B43_NPHY_BPHY_CTL2_LUT_SHIFT 0 +#define B43_NPHY_BPHY_CTL2_MACDEL 0x7FE0 /* MAC delay */ +#define B43_NPHY_BPHY_CTL2_MACDEL_SHIFT 5 +#define B43_NPHY_IQLOCAL_CMD B43_PHY_N(0x0C0) /* I/Q LO cal command */ +#define B43_NPHY_IQLOCAL_CMD_EN 0x8000 +#define B43_NPHY_IQLOCAL_CMDNNUM B43_PHY_N(0x0C1) /* I/Q LO cal command N num */ +#define B43_NPHY_IQLOCAL_CMDGCTL B43_PHY_N(0x0C2) /* I/Q LO cal command G control */ +#define B43_NPHY_SAMP_CMD B43_PHY_N(0x0C3) /* Sample command */ +#define B43_NPHY_SAMP_CMD_STOP 0x0002 /* Stop */ +#define B43_NPHY_SAMP_LOOPCNT B43_PHY_N(0x0C4) /* Sample loop count */ +#define B43_NPHY_SAMP_WAITCNT B43_PHY_N(0x0C5) /* Sample wait count */ +#define B43_NPHY_SAMP_DEPCNT B43_PHY_N(0x0C6) /* Sample depth count */ +#define B43_NPHY_SAMP_STAT B43_PHY_N(0x0C7) /* Sample status */ +#define B43_NPHY_GPIO_LOOEN B43_PHY_N(0x0C8) /* GPIO low out enable */ +#define B43_NPHY_GPIO_HIOEN B43_PHY_N(0x0C9) /* GPIO high out enable */ +#define B43_NPHY_GPIO_SEL B43_PHY_N(0x0CA) /* GPIO select */ +#define B43_NPHY_GPIO_CLKCTL B43_PHY_N(0x0CB) /* GPIO clock control */ +#define B43_NPHY_TXF_20CO_AS0 B43_PHY_N(0x0CC) /* TX filter 20 coeff A stage 0 */ +#define B43_NPHY_TXF_20CO_AS1 B43_PHY_N(0x0CD) /* TX filter 20 coeff A stage 1 */ +#define B43_NPHY_TXF_20CO_AS2 B43_PHY_N(0x0CE) /* TX filter 20 coeff A stage 2 */ +#define B43_NPHY_TXF_20CO_B32S0 B43_PHY_N(0x0CF) /* TX filter 20 coeff B32 stage 0 */ +#define B43_NPHY_TXF_20CO_B1S0 B43_PHY_N(0x0D0) /* TX filter 20 coeff B1 stage 0 */ +#define B43_NPHY_TXF_20CO_B32S1 B43_PHY_N(0x0D1) /* TX filter 20 coeff B32 stage 1 */ +#define B43_NPHY_TXF_20CO_B1S1 B43_PHY_N(0x0D2) /* TX filter 20 coeff B1 stage 1 */ +#define B43_NPHY_TXF_20CO_B32S2 B43_PHY_N(0x0D3) /* TX filter 20 coeff B32 stage 2 */ +#define B43_NPHY_TXF_20CO_B1S2 B43_PHY_N(0x0D4) /* TX filter 20 coeff B1 stage 2 */ +#define B43_NPHY_SIGFLDTOL B43_PHY_N(0x0D5) /* Signal fld tolerance */ +#define B43_NPHY_TXSERFLD B43_PHY_N(0x0D6) /* TX service field */ +#define B43_NPHY_AFESEQ_RX2TX_PUD B43_PHY_N(0x0D7) /* AFE seq RX2TX power up/down delay */ +#define B43_NPHY_AFESEQ_TX2RX_PUD B43_PHY_N(0x0D8) /* AFE seq TX2RX power up/down delay */ +#define B43_NPHY_TGNSYNC_SCRAMI0 B43_PHY_N(0x0D9) /* TGNsync scram init 0 */ +#define B43_NPHY_TGNSYNC_SCRAMI1 B43_PHY_N(0x0DA) /* TGNsync scram init 1 */ +#define B43_NPHY_INITSWIZPATTLEG B43_PHY_N(0x0DB) /* Initial swizzle pattern leg */ +#define B43_NPHY_BPHY_CTL3 B43_PHY_N(0x0DC) /* B PHY control 3 */ +#define B43_NPHY_BPHY_CTL3_SCALE 0x00FF /* Scale */ +#define B43_NPHY_BPHY_CTL3_SCALE_SHIFT 0 +#define B43_NPHY_BPHY_CTL3_FSC 0xFF00 /* Frame start count value */ +#define B43_NPHY_BPHY_CTL3_FSC_SHIFT 8 +#define B43_NPHY_BPHY_CTL4 B43_PHY_N(0x0DD) /* B PHY control 4 */ +#define B43_NPHY_C1_TXBBMULT B43_PHY_N(0x0DE) /* Core 1 TX BB multiplier */ +#define B43_NPHY_C2_TXBBMULT B43_PHY_N(0x0DF) /* Core 2 TX BB multiplier */ +#define B43_NPHY_TXF_40CO_AS0 B43_PHY_N(0x0E1) /* TX filter 40 coeff A stage 0 */ +#define B43_NPHY_TXF_40CO_AS1 B43_PHY_N(0x0E2) /* TX filter 40 coeff A stage 1 */ +#define B43_NPHY_TXF_40CO_AS2 B43_PHY_N(0x0E3) /* TX filter 40 coeff A stage 2 */ +#define B43_NPHY_TXF_40CO_B32S0 B43_PHY_N(0x0E4) /* TX filter 40 coeff B32 stage 0 */ +#define B43_NPHY_TXF_40CO_B1S0 B43_PHY_N(0x0E5) /* TX filter 40 coeff B1 stage 0 */ +#define B43_NPHY_TXF_40CO_B32S1 B43_PHY_N(0x0E6) /* TX filter 40 coeff B32 stage 1 */ +#define B43_NPHY_TXF_40CO_B1S1 B43_PHY_N(0x0E7) /* TX filter 40 coeff B1 stage 1 */ +#define B43_NPHY_REV3_RFCTL_OVER0 B43_PHY_N(0x0E7) +#define B43_NPHY_TXF_40CO_B32S2 B43_PHY_N(0x0E8) /* TX filter 40 coeff B32 stage 2 */ +#define B43_NPHY_TXF_40CO_B1S2 B43_PHY_N(0x0E9) /* TX filter 40 coeff B1 stage 2 */ +#define B43_NPHY_BIST_STAT2 B43_PHY_N(0x0EA) /* BIST status 2 */ +#define B43_NPHY_BIST_STAT3 B43_PHY_N(0x0EB) /* BIST status 3 */ +#define B43_NPHY_RFCTL_OVER B43_PHY_N(0x0EC) /* RF control override */ +#define B43_NPHY_REV3_RFCTL_OVER1 B43_PHY_N(0x0EC) +#define B43_NPHY_MIMOCFG B43_PHY_N(0x0ED) /* MIMO config */ +#define B43_NPHY_MIMOCFG_GFMIX 0x0004 /* Greenfield or mixed mode */ +#define B43_NPHY_MIMOCFG_AUTO 0x0100 /* Greenfield/mixed mode auto */ +#define B43_NPHY_RADAR_BLNKCTL B43_PHY_N(0x0EE) /* Radar blank control */ +#define B43_NPHY_A0RADAR_FIFOCTL B43_PHY_N(0x0EF) /* Antenna 0 radar FIFO control */ +#define B43_NPHY_A1RADAR_FIFOCTL B43_PHY_N(0x0F0) /* Antenna 1 radar FIFO control */ +#define B43_NPHY_A0RADAR_FIFODAT B43_PHY_N(0x0F1) /* Antenna 0 radar FIFO data */ +#define B43_NPHY_A1RADAR_FIFODAT B43_PHY_N(0x0F2) /* Antenna 1 radar FIFO data */ +#define B43_NPHY_RADAR_THRES0 B43_PHY_N(0x0F3) /* Radar threshold 0 */ +#define B43_NPHY_RADAR_THRES1 B43_PHY_N(0x0F4) /* Radar threshold 1 */ +#define B43_NPHY_RADAR_THRES0R B43_PHY_N(0x0F5) /* Radar threshold 0R */ +#define B43_NPHY_RADAR_THRES1R B43_PHY_N(0x0F6) /* Radar threshold 1R */ +#define B43_NPHY_CSEN_20IN40_DLEN B43_PHY_N(0x0F7) /* Carrier sense 20 in 40 dwell length */ +#define B43_NPHY_RFCTL_LUT_TRSW_LO1 B43_PHY_N(0x0F8) /* RF control LUT TRSW lower 1 */ +#define B43_NPHY_RFCTL_LUT_TRSW_UP1 B43_PHY_N(0x0F9) /* RF control LUT TRSW upper 1 */ +#define B43_NPHY_RFCTL_LUT_TRSW_LO2 B43_PHY_N(0x0FA) /* RF control LUT TRSW lower 2 */ +#define B43_NPHY_RFCTL_LUT_TRSW_UP2 B43_PHY_N(0x0FB) /* RF control LUT TRSW upper 2 */ +#define B43_NPHY_RFCTL_LUT_TRSW_LO3 B43_PHY_N(0x0FC) /* RF control LUT TRSW lower 3 */ +#define B43_NPHY_RFCTL_LUT_TRSW_UP3 B43_PHY_N(0x0FD) /* RF control LUT TRSW upper 3 */ +#define B43_NPHY_RFCTL_LUT_TRSW_LO4 B43_PHY_N(0x0FE) /* RF control LUT TRSW lower 4 */ +#define B43_NPHY_RFCTL_LUT_TRSW_UP4 B43_PHY_N(0x0FF) /* RF control LUT TRSW upper 4 */ +#define B43_NPHY_RFCTL_LUT_LNAPA1 B43_PHY_N(0x100) /* RF control LUT LNA PA 1 */ +#define B43_NPHY_RFCTL_LUT_LNAPA2 B43_PHY_N(0x101) /* RF control LUT LNA PA 2 */ +#define B43_NPHY_RFCTL_LUT_LNAPA3 B43_PHY_N(0x102) /* RF control LUT LNA PA 3 */ +#define B43_NPHY_RFCTL_LUT_LNAPA4 B43_PHY_N(0x103) /* RF control LUT LNA PA 4 */ +#define B43_NPHY_TGNSYNC_CRCM0 B43_PHY_N(0x104) /* TGNsync CRC mask 0 */ +#define B43_NPHY_TGNSYNC_CRCM1 B43_PHY_N(0x105) /* TGNsync CRC mask 1 */ +#define B43_NPHY_TGNSYNC_CRCM2 B43_PHY_N(0x106) /* TGNsync CRC mask 2 */ +#define B43_NPHY_TGNSYNC_CRCM3 B43_PHY_N(0x107) /* TGNsync CRC mask 3 */ +#define B43_NPHY_TGNSYNC_CRCM4 B43_PHY_N(0x108) /* TGNsync CRC mask 4 */ +#define B43_NPHY_CRCPOLY B43_PHY_N(0x109) /* CRC polynomial */ +#define B43_NPHY_SIGCNT B43_PHY_N(0x10A) /* # sig count */ +#define B43_NPHY_SIGSTARTBIT_CTL B43_PHY_N(0x10B) /* Sig start bit control */ +#define B43_NPHY_CRCPOLY_ORDER B43_PHY_N(0x10C) /* CRC polynomial order */ +#define B43_NPHY_RFCTL_CST0 B43_PHY_N(0x10D) /* RF control core swap table 0 */ +#define B43_NPHY_RFCTL_CST1 B43_PHY_N(0x10E) /* RF control core swap table 1 */ +#define B43_NPHY_RFCTL_CST2O B43_PHY_N(0x10F) /* RF control core swap table 2 + others */ +#define B43_NPHY_BPHY_CTL5 B43_PHY_N(0x111) /* B PHY control 5 */ +#define B43_NPHY_RFSEQ_LPFBW B43_PHY_N(0x112) /* RF seq LPF bandwidth */ +#define B43_NPHY_TSSIBIAS1 B43_PHY_N(0x114) /* TSSI bias val 1 */ +#define B43_NPHY_TSSIBIAS2 B43_PHY_N(0x115) /* TSSI bias val 2 */ +#define B43_NPHY_TSSIBIAS_BIAS 0x00FF /* Bias */ +#define B43_NPHY_TSSIBIAS_BIAS_SHIFT 0 +#define B43_NPHY_TSSIBIAS_VAL 0xFF00 /* Value */ +#define B43_NPHY_TSSIBIAS_VAL_SHIFT 8 +#define B43_NPHY_ESTPWR1 B43_PHY_N(0x118) /* Estimated power 1 */ +#define B43_NPHY_ESTPWR2 B43_PHY_N(0x119) /* Estimated power 2 */ +#define B43_NPHY_ESTPWR_PWR 0x00FF /* Estimated power */ +#define B43_NPHY_ESTPWR_PWR_SHIFT 0 +#define B43_NPHY_ESTPWR_VALID 0x0100 /* Estimated power valid */ +#define B43_NPHY_TSSI_MAXTXFDT B43_PHY_N(0x11C) /* TSSI max TX frame delay time */ +#define B43_NPHY_TSSI_MAXTXFDT_VAL 0x00FF /* max TX frame delay time */ +#define B43_NPHY_TSSI_MAXTXFDT_VAL_SHIFT 0 +#define B43_NPHY_TSSI_MAXTDT B43_PHY_N(0x11D) /* TSSI max TSSI delay time */ +#define B43_NPHY_TSSI_MAXTDT_VAL 0x00FF /* max TSSI delay time */ +#define B43_NPHY_TSSI_MAXTDT_VAL_SHIFT 0 +#define B43_NPHY_ITSSI1 B43_PHY_N(0x11E) /* TSSI idle 1 */ +#define B43_NPHY_ITSSI2 B43_PHY_N(0x11F) /* TSSI idle 2 */ +#define B43_NPHY_ITSSI_VAL 0x00FF /* Idle TSSI */ +#define B43_NPHY_ITSSI_VAL_SHIFT 0 +#define B43_NPHY_TSSIMODE B43_PHY_N(0x122) /* TSSI mode */ +#define B43_NPHY_TSSIMODE_EN 0x0001 /* TSSI enable */ +#define B43_NPHY_TSSIMODE_PDEN 0x0002 /* Power det enable */ +#define B43_NPHY_RXMACIFM B43_PHY_N(0x123) /* RX Macif mode */ +#define B43_NPHY_CRSIT_COCNT_LO B43_PHY_N(0x124) /* CRS idle time CRS-on count (low) */ +#define B43_NPHY_CRSIT_COCNT_HI B43_PHY_N(0x125) /* CRS idle time CRS-on count (high) */ +#define B43_NPHY_CRSIT_MTCNT_LO B43_PHY_N(0x126) /* CRS idle time measure time count (low) */ +#define B43_NPHY_CRSIT_MTCNT_HI B43_PHY_N(0x127) /* CRS idle time measure time count (high) */ +#define B43_NPHY_SAMTWC B43_PHY_N(0x128) /* Sample tail wait count */ +#define B43_NPHY_IQEST_CMD B43_PHY_N(0x129) /* I/Q estimate command */ +#define B43_NPHY_IQEST_CMD_START 0x0001 /* Start */ +#define B43_NPHY_IQEST_CMD_MODE 0x0002 /* Mode */ +#define B43_NPHY_IQEST_WT B43_PHY_N(0x12A) /* I/Q estimate wait time */ +#define B43_NPHY_IQEST_WT_VAL 0x00FF /* Wait time */ +#define B43_NPHY_IQEST_WT_VAL_SHIFT 0 +#define B43_NPHY_IQEST_SAMCNT B43_PHY_N(0x12B) /* I/Q estimate sample count */ +#define B43_NPHY_IQEST_IQACC_LO0 B43_PHY_N(0x12C) /* I/Q estimate I/Q acc lo 0 */ +#define B43_NPHY_IQEST_IQACC_HI0 B43_PHY_N(0x12D) /* I/Q estimate I/Q acc hi 0 */ +#define B43_NPHY_IQEST_IPACC_LO0 B43_PHY_N(0x12E) /* I/Q estimate I power acc lo 0 */ +#define B43_NPHY_IQEST_IPACC_HI0 B43_PHY_N(0x12F) /* I/Q estimate I power acc hi 0 */ +#define B43_NPHY_IQEST_QPACC_LO0 B43_PHY_N(0x130) /* I/Q estimate Q power acc lo 0 */ +#define B43_NPHY_IQEST_QPACC_HI0 B43_PHY_N(0x131) /* I/Q estimate Q power acc hi 0 */ +#define B43_NPHY_IQEST_IQACC_LO1 B43_PHY_N(0x134) /* I/Q estimate I/Q acc lo 1 */ +#define B43_NPHY_IQEST_IQACC_HI1 B43_PHY_N(0x135) /* I/Q estimate I/Q acc hi 1 */ +#define B43_NPHY_IQEST_IPACC_LO1 B43_PHY_N(0x136) /* I/Q estimate I power acc lo 1 */ +#define B43_NPHY_IQEST_IPACC_HI1 B43_PHY_N(0x137) /* I/Q estimate I power acc hi 1 */ +#define B43_NPHY_IQEST_QPACC_LO1 B43_PHY_N(0x138) /* I/Q estimate Q power acc lo 1 */ +#define B43_NPHY_IQEST_QPACC_HI1 B43_PHY_N(0x139) /* I/Q estimate Q power acc hi 1 */ +#define B43_NPHY_MIMO_CRSTXEXT B43_PHY_N(0x13A) /* MIMO PHY CRS TX extension */ +#define B43_NPHY_PWRDET1 B43_PHY_N(0x13B) /* Power det 1 */ +#define B43_NPHY_PWRDET2 B43_PHY_N(0x13C) /* Power det 2 */ +#define B43_NPHY_MAXRSSI_DTIME B43_PHY_N(0x13F) /* RSSI max RSSI delay time */ +#define B43_NPHY_PIL_DW0 B43_PHY_N(0x141) /* Pilot data weight 0 */ +#define B43_NPHY_PIL_DW1 B43_PHY_N(0x142) /* Pilot data weight 1 */ +#define B43_NPHY_PIL_DW2 B43_PHY_N(0x143) /* Pilot data weight 2 */ +#define B43_NPHY_PIL_DW_BPSK 0x000F /* BPSK */ +#define B43_NPHY_PIL_DW_BPSK_SHIFT 0 +#define B43_NPHY_PIL_DW_QPSK 0x00F0 /* QPSK */ +#define B43_NPHY_PIL_DW_QPSK_SHIFT 4 +#define B43_NPHY_PIL_DW_16QAM 0x0F00 /* 16-QAM */ +#define B43_NPHY_PIL_DW_16QAM_SHIFT 8 +#define B43_NPHY_PIL_DW_64QAM 0xF000 /* 64-QAM */ +#define B43_NPHY_PIL_DW_64QAM_SHIFT 12 +#define B43_NPHY_FMDEM_CFG B43_PHY_N(0x144) /* FM demodulation config */ +#define B43_NPHY_PHASETR_A0 B43_PHY_N(0x145) /* Phase track alpha 0 */ +#define B43_NPHY_PHASETR_A1 B43_PHY_N(0x146) /* Phase track alpha 1 */ +#define B43_NPHY_PHASETR_A2 B43_PHY_N(0x147) /* Phase track alpha 2 */ +#define B43_NPHY_PHASETR_B0 B43_PHY_N(0x148) /* Phase track beta 0 */ +#define B43_NPHY_PHASETR_B1 B43_PHY_N(0x149) /* Phase track beta 1 */ +#define B43_NPHY_PHASETR_B2 B43_PHY_N(0x14A) /* Phase track beta 2 */ +#define B43_NPHY_PHASETR_CHG0 B43_PHY_N(0x14B) /* Phase track change 0 */ +#define B43_NPHY_PHASETR_CHG1 B43_PHY_N(0x14C) /* Phase track change 1 */ +#define B43_NPHY_PHASETW_OFF B43_PHY_N(0x14D) /* Phase track offset */ +#define B43_NPHY_RFCTL_DBG B43_PHY_N(0x14E) /* RF control debug */ +#define B43_NPHY_CCK_SHIFTB_REF B43_PHY_N(0x150) /* CCK shiftbits reference var */ +#define B43_NPHY_OVER_DGAIN0 B43_PHY_N(0x152) /* Override digital gain 0 */ +#define B43_NPHY_OVER_DGAIN1 B43_PHY_N(0x153) /* Override digital gain 1 */ +#define B43_NPHY_OVER_DGAIN_FDGV 0x0007 /* Force digital gain value */ +#define B43_NPHY_OVER_DGAIN_FDGV_SHIFT 0 +#define B43_NPHY_OVER_DGAIN_FDGEN 0x0008 /* Force digital gain enable */ +#define B43_NPHY_OVER_DGAIN_CCKDGECV 0xFF00 /* CCK digital gain enable count value */ +#define B43_NPHY_OVER_DGAIN_CCKDGECV_SHIFT 8 +#define B43_NPHY_BIST_STAT4 B43_PHY_N(0x156) /* BIST status 4 */ +#define B43_NPHY_RADAR_MAL B43_PHY_N(0x157) /* Radar MA length */ +#define B43_NPHY_RADAR_SRCCTL B43_PHY_N(0x158) /* Radar search control */ +#define B43_NPHY_VLD_DTSIG B43_PHY_N(0x159) /* VLD data tones sig */ +#define B43_NPHY_VLD_DTDAT B43_PHY_N(0x15A) /* VLD data tones data */ +#define B43_NPHY_C1_BPHY_RXIQCA0 B43_PHY_N(0x15B) /* Core 1 B PHY RX I/Q comp A0 */ +#define B43_NPHY_C1_BPHY_RXIQCB0 B43_PHY_N(0x15C) /* Core 1 B PHY RX I/Q comp B0 */ +#define B43_NPHY_C2_BPHY_RXIQCA1 B43_PHY_N(0x15D) /* Core 2 B PHY RX I/Q comp A1 */ +#define B43_NPHY_C2_BPHY_RXIQCB1 B43_PHY_N(0x15E) /* Core 2 B PHY RX I/Q comp B1 */ +#define B43_NPHY_FREQGAIN0 B43_PHY_N(0x160) /* Frequency gain 0 */ +#define B43_NPHY_FREQGAIN1 B43_PHY_N(0x161) /* Frequency gain 1 */ +#define B43_NPHY_FREQGAIN2 B43_PHY_N(0x162) /* Frequency gain 2 */ +#define B43_NPHY_FREQGAIN3 B43_PHY_N(0x163) /* Frequency gain 3 */ +#define B43_NPHY_FREQGAIN4 B43_PHY_N(0x164) /* Frequency gain 4 */ +#define B43_NPHY_FREQGAIN5 B43_PHY_N(0x165) /* Frequency gain 5 */ +#define B43_NPHY_FREQGAIN6 B43_PHY_N(0x166) /* Frequency gain 6 */ +#define B43_NPHY_FREQGAIN7 B43_PHY_N(0x167) /* Frequency gain 7 */ +#define B43_NPHY_FREQGAIN_BYPASS B43_PHY_N(0x168) /* Frequency gain bypass */ +#define B43_NPHY_TRLOSS B43_PHY_N(0x169) /* TR loss value */ +#define B43_NPHY_C1_ADCCLIP B43_PHY_N(0x16A) /* Core 1 ADC clip */ +#define B43_NPHY_C2_ADCCLIP B43_PHY_N(0x16B) /* Core 2 ADC clip */ +#define B43_NPHY_LTRN_OFFGAIN B43_PHY_N(0x16F) /* LTRN offset gain */ +#define B43_NPHY_LTRN_OFF B43_PHY_N(0x170) /* LTRN offset */ +#define B43_NPHY_NRDATAT_WWISE20SIG B43_PHY_N(0x171) /* # data tones WWiSE 20 sig */ +#define B43_NPHY_NRDATAT_WWISE40SIG B43_PHY_N(0x172) /* # data tones WWiSE 40 sig */ +#define B43_NPHY_NRDATAT_TGNSYNC20SIG B43_PHY_N(0x173) /* # data tones TGNsync 20 sig */ +#define B43_NPHY_NRDATAT_TGNSYNC40SIG B43_PHY_N(0x174) /* # data tones TGNsync 40 sig */ +#define B43_NPHY_WWISE_CRCM0 B43_PHY_N(0x175) /* WWiSE CRC mask 0 */ +#define B43_NPHY_WWISE_CRCM1 B43_PHY_N(0x176) /* WWiSE CRC mask 1 */ +#define B43_NPHY_WWISE_CRCM2 B43_PHY_N(0x177) /* WWiSE CRC mask 2 */ +#define B43_NPHY_WWISE_CRCM3 B43_PHY_N(0x178) /* WWiSE CRC mask 3 */ +#define B43_NPHY_WWISE_CRCM4 B43_PHY_N(0x179) /* WWiSE CRC mask 4 */ +#define B43_NPHY_CHANEST_CDDSH B43_PHY_N(0x17A) /* Channel estimate CDD shift */ +#define B43_NPHY_HTAGC_WCNT B43_PHY_N(0x17B) /* HT ADC wait counters */ +#define B43_NPHY_SQPARM B43_PHY_N(0x17C) /* SQ params */ +#define B43_NPHY_MCSDUP6M B43_PHY_N(0x17D) /* MCS dup 6M */ +#define B43_NPHY_NDATAT_DUP40 B43_PHY_N(0x17E) /* # data tones dup 40 */ +#define B43_NPHY_DUP40_TGNSYNC_CYCD B43_PHY_N(0x17F) /* Dup40 TGNsync cycle data */ +#define B43_NPHY_DUP40_GFBL B43_PHY_N(0x180) /* Dup40 GF format BL address */ +#define B43_NPHY_DUP40_BL B43_PHY_N(0x181) /* Dup40 format BL address */ +#define B43_NPHY_LEGDUP_FTA B43_PHY_N(0x182) /* Legacy dup frm table address */ +#define B43_NPHY_PACPROC_DBG B43_PHY_N(0x183) /* Packet processing debug */ +#define B43_NPHY_PIL_CYC1 B43_PHY_N(0x184) /* Pilot cycle counter 1 */ +#define B43_NPHY_PIL_CYC2 B43_PHY_N(0x185) /* Pilot cycle counter 2 */ +#define B43_NPHY_TXF_20CO_S0A1 B43_PHY_N(0x186) /* TX filter 20 coeff stage 0 A1 */ +#define B43_NPHY_TXF_20CO_S0A2 B43_PHY_N(0x187) /* TX filter 20 coeff stage 0 A2 */ +#define B43_NPHY_TXF_20CO_S1A1 B43_PHY_N(0x188) /* TX filter 20 coeff stage 1 A1 */ +#define B43_NPHY_TXF_20CO_S1A2 B43_PHY_N(0x189) /* TX filter 20 coeff stage 1 A2 */ +#define B43_NPHY_TXF_20CO_S2A1 B43_PHY_N(0x18A) /* TX filter 20 coeff stage 2 A1 */ +#define B43_NPHY_TXF_20CO_S2A2 B43_PHY_N(0x18B) /* TX filter 20 coeff stage 2 A2 */ +#define B43_NPHY_TXF_20CO_S0B1 B43_PHY_N(0x18C) /* TX filter 20 coeff stage 0 B1 */ +#define B43_NPHY_TXF_20CO_S0B2 B43_PHY_N(0x18D) /* TX filter 20 coeff stage 0 B2 */ +#define B43_NPHY_TXF_20CO_S0B3 B43_PHY_N(0x18E) /* TX filter 20 coeff stage 0 B3 */ +#define B43_NPHY_TXF_20CO_S1B1 B43_PHY_N(0x18F) /* TX filter 20 coeff stage 1 B1 */ +#define B43_NPHY_TXF_20CO_S1B2 B43_PHY_N(0x190) /* TX filter 20 coeff stage 1 B2 */ +#define B43_NPHY_TXF_20CO_S1B3 B43_PHY_N(0x191) /* TX filter 20 coeff stage 1 B3 */ +#define B43_NPHY_TXF_20CO_S2B1 B43_PHY_N(0x192) /* TX filter 20 coeff stage 2 B1 */ +#define B43_NPHY_TXF_20CO_S2B2 B43_PHY_N(0x193) /* TX filter 20 coeff stage 2 B2 */ +#define B43_NPHY_TXF_20CO_S2B3 B43_PHY_N(0x194) /* TX filter 20 coeff stage 2 B3 */ +#define B43_NPHY_TXF_40CO_S0A1 B43_PHY_N(0x195) /* TX filter 40 coeff stage 0 A1 */ +#define B43_NPHY_TXF_40CO_S0A2 B43_PHY_N(0x196) /* TX filter 40 coeff stage 0 A2 */ +#define B43_NPHY_TXF_40CO_S1A1 B43_PHY_N(0x197) /* TX filter 40 coeff stage 1 A1 */ +#define B43_NPHY_TXF_40CO_S1A2 B43_PHY_N(0x198) /* TX filter 40 coeff stage 1 A2 */ +#define B43_NPHY_TXF_40CO_S2A1 B43_PHY_N(0x199) /* TX filter 40 coeff stage 2 A1 */ +#define B43_NPHY_TXF_40CO_S2A2 B43_PHY_N(0x19A) /* TX filter 40 coeff stage 2 A2 */ +#define B43_NPHY_TXF_40CO_S0B1 B43_PHY_N(0x19B) /* TX filter 40 coeff stage 0 B1 */ +#define B43_NPHY_TXF_40CO_S0B2 B43_PHY_N(0x19C) /* TX filter 40 coeff stage 0 B2 */ +#define B43_NPHY_TXF_40CO_S0B3 B43_PHY_N(0x19D) /* TX filter 40 coeff stage 0 B3 */ +#define B43_NPHY_TXF_40CO_S1B1 B43_PHY_N(0x19E) /* TX filter 40 coeff stage 1 B1 */ +#define B43_NPHY_TXF_40CO_S1B2 B43_PHY_N(0x19F) /* TX filter 40 coeff stage 1 B2 */ +#define B43_NPHY_TXF_40CO_S1B3 B43_PHY_N(0x1A0) /* TX filter 40 coeff stage 1 B3 */ +#define B43_NPHY_TXF_40CO_S2B1 B43_PHY_N(0x1A1) /* TX filter 40 coeff stage 2 B1 */ +#define B43_NPHY_TXF_40CO_S2B2 B43_PHY_N(0x1A2) /* TX filter 40 coeff stage 2 B2 */ +#define B43_NPHY_TXF_40CO_S2B3 B43_PHY_N(0x1A3) /* TX filter 40 coeff stage 2 B3 */ +#define B43_NPHY_RSSIMC_0I_RSSI_X B43_PHY_N(0x1A4) /* RSSI multiplication coefficient 0 I RSSI X */ +#define B43_NPHY_RSSIMC_0I_RSSI_Y B43_PHY_N(0x1A5) /* RSSI multiplication coefficient 0 I RSSI Y */ +#define B43_NPHY_RSSIMC_0I_RSSI_Z B43_PHY_N(0x1A6) /* RSSI multiplication coefficient 0 I RSSI Z */ +#define B43_NPHY_RSSIMC_0I_TBD B43_PHY_N(0x1A7) /* RSSI multiplication coefficient 0 I TBD */ +#define B43_NPHY_RSSIMC_0I_PWRDET B43_PHY_N(0x1A8) /* RSSI multiplication coefficient 0 I power det */ +#define B43_NPHY_RSSIMC_0I_TSSI B43_PHY_N(0x1A9) /* RSSI multiplication coefficient 0 I TSSI */ +#define B43_NPHY_RSSIMC_0Q_RSSI_X B43_PHY_N(0x1AA) /* RSSI multiplication coefficient 0 Q RSSI X */ +#define B43_NPHY_RSSIMC_0Q_RSSI_Y B43_PHY_N(0x1AB) /* RSSI multiplication coefficient 0 Q RSSI Y */ +#define B43_NPHY_RSSIMC_0Q_RSSI_Z B43_PHY_N(0x1AC) /* RSSI multiplication coefficient 0 Q RSSI Z */ +#define B43_NPHY_RSSIMC_0Q_TBD B43_PHY_N(0x1AD) /* RSSI multiplication coefficient 0 Q TBD */ +#define B43_NPHY_RSSIMC_0Q_PWRDET B43_PHY_N(0x1AE) /* RSSI multiplication coefficient 0 Q power det */ +#define B43_NPHY_RSSIMC_0Q_TSSI B43_PHY_N(0x1AF) /* RSSI multiplication coefficient 0 Q TSSI */ +#define B43_NPHY_RSSIMC_1I_RSSI_X B43_PHY_N(0x1B0) /* RSSI multiplication coefficient 1 I RSSI X */ +#define B43_NPHY_RSSIMC_1I_RSSI_Y B43_PHY_N(0x1B1) /* RSSI multiplication coefficient 1 I RSSI Y */ +#define B43_NPHY_RSSIMC_1I_RSSI_Z B43_PHY_N(0x1B2) /* RSSI multiplication coefficient 1 I RSSI Z */ +#define B43_NPHY_RSSIMC_1I_TBD B43_PHY_N(0x1B3) /* RSSI multiplication coefficient 1 I TBD */ +#define B43_NPHY_RSSIMC_1I_PWRDET B43_PHY_N(0x1B4) /* RSSI multiplication coefficient 1 I power det */ +#define B43_NPHY_RSSIMC_1I_TSSI B43_PHY_N(0x1B5) /* RSSI multiplication coefficient 1 I TSSI */ +#define B43_NPHY_RSSIMC_1Q_RSSI_X B43_PHY_N(0x1B6) /* RSSI multiplication coefficient 1 Q RSSI X */ +#define B43_NPHY_RSSIMC_1Q_RSSI_Y B43_PHY_N(0x1B7) /* RSSI multiplication coefficient 1 Q RSSI Y */ +#define B43_NPHY_RSSIMC_1Q_RSSI_Z B43_PHY_N(0x1B8) /* RSSI multiplication coefficient 1 Q RSSI Z */ +#define B43_NPHY_RSSIMC_1Q_TBD B43_PHY_N(0x1B9) /* RSSI multiplication coefficient 1 Q TBD */ +#define B43_NPHY_RSSIMC_1Q_PWRDET B43_PHY_N(0x1BA) /* RSSI multiplication coefficient 1 Q power det */ +#define B43_NPHY_RSSIMC_1Q_TSSI B43_PHY_N(0x1BB) /* RSSI multiplication coefficient 1 Q TSSI */ +#define B43_NPHY_SAMC_WCNT B43_PHY_N(0x1BC) /* Sample collect wait counter */ +#define B43_NPHY_PTHROUGH_CNT B43_PHY_N(0x1BD) /* Pass-through counter */ +#define B43_NPHY_LTRN_OFF_G20L B43_PHY_N(0x1C4) /* LTRN offset gain 20L */ +#define B43_NPHY_LTRN_OFF_20L B43_PHY_N(0x1C5) /* LTRN offset 20L */ +#define B43_NPHY_LTRN_OFF_G20U B43_PHY_N(0x1C6) /* LTRN offset gain 20U */ +#define B43_NPHY_LTRN_OFF_20U B43_PHY_N(0x1C7) /* LTRN offset 20U */ +#define B43_NPHY_DSSSCCK_GAINSL B43_PHY_N(0x1C8) /* DSSS/CCK gain settle length */ +#define B43_NPHY_GPIO_LOOUT B43_PHY_N(0x1C9) /* GPIO low out */ +#define B43_NPHY_GPIO_HIOUT B43_PHY_N(0x1CA) /* GPIO high out */ +#define B43_NPHY_CRS_CHECK B43_PHY_N(0x1CB) /* CRS check */ +#define B43_NPHY_ML_LOGSS_RAT B43_PHY_N(0x1CC) /* ML/logss ratio */ +#define B43_NPHY_DUPSCALE B43_PHY_N(0x1CD) /* Dup scale */ +#define B43_NPHY_BW1A B43_PHY_N(0x1CE) /* BW 1A */ +#define B43_NPHY_BW2 B43_PHY_N(0x1CF) /* BW 2 */ +#define B43_NPHY_BW3 B43_PHY_N(0x1D0) /* BW 3 */ +#define B43_NPHY_BW4 B43_PHY_N(0x1D1) /* BW 4 */ +#define B43_NPHY_BW5 B43_PHY_N(0x1D2) /* BW 5 */ +#define B43_NPHY_BW6 B43_PHY_N(0x1D3) /* BW 6 */ +#define B43_NPHY_COALEN0 B43_PHY_N(0x1D4) /* Coarse length 0 */ +#define B43_NPHY_COALEN1 B43_PHY_N(0x1D5) /* Coarse length 1 */ +#define B43_NPHY_CRSTHRES_1U B43_PHY_N(0x1D6) /* CRS threshold 1 U */ +#define B43_NPHY_CRSTHRES_2U B43_PHY_N(0x1D7) /* CRS threshold 2 U */ +#define B43_NPHY_CRSTHRES_3U B43_PHY_N(0x1D8) /* CRS threshold 3 U */ +#define B43_NPHY_CRSCTL_U B43_PHY_N(0x1D9) /* CRS control U */ +#define B43_NPHY_CRSTHRES_1L B43_PHY_N(0x1DA) /* CRS threshold 1 L */ +#define B43_NPHY_CRSTHRES_2L B43_PHY_N(0x1DB) /* CRS threshold 2 L */ +#define B43_NPHY_CRSTHRES_3L B43_PHY_N(0x1DC) /* CRS threshold 3 L */ +#define B43_NPHY_CRSCTL_L B43_PHY_N(0x1DD) /* CRS control L */ +#define B43_NPHY_STRA_1U B43_PHY_N(0x1DE) /* STR address 1 U */ +#define B43_NPHY_STRA_2U B43_PHY_N(0x1DF) /* STR address 2 U */ +#define B43_NPHY_STRA_1L B43_PHY_N(0x1E0) /* STR address 1 L */ +#define B43_NPHY_STRA_2L B43_PHY_N(0x1E1) /* STR address 2 L */ +#define B43_NPHY_CRSCHECK1 B43_PHY_N(0x1E2) /* CRS check 1 */ +#define B43_NPHY_CRSCHECK2 B43_PHY_N(0x1E3) /* CRS check 2 */ +#define B43_NPHY_CRSCHECK3 B43_PHY_N(0x1E4) /* CRS check 3 */ +#define B43_NPHY_JMPSTP0 B43_PHY_N(0x1E5) /* Jump step 0 */ +#define B43_NPHY_JMPSTP1 B43_PHY_N(0x1E6) /* Jump step 1 */ +#define B43_NPHY_TXPCTL_CMD B43_PHY_N(0x1E7) /* TX power control command */ +#define B43_NPHY_TXPCTL_CMD_INIT 0x007F /* Init */ +#define B43_NPHY_TXPCTL_CMD_INIT_SHIFT 0 +#define B43_NPHY_TXPCTL_CMD_COEFF 0x2000 /* Power control coefficients */ +#define B43_NPHY_TXPCTL_CMD_HWPCTLEN 0x4000 /* Hardware TX power control enable */ +#define B43_NPHY_TXPCTL_CMD_PCTLEN 0x8000 /* TX power control enable */ +#define B43_NPHY_TXPCTL_N B43_PHY_N(0x1E8) /* TX power control N num */ +#define B43_NPHY_TXPCTL_N_TSSID 0x00FF /* N TSSI delay */ +#define B43_NPHY_TXPCTL_N_TSSID_SHIFT 0 +#define B43_NPHY_TXPCTL_N_NPTIL2 0x0700 /* N PT integer log2 */ +#define B43_NPHY_TXPCTL_N_NPTIL2_SHIFT 8 +#define B43_NPHY_TXPCTL_ITSSI B43_PHY_N(0x1E9) /* TX power control idle TSSI */ +#define B43_NPHY_TXPCTL_ITSSI_0 0x003F /* Idle TSSI 0 */ +#define B43_NPHY_TXPCTL_ITSSI_0_SHIFT 0 +#define B43_NPHY_TXPCTL_ITSSI_1 0x3F00 /* Idle TSSI 1 */ +#define B43_NPHY_TXPCTL_ITSSI_1_SHIFT 8 +#define B43_NPHY_TXPCTL_ITSSI_BINF 0x8000 /* Raw TSSI offset bin format */ +#define B43_NPHY_TXPCTL_TPWR B43_PHY_N(0x1EA) /* TX power control target power */ +#define B43_NPHY_TXPCTL_TPWR_0 0x00FF /* Power 0 */ +#define B43_NPHY_TXPCTL_TPWR_0_SHIFT 0 +#define B43_NPHY_TXPCTL_TPWR_1 0xFF00 /* Power 1 */ +#define B43_NPHY_TXPCTL_TPWR_1_SHIFT 8 +#define B43_NPHY_TXPCTL_BIDX B43_PHY_N(0x1EB) /* TX power control base index */ +#define B43_NPHY_TXPCTL_BIDX_0 0x007F /* uC base index 0 */ +#define B43_NPHY_TXPCTL_BIDX_0_SHIFT 0 +#define B43_NPHY_TXPCTL_BIDX_1 0x7F00 /* uC base index 1 */ +#define B43_NPHY_TXPCTL_BIDX_1_SHIFT 8 +#define B43_NPHY_TXPCTL_BIDX_LOAD 0x8000 /* Load base index */ +#define B43_NPHY_TXPCTL_PIDX B43_PHY_N(0x1EC) /* TX power control power index */ +#define B43_NPHY_TXPCTL_PIDX_0 0x007F /* uC power index 0 */ +#define B43_NPHY_TXPCTL_PIDX_0_SHIFT 0 +#define B43_NPHY_TXPCTL_PIDX_1 0x7F00 /* uC power index 1 */ +#define B43_NPHY_TXPCTL_PIDX_1_SHIFT 8 +#define B43_NPHY_C1_TXPCTL_STAT B43_PHY_N(0x1ED) /* Core 1 TX power control status */ +#define B43_NPHY_C2_TXPCTL_STAT B43_PHY_N(0x1EE) /* Core 2 TX power control status */ +#define B43_NPHY_TXPCTL_STAT_EST 0x00FF /* Estimated power */ +#define B43_NPHY_TXPCTL_STAT_EST_SHIFT 0 +#define B43_NPHY_TXPCTL_STAT_BIDX 0x7F00 /* Base index */ +#define B43_NPHY_TXPCTL_STAT_BIDX_SHIFT 8 +#define B43_NPHY_TXPCTL_STAT_ESTVALID 0x8000 /* Estimated power valid */ +#define B43_NPHY_SMALLSGS_LEN B43_PHY_N(0x1EF) /* Small sig gain settle length */ +#define B43_NPHY_PHYSTAT_GAIN0 B43_PHY_N(0x1F0) /* PHY stats gain info 0 */ +#define B43_NPHY_PHYSTAT_GAIN1 B43_PHY_N(0x1F1) /* PHY stats gain info 1 */ +#define B43_NPHY_PHYSTAT_FREQEST B43_PHY_N(0x1F2) /* PHY stats frequency estimate */ +#define B43_NPHY_PHYSTAT_ADVRET B43_PHY_N(0x1F3) /* PHY stats ADV retard */ +#define B43_NPHY_PHYLB_MODE B43_PHY_N(0x1F4) /* PHY loopback mode */ +#define B43_NPHY_TONE_MIDX20_1 B43_PHY_N(0x1F5) /* Tone map index 20/1 */ +#define B43_NPHY_TONE_MIDX20_2 B43_PHY_N(0x1F6) /* Tone map index 20/2 */ +#define B43_NPHY_TONE_MIDX20_3 B43_PHY_N(0x1F7) /* Tone map index 20/3 */ +#define B43_NPHY_TONE_MIDX40_1 B43_PHY_N(0x1F8) /* Tone map index 40/1 */ +#define B43_NPHY_TONE_MIDX40_2 B43_PHY_N(0x1F9) /* Tone map index 40/2 */ +#define B43_NPHY_TONE_MIDX40_3 B43_PHY_N(0x1FA) /* Tone map index 40/3 */ +#define B43_NPHY_TONE_MIDX40_4 B43_PHY_N(0x1FB) /* Tone map index 40/4 */ +#define B43_NPHY_PILTONE_MIDX1 B43_PHY_N(0x1FC) /* Pilot tone map index 1 */ +#define B43_NPHY_PILTONE_MIDX2 B43_PHY_N(0x1FD) /* Pilot tone map index 2 */ +#define B43_NPHY_PILTONE_MIDX3 B43_PHY_N(0x1FE) /* Pilot tone map index 3 */ +#define B43_NPHY_TXRIFS_FRDEL B43_PHY_N(0x1FF) /* TX RIFS frame delay */ +#define B43_NPHY_AFESEQ_RX2TX_PUD_40M B43_PHY_N(0x200) /* AFE seq rx2tx power up/down delay 40M */ +#define B43_NPHY_AFESEQ_TX2RX_PUD_40M B43_PHY_N(0x201) /* AFE seq tx2rx power up/down delay 40M */ +#define B43_NPHY_AFESEQ_RX2TX_PUD_20M B43_PHY_N(0x202) /* AFE seq rx2tx power up/down delay 20M */ +#define B43_NPHY_AFESEQ_TX2RX_PUD_20M B43_PHY_N(0x203) /* AFE seq tx2rx power up/down delay 20M */ +#define B43_NPHY_RX_SIGCTL B43_PHY_N(0x204) /* RX signal control */ +#define B43_NPHY_RXPIL_CYCNT0 B43_PHY_N(0x205) /* RX pilot cycle counter 0 */ +#define B43_NPHY_RXPIL_CYCNT1 B43_PHY_N(0x206) /* RX pilot cycle counter 1 */ +#define B43_NPHY_RXPIL_CYCNT2 B43_PHY_N(0x207) /* RX pilot cycle counter 2 */ +#define B43_NPHY_AFESEQ_RX2TX_PUD_10M B43_PHY_N(0x208) /* AFE seq rx2tx power up/down delay 10M */ +#define B43_NPHY_AFESEQ_TX2RX_PUD_10M B43_PHY_N(0x209) /* AFE seq tx2rx power up/down delay 10M */ +#define B43_NPHY_DSSSCCK_CRSEXTL B43_PHY_N(0x20A) /* DSSS/CCK CRS extension length */ +#define B43_NPHY_ML_LOGSS_RATSLOPE B43_PHY_N(0x20B) /* ML/logss ratio slope */ +#define B43_NPHY_RIFS_SRCTL B43_PHY_N(0x20C) /* RIFS search timeout length */ +#define B43_NPHY_TXREALFD B43_PHY_N(0x20D) /* TX real frame delay */ +#define B43_NPHY_HPANT_SWTHRES B43_PHY_N(0x20E) /* High power antenna switch threshold */ +#define B43_NPHY_EDCRS_ASSTHRES0 B43_PHY_N(0x210) /* ED CRS assert threshold 0 */ +#define B43_NPHY_EDCRS_ASSTHRES1 B43_PHY_N(0x211) /* ED CRS assert threshold 1 */ +#define B43_NPHY_EDCRS_DEASSTHRES0 B43_PHY_N(0x212) /* ED CRS deassert threshold 0 */ +#define B43_NPHY_EDCRS_DEASSTHRES1 B43_PHY_N(0x213) /* ED CRS deassert threshold 1 */ +#define B43_NPHY_STR_WTIME20U B43_PHY_N(0x214) /* STR wait time 20U */ +#define B43_NPHY_STR_WTIME20L B43_PHY_N(0x215) /* STR wait time 20L */ +#define B43_NPHY_TONE_MIDX657M B43_PHY_N(0x216) /* Tone map index 657M */ +#define B43_NPHY_HTSIGTONES B43_PHY_N(0x217) /* HT signal tones */ +#define B43_NPHY_RSSI1 B43_PHY_N(0x219) /* RSSI value 1 */ +#define B43_NPHY_RSSI2 B43_PHY_N(0x21A) /* RSSI value 2 */ +#define B43_NPHY_CHAN_ESTHANG B43_PHY_N(0x21D) /* Channel estimate hang */ +#define B43_NPHY_FINERX2_CGC B43_PHY_N(0x221) /* Fine RX 2 clock gate control */ +#define B43_NPHY_FINERX2_CGC_DECGC 0x0008 /* Decode gated clocks */ +#define B43_NPHY_TXPCTL_INIT B43_PHY_N(0x222) /* TX power control init */ +#define B43_NPHY_TXPCTL_INIT_PIDXI1 0x00FF /* Power index init 1 */ +#define B43_NPHY_TXPCTL_INIT_PIDXI1_SHIFT 0 +#define B43_NPHY_ED_CRSEN B43_PHY_N(0x223) +#define B43_NPHY_ED_CRS40ASSERTTHRESH0 B43_PHY_N(0x224) +#define B43_NPHY_ED_CRS40ASSERTTHRESH1 B43_PHY_N(0x225) +#define B43_NPHY_ED_CRS40DEASSERTTHRESH0 B43_PHY_N(0x226) +#define B43_NPHY_ED_CRS40DEASSERTTHRESH1 B43_PHY_N(0x227) +#define B43_NPHY_ED_CRS20LASSERTTHRESH0 B43_PHY_N(0x228) +#define B43_NPHY_ED_CRS20LASSERTTHRESH1 B43_PHY_N(0x229) +#define B43_NPHY_ED_CRS20LDEASSERTTHRESH0 B43_PHY_N(0x22A) +#define B43_NPHY_ED_CRS20LDEASSERTTHRESH1 B43_PHY_N(0x22B) +#define B43_NPHY_ED_CRS20UASSERTTHRESH0 B43_PHY_N(0x22C) +#define B43_NPHY_ED_CRS20UASSERTTHRESH1 B43_PHY_N(0x22D) +#define B43_NPHY_ED_CRS20UDEASSERTTHRESH0 B43_PHY_N(0x22E) +#define B43_NPHY_ED_CRS20UDEASSERTTHRESH1 B43_PHY_N(0x22F) +#define B43_NPHY_ED_CRS B43_PHY_N(0x230) +#define B43_NPHY_TIMEOUTEN B43_PHY_N(0x231) +#define B43_NPHY_OFDMPAYDECODETIMEOUTLEN B43_PHY_N(0x232) +#define B43_NPHY_CCKPAYDECODETIMEOUTLEN B43_PHY_N(0x233) +#define B43_NPHY_NONPAYDECODETIMEOUTLEN B43_PHY_N(0x234) +#define B43_NPHY_TIMEOUTSTATUS B43_PHY_N(0x235) +#define B43_NPHY_RFCTRLCORE0GPIO0 B43_PHY_N(0x236) +#define B43_NPHY_RFCTRLCORE0GPIO1 B43_PHY_N(0x237) +#define B43_NPHY_RFCTRLCORE0GPIO2 B43_PHY_N(0x238) +#define B43_NPHY_RFCTRLCORE0GPIO3 B43_PHY_N(0x239) +#define B43_NPHY_RFCTRLCORE1GPIO0 B43_PHY_N(0x23A) +#define B43_NPHY_RFCTRLCORE1GPIO1 B43_PHY_N(0x23B) +#define B43_NPHY_RFCTRLCORE1GPIO2 B43_PHY_N(0x23C) +#define B43_NPHY_RFCTRLCORE1GPIO3 B43_PHY_N(0x23D) +#define B43_NPHY_BPHYTESTCONTROL B43_PHY_N(0x23E) +/* REV3+ */ +#define B43_NPHY_FORCEFRONT0 B43_PHY_N(0x23F) +#define B43_NPHY_FORCEFRONT1 B43_PHY_N(0x240) +#define B43_NPHY_NORMVARHYSTTH B43_PHY_N(0x241) +#define B43_NPHY_TXCCKERROR B43_PHY_N(0x242) +#define B43_NPHY_AFESEQINITDACGAIN B43_PHY_N(0x243) +#define B43_NPHY_TXANTSWLUT B43_PHY_N(0x244) +#define B43_NPHY_CORECONFIG B43_PHY_N(0x245) +#define B43_NPHY_ANTENNADIVDWELLTIME B43_PHY_N(0x246) +#define B43_NPHY_ANTENNACCKDIVDWELLTIME B43_PHY_N(0x247) +#define B43_NPHY_ANTENNADIVBACKOFFGAIN B43_PHY_N(0x248) +#define B43_NPHY_ANTENNADIVMINGAIN B43_PHY_N(0x249) +#define B43_NPHY_BRDSEL_NORMVARHYSTTH B43_PHY_N(0x24A) +#define B43_NPHY_RXANTSWITCHCTRL B43_PHY_N(0x24B) +#define B43_NPHY_ENERGYDROPTIMEOUTLEN2 B43_PHY_N(0x24C) +#define B43_NPHY_ML_LOG_TXEVM0 B43_PHY_N(0x250) +#define B43_NPHY_ML_LOG_TXEVM1 B43_PHY_N(0x251) +#define B43_NPHY_ML_LOG_TXEVM2 B43_PHY_N(0x252) +#define B43_NPHY_ML_LOG_TXEVM3 B43_PHY_N(0x253) +#define B43_NPHY_ML_LOG_TXEVM4 B43_PHY_N(0x254) +#define B43_NPHY_ML_LOG_TXEVM5 B43_PHY_N(0x255) +#define B43_NPHY_ML_LOG_TXEVM6 B43_PHY_N(0x256) +#define B43_NPHY_ML_LOG_TXEVM7 B43_PHY_N(0x257) +#define B43_NPHY_ML_SCALE_TWEAK B43_PHY_N(0x258) +#define B43_NPHY_MLUA B43_PHY_N(0x259) +#define B43_NPHY_ZFUA B43_PHY_N(0x25A) +#define B43_NPHY_CHANUPSYM01 B43_PHY_N(0x25B) +#define B43_NPHY_CHANUPSYM2 B43_PHY_N(0x25C) +#define B43_NPHY_RXSTRNFILT20NUM00 B43_PHY_N(0x25D) +#define B43_NPHY_RXSTRNFILT20NUM01 B43_PHY_N(0x25E) +#define B43_NPHY_RXSTRNFILT20NUM02 B43_PHY_N(0x25F) +#define B43_NPHY_RXSTRNFILT20DEN00 B43_PHY_N(0x260) +#define B43_NPHY_RXSTRNFILT20DEN01 B43_PHY_N(0x261) +#define B43_NPHY_RXSTRNFILT20NUM10 B43_PHY_N(0x262) +#define B43_NPHY_RXSTRNFILT20NUM11 B43_PHY_N(0x263) +#define B43_NPHY_RXSTRNFILT20NUM12 B43_PHY_N(0x264) +#define B43_NPHY_RXSTRNFILT20DEN10 B43_PHY_N(0x265) +#define B43_NPHY_RXSTRNFILT20DEN11 B43_PHY_N(0x266) +#define B43_NPHY_RXSTRNFILT40NUM00 B43_PHY_N(0x267) +#define B43_NPHY_RXSTRNFILT40NUM01 B43_PHY_N(0x268) +#define B43_NPHY_RXSTRNFILT40NUM02 B43_PHY_N(0x269) +#define B43_NPHY_RXSTRNFILT40DEN00 B43_PHY_N(0x26A) +#define B43_NPHY_RXSTRNFILT40DEN01 B43_PHY_N(0x26B) +#define B43_NPHY_RXSTRNFILT40NUM10 B43_PHY_N(0x26C) +#define B43_NPHY_RXSTRNFILT40NUM11 B43_PHY_N(0x26D) +#define B43_NPHY_RXSTRNFILT40NUM12 B43_PHY_N(0x26E) +#define B43_NPHY_RXSTRNFILT40DEN10 B43_PHY_N(0x26F) +#define B43_NPHY_RXSTRNFILT40DEN11 B43_PHY_N(0x270) +#define B43_NPHY_CRSHIGHPOWTHRESHOLD1 B43_PHY_N(0x271) +#define B43_NPHY_CRSHIGHPOWTHRESHOLD2 B43_PHY_N(0x272) +#define B43_NPHY_CRSHIGHLOWPOWTHRESHOLD B43_PHY_N(0x273) +#define B43_NPHY_CRSHIGHPOWTHRESHOLD1L B43_PHY_N(0x274) +#define B43_NPHY_CRSHIGHPOWTHRESHOLD2L B43_PHY_N(0x275) +#define B43_NPHY_CRSHIGHLOWPOWTHRESHOLDL B43_PHY_N(0x276) +#define B43_NPHY_CRSHIGHPOWTHRESHOLD1U B43_PHY_N(0x277) +#define B43_NPHY_CRSHIGHPOWTHRESHOLD2U B43_PHY_N(0x278) +#define B43_NPHY_CRSHIGHLOWPOWTHRESHOLDU B43_PHY_N(0x279) +#define B43_NPHY_CRSACIDETECTTHRESH B43_PHY_N(0x27A) +#define B43_NPHY_CRSACIDETECTTHRESHL B43_PHY_N(0x27B) +#define B43_NPHY_CRSACIDETECTTHRESHU B43_PHY_N(0x27C) +#define B43_NPHY_CRSMINPOWER0 B43_PHY_N(0x27D) +#define B43_NPHY_CRSMINPOWER1 B43_PHY_N(0x27E) +#define B43_NPHY_CRSMINPOWER2 B43_PHY_N(0x27F) +#define B43_NPHY_CRSMINPOWERL0 B43_PHY_N(0x280) +#define B43_NPHY_CRSMINPOWERL1 B43_PHY_N(0x281) +#define B43_NPHY_CRSMINPOWERL2 B43_PHY_N(0x282) +#define B43_NPHY_CRSMINPOWERU0 B43_PHY_N(0x283) +#define B43_NPHY_CRSMINPOWERU1 B43_PHY_N(0x284) +#define B43_NPHY_CRSMINPOWERU2 B43_PHY_N(0x285) +#define B43_NPHY_STRPARAM B43_PHY_N(0x286) +#define B43_NPHY_STRPARAML B43_PHY_N(0x287) +#define B43_NPHY_STRPARAMU B43_PHY_N(0x288) +#define B43_NPHY_BPHYCRSMINPOWER0 B43_PHY_N(0x289) +#define B43_NPHY_BPHYCRSMINPOWER1 B43_PHY_N(0x28A) +#define B43_NPHY_BPHYCRSMINPOWER2 B43_PHY_N(0x28B) +#define B43_NPHY_BPHYFILTDEN0COEF B43_PHY_N(0x28C) +#define B43_NPHY_BPHYFILTDEN1COEF B43_PHY_N(0x28D) +#define B43_NPHY_BPHYFILTDEN2COEF B43_PHY_N(0x28E) +#define B43_NPHY_BPHYFILTNUM0COEF B43_PHY_N(0x28F) +#define B43_NPHY_BPHYFILTNUM1COEF B43_PHY_N(0x290) +#define B43_NPHY_BPHYFILTNUM2COEF B43_PHY_N(0x291) +#define B43_NPHY_BPHYFILTNUM01COEF2 B43_PHY_N(0x292) +#define B43_NPHY_BPHYFILTBYPASS B43_PHY_N(0x293) +#define B43_NPHY_SGILTRNOFFSET B43_PHY_N(0x294) +#define B43_NPHY_RADAR_T2_MIN B43_PHY_N(0x295) +#define B43_NPHY_TXPWRCTRLDAMPING B43_PHY_N(0x296) +#define B43_NPHY_PAPD_EN0 B43_PHY_N(0x297) /* PAPD Enable0 TBD */ +#define B43_NPHY_EPS_TABLE_ADJ0 B43_PHY_N(0x298) /* EPS Table Adj0 TBD */ +#define B43_NPHY_EPS_OVERRIDEI_0 B43_PHY_N(0x299) +#define B43_NPHY_EPS_OVERRIDEQ_0 B43_PHY_N(0x29A) +#define B43_NPHY_PAPD_EN1 B43_PHY_N(0x29B) /* PAPD Enable1 TBD */ +#define B43_NPHY_EPS_TABLE_ADJ1 B43_PHY_N(0x29C) /* EPS Table Adj1 TBD */ +#define B43_NPHY_EPS_OVERRIDEI_1 B43_PHY_N(0x29D) +#define B43_NPHY_EPS_OVERRIDEQ_1 B43_PHY_N(0x29E) +#define B43_NPHY_PAPD_CAL_ADDRESS B43_PHY_N(0x29F) +#define B43_NPHY_PAPD_CAL_YREFEPSILON B43_PHY_N(0x2A0) +#define B43_NPHY_PAPD_CAL_SETTLE B43_PHY_N(0x2A1) +#define B43_NPHY_PAPD_CAL_CORRELATE B43_PHY_N(0x2A2) +#define B43_NPHY_PAPD_CAL_SHIFTS0 B43_PHY_N(0x2A3) +#define B43_NPHY_PAPD_CAL_SHIFTS1 B43_PHY_N(0x2A4) +#define B43_NPHY_SAMPLE_START_ADDR B43_PHY_N(0x2A5) +#define B43_NPHY_RADAR_ADC_TO_DBM B43_PHY_N(0x2A6) +#define B43_NPHY_REV3_C2_INITGAIN_A B43_PHY_N(0x2A7) +#define B43_NPHY_REV3_C2_INITGAIN_B B43_PHY_N(0x2A8) +#define B43_NPHY_REV3_C2_CLIP_HIGAIN_A B43_PHY_N(0x2A9) +#define B43_NPHY_REV3_C2_CLIP_HIGAIN_B B43_PHY_N(0x2AA) +#define B43_NPHY_REV3_C2_CLIP_MEDGAIN_A B43_PHY_N(0x2AB) +#define B43_NPHY_REV3_C2_CLIP_MEDGAIN_B B43_PHY_N(0x2AC) +#define B43_NPHY_REV3_C2_CLIP_LOGAIN_A B43_PHY_N(0x2AD) +#define B43_NPHY_REV3_C2_CLIP_LOGAIN_B B43_PHY_N(0x2AE) +#define B43_NPHY_REV3_C2_CLIP2_GAIN_A B43_PHY_N(0x2AF) +#define B43_NPHY_REV3_C2_CLIP2_GAIN_B B43_PHY_N(0x2B0) + +#define B43_NPHY_REV7_RF_CTL_MISC_REG3 B43_PHY_N(0x340) +#define B43_NPHY_REV7_RF_CTL_MISC_REG4 B43_PHY_N(0x341) +#define B43_NPHY_REV7_RF_CTL_OVER3 B43_PHY_N(0x342) +#define B43_NPHY_REV7_RF_CTL_OVER4 B43_PHY_N(0x343) +#define B43_NPHY_REV7_RF_CTL_MISC_REG5 B43_PHY_N(0x344) +#define B43_NPHY_REV7_RF_CTL_MISC_REG6 B43_PHY_N(0x345) +#define B43_NPHY_REV7_RF_CTL_OVER5 B43_PHY_N(0x346) +#define B43_NPHY_REV7_RF_CTL_OVER6 B43_PHY_N(0x347) + +#define B43_PHY_B_BBCFG B43_PHY_N_BMODE(0x001) /* BB config */ +#define B43_PHY_B_BBCFG_RSTCCA 0x4000 /* Reset CCA */ +#define B43_PHY_B_BBCFG_RSTRX 0x8000 /* Reset RX */ +#define B43_PHY_B_TEST B43_PHY_N_BMODE(0x00A) + +struct b43_wldev; + +enum b43_nphy_spur_avoid { + B43_SPUR_AVOID_DISABLE, + B43_SPUR_AVOID_AUTO, + B43_SPUR_AVOID_FORCE, +}; + +struct b43_chanspec { + u16 center_freq; + enum nl80211_channel_type channel_type; +}; + +struct b43_phy_n_iq_comp { + s16 a0; + s16 b0; + s16 a1; + s16 b1; +}; + +struct b43_phy_n_rssical_cache { + u16 rssical_radio_regs_2G[2]; + u16 rssical_phy_regs_2G[12]; + + u16 rssical_radio_regs_5G[2]; + u16 rssical_phy_regs_5G[12]; +}; + +struct b43_phy_n_cal_cache { + u16 txcal_radio_regs_2G[8]; + u16 txcal_coeffs_2G[8]; + struct b43_phy_n_iq_comp rxcal_coeffs_2G; + + u16 txcal_radio_regs_5G[8]; + u16 txcal_coeffs_5G[8]; + struct b43_phy_n_iq_comp rxcal_coeffs_5G; +}; + +struct b43_phy_n_txpwrindex { + s8 index; + s8 index_internal; + s8 index_internal_save; + u16 AfectrlOverride; + u16 AfeCtrlDacGain; + u16 rad_gain; + u8 bbmult; + u16 iqcomp_a; + u16 iqcomp_b; + u16 locomp; +}; + +struct b43_phy_n_pwr_ctl_info { + u8 idle_tssi_2g; + u8 idle_tssi_5g; +}; + +struct b43_phy_n { + u8 antsel_type; + u8 cal_orig_pwr_idx[2]; + u8 measure_hold; + u8 phyrxchain; + u8 hw_phyrxchain; + u8 hw_phytxchain; + u8 perical; + u32 deaf_count; + u32 rxcalparams; + bool hang_avoid; + bool mute; + u16 papd_epsilon_offset[2]; + s32 preamble_override; + u32 bb_mult_save; + + bool gain_boost; + bool elna_gain_config; + bool band5g_pwrgain; + bool use_int_tx_iq_lo_cal; + bool lpf_bw_overrode_for_sample_play; + + u8 mphase_cal_phase_id; + u16 mphase_txcal_cmdidx; + u16 mphase_txcal_numcmds; + u16 mphase_txcal_bestcoeffs[11]; + + bool txpwrctrl; + bool pwg_gain_5ghz; + u8 tx_pwr_idx[2]; + s8 tx_power_offset[101]; + u16 adj_pwr_tbl[84]; + u16 txcal_bbmult; + u16 txiqlocal_bestc[11]; + bool txiqlocal_coeffsvalid; + struct b43_phy_n_txpwrindex txpwrindex[2]; + struct b43_phy_n_pwr_ctl_info pwr_ctl_info[2]; + struct b43_chanspec txiqlocal_chanspec; + struct b43_ppr tx_pwr_max_ppr; + u16 tx_pwr_last_recalc_freq; + int tx_pwr_last_recalc_limit; + + u8 txrx_chain; + u16 tx_rx_cal_phy_saveregs[11]; + u16 tx_rx_cal_radio_saveregs[22]; + + u16 rfctrl_intc1_save; + u16 rfctrl_intc2_save; + + u16 classifier_state; + u16 clip_state[2]; + + enum b43_nphy_spur_avoid spur_avoid; + bool aband_spurwar_en; + bool gband_spurwar_en; + + bool ipa2g_on; + struct b43_chanspec iqcal_chanspec_2G; + struct b43_chanspec rssical_chanspec_2G; + + bool ipa5g_on; + struct b43_chanspec iqcal_chanspec_5G; + struct b43_chanspec rssical_chanspec_5G; + + struct b43_phy_n_rssical_cache rssical_cache; + struct b43_phy_n_cal_cache cal_cache; + bool crsminpwr_adjusted; + bool noisevars_adjusted; +}; + + +struct b43_phy_operations; +extern const struct b43_phy_operations b43_phyops_n; + +#endif /* B43_NPHY_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/pio.c b/drivers/net/wireless/broadcom/b43/pio.c new file mode 100644 index 000000000000..a4ff5e2a42b9 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/pio.c @@ -0,0 +1,834 @@ +/* + + Broadcom B43 wireless driver + + PIO data transfer + + Copyright (c) 2005-2008 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "pio.h" +#include "dma.h" +#include "main.h" +#include "xmit.h" + +#include +#include +#include + + +static u16 generate_cookie(struct b43_pio_txqueue *q, + struct b43_pio_txpacket *pack) +{ + u16 cookie; + + /* Use the upper 4 bits of the cookie as + * PIO controller ID and store the packet index number + * in the lower 12 bits. + * Note that the cookie must never be 0, as this + * is a special value used in RX path. + * It can also not be 0xFFFF because that is special + * for multicast frames. + */ + cookie = (((u16)q->index + 1) << 12); + cookie |= pack->index; + + return cookie; +} + +static +struct b43_pio_txqueue *parse_cookie(struct b43_wldev *dev, + u16 cookie, + struct b43_pio_txpacket **pack) +{ + struct b43_pio *pio = &dev->pio; + struct b43_pio_txqueue *q = NULL; + unsigned int pack_index; + + switch (cookie & 0xF000) { + case 0x1000: + q = pio->tx_queue_AC_BK; + break; + case 0x2000: + q = pio->tx_queue_AC_BE; + break; + case 0x3000: + q = pio->tx_queue_AC_VI; + break; + case 0x4000: + q = pio->tx_queue_AC_VO; + break; + case 0x5000: + q = pio->tx_queue_mcast; + break; + } + if (B43_WARN_ON(!q)) + return NULL; + pack_index = (cookie & 0x0FFF); + if (B43_WARN_ON(pack_index >= ARRAY_SIZE(q->packets))) + return NULL; + *pack = &q->packets[pack_index]; + + return q; +} + +static u16 index_to_pioqueue_base(struct b43_wldev *dev, + unsigned int index) +{ + static const u16 bases[] = { + B43_MMIO_PIO_BASE0, + B43_MMIO_PIO_BASE1, + B43_MMIO_PIO_BASE2, + B43_MMIO_PIO_BASE3, + B43_MMIO_PIO_BASE4, + B43_MMIO_PIO_BASE5, + B43_MMIO_PIO_BASE6, + B43_MMIO_PIO_BASE7, + }; + static const u16 bases_rev11[] = { + B43_MMIO_PIO11_BASE0, + B43_MMIO_PIO11_BASE1, + B43_MMIO_PIO11_BASE2, + B43_MMIO_PIO11_BASE3, + B43_MMIO_PIO11_BASE4, + B43_MMIO_PIO11_BASE5, + }; + + if (dev->dev->core_rev >= 11) { + B43_WARN_ON(index >= ARRAY_SIZE(bases_rev11)); + return bases_rev11[index]; + } + B43_WARN_ON(index >= ARRAY_SIZE(bases)); + return bases[index]; +} + +static u16 pio_txqueue_offset(struct b43_wldev *dev) +{ + if (dev->dev->core_rev >= 11) + return 0x18; + return 0; +} + +static u16 pio_rxqueue_offset(struct b43_wldev *dev) +{ + if (dev->dev->core_rev >= 11) + return 0x38; + return 8; +} + +static struct b43_pio_txqueue *b43_setup_pioqueue_tx(struct b43_wldev *dev, + unsigned int index) +{ + struct b43_pio_txqueue *q; + struct b43_pio_txpacket *p; + unsigned int i; + + q = kzalloc(sizeof(*q), GFP_KERNEL); + if (!q) + return NULL; + q->dev = dev; + q->rev = dev->dev->core_rev; + q->mmio_base = index_to_pioqueue_base(dev, index) + + pio_txqueue_offset(dev); + q->index = index; + + q->free_packet_slots = B43_PIO_MAX_NR_TXPACKETS; + if (q->rev >= 8) { + q->buffer_size = 1920; //FIXME this constant is wrong. + } else { + q->buffer_size = b43_piotx_read16(q, B43_PIO_TXQBUFSIZE); + q->buffer_size -= 80; + } + + INIT_LIST_HEAD(&q->packets_list); + for (i = 0; i < ARRAY_SIZE(q->packets); i++) { + p = &(q->packets[i]); + INIT_LIST_HEAD(&p->list); + p->index = i; + p->queue = q; + list_add(&p->list, &q->packets_list); + } + + return q; +} + +static struct b43_pio_rxqueue *b43_setup_pioqueue_rx(struct b43_wldev *dev, + unsigned int index) +{ + struct b43_pio_rxqueue *q; + + q = kzalloc(sizeof(*q), GFP_KERNEL); + if (!q) + return NULL; + q->dev = dev; + q->rev = dev->dev->core_rev; + q->mmio_base = index_to_pioqueue_base(dev, index) + + pio_rxqueue_offset(dev); + + /* Enable Direct FIFO RX (PIO) on the engine. */ + b43_dma_direct_fifo_rx(dev, index, 1); + + return q; +} + +static void b43_pio_cancel_tx_packets(struct b43_pio_txqueue *q) +{ + struct b43_pio_txpacket *pack; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(q->packets); i++) { + pack = &(q->packets[i]); + if (pack->skb) { + ieee80211_free_txskb(q->dev->wl->hw, pack->skb); + pack->skb = NULL; + } + } +} + +static void b43_destroy_pioqueue_tx(struct b43_pio_txqueue *q, + const char *name) +{ + if (!q) + return; + b43_pio_cancel_tx_packets(q); + kfree(q); +} + +static void b43_destroy_pioqueue_rx(struct b43_pio_rxqueue *q, + const char *name) +{ + if (!q) + return; + kfree(q); +} + +#define destroy_queue_tx(pio, queue) do { \ + b43_destroy_pioqueue_tx((pio)->queue, __stringify(queue)); \ + (pio)->queue = NULL; \ + } while (0) + +#define destroy_queue_rx(pio, queue) do { \ + b43_destroy_pioqueue_rx((pio)->queue, __stringify(queue)); \ + (pio)->queue = NULL; \ + } while (0) + +void b43_pio_free(struct b43_wldev *dev) +{ + struct b43_pio *pio; + + if (!b43_using_pio_transfers(dev)) + return; + pio = &dev->pio; + + destroy_queue_rx(pio, rx_queue); + destroy_queue_tx(pio, tx_queue_mcast); + destroy_queue_tx(pio, tx_queue_AC_VO); + destroy_queue_tx(pio, tx_queue_AC_VI); + destroy_queue_tx(pio, tx_queue_AC_BE); + destroy_queue_tx(pio, tx_queue_AC_BK); +} + +int b43_pio_init(struct b43_wldev *dev) +{ + struct b43_pio *pio = &dev->pio; + int err = -ENOMEM; + + b43_write32(dev, B43_MMIO_MACCTL, b43_read32(dev, B43_MMIO_MACCTL) + & ~B43_MACCTL_BE); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_RXPADOFF, 0); + + pio->tx_queue_AC_BK = b43_setup_pioqueue_tx(dev, 0); + if (!pio->tx_queue_AC_BK) + goto out; + + pio->tx_queue_AC_BE = b43_setup_pioqueue_tx(dev, 1); + if (!pio->tx_queue_AC_BE) + goto err_destroy_bk; + + pio->tx_queue_AC_VI = b43_setup_pioqueue_tx(dev, 2); + if (!pio->tx_queue_AC_VI) + goto err_destroy_be; + + pio->tx_queue_AC_VO = b43_setup_pioqueue_tx(dev, 3); + if (!pio->tx_queue_AC_VO) + goto err_destroy_vi; + + pio->tx_queue_mcast = b43_setup_pioqueue_tx(dev, 4); + if (!pio->tx_queue_mcast) + goto err_destroy_vo; + + pio->rx_queue = b43_setup_pioqueue_rx(dev, 0); + if (!pio->rx_queue) + goto err_destroy_mcast; + + b43dbg(dev->wl, "PIO initialized\n"); + err = 0; +out: + return err; + +err_destroy_mcast: + destroy_queue_tx(pio, tx_queue_mcast); +err_destroy_vo: + destroy_queue_tx(pio, tx_queue_AC_VO); +err_destroy_vi: + destroy_queue_tx(pio, tx_queue_AC_VI); +err_destroy_be: + destroy_queue_tx(pio, tx_queue_AC_BE); +err_destroy_bk: + destroy_queue_tx(pio, tx_queue_AC_BK); + return err; +} + +/* Static mapping of mac80211's queues (priorities) to b43 PIO queues. */ +static struct b43_pio_txqueue *select_queue_by_priority(struct b43_wldev *dev, + u8 queue_prio) +{ + struct b43_pio_txqueue *q; + + if (dev->qos_enabled) { + /* 0 = highest priority */ + switch (queue_prio) { + default: + B43_WARN_ON(1); + /* fallthrough */ + case 0: + q = dev->pio.tx_queue_AC_VO; + break; + case 1: + q = dev->pio.tx_queue_AC_VI; + break; + case 2: + q = dev->pio.tx_queue_AC_BE; + break; + case 3: + q = dev->pio.tx_queue_AC_BK; + break; + } + } else + q = dev->pio.tx_queue_AC_BE; + + return q; +} + +static u16 tx_write_2byte_queue(struct b43_pio_txqueue *q, + u16 ctl, + const void *_data, + unsigned int data_len) +{ + struct b43_wldev *dev = q->dev; + struct b43_wl *wl = dev->wl; + const u8 *data = _data; + + ctl |= B43_PIO_TXCTL_WRITELO | B43_PIO_TXCTL_WRITEHI; + b43_piotx_write16(q, B43_PIO_TXCTL, ctl); + + b43_block_write(dev, data, (data_len & ~1), + q->mmio_base + B43_PIO_TXDATA, + sizeof(u16)); + if (data_len & 1) { + u8 *tail = wl->pio_tailspace; + BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 2); + + /* Write the last byte. */ + ctl &= ~B43_PIO_TXCTL_WRITEHI; + b43_piotx_write16(q, B43_PIO_TXCTL, ctl); + tail[0] = data[data_len - 1]; + tail[1] = 0; + b43_block_write(dev, tail, 2, + q->mmio_base + B43_PIO_TXDATA, + sizeof(u16)); + } + + return ctl; +} + +static void pio_tx_frame_2byte_queue(struct b43_pio_txpacket *pack, + const u8 *hdr, unsigned int hdrlen) +{ + struct b43_pio_txqueue *q = pack->queue; + const char *frame = pack->skb->data; + unsigned int frame_len = pack->skb->len; + u16 ctl; + + ctl = b43_piotx_read16(q, B43_PIO_TXCTL); + ctl |= B43_PIO_TXCTL_FREADY; + ctl &= ~B43_PIO_TXCTL_EOF; + + /* Transfer the header data. */ + ctl = tx_write_2byte_queue(q, ctl, hdr, hdrlen); + /* Transfer the frame data. */ + ctl = tx_write_2byte_queue(q, ctl, frame, frame_len); + + ctl |= B43_PIO_TXCTL_EOF; + b43_piotx_write16(q, B43_PIO_TXCTL, ctl); +} + +static u32 tx_write_4byte_queue(struct b43_pio_txqueue *q, + u32 ctl, + const void *_data, + unsigned int data_len) +{ + struct b43_wldev *dev = q->dev; + struct b43_wl *wl = dev->wl; + const u8 *data = _data; + + ctl |= B43_PIO8_TXCTL_0_7 | B43_PIO8_TXCTL_8_15 | + B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_24_31; + b43_piotx_write32(q, B43_PIO8_TXCTL, ctl); + + b43_block_write(dev, data, (data_len & ~3), + q->mmio_base + B43_PIO8_TXDATA, + sizeof(u32)); + if (data_len & 3) { + u8 *tail = wl->pio_tailspace; + BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 4); + + memset(tail, 0, 4); + /* Write the last few bytes. */ + ctl &= ~(B43_PIO8_TXCTL_8_15 | B43_PIO8_TXCTL_16_23 | + B43_PIO8_TXCTL_24_31); + switch (data_len & 3) { + case 3: + ctl |= B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_8_15; + tail[0] = data[data_len - 3]; + tail[1] = data[data_len - 2]; + tail[2] = data[data_len - 1]; + break; + case 2: + ctl |= B43_PIO8_TXCTL_8_15; + tail[0] = data[data_len - 2]; + tail[1] = data[data_len - 1]; + break; + case 1: + tail[0] = data[data_len - 1]; + break; + } + b43_piotx_write32(q, B43_PIO8_TXCTL, ctl); + b43_block_write(dev, tail, 4, + q->mmio_base + B43_PIO8_TXDATA, + sizeof(u32)); + } + + return ctl; +} + +static void pio_tx_frame_4byte_queue(struct b43_pio_txpacket *pack, + const u8 *hdr, unsigned int hdrlen) +{ + struct b43_pio_txqueue *q = pack->queue; + const char *frame = pack->skb->data; + unsigned int frame_len = pack->skb->len; + u32 ctl; + + ctl = b43_piotx_read32(q, B43_PIO8_TXCTL); + ctl |= B43_PIO8_TXCTL_FREADY; + ctl &= ~B43_PIO8_TXCTL_EOF; + + /* Transfer the header data. */ + ctl = tx_write_4byte_queue(q, ctl, hdr, hdrlen); + /* Transfer the frame data. */ + ctl = tx_write_4byte_queue(q, ctl, frame, frame_len); + + ctl |= B43_PIO8_TXCTL_EOF; + b43_piotx_write32(q, B43_PIO_TXCTL, ctl); +} + +static int pio_tx_frame(struct b43_pio_txqueue *q, + struct sk_buff *skb) +{ + struct b43_wldev *dev = q->dev; + struct b43_wl *wl = dev->wl; + struct b43_pio_txpacket *pack; + u16 cookie; + int err; + unsigned int hdrlen; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct b43_txhdr *txhdr = (struct b43_txhdr *)wl->pio_scratchspace; + + B43_WARN_ON(list_empty(&q->packets_list)); + pack = list_entry(q->packets_list.next, + struct b43_pio_txpacket, list); + + cookie = generate_cookie(q, pack); + hdrlen = b43_txhdr_size(dev); + BUILD_BUG_ON(sizeof(wl->pio_scratchspace) < sizeof(struct b43_txhdr)); + B43_WARN_ON(sizeof(wl->pio_scratchspace) < hdrlen); + err = b43_generate_txhdr(dev, (u8 *)txhdr, skb, + info, cookie); + if (err) + return err; + + if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { + /* Tell the firmware about the cookie of the last + * mcast frame, so it can clear the more-data bit in it. */ + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_MCASTCOOKIE, cookie); + } + + pack->skb = skb; + if (q->rev >= 8) + pio_tx_frame_4byte_queue(pack, (const u8 *)txhdr, hdrlen); + else + pio_tx_frame_2byte_queue(pack, (const u8 *)txhdr, hdrlen); + + /* Remove it from the list of available packet slots. + * It will be put back when we receive the status report. */ + list_del(&pack->list); + + /* Update the queue statistics. */ + q->buffer_used += roundup(skb->len + hdrlen, 4); + q->free_packet_slots -= 1; + + return 0; +} + +int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb) +{ + struct b43_pio_txqueue *q; + struct ieee80211_hdr *hdr; + unsigned int hdrlen, total_len; + int err = 0; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + hdr = (struct ieee80211_hdr *)skb->data; + + if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { + /* The multicast queue will be sent after the DTIM. */ + q = dev->pio.tx_queue_mcast; + /* Set the frame More-Data bit. Ucode will clear it + * for us on the last frame. */ + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } else { + /* Decide by priority where to put this frame. */ + q = select_queue_by_priority(dev, skb_get_queue_mapping(skb)); + } + + hdrlen = b43_txhdr_size(dev); + total_len = roundup(skb->len + hdrlen, 4); + + if (unlikely(total_len > q->buffer_size)) { + err = -ENOBUFS; + b43dbg(dev->wl, "PIO: TX packet longer than queue.\n"); + goto out; + } + if (unlikely(q->free_packet_slots == 0)) { + err = -ENOBUFS; + b43warn(dev->wl, "PIO: TX packet overflow.\n"); + goto out; + } + B43_WARN_ON(q->buffer_used > q->buffer_size); + + if (total_len > (q->buffer_size - q->buffer_used)) { + /* Not enough memory on the queue. */ + err = -EBUSY; + ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb)); + q->stopped = true; + goto out; + } + + /* Assign the queue number to the ring (if not already done before) + * so TX status handling can use it. The mac80211-queue to b43-queue + * mapping is static, so we don't need to store it per frame. */ + q->queue_prio = skb_get_queue_mapping(skb); + + err = pio_tx_frame(q, skb); + if (unlikely(err == -ENOKEY)) { + /* Drop this packet, as we don't have the encryption key + * anymore and must not transmit it unencrypted. */ + ieee80211_free_txskb(dev->wl->hw, skb); + err = 0; + goto out; + } + if (unlikely(err)) { + b43err(dev->wl, "PIO transmission failure\n"); + goto out; + } + + B43_WARN_ON(q->buffer_used > q->buffer_size); + if (((q->buffer_size - q->buffer_used) < roundup(2 + 2 + 6, 4)) || + (q->free_packet_slots == 0)) { + /* The queue is full. */ + ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb)); + q->stopped = true; + } + +out: + return err; +} + +void b43_pio_handle_txstatus(struct b43_wldev *dev, + const struct b43_txstatus *status) +{ + struct b43_pio_txqueue *q; + struct b43_pio_txpacket *pack = NULL; + unsigned int total_len; + struct ieee80211_tx_info *info; + + q = parse_cookie(dev, status->cookie, &pack); + if (unlikely(!q)) + return; + B43_WARN_ON(!pack); + + info = IEEE80211_SKB_CB(pack->skb); + + b43_fill_txstatus_report(dev, info, status); + + total_len = pack->skb->len + b43_txhdr_size(dev); + total_len = roundup(total_len, 4); + q->buffer_used -= total_len; + q->free_packet_slots += 1; + + ieee80211_tx_status(dev->wl->hw, pack->skb); + pack->skb = NULL; + list_add(&pack->list, &q->packets_list); + + if (q->stopped) { + ieee80211_wake_queue(dev->wl->hw, q->queue_prio); + q->stopped = false; + } +} + +/* Returns whether we should fetch another frame. */ +static bool pio_rx_frame(struct b43_pio_rxqueue *q) +{ + struct b43_wldev *dev = q->dev; + struct b43_wl *wl = dev->wl; + u16 len; + u32 macstat = 0; + unsigned int i, padding; + struct sk_buff *skb; + const char *err_msg = NULL; + struct b43_rxhdr_fw4 *rxhdr = + (struct b43_rxhdr_fw4 *)wl->pio_scratchspace; + size_t rxhdr_size = sizeof(*rxhdr); + + BUILD_BUG_ON(sizeof(wl->pio_scratchspace) < sizeof(*rxhdr)); + switch (dev->fw.hdr_format) { + case B43_FW_HDR_410: + case B43_FW_HDR_351: + rxhdr_size -= sizeof(rxhdr->format_598) - + sizeof(rxhdr->format_351); + break; + case B43_FW_HDR_598: + break; + } + memset(rxhdr, 0, rxhdr_size); + + /* Check if we have data and wait for it to get ready. */ + if (q->rev >= 8) { + u32 ctl; + + ctl = b43_piorx_read32(q, B43_PIO8_RXCTL); + if (!(ctl & B43_PIO8_RXCTL_FRAMERDY)) + return false; + b43_piorx_write32(q, B43_PIO8_RXCTL, + B43_PIO8_RXCTL_FRAMERDY); + for (i = 0; i < 10; i++) { + ctl = b43_piorx_read32(q, B43_PIO8_RXCTL); + if (ctl & B43_PIO8_RXCTL_DATARDY) + goto data_ready; + udelay(10); + } + } else { + u16 ctl; + + ctl = b43_piorx_read16(q, B43_PIO_RXCTL); + if (!(ctl & B43_PIO_RXCTL_FRAMERDY)) + return false; + b43_piorx_write16(q, B43_PIO_RXCTL, + B43_PIO_RXCTL_FRAMERDY); + for (i = 0; i < 10; i++) { + ctl = b43_piorx_read16(q, B43_PIO_RXCTL); + if (ctl & B43_PIO_RXCTL_DATARDY) + goto data_ready; + udelay(10); + } + } + b43dbg(q->dev->wl, "PIO RX timed out\n"); + return true; +data_ready: + + /* Get the preamble (RX header) */ + if (q->rev >= 8) { + b43_block_read(dev, rxhdr, rxhdr_size, + q->mmio_base + B43_PIO8_RXDATA, + sizeof(u32)); + } else { + b43_block_read(dev, rxhdr, rxhdr_size, + q->mmio_base + B43_PIO_RXDATA, + sizeof(u16)); + } + /* Sanity checks. */ + len = le16_to_cpu(rxhdr->frame_len); + if (unlikely(len > 0x700)) { + err_msg = "len > 0x700"; + goto rx_error; + } + if (unlikely(len == 0)) { + err_msg = "len == 0"; + goto rx_error; + } + + switch (dev->fw.hdr_format) { + case B43_FW_HDR_598: + macstat = le32_to_cpu(rxhdr->format_598.mac_status); + break; + case B43_FW_HDR_410: + case B43_FW_HDR_351: + macstat = le32_to_cpu(rxhdr->format_351.mac_status); + break; + } + if (macstat & B43_RX_MAC_FCSERR) { + if (!(q->dev->wl->filter_flags & FIF_FCSFAIL)) { + /* Drop frames with failed FCS. */ + err_msg = "Frame FCS error"; + goto rx_error; + } + } + + /* We always pad 2 bytes, as that's what upstream code expects + * due to the RX-header being 30 bytes. In case the frame is + * unaligned, we pad another 2 bytes. */ + padding = (macstat & B43_RX_MAC_PADDING) ? 2 : 0; + skb = dev_alloc_skb(len + padding + 2); + if (unlikely(!skb)) { + err_msg = "Out of memory"; + goto rx_error; + } + skb_reserve(skb, 2); + skb_put(skb, len + padding); + if (q->rev >= 8) { + b43_block_read(dev, skb->data + padding, (len & ~3), + q->mmio_base + B43_PIO8_RXDATA, + sizeof(u32)); + if (len & 3) { + u8 *tail = wl->pio_tailspace; + BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 4); + + /* Read the last few bytes. */ + b43_block_read(dev, tail, 4, + q->mmio_base + B43_PIO8_RXDATA, + sizeof(u32)); + switch (len & 3) { + case 3: + skb->data[len + padding - 3] = tail[0]; + skb->data[len + padding - 2] = tail[1]; + skb->data[len + padding - 1] = tail[2]; + break; + case 2: + skb->data[len + padding - 2] = tail[0]; + skb->data[len + padding - 1] = tail[1]; + break; + case 1: + skb->data[len + padding - 1] = tail[0]; + break; + } + } + } else { + b43_block_read(dev, skb->data + padding, (len & ~1), + q->mmio_base + B43_PIO_RXDATA, + sizeof(u16)); + if (len & 1) { + u8 *tail = wl->pio_tailspace; + BUILD_BUG_ON(sizeof(wl->pio_tailspace) < 2); + + /* Read the last byte. */ + b43_block_read(dev, tail, 2, + q->mmio_base + B43_PIO_RXDATA, + sizeof(u16)); + skb->data[len + padding - 1] = tail[0]; + } + } + + b43_rx(q->dev, skb, rxhdr); + + return true; + +rx_error: + if (err_msg) + b43dbg(q->dev->wl, "PIO RX error: %s\n", err_msg); + if (q->rev >= 8) + b43_piorx_write32(q, B43_PIO8_RXCTL, B43_PIO8_RXCTL_DATARDY); + else + b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_DATARDY); + + return true; +} + +void b43_pio_rx(struct b43_pio_rxqueue *q) +{ + unsigned int count = 0; + bool stop; + + while (1) { + stop = (pio_rx_frame(q) == 0); + if (stop) + break; + cond_resched(); + if (WARN_ON_ONCE(++count > 10000)) + break; + } +} + +static void b43_pio_tx_suspend_queue(struct b43_pio_txqueue *q) +{ + if (q->rev >= 8) { + b43_piotx_write32(q, B43_PIO8_TXCTL, + b43_piotx_read32(q, B43_PIO8_TXCTL) + | B43_PIO8_TXCTL_SUSPREQ); + } else { + b43_piotx_write16(q, B43_PIO_TXCTL, + b43_piotx_read16(q, B43_PIO_TXCTL) + | B43_PIO_TXCTL_SUSPREQ); + } +} + +static void b43_pio_tx_resume_queue(struct b43_pio_txqueue *q) +{ + if (q->rev >= 8) { + b43_piotx_write32(q, B43_PIO8_TXCTL, + b43_piotx_read32(q, B43_PIO8_TXCTL) + & ~B43_PIO8_TXCTL_SUSPREQ); + } else { + b43_piotx_write16(q, B43_PIO_TXCTL, + b43_piotx_read16(q, B43_PIO_TXCTL) + & ~B43_PIO_TXCTL_SUSPREQ); + } +} + +void b43_pio_tx_suspend(struct b43_wldev *dev) +{ + b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); + b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_BK); + b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_BE); + b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_VI); + b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_VO); + b43_pio_tx_suspend_queue(dev->pio.tx_queue_mcast); +} + +void b43_pio_tx_resume(struct b43_wldev *dev) +{ + b43_pio_tx_resume_queue(dev->pio.tx_queue_mcast); + b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_VO); + b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_VI); + b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_BE); + b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_BK); + b43_power_saving_ctl_bits(dev, 0); +} diff --git a/drivers/net/wireless/broadcom/b43/pio.h b/drivers/net/wireless/broadcom/b43/pio.h new file mode 100644 index 000000000000..1e516147424f --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/pio.h @@ -0,0 +1,165 @@ +#ifndef B43_PIO_H_ +#define B43_PIO_H_ + +#include "b43.h" + +#include +#include +#include +#include + + +/*** Registers for PIO queues up to revision 7. ***/ +/* TX queue. */ +#define B43_PIO_TXCTL 0x00 +#define B43_PIO_TXCTL_WRITELO 0x0001 +#define B43_PIO_TXCTL_WRITEHI 0x0002 +#define B43_PIO_TXCTL_EOF 0x0004 +#define B43_PIO_TXCTL_FREADY 0x0008 +#define B43_PIO_TXCTL_FLUSHREQ 0x0020 +#define B43_PIO_TXCTL_FLUSHPEND 0x0040 +#define B43_PIO_TXCTL_SUSPREQ 0x0080 +#define B43_PIO_TXCTL_QSUSP 0x0100 +#define B43_PIO_TXCTL_COMMCNT 0xFC00 +#define B43_PIO_TXCTL_COMMCNT_SHIFT 10 +#define B43_PIO_TXDATA 0x02 +#define B43_PIO_TXQBUFSIZE 0x04 +/* RX queue. */ +#define B43_PIO_RXCTL 0x00 +#define B43_PIO_RXCTL_FRAMERDY 0x0001 +#define B43_PIO_RXCTL_DATARDY 0x0002 +#define B43_PIO_RXDATA 0x02 + +/*** Registers for PIO queues revision 8 and later. ***/ +/* TX queue */ +#define B43_PIO8_TXCTL 0x00 +#define B43_PIO8_TXCTL_0_7 0x00000001 +#define B43_PIO8_TXCTL_8_15 0x00000002 +#define B43_PIO8_TXCTL_16_23 0x00000004 +#define B43_PIO8_TXCTL_24_31 0x00000008 +#define B43_PIO8_TXCTL_EOF 0x00000010 +#define B43_PIO8_TXCTL_FREADY 0x00000080 +#define B43_PIO8_TXCTL_SUSPREQ 0x00000100 +#define B43_PIO8_TXCTL_QSUSP 0x00000200 +#define B43_PIO8_TXCTL_FLUSHREQ 0x00000400 +#define B43_PIO8_TXCTL_FLUSHPEND 0x00000800 +#define B43_PIO8_TXDATA 0x04 +/* RX queue */ +#define B43_PIO8_RXCTL 0x00 +#define B43_PIO8_RXCTL_FRAMERDY 0x00000001 +#define B43_PIO8_RXCTL_DATARDY 0x00000002 +#define B43_PIO8_RXDATA 0x04 + + +/* The maximum number of TX-packets the HW can handle. */ +#define B43_PIO_MAX_NR_TXPACKETS 32 + + +struct b43_pio_txpacket { + /* Pointer to the TX queue we belong to. */ + struct b43_pio_txqueue *queue; + /* The TX data packet. */ + struct sk_buff *skb; + /* Index in the (struct b43_pio_txqueue)->packets array. */ + u8 index; + + struct list_head list; +}; + +struct b43_pio_txqueue { + struct b43_wldev *dev; + u16 mmio_base; + + /* The device queue buffer size in bytes. */ + u16 buffer_size; + /* The number of used bytes in the device queue buffer. */ + u16 buffer_used; + /* The number of packets that can still get queued. + * This is decremented on queueing a packet and incremented + * after receiving the transmit status. */ + u16 free_packet_slots; + + /* True, if the mac80211 queue was stopped due to overflow at TX. */ + bool stopped; + /* Our b43 queue index number */ + u8 index; + /* The mac80211 QoS queue priority. */ + u8 queue_prio; + + /* Buffer for TX packet meta data. */ + struct b43_pio_txpacket packets[B43_PIO_MAX_NR_TXPACKETS]; + struct list_head packets_list; + + /* Shortcut to the 802.11 core revision. This is to + * avoid horrible pointer dereferencing in the fastpaths. */ + u8 rev; +}; + +struct b43_pio_rxqueue { + struct b43_wldev *dev; + u16 mmio_base; + + /* Shortcut to the 802.11 core revision. This is to + * avoid horrible pointer dereferencing in the fastpaths. */ + u8 rev; +}; + + +static inline u16 b43_piotx_read16(struct b43_pio_txqueue *q, u16 offset) +{ + return b43_read16(q->dev, q->mmio_base + offset); +} + +static inline u32 b43_piotx_read32(struct b43_pio_txqueue *q, u16 offset) +{ + return b43_read32(q->dev, q->mmio_base + offset); +} + +static inline void b43_piotx_write16(struct b43_pio_txqueue *q, + u16 offset, u16 value) +{ + b43_write16(q->dev, q->mmio_base + offset, value); +} + +static inline void b43_piotx_write32(struct b43_pio_txqueue *q, + u16 offset, u32 value) +{ + b43_write32(q->dev, q->mmio_base + offset, value); +} + + +static inline u16 b43_piorx_read16(struct b43_pio_rxqueue *q, u16 offset) +{ + return b43_read16(q->dev, q->mmio_base + offset); +} + +static inline u32 b43_piorx_read32(struct b43_pio_rxqueue *q, u16 offset) +{ + return b43_read32(q->dev, q->mmio_base + offset); +} + +static inline void b43_piorx_write16(struct b43_pio_rxqueue *q, + u16 offset, u16 value) +{ + b43_write16(q->dev, q->mmio_base + offset, value); +} + +static inline void b43_piorx_write32(struct b43_pio_rxqueue *q, + u16 offset, u32 value) +{ + b43_write32(q->dev, q->mmio_base + offset, value); +} + + +int b43_pio_init(struct b43_wldev *dev); +void b43_pio_free(struct b43_wldev *dev); + +int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb); +void b43_pio_handle_txstatus(struct b43_wldev *dev, + const struct b43_txstatus *status); +void b43_pio_rx(struct b43_pio_rxqueue *q); + +void b43_pio_tx_suspend(struct b43_wldev *dev); +void b43_pio_tx_resume(struct b43_wldev *dev); + +#endif /* B43_PIO_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/ppr.c b/drivers/net/wireless/broadcom/b43/ppr.c new file mode 100644 index 000000000000..9a770279c415 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/ppr.c @@ -0,0 +1,199 @@ +/* + * Broadcom B43 wireless driver + * PPR (Power Per Rate) management + * + * Copyright (c) 2014 RafaÅ‚ MiÅ‚ecki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ppr.h" +#include "b43.h" + +#define ppr_for_each_entry(ppr, i, entry) \ + for (i = 0, entry = &(ppr)->__all_rates[i]; \ + i < B43_PPR_RATES_NUM; \ + i++, entry++) + +void b43_ppr_clear(struct b43_wldev *dev, struct b43_ppr *ppr) +{ + memset(ppr, 0, sizeof(*ppr)); + + /* Compile-time PPR check */ + BUILD_BUG_ON(sizeof(struct b43_ppr) != B43_PPR_RATES_NUM * sizeof(u8)); +} + +void b43_ppr_add(struct b43_wldev *dev, struct b43_ppr *ppr, int diff) +{ + int i; + u8 *rate; + + ppr_for_each_entry(ppr, i, rate) { + *rate = clamp_val(*rate + diff, 0, 127); + } +} + +void b43_ppr_apply_max(struct b43_wldev *dev, struct b43_ppr *ppr, u8 max) +{ + int i; + u8 *rate; + + ppr_for_each_entry(ppr, i, rate) { + *rate = min(*rate, max); + } +} + +void b43_ppr_apply_min(struct b43_wldev *dev, struct b43_ppr *ppr, u8 min) +{ + int i; + u8 *rate; + + ppr_for_each_entry(ppr, i, rate) { + *rate = max(*rate, min); + } +} + +u8 b43_ppr_get_max(struct b43_wldev *dev, struct b43_ppr *ppr) +{ + u8 res = 0; + int i; + u8 *rate; + + ppr_for_each_entry(ppr, i, rate) { + res = max(*rate, res); + } + + return res; +} + +bool b43_ppr_load_max_from_sprom(struct b43_wldev *dev, struct b43_ppr *ppr, + enum b43_band band) +{ + struct b43_ppr_rates *rates = &ppr->rates; + struct ssb_sprom *sprom = dev->dev->bus_sprom; + struct b43_phy *phy = &dev->phy; + u8 maxpwr, off; + u32 sprom_ofdm_po; + u16 *sprom_mcs_po; + u8 extra_cdd_po, extra_stbc_po; + int i; + + switch (band) { + case B43_BAND_2G: + maxpwr = min(sprom->core_pwr_info[0].maxpwr_2g, + sprom->core_pwr_info[1].maxpwr_2g); + sprom_ofdm_po = sprom->ofdm2gpo; + sprom_mcs_po = sprom->mcs2gpo; + extra_cdd_po = (sprom->cddpo >> 0) & 0xf; + extra_stbc_po = (sprom->stbcpo >> 0) & 0xf; + break; + case B43_BAND_5G_LO: + maxpwr = min(sprom->core_pwr_info[0].maxpwr_5gl, + sprom->core_pwr_info[1].maxpwr_5gl); + sprom_ofdm_po = sprom->ofdm5glpo; + sprom_mcs_po = sprom->mcs5glpo; + extra_cdd_po = (sprom->cddpo >> 8) & 0xf; + extra_stbc_po = (sprom->stbcpo >> 8) & 0xf; + break; + case B43_BAND_5G_MI: + maxpwr = min(sprom->core_pwr_info[0].maxpwr_5g, + sprom->core_pwr_info[1].maxpwr_5g); + sprom_ofdm_po = sprom->ofdm5gpo; + sprom_mcs_po = sprom->mcs5gpo; + extra_cdd_po = (sprom->cddpo >> 4) & 0xf; + extra_stbc_po = (sprom->stbcpo >> 4) & 0xf; + break; + case B43_BAND_5G_HI: + maxpwr = min(sprom->core_pwr_info[0].maxpwr_5gh, + sprom->core_pwr_info[1].maxpwr_5gh); + sprom_ofdm_po = sprom->ofdm5ghpo; + sprom_mcs_po = sprom->mcs5ghpo; + extra_cdd_po = (sprom->cddpo >> 12) & 0xf; + extra_stbc_po = (sprom->stbcpo >> 12) & 0xf; + break; + default: + WARN_ON_ONCE(1); + return false; + } + + if (band == B43_BAND_2G) { + for (i = 0; i < 4; i++) { + off = ((sprom->cck2gpo >> (i * 4)) & 0xf) * 2; + rates->cck[i] = maxpwr - off; + } + } + + /* OFDM */ + for (i = 0; i < 8; i++) { + off = ((sprom_ofdm_po >> (i * 4)) & 0xf) * 2; + rates->ofdm[i] = maxpwr - off; + } + + /* MCS 20 SISO */ + rates->mcs_20[0] = rates->ofdm[0]; + rates->mcs_20[1] = rates->ofdm[2]; + rates->mcs_20[2] = rates->ofdm[3]; + rates->mcs_20[3] = rates->ofdm[4]; + rates->mcs_20[4] = rates->ofdm[5]; + rates->mcs_20[5] = rates->ofdm[6]; + rates->mcs_20[6] = rates->ofdm[7]; + rates->mcs_20[7] = rates->ofdm[7]; + + /* MCS 20 CDD */ + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_cdd[i] = maxpwr - off; + if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) + rates->mcs_20_cdd[i] -= extra_cdd_po; + } + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_cdd[4 + i] = maxpwr - off; + if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) + rates->mcs_20_cdd[4 + i] -= extra_cdd_po; + } + + /* OFDM 20 CDD */ + rates->ofdm_20_cdd[0] = rates->mcs_20_cdd[0]; + rates->ofdm_20_cdd[1] = rates->mcs_20_cdd[0]; + rates->ofdm_20_cdd[2] = rates->mcs_20_cdd[1]; + rates->ofdm_20_cdd[3] = rates->mcs_20_cdd[2]; + rates->ofdm_20_cdd[4] = rates->mcs_20_cdd[3]; + rates->ofdm_20_cdd[5] = rates->mcs_20_cdd[4]; + rates->ofdm_20_cdd[6] = rates->mcs_20_cdd[5]; + rates->ofdm_20_cdd[7] = rates->mcs_20_cdd[6]; + + /* MCS 20 STBC */ + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_stbc[i] = maxpwr - off; + if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) + rates->mcs_20_stbc[i] -= extra_stbc_po; + } + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_stbc[4 + i] = maxpwr - off; + if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) + rates->mcs_20_stbc[4 + i] -= extra_stbc_po; + } + + /* MCS 20 SDM */ + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[2] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_sdm[i] = maxpwr - off; + } + for (i = 0; i < 4; i++) { + off = ((sprom_mcs_po[3] >> (i * 4)) & 0xf) * 2; + rates->mcs_20_sdm[4 + i] = maxpwr - off; + } + + return true; +} diff --git a/drivers/net/wireless/broadcom/b43/ppr.h b/drivers/net/wireless/broadcom/b43/ppr.h new file mode 100644 index 000000000000..24d7447e9f01 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/ppr.h @@ -0,0 +1,45 @@ +#ifndef LINUX_B43_PPR_H_ +#define LINUX_B43_PPR_H_ + +#include + +#define B43_PPR_CCK_RATES_NUM 4 +#define B43_PPR_OFDM_RATES_NUM 8 +#define B43_PPR_MCS_RATES_NUM 8 + +#define B43_PPR_RATES_NUM (B43_PPR_CCK_RATES_NUM + \ + B43_PPR_OFDM_RATES_NUM * 2 + \ + B43_PPR_MCS_RATES_NUM * 4) + +struct b43_ppr_rates { + u8 cck[B43_PPR_CCK_RATES_NUM]; + u8 ofdm[B43_PPR_OFDM_RATES_NUM]; + u8 ofdm_20_cdd[B43_PPR_OFDM_RATES_NUM]; + u8 mcs_20[B43_PPR_MCS_RATES_NUM]; /* SISO */ + u8 mcs_20_cdd[B43_PPR_MCS_RATES_NUM]; + u8 mcs_20_stbc[B43_PPR_MCS_RATES_NUM]; + u8 mcs_20_sdm[B43_PPR_MCS_RATES_NUM]; +}; + +struct b43_ppr { + /* All powers are in qdbm (Q5.2) */ + union { + u8 __all_rates[B43_PPR_RATES_NUM]; + struct b43_ppr_rates rates; + }; +}; + +struct b43_wldev; +enum b43_band; + +void b43_ppr_clear(struct b43_wldev *dev, struct b43_ppr *ppr); + +void b43_ppr_add(struct b43_wldev *dev, struct b43_ppr *ppr, int diff); +void b43_ppr_apply_max(struct b43_wldev *dev, struct b43_ppr *ppr, u8 max); +void b43_ppr_apply_min(struct b43_wldev *dev, struct b43_ppr *ppr, u8 min); +u8 b43_ppr_get_max(struct b43_wldev *dev, struct b43_ppr *ppr); + +bool b43_ppr_load_max_from_sprom(struct b43_wldev *dev, struct b43_ppr *ppr, + enum b43_band band); + +#endif /* LINUX_B43_PPR_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/radio_2055.c b/drivers/net/wireless/broadcom/b43/radio_2055.c new file mode 100644 index 000000000000..5289a18ddd8c --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/radio_2055.c @@ -0,0 +1,1335 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11n PHY and radio device data tables + + Copyright (c) 2008 Michael Buesch + Copyright (c) 2010 RafaÅ‚ MiÅ‚ecki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "radio_2055.h" +#include "phy_common.h" + +struct b2055_inittab_entry { + /* Value to write if we use the 5GHz band. */ + u16 ghz5; + /* Value to write if we use the 2.4GHz band. */ + u16 ghz2; + /* Flags */ + u8 flags; +#define B2055_INITTAB_ENTRY_OK 0x01 +#define B2055_INITTAB_UPLOAD 0x02 +}; +#define UPLOAD .flags = B2055_INITTAB_ENTRY_OK | B2055_INITTAB_UPLOAD +#define NOUPLOAD .flags = B2055_INITTAB_ENTRY_OK + +static const struct b2055_inittab_entry b2055_inittab [] = { + [B2055_SP_PINPD] = { .ghz5 = 0x0080, .ghz2 = 0x0080, NOUPLOAD, }, + [B2055_C1_SP_RSSI] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C1_SP_PDMISC] = { .ghz5 = 0x0027, .ghz2 = 0x0027, NOUPLOAD, }, + [B2055_C2_SP_RSSI] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C2_SP_PDMISC] = { .ghz5 = 0x0027, .ghz2 = 0x0027, NOUPLOAD, }, + [B2055_C1_SP_RXGC1] = { .ghz5 = 0x007F, .ghz2 = 0x007F, UPLOAD, }, + [B2055_C1_SP_RXGC2] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2055_C2_SP_RXGC1] = { .ghz5 = 0x007F, .ghz2 = 0x007F, UPLOAD, }, + [B2055_C2_SP_RXGC2] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2055_C1_SP_LPFBWSEL] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2055_C2_SP_LPFBWSEL] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2055_C1_SP_TXGC1] = { .ghz5 = 0x004F, .ghz2 = 0x004F, UPLOAD, }, + [B2055_C1_SP_TXGC2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, + [B2055_C2_SP_TXGC1] = { .ghz5 = 0x004F, .ghz2 = 0x004F, UPLOAD, }, + [B2055_C2_SP_TXGC2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, + [B2055_MASTER1] = { .ghz5 = 0x00D0, .ghz2 = 0x00D0, NOUPLOAD, }, + [B2055_MASTER2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2055_PD_LGEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_PD_PLLTS] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, + [B2055_C1_PD_LGBUF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C1_PD_TX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C1_PD_RXTX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C1_PD_RSSIMISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C2_PD_LGBUF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C2_PD_TX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C2_PD_RXTX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C2_PD_RSSIMISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_PWRDET_LGEN] = { .ghz5 = 0x00C0, .ghz2 = 0x00C0, NOUPLOAD, }, + [B2055_C1_PWRDET_LGBUF] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, + [B2055_C1_PWRDET_RXTX] = { .ghz5 = 0x00C0, .ghz2 = 0x00C0, NOUPLOAD, }, + [B2055_C2_PWRDET_LGBUF] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, + [B2055_C2_PWRDET_RXTX] = { .ghz5 = 0x00C0, .ghz2 = 0x00C0, NOUPLOAD, }, + [B2055_RRCCAL_CS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_RRCCAL_NOPTSEL] = { .ghz5 = 0x002C, .ghz2 = 0x002C, NOUPLOAD, }, + [B2055_CAL_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_CAL_COUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_CAL_COUT2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_CAL_CVARCTL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_CAL_RVARCTL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_CAL_LPOCTL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_CAL_TS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_CAL_RCCALRTS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_CAL_RCALRTS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_PADDRV] = { .ghz5 = 0x00A4, .ghz2 = 0x00A4, NOUPLOAD, }, + [B2055_XOCTL1] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, + [B2055_XOCTL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_XOREGUL] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, + [B2055_XOMISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_PLL_LFC1] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, + [B2055_PLL_CALVTH] = { .ghz5 = 0x0087, .ghz2 = 0x0087, NOUPLOAD, }, + [B2055_PLL_LFC2] = { .ghz5 = 0x0009, .ghz2 = 0x0009, NOUPLOAD, }, + [B2055_PLL_REF] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2055_PLL_LFR1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2055_PLL_PFDCP] = { .ghz5 = 0x0018, .ghz2 = 0x0018, UPLOAD, }, + [B2055_PLL_IDAC_CPOPAMP] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2055_PLL_CPREG] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, + [B2055_PLL_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2055_RF_PLLMOD0] = { .ghz5 = 0x009E, .ghz2 = 0x009E, NOUPLOAD, }, + [B2055_RF_PLLMOD1] = { .ghz5 = 0x0009, .ghz2 = 0x0009, NOUPLOAD, }, + [B2055_RF_MMDIDAC1] = { .ghz5 = 0x00C8, .ghz2 = 0x00C8, UPLOAD, }, + [B2055_RF_MMDIDAC0] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2055_RF_MMDSP] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_VCO_CAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_VCO_CAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_VCO_CAL3] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2055_VCO_CAL4] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2055_VCO_CAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, + [B2055_VCO_CAL6] = { .ghz5 = 0x003E, .ghz2 = 0x003E, NOUPLOAD, }, + [B2055_VCO_CAL7] = { .ghz5 = 0x003E, .ghz2 = 0x003E, NOUPLOAD, }, + [B2055_VCO_CAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2055_VCO_CAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2055_VCO_CAL10] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2055_VCO_CAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2055_VCO_CAL12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_VCO_CAL13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_VCO_CAL14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_VCO_CAL15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_VCO_CAL16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_VCO_KVCO] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2055_VCO_CAPTAIL] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2055_VCO_IDACVCO] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2055_VCO_REG] = { .ghz5 = 0x0084, .ghz2 = 0x0084, UPLOAD, }, + [B2055_PLL_RFVTH] = { .ghz5 = 0x00C3, .ghz2 = 0x00C3, NOUPLOAD, }, + [B2055_LGBUF_CENBUF] = { .ghz5 = 0x008F, .ghz2 = 0x008F, NOUPLOAD, }, + [B2055_LGEN_TUNE1] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, + [B2055_LGEN_TUNE2] = { .ghz5 = 0x00FF, .ghz2 = 0x00FF, NOUPLOAD, }, + [B2055_LGEN_IDAC1] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2055_LGEN_IDAC2] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2055_LGEN_BIASC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_LGEN_BIASIDAC] = { .ghz5 = 0x00CC, .ghz2 = 0x00CC, NOUPLOAD, }, + [B2055_LGEN_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2055_LGEN_DIV] = { .ghz5 = 0x0080, .ghz2 = 0x0080, NOUPLOAD, }, + [B2055_LGEN_SPARE2] = { .ghz5 = 0x0080, .ghz2 = 0x0080, NOUPLOAD, }, + [B2055_C1_LGBUF_ATUNE] = { .ghz5 = 0x00F8, .ghz2 = 0x00F8, NOUPLOAD, }, + [B2055_C1_LGBUF_GTUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2055_C1_LGBUF_DIV] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2055_C1_LGBUF_AIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0008, UPLOAD, }, + [B2055_C1_LGBUF_GIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2055_C1_LGBUF_IDACFO] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C1_LGBUF_SPARE] = { .ghz5 = 0x0001, .ghz2 = 0x0001, UPLOAD, }, + [B2055_C1_RX_RFSPC1] = { .ghz5 = 0x008A, .ghz2 = 0x008A, NOUPLOAD, }, + [B2055_C1_RX_RFR1] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2055_C1_RX_RFR2] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, + [B2055_C1_RX_RFRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2055_C1_RX_BB_BLCMP] = { .ghz5 = 0x00A0, .ghz2 = 0x00A0, NOUPLOAD, }, + [B2055_C1_RX_BB_LPF] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, + [B2055_C1_RX_BB_MIDACHP] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, + [B2055_C1_RX_BB_VGA1IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, + [B2055_C1_RX_BB_VGA2IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, + [B2055_C1_RX_BB_VGA3IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, + [B2055_C1_RX_BB_BUFOCTL] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, + [B2055_C1_RX_BB_RCCALCTL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2055_C1_RX_BB_RSSICTL1] = { .ghz5 = 0x006A, .ghz2 = 0x006A, UPLOAD, }, + [B2055_C1_RX_BB_RSSICTL2] = { .ghz5 = 0x00AB, .ghz2 = 0x00AB, UPLOAD, }, + [B2055_C1_RX_BB_RSSICTL3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, UPLOAD, }, + [B2055_C1_RX_BB_RSSICTL4] = { .ghz5 = 0x00C1, .ghz2 = 0x00C1, UPLOAD, }, + [B2055_C1_RX_BB_RSSICTL5] = { .ghz5 = 0x00AA, .ghz2 = 0x00AA, UPLOAD, }, + [B2055_C1_RX_BB_REG] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, + [B2055_C1_RX_BB_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C1_RX_TXBBRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2055_C1_TX_RF_SPGA] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2055_C1_TX_RF_SPAD] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2055_C1_TX_RF_CNTPGA1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2055_C1_TX_RF_CNTPAD1] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2055_C1_TX_RF_PGAIDAC] = { .ghz5 = 0x0097, .ghz2 = 0x0097, UPLOAD, }, + [B2055_C1_TX_PGAPADTN] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2055_C1_TX_PADIDAC1] = { .ghz5 = 0x0014, .ghz2 = 0x0014, UPLOAD, }, + [B2055_C1_TX_PADIDAC2] = { .ghz5 = 0x0033, .ghz2 = 0x0033, NOUPLOAD, }, + [B2055_C1_TX_MXBGTRIM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2055_C1_TX_RF_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2055_C1_TX_RF_PADTSSI1] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, + [B2055_C1_TX_RF_PADTSSI2] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, + [B2055_C1_TX_RF_SPARE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, + [B2055_C1_TX_RF_IQCAL1] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, + [B2055_C1_TX_RF_IQCAL2] = { .ghz5 = 0x00A4, .ghz2 = 0x00A4, NOUPLOAD, }, + [B2055_C1_TXBB_RCCAL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2055_C1_TXBB_LPF1] = { .ghz5 = 0x0028, .ghz2 = 0x0028, NOUPLOAD, }, + [B2055_C1_TX_VOSCNCL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C1_TX_LPF_MXGMIDAC] = { .ghz5 = 0x004A, .ghz2 = 0x004A, NOUPLOAD, }, + [B2055_C1_TX_BB_MXGM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C2_LGBUF_ATUNE] = { .ghz5 = 0x00F8, .ghz2 = 0x00F8, NOUPLOAD, }, + [B2055_C2_LGBUF_GTUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2055_C2_LGBUF_DIV] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2055_C2_LGBUF_AIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0008, UPLOAD, }, + [B2055_C2_LGBUF_GIDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2055_C2_LGBUF_IDACFO] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C2_LGBUF_SPARE] = { .ghz5 = 0x0001, .ghz2 = 0x0001, UPLOAD, }, + [B2055_C2_RX_RFSPC1] = { .ghz5 = 0x008A, .ghz2 = 0x008A, NOUPLOAD, }, + [B2055_C2_RX_RFR1] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2055_C2_RX_RFR2] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, + [B2055_C2_RX_RFRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2055_C2_RX_BB_BLCMP] = { .ghz5 = 0x00A0, .ghz2 = 0x00A0, NOUPLOAD, }, + [B2055_C2_RX_BB_LPF] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, + [B2055_C2_RX_BB_MIDACHP] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, + [B2055_C2_RX_BB_VGA1IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, + [B2055_C2_RX_BB_VGA2IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, + [B2055_C2_RX_BB_VGA3IDAC] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, + [B2055_C2_RX_BB_BUFOCTL] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, + [B2055_C2_RX_BB_RCCALCTL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2055_C2_RX_BB_RSSICTL1] = { .ghz5 = 0x006A, .ghz2 = 0x006A, UPLOAD, }, + [B2055_C2_RX_BB_RSSICTL2] = { .ghz5 = 0x00AB, .ghz2 = 0x00AB, UPLOAD, }, + [B2055_C2_RX_BB_RSSICTL3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, UPLOAD, }, + [B2055_C2_RX_BB_RSSICTL4] = { .ghz5 = 0x00C1, .ghz2 = 0x00C1, UPLOAD, }, + [B2055_C2_RX_BB_RSSICTL5] = { .ghz5 = 0x00AA, .ghz2 = 0x00AA, UPLOAD, }, + [B2055_C2_RX_BB_REG] = { .ghz5 = 0x0087, .ghz2 = 0x0087, UPLOAD, }, + [B2055_C2_RX_BB_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C2_RX_TXBBRCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2055_C2_TX_RF_SPGA] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2055_C2_TX_RF_SPAD] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2055_C2_TX_RF_CNTPGA1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2055_C2_TX_RF_CNTPAD1] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2055_C2_TX_RF_PGAIDAC] = { .ghz5 = 0x0097, .ghz2 = 0x0097, UPLOAD, }, + [B2055_C2_TX_PGAPADTN] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2055_C2_TX_PADIDAC1] = { .ghz5 = 0x0014, .ghz2 = 0x0014, UPLOAD, }, + [B2055_C2_TX_PADIDAC2] = { .ghz5 = 0x0033, .ghz2 = 0x0033, NOUPLOAD, }, + [B2055_C2_TX_MXBGTRIM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2055_C2_TX_RF_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2055_C2_TX_RF_PADTSSI1] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, + [B2055_C2_TX_RF_PADTSSI2] = { .ghz5 = 0x000A, .ghz2 = 0x000A, NOUPLOAD, }, + [B2055_C2_TX_RF_SPARE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, UPLOAD, }, + [B2055_C2_TX_RF_IQCAL1] = { .ghz5 = 0x002A, .ghz2 = 0x002A, NOUPLOAD, }, + [B2055_C2_TX_RF_IQCAL2] = { .ghz5 = 0x00A4, .ghz2 = 0x00A4, NOUPLOAD, }, + [B2055_C2_TXBB_RCCAL] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2055_C2_TXBB_LPF1] = { .ghz5 = 0x0028, .ghz2 = 0x0028, NOUPLOAD, }, + [B2055_C2_TX_VOSCNCL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C2_TX_LPF_MXGMIDAC] = { .ghz5 = 0x004A, .ghz2 = 0x004A, NOUPLOAD, }, + [B2055_C2_TX_BB_MXGM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_PRG_GCHP21] = { .ghz5 = 0x0071, .ghz2 = 0x0071, NOUPLOAD, }, + [B2055_PRG_GCHP22] = { .ghz5 = 0x0072, .ghz2 = 0x0072, NOUPLOAD, }, + [B2055_PRG_GCHP23] = { .ghz5 = 0x0073, .ghz2 = 0x0073, NOUPLOAD, }, + [B2055_PRG_GCHP24] = { .ghz5 = 0x0074, .ghz2 = 0x0074, NOUPLOAD, }, + [B2055_PRG_GCHP25] = { .ghz5 = 0x0075, .ghz2 = 0x0075, NOUPLOAD, }, + [B2055_PRG_GCHP26] = { .ghz5 = 0x0076, .ghz2 = 0x0076, NOUPLOAD, }, + [B2055_PRG_GCHP27] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2055_PRG_GCHP28] = { .ghz5 = 0x0078, .ghz2 = 0x0078, NOUPLOAD, }, + [B2055_PRG_GCHP29] = { .ghz5 = 0x0079, .ghz2 = 0x0079, NOUPLOAD, }, + [B2055_PRG_GCHP30] = { .ghz5 = 0x007A, .ghz2 = 0x007A, NOUPLOAD, }, + [0xC7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xC8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xC9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xCA] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xCB] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xCC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C1_LNA_GAINBST] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xCE] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [0xCF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xD0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xD1] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2055_C1_B0NB_RSSIVCM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [0xD3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xD4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xD5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C1_GENSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xD7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xD8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C2_LNA_GAINBST] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xDA] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [0xDB] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xDC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xDD] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2055_C2_B0NB_RSSIVCM] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [0xDF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xE0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [0xE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2055_C2_GENSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +#define RADIOREGS(r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, \ + r12, r13, r14, r15, r16, r17, r18, r19, r20, r21) \ + .radio_pll_ref = r0, \ + .radio_rf_pllmod0 = r1, \ + .radio_rf_pllmod1 = r2, \ + .radio_vco_captail = r3, \ + .radio_vco_cal1 = r4, \ + .radio_vco_cal2 = r5, \ + .radio_pll_lfc1 = r6, \ + .radio_pll_lfr1 = r7, \ + .radio_pll_lfc2 = r8, \ + .radio_lgbuf_cenbuf = r9, \ + .radio_lgen_tune1 = r10, \ + .radio_lgen_tune2 = r11, \ + .radio_c1_lgbuf_atune = r12, \ + .radio_c1_lgbuf_gtune = r13, \ + .radio_c1_rx_rfr1 = r14, \ + .radio_c1_tx_pgapadtn = r15, \ + .radio_c1_tx_mxbgtrim = r16, \ + .radio_c2_lgbuf_atune = r17, \ + .radio_c2_lgbuf_gtune = r18, \ + .radio_c2_rx_rfr1 = r19, \ + .radio_c2_tx_pgapadtn = r20, \ + .radio_c2_tx_mxbgtrim = r21 + +#define PHYREGS(r0, r1, r2, r3, r4, r5) \ + .phy_regs.phy_bw1a = r0, \ + .phy_regs.phy_bw2 = r1, \ + .phy_regs.phy_bw3 = r2, \ + .phy_regs.phy_bw4 = r3, \ + .phy_regs.phy_bw5 = r4, \ + .phy_regs.phy_bw6 = r5 + +static const struct b43_nphy_channeltab_entry_rev2 b43_nphy_channeltab_rev2[] = { + { .channel = 184, + .freq = 4920, /* MHz */ + .unk2 = 3280, + RADIOREGS(0x71, 0xEC, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07B4, 0x07B0, 0x07AC, 0x0214, 0x0215, 0x0216), + }, + { .channel = 186, + .freq = 4930, /* MHz */ + .unk2 = 3287, + RADIOREGS(0x71, 0xED, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07B8, 0x07B4, 0x07B0, 0x0213, 0x0214, 0x0215), + }, + { .channel = 188, + .freq = 4940, /* MHz */ + .unk2 = 3293, + RADIOREGS(0x71, 0xEE, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07BC, 0x07B8, 0x07B4, 0x0212, 0x0213, 0x0214), + }, + { .channel = 190, + .freq = 4950, /* MHz */ + .unk2 = 3300, + RADIOREGS(0x71, 0xEF, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07C0, 0x07BC, 0x07B8, 0x0211, 0x0212, 0x0213), + }, + { .channel = 192, + .freq = 4960, /* MHz */ + .unk2 = 3307, + RADIOREGS(0x71, 0xF0, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07C4, 0x07C0, 0x07BC, 0x020F, 0x0211, 0x0212), + }, + { .channel = 194, + .freq = 4970, /* MHz */ + .unk2 = 3313, + RADIOREGS(0x71, 0xF1, 0x01, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07C8, 0x07C4, 0x07C0, 0x020E, 0x020F, 0x0211), + }, + { .channel = 196, + .freq = 4980, /* MHz */ + .unk2 = 3320, + RADIOREGS(0x71, 0xF2, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07CC, 0x07C8, 0x07C4, 0x020D, 0x020E, 0x020F), + }, + { .channel = 198, + .freq = 4990, /* MHz */ + .unk2 = 3327, + RADIOREGS(0x71, 0xF3, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07D0, 0x07CC, 0x07C8, 0x020C, 0x020D, 0x020E), + }, + { .channel = 200, + .freq = 5000, /* MHz */ + .unk2 = 3333, + RADIOREGS(0x71, 0xF4, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07D4, 0x07D0, 0x07CC, 0x020B, 0x020C, 0x020D), + }, + { .channel = 202, + .freq = 5010, /* MHz */ + .unk2 = 3340, + RADIOREGS(0x71, 0xF5, 0x01, 0x0E, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07D8, 0x07D4, 0x07D0, 0x020A, 0x020B, 0x020C), + }, + { .channel = 204, + .freq = 5020, /* MHz */ + .unk2 = 3347, + RADIOREGS(0x71, 0xF6, 0x01, 0x0E, 0xF7, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07DC, 0x07D8, 0x07D4, 0x0209, 0x020A, 0x020B), + }, + { .channel = 206, + .freq = 5030, /* MHz */ + .unk2 = 3353, + RADIOREGS(0x71, 0xF7, 0x01, 0x0E, 0xF7, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07E0, 0x07DC, 0x07D8, 0x0208, 0x0209, 0x020A), + }, + { .channel = 208, + .freq = 5040, /* MHz */ + .unk2 = 3360, + RADIOREGS(0x71, 0xF8, 0x01, 0x0D, 0xEF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07E4, 0x07E0, 0x07DC, 0x0207, 0x0208, 0x0209), + }, + { .channel = 210, + .freq = 5050, /* MHz */ + .unk2 = 3367, + RADIOREGS(0x71, 0xF9, 0x01, 0x0D, 0xEF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, + 0x8F, 0xFF, 0x00, 0x0F, 0x0F, 0x8F), + PHYREGS(0x07E8, 0x07E4, 0x07E0, 0x0206, 0x0207, 0x0208), + }, + { .channel = 212, + .freq = 5060, /* MHz */ + .unk2 = 3373, + RADIOREGS(0x71, 0xFA, 0x01, 0x0D, 0xE6, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, + 0x8E, 0xFF, 0x00, 0x0E, 0x0F, 0x8E), + PHYREGS(0x07EC, 0x07E8, 0x07E4, 0x0205, 0x0206, 0x0207), + }, + { .channel = 214, + .freq = 5070, /* MHz */ + .unk2 = 3380, + RADIOREGS(0x71, 0xFB, 0x01, 0x0D, 0xE6, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, + 0x8E, 0xFF, 0x00, 0x0E, 0x0F, 0x8E), + PHYREGS(0x07F0, 0x07EC, 0x07E8, 0x0204, 0x0205, 0x0206), + }, + { .channel = 216, + .freq = 5080, /* MHz */ + .unk2 = 3387, + RADIOREGS(0x71, 0xFC, 0x01, 0x0D, 0xDE, 0x01, 0x04, 0x0A, + 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, + 0x8D, 0xEE, 0x00, 0x0E, 0x0F, 0x8D), + PHYREGS(0x07F4, 0x07F0, 0x07EC, 0x0203, 0x0204, 0x0205), + }, + { .channel = 218, + .freq = 5090, /* MHz */ + .unk2 = 3393, + RADIOREGS(0x71, 0xFD, 0x01, 0x0D, 0xDE, 0x01, 0x04, 0x0A, + 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, + 0x8D, 0xEE, 0x00, 0x0E, 0x0F, 0x8D), + PHYREGS(0x07F8, 0x07F4, 0x07F0, 0x0202, 0x0203, 0x0204), + }, + { .channel = 220, + .freq = 5100, /* MHz */ + .unk2 = 3400, + RADIOREGS(0x71, 0xFE, 0x01, 0x0C, 0xD6, 0x01, 0x04, 0x0A, + 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, + 0x8D, 0xEE, 0x00, 0x0D, 0x0F, 0x8D), + PHYREGS(0x07FC, 0x07F8, 0x07F4, 0x0201, 0x0202, 0x0203), + }, + { .channel = 222, + .freq = 5110, /* MHz */ + .unk2 = 3407, + RADIOREGS(0x71, 0xFF, 0x01, 0x0C, 0xD6, 0x01, 0x04, 0x0A, + 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, + 0x8D, 0xEE, 0x00, 0x0D, 0x0F, 0x8D), + PHYREGS(0x0800, 0x07FC, 0x07F8, 0x0200, 0x0201, 0x0202), + }, + { .channel = 224, + .freq = 5120, /* MHz */ + .unk2 = 3413, + RADIOREGS(0x71, 0x00, 0x02, 0x0C, 0xCE, 0x01, 0x04, 0x0A, + 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, + 0x8C, 0xDD, 0x00, 0x0D, 0x0F, 0x8C), + PHYREGS(0x0804, 0x0800, 0x07FC, 0x01FF, 0x0200, 0x0201), + }, + { .channel = 226, + .freq = 5130, /* MHz */ + .unk2 = 3420, + RADIOREGS(0x71, 0x01, 0x02, 0x0C, 0xCE, 0x01, 0x04, 0x0A, + 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, + 0x8C, 0xDD, 0x00, 0x0D, 0x0F, 0x8C), + PHYREGS(0x0808, 0x0804, 0x0800, 0x01FE, 0x01FF, 0x0200), + }, + { .channel = 228, + .freq = 5140, /* MHz */ + .unk2 = 3427, + RADIOREGS(0x71, 0x02, 0x02, 0x0C, 0xC6, 0x01, 0x04, 0x0A, + 0x00, 0x8D, 0x99, 0x99, 0xDD, 0x00, 0x0C, 0x0E, + 0x8B, 0xDD, 0x00, 0x0C, 0x0E, 0x8B), + PHYREGS(0x080C, 0x0808, 0x0804, 0x01FD, 0x01FE, 0x01FF), + }, + { .channel = 32, + .freq = 5160, /* MHz */ + .unk2 = 3440, + RADIOREGS(0x71, 0x04, 0x02, 0x0B, 0xBE, 0x01, 0x04, 0x0A, + 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, + 0x8A, 0xCC, 0x00, 0x0B, 0x0D, 0x8A), + PHYREGS(0x0814, 0x0810, 0x080C, 0x01FB, 0x01FC, 0x01FD), + }, + { .channel = 34, + .freq = 5170, /* MHz */ + .unk2 = 3447, + RADIOREGS(0x71, 0x05, 0x02, 0x0B, 0xBE, 0x01, 0x04, 0x0A, + 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, + 0x8A, 0xCC, 0x00, 0x0B, 0x0D, 0x8A), + PHYREGS(0x0818, 0x0814, 0x0810, 0x01FA, 0x01FB, 0x01FC), + }, + { .channel = 36, + .freq = 5180, /* MHz */ + .unk2 = 3453, + RADIOREGS(0x71, 0x06, 0x02, 0x0B, 0xB6, 0x01, 0x04, 0x0A, + 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, + 0x89, 0xCC, 0x00, 0x0B, 0x0C, 0x89), + PHYREGS(0x081C, 0x0818, 0x0814, 0x01F9, 0x01FA, 0x01FB), + }, + { .channel = 38, + .freq = 5190, /* MHz */ + .unk2 = 3460, + RADIOREGS(0x71, 0x07, 0x02, 0x0B, 0xB6, 0x01, 0x04, 0x0A, + 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, + 0x89, 0xCC, 0x00, 0x0B, 0x0C, 0x89), + PHYREGS(0x0820, 0x081C, 0x0818, 0x01F8, 0x01F9, 0x01FA), + }, + { .channel = 40, + .freq = 5200, /* MHz */ + .unk2 = 3467, + RADIOREGS(0x71, 0x08, 0x02, 0x0B, 0xAF, 0x01, 0x04, 0x0A, + 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, + 0x89, 0xBB, 0x00, 0x0A, 0x0B, 0x89), + PHYREGS(0x0824, 0x0820, 0x081C, 0x01F7, 0x01F8, 0x01F9), + }, + { .channel = 42, + .freq = 5210, /* MHz */ + .unk2 = 3473, + RADIOREGS(0x71, 0x09, 0x02, 0x0B, 0xAF, 0x01, 0x04, 0x0A, + 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, + 0x89, 0xBB, 0x00, 0x0A, 0x0B, 0x89), + PHYREGS(0x0828, 0x0824, 0x0820, 0x01F6, 0x01F7, 0x01F8), + }, + { .channel = 44, + .freq = 5220, /* MHz */ + .unk2 = 3480, + RADIOREGS(0x71, 0x0A, 0x02, 0x0A, 0xA7, 0x01, 0x04, 0x0A, + 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, + 0x88, 0xBB, 0x00, 0x09, 0x0A, 0x88), + PHYREGS(0x082C, 0x0828, 0x0824, 0x01F5, 0x01F6, 0x01F7), + }, + { .channel = 46, + .freq = 5230, /* MHz */ + .unk2 = 3487, + RADIOREGS(0x71, 0x0B, 0x02, 0x0A, 0xA7, 0x01, 0x04, 0x0A, + 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, + 0x88, 0xBB, 0x00, 0x09, 0x0A, 0x88), + PHYREGS(0x0830, 0x082C, 0x0828, 0x01F4, 0x01F5, 0x01F6), + }, + { .channel = 48, + .freq = 5240, /* MHz */ + .unk2 = 3493, + RADIOREGS(0x71, 0x0C, 0x02, 0x0A, 0xA0, 0x01, 0x04, 0x0A, + 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, + 0x87, 0xAA, 0x00, 0x09, 0x0A, 0x87), + PHYREGS(0x0834, 0x0830, 0x082C, 0x01F3, 0x01F4, 0x01F5), + }, + { .channel = 50, + .freq = 5250, /* MHz */ + .unk2 = 3500, + RADIOREGS(0x71, 0x0D, 0x02, 0x0A, 0xA0, 0x01, 0x04, 0x0A, + 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, + 0x87, 0xAA, 0x00, 0x09, 0x0A, 0x87), + PHYREGS(0x0838, 0x0834, 0x0830, 0x01F2, 0x01F3, 0x01F4), + }, + { .channel = 52, + .freq = 5260, /* MHz */ + .unk2 = 3507, + RADIOREGS(0x71, 0x0E, 0x02, 0x0A, 0x98, 0x01, 0x04, 0x0A, + 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, + 0x87, 0xAA, 0x00, 0x08, 0x09, 0x87), + PHYREGS(0x083C, 0x0838, 0x0834, 0x01F1, 0x01F2, 0x01F3), + }, + { .channel = 54, + .freq = 5270, /* MHz */ + .unk2 = 3513, + RADIOREGS(0x71, 0x0F, 0x02, 0x0A, 0x98, 0x01, 0x04, 0x0A, + 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, + 0x87, 0xAA, 0x00, 0x08, 0x09, 0x87), + PHYREGS(0x0840, 0x083C, 0x0838, 0x01F0, 0x01F1, 0x01F2), + }, + { .channel = 56, + .freq = 5280, /* MHz */ + .unk2 = 3520, + RADIOREGS(0x71, 0x10, 0x02, 0x09, 0x91, 0x01, 0x04, 0x0A, + 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, + 0x86, 0x99, 0x00, 0x08, 0x08, 0x86), + PHYREGS(0x0844, 0x0840, 0x083C, 0x01F0, 0x01F0, 0x01F1), + }, + { .channel = 58, + .freq = 5290, /* MHz */ + .unk2 = 3527, + RADIOREGS(0x71, 0x11, 0x02, 0x09, 0x91, 0x01, 0x04, 0x0A, + 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, + 0x86, 0x99, 0x00, 0x08, 0x08, 0x86), + PHYREGS(0x0848, 0x0844, 0x0840, 0x01EF, 0x01F0, 0x01F0), + }, + { .channel = 60, + .freq = 5300, /* MHz */ + .unk2 = 3533, + RADIOREGS(0x71, 0x12, 0x02, 0x09, 0x8A, 0x01, 0x04, 0x0A, + 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, + 0x85, 0x99, 0x00, 0x08, 0x07, 0x85), + PHYREGS(0x084C, 0x0848, 0x0844, 0x01EE, 0x01EF, 0x01F0), + }, + { .channel = 62, + .freq = 5310, /* MHz */ + .unk2 = 3540, + RADIOREGS(0x71, 0x13, 0x02, 0x09, 0x8A, 0x01, 0x04, 0x0A, + 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, + 0x85, 0x99, 0x00, 0x08, 0x07, 0x85), + PHYREGS(0x0850, 0x084C, 0x0848, 0x01ED, 0x01EE, 0x01EF), + }, + { .channel = 64, + .freq = 5320, /* MHz */ + .unk2 = 3547, + RADIOREGS(0x71, 0x14, 0x02, 0x09, 0x83, 0x01, 0x04, 0x0A, + 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, + 0x84, 0x88, 0x00, 0x07, 0x07, 0x84), + PHYREGS(0x0854, 0x0850, 0x084C, 0x01EC, 0x01ED, 0x01EE), + }, + { .channel = 66, + .freq = 5330, /* MHz */ + .unk2 = 3553, + RADIOREGS(0x71, 0x15, 0x02, 0x09, 0x83, 0x01, 0x04, 0x0A, + 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, + 0x84, 0x88, 0x00, 0x07, 0x07, 0x84), + PHYREGS(0x0858, 0x0854, 0x0850, 0x01EB, 0x01EC, 0x01ED), + }, + { .channel = 68, + .freq = 5340, /* MHz */ + .unk2 = 3560, + RADIOREGS(0x71, 0x16, 0x02, 0x08, 0x7C, 0x01, 0x04, 0x0A, + 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, + 0x84, 0x88, 0x00, 0x07, 0x06, 0x84), + PHYREGS(0x085C, 0x0858, 0x0854, 0x01EA, 0x01EB, 0x01EC), + }, + { .channel = 70, + .freq = 5350, /* MHz */ + .unk2 = 3567, + RADIOREGS(0x71, 0x17, 0x02, 0x08, 0x7C, 0x01, 0x04, 0x0A, + 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, + 0x84, 0x88, 0x00, 0x07, 0x06, 0x84), + PHYREGS(0x0860, 0x085C, 0x0858, 0x01E9, 0x01EA, 0x01EB), + }, + { .channel = 72, + .freq = 5360, /* MHz */ + .unk2 = 3573, + RADIOREGS(0x71, 0x18, 0x02, 0x08, 0x75, 0x01, 0x04, 0x0A, + 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, + 0x83, 0x77, 0x00, 0x06, 0x05, 0x83), + PHYREGS(0x0864, 0x0860, 0x085C, 0x01E8, 0x01E9, 0x01EA), + }, + { .channel = 74, + .freq = 5370, /* MHz */ + .unk2 = 3580, + RADIOREGS(0x71, 0x19, 0x02, 0x08, 0x75, 0x01, 0x04, 0x0A, + 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, + 0x83, 0x77, 0x00, 0x06, 0x05, 0x83), + PHYREGS(0x0868, 0x0864, 0x0860, 0x01E7, 0x01E8, 0x01E9), + }, + { .channel = 76, + .freq = 5380, /* MHz */ + .unk2 = 3587, + RADIOREGS(0x71, 0x1A, 0x02, 0x08, 0x6E, 0x01, 0x04, 0x0A, + 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, + 0x82, 0x77, 0x00, 0x06, 0x04, 0x82), + PHYREGS(0x086C, 0x0868, 0x0864, 0x01E6, 0x01E7, 0x01E8), + }, + { .channel = 78, + .freq = 5390, /* MHz */ + .unk2 = 3593, + RADIOREGS(0x71, 0x1B, 0x02, 0x08, 0x6E, 0x01, 0x04, 0x0A, + 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, + 0x82, 0x77, 0x00, 0x06, 0x04, 0x82), + PHYREGS(0x0870, 0x086C, 0x0868, 0x01E5, 0x01E6, 0x01E7), + }, + { .channel = 80, + .freq = 5400, /* MHz */ + .unk2 = 3600, + RADIOREGS(0x71, 0x1C, 0x02, 0x07, 0x67, 0x01, 0x04, 0x0A, + 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, + 0x81, 0x66, 0x00, 0x05, 0x04, 0x81), + PHYREGS(0x0874, 0x0870, 0x086C, 0x01E5, 0x01E5, 0x01E6), + }, + { .channel = 82, + .freq = 5410, /* MHz */ + .unk2 = 3607, + RADIOREGS(0x71, 0x1D, 0x02, 0x07, 0x67, 0x01, 0x04, 0x0A, + 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, + 0x81, 0x66, 0x00, 0x05, 0x04, 0x81), + PHYREGS(0x0878, 0x0874, 0x0870, 0x01E4, 0x01E5, 0x01E5), + }, + { .channel = 84, + .freq = 5420, /* MHz */ + .unk2 = 3613, + RADIOREGS(0x71, 0x1E, 0x02, 0x07, 0x61, 0x01, 0x04, 0x0A, + 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, + 0x80, 0x66, 0x00, 0x05, 0x03, 0x80), + PHYREGS(0x087C, 0x0878, 0x0874, 0x01E3, 0x01E4, 0x01E5), + }, + { .channel = 86, + .freq = 5430, /* MHz */ + .unk2 = 3620, + RADIOREGS(0x71, 0x1F, 0x02, 0x07, 0x61, 0x01, 0x04, 0x0A, + 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, + 0x80, 0x66, 0x00, 0x05, 0x03, 0x80), + PHYREGS(0x0880, 0x087C, 0x0878, 0x01E2, 0x01E3, 0x01E4), + }, + { .channel = 88, + .freq = 5440, /* MHz */ + .unk2 = 3627, + RADIOREGS(0x71, 0x20, 0x02, 0x07, 0x5A, 0x01, 0x04, 0x0A, + 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, + 0x80, 0x55, 0x00, 0x04, 0x02, 0x80), + PHYREGS(0x0884, 0x0880, 0x087C, 0x01E1, 0x01E2, 0x01E3), + }, + { .channel = 90, + .freq = 5450, /* MHz */ + .unk2 = 3633, + RADIOREGS(0x71, 0x21, 0x02, 0x07, 0x5A, 0x01, 0x04, 0x0A, + 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, + 0x80, 0x55, 0x00, 0x04, 0x02, 0x80), + PHYREGS(0x0888, 0x0884, 0x0880, 0x01E0, 0x01E1, 0x01E2), + }, + { .channel = 92, + .freq = 5460, /* MHz */ + .unk2 = 3640, + RADIOREGS(0x71, 0x22, 0x02, 0x06, 0x53, 0x01, 0x04, 0x0A, + 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, + 0x80, 0x55, 0x00, 0x04, 0x01, 0x80), + PHYREGS(0x088C, 0x0888, 0x0884, 0x01DF, 0x01E0, 0x01E1), + }, + { .channel = 94, + .freq = 5470, /* MHz */ + .unk2 = 3647, + RADIOREGS(0x71, 0x23, 0x02, 0x06, 0x53, 0x01, 0x04, 0x0A, + 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, + 0x80, 0x55, 0x00, 0x04, 0x01, 0x80), + PHYREGS(0x0890, 0x088C, 0x0888, 0x01DE, 0x01DF, 0x01E0), + }, + { .channel = 96, + .freq = 5480, /* MHz */ + .unk2 = 3653, + RADIOREGS(0x71, 0x24, 0x02, 0x06, 0x4D, 0x01, 0x04, 0x0A, + 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, + 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), + PHYREGS(0x0894, 0x0890, 0x088C, 0x01DD, 0x01DE, 0x01DF), + }, + { .channel = 98, + .freq = 5490, /* MHz */ + .unk2 = 3660, + RADIOREGS(0x71, 0x25, 0x02, 0x06, 0x4D, 0x01, 0x04, 0x0A, + 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, + 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), + PHYREGS(0x0898, 0x0894, 0x0890, 0x01DD, 0x01DD, 0x01DE), + }, + { .channel = 100, + .freq = 5500, /* MHz */ + .unk2 = 3667, + RADIOREGS(0x71, 0x26, 0x02, 0x06, 0x47, 0x01, 0x04, 0x0A, + 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, + 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), + PHYREGS(0x089C, 0x0898, 0x0894, 0x01DC, 0x01DD, 0x01DD), + }, + { .channel = 102, + .freq = 5510, /* MHz */ + .unk2 = 3673, + RADIOREGS(0x71, 0x27, 0x02, 0x06, 0x47, 0x01, 0x04, 0x0A, + 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, + 0x80, 0x44, 0x00, 0x03, 0x00, 0x80), + PHYREGS(0x08A0, 0x089C, 0x0898, 0x01DB, 0x01DC, 0x01DD), + }, + { .channel = 104, + .freq = 5520, /* MHz */ + .unk2 = 3680, + RADIOREGS(0x71, 0x28, 0x02, 0x05, 0x40, 0x01, 0x04, 0x0A, + 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, + 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), + PHYREGS(0x08A4, 0x08A0, 0x089C, 0x01DA, 0x01DB, 0x01DC), + }, + { .channel = 106, + .freq = 5530, /* MHz */ + .unk2 = 3687, + RADIOREGS(0x71, 0x29, 0x02, 0x05, 0x40, 0x01, 0x04, 0x0A, + 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, + 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), + PHYREGS(0x08A8, 0x08A4, 0x08A0, 0x01D9, 0x01DA, 0x01DB), + }, + { .channel = 108, + .freq = 5540, /* MHz */ + .unk2 = 3693, + RADIOREGS(0x71, 0x2A, 0x02, 0x05, 0x3A, 0x01, 0x04, 0x0A, + 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, + 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), + PHYREGS(0x08AC, 0x08A8, 0x08A4, 0x01D8, 0x01D9, 0x01DA), + }, + { .channel = 110, + .freq = 5550, /* MHz */ + .unk2 = 3700, + RADIOREGS(0x71, 0x2B, 0x02, 0x05, 0x3A, 0x01, 0x04, 0x0A, + 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, + 0x80, 0x33, 0x00, 0x02, 0x00, 0x80), + PHYREGS(0x08B0, 0x08AC, 0x08A8, 0x01D7, 0x01D8, 0x01D9), + }, + { .channel = 112, + .freq = 5560, /* MHz */ + .unk2 = 3707, + RADIOREGS(0x71, 0x2C, 0x02, 0x05, 0x34, 0x01, 0x04, 0x0A, + 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, + 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), + PHYREGS(0x08B4, 0x08B0, 0x08AC, 0x01D7, 0x01D7, 0x01D8), + }, + { .channel = 114, + .freq = 5570, /* MHz */ + .unk2 = 3713, + RADIOREGS(0x71, 0x2D, 0x02, 0x05, 0x34, 0x01, 0x04, 0x0A, + 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, + 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), + PHYREGS(0x08B8, 0x08B4, 0x08B0, 0x01D6, 0x01D7, 0x01D7), + }, + { .channel = 116, + .freq = 5580, /* MHz */ + .unk2 = 3720, + RADIOREGS(0x71, 0x2E, 0x02, 0x04, 0x2E, 0x01, 0x04, 0x0A, + 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, + 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), + PHYREGS(0x08BC, 0x08B8, 0x08B4, 0x01D5, 0x01D6, 0x01D7), + }, + { .channel = 118, + .freq = 5590, /* MHz */ + .unk2 = 3727, + RADIOREGS(0x71, 0x2F, 0x02, 0x04, 0x2E, 0x01, 0x04, 0x0A, + 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, + 0x80, 0x22, 0x00, 0x01, 0x00, 0x80), + PHYREGS(0x08C0, 0x08BC, 0x08B8, 0x01D4, 0x01D5, 0x01D6), + }, + { .channel = 120, + .freq = 5600, /* MHz */ + .unk2 = 3733, + RADIOREGS(0x71, 0x30, 0x02, 0x04, 0x28, 0x01, 0x04, 0x0A, + 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, + 0x80, 0x11, 0x00, 0x01, 0x00, 0x80), + PHYREGS(0x08C4, 0x08C0, 0x08BC, 0x01D3, 0x01D4, 0x01D5), + }, + { .channel = 122, + .freq = 5610, /* MHz */ + .unk2 = 3740, + RADIOREGS(0x71, 0x31, 0x02, 0x04, 0x28, 0x01, 0x04, 0x0A, + 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, + 0x80, 0x11, 0x00, 0x01, 0x00, 0x80), + PHYREGS(0x08C8, 0x08C4, 0x08C0, 0x01D2, 0x01D3, 0x01D4), + }, + { .channel = 124, + .freq = 5620, /* MHz */ + .unk2 = 3747, + RADIOREGS(0x71, 0x32, 0x02, 0x04, 0x21, 0x01, 0x04, 0x0A, + 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x80, 0x11, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08CC, 0x08C8, 0x08C4, 0x01D2, 0x01D2, 0x01D3), + }, + { .channel = 126, + .freq = 5630, /* MHz */ + .unk2 = 3753, + RADIOREGS(0x71, 0x33, 0x02, 0x04, 0x21, 0x01, 0x04, 0x0A, + 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x80, 0x11, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08D0, 0x08CC, 0x08C8, 0x01D1, 0x01D2, 0x01D2), + }, + { .channel = 128, + .freq = 5640, /* MHz */ + .unk2 = 3760, + RADIOREGS(0x71, 0x34, 0x02, 0x03, 0x1C, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08D4, 0x08D0, 0x08CC, 0x01D0, 0x01D1, 0x01D2), + }, + { .channel = 130, + .freq = 5650, /* MHz */ + .unk2 = 3767, + RADIOREGS(0x71, 0x35, 0x02, 0x03, 0x1C, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08D8, 0x08D4, 0x08D0, 0x01CF, 0x01D0, 0x01D1), + }, + { .channel = 132, + .freq = 5660, /* MHz */ + .unk2 = 3773, + RADIOREGS(0x71, 0x36, 0x02, 0x03, 0x16, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08DC, 0x08D8, 0x08D4, 0x01CE, 0x01CF, 0x01D0), + }, + { .channel = 134, + .freq = 5670, /* MHz */ + .unk2 = 3780, + RADIOREGS(0x71, 0x37, 0x02, 0x03, 0x16, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08E0, 0x08DC, 0x08D8, 0x01CE, 0x01CE, 0x01CF), + }, + { .channel = 136, + .freq = 5680, /* MHz */ + .unk2 = 3787, + RADIOREGS(0x71, 0x38, 0x02, 0x03, 0x10, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08E4, 0x08E0, 0x08DC, 0x01CD, 0x01CE, 0x01CE), + }, + { .channel = 138, + .freq = 5690, /* MHz */ + .unk2 = 3793, + RADIOREGS(0x71, 0x39, 0x02, 0x03, 0x10, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08E8, 0x08E4, 0x08E0, 0x01CC, 0x01CD, 0x01CE), + }, + { .channel = 140, + .freq = 5700, /* MHz */ + .unk2 = 3800, + RADIOREGS(0x71, 0x3A, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08EC, 0x08E8, 0x08E4, 0x01CB, 0x01CC, 0x01CD), + }, + { .channel = 142, + .freq = 5710, /* MHz */ + .unk2 = 3807, + RADIOREGS(0x71, 0x3B, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08F0, 0x08EC, 0x08E8, 0x01CA, 0x01CB, 0x01CC), + }, + { .channel = 144, + .freq = 5720, /* MHz */ + .unk2 = 3813, + RADIOREGS(0x71, 0x3C, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08F4, 0x08F0, 0x08EC, 0x01C9, 0x01CA, 0x01CB), + }, + { .channel = 145, + .freq = 5725, /* MHz */ + .unk2 = 3817, + RADIOREGS(0x72, 0x79, 0x04, 0x02, 0x03, 0x01, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08F6, 0x08F2, 0x08EE, 0x01C9, 0x01CA, 0x01CB), + }, + { .channel = 146, + .freq = 5730, /* MHz */ + .unk2 = 3820, + RADIOREGS(0x71, 0x3D, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08F8, 0x08F4, 0x08F0, 0x01C9, 0x01C9, 0x01CA), + }, + { .channel = 147, + .freq = 5735, /* MHz */ + .unk2 = 3823, + RADIOREGS(0x72, 0x7B, 0x04, 0x02, 0x03, 0x01, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08FA, 0x08F6, 0x08F2, 0x01C8, 0x01C9, 0x01CA), + }, + { .channel = 148, + .freq = 5740, /* MHz */ + .unk2 = 3827, + RADIOREGS(0x71, 0x3E, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08FC, 0x08F8, 0x08F4, 0x01C8, 0x01C9, 0x01C9), + }, + { .channel = 149, + .freq = 5745, /* MHz */ + .unk2 = 3830, + RADIOREGS(0x72, 0x7D, 0x04, 0x02, 0xFE, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x08FE, 0x08FA, 0x08F6, 0x01C8, 0x01C8, 0x01C9), + }, + { .channel = 150, + .freq = 5750, /* MHz */ + .unk2 = 3833, + RADIOREGS(0x71, 0x3F, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0900, 0x08FC, 0x08F8, 0x01C7, 0x01C8, 0x01C9), + }, + { .channel = 151, + .freq = 5755, /* MHz */ + .unk2 = 3837, + RADIOREGS(0x72, 0x7F, 0x04, 0x02, 0xFE, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0902, 0x08FE, 0x08FA, 0x01C7, 0x01C8, 0x01C8), + }, + { .channel = 152, + .freq = 5760, /* MHz */ + .unk2 = 3840, + RADIOREGS(0x71, 0x40, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0904, 0x0900, 0x08FC, 0x01C6, 0x01C7, 0x01C8), + }, + { .channel = 153, + .freq = 5765, /* MHz */ + .unk2 = 3843, + RADIOREGS(0x72, 0x81, 0x04, 0x02, 0xF8, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0906, 0x0902, 0x08FE, 0x01C6, 0x01C7, 0x01C8), + }, + { .channel = 154, + .freq = 5770, /* MHz */ + .unk2 = 3847, + RADIOREGS(0x71, 0x41, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0908, 0x0904, 0x0900, 0x01C6, 0x01C6, 0x01C7), + }, + { .channel = 155, + .freq = 5775, /* MHz */ + .unk2 = 3850, + RADIOREGS(0x72, 0x83, 0x04, 0x02, 0xF8, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x090A, 0x0906, 0x0902, 0x01C5, 0x01C6, 0x01C7), + }, + { .channel = 156, + .freq = 5780, /* MHz */ + .unk2 = 3853, + RADIOREGS(0x71, 0x42, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x090C, 0x0908, 0x0904, 0x01C5, 0x01C6, 0x01C6), + }, + { .channel = 157, + .freq = 5785, /* MHz */ + .unk2 = 3857, + RADIOREGS(0x72, 0x85, 0x04, 0x02, 0xF2, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x090E, 0x090A, 0x0906, 0x01C4, 0x01C5, 0x01C6), + }, + { .channel = 158, + .freq = 5790, /* MHz */ + .unk2 = 3860, + RADIOREGS(0x71, 0x43, 0x02, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0910, 0x090C, 0x0908, 0x01C4, 0x01C5, 0x01C6), + }, + { .channel = 159, + .freq = 5795, /* MHz */ + .unk2 = 3863, + RADIOREGS(0x72, 0x87, 0x04, 0x02, 0xF2, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0912, 0x090E, 0x090A, 0x01C4, 0x01C4, 0x01C5), + }, + { .channel = 160, + .freq = 5800, /* MHz */ + .unk2 = 3867, + RADIOREGS(0x71, 0x44, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0914, 0x0910, 0x090C, 0x01C3, 0x01C4, 0x01C5), + }, + { .channel = 161, + .freq = 5805, /* MHz */ + .unk2 = 3870, + RADIOREGS(0x72, 0x89, 0x04, 0x01, 0xED, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0916, 0x0912, 0x090E, 0x01C3, 0x01C4, 0x01C4), + }, + { .channel = 162, + .freq = 5810, /* MHz */ + .unk2 = 3873, + RADIOREGS(0x71, 0x45, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0918, 0x0914, 0x0910, 0x01C2, 0x01C3, 0x01C4), + }, + { .channel = 163, + .freq = 5815, /* MHz */ + .unk2 = 3877, + RADIOREGS(0x72, 0x8B, 0x04, 0x01, 0xED, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x091A, 0x0916, 0x0912, 0x01C2, 0x01C3, 0x01C4), + }, + { .channel = 164, + .freq = 5820, /* MHz */ + .unk2 = 3880, + RADIOREGS(0x71, 0x46, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x091C, 0x0918, 0x0914, 0x01C2, 0x01C2, 0x01C3), + }, + { .channel = 165, + .freq = 5825, /* MHz */ + .unk2 = 3883, + RADIOREGS(0x72, 0x8D, 0x04, 0x01, 0xED, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x091E, 0x091A, 0x0916, 0x01C1, 0x01C2, 0x01C3), + }, + { .channel = 166, + .freq = 5830, /* MHz */ + .unk2 = 3887, + RADIOREGS(0x71, 0x47, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0920, 0x091C, 0x0918, 0x01C1, 0x01C2, 0x01C2), + }, + { .channel = 168, + .freq = 5840, /* MHz */ + .unk2 = 3893, + RADIOREGS(0x71, 0x48, 0x02, 0x01, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0924, 0x0920, 0x091C, 0x01C0, 0x01C1, 0x01C2), + }, + { .channel = 170, + .freq = 5850, /* MHz */ + .unk2 = 3900, + RADIOREGS(0x71, 0x49, 0x02, 0x01, 0xE0, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0928, 0x0924, 0x0920, 0x01BF, 0x01C0, 0x01C1), + }, + { .channel = 172, + .freq = 5860, /* MHz */ + .unk2 = 3907, + RADIOREGS(0x71, 0x4A, 0x02, 0x01, 0xDE, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x092C, 0x0928, 0x0924, 0x01BF, 0x01BF, 0x01C0), + }, + { .channel = 174, + .freq = 5870, /* MHz */ + .unk2 = 3913, + RADIOREGS(0x71, 0x4B, 0x02, 0x00, 0xDB, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0930, 0x092C, 0x0928, 0x01BE, 0x01BF, 0x01BF), + }, + { .channel = 176, + .freq = 5880, /* MHz */ + .unk2 = 3920, + RADIOREGS(0x71, 0x4C, 0x02, 0x00, 0xD8, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0934, 0x0930, 0x092C, 0x01BD, 0x01BE, 0x01BF), + }, + { .channel = 178, + .freq = 5890, /* MHz */ + .unk2 = 3927, + RADIOREGS(0x71, 0x4D, 0x02, 0x00, 0xD6, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0938, 0x0934, 0x0930, 0x01BC, 0x01BD, 0x01BE), + }, + { .channel = 180, + .freq = 5900, /* MHz */ + .unk2 = 3933, + RADIOREGS(0x71, 0x4E, 0x02, 0x00, 0xD3, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x093C, 0x0938, 0x0934, 0x01BC, 0x01BC, 0x01BD), + }, + { .channel = 182, + .freq = 5910, /* MHz */ + .unk2 = 3940, + RADIOREGS(0x71, 0x4F, 0x02, 0x00, 0xD6, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x80), + PHYREGS(0x0940, 0x093C, 0x0938, 0x01BB, 0x01BC, 0x01BC), + }, + { .channel = 1, + .freq = 2412, /* MHz */ + .unk2 = 3216, + RADIOREGS(0x73, 0x6C, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0D, 0x0C, + 0x80, 0xFF, 0x88, 0x0D, 0x0C, 0x80), + PHYREGS(0x03C9, 0x03C5, 0x03C1, 0x043A, 0x043F, 0x0443), + }, + { .channel = 2, + .freq = 2417, /* MHz */ + .unk2 = 3223, + RADIOREGS(0x73, 0x71, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0B, + 0x80, 0xFF, 0x88, 0x0C, 0x0B, 0x80), + PHYREGS(0x03CB, 0x03C7, 0x03C3, 0x0438, 0x043D, 0x0441), + }, + { .channel = 3, + .freq = 2422, /* MHz */ + .unk2 = 3229, + RADIOREGS(0x73, 0x76, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, + 0x80, 0xFF, 0x88, 0x0C, 0x0A, 0x80), + PHYREGS(0x03CD, 0x03C9, 0x03C5, 0x0436, 0x043A, 0x043F), + }, + { .channel = 4, + .freq = 2427, /* MHz */ + .unk2 = 3236, + RADIOREGS(0x73, 0x7B, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, + 0x80, 0xFF, 0x88, 0x0C, 0x0A, 0x80), + PHYREGS(0x03CF, 0x03CB, 0x03C7, 0x0434, 0x0438, 0x043D), + }, + { .channel = 5, + .freq = 2432, /* MHz */ + .unk2 = 3243, + RADIOREGS(0x73, 0x80, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x09, + 0x80, 0xFF, 0x88, 0x0C, 0x09, 0x80), + PHYREGS(0x03D1, 0x03CD, 0x03C9, 0x0431, 0x0436, 0x043A), + }, + { .channel = 6, + .freq = 2437, /* MHz */ + .unk2 = 3249, + RADIOREGS(0x73, 0x85, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0B, 0x08, + 0x80, 0xFF, 0x88, 0x0B, 0x08, 0x80), + PHYREGS(0x03D3, 0x03CF, 0x03CB, 0x042F, 0x0434, 0x0438), + }, + { .channel = 7, + .freq = 2442, /* MHz */ + .unk2 = 3256, + RADIOREGS(0x73, 0x8A, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x07, + 0x80, 0xFF, 0x88, 0x0A, 0x07, 0x80), + PHYREGS(0x03D5, 0x03D1, 0x03CD, 0x042D, 0x0431, 0x0436), + }, + { .channel = 8, + .freq = 2447, /* MHz */ + .unk2 = 3263, + RADIOREGS(0x73, 0x8F, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x06, + 0x80, 0xFF, 0x88, 0x0A, 0x06, 0x80), + PHYREGS(0x03D7, 0x03D3, 0x03CF, 0x042B, 0x042F, 0x0434), + }, + { .channel = 9, + .freq = 2452, /* MHz */ + .unk2 = 3269, + RADIOREGS(0x73, 0x94, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x09, 0x06, + 0x80, 0xFF, 0x88, 0x09, 0x06, 0x80), + PHYREGS(0x03D9, 0x03D5, 0x03D1, 0x0429, 0x042D, 0x0431), + }, + { .channel = 10, + .freq = 2457, /* MHz */ + .unk2 = 3276, + RADIOREGS(0x73, 0x99, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x05, + 0x80, 0xFF, 0x88, 0x08, 0x05, 0x80), + PHYREGS(0x03DB, 0x03D7, 0x03D3, 0x0427, 0x042B, 0x042F), + }, + { .channel = 11, + .freq = 2462, /* MHz */ + .unk2 = 3283, + RADIOREGS(0x73, 0x9E, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x04, + 0x80, 0xFF, 0x88, 0x08, 0x04, 0x80), + PHYREGS(0x03DD, 0x03D9, 0x03D5, 0x0424, 0x0429, 0x042D), + }, + { .channel = 12, + .freq = 2467, /* MHz */ + .unk2 = 3289, + RADIOREGS(0x73, 0xA3, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x03, + 0x80, 0xFF, 0x88, 0x08, 0x03, 0x80), + PHYREGS(0x03DF, 0x03DB, 0x03D7, 0x0422, 0x0427, 0x042B), + }, + { .channel = 13, + .freq = 2472, /* MHz */ + .unk2 = 3296, + RADIOREGS(0x73, 0xA8, 0x09, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x03, + 0x80, 0xFF, 0x88, 0x07, 0x03, 0x80), + PHYREGS(0x03E1, 0x03DD, 0x03D9, 0x0420, 0x0424, 0x0429), + }, + { .channel = 14, + .freq = 2484, /* MHz */ + .unk2 = 3312, + RADIOREGS(0x73, 0xB4, 0x09, 0x0F, 0xFF, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x01, + 0x80, 0xFF, 0x88, 0x07, 0x01, 0x80), + PHYREGS(0x03E6, 0x03E2, 0x03DE, 0x041B, 0x041F, 0x0424), + }, +}; + +void b2055_upload_inittab(struct b43_wldev *dev, + bool ghz5, bool ignore_uploadflag) +{ + const struct b2055_inittab_entry *e; + unsigned int i, writes = 0; + u16 value; + + for (i = 0; i < ARRAY_SIZE(b2055_inittab); i++) { + e = &(b2055_inittab[i]); + if (!(e->flags & B2055_INITTAB_ENTRY_OK)) + continue; + if ((e->flags & B2055_INITTAB_UPLOAD) || ignore_uploadflag) { + if (ghz5) + value = e->ghz5; + else + value = e->ghz2; + b43_radio_write16(dev, i, value); + if (++writes % 4 == 0) + b43_read32(dev, B43_MMIO_MACCTL); /* flush */ + } + } +} + +const struct b43_nphy_channeltab_entry_rev2 * +b43_nphy_get_chantabent_rev2(struct b43_wldev *dev, u8 channel) +{ + const struct b43_nphy_channeltab_entry_rev2 *e; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(b43_nphy_channeltab_rev2); i++) { + e = &(b43_nphy_channeltab_rev2[i]); + if (e->channel == channel) + return e; + } + + return NULL; +} diff --git a/drivers/net/wireless/broadcom/b43/radio_2055.h b/drivers/net/wireless/broadcom/b43/radio_2055.h new file mode 100644 index 000000000000..67f96122f8d8 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/radio_2055.h @@ -0,0 +1,259 @@ +#ifndef B43_RADIO_2055_H_ +#define B43_RADIO_2055_H_ + +#include + +#include "tables_nphy.h" + +#define B2055_GEN_SPARE 0x00 /* GEN spare */ +#define B2055_SP_PINPD 0x02 /* SP PIN PD */ +#define B2055_C1_SP_RSSI 0x03 /* SP RSSI Core 1 */ +#define B2055_C1_SP_PDMISC 0x04 /* SP PD MISC Core 1 */ +#define B2055_C2_SP_RSSI 0x05 /* SP RSSI Core 2 */ +#define B2055_C2_SP_PDMISC 0x06 /* SP PD MISC Core 2 */ +#define B2055_C1_SP_RXGC1 0x07 /* SP RX GC1 Core 1 */ +#define B2055_C1_SP_RXGC2 0x08 /* SP RX GC2 Core 1 */ +#define B2055_C2_SP_RXGC1 0x09 /* SP RX GC1 Core 2 */ +#define B2055_C2_SP_RXGC2 0x0A /* SP RX GC2 Core 2 */ +#define B2055_C1_SP_LPFBWSEL 0x0B /* SP LPF BW select Core 1 */ +#define B2055_C2_SP_LPFBWSEL 0x0C /* SP LPF BW select Core 2 */ +#define B2055_C1_SP_TXGC1 0x0D /* SP TX GC1 Core 1 */ +#define B2055_C1_SP_TXGC2 0x0E /* SP TX GC2 Core 1 */ +#define B2055_C2_SP_TXGC1 0x0F /* SP TX GC1 Core 2 */ +#define B2055_C2_SP_TXGC2 0x10 /* SP TX GC2 Core 2 */ +#define B2055_MASTER1 0x11 /* Master control 1 */ +#define B2055_MASTER2 0x12 /* Master control 2 */ +#define B2055_PD_LGEN 0x13 /* PD LGEN */ +#define B2055_PD_PLLTS 0x14 /* PD PLL TS */ +#define B2055_C1_PD_LGBUF 0x15 /* PD Core 1 LGBUF */ +#define B2055_C1_PD_TX 0x16 /* PD Core 1 TX */ +#define B2055_C1_PD_RXTX 0x17 /* PD Core 1 RXTX */ +#define B2055_C1_PD_RSSIMISC 0x18 /* PD Core 1 RSSI MISC */ +#define B2055_C2_PD_LGBUF 0x19 /* PD Core 2 LGBUF */ +#define B2055_C2_PD_TX 0x1A /* PD Core 2 TX */ +#define B2055_C2_PD_RXTX 0x1B /* PD Core 2 RXTX */ +#define B2055_C2_PD_RSSIMISC 0x1C /* PD Core 2 RSSI MISC */ +#define B2055_PWRDET_LGEN 0x1D /* PWRDET LGEN */ +#define B2055_C1_PWRDET_LGBUF 0x1E /* PWRDET LGBUF Core 1 */ +#define B2055_C1_PWRDET_RXTX 0x1F /* PWRDET RXTX Core 1 */ +#define B2055_C2_PWRDET_LGBUF 0x20 /* PWRDET LGBUF Core 2 */ +#define B2055_C2_PWRDET_RXTX 0x21 /* PWRDET RXTX Core 2 */ +#define B2055_RRCCAL_CS 0x22 /* RRCCAL Control spare */ +#define B2055_RRCCAL_NOPTSEL 0x23 /* RRCCAL N OPT SEL */ +#define B2055_CAL_MISC 0x24 /* CAL MISC */ +#define B2055_CAL_COUT 0x25 /* CAL Counter out */ +#define B2055_CAL_COUT2 0x26 /* CAL Counter out 2 */ +#define B2055_CAL_CVARCTL 0x27 /* CAL CVAR Control */ +#define B2055_CAL_RVARCTL 0x28 /* CAL RVAR Control */ +#define B2055_CAL_LPOCTL 0x29 /* CAL LPO Control */ +#define B2055_CAL_TS 0x2A /* CAL TS */ +#define B2055_CAL_RCCALRTS 0x2B /* CAL RCCAL READ TS */ +#define B2055_CAL_RCALRTS 0x2C /* CAL RCAL READ TS */ +#define B2055_PADDRV 0x2D /* PAD driver */ +#define B2055_XOCTL1 0x2E /* XO Control 1 */ +#define B2055_XOCTL2 0x2F /* XO Control 2 */ +#define B2055_XOREGUL 0x30 /* XO Regulator */ +#define B2055_XOMISC 0x31 /* XO misc */ +#define B2055_PLL_LFC1 0x32 /* PLL LF C1 */ +#define B2055_PLL_CALVTH 0x33 /* PLL CAL VTH */ +#define B2055_PLL_LFC2 0x34 /* PLL LF C2 */ +#define B2055_PLL_REF 0x35 /* PLL reference */ +#define B2055_PLL_LFR1 0x36 /* PLL LF R1 */ +#define B2055_PLL_PFDCP 0x37 /* PLL PFD CP */ +#define B2055_PLL_IDAC_CPOPAMP 0x38 /* PLL IDAC CPOPAMP */ +#define B2055_PLL_CPREG 0x39 /* PLL CP Regulator */ +#define B2055_PLL_RCAL 0x3A /* PLL RCAL */ +#define B2055_RF_PLLMOD0 0x3B /* RF PLL MOD0 */ +#define B2055_RF_PLLMOD1 0x3C /* RF PLL MOD1 */ +#define B2055_RF_MMDIDAC1 0x3D /* RF MMD IDAC 1 */ +#define B2055_RF_MMDIDAC0 0x3E /* RF MMD IDAC 0 */ +#define B2055_RF_MMDSP 0x3F /* RF MMD spare */ +#define B2055_VCO_CAL1 0x40 /* VCO cal 1 */ +#define B2055_VCO_CAL2 0x41 /* VCO cal 2 */ +#define B2055_VCO_CAL3 0x42 /* VCO cal 3 */ +#define B2055_VCO_CAL4 0x43 /* VCO cal 4 */ +#define B2055_VCO_CAL5 0x44 /* VCO cal 5 */ +#define B2055_VCO_CAL6 0x45 /* VCO cal 6 */ +#define B2055_VCO_CAL7 0x46 /* VCO cal 7 */ +#define B2055_VCO_CAL8 0x47 /* VCO cal 8 */ +#define B2055_VCO_CAL9 0x48 /* VCO cal 9 */ +#define B2055_VCO_CAL10 0x49 /* VCO cal 10 */ +#define B2055_VCO_CAL11 0x4A /* VCO cal 11 */ +#define B2055_VCO_CAL12 0x4B /* VCO cal 12 */ +#define B2055_VCO_CAL13 0x4C /* VCO cal 13 */ +#define B2055_VCO_CAL14 0x4D /* VCO cal 14 */ +#define B2055_VCO_CAL15 0x4E /* VCO cal 15 */ +#define B2055_VCO_CAL16 0x4F /* VCO cal 16 */ +#define B2055_VCO_KVCO 0x50 /* VCO KVCO */ +#define B2055_VCO_CAPTAIL 0x51 /* VCO CAP TAIL */ +#define B2055_VCO_IDACVCO 0x52 /* VCO IDAC VCO */ +#define B2055_VCO_REG 0x53 /* VCO Regulator */ +#define B2055_PLL_RFVTH 0x54 /* PLL RF VTH */ +#define B2055_LGBUF_CENBUF 0x55 /* LGBUF CEN BUF */ +#define B2055_LGEN_TUNE1 0x56 /* LGEN tune 1 */ +#define B2055_LGEN_TUNE2 0x57 /* LGEN tune 2 */ +#define B2055_LGEN_IDAC1 0x58 /* LGEN IDAC 1 */ +#define B2055_LGEN_IDAC2 0x59 /* LGEN IDAC 2 */ +#define B2055_LGEN_BIASC 0x5A /* LGEN BIAS counter */ +#define B2055_LGEN_BIASIDAC 0x5B /* LGEN BIAS IDAC */ +#define B2055_LGEN_RCAL 0x5C /* LGEN RCAL */ +#define B2055_LGEN_DIV 0x5D /* LGEN div */ +#define B2055_LGEN_SPARE2 0x5E /* LGEN spare 2 */ +#define B2055_C1_LGBUF_ATUNE 0x5F /* Core 1 LGBUF A tune */ +#define B2055_C1_LGBUF_GTUNE 0x60 /* Core 1 LGBUF G tune */ +#define B2055_C1_LGBUF_DIV 0x61 /* Core 1 LGBUF div */ +#define B2055_C1_LGBUF_AIDAC 0x62 /* Core 1 LGBUF A IDAC */ +#define B2055_C1_LGBUF_GIDAC 0x63 /* Core 1 LGBUF G IDAC */ +#define B2055_C1_LGBUF_IDACFO 0x64 /* Core 1 LGBUF IDAC filter override */ +#define B2055_C1_LGBUF_SPARE 0x65 /* Core 1 LGBUF spare */ +#define B2055_C1_RX_RFSPC1 0x66 /* Core 1 RX RF SPC1 */ +#define B2055_C1_RX_RFR1 0x67 /* Core 1 RX RF reg 1 */ +#define B2055_C1_RX_RFR2 0x68 /* Core 1 RX RF reg 2 */ +#define B2055_C1_RX_RFRCAL 0x69 /* Core 1 RX RF RCAL */ +#define B2055_C1_RX_BB_BLCMP 0x6A /* Core 1 RX Baseband BUFI LPF CMP */ +#define B2055_C1_RX_BB_LPF 0x6B /* Core 1 RX Baseband LPF */ +#define B2055_C1_RX_BB_MIDACHP 0x6C /* Core 1 RX Baseband MIDAC High-pass */ +#define B2055_C1_RX_BB_VGA1IDAC 0x6D /* Core 1 RX Baseband VGA1 IDAC */ +#define B2055_C1_RX_BB_VGA2IDAC 0x6E /* Core 1 RX Baseband VGA2 IDAC */ +#define B2055_C1_RX_BB_VGA3IDAC 0x6F /* Core 1 RX Baseband VGA3 IDAC */ +#define B2055_C1_RX_BB_BUFOCTL 0x70 /* Core 1 RX Baseband BUFO Control */ +#define B2055_C1_RX_BB_RCCALCTL 0x71 /* Core 1 RX Baseband RCCAL Control */ +#define B2055_C1_RX_BB_RSSICTL1 0x72 /* Core 1 RX Baseband RSSI Control 1 */ +#define B2055_C1_RX_BB_RSSICTL2 0x73 /* Core 1 RX Baseband RSSI Control 2 */ +#define B2055_C1_RX_BB_RSSICTL3 0x74 /* Core 1 RX Baseband RSSI Control 3 */ +#define B2055_C1_RX_BB_RSSICTL4 0x75 /* Core 1 RX Baseband RSSI Control 4 */ +#define B2055_C1_RX_BB_RSSICTL5 0x76 /* Core 1 RX Baseband RSSI Control 5 */ +#define B2055_C1_RX_BB_REG 0x77 /* Core 1 RX Baseband Regulator */ +#define B2055_C1_RX_BB_SPARE1 0x78 /* Core 1 RX Baseband spare 1 */ +#define B2055_C1_RX_TXBBRCAL 0x79 /* Core 1 RX TX BB RCAL */ +#define B2055_C1_TX_RF_SPGA 0x7A /* Core 1 TX RF SGM PGA */ +#define B2055_C1_TX_RF_SPAD 0x7B /* Core 1 TX RF SGM PAD */ +#define B2055_C1_TX_RF_CNTPGA1 0x7C /* Core 1 TX RF counter PGA 1 */ +#define B2055_C1_TX_RF_CNTPAD1 0x7D /* Core 1 TX RF counter PAD 1 */ +#define B2055_C1_TX_RF_PGAIDAC 0x7E /* Core 1 TX RF PGA IDAC */ +#define B2055_C1_TX_PGAPADTN 0x7F /* Core 1 TX PGA PAD TN */ +#define B2055_C1_TX_PADIDAC1 0x80 /* Core 1 TX PAD IDAC 1 */ +#define B2055_C1_TX_PADIDAC2 0x81 /* Core 1 TX PAD IDAC 2 */ +#define B2055_C1_TX_MXBGTRIM 0x82 /* Core 1 TX MX B/G TRIM */ +#define B2055_C1_TX_RF_RCAL 0x83 /* Core 1 TX RF RCAL */ +#define B2055_C1_TX_RF_PADTSSI1 0x84 /* Core 1 TX RF PAD TSSI1 */ +#define B2055_C1_TX_RF_PADTSSI2 0x85 /* Core 1 TX RF PAD TSSI2 */ +#define B2055_C1_TX_RF_SPARE 0x86 /* Core 1 TX RF spare */ +#define B2055_C1_TX_RF_IQCAL1 0x87 /* Core 1 TX RF I/Q CAL 1 */ +#define B2055_C1_TX_RF_IQCAL2 0x88 /* Core 1 TX RF I/Q CAL 2 */ +#define B2055_C1_TXBB_RCCAL 0x89 /* Core 1 TXBB RC CAL Control */ +#define B2055_C1_TXBB_LPF1 0x8A /* Core 1 TXBB LPF 1 */ +#define B2055_C1_TX_VOSCNCL 0x8B /* Core 1 TX VOS CNCL */ +#define B2055_C1_TX_LPF_MXGMIDAC 0x8C /* Core 1 TX LPF MXGM IDAC */ +#define B2055_C1_TX_BB_MXGM 0x8D /* Core 1 TX BB MXGM */ +#define B2055_C2_LGBUF_ATUNE 0x8E /* Core 2 LGBUF A tune */ +#define B2055_C2_LGBUF_GTUNE 0x8F /* Core 2 LGBUF G tune */ +#define B2055_C2_LGBUF_DIV 0x90 /* Core 2 LGBUF div */ +#define B2055_C2_LGBUF_AIDAC 0x91 /* Core 2 LGBUF A IDAC */ +#define B2055_C2_LGBUF_GIDAC 0x92 /* Core 2 LGBUF G IDAC */ +#define B2055_C2_LGBUF_IDACFO 0x93 /* Core 2 LGBUF IDAC filter override */ +#define B2055_C2_LGBUF_SPARE 0x94 /* Core 2 LGBUF spare */ +#define B2055_C2_RX_RFSPC1 0x95 /* Core 2 RX RF SPC1 */ +#define B2055_C2_RX_RFR1 0x96 /* Core 2 RX RF reg 1 */ +#define B2055_C2_RX_RFR2 0x97 /* Core 2 RX RF reg 2 */ +#define B2055_C2_RX_RFRCAL 0x98 /* Core 2 RX RF RCAL */ +#define B2055_C2_RX_BB_BLCMP 0x99 /* Core 2 RX Baseband BUFI LPF CMP */ +#define B2055_C2_RX_BB_LPF 0x9A /* Core 2 RX Baseband LPF */ +#define B2055_C2_RX_BB_MIDACHP 0x9B /* Core 2 RX Baseband MIDAC High-pass */ +#define B2055_C2_RX_BB_VGA1IDAC 0x9C /* Core 2 RX Baseband VGA1 IDAC */ +#define B2055_C2_RX_BB_VGA2IDAC 0x9D /* Core 2 RX Baseband VGA2 IDAC */ +#define B2055_C2_RX_BB_VGA3IDAC 0x9E /* Core 2 RX Baseband VGA3 IDAC */ +#define B2055_C2_RX_BB_BUFOCTL 0x9F /* Core 2 RX Baseband BUFO Control */ +#define B2055_C2_RX_BB_RCCALCTL 0xA0 /* Core 2 RX Baseband RCCAL Control */ +#define B2055_C2_RX_BB_RSSICTL1 0xA1 /* Core 2 RX Baseband RSSI Control 1 */ +#define B2055_C2_RX_BB_RSSICTL2 0xA2 /* Core 2 RX Baseband RSSI Control 2 */ +#define B2055_C2_RX_BB_RSSICTL3 0xA3 /* Core 2 RX Baseband RSSI Control 3 */ +#define B2055_C2_RX_BB_RSSICTL4 0xA4 /* Core 2 RX Baseband RSSI Control 4 */ +#define B2055_C2_RX_BB_RSSICTL5 0xA5 /* Core 2 RX Baseband RSSI Control 5 */ +#define B2055_C2_RX_BB_REG 0xA6 /* Core 2 RX Baseband Regulator */ +#define B2055_C2_RX_BB_SPARE1 0xA7 /* Core 2 RX Baseband spare 1 */ +#define B2055_C2_RX_TXBBRCAL 0xA8 /* Core 2 RX TX BB RCAL */ +#define B2055_C2_TX_RF_SPGA 0xA9 /* Core 2 TX RF SGM PGA */ +#define B2055_C2_TX_RF_SPAD 0xAA /* Core 2 TX RF SGM PAD */ +#define B2055_C2_TX_RF_CNTPGA1 0xAB /* Core 2 TX RF counter PGA 1 */ +#define B2055_C2_TX_RF_CNTPAD1 0xAC /* Core 2 TX RF counter PAD 1 */ +#define B2055_C2_TX_RF_PGAIDAC 0xAD /* Core 2 TX RF PGA IDAC */ +#define B2055_C2_TX_PGAPADTN 0xAE /* Core 2 TX PGA PAD TN */ +#define B2055_C2_TX_PADIDAC1 0xAF /* Core 2 TX PAD IDAC 1 */ +#define B2055_C2_TX_PADIDAC2 0xB0 /* Core 2 TX PAD IDAC 2 */ +#define B2055_C2_TX_MXBGTRIM 0xB1 /* Core 2 TX MX B/G TRIM */ +#define B2055_C2_TX_RF_RCAL 0xB2 /* Core 2 TX RF RCAL */ +#define B2055_C2_TX_RF_PADTSSI1 0xB3 /* Core 2 TX RF PAD TSSI1 */ +#define B2055_C2_TX_RF_PADTSSI2 0xB4 /* Core 2 TX RF PAD TSSI2 */ +#define B2055_C2_TX_RF_SPARE 0xB5 /* Core 2 TX RF spare */ +#define B2055_C2_TX_RF_IQCAL1 0xB6 /* Core 2 TX RF I/Q CAL 1 */ +#define B2055_C2_TX_RF_IQCAL2 0xB7 /* Core 2 TX RF I/Q CAL 2 */ +#define B2055_C2_TXBB_RCCAL 0xB8 /* Core 2 TXBB RC CAL Control */ +#define B2055_C2_TXBB_LPF1 0xB9 /* Core 2 TXBB LPF 1 */ +#define B2055_C2_TX_VOSCNCL 0xBA /* Core 2 TX VOS CNCL */ +#define B2055_C2_TX_LPF_MXGMIDAC 0xBB /* Core 2 TX LPF MXGM IDAC */ +#define B2055_C2_TX_BB_MXGM 0xBC /* Core 2 TX BB MXGM */ +#define B2055_PRG_GCHP21 0xBD /* PRG GC HPVGA23 21 */ +#define B2055_PRG_GCHP22 0xBE /* PRG GC HPVGA23 22 */ +#define B2055_PRG_GCHP23 0xBF /* PRG GC HPVGA23 23 */ +#define B2055_PRG_GCHP24 0xC0 /* PRG GC HPVGA23 24 */ +#define B2055_PRG_GCHP25 0xC1 /* PRG GC HPVGA23 25 */ +#define B2055_PRG_GCHP26 0xC2 /* PRG GC HPVGA23 26 */ +#define B2055_PRG_GCHP27 0xC3 /* PRG GC HPVGA23 27 */ +#define B2055_PRG_GCHP28 0xC4 /* PRG GC HPVGA23 28 */ +#define B2055_PRG_GCHP29 0xC5 /* PRG GC HPVGA23 29 */ +#define B2055_PRG_GCHP30 0xC6 /* PRG GC HPVGA23 30 */ +#define B2055_C1_LNA_GAINBST 0xCD /* Core 1 LNA GAINBST */ +#define B2055_C1_B0NB_RSSIVCM 0xD2 /* Core 1 B0 narrow-band RSSI VCM */ +#define B2055_C1_GENSPARE2 0xD6 /* Core 1 GEN spare 2 */ +#define B2055_C2_LNA_GAINBST 0xD9 /* Core 2 LNA GAINBST */ +#define B2055_C2_B0NB_RSSIVCM 0xDE /* Core 2 B0 narrow-band RSSI VCM */ +#define B2055_C2_GENSPARE2 0xE2 /* Core 2 GEN spare 2 */ + +struct b43_nphy_channeltab_entry_rev2 { + /* The channel number */ + u8 channel; + /* The channel frequency in MHz */ + u16 freq; + /* An unknown value */ + u16 unk2; + /* Radio register values on channelswitch */ + u8 radio_pll_ref; + u8 radio_rf_pllmod0; + u8 radio_rf_pllmod1; + u8 radio_vco_captail; + u8 radio_vco_cal1; + u8 radio_vco_cal2; + u8 radio_pll_lfc1; + u8 radio_pll_lfr1; + u8 radio_pll_lfc2; + u8 radio_lgbuf_cenbuf; + u8 radio_lgen_tune1; + u8 radio_lgen_tune2; + u8 radio_c1_lgbuf_atune; + u8 radio_c1_lgbuf_gtune; + u8 radio_c1_rx_rfr1; + u8 radio_c1_tx_pgapadtn; + u8 radio_c1_tx_mxbgtrim; + u8 radio_c2_lgbuf_atune; + u8 radio_c2_lgbuf_gtune; + u8 radio_c2_rx_rfr1; + u8 radio_c2_tx_pgapadtn; + u8 radio_c2_tx_mxbgtrim; + /* PHY register values on channelswitch */ + struct b43_phy_n_sfo_cfg phy_regs; +}; + +/* Upload the default register value table. + * If "ghz5" is true, we upload the 5Ghz table. Otherwise the 2.4Ghz + * table is uploaded. If "ignore_uploadflag" is true, we upload any value + * and ignore the "UPLOAD" flag. */ +void b2055_upload_inittab(struct b43_wldev *dev, + bool ghz5, bool ignore_uploadflag); + +/* Get the NPHY Channel Switch Table entry for a channel. + * Returns NULL on failure to find an entry. */ +const struct b43_nphy_channeltab_entry_rev2 * +b43_nphy_get_chantabent_rev2(struct b43_wldev *dev, u8 channel); + +#endif /* B43_RADIO_2055_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/radio_2056.c b/drivers/net/wireless/broadcom/b43/radio_2056.c new file mode 100644 index 000000000000..2ce25607c60d --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/radio_2056.c @@ -0,0 +1,10318 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11n 2056 radio device data tables + + Copyright (c) 2010 RafaÅ‚ MiÅ‚ecki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "radio_2056.h" +#include "phy_common.h" + +struct b2056_inittab_entry { + /* Value to write if we use the 5GHz band. */ + u16 ghz5; + /* Value to write if we use the 2.4GHz band. */ + u16 ghz2; + /* Flags */ + u8 flags; +}; +#define B2056_INITTAB_ENTRY_OK 0x01 +#define B2056_INITTAB_UPLOAD 0x02 +#define UPLOAD .flags = B2056_INITTAB_ENTRY_OK | B2056_INITTAB_UPLOAD +#define NOUPLOAD .flags = B2056_INITTAB_ENTRY_OK + +struct b2056_inittabs_pts { + const struct b2056_inittab_entry *syn; + unsigned int syn_length; + const struct b2056_inittab_entry *tx; + unsigned int tx_length; + const struct b2056_inittab_entry *rx; + unsigned int rx_length; +}; + +static const struct b2056_inittab_entry b2056_inittab_phy_rev3_syn[] = { + [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, + [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, + [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, + [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, + [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, + [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_phy_rev3_tx[] = { + [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, + [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_TX_PA_SPARE2] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x002d, .ghz2 = 0x002d, NOUPLOAD, }, + [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, + [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, + [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0074, .ghz2 = 0x0074, UPLOAD, }, + [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, + [B2056_TX_TXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_phy_rev3_rx[] = { + [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0099, .ghz2 = 0x0099, NOUPLOAD, }, + [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0044, .ghz2 = 0x0044, UPLOAD, }, + [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0099, .ghz2 = 0x0099, NOUPLOAD, }, + [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_RX_TIA_IMISC] = { .ghz5 = 0x0057, .ghz2 = 0x0057, NOUPLOAD, }, + [B2056_RX_TIA_QMISC] = { .ghz5 = 0x0057, .ghz2 = 0x0057, NOUPLOAD, }, + [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, + [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, + [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, + [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, + [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_phy_rev4_syn[] = { + [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, + [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, + [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, + [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, + [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, + [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_phy_rev4_tx[] = { + [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, + [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_TX_PA_SPARE2] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x002d, .ghz2 = 0x002d, NOUPLOAD, }, + [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, + [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, + [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0072, .ghz2 = 0x0072, UPLOAD, }, + [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, + [B2056_TX_TXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_phy_rev4_rx[] = { + [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0044, .ghz2 = 0x0044, UPLOAD, }, + [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x002f, .ghz2 = 0x002f, UPLOAD, }, + [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, + [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, + [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, + [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev5_syn[] = { + [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, + [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, + [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, + [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, + [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, + [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev5_tx[] = { + [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, + [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_TX_PA_SPARE2] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x002d, .ghz2 = 0x002d, NOUPLOAD, }, + [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, + [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, + [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, + [B2056_TX_TXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, + [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, + [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0072, .ghz2 = 0x0072, UPLOAD, }, + [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0073, .ghz2 = 0x0073, UPLOAD, }, + [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0074, .ghz2 = 0x0074, UPLOAD, }, + [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev5_rx[] = { + [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, + [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, + [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, + [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, + [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, + [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev6_syn[] = { + [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, + [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_CP2] = { .ghz5 = 0x003f, .ghz2 = 0x003f, UPLOAD, }, + [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x002b, .ghz2 = 0x002b, UPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, + [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, + [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, + [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, + [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev6_tx[] = { + [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, + [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, + [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, + [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, + [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, + [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, + [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev6_rx[] = { + [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, + [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, + [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, + [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, + [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, + [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE3] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, + [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_syn[] = { + [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, + [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, + [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, + [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, + [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, + [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_tx[] = { + [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, + [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, + [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, + [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, + [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, + [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, + [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, + [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0071, .ghz2 = 0x0071, UPLOAD, }, + [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0072, .ghz2 = 0x0072, UPLOAD, }, + [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0073, .ghz2 = 0x0073, UPLOAD, }, + [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0074, .ghz2 = 0x0074, UPLOAD, }, + [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_rx[] = { + [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, + [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, + [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, + [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, + [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, + [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev8_syn[] = { + [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_PU] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_GPIO_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_MASTER] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_TOPBIAS_RCAL] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_AFEREG] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSE] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSEIDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_TEMPPROCSENSERCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LPO] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_VDDCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_VDDCAL_STATUS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCAL_CODE_OUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL1] = { .ghz5 = 0x001f, .ghz2 = 0x001f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL2] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL3] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_RCCAL_CTRL11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_ZCAL_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_MAST1] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_MAST2] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_MAST3] = { .ghz5 = 0x0018, .ghz2 = 0x0018, NOUPLOAD, }, + [B2056_SYN_PLL_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL1] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL3] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL5] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_SYN_PLL_XTAL6] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_REFDIV] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_CP1] = { .ghz5 = 0x000f, .ghz2 = 0x000f, NOUPLOAD, }, + [B2056_SYN_PLL_CP2] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, + [B2056_SYN_PLL_CP3] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x000d, .ghz2 = 0x000d, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER3] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER5] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_PLL_MMD1] = { .ghz5 = 0x001c, .ghz2 = 0x001c, NOUPLOAD, }, + [B2056_SYN_PLL_MMD2] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO1] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, + [B2056_SYN_PLL_MONITOR1] = { .ghz5 = 0x00b4, .ghz2 = 0x00b4, NOUPLOAD, }, + [B2056_SYN_PLL_MONITOR2] = { .ghz5 = 0x00d2, .ghz2 = 0x00d2, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL4] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL5] = { .ghz5 = 0x0096, .ghz2 = 0x0096, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL6] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL7] = { .ghz5 = 0x003e, .ghz2 = 0x003e, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL8] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL9] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL11] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_SYN_PLL_VCOCAL13] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_SYN_PLL_VREG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_PLL_STATUS3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU2] = { .ghz5 = 0x0040, .ghz2 = 0x0040, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PU8] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_BIAS_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RCCR1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF1] = { .ghz5 = 0x0060, .ghz2 = 0x0060, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER2] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF1] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, + [B2056_SYN_LOGEN_BUF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF4] = { .ghz5 = 0x00cc, .ghz2 = 0x00cc, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV1] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV2] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_DIV3] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLOUT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACLCAL3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_CALEN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_PEAKDET1] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_DIFF_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_ACL_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_VCOBUF2_OVRVAL]= { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_SYN_LOGEN_MIXER3_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF5_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_BUF6_OVRVAL] = { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CBUFTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSRX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX1_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX2_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX3_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_CMOSTX4_OVRVAL]= { .ghz5 = 0x0066, .ghz2 = 0x0066, NOUPLOAD, }, + [B2056_SYN_LOGEN_ACL_WAITCNT] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_SYN_LOGEN_CORE_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_RX_CMOS_CALVALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev8_tx[] = { + [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_GAIN_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_FINE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_I] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_LOFT_COARSE_Q] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER1] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_TX_COM_MASTER2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_RXIQCAL_TXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_SSI_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_IQCAL_VCM_HG] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_IQCAL_IDAC] = { .ghz5 = 0x0037, .ghz2 = 0x0037, NOUPLOAD, }, + [B2056_TX_TSSI_VCM] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_TX_AMP_DET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TX_SSI_MUX] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSIA] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSIG] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_TSSI_MISC1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TSSI_MISC3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PA_SPARE1] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, + [B2056_TX_INTPAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_TX_INTPAA_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_TX_INTPAA_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAA_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAA_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_BOOST_TUNE] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_STAT] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_TX_INTPAG_IAUX_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_STAT] = { .ghz5 = 0x001e, .ghz2 = 0x001e, NOUPLOAD, }, + [B2056_TX_INTPAG_IMAIN_DYN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_INTPAG_CASCBIAS] = { .ghz5 = 0x006e, .ghz2 = 0x006e, NOUPLOAD, }, + [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAG_PA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PADA_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADA_BOOST_TUNE] = { .ghz5 = 0x0038, .ghz2 = 0x0038, NOUPLOAD, }, + [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PADG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PADG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PADG_CASCBIAS] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_TX_PADG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PADG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PGAA_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PGAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAA_BOOST_TUNE] = { .ghz5 = 0x0083, .ghz2 = 0x0083, NOUPLOAD, }, + [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAA_MISC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_MASTER] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_PGAG_IDAC] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_TX_PGAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_PGAG_BOOST_TUNE] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAG_MISC] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_TX_MIXA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXA_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_MIXG] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_MIXG_BOOST_TUNE] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_TX_BB_GM_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_GM] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_TX_TXLPF_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_RCCAL_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_BW] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_TX_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_0] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_1] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_2] = { .ghz5 = 0x000e, .ghz2 = 0x000e, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_3] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_4] = { .ghz5 = 0x0013, .ghz2 = 0x0013, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_5] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_IDAC_6] = { .ghz5 = 0x001b, .ghz2 = 0x001b, NOUPLOAD, }, + [B2056_TX_TXLPF_OPAMP_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_TX_TXLPF_MISC] = { .ghz5 = 0x005b, .ghz2 = 0x005b, NOUPLOAD, }, + [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, + [B2056_TX_TXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_TXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_INTPA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PAD_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_PGA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_GM_TXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC0] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC1] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC2] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC3] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC4] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC5] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC6] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, + [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev8_rx[] = { + [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_CTRL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_OVR] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RESET] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RCAL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_TXLPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_COM_RC_RXHPF] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR17] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR18] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR19] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR20] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR21] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR22] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR23] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR24] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR25] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR26] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR27] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR28] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR29] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR30] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RESERVED_ADDR31] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXIQCAL_RXMUX] = { .ghz5 = 0x0003, .ghz2 = 0x0003, NOUPLOAD, }, + [B2056_RX_RSSI_PU] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_SEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RSSI_GAIN] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_RSSI_NB_IDAC] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2I_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_1] = { .ghz5 = 0x0015, .ghz2 = 0x0015, NOUPLOAD, }, + [B2056_RX_RSSI_WB2Q_IDAC_2] = { .ghz5 = 0x0005, .ghz2 = 0x0005, NOUPLOAD, }, + [B2056_RX_RSSI_POLE] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_RSSI_WB1_IDAC] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_RSSI_MISC] = { .ghz5 = 0x0090, .ghz2 = 0x0090, NOUPLOAD, }, + [B2056_RX_LNAA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAA_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAA_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_A_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_RX_LNA1A_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_LNAG_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_LNAG_TUNE] = { .ghz5 = 0x0088, .ghz2 = 0x0088, NOUPLOAD, }, + [B2056_RX_LNAG_GAIN] = { .ghz5 = 0x0032, .ghz2 = 0x0032, NOUPLOAD, }, + [B2056_RX_LNA_G_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, NOUPLOAD, }, + [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_RX_LNA1G_MISC] = { .ghz5 = 0x0020, .ghz2 = 0x0020, NOUPLOAD, }, + [B2056_RX_MIXA_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXA_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, + [B2056_RX_MIXA_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXA_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_RX_MIXA_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXA_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXA_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXG_CTRLPTAT] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_LOB_BIAS] = { .ghz5 = 0x0011, .ghz2 = 0x0011, NOUPLOAD, }, + [B2056_RX_MIXG_CORE_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_MIXG_CMFB_IDAC] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MAIN] = { .ghz5 = 0x0006, .ghz2 = 0x0006, NOUPLOAD, }, + [B2056_RX_MIXG_BIAS_MISC] = { .ghz5 = 0x0004, .ghz2 = 0x0004, NOUPLOAD, }, + [B2056_RX_MIXG_MAST_BIAS] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_MASTER] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_GAIN] = { .ghz5 = 0x0044, .ghz2 = 0x0044, NOUPLOAD, }, + [B2056_RX_TIA_SPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TIA_SPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_BB_LPF_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_AACI_MASTER] = { .ghz5 = 0x0008, .ghz2 = 0x0008, NOUPLOAD, }, + [B2056_RX_RXLPF_IDAC] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_LOWQ] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_OPAMPBIAS_HIGHQ]= { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_RXLPF_BIAS_DCCANCEL] = { .ghz5 = 0x0002, .ghz2 = 0x0002, NOUPLOAD, }, + [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, + [B2056_RX_RXLPF_INVCM_BODY] = { .ghz5 = 0x0007, .ghz2 = 0x0007, NOUPLOAD, }, + [B2056_RX_RXLPF_CC_OP] = { .ghz5 = 0x0055, .ghz2 = 0x0055, NOUPLOAD, }, + [B2056_RX_RXLPF_GAIN] = { .ghz5 = 0x0023, .ghz2 = 0x0023, NOUPLOAD, }, + [B2056_RX_RXLPF_Q_BW] = { .ghz5 = 0x0041, .ghz2 = 0x0041, NOUPLOAD, }, + [B2056_RX_RXLPF_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_HPC] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXHPF_OFF7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_RCCAL_LPC] = { .ghz5 = 0x000c, .ghz2 = 0x000c, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_0] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXLPF_OFF_4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_UNUSED] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_VGA_MASTER] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_RX_VGA_GAIN] = { .ghz5 = 0x000a, .ghz2 = 0x000a, NOUPLOAD, }, + [B2056_RX_VGA_HP_CORNER_BW] = { .ghz5 = 0x0001, .ghz2 = 0x0001, NOUPLOAD, }, + [B2056_RX_VGABUF_BIAS] = { .ghz5 = 0x0022, .ghz2 = 0x0022, NOUPLOAD, }, + [B2056_RX_VGABUF_GAIN_BW] = { .ghz5 = 0x0030, .ghz2 = 0x0030, NOUPLOAD, }, + [B2056_RX_TXFBMIX_A] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_TXFBMIX_G] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE1] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE3] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, + [B2056_RX_RXSPARE4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE5] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE6] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE7] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE8] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE9] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE10] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE11] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE12] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE13] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE14] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE15] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_RXSPARE16] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_LNAG_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_MIXTIA_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_VGA_BUF_GAIN] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_Q] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_BUF_BW] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_VGA_HPC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_RXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, + [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev11_syn[] = { + [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, + [B2056_SYN_PLL_CP2] = { .ghz5 = 0x003f, .ghz2 = 0x003f, UPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x002b, .ghz2 = 0x002b, UPLOAD, }, + [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, + [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev11_tx[] = { + [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, + [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev11_rx[] = { + [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, + [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, + [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_RX_RXSPARE3] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, +}; + +#define INITTABSPTS(prefix) \ + static const struct b2056_inittabs_pts prefix = { \ + .syn = prefix##_syn, \ + .syn_length = ARRAY_SIZE(prefix##_syn), \ + .tx = prefix##_tx, \ + .tx_length = ARRAY_SIZE(prefix##_tx), \ + .rx = prefix##_rx, \ + .rx_length = ARRAY_SIZE(prefix##_rx), \ + } + +INITTABSPTS(b2056_inittab_phy_rev3); +INITTABSPTS(b2056_inittab_phy_rev4); +INITTABSPTS(b2056_inittab_radio_rev5); +INITTABSPTS(b2056_inittab_radio_rev6); +INITTABSPTS(b2056_inittab_radio_rev7_9); +INITTABSPTS(b2056_inittab_radio_rev8); +INITTABSPTS(b2056_inittab_radio_rev11); + +#define RADIOREGS3(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ + r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ + r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, \ + r30, r31, r32, r33, r34, r35, r36) \ + .radio_syn_pll_vcocal1 = r00, \ + .radio_syn_pll_vcocal2 = r01, \ + .radio_syn_pll_refdiv = r02, \ + .radio_syn_pll_mmd2 = r03, \ + .radio_syn_pll_mmd1 = r04, \ + .radio_syn_pll_loopfilter1 = r05, \ + .radio_syn_pll_loopfilter2 = r06, \ + .radio_syn_pll_loopfilter3 = r07, \ + .radio_syn_pll_loopfilter4 = r08, \ + .radio_syn_pll_loopfilter5 = r09, \ + .radio_syn_reserved_addr27 = r10, \ + .radio_syn_reserved_addr28 = r11, \ + .radio_syn_reserved_addr29 = r12, \ + .radio_syn_logen_vcobuf1 = r13, \ + .radio_syn_logen_mixer2 = r14, \ + .radio_syn_logen_buf3 = r15, \ + .radio_syn_logen_buf4 = r16, \ + .radio_rx0_lnaa_tune = r17, \ + .radio_rx0_lnag_tune = r18, \ + .radio_tx0_intpaa_boost_tune = r19, \ + .radio_tx0_intpag_boost_tune = r20, \ + .radio_tx0_pada_boost_tune = r21, \ + .radio_tx0_padg_boost_tune = r22, \ + .radio_tx0_pgaa_boost_tune = r23, \ + .radio_tx0_pgag_boost_tune = r24, \ + .radio_tx0_mixa_boost_tune = r25, \ + .radio_tx0_mixg_boost_tune = r26, \ + .radio_rx1_lnaa_tune = r27, \ + .radio_rx1_lnag_tune = r28, \ + .radio_tx1_intpaa_boost_tune = r29, \ + .radio_tx1_intpag_boost_tune = r30, \ + .radio_tx1_pada_boost_tune = r31, \ + .radio_tx1_padg_boost_tune = r32, \ + .radio_tx1_pgaa_boost_tune = r33, \ + .radio_tx1_pgag_boost_tune = r34, \ + .radio_tx1_mixa_boost_tune = r35, \ + .radio_tx1_mixg_boost_tune = r36 + +#define PHYREGS(r0, r1, r2, r3, r4, r5) \ + .phy_regs.phy_bw1a = r0, \ + .phy_regs.phy_bw2 = r1, \ + .phy_regs.phy_bw3 = r2, \ + .phy_regs.phy_bw4 = r3, \ + .phy_regs.phy_bw5 = r4, \ + .phy_regs.phy_bw6 = r5 + +/* http://bcm-v4.sipsolutions.net/802.11/Radio/2056/ChannelTable */ +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_phy_rev3[] = { + { .freq = 4920, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), + }, + { .freq = 4930, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), + }, + { .freq = 4940, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), + }, + { .freq = 4950, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), + }, + { .freq = 4960, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), + }, + { .freq = 4970, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), + }, + { .freq = 4980, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), + }, + { .freq = 4990, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), + }, + { .freq = 5000, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), + }, + { .freq = 5010, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), + }, + { .freq = 5020, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), + }, + { .freq = 5030, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), + }, + { .freq = 5040, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), + }, + { .freq = 5050, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), + }, + { .freq = 5060, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), + }, + { .freq = 5070, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), + }, + { .freq = 5080, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), + }, + { .freq = 5090, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), + }, + { .freq = 5100, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00), + PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), + }, + { .freq = 5110, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00), + PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), + }, + { .freq = 5120, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00), + PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), + }, + { .freq = 5130, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00), + PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), + }, + { .freq = 5140, + RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00), + PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), + }, + { .freq = 5160, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00), + PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), + }, + { .freq = 5170, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00), + PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), + }, + { .freq = 5180, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00), + PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), + }, + { .freq = 5190, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00), + PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), + }, + { .freq = 5200, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xef, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0xef, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00), + PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), + }, + { .freq = 5210, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00), + PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), + }, + { .freq = 5220, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00), + PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), + }, + { .freq = 5230, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00), + PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), + }, + { .freq = 5240, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { .freq = 5250, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00), + PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), + }, + { .freq = 5260, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00), + PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), + }, + { .freq = 5270, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, + 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00), + PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), + }, + { .freq = 5280, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, + 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00), + PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), + }, + { .freq = 5290, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, + 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00), + PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), + }, + { .freq = 5300, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfc, 0x00), + PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), + }, + { .freq = 5310, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00), + PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), + }, + { .freq = 5320, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00), + PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), + }, + { .freq = 5330, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00), + PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), + }, + { .freq = 5340, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00), + PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), + }, + { .freq = 5350, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00), + PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), + }, + { .freq = 5360, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00), + PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), + }, + { .freq = 5370, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00), + PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), + }, + { .freq = 5380, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00), + PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), + }, + { .freq = 5390, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x8f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00), + PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), + }, + { .freq = 5400, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00), + PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), + }, + { .freq = 5410, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00), + PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), + }, + { .freq = 5420, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00), + PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), + }, + { .freq = 5430, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00), + PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), + }, + { .freq = 5440, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x7e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x7e, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00), + PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), + }, + { .freq = 5450, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x7d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x7d, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00), + PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), + }, + { .freq = 5460, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xf8, 0x00), + PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), + }, + { .freq = 5470, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xf8, 0x00), + PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), + }, + { .freq = 5480, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x5d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xf8, 0x00), + PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), + }, + { .freq = 5490, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x5c, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xf8, 0x00), + PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), + }, + { .freq = 5500, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x5c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), + }, + { .freq = 5510, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), + }, + { .freq = 5520, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), + }, + { .freq = 5530, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), + }, + { .freq = 5540, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), + }, + { .freq = 5550, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), + }, + { .freq = 5560, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x2b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x2b, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), + }, + { .freq = 5570, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x2a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x2a, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), + }, + { .freq = 5580, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), + }, + { .freq = 5590, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), + }, + { .freq = 5600, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), + }, + { .freq = 5610, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), + }, + { .freq = 5620, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), + }, + { .freq = 5630, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), + }, + { .freq = 5640, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), + }, + { .freq = 5650, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00), + PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), + }, + { .freq = 5660, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf6, 0x00), + PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), + }, + { .freq = 5670, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf6, 0x00), + PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), + }, + { .freq = 5680, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf6, 0x00), + PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), + }, + { .freq = 5690, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf6, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf6, 0x00), + PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), + }, + { .freq = 5700, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf6, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf6, 0x00), + PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), + }, + { .freq = 5710, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), + }, + { .freq = 5720, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5725, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5730, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), + }, + { .freq = 5735, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), + }, + { .freq = 5740, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), + }, + { .freq = 5745, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), + }, + { .freq = 5750, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), + }, + { .freq = 5755, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), + }, + { .freq = 5760, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5765, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5770, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), + }, + { .freq = 5775, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), + }, + { .freq = 5780, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), + }, + { .freq = 5785, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, + 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5790, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, + 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5795, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, + 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), + }, + { .freq = 5800, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), + }, + { .freq = 5805, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), + }, + { .freq = 5810, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5815, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5820, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), + }, + { .freq = 5825, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), + }, + { .freq = 5830, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), + }, + { .freq = 5840, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), + }, + { .freq = 5850, + RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00), + PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), + }, + { .freq = 5860, + RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf2, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf2, 0x00), + PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), + }, + { .freq = 5870, + RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf2, 0x00), + PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), + }, + { .freq = 5880, + RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf2, 0x00), + PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), + }, + { .freq = 5890, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf2, 0x00), + PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), + }, + { .freq = 5900, + RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, + 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x05, 0x00, 0xf2, 0x00), + PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), + }, + { .freq = 5910, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, + 0x00, 0xf2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x05, 0x00, 0xf2, 0x00), + PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), + }, + { .freq = 2412, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { .freq = 2417, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { .freq = 2422, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { .freq = 2427, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xfd, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xfd, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { .freq = 2432, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xfb, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xfb, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { .freq = 2437, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xfa, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { .freq = 2442, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf8, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { .freq = 2447, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf7, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xf7, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { .freq = 2452, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf6, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0xf6, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { .freq = 2457, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf5, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0d, 0x00, 0xf5, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0d), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { .freq = 2462, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0d, 0x00, 0xf4, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0d), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { .freq = 2467, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf3, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0d, 0x00, 0xf3, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0d), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { .freq = 2472, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0d, 0x00, 0xf2, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0d), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { .freq = 2484, + RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf0, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0d, 0x00, 0xf0, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0d), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + }, +}; + +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_phy_rev4[] = { + { .freq = 4920, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), + }, + { .freq = 4930, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), + }, + { .freq = 4940, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), + }, + { .freq = 4950, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), + }, + { .freq = 4960, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), + }, + { .freq = 4970, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), + }, + { .freq = 4980, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), + }, + { .freq = 4990, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), + }, + { .freq = 5000, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), + }, + { .freq = 5010, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), + }, + { .freq = 5020, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), + }, + { .freq = 5030, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), + }, + { .freq = 5040, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), + }, + { .freq = 5050, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), + }, + { .freq = 5060, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), + }, + { .freq = 5070, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), + }, + { .freq = 5080, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), + }, + { .freq = 5090, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00), + PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), + }, + { .freq = 5100, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00), + PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), + }, + { .freq = 5110, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00), + PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), + }, + { .freq = 5120, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00), + PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), + }, + { .freq = 5130, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00), + PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), + }, + { .freq = 5140, + RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00), + PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), + }, + { .freq = 5160, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00), + PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), + }, + { .freq = 5170, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00), + PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), + }, + { .freq = 5180, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00), + PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), + }, + { .freq = 5190, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00), + PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), + }, + { .freq = 5200, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xef, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0xef, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00), + PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), + }, + { .freq = 5210, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00), + PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), + }, + { .freq = 5220, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00), + PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), + }, + { .freq = 5230, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00), + PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), + }, + { .freq = 5240, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { .freq = 5250, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00), + PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), + }, + { .freq = 5260, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00), + PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), + }, + { .freq = 5270, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, + 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00), + PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), + }, + { .freq = 5280, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, + 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00), + PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), + }, + { .freq = 5290, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, + 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00), + PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), + }, + { .freq = 5300, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00), + PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), + }, + { .freq = 5310, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00), + PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), + }, + { .freq = 5320, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00), + PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), + }, + { .freq = 5330, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00), + PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), + }, + { .freq = 5340, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00), + PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), + }, + { .freq = 5350, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00), + PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), + }, + { .freq = 5360, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00), + PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), + }, + { .freq = 5370, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00), + PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), + }, + { .freq = 5380, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00), + PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), + }, + { .freq = 5390, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x8f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00), + PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), + }, + { .freq = 5400, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00), + PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), + }, + { .freq = 5410, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00), + PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), + }, + { .freq = 5420, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00), + PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), + }, + { .freq = 5430, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00), + PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), + }, + { .freq = 5440, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x7e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x7e, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00), + PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), + }, + { .freq = 5450, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x7d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x7d, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00), + PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), + }, + { .freq = 5460, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00), + PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), + }, + { .freq = 5470, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00), + PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), + }, + { .freq = 5480, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x5d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00), + PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), + }, + { .freq = 5490, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x5c, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00), + PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), + }, + { .freq = 5500, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x5c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x5c, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00), + PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), + }, + { .freq = 5510, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00), + PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), + }, + { .freq = 5520, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00), + PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), + }, + { .freq = 5530, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00), + PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), + }, + { .freq = 5540, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00), + PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), + }, + { .freq = 5550, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00), + PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), + }, + { .freq = 5560, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x2b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x2b, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00), + PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), + }, + { .freq = 5570, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x2a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x2a, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00), + PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), + }, + { .freq = 5580, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00), + PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), + }, + { .freq = 5590, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00), + PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), + }, + { .freq = 5600, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x1a, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x1a, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00), + PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), + }, + { .freq = 5610, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00), + PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), + }, + { .freq = 5620, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00), + PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), + }, + { .freq = 5630, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00), + PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), + }, + { .freq = 5640, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00), + PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), + }, + { .freq = 5650, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00), + PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), + }, + { .freq = 5660, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00), + PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), + }, + { .freq = 5670, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00), + PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), + }, + { .freq = 5680, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00), + PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), + }, + { .freq = 5690, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x07, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x07, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00), + PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), + }, + { .freq = 5700, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), + }, + { .freq = 5710, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), + }, + { .freq = 5720, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5725, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5730, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), + }, + { .freq = 5735, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), + }, + { .freq = 5740, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), + }, + { .freq = 5745, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), + }, + { .freq = 5750, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), + }, + { .freq = 5755, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), + }, + { .freq = 5760, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5765, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5770, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), + }, + { .freq = 5775, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), + }, + { .freq = 5780, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), + }, + { .freq = 5785, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, + 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5790, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, + 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5795, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, + 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00), + PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), + }, + { .freq = 5800, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), + }, + { .freq = 5805, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), + }, + { .freq = 5810, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5815, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5820, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), + }, + { .freq = 5825, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), + }, + { .freq = 5830, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), + }, + { .freq = 5840, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), + }, + { .freq = 5850, + RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), + }, + { .freq = 5860, + RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), + }, + { .freq = 5870, + RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), + }, + { .freq = 5880, + RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), + }, + { .freq = 5890, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00), + PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), + }, + { .freq = 5900, + RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf0, 0x00), + PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), + }, + { .freq = 5910, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf0, 0x00), + PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), + }, + { .freq = 2412, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { .freq = 2417, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { .freq = 2422, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { .freq = 2427, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xfd, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xfd, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { .freq = 2432, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xfb, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xfb, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { .freq = 2437, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { .freq = 2442, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf8, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xf8, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { .freq = 2447, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf7, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xf7, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { .freq = 2452, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf6, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xf6, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { .freq = 2457, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf5, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xf5, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { .freq = 2462, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xf4, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { .freq = 2467, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf3, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xf3, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { .freq = 2472, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xf2, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { .freq = 2484, + RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x00, 0xf0, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + }, +}; + +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev5[] = { + { .freq = 4920, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), + }, + { .freq = 4930, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), + }, + { .freq = 4940, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), + }, + { .freq = 4950, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), + }, + { .freq = 4960, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), + }, + { .freq = 4970, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), + }, + { .freq = 4980, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), + }, + { .freq = 4990, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), + }, + { .freq = 5000, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), + }, + { .freq = 5010, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), + }, + { .freq = 5020, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), + }, + { .freq = 5030, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), + }, + { .freq = 5040, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), + }, + { .freq = 5050, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), + }, + { .freq = 5060, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), + }, + { .freq = 5070, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), + }, + { .freq = 5080, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), + }, + { .freq = 5090, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), + }, + { .freq = 5100, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), + }, + { .freq = 5110, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), + }, + { .freq = 5120, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), + }, + { .freq = 5130, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, + 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), + }, + { .freq = 5140, + RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, + 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), + }, + { .freq = 5160, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), + }, + { .freq = 5170, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), + }, + { .freq = 5180, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), + }, + { .freq = 5190, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), + }, + { .freq = 5200, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), + }, + { .freq = 5210, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), + }, + { .freq = 5220, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), + }, + { .freq = 5230, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x6e, 0x00), + PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), + }, + { .freq = 5240, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x6d, 0x00), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { .freq = 5250, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x6d, 0x00), + PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), + }, + { .freq = 5260, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x6d, 0x00), + PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), + }, + { .freq = 5270, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, + 0xff, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), + }, + { .freq = 5280, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, + 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), + }, + { .freq = 5290, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, + 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), + }, + { .freq = 5300, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), + }, + { .freq = 5310, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), + }, + { .freq = 5320, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), + }, + { .freq = 5330, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6b, 0x00), + PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), + }, + { .freq = 5340, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6b, 0x00), + PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), + }, + { .freq = 5350, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x6b, 0x00), + PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), + }, + { .freq = 5360, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x6b, 0x00), + PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), + }, + { .freq = 5370, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x5b, 0x00), + PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), + }, + { .freq = 5380, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x5a, 0x00), + PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), + }, + { .freq = 5390, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, + 0xff, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x5a, 0x00), + PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), + }, + { .freq = 5400, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x5a, 0x00), + PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), + }, + { .freq = 5410, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x5a, 0x00), + PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), + }, + { .freq = 5420, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x5a, 0x00), + PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), + }, + { .freq = 5430, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xc8, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x59, 0x00), + PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), + }, + { .freq = 5440, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x59, 0x00), + PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), + }, + { .freq = 5450, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x59, 0x00), + PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), + }, + { .freq = 5460, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), + }, + { .freq = 5470, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), + }, + { .freq = 5480, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), + }, + { .freq = 5490, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, + 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), + }, + { .freq = 5500, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x78, 0x00), + PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), + }, + { .freq = 5510, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x78, 0x00), + PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), + }, + { .freq = 5520, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x78, 0x00), + PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), + }, + { .freq = 5530, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x78, 0x00), + PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), + }, + { .freq = 5540, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x77, 0x00), + PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), + }, + { .freq = 5550, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x77, 0x00), + PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), + }, + { .freq = 5560, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x77, 0x00), + PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), + }, + { .freq = 5570, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x76, 0x00), + PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), + }, + { .freq = 5580, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x76, 0x00), + PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), + }, + { .freq = 5590, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, + 0x84, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x76, 0x00), + PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), + }, + { .freq = 5600, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x76, 0x00), + PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), + }, + { .freq = 5610, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x76, 0x00), + PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), + }, + { .freq = 5620, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x76, 0x00), + PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), + }, + { .freq = 5630, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x76, 0x00), + PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), + }, + { .freq = 5640, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x75, 0x00), + PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), + }, + { .freq = 5650, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x75, 0x00), + PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), + }, + { .freq = 5660, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x75, 0x00), + PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), + }, + { .freq = 5670, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x74, 0x00), + PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), + }, + { .freq = 5680, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x74, 0x00), + PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), + }, + { .freq = 5690, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, + 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x74, 0x00), + PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), + }, + { .freq = 5700, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x74, 0x00), + PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), + }, + { .freq = 5710, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x74, 0x00), + PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), + }, + { .freq = 5720, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x74, 0x00), + PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5725, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x74, 0x00), + PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5730, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x84, 0x00), + PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), + }, + { .freq = 5735, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x83, 0x00), + PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), + }, + { .freq = 5740, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x83, 0x00), + PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), + }, + { .freq = 5745, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x83, 0x00), + PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), + }, + { .freq = 5750, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x83, 0x00), + PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), + }, + { .freq = 5755, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x83, 0x00), + PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), + }, + { .freq = 5760, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x83, 0x00), + PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5765, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5770, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), + }, + { .freq = 5775, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), + }, + { .freq = 5780, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, + 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), + }, + { .freq = 5785, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, + 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5790, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, + 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5795, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, + 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), + }, + { .freq = 5800, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), + }, + { .freq = 5805, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), + }, + { .freq = 5810, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5815, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5820, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), + }, + { .freq = 5825, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x82, 0x00), + PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), + }, + { .freq = 5830, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x72, 0x00), + PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), + }, + { .freq = 5840, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x72, 0x00), + PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), + }, + { .freq = 5850, + RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x72, 0x00), + PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), + }, + { .freq = 5860, + RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x72, 0x00), + PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), + }, + { .freq = 5870, + RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x71, 0x00), + PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), + }, + { .freq = 5880, + RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x71, 0x00), + PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), + }, + { .freq = 5890, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x71, 0x00), + PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), + }, + { .freq = 5900, + RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x71, 0x00), + PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), + }, + { .freq = 5910, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x71, 0x00), + PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), + }, + { .freq = 2412, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0b, 0x00, 0x1f, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0b), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { .freq = 2417, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0a, 0x00, 0x1f, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0a), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { .freq = 2422, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0a), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { .freq = 2427, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { .freq = 2432, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { .freq = 2437, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { .freq = 2442, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { .freq = 2447, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x09, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x09), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { .freq = 2452, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x09, 0x00, 0x07, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x09), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { .freq = 2457, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x09), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { .freq = 2462, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x09, 0x00, 0x05, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x09), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { .freq = 2467, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { .freq = 2472, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { .freq = 2484, + RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + }, +}; + +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev6[] = { + { .freq = 4920, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), + }, + { .freq = 4930, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), + }, + { .freq = 4940, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), + }, + { .freq = 4950, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), + }, + { .freq = 4960, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), + }, + { .freq = 4970, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), + }, + { .freq = 4980, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), + }, + { .freq = 4990, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), + }, + { .freq = 5000, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), + }, + { .freq = 5010, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), + }, + { .freq = 5020, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), + }, + { .freq = 5030, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), + }, + { .freq = 5040, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), + }, + { .freq = 5050, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), + }, + { .freq = 5060, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), + }, + { .freq = 5070, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), + }, + { .freq = 5080, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), + }, + { .freq = 5090, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), + }, + { .freq = 5100, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), + }, + { .freq = 5110, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), + }, + { .freq = 5120, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), + }, + { .freq = 5130, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), + }, + { .freq = 5140, + RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), + }, + { .freq = 5160, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), + }, + { .freq = 5170, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), + }, + { .freq = 5180, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), + }, + { .freq = 5190, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), + }, + { .freq = 5200, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), + }, + { .freq = 5210, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), + }, + { .freq = 5220, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), + }, + { .freq = 5230, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), + }, + { .freq = 5240, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { .freq = 5250, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), + }, + { .freq = 5260, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, + 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), + }, + { .freq = 5270, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, + 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), + }, + { .freq = 5280, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), + }, + { .freq = 5290, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), + }, + { .freq = 5300, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), + }, + { .freq = 5310, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), + }, + { .freq = 5320, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), + }, + { .freq = 5330, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), + }, + { .freq = 5340, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), + }, + { .freq = 5350, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), + }, + { .freq = 5360, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), + }, + { .freq = 5370, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), + }, + { .freq = 5380, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), + }, + { .freq = 5390, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), + }, + { .freq = 5400, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), + }, + { .freq = 5410, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), + }, + { .freq = 5420, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), + }, + { .freq = 5430, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, + 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), + }, + { .freq = 5440, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), + }, + { .freq = 5450, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), + }, + { .freq = 5460, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), + }, + { .freq = 5470, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), + }, + { .freq = 5480, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), + }, + { .freq = 5490, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), + }, + { .freq = 5500, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), + }, + { .freq = 5510, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), + }, + { .freq = 5520, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), + }, + { .freq = 5530, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), + }, + { .freq = 5540, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), + }, + { .freq = 5550, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), + }, + { .freq = 5560, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), + }, + { .freq = 5570, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), + }, + { .freq = 5580, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), + }, + { .freq = 5590, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), + }, + { .freq = 5600, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), + }, + { .freq = 5610, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), + }, + { .freq = 5620, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), + }, + { .freq = 5630, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), + }, + { .freq = 5640, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), + }, + { .freq = 5650, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), + }, + { .freq = 5660, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), + }, + { .freq = 5670, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), + }, + { .freq = 5680, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), + }, + { .freq = 5690, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), + }, + { .freq = 5700, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), + }, + { .freq = 5710, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), + }, + { .freq = 5720, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5725, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5730, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), + }, + { .freq = 5735, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), + }, + { .freq = 5740, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), + }, + { .freq = 5745, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), + }, + { .freq = 5750, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6d, 0x00), + PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), + }, + { .freq = 5755, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), + }, + { .freq = 5760, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5765, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5770, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), + }, + { .freq = 5775, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), + }, + { .freq = 5780, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), + }, + { .freq = 5785, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5790, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5795, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), + }, + { .freq = 5800, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), + }, + { .freq = 5805, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), + }, + { .freq = 5810, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5815, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5820, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), + }, + { .freq = 5825, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00), + PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), + }, + { .freq = 5830, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00), + PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), + }, + { .freq = 5840, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), + }, + { .freq = 5850, + RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), + }, + { .freq = 5860, + RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), + }, + { .freq = 5870, + RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), + }, + { .freq = 5880, + RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), + }, + { .freq = 5890, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), + }, + { .freq = 5900, + RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), + }, + { .freq = 5910, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), + }, + { .freq = 2412, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { .freq = 2417, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { .freq = 2422, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { .freq = 2427, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x57, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { .freq = 2432, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x56, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { .freq = 2437, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x46, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { .freq = 2442, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x45, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { .freq = 2447, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { .freq = 2452, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x23, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { .freq = 2457, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x12, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { .freq = 2462, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { .freq = 2467, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { .freq = 2472, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { .freq = 2484, + RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + }, +}; + +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev7_9[] = { + { .freq = 4920, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), + }, + { .freq = 4930, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), + }, + { .freq = 4940, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), + }, + { .freq = 4950, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), + }, + { .freq = 4960, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), + }, + { .freq = 4970, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), + }, + { .freq = 4980, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), + }, + { .freq = 4990, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), + }, + { .freq = 5000, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), + }, + { .freq = 5010, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), + }, + { .freq = 5020, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), + }, + { .freq = 5030, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), + }, + { .freq = 5040, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), + }, + { .freq = 5050, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), + }, + { .freq = 5060, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), + }, + { .freq = 5070, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), + }, + { .freq = 5080, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), + }, + { .freq = 5090, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), + }, + { .freq = 5100, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), + }, + { .freq = 5110, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), + }, + { .freq = 5120, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), + }, + { .freq = 5130, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, + 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), + }, + { .freq = 5140, + RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, + 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), + }, + { .freq = 5160, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), + }, + { .freq = 5170, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), + }, + { .freq = 5180, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), + }, + { .freq = 5190, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), + }, + { .freq = 5200, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), + }, + { .freq = 5210, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), + }, + { .freq = 5220, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xfe, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x6e, 0x00), + PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), + }, + { .freq = 5230, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x6e, 0x00), + PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), + }, + { .freq = 5240, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x6d, 0x00), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { .freq = 5250, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xed, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x6d, 0x00), + PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), + }, + { .freq = 5260, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, + 0xed, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x6d, 0x00), + PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), + }, + { .freq = 5270, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, + 0xed, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), + }, + { .freq = 5280, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), + }, + { .freq = 5290, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), + }, + { .freq = 5300, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), + }, + { .freq = 5310, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), + }, + { .freq = 5320, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdb, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6c, 0x00), + PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), + }, + { .freq = 5330, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xcb, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6b, 0x00), + PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), + }, + { .freq = 5340, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xca, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x6b, 0x00), + PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), + }, + { .freq = 5350, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xca, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x6b, 0x00), + PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), + }, + { .freq = 5360, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x6b, 0x00), + PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), + }, + { .freq = 5370, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x7b, 0x00), + PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), + }, + { .freq = 5380, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x7a, 0x00), + PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), + }, + { .freq = 5390, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x7a, 0x00), + PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), + }, + { .freq = 5400, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x7a, 0x00), + PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), + }, + { .freq = 5410, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb7, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x7a, 0x00), + PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), + }, + { .freq = 5420, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xa7, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x7a, 0x00), + PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), + }, + { .freq = 5430, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, + 0xa6, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x79, 0x00), + PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), + }, + { .freq = 5440, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0xa6, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x79, 0x00), + PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), + }, + { .freq = 5450, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x79, 0x00), + PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), + }, + { .freq = 5460, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x79, 0x00), + PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), + }, + { .freq = 5470, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x94, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x79, 0x00), + PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), + }, + { .freq = 5480, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x78, 0x00), + PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), + }, + { .freq = 5490, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x83, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x78, 0x00), + PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), + }, + { .freq = 5500, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x78, 0x00), + PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), + }, + { .freq = 5510, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x78, 0x00), + PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), + }, + { .freq = 5520, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x72, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x78, 0x00), + PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), + }, + { .freq = 5530, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x72, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x78, 0x00), + PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), + }, + { .freq = 5540, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x71, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x77, 0x00), + PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), + }, + { .freq = 5550, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x77, 0x00), + PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), + }, + { .freq = 5560, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x77, 0x00), + PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), + }, + { .freq = 5570, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x76, 0x00), + PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), + }, + { .freq = 5580, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x60, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x86, 0x00), + PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), + }, + { .freq = 5590, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x86, 0x00), + PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), + }, + { .freq = 5600, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x86, 0x00), + PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), + }, + { .freq = 5610, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x86, 0x00), + PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), + }, + { .freq = 5620, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x86, 0x00), + PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), + }, + { .freq = 5630, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x86, 0x00), + PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), + }, + { .freq = 5640, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x85, 0x00), + PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), + }, + { .freq = 5650, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x85, 0x00), + PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), + }, + { .freq = 5660, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x85, 0x00), + PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), + }, + { .freq = 5670, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x84, 0x00), + PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), + }, + { .freq = 5680, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x84, 0x00), + PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), + }, + { .freq = 5690, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00), + PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), + }, + { .freq = 5700, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00), + PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), + }, + { .freq = 5710, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00), + PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), + }, + { .freq = 5720, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00), + PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5725, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00), + PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5730, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00), + PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), + }, + { .freq = 5735, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00), + PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), + }, + { .freq = 5740, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00), + PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), + }, + { .freq = 5745, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00), + PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), + }, + { .freq = 5750, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00), + PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), + }, + { .freq = 5755, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x10, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00), + PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), + }, + { .freq = 5760, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00), + PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5765, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5770, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), + }, + { .freq = 5775, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), + }, + { .freq = 5780, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), + }, + { .freq = 5785, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5790, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5795, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), + }, + { .freq = 5800, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), + }, + { .freq = 5805, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), + }, + { .freq = 5810, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5815, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5820, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), + }, + { .freq = 5825, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), + }, + { .freq = 5830, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), + }, + { .freq = 5840, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), + }, + { .freq = 5850, + RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), + }, + { .freq = 5860, + RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00), + PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), + }, + { .freq = 5870, + RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00), + PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), + }, + { .freq = 5880, + RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00), + PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), + }, + { .freq = 5890, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00), + PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), + }, + { .freq = 5900, + RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00), + PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), + }, + { .freq = 5910, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00), + PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), + }, + { .freq = 2412, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0b, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0b), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { .freq = 2417, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0a), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { .freq = 2422, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0a), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { .freq = 2427, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { .freq = 2432, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { .freq = 2437, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { .freq = 2442, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { .freq = 2447, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x09), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { .freq = 2452, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x09), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { .freq = 2457, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x09), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { .freq = 2462, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x09), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { .freq = 2467, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x00, 0x22, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { .freq = 2472, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { .freq = 2484, + RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + }, +}; + +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev8[] = { + { .freq = 4920, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), + }, + { .freq = 4930, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), + }, + { .freq = 4940, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), + }, + { .freq = 4950, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), + }, + { .freq = 4960, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), + }, + { .freq = 4970, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), + }, + { .freq = 4980, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), + }, + { .freq = 4990, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), + }, + { .freq = 5000, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), + }, + { .freq = 5010, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), + }, + { .freq = 5020, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), + }, + { .freq = 5030, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), + }, + { .freq = 5040, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), + }, + { .freq = 5050, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), + }, + { .freq = 5060, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), + }, + { .freq = 5070, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), + }, + { .freq = 5080, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), + }, + { .freq = 5090, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), + }, + { .freq = 5100, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), + }, + { .freq = 5110, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), + }, + { .freq = 5120, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), + }, + { .freq = 5130, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), + }, + { .freq = 5140, + RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), + }, + { .freq = 5160, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), + }, + { .freq = 5170, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), + }, + { .freq = 5180, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), + }, + { .freq = 5190, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), + }, + { .freq = 5200, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), + }, + { .freq = 5210, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), + }, + { .freq = 5220, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), + }, + { .freq = 5230, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), + }, + { .freq = 5240, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { .freq = 5250, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), + }, + { .freq = 5260, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, + 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), + }, + { .freq = 5270, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, + 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), + }, + { .freq = 5280, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), + }, + { .freq = 5290, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), + }, + { .freq = 5300, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), + }, + { .freq = 5310, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), + }, + { .freq = 5320, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), + }, + { .freq = 5330, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), + }, + { .freq = 5340, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), + }, + { .freq = 5350, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), + }, + { .freq = 5360, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), + }, + { .freq = 5370, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), + }, + { .freq = 5380, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), + }, + { .freq = 5390, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), + }, + { .freq = 5400, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), + }, + { .freq = 5410, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), + }, + { .freq = 5420, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), + }, + { .freq = 5430, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, + 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), + }, + { .freq = 5440, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), + }, + { .freq = 5450, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), + }, + { .freq = 5460, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), + }, + { .freq = 5470, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), + }, + { .freq = 5480, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), + }, + { .freq = 5490, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), + }, + { .freq = 5500, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), + }, + { .freq = 5510, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), + }, + { .freq = 5520, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), + }, + { .freq = 5530, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), + }, + { .freq = 5540, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), + }, + { .freq = 5550, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), + }, + { .freq = 5560, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), + }, + { .freq = 5570, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), + }, + { .freq = 5580, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), + }, + { .freq = 5590, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), + }, + { .freq = 5600, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), + }, + { .freq = 5610, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), + }, + { .freq = 5620, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), + }, + { .freq = 5630, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), + }, + { .freq = 5640, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), + }, + { .freq = 5650, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), + }, + { .freq = 5660, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), + }, + { .freq = 5670, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), + }, + { .freq = 5680, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), + }, + { .freq = 5690, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), + }, + { .freq = 5700, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), + }, + { .freq = 5710, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), + }, + { .freq = 5720, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5725, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), + }, + { .freq = 5730, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), + }, + { .freq = 5735, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), + }, + { .freq = 5740, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), + }, + { .freq = 5745, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), + }, + { .freq = 5750, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6d, 0x00), + PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), + }, + { .freq = 5755, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), + }, + { .freq = 5760, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5765, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), + }, + { .freq = 5770, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), + }, + { .freq = 5775, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), + }, + { .freq = 5780, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), + }, + { .freq = 5785, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5790, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), + }, + { .freq = 5795, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), + }, + { .freq = 5800, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), + }, + { .freq = 5805, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), + }, + { .freq = 5810, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5815, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), + }, + { .freq = 5820, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), + }, + { .freq = 5825, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, + 0x10, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00), + PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), + }, + { .freq = 5830, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00), + PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), + }, + { .freq = 5840, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), + }, + { .freq = 5850, + RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), + }, + { .freq = 5860, + RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), + }, + { .freq = 5870, + RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), + }, + { .freq = 5880, + RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), + }, + { .freq = 5890, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), + }, + { .freq = 5900, + RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), + }, + { .freq = 5910, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), + }, + { .freq = 2412, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { .freq = 2417, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { .freq = 2422, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { .freq = 2427, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { .freq = 2432, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { .freq = 2437, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { .freq = 2442, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { .freq = 2447, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { .freq = 2452, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { .freq = 2457, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { .freq = 2462, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { .freq = 2467, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { .freq = 2472, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { .freq = 2484, + RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, + 0x16, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + }, +}; + +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev11[] = { + { + .freq = 4920, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), + }, + { + .freq = 4930, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), + }, + { + .freq = 4940, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), + }, + { + .freq = 4950, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), + }, + { + .freq = 4960, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), + }, + { + .freq = 4970, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), + }, + { + .freq = 4980, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), + }, + { + .freq = 4990, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), + }, + { + .freq = 5000, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), + }, + { + .freq = 5010, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), + }, + { + .freq = 5020, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), + }, + { + .freq = 5030, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), + }, + { + .freq = 5040, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), + }, + { + .freq = 5050, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), + }, + { + .freq = 5060, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), + }, + { + .freq = 5070, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), + }, + { + .freq = 5080, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), + }, + { + .freq = 5090, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), + }, + { + .freq = 5100, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), + }, + { + .freq = 5110, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), + }, + { + .freq = 5120, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), + }, + { + .freq = 5130, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), + }, + { + .freq = 5140, + RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), + }, + { + .freq = 5160, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), + }, + { + .freq = 5170, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), + }, + { + .freq = 5180, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), + }, + { + .freq = 5190, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), + }, + { + .freq = 5200, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), + }, + { + .freq = 5210, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), + }, + { + .freq = 5220, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), + }, + { + .freq = 5230, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), + }, + { + .freq = 5240, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { + .freq = 5250, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), + }, + { + .freq = 5260, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, + 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), + }, + { + .freq = 5270, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, + 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), + }, + { + .freq = 5280, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), + }, + { + .freq = 5290, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), + }, + { + .freq = 5300, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), + }, + { + .freq = 5310, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), + }, + { + .freq = 5320, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), + }, + { + .freq = 5330, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), + }, + { + .freq = 5340, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), + }, + { + .freq = 5350, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), + }, + { + .freq = 5360, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), + }, + { + .freq = 5370, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), + }, + { + .freq = 5380, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), + }, + { + .freq = 5390, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), + }, + { + .freq = 5400, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), + }, + { + .freq = 5410, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), + }, + { + .freq = 5420, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), + }, + { + .freq = 5430, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, + 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), + }, + { + .freq = 5440, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), + }, + { + .freq = 5450, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), + }, + { + .freq = 5460, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), + }, + { + .freq = 5470, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), + }, + { + .freq = 5480, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), + }, + { + .freq = 5490, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), + }, + { + .freq = 5500, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), + }, + { + .freq = 5510, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), + }, + { + .freq = 5520, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), + }, + { + .freq = 5530, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), + }, + { + .freq = 5540, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), + }, + { + .freq = 5550, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), + }, + { + .freq = 5560, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), + }, + { + .freq = 5570, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), + }, + { + .freq = 5580, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), + }, + { + .freq = 5590, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), + }, + { + .freq = 5600, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), + }, + { + .freq = 5610, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), + }, + { + .freq = 5620, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), + }, + { + .freq = 5630, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), + }, + { + .freq = 5640, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), + }, + { + .freq = 5650, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), + }, + { + .freq = 5660, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), + }, + { + .freq = 5670, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), + }, + { + .freq = 5680, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), + }, + { + .freq = 5690, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), + }, + { + .freq = 5700, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), + }, + { + .freq = 5710, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), + }, + { + .freq = 5720, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), + }, + { + .freq = 5725, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), + }, + { + .freq = 5730, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), + }, + { + .freq = 5735, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), + }, + { + .freq = 5740, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), + }, + { + .freq = 5745, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), + }, + { + .freq = 5750, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6d, 0x00), + PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), + }, + { + .freq = 5755, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), + }, + { + .freq = 5760, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), + }, + { + .freq = 5765, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), + }, + { + .freq = 5770, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), + }, + { + .freq = 5775, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), + }, + { + .freq = 5780, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), + }, + { + .freq = 5785, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), + }, + { + .freq = 5790, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), + }, + { + .freq = 5795, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), + }, + { + .freq = 5800, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), + }, + { + .freq = 5805, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), + }, + { + .freq = 5810, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), + }, + { + .freq = 5815, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), + }, + { + .freq = 5820, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), + }, + { + .freq = 5825, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00), + PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), + }, + { + .freq = 5830, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00), + PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), + }, + { + .freq = 5840, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), + }, + { + .freq = 5850, + RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), + }, + { + .freq = 5860, + RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), + }, + { + .freq = 5870, + RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), + }, + { + .freq = 5880, + RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), + }, + { + .freq = 5890, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), + }, + { + .freq = 5900, + RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), + }, + { + .freq = 5910, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), + }, + { + .freq = 2412, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { + .freq = 2417, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { + .freq = 2422, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { + .freq = 2427, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { + .freq = 2432, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { + .freq = 2437, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { + .freq = 2442, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { + .freq = 2447, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { + .freq = 2452, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { + .freq = 2457, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { + .freq = 2462, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { + .freq = 2467, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { + .freq = 2472, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { + .freq = 2484, + RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + }, +}; + +static const struct b2056_inittabs_pts +*b43_nphy_get_inittabs_rev3(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + switch (dev->phy.rev) { + case 3: + return &b2056_inittab_phy_rev3; + case 4: + return &b2056_inittab_phy_rev4; + default: + switch (phy->radio_rev) { + case 5: + return &b2056_inittab_radio_rev5; + case 6: + return &b2056_inittab_radio_rev6; + case 7: + case 9: + return &b2056_inittab_radio_rev7_9; + case 8: + return &b2056_inittab_radio_rev8; + case 11: + return &b2056_inittab_radio_rev11; + } + } + + return NULL; +} + +static void b2056_upload_inittab(struct b43_wldev *dev, bool ghz5, + bool ignore_uploadflag, u16 routing, + const struct b2056_inittab_entry *e, + unsigned int length) +{ + unsigned int i; + u16 value; + + for (i = 0; i < length; i++, e++) { + if (!(e->flags & B2056_INITTAB_ENTRY_OK)) + continue; + if ((e->flags & B2056_INITTAB_UPLOAD) || ignore_uploadflag) { + if (ghz5) + value = e->ghz5; + else + value = e->ghz2; + b43_radio_write(dev, routing | i, value); + } + } +} + +void b2056_upload_inittabs(struct b43_wldev *dev, + bool ghz5, bool ignore_uploadflag) +{ + const struct b2056_inittabs_pts *pts; + + pts = b43_nphy_get_inittabs_rev3(dev); + if (!pts) { + B43_WARN_ON(1); + return; + } + + b2056_upload_inittab(dev, ghz5, ignore_uploadflag, + B2056_SYN, pts->syn, pts->syn_length); + b2056_upload_inittab(dev, ghz5, ignore_uploadflag, + B2056_TX0, pts->tx, pts->tx_length); + b2056_upload_inittab(dev, ghz5, ignore_uploadflag, + B2056_TX1, pts->tx, pts->tx_length); + b2056_upload_inittab(dev, ghz5, ignore_uploadflag, + B2056_RX0, pts->rx, pts->rx_length); + b2056_upload_inittab(dev, ghz5, ignore_uploadflag, + B2056_RX1, pts->rx, pts->rx_length); +} + +void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5) +{ + const struct b2056_inittabs_pts *pts; + const struct b2056_inittab_entry *e; + + pts = b43_nphy_get_inittabs_rev3(dev); + if (!pts) { + B43_WARN_ON(1); + return; + } + + e = &pts->syn[B2056_SYN_PLL_CP2]; + + b43_radio_write(dev, B2056_SYN_PLL_CP2, ghz5 ? e->ghz5 : e->ghz2); +} + +const struct b43_nphy_channeltab_entry_rev3 * +b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq) +{ + struct b43_phy *phy = &dev->phy; + const struct b43_nphy_channeltab_entry_rev3 *e; + unsigned int length, i; + + switch (phy->rev) { + case 3: + e = b43_nphy_channeltab_phy_rev3; + length = ARRAY_SIZE(b43_nphy_channeltab_phy_rev3); + break; + case 4: + e = b43_nphy_channeltab_phy_rev4; + length = ARRAY_SIZE(b43_nphy_channeltab_phy_rev4); + break; + default: + switch (phy->radio_rev) { + case 5: + e = b43_nphy_channeltab_radio_rev5; + length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev5); + break; + case 6: + e = b43_nphy_channeltab_radio_rev6; + length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev6); + break; + case 7: + case 9: + e = b43_nphy_channeltab_radio_rev7_9; + length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev7_9); + break; + case 8: + e = b43_nphy_channeltab_radio_rev8; + length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev8); + break; + case 11: + e = b43_nphy_channeltab_radio_rev11; + length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev11); + break; + default: + B43_WARN_ON(1); + return NULL; + } + } + + for (i = 0; i < length; i++, e++) { + if (e->freq == freq) + return e; + } + + return NULL; +} diff --git a/drivers/net/wireless/broadcom/b43/radio_2056.h b/drivers/net/wireless/broadcom/b43/radio_2056.h new file mode 100644 index 000000000000..5b86673459fa --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/radio_2056.h @@ -0,0 +1,1100 @@ +#ifndef B43_RADIO_2056_H_ +#define B43_RADIO_2056_H_ + +#include + +#include "tables_nphy.h" + +#define B2056_SYN (0x0 << 12) +#define B2056_TX0 (0x2 << 12) +#define B2056_TX1 (0x3 << 12) +#define B2056_RX0 (0x6 << 12) +#define B2056_RX1 (0x7 << 12) +#define B2056_ALLTX (0xE << 12) +#define B2056_ALLRX (0xF << 12) + +#define B2056_SYN_RESERVED_ADDR0 0x00 +#define B2056_SYN_IDCODE 0x01 +#define B2056_SYN_RESERVED_ADDR2 0x02 +#define B2056_SYN_RESERVED_ADDR3 0x03 +#define B2056_SYN_RESERVED_ADDR4 0x04 +#define B2056_SYN_RESERVED_ADDR5 0x05 +#define B2056_SYN_RESERVED_ADDR6 0x06 +#define B2056_SYN_RESERVED_ADDR7 0x07 +#define B2056_SYN_COM_CTRL 0x08 +#define B2056_SYN_COM_PU 0x09 +#define B2056_SYN_COM_OVR 0x0A +#define B2056_SYN_COM_RESET 0x0B +#define B2056_SYN_COM_RCAL 0x0C +#define B2056_SYN_COM_RC_RXLPF 0x0D +#define B2056_SYN_COM_RC_TXLPF 0x0E +#define B2056_SYN_COM_RC_RXHPF 0x0F +#define B2056_SYN_RESERVED_ADDR16 0x10 +#define B2056_SYN_RESERVED_ADDR17 0x11 +#define B2056_SYN_RESERVED_ADDR18 0x12 +#define B2056_SYN_RESERVED_ADDR19 0x13 +#define B2056_SYN_RESERVED_ADDR20 0x14 +#define B2056_SYN_RESERVED_ADDR21 0x15 +#define B2056_SYN_RESERVED_ADDR22 0x16 +#define B2056_SYN_RESERVED_ADDR23 0x17 +#define B2056_SYN_RESERVED_ADDR24 0x18 +#define B2056_SYN_RESERVED_ADDR25 0x19 +#define B2056_SYN_RESERVED_ADDR26 0x1A +#define B2056_SYN_RESERVED_ADDR27 0x1B +#define B2056_SYN_RESERVED_ADDR28 0x1C +#define B2056_SYN_RESERVED_ADDR29 0x1D +#define B2056_SYN_RESERVED_ADDR30 0x1E +#define B2056_SYN_RESERVED_ADDR31 0x1F +#define B2056_SYN_GPIO_MASTER1 0x20 +#define B2056_SYN_GPIO_MASTER2 0x21 +#define B2056_SYN_TOPBIAS_MASTER 0x22 +#define B2056_SYN_TOPBIAS_RCAL 0x23 +#define B2056_SYN_AFEREG 0x24 +#define B2056_SYN_TEMPPROCSENSE 0x25 +#define B2056_SYN_TEMPPROCSENSEIDAC 0x26 +#define B2056_SYN_TEMPPROCSENSERCAL 0x27 +#define B2056_SYN_LPO 0x28 +#define B2056_SYN_VDDCAL_MASTER 0x29 +#define B2056_SYN_VDDCAL_IDAC 0x2A +#define B2056_SYN_VDDCAL_STATUS 0x2B +#define B2056_SYN_RCAL_MASTER 0x2C +#define B2056_SYN_RCAL_CODE_OUT 0x2D +#define B2056_SYN_RCCAL_CTRL0 0x2E +#define B2056_SYN_RCCAL_CTRL1 0x2F +#define B2056_SYN_RCCAL_CTRL2 0x30 +#define B2056_SYN_RCCAL_CTRL3 0x31 +#define B2056_SYN_RCCAL_CTRL4 0x32 +#define B2056_SYN_RCCAL_CTRL5 0x33 +#define B2056_SYN_RCCAL_CTRL6 0x34 +#define B2056_SYN_RCCAL_CTRL7 0x35 +#define B2056_SYN_RCCAL_CTRL8 0x36 +#define B2056_SYN_RCCAL_CTRL9 0x37 +#define B2056_SYN_RCCAL_CTRL10 0x38 +#define B2056_SYN_RCCAL_CTRL11 0x39 +#define B2056_SYN_ZCAL_SPARE1 0x3A +#define B2056_SYN_ZCAL_SPARE2 0x3B +#define B2056_SYN_PLL_MAST1 0x3C +#define B2056_SYN_PLL_MAST2 0x3D +#define B2056_SYN_PLL_MAST3 0x3E +#define B2056_SYN_PLL_BIAS_RESET 0x3F +#define B2056_SYN_PLL_XTAL0 0x40 +#define B2056_SYN_PLL_XTAL1 0x41 +#define B2056_SYN_PLL_XTAL3 0x42 +#define B2056_SYN_PLL_XTAL4 0x43 +#define B2056_SYN_PLL_XTAL5 0x44 +#define B2056_SYN_PLL_XTAL6 0x45 +#define B2056_SYN_PLL_REFDIV 0x46 +#define B2056_SYN_PLL_PFD 0x47 +#define B2056_SYN_PLL_CP1 0x48 +#define B2056_SYN_PLL_CP2 0x49 +#define B2056_SYN_PLL_CP3 0x4A +#define B2056_SYN_PLL_LOOPFILTER1 0x4B +#define B2056_SYN_PLL_LOOPFILTER2 0x4C +#define B2056_SYN_PLL_LOOPFILTER3 0x4D +#define B2056_SYN_PLL_LOOPFILTER4 0x4E +#define B2056_SYN_PLL_LOOPFILTER5 0x4F +#define B2056_SYN_PLL_MMD1 0x50 +#define B2056_SYN_PLL_MMD2 0x51 +#define B2056_SYN_PLL_VCO1 0x52 +#define B2056_SYN_PLL_VCO2 0x53 +#define B2056_SYN_PLL_MONITOR1 0x54 +#define B2056_SYN_PLL_MONITOR2 0x55 +#define B2056_SYN_PLL_VCOCAL1 0x56 +#define B2056_SYN_PLL_VCOCAL2 0x57 +#define B2056_SYN_PLL_VCOCAL4 0x58 +#define B2056_SYN_PLL_VCOCAL5 0x59 +#define B2056_SYN_PLL_VCOCAL6 0x5A +#define B2056_SYN_PLL_VCOCAL7 0x5B +#define B2056_SYN_PLL_VCOCAL8 0x5C +#define B2056_SYN_PLL_VCOCAL9 0x5D +#define B2056_SYN_PLL_VCOCAL10 0x5E +#define B2056_SYN_PLL_VCOCAL11 0x5F +#define B2056_SYN_PLL_VCOCAL12 0x60 +#define B2056_SYN_PLL_VCOCAL13 0x61 +#define B2056_SYN_PLL_VREG 0x62 +#define B2056_SYN_PLL_STATUS1 0x63 +#define B2056_SYN_PLL_STATUS2 0x64 +#define B2056_SYN_PLL_STATUS3 0x65 +#define B2056_SYN_LOGEN_PU0 0x66 +#define B2056_SYN_LOGEN_PU1 0x67 +#define B2056_SYN_LOGEN_PU2 0x68 +#define B2056_SYN_LOGEN_PU3 0x69 +#define B2056_SYN_LOGEN_PU5 0x6A +#define B2056_SYN_LOGEN_PU6 0x6B +#define B2056_SYN_LOGEN_PU7 0x6C +#define B2056_SYN_LOGEN_PU8 0x6D +#define B2056_SYN_LOGEN_BIAS_RESET 0x6E +#define B2056_SYN_LOGEN_RCCR1 0x6F +#define B2056_SYN_LOGEN_VCOBUF1 0x70 +#define B2056_SYN_LOGEN_MIXER1 0x71 +#define B2056_SYN_LOGEN_MIXER2 0x72 +#define B2056_SYN_LOGEN_BUF1 0x73 +#define B2056_SYN_LOGENBUF2 0x74 +#define B2056_SYN_LOGEN_BUF3 0x75 +#define B2056_SYN_LOGEN_BUF4 0x76 +#define B2056_SYN_LOGEN_DIV1 0x77 +#define B2056_SYN_LOGEN_DIV2 0x78 +#define B2056_SYN_LOGEN_DIV3 0x79 +#define B2056_SYN_LOGEN_ACL1 0x7A +#define B2056_SYN_LOGEN_ACL2 0x7B +#define B2056_SYN_LOGEN_ACL3 0x7C +#define B2056_SYN_LOGEN_ACL4 0x7D +#define B2056_SYN_LOGEN_ACL5 0x7E +#define B2056_SYN_LOGEN_ACL6 0x7F +#define B2056_SYN_LOGEN_ACLOUT 0x80 +#define B2056_SYN_LOGEN_ACLCAL1 0x81 +#define B2056_SYN_LOGEN_ACLCAL2 0x82 +#define B2056_SYN_LOGEN_ACLCAL3 0x83 +#define B2056_SYN_CALEN 0x84 +#define B2056_SYN_LOGEN_PEAKDET1 0x85 +#define B2056_SYN_LOGEN_CORE_ACL_OVR 0x86 +#define B2056_SYN_LOGEN_RX_DIFF_ACL_OVR 0x87 +#define B2056_SYN_LOGEN_TX_DIFF_ACL_OVR 0x88 +#define B2056_SYN_LOGEN_RX_CMOS_ACL_OVR 0x89 +#define B2056_SYN_LOGEN_TX_CMOS_ACL_OVR 0x8A +#define B2056_SYN_LOGEN_VCOBUF2 0x8B +#define B2056_SYN_LOGEN_MIXER3 0x8C +#define B2056_SYN_LOGEN_BUF5 0x8D +#define B2056_SYN_LOGEN_BUF6 0x8E +#define B2056_SYN_LOGEN_CBUFRX1 0x8F +#define B2056_SYN_LOGEN_CBUFRX2 0x90 +#define B2056_SYN_LOGEN_CBUFRX3 0x91 +#define B2056_SYN_LOGEN_CBUFRX4 0x92 +#define B2056_SYN_LOGEN_CBUFTX1 0x93 +#define B2056_SYN_LOGEN_CBUFTX2 0x94 +#define B2056_SYN_LOGEN_CBUFTX3 0x95 +#define B2056_SYN_LOGEN_CBUFTX4 0x96 +#define B2056_SYN_LOGEN_CMOSRX1 0x97 +#define B2056_SYN_LOGEN_CMOSRX2 0x98 +#define B2056_SYN_LOGEN_CMOSRX3 0x99 +#define B2056_SYN_LOGEN_CMOSRX4 0x9A +#define B2056_SYN_LOGEN_CMOSTX1 0x9B +#define B2056_SYN_LOGEN_CMOSTX2 0x9C +#define B2056_SYN_LOGEN_CMOSTX3 0x9D +#define B2056_SYN_LOGEN_CMOSTX4 0x9E +#define B2056_SYN_LOGEN_VCOBUF2_OVRVAL 0x9F +#define B2056_SYN_LOGEN_MIXER3_OVRVAL 0xA0 +#define B2056_SYN_LOGEN_BUF5_OVRVAL 0xA1 +#define B2056_SYN_LOGEN_BUF6_OVRVAL 0xA2 +#define B2056_SYN_LOGEN_CBUFRX1_OVRVAL 0xA3 +#define B2056_SYN_LOGEN_CBUFRX2_OVRVAL 0xA4 +#define B2056_SYN_LOGEN_CBUFRX3_OVRVAL 0xA5 +#define B2056_SYN_LOGEN_CBUFRX4_OVRVAL 0xA6 +#define B2056_SYN_LOGEN_CBUFTX1_OVRVAL 0xA7 +#define B2056_SYN_LOGEN_CBUFTX2_OVRVAL 0xA8 +#define B2056_SYN_LOGEN_CBUFTX3_OVRVAL 0xA9 +#define B2056_SYN_LOGEN_CBUFTX4_OVRVAL 0xAA +#define B2056_SYN_LOGEN_CMOSRX1_OVRVAL 0xAB +#define B2056_SYN_LOGEN_CMOSRX2_OVRVAL 0xAC +#define B2056_SYN_LOGEN_CMOSRX3_OVRVAL 0xAD +#define B2056_SYN_LOGEN_CMOSRX4_OVRVAL 0xAE +#define B2056_SYN_LOGEN_CMOSTX1_OVRVAL 0xAF +#define B2056_SYN_LOGEN_CMOSTX2_OVRVAL 0xB0 +#define B2056_SYN_LOGEN_CMOSTX3_OVRVAL 0xB1 +#define B2056_SYN_LOGEN_CMOSTX4_OVRVAL 0xB2 +#define B2056_SYN_LOGEN_ACL_WAITCNT 0xB3 +#define B2056_SYN_LOGEN_CORE_CALVALID 0xB4 +#define B2056_SYN_LOGEN_RX_CMOS_CALVALID 0xB5 +#define B2056_SYN_LOGEN_TX_CMOS_VALID 0xB6 + +#define B2056_TX_RESERVED_ADDR0 0x00 +#define B2056_TX_IDCODE 0x01 +#define B2056_TX_RESERVED_ADDR2 0x02 +#define B2056_TX_RESERVED_ADDR3 0x03 +#define B2056_TX_RESERVED_ADDR4 0x04 +#define B2056_TX_RESERVED_ADDR5 0x05 +#define B2056_TX_RESERVED_ADDR6 0x06 +#define B2056_TX_RESERVED_ADDR7 0x07 +#define B2056_TX_COM_CTRL 0x08 +#define B2056_TX_COM_PU 0x09 +#define B2056_TX_COM_OVR 0x0A +#define B2056_TX_COM_RESET 0x0B +#define B2056_TX_COM_RCAL 0x0C +#define B2056_TX_COM_RC_RXLPF 0x0D +#define B2056_TX_COM_RC_TXLPF 0x0E +#define B2056_TX_COM_RC_RXHPF 0x0F +#define B2056_TX_RESERVED_ADDR16 0x10 +#define B2056_TX_RESERVED_ADDR17 0x11 +#define B2056_TX_RESERVED_ADDR18 0x12 +#define B2056_TX_RESERVED_ADDR19 0x13 +#define B2056_TX_RESERVED_ADDR20 0x14 +#define B2056_TX_RESERVED_ADDR21 0x15 +#define B2056_TX_RESERVED_ADDR22 0x16 +#define B2056_TX_RESERVED_ADDR23 0x17 +#define B2056_TX_RESERVED_ADDR24 0x18 +#define B2056_TX_RESERVED_ADDR25 0x19 +#define B2056_TX_RESERVED_ADDR26 0x1A +#define B2056_TX_RESERVED_ADDR27 0x1B +#define B2056_TX_RESERVED_ADDR28 0x1C +#define B2056_TX_RESERVED_ADDR29 0x1D +#define B2056_TX_RESERVED_ADDR30 0x1E +#define B2056_TX_RESERVED_ADDR31 0x1F +#define B2056_TX_IQCAL_GAIN_BW 0x20 +#define B2056_TX_LOFT_FINE_I 0x21 +#define B2056_TX_LOFT_FINE_Q 0x22 +#define B2056_TX_LOFT_COARSE_I 0x23 +#define B2056_TX_LOFT_COARSE_Q 0x24 +#define B2056_TX_TX_COM_MASTER1 0x25 +#define B2056_TX_TX_COM_MASTER2 0x26 +#define B2056_TX_RXIQCAL_TXMUX 0x27 +#define B2056_TX_TX_SSI_MASTER 0x28 +#define B2056_TX_IQCAL_VCM_HG 0x29 +#define B2056_TX_IQCAL_IDAC 0x2A +#define B2056_TX_TSSI_VCM 0x2B +#define B2056_TX_TX_AMP_DET 0x2C +#define B2056_TX_TX_SSI_MUX 0x2D +#define B2056_TX_TSSIA 0x2E +#define B2056_TX_TSSIG 0x2F +#define B2056_TX_TSSI_MISC1 0x30 +#define B2056_TX_TSSI_MISC2 0x31 +#define B2056_TX_TSSI_MISC3 0x32 +#define B2056_TX_PA_SPARE1 0x33 +#define B2056_TX_PA_SPARE2 0x34 +#define B2056_TX_INTPAA_MASTER 0x35 +#define B2056_TX_INTPAA_GAIN 0x36 +#define B2056_TX_INTPAA_BOOST_TUNE 0x37 +#define B2056_TX_INTPAA_IAUX_STAT 0x38 +#define B2056_TX_INTPAA_IAUX_DYN 0x39 +#define B2056_TX_INTPAA_IMAIN_STAT 0x3A +#define B2056_TX_INTPAA_IMAIN_DYN 0x3B +#define B2056_TX_INTPAA_CASCBIAS 0x3C +#define B2056_TX_INTPAA_PASLOPE 0x3D +#define B2056_TX_INTPAA_PA_MISC 0x3E +#define B2056_TX_INTPAG_MASTER 0x3F +#define B2056_TX_INTPAG_GAIN 0x40 +#define B2056_TX_INTPAG_BOOST_TUNE 0x41 +#define B2056_TX_INTPAG_IAUX_STAT 0x42 +#define B2056_TX_INTPAG_IAUX_DYN 0x43 +#define B2056_TX_INTPAG_IMAIN_STAT 0x44 +#define B2056_TX_INTPAG_IMAIN_DYN 0x45 +#define B2056_TX_INTPAG_CASCBIAS 0x46 +#define B2056_TX_INTPAG_PASLOPE 0x47 +#define B2056_TX_INTPAG_PA_MISC 0x48 +#define B2056_TX_PADA_MASTER 0x49 +#define B2056_TX_PADA_IDAC 0x4A +#define B2056_TX_PADA_CASCBIAS 0x4B +#define B2056_TX_PADA_GAIN 0x4C +#define B2056_TX_PADA_BOOST_TUNE 0x4D +#define B2056_TX_PADA_SLOPE 0x4E +#define B2056_TX_PADG_MASTER 0x4F +#define B2056_TX_PADG_IDAC 0x50 +#define B2056_TX_PADG_CASCBIAS 0x51 +#define B2056_TX_PADG_GAIN 0x52 +#define B2056_TX_PADG_BOOST_TUNE 0x53 +#define B2056_TX_PADG_SLOPE 0x54 +#define B2056_TX_PGAA_MASTER 0x55 +#define B2056_TX_PGAA_IDAC 0x56 +#define B2056_TX_PGAA_GAIN 0x57 +#define B2056_TX_PGAA_BOOST_TUNE 0x58 +#define B2056_TX_PGAA_SLOPE 0x59 +#define B2056_TX_PGAA_MISC 0x5A +#define B2056_TX_PGAG_MASTER 0x5B +#define B2056_TX_PGAG_IDAC 0x5C +#define B2056_TX_PGAG_GAIN 0x5D +#define B2056_TX_PGAG_BOOST_TUNE 0x5E +#define B2056_TX_PGAG_SLOPE 0x5F +#define B2056_TX_PGAG_MISC 0x60 +#define B2056_TX_MIXA_MASTER 0x61 +#define B2056_TX_MIXA_BOOST_TUNE 0x62 +#define B2056_TX_MIXG 0x63 +#define B2056_TX_MIXG_BOOST_TUNE 0x64 +#define B2056_TX_BB_GM_MASTER 0x65 +#define B2056_TX_GMBB_GM 0x66 +#define B2056_TX_GMBB_IDAC 0x67 +#define B2056_TX_TXLPF_MASTER 0x68 +#define B2056_TX_TXLPF_RCCAL 0x69 +#define B2056_TX_TXLPF_RCCAL_OFF0 0x6A +#define B2056_TX_TXLPF_RCCAL_OFF1 0x6B +#define B2056_TX_TXLPF_RCCAL_OFF2 0x6C +#define B2056_TX_TXLPF_RCCAL_OFF3 0x6D +#define B2056_TX_TXLPF_RCCAL_OFF4 0x6E +#define B2056_TX_TXLPF_RCCAL_OFF5 0x6F +#define B2056_TX_TXLPF_RCCAL_OFF6 0x70 +#define B2056_TX_TXLPF_BW 0x71 +#define B2056_TX_TXLPF_GAIN 0x72 +#define B2056_TX_TXLPF_IDAC 0x73 +#define B2056_TX_TXLPF_IDAC_0 0x74 +#define B2056_TX_TXLPF_IDAC_1 0x75 +#define B2056_TX_TXLPF_IDAC_2 0x76 +#define B2056_TX_TXLPF_IDAC_3 0x77 +#define B2056_TX_TXLPF_IDAC_4 0x78 +#define B2056_TX_TXLPF_IDAC_5 0x79 +#define B2056_TX_TXLPF_IDAC_6 0x7A +#define B2056_TX_TXLPF_OPAMP_IDAC 0x7B +#define B2056_TX_TXLPF_MISC 0x7C +#define B2056_TX_TXSPARE1 0x7D +#define B2056_TX_TXSPARE2 0x7E +#define B2056_TX_TXSPARE3 0x7F +#define B2056_TX_TXSPARE4 0x80 +#define B2056_TX_TXSPARE5 0x81 +#define B2056_TX_TXSPARE6 0x82 +#define B2056_TX_TXSPARE7 0x83 +#define B2056_TX_TXSPARE8 0x84 +#define B2056_TX_TXSPARE9 0x85 +#define B2056_TX_TXSPARE10 0x86 +#define B2056_TX_TXSPARE11 0x87 +#define B2056_TX_TXSPARE12 0x88 +#define B2056_TX_TXSPARE13 0x89 +#define B2056_TX_TXSPARE14 0x8A +#define B2056_TX_TXSPARE15 0x8B +#define B2056_TX_TXSPARE16 0x8C +#define B2056_TX_STATUS_INTPA_GAIN 0x8D +#define B2056_TX_STATUS_PAD_GAIN 0x8E +#define B2056_TX_STATUS_PGA_GAIN 0x8F +#define B2056_TX_STATUS_GM_TXLPF_GAIN 0x90 +#define B2056_TX_STATUS_TXLPF_BW 0x91 +#define B2056_TX_STATUS_TXLPF_RC 0x92 +#define B2056_TX_GMBB_IDAC0 0x93 +#define B2056_TX_GMBB_IDAC1 0x94 +#define B2056_TX_GMBB_IDAC2 0x95 +#define B2056_TX_GMBB_IDAC3 0x96 +#define B2056_TX_GMBB_IDAC4 0x97 +#define B2056_TX_GMBB_IDAC5 0x98 +#define B2056_TX_GMBB_IDAC6 0x99 +#define B2056_TX_GMBB_IDAC7 0x9A + +#define B2056_RX_RESERVED_ADDR0 0x00 +#define B2056_RX_IDCODE 0x01 +#define B2056_RX_RESERVED_ADDR2 0x02 +#define B2056_RX_RESERVED_ADDR3 0x03 +#define B2056_RX_RESERVED_ADDR4 0x04 +#define B2056_RX_RESERVED_ADDR5 0x05 +#define B2056_RX_RESERVED_ADDR6 0x06 +#define B2056_RX_RESERVED_ADDR7 0x07 +#define B2056_RX_COM_CTRL 0x08 +#define B2056_RX_COM_PU 0x09 +#define B2056_RX_COM_OVR 0x0A +#define B2056_RX_COM_RESET 0x0B +#define B2056_RX_COM_RCAL 0x0C +#define B2056_RX_COM_RC_RXLPF 0x0D +#define B2056_RX_COM_RC_TXLPF 0x0E +#define B2056_RX_COM_RC_RXHPF 0x0F +#define B2056_RX_RESERVED_ADDR16 0x10 +#define B2056_RX_RESERVED_ADDR17 0x11 +#define B2056_RX_RESERVED_ADDR18 0x12 +#define B2056_RX_RESERVED_ADDR19 0x13 +#define B2056_RX_RESERVED_ADDR20 0x14 +#define B2056_RX_RESERVED_ADDR21 0x15 +#define B2056_RX_RESERVED_ADDR22 0x16 +#define B2056_RX_RESERVED_ADDR23 0x17 +#define B2056_RX_RESERVED_ADDR24 0x18 +#define B2056_RX_RESERVED_ADDR25 0x19 +#define B2056_RX_RESERVED_ADDR26 0x1A +#define B2056_RX_RESERVED_ADDR27 0x1B +#define B2056_RX_RESERVED_ADDR28 0x1C +#define B2056_RX_RESERVED_ADDR29 0x1D +#define B2056_RX_RESERVED_ADDR30 0x1E +#define B2056_RX_RESERVED_ADDR31 0x1F +#define B2056_RX_RXIQCAL_RXMUX 0x20 +#define B2056_RX_RSSI_PU 0x21 +#define B2056_RX_RSSI_SEL 0x22 +#define B2056_RX_RSSI_GAIN 0x23 +#define B2056_RX_RSSI_NB_IDAC 0x24 +#define B2056_RX_RSSI_WB2I_IDAC_1 0x25 +#define B2056_RX_RSSI_WB2I_IDAC_2 0x26 +#define B2056_RX_RSSI_WB2Q_IDAC_1 0x27 +#define B2056_RX_RSSI_WB2Q_IDAC_2 0x28 +#define B2056_RX_RSSI_POLE 0x29 +#define B2056_RX_RSSI_WB1_IDAC 0x2A +#define B2056_RX_RSSI_MISC 0x2B +#define B2056_RX_LNAA_MASTER 0x2C +#define B2056_RX_LNAA_TUNE 0x2D +#define B2056_RX_LNAA_GAIN 0x2E +#define B2056_RX_LNA_A_SLOPE 0x2F +#define B2056_RX_BIASPOLE_LNAA1_IDAC 0x30 +#define B2056_RX_LNAA2_IDAC 0x31 +#define B2056_RX_LNA1A_MISC 0x32 +#define B2056_RX_LNAG_MASTER 0x33 +#define B2056_RX_LNAG_TUNE 0x34 +#define B2056_RX_LNAG_GAIN 0x35 +#define B2056_RX_LNA_G_SLOPE 0x36 +#define B2056_RX_BIASPOLE_LNAG1_IDAC 0x37 +#define B2056_RX_LNAG2_IDAC 0x38 +#define B2056_RX_LNA1G_MISC 0x39 +#define B2056_RX_MIXA_MASTER 0x3A +#define B2056_RX_MIXA_VCM 0x3B +#define B2056_RX_MIXA_CTRLPTAT 0x3C +#define B2056_RX_MIXA_LOB_BIAS 0x3D +#define B2056_RX_MIXA_CORE_IDAC 0x3E +#define B2056_RX_MIXA_CMFB_IDAC 0x3F +#define B2056_RX_MIXA_BIAS_AUX 0x40 +#define B2056_RX_MIXA_BIAS_MAIN 0x41 +#define B2056_RX_MIXA_BIAS_MISC 0x42 +#define B2056_RX_MIXA_MAST_BIAS 0x43 +#define B2056_RX_MIXG_MASTER 0x44 +#define B2056_RX_MIXG_VCM 0x45 +#define B2056_RX_MIXG_CTRLPTAT 0x46 +#define B2056_RX_MIXG_LOB_BIAS 0x47 +#define B2056_RX_MIXG_CORE_IDAC 0x48 +#define B2056_RX_MIXG_CMFB_IDAC 0x49 +#define B2056_RX_MIXG_BIAS_AUX 0x4A +#define B2056_RX_MIXG_BIAS_MAIN 0x4B +#define B2056_RX_MIXG_BIAS_MISC 0x4C +#define B2056_RX_MIXG_MAST_BIAS 0x4D +#define B2056_RX_TIA_MASTER 0x4E +#define B2056_RX_TIA_IOPAMP 0x4F +#define B2056_RX_TIA_QOPAMP 0x50 +#define B2056_RX_TIA_IMISC 0x51 +#define B2056_RX_TIA_QMISC 0x52 +#define B2056_RX_TIA_GAIN 0x53 +#define B2056_RX_TIA_SPARE1 0x54 +#define B2056_RX_TIA_SPARE2 0x55 +#define B2056_RX_BB_LPF_MASTER 0x56 +#define B2056_RX_AACI_MASTER 0x57 +#define B2056_RX_RXLPF_IDAC 0x58 +#define B2056_RX_RXLPF_OPAMPBIAS_LOWQ 0x59 +#define B2056_RX_RXLPF_OPAMPBIAS_HIGHQ 0x5A +#define B2056_RX_RXLPF_BIAS_DCCANCEL 0x5B +#define B2056_RX_RXLPF_OUTVCM 0x5C +#define B2056_RX_RXLPF_INVCM_BODY 0x5D +#define B2056_RX_RXLPF_CC_OP 0x5E +#define B2056_RX_RXLPF_GAIN 0x5F +#define B2056_RX_RXLPF_Q_BW 0x60 +#define B2056_RX_RXLPF_HP_CORNER_BW 0x61 +#define B2056_RX_RXLPF_RCCAL_HPC 0x62 +#define B2056_RX_RXHPF_OFF0 0x63 +#define B2056_RX_RXHPF_OFF1 0x64 +#define B2056_RX_RXHPF_OFF2 0x65 +#define B2056_RX_RXHPF_OFF3 0x66 +#define B2056_RX_RXHPF_OFF4 0x67 +#define B2056_RX_RXHPF_OFF5 0x68 +#define B2056_RX_RXHPF_OFF6 0x69 +#define B2056_RX_RXHPF_OFF7 0x6A +#define B2056_RX_RXLPF_RCCAL_LPC 0x6B +#define B2056_RX_RXLPF_OFF_0 0x6C +#define B2056_RX_RXLPF_OFF_1 0x6D +#define B2056_RX_RXLPF_OFF_2 0x6E +#define B2056_RX_RXLPF_OFF_3 0x6F +#define B2056_RX_RXLPF_OFF_4 0x70 +#define B2056_RX_UNUSED 0x71 +#define B2056_RX_VGA_MASTER 0x72 +#define B2056_RX_VGA_BIAS 0x73 +#define B2056_RX_VGA_BIAS_DCCANCEL 0x74 +#define B2056_RX_VGA_GAIN 0x75 +#define B2056_RX_VGA_HP_CORNER_BW 0x76 +#define B2056_RX_VGABUF_BIAS 0x77 +#define B2056_RX_VGABUF_GAIN_BW 0x78 +#define B2056_RX_TXFBMIX_A 0x79 +#define B2056_RX_TXFBMIX_G 0x7A +#define B2056_RX_RXSPARE1 0x7B +#define B2056_RX_RXSPARE2 0x7C +#define B2056_RX_RXSPARE3 0x7D +#define B2056_RX_RXSPARE4 0x7E +#define B2056_RX_RXSPARE5 0x7F +#define B2056_RX_RXSPARE6 0x80 +#define B2056_RX_RXSPARE7 0x81 +#define B2056_RX_RXSPARE8 0x82 +#define B2056_RX_RXSPARE9 0x83 +#define B2056_RX_RXSPARE10 0x84 +#define B2056_RX_RXSPARE11 0x85 +#define B2056_RX_RXSPARE12 0x86 +#define B2056_RX_RXSPARE13 0x87 +#define B2056_RX_RXSPARE14 0x88 +#define B2056_RX_RXSPARE15 0x89 +#define B2056_RX_RXSPARE16 0x8A +#define B2056_RX_STATUS_LNAA_GAIN 0x8B +#define B2056_RX_STATUS_LNAG_GAIN 0x8C +#define B2056_RX_STATUS_MIXTIA_GAIN 0x8D +#define B2056_RX_STATUS_RXLPF_GAIN 0x8E +#define B2056_RX_STATUS_VGA_BUF_GAIN 0x8F +#define B2056_RX_STATUS_RXLPF_Q 0x90 +#define B2056_RX_STATUS_RXLPF_BUF_BW 0x91 +#define B2056_RX_STATUS_RXLPF_VGA_HPC 0x92 +#define B2056_RX_STATUS_RXLPF_RC 0x93 +#define B2056_RX_STATUS_HPC_RC 0x94 + +#define B2056_LNA1_A_PU 0x01 +#define B2056_LNA2_A_PU 0x02 +#define B2056_LNA1_G_PU 0x01 +#define B2056_LNA2_G_PU 0x02 +#define B2056_MIXA_PU_I 0x01 +#define B2056_MIXA_PU_Q 0x02 +#define B2056_MIXA_PU_GM 0x10 +#define B2056_MIXG_PU_I 0x01 +#define B2056_MIXG_PU_Q 0x02 +#define B2056_MIXG_PU_GM 0x10 +#define B2056_TIA_PU 0x01 +#define B2056_BB_LPF_PU 0x20 +#define B2056_W1_PU 0x02 +#define B2056_W2_PU 0x04 +#define B2056_NB_PU 0x08 +#define B2056_RSSI_W1_SEL 0x02 +#define B2056_RSSI_W2_SEL 0x04 +#define B2056_RSSI_NB_SEL 0x08 +#define B2056_VCM_MASK 0x1C +#define B2056_RSSI_VCM_SHIFT 0x02 + +#define B2056_SYN (0x0 << 12) +#define B2056_TX0 (0x2 << 12) +#define B2056_TX1 (0x3 << 12) +#define B2056_RX0 (0x6 << 12) +#define B2056_RX1 (0x7 << 12) +#define B2056_ALLTX (0xE << 12) +#define B2056_ALLRX (0xF << 12) + +#define B2056_SYN_RESERVED_ADDR0 0x00 +#define B2056_SYN_IDCODE 0x01 +#define B2056_SYN_RESERVED_ADDR2 0x02 +#define B2056_SYN_RESERVED_ADDR3 0x03 +#define B2056_SYN_RESERVED_ADDR4 0x04 +#define B2056_SYN_RESERVED_ADDR5 0x05 +#define B2056_SYN_RESERVED_ADDR6 0x06 +#define B2056_SYN_RESERVED_ADDR7 0x07 +#define B2056_SYN_COM_CTRL 0x08 +#define B2056_SYN_COM_PU 0x09 +#define B2056_SYN_COM_OVR 0x0A +#define B2056_SYN_COM_RESET 0x0B +#define B2056_SYN_COM_RCAL 0x0C +#define B2056_SYN_COM_RC_RXLPF 0x0D +#define B2056_SYN_COM_RC_TXLPF 0x0E +#define B2056_SYN_COM_RC_RXHPF 0x0F +#define B2056_SYN_RESERVED_ADDR16 0x10 +#define B2056_SYN_RESERVED_ADDR17 0x11 +#define B2056_SYN_RESERVED_ADDR18 0x12 +#define B2056_SYN_RESERVED_ADDR19 0x13 +#define B2056_SYN_RESERVED_ADDR20 0x14 +#define B2056_SYN_RESERVED_ADDR21 0x15 +#define B2056_SYN_RESERVED_ADDR22 0x16 +#define B2056_SYN_RESERVED_ADDR23 0x17 +#define B2056_SYN_RESERVED_ADDR24 0x18 +#define B2056_SYN_RESERVED_ADDR25 0x19 +#define B2056_SYN_RESERVED_ADDR26 0x1A +#define B2056_SYN_RESERVED_ADDR27 0x1B +#define B2056_SYN_RESERVED_ADDR28 0x1C +#define B2056_SYN_RESERVED_ADDR29 0x1D +#define B2056_SYN_RESERVED_ADDR30 0x1E +#define B2056_SYN_RESERVED_ADDR31 0x1F +#define B2056_SYN_GPIO_MASTER1 0x20 +#define B2056_SYN_GPIO_MASTER2 0x21 +#define B2056_SYN_TOPBIAS_MASTER 0x22 +#define B2056_SYN_TOPBIAS_RCAL 0x23 +#define B2056_SYN_AFEREG 0x24 +#define B2056_SYN_TEMPPROCSENSE 0x25 +#define B2056_SYN_TEMPPROCSENSEIDAC 0x26 +#define B2056_SYN_TEMPPROCSENSERCAL 0x27 +#define B2056_SYN_LPO 0x28 +#define B2056_SYN_VDDCAL_MASTER 0x29 +#define B2056_SYN_VDDCAL_IDAC 0x2A +#define B2056_SYN_VDDCAL_STATUS 0x2B +#define B2056_SYN_RCAL_MASTER 0x2C +#define B2056_SYN_RCAL_CODE_OUT 0x2D +#define B2056_SYN_RCCAL_CTRL0 0x2E +#define B2056_SYN_RCCAL_CTRL1 0x2F +#define B2056_SYN_RCCAL_CTRL2 0x30 +#define B2056_SYN_RCCAL_CTRL3 0x31 +#define B2056_SYN_RCCAL_CTRL4 0x32 +#define B2056_SYN_RCCAL_CTRL5 0x33 +#define B2056_SYN_RCCAL_CTRL6 0x34 +#define B2056_SYN_RCCAL_CTRL7 0x35 +#define B2056_SYN_RCCAL_CTRL8 0x36 +#define B2056_SYN_RCCAL_CTRL9 0x37 +#define B2056_SYN_RCCAL_CTRL10 0x38 +#define B2056_SYN_RCCAL_CTRL11 0x39 +#define B2056_SYN_ZCAL_SPARE1 0x3A +#define B2056_SYN_ZCAL_SPARE2 0x3B +#define B2056_SYN_PLL_MAST1 0x3C +#define B2056_SYN_PLL_MAST2 0x3D +#define B2056_SYN_PLL_MAST3 0x3E +#define B2056_SYN_PLL_BIAS_RESET 0x3F +#define B2056_SYN_PLL_XTAL0 0x40 +#define B2056_SYN_PLL_XTAL1 0x41 +#define B2056_SYN_PLL_XTAL3 0x42 +#define B2056_SYN_PLL_XTAL4 0x43 +#define B2056_SYN_PLL_XTAL5 0x44 +#define B2056_SYN_PLL_XTAL6 0x45 +#define B2056_SYN_PLL_REFDIV 0x46 +#define B2056_SYN_PLL_PFD 0x47 +#define B2056_SYN_PLL_CP1 0x48 +#define B2056_SYN_PLL_CP2 0x49 +#define B2056_SYN_PLL_CP3 0x4A +#define B2056_SYN_PLL_LOOPFILTER1 0x4B +#define B2056_SYN_PLL_LOOPFILTER2 0x4C +#define B2056_SYN_PLL_LOOPFILTER3 0x4D +#define B2056_SYN_PLL_LOOPFILTER4 0x4E +#define B2056_SYN_PLL_LOOPFILTER5 0x4F +#define B2056_SYN_PLL_MMD1 0x50 +#define B2056_SYN_PLL_MMD2 0x51 +#define B2056_SYN_PLL_VCO1 0x52 +#define B2056_SYN_PLL_VCO2 0x53 +#define B2056_SYN_PLL_MONITOR1 0x54 +#define B2056_SYN_PLL_MONITOR2 0x55 +#define B2056_SYN_PLL_VCOCAL1 0x56 +#define B2056_SYN_PLL_VCOCAL2 0x57 +#define B2056_SYN_PLL_VCOCAL4 0x58 +#define B2056_SYN_PLL_VCOCAL5 0x59 +#define B2056_SYN_PLL_VCOCAL6 0x5A +#define B2056_SYN_PLL_VCOCAL7 0x5B +#define B2056_SYN_PLL_VCOCAL8 0x5C +#define B2056_SYN_PLL_VCOCAL9 0x5D +#define B2056_SYN_PLL_VCOCAL10 0x5E +#define B2056_SYN_PLL_VCOCAL11 0x5F +#define B2056_SYN_PLL_VCOCAL12 0x60 +#define B2056_SYN_PLL_VCOCAL13 0x61 +#define B2056_SYN_PLL_VREG 0x62 +#define B2056_SYN_PLL_STATUS1 0x63 +#define B2056_SYN_PLL_STATUS2 0x64 +#define B2056_SYN_PLL_STATUS3 0x65 +#define B2056_SYN_LOGEN_PU0 0x66 +#define B2056_SYN_LOGEN_PU1 0x67 +#define B2056_SYN_LOGEN_PU2 0x68 +#define B2056_SYN_LOGEN_PU3 0x69 +#define B2056_SYN_LOGEN_PU5 0x6A +#define B2056_SYN_LOGEN_PU6 0x6B +#define B2056_SYN_LOGEN_PU7 0x6C +#define B2056_SYN_LOGEN_PU8 0x6D +#define B2056_SYN_LOGEN_BIAS_RESET 0x6E +#define B2056_SYN_LOGEN_RCCR1 0x6F +#define B2056_SYN_LOGEN_VCOBUF1 0x70 +#define B2056_SYN_LOGEN_MIXER1 0x71 +#define B2056_SYN_LOGEN_MIXER2 0x72 +#define B2056_SYN_LOGEN_BUF1 0x73 +#define B2056_SYN_LOGENBUF2 0x74 +#define B2056_SYN_LOGEN_BUF3 0x75 +#define B2056_SYN_LOGEN_BUF4 0x76 +#define B2056_SYN_LOGEN_DIV1 0x77 +#define B2056_SYN_LOGEN_DIV2 0x78 +#define B2056_SYN_LOGEN_DIV3 0x79 +#define B2056_SYN_LOGEN_ACL1 0x7A +#define B2056_SYN_LOGEN_ACL2 0x7B +#define B2056_SYN_LOGEN_ACL3 0x7C +#define B2056_SYN_LOGEN_ACL4 0x7D +#define B2056_SYN_LOGEN_ACL5 0x7E +#define B2056_SYN_LOGEN_ACL6 0x7F +#define B2056_SYN_LOGEN_ACLOUT 0x80 +#define B2056_SYN_LOGEN_ACLCAL1 0x81 +#define B2056_SYN_LOGEN_ACLCAL2 0x82 +#define B2056_SYN_LOGEN_ACLCAL3 0x83 +#define B2056_SYN_CALEN 0x84 +#define B2056_SYN_LOGEN_PEAKDET1 0x85 +#define B2056_SYN_LOGEN_CORE_ACL_OVR 0x86 +#define B2056_SYN_LOGEN_RX_DIFF_ACL_OVR 0x87 +#define B2056_SYN_LOGEN_TX_DIFF_ACL_OVR 0x88 +#define B2056_SYN_LOGEN_RX_CMOS_ACL_OVR 0x89 +#define B2056_SYN_LOGEN_TX_CMOS_ACL_OVR 0x8A +#define B2056_SYN_LOGEN_VCOBUF2 0x8B +#define B2056_SYN_LOGEN_MIXER3 0x8C +#define B2056_SYN_LOGEN_BUF5 0x8D +#define B2056_SYN_LOGEN_BUF6 0x8E +#define B2056_SYN_LOGEN_CBUFRX1 0x8F +#define B2056_SYN_LOGEN_CBUFRX2 0x90 +#define B2056_SYN_LOGEN_CBUFRX3 0x91 +#define B2056_SYN_LOGEN_CBUFRX4 0x92 +#define B2056_SYN_LOGEN_CBUFTX1 0x93 +#define B2056_SYN_LOGEN_CBUFTX2 0x94 +#define B2056_SYN_LOGEN_CBUFTX3 0x95 +#define B2056_SYN_LOGEN_CBUFTX4 0x96 +#define B2056_SYN_LOGEN_CMOSRX1 0x97 +#define B2056_SYN_LOGEN_CMOSRX2 0x98 +#define B2056_SYN_LOGEN_CMOSRX3 0x99 +#define B2056_SYN_LOGEN_CMOSRX4 0x9A +#define B2056_SYN_LOGEN_CMOSTX1 0x9B +#define B2056_SYN_LOGEN_CMOSTX2 0x9C +#define B2056_SYN_LOGEN_CMOSTX3 0x9D +#define B2056_SYN_LOGEN_CMOSTX4 0x9E +#define B2056_SYN_LOGEN_VCOBUF2_OVRVAL 0x9F +#define B2056_SYN_LOGEN_MIXER3_OVRVAL 0xA0 +#define B2056_SYN_LOGEN_BUF5_OVRVAL 0xA1 +#define B2056_SYN_LOGEN_BUF6_OVRVAL 0xA2 +#define B2056_SYN_LOGEN_CBUFRX1_OVRVAL 0xA3 +#define B2056_SYN_LOGEN_CBUFRX2_OVRVAL 0xA4 +#define B2056_SYN_LOGEN_CBUFRX3_OVRVAL 0xA5 +#define B2056_SYN_LOGEN_CBUFRX4_OVRVAL 0xA6 +#define B2056_SYN_LOGEN_CBUFTX1_OVRVAL 0xA7 +#define B2056_SYN_LOGEN_CBUFTX2_OVRVAL 0xA8 +#define B2056_SYN_LOGEN_CBUFTX3_OVRVAL 0xA9 +#define B2056_SYN_LOGEN_CBUFTX4_OVRVAL 0xAA +#define B2056_SYN_LOGEN_CMOSRX1_OVRVAL 0xAB +#define B2056_SYN_LOGEN_CMOSRX2_OVRVAL 0xAC +#define B2056_SYN_LOGEN_CMOSRX3_OVRVAL 0xAD +#define B2056_SYN_LOGEN_CMOSRX4_OVRVAL 0xAE +#define B2056_SYN_LOGEN_CMOSTX1_OVRVAL 0xAF +#define B2056_SYN_LOGEN_CMOSTX2_OVRVAL 0xB0 +#define B2056_SYN_LOGEN_CMOSTX3_OVRVAL 0xB1 +#define B2056_SYN_LOGEN_CMOSTX4_OVRVAL 0xB2 +#define B2056_SYN_LOGEN_ACL_WAITCNT 0xB3 +#define B2056_SYN_LOGEN_CORE_CALVALID 0xB4 +#define B2056_SYN_LOGEN_RX_CMOS_CALVALID 0xB5 +#define B2056_SYN_LOGEN_TX_CMOS_VALID 0xB6 + +#define B2056_TX_RESERVED_ADDR0 0x00 +#define B2056_TX_IDCODE 0x01 +#define B2056_TX_RESERVED_ADDR2 0x02 +#define B2056_TX_RESERVED_ADDR3 0x03 +#define B2056_TX_RESERVED_ADDR4 0x04 +#define B2056_TX_RESERVED_ADDR5 0x05 +#define B2056_TX_RESERVED_ADDR6 0x06 +#define B2056_TX_RESERVED_ADDR7 0x07 +#define B2056_TX_COM_CTRL 0x08 +#define B2056_TX_COM_PU 0x09 +#define B2056_TX_COM_OVR 0x0A +#define B2056_TX_COM_RESET 0x0B +#define B2056_TX_COM_RCAL 0x0C +#define B2056_TX_COM_RC_RXLPF 0x0D +#define B2056_TX_COM_RC_TXLPF 0x0E +#define B2056_TX_COM_RC_RXHPF 0x0F +#define B2056_TX_RESERVED_ADDR16 0x10 +#define B2056_TX_RESERVED_ADDR17 0x11 +#define B2056_TX_RESERVED_ADDR18 0x12 +#define B2056_TX_RESERVED_ADDR19 0x13 +#define B2056_TX_RESERVED_ADDR20 0x14 +#define B2056_TX_RESERVED_ADDR21 0x15 +#define B2056_TX_RESERVED_ADDR22 0x16 +#define B2056_TX_RESERVED_ADDR23 0x17 +#define B2056_TX_RESERVED_ADDR24 0x18 +#define B2056_TX_RESERVED_ADDR25 0x19 +#define B2056_TX_RESERVED_ADDR26 0x1A +#define B2056_TX_RESERVED_ADDR27 0x1B +#define B2056_TX_RESERVED_ADDR28 0x1C +#define B2056_TX_RESERVED_ADDR29 0x1D +#define B2056_TX_RESERVED_ADDR30 0x1E +#define B2056_TX_RESERVED_ADDR31 0x1F +#define B2056_TX_IQCAL_GAIN_BW 0x20 +#define B2056_TX_LOFT_FINE_I 0x21 +#define B2056_TX_LOFT_FINE_Q 0x22 +#define B2056_TX_LOFT_COARSE_I 0x23 +#define B2056_TX_LOFT_COARSE_Q 0x24 +#define B2056_TX_TX_COM_MASTER1 0x25 +#define B2056_TX_TX_COM_MASTER2 0x26 +#define B2056_TX_RXIQCAL_TXMUX 0x27 +#define B2056_TX_TX_SSI_MASTER 0x28 +#define B2056_TX_IQCAL_VCM_HG 0x29 +#define B2056_TX_IQCAL_IDAC 0x2A +#define B2056_TX_TSSI_VCM 0x2B +#define B2056_TX_TX_AMP_DET 0x2C +#define B2056_TX_TX_SSI_MUX 0x2D +#define B2056_TX_TSSIA 0x2E +#define B2056_TX_TSSIG 0x2F +#define B2056_TX_TSSI_MISC1 0x30 +#define B2056_TX_TSSI_MISC2 0x31 +#define B2056_TX_TSSI_MISC3 0x32 +#define B2056_TX_PA_SPARE1 0x33 +#define B2056_TX_PA_SPARE2 0x34 +#define B2056_TX_INTPAA_MASTER 0x35 +#define B2056_TX_INTPAA_GAIN 0x36 +#define B2056_TX_INTPAA_BOOST_TUNE 0x37 +#define B2056_TX_INTPAA_IAUX_STAT 0x38 +#define B2056_TX_INTPAA_IAUX_DYN 0x39 +#define B2056_TX_INTPAA_IMAIN_STAT 0x3A +#define B2056_TX_INTPAA_IMAIN_DYN 0x3B +#define B2056_TX_INTPAA_CASCBIAS 0x3C +#define B2056_TX_INTPAA_PASLOPE 0x3D +#define B2056_TX_INTPAA_PA_MISC 0x3E +#define B2056_TX_INTPAG_MASTER 0x3F +#define B2056_TX_INTPAG_GAIN 0x40 +#define B2056_TX_INTPAG_BOOST_TUNE 0x41 +#define B2056_TX_INTPAG_IAUX_STAT 0x42 +#define B2056_TX_INTPAG_IAUX_DYN 0x43 +#define B2056_TX_INTPAG_IMAIN_STAT 0x44 +#define B2056_TX_INTPAG_IMAIN_DYN 0x45 +#define B2056_TX_INTPAG_CASCBIAS 0x46 +#define B2056_TX_INTPAG_PASLOPE 0x47 +#define B2056_TX_INTPAG_PA_MISC 0x48 +#define B2056_TX_PADA_MASTER 0x49 +#define B2056_TX_PADA_IDAC 0x4A +#define B2056_TX_PADA_CASCBIAS 0x4B +#define B2056_TX_PADA_GAIN 0x4C +#define B2056_TX_PADA_BOOST_TUNE 0x4D +#define B2056_TX_PADA_SLOPE 0x4E +#define B2056_TX_PADG_MASTER 0x4F +#define B2056_TX_PADG_IDAC 0x50 +#define B2056_TX_PADG_CASCBIAS 0x51 +#define B2056_TX_PADG_GAIN 0x52 +#define B2056_TX_PADG_BOOST_TUNE 0x53 +#define B2056_TX_PADG_SLOPE 0x54 +#define B2056_TX_PGAA_MASTER 0x55 +#define B2056_TX_PGAA_IDAC 0x56 +#define B2056_TX_PGAA_GAIN 0x57 +#define B2056_TX_PGAA_BOOST_TUNE 0x58 +#define B2056_TX_PGAA_SLOPE 0x59 +#define B2056_TX_PGAA_MISC 0x5A +#define B2056_TX_PGAG_MASTER 0x5B +#define B2056_TX_PGAG_IDAC 0x5C +#define B2056_TX_PGAG_GAIN 0x5D +#define B2056_TX_PGAG_BOOST_TUNE 0x5E +#define B2056_TX_PGAG_SLOPE 0x5F +#define B2056_TX_PGAG_MISC 0x60 +#define B2056_TX_MIXA_MASTER 0x61 +#define B2056_TX_MIXA_BOOST_TUNE 0x62 +#define B2056_TX_MIXG 0x63 +#define B2056_TX_MIXG_BOOST_TUNE 0x64 +#define B2056_TX_BB_GM_MASTER 0x65 +#define B2056_TX_GMBB_GM 0x66 +#define B2056_TX_GMBB_IDAC 0x67 +#define B2056_TX_TXLPF_MASTER 0x68 +#define B2056_TX_TXLPF_RCCAL 0x69 +#define B2056_TX_TXLPF_RCCAL_OFF0 0x6A +#define B2056_TX_TXLPF_RCCAL_OFF1 0x6B +#define B2056_TX_TXLPF_RCCAL_OFF2 0x6C +#define B2056_TX_TXLPF_RCCAL_OFF3 0x6D +#define B2056_TX_TXLPF_RCCAL_OFF4 0x6E +#define B2056_TX_TXLPF_RCCAL_OFF5 0x6F +#define B2056_TX_TXLPF_RCCAL_OFF6 0x70 +#define B2056_TX_TXLPF_BW 0x71 +#define B2056_TX_TXLPF_GAIN 0x72 +#define B2056_TX_TXLPF_IDAC 0x73 +#define B2056_TX_TXLPF_IDAC_0 0x74 +#define B2056_TX_TXLPF_IDAC_1 0x75 +#define B2056_TX_TXLPF_IDAC_2 0x76 +#define B2056_TX_TXLPF_IDAC_3 0x77 +#define B2056_TX_TXLPF_IDAC_4 0x78 +#define B2056_TX_TXLPF_IDAC_5 0x79 +#define B2056_TX_TXLPF_IDAC_6 0x7A +#define B2056_TX_TXLPF_OPAMP_IDAC 0x7B +#define B2056_TX_TXLPF_MISC 0x7C +#define B2056_TX_TXSPARE1 0x7D +#define B2056_TX_TXSPARE2 0x7E +#define B2056_TX_TXSPARE3 0x7F +#define B2056_TX_TXSPARE4 0x80 +#define B2056_TX_TXSPARE5 0x81 +#define B2056_TX_TXSPARE6 0x82 +#define B2056_TX_TXSPARE7 0x83 +#define B2056_TX_TXSPARE8 0x84 +#define B2056_TX_TXSPARE9 0x85 +#define B2056_TX_TXSPARE10 0x86 +#define B2056_TX_TXSPARE11 0x87 +#define B2056_TX_TXSPARE12 0x88 +#define B2056_TX_TXSPARE13 0x89 +#define B2056_TX_TXSPARE14 0x8A +#define B2056_TX_TXSPARE15 0x8B +#define B2056_TX_TXSPARE16 0x8C +#define B2056_TX_STATUS_INTPA_GAIN 0x8D +#define B2056_TX_STATUS_PAD_GAIN 0x8E +#define B2056_TX_STATUS_PGA_GAIN 0x8F +#define B2056_TX_STATUS_GM_TXLPF_GAIN 0x90 +#define B2056_TX_STATUS_TXLPF_BW 0x91 +#define B2056_TX_STATUS_TXLPF_RC 0x92 +#define B2056_TX_GMBB_IDAC0 0x93 +#define B2056_TX_GMBB_IDAC1 0x94 +#define B2056_TX_GMBB_IDAC2 0x95 +#define B2056_TX_GMBB_IDAC3 0x96 +#define B2056_TX_GMBB_IDAC4 0x97 +#define B2056_TX_GMBB_IDAC5 0x98 +#define B2056_TX_GMBB_IDAC6 0x99 +#define B2056_TX_GMBB_IDAC7 0x9A + +#define B2056_RX_RESERVED_ADDR0 0x00 +#define B2056_RX_IDCODE 0x01 +#define B2056_RX_RESERVED_ADDR2 0x02 +#define B2056_RX_RESERVED_ADDR3 0x03 +#define B2056_RX_RESERVED_ADDR4 0x04 +#define B2056_RX_RESERVED_ADDR5 0x05 +#define B2056_RX_RESERVED_ADDR6 0x06 +#define B2056_RX_RESERVED_ADDR7 0x07 +#define B2056_RX_COM_CTRL 0x08 +#define B2056_RX_COM_PU 0x09 +#define B2056_RX_COM_OVR 0x0A +#define B2056_RX_COM_RESET 0x0B +#define B2056_RX_COM_RCAL 0x0C +#define B2056_RX_COM_RC_RXLPF 0x0D +#define B2056_RX_COM_RC_TXLPF 0x0E +#define B2056_RX_COM_RC_RXHPF 0x0F +#define B2056_RX_RESERVED_ADDR16 0x10 +#define B2056_RX_RESERVED_ADDR17 0x11 +#define B2056_RX_RESERVED_ADDR18 0x12 +#define B2056_RX_RESERVED_ADDR19 0x13 +#define B2056_RX_RESERVED_ADDR20 0x14 +#define B2056_RX_RESERVED_ADDR21 0x15 +#define B2056_RX_RESERVED_ADDR22 0x16 +#define B2056_RX_RESERVED_ADDR23 0x17 +#define B2056_RX_RESERVED_ADDR24 0x18 +#define B2056_RX_RESERVED_ADDR25 0x19 +#define B2056_RX_RESERVED_ADDR26 0x1A +#define B2056_RX_RESERVED_ADDR27 0x1B +#define B2056_RX_RESERVED_ADDR28 0x1C +#define B2056_RX_RESERVED_ADDR29 0x1D +#define B2056_RX_RESERVED_ADDR30 0x1E +#define B2056_RX_RESERVED_ADDR31 0x1F +#define B2056_RX_RXIQCAL_RXMUX 0x20 +#define B2056_RX_RSSI_PU 0x21 +#define B2056_RX_RSSI_SEL 0x22 +#define B2056_RX_RSSI_GAIN 0x23 +#define B2056_RX_RSSI_NB_IDAC 0x24 +#define B2056_RX_RSSI_WB2I_IDAC_1 0x25 +#define B2056_RX_RSSI_WB2I_IDAC_2 0x26 +#define B2056_RX_RSSI_WB2Q_IDAC_1 0x27 +#define B2056_RX_RSSI_WB2Q_IDAC_2 0x28 +#define B2056_RX_RSSI_POLE 0x29 +#define B2056_RX_RSSI_WB1_IDAC 0x2A +#define B2056_RX_RSSI_MISC 0x2B +#define B2056_RX_LNAA_MASTER 0x2C +#define B2056_RX_LNAA_TUNE 0x2D +#define B2056_RX_LNAA_GAIN 0x2E +#define B2056_RX_LNA_A_SLOPE 0x2F +#define B2056_RX_BIASPOLE_LNAA1_IDAC 0x30 +#define B2056_RX_LNAA2_IDAC 0x31 +#define B2056_RX_LNA1A_MISC 0x32 +#define B2056_RX_LNAG_MASTER 0x33 +#define B2056_RX_LNAG_TUNE 0x34 +#define B2056_RX_LNAG_GAIN 0x35 +#define B2056_RX_LNA_G_SLOPE 0x36 +#define B2056_RX_BIASPOLE_LNAG1_IDAC 0x37 +#define B2056_RX_LNAG2_IDAC 0x38 +#define B2056_RX_LNA1G_MISC 0x39 +#define B2056_RX_MIXA_MASTER 0x3A +#define B2056_RX_MIXA_VCM 0x3B +#define B2056_RX_MIXA_CTRLPTAT 0x3C +#define B2056_RX_MIXA_LOB_BIAS 0x3D +#define B2056_RX_MIXA_CORE_IDAC 0x3E +#define B2056_RX_MIXA_CMFB_IDAC 0x3F +#define B2056_RX_MIXA_BIAS_AUX 0x40 +#define B2056_RX_MIXA_BIAS_MAIN 0x41 +#define B2056_RX_MIXA_BIAS_MISC 0x42 +#define B2056_RX_MIXA_MAST_BIAS 0x43 +#define B2056_RX_MIXG_MASTER 0x44 +#define B2056_RX_MIXG_VCM 0x45 +#define B2056_RX_MIXG_CTRLPTAT 0x46 +#define B2056_RX_MIXG_LOB_BIAS 0x47 +#define B2056_RX_MIXG_CORE_IDAC 0x48 +#define B2056_RX_MIXG_CMFB_IDAC 0x49 +#define B2056_RX_MIXG_BIAS_AUX 0x4A +#define B2056_RX_MIXG_BIAS_MAIN 0x4B +#define B2056_RX_MIXG_BIAS_MISC 0x4C +#define B2056_RX_MIXG_MAST_BIAS 0x4D +#define B2056_RX_TIA_MASTER 0x4E +#define B2056_RX_TIA_IOPAMP 0x4F +#define B2056_RX_TIA_QOPAMP 0x50 +#define B2056_RX_TIA_IMISC 0x51 +#define B2056_RX_TIA_QMISC 0x52 +#define B2056_RX_TIA_GAIN 0x53 +#define B2056_RX_TIA_SPARE1 0x54 +#define B2056_RX_TIA_SPARE2 0x55 +#define B2056_RX_BB_LPF_MASTER 0x56 +#define B2056_RX_AACI_MASTER 0x57 +#define B2056_RX_RXLPF_IDAC 0x58 +#define B2056_RX_RXLPF_OPAMPBIAS_LOWQ 0x59 +#define B2056_RX_RXLPF_OPAMPBIAS_HIGHQ 0x5A +#define B2056_RX_RXLPF_BIAS_DCCANCEL 0x5B +#define B2056_RX_RXLPF_OUTVCM 0x5C +#define B2056_RX_RXLPF_INVCM_BODY 0x5D +#define B2056_RX_RXLPF_CC_OP 0x5E +#define B2056_RX_RXLPF_GAIN 0x5F +#define B2056_RX_RXLPF_Q_BW 0x60 +#define B2056_RX_RXLPF_HP_CORNER_BW 0x61 +#define B2056_RX_RXLPF_RCCAL_HPC 0x62 +#define B2056_RX_RXHPF_OFF0 0x63 +#define B2056_RX_RXHPF_OFF1 0x64 +#define B2056_RX_RXHPF_OFF2 0x65 +#define B2056_RX_RXHPF_OFF3 0x66 +#define B2056_RX_RXHPF_OFF4 0x67 +#define B2056_RX_RXHPF_OFF5 0x68 +#define B2056_RX_RXHPF_OFF6 0x69 +#define B2056_RX_RXHPF_OFF7 0x6A +#define B2056_RX_RXLPF_RCCAL_LPC 0x6B +#define B2056_RX_RXLPF_OFF_0 0x6C +#define B2056_RX_RXLPF_OFF_1 0x6D +#define B2056_RX_RXLPF_OFF_2 0x6E +#define B2056_RX_RXLPF_OFF_3 0x6F +#define B2056_RX_RXLPF_OFF_4 0x70 +#define B2056_RX_UNUSED 0x71 +#define B2056_RX_VGA_MASTER 0x72 +#define B2056_RX_VGA_BIAS 0x73 +#define B2056_RX_VGA_BIAS_DCCANCEL 0x74 +#define B2056_RX_VGA_GAIN 0x75 +#define B2056_RX_VGA_HP_CORNER_BW 0x76 +#define B2056_RX_VGABUF_BIAS 0x77 +#define B2056_RX_VGABUF_GAIN_BW 0x78 +#define B2056_RX_TXFBMIX_A 0x79 +#define B2056_RX_TXFBMIX_G 0x7A +#define B2056_RX_RXSPARE1 0x7B +#define B2056_RX_RXSPARE2 0x7C +#define B2056_RX_RXSPARE3 0x7D +#define B2056_RX_RXSPARE4 0x7E +#define B2056_RX_RXSPARE5 0x7F +#define B2056_RX_RXSPARE6 0x80 +#define B2056_RX_RXSPARE7 0x81 +#define B2056_RX_RXSPARE8 0x82 +#define B2056_RX_RXSPARE9 0x83 +#define B2056_RX_RXSPARE10 0x84 +#define B2056_RX_RXSPARE11 0x85 +#define B2056_RX_RXSPARE12 0x86 +#define B2056_RX_RXSPARE13 0x87 +#define B2056_RX_RXSPARE14 0x88 +#define B2056_RX_RXSPARE15 0x89 +#define B2056_RX_RXSPARE16 0x8A +#define B2056_RX_STATUS_LNAA_GAIN 0x8B +#define B2056_RX_STATUS_LNAG_GAIN 0x8C +#define B2056_RX_STATUS_MIXTIA_GAIN 0x8D +#define B2056_RX_STATUS_RXLPF_GAIN 0x8E +#define B2056_RX_STATUS_VGA_BUF_GAIN 0x8F +#define B2056_RX_STATUS_RXLPF_Q 0x90 +#define B2056_RX_STATUS_RXLPF_BUF_BW 0x91 +#define B2056_RX_STATUS_RXLPF_VGA_HPC 0x92 +#define B2056_RX_STATUS_RXLPF_RC 0x93 +#define B2056_RX_STATUS_HPC_RC 0x94 + +#define B2056_LNA1_A_PU 0x01 +#define B2056_LNA2_A_PU 0x02 +#define B2056_LNA1_G_PU 0x01 +#define B2056_LNA2_G_PU 0x02 +#define B2056_MIXA_PU_I 0x01 +#define B2056_MIXA_PU_Q 0x02 +#define B2056_MIXA_PU_GM 0x10 +#define B2056_MIXG_PU_I 0x01 +#define B2056_MIXG_PU_Q 0x02 +#define B2056_MIXG_PU_GM 0x10 +#define B2056_TIA_PU 0x01 +#define B2056_BB_LPF_PU 0x20 +#define B2056_W1_PU 0x02 +#define B2056_W2_PU 0x04 +#define B2056_NB_PU 0x08 +#define B2056_RSSI_W1_SEL 0x02 +#define B2056_RSSI_W2_SEL 0x04 +#define B2056_RSSI_NB_SEL 0x08 +#define B2056_VCM_MASK 0x1C +#define B2056_RSSI_VCM_SHIFT 0x02 + +struct b43_nphy_channeltab_entry_rev3 { + /* The channel frequency in MHz */ + u16 freq; + /* Radio register values on channelswitch */ + u8 radio_syn_pll_vcocal1; + u8 radio_syn_pll_vcocal2; + u8 radio_syn_pll_refdiv; + u8 radio_syn_pll_mmd2; + u8 radio_syn_pll_mmd1; + u8 radio_syn_pll_loopfilter1; + u8 radio_syn_pll_loopfilter2; + u8 radio_syn_pll_loopfilter3; + u8 radio_syn_pll_loopfilter4; + u8 radio_syn_pll_loopfilter5; + u8 radio_syn_reserved_addr27; + u8 radio_syn_reserved_addr28; + u8 radio_syn_reserved_addr29; + u8 radio_syn_logen_vcobuf1; + u8 radio_syn_logen_mixer2; + u8 radio_syn_logen_buf3; + u8 radio_syn_logen_buf4; + u8 radio_rx0_lnaa_tune; + u8 radio_rx0_lnag_tune; + u8 radio_tx0_intpaa_boost_tune; + u8 radio_tx0_intpag_boost_tune; + u8 radio_tx0_pada_boost_tune; + u8 radio_tx0_padg_boost_tune; + u8 radio_tx0_pgaa_boost_tune; + u8 radio_tx0_pgag_boost_tune; + u8 radio_tx0_mixa_boost_tune; + u8 radio_tx0_mixg_boost_tune; + u8 radio_rx1_lnaa_tune; + u8 radio_rx1_lnag_tune; + u8 radio_tx1_intpaa_boost_tune; + u8 radio_tx1_intpag_boost_tune; + u8 radio_tx1_pada_boost_tune; + u8 radio_tx1_padg_boost_tune; + u8 radio_tx1_pgaa_boost_tune; + u8 radio_tx1_pgag_boost_tune; + u8 radio_tx1_mixa_boost_tune; + u8 radio_tx1_mixg_boost_tune; + /* PHY register values on channelswitch */ + struct b43_phy_n_sfo_cfg phy_regs; +}; + +void b2056_upload_inittabs(struct b43_wldev *dev, + bool ghz5, bool ignore_uploadflag); +void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5); + +/* Get the NPHY Channel Switch Table entry for a channel. + * Returns NULL on failure to find an entry. */ +const struct b43_nphy_channeltab_entry_rev3 * +b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq); + +#endif /* B43_RADIO_2056_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/radio_2057.c b/drivers/net/wireless/broadcom/b43/radio_2057.c new file mode 100644 index 000000000000..ff1e026a61a1 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/radio_2057.c @@ -0,0 +1,637 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11n 2057 radio device data tables + + Copyright (c) 2010 RafaÅ‚ MiÅ‚ecki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "radio_2057.h" +#include "phy_common.h" + +static u16 r2057_rev4_init[][2] = { + { 0x0E, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, + { 0x35, 0x26 }, { 0x3C, 0xff }, { 0x3D, 0xff }, { 0x3E, 0xff }, + { 0x3F, 0xff }, { 0x62, 0x33 }, { 0x8A, 0xf0 }, { 0x8B, 0x10 }, + { 0x8C, 0xf0 }, { 0x91, 0x3f }, { 0x92, 0x36 }, { 0xA4, 0x8c }, + { 0xA8, 0x55 }, { 0xAF, 0x01 }, { 0x10F, 0xf0 }, { 0x110, 0x10 }, + { 0x111, 0xf0 }, { 0x116, 0x3f }, { 0x117, 0x36 }, { 0x129, 0x8c }, + { 0x12D, 0x55 }, { 0x134, 0x01 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, + { 0x160, 0x00 }, { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, + { 0x169, 0x02 }, { 0x16A, 0x00 }, { 0x16B, 0x00 }, { 0x16C, 0x00 }, + { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, + { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, +}; + +static u16 r2057_rev5_init[][2] = { + { 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 }, + { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, + { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, + { 0x64, 0x0f }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, + { 0xA1, 0x20 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, { 0xE1, 0x20 }, + { 0xE8, 0x0f }, { 0xE9, 0x0f }, { 0x106, 0x01 }, { 0x116, 0x3f }, + { 0x117, 0x36 }, { 0x126, 0x20 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, + { 0x160, 0x00 }, { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, + { 0x16A, 0x00 }, { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, + { 0x1A5, 0x00 }, { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, + { 0x1AC, 0x00 }, { 0x1B7, 0x0c }, { 0x1C1, 0x01 }, { 0x1C2, 0x80 }, +}; + +static u16 r2057_rev5a_init[][2] = { + { 0x00, 0x15 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 }, + { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, + { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, + { 0x64, 0x0f }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, + { 0xC9, 0x01 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, { 0xE1, 0x20 }, + { 0xE8, 0x0f }, { 0xE9, 0x0f }, { 0x106, 0x01 }, { 0x116, 0x3f }, + { 0x117, 0x36 }, { 0x126, 0x20 }, { 0x14E, 0x01 }, { 0x15E, 0x00 }, + { 0x15F, 0x00 }, { 0x160, 0x00 }, { 0x161, 0x00 }, { 0x162, 0x00 }, + { 0x163, 0x00 }, { 0x16A, 0x00 }, { 0x16B, 0x00 }, { 0x16C, 0x00 }, + { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, + { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, { 0x1B7, 0x0c }, { 0x1C1, 0x01 }, + { 0x1C2, 0x80 }, +}; + +static u16 r2057_rev7_init[][2] = { + { 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 }, + { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, + { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x13 }, + { 0x66, 0xee }, { 0x6E, 0x58 }, { 0x75, 0x13 }, { 0x7B, 0x13 }, + { 0x7C, 0x14 }, { 0x7D, 0xee }, { 0x81, 0x01 }, { 0x91, 0x3f }, + { 0x92, 0x36 }, { 0xA1, 0x20 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, + { 0xE1, 0x20 }, { 0xE8, 0x0f }, { 0xE9, 0x13 }, { 0xEB, 0xee }, + { 0xF3, 0x58 }, { 0xFA, 0x13 }, { 0x100, 0x13 }, { 0x101, 0x14 }, + { 0x102, 0xee }, { 0x106, 0x01 }, { 0x116, 0x3f }, { 0x117, 0x36 }, + { 0x126, 0x20 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, { 0x160, 0x00 }, + { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, { 0x16A, 0x00 }, + { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, + { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, + { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, +}; + +/* TODO: Which devices should use it? +static u16 r2057_rev8_init[][2] = { + { 0x00, 0x08 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 }, + { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 }, + { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f }, + { 0x6E, 0x58 }, { 0x75, 0x13 }, { 0x7B, 0x13 }, { 0x7C, 0x0f }, + { 0x7D, 0xee }, { 0x81, 0x01 }, { 0x91, 0x3f }, { 0x92, 0x36 }, + { 0xA1, 0x20 }, { 0xC9, 0x01 }, { 0xD6, 0x70 }, { 0xDE, 0x88 }, + { 0xE1, 0x20 }, { 0xE8, 0x0f }, { 0xE9, 0x0f }, { 0xF3, 0x58 }, + { 0xFA, 0x13 }, { 0x100, 0x13 }, { 0x101, 0x0f }, { 0x102, 0xee }, + { 0x106, 0x01 }, { 0x116, 0x3f }, { 0x117, 0x36 }, { 0x126, 0x20 }, + { 0x14E, 0x01 }, { 0x15E, 0x00 }, { 0x15F, 0x00 }, { 0x160, 0x00 }, + { 0x161, 0x00 }, { 0x162, 0x00 }, { 0x163, 0x00 }, { 0x16A, 0x00 }, + { 0x16B, 0x00 }, { 0x16C, 0x00 }, { 0x1A4, 0x00 }, { 0x1A5, 0x00 }, + { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 }, + { 0x1B7, 0x05 }, { 0x1C2, 0xa0 }, +}; +*/ + +/* Extracted from MMIO dump of 6.30.223.141 */ +static u16 r2057_rev9_init[][2] = { + { 0x27, 0x1f }, { 0x28, 0x0a }, { 0x29, 0x2f }, { 0x42, 0x1f }, + { 0x48, 0x3f }, { 0x5c, 0x41 }, { 0x63, 0x14 }, { 0x64, 0x12 }, + { 0x66, 0xff }, { 0x74, 0xa3 }, { 0x7b, 0x14 }, { 0x7c, 0x14 }, + { 0x7d, 0xee }, { 0x86, 0xc0 }, { 0xc4, 0x10 }, { 0xc9, 0x01 }, + { 0xe1, 0x41 }, { 0xe8, 0x14 }, { 0xe9, 0x12 }, { 0xeb, 0xff }, + { 0xf5, 0x0a }, { 0xf8, 0x09 }, { 0xf9, 0xa3 }, { 0x100, 0x14 }, + { 0x101, 0x10 }, { 0x102, 0xee }, { 0x10b, 0xc0 }, { 0x149, 0x10 }, + { 0x14e, 0x01 }, { 0x1b7, 0x05 }, { 0x1c2, 0xa0 }, +}; + +/* Extracted from MMIO dump of 6.30.223.248 */ +static u16 r2057_rev14_init[][2] = { + { 0x011, 0xfc }, { 0x030, 0x24 }, { 0x040, 0x1c }, { 0x082, 0x08 }, + { 0x0b4, 0x44 }, { 0x0c8, 0x01 }, { 0x0c9, 0x01 }, { 0x107, 0x08 }, + { 0x14d, 0x01 }, { 0x14e, 0x01 }, { 0x1af, 0x40 }, { 0x1b0, 0x40 }, + { 0x1cc, 0x01 }, { 0x1cf, 0x10 }, { 0x1d0, 0x0f }, { 0x1d3, 0x10 }, + { 0x1d4, 0x0f }, +}; + +#define RADIOREGS7(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ + r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ + r20, r21, r22, r23, r24, r25, r26, r27) \ + .radio_vcocal_countval0 = r00, \ + .radio_vcocal_countval1 = r01, \ + .radio_rfpll_refmaster_sparextalsize = r02, \ + .radio_rfpll_loopfilter_r1 = r03, \ + .radio_rfpll_loopfilter_c2 = r04, \ + .radio_rfpll_loopfilter_c1 = r05, \ + .radio_cp_kpd_idac = r06, \ + .radio_rfpll_mmd0 = r07, \ + .radio_rfpll_mmd1 = r08, \ + .radio_vcobuf_tune = r09, \ + .radio_logen_mx2g_tune = r10, \ + .radio_logen_mx5g_tune = r11, \ + .radio_logen_indbuf2g_tune = r12, \ + .radio_logen_indbuf5g_tune = r13, \ + .radio_txmix2g_tune_boost_pu_core0 = r14, \ + .radio_pad2g_tune_pus_core0 = r15, \ + .radio_pga_boost_tune_core0 = r16, \ + .radio_txmix5g_boost_tune_core0 = r17, \ + .radio_pad5g_tune_misc_pus_core0 = r18, \ + .radio_lna2g_tune_core0 = r19, \ + .radio_lna5g_tune_core0 = r20, \ + .radio_txmix2g_tune_boost_pu_core1 = r21, \ + .radio_pad2g_tune_pus_core1 = r22, \ + .radio_pga_boost_tune_core1 = r23, \ + .radio_txmix5g_boost_tune_core1 = r24, \ + .radio_pad5g_tune_misc_pus_core1 = r25, \ + .radio_lna2g_tune_core1 = r26, \ + .radio_lna5g_tune_core1 = r27 + +#define RADIOREGS7_2G(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ + r10, r11, r12, r13, r14, r15, r16, r17) \ + .radio_vcocal_countval0 = r00, \ + .radio_vcocal_countval1 = r01, \ + .radio_rfpll_refmaster_sparextalsize = r02, \ + .radio_rfpll_loopfilter_r1 = r03, \ + .radio_rfpll_loopfilter_c2 = r04, \ + .radio_rfpll_loopfilter_c1 = r05, \ + .radio_cp_kpd_idac = r06, \ + .radio_rfpll_mmd0 = r07, \ + .radio_rfpll_mmd1 = r08, \ + .radio_vcobuf_tune = r09, \ + .radio_logen_mx2g_tune = r10, \ + .radio_logen_indbuf2g_tune = r11, \ + .radio_txmix2g_tune_boost_pu_core0 = r12, \ + .radio_pad2g_tune_pus_core0 = r13, \ + .radio_lna2g_tune_core0 = r14, \ + .radio_txmix2g_tune_boost_pu_core1 = r15, \ + .radio_pad2g_tune_pus_core1 = r16, \ + .radio_lna2g_tune_core1 = r17 + +#define PHYREGS(r0, r1, r2, r3, r4, r5) \ + .phy_regs.phy_bw1a = r0, \ + .phy_regs.phy_bw2 = r1, \ + .phy_regs.phy_bw3 = r2, \ + .phy_regs.phy_bw4 = r3, \ + .phy_regs.phy_bw5 = r4, \ + .phy_regs.phy_bw6 = r5 + +/* Copied from brcmsmac (5.75.11): chan_info_nphyrev8_2057_rev5 */ +static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev8_radio_rev5[] = { + { + .freq = 2412, + RADIOREGS7_2G(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, + 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, + 0x03, 0xff), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { + .freq = 2417, + RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, + 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, + 0x03, 0xff), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { + .freq = 2422, + RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, + 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xef, 0x61, + 0x03, 0xef), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { + .freq = 2427, + RADIOREGS7_2G(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, + 0x09, 0x0c, 0x08, 0x0e, 0x61, 0x03, 0xdf, 0x61, + 0x03, 0xdf), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { + .freq = 2432, + RADIOREGS7_2G(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, + 0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xcf, 0x61, + 0x03, 0xcf), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { + .freq = 2437, + RADIOREGS7_2G(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, + 0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xbf, 0x61, + 0x03, 0xbf), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { + .freq = 2442, + RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, + 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0xaf, 0x61, + 0x03, 0xaf), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { + .freq = 2447, + RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, + 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x9f, 0x61, + 0x03, 0x9f), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { + .freq = 2452, + RADIOREGS7_2G(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, + 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x8f, 0x61, + 0x03, 0x8f), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { + .freq = 2457, + RADIOREGS7_2G(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, + 0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x7f, 0x61, + 0x03, 0x7f), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { + .freq = 2462, + RADIOREGS7_2G(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, + 0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x6f, 0x61, + 0x03, 0x6f), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { + .freq = 2467, + RADIOREGS7_2G(0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, + 0x09, 0x0b, 0x06, 0x0c, 0x61, 0x03, 0x5f, 0x61, + 0x03, 0x5f), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { + .freq = 2472, + RADIOREGS7_2G(0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, + 0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x4f, 0x61, + 0x03, 0x4f), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { + .freq = 2484, + RADIOREGS7_2G(0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, + 0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x3f, 0x61, + 0x03, 0x3f), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + } +}; + +/* Extracted from MMIO dump of 6.30.223.248 */ +static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev17_radio_rev14[] = { + { + .freq = 2412, + RADIOREGS7_2G(0x48, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x6c, + 0x09, 0x0d, 0x09, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { + .freq = 2417, + RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x71, + 0x09, 0x0d, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { + .freq = 2422, + RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x76, + 0x09, 0x0d, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { + .freq = 2427, + RADIOREGS7_2G(0x52, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x7b, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { + .freq = 2432, + RADIOREGS7_2G(0x55, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x80, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { + .freq = 2437, + RADIOREGS7_2G(0x58, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x85, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { + .freq = 2442, + RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x8a, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21, + 0x43, 0xff), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { + .freq = 2447, + RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x8f, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21, + 0x43, 0xff), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { + .freq = 2452, + RADIOREGS7_2G(0x62, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x94, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21, + 0x43, 0xff), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { + .freq = 2457, + RADIOREGS7_2G(0x66, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x99, + 0x09, 0x0b, 0x07, 0x03, 0x21, 0x43, 0xff, 0x21, + 0x43, 0xff), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { + .freq = 2462, + RADIOREGS7_2G(0x69, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x9e, + 0x09, 0x0b, 0x07, 0x03, 0x01, 0x43, 0xff, 0x01, + 0x43, 0xff), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, +}; + +/* Extracted from MMIO dump of 6.30.223.141 */ +static const struct b43_nphy_chantabent_rev7 b43_nphy_chantab_phy_rev16_radio_rev9[] = { + { + .freq = 2412, + RADIOREGS7(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, + 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { + .freq = 2417, + RADIOREGS7(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, + 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { + .freq = 2422, + RADIOREGS7(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, + 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { + .freq = 2427, + RADIOREGS7(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, + 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { + .freq = 2432, + RADIOREGS7(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, + 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { + .freq = 2437, + RADIOREGS7(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, + 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { + .freq = 2442, + RADIOREGS7(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, + 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { + .freq = 2447, + RADIOREGS7(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, + 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { + .freq = 2452, + RADIOREGS7(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, + 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { + .freq = 2457, + RADIOREGS7(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, + 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { + .freq = 2462, + RADIOREGS7(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, + 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { + .freq = 5180, + RADIOREGS7(0xbe, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x06, + 0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00, + 0x9f, 0x2f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x4f, + 0x3a, 0x83, 0x00, 0xfc), + PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), + }, + { + .freq = 5200, + RADIOREGS7(0xc5, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x08, + 0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00, + 0x7f, 0x2f, 0x83, 0x00, 0xf8, 0x00, 0x00, 0x4c, + 0x4a, 0x83, 0x00, 0xf8), + PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), + }, + { + .freq = 5220, + RADIOREGS7(0xcc, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0a, + 0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00, + 0x6d, 0x3d, 0x83, 0x00, 0xf8, 0x00, 0x00, 0x2d, + 0x2a, 0x73, 0x00, 0xf8), + PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), + }, + { + .freq = 5240, + RADIOREGS7(0xd2, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0c, + 0x02, 0x0d, 0x00, 0x0d, 0x00, 0x8d, 0x00, 0x00, + 0x4d, 0x1c, 0x73, 0x00, 0xf8, 0x00, 0x00, 0x4d, + 0x2b, 0x73, 0x00, 0xf8), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { + .freq = 5745, + RADIOREGS7(0x7b, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x7d, + 0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, + 0x08, 0x03, 0x03, 0x00, 0x30, 0x00, 0x00, 0x06, + 0x02, 0x03, 0x00, 0x30), + PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), + }, + { + .freq = 5765, + RADIOREGS7(0x81, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x81, + 0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, + 0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x02, 0x03, 0x00, 0x00), + PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), + }, + { + .freq = 5785, + RADIOREGS7(0x88, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x85, + 0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, + 0x08, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x21, 0x03, 0x00, 0x00), + PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), + }, + { + .freq = 5805, + RADIOREGS7(0x8f, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x89, + 0x04, 0x07, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, + 0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x00, 0x00), + PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), + }, + { + .freq = 5825, + RADIOREGS7(0x95, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x8d, + 0x04, 0x07, 0x00, 0x05, 0x00, 0x03, 0x00, 0x00, + 0x05, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x00, 0x00), + PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), + }, +}; + +void r2057_upload_inittabs(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + u16 *table = NULL; + u16 size, i; + + switch (phy->rev) { + case 7: + table = r2057_rev4_init[0]; + size = ARRAY_SIZE(r2057_rev4_init); + break; + case 8: + if (phy->radio_rev == 5) { + table = r2057_rev5_init[0]; + size = ARRAY_SIZE(r2057_rev5_init); + } else if (phy->radio_rev == 7) { + table = r2057_rev7_init[0]; + size = ARRAY_SIZE(r2057_rev7_init); + } + break; + case 9: + if (phy->radio_rev == 5) { + table = r2057_rev5a_init[0]; + size = ARRAY_SIZE(r2057_rev5a_init); + } + break; + case 16: + if (phy->radio_rev == 9) { + table = r2057_rev9_init[0]; + size = ARRAY_SIZE(r2057_rev9_init); + } + break; + case 17: + if (phy->radio_rev == 14) { + table = r2057_rev14_init[0]; + size = ARRAY_SIZE(r2057_rev14_init); + } + break; + } + + B43_WARN_ON(!table); + + if (table) { + for (i = 0; i < size; i++, table += 2) + b43_radio_write(dev, table[0], table[1]); + } +} + +void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, + const struct b43_nphy_chantabent_rev7 **tabent_r7, + const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g) +{ + struct b43_phy *phy = &dev->phy; + const struct b43_nphy_chantabent_rev7 *e_r7 = NULL; + const struct b43_nphy_chantabent_rev7_2g *e_r7_2g = NULL; + unsigned int len, i; + + *tabent_r7 = NULL; + *tabent_r7_2g = NULL; + + switch (phy->rev) { + case 8: + if (phy->radio_rev == 5) { + e_r7_2g = b43_nphy_chantab_phy_rev8_radio_rev5; + len = ARRAY_SIZE(b43_nphy_chantab_phy_rev8_radio_rev5); + } + break; + case 16: + if (phy->radio_rev == 9) { + e_r7 = b43_nphy_chantab_phy_rev16_radio_rev9; + len = ARRAY_SIZE(b43_nphy_chantab_phy_rev16_radio_rev9); + } + break; + case 17: + if (phy->radio_rev == 14) { + e_r7_2g = b43_nphy_chantab_phy_rev17_radio_rev14; + len = ARRAY_SIZE(b43_nphy_chantab_phy_rev17_radio_rev14); + } + break; + default: + break; + } + + if (e_r7) { + for (i = 0; i < len; i++, e_r7++) { + if (e_r7->freq == freq) { + *tabent_r7 = e_r7; + return; + } + } + } else if (e_r7_2g) { + for (i = 0; i < len; i++, e_r7_2g++) { + if (e_r7_2g->freq == freq) { + *tabent_r7_2g = e_r7_2g; + return; + } + } + } else { + B43_WARN_ON(1); + } +} diff --git a/drivers/net/wireless/broadcom/b43/radio_2057.h b/drivers/net/wireless/broadcom/b43/radio_2057.h new file mode 100644 index 000000000000..220d080238ff --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/radio_2057.h @@ -0,0 +1,506 @@ +#ifndef B43_RADIO_2057_H_ +#define B43_RADIO_2057_H_ + +#include + +#include "tables_nphy.h" + +#define R2057_DACBUF_VINCM_CORE0 0x000 +#define R2057_IDCODE 0x001 +#define R2057_RCCAL_MASTER 0x002 +#define R2057_RCCAL_CAP_SIZE 0x003 +#define R2057_RCAL_CONFIG 0x004 +#define R2057_GPAIO_CONFIG 0x005 +#define R2057_GPAIO_SEL1 0x006 +#define R2057_GPAIO_SEL0 0x007 +#define R2057_CLPO_CONFIG 0x008 +#define R2057_BANDGAP_CONFIG 0x009 +#define R2057_BANDGAP_RCAL_TRIM 0x00a +#define R2057_AFEREG_CONFIG 0x00b +#define R2057_TEMPSENSE_CONFIG 0x00c +#define R2057_XTAL_CONFIG1 0x00d +#define R2057_XTAL_ICORE_SIZE 0x00e +#define R2057_XTAL_BUF_SIZE 0x00f +#define R2057_XTAL_PULLCAP_SIZE 0x010 +#define R2057_RFPLL_MASTER 0x011 +#define R2057_VCOMONITOR_VTH_L 0x012 +#define R2057_VCOMONITOR_VTH_H 0x013 +#define R2057_VCOCAL_BIASRESET_RFPLLREG_VOUT 0x014 +#define R2057_VCO_VARCSIZE_IDAC 0x015 +#define R2057_VCOCAL_COUNTVAL0 0x016 +#define R2057_VCOCAL_COUNTVAL1 0x017 +#define R2057_VCOCAL_INTCLK_COUNT 0x018 +#define R2057_VCOCAL_MASTER 0x019 +#define R2057_VCOCAL_NUMCAPCHANGE 0x01a +#define R2057_VCOCAL_WINSIZE 0x01b +#define R2057_VCOCAL_DELAY_AFTER_REFRESH 0x01c +#define R2057_VCOCAL_DELAY_AFTER_CLOSELOOP 0x01d +#define R2057_VCOCAL_DELAY_AFTER_OPENLOOP 0x01e +#define R2057_VCOCAL_DELAY_BEFORE_OPENLOOP 0x01f +#define R2057_VCO_FORCECAPEN_FORCECAP1 0x020 +#define R2057_VCO_FORCECAP0 0x021 +#define R2057_RFPLL_REFMASTER_SPAREXTALSIZE 0x022 +#define R2057_RFPLL_PFD_RESET_PW 0x023 +#define R2057_RFPLL_LOOPFILTER_R2 0x024 +#define R2057_RFPLL_LOOPFILTER_R1 0x025 +#define R2057_RFPLL_LOOPFILTER_C3 0x026 +#define R2057_RFPLL_LOOPFILTER_C2 0x027 +#define R2057_RFPLL_LOOPFILTER_C1 0x028 +#define R2057_CP_KPD_IDAC 0x029 +#define R2057_RFPLL_IDACS 0x02a +#define R2057_RFPLL_MISC_EN 0x02b +#define R2057_RFPLL_MMD0 0x02c +#define R2057_RFPLL_MMD1 0x02d +#define R2057_RFPLL_MISC_CAL_RESETN 0x02e +#define R2057_JTAGXTAL_SIZE_CPBIAS_FILTRES 0x02f +#define R2057_VCO_ALCREF_BBPLLXTAL_SIZE 0x030 +#define R2057_VCOCAL_READCAP0 0x031 +#define R2057_VCOCAL_READCAP1 0x032 +#define R2057_VCOCAL_STATUS 0x033 +#define R2057_LOGEN_PUS 0x034 +#define R2057_LOGEN_PTAT_RESETS 0x035 +#define R2057_VCOBUF_IDACS 0x036 +#define R2057_VCOBUF_TUNE 0x037 +#define R2057_CMOSBUF_TX2GQ_IDACS 0x038 +#define R2057_CMOSBUF_TX2GI_IDACS 0x039 +#define R2057_CMOSBUF_TX5GQ_IDACS 0x03a +#define R2057_CMOSBUF_TX5GI_IDACS 0x03b +#define R2057_CMOSBUF_RX2GQ_IDACS 0x03c +#define R2057_CMOSBUF_RX2GI_IDACS 0x03d +#define R2057_CMOSBUF_RX5GQ_IDACS 0x03e +#define R2057_CMOSBUF_RX5GI_IDACS 0x03f +#define R2057_LOGEN_MX2G_IDACS 0x040 +#define R2057_LOGEN_MX2G_TUNE 0x041 +#define R2057_LOGEN_MX5G_IDACS 0x042 +#define R2057_LOGEN_MX5G_TUNE 0x043 +#define R2057_LOGEN_MX5G_RCCR 0x044 +#define R2057_LOGEN_INDBUF2G_IDAC 0x045 +#define R2057_LOGEN_INDBUF2G_IBOOST 0x046 +#define R2057_LOGEN_INDBUF2G_TUNE 0x047 +#define R2057_LOGEN_INDBUF5G_IDAC 0x048 +#define R2057_LOGEN_INDBUF5G_IBOOST 0x049 +#define R2057_LOGEN_INDBUF5G_TUNE 0x04a +#define R2057_CMOSBUF_TX_RCCR 0x04b +#define R2057_CMOSBUF_RX_RCCR 0x04c +#define R2057_LOGEN_SEL_PKDET 0x04d +#define R2057_CMOSBUF_SHAREIQ_PTAT 0x04e + +/* MISC core 0 */ +#define R2057_RXTXBIAS_CONFIG_CORE0 0x04f +#define R2057_TXGM_TXRF_PUS_CORE0 0x050 +#define R2057_TXGM_IDAC_BLEED_CORE0 0x051 +#define R2057_TXGM_GAIN_CORE0 0x056 +#define R2057_TXGM2G_PKDET_PUS_CORE0 0x057 +#define R2057_PAD2G_PTATS_CORE0 0x058 +#define R2057_PAD2G_IDACS_CORE0 0x059 +#define R2057_PAD2G_BOOST_PU_CORE0 0x05a +#define R2057_PAD2G_CASCV_GAIN_CORE0 0x05b +#define R2057_TXMIX2G_TUNE_BOOST_PU_CORE0 0x05c +#define R2057_TXMIX2G_LODC_CORE0 0x05d +#define R2057_PAD2G_TUNE_PUS_CORE0 0x05e +#define R2057_IPA2G_GAIN_CORE0 0x05f +#define R2057_TSSI2G_SPARE1_CORE0 0x060 +#define R2057_TSSI2G_SPARE2_CORE0 0x061 +#define R2057_IPA2G_TUNEV_CASCV_PTAT_CORE0 0x062 +#define R2057_IPA2G_IMAIN_CORE0 0x063 +#define R2057_IPA2G_CASCONV_CORE0 0x064 +#define R2057_IPA2G_CASCOFFV_CORE0 0x065 +#define R2057_IPA2G_BIAS_FILTER_CORE0 0x066 +#define R2057_TX5G_PKDET_CORE0 0x069 +#define R2057_PGA_PTAT_TXGM5G_PU_CORE0 0x06a +#define R2057_PAD5G_PTATS1_CORE0 0x06b +#define R2057_PAD5G_CLASS_PTATS2_CORE0 0x06c +#define R2057_PGA_BOOSTPTAT_IMAIN_CORE0 0x06d +#define R2057_PAD5G_CASCV_IMAIN_CORE0 0x06e +#define R2057_TXMIX5G_IBOOST_PAD_IAUX_CORE0 0x06f +#define R2057_PGA_BOOST_TUNE_CORE0 0x070 +#define R2057_PGA_GAIN_CORE0 0x071 +#define R2057_PAD5G_CASCOFFV_GAIN_PUS_CORE0 0x072 +#define R2057_TXMIX5G_BOOST_TUNE_CORE0 0x073 +#define R2057_PAD5G_TUNE_MISC_PUS_CORE0 0x074 +#define R2057_IPA5G_IAUX_CORE0 0x075 +#define R2057_IPA5G_GAIN_CORE0 0x076 +#define R2057_TSSI5G_SPARE1_CORE0 0x077 +#define R2057_TSSI5G_SPARE2_CORE0 0x078 +#define R2057_IPA5G_CASCOFFV_PU_CORE0 0x079 +#define R2057_IPA5G_PTAT_CORE0 0x07a +#define R2057_IPA5G_IMAIN_CORE0 0x07b +#define R2057_IPA5G_CASCONV_CORE0 0x07c +#define R2057_IPA5G_BIAS_FILTER_CORE0 0x07d +#define R2057_PAD_BIAS_FILTER_BWS_CORE0 0x080 +#define R2057_TR2G_CONFIG1_CORE0_NU 0x081 +#define R2057_TR2G_CONFIG2_CORE0_NU 0x082 +#define R2057_LNA5G_RFEN_CORE0 0x083 +#define R2057_TR5G_CONFIG2_CORE0_NU 0x084 +#define R2057_RXRFBIAS_IBOOST_PU_CORE0 0x085 +#define R2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE0 0x086 +#define R2057_RXGM_CMFBITAIL_AUXPTAT_CORE0 0x087 +#define R2057_RXMIX_ICORE_RXGM_IAUX_CORE0 0x088 +#define R2057_RXMIX_CMFBITAIL_PU_CORE0 0x089 +#define R2057_LNA2_IMAIN_PTAT_PU_CORE0 0x08a +#define R2057_LNA2_IAUX_PTAT_CORE0 0x08b +#define R2057_LNA1_IMAIN_PTAT_PU_CORE0 0x08c +#define R2057_LNA15G_INPUT_MATCH_TUNE_CORE0 0x08d +#define R2057_RXRFBIAS_BANDSEL_CORE0 0x08e +#define R2057_TIA_CONFIG_CORE0 0x08f +#define R2057_TIA_IQGAIN_CORE0 0x090 +#define R2057_TIA_IBIAS2_CORE0 0x091 +#define R2057_TIA_IBIAS1_CORE0 0x092 +#define R2057_TIA_SPARE_Q_CORE0 0x093 +#define R2057_TIA_SPARE_I_CORE0 0x094 +#define R2057_RXMIX2G_PUS_CORE0 0x095 +#define R2057_RXMIX2G_VCMREFS_CORE0 0x096 +#define R2057_RXMIX2G_LODC_QI_CORE0 0x097 +#define R2057_W12G_BW_LNA2G_PUS_CORE0 0x098 +#define R2057_LNA2G_GAIN_CORE0 0x099 +#define R2057_LNA2G_TUNE_CORE0 0x09a +#define R2057_RXMIX5G_PUS_CORE0 0x09b +#define R2057_RXMIX5G_VCMREFS_CORE0 0x09c +#define R2057_RXMIX5G_LODC_QI_CORE0 0x09d +#define R2057_W15G_BW_LNA5G_PUS_CORE0 0x09e +#define R2057_LNA5G_GAIN_CORE0 0x09f +#define R2057_LNA5G_TUNE_CORE0 0x0a0 +#define R2057_LPFSEL_TXRX_RXBB_PUS_CORE0 0x0a1 +#define R2057_RXBB_BIAS_MASTER_CORE0 0x0a2 +#define R2057_RXBB_VGABUF_IDACS_CORE0 0x0a3 +#define R2057_LPF_VCMREF_TXBUF_VCMREF_CORE0 0x0a4 +#define R2057_TXBUF_VINCM_CORE0 0x0a5 +#define R2057_TXBUF_IDACS_CORE0 0x0a6 +#define R2057_LPF_RESP_RXBUF_BW_CORE0 0x0a7 +#define R2057_RXBB_CC_CORE0 0x0a8 +#define R2057_RXBB_SPARE3_CORE0 0x0a9 +#define R2057_RXBB_RCCAL_HPC_CORE0 0x0aa +#define R2057_LPF_IDACS_CORE0 0x0ab +#define R2057_LPFBYP_DCLOOP_BYP_IDAC_CORE0 0x0ac +#define R2057_TXBUF_GAIN_CORE0 0x0ad +#define R2057_AFELOOPBACK_AACI_RESP_CORE0 0x0ae +#define R2057_RXBUF_DEGEN_CORE0 0x0af +#define R2057_RXBB_SPARE2_CORE0 0x0b0 +#define R2057_RXBB_SPARE1_CORE0 0x0b1 +#define R2057_RSSI_MASTER_CORE0 0x0b2 +#define R2057_W2_MASTER_CORE0 0x0b3 +#define R2057_NB_MASTER_CORE0 0x0b4 +#define R2057_W2_IDACS0_Q_CORE0 0x0b5 +#define R2057_W2_IDACS1_Q_CORE0 0x0b6 +#define R2057_W2_IDACS0_I_CORE0 0x0b7 +#define R2057_W2_IDACS1_I_CORE0 0x0b8 +#define R2057_RSSI_GPAIOSEL_W1_IDACS_CORE0 0x0b9 +#define R2057_NB_IDACS_Q_CORE0 0x0ba +#define R2057_NB_IDACS_I_CORE0 0x0bb +#define R2057_BACKUP4_CORE0 0x0c1 +#define R2057_BACKUP3_CORE0 0x0c2 +#define R2057_BACKUP2_CORE0 0x0c3 +#define R2057_BACKUP1_CORE0 0x0c4 +#define R2057_SPARE16_CORE0 0x0c5 +#define R2057_SPARE15_CORE0 0x0c6 +#define R2057_SPARE14_CORE0 0x0c7 +#define R2057_SPARE13_CORE0 0x0c8 +#define R2057_SPARE12_CORE0 0x0c9 +#define R2057_SPARE11_CORE0 0x0ca +#define R2057_TX2G_BIAS_RESETS_CORE0 0x0cb +#define R2057_TX5G_BIAS_RESETS_CORE0 0x0cc +#define R2057_IQTEST_SEL_PU 0x0cd +#define R2057_XTAL_CONFIG2 0x0ce +#define R2057_BUFS_MISC_LPFBW_CORE0 0x0cf +#define R2057_TXLPF_RCCAL_CORE0 0x0d0 +#define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE0 0x0d1 +#define R2057_LPF_GAIN_CORE0 0x0d2 +#define R2057_DACBUF_IDACS_BW_CORE0 0x0d3 + +/* MISC core 1 */ +#define R2057_RXTXBIAS_CONFIG_CORE1 0x0d4 +#define R2057_TXGM_TXRF_PUS_CORE1 0x0d5 +#define R2057_TXGM_IDAC_BLEED_CORE1 0x0d6 +#define R2057_TXGM_GAIN_CORE1 0x0db +#define R2057_TXGM2G_PKDET_PUS_CORE1 0x0dc +#define R2057_PAD2G_PTATS_CORE1 0x0dd +#define R2057_PAD2G_IDACS_CORE1 0x0de +#define R2057_PAD2G_BOOST_PU_CORE1 0x0df +#define R2057_PAD2G_CASCV_GAIN_CORE1 0x0e0 +#define R2057_TXMIX2G_TUNE_BOOST_PU_CORE1 0x0e1 +#define R2057_TXMIX2G_LODC_CORE1 0x0e2 +#define R2057_PAD2G_TUNE_PUS_CORE1 0x0e3 +#define R2057_IPA2G_GAIN_CORE1 0x0e4 +#define R2057_TSSI2G_SPARE1_CORE1 0x0e5 +#define R2057_TSSI2G_SPARE2_CORE1 0x0e6 +#define R2057_IPA2G_TUNEV_CASCV_PTAT_CORE1 0x0e7 +#define R2057_IPA2G_IMAIN_CORE1 0x0e8 +#define R2057_IPA2G_CASCONV_CORE1 0x0e9 +#define R2057_IPA2G_CASCOFFV_CORE1 0x0ea +#define R2057_IPA2G_BIAS_FILTER_CORE1 0x0eb +#define R2057_TX5G_PKDET_CORE1 0x0ee +#define R2057_PGA_PTAT_TXGM5G_PU_CORE1 0x0ef +#define R2057_PAD5G_PTATS1_CORE1 0x0f0 +#define R2057_PAD5G_CLASS_PTATS2_CORE1 0x0f1 +#define R2057_PGA_BOOSTPTAT_IMAIN_CORE1 0x0f2 +#define R2057_PAD5G_CASCV_IMAIN_CORE1 0x0f3 +#define R2057_TXMIX5G_IBOOST_PAD_IAUX_CORE1 0x0f4 +#define R2057_PGA_BOOST_TUNE_CORE1 0x0f5 +#define R2057_PGA_GAIN_CORE1 0x0f6 +#define R2057_PAD5G_CASCOFFV_GAIN_PUS_CORE1 0x0f7 +#define R2057_TXMIX5G_BOOST_TUNE_CORE1 0x0f8 +#define R2057_PAD5G_TUNE_MISC_PUS_CORE1 0x0f9 +#define R2057_IPA5G_IAUX_CORE1 0x0fa +#define R2057_IPA5G_GAIN_CORE1 0x0fb +#define R2057_TSSI5G_SPARE1_CORE1 0x0fc +#define R2057_TSSI5G_SPARE2_CORE1 0x0fd +#define R2057_IPA5G_CASCOFFV_PU_CORE1 0x0fe +#define R2057_IPA5G_PTAT_CORE1 0x0ff +#define R2057_IPA5G_IMAIN_CORE1 0x100 +#define R2057_IPA5G_CASCONV_CORE1 0x101 +#define R2057_IPA5G_BIAS_FILTER_CORE1 0x102 +#define R2057_PAD_BIAS_FILTER_BWS_CORE1 0x105 +#define R2057_TR2G_CONFIG1_CORE1_NU 0x106 +#define R2057_TR2G_CONFIG2_CORE1_NU 0x107 +#define R2057_LNA5G_RFEN_CORE1 0x108 +#define R2057_TR5G_CONFIG2_CORE1_NU 0x109 +#define R2057_RXRFBIAS_IBOOST_PU_CORE1 0x10a +#define R2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE1 0x10b +#define R2057_RXGM_CMFBITAIL_AUXPTAT_CORE1 0x10c +#define R2057_RXMIX_ICORE_RXGM_IAUX_CORE1 0x10d +#define R2057_RXMIX_CMFBITAIL_PU_CORE1 0x10e +#define R2057_LNA2_IMAIN_PTAT_PU_CORE1 0x10f +#define R2057_LNA2_IAUX_PTAT_CORE1 0x110 +#define R2057_LNA1_IMAIN_PTAT_PU_CORE1 0x111 +#define R2057_LNA15G_INPUT_MATCH_TUNE_CORE1 0x112 +#define R2057_RXRFBIAS_BANDSEL_CORE1 0x113 +#define R2057_TIA_CONFIG_CORE1 0x114 +#define R2057_TIA_IQGAIN_CORE1 0x115 +#define R2057_TIA_IBIAS2_CORE1 0x116 +#define R2057_TIA_IBIAS1_CORE1 0x117 +#define R2057_TIA_SPARE_Q_CORE1 0x118 +#define R2057_TIA_SPARE_I_CORE1 0x119 +#define R2057_RXMIX2G_PUS_CORE1 0x11a +#define R2057_RXMIX2G_VCMREFS_CORE1 0x11b +#define R2057_RXMIX2G_LODC_QI_CORE1 0x11c +#define R2057_W12G_BW_LNA2G_PUS_CORE1 0x11d +#define R2057_LNA2G_GAIN_CORE1 0x11e +#define R2057_LNA2G_TUNE_CORE1 0x11f +#define R2057_RXMIX5G_PUS_CORE1 0x120 +#define R2057_RXMIX5G_VCMREFS_CORE1 0x121 +#define R2057_RXMIX5G_LODC_QI_CORE1 0x122 +#define R2057_W15G_BW_LNA5G_PUS_CORE1 0x123 +#define R2057_LNA5G_GAIN_CORE1 0x124 +#define R2057_LNA5G_TUNE_CORE1 0x125 +#define R2057_LPFSEL_TXRX_RXBB_PUS_CORE1 0x126 +#define R2057_RXBB_BIAS_MASTER_CORE1 0x127 +#define R2057_RXBB_VGABUF_IDACS_CORE1 0x128 +#define R2057_LPF_VCMREF_TXBUF_VCMREF_CORE1 0x129 +#define R2057_TXBUF_VINCM_CORE1 0x12a +#define R2057_TXBUF_IDACS_CORE1 0x12b +#define R2057_LPF_RESP_RXBUF_BW_CORE1 0x12c +#define R2057_RXBB_CC_CORE1 0x12d +#define R2057_RXBB_SPARE3_CORE1 0x12e +#define R2057_RXBB_RCCAL_HPC_CORE1 0x12f +#define R2057_LPF_IDACS_CORE1 0x130 +#define R2057_LPFBYP_DCLOOP_BYP_IDAC_CORE1 0x131 +#define R2057_TXBUF_GAIN_CORE1 0x132 +#define R2057_AFELOOPBACK_AACI_RESP_CORE1 0x133 +#define R2057_RXBUF_DEGEN_CORE1 0x134 +#define R2057_RXBB_SPARE2_CORE1 0x135 +#define R2057_RXBB_SPARE1_CORE1 0x136 +#define R2057_RSSI_MASTER_CORE1 0x137 +#define R2057_W2_MASTER_CORE1 0x138 +#define R2057_NB_MASTER_CORE1 0x139 +#define R2057_W2_IDACS0_Q_CORE1 0x13a +#define R2057_W2_IDACS1_Q_CORE1 0x13b +#define R2057_W2_IDACS0_I_CORE1 0x13c +#define R2057_W2_IDACS1_I_CORE1 0x13d +#define R2057_RSSI_GPAIOSEL_W1_IDACS_CORE1 0x13e +#define R2057_NB_IDACS_Q_CORE1 0x13f +#define R2057_NB_IDACS_I_CORE1 0x140 +#define R2057_BACKUP4_CORE1 0x146 +#define R2057_BACKUP3_CORE1 0x147 +#define R2057_BACKUP2_CORE1 0x148 +#define R2057_BACKUP1_CORE1 0x149 +#define R2057_SPARE16_CORE1 0x14a +#define R2057_SPARE15_CORE1 0x14b +#define R2057_SPARE14_CORE1 0x14c +#define R2057_SPARE13_CORE1 0x14d +#define R2057_SPARE12_CORE1 0x14e +#define R2057_SPARE11_CORE1 0x14f +#define R2057_TX2G_BIAS_RESETS_CORE1 0x150 +#define R2057_TX5G_BIAS_RESETS_CORE1 0x151 +#define R2057_SPARE8_CORE1 0x152 +#define R2057_SPARE7_CORE1 0x153 +#define R2057_BUFS_MISC_LPFBW_CORE1 0x154 +#define R2057_TXLPF_RCCAL_CORE1 0x155 +#define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE1 0x156 +#define R2057_LPF_GAIN_CORE1 0x157 +#define R2057_DACBUF_IDACS_BW_CORE1 0x158 + +#define R2057_DACBUF_VINCM_CORE1 0x159 +#define R2057_RCCAL_START_R1_Q1_P1 0x15a +#define R2057_RCCAL_X1 0x15b +#define R2057_RCCAL_TRC0 0x15c +#define R2057_RCCAL_TRC1 0x15d +#define R2057_RCCAL_DONE_OSCCAP 0x15e +#define R2057_RCCAL_N0_0 0x15f +#define R2057_RCCAL_N0_1 0x160 +#define R2057_RCCAL_N1_0 0x161 +#define R2057_RCCAL_N1_1 0x162 +#define R2057_RCAL_STATUS 0x163 +#define R2057_XTALPUOVR_PINCTRL 0x164 +#define R2057_OVR_REG0 0x165 +#define R2057_OVR_REG1 0x166 +#define R2057_OVR_REG2 0x167 +#define R2057_OVR_REG3 0x168 +#define R2057_OVR_REG4 0x169 +#define R2057_RCCAL_SCAP_VAL 0x16a +#define R2057_RCCAL_BCAP_VAL 0x16b +#define R2057_RCCAL_HPC_VAL 0x16c +#define R2057_RCCAL_OVERRIDES 0x16d + +/* TX core 0 */ +#define R2057_TX0_IQCAL_GAIN_BW 0x170 +#define R2057_TX0_LOFT_FINE_I 0x171 +#define R2057_TX0_LOFT_FINE_Q 0x172 +#define R2057_TX0_LOFT_COARSE_I 0x173 +#define R2057_TX0_LOFT_COARSE_Q 0x174 +#define R2057_TX0_TX_SSI_MASTER 0x175 +#define R2057_TX0_IQCAL_VCM_HG 0x176 +#define R2057_TX0_IQCAL_IDAC 0x177 +#define R2057_TX0_TSSI_VCM 0x178 +#define R2057_TX0_TX_SSI_MUX 0x179 +#define R2057_TX0_TSSIA 0x17a +#define R2057_TX0_TSSIG 0x17b +#define R2057_TX0_TSSI_MISC1 0x17c +#define R2057_TX0_TXRXCOUPLE_2G_ATTEN 0x17d +#define R2057_TX0_TXRXCOUPLE_2G_PWRUP 0x17e +#define R2057_TX0_TXRXCOUPLE_5G_ATTEN 0x17f +#define R2057_TX0_TXRXCOUPLE_5G_PWRUP 0x180 + +/* TX core 1 */ +#define R2057_TX1_IQCAL_GAIN_BW 0x190 +#define R2057_TX1_LOFT_FINE_I 0x191 +#define R2057_TX1_LOFT_FINE_Q 0x192 +#define R2057_TX1_LOFT_COARSE_I 0x193 +#define R2057_TX1_LOFT_COARSE_Q 0x194 +#define R2057_TX1_TX_SSI_MASTER 0x195 +#define R2057_TX1_IQCAL_VCM_HG 0x196 +#define R2057_TX1_IQCAL_IDAC 0x197 +#define R2057_TX1_TSSI_VCM 0x198 +#define R2057_TX1_TX_SSI_MUX 0x199 +#define R2057_TX1_TSSIA 0x19a +#define R2057_TX1_TSSIG 0x19b +#define R2057_TX1_TSSI_MISC1 0x19c +#define R2057_TX1_TXRXCOUPLE_2G_ATTEN 0x19d +#define R2057_TX1_TXRXCOUPLE_2G_PWRUP 0x19e +#define R2057_TX1_TXRXCOUPLE_5G_ATTEN 0x19f +#define R2057_TX1_TXRXCOUPLE_5G_PWRUP 0x1a0 + +#define R2057_AFE_VCM_CAL_MASTER_CORE0 0x1a1 +#define R2057_AFE_SET_VCM_I_CORE0 0x1a2 +#define R2057_AFE_SET_VCM_Q_CORE0 0x1a3 +#define R2057_AFE_STATUS_VCM_IQADC_CORE0 0x1a4 +#define R2057_AFE_STATUS_VCM_I_CORE0 0x1a5 +#define R2057_AFE_STATUS_VCM_Q_CORE0 0x1a6 +#define R2057_AFE_VCM_CAL_MASTER_CORE1 0x1a7 +#define R2057_AFE_SET_VCM_I_CORE1 0x1a8 +#define R2057_AFE_SET_VCM_Q_CORE1 0x1a9 +#define R2057_AFE_STATUS_VCM_IQADC_CORE1 0x1aa +#define R2057_AFE_STATUS_VCM_I_CORE1 0x1ab +#define R2057_AFE_STATUS_VCM_Q_CORE1 0x1ac + +#define R2057v7_DACBUF_VINCM_CORE0 0x1ad +#define R2057v7_RCCAL_MASTER 0x1ae +#define R2057v7_TR2G_CONFIG3_CORE0_NU 0x1af +#define R2057v7_TR2G_CONFIG3_CORE1_NU 0x1b0 +#define R2057v7_LOGEN_PUS1 0x1b1 +#define R2057v7_OVR_REG5 0x1b2 +#define R2057v7_OVR_REG6 0x1b3 +#define R2057v7_OVR_REG7 0x1b4 +#define R2057v7_OVR_REG8 0x1b5 +#define R2057v7_OVR_REG9 0x1b6 +#define R2057v7_OVR_REG10 0x1b7 +#define R2057v7_OVR_REG11 0x1b8 +#define R2057v7_OVR_REG12 0x1b9 +#define R2057v7_OVR_REG13 0x1ba +#define R2057v7_OVR_REG14 0x1bb +#define R2057v7_OVR_REG15 0x1bc +#define R2057v7_OVR_REG16 0x1bd +#define R2057v7_OVR_REG1 0x1be +#define R2057v7_OVR_REG18 0x1bf +#define R2057v7_OVR_REG19 0x1c0 +#define R2057v7_OVR_REG20 0x1c1 +#define R2057v7_OVR_REG21 0x1c2 +#define R2057v7_OVR_REG2 0x1c3 +#define R2057v7_OVR_REG23 0x1c4 +#define R2057v7_OVR_REG24 0x1c5 +#define R2057v7_OVR_REG25 0x1c6 +#define R2057v7_OVR_REG26 0x1c7 +#define R2057v7_OVR_REG27 0x1c8 +#define R2057v7_OVR_REG28 0x1c9 +#define R2057v7_IQTEST_SEL_PU2 0x1ca + +#define R2057_VCM_MASK 0x7 + +struct b43_nphy_chantabent_rev7 { + /* The channel frequency in MHz */ + u16 freq; + /* Radio regs values on channelswitch */ + u8 radio_vcocal_countval0; + u8 radio_vcocal_countval1; + u8 radio_rfpll_refmaster_sparextalsize; + u8 radio_rfpll_loopfilter_r1; + u8 radio_rfpll_loopfilter_c2; + u8 radio_rfpll_loopfilter_c1; + u8 radio_cp_kpd_idac; + u8 radio_rfpll_mmd0; + u8 radio_rfpll_mmd1; + u8 radio_vcobuf_tune; + u8 radio_logen_mx2g_tune; + u8 radio_logen_mx5g_tune; + u8 radio_logen_indbuf2g_tune; + u8 radio_logen_indbuf5g_tune; + u8 radio_txmix2g_tune_boost_pu_core0; + u8 radio_pad2g_tune_pus_core0; + u8 radio_pga_boost_tune_core0; + u8 radio_txmix5g_boost_tune_core0; + u8 radio_pad5g_tune_misc_pus_core0; + u8 radio_lna2g_tune_core0; + u8 radio_lna5g_tune_core0; + u8 radio_txmix2g_tune_boost_pu_core1; + u8 radio_pad2g_tune_pus_core1; + u8 radio_pga_boost_tune_core1; + u8 radio_txmix5g_boost_tune_core1; + u8 radio_pad5g_tune_misc_pus_core1; + u8 radio_lna2g_tune_core1; + u8 radio_lna5g_tune_core1; + /* PHY res values on channelswitch */ + struct b43_phy_n_sfo_cfg phy_regs; +}; + +struct b43_nphy_chantabent_rev7_2g { + /* The channel frequency in MHz */ + u16 freq; + /* Radio regs values on channelswitch */ + u8 radio_vcocal_countval0; + u8 radio_vcocal_countval1; + u8 radio_rfpll_refmaster_sparextalsize; + u8 radio_rfpll_loopfilter_r1; + u8 radio_rfpll_loopfilter_c2; + u8 radio_rfpll_loopfilter_c1; + u8 radio_cp_kpd_idac; + u8 radio_rfpll_mmd0; + u8 radio_rfpll_mmd1; + u8 radio_vcobuf_tune; + u8 radio_logen_mx2g_tune; + u8 radio_logen_indbuf2g_tune; + u8 radio_txmix2g_tune_boost_pu_core0; + u8 radio_pad2g_tune_pus_core0; + u8 radio_lna2g_tune_core0; + u8 radio_txmix2g_tune_boost_pu_core1; + u8 radio_pad2g_tune_pus_core1; + u8 radio_lna2g_tune_core1; + /* PHY regs values on channelswitch */ + struct b43_phy_n_sfo_cfg phy_regs; +}; + +void r2057_upload_inittabs(struct b43_wldev *dev); + +void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, + const struct b43_nphy_chantabent_rev7 **tabent_r7, + const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g); + +#endif /* B43_RADIO_2057_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/radio_2059.c b/drivers/net/wireless/broadcom/b43/radio_2059.c new file mode 100644 index 000000000000..a3cf9efd7e21 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/radio_2059.c @@ -0,0 +1,364 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11n 2059 radio device data tables + + Copyright (c) 2011 Rafał Miłecki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "radio_2059.h" + +/* Extracted from MMIO dump of 6.30.223.141 */ +static u16 r2059_phy_rev1_init[][2] = { + { 0x051, 0x70 }, { 0x05a, 0x03 }, { 0x079, 0x01 }, { 0x082, 0x70 }, + { 0x083, 0x00 }, { 0x084, 0x70 }, { 0x09a, 0x7f }, { 0x0b6, 0x10 }, + { 0x188, 0x05 }, +}; + +#define RADIOREGS(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ + r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ + r20) \ + .radio_syn16 = r00, \ + .radio_syn17 = r01, \ + .radio_syn22 = r02, \ + .radio_syn25 = r03, \ + .radio_syn27 = r04, \ + .radio_syn28 = r05, \ + .radio_syn29 = r06, \ + .radio_syn2c = r07, \ + .radio_syn2d = r08, \ + .radio_syn37 = r09, \ + .radio_syn41 = r10, \ + .radio_syn43 = r11, \ + .radio_syn47 = r12, \ + .radio_rxtx4a = r13, \ + .radio_rxtx58 = r14, \ + .radio_rxtx5a = r15, \ + .radio_rxtx6a = r16, \ + .radio_rxtx6d = r17, \ + .radio_rxtx6e = r18, \ + .radio_rxtx92 = r19, \ + .radio_rxtx98 = r20 + +#define PHYREGS(r0, r1, r2, r3, r4, r5) \ + .phy_regs.bw1 = r0, \ + .phy_regs.bw2 = r1, \ + .phy_regs.bw3 = r2, \ + .phy_regs.bw4 = r3, \ + .phy_regs.bw5 = r4, \ + .phy_regs.bw6 = r5 + +/* Extracted from MMIO dump of 6.30.223.141 + * TODO: Values for channels 12 & 13 are outdated (from some old 5.x driver)! + */ +static const struct b43_phy_ht_channeltab_e_radio2059 b43_phy_ht_channeltab_radio2059[] = { + { + .freq = 2412, + RADIOREGS(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, + 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, + 0x00, 0x00, 0x00, 0xd0, 0x00), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { + .freq = 2417, + RADIOREGS(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, + 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, + 0x00, 0x00, 0x00, 0xd0, 0x00), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { + .freq = 2422, + RADIOREGS(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, + 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, + 0x00, 0x00, 0x00, 0xd0, 0x00), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { + .freq = 2427, + RADIOREGS(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, + 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, + 0x00, 0x00, 0x00, 0xa0, 0x00), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { + .freq = 2432, + RADIOREGS(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, + 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, + 0x00, 0x00, 0x00, 0xa0, 0x00), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { + .freq = 2437, + RADIOREGS(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, + 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, + 0x00, 0x00, 0x00, 0xa0, 0x00), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { + .freq = 2442, + RADIOREGS(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, + 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, + 0x00, 0x00, 0x00, 0x80, 0x00), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { + .freq = 2447, + RADIOREGS(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, + 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, + 0x00, 0x00, 0x00, 0x80, 0x00), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { + .freq = 2452, + RADIOREGS(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, + 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, + 0x00, 0x00, 0x00, 0x80, 0x00), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { + .freq = 2457, + RADIOREGS(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, + 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, + 0x00, 0x00, 0x00, 0x60, 0x00), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { + .freq = 2462, + RADIOREGS(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, + 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, + 0x00, 0x00, 0x00, 0x60, 0x00), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { .freq = 2467, + RADIOREGS(0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, + 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x03, + 0x00, 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { .freq = 2472, + RADIOREGS(0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, + 0x09, 0x0f, 0x05, 0x00, 0x05, 0x00, 0x61, 0x03, + 0x00, 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { + .freq = 5180, + RADIOREGS(0xbe, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x06, + 0x02, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, + 0x0f, 0x4f, 0xa3, 0x00, 0xfc), + PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), + }, + { + .freq = 5200, + RADIOREGS(0xc5, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x08, + 0x02, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, + 0x0f, 0x4f, 0x93, 0x00, 0xfb), + PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), + }, + { + .freq = 5220, + RADIOREGS(0xcc, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0a, + 0x02, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, + 0x0f, 0x4f, 0x93, 0x00, 0xea), + PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), + }, + { + .freq = 5240, + RADIOREGS(0xd2, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0c, + 0x02, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, + 0x0f, 0x4f, 0x93, 0x00, 0xda), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { + .freq = 5260, + RADIOREGS(0xd9, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0e, + 0x02, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x00, + 0x0f, 0x4f, 0x93, 0x00, 0xca), + PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), + }, + { + .freq = 5280, + RADIOREGS(0xe0, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x10, + 0x02, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x00, + 0x0f, 0x4f, 0x93, 0x00, 0xb9), + PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), + }, + { + .freq = 5300, + RADIOREGS(0xe6, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x12, + 0x02, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x00, + 0x0f, 0x4c, 0x83, 0x00, 0xb8), + PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), + }, + { + .freq = 5320, + RADIOREGS(0xed, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x14, + 0x02, 0x0b, 0x00, 0x0b, 0x00, 0x0b, 0x00, 0x00, + 0x0f, 0x4c, 0x83, 0x00, 0xa8), + PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), + }, + { + .freq = 5500, + RADIOREGS(0x29, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x26, + 0x02, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, + 0x0a, 0x46, 0x43, 0x00, 0x75), + PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), + }, + { + .freq = 5520, + RADIOREGS(0x30, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x28, + 0x02, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, + 0x0a, 0x46, 0x43, 0x00, 0x75), + PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), + }, + { + .freq = 5540, + RADIOREGS(0x36, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x2a, + 0x02, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, + 0x0a, 0x46, 0x43, 0x00, 0x75), + PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), + }, + { + .freq = 5560, + RADIOREGS(0x3d, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x2c, + 0x02, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, + 0x0a, 0x46, 0x43, 0x00, 0x75), + PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), + }, + { + .freq = 5580, + RADIOREGS(0x44, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x2e, + 0x02, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, + 0x0a, 0x46, 0x43, 0x00, 0x74), + PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), + }, + { + .freq = 5600, + RADIOREGS(0x4a, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x30, + 0x02, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, + 0x09, 0x44, 0x23, 0x00, 0x54), + PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), + }, + { + .freq = 5620, + RADIOREGS(0x51, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x32, + 0x02, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, + 0x09, 0x44, 0x23, 0x00, 0x54), + PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), + }, + { + .freq = 5640, + RADIOREGS(0x58, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x34, + 0x02, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, + 0x09, 0x44, 0x23, 0x00, 0x43), + PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), + }, + { + .freq = 5660, + RADIOREGS(0x5e, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x36, + 0x02, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, + 0x09, 0x43, 0x23, 0x00, 0x43), + PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), + }, + { + .freq = 5680, + RADIOREGS(0x65, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x38, + 0x02, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, + 0x09, 0x42, 0x23, 0x00, 0x43), + PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), + }, + { + .freq = 5700, + RADIOREGS(0x6c, 0x17, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x3a, + 0x02, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, + 0x08, 0x42, 0x13, 0x00, 0x32), + PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), + }, + { + .freq = 5745, + RADIOREGS(0x7b, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x7d, + 0x04, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, + 0x08, 0x42, 0x13, 0x00, 0x21), + PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), + }, + { + .freq = 5765, + RADIOREGS(0x81, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x81, + 0x04, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, + 0x08, 0x42, 0x13, 0x00, 0x11), + PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), + }, + { + .freq = 5785, + RADIOREGS(0x88, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x85, + 0x04, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, + 0x08, 0x42, 0x13, 0x00, 0x00), + PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), + }, + { + .freq = 5805, + RADIOREGS(0x8f, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x89, + 0x04, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, + 0x06, 0x41, 0x03, 0x00, 0x00), + PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), + }, + { + .freq = 5825, + RADIOREGS(0x95, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x8d, + 0x04, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, + 0x06, 0x41, 0x03, 0x00, 0x00), + PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), + }, +}; + +void r2059_upload_inittabs(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + u16 *table = NULL; + u16 size, i; + + switch (phy->rev) { + case 1: + table = r2059_phy_rev1_init[0]; + size = ARRAY_SIZE(r2059_phy_rev1_init); + break; + default: + B43_WARN_ON(1); + return; + } + + for (i = 0; i < size; i++, table += 2) + b43_radio_write(dev, R2059_ALL | table[0], table[1]); +} + +const struct b43_phy_ht_channeltab_e_radio2059 +*b43_phy_ht_get_channeltab_e_r2059(struct b43_wldev *dev, u16 freq) +{ + const struct b43_phy_ht_channeltab_e_radio2059 *e; + unsigned int i; + + e = b43_phy_ht_channeltab_radio2059; + for (i = 0; i < ARRAY_SIZE(b43_phy_ht_channeltab_radio2059); i++, e++) { + if (e->freq == freq) + return e; + } + + return NULL; +} diff --git a/drivers/net/wireless/broadcom/b43/radio_2059.h b/drivers/net/wireless/broadcom/b43/radio_2059.h new file mode 100644 index 000000000000..9e22fb60588b --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/radio_2059.h @@ -0,0 +1,60 @@ +#ifndef B43_RADIO_2059_H_ +#define B43_RADIO_2059_H_ + +#include + +#include "phy_ht.h" + +#define R2059_C1 0x000 +#define R2059_C2 0x400 +#define R2059_C3 0x800 +#define R2059_ALL 0xC00 + +#define R2059_RCAL_CONFIG 0x004 +#define R2059_RFPLL_MASTER 0x011 +#define R2059_RFPLL_MISC_EN 0x02b +#define R2059_RFPLL_MISC_CAL_RESETN 0x02e +#define R2059_XTAL_CONFIG2 0x0c0 +#define R2059_RCCAL_START_R1_Q1_P1 0x13c +#define R2059_RCCAL_X1 0x13d +#define R2059_RCCAL_TRC0 0x13e +#define R2059_RCCAL_DONE_OSCCAP 0x140 +#define R2059_RCAL_STATUS 0x145 +#define R2059_RCCAL_MASTER 0x17f + +/* Values for various registers uploaded on channel switching */ +struct b43_phy_ht_channeltab_e_radio2059 { + /* The channel frequency in MHz */ + u16 freq; + /* Values for radio registers */ + u8 radio_syn16; + u8 radio_syn17; + u8 radio_syn22; + u8 radio_syn25; + u8 radio_syn27; + u8 radio_syn28; + u8 radio_syn29; + u8 radio_syn2c; + u8 radio_syn2d; + u8 radio_syn37; + u8 radio_syn41; + u8 radio_syn43; + u8 radio_syn47; + u8 radio_rxtx4a; + u8 radio_rxtx58; + u8 radio_rxtx5a; + u8 radio_rxtx6a; + u8 radio_rxtx6d; + u8 radio_rxtx6e; + u8 radio_rxtx92; + u8 radio_rxtx98; + /* Values for PHY registers */ + struct b43_phy_ht_channeltab_e_phy phy_regs; +}; + +void r2059_upload_inittabs(struct b43_wldev *dev); + +const struct b43_phy_ht_channeltab_e_radio2059 +*b43_phy_ht_get_channeltab_e_r2059(struct b43_wldev *dev, u16 freq); + +#endif /* B43_RADIO_2059_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/rfkill.c b/drivers/net/wireless/broadcom/b43/rfkill.c new file mode 100644 index 000000000000..70c2fcedd1bb --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/rfkill.c @@ -0,0 +1,70 @@ +/* + + Broadcom B43 wireless driver + RFKILL support + + Copyright (c) 2007 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" + + +/* Returns TRUE, if the radio is enabled in hardware. */ +bool b43_is_hw_radio_enabled(struct b43_wldev *dev) +{ + return !(b43_read32(dev, B43_MMIO_RADIO_HWENABLED_HI) + & B43_MMIO_RADIO_HWENABLED_HI_MASK); +} + +/* The poll callback for the hardware button. */ +void b43_rfkill_poll(struct ieee80211_hw *hw) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev = wl->current_dev; + bool enabled; + bool brought_up = false; + + mutex_lock(&wl->mutex); + if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) { + if (b43_bus_powerup(dev, 0)) { + mutex_unlock(&wl->mutex); + return; + } + b43_device_enable(dev, 0); + brought_up = true; + } + + enabled = b43_is_hw_radio_enabled(dev); + + if (unlikely(enabled != dev->radio_hw_enable)) { + dev->radio_hw_enable = enabled; + b43info(wl, "Radio hardware status changed to %s\n", + enabled ? "ENABLED" : "DISABLED"); + wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); + if (enabled != dev->phy.radio_on) + b43_software_rfkill(dev, !enabled); + } + + if (brought_up) { + b43_device_disable(dev, 0); + b43_bus_may_powerdown(dev); + } + + mutex_unlock(&wl->mutex); +} diff --git a/drivers/net/wireless/broadcom/b43/rfkill.h b/drivers/net/wireless/broadcom/b43/rfkill.h new file mode 100644 index 000000000000..f046c3ca0519 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/rfkill.h @@ -0,0 +1,11 @@ +#ifndef B43_RFKILL_H_ +#define B43_RFKILL_H_ + +struct ieee80211_hw; +struct b43_wldev; + +void b43_rfkill_poll(struct ieee80211_hw *hw); + +bool b43_is_hw_radio_enabled(struct b43_wldev *dev); + +#endif /* B43_RFKILL_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/sdio.c b/drivers/net/wireless/broadcom/b43/sdio.c new file mode 100644 index 000000000000..59a521800694 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/sdio.c @@ -0,0 +1,207 @@ +/* + * Broadcom B43 wireless driver + * + * SDIO over Sonics Silicon Backplane bus glue for b43. + * + * Copyright (C) 2009 Albert Herranz + * Copyright (C) 2009 Michael Buesch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include "sdio.h" +#include "b43.h" + + +#define HNBU_CHIPID 0x01 /* vendor & device id */ + +#define B43_SDIO_BLOCK_SIZE 64 /* rx fifo max size in bytes */ + + +static const struct b43_sdio_quirk { + u16 vendor; + u16 device; + unsigned int quirks; +} b43_sdio_quirks[] = { + { 0x14E4, 0x4318, SSB_QUIRK_SDIO_READ_AFTER_WRITE32, }, + { }, +}; + + +static unsigned int b43_sdio_get_quirks(u16 vendor, u16 device) +{ + const struct b43_sdio_quirk *q; + + for (q = b43_sdio_quirks; q->quirks; q++) { + if (vendor == q->vendor && device == q->device) + return q->quirks; + } + + return 0; +} + +static void b43_sdio_interrupt_dispatcher(struct sdio_func *func) +{ + struct b43_sdio *sdio = sdio_get_drvdata(func); + struct b43_wldev *dev = sdio->irq_handler_opaque; + + if (unlikely(b43_status(dev) < B43_STAT_STARTED)) + return; + + sdio_release_host(func); + sdio->irq_handler(dev); + sdio_claim_host(func); +} + +int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)) +{ + struct ssb_bus *bus = dev->dev->sdev->bus; + struct sdio_func *func = bus->host_sdio; + struct b43_sdio *sdio = sdio_get_drvdata(func); + int err; + + sdio->irq_handler_opaque = dev; + sdio->irq_handler = handler; + sdio_claim_host(func); + err = sdio_claim_irq(func, b43_sdio_interrupt_dispatcher); + sdio_release_host(func); + + return err; +} + +void b43_sdio_free_irq(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->sdev->bus; + struct sdio_func *func = bus->host_sdio; + struct b43_sdio *sdio = sdio_get_drvdata(func); + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_release_host(func); + sdio->irq_handler_opaque = NULL; + sdio->irq_handler = NULL; +} + +static int b43_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct b43_sdio *sdio; + struct sdio_func_tuple *tuple; + u16 vendor = 0, device = 0; + int error; + + /* Look for the card chip identifier. */ + tuple = func->tuples; + while (tuple) { + switch (tuple->code) { + case 0x80: + switch (tuple->data[0]) { + case HNBU_CHIPID: + if (tuple->size != 5) + break; + vendor = tuple->data[1] | (tuple->data[2]<<8); + device = tuple->data[3] | (tuple->data[4]<<8); + dev_info(&func->dev, "Chip ID %04x:%04x\n", + vendor, device); + break; + default: + break; + } + break; + default: + break; + } + tuple = tuple->next; + } + if (!vendor || !device) { + error = -ENODEV; + goto out; + } + + sdio_claim_host(func); + error = sdio_set_block_size(func, B43_SDIO_BLOCK_SIZE); + if (error) { + dev_err(&func->dev, "failed to set block size to %u bytes," + " error %d\n", B43_SDIO_BLOCK_SIZE, error); + goto err_release_host; + } + error = sdio_enable_func(func); + if (error) { + dev_err(&func->dev, "failed to enable func, error %d\n", error); + goto err_release_host; + } + sdio_release_host(func); + + sdio = kzalloc(sizeof(*sdio), GFP_KERNEL); + if (!sdio) { + error = -ENOMEM; + dev_err(&func->dev, "failed to allocate ssb bus\n"); + goto err_disable_func; + } + error = ssb_bus_sdiobus_register(&sdio->ssb, func, + b43_sdio_get_quirks(vendor, device)); + if (error) { + dev_err(&func->dev, "failed to register ssb sdio bus," + " error %d\n", error); + goto err_free_ssb; + } + sdio_set_drvdata(func, sdio); + + return 0; + +err_free_ssb: + kfree(sdio); +err_disable_func: + sdio_claim_host(func); + sdio_disable_func(func); +err_release_host: + sdio_release_host(func); +out: + return error; +} + +static void b43_sdio_remove(struct sdio_func *func) +{ + struct b43_sdio *sdio = sdio_get_drvdata(func); + + ssb_bus_unregister(&sdio->ssb); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + kfree(sdio); + sdio_set_drvdata(func, NULL); +} + +static const struct sdio_device_id b43_sdio_ids[] = { + { SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */ + { SDIO_DEVICE(0x0092, 0x0004) }, /* C-guys, Inc. EW-CG1102GC */ + { }, +}; + +static struct sdio_driver b43_sdio_driver = { + .name = "b43-sdio", + .id_table = b43_sdio_ids, + .probe = b43_sdio_probe, + .remove = b43_sdio_remove, +}; + +int b43_sdio_init(void) +{ + return sdio_register_driver(&b43_sdio_driver); +} + +void b43_sdio_exit(void) +{ + sdio_unregister_driver(&b43_sdio_driver); +} diff --git a/drivers/net/wireless/broadcom/b43/sdio.h b/drivers/net/wireless/broadcom/b43/sdio.h new file mode 100644 index 000000000000..1e93926f388f --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/sdio.h @@ -0,0 +1,45 @@ +#ifndef B43_SDIO_H_ +#define B43_SDIO_H_ + +#include + +struct b43_wldev; + + +#ifdef CONFIG_B43_SDIO + +struct b43_sdio { + struct ssb_bus ssb; + void *irq_handler_opaque; + void (*irq_handler)(struct b43_wldev *dev); +}; + +int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)); +void b43_sdio_free_irq(struct b43_wldev *dev); + +int b43_sdio_init(void); +void b43_sdio_exit(void); + + +#else /* CONFIG_B43_SDIO */ + + +static inline int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)) +{ + return -ENODEV; +} +static inline void b43_sdio_free_irq(struct b43_wldev *dev) +{ +} +static inline int b43_sdio_init(void) +{ + return 0; +} +static inline void b43_sdio_exit(void) +{ +} + +#endif /* CONFIG_B43_SDIO */ +#endif /* B43_SDIO_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/sysfs.c b/drivers/net/wireless/broadcom/b43/sysfs.c new file mode 100644 index 000000000000..3190493bd07f --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/sysfs.c @@ -0,0 +1,155 @@ +/* + + Broadcom B43 wireless driver + + SYSFS support routines + + Copyright (c) 2006 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include +#include + +#include "b43.h" +#include "sysfs.h" +#include "main.h" +#include "phy_common.h" + +#define GENERIC_FILESIZE 64 + +static int get_integer(const char *buf, size_t count) +{ + char tmp[10 + 1] = { 0 }; + int ret = -EINVAL; + + if (count == 0) + goto out; + count = min_t(size_t, count, 10); + memcpy(tmp, buf, count); + ret = simple_strtol(tmp, NULL, 10); + out: + return ret; +} + +static ssize_t b43_attr_interfmode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct b43_wldev *wldev = dev_to_b43_wldev(dev); + ssize_t count = 0; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + mutex_lock(&wldev->wl->mutex); + + if (wldev->phy.type != B43_PHYTYPE_G) { + mutex_unlock(&wldev->wl->mutex); + return -ENOSYS; + } + + switch (wldev->phy.g->interfmode) { + case B43_INTERFMODE_NONE: + count = + snprintf(buf, PAGE_SIZE, + "0 (No Interference Mitigation)\n"); + break; + case B43_INTERFMODE_NONWLAN: + count = + snprintf(buf, PAGE_SIZE, + "1 (Non-WLAN Interference Mitigation)\n"); + break; + case B43_INTERFMODE_MANUALWLAN: + count = + snprintf(buf, PAGE_SIZE, + "2 (WLAN Interference Mitigation)\n"); + break; + default: + B43_WARN_ON(1); + } + + mutex_unlock(&wldev->wl->mutex); + + return count; +} + +static ssize_t b43_attr_interfmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct b43_wldev *wldev = dev_to_b43_wldev(dev); + int err; + int mode; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + mode = get_integer(buf, count); + switch (mode) { + case 0: + mode = B43_INTERFMODE_NONE; + break; + case 1: + mode = B43_INTERFMODE_NONWLAN; + break; + case 2: + mode = B43_INTERFMODE_MANUALWLAN; + break; + case 3: + mode = B43_INTERFMODE_AUTOWLAN; + break; + default: + return -EINVAL; + } + + mutex_lock(&wldev->wl->mutex); + + if (wldev->phy.ops->interf_mitigation) { + err = wldev->phy.ops->interf_mitigation(wldev, mode); + if (err) { + b43err(wldev->wl, "Interference Mitigation not " + "supported by device\n"); + } + } else + err = -ENOSYS; + + mmiowb(); + mutex_unlock(&wldev->wl->mutex); + + return err ? err : count; +} + +static DEVICE_ATTR(interference, 0644, + b43_attr_interfmode_show, b43_attr_interfmode_store); + +int b43_sysfs_register(struct b43_wldev *wldev) +{ + struct device *dev = wldev->dev->dev; + + B43_WARN_ON(b43_status(wldev) != B43_STAT_INITIALIZED); + + return device_create_file(dev, &dev_attr_interference); +} + +void b43_sysfs_unregister(struct b43_wldev *wldev) +{ + struct device *dev = wldev->dev->dev; + + device_remove_file(dev, &dev_attr_interference); +} diff --git a/drivers/net/wireless/broadcom/b43/sysfs.h b/drivers/net/wireless/broadcom/b43/sysfs.h new file mode 100644 index 000000000000..12bda9ef1a85 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/sysfs.h @@ -0,0 +1,9 @@ +#ifndef B43_SYSFS_H_ +#define B43_SYSFS_H_ + +struct b43_wldev; + +int b43_sysfs_register(struct b43_wldev *dev); +void b43_sysfs_unregister(struct b43_wldev *dev); + +#endif /* B43_SYSFS_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/tables.c b/drivers/net/wireless/broadcom/b43/tables.c new file mode 100644 index 000000000000..ea288df8aee9 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/tables.c @@ -0,0 +1,466 @@ +/* + + Broadcom B43 wireless driver + + Copyright (c) 2005 Martin Langer , + Copyright (c) 2005-2007 Stefano Brivio + Copyright (c) 2006, 2006 Michael Buesch + Copyright (c) 2005 Danny van Dyk + Copyright (c) 2005 Andreas Jaggi + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "tables.h" +#include "phy_g.h" + + +const u32 b43_tab_rotor[] = { + 0xFEB93FFD, 0xFEC63FFD, /* 0 */ + 0xFED23FFD, 0xFEDF3FFD, + 0xFEEC3FFE, 0xFEF83FFE, + 0xFF053FFE, 0xFF113FFE, + 0xFF1E3FFE, 0xFF2A3FFF, /* 8 */ + 0xFF373FFF, 0xFF443FFF, + 0xFF503FFF, 0xFF5D3FFF, + 0xFF693FFF, 0xFF763FFF, + 0xFF824000, 0xFF8F4000, /* 16 */ + 0xFF9B4000, 0xFFA84000, + 0xFFB54000, 0xFFC14000, + 0xFFCE4000, 0xFFDA4000, + 0xFFE74000, 0xFFF34000, /* 24 */ + 0x00004000, 0x000D4000, + 0x00194000, 0x00264000, + 0x00324000, 0x003F4000, + 0x004B4000, 0x00584000, /* 32 */ + 0x00654000, 0x00714000, + 0x007E4000, 0x008A3FFF, + 0x00973FFF, 0x00A33FFF, + 0x00B03FFF, 0x00BC3FFF, /* 40 */ + 0x00C93FFF, 0x00D63FFF, + 0x00E23FFE, 0x00EF3FFE, + 0x00FB3FFE, 0x01083FFE, + 0x01143FFE, 0x01213FFD, /* 48 */ + 0x012E3FFD, 0x013A3FFD, + 0x01473FFD, +}; + +const u32 b43_tab_retard[] = { + 0xDB93CB87, 0xD666CF64, /* 0 */ + 0xD1FDD358, 0xCDA6D826, + 0xCA38DD9F, 0xC729E2B4, + 0xC469E88E, 0xC26AEE2B, + 0xC0DEF46C, 0xC073FA62, /* 8 */ + 0xC01D00D5, 0xC0760743, + 0xC1560D1E, 0xC2E51369, + 0xC4ED18FF, 0xC7AC1ED7, + 0xCB2823B2, 0xCEFA28D9, /* 16 */ + 0xD2F62D3F, 0xD7BB3197, + 0xDCE53568, 0xE1FE3875, + 0xE7D13B35, 0xED663D35, + 0xF39B3EC4, 0xF98E3FA7, /* 24 */ + 0x00004000, 0x06723FA7, + 0x0C653EC4, 0x129A3D35, + 0x182F3B35, 0x1E023875, + 0x231B3568, 0x28453197, /* 32 */ + 0x2D0A2D3F, 0x310628D9, + 0x34D823B2, 0x38541ED7, + 0x3B1318FF, 0x3D1B1369, + 0x3EAA0D1E, 0x3F8A0743, /* 40 */ + 0x3FE300D5, 0x3F8DFA62, + 0x3F22F46C, 0x3D96EE2B, + 0x3B97E88E, 0x38D7E2B4, + 0x35C8DD9F, 0x325AD826, /* 48 */ + 0x2E03D358, 0x299ACF64, + 0x246DCB87, +}; + +const u16 b43_tab_finefreqa[] = { + 0x0082, 0x0082, 0x0102, 0x0182, /* 0 */ + 0x0202, 0x0282, 0x0302, 0x0382, + 0x0402, 0x0482, 0x0502, 0x0582, + 0x05E2, 0x0662, 0x06E2, 0x0762, + 0x07E2, 0x0842, 0x08C2, 0x0942, /* 16 */ + 0x09C2, 0x0A22, 0x0AA2, 0x0B02, + 0x0B82, 0x0BE2, 0x0C62, 0x0CC2, + 0x0D42, 0x0DA2, 0x0E02, 0x0E62, + 0x0EE2, 0x0F42, 0x0FA2, 0x1002, /* 32 */ + 0x1062, 0x10C2, 0x1122, 0x1182, + 0x11E2, 0x1242, 0x12A2, 0x12E2, + 0x1342, 0x13A2, 0x1402, 0x1442, + 0x14A2, 0x14E2, 0x1542, 0x1582, /* 48 */ + 0x15E2, 0x1622, 0x1662, 0x16C1, + 0x1701, 0x1741, 0x1781, 0x17E1, + 0x1821, 0x1861, 0x18A1, 0x18E1, + 0x1921, 0x1961, 0x19A1, 0x19E1, /* 64 */ + 0x1A21, 0x1A61, 0x1AA1, 0x1AC1, + 0x1B01, 0x1B41, 0x1B81, 0x1BA1, + 0x1BE1, 0x1C21, 0x1C41, 0x1C81, + 0x1CA1, 0x1CE1, 0x1D01, 0x1D41, /* 80 */ + 0x1D61, 0x1DA1, 0x1DC1, 0x1E01, + 0x1E21, 0x1E61, 0x1E81, 0x1EA1, + 0x1EE1, 0x1F01, 0x1F21, 0x1F41, + 0x1F81, 0x1FA1, 0x1FC1, 0x1FE1, /* 96 */ + 0x2001, 0x2041, 0x2061, 0x2081, + 0x20A1, 0x20C1, 0x20E1, 0x2101, + 0x2121, 0x2141, 0x2161, 0x2181, + 0x21A1, 0x21C1, 0x21E1, 0x2201, /* 112 */ + 0x2221, 0x2241, 0x2261, 0x2281, + 0x22A1, 0x22C1, 0x22C1, 0x22E1, + 0x2301, 0x2321, 0x2341, 0x2361, + 0x2361, 0x2381, 0x23A1, 0x23C1, /* 128 */ + 0x23E1, 0x23E1, 0x2401, 0x2421, + 0x2441, 0x2441, 0x2461, 0x2481, + 0x2481, 0x24A1, 0x24C1, 0x24C1, + 0x24E1, 0x2501, 0x2501, 0x2521, /* 144 */ + 0x2541, 0x2541, 0x2561, 0x2561, + 0x2581, 0x25A1, 0x25A1, 0x25C1, + 0x25C1, 0x25E1, 0x2601, 0x2601, + 0x2621, 0x2621, 0x2641, 0x2641, /* 160 */ + 0x2661, 0x2661, 0x2681, 0x2681, + 0x26A1, 0x26A1, 0x26C1, 0x26C1, + 0x26E1, 0x26E1, 0x2701, 0x2701, + 0x2721, 0x2721, 0x2740, 0x2740, /* 176 */ + 0x2760, 0x2760, 0x2780, 0x2780, + 0x2780, 0x27A0, 0x27A0, 0x27C0, + 0x27C0, 0x27E0, 0x27E0, 0x27E0, + 0x2800, 0x2800, 0x2820, 0x2820, /* 192 */ + 0x2820, 0x2840, 0x2840, 0x2840, + 0x2860, 0x2860, 0x2880, 0x2880, + 0x2880, 0x28A0, 0x28A0, 0x28A0, + 0x28C0, 0x28C0, 0x28C0, 0x28E0, /* 208 */ + 0x28E0, 0x28E0, 0x2900, 0x2900, + 0x2900, 0x2920, 0x2920, 0x2920, + 0x2940, 0x2940, 0x2940, 0x2960, + 0x2960, 0x2960, 0x2960, 0x2980, /* 224 */ + 0x2980, 0x2980, 0x29A0, 0x29A0, + 0x29A0, 0x29A0, 0x29C0, 0x29C0, + 0x29C0, 0x29E0, 0x29E0, 0x29E0, + 0x29E0, 0x2A00, 0x2A00, 0x2A00, /* 240 */ + 0x2A00, 0x2A20, 0x2A20, 0x2A20, + 0x2A20, 0x2A40, 0x2A40, 0x2A40, + 0x2A40, 0x2A60, 0x2A60, 0x2A60, +}; + +const u16 b43_tab_finefreqg[] = { + 0x0089, 0x02E9, 0x0409, 0x04E9, /* 0 */ + 0x05A9, 0x0669, 0x0709, 0x0789, + 0x0829, 0x08A9, 0x0929, 0x0989, + 0x0A09, 0x0A69, 0x0AC9, 0x0B29, + 0x0BA9, 0x0BE9, 0x0C49, 0x0CA9, /* 16 */ + 0x0D09, 0x0D69, 0x0DA9, 0x0E09, + 0x0E69, 0x0EA9, 0x0F09, 0x0F49, + 0x0FA9, 0x0FE9, 0x1029, 0x1089, + 0x10C9, 0x1109, 0x1169, 0x11A9, /* 32 */ + 0x11E9, 0x1229, 0x1289, 0x12C9, + 0x1309, 0x1349, 0x1389, 0x13C9, + 0x1409, 0x1449, 0x14A9, 0x14E9, + 0x1529, 0x1569, 0x15A9, 0x15E9, /* 48 */ + 0x1629, 0x1669, 0x16A9, 0x16E8, + 0x1728, 0x1768, 0x17A8, 0x17E8, + 0x1828, 0x1868, 0x18A8, 0x18E8, + 0x1928, 0x1968, 0x19A8, 0x19E8, /* 64 */ + 0x1A28, 0x1A68, 0x1AA8, 0x1AE8, + 0x1B28, 0x1B68, 0x1BA8, 0x1BE8, + 0x1C28, 0x1C68, 0x1CA8, 0x1CE8, + 0x1D28, 0x1D68, 0x1DC8, 0x1E08, /* 80 */ + 0x1E48, 0x1E88, 0x1EC8, 0x1F08, + 0x1F48, 0x1F88, 0x1FE8, 0x2028, + 0x2068, 0x20A8, 0x2108, 0x2148, + 0x2188, 0x21C8, 0x2228, 0x2268, /* 96 */ + 0x22C8, 0x2308, 0x2348, 0x23A8, + 0x23E8, 0x2448, 0x24A8, 0x24E8, + 0x2548, 0x25A8, 0x2608, 0x2668, + 0x26C8, 0x2728, 0x2787, 0x27E7, /* 112 */ + 0x2847, 0x28C7, 0x2947, 0x29A7, + 0x2A27, 0x2AC7, 0x2B47, 0x2BE7, + 0x2CA7, 0x2D67, 0x2E47, 0x2F67, + 0x3247, 0x3526, 0x3646, 0x3726, /* 128 */ + 0x3806, 0x38A6, 0x3946, 0x39E6, + 0x3A66, 0x3AE6, 0x3B66, 0x3BC6, + 0x3C45, 0x3CA5, 0x3D05, 0x3D85, + 0x3DE5, 0x3E45, 0x3EA5, 0x3EE5, /* 144 */ + 0x3F45, 0x3FA5, 0x4005, 0x4045, + 0x40A5, 0x40E5, 0x4145, 0x4185, + 0x41E5, 0x4225, 0x4265, 0x42C5, + 0x4305, 0x4345, 0x43A5, 0x43E5, /* 160 */ + 0x4424, 0x4464, 0x44C4, 0x4504, + 0x4544, 0x4584, 0x45C4, 0x4604, + 0x4644, 0x46A4, 0x46E4, 0x4724, + 0x4764, 0x47A4, 0x47E4, 0x4824, /* 176 */ + 0x4864, 0x48A4, 0x48E4, 0x4924, + 0x4964, 0x49A4, 0x49E4, 0x4A24, + 0x4A64, 0x4AA4, 0x4AE4, 0x4B23, + 0x4B63, 0x4BA3, 0x4BE3, 0x4C23, /* 192 */ + 0x4C63, 0x4CA3, 0x4CE3, 0x4D23, + 0x4D63, 0x4DA3, 0x4DE3, 0x4E23, + 0x4E63, 0x4EA3, 0x4EE3, 0x4F23, + 0x4F63, 0x4FC3, 0x5003, 0x5043, /* 208 */ + 0x5083, 0x50C3, 0x5103, 0x5143, + 0x5183, 0x51E2, 0x5222, 0x5262, + 0x52A2, 0x52E2, 0x5342, 0x5382, + 0x53C2, 0x5402, 0x5462, 0x54A2, /* 224 */ + 0x5502, 0x5542, 0x55A2, 0x55E2, + 0x5642, 0x5682, 0x56E2, 0x5722, + 0x5782, 0x57E1, 0x5841, 0x58A1, + 0x5901, 0x5961, 0x59C1, 0x5A21, /* 240 */ + 0x5AA1, 0x5B01, 0x5B81, 0x5BE1, + 0x5C61, 0x5D01, 0x5D80, 0x5E20, + 0x5EE0, 0x5FA0, 0x6080, 0x61C0, +}; + +const u16 b43_tab_noisea2[] = { + 0x0001, 0x0001, 0x0001, 0xFFFE, + 0xFFFE, 0x3FFF, 0x1000, 0x0393, +}; + +const u16 b43_tab_noisea3[] = { + 0x5E5E, 0x5E5E, 0x5E5E, 0x3F48, + 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, +}; + +const u16 b43_tab_noiseg1[] = { + 0x013C, 0x01F5, 0x031A, 0x0631, + 0x0001, 0x0001, 0x0001, 0x0001, +}; + +const u16 b43_tab_noiseg2[] = { + 0x5484, 0x3C40, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, +}; + +const u16 b43_tab_noisescalea2[] = { + 0x6767, 0x6767, 0x6767, 0x6767, /* 0 */ + 0x6767, 0x6767, 0x6767, 0x6767, + 0x6767, 0x6767, 0x6767, 0x6767, + 0x6767, 0x6700, 0x6767, 0x6767, + 0x6767, 0x6767, 0x6767, 0x6767, /* 16 */ + 0x6767, 0x6767, 0x6767, 0x6767, + 0x6767, 0x6767, 0x0067, +}; + +const u16 b43_tab_noisescalea3[] = { + 0x2323, 0x2323, 0x2323, 0x2323, /* 0 */ + 0x2323, 0x2323, 0x2323, 0x2323, + 0x2323, 0x2323, 0x2323, 0x2323, + 0x2323, 0x2300, 0x2323, 0x2323, + 0x2323, 0x2323, 0x2323, 0x2323, /* 16 */ + 0x2323, 0x2323, 0x2323, 0x2323, + 0x2323, 0x2323, 0x0023, +}; + +const u16 b43_tab_noisescaleg1[] = { + 0x6C77, 0x5162, 0x3B40, 0x3335, /* 0 */ + 0x2F2D, 0x2A2A, 0x2527, 0x1F21, + 0x1A1D, 0x1719, 0x1616, 0x1414, + 0x1414, 0x1400, 0x1414, 0x1614, + 0x1716, 0x1A19, 0x1F1D, 0x2521, /* 16 */ + 0x2A27, 0x2F2A, 0x332D, 0x3B35, + 0x5140, 0x6C62, 0x0077, +}; + +const u16 b43_tab_noisescaleg2[] = { + 0xD8DD, 0xCBD4, 0xBCC0, 0xB6B7, /* 0 */ + 0xB2B0, 0xADAD, 0xA7A9, 0x9FA1, + 0x969B, 0x9195, 0x8F8F, 0x8A8A, + 0x8A8A, 0x8A00, 0x8A8A, 0x8F8A, + 0x918F, 0x9695, 0x9F9B, 0xA7A1, /* 16 */ + 0xADA9, 0xB2AD, 0xB6B0, 0xBCB7, + 0xCBC0, 0xD8D4, 0x00DD, +}; + +const u16 b43_tab_noisescaleg3[] = { + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 0 */ + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA400, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 16 */ + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0x00A4, +}; + +const u16 b43_tab_sigmasqr1[] = { + 0x007A, 0x0075, 0x0071, 0x006C, /* 0 */ + 0x0067, 0x0063, 0x005E, 0x0059, + 0x0054, 0x0050, 0x004B, 0x0046, + 0x0042, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, /* 16 */ + 0x003D, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x0000, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, /* 32 */ + 0x003D, 0x003D, 0x003D, 0x003D, + 0x0042, 0x0046, 0x004B, 0x0050, + 0x0054, 0x0059, 0x005E, 0x0063, + 0x0067, 0x006C, 0x0071, 0x0075, /* 48 */ + 0x007A, +}; + +const u16 b43_tab_sigmasqr2[] = { + 0x00DE, 0x00DC, 0x00DA, 0x00D8, /* 0 */ + 0x00D6, 0x00D4, 0x00D2, 0x00CF, + 0x00CD, 0x00CA, 0x00C7, 0x00C4, + 0x00C1, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 16 */ + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x0000, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 32 */ + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00C1, 0x00C4, 0x00C7, 0x00CA, + 0x00CD, 0x00CF, 0x00D2, 0x00D4, + 0x00D6, 0x00D8, 0x00DA, 0x00DC, /* 48 */ + 0x00DE, +}; + +const u16 b43_tab_rssiagc1[] = { + 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, /* 0 */ + 0xFFF8, 0xFFF9, 0xFFFC, 0xFFFE, + 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, + 0xFFF8, 0xFFF8, 0xFFF8, 0xFFF8, +}; + +const u16 b43_tab_rssiagc2[] = { + 0x0820, 0x0820, 0x0920, 0x0C38, /* 0 */ + 0x0820, 0x0820, 0x0820, 0x0820, + 0x0820, 0x0820, 0x0920, 0x0A38, + 0x0820, 0x0820, 0x0820, 0x0820, + 0x0820, 0x0820, 0x0920, 0x0A38, /* 16 */ + 0x0820, 0x0820, 0x0820, 0x0820, + 0x0820, 0x0820, 0x0920, 0x0A38, + 0x0820, 0x0820, 0x0820, 0x0820, + 0x0820, 0x0820, 0x0920, 0x0A38, /* 32 */ + 0x0820, 0x0820, 0x0820, 0x0820, + 0x0820, 0x0820, 0x0920, 0x0A38, + 0x0820, 0x0820, 0x0820, 0x0820, +}; + +static inline void assert_sizes(void) +{ + BUILD_BUG_ON(B43_TAB_ROTOR_SIZE != ARRAY_SIZE(b43_tab_rotor)); + BUILD_BUG_ON(B43_TAB_RETARD_SIZE != ARRAY_SIZE(b43_tab_retard)); + BUILD_BUG_ON(B43_TAB_FINEFREQA_SIZE != ARRAY_SIZE(b43_tab_finefreqa)); + BUILD_BUG_ON(B43_TAB_FINEFREQG_SIZE != ARRAY_SIZE(b43_tab_finefreqg)); + BUILD_BUG_ON(B43_TAB_NOISEA2_SIZE != ARRAY_SIZE(b43_tab_noisea2)); + BUILD_BUG_ON(B43_TAB_NOISEA3_SIZE != ARRAY_SIZE(b43_tab_noisea3)); + BUILD_BUG_ON(B43_TAB_NOISEG1_SIZE != ARRAY_SIZE(b43_tab_noiseg1)); + BUILD_BUG_ON(B43_TAB_NOISEG2_SIZE != ARRAY_SIZE(b43_tab_noiseg2)); + BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != + ARRAY_SIZE(b43_tab_noisescalea2)); + BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != + ARRAY_SIZE(b43_tab_noisescalea3)); + BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != + ARRAY_SIZE(b43_tab_noisescaleg1)); + BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != + ARRAY_SIZE(b43_tab_noisescaleg2)); + BUILD_BUG_ON(B43_TAB_NOISESCALE_SIZE != + ARRAY_SIZE(b43_tab_noisescaleg3)); + BUILD_BUG_ON(B43_TAB_SIGMASQR_SIZE != ARRAY_SIZE(b43_tab_sigmasqr1)); + BUILD_BUG_ON(B43_TAB_SIGMASQR_SIZE != ARRAY_SIZE(b43_tab_sigmasqr2)); + BUILD_BUG_ON(B43_TAB_RSSIAGC1_SIZE != ARRAY_SIZE(b43_tab_rssiagc1)); + BUILD_BUG_ON(B43_TAB_RSSIAGC2_SIZE != ARRAY_SIZE(b43_tab_rssiagc2)); +} + +u16 b43_ofdmtab_read16(struct b43_wldev *dev, u16 table, u16 offset) +{ + struct b43_phy_g *gphy = dev->phy.g; + u16 addr; + + addr = table + offset; + if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_READ) || + (addr - 1 != gphy->ofdmtab_addr)) { + /* The hardware has a different address in memory. Update it. */ + b43_phy_write(dev, B43_PHY_OTABLECTL, addr); + gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_READ; + } + gphy->ofdmtab_addr = addr; + + return b43_phy_read(dev, B43_PHY_OTABLEI); + + /* Some compiletime assertions... */ + assert_sizes(); +} + +void b43_ofdmtab_write16(struct b43_wldev *dev, u16 table, + u16 offset, u16 value) +{ + struct b43_phy_g *gphy = dev->phy.g; + u16 addr; + + addr = table + offset; + if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_WRITE) || + (addr - 1 != gphy->ofdmtab_addr)) { + /* The hardware has a different address in memory. Update it. */ + b43_phy_write(dev, B43_PHY_OTABLECTL, addr); + gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_WRITE; + } + gphy->ofdmtab_addr = addr; + b43_phy_write(dev, B43_PHY_OTABLEI, value); +} + +u32 b43_ofdmtab_read32(struct b43_wldev *dev, u16 table, u16 offset) +{ + struct b43_phy_g *gphy = dev->phy.g; + u32 ret; + u16 addr; + + addr = table + offset; + if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_READ) || + (addr - 1 != gphy->ofdmtab_addr)) { + /* The hardware has a different address in memory. Update it. */ + b43_phy_write(dev, B43_PHY_OTABLECTL, addr); + gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_READ; + } + gphy->ofdmtab_addr = addr; + ret = b43_phy_read(dev, B43_PHY_OTABLEQ); + ret <<= 16; + ret |= b43_phy_read(dev, B43_PHY_OTABLEI); + + return ret; +} + +void b43_ofdmtab_write32(struct b43_wldev *dev, u16 table, + u16 offset, u32 value) +{ + struct b43_phy_g *gphy = dev->phy.g; + u16 addr; + + addr = table + offset; + if ((gphy->ofdmtab_addr_direction != B43_OFDMTAB_DIRECTION_WRITE) || + (addr - 1 != gphy->ofdmtab_addr)) { + /* The hardware has a different address in memory. Update it. */ + b43_phy_write(dev, B43_PHY_OTABLECTL, addr); + gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_WRITE; + } + gphy->ofdmtab_addr = addr; + + b43_phy_write(dev, B43_PHY_OTABLEI, value); + b43_phy_write(dev, B43_PHY_OTABLEQ, (value >> 16)); +} + +u16 b43_gtab_read(struct b43_wldev *dev, u16 table, u16 offset) +{ + b43_phy_write(dev, B43_PHY_GTABCTL, table + offset); + return b43_phy_read(dev, B43_PHY_GTABDATA); +} + +void b43_gtab_write(struct b43_wldev *dev, u16 table, u16 offset, u16 value) +{ + b43_phy_write(dev, B43_PHY_GTABCTL, table + offset); + b43_phy_write(dev, B43_PHY_GTABDATA, value); +} diff --git a/drivers/net/wireless/broadcom/b43/tables.h b/drivers/net/wireless/broadcom/b43/tables.h new file mode 100644 index 000000000000..80e73c7cbac5 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/tables.h @@ -0,0 +1,34 @@ +#ifndef B43_TABLES_H_ +#define B43_TABLES_H_ + +#define B43_TAB_ROTOR_SIZE 53 +extern const u32 b43_tab_rotor[]; +#define B43_TAB_RETARD_SIZE 53 +extern const u32 b43_tab_retard[]; +#define B43_TAB_FINEFREQA_SIZE 256 +extern const u16 b43_tab_finefreqa[]; +#define B43_TAB_FINEFREQG_SIZE 256 +extern const u16 b43_tab_finefreqg[]; +#define B43_TAB_NOISEA2_SIZE 8 +extern const u16 b43_tab_noisea2[]; +#define B43_TAB_NOISEA3_SIZE 8 +extern const u16 b43_tab_noisea3[]; +#define B43_TAB_NOISEG1_SIZE 8 +extern const u16 b43_tab_noiseg1[]; +#define B43_TAB_NOISEG2_SIZE 8 +extern const u16 b43_tab_noiseg2[]; +#define B43_TAB_NOISESCALE_SIZE 27 +extern const u16 b43_tab_noisescalea2[]; +extern const u16 b43_tab_noisescalea3[]; +extern const u16 b43_tab_noisescaleg1[]; +extern const u16 b43_tab_noisescaleg2[]; +extern const u16 b43_tab_noisescaleg3[]; +#define B43_TAB_SIGMASQR_SIZE 53 +extern const u16 b43_tab_sigmasqr1[]; +extern const u16 b43_tab_sigmasqr2[]; +#define B43_TAB_RSSIAGC1_SIZE 16 +extern const u16 b43_tab_rssiagc1[]; +#define B43_TAB_RSSIAGC2_SIZE 48 +extern const u16 b43_tab_rssiagc2[]; + +#endif /* B43_TABLES_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/tables_lpphy.c b/drivers/net/wireless/broadcom/b43/tables_lpphy.c new file mode 100644 index 000000000000..cff187c5616d --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/tables_lpphy.c @@ -0,0 +1,2456 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11a/g LP-PHY and radio device data tables + + Copyright (c) 2009 Michael Buesch + Copyright (c) 2009 Gábor Stefanik + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "tables_lpphy.h" +#include "phy_common.h" +#include "phy_lp.h" + + +/* Entry of the 2062/2063 radio init table */ +struct b206x_init_tab_entry { + u16 offset; + u16 value_a; + u16 value_g; + u8 flags; +}; +#define B206X_FLAG_A 0x01 /* Flag: Init in A mode */ +#define B206X_FLAG_G 0x02 /* Flag: Init in G mode */ + +static const struct b206x_init_tab_entry b2062_init_tab[] = { + /* { .offset = B2062_N_COMM1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = 0x0001, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_N_COMM4, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_N_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_COMM15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_PDN_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_N_PDN_CTL1, .value_a = 0x0000, .value_g = 0x00CA, .flags = B206X_FLAG_G, }, + /* { .offset = B2062_N_PDN_CTL2, .value_a = 0x0018, .value_g = 0x0018, .flags = 0, }, */ + { .offset = B2062_N_PDN_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_N_PDN_CTL4, .value_a = 0x0015, .value_g = 0x002A, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_N_GEN_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_IQ_CALIB, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + { .offset = B2062_N_LGENC, .value_a = 0x00DB, .value_g = 0x00FF, .flags = B206X_FLAG_A, }, + /* { .offset = B2062_N_LGENA_LPF, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2062_N_LGENA_BIAS0, .value_a = 0x0041, .value_g = 0x0041, .flags = 0, }, */ + /* { .offset = B2062_N_LGNEA_BIAS1, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ + /* { .offset = B2062_N_LGENA_CTL0, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ + /* { .offset = B2062_N_LGENA_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_LGENA_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_N_LGENA_TUNE0, .value_a = 0x00DD, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_N_LGENA_TUNE1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_N_LGENA_TUNE2, .value_a = 0x00DD, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_N_LGENA_TUNE3, .value_a = 0x0077, .value_g = 0x00B5, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_N_LGENA_CTL3, .value_a = 0x0000, .value_g = 0x00FF, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_N_LGENA_CTL4, .value_a = 0x001F, .value_g = 0x001F, .flags = 0, }, */ + /* { .offset = B2062_N_LGENA_CTL5, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ + /* { .offset = B2062_N_LGENA_CTL6, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ + { .offset = B2062_N_LGENA_CTL7, .value_a = 0x0033, .value_g = 0x0033, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_N_RXA_CTL0, .value_a = 0x0009, .value_g = 0x0009, .flags = 0, }, */ + { .offset = B2062_N_RXA_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + /* { .offset = B2062_N_RXA_CTL2, .value_a = 0x0018, .value_g = 0x0018, .flags = 0, }, */ + /* { .offset = B2062_N_RXA_CTL3, .value_a = 0x0027, .value_g = 0x0027, .flags = 0, }, */ + /* { .offset = B2062_N_RXA_CTL4, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ + /* { .offset = B2062_N_RXA_CTL5, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ + /* { .offset = B2062_N_RXA_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_RXA_CTL7, .value_a = 0x0008, .value_g = 0x0008, .flags = 0, }, */ + { .offset = B2062_N_RXBB_CTL0, .value_a = 0x0082, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_N_RXBB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_GAIN0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_N_RXBB_GAIN1, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_N_RXBB_GAIN2, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_N_RXBB_GAIN3, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_RSSI0, .value_a = 0x0043, .value_g = 0x0043, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_RSSI1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_CALIB0, .value_a = 0x0010, .value_g = 0x0010, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_CALIB1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_CALIB2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_BIAS0, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_BIAS1, .value_a = 0x002A, .value_g = 0x002A, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_BIAS2, .value_a = 0x00AA, .value_g = 0x00AA, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_BIAS3, .value_a = 0x0021, .value_g = 0x0021, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_BIAS4, .value_a = 0x00AA, .value_g = 0x00AA, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_BIAS5, .value_a = 0x0022, .value_g = 0x0022, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_RSSI2, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_RSSI3, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_RSSI4, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2062_N_RXBB_RSSI5, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2062_N_TX_CTL0, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2062_N_TX_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_TX_CTL2, .value_a = 0x0084, .value_g = 0x0084, .flags = 0, }, */ + /* { .offset = B2062_N_TX_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_N_TX_CTL4, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_N_TX_CTL5, .value_a = 0x0002, .value_g = 0x0002, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_N_TX_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_TX_CTL7, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ + /* { .offset = B2062_N_TX_CTL8, .value_a = 0x0082, .value_g = 0x0082, .flags = 0, }, */ + /* { .offset = B2062_N_TX_CTL9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_TX_CTL_A, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_TX_GC2G, .value_a = 0x00FF, .value_g = 0x00FF, .flags = 0, }, */ + /* { .offset = B2062_N_TX_GC5G, .value_a = 0x00FF, .value_g = 0x00FF, .flags = 0, }, */ + { .offset = B2062_N_TX_TUNE, .value_a = 0x0088, .value_g = 0x001B, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_N_TX_PAD, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2062_N_TX_PGA, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2062_N_TX_PADAUX, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2062_N_TX_PGAAUX, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2062_N_TSSI_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_TSSI_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_TSSI_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_IQ_CALIB_CTL0, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2062_N_IQ_CALIB_CTL1, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2062_N_IQ_CALIB_CTL2, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ + /* { .offset = B2062_N_CALIB_TS, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_CALIB_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_CALIB_CTL1, .value_a = 0x0015, .value_g = 0x0015, .flags = 0, }, */ + /* { .offset = B2062_N_CALIB_CTL2, .value_a = 0x000F, .value_g = 0x000F, .flags = 0, }, */ + /* { .offset = B2062_N_CALIB_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_CALIB_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_CALIB_DBG0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_CALIB_DBG1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_CALIB_DBG2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_CALIB_DBG3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_PSENSE_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_PSENSE_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_PSENSE_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_N_TEST_BUF0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RADIO_ID_CODE, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_S_COMM4, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_S_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_COMM15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_S_PDS_CTL0, .value_a = 0x00FF, .value_g = 0x00FF, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_S_PDS_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_PDS_CTL2, .value_a = 0x008E, .value_g = 0x008E, .flags = 0, }, */ + /* { .offset = B2062_S_PDS_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_BG_CTL0, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ + /* { .offset = B2062_S_BG_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_BG_CTL2, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ + { .offset = B2062_S_LGENG_CTL0, .value_a = 0x00F8, .value_g = 0x00D8, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_LGENG_CTL1, .value_a = 0x003C, .value_g = 0x0024, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_S_LGENG_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_LGENG_CTL3, .value_a = 0x0041, .value_g = 0x0041, .flags = 0, }, */ + /* { .offset = B2062_S_LGENG_CTL4, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ + /* { .offset = B2062_S_LGENG_CTL5, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2062_S_LGENG_CTL6, .value_a = 0x0022, .value_g = 0x0022, .flags = 0, }, */ + /* { .offset = B2062_S_LGENG_CTL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_S_LGENG_CTL8, .value_a = 0x0088, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_S_LGENG_CTL9, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + { .offset = B2062_S_LGENG_CTL10, .value_a = 0x0088, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_S_LGENG_CTL11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL1, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL2, .value_a = 0x00AF, .value_g = 0x00AF, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL3, .value_a = 0x0012, .value_g = 0x0012, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL4, .value_a = 0x000B, .value_g = 0x000B, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL5, .value_a = 0x005F, .value_g = 0x005F, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL7, .value_a = 0x0040, .value_g = 0x0040, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL8, .value_a = 0x0052, .value_g = 0x0052, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL9, .value_a = 0x0026, .value_g = 0x0026, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL10, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL11, .value_a = 0x0036, .value_g = 0x0036, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL12, .value_a = 0x0057, .value_g = 0x0057, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL13, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL14, .value_a = 0x0075, .value_g = 0x0075, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL15, .value_a = 0x00B4, .value_g = 0x00B4, .flags = 0, }, */ + /* { .offset = B2062_S_REFPLL_CTL16, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_S_RFPLL_CTL0, .value_a = 0x0098, .value_g = 0x0098, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL1, .value_a = 0x0010, .value_g = 0x0010, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_S_RFPLL_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RFPLL_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RFPLL_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_S_RFPLL_CTL5, .value_a = 0x0043, .value_g = 0x0043, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL6, .value_a = 0x0047, .value_g = 0x0047, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL7, .value_a = 0x000C, .value_g = 0x000C, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL8, .value_a = 0x0011, .value_g = 0x0011, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL9, .value_a = 0x0011, .value_g = 0x0011, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL10, .value_a = 0x000E, .value_g = 0x000E, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL11, .value_a = 0x0008, .value_g = 0x0008, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL12, .value_a = 0x0033, .value_g = 0x0033, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL13, .value_a = 0x000A, .value_g = 0x000A, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL14, .value_a = 0x0006, .value_g = 0x0006, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_S_RFPLL_CTL15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RFPLL_CTL16, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RFPLL_CTL17, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_S_RFPLL_CTL18, .value_a = 0x003E, .value_g = 0x003E, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL19, .value_a = 0x0013, .value_g = 0x0013, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_S_RFPLL_CTL20, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_S_RFPLL_CTL21, .value_a = 0x0062, .value_g = 0x0062, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL22, .value_a = 0x0007, .value_g = 0x0007, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL23, .value_a = 0x0016, .value_g = 0x0016, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL24, .value_a = 0x005C, .value_g = 0x005C, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL25, .value_a = 0x0095, .value_g = 0x0095, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_S_RFPLL_CTL26, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RFPLL_CTL27, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RFPLL_CTL28, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RFPLL_CTL29, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_S_RFPLL_CTL30, .value_a = 0x00A0, .value_g = 0x00A0, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL31, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_S_RFPLL_CTL32, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2062_S_RFPLL_CTL33, .value_a = 0x00CC, .value_g = 0x00CC, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL34, .value_a = 0x0007, .value_g = 0x0007, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2062_S_RXG_CNT0, .value_a = 0x0010, .value_g = 0x0010, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT5, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT6, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT7, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + { .offset = B2062_S_RXG_CNT8, .value_a = 0x000F, .value_g = 0x000F, .flags = B206X_FLAG_A, }, + /* { .offset = B2062_S_RXG_CNT9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT10, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT11, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT12, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT13, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT14, .value_a = 0x00A0, .value_g = 0x00A0, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT15, .value_a = 0x0004, .value_g = 0x0004, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT16, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2062_S_RXG_CNT17, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ +}; + +static const struct b206x_init_tab_entry b2063_init_tab[] = { + { .offset = B2063_COMM1, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + /* { .offset = B2063_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_COMM10, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A, }, + /* { .offset = B2063_COMM11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM14, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ + /* { .offset = B2063_COMM15, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ + { .offset = B2063_COMM16, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM17, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM18, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM19, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM20, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM21, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM22, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM23, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM24, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + /* { .offset = B2063_PWR_SWITCH_CTL, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ + /* { .offset = B2063_PLL_SP1, .value_a = 0x003f, .value_g = 0x003f, .flags = 0, }, */ + /* { .offset = B2063_PLL_SP2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_LOGEN_SP1, .value_a = 0x00e8, .value_g = 0x00d4, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2063_LOGEN_SP2, .value_a = 0x00a7, .value_g = 0x0053, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_LOGEN_SP3, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + { .offset = B2063_LOGEN_SP4, .value_a = 0x00f0, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_LOGEN_SP5, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + { .offset = B2063_G_RX_SP1, .value_a = 0x001f, .value_g = 0x005e, .flags = B206X_FLAG_G, }, + { .offset = B2063_G_RX_SP2, .value_a = 0x007f, .value_g = 0x007e, .flags = B206X_FLAG_G, }, + { .offset = B2063_G_RX_SP3, .value_a = 0x0030, .value_g = 0x00f0, .flags = B206X_FLAG_G, }, + /* { .offset = B2063_G_RX_SP4, .value_a = 0x0035, .value_g = 0x0035, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SP5, .value_a = 0x003f, .value_g = 0x003f, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_G_RX_SP7, .value_a = 0x007f, .value_g = 0x007f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_G_RX_SP8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SP9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_G_RX_SP10, .value_a = 0x000c, .value_g = 0x000c, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_G_RX_SP11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_A_RX_SP1, .value_a = 0x003c, .value_g = 0x003f, .flags = B206X_FLAG_A, }, + { .offset = B2063_A_RX_SP2, .value_a = 0x00fc, .value_g = 0x00fe, .flags = B206X_FLAG_A, }, + /* { .offset = B2063_A_RX_SP3, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SP4, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SP5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_A_RX_SP7, .value_a = 0x0008, .value_g = 0x0008, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_RX_BB_SP1, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_SP2, .value_a = 0x0022, .value_g = 0x0022, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_SP3, .value_a = 0x00a8, .value_g = 0x00a8, .flags = 0, }, */ + { .offset = B2063_RX_BB_SP4, .value_a = 0x0060, .value_g = 0x0060, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_RX_BB_SP5, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_SP7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_RX_BB_SP8, .value_a = 0x0030, .value_g = 0x0030, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_TX_RF_SP1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP2, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ + { .offset = B2063_TX_RF_SP3, .value_a = 0x000c, .value_g = 0x000b, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2063_TX_RF_SP4, .value_a = 0x0010, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_TX_RF_SP5, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP6, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP7, .value_a = 0x0068, .value_g = 0x0068, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP8, .value_a = 0x0068, .value_g = 0x0068, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP9, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP10, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP11, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP12, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP13, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP14, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP15, .value_a = 0x00c0, .value_g = 0x00c0, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP16, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP17, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + { .offset = B2063_PA_SP1, .value_a = 0x003d, .value_g = 0x00fd, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_PA_SP2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ + /* { .offset = B2063_PA_SP3, .value_a = 0x0096, .value_g = 0x0096, .flags = 0, }, */ + /* { .offset = B2063_PA_SP4, .value_a = 0x005a, .value_g = 0x005a, .flags = 0, }, */ + /* { .offset = B2063_PA_SP5, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ + /* { .offset = B2063_PA_SP6, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ + /* { .offset = B2063_PA_SP7, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + { .offset = B2063_TX_BB_SP1, .value_a = 0x0002, .value_g = 0x0002, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_TX_BB_SP2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_BB_SP3, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ + /* { .offset = B2063_REG_SP1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_BANDGAP_CTL1, .value_a = 0x0056, .value_g = 0x0056, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_BANDGAP_CTL2, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ + /* { .offset = B2063_LPO_CTL1, .value_a = 0x000e, .value_g = 0x000e, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL1, .value_a = 0x007e, .value_g = 0x007e, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL2, .value_a = 0x0015, .value_g = 0x0015, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL3, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_CALNRST, .value_a = 0x0004, .value_g = 0x0004, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_IN_PLL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_IN_PLL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_CP1, .value_a = 0x00cf, .value_g = 0x00cf, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_CP2, .value_a = 0x0059, .value_g = 0x0059, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_CP3, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_CP4, .value_a = 0x0042, .value_g = 0x0042, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_LF1, .value_a = 0x00db, .value_g = 0x00db, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_LF2, .value_a = 0x0094, .value_g = 0x0094, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_LF3, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_LF4, .value_a = 0x0063, .value_g = 0x0063, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_SG1, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_SG2, .value_a = 0x00d3, .value_g = 0x00d3, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_SG3, .value_a = 0x00b1, .value_g = 0x00b1, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_SG4, .value_a = 0x003b, .value_g = 0x003b, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_SG5, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO1, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ + { .offset = B2063_PLL_JTAG_PLL_VCO2, .value_a = 0x00f7, .value_g = 0x00f7, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB3, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB5, .value_a = 0x0009, .value_g = 0x0009, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB6, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB7, .value_a = 0x0016, .value_g = 0x0016, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB8, .value_a = 0x006b, .value_g = 0x006b, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB10, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_XTAL_12, .value_a = 0x0004, .value_g = 0x0004, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_XTAL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_ACL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_ACL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_ACL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_ACL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_ACL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_INPUTS, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_WAITCNT, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVR1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVR2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL3, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL4, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL5, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL6, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL7, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CALVLD1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CALVLD2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_CALIB_EN, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_PEAKDET1, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_RCCR1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_VCOBUF1, .value_a = 0x0060, .value_g = 0x0060, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_MIXER1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_MIXER2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_BUF1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_BUF2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_DIV1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_DIV2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_DIV3, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_CBUFRX1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_CBUFRX2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_CBUFTX1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_CBUFTX2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_IDAC1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_SPARE1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_SPARE2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_SPARE3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_1ST1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_G_RX_1ST2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_1ST3, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND1, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND2, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND3, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND5, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND7, .value_a = 0x0035, .value_g = 0x0035, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PS1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PS2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PS3, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PS4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PS5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_MIX1, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ + /* { .offset = B2063_G_RX_MIX2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_G_RX_MIX3, .value_a = 0x0071, .value_g = 0x0071, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2063_G_RX_MIX4, .value_a = 0x0071, .value_g = 0x0071, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_G_RX_MIX5, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ + /* { .offset = B2063_G_RX_MIX6, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_G_RX_MIX7, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ + /* { .offset = B2063_G_RX_MIX8, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PDET1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SPARES1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SPARES2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SPARES3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_1ST1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_A_RX_1ST2, .value_a = 0x00f0, .value_g = 0x0030, .flags = B206X_FLAG_A, }, + /* { .offset = B2063_A_RX_1ST3, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_A_RX_1ST4, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_A_RX_1ST5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND1, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND4, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND7, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PS1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PS2, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PS3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PS4, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PS5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_A_RX_PS6, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_A_RX_MIX1, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_A_RX_MIX2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_MIX3, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ + { .offset = B2063_A_RX_MIX4, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2063_A_RX_MIX5, .value_a = 0x000f, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2063_A_RX_MIX6, .value_a = 0x000f, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_A_RX_MIX7, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ + /* { .offset = B2063_A_RX_MIX8, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PWRDET1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SPARE1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SPARE2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SPARE3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_RX_TIA_CTL1, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_RX_TIA_CTL2, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ + { .offset = B2063_RX_TIA_CTL3, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_RX_TIA_CTL4, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ + /* { .offset = B2063_RX_TIA_CTL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RX_TIA_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL1, .value_a = 0x0074, .value_g = 0x0074, .flags = 0, }, */ + { .offset = B2063_RX_BB_CTL2, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_RX_BB_CTL3, .value_a = 0x00a2, .value_g = 0x00a2, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL4, .value_a = 0x00aa, .value_g = 0x00aa, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL5, .value_a = 0x0024, .value_g = 0x0024, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL6, .value_a = 0x00a9, .value_g = 0x00a9, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL7, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL8, .value_a = 0x0010, .value_g = 0x0010, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL9, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL1, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_IDAC_LO_RF_I, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_IDAC_LO_RF_Q, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_IDAC_LO_BB_I, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_IDAC_LO_BB_Q, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL2, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL3, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL4, .value_a = 0x00b8, .value_g = 0x00b8, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL5, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL6, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL7, .value_a = 0x0078, .value_g = 0x0078, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL8, .value_a = 0x00c0, .value_g = 0x00c0, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL9, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_PA_CTL1, .value_a = 0x0000, .value_g = 0x0004, .flags = B206X_FLAG_A, }, + /* { .offset = B2063_PA_CTL2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL5, .value_a = 0x0096, .value_g = 0x0096, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL6, .value_a = 0x0077, .value_g = 0x0077, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL7, .value_a = 0x005a, .value_g = 0x005a, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL10, .value_a = 0x0021, .value_g = 0x0021, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL11, .value_a = 0x0070, .value_g = 0x0070, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_BB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_BB_CTL2, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ + /* { .offset = B2063_TX_BB_CTL3, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2063_TX_BB_CTL4, .value_a = 0x000b, .value_g = 0x000b, .flags = 0, }, */ + /* { .offset = B2063_GPIO_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_VREG_CTL1, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_AMUX_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_IQ_CALIB_GVAR, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ + /* { .offset = B2063_IQ_CALIB_CTL1, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2063_IQ_CALIB_CTL2, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ + /* { .offset = B2063_TEMPSENSE_CTL1, .value_a = 0x0046, .value_g = 0x0046, .flags = 0, }, */ + /* { .offset = B2063_TEMPSENSE_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_RX_LOOPBACK1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_RX_LOOPBACK2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_EXT_TSSI_CTL1, .value_a = 0x0021, .value_g = 0x0021, .flags = 0, }, */ + /* { .offset = B2063_EXT_TSSI_CTL2, .value_a = 0x0023, .value_g = 0x0023, .flags = 0, }, */ + /* { .offset = B2063_AFE_CTL , .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ +}; + +void b2062_upload_init_table(struct b43_wldev *dev) +{ + const struct b206x_init_tab_entry *e; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(b2062_init_tab); i++) { + e = &b2062_init_tab[i]; + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (!(e->flags & B206X_FLAG_G)) + continue; + b43_radio_write(dev, e->offset, e->value_g); + } else { + if (!(e->flags & B206X_FLAG_A)) + continue; + b43_radio_write(dev, e->offset, e->value_a); + } + } +} + +void b2063_upload_init_table(struct b43_wldev *dev) +{ + const struct b206x_init_tab_entry *e; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(b2063_init_tab); i++) { + e = &b2063_init_tab[i]; + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (!(e->flags & B206X_FLAG_G)) + continue; + b43_radio_write(dev, e->offset, e->value_g); + } else { + if (!(e->flags & B206X_FLAG_A)) + continue; + b43_radio_write(dev, e->offset, e->value_a); + } + } +} + +u32 b43_lptab_read(struct b43_wldev *dev, u32 offset) +{ + u32 type, value; + + type = offset & B43_LPTAB_TYPEMASK; + offset &= ~B43_LPTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + switch (type) { + case B43_LPTAB_8BIT: + b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_LPPHY_TABLEDATALO) & 0xFF; + break; + case B43_LPTAB_16BIT: + b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_LPPHY_TABLEDATALO); + break; + case B43_LPTAB_32BIT: + b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_LPPHY_TABLEDATAHI); + value <<= 16; + value |= b43_phy_read(dev, B43_LPPHY_TABLEDATALO); + break; + default: + B43_WARN_ON(1); + value = 0; + } + + return value; +} + +void b43_lptab_read_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, void *_data) +{ + u32 type; + u8 *data = _data; + unsigned int i; + + type = offset & B43_LPTAB_TYPEMASK; + offset &= ~B43_LPTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); + + for (i = 0; i < nr_elements; i++) { + switch (type) { + case B43_LPTAB_8BIT: + *data = b43_phy_read(dev, B43_LPPHY_TABLEDATALO) & 0xFF; + data++; + break; + case B43_LPTAB_16BIT: + *((u16 *)data) = b43_phy_read(dev, B43_LPPHY_TABLEDATALO); + data += 2; + break; + case B43_LPTAB_32BIT: + *((u32 *)data) = b43_phy_read(dev, B43_LPPHY_TABLEDATAHI); + *((u32 *)data) <<= 16; + *((u32 *)data) |= b43_phy_read(dev, B43_LPPHY_TABLEDATALO); + data += 4; + break; + default: + B43_WARN_ON(1); + } + } +} + +void b43_lptab_write(struct b43_wldev *dev, u32 offset, u32 value) +{ + u32 type; + + type = offset & B43_LPTAB_TYPEMASK; + offset &= ~B43_LPTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + switch (type) { + case B43_LPTAB_8BIT: + B43_WARN_ON(value & ~0xFF); + b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); + b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); + break; + case B43_LPTAB_16BIT: + B43_WARN_ON(value & ~0xFFFF); + b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); + b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); + break; + case B43_LPTAB_32BIT: + b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); + b43_phy_write(dev, B43_LPPHY_TABLEDATAHI, value >> 16); + b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); + break; + default: + B43_WARN_ON(1); + } +} + +void b43_lptab_write_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, const void *_data) +{ + u32 type, value; + const u8 *data = _data; + unsigned int i; + + type = offset & B43_LPTAB_TYPEMASK; + offset &= ~B43_LPTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); + + for (i = 0; i < nr_elements; i++) { + switch (type) { + case B43_LPTAB_8BIT: + value = *data; + data++; + B43_WARN_ON(value & ~0xFF); + b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); + break; + case B43_LPTAB_16BIT: + value = *((u16 *)data); + data += 2; + B43_WARN_ON(value & ~0xFFFF); + b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); + break; + case B43_LPTAB_32BIT: + value = *((u32 *)data); + data += 4; + b43_phy_write(dev, B43_LPPHY_TABLEDATAHI, value >> 16); + b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); + break; + default: + B43_WARN_ON(1); + } + } +} + +static const u8 lpphy_min_sig_sq_table[] = { + 0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xcf, 0xcd, + 0xca, 0xc7, 0xc4, 0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0x00, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xc1, 0xc4, 0xc7, 0xca, 0xcd, + 0xcf, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, +}; + +static const u16 lpphy_rev01_noise_scale_table[] = { + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa400, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0x00a4, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4c00, 0x2d36, + 0x0000, 0x0000, 0x4c00, 0x2d36, +}; + +static const u16 lpphy_rev2plus_noise_scale_table[] = { + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x0000, + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, + 0x00a4, +}; + +static const u16 lpphy_crs_gain_nft_table[] = { + 0x0366, 0x036a, 0x036f, 0x0364, 0x0367, 0x036d, 0x0374, 0x037f, 0x036f, + 0x037b, 0x038a, 0x0378, 0x0367, 0x036d, 0x0375, 0x0381, 0x0374, 0x0381, + 0x0392, 0x03a9, 0x03c4, 0x03e1, 0x0001, 0x001f, 0x0040, 0x005e, 0x007f, + 0x009e, 0x00bd, 0x00dd, 0x00fd, 0x011d, 0x013d, +}; + +static const u16 lpphy_rev01_filter_control_table[] = { + 0xa0fc, 0x10fc, 0x10db, 0x20b7, 0xff93, 0x10bf, 0x109b, 0x2077, 0xff53, + 0x0127, +}; + +static const u32 lpphy_rev2plus_filter_control_table[] = { + 0x000141fc, 0x000021fc, 0x000021b7, 0x0000416f, 0x0001ff27, 0x0000217f, + 0x00002137, 0x000040ef, 0x0001fea7, 0x0000024f, +}; + +static const u32 lpphy_rev01_ps_control_table[] = { + 0x00010000, 0x000000a0, 0x00040000, 0x00000048, 0x08080101, 0x00000080, + 0x08080101, 0x00000040, 0x08080101, 0x000000c0, 0x08a81501, 0x000000c0, + 0x0fe8fd01, 0x000000c0, 0x08300105, 0x000000c0, 0x08080201, 0x000000c0, + 0x08280205, 0x000000c0, 0xe80802fe, 0x000000c7, 0x28080206, 0x000000c0, + 0x08080202, 0x000000c0, 0x0ba87602, 0x000000c0, 0x1068013d, 0x000000c0, + 0x10280105, 0x000000c0, 0x08880102, 0x000000c0, 0x08280106, 0x000000c0, + 0xe80801fd, 0x000000c7, 0xa8080115, 0x000000c0, +}; + +static const u32 lpphy_rev2plus_ps_control_table[] = { + 0x00e38e08, 0x00e08e38, 0x00000000, 0x00000000, 0x00000000, 0x00002080, + 0x00006180, 0x00003002, 0x00000040, 0x00002042, 0x00180047, 0x00080043, + 0x00000041, 0x000020c1, 0x00046006, 0x00042002, 0x00040000, 0x00002003, + 0x00180006, 0x00080002, +}; + +static const u8 lpphy_pll_fraction_table[] = { + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +}; + +static const u16 lpphy_iqlo_cal_table[] = { + 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, + 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600, + 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, + 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const u16 lpphy_rev0_ofdm_cck_gain_table[] = { + 0x0001, 0x0001, 0x0001, 0x0001, 0x1001, 0x2001, 0x3001, 0x4001, 0x5001, + 0x6001, 0x7001, 0x7011, 0x7021, 0x2035, 0x2045, 0x2055, 0x2065, 0x2075, + 0x006d, 0x007d, 0x014d, 0x015d, 0x115d, 0x035d, 0x135d, 0x055d, 0x155d, + 0x0d5d, 0x1d5d, 0x2d5d, 0x555d, 0x655d, 0x755d, +}; + +static const u16 lpphy_rev1_ofdm_cck_gain_table[] = { + 0x5000, 0x6000, 0x7000, 0x0001, 0x1001, 0x2001, 0x3001, 0x4001, 0x5001, + 0x6001, 0x7001, 0x7011, 0x7021, 0x2035, 0x2045, 0x2055, 0x2065, 0x2075, + 0x006d, 0x007d, 0x014d, 0x015d, 0x115d, 0x035d, 0x135d, 0x055d, 0x155d, + 0x0d5d, 0x1d5d, 0x2d5d, 0x555d, 0x655d, 0x755d, +}; + +static const u16 lpphy_gain_delta_table[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const u32 lpphy_tx_power_control_table[] = { + 0x00000050, 0x0000004f, 0x0000004e, 0x0000004d, 0x0000004c, 0x0000004b, + 0x0000004a, 0x00000049, 0x00000048, 0x00000047, 0x00000046, 0x00000045, + 0x00000044, 0x00000043, 0x00000042, 0x00000041, 0x00000040, 0x0000003f, + 0x0000003e, 0x0000003d, 0x0000003c, 0x0000003b, 0x0000003a, 0x00000039, + 0x00000038, 0x00000037, 0x00000036, 0x00000035, 0x00000034, 0x00000033, + 0x00000032, 0x00000031, 0x00000030, 0x0000002f, 0x0000002e, 0x0000002d, + 0x0000002c, 0x0000002b, 0x0000002a, 0x00000029, 0x00000028, 0x00000027, + 0x00000026, 0x00000025, 0x00000024, 0x00000023, 0x00000022, 0x00000021, + 0x00000020, 0x0000001f, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001b, + 0x0000001a, 0x00000019, 0x00000018, 0x00000017, 0x00000016, 0x00000015, + 0x00000014, 0x00000013, 0x00000012, 0x00000011, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x000075a0, 0x000075a0, 0x000075a1, 0x000075a1, 0x000075a2, 0x000075a2, + 0x000075a3, 0x000075a3, 0x000074b0, 0x000074b0, 0x000074b1, 0x000074b1, + 0x000074b2, 0x000074b2, 0x000074b3, 0x000074b3, 0x00006d20, 0x00006d20, + 0x00006d21, 0x00006d21, 0x00006d22, 0x00006d22, 0x00006d23, 0x00006d23, + 0x00004660, 0x00004660, 0x00004661, 0x00004661, 0x00004662, 0x00004662, + 0x00004663, 0x00004663, 0x00003e60, 0x00003e60, 0x00003e61, 0x00003e61, + 0x00003e62, 0x00003e62, 0x00003e63, 0x00003e63, 0x00003660, 0x00003660, + 0x00003661, 0x00003661, 0x00003662, 0x00003662, 0x00003663, 0x00003663, + 0x00002e60, 0x00002e60, 0x00002e61, 0x00002e61, 0x00002e62, 0x00002e62, + 0x00002e63, 0x00002e63, 0x00002660, 0x00002660, 0x00002661, 0x00002661, + 0x00002662, 0x00002662, 0x00002663, 0x00002663, 0x000025e0, 0x000025e0, + 0x000025e1, 0x000025e1, 0x000025e2, 0x000025e2, 0x000025e3, 0x000025e3, + 0x00001de0, 0x00001de0, 0x00001de1, 0x00001de1, 0x00001de2, 0x00001de2, + 0x00001de3, 0x00001de3, 0x00001d60, 0x00001d60, 0x00001d61, 0x00001d61, + 0x00001d62, 0x00001d62, 0x00001d63, 0x00001d63, 0x00001560, 0x00001560, + 0x00001561, 0x00001561, 0x00001562, 0x00001562, 0x00001563, 0x00001563, + 0x00000d60, 0x00000d60, 0x00000d61, 0x00000d61, 0x00000d62, 0x00000d62, + 0x00000d63, 0x00000d63, 0x00000ce0, 0x00000ce0, 0x00000ce1, 0x00000ce1, + 0x00000ce2, 0x00000ce2, 0x00000ce3, 0x00000ce3, 0x00000e10, 0x00000e10, + 0x00000e11, 0x00000e11, 0x00000e12, 0x00000e12, 0x00000e13, 0x00000e13, + 0x00000bf0, 0x00000bf0, 0x00000bf1, 0x00000bf1, 0x00000bf2, 0x00000bf2, + 0x00000bf3, 0x00000bf3, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x000000ff, 0x000002fc, + 0x0000fa08, 0x00000305, 0x00000206, 0x00000304, 0x0000fb04, 0x0000fcff, + 0x000005fb, 0x0000fd01, 0x00000401, 0x00000006, 0x0000ff03, 0x000007fc, + 0x0000fc08, 0x00000203, 0x0000fffb, 0x00000600, 0x0000fa01, 0x0000fc03, + 0x0000fe06, 0x0000fe00, 0x00000102, 0x000007fd, 0x000004fb, 0x000006ff, + 0x000004fd, 0x0000fdfa, 0x000007fb, 0x0000fdfa, 0x0000fa06, 0x00000500, + 0x0000f902, 0x000007fa, 0x0000fafa, 0x00000500, 0x000007fa, 0x00000700, + 0x00000305, 0x000004ff, 0x00000801, 0x00000503, 0x000005f9, 0x00000404, + 0x0000fb08, 0x000005fd, 0x00000501, 0x00000405, 0x0000fb03, 0x000007fc, + 0x00000403, 0x00000303, 0x00000402, 0x0000faff, 0x0000fe05, 0x000005fd, + 0x0000fe01, 0x000007fa, 0x00000202, 0x00000504, 0x00000102, 0x000008fe, + 0x0000fa04, 0x0000fafc, 0x0000fe08, 0x000000f9, 0x000002fa, 0x000003fe, + 0x00000304, 0x000004f9, 0x00000100, 0x0000fd06, 0x000008fc, 0x00000701, + 0x00000504, 0x0000fdfe, 0x0000fdfc, 0x000003fe, 0x00000704, 0x000002fc, + 0x000004f9, 0x0000fdfd, 0x0000fa07, 0x00000205, 0x000003fd, 0x000005fb, + 0x000004f9, 0x00000804, 0x0000fc06, 0x0000fcf9, 0x00000100, 0x0000fe05, + 0x00000408, 0x0000fb02, 0x00000304, 0x000006fe, 0x000004fa, 0x00000305, + 0x000008fc, 0x00000102, 0x000001fd, 0x000004fc, 0x0000fe03, 0x00000701, + 0x000001fb, 0x000001f9, 0x00000206, 0x000006fd, 0x00000508, 0x00000700, + 0x00000304, 0x000005fe, 0x000005ff, 0x0000fa04, 0x00000303, 0x0000fefb, + 0x000007f9, 0x0000fefc, 0x000004fd, 0x000005fc, 0x0000fffd, 0x0000fc08, + 0x0000fbf9, 0x0000fd07, 0x000008fb, 0x0000fe02, 0x000006fb, 0x00000702, +}; + +static const u32 lpphy_gain_idx_table[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x10000001, 0x00000000, 0x20000082, 0x00000000, 0x40000104, 0x00000000, + 0x60004207, 0x00000001, 0x7000838a, 0x00000001, 0xd021050d, 0x00000001, + 0xe041c683, 0x00000001, 0x50828805, 0x00000000, 0x80e34288, 0x00000000, + 0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000, 0x12064711, 0x00000001, + 0xb0a18612, 0x00000010, 0xe1024794, 0x00000010, 0x11630915, 0x00000011, + 0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018, 0xf1e50da0, 0x00000018, + 0x22468e21, 0x00000019, 0x4286d023, 0x00000019, 0xa347d0a4, 0x00000019, + 0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019, 0x0408d329, 0x0000001a, + 0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a, 0x54aa152c, 0x0000001a, + 0x64ca55ad, 0x0000001a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x10000001, 0x00000000, 0x20000082, 0x00000000, + 0x40000104, 0x00000000, 0x60004207, 0x00000001, 0x7000838a, 0x00000001, + 0xd021050d, 0x00000001, 0xe041c683, 0x00000001, 0x50828805, 0x00000000, + 0x80e34288, 0x00000000, 0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000, + 0x12064711, 0x00000001, 0xb0a18612, 0x00000010, 0xe1024794, 0x00000010, + 0x11630915, 0x00000011, 0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018, + 0xf1e50da0, 0x00000018, 0x22468e21, 0x00000019, 0x4286d023, 0x00000019, + 0xa347d0a4, 0x00000019, 0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019, + 0x0408d329, 0x0000001a, 0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a, + 0x54aa152c, 0x0000001a, 0x64ca55ad, 0x0000001a, +}; + +static const u16 lpphy_aux_gain_idx_table[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0002, 0x0004, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0016, +}; + +static const u32 lpphy_gain_value_table[] = { + 0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004, + 0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a, + 0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006, + 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000, + 0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f, + 0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000009, 0x000000f1, + 0x00000000, 0x00000000, +}; + +static const u16 lpphy_gain_table[] = { + 0x0000, 0x0400, 0x0800, 0x0802, 0x0804, 0x0806, 0x0807, 0x0808, 0x080a, + 0x080b, 0x080c, 0x080e, 0x080f, 0x0810, 0x0812, 0x0813, 0x0814, 0x0816, + 0x0817, 0x081a, 0x081b, 0x081f, 0x0820, 0x0824, 0x0830, 0x0834, 0x0837, + 0x083b, 0x083f, 0x0840, 0x0844, 0x0857, 0x085b, 0x085f, 0x08d7, 0x08db, + 0x08df, 0x0957, 0x095b, 0x095f, 0x0b57, 0x0b5b, 0x0b5f, 0x0f5f, 0x135f, + 0x175f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const u32 lpphy_a0_gain_idx_table[] = { + 0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065, + 0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f, + 0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca, + 0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e, + 0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193, + 0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a, + 0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325, + 0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471, + 0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646, + 0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd, + 0x00036963, 0x000339f2, 0x00030a89, 0x0002db28, +}; + +static const u16 lpphy_a0_aux_gain_idx_table[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0002, 0x0014, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0014, +}; + +static const u32 lpphy_a0_gain_value_table[] = { + 0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004, + 0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a, + 0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006, + 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000, + 0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f, + 0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000f, 0x000000f7, + 0x00000000, 0x00000000, +}; + +static const u16 lpphy_a0_gain_table[] = { + 0x0000, 0x0002, 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, + 0x000e, 0x000f, 0x0010, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x001a, + 0x001b, 0x001f, 0x0020, 0x0024, 0x0030, 0x0034, 0x0037, 0x003b, 0x003f, + 0x0040, 0x0044, 0x0057, 0x005b, 0x005f, 0x00d7, 0x00db, 0x00df, 0x0157, + 0x015b, 0x015f, 0x0357, 0x035b, 0x035f, 0x075f, 0x0b5f, 0x0f5f, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const u16 lpphy_sw_control_table[] = { + 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0128, + 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0009, 0x0009, + 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0018, 0x0018, 0x0018, + 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0128, 0x0128, 0x0009, 0x0009, + 0x0028, 0x0028, 0x0028, 0x0028, 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, + 0x0028, 0x0028, 0x0028, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, + 0x0009, 0x0009, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, + 0x0018, +}; + +static const u8 lpphy_hf_table[] = { + 0x4b, 0x36, 0x24, 0x18, 0x49, 0x34, 0x23, 0x17, 0x48, + 0x33, 0x23, 0x17, 0x48, 0x33, 0x23, 0x17, +}; + +static const u32 lpphy_papd_eps_table[] = { + 0x00000000, 0x00013ffc, 0x0001dff3, 0x0001bff0, 0x00023fe9, 0x00021fdf, + 0x00028fdf, 0x00033fd2, 0x00039fcb, 0x00043fc7, 0x0004efc2, 0x00055fb5, + 0x0005cfb0, 0x00063fa8, 0x00068fa3, 0x00071f98, 0x0007ef92, 0x00084f8b, + 0x0008df82, 0x00097f77, 0x0009df69, 0x000a3f62, 0x000adf57, 0x000b6f4c, + 0x000bff41, 0x000c9f39, 0x000cff30, 0x000dbf27, 0x000e4f1e, 0x000edf16, + 0x000f7f13, 0x00102f11, 0x00110f10, 0x0011df11, 0x0012ef15, 0x00143f1c, + 0x00158f27, 0x00172f35, 0x00193f47, 0x001baf5f, 0x001e6f7e, 0x0021cfa4, + 0x0025bfd2, 0x002a2008, 0x002fb047, 0x00360090, 0x003d40e0, 0x0045c135, + 0x004fb189, 0x005ae1d7, 0x0067221d, 0x0075025a, 0x007ff291, 0x007ff2bf, + 0x007ff2e3, 0x007ff2ff, 0x007ff315, 0x007ff329, 0x007ff33f, 0x007ff356, + 0x007ff36e, 0x007ff39c, 0x007ff441, 0x007ff506, +}; + +static const u32 lpphy_papd_mult_table[] = { + 0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065, + 0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f, + 0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca, + 0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e, + 0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193, + 0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a, + 0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325, + 0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471, + 0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646, + 0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd, + 0x00036963, 0x000339f2, 0x00030a89, 0x0002db28, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev0_nopa_tx_gain_table[] = { + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 152, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 147, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 143, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 139, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 135, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 131, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 128, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 124, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 121, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 117, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 114, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 111, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 107, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 104, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 101, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 99, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 96, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 93, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 90, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 88, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 85, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 83, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 81, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 78, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 76, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 74, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev0_2ghz_tx_gain_table[] = { + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 71, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 69, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 58, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 58, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 57, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 83, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 81, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 78, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 76, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 74, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 72, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev0_5ghz_tx_gain_table[] = { + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 99, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 96, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 93, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 55, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 55, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev1_nopa_tx_gain_table[] = { + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 152, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 147, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 143, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 139, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 135, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 131, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 128, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 124, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 121, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 117, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 114, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 111, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 107, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 104, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 101, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 99, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 96, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 93, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 90, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 88, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 85, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 83, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 81, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 78, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 76, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 74, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev1_2ghz_tx_gain_table[] = { + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 60, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev1_5ghz_tx_gain_table[] = { + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 99, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 96, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 93, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 55, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 55, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev2_nopa_tx_gain_table[] = { + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 152, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 147, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 143, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 139, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 135, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 131, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 128, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 124, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 121, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 117, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 114, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 111, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 107, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 104, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 101, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 99, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 96, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 93, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 90, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 88, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 85, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 83, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 81, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 78, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 76, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 74, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 72, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 70, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 68, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 66, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 197, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 192, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 186, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 181, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 176, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 171, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 166, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 161, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 157, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 152, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 148, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 144, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 140, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 136, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 132, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 128, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 124, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 121, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 117, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 114, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 111, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 108, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 105, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 102, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 99, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 96, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 93, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 91, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 88, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 86, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 83, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 81, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 79, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 76, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 74, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 72, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 70, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 68, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 66, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 64, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 248, .pad = 64, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 248, .pad = 62, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 241, .pad = 62, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 241, .pad = 60, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 234, .pad = 60, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 234, .pad = 59, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 227, .pad = 59, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 227, .pad = 57, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 221, .pad = 57, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 221, .pad = 55, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 215, .pad = 55, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 215, .pad = 54, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 208, .pad = 54, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 208, .pad = 52, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 203, .pad = 52, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 203, .pad = 51, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 197, .pad = 51, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 197, .pad = 49, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 191, .pad = 49, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 191, .pad = 48, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 186, .pad = 48, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 186, .pad = 47, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 181, .pad = 47, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 181, .pad = 45, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 175, .pad = 45, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 175, .pad = 44, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 170, .pad = 44, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 170, .pad = 43, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 166, .pad = 43, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 166, .pad = 42, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 161, .pad = 42, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 161, .pad = 40, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 156, .pad = 40, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 156, .pad = 39, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 152, .pad = 39, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 152, .pad = 38, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 148, .pad = 38, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 148, .pad = 37, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 143, .pad = 37, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 143, .pad = 36, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 139, .pad = 36, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 139, .pad = 35, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 135, .pad = 35, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 135, .pad = 34, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 132, .pad = 34, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 132, .pad = 33, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 128, .pad = 33, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 128, .pad = 32, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 124, .pad = 32, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 124, .pad = 31, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 121, .pad = 31, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 121, .pad = 30, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 117, .pad = 30, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 117, .pad = 29, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 114, .pad = 29, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 114, .pad = 29, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 111, .pad = 29, .dac = 0, .bb_mult = 64, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev2_2ghz_tx_gain_table[] = { + { .gm = 7, .pga = 99, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 96, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 93, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 90, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 88, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 85, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 83, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 81, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 78, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 76, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 74, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 72, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 70, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 68, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 66, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 62, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 62, .pad = 248, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 60, .pad = 248, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 60, .pad = 241, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 59, .pad = 241, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 59, .pad = 234, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 57, .pad = 234, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 57, .pad = 227, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 55, .pad = 227, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 55, .pad = 221, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 54, .pad = 221, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 54, .pad = 215, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 52, .pad = 215, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 52, .pad = 208, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 51, .pad = 208, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 51, .pad = 203, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 49, .pad = 203, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 49, .pad = 197, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 48, .pad = 197, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 48, .pad = 191, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 47, .pad = 191, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 47, .pad = 186, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 45, .pad = 186, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 45, .pad = 181, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 44, .pad = 181, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 44, .pad = 175, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 43, .pad = 175, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 43, .pad = 170, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 42, .pad = 170, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 42, .pad = 166, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 40, .pad = 166, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 40, .pad = 161, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 39, .pad = 161, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 39, .pad = 156, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 38, .pad = 156, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 38, .pad = 152, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 37, .pad = 152, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 37, .pad = 148, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 36, .pad = 148, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 36, .pad = 143, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 35, .pad = 143, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 35, .pad = 139, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 34, .pad = 139, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 34, .pad = 135, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 33, .pad = 135, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 33, .pad = 132, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 32, .pad = 132, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 32, .pad = 128, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 31, .pad = 128, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 31, .pad = 124, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 30, .pad = 124, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 30, .pad = 121, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 29, .pad = 121, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 29, .pad = 117, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 29, .pad = 117, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 29, .pad = 114, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 28, .pad = 114, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 28, .pad = 111, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 27, .pad = 111, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 27, .pad = 108, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 26, .pad = 108, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 26, .pad = 104, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 25, .pad = 104, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 25, .pad = 102, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 25, .pad = 102, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 25, .pad = 99, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 24, .pad = 99, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 24, .pad = 96, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 23, .pad = 96, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 23, .pad = 93, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 23, .pad = 93, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 23, .pad = 90, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 22, .pad = 90, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 22, .pad = 88, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 21, .pad = 88, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 21, .pad = 85, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 21, .pad = 85, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 21, .pad = 83, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 20, .pad = 83, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 20, .pad = 81, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 20, .pad = 81, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 20, .pad = 78, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 19, .pad = 78, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 19, .pad = 76, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 19, .pad = 76, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 19, .pad = 74, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 18, .pad = 74, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 18, .pad = 72, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 18, .pad = 72, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 18, .pad = 70, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 17, .pad = 70, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 17, .pad = 68, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 17, .pad = 68, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 17, .pad = 66, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 16, .pad = 66, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 16, .pad = 64, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 16, .pad = 64, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 16, .pad = 62, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 62, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 60, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 60, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 59, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 59, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 57, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 57, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 55, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 55, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 54, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 54, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 52, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 52, .dac = 0, .bb_mult = 64, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev2_5ghz_tx_gain_table[] = { + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 152, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 147, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 143, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 139, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 135, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 131, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 128, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 124, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 121, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 117, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 114, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 111, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 107, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 104, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 101, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 99, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 96, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 93, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 90, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 88, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 85, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 83, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 81, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 78, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 76, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 74, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 72, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 70, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 68, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 66, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 248, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 241, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 234, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 227, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 221, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 215, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 208, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 197, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 191, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 186, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 181, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 175, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 170, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 166, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 161, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 156, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 152, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 148, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 143, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 139, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 135, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 132, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 128, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 124, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 121, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 117, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 114, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 111, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 108, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 104, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 102, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 99, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 96, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 93, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 90, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 88, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 85, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 83, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 81, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 78, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 76, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 74, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 72, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 70, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 68, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 66, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 64, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 64, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 62, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 248, .pad = 62, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 248, .pad = 60, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 241, .pad = 60, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 241, .pad = 59, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 234, .pad = 59, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 234, .pad = 57, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 227, .pad = 57, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 227, .pad = 55, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 221, .pad = 55, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 221, .pad = 54, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 215, .pad = 54, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 215, .pad = 52, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 208, .pad = 52, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 208, .pad = 51, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 203, .pad = 51, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 203, .pad = 49, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 197, .pad = 49, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 197, .pad = 48, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 191, .pad = 48, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 191, .pad = 47, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 186, .pad = 47, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 186, .pad = 45, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 181, .pad = 45, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 181, .pad = 44, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 175, .pad = 44, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 175, .pad = 43, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 170, .pad = 43, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 170, .pad = 42, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 166, .pad = 42, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 166, .pad = 40, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 161, .pad = 40, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 161, .pad = 39, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 156, .pad = 39, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 156, .pad = 38, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 152, .pad = 38, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 152, .pad = 37, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 148, .pad = 37, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 148, .pad = 36, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 143, .pad = 36, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 143, .pad = 35, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 139, .pad = 35, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 139, .pad = 34, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 135, .pad = 34, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 135, .pad = 33, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 132, .pad = 33, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 132, .pad = 32, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 128, .pad = 32, .dac = 0, .bb_mult = 64, }, +}; + +void lpphy_rev0_1_table_init(struct b43_wldev *dev) +{ + B43_WARN_ON(dev->phy.rev >= 2); + + b43_lptab_write_bulk(dev, B43_LPTAB8(2, 0), + ARRAY_SIZE(lpphy_min_sig_sq_table), lpphy_min_sig_sq_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(1, 0), + ARRAY_SIZE(lpphy_rev01_noise_scale_table), lpphy_rev01_noise_scale_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), + ARRAY_SIZE(lpphy_crs_gain_nft_table), lpphy_crs_gain_nft_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(8, 0), + ARRAY_SIZE(lpphy_rev01_filter_control_table), lpphy_rev01_filter_control_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(9, 0), + ARRAY_SIZE(lpphy_rev01_ps_control_table), lpphy_rev01_ps_control_table); + b43_lptab_write_bulk(dev, B43_LPTAB8(6, 0), + ARRAY_SIZE(lpphy_pll_fraction_table), lpphy_pll_fraction_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(0, 0), + ARRAY_SIZE(lpphy_iqlo_cal_table), lpphy_iqlo_cal_table); + if (dev->phy.rev == 0) { + b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), + ARRAY_SIZE(lpphy_rev0_ofdm_cck_gain_table), lpphy_rev0_ofdm_cck_gain_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), + ARRAY_SIZE(lpphy_rev0_ofdm_cck_gain_table), lpphy_rev0_ofdm_cck_gain_table); + } else { + b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), + ARRAY_SIZE(lpphy_rev1_ofdm_cck_gain_table), lpphy_rev1_ofdm_cck_gain_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), + ARRAY_SIZE(lpphy_rev1_ofdm_cck_gain_table), lpphy_rev1_ofdm_cck_gain_table); +} + b43_lptab_write_bulk(dev, B43_LPTAB16(15, 0), + ARRAY_SIZE(lpphy_gain_delta_table), lpphy_gain_delta_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0), + ARRAY_SIZE(lpphy_tx_power_control_table), lpphy_tx_power_control_table); +} + +void lpphy_rev2plus_table_init(struct b43_wldev *dev) +{ + int i; + + B43_WARN_ON(dev->phy.rev < 2); + + for (i = 0; i < 704; i++) + b43_lptab_write(dev, B43_LPTAB32(7, i), 0); + + b43_lptab_write_bulk(dev, B43_LPTAB8(2, 0), + ARRAY_SIZE(lpphy_min_sig_sq_table), lpphy_min_sig_sq_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(1, 0), + ARRAY_SIZE(lpphy_rev2plus_noise_scale_table), lpphy_rev2plus_noise_scale_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(11, 0), + ARRAY_SIZE(lpphy_rev2plus_filter_control_table), lpphy_rev2plus_filter_control_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(12, 0), + ARRAY_SIZE(lpphy_rev2plus_ps_control_table), lpphy_rev2plus_ps_control_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(13, 0), + ARRAY_SIZE(lpphy_gain_idx_table), lpphy_gain_idx_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), + ARRAY_SIZE(lpphy_aux_gain_idx_table), lpphy_aux_gain_idx_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(15, 0), + ARRAY_SIZE(lpphy_sw_control_table), lpphy_sw_control_table); + b43_lptab_write_bulk(dev, B43_LPTAB8(16, 0), + ARRAY_SIZE(lpphy_hf_table), lpphy_hf_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(17, 0), + ARRAY_SIZE(lpphy_gain_value_table), lpphy_gain_value_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(18, 0), + ARRAY_SIZE(lpphy_gain_table), lpphy_gain_table); + b43_lptab_write_bulk(dev, B43_LPTAB8(6, 0), + ARRAY_SIZE(lpphy_pll_fraction_table), lpphy_pll_fraction_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(0, 0), + ARRAY_SIZE(lpphy_iqlo_cal_table), lpphy_iqlo_cal_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(9, 0), + ARRAY_SIZE(lpphy_papd_eps_table), lpphy_papd_eps_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0), + ARRAY_SIZE(lpphy_papd_mult_table), lpphy_papd_mult_table); + + if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) { + b43_lptab_write_bulk(dev, B43_LPTAB32(13, 0), + ARRAY_SIZE(lpphy_a0_gain_idx_table), lpphy_a0_gain_idx_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), + ARRAY_SIZE(lpphy_a0_aux_gain_idx_table), lpphy_a0_aux_gain_idx_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(17, 0), + ARRAY_SIZE(lpphy_a0_gain_value_table), lpphy_a0_gain_value_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(18, 0), + ARRAY_SIZE(lpphy_a0_gain_table), lpphy_a0_gain_table); + } +} + +static void lpphy_rev0_1_write_gain_table(struct b43_wldev *dev, int offset, + struct lpphy_tx_gain_table_entry data) +{ + u32 tmp; + + B43_WARN_ON(dev->phy.rev >= 2); + + tmp = data.pad << 11; + tmp |= data.pga << 7; + tmp |= data.gm << 4; + tmp |= data.dac; + b43_lptab_write(dev, B43_LPTAB32(10, 0xC0 + offset), tmp); + tmp = data.bb_mult << 20; + b43_lptab_write(dev, B43_LPTAB32(10, 0x140 + offset), tmp); +} + +static void lpphy_rev2plus_write_gain_table(struct b43_wldev *dev, int offset, + struct lpphy_tx_gain_table_entry data) +{ + u32 tmp; + + B43_WARN_ON(dev->phy.rev < 2); + + tmp = data.pad << 16; + tmp |= data.pga << 8; + tmp |= data.gm; + if (dev->phy.rev >= 3) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + tmp |= 0x10 << 24; + else + tmp |= 0x70 << 24; + } else { + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + tmp |= 0x14 << 24; + else + tmp |= 0x7F << 24; + } + b43_lptab_write(dev, B43_LPTAB32(7, 0xC0 + offset), tmp); + tmp = data.bb_mult << 20; + tmp |= data.dac << 28; + b43_lptab_write(dev, B43_LPTAB32(7, 0x140 + offset), tmp); +} + +void lpphy_write_gain_table(struct b43_wldev *dev, int offset, + struct lpphy_tx_gain_table_entry data) +{ + if (dev->phy.rev >= 2) + lpphy_rev2plus_write_gain_table(dev, offset, data); + else + lpphy_rev0_1_write_gain_table(dev, offset, data); +} + +void lpphy_write_gain_table_bulk(struct b43_wldev *dev, int offset, int count, + struct lpphy_tx_gain_table_entry *table) +{ + int i; + + for (i = offset; i < count; i++) + lpphy_write_gain_table(dev, i, table[i]); +} + +void lpphy_init_tx_gain_table(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + + switch (dev->phy.rev) { + case 0: + if ((sprom->boardflags_hi & B43_BFH_NOPA) || + (sprom->boardflags_lo & B43_BFL_HGPA)) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev0_nopa_tx_gain_table); + else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev0_2ghz_tx_gain_table); + else + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev0_5ghz_tx_gain_table); + break; + case 1: + if ((sprom->boardflags_hi & B43_BFH_NOPA) || + (sprom->boardflags_lo & B43_BFL_HGPA)) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev1_nopa_tx_gain_table); + else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev1_2ghz_tx_gain_table); + else + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev1_5ghz_tx_gain_table); + break; + default: + if (sprom->boardflags_hi & B43_BFH_NOPA) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev2_nopa_tx_gain_table); + else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev2_2ghz_tx_gain_table); + else + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev2_5ghz_tx_gain_table); + } +} diff --git a/drivers/net/wireless/broadcom/b43/tables_lpphy.h b/drivers/net/wireless/broadcom/b43/tables_lpphy.h new file mode 100644 index 000000000000..84f1d265f657 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/tables_lpphy.h @@ -0,0 +1,44 @@ +#ifndef B43_TABLES_LPPHY_H_ +#define B43_TABLES_LPPHY_H_ + + +#define B43_LPTAB_TYPEMASK 0xF0000000 +#define B43_LPTAB_8BIT 0x10000000 +#define B43_LPTAB_16BIT 0x20000000 +#define B43_LPTAB_32BIT 0x30000000 +#define B43_LPTAB8(table, offset) (((table) << 10) | (offset) | B43_LPTAB_8BIT) +#define B43_LPTAB16(table, offset) (((table) << 10) | (offset) | B43_LPTAB_16BIT) +#define B43_LPTAB32(table, offset) (((table) << 10) | (offset) | B43_LPTAB_32BIT) + +/* Table definitions */ +#define B43_LPTAB_TXPWR_R2PLUS B43_LPTAB32(0x07, 0) /* TX power lookup table (rev >= 2) */ +#define B43_LPTAB_TXPWR_R0_1 B43_LPTAB32(0xA0, 0) /* TX power lookup table (rev < 2) */ + +u32 b43_lptab_read(struct b43_wldev *dev, u32 offset); +void b43_lptab_write(struct b43_wldev *dev, u32 offset, u32 value); + +/* Bulk table access. Note that these functions return the bulk data in + * host endianness! The returned data is _not_ a bytearray, but an array + * consisting of nr_elements of the data type. */ +void b43_lptab_read_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, void *data); +void b43_lptab_write_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, const void *data); + +void b2062_upload_init_table(struct b43_wldev *dev); +void b2063_upload_init_table(struct b43_wldev *dev); + +struct lpphy_tx_gain_table_entry { + u8 gm, pga, pad, dac, bb_mult; +}; + +void lpphy_write_gain_table(struct b43_wldev *dev, int offset, + struct lpphy_tx_gain_table_entry data); +void lpphy_write_gain_table_bulk(struct b43_wldev *dev, int offset, int count, + struct lpphy_tx_gain_table_entry *table); + +void lpphy_rev0_1_table_init(struct b43_wldev *dev); +void lpphy_rev2plus_table_init(struct b43_wldev *dev); +void lpphy_init_tx_gain_table(struct b43_wldev *dev); + +#endif /* B43_TABLES_LPPHY_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/tables_nphy.c b/drivers/net/wireless/broadcom/b43/tables_nphy.c new file mode 100644 index 000000000000..b2f0d245bcf3 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/tables_nphy.c @@ -0,0 +1,3878 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11n PHY data tables + + Copyright (c) 2008 Michael Buesch + Copyright (c) 2010 Rafał Miłecki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "tables_nphy.h" +#include "phy_common.h" +#include "phy_n.h" + +static const u8 b43_ntab_adjustpower0[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const u8 b43_ntab_adjustpower1[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const u16 b43_ntab_bdi[] = { + 0x0070, 0x0126, 0x012C, 0x0246, 0x048D, 0x04D2, +}; + +static const u32 b43_ntab_channelest[] = { + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, +}; + +static const u8 b43_ntab_estimatepowerlt0[] = { + 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, + 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, + 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, + 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, + 0x30, 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, + 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, + 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, + 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, +}; + +static const u8 b43_ntab_estimatepowerlt1[] = { + 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, + 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, + 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, + 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, + 0x30, 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, + 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, + 0x20, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, + 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, +}; + +static const u8 b43_ntab_framelookup[] = { + 0x02, 0x04, 0x14, 0x14, 0x03, 0x05, 0x16, 0x16, + 0x0A, 0x0C, 0x1C, 0x1C, 0x0B, 0x0D, 0x1E, 0x1E, + 0x06, 0x08, 0x18, 0x18, 0x07, 0x09, 0x1A, 0x1A, + 0x0E, 0x10, 0x20, 0x28, 0x0F, 0x11, 0x22, 0x2A, +}; + +static const u32 b43_ntab_framestruct[] = { + 0x08004A04, 0x00100000, 0x01000A05, 0x00100020, + 0x09804506, 0x00100030, 0x09804507, 0x00100030, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004A0C, 0x00100004, 0x01000A0D, 0x00100024, + 0x0980450E, 0x00100034, 0x0980450F, 0x00100034, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000A04, 0x00100000, 0x11008A05, 0x00100020, + 0x1980C506, 0x00100030, 0x21810506, 0x00100030, + 0x21810506, 0x00100030, 0x01800504, 0x00100030, + 0x11808505, 0x00100030, 0x29814507, 0x01100030, + 0x00000A04, 0x00100000, 0x11008A05, 0x00100020, + 0x21810506, 0x00100030, 0x21810506, 0x00100030, + 0x29814507, 0x01100030, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000A0C, 0x00100008, 0x11008A0D, 0x00100028, + 0x1980C50E, 0x00100038, 0x2181050E, 0x00100038, + 0x2181050E, 0x00100038, 0x0180050C, 0x00100038, + 0x1180850D, 0x00100038, 0x2981450F, 0x01100038, + 0x00000A0C, 0x00100008, 0x11008A0D, 0x00100028, + 0x2181050E, 0x00100038, 0x2181050E, 0x00100038, + 0x2981450F, 0x01100038, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004A04, 0x00100000, 0x01000A05, 0x00100020, + 0x1980C506, 0x00100030, 0x1980C506, 0x00100030, + 0x11808504, 0x00100030, 0x3981CA05, 0x00100030, + 0x29814507, 0x01100030, 0x00000000, 0x00000000, + 0x10008A04, 0x00100000, 0x3981CA05, 0x00100030, + 0x1980C506, 0x00100030, 0x29814507, 0x01100030, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004A0C, 0x00100008, 0x01000A0D, 0x00100028, + 0x1980C50E, 0x00100038, 0x1980C50E, 0x00100038, + 0x1180850C, 0x00100038, 0x3981CA0D, 0x00100038, + 0x2981450F, 0x01100038, 0x00000000, 0x00000000, + 0x10008A0C, 0x00100008, 0x3981CA0D, 0x00100038, + 0x1980C50E, 0x00100038, 0x2981450F, 0x01100038, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x00100000, 0x02001405, 0x00100040, + 0x0B004A06, 0x01900060, 0x13008A06, 0x01900060, + 0x13008A06, 0x01900060, 0x43020A04, 0x00100060, + 0x1B00CA05, 0x00100060, 0x23010A07, 0x01500060, + 0x40021404, 0x00100000, 0x1A00D405, 0x00100040, + 0x13008A06, 0x01900060, 0x13008A06, 0x01900060, + 0x23010A07, 0x01500060, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140C, 0x00100010, 0x0200140D, 0x00100050, + 0x0B004A0E, 0x01900070, 0x13008A0E, 0x01900070, + 0x13008A0E, 0x01900070, 0x43020A0C, 0x00100070, + 0x1B00CA0D, 0x00100070, 0x23010A0F, 0x01500070, + 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, + 0x13008A0E, 0x01900070, 0x13008A0E, 0x01900070, + 0x23010A0F, 0x01500070, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x50029404, 0x00100000, 0x32019405, 0x00100040, + 0x0B004A06, 0x01900060, 0x0B004A06, 0x01900060, + 0x5B02CA04, 0x00100060, 0x3B01D405, 0x00100060, + 0x23010A07, 0x01500060, 0x00000000, 0x00000000, + 0x5802D404, 0x00100000, 0x3B01D405, 0x00100060, + 0x0B004A06, 0x01900060, 0x23010A07, 0x01500060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x5002940C, 0x00100010, 0x3201940D, 0x00100050, + 0x0B004A0E, 0x01900070, 0x0B004A0E, 0x01900070, + 0x5B02CA0C, 0x00100070, 0x3B01D40D, 0x00100070, + 0x23010A0F, 0x01500070, 0x00000000, 0x00000000, + 0x5802D40C, 0x00100010, 0x3B01D40D, 0x00100070, + 0x0B004A0E, 0x01900070, 0x23010A0F, 0x01500070, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x000F4800, 0x62031405, 0x00100040, + 0x53028A06, 0x01900060, 0x53028A07, 0x01900060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140C, 0x000F4808, 0x6203140D, 0x00100048, + 0x53028A0E, 0x01900068, 0x53028A0F, 0x01900068, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000A0C, 0x00100004, 0x11008A0D, 0x00100024, + 0x1980C50E, 0x00100034, 0x2181050E, 0x00100034, + 0x2181050E, 0x00100034, 0x0180050C, 0x00100038, + 0x1180850D, 0x00100038, 0x1181850D, 0x00100038, + 0x2981450F, 0x01100038, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000A0C, 0x00100008, 0x11008A0D, 0x00100028, + 0x2181050E, 0x00100038, 0x2181050E, 0x00100038, + 0x1181850D, 0x00100038, 0x2981450F, 0x01100038, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004A04, 0x00100000, 0x01000A05, 0x00100020, + 0x0180C506, 0x00100030, 0x0180C506, 0x00100030, + 0x2180C50C, 0x00100030, 0x49820A0D, 0x0016A130, + 0x41824A0D, 0x0016A130, 0x2981450F, 0x01100030, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x2000CA0C, 0x00100000, 0x49820A0D, 0x0016A130, + 0x1980C50E, 0x00100030, 0x41824A0D, 0x0016A130, + 0x2981450F, 0x01100030, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140C, 0x00100008, 0x0200140D, 0x00100048, + 0x0B004A0E, 0x01900068, 0x13008A0E, 0x01900068, + 0x13008A0E, 0x01900068, 0x43020A0C, 0x00100070, + 0x1B00CA0D, 0x00100070, 0x1B014A0D, 0x00100070, + 0x23010A0F, 0x01500070, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, + 0x13008A0E, 0x01900070, 0x13008A0E, 0x01900070, + 0x1B014A0D, 0x00100070, 0x23010A0F, 0x01500070, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x50029404, 0x00100000, 0x32019405, 0x00100040, + 0x03004A06, 0x01900060, 0x03004A06, 0x01900060, + 0x6B030A0C, 0x00100060, 0x4B02140D, 0x0016A160, + 0x4302540D, 0x0016A160, 0x23010A0F, 0x01500060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x6B03140C, 0x00100060, 0x4B02140D, 0x0016A160, + 0x0B004A0E, 0x01900060, 0x4302540D, 0x0016A160, + 0x23010A0F, 0x01500060, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x00100000, 0x1A00D405, 0x00100040, + 0x53028A06, 0x01900060, 0x5B02CA06, 0x01900060, + 0x5B02CA06, 0x01900060, 0x43020A04, 0x00100060, + 0x1B00CA05, 0x00100060, 0x53028A07, 0x0190C060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, + 0x53028A0E, 0x01900070, 0x5B02CA0E, 0x01900070, + 0x5B02CA0E, 0x01900070, 0x43020A0C, 0x00100070, + 0x1B00CA0D, 0x00100070, 0x53028A0F, 0x0190C070, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x00100000, 0x1A00D405, 0x00100040, + 0x5B02CA06, 0x01900060, 0x5B02CA06, 0x01900060, + 0x53028A07, 0x0190C060, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140C, 0x00100010, 0x1A00D40D, 0x00100050, + 0x5B02CA0E, 0x01900070, 0x5B02CA0E, 0x01900070, + 0x53028A0F, 0x0190C070, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_gainctl0[] = { + 0x03CC2B44, 0x03CC2B42, 0x03CC2B40, 0x03CC2B3E, + 0x03CC2B3D, 0x03CC2B3B, 0x03C82B44, 0x03C82B42, + 0x03C82B40, 0x03C82B3E, 0x03C82B3D, 0x03C82B3B, + 0x03C82B39, 0x03C82B38, 0x03C82B36, 0x03C82B34, + 0x03C42B44, 0x03C42B42, 0x03C42B40, 0x03C42B3E, + 0x03C42B3D, 0x03C42B3B, 0x03C42B39, 0x03C42B38, + 0x03C42B36, 0x03C42B34, 0x03C42B33, 0x03C42B32, + 0x03C42B30, 0x03C42B2F, 0x03C42B2D, 0x03C02B44, + 0x03C02B42, 0x03C02B40, 0x03C02B3E, 0x03C02B3D, + 0x03C02B3B, 0x03C02B39, 0x03C02B38, 0x03C02B36, + 0x03C02B34, 0x03B02B44, 0x03B02B42, 0x03B02B40, + 0x03B02B3E, 0x03B02B3D, 0x03B02B3B, 0x03B02B39, + 0x03B02B38, 0x03B02B36, 0x03B02B34, 0x03B02B33, + 0x03B02B32, 0x03B02B30, 0x03B02B2F, 0x03B02B2D, + 0x03A02B44, 0x03A02B42, 0x03A02B40, 0x03A02B3E, + 0x03A02B3D, 0x03A02B3B, 0x03A02B39, 0x03A02B38, + 0x03A02B36, 0x03A02B34, 0x03902B44, 0x03902B42, + 0x03902B40, 0x03902B3E, 0x03902B3D, 0x03902B3B, + 0x03902B39, 0x03902B38, 0x03902B36, 0x03902B34, + 0x03902B33, 0x03902B32, 0x03902B30, 0x03802B44, + 0x03802B42, 0x03802B40, 0x03802B3E, 0x03802B3D, + 0x03802B3B, 0x03802B39, 0x03802B38, 0x03802B36, + 0x03802B34, 0x03802B33, 0x03802B32, 0x03802B30, + 0x03802B2F, 0x03802B2D, 0x03802B2C, 0x03802B2B, + 0x03802B2A, 0x03802B29, 0x03802B27, 0x03802B26, + 0x03802B25, 0x03802B24, 0x03802B23, 0x03802B22, + 0x03802B21, 0x03802B20, 0x03802B1F, 0x03802B1E, + 0x03802B1E, 0x03802B1D, 0x03802B1C, 0x03802B1B, + 0x03802B1A, 0x03802B1A, 0x03802B19, 0x03802B18, + 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, + 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, + 0x03802B18, 0x03802B18, 0x03802B18, 0x00002B00, +}; + +static const u32 b43_ntab_gainctl1[] = { + 0x03CC2B44, 0x03CC2B42, 0x03CC2B40, 0x03CC2B3E, + 0x03CC2B3D, 0x03CC2B3B, 0x03C82B44, 0x03C82B42, + 0x03C82B40, 0x03C82B3E, 0x03C82B3D, 0x03C82B3B, + 0x03C82B39, 0x03C82B38, 0x03C82B36, 0x03C82B34, + 0x03C42B44, 0x03C42B42, 0x03C42B40, 0x03C42B3E, + 0x03C42B3D, 0x03C42B3B, 0x03C42B39, 0x03C42B38, + 0x03C42B36, 0x03C42B34, 0x03C42B33, 0x03C42B32, + 0x03C42B30, 0x03C42B2F, 0x03C42B2D, 0x03C02B44, + 0x03C02B42, 0x03C02B40, 0x03C02B3E, 0x03C02B3D, + 0x03C02B3B, 0x03C02B39, 0x03C02B38, 0x03C02B36, + 0x03C02B34, 0x03B02B44, 0x03B02B42, 0x03B02B40, + 0x03B02B3E, 0x03B02B3D, 0x03B02B3B, 0x03B02B39, + 0x03B02B38, 0x03B02B36, 0x03B02B34, 0x03B02B33, + 0x03B02B32, 0x03B02B30, 0x03B02B2F, 0x03B02B2D, + 0x03A02B44, 0x03A02B42, 0x03A02B40, 0x03A02B3E, + 0x03A02B3D, 0x03A02B3B, 0x03A02B39, 0x03A02B38, + 0x03A02B36, 0x03A02B34, 0x03902B44, 0x03902B42, + 0x03902B40, 0x03902B3E, 0x03902B3D, 0x03902B3B, + 0x03902B39, 0x03902B38, 0x03902B36, 0x03902B34, + 0x03902B33, 0x03902B32, 0x03902B30, 0x03802B44, + 0x03802B42, 0x03802B40, 0x03802B3E, 0x03802B3D, + 0x03802B3B, 0x03802B39, 0x03802B38, 0x03802B36, + 0x03802B34, 0x03802B33, 0x03802B32, 0x03802B30, + 0x03802B2F, 0x03802B2D, 0x03802B2C, 0x03802B2B, + 0x03802B2A, 0x03802B29, 0x03802B27, 0x03802B26, + 0x03802B25, 0x03802B24, 0x03802B23, 0x03802B22, + 0x03802B21, 0x03802B20, 0x03802B1F, 0x03802B1E, + 0x03802B1E, 0x03802B1D, 0x03802B1C, 0x03802B1B, + 0x03802B1A, 0x03802B1A, 0x03802B19, 0x03802B18, + 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, + 0x03802B18, 0x03802B18, 0x03802B18, 0x03802B18, + 0x03802B18, 0x03802B18, 0x03802B18, 0x00002B00, +}; + +static const u32 b43_ntab_intlevel[] = { + 0x00802070, 0x0671188D, 0x0A60192C, 0x0A300E46, + 0x00C1188D, 0x080024D2, 0x00000070, +}; + +static const u32 b43_ntab_iqlt0[] = { + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, +}; + +static const u32 b43_ntab_iqlt1[] = { + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, + 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, +}; + +static const u16 b43_ntab_loftlt0[] = { + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, +}; + +static const u16 b43_ntab_loftlt1[] = { + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, + 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, + 0x0002, 0x0103, +}; + +static const u8 b43_ntab_mcs[] = { + 0x00, 0x08, 0x0A, 0x10, 0x12, 0x19, 0x1A, 0x1C, + 0x40, 0x48, 0x4A, 0x50, 0x52, 0x59, 0x5A, 0x5C, + 0x80, 0x88, 0x8A, 0x90, 0x92, 0x99, 0x9A, 0x9C, + 0xC0, 0xC8, 0xCA, 0xD0, 0xD2, 0xD9, 0xDA, 0xDC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x04, 0x08, 0x09, 0x0A, 0x0C, + 0x10, 0x11, 0x12, 0x14, 0x18, 0x19, 0x1A, 0x1C, + 0x20, 0x21, 0x22, 0x24, 0x40, 0x41, 0x42, 0x44, + 0x48, 0x49, 0x4A, 0x4C, 0x50, 0x51, 0x52, 0x54, + 0x58, 0x59, 0x5A, 0x5C, 0x60, 0x61, 0x62, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const u32 b43_ntab_noisevar10[] = { + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, +}; + +static const u32 b43_ntab_noisevar11[] = { + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, + 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, +}; + +static const u16 b43_ntab_pilot[] = { + 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, + 0xFF08, 0xFF08, 0x80D5, 0x80D5, 0x80D5, 0x80D5, + 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0xFF0A, 0xFF82, + 0xFFA0, 0xFF28, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFF82, 0xFFA0, 0xFF28, 0xFF0A, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xF83F, 0xFA1F, 0xFA97, 0xFAB5, + 0xF2BD, 0xF0BF, 0xFFFF, 0xFFFF, 0xF017, 0xF815, + 0xF215, 0xF095, 0xF035, 0xF01D, 0xFFFF, 0xFFFF, + 0xFF08, 0xFF02, 0xFF80, 0xFF20, 0xFF08, 0xFF02, + 0xFF80, 0xFF20, 0xF01F, 0xF817, 0xFA15, 0xF295, + 0xF0B5, 0xF03D, 0xFFFF, 0xFFFF, 0xF82A, 0xFA0A, + 0xFA82, 0xFAA0, 0xF2A8, 0xF0AA, 0xFFFF, 0xFFFF, + 0xF002, 0xF800, 0xF200, 0xF080, 0xF020, 0xF008, + 0xFFFF, 0xFFFF, 0xF00A, 0xF802, 0xFA00, 0xF280, + 0xF0A0, 0xF028, 0xFFFF, 0xFFFF, +}; + +static const u32 b43_ntab_pilotlt[] = { + 0x76540123, 0x62407351, 0x76543201, 0x76540213, + 0x76540123, 0x76430521, +}; + +static const u32 b43_ntab_tdi20a0[] = { + 0x00091226, 0x000A1429, 0x000B56AD, 0x000C58B0, + 0x000D5AB3, 0x000E9CB6, 0x000F9EBA, 0x0000C13D, + 0x00020301, 0x00030504, 0x00040708, 0x0005090B, + 0x00064B8E, 0x00095291, 0x000A5494, 0x000B9718, + 0x000C9927, 0x000D9B2A, 0x000EDD2E, 0x000FDF31, + 0x000101B4, 0x000243B7, 0x000345BB, 0x000447BE, + 0x00058982, 0x00068C05, 0x00099309, 0x000A950C, + 0x000BD78F, 0x000CD992, 0x000DDB96, 0x000F1D99, + 0x00005FA8, 0x0001422C, 0x0002842F, 0x00038632, + 0x00048835, 0x0005CA38, 0x0006CCBC, 0x0009D3BF, + 0x000B1603, 0x000C1806, 0x000D1A0A, 0x000E1C0D, + 0x000F5E10, 0x00008093, 0x00018297, 0x0002C49A, + 0x0003C680, 0x0004C880, 0x00060B00, 0x00070D00, + 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_tdi20a1[] = { + 0x00014B26, 0x00028D29, 0x000393AD, 0x00049630, + 0x0005D833, 0x0006DA36, 0x00099C3A, 0x000A9E3D, + 0x000BC081, 0x000CC284, 0x000DC488, 0x000F068B, + 0x0000488E, 0x00018B91, 0x0002D214, 0x0003D418, + 0x0004D6A7, 0x000618AA, 0x00071AAE, 0x0009DCB1, + 0x000B1EB4, 0x000C0137, 0x000D033B, 0x000E053E, + 0x000F4702, 0x00008905, 0x00020C09, 0x0003128C, + 0x0004148F, 0x00051712, 0x00065916, 0x00091B19, + 0x000A1D28, 0x000B5F2C, 0x000C41AF, 0x000D43B2, + 0x000E85B5, 0x000F87B8, 0x0000C9BC, 0x00024CBF, + 0x00035303, 0x00045506, 0x0005978A, 0x0006998D, + 0x00095B90, 0x000A5D93, 0x000B9F97, 0x000C821A, + 0x000D8400, 0x000EC600, 0x000FC800, 0x00010A00, + 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_tdi40a0[] = { + 0x0011A346, 0x00136CCF, 0x0014F5D9, 0x001641E2, + 0x0017CB6B, 0x00195475, 0x001B2383, 0x001CAD0C, + 0x001E7616, 0x0000821F, 0x00020BA8, 0x0003D4B2, + 0x00056447, 0x00072DD0, 0x0008B6DA, 0x000A02E3, + 0x000B8C6C, 0x000D15F6, 0x0011E484, 0x0013AE0D, + 0x00153717, 0x00168320, 0x00180CA9, 0x00199633, + 0x001B6548, 0x001CEED1, 0x001EB7DB, 0x0000C3E4, + 0x00024D6D, 0x000416F7, 0x0005A585, 0x00076F0F, + 0x0008F818, 0x000A4421, 0x000BCDAB, 0x000D9734, + 0x00122649, 0x0013EFD2, 0x001578DC, 0x0016C4E5, + 0x00184E6E, 0x001A17F8, 0x001BA686, 0x001D3010, + 0x001EF999, 0x00010522, 0x00028EAC, 0x00045835, + 0x0005E74A, 0x0007B0D3, 0x00093A5D, 0x000A85E6, + 0x000C0F6F, 0x000DD8F9, 0x00126787, 0x00143111, + 0x0015BA9A, 0x00170623, 0x00188FAD, 0x001A5936, + 0x001BE84B, 0x001DB1D4, 0x001F3B5E, 0x000146E7, + 0x00031070, 0x000499FA, 0x00062888, 0x0007F212, + 0x00097B9B, 0x000AC7A4, 0x000C50AE, 0x000E1A37, + 0x0012A94C, 0x001472D5, 0x0015FC5F, 0x00174868, + 0x0018D171, 0x001A9AFB, 0x001C2989, 0x001DF313, + 0x001F7C9C, 0x000188A5, 0x000351AF, 0x0004DB38, + 0x0006AA4D, 0x000833D7, 0x0009BD60, 0x000B0969, + 0x000C9273, 0x000E5BFC, 0x00132A8A, 0x0014B414, + 0x00163D9D, 0x001789A6, 0x001912B0, 0x001ADC39, + 0x001C6BCE, 0x001E34D8, 0x001FBE61, 0x0001CA6A, + 0x00039374, 0x00051CFD, 0x0006EC0B, 0x00087515, + 0x0009FE9E, 0x000B4AA7, 0x000CD3B1, 0x000E9D3A, + 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_tdi40a1[] = { + 0x001EDB36, 0x000129CA, 0x0002B353, 0x00047CDD, + 0x0005C8E6, 0x000791EF, 0x00091BF9, 0x000AAA07, + 0x000C3391, 0x000DFD1A, 0x00120923, 0x0013D22D, + 0x00155C37, 0x0016EACB, 0x00187454, 0x001A3DDE, + 0x001B89E7, 0x001D12F0, 0x001F1CFA, 0x00016B88, + 0x00033492, 0x0004BE1B, 0x00060A24, 0x0007D32E, + 0x00095D38, 0x000AEC4C, 0x000C7555, 0x000E3EDF, + 0x00124AE8, 0x001413F1, 0x0015A37B, 0x00172C89, + 0x0018B593, 0x001A419C, 0x001BCB25, 0x001D942F, + 0x001F63B9, 0x0001AD4D, 0x00037657, 0x0004C260, + 0x00068BE9, 0x000814F3, 0x0009A47C, 0x000B2D8A, + 0x000CB694, 0x000E429D, 0x00128C26, 0x001455B0, + 0x0015E4BA, 0x00176E4E, 0x0018F758, 0x001A8361, + 0x001C0CEA, 0x001DD674, 0x001FA57D, 0x0001EE8B, + 0x0003B795, 0x0005039E, 0x0006CD27, 0x000856B1, + 0x0009E5C6, 0x000B6F4F, 0x000CF859, 0x000E8462, + 0x00130DEB, 0x00149775, 0x00162603, 0x0017AF8C, + 0x00193896, 0x001AC49F, 0x001C4E28, 0x001E17B2, + 0x0000A6C7, 0x00023050, 0x0003F9DA, 0x00054563, + 0x00070EEC, 0x00089876, 0x000A2704, 0x000BB08D, + 0x000D3A17, 0x001185A0, 0x00134F29, 0x0014D8B3, + 0x001667C8, 0x0017F151, 0x00197ADB, 0x001B0664, + 0x001C8FED, 0x001E5977, 0x0000E805, 0x0002718F, + 0x00043B18, 0x000586A1, 0x0007502B, 0x0008D9B4, + 0x000A68C9, 0x000BF252, 0x000DBBDC, 0x0011C7E5, + 0x001390EE, 0x00151A78, 0x0016A906, 0x00183290, + 0x0019BC19, 0x001B4822, 0x001CD12C, 0x001E9AB5, + 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_tdtrn[] = { + 0x061C061C, 0x0050EE68, 0xF592FE36, 0xFE5212F6, + 0x00000C38, 0xFE5212F6, 0xF592FE36, 0x0050EE68, + 0x061C061C, 0xEE680050, 0xFE36F592, 0x12F6FE52, + 0x0C380000, 0x12F6FE52, 0xFE36F592, 0xEE680050, + 0x061C061C, 0x0050EE68, 0xF592FE36, 0xFE5212F6, + 0x00000C38, 0xFE5212F6, 0xF592FE36, 0x0050EE68, + 0x061C061C, 0xEE680050, 0xFE36F592, 0x12F6FE52, + 0x0C380000, 0x12F6FE52, 0xFE36F592, 0xEE680050, + 0x05E305E3, 0x004DEF0C, 0xF5F3FE47, 0xFE611246, + 0x00000BC7, 0xFE611246, 0xF5F3FE47, 0x004DEF0C, + 0x05E305E3, 0xEF0C004D, 0xFE47F5F3, 0x1246FE61, + 0x0BC70000, 0x1246FE61, 0xFE47F5F3, 0xEF0C004D, + 0x05E305E3, 0x004DEF0C, 0xF5F3FE47, 0xFE611246, + 0x00000BC7, 0xFE611246, 0xF5F3FE47, 0x004DEF0C, + 0x05E305E3, 0xEF0C004D, 0xFE47F5F3, 0x1246FE61, + 0x0BC70000, 0x1246FE61, 0xFE47F5F3, 0xEF0C004D, + 0xFA58FA58, 0xF895043B, 0xFF4C09C0, 0xFBC6FFA8, + 0xFB84F384, 0x0798F6F9, 0x05760122, 0x058409F6, + 0x0B500000, 0x05B7F542, 0x08860432, 0x06DDFEE7, + 0xFB84F384, 0xF9D90664, 0xF7E8025C, 0x00FFF7BD, + 0x05A805A8, 0xF7BD00FF, 0x025CF7E8, 0x0664F9D9, + 0xF384FB84, 0xFEE706DD, 0x04320886, 0xF54205B7, + 0x00000B50, 0x09F60584, 0x01220576, 0xF6F90798, + 0xF384FB84, 0xFFA8FBC6, 0x09C0FF4C, 0x043BF895, + 0x02D402D4, 0x07DE0270, 0xFC96079C, 0xF90AFE94, + 0xFE00FF2C, 0x02D4065D, 0x092A0096, 0x0014FBB8, + 0xFD2CFD2C, 0x076AFB3C, 0x0096F752, 0xF991FD87, + 0xFB2C0200, 0xFEB8F960, 0x08E0FC96, 0x049802A8, + 0xFD2CFD2C, 0x02A80498, 0xFC9608E0, 0xF960FEB8, + 0x0200FB2C, 0xFD87F991, 0xF7520096, 0xFB3C076A, + 0xFD2CFD2C, 0xFBB80014, 0x0096092A, 0x065D02D4, + 0xFF2CFE00, 0xFE94F90A, 0x079CFC96, 0x027007DE, + 0x02D402D4, 0x027007DE, 0x079CFC96, 0xFE94F90A, + 0xFF2CFE00, 0x065D02D4, 0x0096092A, 0xFBB80014, + 0xFD2CFD2C, 0xFB3C076A, 0xF7520096, 0xFD87F991, + 0x0200FB2C, 0xF960FEB8, 0xFC9608E0, 0x02A80498, + 0xFD2CFD2C, 0x049802A8, 0x08E0FC96, 0xFEB8F960, + 0xFB2C0200, 0xF991FD87, 0x0096F752, 0x076AFB3C, + 0xFD2CFD2C, 0x0014FBB8, 0x092A0096, 0x02D4065D, + 0xFE00FF2C, 0xF90AFE94, 0xFC96079C, 0x07DE0270, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x062A0000, 0xFEFA0759, 0x08B80908, 0xF396FC2D, + 0xF9D6045C, 0xFC4EF608, 0xF748F596, 0x07B207BF, + 0x062A062A, 0xF84EF841, 0xF748F596, 0x03B209F8, + 0xF9D6045C, 0x0C6A03D3, 0x08B80908, 0x0106F8A7, + 0x062A0000, 0xFEFAF8A7, 0x08B8F6F8, 0xF39603D3, + 0xF9D6FBA4, 0xFC4E09F8, 0xF7480A6A, 0x07B2F841, + 0x062AF9D6, 0xF84E07BF, 0xF7480A6A, 0x03B2F608, + 0xF9D6FBA4, 0x0C6AFC2D, 0x08B8F6F8, 0x01060759, + 0x062A0000, 0xFEFA0759, 0x08B80908, 0xF396FC2D, + 0xF9D6045C, 0xFC4EF608, 0xF748F596, 0x07B207BF, + 0x062A062A, 0xF84EF841, 0xF748F596, 0x03B209F8, + 0xF9D6045C, 0x0C6A03D3, 0x08B80908, 0x0106F8A7, + 0x062A0000, 0xFEFAF8A7, 0x08B8F6F8, 0xF39603D3, + 0xF9D6FBA4, 0xFC4E09F8, 0xF7480A6A, 0x07B2F841, + 0x062AF9D6, 0xF84E07BF, 0xF7480A6A, 0x03B2F608, + 0xF9D6FBA4, 0x0C6AFC2D, 0x08B8F6F8, 0x01060759, + 0x061C061C, 0xFF30009D, 0xFFB21141, 0xFD87FB54, + 0xF65DFE59, 0x02EEF99E, 0x0166F03C, 0xFFF809B6, + 0x000008A4, 0x000AF42B, 0x00EFF577, 0xFA840BF2, + 0xFC02FF51, 0x08260F67, 0xFFF0036F, 0x0842F9C3, + 0x00000000, 0x063DF7BE, 0xFC910010, 0xF099F7DA, + 0x00AF03FE, 0xF40E057C, 0x0A89FF11, 0x0BD5FFF6, + 0xF75C0000, 0xF64A0008, 0x0FC4FE9A, 0x0662FD12, + 0x01A709A3, 0x04AC0279, 0xEEBF004E, 0xFF6300D0, + 0xF9E4F9E4, 0x00D0FF63, 0x004EEEBF, 0x027904AC, + 0x09A301A7, 0xFD120662, 0xFE9A0FC4, 0x0008F64A, + 0x0000F75C, 0xFFF60BD5, 0xFF110A89, 0x057CF40E, + 0x03FE00AF, 0xF7DAF099, 0x0010FC91, 0xF7BE063D, + 0x00000000, 0xF9C30842, 0x036FFFF0, 0x0F670826, + 0xFF51FC02, 0x0BF2FA84, 0xF57700EF, 0xF42B000A, + 0x08A40000, 0x09B6FFF8, 0xF03C0166, 0xF99E02EE, + 0xFE59F65D, 0xFB54FD87, 0x1141FFB2, 0x009DFF30, + 0x05E30000, 0xFF060705, 0x085408A0, 0xF425FC59, + 0xFA1D042A, 0xFC78F67A, 0xF7ACF60E, 0x075A0766, + 0x05E305E3, 0xF8A6F89A, 0xF7ACF60E, 0x03880986, + 0xFA1D042A, 0x0BDB03A7, 0x085408A0, 0x00FAF8FB, + 0x05E30000, 0xFF06F8FB, 0x0854F760, 0xF42503A7, + 0xFA1DFBD6, 0xFC780986, 0xF7AC09F2, 0x075AF89A, + 0x05E3FA1D, 0xF8A60766, 0xF7AC09F2, 0x0388F67A, + 0xFA1DFBD6, 0x0BDBFC59, 0x0854F760, 0x00FA0705, + 0x05E30000, 0xFF060705, 0x085408A0, 0xF425FC59, + 0xFA1D042A, 0xFC78F67A, 0xF7ACF60E, 0x075A0766, + 0x05E305E3, 0xF8A6F89A, 0xF7ACF60E, 0x03880986, + 0xFA1D042A, 0x0BDB03A7, 0x085408A0, 0x00FAF8FB, + 0x05E30000, 0xFF06F8FB, 0x0854F760, 0xF42503A7, + 0xFA1DFBD6, 0xFC780986, 0xF7AC09F2, 0x075AF89A, + 0x05E3FA1D, 0xF8A60766, 0xF7AC09F2, 0x0388F67A, + 0xFA1DFBD6, 0x0BDBFC59, 0x0854F760, 0x00FA0705, + 0xFA58FA58, 0xF8F0FE00, 0x0448073D, 0xFDC9FE46, + 0xF9910258, 0x089D0407, 0xFD5CF71A, 0x02AFFDE0, + 0x083E0496, 0xFF5A0740, 0xFF7AFD97, 0x00FE01F1, + 0x0009082E, 0xFA94FF75, 0xFECDF8EA, 0xFFB0F693, + 0xFD2CFA58, 0x0433FF16, 0xFBA405DD, 0xFA610341, + 0x06A606CB, 0x0039FD2D, 0x0677FA97, 0x01FA05E0, + 0xF896003E, 0x075A068B, 0x012CFC3E, 0xFA23F98D, + 0xFC7CFD43, 0xFF90FC0D, 0x01C10982, 0x00C601D6, + 0xFD2CFD2C, 0x01D600C6, 0x098201C1, 0xFC0DFF90, + 0xFD43FC7C, 0xF98DFA23, 0xFC3E012C, 0x068B075A, + 0x003EF896, 0x05E001FA, 0xFA970677, 0xFD2D0039, + 0x06CB06A6, 0x0341FA61, 0x05DDFBA4, 0xFF160433, + 0xFA58FD2C, 0xF693FFB0, 0xF8EAFECD, 0xFF75FA94, + 0x082E0009, 0x01F100FE, 0xFD97FF7A, 0x0740FF5A, + 0x0496083E, 0xFDE002AF, 0xF71AFD5C, 0x0407089D, + 0x0258F991, 0xFE46FDC9, 0x073D0448, 0xFE00F8F0, + 0xFD2CFD2C, 0xFCE00500, 0xFC09FDDC, 0xFE680157, + 0x04C70571, 0xFC3AFF21, 0xFCD70228, 0x056D0277, + 0x0200FE00, 0x0022F927, 0xFE3C032B, 0xFC44FF3C, + 0x03E9FBDB, 0x04570313, 0x04C9FF5C, 0x000D03B8, + 0xFA580000, 0xFBE900D2, 0xF9D0FE0B, 0x0125FDF9, + 0x042501BF, 0x0328FA2B, 0xFFA902F0, 0xFA250157, + 0x0200FE00, 0x03740438, 0xFF0405FD, 0x030CFE52, + 0x0037FB39, 0xFF6904C5, 0x04F8FD23, 0xFD31FC1B, + 0xFD2CFD2C, 0xFC1BFD31, 0xFD2304F8, 0x04C5FF69, + 0xFB390037, 0xFE52030C, 0x05FDFF04, 0x04380374, + 0xFE000200, 0x0157FA25, 0x02F0FFA9, 0xFA2B0328, + 0x01BF0425, 0xFDF90125, 0xFE0BF9D0, 0x00D2FBE9, + 0x0000FA58, 0x03B8000D, 0xFF5C04C9, 0x03130457, + 0xFBDB03E9, 0xFF3CFC44, 0x032BFE3C, 0xF9270022, + 0xFE000200, 0x0277056D, 0x0228FCD7, 0xFF21FC3A, + 0x057104C7, 0x0157FE68, 0xFDDCFC09, 0x0500FCE0, + 0xFD2CFD2C, 0x0500FCE0, 0xFDDCFC09, 0x0157FE68, + 0x057104C7, 0xFF21FC3A, 0x0228FCD7, 0x0277056D, + 0xFE000200, 0xF9270022, 0x032BFE3C, 0xFF3CFC44, + 0xFBDB03E9, 0x03130457, 0xFF5C04C9, 0x03B8000D, + 0x0000FA58, 0x00D2FBE9, 0xFE0BF9D0, 0xFDF90125, + 0x01BF0425, 0xFA2B0328, 0x02F0FFA9, 0x0157FA25, + 0xFE000200, 0x04380374, 0x05FDFF04, 0xFE52030C, + 0xFB390037, 0x04C5FF69, 0xFD2304F8, 0xFC1BFD31, + 0xFD2CFD2C, 0xFD31FC1B, 0x04F8FD23, 0xFF6904C5, + 0x0037FB39, 0x030CFE52, 0xFF0405FD, 0x03740438, + 0x0200FE00, 0xFA250157, 0xFFA902F0, 0x0328FA2B, + 0x042501BF, 0x0125FDF9, 0xF9D0FE0B, 0xFBE900D2, + 0xFA580000, 0x000D03B8, 0x04C9FF5C, 0x04570313, + 0x03E9FBDB, 0xFC44FF3C, 0xFE3C032B, 0x0022F927, + 0x0200FE00, 0x056D0277, 0xFCD70228, 0xFC3AFF21, + 0x04C70571, 0xFE680157, 0xFC09FDDC, 0xFCE00500, + 0x05A80000, 0xFF1006BE, 0x0800084A, 0xF49CFC7E, + 0xFA580400, 0xFC9CF6DA, 0xF800F672, 0x0710071C, + 0x05A805A8, 0xF8F0F8E4, 0xF800F672, 0x03640926, + 0xFA580400, 0x0B640382, 0x0800084A, 0x00F0F942, + 0x05A80000, 0xFF10F942, 0x0800F7B6, 0xF49C0382, + 0xFA58FC00, 0xFC9C0926, 0xF800098E, 0x0710F8E4, + 0x05A8FA58, 0xF8F0071C, 0xF800098E, 0x0364F6DA, + 0xFA58FC00, 0x0B64FC7E, 0x0800F7B6, 0x00F006BE, + 0x05A80000, 0xFF1006BE, 0x0800084A, 0xF49CFC7E, + 0xFA580400, 0xFC9CF6DA, 0xF800F672, 0x0710071C, + 0x05A805A8, 0xF8F0F8E4, 0xF800F672, 0x03640926, + 0xFA580400, 0x0B640382, 0x0800084A, 0x00F0F942, + 0x05A80000, 0xFF10F942, 0x0800F7B6, 0xF49C0382, + 0xFA58FC00, 0xFC9C0926, 0xF800098E, 0x0710F8E4, + 0x05A8FA58, 0xF8F0071C, 0xF800098E, 0x0364F6DA, + 0xFA58FC00, 0x0B64FC7E, 0x0800F7B6, 0x00F006BE, +}; + +static const u32 b43_ntab_tmap[] = { + 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, + 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0xF1111110, 0x11111111, 0x11F11111, 0x00000111, + 0x11000000, 0x1111F111, 0x11111111, 0x111111F1, + 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x000AA888, + 0x88880000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, + 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, + 0xA2222220, 0x22222222, 0x22C22222, 0x00000222, + 0x22000000, 0x2222A222, 0x22222222, 0x222222A2, + 0xF1111110, 0x11111111, 0x11F11111, 0x00011111, + 0x11110000, 0x1111F111, 0x11111111, 0x111111F1, + 0xA8AA88A0, 0xA88888A8, 0xA8A8A88A, 0x00088AAA, + 0xAAAA0000, 0xA8A8AA88, 0xA88AAAAA, 0xAAAA8A8A, + 0xAAA8AAA0, 0x8AAA8AAA, 0xAA8A8A8A, 0x000AAA88, + 0x8AAA0000, 0xAAA8A888, 0x8AA88A8A, 0x8A88A888, + 0x08080A00, 0x0A08080A, 0x080A0A08, 0x00080808, + 0x080A0000, 0x080A0808, 0x080A0808, 0x0A0A0A08, + 0xA0A0A0A0, 0x80A0A080, 0x8080A0A0, 0x00008080, + 0x80A00000, 0x80A080A0, 0xA080A0A0, 0x8080A0A0, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x99999000, 0x9B9B99BB, 0x9BB99999, 0x9999B9B9, + 0x9B99BB90, 0x9BBBBB9B, 0x9B9B9BB9, 0x00000999, + 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00AAA888, + 0x22000000, 0x2222B222, 0x22222222, 0x222222B2, + 0xB2222220, 0x22222222, 0x22D22222, 0x00000222, + 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, + 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, + 0x33000000, 0x3333B333, 0x33333333, 0x333333B3, + 0xB3333330, 0x33333333, 0x33D33333, 0x00000333, + 0x22000000, 0x2222A222, 0x22222222, 0x222222A2, + 0xA2222220, 0x22222222, 0x22C22222, 0x00000222, + 0x99B99B00, 0x9B9B99BB, 0x9BB99999, 0x9999B9B9, + 0x9B99BB99, 0x9BBBBB9B, 0x9B9B9BB9, 0x00000999, + 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0x8A88AA88, 0x8AAAAA8A, 0x8A8A8AA8, 0x08AAA888, + 0x22222200, 0x2222F222, 0x22222222, 0x222222F2, + 0x22222222, 0x22222222, 0x22F22222, 0x00000222, + 0x11000000, 0x1111F111, 0x11111111, 0x11111111, + 0xF1111111, 0x11111111, 0x11F11111, 0x01111111, + 0xBB9BB900, 0xB9B9BB99, 0xB99BBBBB, 0xBBBB9B9B, + 0xB9BB99BB, 0xB99999B9, 0xB9B9B99B, 0x00000BBB, + 0xAA000000, 0xA8A8AA88, 0xA88AAAAA, 0xAAAA8A8A, + 0xA8AA88AA, 0xA88888A8, 0xA8A8A88A, 0x0A888AAA, + 0xAA000000, 0xA8A8AA88, 0xA88AAAAA, 0xAAAA8A8A, + 0xA8AA88A0, 0xA88888A8, 0xA8A8A88A, 0x00000AAA, + 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, + 0xBBBBBB00, 0x999BBBBB, 0x9BB99B9B, 0xB9B9B9BB, + 0xB9B99BBB, 0xB9B9B9BB, 0xB9BB9B99, 0x00000999, + 0x8A000000, 0xAA88A888, 0xA88888AA, 0xA88A8A88, + 0xA88AA88A, 0x88A8AAAA, 0xA8AA8AAA, 0x0888A88A, + 0x0B0B0B00, 0x090B0B0B, 0x0B090B0B, 0x0909090B, + 0x09090B0B, 0x09090B0B, 0x09090B09, 0x00000909, + 0x0A000000, 0x0A080808, 0x080A080A, 0x080A0A08, + 0x080A080A, 0x0808080A, 0x0A0A0A08, 0x0808080A, + 0xB0B0B000, 0x9090B0B0, 0x90B09090, 0xB0B0B090, + 0xB0B090B0, 0x90B0B0B0, 0xB0B09090, 0x00000090, + 0x80000000, 0xA080A080, 0xA08080A0, 0xA0808080, + 0xA080A080, 0x80A0A0A0, 0xA0A080A0, 0x00A0A0A0, + 0x22000000, 0x2222F222, 0x22222222, 0x222222F2, + 0xF2222220, 0x22222222, 0x22F22222, 0x00000222, + 0x11000000, 0x1111F111, 0x11111111, 0x111111F1, + 0xF1111110, 0x11111111, 0x11F11111, 0x00000111, + 0x33000000, 0x3333F333, 0x33333333, 0x333333F3, + 0xF3333330, 0x33333333, 0x33F33333, 0x00000333, + 0x22000000, 0x2222F222, 0x22222222, 0x222222F2, + 0xF2222220, 0x22222222, 0x22F22222, 0x00000222, + 0x99000000, 0x9B9B99BB, 0x9BB99999, 0x9999B9B9, + 0x9B99BB90, 0x9BBBBB9B, 0x9B9B9BB9, 0x00000999, + 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, + 0x88888000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, + 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00AAA888, + 0x88A88A00, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0x8A88AA88, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, + 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0x8A88AA88, 0x8AAAAA8A, 0x8A8A8AA8, 0x08AAA888, + 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, + 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, + 0x11000000, 0x1111A111, 0x11111111, 0x111111A1, + 0xA1111110, 0x11111111, 0x11C11111, 0x00000111, + 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, + 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, + 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +/* static tables, PHY revision >= 3 */ +static const u32 b43_ntab_framestruct_r3[] = { + 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, + 0x09804506, 0x00100030, 0x09804507, 0x00100030, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004a0c, 0x00100004, 0x01000a0d, 0x00100024, + 0x0980450e, 0x00100034, 0x0980450f, 0x00100034, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, + 0x1980c506, 0x00100030, 0x21810506, 0x00100030, + 0x21810506, 0x00100030, 0x01800504, 0x00100030, + 0x11808505, 0x00100030, 0x29814507, 0x01100030, + 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, + 0x21810506, 0x00100030, 0x21810506, 0x00100030, + 0x29814507, 0x01100030, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, + 0x1980c50e, 0x00100038, 0x2181050e, 0x00100038, + 0x2181050e, 0x00100038, 0x0180050c, 0x00100038, + 0x1180850d, 0x00100038, 0x2981450f, 0x01100038, + 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, + 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, + 0x2981450f, 0x01100038, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, + 0x1980c506, 0x00100030, 0x1980c506, 0x00100030, + 0x11808504, 0x00100030, 0x3981ca05, 0x00100030, + 0x29814507, 0x01100030, 0x00000000, 0x00000000, + 0x10008a04, 0x00100000, 0x3981ca05, 0x00100030, + 0x1980c506, 0x00100030, 0x29814507, 0x01100030, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004a0c, 0x00100008, 0x01000a0d, 0x00100028, + 0x1980c50e, 0x00100038, 0x1980c50e, 0x00100038, + 0x1180850c, 0x00100038, 0x3981ca0d, 0x00100038, + 0x2981450f, 0x01100038, 0x00000000, 0x00000000, + 0x10008a0c, 0x00100008, 0x3981ca0d, 0x00100038, + 0x1980c50e, 0x00100038, 0x2981450f, 0x01100038, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x00100000, 0x02001405, 0x00100040, + 0x0b004a06, 0x01900060, 0x13008a06, 0x01900060, + 0x13008a06, 0x01900060, 0x43020a04, 0x00100060, + 0x1b00ca05, 0x00100060, 0x23010a07, 0x01500060, + 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, + 0x13008a06, 0x01900060, 0x13008a06, 0x01900060, + 0x23010a07, 0x01500060, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x00100010, 0x0200140d, 0x00100050, + 0x0b004a0e, 0x01900070, 0x13008a0e, 0x01900070, + 0x13008a0e, 0x01900070, 0x43020a0c, 0x00100070, + 0x1b00ca0d, 0x00100070, 0x23010a0f, 0x01500070, + 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, + 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, + 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x50029404, 0x00100000, 0x32019405, 0x00100040, + 0x0b004a06, 0x01900060, 0x0b004a06, 0x01900060, + 0x5b02ca04, 0x00100060, 0x3b01d405, 0x00100060, + 0x23010a07, 0x01500060, 0x00000000, 0x00000000, + 0x5802d404, 0x00100000, 0x3b01d405, 0x00100060, + 0x0b004a06, 0x01900060, 0x23010a07, 0x01500060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x5002940c, 0x00100010, 0x3201940d, 0x00100050, + 0x0b004a0e, 0x01900070, 0x0b004a0e, 0x01900070, + 0x5b02ca0c, 0x00100070, 0x3b01d40d, 0x00100070, + 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, + 0x5802d40c, 0x00100010, 0x3b01d40d, 0x00100070, + 0x0b004a0e, 0x01900070, 0x23010a0f, 0x01500070, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x000f4800, 0x62031405, 0x00100040, + 0x53028a06, 0x01900060, 0x53028a07, 0x01900060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x000f4808, 0x6203140d, 0x00100048, + 0x53028a0e, 0x01900068, 0x53028a0f, 0x01900068, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000a0c, 0x00100004, 0x11008a0d, 0x00100024, + 0x1980c50e, 0x00100034, 0x2181050e, 0x00100034, + 0x2181050e, 0x00100034, 0x0180050c, 0x00100038, + 0x1180850d, 0x00100038, 0x1181850d, 0x00100038, + 0x2981450f, 0x01100038, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, + 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, + 0x1181850d, 0x00100038, 0x2981450f, 0x01100038, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, + 0x0180c506, 0x00100030, 0x0180c506, 0x00100030, + 0x2180c50c, 0x00100030, 0x49820a0d, 0x0016a130, + 0x41824a0d, 0x0016a130, 0x2981450f, 0x01100030, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x2000ca0c, 0x00100000, 0x49820a0d, 0x0016a130, + 0x1980c50e, 0x00100030, 0x41824a0d, 0x0016a130, + 0x2981450f, 0x01100030, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x00100008, 0x0200140d, 0x00100048, + 0x0b004a0e, 0x01900068, 0x13008a0e, 0x01900068, + 0x13008a0e, 0x01900068, 0x43020a0c, 0x00100070, + 0x1b00ca0d, 0x00100070, 0x1b014a0d, 0x00100070, + 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, + 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, + 0x1b014a0d, 0x00100070, 0x23010a0f, 0x01500070, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x50029404, 0x00100000, 0x32019405, 0x00100040, + 0x03004a06, 0x01900060, 0x03004a06, 0x01900060, + 0x6b030a0c, 0x00100060, 0x4b02140d, 0x0016a160, + 0x4302540d, 0x0016a160, 0x23010a0f, 0x01500060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x6b03140c, 0x00100060, 0x4b02140d, 0x0016a160, + 0x0b004a0e, 0x01900060, 0x4302540d, 0x0016a160, + 0x23010a0f, 0x01500060, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, + 0x53028a06, 0x01900060, 0x5b02ca06, 0x01900060, + 0x5b02ca06, 0x01900060, 0x43020a04, 0x00100060, + 0x1b00ca05, 0x00100060, 0x53028a07, 0x0190c060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, + 0x53028a0e, 0x01900070, 0x5b02ca0e, 0x01900070, + 0x5b02ca0e, 0x01900070, 0x43020a0c, 0x00100070, + 0x1b00ca0d, 0x00100070, 0x53028a0f, 0x0190c070, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, + 0x5b02ca06, 0x01900060, 0x5b02ca06, 0x01900060, + 0x53028a07, 0x0190c060, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, + 0x5b02ca0e, 0x01900070, 0x5b02ca0e, 0x01900070, + 0x53028a0f, 0x0190c070, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u16 b43_ntab_pilot_r3[] = { + 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, + 0xff08, 0xff08, 0x80d5, 0x80d5, 0x80d5, 0x80d5, + 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0xff0a, 0xff82, + 0xffa0, 0xff28, 0xffff, 0xffff, 0xffff, 0xffff, + 0xff82, 0xffa0, 0xff28, 0xff0a, 0xffff, 0xffff, + 0xffff, 0xffff, 0xf83f, 0xfa1f, 0xfa97, 0xfab5, + 0xf2bd, 0xf0bf, 0xffff, 0xffff, 0xf017, 0xf815, + 0xf215, 0xf095, 0xf035, 0xf01d, 0xffff, 0xffff, + 0xff08, 0xff02, 0xff80, 0xff20, 0xff08, 0xff02, + 0xff80, 0xff20, 0xf01f, 0xf817, 0xfa15, 0xf295, + 0xf0b5, 0xf03d, 0xffff, 0xffff, 0xf82a, 0xfa0a, + 0xfa82, 0xfaa0, 0xf2a8, 0xf0aa, 0xffff, 0xffff, + 0xf002, 0xf800, 0xf200, 0xf080, 0xf020, 0xf008, + 0xffff, 0xffff, 0xf00a, 0xf802, 0xfa00, 0xf280, + 0xf0a0, 0xf028, 0xffff, 0xffff, +}; + +static const u32 b43_ntab_tmap_r3[] = { + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, + 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, + 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, + 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, + 0xf1111110, 0x11111111, 0x11f11111, 0x00011111, + 0x11110000, 0x1111f111, 0x11111111, 0x111111f1, + 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa, + 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88, + 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888, + 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808, + 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08, + 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080, + 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, + 0x22000000, 0x2222b222, 0x22222222, 0x222222b2, + 0xb2222220, 0x22222222, 0x22d22222, 0x00000222, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x33000000, 0x3333b333, 0x33333333, 0x333333b3, + 0xb3333330, 0x33333333, 0x33d33333, 0x00000333, + 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, + 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, + 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, + 0x22222200, 0x2222f222, 0x22222222, 0x222222f2, + 0x22222222, 0x22222222, 0x22f22222, 0x00000222, + 0x11000000, 0x1111f111, 0x11111111, 0x11111111, + 0xf1111111, 0x11111111, 0x11f11111, 0x01111111, + 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b, + 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb, + 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa, + 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb, + 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999, + 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88, + 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a, + 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b, + 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909, + 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08, + 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a, + 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090, + 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090, + 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080, + 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0, + 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, + 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, + 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, + 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, + 0x33000000, 0x3333f333, 0x33333333, 0x333333f3, + 0xf3333330, 0x33333333, 0x33f33333, 0x00000333, + 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, + 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, + 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, + 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_intlevel_r3[] = { + 0x00802070, 0x0671188d, 0x0a60192c, 0x0a300e46, + 0x00c1188d, 0x080024d2, 0x00000070, +}; + +static const u32 b43_ntab_tdtrn_r3[] = { + 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, + 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, + 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, + 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, + 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, + 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, + 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, + 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, + 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, + 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, + 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, + 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, + 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, + 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, + 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, + 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, + 0xfa58fa58, 0xf895043b, 0xff4c09c0, 0xfbc6ffa8, + 0xfb84f384, 0x0798f6f9, 0x05760122, 0x058409f6, + 0x0b500000, 0x05b7f542, 0x08860432, 0x06ddfee7, + 0xfb84f384, 0xf9d90664, 0xf7e8025c, 0x00fff7bd, + 0x05a805a8, 0xf7bd00ff, 0x025cf7e8, 0x0664f9d9, + 0xf384fb84, 0xfee706dd, 0x04320886, 0xf54205b7, + 0x00000b50, 0x09f60584, 0x01220576, 0xf6f90798, + 0xf384fb84, 0xffa8fbc6, 0x09c0ff4c, 0x043bf895, + 0x02d402d4, 0x07de0270, 0xfc96079c, 0xf90afe94, + 0xfe00ff2c, 0x02d4065d, 0x092a0096, 0x0014fbb8, + 0xfd2cfd2c, 0x076afb3c, 0x0096f752, 0xf991fd87, + 0xfb2c0200, 0xfeb8f960, 0x08e0fc96, 0x049802a8, + 0xfd2cfd2c, 0x02a80498, 0xfc9608e0, 0xf960feb8, + 0x0200fb2c, 0xfd87f991, 0xf7520096, 0xfb3c076a, + 0xfd2cfd2c, 0xfbb80014, 0x0096092a, 0x065d02d4, + 0xff2cfe00, 0xfe94f90a, 0x079cfc96, 0x027007de, + 0x02d402d4, 0x027007de, 0x079cfc96, 0xfe94f90a, + 0xff2cfe00, 0x065d02d4, 0x0096092a, 0xfbb80014, + 0xfd2cfd2c, 0xfb3c076a, 0xf7520096, 0xfd87f991, + 0x0200fb2c, 0xf960feb8, 0xfc9608e0, 0x02a80498, + 0xfd2cfd2c, 0x049802a8, 0x08e0fc96, 0xfeb8f960, + 0xfb2c0200, 0xf991fd87, 0x0096f752, 0x076afb3c, + 0xfd2cfd2c, 0x0014fbb8, 0x092a0096, 0x02d4065d, + 0xfe00ff2c, 0xf90afe94, 0xfc96079c, 0x07de0270, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, + 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, + 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, + 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, + 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, + 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, + 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, + 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, + 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, + 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, + 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, + 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, + 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, + 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, + 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, + 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, + 0x061c061c, 0xff30009d, 0xffb21141, 0xfd87fb54, + 0xf65dfe59, 0x02eef99e, 0x0166f03c, 0xfff809b6, + 0x000008a4, 0x000af42b, 0x00eff577, 0xfa840bf2, + 0xfc02ff51, 0x08260f67, 0xfff0036f, 0x0842f9c3, + 0x00000000, 0x063df7be, 0xfc910010, 0xf099f7da, + 0x00af03fe, 0xf40e057c, 0x0a89ff11, 0x0bd5fff6, + 0xf75c0000, 0xf64a0008, 0x0fc4fe9a, 0x0662fd12, + 0x01a709a3, 0x04ac0279, 0xeebf004e, 0xff6300d0, + 0xf9e4f9e4, 0x00d0ff63, 0x004eeebf, 0x027904ac, + 0x09a301a7, 0xfd120662, 0xfe9a0fc4, 0x0008f64a, + 0x0000f75c, 0xfff60bd5, 0xff110a89, 0x057cf40e, + 0x03fe00af, 0xf7daf099, 0x0010fc91, 0xf7be063d, + 0x00000000, 0xf9c30842, 0x036ffff0, 0x0f670826, + 0xff51fc02, 0x0bf2fa84, 0xf57700ef, 0xf42b000a, + 0x08a40000, 0x09b6fff8, 0xf03c0166, 0xf99e02ee, + 0xfe59f65d, 0xfb54fd87, 0x1141ffb2, 0x009dff30, + 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, + 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, + 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, + 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, + 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, + 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, + 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, + 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, + 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, + 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, + 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, + 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, + 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, + 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, + 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, + 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, + 0xfa58fa58, 0xf8f0fe00, 0x0448073d, 0xfdc9fe46, + 0xf9910258, 0x089d0407, 0xfd5cf71a, 0x02affde0, + 0x083e0496, 0xff5a0740, 0xff7afd97, 0x00fe01f1, + 0x0009082e, 0xfa94ff75, 0xfecdf8ea, 0xffb0f693, + 0xfd2cfa58, 0x0433ff16, 0xfba405dd, 0xfa610341, + 0x06a606cb, 0x0039fd2d, 0x0677fa97, 0x01fa05e0, + 0xf896003e, 0x075a068b, 0x012cfc3e, 0xfa23f98d, + 0xfc7cfd43, 0xff90fc0d, 0x01c10982, 0x00c601d6, + 0xfd2cfd2c, 0x01d600c6, 0x098201c1, 0xfc0dff90, + 0xfd43fc7c, 0xf98dfa23, 0xfc3e012c, 0x068b075a, + 0x003ef896, 0x05e001fa, 0xfa970677, 0xfd2d0039, + 0x06cb06a6, 0x0341fa61, 0x05ddfba4, 0xff160433, + 0xfa58fd2c, 0xf693ffb0, 0xf8eafecd, 0xff75fa94, + 0x082e0009, 0x01f100fe, 0xfd97ff7a, 0x0740ff5a, + 0x0496083e, 0xfde002af, 0xf71afd5c, 0x0407089d, + 0x0258f991, 0xfe46fdc9, 0x073d0448, 0xfe00f8f0, + 0xfd2cfd2c, 0xfce00500, 0xfc09fddc, 0xfe680157, + 0x04c70571, 0xfc3aff21, 0xfcd70228, 0x056d0277, + 0x0200fe00, 0x0022f927, 0xfe3c032b, 0xfc44ff3c, + 0x03e9fbdb, 0x04570313, 0x04c9ff5c, 0x000d03b8, + 0xfa580000, 0xfbe900d2, 0xf9d0fe0b, 0x0125fdf9, + 0x042501bf, 0x0328fa2b, 0xffa902f0, 0xfa250157, + 0x0200fe00, 0x03740438, 0xff0405fd, 0x030cfe52, + 0x0037fb39, 0xff6904c5, 0x04f8fd23, 0xfd31fc1b, + 0xfd2cfd2c, 0xfc1bfd31, 0xfd2304f8, 0x04c5ff69, + 0xfb390037, 0xfe52030c, 0x05fdff04, 0x04380374, + 0xfe000200, 0x0157fa25, 0x02f0ffa9, 0xfa2b0328, + 0x01bf0425, 0xfdf90125, 0xfe0bf9d0, 0x00d2fbe9, + 0x0000fa58, 0x03b8000d, 0xff5c04c9, 0x03130457, + 0xfbdb03e9, 0xff3cfc44, 0x032bfe3c, 0xf9270022, + 0xfe000200, 0x0277056d, 0x0228fcd7, 0xff21fc3a, + 0x057104c7, 0x0157fe68, 0xfddcfc09, 0x0500fce0, + 0xfd2cfd2c, 0x0500fce0, 0xfddcfc09, 0x0157fe68, + 0x057104c7, 0xff21fc3a, 0x0228fcd7, 0x0277056d, + 0xfe000200, 0xf9270022, 0x032bfe3c, 0xff3cfc44, + 0xfbdb03e9, 0x03130457, 0xff5c04c9, 0x03b8000d, + 0x0000fa58, 0x00d2fbe9, 0xfe0bf9d0, 0xfdf90125, + 0x01bf0425, 0xfa2b0328, 0x02f0ffa9, 0x0157fa25, + 0xfe000200, 0x04380374, 0x05fdff04, 0xfe52030c, + 0xfb390037, 0x04c5ff69, 0xfd2304f8, 0xfc1bfd31, + 0xfd2cfd2c, 0xfd31fc1b, 0x04f8fd23, 0xff6904c5, + 0x0037fb39, 0x030cfe52, 0xff0405fd, 0x03740438, + 0x0200fe00, 0xfa250157, 0xffa902f0, 0x0328fa2b, + 0x042501bf, 0x0125fdf9, 0xf9d0fe0b, 0xfbe900d2, + 0xfa580000, 0x000d03b8, 0x04c9ff5c, 0x04570313, + 0x03e9fbdb, 0xfc44ff3c, 0xfe3c032b, 0x0022f927, + 0x0200fe00, 0x056d0277, 0xfcd70228, 0xfc3aff21, + 0x04c70571, 0xfe680157, 0xfc09fddc, 0xfce00500, + 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, + 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, + 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, + 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, + 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, + 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, + 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, + 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, + 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, + 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, + 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, + 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, + 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, + 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, + 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, + 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, +}; + +static const u32 b43_ntab_noisevar_r3[] = { + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, +}; + +static const u16 b43_ntab_mcs_r3[] = { + 0x0000, 0x0008, 0x000a, 0x0010, 0x0012, 0x0019, + 0x001a, 0x001c, 0x0080, 0x0088, 0x008a, 0x0090, + 0x0092, 0x0099, 0x009a, 0x009c, 0x0100, 0x0108, + 0x010a, 0x0110, 0x0112, 0x0119, 0x011a, 0x011c, + 0x0180, 0x0188, 0x018a, 0x0190, 0x0192, 0x0199, + 0x019a, 0x019c, 0x0000, 0x0098, 0x00a0, 0x00a8, + 0x009a, 0x00a2, 0x00aa, 0x0120, 0x0128, 0x0128, + 0x0130, 0x0138, 0x0138, 0x0140, 0x0122, 0x012a, + 0x012a, 0x0132, 0x013a, 0x013a, 0x0142, 0x01a8, + 0x01b0, 0x01b8, 0x01b0, 0x01b8, 0x01c0, 0x01c8, + 0x01c0, 0x01c8, 0x01d0, 0x01d0, 0x01d8, 0x01aa, + 0x01b2, 0x01ba, 0x01b2, 0x01ba, 0x01c2, 0x01ca, + 0x01c2, 0x01ca, 0x01d2, 0x01d2, 0x01da, 0x0001, + 0x0002, 0x0004, 0x0009, 0x000c, 0x0011, 0x0014, + 0x0018, 0x0020, 0x0021, 0x0022, 0x0024, 0x0081, + 0x0082, 0x0084, 0x0089, 0x008c, 0x0091, 0x0094, + 0x0098, 0x00a0, 0x00a1, 0x00a2, 0x00a4, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, +}; + +static const u32 b43_ntab_tdi20a0_r3[] = { + 0x00091226, 0x000a1429, 0x000b56ad, 0x000c58b0, + 0x000d5ab3, 0x000e9cb6, 0x000f9eba, 0x0000c13d, + 0x00020301, 0x00030504, 0x00040708, 0x0005090b, + 0x00064b8e, 0x00095291, 0x000a5494, 0x000b9718, + 0x000c9927, 0x000d9b2a, 0x000edd2e, 0x000fdf31, + 0x000101b4, 0x000243b7, 0x000345bb, 0x000447be, + 0x00058982, 0x00068c05, 0x00099309, 0x000a950c, + 0x000bd78f, 0x000cd992, 0x000ddb96, 0x000f1d99, + 0x00005fa8, 0x0001422c, 0x0002842f, 0x00038632, + 0x00048835, 0x0005ca38, 0x0006ccbc, 0x0009d3bf, + 0x000b1603, 0x000c1806, 0x000d1a0a, 0x000e1c0d, + 0x000f5e10, 0x00008093, 0x00018297, 0x0002c49a, + 0x0003c680, 0x0004c880, 0x00060b00, 0x00070d00, + 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_tdi20a1_r3[] = { + 0x00014b26, 0x00028d29, 0x000393ad, 0x00049630, + 0x0005d833, 0x0006da36, 0x00099c3a, 0x000a9e3d, + 0x000bc081, 0x000cc284, 0x000dc488, 0x000f068b, + 0x0000488e, 0x00018b91, 0x0002d214, 0x0003d418, + 0x0004d6a7, 0x000618aa, 0x00071aae, 0x0009dcb1, + 0x000b1eb4, 0x000c0137, 0x000d033b, 0x000e053e, + 0x000f4702, 0x00008905, 0x00020c09, 0x0003128c, + 0x0004148f, 0x00051712, 0x00065916, 0x00091b19, + 0x000a1d28, 0x000b5f2c, 0x000c41af, 0x000d43b2, + 0x000e85b5, 0x000f87b8, 0x0000c9bc, 0x00024cbf, + 0x00035303, 0x00045506, 0x0005978a, 0x0006998d, + 0x00095b90, 0x000a5d93, 0x000b9f97, 0x000c821a, + 0x000d8400, 0x000ec600, 0x000fc800, 0x00010a00, + 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_tdi40a0_r3[] = { + 0x0011a346, 0x00136ccf, 0x0014f5d9, 0x001641e2, + 0x0017cb6b, 0x00195475, 0x001b2383, 0x001cad0c, + 0x001e7616, 0x0000821f, 0x00020ba8, 0x0003d4b2, + 0x00056447, 0x00072dd0, 0x0008b6da, 0x000a02e3, + 0x000b8c6c, 0x000d15f6, 0x0011e484, 0x0013ae0d, + 0x00153717, 0x00168320, 0x00180ca9, 0x00199633, + 0x001b6548, 0x001ceed1, 0x001eb7db, 0x0000c3e4, + 0x00024d6d, 0x000416f7, 0x0005a585, 0x00076f0f, + 0x0008f818, 0x000a4421, 0x000bcdab, 0x000d9734, + 0x00122649, 0x0013efd2, 0x001578dc, 0x0016c4e5, + 0x00184e6e, 0x001a17f8, 0x001ba686, 0x001d3010, + 0x001ef999, 0x00010522, 0x00028eac, 0x00045835, + 0x0005e74a, 0x0007b0d3, 0x00093a5d, 0x000a85e6, + 0x000c0f6f, 0x000dd8f9, 0x00126787, 0x00143111, + 0x0015ba9a, 0x00170623, 0x00188fad, 0x001a5936, + 0x001be84b, 0x001db1d4, 0x001f3b5e, 0x000146e7, + 0x00031070, 0x000499fa, 0x00062888, 0x0007f212, + 0x00097b9b, 0x000ac7a4, 0x000c50ae, 0x000e1a37, + 0x0012a94c, 0x001472d5, 0x0015fc5f, 0x00174868, + 0x0018d171, 0x001a9afb, 0x001c2989, 0x001df313, + 0x001f7c9c, 0x000188a5, 0x000351af, 0x0004db38, + 0x0006aa4d, 0x000833d7, 0x0009bd60, 0x000b0969, + 0x000c9273, 0x000e5bfc, 0x00132a8a, 0x0014b414, + 0x00163d9d, 0x001789a6, 0x001912b0, 0x001adc39, + 0x001c6bce, 0x001e34d8, 0x001fbe61, 0x0001ca6a, + 0x00039374, 0x00051cfd, 0x0006ec0b, 0x00087515, + 0x0009fe9e, 0x000b4aa7, 0x000cd3b1, 0x000e9d3a, + 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_tdi40a1_r3[] = { + 0x001edb36, 0x000129ca, 0x0002b353, 0x00047cdd, + 0x0005c8e6, 0x000791ef, 0x00091bf9, 0x000aaa07, + 0x000c3391, 0x000dfd1a, 0x00120923, 0x0013d22d, + 0x00155c37, 0x0016eacb, 0x00187454, 0x001a3dde, + 0x001b89e7, 0x001d12f0, 0x001f1cfa, 0x00016b88, + 0x00033492, 0x0004be1b, 0x00060a24, 0x0007d32e, + 0x00095d38, 0x000aec4c, 0x000c7555, 0x000e3edf, + 0x00124ae8, 0x001413f1, 0x0015a37b, 0x00172c89, + 0x0018b593, 0x001a419c, 0x001bcb25, 0x001d942f, + 0x001f63b9, 0x0001ad4d, 0x00037657, 0x0004c260, + 0x00068be9, 0x000814f3, 0x0009a47c, 0x000b2d8a, + 0x000cb694, 0x000e429d, 0x00128c26, 0x001455b0, + 0x0015e4ba, 0x00176e4e, 0x0018f758, 0x001a8361, + 0x001c0cea, 0x001dd674, 0x001fa57d, 0x0001ee8b, + 0x0003b795, 0x0005039e, 0x0006cd27, 0x000856b1, + 0x0009e5c6, 0x000b6f4f, 0x000cf859, 0x000e8462, + 0x00130deb, 0x00149775, 0x00162603, 0x0017af8c, + 0x00193896, 0x001ac49f, 0x001c4e28, 0x001e17b2, + 0x0000a6c7, 0x00023050, 0x0003f9da, 0x00054563, + 0x00070eec, 0x00089876, 0x000a2704, 0x000bb08d, + 0x000d3a17, 0x001185a0, 0x00134f29, 0x0014d8b3, + 0x001667c8, 0x0017f151, 0x00197adb, 0x001b0664, + 0x001c8fed, 0x001e5977, 0x0000e805, 0x0002718f, + 0x00043b18, 0x000586a1, 0x0007502b, 0x0008d9b4, + 0x000a68c9, 0x000bf252, 0x000dbbdc, 0x0011c7e5, + 0x001390ee, 0x00151a78, 0x0016a906, 0x00183290, + 0x0019bc19, 0x001b4822, 0x001cd12c, 0x001e9ab5, + 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_pilotlt_r3[] = { + 0x76540213, 0x62407351, 0x76543210, 0x76540213, + 0x76540213, 0x76430521, +}; + +static const u32 b43_ntab_channelest_r3[] = { + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, +}; + +static const u8 b43_ntab_framelookup_r3[] = { + 0x02, 0x04, 0x14, 0x14, 0x03, 0x05, 0x16, 0x16, + 0x0a, 0x0c, 0x1c, 0x1c, 0x0b, 0x0d, 0x1e, 0x1e, + 0x06, 0x08, 0x18, 0x18, 0x07, 0x09, 0x1a, 0x1a, + 0x0e, 0x10, 0x20, 0x28, 0x0f, 0x11, 0x22, 0x2a, +}; + +static const u8 b43_ntab_estimatepowerlt0_r3[] = { + 0x55, 0x54, 0x54, 0x53, 0x52, 0x52, 0x51, 0x51, + 0x50, 0x4f, 0x4f, 0x4e, 0x4e, 0x4d, 0x4c, 0x4c, + 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x47, 0x46, 0x46, + 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x40, 0x3f, + 0x3e, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x37, 0x36, + 0x35, 0x33, 0x32, 0x31, 0x2f, 0x2e, 0x2c, 0x2b, + 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1a, + 0x18, 0x15, 0x12, 0x0e, 0x0b, 0x07, 0x02, 0xfd, +}; + +static const u8 b43_ntab_estimatepowerlt1_r3[] = { + 0x55, 0x54, 0x54, 0x53, 0x52, 0x52, 0x51, 0x51, + 0x50, 0x4f, 0x4f, 0x4e, 0x4e, 0x4d, 0x4c, 0x4c, + 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x47, 0x46, 0x46, + 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x40, 0x3f, + 0x3e, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x37, 0x36, + 0x35, 0x33, 0x32, 0x31, 0x2f, 0x2e, 0x2c, 0x2b, + 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1a, + 0x18, 0x15, 0x12, 0x0e, 0x0b, 0x07, 0x02, 0xfd, +}; + +static const u8 b43_ntab_adjustpower0_r3[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const u8 b43_ntab_adjustpower1_r3[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const u32 b43_ntab_gainctl0_r3[] = { + 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, + 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, + 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, + 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, + 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, + 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, + 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, + 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, + 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, + 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, + 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, + 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, + 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, + 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, + 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, + 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, + 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, + 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, + 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, + 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, + 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, + 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, + 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, + 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, + 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, + 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, + 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, + 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, + 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, + 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, + 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, + 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, +}; + +static const u32 b43_ntab_gainctl1_r3[] = { + 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, + 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, + 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, + 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, + 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, + 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, + 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, + 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, + 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, + 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, + 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, + 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, + 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, + 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, + 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, + 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, + 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, + 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, + 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, + 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, + 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, + 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, + 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, + 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, + 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, + 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, + 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, + 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, + 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, + 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, + 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, + 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, +}; + +static const u32 b43_ntab_iqlt0_r3[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_iqlt1_r3[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u16 b43_ntab_loftlt0_r3[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, +}; + +static const u16 b43_ntab_loftlt1_r3[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, +}; + +/* volatile tables, PHY revision >= 3 */ + +/* indexed by antswctl2g */ +static const u16 b43_ntab_antswctl_r3[4][32] = { + { + 0x0082, 0x0082, 0x0211, 0x0222, 0x0328, + 0x0000, 0x0000, 0x0000, 0x0144, 0x0000, + 0x0000, 0x0000, 0x0188, 0x0000, 0x0000, + 0x0000, 0x0082, 0x0082, 0x0211, 0x0222, + 0x0328, 0x0000, 0x0000, 0x0000, 0x0144, + 0x0000, 0x0000, 0x0000, 0x0188, 0x0000, + 0x0000, 0x0000, + }, + { + 0x0022, 0x0022, 0x0011, 0x0022, 0x0022, + 0x0000, 0x0000, 0x0000, 0x0011, 0x0000, + 0x0000, 0x0000, 0x0022, 0x0000, 0x0000, + 0x0000, 0x0022, 0x0022, 0x0011, 0x0022, + 0x0022, 0x0000, 0x0000, 0x0000, 0x0011, + 0x0000, 0x0000, 0x0000, 0x0022, 0x0000, + 0x0000, 0x0000, + }, + { + 0x0088, 0x0088, 0x0044, 0x0088, 0x0088, + 0x0000, 0x0000, 0x0000, 0x0044, 0x0000, + 0x0000, 0x0000, 0x0088, 0x0000, 0x0000, + 0x0000, 0x0088, 0x0088, 0x0044, 0x0088, + 0x0088, 0x0000, 0x0000, 0x0000, 0x0044, + 0x0000, 0x0000, 0x0000, 0x0088, 0x0000, + 0x0000, 0x0000, + }, + { + 0x0022, 0x0022, 0x0011, 0x0022, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0011, 0x0000, + 0x0000, 0x0000, 0x0022, 0x0000, 0x0000, + 0x03cc, 0x0022, 0x0022, 0x0011, 0x0022, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0011, + 0x0000, 0x0000, 0x0000, 0x0022, 0x0000, + 0x0000, 0x03cc, + } +}; + +/* static tables, PHY revision >= 7 */ + +/* Copied from brcmsmac (5.75.11) */ +static const u32 b43_ntab_tmap_r7[] = { + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, + 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, + 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, + 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, + 0xf1111110, 0x11111111, 0x11f11111, 0x00011111, + 0x11110000, 0x1111f111, 0x11111111, 0x111111f1, + 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa, + 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88, + 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888, + 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808, + 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08, + 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080, + 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, + 0x22000000, 0x2222b222, 0x22222222, 0x222222b2, + 0xb2222220, 0x22222222, 0x22d22222, 0x00000222, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x33000000, 0x3333b333, 0x33333333, 0x333333b3, + 0xb3333330, 0x33333333, 0x33d33333, 0x00000333, + 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, + 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, + 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, + 0x22222200, 0x2222f222, 0x22222222, 0x222222f2, + 0x22222222, 0x22222222, 0x22f22222, 0x00000222, + 0x11000000, 0x1111f111, 0x11111111, 0x11111111, + 0xf1111111, 0x11111111, 0x11f11111, 0x01111111, + 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b, + 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb, + 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa, + 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb, + 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999, + 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88, + 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a, + 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b, + 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909, + 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08, + 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a, + 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090, + 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090, + 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080, + 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0, + 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, + 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, + 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, + 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, + 0x33000000, 0x3333f333, 0x33333333, 0x333333f3, + 0xf3333330, 0x33333333, 0x33f33333, 0x00000333, + 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, + 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, + 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, + 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, + 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +/* Extracted from MMIO dump of 6.30.223.141 */ +static const u32 b43_ntab_noisevar_r7[] = { + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, +}; + +/************************************************** + * TX gain tables + **************************************************/ + +static const u32 b43_ntab_tx_gain_rev0_1_2[] = { + 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, + 0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44, + 0x03c82a42, 0x03c82944, 0x03c82942, 0x03c82844, + 0x03c82842, 0x03c42b44, 0x03c42b42, 0x03c42a44, + 0x03c42a42, 0x03c42944, 0x03c42942, 0x03c42844, + 0x03c42842, 0x03c42744, 0x03c42742, 0x03c42644, + 0x03c42642, 0x03c42544, 0x03c42542, 0x03c42444, + 0x03c42442, 0x03c02b44, 0x03c02b42, 0x03c02a44, + 0x03c02a42, 0x03c02944, 0x03c02942, 0x03c02844, + 0x03c02842, 0x03c02744, 0x03c02742, 0x03b02b44, + 0x03b02b42, 0x03b02a44, 0x03b02a42, 0x03b02944, + 0x03b02942, 0x03b02844, 0x03b02842, 0x03b02744, + 0x03b02742, 0x03b02644, 0x03b02642, 0x03b02544, + 0x03b02542, 0x03a02b44, 0x03a02b42, 0x03a02a44, + 0x03a02a42, 0x03a02944, 0x03a02942, 0x03a02844, + 0x03a02842, 0x03a02744, 0x03a02742, 0x03902b44, + 0x03902b42, 0x03902a44, 0x03902a42, 0x03902944, + 0x03902942, 0x03902844, 0x03902842, 0x03902744, + 0x03902742, 0x03902644, 0x03902642, 0x03902544, + 0x03902542, 0x03802b44, 0x03802b42, 0x03802a44, + 0x03802a42, 0x03802944, 0x03802942, 0x03802844, + 0x03802842, 0x03802744, 0x03802742, 0x03802644, + 0x03802642, 0x03802544, 0x03802542, 0x03802444, + 0x03802442, 0x03802344, 0x03802342, 0x03802244, + 0x03802242, 0x03802144, 0x03802142, 0x03802044, + 0x03802042, 0x03801f44, 0x03801f42, 0x03801e44, + 0x03801e42, 0x03801d44, 0x03801d42, 0x03801c44, + 0x03801c42, 0x03801b44, 0x03801b42, 0x03801a44, + 0x03801a42, 0x03801944, 0x03801942, 0x03801844, + 0x03801842, 0x03801744, 0x03801742, 0x03801644, + 0x03801642, 0x03801544, 0x03801542, 0x03801444, + 0x03801442, 0x03801344, 0x03801342, 0x00002b00, +}; + +/* EPA 2 GHz */ + +static const u32 b43_ntab_tx_gain_epa_rev3_2g[] = { + 0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e, + 0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037, + 0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e, + 0x1e41003c, 0x1e41003b, 0x1e410039, 0x1e410037, + 0x1d410044, 0x1d410042, 0x1d410040, 0x1d41003e, + 0x1d41003c, 0x1d41003b, 0x1d410039, 0x1d410037, + 0x1c410044, 0x1c410042, 0x1c410040, 0x1c41003e, + 0x1c41003c, 0x1c41003b, 0x1c410039, 0x1c410037, + 0x1b410044, 0x1b410042, 0x1b410040, 0x1b41003e, + 0x1b41003c, 0x1b41003b, 0x1b410039, 0x1b410037, + 0x1a410044, 0x1a410042, 0x1a410040, 0x1a41003e, + 0x1a41003c, 0x1a41003b, 0x1a410039, 0x1a410037, + 0x19410044, 0x19410042, 0x19410040, 0x1941003e, + 0x1941003c, 0x1941003b, 0x19410039, 0x19410037, + 0x18410044, 0x18410042, 0x18410040, 0x1841003e, + 0x1841003c, 0x1841003b, 0x18410039, 0x18410037, + 0x17410044, 0x17410042, 0x17410040, 0x1741003e, + 0x1741003c, 0x1741003b, 0x17410039, 0x17410037, + 0x16410044, 0x16410042, 0x16410040, 0x1641003e, + 0x1641003c, 0x1641003b, 0x16410039, 0x16410037, + 0x15410044, 0x15410042, 0x15410040, 0x1541003e, + 0x1541003c, 0x1541003b, 0x15410039, 0x15410037, + 0x14410044, 0x14410042, 0x14410040, 0x1441003e, + 0x1441003c, 0x1441003b, 0x14410039, 0x14410037, + 0x13410044, 0x13410042, 0x13410040, 0x1341003e, + 0x1341003c, 0x1341003b, 0x13410039, 0x13410037, + 0x12410044, 0x12410042, 0x12410040, 0x1241003e, + 0x1241003c, 0x1241003b, 0x12410039, 0x12410037, + 0x11410044, 0x11410042, 0x11410040, 0x1141003e, + 0x1141003c, 0x1141003b, 0x11410039, 0x11410037, + 0x10410044, 0x10410042, 0x10410040, 0x1041003e, + 0x1041003c, 0x1041003b, 0x10410039, 0x10410037, +}; + +static const u32 b43_ntab_tx_gain_epa_rev3_hi_pwr_2g[] = { + 0x0f410044, 0x0f410042, 0x0f410040, 0x0f41003e, + 0x0f41003c, 0x0f41003b, 0x0f410039, 0x0f410037, + 0x0e410044, 0x0e410042, 0x0e410040, 0x0e41003e, + 0x0e41003c, 0x0e41003b, 0x0e410039, 0x0e410037, + 0x0d410044, 0x0d410042, 0x0d410040, 0x0d41003e, + 0x0d41003c, 0x0d41003b, 0x0d410039, 0x0d410037, + 0x0c410044, 0x0c410042, 0x0c410040, 0x0c41003e, + 0x0c41003c, 0x0c41003b, 0x0c410039, 0x0c410037, + 0x0b410044, 0x0b410042, 0x0b410040, 0x0b41003e, + 0x0b41003c, 0x0b41003b, 0x0b410039, 0x0b410037, + 0x0a410044, 0x0a410042, 0x0a410040, 0x0a41003e, + 0x0a41003c, 0x0a41003b, 0x0a410039, 0x0a410037, + 0x09410044, 0x09410042, 0x09410040, 0x0941003e, + 0x0941003c, 0x0941003b, 0x09410039, 0x09410037, + 0x08410044, 0x08410042, 0x08410040, 0x0841003e, + 0x0841003c, 0x0841003b, 0x08410039, 0x08410037, + 0x07410044, 0x07410042, 0x07410040, 0x0741003e, + 0x0741003c, 0x0741003b, 0x07410039, 0x07410037, + 0x06410044, 0x06410042, 0x06410040, 0x0641003e, + 0x0641003c, 0x0641003b, 0x06410039, 0x06410037, + 0x05410044, 0x05410042, 0x05410040, 0x0541003e, + 0x0541003c, 0x0541003b, 0x05410039, 0x05410037, + 0x04410044, 0x04410042, 0x04410040, 0x0441003e, + 0x0441003c, 0x0441003b, 0x04410039, 0x04410037, + 0x03410044, 0x03410042, 0x03410040, 0x0341003e, + 0x0341003c, 0x0341003b, 0x03410039, 0x03410037, + 0x02410044, 0x02410042, 0x02410040, 0x0241003e, + 0x0241003c, 0x0241003b, 0x02410039, 0x02410037, + 0x01410044, 0x01410042, 0x01410040, 0x0141003e, + 0x0141003c, 0x0141003b, 0x01410039, 0x01410037, + 0x00410044, 0x00410042, 0x00410040, 0x0041003e, + 0x0041003c, 0x0041003b, 0x00410039, 0x00410037 +}; + +/* EPA 5 GHz */ + +static const u32 b43_ntab_tx_gain_epa_rev3_5g[] = { + 0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e, + 0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037, + 0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e, + 0xcef7003c, 0xcef7003b, 0xcef70039, 0xcef70037, + 0xcdf70044, 0xcdf70042, 0xcdf70040, 0xcdf7003e, + 0xcdf7003c, 0xcdf7003b, 0xcdf70039, 0xcdf70037, + 0xccf70044, 0xccf70042, 0xccf70040, 0xccf7003e, + 0xccf7003c, 0xccf7003b, 0xccf70039, 0xccf70037, + 0xcbf70044, 0xcbf70042, 0xcbf70040, 0xcbf7003e, + 0xcbf7003c, 0xcbf7003b, 0xcbf70039, 0xcbf70037, + 0xcaf70044, 0xcaf70042, 0xcaf70040, 0xcaf7003e, + 0xcaf7003c, 0xcaf7003b, 0xcaf70039, 0xcaf70037, + 0xc9f70044, 0xc9f70042, 0xc9f70040, 0xc9f7003e, + 0xc9f7003c, 0xc9f7003b, 0xc9f70039, 0xc9f70037, + 0xc8f70044, 0xc8f70042, 0xc8f70040, 0xc8f7003e, + 0xc8f7003c, 0xc8f7003b, 0xc8f70039, 0xc8f70037, + 0xc7f70044, 0xc7f70042, 0xc7f70040, 0xc7f7003e, + 0xc7f7003c, 0xc7f7003b, 0xc7f70039, 0xc7f70037, + 0xc6f70044, 0xc6f70042, 0xc6f70040, 0xc6f7003e, + 0xc6f7003c, 0xc6f7003b, 0xc6f70039, 0xc6f70037, + 0xc5f70044, 0xc5f70042, 0xc5f70040, 0xc5f7003e, + 0xc5f7003c, 0xc5f7003b, 0xc5f70039, 0xc5f70037, + 0xc4f70044, 0xc4f70042, 0xc4f70040, 0xc4f7003e, + 0xc4f7003c, 0xc4f7003b, 0xc4f70039, 0xc4f70037, + 0xc3f70044, 0xc3f70042, 0xc3f70040, 0xc3f7003e, + 0xc3f7003c, 0xc3f7003b, 0xc3f70039, 0xc3f70037, + 0xc2f70044, 0xc2f70042, 0xc2f70040, 0xc2f7003e, + 0xc2f7003c, 0xc2f7003b, 0xc2f70039, 0xc2f70037, + 0xc1f70044, 0xc1f70042, 0xc1f70040, 0xc1f7003e, + 0xc1f7003c, 0xc1f7003b, 0xc1f70039, 0xc1f70037, + 0xc0f70044, 0xc0f70042, 0xc0f70040, 0xc0f7003e, + 0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037, +}; + +static const u32 b43_ntab_tx_gain_epa_rev4_5g[] = { + 0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e, + 0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037, + 0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e, + 0x2ef2003c, 0x2ef2003b, 0x2ef20039, 0x2ef20037, + 0x2df20044, 0x2df20042, 0x2df20040, 0x2df2003e, + 0x2df2003c, 0x2df2003b, 0x2df20039, 0x2df20037, + 0x2cf20044, 0x2cf20042, 0x2cf20040, 0x2cf2003e, + 0x2cf2003c, 0x2cf2003b, 0x2cf20039, 0x2cf20037, + 0x2bf20044, 0x2bf20042, 0x2bf20040, 0x2bf2003e, + 0x2bf2003c, 0x2bf2003b, 0x2bf20039, 0x2bf20037, + 0x2af20044, 0x2af20042, 0x2af20040, 0x2af2003e, + 0x2af2003c, 0x2af2003b, 0x2af20039, 0x2af20037, + 0x29f20044, 0x29f20042, 0x29f20040, 0x29f2003e, + 0x29f2003c, 0x29f2003b, 0x29f20039, 0x29f20037, + 0x28f20044, 0x28f20042, 0x28f20040, 0x28f2003e, + 0x28f2003c, 0x28f2003b, 0x28f20039, 0x28f20037, + 0x27f20044, 0x27f20042, 0x27f20040, 0x27f2003e, + 0x27f2003c, 0x27f2003b, 0x27f20039, 0x27f20037, + 0x26f20044, 0x26f20042, 0x26f20040, 0x26f2003e, + 0x26f2003c, 0x26f2003b, 0x26f20039, 0x26f20037, + 0x25f20044, 0x25f20042, 0x25f20040, 0x25f2003e, + 0x25f2003c, 0x25f2003b, 0x25f20039, 0x25f20037, + 0x24f20044, 0x24f20042, 0x24f20040, 0x24f2003e, + 0x24f2003c, 0x24f2003b, 0x24f20039, 0x24f20038, + 0x23f20041, 0x23f20040, 0x23f2003f, 0x23f2003e, + 0x23f2003c, 0x23f2003b, 0x23f20039, 0x23f20037, + 0x22f20044, 0x22f20042, 0x22f20040, 0x22f2003e, + 0x22f2003c, 0x22f2003b, 0x22f20039, 0x22f20037, + 0x21f20044, 0x21f20042, 0x21f20040, 0x21f2003e, + 0x21f2003c, 0x21f2003b, 0x21f20039, 0x21f20037, + 0x20d20043, 0x20d20041, 0x20d2003e, 0x20d2003c, + 0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034, +}; + +static const u32 b43_ntab_tx_gain_epa_rev4_hi_pwr_5g[] = { + 0x2ff10044, 0x2ff10042, 0x2ff10040, 0x2ff1003e, + 0x2ff1003c, 0x2ff1003b, 0x2ff10039, 0x2ff10037, + 0x2ef10044, 0x2ef10042, 0x2ef10040, 0x2ef1003e, + 0x2ef1003c, 0x2ef1003b, 0x2ef10039, 0x2ef10037, + 0x2df10044, 0x2df10042, 0x2df10040, 0x2df1003e, + 0x2df1003c, 0x2df1003b, 0x2df10039, 0x2df10037, + 0x2cf10044, 0x2cf10042, 0x2cf10040, 0x2cf1003e, + 0x2cf1003c, 0x2cf1003b, 0x2cf10039, 0x2cf10037, + 0x2bf10044, 0x2bf10042, 0x2bf10040, 0x2bf1003e, + 0x2bf1003c, 0x2bf1003b, 0x2bf10039, 0x2bf10037, + 0x2af10044, 0x2af10042, 0x2af10040, 0x2af1003e, + 0x2af1003c, 0x2af1003b, 0x2af10039, 0x2af10037, + 0x29f10044, 0x29f10042, 0x29f10040, 0x29f1003e, + 0x29f1003c, 0x29f1003b, 0x29f10039, 0x29f10037, + 0x28f10044, 0x28f10042, 0x28f10040, 0x28f1003e, + 0x28f1003c, 0x28f1003b, 0x28f10039, 0x28f10037, + 0x27f10044, 0x27f10042, 0x27f10040, 0x27f1003e, + 0x27f1003c, 0x27f1003b, 0x27f10039, 0x27f10037, + 0x26f10044, 0x26f10042, 0x26f10040, 0x26f1003e, + 0x26f1003c, 0x26f1003b, 0x26f10039, 0x26f10037, + 0x25f10044, 0x25f10042, 0x25f10040, 0x25f1003e, + 0x25f1003c, 0x25f1003b, 0x25f10039, 0x25f10037, + 0x24f10044, 0x24f10042, 0x24f10040, 0x24f1003e, + 0x24f1003c, 0x24f1003b, 0x24f10039, 0x24f10038, + 0x23f10041, 0x23f10040, 0x23f1003f, 0x23f1003e, + 0x23f1003c, 0x23f1003b, 0x23f10039, 0x23f10037, + 0x22f10044, 0x22f10042, 0x22f10040, 0x22f1003e, + 0x22f1003c, 0x22f1003b, 0x22f10039, 0x22f10037, + 0x21f10044, 0x21f10042, 0x21f10040, 0x21f1003e, + 0x21f1003c, 0x21f1003b, 0x21f10039, 0x21f10037, + 0x20d10043, 0x20d10041, 0x20d1003e, 0x20d1003c, + 0x20d1003a, 0x20d10038, 0x20d10036, 0x20d10034 +}; + +static const u32 b43_ntab_tx_gain_epa_rev5_5g[] = { + 0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044, + 0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c, + 0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e, + 0x0e62003c, 0x0e62003d, 0x0e62003b, 0x0e62003a, + 0x0d620043, 0x0d620041, 0x0d620040, 0x0d62003e, + 0x0d62003d, 0x0d62003c, 0x0d62003b, 0x0d62003a, + 0x0c620041, 0x0c620040, 0x0c62003f, 0x0c62003e, + 0x0c62003c, 0x0c62003b, 0x0c620039, 0x0c620037, + 0x0b620046, 0x0b620044, 0x0b620042, 0x0b620040, + 0x0b62003e, 0x0b62003c, 0x0b62003b, 0x0b62003a, + 0x0a620041, 0x0a620040, 0x0a62003e, 0x0a62003c, + 0x0a62003b, 0x0a62003a, 0x0a620039, 0x0a620038, + 0x0962003e, 0x0962003d, 0x0962003c, 0x0962003b, + 0x09620039, 0x09620037, 0x09620035, 0x09620033, + 0x08620044, 0x08620042, 0x08620040, 0x0862003e, + 0x0862003c, 0x0862003b, 0x0862003a, 0x08620039, + 0x07620043, 0x07620042, 0x07620040, 0x0762003f, + 0x0762003d, 0x0762003b, 0x0762003a, 0x07620039, + 0x0662003e, 0x0662003d, 0x0662003c, 0x0662003b, + 0x06620039, 0x06620037, 0x06620035, 0x06620033, + 0x05620046, 0x05620044, 0x05620042, 0x05620040, + 0x0562003e, 0x0562003c, 0x0562003b, 0x05620039, + 0x04620044, 0x04620042, 0x04620040, 0x0462003e, + 0x0462003c, 0x0462003b, 0x04620039, 0x04620038, + 0x0362003c, 0x0362003b, 0x0362003a, 0x03620039, + 0x03620038, 0x03620037, 0x03620035, 0x03620033, + 0x0262004c, 0x0262004a, 0x02620048, 0x02620047, + 0x02620046, 0x02620044, 0x02620043, 0x02620042, + 0x0162004a, 0x01620048, 0x01620046, 0x01620044, + 0x01620043, 0x01620042, 0x01620041, 0x01620040, + 0x00620042, 0x00620040, 0x0062003e, 0x0062003c, + 0x0062003b, 0x00620039, 0x00620037, 0x00620035, +}; + +/* IPA 2 GHz */ + +static const u32 b43_ntab_tx_gain_ipa_rev3_2g[] = { + 0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029, + 0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025, + 0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029, + 0x5ef70028, 0x5ef70027, 0x5ef70026, 0x5ef70025, + 0x5df7002d, 0x5df7002b, 0x5df7002a, 0x5df70029, + 0x5df70028, 0x5df70027, 0x5df70026, 0x5df70025, + 0x5cf7002d, 0x5cf7002b, 0x5cf7002a, 0x5cf70029, + 0x5cf70028, 0x5cf70027, 0x5cf70026, 0x5cf70025, + 0x5bf7002d, 0x5bf7002b, 0x5bf7002a, 0x5bf70029, + 0x5bf70028, 0x5bf70027, 0x5bf70026, 0x5bf70025, + 0x5af7002d, 0x5af7002b, 0x5af7002a, 0x5af70029, + 0x5af70028, 0x5af70027, 0x5af70026, 0x5af70025, + 0x59f7002d, 0x59f7002b, 0x59f7002a, 0x59f70029, + 0x59f70028, 0x59f70027, 0x59f70026, 0x59f70025, + 0x58f7002d, 0x58f7002b, 0x58f7002a, 0x58f70029, + 0x58f70028, 0x58f70027, 0x58f70026, 0x58f70025, + 0x57f7002d, 0x57f7002b, 0x57f7002a, 0x57f70029, + 0x57f70028, 0x57f70027, 0x57f70026, 0x57f70025, + 0x56f7002d, 0x56f7002b, 0x56f7002a, 0x56f70029, + 0x56f70028, 0x56f70027, 0x56f70026, 0x56f70025, + 0x55f7002d, 0x55f7002b, 0x55f7002a, 0x55f70029, + 0x55f70028, 0x55f70027, 0x55f70026, 0x55f70025, + 0x54f7002d, 0x54f7002b, 0x54f7002a, 0x54f70029, + 0x54f70028, 0x54f70027, 0x54f70026, 0x54f70025, + 0x53f7002d, 0x53f7002b, 0x53f7002a, 0x53f70029, + 0x53f70028, 0x53f70027, 0x53f70026, 0x53f70025, + 0x52f7002d, 0x52f7002b, 0x52f7002a, 0x52f70029, + 0x52f70028, 0x52f70027, 0x52f70026, 0x52f70025, + 0x51f7002d, 0x51f7002b, 0x51f7002a, 0x51f70029, + 0x51f70028, 0x51f70027, 0x51f70026, 0x51f70025, + 0x50f7002d, 0x50f7002b, 0x50f7002a, 0x50f70029, + 0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025, +}; + +static const u32 b43_ntab_tx_gain_ipa_rev5_2g[] = { + 0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029, + 0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025, + 0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029, + 0x1ef70028, 0x1ef70027, 0x1ef70026, 0x1ef70025, + 0x1df7002d, 0x1df7002b, 0x1df7002a, 0x1df70029, + 0x1df70028, 0x1df70027, 0x1df70026, 0x1df70025, + 0x1cf7002d, 0x1cf7002b, 0x1cf7002a, 0x1cf70029, + 0x1cf70028, 0x1cf70027, 0x1cf70026, 0x1cf70025, + 0x1bf7002d, 0x1bf7002b, 0x1bf7002a, 0x1bf70029, + 0x1bf70028, 0x1bf70027, 0x1bf70026, 0x1bf70025, + 0x1af7002d, 0x1af7002b, 0x1af7002a, 0x1af70029, + 0x1af70028, 0x1af70027, 0x1af70026, 0x1af70025, + 0x19f7002d, 0x19f7002b, 0x19f7002a, 0x19f70029, + 0x19f70028, 0x19f70027, 0x19f70026, 0x19f70025, + 0x18f7002d, 0x18f7002b, 0x18f7002a, 0x18f70029, + 0x18f70028, 0x18f70027, 0x18f70026, 0x18f70025, + 0x17f7002d, 0x17f7002b, 0x17f7002a, 0x17f70029, + 0x17f70028, 0x17f70027, 0x17f70026, 0x17f70025, + 0x16f7002d, 0x16f7002b, 0x16f7002a, 0x16f70029, + 0x16f70028, 0x16f70027, 0x16f70026, 0x16f70025, + 0x15f7002d, 0x15f7002b, 0x15f7002a, 0x15f70029, + 0x15f70028, 0x15f70027, 0x15f70026, 0x15f70025, + 0x14f7002d, 0x14f7002b, 0x14f7002a, 0x14f70029, + 0x14f70028, 0x14f70027, 0x14f70026, 0x14f70025, + 0x13f7002d, 0x13f7002b, 0x13f7002a, 0x13f70029, + 0x13f70028, 0x13f70027, 0x13f70026, 0x13f70025, + 0x12f7002d, 0x12f7002b, 0x12f7002a, 0x12f70029, + 0x12f70028, 0x12f70027, 0x12f70026, 0x12f70025, + 0x11f7002d, 0x11f7002b, 0x11f7002a, 0x11f70029, + 0x11f70028, 0x11f70027, 0x11f70026, 0x11f70025, + 0x10f7002d, 0x10f7002b, 0x10f7002a, 0x10f70029, + 0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025, +}; + +static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = { + 0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029, + 0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025, + 0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029, + 0x0ef70028, 0x0ef70027, 0x0ef70026, 0x0ef70025, + 0x0df7002d, 0x0df7002b, 0x0df7002a, 0x0df70029, + 0x0df70028, 0x0df70027, 0x0df70026, 0x0df70025, + 0x0cf7002d, 0x0cf7002b, 0x0cf7002a, 0x0cf70029, + 0x0cf70028, 0x0cf70027, 0x0cf70026, 0x0cf70025, + 0x0bf7002d, 0x0bf7002b, 0x0bf7002a, 0x0bf70029, + 0x0bf70028, 0x0bf70027, 0x0bf70026, 0x0bf70025, + 0x0af7002d, 0x0af7002b, 0x0af7002a, 0x0af70029, + 0x0af70028, 0x0af70027, 0x0af70026, 0x0af70025, + 0x09f7002d, 0x09f7002b, 0x09f7002a, 0x09f70029, + 0x09f70028, 0x09f70027, 0x09f70026, 0x09f70025, + 0x08f7002d, 0x08f7002b, 0x08f7002a, 0x08f70029, + 0x08f70028, 0x08f70027, 0x08f70026, 0x08f70025, + 0x07f7002d, 0x07f7002b, 0x07f7002a, 0x07f70029, + 0x07f70028, 0x07f70027, 0x07f70026, 0x07f70025, + 0x06f7002d, 0x06f7002b, 0x06f7002a, 0x06f70029, + 0x06f70028, 0x06f70027, 0x06f70026, 0x06f70025, + 0x05f7002d, 0x05f7002b, 0x05f7002a, 0x05f70029, + 0x05f70028, 0x05f70027, 0x05f70026, 0x05f70025, + 0x04f7002d, 0x04f7002b, 0x04f7002a, 0x04f70029, + 0x04f70028, 0x04f70027, 0x04f70026, 0x04f70025, + 0x03f7002d, 0x03f7002b, 0x03f7002a, 0x03f70029, + 0x03f70028, 0x03f70027, 0x03f70026, 0x03f70025, + 0x02f7002d, 0x02f7002b, 0x02f7002a, 0x02f70029, + 0x02f70028, 0x02f70027, 0x02f70026, 0x02f70025, + 0x01f7002d, 0x01f7002b, 0x01f7002a, 0x01f70029, + 0x01f70028, 0x01f70027, 0x01f70026, 0x01f70025, + 0x00f7002d, 0x00f7002b, 0x00f7002a, 0x00f70029, + 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025, +}; + +/* Copied from brcmsmac (5.75.11): nphy_tpc_txgain_ipa_2g_2057rev5 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev5_2g[] = { + 0x30ff0031, 0x30e70031, 0x30e7002e, 0x30cf002e, + 0x30bf002e, 0x30af002e, 0x309f002f, 0x307f0033, + 0x307f0031, 0x307f002e, 0x3077002e, 0x306f002e, + 0x3067002e, 0x305f002f, 0x30570030, 0x3057002d, + 0x304f002e, 0x30470031, 0x3047002e, 0x3047002c, + 0x30470029, 0x303f002c, 0x303f0029, 0x3037002d, + 0x3037002a, 0x30370028, 0x302f002c, 0x302f002a, + 0x302f0028, 0x302f0026, 0x3027002c, 0x30270029, + 0x30270027, 0x30270025, 0x30270023, 0x301f002c, + 0x301f002a, 0x301f0028, 0x301f0025, 0x301f0024, + 0x301f0022, 0x301f001f, 0x3017002d, 0x3017002b, + 0x30170028, 0x30170026, 0x30170024, 0x30170022, + 0x30170020, 0x3017001e, 0x3017001d, 0x3017001b, + 0x3017001a, 0x30170018, 0x30170017, 0x30170015, + 0x300f002c, 0x300f0029, 0x300f0027, 0x300f0024, + 0x300f0022, 0x300f0021, 0x300f001f, 0x300f001d, + 0x300f001b, 0x300f001a, 0x300f0018, 0x300f0017, + 0x300f0016, 0x300f0015, 0x300f0115, 0x300f0215, + 0x300f0315, 0x300f0415, 0x300f0515, 0x300f0615, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, +}; + +/* Extracted from MMIO dump of 6.30.223.141 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = { + 0x60ff0031, 0x60e7002c, 0x60cf002a, 0x60c70029, + 0x60b70029, 0x60a70029, 0x609f002a, 0x6097002b, + 0x6087002e, 0x60770031, 0x606f0032, 0x60670034, + 0x60670031, 0x605f0033, 0x605f0031, 0x60570033, + 0x60570030, 0x6057002d, 0x6057002b, 0x604f002d, + 0x604f002b, 0x604f0029, 0x604f0026, 0x60470029, + 0x60470027, 0x603f0029, 0x603f0027, 0x603f0025, + 0x60370029, 0x60370027, 0x60370024, 0x602f002a, + 0x602f0028, 0x602f0026, 0x602f0024, 0x6027002a, + 0x60270028, 0x60270026, 0x60270024, 0x60270022, + 0x601f002b, 0x601f0029, 0x601f0027, 0x601f0024, + 0x601f0022, 0x601f0020, 0x601f001f, 0x601f001d, + 0x60170029, 0x60170027, 0x60170025, 0x60170023, + 0x60170021, 0x6017001f, 0x6017001d, 0x6017001c, + 0x6017001a, 0x60170018, 0x60170018, 0x60170016, + 0x60170015, 0x600f0029, 0x600f0027, 0x600f0025, + 0x600f0023, 0x600f0021, 0x600f001f, 0x600f001d, + 0x600f001c, 0x600f001a, 0x600f0019, 0x600f0018, + 0x600f0016, 0x600f0015, 0x600f0115, 0x600f0215, + 0x600f0315, 0x600f0415, 0x600f0515, 0x600f0615, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, +}; + +/* Extracted from MMIO dump of 6.30.223.248 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev14_2g[] = { + 0x50df002e, 0x50cf002d, 0x50bf002c, 0x50b7002b, + 0x50af002a, 0x50a70029, 0x509f0029, 0x50970028, + 0x508f0027, 0x50870027, 0x507f0027, 0x50770027, + 0x506f0027, 0x50670027, 0x505f0028, 0x50570029, + 0x504f002b, 0x5047002e, 0x5047002b, 0x50470029, + 0x503f002c, 0x503f0029, 0x5037002c, 0x5037002a, + 0x50370028, 0x502f002d, 0x502f002b, 0x502f0028, + 0x502f0026, 0x5027002d, 0x5027002a, 0x50270028, + 0x50270026, 0x50270024, 0x501f002e, 0x501f002b, + 0x501f0029, 0x501f0027, 0x501f0024, 0x501f0022, + 0x501f0020, 0x501f001f, 0x5017002c, 0x50170029, + 0x50170027, 0x50170024, 0x50170022, 0x50170021, + 0x5017001f, 0x5017001d, 0x5017001b, 0x5017001a, + 0x50170018, 0x50170017, 0x50170015, 0x500f002c, + 0x500f002a, 0x500f0027, 0x500f0025, 0x500f0023, + 0x500f0022, 0x500f001f, 0x500f001e, 0x500f001c, + 0x500f001a, 0x500f0019, 0x500f0018, 0x500f0016, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, +}; + +/* IPA 2 5Hz */ + +static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = { + 0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031, + 0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b, + 0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027, + 0x7ff70026, 0x7ff70024, 0x7ff70023, 0x7ff70022, + 0x7ef70028, 0x7ef70027, 0x7ef70026, 0x7ef70025, + 0x7ef70024, 0x7ef70023, 0x7df70028, 0x7df70027, + 0x7df70026, 0x7df70025, 0x7df70024, 0x7df70023, + 0x7df70022, 0x7cf70029, 0x7cf70028, 0x7cf70027, + 0x7cf70026, 0x7cf70025, 0x7cf70023, 0x7cf70022, + 0x7bf70029, 0x7bf70028, 0x7bf70026, 0x7bf70025, + 0x7bf70024, 0x7bf70023, 0x7bf70022, 0x7bf70021, + 0x7af70029, 0x7af70028, 0x7af70027, 0x7af70026, + 0x7af70025, 0x7af70024, 0x7af70023, 0x7af70022, + 0x79f70029, 0x79f70028, 0x79f70027, 0x79f70026, + 0x79f70025, 0x79f70024, 0x79f70023, 0x79f70022, + 0x78f70029, 0x78f70028, 0x78f70027, 0x78f70026, + 0x78f70025, 0x78f70024, 0x78f70023, 0x78f70022, + 0x77f70029, 0x77f70028, 0x77f70027, 0x77f70026, + 0x77f70025, 0x77f70024, 0x77f70023, 0x77f70022, + 0x76f70029, 0x76f70028, 0x76f70027, 0x76f70026, + 0x76f70024, 0x76f70023, 0x76f70022, 0x76f70021, + 0x75f70029, 0x75f70028, 0x75f70027, 0x75f70026, + 0x75f70025, 0x75f70024, 0x75f70023, 0x74f70029, + 0x74f70028, 0x74f70026, 0x74f70025, 0x74f70024, + 0x74f70023, 0x74f70022, 0x73f70029, 0x73f70027, + 0x73f70026, 0x73f70025, 0x73f70024, 0x73f70023, + 0x73f70022, 0x72f70028, 0x72f70027, 0x72f70026, + 0x72f70025, 0x72f70024, 0x72f70023, 0x72f70022, + 0x71f70028, 0x71f70027, 0x71f70026, 0x71f70025, + 0x71f70024, 0x71f70023, 0x70f70028, 0x70f70027, + 0x70f70026, 0x70f70024, 0x70f70023, 0x70f70022, + 0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f, +}; + +/* Extracted from MMIO dump of 6.30.223.141 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev9_5g[] = { + 0x7f7f0053, 0x7f7f004b, 0x7f7f0044, 0x7f7f003f, + 0x7f7f0039, 0x7f7f0035, 0x7f7f0032, 0x7f7f0030, + 0x7f7f002d, 0x7e7f0030, 0x7e7f002d, 0x7d7f0032, + 0x7d7f002f, 0x7d7f002c, 0x7c7f0032, 0x7c7f0030, + 0x7c7f002d, 0x7b7f0030, 0x7b7f002e, 0x7b7f002b, + 0x7a7f0032, 0x7a7f0030, 0x7a7f002d, 0x7a7f002b, + 0x797f0030, 0x797f002e, 0x797f002b, 0x797f0029, + 0x787f0030, 0x787f002d, 0x787f002b, 0x777f0032, + 0x777f0030, 0x777f002d, 0x777f002b, 0x767f0031, + 0x767f002f, 0x767f002c, 0x767f002a, 0x757f0031, + 0x757f002f, 0x757f002c, 0x757f002a, 0x747f0030, + 0x747f002d, 0x747f002b, 0x737f0032, 0x737f002f, + 0x737f002c, 0x737f002a, 0x727f0030, 0x727f002d, + 0x727f002b, 0x727f0029, 0x717f0030, 0x717f002d, + 0x717f002b, 0x707f0031, 0x707f002f, 0x707f002c, + 0x707f002a, 0x707f0027, 0x707f0025, 0x707f0023, + 0x707f0021, 0x707f001f, 0x707f001d, 0x707f001c, + 0x707f001a, 0x707f0019, 0x707f0017, 0x707f0016, + 0x707f0015, 0x707f0014, 0x707f0012, 0x707f0012, + 0x707f0011, 0x707f0010, 0x707f000f, 0x707f000e, + 0x707f000d, 0x707f000d, 0x707f000c, 0x707f000b, + 0x707f000a, 0x707f000a, 0x707f0009, 0x707f0008, + 0x707f0008, 0x707f0008, 0x707f0008, 0x707f0007, + 0x707f0007, 0x707f0006, 0x707f0006, 0x707f0006, + 0x707f0005, 0x707f0005, 0x707f0005, 0x707f0004, + 0x707f0004, 0x707f0004, 0x707f0003, 0x707f0003, + 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, + 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, + 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, + 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, + 0x707f0002, 0x707f0001, 0x707f0001, 0x707f0001, + 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001, +}; + +const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[] = { + -114, -108, -98, -91, -84, -78, -70, -62, + -54, -46, -39, -31, -23, -15, -8, 0 +}; + +/* Extracted from MMIO dump of 6.30.223.248 + * Entries: 0, 15, 17, 21, 24, 26, 27, 29, 30 were guessed + */ +static const s16 b43_ntab_rf_pwr_offset_2057_rev9_2g[] = { + -133, -133, -107, -92, -81, + -73, -66, -61, -56, -52, + -48, -44, -41, -37, -34, + -31, -28, -25, -22, -19, + -17, -14, -12, -10, -9, + -7, -5, -4, -3, -2, + -1, 0, +}; + +/* Extracted from MMIO dump of 6.30.223.248 */ +static const s16 b43_ntab_rf_pwr_offset_2057_rev9_5g[] = { + -101, -94, -86, -79, -72, + -65, -57, -50, -42, -35, + -28, -21, -16, -9, -4, + 0, +}; + +/* Extracted from MMIO dump of 6.30.223.248 + * Entries: 0, 26, 28, 29, 30, 31 were guessed + */ +static const s16 b43_ntab_rf_pwr_offset_2057_rev14_2g[] = { + -111, -111, -111, -84, -70, + -59, -52, -45, -40, -36, + -32, -29, -26, -23, -21, + -18, -16, -15, -13, -11, + -10, -8, -7, -6, -5, + -4, -4, -3, -3, -2, + -2, -1, +}; + +const u16 tbl_iqcal_gainparams[2][9][8] = { + { + { 0x000, 0, 0, 2, 0x69, 0x69, 0x69, 0x69 }, + { 0x700, 7, 0, 0, 0x69, 0x69, 0x69, 0x69 }, + { 0x710, 7, 1, 0, 0x68, 0x68, 0x68, 0x68 }, + { 0x720, 7, 2, 0, 0x67, 0x67, 0x67, 0x67 }, + { 0x730, 7, 3, 0, 0x66, 0x66, 0x66, 0x66 }, + { 0x740, 7, 4, 0, 0x65, 0x65, 0x65, 0x65 }, + { 0x741, 7, 4, 1, 0x65, 0x65, 0x65, 0x65 }, + { 0x742, 7, 4, 2, 0x65, 0x65, 0x65, 0x65 }, + { 0x743, 7, 4, 3, 0x65, 0x65, 0x65, 0x65 } + }, + { + { 0x000, 7, 0, 0, 0x79, 0x79, 0x79, 0x79 }, + { 0x700, 7, 0, 0, 0x79, 0x79, 0x79, 0x79 }, + { 0x710, 7, 1, 0, 0x79, 0x79, 0x79, 0x79 }, + { 0x720, 7, 2, 0, 0x78, 0x78, 0x78, 0x78 }, + { 0x730, 7, 3, 0, 0x78, 0x78, 0x78, 0x78 }, + { 0x740, 7, 4, 0, 0x78, 0x78, 0x78, 0x78 }, + { 0x741, 7, 4, 1, 0x78, 0x78, 0x78, 0x78 }, + { 0x742, 7, 4, 2, 0x78, 0x78, 0x78, 0x78 }, + { 0x743, 7, 4, 3, 0x78, 0x78, 0x78, 0x78 } + } +}; + +const struct nphy_txiqcal_ladder ladder_lo[] = { + { 3, 0 }, + { 4, 0 }, + { 6, 0 }, + { 9, 0 }, + { 13, 0 }, + { 18, 0 }, + { 25, 0 }, + { 25, 1 }, + { 25, 2 }, + { 25, 3 }, + { 25, 4 }, + { 25, 5 }, + { 25, 6 }, + { 25, 7 }, + { 35, 7 }, + { 50, 7 }, + { 71, 7 }, + { 100, 7 } +}; + +const struct nphy_txiqcal_ladder ladder_iq[] = { + { 3, 0 }, + { 4, 0 }, + { 6, 0 }, + { 9, 0 }, + { 13, 0 }, + { 18, 0 }, + { 25, 0 }, + { 35, 0 }, + { 50, 0 }, + { 71, 0 }, + { 100, 0 }, + { 100, 1 }, + { 100, 2 }, + { 100, 3 }, + { 100, 4 }, + { 100, 5 }, + { 100, 6 }, + { 100, 7 } +}; + +const u16 loscale[] = { + 256, 256, 271, 271, + 287, 256, 256, 271, + 271, 287, 287, 304, + 304, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 341, + 341, 362, 362, 383, + 383, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 256, + 256, 271, 271, 287, + 287, 304, 304, 322, + 322, 341, 341, 362, + 362, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 256, + 256, 271, 271, 287, + 287, 304, 304, 322, + 322, 341, 341, 362, + 362, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 341, + 341, 362, 362, 383, + 383, 406, 406, 430, + 430, 455, 455, 482, + 482, 511, 511, 541, + 541, 573, 573, 607, + 607, 643, 643, 681, + 681, 722, 722, 764, + 764, 810, 810, 858, + 858, 908, 908, 962, + 962, 1019, 1019, 256 +}; + +const u16 tbl_tx_iqlo_cal_loft_ladder_40[] = { + 0x0200, 0x0300, 0x0400, 0x0700, + 0x0900, 0x0c00, 0x1200, 0x1201, + 0x1202, 0x1203, 0x1204, 0x1205, + 0x1206, 0x1207, 0x1907, 0x2307, + 0x3207, 0x4707 +}; + +const u16 tbl_tx_iqlo_cal_loft_ladder_20[] = { + 0x0300, 0x0500, 0x0700, 0x0900, + 0x0d00, 0x1100, 0x1900, 0x1901, + 0x1902, 0x1903, 0x1904, 0x1905, + 0x1906, 0x1907, 0x2407, 0x3207, + 0x4607, 0x6407 +}; + +const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = { + 0x0100, 0x0200, 0x0400, 0x0700, + 0x0900, 0x0c00, 0x1200, 0x1900, + 0x2300, 0x3200, 0x4700, 0x4701, + 0x4702, 0x4703, 0x4704, 0x4705, + 0x4706, 0x4707 +}; + +const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = { + 0x0200, 0x0300, 0x0600, 0x0900, + 0x0d00, 0x1100, 0x1900, 0x2400, + 0x3200, 0x4600, 0x6400, 0x6401, + 0x6402, 0x6403, 0x6404, 0x6405, + 0x6406, 0x6407 +}; + +const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3] = { }; + +const u16 tbl_tx_iqlo_cal_startcoefs[B43_NTAB_TX_IQLO_CAL_STARTCOEFS] = { }; + +const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = { + 0x8423, 0x8323, 0x8073, 0x8256, + 0x8045, 0x8223, 0x9423, 0x9323, + 0x9073, 0x9256, 0x9045, 0x9223 +}; + +const u16 tbl_tx_iqlo_cal_cmds_recal[] = { + 0x8101, 0x8253, 0x8053, 0x8234, + 0x8034, 0x9101, 0x9253, 0x9053, + 0x9234, 0x9034 +}; + +const u16 tbl_tx_iqlo_cal_cmds_fullcal[] = { + 0x8123, 0x8264, 0x8086, 0x8245, + 0x8056, 0x9123, 0x9264, 0x9086, + 0x9245, 0x9056 +}; + +const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = { + 0x8434, 0x8334, 0x8084, 0x8267, + 0x8056, 0x8234, 0x9434, 0x9334, + 0x9084, 0x9267, 0x9056, 0x9234 +}; + +const s16 tbl_tx_filter_coef_rev4[7][15] = { + { -377, 137, -407, 208, -1527, + 956, 93, 186, 93, 230, + -44, 230, 201, -191, 201 }, + { -77, 20, -98, 49, -93, + 60, 56, 111, 56, 26, + -5, 26, 34, -32, 34 }, + { -360, 164, -376, 164, -1533, + 576, 308, -314, 308, 121, + -73, 121, 91, 124, 91 }, + { -295, 200, -363, 142, -1391, + 826, 151, 301, 151, 151, + 301, 151, 602, -752, 602 }, + { -92, 58, -96, 49, -104, + 44, 17, 35, 17, 12, + 25, 12, 13, 27, 13 }, + { -375, 136, -399, 209, -1479, + 949, 130, 260, 130, 230, + -44, 230, 201, -191, 201 }, + { 0xed9, 0xc8, 0xe95, 0x8e, 0xa91, + 0x33a, 0x97, 0x12d, 0x97, 0x97, + 0x12d, 0x97, 0x25a, 0xd10, 0x25a } +}; + +/* addr0, addr1, bmask, shift */ +const struct nphy_rf_control_override_rev2 tbl_rf_control_override_rev2[] = { + { 0x78, 0x78, 0x0038, 3 }, /* for field == 0x0002 (fls == 2) */ + { 0x7A, 0x7D, 0x0001, 0 }, /* for field == 0x0004 (fls == 3) */ + { 0x7A, 0x7D, 0x0002, 1 }, /* for field == 0x0008 (fls == 4) */ + { 0x7A, 0x7D, 0x0004, 2 }, /* for field == 0x0010 (fls == 5) */ + { 0x7A, 0x7D, 0x0030, 4 }, /* for field == 0x0020 (fls == 6) */ + { 0x7A, 0x7D, 0x00C0, 6 }, /* for field == 0x0040 (fls == 7) */ + { 0x7A, 0x7D, 0x0100, 8 }, /* for field == 0x0080 (fls == 8) */ + { 0x7A, 0x7D, 0x0200, 9 }, /* for field == 0x0100 (fls == 9) */ + { 0x78, 0x78, 0x0004, 2 }, /* for field == 0x0200 (fls == 10) */ + { 0x7B, 0x7E, 0x01FF, 0 }, /* for field == 0x0400 (fls == 11) */ + { 0x7C, 0x7F, 0x01FF, 0 }, /* for field == 0x0800 (fls == 12) */ + { 0x78, 0x78, 0x0100, 8 }, /* for field == 0x1000 (fls == 13) */ + { 0x78, 0x78, 0x0200, 9 }, /* for field == 0x2000 (fls == 14) */ + { 0x78, 0x78, 0xF000, 12 } /* for field == 0x4000 (fls == 15) */ +}; + +/* val_mask, val_shift, en_addr0, val_addr0, en_addr1, val_addr1 */ +const struct nphy_rf_control_override_rev3 tbl_rf_control_override_rev3[] = { + { 0x8000, 15, 0xE5, 0xF9, 0xE6, 0xFB }, /* field == 0x0001 (fls 1) */ + { 0x0001, 0, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0002 (fls 2) */ + { 0x0002, 1, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0004 (fls 3) */ + { 0x0004, 2, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0008 (fls 4) */ + { 0x0010, 4, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0010 (fls 5) */ + { 0x0020, 5, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0020 (fls 6) */ + { 0x0040, 6, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0040 (fls 7) */ + { 0x0080, 7, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0080 (fls 8) */ + { 0x0100, 8, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0100 (fls 9) */ + { 0x0007, 0, 0xE7, 0xF8, 0xEC, 0xFA }, /* field == 0x0200 (fls 10) */ + { 0x0070, 4, 0xE7, 0xF8, 0xEC, 0xFA }, /* field == 0x0400 (fls 11) */ + { 0xE000, 13, 0xE7, 0x7A, 0xEC, 0x7D }, /* field == 0x0800 (fls 12) */ + { 0xFFFF, 0, 0xE7, 0x7B, 0xEC, 0x7E }, /* field == 0x1000 (fls 13) */ + { 0xFFFF, 0, 0xE7, 0x7C, 0xEC, 0x7F }, /* field == 0x2000 (fls 14) */ + { 0x00C0, 6, 0xE7, 0xF9, 0xEC, 0xFB } /* field == 0x4000 (fls 15) */ +}; + +/* field, val_addr_core0, val_addr_core1, val_mask, val_shift */ +static const struct nphy_rf_control_override_rev7 + tbl_rf_control_override_rev7_over0[] = { + { 0x0004, 0x07A, 0x07D, 0x0002, 1 }, + { 0x0008, 0x07A, 0x07D, 0x0004, 2 }, + { 0x0010, 0x07A, 0x07D, 0x0010, 4 }, + { 0x0020, 0x07A, 0x07D, 0x0020, 5 }, + { 0x0040, 0x07A, 0x07D, 0x0040, 6 }, + { 0x0080, 0x07A, 0x07D, 0x0080, 7 }, + { 0x0400, 0x0F8, 0x0FA, 0x0070, 4 }, + { 0x0800, 0x07B, 0x07E, 0xFFFF, 0 }, + { 0x1000, 0x07C, 0x07F, 0xFFFF, 0 }, + { 0x6000, 0x348, 0x349, 0x00FF, 0 }, + { 0x2000, 0x348, 0x349, 0x000F, 0 }, +}; + +/* field, val_addr_core0, val_addr_core1, val_mask, val_shift */ +static const struct nphy_rf_control_override_rev7 + tbl_rf_control_override_rev7_over1[] = { + { 0x0002, 0x340, 0x341, 0x0002, 1 }, + { 0x0008, 0x340, 0x341, 0x0008, 3 }, + { 0x0020, 0x340, 0x341, 0x0020, 5 }, + { 0x0010, 0x340, 0x341, 0x0010, 4 }, + { 0x0004, 0x340, 0x341, 0x0004, 2 }, + { 0x0080, 0x340, 0x341, 0x0700, 8 }, + { 0x0800, 0x340, 0x341, 0x4000, 14 }, + { 0x0400, 0x340, 0x341, 0x2000, 13 }, + { 0x0200, 0x340, 0x341, 0x0800, 12 }, + { 0x0100, 0x340, 0x341, 0x0100, 11 }, + { 0x0040, 0x340, 0x341, 0x0040, 6 }, + { 0x0001, 0x340, 0x341, 0x0001, 0 }, +}; + +/* field, val_addr_core0, val_addr_core1, val_mask, val_shift */ +static const struct nphy_rf_control_override_rev7 + tbl_rf_control_override_rev7_over2[] = { + { 0x0008, 0x344, 0x345, 0x0008, 3 }, + { 0x0002, 0x344, 0x345, 0x0002, 1 }, + { 0x0001, 0x344, 0x345, 0x0001, 0 }, + { 0x0004, 0x344, 0x345, 0x0004, 2 }, + { 0x0010, 0x344, 0x345, 0x0010, 4 }, +}; + +static struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_wa_phy6_radio11_ghz2 = { + { 10, 14, 19, 27 }, + { -5, 6, 10, 15 }, + { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, + 0x427E, + { 0x413F, 0x413F, 0x413F, 0x413F }, + 0x007E, 0x0066, 0x1074, + 0x18, 0x18, 0x18, + 0x01D0, 0x5, +}; +static struct nphy_gain_ctl_workaround_entry nphy_gain_ctl_workaround[2][4] = { + { /* 2GHz */ + { /* PHY rev 3 */ + { 7, 11, 16, 23 }, + { -5, 6, 10, 14 }, + { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, + 0x627E, + { 0x613F, 0x613F, 0x613F, 0x613F }, + 0x107E, 0x0066, 0x0074, + 0x18, 0x18, 0x18, + 0x020D, 0x5, + }, + { /* PHY rev 4 */ + { 8, 12, 17, 25 }, + { -5, 6, 10, 14 }, + { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, + 0x527E, + { 0x513F, 0x513F, 0x513F, 0x513F }, + 0x007E, 0x0066, 0x0074, + 0x18, 0x18, 0x18, + 0x01A1, 0x5, + }, + { /* PHY rev 5 */ + { 9, 13, 18, 26 }, + { -3, 7, 11, 16 }, + { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, + 0x427E, /* invalid for external LNA! */ + { 0x413F, 0x413F, 0x413F, 0x413F }, /* invalid for external LNA! */ + 0x1076, 0x0066, 0x0000, /* low is invalid (the last one) */ + 0x18, 0x18, 0x18, + 0x01D0, 0x9, + }, + { /* PHY rev 6+ */ + { 8, 13, 18, 25 }, + { -5, 6, 10, 14 }, + { 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA, 0xA }, + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, + 0x527E, /* invalid for external LNA! */ + { 0x513F, 0x513F, 0x513F, 0x513F }, /* invalid for external LNA! */ + 0x007E, 0x0066, 0x0000, /* low is invalid (the last one) */ + 0x18, 0x18, 0x18, + 0x01D0, 0x5, + }, + }, + { /* 5GHz */ + { /* PHY rev 3 */ + { 7, 11, 17, 23 }, + { -6, 2, 6, 10 }, + { 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13 }, + { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + 0x52DE, + { 0x516F, 0x516F, 0x516F, 0x516F }, + 0x00DE, 0x00CA, 0x00CC, + 0x1E, 0x1E, 0x1E, + 0x01A1, 25, + }, + { /* PHY rev 4 */ + { 8, 12, 18, 23 }, + { -5, 2, 6, 10 }, + { 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD }, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, + 0x629E, + { 0x614F, 0x614F, 0x614F, 0x614F }, + 0x029E, 0x1084, 0x0086, + 0x24, 0x24, 0x24, + 0x0107, 25, + }, + { /* PHY rev 5 */ + { 6, 10, 16, 21 }, + { -7, 0, 4, 8 }, + { 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD }, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, + 0x729E, + { 0x714F, 0x714F, 0x714F, 0x714F }, + 0x029E, 0x2084, 0x2086, + 0x24, 0x24, 0x24, + 0x00A9, 25, + }, + { /* PHY rev 6+ */ + { 6, 10, 16, 21 }, + { -7, 0, 4, 8 }, + { 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD, 0xD }, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, + 0x729E, + { 0x714F, 0x714F, 0x714F, 0x714F }, + 0x029E, 0x2084, 0x2086, + 0x24, 0x24, 0x24, /* low is invalid for radio rev 11! */ + 0x00F0, 25, + }, + }, +}; + +static inline void assert_ntab_array_sizes(void) +{ +#undef check +#define check(table, size) \ + BUILD_BUG_ON(ARRAY_SIZE(b43_ntab_##table) != B43_NTAB_##size##_SIZE) + + check(adjustpower0, C0_ADJPLT); + check(adjustpower1, C1_ADJPLT); + check(bdi, BDI); + check(channelest, CHANEST); + check(estimatepowerlt0, C0_ESTPLT); + check(estimatepowerlt1, C1_ESTPLT); + check(framelookup, FRAMELT); + check(framestruct, FRAMESTRUCT); + check(gainctl0, C0_GAINCTL); + check(gainctl1, C1_GAINCTL); + check(intlevel, INTLEVEL); + check(iqlt0, C0_IQLT); + check(iqlt1, C1_IQLT); + check(loftlt0, C0_LOFEEDTH); + check(loftlt1, C1_LOFEEDTH); + check(mcs, MCS); + check(noisevar10, NOISEVAR10); + check(noisevar11, NOISEVAR11); + check(pilot, PILOT); + check(pilotlt, PILOTLT); + check(tdi20a0, TDI20A0); + check(tdi20a1, TDI20A1); + check(tdi40a0, TDI40A0); + check(tdi40a1, TDI40A1); + check(tdtrn, TDTRN); + check(tmap, TMAP); + +#undef check +} + +u32 b43_ntab_read(struct b43_wldev *dev, u32 offset) +{ + u32 type, value; + + type = offset & B43_NTAB_TYPEMASK; + offset &= ~B43_NTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + switch (type) { + case B43_NTAB_8BIT: + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_NPHY_TABLE_DATALO) & 0xFF; + break; + case B43_NTAB_16BIT: + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + break; + case B43_NTAB_32BIT: + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + value |= b43_phy_read(dev, B43_NPHY_TABLE_DATAHI) << 16; + break; + default: + B43_WARN_ON(1); + value = 0; + } + + return value; +} + +void b43_ntab_read_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, void *_data) +{ + u32 type; + u8 *data = _data; + unsigned int i; + + type = offset & B43_NTAB_TYPEMASK; + offset &= ~B43_NTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + + for (i = 0; i < nr_elements; i++) { + /* Auto increment broken + caching issue on BCM43224? */ + if (dev->dev->chip_id == 43224 && dev->dev->chip_rev == 1) { + b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset + i); + } + + switch (type) { + case B43_NTAB_8BIT: + *data = b43_phy_read(dev, B43_NPHY_TABLE_DATALO) & 0xFF; + data++; + break; + case B43_NTAB_16BIT: + *((u16 *)data) = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + data += 2; + break; + case B43_NTAB_32BIT: + *((u32 *)data) = + b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + *((u32 *)data) |= + b43_phy_read(dev, B43_NPHY_TABLE_DATAHI) << 16; + data += 4; + break; + default: + B43_WARN_ON(1); + } + } +} + +void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value) +{ + u32 type; + + type = offset & B43_NTAB_TYPEMASK; + offset &= 0xFFFF; + + switch (type) { + case B43_NTAB_8BIT: + B43_WARN_ON(value & ~0xFF); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); + break; + case B43_NTAB_16BIT: + B43_WARN_ON(value & ~0xFFFF); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); + break; + case B43_NTAB_32BIT: + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, value >> 16); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value & 0xFFFF); + break; + default: + B43_WARN_ON(1); + } + + return; + + /* Some compiletime assertions... */ + assert_ntab_array_sizes(); +} + +void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, const void *_data) +{ + u32 type, value; + const u8 *data = _data; + unsigned int i; + + type = offset & B43_NTAB_TYPEMASK; + offset &= ~B43_NTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset); + + for (i = 0; i < nr_elements; i++) { + /* Auto increment broken + caching issue on BCM43224? */ + if ((offset >> 10) == 9 && dev->dev->chip_id == 43224 && + dev->dev->chip_rev == 1) { + b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, offset + i); + } + + switch (type) { + case B43_NTAB_8BIT: + value = *data; + data++; + B43_WARN_ON(value & ~0xFF); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); + break; + case B43_NTAB_16BIT: + value = *((u16 *)data); + data += 2; + B43_WARN_ON(value & ~0xFFFF); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, value); + break; + case B43_NTAB_32BIT: + value = *((u32 *)data); + data += 4; + b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, value >> 16); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + value & 0xFFFF); + break; + default: + B43_WARN_ON(1); + } + } +} + +#define ntab_upload(dev, offset, data) do { \ + b43_ntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ + } while (0) + +static void b43_nphy_tables_init_shared_lut(struct b43_wldev *dev) +{ + ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3); + ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3); + ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); + ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); + ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); + ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); + ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); + ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); + ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); + ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); +} + +static void b43_nphy_tables_init_rev7_volatile(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + u8 antswlut; + int core, offset, i; + + const int antswlut0_offsets[] = { 0, 4, 8, }; /* Offsets for values */ + const u8 antswlut0_values[][3] = { + { 0x2, 0x12, 0x8 }, /* Core 0 */ + { 0x2, 0x18, 0x2 }, /* Core 1 */ + }; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + antswlut = sprom->fem.ghz5.antswlut; + else + antswlut = sprom->fem.ghz2.antswlut; + + switch (antswlut) { + case 0: + for (core = 0; core < 2; core++) { + for (i = 0; i < ARRAY_SIZE(antswlut0_values[0]); i++) { + offset = core ? 0x20 : 0x00; + offset += antswlut0_offsets[i]; + b43_ntab_write(dev, B43_NTAB8(9, offset), + antswlut0_values[core][i]); + } + } + break; + default: + b43err(dev->wl, "Unsupported antswlut: %d\n", antswlut); + break; + } +} + +static void b43_nphy_tables_init_rev16(struct b43_wldev *dev) +{ + /* Static tables */ + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7); + b43_nphy_tables_init_shared_lut(dev); + } + + /* Volatile tables */ + b43_nphy_tables_init_rev7_volatile(dev); +} + +static void b43_nphy_tables_init_rev7(struct b43_wldev *dev) +{ + /* Static tables */ + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); + ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); + ntab_upload(dev, B43_NTAB_TMAP_R7, b43_ntab_tmap_r7); + ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); + ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); + ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7); + ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); + ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); + ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); + ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); + ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); + ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); + ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); + ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); + b43_nphy_tables_init_shared_lut(dev); + } + + /* Volatile tables */ + b43_nphy_tables_init_rev7_volatile(dev); +} + +static void b43_nphy_tables_init_rev3(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + u8 antswlut; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + antswlut = sprom->fem.ghz5.antswlut; + else + antswlut = sprom->fem.ghz2.antswlut; + + /* Static tables */ + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); + ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); + ntab_upload(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3); + ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); + ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); + ntab_upload(dev, B43_NTAB_NOISEVAR_R3, b43_ntab_noisevar_r3); + ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); + ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); + ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); + ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); + ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); + ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); + ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); + ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); + b43_nphy_tables_init_shared_lut(dev); + } + + /* Volatile tables */ + if (antswlut < ARRAY_SIZE(b43_ntab_antswctl_r3)) + ntab_upload(dev, B43_NTAB_ANT_SW_CTL_R3, + b43_ntab_antswctl_r3[antswlut]); + else + B43_WARN_ON(1); +} + +static void b43_nphy_tables_init_rev0(struct b43_wldev *dev) +{ + /* Static tables */ + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct); + ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup); + ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap); + ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn); + ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel); + ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot); + ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0); + ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1); + ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0); + ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1); + ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest); + ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs); + ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10); + ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11); + } + + /* Volatile tables */ + ntab_upload(dev, B43_NTAB_BDI, b43_ntab_bdi); + ntab_upload(dev, B43_NTAB_PILOTLT, b43_ntab_pilotlt); + ntab_upload(dev, B43_NTAB_C0_GAINCTL, b43_ntab_gainctl0); + ntab_upload(dev, B43_NTAB_C1_GAINCTL, b43_ntab_gainctl1); + ntab_upload(dev, B43_NTAB_C0_ESTPLT, b43_ntab_estimatepowerlt0); + ntab_upload(dev, B43_NTAB_C1_ESTPLT, b43_ntab_estimatepowerlt1); + ntab_upload(dev, B43_NTAB_C0_ADJPLT, b43_ntab_adjustpower0); + ntab_upload(dev, B43_NTAB_C1_ADJPLT, b43_ntab_adjustpower1); + ntab_upload(dev, B43_NTAB_C0_IQLT, b43_ntab_iqlt0); + ntab_upload(dev, B43_NTAB_C1_IQLT, b43_ntab_iqlt1); + ntab_upload(dev, B43_NTAB_C0_LOFEEDTH, b43_ntab_loftlt0); + ntab_upload(dev, B43_NTAB_C1_LOFEEDTH, b43_ntab_loftlt1); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/InitTables */ +void b43_nphy_tables_init(struct b43_wldev *dev) +{ + if (dev->phy.rev >= 16) + b43_nphy_tables_init_rev16(dev); + else if (dev->phy.rev >= 7) + b43_nphy_tables_init_rev7(dev); + else if (dev->phy.rev >= 3) + b43_nphy_tables_init_rev3(dev); + else + b43_nphy_tables_init_rev0(dev); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */ +static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + switch (phy->rev) { + case 17: + if (phy->radio_rev == 14) + return b43_ntab_tx_gain_ipa_2057_rev14_2g; + break; + case 16: + if (phy->radio_rev == 9) + return b43_ntab_tx_gain_ipa_2057_rev9_2g; + break; + case 8: + if (phy->radio_rev == 5) + return b43_ntab_tx_gain_ipa_2057_rev5_2g; + break; + case 6: + if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) + return b43_ntab_tx_gain_ipa_rev5_2g; + return b43_ntab_tx_gain_ipa_rev6_2g; + case 5: + return b43_ntab_tx_gain_ipa_rev5_2g; + case 4: + case 3: + return b43_ntab_tx_gain_ipa_rev3_2g; + } + + b43err(dev->wl, + "No 2GHz IPA gain table available for this device\n"); + return NULL; + } else { + switch (phy->rev) { + case 16: + if (phy->radio_rev == 9) + return b43_ntab_tx_gain_ipa_2057_rev9_5g; + break; + case 3 ... 6: + return b43_ntab_tx_gain_ipa_rev3_5g; + } + + b43err(dev->wl, + "No 5GHz IPA gain table available for this device\n"); + return NULL; + } +} + +const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + enum ieee80211_band band = b43_current_band(dev->wl); + struct ssb_sprom *sprom = dev->dev->bus_sprom; + + if (dev->phy.rev < 3) + return b43_ntab_tx_gain_rev0_1_2; + + /* rev 3+ */ + if ((dev->phy.n->ipa2g_on && band == IEEE80211_BAND_2GHZ) || + (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)) { + return b43_nphy_get_ipa_gain_table(dev); + } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + switch (phy->rev) { + case 6: + case 5: + return b43_ntab_tx_gain_epa_rev5_5g; + case 4: + return sprom->fem.ghz5.extpa_gain == 3 ? + b43_ntab_tx_gain_epa_rev4_5g : + b43_ntab_tx_gain_epa_rev4_hi_pwr_5g; + case 3: + return b43_ntab_tx_gain_epa_rev3_5g; + default: + b43err(dev->wl, + "No 5GHz EPA gain table available for this device\n"); + return NULL; + } + } else { + switch (phy->rev) { + case 6: + case 5: + if (sprom->fem.ghz2.extpa_gain == 3) + return b43_ntab_tx_gain_epa_rev3_hi_pwr_2g; + /* fall through */ + case 4: + case 3: + return b43_ntab_tx_gain_epa_rev3_2g; + default: + b43err(dev->wl, + "No 2GHz EPA gain table available for this device\n"); + return NULL; + } + } +} + +const s16 *b43_ntab_get_rf_pwr_offset_table(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + switch (phy->rev) { + case 17: + if (phy->radio_rev == 14) + return b43_ntab_rf_pwr_offset_2057_rev14_2g; + break; + case 16: + if (phy->radio_rev == 9) + return b43_ntab_rf_pwr_offset_2057_rev9_2g; + break; + } + + b43err(dev->wl, + "No 2GHz RF power table available for this device\n"); + return NULL; + } else { + switch (phy->rev) { + case 16: + if (phy->radio_rev == 9) + return b43_ntab_rf_pwr_offset_2057_rev9_5g; + break; + } + + b43err(dev->wl, + "No 5GHz RF power table available for this device\n"); + return NULL; + } +} + +struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( + struct b43_wldev *dev, bool ghz5, bool ext_lna) +{ + struct b43_phy *phy = &dev->phy; + struct nphy_gain_ctl_workaround_entry *e; + u8 phy_idx; + + if (!ghz5 && dev->phy.rev >= 6 && dev->phy.radio_rev == 11) + return &nphy_gain_ctl_wa_phy6_radio11_ghz2; + + B43_WARN_ON(dev->phy.rev < 3); + if (dev->phy.rev >= 6) + phy_idx = 3; + else if (dev->phy.rev == 5) + phy_idx = 2; + else if (dev->phy.rev == 4) + phy_idx = 1; + else + phy_idx = 0; + e = &nphy_gain_ctl_workaround[ghz5][phy_idx]; + + /* Some workarounds to the workarounds... */ + if (!ghz5) { + u8 tr_iso = dev->dev->bus_sprom->fem.ghz2.tr_iso; + + if (tr_iso > 7) + tr_iso = 3; + + if (phy->rev >= 6) { + static const int gain_data[] = { 0x106a, 0x106c, 0x1074, + 0x107c, 0x007e, 0x107e, + 0x207e, 0x307e, }; + + e->cliplo_gain = gain_data[tr_iso]; + } else if (phy->rev == 5) { + static const int gain_data[] = { 0x0062, 0x0064, 0x006a, + 0x106a, 0x106c, 0x1074, + 0x107c, 0x207c, }; + + e->cliplo_gain = gain_data[tr_iso]; + } + + if (phy->rev >= 5 && ext_lna) { + e->rfseq_init[0] &= ~0x4000; + e->rfseq_init[1] &= ~0x4000; + e->rfseq_init[2] &= ~0x4000; + e->rfseq_init[3] &= ~0x4000; + e->init_gain &= ~0x4000; + } + } else { + if (phy->rev >= 6) { + if (phy->radio_rev == 11 && !b43_is_40mhz(dev)) + e->crsminu = 0x2d; + } else if (phy->rev == 4 && ext_lna) { + e->rfseq_init[0] &= ~0x4000; + e->rfseq_init[1] &= ~0x4000; + e->rfseq_init[2] &= ~0x4000; + e->rfseq_init[3] &= ~0x4000; + e->init_gain &= ~0x4000; + e->rfseq_init[0] |= 0x1000; + e->rfseq_init[1] |= 0x1000; + e->rfseq_init[2] |= 0x1000; + e->rfseq_init[3] |= 0x1000; + e->init_gain |= 0x1000; + } + } + + return e; +} + +const struct nphy_rf_control_override_rev7 *b43_nphy_get_rf_ctl_over_rev7( + struct b43_wldev *dev, u16 field, u8 override) +{ + const struct nphy_rf_control_override_rev7 *e; + u8 size, i; + + switch (override) { + case 0: + e = tbl_rf_control_override_rev7_over0; + size = ARRAY_SIZE(tbl_rf_control_override_rev7_over0); + break; + case 1: + e = tbl_rf_control_override_rev7_over1; + size = ARRAY_SIZE(tbl_rf_control_override_rev7_over1); + break; + case 2: + e = tbl_rf_control_override_rev7_over2; + size = ARRAY_SIZE(tbl_rf_control_override_rev7_over2); + break; + default: + b43err(dev->wl, "Invalid override value %d\n", override); + return NULL; + } + + for (i = 0; i < size; i++) { + if (e[i].field == field) + return &e[i]; + } + + return NULL; +} diff --git a/drivers/net/wireless/broadcom/b43/tables_nphy.h b/drivers/net/wireless/broadcom/b43/tables_nphy.h new file mode 100644 index 000000000000..b51f386db02f --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/tables_nphy.h @@ -0,0 +1,222 @@ +#ifndef B43_TABLES_NPHY_H_ +#define B43_TABLES_NPHY_H_ + +#include + +struct b43_phy_n_sfo_cfg { + u16 phy_bw1a; + u16 phy_bw2; + u16 phy_bw3; + u16 phy_bw4; + u16 phy_bw5; + u16 phy_bw6; +}; + +struct b43_wldev; + +struct nphy_txiqcal_ladder { + u8 percent; + u8 g_env; +}; + +struct nphy_rf_control_override_rev2 { + u8 addr0; + u8 addr1; + u16 bmask; + u8 shift; +}; + +struct nphy_rf_control_override_rev3 { + u16 val_mask; + u8 val_shift; + u8 en_addr0; + u8 val_addr0; + u8 en_addr1; + u8 val_addr1; +}; + +struct nphy_rf_control_override_rev7 { + u16 field; + u16 val_addr_core0; + u16 val_addr_core1; + u16 val_mask; + u8 val_shift; +}; + +struct nphy_gain_ctl_workaround_entry { + s8 lna1_gain[4]; + s8 lna2_gain[4]; + u8 gain_db[10]; + u8 gain_bits[10]; + + u16 init_gain; + u16 rfseq_init[4]; + + u16 cliphi_gain; + u16 clipmd_gain; + u16 cliplo_gain; + + u16 crsmin; + u16 crsminl; + u16 crsminu; + + u16 nbclip; + u16 wlclip; +}; + +/* Get entry with workaround values for gain ctl. Does not return NULL. */ +struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( + struct b43_wldev *dev, bool ghz5, bool ext_lna); + + +/* The N-PHY tables. */ +#define B43_NTAB_TYPEMASK 0xF0000000 +#define B43_NTAB_8BIT 0x10000000 +#define B43_NTAB_16BIT 0x20000000 +#define B43_NTAB_32BIT 0x30000000 +#define B43_NTAB8(table, offset) (((table) << 10) | (offset) | B43_NTAB_8BIT) +#define B43_NTAB16(table, offset) (((table) << 10) | (offset) | B43_NTAB_16BIT) +#define B43_NTAB32(table, offset) (((table) << 10) | (offset) | B43_NTAB_32BIT) + +/* Static N-PHY tables */ +#define B43_NTAB_FRAMESTRUCT B43_NTAB32(0x0A, 0x000) /* Frame Struct Table */ +#define B43_NTAB_FRAMESTRUCT_SIZE 832 +#define B43_NTAB_FRAMELT B43_NTAB8 (0x18, 0x000) /* Frame Lookup Table */ +#define B43_NTAB_FRAMELT_SIZE 32 +#define B43_NTAB_TMAP B43_NTAB32(0x0C, 0x000) /* T Map Table */ +#define B43_NTAB_TMAP_SIZE 448 +#define B43_NTAB_TDTRN B43_NTAB32(0x0E, 0x000) /* TDTRN Table */ +#define B43_NTAB_TDTRN_SIZE 704 +#define B43_NTAB_INTLEVEL B43_NTAB32(0x0D, 0x000) /* Int Level Table */ +#define B43_NTAB_INTLEVEL_SIZE 7 +#define B43_NTAB_PILOT B43_NTAB16(0x0B, 0x000) /* Pilot Table */ +#define B43_NTAB_PILOT_SIZE 88 +#define B43_NTAB_PILOTLT B43_NTAB32(0x14, 0x000) /* Pilot Lookup Table */ +#define B43_NTAB_PILOTLT_SIZE 6 +#define B43_NTAB_TDI20A0 B43_NTAB32(0x13, 0x080) /* TDI Table 20 Antenna 0 */ +#define B43_NTAB_TDI20A0_SIZE 55 +#define B43_NTAB_TDI20A1 B43_NTAB32(0x13, 0x100) /* TDI Table 20 Antenna 1 */ +#define B43_NTAB_TDI20A1_SIZE 55 +#define B43_NTAB_TDI40A0 B43_NTAB32(0x13, 0x280) /* TDI Table 40 Antenna 0 */ +#define B43_NTAB_TDI40A0_SIZE 110 +#define B43_NTAB_TDI40A1 B43_NTAB32(0x13, 0x300) /* TDI Table 40 Antenna 1 */ +#define B43_NTAB_TDI40A1_SIZE 110 +#define B43_NTAB_BDI B43_NTAB16(0x15, 0x000) /* BDI Table */ +#define B43_NTAB_BDI_SIZE 6 +#define B43_NTAB_CHANEST B43_NTAB32(0x16, 0x000) /* Channel Estimate Table */ +#define B43_NTAB_CHANEST_SIZE 96 +#define B43_NTAB_MCS B43_NTAB8 (0x12, 0x000) /* MCS Table */ +#define B43_NTAB_MCS_SIZE 128 + +/* Volatile N-PHY tables */ +#define B43_NTAB_NOISEVAR10 B43_NTAB32(0x10, 0x000) /* Noise Var Table 10 */ +#define B43_NTAB_NOISEVAR10_SIZE 256 +#define B43_NTAB_NOISEVAR11 B43_NTAB32(0x10, 0x080) /* Noise Var Table 11 */ +#define B43_NTAB_NOISEVAR11_SIZE 256 +#define B43_NTAB_C0_ESTPLT B43_NTAB8 (0x1A, 0x000) /* Estimate Power Lookup Table Core 0 */ +#define B43_NTAB_C0_ESTPLT_SIZE 64 +#define B43_NTAB_C0_ADJPLT B43_NTAB8 (0x1A, 0x040) /* Adjust Power Lookup Table Core 0 */ +#define B43_NTAB_C0_ADJPLT_SIZE 128 +#define B43_NTAB_C0_GAINCTL B43_NTAB32(0x1A, 0x0C0) /* Gain Control Lookup Table Core 0 */ +#define B43_NTAB_C0_GAINCTL_SIZE 128 +#define B43_NTAB_C0_IQLT B43_NTAB32(0x1A, 0x140) /* IQ Lookup Table Core 0 */ +#define B43_NTAB_C0_IQLT_SIZE 128 +#define B43_NTAB_C0_LOFEEDTH B43_NTAB16(0x1A, 0x1C0) /* Local Oscillator Feed Through Lookup Table Core 0 */ +#define B43_NTAB_C0_LOFEEDTH_SIZE 128 +#define B43_NTAB_C1_ESTPLT B43_NTAB8 (0x1B, 0x000) /* Estimate Power Lookup Table Core 1 */ +#define B43_NTAB_C1_ESTPLT_SIZE 64 +#define B43_NTAB_C1_ADJPLT B43_NTAB8 (0x1B, 0x040) /* Adjust Power Lookup Table Core 1 */ +#define B43_NTAB_C1_ADJPLT_SIZE 128 +#define B43_NTAB_C1_GAINCTL B43_NTAB32(0x1B, 0x0C0) /* Gain Control Lookup Table Core 1 */ +#define B43_NTAB_C1_GAINCTL_SIZE 128 +#define B43_NTAB_C1_IQLT B43_NTAB32(0x1B, 0x140) /* IQ Lookup Table Core 1 */ +#define B43_NTAB_C1_IQLT_SIZE 128 +#define B43_NTAB_C1_LOFEEDTH B43_NTAB16(0x1B, 0x1C0) /* Local Oscillator Feed Through Lookup Table Core 1 */ +#define B43_NTAB_C1_LOFEEDTH_SIZE 128 + +/* Volatile N-PHY tables, PHY revision >= 3 */ +#define B43_NTAB_ANT_SW_CTL_R3 B43_NTAB16( 9, 0) /* antenna software control */ + +/* Static N-PHY tables, PHY revision >= 3 */ +#define B43_NTAB_FRAMESTRUCT_R3 B43_NTAB32(10, 0) /* frame struct */ +#define B43_NTAB_PILOT_R3 B43_NTAB16(11, 0) /* pilot */ +#define B43_NTAB_TMAP_R3 B43_NTAB32(12, 0) /* TM AP */ +#define B43_NTAB_INTLEVEL_R3 B43_NTAB32(13, 0) /* INT LV */ +#define B43_NTAB_TDTRN_R3 B43_NTAB32(14, 0) /* TD TRN */ +#define B43_NTAB_NOISEVAR_R3 B43_NTAB32(16, 0) /* noise variance */ +#define B43_NTAB_MCS_R3 B43_NTAB16(18, 0) /* MCS */ +#define B43_NTAB_TDI20A0_R3 B43_NTAB32(19, 128) /* TDI 20/0 */ +#define B43_NTAB_TDI20A1_R3 B43_NTAB32(19, 256) /* TDI 20/1 */ +#define B43_NTAB_TDI40A0_R3 B43_NTAB32(19, 640) /* TDI 40/0 */ +#define B43_NTAB_TDI40A1_R3 B43_NTAB32(19, 768) /* TDI 40/1 */ +#define B43_NTAB_PILOTLT_R3 B43_NTAB32(20, 0) /* PLT lookup */ +#define B43_NTAB_CHANEST_R3 B43_NTAB32(22, 0) /* channel estimate */ +#define B43_NTAB_FRAMELT_R3 B43_NTAB8(24, 0) /* frame lookup */ +#define B43_NTAB_C0_ESTPLT_R3 B43_NTAB8(26, 0) /* estimated power lookup 0 */ +#define B43_NTAB_C0_ADJPLT_R3 B43_NTAB8(26, 64) /* adjusted power lookup 0 */ +#define B43_NTAB_C0_GAINCTL_R3 B43_NTAB32(26, 192) /* gain control lookup 0 */ +#define B43_NTAB_C0_IQLT_R3 B43_NTAB32(26, 320) /* I/Q lookup 0 */ +#define B43_NTAB_C0_LOFEEDTH_R3 B43_NTAB16(26, 448) /* Local Oscillator Feed Through lookup 0 */ +#define B43_NTAB_C0_PAPD_COMP_R3 B43_NTAB16(26, 576) +#define B43_NTAB_C1_ESTPLT_R3 B43_NTAB8(27, 0) /* estimated power lookup 1 */ +#define B43_NTAB_C1_ADJPLT_R3 B43_NTAB8(27, 64) /* adjusted power lookup 1 */ +#define B43_NTAB_C1_GAINCTL_R3 B43_NTAB32(27, 192) /* gain control lookup 1 */ +#define B43_NTAB_C1_IQLT_R3 B43_NTAB32(27, 320) /* I/Q lookup 1 */ +#define B43_NTAB_C1_LOFEEDTH_R3 B43_NTAB16(27, 448) /* Local Oscillator Feed Through lookup 1 */ +#define B43_NTAB_C1_PAPD_COMP_R3 B43_NTAB16(27, 576) + +/* Static N-PHY tables, PHY revision >= 7 */ +#define B43_NTAB_TMAP_R7 B43_NTAB32(12, 0) /* TM AP */ +#define B43_NTAB_NOISEVAR_R7 B43_NTAB32(16, 0) /* noise variance */ + +#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_20_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3 11 +#define B43_NTAB_TX_IQLO_CAL_STARTCOEFS 9 +#define B43_NTAB_TX_IQLO_CAL_CMDS_RECAL_REV3 12 +#define B43_NTAB_TX_IQLO_CAL_CMDS_RECAL 10 +#define B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL 10 +#define B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL_REV3 12 + +u32 b43_ntab_read(struct b43_wldev *dev, u32 offset); +void b43_ntab_read_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, void *_data); +void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value); +void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, const void *_data); + +void b43_nphy_tables_init(struct b43_wldev *dev); + +const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev); + +const s16 *b43_ntab_get_rf_pwr_offset_table(struct b43_wldev *dev); + +extern const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[]; + +extern const u16 tbl_iqcal_gainparams[2][9][8]; +extern const struct nphy_txiqcal_ladder ladder_lo[]; +extern const struct nphy_txiqcal_ladder ladder_iq[]; +extern const u16 loscale[]; + +extern const u16 tbl_tx_iqlo_cal_loft_ladder_40[]; +extern const u16 tbl_tx_iqlo_cal_loft_ladder_20[]; +extern const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[]; +extern const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[]; +extern const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[]; +extern const u16 tbl_tx_iqlo_cal_startcoefs[]; +extern const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[]; +extern const u16 tbl_tx_iqlo_cal_cmds_recal[]; +extern const u16 tbl_tx_iqlo_cal_cmds_fullcal[]; +extern const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[]; +extern const s16 tbl_tx_filter_coef_rev4[7][15]; + +extern const struct nphy_rf_control_override_rev2 + tbl_rf_control_override_rev2[]; +extern const struct nphy_rf_control_override_rev3 + tbl_rf_control_override_rev3[]; +const struct nphy_rf_control_override_rev7 *b43_nphy_get_rf_ctl_over_rev7( + struct b43_wldev *dev, u16 field, u8 override); + +#endif /* B43_TABLES_NPHY_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/tables_phy_ht.c b/drivers/net/wireless/broadcom/b43/tables_phy_ht.c new file mode 100644 index 000000000000..176c49d74ef4 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/tables_phy_ht.c @@ -0,0 +1,836 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11n HT-PHY data tables + + Copyright (c) 2011 Rafał Miłecki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "tables_phy_ht.h" +#include "phy_common.h" +#include "phy_ht.h" + +static const u16 b43_httab_0x12[] = { + 0x0000, 0x0008, 0x000a, 0x0010, 0x0012, 0x0019, + 0x001a, 0x001c, 0x0080, 0x0088, 0x008a, 0x0090, + 0x0092, 0x0099, 0x009a, 0x009c, 0x0100, 0x0108, + 0x010a, 0x0110, 0x0112, 0x0119, 0x011a, 0x011c, + 0x0180, 0x0188, 0x018a, 0x0190, 0x0192, 0x0199, + 0x019a, 0x019c, 0x0000, 0x0098, 0x00a0, 0x00a8, + 0x009a, 0x00a2, 0x00aa, 0x0120, 0x0128, 0x0128, + 0x0130, 0x0138, 0x0138, 0x0140, 0x0122, 0x012a, + 0x012a, 0x0132, 0x013a, 0x013a, 0x0142, 0x01a8, + 0x01b0, 0x01b8, 0x01b0, 0x01b8, 0x01c0, 0x01c8, + 0x01c0, 0x01c8, 0x01d0, 0x01d0, 0x01d8, 0x01aa, + 0x01b2, 0x01ba, 0x01b2, 0x01ba, 0x01c2, 0x01ca, + 0x01c2, 0x01ca, 0x01d2, 0x01d2, 0x01da, 0x0001, + 0x0002, 0x0004, 0x0009, 0x000c, 0x0011, 0x0014, + 0x0018, 0x0020, 0x0021, 0x0022, 0x0024, 0x0081, + 0x0082, 0x0084, 0x0089, 0x008c, 0x0091, 0x0094, + 0x0098, 0x00a0, 0x00a1, 0x00a2, 0x00a4, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, +}; + +static const u16 b43_httab_0x27[] = { + 0x0009, 0x000e, 0x0011, 0x0014, 0x0017, 0x001a, + 0x001d, 0x0020, 0x0009, 0x000e, 0x0011, 0x0014, + 0x0017, 0x001a, 0x001d, 0x0020, 0x0009, 0x000e, + 0x0011, 0x0014, 0x0017, 0x001a, 0x001d, 0x0020, + 0x0009, 0x000e, 0x0011, 0x0014, 0x0017, 0x001a, + 0x001d, 0x0020, +}; + +static const u16 b43_httab_0x26[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, +}; + +static const u32 b43_httab_0x25[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_httab_0x2f[] = { + 0x00035700, 0x0002cc9a, 0x00026666, 0x0001581f, + 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, + 0x0001581f, 0x0001581f, 0x0001581f, 0x00035700, + 0x0002cc9a, 0x00026666, 0x0001581f, 0x0001581f, + 0x0001581f, 0x0001581f, 0x0001581f, 0x0001581f, + 0x0001581f, 0x0001581f, +}; + +static const u16 b43_httab_0x1a[] = { + 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052, + 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e, + 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a, + 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046, + 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040, + 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a, + 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033, + 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b, + 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, + 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e, + 0x000b, 0x0007, 0x0002, 0x00fd, +}; + +static const u16 b43_httab_0x1b[] = { + 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052, + 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e, + 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a, + 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046, + 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040, + 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a, + 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033, + 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b, + 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, + 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e, + 0x000b, 0x0007, 0x0002, 0x00fd, +}; + +static const u16 b43_httab_0x1c[] = { + 0x0055, 0x0054, 0x0054, 0x0053, 0x0052, 0x0052, + 0x0051, 0x0051, 0x0050, 0x004f, 0x004f, 0x004e, + 0x004e, 0x004d, 0x004c, 0x004c, 0x004b, 0x004a, + 0x0049, 0x0049, 0x0048, 0x0047, 0x0046, 0x0046, + 0x0045, 0x0044, 0x0043, 0x0042, 0x0041, 0x0040, + 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003a, + 0x0039, 0x0038, 0x0037, 0x0036, 0x0035, 0x0033, + 0x0032, 0x0031, 0x002f, 0x002e, 0x002c, 0x002b, + 0x0029, 0x0027, 0x0025, 0x0023, 0x0021, 0x001f, + 0x001d, 0x001a, 0x0018, 0x0015, 0x0012, 0x000e, + 0x000b, 0x0007, 0x0002, 0x00fd, +}; + +static const u32 b43_httab_0x1a_0xc0[] = { + 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, + 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, + 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, + 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, + 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, + 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, + 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, + 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, + 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, + 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, + 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, + 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, + 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, + 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, + 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, + 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, + 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, + 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, + 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, + 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, + 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, + 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, + 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, + 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, + 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, + 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, + 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, + 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, + 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, + 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, + 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, + 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, +}; + +static const u32 b43_httab_0x1a_0x140[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_httab_0x1b_0x140[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_httab_0x1c_0x140[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u16 b43_httab_0x1a_0x1c0[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, +}; + +static const u16 b43_httab_0x1b_0x1c0[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, +}; + +static const u16 b43_httab_0x1c_0x1c0[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, +}; + +static const u16 b43_httab_0x1a_0x240[] = { + 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, + 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, + 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, + 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, + 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e, + 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, + 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, + 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc, + 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, + 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, +}; + +static const u16 b43_httab_0x1b_0x240[] = { + 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, + 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, + 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, + 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, + 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e, + 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, + 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, + 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc, + 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, + 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, +}; + +static const u16 b43_httab_0x1c_0x240[] = { + 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, + 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, 0x0036, + 0x0036, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, 0x002a, + 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, + 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, 0x001e, + 0x001e, 0x001e, 0x001e, 0x001e, 0x000e, 0x000e, + 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, + 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, 0x000e, + 0x000e, 0x000e, 0x000e, 0x000e, 0x01fc, 0x01fc, + 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, + 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, 0x01fc, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, 0x01d6, + 0x01d6, 0x01d6, +}; + +static const u32 b43_httab_0x1f[] = { + 0x00000000, 0x00000000, 0x00016023, 0x00006028, + 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037, + 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d, + 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9, + 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9, + 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6, + 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c, + 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b, + 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15, + 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9, + 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87, + 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3, + 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa, + 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff, + 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff, + 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff, +}; + +static const u32 b43_httab_0x21[] = { + 0x00000000, 0x00000000, 0x00016023, 0x00006028, + 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037, + 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d, + 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9, + 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9, + 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6, + 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c, + 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b, + 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15, + 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9, + 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87, + 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3, + 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa, + 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff, + 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff, + 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff, +}; + +static const u32 b43_httab_0x23[] = { + 0x00000000, 0x00000000, 0x00016023, 0x00006028, + 0x00034036, 0x0003402e, 0x0007203c, 0x0006e037, + 0x00070030, 0x0009401f, 0x0009a00f, 0x000b600d, + 0x000c8007, 0x000ce007, 0x00101fff, 0x00121ff9, + 0x0012e004, 0x0014dffc, 0x0016dff6, 0x0018dfe9, + 0x001b3fe5, 0x001c5fd0, 0x001ddfc2, 0x001f1fb6, + 0x00207fa4, 0x00219f8f, 0x0022ff7d, 0x00247f6c, + 0x0024df5b, 0x00267f4b, 0x0027df3b, 0x0029bf3b, + 0x002b5f2f, 0x002d3f2e, 0x002f5f2a, 0x002fff15, + 0x00315f0b, 0x0032defa, 0x0033beeb, 0x0034fed9, + 0x00353ec5, 0x00361eb0, 0x00363e9b, 0x0036be87, + 0x0036be70, 0x0038fe67, 0x0044beb2, 0x00513ef3, + 0x00595f11, 0x00669f3d, 0x0078dfdf, 0x00a143aa, + 0x01642fff, 0x0162afff, 0x01620fff, 0x0160cfff, + 0x015f0fff, 0x015dafff, 0x015bcfff, 0x015bcfff, + 0x015b4fff, 0x015acfff, 0x01590fff, 0x0156cfff, +}; + +static const u32 b43_httab_0x20[] = { + 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035, + 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043, + 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, + 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, + 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, + 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, + 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, + 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, + 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e, + 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, + 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212, + 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b, + 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348, + 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422, + 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533, + 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c, +}; + +static const u32 b43_httab_0x22[] = { + 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035, + 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043, + 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, + 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, + 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, + 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, + 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, + 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, + 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e, + 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, + 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212, + 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b, + 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348, + 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422, + 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533, + 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c, +}; + +static const u32 b43_httab_0x24[] = { + 0x0b5e002d, 0x0ae2002f, 0x0a3b0032, 0x09a70035, + 0x09220038, 0x08ab003b, 0x081f003f, 0x07a20043, + 0x07340047, 0x06d2004b, 0x067a004f, 0x06170054, + 0x05bf0059, 0x0571005e, 0x051e0064, 0x04d3006a, + 0x04910070, 0x044c0077, 0x040f007e, 0x03d90085, + 0x03a1008d, 0x036f0095, 0x033d009e, 0x030b00a8, + 0x02e000b2, 0x02b900bc, 0x029200c7, 0x026d00d3, + 0x024900e0, 0x022900ed, 0x020a00fb, 0x01ec010a, + 0x01d20119, 0x01b7012a, 0x019e013c, 0x0188014e, + 0x01720162, 0x015d0177, 0x0149018e, 0x013701a5, + 0x012601be, 0x011501d8, 0x010601f4, 0x00f70212, + 0x00e90231, 0x00dc0253, 0x00d00276, 0x00c4029b, + 0x00b902c3, 0x00af02ed, 0x00a50319, 0x009c0348, + 0x0093037a, 0x008b03af, 0x008303e6, 0x007c0422, + 0x00750460, 0x006e04a3, 0x006804e9, 0x00620533, + 0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c, +}; + +/* Some late-init table */ +const u32 b43_httab_0x1a_0xc0_late[] = { + 0x10f90040, 0x10e10040, 0x10e1003c, 0x10c9003d, + 0x10b9003c, 0x10a9003d, 0x10a1003c, 0x1099003b, + 0x1091003b, 0x1089003a, 0x1081003a, 0x10790039, + 0x10710039, 0x1069003a, 0x1061003b, 0x1059003d, + 0x1051003f, 0x10490042, 0x1049003e, 0x1049003b, + 0x1041003e, 0x1041003b, 0x1039003e, 0x1039003b, + 0x10390038, 0x10390035, 0x1031003a, 0x10310036, + 0x10310033, 0x1029003a, 0x10290037, 0x10290034, + 0x10290031, 0x10210039, 0x10210036, 0x10210033, + 0x10210030, 0x1019003c, 0x10190039, 0x10190036, + 0x10190033, 0x10190030, 0x1019002d, 0x1019002b, + 0x10190028, 0x1011003a, 0x10110036, 0x10110033, + 0x10110030, 0x1011002e, 0x1011002b, 0x10110029, + 0x10110027, 0x10110024, 0x10110022, 0x10110020, + 0x1011001f, 0x1011001d, 0x1009003a, 0x10090037, + 0x10090034, 0x10090031, 0x1009002e, 0x1009002c, + 0x10090029, 0x10090027, 0x10090025, 0x10090023, + 0x10090021, 0x1009001f, 0x1009001d, 0x1009001b, + 0x1009001a, 0x10090018, 0x10090017, 0x10090016, + 0x10090015, 0x10090013, 0x10090012, 0x10090011, + 0x10090010, 0x1009000f, 0x1009000f, 0x1009000e, + 0x1009000d, 0x1009000c, 0x1009000c, 0x1009000b, + 0x1009000a, 0x1009000a, 0x10090009, 0x10090009, + 0x10090008, 0x10090008, 0x10090007, 0x10090007, + 0x10090007, 0x10090006, 0x10090006, 0x10090005, + 0x10090005, 0x10090005, 0x10090005, 0x10090004, + 0x10090004, 0x10090004, 0x10090004, 0x10090003, + 0x10090003, 0x10090003, 0x10090003, 0x10090003, + 0x10090003, 0x10090002, 0x10090002, 0x10090002, + 0x10090002, 0x10090002, 0x10090002, 0x10090002, + 0x10090002, 0x10090002, 0x10090001, 0x10090001, + 0x10090001, 0x10090001, 0x10090001, 0x10090001, +}; + +/************************************************** + * R/W ops. + **************************************************/ + +u32 b43_httab_read(struct b43_wldev *dev, u32 offset) +{ + u32 type, value; + + type = offset & B43_HTTAB_TYPEMASK; + offset &= ~B43_HTTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + switch (type) { + case B43_HTTAB_8BIT: + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO) & 0xFF; + break; + case B43_HTTAB_16BIT: + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); + break; + case B43_HTTAB_32BIT: + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_PHY_HT_TABLE_DATAHI); + value <<= 16; + value |= b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); + break; + default: + B43_WARN_ON(1); + value = 0; + } + + return value; +} + +void b43_httab_read_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, void *_data) +{ + u32 type; + u8 *data = _data; + unsigned int i; + + type = offset & B43_HTTAB_TYPEMASK; + offset &= ~B43_HTTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); + + for (i = 0; i < nr_elements; i++) { + switch (type) { + case B43_HTTAB_8BIT: + *data = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO) & 0xFF; + data++; + break; + case B43_HTTAB_16BIT: + *((u16 *)data) = b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); + data += 2; + break; + case B43_HTTAB_32BIT: + *((u32 *)data) = b43_phy_read(dev, B43_PHY_HT_TABLE_DATAHI); + *((u32 *)data) <<= 16; + *((u32 *)data) |= b43_phy_read(dev, B43_PHY_HT_TABLE_DATALO); + data += 4; + break; + default: + B43_WARN_ON(1); + } + } +} + +void b43_httab_write(struct b43_wldev *dev, u32 offset, u32 value) +{ + u32 type; + + type = offset & B43_HTTAB_TYPEMASK; + offset &= 0xFFFF; + + switch (type) { + case B43_HTTAB_8BIT: + B43_WARN_ON(value & ~0xFF); + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); + break; + case B43_HTTAB_16BIT: + B43_WARN_ON(value & ~0xFFFF); + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); + break; + case B43_HTTAB_32BIT: + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, value >> 16); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value & 0xFFFF); + break; + default: + B43_WARN_ON(1); + } + + return; +} + +void b43_httab_write_few(struct b43_wldev *dev, u32 offset, size_t num, ...) +{ + va_list args; + u32 type, value; + unsigned int i; + + type = offset & B43_HTTAB_TYPEMASK; + offset &= 0xFFFF; + + va_start(args, num); + switch (type) { + case B43_HTTAB_8BIT: + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); + for (i = 0; i < num; i++) { + value = va_arg(args, int); + B43_WARN_ON(value & ~0xFF); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); + } + break; + case B43_HTTAB_16BIT: + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); + for (i = 0; i < num; i++) { + value = va_arg(args, int); + B43_WARN_ON(value & ~0xFFFF); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); + } + break; + case B43_HTTAB_32BIT: + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); + for (i = 0; i < num; i++) { + value = va_arg(args, int); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, + value >> 16); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, + value & 0xFFFF); + } + break; + default: + B43_WARN_ON(1); + } + va_end(args); + + return; +} + +void b43_httab_write_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, const void *_data) +{ + u32 type, value; + const u8 *data = _data; + unsigned int i; + + type = offset & B43_HTTAB_TYPEMASK; + offset &= ~B43_HTTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset); + + for (i = 0; i < nr_elements; i++) { + switch (type) { + case B43_HTTAB_8BIT: + value = *data; + data++; + B43_WARN_ON(value & ~0xFF); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); + break; + case B43_HTTAB_16BIT: + value = *((u16 *)data); + data += 2; + B43_WARN_ON(value & ~0xFFFF); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value); + break; + case B43_HTTAB_32BIT: + value = *((u32 *)data); + data += 4; + b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI, value >> 16); + b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, + value & 0xFFFF); + break; + default: + B43_WARN_ON(1); + } + } +} + +/************************************************** + * Tables ops. + **************************************************/ + +#define httab_upload(dev, offset, data) do { \ + b43_httab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ + } while (0) +void b43_phy_ht_tables_init(struct b43_wldev *dev) +{ + BUILD_BUG_ON(ARRAY_SIZE(b43_httab_0x1a_0xc0_late) != + B43_HTTAB_1A_C0_LATE_SIZE); + + httab_upload(dev, B43_HTTAB16(0x12, 0), b43_httab_0x12); + httab_upload(dev, B43_HTTAB16(0x27, 0), b43_httab_0x27); + httab_upload(dev, B43_HTTAB16(0x26, 0), b43_httab_0x26); + httab_upload(dev, B43_HTTAB32(0x25, 0), b43_httab_0x25); + httab_upload(dev, B43_HTTAB32(0x2f, 0), b43_httab_0x2f); + httab_upload(dev, B43_HTTAB16(0x1a, 0), b43_httab_0x1a); + httab_upload(dev, B43_HTTAB16(0x1b, 0), b43_httab_0x1b); + httab_upload(dev, B43_HTTAB16(0x1c, 0), b43_httab_0x1c); + httab_upload(dev, B43_HTTAB32(0x1a, 0x0c0), b43_httab_0x1a_0xc0); + httab_upload(dev, B43_HTTAB32(0x1a, 0x140), b43_httab_0x1a_0x140); + httab_upload(dev, B43_HTTAB32(0x1b, 0x140), b43_httab_0x1b_0x140); + httab_upload(dev, B43_HTTAB32(0x1c, 0x140), b43_httab_0x1c_0x140); + httab_upload(dev, B43_HTTAB16(0x1a, 0x1c0), b43_httab_0x1a_0x1c0); + httab_upload(dev, B43_HTTAB16(0x1b, 0x1c0), b43_httab_0x1b_0x1c0); + httab_upload(dev, B43_HTTAB16(0x1c, 0x1c0), b43_httab_0x1c_0x1c0); + httab_upload(dev, B43_HTTAB16(0x1a, 0x240), b43_httab_0x1a_0x240); + httab_upload(dev, B43_HTTAB16(0x1b, 0x240), b43_httab_0x1b_0x240); + httab_upload(dev, B43_HTTAB16(0x1c, 0x240), b43_httab_0x1c_0x240); + httab_upload(dev, B43_HTTAB32(0x1f, 0), b43_httab_0x1f); + httab_upload(dev, B43_HTTAB32(0x21, 0), b43_httab_0x21); + httab_upload(dev, B43_HTTAB32(0x23, 0), b43_httab_0x23); + httab_upload(dev, B43_HTTAB32(0x20, 0), b43_httab_0x20); + httab_upload(dev, B43_HTTAB32(0x22, 0), b43_httab_0x22); + httab_upload(dev, B43_HTTAB32(0x24, 0), b43_httab_0x24); +} diff --git a/drivers/net/wireless/broadcom/b43/tables_phy_ht.h b/drivers/net/wireless/broadcom/b43/tables_phy_ht.h new file mode 100644 index 000000000000..1b5ef2bc770c --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/tables_phy_ht.h @@ -0,0 +1,26 @@ +#ifndef B43_TABLES_PHY_HT_H_ +#define B43_TABLES_PHY_HT_H_ + +/* The HT-PHY tables. */ +#define B43_HTTAB_TYPEMASK 0xF0000000 +#define B43_HTTAB_8BIT 0x10000000 +#define B43_HTTAB_16BIT 0x20000000 +#define B43_HTTAB_32BIT 0x30000000 +#define B43_HTTAB8(table, offset) (((table) << 10) | (offset) | B43_HTTAB_8BIT) +#define B43_HTTAB16(table, offset) (((table) << 10) | (offset) | B43_HTTAB_16BIT) +#define B43_HTTAB32(table, offset) (((table) << 10) | (offset) | B43_HTTAB_32BIT) + +u32 b43_httab_read(struct b43_wldev *dev, u32 offset); +void b43_httab_read_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, void *_data); +void b43_httab_write(struct b43_wldev *dev, u32 offset, u32 value); +void b43_httab_write_few(struct b43_wldev *dev, u32 offset, size_t num, ...); +void b43_httab_write_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, const void *_data); + +void b43_phy_ht_tables_init(struct b43_wldev *dev); + +#define B43_HTTAB_1A_C0_LATE_SIZE 128 +extern const u32 b43_httab_0x1a_0xc0_late[]; + +#endif /* B43_TABLES_PHY_HT_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/tables_phy_lcn.c b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.c new file mode 100644 index 000000000000..e347b8d80ea4 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.c @@ -0,0 +1,724 @@ +/* + + Broadcom B43 wireless driver + IEEE 802.11n LCN-PHY data tables + + Copyright (c) 2011 Rafał Miłecki + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "tables_phy_lcn.h" +#include "phy_common.h" +#include "phy_lcn.h" + +struct b43_lcntab_tx_gain_tbl_entry { + u8 gm; + u8 pga; + u8 pad; + u8 dac; + u8 bb_mult; +}; + +/************************************************** + * Static tables. + **************************************************/ + +static const u16 b43_lcntab_0x02[] = { + 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, + 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, + 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, + 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, + 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, + 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, + 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, + 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, + 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, + 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d, + 0x014d, 0x014d, 0x014d, 0x014d, +}; + +static const u16 b43_lcntab_0x01[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const u32 b43_lcntab_0x0b[] = { + 0x000141f8, 0x000021f8, 0x000021fb, 0x000041fb, + 0x0001fedb, 0x0000217b, 0x00002133, 0x000040eb, + 0x0001fea3, 0x0000024b, +}; + +static const u32 b43_lcntab_0x0c[] = { + 0x00100001, 0x00200010, 0x00300001, 0x00400010, + 0x00500022, 0x00600122, 0x00700222, 0x00800322, + 0x00900422, 0x00a00522, 0x00b00622, 0x00c00722, + 0x00d00822, 0x00f00922, 0x00100a22, 0x00200b22, + 0x00300c22, 0x00400d22, 0x00500e22, 0x00600f22, +}; + +static const u32 b43_lcntab_0x0d[] = { + 0x00000000, 0x00000000, 0x10000000, 0x00000000, + 0x20000000, 0x00000000, 0x30000000, 0x00000000, + 0x40000000, 0x00000000, 0x50000000, 0x00000000, + 0x60000000, 0x00000000, 0x70000000, 0x00000000, + 0x80000000, 0x00000000, 0x90000000, 0x00000008, + 0xa0000000, 0x00000008, 0xb0000000, 0x00000008, + 0xc0000000, 0x00000008, 0xd0000000, 0x00000008, + 0xe0000000, 0x00000008, 0xf0000000, 0x00000008, + 0x00000000, 0x00000009, 0x10000000, 0x00000009, + 0x20000000, 0x00000019, 0x30000000, 0x00000019, + 0x40000000, 0x00000019, 0x50000000, 0x00000019, + 0x60000000, 0x00000019, 0x70000000, 0x00000019, + 0x80000000, 0x00000019, 0x90000000, 0x00000019, + 0xa0000000, 0x00000019, 0xb0000000, 0x00000019, + 0xc0000000, 0x00000019, 0xd0000000, 0x00000019, + 0xe0000000, 0x00000019, 0xf0000000, 0x00000019, + 0x00000000, 0x0000001a, 0x10000000, 0x0000001a, + 0x20000000, 0x0000001a, 0x30000000, 0x0000001a, + 0x40000000, 0x0000001a, 0x50000000, 0x00000002, + 0x60000000, 0x00000002, 0x70000000, 0x00000002, + 0x80000000, 0x00000002, 0x90000000, 0x00000002, + 0xa0000000, 0x00000002, 0xb0000000, 0x00000002, + 0xc0000000, 0x0000000a, 0xd0000000, 0x0000000a, + 0xe0000000, 0x0000000a, 0xf0000000, 0x0000000a, + 0x00000000, 0x0000000b, 0x10000000, 0x0000000b, + 0x20000000, 0x0000000b, 0x30000000, 0x0000000b, + 0x40000000, 0x0000000b, 0x50000000, 0x0000001b, + 0x60000000, 0x0000001b, 0x70000000, 0x0000001b, + 0x80000000, 0x0000001b, 0x90000000, 0x0000001b, + 0xa0000000, 0x0000001b, 0xb0000000, 0x0000001b, + 0xc0000000, 0x0000001b, 0xd0000000, 0x0000001b, + 0xe0000000, 0x0000001b, 0xf0000000, 0x0000001b, + 0x00000000, 0x0000001c, 0x10000000, 0x0000001c, + 0x20000000, 0x0000001c, 0x30000000, 0x0000001c, + 0x40000000, 0x0000001c, 0x50000000, 0x0000001c, + 0x60000000, 0x0000001c, 0x70000000, 0x0000001c, + 0x80000000, 0x0000001c, 0x90000000, 0x0000001c, +}; + +static const u16 b43_lcntab_0x0e[] = { + 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, + 0x0407, 0x0408, 0x0409, 0x040a, 0x058b, 0x058c, + 0x058d, 0x058e, 0x058f, 0x0090, 0x0091, 0x0092, + 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, 0x0198, + 0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x019e, + 0x019f, 0x01a0, 0x01a1, 0x01a2, 0x01a3, 0x01a4, + 0x01a5, 0x0000, +}; + +static const u16 b43_lcntab_0x0f[] = { + 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, + 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, + 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, + 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, + 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, + 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, + 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, + 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, + 0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009, + 0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005, + 0x000a, 0x0009, 0x0006, 0x0005, +}; + +static const u16 b43_lcntab_0x10[] = { + 0x005f, 0x0036, 0x0029, 0x001f, 0x005f, 0x0036, + 0x0029, 0x001f, 0x005f, 0x0036, 0x0029, 0x001f, + 0x005f, 0x0036, 0x0029, 0x001f, +}; + +static const u16 b43_lcntab_0x11[] = { + 0x0009, 0x000f, 0x0014, 0x0018, 0x00fe, 0x0007, + 0x000b, 0x000f, 0x00fb, 0x00fe, 0x0001, 0x0005, + 0x0008, 0x000b, 0x000e, 0x0011, 0x0014, 0x0017, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0003, 0x0006, 0x0009, 0x000c, 0x000f, + 0x0012, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, + 0x0006, 0x0009, 0x000c, 0x000f, 0x0012, 0x0015, + 0x0018, 0x001b, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0003, 0x00eb, 0x0000, 0x0000, +}; + +static const u32 b43_lcntab_0x12[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000004, 0x00000000, 0x00000004, 0x00000008, + 0x00000001, 0x00000005, 0x00000009, 0x0000000d, + 0x0000004d, 0x0000008d, 0x0000000d, 0x0000004d, + 0x0000008d, 0x000000cd, 0x0000004f, 0x0000008f, + 0x000000cf, 0x000000d3, 0x00000113, 0x00000513, + 0x00000913, 0x00000953, 0x00000d53, 0x00001153, + 0x00001193, 0x00005193, 0x00009193, 0x0000d193, + 0x00011193, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000004, + 0x00000000, 0x00000004, 0x00000008, 0x00000001, + 0x00000005, 0x00000009, 0x0000000d, 0x0000004d, + 0x0000008d, 0x0000000d, 0x0000004d, 0x0000008d, + 0x000000cd, 0x0000004f, 0x0000008f, 0x000000cf, + 0x000000d3, 0x00000113, 0x00000513, 0x00000913, + 0x00000953, 0x00000d53, 0x00001153, 0x00005153, + 0x00009153, 0x0000d153, 0x00011153, 0x00015153, + 0x00019153, 0x0001d153, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u16 b43_lcntab_0x14[] = { + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0002, 0x0003, 0x0001, 0x0003, 0x0002, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0003, + 0x0001, 0x0003, 0x0002, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, +}; + +static const u16 b43_lcntab_0x17[] = { + 0x001a, 0x0034, 0x004e, 0x0068, 0x009c, 0x00d0, + 0x00ea, 0x0104, 0x0034, 0x0068, 0x009c, 0x00d0, + 0x0138, 0x01a0, 0x01d4, 0x0208, 0x004e, 0x009c, + 0x00ea, 0x0138, 0x01d4, 0x0270, 0x02be, 0x030c, + 0x0068, 0x00d0, 0x0138, 0x01a0, 0x0270, 0x0340, + 0x03a8, 0x0410, 0x0018, 0x009c, 0x00d0, 0x0104, + 0x00ea, 0x0138, 0x0186, 0x00d0, 0x0104, 0x0104, + 0x0138, 0x016c, 0x016c, 0x01a0, 0x0138, 0x0186, + 0x0186, 0x01d4, 0x0222, 0x0222, 0x0270, 0x0104, + 0x0138, 0x016c, 0x0138, 0x016c, 0x01a0, 0x01d4, + 0x01a0, 0x01d4, 0x0208, 0x0208, 0x023c, 0x0186, + 0x01d4, 0x0222, 0x01d4, 0x0222, 0x0270, 0x02be, + 0x0270, 0x02be, 0x030c, 0x030c, 0x035a, 0x0036, + 0x006c, 0x00a2, 0x00d8, 0x0144, 0x01b0, 0x01e6, + 0x021c, 0x006c, 0x00d8, 0x0144, 0x01b0, 0x0288, + 0x0360, 0x03cc, 0x0438, 0x00a2, 0x0144, 0x01e6, + 0x0288, 0x03cc, 0x0510, 0x05b2, 0x0654, 0x00d8, + 0x01b0, 0x0288, 0x0360, 0x0510, 0x06c0, 0x0798, + 0x0870, 0x0018, 0x0144, 0x01b0, 0x021c, 0x01e6, + 0x0288, 0x032a, 0x01b0, 0x021c, 0x021c, 0x0288, + 0x02f4, 0x02f4, 0x0360, 0x0288, 0x032a, 0x032a, + 0x03cc, 0x046e, 0x046e, 0x0510, 0x021c, 0x0288, + 0x02f4, 0x0288, 0x02f4, 0x0360, 0x03cc, 0x0360, + 0x03cc, 0x0438, 0x0438, 0x04a4, 0x032a, 0x03cc, + 0x046e, 0x03cc, 0x046e, 0x0510, 0x05b2, 0x0510, + 0x05b2, 0x0654, 0x0654, 0x06f6, +}; + +static const u16 b43_lcntab_0x00[] = { + 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, + 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, + 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600, + 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, + 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, + 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const u32 b43_lcntab_0x18[] = { + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, + 0x00080000, 0x00080000, 0x00080000, 0x00080000, +}; + +/************************************************** + * TX gain. + **************************************************/ + +static const struct b43_lcntab_tx_gain_tbl_entry + b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0[B43_LCNTAB_TX_GAIN_SIZE] = { + { 0x03, 0x00, 0x1f, 0x0, 0x48 }, + { 0x03, 0x00, 0x1f, 0x0, 0x46 }, + { 0x03, 0x00, 0x1f, 0x0, 0x44 }, + { 0x03, 0x00, 0x1e, 0x0, 0x43 }, + { 0x03, 0x00, 0x1d, 0x0, 0x44 }, + { 0x03, 0x00, 0x1c, 0x0, 0x44 }, + { 0x03, 0x00, 0x1b, 0x0, 0x45 }, + { 0x03, 0x00, 0x1a, 0x0, 0x46 }, + { 0x03, 0x00, 0x19, 0x0, 0x46 }, + { 0x03, 0x00, 0x18, 0x0, 0x47 }, + { 0x03, 0x00, 0x17, 0x0, 0x48 }, + { 0x03, 0x00, 0x17, 0x0, 0x46 }, + { 0x03, 0x00, 0x16, 0x0, 0x47 }, + { 0x03, 0x00, 0x15, 0x0, 0x48 }, + { 0x03, 0x00, 0x15, 0x0, 0x46 }, + { 0x03, 0x00, 0x15, 0x0, 0x44 }, + { 0x03, 0x00, 0x15, 0x0, 0x42 }, + { 0x03, 0x00, 0x15, 0x0, 0x40 }, + { 0x03, 0x00, 0x15, 0x0, 0x3f }, + { 0x03, 0x00, 0x14, 0x0, 0x40 }, + { 0x03, 0x00, 0x13, 0x0, 0x41 }, + { 0x03, 0x00, 0x13, 0x0, 0x40 }, + { 0x03, 0x00, 0x12, 0x0, 0x41 }, + { 0x03, 0x00, 0x12, 0x0, 0x40 }, + { 0x03, 0x00, 0x11, 0x0, 0x41 }, + { 0x03, 0x00, 0x11, 0x0, 0x40 }, + { 0x03, 0x00, 0x10, 0x0, 0x41 }, + { 0x03, 0x00, 0x10, 0x0, 0x40 }, + { 0x03, 0x00, 0x10, 0x0, 0x3e }, + { 0x03, 0x00, 0x10, 0x0, 0x3c }, + { 0x03, 0x00, 0x10, 0x0, 0x3a }, + { 0x03, 0x00, 0x0f, 0x0, 0x3d }, + { 0x03, 0x00, 0x0f, 0x0, 0x3b }, + { 0x03, 0x00, 0x0e, 0x0, 0x3d }, + { 0x03, 0x00, 0x0e, 0x0, 0x3c }, + { 0x03, 0x00, 0x0e, 0x0, 0x3a }, + { 0x03, 0x00, 0x0d, 0x0, 0x3c }, + { 0x03, 0x00, 0x0d, 0x0, 0x3b }, + { 0x03, 0x00, 0x0c, 0x0, 0x3e }, + { 0x03, 0x00, 0x0c, 0x0, 0x3c }, + { 0x03, 0x00, 0x0c, 0x0, 0x3a }, + { 0x03, 0x00, 0x0b, 0x0, 0x3e }, + { 0x03, 0x00, 0x0b, 0x0, 0x3c }, + { 0x03, 0x00, 0x0b, 0x0, 0x3b }, + { 0x03, 0x00, 0x0b, 0x0, 0x39 }, + { 0x03, 0x00, 0x0a, 0x0, 0x3d }, + { 0x03, 0x00, 0x0a, 0x0, 0x3b }, + { 0x03, 0x00, 0x0a, 0x0, 0x39 }, + { 0x03, 0x00, 0x09, 0x0, 0x3e }, + { 0x03, 0x00, 0x09, 0x0, 0x3c }, + { 0x03, 0x00, 0x09, 0x0, 0x3a }, + { 0x03, 0x00, 0x09, 0x0, 0x39 }, + { 0x03, 0x00, 0x08, 0x0, 0x3e }, + { 0x03, 0x00, 0x08, 0x0, 0x3c }, + { 0x03, 0x00, 0x08, 0x0, 0x3a }, + { 0x03, 0x00, 0x08, 0x0, 0x39 }, + { 0x03, 0x00, 0x08, 0x0, 0x37 }, + { 0x03, 0x00, 0x07, 0x0, 0x3d }, + { 0x03, 0x00, 0x07, 0x0, 0x3c }, + { 0x03, 0x00, 0x07, 0x0, 0x3a }, + { 0x03, 0x00, 0x07, 0x0, 0x38 }, + { 0x03, 0x00, 0x07, 0x0, 0x37 }, + { 0x03, 0x00, 0x06, 0x0, 0x3e }, + { 0x03, 0x00, 0x06, 0x0, 0x3c }, + { 0x03, 0x00, 0x06, 0x0, 0x3a }, + { 0x03, 0x00, 0x06, 0x0, 0x39 }, + { 0x03, 0x00, 0x06, 0x0, 0x37 }, + { 0x03, 0x00, 0x06, 0x0, 0x36 }, + { 0x03, 0x00, 0x06, 0x0, 0x34 }, + { 0x03, 0x00, 0x05, 0x0, 0x3d }, + { 0x03, 0x00, 0x05, 0x0, 0x3b }, + { 0x03, 0x00, 0x05, 0x0, 0x39 }, + { 0x03, 0x00, 0x05, 0x0, 0x38 }, + { 0x03, 0x00, 0x05, 0x0, 0x36 }, + { 0x03, 0x00, 0x05, 0x0, 0x35 }, + { 0x03, 0x00, 0x05, 0x0, 0x33 }, + { 0x03, 0x00, 0x04, 0x0, 0x3e }, + { 0x03, 0x00, 0x04, 0x0, 0x3c }, + { 0x03, 0x00, 0x04, 0x0, 0x3a }, + { 0x03, 0x00, 0x04, 0x0, 0x39 }, + { 0x03, 0x00, 0x04, 0x0, 0x37 }, + { 0x03, 0x00, 0x04, 0x0, 0x36 }, + { 0x03, 0x00, 0x04, 0x0, 0x34 }, + { 0x03, 0x00, 0x04, 0x0, 0x33 }, + { 0x03, 0x00, 0x04, 0x0, 0x31 }, + { 0x03, 0x00, 0x04, 0x0, 0x30 }, + { 0x03, 0x00, 0x04, 0x0, 0x2e }, + { 0x03, 0x00, 0x03, 0x0, 0x3c }, + { 0x03, 0x00, 0x03, 0x0, 0x3a }, + { 0x03, 0x00, 0x03, 0x0, 0x39 }, + { 0x03, 0x00, 0x03, 0x0, 0x37 }, + { 0x03, 0x00, 0x03, 0x0, 0x36 }, + { 0x03, 0x00, 0x03, 0x0, 0x34 }, + { 0x03, 0x00, 0x03, 0x0, 0x33 }, + { 0x03, 0x00, 0x03, 0x0, 0x31 }, + { 0x03, 0x00, 0x03, 0x0, 0x30 }, + { 0x03, 0x00, 0x03, 0x0, 0x2e }, + { 0x03, 0x00, 0x03, 0x0, 0x2d }, + { 0x03, 0x00, 0x03, 0x0, 0x2c }, + { 0x03, 0x00, 0x03, 0x0, 0x2b }, + { 0x03, 0x00, 0x03, 0x0, 0x29 }, + { 0x03, 0x00, 0x02, 0x0, 0x3d }, + { 0x03, 0x00, 0x02, 0x0, 0x3b }, + { 0x03, 0x00, 0x02, 0x0, 0x39 }, + { 0x03, 0x00, 0x02, 0x0, 0x38 }, + { 0x03, 0x00, 0x02, 0x0, 0x36 }, + { 0x03, 0x00, 0x02, 0x0, 0x35 }, + { 0x03, 0x00, 0x02, 0x0, 0x33 }, + { 0x03, 0x00, 0x02, 0x0, 0x32 }, + { 0x03, 0x00, 0x02, 0x0, 0x30 }, + { 0x03, 0x00, 0x02, 0x0, 0x2f }, + { 0x03, 0x00, 0x02, 0x0, 0x2e }, + { 0x03, 0x00, 0x02, 0x0, 0x2c }, + { 0x03, 0x00, 0x02, 0x0, 0x2b }, + { 0x03, 0x00, 0x02, 0x0, 0x2a }, + { 0x03, 0x00, 0x02, 0x0, 0x29 }, + { 0x03, 0x00, 0x02, 0x0, 0x27 }, + { 0x03, 0x00, 0x02, 0x0, 0x26 }, + { 0x03, 0x00, 0x02, 0x0, 0x25 }, + { 0x03, 0x00, 0x02, 0x0, 0x24 }, + { 0x03, 0x00, 0x02, 0x0, 0x23 }, + { 0x03, 0x00, 0x02, 0x0, 0x22 }, + { 0x03, 0x00, 0x02, 0x0, 0x21 }, + { 0x03, 0x00, 0x02, 0x0, 0x20 }, + { 0x03, 0x00, 0x01, 0x0, 0x3f }, + { 0x03, 0x00, 0x01, 0x0, 0x3d }, + { 0x03, 0x00, 0x01, 0x0, 0x3b }, + { 0x03, 0x00, 0x01, 0x0, 0x39 }, +}; + +/************************************************** + * SW control. + **************************************************/ + +static const u16 b43_lcntab_sw_ctl_4313_epa_rev0[] = { + 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, + 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, + 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, + 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, + 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, + 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, + 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, + 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, + 0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008, + 0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001, + 0x0002, 0x0008, 0x0004, 0x0001, +}; + +/************************************************** + * R/W ops. + **************************************************/ + +u32 b43_lcntab_read(struct b43_wldev *dev, u32 offset) +{ + u32 type, value; + + type = offset & B43_LCNTAB_TYPEMASK; + offset &= ~B43_LCNTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + switch (type) { + case B43_LCNTAB_8BIT: + b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO) & 0xFF; + break; + case B43_LCNTAB_16BIT: + b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO); + break; + case B43_LCNTAB_32BIT: + b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); + value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO); + value |= (b43_phy_read(dev, B43_PHY_LCN_TABLE_DATAHI) << 16); + break; + default: + B43_WARN_ON(1); + value = 0; + } + + return value; +} + +void b43_lcntab_read_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, void *_data) +{ + u32 type; + u8 *data = _data; + unsigned int i; + + type = offset & B43_LCNTAB_TYPEMASK; + offset &= ~B43_LCNTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); + + for (i = 0; i < nr_elements; i++) { + switch (type) { + case B43_LCNTAB_8BIT: + *data = b43_phy_read(dev, + B43_PHY_LCN_TABLE_DATALO) & 0xFF; + data++; + break; + case B43_LCNTAB_16BIT: + *((u16 *)data) = b43_phy_read(dev, + B43_PHY_LCN_TABLE_DATALO); + data += 2; + break; + case B43_LCNTAB_32BIT: + *((u32 *)data) = b43_phy_read(dev, + B43_PHY_LCN_TABLE_DATALO); + *((u32 *)data) |= (b43_phy_read(dev, + B43_PHY_LCN_TABLE_DATAHI) << 16); + data += 4; + break; + default: + B43_WARN_ON(1); + } + } +} + +void b43_lcntab_write(struct b43_wldev *dev, u32 offset, u32 value) +{ + u32 type; + + type = offset & B43_LCNTAB_TYPEMASK; + offset &= 0xFFFF; + + switch (type) { + case B43_LCNTAB_8BIT: + B43_WARN_ON(value & ~0xFF); + b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); + break; + case B43_LCNTAB_16BIT: + B43_WARN_ON(value & ~0xFFFF); + b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); + break; + case B43_LCNTAB_32BIT: + b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, value >> 16); + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value & 0xFFFF); + break; + default: + B43_WARN_ON(1); + } + + return; +} + +void b43_lcntab_write_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, const void *_data) +{ + u32 type, value; + const u8 *data = _data; + unsigned int i; + + type = offset & B43_LCNTAB_TYPEMASK; + offset &= ~B43_LCNTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset); + + for (i = 0; i < nr_elements; i++) { + switch (type) { + case B43_LCNTAB_8BIT: + value = *data; + data++; + B43_WARN_ON(value & ~0xFF); + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); + break; + case B43_LCNTAB_16BIT: + value = *((u16 *)data); + data += 2; + B43_WARN_ON(value & ~0xFFFF); + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value); + break; + case B43_LCNTAB_32BIT: + value = *((u32 *)data); + data += 4; + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, + value >> 16); + b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, + value & 0xFFFF); + break; + default: + B43_WARN_ON(1); + } + } +} + +/************************************************** + * Tables ops. + **************************************************/ + +#define lcntab_upload(dev, offset, data) do { \ + b43_lcntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ + } while (0) +static void b43_phy_lcn_upload_static_tables(struct b43_wldev *dev) +{ + lcntab_upload(dev, B43_LCNTAB16(0x02, 0), b43_lcntab_0x02); + lcntab_upload(dev, B43_LCNTAB16(0x01, 0), b43_lcntab_0x01); + lcntab_upload(dev, B43_LCNTAB32(0x0b, 0), b43_lcntab_0x0b); + lcntab_upload(dev, B43_LCNTAB32(0x0c, 0), b43_lcntab_0x0c); + lcntab_upload(dev, B43_LCNTAB32(0x0d, 0), b43_lcntab_0x0d); + lcntab_upload(dev, B43_LCNTAB16(0x0e, 0), b43_lcntab_0x0e); + lcntab_upload(dev, B43_LCNTAB16(0x0f, 0), b43_lcntab_0x0f); + lcntab_upload(dev, B43_LCNTAB16(0x10, 0), b43_lcntab_0x10); + lcntab_upload(dev, B43_LCNTAB16(0x11, 0), b43_lcntab_0x11); + lcntab_upload(dev, B43_LCNTAB32(0x12, 0), b43_lcntab_0x12); + lcntab_upload(dev, B43_LCNTAB16(0x14, 0), b43_lcntab_0x14); + lcntab_upload(dev, B43_LCNTAB16(0x17, 0), b43_lcntab_0x17); + lcntab_upload(dev, B43_LCNTAB16(0x00, 0), b43_lcntab_0x00); + lcntab_upload(dev, B43_LCNTAB32(0x18, 0), b43_lcntab_0x18); +} + +static void b43_phy_lcn_load_tx_gain_tab(struct b43_wldev *dev, + const struct b43_lcntab_tx_gain_tbl_entry *gain_table) +{ + u32 i; + u32 val; + + u16 pa_gain = 0x70; + if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM) + pa_gain = 0x10; + + for (i = 0; i < B43_LCNTAB_TX_GAIN_SIZE; i++) { + val = ((pa_gain << 24) | + (gain_table[i].pad << 16) | + (gain_table[i].pga << 8) | + gain_table[i].gm); + b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0xc0 + i), val); + + /* brcmsmac doesn't maskset, we follow newer wl here */ + val = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x140 + i)); + val &= 0x000fffff; + val |= ((gain_table[i].dac << 28) | + (gain_table[i].bb_mult << 20)); + b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x140 + i), val); + } +} + +/* wlc_lcnphy_load_rfpower */ +static void b43_phy_lcn_load_rfpower(struct b43_wldev *dev) +{ + u32 bbmult, rfgain; + u8 i; + + for (i = 0; i < 128; i++) { + bbmult = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x140 + i)); + bbmult >>= 20; + rfgain = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0xc0 + i)); + + /* TODO: calculate value for 0x240 + i table offset + * b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x240 + i), val); + */ + } +} + +/* Not implemented in brcmsmac, noticed in wl in MMIO dump */ +static void b43_phy_lcn_rewrite_rfpower_table(struct b43_wldev *dev) +{ + int i; + u32 tmp; + for (i = 0; i < 128; i++) { + tmp = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x240 + i)); + b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x240 + i), tmp); + } +} + +/* wlc_lcnphy_clear_papd_comptable */ +static void b43_phy_lcn_clean_papd_comp_table(struct b43_wldev *dev) +{ + u8 i; + + for (i = 0; i < 0x80; i++) + b43_lcntab_write(dev, B43_LCNTAB32(0x18, i), 0x80000); +} + +/* wlc_lcnphy_tbl_init */ +void b43_phy_lcn_tables_init(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + + b43_phy_lcn_upload_static_tables(dev); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (sprom->boardflags_lo & B43_BFL_FEM) + b43_phy_lcn_load_tx_gain_tab(dev, + b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0); + else + b43err(dev->wl, + "TX gain table unknown for this card\n"); + } + + if (sprom->boardflags_lo & B43_BFL_FEM && + !(sprom->boardflags_hi & B43_BFH_FEM_BT)) + b43_lcntab_write_bulk(dev, B43_LCNTAB16(0xf, 0), + ARRAY_SIZE(b43_lcntab_sw_ctl_4313_epa_rev0), + b43_lcntab_sw_ctl_4313_epa_rev0); + else + b43err(dev->wl, "SW ctl table is unknown for this card\n"); + + b43_phy_lcn_load_rfpower(dev); + b43_phy_lcn_rewrite_rfpower_table(dev); + b43_phy_lcn_clean_papd_comp_table(dev); +} diff --git a/drivers/net/wireless/broadcom/b43/tables_phy_lcn.h b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.h new file mode 100644 index 000000000000..caff9db6831f --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.h @@ -0,0 +1,24 @@ +#ifndef B43_TABLES_PHY_LCN_H_ +#define B43_TABLES_PHY_LCN_H_ + +/* The LCN-PHY tables. */ +#define B43_LCNTAB_TYPEMASK 0xF0000000 +#define B43_LCNTAB_8BIT 0x10000000 +#define B43_LCNTAB_16BIT 0x20000000 +#define B43_LCNTAB_32BIT 0x30000000 +#define B43_LCNTAB8(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_8BIT) +#define B43_LCNTAB16(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_16BIT) +#define B43_LCNTAB32(table, offset) (((table) << 10) | (offset) | B43_LCNTAB_32BIT) + +#define B43_LCNTAB_TX_GAIN_SIZE 128 + +u32 b43_lcntab_read(struct b43_wldev *dev, u32 offset); +void b43_lcntab_read_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, void *_data); +void b43_lcntab_write(struct b43_wldev *dev, u32 offset, u32 value); +void b43_lcntab_write_bulk(struct b43_wldev *dev, u32 offset, + unsigned int nr_elements, const void *_data); + +void b43_phy_lcn_tables_init(struct b43_wldev *dev); + +#endif /* B43_TABLES_PHY_LCN_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/wa.c b/drivers/net/wireless/broadcom/b43/wa.c new file mode 100644 index 000000000000..c218c08fb2f5 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/wa.c @@ -0,0 +1,634 @@ +/* + + Broadcom B43 wireless driver + + PHY workarounds. + + Copyright (c) 2005-2007 Stefano Brivio + Copyright (c) 2005-2007 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43.h" +#include "main.h" +#include "tables.h" +#include "phy_common.h" +#include "wa.h" + +static void b43_wa_papd(struct b43_wldev *dev) +{ + u16 backup; + + backup = b43_ofdmtab_read16(dev, B43_OFDMTAB_PWRDYN2, 0); + b43_ofdmtab_write16(dev, B43_OFDMTAB_PWRDYN2, 0, 7); + b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_APHY, 0, 0); + b43_dummy_transmission(dev, true, true); + b43_ofdmtab_write16(dev, B43_OFDMTAB_PWRDYN2, 0, backup); +} + +static void b43_wa_auxclipthr(struct b43_wldev *dev) +{ + b43_phy_write(dev, B43_PHY_OFDM(0x8E), 0x3800); +} + +static void b43_wa_afcdac(struct b43_wldev *dev) +{ + b43_phy_write(dev, 0x0035, 0x03FF); + b43_phy_write(dev, 0x0036, 0x0400); +} + +static void b43_wa_txdc_offset(struct b43_wldev *dev) +{ + b43_ofdmtab_write16(dev, B43_OFDMTAB_DC, 0, 0x0051); +} + +void b43_wa_initgains(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + b43_phy_write(dev, B43_PHY_LNAHPFCTL, 0x1FF9); + b43_phy_mask(dev, B43_PHY_LPFGAINCTL, 0xFF0F); + if (phy->rev <= 2) + b43_ofdmtab_write16(dev, B43_OFDMTAB_LPFGAIN, 0, 0x1FBF); + b43_radio_write16(dev, 0x0002, 0x1FBF); + + b43_phy_write(dev, 0x0024, 0x4680); + b43_phy_write(dev, 0x0020, 0x0003); + b43_phy_write(dev, 0x001D, 0x0F40); + b43_phy_write(dev, 0x001F, 0x1C00); + if (phy->rev <= 3) + b43_phy_maskset(dev, 0x002A, 0x00FF, 0x0400); + else if (phy->rev == 5) { + b43_phy_maskset(dev, 0x002A, 0x00FF, 0x1A00); + b43_phy_write(dev, 0x00CC, 0x2121); + } + if (phy->rev >= 3) + b43_phy_write(dev, 0x00BA, 0x3ED5); +} + +static void b43_wa_divider(struct b43_wldev *dev) +{ + b43_phy_mask(dev, 0x002B, ~0x0100); + b43_phy_write(dev, 0x008E, 0x58C1); +} + +static void b43_wa_gt(struct b43_wldev *dev) /* Gain table. */ +{ + if (dev->phy.rev <= 2) { + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 0, 15); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 1, 31); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 2, 42); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 3, 48); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN2, 4, 58); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 0, 19); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 1, 19); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 2, 19); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 3, 19); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 4, 21); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 5, 21); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 6, 25); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN1, 0, 3); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN1, 1, 3); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN1, 2, 7); + } else { + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 0, 19); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 1, 19); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 2, 19); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 3, 19); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 4, 21); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 5, 21); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAIN0, 6, 25); + } +} + +static void b43_wa_rssi_lt(struct b43_wldev *dev) /* RSSI lookup table */ +{ + int i; + + if (0 /* FIXME: For APHY.rev=2 this might be needed */) { + for (i = 0; i < 8; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_RSSI, i, i + 8); + for (i = 8; i < 16; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_RSSI, i, i - 8); + } else { + for (i = 0; i < 64; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_RSSI, i, i); + } +} + +static void b43_wa_analog(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + u16 ofdmrev; + + ofdmrev = b43_phy_read(dev, B43_PHY_VERSION_OFDM) & B43_PHYVER_VERSION; + if (ofdmrev > 2) { + if (phy->type == B43_PHYTYPE_A) + b43_phy_write(dev, B43_PHY_PWRDOWN, 0x1808); + else + b43_phy_write(dev, B43_PHY_PWRDOWN, 0x1000); + } else { + b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 3, 0x1044); + b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 4, 0x7201); + b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 6, 0x0040); + } +} + +static void b43_wa_dac(struct b43_wldev *dev) +{ + if (dev->phy.analog == 1) + b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 1, + (b43_ofdmtab_read16(dev, B43_OFDMTAB_DAC, 1) & ~0x0034) | 0x0008); + else + b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 1, + (b43_ofdmtab_read16(dev, B43_OFDMTAB_DAC, 1) & ~0x0078) | 0x0010); +} + +static void b43_wa_fft(struct b43_wldev *dev) /* Fine frequency table */ +{ + int i; + + if (dev->phy.type == B43_PHYTYPE_A) + for (i = 0; i < B43_TAB_FINEFREQA_SIZE; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_DACRFPABB, i, b43_tab_finefreqa[i]); + else + for (i = 0; i < B43_TAB_FINEFREQG_SIZE; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_DACRFPABB, i, b43_tab_finefreqg[i]); +} + +static void b43_wa_nft(struct b43_wldev *dev) /* Noise figure table */ +{ + struct b43_phy *phy = &dev->phy; + int i; + + if (phy->type == B43_PHYTYPE_A) { + if (phy->rev == 2) + for (i = 0; i < B43_TAB_NOISEA2_SIZE; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noisea2[i]); + else + for (i = 0; i < B43_TAB_NOISEA3_SIZE; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noisea3[i]); + } else { + if (phy->rev == 1) + for (i = 0; i < B43_TAB_NOISEG1_SIZE; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noiseg1[i]); + else + for (i = 0; i < B43_TAB_NOISEG2_SIZE; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, i, b43_tab_noiseg2[i]); + } +} + +static void b43_wa_rt(struct b43_wldev *dev) /* Rotor table */ +{ + int i; + + for (i = 0; i < B43_TAB_ROTOR_SIZE; i++) + b43_ofdmtab_write32(dev, B43_OFDMTAB_ROTOR, i, b43_tab_rotor[i]); +} + +static void b43_write_null_nst(struct b43_wldev *dev) +{ + int i; + + for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE, i, 0); +} + +static void b43_write_nst(struct b43_wldev *dev, const u16 *nst) +{ + int i; + + for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++) + b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE, i, nst[i]); +} + +static void b43_wa_nst(struct b43_wldev *dev) /* Noise scale table */ +{ + struct b43_phy *phy = &dev->phy; + + if (phy->type == B43_PHYTYPE_A) { + if (phy->rev <= 1) + b43_write_null_nst(dev); + else if (phy->rev == 2) + b43_write_nst(dev, b43_tab_noisescalea2); + else if (phy->rev == 3) + b43_write_nst(dev, b43_tab_noisescalea3); + else + b43_write_nst(dev, b43_tab_noisescaleg3); + } else { + if (phy->rev >= 6) { + if (b43_phy_read(dev, B43_PHY_ENCORE) & B43_PHY_ENCORE_EN) + b43_write_nst(dev, b43_tab_noisescaleg3); + else + b43_write_nst(dev, b43_tab_noisescaleg2); + } else { + b43_write_nst(dev, b43_tab_noisescaleg1); + } + } +} + +static void b43_wa_art(struct b43_wldev *dev) /* ADV retard table */ +{ + int i; + + for (i = 0; i < B43_TAB_RETARD_SIZE; i++) + b43_ofdmtab_write32(dev, B43_OFDMTAB_ADVRETARD, + i, b43_tab_retard[i]); +} + +static void b43_wa_txlna_gain(struct b43_wldev *dev) +{ + b43_ofdmtab_write16(dev, B43_OFDMTAB_DC, 13, 0x0000); +} + +static void b43_wa_crs_reset(struct b43_wldev *dev) +{ + b43_phy_write(dev, 0x002C, 0x0064); +} + +static void b43_wa_2060txlna_gain(struct b43_wldev *dev) +{ + b43_hf_write(dev, b43_hf_read(dev) | + B43_HF_2060W); +} + +static void b43_wa_lms(struct b43_wldev *dev) +{ + b43_phy_maskset(dev, 0x0055, 0xFFC0, 0x0004); +} + +static void b43_wa_mixedsignal(struct b43_wldev *dev) +{ + b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 1, 3); +} + +static void b43_wa_msst(struct b43_wldev *dev) /* Min sigma square table */ +{ + struct b43_phy *phy = &dev->phy; + int i; + const u16 *tab; + + if (phy->type == B43_PHYTYPE_A) { + tab = b43_tab_sigmasqr1; + } else if (phy->type == B43_PHYTYPE_G) { + tab = b43_tab_sigmasqr2; + } else { + B43_WARN_ON(1); + return; + } + + for (i = 0; i < B43_TAB_SIGMASQR_SIZE; i++) { + b43_ofdmtab_write16(dev, B43_OFDMTAB_MINSIGSQ, + i, tab[i]); + } +} + +static void b43_wa_iqadc(struct b43_wldev *dev) +{ + if (dev->phy.analog == 4) + b43_ofdmtab_write16(dev, B43_OFDMTAB_DAC, 0, + b43_ofdmtab_read16(dev, B43_OFDMTAB_DAC, 0) & ~0xF000); +} + +static void b43_wa_crs_ed(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + if (phy->rev == 1) { + b43_phy_write(dev, B43_PHY_CRSTHRES1_R1, 0x4F19); + } else if (phy->rev == 2) { + b43_phy_write(dev, B43_PHY_CRSTHRES1, 0x1861); + b43_phy_write(dev, B43_PHY_CRSTHRES2, 0x0271); + b43_phy_set(dev, B43_PHY_ANTDWELL, 0x0800); + } else { + b43_phy_write(dev, B43_PHY_CRSTHRES1, 0x0098); + b43_phy_write(dev, B43_PHY_CRSTHRES2, 0x0070); + b43_phy_write(dev, B43_PHY_OFDM(0xC9), 0x0080); + b43_phy_set(dev, B43_PHY_ANTDWELL, 0x0800); + } +} + +static void b43_wa_crs_thr(struct b43_wldev *dev) +{ + b43_phy_maskset(dev, B43_PHY_CRS0, ~0x03C0, 0xD000); +} + +static void b43_wa_crs_blank(struct b43_wldev *dev) +{ + b43_phy_write(dev, B43_PHY_OFDM(0x2C), 0x005A); +} + +static void b43_wa_cck_shiftbits(struct b43_wldev *dev) +{ + b43_phy_write(dev, B43_PHY_CCKSHIFTBITS, 0x0026); +} + +static void b43_wa_wrssi_offset(struct b43_wldev *dev) +{ + int i; + + if (dev->phy.rev == 1) { + for (i = 0; i < 16; i++) { + b43_ofdmtab_write16(dev, B43_OFDMTAB_WRSSI_R1, + i, 0x0020); + } + } else { + for (i = 0; i < 32; i++) { + b43_ofdmtab_write16(dev, B43_OFDMTAB_WRSSI, + i, 0x0820); + } + } +} + +static void b43_wa_txpuoff_rxpuon(struct b43_wldev *dev) +{ + b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_0F, 2, 15); + b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_0F, 3, 20); +} + +static void b43_wa_altagc(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + if (phy->rev == 1) { + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 0, 254); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 1, 13); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 2, 19); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1_R1, 3, 25); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 0, 0x2710); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 1, 0x9B83); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 2, 0x9B83); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC2, 3, 0x0F8D); + b43_phy_write(dev, B43_PHY_LMS, 4); + } else { + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 0, 254); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 1, 13); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 2, 19); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC1, 3, 25); + } + + b43_phy_maskset(dev, B43_PHY_CCKSHIFTBITS_WA, 0x00FF, 0x5700); + b43_phy_maskset(dev, B43_PHY_OFDM(0x1A), ~0x007F, 0x000F); + b43_phy_maskset(dev, B43_PHY_OFDM(0x1A), ~0x3F80, 0x2B80); + b43_phy_maskset(dev, B43_PHY_ANTWRSETT, 0xF0FF, 0x0300); + b43_radio_set(dev, 0x7A, 0x0008); + b43_phy_maskset(dev, B43_PHY_N1P1GAIN, ~0x000F, 0x0008); + b43_phy_maskset(dev, B43_PHY_P1P2GAIN, ~0x0F00, 0x0600); + b43_phy_maskset(dev, B43_PHY_N1N2GAIN, ~0x0F00, 0x0700); + b43_phy_maskset(dev, B43_PHY_N1P1GAIN, ~0x0F00, 0x0100); + if (phy->rev == 1) { + b43_phy_maskset(dev, B43_PHY_N1N2GAIN, ~0x000F, 0x0007); + } + b43_phy_maskset(dev, B43_PHY_OFDM(0x88), ~0x00FF, 0x001C); + b43_phy_maskset(dev, B43_PHY_OFDM(0x88), ~0x3F00, 0x0200); + b43_phy_maskset(dev, B43_PHY_OFDM(0x96), ~0x00FF, 0x001C); + b43_phy_maskset(dev, B43_PHY_OFDM(0x89), ~0x00FF, 0x0020); + b43_phy_maskset(dev, B43_PHY_OFDM(0x89), ~0x3F00, 0x0200); + b43_phy_maskset(dev, B43_PHY_OFDM(0x82), ~0x00FF, 0x002E); + b43_phy_maskset(dev, B43_PHY_OFDM(0x96), 0x00FF, 0x1A00); + b43_phy_maskset(dev, B43_PHY_OFDM(0x81), ~0x00FF, 0x0028); + b43_phy_maskset(dev, B43_PHY_OFDM(0x81), 0x00FF, 0x2C00); + if (phy->rev == 1) { + b43_phy_write(dev, B43_PHY_PEAK_COUNT, 0x092B); + b43_phy_maskset(dev, B43_PHY_OFDM(0x1B), ~0x001E, 0x0002); + } else { + b43_phy_mask(dev, B43_PHY_OFDM(0x1B), ~0x001E); + b43_phy_write(dev, B43_PHY_OFDM(0x1F), 0x287A); + b43_phy_maskset(dev, B43_PHY_LPFGAINCTL, ~0x000F, 0x0004); + if (phy->rev >= 6) { + b43_phy_write(dev, B43_PHY_OFDM(0x22), 0x287A); + b43_phy_maskset(dev, B43_PHY_LPFGAINCTL, 0x0FFF, 0x3000); + } + } + b43_phy_maskset(dev, B43_PHY_DIVSRCHIDX, 0x8080, 0x7874); + b43_phy_write(dev, B43_PHY_OFDM(0x8E), 0x1C00); + if (phy->rev == 1) { + b43_phy_maskset(dev, B43_PHY_DIVP1P2GAIN, ~0x0F00, 0x0600); + b43_phy_write(dev, B43_PHY_OFDM(0x8B), 0x005E); + b43_phy_maskset(dev, B43_PHY_ANTWRSETT, ~0x00FF, 0x001E); + b43_phy_write(dev, B43_PHY_OFDM(0x8D), 0x0002); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 0, 0); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 1, 7); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 2, 16); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3_R1, 3, 28); + } else { + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 0, 0); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 1, 7); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 2, 16); + b43_ofdmtab_write16(dev, B43_OFDMTAB_AGC3, 3, 28); + } + if (phy->rev >= 6) { + b43_phy_mask(dev, B43_PHY_OFDM(0x26), ~0x0003); + b43_phy_mask(dev, B43_PHY_OFDM(0x26), ~0x1000); + } + b43_phy_read(dev, B43_PHY_VERSION_OFDM); /* Dummy read */ +} + +static void b43_wa_tr_ltov(struct b43_wldev *dev) /* TR Lookup Table Original Values */ +{ + b43_gtab_write(dev, B43_GTAB_ORIGTR, 0, 0x7654); +} + +static void b43_wa_cpll_nonpilot(struct b43_wldev *dev) +{ + b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_11, 0, 0); + b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_11, 1, 0); +} + +static void b43_wa_rssi_adc(struct b43_wldev *dev) +{ + if (dev->phy.analog == 4) + b43_phy_write(dev, 0x00DC, 0x7454); +} + +static void b43_wa_boards_a(struct b43_wldev *dev) +{ + if (dev->dev->board_vendor == SSB_BOARDVENDOR_BCM && + dev->dev->board_type == SSB_BOARD_BU4306 && + dev->dev->board_rev < 0x30) { + b43_phy_write(dev, 0x0010, 0xE000); + b43_phy_write(dev, 0x0013, 0x0140); + b43_phy_write(dev, 0x0014, 0x0280); + } else { + if (dev->dev->board_type == SSB_BOARD_MP4318 && + dev->dev->board_rev < 0x20) { + b43_phy_write(dev, 0x0013, 0x0210); + b43_phy_write(dev, 0x0014, 0x0840); + } else { + b43_phy_write(dev, 0x0013, 0x0140); + b43_phy_write(dev, 0x0014, 0x0280); + } + if (dev->phy.rev <= 4) + b43_phy_write(dev, 0x0010, 0xE000); + else + b43_phy_write(dev, 0x0010, 0x2000); + b43_ofdmtab_write16(dev, B43_OFDMTAB_DC, 1, 0x0039); + b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_APHY, 7, 0x0040); + } +} + +static void b43_wa_boards_g(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + struct b43_phy *phy = &dev->phy; + + if (dev->dev->board_vendor != SSB_BOARDVENDOR_BCM || + dev->dev->board_type != SSB_BOARD_BU4306 || + dev->dev->board_rev != 0x17) { + if (phy->rev < 2) { + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX_R1, 1, 0x0002); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX_R1, 2, 0x0001); + } else { + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 1, 0x0002); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 2, 0x0001); + if ((sprom->boardflags_lo & B43_BFL_EXTLNA) && + (phy->rev >= 7)) { + b43_phy_mask(dev, B43_PHY_EXTG(0x11), 0xF7FF); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0020, 0x0001); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0021, 0x0001); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0022, 0x0001); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0023, 0x0000); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0000, 0x0000); + b43_ofdmtab_write16(dev, B43_OFDMTAB_GAINX, 0x0003, 0x0002); + } + } + } + if (sprom->boardflags_lo & B43_BFL_FEM) { + b43_phy_write(dev, B43_PHY_GTABCTL, 0x3120); + b43_phy_write(dev, B43_PHY_GTABDATA, 0xC480); + } +} + +void b43_wa_all(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + if (phy->type == B43_PHYTYPE_A) { + switch (phy->rev) { + case 2: + b43_wa_papd(dev); + b43_wa_auxclipthr(dev); + b43_wa_afcdac(dev); + b43_wa_txdc_offset(dev); + b43_wa_initgains(dev); + b43_wa_divider(dev); + b43_wa_gt(dev); + b43_wa_rssi_lt(dev); + b43_wa_analog(dev); + b43_wa_dac(dev); + b43_wa_fft(dev); + b43_wa_nft(dev); + b43_wa_rt(dev); + b43_wa_nst(dev); + b43_wa_art(dev); + b43_wa_txlna_gain(dev); + b43_wa_crs_reset(dev); + b43_wa_2060txlna_gain(dev); + b43_wa_lms(dev); + break; + case 3: + b43_wa_papd(dev); + b43_wa_mixedsignal(dev); + b43_wa_rssi_lt(dev); + b43_wa_txdc_offset(dev); + b43_wa_initgains(dev); + b43_wa_dac(dev); + b43_wa_nft(dev); + b43_wa_nst(dev); + b43_wa_msst(dev); + b43_wa_analog(dev); + b43_wa_gt(dev); + b43_wa_txpuoff_rxpuon(dev); + b43_wa_txlna_gain(dev); + break; + case 5: + b43_wa_iqadc(dev); + case 6: + b43_wa_papd(dev); + b43_wa_rssi_lt(dev); + b43_wa_txdc_offset(dev); + b43_wa_initgains(dev); + b43_wa_dac(dev); + b43_wa_nft(dev); + b43_wa_nst(dev); + b43_wa_msst(dev); + b43_wa_analog(dev); + b43_wa_gt(dev); + b43_wa_txpuoff_rxpuon(dev); + b43_wa_txlna_gain(dev); + break; + case 7: + b43_wa_iqadc(dev); + b43_wa_papd(dev); + b43_wa_rssi_lt(dev); + b43_wa_txdc_offset(dev); + b43_wa_initgains(dev); + b43_wa_dac(dev); + b43_wa_nft(dev); + b43_wa_nst(dev); + b43_wa_msst(dev); + b43_wa_analog(dev); + b43_wa_gt(dev); + b43_wa_txpuoff_rxpuon(dev); + b43_wa_txlna_gain(dev); + b43_wa_rssi_adc(dev); + default: + B43_WARN_ON(1); + } + b43_wa_boards_a(dev); + } else if (phy->type == B43_PHYTYPE_G) { + switch (phy->rev) { + case 1://XXX review rev1 + b43_wa_crs_ed(dev); + b43_wa_crs_thr(dev); + b43_wa_crs_blank(dev); + b43_wa_cck_shiftbits(dev); + b43_wa_fft(dev); + b43_wa_nft(dev); + b43_wa_rt(dev); + b43_wa_nst(dev); + b43_wa_art(dev); + b43_wa_wrssi_offset(dev); + b43_wa_altagc(dev); + break; + case 2: + case 6: + case 7: + case 8: + case 9: + b43_wa_tr_ltov(dev); + b43_wa_crs_ed(dev); + b43_wa_rssi_lt(dev); + b43_wa_nft(dev); + b43_wa_nst(dev); + b43_wa_msst(dev); + b43_wa_wrssi_offset(dev); + b43_wa_altagc(dev); + b43_wa_analog(dev); + b43_wa_txpuoff_rxpuon(dev); + break; + default: + B43_WARN_ON(1); + } + b43_wa_boards_g(dev); + } else { /* No N PHY support so far, LP PHY is in phy_lp.c */ + B43_WARN_ON(1); + } + + b43_wa_cpll_nonpilot(dev); +} diff --git a/drivers/net/wireless/broadcom/b43/wa.h b/drivers/net/wireless/broadcom/b43/wa.h new file mode 100644 index 000000000000..e163c5e56e81 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/wa.h @@ -0,0 +1,7 @@ +#ifndef B43_WA_H_ +#define B43_WA_H_ + +void b43_wa_initgains(struct b43_wldev *dev); +void b43_wa_all(struct b43_wldev *dev); + +#endif /* B43_WA_H_ */ diff --git a/drivers/net/wireless/broadcom/b43/xmit.c b/drivers/net/wireless/broadcom/b43/xmit.c new file mode 100644 index 000000000000..426dc13c44cd --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/xmit.c @@ -0,0 +1,947 @@ +/* + + Broadcom B43 wireless driver + + Transmission (TX/RX) related functions. + + Copyright (C) 2005 Martin Langer + Copyright (C) 2005 Stefano Brivio + Copyright (C) 2005, 2006 Michael Buesch + Copyright (C) 2005 Danny van Dyk + Copyright (C) 2005 Andreas Jaggi + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "xmit.h" +#include "phy_common.h" +#include "dma.h" +#include "pio.h" + +static const struct b43_tx_legacy_rate_phy_ctl_entry b43_tx_legacy_rate_phy_ctl[] = { + { B43_CCK_RATE_1MB, 0x0, 0x0 }, + { B43_CCK_RATE_2MB, 0x0, 0x1 }, + { B43_CCK_RATE_5MB, 0x0, 0x2 }, + { B43_CCK_RATE_11MB, 0x0, 0x3 }, + { B43_OFDM_RATE_6MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_BPSK }, + { B43_OFDM_RATE_9MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_BPSK }, + { B43_OFDM_RATE_12MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_QPSK }, + { B43_OFDM_RATE_18MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QPSK }, + { B43_OFDM_RATE_24MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_QAM16 }, + { B43_OFDM_RATE_36MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QAM16 }, + { B43_OFDM_RATE_48MB, B43_TXH_PHY1_CRATE_2_3, B43_TXH_PHY1_MODUL_QAM64 }, + { B43_OFDM_RATE_54MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QAM64 }, +}; + +static const struct b43_tx_legacy_rate_phy_ctl_entry * +b43_tx_legacy_rate_phy_ctl_ent(u8 bitrate) +{ + const struct b43_tx_legacy_rate_phy_ctl_entry *e; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(b43_tx_legacy_rate_phy_ctl); i++) { + e = &(b43_tx_legacy_rate_phy_ctl[i]); + if (e->bitrate == bitrate) + return e; + } + + B43_WARN_ON(1); + return NULL; +} + +/* Extract the bitrate index out of a CCK PLCP header. */ +static int b43_plcp_get_bitrate_idx_cck(struct b43_plcp_hdr6 *plcp) +{ + switch (plcp->raw[0]) { + case 0x0A: + return 0; + case 0x14: + return 1; + case 0x37: + return 2; + case 0x6E: + return 3; + } + return -1; +} + +/* Extract the bitrate index out of an OFDM PLCP header. */ +static int b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool ghz5) +{ + /* For 2 GHz band first OFDM rate is at index 4, see main.c */ + int base = ghz5 ? 0 : 4; + + switch (plcp->raw[0] & 0xF) { + case 0xB: + return base + 0; + case 0xF: + return base + 1; + case 0xA: + return base + 2; + case 0xE: + return base + 3; + case 0x9: + return base + 4; + case 0xD: + return base + 5; + case 0x8: + return base + 6; + case 0xC: + return base + 7; + } + return -1; +} + +u8 b43_plcp_get_ratecode_cck(const u8 bitrate) +{ + switch (bitrate) { + case B43_CCK_RATE_1MB: + return 0x0A; + case B43_CCK_RATE_2MB: + return 0x14; + case B43_CCK_RATE_5MB: + return 0x37; + case B43_CCK_RATE_11MB: + return 0x6E; + } + B43_WARN_ON(1); + return 0; +} + +u8 b43_plcp_get_ratecode_ofdm(const u8 bitrate) +{ + switch (bitrate) { + case B43_OFDM_RATE_6MB: + return 0xB; + case B43_OFDM_RATE_9MB: + return 0xF; + case B43_OFDM_RATE_12MB: + return 0xA; + case B43_OFDM_RATE_18MB: + return 0xE; + case B43_OFDM_RATE_24MB: + return 0x9; + case B43_OFDM_RATE_36MB: + return 0xD; + case B43_OFDM_RATE_48MB: + return 0x8; + case B43_OFDM_RATE_54MB: + return 0xC; + } + B43_WARN_ON(1); + return 0; +} + +void b43_generate_plcp_hdr(struct b43_plcp_hdr4 *plcp, + const u16 octets, const u8 bitrate) +{ + __u8 *raw = plcp->raw; + + if (b43_is_ofdm_rate(bitrate)) { + u32 d; + + d = b43_plcp_get_ratecode_ofdm(bitrate); + B43_WARN_ON(octets & 0xF000); + d |= (octets << 5); + plcp->data = cpu_to_le32(d); + } else { + u32 plen; + + plen = octets * 16 / bitrate; + if ((octets * 16 % bitrate) > 0) { + plen++; + if ((bitrate == B43_CCK_RATE_11MB) + && ((octets * 8 % 11) < 4)) { + raw[1] = 0x84; + } else + raw[1] = 0x04; + } else + raw[1] = 0x04; + plcp->data |= cpu_to_le32(plen << 16); + raw[0] = b43_plcp_get_ratecode_cck(bitrate); + } +} + +/* TODO: verify if needed for SSLPN or LCN */ +static u16 b43_generate_tx_phy_ctl1(struct b43_wldev *dev, u8 bitrate) +{ + const struct b43_phy *phy = &dev->phy; + const struct b43_tx_legacy_rate_phy_ctl_entry *e; + u16 control = 0; + u16 bw; + + if (phy->type == B43_PHYTYPE_LP) + bw = B43_TXH_PHY1_BW_20; + else /* FIXME */ + bw = B43_TXH_PHY1_BW_20; + + if (0) { /* FIXME: MIMO */ + } else if (b43_is_cck_rate(bitrate) && phy->type != B43_PHYTYPE_LP) { + control = bw; + } else { + control = bw; + e = b43_tx_legacy_rate_phy_ctl_ent(bitrate); + if (e) { + control |= e->coding_rate; + control |= e->modulation; + } + control |= B43_TXH_PHY1_MODE_SISO; + } + + return control; +} + +static u8 b43_calc_fallback_rate(u8 bitrate) +{ + switch (bitrate) { + case B43_CCK_RATE_1MB: + return B43_CCK_RATE_1MB; + case B43_CCK_RATE_2MB: + return B43_CCK_RATE_1MB; + case B43_CCK_RATE_5MB: + return B43_CCK_RATE_2MB; + case B43_CCK_RATE_11MB: + return B43_CCK_RATE_5MB; + case B43_OFDM_RATE_6MB: + return B43_CCK_RATE_5MB; + case B43_OFDM_RATE_9MB: + return B43_OFDM_RATE_6MB; + case B43_OFDM_RATE_12MB: + return B43_OFDM_RATE_9MB; + case B43_OFDM_RATE_18MB: + return B43_OFDM_RATE_12MB; + case B43_OFDM_RATE_24MB: + return B43_OFDM_RATE_18MB; + case B43_OFDM_RATE_36MB: + return B43_OFDM_RATE_24MB; + case B43_OFDM_RATE_48MB: + return B43_OFDM_RATE_36MB; + case B43_OFDM_RATE_54MB: + return B43_OFDM_RATE_48MB; + } + B43_WARN_ON(1); + return 0; +} + +/* Generate a TX data header. */ +int b43_generate_txhdr(struct b43_wldev *dev, + u8 *_txhdr, + struct sk_buff *skb_frag, + struct ieee80211_tx_info *info, + u16 cookie) +{ + const unsigned char *fragment_data = skb_frag->data; + unsigned int fragment_len = skb_frag->len; + struct b43_txhdr *txhdr = (struct b43_txhdr *)_txhdr; + const struct b43_phy *phy = &dev->phy; + const struct ieee80211_hdr *wlhdr = + (const struct ieee80211_hdr *)fragment_data; + int use_encryption = !!info->control.hw_key; + __le16 fctl = wlhdr->frame_control; + struct ieee80211_rate *fbrate; + u8 rate, rate_fb; + int rate_ofdm, rate_fb_ofdm; + unsigned int plcp_fragment_len; + u32 mac_ctl = 0; + u16 phy_ctl = 0; + bool fill_phy_ctl1 = (phy->type == B43_PHYTYPE_LP || + phy->type == B43_PHYTYPE_N || + phy->type == B43_PHYTYPE_HT); + u8 extra_ft = 0; + struct ieee80211_rate *txrate; + struct ieee80211_tx_rate *rates; + + memset(txhdr, 0, sizeof(*txhdr)); + + txrate = ieee80211_get_tx_rate(dev->wl->hw, info); + rate = txrate ? txrate->hw_value : B43_CCK_RATE_1MB; + rate_ofdm = b43_is_ofdm_rate(rate); + fbrate = ieee80211_get_alt_retry_rate(dev->wl->hw, info, 0) ? : txrate; + rate_fb = fbrate->hw_value; + rate_fb_ofdm = b43_is_ofdm_rate(rate_fb); + + if (rate_ofdm) + txhdr->phy_rate = b43_plcp_get_ratecode_ofdm(rate); + else + txhdr->phy_rate = b43_plcp_get_ratecode_cck(rate); + txhdr->mac_frame_ctl = wlhdr->frame_control; + memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN); + + /* Calculate duration for fallback rate */ + if ((rate_fb == rate) || + (wlhdr->duration_id & cpu_to_le16(0x8000)) || + (wlhdr->duration_id == cpu_to_le16(0))) { + /* If the fallback rate equals the normal rate or the + * dur_id field contains an AID, CFP magic or 0, + * use the original dur_id field. */ + txhdr->dur_fb = wlhdr->duration_id; + } else { + txhdr->dur_fb = ieee80211_generic_frame_duration( + dev->wl->hw, info->control.vif, info->band, + fragment_len, fbrate); + } + + plcp_fragment_len = fragment_len + FCS_LEN; + if (use_encryption) { + u8 key_idx = info->control.hw_key->hw_key_idx; + struct b43_key *key; + int wlhdr_len; + size_t iv_len; + + B43_WARN_ON(key_idx >= ARRAY_SIZE(dev->key)); + key = &(dev->key[key_idx]); + + if (unlikely(!key->keyconf)) { + /* This key is invalid. This might only happen + * in a short timeframe after machine resume before + * we were able to reconfigure keys. + * Drop this packet completely. Do not transmit it + * unencrypted to avoid leaking information. */ + return -ENOKEY; + } + + /* Hardware appends ICV. */ + plcp_fragment_len += info->control.hw_key->icv_len; + + key_idx = b43_kidx_to_fw(dev, key_idx); + mac_ctl |= (key_idx << B43_TXH_MAC_KEYIDX_SHIFT) & + B43_TXH_MAC_KEYIDX; + mac_ctl |= (key->algorithm << B43_TXH_MAC_KEYALG_SHIFT) & + B43_TXH_MAC_KEYALG; + wlhdr_len = ieee80211_hdrlen(fctl); + if (key->algorithm == B43_SEC_ALGO_TKIP) { + u16 phase1key[5]; + int i; + /* we give the phase1key and iv16 here, the key is stored in + * shm. With that the hardware can do phase 2 and encryption. + */ + ieee80211_get_tkip_p1k(info->control.hw_key, skb_frag, phase1key); + /* phase1key is in host endian. Copy to little-endian txhdr->iv. */ + for (i = 0; i < 5; i++) { + txhdr->iv[i * 2 + 0] = phase1key[i]; + txhdr->iv[i * 2 + 1] = phase1key[i] >> 8; + } + /* iv16 */ + memcpy(txhdr->iv + 10, ((u8 *) wlhdr) + wlhdr_len, 3); + } else { + iv_len = min_t(size_t, info->control.hw_key->iv_len, + ARRAY_SIZE(txhdr->iv)); + memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len); + } + } + switch (dev->fw.hdr_format) { + case B43_FW_HDR_598: + b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_598.plcp), + plcp_fragment_len, rate); + break; + case B43_FW_HDR_351: + b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_351.plcp), + plcp_fragment_len, rate); + break; + case B43_FW_HDR_410: + b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_410.plcp), + plcp_fragment_len, rate); + break; + } + b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->plcp_fb), + plcp_fragment_len, rate_fb); + + /* Extra Frame Types */ + if (rate_fb_ofdm) + extra_ft |= B43_TXH_EFT_FB_OFDM; + else + extra_ft |= B43_TXH_EFT_FB_CCK; + + /* Set channel radio code. Note that the micrcode ORs 0x100 to + * this value before comparing it to the value in SHM, if this + * is a 5Ghz packet. + */ + txhdr->chan_radio_code = phy->channel; + + /* PHY TX Control word */ + if (rate_ofdm) + phy_ctl |= B43_TXH_PHY_ENC_OFDM; + else + phy_ctl |= B43_TXH_PHY_ENC_CCK; + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + phy_ctl |= B43_TXH_PHY_SHORTPRMBL; + + switch (b43_ieee80211_antenna_sanitize(dev, 0)) { + case 0: /* Default */ + phy_ctl |= B43_TXH_PHY_ANT01AUTO; + break; + case 1: /* Antenna 0 */ + phy_ctl |= B43_TXH_PHY_ANT0; + break; + case 2: /* Antenna 1 */ + phy_ctl |= B43_TXH_PHY_ANT1; + break; + case 3: /* Antenna 2 */ + phy_ctl |= B43_TXH_PHY_ANT2; + break; + case 4: /* Antenna 3 */ + phy_ctl |= B43_TXH_PHY_ANT3; + break; + default: + B43_WARN_ON(1); + } + + rates = info->control.rates; + /* MAC control */ + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + mac_ctl |= B43_TXH_MAC_ACK; + /* use hardware sequence counter as the non-TID counter */ + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + mac_ctl |= B43_TXH_MAC_HWSEQ; + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + mac_ctl |= B43_TXH_MAC_STMSDU; + if (!phy->gmode) + mac_ctl |= B43_TXH_MAC_5GHZ; + + /* Overwrite rates[0].count to make the retry calculation + * in the tx status easier. need the actual retry limit to + * detect whether the fallback rate was used. + */ + if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || + (rates[0].count <= dev->wl->hw->conf.long_frame_max_tx_count)) { + rates[0].count = dev->wl->hw->conf.long_frame_max_tx_count; + mac_ctl |= B43_TXH_MAC_LONGFRAME; + } else { + rates[0].count = dev->wl->hw->conf.short_frame_max_tx_count; + } + + /* Generate the RTS or CTS-to-self frame */ + if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || + (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) { + unsigned int len; + struct ieee80211_hdr *uninitialized_var(hdr); + int rts_rate, rts_rate_fb; + int rts_rate_ofdm, rts_rate_fb_ofdm; + struct b43_plcp_hdr6 *uninitialized_var(plcp); + struct ieee80211_rate *rts_cts_rate; + + rts_cts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info); + + rts_rate = rts_cts_rate ? rts_cts_rate->hw_value : B43_CCK_RATE_1MB; + rts_rate_ofdm = b43_is_ofdm_rate(rts_rate); + rts_rate_fb = b43_calc_fallback_rate(rts_rate); + rts_rate_fb_ofdm = b43_is_ofdm_rate(rts_rate_fb); + + if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + struct ieee80211_cts *uninitialized_var(cts); + + switch (dev->fw.hdr_format) { + case B43_FW_HDR_598: + cts = (struct ieee80211_cts *) + (txhdr->format_598.rts_frame); + break; + case B43_FW_HDR_351: + cts = (struct ieee80211_cts *) + (txhdr->format_351.rts_frame); + break; + case B43_FW_HDR_410: + cts = (struct ieee80211_cts *) + (txhdr->format_410.rts_frame); + break; + } + ieee80211_ctstoself_get(dev->wl->hw, info->control.vif, + fragment_data, fragment_len, + info, cts); + mac_ctl |= B43_TXH_MAC_SENDCTS; + len = sizeof(struct ieee80211_cts); + } else { + struct ieee80211_rts *uninitialized_var(rts); + + switch (dev->fw.hdr_format) { + case B43_FW_HDR_598: + rts = (struct ieee80211_rts *) + (txhdr->format_598.rts_frame); + break; + case B43_FW_HDR_351: + rts = (struct ieee80211_rts *) + (txhdr->format_351.rts_frame); + break; + case B43_FW_HDR_410: + rts = (struct ieee80211_rts *) + (txhdr->format_410.rts_frame); + break; + } + ieee80211_rts_get(dev->wl->hw, info->control.vif, + fragment_data, fragment_len, + info, rts); + mac_ctl |= B43_TXH_MAC_SENDRTS; + len = sizeof(struct ieee80211_rts); + } + len += FCS_LEN; + + /* Generate the PLCP headers for the RTS/CTS frame */ + switch (dev->fw.hdr_format) { + case B43_FW_HDR_598: + plcp = &txhdr->format_598.rts_plcp; + break; + case B43_FW_HDR_351: + plcp = &txhdr->format_351.rts_plcp; + break; + case B43_FW_HDR_410: + plcp = &txhdr->format_410.rts_plcp; + break; + } + b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)plcp, + len, rts_rate); + plcp = &txhdr->rts_plcp_fb; + b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)plcp, + len, rts_rate_fb); + + switch (dev->fw.hdr_format) { + case B43_FW_HDR_598: + hdr = (struct ieee80211_hdr *) + (&txhdr->format_598.rts_frame); + break; + case B43_FW_HDR_351: + hdr = (struct ieee80211_hdr *) + (&txhdr->format_351.rts_frame); + break; + case B43_FW_HDR_410: + hdr = (struct ieee80211_hdr *) + (&txhdr->format_410.rts_frame); + break; + } + txhdr->rts_dur_fb = hdr->duration_id; + + if (rts_rate_ofdm) { + extra_ft |= B43_TXH_EFT_RTS_OFDM; + txhdr->phy_rate_rts = + b43_plcp_get_ratecode_ofdm(rts_rate); + } else { + extra_ft |= B43_TXH_EFT_RTS_CCK; + txhdr->phy_rate_rts = + b43_plcp_get_ratecode_cck(rts_rate); + } + if (rts_rate_fb_ofdm) + extra_ft |= B43_TXH_EFT_RTSFB_OFDM; + else + extra_ft |= B43_TXH_EFT_RTSFB_CCK; + + if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS && + fill_phy_ctl1) { + txhdr->phy_ctl1_rts = cpu_to_le16( + b43_generate_tx_phy_ctl1(dev, rts_rate)); + txhdr->phy_ctl1_rts_fb = cpu_to_le16( + b43_generate_tx_phy_ctl1(dev, rts_rate_fb)); + } + } + + /* Magic cookie */ + switch (dev->fw.hdr_format) { + case B43_FW_HDR_598: + txhdr->format_598.cookie = cpu_to_le16(cookie); + break; + case B43_FW_HDR_351: + txhdr->format_351.cookie = cpu_to_le16(cookie); + break; + case B43_FW_HDR_410: + txhdr->format_410.cookie = cpu_to_le16(cookie); + break; + } + + if (fill_phy_ctl1) { + txhdr->phy_ctl1 = + cpu_to_le16(b43_generate_tx_phy_ctl1(dev, rate)); + txhdr->phy_ctl1_fb = + cpu_to_le16(b43_generate_tx_phy_ctl1(dev, rate_fb)); + } + + /* Apply the bitfields */ + txhdr->mac_ctl = cpu_to_le32(mac_ctl); + txhdr->phy_ctl = cpu_to_le16(phy_ctl); + txhdr->extra_ft = extra_ft; + + return 0; +} + +static s8 b43_rssi_postprocess(struct b43_wldev *dev, + u8 in_rssi, int ofdm, + int adjust_2053, int adjust_2050) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_g *gphy = phy->g; + s32 tmp; + + switch (phy->radio_ver) { + case 0x2050: + if (ofdm) { + tmp = in_rssi; + if (tmp > 127) + tmp -= 256; + tmp *= 73; + tmp /= 64; + if (adjust_2050) + tmp += 25; + else + tmp -= 3; + } else { + if (dev->dev->bus_sprom-> + boardflags_lo & B43_BFL_RSSI) { + if (in_rssi > 63) + in_rssi = 63; + B43_WARN_ON(phy->type != B43_PHYTYPE_G); + tmp = gphy->nrssi_lt[in_rssi]; + tmp = 31 - tmp; + tmp *= -131; + tmp /= 128; + tmp -= 57; + } else { + tmp = in_rssi; + tmp = 31 - tmp; + tmp *= -149; + tmp /= 128; + tmp -= 68; + } + if (phy->type == B43_PHYTYPE_G && adjust_2050) + tmp += 25; + } + break; + case 0x2060: + if (in_rssi > 127) + tmp = in_rssi - 256; + else + tmp = in_rssi; + break; + default: + tmp = in_rssi; + tmp -= 11; + tmp *= 103; + tmp /= 64; + if (adjust_2053) + tmp -= 109; + else + tmp -= 83; + } + + return (s8) tmp; +} + +//TODO +#if 0 +static s8 b43_rssinoise_postprocess(struct b43_wldev *dev, u8 in_rssi) +{ + struct b43_phy *phy = &dev->phy; + s8 ret; + + if (phy->type == B43_PHYTYPE_A) { + //TODO: Incomplete specs. + ret = 0; + } else + ret = b43_rssi_postprocess(dev, in_rssi, 0, 1, 1); + + return ret; +} +#endif + +void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) +{ + struct ieee80211_rx_status status; + struct b43_plcp_hdr6 *plcp; + struct ieee80211_hdr *wlhdr; + const struct b43_rxhdr_fw4 *rxhdr = _rxhdr; + __le16 fctl; + u16 phystat0, phystat3; + u16 uninitialized_var(chanstat), uninitialized_var(mactime); + u32 uninitialized_var(macstat); + u16 chanid; + u16 phytype; + int padding, rate_idx; + + memset(&status, 0, sizeof(status)); + + /* Get metadata about the frame from the header. */ + phystat0 = le16_to_cpu(rxhdr->phy_status0); + phystat3 = le16_to_cpu(rxhdr->phy_status3); + switch (dev->fw.hdr_format) { + case B43_FW_HDR_598: + macstat = le32_to_cpu(rxhdr->format_598.mac_status); + mactime = le16_to_cpu(rxhdr->format_598.mac_time); + chanstat = le16_to_cpu(rxhdr->format_598.channel); + break; + case B43_FW_HDR_410: + case B43_FW_HDR_351: + macstat = le32_to_cpu(rxhdr->format_351.mac_status); + mactime = le16_to_cpu(rxhdr->format_351.mac_time); + chanstat = le16_to_cpu(rxhdr->format_351.channel); + break; + } + phytype = chanstat & B43_RX_CHAN_PHYTYPE; + + if (unlikely(macstat & B43_RX_MAC_FCSERR)) { + dev->wl->ieee_stats.dot11FCSErrorCount++; + status.flag |= RX_FLAG_FAILED_FCS_CRC; + } + if (unlikely(phystat0 & (B43_RX_PHYST0_PLCPHCF | B43_RX_PHYST0_PLCPFV))) + status.flag |= RX_FLAG_FAILED_PLCP_CRC; + if (phystat0 & B43_RX_PHYST0_SHORTPRMBL) + status.flag |= RX_FLAG_SHORTPRE; + if (macstat & B43_RX_MAC_DECERR) { + /* Decryption with the given key failed. + * Drop the packet. We also won't be able to decrypt it with + * the key in software. */ + goto drop; + } + + /* Skip PLCP and padding */ + padding = (macstat & B43_RX_MAC_PADDING) ? 2 : 0; + if (unlikely(skb->len < (sizeof(struct b43_plcp_hdr6) + padding))) { + b43dbg(dev->wl, "RX: Packet size underrun (1)\n"); + goto drop; + } + plcp = (struct b43_plcp_hdr6 *)(skb->data + padding); + skb_pull(skb, sizeof(struct b43_plcp_hdr6) + padding); + /* The skb contains the Wireless Header + payload data now */ + if (unlikely(skb->len < (2 + 2 + 6 /*minimum hdr */ + FCS_LEN))) { + b43dbg(dev->wl, "RX: Packet size underrun (2)\n"); + goto drop; + } + wlhdr = (struct ieee80211_hdr *)(skb->data); + fctl = wlhdr->frame_control; + + if (macstat & B43_RX_MAC_DEC) { + unsigned int keyidx; + int wlhdr_len; + + keyidx = ((macstat & B43_RX_MAC_KEYIDX) + >> B43_RX_MAC_KEYIDX_SHIFT); + /* We must adjust the key index here. We want the "physical" + * key index, but the ucode passed it slightly different. + */ + keyidx = b43_kidx_to_raw(dev, keyidx); + B43_WARN_ON(keyidx >= ARRAY_SIZE(dev->key)); + + if (dev->key[keyidx].algorithm != B43_SEC_ALGO_NONE) { + wlhdr_len = ieee80211_hdrlen(fctl); + if (unlikely(skb->len < (wlhdr_len + 3))) { + b43dbg(dev->wl, + "RX: Packet size underrun (3)\n"); + goto drop; + } + status.flag |= RX_FLAG_DECRYPTED; + } + } + + /* Link quality statistics */ + switch (chanstat & B43_RX_CHAN_PHYTYPE) { + case B43_PHYTYPE_HT: + /* TODO: is max the right choice? */ + status.signal = max_t(__s8, + max(rxhdr->phy_ht_power0, rxhdr->phy_ht_power1), + rxhdr->phy_ht_power2); + break; + case B43_PHYTYPE_N: + /* Broadcom has code for min and avg, but always uses max */ + if (rxhdr->power0 == 16 || rxhdr->power0 == 32) + status.signal = max(rxhdr->power1, rxhdr->power2); + else + status.signal = max(rxhdr->power0, rxhdr->power1); + break; + case B43_PHYTYPE_A: + case B43_PHYTYPE_B: + case B43_PHYTYPE_G: + case B43_PHYTYPE_LP: + status.signal = b43_rssi_postprocess(dev, rxhdr->jssi, + (phystat0 & B43_RX_PHYST0_OFDM), + (phystat0 & B43_RX_PHYST0_GAINCTL), + (phystat3 & B43_RX_PHYST3_TRSTATE)); + break; + } + + if (phystat0 & B43_RX_PHYST0_OFDM) + rate_idx = b43_plcp_get_bitrate_idx_ofdm(plcp, + !!(chanstat & B43_RX_CHAN_5GHZ)); + else + rate_idx = b43_plcp_get_bitrate_idx_cck(plcp); + if (unlikely(rate_idx == -1)) { + /* PLCP seems to be corrupted. + * Drop the frame, if we are not interested in corrupted frames. */ + if (!(dev->wl->filter_flags & FIF_PLCPFAIL)) + goto drop; + } + status.rate_idx = rate_idx; + status.antenna = !!(phystat0 & B43_RX_PHYST0_ANT); + + /* + * All frames on monitor interfaces and beacons always need a full + * 64-bit timestamp. Monitor interfaces need it for diagnostic + * purposes and beacons for IBSS merging. + * This code assumes we get to process the packet within 16 bits + * of timestamp, i.e. about 65 milliseconds after the PHY received + * the first symbol. + */ + if (ieee80211_is_beacon(fctl) || dev->wl->radiotap_enabled) { + u16 low_mactime_now; + + b43_tsf_read(dev, &status.mactime); + low_mactime_now = status.mactime; + status.mactime = status.mactime & ~0xFFFFULL; + status.mactime += mactime; + if (low_mactime_now <= mactime) + status.mactime -= 0x10000; + status.flag |= RX_FLAG_MACTIME_START; + } + + chanid = (chanstat & B43_RX_CHAN_ID) >> B43_RX_CHAN_ID_SHIFT; + switch (chanstat & B43_RX_CHAN_PHYTYPE) { + case B43_PHYTYPE_A: + status.band = IEEE80211_BAND_5GHZ; + B43_WARN_ON(1); + /* FIXME: We don't really know which value the "chanid" contains. + * So the following assignment might be wrong. */ + status.freq = + ieee80211_channel_to_frequency(chanid, status.band); + break; + case B43_PHYTYPE_G: + status.band = IEEE80211_BAND_2GHZ; + /* Somewhere between 478.104 and 508.1084 firmware for G-PHY + * has been modified to be compatible with N-PHY and others. + */ + if (dev->fw.rev >= 508) + status.freq = ieee80211_channel_to_frequency(chanid, status.band); + else + status.freq = chanid + 2400; + break; + case B43_PHYTYPE_N: + case B43_PHYTYPE_LP: + case B43_PHYTYPE_HT: + /* chanid is the SHM channel cookie. Which is the plain + * channel number in b43. */ + if (chanstat & B43_RX_CHAN_5GHZ) + status.band = IEEE80211_BAND_5GHZ; + else + status.band = IEEE80211_BAND_2GHZ; + status.freq = + ieee80211_channel_to_frequency(chanid, status.band); + break; + default: + B43_WARN_ON(1); + goto drop; + } + + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + ieee80211_rx_ni(dev->wl->hw, skb); + +#if B43_DEBUG + dev->rx_count++; +#endif + return; +drop: + dev_kfree_skb_any(skb); +} + +void b43_handle_txstatus(struct b43_wldev *dev, + const struct b43_txstatus *status) +{ + b43_debugfs_log_txstat(dev, status); + + if (status->intermediate) + return; + if (status->for_ampdu) + return; + if (!status->acked) + dev->wl->ieee_stats.dot11ACKFailureCount++; + if (status->rts_count) { + if (status->rts_count == 0xF) //FIXME + dev->wl->ieee_stats.dot11RTSFailureCount++; + else + dev->wl->ieee_stats.dot11RTSSuccessCount++; + } + + if (b43_using_pio_transfers(dev)) + b43_pio_handle_txstatus(dev, status); + else + b43_dma_handle_txstatus(dev, status); + + b43_phy_txpower_check(dev, 0); +} + +/* Fill out the mac80211 TXstatus report based on the b43-specific + * txstatus report data. This returns a boolean whether the frame was + * successfully transmitted. */ +bool b43_fill_txstatus_report(struct b43_wldev *dev, + struct ieee80211_tx_info *report, + const struct b43_txstatus *status) +{ + bool frame_success = true; + int retry_limit; + + /* preserve the confiured retry limit before clearing the status + * The xmit function has overwritten the rc's value with the actual + * retry limit done by the hardware */ + retry_limit = report->status.rates[0].count; + ieee80211_tx_info_clear_status(report); + + if (status->acked) { + /* The frame was ACKed. */ + report->flags |= IEEE80211_TX_STAT_ACK; + } else { + /* The frame was not ACKed... */ + if (!(report->flags & IEEE80211_TX_CTL_NO_ACK)) { + /* ...but we expected an ACK. */ + frame_success = false; + } + } + if (status->frame_count == 0) { + /* The frame was not transmitted at all. */ + report->status.rates[0].count = 0; + } else if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) { + /* + * If the short retries (RTS, not data frame) have exceeded + * the limit, the hw will not have tried the selected rate, + * but will have used the fallback rate instead. + * Don't let the rate control count attempts for the selected + * rate in this case, otherwise the statistics will be off. + */ + report->status.rates[0].count = 0; + report->status.rates[1].count = status->frame_count; + } else { + if (status->frame_count > retry_limit) { + report->status.rates[0].count = retry_limit; + report->status.rates[1].count = status->frame_count - + retry_limit; + + } else { + report->status.rates[0].count = status->frame_count; + report->status.rates[1].idx = -1; + } + } + + return frame_success; +} + +/* Stop any TX operation on the device (suspend the hardware queues) */ +void b43_tx_suspend(struct b43_wldev *dev) +{ + if (b43_using_pio_transfers(dev)) + b43_pio_tx_suspend(dev); + else + b43_dma_tx_suspend(dev); +} + +/* Resume any TX operation on the device (resume the hardware queues) */ +void b43_tx_resume(struct b43_wldev *dev) +{ + if (b43_using_pio_transfers(dev)) + b43_pio_tx_resume(dev); + else + b43_dma_tx_resume(dev); +} diff --git a/drivers/net/wireless/broadcom/b43/xmit.h b/drivers/net/wireless/broadcom/b43/xmit.h new file mode 100644 index 000000000000..ba6115308068 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43/xmit.h @@ -0,0 +1,416 @@ +#ifndef B43_XMIT_H_ +#define B43_XMIT_H_ + +#include "main.h" +#include + + +#define _b43_declare_plcp_hdr(size) \ + struct b43_plcp_hdr##size { \ + union { \ + __le32 data; \ + __u8 raw[size]; \ + } __packed; \ + } __packed + +/* struct b43_plcp_hdr4 */ +_b43_declare_plcp_hdr(4); +/* struct b43_plcp_hdr6 */ +_b43_declare_plcp_hdr(6); + +#undef _b43_declare_plcp_hdr + +/* TX header for v4 firmware */ +struct b43_txhdr { + __le32 mac_ctl; /* MAC TX control */ + __le16 mac_frame_ctl; /* Copy of the FrameControl field */ + __le16 tx_fes_time_norm; /* TX FES Time Normal */ + __le16 phy_ctl; /* PHY TX control */ + __le16 phy_ctl1; /* PHY TX control word 1 */ + __le16 phy_ctl1_fb; /* PHY TX control word 1 for fallback rates */ + __le16 phy_ctl1_rts; /* PHY TX control word 1 RTS */ + __le16 phy_ctl1_rts_fb; /* PHY TX control word 1 RTS for fallback rates */ + __u8 phy_rate; /* PHY rate */ + __u8 phy_rate_rts; /* PHY rate for RTS/CTS */ + __u8 extra_ft; /* Extra Frame Types */ + __u8 chan_radio_code; /* Channel Radio Code */ + __u8 iv[16]; /* Encryption IV */ + __u8 tx_receiver[6]; /* TX Frame Receiver address */ + __le16 tx_fes_time_fb; /* TX FES Time Fallback */ + struct b43_plcp_hdr6 rts_plcp_fb; /* RTS fallback PLCP header */ + __le16 rts_dur_fb; /* RTS fallback duration */ + struct b43_plcp_hdr6 plcp_fb; /* Fallback PLCP header */ + __le16 dur_fb; /* Fallback duration */ + __le16 mimo_modelen; /* MIMO mode length */ + __le16 mimo_ratelen_fb; /* MIMO fallback rate length */ + __le32 timeout; /* Timeout */ + + union { + /* Tested with 598.314, 644.1001 and 666.2 */ + struct { + __le16 mimo_antenna; /* MIMO antenna select */ + __le16 preload_size; /* Preload size */ + PAD_BYTES(2); + __le16 cookie; /* TX frame cookie */ + __le16 tx_status; /* TX status */ + __le16 max_n_mpdus; + __le16 max_a_bytes_mrt; + __le16 max_a_bytes_fbr; + __le16 min_m_bytes; + struct b43_plcp_hdr6 rts_plcp; /* RTS PLCP header */ + __u8 rts_frame[16]; /* The RTS frame (if used) */ + PAD_BYTES(2); + struct b43_plcp_hdr6 plcp; /* Main PLCP header */ + } format_598 __packed; + + /* Tested with 410.2160, 478.104 and 508.* */ + struct { + __le16 mimo_antenna; /* MIMO antenna select */ + __le16 preload_size; /* Preload size */ + PAD_BYTES(2); + __le16 cookie; /* TX frame cookie */ + __le16 tx_status; /* TX status */ + struct b43_plcp_hdr6 rts_plcp; /* RTS PLCP header */ + __u8 rts_frame[16]; /* The RTS frame (if used) */ + PAD_BYTES(2); + struct b43_plcp_hdr6 plcp; /* Main PLCP header */ + } format_410 __packed; + + /* Tested with 351.126 */ + struct { + PAD_BYTES(2); + __le16 cookie; /* TX frame cookie */ + __le16 tx_status; /* TX status */ + struct b43_plcp_hdr6 rts_plcp; /* RTS PLCP header */ + __u8 rts_frame[16]; /* The RTS frame (if used) */ + PAD_BYTES(2); + struct b43_plcp_hdr6 plcp; /* Main PLCP header */ + } format_351 __packed; + + } __packed; +} __packed; + +struct b43_tx_legacy_rate_phy_ctl_entry { + u8 bitrate; + u16 coding_rate; + u16 modulation; +}; + +/* MAC TX control */ +#define B43_TXH_MAC_RTS_FB_SHORTPRMBL 0x80000000 /* RTS fallback preamble */ +#define B43_TXH_MAC_RTS_SHORTPRMBL 0x40000000 /* RTS main rate preamble */ +#define B43_TXH_MAC_FB_SHORTPRMBL 0x20000000 /* Main fallback preamble */ +#define B43_TXH_MAC_USEFBR 0x10000000 /* Use fallback rate for this AMPDU */ +#define B43_TXH_MAC_KEYIDX 0x0FF00000 /* Security key index */ +#define B43_TXH_MAC_KEYIDX_SHIFT 20 +#define B43_TXH_MAC_ALT_TXPWR 0x00080000 /* Use alternate txpwr defined at loc. M_ALT_TXPWR_IDX */ +#define B43_TXH_MAC_KEYALG 0x00070000 /* Security key algorithm */ +#define B43_TXH_MAC_KEYALG_SHIFT 16 +#define B43_TXH_MAC_AMIC 0x00008000 /* AMIC */ +#define B43_TXH_MAC_RIFS 0x00004000 /* Use RIFS */ +#define B43_TXH_MAC_LIFETIME 0x00002000 /* Lifetime */ +#define B43_TXH_MAC_FRAMEBURST 0x00001000 /* Frameburst */ +#define B43_TXH_MAC_SENDCTS 0x00000800 /* Send CTS-to-self */ +#define B43_TXH_MAC_AMPDU 0x00000600 /* AMPDU status */ +#define B43_TXH_MAC_AMPDU_MPDU 0x00000000 /* Regular MPDU, not an AMPDU */ +#define B43_TXH_MAC_AMPDU_FIRST 0x00000200 /* First MPDU or AMPDU */ +#define B43_TXH_MAC_AMPDU_INTER 0x00000400 /* Intermediate MPDU or AMPDU */ +#define B43_TXH_MAC_AMPDU_LAST 0x00000600 /* Last (or only) MPDU of AMPDU */ +#define B43_TXH_MAC_40MHZ 0x00000100 /* Use 40 MHz bandwidth */ +#define B43_TXH_MAC_5GHZ 0x00000080 /* 5GHz band */ +#define B43_TXH_MAC_DFCS 0x00000040 /* DFCS */ +#define B43_TXH_MAC_IGNPMQ 0x00000020 /* Ignore PMQ */ +#define B43_TXH_MAC_HWSEQ 0x00000010 /* Use Hardware Sequence Number */ +#define B43_TXH_MAC_STMSDU 0x00000008 /* Start MSDU */ +#define B43_TXH_MAC_SENDRTS 0x00000004 /* Send RTS */ +#define B43_TXH_MAC_LONGFRAME 0x00000002 /* Long frame */ +#define B43_TXH_MAC_ACK 0x00000001 /* Immediate ACK */ + +/* Extra Frame Types */ +#define B43_TXH_EFT_FB 0x03 /* Data frame fallback encoding */ +#define B43_TXH_EFT_FB_CCK 0x00 /* CCK */ +#define B43_TXH_EFT_FB_OFDM 0x01 /* OFDM */ +#define B43_TXH_EFT_FB_HT 0x02 /* HT */ +#define B43_TXH_EFT_FB_VHT 0x03 /* VHT */ +#define B43_TXH_EFT_RTS 0x0C /* RTS/CTS encoding */ +#define B43_TXH_EFT_RTS_CCK 0x00 /* CCK */ +#define B43_TXH_EFT_RTS_OFDM 0x04 /* OFDM */ +#define B43_TXH_EFT_RTS_HT 0x08 /* HT */ +#define B43_TXH_EFT_RTS_VHT 0x0C /* VHT */ +#define B43_TXH_EFT_RTSFB 0x30 /* RTS/CTS fallback encoding */ +#define B43_TXH_EFT_RTSFB_CCK 0x00 /* CCK */ +#define B43_TXH_EFT_RTSFB_OFDM 0x10 /* OFDM */ +#define B43_TXH_EFT_RTSFB_HT 0x20 /* HT */ +#define B43_TXH_EFT_RTSFB_VHT 0x30 /* VHT */ + +/* PHY TX control word */ +#define B43_TXH_PHY_ENC 0x0003 /* Data frame encoding */ +#define B43_TXH_PHY_ENC_CCK 0x0000 /* CCK */ +#define B43_TXH_PHY_ENC_OFDM 0x0001 /* OFDM */ +#define B43_TXH_PHY_ENC_HT 0x0002 /* HT */ +#define B43_TXH_PHY_ENC_VHT 0x0003 /* VHT */ +#define B43_TXH_PHY_SHORTPRMBL 0x0010 /* Use short preamble */ +#define B43_TXH_PHY_ANT 0x03C0 /* Antenna selection */ +#define B43_TXH_PHY_ANT0 0x0000 /* Use antenna 0 */ +#define B43_TXH_PHY_ANT1 0x0040 /* Use antenna 1 */ +#define B43_TXH_PHY_ANT01AUTO 0x00C0 /* Use antenna 0/1 auto */ +#define B43_TXH_PHY_ANT2 0x0100 /* Use antenna 2 */ +#define B43_TXH_PHY_ANT3 0x0200 /* Use antenna 3 */ +#define B43_TXH_PHY_TXPWR 0xFC00 /* TX power */ +#define B43_TXH_PHY_TXPWR_SHIFT 10 + +/* PHY TX control word 1 */ +#define B43_TXH_PHY1_BW 0x0007 /* Bandwidth */ +#define B43_TXH_PHY1_BW_10 0x0000 /* 10 MHz */ +#define B43_TXH_PHY1_BW_10U 0x0001 /* 10 MHz upper */ +#define B43_TXH_PHY1_BW_20 0x0002 /* 20 MHz */ +#define B43_TXH_PHY1_BW_20U 0x0003 /* 20 MHz upper */ +#define B43_TXH_PHY1_BW_40 0x0004 /* 40 MHz */ +#define B43_TXH_PHY1_BW_40DUP 0x0005 /* 40 MHz duplicate */ +#define B43_TXH_PHY1_MODE 0x0038 /* Mode */ +#define B43_TXH_PHY1_MODE_SISO 0x0000 /* SISO */ +#define B43_TXH_PHY1_MODE_CDD 0x0008 /* CDD */ +#define B43_TXH_PHY1_MODE_STBC 0x0010 /* STBC */ +#define B43_TXH_PHY1_MODE_SDM 0x0018 /* SDM */ +#define B43_TXH_PHY1_CRATE 0x0700 /* Coding rate */ +#define B43_TXH_PHY1_CRATE_1_2 0x0000 /* 1/2 */ +#define B43_TXH_PHY1_CRATE_2_3 0x0100 /* 2/3 */ +#define B43_TXH_PHY1_CRATE_3_4 0x0200 /* 3/4 */ +#define B43_TXH_PHY1_CRATE_4_5 0x0300 /* 4/5 */ +#define B43_TXH_PHY1_CRATE_5_6 0x0400 /* 5/6 */ +#define B43_TXH_PHY1_CRATE_7_8 0x0600 /* 7/8 */ +#define B43_TXH_PHY1_MODUL 0x3800 /* Modulation scheme */ +#define B43_TXH_PHY1_MODUL_BPSK 0x0000 /* BPSK */ +#define B43_TXH_PHY1_MODUL_QPSK 0x0800 /* QPSK */ +#define B43_TXH_PHY1_MODUL_QAM16 0x1000 /* QAM16 */ +#define B43_TXH_PHY1_MODUL_QAM64 0x1800 /* QAM64 */ +#define B43_TXH_PHY1_MODUL_QAM256 0x2000 /* QAM256 */ + + +static inline +size_t b43_txhdr_size(struct b43_wldev *dev) +{ + switch (dev->fw.hdr_format) { + case B43_FW_HDR_598: + return 112 + sizeof(struct b43_plcp_hdr6); + case B43_FW_HDR_410: + return 104 + sizeof(struct b43_plcp_hdr6); + case B43_FW_HDR_351: + return 100 + sizeof(struct b43_plcp_hdr6); + } + return 0; +} + + +int b43_generate_txhdr(struct b43_wldev *dev, + u8 * txhdr, + struct sk_buff *skb_frag, + struct ieee80211_tx_info *txctl, u16 cookie); + +/* Transmit Status */ +struct b43_txstatus { + u16 cookie; /* The cookie from the txhdr */ + u16 seq; /* Sequence number */ + u8 phy_stat; /* PHY TX status */ + u8 frame_count; /* Frame transmit count */ + u8 rts_count; /* RTS transmit count */ + u8 supp_reason; /* Suppression reason */ + /* flags */ + u8 pm_indicated; /* PM mode indicated to AP */ + u8 intermediate; /* Intermediate status notification (not final) */ + u8 for_ampdu; /* Status is for an AMPDU (afterburner) */ + u8 acked; /* Wireless ACK received */ +}; + +/* txstatus supp_reason values */ +enum { + B43_TXST_SUPP_NONE, /* Not suppressed */ + B43_TXST_SUPP_PMQ, /* Suppressed due to PMQ entry */ + B43_TXST_SUPP_FLUSH, /* Suppressed due to flush request */ + B43_TXST_SUPP_PREV, /* Previous fragment failed */ + B43_TXST_SUPP_CHAN, /* Channel mismatch */ + B43_TXST_SUPP_LIFE, /* Lifetime expired */ + B43_TXST_SUPP_UNDER, /* Buffer underflow */ + B43_TXST_SUPP_ABNACK, /* Afterburner NACK */ +}; + +/* Receive header for v4 firmware. */ +struct b43_rxhdr_fw4 { + __le16 frame_len; /* Frame length */ + PAD_BYTES(2); + __le16 phy_status0; /* PHY RX Status 0 */ + union { + /* RSSI for A/B/G-PHYs */ + struct { + __u8 jssi; /* PHY RX Status 1: JSSI */ + __u8 sig_qual; /* PHY RX Status 1: Signal Quality */ + } __packed; + + /* RSSI for N-PHYs */ + struct { + __s8 power0; /* PHY RX Status 1: Power 0 */ + __s8 power1; /* PHY RX Status 1: Power 1 */ + } __packed; + } __packed; + union { + /* HT-PHY */ + struct { + PAD_BYTES(1); + __s8 phy_ht_power0; + } __packed; + + /* RSSI for N-PHYs */ + struct { + __s8 power2; + PAD_BYTES(1); + } __packed; + + __le16 phy_status2; /* PHY RX Status 2 */ + } __packed; + union { + /* HT-PHY */ + struct { + __s8 phy_ht_power1; + __s8 phy_ht_power2; + } __packed; + + __le16 phy_status3; /* PHY RX Status 3 */ + } __packed; + union { + /* Tested with 598.314, 644.1001 and 666.2 */ + struct { + __le16 phy_status4; /* PHY RX Status 4 */ + __le16 phy_status5; /* PHY RX Status 5 */ + __le32 mac_status; /* MAC RX status */ + __le16 mac_time; + __le16 channel; + } format_598 __packed; + + /* Tested with 351.126, 410.2160, 478.104 and 508.* */ + struct { + __le32 mac_status; /* MAC RX status */ + __le16 mac_time; + __le16 channel; + } format_351 __packed; + } __packed; +} __packed; + +/* PHY RX Status 0 */ +#define B43_RX_PHYST0_GAINCTL 0x4000 /* Gain Control */ +#define B43_RX_PHYST0_PLCPHCF 0x0200 +#define B43_RX_PHYST0_PLCPFV 0x0100 +#define B43_RX_PHYST0_SHORTPRMBL 0x0080 /* Received with Short Preamble */ +#define B43_RX_PHYST0_LCRS 0x0040 +#define B43_RX_PHYST0_ANT 0x0020 /* Antenna */ +#define B43_RX_PHYST0_UNSRATE 0x0010 +#define B43_RX_PHYST0_CLIP 0x000C +#define B43_RX_PHYST0_CLIP_SHIFT 2 +#define B43_RX_PHYST0_FTYPE 0x0003 /* Frame type */ +#define B43_RX_PHYST0_CCK 0x0000 /* Frame type: CCK */ +#define B43_RX_PHYST0_OFDM 0x0001 /* Frame type: OFDM */ +#define B43_RX_PHYST0_PRE_N 0x0002 /* Pre-standard N-PHY frame */ +#define B43_RX_PHYST0_STD_N 0x0003 /* Standard N-PHY frame */ + +/* PHY RX Status 2 */ +#define B43_RX_PHYST2_LNAG 0xC000 /* LNA Gain */ +#define B43_RX_PHYST2_LNAG_SHIFT 14 +#define B43_RX_PHYST2_PNAG 0x3C00 /* PNA Gain */ +#define B43_RX_PHYST2_PNAG_SHIFT 10 +#define B43_RX_PHYST2_FOFF 0x03FF /* F offset */ + +/* PHY RX Status 3 */ +#define B43_RX_PHYST3_DIGG 0x1800 /* DIG Gain */ +#define B43_RX_PHYST3_DIGG_SHIFT 11 +#define B43_RX_PHYST3_TRSTATE 0x0400 /* TR state */ + +/* MAC RX Status */ +#define B43_RX_MAC_RXST_VALID 0x01000000 /* PHY RXST valid */ +#define B43_RX_MAC_TKIP_MICERR 0x00100000 /* TKIP MIC error */ +#define B43_RX_MAC_TKIP_MICATT 0x00080000 /* TKIP MIC attempted */ +#define B43_RX_MAC_AGGTYPE 0x00060000 /* Aggregation type */ +#define B43_RX_MAC_AGGTYPE_SHIFT 17 +#define B43_RX_MAC_AMSDU 0x00010000 /* A-MSDU mask */ +#define B43_RX_MAC_BEACONSENT 0x00008000 /* Beacon sent flag */ +#define B43_RX_MAC_KEYIDX 0x000007E0 /* Key index */ +#define B43_RX_MAC_KEYIDX_SHIFT 5 +#define B43_RX_MAC_DECERR 0x00000010 /* Decrypt error */ +#define B43_RX_MAC_DEC 0x00000008 /* Decryption attempted */ +#define B43_RX_MAC_PADDING 0x00000004 /* Pad bytes present */ +#define B43_RX_MAC_RESP 0x00000002 /* Response frame transmitted */ +#define B43_RX_MAC_FCSERR 0x00000001 /* FCS error */ + +/* RX channel */ +#define B43_RX_CHAN_40MHZ 0x1000 /* 40 Mhz channel width */ +#define B43_RX_CHAN_5GHZ 0x0800 /* 5 Ghz band */ +#define B43_RX_CHAN_ID 0x07F8 /* Channel ID */ +#define B43_RX_CHAN_ID_SHIFT 3 +#define B43_RX_CHAN_PHYTYPE 0x0007 /* PHY type */ + + +u8 b43_plcp_get_ratecode_cck(const u8 bitrate); +u8 b43_plcp_get_ratecode_ofdm(const u8 bitrate); + +void b43_generate_plcp_hdr(struct b43_plcp_hdr4 *plcp, + const u16 octets, const u8 bitrate); + +void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr); + +void b43_handle_txstatus(struct b43_wldev *dev, + const struct b43_txstatus *status); +bool b43_fill_txstatus_report(struct b43_wldev *dev, + struct ieee80211_tx_info *report, + const struct b43_txstatus *status); + +void b43_tx_suspend(struct b43_wldev *dev); +void b43_tx_resume(struct b43_wldev *dev); + + +/* Helper functions for converting the key-table index from "firmware-format" + * to "raw-format" and back. The firmware API changed for this at some revision. + * We need to account for that here. */ +static inline int b43_new_kidx_api(struct b43_wldev *dev) +{ + /* FIXME: Not sure the change was at rev 351 */ + return (dev->fw.rev >= 351); +} +static inline u8 b43_kidx_to_fw(struct b43_wldev *dev, u8 raw_kidx) +{ + u8 firmware_kidx; + if (b43_new_kidx_api(dev)) { + firmware_kidx = raw_kidx; + } else { + if (raw_kidx >= 4) /* Is per STA key? */ + firmware_kidx = raw_kidx - 4; + else + firmware_kidx = raw_kidx; /* TX default key */ + } + return firmware_kidx; +} +static inline u8 b43_kidx_to_raw(struct b43_wldev *dev, u8 firmware_kidx) +{ + u8 raw_kidx; + if (b43_new_kidx_api(dev)) + raw_kidx = firmware_kidx; + else + raw_kidx = firmware_kidx + 4; /* RX default keys or per STA keys */ + return raw_kidx; +} + +/* struct b43_private_tx_info - TX info private to b43. + * The structure is placed in (struct ieee80211_tx_info *)->rate_driver_data + * + * @bouncebuffer: DMA Bouncebuffer (if used) + */ +struct b43_private_tx_info { + void *bouncebuffer; +}; + +static inline struct b43_private_tx_info * +b43_get_priv_tx_info(struct ieee80211_tx_info *info) +{ + BUILD_BUG_ON(sizeof(struct b43_private_tx_info) > + sizeof(info->rate_driver_data)); + return (struct b43_private_tx_info *)info->rate_driver_data; +} + +#endif /* B43_XMIT_H_ */ -- cgit v1.2.3-59-g8ed1b From 423e3ce336499ddb4e094f40649d12d9820f785b Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 19:49:26 +0200 Subject: b43legacy: move under broadcom vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 1 - drivers/net/wireless/Makefile | 1 - drivers/net/wireless/b43legacy/Kconfig | 104 - drivers/net/wireless/b43legacy/Makefile | 19 - drivers/net/wireless/b43legacy/b43legacy.h | 858 ----- drivers/net/wireless/b43legacy/debugfs.c | 500 --- drivers/net/wireless/b43legacy/debugfs.h | 89 - drivers/net/wireless/b43legacy/dma.c | 1455 ------- drivers/net/wireless/b43legacy/dma.h | 231 -- drivers/net/wireless/b43legacy/ilt.c | 336 -- drivers/net/wireless/b43legacy/ilt.h | 34 - drivers/net/wireless/b43legacy/leds.c | 243 -- drivers/net/wireless/b43legacy/leds.h | 63 - drivers/net/wireless/b43legacy/main.c | 4062 -------------------- drivers/net/wireless/b43legacy/main.h | 127 - drivers/net/wireless/b43legacy/phy.c | 2258 ----------- drivers/net/wireless/b43legacy/phy.h | 209 - drivers/net/wireless/b43legacy/pio.c | 695 ---- drivers/net/wireless/b43legacy/pio.h | 158 - drivers/net/wireless/b43legacy/radio.c | 2143 ----------- drivers/net/wireless/b43legacy/radio.h | 97 - drivers/net/wireless/b43legacy/rfkill.c | 91 - drivers/net/wireless/b43legacy/rfkill.h | 11 - drivers/net/wireless/b43legacy/sysfs.c | 238 -- drivers/net/wireless/b43legacy/sysfs.h | 9 - drivers/net/wireless/b43legacy/xmit.c | 664 ---- drivers/net/wireless/b43legacy/xmit.h | 261 -- drivers/net/wireless/broadcom/Kconfig | 1 + drivers/net/wireless/broadcom/Makefile | 1 + drivers/net/wireless/broadcom/b43legacy/Kconfig | 104 + drivers/net/wireless/broadcom/b43legacy/Makefile | 19 + .../net/wireless/broadcom/b43legacy/b43legacy.h | 858 +++++ drivers/net/wireless/broadcom/b43legacy/debugfs.c | 500 +++ drivers/net/wireless/broadcom/b43legacy/debugfs.h | 89 + drivers/net/wireless/broadcom/b43legacy/dma.c | 1455 +++++++ drivers/net/wireless/broadcom/b43legacy/dma.h | 231 ++ drivers/net/wireless/broadcom/b43legacy/ilt.c | 336 ++ drivers/net/wireless/broadcom/b43legacy/ilt.h | 34 + drivers/net/wireless/broadcom/b43legacy/leds.c | 243 ++ drivers/net/wireless/broadcom/b43legacy/leds.h | 63 + drivers/net/wireless/broadcom/b43legacy/main.c | 4062 ++++++++++++++++++++ drivers/net/wireless/broadcom/b43legacy/main.h | 127 + drivers/net/wireless/broadcom/b43legacy/phy.c | 2258 +++++++++++ drivers/net/wireless/broadcom/b43legacy/phy.h | 209 + drivers/net/wireless/broadcom/b43legacy/pio.c | 695 ++++ drivers/net/wireless/broadcom/b43legacy/pio.h | 158 + drivers/net/wireless/broadcom/b43legacy/radio.c | 2143 +++++++++++ drivers/net/wireless/broadcom/b43legacy/radio.h | 97 + drivers/net/wireless/broadcom/b43legacy/rfkill.c | 91 + drivers/net/wireless/broadcom/b43legacy/rfkill.h | 11 + drivers/net/wireless/broadcom/b43legacy/sysfs.c | 238 ++ drivers/net/wireless/broadcom/b43legacy/sysfs.h | 9 + drivers/net/wireless/broadcom/b43legacy/xmit.c | 664 ++++ drivers/net/wireless/broadcom/b43legacy/xmit.h | 261 ++ 55 files changed, 14958 insertions(+), 14958 deletions(-) delete mode 100644 drivers/net/wireless/b43legacy/Kconfig delete mode 100644 drivers/net/wireless/b43legacy/Makefile delete mode 100644 drivers/net/wireless/b43legacy/b43legacy.h delete mode 100644 drivers/net/wireless/b43legacy/debugfs.c delete mode 100644 drivers/net/wireless/b43legacy/debugfs.h delete mode 100644 drivers/net/wireless/b43legacy/dma.c delete mode 100644 drivers/net/wireless/b43legacy/dma.h delete mode 100644 drivers/net/wireless/b43legacy/ilt.c delete mode 100644 drivers/net/wireless/b43legacy/ilt.h delete mode 100644 drivers/net/wireless/b43legacy/leds.c delete mode 100644 drivers/net/wireless/b43legacy/leds.h delete mode 100644 drivers/net/wireless/b43legacy/main.c delete mode 100644 drivers/net/wireless/b43legacy/main.h delete mode 100644 drivers/net/wireless/b43legacy/phy.c delete mode 100644 drivers/net/wireless/b43legacy/phy.h delete mode 100644 drivers/net/wireless/b43legacy/pio.c delete mode 100644 drivers/net/wireless/b43legacy/pio.h delete mode 100644 drivers/net/wireless/b43legacy/radio.c delete mode 100644 drivers/net/wireless/b43legacy/radio.h delete mode 100644 drivers/net/wireless/b43legacy/rfkill.c delete mode 100644 drivers/net/wireless/b43legacy/rfkill.h delete mode 100644 drivers/net/wireless/b43legacy/sysfs.c delete mode 100644 drivers/net/wireless/b43legacy/sysfs.h delete mode 100644 drivers/net/wireless/b43legacy/xmit.c delete mode 100644 drivers/net/wireless/b43legacy/xmit.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/Kconfig create mode 100644 drivers/net/wireless/broadcom/b43legacy/Makefile create mode 100644 drivers/net/wireless/broadcom/b43legacy/b43legacy.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/debugfs.c create mode 100644 drivers/net/wireless/broadcom/b43legacy/debugfs.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/dma.c create mode 100644 drivers/net/wireless/broadcom/b43legacy/dma.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/ilt.c create mode 100644 drivers/net/wireless/broadcom/b43legacy/ilt.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/leds.c create mode 100644 drivers/net/wireless/broadcom/b43legacy/leds.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/main.c create mode 100644 drivers/net/wireless/broadcom/b43legacy/main.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/phy.c create mode 100644 drivers/net/wireless/broadcom/b43legacy/phy.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/pio.c create mode 100644 drivers/net/wireless/broadcom/b43legacy/pio.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/radio.c create mode 100644 drivers/net/wireless/broadcom/b43legacy/radio.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/rfkill.c create mode 100644 drivers/net/wireless/broadcom/b43legacy/rfkill.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/sysfs.c create mode 100644 drivers/net/wireless/broadcom/b43legacy/sysfs.h create mode 100644 drivers/net/wireless/broadcom/b43legacy/xmit.c create mode 100644 drivers/net/wireless/broadcom/b43legacy/xmit.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 68793777fdb5..dd20c0b1eb02 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2089,7 +2089,7 @@ L: linux-wireless@vger.kernel.org L: b43-dev@lists.infradead.org W: http://wireless.kernel.org/en/users/Drivers/b43 S: Maintained -F: drivers/net/wireless/b43legacy/ +F: drivers/net/wireless/broadcom/b43legacy/ BACKLIGHT CLASS/SUBSYSTEM M: Jingoo Han diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 0c47f2ca2207..e261df26b0dd 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -159,7 +159,6 @@ config MWL8K will be called mwl8k. If unsure, say N. source "drivers/net/wireless/ath/Kconfig" -source "drivers/net/wireless/b43legacy/Kconfig" source "drivers/net/wireless/brcm80211/Kconfig" source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/ipw2x00/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 7907674ad5b4..13ac61737acd 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_HERMES) += orinoco/ obj-$(CONFIG_PRISM54) += prism54/ obj-$(CONFIG_HOSTAP) += hostap/ -obj-$(CONFIG_B43LEGACY) += b43legacy/ obj-$(CONFIG_ZD1211RW) += zd1211rw/ obj-$(CONFIG_WLAN) += realtek/ diff --git a/drivers/net/wireless/b43legacy/Kconfig b/drivers/net/wireless/b43legacy/Kconfig deleted file mode 100644 index 1ffa28835c58..000000000000 --- a/drivers/net/wireless/b43legacy/Kconfig +++ /dev/null @@ -1,104 +0,0 @@ -config B43LEGACY - tristate "Broadcom 43xx-legacy wireless support (mac80211 stack)" - depends on SSB_POSSIBLE && MAC80211 && HAS_DMA - select SSB - select FW_LOADER - ---help--- - b43legacy is a driver for 802.11b devices from Broadcom (BCM4301 and - BCM4303) and early model 802.11g chips (BCM4306 Ver. 2) used in the - Linksys WPC54G V1 PCMCIA devices. - - Newer 802.11g and 802.11a devices need b43. - - It is safe to include both b43 and b43legacy as the underlying glue - layer will automatically load the correct version for your device. - - This driver uses V3 firmware, which must be installed separately using - b43-fwcutter. - - This driver can be built as a module (recommended) that will be - called "b43legacy". If unsure, say M. - -# Auto-select SSB PCI-HOST support, if possible -config B43LEGACY_PCI_AUTOSELECT - bool - depends on B43LEGACY && SSB_PCIHOST_POSSIBLE - select SSB_PCIHOST - select SSB_B43_PCI_BRIDGE - default y - -# Auto-select SSB PCICORE driver, if possible -config B43LEGACY_PCICORE_AUTOSELECT - bool - depends on B43LEGACY && SSB_DRIVER_PCICORE_POSSIBLE - select SSB_DRIVER_PCICORE - default y - -# LED support -# This config option automatically enables b43legacy LEDS support, -# if it's possible. -config B43LEGACY_LEDS - bool - depends on B43LEGACY && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = B43LEGACY) - default y - -# This config option automatically enables b43 HW-RNG support, -# if the HW-RNG core is enabled. -config B43LEGACY_HWRNG - bool - depends on B43LEGACY && (HW_RANDOM = y || HW_RANDOM = B43LEGACY) - default y - -config B43LEGACY_DEBUG - bool "Broadcom 43xx-legacy debugging" - depends on B43LEGACY - default y - ---help--- - Say Y, because this information will help you get the driver running. - This option generates a minimum of log output. - -config B43LEGACY_DMA - bool - depends on B43LEGACY - -config B43LEGACY_PIO - bool - depends on B43LEGACY - -choice - prompt "Broadcom 43xx-legacy data transfer mode" - depends on B43LEGACY - default B43LEGACY_DMA_AND_PIO_MODE - -config B43LEGACY_DMA_AND_PIO_MODE - bool "DMA + PIO" - select B43LEGACY_DMA - select B43LEGACY_PIO - ---help--- - Include both, Direct Memory Access (DMA) and Programmed I/O (PIO) - data transfer modes. The mode actually used is selectable through - the module parameter "pio". With pio=0 as a module parameter, the - default DMA is used, otherwise PIO is used. - - If unsure, choose this option. - -config B43LEGACY_DMA_MODE - bool "DMA (Direct Memory Access) only" - select B43LEGACY_DMA - ---help--- - Only include Direct Memory Access (DMA). - This reduces the size of the driver module, by omitting the PIO code. - -config B43LEGACY_PIO_MODE - bool "PIO (Programmed I/O) only" - select B43LEGACY_PIO - ---help--- - Only include Programmed I/O (PIO). - This reduces the size of the driver module, by omitting the DMA code. - Please note that PIO transfers are slow (compared to DMA). - - Also note that not all devices of the b43legacy series support PIO. - - You should use PIO only if DMA does not work for you. - -endchoice diff --git a/drivers/net/wireless/b43legacy/Makefile b/drivers/net/wireless/b43legacy/Makefile deleted file mode 100644 index 227a77e84362..000000000000 --- a/drivers/net/wireless/b43legacy/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# b43legacy core -b43legacy-y += main.o -b43legacy-y += ilt.o -b43legacy-y += phy.o -b43legacy-y += radio.o -b43legacy-y += sysfs.o -b43legacy-y += xmit.o -# b43 RFKILL button support -b43legacy-y += rfkill.o -# b43legacy LED support -b43legacy-$(CONFIG_B43LEGACY_LEDS) += leds.o -# b43legacy debugging -b43legacy-$(CONFIG_B43LEGACY_DEBUG) += debugfs.o -# b43legacy DMA and PIO -b43legacy-$(CONFIG_B43LEGACY_DMA) += dma.o -b43legacy-$(CONFIG_B43LEGACY_PIO) += pio.o - -obj-$(CONFIG_B43LEGACY) += b43legacy.o - diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h deleted file mode 100644 index 482476fdb1f3..000000000000 --- a/drivers/net/wireless/b43legacy/b43legacy.h +++ /dev/null @@ -1,858 +0,0 @@ -#ifndef B43legacy_H_ -#define B43legacy_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "debugfs.h" -#include "leds.h" -#include "rfkill.h" -#include "phy.h" - - -#define B43legacy_IRQWAIT_MAX_RETRIES 20 - -/* MMIO offsets */ -#define B43legacy_MMIO_DMA0_REASON 0x20 -#define B43legacy_MMIO_DMA0_IRQ_MASK 0x24 -#define B43legacy_MMIO_DMA1_REASON 0x28 -#define B43legacy_MMIO_DMA1_IRQ_MASK 0x2C -#define B43legacy_MMIO_DMA2_REASON 0x30 -#define B43legacy_MMIO_DMA2_IRQ_MASK 0x34 -#define B43legacy_MMIO_DMA3_REASON 0x38 -#define B43legacy_MMIO_DMA3_IRQ_MASK 0x3C -#define B43legacy_MMIO_DMA4_REASON 0x40 -#define B43legacy_MMIO_DMA4_IRQ_MASK 0x44 -#define B43legacy_MMIO_DMA5_REASON 0x48 -#define B43legacy_MMIO_DMA5_IRQ_MASK 0x4C -#define B43legacy_MMIO_MACCTL 0x120 /* MAC control */ -#define B43legacy_MMIO_MACCMD 0x124 /* MAC command */ -#define B43legacy_MMIO_GEN_IRQ_REASON 0x128 -#define B43legacy_MMIO_GEN_IRQ_MASK 0x12C -#define B43legacy_MMIO_RAM_CONTROL 0x130 -#define B43legacy_MMIO_RAM_DATA 0x134 -#define B43legacy_MMIO_PS_STATUS 0x140 -#define B43legacy_MMIO_RADIO_HWENABLED_HI 0x158 -#define B43legacy_MMIO_SHM_CONTROL 0x160 -#define B43legacy_MMIO_SHM_DATA 0x164 -#define B43legacy_MMIO_SHM_DATA_UNALIGNED 0x166 -#define B43legacy_MMIO_XMITSTAT_0 0x170 -#define B43legacy_MMIO_XMITSTAT_1 0x174 -#define B43legacy_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */ -#define B43legacy_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */ -#define B43legacy_MMIO_TSF_CFP_REP 0x188 -#define B43legacy_MMIO_TSF_CFP_START 0x18C -/* 32-bit DMA */ -#define B43legacy_MMIO_DMA32_BASE0 0x200 -#define B43legacy_MMIO_DMA32_BASE1 0x220 -#define B43legacy_MMIO_DMA32_BASE2 0x240 -#define B43legacy_MMIO_DMA32_BASE3 0x260 -#define B43legacy_MMIO_DMA32_BASE4 0x280 -#define B43legacy_MMIO_DMA32_BASE5 0x2A0 -/* 64-bit DMA */ -#define B43legacy_MMIO_DMA64_BASE0 0x200 -#define B43legacy_MMIO_DMA64_BASE1 0x240 -#define B43legacy_MMIO_DMA64_BASE2 0x280 -#define B43legacy_MMIO_DMA64_BASE3 0x2C0 -#define B43legacy_MMIO_DMA64_BASE4 0x300 -#define B43legacy_MMIO_DMA64_BASE5 0x340 -/* PIO */ -#define B43legacy_MMIO_PIO1_BASE 0x300 -#define B43legacy_MMIO_PIO2_BASE 0x310 -#define B43legacy_MMIO_PIO3_BASE 0x320 -#define B43legacy_MMIO_PIO4_BASE 0x330 - -#define B43legacy_MMIO_PHY_VER 0x3E0 -#define B43legacy_MMIO_PHY_RADIO 0x3E2 -#define B43legacy_MMIO_PHY0 0x3E6 -#define B43legacy_MMIO_ANTENNA 0x3E8 -#define B43legacy_MMIO_CHANNEL 0x3F0 -#define B43legacy_MMIO_CHANNEL_EXT 0x3F4 -#define B43legacy_MMIO_RADIO_CONTROL 0x3F6 -#define B43legacy_MMIO_RADIO_DATA_HIGH 0x3F8 -#define B43legacy_MMIO_RADIO_DATA_LOW 0x3FA -#define B43legacy_MMIO_PHY_CONTROL 0x3FC -#define B43legacy_MMIO_PHY_DATA 0x3FE -#define B43legacy_MMIO_MACFILTER_CONTROL 0x420 -#define B43legacy_MMIO_MACFILTER_DATA 0x422 -#define B43legacy_MMIO_RCMTA_COUNT 0x43C /* Receive Match Transmitter Addr */ -#define B43legacy_MMIO_RADIO_HWENABLED_LO 0x49A -#define B43legacy_MMIO_GPIO_CONTROL 0x49C -#define B43legacy_MMIO_GPIO_MASK 0x49E -#define B43legacy_MMIO_TSF_CFP_PRETBTT 0x612 -#define B43legacy_MMIO_TSF_0 0x632 /* core rev < 3 only */ -#define B43legacy_MMIO_TSF_1 0x634 /* core rev < 3 only */ -#define B43legacy_MMIO_TSF_2 0x636 /* core rev < 3 only */ -#define B43legacy_MMIO_TSF_3 0x638 /* core rev < 3 only */ -#define B43legacy_MMIO_RNG 0x65A -#define B43legacy_MMIO_POWERUP_DELAY 0x6A8 - -/* SPROM boardflags_lo values */ -#define B43legacy_BFL_PACTRL 0x0002 -#define B43legacy_BFL_RSSI 0x0008 -#define B43legacy_BFL_EXTLNA 0x1000 - -/* GPIO register offset, in both ChipCommon and PCI core. */ -#define B43legacy_GPIO_CONTROL 0x6c - -/* SHM Routing */ -#define B43legacy_SHM_SHARED 0x0001 -#define B43legacy_SHM_WIRELESS 0x0002 -#define B43legacy_SHM_HW 0x0004 -#define B43legacy_SHM_UCODE 0x0300 - -/* SHM Routing modifiers */ -#define B43legacy_SHM_AUTOINC_R 0x0200 /* Read Auto-increment */ -#define B43legacy_SHM_AUTOINC_W 0x0100 /* Write Auto-increment */ -#define B43legacy_SHM_AUTOINC_RW (B43legacy_SHM_AUTOINC_R | \ - B43legacy_SHM_AUTOINC_W) - -/* Misc SHM_SHARED offsets */ -#define B43legacy_SHM_SH_WLCOREREV 0x0016 /* 802.11 core revision */ -#define B43legacy_SHM_SH_HOSTFLO 0x005E /* Hostflags ucode opts (low) */ -#define B43legacy_SHM_SH_HOSTFHI 0x0060 /* Hostflags ucode opts (high) */ -/* SHM_SHARED crypto engine */ -#define B43legacy_SHM_SH_KEYIDXBLOCK 0x05D4 /* Key index/algorithm block */ -/* SHM_SHARED beacon/AP variables */ -#define B43legacy_SHM_SH_DTIMP 0x0012 /* DTIM period */ -#define B43legacy_SHM_SH_BTL0 0x0018 /* Beacon template length 0 */ -#define B43legacy_SHM_SH_BTL1 0x001A /* Beacon template length 1 */ -#define B43legacy_SHM_SH_BTSFOFF 0x001C /* Beacon TSF offset */ -#define B43legacy_SHM_SH_TIMPOS 0x001E /* TIM position in beacon */ -#define B43legacy_SHM_SH_BEACPHYCTL 0x0054 /* Beacon PHY TX control word */ -/* SHM_SHARED ACK/CTS control */ -#define B43legacy_SHM_SH_ACKCTSPHYCTL 0x0022 /* ACK/CTS PHY control word */ -/* SHM_SHARED probe response variables */ -#define B43legacy_SHM_SH_PRTLEN 0x004A /* Probe Response template length */ -#define B43legacy_SHM_SH_PRMAXTIME 0x0074 /* Probe Response max time */ -#define B43legacy_SHM_SH_PRPHYCTL 0x0188 /* Probe Resp PHY TX control */ -/* SHM_SHARED rate tables */ -#define B43legacy_SHM_SH_OFDMDIRECT 0x0480 /* Pointer to OFDM direct map */ -#define B43legacy_SHM_SH_OFDMBASIC 0x04A0 /* Pointer to OFDM basic rate map */ -#define B43legacy_SHM_SH_CCKDIRECT 0x04C0 /* Pointer to CCK direct map */ -#define B43legacy_SHM_SH_CCKBASIC 0x04E0 /* Pointer to CCK basic rate map */ -/* SHM_SHARED microcode soft registers */ -#define B43legacy_SHM_SH_UCODEREV 0x0000 /* Microcode revision */ -#define B43legacy_SHM_SH_UCODEPATCH 0x0002 /* Microcode patchlevel */ -#define B43legacy_SHM_SH_UCODEDATE 0x0004 /* Microcode date */ -#define B43legacy_SHM_SH_UCODETIME 0x0006 /* Microcode time */ -#define B43legacy_SHM_SH_SPUWKUP 0x0094 /* pre-wakeup for synth PU in us */ -#define B43legacy_SHM_SH_PRETBTT 0x0096 /* pre-TBTT in us */ - -#define B43legacy_UCODEFLAGS_OFFSET 0x005E - -/* Hardware Radio Enable masks */ -#define B43legacy_MMIO_RADIO_HWENABLED_HI_MASK (1 << 16) -#define B43legacy_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4) - -/* HostFlags. See b43legacy_hf_read/write() */ -#define B43legacy_HF_SYMW 0x00000002 /* G-PHY SYM workaround */ -#define B43legacy_HF_GDCW 0x00000020 /* G-PHY DV cancel filter */ -#define B43legacy_HF_OFDMPABOOST 0x00000040 /* Enable PA boost OFDM */ -#define B43legacy_HF_EDCF 0x00000100 /* on if WME/MAC suspended */ - -/* MacFilter offsets. */ -#define B43legacy_MACFILTER_SELF 0x0000 -#define B43legacy_MACFILTER_BSSID 0x0003 -#define B43legacy_MACFILTER_MAC 0x0010 - -/* PHYVersioning */ -#define B43legacy_PHYTYPE_B 0x01 -#define B43legacy_PHYTYPE_G 0x02 - -/* PHYRegisters */ -#define B43legacy_PHY_G_LO_CONTROL 0x0810 -#define B43legacy_PHY_ILT_G_CTRL 0x0472 -#define B43legacy_PHY_ILT_G_DATA1 0x0473 -#define B43legacy_PHY_ILT_G_DATA2 0x0474 -#define B43legacy_PHY_G_PCTL 0x0029 -#define B43legacy_PHY_RADIO_BITFIELD 0x0401 -#define B43legacy_PHY_G_CRS 0x0429 -#define B43legacy_PHY_NRSSILT_CTRL 0x0803 -#define B43legacy_PHY_NRSSILT_DATA 0x0804 - -/* RadioRegisters */ -#define B43legacy_RADIOCTL_ID 0x01 - -/* MAC Control bitfield */ -#define B43legacy_MACCTL_ENABLED 0x00000001 /* MAC Enabled */ -#define B43legacy_MACCTL_PSM_RUN 0x00000002 /* Run Microcode */ -#define B43legacy_MACCTL_PSM_JMP0 0x00000004 /* Microcode jump to 0 */ -#define B43legacy_MACCTL_SHM_ENABLED 0x00000100 /* SHM Enabled */ -#define B43legacy_MACCTL_IHR_ENABLED 0x00000400 /* IHR Region Enabled */ -#define B43legacy_MACCTL_BE 0x00010000 /* Big Endian mode */ -#define B43legacy_MACCTL_INFRA 0x00020000 /* Infrastructure mode */ -#define B43legacy_MACCTL_AP 0x00040000 /* AccessPoint mode */ -#define B43legacy_MACCTL_RADIOLOCK 0x00080000 /* Radio lock */ -#define B43legacy_MACCTL_BEACPROMISC 0x00100000 /* Beacon Promiscuous */ -#define B43legacy_MACCTL_KEEP_BADPLCP 0x00200000 /* Keep bad PLCP frames */ -#define B43legacy_MACCTL_KEEP_CTL 0x00400000 /* Keep control frames */ -#define B43legacy_MACCTL_KEEP_BAD 0x00800000 /* Keep bad frames (FCS) */ -#define B43legacy_MACCTL_PROMISC 0x01000000 /* Promiscuous mode */ -#define B43legacy_MACCTL_HWPS 0x02000000 /* Hardware Power Saving */ -#define B43legacy_MACCTL_AWAKE 0x04000000 /* Device is awake */ -#define B43legacy_MACCTL_TBTTHOLD 0x10000000 /* TBTT Hold */ -#define B43legacy_MACCTL_GMODE 0x80000000 /* G Mode */ - -/* MAC Command bitfield */ -#define B43legacy_MACCMD_BEACON0_VALID 0x00000001 /* Beacon 0 in template RAM is busy/valid */ -#define B43legacy_MACCMD_BEACON1_VALID 0x00000002 /* Beacon 1 in template RAM is busy/valid */ -#define B43legacy_MACCMD_DFQ_VALID 0x00000004 /* Directed frame queue valid (IBSS PS mode, ATIM) */ -#define B43legacy_MACCMD_CCA 0x00000008 /* Clear channel assessment */ -#define B43legacy_MACCMD_BGNOISE 0x00000010 /* Background noise */ - -/* 802.11 core specific TM State Low flags */ -#define B43legacy_TMSLOW_GMODE 0x20000000 /* G Mode Enable */ -#define B43legacy_TMSLOW_PLLREFSEL 0x00200000 /* PLL Freq Ref Select */ -#define B43legacy_TMSLOW_MACPHYCLKEN 0x00100000 /* MAC PHY Clock Ctrl Enbl */ -#define B43legacy_TMSLOW_PHYRESET 0x00080000 /* PHY Reset */ -#define B43legacy_TMSLOW_PHYCLKEN 0x00040000 /* PHY Clock Enable */ - -/* 802.11 core specific TM State High flags */ -#define B43legacy_TMSHIGH_FCLOCK 0x00040000 /* Fast Clock Available */ -#define B43legacy_TMSHIGH_GPHY 0x00010000 /* G-PHY avail (rev >= 5) */ - -#define B43legacy_UCODEFLAG_AUTODIV 0x0001 - -/* Generic-Interrupt reasons. */ -#define B43legacy_IRQ_MAC_SUSPENDED 0x00000001 -#define B43legacy_IRQ_BEACON 0x00000002 -#define B43legacy_IRQ_TBTT_INDI 0x00000004 /* Target Beacon Transmit Time */ -#define B43legacy_IRQ_BEACON_TX_OK 0x00000008 -#define B43legacy_IRQ_BEACON_CANCEL 0x00000010 -#define B43legacy_IRQ_ATIM_END 0x00000020 -#define B43legacy_IRQ_PMQ 0x00000040 -#define B43legacy_IRQ_PIO_WORKAROUND 0x00000100 -#define B43legacy_IRQ_MAC_TXERR 0x00000200 -#define B43legacy_IRQ_PHY_TXERR 0x00000800 -#define B43legacy_IRQ_PMEVENT 0x00001000 -#define B43legacy_IRQ_TIMER0 0x00002000 -#define B43legacy_IRQ_TIMER1 0x00004000 -#define B43legacy_IRQ_DMA 0x00008000 -#define B43legacy_IRQ_TXFIFO_FLUSH_OK 0x00010000 -#define B43legacy_IRQ_CCA_MEASURE_OK 0x00020000 -#define B43legacy_IRQ_NOISESAMPLE_OK 0x00040000 -#define B43legacy_IRQ_UCODE_DEBUG 0x08000000 -#define B43legacy_IRQ_RFKILL 0x10000000 -#define B43legacy_IRQ_TX_OK 0x20000000 -#define B43legacy_IRQ_PHY_G_CHANGED 0x40000000 -#define B43legacy_IRQ_TIMEOUT 0x80000000 - -#define B43legacy_IRQ_ALL 0xFFFFFFFF -#define B43legacy_IRQ_MASKTEMPLATE (B43legacy_IRQ_MAC_SUSPENDED | \ - B43legacy_IRQ_TBTT_INDI | \ - B43legacy_IRQ_ATIM_END | \ - B43legacy_IRQ_PMQ | \ - B43legacy_IRQ_MAC_TXERR | \ - B43legacy_IRQ_PHY_TXERR | \ - B43legacy_IRQ_DMA | \ - B43legacy_IRQ_TXFIFO_FLUSH_OK | \ - B43legacy_IRQ_NOISESAMPLE_OK | \ - B43legacy_IRQ_UCODE_DEBUG | \ - B43legacy_IRQ_RFKILL | \ - B43legacy_IRQ_TX_OK) - -/* Device specific rate values. - * The actual values defined here are (rate_in_mbps * 2). - * Some code depends on this. Don't change it. */ -#define B43legacy_CCK_RATE_1MB 2 -#define B43legacy_CCK_RATE_2MB 4 -#define B43legacy_CCK_RATE_5MB 11 -#define B43legacy_CCK_RATE_11MB 22 -#define B43legacy_OFDM_RATE_6MB 12 -#define B43legacy_OFDM_RATE_9MB 18 -#define B43legacy_OFDM_RATE_12MB 24 -#define B43legacy_OFDM_RATE_18MB 36 -#define B43legacy_OFDM_RATE_24MB 48 -#define B43legacy_OFDM_RATE_36MB 72 -#define B43legacy_OFDM_RATE_48MB 96 -#define B43legacy_OFDM_RATE_54MB 108 -/* Convert a b43legacy rate value to a rate in 100kbps */ -#define B43legacy_RATE_TO_100KBPS(rate) (((rate) * 10) / 2) - - -#define B43legacy_DEFAULT_SHORT_RETRY_LIMIT 7 -#define B43legacy_DEFAULT_LONG_RETRY_LIMIT 4 - -#define B43legacy_PHY_TX_BADNESS_LIMIT 1000 - -/* Max size of a security key */ -#define B43legacy_SEC_KEYSIZE 16 -/* Security algorithms. */ -enum { - B43legacy_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */ - B43legacy_SEC_ALGO_WEP40, - B43legacy_SEC_ALGO_TKIP, - B43legacy_SEC_ALGO_AES, - B43legacy_SEC_ALGO_WEP104, - B43legacy_SEC_ALGO_AES_LEGACY, -}; - -/* Core Information Registers */ -#define B43legacy_CIR_BASE 0xf00 -#define B43legacy_CIR_SBTPSFLAG (B43legacy_CIR_BASE + 0x18) -#define B43legacy_CIR_SBIMSTATE (B43legacy_CIR_BASE + 0x90) -#define B43legacy_CIR_SBINTVEC (B43legacy_CIR_BASE + 0x94) -#define B43legacy_CIR_SBTMSTATELOW (B43legacy_CIR_BASE + 0x98) -#define B43legacy_CIR_SBTMSTATEHIGH (B43legacy_CIR_BASE + 0x9c) -#define B43legacy_CIR_SBIMCONFIGLOW (B43legacy_CIR_BASE + 0xa8) -#define B43legacy_CIR_SB_ID_HI (B43legacy_CIR_BASE + 0xfc) - -/* sbtmstatehigh state flags */ -#define B43legacy_SBTMSTATEHIGH_SERROR 0x00000001 -#define B43legacy_SBTMSTATEHIGH_BUSY 0x00000004 -#define B43legacy_SBTMSTATEHIGH_TIMEOUT 0x00000020 -#define B43legacy_SBTMSTATEHIGH_G_PHY_AVAIL 0x00010000 -#define B43legacy_SBTMSTATEHIGH_COREFLAGS 0x1FFF0000 -#define B43legacy_SBTMSTATEHIGH_DMA64BIT 0x10000000 -#define B43legacy_SBTMSTATEHIGH_GATEDCLK 0x20000000 -#define B43legacy_SBTMSTATEHIGH_BISTFAILED 0x40000000 -#define B43legacy_SBTMSTATEHIGH_BISTCOMPLETE 0x80000000 - -/* sbimstate flags */ -#define B43legacy_SBIMSTATE_IB_ERROR 0x20000 -#define B43legacy_SBIMSTATE_TIMEOUT 0x40000 - -#define PFX KBUILD_MODNAME ": " -#ifdef assert -# undef assert -#endif -#ifdef CONFIG_B43LEGACY_DEBUG -# define B43legacy_WARN_ON(x) WARN_ON(x) -# define B43legacy_BUG_ON(expr) \ - do { \ - if (unlikely((expr))) { \ - printk(KERN_INFO PFX "Test (%s) failed\n", \ - #expr); \ - BUG_ON(expr); \ - } \ - } while (0) -# define B43legacy_DEBUG 1 -#else -/* This will evaluate the argument even if debugging is disabled. */ -static inline bool __b43legacy_warn_on_dummy(bool x) { return x; } -# define B43legacy_WARN_ON(x) __b43legacy_warn_on_dummy(unlikely(!!(x))) -# define B43legacy_BUG_ON(x) do { /* nothing */ } while (0) -# define B43legacy_DEBUG 0 -#endif - - -struct net_device; -struct pci_dev; -struct b43legacy_dmaring; -struct b43legacy_pioqueue; - -/* The firmware file header */ -#define B43legacy_FW_TYPE_UCODE 'u' -#define B43legacy_FW_TYPE_PCM 'p' -#define B43legacy_FW_TYPE_IV 'i' -struct b43legacy_fw_header { - /* File type */ - u8 type; - /* File format version */ - u8 ver; - u8 __padding[2]; - /* Size of the data. For ucode and PCM this is in bytes. - * For IV this is number-of-ivs. */ - __be32 size; -} __packed; - -/* Initial Value file format */ -#define B43legacy_IV_OFFSET_MASK 0x7FFF -#define B43legacy_IV_32BIT 0x8000 -struct b43legacy_iv { - __be16 offset_size; - union { - __be16 d16; - __be32 d32; - } data __packed; -} __packed; - -#define B43legacy_PHYMODE(phytype) (1 << (phytype)) -#define B43legacy_PHYMODE_B B43legacy_PHYMODE \ - ((B43legacy_PHYTYPE_B)) -#define B43legacy_PHYMODE_G B43legacy_PHYMODE \ - ((B43legacy_PHYTYPE_G)) - -/* Value pair to measure the LocalOscillator. */ -struct b43legacy_lopair { - s8 low; - s8 high; - u8 used:1; -}; -#define B43legacy_LO_COUNT (14*4) - -struct b43legacy_phy { - /* Possible PHYMODEs on this PHY */ - u8 possible_phymodes; - /* GMODE bit enabled in MACCTL? */ - bool gmode; - - /* Analog Type */ - u8 analog; - /* B43legacy_PHYTYPE_ */ - u8 type; - /* PHY revision number. */ - u8 rev; - - u16 antenna_diversity; - u16 savedpctlreg; - /* Radio versioning */ - u16 radio_manuf; /* Radio manufacturer */ - u16 radio_ver; /* Radio version */ - u8 calibrated:1; - u8 radio_rev; /* Radio revision */ - - bool dyn_tssi_tbl; /* tssi2dbm is kmalloc()ed. */ - - /* ACI (adjacent channel interference) flags. */ - bool aci_enable; - bool aci_wlan_automatic; - bool aci_hw_rssi; - - /* Radio switched on/off */ - bool radio_on; - struct { - /* Values saved when turning the radio off. - * They are needed when turning it on again. */ - bool valid; - u16 rfover; - u16 rfoverval; - } radio_off_context; - - u16 minlowsig[2]; - u16 minlowsigpos[2]; - - /* LO Measurement Data. - * Use b43legacy_get_lopair() to get a value. - */ - struct b43legacy_lopair *_lo_pairs; - /* TSSI to dBm table in use */ - const s8 *tssi2dbm; - /* idle TSSI value */ - s8 idle_tssi; - /* Target idle TSSI */ - int tgt_idle_tssi; - /* Current idle TSSI */ - int cur_idle_tssi; - - /* LocalOscillator control values. */ - struct b43legacy_txpower_lo_control *lo_control; - /* Values from b43legacy_calc_loopback_gain() */ - s16 max_lb_gain; /* Maximum Loopback gain in hdB */ - s16 trsw_rx_gain; /* TRSW RX gain in hdB */ - s16 lna_lod_gain; /* LNA lod */ - s16 lna_gain; /* LNA */ - s16 pga_gain; /* PGA */ - - /* Desired TX power level (in dBm). This is set by the user and - * adjusted in b43legacy_phy_xmitpower(). */ - u8 power_level; - - /* Values from b43legacy_calc_loopback_gain() */ - u16 loopback_gain[2]; - - /* TX Power control values. */ - /* B/G PHY */ - struct { - /* Current Radio Attenuation for TXpower recalculation. */ - u16 rfatt; - /* Current Baseband Attenuation for TXpower recalculation. */ - u16 bbatt; - /* Current TXpower control value for TXpower recalculation. */ - u16 txctl1; - u16 txctl2; - }; - /* A PHY */ - struct { - u16 txpwr_offset; - }; - - /* Current Interference Mitigation mode */ - int interfmode; - /* Stack of saved values from the Interference Mitigation code. - * Each value in the stack is laid out as follows: - * bit 0-11: offset - * bit 12-15: register ID - * bit 16-32: value - * register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT - */ -#define B43legacy_INTERFSTACK_SIZE 26 - u32 interfstack[B43legacy_INTERFSTACK_SIZE]; - - /* Saved values from the NRSSI Slope calculation */ - s16 nrssi[2]; - s32 nrssislope; - /* In memory nrssi lookup table. */ - s8 nrssi_lt[64]; - - /* current channel */ - u8 channel; - - u16 lofcal; - - u16 initval; - - /* PHY TX errors counter. */ - atomic_t txerr_cnt; - -#if B43legacy_DEBUG - /* Manual TX-power control enabled? */ - bool manual_txpower_control; - /* PHY registers locked by b43legacy_phy_lock()? */ - bool phy_locked; -#endif /* B43legacy_DEBUG */ -}; - -/* Data structures for DMA transmission, per 80211 core. */ -struct b43legacy_dma { - struct b43legacy_dmaring *tx_ring0; - struct b43legacy_dmaring *tx_ring1; - struct b43legacy_dmaring *tx_ring2; - struct b43legacy_dmaring *tx_ring3; - struct b43legacy_dmaring *tx_ring4; - struct b43legacy_dmaring *tx_ring5; - - struct b43legacy_dmaring *rx_ring0; - struct b43legacy_dmaring *rx_ring3; /* only on core.rev < 5 */ - - u32 translation; /* Routing bits */ -}; - -/* Data structures for PIO transmission, per 80211 core. */ -struct b43legacy_pio { - struct b43legacy_pioqueue *queue0; - struct b43legacy_pioqueue *queue1; - struct b43legacy_pioqueue *queue2; - struct b43legacy_pioqueue *queue3; -}; - -/* Context information for a noise calculation (Link Quality). */ -struct b43legacy_noise_calculation { - u8 channel_at_start; - bool calculation_running; - u8 nr_samples; - s8 samples[8][4]; -}; - -struct b43legacy_stats { - u8 link_noise; - /* Store the last TX/RX times here for updating the leds. */ - unsigned long last_tx; - unsigned long last_rx; -}; - -struct b43legacy_key { - void *keyconf; - bool enabled; - u8 algorithm; -}; - -#define B43legacy_QOS_QUEUE_NUM 4 - -struct b43legacy_wldev; - -/* QOS parameters for a queue. */ -struct b43legacy_qos_params { - /* The QOS parameters */ - struct ieee80211_tx_queue_params p; -}; - -/* Data structure for the WLAN parts (802.11 cores) of the b43legacy chip. */ -struct b43legacy_wl { - /* Pointer to the active wireless device on this chip */ - struct b43legacy_wldev *current_dev; - /* Pointer to the ieee80211 hardware data structure */ - struct ieee80211_hw *hw; - - spinlock_t irq_lock; /* locks IRQ */ - struct mutex mutex; /* locks wireless core state */ - spinlock_t leds_lock; /* lock for leds */ - - /* firmware loading work */ - struct work_struct firmware_load; - - /* We can only have one operating interface (802.11 core) - * at a time. General information about this interface follows. - */ - - struct ieee80211_vif *vif; - /* MAC address (can be NULL). */ - u8 mac_addr[ETH_ALEN]; - /* Current BSSID (can be NULL). */ - u8 bssid[ETH_ALEN]; - /* Interface type. (IEEE80211_IF_TYPE_XXX) */ - int if_type; - /* Is the card operating in AP, STA or IBSS mode? */ - bool operating; - /* filter flags */ - unsigned int filter_flags; - /* Stats about the wireless interface */ - struct ieee80211_low_level_stats ieee_stats; - -#ifdef CONFIG_B43LEGACY_HWRNG - struct hwrng rng; - u8 rng_initialized; - char rng_name[30 + 1]; -#endif - - /* List of all wireless devices on this chip */ - struct list_head devlist; - u8 nr_devs; - - bool radiotap_enabled; - bool radio_enabled; - - /* The beacon we are currently using (AP or IBSS mode). - * This beacon stuff is protected by the irq_lock. */ - struct sk_buff *current_beacon; - bool beacon0_uploaded; - bool beacon1_uploaded; - bool beacon_templates_virgin; /* Never wrote the templates? */ - struct work_struct beacon_update_trigger; - /* The current QOS parameters for the 4 queues. */ - struct b43legacy_qos_params qos_params[B43legacy_QOS_QUEUE_NUM]; - - /* Packet transmit work */ - struct work_struct tx_work; - - /* Queue of packets to be transmitted. */ - struct sk_buff_head tx_queue[B43legacy_QOS_QUEUE_NUM]; - - /* Flag that implement the queues stopping. */ - bool tx_queue_stopped[B43legacy_QOS_QUEUE_NUM]; - -}; - -/* Pointers to the firmware data and meta information about it. */ -struct b43legacy_firmware { - /* Microcode */ - const struct firmware *ucode; - /* PCM code */ - const struct firmware *pcm; - /* Initial MMIO values for the firmware */ - const struct firmware *initvals; - /* Initial MMIO values for the firmware, band-specific */ - const struct firmware *initvals_band; - /* Firmware revision */ - u16 rev; - /* Firmware patchlevel */ - u16 patch; -}; - -/* Device (802.11 core) initialization status. */ -enum { - B43legacy_STAT_UNINIT = 0, /* Uninitialized. */ - B43legacy_STAT_INITIALIZED = 1, /* Initialized, not yet started. */ - B43legacy_STAT_STARTED = 2, /* Up and running. */ -}; -#define b43legacy_status(wldev) atomic_read(&(wldev)->__init_status) -#define b43legacy_set_status(wldev, stat) do { \ - atomic_set(&(wldev)->__init_status, (stat)); \ - smp_wmb(); \ - } while (0) - -/* *** --- HOW LOCKING WORKS IN B43legacy --- *** - * - * You should always acquire both, wl->mutex and wl->irq_lock unless: - * - You don't need to acquire wl->irq_lock, if the interface is stopped. - * - You don't need to acquire wl->mutex in the IRQ handler, IRQ tasklet - * and packet TX path (and _ONLY_ there.) - */ - -/* Data structure for one wireless device (802.11 core) */ -struct b43legacy_wldev { - struct ssb_device *dev; - struct b43legacy_wl *wl; - - /* The device initialization status. - * Use b43legacy_status() to query. */ - atomic_t __init_status; - /* Saved init status for handling suspend. */ - int suspend_init_status; - - bool __using_pio; /* Using pio rather than dma. */ - bool bad_frames_preempt;/* Use "Bad Frames Preemption". */ - bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM). */ - bool short_preamble; /* TRUE if using short preamble. */ - bool radio_hw_enable; /* State of radio hardware enable bit. */ - - /* PHY/Radio device. */ - struct b43legacy_phy phy; - union { - /* DMA engines. */ - struct b43legacy_dma dma; - /* PIO engines. */ - struct b43legacy_pio pio; - }; - - /* Various statistics about the physical device. */ - struct b43legacy_stats stats; - - /* The device LEDs. */ - struct b43legacy_led led_tx; - struct b43legacy_led led_rx; - struct b43legacy_led led_assoc; - struct b43legacy_led led_radio; - - /* Reason code of the last interrupt. */ - u32 irq_reason; - u32 dma_reason[6]; - /* The currently active generic-interrupt mask. */ - u32 irq_mask; - /* Link Quality calculation context. */ - struct b43legacy_noise_calculation noisecalc; - /* if > 0 MAC is suspended. if == 0 MAC is enabled. */ - int mac_suspended; - - /* Interrupt Service Routine tasklet (bottom-half) */ - struct tasklet_struct isr_tasklet; - - /* Periodic tasks */ - struct delayed_work periodic_work; - unsigned int periodic_state; - - struct work_struct restart_work; - - /* encryption/decryption */ - u16 ktp; /* Key table pointer */ - u8 max_nr_keys; - struct b43legacy_key key[58]; - - /* Firmware data */ - struct b43legacy_firmware fw; - const struct firmware *fwp; /* needed to pass fw pointer */ - - /* completion struct for firmware loading */ - struct completion fw_load_complete; - - /* Devicelist in struct b43legacy_wl (all 802.11 cores) */ - struct list_head list; - - /* Debugging stuff follows. */ -#ifdef CONFIG_B43LEGACY_DEBUG - struct b43legacy_dfsentry *dfsentry; -#endif -}; - - -static inline -struct b43legacy_wl *hw_to_b43legacy_wl(struct ieee80211_hw *hw) -{ - return hw->priv; -} - -/* Helper function, which returns a boolean. - * TRUE, if PIO is used; FALSE, if DMA is used. - */ -#if defined(CONFIG_B43LEGACY_DMA) && defined(CONFIG_B43LEGACY_PIO) -static inline -int b43legacy_using_pio(struct b43legacy_wldev *dev) -{ - return dev->__using_pio; -} -#elif defined(CONFIG_B43LEGACY_DMA) -static inline -int b43legacy_using_pio(struct b43legacy_wldev *dev) -{ - return 0; -} -#elif defined(CONFIG_B43LEGACY_PIO) -static inline -int b43legacy_using_pio(struct b43legacy_wldev *dev) -{ - return 1; -} -#else -# error "Using neither DMA nor PIO? Confused..." -#endif - - -static inline -struct b43legacy_wldev *dev_to_b43legacy_wldev(struct device *dev) -{ - struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); - return ssb_get_drvdata(ssb_dev); -} - -/* Is the device operating in a specified mode (IEEE80211_IF_TYPE_XXX). */ -static inline -int b43legacy_is_mode(struct b43legacy_wl *wl, int type) -{ - return (wl->operating && - wl->if_type == type); -} - -static inline -bool is_bcm_board_vendor(struct b43legacy_wldev *dev) -{ - return (dev->dev->bus->boardinfo.vendor == PCI_VENDOR_ID_BROADCOM); -} - -static inline -u16 b43legacy_read16(struct b43legacy_wldev *dev, u16 offset) -{ - return ssb_read16(dev->dev, offset); -} - -static inline -void b43legacy_write16(struct b43legacy_wldev *dev, u16 offset, u16 value) -{ - ssb_write16(dev->dev, offset, value); -} - -static inline -u32 b43legacy_read32(struct b43legacy_wldev *dev, u16 offset) -{ - return ssb_read32(dev->dev, offset); -} - -static inline -void b43legacy_write32(struct b43legacy_wldev *dev, u16 offset, u32 value) -{ - ssb_write32(dev->dev, offset, value); -} - -static inline -struct b43legacy_lopair *b43legacy_get_lopair(struct b43legacy_phy *phy, - u16 radio_attenuation, - u16 baseband_attenuation) -{ - return phy->_lo_pairs + (radio_attenuation - + 14 * (baseband_attenuation / 2)); -} - - - -/* Message printing */ -__printf(2, 3) -void b43legacyinfo(struct b43legacy_wl *wl, const char *fmt, ...); -__printf(2, 3) -void b43legacyerr(struct b43legacy_wl *wl, const char *fmt, ...); -__printf(2, 3) -void b43legacywarn(struct b43legacy_wl *wl, const char *fmt, ...); -#if B43legacy_DEBUG -__printf(2, 3) -void b43legacydbg(struct b43legacy_wl *wl, const char *fmt, ...); -#else /* DEBUG */ -# define b43legacydbg(wl, fmt...) do { /* nothing */ } while (0) -#endif /* DEBUG */ - -/* Macros for printing a value in Q5.2 format */ -#define Q52_FMT "%u.%u" -#define Q52_ARG(q52) ((q52) / 4), (((q52) & 3) * 100 / 4) - -#endif /* B43legacy_H_ */ diff --git a/drivers/net/wireless/b43legacy/debugfs.c b/drivers/net/wireless/b43legacy/debugfs.c deleted file mode 100644 index 090910ea259e..000000000000 --- a/drivers/net/wireless/b43legacy/debugfs.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - - Broadcom B43legacy wireless driver - - debugfs driver debugging code - - Copyright (c) 2005-2007 Michael Buesch - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include -#include -#include -#include -#include -#include - -#include "b43legacy.h" -#include "main.h" -#include "debugfs.h" -#include "dma.h" -#include "pio.h" -#include "xmit.h" - - -/* The root directory. */ -static struct dentry *rootdir; - -struct b43legacy_debugfs_fops { - ssize_t (*read)(struct b43legacy_wldev *dev, char *buf, size_t bufsize); - int (*write)(struct b43legacy_wldev *dev, const char *buf, size_t count); - struct file_operations fops; - /* Offset of struct b43legacy_dfs_file in struct b43legacy_dfsentry */ - size_t file_struct_offset; - /* Take wl->irq_lock before calling read/write? */ - bool take_irqlock; -}; - -static inline -struct b43legacy_dfs_file * fops_to_dfs_file(struct b43legacy_wldev *dev, - const struct b43legacy_debugfs_fops *dfops) -{ - void *p; - - p = dev->dfsentry; - p += dfops->file_struct_offset; - - return p; -} - - -#define fappend(fmt, x...) \ - do { \ - if (bufsize - count) \ - count += snprintf(buf + count, \ - bufsize - count, \ - fmt , ##x); \ - else \ - printk(KERN_ERR "b43legacy: fappend overflow\n"); \ - } while (0) - - -/* wl->irq_lock is locked */ -static ssize_t tsf_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) -{ - ssize_t count = 0; - u64 tsf; - - b43legacy_tsf_read(dev, &tsf); - fappend("0x%08x%08x\n", - (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32), - (unsigned int)(tsf & 0xFFFFFFFFULL)); - - return count; -} - -/* wl->irq_lock is locked */ -static int tsf_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count) -{ - u64 tsf; - - if (sscanf(buf, "%llu", (unsigned long long *)(&tsf)) != 1) - return -EINVAL; - b43legacy_tsf_write(dev, tsf); - - return 0; -} - -/* wl->irq_lock is locked */ -static ssize_t ucode_regs_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) -{ - ssize_t count = 0; - int i; - - for (i = 0; i < 64; i++) { - fappend("r%d = 0x%04x\n", i, - b43legacy_shm_read16(dev, B43legacy_SHM_WIRELESS, i)); - } - - return count; -} - -/* wl->irq_lock is locked */ -static ssize_t shm_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) -{ - ssize_t count = 0; - int i; - u16 tmp; - __le16 *le16buf = (__le16 *)buf; - - for (i = 0; i < 0x1000; i++) { - if (bufsize < sizeof(tmp)) - break; - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 2 * i); - le16buf[i] = cpu_to_le16(tmp); - count += sizeof(tmp); - bufsize -= sizeof(tmp); - } - - return count; -} - -static ssize_t txstat_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) -{ - struct b43legacy_txstatus_log *log = &dev->dfsentry->txstatlog; - ssize_t count = 0; - unsigned long flags; - int i, idx; - struct b43legacy_txstatus *stat; - - spin_lock_irqsave(&log->lock, flags); - if (log->end < 0) { - fappend("Nothing transmitted, yet\n"); - goto out_unlock; - } - fappend("b43legacy TX status reports:\n\n" - "index | cookie | seq | phy_stat | frame_count | " - "rts_count | supp_reason | pm_indicated | " - "intermediate | for_ampdu | acked\n" "---\n"); - i = log->end + 1; - idx = 0; - while (1) { - if (i == B43legacy_NR_LOGGED_TXSTATUS) - i = 0; - stat = &(log->log[i]); - if (stat->cookie) { - fappend("%03d | " - "0x%04X | 0x%04X | 0x%02X | " - "0x%X | 0x%X | " - "%u | %u | " - "%u | %u | %u\n", - idx, - stat->cookie, stat->seq, stat->phy_stat, - stat->frame_count, stat->rts_count, - stat->supp_reason, stat->pm_indicated, - stat->intermediate, stat->for_ampdu, - stat->acked); - idx++; - } - if (i == log->end) - break; - i++; - } -out_unlock: - spin_unlock_irqrestore(&log->lock, flags); - - return count; -} - -/* wl->irq_lock is locked */ -static int restart_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count) -{ - int err = 0; - - if (count > 0 && buf[0] == '1') { - b43legacy_controller_restart(dev, "manually restarted"); - } else - err = -EINVAL; - - return err; -} - -#undef fappend - -static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct b43legacy_wldev *dev; - struct b43legacy_debugfs_fops *dfops; - struct b43legacy_dfs_file *dfile; - ssize_t uninitialized_var(ret); - char *buf; - const size_t bufsize = 1024 * 16; /* 16 KiB buffer */ - const size_t buforder = get_order(bufsize); - int err = 0; - - if (!count) - return 0; - dev = file->private_data; - if (!dev) - return -ENODEV; - - mutex_lock(&dev->wl->mutex); - if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) { - err = -ENODEV; - goto out_unlock; - } - - dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops); - if (!dfops->read) { - err = -ENOSYS; - goto out_unlock; - } - dfile = fops_to_dfs_file(dev, dfops); - - if (!dfile->buffer) { - buf = (char *)__get_free_pages(GFP_KERNEL, buforder); - if (!buf) { - err = -ENOMEM; - goto out_unlock; - } - memset(buf, 0, bufsize); - if (dfops->take_irqlock) { - spin_lock_irq(&dev->wl->irq_lock); - ret = dfops->read(dev, buf, bufsize); - spin_unlock_irq(&dev->wl->irq_lock); - } else - ret = dfops->read(dev, buf, bufsize); - if (ret <= 0) { - free_pages((unsigned long)buf, buforder); - err = ret; - goto out_unlock; - } - dfile->data_len = ret; - dfile->buffer = buf; - } - - ret = simple_read_from_buffer(userbuf, count, ppos, - dfile->buffer, - dfile->data_len); - if (*ppos >= dfile->data_len) { - free_pages((unsigned long)dfile->buffer, buforder); - dfile->buffer = NULL; - dfile->data_len = 0; - } -out_unlock: - mutex_unlock(&dev->wl->mutex); - - return err ? err : ret; -} - -static ssize_t b43legacy_debugfs_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct b43legacy_wldev *dev; - struct b43legacy_debugfs_fops *dfops; - char *buf; - int err = 0; - - if (!count) - return 0; - if (count > PAGE_SIZE) - return -E2BIG; - dev = file->private_data; - if (!dev) - return -ENODEV; - - mutex_lock(&dev->wl->mutex); - if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) { - err = -ENODEV; - goto out_unlock; - } - - dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops); - if (!dfops->write) { - err = -ENOSYS; - goto out_unlock; - } - - buf = (char *)get_zeroed_page(GFP_KERNEL); - if (!buf) { - err = -ENOMEM; - goto out_unlock; - } - if (copy_from_user(buf, userbuf, count)) { - err = -EFAULT; - goto out_freepage; - } - if (dfops->take_irqlock) { - spin_lock_irq(&dev->wl->irq_lock); - err = dfops->write(dev, buf, count); - spin_unlock_irq(&dev->wl->irq_lock); - } else - err = dfops->write(dev, buf, count); - if (err) - goto out_freepage; - -out_freepage: - free_page((unsigned long)buf); -out_unlock: - mutex_unlock(&dev->wl->mutex); - - return err ? err : count; -} - - -#define B43legacy_DEBUGFS_FOPS(name, _read, _write, _take_irqlock) \ - static struct b43legacy_debugfs_fops fops_##name = { \ - .read = _read, \ - .write = _write, \ - .fops = { \ - .open = simple_open, \ - .read = b43legacy_debugfs_read, \ - .write = b43legacy_debugfs_write, \ - .llseek = generic_file_llseek, \ - }, \ - .file_struct_offset = offsetof(struct b43legacy_dfsentry, \ - file_##name), \ - .take_irqlock = _take_irqlock, \ - } - -B43legacy_DEBUGFS_FOPS(tsf, tsf_read_file, tsf_write_file, 1); -B43legacy_DEBUGFS_FOPS(ucode_regs, ucode_regs_read_file, NULL, 1); -B43legacy_DEBUGFS_FOPS(shm, shm_read_file, NULL, 1); -B43legacy_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0); -B43legacy_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1); - - -int b43legacy_debug(struct b43legacy_wldev *dev, enum b43legacy_dyndbg feature) -{ - return !!(dev->dfsentry && dev->dfsentry->dyn_debug[feature]); -} - -static void b43legacy_remove_dynamic_debug(struct b43legacy_wldev *dev) -{ - struct b43legacy_dfsentry *e = dev->dfsentry; - int i; - - for (i = 0; i < __B43legacy_NR_DYNDBG; i++) - debugfs_remove(e->dyn_debug_dentries[i]); -} - -static void b43legacy_add_dynamic_debug(struct b43legacy_wldev *dev) -{ - struct b43legacy_dfsentry *e = dev->dfsentry; - struct dentry *d; - -#define add_dyn_dbg(name, id, initstate) do { \ - e->dyn_debug[id] = (initstate); \ - d = debugfs_create_bool(name, 0600, e->subdir, \ - &(e->dyn_debug[id])); \ - if (!IS_ERR(d)) \ - e->dyn_debug_dentries[id] = d; \ - } while (0) - - add_dyn_dbg("debug_xmitpower", B43legacy_DBG_XMITPOWER, false); - add_dyn_dbg("debug_dmaoverflow", B43legacy_DBG_DMAOVERFLOW, false); - add_dyn_dbg("debug_dmaverbose", B43legacy_DBG_DMAVERBOSE, false); - add_dyn_dbg("debug_pwork_fast", B43legacy_DBG_PWORK_FAST, false); - add_dyn_dbg("debug_pwork_stop", B43legacy_DBG_PWORK_STOP, false); - -#undef add_dyn_dbg -} - -void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev) -{ - struct b43legacy_dfsentry *e; - struct b43legacy_txstatus_log *log; - char devdir[16]; - - B43legacy_WARN_ON(!dev); - e = kzalloc(sizeof(*e), GFP_KERNEL); - if (!e) { - b43legacyerr(dev->wl, "debugfs: add device OOM\n"); - return; - } - e->dev = dev; - log = &e->txstatlog; - log->log = kcalloc(B43legacy_NR_LOGGED_TXSTATUS, - sizeof(struct b43legacy_txstatus), GFP_KERNEL); - if (!log->log) { - b43legacyerr(dev->wl, "debugfs: add device txstatus OOM\n"); - kfree(e); - return; - } - log->end = -1; - spin_lock_init(&log->lock); - - dev->dfsentry = e; - - snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy)); - e->subdir = debugfs_create_dir(devdir, rootdir); - if (!e->subdir || IS_ERR(e->subdir)) { - if (e->subdir == ERR_PTR(-ENODEV)) { - b43legacydbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not " - "enabled in kernel config\n"); - } else { - b43legacyerr(dev->wl, "debugfs: cannot create %s directory\n", - devdir); - } - dev->dfsentry = NULL; - kfree(log->log); - kfree(e); - return; - } - -#define ADD_FILE(name, mode) \ - do { \ - struct dentry *d; \ - d = debugfs_create_file(__stringify(name), \ - mode, e->subdir, dev, \ - &fops_##name.fops); \ - e->file_##name.dentry = NULL; \ - if (!IS_ERR(d)) \ - e->file_##name.dentry = d; \ - } while (0) - - - ADD_FILE(tsf, 0600); - ADD_FILE(ucode_regs, 0400); - ADD_FILE(shm, 0400); - ADD_FILE(txstat, 0400); - ADD_FILE(restart, 0200); - -#undef ADD_FILE - - b43legacy_add_dynamic_debug(dev); -} - -void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev) -{ - struct b43legacy_dfsentry *e; - - if (!dev) - return; - e = dev->dfsentry; - if (!e) - return; - b43legacy_remove_dynamic_debug(dev); - - debugfs_remove(e->file_tsf.dentry); - debugfs_remove(e->file_ucode_regs.dentry); - debugfs_remove(e->file_shm.dentry); - debugfs_remove(e->file_txstat.dentry); - debugfs_remove(e->file_restart.dentry); - - debugfs_remove(e->subdir); - kfree(e->txstatlog.log); - kfree(e); -} - -void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev, - const struct b43legacy_txstatus *status) -{ - struct b43legacy_dfsentry *e = dev->dfsentry; - struct b43legacy_txstatus_log *log; - struct b43legacy_txstatus *cur; - int i; - - if (!e) - return; - log = &e->txstatlog; - B43legacy_WARN_ON(!irqs_disabled()); - spin_lock(&log->lock); - i = log->end + 1; - if (i == B43legacy_NR_LOGGED_TXSTATUS) - i = 0; - log->end = i; - cur = &(log->log[i]); - memcpy(cur, status, sizeof(*cur)); - spin_unlock(&log->lock); -} - -void b43legacy_debugfs_init(void) -{ - rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); - if (IS_ERR(rootdir)) - rootdir = NULL; -} - -void b43legacy_debugfs_exit(void) -{ - debugfs_remove(rootdir); -} diff --git a/drivers/net/wireless/b43legacy/debugfs.h b/drivers/net/wireless/b43legacy/debugfs.h deleted file mode 100644 index 9ee32158b947..000000000000 --- a/drivers/net/wireless/b43legacy/debugfs.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef B43legacy_DEBUGFS_H_ -#define B43legacy_DEBUGFS_H_ - -struct b43legacy_wldev; -struct b43legacy_txstatus; - -enum b43legacy_dyndbg { /* Dynamic debugging features */ - B43legacy_DBG_XMITPOWER, - B43legacy_DBG_DMAOVERFLOW, - B43legacy_DBG_DMAVERBOSE, - B43legacy_DBG_PWORK_FAST, - B43legacy_DBG_PWORK_STOP, - __B43legacy_NR_DYNDBG, -}; - - -#ifdef CONFIG_B43LEGACY_DEBUG - -struct dentry; - -#define B43legacy_NR_LOGGED_TXSTATUS 100 - -struct b43legacy_txstatus_log { - struct b43legacy_txstatus *log; - int end; - spinlock_t lock; /* lock for debugging */ -}; - -struct b43legacy_dfs_file { - struct dentry *dentry; - char *buffer; - size_t data_len; -}; - -struct b43legacy_dfsentry { - struct b43legacy_wldev *dev; - struct dentry *subdir; - - struct b43legacy_dfs_file file_tsf; - struct b43legacy_dfs_file file_ucode_regs; - struct b43legacy_dfs_file file_shm; - struct b43legacy_dfs_file file_txstat; - struct b43legacy_dfs_file file_txpower_g; - struct b43legacy_dfs_file file_restart; - struct b43legacy_dfs_file file_loctls; - - struct b43legacy_txstatus_log txstatlog; - - /* Enabled/Disabled list for the dynamic debugging features. */ - bool dyn_debug[__B43legacy_NR_DYNDBG]; - /* Dentries for the dynamic debugging entries. */ - struct dentry *dyn_debug_dentries[__B43legacy_NR_DYNDBG]; -}; - -int b43legacy_debug(struct b43legacy_wldev *dev, - enum b43legacy_dyndbg feature); - -void b43legacy_debugfs_init(void); -void b43legacy_debugfs_exit(void); -void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev); -void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev); -void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev, - const struct b43legacy_txstatus *status); - -#else /* CONFIG_B43LEGACY_DEBUG*/ - -static inline -int b43legacy_debug(struct b43legacy_wldev *dev, - enum b43legacy_dyndbg feature) -{ - return 0; -} - -static inline -void b43legacy_debugfs_init(void) { } -static inline -void b43legacy_debugfs_exit(void) { } -static inline -void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev) { } -static inline -void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev) { } -static inline -void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev, - const struct b43legacy_txstatus *status) - { } - -#endif /* CONFIG_B43LEGACY_DEBUG*/ - -#endif /* B43legacy_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c deleted file mode 100644 index f9dd892b9f27..000000000000 --- a/drivers/net/wireless/b43legacy/dma.c +++ /dev/null @@ -1,1455 +0,0 @@ -/* - - Broadcom B43legacy wireless driver - - DMA ringbuffer and descriptor allocation/management - - Copyright (c) 2005, 2006 Michael Buesch - - Some code in this file is derived from the b44.c driver - Copyright (C) 2002 David S. Miller - Copyright (C) Pekka Pietikainen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43legacy.h" -#include "dma.h" -#include "main.h" -#include "debugfs.h" -#include "xmit.h" - -#include -#include -#include -#include -#include -#include - -/* 32bit DMA ops. */ -static -struct b43legacy_dmadesc32 *op32_idx2desc(struct b43legacy_dmaring *ring, - int slot, - struct b43legacy_dmadesc_meta **meta) -{ - struct b43legacy_dmadesc32 *desc; - - *meta = &(ring->meta[slot]); - desc = ring->descbase; - desc = &(desc[slot]); - - return desc; -} - -static void op32_fill_descriptor(struct b43legacy_dmaring *ring, - struct b43legacy_dmadesc32 *desc, - dma_addr_t dmaaddr, u16 bufsize, - int start, int end, int irq) -{ - struct b43legacy_dmadesc32 *descbase = ring->descbase; - int slot; - u32 ctl; - u32 addr; - u32 addrext; - - slot = (int)(desc - descbase); - B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); - - addr = (u32)(dmaaddr & ~SSB_DMA_TRANSLATION_MASK); - addrext = (u32)(dmaaddr & SSB_DMA_TRANSLATION_MASK) - >> SSB_DMA_TRANSLATION_SHIFT; - addr |= ring->dev->dma.translation; - ctl = (bufsize - ring->frameoffset) - & B43legacy_DMA32_DCTL_BYTECNT; - if (slot == ring->nr_slots - 1) - ctl |= B43legacy_DMA32_DCTL_DTABLEEND; - if (start) - ctl |= B43legacy_DMA32_DCTL_FRAMESTART; - if (end) - ctl |= B43legacy_DMA32_DCTL_FRAMEEND; - if (irq) - ctl |= B43legacy_DMA32_DCTL_IRQ; - ctl |= (addrext << B43legacy_DMA32_DCTL_ADDREXT_SHIFT) - & B43legacy_DMA32_DCTL_ADDREXT_MASK; - - desc->control = cpu_to_le32(ctl); - desc->address = cpu_to_le32(addr); -} - -static void op32_poke_tx(struct b43legacy_dmaring *ring, int slot) -{ - b43legacy_dma_write(ring, B43legacy_DMA32_TXINDEX, - (u32)(slot * sizeof(struct b43legacy_dmadesc32))); -} - -static void op32_tx_suspend(struct b43legacy_dmaring *ring) -{ - b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, - b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL) - | B43legacy_DMA32_TXSUSPEND); -} - -static void op32_tx_resume(struct b43legacy_dmaring *ring) -{ - b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, - b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL) - & ~B43legacy_DMA32_TXSUSPEND); -} - -static int op32_get_current_rxslot(struct b43legacy_dmaring *ring) -{ - u32 val; - - val = b43legacy_dma_read(ring, B43legacy_DMA32_RXSTATUS); - val &= B43legacy_DMA32_RXDPTR; - - return (val / sizeof(struct b43legacy_dmadesc32)); -} - -static void op32_set_current_rxslot(struct b43legacy_dmaring *ring, - int slot) -{ - b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX, - (u32)(slot * sizeof(struct b43legacy_dmadesc32))); -} - -static inline int free_slots(struct b43legacy_dmaring *ring) -{ - return (ring->nr_slots - ring->used_slots); -} - -static inline int next_slot(struct b43legacy_dmaring *ring, int slot) -{ - B43legacy_WARN_ON(!(slot >= -1 && slot <= ring->nr_slots - 1)); - if (slot == ring->nr_slots - 1) - return 0; - return slot + 1; -} - -static inline int prev_slot(struct b43legacy_dmaring *ring, int slot) -{ - B43legacy_WARN_ON(!(slot >= 0 && slot <= ring->nr_slots - 1)); - if (slot == 0) - return ring->nr_slots - 1; - return slot - 1; -} - -#ifdef CONFIG_B43LEGACY_DEBUG -static void update_max_used_slots(struct b43legacy_dmaring *ring, - int current_used_slots) -{ - if (current_used_slots <= ring->max_used_slots) - return; - ring->max_used_slots = current_used_slots; - if (b43legacy_debug(ring->dev, B43legacy_DBG_DMAVERBOSE)) - b43legacydbg(ring->dev->wl, - "max_used_slots increased to %d on %s ring %d\n", - ring->max_used_slots, - ring->tx ? "TX" : "RX", - ring->index); -} -#else -static inline -void update_max_used_slots(struct b43legacy_dmaring *ring, - int current_used_slots) -{ } -#endif /* DEBUG */ - -/* Request a slot for usage. */ -static inline -int request_slot(struct b43legacy_dmaring *ring) -{ - int slot; - - B43legacy_WARN_ON(!ring->tx); - B43legacy_WARN_ON(ring->stopped); - B43legacy_WARN_ON(free_slots(ring) == 0); - - slot = next_slot(ring, ring->current_slot); - ring->current_slot = slot; - ring->used_slots++; - - update_max_used_slots(ring, ring->used_slots); - - return slot; -} - -/* Mac80211-queue to b43legacy-ring mapping */ -static struct b43legacy_dmaring *priority_to_txring( - struct b43legacy_wldev *dev, - int queue_priority) -{ - struct b43legacy_dmaring *ring; - -/*FIXME: For now we always run on TX-ring-1 */ -return dev->dma.tx_ring1; - - /* 0 = highest priority */ - switch (queue_priority) { - default: - B43legacy_WARN_ON(1); - /* fallthrough */ - case 0: - ring = dev->dma.tx_ring3; - break; - case 1: - ring = dev->dma.tx_ring2; - break; - case 2: - ring = dev->dma.tx_ring1; - break; - case 3: - ring = dev->dma.tx_ring0; - break; - case 4: - ring = dev->dma.tx_ring4; - break; - case 5: - ring = dev->dma.tx_ring5; - break; - } - - return ring; -} - -/* Bcm4301-ring to mac80211-queue mapping */ -static inline int txring_to_priority(struct b43legacy_dmaring *ring) -{ - static const u8 idx_to_prio[] = - { 3, 2, 1, 0, 4, 5, }; - -/*FIXME: have only one queue, for now */ -return 0; - - return idx_to_prio[ring->index]; -} - - -static u16 b43legacy_dmacontroller_base(enum b43legacy_dmatype type, - int controller_idx) -{ - static const u16 map32[] = { - B43legacy_MMIO_DMA32_BASE0, - B43legacy_MMIO_DMA32_BASE1, - B43legacy_MMIO_DMA32_BASE2, - B43legacy_MMIO_DMA32_BASE3, - B43legacy_MMIO_DMA32_BASE4, - B43legacy_MMIO_DMA32_BASE5, - }; - - B43legacy_WARN_ON(!(controller_idx >= 0 && - controller_idx < ARRAY_SIZE(map32))); - return map32[controller_idx]; -} - -static inline -dma_addr_t map_descbuffer(struct b43legacy_dmaring *ring, - unsigned char *buf, - size_t len, - int tx) -{ - dma_addr_t dmaaddr; - - if (tx) - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, - DMA_TO_DEVICE); - else - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, - DMA_FROM_DEVICE); - - return dmaaddr; -} - -static inline -void unmap_descbuffer(struct b43legacy_dmaring *ring, - dma_addr_t addr, - size_t len, - int tx) -{ - if (tx) - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, - DMA_TO_DEVICE); - else - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, - DMA_FROM_DEVICE); -} - -static inline -void sync_descbuffer_for_cpu(struct b43legacy_dmaring *ring, - dma_addr_t addr, - size_t len) -{ - B43legacy_WARN_ON(ring->tx); - - dma_sync_single_for_cpu(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); -} - -static inline -void sync_descbuffer_for_device(struct b43legacy_dmaring *ring, - dma_addr_t addr, - size_t len) -{ - B43legacy_WARN_ON(ring->tx); - - dma_sync_single_for_device(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); -} - -static inline -void free_descriptor_buffer(struct b43legacy_dmaring *ring, - struct b43legacy_dmadesc_meta *meta, - int irq_context) -{ - if (meta->skb) { - if (irq_context) - dev_kfree_skb_irq(meta->skb); - else - dev_kfree_skb(meta->skb); - meta->skb = NULL; - } -} - -static int alloc_ringmemory(struct b43legacy_dmaring *ring) -{ - /* GFP flags must match the flags in free_ringmemory()! */ - ring->descbase = dma_zalloc_coherent(ring->dev->dev->dma_dev, - B43legacy_DMA_RINGMEMSIZE, - &(ring->dmabase), GFP_KERNEL); - if (!ring->descbase) - return -ENOMEM; - - return 0; -} - -static void free_ringmemory(struct b43legacy_dmaring *ring) -{ - dma_free_coherent(ring->dev->dev->dma_dev, B43legacy_DMA_RINGMEMSIZE, - ring->descbase, ring->dmabase); -} - -/* Reset the RX DMA channel */ -static int b43legacy_dmacontroller_rx_reset(struct b43legacy_wldev *dev, - u16 mmio_base, - enum b43legacy_dmatype type) -{ - int i; - u32 value; - u16 offset; - - might_sleep(); - - offset = B43legacy_DMA32_RXCTL; - b43legacy_write32(dev, mmio_base + offset, 0); - for (i = 0; i < 10; i++) { - offset = B43legacy_DMA32_RXSTATUS; - value = b43legacy_read32(dev, mmio_base + offset); - value &= B43legacy_DMA32_RXSTATE; - if (value == B43legacy_DMA32_RXSTAT_DISABLED) { - i = -1; - break; - } - msleep(1); - } - if (i != -1) { - b43legacyerr(dev->wl, "DMA RX reset timed out\n"); - return -ENODEV; - } - - return 0; -} - -/* Reset the RX DMA channel */ -static int b43legacy_dmacontroller_tx_reset(struct b43legacy_wldev *dev, - u16 mmio_base, - enum b43legacy_dmatype type) -{ - int i; - u32 value; - u16 offset; - - might_sleep(); - - for (i = 0; i < 10; i++) { - offset = B43legacy_DMA32_TXSTATUS; - value = b43legacy_read32(dev, mmio_base + offset); - value &= B43legacy_DMA32_TXSTATE; - if (value == B43legacy_DMA32_TXSTAT_DISABLED || - value == B43legacy_DMA32_TXSTAT_IDLEWAIT || - value == B43legacy_DMA32_TXSTAT_STOPPED) - break; - msleep(1); - } - offset = B43legacy_DMA32_TXCTL; - b43legacy_write32(dev, mmio_base + offset, 0); - for (i = 0; i < 10; i++) { - offset = B43legacy_DMA32_TXSTATUS; - value = b43legacy_read32(dev, mmio_base + offset); - value &= B43legacy_DMA32_TXSTATE; - if (value == B43legacy_DMA32_TXSTAT_DISABLED) { - i = -1; - break; - } - msleep(1); - } - if (i != -1) { - b43legacyerr(dev->wl, "DMA TX reset timed out\n"); - return -ENODEV; - } - /* ensure the reset is completed. */ - msleep(1); - - return 0; -} - -/* Check if a DMA mapping address is invalid. */ -static bool b43legacy_dma_mapping_error(struct b43legacy_dmaring *ring, - dma_addr_t addr, - size_t buffersize, - bool dma_to_device) -{ - if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr))) - return true; - - switch (ring->type) { - case B43legacy_DMA_30BIT: - if ((u64)addr + buffersize > (1ULL << 30)) - goto address_error; - break; - case B43legacy_DMA_32BIT: - if ((u64)addr + buffersize > (1ULL << 32)) - goto address_error; - break; - } - - /* The address is OK. */ - return false; - -address_error: - /* We can't support this address. Unmap it again. */ - unmap_descbuffer(ring, addr, buffersize, dma_to_device); - - return true; -} - -static int setup_rx_descbuffer(struct b43legacy_dmaring *ring, - struct b43legacy_dmadesc32 *desc, - struct b43legacy_dmadesc_meta *meta, - gfp_t gfp_flags) -{ - struct b43legacy_rxhdr_fw3 *rxhdr; - struct b43legacy_hwtxstatus *txstat; - dma_addr_t dmaaddr; - struct sk_buff *skb; - - B43legacy_WARN_ON(ring->tx); - - skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); - if (unlikely(!skb)) - return -ENOMEM; - dmaaddr = map_descbuffer(ring, skb->data, - ring->rx_buffersize, 0); - if (b43legacy_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { - /* ugh. try to realloc in zone_dma */ - gfp_flags |= GFP_DMA; - - dev_kfree_skb_any(skb); - - skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); - if (unlikely(!skb)) - return -ENOMEM; - dmaaddr = map_descbuffer(ring, skb->data, - ring->rx_buffersize, 0); - } - - if (b43legacy_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { - dev_kfree_skb_any(skb); - return -EIO; - } - - meta->skb = skb; - meta->dmaaddr = dmaaddr; - op32_fill_descriptor(ring, desc, dmaaddr, ring->rx_buffersize, 0, 0, 0); - - rxhdr = (struct b43legacy_rxhdr_fw3 *)(skb->data); - rxhdr->frame_len = 0; - txstat = (struct b43legacy_hwtxstatus *)(skb->data); - txstat->cookie = 0; - - return 0; -} - -/* Allocate the initial descbuffers. - * This is used for an RX ring only. - */ -static int alloc_initial_descbuffers(struct b43legacy_dmaring *ring) -{ - int i; - int err = -ENOMEM; - struct b43legacy_dmadesc32 *desc; - struct b43legacy_dmadesc_meta *meta; - - for (i = 0; i < ring->nr_slots; i++) { - desc = op32_idx2desc(ring, i, &meta); - - err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); - if (err) { - b43legacyerr(ring->dev->wl, - "Failed to allocate initial descbuffers\n"); - goto err_unwind; - } - } - mb(); /* all descbuffer setup before next line */ - ring->used_slots = ring->nr_slots; - err = 0; -out: - return err; - -err_unwind: - for (i--; i >= 0; i--) { - desc = op32_idx2desc(ring, i, &meta); - - unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); - dev_kfree_skb(meta->skb); - } - goto out; -} - -/* Do initial setup of the DMA controller. - * Reset the controller, write the ring busaddress - * and switch the "enable" bit on. - */ -static int dmacontroller_setup(struct b43legacy_dmaring *ring) -{ - int err = 0; - u32 value; - u32 addrext; - u32 trans = ring->dev->dma.translation; - u32 ringbase = (u32)(ring->dmabase); - - if (ring->tx) { - addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) - >> SSB_DMA_TRANSLATION_SHIFT; - value = B43legacy_DMA32_TXENABLE; - value |= (addrext << B43legacy_DMA32_TXADDREXT_SHIFT) - & B43legacy_DMA32_TXADDREXT_MASK; - b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, value); - b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, - (ringbase & ~SSB_DMA_TRANSLATION_MASK) - | trans); - } else { - err = alloc_initial_descbuffers(ring); - if (err) - goto out; - - addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) - >> SSB_DMA_TRANSLATION_SHIFT; - value = (ring->frameoffset << - B43legacy_DMA32_RXFROFF_SHIFT); - value |= B43legacy_DMA32_RXENABLE; - value |= (addrext << B43legacy_DMA32_RXADDREXT_SHIFT) - & B43legacy_DMA32_RXADDREXT_MASK; - b43legacy_dma_write(ring, B43legacy_DMA32_RXCTL, value); - b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, - (ringbase & ~SSB_DMA_TRANSLATION_MASK) - | trans); - b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX, 200); - } - -out: - return err; -} - -/* Shutdown the DMA controller. */ -static void dmacontroller_cleanup(struct b43legacy_dmaring *ring) -{ - if (ring->tx) { - b43legacy_dmacontroller_tx_reset(ring->dev, ring->mmio_base, - ring->type); - b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, 0); - } else { - b43legacy_dmacontroller_rx_reset(ring->dev, ring->mmio_base, - ring->type); - b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, 0); - } -} - -static void free_all_descbuffers(struct b43legacy_dmaring *ring) -{ - struct b43legacy_dmadesc_meta *meta; - int i; - - if (!ring->used_slots) - return; - for (i = 0; i < ring->nr_slots; i++) { - op32_idx2desc(ring, i, &meta); - - if (!meta->skb) { - B43legacy_WARN_ON(!ring->tx); - continue; - } - if (ring->tx) - unmap_descbuffer(ring, meta->dmaaddr, - meta->skb->len, 1); - else - unmap_descbuffer(ring, meta->dmaaddr, - ring->rx_buffersize, 0); - free_descriptor_buffer(ring, meta, 0); - } -} - -static u64 supported_dma_mask(struct b43legacy_wldev *dev) -{ - u32 tmp; - u16 mmio_base; - - mmio_base = b43legacy_dmacontroller_base(0, 0); - b43legacy_write32(dev, - mmio_base + B43legacy_DMA32_TXCTL, - B43legacy_DMA32_TXADDREXT_MASK); - tmp = b43legacy_read32(dev, mmio_base + - B43legacy_DMA32_TXCTL); - if (tmp & B43legacy_DMA32_TXADDREXT_MASK) - return DMA_BIT_MASK(32); - - return DMA_BIT_MASK(30); -} - -static enum b43legacy_dmatype dma_mask_to_engine_type(u64 dmamask) -{ - if (dmamask == DMA_BIT_MASK(30)) - return B43legacy_DMA_30BIT; - if (dmamask == DMA_BIT_MASK(32)) - return B43legacy_DMA_32BIT; - B43legacy_WARN_ON(1); - return B43legacy_DMA_30BIT; -} - -/* Main initialization function. */ -static -struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, - int controller_index, - int for_tx, - enum b43legacy_dmatype type) -{ - struct b43legacy_dmaring *ring; - int err; - int nr_slots; - dma_addr_t dma_test; - - ring = kzalloc(sizeof(*ring), GFP_KERNEL); - if (!ring) - goto out; - ring->type = type; - ring->dev = dev; - - nr_slots = B43legacy_RXRING_SLOTS; - if (for_tx) - nr_slots = B43legacy_TXRING_SLOTS; - - ring->meta = kcalloc(nr_slots, sizeof(struct b43legacy_dmadesc_meta), - GFP_KERNEL); - if (!ring->meta) - goto err_kfree_ring; - if (for_tx) { - ring->txhdr_cache = kcalloc(nr_slots, - sizeof(struct b43legacy_txhdr_fw3), - GFP_KERNEL); - if (!ring->txhdr_cache) - goto err_kfree_meta; - - /* test for ability to dma to txhdr_cache */ - dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, - sizeof(struct b43legacy_txhdr_fw3), - DMA_TO_DEVICE); - - if (b43legacy_dma_mapping_error(ring, dma_test, - sizeof(struct b43legacy_txhdr_fw3), 1)) { - /* ugh realloc */ - kfree(ring->txhdr_cache); - ring->txhdr_cache = kcalloc(nr_slots, - sizeof(struct b43legacy_txhdr_fw3), - GFP_KERNEL | GFP_DMA); - if (!ring->txhdr_cache) - goto err_kfree_meta; - - dma_test = dma_map_single(dev->dev->dma_dev, - ring->txhdr_cache, - sizeof(struct b43legacy_txhdr_fw3), - DMA_TO_DEVICE); - - if (b43legacy_dma_mapping_error(ring, dma_test, - sizeof(struct b43legacy_txhdr_fw3), 1)) - goto err_kfree_txhdr_cache; - } - - dma_unmap_single(dev->dev->dma_dev, dma_test, - sizeof(struct b43legacy_txhdr_fw3), - DMA_TO_DEVICE); - } - - ring->nr_slots = nr_slots; - ring->mmio_base = b43legacy_dmacontroller_base(type, controller_index); - ring->index = controller_index; - if (for_tx) { - ring->tx = true; - ring->current_slot = -1; - } else { - if (ring->index == 0) { - ring->rx_buffersize = B43legacy_DMA0_RX_BUFFERSIZE; - ring->frameoffset = B43legacy_DMA0_RX_FRAMEOFFSET; - } else if (ring->index == 3) { - ring->rx_buffersize = B43legacy_DMA3_RX_BUFFERSIZE; - ring->frameoffset = B43legacy_DMA3_RX_FRAMEOFFSET; - } else - B43legacy_WARN_ON(1); - } -#ifdef CONFIG_B43LEGACY_DEBUG - ring->last_injected_overflow = jiffies; -#endif - - err = alloc_ringmemory(ring); - if (err) - goto err_kfree_txhdr_cache; - err = dmacontroller_setup(ring); - if (err) - goto err_free_ringmemory; - -out: - return ring; - -err_free_ringmemory: - free_ringmemory(ring); -err_kfree_txhdr_cache: - kfree(ring->txhdr_cache); -err_kfree_meta: - kfree(ring->meta); -err_kfree_ring: - kfree(ring); - ring = NULL; - goto out; -} - -/* Main cleanup function. */ -static void b43legacy_destroy_dmaring(struct b43legacy_dmaring *ring) -{ - if (!ring) - return; - - b43legacydbg(ring->dev->wl, "DMA-%u 0x%04X (%s) max used slots:" - " %d/%d\n", (unsigned int)(ring->type), ring->mmio_base, - (ring->tx) ? "TX" : "RX", ring->max_used_slots, - ring->nr_slots); - /* Device IRQs are disabled prior entering this function, - * so no need to take care of concurrency with rx handler stuff. - */ - dmacontroller_cleanup(ring); - free_all_descbuffers(ring); - free_ringmemory(ring); - - kfree(ring->txhdr_cache); - kfree(ring->meta); - kfree(ring); -} - -void b43legacy_dma_free(struct b43legacy_wldev *dev) -{ - struct b43legacy_dma *dma; - - if (b43legacy_using_pio(dev)) - return; - dma = &dev->dma; - - b43legacy_destroy_dmaring(dma->rx_ring3); - dma->rx_ring3 = NULL; - b43legacy_destroy_dmaring(dma->rx_ring0); - dma->rx_ring0 = NULL; - - b43legacy_destroy_dmaring(dma->tx_ring5); - dma->tx_ring5 = NULL; - b43legacy_destroy_dmaring(dma->tx_ring4); - dma->tx_ring4 = NULL; - b43legacy_destroy_dmaring(dma->tx_ring3); - dma->tx_ring3 = NULL; - b43legacy_destroy_dmaring(dma->tx_ring2); - dma->tx_ring2 = NULL; - b43legacy_destroy_dmaring(dma->tx_ring1); - dma->tx_ring1 = NULL; - b43legacy_destroy_dmaring(dma->tx_ring0); - dma->tx_ring0 = NULL; -} - -static int b43legacy_dma_set_mask(struct b43legacy_wldev *dev, u64 mask) -{ - u64 orig_mask = mask; - bool fallback = false; - int err; - - /* Try to set the DMA mask. If it fails, try falling back to a - * lower mask, as we can always also support a lower one. */ - while (1) { - err = dma_set_mask_and_coherent(dev->dev->dma_dev, mask); - if (!err) - break; - if (mask == DMA_BIT_MASK(64)) { - mask = DMA_BIT_MASK(32); - fallback = true; - continue; - } - if (mask == DMA_BIT_MASK(32)) { - mask = DMA_BIT_MASK(30); - fallback = true; - continue; - } - b43legacyerr(dev->wl, "The machine/kernel does not support " - "the required %u-bit DMA mask\n", - (unsigned int)dma_mask_to_engine_type(orig_mask)); - return -EOPNOTSUPP; - } - if (fallback) { - b43legacyinfo(dev->wl, "DMA mask fallback from %u-bit to %u-" - "bit\n", - (unsigned int)dma_mask_to_engine_type(orig_mask), - (unsigned int)dma_mask_to_engine_type(mask)); - } - - return 0; -} - -int b43legacy_dma_init(struct b43legacy_wldev *dev) -{ - struct b43legacy_dma *dma = &dev->dma; - struct b43legacy_dmaring *ring; - int err; - u64 dmamask; - enum b43legacy_dmatype type; - - dmamask = supported_dma_mask(dev); - type = dma_mask_to_engine_type(dmamask); - err = b43legacy_dma_set_mask(dev, dmamask); - if (err) { -#ifdef CONFIG_B43LEGACY_PIO - b43legacywarn(dev->wl, "DMA for this device not supported. " - "Falling back to PIO\n"); - dev->__using_pio = true; - return -EAGAIN; -#else - b43legacyerr(dev->wl, "DMA for this device not supported and " - "no PIO support compiled in\n"); - return -EOPNOTSUPP; -#endif - } - dma->translation = ssb_dma_translation(dev->dev); - - err = -ENOMEM; - /* setup TX DMA channels. */ - ring = b43legacy_setup_dmaring(dev, 0, 1, type); - if (!ring) - goto out; - dma->tx_ring0 = ring; - - ring = b43legacy_setup_dmaring(dev, 1, 1, type); - if (!ring) - goto err_destroy_tx0; - dma->tx_ring1 = ring; - - ring = b43legacy_setup_dmaring(dev, 2, 1, type); - if (!ring) - goto err_destroy_tx1; - dma->tx_ring2 = ring; - - ring = b43legacy_setup_dmaring(dev, 3, 1, type); - if (!ring) - goto err_destroy_tx2; - dma->tx_ring3 = ring; - - ring = b43legacy_setup_dmaring(dev, 4, 1, type); - if (!ring) - goto err_destroy_tx3; - dma->tx_ring4 = ring; - - ring = b43legacy_setup_dmaring(dev, 5, 1, type); - if (!ring) - goto err_destroy_tx4; - dma->tx_ring5 = ring; - - /* setup RX DMA channels. */ - ring = b43legacy_setup_dmaring(dev, 0, 0, type); - if (!ring) - goto err_destroy_tx5; - dma->rx_ring0 = ring; - - if (dev->dev->id.revision < 5) { - ring = b43legacy_setup_dmaring(dev, 3, 0, type); - if (!ring) - goto err_destroy_rx0; - dma->rx_ring3 = ring; - } - - b43legacydbg(dev->wl, "%u-bit DMA initialized\n", (unsigned int)type); - err = 0; -out: - return err; - -err_destroy_rx0: - b43legacy_destroy_dmaring(dma->rx_ring0); - dma->rx_ring0 = NULL; -err_destroy_tx5: - b43legacy_destroy_dmaring(dma->tx_ring5); - dma->tx_ring5 = NULL; -err_destroy_tx4: - b43legacy_destroy_dmaring(dma->tx_ring4); - dma->tx_ring4 = NULL; -err_destroy_tx3: - b43legacy_destroy_dmaring(dma->tx_ring3); - dma->tx_ring3 = NULL; -err_destroy_tx2: - b43legacy_destroy_dmaring(dma->tx_ring2); - dma->tx_ring2 = NULL; -err_destroy_tx1: - b43legacy_destroy_dmaring(dma->tx_ring1); - dma->tx_ring1 = NULL; -err_destroy_tx0: - b43legacy_destroy_dmaring(dma->tx_ring0); - dma->tx_ring0 = NULL; - goto out; -} - -/* Generate a cookie for the TX header. */ -static u16 generate_cookie(struct b43legacy_dmaring *ring, - int slot) -{ - u16 cookie = 0x1000; - - /* Use the upper 4 bits of the cookie as - * DMA controller ID and store the slot number - * in the lower 12 bits. - * Note that the cookie must never be 0, as this - * is a special value used in RX path. - */ - switch (ring->index) { - case 0: - cookie = 0xA000; - break; - case 1: - cookie = 0xB000; - break; - case 2: - cookie = 0xC000; - break; - case 3: - cookie = 0xD000; - break; - case 4: - cookie = 0xE000; - break; - case 5: - cookie = 0xF000; - break; - } - B43legacy_WARN_ON(!(((u16)slot & 0xF000) == 0x0000)); - cookie |= (u16)slot; - - return cookie; -} - -/* Inspect a cookie and find out to which controller/slot it belongs. */ -static -struct b43legacy_dmaring *parse_cookie(struct b43legacy_wldev *dev, - u16 cookie, int *slot) -{ - struct b43legacy_dma *dma = &dev->dma; - struct b43legacy_dmaring *ring = NULL; - - switch (cookie & 0xF000) { - case 0xA000: - ring = dma->tx_ring0; - break; - case 0xB000: - ring = dma->tx_ring1; - break; - case 0xC000: - ring = dma->tx_ring2; - break; - case 0xD000: - ring = dma->tx_ring3; - break; - case 0xE000: - ring = dma->tx_ring4; - break; - case 0xF000: - ring = dma->tx_ring5; - break; - default: - B43legacy_WARN_ON(1); - } - *slot = (cookie & 0x0FFF); - B43legacy_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots)); - - return ring; -} - -static int dma_tx_fragment(struct b43legacy_dmaring *ring, - struct sk_buff **in_skb) -{ - struct sk_buff *skb = *in_skb; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - u8 *header; - int slot, old_top_slot, old_used_slots; - int err; - struct b43legacy_dmadesc32 *desc; - struct b43legacy_dmadesc_meta *meta; - struct b43legacy_dmadesc_meta *meta_hdr; - struct sk_buff *bounce_skb; - -#define SLOTS_PER_PACKET 2 - B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0); - - old_top_slot = ring->current_slot; - old_used_slots = ring->used_slots; - - /* Get a slot for the header. */ - slot = request_slot(ring); - desc = op32_idx2desc(ring, slot, &meta_hdr); - memset(meta_hdr, 0, sizeof(*meta_hdr)); - - header = &(ring->txhdr_cache[slot * sizeof( - struct b43legacy_txhdr_fw3)]); - err = b43legacy_generate_txhdr(ring->dev, header, - skb->data, skb->len, info, - generate_cookie(ring, slot)); - if (unlikely(err)) { - ring->current_slot = old_top_slot; - ring->used_slots = old_used_slots; - return err; - } - - meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, - sizeof(struct b43legacy_txhdr_fw3), 1); - if (b43legacy_dma_mapping_error(ring, meta_hdr->dmaaddr, - sizeof(struct b43legacy_txhdr_fw3), 1)) { - ring->current_slot = old_top_slot; - ring->used_slots = old_used_slots; - return -EIO; - } - op32_fill_descriptor(ring, desc, meta_hdr->dmaaddr, - sizeof(struct b43legacy_txhdr_fw3), 1, 0, 0); - - /* Get a slot for the payload. */ - slot = request_slot(ring); - desc = op32_idx2desc(ring, slot, &meta); - memset(meta, 0, sizeof(*meta)); - - meta->skb = skb; - meta->is_last_fragment = true; - - meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); - /* create a bounce buffer in zone_dma on mapping failure. */ - if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { - bounce_skb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); - if (!bounce_skb) { - ring->current_slot = old_top_slot; - ring->used_slots = old_used_slots; - err = -ENOMEM; - goto out_unmap_hdr; - } - - memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len); - memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb)); - bounce_skb->dev = skb->dev; - skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb)); - info = IEEE80211_SKB_CB(bounce_skb); - - dev_kfree_skb_any(skb); - skb = bounce_skb; - *in_skb = bounce_skb; - meta->skb = skb; - meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); - if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { - ring->current_slot = old_top_slot; - ring->used_slots = old_used_slots; - err = -EIO; - goto out_free_bounce; - } - } - - op32_fill_descriptor(ring, desc, meta->dmaaddr, - skb->len, 0, 1, 1); - - wmb(); /* previous stuff MUST be done */ - /* Now transfer the whole frame. */ - op32_poke_tx(ring, next_slot(ring, slot)); - return 0; - -out_free_bounce: - dev_kfree_skb_any(skb); -out_unmap_hdr: - unmap_descbuffer(ring, meta_hdr->dmaaddr, - sizeof(struct b43legacy_txhdr_fw3), 1); - return err; -} - -static inline -int should_inject_overflow(struct b43legacy_dmaring *ring) -{ -#ifdef CONFIG_B43LEGACY_DEBUG - if (unlikely(b43legacy_debug(ring->dev, - B43legacy_DBG_DMAOVERFLOW))) { - /* Check if we should inject another ringbuffer overflow - * to test handling of this situation in the stack. */ - unsigned long next_overflow; - - next_overflow = ring->last_injected_overflow + HZ; - if (time_after(jiffies, next_overflow)) { - ring->last_injected_overflow = jiffies; - b43legacydbg(ring->dev->wl, - "Injecting TX ring overflow on " - "DMA controller %d\n", ring->index); - return 1; - } - } -#endif /* CONFIG_B43LEGACY_DEBUG */ - return 0; -} - -int b43legacy_dma_tx(struct b43legacy_wldev *dev, - struct sk_buff *skb) -{ - struct b43legacy_dmaring *ring; - int err = 0; - - ring = priority_to_txring(dev, skb_get_queue_mapping(skb)); - B43legacy_WARN_ON(!ring->tx); - - if (unlikely(ring->stopped)) { - /* We get here only because of a bug in mac80211. - * Because of a race, one packet may be queued after - * the queue is stopped, thus we got called when we shouldn't. - * For now, just refuse the transmit. */ - if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) - b43legacyerr(dev->wl, "Packet after queue stopped\n"); - return -ENOSPC; - } - - if (unlikely(WARN_ON(free_slots(ring) < SLOTS_PER_PACKET))) { - /* If we get here, we have a real error with the queue - * full, but queues not stopped. */ - b43legacyerr(dev->wl, "DMA queue overflow\n"); - return -ENOSPC; - } - - /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing - * into the skb data or cb now. */ - err = dma_tx_fragment(ring, &skb); - if (unlikely(err == -ENOKEY)) { - /* Drop this packet, as we don't have the encryption key - * anymore and must not transmit it unencrypted. */ - dev_kfree_skb_any(skb); - return 0; - } - if (unlikely(err)) { - b43legacyerr(dev->wl, "DMA tx mapping failure\n"); - return err; - } - if ((free_slots(ring) < SLOTS_PER_PACKET) || - should_inject_overflow(ring)) { - /* This TX ring is full. */ - unsigned int skb_mapping = skb_get_queue_mapping(skb); - ieee80211_stop_queue(dev->wl->hw, skb_mapping); - dev->wl->tx_queue_stopped[skb_mapping] = 1; - ring->stopped = true; - if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) - b43legacydbg(dev->wl, "Stopped TX ring %d\n", - ring->index); - } - return err; -} - -void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, - const struct b43legacy_txstatus *status) -{ - struct b43legacy_dmaring *ring; - struct b43legacy_dmadesc_meta *meta; - int retry_limit; - int slot; - int firstused; - - ring = parse_cookie(dev, status->cookie, &slot); - if (unlikely(!ring)) - return; - B43legacy_WARN_ON(!ring->tx); - - /* Sanity check: TX packets are processed in-order on one ring. - * Check if the slot deduced from the cookie really is the first - * used slot. */ - firstused = ring->current_slot - ring->used_slots + 1; - if (firstused < 0) - firstused = ring->nr_slots + firstused; - if (unlikely(slot != firstused)) { - /* This possibly is a firmware bug and will result in - * malfunction, memory leaks and/or stall of DMA functionality. - */ - b43legacydbg(dev->wl, "Out of order TX status report on DMA " - "ring %d. Expected %d, but got %d\n", - ring->index, firstused, slot); - return; - } - - while (1) { - B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); - op32_idx2desc(ring, slot, &meta); - - if (meta->skb) - unmap_descbuffer(ring, meta->dmaaddr, - meta->skb->len, 1); - else - unmap_descbuffer(ring, meta->dmaaddr, - sizeof(struct b43legacy_txhdr_fw3), - 1); - - if (meta->is_last_fragment) { - struct ieee80211_tx_info *info; - BUG_ON(!meta->skb); - info = IEEE80211_SKB_CB(meta->skb); - - /* preserve the confiured retry limit before clearing the status - * The xmit function has overwritten the rc's value with the actual - * retry limit done by the hardware */ - retry_limit = info->status.rates[0].count; - ieee80211_tx_info_clear_status(info); - - if (status->acked) - info->flags |= IEEE80211_TX_STAT_ACK; - - if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) { - /* - * If the short retries (RTS, not data frame) have exceeded - * the limit, the hw will not have tried the selected rate, - * but will have used the fallback rate instead. - * Don't let the rate control count attempts for the selected - * rate in this case, otherwise the statistics will be off. - */ - info->status.rates[0].count = 0; - info->status.rates[1].count = status->frame_count; - } else { - if (status->frame_count > retry_limit) { - info->status.rates[0].count = retry_limit; - info->status.rates[1].count = status->frame_count - - retry_limit; - - } else { - info->status.rates[0].count = status->frame_count; - info->status.rates[1].idx = -1; - } - } - - /* Call back to inform the ieee80211 subsystem about the - * status of the transmission. - * Some fields of txstat are already filled in dma_tx(). - */ - ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb); - /* skb is freed by ieee80211_tx_status_irqsafe() */ - meta->skb = NULL; - } else { - /* No need to call free_descriptor_buffer here, as - * this is only the txhdr, which is not allocated. - */ - B43legacy_WARN_ON(meta->skb != NULL); - } - - /* Everything unmapped and free'd. So it's not used anymore. */ - ring->used_slots--; - - if (meta->is_last_fragment) - break; - slot = next_slot(ring, slot); - } - dev->stats.last_tx = jiffies; - if (ring->stopped) { - B43legacy_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET); - ring->stopped = false; - } - - if (dev->wl->tx_queue_stopped[ring->queue_prio]) { - dev->wl->tx_queue_stopped[ring->queue_prio] = 0; - } else { - /* If the driver queue is running wake the corresponding - * mac80211 queue. */ - ieee80211_wake_queue(dev->wl->hw, ring->queue_prio); - if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) - b43legacydbg(dev->wl, "Woke up TX ring %d\n", - ring->index); - } - /* Add work to the queue. */ - ieee80211_queue_work(dev->wl->hw, &dev->wl->tx_work); -} - -static void dma_rx(struct b43legacy_dmaring *ring, - int *slot) -{ - struct b43legacy_dmadesc32 *desc; - struct b43legacy_dmadesc_meta *meta; - struct b43legacy_rxhdr_fw3 *rxhdr; - struct sk_buff *skb; - u16 len; - int err; - dma_addr_t dmaaddr; - - desc = op32_idx2desc(ring, *slot, &meta); - - sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); - skb = meta->skb; - - if (ring->index == 3) { - /* We received an xmit status. */ - struct b43legacy_hwtxstatus *hw = - (struct b43legacy_hwtxstatus *)skb->data; - int i = 0; - - while (hw->cookie == 0) { - if (i > 100) - break; - i++; - udelay(2); - barrier(); - } - b43legacy_handle_hwtxstatus(ring->dev, hw); - /* recycle the descriptor buffer. */ - sync_descbuffer_for_device(ring, meta->dmaaddr, - ring->rx_buffersize); - - return; - } - rxhdr = (struct b43legacy_rxhdr_fw3 *)skb->data; - len = le16_to_cpu(rxhdr->frame_len); - if (len == 0) { - int i = 0; - - do { - udelay(2); - barrier(); - len = le16_to_cpu(rxhdr->frame_len); - } while (len == 0 && i++ < 5); - if (unlikely(len == 0)) { - /* recycle the descriptor buffer. */ - sync_descbuffer_for_device(ring, meta->dmaaddr, - ring->rx_buffersize); - goto drop; - } - } - if (unlikely(len > ring->rx_buffersize)) { - /* The data did not fit into one descriptor buffer - * and is split over multiple buffers. - * This should never happen, as we try to allocate buffers - * big enough. So simply ignore this packet. - */ - int cnt = 0; - s32 tmp = len; - - while (1) { - desc = op32_idx2desc(ring, *slot, &meta); - /* recycle the descriptor buffer. */ - sync_descbuffer_for_device(ring, meta->dmaaddr, - ring->rx_buffersize); - *slot = next_slot(ring, *slot); - cnt++; - tmp -= ring->rx_buffersize; - if (tmp <= 0) - break; - } - b43legacyerr(ring->dev->wl, "DMA RX buffer too small " - "(len: %u, buffer: %u, nr-dropped: %d)\n", - len, ring->rx_buffersize, cnt); - goto drop; - } - - dmaaddr = meta->dmaaddr; - err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); - if (unlikely(err)) { - b43legacydbg(ring->dev->wl, "DMA RX: setup_rx_descbuffer()" - " failed\n"); - sync_descbuffer_for_device(ring, dmaaddr, - ring->rx_buffersize); - goto drop; - } - - unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); - skb_put(skb, len + ring->frameoffset); - skb_pull(skb, ring->frameoffset); - - b43legacy_rx(ring->dev, skb, rxhdr); -drop: - return; -} - -void b43legacy_dma_rx(struct b43legacy_dmaring *ring) -{ - int slot; - int current_slot; - int used_slots = 0; - - B43legacy_WARN_ON(ring->tx); - current_slot = op32_get_current_rxslot(ring); - B43legacy_WARN_ON(!(current_slot >= 0 && current_slot < - ring->nr_slots)); - - slot = ring->current_slot; - for (; slot != current_slot; slot = next_slot(ring, slot)) { - dma_rx(ring, &slot); - update_max_used_slots(ring, ++used_slots); - } - op32_set_current_rxslot(ring, slot); - ring->current_slot = slot; -} - -static void b43legacy_dma_tx_suspend_ring(struct b43legacy_dmaring *ring) -{ - B43legacy_WARN_ON(!ring->tx); - op32_tx_suspend(ring); -} - -static void b43legacy_dma_tx_resume_ring(struct b43legacy_dmaring *ring) -{ - B43legacy_WARN_ON(!ring->tx); - op32_tx_resume(ring); -} - -void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev) -{ - b43legacy_power_saving_ctl_bits(dev, -1, 1); - b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring0); - b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring1); - b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring2); - b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring3); - b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring4); - b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring5); -} - -void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev) -{ - b43legacy_dma_tx_resume_ring(dev->dma.tx_ring5); - b43legacy_dma_tx_resume_ring(dev->dma.tx_ring4); - b43legacy_dma_tx_resume_ring(dev->dma.tx_ring3); - b43legacy_dma_tx_resume_ring(dev->dma.tx_ring2); - b43legacy_dma_tx_resume_ring(dev->dma.tx_ring1); - b43legacy_dma_tx_resume_ring(dev->dma.tx_ring0); - b43legacy_power_saving_ctl_bits(dev, -1, -1); -} diff --git a/drivers/net/wireless/b43legacy/dma.h b/drivers/net/wireless/b43legacy/dma.h deleted file mode 100644 index c3282f906bc7..000000000000 --- a/drivers/net/wireless/b43legacy/dma.h +++ /dev/null @@ -1,231 +0,0 @@ -#ifndef B43legacy_DMA_H_ -#define B43legacy_DMA_H_ - -#include -#include -#include -#include -#include - -#include "b43legacy.h" - - -/* DMA-Interrupt reasons. */ -#define B43legacy_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \ - | (1 << 14) | (1 << 15)) -#define B43legacy_DMAIRQ_NONFATALMASK (1 << 13) -#define B43legacy_DMAIRQ_RX_DONE (1 << 16) - - -/*** 32-bit DMA Engine. ***/ - -/* 32-bit DMA controller registers. */ -#define B43legacy_DMA32_TXCTL 0x00 -#define B43legacy_DMA32_TXENABLE 0x00000001 -#define B43legacy_DMA32_TXSUSPEND 0x00000002 -#define B43legacy_DMA32_TXLOOPBACK 0x00000004 -#define B43legacy_DMA32_TXFLUSH 0x00000010 -#define B43legacy_DMA32_TXADDREXT_MASK 0x00030000 -#define B43legacy_DMA32_TXADDREXT_SHIFT 16 -#define B43legacy_DMA32_TXRING 0x04 -#define B43legacy_DMA32_TXINDEX 0x08 -#define B43legacy_DMA32_TXSTATUS 0x0C -#define B43legacy_DMA32_TXDPTR 0x00000FFF -#define B43legacy_DMA32_TXSTATE 0x0000F000 -#define B43legacy_DMA32_TXSTAT_DISABLED 0x00000000 -#define B43legacy_DMA32_TXSTAT_ACTIVE 0x00001000 -#define B43legacy_DMA32_TXSTAT_IDLEWAIT 0x00002000 -#define B43legacy_DMA32_TXSTAT_STOPPED 0x00003000 -#define B43legacy_DMA32_TXSTAT_SUSP 0x00004000 -#define B43legacy_DMA32_TXERROR 0x000F0000 -#define B43legacy_DMA32_TXERR_NOERR 0x00000000 -#define B43legacy_DMA32_TXERR_PROT 0x00010000 -#define B43legacy_DMA32_TXERR_UNDERRUN 0x00020000 -#define B43legacy_DMA32_TXERR_BUFREAD 0x00030000 -#define B43legacy_DMA32_TXERR_DESCREAD 0x00040000 -#define B43legacy_DMA32_TXACTIVE 0xFFF00000 -#define B43legacy_DMA32_RXCTL 0x10 -#define B43legacy_DMA32_RXENABLE 0x00000001 -#define B43legacy_DMA32_RXFROFF_MASK 0x000000FE -#define B43legacy_DMA32_RXFROFF_SHIFT 1 -#define B43legacy_DMA32_RXDIRECTFIFO 0x00000100 -#define B43legacy_DMA32_RXADDREXT_MASK 0x00030000 -#define B43legacy_DMA32_RXADDREXT_SHIFT 16 -#define B43legacy_DMA32_RXRING 0x14 -#define B43legacy_DMA32_RXINDEX 0x18 -#define B43legacy_DMA32_RXSTATUS 0x1C -#define B43legacy_DMA32_RXDPTR 0x00000FFF -#define B43legacy_DMA32_RXSTATE 0x0000F000 -#define B43legacy_DMA32_RXSTAT_DISABLED 0x00000000 -#define B43legacy_DMA32_RXSTAT_ACTIVE 0x00001000 -#define B43legacy_DMA32_RXSTAT_IDLEWAIT 0x00002000 -#define B43legacy_DMA32_RXSTAT_STOPPED 0x00003000 -#define B43legacy_DMA32_RXERROR 0x000F0000 -#define B43legacy_DMA32_RXERR_NOERR 0x00000000 -#define B43legacy_DMA32_RXERR_PROT 0x00010000 -#define B43legacy_DMA32_RXERR_OVERFLOW 0x00020000 -#define B43legacy_DMA32_RXERR_BUFWRITE 0x00030000 -#define B43legacy_DMA32_RXERR_DESCREAD 0x00040000 -#define B43legacy_DMA32_RXACTIVE 0xFFF00000 - -/* 32-bit DMA descriptor. */ -struct b43legacy_dmadesc32 { - __le32 control; - __le32 address; -} __packed; -#define B43legacy_DMA32_DCTL_BYTECNT 0x00001FFF -#define B43legacy_DMA32_DCTL_ADDREXT_MASK 0x00030000 -#define B43legacy_DMA32_DCTL_ADDREXT_SHIFT 16 -#define B43legacy_DMA32_DCTL_DTABLEEND 0x10000000 -#define B43legacy_DMA32_DCTL_IRQ 0x20000000 -#define B43legacy_DMA32_DCTL_FRAMEEND 0x40000000 -#define B43legacy_DMA32_DCTL_FRAMESTART 0x80000000 - - -/* Misc DMA constants */ -#define B43legacy_DMA_RINGMEMSIZE PAGE_SIZE -#define B43legacy_DMA0_RX_FRAMEOFFSET 30 -#define B43legacy_DMA3_RX_FRAMEOFFSET 0 - - -/* DMA engine tuning knobs */ -#define B43legacy_TXRING_SLOTS 128 -#define B43legacy_RXRING_SLOTS 64 -#define B43legacy_DMA0_RX_BUFFERSIZE (2304 + 100) -#define B43legacy_DMA3_RX_BUFFERSIZE 16 - - - -#ifdef CONFIG_B43LEGACY_DMA - - -struct sk_buff; -struct b43legacy_private; -struct b43legacy_txstatus; - - -struct b43legacy_dmadesc_meta { - /* The kernel DMA-able buffer. */ - struct sk_buff *skb; - /* DMA base bus-address of the descriptor buffer. */ - dma_addr_t dmaaddr; - /* ieee80211 TX status. Only used once per 802.11 frag. */ - bool is_last_fragment; -}; - -enum b43legacy_dmatype { - B43legacy_DMA_30BIT = 30, - B43legacy_DMA_32BIT = 32, -}; - -struct b43legacy_dmaring { - /* Kernel virtual base address of the ring memory. */ - void *descbase; - /* Meta data about all descriptors. */ - struct b43legacy_dmadesc_meta *meta; - /* Cache of TX headers for each slot. - * This is to avoid an allocation on each TX. - * This is NULL for an RX ring. - */ - u8 *txhdr_cache; - /* (Unadjusted) DMA base bus-address of the ring memory. */ - dma_addr_t dmabase; - /* Number of descriptor slots in the ring. */ - int nr_slots; - /* Number of used descriptor slots. */ - int used_slots; - /* Currently used slot in the ring. */ - int current_slot; - /* Frameoffset in octets. */ - u32 frameoffset; - /* Descriptor buffer size. */ - u16 rx_buffersize; - /* The MMIO base register of the DMA controller. */ - u16 mmio_base; - /* DMA controller index number (0-5). */ - int index; - /* Boolean. Is this a TX ring? */ - bool tx; - /* The type of DMA engine used. */ - enum b43legacy_dmatype type; - /* Boolean. Is this ring stopped at ieee80211 level? */ - bool stopped; - /* The QOS priority assigned to this ring. Only used for TX rings. - * This is the mac80211 "queue" value. */ - u8 queue_prio; - struct b43legacy_wldev *dev; -#ifdef CONFIG_B43LEGACY_DEBUG - /* Maximum number of used slots. */ - int max_used_slots; - /* Last time we injected a ring overflow. */ - unsigned long last_injected_overflow; -#endif /* CONFIG_B43LEGACY_DEBUG*/ -}; - - -static inline -u32 b43legacy_dma_read(struct b43legacy_dmaring *ring, - u16 offset) -{ - return b43legacy_read32(ring->dev, ring->mmio_base + offset); -} - -static inline -void b43legacy_dma_write(struct b43legacy_dmaring *ring, - u16 offset, u32 value) -{ - b43legacy_write32(ring->dev, ring->mmio_base + offset, value); -} - - -int b43legacy_dma_init(struct b43legacy_wldev *dev); -void b43legacy_dma_free(struct b43legacy_wldev *dev); - -void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev); -void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev); - -int b43legacy_dma_tx(struct b43legacy_wldev *dev, - struct sk_buff *skb); -void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, - const struct b43legacy_txstatus *status); - -void b43legacy_dma_rx(struct b43legacy_dmaring *ring); - -#else /* CONFIG_B43LEGACY_DMA */ - - -static inline -int b43legacy_dma_init(struct b43legacy_wldev *dev) -{ - return 0; -} -static inline -void b43legacy_dma_free(struct b43legacy_wldev *dev) -{ -} -static inline -int b43legacy_dma_tx(struct b43legacy_wldev *dev, - struct sk_buff *skb) -{ - return 0; -} -static inline -void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, - const struct b43legacy_txstatus *status) -{ -} -static inline -void b43legacy_dma_rx(struct b43legacy_dmaring *ring) -{ -} -static inline -void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev) -{ -} -static inline -void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev) -{ -} - -#endif /* CONFIG_B43LEGACY_DMA */ -#endif /* B43legacy_DMA_H_ */ diff --git a/drivers/net/wireless/b43legacy/ilt.c b/drivers/net/wireless/b43legacy/ilt.c deleted file mode 100644 index ee5682e54204..000000000000 --- a/drivers/net/wireless/b43legacy/ilt.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - - Broadcom B43legacy wireless driver - - Copyright (c) 2005 Martin Langer , - Stefano Brivio - Michael Buesch - Danny van Dyk - Andreas Jaggi - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43legacy.h" -#include "ilt.h" -#include "phy.h" - - -/**** Initial Internal Lookup Tables ****/ - -const u32 b43legacy_ilt_rotor[B43legacy_ILT_ROTOR_SIZE] = { - 0xFEB93FFD, 0xFEC63FFD, /* 0 */ - 0xFED23FFD, 0xFEDF3FFD, - 0xFEEC3FFE, 0xFEF83FFE, - 0xFF053FFE, 0xFF113FFE, - 0xFF1E3FFE, 0xFF2A3FFF, /* 8 */ - 0xFF373FFF, 0xFF443FFF, - 0xFF503FFF, 0xFF5D3FFF, - 0xFF693FFF, 0xFF763FFF, - 0xFF824000, 0xFF8F4000, /* 16 */ - 0xFF9B4000, 0xFFA84000, - 0xFFB54000, 0xFFC14000, - 0xFFCE4000, 0xFFDA4000, - 0xFFE74000, 0xFFF34000, /* 24 */ - 0x00004000, 0x000D4000, - 0x00194000, 0x00264000, - 0x00324000, 0x003F4000, - 0x004B4000, 0x00584000, /* 32 */ - 0x00654000, 0x00714000, - 0x007E4000, 0x008A3FFF, - 0x00973FFF, 0x00A33FFF, - 0x00B03FFF, 0x00BC3FFF, /* 40 */ - 0x00C93FFF, 0x00D63FFF, - 0x00E23FFE, 0x00EF3FFE, - 0x00FB3FFE, 0x01083FFE, - 0x01143FFE, 0x01213FFD, /* 48 */ - 0x012E3FFD, 0x013A3FFD, - 0x01473FFD, -}; - -const u32 b43legacy_ilt_retard[B43legacy_ILT_RETARD_SIZE] = { - 0xDB93CB87, 0xD666CF64, /* 0 */ - 0xD1FDD358, 0xCDA6D826, - 0xCA38DD9F, 0xC729E2B4, - 0xC469E88E, 0xC26AEE2B, - 0xC0DEF46C, 0xC073FA62, /* 8 */ - 0xC01D00D5, 0xC0760743, - 0xC1560D1E, 0xC2E51369, - 0xC4ED18FF, 0xC7AC1ED7, - 0xCB2823B2, 0xCEFA28D9, /* 16 */ - 0xD2F62D3F, 0xD7BB3197, - 0xDCE53568, 0xE1FE3875, - 0xE7D13B35, 0xED663D35, - 0xF39B3EC4, 0xF98E3FA7, /* 24 */ - 0x00004000, 0x06723FA7, - 0x0C653EC4, 0x129A3D35, - 0x182F3B35, 0x1E023875, - 0x231B3568, 0x28453197, /* 32 */ - 0x2D0A2D3F, 0x310628D9, - 0x34D823B2, 0x38541ED7, - 0x3B1318FF, 0x3D1B1369, - 0x3EAA0D1E, 0x3F8A0743, /* 40 */ - 0x3FE300D5, 0x3F8DFA62, - 0x3F22F46C, 0x3D96EE2B, - 0x3B97E88E, 0x38D7E2B4, - 0x35C8DD9F, 0x325AD826, /* 48 */ - 0x2E03D358, 0x299ACF64, - 0x246DCB87, -}; - -const u16 b43legacy_ilt_finefreqa[B43legacy_ILT_FINEFREQA_SIZE] = { - 0x0082, 0x0082, 0x0102, 0x0182, /* 0 */ - 0x0202, 0x0282, 0x0302, 0x0382, - 0x0402, 0x0482, 0x0502, 0x0582, - 0x05E2, 0x0662, 0x06E2, 0x0762, - 0x07E2, 0x0842, 0x08C2, 0x0942, /* 16 */ - 0x09C2, 0x0A22, 0x0AA2, 0x0B02, - 0x0B82, 0x0BE2, 0x0C62, 0x0CC2, - 0x0D42, 0x0DA2, 0x0E02, 0x0E62, - 0x0EE2, 0x0F42, 0x0FA2, 0x1002, /* 32 */ - 0x1062, 0x10C2, 0x1122, 0x1182, - 0x11E2, 0x1242, 0x12A2, 0x12E2, - 0x1342, 0x13A2, 0x1402, 0x1442, - 0x14A2, 0x14E2, 0x1542, 0x1582, /* 48 */ - 0x15E2, 0x1622, 0x1662, 0x16C1, - 0x1701, 0x1741, 0x1781, 0x17E1, - 0x1821, 0x1861, 0x18A1, 0x18E1, - 0x1921, 0x1961, 0x19A1, 0x19E1, /* 64 */ - 0x1A21, 0x1A61, 0x1AA1, 0x1AC1, - 0x1B01, 0x1B41, 0x1B81, 0x1BA1, - 0x1BE1, 0x1C21, 0x1C41, 0x1C81, - 0x1CA1, 0x1CE1, 0x1D01, 0x1D41, /* 80 */ - 0x1D61, 0x1DA1, 0x1DC1, 0x1E01, - 0x1E21, 0x1E61, 0x1E81, 0x1EA1, - 0x1EE1, 0x1F01, 0x1F21, 0x1F41, - 0x1F81, 0x1FA1, 0x1FC1, 0x1FE1, /* 96 */ - 0x2001, 0x2041, 0x2061, 0x2081, - 0x20A1, 0x20C1, 0x20E1, 0x2101, - 0x2121, 0x2141, 0x2161, 0x2181, - 0x21A1, 0x21C1, 0x21E1, 0x2201, /* 112 */ - 0x2221, 0x2241, 0x2261, 0x2281, - 0x22A1, 0x22C1, 0x22C1, 0x22E1, - 0x2301, 0x2321, 0x2341, 0x2361, - 0x2361, 0x2381, 0x23A1, 0x23C1, /* 128 */ - 0x23E1, 0x23E1, 0x2401, 0x2421, - 0x2441, 0x2441, 0x2461, 0x2481, - 0x2481, 0x24A1, 0x24C1, 0x24C1, - 0x24E1, 0x2501, 0x2501, 0x2521, /* 144 */ - 0x2541, 0x2541, 0x2561, 0x2561, - 0x2581, 0x25A1, 0x25A1, 0x25C1, - 0x25C1, 0x25E1, 0x2601, 0x2601, - 0x2621, 0x2621, 0x2641, 0x2641, /* 160 */ - 0x2661, 0x2661, 0x2681, 0x2681, - 0x26A1, 0x26A1, 0x26C1, 0x26C1, - 0x26E1, 0x26E1, 0x2701, 0x2701, - 0x2721, 0x2721, 0x2740, 0x2740, /* 176 */ - 0x2760, 0x2760, 0x2780, 0x2780, - 0x2780, 0x27A0, 0x27A0, 0x27C0, - 0x27C0, 0x27E0, 0x27E0, 0x27E0, - 0x2800, 0x2800, 0x2820, 0x2820, /* 192 */ - 0x2820, 0x2840, 0x2840, 0x2840, - 0x2860, 0x2860, 0x2880, 0x2880, - 0x2880, 0x28A0, 0x28A0, 0x28A0, - 0x28C0, 0x28C0, 0x28C0, 0x28E0, /* 208 */ - 0x28E0, 0x28E0, 0x2900, 0x2900, - 0x2900, 0x2920, 0x2920, 0x2920, - 0x2940, 0x2940, 0x2940, 0x2960, - 0x2960, 0x2960, 0x2960, 0x2980, /* 224 */ - 0x2980, 0x2980, 0x29A0, 0x29A0, - 0x29A0, 0x29A0, 0x29C0, 0x29C0, - 0x29C0, 0x29E0, 0x29E0, 0x29E0, - 0x29E0, 0x2A00, 0x2A00, 0x2A00, /* 240 */ - 0x2A00, 0x2A20, 0x2A20, 0x2A20, - 0x2A20, 0x2A40, 0x2A40, 0x2A40, - 0x2A40, 0x2A60, 0x2A60, 0x2A60, -}; - -const u16 b43legacy_ilt_finefreqg[B43legacy_ILT_FINEFREQG_SIZE] = { - 0x0089, 0x02E9, 0x0409, 0x04E9, /* 0 */ - 0x05A9, 0x0669, 0x0709, 0x0789, - 0x0829, 0x08A9, 0x0929, 0x0989, - 0x0A09, 0x0A69, 0x0AC9, 0x0B29, - 0x0BA9, 0x0BE9, 0x0C49, 0x0CA9, /* 16 */ - 0x0D09, 0x0D69, 0x0DA9, 0x0E09, - 0x0E69, 0x0EA9, 0x0F09, 0x0F49, - 0x0FA9, 0x0FE9, 0x1029, 0x1089, - 0x10C9, 0x1109, 0x1169, 0x11A9, /* 32 */ - 0x11E9, 0x1229, 0x1289, 0x12C9, - 0x1309, 0x1349, 0x1389, 0x13C9, - 0x1409, 0x1449, 0x14A9, 0x14E9, - 0x1529, 0x1569, 0x15A9, 0x15E9, /* 48 */ - 0x1629, 0x1669, 0x16A9, 0x16E8, - 0x1728, 0x1768, 0x17A8, 0x17E8, - 0x1828, 0x1868, 0x18A8, 0x18E8, - 0x1928, 0x1968, 0x19A8, 0x19E8, /* 64 */ - 0x1A28, 0x1A68, 0x1AA8, 0x1AE8, - 0x1B28, 0x1B68, 0x1BA8, 0x1BE8, - 0x1C28, 0x1C68, 0x1CA8, 0x1CE8, - 0x1D28, 0x1D68, 0x1DC8, 0x1E08, /* 80 */ - 0x1E48, 0x1E88, 0x1EC8, 0x1F08, - 0x1F48, 0x1F88, 0x1FE8, 0x2028, - 0x2068, 0x20A8, 0x2108, 0x2148, - 0x2188, 0x21C8, 0x2228, 0x2268, /* 96 */ - 0x22C8, 0x2308, 0x2348, 0x23A8, - 0x23E8, 0x2448, 0x24A8, 0x24E8, - 0x2548, 0x25A8, 0x2608, 0x2668, - 0x26C8, 0x2728, 0x2787, 0x27E7, /* 112 */ - 0x2847, 0x28C7, 0x2947, 0x29A7, - 0x2A27, 0x2AC7, 0x2B47, 0x2BE7, - 0x2CA7, 0x2D67, 0x2E47, 0x2F67, - 0x3247, 0x3526, 0x3646, 0x3726, /* 128 */ - 0x3806, 0x38A6, 0x3946, 0x39E6, - 0x3A66, 0x3AE6, 0x3B66, 0x3BC6, - 0x3C45, 0x3CA5, 0x3D05, 0x3D85, - 0x3DE5, 0x3E45, 0x3EA5, 0x3EE5, /* 144 */ - 0x3F45, 0x3FA5, 0x4005, 0x4045, - 0x40A5, 0x40E5, 0x4145, 0x4185, - 0x41E5, 0x4225, 0x4265, 0x42C5, - 0x4305, 0x4345, 0x43A5, 0x43E5, /* 160 */ - 0x4424, 0x4464, 0x44C4, 0x4504, - 0x4544, 0x4584, 0x45C4, 0x4604, - 0x4644, 0x46A4, 0x46E4, 0x4724, - 0x4764, 0x47A4, 0x47E4, 0x4824, /* 176 */ - 0x4864, 0x48A4, 0x48E4, 0x4924, - 0x4964, 0x49A4, 0x49E4, 0x4A24, - 0x4A64, 0x4AA4, 0x4AE4, 0x4B23, - 0x4B63, 0x4BA3, 0x4BE3, 0x4C23, /* 192 */ - 0x4C63, 0x4CA3, 0x4CE3, 0x4D23, - 0x4D63, 0x4DA3, 0x4DE3, 0x4E23, - 0x4E63, 0x4EA3, 0x4EE3, 0x4F23, - 0x4F63, 0x4FC3, 0x5003, 0x5043, /* 208 */ - 0x5083, 0x50C3, 0x5103, 0x5143, - 0x5183, 0x51E2, 0x5222, 0x5262, - 0x52A2, 0x52E2, 0x5342, 0x5382, - 0x53C2, 0x5402, 0x5462, 0x54A2, /* 224 */ - 0x5502, 0x5542, 0x55A2, 0x55E2, - 0x5642, 0x5682, 0x56E2, 0x5722, - 0x5782, 0x57E1, 0x5841, 0x58A1, - 0x5901, 0x5961, 0x59C1, 0x5A21, /* 240 */ - 0x5AA1, 0x5B01, 0x5B81, 0x5BE1, - 0x5C61, 0x5D01, 0x5D80, 0x5E20, - 0x5EE0, 0x5FA0, 0x6080, 0x61C0, -}; - -const u16 b43legacy_ilt_noisea2[B43legacy_ILT_NOISEA2_SIZE] = { - 0x0001, 0x0001, 0x0001, 0xFFFE, - 0xFFFE, 0x3FFF, 0x1000, 0x0393, -}; - -const u16 b43legacy_ilt_noisea3[B43legacy_ILT_NOISEA3_SIZE] = { - 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, - 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, -}; - -const u16 b43legacy_ilt_noiseg1[B43legacy_ILT_NOISEG1_SIZE] = { - 0x013C, 0x01F5, 0x031A, 0x0631, - 0x0001, 0x0001, 0x0001, 0x0001, -}; - -const u16 b43legacy_ilt_noiseg2[B43legacy_ILT_NOISEG2_SIZE] = { - 0x5484, 0x3C40, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, -}; - -const u16 b43legacy_ilt_noisescaleg1[B43legacy_ILT_NOISESCALEG_SIZE] = { - 0x6C77, 0x5162, 0x3B40, 0x3335, /* 0 */ - 0x2F2D, 0x2A2A, 0x2527, 0x1F21, - 0x1A1D, 0x1719, 0x1616, 0x1414, - 0x1414, 0x1400, 0x1414, 0x1614, - 0x1716, 0x1A19, 0x1F1D, 0x2521, /* 16 */ - 0x2A27, 0x2F2A, 0x332D, 0x3B35, - 0x5140, 0x6C62, 0x0077, -}; - -const u16 b43legacy_ilt_noisescaleg2[B43legacy_ILT_NOISESCALEG_SIZE] = { - 0xD8DD, 0xCBD4, 0xBCC0, 0XB6B7, /* 0 */ - 0xB2B0, 0xADAD, 0xA7A9, 0x9FA1, - 0x969B, 0x9195, 0x8F8F, 0x8A8A, - 0x8A8A, 0x8A00, 0x8A8A, 0x8F8A, - 0x918F, 0x9695, 0x9F9B, 0xA7A1, /* 16 */ - 0xADA9, 0xB2AD, 0xB6B0, 0xBCB7, - 0xCBC0, 0xD8D4, 0x00DD, -}; - -const u16 b43legacy_ilt_noisescaleg3[B43legacy_ILT_NOISESCALEG_SIZE] = { - 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 0 */ - 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, - 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, - 0xA4A4, 0xA400, 0xA4A4, 0xA4A4, - 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 16 */ - 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, - 0xA4A4, 0xA4A4, 0x00A4, -}; - -const u16 b43legacy_ilt_sigmasqr1[B43legacy_ILT_SIGMASQR_SIZE] = { - 0x007A, 0x0075, 0x0071, 0x006C, /* 0 */ - 0x0067, 0x0063, 0x005E, 0x0059, - 0x0054, 0x0050, 0x004B, 0x0046, - 0x0042, 0x003D, 0x003D, 0x003D, - 0x003D, 0x003D, 0x003D, 0x003D, /* 16 */ - 0x003D, 0x003D, 0x003D, 0x003D, - 0x003D, 0x003D, 0x0000, 0x003D, - 0x003D, 0x003D, 0x003D, 0x003D, - 0x003D, 0x003D, 0x003D, 0x003D, /* 32 */ - 0x003D, 0x003D, 0x003D, 0x003D, - 0x0042, 0x0046, 0x004B, 0x0050, - 0x0054, 0x0059, 0x005E, 0x0063, - 0x0067, 0x006C, 0x0071, 0x0075, /* 48 */ - 0x007A, -}; - -const u16 b43legacy_ilt_sigmasqr2[B43legacy_ILT_SIGMASQR_SIZE] = { - 0x00DE, 0x00DC, 0x00DA, 0x00D8, /* 0 */ - 0x00D6, 0x00D4, 0x00D2, 0x00CF, - 0x00CD, 0x00CA, 0x00C7, 0x00C4, - 0x00C1, 0x00BE, 0x00BE, 0x00BE, - 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 16 */ - 0x00BE, 0x00BE, 0x00BE, 0x00BE, - 0x00BE, 0x00BE, 0x0000, 0x00BE, - 0x00BE, 0x00BE, 0x00BE, 0x00BE, - 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 32 */ - 0x00BE, 0x00BE, 0x00BE, 0x00BE, - 0x00C1, 0x00C4, 0x00C7, 0x00CA, - 0x00CD, 0x00CF, 0x00D2, 0x00D4, - 0x00D6, 0x00D8, 0x00DA, 0x00DC, /* 48 */ - 0x00DE, -}; - -/**** Helper functions to access the device Internal Lookup Tables ****/ - -void b43legacy_ilt_write(struct b43legacy_wldev *dev, u16 offset, u16 val) -{ - b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset); - mmiowb(); - b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA1, val); -} - -void b43legacy_ilt_write32(struct b43legacy_wldev *dev, u16 offset, u32 val) -{ - b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset); - mmiowb(); - b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA2, - (val & 0xFFFF0000) >> 16); - b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA1, - val & 0x0000FFFF); -} - -u16 b43legacy_ilt_read(struct b43legacy_wldev *dev, u16 offset) -{ - b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset); - return b43legacy_phy_read(dev, B43legacy_PHY_ILT_G_DATA1); -} diff --git a/drivers/net/wireless/b43legacy/ilt.h b/drivers/net/wireless/b43legacy/ilt.h deleted file mode 100644 index 48bcf37eccb8..000000000000 --- a/drivers/net/wireless/b43legacy/ilt.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef B43legacy_ILT_H_ -#define B43legacy_ILT_H_ - -#define B43legacy_ILT_ROTOR_SIZE 53 -extern const u32 b43legacy_ilt_rotor[B43legacy_ILT_ROTOR_SIZE]; -#define B43legacy_ILT_RETARD_SIZE 53 -extern const u32 b43legacy_ilt_retard[B43legacy_ILT_RETARD_SIZE]; -#define B43legacy_ILT_FINEFREQA_SIZE 256 -extern const u16 b43legacy_ilt_finefreqa[B43legacy_ILT_FINEFREQA_SIZE]; -#define B43legacy_ILT_FINEFREQG_SIZE 256 -extern const u16 b43legacy_ilt_finefreqg[B43legacy_ILT_FINEFREQG_SIZE]; -#define B43legacy_ILT_NOISEA2_SIZE 8 -extern const u16 b43legacy_ilt_noisea2[B43legacy_ILT_NOISEA2_SIZE]; -#define B43legacy_ILT_NOISEA3_SIZE 8 -extern const u16 b43legacy_ilt_noisea3[B43legacy_ILT_NOISEA3_SIZE]; -#define B43legacy_ILT_NOISEG1_SIZE 8 -extern const u16 b43legacy_ilt_noiseg1[B43legacy_ILT_NOISEG1_SIZE]; -#define B43legacy_ILT_NOISEG2_SIZE 8 -extern const u16 b43legacy_ilt_noiseg2[B43legacy_ILT_NOISEG2_SIZE]; -#define B43legacy_ILT_NOISESCALEG_SIZE 27 -extern const u16 b43legacy_ilt_noisescaleg1[B43legacy_ILT_NOISESCALEG_SIZE]; -extern const u16 b43legacy_ilt_noisescaleg2[B43legacy_ILT_NOISESCALEG_SIZE]; -extern const u16 b43legacy_ilt_noisescaleg3[B43legacy_ILT_NOISESCALEG_SIZE]; -#define B43legacy_ILT_SIGMASQR_SIZE 53 -extern const u16 b43legacy_ilt_sigmasqr1[B43legacy_ILT_SIGMASQR_SIZE]; -extern const u16 b43legacy_ilt_sigmasqr2[B43legacy_ILT_SIGMASQR_SIZE]; - - -void b43legacy_ilt_write(struct b43legacy_wldev *dev, u16 offset, u16 val); -void b43legacy_ilt_write32(struct b43legacy_wldev *dev, u16 offset, - u32 val); -u16 b43legacy_ilt_read(struct b43legacy_wldev *dev, u16 offset); - -#endif /* B43legacy_ILT_H_ */ diff --git a/drivers/net/wireless/b43legacy/leds.c b/drivers/net/wireless/b43legacy/leds.c deleted file mode 100644 index fd4565389c77..000000000000 --- a/drivers/net/wireless/b43legacy/leds.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - - Broadcom B43 wireless driver - LED control - - Copyright (c) 2005 Martin Langer , - Copyright (c) 2005 Stefano Brivio - Copyright (c) 2005-2007 Michael Buesch - Copyright (c) 2005 Danny van Dyk - Copyright (c) 2005 Andreas Jaggi - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43legacy.h" -#include "leds.h" -#include "rfkill.h" - - -static void b43legacy_led_turn_on(struct b43legacy_wldev *dev, u8 led_index, - bool activelow) -{ - struct b43legacy_wl *wl = dev->wl; - unsigned long flags; - u16 ctl; - - spin_lock_irqsave(&wl->leds_lock, flags); - ctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL); - if (activelow) - ctl &= ~(1 << led_index); - else - ctl |= (1 << led_index); - b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ctl); - spin_unlock_irqrestore(&wl->leds_lock, flags); -} - -static void b43legacy_led_turn_off(struct b43legacy_wldev *dev, u8 led_index, - bool activelow) -{ - struct b43legacy_wl *wl = dev->wl; - unsigned long flags; - u16 ctl; - - spin_lock_irqsave(&wl->leds_lock, flags); - ctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL); - if (activelow) - ctl |= (1 << led_index); - else - ctl &= ~(1 << led_index); - b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ctl); - spin_unlock_irqrestore(&wl->leds_lock, flags); -} - -/* Callback from the LED subsystem. */ -static void b43legacy_led_brightness_set(struct led_classdev *led_dev, - enum led_brightness brightness) -{ - struct b43legacy_led *led = container_of(led_dev, struct b43legacy_led, - led_dev); - struct b43legacy_wldev *dev = led->dev; - bool radio_enabled; - - /* Checking the radio-enabled status here is slightly racy, - * but we want to avoid the locking overhead and we don't care - * whether the LED has the wrong state for a second. */ - radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable); - - if (brightness == LED_OFF || !radio_enabled) - b43legacy_led_turn_off(dev, led->index, led->activelow); - else - b43legacy_led_turn_on(dev, led->index, led->activelow); -} - -static int b43legacy_register_led(struct b43legacy_wldev *dev, - struct b43legacy_led *led, - const char *name, - const char *default_trigger, - u8 led_index, bool activelow) -{ - int err; - - b43legacy_led_turn_off(dev, led_index, activelow); - if (led->dev) - return -EEXIST; - if (!default_trigger) - return -EINVAL; - led->dev = dev; - led->index = led_index; - led->activelow = activelow; - strncpy(led->name, name, sizeof(led->name)); - - led->led_dev.name = led->name; - led->led_dev.default_trigger = default_trigger; - led->led_dev.brightness_set = b43legacy_led_brightness_set; - - err = led_classdev_register(dev->dev->dev, &led->led_dev); - if (err) { - b43legacywarn(dev->wl, "LEDs: Failed to register %s\n", name); - led->dev = NULL; - return err; - } - return 0; -} - -static void b43legacy_unregister_led(struct b43legacy_led *led) -{ - if (!led->dev) - return; - led_classdev_unregister(&led->led_dev); - b43legacy_led_turn_off(led->dev, led->index, led->activelow); - led->dev = NULL; -} - -static void b43legacy_map_led(struct b43legacy_wldev *dev, - u8 led_index, - enum b43legacy_led_behaviour behaviour, - bool activelow) -{ - struct ieee80211_hw *hw = dev->wl->hw; - char name[B43legacy_LED_MAX_NAME_LEN + 1]; - - /* Map the b43 specific LED behaviour value to the - * generic LED triggers. */ - switch (behaviour) { - case B43legacy_LED_INACTIVE: - break; - case B43legacy_LED_OFF: - b43legacy_led_turn_off(dev, led_index, activelow); - break; - case B43legacy_LED_ON: - b43legacy_led_turn_on(dev, led_index, activelow); - break; - case B43legacy_LED_ACTIVITY: - case B43legacy_LED_TRANSFER: - case B43legacy_LED_APTRANSFER: - snprintf(name, sizeof(name), - "b43legacy-%s::tx", wiphy_name(hw->wiphy)); - b43legacy_register_led(dev, &dev->led_tx, name, - ieee80211_get_tx_led_name(hw), - led_index, activelow); - snprintf(name, sizeof(name), - "b43legacy-%s::rx", wiphy_name(hw->wiphy)); - b43legacy_register_led(dev, &dev->led_rx, name, - ieee80211_get_rx_led_name(hw), - led_index, activelow); - break; - case B43legacy_LED_RADIO_ALL: - case B43legacy_LED_RADIO_A: - case B43legacy_LED_RADIO_B: - case B43legacy_LED_MODE_BG: - snprintf(name, sizeof(name), - "b43legacy-%s::radio", wiphy_name(hw->wiphy)); - b43legacy_register_led(dev, &dev->led_radio, name, - ieee80211_get_radio_led_name(hw), - led_index, activelow); - /* Sync the RF-kill LED state with radio and switch states. */ - if (dev->phy.radio_on && b43legacy_is_hw_radio_enabled(dev)) - b43legacy_led_turn_on(dev, led_index, activelow); - break; - case B43legacy_LED_WEIRD: - case B43legacy_LED_ASSOC: - snprintf(name, sizeof(name), - "b43legacy-%s::assoc", wiphy_name(hw->wiphy)); - b43legacy_register_led(dev, &dev->led_assoc, name, - ieee80211_get_assoc_led_name(hw), - led_index, activelow); - break; - default: - b43legacywarn(dev->wl, "LEDs: Unknown behaviour 0x%02X\n", - behaviour); - break; - } -} - -void b43legacy_leds_init(struct b43legacy_wldev *dev) -{ - struct ssb_bus *bus = dev->dev->bus; - u8 sprom[4]; - int i; - enum b43legacy_led_behaviour behaviour; - bool activelow; - - sprom[0] = bus->sprom.gpio0; - sprom[1] = bus->sprom.gpio1; - sprom[2] = bus->sprom.gpio2; - sprom[3] = bus->sprom.gpio3; - - for (i = 0; i < 4; i++) { - if (sprom[i] == 0xFF) { - /* There is no LED information in the SPROM - * for this LED. Hardcode it here. */ - activelow = false; - switch (i) { - case 0: - behaviour = B43legacy_LED_ACTIVITY; - activelow = true; - if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ) - behaviour = B43legacy_LED_RADIO_ALL; - break; - case 1: - behaviour = B43legacy_LED_RADIO_B; - if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK) - behaviour = B43legacy_LED_ASSOC; - break; - case 2: - behaviour = B43legacy_LED_RADIO_A; - break; - case 3: - behaviour = B43legacy_LED_OFF; - break; - default: - B43legacy_WARN_ON(1); - return; - } - } else { - behaviour = sprom[i] & B43legacy_LED_BEHAVIOUR; - activelow = !!(sprom[i] & B43legacy_LED_ACTIVELOW); - } - b43legacy_map_led(dev, i, behaviour, activelow); - } -} - -void b43legacy_leds_exit(struct b43legacy_wldev *dev) -{ - b43legacy_unregister_led(&dev->led_tx); - b43legacy_unregister_led(&dev->led_rx); - b43legacy_unregister_led(&dev->led_assoc); - b43legacy_unregister_led(&dev->led_radio); -} diff --git a/drivers/net/wireless/b43legacy/leds.h b/drivers/net/wireless/b43legacy/leds.h deleted file mode 100644 index 9ff6750dc57f..000000000000 --- a/drivers/net/wireless/b43legacy/leds.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef B43legacy_LEDS_H_ -#define B43legacy_LEDS_H_ - -struct b43legacy_wldev; - -#ifdef CONFIG_B43LEGACY_LEDS - -#include -#include - - -#define B43legacy_LED_MAX_NAME_LEN 31 - -struct b43legacy_led { - struct b43legacy_wldev *dev; - /* The LED class device */ - struct led_classdev led_dev; - /* The index number of the LED. */ - u8 index; - /* If activelow is true, the LED is ON if the - * bit is switched off. */ - bool activelow; - /* The unique name string for this LED device. */ - char name[B43legacy_LED_MAX_NAME_LEN + 1]; -}; - -#define B43legacy_LED_BEHAVIOUR 0x7F -#define B43legacy_LED_ACTIVELOW 0x80 -/* LED behaviour values */ -enum b43legacy_led_behaviour { - B43legacy_LED_OFF, - B43legacy_LED_ON, - B43legacy_LED_ACTIVITY, - B43legacy_LED_RADIO_ALL, - B43legacy_LED_RADIO_A, - B43legacy_LED_RADIO_B, - B43legacy_LED_MODE_BG, - B43legacy_LED_TRANSFER, - B43legacy_LED_APTRANSFER, - B43legacy_LED_WEIRD, - B43legacy_LED_ASSOC, - B43legacy_LED_INACTIVE, -}; - -void b43legacy_leds_init(struct b43legacy_wldev *dev); -void b43legacy_leds_exit(struct b43legacy_wldev *dev); - -#else /* CONFIG_B43LEGACY_LEDS */ -/* LED support disabled */ - -struct b43legacy_led { - /* empty */ -}; - -static inline void b43legacy_leds_init(struct b43legacy_wldev *dev) -{ -} -static inline void b43legacy_leds_exit(struct b43legacy_wldev *dev) -{ -} -#endif /* CONFIG_B43LEGACY_LEDS */ - -#endif /* B43legacy_LEDS_H_ */ diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c deleted file mode 100644 index afc1fb3e38df..000000000000 --- a/drivers/net/wireless/b43legacy/main.c +++ /dev/null @@ -1,4062 +0,0 @@ -/* - * - * Broadcom B43legacy wireless driver - * - * Copyright (c) 2005 Martin Langer - * Copyright (c) 2005-2008 Stefano Brivio - * Copyright (c) 2005, 2006 Michael Buesch - * Copyright (c) 2005 Danny van Dyk - * Copyright (c) 2005 Andreas Jaggi - * Copyright (c) 2007 Larry Finger - * - * Some parts of the code in this file are derived from the ipw2200 - * driver Copyright(c) 2003 - 2004 Intel Corporation. - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "b43legacy.h" -#include "main.h" -#include "debugfs.h" -#include "phy.h" -#include "dma.h" -#include "pio.h" -#include "sysfs.h" -#include "xmit.h" -#include "radio.h" - - -MODULE_DESCRIPTION("Broadcom B43legacy wireless driver"); -MODULE_AUTHOR("Martin Langer"); -MODULE_AUTHOR("Stefano Brivio"); -MODULE_AUTHOR("Michael Buesch"); -MODULE_LICENSE("GPL"); - -MODULE_FIRMWARE("b43legacy/ucode2.fw"); -MODULE_FIRMWARE("b43legacy/ucode4.fw"); - -#if defined(CONFIG_B43LEGACY_DMA) && defined(CONFIG_B43LEGACY_PIO) -static int modparam_pio; -module_param_named(pio, modparam_pio, int, 0444); -MODULE_PARM_DESC(pio, "enable(1) / disable(0) PIO mode"); -#elif defined(CONFIG_B43LEGACY_DMA) -# define modparam_pio 0 -#elif defined(CONFIG_B43LEGACY_PIO) -# define modparam_pio 1 -#endif - -static int modparam_bad_frames_preempt; -module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); -MODULE_PARM_DESC(bad_frames_preempt, "enable(1) / disable(0) Bad Frames" - " Preemption"); - -static char modparam_fwpostfix[16]; -module_param_string(fwpostfix, modparam_fwpostfix, 16, 0444); -MODULE_PARM_DESC(fwpostfix, "Postfix for the firmware files to load."); - -/* The following table supports BCM4301, BCM4303 and BCM4306/2 devices. */ -static const struct ssb_device_id b43legacy_ssb_tbl[] = { - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 2), - SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 4), - {}, -}; -MODULE_DEVICE_TABLE(ssb, b43legacy_ssb_tbl); - - -/* Channel and ratetables are shared for all devices. - * They can't be const, because ieee80211 puts some precalculated - * data in there. This data is the same for all devices, so we don't - * get concurrency issues */ -#define RATETAB_ENT(_rateid, _flags) \ - { \ - .bitrate = B43legacy_RATE_TO_100KBPS(_rateid), \ - .hw_value = (_rateid), \ - .flags = (_flags), \ - } -/* - * NOTE: When changing this, sync with xmit.c's - * b43legacy_plcp_get_bitrate_idx_* functions! - */ -static struct ieee80211_rate __b43legacy_ratetable[] = { - RATETAB_ENT(B43legacy_CCK_RATE_1MB, 0), - RATETAB_ENT(B43legacy_CCK_RATE_2MB, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(B43legacy_CCK_RATE_5MB, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(B43legacy_CCK_RATE_11MB, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(B43legacy_OFDM_RATE_6MB, 0), - RATETAB_ENT(B43legacy_OFDM_RATE_9MB, 0), - RATETAB_ENT(B43legacy_OFDM_RATE_12MB, 0), - RATETAB_ENT(B43legacy_OFDM_RATE_18MB, 0), - RATETAB_ENT(B43legacy_OFDM_RATE_24MB, 0), - RATETAB_ENT(B43legacy_OFDM_RATE_36MB, 0), - RATETAB_ENT(B43legacy_OFDM_RATE_48MB, 0), - RATETAB_ENT(B43legacy_OFDM_RATE_54MB, 0), -}; -#define b43legacy_b_ratetable (__b43legacy_ratetable + 0) -#define b43legacy_b_ratetable_size 4 -#define b43legacy_g_ratetable (__b43legacy_ratetable + 0) -#define b43legacy_g_ratetable_size 12 - -#define CHANTAB_ENT(_chanid, _freq) \ - { \ - .center_freq = (_freq), \ - .hw_value = (_chanid), \ - } -static struct ieee80211_channel b43legacy_bg_chantable[] = { - CHANTAB_ENT(1, 2412), - CHANTAB_ENT(2, 2417), - CHANTAB_ENT(3, 2422), - CHANTAB_ENT(4, 2427), - CHANTAB_ENT(5, 2432), - CHANTAB_ENT(6, 2437), - CHANTAB_ENT(7, 2442), - CHANTAB_ENT(8, 2447), - CHANTAB_ENT(9, 2452), - CHANTAB_ENT(10, 2457), - CHANTAB_ENT(11, 2462), - CHANTAB_ENT(12, 2467), - CHANTAB_ENT(13, 2472), - CHANTAB_ENT(14, 2484), -}; - -static struct ieee80211_supported_band b43legacy_band_2GHz_BPHY = { - .channels = b43legacy_bg_chantable, - .n_channels = ARRAY_SIZE(b43legacy_bg_chantable), - .bitrates = b43legacy_b_ratetable, - .n_bitrates = b43legacy_b_ratetable_size, -}; - -static struct ieee80211_supported_band b43legacy_band_2GHz_GPHY = { - .channels = b43legacy_bg_chantable, - .n_channels = ARRAY_SIZE(b43legacy_bg_chantable), - .bitrates = b43legacy_g_ratetable, - .n_bitrates = b43legacy_g_ratetable_size, -}; - -static void b43legacy_wireless_core_exit(struct b43legacy_wldev *dev); -static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev); -static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev); -static int b43legacy_wireless_core_start(struct b43legacy_wldev *dev); - - -static int b43legacy_ratelimit(struct b43legacy_wl *wl) -{ - if (!wl || !wl->current_dev) - return 1; - if (b43legacy_status(wl->current_dev) < B43legacy_STAT_STARTED) - return 1; - /* We are up and running. - * Ratelimit the messages to avoid DoS over the net. */ - return net_ratelimit(); -} - -void b43legacyinfo(struct b43legacy_wl *wl, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - if (!b43legacy_ratelimit(wl)) - return; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - printk(KERN_INFO "b43legacy-%s: %pV", - (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); - - va_end(args); -} - -void b43legacyerr(struct b43legacy_wl *wl, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - if (!b43legacy_ratelimit(wl)) - return; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - printk(KERN_ERR "b43legacy-%s ERROR: %pV", - (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); - - va_end(args); -} - -void b43legacywarn(struct b43legacy_wl *wl, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - if (!b43legacy_ratelimit(wl)) - return; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - printk(KERN_WARNING "b43legacy-%s warning: %pV", - (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); - - va_end(args); -} - -#if B43legacy_DEBUG -void b43legacydbg(struct b43legacy_wl *wl, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - printk(KERN_DEBUG "b43legacy-%s debug: %pV", - (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); - - va_end(args); -} -#endif /* DEBUG */ - -static void b43legacy_ram_write(struct b43legacy_wldev *dev, u16 offset, - u32 val) -{ - u32 status; - - B43legacy_WARN_ON(offset % 4 != 0); - - status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - if (status & B43legacy_MACCTL_BE) - val = swab32(val); - - b43legacy_write32(dev, B43legacy_MMIO_RAM_CONTROL, offset); - mmiowb(); - b43legacy_write32(dev, B43legacy_MMIO_RAM_DATA, val); -} - -static inline -void b43legacy_shm_control_word(struct b43legacy_wldev *dev, - u16 routing, u16 offset) -{ - u32 control; - - /* "offset" is the WORD offset. */ - - control = routing; - control <<= 16; - control |= offset; - b43legacy_write32(dev, B43legacy_MMIO_SHM_CONTROL, control); -} - -u32 b43legacy_shm_read32(struct b43legacy_wldev *dev, - u16 routing, u16 offset) -{ - u32 ret; - - if (routing == B43legacy_SHM_SHARED) { - B43legacy_WARN_ON((offset & 0x0001) != 0); - if (offset & 0x0003) { - /* Unaligned access */ - b43legacy_shm_control_word(dev, routing, offset >> 2); - ret = b43legacy_read16(dev, - B43legacy_MMIO_SHM_DATA_UNALIGNED); - ret <<= 16; - b43legacy_shm_control_word(dev, routing, - (offset >> 2) + 1); - ret |= b43legacy_read16(dev, B43legacy_MMIO_SHM_DATA); - - return ret; - } - offset >>= 2; - } - b43legacy_shm_control_word(dev, routing, offset); - ret = b43legacy_read32(dev, B43legacy_MMIO_SHM_DATA); - - return ret; -} - -u16 b43legacy_shm_read16(struct b43legacy_wldev *dev, - u16 routing, u16 offset) -{ - u16 ret; - - if (routing == B43legacy_SHM_SHARED) { - B43legacy_WARN_ON((offset & 0x0001) != 0); - if (offset & 0x0003) { - /* Unaligned access */ - b43legacy_shm_control_word(dev, routing, offset >> 2); - ret = b43legacy_read16(dev, - B43legacy_MMIO_SHM_DATA_UNALIGNED); - - return ret; - } - offset >>= 2; - } - b43legacy_shm_control_word(dev, routing, offset); - ret = b43legacy_read16(dev, B43legacy_MMIO_SHM_DATA); - - return ret; -} - -void b43legacy_shm_write32(struct b43legacy_wldev *dev, - u16 routing, u16 offset, - u32 value) -{ - if (routing == B43legacy_SHM_SHARED) { - B43legacy_WARN_ON((offset & 0x0001) != 0); - if (offset & 0x0003) { - /* Unaligned access */ - b43legacy_shm_control_word(dev, routing, offset >> 2); - mmiowb(); - b43legacy_write16(dev, - B43legacy_MMIO_SHM_DATA_UNALIGNED, - (value >> 16) & 0xffff); - mmiowb(); - b43legacy_shm_control_word(dev, routing, - (offset >> 2) + 1); - mmiowb(); - b43legacy_write16(dev, B43legacy_MMIO_SHM_DATA, - value & 0xffff); - return; - } - offset >>= 2; - } - b43legacy_shm_control_word(dev, routing, offset); - mmiowb(); - b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, value); -} - -void b43legacy_shm_write16(struct b43legacy_wldev *dev, u16 routing, u16 offset, - u16 value) -{ - if (routing == B43legacy_SHM_SHARED) { - B43legacy_WARN_ON((offset & 0x0001) != 0); - if (offset & 0x0003) { - /* Unaligned access */ - b43legacy_shm_control_word(dev, routing, offset >> 2); - mmiowb(); - b43legacy_write16(dev, - B43legacy_MMIO_SHM_DATA_UNALIGNED, - value); - return; - } - offset >>= 2; - } - b43legacy_shm_control_word(dev, routing, offset); - mmiowb(); - b43legacy_write16(dev, B43legacy_MMIO_SHM_DATA, value); -} - -/* Read HostFlags */ -u32 b43legacy_hf_read(struct b43legacy_wldev *dev) -{ - u32 ret; - - ret = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_HOSTFHI); - ret <<= 16; - ret |= b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_HOSTFLO); - - return ret; -} - -/* Write HostFlags */ -void b43legacy_hf_write(struct b43legacy_wldev *dev, u32 value) -{ - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_HOSTFLO, - (value & 0x0000FFFF)); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_HOSTFHI, - ((value & 0xFFFF0000) >> 16)); -} - -void b43legacy_tsf_read(struct b43legacy_wldev *dev, u64 *tsf) -{ - /* We need to be careful. As we read the TSF from multiple - * registers, we should take care of register overflows. - * In theory, the whole tsf read process should be atomic. - * We try to be atomic here, by restaring the read process, - * if any of the high registers changed (overflew). - */ - if (dev->dev->id.revision >= 3) { - u32 low; - u32 high; - u32 high2; - - do { - high = b43legacy_read32(dev, - B43legacy_MMIO_REV3PLUS_TSF_HIGH); - low = b43legacy_read32(dev, - B43legacy_MMIO_REV3PLUS_TSF_LOW); - high2 = b43legacy_read32(dev, - B43legacy_MMIO_REV3PLUS_TSF_HIGH); - } while (unlikely(high != high2)); - - *tsf = high; - *tsf <<= 32; - *tsf |= low; - } else { - u64 tmp; - u16 v0; - u16 v1; - u16 v2; - u16 v3; - u16 test1; - u16 test2; - u16 test3; - - do { - v3 = b43legacy_read16(dev, B43legacy_MMIO_TSF_3); - v2 = b43legacy_read16(dev, B43legacy_MMIO_TSF_2); - v1 = b43legacy_read16(dev, B43legacy_MMIO_TSF_1); - v0 = b43legacy_read16(dev, B43legacy_MMIO_TSF_0); - - test3 = b43legacy_read16(dev, B43legacy_MMIO_TSF_3); - test2 = b43legacy_read16(dev, B43legacy_MMIO_TSF_2); - test1 = b43legacy_read16(dev, B43legacy_MMIO_TSF_1); - } while (v3 != test3 || v2 != test2 || v1 != test1); - - *tsf = v3; - *tsf <<= 48; - tmp = v2; - tmp <<= 32; - *tsf |= tmp; - tmp = v1; - tmp <<= 16; - *tsf |= tmp; - *tsf |= v0; - } -} - -static void b43legacy_time_lock(struct b43legacy_wldev *dev) -{ - u32 status; - - status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - status |= B43legacy_MACCTL_TBTTHOLD; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); - mmiowb(); -} - -static void b43legacy_time_unlock(struct b43legacy_wldev *dev) -{ - u32 status; - - status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - status &= ~B43legacy_MACCTL_TBTTHOLD; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); -} - -static void b43legacy_tsf_write_locked(struct b43legacy_wldev *dev, u64 tsf) -{ - /* Be careful with the in-progress timer. - * First zero out the low register, so we have a full - * register-overflow duration to complete the operation. - */ - if (dev->dev->id.revision >= 3) { - u32 lo = (tsf & 0x00000000FFFFFFFFULL); - u32 hi = (tsf & 0xFFFFFFFF00000000ULL) >> 32; - - b43legacy_write32(dev, B43legacy_MMIO_REV3PLUS_TSF_LOW, 0); - mmiowb(); - b43legacy_write32(dev, B43legacy_MMIO_REV3PLUS_TSF_HIGH, - hi); - mmiowb(); - b43legacy_write32(dev, B43legacy_MMIO_REV3PLUS_TSF_LOW, - lo); - } else { - u16 v0 = (tsf & 0x000000000000FFFFULL); - u16 v1 = (tsf & 0x00000000FFFF0000ULL) >> 16; - u16 v2 = (tsf & 0x0000FFFF00000000ULL) >> 32; - u16 v3 = (tsf & 0xFFFF000000000000ULL) >> 48; - - b43legacy_write16(dev, B43legacy_MMIO_TSF_0, 0); - mmiowb(); - b43legacy_write16(dev, B43legacy_MMIO_TSF_3, v3); - mmiowb(); - b43legacy_write16(dev, B43legacy_MMIO_TSF_2, v2); - mmiowb(); - b43legacy_write16(dev, B43legacy_MMIO_TSF_1, v1); - mmiowb(); - b43legacy_write16(dev, B43legacy_MMIO_TSF_0, v0); - } -} - -void b43legacy_tsf_write(struct b43legacy_wldev *dev, u64 tsf) -{ - b43legacy_time_lock(dev); - b43legacy_tsf_write_locked(dev, tsf); - b43legacy_time_unlock(dev); -} - -static -void b43legacy_macfilter_set(struct b43legacy_wldev *dev, - u16 offset, const u8 *mac) -{ - static const u8 zero_addr[ETH_ALEN] = { 0 }; - u16 data; - - if (!mac) - mac = zero_addr; - - offset |= 0x0020; - b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_CONTROL, offset); - - data = mac[0]; - data |= mac[1] << 8; - b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_DATA, data); - data = mac[2]; - data |= mac[3] << 8; - b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_DATA, data); - data = mac[4]; - data |= mac[5] << 8; - b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_DATA, data); -} - -static void b43legacy_write_mac_bssid_templates(struct b43legacy_wldev *dev) -{ - static const u8 zero_addr[ETH_ALEN] = { 0 }; - const u8 *mac = dev->wl->mac_addr; - const u8 *bssid = dev->wl->bssid; - u8 mac_bssid[ETH_ALEN * 2]; - int i; - u32 tmp; - - if (!bssid) - bssid = zero_addr; - if (!mac) - mac = zero_addr; - - b43legacy_macfilter_set(dev, B43legacy_MACFILTER_BSSID, bssid); - - memcpy(mac_bssid, mac, ETH_ALEN); - memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN); - - /* Write our MAC address and BSSID to template ram */ - for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) { - tmp = (u32)(mac_bssid[i + 0]); - tmp |= (u32)(mac_bssid[i + 1]) << 8; - tmp |= (u32)(mac_bssid[i + 2]) << 16; - tmp |= (u32)(mac_bssid[i + 3]) << 24; - b43legacy_ram_write(dev, 0x20 + i, tmp); - b43legacy_ram_write(dev, 0x78 + i, tmp); - b43legacy_ram_write(dev, 0x478 + i, tmp); - } -} - -static void b43legacy_upload_card_macaddress(struct b43legacy_wldev *dev) -{ - b43legacy_write_mac_bssid_templates(dev); - b43legacy_macfilter_set(dev, B43legacy_MACFILTER_SELF, - dev->wl->mac_addr); -} - -static void b43legacy_set_slot_time(struct b43legacy_wldev *dev, - u16 slot_time) -{ - /* slot_time is in usec. */ - if (dev->phy.type != B43legacy_PHYTYPE_G) - return; - b43legacy_write16(dev, 0x684, 510 + slot_time); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0010, - slot_time); -} - -static void b43legacy_short_slot_timing_enable(struct b43legacy_wldev *dev) -{ - b43legacy_set_slot_time(dev, 9); -} - -static void b43legacy_short_slot_timing_disable(struct b43legacy_wldev *dev) -{ - b43legacy_set_slot_time(dev, 20); -} - -/* Synchronize IRQ top- and bottom-half. - * IRQs must be masked before calling this. - * This must not be called with the irq_lock held. - */ -static void b43legacy_synchronize_irq(struct b43legacy_wldev *dev) -{ - synchronize_irq(dev->dev->irq); - tasklet_kill(&dev->isr_tasklet); -} - -/* DummyTransmission function, as documented on - * http://bcm-specs.sipsolutions.net/DummyTransmission - */ -void b43legacy_dummy_transmission(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - unsigned int i; - unsigned int max_loop; - u16 value; - u32 buffer[5] = { - 0x00000000, - 0x00D40000, - 0x00000000, - 0x01000000, - 0x00000000, - }; - - switch (phy->type) { - case B43legacy_PHYTYPE_B: - case B43legacy_PHYTYPE_G: - max_loop = 0xFA; - buffer[0] = 0x000B846E; - break; - default: - B43legacy_BUG_ON(1); - return; - } - - for (i = 0; i < 5; i++) - b43legacy_ram_write(dev, i * 4, buffer[i]); - - /* dummy read follows */ - b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - - b43legacy_write16(dev, 0x0568, 0x0000); - b43legacy_write16(dev, 0x07C0, 0x0000); - b43legacy_write16(dev, 0x050C, 0x0000); - b43legacy_write16(dev, 0x0508, 0x0000); - b43legacy_write16(dev, 0x050A, 0x0000); - b43legacy_write16(dev, 0x054C, 0x0000); - b43legacy_write16(dev, 0x056A, 0x0014); - b43legacy_write16(dev, 0x0568, 0x0826); - b43legacy_write16(dev, 0x0500, 0x0000); - b43legacy_write16(dev, 0x0502, 0x0030); - - if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) - b43legacy_radio_write16(dev, 0x0051, 0x0017); - for (i = 0x00; i < max_loop; i++) { - value = b43legacy_read16(dev, 0x050E); - if (value & 0x0080) - break; - udelay(10); - } - for (i = 0x00; i < 0x0A; i++) { - value = b43legacy_read16(dev, 0x050E); - if (value & 0x0400) - break; - udelay(10); - } - for (i = 0x00; i < 0x0A; i++) { - value = b43legacy_read16(dev, 0x0690); - if (!(value & 0x0100)) - break; - udelay(10); - } - if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) - b43legacy_radio_write16(dev, 0x0051, 0x0037); -} - -/* Turn the Analog ON/OFF */ -static void b43legacy_switch_analog(struct b43legacy_wldev *dev, int on) -{ - b43legacy_write16(dev, B43legacy_MMIO_PHY0, on ? 0 : 0xF4); -} - -void b43legacy_wireless_core_reset(struct b43legacy_wldev *dev, u32 flags) -{ - u32 tmslow; - u32 macctl; - - flags |= B43legacy_TMSLOW_PHYCLKEN; - flags |= B43legacy_TMSLOW_PHYRESET; - ssb_device_enable(dev->dev, flags); - msleep(2); /* Wait for the PLL to turn on. */ - - /* Now take the PHY out of Reset again */ - tmslow = ssb_read32(dev->dev, SSB_TMSLOW); - tmslow |= SSB_TMSLOW_FGC; - tmslow &= ~B43legacy_TMSLOW_PHYRESET; - ssb_write32(dev->dev, SSB_TMSLOW, tmslow); - ssb_read32(dev->dev, SSB_TMSLOW); /* flush */ - msleep(1); - tmslow &= ~SSB_TMSLOW_FGC; - ssb_write32(dev->dev, SSB_TMSLOW, tmslow); - ssb_read32(dev->dev, SSB_TMSLOW); /* flush */ - msleep(1); - - /* Turn Analog ON */ - b43legacy_switch_analog(dev, 1); - - macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - macctl &= ~B43legacy_MACCTL_GMODE; - if (flags & B43legacy_TMSLOW_GMODE) { - macctl |= B43legacy_MACCTL_GMODE; - dev->phy.gmode = true; - } else - dev->phy.gmode = false; - macctl |= B43legacy_MACCTL_IHR_ENABLED; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); -} - -static void handle_irq_transmit_status(struct b43legacy_wldev *dev) -{ - u32 v0; - u32 v1; - u16 tmp; - struct b43legacy_txstatus stat; - - while (1) { - v0 = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_0); - if (!(v0 & 0x00000001)) - break; - v1 = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_1); - - stat.cookie = (v0 >> 16); - stat.seq = (v1 & 0x0000FFFF); - stat.phy_stat = ((v1 & 0x00FF0000) >> 16); - tmp = (v0 & 0x0000FFFF); - stat.frame_count = ((tmp & 0xF000) >> 12); - stat.rts_count = ((tmp & 0x0F00) >> 8); - stat.supp_reason = ((tmp & 0x001C) >> 2); - stat.pm_indicated = !!(tmp & 0x0080); - stat.intermediate = !!(tmp & 0x0040); - stat.for_ampdu = !!(tmp & 0x0020); - stat.acked = !!(tmp & 0x0002); - - b43legacy_handle_txstatus(dev, &stat); - } -} - -static void drain_txstatus_queue(struct b43legacy_wldev *dev) -{ - u32 dummy; - - if (dev->dev->id.revision < 5) - return; - /* Read all entries from the microcode TXstatus FIFO - * and throw them away. - */ - while (1) { - dummy = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_0); - if (!(dummy & 0x00000001)) - break; - dummy = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_1); - } -} - -static u32 b43legacy_jssi_read(struct b43legacy_wldev *dev) -{ - u32 val = 0; - - val = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x40A); - val <<= 16; - val |= b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x408); - - return val; -} - -static void b43legacy_jssi_write(struct b43legacy_wldev *dev, u32 jssi) -{ - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x408, - (jssi & 0x0000FFFF)); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x40A, - (jssi & 0xFFFF0000) >> 16); -} - -static void b43legacy_generate_noise_sample(struct b43legacy_wldev *dev) -{ - b43legacy_jssi_write(dev, 0x7F7F7F7F); - b43legacy_write32(dev, B43legacy_MMIO_MACCMD, - b43legacy_read32(dev, B43legacy_MMIO_MACCMD) - | B43legacy_MACCMD_BGNOISE); - B43legacy_WARN_ON(dev->noisecalc.channel_at_start != - dev->phy.channel); -} - -static void b43legacy_calculate_link_quality(struct b43legacy_wldev *dev) -{ - /* Top half of Link Quality calculation. */ - - if (dev->noisecalc.calculation_running) - return; - dev->noisecalc.channel_at_start = dev->phy.channel; - dev->noisecalc.calculation_running = true; - dev->noisecalc.nr_samples = 0; - - b43legacy_generate_noise_sample(dev); -} - -static void handle_irq_noise(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 tmp; - u8 noise[4]; - u8 i; - u8 j; - s32 average; - - /* Bottom half of Link Quality calculation. */ - - B43legacy_WARN_ON(!dev->noisecalc.calculation_running); - if (dev->noisecalc.channel_at_start != phy->channel) - goto drop_calculation; - *((__le32 *)noise) = cpu_to_le32(b43legacy_jssi_read(dev)); - if (noise[0] == 0x7F || noise[1] == 0x7F || - noise[2] == 0x7F || noise[3] == 0x7F) - goto generate_new; - - /* Get the noise samples. */ - B43legacy_WARN_ON(dev->noisecalc.nr_samples >= 8); - i = dev->noisecalc.nr_samples; - noise[0] = clamp_val(noise[0], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); - noise[1] = clamp_val(noise[1], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); - noise[2] = clamp_val(noise[2], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); - noise[3] = clamp_val(noise[3], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); - dev->noisecalc.samples[i][0] = phy->nrssi_lt[noise[0]]; - dev->noisecalc.samples[i][1] = phy->nrssi_lt[noise[1]]; - dev->noisecalc.samples[i][2] = phy->nrssi_lt[noise[2]]; - dev->noisecalc.samples[i][3] = phy->nrssi_lt[noise[3]]; - dev->noisecalc.nr_samples++; - if (dev->noisecalc.nr_samples == 8) { - /* Calculate the Link Quality by the noise samples. */ - average = 0; - for (i = 0; i < 8; i++) { - for (j = 0; j < 4; j++) - average += dev->noisecalc.samples[i][j]; - } - average /= (8 * 4); - average *= 125; - average += 64; - average /= 128; - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - 0x40C); - tmp = (tmp / 128) & 0x1F; - if (tmp >= 8) - average += 2; - else - average -= 25; - if (tmp == 8) - average -= 72; - else - average -= 48; - - dev->stats.link_noise = average; -drop_calculation: - dev->noisecalc.calculation_running = false; - return; - } -generate_new: - b43legacy_generate_noise_sample(dev); -} - -static void handle_irq_tbtt_indication(struct b43legacy_wldev *dev) -{ - if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_AP)) { - /* TODO: PS TBTT */ - } else { - if (1/*FIXME: the last PSpoll frame was sent successfully */) - b43legacy_power_saving_ctl_bits(dev, -1, -1); - } - if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) - dev->dfq_valid = true; -} - -static void handle_irq_atim_end(struct b43legacy_wldev *dev) -{ - if (dev->dfq_valid) { - b43legacy_write32(dev, B43legacy_MMIO_MACCMD, - b43legacy_read32(dev, B43legacy_MMIO_MACCMD) - | B43legacy_MACCMD_DFQ_VALID); - dev->dfq_valid = false; - } -} - -static void handle_irq_pmq(struct b43legacy_wldev *dev) -{ - u32 tmp; - - /* TODO: AP mode. */ - - while (1) { - tmp = b43legacy_read32(dev, B43legacy_MMIO_PS_STATUS); - if (!(tmp & 0x00000008)) - break; - } - /* 16bit write is odd, but correct. */ - b43legacy_write16(dev, B43legacy_MMIO_PS_STATUS, 0x0002); -} - -static void b43legacy_write_template_common(struct b43legacy_wldev *dev, - const u8 *data, u16 size, - u16 ram_offset, - u16 shm_size_offset, u8 rate) -{ - u32 i; - u32 tmp; - struct b43legacy_plcp_hdr4 plcp; - - plcp.data = 0; - b43legacy_generate_plcp_hdr(&plcp, size + FCS_LEN, rate); - b43legacy_ram_write(dev, ram_offset, le32_to_cpu(plcp.data)); - ram_offset += sizeof(u32); - /* The PLCP is 6 bytes long, but we only wrote 4 bytes, yet. - * So leave the first two bytes of the next write blank. - */ - tmp = (u32)(data[0]) << 16; - tmp |= (u32)(data[1]) << 24; - b43legacy_ram_write(dev, ram_offset, tmp); - ram_offset += sizeof(u32); - for (i = 2; i < size; i += sizeof(u32)) { - tmp = (u32)(data[i + 0]); - if (i + 1 < size) - tmp |= (u32)(data[i + 1]) << 8; - if (i + 2 < size) - tmp |= (u32)(data[i + 2]) << 16; - if (i + 3 < size) - tmp |= (u32)(data[i + 3]) << 24; - b43legacy_ram_write(dev, ram_offset + i - 2, tmp); - } - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_size_offset, - size + sizeof(struct b43legacy_plcp_hdr6)); -} - -/* Convert a b43legacy antenna number value to the PHY TX control value. */ -static u16 b43legacy_antenna_to_phyctl(int antenna) -{ - switch (antenna) { - case B43legacy_ANTENNA0: - return B43legacy_TX4_PHY_ANT0; - case B43legacy_ANTENNA1: - return B43legacy_TX4_PHY_ANT1; - } - return B43legacy_TX4_PHY_ANTLAST; -} - -static void b43legacy_write_beacon_template(struct b43legacy_wldev *dev, - u16 ram_offset, - u16 shm_size_offset) -{ - - unsigned int i, len, variable_len; - const struct ieee80211_mgmt *bcn; - const u8 *ie; - bool tim_found = false; - unsigned int rate; - u16 ctl; - int antenna; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon); - - bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data); - len = min_t(size_t, dev->wl->current_beacon->len, - 0x200 - sizeof(struct b43legacy_plcp_hdr6)); - rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value; - - b43legacy_write_template_common(dev, (const u8 *)bcn, len, ram_offset, - shm_size_offset, rate); - - /* Write the PHY TX control parameters. */ - antenna = B43legacy_ANTENNA_DEFAULT; - antenna = b43legacy_antenna_to_phyctl(antenna); - ctl = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_BEACPHYCTL); - /* We can't send beacons with short preamble. Would get PHY errors. */ - ctl &= ~B43legacy_TX4_PHY_SHORTPRMBL; - ctl &= ~B43legacy_TX4_PHY_ANT; - ctl &= ~B43legacy_TX4_PHY_ENC; - ctl |= antenna; - ctl |= B43legacy_TX4_PHY_ENC_CCK; - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_BEACPHYCTL, ctl); - - /* Find the position of the TIM and the DTIM_period value - * and write them to SHM. */ - ie = bcn->u.beacon.variable; - variable_len = len - offsetof(struct ieee80211_mgmt, u.beacon.variable); - for (i = 0; i < variable_len - 2; ) { - uint8_t ie_id, ie_len; - - ie_id = ie[i]; - ie_len = ie[i + 1]; - if (ie_id == 5) { - u16 tim_position; - u16 dtim_period; - /* This is the TIM Information Element */ - - /* Check whether the ie_len is in the beacon data range. */ - if (variable_len < ie_len + 2 + i) - break; - /* A valid TIM is at least 4 bytes long. */ - if (ie_len < 4) - break; - tim_found = true; - - tim_position = sizeof(struct b43legacy_plcp_hdr6); - tim_position += offsetof(struct ieee80211_mgmt, - u.beacon.variable); - tim_position += i; - - dtim_period = ie[i + 3]; - - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_TIMPOS, tim_position); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_DTIMP, dtim_period); - break; - } - i += ie_len + 2; - } - if (!tim_found) { - b43legacywarn(dev->wl, "Did not find a valid TIM IE in the " - "beacon template packet. AP or IBSS operation " - "may be broken.\n"); - } else - b43legacydbg(dev->wl, "Updated beacon template\n"); -} - -static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev, - u16 shm_offset, u16 size, - struct ieee80211_rate *rate) -{ - struct b43legacy_plcp_hdr4 plcp; - u32 tmp; - __le16 dur; - - plcp.data = 0; - b43legacy_generate_plcp_hdr(&plcp, size + FCS_LEN, rate->hw_value); - dur = ieee80211_generic_frame_duration(dev->wl->hw, - dev->wl->vif, - IEEE80211_BAND_2GHZ, - size, - rate); - /* Write PLCP in two parts and timing for packet transfer */ - tmp = le32_to_cpu(plcp.data); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_offset, - tmp & 0xFFFF); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_offset + 2, - tmp >> 16); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_offset + 6, - le16_to_cpu(dur)); -} - -/* Instead of using custom probe response template, this function - * just patches custom beacon template by: - * 1) Changing packet type - * 2) Patching duration field - * 3) Stripping TIM - */ -static const u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev, - u16 *dest_size, - struct ieee80211_rate *rate) -{ - const u8 *src_data; - u8 *dest_data; - u16 src_size, elem_size, src_pos, dest_pos; - __le16 dur; - struct ieee80211_hdr *hdr; - size_t ie_start; - - src_size = dev->wl->current_beacon->len; - src_data = (const u8 *)dev->wl->current_beacon->data; - - /* Get the start offset of the variable IEs in the packet. */ - ie_start = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - B43legacy_WARN_ON(ie_start != offsetof(struct ieee80211_mgmt, - u.beacon.variable)); - - if (B43legacy_WARN_ON(src_size < ie_start)) - return NULL; - - dest_data = kmalloc(src_size, GFP_ATOMIC); - if (unlikely(!dest_data)) - return NULL; - - /* Copy the static data and all Information Elements, except the TIM. */ - memcpy(dest_data, src_data, ie_start); - src_pos = ie_start; - dest_pos = ie_start; - for ( ; src_pos < src_size - 2; src_pos += elem_size) { - elem_size = src_data[src_pos + 1] + 2; - if (src_data[src_pos] == 5) { - /* This is the TIM. */ - continue; - } - memcpy(dest_data + dest_pos, src_data + src_pos, elem_size); - dest_pos += elem_size; - } - *dest_size = dest_pos; - hdr = (struct ieee80211_hdr *)dest_data; - - /* Set the frame control. */ - hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_PROBE_RESP); - dur = ieee80211_generic_frame_duration(dev->wl->hw, - dev->wl->vif, - IEEE80211_BAND_2GHZ, - *dest_size, - rate); - hdr->duration_id = dur; - - return dest_data; -} - -static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev, - u16 ram_offset, - u16 shm_size_offset, - struct ieee80211_rate *rate) -{ - const u8 *probe_resp_data; - u16 size; - - size = dev->wl->current_beacon->len; - probe_resp_data = b43legacy_generate_probe_resp(dev, &size, rate); - if (unlikely(!probe_resp_data)) - return; - - /* Looks like PLCP headers plus packet timings are stored for - * all possible basic rates - */ - b43legacy_write_probe_resp_plcp(dev, 0x31A, size, - &b43legacy_b_ratetable[0]); - b43legacy_write_probe_resp_plcp(dev, 0x32C, size, - &b43legacy_b_ratetable[1]); - b43legacy_write_probe_resp_plcp(dev, 0x33E, size, - &b43legacy_b_ratetable[2]); - b43legacy_write_probe_resp_plcp(dev, 0x350, size, - &b43legacy_b_ratetable[3]); - - size = min_t(size_t, size, - 0x200 - sizeof(struct b43legacy_plcp_hdr6)); - b43legacy_write_template_common(dev, probe_resp_data, - size, ram_offset, - shm_size_offset, rate->hw_value); - kfree(probe_resp_data); -} - -static void b43legacy_upload_beacon0(struct b43legacy_wldev *dev) -{ - struct b43legacy_wl *wl = dev->wl; - - if (wl->beacon0_uploaded) - return; - b43legacy_write_beacon_template(dev, 0x68, 0x18); - /* FIXME: Probe resp upload doesn't really belong here, - * but we don't use that feature anyway. */ - b43legacy_write_probe_resp_template(dev, 0x268, 0x4A, - &__b43legacy_ratetable[3]); - wl->beacon0_uploaded = true; -} - -static void b43legacy_upload_beacon1(struct b43legacy_wldev *dev) -{ - struct b43legacy_wl *wl = dev->wl; - - if (wl->beacon1_uploaded) - return; - b43legacy_write_beacon_template(dev, 0x468, 0x1A); - wl->beacon1_uploaded = true; -} - -static void handle_irq_beacon(struct b43legacy_wldev *dev) -{ - struct b43legacy_wl *wl = dev->wl; - u32 cmd, beacon0_valid, beacon1_valid; - - if (!b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) - return; - - /* This is the bottom half of the asynchronous beacon update. */ - - /* Ignore interrupt in the future. */ - dev->irq_mask &= ~B43legacy_IRQ_BEACON; - - cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); - beacon0_valid = (cmd & B43legacy_MACCMD_BEACON0_VALID); - beacon1_valid = (cmd & B43legacy_MACCMD_BEACON1_VALID); - - /* Schedule interrupt manually, if busy. */ - if (beacon0_valid && beacon1_valid) { - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, B43legacy_IRQ_BEACON); - dev->irq_mask |= B43legacy_IRQ_BEACON; - return; - } - - if (unlikely(wl->beacon_templates_virgin)) { - /* We never uploaded a beacon before. - * Upload both templates now, but only mark one valid. */ - wl->beacon_templates_virgin = false; - b43legacy_upload_beacon0(dev); - b43legacy_upload_beacon1(dev); - cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); - cmd |= B43legacy_MACCMD_BEACON0_VALID; - b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); - } else { - if (!beacon0_valid) { - b43legacy_upload_beacon0(dev); - cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); - cmd |= B43legacy_MACCMD_BEACON0_VALID; - b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); - } else if (!beacon1_valid) { - b43legacy_upload_beacon1(dev); - cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); - cmd |= B43legacy_MACCMD_BEACON1_VALID; - b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); - } - } -} - -static void b43legacy_beacon_update_trigger_work(struct work_struct *work) -{ - struct b43legacy_wl *wl = container_of(work, struct b43legacy_wl, - beacon_update_trigger); - struct b43legacy_wldev *dev; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (likely(dev && (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED))) { - spin_lock_irq(&wl->irq_lock); - /* Update beacon right away or defer to IRQ. */ - handle_irq_beacon(dev); - /* The handler might have updated the IRQ mask. */ - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, - dev->irq_mask); - mmiowb(); - spin_unlock_irq(&wl->irq_lock); - } - mutex_unlock(&wl->mutex); -} - -/* Asynchronously update the packet templates in template RAM. - * Locking: Requires wl->irq_lock to be locked. */ -static void b43legacy_update_templates(struct b43legacy_wl *wl) -{ - struct sk_buff *beacon; - /* This is the top half of the ansynchronous beacon update. The bottom - * half is the beacon IRQ. Beacon update must be asynchronous to avoid - * sending an invalid beacon. This can happen for example, if the - * firmware transmits a beacon while we are updating it. */ - - /* We could modify the existing beacon and set the aid bit in the TIM - * field, but that would probably require resizing and moving of data - * within the beacon template. Simply request a new beacon and let - * mac80211 do the hard work. */ - beacon = ieee80211_beacon_get(wl->hw, wl->vif); - if (unlikely(!beacon)) - return; - - if (wl->current_beacon) - dev_kfree_skb_any(wl->current_beacon); - wl->current_beacon = beacon; - wl->beacon0_uploaded = false; - wl->beacon1_uploaded = false; - ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger); -} - -static void b43legacy_set_beacon_int(struct b43legacy_wldev *dev, - u16 beacon_int) -{ - b43legacy_time_lock(dev); - if (dev->dev->id.revision >= 3) { - b43legacy_write32(dev, B43legacy_MMIO_TSF_CFP_REP, - (beacon_int << 16)); - b43legacy_write32(dev, B43legacy_MMIO_TSF_CFP_START, - (beacon_int << 10)); - } else { - b43legacy_write16(dev, 0x606, (beacon_int >> 6)); - b43legacy_write16(dev, 0x610, beacon_int); - } - b43legacy_time_unlock(dev); - b43legacydbg(dev->wl, "Set beacon interval to %u\n", beacon_int); -} - -static void handle_irq_ucode_debug(struct b43legacy_wldev *dev) -{ -} - -/* Interrupt handler bottom-half */ -static void b43legacy_interrupt_tasklet(struct b43legacy_wldev *dev) -{ - u32 reason; - u32 dma_reason[ARRAY_SIZE(dev->dma_reason)]; - u32 merged_dma_reason = 0; - int i; - unsigned long flags; - - spin_lock_irqsave(&dev->wl->irq_lock, flags); - - B43legacy_WARN_ON(b43legacy_status(dev) < - B43legacy_STAT_INITIALIZED); - - reason = dev->irq_reason; - for (i = 0; i < ARRAY_SIZE(dma_reason); i++) { - dma_reason[i] = dev->dma_reason[i]; - merged_dma_reason |= dma_reason[i]; - } - - if (unlikely(reason & B43legacy_IRQ_MAC_TXERR)) - b43legacyerr(dev->wl, "MAC transmission error\n"); - - if (unlikely(reason & B43legacy_IRQ_PHY_TXERR)) { - b43legacyerr(dev->wl, "PHY transmission error\n"); - rmb(); - if (unlikely(atomic_dec_and_test(&dev->phy.txerr_cnt))) { - b43legacyerr(dev->wl, "Too many PHY TX errors, " - "restarting the controller\n"); - b43legacy_controller_restart(dev, "PHY TX errors"); - } - } - - if (unlikely(merged_dma_reason & (B43legacy_DMAIRQ_FATALMASK | - B43legacy_DMAIRQ_NONFATALMASK))) { - if (merged_dma_reason & B43legacy_DMAIRQ_FATALMASK) { - b43legacyerr(dev->wl, "Fatal DMA error: " - "0x%08X, 0x%08X, 0x%08X, " - "0x%08X, 0x%08X, 0x%08X\n", - dma_reason[0], dma_reason[1], - dma_reason[2], dma_reason[3], - dma_reason[4], dma_reason[5]); - b43legacy_controller_restart(dev, "DMA error"); - mmiowb(); - spin_unlock_irqrestore(&dev->wl->irq_lock, flags); - return; - } - if (merged_dma_reason & B43legacy_DMAIRQ_NONFATALMASK) - b43legacyerr(dev->wl, "DMA error: " - "0x%08X, 0x%08X, 0x%08X, " - "0x%08X, 0x%08X, 0x%08X\n", - dma_reason[0], dma_reason[1], - dma_reason[2], dma_reason[3], - dma_reason[4], dma_reason[5]); - } - - if (unlikely(reason & B43legacy_IRQ_UCODE_DEBUG)) - handle_irq_ucode_debug(dev); - if (reason & B43legacy_IRQ_TBTT_INDI) - handle_irq_tbtt_indication(dev); - if (reason & B43legacy_IRQ_ATIM_END) - handle_irq_atim_end(dev); - if (reason & B43legacy_IRQ_BEACON) - handle_irq_beacon(dev); - if (reason & B43legacy_IRQ_PMQ) - handle_irq_pmq(dev); - if (reason & B43legacy_IRQ_TXFIFO_FLUSH_OK) - ;/*TODO*/ - if (reason & B43legacy_IRQ_NOISESAMPLE_OK) - handle_irq_noise(dev); - - /* Check the DMA reason registers for received data. */ - if (dma_reason[0] & B43legacy_DMAIRQ_RX_DONE) { - if (b43legacy_using_pio(dev)) - b43legacy_pio_rx(dev->pio.queue0); - else - b43legacy_dma_rx(dev->dma.rx_ring0); - } - B43legacy_WARN_ON(dma_reason[1] & B43legacy_DMAIRQ_RX_DONE); - B43legacy_WARN_ON(dma_reason[2] & B43legacy_DMAIRQ_RX_DONE); - if (dma_reason[3] & B43legacy_DMAIRQ_RX_DONE) { - if (b43legacy_using_pio(dev)) - b43legacy_pio_rx(dev->pio.queue3); - else - b43legacy_dma_rx(dev->dma.rx_ring3); - } - B43legacy_WARN_ON(dma_reason[4] & B43legacy_DMAIRQ_RX_DONE); - B43legacy_WARN_ON(dma_reason[5] & B43legacy_DMAIRQ_RX_DONE); - - if (reason & B43legacy_IRQ_TX_OK) - handle_irq_transmit_status(dev); - - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); - mmiowb(); - spin_unlock_irqrestore(&dev->wl->irq_lock, flags); -} - -static void pio_irq_workaround(struct b43legacy_wldev *dev, - u16 base, int queueidx) -{ - u16 rxctl; - - rxctl = b43legacy_read16(dev, base + B43legacy_PIO_RXCTL); - if (rxctl & B43legacy_PIO_RXCTL_DATAAVAILABLE) - dev->dma_reason[queueidx] |= B43legacy_DMAIRQ_RX_DONE; - else - dev->dma_reason[queueidx] &= ~B43legacy_DMAIRQ_RX_DONE; -} - -static void b43legacy_interrupt_ack(struct b43legacy_wldev *dev, u32 reason) -{ - if (b43legacy_using_pio(dev) && - (dev->dev->id.revision < 3) && - (!(reason & B43legacy_IRQ_PIO_WORKAROUND))) { - /* Apply a PIO specific workaround to the dma_reasons */ - pio_irq_workaround(dev, B43legacy_MMIO_PIO1_BASE, 0); - pio_irq_workaround(dev, B43legacy_MMIO_PIO2_BASE, 1); - pio_irq_workaround(dev, B43legacy_MMIO_PIO3_BASE, 2); - pio_irq_workaround(dev, B43legacy_MMIO_PIO4_BASE, 3); - } - - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, reason); - - b43legacy_write32(dev, B43legacy_MMIO_DMA0_REASON, - dev->dma_reason[0]); - b43legacy_write32(dev, B43legacy_MMIO_DMA1_REASON, - dev->dma_reason[1]); - b43legacy_write32(dev, B43legacy_MMIO_DMA2_REASON, - dev->dma_reason[2]); - b43legacy_write32(dev, B43legacy_MMIO_DMA3_REASON, - dev->dma_reason[3]); - b43legacy_write32(dev, B43legacy_MMIO_DMA4_REASON, - dev->dma_reason[4]); - b43legacy_write32(dev, B43legacy_MMIO_DMA5_REASON, - dev->dma_reason[5]); -} - -/* Interrupt handler top-half */ -static irqreturn_t b43legacy_interrupt_handler(int irq, void *dev_id) -{ - irqreturn_t ret = IRQ_NONE; - struct b43legacy_wldev *dev = dev_id; - u32 reason; - - B43legacy_WARN_ON(!dev); - - spin_lock(&dev->wl->irq_lock); - - if (unlikely(b43legacy_status(dev) < B43legacy_STAT_STARTED)) - /* This can only happen on shared IRQ lines. */ - goto out; - reason = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); - if (reason == 0xffffffff) /* shared IRQ */ - goto out; - ret = IRQ_HANDLED; - reason &= dev->irq_mask; - if (!reason) - goto out; - - dev->dma_reason[0] = b43legacy_read32(dev, - B43legacy_MMIO_DMA0_REASON) - & 0x0001DC00; - dev->dma_reason[1] = b43legacy_read32(dev, - B43legacy_MMIO_DMA1_REASON) - & 0x0000DC00; - dev->dma_reason[2] = b43legacy_read32(dev, - B43legacy_MMIO_DMA2_REASON) - & 0x0000DC00; - dev->dma_reason[3] = b43legacy_read32(dev, - B43legacy_MMIO_DMA3_REASON) - & 0x0001DC00; - dev->dma_reason[4] = b43legacy_read32(dev, - B43legacy_MMIO_DMA4_REASON) - & 0x0000DC00; - dev->dma_reason[5] = b43legacy_read32(dev, - B43legacy_MMIO_DMA5_REASON) - & 0x0000DC00; - - b43legacy_interrupt_ack(dev, reason); - /* Disable all IRQs. They are enabled again in the bottom half. */ - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); - /* Save the reason code and call our bottom half. */ - dev->irq_reason = reason; - tasklet_schedule(&dev->isr_tasklet); -out: - mmiowb(); - spin_unlock(&dev->wl->irq_lock); - - return ret; -} - -static void b43legacy_release_firmware(struct b43legacy_wldev *dev) -{ - release_firmware(dev->fw.ucode); - dev->fw.ucode = NULL; - release_firmware(dev->fw.pcm); - dev->fw.pcm = NULL; - release_firmware(dev->fw.initvals); - dev->fw.initvals = NULL; - release_firmware(dev->fw.initvals_band); - dev->fw.initvals_band = NULL; -} - -static void b43legacy_print_fw_helptext(struct b43legacy_wl *wl) -{ - b43legacyerr(wl, "You must go to http://wireless.kernel.org/en/users/" - "Drivers/b43#devicefirmware " - "and download the correct firmware (version 3).\n"); -} - -static void b43legacy_fw_cb(const struct firmware *firmware, void *context) -{ - struct b43legacy_wldev *dev = context; - - dev->fwp = firmware; - complete(&dev->fw_load_complete); -} - -static int do_request_fw(struct b43legacy_wldev *dev, - const char *name, - const struct firmware **fw, bool async) -{ - char path[sizeof(modparam_fwpostfix) + 32]; - struct b43legacy_fw_header *hdr; - u32 size; - int err; - - if (!name) - return 0; - - snprintf(path, ARRAY_SIZE(path), - "b43legacy%s/%s.fw", - modparam_fwpostfix, name); - b43legacyinfo(dev->wl, "Loading firmware %s\n", path); - if (async) { - init_completion(&dev->fw_load_complete); - err = request_firmware_nowait(THIS_MODULE, 1, path, - dev->dev->dev, GFP_KERNEL, - dev, b43legacy_fw_cb); - if (err) { - b43legacyerr(dev->wl, "Unable to load firmware\n"); - return err; - } - /* stall here until fw ready */ - wait_for_completion(&dev->fw_load_complete); - if (!dev->fwp) - err = -EINVAL; - *fw = dev->fwp; - } else { - err = request_firmware(fw, path, dev->dev->dev); - } - if (err) { - b43legacyerr(dev->wl, "Firmware file \"%s\" not found " - "or load failed.\n", path); - return err; - } - if ((*fw)->size < sizeof(struct b43legacy_fw_header)) - goto err_format; - hdr = (struct b43legacy_fw_header *)((*fw)->data); - switch (hdr->type) { - case B43legacy_FW_TYPE_UCODE: - case B43legacy_FW_TYPE_PCM: - size = be32_to_cpu(hdr->size); - if (size != (*fw)->size - sizeof(struct b43legacy_fw_header)) - goto err_format; - /* fallthrough */ - case B43legacy_FW_TYPE_IV: - if (hdr->ver != 1) - goto err_format; - break; - default: - goto err_format; - } - - return err; - -err_format: - b43legacyerr(dev->wl, "Firmware file \"%s\" format error.\n", path); - return -EPROTO; -} - -static int b43legacy_one_core_attach(struct ssb_device *dev, - struct b43legacy_wl *wl); -static void b43legacy_one_core_detach(struct ssb_device *dev); - -static void b43legacy_request_firmware(struct work_struct *work) -{ - struct b43legacy_wl *wl = container_of(work, - struct b43legacy_wl, firmware_load); - struct b43legacy_wldev *dev = wl->current_dev; - struct b43legacy_firmware *fw = &dev->fw; - const u8 rev = dev->dev->id.revision; - const char *filename; - int err; - - if (!fw->ucode) { - if (rev == 2) - filename = "ucode2"; - else if (rev == 4) - filename = "ucode4"; - else - filename = "ucode5"; - err = do_request_fw(dev, filename, &fw->ucode, true); - if (err) - goto err_load; - } - if (!fw->pcm) { - if (rev < 5) - filename = "pcm4"; - else - filename = "pcm5"; - err = do_request_fw(dev, filename, &fw->pcm, false); - if (err) - goto err_load; - } - if (!fw->initvals) { - switch (dev->phy.type) { - case B43legacy_PHYTYPE_B: - case B43legacy_PHYTYPE_G: - if ((rev >= 5) && (rev <= 10)) - filename = "b0g0initvals5"; - else if (rev == 2 || rev == 4) - filename = "b0g0initvals2"; - else - goto err_no_initvals; - break; - default: - goto err_no_initvals; - } - err = do_request_fw(dev, filename, &fw->initvals, false); - if (err) - goto err_load; - } - if (!fw->initvals_band) { - switch (dev->phy.type) { - case B43legacy_PHYTYPE_B: - case B43legacy_PHYTYPE_G: - if ((rev >= 5) && (rev <= 10)) - filename = "b0g0bsinitvals5"; - else if (rev >= 11) - filename = NULL; - else if (rev == 2 || rev == 4) - filename = NULL; - else - goto err_no_initvals; - break; - default: - goto err_no_initvals; - } - err = do_request_fw(dev, filename, &fw->initvals_band, false); - if (err) - goto err_load; - } - err = ieee80211_register_hw(wl->hw); - if (err) - goto err_one_core_detach; - return; - -err_one_core_detach: - b43legacy_one_core_detach(dev->dev); - goto error; - -err_load: - b43legacy_print_fw_helptext(dev->wl); - goto error; - -err_no_initvals: - err = -ENODEV; - b43legacyerr(dev->wl, "No Initial Values firmware file for PHY %u, " - "core rev %u\n", dev->phy.type, rev); - goto error; - -error: - b43legacy_release_firmware(dev); - return; -} - -static int b43legacy_upload_microcode(struct b43legacy_wldev *dev) -{ - struct wiphy *wiphy = dev->wl->hw->wiphy; - const size_t hdr_len = sizeof(struct b43legacy_fw_header); - const __be32 *data; - unsigned int i; - unsigned int len; - u16 fwrev; - u16 fwpatch; - u16 fwdate; - u16 fwtime; - u32 tmp, macctl; - int err = 0; - - /* Jump the microcode PSM to offset 0 */ - macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - B43legacy_WARN_ON(macctl & B43legacy_MACCTL_PSM_RUN); - macctl |= B43legacy_MACCTL_PSM_JMP0; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); - /* Zero out all microcode PSM registers and shared memory. */ - for (i = 0; i < 64; i++) - b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, i, 0); - for (i = 0; i < 4096; i += 2) - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, i, 0); - - /* Upload Microcode. */ - data = (__be32 *) (dev->fw.ucode->data + hdr_len); - len = (dev->fw.ucode->size - hdr_len) / sizeof(__be32); - b43legacy_shm_control_word(dev, - B43legacy_SHM_UCODE | - B43legacy_SHM_AUTOINC_W, - 0x0000); - for (i = 0; i < len; i++) { - b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, - be32_to_cpu(data[i])); - udelay(10); - } - - if (dev->fw.pcm) { - /* Upload PCM data. */ - data = (__be32 *) (dev->fw.pcm->data + hdr_len); - len = (dev->fw.pcm->size - hdr_len) / sizeof(__be32); - b43legacy_shm_control_word(dev, B43legacy_SHM_HW, 0x01EA); - b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, 0x00004000); - /* No need for autoinc bit in SHM_HW */ - b43legacy_shm_control_word(dev, B43legacy_SHM_HW, 0x01EB); - for (i = 0; i < len; i++) { - b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, - be32_to_cpu(data[i])); - udelay(10); - } - } - - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, - B43legacy_IRQ_ALL); - - /* Start the microcode PSM */ - macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - macctl &= ~B43legacy_MACCTL_PSM_JMP0; - macctl |= B43legacy_MACCTL_PSM_RUN; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); - - /* Wait for the microcode to load and respond */ - i = 0; - while (1) { - tmp = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); - if (tmp == B43legacy_IRQ_MAC_SUSPENDED) - break; - i++; - if (i >= B43legacy_IRQWAIT_MAX_RETRIES) { - b43legacyerr(dev->wl, "Microcode not responding\n"); - b43legacy_print_fw_helptext(dev->wl); - err = -ENODEV; - goto error; - } - msleep_interruptible(50); - if (signal_pending(current)) { - err = -EINTR; - goto error; - } - } - /* dummy read follows */ - b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); - - /* Get and check the revisions. */ - fwrev = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_UCODEREV); - fwpatch = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_UCODEPATCH); - fwdate = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_UCODEDATE); - fwtime = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_UCODETIME); - - if (fwrev > 0x128) { - b43legacyerr(dev->wl, "YOU ARE TRYING TO LOAD V4 FIRMWARE." - " Only firmware from binary drivers version 3.x" - " is supported. You must change your firmware" - " files.\n"); - b43legacy_print_fw_helptext(dev->wl); - err = -EOPNOTSUPP; - goto error; - } - b43legacyinfo(dev->wl, "Loading firmware version 0x%X, patch level %u " - "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n", fwrev, fwpatch, - (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF, - (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, - fwtime & 0x1F); - - dev->fw.rev = fwrev; - dev->fw.patch = fwpatch; - - snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "%u.%u", - dev->fw.rev, dev->fw.patch); - wiphy->hw_version = dev->dev->id.coreid; - - return 0; - -error: - macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - macctl &= ~B43legacy_MACCTL_PSM_RUN; - macctl |= B43legacy_MACCTL_PSM_JMP0; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); - - return err; -} - -static int b43legacy_write_initvals(struct b43legacy_wldev *dev, - const struct b43legacy_iv *ivals, - size_t count, - size_t array_size) -{ - const struct b43legacy_iv *iv; - u16 offset; - size_t i; - bool bit32; - - BUILD_BUG_ON(sizeof(struct b43legacy_iv) != 6); - iv = ivals; - for (i = 0; i < count; i++) { - if (array_size < sizeof(iv->offset_size)) - goto err_format; - array_size -= sizeof(iv->offset_size); - offset = be16_to_cpu(iv->offset_size); - bit32 = !!(offset & B43legacy_IV_32BIT); - offset &= B43legacy_IV_OFFSET_MASK; - if (offset >= 0x1000) - goto err_format; - if (bit32) { - u32 value; - - if (array_size < sizeof(iv->data.d32)) - goto err_format; - array_size -= sizeof(iv->data.d32); - - value = get_unaligned_be32(&iv->data.d32); - b43legacy_write32(dev, offset, value); - - iv = (const struct b43legacy_iv *)((const uint8_t *)iv + - sizeof(__be16) + - sizeof(__be32)); - } else { - u16 value; - - if (array_size < sizeof(iv->data.d16)) - goto err_format; - array_size -= sizeof(iv->data.d16); - - value = be16_to_cpu(iv->data.d16); - b43legacy_write16(dev, offset, value); - - iv = (const struct b43legacy_iv *)((const uint8_t *)iv + - sizeof(__be16) + - sizeof(__be16)); - } - } - if (array_size) - goto err_format; - - return 0; - -err_format: - b43legacyerr(dev->wl, "Initial Values Firmware file-format error.\n"); - b43legacy_print_fw_helptext(dev->wl); - - return -EPROTO; -} - -static int b43legacy_upload_initvals(struct b43legacy_wldev *dev) -{ - const size_t hdr_len = sizeof(struct b43legacy_fw_header); - const struct b43legacy_fw_header *hdr; - struct b43legacy_firmware *fw = &dev->fw; - const struct b43legacy_iv *ivals; - size_t count; - int err; - - hdr = (const struct b43legacy_fw_header *)(fw->initvals->data); - ivals = (const struct b43legacy_iv *)(fw->initvals->data + hdr_len); - count = be32_to_cpu(hdr->size); - err = b43legacy_write_initvals(dev, ivals, count, - fw->initvals->size - hdr_len); - if (err) - goto out; - if (fw->initvals_band) { - hdr = (const struct b43legacy_fw_header *) - (fw->initvals_band->data); - ivals = (const struct b43legacy_iv *)(fw->initvals_band->data - + hdr_len); - count = be32_to_cpu(hdr->size); - err = b43legacy_write_initvals(dev, ivals, count, - fw->initvals_band->size - hdr_len); - if (err) - goto out; - } -out: - - return err; -} - -/* Initialize the GPIOs - * http://bcm-specs.sipsolutions.net/GPIO - */ -static int b43legacy_gpio_init(struct b43legacy_wldev *dev) -{ - struct ssb_bus *bus = dev->dev->bus; - struct ssb_device *gpiodev, *pcidev = NULL; - u32 mask; - u32 set; - - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, - b43legacy_read32(dev, - B43legacy_MMIO_MACCTL) - & 0xFFFF3FFF); - - b43legacy_write16(dev, B43legacy_MMIO_GPIO_MASK, - b43legacy_read16(dev, - B43legacy_MMIO_GPIO_MASK) - | 0x000F); - - mask = 0x0000001F; - set = 0x0000000F; - if (dev->dev->bus->chip_id == 0x4301) { - mask |= 0x0060; - set |= 0x0060; - } - if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_PACTRL) { - b43legacy_write16(dev, B43legacy_MMIO_GPIO_MASK, - b43legacy_read16(dev, - B43legacy_MMIO_GPIO_MASK) - | 0x0200); - mask |= 0x0200; - set |= 0x0200; - } - if (dev->dev->id.revision >= 2) - mask |= 0x0010; /* FIXME: This is redundant. */ - -#ifdef CONFIG_SSB_DRIVER_PCICORE - pcidev = bus->pcicore.dev; -#endif - gpiodev = bus->chipco.dev ? : pcidev; - if (!gpiodev) - return 0; - ssb_write32(gpiodev, B43legacy_GPIO_CONTROL, - (ssb_read32(gpiodev, B43legacy_GPIO_CONTROL) - & ~mask) | set); - - return 0; -} - -/* Turn off all GPIO stuff. Call this on module unload, for example. */ -static void b43legacy_gpio_cleanup(struct b43legacy_wldev *dev) -{ - struct ssb_bus *bus = dev->dev->bus; - struct ssb_device *gpiodev, *pcidev = NULL; - -#ifdef CONFIG_SSB_DRIVER_PCICORE - pcidev = bus->pcicore.dev; -#endif - gpiodev = bus->chipco.dev ? : pcidev; - if (!gpiodev) - return; - ssb_write32(gpiodev, B43legacy_GPIO_CONTROL, 0); -} - -/* http://bcm-specs.sipsolutions.net/EnableMac */ -void b43legacy_mac_enable(struct b43legacy_wldev *dev) -{ - dev->mac_suspended--; - B43legacy_WARN_ON(dev->mac_suspended < 0); - B43legacy_WARN_ON(irqs_disabled()); - if (dev->mac_suspended == 0) { - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, - b43legacy_read32(dev, - B43legacy_MMIO_MACCTL) - | B43legacy_MACCTL_ENABLED); - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, - B43legacy_IRQ_MAC_SUSPENDED); - /* the next two are dummy reads */ - b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); - b43legacy_power_saving_ctl_bits(dev, -1, -1); - - /* Re-enable IRQs. */ - spin_lock_irq(&dev->wl->irq_lock); - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, - dev->irq_mask); - spin_unlock_irq(&dev->wl->irq_lock); - } -} - -/* http://bcm-specs.sipsolutions.net/SuspendMAC */ -void b43legacy_mac_suspend(struct b43legacy_wldev *dev) -{ - int i; - u32 tmp; - - might_sleep(); - B43legacy_WARN_ON(irqs_disabled()); - B43legacy_WARN_ON(dev->mac_suspended < 0); - - if (dev->mac_suspended == 0) { - /* Mask IRQs before suspending MAC. Otherwise - * the MAC stays busy and won't suspend. */ - spin_lock_irq(&dev->wl->irq_lock); - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); - spin_unlock_irq(&dev->wl->irq_lock); - b43legacy_synchronize_irq(dev); - - b43legacy_power_saving_ctl_bits(dev, -1, 1); - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, - b43legacy_read32(dev, - B43legacy_MMIO_MACCTL) - & ~B43legacy_MACCTL_ENABLED); - b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); - for (i = 40; i; i--) { - tmp = b43legacy_read32(dev, - B43legacy_MMIO_GEN_IRQ_REASON); - if (tmp & B43legacy_IRQ_MAC_SUSPENDED) - goto out; - msleep(1); - } - b43legacyerr(dev->wl, "MAC suspend failed\n"); - } -out: - dev->mac_suspended++; -} - -static void b43legacy_adjust_opmode(struct b43legacy_wldev *dev) -{ - struct b43legacy_wl *wl = dev->wl; - u32 ctl; - u16 cfp_pretbtt; - - ctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - /* Reset status to STA infrastructure mode. */ - ctl &= ~B43legacy_MACCTL_AP; - ctl &= ~B43legacy_MACCTL_KEEP_CTL; - ctl &= ~B43legacy_MACCTL_KEEP_BADPLCP; - ctl &= ~B43legacy_MACCTL_KEEP_BAD; - ctl &= ~B43legacy_MACCTL_PROMISC; - ctl &= ~B43legacy_MACCTL_BEACPROMISC; - ctl |= B43legacy_MACCTL_INFRA; - - if (b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) - ctl |= B43legacy_MACCTL_AP; - else if (b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC)) - ctl &= ~B43legacy_MACCTL_INFRA; - - if (wl->filter_flags & FIF_CONTROL) - ctl |= B43legacy_MACCTL_KEEP_CTL; - if (wl->filter_flags & FIF_FCSFAIL) - ctl |= B43legacy_MACCTL_KEEP_BAD; - if (wl->filter_flags & FIF_PLCPFAIL) - ctl |= B43legacy_MACCTL_KEEP_BADPLCP; - if (wl->filter_flags & FIF_BCN_PRBRESP_PROMISC) - ctl |= B43legacy_MACCTL_BEACPROMISC; - - /* Workaround: On old hardware the HW-MAC-address-filter - * doesn't work properly, so always run promisc in filter - * it in software. */ - if (dev->dev->id.revision <= 4) - ctl |= B43legacy_MACCTL_PROMISC; - - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, ctl); - - cfp_pretbtt = 2; - if ((ctl & B43legacy_MACCTL_INFRA) && - !(ctl & B43legacy_MACCTL_AP)) { - if (dev->dev->bus->chip_id == 0x4306 && - dev->dev->bus->chip_rev == 3) - cfp_pretbtt = 100; - else - cfp_pretbtt = 50; - } - b43legacy_write16(dev, 0x612, cfp_pretbtt); -} - -static void b43legacy_rate_memory_write(struct b43legacy_wldev *dev, - u16 rate, - int is_ofdm) -{ - u16 offset; - - if (is_ofdm) { - offset = 0x480; - offset += (b43legacy_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2; - } else { - offset = 0x4C0; - offset += (b43legacy_plcp_get_ratecode_cck(rate) & 0x000F) * 2; - } - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, offset + 0x20, - b43legacy_shm_read16(dev, - B43legacy_SHM_SHARED, offset)); -} - -static void b43legacy_rate_memory_init(struct b43legacy_wldev *dev) -{ - switch (dev->phy.type) { - case B43legacy_PHYTYPE_G: - b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_6MB, 1); - b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_12MB, 1); - b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_18MB, 1); - b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_24MB, 1); - b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_36MB, 1); - b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_48MB, 1); - b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_54MB, 1); - /* fallthrough */ - case B43legacy_PHYTYPE_B: - b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_1MB, 0); - b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_2MB, 0); - b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_5MB, 0); - b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_11MB, 0); - break; - default: - B43legacy_BUG_ON(1); - } -} - -/* Set the TX-Antenna for management frames sent by firmware. */ -static void b43legacy_mgmtframe_txantenna(struct b43legacy_wldev *dev, - int antenna) -{ - u16 ant = 0; - u16 tmp; - - switch (antenna) { - case B43legacy_ANTENNA0: - ant |= B43legacy_TX4_PHY_ANT0; - break; - case B43legacy_ANTENNA1: - ant |= B43legacy_TX4_PHY_ANT1; - break; - case B43legacy_ANTENNA_AUTO: - ant |= B43legacy_TX4_PHY_ANTLAST; - break; - default: - B43legacy_BUG_ON(1); - } - - /* FIXME We also need to set the other flags of the PHY control - * field somewhere. */ - - /* For Beacons */ - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_BEACPHYCTL); - tmp = (tmp & ~B43legacy_TX4_PHY_ANT) | ant; - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_BEACPHYCTL, tmp); - /* For ACK/CTS */ - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_ACKCTSPHYCTL); - tmp = (tmp & ~B43legacy_TX4_PHY_ANT) | ant; - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_ACKCTSPHYCTL, tmp); - /* For Probe Resposes */ - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_PRPHYCTL); - tmp = (tmp & ~B43legacy_TX4_PHY_ANT) | ant; - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_PRPHYCTL, tmp); -} - -/* This is the opposite of b43legacy_chip_init() */ -static void b43legacy_chip_exit(struct b43legacy_wldev *dev) -{ - b43legacy_radio_turn_off(dev, 1); - b43legacy_gpio_cleanup(dev); - /* firmware is released later */ -} - -/* Initialize the chip - * http://bcm-specs.sipsolutions.net/ChipInit - */ -static int b43legacy_chip_init(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - int err; - int tmp; - u32 value32, macctl; - u16 value16; - - /* Initialize the MAC control */ - macctl = B43legacy_MACCTL_IHR_ENABLED | B43legacy_MACCTL_SHM_ENABLED; - if (dev->phy.gmode) - macctl |= B43legacy_MACCTL_GMODE; - macctl |= B43legacy_MACCTL_INFRA; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); - - err = b43legacy_upload_microcode(dev); - if (err) - goto out; /* firmware is released later */ - - err = b43legacy_gpio_init(dev); - if (err) - goto out; /* firmware is released later */ - - err = b43legacy_upload_initvals(dev); - if (err) - goto err_gpio_clean; - b43legacy_radio_turn_on(dev); - - b43legacy_write16(dev, 0x03E6, 0x0000); - err = b43legacy_phy_init(dev); - if (err) - goto err_radio_off; - - /* Select initial Interference Mitigation. */ - tmp = phy->interfmode; - phy->interfmode = B43legacy_INTERFMODE_NONE; - b43legacy_radio_set_interference_mitigation(dev, tmp); - - b43legacy_phy_set_antenna_diversity(dev); - b43legacy_mgmtframe_txantenna(dev, B43legacy_ANTENNA_DEFAULT); - - if (phy->type == B43legacy_PHYTYPE_B) { - value16 = b43legacy_read16(dev, 0x005E); - value16 |= 0x0004; - b43legacy_write16(dev, 0x005E, value16); - } - b43legacy_write32(dev, 0x0100, 0x01000000); - if (dev->dev->id.revision < 5) - b43legacy_write32(dev, 0x010C, 0x01000000); - - value32 = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - value32 &= ~B43legacy_MACCTL_INFRA; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value32); - value32 = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - value32 |= B43legacy_MACCTL_INFRA; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value32); - - if (b43legacy_using_pio(dev)) { - b43legacy_write32(dev, 0x0210, 0x00000100); - b43legacy_write32(dev, 0x0230, 0x00000100); - b43legacy_write32(dev, 0x0250, 0x00000100); - b43legacy_write32(dev, 0x0270, 0x00000100); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0034, - 0x0000); - } - - /* Probe Response Timeout value */ - /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */ - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0074, 0x0000); - - /* Initially set the wireless operation mode. */ - b43legacy_adjust_opmode(dev); - - if (dev->dev->id.revision < 3) { - b43legacy_write16(dev, 0x060E, 0x0000); - b43legacy_write16(dev, 0x0610, 0x8000); - b43legacy_write16(dev, 0x0604, 0x0000); - b43legacy_write16(dev, 0x0606, 0x0200); - } else { - b43legacy_write32(dev, 0x0188, 0x80000000); - b43legacy_write32(dev, 0x018C, 0x02000000); - } - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, 0x00004000); - b43legacy_write32(dev, B43legacy_MMIO_DMA0_IRQ_MASK, 0x0001DC00); - b43legacy_write32(dev, B43legacy_MMIO_DMA1_IRQ_MASK, 0x0000DC00); - b43legacy_write32(dev, B43legacy_MMIO_DMA2_IRQ_MASK, 0x0000DC00); - b43legacy_write32(dev, B43legacy_MMIO_DMA3_IRQ_MASK, 0x0001DC00); - b43legacy_write32(dev, B43legacy_MMIO_DMA4_IRQ_MASK, 0x0000DC00); - b43legacy_write32(dev, B43legacy_MMIO_DMA5_IRQ_MASK, 0x0000DC00); - - value32 = ssb_read32(dev->dev, SSB_TMSLOW); - value32 |= B43legacy_TMSLOW_MACPHYCLKEN; - ssb_write32(dev->dev, SSB_TMSLOW, value32); - - b43legacy_write16(dev, B43legacy_MMIO_POWERUP_DELAY, - dev->dev->bus->chipco.fast_pwrup_delay); - - /* PHY TX errors counter. */ - atomic_set(&phy->txerr_cnt, B43legacy_PHY_TX_BADNESS_LIMIT); - - B43legacy_WARN_ON(err != 0); - b43legacydbg(dev->wl, "Chip initialized\n"); -out: - return err; - -err_radio_off: - b43legacy_radio_turn_off(dev, 1); -err_gpio_clean: - b43legacy_gpio_cleanup(dev); - goto out; -} - -static void b43legacy_periodic_every120sec(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - - if (phy->type != B43legacy_PHYTYPE_G || phy->rev < 2) - return; - - b43legacy_mac_suspend(dev); - b43legacy_phy_lo_g_measure(dev); - b43legacy_mac_enable(dev); -} - -static void b43legacy_periodic_every60sec(struct b43legacy_wldev *dev) -{ - b43legacy_phy_lo_mark_all_unused(dev); - if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI) { - b43legacy_mac_suspend(dev); - b43legacy_calc_nrssi_slope(dev); - b43legacy_mac_enable(dev); - } -} - -static void b43legacy_periodic_every30sec(struct b43legacy_wldev *dev) -{ - /* Update device statistics. */ - b43legacy_calculate_link_quality(dev); -} - -static void b43legacy_periodic_every15sec(struct b43legacy_wldev *dev) -{ - b43legacy_phy_xmitpower(dev); /* FIXME: unless scanning? */ - - atomic_set(&dev->phy.txerr_cnt, B43legacy_PHY_TX_BADNESS_LIMIT); - wmb(); -} - -static void do_periodic_work(struct b43legacy_wldev *dev) -{ - unsigned int state; - - state = dev->periodic_state; - if (state % 8 == 0) - b43legacy_periodic_every120sec(dev); - if (state % 4 == 0) - b43legacy_periodic_every60sec(dev); - if (state % 2 == 0) - b43legacy_periodic_every30sec(dev); - b43legacy_periodic_every15sec(dev); -} - -/* Periodic work locking policy: - * The whole periodic work handler is protected by - * wl->mutex. If another lock is needed somewhere in the - * pwork callchain, it's acquired in-place, where it's needed. - */ -static void b43legacy_periodic_work_handler(struct work_struct *work) -{ - struct b43legacy_wldev *dev = container_of(work, struct b43legacy_wldev, - periodic_work.work); - struct b43legacy_wl *wl = dev->wl; - unsigned long delay; - - mutex_lock(&wl->mutex); - - if (unlikely(b43legacy_status(dev) != B43legacy_STAT_STARTED)) - goto out; - if (b43legacy_debug(dev, B43legacy_DBG_PWORK_STOP)) - goto out_requeue; - - do_periodic_work(dev); - - dev->periodic_state++; -out_requeue: - if (b43legacy_debug(dev, B43legacy_DBG_PWORK_FAST)) - delay = msecs_to_jiffies(50); - else - delay = round_jiffies_relative(HZ * 15); - ieee80211_queue_delayed_work(wl->hw, &dev->periodic_work, delay); -out: - mutex_unlock(&wl->mutex); -} - -static void b43legacy_periodic_tasks_setup(struct b43legacy_wldev *dev) -{ - struct delayed_work *work = &dev->periodic_work; - - dev->periodic_state = 0; - INIT_DELAYED_WORK(work, b43legacy_periodic_work_handler); - ieee80211_queue_delayed_work(dev->wl->hw, work, 0); -} - -/* Validate access to the chip (SHM) */ -static int b43legacy_validate_chipaccess(struct b43legacy_wldev *dev) -{ - u32 value; - u32 shm_backup; - - shm_backup = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, 0); - b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, 0, 0xAA5555AA); - if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, 0) != - 0xAA5555AA) - goto error; - b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, 0, 0x55AAAA55); - if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, 0) != - 0x55AAAA55) - goto error; - b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, 0, shm_backup); - - value = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - if ((value | B43legacy_MACCTL_GMODE) != - (B43legacy_MACCTL_GMODE | B43legacy_MACCTL_IHR_ENABLED)) - goto error; - - value = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); - if (value) - goto error; - - return 0; -error: - b43legacyerr(dev->wl, "Failed to validate the chipaccess\n"); - return -ENODEV; -} - -static void b43legacy_security_init(struct b43legacy_wldev *dev) -{ - dev->max_nr_keys = (dev->dev->id.revision >= 5) ? 58 : 20; - B43legacy_WARN_ON(dev->max_nr_keys > ARRAY_SIZE(dev->key)); - dev->ktp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - 0x0056); - /* KTP is a word address, but we address SHM bytewise. - * So multiply by two. - */ - dev->ktp *= 2; - if (dev->dev->id.revision >= 5) - /* Number of RCMTA address slots */ - b43legacy_write16(dev, B43legacy_MMIO_RCMTA_COUNT, - dev->max_nr_keys - 8); -} - -#ifdef CONFIG_B43LEGACY_HWRNG -static int b43legacy_rng_read(struct hwrng *rng, u32 *data) -{ - struct b43legacy_wl *wl = (struct b43legacy_wl *)rng->priv; - unsigned long flags; - - /* Don't take wl->mutex here, as it could deadlock with - * hwrng internal locking. It's not needed to take - * wl->mutex here, anyway. */ - - spin_lock_irqsave(&wl->irq_lock, flags); - *data = b43legacy_read16(wl->current_dev, B43legacy_MMIO_RNG); - spin_unlock_irqrestore(&wl->irq_lock, flags); - - return (sizeof(u16)); -} -#endif - -static void b43legacy_rng_exit(struct b43legacy_wl *wl) -{ -#ifdef CONFIG_B43LEGACY_HWRNG - if (wl->rng_initialized) - hwrng_unregister(&wl->rng); -#endif -} - -static int b43legacy_rng_init(struct b43legacy_wl *wl) -{ - int err = 0; - -#ifdef CONFIG_B43LEGACY_HWRNG - snprintf(wl->rng_name, ARRAY_SIZE(wl->rng_name), - "%s_%s", KBUILD_MODNAME, wiphy_name(wl->hw->wiphy)); - wl->rng.name = wl->rng_name; - wl->rng.data_read = b43legacy_rng_read; - wl->rng.priv = (unsigned long)wl; - wl->rng_initialized = 1; - err = hwrng_register(&wl->rng); - if (err) { - wl->rng_initialized = 0; - b43legacyerr(wl, "Failed to register the random " - "number generator (%d)\n", err); - } - -#endif - return err; -} - -static void b43legacy_tx_work(struct work_struct *work) -{ - struct b43legacy_wl *wl = container_of(work, struct b43legacy_wl, - tx_work); - struct b43legacy_wldev *dev; - struct sk_buff *skb; - int queue_num; - int err = 0; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - if (unlikely(!dev || b43legacy_status(dev) < B43legacy_STAT_STARTED)) { - mutex_unlock(&wl->mutex); - return; - } - - for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { - while (skb_queue_len(&wl->tx_queue[queue_num])) { - skb = skb_dequeue(&wl->tx_queue[queue_num]); - if (b43legacy_using_pio(dev)) - err = b43legacy_pio_tx(dev, skb); - else - err = b43legacy_dma_tx(dev, skb); - if (err == -ENOSPC) { - wl->tx_queue_stopped[queue_num] = 1; - ieee80211_stop_queue(wl->hw, queue_num); - skb_queue_head(&wl->tx_queue[queue_num], skb); - break; - } - if (unlikely(err)) - dev_kfree_skb(skb); /* Drop it */ - err = 0; - } - - if (!err) - wl->tx_queue_stopped[queue_num] = 0; - } - - mutex_unlock(&wl->mutex); -} - -static void b43legacy_op_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - - if (unlikely(skb->len < 2 + 2 + 6)) { - /* Too short, this can't be a valid frame. */ - dev_kfree_skb_any(skb); - return; - } - B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags); - - skb_queue_tail(&wl->tx_queue[skb->queue_mapping], skb); - if (!wl->tx_queue_stopped[skb->queue_mapping]) - ieee80211_queue_work(wl->hw, &wl->tx_work); - else - ieee80211_stop_queue(wl->hw, skb->queue_mapping); -} - -static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - return 0; -} - -static int b43legacy_op_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - unsigned long flags; - - spin_lock_irqsave(&wl->irq_lock, flags); - memcpy(stats, &wl->ieee_stats, sizeof(*stats)); - spin_unlock_irqrestore(&wl->irq_lock, flags); - - return 0; -} - -static const char *phymode_to_string(unsigned int phymode) -{ - switch (phymode) { - case B43legacy_PHYMODE_B: - return "B"; - case B43legacy_PHYMODE_G: - return "G"; - default: - B43legacy_BUG_ON(1); - } - return ""; -} - -static int find_wldev_for_phymode(struct b43legacy_wl *wl, - unsigned int phymode, - struct b43legacy_wldev **dev, - bool *gmode) -{ - struct b43legacy_wldev *d; - - list_for_each_entry(d, &wl->devlist, list) { - if (d->phy.possible_phymodes & phymode) { - /* Ok, this device supports the PHY-mode. - * Set the gmode bit. */ - *gmode = true; - *dev = d; - - return 0; - } - } - - return -ESRCH; -} - -static void b43legacy_put_phy_into_reset(struct b43legacy_wldev *dev) -{ - struct ssb_device *sdev = dev->dev; - u32 tmslow; - - tmslow = ssb_read32(sdev, SSB_TMSLOW); - tmslow &= ~B43legacy_TMSLOW_GMODE; - tmslow |= B43legacy_TMSLOW_PHYRESET; - tmslow |= SSB_TMSLOW_FGC; - ssb_write32(sdev, SSB_TMSLOW, tmslow); - msleep(1); - - tmslow = ssb_read32(sdev, SSB_TMSLOW); - tmslow &= ~SSB_TMSLOW_FGC; - tmslow |= B43legacy_TMSLOW_PHYRESET; - ssb_write32(sdev, SSB_TMSLOW, tmslow); - msleep(1); -} - -/* Expects wl->mutex locked */ -static int b43legacy_switch_phymode(struct b43legacy_wl *wl, - unsigned int new_mode) -{ - struct b43legacy_wldev *uninitialized_var(up_dev); - struct b43legacy_wldev *down_dev; - int err; - bool gmode = false; - int prev_status; - - err = find_wldev_for_phymode(wl, new_mode, &up_dev, &gmode); - if (err) { - b43legacyerr(wl, "Could not find a device for %s-PHY mode\n", - phymode_to_string(new_mode)); - return err; - } - if ((up_dev == wl->current_dev) && - (!!wl->current_dev->phy.gmode == !!gmode)) - /* This device is already running. */ - return 0; - b43legacydbg(wl, "Reconfiguring PHYmode to %s-PHY\n", - phymode_to_string(new_mode)); - down_dev = wl->current_dev; - - prev_status = b43legacy_status(down_dev); - /* Shutdown the currently running core. */ - if (prev_status >= B43legacy_STAT_STARTED) - b43legacy_wireless_core_stop(down_dev); - if (prev_status >= B43legacy_STAT_INITIALIZED) - b43legacy_wireless_core_exit(down_dev); - - if (down_dev != up_dev) - /* We switch to a different core, so we put PHY into - * RESET on the old core. */ - b43legacy_put_phy_into_reset(down_dev); - - /* Now start the new core. */ - up_dev->phy.gmode = gmode; - if (prev_status >= B43legacy_STAT_INITIALIZED) { - err = b43legacy_wireless_core_init(up_dev); - if (err) { - b43legacyerr(wl, "Fatal: Could not initialize device" - " for newly selected %s-PHY mode\n", - phymode_to_string(new_mode)); - goto init_failure; - } - } - if (prev_status >= B43legacy_STAT_STARTED) { - err = b43legacy_wireless_core_start(up_dev); - if (err) { - b43legacyerr(wl, "Fatal: Could not start device for " - "newly selected %s-PHY mode\n", - phymode_to_string(new_mode)); - b43legacy_wireless_core_exit(up_dev); - goto init_failure; - } - } - B43legacy_WARN_ON(b43legacy_status(up_dev) != prev_status); - - b43legacy_shm_write32(up_dev, B43legacy_SHM_SHARED, 0x003E, 0); - - wl->current_dev = up_dev; - - return 0; -init_failure: - /* Whoops, failed to init the new core. No core is operating now. */ - wl->current_dev = NULL; - return err; -} - -/* Write the short and long frame retry limit values. */ -static void b43legacy_set_retry_limits(struct b43legacy_wldev *dev, - unsigned int short_retry, - unsigned int long_retry) -{ - /* The retry limit is a 4-bit counter. Enforce this to avoid overflowing - * the chip-internal counter. */ - short_retry = min(short_retry, (unsigned int)0xF); - long_retry = min(long_retry, (unsigned int)0xF); - - b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0006, short_retry); - b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0007, long_retry); -} - -static int b43legacy_op_dev_config(struct ieee80211_hw *hw, - u32 changed) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - struct b43legacy_wldev *dev; - struct b43legacy_phy *phy; - struct ieee80211_conf *conf = &hw->conf; - unsigned long flags; - unsigned int new_phymode = 0xFFFF; - int antenna_tx; - int err = 0; - - antenna_tx = B43legacy_ANTENNA_DEFAULT; - - mutex_lock(&wl->mutex); - dev = wl->current_dev; - phy = &dev->phy; - - if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) - b43legacy_set_retry_limits(dev, - conf->short_frame_max_tx_count, - conf->long_frame_max_tx_count); - changed &= ~IEEE80211_CONF_CHANGE_RETRY_LIMITS; - if (!changed) - goto out_unlock_mutex; - - /* Switch the PHY mode (if necessary). */ - switch (conf->chandef.chan->band) { - case IEEE80211_BAND_2GHZ: - if (phy->type == B43legacy_PHYTYPE_B) - new_phymode = B43legacy_PHYMODE_B; - else - new_phymode = B43legacy_PHYMODE_G; - break; - default: - B43legacy_WARN_ON(1); - } - err = b43legacy_switch_phymode(wl, new_phymode); - if (err) - goto out_unlock_mutex; - - /* Disable IRQs while reconfiguring the device. - * This makes it possible to drop the spinlock throughout - * the reconfiguration process. */ - spin_lock_irqsave(&wl->irq_lock, flags); - if (b43legacy_status(dev) < B43legacy_STAT_STARTED) { - spin_unlock_irqrestore(&wl->irq_lock, flags); - goto out_unlock_mutex; - } - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); - spin_unlock_irqrestore(&wl->irq_lock, flags); - b43legacy_synchronize_irq(dev); - - /* Switch to the requested channel. - * The firmware takes care of races with the TX handler. */ - if (conf->chandef.chan->hw_value != phy->channel) - b43legacy_radio_selectchannel(dev, conf->chandef.chan->hw_value, - 0); - - dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR); - - /* Adjust the desired TX power level. */ - if (conf->power_level != 0) { - if (conf->power_level != phy->power_level) { - phy->power_level = conf->power_level; - b43legacy_phy_xmitpower(dev); - } - } - - /* Antennas for RX and management frame TX. */ - b43legacy_mgmtframe_txantenna(dev, antenna_tx); - - if (wl->radio_enabled != phy->radio_on) { - if (wl->radio_enabled) { - b43legacy_radio_turn_on(dev); - b43legacyinfo(dev->wl, "Radio turned on by software\n"); - if (!dev->radio_hw_enable) - b43legacyinfo(dev->wl, "The hardware RF-kill" - " button still turns the radio" - " physically off. Press the" - " button to turn it on.\n"); - } else { - b43legacy_radio_turn_off(dev, 0); - b43legacyinfo(dev->wl, "Radio turned off by" - " software\n"); - } - } - - spin_lock_irqsave(&wl->irq_lock, flags); - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); - mmiowb(); - spin_unlock_irqrestore(&wl->irq_lock, flags); -out_unlock_mutex: - mutex_unlock(&wl->mutex); - - return err; -} - -static void b43legacy_update_basic_rates(struct b43legacy_wldev *dev, u32 brates) -{ - struct ieee80211_supported_band *sband = - dev->wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; - struct ieee80211_rate *rate; - int i; - u16 basic, direct, offset, basic_offset, rateptr; - - for (i = 0; i < sband->n_bitrates; i++) { - rate = &sband->bitrates[i]; - - if (b43legacy_is_cck_rate(rate->hw_value)) { - direct = B43legacy_SHM_SH_CCKDIRECT; - basic = B43legacy_SHM_SH_CCKBASIC; - offset = b43legacy_plcp_get_ratecode_cck(rate->hw_value); - offset &= 0xF; - } else { - direct = B43legacy_SHM_SH_OFDMDIRECT; - basic = B43legacy_SHM_SH_OFDMBASIC; - offset = b43legacy_plcp_get_ratecode_ofdm(rate->hw_value); - offset &= 0xF; - } - - rate = ieee80211_get_response_rate(sband, brates, rate->bitrate); - - if (b43legacy_is_cck_rate(rate->hw_value)) { - basic_offset = b43legacy_plcp_get_ratecode_cck(rate->hw_value); - basic_offset &= 0xF; - } else { - basic_offset = b43legacy_plcp_get_ratecode_ofdm(rate->hw_value); - basic_offset &= 0xF; - } - - /* - * Get the pointer that we need to point to - * from the direct map - */ - rateptr = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - direct + 2 * basic_offset); - /* and write it to the basic map */ - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - basic + 2 * offset, rateptr); - } -} - -static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf, - u32 changed) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - struct b43legacy_wldev *dev; - unsigned long flags; - - mutex_lock(&wl->mutex); - B43legacy_WARN_ON(wl->vif != vif); - - dev = wl->current_dev; - - /* Disable IRQs while reconfiguring the device. - * This makes it possible to drop the spinlock throughout - * the reconfiguration process. */ - spin_lock_irqsave(&wl->irq_lock, flags); - if (b43legacy_status(dev) < B43legacy_STAT_STARTED) { - spin_unlock_irqrestore(&wl->irq_lock, flags); - goto out_unlock_mutex; - } - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); - - if (changed & BSS_CHANGED_BSSID) { - b43legacy_synchronize_irq(dev); - - if (conf->bssid) - memcpy(wl->bssid, conf->bssid, ETH_ALEN); - else - eth_zero_addr(wl->bssid); - } - - if (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) { - if (changed & BSS_CHANGED_BEACON && - (b43legacy_is_mode(wl, NL80211_IFTYPE_AP) || - b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC))) - b43legacy_update_templates(wl); - - if (changed & BSS_CHANGED_BSSID) - b43legacy_write_mac_bssid_templates(dev); - } - spin_unlock_irqrestore(&wl->irq_lock, flags); - - b43legacy_mac_suspend(dev); - - if (changed & BSS_CHANGED_BEACON_INT && - (b43legacy_is_mode(wl, NL80211_IFTYPE_AP) || - b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC))) - b43legacy_set_beacon_int(dev, conf->beacon_int); - - if (changed & BSS_CHANGED_BASIC_RATES) - b43legacy_update_basic_rates(dev, conf->basic_rates); - - if (changed & BSS_CHANGED_ERP_SLOT) { - if (conf->use_short_slot) - b43legacy_short_slot_timing_enable(dev); - else - b43legacy_short_slot_timing_disable(dev); - } - - b43legacy_mac_enable(dev); - - spin_lock_irqsave(&wl->irq_lock, flags); - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); - /* XXX: why? */ - mmiowb(); - spin_unlock_irqrestore(&wl->irq_lock, flags); - out_unlock_mutex: - mutex_unlock(&wl->mutex); -} - -static void b43legacy_op_configure_filter(struct ieee80211_hw *hw, - unsigned int changed, - unsigned int *fflags,u64 multicast) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - struct b43legacy_wldev *dev = wl->current_dev; - unsigned long flags; - - if (!dev) { - *fflags = 0; - return; - } - - spin_lock_irqsave(&wl->irq_lock, flags); - *fflags &= FIF_ALLMULTI | - FIF_FCSFAIL | - FIF_PLCPFAIL | - FIF_CONTROL | - FIF_OTHER_BSS | - FIF_BCN_PRBRESP_PROMISC; - - changed &= FIF_ALLMULTI | - FIF_FCSFAIL | - FIF_PLCPFAIL | - FIF_CONTROL | - FIF_OTHER_BSS | - FIF_BCN_PRBRESP_PROMISC; - - wl->filter_flags = *fflags; - - if (changed && b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) - b43legacy_adjust_opmode(dev); - spin_unlock_irqrestore(&wl->irq_lock, flags); -} - -/* Locking: wl->mutex */ -static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev) -{ - struct b43legacy_wl *wl = dev->wl; - unsigned long flags; - int queue_num; - - if (b43legacy_status(dev) < B43legacy_STAT_STARTED) - return; - - /* Disable and sync interrupts. We must do this before than - * setting the status to INITIALIZED, as the interrupt handler - * won't care about IRQs then. */ - spin_lock_irqsave(&wl->irq_lock, flags); - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); - b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK); /* flush */ - spin_unlock_irqrestore(&wl->irq_lock, flags); - b43legacy_synchronize_irq(dev); - - b43legacy_set_status(dev, B43legacy_STAT_INITIALIZED); - - mutex_unlock(&wl->mutex); - /* Must unlock as it would otherwise deadlock. No races here. - * Cancel the possibly running self-rearming periodic work. */ - cancel_delayed_work_sync(&dev->periodic_work); - cancel_work_sync(&wl->tx_work); - mutex_lock(&wl->mutex); - - /* Drain all TX queues. */ - for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { - while (skb_queue_len(&wl->tx_queue[queue_num])) - dev_kfree_skb(skb_dequeue(&wl->tx_queue[queue_num])); - } - -b43legacy_mac_suspend(dev); - free_irq(dev->dev->irq, dev); - b43legacydbg(wl, "Wireless interface stopped\n"); -} - -/* Locking: wl->mutex */ -static int b43legacy_wireless_core_start(struct b43legacy_wldev *dev) -{ - int err; - - B43legacy_WARN_ON(b43legacy_status(dev) != B43legacy_STAT_INITIALIZED); - - drain_txstatus_queue(dev); - err = request_irq(dev->dev->irq, b43legacy_interrupt_handler, - IRQF_SHARED, KBUILD_MODNAME, dev); - if (err) { - b43legacyerr(dev->wl, "Cannot request IRQ-%d\n", - dev->dev->irq); - goto out; - } - /* We are ready to run. */ - ieee80211_wake_queues(dev->wl->hw); - b43legacy_set_status(dev, B43legacy_STAT_STARTED); - - /* Start data flow (TX/RX) */ - b43legacy_mac_enable(dev); - b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); - - /* Start maintenance work */ - b43legacy_periodic_tasks_setup(dev); - - b43legacydbg(dev->wl, "Wireless interface started\n"); -out: - return err; -} - -/* Get PHY and RADIO versioning numbers */ -static int b43legacy_phy_versioning(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u32 tmp; - u8 analog_type; - u8 phy_type; - u8 phy_rev; - u16 radio_manuf; - u16 radio_ver; - u16 radio_rev; - int unsupported = 0; - - /* Get PHY versioning */ - tmp = b43legacy_read16(dev, B43legacy_MMIO_PHY_VER); - analog_type = (tmp & B43legacy_PHYVER_ANALOG) - >> B43legacy_PHYVER_ANALOG_SHIFT; - phy_type = (tmp & B43legacy_PHYVER_TYPE) >> B43legacy_PHYVER_TYPE_SHIFT; - phy_rev = (tmp & B43legacy_PHYVER_VERSION); - switch (phy_type) { - case B43legacy_PHYTYPE_B: - if (phy_rev != 2 && phy_rev != 4 - && phy_rev != 6 && phy_rev != 7) - unsupported = 1; - break; - case B43legacy_PHYTYPE_G: - if (phy_rev > 8) - unsupported = 1; - break; - default: - unsupported = 1; - } - if (unsupported) { - b43legacyerr(dev->wl, "FOUND UNSUPPORTED PHY " - "(Analog %u, Type %u, Revision %u)\n", - analog_type, phy_type, phy_rev); - return -EOPNOTSUPP; - } - b43legacydbg(dev->wl, "Found PHY: Analog %u, Type %u, Revision %u\n", - analog_type, phy_type, phy_rev); - - - /* Get RADIO versioning */ - if (dev->dev->bus->chip_id == 0x4317) { - if (dev->dev->bus->chip_rev == 0) - tmp = 0x3205017F; - else if (dev->dev->bus->chip_rev == 1) - tmp = 0x4205017F; - else - tmp = 0x5205017F; - } else { - b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, - B43legacy_RADIOCTL_ID); - tmp = b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_HIGH); - tmp <<= 16; - b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, - B43legacy_RADIOCTL_ID); - tmp |= b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_LOW); - } - radio_manuf = (tmp & 0x00000FFF); - radio_ver = (tmp & 0x0FFFF000) >> 12; - radio_rev = (tmp & 0xF0000000) >> 28; - switch (phy_type) { - case B43legacy_PHYTYPE_B: - if ((radio_ver & 0xFFF0) != 0x2050) - unsupported = 1; - break; - case B43legacy_PHYTYPE_G: - if (radio_ver != 0x2050) - unsupported = 1; - break; - default: - B43legacy_BUG_ON(1); - } - if (unsupported) { - b43legacyerr(dev->wl, "FOUND UNSUPPORTED RADIO " - "(Manuf 0x%X, Version 0x%X, Revision %u)\n", - radio_manuf, radio_ver, radio_rev); - return -EOPNOTSUPP; - } - b43legacydbg(dev->wl, "Found Radio: Manuf 0x%X, Version 0x%X," - " Revision %u\n", radio_manuf, radio_ver, radio_rev); - - - phy->radio_manuf = radio_manuf; - phy->radio_ver = radio_ver; - phy->radio_rev = radio_rev; - - phy->analog = analog_type; - phy->type = phy_type; - phy->rev = phy_rev; - - return 0; -} - -static void setup_struct_phy_for_init(struct b43legacy_wldev *dev, - struct b43legacy_phy *phy) -{ - struct b43legacy_lopair *lo; - int i; - - memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig)); - memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos)); - - /* Assume the radio is enabled. If it's not enabled, the state will - * immediately get fixed on the first periodic work run. */ - dev->radio_hw_enable = true; - - phy->savedpctlreg = 0xFFFF; - phy->aci_enable = false; - phy->aci_wlan_automatic = false; - phy->aci_hw_rssi = false; - - lo = phy->_lo_pairs; - if (lo) - memset(lo, 0, sizeof(struct b43legacy_lopair) * - B43legacy_LO_COUNT); - phy->max_lb_gain = 0; - phy->trsw_rx_gain = 0; - - /* Set default attenuation values. */ - phy->bbatt = b43legacy_default_baseband_attenuation(dev); - phy->rfatt = b43legacy_default_radio_attenuation(dev); - phy->txctl1 = b43legacy_default_txctl1(dev); - phy->txpwr_offset = 0; - - /* NRSSI */ - phy->nrssislope = 0; - for (i = 0; i < ARRAY_SIZE(phy->nrssi); i++) - phy->nrssi[i] = -1000; - for (i = 0; i < ARRAY_SIZE(phy->nrssi_lt); i++) - phy->nrssi_lt[i] = i; - - phy->lofcal = 0xFFFF; - phy->initval = 0xFFFF; - - phy->interfmode = B43legacy_INTERFMODE_NONE; - phy->channel = 0xFF; -} - -static void setup_struct_wldev_for_init(struct b43legacy_wldev *dev) -{ - /* Flags */ - dev->dfq_valid = false; - - /* Stats */ - memset(&dev->stats, 0, sizeof(dev->stats)); - - setup_struct_phy_for_init(dev, &dev->phy); - - /* IRQ related flags */ - dev->irq_reason = 0; - memset(dev->dma_reason, 0, sizeof(dev->dma_reason)); - dev->irq_mask = B43legacy_IRQ_MASKTEMPLATE; - - dev->mac_suspended = 1; - - /* Noise calculation context */ - memset(&dev->noisecalc, 0, sizeof(dev->noisecalc)); -} - -static void b43legacy_set_synth_pu_delay(struct b43legacy_wldev *dev, - bool idle) { - u16 pu_delay = 1050; - - if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_ADHOC) || idle) - pu_delay = 500; - if ((dev->phy.radio_ver == 0x2050) && (dev->phy.radio_rev == 8)) - pu_delay = max(pu_delay, (u16)2400); - - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_SPUWKUP, pu_delay); -} - -/* Set the TSF CFP pre-TargetBeaconTransmissionTime. */ -static void b43legacy_set_pretbtt(struct b43legacy_wldev *dev) -{ - u16 pretbtt; - - /* The time value is in microseconds. */ - if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) - pretbtt = 2; - else - pretbtt = 250; - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_PRETBTT, pretbtt); - b43legacy_write16(dev, B43legacy_MMIO_TSF_CFP_PRETBTT, pretbtt); -} - -/* Shutdown a wireless core */ -/* Locking: wl->mutex */ -static void b43legacy_wireless_core_exit(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u32 macctl; - - B43legacy_WARN_ON(b43legacy_status(dev) > B43legacy_STAT_INITIALIZED); - if (b43legacy_status(dev) != B43legacy_STAT_INITIALIZED) - return; - b43legacy_set_status(dev, B43legacy_STAT_UNINIT); - - /* Stop the microcode PSM. */ - macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - macctl &= ~B43legacy_MACCTL_PSM_RUN; - macctl |= B43legacy_MACCTL_PSM_JMP0; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); - - b43legacy_leds_exit(dev); - b43legacy_rng_exit(dev->wl); - b43legacy_pio_free(dev); - b43legacy_dma_free(dev); - b43legacy_chip_exit(dev); - b43legacy_radio_turn_off(dev, 1); - b43legacy_switch_analog(dev, 0); - if (phy->dyn_tssi_tbl) - kfree(phy->tssi2dbm); - kfree(phy->lo_control); - phy->lo_control = NULL; - if (dev->wl->current_beacon) { - dev_kfree_skb_any(dev->wl->current_beacon); - dev->wl->current_beacon = NULL; - } - - ssb_device_disable(dev->dev, 0); - ssb_bus_may_powerdown(dev->dev->bus); -} - -static void prepare_phy_data_for_init(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - int i; - - /* Set default attenuation values. */ - phy->bbatt = b43legacy_default_baseband_attenuation(dev); - phy->rfatt = b43legacy_default_radio_attenuation(dev); - phy->txctl1 = b43legacy_default_txctl1(dev); - phy->txctl2 = 0xFFFF; - phy->txpwr_offset = 0; - - /* NRSSI */ - phy->nrssislope = 0; - for (i = 0; i < ARRAY_SIZE(phy->nrssi); i++) - phy->nrssi[i] = -1000; - for (i = 0; i < ARRAY_SIZE(phy->nrssi_lt); i++) - phy->nrssi_lt[i] = i; - - phy->lofcal = 0xFFFF; - phy->initval = 0xFFFF; - - phy->aci_enable = false; - phy->aci_wlan_automatic = false; - phy->aci_hw_rssi = false; - - phy->antenna_diversity = 0xFFFF; - memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig)); - memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos)); - - /* Flags */ - phy->calibrated = 0; - - if (phy->_lo_pairs) - memset(phy->_lo_pairs, 0, - sizeof(struct b43legacy_lopair) * B43legacy_LO_COUNT); - memset(phy->loopback_gain, 0, sizeof(phy->loopback_gain)); -} - -/* Initialize a wireless core */ -static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev) -{ - struct b43legacy_wl *wl = dev->wl; - struct ssb_bus *bus = dev->dev->bus; - struct b43legacy_phy *phy = &dev->phy; - struct ssb_sprom *sprom = &dev->dev->bus->sprom; - int err; - u32 hf; - u32 tmp; - - B43legacy_WARN_ON(b43legacy_status(dev) != B43legacy_STAT_UNINIT); - - err = ssb_bus_powerup(bus, 0); - if (err) - goto out; - if (!ssb_device_is_enabled(dev->dev)) { - tmp = phy->gmode ? B43legacy_TMSLOW_GMODE : 0; - b43legacy_wireless_core_reset(dev, tmp); - } - - if ((phy->type == B43legacy_PHYTYPE_B) || - (phy->type == B43legacy_PHYTYPE_G)) { - phy->_lo_pairs = kzalloc(sizeof(struct b43legacy_lopair) - * B43legacy_LO_COUNT, - GFP_KERNEL); - if (!phy->_lo_pairs) - return -ENOMEM; - } - setup_struct_wldev_for_init(dev); - - err = b43legacy_phy_init_tssi2dbm_table(dev); - if (err) - goto err_kfree_lo_control; - - /* Enable IRQ routing to this device. */ - ssb_pcicore_dev_irqvecs_enable(&bus->pcicore, dev->dev); - - prepare_phy_data_for_init(dev); - b43legacy_phy_calibrate(dev); - err = b43legacy_chip_init(dev); - if (err) - goto err_kfree_tssitbl; - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_WLCOREREV, - dev->dev->id.revision); - hf = b43legacy_hf_read(dev); - if (phy->type == B43legacy_PHYTYPE_G) { - hf |= B43legacy_HF_SYMW; - if (phy->rev == 1) - hf |= B43legacy_HF_GDCW; - if (sprom->boardflags_lo & B43legacy_BFL_PACTRL) - hf |= B43legacy_HF_OFDMPABOOST; - } else if (phy->type == B43legacy_PHYTYPE_B) { - hf |= B43legacy_HF_SYMW; - if (phy->rev >= 2 && phy->radio_ver == 0x2050) - hf &= ~B43legacy_HF_GDCW; - } - b43legacy_hf_write(dev, hf); - - b43legacy_set_retry_limits(dev, - B43legacy_DEFAULT_SHORT_RETRY_LIMIT, - B43legacy_DEFAULT_LONG_RETRY_LIMIT); - - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - 0x0044, 3); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - 0x0046, 2); - - /* Disable sending probe responses from firmware. - * Setting the MaxTime to one usec will always trigger - * a timeout, so we never send any probe resp. - * A timeout of zero is infinite. */ - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, - B43legacy_SHM_SH_PRMAXTIME, 1); - - b43legacy_rate_memory_init(dev); - - /* Minimum Contention Window */ - if (phy->type == B43legacy_PHYTYPE_B) - b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, - 0x0003, 31); - else - b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, - 0x0003, 15); - /* Maximum Contention Window */ - b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, - 0x0004, 1023); - - do { - if (b43legacy_using_pio(dev)) - err = b43legacy_pio_init(dev); - else { - err = b43legacy_dma_init(dev); - if (!err) - b43legacy_qos_init(dev); - } - } while (err == -EAGAIN); - if (err) - goto err_chip_exit; - - b43legacy_set_synth_pu_delay(dev, 1); - - ssb_bus_powerup(bus, 1); /* Enable dynamic PCTL */ - b43legacy_upload_card_macaddress(dev); - b43legacy_security_init(dev); - b43legacy_rng_init(wl); - - ieee80211_wake_queues(dev->wl->hw); - b43legacy_set_status(dev, B43legacy_STAT_INITIALIZED); - - b43legacy_leds_init(dev); -out: - return err; - -err_chip_exit: - b43legacy_chip_exit(dev); -err_kfree_tssitbl: - if (phy->dyn_tssi_tbl) - kfree(phy->tssi2dbm); -err_kfree_lo_control: - kfree(phy->lo_control); - phy->lo_control = NULL; - ssb_bus_may_powerdown(bus); - B43legacy_WARN_ON(b43legacy_status(dev) != B43legacy_STAT_UNINIT); - return err; -} - -static int b43legacy_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - struct b43legacy_wldev *dev; - unsigned long flags; - int err = -EOPNOTSUPP; - - /* TODO: allow WDS/AP devices to coexist */ - - if (vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_STATION && - vif->type != NL80211_IFTYPE_WDS && - vif->type != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - mutex_lock(&wl->mutex); - if (wl->operating) - goto out_mutex_unlock; - - b43legacydbg(wl, "Adding Interface type %d\n", vif->type); - - dev = wl->current_dev; - wl->operating = true; - wl->vif = vif; - wl->if_type = vif->type; - memcpy(wl->mac_addr, vif->addr, ETH_ALEN); - - spin_lock_irqsave(&wl->irq_lock, flags); - b43legacy_adjust_opmode(dev); - b43legacy_set_pretbtt(dev); - b43legacy_set_synth_pu_delay(dev, 0); - b43legacy_upload_card_macaddress(dev); - spin_unlock_irqrestore(&wl->irq_lock, flags); - - err = 0; - out_mutex_unlock: - mutex_unlock(&wl->mutex); - - return err; -} - -static void b43legacy_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - struct b43legacy_wldev *dev = wl->current_dev; - unsigned long flags; - - b43legacydbg(wl, "Removing Interface type %d\n", vif->type); - - mutex_lock(&wl->mutex); - - B43legacy_WARN_ON(!wl->operating); - B43legacy_WARN_ON(wl->vif != vif); - wl->vif = NULL; - - wl->operating = false; - - spin_lock_irqsave(&wl->irq_lock, flags); - b43legacy_adjust_opmode(dev); - eth_zero_addr(wl->mac_addr); - b43legacy_upload_card_macaddress(dev); - spin_unlock_irqrestore(&wl->irq_lock, flags); - - mutex_unlock(&wl->mutex); -} - -static int b43legacy_op_start(struct ieee80211_hw *hw) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - struct b43legacy_wldev *dev = wl->current_dev; - int did_init = 0; - int err = 0; - - /* Kill all old instance specific information to make sure - * the card won't use it in the short timeframe between start - * and mac80211 reconfiguring it. */ - eth_zero_addr(wl->bssid); - eth_zero_addr(wl->mac_addr); - wl->filter_flags = 0; - wl->beacon0_uploaded = false; - wl->beacon1_uploaded = false; - wl->beacon_templates_virgin = true; - wl->radio_enabled = true; - - mutex_lock(&wl->mutex); - - if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) { - err = b43legacy_wireless_core_init(dev); - if (err) - goto out_mutex_unlock; - did_init = 1; - } - - if (b43legacy_status(dev) < B43legacy_STAT_STARTED) { - err = b43legacy_wireless_core_start(dev); - if (err) { - if (did_init) - b43legacy_wireless_core_exit(dev); - goto out_mutex_unlock; - } - } - - wiphy_rfkill_start_polling(hw->wiphy); - -out_mutex_unlock: - mutex_unlock(&wl->mutex); - - return err; -} - -static void b43legacy_op_stop(struct ieee80211_hw *hw) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - struct b43legacy_wldev *dev = wl->current_dev; - - cancel_work_sync(&(wl->beacon_update_trigger)); - - mutex_lock(&wl->mutex); - if (b43legacy_status(dev) >= B43legacy_STAT_STARTED) - b43legacy_wireless_core_stop(dev); - b43legacy_wireless_core_exit(dev); - wl->radio_enabled = false; - mutex_unlock(&wl->mutex); -} - -static int b43legacy_op_beacon_set_tim(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, bool set) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - unsigned long flags; - - spin_lock_irqsave(&wl->irq_lock, flags); - b43legacy_update_templates(wl); - spin_unlock_irqrestore(&wl->irq_lock, flags); - - return 0; -} - -static int b43legacy_op_get_survey(struct ieee80211_hw *hw, int idx, - struct survey_info *survey) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - struct b43legacy_wldev *dev = wl->current_dev; - struct ieee80211_conf *conf = &hw->conf; - - if (idx != 0) - return -ENOENT; - - survey->channel = conf->chandef.chan; - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = dev->stats.link_noise; - - return 0; -} - -static const struct ieee80211_ops b43legacy_hw_ops = { - .tx = b43legacy_op_tx, - .conf_tx = b43legacy_op_conf_tx, - .add_interface = b43legacy_op_add_interface, - .remove_interface = b43legacy_op_remove_interface, - .config = b43legacy_op_dev_config, - .bss_info_changed = b43legacy_op_bss_info_changed, - .configure_filter = b43legacy_op_configure_filter, - .get_stats = b43legacy_op_get_stats, - .start = b43legacy_op_start, - .stop = b43legacy_op_stop, - .set_tim = b43legacy_op_beacon_set_tim, - .get_survey = b43legacy_op_get_survey, - .rfkill_poll = b43legacy_rfkill_poll, -}; - -/* Hard-reset the chip. Do not call this directly. - * Use b43legacy_controller_restart() - */ -static void b43legacy_chip_reset(struct work_struct *work) -{ - struct b43legacy_wldev *dev = - container_of(work, struct b43legacy_wldev, restart_work); - struct b43legacy_wl *wl = dev->wl; - int err = 0; - int prev_status; - - mutex_lock(&wl->mutex); - - prev_status = b43legacy_status(dev); - /* Bring the device down... */ - if (prev_status >= B43legacy_STAT_STARTED) - b43legacy_wireless_core_stop(dev); - if (prev_status >= B43legacy_STAT_INITIALIZED) - b43legacy_wireless_core_exit(dev); - - /* ...and up again. */ - if (prev_status >= B43legacy_STAT_INITIALIZED) { - err = b43legacy_wireless_core_init(dev); - if (err) - goto out; - } - if (prev_status >= B43legacy_STAT_STARTED) { - err = b43legacy_wireless_core_start(dev); - if (err) { - b43legacy_wireless_core_exit(dev); - goto out; - } - } -out: - if (err) - wl->current_dev = NULL; /* Failed to init the dev. */ - mutex_unlock(&wl->mutex); - if (err) - b43legacyerr(wl, "Controller restart FAILED\n"); - else - b43legacyinfo(wl, "Controller restarted\n"); -} - -static int b43legacy_setup_modes(struct b43legacy_wldev *dev, - int have_bphy, - int have_gphy) -{ - struct ieee80211_hw *hw = dev->wl->hw; - struct b43legacy_phy *phy = &dev->phy; - - phy->possible_phymodes = 0; - if (have_bphy) { - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &b43legacy_band_2GHz_BPHY; - phy->possible_phymodes |= B43legacy_PHYMODE_B; - } - - if (have_gphy) { - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &b43legacy_band_2GHz_GPHY; - phy->possible_phymodes |= B43legacy_PHYMODE_G; - } - - return 0; -} - -static void b43legacy_wireless_core_detach(struct b43legacy_wldev *dev) -{ - /* We release firmware that late to not be required to re-request - * is all the time when we reinit the core. */ - b43legacy_release_firmware(dev); -} - -static int b43legacy_wireless_core_attach(struct b43legacy_wldev *dev) -{ - struct b43legacy_wl *wl = dev->wl; - struct ssb_bus *bus = dev->dev->bus; - struct pci_dev *pdev = (bus->bustype == SSB_BUSTYPE_PCI) ? bus->host_pci : NULL; - int err; - int have_bphy = 0; - int have_gphy = 0; - u32 tmp; - - /* Do NOT do any device initialization here. - * Do it in wireless_core_init() instead. - * This function is for gathering basic information about the HW, only. - * Also some structs may be set up here. But most likely you want to - * have that in core_init(), too. - */ - - err = ssb_bus_powerup(bus, 0); - if (err) { - b43legacyerr(wl, "Bus powerup failed\n"); - goto out; - } - /* Get the PHY type. */ - if (dev->dev->id.revision >= 5) { - u32 tmshigh; - - tmshigh = ssb_read32(dev->dev, SSB_TMSHIGH); - have_gphy = !!(tmshigh & B43legacy_TMSHIGH_GPHY); - if (!have_gphy) - have_bphy = 1; - } else if (dev->dev->id.revision == 4) - have_gphy = 1; - else - have_bphy = 1; - - dev->phy.gmode = (have_gphy || have_bphy); - dev->phy.radio_on = true; - tmp = dev->phy.gmode ? B43legacy_TMSLOW_GMODE : 0; - b43legacy_wireless_core_reset(dev, tmp); - - err = b43legacy_phy_versioning(dev); - if (err) - goto err_powerdown; - /* Check if this device supports multiband. */ - if (!pdev || - (pdev->device != 0x4312 && - pdev->device != 0x4319 && - pdev->device != 0x4324)) { - /* No multiband support. */ - have_bphy = 0; - have_gphy = 0; - switch (dev->phy.type) { - case B43legacy_PHYTYPE_B: - have_bphy = 1; - break; - case B43legacy_PHYTYPE_G: - have_gphy = 1; - break; - default: - B43legacy_BUG_ON(1); - } - } - dev->phy.gmode = (have_gphy || have_bphy); - tmp = dev->phy.gmode ? B43legacy_TMSLOW_GMODE : 0; - b43legacy_wireless_core_reset(dev, tmp); - - err = b43legacy_validate_chipaccess(dev); - if (err) - goto err_powerdown; - err = b43legacy_setup_modes(dev, have_bphy, have_gphy); - if (err) - goto err_powerdown; - - /* Now set some default "current_dev" */ - if (!wl->current_dev) - wl->current_dev = dev; - INIT_WORK(&dev->restart_work, b43legacy_chip_reset); - - b43legacy_radio_turn_off(dev, 1); - b43legacy_switch_analog(dev, 0); - ssb_device_disable(dev->dev, 0); - ssb_bus_may_powerdown(bus); - -out: - return err; - -err_powerdown: - ssb_bus_may_powerdown(bus); - return err; -} - -static void b43legacy_one_core_detach(struct ssb_device *dev) -{ - struct b43legacy_wldev *wldev; - struct b43legacy_wl *wl; - - /* Do not cancel ieee80211-workqueue based work here. - * See comment in b43legacy_remove(). */ - - wldev = ssb_get_drvdata(dev); - wl = wldev->wl; - b43legacy_debugfs_remove_device(wldev); - b43legacy_wireless_core_detach(wldev); - list_del(&wldev->list); - wl->nr_devs--; - ssb_set_drvdata(dev, NULL); - kfree(wldev); -} - -static int b43legacy_one_core_attach(struct ssb_device *dev, - struct b43legacy_wl *wl) -{ - struct b43legacy_wldev *wldev; - int err = -ENOMEM; - - wldev = kzalloc(sizeof(*wldev), GFP_KERNEL); - if (!wldev) - goto out; - - wldev->dev = dev; - wldev->wl = wl; - b43legacy_set_status(wldev, B43legacy_STAT_UNINIT); - wldev->bad_frames_preempt = modparam_bad_frames_preempt; - tasklet_init(&wldev->isr_tasklet, - (void (*)(unsigned long))b43legacy_interrupt_tasklet, - (unsigned long)wldev); - if (modparam_pio) - wldev->__using_pio = true; - INIT_LIST_HEAD(&wldev->list); - - err = b43legacy_wireless_core_attach(wldev); - if (err) - goto err_kfree_wldev; - - list_add(&wldev->list, &wl->devlist); - wl->nr_devs++; - ssb_set_drvdata(dev, wldev); - b43legacy_debugfs_add_device(wldev); -out: - return err; - -err_kfree_wldev: - kfree(wldev); - return err; -} - -static void b43legacy_sprom_fixup(struct ssb_bus *bus) -{ - /* boardflags workarounds */ - if (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE && - bus->boardinfo.type == 0x4E && - bus->sprom.board_rev > 0x40) - bus->sprom.boardflags_lo |= B43legacy_BFL_PACTRL; -} - -static void b43legacy_wireless_exit(struct ssb_device *dev, - struct b43legacy_wl *wl) -{ - struct ieee80211_hw *hw = wl->hw; - - ssb_set_devtypedata(dev, NULL); - ieee80211_free_hw(hw); -} - -static int b43legacy_wireless_init(struct ssb_device *dev) -{ - struct ssb_sprom *sprom = &dev->bus->sprom; - struct ieee80211_hw *hw; - struct b43legacy_wl *wl; - int err = -ENOMEM; - int queue_num; - - b43legacy_sprom_fixup(dev->bus); - - hw = ieee80211_alloc_hw(sizeof(*wl), &b43legacy_hw_ops); - if (!hw) { - b43legacyerr(NULL, "Could not allocate ieee80211 device\n"); - goto out; - } - - /* fill hw info */ - ieee80211_hw_set(hw, RX_INCLUDES_FCS); - ieee80211_hw_set(hw, SIGNAL_DBM); - - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_WDS) | - BIT(NL80211_IFTYPE_ADHOC); - hw->queues = 1; /* FIXME: hardware has more queues */ - hw->max_rates = 2; - SET_IEEE80211_DEV(hw, dev->dev); - if (is_valid_ether_addr(sprom->et1mac)) - SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac); - else - SET_IEEE80211_PERM_ADDR(hw, sprom->il0mac); - - /* Get and initialize struct b43legacy_wl */ - wl = hw_to_b43legacy_wl(hw); - memset(wl, 0, sizeof(*wl)); - wl->hw = hw; - spin_lock_init(&wl->irq_lock); - spin_lock_init(&wl->leds_lock); - mutex_init(&wl->mutex); - INIT_LIST_HEAD(&wl->devlist); - INIT_WORK(&wl->beacon_update_trigger, b43legacy_beacon_update_trigger_work); - INIT_WORK(&wl->tx_work, b43legacy_tx_work); - - /* Initialize queues and flags. */ - for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { - skb_queue_head_init(&wl->tx_queue[queue_num]); - wl->tx_queue_stopped[queue_num] = 0; - } - - ssb_set_devtypedata(dev, wl); - b43legacyinfo(wl, "Broadcom %04X WLAN found (core revision %u)\n", - dev->bus->chip_id, dev->id.revision); - err = 0; -out: - return err; -} - -static int b43legacy_probe(struct ssb_device *dev, - const struct ssb_device_id *id) -{ - struct b43legacy_wl *wl; - int err; - int first = 0; - - wl = ssb_get_devtypedata(dev); - if (!wl) { - /* Probing the first core - setup common struct b43legacy_wl */ - first = 1; - err = b43legacy_wireless_init(dev); - if (err) - goto out; - wl = ssb_get_devtypedata(dev); - B43legacy_WARN_ON(!wl); - } - err = b43legacy_one_core_attach(dev, wl); - if (err) - goto err_wireless_exit; - - /* setup and start work to load firmware */ - INIT_WORK(&wl->firmware_load, b43legacy_request_firmware); - schedule_work(&wl->firmware_load); - -out: - return err; - -err_wireless_exit: - if (first) - b43legacy_wireless_exit(dev, wl); - return err; -} - -static void b43legacy_remove(struct ssb_device *dev) -{ - struct b43legacy_wl *wl = ssb_get_devtypedata(dev); - struct b43legacy_wldev *wldev = ssb_get_drvdata(dev); - - /* We must cancel any work here before unregistering from ieee80211, - * 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) - return; /* NULL if fw never loaded */ - if (wl->current_dev == wldev) - ieee80211_unregister_hw(wl->hw); - - b43legacy_one_core_detach(dev); - - if (list_empty(&wl->devlist)) - /* Last core on the chip unregistered. - * We can destroy common struct b43legacy_wl. - */ - b43legacy_wireless_exit(dev, wl); -} - -/* Perform a hardware reset. This can be called from any context. */ -void b43legacy_controller_restart(struct b43legacy_wldev *dev, - const char *reason) -{ - /* Must avoid requeueing, if we are in shutdown. */ - if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) - return; - b43legacyinfo(dev->wl, "Controller RESET (%s) ...\n", reason); - ieee80211_queue_work(dev->wl->hw, &dev->restart_work); -} - -#ifdef CONFIG_PM - -static int b43legacy_suspend(struct ssb_device *dev, pm_message_t state) -{ - struct b43legacy_wldev *wldev = ssb_get_drvdata(dev); - struct b43legacy_wl *wl = wldev->wl; - - b43legacydbg(wl, "Suspending...\n"); - - mutex_lock(&wl->mutex); - wldev->suspend_init_status = b43legacy_status(wldev); - if (wldev->suspend_init_status >= B43legacy_STAT_STARTED) - b43legacy_wireless_core_stop(wldev); - if (wldev->suspend_init_status >= B43legacy_STAT_INITIALIZED) - b43legacy_wireless_core_exit(wldev); - mutex_unlock(&wl->mutex); - - b43legacydbg(wl, "Device suspended.\n"); - - return 0; -} - -static int b43legacy_resume(struct ssb_device *dev) -{ - struct b43legacy_wldev *wldev = ssb_get_drvdata(dev); - struct b43legacy_wl *wl = wldev->wl; - int err = 0; - - b43legacydbg(wl, "Resuming...\n"); - - mutex_lock(&wl->mutex); - if (wldev->suspend_init_status >= B43legacy_STAT_INITIALIZED) { - err = b43legacy_wireless_core_init(wldev); - if (err) { - b43legacyerr(wl, "Resume failed at core init\n"); - goto out; - } - } - if (wldev->suspend_init_status >= B43legacy_STAT_STARTED) { - err = b43legacy_wireless_core_start(wldev); - if (err) { - b43legacy_wireless_core_exit(wldev); - b43legacyerr(wl, "Resume failed at core start\n"); - goto out; - } - } - - b43legacydbg(wl, "Device resumed.\n"); -out: - mutex_unlock(&wl->mutex); - return err; -} - -#else /* CONFIG_PM */ -# define b43legacy_suspend NULL -# define b43legacy_resume NULL -#endif /* CONFIG_PM */ - -static struct ssb_driver b43legacy_ssb_driver = { - .name = KBUILD_MODNAME, - .id_table = b43legacy_ssb_tbl, - .probe = b43legacy_probe, - .remove = b43legacy_remove, - .suspend = b43legacy_suspend, - .resume = b43legacy_resume, -}; - -static void b43legacy_print_driverinfo(void) -{ - const char *feat_pci = "", *feat_leds = "", - *feat_pio = "", *feat_dma = ""; - -#ifdef CONFIG_B43LEGACY_PCI_AUTOSELECT - feat_pci = "P"; -#endif -#ifdef CONFIG_B43LEGACY_LEDS - feat_leds = "L"; -#endif -#ifdef CONFIG_B43LEGACY_PIO - feat_pio = "I"; -#endif -#ifdef CONFIG_B43LEGACY_DMA - feat_dma = "D"; -#endif - printk(KERN_INFO "Broadcom 43xx-legacy driver loaded " - "[ Features: %s%s%s%s ]\n", - feat_pci, feat_leds, feat_pio, feat_dma); -} - -static int __init b43legacy_init(void) -{ - int err; - - b43legacy_debugfs_init(); - - err = ssb_driver_register(&b43legacy_ssb_driver); - if (err) - goto err_dfs_exit; - - b43legacy_print_driverinfo(); - - return err; - -err_dfs_exit: - b43legacy_debugfs_exit(); - return err; -} - -static void __exit b43legacy_exit(void) -{ - ssb_driver_unregister(&b43legacy_ssb_driver); - b43legacy_debugfs_exit(); -} - -module_init(b43legacy_init) -module_exit(b43legacy_exit) diff --git a/drivers/net/wireless/b43legacy/main.h b/drivers/net/wireless/b43legacy/main.h deleted file mode 100644 index b74a058d7bac..000000000000 --- a/drivers/net/wireless/b43legacy/main.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - - Broadcom B43legacy wireless driver - - Copyright (c) 2005 Martin Langer , - Copyright (c) 2005 Stefano Brivio - Copyright (c) 2005, 2006 Michael Buesch - Copyright (c) 2005 Danny van Dyk - Copyright (c) 2005 Andreas Jaggi - Copyright (c) 2007 Larry Finger - - Some parts of the code in this file are derived from the ipw2200 - driver Copyright(c) 2003 - 2004 Intel Corporation. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#ifndef B43legacy_MAIN_H_ -#define B43legacy_MAIN_H_ - -#include "b43legacy.h" - - -#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] -#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) -/* Magic helper macro to pad structures. Ignore those above. It's magic. */ -#define PAD_BYTES(nr_bytes) P4D_BYTES(__LINE__ , (nr_bytes)) - - -/* Lightweight function to convert a frequency (in Mhz) to a channel number. */ -static inline -u8 b43legacy_freq_to_channel_bg(int freq) -{ - u8 channel; - - if (freq == 2484) - channel = 14; - else - channel = (freq - 2407) / 5; - - return channel; -} -static inline -u8 b43legacy_freq_to_channel(struct b43legacy_wldev *dev, - int freq) -{ - return b43legacy_freq_to_channel_bg(freq); -} - -/* Lightweight function to convert a channel number to a frequency (in Mhz). */ -static inline -int b43legacy_channel_to_freq_bg(u8 channel) -{ - int freq; - - if (channel == 14) - freq = 2484; - else - freq = 2407 + (5 * channel); - - return freq; -} - -static inline -int b43legacy_channel_to_freq(struct b43legacy_wldev *dev, - u8 channel) -{ - return b43legacy_channel_to_freq_bg(channel); -} - -static inline -int b43legacy_is_cck_rate(int rate) -{ - return (rate == B43legacy_CCK_RATE_1MB || - rate == B43legacy_CCK_RATE_2MB || - rate == B43legacy_CCK_RATE_5MB || - rate == B43legacy_CCK_RATE_11MB); -} - -static inline -int b43legacy_is_ofdm_rate(int rate) -{ - return !b43legacy_is_cck_rate(rate); -} - -void b43legacy_tsf_read(struct b43legacy_wldev *dev, u64 *tsf); -void b43legacy_tsf_write(struct b43legacy_wldev *dev, u64 tsf); - -u32 b43legacy_shm_read32(struct b43legacy_wldev *dev, - u16 routing, u16 offset); -u16 b43legacy_shm_read16(struct b43legacy_wldev *dev, - u16 routing, u16 offset); -void b43legacy_shm_write32(struct b43legacy_wldev *dev, - u16 routing, u16 offset, - u32 value); -void b43legacy_shm_write16(struct b43legacy_wldev *dev, - u16 routing, u16 offset, - u16 value); - -u32 b43legacy_hf_read(struct b43legacy_wldev *dev); -void b43legacy_hf_write(struct b43legacy_wldev *dev, u32 value); - -void b43legacy_dummy_transmission(struct b43legacy_wldev *dev); - -void b43legacy_wireless_core_reset(struct b43legacy_wldev *dev, u32 flags); - -void b43legacy_mac_suspend(struct b43legacy_wldev *dev); -void b43legacy_mac_enable(struct b43legacy_wldev *dev); - -void b43legacy_controller_restart(struct b43legacy_wldev *dev, - const char *reason); - -#endif /* B43legacy_MAIN_H_ */ diff --git a/drivers/net/wireless/b43legacy/phy.c b/drivers/net/wireless/b43legacy/phy.c deleted file mode 100644 index 995c7d0c212a..000000000000 --- a/drivers/net/wireless/b43legacy/phy.c +++ /dev/null @@ -1,2258 +0,0 @@ -/* - - Broadcom B43legacy wireless driver - - Copyright (c) 2005 Martin Langer , - Stefano Brivio - Michael Buesch - Danny van Dyk - Andreas Jaggi - Copyright (c) 2007 Larry Finger - - Some parts of the code in this file are derived from the ipw2200 - driver Copyright(c) 2003 - 2004 Intel Corporation. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include -#include -#include -#include -#include - -#include "b43legacy.h" -#include "phy.h" -#include "main.h" -#include "radio.h" -#include "ilt.h" - - -static const s8 b43legacy_tssi2dbm_b_table[] = { - 0x4D, 0x4C, 0x4B, 0x4A, - 0x4A, 0x49, 0x48, 0x47, - 0x47, 0x46, 0x45, 0x45, - 0x44, 0x43, 0x42, 0x42, - 0x41, 0x40, 0x3F, 0x3E, - 0x3D, 0x3C, 0x3B, 0x3A, - 0x39, 0x38, 0x37, 0x36, - 0x35, 0x34, 0x32, 0x31, - 0x30, 0x2F, 0x2D, 0x2C, - 0x2B, 0x29, 0x28, 0x26, - 0x25, 0x23, 0x21, 0x1F, - 0x1D, 0x1A, 0x17, 0x14, - 0x10, 0x0C, 0x06, 0x00, - -7, -7, -7, -7, - -7, -7, -7, -7, - -7, -7, -7, -7, -}; - -static const s8 b43legacy_tssi2dbm_g_table[] = { - 77, 77, 77, 76, - 76, 76, 75, 75, - 74, 74, 73, 73, - 73, 72, 72, 71, - 71, 70, 70, 69, - 68, 68, 67, 67, - 66, 65, 65, 64, - 63, 63, 62, 61, - 60, 59, 58, 57, - 56, 55, 54, 53, - 52, 50, 49, 47, - 45, 43, 40, 37, - 33, 28, 22, 14, - 5, -7, -20, -20, - -20, -20, -20, -20, - -20, -20, -20, -20, -}; - -static void b43legacy_phy_initg(struct b43legacy_wldev *dev); - - -static inline -void b43legacy_voluntary_preempt(void) -{ - B43legacy_BUG_ON(!(!in_atomic() && !in_irq() && - !in_interrupt() && !irqs_disabled())); -#ifndef CONFIG_PREEMPT - cond_resched(); -#endif /* CONFIG_PREEMPT */ -} - -/* Lock the PHY registers against concurrent access from the microcode. - * This lock is nonrecursive. */ -void b43legacy_phy_lock(struct b43legacy_wldev *dev) -{ -#if B43legacy_DEBUG - B43legacy_WARN_ON(dev->phy.phy_locked); - dev->phy.phy_locked = 1; -#endif - - if (dev->dev->id.revision < 3) { - b43legacy_mac_suspend(dev); - } else { - if (!b43legacy_is_mode(dev->wl, NL80211_IFTYPE_AP)) - b43legacy_power_saving_ctl_bits(dev, -1, 1); - } -} - -void b43legacy_phy_unlock(struct b43legacy_wldev *dev) -{ -#if B43legacy_DEBUG - B43legacy_WARN_ON(!dev->phy.phy_locked); - dev->phy.phy_locked = 0; -#endif - - if (dev->dev->id.revision < 3) { - b43legacy_mac_enable(dev); - } else { - if (!b43legacy_is_mode(dev->wl, NL80211_IFTYPE_AP)) - b43legacy_power_saving_ctl_bits(dev, -1, -1); - } -} - -u16 b43legacy_phy_read(struct b43legacy_wldev *dev, u16 offset) -{ - b43legacy_write16(dev, B43legacy_MMIO_PHY_CONTROL, offset); - return b43legacy_read16(dev, B43legacy_MMIO_PHY_DATA); -} - -void b43legacy_phy_write(struct b43legacy_wldev *dev, u16 offset, u16 val) -{ - b43legacy_write16(dev, B43legacy_MMIO_PHY_CONTROL, offset); - mmiowb(); - b43legacy_write16(dev, B43legacy_MMIO_PHY_DATA, val); -} - -void b43legacy_phy_calibrate(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - - b43legacy_read32(dev, B43legacy_MMIO_MACCTL); /* Dummy read. */ - if (phy->calibrated) - return; - if (phy->type == B43legacy_PHYTYPE_G && phy->rev == 1) { - b43legacy_wireless_core_reset(dev, 0); - b43legacy_phy_initg(dev); - b43legacy_wireless_core_reset(dev, B43legacy_TMSLOW_GMODE); - } - phy->calibrated = 1; -} - -/* initialize B PHY power control - * as described in http://bcm-specs.sipsolutions.net/InitPowerControl - */ -static void b43legacy_phy_init_pctl(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 saved_batt = 0; - u16 saved_ratt = 0; - u16 saved_txctl1 = 0; - int must_reset_txpower = 0; - - B43legacy_BUG_ON(!(phy->type == B43legacy_PHYTYPE_B || - phy->type == B43legacy_PHYTYPE_G)); - if (is_bcm_board_vendor(dev) && - (dev->dev->bus->boardinfo.type == 0x0416)) - return; - - b43legacy_phy_write(dev, 0x0028, 0x8018); - b43legacy_write16(dev, 0x03E6, b43legacy_read16(dev, 0x03E6) & 0xFFDF); - - if (phy->type == B43legacy_PHYTYPE_G) { - if (!phy->gmode) - return; - b43legacy_phy_write(dev, 0x047A, 0xC111); - } - if (phy->savedpctlreg != 0xFFFF) - return; -#ifdef CONFIG_B43LEGACY_DEBUG - if (phy->manual_txpower_control) - return; -#endif - - if (phy->type == B43legacy_PHYTYPE_B && - phy->rev >= 2 && - phy->radio_ver == 0x2050) - b43legacy_radio_write16(dev, 0x0076, - b43legacy_radio_read16(dev, 0x0076) - | 0x0084); - else { - saved_batt = phy->bbatt; - saved_ratt = phy->rfatt; - saved_txctl1 = phy->txctl1; - if ((phy->radio_rev >= 6) && (phy->radio_rev <= 8) - && /*FIXME: incomplete specs for 5 < revision < 9 */ 0) - b43legacy_radio_set_txpower_bg(dev, 0xB, 0x1F, 0); - else - b43legacy_radio_set_txpower_bg(dev, 0xB, 9, 0); - must_reset_txpower = 1; - } - b43legacy_dummy_transmission(dev); - - phy->savedpctlreg = b43legacy_phy_read(dev, B43legacy_PHY_G_PCTL); - - if (must_reset_txpower) - b43legacy_radio_set_txpower_bg(dev, saved_batt, saved_ratt, - saved_txctl1); - else - b43legacy_radio_write16(dev, 0x0076, b43legacy_radio_read16(dev, - 0x0076) & 0xFF7B); - b43legacy_radio_clear_tssi(dev); -} - -static void b43legacy_phy_agcsetup(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 offset = 0x0000; - - if (phy->rev == 1) - offset = 0x4C00; - - b43legacy_ilt_write(dev, offset, 0x00FE); - b43legacy_ilt_write(dev, offset + 1, 0x000D); - b43legacy_ilt_write(dev, offset + 2, 0x0013); - b43legacy_ilt_write(dev, offset + 3, 0x0019); - - if (phy->rev == 1) { - b43legacy_ilt_write(dev, 0x1800, 0x2710); - b43legacy_ilt_write(dev, 0x1801, 0x9B83); - b43legacy_ilt_write(dev, 0x1802, 0x9B83); - b43legacy_ilt_write(dev, 0x1803, 0x0F8D); - b43legacy_phy_write(dev, 0x0455, 0x0004); - } - - b43legacy_phy_write(dev, 0x04A5, (b43legacy_phy_read(dev, 0x04A5) - & 0x00FF) | 0x5700); - b43legacy_phy_write(dev, 0x041A, (b43legacy_phy_read(dev, 0x041A) - & 0xFF80) | 0x000F); - b43legacy_phy_write(dev, 0x041A, (b43legacy_phy_read(dev, 0x041A) - & 0xC07F) | 0x2B80); - b43legacy_phy_write(dev, 0x048C, (b43legacy_phy_read(dev, 0x048C) - & 0xF0FF) | 0x0300); - - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - | 0x0008); - - b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) - & 0xFFF0) | 0x0008); - b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1) - & 0xF0FF) | 0x0600); - b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2) - & 0xF0FF) | 0x0700); - b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) - & 0xF0FF) | 0x0100); - - if (phy->rev == 1) - b43legacy_phy_write(dev, 0x04A2, - (b43legacy_phy_read(dev, 0x04A2) - & 0xFFF0) | 0x0007); - - b43legacy_phy_write(dev, 0x0488, (b43legacy_phy_read(dev, 0x0488) - & 0xFF00) | 0x001C); - b43legacy_phy_write(dev, 0x0488, (b43legacy_phy_read(dev, 0x0488) - & 0xC0FF) | 0x0200); - b43legacy_phy_write(dev, 0x0496, (b43legacy_phy_read(dev, 0x0496) - & 0xFF00) | 0x001C); - b43legacy_phy_write(dev, 0x0489, (b43legacy_phy_read(dev, 0x0489) - & 0xFF00) | 0x0020); - b43legacy_phy_write(dev, 0x0489, (b43legacy_phy_read(dev, 0x0489) - & 0xC0FF) | 0x0200); - b43legacy_phy_write(dev, 0x0482, (b43legacy_phy_read(dev, 0x0482) - & 0xFF00) | 0x002E); - b43legacy_phy_write(dev, 0x0496, (b43legacy_phy_read(dev, 0x0496) - & 0x00FF) | 0x1A00); - b43legacy_phy_write(dev, 0x0481, (b43legacy_phy_read(dev, 0x0481) - & 0xFF00) | 0x0028); - b43legacy_phy_write(dev, 0x0481, (b43legacy_phy_read(dev, 0x0481) - & 0x00FF) | 0x2C00); - - if (phy->rev == 1) { - b43legacy_phy_write(dev, 0x0430, 0x092B); - b43legacy_phy_write(dev, 0x041B, - (b43legacy_phy_read(dev, 0x041B) - & 0xFFE1) | 0x0002); - } else { - b43legacy_phy_write(dev, 0x041B, - b43legacy_phy_read(dev, 0x041B) & 0xFFE1); - b43legacy_phy_write(dev, 0x041F, 0x287A); - b43legacy_phy_write(dev, 0x0420, - (b43legacy_phy_read(dev, 0x0420) - & 0xFFF0) | 0x0004); - } - - if (phy->rev > 2) { - b43legacy_phy_write(dev, 0x0422, 0x287A); - b43legacy_phy_write(dev, 0x0420, - (b43legacy_phy_read(dev, 0x0420) - & 0x0FFF) | 0x3000); - } - - b43legacy_phy_write(dev, 0x04A8, (b43legacy_phy_read(dev, 0x04A8) - & 0x8080) | 0x7874); - b43legacy_phy_write(dev, 0x048E, 0x1C00); - - if (phy->rev == 1) { - b43legacy_phy_write(dev, 0x04AB, - (b43legacy_phy_read(dev, 0x04AB) - & 0xF0FF) | 0x0600); - b43legacy_phy_write(dev, 0x048B, 0x005E); - b43legacy_phy_write(dev, 0x048C, - (b43legacy_phy_read(dev, 0x048C) & 0xFF00) - | 0x001E); - b43legacy_phy_write(dev, 0x048D, 0x0002); - } - - b43legacy_ilt_write(dev, offset + 0x0800, 0); - b43legacy_ilt_write(dev, offset + 0x0801, 7); - b43legacy_ilt_write(dev, offset + 0x0802, 16); - b43legacy_ilt_write(dev, offset + 0x0803, 28); - - if (phy->rev >= 6) { - b43legacy_phy_write(dev, 0x0426, - (b43legacy_phy_read(dev, 0x0426) & 0xFFFC)); - b43legacy_phy_write(dev, 0x0426, - (b43legacy_phy_read(dev, 0x0426) & 0xEFFF)); - } -} - -static void b43legacy_phy_setupg(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 i; - - B43legacy_BUG_ON(phy->type != B43legacy_PHYTYPE_G); - if (phy->rev == 1) { - b43legacy_phy_write(dev, 0x0406, 0x4F19); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, - (b43legacy_phy_read(dev, - B43legacy_PHY_G_CRS) & 0xFC3F) | 0x0340); - b43legacy_phy_write(dev, 0x042C, 0x005A); - b43legacy_phy_write(dev, 0x0427, 0x001A); - - for (i = 0; i < B43legacy_ILT_FINEFREQG_SIZE; i++) - b43legacy_ilt_write(dev, 0x5800 + i, - b43legacy_ilt_finefreqg[i]); - for (i = 0; i < B43legacy_ILT_NOISEG1_SIZE; i++) - b43legacy_ilt_write(dev, 0x1800 + i, - b43legacy_ilt_noiseg1[i]); - for (i = 0; i < B43legacy_ILT_ROTOR_SIZE; i++) - b43legacy_ilt_write32(dev, 0x2000 + i, - b43legacy_ilt_rotor[i]); - } else { - /* nrssi values are signed 6-bit values. Why 0x7654 here? */ - b43legacy_nrssi_hw_write(dev, 0xBA98, (s16)0x7654); - - if (phy->rev == 2) { - b43legacy_phy_write(dev, 0x04C0, 0x1861); - b43legacy_phy_write(dev, 0x04C1, 0x0271); - } else if (phy->rev > 2) { - b43legacy_phy_write(dev, 0x04C0, 0x0098); - b43legacy_phy_write(dev, 0x04C1, 0x0070); - b43legacy_phy_write(dev, 0x04C9, 0x0080); - } - b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, - 0x042B) | 0x800); - - for (i = 0; i < 64; i++) - b43legacy_ilt_write(dev, 0x4000 + i, i); - for (i = 0; i < B43legacy_ILT_NOISEG2_SIZE; i++) - b43legacy_ilt_write(dev, 0x1800 + i, - b43legacy_ilt_noiseg2[i]); - } - - if (phy->rev <= 2) - for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++) - b43legacy_ilt_write(dev, 0x1400 + i, - b43legacy_ilt_noisescaleg1[i]); - else if ((phy->rev >= 7) && (b43legacy_phy_read(dev, 0x0449) & 0x0200)) - for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++) - b43legacy_ilt_write(dev, 0x1400 + i, - b43legacy_ilt_noisescaleg3[i]); - else - for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++) - b43legacy_ilt_write(dev, 0x1400 + i, - b43legacy_ilt_noisescaleg2[i]); - - if (phy->rev == 2) - for (i = 0; i < B43legacy_ILT_SIGMASQR_SIZE; i++) - b43legacy_ilt_write(dev, 0x5000 + i, - b43legacy_ilt_sigmasqr1[i]); - else if ((phy->rev > 2) && (phy->rev <= 8)) - for (i = 0; i < B43legacy_ILT_SIGMASQR_SIZE; i++) - b43legacy_ilt_write(dev, 0x5000 + i, - b43legacy_ilt_sigmasqr2[i]); - - if (phy->rev == 1) { - for (i = 0; i < B43legacy_ILT_RETARD_SIZE; i++) - b43legacy_ilt_write32(dev, 0x2400 + i, - b43legacy_ilt_retard[i]); - for (i = 4; i < 20; i++) - b43legacy_ilt_write(dev, 0x5400 + i, 0x0020); - b43legacy_phy_agcsetup(dev); - - if (is_bcm_board_vendor(dev) && - (dev->dev->bus->boardinfo.type == 0x0416) && - (dev->dev->bus->sprom.board_rev == 0x0017)) - return; - - b43legacy_ilt_write(dev, 0x5001, 0x0002); - b43legacy_ilt_write(dev, 0x5002, 0x0001); - } else { - for (i = 0; i <= 0x20; i++) - b43legacy_ilt_write(dev, 0x1000 + i, 0x0820); - b43legacy_phy_agcsetup(dev); - b43legacy_phy_read(dev, 0x0400); /* dummy read */ - b43legacy_phy_write(dev, 0x0403, 0x1000); - b43legacy_ilt_write(dev, 0x3C02, 0x000F); - b43legacy_ilt_write(dev, 0x3C03, 0x0014); - - if (is_bcm_board_vendor(dev) && - (dev->dev->bus->boardinfo.type == 0x0416) && - (dev->dev->bus->sprom.board_rev == 0x0017)) - return; - - b43legacy_ilt_write(dev, 0x0401, 0x0002); - b43legacy_ilt_write(dev, 0x0402, 0x0001); - } -} - -/* Initialize the APHY portion of a GPHY. */ -static void b43legacy_phy_inita(struct b43legacy_wldev *dev) -{ - - might_sleep(); - - b43legacy_phy_setupg(dev); - if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_PACTRL) - b43legacy_phy_write(dev, 0x046E, 0x03CF); -} - -static void b43legacy_phy_initb2(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 offset; - int val; - - b43legacy_write16(dev, 0x03EC, 0x3F22); - b43legacy_phy_write(dev, 0x0020, 0x301C); - b43legacy_phy_write(dev, 0x0026, 0x0000); - b43legacy_phy_write(dev, 0x0030, 0x00C6); - b43legacy_phy_write(dev, 0x0088, 0x3E00); - val = 0x3C3D; - for (offset = 0x0089; offset < 0x00A7; offset++) { - b43legacy_phy_write(dev, offset, val); - val -= 0x0202; - } - b43legacy_phy_write(dev, 0x03E4, 0x3000); - b43legacy_radio_selectchannel(dev, phy->channel, 0); - if (phy->radio_ver != 0x2050) { - b43legacy_radio_write16(dev, 0x0075, 0x0080); - b43legacy_radio_write16(dev, 0x0079, 0x0081); - } - b43legacy_radio_write16(dev, 0x0050, 0x0020); - b43legacy_radio_write16(dev, 0x0050, 0x0023); - if (phy->radio_ver == 0x2050) { - b43legacy_radio_write16(dev, 0x0050, 0x0020); - b43legacy_radio_write16(dev, 0x005A, 0x0070); - b43legacy_radio_write16(dev, 0x005B, 0x007B); - b43legacy_radio_write16(dev, 0x005C, 0x00B0); - b43legacy_radio_write16(dev, 0x007A, 0x000F); - b43legacy_phy_write(dev, 0x0038, 0x0677); - b43legacy_radio_init2050(dev); - } - b43legacy_phy_write(dev, 0x0014, 0x0080); - b43legacy_phy_write(dev, 0x0032, 0x00CA); - b43legacy_phy_write(dev, 0x0032, 0x00CC); - b43legacy_phy_write(dev, 0x0035, 0x07C2); - b43legacy_phy_lo_b_measure(dev); - b43legacy_phy_write(dev, 0x0026, 0xCC00); - if (phy->radio_ver != 0x2050) - b43legacy_phy_write(dev, 0x0026, 0xCE00); - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x1000); - b43legacy_phy_write(dev, 0x002A, 0x88A3); - if (phy->radio_ver != 0x2050) - b43legacy_phy_write(dev, 0x002A, 0x88C2); - b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); - b43legacy_phy_init_pctl(dev); -} - -static void b43legacy_phy_initb4(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 offset; - u16 val; - - b43legacy_write16(dev, 0x03EC, 0x3F22); - b43legacy_phy_write(dev, 0x0020, 0x301C); - b43legacy_phy_write(dev, 0x0026, 0x0000); - b43legacy_phy_write(dev, 0x0030, 0x00C6); - b43legacy_phy_write(dev, 0x0088, 0x3E00); - val = 0x3C3D; - for (offset = 0x0089; offset < 0x00A7; offset++) { - b43legacy_phy_write(dev, offset, val); - val -= 0x0202; - } - b43legacy_phy_write(dev, 0x03E4, 0x3000); - b43legacy_radio_selectchannel(dev, phy->channel, 0); - if (phy->radio_ver != 0x2050) { - b43legacy_radio_write16(dev, 0x0075, 0x0080); - b43legacy_radio_write16(dev, 0x0079, 0x0081); - } - b43legacy_radio_write16(dev, 0x0050, 0x0020); - b43legacy_radio_write16(dev, 0x0050, 0x0023); - if (phy->radio_ver == 0x2050) { - b43legacy_radio_write16(dev, 0x0050, 0x0020); - b43legacy_radio_write16(dev, 0x005A, 0x0070); - b43legacy_radio_write16(dev, 0x005B, 0x007B); - b43legacy_radio_write16(dev, 0x005C, 0x00B0); - b43legacy_radio_write16(dev, 0x007A, 0x000F); - b43legacy_phy_write(dev, 0x0038, 0x0677); - b43legacy_radio_init2050(dev); - } - b43legacy_phy_write(dev, 0x0014, 0x0080); - b43legacy_phy_write(dev, 0x0032, 0x00CA); - if (phy->radio_ver == 0x2050) - b43legacy_phy_write(dev, 0x0032, 0x00E0); - b43legacy_phy_write(dev, 0x0035, 0x07C2); - - b43legacy_phy_lo_b_measure(dev); - - b43legacy_phy_write(dev, 0x0026, 0xCC00); - if (phy->radio_ver == 0x2050) - b43legacy_phy_write(dev, 0x0026, 0xCE00); - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x1100); - b43legacy_phy_write(dev, 0x002A, 0x88A3); - if (phy->radio_ver == 0x2050) - b43legacy_phy_write(dev, 0x002A, 0x88C2); - b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); - if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI) { - b43legacy_calc_nrssi_slope(dev); - b43legacy_calc_nrssi_threshold(dev); - } - b43legacy_phy_init_pctl(dev); -} - -static void b43legacy_phy_initb5(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 offset; - u16 value; - u8 old_channel; - - if (phy->analog == 1) - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - | 0x0050); - if (!is_bcm_board_vendor(dev) && - (dev->dev->bus->boardinfo.type != 0x0416)) { - value = 0x2120; - for (offset = 0x00A8 ; offset < 0x00C7; offset++) { - b43legacy_phy_write(dev, offset, value); - value += 0x0202; - } - } - b43legacy_phy_write(dev, 0x0035, - (b43legacy_phy_read(dev, 0x0035) & 0xF0FF) - | 0x0700); - if (phy->radio_ver == 0x2050) - b43legacy_phy_write(dev, 0x0038, 0x0667); - - if (phy->gmode) { - if (phy->radio_ver == 0x2050) { - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - | 0x0020); - b43legacy_radio_write16(dev, 0x0051, - b43legacy_radio_read16(dev, 0x0051) - | 0x0004); - } - b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO, 0x0000); - - b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) - | 0x0100); - b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B) - | 0x2000); - - b43legacy_phy_write(dev, 0x001C, 0x186A); - - b43legacy_phy_write(dev, 0x0013, (b43legacy_phy_read(dev, - 0x0013) & 0x00FF) | 0x1900); - b43legacy_phy_write(dev, 0x0035, (b43legacy_phy_read(dev, - 0x0035) & 0xFFC0) | 0x0064); - b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev, - 0x005D) & 0xFF80) | 0x000A); - b43legacy_phy_write(dev, 0x5B, 0x0000); - b43legacy_phy_write(dev, 0x5C, 0x0000); - } - - if (dev->bad_frames_preempt) - b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, - b43legacy_phy_read(dev, - B43legacy_PHY_RADIO_BITFIELD) | (1 << 12)); - - if (phy->analog == 1) { - b43legacy_phy_write(dev, 0x0026, 0xCE00); - b43legacy_phy_write(dev, 0x0021, 0x3763); - b43legacy_phy_write(dev, 0x0022, 0x1BC3); - b43legacy_phy_write(dev, 0x0023, 0x06F9); - b43legacy_phy_write(dev, 0x0024, 0x037E); - } else - b43legacy_phy_write(dev, 0x0026, 0xCC00); - b43legacy_phy_write(dev, 0x0030, 0x00C6); - b43legacy_write16(dev, 0x03EC, 0x3F22); - - if (phy->analog == 1) - b43legacy_phy_write(dev, 0x0020, 0x3E1C); - else - b43legacy_phy_write(dev, 0x0020, 0x301C); - - if (phy->analog == 0) - b43legacy_write16(dev, 0x03E4, 0x3000); - - old_channel = (phy->channel == 0xFF) ? 1 : phy->channel; - /* Force to channel 7, even if not supported. */ - b43legacy_radio_selectchannel(dev, 7, 0); - - if (phy->radio_ver != 0x2050) { - b43legacy_radio_write16(dev, 0x0075, 0x0080); - b43legacy_radio_write16(dev, 0x0079, 0x0081); - } - - b43legacy_radio_write16(dev, 0x0050, 0x0020); - b43legacy_radio_write16(dev, 0x0050, 0x0023); - - if (phy->radio_ver == 0x2050) { - b43legacy_radio_write16(dev, 0x0050, 0x0020); - b43legacy_radio_write16(dev, 0x005A, 0x0070); - } - - b43legacy_radio_write16(dev, 0x005B, 0x007B); - b43legacy_radio_write16(dev, 0x005C, 0x00B0); - - b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, - 0x007A) | 0x0007); - - b43legacy_radio_selectchannel(dev, old_channel, 0); - - b43legacy_phy_write(dev, 0x0014, 0x0080); - b43legacy_phy_write(dev, 0x0032, 0x00CA); - b43legacy_phy_write(dev, 0x002A, 0x88A3); - - b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); - - if (phy->radio_ver == 0x2050) - b43legacy_radio_write16(dev, 0x005D, 0x000D); - - b43legacy_write16(dev, 0x03E4, (b43legacy_read16(dev, 0x03E4) & - 0xFFC0) | 0x0004); -} - -static void b43legacy_phy_initb6(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 offset; - u16 val; - u8 old_channel; - - b43legacy_phy_write(dev, 0x003E, 0x817A); - b43legacy_radio_write16(dev, 0x007A, - (b43legacy_radio_read16(dev, 0x007A) | 0x0058)); - if (phy->radio_rev == 4 || - phy->radio_rev == 5) { - b43legacy_radio_write16(dev, 0x0051, 0x0037); - b43legacy_radio_write16(dev, 0x0052, 0x0070); - b43legacy_radio_write16(dev, 0x0053, 0x00B3); - b43legacy_radio_write16(dev, 0x0054, 0x009B); - b43legacy_radio_write16(dev, 0x005A, 0x0088); - b43legacy_radio_write16(dev, 0x005B, 0x0088); - b43legacy_radio_write16(dev, 0x005D, 0x0088); - b43legacy_radio_write16(dev, 0x005E, 0x0088); - b43legacy_radio_write16(dev, 0x007D, 0x0088); - b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET, - (b43legacy_shm_read32(dev, - B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET) - | 0x00000200)); - } - if (phy->radio_rev == 8) { - b43legacy_radio_write16(dev, 0x0051, 0x0000); - b43legacy_radio_write16(dev, 0x0052, 0x0040); - b43legacy_radio_write16(dev, 0x0053, 0x00B7); - b43legacy_radio_write16(dev, 0x0054, 0x0098); - b43legacy_radio_write16(dev, 0x005A, 0x0088); - b43legacy_radio_write16(dev, 0x005B, 0x006B); - b43legacy_radio_write16(dev, 0x005C, 0x000F); - if (dev->dev->bus->sprom.boardflags_lo & 0x8000) { - b43legacy_radio_write16(dev, 0x005D, 0x00FA); - b43legacy_radio_write16(dev, 0x005E, 0x00D8); - } else { - b43legacy_radio_write16(dev, 0x005D, 0x00F5); - b43legacy_radio_write16(dev, 0x005E, 0x00B8); - } - b43legacy_radio_write16(dev, 0x0073, 0x0003); - b43legacy_radio_write16(dev, 0x007D, 0x00A8); - b43legacy_radio_write16(dev, 0x007C, 0x0001); - b43legacy_radio_write16(dev, 0x007E, 0x0008); - } - val = 0x1E1F; - for (offset = 0x0088; offset < 0x0098; offset++) { - b43legacy_phy_write(dev, offset, val); - val -= 0x0202; - } - val = 0x3E3F; - for (offset = 0x0098; offset < 0x00A8; offset++) { - b43legacy_phy_write(dev, offset, val); - val -= 0x0202; - } - val = 0x2120; - for (offset = 0x00A8; offset < 0x00C8; offset++) { - b43legacy_phy_write(dev, offset, (val & 0x3F3F)); - val += 0x0202; - } - if (phy->type == B43legacy_PHYTYPE_G) { - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) | - 0x0020); - b43legacy_radio_write16(dev, 0x0051, - b43legacy_radio_read16(dev, 0x0051) | - 0x0004); - b43legacy_phy_write(dev, 0x0802, - b43legacy_phy_read(dev, 0x0802) | 0x0100); - b43legacy_phy_write(dev, 0x042B, - b43legacy_phy_read(dev, 0x042B) | 0x2000); - b43legacy_phy_write(dev, 0x5B, 0x0000); - b43legacy_phy_write(dev, 0x5C, 0x0000); - } - - old_channel = phy->channel; - if (old_channel >= 8) - b43legacy_radio_selectchannel(dev, 1, 0); - else - b43legacy_radio_selectchannel(dev, 13, 0); - - b43legacy_radio_write16(dev, 0x0050, 0x0020); - b43legacy_radio_write16(dev, 0x0050, 0x0023); - udelay(40); - if (phy->radio_rev < 6 || phy->radio_rev == 8) { - b43legacy_radio_write16(dev, 0x007C, - (b43legacy_radio_read16(dev, 0x007C) - | 0x0002)); - b43legacy_radio_write16(dev, 0x0050, 0x0020); - } - if (phy->radio_rev <= 2) { - b43legacy_radio_write16(dev, 0x0050, 0x0020); - b43legacy_radio_write16(dev, 0x005A, 0x0070); - b43legacy_radio_write16(dev, 0x005B, 0x007B); - b43legacy_radio_write16(dev, 0x005C, 0x00B0); - } - b43legacy_radio_write16(dev, 0x007A, - (b43legacy_radio_read16(dev, - 0x007A) & 0x00F8) | 0x0007); - - b43legacy_radio_selectchannel(dev, old_channel, 0); - - b43legacy_phy_write(dev, 0x0014, 0x0200); - if (phy->radio_rev >= 6) - b43legacy_phy_write(dev, 0x002A, 0x88C2); - else - b43legacy_phy_write(dev, 0x002A, 0x8AC0); - b43legacy_phy_write(dev, 0x0038, 0x0668); - b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); - if (phy->radio_rev == 4 || phy->radio_rev == 5) - b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev, - 0x005D) & 0xFF80) | 0x0003); - if (phy->radio_rev <= 2) - b43legacy_radio_write16(dev, 0x005D, 0x000D); - - if (phy->analog == 4) { - b43legacy_write16(dev, 0x03E4, 0x0009); - b43legacy_phy_write(dev, 0x61, b43legacy_phy_read(dev, 0x61) - & 0xFFF); - } else - b43legacy_phy_write(dev, 0x0002, (b43legacy_phy_read(dev, - 0x0002) & 0xFFC0) | 0x0004); - if (phy->type == B43legacy_PHYTYPE_G) - b43legacy_write16(dev, 0x03E6, 0x0); - if (phy->type == B43legacy_PHYTYPE_B) { - b43legacy_write16(dev, 0x03E6, 0x8140); - b43legacy_phy_write(dev, 0x0016, 0x0410); - b43legacy_phy_write(dev, 0x0017, 0x0820); - b43legacy_phy_write(dev, 0x0062, 0x0007); - b43legacy_radio_init2050(dev); - b43legacy_phy_lo_g_measure(dev); - if (dev->dev->bus->sprom.boardflags_lo & - B43legacy_BFL_RSSI) { - b43legacy_calc_nrssi_slope(dev); - b43legacy_calc_nrssi_threshold(dev); - } - b43legacy_phy_init_pctl(dev); - } -} - -static void b43legacy_calc_loopback_gain(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 backup_phy[15] = {0}; - u16 backup_radio[3]; - u16 backup_bband; - u16 i; - u16 loop1_cnt; - u16 loop1_done; - u16 loop1_omitted; - u16 loop2_done; - - backup_phy[0] = b43legacy_phy_read(dev, 0x0429); - backup_phy[1] = b43legacy_phy_read(dev, 0x0001); - backup_phy[2] = b43legacy_phy_read(dev, 0x0811); - backup_phy[3] = b43legacy_phy_read(dev, 0x0812); - if (phy->rev != 1) { - backup_phy[4] = b43legacy_phy_read(dev, 0x0814); - backup_phy[5] = b43legacy_phy_read(dev, 0x0815); - } - backup_phy[6] = b43legacy_phy_read(dev, 0x005A); - backup_phy[7] = b43legacy_phy_read(dev, 0x0059); - backup_phy[8] = b43legacy_phy_read(dev, 0x0058); - backup_phy[9] = b43legacy_phy_read(dev, 0x000A); - backup_phy[10] = b43legacy_phy_read(dev, 0x0003); - backup_phy[11] = b43legacy_phy_read(dev, 0x080F); - backup_phy[12] = b43legacy_phy_read(dev, 0x0810); - backup_phy[13] = b43legacy_phy_read(dev, 0x002B); - backup_phy[14] = b43legacy_phy_read(dev, 0x0015); - b43legacy_phy_read(dev, 0x002D); /* dummy read */ - backup_bband = phy->bbatt; - backup_radio[0] = b43legacy_radio_read16(dev, 0x0052); - backup_radio[1] = b43legacy_radio_read16(dev, 0x0043); - backup_radio[2] = b43legacy_radio_read16(dev, 0x007A); - - b43legacy_phy_write(dev, 0x0429, - b43legacy_phy_read(dev, 0x0429) & 0x3FFF); - b43legacy_phy_write(dev, 0x0001, - b43legacy_phy_read(dev, 0x0001) & 0x8000); - b43legacy_phy_write(dev, 0x0811, - b43legacy_phy_read(dev, 0x0811) | 0x0002); - b43legacy_phy_write(dev, 0x0812, - b43legacy_phy_read(dev, 0x0812) & 0xFFFD); - b43legacy_phy_write(dev, 0x0811, - b43legacy_phy_read(dev, 0x0811) | 0x0001); - b43legacy_phy_write(dev, 0x0812, - b43legacy_phy_read(dev, 0x0812) & 0xFFFE); - if (phy->rev != 1) { - b43legacy_phy_write(dev, 0x0814, - b43legacy_phy_read(dev, 0x0814) | 0x0001); - b43legacy_phy_write(dev, 0x0815, - b43legacy_phy_read(dev, 0x0815) & 0xFFFE); - b43legacy_phy_write(dev, 0x0814, - b43legacy_phy_read(dev, 0x0814) | 0x0002); - b43legacy_phy_write(dev, 0x0815, - b43legacy_phy_read(dev, 0x0815) & 0xFFFD); - } - b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | - 0x000C); - b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) | - 0x000C); - - b43legacy_phy_write(dev, 0x0811, (b43legacy_phy_read(dev, 0x0811) - & 0xFFCF) | 0x0030); - b43legacy_phy_write(dev, 0x0812, (b43legacy_phy_read(dev, 0x0812) - & 0xFFCF) | 0x0010); - - b43legacy_phy_write(dev, 0x005A, 0x0780); - b43legacy_phy_write(dev, 0x0059, 0xC810); - b43legacy_phy_write(dev, 0x0058, 0x000D); - if (phy->analog == 0) - b43legacy_phy_write(dev, 0x0003, 0x0122); - else - b43legacy_phy_write(dev, 0x000A, - b43legacy_phy_read(dev, 0x000A) - | 0x2000); - if (phy->rev != 1) { - b43legacy_phy_write(dev, 0x0814, - b43legacy_phy_read(dev, 0x0814) | 0x0004); - b43legacy_phy_write(dev, 0x0815, - b43legacy_phy_read(dev, 0x0815) & 0xFFFB); - } - b43legacy_phy_write(dev, 0x0003, - (b43legacy_phy_read(dev, 0x0003) - & 0xFF9F) | 0x0040); - if (phy->radio_ver == 0x2050 && phy->radio_rev == 2) { - b43legacy_radio_write16(dev, 0x0052, 0x0000); - b43legacy_radio_write16(dev, 0x0043, - (b43legacy_radio_read16(dev, 0x0043) - & 0xFFF0) | 0x0009); - loop1_cnt = 9; - } else if (phy->radio_rev == 8) { - b43legacy_radio_write16(dev, 0x0043, 0x000F); - loop1_cnt = 15; - } else - loop1_cnt = 0; - - b43legacy_phy_set_baseband_attenuation(dev, 11); - - if (phy->rev >= 3) - b43legacy_phy_write(dev, 0x080F, 0xC020); - else - b43legacy_phy_write(dev, 0x080F, 0x8020); - b43legacy_phy_write(dev, 0x0810, 0x0000); - - b43legacy_phy_write(dev, 0x002B, - (b43legacy_phy_read(dev, 0x002B) - & 0xFFC0) | 0x0001); - b43legacy_phy_write(dev, 0x002B, - (b43legacy_phy_read(dev, 0x002B) - & 0xC0FF) | 0x0800); - b43legacy_phy_write(dev, 0x0811, - b43legacy_phy_read(dev, 0x0811) | 0x0100); - b43legacy_phy_write(dev, 0x0812, - b43legacy_phy_read(dev, 0x0812) & 0xCFFF); - if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_EXTLNA) { - if (phy->rev >= 7) { - b43legacy_phy_write(dev, 0x0811, - b43legacy_phy_read(dev, 0x0811) - | 0x0800); - b43legacy_phy_write(dev, 0x0812, - b43legacy_phy_read(dev, 0x0812) - | 0x8000); - } - } - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - & 0x00F7); - - for (i = 0; i < loop1_cnt; i++) { - b43legacy_radio_write16(dev, 0x0043, loop1_cnt); - b43legacy_phy_write(dev, 0x0812, - (b43legacy_phy_read(dev, 0x0812) - & 0xF0FF) | (i << 8)); - b43legacy_phy_write(dev, 0x0015, - (b43legacy_phy_read(dev, 0x0015) - & 0x0FFF) | 0xA000); - b43legacy_phy_write(dev, 0x0015, - (b43legacy_phy_read(dev, 0x0015) - & 0x0FFF) | 0xF000); - udelay(20); - if (b43legacy_phy_read(dev, 0x002D) >= 0x0DFC) - break; - } - loop1_done = i; - loop1_omitted = loop1_cnt - loop1_done; - - loop2_done = 0; - if (loop1_done >= 8) { - b43legacy_phy_write(dev, 0x0812, - b43legacy_phy_read(dev, 0x0812) - | 0x0030); - for (i = loop1_done - 8; i < 16; i++) { - b43legacy_phy_write(dev, 0x0812, - (b43legacy_phy_read(dev, 0x0812) - & 0xF0FF) | (i << 8)); - b43legacy_phy_write(dev, 0x0015, - (b43legacy_phy_read(dev, 0x0015) - & 0x0FFF) | 0xA000); - b43legacy_phy_write(dev, 0x0015, - (b43legacy_phy_read(dev, 0x0015) - & 0x0FFF) | 0xF000); - udelay(20); - if (b43legacy_phy_read(dev, 0x002D) >= 0x0DFC) - break; - } - } - - if (phy->rev != 1) { - b43legacy_phy_write(dev, 0x0814, backup_phy[4]); - b43legacy_phy_write(dev, 0x0815, backup_phy[5]); - } - b43legacy_phy_write(dev, 0x005A, backup_phy[6]); - b43legacy_phy_write(dev, 0x0059, backup_phy[7]); - b43legacy_phy_write(dev, 0x0058, backup_phy[8]); - b43legacy_phy_write(dev, 0x000A, backup_phy[9]); - b43legacy_phy_write(dev, 0x0003, backup_phy[10]); - b43legacy_phy_write(dev, 0x080F, backup_phy[11]); - b43legacy_phy_write(dev, 0x0810, backup_phy[12]); - b43legacy_phy_write(dev, 0x002B, backup_phy[13]); - b43legacy_phy_write(dev, 0x0015, backup_phy[14]); - - b43legacy_phy_set_baseband_attenuation(dev, backup_bband); - - b43legacy_radio_write16(dev, 0x0052, backup_radio[0]); - b43legacy_radio_write16(dev, 0x0043, backup_radio[1]); - b43legacy_radio_write16(dev, 0x007A, backup_radio[2]); - - b43legacy_phy_write(dev, 0x0811, backup_phy[2] | 0x0003); - udelay(10); - b43legacy_phy_write(dev, 0x0811, backup_phy[2]); - b43legacy_phy_write(dev, 0x0812, backup_phy[3]); - b43legacy_phy_write(dev, 0x0429, backup_phy[0]); - b43legacy_phy_write(dev, 0x0001, backup_phy[1]); - - phy->loopback_gain[0] = ((loop1_done * 6) - (loop1_omitted * 4)) - 11; - phy->loopback_gain[1] = (24 - (3 * loop2_done)) * 2; -} - -static void b43legacy_phy_initg(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 tmp; - - if (phy->rev == 1) - b43legacy_phy_initb5(dev); - else - b43legacy_phy_initb6(dev); - if (phy->rev >= 2 && phy->gmode) - b43legacy_phy_inita(dev); - - if (phy->rev >= 2) { - b43legacy_phy_write(dev, 0x0814, 0x0000); - b43legacy_phy_write(dev, 0x0815, 0x0000); - } - if (phy->rev == 2) { - b43legacy_phy_write(dev, 0x0811, 0x0000); - b43legacy_phy_write(dev, 0x0015, 0x00C0); - } - if (phy->rev > 5) { - b43legacy_phy_write(dev, 0x0811, 0x0400); - b43legacy_phy_write(dev, 0x0015, 0x00C0); - } - if (phy->gmode) { - tmp = b43legacy_phy_read(dev, 0x0400) & 0xFF; - if (tmp == 3) { - b43legacy_phy_write(dev, 0x04C2, 0x1816); - b43legacy_phy_write(dev, 0x04C3, 0x8606); - } - if (tmp == 4 || tmp == 5) { - b43legacy_phy_write(dev, 0x04C2, 0x1816); - b43legacy_phy_write(dev, 0x04C3, 0x8006); - b43legacy_phy_write(dev, 0x04CC, - (b43legacy_phy_read(dev, - 0x04CC) & 0x00FF) | - 0x1F00); - } - if (phy->rev >= 2) - b43legacy_phy_write(dev, 0x047E, 0x0078); - } - if (phy->radio_rev == 8) { - b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801) - | 0x0080); - b43legacy_phy_write(dev, 0x043E, b43legacy_phy_read(dev, 0x043E) - | 0x0004); - } - if (phy->rev >= 2 && phy->gmode) - b43legacy_calc_loopback_gain(dev); - if (phy->radio_rev != 8) { - if (phy->initval == 0xFFFF) - phy->initval = b43legacy_radio_init2050(dev); - else - b43legacy_radio_write16(dev, 0x0078, phy->initval); - } - if (phy->txctl2 == 0xFFFF) - b43legacy_phy_lo_g_measure(dev); - else { - if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) - b43legacy_radio_write16(dev, 0x0052, - (phy->txctl1 << 4) | - phy->txctl2); - else - b43legacy_radio_write16(dev, 0x0052, - (b43legacy_radio_read16(dev, - 0x0052) & 0xFFF0) | - phy->txctl1); - if (phy->rev >= 6) - b43legacy_phy_write(dev, 0x0036, - (b43legacy_phy_read(dev, 0x0036) - & 0x0FFF) | (phy->txctl2 << 12)); - if (dev->dev->bus->sprom.boardflags_lo & - B43legacy_BFL_PACTRL) - b43legacy_phy_write(dev, 0x002E, 0x8075); - else - b43legacy_phy_write(dev, 0x002E, 0x807F); - if (phy->rev < 2) - b43legacy_phy_write(dev, 0x002F, 0x0101); - else - b43legacy_phy_write(dev, 0x002F, 0x0202); - } - if (phy->gmode) { - b43legacy_phy_lo_adjust(dev, 0); - b43legacy_phy_write(dev, 0x080F, 0x8078); - } - - if (!(dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI)) { - /* The specs state to update the NRSSI LT with - * the value 0x7FFFFFFF here. I think that is some weird - * compiler optimization in the original driver. - * Essentially, what we do here is resetting all NRSSI LT - * entries to -32 (see the clamp_val() in nrssi_hw_update()) - */ - b43legacy_nrssi_hw_update(dev, 0xFFFF); - b43legacy_calc_nrssi_threshold(dev); - } else if (phy->gmode || phy->rev >= 2) { - if (phy->nrssi[0] == -1000) { - B43legacy_WARN_ON(phy->nrssi[1] != -1000); - b43legacy_calc_nrssi_slope(dev); - } else { - B43legacy_WARN_ON(phy->nrssi[1] == -1000); - b43legacy_calc_nrssi_threshold(dev); - } - } - if (phy->radio_rev == 8) - b43legacy_phy_write(dev, 0x0805, 0x3230); - b43legacy_phy_init_pctl(dev); - if (dev->dev->bus->chip_id == 0x4306 - && dev->dev->bus->chip_package == 2) { - b43legacy_phy_write(dev, 0x0429, - b43legacy_phy_read(dev, 0x0429) & 0xBFFF); - b43legacy_phy_write(dev, 0x04C3, - b43legacy_phy_read(dev, 0x04C3) & 0x7FFF); - } -} - -static u16 b43legacy_phy_lo_b_r15_loop(struct b43legacy_wldev *dev) -{ - int i; - u16 ret = 0; - unsigned long flags; - - local_irq_save(flags); - for (i = 0; i < 10; i++) { - b43legacy_phy_write(dev, 0x0015, 0xAFA0); - udelay(1); - b43legacy_phy_write(dev, 0x0015, 0xEFA0); - udelay(10); - b43legacy_phy_write(dev, 0x0015, 0xFFA0); - udelay(40); - ret += b43legacy_phy_read(dev, 0x002C); - } - local_irq_restore(flags); - b43legacy_voluntary_preempt(); - - return ret; -} - -void b43legacy_phy_lo_b_measure(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 regstack[12] = { 0 }; - u16 mls; - u16 fval; - int i; - int j; - - regstack[0] = b43legacy_phy_read(dev, 0x0015); - regstack[1] = b43legacy_radio_read16(dev, 0x0052) & 0xFFF0; - - if (phy->radio_ver == 0x2053) { - regstack[2] = b43legacy_phy_read(dev, 0x000A); - regstack[3] = b43legacy_phy_read(dev, 0x002A); - regstack[4] = b43legacy_phy_read(dev, 0x0035); - regstack[5] = b43legacy_phy_read(dev, 0x0003); - regstack[6] = b43legacy_phy_read(dev, 0x0001); - regstack[7] = b43legacy_phy_read(dev, 0x0030); - - regstack[8] = b43legacy_radio_read16(dev, 0x0043); - regstack[9] = b43legacy_radio_read16(dev, 0x007A); - regstack[10] = b43legacy_read16(dev, 0x03EC); - regstack[11] = b43legacy_radio_read16(dev, 0x0052) & 0x00F0; - - b43legacy_phy_write(dev, 0x0030, 0x00FF); - b43legacy_write16(dev, 0x03EC, 0x3F3F); - b43legacy_phy_write(dev, 0x0035, regstack[4] & 0xFF7F); - b43legacy_radio_write16(dev, 0x007A, regstack[9] & 0xFFF0); - } - b43legacy_phy_write(dev, 0x0015, 0xB000); - b43legacy_phy_write(dev, 0x002B, 0x0004); - - if (phy->radio_ver == 0x2053) { - b43legacy_phy_write(dev, 0x002B, 0x0203); - b43legacy_phy_write(dev, 0x002A, 0x08A3); - } - - phy->minlowsig[0] = 0xFFFF; - - for (i = 0; i < 4; i++) { - b43legacy_radio_write16(dev, 0x0052, regstack[1] | i); - b43legacy_phy_lo_b_r15_loop(dev); - } - for (i = 0; i < 10; i++) { - b43legacy_radio_write16(dev, 0x0052, regstack[1] | i); - mls = b43legacy_phy_lo_b_r15_loop(dev) / 10; - if (mls < phy->minlowsig[0]) { - phy->minlowsig[0] = mls; - phy->minlowsigpos[0] = i; - } - } - b43legacy_radio_write16(dev, 0x0052, regstack[1] - | phy->minlowsigpos[0]); - - phy->minlowsig[1] = 0xFFFF; - - for (i = -4; i < 5; i += 2) { - for (j = -4; j < 5; j += 2) { - if (j < 0) - fval = (0x0100 * i) + j + 0x0100; - else - fval = (0x0100 * i) + j; - b43legacy_phy_write(dev, 0x002F, fval); - mls = b43legacy_phy_lo_b_r15_loop(dev) / 10; - if (mls < phy->minlowsig[1]) { - phy->minlowsig[1] = mls; - phy->minlowsigpos[1] = fval; - } - } - } - phy->minlowsigpos[1] += 0x0101; - - b43legacy_phy_write(dev, 0x002F, phy->minlowsigpos[1]); - if (phy->radio_ver == 0x2053) { - b43legacy_phy_write(dev, 0x000A, regstack[2]); - b43legacy_phy_write(dev, 0x002A, regstack[3]); - b43legacy_phy_write(dev, 0x0035, regstack[4]); - b43legacy_phy_write(dev, 0x0003, regstack[5]); - b43legacy_phy_write(dev, 0x0001, regstack[6]); - b43legacy_phy_write(dev, 0x0030, regstack[7]); - - b43legacy_radio_write16(dev, 0x0043, regstack[8]); - b43legacy_radio_write16(dev, 0x007A, regstack[9]); - - b43legacy_radio_write16(dev, 0x0052, - (b43legacy_radio_read16(dev, 0x0052) - & 0x000F) | regstack[11]); - - b43legacy_write16(dev, 0x03EC, regstack[10]); - } - b43legacy_phy_write(dev, 0x0015, regstack[0]); -} - -static inline -u16 b43legacy_phy_lo_g_deviation_subval(struct b43legacy_wldev *dev, - u16 control) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 ret; - unsigned long flags; - - local_irq_save(flags); - if (phy->gmode) { - b43legacy_phy_write(dev, 0x15, 0xE300); - control <<= 8; - b43legacy_phy_write(dev, 0x0812, control | 0x00B0); - udelay(5); - b43legacy_phy_write(dev, 0x0812, control | 0x00B2); - udelay(2); - b43legacy_phy_write(dev, 0x0812, control | 0x00B3); - udelay(4); - b43legacy_phy_write(dev, 0x0015, 0xF300); - udelay(8); - } else { - b43legacy_phy_write(dev, 0x0015, control | 0xEFA0); - udelay(2); - b43legacy_phy_write(dev, 0x0015, control | 0xEFE0); - udelay(4); - b43legacy_phy_write(dev, 0x0015, control | 0xFFE0); - udelay(8); - } - ret = b43legacy_phy_read(dev, 0x002D); - local_irq_restore(flags); - b43legacy_voluntary_preempt(); - - return ret; -} - -static u32 b43legacy_phy_lo_g_singledeviation(struct b43legacy_wldev *dev, - u16 control) -{ - int i; - u32 ret = 0; - - for (i = 0; i < 8; i++) - ret += b43legacy_phy_lo_g_deviation_subval(dev, control); - - return ret; -} - -/* Write the LocalOscillator CONTROL */ -static inline -void b43legacy_lo_write(struct b43legacy_wldev *dev, - struct b43legacy_lopair *pair) -{ - u16 value; - - value = (u8)(pair->low); - value |= ((u8)(pair->high)) << 8; - -#ifdef CONFIG_B43LEGACY_DEBUG - /* Sanity check. */ - if (pair->low < -8 || pair->low > 8 || - pair->high < -8 || pair->high > 8) { - b43legacydbg(dev->wl, - "WARNING: Writing invalid LOpair " - "(low: %d, high: %d)\n", - pair->low, pair->high); - dump_stack(); - } -#endif - - b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, value); -} - -static inline -struct b43legacy_lopair *b43legacy_find_lopair(struct b43legacy_wldev *dev, - u16 bbatt, - u16 rfatt, - u16 tx) -{ - static const u8 dict[10] = { 11, 10, 11, 12, 13, 12, 13, 12, 13, 12 }; - struct b43legacy_phy *phy = &dev->phy; - - if (bbatt > 6) - bbatt = 6; - B43legacy_WARN_ON(rfatt >= 10); - - if (tx == 3) - return b43legacy_get_lopair(phy, rfatt, bbatt); - return b43legacy_get_lopair(phy, dict[rfatt], bbatt); -} - -static inline -struct b43legacy_lopair *b43legacy_current_lopair(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - - return b43legacy_find_lopair(dev, phy->bbatt, - phy->rfatt, phy->txctl1); -} - -/* Adjust B/G LO */ -void b43legacy_phy_lo_adjust(struct b43legacy_wldev *dev, int fixed) -{ - struct b43legacy_lopair *pair; - - if (fixed) { - /* Use fixed values. Only for initialization. */ - pair = b43legacy_find_lopair(dev, 2, 3, 0); - } else - pair = b43legacy_current_lopair(dev); - b43legacy_lo_write(dev, pair); -} - -static void b43legacy_phy_lo_g_measure_txctl2(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 txctl2 = 0; - u16 i; - u32 smallest; - u32 tmp; - - b43legacy_radio_write16(dev, 0x0052, 0x0000); - udelay(10); - smallest = b43legacy_phy_lo_g_singledeviation(dev, 0); - for (i = 0; i < 16; i++) { - b43legacy_radio_write16(dev, 0x0052, i); - udelay(10); - tmp = b43legacy_phy_lo_g_singledeviation(dev, 0); - if (tmp < smallest) { - smallest = tmp; - txctl2 = i; - } - } - phy->txctl2 = txctl2; -} - -static -void b43legacy_phy_lo_g_state(struct b43legacy_wldev *dev, - const struct b43legacy_lopair *in_pair, - struct b43legacy_lopair *out_pair, - u16 r27) -{ - static const struct b43legacy_lopair transitions[8] = { - { .high = 1, .low = 1, }, - { .high = 1, .low = 0, }, - { .high = 1, .low = -1, }, - { .high = 0, .low = -1, }, - { .high = -1, .low = -1, }, - { .high = -1, .low = 0, }, - { .high = -1, .low = 1, }, - { .high = 0, .low = 1, }, - }; - struct b43legacy_lopair lowest_transition = { - .high = in_pair->high, - .low = in_pair->low, - }; - struct b43legacy_lopair tmp_pair; - struct b43legacy_lopair transition; - int i = 12; - int state = 0; - int found_lower; - int j; - int begin; - int end; - u32 lowest_deviation; - u32 tmp; - - /* Note that in_pair and out_pair can point to the same pair. - * Be careful. */ - - b43legacy_lo_write(dev, &lowest_transition); - lowest_deviation = b43legacy_phy_lo_g_singledeviation(dev, r27); - do { - found_lower = 0; - B43legacy_WARN_ON(!(state >= 0 && state <= 8)); - if (state == 0) { - begin = 1; - end = 8; - } else if (state % 2 == 0) { - begin = state - 1; - end = state + 1; - } else { - begin = state - 2; - end = state + 2; - } - if (begin < 1) - begin += 8; - if (end > 8) - end -= 8; - - j = begin; - tmp_pair.high = lowest_transition.high; - tmp_pair.low = lowest_transition.low; - while (1) { - B43legacy_WARN_ON(!(j >= 1 && j <= 8)); - transition.high = tmp_pair.high + - transitions[j - 1].high; - transition.low = tmp_pair.low + transitions[j - 1].low; - if ((abs(transition.low) < 9) - && (abs(transition.high) < 9)) { - b43legacy_lo_write(dev, &transition); - tmp = b43legacy_phy_lo_g_singledeviation(dev, - r27); - if (tmp < lowest_deviation) { - lowest_deviation = tmp; - state = j; - found_lower = 1; - - lowest_transition.high = - transition.high; - lowest_transition.low = transition.low; - } - } - if (j == end) - break; - if (j == 8) - j = 1; - else - j++; - } - } while (i-- && found_lower); - - out_pair->high = lowest_transition.high; - out_pair->low = lowest_transition.low; -} - -/* Set the baseband attenuation value on chip. */ -void b43legacy_phy_set_baseband_attenuation(struct b43legacy_wldev *dev, - u16 bbatt) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 value; - - if (phy->analog == 0) { - value = (b43legacy_read16(dev, 0x03E6) & 0xFFF0); - value |= (bbatt & 0x000F); - b43legacy_write16(dev, 0x03E6, value); - return; - } - - if (phy->analog > 1) { - value = b43legacy_phy_read(dev, 0x0060) & 0xFFC3; - value |= (bbatt << 2) & 0x003C; - } else { - value = b43legacy_phy_read(dev, 0x0060) & 0xFF87; - value |= (bbatt << 3) & 0x0078; - } - b43legacy_phy_write(dev, 0x0060, value); -} - -/* http://bcm-specs.sipsolutions.net/LocalOscillator/Measure */ -void b43legacy_phy_lo_g_measure(struct b43legacy_wldev *dev) -{ - static const u8 pairorder[10] = { 3, 1, 5, 7, 9, 2, 0, 4, 6, 8 }; - const int is_initializing = (b43legacy_status(dev) - < B43legacy_STAT_STARTED); - struct b43legacy_phy *phy = &dev->phy; - u16 h; - u16 i; - u16 oldi = 0; - u16 j; - struct b43legacy_lopair control; - struct b43legacy_lopair *tmp_control; - u16 tmp; - u16 regstack[16] = { 0 }; - u8 oldchannel; - - /* XXX: What are these? */ - u8 r27 = 0; - u16 r31; - - oldchannel = phy->channel; - /* Setup */ - if (phy->gmode) { - regstack[0] = b43legacy_phy_read(dev, B43legacy_PHY_G_CRS); - regstack[1] = b43legacy_phy_read(dev, 0x0802); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0] - & 0x7FFF); - b43legacy_phy_write(dev, 0x0802, regstack[1] & 0xFFFC); - } - regstack[3] = b43legacy_read16(dev, 0x03E2); - b43legacy_write16(dev, 0x03E2, regstack[3] | 0x8000); - regstack[4] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); - regstack[5] = b43legacy_phy_read(dev, 0x15); - regstack[6] = b43legacy_phy_read(dev, 0x2A); - regstack[7] = b43legacy_phy_read(dev, 0x35); - regstack[8] = b43legacy_phy_read(dev, 0x60); - regstack[9] = b43legacy_radio_read16(dev, 0x43); - regstack[10] = b43legacy_radio_read16(dev, 0x7A); - regstack[11] = b43legacy_radio_read16(dev, 0x52); - if (phy->gmode) { - regstack[12] = b43legacy_phy_read(dev, 0x0811); - regstack[13] = b43legacy_phy_read(dev, 0x0812); - regstack[14] = b43legacy_phy_read(dev, 0x0814); - regstack[15] = b43legacy_phy_read(dev, 0x0815); - } - b43legacy_radio_selectchannel(dev, 6, 0); - if (phy->gmode) { - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0] - & 0x7FFF); - b43legacy_phy_write(dev, 0x0802, regstack[1] & 0xFFFC); - b43legacy_dummy_transmission(dev); - } - b43legacy_radio_write16(dev, 0x0043, 0x0006); - - b43legacy_phy_set_baseband_attenuation(dev, 2); - - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x0000); - b43legacy_phy_write(dev, 0x002E, 0x007F); - b43legacy_phy_write(dev, 0x080F, 0x0078); - b43legacy_phy_write(dev, 0x0035, regstack[7] & ~(1 << 7)); - b43legacy_radio_write16(dev, 0x007A, regstack[10] & 0xFFF0); - b43legacy_phy_write(dev, 0x002B, 0x0203); - b43legacy_phy_write(dev, 0x002A, 0x08A3); - if (phy->gmode) { - b43legacy_phy_write(dev, 0x0814, regstack[14] | 0x0003); - b43legacy_phy_write(dev, 0x0815, regstack[15] & 0xFFFC); - b43legacy_phy_write(dev, 0x0811, 0x01B3); - b43legacy_phy_write(dev, 0x0812, 0x00B2); - } - if (is_initializing) - b43legacy_phy_lo_g_measure_txctl2(dev); - b43legacy_phy_write(dev, 0x080F, 0x8078); - - /* Measure */ - control.low = 0; - control.high = 0; - for (h = 0; h < 10; h++) { - /* Loop over each possible RadioAttenuation (0-9) */ - i = pairorder[h]; - if (is_initializing) { - if (i == 3) { - control.low = 0; - control.high = 0; - } else if (((i % 2 == 1) && (oldi % 2 == 1)) || - ((i % 2 == 0) && (oldi % 2 == 0))) { - tmp_control = b43legacy_get_lopair(phy, oldi, - 0); - memcpy(&control, tmp_control, sizeof(control)); - } else { - tmp_control = b43legacy_get_lopair(phy, 3, 0); - memcpy(&control, tmp_control, sizeof(control)); - } - } - /* Loop over each possible BasebandAttenuation/2 */ - for (j = 0; j < 4; j++) { - if (is_initializing) { - tmp = i * 2 + j; - r27 = 0; - r31 = 0; - if (tmp > 14) { - r31 = 1; - if (tmp > 17) - r27 = 1; - if (tmp > 19) - r27 = 2; - } - } else { - tmp_control = b43legacy_get_lopair(phy, i, - j * 2); - if (!tmp_control->used) - continue; - memcpy(&control, tmp_control, sizeof(control)); - r27 = 3; - r31 = 0; - } - b43legacy_radio_write16(dev, 0x43, i); - b43legacy_radio_write16(dev, 0x52, phy->txctl2); - udelay(10); - b43legacy_voluntary_preempt(); - - b43legacy_phy_set_baseband_attenuation(dev, j * 2); - - tmp = (regstack[10] & 0xFFF0); - if (r31) - tmp |= 0x0008; - b43legacy_radio_write16(dev, 0x007A, tmp); - - tmp_control = b43legacy_get_lopair(phy, i, j * 2); - b43legacy_phy_lo_g_state(dev, &control, tmp_control, - r27); - } - oldi = i; - } - /* Loop over each possible RadioAttenuation (10-13) */ - for (i = 10; i < 14; i++) { - /* Loop over each possible BasebandAttenuation/2 */ - for (j = 0; j < 4; j++) { - if (is_initializing) { - tmp_control = b43legacy_get_lopair(phy, i - 9, - j * 2); - memcpy(&control, tmp_control, sizeof(control)); - /* FIXME: The next line is wrong, as the - * following if statement can never trigger. */ - tmp = (i - 9) * 2 + j - 5; - r27 = 0; - r31 = 0; - if (tmp > 14) { - r31 = 1; - if (tmp > 17) - r27 = 1; - if (tmp > 19) - r27 = 2; - } - } else { - tmp_control = b43legacy_get_lopair(phy, i - 9, - j * 2); - if (!tmp_control->used) - continue; - memcpy(&control, tmp_control, sizeof(control)); - r27 = 3; - r31 = 0; - } - b43legacy_radio_write16(dev, 0x43, i - 9); - /* FIXME: shouldn't txctl1 be zero in the next line - * and 3 in the loop above? */ - b43legacy_radio_write16(dev, 0x52, - phy->txctl2 - | (3/*txctl1*/ << 4)); - udelay(10); - b43legacy_voluntary_preempt(); - - b43legacy_phy_set_baseband_attenuation(dev, j * 2); - - tmp = (regstack[10] & 0xFFF0); - if (r31) - tmp |= 0x0008; - b43legacy_radio_write16(dev, 0x7A, tmp); - - tmp_control = b43legacy_get_lopair(phy, i, j * 2); - b43legacy_phy_lo_g_state(dev, &control, tmp_control, - r27); - } - } - - /* Restoration */ - if (phy->gmode) { - b43legacy_phy_write(dev, 0x0015, 0xE300); - b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA0); - udelay(5); - b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA2); - udelay(2); - b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA3); - b43legacy_voluntary_preempt(); - } else - b43legacy_phy_write(dev, 0x0015, r27 | 0xEFA0); - b43legacy_phy_lo_adjust(dev, is_initializing); - b43legacy_phy_write(dev, 0x002E, 0x807F); - if (phy->gmode) - b43legacy_phy_write(dev, 0x002F, 0x0202); - else - b43legacy_phy_write(dev, 0x002F, 0x0101); - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, regstack[4]); - b43legacy_phy_write(dev, 0x0015, regstack[5]); - b43legacy_phy_write(dev, 0x002A, regstack[6]); - b43legacy_phy_write(dev, 0x0035, regstack[7]); - b43legacy_phy_write(dev, 0x0060, regstack[8]); - b43legacy_radio_write16(dev, 0x0043, regstack[9]); - b43legacy_radio_write16(dev, 0x007A, regstack[10]); - regstack[11] &= 0x00F0; - regstack[11] |= (b43legacy_radio_read16(dev, 0x52) & 0x000F); - b43legacy_radio_write16(dev, 0x52, regstack[11]); - b43legacy_write16(dev, 0x03E2, regstack[3]); - if (phy->gmode) { - b43legacy_phy_write(dev, 0x0811, regstack[12]); - b43legacy_phy_write(dev, 0x0812, regstack[13]); - b43legacy_phy_write(dev, 0x0814, regstack[14]); - b43legacy_phy_write(dev, 0x0815, regstack[15]); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0]); - b43legacy_phy_write(dev, 0x0802, regstack[1]); - } - b43legacy_radio_selectchannel(dev, oldchannel, 1); - -#ifdef CONFIG_B43LEGACY_DEBUG - { - /* Sanity check for all lopairs. */ - for (i = 0; i < B43legacy_LO_COUNT; i++) { - tmp_control = phy->_lo_pairs + i; - if (tmp_control->low < -8 || tmp_control->low > 8 || - tmp_control->high < -8 || tmp_control->high > 8) - b43legacywarn(dev->wl, - "WARNING: Invalid LOpair (low: %d, high:" - " %d, index: %d)\n", - tmp_control->low, tmp_control->high, i); - } - } -#endif /* CONFIG_B43LEGACY_DEBUG */ -} - -static -void b43legacy_phy_lo_mark_current_used(struct b43legacy_wldev *dev) -{ - struct b43legacy_lopair *pair; - - pair = b43legacy_current_lopair(dev); - pair->used = 1; -} - -void b43legacy_phy_lo_mark_all_unused(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - struct b43legacy_lopair *pair; - int i; - - for (i = 0; i < B43legacy_LO_COUNT; i++) { - pair = phy->_lo_pairs + i; - pair->used = 0; - } -} - -/* http://bcm-specs.sipsolutions.net/EstimatePowerOut - * This function converts a TSSI value to dBm in Q5.2 - */ -static s8 b43legacy_phy_estimate_power_out(struct b43legacy_wldev *dev, s8 tssi) -{ - struct b43legacy_phy *phy = &dev->phy; - s8 dbm = 0; - s32 tmp; - - tmp = phy->idle_tssi; - tmp += tssi; - tmp -= phy->savedpctlreg; - - switch (phy->type) { - case B43legacy_PHYTYPE_B: - case B43legacy_PHYTYPE_G: - tmp = clamp_val(tmp, 0x00, 0x3F); - dbm = phy->tssi2dbm[tmp]; - break; - default: - B43legacy_BUG_ON(1); - } - - return dbm; -} - -/* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */ -void b43legacy_phy_xmitpower(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 tmp; - u16 txpower; - s8 v0; - s8 v1; - s8 v2; - s8 v3; - s8 average; - int max_pwr; - s16 desired_pwr; - s16 estimated_pwr; - s16 pwr_adjust; - s16 radio_att_delta; - s16 baseband_att_delta; - s16 radio_attenuation; - s16 baseband_attenuation; - - if (phy->savedpctlreg == 0xFFFF) - return; - if ((dev->dev->bus->boardinfo.type == 0x0416) && - is_bcm_board_vendor(dev)) - return; -#ifdef CONFIG_B43LEGACY_DEBUG - if (phy->manual_txpower_control) - return; -#endif - - B43legacy_BUG_ON(!(phy->type == B43legacy_PHYTYPE_B || - phy->type == B43legacy_PHYTYPE_G)); - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0058); - v0 = (s8)(tmp & 0x00FF); - v1 = (s8)((tmp & 0xFF00) >> 8); - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x005A); - v2 = (s8)(tmp & 0x00FF); - v3 = (s8)((tmp & 0xFF00) >> 8); - tmp = 0; - - if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) { - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - 0x0070); - v0 = (s8)(tmp & 0x00FF); - v1 = (s8)((tmp & 0xFF00) >> 8); - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, - 0x0072); - v2 = (s8)(tmp & 0x00FF); - v3 = (s8)((tmp & 0xFF00) >> 8); - if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) - return; - v0 = (v0 + 0x20) & 0x3F; - v1 = (v1 + 0x20) & 0x3F; - v2 = (v2 + 0x20) & 0x3F; - v3 = (v3 + 0x20) & 0x3F; - tmp = 1; - } - b43legacy_radio_clear_tssi(dev); - - average = (v0 + v1 + v2 + v3 + 2) / 4; - - if (tmp && (b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x005E) - & 0x8)) - average -= 13; - - estimated_pwr = b43legacy_phy_estimate_power_out(dev, average); - - max_pwr = dev->dev->bus->sprom.maxpwr_bg; - - if ((dev->dev->bus->sprom.boardflags_lo - & B43legacy_BFL_PACTRL) && - (phy->type == B43legacy_PHYTYPE_G)) - max_pwr -= 0x3; - if (unlikely(max_pwr <= 0)) { - b43legacywarn(dev->wl, "Invalid max-TX-power value in SPROM." - "\n"); - max_pwr = 74; /* fake it */ - dev->dev->bus->sprom.maxpwr_bg = max_pwr; - } - - /* Use regulatory information to get the maximum power. - * In the absence of such data from mac80211, we will use 20 dBm, which - * is the value for the EU, US, Canada, and most of the world. - * The regulatory maximum is reduced by the antenna gain (from sprom) - * and 1.5 dBm (a safety factor??). The result is in Q5.2 format - * which accounts for the factor of 4 */ -#define REG_MAX_PWR 20 - max_pwr = min(REG_MAX_PWR * 4 - - dev->dev->bus->sprom.antenna_gain.a0 - - 0x6, max_pwr); - - /* find the desired power in Q5.2 - power_level is in dBm - * and limit it - max_pwr is already in Q5.2 */ - desired_pwr = clamp_val(phy->power_level << 2, 0, max_pwr); - if (b43legacy_debug(dev, B43legacy_DBG_XMITPOWER)) - b43legacydbg(dev->wl, "Current TX power output: " Q52_FMT - " dBm, Desired TX power output: " Q52_FMT - " dBm\n", Q52_ARG(estimated_pwr), - Q52_ARG(desired_pwr)); - /* Check if we need to adjust the current power. The factor of 2 is - * for damping */ - pwr_adjust = (desired_pwr - estimated_pwr) / 2; - /* RF attenuation delta - * The minus sign is because lower attenuation => more power */ - radio_att_delta = -(pwr_adjust + 7) >> 3; - /* Baseband attenuation delta */ - baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta); - /* Do we need to adjust anything? */ - if ((radio_att_delta == 0) && (baseband_att_delta == 0)) { - b43legacy_phy_lo_mark_current_used(dev); - return; - } - - /* Calculate the new attenuation values. */ - baseband_attenuation = phy->bbatt; - baseband_attenuation += baseband_att_delta; - radio_attenuation = phy->rfatt; - radio_attenuation += radio_att_delta; - - /* Get baseband and radio attenuation values into permitted ranges. - * baseband 0-11, radio 0-9. - * Radio attenuation affects power level 4 times as much as baseband. - */ - if (radio_attenuation < 0) { - baseband_attenuation -= (4 * -radio_attenuation); - radio_attenuation = 0; - } else if (radio_attenuation > 9) { - baseband_attenuation += (4 * (radio_attenuation - 9)); - radio_attenuation = 9; - } else { - while (baseband_attenuation < 0 && radio_attenuation > 0) { - baseband_attenuation += 4; - radio_attenuation--; - } - while (baseband_attenuation > 11 && radio_attenuation < 9) { - baseband_attenuation -= 4; - radio_attenuation++; - } - } - baseband_attenuation = clamp_val(baseband_attenuation, 0, 11); - - txpower = phy->txctl1; - if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 2)) { - if (radio_attenuation <= 1) { - if (txpower == 0) { - txpower = 3; - radio_attenuation += 2; - baseband_attenuation += 2; - } else if (dev->dev->bus->sprom.boardflags_lo - & B43legacy_BFL_PACTRL) { - baseband_attenuation += 4 * - (radio_attenuation - 2); - radio_attenuation = 2; - } - } else if (radio_attenuation > 4 && txpower != 0) { - txpower = 0; - if (baseband_attenuation < 3) { - radio_attenuation -= 3; - baseband_attenuation += 2; - } else { - radio_attenuation -= 2; - baseband_attenuation -= 2; - } - } - } - /* Save the control values */ - phy->txctl1 = txpower; - baseband_attenuation = clamp_val(baseband_attenuation, 0, 11); - radio_attenuation = clamp_val(radio_attenuation, 0, 9); - phy->rfatt = radio_attenuation; - phy->bbatt = baseband_attenuation; - - /* Adjust the hardware */ - b43legacy_phy_lock(dev); - b43legacy_radio_lock(dev); - b43legacy_radio_set_txpower_bg(dev, baseband_attenuation, - radio_attenuation, txpower); - b43legacy_phy_lo_mark_current_used(dev); - b43legacy_radio_unlock(dev); - b43legacy_phy_unlock(dev); -} - -static inline -s32 b43legacy_tssi2dbm_ad(s32 num, s32 den) -{ - if (num < 0) - return num/den; - else - return (num+den/2)/den; -} - -static inline -s8 b43legacy_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2) -{ - s32 m1; - s32 m2; - s32 f = 256; - s32 q; - s32 delta; - s8 i = 0; - - m1 = b43legacy_tssi2dbm_ad(16 * pab0 + index * pab1, 32); - m2 = max(b43legacy_tssi2dbm_ad(32768 + index * pab2, 256), 1); - do { - if (i > 15) - return -EINVAL; - q = b43legacy_tssi2dbm_ad(f * 4096 - - b43legacy_tssi2dbm_ad(m2 * f, 16) * - f, 2048); - delta = abs(q - f); - f = q; - i++; - } while (delta >= 2); - entry[index] = clamp_val(b43legacy_tssi2dbm_ad(m1 * f, 8192), - -127, 128); - return 0; -} - -/* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */ -int b43legacy_phy_init_tssi2dbm_table(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - s16 pab0; - s16 pab1; - s16 pab2; - u8 idx; - s8 *dyn_tssi2dbm; - - B43legacy_WARN_ON(!(phy->type == B43legacy_PHYTYPE_B || - phy->type == B43legacy_PHYTYPE_G)); - pab0 = (s16)(dev->dev->bus->sprom.pa0b0); - pab1 = (s16)(dev->dev->bus->sprom.pa0b1); - pab2 = (s16)(dev->dev->bus->sprom.pa0b2); - - if ((dev->dev->bus->chip_id == 0x4301) && (phy->radio_ver != 0x2050)) { - phy->idle_tssi = 0x34; - phy->tssi2dbm = b43legacy_tssi2dbm_b_table; - return 0; - } - - if (pab0 != 0 && pab1 != 0 && pab2 != 0 && - pab0 != -1 && pab1 != -1 && pab2 != -1) { - /* The pabX values are set in SPROM. Use them. */ - if ((s8)dev->dev->bus->sprom.itssi_bg != 0 && - (s8)dev->dev->bus->sprom.itssi_bg != -1) - phy->idle_tssi = (s8)(dev->dev->bus->sprom. - itssi_bg); - else - phy->idle_tssi = 62; - dyn_tssi2dbm = kmalloc(64, GFP_KERNEL); - if (dyn_tssi2dbm == NULL) { - b43legacyerr(dev->wl, "Could not allocate memory " - "for tssi2dbm table\n"); - return -ENOMEM; - } - for (idx = 0; idx < 64; idx++) - if (b43legacy_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0, - pab1, pab2)) { - phy->tssi2dbm = NULL; - b43legacyerr(dev->wl, "Could not generate " - "tssi2dBm table\n"); - kfree(dyn_tssi2dbm); - return -ENODEV; - } - phy->tssi2dbm = dyn_tssi2dbm; - phy->dyn_tssi_tbl = 1; - } else { - /* pabX values not set in SPROM. */ - switch (phy->type) { - case B43legacy_PHYTYPE_B: - phy->idle_tssi = 0x34; - phy->tssi2dbm = b43legacy_tssi2dbm_b_table; - break; - case B43legacy_PHYTYPE_G: - phy->idle_tssi = 0x34; - phy->tssi2dbm = b43legacy_tssi2dbm_g_table; - break; - } - } - - return 0; -} - -int b43legacy_phy_init(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - int err = -ENODEV; - - switch (phy->type) { - case B43legacy_PHYTYPE_B: - switch (phy->rev) { - case 2: - b43legacy_phy_initb2(dev); - err = 0; - break; - case 4: - b43legacy_phy_initb4(dev); - err = 0; - break; - case 5: - b43legacy_phy_initb5(dev); - err = 0; - break; - case 6: - b43legacy_phy_initb6(dev); - err = 0; - break; - } - break; - case B43legacy_PHYTYPE_G: - b43legacy_phy_initg(dev); - err = 0; - break; - } - if (err) - b43legacyerr(dev->wl, "Unknown PHYTYPE found\n"); - - return err; -} - -void b43legacy_phy_set_antenna_diversity(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 antennadiv; - u16 offset; - u16 value; - u32 ucodeflags; - - antennadiv = phy->antenna_diversity; - - if (antennadiv == 0xFFFF) - antennadiv = 3; - B43legacy_WARN_ON(antennadiv > 3); - - ucodeflags = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET); - b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET, - ucodeflags & ~B43legacy_UCODEFLAG_AUTODIV); - - switch (phy->type) { - case B43legacy_PHYTYPE_G: - offset = 0x0400; - - if (antennadiv == 2) - value = (3/*automatic*/ << 7); - else - value = (antennadiv << 7); - b43legacy_phy_write(dev, offset + 1, - (b43legacy_phy_read(dev, offset + 1) - & 0x7E7F) | value); - - if (antennadiv >= 2) { - if (antennadiv == 2) - value = (antennadiv << 7); - else - value = (0/*force0*/ << 7); - b43legacy_phy_write(dev, offset + 0x2B, - (b43legacy_phy_read(dev, - offset + 0x2B) - & 0xFEFF) | value); - } - - if (phy->type == B43legacy_PHYTYPE_G) { - if (antennadiv >= 2) - b43legacy_phy_write(dev, 0x048C, - b43legacy_phy_read(dev, - 0x048C) | 0x2000); - else - b43legacy_phy_write(dev, 0x048C, - b43legacy_phy_read(dev, - 0x048C) & ~0x2000); - if (phy->rev >= 2) { - b43legacy_phy_write(dev, 0x0461, - b43legacy_phy_read(dev, - 0x0461) | 0x0010); - b43legacy_phy_write(dev, 0x04AD, - (b43legacy_phy_read(dev, - 0x04AD) - & 0x00FF) | 0x0015); - if (phy->rev == 2) - b43legacy_phy_write(dev, 0x0427, - 0x0008); - else - b43legacy_phy_write(dev, 0x0427, - (b43legacy_phy_read(dev, 0x0427) - & 0x00FF) | 0x0008); - } else if (phy->rev >= 6) - b43legacy_phy_write(dev, 0x049B, 0x00DC); - } else { - if (phy->rev < 3) - b43legacy_phy_write(dev, 0x002B, - (b43legacy_phy_read(dev, - 0x002B) & 0x00FF) - | 0x0024); - else { - b43legacy_phy_write(dev, 0x0061, - b43legacy_phy_read(dev, - 0x0061) | 0x0010); - if (phy->rev == 3) { - b43legacy_phy_write(dev, 0x0093, - 0x001D); - b43legacy_phy_write(dev, 0x0027, - 0x0008); - } else { - b43legacy_phy_write(dev, 0x0093, - 0x003A); - b43legacy_phy_write(dev, 0x0027, - (b43legacy_phy_read(dev, 0x0027) - & 0x00FF) | 0x0008); - } - } - } - break; - case B43legacy_PHYTYPE_B: - if (dev->dev->id.revision == 2) - value = (3/*automatic*/ << 7); - else - value = (antennadiv << 7); - b43legacy_phy_write(dev, 0x03E2, - (b43legacy_phy_read(dev, 0x03E2) - & 0xFE7F) | value); - break; - default: - B43legacy_WARN_ON(1); - } - - if (antennadiv >= 2) { - ucodeflags = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET); - b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET, - ucodeflags | B43legacy_UCODEFLAG_AUTODIV); - } - - phy->antenna_diversity = antennadiv; -} - -/* Set the PowerSavingControlBits. - * Bitvalues: - * 0 => unset the bit - * 1 => set the bit - * -1 => calculate the bit - */ -void b43legacy_power_saving_ctl_bits(struct b43legacy_wldev *dev, - int bit25, int bit26) -{ - int i; - u32 status; - -/* FIXME: Force 25 to off and 26 to on for now: */ -bit25 = 0; -bit26 = 1; - - if (bit25 == -1) { - /* TODO: If powersave is not off and FIXME is not set and we - * are not in adhoc and thus is not an AP and we arei - * associated, set bit 25 */ - } - if (bit26 == -1) { - /* TODO: If the device is awake or this is an AP, or we are - * scanning, or FIXME, or we are associated, or FIXME, - * or the latest PS-Poll packet sent was successful, - * set bit26 */ - } - status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - if (bit25) - status |= B43legacy_MACCTL_HWPS; - else - status &= ~B43legacy_MACCTL_HWPS; - if (bit26) - status |= B43legacy_MACCTL_AWAKE; - else - status &= ~B43legacy_MACCTL_AWAKE; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); - if (bit26 && dev->dev->id.revision >= 5) { - for (i = 0; i < 100; i++) { - if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, - 0x0040) != 4) - break; - udelay(10); - } - } -} diff --git a/drivers/net/wireless/b43legacy/phy.h b/drivers/net/wireless/b43legacy/phy.h deleted file mode 100644 index 831a7a4760e5..000000000000 --- a/drivers/net/wireless/b43legacy/phy.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - - Broadcom B43legacy wireless driver - - Copyright (c) 2005 Martin Langer , - Stefano Brivio - Michael Buesch - Danny van Dyk - Andreas Jaggi - Copyright (c) 2007 Larry Finger - - Some parts of the code in this file are derived from the ipw2200 - driver Copyright(c) 2003 - 2004 Intel Corporation. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#ifndef B43legacy_PHY_H_ -#define B43legacy_PHY_H_ - -#include - -enum { - B43legacy_ANTENNA0, /* Antenna 0 */ - B43legacy_ANTENNA1, /* Antenna 0 */ - B43legacy_ANTENNA_AUTO1, /* Automatic, starting with antenna 1 */ - B43legacy_ANTENNA_AUTO0, /* Automatic, starting with antenna 0 */ - - B43legacy_ANTENNA_AUTO = B43legacy_ANTENNA_AUTO0, - B43legacy_ANTENNA_DEFAULT = B43legacy_ANTENNA_AUTO, -}; - -enum { - B43legacy_INTERFMODE_NONE, - B43legacy_INTERFMODE_NONWLAN, - B43legacy_INTERFMODE_MANUALWLAN, - B43legacy_INTERFMODE_AUTOWLAN, -}; - -/*** PHY Registers ***/ - -/* Routing */ -#define B43legacy_PHYROUTE_OFDM_GPHY 0x400 -#define B43legacy_PHYROUTE_EXT_GPHY 0x800 - -/* Base registers. */ -#define B43legacy_PHY_BASE(reg) (reg) -/* OFDM (A) registers of a G-PHY */ -#define B43legacy_PHY_OFDM(reg) ((reg) | B43legacy_PHYROUTE_OFDM_GPHY) -/* Extended G-PHY registers */ -#define B43legacy_PHY_EXTG(reg) ((reg) | B43legacy_PHYROUTE_EXT_GPHY) - - -/* Extended G-PHY Registers */ -#define B43legacy_PHY_CLASSCTL B43legacy_PHY_EXTG(0x02) /* Classify control */ -#define B43legacy_PHY_GTABCTL B43legacy_PHY_EXTG(0x03) /* G-PHY table control (see below) */ -#define B43legacy_PHY_GTABOFF 0x03FF /* G-PHY table offset (see below) */ -#define B43legacy_PHY_GTABNR 0xFC00 /* G-PHY table number (see below) */ -#define B43legacy_PHY_GTABNR_SHIFT 10 -#define B43legacy_PHY_GTABDATA B43legacy_PHY_EXTG(0x04) /* G-PHY table data */ -#define B43legacy_PHY_LO_MASK B43legacy_PHY_EXTG(0x0F) /* Local Oscillator control mask */ -#define B43legacy_PHY_LO_CTL B43legacy_PHY_EXTG(0x10) /* Local Oscillator control */ -#define B43legacy_PHY_RFOVER B43legacy_PHY_EXTG(0x11) /* RF override */ -#define B43legacy_PHY_RFOVERVAL B43legacy_PHY_EXTG(0x12) /* RF override value */ -/*** OFDM table numbers ***/ -#define B43legacy_OFDMTAB(number, offset) \ - (((number) << B43legacy_PHY_OTABLENR_SHIFT) \ - | (offset)) -#define B43legacy_OFDMTAB_AGC1 B43legacy_OFDMTAB(0x00, 0) -#define B43legacy_OFDMTAB_GAIN0 B43legacy_OFDMTAB(0x00, 0) -#define B43legacy_OFDMTAB_GAINX B43legacy_OFDMTAB(0x01, 0) -#define B43legacy_OFDMTAB_GAIN1 B43legacy_OFDMTAB(0x01, 4) -#define B43legacy_OFDMTAB_AGC3 B43legacy_OFDMTAB(0x02, 0) -#define B43legacy_OFDMTAB_GAIN2 B43legacy_OFDMTAB(0x02, 3) -#define B43legacy_OFDMTAB_LNAHPFGAIN1 B43legacy_OFDMTAB(0x03, 0) -#define B43legacy_OFDMTAB_WRSSI B43legacy_OFDMTAB(0x04, 0) -#define B43legacy_OFDMTAB_LNAHPFGAIN2 B43legacy_OFDMTAB(0x04, 0) -#define B43legacy_OFDMTAB_NOISESCALE B43legacy_OFDMTAB(0x05, 0) -#define B43legacy_OFDMTAB_AGC2 B43legacy_OFDMTAB(0x06, 0) -#define B43legacy_OFDMTAB_ROTOR B43legacy_OFDMTAB(0x08, 0) -#define B43legacy_OFDMTAB_ADVRETARD B43legacy_OFDMTAB(0x09, 0) -#define B43legacy_OFDMTAB_DAC B43legacy_OFDMTAB(0x0C, 0) -#define B43legacy_OFDMTAB_DC B43legacy_OFDMTAB(0x0E, 7) -#define B43legacy_OFDMTAB_PWRDYN2 B43legacy_OFDMTAB(0x0E, 12) -#define B43legacy_OFDMTAB_LNAGAIN B43legacy_OFDMTAB(0x0E, 13) - -#define B43legacy_OFDMTAB_LPFGAIN B43legacy_OFDMTAB(0x0F, 12) -#define B43legacy_OFDMTAB_RSSI B43legacy_OFDMTAB(0x10, 0) - -#define B43legacy_OFDMTAB_AGC1_R1 B43legacy_OFDMTAB(0x13, 0) -#define B43legacy_OFDMTAB_GAINX_R1 B43legacy_OFDMTAB(0x14, 0) -#define B43legacy_OFDMTAB_MINSIGSQ B43legacy_OFDMTAB(0x14, 1) -#define B43legacy_OFDMTAB_AGC3_R1 B43legacy_OFDMTAB(0x15, 0) -#define B43legacy_OFDMTAB_WRSSI_R1 B43legacy_OFDMTAB(0x15, 4) -#define B43legacy_OFDMTAB_TSSI B43legacy_OFDMTAB(0x15, 0) -#define B43legacy_OFDMTAB_DACRFPABB B43legacy_OFDMTAB(0x16, 0) -#define B43legacy_OFDMTAB_DACOFF B43legacy_OFDMTAB(0x17, 0) -#define B43legacy_OFDMTAB_DCBIAS B43legacy_OFDMTAB(0x18, 0) - -void b43legacy_put_attenuation_into_ranges(int *_bbatt, int *_rfatt); - -/* OFDM (A) PHY Registers */ -#define B43legacy_PHY_VERSION_OFDM B43legacy_PHY_OFDM(0x00) /* Versioning register for A-PHY */ -#define B43legacy_PHY_BBANDCFG B43legacy_PHY_OFDM(0x01) /* Baseband config */ -#define B43legacy_PHY_BBANDCFG_RXANT 0x180 /* RX Antenna selection */ -#define B43legacy_PHY_BBANDCFG_RXANT_SHIFT 7 -#define B43legacy_PHY_PWRDOWN B43legacy_PHY_OFDM(0x03) /* Powerdown */ -#define B43legacy_PHY_CRSTHRES1 B43legacy_PHY_OFDM(0x06) /* CRS Threshold 1 */ -#define B43legacy_PHY_LNAHPFCTL B43legacy_PHY_OFDM(0x1C) /* LNA/HPF control */ -#define B43legacy_PHY_ADIVRELATED B43legacy_PHY_OFDM(0x27) /* FIXME rename */ -#define B43legacy_PHY_CRS0 B43legacy_PHY_OFDM(0x29) -#define B43legacy_PHY_ANTDWELL B43legacy_PHY_OFDM(0x2B) /* Antenna dwell */ -#define B43legacy_PHY_ANTDWELL_AUTODIV1 0x0100 /* Automatic RX diversity start antenna */ -#define B43legacy_PHY_ENCORE B43legacy_PHY_OFDM(0x49) /* "Encore" (RangeMax / BroadRange) */ -#define B43legacy_PHY_ENCORE_EN 0x0200 /* Encore enable */ -#define B43legacy_PHY_LMS B43legacy_PHY_OFDM(0x55) -#define B43legacy_PHY_OFDM61 B43legacy_PHY_OFDM(0x61) /* FIXME rename */ -#define B43legacy_PHY_OFDM61_10 0x0010 /* FIXME rename */ -#define B43legacy_PHY_IQBAL B43legacy_PHY_OFDM(0x69) /* I/Q balance */ -#define B43legacy_PHY_OTABLECTL B43legacy_PHY_OFDM(0x72) /* OFDM table control (see below) */ -#define B43legacy_PHY_OTABLEOFF 0x03FF /* OFDM table offset (see below) */ -#define B43legacy_PHY_OTABLENR 0xFC00 /* OFDM table number (see below) */ -#define B43legacy_PHY_OTABLENR_SHIFT 10 -#define B43legacy_PHY_OTABLEI B43legacy_PHY_OFDM(0x73) /* OFDM table data I */ -#define B43legacy_PHY_OTABLEQ B43legacy_PHY_OFDM(0x74) /* OFDM table data Q */ -#define B43legacy_PHY_HPWR_TSSICTL B43legacy_PHY_OFDM(0x78) /* Hardware power TSSI control */ -#define B43legacy_PHY_NRSSITHRES B43legacy_PHY_OFDM(0x8A) /* NRSSI threshold */ -#define B43legacy_PHY_ANTWRSETT B43legacy_PHY_OFDM(0x8C) /* Antenna WR settle */ -#define B43legacy_PHY_ANTWRSETT_ARXDIV 0x2000 /* Automatic RX diversity enabled */ -#define B43legacy_PHY_CLIPPWRDOWNT B43legacy_PHY_OFDM(0x93) /* Clip powerdown threshold */ -#define B43legacy_PHY_OFDM9B B43legacy_PHY_OFDM(0x9B) /* FIXME rename */ -#define B43legacy_PHY_N1P1GAIN B43legacy_PHY_OFDM(0xA0) -#define B43legacy_PHY_P1P2GAIN B43legacy_PHY_OFDM(0xA1) -#define B43legacy_PHY_N1N2GAIN B43legacy_PHY_OFDM(0xA2) -#define B43legacy_PHY_CLIPTHRES B43legacy_PHY_OFDM(0xA3) -#define B43legacy_PHY_CLIPN1P2THRES B43legacy_PHY_OFDM(0xA4) -#define B43legacy_PHY_DIVSRCHIDX B43legacy_PHY_OFDM(0xA8) /* Divider search gain/index */ -#define B43legacy_PHY_CLIPP2THRES B43legacy_PHY_OFDM(0xA9) -#define B43legacy_PHY_CLIPP3THRES B43legacy_PHY_OFDM(0xAA) -#define B43legacy_PHY_DIVP1P2GAIN B43legacy_PHY_OFDM(0xAB) -#define B43legacy_PHY_DIVSRCHGAINBACK B43legacy_PHY_OFDM(0xAD) /* Divider search gain back */ -#define B43legacy_PHY_DIVSRCHGAINCHNG B43legacy_PHY_OFDM(0xAE) /* Divider search gain change */ -#define B43legacy_PHY_CRSTHRES1_R1 B43legacy_PHY_OFDM(0xC0) /* CRS Threshold 1 (rev 1 only) */ -#define B43legacy_PHY_CRSTHRES2_R1 B43legacy_PHY_OFDM(0xC1) /* CRS Threshold 2 (rev 1 only) */ -#define B43legacy_PHY_TSSIP_LTBASE B43legacy_PHY_OFDM(0x380) /* TSSI power lookup table base */ -#define B43legacy_PHY_DC_LTBASE B43legacy_PHY_OFDM(0x3A0) /* DC lookup table base */ -#define B43legacy_PHY_GAIN_LTBASE B43legacy_PHY_OFDM(0x3C0) /* Gain lookup table base */ - -void b43legacy_put_attenuation_into_ranges(int *_bbatt, int *_rfatt); - -/* Masks for the different PHY versioning registers. */ -#define B43legacy_PHYVER_ANALOG 0xF000 -#define B43legacy_PHYVER_ANALOG_SHIFT 12 -#define B43legacy_PHYVER_TYPE 0x0F00 -#define B43legacy_PHYVER_TYPE_SHIFT 8 -#define B43legacy_PHYVER_VERSION 0x00FF - -struct b43legacy_wldev; - -void b43legacy_phy_lock(struct b43legacy_wldev *dev); -void b43legacy_phy_unlock(struct b43legacy_wldev *dev); - -/* Card uses the loopback gain stuff */ -#define has_loopback_gain(phy) \ - (((phy)->rev > 1) || ((phy)->gmode)) - -u16 b43legacy_phy_read(struct b43legacy_wldev *dev, u16 offset); -void b43legacy_phy_write(struct b43legacy_wldev *dev, u16 offset, u16 val); - -int b43legacy_phy_init_tssi2dbm_table(struct b43legacy_wldev *dev); -int b43legacy_phy_init(struct b43legacy_wldev *dev); - -void b43legacy_set_rx_antenna(struct b43legacy_wldev *dev, int antenna); - -void b43legacy_phy_set_antenna_diversity(struct b43legacy_wldev *dev); -void b43legacy_phy_calibrate(struct b43legacy_wldev *dev); -int b43legacy_phy_connect(struct b43legacy_wldev *dev, int connect); - -void b43legacy_phy_lo_b_measure(struct b43legacy_wldev *dev); -void b43legacy_phy_lo_g_measure(struct b43legacy_wldev *dev); -void b43legacy_phy_xmitpower(struct b43legacy_wldev *dev); - -/* Adjust the LocalOscillator to the saved values. - * "fixed" is only set to 1 once in initialization. Set to 0 otherwise. - */ -void b43legacy_phy_lo_adjust(struct b43legacy_wldev *dev, int fixed); -void b43legacy_phy_lo_mark_all_unused(struct b43legacy_wldev *dev); - -void b43legacy_phy_set_baseband_attenuation(struct b43legacy_wldev *dev, - u16 baseband_attenuation); - -void b43legacy_power_saving_ctl_bits(struct b43legacy_wldev *dev, - int bit25, int bit26); - -#endif /* B43legacy_PHY_H_ */ diff --git a/drivers/net/wireless/b43legacy/pio.c b/drivers/net/wireless/b43legacy/pio.c deleted file mode 100644 index 282eedec675e..000000000000 --- a/drivers/net/wireless/b43legacy/pio.c +++ /dev/null @@ -1,695 +0,0 @@ -/* - - Broadcom B43legacy wireless driver - - PIO Transmission - - Copyright (c) 2005 Michael Buesch - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "b43legacy.h" -#include "pio.h" -#include "main.h" -#include "xmit.h" - -#include -#include - - -static void tx_start(struct b43legacy_pioqueue *queue) -{ - b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, - B43legacy_PIO_TXCTL_INIT); -} - -static void tx_octet(struct b43legacy_pioqueue *queue, - u8 octet) -{ - if (queue->need_workarounds) { - b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, octet); - b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, - B43legacy_PIO_TXCTL_WRITELO); - } else { - b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, - B43legacy_PIO_TXCTL_WRITELO); - b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, octet); - } -} - -static u16 tx_get_next_word(const u8 *txhdr, - const u8 *packet, - size_t txhdr_size, - unsigned int *pos) -{ - const u8 *source; - unsigned int i = *pos; - u16 ret; - - if (i < txhdr_size) - source = txhdr; - else { - source = packet; - i -= txhdr_size; - } - ret = le16_to_cpu(*((__le16 *)(source + i))); - *pos += 2; - - return ret; -} - -static void tx_data(struct b43legacy_pioqueue *queue, - u8 *txhdr, - const u8 *packet, - unsigned int octets) -{ - u16 data; - unsigned int i = 0; - - if (queue->need_workarounds) { - data = tx_get_next_word(txhdr, packet, - sizeof(struct b43legacy_txhdr_fw3), &i); - b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, data); - } - b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, - B43legacy_PIO_TXCTL_WRITELO | - B43legacy_PIO_TXCTL_WRITEHI); - while (i < octets - 1) { - data = tx_get_next_word(txhdr, packet, - sizeof(struct b43legacy_txhdr_fw3), &i); - b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, data); - } - if (octets % 2) - tx_octet(queue, packet[octets - - sizeof(struct b43legacy_txhdr_fw3) - 1]); -} - -static void tx_complete(struct b43legacy_pioqueue *queue, - struct sk_buff *skb) -{ - if (queue->need_workarounds) { - b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, - skb->data[skb->len - 1]); - b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, - B43legacy_PIO_TXCTL_WRITELO | - B43legacy_PIO_TXCTL_COMPLETE); - } else - b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, - B43legacy_PIO_TXCTL_COMPLETE); -} - -static u16 generate_cookie(struct b43legacy_pioqueue *queue, - struct b43legacy_pio_txpacket *packet) -{ - u16 cookie = 0x0000; - int packetindex; - - /* We use the upper 4 bits for the PIO - * controller ID and the lower 12 bits - * for the packet index (in the cache). - */ - switch (queue->mmio_base) { - case B43legacy_MMIO_PIO1_BASE: - break; - case B43legacy_MMIO_PIO2_BASE: - cookie = 0x1000; - break; - case B43legacy_MMIO_PIO3_BASE: - cookie = 0x2000; - break; - case B43legacy_MMIO_PIO4_BASE: - cookie = 0x3000; - break; - default: - B43legacy_WARN_ON(1); - } - packetindex = pio_txpacket_getindex(packet); - B43legacy_WARN_ON(!(((u16)packetindex & 0xF000) == 0x0000)); - cookie |= (u16)packetindex; - - return cookie; -} - -static -struct b43legacy_pioqueue *parse_cookie(struct b43legacy_wldev *dev, - u16 cookie, - struct b43legacy_pio_txpacket **packet) -{ - struct b43legacy_pio *pio = &dev->pio; - struct b43legacy_pioqueue *queue = NULL; - int packetindex; - - switch (cookie & 0xF000) { - case 0x0000: - queue = pio->queue0; - break; - case 0x1000: - queue = pio->queue1; - break; - case 0x2000: - queue = pio->queue2; - break; - case 0x3000: - queue = pio->queue3; - break; - default: - B43legacy_WARN_ON(1); - } - packetindex = (cookie & 0x0FFF); - B43legacy_WARN_ON(!(packetindex >= 0 && packetindex - < B43legacy_PIO_MAXTXPACKETS)); - *packet = &(queue->tx_packets_cache[packetindex]); - - return queue; -} - -union txhdr_union { - struct b43legacy_txhdr_fw3 txhdr_fw3; -}; - -static int pio_tx_write_fragment(struct b43legacy_pioqueue *queue, - struct sk_buff *skb, - struct b43legacy_pio_txpacket *packet, - size_t txhdr_size) -{ - union txhdr_union txhdr_data; - u8 *txhdr = NULL; - unsigned int octets; - int err; - - txhdr = (u8 *)(&txhdr_data.txhdr_fw3); - - B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0); - err = b43legacy_generate_txhdr(queue->dev, - txhdr, skb->data, skb->len, - IEEE80211_SKB_CB(skb), - generate_cookie(queue, packet)); - if (err) - return err; - - tx_start(queue); - octets = skb->len + txhdr_size; - if (queue->need_workarounds) - octets--; - tx_data(queue, txhdr, (u8 *)skb->data, octets); - tx_complete(queue, skb); - - return 0; -} - -static void free_txpacket(struct b43legacy_pio_txpacket *packet, - int irq_context) -{ - struct b43legacy_pioqueue *queue = packet->queue; - - if (packet->skb) { - if (irq_context) - dev_kfree_skb_irq(packet->skb); - else - dev_kfree_skb(packet->skb); - } - list_move(&packet->list, &queue->txfree); - queue->nr_txfree++; -} - -static int pio_tx_packet(struct b43legacy_pio_txpacket *packet) -{ - struct b43legacy_pioqueue *queue = packet->queue; - struct sk_buff *skb = packet->skb; - u16 octets; - int err; - - octets = (u16)skb->len + sizeof(struct b43legacy_txhdr_fw3); - if (queue->tx_devq_size < octets) { - b43legacywarn(queue->dev->wl, "PIO queue too small. " - "Dropping packet.\n"); - /* Drop it silently (return success) */ - free_txpacket(packet, 1); - return 0; - } - B43legacy_WARN_ON(queue->tx_devq_packets > - B43legacy_PIO_MAXTXDEVQPACKETS); - B43legacy_WARN_ON(queue->tx_devq_used > queue->tx_devq_size); - /* Check if there is sufficient free space on the device - * TX queue. If not, return and let the TX tasklet - * retry later. - */ - if (queue->tx_devq_packets == B43legacy_PIO_MAXTXDEVQPACKETS) - return -EBUSY; - if (queue->tx_devq_used + octets > queue->tx_devq_size) - return -EBUSY; - /* Now poke the device. */ - err = pio_tx_write_fragment(queue, skb, packet, - sizeof(struct b43legacy_txhdr_fw3)); - if (unlikely(err == -ENOKEY)) { - /* Drop this packet, as we don't have the encryption key - * anymore and must not transmit it unencrypted. */ - free_txpacket(packet, 1); - return 0; - } - - /* Account for the packet size. - * (We must not overflow the device TX queue) - */ - queue->tx_devq_packets++; - queue->tx_devq_used += octets; - - /* Transmission started, everything ok, move the - * packet to the txrunning list. - */ - list_move_tail(&packet->list, &queue->txrunning); - - return 0; -} - -static void tx_tasklet(unsigned long d) -{ - struct b43legacy_pioqueue *queue = (struct b43legacy_pioqueue *)d; - struct b43legacy_wldev *dev = queue->dev; - unsigned long flags; - struct b43legacy_pio_txpacket *packet, *tmp_packet; - int err; - u16 txctl; - - spin_lock_irqsave(&dev->wl->irq_lock, flags); - if (queue->tx_frozen) - goto out_unlock; - txctl = b43legacy_pio_read(queue, B43legacy_PIO_TXCTL); - if (txctl & B43legacy_PIO_TXCTL_SUSPEND) - goto out_unlock; - - list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) { - /* Try to transmit the packet. This can fail, if - * the device queue is full. In case of failure, the - * packet is left in the txqueue. - * If transmission succeed, the packet is moved to txrunning. - * If it is impossible to transmit the packet, it - * is dropped. - */ - err = pio_tx_packet(packet); - if (err) - break; - } -out_unlock: - spin_unlock_irqrestore(&dev->wl->irq_lock, flags); -} - -static void setup_txqueues(struct b43legacy_pioqueue *queue) -{ - struct b43legacy_pio_txpacket *packet; - int i; - - queue->nr_txfree = B43legacy_PIO_MAXTXPACKETS; - for (i = 0; i < B43legacy_PIO_MAXTXPACKETS; i++) { - packet = &(queue->tx_packets_cache[i]); - - packet->queue = queue; - INIT_LIST_HEAD(&packet->list); - - list_add(&packet->list, &queue->txfree); - } -} - -static -struct b43legacy_pioqueue *b43legacy_setup_pioqueue(struct b43legacy_wldev *dev, - u16 pio_mmio_base) -{ - struct b43legacy_pioqueue *queue; - u32 value; - u16 qsize; - - queue = kzalloc(sizeof(*queue), GFP_KERNEL); - if (!queue) - goto out; - - queue->dev = dev; - queue->mmio_base = pio_mmio_base; - queue->need_workarounds = (dev->dev->id.revision < 3); - - INIT_LIST_HEAD(&queue->txfree); - INIT_LIST_HEAD(&queue->txqueue); - INIT_LIST_HEAD(&queue->txrunning); - tasklet_init(&queue->txtask, tx_tasklet, - (unsigned long)queue); - - value = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - value &= ~B43legacy_MACCTL_BE; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value); - - qsize = b43legacy_read16(dev, queue->mmio_base - + B43legacy_PIO_TXQBUFSIZE); - if (qsize == 0) { - b43legacyerr(dev->wl, "This card does not support PIO " - "operation mode. Please use DMA mode " - "(module parameter pio=0).\n"); - goto err_freequeue; - } - if (qsize <= B43legacy_PIO_TXQADJUST) { - b43legacyerr(dev->wl, "PIO tx device-queue too small (%u)\n", - qsize); - goto err_freequeue; - } - qsize -= B43legacy_PIO_TXQADJUST; - queue->tx_devq_size = qsize; - - setup_txqueues(queue); - -out: - return queue; - -err_freequeue: - kfree(queue); - queue = NULL; - goto out; -} - -static void cancel_transfers(struct b43legacy_pioqueue *queue) -{ - struct b43legacy_pio_txpacket *packet, *tmp_packet; - - tasklet_kill(&queue->txtask); - - list_for_each_entry_safe(packet, tmp_packet, &queue->txrunning, list) - free_txpacket(packet, 0); - list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) - free_txpacket(packet, 0); -} - -static void b43legacy_destroy_pioqueue(struct b43legacy_pioqueue *queue) -{ - if (!queue) - return; - - cancel_transfers(queue); - kfree(queue); -} - -void b43legacy_pio_free(struct b43legacy_wldev *dev) -{ - struct b43legacy_pio *pio; - - if (!b43legacy_using_pio(dev)) - return; - pio = &dev->pio; - - b43legacy_destroy_pioqueue(pio->queue3); - pio->queue3 = NULL; - b43legacy_destroy_pioqueue(pio->queue2); - pio->queue2 = NULL; - b43legacy_destroy_pioqueue(pio->queue1); - pio->queue1 = NULL; - b43legacy_destroy_pioqueue(pio->queue0); - pio->queue0 = NULL; -} - -int b43legacy_pio_init(struct b43legacy_wldev *dev) -{ - struct b43legacy_pio *pio = &dev->pio; - struct b43legacy_pioqueue *queue; - int err = -ENOMEM; - - queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO1_BASE); - if (!queue) - goto out; - pio->queue0 = queue; - - queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO2_BASE); - if (!queue) - goto err_destroy0; - pio->queue1 = queue; - - queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO3_BASE); - if (!queue) - goto err_destroy1; - pio->queue2 = queue; - - queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO4_BASE); - if (!queue) - goto err_destroy2; - pio->queue3 = queue; - - if (dev->dev->id.revision < 3) - dev->irq_mask |= B43legacy_IRQ_PIO_WORKAROUND; - - b43legacydbg(dev->wl, "PIO initialized\n"); - err = 0; -out: - return err; - -err_destroy2: - b43legacy_destroy_pioqueue(pio->queue2); - pio->queue2 = NULL; -err_destroy1: - b43legacy_destroy_pioqueue(pio->queue1); - pio->queue1 = NULL; -err_destroy0: - b43legacy_destroy_pioqueue(pio->queue0); - pio->queue0 = NULL; - goto out; -} - -int b43legacy_pio_tx(struct b43legacy_wldev *dev, - struct sk_buff *skb) -{ - struct b43legacy_pioqueue *queue = dev->pio.queue1; - struct b43legacy_pio_txpacket *packet; - - B43legacy_WARN_ON(queue->tx_suspended); - B43legacy_WARN_ON(list_empty(&queue->txfree)); - - packet = list_entry(queue->txfree.next, struct b43legacy_pio_txpacket, - list); - packet->skb = skb; - - list_move_tail(&packet->list, &queue->txqueue); - queue->nr_txfree--; - B43legacy_WARN_ON(queue->nr_txfree >= B43legacy_PIO_MAXTXPACKETS); - - tasklet_schedule(&queue->txtask); - - return 0; -} - -void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, - const struct b43legacy_txstatus *status) -{ - struct b43legacy_pioqueue *queue; - struct b43legacy_pio_txpacket *packet; - struct ieee80211_tx_info *info; - int retry_limit; - - queue = parse_cookie(dev, status->cookie, &packet); - B43legacy_WARN_ON(!queue); - - if (!packet->skb) - return; - - queue->tx_devq_packets--; - queue->tx_devq_used -= (packet->skb->len + - sizeof(struct b43legacy_txhdr_fw3)); - - info = IEEE80211_SKB_CB(packet->skb); - - /* preserve the confiured retry limit before clearing the status - * The xmit function has overwritten the rc's value with the actual - * retry limit done by the hardware */ - retry_limit = info->status.rates[0].count; - ieee80211_tx_info_clear_status(info); - - if (status->acked) - info->flags |= IEEE80211_TX_STAT_ACK; - - if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) { - /* - * If the short retries (RTS, not data frame) have exceeded - * the limit, the hw will not have tried the selected rate, - * but will have used the fallback rate instead. - * Don't let the rate control count attempts for the selected - * rate in this case, otherwise the statistics will be off. - */ - info->status.rates[0].count = 0; - info->status.rates[1].count = status->frame_count; - } else { - if (status->frame_count > retry_limit) { - info->status.rates[0].count = retry_limit; - info->status.rates[1].count = status->frame_count - - retry_limit; - - } else { - info->status.rates[0].count = status->frame_count; - info->status.rates[1].idx = -1; - } - } - ieee80211_tx_status_irqsafe(dev->wl->hw, packet->skb); - packet->skb = NULL; - - free_txpacket(packet, 1); - /* If there are packets on the txqueue, poke the tasklet - * to transmit them. - */ - if (!list_empty(&queue->txqueue)) - tasklet_schedule(&queue->txtask); -} - -static void pio_rx_error(struct b43legacy_pioqueue *queue, - int clear_buffers, - const char *error) -{ - int i; - - b43legacyerr(queue->dev->wl, "PIO RX error: %s\n", error); - b43legacy_pio_write(queue, B43legacy_PIO_RXCTL, - B43legacy_PIO_RXCTL_READY); - if (clear_buffers) { - B43legacy_WARN_ON(queue->mmio_base != B43legacy_MMIO_PIO1_BASE); - for (i = 0; i < 15; i++) { - /* Dummy read. */ - b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); - } - } -} - -void b43legacy_pio_rx(struct b43legacy_pioqueue *queue) -{ - __le16 preamble[21] = { 0 }; - struct b43legacy_rxhdr_fw3 *rxhdr; - u16 tmp; - u16 len; - u16 macstat; - int i; - int preamble_readwords; - struct sk_buff *skb; - - tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXCTL); - if (!(tmp & B43legacy_PIO_RXCTL_DATAAVAILABLE)) - return; - b43legacy_pio_write(queue, B43legacy_PIO_RXCTL, - B43legacy_PIO_RXCTL_DATAAVAILABLE); - - for (i = 0; i < 10; i++) { - tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXCTL); - if (tmp & B43legacy_PIO_RXCTL_READY) - goto data_ready; - udelay(10); - } - b43legacydbg(queue->dev->wl, "PIO RX timed out\n"); - return; -data_ready: - - len = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); - if (unlikely(len > 0x700)) { - pio_rx_error(queue, 0, "len > 0x700"); - return; - } - if (unlikely(len == 0 && queue->mmio_base != - B43legacy_MMIO_PIO4_BASE)) { - pio_rx_error(queue, 0, "len == 0"); - return; - } - preamble[0] = cpu_to_le16(len); - if (queue->mmio_base == B43legacy_MMIO_PIO4_BASE) - preamble_readwords = 14 / sizeof(u16); - else - preamble_readwords = 18 / sizeof(u16); - for (i = 0; i < preamble_readwords; i++) { - tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); - preamble[i + 1] = cpu_to_le16(tmp); - } - rxhdr = (struct b43legacy_rxhdr_fw3 *)preamble; - macstat = le16_to_cpu(rxhdr->mac_status); - if (macstat & B43legacy_RX_MAC_FCSERR) { - pio_rx_error(queue, - (queue->mmio_base == B43legacy_MMIO_PIO1_BASE), - "Frame FCS error"); - return; - } - if (queue->mmio_base == B43legacy_MMIO_PIO4_BASE) { - /* We received an xmit status. */ - struct b43legacy_hwtxstatus *hw; - - hw = (struct b43legacy_hwtxstatus *)(preamble + 1); - b43legacy_handle_hwtxstatus(queue->dev, hw); - - return; - } - - skb = dev_alloc_skb(len); - if (unlikely(!skb)) { - pio_rx_error(queue, 1, "OOM"); - return; - } - skb_put(skb, len); - for (i = 0; i < len - 1; i += 2) { - tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); - *((__le16 *)(skb->data + i)) = cpu_to_le16(tmp); - } - if (len % 2) { - tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); - skb->data[len - 1] = (tmp & 0x00FF); - } - b43legacy_rx(queue->dev, skb, rxhdr); -} - -void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue) -{ - b43legacy_power_saving_ctl_bits(queue->dev, -1, 1); - b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, - b43legacy_pio_read(queue, B43legacy_PIO_TXCTL) - | B43legacy_PIO_TXCTL_SUSPEND); -} - -void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue) -{ - b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, - b43legacy_pio_read(queue, B43legacy_PIO_TXCTL) - & ~B43legacy_PIO_TXCTL_SUSPEND); - b43legacy_power_saving_ctl_bits(queue->dev, -1, -1); - tasklet_schedule(&queue->txtask); -} - -void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev) -{ - struct b43legacy_pio *pio; - - B43legacy_WARN_ON(!b43legacy_using_pio(dev)); - pio = &dev->pio; - pio->queue0->tx_frozen = 1; - pio->queue1->tx_frozen = 1; - pio->queue2->tx_frozen = 1; - pio->queue3->tx_frozen = 1; -} - -void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev) -{ - struct b43legacy_pio *pio; - - B43legacy_WARN_ON(!b43legacy_using_pio(dev)); - pio = &dev->pio; - pio->queue0->tx_frozen = 0; - pio->queue1->tx_frozen = 0; - pio->queue2->tx_frozen = 0; - pio->queue3->tx_frozen = 0; - if (!list_empty(&pio->queue0->txqueue)) - tasklet_schedule(&pio->queue0->txtask); - if (!list_empty(&pio->queue1->txqueue)) - tasklet_schedule(&pio->queue1->txtask); - if (!list_empty(&pio->queue2->txqueue)) - tasklet_schedule(&pio->queue2->txtask); - if (!list_empty(&pio->queue3->txqueue)) - tasklet_schedule(&pio->queue3->txtask); -} diff --git a/drivers/net/wireless/b43legacy/pio.h b/drivers/net/wireless/b43legacy/pio.h deleted file mode 100644 index 8e6773ea6e75..000000000000 --- a/drivers/net/wireless/b43legacy/pio.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef B43legacy_PIO_H_ -#define B43legacy_PIO_H_ - -#include "b43legacy.h" - -#include -#include -#include - - -#define B43legacy_PIO_TXCTL 0x00 -#define B43legacy_PIO_TXDATA 0x02 -#define B43legacy_PIO_TXQBUFSIZE 0x04 -#define B43legacy_PIO_RXCTL 0x08 -#define B43legacy_PIO_RXDATA 0x0A - -#define B43legacy_PIO_TXCTL_WRITELO (1 << 0) -#define B43legacy_PIO_TXCTL_WRITEHI (1 << 1) -#define B43legacy_PIO_TXCTL_COMPLETE (1 << 2) -#define B43legacy_PIO_TXCTL_INIT (1 << 3) -#define B43legacy_PIO_TXCTL_SUSPEND (1 << 7) - -#define B43legacy_PIO_RXCTL_DATAAVAILABLE (1 << 0) -#define B43legacy_PIO_RXCTL_READY (1 << 1) - -/* PIO constants */ -#define B43legacy_PIO_MAXTXDEVQPACKETS 31 -#define B43legacy_PIO_TXQADJUST 80 - -/* PIO tuning knobs */ -#define B43legacy_PIO_MAXTXPACKETS 256 - - - -#ifdef CONFIG_B43LEGACY_PIO - - -struct b43legacy_pioqueue; -struct b43legacy_xmitstatus; - -struct b43legacy_pio_txpacket { - struct b43legacy_pioqueue *queue; - struct sk_buff *skb; - struct list_head list; -}; - -#define pio_txpacket_getindex(packet) ((int)((packet) - \ - (packet)->queue->tx_packets_cache)) - -struct b43legacy_pioqueue { - struct b43legacy_wldev *dev; - u16 mmio_base; - - bool tx_suspended; - bool tx_frozen; - bool need_workarounds; /* Workarounds needed for core.rev < 3 */ - - /* Adjusted size of the device internal TX buffer. */ - u16 tx_devq_size; - /* Used octets of the device internal TX buffer. */ - u16 tx_devq_used; - /* Used packet slots in the device internal TX buffer. */ - u8 tx_devq_packets; - /* Packets from the txfree list can - * be taken on incoming TX requests. - */ - struct list_head txfree; - unsigned int nr_txfree; - /* Packets on the txqueue are queued, - * but not completely written to the chip, yet. - */ - struct list_head txqueue; - /* Packets on the txrunning queue are completely - * posted to the device. We are waiting for the txstatus. - */ - struct list_head txrunning; - struct tasklet_struct txtask; - struct b43legacy_pio_txpacket - tx_packets_cache[B43legacy_PIO_MAXTXPACKETS]; -}; - -static inline -u16 b43legacy_pio_read(struct b43legacy_pioqueue *queue, - u16 offset) -{ - return b43legacy_read16(queue->dev, queue->mmio_base + offset); -} - -static inline -void b43legacy_pio_write(struct b43legacy_pioqueue *queue, - u16 offset, u16 value) -{ - b43legacy_write16(queue->dev, queue->mmio_base + offset, value); - mmiowb(); -} - - -int b43legacy_pio_init(struct b43legacy_wldev *dev); -void b43legacy_pio_free(struct b43legacy_wldev *dev); - -int b43legacy_pio_tx(struct b43legacy_wldev *dev, - struct sk_buff *skb); -void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, - const struct b43legacy_txstatus *status); -void b43legacy_pio_rx(struct b43legacy_pioqueue *queue); - -/* Suspend TX queue in hardware. */ -void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue); -void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue); -/* Suspend (freeze) the TX tasklet (software level). */ -void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev); -void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev); - -#else /* CONFIG_B43LEGACY_PIO */ - -static inline -int b43legacy_pio_init(struct b43legacy_wldev *dev) -{ - return 0; -} -static inline -void b43legacy_pio_free(struct b43legacy_wldev *dev) -{ -} -static inline -int b43legacy_pio_tx(struct b43legacy_wldev *dev, - struct sk_buff *skb) -{ - return 0; -} -static inline -void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, - const struct b43legacy_txstatus *status) -{ -} -static inline -void b43legacy_pio_rx(struct b43legacy_pioqueue *queue) -{ -} -static inline -void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue) -{ -} -static inline -void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue) -{ -} -static inline -void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev) -{ -} -static inline -void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev) -{ -} - -#endif /* CONFIG_B43LEGACY_PIO */ -#endif /* B43legacy_PIO_H_ */ diff --git a/drivers/net/wireless/b43legacy/radio.c b/drivers/net/wireless/b43legacy/radio.c deleted file mode 100644 index 9501420340a9..000000000000 --- a/drivers/net/wireless/b43legacy/radio.c +++ /dev/null @@ -1,2143 +0,0 @@ -/* - - Broadcom B43legacy wireless driver - - Copyright (c) 2005 Martin Langer , - Stefano Brivio - Michael Buesch - Danny van Dyk - Andreas Jaggi - Copyright (c) 2007 Larry Finger - - Some parts of the code in this file are derived from the ipw2200 - driver Copyright(c) 2003 - 2004 Intel Corporation. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include - -#include "b43legacy.h" -#include "main.h" -#include "phy.h" -#include "radio.h" -#include "ilt.h" - - -/* Table for b43legacy_radio_calibrationvalue() */ -static const u16 rcc_table[16] = { - 0x0002, 0x0003, 0x0001, 0x000F, - 0x0006, 0x0007, 0x0005, 0x000F, - 0x000A, 0x000B, 0x0009, 0x000F, - 0x000E, 0x000F, 0x000D, 0x000F, -}; - -/* Reverse the bits of a 4bit value. - * Example: 1101 is flipped 1011 - */ -static u16 flip_4bit(u16 value) -{ - u16 flipped = 0x0000; - - B43legacy_BUG_ON(!((value & ~0x000F) == 0x0000)); - - flipped |= (value & 0x0001) << 3; - flipped |= (value & 0x0002) << 1; - flipped |= (value & 0x0004) >> 1; - flipped |= (value & 0x0008) >> 3; - - return flipped; -} - -/* Get the freq, as it has to be written to the device. */ -static inline -u16 channel2freq_bg(u8 channel) -{ - /* Frequencies are given as frequencies_bg[index] + 2.4GHz - * Starting with channel 1 - */ - static const u16 frequencies_bg[14] = { - 12, 17, 22, 27, - 32, 37, 42, 47, - 52, 57, 62, 67, - 72, 84, - }; - - if (unlikely(channel < 1 || channel > 14)) { - printk(KERN_INFO "b43legacy: Channel %d is out of range\n", - channel); - dump_stack(); - return 2412; - } - - return frequencies_bg[channel - 1]; -} - -void b43legacy_radio_lock(struct b43legacy_wldev *dev) -{ - u32 status; - - status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - B43legacy_WARN_ON(status & B43legacy_MACCTL_RADIOLOCK); - status |= B43legacy_MACCTL_RADIOLOCK; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); - mmiowb(); - udelay(10); -} - -void b43legacy_radio_unlock(struct b43legacy_wldev *dev) -{ - u32 status; - - b43legacy_read16(dev, B43legacy_MMIO_PHY_VER); /* dummy read */ - status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); - B43legacy_WARN_ON(!(status & B43legacy_MACCTL_RADIOLOCK)); - status &= ~B43legacy_MACCTL_RADIOLOCK; - b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); - mmiowb(); -} - -u16 b43legacy_radio_read16(struct b43legacy_wldev *dev, u16 offset) -{ - struct b43legacy_phy *phy = &dev->phy; - - switch (phy->type) { - case B43legacy_PHYTYPE_B: - if (phy->radio_ver == 0x2053) { - if (offset < 0x70) - offset += 0x80; - else if (offset < 0x80) - offset += 0x70; - } else if (phy->radio_ver == 0x2050) - offset |= 0x80; - else - B43legacy_WARN_ON(1); - break; - case B43legacy_PHYTYPE_G: - offset |= 0x80; - break; - default: - B43legacy_BUG_ON(1); - } - - b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset); - return b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_LOW); -} - -void b43legacy_radio_write16(struct b43legacy_wldev *dev, u16 offset, u16 val) -{ - b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset); - mmiowb(); - b43legacy_write16(dev, B43legacy_MMIO_RADIO_DATA_LOW, val); -} - -static void b43legacy_set_all_gains(struct b43legacy_wldev *dev, - s16 first, s16 second, s16 third) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 i; - u16 start = 0x08; - u16 end = 0x18; - u16 offset = 0x0400; - u16 tmp; - - if (phy->rev <= 1) { - offset = 0x5000; - start = 0x10; - end = 0x20; - } - - for (i = 0; i < 4; i++) - b43legacy_ilt_write(dev, offset + i, first); - - for (i = start; i < end; i++) - b43legacy_ilt_write(dev, offset + i, second); - - if (third != -1) { - tmp = ((u16)third << 14) | ((u16)third << 6); - b43legacy_phy_write(dev, 0x04A0, - (b43legacy_phy_read(dev, 0x04A0) & 0xBFBF) - | tmp); - b43legacy_phy_write(dev, 0x04A1, - (b43legacy_phy_read(dev, 0x04A1) & 0xBFBF) - | tmp); - b43legacy_phy_write(dev, 0x04A2, - (b43legacy_phy_read(dev, 0x04A2) & 0xBFBF) - | tmp); - } - b43legacy_dummy_transmission(dev); -} - -static void b43legacy_set_original_gains(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 i; - u16 tmp; - u16 offset = 0x0400; - u16 start = 0x0008; - u16 end = 0x0018; - - if (phy->rev <= 1) { - offset = 0x5000; - start = 0x0010; - end = 0x0020; - } - - for (i = 0; i < 4; i++) { - tmp = (i & 0xFFFC); - tmp |= (i & 0x0001) << 1; - tmp |= (i & 0x0002) >> 1; - - b43legacy_ilt_write(dev, offset + i, tmp); - } - - for (i = start; i < end; i++) - b43legacy_ilt_write(dev, offset + i, i - start); - - b43legacy_phy_write(dev, 0x04A0, - (b43legacy_phy_read(dev, 0x04A0) & 0xBFBF) - | 0x4040); - b43legacy_phy_write(dev, 0x04A1, - (b43legacy_phy_read(dev, 0x04A1) & 0xBFBF) - | 0x4040); - b43legacy_phy_write(dev, 0x04A2, - (b43legacy_phy_read(dev, 0x04A2) & 0xBFBF) - | 0x4000); - b43legacy_dummy_transmission(dev); -} - -/* Synthetic PU workaround */ -static void b43legacy_synth_pu_workaround(struct b43legacy_wldev *dev, - u8 channel) -{ - struct b43legacy_phy *phy = &dev->phy; - - might_sleep(); - - if (phy->radio_ver != 0x2050 || phy->radio_rev >= 6) - /* We do not need the workaround. */ - return; - - if (channel <= 10) - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, - channel2freq_bg(channel + 4)); - else - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, - channel2freq_bg(channel)); - msleep(1); - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, - channel2freq_bg(channel)); -} - -u8 b43legacy_radio_aci_detect(struct b43legacy_wldev *dev, u8 channel) -{ - struct b43legacy_phy *phy = &dev->phy; - u8 ret = 0; - u16 saved; - u16 rssi; - u16 temp; - int i; - int j = 0; - - saved = b43legacy_phy_read(dev, 0x0403); - b43legacy_radio_selectchannel(dev, channel, 0); - b43legacy_phy_write(dev, 0x0403, (saved & 0xFFF8) | 5); - if (phy->aci_hw_rssi) - rssi = b43legacy_phy_read(dev, 0x048A) & 0x3F; - else - rssi = saved & 0x3F; - /* clamp temp to signed 5bit */ - if (rssi > 32) - rssi -= 64; - for (i = 0; i < 100; i++) { - temp = (b43legacy_phy_read(dev, 0x047F) >> 8) & 0x3F; - if (temp > 32) - temp -= 64; - if (temp < rssi) - j++; - if (j >= 20) - ret = 1; - } - b43legacy_phy_write(dev, 0x0403, saved); - - return ret; -} - -u8 b43legacy_radio_aci_scan(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u8 ret[13]; - unsigned int channel = phy->channel; - unsigned int i; - unsigned int j; - unsigned int start; - unsigned int end; - - if (!((phy->type == B43legacy_PHYTYPE_G) && (phy->rev > 0))) - return 0; - - b43legacy_phy_lock(dev); - b43legacy_radio_lock(dev); - b43legacy_phy_write(dev, 0x0802, - b43legacy_phy_read(dev, 0x0802) & 0xFFFC); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, - b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) - & 0x7FFF); - b43legacy_set_all_gains(dev, 3, 8, 1); - - start = (channel - 5 > 0) ? channel - 5 : 1; - end = (channel + 5 < 14) ? channel + 5 : 13; - - for (i = start; i <= end; i++) { - if (abs(channel - i) > 2) - ret[i-1] = b43legacy_radio_aci_detect(dev, i); - } - b43legacy_radio_selectchannel(dev, channel, 0); - b43legacy_phy_write(dev, 0x0802, - (b43legacy_phy_read(dev, 0x0802) & 0xFFFC) - | 0x0003); - b43legacy_phy_write(dev, 0x0403, - b43legacy_phy_read(dev, 0x0403) & 0xFFF8); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, - b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) - | 0x8000); - b43legacy_set_original_gains(dev); - for (i = 0; i < 13; i++) { - if (!ret[i]) - continue; - end = (i + 5 < 13) ? i + 5 : 13; - for (j = i; j < end; j++) - ret[j] = 1; - } - b43legacy_radio_unlock(dev); - b43legacy_phy_unlock(dev); - - return ret[channel - 1]; -} - -/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ -void b43legacy_nrssi_hw_write(struct b43legacy_wldev *dev, u16 offset, s16 val) -{ - b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset); - mmiowb(); - b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_DATA, (u16)val); -} - -/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ -s16 b43legacy_nrssi_hw_read(struct b43legacy_wldev *dev, u16 offset) -{ - u16 val; - - b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset); - val = b43legacy_phy_read(dev, B43legacy_PHY_NRSSILT_DATA); - - return (s16)val; -} - -/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ -void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val) -{ - u16 i; - s16 tmp; - - for (i = 0; i < 64; i++) { - tmp = b43legacy_nrssi_hw_read(dev, i); - tmp -= val; - tmp = clamp_val(tmp, -32, 31); - b43legacy_nrssi_hw_write(dev, i, tmp); - } -} - -/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ -void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - s16 i; - s16 delta; - s32 tmp; - - delta = 0x1F - phy->nrssi[0]; - for (i = 0; i < 64; i++) { - tmp = (i - delta) * phy->nrssislope; - tmp /= 0x10000; - tmp += 0x3A; - tmp = clamp_val(tmp, 0, 0x3F); - phy->nrssi_lt[i] = tmp; - } -} - -static void b43legacy_calc_nrssi_offset(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 backup[20] = { 0 }; - s16 v47F; - u16 i; - u16 saved = 0xFFFF; - - backup[0] = b43legacy_phy_read(dev, 0x0001); - backup[1] = b43legacy_phy_read(dev, 0x0811); - backup[2] = b43legacy_phy_read(dev, 0x0812); - backup[3] = b43legacy_phy_read(dev, 0x0814); - backup[4] = b43legacy_phy_read(dev, 0x0815); - backup[5] = b43legacy_phy_read(dev, 0x005A); - backup[6] = b43legacy_phy_read(dev, 0x0059); - backup[7] = b43legacy_phy_read(dev, 0x0058); - backup[8] = b43legacy_phy_read(dev, 0x000A); - backup[9] = b43legacy_phy_read(dev, 0x0003); - backup[10] = b43legacy_radio_read16(dev, 0x007A); - backup[11] = b43legacy_radio_read16(dev, 0x0043); - - b43legacy_phy_write(dev, 0x0429, - b43legacy_phy_read(dev, 0x0429) & 0x7FFF); - b43legacy_phy_write(dev, 0x0001, - (b43legacy_phy_read(dev, 0x0001) & 0x3FFF) - | 0x4000); - b43legacy_phy_write(dev, 0x0811, - b43legacy_phy_read(dev, 0x0811) | 0x000C); - b43legacy_phy_write(dev, 0x0812, - (b43legacy_phy_read(dev, 0x0812) & 0xFFF3) - | 0x0004); - b43legacy_phy_write(dev, 0x0802, - b43legacy_phy_read(dev, 0x0802) & ~(0x1 | 0x2)); - if (phy->rev >= 6) { - backup[12] = b43legacy_phy_read(dev, 0x002E); - backup[13] = b43legacy_phy_read(dev, 0x002F); - backup[14] = b43legacy_phy_read(dev, 0x080F); - backup[15] = b43legacy_phy_read(dev, 0x0810); - backup[16] = b43legacy_phy_read(dev, 0x0801); - backup[17] = b43legacy_phy_read(dev, 0x0060); - backup[18] = b43legacy_phy_read(dev, 0x0014); - backup[19] = b43legacy_phy_read(dev, 0x0478); - - b43legacy_phy_write(dev, 0x002E, 0); - b43legacy_phy_write(dev, 0x002F, 0); - b43legacy_phy_write(dev, 0x080F, 0); - b43legacy_phy_write(dev, 0x0810, 0); - b43legacy_phy_write(dev, 0x0478, - b43legacy_phy_read(dev, 0x0478) | 0x0100); - b43legacy_phy_write(dev, 0x0801, - b43legacy_phy_read(dev, 0x0801) | 0x0040); - b43legacy_phy_write(dev, 0x0060, - b43legacy_phy_read(dev, 0x0060) | 0x0040); - b43legacy_phy_write(dev, 0x0014, - b43legacy_phy_read(dev, 0x0014) | 0x0200); - } - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) | 0x0070); - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) | 0x0080); - udelay(30); - - v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); - if (v47F >= 0x20) - v47F -= 0x40; - if (v47F == 31) { - for (i = 7; i >= 4; i--) { - b43legacy_radio_write16(dev, 0x007B, i); - udelay(20); - v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) - & 0x003F); - if (v47F >= 0x20) - v47F -= 0x40; - if (v47F < 31 && saved == 0xFFFF) - saved = i; - } - if (saved == 0xFFFF) - saved = 4; - } else { - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - & 0x007F); - b43legacy_phy_write(dev, 0x0814, - b43legacy_phy_read(dev, 0x0814) | 0x0001); - b43legacy_phy_write(dev, 0x0815, - b43legacy_phy_read(dev, 0x0815) & 0xFFFE); - b43legacy_phy_write(dev, 0x0811, - b43legacy_phy_read(dev, 0x0811) | 0x000C); - b43legacy_phy_write(dev, 0x0812, - b43legacy_phy_read(dev, 0x0812) | 0x000C); - b43legacy_phy_write(dev, 0x0811, - b43legacy_phy_read(dev, 0x0811) | 0x0030); - b43legacy_phy_write(dev, 0x0812, - b43legacy_phy_read(dev, 0x0812) | 0x0030); - b43legacy_phy_write(dev, 0x005A, 0x0480); - b43legacy_phy_write(dev, 0x0059, 0x0810); - b43legacy_phy_write(dev, 0x0058, 0x000D); - if (phy->analog == 0) - b43legacy_phy_write(dev, 0x0003, 0x0122); - else - b43legacy_phy_write(dev, 0x000A, - b43legacy_phy_read(dev, 0x000A) - | 0x2000); - b43legacy_phy_write(dev, 0x0814, - b43legacy_phy_read(dev, 0x0814) | 0x0004); - b43legacy_phy_write(dev, 0x0815, - b43legacy_phy_read(dev, 0x0815) & 0xFFFB); - b43legacy_phy_write(dev, 0x0003, - (b43legacy_phy_read(dev, 0x0003) & 0xFF9F) - | 0x0040); - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - | 0x000F); - b43legacy_set_all_gains(dev, 3, 0, 1); - b43legacy_radio_write16(dev, 0x0043, - (b43legacy_radio_read16(dev, 0x0043) - & 0x00F0) | 0x000F); - udelay(30); - v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); - if (v47F >= 0x20) - v47F -= 0x40; - if (v47F == -32) { - for (i = 0; i < 4; i++) { - b43legacy_radio_write16(dev, 0x007B, i); - udelay(20); - v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> - 8) & 0x003F); - if (v47F >= 0x20) - v47F -= 0x40; - if (v47F > -31 && saved == 0xFFFF) - saved = i; - } - if (saved == 0xFFFF) - saved = 3; - } else - saved = 0; - } - b43legacy_radio_write16(dev, 0x007B, saved); - - if (phy->rev >= 6) { - b43legacy_phy_write(dev, 0x002E, backup[12]); - b43legacy_phy_write(dev, 0x002F, backup[13]); - b43legacy_phy_write(dev, 0x080F, backup[14]); - b43legacy_phy_write(dev, 0x0810, backup[15]); - } - b43legacy_phy_write(dev, 0x0814, backup[3]); - b43legacy_phy_write(dev, 0x0815, backup[4]); - b43legacy_phy_write(dev, 0x005A, backup[5]); - b43legacy_phy_write(dev, 0x0059, backup[6]); - b43legacy_phy_write(dev, 0x0058, backup[7]); - b43legacy_phy_write(dev, 0x000A, backup[8]); - b43legacy_phy_write(dev, 0x0003, backup[9]); - b43legacy_radio_write16(dev, 0x0043, backup[11]); - b43legacy_radio_write16(dev, 0x007A, backup[10]); - b43legacy_phy_write(dev, 0x0802, - b43legacy_phy_read(dev, 0x0802) | 0x1 | 0x2); - b43legacy_phy_write(dev, 0x0429, - b43legacy_phy_read(dev, 0x0429) | 0x8000); - b43legacy_set_original_gains(dev); - if (phy->rev >= 6) { - b43legacy_phy_write(dev, 0x0801, backup[16]); - b43legacy_phy_write(dev, 0x0060, backup[17]); - b43legacy_phy_write(dev, 0x0014, backup[18]); - b43legacy_phy_write(dev, 0x0478, backup[19]); - } - b43legacy_phy_write(dev, 0x0001, backup[0]); - b43legacy_phy_write(dev, 0x0812, backup[2]); - b43legacy_phy_write(dev, 0x0811, backup[1]); -} - -void b43legacy_calc_nrssi_slope(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 backup[18] = { 0 }; - u16 tmp; - s16 nrssi0; - s16 nrssi1; - - switch (phy->type) { - case B43legacy_PHYTYPE_B: - backup[0] = b43legacy_radio_read16(dev, 0x007A); - backup[1] = b43legacy_radio_read16(dev, 0x0052); - backup[2] = b43legacy_radio_read16(dev, 0x0043); - backup[3] = b43legacy_phy_read(dev, 0x0030); - backup[4] = b43legacy_phy_read(dev, 0x0026); - backup[5] = b43legacy_phy_read(dev, 0x0015); - backup[6] = b43legacy_phy_read(dev, 0x002A); - backup[7] = b43legacy_phy_read(dev, 0x0020); - backup[8] = b43legacy_phy_read(dev, 0x005A); - backup[9] = b43legacy_phy_read(dev, 0x0059); - backup[10] = b43legacy_phy_read(dev, 0x0058); - backup[11] = b43legacy_read16(dev, 0x03E2); - backup[12] = b43legacy_read16(dev, 0x03E6); - backup[13] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); - - tmp = b43legacy_radio_read16(dev, 0x007A); - tmp &= (phy->rev >= 5) ? 0x007F : 0x000F; - b43legacy_radio_write16(dev, 0x007A, tmp); - b43legacy_phy_write(dev, 0x0030, 0x00FF); - b43legacy_write16(dev, 0x03EC, 0x7F7F); - b43legacy_phy_write(dev, 0x0026, 0x0000); - b43legacy_phy_write(dev, 0x0015, - b43legacy_phy_read(dev, 0x0015) | 0x0020); - b43legacy_phy_write(dev, 0x002A, 0x08A3); - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - | 0x0080); - - nrssi0 = (s16)b43legacy_phy_read(dev, 0x0027); - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - & 0x007F); - if (phy->analog >= 2) - b43legacy_write16(dev, 0x03E6, 0x0040); - else if (phy->analog == 0) - b43legacy_write16(dev, 0x03E6, 0x0122); - else - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, - b43legacy_read16(dev, - B43legacy_MMIO_CHANNEL_EXT) & 0x2000); - b43legacy_phy_write(dev, 0x0020, 0x3F3F); - b43legacy_phy_write(dev, 0x0015, 0xF330); - b43legacy_radio_write16(dev, 0x005A, 0x0060); - b43legacy_radio_write16(dev, 0x0043, - b43legacy_radio_read16(dev, 0x0043) - & 0x00F0); - b43legacy_phy_write(dev, 0x005A, 0x0480); - b43legacy_phy_write(dev, 0x0059, 0x0810); - b43legacy_phy_write(dev, 0x0058, 0x000D); - udelay(20); - - nrssi1 = (s16)b43legacy_phy_read(dev, 0x0027); - b43legacy_phy_write(dev, 0x0030, backup[3]); - b43legacy_radio_write16(dev, 0x007A, backup[0]); - b43legacy_write16(dev, 0x03E2, backup[11]); - b43legacy_phy_write(dev, 0x0026, backup[4]); - b43legacy_phy_write(dev, 0x0015, backup[5]); - b43legacy_phy_write(dev, 0x002A, backup[6]); - b43legacy_synth_pu_workaround(dev, phy->channel); - if (phy->analog != 0) - b43legacy_write16(dev, 0x03F4, backup[13]); - - b43legacy_phy_write(dev, 0x0020, backup[7]); - b43legacy_phy_write(dev, 0x005A, backup[8]); - b43legacy_phy_write(dev, 0x0059, backup[9]); - b43legacy_phy_write(dev, 0x0058, backup[10]); - b43legacy_radio_write16(dev, 0x0052, backup[1]); - b43legacy_radio_write16(dev, 0x0043, backup[2]); - - if (nrssi0 == nrssi1) - phy->nrssislope = 0x00010000; - else - phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1); - - if (nrssi0 <= -4) { - phy->nrssi[0] = nrssi0; - phy->nrssi[1] = nrssi1; - } - break; - case B43legacy_PHYTYPE_G: - if (phy->radio_rev >= 9) - return; - if (phy->radio_rev == 8) - b43legacy_calc_nrssi_offset(dev); - - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, - b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) - & 0x7FFF); - b43legacy_phy_write(dev, 0x0802, - b43legacy_phy_read(dev, 0x0802) & 0xFFFC); - backup[7] = b43legacy_read16(dev, 0x03E2); - b43legacy_write16(dev, 0x03E2, - b43legacy_read16(dev, 0x03E2) | 0x8000); - backup[0] = b43legacy_radio_read16(dev, 0x007A); - backup[1] = b43legacy_radio_read16(dev, 0x0052); - backup[2] = b43legacy_radio_read16(dev, 0x0043); - backup[3] = b43legacy_phy_read(dev, 0x0015); - backup[4] = b43legacy_phy_read(dev, 0x005A); - backup[5] = b43legacy_phy_read(dev, 0x0059); - backup[6] = b43legacy_phy_read(dev, 0x0058); - backup[8] = b43legacy_read16(dev, 0x03E6); - backup[9] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); - if (phy->rev >= 3) { - backup[10] = b43legacy_phy_read(dev, 0x002E); - backup[11] = b43legacy_phy_read(dev, 0x002F); - backup[12] = b43legacy_phy_read(dev, 0x080F); - backup[13] = b43legacy_phy_read(dev, - B43legacy_PHY_G_LO_CONTROL); - backup[14] = b43legacy_phy_read(dev, 0x0801); - backup[15] = b43legacy_phy_read(dev, 0x0060); - backup[16] = b43legacy_phy_read(dev, 0x0014); - backup[17] = b43legacy_phy_read(dev, 0x0478); - b43legacy_phy_write(dev, 0x002E, 0); - b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, 0); - switch (phy->rev) { - case 4: case 6: case 7: - b43legacy_phy_write(dev, 0x0478, - b43legacy_phy_read(dev, - 0x0478) | 0x0100); - b43legacy_phy_write(dev, 0x0801, - b43legacy_phy_read(dev, - 0x0801) | 0x0040); - break; - case 3: case 5: - b43legacy_phy_write(dev, 0x0801, - b43legacy_phy_read(dev, - 0x0801) & 0xFFBF); - break; - } - b43legacy_phy_write(dev, 0x0060, - b43legacy_phy_read(dev, 0x0060) - | 0x0040); - b43legacy_phy_write(dev, 0x0014, - b43legacy_phy_read(dev, 0x0014) - | 0x0200); - } - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - | 0x0070); - b43legacy_set_all_gains(dev, 0, 8, 0); - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - & 0x00F7); - if (phy->rev >= 2) { - b43legacy_phy_write(dev, 0x0811, - (b43legacy_phy_read(dev, 0x0811) - & 0xFFCF) | 0x0030); - b43legacy_phy_write(dev, 0x0812, - (b43legacy_phy_read(dev, 0x0812) - & 0xFFCF) | 0x0010); - } - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - | 0x0080); - udelay(20); - - nrssi0 = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); - if (nrssi0 >= 0x0020) - nrssi0 -= 0x0040; - - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - & 0x007F); - if (phy->analog >= 2) - b43legacy_phy_write(dev, 0x0003, - (b43legacy_phy_read(dev, 0x0003) - & 0xFF9F) | 0x0040); - - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, - b43legacy_read16(dev, - B43legacy_MMIO_CHANNEL_EXT) | 0x2000); - b43legacy_radio_write16(dev, 0x007A, - b43legacy_radio_read16(dev, 0x007A) - | 0x000F); - b43legacy_phy_write(dev, 0x0015, 0xF330); - if (phy->rev >= 2) { - b43legacy_phy_write(dev, 0x0812, - (b43legacy_phy_read(dev, 0x0812) - & 0xFFCF) | 0x0020); - b43legacy_phy_write(dev, 0x0811, - (b43legacy_phy_read(dev, 0x0811) - & 0xFFCF) | 0x0020); - } - - b43legacy_set_all_gains(dev, 3, 0, 1); - if (phy->radio_rev == 8) - b43legacy_radio_write16(dev, 0x0043, 0x001F); - else { - tmp = b43legacy_radio_read16(dev, 0x0052) & 0xFF0F; - b43legacy_radio_write16(dev, 0x0052, tmp | 0x0060); - tmp = b43legacy_radio_read16(dev, 0x0043) & 0xFFF0; - b43legacy_radio_write16(dev, 0x0043, tmp | 0x0009); - } - b43legacy_phy_write(dev, 0x005A, 0x0480); - b43legacy_phy_write(dev, 0x0059, 0x0810); - b43legacy_phy_write(dev, 0x0058, 0x000D); - udelay(20); - nrssi1 = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); - if (nrssi1 >= 0x0020) - nrssi1 -= 0x0040; - if (nrssi0 == nrssi1) - phy->nrssislope = 0x00010000; - else - phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1); - if (nrssi0 >= -4) { - phy->nrssi[0] = nrssi1; - phy->nrssi[1] = nrssi0; - } - if (phy->rev >= 3) { - b43legacy_phy_write(dev, 0x002E, backup[10]); - b43legacy_phy_write(dev, 0x002F, backup[11]); - b43legacy_phy_write(dev, 0x080F, backup[12]); - b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, - backup[13]); - } - if (phy->rev >= 2) { - b43legacy_phy_write(dev, 0x0812, - b43legacy_phy_read(dev, 0x0812) - & 0xFFCF); - b43legacy_phy_write(dev, 0x0811, - b43legacy_phy_read(dev, 0x0811) - & 0xFFCF); - } - - b43legacy_radio_write16(dev, 0x007A, backup[0]); - b43legacy_radio_write16(dev, 0x0052, backup[1]); - b43legacy_radio_write16(dev, 0x0043, backup[2]); - b43legacy_write16(dev, 0x03E2, backup[7]); - b43legacy_write16(dev, 0x03E6, backup[8]); - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, backup[9]); - b43legacy_phy_write(dev, 0x0015, backup[3]); - b43legacy_phy_write(dev, 0x005A, backup[4]); - b43legacy_phy_write(dev, 0x0059, backup[5]); - b43legacy_phy_write(dev, 0x0058, backup[6]); - b43legacy_synth_pu_workaround(dev, phy->channel); - b43legacy_phy_write(dev, 0x0802, - b43legacy_phy_read(dev, 0x0802) | 0x0003); - b43legacy_set_original_gains(dev); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, - b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) - | 0x8000); - if (phy->rev >= 3) { - b43legacy_phy_write(dev, 0x0801, backup[14]); - b43legacy_phy_write(dev, 0x0060, backup[15]); - b43legacy_phy_write(dev, 0x0014, backup[16]); - b43legacy_phy_write(dev, 0x0478, backup[17]); - } - b43legacy_nrssi_mem_update(dev); - b43legacy_calc_nrssi_threshold(dev); - break; - default: - B43legacy_BUG_ON(1); - } -} - -void b43legacy_calc_nrssi_threshold(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - s32 threshold; - s32 a; - s32 b; - s16 tmp16; - u16 tmp_u16; - - switch (phy->type) { - case B43legacy_PHYTYPE_B: { - if (phy->radio_ver != 0x2050) - return; - if (!(dev->dev->bus->sprom.boardflags_lo & - B43legacy_BFL_RSSI)) - return; - - if (phy->radio_rev >= 6) { - threshold = (phy->nrssi[1] - phy->nrssi[0]) * 32; - threshold += 20 * (phy->nrssi[0] + 1); - threshold /= 40; - } else - threshold = phy->nrssi[1] - 5; - - threshold = clamp_val(threshold, 0, 0x3E); - b43legacy_phy_read(dev, 0x0020); /* dummy read */ - b43legacy_phy_write(dev, 0x0020, (((u16)threshold) << 8) - | 0x001C); - - if (phy->radio_rev >= 6) { - b43legacy_phy_write(dev, 0x0087, 0x0E0D); - b43legacy_phy_write(dev, 0x0086, 0x0C0B); - b43legacy_phy_write(dev, 0x0085, 0x0A09); - b43legacy_phy_write(dev, 0x0084, 0x0808); - b43legacy_phy_write(dev, 0x0083, 0x0808); - b43legacy_phy_write(dev, 0x0082, 0x0604); - b43legacy_phy_write(dev, 0x0081, 0x0302); - b43legacy_phy_write(dev, 0x0080, 0x0100); - } - break; - } - case B43legacy_PHYTYPE_G: - if (!phy->gmode || - !(dev->dev->bus->sprom.boardflags_lo & - B43legacy_BFL_RSSI)) { - tmp16 = b43legacy_nrssi_hw_read(dev, 0x20); - if (tmp16 >= 0x20) - tmp16 -= 0x40; - if (tmp16 < 3) - b43legacy_phy_write(dev, 0x048A, - (b43legacy_phy_read(dev, - 0x048A) & 0xF000) | 0x09EB); - else - b43legacy_phy_write(dev, 0x048A, - (b43legacy_phy_read(dev, - 0x048A) & 0xF000) | 0x0AED); - } else { - if (phy->interfmode == - B43legacy_RADIO_INTERFMODE_NONWLAN) { - a = 0xE; - b = 0xA; - } else if (!phy->aci_wlan_automatic && - phy->aci_enable) { - a = 0x13; - b = 0x12; - } else { - a = 0xE; - b = 0x11; - } - - a = a * (phy->nrssi[1] - phy->nrssi[0]); - a += (phy->nrssi[0] << 6); - if (a < 32) - a += 31; - else - a += 32; - a = a >> 6; - a = clamp_val(a, -31, 31); - - b = b * (phy->nrssi[1] - phy->nrssi[0]); - b += (phy->nrssi[0] << 6); - if (b < 32) - b += 31; - else - b += 32; - b = b >> 6; - b = clamp_val(b, -31, 31); - - tmp_u16 = b43legacy_phy_read(dev, 0x048A) & 0xF000; - tmp_u16 |= ((u32)b & 0x0000003F); - tmp_u16 |= (((u32)a & 0x0000003F) << 6); - b43legacy_phy_write(dev, 0x048A, tmp_u16); - } - break; - default: - B43legacy_BUG_ON(1); - } -} - -/* Stack implementation to save/restore values from the - * interference mitigation code. - * It is save to restore values in random order. - */ -static void _stack_save(u32 *_stackptr, size_t *stackidx, - u8 id, u16 offset, u16 value) -{ - u32 *stackptr = &(_stackptr[*stackidx]); - - B43legacy_WARN_ON(!((offset & 0xE000) == 0x0000)); - B43legacy_WARN_ON(!((id & 0xF8) == 0x00)); - *stackptr = offset; - *stackptr |= ((u32)id) << 13; - *stackptr |= ((u32)value) << 16; - (*stackidx)++; - B43legacy_WARN_ON(!(*stackidx < B43legacy_INTERFSTACK_SIZE)); -} - -static u16 _stack_restore(u32 *stackptr, - u8 id, u16 offset) -{ - size_t i; - - B43legacy_WARN_ON(!((offset & 0xE000) == 0x0000)); - B43legacy_WARN_ON(!((id & 0xF8) == 0x00)); - for (i = 0; i < B43legacy_INTERFSTACK_SIZE; i++, stackptr++) { - if ((*stackptr & 0x00001FFF) != offset) - continue; - if (((*stackptr & 0x00007000) >> 13) != id) - continue; - return ((*stackptr & 0xFFFF0000) >> 16); - } - B43legacy_BUG_ON(1); - - return 0; -} - -#define phy_stacksave(offset) \ - do { \ - _stack_save(stack, &stackidx, 0x1, (offset), \ - b43legacy_phy_read(dev, (offset))); \ - } while (0) -#define phy_stackrestore(offset) \ - do { \ - b43legacy_phy_write(dev, (offset), \ - _stack_restore(stack, 0x1, \ - (offset))); \ - } while (0) -#define radio_stacksave(offset) \ - do { \ - _stack_save(stack, &stackidx, 0x2, (offset), \ - b43legacy_radio_read16(dev, (offset))); \ - } while (0) -#define radio_stackrestore(offset) \ - do { \ - b43legacy_radio_write16(dev, (offset), \ - _stack_restore(stack, 0x2, \ - (offset))); \ - } while (0) -#define ilt_stacksave(offset) \ - do { \ - _stack_save(stack, &stackidx, 0x3, (offset), \ - b43legacy_ilt_read(dev, (offset))); \ - } while (0) -#define ilt_stackrestore(offset) \ - do { \ - b43legacy_ilt_write(dev, (offset), \ - _stack_restore(stack, 0x3, \ - (offset))); \ - } while (0) - -static void -b43legacy_radio_interference_mitigation_enable(struct b43legacy_wldev *dev, - int mode) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 tmp; - u16 flipped; - u32 tmp32; - size_t stackidx = 0; - u32 *stack = phy->interfstack; - - switch (mode) { - case B43legacy_RADIO_INTERFMODE_NONWLAN: - if (phy->rev != 1) { - b43legacy_phy_write(dev, 0x042B, - b43legacy_phy_read(dev, 0x042B) - | 0x0800); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, - b43legacy_phy_read(dev, - B43legacy_PHY_G_CRS) & ~0x4000); - break; - } - radio_stacksave(0x0078); - tmp = (b43legacy_radio_read16(dev, 0x0078) & 0x001E); - flipped = flip_4bit(tmp); - if (flipped < 10 && flipped >= 8) - flipped = 7; - else if (flipped >= 10) - flipped -= 3; - flipped = flip_4bit(flipped); - flipped = (flipped << 1) | 0x0020; - b43legacy_radio_write16(dev, 0x0078, flipped); - - b43legacy_calc_nrssi_threshold(dev); - - phy_stacksave(0x0406); - b43legacy_phy_write(dev, 0x0406, 0x7E28); - - b43legacy_phy_write(dev, 0x042B, - b43legacy_phy_read(dev, 0x042B) | 0x0800); - b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, - b43legacy_phy_read(dev, - B43legacy_PHY_RADIO_BITFIELD) | 0x1000); - - phy_stacksave(0x04A0); - b43legacy_phy_write(dev, 0x04A0, - (b43legacy_phy_read(dev, 0x04A0) & 0xC0C0) - | 0x0008); - phy_stacksave(0x04A1); - b43legacy_phy_write(dev, 0x04A1, - (b43legacy_phy_read(dev, 0x04A1) & 0xC0C0) - | 0x0605); - phy_stacksave(0x04A2); - b43legacy_phy_write(dev, 0x04A2, - (b43legacy_phy_read(dev, 0x04A2) & 0xC0C0) - | 0x0204); - phy_stacksave(0x04A8); - b43legacy_phy_write(dev, 0x04A8, - (b43legacy_phy_read(dev, 0x04A8) & 0xC0C0) - | 0x0803); - phy_stacksave(0x04AB); - b43legacy_phy_write(dev, 0x04AB, - (b43legacy_phy_read(dev, 0x04AB) & 0xC0C0) - | 0x0605); - - phy_stacksave(0x04A7); - b43legacy_phy_write(dev, 0x04A7, 0x0002); - phy_stacksave(0x04A3); - b43legacy_phy_write(dev, 0x04A3, 0x287A); - phy_stacksave(0x04A9); - b43legacy_phy_write(dev, 0x04A9, 0x2027); - phy_stacksave(0x0493); - b43legacy_phy_write(dev, 0x0493, 0x32F5); - phy_stacksave(0x04AA); - b43legacy_phy_write(dev, 0x04AA, 0x2027); - phy_stacksave(0x04AC); - b43legacy_phy_write(dev, 0x04AC, 0x32F5); - break; - case B43legacy_RADIO_INTERFMODE_MANUALWLAN: - if (b43legacy_phy_read(dev, 0x0033) & 0x0800) - break; - - phy->aci_enable = true; - - phy_stacksave(B43legacy_PHY_RADIO_BITFIELD); - phy_stacksave(B43legacy_PHY_G_CRS); - if (phy->rev < 2) - phy_stacksave(0x0406); - else { - phy_stacksave(0x04C0); - phy_stacksave(0x04C1); - } - phy_stacksave(0x0033); - phy_stacksave(0x04A7); - phy_stacksave(0x04A3); - phy_stacksave(0x04A9); - phy_stacksave(0x04AA); - phy_stacksave(0x04AC); - phy_stacksave(0x0493); - phy_stacksave(0x04A1); - phy_stacksave(0x04A0); - phy_stacksave(0x04A2); - phy_stacksave(0x048A); - phy_stacksave(0x04A8); - phy_stacksave(0x04AB); - if (phy->rev == 2) { - phy_stacksave(0x04AD); - phy_stacksave(0x04AE); - } else if (phy->rev >= 3) { - phy_stacksave(0x04AD); - phy_stacksave(0x0415); - phy_stacksave(0x0416); - phy_stacksave(0x0417); - ilt_stacksave(0x1A00 + 0x2); - ilt_stacksave(0x1A00 + 0x3); - } - phy_stacksave(0x042B); - phy_stacksave(0x048C); - - b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, - b43legacy_phy_read(dev, - B43legacy_PHY_RADIO_BITFIELD) & ~0x1000); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, - (b43legacy_phy_read(dev, - B43legacy_PHY_G_CRS) - & 0xFFFC) | 0x0002); - - b43legacy_phy_write(dev, 0x0033, 0x0800); - b43legacy_phy_write(dev, 0x04A3, 0x2027); - b43legacy_phy_write(dev, 0x04A9, 0x1CA8); - b43legacy_phy_write(dev, 0x0493, 0x287A); - b43legacy_phy_write(dev, 0x04AA, 0x1CA8); - b43legacy_phy_write(dev, 0x04AC, 0x287A); - - b43legacy_phy_write(dev, 0x04A0, - (b43legacy_phy_read(dev, 0x04A0) - & 0xFFC0) | 0x001A); - b43legacy_phy_write(dev, 0x04A7, 0x000D); - - if (phy->rev < 2) - b43legacy_phy_write(dev, 0x0406, 0xFF0D); - else if (phy->rev == 2) { - b43legacy_phy_write(dev, 0x04C0, 0xFFFF); - b43legacy_phy_write(dev, 0x04C1, 0x00A9); - } else { - b43legacy_phy_write(dev, 0x04C0, 0x00C1); - b43legacy_phy_write(dev, 0x04C1, 0x0059); - } - - b43legacy_phy_write(dev, 0x04A1, - (b43legacy_phy_read(dev, 0x04A1) - & 0xC0FF) | 0x1800); - b43legacy_phy_write(dev, 0x04A1, - (b43legacy_phy_read(dev, 0x04A1) - & 0xFFC0) | 0x0015); - b43legacy_phy_write(dev, 0x04A8, - (b43legacy_phy_read(dev, 0x04A8) - & 0xCFFF) | 0x1000); - b43legacy_phy_write(dev, 0x04A8, - (b43legacy_phy_read(dev, 0x04A8) - & 0xF0FF) | 0x0A00); - b43legacy_phy_write(dev, 0x04AB, - (b43legacy_phy_read(dev, 0x04AB) - & 0xCFFF) | 0x1000); - b43legacy_phy_write(dev, 0x04AB, - (b43legacy_phy_read(dev, 0x04AB) - & 0xF0FF) | 0x0800); - b43legacy_phy_write(dev, 0x04AB, - (b43legacy_phy_read(dev, 0x04AB) - & 0xFFCF) | 0x0010); - b43legacy_phy_write(dev, 0x04AB, - (b43legacy_phy_read(dev, 0x04AB) - & 0xFFF0) | 0x0005); - b43legacy_phy_write(dev, 0x04A8, - (b43legacy_phy_read(dev, 0x04A8) - & 0xFFCF) | 0x0010); - b43legacy_phy_write(dev, 0x04A8, - (b43legacy_phy_read(dev, 0x04A8) - & 0xFFF0) | 0x0006); - b43legacy_phy_write(dev, 0x04A2, - (b43legacy_phy_read(dev, 0x04A2) - & 0xF0FF) | 0x0800); - b43legacy_phy_write(dev, 0x04A0, - (b43legacy_phy_read(dev, 0x04A0) - & 0xF0FF) | 0x0500); - b43legacy_phy_write(dev, 0x04A2, - (b43legacy_phy_read(dev, 0x04A2) - & 0xFFF0) | 0x000B); - - if (phy->rev >= 3) { - b43legacy_phy_write(dev, 0x048A, - b43legacy_phy_read(dev, 0x048A) - & ~0x8000); - b43legacy_phy_write(dev, 0x0415, - (b43legacy_phy_read(dev, 0x0415) - & 0x8000) | 0x36D8); - b43legacy_phy_write(dev, 0x0416, - (b43legacy_phy_read(dev, 0x0416) - & 0x8000) | 0x36D8); - b43legacy_phy_write(dev, 0x0417, - (b43legacy_phy_read(dev, 0x0417) - & 0xFE00) | 0x016D); - } else { - b43legacy_phy_write(dev, 0x048A, - b43legacy_phy_read(dev, 0x048A) - | 0x1000); - b43legacy_phy_write(dev, 0x048A, - (b43legacy_phy_read(dev, 0x048A) - & 0x9FFF) | 0x2000); - tmp32 = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET); - if (!(tmp32 & 0x800)) { - tmp32 |= 0x800; - b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET, - tmp32); - } - } - if (phy->rev >= 2) - b43legacy_phy_write(dev, 0x042B, - b43legacy_phy_read(dev, 0x042B) - | 0x0800); - b43legacy_phy_write(dev, 0x048C, - (b43legacy_phy_read(dev, 0x048C) - & 0xF0FF) | 0x0200); - if (phy->rev == 2) { - b43legacy_phy_write(dev, 0x04AE, - (b43legacy_phy_read(dev, 0x04AE) - & 0xFF00) | 0x007F); - b43legacy_phy_write(dev, 0x04AD, - (b43legacy_phy_read(dev, 0x04AD) - & 0x00FF) | 0x1300); - } else if (phy->rev >= 6) { - b43legacy_ilt_write(dev, 0x1A00 + 0x3, 0x007F); - b43legacy_ilt_write(dev, 0x1A00 + 0x2, 0x007F); - b43legacy_phy_write(dev, 0x04AD, - b43legacy_phy_read(dev, 0x04AD) - & 0x00FF); - } - b43legacy_calc_nrssi_slope(dev); - break; - default: - B43legacy_BUG_ON(1); - } -} - -static void -b43legacy_radio_interference_mitigation_disable(struct b43legacy_wldev *dev, - int mode) -{ - struct b43legacy_phy *phy = &dev->phy; - u32 tmp32; - u32 *stack = phy->interfstack; - - switch (mode) { - case B43legacy_RADIO_INTERFMODE_NONWLAN: - if (phy->rev != 1) { - b43legacy_phy_write(dev, 0x042B, - b43legacy_phy_read(dev, 0x042B) - & ~0x0800); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, - b43legacy_phy_read(dev, - B43legacy_PHY_G_CRS) | 0x4000); - break; - } - phy_stackrestore(0x0078); - b43legacy_calc_nrssi_threshold(dev); - phy_stackrestore(0x0406); - b43legacy_phy_write(dev, 0x042B, - b43legacy_phy_read(dev, 0x042B) & ~0x0800); - if (!dev->bad_frames_preempt) - b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, - b43legacy_phy_read(dev, - B43legacy_PHY_RADIO_BITFIELD) - & ~(1 << 11)); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, - b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) - | 0x4000); - phy_stackrestore(0x04A0); - phy_stackrestore(0x04A1); - phy_stackrestore(0x04A2); - phy_stackrestore(0x04A8); - phy_stackrestore(0x04AB); - phy_stackrestore(0x04A7); - phy_stackrestore(0x04A3); - phy_stackrestore(0x04A9); - phy_stackrestore(0x0493); - phy_stackrestore(0x04AA); - phy_stackrestore(0x04AC); - break; - case B43legacy_RADIO_INTERFMODE_MANUALWLAN: - if (!(b43legacy_phy_read(dev, 0x0033) & 0x0800)) - break; - - phy->aci_enable = false; - - phy_stackrestore(B43legacy_PHY_RADIO_BITFIELD); - phy_stackrestore(B43legacy_PHY_G_CRS); - phy_stackrestore(0x0033); - phy_stackrestore(0x04A3); - phy_stackrestore(0x04A9); - phy_stackrestore(0x0493); - phy_stackrestore(0x04AA); - phy_stackrestore(0x04AC); - phy_stackrestore(0x04A0); - phy_stackrestore(0x04A7); - if (phy->rev >= 2) { - phy_stackrestore(0x04C0); - phy_stackrestore(0x04C1); - } else - phy_stackrestore(0x0406); - phy_stackrestore(0x04A1); - phy_stackrestore(0x04AB); - phy_stackrestore(0x04A8); - if (phy->rev == 2) { - phy_stackrestore(0x04AD); - phy_stackrestore(0x04AE); - } else if (phy->rev >= 3) { - phy_stackrestore(0x04AD); - phy_stackrestore(0x0415); - phy_stackrestore(0x0416); - phy_stackrestore(0x0417); - ilt_stackrestore(0x1A00 + 0x2); - ilt_stackrestore(0x1A00 + 0x3); - } - phy_stackrestore(0x04A2); - phy_stackrestore(0x04A8); - phy_stackrestore(0x042B); - phy_stackrestore(0x048C); - tmp32 = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET); - if (tmp32 & 0x800) { - tmp32 &= ~0x800; - b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET, - tmp32); - } - b43legacy_calc_nrssi_slope(dev); - break; - default: - B43legacy_BUG_ON(1); - } -} - -#undef phy_stacksave -#undef phy_stackrestore -#undef radio_stacksave -#undef radio_stackrestore -#undef ilt_stacksave -#undef ilt_stackrestore - -int b43legacy_radio_set_interference_mitigation(struct b43legacy_wldev *dev, - int mode) -{ - struct b43legacy_phy *phy = &dev->phy; - int currentmode; - - if ((phy->type != B43legacy_PHYTYPE_G) || - (phy->rev == 0) || (!phy->gmode)) - return -ENODEV; - - phy->aci_wlan_automatic = false; - switch (mode) { - case B43legacy_RADIO_INTERFMODE_AUTOWLAN: - phy->aci_wlan_automatic = true; - if (phy->aci_enable) - mode = B43legacy_RADIO_INTERFMODE_MANUALWLAN; - else - mode = B43legacy_RADIO_INTERFMODE_NONE; - break; - case B43legacy_RADIO_INTERFMODE_NONE: - case B43legacy_RADIO_INTERFMODE_NONWLAN: - case B43legacy_RADIO_INTERFMODE_MANUALWLAN: - break; - default: - return -EINVAL; - } - - currentmode = phy->interfmode; - if (currentmode == mode) - return 0; - if (currentmode != B43legacy_RADIO_INTERFMODE_NONE) - b43legacy_radio_interference_mitigation_disable(dev, - currentmode); - - if (mode == B43legacy_RADIO_INTERFMODE_NONE) { - phy->aci_enable = false; - phy->aci_hw_rssi = false; - } else - b43legacy_radio_interference_mitigation_enable(dev, mode); - phy->interfmode = mode; - - return 0; -} - -u16 b43legacy_radio_calibrationvalue(struct b43legacy_wldev *dev) -{ - u16 reg; - u16 index; - u16 ret; - - reg = b43legacy_radio_read16(dev, 0x0060); - index = (reg & 0x001E) >> 1; - ret = rcc_table[index] << 1; - ret |= (reg & 0x0001); - ret |= 0x0020; - - return ret; -} - -#define LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0)) -static u16 b43legacy_get_812_value(struct b43legacy_wldev *dev, u8 lpd) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 loop_or = 0; - u16 adj_loopback_gain = phy->loopback_gain[0]; - u8 loop; - u16 extern_lna_control; - - if (!phy->gmode) - return 0; - if (!has_loopback_gain(phy)) { - if (phy->rev < 7 || !(dev->dev->bus->sprom.boardflags_lo - & B43legacy_BFL_EXTLNA)) { - switch (lpd) { - case LPD(0, 1, 1): - return 0x0FB2; - case LPD(0, 0, 1): - return 0x00B2; - case LPD(1, 0, 1): - return 0x30B2; - case LPD(1, 0, 0): - return 0x30B3; - default: - B43legacy_BUG_ON(1); - } - } else { - switch (lpd) { - case LPD(0, 1, 1): - return 0x8FB2; - case LPD(0, 0, 1): - return 0x80B2; - case LPD(1, 0, 1): - return 0x20B2; - case LPD(1, 0, 0): - return 0x20B3; - default: - B43legacy_BUG_ON(1); - } - } - } else { - if (phy->radio_rev == 8) - adj_loopback_gain += 0x003E; - else - adj_loopback_gain += 0x0026; - if (adj_loopback_gain >= 0x46) { - adj_loopback_gain -= 0x46; - extern_lna_control = 0x3000; - } else if (adj_loopback_gain >= 0x3A) { - adj_loopback_gain -= 0x3A; - extern_lna_control = 0x2000; - } else if (adj_loopback_gain >= 0x2E) { - adj_loopback_gain -= 0x2E; - extern_lna_control = 0x1000; - } else { - adj_loopback_gain -= 0x10; - extern_lna_control = 0x0000; - } - for (loop = 0; loop < 16; loop++) { - u16 tmp = adj_loopback_gain - 6 * loop; - if (tmp < 6) - break; - } - - loop_or = (loop << 8) | extern_lna_control; - if (phy->rev >= 7 && dev->dev->bus->sprom.boardflags_lo - & B43legacy_BFL_EXTLNA) { - if (extern_lna_control) - loop_or |= 0x8000; - switch (lpd) { - case LPD(0, 1, 1): - return 0x8F92; - case LPD(0, 0, 1): - return (0x8092 | loop_or); - case LPD(1, 0, 1): - return (0x2092 | loop_or); - case LPD(1, 0, 0): - return (0x2093 | loop_or); - default: - B43legacy_BUG_ON(1); - } - } else { - switch (lpd) { - case LPD(0, 1, 1): - return 0x0F92; - case LPD(0, 0, 1): - case LPD(1, 0, 1): - return (0x0092 | loop_or); - case LPD(1, 0, 0): - return (0x0093 | loop_or); - default: - B43legacy_BUG_ON(1); - } - } - } - return 0; -} - -u16 b43legacy_radio_init2050(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 backup[21] = { 0 }; - u16 ret; - u16 i; - u16 j; - u32 tmp1 = 0; - u32 tmp2 = 0; - - backup[0] = b43legacy_radio_read16(dev, 0x0043); - backup[14] = b43legacy_radio_read16(dev, 0x0051); - backup[15] = b43legacy_radio_read16(dev, 0x0052); - backup[1] = b43legacy_phy_read(dev, 0x0015); - backup[16] = b43legacy_phy_read(dev, 0x005A); - backup[17] = b43legacy_phy_read(dev, 0x0059); - backup[18] = b43legacy_phy_read(dev, 0x0058); - if (phy->type == B43legacy_PHYTYPE_B) { - backup[2] = b43legacy_phy_read(dev, 0x0030); - backup[3] = b43legacy_read16(dev, 0x03EC); - b43legacy_phy_write(dev, 0x0030, 0x00FF); - b43legacy_write16(dev, 0x03EC, 0x3F3F); - } else { - if (phy->gmode) { - backup[4] = b43legacy_phy_read(dev, 0x0811); - backup[5] = b43legacy_phy_read(dev, 0x0812); - backup[6] = b43legacy_phy_read(dev, 0x0814); - backup[7] = b43legacy_phy_read(dev, 0x0815); - backup[8] = b43legacy_phy_read(dev, - B43legacy_PHY_G_CRS); - backup[9] = b43legacy_phy_read(dev, 0x0802); - b43legacy_phy_write(dev, 0x0814, - (b43legacy_phy_read(dev, 0x0814) - | 0x0003)); - b43legacy_phy_write(dev, 0x0815, - (b43legacy_phy_read(dev, 0x0815) - & 0xFFFC)); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, - (b43legacy_phy_read(dev, - B43legacy_PHY_G_CRS) & 0x7FFF)); - b43legacy_phy_write(dev, 0x0802, - (b43legacy_phy_read(dev, 0x0802) - & 0xFFFC)); - if (phy->rev > 1) { /* loopback gain enabled */ - backup[19] = b43legacy_phy_read(dev, 0x080F); - backup[20] = b43legacy_phy_read(dev, 0x0810); - if (phy->rev >= 3) - b43legacy_phy_write(dev, 0x080F, - 0xC020); - else - b43legacy_phy_write(dev, 0x080F, - 0x8020); - b43legacy_phy_write(dev, 0x0810, 0x0000); - } - b43legacy_phy_write(dev, 0x0812, - b43legacy_get_812_value(dev, - LPD(0, 1, 1))); - if (phy->rev < 7 || - !(dev->dev->bus->sprom.boardflags_lo - & B43legacy_BFL_EXTLNA)) - b43legacy_phy_write(dev, 0x0811, 0x01B3); - else - b43legacy_phy_write(dev, 0x0811, 0x09B3); - } - } - b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO, - (b43legacy_read16(dev, B43legacy_MMIO_PHY_RADIO) - | 0x8000)); - backup[10] = b43legacy_phy_read(dev, 0x0035); - b43legacy_phy_write(dev, 0x0035, - (b43legacy_phy_read(dev, 0x0035) & 0xFF7F)); - backup[11] = b43legacy_read16(dev, 0x03E6); - backup[12] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); - - /* Initialization */ - if (phy->analog == 0) - b43legacy_write16(dev, 0x03E6, 0x0122); - else { - if (phy->analog >= 2) - b43legacy_phy_write(dev, 0x0003, - (b43legacy_phy_read(dev, 0x0003) - & 0xFFBF) | 0x0040); - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, - (b43legacy_read16(dev, - B43legacy_MMIO_CHANNEL_EXT) | 0x2000)); - } - - ret = b43legacy_radio_calibrationvalue(dev); - - if (phy->type == B43legacy_PHYTYPE_B) - b43legacy_radio_write16(dev, 0x0078, 0x0026); - - if (phy->gmode) - b43legacy_phy_write(dev, 0x0812, - b43legacy_get_812_value(dev, - LPD(0, 1, 1))); - b43legacy_phy_write(dev, 0x0015, 0xBFAF); - b43legacy_phy_write(dev, 0x002B, 0x1403); - if (phy->gmode) - b43legacy_phy_write(dev, 0x0812, - b43legacy_get_812_value(dev, - LPD(0, 0, 1))); - b43legacy_phy_write(dev, 0x0015, 0xBFA0); - b43legacy_radio_write16(dev, 0x0051, - (b43legacy_radio_read16(dev, 0x0051) - | 0x0004)); - if (phy->radio_rev == 8) - b43legacy_radio_write16(dev, 0x0043, 0x001F); - else { - b43legacy_radio_write16(dev, 0x0052, 0x0000); - b43legacy_radio_write16(dev, 0x0043, - (b43legacy_radio_read16(dev, 0x0043) - & 0xFFF0) | 0x0009); - } - b43legacy_phy_write(dev, 0x0058, 0x0000); - - for (i = 0; i < 16; i++) { - b43legacy_phy_write(dev, 0x005A, 0x0480); - b43legacy_phy_write(dev, 0x0059, 0xC810); - b43legacy_phy_write(dev, 0x0058, 0x000D); - if (phy->gmode) - b43legacy_phy_write(dev, 0x0812, - b43legacy_get_812_value(dev, - LPD(1, 0, 1))); - b43legacy_phy_write(dev, 0x0015, 0xAFB0); - udelay(10); - if (phy->gmode) - b43legacy_phy_write(dev, 0x0812, - b43legacy_get_812_value(dev, - LPD(1, 0, 1))); - b43legacy_phy_write(dev, 0x0015, 0xEFB0); - udelay(10); - if (phy->gmode) - b43legacy_phy_write(dev, 0x0812, - b43legacy_get_812_value(dev, - LPD(1, 0, 0))); - b43legacy_phy_write(dev, 0x0015, 0xFFF0); - udelay(20); - tmp1 += b43legacy_phy_read(dev, 0x002D); - b43legacy_phy_write(dev, 0x0058, 0x0000); - if (phy->gmode) - b43legacy_phy_write(dev, 0x0812, - b43legacy_get_812_value(dev, - LPD(1, 0, 1))); - b43legacy_phy_write(dev, 0x0015, 0xAFB0); - } - - tmp1++; - tmp1 >>= 9; - udelay(10); - b43legacy_phy_write(dev, 0x0058, 0x0000); - - for (i = 0; i < 16; i++) { - b43legacy_radio_write16(dev, 0x0078, (flip_4bit(i) << 1) - | 0x0020); - backup[13] = b43legacy_radio_read16(dev, 0x0078); - udelay(10); - for (j = 0; j < 16; j++) { - b43legacy_phy_write(dev, 0x005A, 0x0D80); - b43legacy_phy_write(dev, 0x0059, 0xC810); - b43legacy_phy_write(dev, 0x0058, 0x000D); - if (phy->gmode) - b43legacy_phy_write(dev, 0x0812, - b43legacy_get_812_value(dev, - LPD(1, 0, 1))); - b43legacy_phy_write(dev, 0x0015, 0xAFB0); - udelay(10); - if (phy->gmode) - b43legacy_phy_write(dev, 0x0812, - b43legacy_get_812_value(dev, - LPD(1, 0, 1))); - b43legacy_phy_write(dev, 0x0015, 0xEFB0); - udelay(10); - if (phy->gmode) - b43legacy_phy_write(dev, 0x0812, - b43legacy_get_812_value(dev, - LPD(1, 0, 0))); - b43legacy_phy_write(dev, 0x0015, 0xFFF0); - udelay(10); - tmp2 += b43legacy_phy_read(dev, 0x002D); - b43legacy_phy_write(dev, 0x0058, 0x0000); - if (phy->gmode) - b43legacy_phy_write(dev, 0x0812, - b43legacy_get_812_value(dev, - LPD(1, 0, 1))); - b43legacy_phy_write(dev, 0x0015, 0xAFB0); - } - tmp2++; - tmp2 >>= 8; - if (tmp1 < tmp2) - break; - } - - /* Restore the registers */ - b43legacy_phy_write(dev, 0x0015, backup[1]); - b43legacy_radio_write16(dev, 0x0051, backup[14]); - b43legacy_radio_write16(dev, 0x0052, backup[15]); - b43legacy_radio_write16(dev, 0x0043, backup[0]); - b43legacy_phy_write(dev, 0x005A, backup[16]); - b43legacy_phy_write(dev, 0x0059, backup[17]); - b43legacy_phy_write(dev, 0x0058, backup[18]); - b43legacy_write16(dev, 0x03E6, backup[11]); - if (phy->analog != 0) - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, backup[12]); - b43legacy_phy_write(dev, 0x0035, backup[10]); - b43legacy_radio_selectchannel(dev, phy->channel, 1); - if (phy->type == B43legacy_PHYTYPE_B) { - b43legacy_phy_write(dev, 0x0030, backup[2]); - b43legacy_write16(dev, 0x03EC, backup[3]); - } else { - if (phy->gmode) { - b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO, - (b43legacy_read16(dev, - B43legacy_MMIO_PHY_RADIO) & 0x7FFF)); - b43legacy_phy_write(dev, 0x0811, backup[4]); - b43legacy_phy_write(dev, 0x0812, backup[5]); - b43legacy_phy_write(dev, 0x0814, backup[6]); - b43legacy_phy_write(dev, 0x0815, backup[7]); - b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, - backup[8]); - b43legacy_phy_write(dev, 0x0802, backup[9]); - if (phy->rev > 1) { - b43legacy_phy_write(dev, 0x080F, backup[19]); - b43legacy_phy_write(dev, 0x0810, backup[20]); - } - } - } - if (i >= 15) - ret = backup[13]; - - return ret; -} - -static inline -u16 freq_r3A_value(u16 frequency) -{ - u16 value; - - if (frequency < 5091) - value = 0x0040; - else if (frequency < 5321) - value = 0x0000; - else if (frequency < 5806) - value = 0x0080; - else - value = 0x0040; - - return value; -} - -int b43legacy_radio_selectchannel(struct b43legacy_wldev *dev, - u8 channel, - int synthetic_pu_workaround) -{ - struct b43legacy_phy *phy = &dev->phy; - - if (channel == 0xFF) { - switch (phy->type) { - case B43legacy_PHYTYPE_B: - case B43legacy_PHYTYPE_G: - channel = B43legacy_RADIO_DEFAULT_CHANNEL_BG; - break; - default: - B43legacy_WARN_ON(1); - } - } - -/* TODO: Check if channel is valid - return -EINVAL if not */ - if (synthetic_pu_workaround) - b43legacy_synth_pu_workaround(dev, channel); - - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, - channel2freq_bg(channel)); - - if (channel == 14) { - if (dev->dev->bus->sprom.country_code == 5) /* JAPAN) */ - b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET, - b43legacy_shm_read32(dev, - B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET) - & ~(1 << 7)); - else - b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET, - b43legacy_shm_read32(dev, - B43legacy_SHM_SHARED, - B43legacy_UCODEFLAGS_OFFSET) - | (1 << 7)); - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, - b43legacy_read16(dev, - B43legacy_MMIO_CHANNEL_EXT) | (1 << 11)); - } else - b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, - b43legacy_read16(dev, - B43legacy_MMIO_CHANNEL_EXT) & 0xF7BF); - - phy->channel = channel; - /*XXX: Using the longer of 2 timeouts (8000 vs 2000 usecs). Specs states - * that 2000 usecs might suffice. */ - msleep(8); - - return 0; -} - -void b43legacy_radio_set_txantenna(struct b43legacy_wldev *dev, u32 val) -{ - u16 tmp; - - val <<= 8; - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0022) & 0xFCFF; - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0022, tmp | val); - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x03A8) & 0xFCFF; - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x03A8, tmp | val); - tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0054) & 0xFCFF; - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0054, tmp | val); -} - -/* http://bcm-specs.sipsolutions.net/TX_Gain_Base_Band */ -static u16 b43legacy_get_txgain_base_band(u16 txpower) -{ - u16 ret; - - B43legacy_WARN_ON(txpower > 63); - - if (txpower >= 54) - ret = 2; - else if (txpower >= 49) - ret = 4; - else if (txpower >= 44) - ret = 5; - else - ret = 6; - - return ret; -} - -/* http://bcm-specs.sipsolutions.net/TX_Gain_Radio_Frequency_Power_Amplifier */ -static u16 b43legacy_get_txgain_freq_power_amp(u16 txpower) -{ - u16 ret; - - B43legacy_WARN_ON(txpower > 63); - - if (txpower >= 32) - ret = 0; - else if (txpower >= 25) - ret = 1; - else if (txpower >= 20) - ret = 2; - else if (txpower >= 12) - ret = 3; - else - ret = 4; - - return ret; -} - -/* http://bcm-specs.sipsolutions.net/TX_Gain_Digital_Analog_Converter */ -static u16 b43legacy_get_txgain_dac(u16 txpower) -{ - u16 ret; - - B43legacy_WARN_ON(txpower > 63); - - if (txpower >= 54) - ret = txpower - 53; - else if (txpower >= 49) - ret = txpower - 42; - else if (txpower >= 44) - ret = txpower - 37; - else if (txpower >= 32) - ret = txpower - 32; - else if (txpower >= 25) - ret = txpower - 20; - else if (txpower >= 20) - ret = txpower - 13; - else if (txpower >= 12) - ret = txpower - 8; - else - ret = txpower; - - return ret; -} - -void b43legacy_radio_set_txpower_a(struct b43legacy_wldev *dev, u16 txpower) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 pamp; - u16 base; - u16 dac; - u16 ilt; - - txpower = clamp_val(txpower, 0, 63); - - pamp = b43legacy_get_txgain_freq_power_amp(txpower); - pamp <<= 5; - pamp &= 0x00E0; - b43legacy_phy_write(dev, 0x0019, pamp); - - base = b43legacy_get_txgain_base_band(txpower); - base &= 0x000F; - b43legacy_phy_write(dev, 0x0017, base | 0x0020); - - ilt = b43legacy_ilt_read(dev, 0x3001); - ilt &= 0x0007; - - dac = b43legacy_get_txgain_dac(txpower); - dac <<= 3; - dac |= ilt; - - b43legacy_ilt_write(dev, 0x3001, dac); - - phy->txpwr_offset = txpower; - - /* TODO: FuncPlaceholder (Adjust BB loft cancel) */ -} - -void b43legacy_radio_set_txpower_bg(struct b43legacy_wldev *dev, - u16 baseband_attenuation, - u16 radio_attenuation, - u16 txpower) -{ - struct b43legacy_phy *phy = &dev->phy; - - if (baseband_attenuation == 0xFFFF) - baseband_attenuation = phy->bbatt; - if (radio_attenuation == 0xFFFF) - radio_attenuation = phy->rfatt; - if (txpower == 0xFFFF) - txpower = phy->txctl1; - phy->bbatt = baseband_attenuation; - phy->rfatt = radio_attenuation; - phy->txctl1 = txpower; - - B43legacy_WARN_ON(baseband_attenuation > 11); - if (phy->radio_rev < 6) - B43legacy_WARN_ON(radio_attenuation > 9); - else - B43legacy_WARN_ON(radio_attenuation > 31); - B43legacy_WARN_ON(txpower > 7); - - b43legacy_phy_set_baseband_attenuation(dev, baseband_attenuation); - b43legacy_radio_write16(dev, 0x0043, radio_attenuation); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0064, - radio_attenuation); - if (phy->radio_ver == 0x2050) - b43legacy_radio_write16(dev, 0x0052, - (b43legacy_radio_read16(dev, 0x0052) - & ~0x0070) | ((txpower << 4) & 0x0070)); - /* FIXME: The spec is very weird and unclear here. */ - if (phy->type == B43legacy_PHYTYPE_G) - b43legacy_phy_lo_adjust(dev, 0); -} - -u16 b43legacy_default_baseband_attenuation(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - - if (phy->radio_ver == 0x2050 && phy->radio_rev < 6) - return 0; - return 2; -} - -u16 b43legacy_default_radio_attenuation(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - u16 att = 0xFFFF; - - switch (phy->radio_ver) { - case 0x2053: - switch (phy->radio_rev) { - case 1: - att = 6; - break; - } - break; - case 0x2050: - switch (phy->radio_rev) { - case 0: - att = 5; - break; - case 1: - if (phy->type == B43legacy_PHYTYPE_G) { - if (is_bcm_board_vendor(dev) && - dev->dev->bus->boardinfo.type == 0x421 && - dev->dev->bus->sprom.board_rev >= 30) - att = 3; - else if (is_bcm_board_vendor(dev) && - dev->dev->bus->boardinfo.type == 0x416) - att = 3; - else - att = 1; - } else { - if (is_bcm_board_vendor(dev) && - dev->dev->bus->boardinfo.type == 0x421 && - dev->dev->bus->sprom.board_rev >= 30) - att = 7; - else - att = 6; - } - break; - case 2: - if (phy->type == B43legacy_PHYTYPE_G) { - if (is_bcm_board_vendor(dev) && - dev->dev->bus->boardinfo.type == 0x421 && - dev->dev->bus->sprom.board_rev >= 30) - att = 3; - else if (is_bcm_board_vendor(dev) && - dev->dev->bus->boardinfo.type == - 0x416) - att = 5; - else if (dev->dev->bus->chip_id == 0x4320) - att = 4; - else - att = 3; - } else - att = 6; - break; - case 3: - att = 5; - break; - case 4: - case 5: - att = 1; - break; - case 6: - case 7: - att = 5; - break; - case 8: - att = 0x1A; - break; - case 9: - default: - att = 5; - } - } - if (is_bcm_board_vendor(dev) && - dev->dev->bus->boardinfo.type == 0x421) { - if (dev->dev->bus->sprom.board_rev < 0x43) - att = 2; - else if (dev->dev->bus->sprom.board_rev < 0x51) - att = 3; - } - if (att == 0xFFFF) - att = 5; - - return att; -} - -u16 b43legacy_default_txctl1(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - - if (phy->radio_ver != 0x2050) - return 0; - if (phy->radio_rev == 1) - return 3; - if (phy->radio_rev < 6) - return 2; - if (phy->radio_rev == 8) - return 1; - return 0; -} - -void b43legacy_radio_turn_on(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - int err; - u8 channel; - - might_sleep(); - - if (phy->radio_on) - return; - - switch (phy->type) { - case B43legacy_PHYTYPE_B: - case B43legacy_PHYTYPE_G: - b43legacy_phy_write(dev, 0x0015, 0x8000); - b43legacy_phy_write(dev, 0x0015, 0xCC00); - b43legacy_phy_write(dev, 0x0015, - (phy->gmode ? 0x00C0 : 0x0000)); - if (phy->radio_off_context.valid) { - /* Restore the RFover values. */ - b43legacy_phy_write(dev, B43legacy_PHY_RFOVER, - phy->radio_off_context.rfover); - b43legacy_phy_write(dev, B43legacy_PHY_RFOVERVAL, - phy->radio_off_context.rfoverval); - phy->radio_off_context.valid = false; - } - channel = phy->channel; - err = b43legacy_radio_selectchannel(dev, - B43legacy_RADIO_DEFAULT_CHANNEL_BG, 1); - err |= b43legacy_radio_selectchannel(dev, channel, 0); - B43legacy_WARN_ON(err); - break; - default: - B43legacy_BUG_ON(1); - } - phy->radio_on = true; -} - -void b43legacy_radio_turn_off(struct b43legacy_wldev *dev, bool force) -{ - struct b43legacy_phy *phy = &dev->phy; - - if (!phy->radio_on && !force) - return; - - if (phy->type == B43legacy_PHYTYPE_G && dev->dev->id.revision >= 5) { - u16 rfover, rfoverval; - - rfover = b43legacy_phy_read(dev, B43legacy_PHY_RFOVER); - rfoverval = b43legacy_phy_read(dev, B43legacy_PHY_RFOVERVAL); - if (!force) { - phy->radio_off_context.rfover = rfover; - phy->radio_off_context.rfoverval = rfoverval; - phy->radio_off_context.valid = true; - } - b43legacy_phy_write(dev, B43legacy_PHY_RFOVER, rfover | 0x008C); - b43legacy_phy_write(dev, B43legacy_PHY_RFOVERVAL, - rfoverval & 0xFF73); - } else - b43legacy_phy_write(dev, 0x0015, 0xAA00); - phy->radio_on = false; - b43legacydbg(dev->wl, "Radio initialized\n"); -} - -void b43legacy_radio_clear_tssi(struct b43legacy_wldev *dev) -{ - struct b43legacy_phy *phy = &dev->phy; - - switch (phy->type) { - case B43legacy_PHYTYPE_B: - case B43legacy_PHYTYPE_G: - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0058, - 0x7F7F); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x005a, - 0x7F7F); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0070, - 0x7F7F); - b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0072, - 0x7F7F); - break; - } -} diff --git a/drivers/net/wireless/b43legacy/radio.h b/drivers/net/wireless/b43legacy/radio.h deleted file mode 100644 index dd2976d1d561..000000000000 --- a/drivers/net/wireless/b43legacy/radio.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - - Broadcom B43legacy wireless driver - - Copyright (c) 2005 Martin Langer , - Stefano Brivio - Michael Buesch - Danny van Dyk - Andreas Jaggi - - Some parts of the code in this file are derived from the ipw2200 - driver Copyright(c) 2003 - 2004 Intel Corporation. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#ifndef B43legacy_RADIO_H_ -#define B43legacy_RADIO_H_ - -#include "b43legacy.h" - - -#define B43legacy_RADIO_DEFAULT_CHANNEL_BG 6 - -/* Force antenna 0. */ -#define B43legacy_RADIO_TXANTENNA_0 0 -/* Force antenna 1. */ -#define B43legacy_RADIO_TXANTENNA_1 1 -/* Use the RX antenna, that was selected for the most recently - * received good PLCP header. - */ -#define B43legacy_RADIO_TXANTENNA_LASTPLCP 3 -#define B43legacy_RADIO_TXANTENNA_DEFAULT B43legacy_RADIO_TXANTENNA_LASTPLCP - -#define B43legacy_RADIO_INTERFMODE_NONE 0 -#define B43legacy_RADIO_INTERFMODE_NONWLAN 1 -#define B43legacy_RADIO_INTERFMODE_MANUALWLAN 2 -#define B43legacy_RADIO_INTERFMODE_AUTOWLAN 3 - - -void b43legacy_radio_lock(struct b43legacy_wldev *dev); -void b43legacy_radio_unlock(struct b43legacy_wldev *dev); - -u16 b43legacy_radio_read16(struct b43legacy_wldev *dev, u16 offset); -void b43legacy_radio_write16(struct b43legacy_wldev *dev, u16 offset, u16 val); - -u16 b43legacy_radio_init2050(struct b43legacy_wldev *dev); - -void b43legacy_radio_turn_on(struct b43legacy_wldev *dev); -void b43legacy_radio_turn_off(struct b43legacy_wldev *dev, bool force); - -int b43legacy_radio_selectchannel(struct b43legacy_wldev *dev, u8 channel, - int synthetic_pu_workaround); - -void b43legacy_radio_set_txpower_a(struct b43legacy_wldev *dev, u16 txpower); -void b43legacy_radio_set_txpower_bg(struct b43legacy_wldev *dev, - u16 baseband_attenuation, u16 attenuation, - u16 txpower); - -u16 b43legacy_default_baseband_attenuation(struct b43legacy_wldev *dev); -u16 b43legacy_default_radio_attenuation(struct b43legacy_wldev *dev); -u16 b43legacy_default_txctl1(struct b43legacy_wldev *dev); - -void b43legacy_radio_set_txantenna(struct b43legacy_wldev *dev, u32 val); - -void b43legacy_radio_clear_tssi(struct b43legacy_wldev *dev); - -u8 b43legacy_radio_aci_detect(struct b43legacy_wldev *dev, u8 channel); -u8 b43legacy_radio_aci_scan(struct b43legacy_wldev *dev); - -int b43legacy_radio_set_interference_mitigation(struct b43legacy_wldev *dev, - int mode); - -void b43legacy_calc_nrssi_slope(struct b43legacy_wldev *dev); -void b43legacy_calc_nrssi_threshold(struct b43legacy_wldev *dev); -s16 b43legacy_nrssi_hw_read(struct b43legacy_wldev *dev, u16 offset); -void b43legacy_nrssi_hw_write(struct b43legacy_wldev *dev, u16 offset, s16 val); -void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val); -void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev); - -u16 b43legacy_radio_calibrationvalue(struct b43legacy_wldev *dev); - -#endif /* B43legacy_RADIO_H_ */ diff --git a/drivers/net/wireless/b43legacy/rfkill.c b/drivers/net/wireless/b43legacy/rfkill.c deleted file mode 100644 index 7c1bdbc02569..000000000000 --- a/drivers/net/wireless/b43legacy/rfkill.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - - Broadcom B43 wireless driver - RFKILL support - - Copyright (c) 2007 Michael Buesch - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "radio.h" -#include "b43legacy.h" - - -/* Returns TRUE, if the radio is enabled in hardware. */ -bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev) -{ - if (dev->dev->id.revision >= 3) { - if (!(b43legacy_read32(dev, B43legacy_MMIO_RADIO_HWENABLED_HI) - & B43legacy_MMIO_RADIO_HWENABLED_HI_MASK)) - return true; - } else { - /* To prevent CPU fault on PPC, do not read a register - * unless the interface is started; however, on resume - * for hibernation, this routine is entered early. When - * that happens, unconditionally return TRUE. - */ - if (b43legacy_status(dev) < B43legacy_STAT_STARTED) - return true; - if (b43legacy_read16(dev, B43legacy_MMIO_RADIO_HWENABLED_LO) - & B43legacy_MMIO_RADIO_HWENABLED_LO_MASK) - return true; - } - return false; -} - -/* The poll callback for the hardware button. */ -void b43legacy_rfkill_poll(struct ieee80211_hw *hw) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - struct b43legacy_wldev *dev = wl->current_dev; - struct ssb_bus *bus = dev->dev->bus; - bool enabled; - bool brought_up = false; - - mutex_lock(&wl->mutex); - if (unlikely(b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)) { - if (ssb_bus_powerup(bus, 0)) { - mutex_unlock(&wl->mutex); - return; - } - ssb_device_enable(dev->dev, 0); - brought_up = true; - } - - enabled = b43legacy_is_hw_radio_enabled(dev); - - if (unlikely(enabled != dev->radio_hw_enable)) { - dev->radio_hw_enable = enabled; - b43legacyinfo(wl, "Radio hardware status changed to %s\n", - enabled ? "ENABLED" : "DISABLED"); - wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); - if (enabled != dev->phy.radio_on) { - if (enabled) - b43legacy_radio_turn_on(dev); - else - b43legacy_radio_turn_off(dev, 0); - } - } - - if (brought_up) { - ssb_device_disable(dev->dev, 0); - ssb_bus_may_powerdown(bus); - } - - mutex_unlock(&wl->mutex); -} diff --git a/drivers/net/wireless/b43legacy/rfkill.h b/drivers/net/wireless/b43legacy/rfkill.h deleted file mode 100644 index 75585571c544..000000000000 --- a/drivers/net/wireless/b43legacy/rfkill.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef B43legacy_RFKILL_H_ -#define B43legacy_RFKILL_H_ - -struct ieee80211_hw; -struct b43legacy_wldev; - -void b43legacy_rfkill_poll(struct ieee80211_hw *hw); - -bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev); - -#endif /* B43legacy_RFKILL_H_ */ diff --git a/drivers/net/wireless/b43legacy/sysfs.c b/drivers/net/wireless/b43legacy/sysfs.c deleted file mode 100644 index 2a1da15c913b..000000000000 --- a/drivers/net/wireless/b43legacy/sysfs.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - - Broadcom B43legacy wireless driver - - SYSFS support routines - - Copyright (c) 2006 Michael Buesch - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include "sysfs.h" -#include "b43legacy.h" -#include "main.h" -#include "phy.h" -#include "radio.h" - -#include - - -#define GENERIC_FILESIZE 64 - - -static int get_integer(const char *buf, size_t count) -{ - char tmp[10 + 1] = { 0 }; - int ret = -EINVAL; - - if (count == 0) - goto out; - count = min_t(size_t, count, 10); - memcpy(tmp, buf, count); - ret = simple_strtol(tmp, NULL, 10); -out: - return ret; -} - -static int get_boolean(const char *buf, size_t count) -{ - if (count != 0) { - if (buf[0] == '1') - return 1; - if (buf[0] == '0') - return 0; - if (count >= 4 && memcmp(buf, "true", 4) == 0) - return 1; - if (count >= 5 && memcmp(buf, "false", 5) == 0) - return 0; - if (count >= 3 && memcmp(buf, "yes", 3) == 0) - return 1; - if (count >= 2 && memcmp(buf, "no", 2) == 0) - return 0; - if (count >= 2 && memcmp(buf, "on", 2) == 0) - return 1; - if (count >= 3 && memcmp(buf, "off", 3) == 0) - return 0; - } - return -EINVAL; -} - -static ssize_t b43legacy_attr_interfmode_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); - ssize_t count = 0; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - mutex_lock(&wldev->wl->mutex); - - switch (wldev->phy.interfmode) { - case B43legacy_INTERFMODE_NONE: - count = snprintf(buf, PAGE_SIZE, "0 (No Interference" - " Mitigation)\n"); - break; - case B43legacy_INTERFMODE_NONWLAN: - count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference" - " Mitigation)\n"); - break; - case B43legacy_INTERFMODE_MANUALWLAN: - count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference" - " Mitigation)\n"); - break; - default: - B43legacy_WARN_ON(1); - } - - mutex_unlock(&wldev->wl->mutex); - - return count; -} - -static ssize_t b43legacy_attr_interfmode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); - unsigned long flags; - int err; - int mode; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - mode = get_integer(buf, count); - switch (mode) { - case 0: - mode = B43legacy_INTERFMODE_NONE; - break; - case 1: - mode = B43legacy_INTERFMODE_NONWLAN; - break; - case 2: - mode = B43legacy_INTERFMODE_MANUALWLAN; - break; - case 3: - mode = B43legacy_INTERFMODE_AUTOWLAN; - break; - default: - return -EINVAL; - } - - mutex_lock(&wldev->wl->mutex); - spin_lock_irqsave(&wldev->wl->irq_lock, flags); - - err = b43legacy_radio_set_interference_mitigation(wldev, mode); - if (err) - b43legacyerr(wldev->wl, "Interference Mitigation not " - "supported by device\n"); - mmiowb(); - spin_unlock_irqrestore(&wldev->wl->irq_lock, flags); - mutex_unlock(&wldev->wl->mutex); - - return err ? err : count; -} - -static DEVICE_ATTR(interference, 0644, - b43legacy_attr_interfmode_show, - b43legacy_attr_interfmode_store); - -static ssize_t b43legacy_attr_preamble_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); - ssize_t count; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - mutex_lock(&wldev->wl->mutex); - - if (wldev->short_preamble) - count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble" - " enabled)\n"); - else - count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble" - " disabled)\n"); - - mutex_unlock(&wldev->wl->mutex); - - return count; -} - -static ssize_t b43legacy_attr_preamble_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); - unsigned long flags; - int value; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - value = get_boolean(buf, count); - if (value < 0) - return value; - mutex_lock(&wldev->wl->mutex); - spin_lock_irqsave(&wldev->wl->irq_lock, flags); - - wldev->short_preamble = !!value; - - spin_unlock_irqrestore(&wldev->wl->irq_lock, flags); - mutex_unlock(&wldev->wl->mutex); - - return count; -} - -static DEVICE_ATTR(shortpreamble, 0644, - b43legacy_attr_preamble_show, - b43legacy_attr_preamble_store); - -int b43legacy_sysfs_register(struct b43legacy_wldev *wldev) -{ - struct device *dev = wldev->dev->dev; - int err; - - B43legacy_WARN_ON(b43legacy_status(wldev) != - B43legacy_STAT_INITIALIZED); - - err = device_create_file(dev, &dev_attr_interference); - if (err) - goto out; - err = device_create_file(dev, &dev_attr_shortpreamble); - if (err) - goto err_remove_interfmode; - -out: - return err; -err_remove_interfmode: - device_remove_file(dev, &dev_attr_interference); - goto out; -} - -void b43legacy_sysfs_unregister(struct b43legacy_wldev *wldev) -{ - struct device *dev = wldev->dev->dev; - - device_remove_file(dev, &dev_attr_shortpreamble); - device_remove_file(dev, &dev_attr_interference); -} diff --git a/drivers/net/wireless/b43legacy/sysfs.h b/drivers/net/wireless/b43legacy/sysfs.h deleted file mode 100644 index 417d509803c7..000000000000 --- a/drivers/net/wireless/b43legacy/sysfs.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef B43legacy_SYSFS_H_ -#define B43legacy_SYSFS_H_ - -struct b43legacy_wldev; - -int b43legacy_sysfs_register(struct b43legacy_wldev *dev); -void b43legacy_sysfs_unregister(struct b43legacy_wldev *dev); - -#endif /* B43legacy_SYSFS_H_ */ diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c deleted file mode 100644 index 34bf3f0b729f..000000000000 --- a/drivers/net/wireless/b43legacy/xmit.c +++ /dev/null @@ -1,664 +0,0 @@ -/* - - Broadcom B43legacy wireless driver - - Transmission (TX/RX) related functions. - - Copyright (C) 2005 Martin Langer - Copyright (C) 2005 Stefano Brivio - Copyright (C) 2005, 2006 Michael Buesch - Copyright (C) 2005 Danny van Dyk - Copyright (C) 2005 Andreas Jaggi - Copyright (C) 2007 Larry Finger - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#include - -#include "xmit.h" -#include "phy.h" -#include "dma.h" -#include "pio.h" - - -/* Extract the bitrate out of a CCK PLCP header. */ -static u8 b43legacy_plcp_get_bitrate_idx_cck(struct b43legacy_plcp_hdr6 *plcp) -{ - switch (plcp->raw[0]) { - case 0x0A: - return 0; - case 0x14: - return 1; - case 0x37: - return 2; - case 0x6E: - return 3; - } - B43legacy_BUG_ON(1); - return -1; -} - -/* Extract the bitrate out of an OFDM PLCP header. */ -static u8 b43legacy_plcp_get_bitrate_idx_ofdm(struct b43legacy_plcp_hdr6 *plcp, - bool aphy) -{ - int base = aphy ? 0 : 4; - - switch (plcp->raw[0] & 0xF) { - case 0xB: - return base + 0; - case 0xF: - return base + 1; - case 0xA: - return base + 2; - case 0xE: - return base + 3; - case 0x9: - return base + 4; - case 0xD: - return base + 5; - case 0x8: - return base + 6; - case 0xC: - return base + 7; - } - B43legacy_BUG_ON(1); - return -1; -} - -u8 b43legacy_plcp_get_ratecode_cck(const u8 bitrate) -{ - switch (bitrate) { - case B43legacy_CCK_RATE_1MB: - return 0x0A; - case B43legacy_CCK_RATE_2MB: - return 0x14; - case B43legacy_CCK_RATE_5MB: - return 0x37; - case B43legacy_CCK_RATE_11MB: - return 0x6E; - } - B43legacy_BUG_ON(1); - return 0; -} - -u8 b43legacy_plcp_get_ratecode_ofdm(const u8 bitrate) -{ - switch (bitrate) { - case B43legacy_OFDM_RATE_6MB: - return 0xB; - case B43legacy_OFDM_RATE_9MB: - return 0xF; - case B43legacy_OFDM_RATE_12MB: - return 0xA; - case B43legacy_OFDM_RATE_18MB: - return 0xE; - case B43legacy_OFDM_RATE_24MB: - return 0x9; - case B43legacy_OFDM_RATE_36MB: - return 0xD; - case B43legacy_OFDM_RATE_48MB: - return 0x8; - case B43legacy_OFDM_RATE_54MB: - return 0xC; - } - B43legacy_BUG_ON(1); - return 0; -} - -void b43legacy_generate_plcp_hdr(struct b43legacy_plcp_hdr4 *plcp, - const u16 octets, const u8 bitrate) -{ - __le32 *data = &(plcp->data); - __u8 *raw = plcp->raw; - - if (b43legacy_is_ofdm_rate(bitrate)) { - u16 d; - - d = b43legacy_plcp_get_ratecode_ofdm(bitrate); - B43legacy_WARN_ON(octets & 0xF000); - d |= (octets << 5); - *data = cpu_to_le32(d); - } else { - u32 plen; - - plen = octets * 16 / bitrate; - if ((octets * 16 % bitrate) > 0) { - plen++; - if ((bitrate == B43legacy_CCK_RATE_11MB) - && ((octets * 8 % 11) < 4)) - raw[1] = 0x84; - else - raw[1] = 0x04; - } else - raw[1] = 0x04; - *data |= cpu_to_le32(plen << 16); - raw[0] = b43legacy_plcp_get_ratecode_cck(bitrate); - } -} - -static u8 b43legacy_calc_fallback_rate(u8 bitrate) -{ - switch (bitrate) { - case B43legacy_CCK_RATE_1MB: - return B43legacy_CCK_RATE_1MB; - case B43legacy_CCK_RATE_2MB: - return B43legacy_CCK_RATE_1MB; - case B43legacy_CCK_RATE_5MB: - return B43legacy_CCK_RATE_2MB; - case B43legacy_CCK_RATE_11MB: - return B43legacy_CCK_RATE_5MB; - case B43legacy_OFDM_RATE_6MB: - return B43legacy_CCK_RATE_5MB; - case B43legacy_OFDM_RATE_9MB: - return B43legacy_OFDM_RATE_6MB; - case B43legacy_OFDM_RATE_12MB: - return B43legacy_OFDM_RATE_9MB; - case B43legacy_OFDM_RATE_18MB: - return B43legacy_OFDM_RATE_12MB; - case B43legacy_OFDM_RATE_24MB: - return B43legacy_OFDM_RATE_18MB; - case B43legacy_OFDM_RATE_36MB: - return B43legacy_OFDM_RATE_24MB; - case B43legacy_OFDM_RATE_48MB: - return B43legacy_OFDM_RATE_36MB; - case B43legacy_OFDM_RATE_54MB: - return B43legacy_OFDM_RATE_48MB; - } - B43legacy_BUG_ON(1); - return 0; -} - -static int generate_txhdr_fw3(struct b43legacy_wldev *dev, - struct b43legacy_txhdr_fw3 *txhdr, - const unsigned char *fragment_data, - unsigned int fragment_len, - struct ieee80211_tx_info *info, - u16 cookie) -{ - const struct ieee80211_hdr *wlhdr; - int use_encryption = !!info->control.hw_key; - u8 rate; - struct ieee80211_rate *rate_fb; - int rate_ofdm; - int rate_fb_ofdm; - unsigned int plcp_fragment_len; - u32 mac_ctl = 0; - u16 phy_ctl = 0; - struct ieee80211_rate *tx_rate; - struct ieee80211_tx_rate *rates; - - wlhdr = (const struct ieee80211_hdr *)fragment_data; - - memset(txhdr, 0, sizeof(*txhdr)); - - tx_rate = ieee80211_get_tx_rate(dev->wl->hw, info); - - rate = tx_rate->hw_value; - rate_ofdm = b43legacy_is_ofdm_rate(rate); - rate_fb = ieee80211_get_alt_retry_rate(dev->wl->hw, info, 0) ? : tx_rate; - rate_fb_ofdm = b43legacy_is_ofdm_rate(rate_fb->hw_value); - - txhdr->mac_frame_ctl = wlhdr->frame_control; - memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN); - - /* Calculate duration for fallback rate */ - if ((rate_fb->hw_value == rate) || - (wlhdr->duration_id & cpu_to_le16(0x8000)) || - (wlhdr->duration_id == cpu_to_le16(0))) { - /* If the fallback rate equals the normal rate or the - * dur_id field contains an AID, CFP magic or 0, - * use the original dur_id field. */ - txhdr->dur_fb = wlhdr->duration_id; - } else { - txhdr->dur_fb = ieee80211_generic_frame_duration(dev->wl->hw, - info->control.vif, - info->band, - fragment_len, - rate_fb); - } - - plcp_fragment_len = fragment_len + FCS_LEN; - if (use_encryption) { - u8 key_idx = info->control.hw_key->hw_key_idx; - struct b43legacy_key *key; - int wlhdr_len; - size_t iv_len; - - B43legacy_WARN_ON(key_idx >= dev->max_nr_keys); - key = &(dev->key[key_idx]); - - if (key->enabled) { - /* Hardware appends ICV. */ - plcp_fragment_len += info->control.hw_key->icv_len; - - key_idx = b43legacy_kidx_to_fw(dev, key_idx); - mac_ctl |= (key_idx << B43legacy_TX4_MAC_KEYIDX_SHIFT) & - B43legacy_TX4_MAC_KEYIDX; - mac_ctl |= (key->algorithm << - B43legacy_TX4_MAC_KEYALG_SHIFT) & - B43legacy_TX4_MAC_KEYALG; - wlhdr_len = ieee80211_hdrlen(wlhdr->frame_control); - iv_len = min_t(size_t, info->control.hw_key->iv_len, - ARRAY_SIZE(txhdr->iv)); - memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len); - } else { - /* This key is invalid. This might only happen - * in a short timeframe after machine resume before - * we were able to reconfigure keys. - * Drop this packet completely. Do not transmit it - * unencrypted to avoid leaking information. */ - return -ENOKEY; - } - } - b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) - (&txhdr->plcp), plcp_fragment_len, - rate); - b43legacy_generate_plcp_hdr(&txhdr->plcp_fb, plcp_fragment_len, - rate_fb->hw_value); - - /* PHY TX Control word */ - if (rate_ofdm) - phy_ctl |= B43legacy_TX4_PHY_ENC_OFDM; - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) - phy_ctl |= B43legacy_TX4_PHY_SHORTPRMBL; - phy_ctl |= B43legacy_TX4_PHY_ANTLAST; - - /* MAC control */ - rates = info->control.rates; - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) - mac_ctl |= B43legacy_TX4_MAC_ACK; - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) - mac_ctl |= B43legacy_TX4_MAC_HWSEQ; - if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) - mac_ctl |= B43legacy_TX4_MAC_STMSDU; - if (rate_fb_ofdm) - mac_ctl |= B43legacy_TX4_MAC_FALLBACKOFDM; - - /* Overwrite rates[0].count to make the retry calculation - * in the tx status easier. need the actual retry limit to - * detect whether the fallback rate was used. - */ - if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || - (rates[0].count <= dev->wl->hw->conf.long_frame_max_tx_count)) { - rates[0].count = dev->wl->hw->conf.long_frame_max_tx_count; - mac_ctl |= B43legacy_TX4_MAC_LONGFRAME; - } else { - rates[0].count = dev->wl->hw->conf.short_frame_max_tx_count; - } - - /* Generate the RTS or CTS-to-self frame */ - if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || - (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) { - unsigned int len; - struct ieee80211_hdr *hdr; - int rts_rate; - int rts_rate_fb; - int rts_rate_fb_ofdm; - - rts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info)->hw_value; - rts_rate_fb = b43legacy_calc_fallback_rate(rts_rate); - rts_rate_fb_ofdm = b43legacy_is_ofdm_rate(rts_rate_fb); - if (rts_rate_fb_ofdm) - mac_ctl |= B43legacy_TX4_MAC_CTSFALLBACKOFDM; - - if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - ieee80211_ctstoself_get(dev->wl->hw, - info->control.vif, - fragment_data, - fragment_len, info, - (struct ieee80211_cts *) - (txhdr->rts_frame)); - mac_ctl |= B43legacy_TX4_MAC_SENDCTS; - len = sizeof(struct ieee80211_cts); - } else { - ieee80211_rts_get(dev->wl->hw, - info->control.vif, - fragment_data, fragment_len, info, - (struct ieee80211_rts *) - (txhdr->rts_frame)); - mac_ctl |= B43legacy_TX4_MAC_SENDRTS; - len = sizeof(struct ieee80211_rts); - } - len += FCS_LEN; - b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) - (&txhdr->rts_plcp), - len, rts_rate); - b43legacy_generate_plcp_hdr(&txhdr->rts_plcp_fb, - len, rts_rate_fb); - hdr = (struct ieee80211_hdr *)(&txhdr->rts_frame); - txhdr->rts_dur_fb = hdr->duration_id; - } - - /* Magic cookie */ - txhdr->cookie = cpu_to_le16(cookie); - - /* Apply the bitfields */ - txhdr->mac_ctl = cpu_to_le32(mac_ctl); - txhdr->phy_ctl = cpu_to_le16(phy_ctl); - - return 0; -} - -int b43legacy_generate_txhdr(struct b43legacy_wldev *dev, - u8 *txhdr, - const unsigned char *fragment_data, - unsigned int fragment_len, - struct ieee80211_tx_info *info, - u16 cookie) -{ - return generate_txhdr_fw3(dev, (struct b43legacy_txhdr_fw3 *)txhdr, - fragment_data, fragment_len, - info, cookie); -} - -static s8 b43legacy_rssi_postprocess(struct b43legacy_wldev *dev, - u8 in_rssi, int ofdm, - int adjust_2053, int adjust_2050) -{ - struct b43legacy_phy *phy = &dev->phy; - s32 tmp; - - switch (phy->radio_ver) { - case 0x2050: - if (ofdm) { - tmp = in_rssi; - if (tmp > 127) - tmp -= 256; - tmp *= 73; - tmp /= 64; - if (adjust_2050) - tmp += 25; - else - tmp -= 3; - } else { - if (dev->dev->bus->sprom.boardflags_lo - & B43legacy_BFL_RSSI) { - if (in_rssi > 63) - in_rssi = 63; - tmp = phy->nrssi_lt[in_rssi]; - tmp = 31 - tmp; - tmp *= -131; - tmp /= 128; - tmp -= 57; - } else { - tmp = in_rssi; - tmp = 31 - tmp; - tmp *= -149; - tmp /= 128; - tmp -= 68; - } - if (phy->type == B43legacy_PHYTYPE_G && - adjust_2050) - tmp += 25; - } - break; - case 0x2060: - if (in_rssi > 127) - tmp = in_rssi - 256; - else - tmp = in_rssi; - break; - default: - tmp = in_rssi; - tmp -= 11; - tmp *= 103; - tmp /= 64; - if (adjust_2053) - tmp -= 109; - else - tmp -= 83; - } - - return (s8)tmp; -} - -void b43legacy_rx(struct b43legacy_wldev *dev, - struct sk_buff *skb, - const void *_rxhdr) -{ - struct ieee80211_rx_status status; - struct b43legacy_plcp_hdr6 *plcp; - struct ieee80211_hdr *wlhdr; - const struct b43legacy_rxhdr_fw3 *rxhdr = _rxhdr; - __le16 fctl; - u16 phystat0; - u16 phystat3; - u16 chanstat; - u16 mactime; - u32 macstat; - u16 chanid; - u8 jssi; - int padding; - - memset(&status, 0, sizeof(status)); - - /* Get metadata about the frame from the header. */ - phystat0 = le16_to_cpu(rxhdr->phy_status0); - phystat3 = le16_to_cpu(rxhdr->phy_status3); - jssi = rxhdr->jssi; - macstat = le16_to_cpu(rxhdr->mac_status); - mactime = le16_to_cpu(rxhdr->mac_time); - chanstat = le16_to_cpu(rxhdr->channel); - - if (macstat & B43legacy_RX_MAC_FCSERR) - dev->wl->ieee_stats.dot11FCSErrorCount++; - - /* Skip PLCP and padding */ - padding = (macstat & B43legacy_RX_MAC_PADDING) ? 2 : 0; - if (unlikely(skb->len < (sizeof(struct b43legacy_plcp_hdr6) + - padding))) { - b43legacydbg(dev->wl, "RX: Packet size underrun (1)\n"); - goto drop; - } - plcp = (struct b43legacy_plcp_hdr6 *)(skb->data + padding); - skb_pull(skb, sizeof(struct b43legacy_plcp_hdr6) + padding); - /* The skb contains the Wireless Header + payload data now */ - if (unlikely(skb->len < (2+2+6/*minimum hdr*/ + FCS_LEN))) { - b43legacydbg(dev->wl, "RX: Packet size underrun (2)\n"); - goto drop; - } - wlhdr = (struct ieee80211_hdr *)(skb->data); - fctl = wlhdr->frame_control; - - if ((macstat & B43legacy_RX_MAC_DEC) && - !(macstat & B43legacy_RX_MAC_DECERR)) { - unsigned int keyidx; - int wlhdr_len; - int iv_len; - int icv_len; - - keyidx = ((macstat & B43legacy_RX_MAC_KEYIDX) - >> B43legacy_RX_MAC_KEYIDX_SHIFT); - /* We must adjust the key index here. We want the "physical" - * key index, but the ucode passed it slightly different. - */ - keyidx = b43legacy_kidx_to_raw(dev, keyidx); - B43legacy_WARN_ON(keyidx >= dev->max_nr_keys); - - if (dev->key[keyidx].algorithm != B43legacy_SEC_ALGO_NONE) { - /* Remove PROTECTED flag to mark it as decrypted. */ - B43legacy_WARN_ON(!ieee80211_has_protected(fctl)); - fctl &= ~cpu_to_le16(IEEE80211_FCTL_PROTECTED); - wlhdr->frame_control = fctl; - - wlhdr_len = ieee80211_hdrlen(fctl); - if (unlikely(skb->len < (wlhdr_len + 3))) { - b43legacydbg(dev->wl, "RX: Packet size" - " underrun3\n"); - goto drop; - } - if (skb->data[wlhdr_len + 3] & (1 << 5)) { - /* The Ext-IV Bit is set in the "KeyID" - * octet of the IV. - */ - iv_len = 8; - icv_len = 8; - } else { - iv_len = 4; - icv_len = 4; - } - if (unlikely(skb->len < (wlhdr_len + iv_len + - icv_len))) { - b43legacydbg(dev->wl, "RX: Packet size" - " underrun4\n"); - goto drop; - } - /* Remove the IV */ - memmove(skb->data + iv_len, skb->data, wlhdr_len); - skb_pull(skb, iv_len); - /* Remove the ICV */ - skb_trim(skb, skb->len - icv_len); - - status.flag |= RX_FLAG_DECRYPTED; - } - } - - status.signal = b43legacy_rssi_postprocess(dev, jssi, - (phystat0 & B43legacy_RX_PHYST0_OFDM), - (phystat0 & B43legacy_RX_PHYST0_GAINCTL), - (phystat3 & B43legacy_RX_PHYST3_TRSTATE)); - /* change to support A PHY */ - if (phystat0 & B43legacy_RX_PHYST0_OFDM) - status.rate_idx = b43legacy_plcp_get_bitrate_idx_ofdm(plcp, false); - else - status.rate_idx = b43legacy_plcp_get_bitrate_idx_cck(plcp); - status.antenna = !!(phystat0 & B43legacy_RX_PHYST0_ANT); - - /* - * All frames on monitor interfaces and beacons always need a full - * 64-bit timestamp. Monitor interfaces need it for diagnostic - * purposes and beacons for IBSS merging. - * This code assumes we get to process the packet within 16 bits - * of timestamp, i.e. about 65 milliseconds after the PHY received - * the first symbol. - */ - if (ieee80211_is_beacon(fctl) || dev->wl->radiotap_enabled) { - u16 low_mactime_now; - - b43legacy_tsf_read(dev, &status.mactime); - low_mactime_now = status.mactime; - status.mactime = status.mactime & ~0xFFFFULL; - status.mactime += mactime; - if (low_mactime_now <= mactime) - status.mactime -= 0x10000; - status.flag |= RX_FLAG_MACTIME_START; - } - - chanid = (chanstat & B43legacy_RX_CHAN_ID) >> - B43legacy_RX_CHAN_ID_SHIFT; - switch (chanstat & B43legacy_RX_CHAN_PHYTYPE) { - case B43legacy_PHYTYPE_B: - case B43legacy_PHYTYPE_G: - status.band = IEEE80211_BAND_2GHZ; - status.freq = chanid + 2400; - break; - default: - b43legacywarn(dev->wl, "Unexpected value for chanstat (0x%X)\n", - chanstat); - } - - memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx_irqsafe(dev->wl->hw, skb); - - return; -drop: - b43legacydbg(dev->wl, "RX: Packet dropped\n"); - dev_kfree_skb_any(skb); -} - -void b43legacy_handle_txstatus(struct b43legacy_wldev *dev, - const struct b43legacy_txstatus *status) -{ - b43legacy_debugfs_log_txstat(dev, status); - - if (status->intermediate) - return; - if (status->for_ampdu) - return; - if (!status->acked) - dev->wl->ieee_stats.dot11ACKFailureCount++; - if (status->rts_count) { - if (status->rts_count == 0xF) /* FIXME */ - dev->wl->ieee_stats.dot11RTSFailureCount++; - else - dev->wl->ieee_stats.dot11RTSSuccessCount++; - } - - if (b43legacy_using_pio(dev)) - b43legacy_pio_handle_txstatus(dev, status); - else - b43legacy_dma_handle_txstatus(dev, status); -} - -/* Handle TX status report as received through DMA/PIO queues */ -void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev, - const struct b43legacy_hwtxstatus *hw) -{ - struct b43legacy_txstatus status; - u8 tmp; - - status.cookie = le16_to_cpu(hw->cookie); - status.seq = le16_to_cpu(hw->seq); - status.phy_stat = hw->phy_stat; - tmp = hw->count; - status.frame_count = (tmp >> 4); - status.rts_count = (tmp & 0x0F); - tmp = hw->flags << 1; - status.supp_reason = ((tmp & 0x1C) >> 2); - status.pm_indicated = !!(tmp & 0x80); - status.intermediate = !!(tmp & 0x40); - status.for_ampdu = !!(tmp & 0x20); - status.acked = !!(tmp & 0x02); - - b43legacy_handle_txstatus(dev, &status); -} - -/* Stop any TX operation on the device (suspend the hardware queues) */ -void b43legacy_tx_suspend(struct b43legacy_wldev *dev) -{ - if (b43legacy_using_pio(dev)) - b43legacy_pio_freeze_txqueues(dev); - else - b43legacy_dma_tx_suspend(dev); -} - -/* Resume any TX operation on the device (resume the hardware queues) */ -void b43legacy_tx_resume(struct b43legacy_wldev *dev) -{ - if (b43legacy_using_pio(dev)) - b43legacy_pio_thaw_txqueues(dev); - else - b43legacy_dma_tx_resume(dev); -} - -/* Initialize the QoS parameters */ -void b43legacy_qos_init(struct b43legacy_wldev *dev) -{ - /* FIXME: This function must probably be called from the mac80211 - * config callback. */ -return; - - b43legacy_hf_write(dev, b43legacy_hf_read(dev) | B43legacy_HF_EDCF); - /* FIXME kill magic */ - b43legacy_write16(dev, 0x688, - b43legacy_read16(dev, 0x688) | 0x4); - - - /*TODO: We might need some stack support here to get the values. */ -} diff --git a/drivers/net/wireless/b43legacy/xmit.h b/drivers/net/wireless/b43legacy/xmit.h deleted file mode 100644 index 289db00a4a7b..000000000000 --- a/drivers/net/wireless/b43legacy/xmit.h +++ /dev/null @@ -1,261 +0,0 @@ -#ifndef B43legacy_XMIT_H_ -#define B43legacy_XMIT_H_ - -#include "main.h" - - -#define _b43legacy_declare_plcp_hdr(size) \ - struct b43legacy_plcp_hdr##size { \ - union { \ - __le32 data; \ - __u8 raw[size]; \ - } __packed; \ - } __packed - -/* struct b43legacy_plcp_hdr4 */ -_b43legacy_declare_plcp_hdr(4); -/* struct b43legacy_plcp_hdr6 */ -_b43legacy_declare_plcp_hdr(6); - -#undef _b43legacy_declare_plcp_hdr - - -/* TX header for v3 firmware */ -struct b43legacy_txhdr_fw3 { - __le32 mac_ctl; /* MAC TX control */ - __le16 mac_frame_ctl; /* Copy of the FrameControl */ - __le16 tx_fes_time_norm; /* TX FES Time Normal */ - __le16 phy_ctl; /* PHY TX control */ - __u8 iv[16]; /* Encryption IV */ - __u8 tx_receiver[6]; /* TX Frame Receiver address */ - __le16 tx_fes_time_fb; /* TX FES Time Fallback */ - struct b43legacy_plcp_hdr4 rts_plcp_fb; /* RTS fallback PLCP */ - __le16 rts_dur_fb; /* RTS fallback duration */ - struct b43legacy_plcp_hdr4 plcp_fb; /* Fallback PLCP */ - __le16 dur_fb; /* Fallback duration */ - PAD_BYTES(2); - __le16 cookie; - __le16 unknown_scb_stuff; - struct b43legacy_plcp_hdr6 rts_plcp; /* RTS PLCP */ - __u8 rts_frame[18]; /* The RTS frame (if used) */ - struct b43legacy_plcp_hdr6 plcp; -} __packed; - -/* MAC TX control */ -#define B43legacy_TX4_MAC_KEYIDX 0x0FF00000 /* Security key index */ -#define B43legacy_TX4_MAC_KEYIDX_SHIFT 20 -#define B43legacy_TX4_MAC_KEYALG 0x00070000 /* Security key algorithm */ -#define B43legacy_TX4_MAC_KEYALG_SHIFT 16 -#define B43legacy_TX4_MAC_LIFETIME 0x00001000 -#define B43legacy_TX4_MAC_FRAMEBURST 0x00000800 -#define B43legacy_TX4_MAC_SENDCTS 0x00000400 -#define B43legacy_TX4_MAC_AMPDU 0x00000300 -#define B43legacy_TX4_MAC_AMPDU_SHIFT 8 -#define B43legacy_TX4_MAC_CTSFALLBACKOFDM 0x00000200 -#define B43legacy_TX4_MAC_FALLBACKOFDM 0x00000100 -#define B43legacy_TX4_MAC_5GHZ 0x00000080 -#define B43legacy_TX4_MAC_IGNPMQ 0x00000020 -#define B43legacy_TX4_MAC_HWSEQ 0x00000010 /* Use Hardware Seq No */ -#define B43legacy_TX4_MAC_STMSDU 0x00000008 /* Start MSDU */ -#define B43legacy_TX4_MAC_SENDRTS 0x00000004 -#define B43legacy_TX4_MAC_LONGFRAME 0x00000002 -#define B43legacy_TX4_MAC_ACK 0x00000001 - -/* Extra Frame Types */ -#define B43legacy_TX4_EFT_FBOFDM 0x0001 /* Data frame fb rate type */ -#define B43legacy_TX4_EFT_RTSOFDM 0x0004 /* RTS/CTS rate type */ -#define B43legacy_TX4_EFT_RTSFBOFDM 0x0010 /* RTS/CTS fallback rate type */ - -/* PHY TX control word */ -#define B43legacy_TX4_PHY_ENC 0x0003 /* Data frame encoding */ -#define B43legacy_TX4_PHY_ENC_CCK 0x0000 /* CCK */ -#define B43legacy_TX4_PHY_ENC_OFDM 0x0001 /* Data frame rate type */ -#define B43legacy_TX4_PHY_SHORTPRMBL 0x0010 /* Use short preamble */ -#define B43legacy_TX4_PHY_ANT 0x03C0 /* Antenna selection */ -#define B43legacy_TX4_PHY_ANT0 0x0000 /* Use antenna 0 */ -#define B43legacy_TX4_PHY_ANT1 0x0100 /* Use antenna 1 */ -#define B43legacy_TX4_PHY_ANTLAST 0x0300 /* Use last used antenna */ - - - -int b43legacy_generate_txhdr(struct b43legacy_wldev *dev, - u8 *txhdr, - const unsigned char *fragment_data, - unsigned int fragment_len, - struct ieee80211_tx_info *info, - u16 cookie); - - -/* Transmit Status */ -struct b43legacy_txstatus { - u16 cookie; /* The cookie from the txhdr */ - u16 seq; /* Sequence number */ - u8 phy_stat; /* PHY TX status */ - u8 frame_count; /* Frame transmit count */ - u8 rts_count; /* RTS transmit count */ - u8 supp_reason; /* Suppression reason */ - /* flags */ - u8 pm_indicated;/* PM mode indicated to AP */ - u8 intermediate;/* Intermediate status notification */ - u8 for_ampdu; /* Status is for an AMPDU (afterburner) */ - u8 acked; /* Wireless ACK received */ -}; - -/* txstatus supp_reason values */ -enum { - B43legacy_TXST_SUPP_NONE, /* Not suppressed */ - B43legacy_TXST_SUPP_PMQ, /* Suppressed due to PMQ entry */ - B43legacy_TXST_SUPP_FLUSH, /* Suppressed due to flush request */ - B43legacy_TXST_SUPP_PREV, /* Previous fragment failed */ - B43legacy_TXST_SUPP_CHAN, /* Channel mismatch */ - B43legacy_TXST_SUPP_LIFE, /* Lifetime expired */ - B43legacy_TXST_SUPP_UNDER, /* Buffer underflow */ - B43legacy_TXST_SUPP_ABNACK, /* Afterburner NACK */ -}; - -/* Transmit Status as received through DMA/PIO on old chips */ -struct b43legacy_hwtxstatus { - PAD_BYTES(4); - __le16 cookie; - u8 flags; - u8 count; - PAD_BYTES(2); - __le16 seq; - u8 phy_stat; - PAD_BYTES(1); -} __packed; - - -/* Receive header for v3 firmware. */ -struct b43legacy_rxhdr_fw3 { - __le16 frame_len; /* Frame length */ - PAD_BYTES(2); - __le16 phy_status0; /* PHY RX Status 0 */ - __u8 jssi; /* PHY RX Status 1: JSSI */ - __u8 sig_qual; /* PHY RX Status 1: Signal Quality */ - PAD_BYTES(2); /* PHY RX Status 2 */ - __le16 phy_status3; /* PHY RX Status 3 */ - __le16 mac_status; /* MAC RX status */ - __le16 mac_time; - __le16 channel; -} __packed; - - -/* PHY RX Status 0 */ -#define B43legacy_RX_PHYST0_GAINCTL 0x4000 /* Gain Control */ -#define B43legacy_RX_PHYST0_PLCPHCF 0x0200 -#define B43legacy_RX_PHYST0_PLCPFV 0x0100 -#define B43legacy_RX_PHYST0_SHORTPRMBL 0x0080 /* Recvd with Short Preamble */ -#define B43legacy_RX_PHYST0_LCRS 0x0040 -#define B43legacy_RX_PHYST0_ANT 0x0020 /* Antenna */ -#define B43legacy_RX_PHYST0_UNSRATE 0x0010 -#define B43legacy_RX_PHYST0_CLIP 0x000C -#define B43legacy_RX_PHYST0_CLIP_SHIFT 2 -#define B43legacy_RX_PHYST0_FTYPE 0x0003 /* Frame type */ -#define B43legacy_RX_PHYST0_CCK 0x0000 /* Frame type: CCK */ -#define B43legacy_RX_PHYST0_OFDM 0x0001 /* Frame type: OFDM */ -#define B43legacy_RX_PHYST0_PRE_N 0x0002 /* Pre-standard N-PHY frame */ -#define B43legacy_RX_PHYST0_STD_N 0x0003 /* Standard N-PHY frame */ - -/* PHY RX Status 2 */ -#define B43legacy_RX_PHYST2_LNAG 0xC000 /* LNA Gain */ -#define B43legacy_RX_PHYST2_LNAG_SHIFT 14 -#define B43legacy_RX_PHYST2_PNAG 0x3C00 /* PNA Gain */ -#define B43legacy_RX_PHYST2_PNAG_SHIFT 10 -#define B43legacy_RX_PHYST2_FOFF 0x03FF /* F offset */ - -/* PHY RX Status 3 */ -#define B43legacy_RX_PHYST3_DIGG 0x1800 /* DIG Gain */ -#define B43legacy_RX_PHYST3_DIGG_SHIFT 11 -#define B43legacy_RX_PHYST3_TRSTATE 0x0400 /* TR state */ - -/* MAC RX Status */ -#define B43legacy_RX_MAC_BEACONSENT 0x00008000 /* Beacon send flag */ -#define B43legacy_RX_MAC_KEYIDX 0x000007E0 /* Key index */ -#define B43legacy_RX_MAC_KEYIDX_SHIFT 5 -#define B43legacy_RX_MAC_DECERR 0x00000010 /* Decrypt error */ -#define B43legacy_RX_MAC_DEC 0x00000008 /* Decryption attempted */ -#define B43legacy_RX_MAC_PADDING 0x00000004 /* Pad bytes present */ -#define B43legacy_RX_MAC_RESP 0x00000002 /* Response frame xmitted */ -#define B43legacy_RX_MAC_FCSERR 0x00000001 /* FCS error */ - -/* RX channel */ -#define B43legacy_RX_CHAN_GAIN 0xFC00 /* Gain */ -#define B43legacy_RX_CHAN_GAIN_SHIFT 10 -#define B43legacy_RX_CHAN_ID 0x03FC /* Channel ID */ -#define B43legacy_RX_CHAN_ID_SHIFT 2 -#define B43legacy_RX_CHAN_PHYTYPE 0x0003 /* PHY type */ - - - -u8 b43legacy_plcp_get_ratecode_cck(const u8 bitrate); -u8 b43legacy_plcp_get_ratecode_ofdm(const u8 bitrate); - -void b43legacy_generate_plcp_hdr(struct b43legacy_plcp_hdr4 *plcp, - const u16 octets, const u8 bitrate); - -void b43legacy_rx(struct b43legacy_wldev *dev, - struct sk_buff *skb, - const void *_rxhdr); - -void b43legacy_handle_txstatus(struct b43legacy_wldev *dev, - const struct b43legacy_txstatus *status); - -void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev, - const struct b43legacy_hwtxstatus *hw); - -void b43legacy_tx_suspend(struct b43legacy_wldev *dev); -void b43legacy_tx_resume(struct b43legacy_wldev *dev); - - -#define B43legacy_NR_QOSPARMS 22 -enum { - B43legacy_QOSPARM_TXOP = 0, - B43legacy_QOSPARM_CWMIN, - B43legacy_QOSPARM_CWMAX, - B43legacy_QOSPARM_CWCUR, - B43legacy_QOSPARM_AIFS, - B43legacy_QOSPARM_BSLOTS, - B43legacy_QOSPARM_REGGAP, - B43legacy_QOSPARM_STATUS, -}; - -void b43legacy_qos_init(struct b43legacy_wldev *dev); - - -/* Helper functions for converting the key-table index from "firmware-format" - * to "raw-format" and back. The firmware API changed for this at some revision. - * We need to account for that here. */ -static inline -int b43legacy_new_kidx_api(struct b43legacy_wldev *dev) -{ - /* FIXME: Not sure the change was at rev 351 */ - return (dev->fw.rev >= 351); -} -static inline -u8 b43legacy_kidx_to_fw(struct b43legacy_wldev *dev, u8 raw_kidx) -{ - u8 firmware_kidx; - if (b43legacy_new_kidx_api(dev)) - firmware_kidx = raw_kidx; - else { - if (raw_kidx >= 4) /* Is per STA key? */ - firmware_kidx = raw_kidx - 4; - else - firmware_kidx = raw_kidx; /* TX default key */ - } - return firmware_kidx; -} -static inline -u8 b43legacy_kidx_to_raw(struct b43legacy_wldev *dev, u8 firmware_kidx) -{ - u8 raw_kidx; - if (b43legacy_new_kidx_api(dev)) - raw_kidx = firmware_kidx; - else - /* RX default keys or per STA keys */ - raw_kidx = firmware_kidx + 4; - return raw_kidx; -} - -#endif /* B43legacy_XMIT_H_ */ diff --git a/drivers/net/wireless/broadcom/Kconfig b/drivers/net/wireless/broadcom/Kconfig index 0ba8119d2cf2..323e738dd96f 100644 --- a/drivers/net/wireless/broadcom/Kconfig +++ b/drivers/net/wireless/broadcom/Kconfig @@ -12,5 +12,6 @@ config WLAN_VENDOR_BROADCOM if WLAN_VENDOR_BROADCOM source "drivers/net/wireless/broadcom/b43/Kconfig" +source "drivers/net/wireless/broadcom/b43legacy/Kconfig" endif # WLAN_VENDOR_BROADCOM diff --git a/drivers/net/wireless/broadcom/Makefile b/drivers/net/wireless/broadcom/Makefile index 0140a810107c..66d7cc461fcf 100644 --- a/drivers/net/wireless/broadcom/Makefile +++ b/drivers/net/wireless/broadcom/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_B43) += b43/ +obj-$(CONFIG_B43LEGACY) += b43legacy/ diff --git a/drivers/net/wireless/broadcom/b43legacy/Kconfig b/drivers/net/wireless/broadcom/b43legacy/Kconfig new file mode 100644 index 000000000000..1ffa28835c58 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/Kconfig @@ -0,0 +1,104 @@ +config B43LEGACY + tristate "Broadcom 43xx-legacy wireless support (mac80211 stack)" + depends on SSB_POSSIBLE && MAC80211 && HAS_DMA + select SSB + select FW_LOADER + ---help--- + b43legacy is a driver for 802.11b devices from Broadcom (BCM4301 and + BCM4303) and early model 802.11g chips (BCM4306 Ver. 2) used in the + Linksys WPC54G V1 PCMCIA devices. + + Newer 802.11g and 802.11a devices need b43. + + It is safe to include both b43 and b43legacy as the underlying glue + layer will automatically load the correct version for your device. + + This driver uses V3 firmware, which must be installed separately using + b43-fwcutter. + + This driver can be built as a module (recommended) that will be + called "b43legacy". If unsure, say M. + +# Auto-select SSB PCI-HOST support, if possible +config B43LEGACY_PCI_AUTOSELECT + bool + depends on B43LEGACY && SSB_PCIHOST_POSSIBLE + select SSB_PCIHOST + select SSB_B43_PCI_BRIDGE + default y + +# Auto-select SSB PCICORE driver, if possible +config B43LEGACY_PCICORE_AUTOSELECT + bool + depends on B43LEGACY && SSB_DRIVER_PCICORE_POSSIBLE + select SSB_DRIVER_PCICORE + default y + +# LED support +# This config option automatically enables b43legacy LEDS support, +# if it's possible. +config B43LEGACY_LEDS + bool + depends on B43LEGACY && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = B43LEGACY) + default y + +# This config option automatically enables b43 HW-RNG support, +# if the HW-RNG core is enabled. +config B43LEGACY_HWRNG + bool + depends on B43LEGACY && (HW_RANDOM = y || HW_RANDOM = B43LEGACY) + default y + +config B43LEGACY_DEBUG + bool "Broadcom 43xx-legacy debugging" + depends on B43LEGACY + default y + ---help--- + Say Y, because this information will help you get the driver running. + This option generates a minimum of log output. + +config B43LEGACY_DMA + bool + depends on B43LEGACY + +config B43LEGACY_PIO + bool + depends on B43LEGACY + +choice + prompt "Broadcom 43xx-legacy data transfer mode" + depends on B43LEGACY + default B43LEGACY_DMA_AND_PIO_MODE + +config B43LEGACY_DMA_AND_PIO_MODE + bool "DMA + PIO" + select B43LEGACY_DMA + select B43LEGACY_PIO + ---help--- + Include both, Direct Memory Access (DMA) and Programmed I/O (PIO) + data transfer modes. The mode actually used is selectable through + the module parameter "pio". With pio=0 as a module parameter, the + default DMA is used, otherwise PIO is used. + + If unsure, choose this option. + +config B43LEGACY_DMA_MODE + bool "DMA (Direct Memory Access) only" + select B43LEGACY_DMA + ---help--- + Only include Direct Memory Access (DMA). + This reduces the size of the driver module, by omitting the PIO code. + +config B43LEGACY_PIO_MODE + bool "PIO (Programmed I/O) only" + select B43LEGACY_PIO + ---help--- + Only include Programmed I/O (PIO). + This reduces the size of the driver module, by omitting the DMA code. + Please note that PIO transfers are slow (compared to DMA). + + Also note that not all devices of the b43legacy series support PIO. + + You should use PIO only if DMA does not work for you. + +endchoice diff --git a/drivers/net/wireless/broadcom/b43legacy/Makefile b/drivers/net/wireless/broadcom/b43legacy/Makefile new file mode 100644 index 000000000000..227a77e84362 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/Makefile @@ -0,0 +1,19 @@ +# b43legacy core +b43legacy-y += main.o +b43legacy-y += ilt.o +b43legacy-y += phy.o +b43legacy-y += radio.o +b43legacy-y += sysfs.o +b43legacy-y += xmit.o +# b43 RFKILL button support +b43legacy-y += rfkill.o +# b43legacy LED support +b43legacy-$(CONFIG_B43LEGACY_LEDS) += leds.o +# b43legacy debugging +b43legacy-$(CONFIG_B43LEGACY_DEBUG) += debugfs.o +# b43legacy DMA and PIO +b43legacy-$(CONFIG_B43LEGACY_DMA) += dma.o +b43legacy-$(CONFIG_B43LEGACY_PIO) += pio.o + +obj-$(CONFIG_B43LEGACY) += b43legacy.o + diff --git a/drivers/net/wireless/broadcom/b43legacy/b43legacy.h b/drivers/net/wireless/broadcom/b43legacy/b43legacy.h new file mode 100644 index 000000000000..482476fdb1f3 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/b43legacy.h @@ -0,0 +1,858 @@ +#ifndef B43legacy_H_ +#define B43legacy_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "debugfs.h" +#include "leds.h" +#include "rfkill.h" +#include "phy.h" + + +#define B43legacy_IRQWAIT_MAX_RETRIES 20 + +/* MMIO offsets */ +#define B43legacy_MMIO_DMA0_REASON 0x20 +#define B43legacy_MMIO_DMA0_IRQ_MASK 0x24 +#define B43legacy_MMIO_DMA1_REASON 0x28 +#define B43legacy_MMIO_DMA1_IRQ_MASK 0x2C +#define B43legacy_MMIO_DMA2_REASON 0x30 +#define B43legacy_MMIO_DMA2_IRQ_MASK 0x34 +#define B43legacy_MMIO_DMA3_REASON 0x38 +#define B43legacy_MMIO_DMA3_IRQ_MASK 0x3C +#define B43legacy_MMIO_DMA4_REASON 0x40 +#define B43legacy_MMIO_DMA4_IRQ_MASK 0x44 +#define B43legacy_MMIO_DMA5_REASON 0x48 +#define B43legacy_MMIO_DMA5_IRQ_MASK 0x4C +#define B43legacy_MMIO_MACCTL 0x120 /* MAC control */ +#define B43legacy_MMIO_MACCMD 0x124 /* MAC command */ +#define B43legacy_MMIO_GEN_IRQ_REASON 0x128 +#define B43legacy_MMIO_GEN_IRQ_MASK 0x12C +#define B43legacy_MMIO_RAM_CONTROL 0x130 +#define B43legacy_MMIO_RAM_DATA 0x134 +#define B43legacy_MMIO_PS_STATUS 0x140 +#define B43legacy_MMIO_RADIO_HWENABLED_HI 0x158 +#define B43legacy_MMIO_SHM_CONTROL 0x160 +#define B43legacy_MMIO_SHM_DATA 0x164 +#define B43legacy_MMIO_SHM_DATA_UNALIGNED 0x166 +#define B43legacy_MMIO_XMITSTAT_0 0x170 +#define B43legacy_MMIO_XMITSTAT_1 0x174 +#define B43legacy_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */ +#define B43legacy_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */ +#define B43legacy_MMIO_TSF_CFP_REP 0x188 +#define B43legacy_MMIO_TSF_CFP_START 0x18C +/* 32-bit DMA */ +#define B43legacy_MMIO_DMA32_BASE0 0x200 +#define B43legacy_MMIO_DMA32_BASE1 0x220 +#define B43legacy_MMIO_DMA32_BASE2 0x240 +#define B43legacy_MMIO_DMA32_BASE3 0x260 +#define B43legacy_MMIO_DMA32_BASE4 0x280 +#define B43legacy_MMIO_DMA32_BASE5 0x2A0 +/* 64-bit DMA */ +#define B43legacy_MMIO_DMA64_BASE0 0x200 +#define B43legacy_MMIO_DMA64_BASE1 0x240 +#define B43legacy_MMIO_DMA64_BASE2 0x280 +#define B43legacy_MMIO_DMA64_BASE3 0x2C0 +#define B43legacy_MMIO_DMA64_BASE4 0x300 +#define B43legacy_MMIO_DMA64_BASE5 0x340 +/* PIO */ +#define B43legacy_MMIO_PIO1_BASE 0x300 +#define B43legacy_MMIO_PIO2_BASE 0x310 +#define B43legacy_MMIO_PIO3_BASE 0x320 +#define B43legacy_MMIO_PIO4_BASE 0x330 + +#define B43legacy_MMIO_PHY_VER 0x3E0 +#define B43legacy_MMIO_PHY_RADIO 0x3E2 +#define B43legacy_MMIO_PHY0 0x3E6 +#define B43legacy_MMIO_ANTENNA 0x3E8 +#define B43legacy_MMIO_CHANNEL 0x3F0 +#define B43legacy_MMIO_CHANNEL_EXT 0x3F4 +#define B43legacy_MMIO_RADIO_CONTROL 0x3F6 +#define B43legacy_MMIO_RADIO_DATA_HIGH 0x3F8 +#define B43legacy_MMIO_RADIO_DATA_LOW 0x3FA +#define B43legacy_MMIO_PHY_CONTROL 0x3FC +#define B43legacy_MMIO_PHY_DATA 0x3FE +#define B43legacy_MMIO_MACFILTER_CONTROL 0x420 +#define B43legacy_MMIO_MACFILTER_DATA 0x422 +#define B43legacy_MMIO_RCMTA_COUNT 0x43C /* Receive Match Transmitter Addr */ +#define B43legacy_MMIO_RADIO_HWENABLED_LO 0x49A +#define B43legacy_MMIO_GPIO_CONTROL 0x49C +#define B43legacy_MMIO_GPIO_MASK 0x49E +#define B43legacy_MMIO_TSF_CFP_PRETBTT 0x612 +#define B43legacy_MMIO_TSF_0 0x632 /* core rev < 3 only */ +#define B43legacy_MMIO_TSF_1 0x634 /* core rev < 3 only */ +#define B43legacy_MMIO_TSF_2 0x636 /* core rev < 3 only */ +#define B43legacy_MMIO_TSF_3 0x638 /* core rev < 3 only */ +#define B43legacy_MMIO_RNG 0x65A +#define B43legacy_MMIO_POWERUP_DELAY 0x6A8 + +/* SPROM boardflags_lo values */ +#define B43legacy_BFL_PACTRL 0x0002 +#define B43legacy_BFL_RSSI 0x0008 +#define B43legacy_BFL_EXTLNA 0x1000 + +/* GPIO register offset, in both ChipCommon and PCI core. */ +#define B43legacy_GPIO_CONTROL 0x6c + +/* SHM Routing */ +#define B43legacy_SHM_SHARED 0x0001 +#define B43legacy_SHM_WIRELESS 0x0002 +#define B43legacy_SHM_HW 0x0004 +#define B43legacy_SHM_UCODE 0x0300 + +/* SHM Routing modifiers */ +#define B43legacy_SHM_AUTOINC_R 0x0200 /* Read Auto-increment */ +#define B43legacy_SHM_AUTOINC_W 0x0100 /* Write Auto-increment */ +#define B43legacy_SHM_AUTOINC_RW (B43legacy_SHM_AUTOINC_R | \ + B43legacy_SHM_AUTOINC_W) + +/* Misc SHM_SHARED offsets */ +#define B43legacy_SHM_SH_WLCOREREV 0x0016 /* 802.11 core revision */ +#define B43legacy_SHM_SH_HOSTFLO 0x005E /* Hostflags ucode opts (low) */ +#define B43legacy_SHM_SH_HOSTFHI 0x0060 /* Hostflags ucode opts (high) */ +/* SHM_SHARED crypto engine */ +#define B43legacy_SHM_SH_KEYIDXBLOCK 0x05D4 /* Key index/algorithm block */ +/* SHM_SHARED beacon/AP variables */ +#define B43legacy_SHM_SH_DTIMP 0x0012 /* DTIM period */ +#define B43legacy_SHM_SH_BTL0 0x0018 /* Beacon template length 0 */ +#define B43legacy_SHM_SH_BTL1 0x001A /* Beacon template length 1 */ +#define B43legacy_SHM_SH_BTSFOFF 0x001C /* Beacon TSF offset */ +#define B43legacy_SHM_SH_TIMPOS 0x001E /* TIM position in beacon */ +#define B43legacy_SHM_SH_BEACPHYCTL 0x0054 /* Beacon PHY TX control word */ +/* SHM_SHARED ACK/CTS control */ +#define B43legacy_SHM_SH_ACKCTSPHYCTL 0x0022 /* ACK/CTS PHY control word */ +/* SHM_SHARED probe response variables */ +#define B43legacy_SHM_SH_PRTLEN 0x004A /* Probe Response template length */ +#define B43legacy_SHM_SH_PRMAXTIME 0x0074 /* Probe Response max time */ +#define B43legacy_SHM_SH_PRPHYCTL 0x0188 /* Probe Resp PHY TX control */ +/* SHM_SHARED rate tables */ +#define B43legacy_SHM_SH_OFDMDIRECT 0x0480 /* Pointer to OFDM direct map */ +#define B43legacy_SHM_SH_OFDMBASIC 0x04A0 /* Pointer to OFDM basic rate map */ +#define B43legacy_SHM_SH_CCKDIRECT 0x04C0 /* Pointer to CCK direct map */ +#define B43legacy_SHM_SH_CCKBASIC 0x04E0 /* Pointer to CCK basic rate map */ +/* SHM_SHARED microcode soft registers */ +#define B43legacy_SHM_SH_UCODEREV 0x0000 /* Microcode revision */ +#define B43legacy_SHM_SH_UCODEPATCH 0x0002 /* Microcode patchlevel */ +#define B43legacy_SHM_SH_UCODEDATE 0x0004 /* Microcode date */ +#define B43legacy_SHM_SH_UCODETIME 0x0006 /* Microcode time */ +#define B43legacy_SHM_SH_SPUWKUP 0x0094 /* pre-wakeup for synth PU in us */ +#define B43legacy_SHM_SH_PRETBTT 0x0096 /* pre-TBTT in us */ + +#define B43legacy_UCODEFLAGS_OFFSET 0x005E + +/* Hardware Radio Enable masks */ +#define B43legacy_MMIO_RADIO_HWENABLED_HI_MASK (1 << 16) +#define B43legacy_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4) + +/* HostFlags. See b43legacy_hf_read/write() */ +#define B43legacy_HF_SYMW 0x00000002 /* G-PHY SYM workaround */ +#define B43legacy_HF_GDCW 0x00000020 /* G-PHY DV cancel filter */ +#define B43legacy_HF_OFDMPABOOST 0x00000040 /* Enable PA boost OFDM */ +#define B43legacy_HF_EDCF 0x00000100 /* on if WME/MAC suspended */ + +/* MacFilter offsets. */ +#define B43legacy_MACFILTER_SELF 0x0000 +#define B43legacy_MACFILTER_BSSID 0x0003 +#define B43legacy_MACFILTER_MAC 0x0010 + +/* PHYVersioning */ +#define B43legacy_PHYTYPE_B 0x01 +#define B43legacy_PHYTYPE_G 0x02 + +/* PHYRegisters */ +#define B43legacy_PHY_G_LO_CONTROL 0x0810 +#define B43legacy_PHY_ILT_G_CTRL 0x0472 +#define B43legacy_PHY_ILT_G_DATA1 0x0473 +#define B43legacy_PHY_ILT_G_DATA2 0x0474 +#define B43legacy_PHY_G_PCTL 0x0029 +#define B43legacy_PHY_RADIO_BITFIELD 0x0401 +#define B43legacy_PHY_G_CRS 0x0429 +#define B43legacy_PHY_NRSSILT_CTRL 0x0803 +#define B43legacy_PHY_NRSSILT_DATA 0x0804 + +/* RadioRegisters */ +#define B43legacy_RADIOCTL_ID 0x01 + +/* MAC Control bitfield */ +#define B43legacy_MACCTL_ENABLED 0x00000001 /* MAC Enabled */ +#define B43legacy_MACCTL_PSM_RUN 0x00000002 /* Run Microcode */ +#define B43legacy_MACCTL_PSM_JMP0 0x00000004 /* Microcode jump to 0 */ +#define B43legacy_MACCTL_SHM_ENABLED 0x00000100 /* SHM Enabled */ +#define B43legacy_MACCTL_IHR_ENABLED 0x00000400 /* IHR Region Enabled */ +#define B43legacy_MACCTL_BE 0x00010000 /* Big Endian mode */ +#define B43legacy_MACCTL_INFRA 0x00020000 /* Infrastructure mode */ +#define B43legacy_MACCTL_AP 0x00040000 /* AccessPoint mode */ +#define B43legacy_MACCTL_RADIOLOCK 0x00080000 /* Radio lock */ +#define B43legacy_MACCTL_BEACPROMISC 0x00100000 /* Beacon Promiscuous */ +#define B43legacy_MACCTL_KEEP_BADPLCP 0x00200000 /* Keep bad PLCP frames */ +#define B43legacy_MACCTL_KEEP_CTL 0x00400000 /* Keep control frames */ +#define B43legacy_MACCTL_KEEP_BAD 0x00800000 /* Keep bad frames (FCS) */ +#define B43legacy_MACCTL_PROMISC 0x01000000 /* Promiscuous mode */ +#define B43legacy_MACCTL_HWPS 0x02000000 /* Hardware Power Saving */ +#define B43legacy_MACCTL_AWAKE 0x04000000 /* Device is awake */ +#define B43legacy_MACCTL_TBTTHOLD 0x10000000 /* TBTT Hold */ +#define B43legacy_MACCTL_GMODE 0x80000000 /* G Mode */ + +/* MAC Command bitfield */ +#define B43legacy_MACCMD_BEACON0_VALID 0x00000001 /* Beacon 0 in template RAM is busy/valid */ +#define B43legacy_MACCMD_BEACON1_VALID 0x00000002 /* Beacon 1 in template RAM is busy/valid */ +#define B43legacy_MACCMD_DFQ_VALID 0x00000004 /* Directed frame queue valid (IBSS PS mode, ATIM) */ +#define B43legacy_MACCMD_CCA 0x00000008 /* Clear channel assessment */ +#define B43legacy_MACCMD_BGNOISE 0x00000010 /* Background noise */ + +/* 802.11 core specific TM State Low flags */ +#define B43legacy_TMSLOW_GMODE 0x20000000 /* G Mode Enable */ +#define B43legacy_TMSLOW_PLLREFSEL 0x00200000 /* PLL Freq Ref Select */ +#define B43legacy_TMSLOW_MACPHYCLKEN 0x00100000 /* MAC PHY Clock Ctrl Enbl */ +#define B43legacy_TMSLOW_PHYRESET 0x00080000 /* PHY Reset */ +#define B43legacy_TMSLOW_PHYCLKEN 0x00040000 /* PHY Clock Enable */ + +/* 802.11 core specific TM State High flags */ +#define B43legacy_TMSHIGH_FCLOCK 0x00040000 /* Fast Clock Available */ +#define B43legacy_TMSHIGH_GPHY 0x00010000 /* G-PHY avail (rev >= 5) */ + +#define B43legacy_UCODEFLAG_AUTODIV 0x0001 + +/* Generic-Interrupt reasons. */ +#define B43legacy_IRQ_MAC_SUSPENDED 0x00000001 +#define B43legacy_IRQ_BEACON 0x00000002 +#define B43legacy_IRQ_TBTT_INDI 0x00000004 /* Target Beacon Transmit Time */ +#define B43legacy_IRQ_BEACON_TX_OK 0x00000008 +#define B43legacy_IRQ_BEACON_CANCEL 0x00000010 +#define B43legacy_IRQ_ATIM_END 0x00000020 +#define B43legacy_IRQ_PMQ 0x00000040 +#define B43legacy_IRQ_PIO_WORKAROUND 0x00000100 +#define B43legacy_IRQ_MAC_TXERR 0x00000200 +#define B43legacy_IRQ_PHY_TXERR 0x00000800 +#define B43legacy_IRQ_PMEVENT 0x00001000 +#define B43legacy_IRQ_TIMER0 0x00002000 +#define B43legacy_IRQ_TIMER1 0x00004000 +#define B43legacy_IRQ_DMA 0x00008000 +#define B43legacy_IRQ_TXFIFO_FLUSH_OK 0x00010000 +#define B43legacy_IRQ_CCA_MEASURE_OK 0x00020000 +#define B43legacy_IRQ_NOISESAMPLE_OK 0x00040000 +#define B43legacy_IRQ_UCODE_DEBUG 0x08000000 +#define B43legacy_IRQ_RFKILL 0x10000000 +#define B43legacy_IRQ_TX_OK 0x20000000 +#define B43legacy_IRQ_PHY_G_CHANGED 0x40000000 +#define B43legacy_IRQ_TIMEOUT 0x80000000 + +#define B43legacy_IRQ_ALL 0xFFFFFFFF +#define B43legacy_IRQ_MASKTEMPLATE (B43legacy_IRQ_MAC_SUSPENDED | \ + B43legacy_IRQ_TBTT_INDI | \ + B43legacy_IRQ_ATIM_END | \ + B43legacy_IRQ_PMQ | \ + B43legacy_IRQ_MAC_TXERR | \ + B43legacy_IRQ_PHY_TXERR | \ + B43legacy_IRQ_DMA | \ + B43legacy_IRQ_TXFIFO_FLUSH_OK | \ + B43legacy_IRQ_NOISESAMPLE_OK | \ + B43legacy_IRQ_UCODE_DEBUG | \ + B43legacy_IRQ_RFKILL | \ + B43legacy_IRQ_TX_OK) + +/* Device specific rate values. + * The actual values defined here are (rate_in_mbps * 2). + * Some code depends on this. Don't change it. */ +#define B43legacy_CCK_RATE_1MB 2 +#define B43legacy_CCK_RATE_2MB 4 +#define B43legacy_CCK_RATE_5MB 11 +#define B43legacy_CCK_RATE_11MB 22 +#define B43legacy_OFDM_RATE_6MB 12 +#define B43legacy_OFDM_RATE_9MB 18 +#define B43legacy_OFDM_RATE_12MB 24 +#define B43legacy_OFDM_RATE_18MB 36 +#define B43legacy_OFDM_RATE_24MB 48 +#define B43legacy_OFDM_RATE_36MB 72 +#define B43legacy_OFDM_RATE_48MB 96 +#define B43legacy_OFDM_RATE_54MB 108 +/* Convert a b43legacy rate value to a rate in 100kbps */ +#define B43legacy_RATE_TO_100KBPS(rate) (((rate) * 10) / 2) + + +#define B43legacy_DEFAULT_SHORT_RETRY_LIMIT 7 +#define B43legacy_DEFAULT_LONG_RETRY_LIMIT 4 + +#define B43legacy_PHY_TX_BADNESS_LIMIT 1000 + +/* Max size of a security key */ +#define B43legacy_SEC_KEYSIZE 16 +/* Security algorithms. */ +enum { + B43legacy_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */ + B43legacy_SEC_ALGO_WEP40, + B43legacy_SEC_ALGO_TKIP, + B43legacy_SEC_ALGO_AES, + B43legacy_SEC_ALGO_WEP104, + B43legacy_SEC_ALGO_AES_LEGACY, +}; + +/* Core Information Registers */ +#define B43legacy_CIR_BASE 0xf00 +#define B43legacy_CIR_SBTPSFLAG (B43legacy_CIR_BASE + 0x18) +#define B43legacy_CIR_SBIMSTATE (B43legacy_CIR_BASE + 0x90) +#define B43legacy_CIR_SBINTVEC (B43legacy_CIR_BASE + 0x94) +#define B43legacy_CIR_SBTMSTATELOW (B43legacy_CIR_BASE + 0x98) +#define B43legacy_CIR_SBTMSTATEHIGH (B43legacy_CIR_BASE + 0x9c) +#define B43legacy_CIR_SBIMCONFIGLOW (B43legacy_CIR_BASE + 0xa8) +#define B43legacy_CIR_SB_ID_HI (B43legacy_CIR_BASE + 0xfc) + +/* sbtmstatehigh state flags */ +#define B43legacy_SBTMSTATEHIGH_SERROR 0x00000001 +#define B43legacy_SBTMSTATEHIGH_BUSY 0x00000004 +#define B43legacy_SBTMSTATEHIGH_TIMEOUT 0x00000020 +#define B43legacy_SBTMSTATEHIGH_G_PHY_AVAIL 0x00010000 +#define B43legacy_SBTMSTATEHIGH_COREFLAGS 0x1FFF0000 +#define B43legacy_SBTMSTATEHIGH_DMA64BIT 0x10000000 +#define B43legacy_SBTMSTATEHIGH_GATEDCLK 0x20000000 +#define B43legacy_SBTMSTATEHIGH_BISTFAILED 0x40000000 +#define B43legacy_SBTMSTATEHIGH_BISTCOMPLETE 0x80000000 + +/* sbimstate flags */ +#define B43legacy_SBIMSTATE_IB_ERROR 0x20000 +#define B43legacy_SBIMSTATE_TIMEOUT 0x40000 + +#define PFX KBUILD_MODNAME ": " +#ifdef assert +# undef assert +#endif +#ifdef CONFIG_B43LEGACY_DEBUG +# define B43legacy_WARN_ON(x) WARN_ON(x) +# define B43legacy_BUG_ON(expr) \ + do { \ + if (unlikely((expr))) { \ + printk(KERN_INFO PFX "Test (%s) failed\n", \ + #expr); \ + BUG_ON(expr); \ + } \ + } while (0) +# define B43legacy_DEBUG 1 +#else +/* This will evaluate the argument even if debugging is disabled. */ +static inline bool __b43legacy_warn_on_dummy(bool x) { return x; } +# define B43legacy_WARN_ON(x) __b43legacy_warn_on_dummy(unlikely(!!(x))) +# define B43legacy_BUG_ON(x) do { /* nothing */ } while (0) +# define B43legacy_DEBUG 0 +#endif + + +struct net_device; +struct pci_dev; +struct b43legacy_dmaring; +struct b43legacy_pioqueue; + +/* The firmware file header */ +#define B43legacy_FW_TYPE_UCODE 'u' +#define B43legacy_FW_TYPE_PCM 'p' +#define B43legacy_FW_TYPE_IV 'i' +struct b43legacy_fw_header { + /* File type */ + u8 type; + /* File format version */ + u8 ver; + u8 __padding[2]; + /* Size of the data. For ucode and PCM this is in bytes. + * For IV this is number-of-ivs. */ + __be32 size; +} __packed; + +/* Initial Value file format */ +#define B43legacy_IV_OFFSET_MASK 0x7FFF +#define B43legacy_IV_32BIT 0x8000 +struct b43legacy_iv { + __be16 offset_size; + union { + __be16 d16; + __be32 d32; + } data __packed; +} __packed; + +#define B43legacy_PHYMODE(phytype) (1 << (phytype)) +#define B43legacy_PHYMODE_B B43legacy_PHYMODE \ + ((B43legacy_PHYTYPE_B)) +#define B43legacy_PHYMODE_G B43legacy_PHYMODE \ + ((B43legacy_PHYTYPE_G)) + +/* Value pair to measure the LocalOscillator. */ +struct b43legacy_lopair { + s8 low; + s8 high; + u8 used:1; +}; +#define B43legacy_LO_COUNT (14*4) + +struct b43legacy_phy { + /* Possible PHYMODEs on this PHY */ + u8 possible_phymodes; + /* GMODE bit enabled in MACCTL? */ + bool gmode; + + /* Analog Type */ + u8 analog; + /* B43legacy_PHYTYPE_ */ + u8 type; + /* PHY revision number. */ + u8 rev; + + u16 antenna_diversity; + u16 savedpctlreg; + /* Radio versioning */ + u16 radio_manuf; /* Radio manufacturer */ + u16 radio_ver; /* Radio version */ + u8 calibrated:1; + u8 radio_rev; /* Radio revision */ + + bool dyn_tssi_tbl; /* tssi2dbm is kmalloc()ed. */ + + /* ACI (adjacent channel interference) flags. */ + bool aci_enable; + bool aci_wlan_automatic; + bool aci_hw_rssi; + + /* Radio switched on/off */ + bool radio_on; + struct { + /* Values saved when turning the radio off. + * They are needed when turning it on again. */ + bool valid; + u16 rfover; + u16 rfoverval; + } radio_off_context; + + u16 minlowsig[2]; + u16 minlowsigpos[2]; + + /* LO Measurement Data. + * Use b43legacy_get_lopair() to get a value. + */ + struct b43legacy_lopair *_lo_pairs; + /* TSSI to dBm table in use */ + const s8 *tssi2dbm; + /* idle TSSI value */ + s8 idle_tssi; + /* Target idle TSSI */ + int tgt_idle_tssi; + /* Current idle TSSI */ + int cur_idle_tssi; + + /* LocalOscillator control values. */ + struct b43legacy_txpower_lo_control *lo_control; + /* Values from b43legacy_calc_loopback_gain() */ + s16 max_lb_gain; /* Maximum Loopback gain in hdB */ + s16 trsw_rx_gain; /* TRSW RX gain in hdB */ + s16 lna_lod_gain; /* LNA lod */ + s16 lna_gain; /* LNA */ + s16 pga_gain; /* PGA */ + + /* Desired TX power level (in dBm). This is set by the user and + * adjusted in b43legacy_phy_xmitpower(). */ + u8 power_level; + + /* Values from b43legacy_calc_loopback_gain() */ + u16 loopback_gain[2]; + + /* TX Power control values. */ + /* B/G PHY */ + struct { + /* Current Radio Attenuation for TXpower recalculation. */ + u16 rfatt; + /* Current Baseband Attenuation for TXpower recalculation. */ + u16 bbatt; + /* Current TXpower control value for TXpower recalculation. */ + u16 txctl1; + u16 txctl2; + }; + /* A PHY */ + struct { + u16 txpwr_offset; + }; + + /* Current Interference Mitigation mode */ + int interfmode; + /* Stack of saved values from the Interference Mitigation code. + * Each value in the stack is laid out as follows: + * bit 0-11: offset + * bit 12-15: register ID + * bit 16-32: value + * register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT + */ +#define B43legacy_INTERFSTACK_SIZE 26 + u32 interfstack[B43legacy_INTERFSTACK_SIZE]; + + /* Saved values from the NRSSI Slope calculation */ + s16 nrssi[2]; + s32 nrssislope; + /* In memory nrssi lookup table. */ + s8 nrssi_lt[64]; + + /* current channel */ + u8 channel; + + u16 lofcal; + + u16 initval; + + /* PHY TX errors counter. */ + atomic_t txerr_cnt; + +#if B43legacy_DEBUG + /* Manual TX-power control enabled? */ + bool manual_txpower_control; + /* PHY registers locked by b43legacy_phy_lock()? */ + bool phy_locked; +#endif /* B43legacy_DEBUG */ +}; + +/* Data structures for DMA transmission, per 80211 core. */ +struct b43legacy_dma { + struct b43legacy_dmaring *tx_ring0; + struct b43legacy_dmaring *tx_ring1; + struct b43legacy_dmaring *tx_ring2; + struct b43legacy_dmaring *tx_ring3; + struct b43legacy_dmaring *tx_ring4; + struct b43legacy_dmaring *tx_ring5; + + struct b43legacy_dmaring *rx_ring0; + struct b43legacy_dmaring *rx_ring3; /* only on core.rev < 5 */ + + u32 translation; /* Routing bits */ +}; + +/* Data structures for PIO transmission, per 80211 core. */ +struct b43legacy_pio { + struct b43legacy_pioqueue *queue0; + struct b43legacy_pioqueue *queue1; + struct b43legacy_pioqueue *queue2; + struct b43legacy_pioqueue *queue3; +}; + +/* Context information for a noise calculation (Link Quality). */ +struct b43legacy_noise_calculation { + u8 channel_at_start; + bool calculation_running; + u8 nr_samples; + s8 samples[8][4]; +}; + +struct b43legacy_stats { + u8 link_noise; + /* Store the last TX/RX times here for updating the leds. */ + unsigned long last_tx; + unsigned long last_rx; +}; + +struct b43legacy_key { + void *keyconf; + bool enabled; + u8 algorithm; +}; + +#define B43legacy_QOS_QUEUE_NUM 4 + +struct b43legacy_wldev; + +/* QOS parameters for a queue. */ +struct b43legacy_qos_params { + /* The QOS parameters */ + struct ieee80211_tx_queue_params p; +}; + +/* Data structure for the WLAN parts (802.11 cores) of the b43legacy chip. */ +struct b43legacy_wl { + /* Pointer to the active wireless device on this chip */ + struct b43legacy_wldev *current_dev; + /* Pointer to the ieee80211 hardware data structure */ + struct ieee80211_hw *hw; + + spinlock_t irq_lock; /* locks IRQ */ + struct mutex mutex; /* locks wireless core state */ + spinlock_t leds_lock; /* lock for leds */ + + /* firmware loading work */ + struct work_struct firmware_load; + + /* We can only have one operating interface (802.11 core) + * at a time. General information about this interface follows. + */ + + struct ieee80211_vif *vif; + /* MAC address (can be NULL). */ + u8 mac_addr[ETH_ALEN]; + /* Current BSSID (can be NULL). */ + u8 bssid[ETH_ALEN]; + /* Interface type. (IEEE80211_IF_TYPE_XXX) */ + int if_type; + /* Is the card operating in AP, STA or IBSS mode? */ + bool operating; + /* filter flags */ + unsigned int filter_flags; + /* Stats about the wireless interface */ + struct ieee80211_low_level_stats ieee_stats; + +#ifdef CONFIG_B43LEGACY_HWRNG + struct hwrng rng; + u8 rng_initialized; + char rng_name[30 + 1]; +#endif + + /* List of all wireless devices on this chip */ + struct list_head devlist; + u8 nr_devs; + + bool radiotap_enabled; + bool radio_enabled; + + /* The beacon we are currently using (AP or IBSS mode). + * This beacon stuff is protected by the irq_lock. */ + struct sk_buff *current_beacon; + bool beacon0_uploaded; + bool beacon1_uploaded; + bool beacon_templates_virgin; /* Never wrote the templates? */ + struct work_struct beacon_update_trigger; + /* The current QOS parameters for the 4 queues. */ + struct b43legacy_qos_params qos_params[B43legacy_QOS_QUEUE_NUM]; + + /* Packet transmit work */ + struct work_struct tx_work; + + /* Queue of packets to be transmitted. */ + struct sk_buff_head tx_queue[B43legacy_QOS_QUEUE_NUM]; + + /* Flag that implement the queues stopping. */ + bool tx_queue_stopped[B43legacy_QOS_QUEUE_NUM]; + +}; + +/* Pointers to the firmware data and meta information about it. */ +struct b43legacy_firmware { + /* Microcode */ + const struct firmware *ucode; + /* PCM code */ + const struct firmware *pcm; + /* Initial MMIO values for the firmware */ + const struct firmware *initvals; + /* Initial MMIO values for the firmware, band-specific */ + const struct firmware *initvals_band; + /* Firmware revision */ + u16 rev; + /* Firmware patchlevel */ + u16 patch; +}; + +/* Device (802.11 core) initialization status. */ +enum { + B43legacy_STAT_UNINIT = 0, /* Uninitialized. */ + B43legacy_STAT_INITIALIZED = 1, /* Initialized, not yet started. */ + B43legacy_STAT_STARTED = 2, /* Up and running. */ +}; +#define b43legacy_status(wldev) atomic_read(&(wldev)->__init_status) +#define b43legacy_set_status(wldev, stat) do { \ + atomic_set(&(wldev)->__init_status, (stat)); \ + smp_wmb(); \ + } while (0) + +/* *** --- HOW LOCKING WORKS IN B43legacy --- *** + * + * You should always acquire both, wl->mutex and wl->irq_lock unless: + * - You don't need to acquire wl->irq_lock, if the interface is stopped. + * - You don't need to acquire wl->mutex in the IRQ handler, IRQ tasklet + * and packet TX path (and _ONLY_ there.) + */ + +/* Data structure for one wireless device (802.11 core) */ +struct b43legacy_wldev { + struct ssb_device *dev; + struct b43legacy_wl *wl; + + /* The device initialization status. + * Use b43legacy_status() to query. */ + atomic_t __init_status; + /* Saved init status for handling suspend. */ + int suspend_init_status; + + bool __using_pio; /* Using pio rather than dma. */ + bool bad_frames_preempt;/* Use "Bad Frames Preemption". */ + bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM). */ + bool short_preamble; /* TRUE if using short preamble. */ + bool radio_hw_enable; /* State of radio hardware enable bit. */ + + /* PHY/Radio device. */ + struct b43legacy_phy phy; + union { + /* DMA engines. */ + struct b43legacy_dma dma; + /* PIO engines. */ + struct b43legacy_pio pio; + }; + + /* Various statistics about the physical device. */ + struct b43legacy_stats stats; + + /* The device LEDs. */ + struct b43legacy_led led_tx; + struct b43legacy_led led_rx; + struct b43legacy_led led_assoc; + struct b43legacy_led led_radio; + + /* Reason code of the last interrupt. */ + u32 irq_reason; + u32 dma_reason[6]; + /* The currently active generic-interrupt mask. */ + u32 irq_mask; + /* Link Quality calculation context. */ + struct b43legacy_noise_calculation noisecalc; + /* if > 0 MAC is suspended. if == 0 MAC is enabled. */ + int mac_suspended; + + /* Interrupt Service Routine tasklet (bottom-half) */ + struct tasklet_struct isr_tasklet; + + /* Periodic tasks */ + struct delayed_work periodic_work; + unsigned int periodic_state; + + struct work_struct restart_work; + + /* encryption/decryption */ + u16 ktp; /* Key table pointer */ + u8 max_nr_keys; + struct b43legacy_key key[58]; + + /* Firmware data */ + struct b43legacy_firmware fw; + const struct firmware *fwp; /* needed to pass fw pointer */ + + /* completion struct for firmware loading */ + struct completion fw_load_complete; + + /* Devicelist in struct b43legacy_wl (all 802.11 cores) */ + struct list_head list; + + /* Debugging stuff follows. */ +#ifdef CONFIG_B43LEGACY_DEBUG + struct b43legacy_dfsentry *dfsentry; +#endif +}; + + +static inline +struct b43legacy_wl *hw_to_b43legacy_wl(struct ieee80211_hw *hw) +{ + return hw->priv; +} + +/* Helper function, which returns a boolean. + * TRUE, if PIO is used; FALSE, if DMA is used. + */ +#if defined(CONFIG_B43LEGACY_DMA) && defined(CONFIG_B43LEGACY_PIO) +static inline +int b43legacy_using_pio(struct b43legacy_wldev *dev) +{ + return dev->__using_pio; +} +#elif defined(CONFIG_B43LEGACY_DMA) +static inline +int b43legacy_using_pio(struct b43legacy_wldev *dev) +{ + return 0; +} +#elif defined(CONFIG_B43LEGACY_PIO) +static inline +int b43legacy_using_pio(struct b43legacy_wldev *dev) +{ + return 1; +} +#else +# error "Using neither DMA nor PIO? Confused..." +#endif + + +static inline +struct b43legacy_wldev *dev_to_b43legacy_wldev(struct device *dev) +{ + struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); + return ssb_get_drvdata(ssb_dev); +} + +/* Is the device operating in a specified mode (IEEE80211_IF_TYPE_XXX). */ +static inline +int b43legacy_is_mode(struct b43legacy_wl *wl, int type) +{ + return (wl->operating && + wl->if_type == type); +} + +static inline +bool is_bcm_board_vendor(struct b43legacy_wldev *dev) +{ + return (dev->dev->bus->boardinfo.vendor == PCI_VENDOR_ID_BROADCOM); +} + +static inline +u16 b43legacy_read16(struct b43legacy_wldev *dev, u16 offset) +{ + return ssb_read16(dev->dev, offset); +} + +static inline +void b43legacy_write16(struct b43legacy_wldev *dev, u16 offset, u16 value) +{ + ssb_write16(dev->dev, offset, value); +} + +static inline +u32 b43legacy_read32(struct b43legacy_wldev *dev, u16 offset) +{ + return ssb_read32(dev->dev, offset); +} + +static inline +void b43legacy_write32(struct b43legacy_wldev *dev, u16 offset, u32 value) +{ + ssb_write32(dev->dev, offset, value); +} + +static inline +struct b43legacy_lopair *b43legacy_get_lopair(struct b43legacy_phy *phy, + u16 radio_attenuation, + u16 baseband_attenuation) +{ + return phy->_lo_pairs + (radio_attenuation + + 14 * (baseband_attenuation / 2)); +} + + + +/* Message printing */ +__printf(2, 3) +void b43legacyinfo(struct b43legacy_wl *wl, const char *fmt, ...); +__printf(2, 3) +void b43legacyerr(struct b43legacy_wl *wl, const char *fmt, ...); +__printf(2, 3) +void b43legacywarn(struct b43legacy_wl *wl, const char *fmt, ...); +#if B43legacy_DEBUG +__printf(2, 3) +void b43legacydbg(struct b43legacy_wl *wl, const char *fmt, ...); +#else /* DEBUG */ +# define b43legacydbg(wl, fmt...) do { /* nothing */ } while (0) +#endif /* DEBUG */ + +/* Macros for printing a value in Q5.2 format */ +#define Q52_FMT "%u.%u" +#define Q52_ARG(q52) ((q52) / 4), (((q52) & 3) * 100 / 4) + +#endif /* B43legacy_H_ */ diff --git a/drivers/net/wireless/broadcom/b43legacy/debugfs.c b/drivers/net/wireless/broadcom/b43legacy/debugfs.c new file mode 100644 index 000000000000..090910ea259e --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/debugfs.c @@ -0,0 +1,500 @@ +/* + + Broadcom B43legacy wireless driver + + debugfs driver debugging code + + Copyright (c) 2005-2007 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include +#include +#include +#include +#include +#include + +#include "b43legacy.h" +#include "main.h" +#include "debugfs.h" +#include "dma.h" +#include "pio.h" +#include "xmit.h" + + +/* The root directory. */ +static struct dentry *rootdir; + +struct b43legacy_debugfs_fops { + ssize_t (*read)(struct b43legacy_wldev *dev, char *buf, size_t bufsize); + int (*write)(struct b43legacy_wldev *dev, const char *buf, size_t count); + struct file_operations fops; + /* Offset of struct b43legacy_dfs_file in struct b43legacy_dfsentry */ + size_t file_struct_offset; + /* Take wl->irq_lock before calling read/write? */ + bool take_irqlock; +}; + +static inline +struct b43legacy_dfs_file * fops_to_dfs_file(struct b43legacy_wldev *dev, + const struct b43legacy_debugfs_fops *dfops) +{ + void *p; + + p = dev->dfsentry; + p += dfops->file_struct_offset; + + return p; +} + + +#define fappend(fmt, x...) \ + do { \ + if (bufsize - count) \ + count += snprintf(buf + count, \ + bufsize - count, \ + fmt , ##x); \ + else \ + printk(KERN_ERR "b43legacy: fappend overflow\n"); \ + } while (0) + + +/* wl->irq_lock is locked */ +static ssize_t tsf_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) +{ + ssize_t count = 0; + u64 tsf; + + b43legacy_tsf_read(dev, &tsf); + fappend("0x%08x%08x\n", + (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32), + (unsigned int)(tsf & 0xFFFFFFFFULL)); + + return count; +} + +/* wl->irq_lock is locked */ +static int tsf_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count) +{ + u64 tsf; + + if (sscanf(buf, "%llu", (unsigned long long *)(&tsf)) != 1) + return -EINVAL; + b43legacy_tsf_write(dev, tsf); + + return 0; +} + +/* wl->irq_lock is locked */ +static ssize_t ucode_regs_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) +{ + ssize_t count = 0; + int i; + + for (i = 0; i < 64; i++) { + fappend("r%d = 0x%04x\n", i, + b43legacy_shm_read16(dev, B43legacy_SHM_WIRELESS, i)); + } + + return count; +} + +/* wl->irq_lock is locked */ +static ssize_t shm_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) +{ + ssize_t count = 0; + int i; + u16 tmp; + __le16 *le16buf = (__le16 *)buf; + + for (i = 0; i < 0x1000; i++) { + if (bufsize < sizeof(tmp)) + break; + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 2 * i); + le16buf[i] = cpu_to_le16(tmp); + count += sizeof(tmp); + bufsize -= sizeof(tmp); + } + + return count; +} + +static ssize_t txstat_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize) +{ + struct b43legacy_txstatus_log *log = &dev->dfsentry->txstatlog; + ssize_t count = 0; + unsigned long flags; + int i, idx; + struct b43legacy_txstatus *stat; + + spin_lock_irqsave(&log->lock, flags); + if (log->end < 0) { + fappend("Nothing transmitted, yet\n"); + goto out_unlock; + } + fappend("b43legacy TX status reports:\n\n" + "index | cookie | seq | phy_stat | frame_count | " + "rts_count | supp_reason | pm_indicated | " + "intermediate | for_ampdu | acked\n" "---\n"); + i = log->end + 1; + idx = 0; + while (1) { + if (i == B43legacy_NR_LOGGED_TXSTATUS) + i = 0; + stat = &(log->log[i]); + if (stat->cookie) { + fappend("%03d | " + "0x%04X | 0x%04X | 0x%02X | " + "0x%X | 0x%X | " + "%u | %u | " + "%u | %u | %u\n", + idx, + stat->cookie, stat->seq, stat->phy_stat, + stat->frame_count, stat->rts_count, + stat->supp_reason, stat->pm_indicated, + stat->intermediate, stat->for_ampdu, + stat->acked); + idx++; + } + if (i == log->end) + break; + i++; + } +out_unlock: + spin_unlock_irqrestore(&log->lock, flags); + + return count; +} + +/* wl->irq_lock is locked */ +static int restart_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count) +{ + int err = 0; + + if (count > 0 && buf[0] == '1') { + b43legacy_controller_restart(dev, "manually restarted"); + } else + err = -EINVAL; + + return err; +} + +#undef fappend + +static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct b43legacy_wldev *dev; + struct b43legacy_debugfs_fops *dfops; + struct b43legacy_dfs_file *dfile; + ssize_t uninitialized_var(ret); + char *buf; + const size_t bufsize = 1024 * 16; /* 16 KiB buffer */ + const size_t buforder = get_order(bufsize); + int err = 0; + + if (!count) + return 0; + dev = file->private_data; + if (!dev) + return -ENODEV; + + mutex_lock(&dev->wl->mutex); + if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) { + err = -ENODEV; + goto out_unlock; + } + + dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops); + if (!dfops->read) { + err = -ENOSYS; + goto out_unlock; + } + dfile = fops_to_dfs_file(dev, dfops); + + if (!dfile->buffer) { + buf = (char *)__get_free_pages(GFP_KERNEL, buforder); + if (!buf) { + err = -ENOMEM; + goto out_unlock; + } + memset(buf, 0, bufsize); + if (dfops->take_irqlock) { + spin_lock_irq(&dev->wl->irq_lock); + ret = dfops->read(dev, buf, bufsize); + spin_unlock_irq(&dev->wl->irq_lock); + } else + ret = dfops->read(dev, buf, bufsize); + if (ret <= 0) { + free_pages((unsigned long)buf, buforder); + err = ret; + goto out_unlock; + } + dfile->data_len = ret; + dfile->buffer = buf; + } + + ret = simple_read_from_buffer(userbuf, count, ppos, + dfile->buffer, + dfile->data_len); + if (*ppos >= dfile->data_len) { + free_pages((unsigned long)dfile->buffer, buforder); + dfile->buffer = NULL; + dfile->data_len = 0; + } +out_unlock: + mutex_unlock(&dev->wl->mutex); + + return err ? err : ret; +} + +static ssize_t b43legacy_debugfs_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct b43legacy_wldev *dev; + struct b43legacy_debugfs_fops *dfops; + char *buf; + int err = 0; + + if (!count) + return 0; + if (count > PAGE_SIZE) + return -E2BIG; + dev = file->private_data; + if (!dev) + return -ENODEV; + + mutex_lock(&dev->wl->mutex); + if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) { + err = -ENODEV; + goto out_unlock; + } + + dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops); + if (!dfops->write) { + err = -ENOSYS; + goto out_unlock; + } + + buf = (char *)get_zeroed_page(GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto out_unlock; + } + if (copy_from_user(buf, userbuf, count)) { + err = -EFAULT; + goto out_freepage; + } + if (dfops->take_irqlock) { + spin_lock_irq(&dev->wl->irq_lock); + err = dfops->write(dev, buf, count); + spin_unlock_irq(&dev->wl->irq_lock); + } else + err = dfops->write(dev, buf, count); + if (err) + goto out_freepage; + +out_freepage: + free_page((unsigned long)buf); +out_unlock: + mutex_unlock(&dev->wl->mutex); + + return err ? err : count; +} + + +#define B43legacy_DEBUGFS_FOPS(name, _read, _write, _take_irqlock) \ + static struct b43legacy_debugfs_fops fops_##name = { \ + .read = _read, \ + .write = _write, \ + .fops = { \ + .open = simple_open, \ + .read = b43legacy_debugfs_read, \ + .write = b43legacy_debugfs_write, \ + .llseek = generic_file_llseek, \ + }, \ + .file_struct_offset = offsetof(struct b43legacy_dfsentry, \ + file_##name), \ + .take_irqlock = _take_irqlock, \ + } + +B43legacy_DEBUGFS_FOPS(tsf, tsf_read_file, tsf_write_file, 1); +B43legacy_DEBUGFS_FOPS(ucode_regs, ucode_regs_read_file, NULL, 1); +B43legacy_DEBUGFS_FOPS(shm, shm_read_file, NULL, 1); +B43legacy_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0); +B43legacy_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1); + + +int b43legacy_debug(struct b43legacy_wldev *dev, enum b43legacy_dyndbg feature) +{ + return !!(dev->dfsentry && dev->dfsentry->dyn_debug[feature]); +} + +static void b43legacy_remove_dynamic_debug(struct b43legacy_wldev *dev) +{ + struct b43legacy_dfsentry *e = dev->dfsentry; + int i; + + for (i = 0; i < __B43legacy_NR_DYNDBG; i++) + debugfs_remove(e->dyn_debug_dentries[i]); +} + +static void b43legacy_add_dynamic_debug(struct b43legacy_wldev *dev) +{ + struct b43legacy_dfsentry *e = dev->dfsentry; + struct dentry *d; + +#define add_dyn_dbg(name, id, initstate) do { \ + e->dyn_debug[id] = (initstate); \ + d = debugfs_create_bool(name, 0600, e->subdir, \ + &(e->dyn_debug[id])); \ + if (!IS_ERR(d)) \ + e->dyn_debug_dentries[id] = d; \ + } while (0) + + add_dyn_dbg("debug_xmitpower", B43legacy_DBG_XMITPOWER, false); + add_dyn_dbg("debug_dmaoverflow", B43legacy_DBG_DMAOVERFLOW, false); + add_dyn_dbg("debug_dmaverbose", B43legacy_DBG_DMAVERBOSE, false); + add_dyn_dbg("debug_pwork_fast", B43legacy_DBG_PWORK_FAST, false); + add_dyn_dbg("debug_pwork_stop", B43legacy_DBG_PWORK_STOP, false); + +#undef add_dyn_dbg +} + +void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev) +{ + struct b43legacy_dfsentry *e; + struct b43legacy_txstatus_log *log; + char devdir[16]; + + B43legacy_WARN_ON(!dev); + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (!e) { + b43legacyerr(dev->wl, "debugfs: add device OOM\n"); + return; + } + e->dev = dev; + log = &e->txstatlog; + log->log = kcalloc(B43legacy_NR_LOGGED_TXSTATUS, + sizeof(struct b43legacy_txstatus), GFP_KERNEL); + if (!log->log) { + b43legacyerr(dev->wl, "debugfs: add device txstatus OOM\n"); + kfree(e); + return; + } + log->end = -1; + spin_lock_init(&log->lock); + + dev->dfsentry = e; + + snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy)); + e->subdir = debugfs_create_dir(devdir, rootdir); + if (!e->subdir || IS_ERR(e->subdir)) { + if (e->subdir == ERR_PTR(-ENODEV)) { + b43legacydbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not " + "enabled in kernel config\n"); + } else { + b43legacyerr(dev->wl, "debugfs: cannot create %s directory\n", + devdir); + } + dev->dfsentry = NULL; + kfree(log->log); + kfree(e); + return; + } + +#define ADD_FILE(name, mode) \ + do { \ + struct dentry *d; \ + d = debugfs_create_file(__stringify(name), \ + mode, e->subdir, dev, \ + &fops_##name.fops); \ + e->file_##name.dentry = NULL; \ + if (!IS_ERR(d)) \ + e->file_##name.dentry = d; \ + } while (0) + + + ADD_FILE(tsf, 0600); + ADD_FILE(ucode_regs, 0400); + ADD_FILE(shm, 0400); + ADD_FILE(txstat, 0400); + ADD_FILE(restart, 0200); + +#undef ADD_FILE + + b43legacy_add_dynamic_debug(dev); +} + +void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev) +{ + struct b43legacy_dfsentry *e; + + if (!dev) + return; + e = dev->dfsentry; + if (!e) + return; + b43legacy_remove_dynamic_debug(dev); + + debugfs_remove(e->file_tsf.dentry); + debugfs_remove(e->file_ucode_regs.dentry); + debugfs_remove(e->file_shm.dentry); + debugfs_remove(e->file_txstat.dentry); + debugfs_remove(e->file_restart.dentry); + + debugfs_remove(e->subdir); + kfree(e->txstatlog.log); + kfree(e); +} + +void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev, + const struct b43legacy_txstatus *status) +{ + struct b43legacy_dfsentry *e = dev->dfsentry; + struct b43legacy_txstatus_log *log; + struct b43legacy_txstatus *cur; + int i; + + if (!e) + return; + log = &e->txstatlog; + B43legacy_WARN_ON(!irqs_disabled()); + spin_lock(&log->lock); + i = log->end + 1; + if (i == B43legacy_NR_LOGGED_TXSTATUS) + i = 0; + log->end = i; + cur = &(log->log[i]); + memcpy(cur, status, sizeof(*cur)); + spin_unlock(&log->lock); +} + +void b43legacy_debugfs_init(void) +{ + rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (IS_ERR(rootdir)) + rootdir = NULL; +} + +void b43legacy_debugfs_exit(void) +{ + debugfs_remove(rootdir); +} diff --git a/drivers/net/wireless/broadcom/b43legacy/debugfs.h b/drivers/net/wireless/broadcom/b43legacy/debugfs.h new file mode 100644 index 000000000000..9ee32158b947 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/debugfs.h @@ -0,0 +1,89 @@ +#ifndef B43legacy_DEBUGFS_H_ +#define B43legacy_DEBUGFS_H_ + +struct b43legacy_wldev; +struct b43legacy_txstatus; + +enum b43legacy_dyndbg { /* Dynamic debugging features */ + B43legacy_DBG_XMITPOWER, + B43legacy_DBG_DMAOVERFLOW, + B43legacy_DBG_DMAVERBOSE, + B43legacy_DBG_PWORK_FAST, + B43legacy_DBG_PWORK_STOP, + __B43legacy_NR_DYNDBG, +}; + + +#ifdef CONFIG_B43LEGACY_DEBUG + +struct dentry; + +#define B43legacy_NR_LOGGED_TXSTATUS 100 + +struct b43legacy_txstatus_log { + struct b43legacy_txstatus *log; + int end; + spinlock_t lock; /* lock for debugging */ +}; + +struct b43legacy_dfs_file { + struct dentry *dentry; + char *buffer; + size_t data_len; +}; + +struct b43legacy_dfsentry { + struct b43legacy_wldev *dev; + struct dentry *subdir; + + struct b43legacy_dfs_file file_tsf; + struct b43legacy_dfs_file file_ucode_regs; + struct b43legacy_dfs_file file_shm; + struct b43legacy_dfs_file file_txstat; + struct b43legacy_dfs_file file_txpower_g; + struct b43legacy_dfs_file file_restart; + struct b43legacy_dfs_file file_loctls; + + struct b43legacy_txstatus_log txstatlog; + + /* Enabled/Disabled list for the dynamic debugging features. */ + bool dyn_debug[__B43legacy_NR_DYNDBG]; + /* Dentries for the dynamic debugging entries. */ + struct dentry *dyn_debug_dentries[__B43legacy_NR_DYNDBG]; +}; + +int b43legacy_debug(struct b43legacy_wldev *dev, + enum b43legacy_dyndbg feature); + +void b43legacy_debugfs_init(void); +void b43legacy_debugfs_exit(void); +void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev); +void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev); +void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev, + const struct b43legacy_txstatus *status); + +#else /* CONFIG_B43LEGACY_DEBUG*/ + +static inline +int b43legacy_debug(struct b43legacy_wldev *dev, + enum b43legacy_dyndbg feature) +{ + return 0; +} + +static inline +void b43legacy_debugfs_init(void) { } +static inline +void b43legacy_debugfs_exit(void) { } +static inline +void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev) { } +static inline +void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev) { } +static inline +void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev, + const struct b43legacy_txstatus *status) + { } + +#endif /* CONFIG_B43LEGACY_DEBUG*/ + +#endif /* B43legacy_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/broadcom/b43legacy/dma.c b/drivers/net/wireless/broadcom/b43legacy/dma.c new file mode 100644 index 000000000000..f9dd892b9f27 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/dma.c @@ -0,0 +1,1455 @@ +/* + + Broadcom B43legacy wireless driver + + DMA ringbuffer and descriptor allocation/management + + Copyright (c) 2005, 2006 Michael Buesch + + Some code in this file is derived from the b44.c driver + Copyright (C) 2002 David S. Miller + Copyright (C) Pekka Pietikainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43legacy.h" +#include "dma.h" +#include "main.h" +#include "debugfs.h" +#include "xmit.h" + +#include +#include +#include +#include +#include +#include + +/* 32bit DMA ops. */ +static +struct b43legacy_dmadesc32 *op32_idx2desc(struct b43legacy_dmaring *ring, + int slot, + struct b43legacy_dmadesc_meta **meta) +{ + struct b43legacy_dmadesc32 *desc; + + *meta = &(ring->meta[slot]); + desc = ring->descbase; + desc = &(desc[slot]); + + return desc; +} + +static void op32_fill_descriptor(struct b43legacy_dmaring *ring, + struct b43legacy_dmadesc32 *desc, + dma_addr_t dmaaddr, u16 bufsize, + int start, int end, int irq) +{ + struct b43legacy_dmadesc32 *descbase = ring->descbase; + int slot; + u32 ctl; + u32 addr; + u32 addrext; + + slot = (int)(desc - descbase); + B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); + + addr = (u32)(dmaaddr & ~SSB_DMA_TRANSLATION_MASK); + addrext = (u32)(dmaaddr & SSB_DMA_TRANSLATION_MASK) + >> SSB_DMA_TRANSLATION_SHIFT; + addr |= ring->dev->dma.translation; + ctl = (bufsize - ring->frameoffset) + & B43legacy_DMA32_DCTL_BYTECNT; + if (slot == ring->nr_slots - 1) + ctl |= B43legacy_DMA32_DCTL_DTABLEEND; + if (start) + ctl |= B43legacy_DMA32_DCTL_FRAMESTART; + if (end) + ctl |= B43legacy_DMA32_DCTL_FRAMEEND; + if (irq) + ctl |= B43legacy_DMA32_DCTL_IRQ; + ctl |= (addrext << B43legacy_DMA32_DCTL_ADDREXT_SHIFT) + & B43legacy_DMA32_DCTL_ADDREXT_MASK; + + desc->control = cpu_to_le32(ctl); + desc->address = cpu_to_le32(addr); +} + +static void op32_poke_tx(struct b43legacy_dmaring *ring, int slot) +{ + b43legacy_dma_write(ring, B43legacy_DMA32_TXINDEX, + (u32)(slot * sizeof(struct b43legacy_dmadesc32))); +} + +static void op32_tx_suspend(struct b43legacy_dmaring *ring) +{ + b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, + b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL) + | B43legacy_DMA32_TXSUSPEND); +} + +static void op32_tx_resume(struct b43legacy_dmaring *ring) +{ + b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, + b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL) + & ~B43legacy_DMA32_TXSUSPEND); +} + +static int op32_get_current_rxslot(struct b43legacy_dmaring *ring) +{ + u32 val; + + val = b43legacy_dma_read(ring, B43legacy_DMA32_RXSTATUS); + val &= B43legacy_DMA32_RXDPTR; + + return (val / sizeof(struct b43legacy_dmadesc32)); +} + +static void op32_set_current_rxslot(struct b43legacy_dmaring *ring, + int slot) +{ + b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX, + (u32)(slot * sizeof(struct b43legacy_dmadesc32))); +} + +static inline int free_slots(struct b43legacy_dmaring *ring) +{ + return (ring->nr_slots - ring->used_slots); +} + +static inline int next_slot(struct b43legacy_dmaring *ring, int slot) +{ + B43legacy_WARN_ON(!(slot >= -1 && slot <= ring->nr_slots - 1)); + if (slot == ring->nr_slots - 1) + return 0; + return slot + 1; +} + +static inline int prev_slot(struct b43legacy_dmaring *ring, int slot) +{ + B43legacy_WARN_ON(!(slot >= 0 && slot <= ring->nr_slots - 1)); + if (slot == 0) + return ring->nr_slots - 1; + return slot - 1; +} + +#ifdef CONFIG_B43LEGACY_DEBUG +static void update_max_used_slots(struct b43legacy_dmaring *ring, + int current_used_slots) +{ + if (current_used_slots <= ring->max_used_slots) + return; + ring->max_used_slots = current_used_slots; + if (b43legacy_debug(ring->dev, B43legacy_DBG_DMAVERBOSE)) + b43legacydbg(ring->dev->wl, + "max_used_slots increased to %d on %s ring %d\n", + ring->max_used_slots, + ring->tx ? "TX" : "RX", + ring->index); +} +#else +static inline +void update_max_used_slots(struct b43legacy_dmaring *ring, + int current_used_slots) +{ } +#endif /* DEBUG */ + +/* Request a slot for usage. */ +static inline +int request_slot(struct b43legacy_dmaring *ring) +{ + int slot; + + B43legacy_WARN_ON(!ring->tx); + B43legacy_WARN_ON(ring->stopped); + B43legacy_WARN_ON(free_slots(ring) == 0); + + slot = next_slot(ring, ring->current_slot); + ring->current_slot = slot; + ring->used_slots++; + + update_max_used_slots(ring, ring->used_slots); + + return slot; +} + +/* Mac80211-queue to b43legacy-ring mapping */ +static struct b43legacy_dmaring *priority_to_txring( + struct b43legacy_wldev *dev, + int queue_priority) +{ + struct b43legacy_dmaring *ring; + +/*FIXME: For now we always run on TX-ring-1 */ +return dev->dma.tx_ring1; + + /* 0 = highest priority */ + switch (queue_priority) { + default: + B43legacy_WARN_ON(1); + /* fallthrough */ + case 0: + ring = dev->dma.tx_ring3; + break; + case 1: + ring = dev->dma.tx_ring2; + break; + case 2: + ring = dev->dma.tx_ring1; + break; + case 3: + ring = dev->dma.tx_ring0; + break; + case 4: + ring = dev->dma.tx_ring4; + break; + case 5: + ring = dev->dma.tx_ring5; + break; + } + + return ring; +} + +/* Bcm4301-ring to mac80211-queue mapping */ +static inline int txring_to_priority(struct b43legacy_dmaring *ring) +{ + static const u8 idx_to_prio[] = + { 3, 2, 1, 0, 4, 5, }; + +/*FIXME: have only one queue, for now */ +return 0; + + return idx_to_prio[ring->index]; +} + + +static u16 b43legacy_dmacontroller_base(enum b43legacy_dmatype type, + int controller_idx) +{ + static const u16 map32[] = { + B43legacy_MMIO_DMA32_BASE0, + B43legacy_MMIO_DMA32_BASE1, + B43legacy_MMIO_DMA32_BASE2, + B43legacy_MMIO_DMA32_BASE3, + B43legacy_MMIO_DMA32_BASE4, + B43legacy_MMIO_DMA32_BASE5, + }; + + B43legacy_WARN_ON(!(controller_idx >= 0 && + controller_idx < ARRAY_SIZE(map32))); + return map32[controller_idx]; +} + +static inline +dma_addr_t map_descbuffer(struct b43legacy_dmaring *ring, + unsigned char *buf, + size_t len, + int tx) +{ + dma_addr_t dmaaddr; + + if (tx) + dmaaddr = dma_map_single(ring->dev->dev->dma_dev, + buf, len, + DMA_TO_DEVICE); + else + dmaaddr = dma_map_single(ring->dev->dev->dma_dev, + buf, len, + DMA_FROM_DEVICE); + + return dmaaddr; +} + +static inline +void unmap_descbuffer(struct b43legacy_dmaring *ring, + dma_addr_t addr, + size_t len, + int tx) +{ + if (tx) + dma_unmap_single(ring->dev->dev->dma_dev, + addr, len, + DMA_TO_DEVICE); + else + dma_unmap_single(ring->dev->dev->dma_dev, + addr, len, + DMA_FROM_DEVICE); +} + +static inline +void sync_descbuffer_for_cpu(struct b43legacy_dmaring *ring, + dma_addr_t addr, + size_t len) +{ + B43legacy_WARN_ON(ring->tx); + + dma_sync_single_for_cpu(ring->dev->dev->dma_dev, + addr, len, DMA_FROM_DEVICE); +} + +static inline +void sync_descbuffer_for_device(struct b43legacy_dmaring *ring, + dma_addr_t addr, + size_t len) +{ + B43legacy_WARN_ON(ring->tx); + + dma_sync_single_for_device(ring->dev->dev->dma_dev, + addr, len, DMA_FROM_DEVICE); +} + +static inline +void free_descriptor_buffer(struct b43legacy_dmaring *ring, + struct b43legacy_dmadesc_meta *meta, + int irq_context) +{ + if (meta->skb) { + if (irq_context) + dev_kfree_skb_irq(meta->skb); + else + dev_kfree_skb(meta->skb); + meta->skb = NULL; + } +} + +static int alloc_ringmemory(struct b43legacy_dmaring *ring) +{ + /* GFP flags must match the flags in free_ringmemory()! */ + ring->descbase = dma_zalloc_coherent(ring->dev->dev->dma_dev, + B43legacy_DMA_RINGMEMSIZE, + &(ring->dmabase), GFP_KERNEL); + if (!ring->descbase) + return -ENOMEM; + + return 0; +} + +static void free_ringmemory(struct b43legacy_dmaring *ring) +{ + dma_free_coherent(ring->dev->dev->dma_dev, B43legacy_DMA_RINGMEMSIZE, + ring->descbase, ring->dmabase); +} + +/* Reset the RX DMA channel */ +static int b43legacy_dmacontroller_rx_reset(struct b43legacy_wldev *dev, + u16 mmio_base, + enum b43legacy_dmatype type) +{ + int i; + u32 value; + u16 offset; + + might_sleep(); + + offset = B43legacy_DMA32_RXCTL; + b43legacy_write32(dev, mmio_base + offset, 0); + for (i = 0; i < 10; i++) { + offset = B43legacy_DMA32_RXSTATUS; + value = b43legacy_read32(dev, mmio_base + offset); + value &= B43legacy_DMA32_RXSTATE; + if (value == B43legacy_DMA32_RXSTAT_DISABLED) { + i = -1; + break; + } + msleep(1); + } + if (i != -1) { + b43legacyerr(dev->wl, "DMA RX reset timed out\n"); + return -ENODEV; + } + + return 0; +} + +/* Reset the RX DMA channel */ +static int b43legacy_dmacontroller_tx_reset(struct b43legacy_wldev *dev, + u16 mmio_base, + enum b43legacy_dmatype type) +{ + int i; + u32 value; + u16 offset; + + might_sleep(); + + for (i = 0; i < 10; i++) { + offset = B43legacy_DMA32_TXSTATUS; + value = b43legacy_read32(dev, mmio_base + offset); + value &= B43legacy_DMA32_TXSTATE; + if (value == B43legacy_DMA32_TXSTAT_DISABLED || + value == B43legacy_DMA32_TXSTAT_IDLEWAIT || + value == B43legacy_DMA32_TXSTAT_STOPPED) + break; + msleep(1); + } + offset = B43legacy_DMA32_TXCTL; + b43legacy_write32(dev, mmio_base + offset, 0); + for (i = 0; i < 10; i++) { + offset = B43legacy_DMA32_TXSTATUS; + value = b43legacy_read32(dev, mmio_base + offset); + value &= B43legacy_DMA32_TXSTATE; + if (value == B43legacy_DMA32_TXSTAT_DISABLED) { + i = -1; + break; + } + msleep(1); + } + if (i != -1) { + b43legacyerr(dev->wl, "DMA TX reset timed out\n"); + return -ENODEV; + } + /* ensure the reset is completed. */ + msleep(1); + + return 0; +} + +/* Check if a DMA mapping address is invalid. */ +static bool b43legacy_dma_mapping_error(struct b43legacy_dmaring *ring, + dma_addr_t addr, + size_t buffersize, + bool dma_to_device) +{ + if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr))) + return true; + + switch (ring->type) { + case B43legacy_DMA_30BIT: + if ((u64)addr + buffersize > (1ULL << 30)) + goto address_error; + break; + case B43legacy_DMA_32BIT: + if ((u64)addr + buffersize > (1ULL << 32)) + goto address_error; + break; + } + + /* The address is OK. */ + return false; + +address_error: + /* We can't support this address. Unmap it again. */ + unmap_descbuffer(ring, addr, buffersize, dma_to_device); + + return true; +} + +static int setup_rx_descbuffer(struct b43legacy_dmaring *ring, + struct b43legacy_dmadesc32 *desc, + struct b43legacy_dmadesc_meta *meta, + gfp_t gfp_flags) +{ + struct b43legacy_rxhdr_fw3 *rxhdr; + struct b43legacy_hwtxstatus *txstat; + dma_addr_t dmaaddr; + struct sk_buff *skb; + + B43legacy_WARN_ON(ring->tx); + + skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); + if (unlikely(!skb)) + return -ENOMEM; + dmaaddr = map_descbuffer(ring, skb->data, + ring->rx_buffersize, 0); + if (b43legacy_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { + /* ugh. try to realloc in zone_dma */ + gfp_flags |= GFP_DMA; + + dev_kfree_skb_any(skb); + + skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); + if (unlikely(!skb)) + return -ENOMEM; + dmaaddr = map_descbuffer(ring, skb->data, + ring->rx_buffersize, 0); + } + + if (b43legacy_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { + dev_kfree_skb_any(skb); + return -EIO; + } + + meta->skb = skb; + meta->dmaaddr = dmaaddr; + op32_fill_descriptor(ring, desc, dmaaddr, ring->rx_buffersize, 0, 0, 0); + + rxhdr = (struct b43legacy_rxhdr_fw3 *)(skb->data); + rxhdr->frame_len = 0; + txstat = (struct b43legacy_hwtxstatus *)(skb->data); + txstat->cookie = 0; + + return 0; +} + +/* Allocate the initial descbuffers. + * This is used for an RX ring only. + */ +static int alloc_initial_descbuffers(struct b43legacy_dmaring *ring) +{ + int i; + int err = -ENOMEM; + struct b43legacy_dmadesc32 *desc; + struct b43legacy_dmadesc_meta *meta; + + for (i = 0; i < ring->nr_slots; i++) { + desc = op32_idx2desc(ring, i, &meta); + + err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); + if (err) { + b43legacyerr(ring->dev->wl, + "Failed to allocate initial descbuffers\n"); + goto err_unwind; + } + } + mb(); /* all descbuffer setup before next line */ + ring->used_slots = ring->nr_slots; + err = 0; +out: + return err; + +err_unwind: + for (i--; i >= 0; i--) { + desc = op32_idx2desc(ring, i, &meta); + + unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); + dev_kfree_skb(meta->skb); + } + goto out; +} + +/* Do initial setup of the DMA controller. + * Reset the controller, write the ring busaddress + * and switch the "enable" bit on. + */ +static int dmacontroller_setup(struct b43legacy_dmaring *ring) +{ + int err = 0; + u32 value; + u32 addrext; + u32 trans = ring->dev->dma.translation; + u32 ringbase = (u32)(ring->dmabase); + + if (ring->tx) { + addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) + >> SSB_DMA_TRANSLATION_SHIFT; + value = B43legacy_DMA32_TXENABLE; + value |= (addrext << B43legacy_DMA32_TXADDREXT_SHIFT) + & B43legacy_DMA32_TXADDREXT_MASK; + b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, value); + b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, + (ringbase & ~SSB_DMA_TRANSLATION_MASK) + | trans); + } else { + err = alloc_initial_descbuffers(ring); + if (err) + goto out; + + addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) + >> SSB_DMA_TRANSLATION_SHIFT; + value = (ring->frameoffset << + B43legacy_DMA32_RXFROFF_SHIFT); + value |= B43legacy_DMA32_RXENABLE; + value |= (addrext << B43legacy_DMA32_RXADDREXT_SHIFT) + & B43legacy_DMA32_RXADDREXT_MASK; + b43legacy_dma_write(ring, B43legacy_DMA32_RXCTL, value); + b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, + (ringbase & ~SSB_DMA_TRANSLATION_MASK) + | trans); + b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX, 200); + } + +out: + return err; +} + +/* Shutdown the DMA controller. */ +static void dmacontroller_cleanup(struct b43legacy_dmaring *ring) +{ + if (ring->tx) { + b43legacy_dmacontroller_tx_reset(ring->dev, ring->mmio_base, + ring->type); + b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, 0); + } else { + b43legacy_dmacontroller_rx_reset(ring->dev, ring->mmio_base, + ring->type); + b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, 0); + } +} + +static void free_all_descbuffers(struct b43legacy_dmaring *ring) +{ + struct b43legacy_dmadesc_meta *meta; + int i; + + if (!ring->used_slots) + return; + for (i = 0; i < ring->nr_slots; i++) { + op32_idx2desc(ring, i, &meta); + + if (!meta->skb) { + B43legacy_WARN_ON(!ring->tx); + continue; + } + if (ring->tx) + unmap_descbuffer(ring, meta->dmaaddr, + meta->skb->len, 1); + else + unmap_descbuffer(ring, meta->dmaaddr, + ring->rx_buffersize, 0); + free_descriptor_buffer(ring, meta, 0); + } +} + +static u64 supported_dma_mask(struct b43legacy_wldev *dev) +{ + u32 tmp; + u16 mmio_base; + + mmio_base = b43legacy_dmacontroller_base(0, 0); + b43legacy_write32(dev, + mmio_base + B43legacy_DMA32_TXCTL, + B43legacy_DMA32_TXADDREXT_MASK); + tmp = b43legacy_read32(dev, mmio_base + + B43legacy_DMA32_TXCTL); + if (tmp & B43legacy_DMA32_TXADDREXT_MASK) + return DMA_BIT_MASK(32); + + return DMA_BIT_MASK(30); +} + +static enum b43legacy_dmatype dma_mask_to_engine_type(u64 dmamask) +{ + if (dmamask == DMA_BIT_MASK(30)) + return B43legacy_DMA_30BIT; + if (dmamask == DMA_BIT_MASK(32)) + return B43legacy_DMA_32BIT; + B43legacy_WARN_ON(1); + return B43legacy_DMA_30BIT; +} + +/* Main initialization function. */ +static +struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, + int controller_index, + int for_tx, + enum b43legacy_dmatype type) +{ + struct b43legacy_dmaring *ring; + int err; + int nr_slots; + dma_addr_t dma_test; + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) + goto out; + ring->type = type; + ring->dev = dev; + + nr_slots = B43legacy_RXRING_SLOTS; + if (for_tx) + nr_slots = B43legacy_TXRING_SLOTS; + + ring->meta = kcalloc(nr_slots, sizeof(struct b43legacy_dmadesc_meta), + GFP_KERNEL); + if (!ring->meta) + goto err_kfree_ring; + if (for_tx) { + ring->txhdr_cache = kcalloc(nr_slots, + sizeof(struct b43legacy_txhdr_fw3), + GFP_KERNEL); + if (!ring->txhdr_cache) + goto err_kfree_meta; + + /* test for ability to dma to txhdr_cache */ + dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, + sizeof(struct b43legacy_txhdr_fw3), + DMA_TO_DEVICE); + + if (b43legacy_dma_mapping_error(ring, dma_test, + sizeof(struct b43legacy_txhdr_fw3), 1)) { + /* ugh realloc */ + kfree(ring->txhdr_cache); + ring->txhdr_cache = kcalloc(nr_slots, + sizeof(struct b43legacy_txhdr_fw3), + GFP_KERNEL | GFP_DMA); + if (!ring->txhdr_cache) + goto err_kfree_meta; + + dma_test = dma_map_single(dev->dev->dma_dev, + ring->txhdr_cache, + sizeof(struct b43legacy_txhdr_fw3), + DMA_TO_DEVICE); + + if (b43legacy_dma_mapping_error(ring, dma_test, + sizeof(struct b43legacy_txhdr_fw3), 1)) + goto err_kfree_txhdr_cache; + } + + dma_unmap_single(dev->dev->dma_dev, dma_test, + sizeof(struct b43legacy_txhdr_fw3), + DMA_TO_DEVICE); + } + + ring->nr_slots = nr_slots; + ring->mmio_base = b43legacy_dmacontroller_base(type, controller_index); + ring->index = controller_index; + if (for_tx) { + ring->tx = true; + ring->current_slot = -1; + } else { + if (ring->index == 0) { + ring->rx_buffersize = B43legacy_DMA0_RX_BUFFERSIZE; + ring->frameoffset = B43legacy_DMA0_RX_FRAMEOFFSET; + } else if (ring->index == 3) { + ring->rx_buffersize = B43legacy_DMA3_RX_BUFFERSIZE; + ring->frameoffset = B43legacy_DMA3_RX_FRAMEOFFSET; + } else + B43legacy_WARN_ON(1); + } +#ifdef CONFIG_B43LEGACY_DEBUG + ring->last_injected_overflow = jiffies; +#endif + + err = alloc_ringmemory(ring); + if (err) + goto err_kfree_txhdr_cache; + err = dmacontroller_setup(ring); + if (err) + goto err_free_ringmemory; + +out: + return ring; + +err_free_ringmemory: + free_ringmemory(ring); +err_kfree_txhdr_cache: + kfree(ring->txhdr_cache); +err_kfree_meta: + kfree(ring->meta); +err_kfree_ring: + kfree(ring); + ring = NULL; + goto out; +} + +/* Main cleanup function. */ +static void b43legacy_destroy_dmaring(struct b43legacy_dmaring *ring) +{ + if (!ring) + return; + + b43legacydbg(ring->dev->wl, "DMA-%u 0x%04X (%s) max used slots:" + " %d/%d\n", (unsigned int)(ring->type), ring->mmio_base, + (ring->tx) ? "TX" : "RX", ring->max_used_slots, + ring->nr_slots); + /* Device IRQs are disabled prior entering this function, + * so no need to take care of concurrency with rx handler stuff. + */ + dmacontroller_cleanup(ring); + free_all_descbuffers(ring); + free_ringmemory(ring); + + kfree(ring->txhdr_cache); + kfree(ring->meta); + kfree(ring); +} + +void b43legacy_dma_free(struct b43legacy_wldev *dev) +{ + struct b43legacy_dma *dma; + + if (b43legacy_using_pio(dev)) + return; + dma = &dev->dma; + + b43legacy_destroy_dmaring(dma->rx_ring3); + dma->rx_ring3 = NULL; + b43legacy_destroy_dmaring(dma->rx_ring0); + dma->rx_ring0 = NULL; + + b43legacy_destroy_dmaring(dma->tx_ring5); + dma->tx_ring5 = NULL; + b43legacy_destroy_dmaring(dma->tx_ring4); + dma->tx_ring4 = NULL; + b43legacy_destroy_dmaring(dma->tx_ring3); + dma->tx_ring3 = NULL; + b43legacy_destroy_dmaring(dma->tx_ring2); + dma->tx_ring2 = NULL; + b43legacy_destroy_dmaring(dma->tx_ring1); + dma->tx_ring1 = NULL; + b43legacy_destroy_dmaring(dma->tx_ring0); + dma->tx_ring0 = NULL; +} + +static int b43legacy_dma_set_mask(struct b43legacy_wldev *dev, u64 mask) +{ + u64 orig_mask = mask; + bool fallback = false; + int err; + + /* Try to set the DMA mask. If it fails, try falling back to a + * lower mask, as we can always also support a lower one. */ + while (1) { + err = dma_set_mask_and_coherent(dev->dev->dma_dev, mask); + if (!err) + break; + if (mask == DMA_BIT_MASK(64)) { + mask = DMA_BIT_MASK(32); + fallback = true; + continue; + } + if (mask == DMA_BIT_MASK(32)) { + mask = DMA_BIT_MASK(30); + fallback = true; + continue; + } + b43legacyerr(dev->wl, "The machine/kernel does not support " + "the required %u-bit DMA mask\n", + (unsigned int)dma_mask_to_engine_type(orig_mask)); + return -EOPNOTSUPP; + } + if (fallback) { + b43legacyinfo(dev->wl, "DMA mask fallback from %u-bit to %u-" + "bit\n", + (unsigned int)dma_mask_to_engine_type(orig_mask), + (unsigned int)dma_mask_to_engine_type(mask)); + } + + return 0; +} + +int b43legacy_dma_init(struct b43legacy_wldev *dev) +{ + struct b43legacy_dma *dma = &dev->dma; + struct b43legacy_dmaring *ring; + int err; + u64 dmamask; + enum b43legacy_dmatype type; + + dmamask = supported_dma_mask(dev); + type = dma_mask_to_engine_type(dmamask); + err = b43legacy_dma_set_mask(dev, dmamask); + if (err) { +#ifdef CONFIG_B43LEGACY_PIO + b43legacywarn(dev->wl, "DMA for this device not supported. " + "Falling back to PIO\n"); + dev->__using_pio = true; + return -EAGAIN; +#else + b43legacyerr(dev->wl, "DMA for this device not supported and " + "no PIO support compiled in\n"); + return -EOPNOTSUPP; +#endif + } + dma->translation = ssb_dma_translation(dev->dev); + + err = -ENOMEM; + /* setup TX DMA channels. */ + ring = b43legacy_setup_dmaring(dev, 0, 1, type); + if (!ring) + goto out; + dma->tx_ring0 = ring; + + ring = b43legacy_setup_dmaring(dev, 1, 1, type); + if (!ring) + goto err_destroy_tx0; + dma->tx_ring1 = ring; + + ring = b43legacy_setup_dmaring(dev, 2, 1, type); + if (!ring) + goto err_destroy_tx1; + dma->tx_ring2 = ring; + + ring = b43legacy_setup_dmaring(dev, 3, 1, type); + if (!ring) + goto err_destroy_tx2; + dma->tx_ring3 = ring; + + ring = b43legacy_setup_dmaring(dev, 4, 1, type); + if (!ring) + goto err_destroy_tx3; + dma->tx_ring4 = ring; + + ring = b43legacy_setup_dmaring(dev, 5, 1, type); + if (!ring) + goto err_destroy_tx4; + dma->tx_ring5 = ring; + + /* setup RX DMA channels. */ + ring = b43legacy_setup_dmaring(dev, 0, 0, type); + if (!ring) + goto err_destroy_tx5; + dma->rx_ring0 = ring; + + if (dev->dev->id.revision < 5) { + ring = b43legacy_setup_dmaring(dev, 3, 0, type); + if (!ring) + goto err_destroy_rx0; + dma->rx_ring3 = ring; + } + + b43legacydbg(dev->wl, "%u-bit DMA initialized\n", (unsigned int)type); + err = 0; +out: + return err; + +err_destroy_rx0: + b43legacy_destroy_dmaring(dma->rx_ring0); + dma->rx_ring0 = NULL; +err_destroy_tx5: + b43legacy_destroy_dmaring(dma->tx_ring5); + dma->tx_ring5 = NULL; +err_destroy_tx4: + b43legacy_destroy_dmaring(dma->tx_ring4); + dma->tx_ring4 = NULL; +err_destroy_tx3: + b43legacy_destroy_dmaring(dma->tx_ring3); + dma->tx_ring3 = NULL; +err_destroy_tx2: + b43legacy_destroy_dmaring(dma->tx_ring2); + dma->tx_ring2 = NULL; +err_destroy_tx1: + b43legacy_destroy_dmaring(dma->tx_ring1); + dma->tx_ring1 = NULL; +err_destroy_tx0: + b43legacy_destroy_dmaring(dma->tx_ring0); + dma->tx_ring0 = NULL; + goto out; +} + +/* Generate a cookie for the TX header. */ +static u16 generate_cookie(struct b43legacy_dmaring *ring, + int slot) +{ + u16 cookie = 0x1000; + + /* Use the upper 4 bits of the cookie as + * DMA controller ID and store the slot number + * in the lower 12 bits. + * Note that the cookie must never be 0, as this + * is a special value used in RX path. + */ + switch (ring->index) { + case 0: + cookie = 0xA000; + break; + case 1: + cookie = 0xB000; + break; + case 2: + cookie = 0xC000; + break; + case 3: + cookie = 0xD000; + break; + case 4: + cookie = 0xE000; + break; + case 5: + cookie = 0xF000; + break; + } + B43legacy_WARN_ON(!(((u16)slot & 0xF000) == 0x0000)); + cookie |= (u16)slot; + + return cookie; +} + +/* Inspect a cookie and find out to which controller/slot it belongs. */ +static +struct b43legacy_dmaring *parse_cookie(struct b43legacy_wldev *dev, + u16 cookie, int *slot) +{ + struct b43legacy_dma *dma = &dev->dma; + struct b43legacy_dmaring *ring = NULL; + + switch (cookie & 0xF000) { + case 0xA000: + ring = dma->tx_ring0; + break; + case 0xB000: + ring = dma->tx_ring1; + break; + case 0xC000: + ring = dma->tx_ring2; + break; + case 0xD000: + ring = dma->tx_ring3; + break; + case 0xE000: + ring = dma->tx_ring4; + break; + case 0xF000: + ring = dma->tx_ring5; + break; + default: + B43legacy_WARN_ON(1); + } + *slot = (cookie & 0x0FFF); + B43legacy_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots)); + + return ring; +} + +static int dma_tx_fragment(struct b43legacy_dmaring *ring, + struct sk_buff **in_skb) +{ + struct sk_buff *skb = *in_skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + u8 *header; + int slot, old_top_slot, old_used_slots; + int err; + struct b43legacy_dmadesc32 *desc; + struct b43legacy_dmadesc_meta *meta; + struct b43legacy_dmadesc_meta *meta_hdr; + struct sk_buff *bounce_skb; + +#define SLOTS_PER_PACKET 2 + B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0); + + old_top_slot = ring->current_slot; + old_used_slots = ring->used_slots; + + /* Get a slot for the header. */ + slot = request_slot(ring); + desc = op32_idx2desc(ring, slot, &meta_hdr); + memset(meta_hdr, 0, sizeof(*meta_hdr)); + + header = &(ring->txhdr_cache[slot * sizeof( + struct b43legacy_txhdr_fw3)]); + err = b43legacy_generate_txhdr(ring->dev, header, + skb->data, skb->len, info, + generate_cookie(ring, slot)); + if (unlikely(err)) { + ring->current_slot = old_top_slot; + ring->used_slots = old_used_slots; + return err; + } + + meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, + sizeof(struct b43legacy_txhdr_fw3), 1); + if (b43legacy_dma_mapping_error(ring, meta_hdr->dmaaddr, + sizeof(struct b43legacy_txhdr_fw3), 1)) { + ring->current_slot = old_top_slot; + ring->used_slots = old_used_slots; + return -EIO; + } + op32_fill_descriptor(ring, desc, meta_hdr->dmaaddr, + sizeof(struct b43legacy_txhdr_fw3), 1, 0, 0); + + /* Get a slot for the payload. */ + slot = request_slot(ring); + desc = op32_idx2desc(ring, slot, &meta); + memset(meta, 0, sizeof(*meta)); + + meta->skb = skb; + meta->is_last_fragment = true; + + meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); + /* create a bounce buffer in zone_dma on mapping failure. */ + if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { + bounce_skb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); + if (!bounce_skb) { + ring->current_slot = old_top_slot; + ring->used_slots = old_used_slots; + err = -ENOMEM; + goto out_unmap_hdr; + } + + memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len); + memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb)); + bounce_skb->dev = skb->dev; + skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb)); + info = IEEE80211_SKB_CB(bounce_skb); + + dev_kfree_skb_any(skb); + skb = bounce_skb; + *in_skb = bounce_skb; + meta->skb = skb; + meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); + if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { + ring->current_slot = old_top_slot; + ring->used_slots = old_used_slots; + err = -EIO; + goto out_free_bounce; + } + } + + op32_fill_descriptor(ring, desc, meta->dmaaddr, + skb->len, 0, 1, 1); + + wmb(); /* previous stuff MUST be done */ + /* Now transfer the whole frame. */ + op32_poke_tx(ring, next_slot(ring, slot)); + return 0; + +out_free_bounce: + dev_kfree_skb_any(skb); +out_unmap_hdr: + unmap_descbuffer(ring, meta_hdr->dmaaddr, + sizeof(struct b43legacy_txhdr_fw3), 1); + return err; +} + +static inline +int should_inject_overflow(struct b43legacy_dmaring *ring) +{ +#ifdef CONFIG_B43LEGACY_DEBUG + if (unlikely(b43legacy_debug(ring->dev, + B43legacy_DBG_DMAOVERFLOW))) { + /* Check if we should inject another ringbuffer overflow + * to test handling of this situation in the stack. */ + unsigned long next_overflow; + + next_overflow = ring->last_injected_overflow + HZ; + if (time_after(jiffies, next_overflow)) { + ring->last_injected_overflow = jiffies; + b43legacydbg(ring->dev->wl, + "Injecting TX ring overflow on " + "DMA controller %d\n", ring->index); + return 1; + } + } +#endif /* CONFIG_B43LEGACY_DEBUG */ + return 0; +} + +int b43legacy_dma_tx(struct b43legacy_wldev *dev, + struct sk_buff *skb) +{ + struct b43legacy_dmaring *ring; + int err = 0; + + ring = priority_to_txring(dev, skb_get_queue_mapping(skb)); + B43legacy_WARN_ON(!ring->tx); + + if (unlikely(ring->stopped)) { + /* We get here only because of a bug in mac80211. + * Because of a race, one packet may be queued after + * the queue is stopped, thus we got called when we shouldn't. + * For now, just refuse the transmit. */ + if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) + b43legacyerr(dev->wl, "Packet after queue stopped\n"); + return -ENOSPC; + } + + if (unlikely(WARN_ON(free_slots(ring) < SLOTS_PER_PACKET))) { + /* If we get here, we have a real error with the queue + * full, but queues not stopped. */ + b43legacyerr(dev->wl, "DMA queue overflow\n"); + return -ENOSPC; + } + + /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing + * into the skb data or cb now. */ + err = dma_tx_fragment(ring, &skb); + if (unlikely(err == -ENOKEY)) { + /* Drop this packet, as we don't have the encryption key + * anymore and must not transmit it unencrypted. */ + dev_kfree_skb_any(skb); + return 0; + } + if (unlikely(err)) { + b43legacyerr(dev->wl, "DMA tx mapping failure\n"); + return err; + } + if ((free_slots(ring) < SLOTS_PER_PACKET) || + should_inject_overflow(ring)) { + /* This TX ring is full. */ + unsigned int skb_mapping = skb_get_queue_mapping(skb); + ieee80211_stop_queue(dev->wl->hw, skb_mapping); + dev->wl->tx_queue_stopped[skb_mapping] = 1; + ring->stopped = true; + if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) + b43legacydbg(dev->wl, "Stopped TX ring %d\n", + ring->index); + } + return err; +} + +void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, + const struct b43legacy_txstatus *status) +{ + struct b43legacy_dmaring *ring; + struct b43legacy_dmadesc_meta *meta; + int retry_limit; + int slot; + int firstused; + + ring = parse_cookie(dev, status->cookie, &slot); + if (unlikely(!ring)) + return; + B43legacy_WARN_ON(!ring->tx); + + /* Sanity check: TX packets are processed in-order on one ring. + * Check if the slot deduced from the cookie really is the first + * used slot. */ + firstused = ring->current_slot - ring->used_slots + 1; + if (firstused < 0) + firstused = ring->nr_slots + firstused; + if (unlikely(slot != firstused)) { + /* This possibly is a firmware bug and will result in + * malfunction, memory leaks and/or stall of DMA functionality. + */ + b43legacydbg(dev->wl, "Out of order TX status report on DMA " + "ring %d. Expected %d, but got %d\n", + ring->index, firstused, slot); + return; + } + + while (1) { + B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); + op32_idx2desc(ring, slot, &meta); + + if (meta->skb) + unmap_descbuffer(ring, meta->dmaaddr, + meta->skb->len, 1); + else + unmap_descbuffer(ring, meta->dmaaddr, + sizeof(struct b43legacy_txhdr_fw3), + 1); + + if (meta->is_last_fragment) { + struct ieee80211_tx_info *info; + BUG_ON(!meta->skb); + info = IEEE80211_SKB_CB(meta->skb); + + /* preserve the confiured retry limit before clearing the status + * The xmit function has overwritten the rc's value with the actual + * retry limit done by the hardware */ + retry_limit = info->status.rates[0].count; + ieee80211_tx_info_clear_status(info); + + if (status->acked) + info->flags |= IEEE80211_TX_STAT_ACK; + + if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) { + /* + * If the short retries (RTS, not data frame) have exceeded + * the limit, the hw will not have tried the selected rate, + * but will have used the fallback rate instead. + * Don't let the rate control count attempts for the selected + * rate in this case, otherwise the statistics will be off. + */ + info->status.rates[0].count = 0; + info->status.rates[1].count = status->frame_count; + } else { + if (status->frame_count > retry_limit) { + info->status.rates[0].count = retry_limit; + info->status.rates[1].count = status->frame_count - + retry_limit; + + } else { + info->status.rates[0].count = status->frame_count; + info->status.rates[1].idx = -1; + } + } + + /* Call back to inform the ieee80211 subsystem about the + * status of the transmission. + * Some fields of txstat are already filled in dma_tx(). + */ + ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb); + /* skb is freed by ieee80211_tx_status_irqsafe() */ + meta->skb = NULL; + } else { + /* No need to call free_descriptor_buffer here, as + * this is only the txhdr, which is not allocated. + */ + B43legacy_WARN_ON(meta->skb != NULL); + } + + /* Everything unmapped and free'd. So it's not used anymore. */ + ring->used_slots--; + + if (meta->is_last_fragment) + break; + slot = next_slot(ring, slot); + } + dev->stats.last_tx = jiffies; + if (ring->stopped) { + B43legacy_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET); + ring->stopped = false; + } + + if (dev->wl->tx_queue_stopped[ring->queue_prio]) { + dev->wl->tx_queue_stopped[ring->queue_prio] = 0; + } else { + /* If the driver queue is running wake the corresponding + * mac80211 queue. */ + ieee80211_wake_queue(dev->wl->hw, ring->queue_prio); + if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) + b43legacydbg(dev->wl, "Woke up TX ring %d\n", + ring->index); + } + /* Add work to the queue. */ + ieee80211_queue_work(dev->wl->hw, &dev->wl->tx_work); +} + +static void dma_rx(struct b43legacy_dmaring *ring, + int *slot) +{ + struct b43legacy_dmadesc32 *desc; + struct b43legacy_dmadesc_meta *meta; + struct b43legacy_rxhdr_fw3 *rxhdr; + struct sk_buff *skb; + u16 len; + int err; + dma_addr_t dmaaddr; + + desc = op32_idx2desc(ring, *slot, &meta); + + sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); + skb = meta->skb; + + if (ring->index == 3) { + /* We received an xmit status. */ + struct b43legacy_hwtxstatus *hw = + (struct b43legacy_hwtxstatus *)skb->data; + int i = 0; + + while (hw->cookie == 0) { + if (i > 100) + break; + i++; + udelay(2); + barrier(); + } + b43legacy_handle_hwtxstatus(ring->dev, hw); + /* recycle the descriptor buffer. */ + sync_descbuffer_for_device(ring, meta->dmaaddr, + ring->rx_buffersize); + + return; + } + rxhdr = (struct b43legacy_rxhdr_fw3 *)skb->data; + len = le16_to_cpu(rxhdr->frame_len); + if (len == 0) { + int i = 0; + + do { + udelay(2); + barrier(); + len = le16_to_cpu(rxhdr->frame_len); + } while (len == 0 && i++ < 5); + if (unlikely(len == 0)) { + /* recycle the descriptor buffer. */ + sync_descbuffer_for_device(ring, meta->dmaaddr, + ring->rx_buffersize); + goto drop; + } + } + if (unlikely(len > ring->rx_buffersize)) { + /* The data did not fit into one descriptor buffer + * and is split over multiple buffers. + * This should never happen, as we try to allocate buffers + * big enough. So simply ignore this packet. + */ + int cnt = 0; + s32 tmp = len; + + while (1) { + desc = op32_idx2desc(ring, *slot, &meta); + /* recycle the descriptor buffer. */ + sync_descbuffer_for_device(ring, meta->dmaaddr, + ring->rx_buffersize); + *slot = next_slot(ring, *slot); + cnt++; + tmp -= ring->rx_buffersize; + if (tmp <= 0) + break; + } + b43legacyerr(ring->dev->wl, "DMA RX buffer too small " + "(len: %u, buffer: %u, nr-dropped: %d)\n", + len, ring->rx_buffersize, cnt); + goto drop; + } + + dmaaddr = meta->dmaaddr; + err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); + if (unlikely(err)) { + b43legacydbg(ring->dev->wl, "DMA RX: setup_rx_descbuffer()" + " failed\n"); + sync_descbuffer_for_device(ring, dmaaddr, + ring->rx_buffersize); + goto drop; + } + + unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); + skb_put(skb, len + ring->frameoffset); + skb_pull(skb, ring->frameoffset); + + b43legacy_rx(ring->dev, skb, rxhdr); +drop: + return; +} + +void b43legacy_dma_rx(struct b43legacy_dmaring *ring) +{ + int slot; + int current_slot; + int used_slots = 0; + + B43legacy_WARN_ON(ring->tx); + current_slot = op32_get_current_rxslot(ring); + B43legacy_WARN_ON(!(current_slot >= 0 && current_slot < + ring->nr_slots)); + + slot = ring->current_slot; + for (; slot != current_slot; slot = next_slot(ring, slot)) { + dma_rx(ring, &slot); + update_max_used_slots(ring, ++used_slots); + } + op32_set_current_rxslot(ring, slot); + ring->current_slot = slot; +} + +static void b43legacy_dma_tx_suspend_ring(struct b43legacy_dmaring *ring) +{ + B43legacy_WARN_ON(!ring->tx); + op32_tx_suspend(ring); +} + +static void b43legacy_dma_tx_resume_ring(struct b43legacy_dmaring *ring) +{ + B43legacy_WARN_ON(!ring->tx); + op32_tx_resume(ring); +} + +void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev) +{ + b43legacy_power_saving_ctl_bits(dev, -1, 1); + b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring0); + b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring1); + b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring2); + b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring3); + b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring4); + b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring5); +} + +void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev) +{ + b43legacy_dma_tx_resume_ring(dev->dma.tx_ring5); + b43legacy_dma_tx_resume_ring(dev->dma.tx_ring4); + b43legacy_dma_tx_resume_ring(dev->dma.tx_ring3); + b43legacy_dma_tx_resume_ring(dev->dma.tx_ring2); + b43legacy_dma_tx_resume_ring(dev->dma.tx_ring1); + b43legacy_dma_tx_resume_ring(dev->dma.tx_ring0); + b43legacy_power_saving_ctl_bits(dev, -1, -1); +} diff --git a/drivers/net/wireless/broadcom/b43legacy/dma.h b/drivers/net/wireless/broadcom/b43legacy/dma.h new file mode 100644 index 000000000000..c3282f906bc7 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/dma.h @@ -0,0 +1,231 @@ +#ifndef B43legacy_DMA_H_ +#define B43legacy_DMA_H_ + +#include +#include +#include +#include +#include + +#include "b43legacy.h" + + +/* DMA-Interrupt reasons. */ +#define B43legacy_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \ + | (1 << 14) | (1 << 15)) +#define B43legacy_DMAIRQ_NONFATALMASK (1 << 13) +#define B43legacy_DMAIRQ_RX_DONE (1 << 16) + + +/*** 32-bit DMA Engine. ***/ + +/* 32-bit DMA controller registers. */ +#define B43legacy_DMA32_TXCTL 0x00 +#define B43legacy_DMA32_TXENABLE 0x00000001 +#define B43legacy_DMA32_TXSUSPEND 0x00000002 +#define B43legacy_DMA32_TXLOOPBACK 0x00000004 +#define B43legacy_DMA32_TXFLUSH 0x00000010 +#define B43legacy_DMA32_TXADDREXT_MASK 0x00030000 +#define B43legacy_DMA32_TXADDREXT_SHIFT 16 +#define B43legacy_DMA32_TXRING 0x04 +#define B43legacy_DMA32_TXINDEX 0x08 +#define B43legacy_DMA32_TXSTATUS 0x0C +#define B43legacy_DMA32_TXDPTR 0x00000FFF +#define B43legacy_DMA32_TXSTATE 0x0000F000 +#define B43legacy_DMA32_TXSTAT_DISABLED 0x00000000 +#define B43legacy_DMA32_TXSTAT_ACTIVE 0x00001000 +#define B43legacy_DMA32_TXSTAT_IDLEWAIT 0x00002000 +#define B43legacy_DMA32_TXSTAT_STOPPED 0x00003000 +#define B43legacy_DMA32_TXSTAT_SUSP 0x00004000 +#define B43legacy_DMA32_TXERROR 0x000F0000 +#define B43legacy_DMA32_TXERR_NOERR 0x00000000 +#define B43legacy_DMA32_TXERR_PROT 0x00010000 +#define B43legacy_DMA32_TXERR_UNDERRUN 0x00020000 +#define B43legacy_DMA32_TXERR_BUFREAD 0x00030000 +#define B43legacy_DMA32_TXERR_DESCREAD 0x00040000 +#define B43legacy_DMA32_TXACTIVE 0xFFF00000 +#define B43legacy_DMA32_RXCTL 0x10 +#define B43legacy_DMA32_RXENABLE 0x00000001 +#define B43legacy_DMA32_RXFROFF_MASK 0x000000FE +#define B43legacy_DMA32_RXFROFF_SHIFT 1 +#define B43legacy_DMA32_RXDIRECTFIFO 0x00000100 +#define B43legacy_DMA32_RXADDREXT_MASK 0x00030000 +#define B43legacy_DMA32_RXADDREXT_SHIFT 16 +#define B43legacy_DMA32_RXRING 0x14 +#define B43legacy_DMA32_RXINDEX 0x18 +#define B43legacy_DMA32_RXSTATUS 0x1C +#define B43legacy_DMA32_RXDPTR 0x00000FFF +#define B43legacy_DMA32_RXSTATE 0x0000F000 +#define B43legacy_DMA32_RXSTAT_DISABLED 0x00000000 +#define B43legacy_DMA32_RXSTAT_ACTIVE 0x00001000 +#define B43legacy_DMA32_RXSTAT_IDLEWAIT 0x00002000 +#define B43legacy_DMA32_RXSTAT_STOPPED 0x00003000 +#define B43legacy_DMA32_RXERROR 0x000F0000 +#define B43legacy_DMA32_RXERR_NOERR 0x00000000 +#define B43legacy_DMA32_RXERR_PROT 0x00010000 +#define B43legacy_DMA32_RXERR_OVERFLOW 0x00020000 +#define B43legacy_DMA32_RXERR_BUFWRITE 0x00030000 +#define B43legacy_DMA32_RXERR_DESCREAD 0x00040000 +#define B43legacy_DMA32_RXACTIVE 0xFFF00000 + +/* 32-bit DMA descriptor. */ +struct b43legacy_dmadesc32 { + __le32 control; + __le32 address; +} __packed; +#define B43legacy_DMA32_DCTL_BYTECNT 0x00001FFF +#define B43legacy_DMA32_DCTL_ADDREXT_MASK 0x00030000 +#define B43legacy_DMA32_DCTL_ADDREXT_SHIFT 16 +#define B43legacy_DMA32_DCTL_DTABLEEND 0x10000000 +#define B43legacy_DMA32_DCTL_IRQ 0x20000000 +#define B43legacy_DMA32_DCTL_FRAMEEND 0x40000000 +#define B43legacy_DMA32_DCTL_FRAMESTART 0x80000000 + + +/* Misc DMA constants */ +#define B43legacy_DMA_RINGMEMSIZE PAGE_SIZE +#define B43legacy_DMA0_RX_FRAMEOFFSET 30 +#define B43legacy_DMA3_RX_FRAMEOFFSET 0 + + +/* DMA engine tuning knobs */ +#define B43legacy_TXRING_SLOTS 128 +#define B43legacy_RXRING_SLOTS 64 +#define B43legacy_DMA0_RX_BUFFERSIZE (2304 + 100) +#define B43legacy_DMA3_RX_BUFFERSIZE 16 + + + +#ifdef CONFIG_B43LEGACY_DMA + + +struct sk_buff; +struct b43legacy_private; +struct b43legacy_txstatus; + + +struct b43legacy_dmadesc_meta { + /* The kernel DMA-able buffer. */ + struct sk_buff *skb; + /* DMA base bus-address of the descriptor buffer. */ + dma_addr_t dmaaddr; + /* ieee80211 TX status. Only used once per 802.11 frag. */ + bool is_last_fragment; +}; + +enum b43legacy_dmatype { + B43legacy_DMA_30BIT = 30, + B43legacy_DMA_32BIT = 32, +}; + +struct b43legacy_dmaring { + /* Kernel virtual base address of the ring memory. */ + void *descbase; + /* Meta data about all descriptors. */ + struct b43legacy_dmadesc_meta *meta; + /* Cache of TX headers for each slot. + * This is to avoid an allocation on each TX. + * This is NULL for an RX ring. + */ + u8 *txhdr_cache; + /* (Unadjusted) DMA base bus-address of the ring memory. */ + dma_addr_t dmabase; + /* Number of descriptor slots in the ring. */ + int nr_slots; + /* Number of used descriptor slots. */ + int used_slots; + /* Currently used slot in the ring. */ + int current_slot; + /* Frameoffset in octets. */ + u32 frameoffset; + /* Descriptor buffer size. */ + u16 rx_buffersize; + /* The MMIO base register of the DMA controller. */ + u16 mmio_base; + /* DMA controller index number (0-5). */ + int index; + /* Boolean. Is this a TX ring? */ + bool tx; + /* The type of DMA engine used. */ + enum b43legacy_dmatype type; + /* Boolean. Is this ring stopped at ieee80211 level? */ + bool stopped; + /* The QOS priority assigned to this ring. Only used for TX rings. + * This is the mac80211 "queue" value. */ + u8 queue_prio; + struct b43legacy_wldev *dev; +#ifdef CONFIG_B43LEGACY_DEBUG + /* Maximum number of used slots. */ + int max_used_slots; + /* Last time we injected a ring overflow. */ + unsigned long last_injected_overflow; +#endif /* CONFIG_B43LEGACY_DEBUG*/ +}; + + +static inline +u32 b43legacy_dma_read(struct b43legacy_dmaring *ring, + u16 offset) +{ + return b43legacy_read32(ring->dev, ring->mmio_base + offset); +} + +static inline +void b43legacy_dma_write(struct b43legacy_dmaring *ring, + u16 offset, u32 value) +{ + b43legacy_write32(ring->dev, ring->mmio_base + offset, value); +} + + +int b43legacy_dma_init(struct b43legacy_wldev *dev); +void b43legacy_dma_free(struct b43legacy_wldev *dev); + +void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev); +void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev); + +int b43legacy_dma_tx(struct b43legacy_wldev *dev, + struct sk_buff *skb); +void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, + const struct b43legacy_txstatus *status); + +void b43legacy_dma_rx(struct b43legacy_dmaring *ring); + +#else /* CONFIG_B43LEGACY_DMA */ + + +static inline +int b43legacy_dma_init(struct b43legacy_wldev *dev) +{ + return 0; +} +static inline +void b43legacy_dma_free(struct b43legacy_wldev *dev) +{ +} +static inline +int b43legacy_dma_tx(struct b43legacy_wldev *dev, + struct sk_buff *skb) +{ + return 0; +} +static inline +void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, + const struct b43legacy_txstatus *status) +{ +} +static inline +void b43legacy_dma_rx(struct b43legacy_dmaring *ring) +{ +} +static inline +void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev) +{ +} +static inline +void b43legacy_dma_tx_resume(struct b43legacy_wldev *dev) +{ +} + +#endif /* CONFIG_B43LEGACY_DMA */ +#endif /* B43legacy_DMA_H_ */ diff --git a/drivers/net/wireless/broadcom/b43legacy/ilt.c b/drivers/net/wireless/broadcom/b43legacy/ilt.c new file mode 100644 index 000000000000..ee5682e54204 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/ilt.c @@ -0,0 +1,336 @@ +/* + + Broadcom B43legacy wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43legacy.h" +#include "ilt.h" +#include "phy.h" + + +/**** Initial Internal Lookup Tables ****/ + +const u32 b43legacy_ilt_rotor[B43legacy_ILT_ROTOR_SIZE] = { + 0xFEB93FFD, 0xFEC63FFD, /* 0 */ + 0xFED23FFD, 0xFEDF3FFD, + 0xFEEC3FFE, 0xFEF83FFE, + 0xFF053FFE, 0xFF113FFE, + 0xFF1E3FFE, 0xFF2A3FFF, /* 8 */ + 0xFF373FFF, 0xFF443FFF, + 0xFF503FFF, 0xFF5D3FFF, + 0xFF693FFF, 0xFF763FFF, + 0xFF824000, 0xFF8F4000, /* 16 */ + 0xFF9B4000, 0xFFA84000, + 0xFFB54000, 0xFFC14000, + 0xFFCE4000, 0xFFDA4000, + 0xFFE74000, 0xFFF34000, /* 24 */ + 0x00004000, 0x000D4000, + 0x00194000, 0x00264000, + 0x00324000, 0x003F4000, + 0x004B4000, 0x00584000, /* 32 */ + 0x00654000, 0x00714000, + 0x007E4000, 0x008A3FFF, + 0x00973FFF, 0x00A33FFF, + 0x00B03FFF, 0x00BC3FFF, /* 40 */ + 0x00C93FFF, 0x00D63FFF, + 0x00E23FFE, 0x00EF3FFE, + 0x00FB3FFE, 0x01083FFE, + 0x01143FFE, 0x01213FFD, /* 48 */ + 0x012E3FFD, 0x013A3FFD, + 0x01473FFD, +}; + +const u32 b43legacy_ilt_retard[B43legacy_ILT_RETARD_SIZE] = { + 0xDB93CB87, 0xD666CF64, /* 0 */ + 0xD1FDD358, 0xCDA6D826, + 0xCA38DD9F, 0xC729E2B4, + 0xC469E88E, 0xC26AEE2B, + 0xC0DEF46C, 0xC073FA62, /* 8 */ + 0xC01D00D5, 0xC0760743, + 0xC1560D1E, 0xC2E51369, + 0xC4ED18FF, 0xC7AC1ED7, + 0xCB2823B2, 0xCEFA28D9, /* 16 */ + 0xD2F62D3F, 0xD7BB3197, + 0xDCE53568, 0xE1FE3875, + 0xE7D13B35, 0xED663D35, + 0xF39B3EC4, 0xF98E3FA7, /* 24 */ + 0x00004000, 0x06723FA7, + 0x0C653EC4, 0x129A3D35, + 0x182F3B35, 0x1E023875, + 0x231B3568, 0x28453197, /* 32 */ + 0x2D0A2D3F, 0x310628D9, + 0x34D823B2, 0x38541ED7, + 0x3B1318FF, 0x3D1B1369, + 0x3EAA0D1E, 0x3F8A0743, /* 40 */ + 0x3FE300D5, 0x3F8DFA62, + 0x3F22F46C, 0x3D96EE2B, + 0x3B97E88E, 0x38D7E2B4, + 0x35C8DD9F, 0x325AD826, /* 48 */ + 0x2E03D358, 0x299ACF64, + 0x246DCB87, +}; + +const u16 b43legacy_ilt_finefreqa[B43legacy_ILT_FINEFREQA_SIZE] = { + 0x0082, 0x0082, 0x0102, 0x0182, /* 0 */ + 0x0202, 0x0282, 0x0302, 0x0382, + 0x0402, 0x0482, 0x0502, 0x0582, + 0x05E2, 0x0662, 0x06E2, 0x0762, + 0x07E2, 0x0842, 0x08C2, 0x0942, /* 16 */ + 0x09C2, 0x0A22, 0x0AA2, 0x0B02, + 0x0B82, 0x0BE2, 0x0C62, 0x0CC2, + 0x0D42, 0x0DA2, 0x0E02, 0x0E62, + 0x0EE2, 0x0F42, 0x0FA2, 0x1002, /* 32 */ + 0x1062, 0x10C2, 0x1122, 0x1182, + 0x11E2, 0x1242, 0x12A2, 0x12E2, + 0x1342, 0x13A2, 0x1402, 0x1442, + 0x14A2, 0x14E2, 0x1542, 0x1582, /* 48 */ + 0x15E2, 0x1622, 0x1662, 0x16C1, + 0x1701, 0x1741, 0x1781, 0x17E1, + 0x1821, 0x1861, 0x18A1, 0x18E1, + 0x1921, 0x1961, 0x19A1, 0x19E1, /* 64 */ + 0x1A21, 0x1A61, 0x1AA1, 0x1AC1, + 0x1B01, 0x1B41, 0x1B81, 0x1BA1, + 0x1BE1, 0x1C21, 0x1C41, 0x1C81, + 0x1CA1, 0x1CE1, 0x1D01, 0x1D41, /* 80 */ + 0x1D61, 0x1DA1, 0x1DC1, 0x1E01, + 0x1E21, 0x1E61, 0x1E81, 0x1EA1, + 0x1EE1, 0x1F01, 0x1F21, 0x1F41, + 0x1F81, 0x1FA1, 0x1FC1, 0x1FE1, /* 96 */ + 0x2001, 0x2041, 0x2061, 0x2081, + 0x20A1, 0x20C1, 0x20E1, 0x2101, + 0x2121, 0x2141, 0x2161, 0x2181, + 0x21A1, 0x21C1, 0x21E1, 0x2201, /* 112 */ + 0x2221, 0x2241, 0x2261, 0x2281, + 0x22A1, 0x22C1, 0x22C1, 0x22E1, + 0x2301, 0x2321, 0x2341, 0x2361, + 0x2361, 0x2381, 0x23A1, 0x23C1, /* 128 */ + 0x23E1, 0x23E1, 0x2401, 0x2421, + 0x2441, 0x2441, 0x2461, 0x2481, + 0x2481, 0x24A1, 0x24C1, 0x24C1, + 0x24E1, 0x2501, 0x2501, 0x2521, /* 144 */ + 0x2541, 0x2541, 0x2561, 0x2561, + 0x2581, 0x25A1, 0x25A1, 0x25C1, + 0x25C1, 0x25E1, 0x2601, 0x2601, + 0x2621, 0x2621, 0x2641, 0x2641, /* 160 */ + 0x2661, 0x2661, 0x2681, 0x2681, + 0x26A1, 0x26A1, 0x26C1, 0x26C1, + 0x26E1, 0x26E1, 0x2701, 0x2701, + 0x2721, 0x2721, 0x2740, 0x2740, /* 176 */ + 0x2760, 0x2760, 0x2780, 0x2780, + 0x2780, 0x27A0, 0x27A0, 0x27C0, + 0x27C0, 0x27E0, 0x27E0, 0x27E0, + 0x2800, 0x2800, 0x2820, 0x2820, /* 192 */ + 0x2820, 0x2840, 0x2840, 0x2840, + 0x2860, 0x2860, 0x2880, 0x2880, + 0x2880, 0x28A0, 0x28A0, 0x28A0, + 0x28C0, 0x28C0, 0x28C0, 0x28E0, /* 208 */ + 0x28E0, 0x28E0, 0x2900, 0x2900, + 0x2900, 0x2920, 0x2920, 0x2920, + 0x2940, 0x2940, 0x2940, 0x2960, + 0x2960, 0x2960, 0x2960, 0x2980, /* 224 */ + 0x2980, 0x2980, 0x29A0, 0x29A0, + 0x29A0, 0x29A0, 0x29C0, 0x29C0, + 0x29C0, 0x29E0, 0x29E0, 0x29E0, + 0x29E0, 0x2A00, 0x2A00, 0x2A00, /* 240 */ + 0x2A00, 0x2A20, 0x2A20, 0x2A20, + 0x2A20, 0x2A40, 0x2A40, 0x2A40, + 0x2A40, 0x2A60, 0x2A60, 0x2A60, +}; + +const u16 b43legacy_ilt_finefreqg[B43legacy_ILT_FINEFREQG_SIZE] = { + 0x0089, 0x02E9, 0x0409, 0x04E9, /* 0 */ + 0x05A9, 0x0669, 0x0709, 0x0789, + 0x0829, 0x08A9, 0x0929, 0x0989, + 0x0A09, 0x0A69, 0x0AC9, 0x0B29, + 0x0BA9, 0x0BE9, 0x0C49, 0x0CA9, /* 16 */ + 0x0D09, 0x0D69, 0x0DA9, 0x0E09, + 0x0E69, 0x0EA9, 0x0F09, 0x0F49, + 0x0FA9, 0x0FE9, 0x1029, 0x1089, + 0x10C9, 0x1109, 0x1169, 0x11A9, /* 32 */ + 0x11E9, 0x1229, 0x1289, 0x12C9, + 0x1309, 0x1349, 0x1389, 0x13C9, + 0x1409, 0x1449, 0x14A9, 0x14E9, + 0x1529, 0x1569, 0x15A9, 0x15E9, /* 48 */ + 0x1629, 0x1669, 0x16A9, 0x16E8, + 0x1728, 0x1768, 0x17A8, 0x17E8, + 0x1828, 0x1868, 0x18A8, 0x18E8, + 0x1928, 0x1968, 0x19A8, 0x19E8, /* 64 */ + 0x1A28, 0x1A68, 0x1AA8, 0x1AE8, + 0x1B28, 0x1B68, 0x1BA8, 0x1BE8, + 0x1C28, 0x1C68, 0x1CA8, 0x1CE8, + 0x1D28, 0x1D68, 0x1DC8, 0x1E08, /* 80 */ + 0x1E48, 0x1E88, 0x1EC8, 0x1F08, + 0x1F48, 0x1F88, 0x1FE8, 0x2028, + 0x2068, 0x20A8, 0x2108, 0x2148, + 0x2188, 0x21C8, 0x2228, 0x2268, /* 96 */ + 0x22C8, 0x2308, 0x2348, 0x23A8, + 0x23E8, 0x2448, 0x24A8, 0x24E8, + 0x2548, 0x25A8, 0x2608, 0x2668, + 0x26C8, 0x2728, 0x2787, 0x27E7, /* 112 */ + 0x2847, 0x28C7, 0x2947, 0x29A7, + 0x2A27, 0x2AC7, 0x2B47, 0x2BE7, + 0x2CA7, 0x2D67, 0x2E47, 0x2F67, + 0x3247, 0x3526, 0x3646, 0x3726, /* 128 */ + 0x3806, 0x38A6, 0x3946, 0x39E6, + 0x3A66, 0x3AE6, 0x3B66, 0x3BC6, + 0x3C45, 0x3CA5, 0x3D05, 0x3D85, + 0x3DE5, 0x3E45, 0x3EA5, 0x3EE5, /* 144 */ + 0x3F45, 0x3FA5, 0x4005, 0x4045, + 0x40A5, 0x40E5, 0x4145, 0x4185, + 0x41E5, 0x4225, 0x4265, 0x42C5, + 0x4305, 0x4345, 0x43A5, 0x43E5, /* 160 */ + 0x4424, 0x4464, 0x44C4, 0x4504, + 0x4544, 0x4584, 0x45C4, 0x4604, + 0x4644, 0x46A4, 0x46E4, 0x4724, + 0x4764, 0x47A4, 0x47E4, 0x4824, /* 176 */ + 0x4864, 0x48A4, 0x48E4, 0x4924, + 0x4964, 0x49A4, 0x49E4, 0x4A24, + 0x4A64, 0x4AA4, 0x4AE4, 0x4B23, + 0x4B63, 0x4BA3, 0x4BE3, 0x4C23, /* 192 */ + 0x4C63, 0x4CA3, 0x4CE3, 0x4D23, + 0x4D63, 0x4DA3, 0x4DE3, 0x4E23, + 0x4E63, 0x4EA3, 0x4EE3, 0x4F23, + 0x4F63, 0x4FC3, 0x5003, 0x5043, /* 208 */ + 0x5083, 0x50C3, 0x5103, 0x5143, + 0x5183, 0x51E2, 0x5222, 0x5262, + 0x52A2, 0x52E2, 0x5342, 0x5382, + 0x53C2, 0x5402, 0x5462, 0x54A2, /* 224 */ + 0x5502, 0x5542, 0x55A2, 0x55E2, + 0x5642, 0x5682, 0x56E2, 0x5722, + 0x5782, 0x57E1, 0x5841, 0x58A1, + 0x5901, 0x5961, 0x59C1, 0x5A21, /* 240 */ + 0x5AA1, 0x5B01, 0x5B81, 0x5BE1, + 0x5C61, 0x5D01, 0x5D80, 0x5E20, + 0x5EE0, 0x5FA0, 0x6080, 0x61C0, +}; + +const u16 b43legacy_ilt_noisea2[B43legacy_ILT_NOISEA2_SIZE] = { + 0x0001, 0x0001, 0x0001, 0xFFFE, + 0xFFFE, 0x3FFF, 0x1000, 0x0393, +}; + +const u16 b43legacy_ilt_noisea3[B43legacy_ILT_NOISEA3_SIZE] = { + 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, + 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, +}; + +const u16 b43legacy_ilt_noiseg1[B43legacy_ILT_NOISEG1_SIZE] = { + 0x013C, 0x01F5, 0x031A, 0x0631, + 0x0001, 0x0001, 0x0001, 0x0001, +}; + +const u16 b43legacy_ilt_noiseg2[B43legacy_ILT_NOISEG2_SIZE] = { + 0x5484, 0x3C40, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, +}; + +const u16 b43legacy_ilt_noisescaleg1[B43legacy_ILT_NOISESCALEG_SIZE] = { + 0x6C77, 0x5162, 0x3B40, 0x3335, /* 0 */ + 0x2F2D, 0x2A2A, 0x2527, 0x1F21, + 0x1A1D, 0x1719, 0x1616, 0x1414, + 0x1414, 0x1400, 0x1414, 0x1614, + 0x1716, 0x1A19, 0x1F1D, 0x2521, /* 16 */ + 0x2A27, 0x2F2A, 0x332D, 0x3B35, + 0x5140, 0x6C62, 0x0077, +}; + +const u16 b43legacy_ilt_noisescaleg2[B43legacy_ILT_NOISESCALEG_SIZE] = { + 0xD8DD, 0xCBD4, 0xBCC0, 0XB6B7, /* 0 */ + 0xB2B0, 0xADAD, 0xA7A9, 0x9FA1, + 0x969B, 0x9195, 0x8F8F, 0x8A8A, + 0x8A8A, 0x8A00, 0x8A8A, 0x8F8A, + 0x918F, 0x9695, 0x9F9B, 0xA7A1, /* 16 */ + 0xADA9, 0xB2AD, 0xB6B0, 0xBCB7, + 0xCBC0, 0xD8D4, 0x00DD, +}; + +const u16 b43legacy_ilt_noisescaleg3[B43legacy_ILT_NOISESCALEG_SIZE] = { + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 0 */ + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA400, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 16 */ + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0x00A4, +}; + +const u16 b43legacy_ilt_sigmasqr1[B43legacy_ILT_SIGMASQR_SIZE] = { + 0x007A, 0x0075, 0x0071, 0x006C, /* 0 */ + 0x0067, 0x0063, 0x005E, 0x0059, + 0x0054, 0x0050, 0x004B, 0x0046, + 0x0042, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, /* 16 */ + 0x003D, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x0000, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, /* 32 */ + 0x003D, 0x003D, 0x003D, 0x003D, + 0x0042, 0x0046, 0x004B, 0x0050, + 0x0054, 0x0059, 0x005E, 0x0063, + 0x0067, 0x006C, 0x0071, 0x0075, /* 48 */ + 0x007A, +}; + +const u16 b43legacy_ilt_sigmasqr2[B43legacy_ILT_SIGMASQR_SIZE] = { + 0x00DE, 0x00DC, 0x00DA, 0x00D8, /* 0 */ + 0x00D6, 0x00D4, 0x00D2, 0x00CF, + 0x00CD, 0x00CA, 0x00C7, 0x00C4, + 0x00C1, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 16 */ + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x0000, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 32 */ + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00C1, 0x00C4, 0x00C7, 0x00CA, + 0x00CD, 0x00CF, 0x00D2, 0x00D4, + 0x00D6, 0x00D8, 0x00DA, 0x00DC, /* 48 */ + 0x00DE, +}; + +/**** Helper functions to access the device Internal Lookup Tables ****/ + +void b43legacy_ilt_write(struct b43legacy_wldev *dev, u16 offset, u16 val) +{ + b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset); + mmiowb(); + b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA1, val); +} + +void b43legacy_ilt_write32(struct b43legacy_wldev *dev, u16 offset, u32 val) +{ + b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset); + mmiowb(); + b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA2, + (val & 0xFFFF0000) >> 16); + b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_DATA1, + val & 0x0000FFFF); +} + +u16 b43legacy_ilt_read(struct b43legacy_wldev *dev, u16 offset) +{ + b43legacy_phy_write(dev, B43legacy_PHY_ILT_G_CTRL, offset); + return b43legacy_phy_read(dev, B43legacy_PHY_ILT_G_DATA1); +} diff --git a/drivers/net/wireless/broadcom/b43legacy/ilt.h b/drivers/net/wireless/broadcom/b43legacy/ilt.h new file mode 100644 index 000000000000..48bcf37eccb8 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/ilt.h @@ -0,0 +1,34 @@ +#ifndef B43legacy_ILT_H_ +#define B43legacy_ILT_H_ + +#define B43legacy_ILT_ROTOR_SIZE 53 +extern const u32 b43legacy_ilt_rotor[B43legacy_ILT_ROTOR_SIZE]; +#define B43legacy_ILT_RETARD_SIZE 53 +extern const u32 b43legacy_ilt_retard[B43legacy_ILT_RETARD_SIZE]; +#define B43legacy_ILT_FINEFREQA_SIZE 256 +extern const u16 b43legacy_ilt_finefreqa[B43legacy_ILT_FINEFREQA_SIZE]; +#define B43legacy_ILT_FINEFREQG_SIZE 256 +extern const u16 b43legacy_ilt_finefreqg[B43legacy_ILT_FINEFREQG_SIZE]; +#define B43legacy_ILT_NOISEA2_SIZE 8 +extern const u16 b43legacy_ilt_noisea2[B43legacy_ILT_NOISEA2_SIZE]; +#define B43legacy_ILT_NOISEA3_SIZE 8 +extern const u16 b43legacy_ilt_noisea3[B43legacy_ILT_NOISEA3_SIZE]; +#define B43legacy_ILT_NOISEG1_SIZE 8 +extern const u16 b43legacy_ilt_noiseg1[B43legacy_ILT_NOISEG1_SIZE]; +#define B43legacy_ILT_NOISEG2_SIZE 8 +extern const u16 b43legacy_ilt_noiseg2[B43legacy_ILT_NOISEG2_SIZE]; +#define B43legacy_ILT_NOISESCALEG_SIZE 27 +extern const u16 b43legacy_ilt_noisescaleg1[B43legacy_ILT_NOISESCALEG_SIZE]; +extern const u16 b43legacy_ilt_noisescaleg2[B43legacy_ILT_NOISESCALEG_SIZE]; +extern const u16 b43legacy_ilt_noisescaleg3[B43legacy_ILT_NOISESCALEG_SIZE]; +#define B43legacy_ILT_SIGMASQR_SIZE 53 +extern const u16 b43legacy_ilt_sigmasqr1[B43legacy_ILT_SIGMASQR_SIZE]; +extern const u16 b43legacy_ilt_sigmasqr2[B43legacy_ILT_SIGMASQR_SIZE]; + + +void b43legacy_ilt_write(struct b43legacy_wldev *dev, u16 offset, u16 val); +void b43legacy_ilt_write32(struct b43legacy_wldev *dev, u16 offset, + u32 val); +u16 b43legacy_ilt_read(struct b43legacy_wldev *dev, u16 offset); + +#endif /* B43legacy_ILT_H_ */ diff --git a/drivers/net/wireless/broadcom/b43legacy/leds.c b/drivers/net/wireless/broadcom/b43legacy/leds.c new file mode 100644 index 000000000000..fd4565389c77 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/leds.c @@ -0,0 +1,243 @@ +/* + + Broadcom B43 wireless driver + LED control + + Copyright (c) 2005 Martin Langer , + Copyright (c) 2005 Stefano Brivio + Copyright (c) 2005-2007 Michael Buesch + Copyright (c) 2005 Danny van Dyk + Copyright (c) 2005 Andreas Jaggi + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43legacy.h" +#include "leds.h" +#include "rfkill.h" + + +static void b43legacy_led_turn_on(struct b43legacy_wldev *dev, u8 led_index, + bool activelow) +{ + struct b43legacy_wl *wl = dev->wl; + unsigned long flags; + u16 ctl; + + spin_lock_irqsave(&wl->leds_lock, flags); + ctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL); + if (activelow) + ctl &= ~(1 << led_index); + else + ctl |= (1 << led_index); + b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ctl); + spin_unlock_irqrestore(&wl->leds_lock, flags); +} + +static void b43legacy_led_turn_off(struct b43legacy_wldev *dev, u8 led_index, + bool activelow) +{ + struct b43legacy_wl *wl = dev->wl; + unsigned long flags; + u16 ctl; + + spin_lock_irqsave(&wl->leds_lock, flags); + ctl = b43legacy_read16(dev, B43legacy_MMIO_GPIO_CONTROL); + if (activelow) + ctl |= (1 << led_index); + else + ctl &= ~(1 << led_index); + b43legacy_write16(dev, B43legacy_MMIO_GPIO_CONTROL, ctl); + spin_unlock_irqrestore(&wl->leds_lock, flags); +} + +/* Callback from the LED subsystem. */ +static void b43legacy_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct b43legacy_led *led = container_of(led_dev, struct b43legacy_led, + led_dev); + struct b43legacy_wldev *dev = led->dev; + bool radio_enabled; + + /* Checking the radio-enabled status here is slightly racy, + * but we want to avoid the locking overhead and we don't care + * whether the LED has the wrong state for a second. */ + radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable); + + if (brightness == LED_OFF || !radio_enabled) + b43legacy_led_turn_off(dev, led->index, led->activelow); + else + b43legacy_led_turn_on(dev, led->index, led->activelow); +} + +static int b43legacy_register_led(struct b43legacy_wldev *dev, + struct b43legacy_led *led, + const char *name, + const char *default_trigger, + u8 led_index, bool activelow) +{ + int err; + + b43legacy_led_turn_off(dev, led_index, activelow); + if (led->dev) + return -EEXIST; + if (!default_trigger) + return -EINVAL; + led->dev = dev; + led->index = led_index; + led->activelow = activelow; + strncpy(led->name, name, sizeof(led->name)); + + led->led_dev.name = led->name; + led->led_dev.default_trigger = default_trigger; + led->led_dev.brightness_set = b43legacy_led_brightness_set; + + err = led_classdev_register(dev->dev->dev, &led->led_dev); + if (err) { + b43legacywarn(dev->wl, "LEDs: Failed to register %s\n", name); + led->dev = NULL; + return err; + } + return 0; +} + +static void b43legacy_unregister_led(struct b43legacy_led *led) +{ + if (!led->dev) + return; + led_classdev_unregister(&led->led_dev); + b43legacy_led_turn_off(led->dev, led->index, led->activelow); + led->dev = NULL; +} + +static void b43legacy_map_led(struct b43legacy_wldev *dev, + u8 led_index, + enum b43legacy_led_behaviour behaviour, + bool activelow) +{ + struct ieee80211_hw *hw = dev->wl->hw; + char name[B43legacy_LED_MAX_NAME_LEN + 1]; + + /* Map the b43 specific LED behaviour value to the + * generic LED triggers. */ + switch (behaviour) { + case B43legacy_LED_INACTIVE: + break; + case B43legacy_LED_OFF: + b43legacy_led_turn_off(dev, led_index, activelow); + break; + case B43legacy_LED_ON: + b43legacy_led_turn_on(dev, led_index, activelow); + break; + case B43legacy_LED_ACTIVITY: + case B43legacy_LED_TRANSFER: + case B43legacy_LED_APTRANSFER: + snprintf(name, sizeof(name), + "b43legacy-%s::tx", wiphy_name(hw->wiphy)); + b43legacy_register_led(dev, &dev->led_tx, name, + ieee80211_get_tx_led_name(hw), + led_index, activelow); + snprintf(name, sizeof(name), + "b43legacy-%s::rx", wiphy_name(hw->wiphy)); + b43legacy_register_led(dev, &dev->led_rx, name, + ieee80211_get_rx_led_name(hw), + led_index, activelow); + break; + case B43legacy_LED_RADIO_ALL: + case B43legacy_LED_RADIO_A: + case B43legacy_LED_RADIO_B: + case B43legacy_LED_MODE_BG: + snprintf(name, sizeof(name), + "b43legacy-%s::radio", wiphy_name(hw->wiphy)); + b43legacy_register_led(dev, &dev->led_radio, name, + ieee80211_get_radio_led_name(hw), + led_index, activelow); + /* Sync the RF-kill LED state with radio and switch states. */ + if (dev->phy.radio_on && b43legacy_is_hw_radio_enabled(dev)) + b43legacy_led_turn_on(dev, led_index, activelow); + break; + case B43legacy_LED_WEIRD: + case B43legacy_LED_ASSOC: + snprintf(name, sizeof(name), + "b43legacy-%s::assoc", wiphy_name(hw->wiphy)); + b43legacy_register_led(dev, &dev->led_assoc, name, + ieee80211_get_assoc_led_name(hw), + led_index, activelow); + break; + default: + b43legacywarn(dev->wl, "LEDs: Unknown behaviour 0x%02X\n", + behaviour); + break; + } +} + +void b43legacy_leds_init(struct b43legacy_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + u8 sprom[4]; + int i; + enum b43legacy_led_behaviour behaviour; + bool activelow; + + sprom[0] = bus->sprom.gpio0; + sprom[1] = bus->sprom.gpio1; + sprom[2] = bus->sprom.gpio2; + sprom[3] = bus->sprom.gpio3; + + for (i = 0; i < 4; i++) { + if (sprom[i] == 0xFF) { + /* There is no LED information in the SPROM + * for this LED. Hardcode it here. */ + activelow = false; + switch (i) { + case 0: + behaviour = B43legacy_LED_ACTIVITY; + activelow = true; + if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ) + behaviour = B43legacy_LED_RADIO_ALL; + break; + case 1: + behaviour = B43legacy_LED_RADIO_B; + if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK) + behaviour = B43legacy_LED_ASSOC; + break; + case 2: + behaviour = B43legacy_LED_RADIO_A; + break; + case 3: + behaviour = B43legacy_LED_OFF; + break; + default: + B43legacy_WARN_ON(1); + return; + } + } else { + behaviour = sprom[i] & B43legacy_LED_BEHAVIOUR; + activelow = !!(sprom[i] & B43legacy_LED_ACTIVELOW); + } + b43legacy_map_led(dev, i, behaviour, activelow); + } +} + +void b43legacy_leds_exit(struct b43legacy_wldev *dev) +{ + b43legacy_unregister_led(&dev->led_tx); + b43legacy_unregister_led(&dev->led_rx); + b43legacy_unregister_led(&dev->led_assoc); + b43legacy_unregister_led(&dev->led_radio); +} diff --git a/drivers/net/wireless/broadcom/b43legacy/leds.h b/drivers/net/wireless/broadcom/b43legacy/leds.h new file mode 100644 index 000000000000..9ff6750dc57f --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/leds.h @@ -0,0 +1,63 @@ +#ifndef B43legacy_LEDS_H_ +#define B43legacy_LEDS_H_ + +struct b43legacy_wldev; + +#ifdef CONFIG_B43LEGACY_LEDS + +#include +#include + + +#define B43legacy_LED_MAX_NAME_LEN 31 + +struct b43legacy_led { + struct b43legacy_wldev *dev; + /* The LED class device */ + struct led_classdev led_dev; + /* The index number of the LED. */ + u8 index; + /* If activelow is true, the LED is ON if the + * bit is switched off. */ + bool activelow; + /* The unique name string for this LED device. */ + char name[B43legacy_LED_MAX_NAME_LEN + 1]; +}; + +#define B43legacy_LED_BEHAVIOUR 0x7F +#define B43legacy_LED_ACTIVELOW 0x80 +/* LED behaviour values */ +enum b43legacy_led_behaviour { + B43legacy_LED_OFF, + B43legacy_LED_ON, + B43legacy_LED_ACTIVITY, + B43legacy_LED_RADIO_ALL, + B43legacy_LED_RADIO_A, + B43legacy_LED_RADIO_B, + B43legacy_LED_MODE_BG, + B43legacy_LED_TRANSFER, + B43legacy_LED_APTRANSFER, + B43legacy_LED_WEIRD, + B43legacy_LED_ASSOC, + B43legacy_LED_INACTIVE, +}; + +void b43legacy_leds_init(struct b43legacy_wldev *dev); +void b43legacy_leds_exit(struct b43legacy_wldev *dev); + +#else /* CONFIG_B43LEGACY_LEDS */ +/* LED support disabled */ + +struct b43legacy_led { + /* empty */ +}; + +static inline void b43legacy_leds_init(struct b43legacy_wldev *dev) +{ +} +static inline void b43legacy_leds_exit(struct b43legacy_wldev *dev) +{ +} +#endif /* CONFIG_B43LEGACY_LEDS */ + +#endif /* B43legacy_LEDS_H_ */ diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c new file mode 100644 index 000000000000..afc1fb3e38df --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/main.c @@ -0,0 +1,4062 @@ +/* + * + * Broadcom B43legacy wireless driver + * + * Copyright (c) 2005 Martin Langer + * Copyright (c) 2005-2008 Stefano Brivio + * Copyright (c) 2005, 2006 Michael Buesch + * Copyright (c) 2005 Danny van Dyk + * Copyright (c) 2005 Andreas Jaggi + * Copyright (c) 2007 Larry Finger + * + * Some parts of the code in this file are derived from the ipw2200 + * driver Copyright(c) 2003 - 2004 Intel Corporation. + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "b43legacy.h" +#include "main.h" +#include "debugfs.h" +#include "phy.h" +#include "dma.h" +#include "pio.h" +#include "sysfs.h" +#include "xmit.h" +#include "radio.h" + + +MODULE_DESCRIPTION("Broadcom B43legacy wireless driver"); +MODULE_AUTHOR("Martin Langer"); +MODULE_AUTHOR("Stefano Brivio"); +MODULE_AUTHOR("Michael Buesch"); +MODULE_LICENSE("GPL"); + +MODULE_FIRMWARE("b43legacy/ucode2.fw"); +MODULE_FIRMWARE("b43legacy/ucode4.fw"); + +#if defined(CONFIG_B43LEGACY_DMA) && defined(CONFIG_B43LEGACY_PIO) +static int modparam_pio; +module_param_named(pio, modparam_pio, int, 0444); +MODULE_PARM_DESC(pio, "enable(1) / disable(0) PIO mode"); +#elif defined(CONFIG_B43LEGACY_DMA) +# define modparam_pio 0 +#elif defined(CONFIG_B43LEGACY_PIO) +# define modparam_pio 1 +#endif + +static int modparam_bad_frames_preempt; +module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); +MODULE_PARM_DESC(bad_frames_preempt, "enable(1) / disable(0) Bad Frames" + " Preemption"); + +static char modparam_fwpostfix[16]; +module_param_string(fwpostfix, modparam_fwpostfix, 16, 0444); +MODULE_PARM_DESC(fwpostfix, "Postfix for the firmware files to load."); + +/* The following table supports BCM4301, BCM4303 and BCM4306/2 devices. */ +static const struct ssb_device_id b43legacy_ssb_tbl[] = { + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 2), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 4), + {}, +}; +MODULE_DEVICE_TABLE(ssb, b43legacy_ssb_tbl); + + +/* Channel and ratetables are shared for all devices. + * They can't be const, because ieee80211 puts some precalculated + * data in there. This data is the same for all devices, so we don't + * get concurrency issues */ +#define RATETAB_ENT(_rateid, _flags) \ + { \ + .bitrate = B43legacy_RATE_TO_100KBPS(_rateid), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } +/* + * NOTE: When changing this, sync with xmit.c's + * b43legacy_plcp_get_bitrate_idx_* functions! + */ +static struct ieee80211_rate __b43legacy_ratetable[] = { + RATETAB_ENT(B43legacy_CCK_RATE_1MB, 0), + RATETAB_ENT(B43legacy_CCK_RATE_2MB, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(B43legacy_CCK_RATE_5MB, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(B43legacy_CCK_RATE_11MB, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(B43legacy_OFDM_RATE_6MB, 0), + RATETAB_ENT(B43legacy_OFDM_RATE_9MB, 0), + RATETAB_ENT(B43legacy_OFDM_RATE_12MB, 0), + RATETAB_ENT(B43legacy_OFDM_RATE_18MB, 0), + RATETAB_ENT(B43legacy_OFDM_RATE_24MB, 0), + RATETAB_ENT(B43legacy_OFDM_RATE_36MB, 0), + RATETAB_ENT(B43legacy_OFDM_RATE_48MB, 0), + RATETAB_ENT(B43legacy_OFDM_RATE_54MB, 0), +}; +#define b43legacy_b_ratetable (__b43legacy_ratetable + 0) +#define b43legacy_b_ratetable_size 4 +#define b43legacy_g_ratetable (__b43legacy_ratetable + 0) +#define b43legacy_g_ratetable_size 12 + +#define CHANTAB_ENT(_chanid, _freq) \ + { \ + .center_freq = (_freq), \ + .hw_value = (_chanid), \ + } +static struct ieee80211_channel b43legacy_bg_chantable[] = { + CHANTAB_ENT(1, 2412), + CHANTAB_ENT(2, 2417), + CHANTAB_ENT(3, 2422), + CHANTAB_ENT(4, 2427), + CHANTAB_ENT(5, 2432), + CHANTAB_ENT(6, 2437), + CHANTAB_ENT(7, 2442), + CHANTAB_ENT(8, 2447), + CHANTAB_ENT(9, 2452), + CHANTAB_ENT(10, 2457), + CHANTAB_ENT(11, 2462), + CHANTAB_ENT(12, 2467), + CHANTAB_ENT(13, 2472), + CHANTAB_ENT(14, 2484), +}; + +static struct ieee80211_supported_band b43legacy_band_2GHz_BPHY = { + .channels = b43legacy_bg_chantable, + .n_channels = ARRAY_SIZE(b43legacy_bg_chantable), + .bitrates = b43legacy_b_ratetable, + .n_bitrates = b43legacy_b_ratetable_size, +}; + +static struct ieee80211_supported_band b43legacy_band_2GHz_GPHY = { + .channels = b43legacy_bg_chantable, + .n_channels = ARRAY_SIZE(b43legacy_bg_chantable), + .bitrates = b43legacy_g_ratetable, + .n_bitrates = b43legacy_g_ratetable_size, +}; + +static void b43legacy_wireless_core_exit(struct b43legacy_wldev *dev); +static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev); +static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev); +static int b43legacy_wireless_core_start(struct b43legacy_wldev *dev); + + +static int b43legacy_ratelimit(struct b43legacy_wl *wl) +{ + if (!wl || !wl->current_dev) + return 1; + if (b43legacy_status(wl->current_dev) < B43legacy_STAT_STARTED) + return 1; + /* We are up and running. + * Ratelimit the messages to avoid DoS over the net. */ + return net_ratelimit(); +} + +void b43legacyinfo(struct b43legacy_wl *wl, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (!b43legacy_ratelimit(wl)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + printk(KERN_INFO "b43legacy-%s: %pV", + (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); + + va_end(args); +} + +void b43legacyerr(struct b43legacy_wl *wl, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (!b43legacy_ratelimit(wl)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + printk(KERN_ERR "b43legacy-%s ERROR: %pV", + (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); + + va_end(args); +} + +void b43legacywarn(struct b43legacy_wl *wl, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (!b43legacy_ratelimit(wl)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + printk(KERN_WARNING "b43legacy-%s warning: %pV", + (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); + + va_end(args); +} + +#if B43legacy_DEBUG +void b43legacydbg(struct b43legacy_wl *wl, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + printk(KERN_DEBUG "b43legacy-%s debug: %pV", + (wl && wl->hw) ? wiphy_name(wl->hw->wiphy) : "wlan", &vaf); + + va_end(args); +} +#endif /* DEBUG */ + +static void b43legacy_ram_write(struct b43legacy_wldev *dev, u16 offset, + u32 val) +{ + u32 status; + + B43legacy_WARN_ON(offset % 4 != 0); + + status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + if (status & B43legacy_MACCTL_BE) + val = swab32(val); + + b43legacy_write32(dev, B43legacy_MMIO_RAM_CONTROL, offset); + mmiowb(); + b43legacy_write32(dev, B43legacy_MMIO_RAM_DATA, val); +} + +static inline +void b43legacy_shm_control_word(struct b43legacy_wldev *dev, + u16 routing, u16 offset) +{ + u32 control; + + /* "offset" is the WORD offset. */ + + control = routing; + control <<= 16; + control |= offset; + b43legacy_write32(dev, B43legacy_MMIO_SHM_CONTROL, control); +} + +u32 b43legacy_shm_read32(struct b43legacy_wldev *dev, + u16 routing, u16 offset) +{ + u32 ret; + + if (routing == B43legacy_SHM_SHARED) { + B43legacy_WARN_ON((offset & 0x0001) != 0); + if (offset & 0x0003) { + /* Unaligned access */ + b43legacy_shm_control_word(dev, routing, offset >> 2); + ret = b43legacy_read16(dev, + B43legacy_MMIO_SHM_DATA_UNALIGNED); + ret <<= 16; + b43legacy_shm_control_word(dev, routing, + (offset >> 2) + 1); + ret |= b43legacy_read16(dev, B43legacy_MMIO_SHM_DATA); + + return ret; + } + offset >>= 2; + } + b43legacy_shm_control_word(dev, routing, offset); + ret = b43legacy_read32(dev, B43legacy_MMIO_SHM_DATA); + + return ret; +} + +u16 b43legacy_shm_read16(struct b43legacy_wldev *dev, + u16 routing, u16 offset) +{ + u16 ret; + + if (routing == B43legacy_SHM_SHARED) { + B43legacy_WARN_ON((offset & 0x0001) != 0); + if (offset & 0x0003) { + /* Unaligned access */ + b43legacy_shm_control_word(dev, routing, offset >> 2); + ret = b43legacy_read16(dev, + B43legacy_MMIO_SHM_DATA_UNALIGNED); + + return ret; + } + offset >>= 2; + } + b43legacy_shm_control_word(dev, routing, offset); + ret = b43legacy_read16(dev, B43legacy_MMIO_SHM_DATA); + + return ret; +} + +void b43legacy_shm_write32(struct b43legacy_wldev *dev, + u16 routing, u16 offset, + u32 value) +{ + if (routing == B43legacy_SHM_SHARED) { + B43legacy_WARN_ON((offset & 0x0001) != 0); + if (offset & 0x0003) { + /* Unaligned access */ + b43legacy_shm_control_word(dev, routing, offset >> 2); + mmiowb(); + b43legacy_write16(dev, + B43legacy_MMIO_SHM_DATA_UNALIGNED, + (value >> 16) & 0xffff); + mmiowb(); + b43legacy_shm_control_word(dev, routing, + (offset >> 2) + 1); + mmiowb(); + b43legacy_write16(dev, B43legacy_MMIO_SHM_DATA, + value & 0xffff); + return; + } + offset >>= 2; + } + b43legacy_shm_control_word(dev, routing, offset); + mmiowb(); + b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, value); +} + +void b43legacy_shm_write16(struct b43legacy_wldev *dev, u16 routing, u16 offset, + u16 value) +{ + if (routing == B43legacy_SHM_SHARED) { + B43legacy_WARN_ON((offset & 0x0001) != 0); + if (offset & 0x0003) { + /* Unaligned access */ + b43legacy_shm_control_word(dev, routing, offset >> 2); + mmiowb(); + b43legacy_write16(dev, + B43legacy_MMIO_SHM_DATA_UNALIGNED, + value); + return; + } + offset >>= 2; + } + b43legacy_shm_control_word(dev, routing, offset); + mmiowb(); + b43legacy_write16(dev, B43legacy_MMIO_SHM_DATA, value); +} + +/* Read HostFlags */ +u32 b43legacy_hf_read(struct b43legacy_wldev *dev) +{ + u32 ret; + + ret = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_HOSTFHI); + ret <<= 16; + ret |= b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_HOSTFLO); + + return ret; +} + +/* Write HostFlags */ +void b43legacy_hf_write(struct b43legacy_wldev *dev, u32 value) +{ + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_HOSTFLO, + (value & 0x0000FFFF)); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_HOSTFHI, + ((value & 0xFFFF0000) >> 16)); +} + +void b43legacy_tsf_read(struct b43legacy_wldev *dev, u64 *tsf) +{ + /* We need to be careful. As we read the TSF from multiple + * registers, we should take care of register overflows. + * In theory, the whole tsf read process should be atomic. + * We try to be atomic here, by restaring the read process, + * if any of the high registers changed (overflew). + */ + if (dev->dev->id.revision >= 3) { + u32 low; + u32 high; + u32 high2; + + do { + high = b43legacy_read32(dev, + B43legacy_MMIO_REV3PLUS_TSF_HIGH); + low = b43legacy_read32(dev, + B43legacy_MMIO_REV3PLUS_TSF_LOW); + high2 = b43legacy_read32(dev, + B43legacy_MMIO_REV3PLUS_TSF_HIGH); + } while (unlikely(high != high2)); + + *tsf = high; + *tsf <<= 32; + *tsf |= low; + } else { + u64 tmp; + u16 v0; + u16 v1; + u16 v2; + u16 v3; + u16 test1; + u16 test2; + u16 test3; + + do { + v3 = b43legacy_read16(dev, B43legacy_MMIO_TSF_3); + v2 = b43legacy_read16(dev, B43legacy_MMIO_TSF_2); + v1 = b43legacy_read16(dev, B43legacy_MMIO_TSF_1); + v0 = b43legacy_read16(dev, B43legacy_MMIO_TSF_0); + + test3 = b43legacy_read16(dev, B43legacy_MMIO_TSF_3); + test2 = b43legacy_read16(dev, B43legacy_MMIO_TSF_2); + test1 = b43legacy_read16(dev, B43legacy_MMIO_TSF_1); + } while (v3 != test3 || v2 != test2 || v1 != test1); + + *tsf = v3; + *tsf <<= 48; + tmp = v2; + tmp <<= 32; + *tsf |= tmp; + tmp = v1; + tmp <<= 16; + *tsf |= tmp; + *tsf |= v0; + } +} + +static void b43legacy_time_lock(struct b43legacy_wldev *dev) +{ + u32 status; + + status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + status |= B43legacy_MACCTL_TBTTHOLD; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); + mmiowb(); +} + +static void b43legacy_time_unlock(struct b43legacy_wldev *dev) +{ + u32 status; + + status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + status &= ~B43legacy_MACCTL_TBTTHOLD; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); +} + +static void b43legacy_tsf_write_locked(struct b43legacy_wldev *dev, u64 tsf) +{ + /* Be careful with the in-progress timer. + * First zero out the low register, so we have a full + * register-overflow duration to complete the operation. + */ + if (dev->dev->id.revision >= 3) { + u32 lo = (tsf & 0x00000000FFFFFFFFULL); + u32 hi = (tsf & 0xFFFFFFFF00000000ULL) >> 32; + + b43legacy_write32(dev, B43legacy_MMIO_REV3PLUS_TSF_LOW, 0); + mmiowb(); + b43legacy_write32(dev, B43legacy_MMIO_REV3PLUS_TSF_HIGH, + hi); + mmiowb(); + b43legacy_write32(dev, B43legacy_MMIO_REV3PLUS_TSF_LOW, + lo); + } else { + u16 v0 = (tsf & 0x000000000000FFFFULL); + u16 v1 = (tsf & 0x00000000FFFF0000ULL) >> 16; + u16 v2 = (tsf & 0x0000FFFF00000000ULL) >> 32; + u16 v3 = (tsf & 0xFFFF000000000000ULL) >> 48; + + b43legacy_write16(dev, B43legacy_MMIO_TSF_0, 0); + mmiowb(); + b43legacy_write16(dev, B43legacy_MMIO_TSF_3, v3); + mmiowb(); + b43legacy_write16(dev, B43legacy_MMIO_TSF_2, v2); + mmiowb(); + b43legacy_write16(dev, B43legacy_MMIO_TSF_1, v1); + mmiowb(); + b43legacy_write16(dev, B43legacy_MMIO_TSF_0, v0); + } +} + +void b43legacy_tsf_write(struct b43legacy_wldev *dev, u64 tsf) +{ + b43legacy_time_lock(dev); + b43legacy_tsf_write_locked(dev, tsf); + b43legacy_time_unlock(dev); +} + +static +void b43legacy_macfilter_set(struct b43legacy_wldev *dev, + u16 offset, const u8 *mac) +{ + static const u8 zero_addr[ETH_ALEN] = { 0 }; + u16 data; + + if (!mac) + mac = zero_addr; + + offset |= 0x0020; + b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_CONTROL, offset); + + data = mac[0]; + data |= mac[1] << 8; + b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_DATA, data); + data = mac[2]; + data |= mac[3] << 8; + b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_DATA, data); + data = mac[4]; + data |= mac[5] << 8; + b43legacy_write16(dev, B43legacy_MMIO_MACFILTER_DATA, data); +} + +static void b43legacy_write_mac_bssid_templates(struct b43legacy_wldev *dev) +{ + static const u8 zero_addr[ETH_ALEN] = { 0 }; + const u8 *mac = dev->wl->mac_addr; + const u8 *bssid = dev->wl->bssid; + u8 mac_bssid[ETH_ALEN * 2]; + int i; + u32 tmp; + + if (!bssid) + bssid = zero_addr; + if (!mac) + mac = zero_addr; + + b43legacy_macfilter_set(dev, B43legacy_MACFILTER_BSSID, bssid); + + memcpy(mac_bssid, mac, ETH_ALEN); + memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN); + + /* Write our MAC address and BSSID to template ram */ + for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) { + tmp = (u32)(mac_bssid[i + 0]); + tmp |= (u32)(mac_bssid[i + 1]) << 8; + tmp |= (u32)(mac_bssid[i + 2]) << 16; + tmp |= (u32)(mac_bssid[i + 3]) << 24; + b43legacy_ram_write(dev, 0x20 + i, tmp); + b43legacy_ram_write(dev, 0x78 + i, tmp); + b43legacy_ram_write(dev, 0x478 + i, tmp); + } +} + +static void b43legacy_upload_card_macaddress(struct b43legacy_wldev *dev) +{ + b43legacy_write_mac_bssid_templates(dev); + b43legacy_macfilter_set(dev, B43legacy_MACFILTER_SELF, + dev->wl->mac_addr); +} + +static void b43legacy_set_slot_time(struct b43legacy_wldev *dev, + u16 slot_time) +{ + /* slot_time is in usec. */ + if (dev->phy.type != B43legacy_PHYTYPE_G) + return; + b43legacy_write16(dev, 0x684, 510 + slot_time); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0010, + slot_time); +} + +static void b43legacy_short_slot_timing_enable(struct b43legacy_wldev *dev) +{ + b43legacy_set_slot_time(dev, 9); +} + +static void b43legacy_short_slot_timing_disable(struct b43legacy_wldev *dev) +{ + b43legacy_set_slot_time(dev, 20); +} + +/* Synchronize IRQ top- and bottom-half. + * IRQs must be masked before calling this. + * This must not be called with the irq_lock held. + */ +static void b43legacy_synchronize_irq(struct b43legacy_wldev *dev) +{ + synchronize_irq(dev->dev->irq); + tasklet_kill(&dev->isr_tasklet); +} + +/* DummyTransmission function, as documented on + * http://bcm-specs.sipsolutions.net/DummyTransmission + */ +void b43legacy_dummy_transmission(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + unsigned int i; + unsigned int max_loop; + u16 value; + u32 buffer[5] = { + 0x00000000, + 0x00D40000, + 0x00000000, + 0x01000000, + 0x00000000, + }; + + switch (phy->type) { + case B43legacy_PHYTYPE_B: + case B43legacy_PHYTYPE_G: + max_loop = 0xFA; + buffer[0] = 0x000B846E; + break; + default: + B43legacy_BUG_ON(1); + return; + } + + for (i = 0; i < 5; i++) + b43legacy_ram_write(dev, i * 4, buffer[i]); + + /* dummy read follows */ + b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + + b43legacy_write16(dev, 0x0568, 0x0000); + b43legacy_write16(dev, 0x07C0, 0x0000); + b43legacy_write16(dev, 0x050C, 0x0000); + b43legacy_write16(dev, 0x0508, 0x0000); + b43legacy_write16(dev, 0x050A, 0x0000); + b43legacy_write16(dev, 0x054C, 0x0000); + b43legacy_write16(dev, 0x056A, 0x0014); + b43legacy_write16(dev, 0x0568, 0x0826); + b43legacy_write16(dev, 0x0500, 0x0000); + b43legacy_write16(dev, 0x0502, 0x0030); + + if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) + b43legacy_radio_write16(dev, 0x0051, 0x0017); + for (i = 0x00; i < max_loop; i++) { + value = b43legacy_read16(dev, 0x050E); + if (value & 0x0080) + break; + udelay(10); + } + for (i = 0x00; i < 0x0A; i++) { + value = b43legacy_read16(dev, 0x050E); + if (value & 0x0400) + break; + udelay(10); + } + for (i = 0x00; i < 0x0A; i++) { + value = b43legacy_read16(dev, 0x0690); + if (!(value & 0x0100)) + break; + udelay(10); + } + if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) + b43legacy_radio_write16(dev, 0x0051, 0x0037); +} + +/* Turn the Analog ON/OFF */ +static void b43legacy_switch_analog(struct b43legacy_wldev *dev, int on) +{ + b43legacy_write16(dev, B43legacy_MMIO_PHY0, on ? 0 : 0xF4); +} + +void b43legacy_wireless_core_reset(struct b43legacy_wldev *dev, u32 flags) +{ + u32 tmslow; + u32 macctl; + + flags |= B43legacy_TMSLOW_PHYCLKEN; + flags |= B43legacy_TMSLOW_PHYRESET; + ssb_device_enable(dev->dev, flags); + msleep(2); /* Wait for the PLL to turn on. */ + + /* Now take the PHY out of Reset again */ + tmslow = ssb_read32(dev->dev, SSB_TMSLOW); + tmslow |= SSB_TMSLOW_FGC; + tmslow &= ~B43legacy_TMSLOW_PHYRESET; + ssb_write32(dev->dev, SSB_TMSLOW, tmslow); + ssb_read32(dev->dev, SSB_TMSLOW); /* flush */ + msleep(1); + tmslow &= ~SSB_TMSLOW_FGC; + ssb_write32(dev->dev, SSB_TMSLOW, tmslow); + ssb_read32(dev->dev, SSB_TMSLOW); /* flush */ + msleep(1); + + /* Turn Analog ON */ + b43legacy_switch_analog(dev, 1); + + macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + macctl &= ~B43legacy_MACCTL_GMODE; + if (flags & B43legacy_TMSLOW_GMODE) { + macctl |= B43legacy_MACCTL_GMODE; + dev->phy.gmode = true; + } else + dev->phy.gmode = false; + macctl |= B43legacy_MACCTL_IHR_ENABLED; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); +} + +static void handle_irq_transmit_status(struct b43legacy_wldev *dev) +{ + u32 v0; + u32 v1; + u16 tmp; + struct b43legacy_txstatus stat; + + while (1) { + v0 = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_0); + if (!(v0 & 0x00000001)) + break; + v1 = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_1); + + stat.cookie = (v0 >> 16); + stat.seq = (v1 & 0x0000FFFF); + stat.phy_stat = ((v1 & 0x00FF0000) >> 16); + tmp = (v0 & 0x0000FFFF); + stat.frame_count = ((tmp & 0xF000) >> 12); + stat.rts_count = ((tmp & 0x0F00) >> 8); + stat.supp_reason = ((tmp & 0x001C) >> 2); + stat.pm_indicated = !!(tmp & 0x0080); + stat.intermediate = !!(tmp & 0x0040); + stat.for_ampdu = !!(tmp & 0x0020); + stat.acked = !!(tmp & 0x0002); + + b43legacy_handle_txstatus(dev, &stat); + } +} + +static void drain_txstatus_queue(struct b43legacy_wldev *dev) +{ + u32 dummy; + + if (dev->dev->id.revision < 5) + return; + /* Read all entries from the microcode TXstatus FIFO + * and throw them away. + */ + while (1) { + dummy = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_0); + if (!(dummy & 0x00000001)) + break; + dummy = b43legacy_read32(dev, B43legacy_MMIO_XMITSTAT_1); + } +} + +static u32 b43legacy_jssi_read(struct b43legacy_wldev *dev) +{ + u32 val = 0; + + val = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x40A); + val <<= 16; + val |= b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x408); + + return val; +} + +static void b43legacy_jssi_write(struct b43legacy_wldev *dev, u32 jssi) +{ + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x408, + (jssi & 0x0000FFFF)); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x40A, + (jssi & 0xFFFF0000) >> 16); +} + +static void b43legacy_generate_noise_sample(struct b43legacy_wldev *dev) +{ + b43legacy_jssi_write(dev, 0x7F7F7F7F); + b43legacy_write32(dev, B43legacy_MMIO_MACCMD, + b43legacy_read32(dev, B43legacy_MMIO_MACCMD) + | B43legacy_MACCMD_BGNOISE); + B43legacy_WARN_ON(dev->noisecalc.channel_at_start != + dev->phy.channel); +} + +static void b43legacy_calculate_link_quality(struct b43legacy_wldev *dev) +{ + /* Top half of Link Quality calculation. */ + + if (dev->noisecalc.calculation_running) + return; + dev->noisecalc.channel_at_start = dev->phy.channel; + dev->noisecalc.calculation_running = true; + dev->noisecalc.nr_samples = 0; + + b43legacy_generate_noise_sample(dev); +} + +static void handle_irq_noise(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 tmp; + u8 noise[4]; + u8 i; + u8 j; + s32 average; + + /* Bottom half of Link Quality calculation. */ + + B43legacy_WARN_ON(!dev->noisecalc.calculation_running); + if (dev->noisecalc.channel_at_start != phy->channel) + goto drop_calculation; + *((__le32 *)noise) = cpu_to_le32(b43legacy_jssi_read(dev)); + if (noise[0] == 0x7F || noise[1] == 0x7F || + noise[2] == 0x7F || noise[3] == 0x7F) + goto generate_new; + + /* Get the noise samples. */ + B43legacy_WARN_ON(dev->noisecalc.nr_samples >= 8); + i = dev->noisecalc.nr_samples; + noise[0] = clamp_val(noise[0], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + noise[1] = clamp_val(noise[1], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + noise[2] = clamp_val(noise[2], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + noise[3] = clamp_val(noise[3], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + dev->noisecalc.samples[i][0] = phy->nrssi_lt[noise[0]]; + dev->noisecalc.samples[i][1] = phy->nrssi_lt[noise[1]]; + dev->noisecalc.samples[i][2] = phy->nrssi_lt[noise[2]]; + dev->noisecalc.samples[i][3] = phy->nrssi_lt[noise[3]]; + dev->noisecalc.nr_samples++; + if (dev->noisecalc.nr_samples == 8) { + /* Calculate the Link Quality by the noise samples. */ + average = 0; + for (i = 0; i < 8; i++) { + for (j = 0; j < 4; j++) + average += dev->noisecalc.samples[i][j]; + } + average /= (8 * 4); + average *= 125; + average += 64; + average /= 128; + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + 0x40C); + tmp = (tmp / 128) & 0x1F; + if (tmp >= 8) + average += 2; + else + average -= 25; + if (tmp == 8) + average -= 72; + else + average -= 48; + + dev->stats.link_noise = average; +drop_calculation: + dev->noisecalc.calculation_running = false; + return; + } +generate_new: + b43legacy_generate_noise_sample(dev); +} + +static void handle_irq_tbtt_indication(struct b43legacy_wldev *dev) +{ + if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_AP)) { + /* TODO: PS TBTT */ + } else { + if (1/*FIXME: the last PSpoll frame was sent successfully */) + b43legacy_power_saving_ctl_bits(dev, -1, -1); + } + if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) + dev->dfq_valid = true; +} + +static void handle_irq_atim_end(struct b43legacy_wldev *dev) +{ + if (dev->dfq_valid) { + b43legacy_write32(dev, B43legacy_MMIO_MACCMD, + b43legacy_read32(dev, B43legacy_MMIO_MACCMD) + | B43legacy_MACCMD_DFQ_VALID); + dev->dfq_valid = false; + } +} + +static void handle_irq_pmq(struct b43legacy_wldev *dev) +{ + u32 tmp; + + /* TODO: AP mode. */ + + while (1) { + tmp = b43legacy_read32(dev, B43legacy_MMIO_PS_STATUS); + if (!(tmp & 0x00000008)) + break; + } + /* 16bit write is odd, but correct. */ + b43legacy_write16(dev, B43legacy_MMIO_PS_STATUS, 0x0002); +} + +static void b43legacy_write_template_common(struct b43legacy_wldev *dev, + const u8 *data, u16 size, + u16 ram_offset, + u16 shm_size_offset, u8 rate) +{ + u32 i; + u32 tmp; + struct b43legacy_plcp_hdr4 plcp; + + plcp.data = 0; + b43legacy_generate_plcp_hdr(&plcp, size + FCS_LEN, rate); + b43legacy_ram_write(dev, ram_offset, le32_to_cpu(plcp.data)); + ram_offset += sizeof(u32); + /* The PLCP is 6 bytes long, but we only wrote 4 bytes, yet. + * So leave the first two bytes of the next write blank. + */ + tmp = (u32)(data[0]) << 16; + tmp |= (u32)(data[1]) << 24; + b43legacy_ram_write(dev, ram_offset, tmp); + ram_offset += sizeof(u32); + for (i = 2; i < size; i += sizeof(u32)) { + tmp = (u32)(data[i + 0]); + if (i + 1 < size) + tmp |= (u32)(data[i + 1]) << 8; + if (i + 2 < size) + tmp |= (u32)(data[i + 2]) << 16; + if (i + 3 < size) + tmp |= (u32)(data[i + 3]) << 24; + b43legacy_ram_write(dev, ram_offset + i - 2, tmp); + } + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_size_offset, + size + sizeof(struct b43legacy_plcp_hdr6)); +} + +/* Convert a b43legacy antenna number value to the PHY TX control value. */ +static u16 b43legacy_antenna_to_phyctl(int antenna) +{ + switch (antenna) { + case B43legacy_ANTENNA0: + return B43legacy_TX4_PHY_ANT0; + case B43legacy_ANTENNA1: + return B43legacy_TX4_PHY_ANT1; + } + return B43legacy_TX4_PHY_ANTLAST; +} + +static void b43legacy_write_beacon_template(struct b43legacy_wldev *dev, + u16 ram_offset, + u16 shm_size_offset) +{ + + unsigned int i, len, variable_len; + const struct ieee80211_mgmt *bcn; + const u8 *ie; + bool tim_found = false; + unsigned int rate; + u16 ctl; + int antenna; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon); + + bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data); + len = min_t(size_t, dev->wl->current_beacon->len, + 0x200 - sizeof(struct b43legacy_plcp_hdr6)); + rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value; + + b43legacy_write_template_common(dev, (const u8 *)bcn, len, ram_offset, + shm_size_offset, rate); + + /* Write the PHY TX control parameters. */ + antenna = B43legacy_ANTENNA_DEFAULT; + antenna = b43legacy_antenna_to_phyctl(antenna); + ctl = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_BEACPHYCTL); + /* We can't send beacons with short preamble. Would get PHY errors. */ + ctl &= ~B43legacy_TX4_PHY_SHORTPRMBL; + ctl &= ~B43legacy_TX4_PHY_ANT; + ctl &= ~B43legacy_TX4_PHY_ENC; + ctl |= antenna; + ctl |= B43legacy_TX4_PHY_ENC_CCK; + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_BEACPHYCTL, ctl); + + /* Find the position of the TIM and the DTIM_period value + * and write them to SHM. */ + ie = bcn->u.beacon.variable; + variable_len = len - offsetof(struct ieee80211_mgmt, u.beacon.variable); + for (i = 0; i < variable_len - 2; ) { + uint8_t ie_id, ie_len; + + ie_id = ie[i]; + ie_len = ie[i + 1]; + if (ie_id == 5) { + u16 tim_position; + u16 dtim_period; + /* This is the TIM Information Element */ + + /* Check whether the ie_len is in the beacon data range. */ + if (variable_len < ie_len + 2 + i) + break; + /* A valid TIM is at least 4 bytes long. */ + if (ie_len < 4) + break; + tim_found = true; + + tim_position = sizeof(struct b43legacy_plcp_hdr6); + tim_position += offsetof(struct ieee80211_mgmt, + u.beacon.variable); + tim_position += i; + + dtim_period = ie[i + 3]; + + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_TIMPOS, tim_position); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_DTIMP, dtim_period); + break; + } + i += ie_len + 2; + } + if (!tim_found) { + b43legacywarn(dev->wl, "Did not find a valid TIM IE in the " + "beacon template packet. AP or IBSS operation " + "may be broken.\n"); + } else + b43legacydbg(dev->wl, "Updated beacon template\n"); +} + +static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev, + u16 shm_offset, u16 size, + struct ieee80211_rate *rate) +{ + struct b43legacy_plcp_hdr4 plcp; + u32 tmp; + __le16 dur; + + plcp.data = 0; + b43legacy_generate_plcp_hdr(&plcp, size + FCS_LEN, rate->hw_value); + dur = ieee80211_generic_frame_duration(dev->wl->hw, + dev->wl->vif, + IEEE80211_BAND_2GHZ, + size, + rate); + /* Write PLCP in two parts and timing for packet transfer */ + tmp = le32_to_cpu(plcp.data); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_offset, + tmp & 0xFFFF); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_offset + 2, + tmp >> 16); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_offset + 6, + le16_to_cpu(dur)); +} + +/* Instead of using custom probe response template, this function + * just patches custom beacon template by: + * 1) Changing packet type + * 2) Patching duration field + * 3) Stripping TIM + */ +static const u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev, + u16 *dest_size, + struct ieee80211_rate *rate) +{ + const u8 *src_data; + u8 *dest_data; + u16 src_size, elem_size, src_pos, dest_pos; + __le16 dur; + struct ieee80211_hdr *hdr; + size_t ie_start; + + src_size = dev->wl->current_beacon->len; + src_data = (const u8 *)dev->wl->current_beacon->data; + + /* Get the start offset of the variable IEs in the packet. */ + ie_start = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + B43legacy_WARN_ON(ie_start != offsetof(struct ieee80211_mgmt, + u.beacon.variable)); + + if (B43legacy_WARN_ON(src_size < ie_start)) + return NULL; + + dest_data = kmalloc(src_size, GFP_ATOMIC); + if (unlikely(!dest_data)) + return NULL; + + /* Copy the static data and all Information Elements, except the TIM. */ + memcpy(dest_data, src_data, ie_start); + src_pos = ie_start; + dest_pos = ie_start; + for ( ; src_pos < src_size - 2; src_pos += elem_size) { + elem_size = src_data[src_pos + 1] + 2; + if (src_data[src_pos] == 5) { + /* This is the TIM. */ + continue; + } + memcpy(dest_data + dest_pos, src_data + src_pos, elem_size); + dest_pos += elem_size; + } + *dest_size = dest_pos; + hdr = (struct ieee80211_hdr *)dest_data; + + /* Set the frame control. */ + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + dur = ieee80211_generic_frame_duration(dev->wl->hw, + dev->wl->vif, + IEEE80211_BAND_2GHZ, + *dest_size, + rate); + hdr->duration_id = dur; + + return dest_data; +} + +static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev, + u16 ram_offset, + u16 shm_size_offset, + struct ieee80211_rate *rate) +{ + const u8 *probe_resp_data; + u16 size; + + size = dev->wl->current_beacon->len; + probe_resp_data = b43legacy_generate_probe_resp(dev, &size, rate); + if (unlikely(!probe_resp_data)) + return; + + /* Looks like PLCP headers plus packet timings are stored for + * all possible basic rates + */ + b43legacy_write_probe_resp_plcp(dev, 0x31A, size, + &b43legacy_b_ratetable[0]); + b43legacy_write_probe_resp_plcp(dev, 0x32C, size, + &b43legacy_b_ratetable[1]); + b43legacy_write_probe_resp_plcp(dev, 0x33E, size, + &b43legacy_b_ratetable[2]); + b43legacy_write_probe_resp_plcp(dev, 0x350, size, + &b43legacy_b_ratetable[3]); + + size = min_t(size_t, size, + 0x200 - sizeof(struct b43legacy_plcp_hdr6)); + b43legacy_write_template_common(dev, probe_resp_data, + size, ram_offset, + shm_size_offset, rate->hw_value); + kfree(probe_resp_data); +} + +static void b43legacy_upload_beacon0(struct b43legacy_wldev *dev) +{ + struct b43legacy_wl *wl = dev->wl; + + if (wl->beacon0_uploaded) + return; + b43legacy_write_beacon_template(dev, 0x68, 0x18); + /* FIXME: Probe resp upload doesn't really belong here, + * but we don't use that feature anyway. */ + b43legacy_write_probe_resp_template(dev, 0x268, 0x4A, + &__b43legacy_ratetable[3]); + wl->beacon0_uploaded = true; +} + +static void b43legacy_upload_beacon1(struct b43legacy_wldev *dev) +{ + struct b43legacy_wl *wl = dev->wl; + + if (wl->beacon1_uploaded) + return; + b43legacy_write_beacon_template(dev, 0x468, 0x1A); + wl->beacon1_uploaded = true; +} + +static void handle_irq_beacon(struct b43legacy_wldev *dev) +{ + struct b43legacy_wl *wl = dev->wl; + u32 cmd, beacon0_valid, beacon1_valid; + + if (!b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) + return; + + /* This is the bottom half of the asynchronous beacon update. */ + + /* Ignore interrupt in the future. */ + dev->irq_mask &= ~B43legacy_IRQ_BEACON; + + cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); + beacon0_valid = (cmd & B43legacy_MACCMD_BEACON0_VALID); + beacon1_valid = (cmd & B43legacy_MACCMD_BEACON1_VALID); + + /* Schedule interrupt manually, if busy. */ + if (beacon0_valid && beacon1_valid) { + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, B43legacy_IRQ_BEACON); + dev->irq_mask |= B43legacy_IRQ_BEACON; + return; + } + + if (unlikely(wl->beacon_templates_virgin)) { + /* We never uploaded a beacon before. + * Upload both templates now, but only mark one valid. */ + wl->beacon_templates_virgin = false; + b43legacy_upload_beacon0(dev); + b43legacy_upload_beacon1(dev); + cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); + cmd |= B43legacy_MACCMD_BEACON0_VALID; + b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); + } else { + if (!beacon0_valid) { + b43legacy_upload_beacon0(dev); + cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); + cmd |= B43legacy_MACCMD_BEACON0_VALID; + b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); + } else if (!beacon1_valid) { + b43legacy_upload_beacon1(dev); + cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); + cmd |= B43legacy_MACCMD_BEACON1_VALID; + b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); + } + } +} + +static void b43legacy_beacon_update_trigger_work(struct work_struct *work) +{ + struct b43legacy_wl *wl = container_of(work, struct b43legacy_wl, + beacon_update_trigger); + struct b43legacy_wldev *dev; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (likely(dev && (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED))) { + spin_lock_irq(&wl->irq_lock); + /* Update beacon right away or defer to IRQ. */ + handle_irq_beacon(dev); + /* The handler might have updated the IRQ mask. */ + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, + dev->irq_mask); + mmiowb(); + spin_unlock_irq(&wl->irq_lock); + } + mutex_unlock(&wl->mutex); +} + +/* Asynchronously update the packet templates in template RAM. + * Locking: Requires wl->irq_lock to be locked. */ +static void b43legacy_update_templates(struct b43legacy_wl *wl) +{ + struct sk_buff *beacon; + /* This is the top half of the ansynchronous beacon update. The bottom + * half is the beacon IRQ. Beacon update must be asynchronous to avoid + * sending an invalid beacon. This can happen for example, if the + * firmware transmits a beacon while we are updating it. */ + + /* We could modify the existing beacon and set the aid bit in the TIM + * field, but that would probably require resizing and moving of data + * within the beacon template. Simply request a new beacon and let + * mac80211 do the hard work. */ + beacon = ieee80211_beacon_get(wl->hw, wl->vif); + if (unlikely(!beacon)) + return; + + if (wl->current_beacon) + dev_kfree_skb_any(wl->current_beacon); + wl->current_beacon = beacon; + wl->beacon0_uploaded = false; + wl->beacon1_uploaded = false; + ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger); +} + +static void b43legacy_set_beacon_int(struct b43legacy_wldev *dev, + u16 beacon_int) +{ + b43legacy_time_lock(dev); + if (dev->dev->id.revision >= 3) { + b43legacy_write32(dev, B43legacy_MMIO_TSF_CFP_REP, + (beacon_int << 16)); + b43legacy_write32(dev, B43legacy_MMIO_TSF_CFP_START, + (beacon_int << 10)); + } else { + b43legacy_write16(dev, 0x606, (beacon_int >> 6)); + b43legacy_write16(dev, 0x610, beacon_int); + } + b43legacy_time_unlock(dev); + b43legacydbg(dev->wl, "Set beacon interval to %u\n", beacon_int); +} + +static void handle_irq_ucode_debug(struct b43legacy_wldev *dev) +{ +} + +/* Interrupt handler bottom-half */ +static void b43legacy_interrupt_tasklet(struct b43legacy_wldev *dev) +{ + u32 reason; + u32 dma_reason[ARRAY_SIZE(dev->dma_reason)]; + u32 merged_dma_reason = 0; + int i; + unsigned long flags; + + spin_lock_irqsave(&dev->wl->irq_lock, flags); + + B43legacy_WARN_ON(b43legacy_status(dev) < + B43legacy_STAT_INITIALIZED); + + reason = dev->irq_reason; + for (i = 0; i < ARRAY_SIZE(dma_reason); i++) { + dma_reason[i] = dev->dma_reason[i]; + merged_dma_reason |= dma_reason[i]; + } + + if (unlikely(reason & B43legacy_IRQ_MAC_TXERR)) + b43legacyerr(dev->wl, "MAC transmission error\n"); + + if (unlikely(reason & B43legacy_IRQ_PHY_TXERR)) { + b43legacyerr(dev->wl, "PHY transmission error\n"); + rmb(); + if (unlikely(atomic_dec_and_test(&dev->phy.txerr_cnt))) { + b43legacyerr(dev->wl, "Too many PHY TX errors, " + "restarting the controller\n"); + b43legacy_controller_restart(dev, "PHY TX errors"); + } + } + + if (unlikely(merged_dma_reason & (B43legacy_DMAIRQ_FATALMASK | + B43legacy_DMAIRQ_NONFATALMASK))) { + if (merged_dma_reason & B43legacy_DMAIRQ_FATALMASK) { + b43legacyerr(dev->wl, "Fatal DMA error: " + "0x%08X, 0x%08X, 0x%08X, " + "0x%08X, 0x%08X, 0x%08X\n", + dma_reason[0], dma_reason[1], + dma_reason[2], dma_reason[3], + dma_reason[4], dma_reason[5]); + b43legacy_controller_restart(dev, "DMA error"); + mmiowb(); + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); + return; + } + if (merged_dma_reason & B43legacy_DMAIRQ_NONFATALMASK) + b43legacyerr(dev->wl, "DMA error: " + "0x%08X, 0x%08X, 0x%08X, " + "0x%08X, 0x%08X, 0x%08X\n", + dma_reason[0], dma_reason[1], + dma_reason[2], dma_reason[3], + dma_reason[4], dma_reason[5]); + } + + if (unlikely(reason & B43legacy_IRQ_UCODE_DEBUG)) + handle_irq_ucode_debug(dev); + if (reason & B43legacy_IRQ_TBTT_INDI) + handle_irq_tbtt_indication(dev); + if (reason & B43legacy_IRQ_ATIM_END) + handle_irq_atim_end(dev); + if (reason & B43legacy_IRQ_BEACON) + handle_irq_beacon(dev); + if (reason & B43legacy_IRQ_PMQ) + handle_irq_pmq(dev); + if (reason & B43legacy_IRQ_TXFIFO_FLUSH_OK) + ;/*TODO*/ + if (reason & B43legacy_IRQ_NOISESAMPLE_OK) + handle_irq_noise(dev); + + /* Check the DMA reason registers for received data. */ + if (dma_reason[0] & B43legacy_DMAIRQ_RX_DONE) { + if (b43legacy_using_pio(dev)) + b43legacy_pio_rx(dev->pio.queue0); + else + b43legacy_dma_rx(dev->dma.rx_ring0); + } + B43legacy_WARN_ON(dma_reason[1] & B43legacy_DMAIRQ_RX_DONE); + B43legacy_WARN_ON(dma_reason[2] & B43legacy_DMAIRQ_RX_DONE); + if (dma_reason[3] & B43legacy_DMAIRQ_RX_DONE) { + if (b43legacy_using_pio(dev)) + b43legacy_pio_rx(dev->pio.queue3); + else + b43legacy_dma_rx(dev->dma.rx_ring3); + } + B43legacy_WARN_ON(dma_reason[4] & B43legacy_DMAIRQ_RX_DONE); + B43legacy_WARN_ON(dma_reason[5] & B43legacy_DMAIRQ_RX_DONE); + + if (reason & B43legacy_IRQ_TX_OK) + handle_irq_transmit_status(dev); + + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); + mmiowb(); + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); +} + +static void pio_irq_workaround(struct b43legacy_wldev *dev, + u16 base, int queueidx) +{ + u16 rxctl; + + rxctl = b43legacy_read16(dev, base + B43legacy_PIO_RXCTL); + if (rxctl & B43legacy_PIO_RXCTL_DATAAVAILABLE) + dev->dma_reason[queueidx] |= B43legacy_DMAIRQ_RX_DONE; + else + dev->dma_reason[queueidx] &= ~B43legacy_DMAIRQ_RX_DONE; +} + +static void b43legacy_interrupt_ack(struct b43legacy_wldev *dev, u32 reason) +{ + if (b43legacy_using_pio(dev) && + (dev->dev->id.revision < 3) && + (!(reason & B43legacy_IRQ_PIO_WORKAROUND))) { + /* Apply a PIO specific workaround to the dma_reasons */ + pio_irq_workaround(dev, B43legacy_MMIO_PIO1_BASE, 0); + pio_irq_workaround(dev, B43legacy_MMIO_PIO2_BASE, 1); + pio_irq_workaround(dev, B43legacy_MMIO_PIO3_BASE, 2); + pio_irq_workaround(dev, B43legacy_MMIO_PIO4_BASE, 3); + } + + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, reason); + + b43legacy_write32(dev, B43legacy_MMIO_DMA0_REASON, + dev->dma_reason[0]); + b43legacy_write32(dev, B43legacy_MMIO_DMA1_REASON, + dev->dma_reason[1]); + b43legacy_write32(dev, B43legacy_MMIO_DMA2_REASON, + dev->dma_reason[2]); + b43legacy_write32(dev, B43legacy_MMIO_DMA3_REASON, + dev->dma_reason[3]); + b43legacy_write32(dev, B43legacy_MMIO_DMA4_REASON, + dev->dma_reason[4]); + b43legacy_write32(dev, B43legacy_MMIO_DMA5_REASON, + dev->dma_reason[5]); +} + +/* Interrupt handler top-half */ +static irqreturn_t b43legacy_interrupt_handler(int irq, void *dev_id) +{ + irqreturn_t ret = IRQ_NONE; + struct b43legacy_wldev *dev = dev_id; + u32 reason; + + B43legacy_WARN_ON(!dev); + + spin_lock(&dev->wl->irq_lock); + + if (unlikely(b43legacy_status(dev) < B43legacy_STAT_STARTED)) + /* This can only happen on shared IRQ lines. */ + goto out; + reason = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); + if (reason == 0xffffffff) /* shared IRQ */ + goto out; + ret = IRQ_HANDLED; + reason &= dev->irq_mask; + if (!reason) + goto out; + + dev->dma_reason[0] = b43legacy_read32(dev, + B43legacy_MMIO_DMA0_REASON) + & 0x0001DC00; + dev->dma_reason[1] = b43legacy_read32(dev, + B43legacy_MMIO_DMA1_REASON) + & 0x0000DC00; + dev->dma_reason[2] = b43legacy_read32(dev, + B43legacy_MMIO_DMA2_REASON) + & 0x0000DC00; + dev->dma_reason[3] = b43legacy_read32(dev, + B43legacy_MMIO_DMA3_REASON) + & 0x0001DC00; + dev->dma_reason[4] = b43legacy_read32(dev, + B43legacy_MMIO_DMA4_REASON) + & 0x0000DC00; + dev->dma_reason[5] = b43legacy_read32(dev, + B43legacy_MMIO_DMA5_REASON) + & 0x0000DC00; + + b43legacy_interrupt_ack(dev, reason); + /* Disable all IRQs. They are enabled again in the bottom half. */ + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); + /* Save the reason code and call our bottom half. */ + dev->irq_reason = reason; + tasklet_schedule(&dev->isr_tasklet); +out: + mmiowb(); + spin_unlock(&dev->wl->irq_lock); + + return ret; +} + +static void b43legacy_release_firmware(struct b43legacy_wldev *dev) +{ + release_firmware(dev->fw.ucode); + dev->fw.ucode = NULL; + release_firmware(dev->fw.pcm); + dev->fw.pcm = NULL; + release_firmware(dev->fw.initvals); + dev->fw.initvals = NULL; + release_firmware(dev->fw.initvals_band); + dev->fw.initvals_band = NULL; +} + +static void b43legacy_print_fw_helptext(struct b43legacy_wl *wl) +{ + b43legacyerr(wl, "You must go to http://wireless.kernel.org/en/users/" + "Drivers/b43#devicefirmware " + "and download the correct firmware (version 3).\n"); +} + +static void b43legacy_fw_cb(const struct firmware *firmware, void *context) +{ + struct b43legacy_wldev *dev = context; + + dev->fwp = firmware; + complete(&dev->fw_load_complete); +} + +static int do_request_fw(struct b43legacy_wldev *dev, + const char *name, + const struct firmware **fw, bool async) +{ + char path[sizeof(modparam_fwpostfix) + 32]; + struct b43legacy_fw_header *hdr; + u32 size; + int err; + + if (!name) + return 0; + + snprintf(path, ARRAY_SIZE(path), + "b43legacy%s/%s.fw", + modparam_fwpostfix, name); + b43legacyinfo(dev->wl, "Loading firmware %s\n", path); + if (async) { + init_completion(&dev->fw_load_complete); + err = request_firmware_nowait(THIS_MODULE, 1, path, + dev->dev->dev, GFP_KERNEL, + dev, b43legacy_fw_cb); + if (err) { + b43legacyerr(dev->wl, "Unable to load firmware\n"); + return err; + } + /* stall here until fw ready */ + wait_for_completion(&dev->fw_load_complete); + if (!dev->fwp) + err = -EINVAL; + *fw = dev->fwp; + } else { + err = request_firmware(fw, path, dev->dev->dev); + } + if (err) { + b43legacyerr(dev->wl, "Firmware file \"%s\" not found " + "or load failed.\n", path); + return err; + } + if ((*fw)->size < sizeof(struct b43legacy_fw_header)) + goto err_format; + hdr = (struct b43legacy_fw_header *)((*fw)->data); + switch (hdr->type) { + case B43legacy_FW_TYPE_UCODE: + case B43legacy_FW_TYPE_PCM: + size = be32_to_cpu(hdr->size); + if (size != (*fw)->size - sizeof(struct b43legacy_fw_header)) + goto err_format; + /* fallthrough */ + case B43legacy_FW_TYPE_IV: + if (hdr->ver != 1) + goto err_format; + break; + default: + goto err_format; + } + + return err; + +err_format: + b43legacyerr(dev->wl, "Firmware file \"%s\" format error.\n", path); + return -EPROTO; +} + +static int b43legacy_one_core_attach(struct ssb_device *dev, + struct b43legacy_wl *wl); +static void b43legacy_one_core_detach(struct ssb_device *dev); + +static void b43legacy_request_firmware(struct work_struct *work) +{ + struct b43legacy_wl *wl = container_of(work, + struct b43legacy_wl, firmware_load); + struct b43legacy_wldev *dev = wl->current_dev; + struct b43legacy_firmware *fw = &dev->fw; + const u8 rev = dev->dev->id.revision; + const char *filename; + int err; + + if (!fw->ucode) { + if (rev == 2) + filename = "ucode2"; + else if (rev == 4) + filename = "ucode4"; + else + filename = "ucode5"; + err = do_request_fw(dev, filename, &fw->ucode, true); + if (err) + goto err_load; + } + if (!fw->pcm) { + if (rev < 5) + filename = "pcm4"; + else + filename = "pcm5"; + err = do_request_fw(dev, filename, &fw->pcm, false); + if (err) + goto err_load; + } + if (!fw->initvals) { + switch (dev->phy.type) { + case B43legacy_PHYTYPE_B: + case B43legacy_PHYTYPE_G: + if ((rev >= 5) && (rev <= 10)) + filename = "b0g0initvals5"; + else if (rev == 2 || rev == 4) + filename = "b0g0initvals2"; + else + goto err_no_initvals; + break; + default: + goto err_no_initvals; + } + err = do_request_fw(dev, filename, &fw->initvals, false); + if (err) + goto err_load; + } + if (!fw->initvals_band) { + switch (dev->phy.type) { + case B43legacy_PHYTYPE_B: + case B43legacy_PHYTYPE_G: + if ((rev >= 5) && (rev <= 10)) + filename = "b0g0bsinitvals5"; + else if (rev >= 11) + filename = NULL; + else if (rev == 2 || rev == 4) + filename = NULL; + else + goto err_no_initvals; + break; + default: + goto err_no_initvals; + } + err = do_request_fw(dev, filename, &fw->initvals_band, false); + if (err) + goto err_load; + } + err = ieee80211_register_hw(wl->hw); + if (err) + goto err_one_core_detach; + return; + +err_one_core_detach: + b43legacy_one_core_detach(dev->dev); + goto error; + +err_load: + b43legacy_print_fw_helptext(dev->wl); + goto error; + +err_no_initvals: + err = -ENODEV; + b43legacyerr(dev->wl, "No Initial Values firmware file for PHY %u, " + "core rev %u\n", dev->phy.type, rev); + goto error; + +error: + b43legacy_release_firmware(dev); + return; +} + +static int b43legacy_upload_microcode(struct b43legacy_wldev *dev) +{ + struct wiphy *wiphy = dev->wl->hw->wiphy; + const size_t hdr_len = sizeof(struct b43legacy_fw_header); + const __be32 *data; + unsigned int i; + unsigned int len; + u16 fwrev; + u16 fwpatch; + u16 fwdate; + u16 fwtime; + u32 tmp, macctl; + int err = 0; + + /* Jump the microcode PSM to offset 0 */ + macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + B43legacy_WARN_ON(macctl & B43legacy_MACCTL_PSM_RUN); + macctl |= B43legacy_MACCTL_PSM_JMP0; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); + /* Zero out all microcode PSM registers and shared memory. */ + for (i = 0; i < 64; i++) + b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, i, 0); + for (i = 0; i < 4096; i += 2) + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, i, 0); + + /* Upload Microcode. */ + data = (__be32 *) (dev->fw.ucode->data + hdr_len); + len = (dev->fw.ucode->size - hdr_len) / sizeof(__be32); + b43legacy_shm_control_word(dev, + B43legacy_SHM_UCODE | + B43legacy_SHM_AUTOINC_W, + 0x0000); + for (i = 0; i < len; i++) { + b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, + be32_to_cpu(data[i])); + udelay(10); + } + + if (dev->fw.pcm) { + /* Upload PCM data. */ + data = (__be32 *) (dev->fw.pcm->data + hdr_len); + len = (dev->fw.pcm->size - hdr_len) / sizeof(__be32); + b43legacy_shm_control_word(dev, B43legacy_SHM_HW, 0x01EA); + b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, 0x00004000); + /* No need for autoinc bit in SHM_HW */ + b43legacy_shm_control_word(dev, B43legacy_SHM_HW, 0x01EB); + for (i = 0; i < len; i++) { + b43legacy_write32(dev, B43legacy_MMIO_SHM_DATA, + be32_to_cpu(data[i])); + udelay(10); + } + } + + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, + B43legacy_IRQ_ALL); + + /* Start the microcode PSM */ + macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + macctl &= ~B43legacy_MACCTL_PSM_JMP0; + macctl |= B43legacy_MACCTL_PSM_RUN; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); + + /* Wait for the microcode to load and respond */ + i = 0; + while (1) { + tmp = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); + if (tmp == B43legacy_IRQ_MAC_SUSPENDED) + break; + i++; + if (i >= B43legacy_IRQWAIT_MAX_RETRIES) { + b43legacyerr(dev->wl, "Microcode not responding\n"); + b43legacy_print_fw_helptext(dev->wl); + err = -ENODEV; + goto error; + } + msleep_interruptible(50); + if (signal_pending(current)) { + err = -EINTR; + goto error; + } + } + /* dummy read follows */ + b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); + + /* Get and check the revisions. */ + fwrev = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_UCODEREV); + fwpatch = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_UCODEPATCH); + fwdate = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_UCODEDATE); + fwtime = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_UCODETIME); + + if (fwrev > 0x128) { + b43legacyerr(dev->wl, "YOU ARE TRYING TO LOAD V4 FIRMWARE." + " Only firmware from binary drivers version 3.x" + " is supported. You must change your firmware" + " files.\n"); + b43legacy_print_fw_helptext(dev->wl); + err = -EOPNOTSUPP; + goto error; + } + b43legacyinfo(dev->wl, "Loading firmware version 0x%X, patch level %u " + "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n", fwrev, fwpatch, + (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF, + (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, + fwtime & 0x1F); + + dev->fw.rev = fwrev; + dev->fw.patch = fwpatch; + + snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "%u.%u", + dev->fw.rev, dev->fw.patch); + wiphy->hw_version = dev->dev->id.coreid; + + return 0; + +error: + macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + macctl &= ~B43legacy_MACCTL_PSM_RUN; + macctl |= B43legacy_MACCTL_PSM_JMP0; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); + + return err; +} + +static int b43legacy_write_initvals(struct b43legacy_wldev *dev, + const struct b43legacy_iv *ivals, + size_t count, + size_t array_size) +{ + const struct b43legacy_iv *iv; + u16 offset; + size_t i; + bool bit32; + + BUILD_BUG_ON(sizeof(struct b43legacy_iv) != 6); + iv = ivals; + for (i = 0; i < count; i++) { + if (array_size < sizeof(iv->offset_size)) + goto err_format; + array_size -= sizeof(iv->offset_size); + offset = be16_to_cpu(iv->offset_size); + bit32 = !!(offset & B43legacy_IV_32BIT); + offset &= B43legacy_IV_OFFSET_MASK; + if (offset >= 0x1000) + goto err_format; + if (bit32) { + u32 value; + + if (array_size < sizeof(iv->data.d32)) + goto err_format; + array_size -= sizeof(iv->data.d32); + + value = get_unaligned_be32(&iv->data.d32); + b43legacy_write32(dev, offset, value); + + iv = (const struct b43legacy_iv *)((const uint8_t *)iv + + sizeof(__be16) + + sizeof(__be32)); + } else { + u16 value; + + if (array_size < sizeof(iv->data.d16)) + goto err_format; + array_size -= sizeof(iv->data.d16); + + value = be16_to_cpu(iv->data.d16); + b43legacy_write16(dev, offset, value); + + iv = (const struct b43legacy_iv *)((const uint8_t *)iv + + sizeof(__be16) + + sizeof(__be16)); + } + } + if (array_size) + goto err_format; + + return 0; + +err_format: + b43legacyerr(dev->wl, "Initial Values Firmware file-format error.\n"); + b43legacy_print_fw_helptext(dev->wl); + + return -EPROTO; +} + +static int b43legacy_upload_initvals(struct b43legacy_wldev *dev) +{ + const size_t hdr_len = sizeof(struct b43legacy_fw_header); + const struct b43legacy_fw_header *hdr; + struct b43legacy_firmware *fw = &dev->fw; + const struct b43legacy_iv *ivals; + size_t count; + int err; + + hdr = (const struct b43legacy_fw_header *)(fw->initvals->data); + ivals = (const struct b43legacy_iv *)(fw->initvals->data + hdr_len); + count = be32_to_cpu(hdr->size); + err = b43legacy_write_initvals(dev, ivals, count, + fw->initvals->size - hdr_len); + if (err) + goto out; + if (fw->initvals_band) { + hdr = (const struct b43legacy_fw_header *) + (fw->initvals_band->data); + ivals = (const struct b43legacy_iv *)(fw->initvals_band->data + + hdr_len); + count = be32_to_cpu(hdr->size); + err = b43legacy_write_initvals(dev, ivals, count, + fw->initvals_band->size - hdr_len); + if (err) + goto out; + } +out: + + return err; +} + +/* Initialize the GPIOs + * http://bcm-specs.sipsolutions.net/GPIO + */ +static int b43legacy_gpio_init(struct b43legacy_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct ssb_device *gpiodev, *pcidev = NULL; + u32 mask; + u32 set; + + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, + b43legacy_read32(dev, + B43legacy_MMIO_MACCTL) + & 0xFFFF3FFF); + + b43legacy_write16(dev, B43legacy_MMIO_GPIO_MASK, + b43legacy_read16(dev, + B43legacy_MMIO_GPIO_MASK) + | 0x000F); + + mask = 0x0000001F; + set = 0x0000000F; + if (dev->dev->bus->chip_id == 0x4301) { + mask |= 0x0060; + set |= 0x0060; + } + if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_PACTRL) { + b43legacy_write16(dev, B43legacy_MMIO_GPIO_MASK, + b43legacy_read16(dev, + B43legacy_MMIO_GPIO_MASK) + | 0x0200); + mask |= 0x0200; + set |= 0x0200; + } + if (dev->dev->id.revision >= 2) + mask |= 0x0010; /* FIXME: This is redundant. */ + +#ifdef CONFIG_SSB_DRIVER_PCICORE + pcidev = bus->pcicore.dev; +#endif + gpiodev = bus->chipco.dev ? : pcidev; + if (!gpiodev) + return 0; + ssb_write32(gpiodev, B43legacy_GPIO_CONTROL, + (ssb_read32(gpiodev, B43legacy_GPIO_CONTROL) + & ~mask) | set); + + return 0; +} + +/* Turn off all GPIO stuff. Call this on module unload, for example. */ +static void b43legacy_gpio_cleanup(struct b43legacy_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct ssb_device *gpiodev, *pcidev = NULL; + +#ifdef CONFIG_SSB_DRIVER_PCICORE + pcidev = bus->pcicore.dev; +#endif + gpiodev = bus->chipco.dev ? : pcidev; + if (!gpiodev) + return; + ssb_write32(gpiodev, B43legacy_GPIO_CONTROL, 0); +} + +/* http://bcm-specs.sipsolutions.net/EnableMac */ +void b43legacy_mac_enable(struct b43legacy_wldev *dev) +{ + dev->mac_suspended--; + B43legacy_WARN_ON(dev->mac_suspended < 0); + B43legacy_WARN_ON(irqs_disabled()); + if (dev->mac_suspended == 0) { + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, + b43legacy_read32(dev, + B43legacy_MMIO_MACCTL) + | B43legacy_MACCTL_ENABLED); + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, + B43legacy_IRQ_MAC_SUSPENDED); + /* the next two are dummy reads */ + b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); + b43legacy_power_saving_ctl_bits(dev, -1, -1); + + /* Re-enable IRQs. */ + spin_lock_irq(&dev->wl->irq_lock); + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, + dev->irq_mask); + spin_unlock_irq(&dev->wl->irq_lock); + } +} + +/* http://bcm-specs.sipsolutions.net/SuspendMAC */ +void b43legacy_mac_suspend(struct b43legacy_wldev *dev) +{ + int i; + u32 tmp; + + might_sleep(); + B43legacy_WARN_ON(irqs_disabled()); + B43legacy_WARN_ON(dev->mac_suspended < 0); + + if (dev->mac_suspended == 0) { + /* Mask IRQs before suspending MAC. Otherwise + * the MAC stays busy and won't suspend. */ + spin_lock_irq(&dev->wl->irq_lock); + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); + spin_unlock_irq(&dev->wl->irq_lock); + b43legacy_synchronize_irq(dev); + + b43legacy_power_saving_ctl_bits(dev, -1, 1); + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, + b43legacy_read32(dev, + B43legacy_MMIO_MACCTL) + & ~B43legacy_MACCTL_ENABLED); + b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); + for (i = 40; i; i--) { + tmp = b43legacy_read32(dev, + B43legacy_MMIO_GEN_IRQ_REASON); + if (tmp & B43legacy_IRQ_MAC_SUSPENDED) + goto out; + msleep(1); + } + b43legacyerr(dev->wl, "MAC suspend failed\n"); + } +out: + dev->mac_suspended++; +} + +static void b43legacy_adjust_opmode(struct b43legacy_wldev *dev) +{ + struct b43legacy_wl *wl = dev->wl; + u32 ctl; + u16 cfp_pretbtt; + + ctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + /* Reset status to STA infrastructure mode. */ + ctl &= ~B43legacy_MACCTL_AP; + ctl &= ~B43legacy_MACCTL_KEEP_CTL; + ctl &= ~B43legacy_MACCTL_KEEP_BADPLCP; + ctl &= ~B43legacy_MACCTL_KEEP_BAD; + ctl &= ~B43legacy_MACCTL_PROMISC; + ctl &= ~B43legacy_MACCTL_BEACPROMISC; + ctl |= B43legacy_MACCTL_INFRA; + + if (b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) + ctl |= B43legacy_MACCTL_AP; + else if (b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC)) + ctl &= ~B43legacy_MACCTL_INFRA; + + if (wl->filter_flags & FIF_CONTROL) + ctl |= B43legacy_MACCTL_KEEP_CTL; + if (wl->filter_flags & FIF_FCSFAIL) + ctl |= B43legacy_MACCTL_KEEP_BAD; + if (wl->filter_flags & FIF_PLCPFAIL) + ctl |= B43legacy_MACCTL_KEEP_BADPLCP; + if (wl->filter_flags & FIF_BCN_PRBRESP_PROMISC) + ctl |= B43legacy_MACCTL_BEACPROMISC; + + /* Workaround: On old hardware the HW-MAC-address-filter + * doesn't work properly, so always run promisc in filter + * it in software. */ + if (dev->dev->id.revision <= 4) + ctl |= B43legacy_MACCTL_PROMISC; + + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, ctl); + + cfp_pretbtt = 2; + if ((ctl & B43legacy_MACCTL_INFRA) && + !(ctl & B43legacy_MACCTL_AP)) { + if (dev->dev->bus->chip_id == 0x4306 && + dev->dev->bus->chip_rev == 3) + cfp_pretbtt = 100; + else + cfp_pretbtt = 50; + } + b43legacy_write16(dev, 0x612, cfp_pretbtt); +} + +static void b43legacy_rate_memory_write(struct b43legacy_wldev *dev, + u16 rate, + int is_ofdm) +{ + u16 offset; + + if (is_ofdm) { + offset = 0x480; + offset += (b43legacy_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2; + } else { + offset = 0x4C0; + offset += (b43legacy_plcp_get_ratecode_cck(rate) & 0x000F) * 2; + } + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, offset + 0x20, + b43legacy_shm_read16(dev, + B43legacy_SHM_SHARED, offset)); +} + +static void b43legacy_rate_memory_init(struct b43legacy_wldev *dev) +{ + switch (dev->phy.type) { + case B43legacy_PHYTYPE_G: + b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_6MB, 1); + b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_12MB, 1); + b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_18MB, 1); + b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_24MB, 1); + b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_36MB, 1); + b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_48MB, 1); + b43legacy_rate_memory_write(dev, B43legacy_OFDM_RATE_54MB, 1); + /* fallthrough */ + case B43legacy_PHYTYPE_B: + b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_1MB, 0); + b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_2MB, 0); + b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_5MB, 0); + b43legacy_rate_memory_write(dev, B43legacy_CCK_RATE_11MB, 0); + break; + default: + B43legacy_BUG_ON(1); + } +} + +/* Set the TX-Antenna for management frames sent by firmware. */ +static void b43legacy_mgmtframe_txantenna(struct b43legacy_wldev *dev, + int antenna) +{ + u16 ant = 0; + u16 tmp; + + switch (antenna) { + case B43legacy_ANTENNA0: + ant |= B43legacy_TX4_PHY_ANT0; + break; + case B43legacy_ANTENNA1: + ant |= B43legacy_TX4_PHY_ANT1; + break; + case B43legacy_ANTENNA_AUTO: + ant |= B43legacy_TX4_PHY_ANTLAST; + break; + default: + B43legacy_BUG_ON(1); + } + + /* FIXME We also need to set the other flags of the PHY control + * field somewhere. */ + + /* For Beacons */ + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_BEACPHYCTL); + tmp = (tmp & ~B43legacy_TX4_PHY_ANT) | ant; + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_BEACPHYCTL, tmp); + /* For ACK/CTS */ + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_ACKCTSPHYCTL); + tmp = (tmp & ~B43legacy_TX4_PHY_ANT) | ant; + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_ACKCTSPHYCTL, tmp); + /* For Probe Resposes */ + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_PRPHYCTL); + tmp = (tmp & ~B43legacy_TX4_PHY_ANT) | ant; + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_PRPHYCTL, tmp); +} + +/* This is the opposite of b43legacy_chip_init() */ +static void b43legacy_chip_exit(struct b43legacy_wldev *dev) +{ + b43legacy_radio_turn_off(dev, 1); + b43legacy_gpio_cleanup(dev); + /* firmware is released later */ +} + +/* Initialize the chip + * http://bcm-specs.sipsolutions.net/ChipInit + */ +static int b43legacy_chip_init(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + int err; + int tmp; + u32 value32, macctl; + u16 value16; + + /* Initialize the MAC control */ + macctl = B43legacy_MACCTL_IHR_ENABLED | B43legacy_MACCTL_SHM_ENABLED; + if (dev->phy.gmode) + macctl |= B43legacy_MACCTL_GMODE; + macctl |= B43legacy_MACCTL_INFRA; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); + + err = b43legacy_upload_microcode(dev); + if (err) + goto out; /* firmware is released later */ + + err = b43legacy_gpio_init(dev); + if (err) + goto out; /* firmware is released later */ + + err = b43legacy_upload_initvals(dev); + if (err) + goto err_gpio_clean; + b43legacy_radio_turn_on(dev); + + b43legacy_write16(dev, 0x03E6, 0x0000); + err = b43legacy_phy_init(dev); + if (err) + goto err_radio_off; + + /* Select initial Interference Mitigation. */ + tmp = phy->interfmode; + phy->interfmode = B43legacy_INTERFMODE_NONE; + b43legacy_radio_set_interference_mitigation(dev, tmp); + + b43legacy_phy_set_antenna_diversity(dev); + b43legacy_mgmtframe_txantenna(dev, B43legacy_ANTENNA_DEFAULT); + + if (phy->type == B43legacy_PHYTYPE_B) { + value16 = b43legacy_read16(dev, 0x005E); + value16 |= 0x0004; + b43legacy_write16(dev, 0x005E, value16); + } + b43legacy_write32(dev, 0x0100, 0x01000000); + if (dev->dev->id.revision < 5) + b43legacy_write32(dev, 0x010C, 0x01000000); + + value32 = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + value32 &= ~B43legacy_MACCTL_INFRA; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value32); + value32 = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + value32 |= B43legacy_MACCTL_INFRA; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value32); + + if (b43legacy_using_pio(dev)) { + b43legacy_write32(dev, 0x0210, 0x00000100); + b43legacy_write32(dev, 0x0230, 0x00000100); + b43legacy_write32(dev, 0x0250, 0x00000100); + b43legacy_write32(dev, 0x0270, 0x00000100); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0034, + 0x0000); + } + + /* Probe Response Timeout value */ + /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */ + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0074, 0x0000); + + /* Initially set the wireless operation mode. */ + b43legacy_adjust_opmode(dev); + + if (dev->dev->id.revision < 3) { + b43legacy_write16(dev, 0x060E, 0x0000); + b43legacy_write16(dev, 0x0610, 0x8000); + b43legacy_write16(dev, 0x0604, 0x0000); + b43legacy_write16(dev, 0x0606, 0x0200); + } else { + b43legacy_write32(dev, 0x0188, 0x80000000); + b43legacy_write32(dev, 0x018C, 0x02000000); + } + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, 0x00004000); + b43legacy_write32(dev, B43legacy_MMIO_DMA0_IRQ_MASK, 0x0001DC00); + b43legacy_write32(dev, B43legacy_MMIO_DMA1_IRQ_MASK, 0x0000DC00); + b43legacy_write32(dev, B43legacy_MMIO_DMA2_IRQ_MASK, 0x0000DC00); + b43legacy_write32(dev, B43legacy_MMIO_DMA3_IRQ_MASK, 0x0001DC00); + b43legacy_write32(dev, B43legacy_MMIO_DMA4_IRQ_MASK, 0x0000DC00); + b43legacy_write32(dev, B43legacy_MMIO_DMA5_IRQ_MASK, 0x0000DC00); + + value32 = ssb_read32(dev->dev, SSB_TMSLOW); + value32 |= B43legacy_TMSLOW_MACPHYCLKEN; + ssb_write32(dev->dev, SSB_TMSLOW, value32); + + b43legacy_write16(dev, B43legacy_MMIO_POWERUP_DELAY, + dev->dev->bus->chipco.fast_pwrup_delay); + + /* PHY TX errors counter. */ + atomic_set(&phy->txerr_cnt, B43legacy_PHY_TX_BADNESS_LIMIT); + + B43legacy_WARN_ON(err != 0); + b43legacydbg(dev->wl, "Chip initialized\n"); +out: + return err; + +err_radio_off: + b43legacy_radio_turn_off(dev, 1); +err_gpio_clean: + b43legacy_gpio_cleanup(dev); + goto out; +} + +static void b43legacy_periodic_every120sec(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + + if (phy->type != B43legacy_PHYTYPE_G || phy->rev < 2) + return; + + b43legacy_mac_suspend(dev); + b43legacy_phy_lo_g_measure(dev); + b43legacy_mac_enable(dev); +} + +static void b43legacy_periodic_every60sec(struct b43legacy_wldev *dev) +{ + b43legacy_phy_lo_mark_all_unused(dev); + if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI) { + b43legacy_mac_suspend(dev); + b43legacy_calc_nrssi_slope(dev); + b43legacy_mac_enable(dev); + } +} + +static void b43legacy_periodic_every30sec(struct b43legacy_wldev *dev) +{ + /* Update device statistics. */ + b43legacy_calculate_link_quality(dev); +} + +static void b43legacy_periodic_every15sec(struct b43legacy_wldev *dev) +{ + b43legacy_phy_xmitpower(dev); /* FIXME: unless scanning? */ + + atomic_set(&dev->phy.txerr_cnt, B43legacy_PHY_TX_BADNESS_LIMIT); + wmb(); +} + +static void do_periodic_work(struct b43legacy_wldev *dev) +{ + unsigned int state; + + state = dev->periodic_state; + if (state % 8 == 0) + b43legacy_periodic_every120sec(dev); + if (state % 4 == 0) + b43legacy_periodic_every60sec(dev); + if (state % 2 == 0) + b43legacy_periodic_every30sec(dev); + b43legacy_periodic_every15sec(dev); +} + +/* Periodic work locking policy: + * The whole periodic work handler is protected by + * wl->mutex. If another lock is needed somewhere in the + * pwork callchain, it's acquired in-place, where it's needed. + */ +static void b43legacy_periodic_work_handler(struct work_struct *work) +{ + struct b43legacy_wldev *dev = container_of(work, struct b43legacy_wldev, + periodic_work.work); + struct b43legacy_wl *wl = dev->wl; + unsigned long delay; + + mutex_lock(&wl->mutex); + + if (unlikely(b43legacy_status(dev) != B43legacy_STAT_STARTED)) + goto out; + if (b43legacy_debug(dev, B43legacy_DBG_PWORK_STOP)) + goto out_requeue; + + do_periodic_work(dev); + + dev->periodic_state++; +out_requeue: + if (b43legacy_debug(dev, B43legacy_DBG_PWORK_FAST)) + delay = msecs_to_jiffies(50); + else + delay = round_jiffies_relative(HZ * 15); + ieee80211_queue_delayed_work(wl->hw, &dev->periodic_work, delay); +out: + mutex_unlock(&wl->mutex); +} + +static void b43legacy_periodic_tasks_setup(struct b43legacy_wldev *dev) +{ + struct delayed_work *work = &dev->periodic_work; + + dev->periodic_state = 0; + INIT_DELAYED_WORK(work, b43legacy_periodic_work_handler); + ieee80211_queue_delayed_work(dev->wl->hw, work, 0); +} + +/* Validate access to the chip (SHM) */ +static int b43legacy_validate_chipaccess(struct b43legacy_wldev *dev) +{ + u32 value; + u32 shm_backup; + + shm_backup = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, 0); + b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, 0, 0xAA5555AA); + if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, 0) != + 0xAA5555AA) + goto error; + b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, 0, 0x55AAAA55); + if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, 0) != + 0x55AAAA55) + goto error; + b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, 0, shm_backup); + + value = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + if ((value | B43legacy_MACCTL_GMODE) != + (B43legacy_MACCTL_GMODE | B43legacy_MACCTL_IHR_ENABLED)) + goto error; + + value = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); + if (value) + goto error; + + return 0; +error: + b43legacyerr(dev->wl, "Failed to validate the chipaccess\n"); + return -ENODEV; +} + +static void b43legacy_security_init(struct b43legacy_wldev *dev) +{ + dev->max_nr_keys = (dev->dev->id.revision >= 5) ? 58 : 20; + B43legacy_WARN_ON(dev->max_nr_keys > ARRAY_SIZE(dev->key)); + dev->ktp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + 0x0056); + /* KTP is a word address, but we address SHM bytewise. + * So multiply by two. + */ + dev->ktp *= 2; + if (dev->dev->id.revision >= 5) + /* Number of RCMTA address slots */ + b43legacy_write16(dev, B43legacy_MMIO_RCMTA_COUNT, + dev->max_nr_keys - 8); +} + +#ifdef CONFIG_B43LEGACY_HWRNG +static int b43legacy_rng_read(struct hwrng *rng, u32 *data) +{ + struct b43legacy_wl *wl = (struct b43legacy_wl *)rng->priv; + unsigned long flags; + + /* Don't take wl->mutex here, as it could deadlock with + * hwrng internal locking. It's not needed to take + * wl->mutex here, anyway. */ + + spin_lock_irqsave(&wl->irq_lock, flags); + *data = b43legacy_read16(wl->current_dev, B43legacy_MMIO_RNG); + spin_unlock_irqrestore(&wl->irq_lock, flags); + + return (sizeof(u16)); +} +#endif + +static void b43legacy_rng_exit(struct b43legacy_wl *wl) +{ +#ifdef CONFIG_B43LEGACY_HWRNG + if (wl->rng_initialized) + hwrng_unregister(&wl->rng); +#endif +} + +static int b43legacy_rng_init(struct b43legacy_wl *wl) +{ + int err = 0; + +#ifdef CONFIG_B43LEGACY_HWRNG + snprintf(wl->rng_name, ARRAY_SIZE(wl->rng_name), + "%s_%s", KBUILD_MODNAME, wiphy_name(wl->hw->wiphy)); + wl->rng.name = wl->rng_name; + wl->rng.data_read = b43legacy_rng_read; + wl->rng.priv = (unsigned long)wl; + wl->rng_initialized = 1; + err = hwrng_register(&wl->rng); + if (err) { + wl->rng_initialized = 0; + b43legacyerr(wl, "Failed to register the random " + "number generator (%d)\n", err); + } + +#endif + return err; +} + +static void b43legacy_tx_work(struct work_struct *work) +{ + struct b43legacy_wl *wl = container_of(work, struct b43legacy_wl, + tx_work); + struct b43legacy_wldev *dev; + struct sk_buff *skb; + int queue_num; + int err = 0; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (unlikely(!dev || b43legacy_status(dev) < B43legacy_STAT_STARTED)) { + mutex_unlock(&wl->mutex); + return; + } + + for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { + while (skb_queue_len(&wl->tx_queue[queue_num])) { + skb = skb_dequeue(&wl->tx_queue[queue_num]); + if (b43legacy_using_pio(dev)) + err = b43legacy_pio_tx(dev, skb); + else + err = b43legacy_dma_tx(dev, skb); + if (err == -ENOSPC) { + wl->tx_queue_stopped[queue_num] = 1; + ieee80211_stop_queue(wl->hw, queue_num); + skb_queue_head(&wl->tx_queue[queue_num], skb); + break; + } + if (unlikely(err)) + dev_kfree_skb(skb); /* Drop it */ + err = 0; + } + + if (!err) + wl->tx_queue_stopped[queue_num] = 0; + } + + mutex_unlock(&wl->mutex); +} + +static void b43legacy_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + + if (unlikely(skb->len < 2 + 2 + 6)) { + /* Too short, this can't be a valid frame. */ + dev_kfree_skb_any(skb); + return; + } + B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags); + + skb_queue_tail(&wl->tx_queue[skb->queue_mapping], skb); + if (!wl->tx_queue_stopped[skb->queue_mapping]) + ieee80211_queue_work(wl->hw, &wl->tx_work); + else + ieee80211_stop_queue(wl->hw, skb->queue_mapping); +} + +static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + return 0; +} + +static int b43legacy_op_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + unsigned long flags; + + spin_lock_irqsave(&wl->irq_lock, flags); + memcpy(stats, &wl->ieee_stats, sizeof(*stats)); + spin_unlock_irqrestore(&wl->irq_lock, flags); + + return 0; +} + +static const char *phymode_to_string(unsigned int phymode) +{ + switch (phymode) { + case B43legacy_PHYMODE_B: + return "B"; + case B43legacy_PHYMODE_G: + return "G"; + default: + B43legacy_BUG_ON(1); + } + return ""; +} + +static int find_wldev_for_phymode(struct b43legacy_wl *wl, + unsigned int phymode, + struct b43legacy_wldev **dev, + bool *gmode) +{ + struct b43legacy_wldev *d; + + list_for_each_entry(d, &wl->devlist, list) { + if (d->phy.possible_phymodes & phymode) { + /* Ok, this device supports the PHY-mode. + * Set the gmode bit. */ + *gmode = true; + *dev = d; + + return 0; + } + } + + return -ESRCH; +} + +static void b43legacy_put_phy_into_reset(struct b43legacy_wldev *dev) +{ + struct ssb_device *sdev = dev->dev; + u32 tmslow; + + tmslow = ssb_read32(sdev, SSB_TMSLOW); + tmslow &= ~B43legacy_TMSLOW_GMODE; + tmslow |= B43legacy_TMSLOW_PHYRESET; + tmslow |= SSB_TMSLOW_FGC; + ssb_write32(sdev, SSB_TMSLOW, tmslow); + msleep(1); + + tmslow = ssb_read32(sdev, SSB_TMSLOW); + tmslow &= ~SSB_TMSLOW_FGC; + tmslow |= B43legacy_TMSLOW_PHYRESET; + ssb_write32(sdev, SSB_TMSLOW, tmslow); + msleep(1); +} + +/* Expects wl->mutex locked */ +static int b43legacy_switch_phymode(struct b43legacy_wl *wl, + unsigned int new_mode) +{ + struct b43legacy_wldev *uninitialized_var(up_dev); + struct b43legacy_wldev *down_dev; + int err; + bool gmode = false; + int prev_status; + + err = find_wldev_for_phymode(wl, new_mode, &up_dev, &gmode); + if (err) { + b43legacyerr(wl, "Could not find a device for %s-PHY mode\n", + phymode_to_string(new_mode)); + return err; + } + if ((up_dev == wl->current_dev) && + (!!wl->current_dev->phy.gmode == !!gmode)) + /* This device is already running. */ + return 0; + b43legacydbg(wl, "Reconfiguring PHYmode to %s-PHY\n", + phymode_to_string(new_mode)); + down_dev = wl->current_dev; + + prev_status = b43legacy_status(down_dev); + /* Shutdown the currently running core. */ + if (prev_status >= B43legacy_STAT_STARTED) + b43legacy_wireless_core_stop(down_dev); + if (prev_status >= B43legacy_STAT_INITIALIZED) + b43legacy_wireless_core_exit(down_dev); + + if (down_dev != up_dev) + /* We switch to a different core, so we put PHY into + * RESET on the old core. */ + b43legacy_put_phy_into_reset(down_dev); + + /* Now start the new core. */ + up_dev->phy.gmode = gmode; + if (prev_status >= B43legacy_STAT_INITIALIZED) { + err = b43legacy_wireless_core_init(up_dev); + if (err) { + b43legacyerr(wl, "Fatal: Could not initialize device" + " for newly selected %s-PHY mode\n", + phymode_to_string(new_mode)); + goto init_failure; + } + } + if (prev_status >= B43legacy_STAT_STARTED) { + err = b43legacy_wireless_core_start(up_dev); + if (err) { + b43legacyerr(wl, "Fatal: Could not start device for " + "newly selected %s-PHY mode\n", + phymode_to_string(new_mode)); + b43legacy_wireless_core_exit(up_dev); + goto init_failure; + } + } + B43legacy_WARN_ON(b43legacy_status(up_dev) != prev_status); + + b43legacy_shm_write32(up_dev, B43legacy_SHM_SHARED, 0x003E, 0); + + wl->current_dev = up_dev; + + return 0; +init_failure: + /* Whoops, failed to init the new core. No core is operating now. */ + wl->current_dev = NULL; + return err; +} + +/* Write the short and long frame retry limit values. */ +static void b43legacy_set_retry_limits(struct b43legacy_wldev *dev, + unsigned int short_retry, + unsigned int long_retry) +{ + /* The retry limit is a 4-bit counter. Enforce this to avoid overflowing + * the chip-internal counter. */ + short_retry = min(short_retry, (unsigned int)0xF); + long_retry = min(long_retry, (unsigned int)0xF); + + b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0006, short_retry); + b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0007, long_retry); +} + +static int b43legacy_op_dev_config(struct ieee80211_hw *hw, + u32 changed) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + struct b43legacy_wldev *dev; + struct b43legacy_phy *phy; + struct ieee80211_conf *conf = &hw->conf; + unsigned long flags; + unsigned int new_phymode = 0xFFFF; + int antenna_tx; + int err = 0; + + antenna_tx = B43legacy_ANTENNA_DEFAULT; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + phy = &dev->phy; + + if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + b43legacy_set_retry_limits(dev, + conf->short_frame_max_tx_count, + conf->long_frame_max_tx_count); + changed &= ~IEEE80211_CONF_CHANGE_RETRY_LIMITS; + if (!changed) + goto out_unlock_mutex; + + /* Switch the PHY mode (if necessary). */ + switch (conf->chandef.chan->band) { + case IEEE80211_BAND_2GHZ: + if (phy->type == B43legacy_PHYTYPE_B) + new_phymode = B43legacy_PHYMODE_B; + else + new_phymode = B43legacy_PHYMODE_G; + break; + default: + B43legacy_WARN_ON(1); + } + err = b43legacy_switch_phymode(wl, new_phymode); + if (err) + goto out_unlock_mutex; + + /* Disable IRQs while reconfiguring the device. + * This makes it possible to drop the spinlock throughout + * the reconfiguration process. */ + spin_lock_irqsave(&wl->irq_lock, flags); + if (b43legacy_status(dev) < B43legacy_STAT_STARTED) { + spin_unlock_irqrestore(&wl->irq_lock, flags); + goto out_unlock_mutex; + } + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); + spin_unlock_irqrestore(&wl->irq_lock, flags); + b43legacy_synchronize_irq(dev); + + /* Switch to the requested channel. + * The firmware takes care of races with the TX handler. */ + if (conf->chandef.chan->hw_value != phy->channel) + b43legacy_radio_selectchannel(dev, conf->chandef.chan->hw_value, + 0); + + dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR); + + /* Adjust the desired TX power level. */ + if (conf->power_level != 0) { + if (conf->power_level != phy->power_level) { + phy->power_level = conf->power_level; + b43legacy_phy_xmitpower(dev); + } + } + + /* Antennas for RX and management frame TX. */ + b43legacy_mgmtframe_txantenna(dev, antenna_tx); + + if (wl->radio_enabled != phy->radio_on) { + if (wl->radio_enabled) { + b43legacy_radio_turn_on(dev); + b43legacyinfo(dev->wl, "Radio turned on by software\n"); + if (!dev->radio_hw_enable) + b43legacyinfo(dev->wl, "The hardware RF-kill" + " button still turns the radio" + " physically off. Press the" + " button to turn it on.\n"); + } else { + b43legacy_radio_turn_off(dev, 0); + b43legacyinfo(dev->wl, "Radio turned off by" + " software\n"); + } + } + + spin_lock_irqsave(&wl->irq_lock, flags); + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); + mmiowb(); + spin_unlock_irqrestore(&wl->irq_lock, flags); +out_unlock_mutex: + mutex_unlock(&wl->mutex); + + return err; +} + +static void b43legacy_update_basic_rates(struct b43legacy_wldev *dev, u32 brates) +{ + struct ieee80211_supported_band *sband = + dev->wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + struct ieee80211_rate *rate; + int i; + u16 basic, direct, offset, basic_offset, rateptr; + + for (i = 0; i < sband->n_bitrates; i++) { + rate = &sband->bitrates[i]; + + if (b43legacy_is_cck_rate(rate->hw_value)) { + direct = B43legacy_SHM_SH_CCKDIRECT; + basic = B43legacy_SHM_SH_CCKBASIC; + offset = b43legacy_plcp_get_ratecode_cck(rate->hw_value); + offset &= 0xF; + } else { + direct = B43legacy_SHM_SH_OFDMDIRECT; + basic = B43legacy_SHM_SH_OFDMBASIC; + offset = b43legacy_plcp_get_ratecode_ofdm(rate->hw_value); + offset &= 0xF; + } + + rate = ieee80211_get_response_rate(sband, brates, rate->bitrate); + + if (b43legacy_is_cck_rate(rate->hw_value)) { + basic_offset = b43legacy_plcp_get_ratecode_cck(rate->hw_value); + basic_offset &= 0xF; + } else { + basic_offset = b43legacy_plcp_get_ratecode_ofdm(rate->hw_value); + basic_offset &= 0xF; + } + + /* + * Get the pointer that we need to point to + * from the direct map + */ + rateptr = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + direct + 2 * basic_offset); + /* and write it to the basic map */ + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + basic + 2 * offset, rateptr); + } +} + +static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, + u32 changed) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + struct b43legacy_wldev *dev; + unsigned long flags; + + mutex_lock(&wl->mutex); + B43legacy_WARN_ON(wl->vif != vif); + + dev = wl->current_dev; + + /* Disable IRQs while reconfiguring the device. + * This makes it possible to drop the spinlock throughout + * the reconfiguration process. */ + spin_lock_irqsave(&wl->irq_lock, flags); + if (b43legacy_status(dev) < B43legacy_STAT_STARTED) { + spin_unlock_irqrestore(&wl->irq_lock, flags); + goto out_unlock_mutex; + } + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); + + if (changed & BSS_CHANGED_BSSID) { + b43legacy_synchronize_irq(dev); + + if (conf->bssid) + memcpy(wl->bssid, conf->bssid, ETH_ALEN); + else + eth_zero_addr(wl->bssid); + } + + if (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) { + if (changed & BSS_CHANGED_BEACON && + (b43legacy_is_mode(wl, NL80211_IFTYPE_AP) || + b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC))) + b43legacy_update_templates(wl); + + if (changed & BSS_CHANGED_BSSID) + b43legacy_write_mac_bssid_templates(dev); + } + spin_unlock_irqrestore(&wl->irq_lock, flags); + + b43legacy_mac_suspend(dev); + + if (changed & BSS_CHANGED_BEACON_INT && + (b43legacy_is_mode(wl, NL80211_IFTYPE_AP) || + b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC))) + b43legacy_set_beacon_int(dev, conf->beacon_int); + + if (changed & BSS_CHANGED_BASIC_RATES) + b43legacy_update_basic_rates(dev, conf->basic_rates); + + if (changed & BSS_CHANGED_ERP_SLOT) { + if (conf->use_short_slot) + b43legacy_short_slot_timing_enable(dev); + else + b43legacy_short_slot_timing_disable(dev); + } + + b43legacy_mac_enable(dev); + + spin_lock_irqsave(&wl->irq_lock, flags); + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); + /* XXX: why? */ + mmiowb(); + spin_unlock_irqrestore(&wl->irq_lock, flags); + out_unlock_mutex: + mutex_unlock(&wl->mutex); +} + +static void b43legacy_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed, + unsigned int *fflags,u64 multicast) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + struct b43legacy_wldev *dev = wl->current_dev; + unsigned long flags; + + if (!dev) { + *fflags = 0; + return; + } + + spin_lock_irqsave(&wl->irq_lock, flags); + *fflags &= FIF_ALLMULTI | + FIF_FCSFAIL | + FIF_PLCPFAIL | + FIF_CONTROL | + FIF_OTHER_BSS | + FIF_BCN_PRBRESP_PROMISC; + + changed &= FIF_ALLMULTI | + FIF_FCSFAIL | + FIF_PLCPFAIL | + FIF_CONTROL | + FIF_OTHER_BSS | + FIF_BCN_PRBRESP_PROMISC; + + wl->filter_flags = *fflags; + + if (changed && b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) + b43legacy_adjust_opmode(dev); + spin_unlock_irqrestore(&wl->irq_lock, flags); +} + +/* Locking: wl->mutex */ +static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev) +{ + struct b43legacy_wl *wl = dev->wl; + unsigned long flags; + int queue_num; + + if (b43legacy_status(dev) < B43legacy_STAT_STARTED) + return; + + /* Disable and sync interrupts. We must do this before than + * setting the status to INITIALIZED, as the interrupt handler + * won't care about IRQs then. */ + spin_lock_irqsave(&wl->irq_lock, flags); + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0); + b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK); /* flush */ + spin_unlock_irqrestore(&wl->irq_lock, flags); + b43legacy_synchronize_irq(dev); + + b43legacy_set_status(dev, B43legacy_STAT_INITIALIZED); + + mutex_unlock(&wl->mutex); + /* Must unlock as it would otherwise deadlock. No races here. + * Cancel the possibly running self-rearming periodic work. */ + cancel_delayed_work_sync(&dev->periodic_work); + cancel_work_sync(&wl->tx_work); + mutex_lock(&wl->mutex); + + /* Drain all TX queues. */ + for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { + while (skb_queue_len(&wl->tx_queue[queue_num])) + dev_kfree_skb(skb_dequeue(&wl->tx_queue[queue_num])); + } + +b43legacy_mac_suspend(dev); + free_irq(dev->dev->irq, dev); + b43legacydbg(wl, "Wireless interface stopped\n"); +} + +/* Locking: wl->mutex */ +static int b43legacy_wireless_core_start(struct b43legacy_wldev *dev) +{ + int err; + + B43legacy_WARN_ON(b43legacy_status(dev) != B43legacy_STAT_INITIALIZED); + + drain_txstatus_queue(dev); + err = request_irq(dev->dev->irq, b43legacy_interrupt_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (err) { + b43legacyerr(dev->wl, "Cannot request IRQ-%d\n", + dev->dev->irq); + goto out; + } + /* We are ready to run. */ + ieee80211_wake_queues(dev->wl->hw); + b43legacy_set_status(dev, B43legacy_STAT_STARTED); + + /* Start data flow (TX/RX) */ + b43legacy_mac_enable(dev); + b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask); + + /* Start maintenance work */ + b43legacy_periodic_tasks_setup(dev); + + b43legacydbg(dev->wl, "Wireless interface started\n"); +out: + return err; +} + +/* Get PHY and RADIO versioning numbers */ +static int b43legacy_phy_versioning(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u32 tmp; + u8 analog_type; + u8 phy_type; + u8 phy_rev; + u16 radio_manuf; + u16 radio_ver; + u16 radio_rev; + int unsupported = 0; + + /* Get PHY versioning */ + tmp = b43legacy_read16(dev, B43legacy_MMIO_PHY_VER); + analog_type = (tmp & B43legacy_PHYVER_ANALOG) + >> B43legacy_PHYVER_ANALOG_SHIFT; + phy_type = (tmp & B43legacy_PHYVER_TYPE) >> B43legacy_PHYVER_TYPE_SHIFT; + phy_rev = (tmp & B43legacy_PHYVER_VERSION); + switch (phy_type) { + case B43legacy_PHYTYPE_B: + if (phy_rev != 2 && phy_rev != 4 + && phy_rev != 6 && phy_rev != 7) + unsupported = 1; + break; + case B43legacy_PHYTYPE_G: + if (phy_rev > 8) + unsupported = 1; + break; + default: + unsupported = 1; + } + if (unsupported) { + b43legacyerr(dev->wl, "FOUND UNSUPPORTED PHY " + "(Analog %u, Type %u, Revision %u)\n", + analog_type, phy_type, phy_rev); + return -EOPNOTSUPP; + } + b43legacydbg(dev->wl, "Found PHY: Analog %u, Type %u, Revision %u\n", + analog_type, phy_type, phy_rev); + + + /* Get RADIO versioning */ + if (dev->dev->bus->chip_id == 0x4317) { + if (dev->dev->bus->chip_rev == 0) + tmp = 0x3205017F; + else if (dev->dev->bus->chip_rev == 1) + tmp = 0x4205017F; + else + tmp = 0x5205017F; + } else { + b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, + B43legacy_RADIOCTL_ID); + tmp = b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_HIGH); + tmp <<= 16; + b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, + B43legacy_RADIOCTL_ID); + tmp |= b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_LOW); + } + radio_manuf = (tmp & 0x00000FFF); + radio_ver = (tmp & 0x0FFFF000) >> 12; + radio_rev = (tmp & 0xF0000000) >> 28; + switch (phy_type) { + case B43legacy_PHYTYPE_B: + if ((radio_ver & 0xFFF0) != 0x2050) + unsupported = 1; + break; + case B43legacy_PHYTYPE_G: + if (radio_ver != 0x2050) + unsupported = 1; + break; + default: + B43legacy_BUG_ON(1); + } + if (unsupported) { + b43legacyerr(dev->wl, "FOUND UNSUPPORTED RADIO " + "(Manuf 0x%X, Version 0x%X, Revision %u)\n", + radio_manuf, radio_ver, radio_rev); + return -EOPNOTSUPP; + } + b43legacydbg(dev->wl, "Found Radio: Manuf 0x%X, Version 0x%X," + " Revision %u\n", radio_manuf, radio_ver, radio_rev); + + + phy->radio_manuf = radio_manuf; + phy->radio_ver = radio_ver; + phy->radio_rev = radio_rev; + + phy->analog = analog_type; + phy->type = phy_type; + phy->rev = phy_rev; + + return 0; +} + +static void setup_struct_phy_for_init(struct b43legacy_wldev *dev, + struct b43legacy_phy *phy) +{ + struct b43legacy_lopair *lo; + int i; + + memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig)); + memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos)); + + /* Assume the radio is enabled. If it's not enabled, the state will + * immediately get fixed on the first periodic work run. */ + dev->radio_hw_enable = true; + + phy->savedpctlreg = 0xFFFF; + phy->aci_enable = false; + phy->aci_wlan_automatic = false; + phy->aci_hw_rssi = false; + + lo = phy->_lo_pairs; + if (lo) + memset(lo, 0, sizeof(struct b43legacy_lopair) * + B43legacy_LO_COUNT); + phy->max_lb_gain = 0; + phy->trsw_rx_gain = 0; + + /* Set default attenuation values. */ + phy->bbatt = b43legacy_default_baseband_attenuation(dev); + phy->rfatt = b43legacy_default_radio_attenuation(dev); + phy->txctl1 = b43legacy_default_txctl1(dev); + phy->txpwr_offset = 0; + + /* NRSSI */ + phy->nrssislope = 0; + for (i = 0; i < ARRAY_SIZE(phy->nrssi); i++) + phy->nrssi[i] = -1000; + for (i = 0; i < ARRAY_SIZE(phy->nrssi_lt); i++) + phy->nrssi_lt[i] = i; + + phy->lofcal = 0xFFFF; + phy->initval = 0xFFFF; + + phy->interfmode = B43legacy_INTERFMODE_NONE; + phy->channel = 0xFF; +} + +static void setup_struct_wldev_for_init(struct b43legacy_wldev *dev) +{ + /* Flags */ + dev->dfq_valid = false; + + /* Stats */ + memset(&dev->stats, 0, sizeof(dev->stats)); + + setup_struct_phy_for_init(dev, &dev->phy); + + /* IRQ related flags */ + dev->irq_reason = 0; + memset(dev->dma_reason, 0, sizeof(dev->dma_reason)); + dev->irq_mask = B43legacy_IRQ_MASKTEMPLATE; + + dev->mac_suspended = 1; + + /* Noise calculation context */ + memset(&dev->noisecalc, 0, sizeof(dev->noisecalc)); +} + +static void b43legacy_set_synth_pu_delay(struct b43legacy_wldev *dev, + bool idle) { + u16 pu_delay = 1050; + + if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_ADHOC) || idle) + pu_delay = 500; + if ((dev->phy.radio_ver == 0x2050) && (dev->phy.radio_rev == 8)) + pu_delay = max(pu_delay, (u16)2400); + + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_SPUWKUP, pu_delay); +} + +/* Set the TSF CFP pre-TargetBeaconTransmissionTime. */ +static void b43legacy_set_pretbtt(struct b43legacy_wldev *dev) +{ + u16 pretbtt; + + /* The time value is in microseconds. */ + if (b43legacy_is_mode(dev->wl, NL80211_IFTYPE_ADHOC)) + pretbtt = 2; + else + pretbtt = 250; + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_PRETBTT, pretbtt); + b43legacy_write16(dev, B43legacy_MMIO_TSF_CFP_PRETBTT, pretbtt); +} + +/* Shutdown a wireless core */ +/* Locking: wl->mutex */ +static void b43legacy_wireless_core_exit(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u32 macctl; + + B43legacy_WARN_ON(b43legacy_status(dev) > B43legacy_STAT_INITIALIZED); + if (b43legacy_status(dev) != B43legacy_STAT_INITIALIZED) + return; + b43legacy_set_status(dev, B43legacy_STAT_UNINIT); + + /* Stop the microcode PSM. */ + macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + macctl &= ~B43legacy_MACCTL_PSM_RUN; + macctl |= B43legacy_MACCTL_PSM_JMP0; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl); + + b43legacy_leds_exit(dev); + b43legacy_rng_exit(dev->wl); + b43legacy_pio_free(dev); + b43legacy_dma_free(dev); + b43legacy_chip_exit(dev); + b43legacy_radio_turn_off(dev, 1); + b43legacy_switch_analog(dev, 0); + if (phy->dyn_tssi_tbl) + kfree(phy->tssi2dbm); + kfree(phy->lo_control); + phy->lo_control = NULL; + if (dev->wl->current_beacon) { + dev_kfree_skb_any(dev->wl->current_beacon); + dev->wl->current_beacon = NULL; + } + + ssb_device_disable(dev->dev, 0); + ssb_bus_may_powerdown(dev->dev->bus); +} + +static void prepare_phy_data_for_init(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + int i; + + /* Set default attenuation values. */ + phy->bbatt = b43legacy_default_baseband_attenuation(dev); + phy->rfatt = b43legacy_default_radio_attenuation(dev); + phy->txctl1 = b43legacy_default_txctl1(dev); + phy->txctl2 = 0xFFFF; + phy->txpwr_offset = 0; + + /* NRSSI */ + phy->nrssislope = 0; + for (i = 0; i < ARRAY_SIZE(phy->nrssi); i++) + phy->nrssi[i] = -1000; + for (i = 0; i < ARRAY_SIZE(phy->nrssi_lt); i++) + phy->nrssi_lt[i] = i; + + phy->lofcal = 0xFFFF; + phy->initval = 0xFFFF; + + phy->aci_enable = false; + phy->aci_wlan_automatic = false; + phy->aci_hw_rssi = false; + + phy->antenna_diversity = 0xFFFF; + memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig)); + memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos)); + + /* Flags */ + phy->calibrated = 0; + + if (phy->_lo_pairs) + memset(phy->_lo_pairs, 0, + sizeof(struct b43legacy_lopair) * B43legacy_LO_COUNT); + memset(phy->loopback_gain, 0, sizeof(phy->loopback_gain)); +} + +/* Initialize a wireless core */ +static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev) +{ + struct b43legacy_wl *wl = dev->wl; + struct ssb_bus *bus = dev->dev->bus; + struct b43legacy_phy *phy = &dev->phy; + struct ssb_sprom *sprom = &dev->dev->bus->sprom; + int err; + u32 hf; + u32 tmp; + + B43legacy_WARN_ON(b43legacy_status(dev) != B43legacy_STAT_UNINIT); + + err = ssb_bus_powerup(bus, 0); + if (err) + goto out; + if (!ssb_device_is_enabled(dev->dev)) { + tmp = phy->gmode ? B43legacy_TMSLOW_GMODE : 0; + b43legacy_wireless_core_reset(dev, tmp); + } + + if ((phy->type == B43legacy_PHYTYPE_B) || + (phy->type == B43legacy_PHYTYPE_G)) { + phy->_lo_pairs = kzalloc(sizeof(struct b43legacy_lopair) + * B43legacy_LO_COUNT, + GFP_KERNEL); + if (!phy->_lo_pairs) + return -ENOMEM; + } + setup_struct_wldev_for_init(dev); + + err = b43legacy_phy_init_tssi2dbm_table(dev); + if (err) + goto err_kfree_lo_control; + + /* Enable IRQ routing to this device. */ + ssb_pcicore_dev_irqvecs_enable(&bus->pcicore, dev->dev); + + prepare_phy_data_for_init(dev); + b43legacy_phy_calibrate(dev); + err = b43legacy_chip_init(dev); + if (err) + goto err_kfree_tssitbl; + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_WLCOREREV, + dev->dev->id.revision); + hf = b43legacy_hf_read(dev); + if (phy->type == B43legacy_PHYTYPE_G) { + hf |= B43legacy_HF_SYMW; + if (phy->rev == 1) + hf |= B43legacy_HF_GDCW; + if (sprom->boardflags_lo & B43legacy_BFL_PACTRL) + hf |= B43legacy_HF_OFDMPABOOST; + } else if (phy->type == B43legacy_PHYTYPE_B) { + hf |= B43legacy_HF_SYMW; + if (phy->rev >= 2 && phy->radio_ver == 0x2050) + hf &= ~B43legacy_HF_GDCW; + } + b43legacy_hf_write(dev, hf); + + b43legacy_set_retry_limits(dev, + B43legacy_DEFAULT_SHORT_RETRY_LIMIT, + B43legacy_DEFAULT_LONG_RETRY_LIMIT); + + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + 0x0044, 3); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + 0x0046, 2); + + /* Disable sending probe responses from firmware. + * Setting the MaxTime to one usec will always trigger + * a timeout, so we never send any probe resp. + * A timeout of zero is infinite. */ + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, + B43legacy_SHM_SH_PRMAXTIME, 1); + + b43legacy_rate_memory_init(dev); + + /* Minimum Contention Window */ + if (phy->type == B43legacy_PHYTYPE_B) + b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, + 0x0003, 31); + else + b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, + 0x0003, 15); + /* Maximum Contention Window */ + b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, + 0x0004, 1023); + + do { + if (b43legacy_using_pio(dev)) + err = b43legacy_pio_init(dev); + else { + err = b43legacy_dma_init(dev); + if (!err) + b43legacy_qos_init(dev); + } + } while (err == -EAGAIN); + if (err) + goto err_chip_exit; + + b43legacy_set_synth_pu_delay(dev, 1); + + ssb_bus_powerup(bus, 1); /* Enable dynamic PCTL */ + b43legacy_upload_card_macaddress(dev); + b43legacy_security_init(dev); + b43legacy_rng_init(wl); + + ieee80211_wake_queues(dev->wl->hw); + b43legacy_set_status(dev, B43legacy_STAT_INITIALIZED); + + b43legacy_leds_init(dev); +out: + return err; + +err_chip_exit: + b43legacy_chip_exit(dev); +err_kfree_tssitbl: + if (phy->dyn_tssi_tbl) + kfree(phy->tssi2dbm); +err_kfree_lo_control: + kfree(phy->lo_control); + phy->lo_control = NULL; + ssb_bus_may_powerdown(bus); + B43legacy_WARN_ON(b43legacy_status(dev) != B43legacy_STAT_UNINIT); + return err; +} + +static int b43legacy_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + struct b43legacy_wldev *dev; + unsigned long flags; + int err = -EOPNOTSUPP; + + /* TODO: allow WDS/AP devices to coexist */ + + if (vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_WDS && + vif->type != NL80211_IFTYPE_ADHOC) + return -EOPNOTSUPP; + + mutex_lock(&wl->mutex); + if (wl->operating) + goto out_mutex_unlock; + + b43legacydbg(wl, "Adding Interface type %d\n", vif->type); + + dev = wl->current_dev; + wl->operating = true; + wl->vif = vif; + wl->if_type = vif->type; + memcpy(wl->mac_addr, vif->addr, ETH_ALEN); + + spin_lock_irqsave(&wl->irq_lock, flags); + b43legacy_adjust_opmode(dev); + b43legacy_set_pretbtt(dev); + b43legacy_set_synth_pu_delay(dev, 0); + b43legacy_upload_card_macaddress(dev); + spin_unlock_irqrestore(&wl->irq_lock, flags); + + err = 0; + out_mutex_unlock: + mutex_unlock(&wl->mutex); + + return err; +} + +static void b43legacy_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + struct b43legacy_wldev *dev = wl->current_dev; + unsigned long flags; + + b43legacydbg(wl, "Removing Interface type %d\n", vif->type); + + mutex_lock(&wl->mutex); + + B43legacy_WARN_ON(!wl->operating); + B43legacy_WARN_ON(wl->vif != vif); + wl->vif = NULL; + + wl->operating = false; + + spin_lock_irqsave(&wl->irq_lock, flags); + b43legacy_adjust_opmode(dev); + eth_zero_addr(wl->mac_addr); + b43legacy_upload_card_macaddress(dev); + spin_unlock_irqrestore(&wl->irq_lock, flags); + + mutex_unlock(&wl->mutex); +} + +static int b43legacy_op_start(struct ieee80211_hw *hw) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + struct b43legacy_wldev *dev = wl->current_dev; + int did_init = 0; + int err = 0; + + /* Kill all old instance specific information to make sure + * the card won't use it in the short timeframe between start + * and mac80211 reconfiguring it. */ + eth_zero_addr(wl->bssid); + eth_zero_addr(wl->mac_addr); + wl->filter_flags = 0; + wl->beacon0_uploaded = false; + wl->beacon1_uploaded = false; + wl->beacon_templates_virgin = true; + wl->radio_enabled = true; + + mutex_lock(&wl->mutex); + + if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) { + err = b43legacy_wireless_core_init(dev); + if (err) + goto out_mutex_unlock; + did_init = 1; + } + + if (b43legacy_status(dev) < B43legacy_STAT_STARTED) { + err = b43legacy_wireless_core_start(dev); + if (err) { + if (did_init) + b43legacy_wireless_core_exit(dev); + goto out_mutex_unlock; + } + } + + wiphy_rfkill_start_polling(hw->wiphy); + +out_mutex_unlock: + mutex_unlock(&wl->mutex); + + return err; +} + +static void b43legacy_op_stop(struct ieee80211_hw *hw) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + struct b43legacy_wldev *dev = wl->current_dev; + + cancel_work_sync(&(wl->beacon_update_trigger)); + + mutex_lock(&wl->mutex); + if (b43legacy_status(dev) >= B43legacy_STAT_STARTED) + b43legacy_wireless_core_stop(dev); + b43legacy_wireless_core_exit(dev); + wl->radio_enabled = false; + mutex_unlock(&wl->mutex); +} + +static int b43legacy_op_beacon_set_tim(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, bool set) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + unsigned long flags; + + spin_lock_irqsave(&wl->irq_lock, flags); + b43legacy_update_templates(wl); + spin_unlock_irqrestore(&wl->irq_lock, flags); + + return 0; +} + +static int b43legacy_op_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + struct b43legacy_wldev *dev = wl->current_dev; + struct ieee80211_conf *conf = &hw->conf; + + if (idx != 0) + return -ENOENT; + + survey->channel = conf->chandef.chan; + survey->filled = SURVEY_INFO_NOISE_DBM; + survey->noise = dev->stats.link_noise; + + return 0; +} + +static const struct ieee80211_ops b43legacy_hw_ops = { + .tx = b43legacy_op_tx, + .conf_tx = b43legacy_op_conf_tx, + .add_interface = b43legacy_op_add_interface, + .remove_interface = b43legacy_op_remove_interface, + .config = b43legacy_op_dev_config, + .bss_info_changed = b43legacy_op_bss_info_changed, + .configure_filter = b43legacy_op_configure_filter, + .get_stats = b43legacy_op_get_stats, + .start = b43legacy_op_start, + .stop = b43legacy_op_stop, + .set_tim = b43legacy_op_beacon_set_tim, + .get_survey = b43legacy_op_get_survey, + .rfkill_poll = b43legacy_rfkill_poll, +}; + +/* Hard-reset the chip. Do not call this directly. + * Use b43legacy_controller_restart() + */ +static void b43legacy_chip_reset(struct work_struct *work) +{ + struct b43legacy_wldev *dev = + container_of(work, struct b43legacy_wldev, restart_work); + struct b43legacy_wl *wl = dev->wl; + int err = 0; + int prev_status; + + mutex_lock(&wl->mutex); + + prev_status = b43legacy_status(dev); + /* Bring the device down... */ + if (prev_status >= B43legacy_STAT_STARTED) + b43legacy_wireless_core_stop(dev); + if (prev_status >= B43legacy_STAT_INITIALIZED) + b43legacy_wireless_core_exit(dev); + + /* ...and up again. */ + if (prev_status >= B43legacy_STAT_INITIALIZED) { + err = b43legacy_wireless_core_init(dev); + if (err) + goto out; + } + if (prev_status >= B43legacy_STAT_STARTED) { + err = b43legacy_wireless_core_start(dev); + if (err) { + b43legacy_wireless_core_exit(dev); + goto out; + } + } +out: + if (err) + wl->current_dev = NULL; /* Failed to init the dev. */ + mutex_unlock(&wl->mutex); + if (err) + b43legacyerr(wl, "Controller restart FAILED\n"); + else + b43legacyinfo(wl, "Controller restarted\n"); +} + +static int b43legacy_setup_modes(struct b43legacy_wldev *dev, + int have_bphy, + int have_gphy) +{ + struct ieee80211_hw *hw = dev->wl->hw; + struct b43legacy_phy *phy = &dev->phy; + + phy->possible_phymodes = 0; + if (have_bphy) { + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &b43legacy_band_2GHz_BPHY; + phy->possible_phymodes |= B43legacy_PHYMODE_B; + } + + if (have_gphy) { + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &b43legacy_band_2GHz_GPHY; + phy->possible_phymodes |= B43legacy_PHYMODE_G; + } + + return 0; +} + +static void b43legacy_wireless_core_detach(struct b43legacy_wldev *dev) +{ + /* We release firmware that late to not be required to re-request + * is all the time when we reinit the core. */ + b43legacy_release_firmware(dev); +} + +static int b43legacy_wireless_core_attach(struct b43legacy_wldev *dev) +{ + struct b43legacy_wl *wl = dev->wl; + struct ssb_bus *bus = dev->dev->bus; + struct pci_dev *pdev = (bus->bustype == SSB_BUSTYPE_PCI) ? bus->host_pci : NULL; + int err; + int have_bphy = 0; + int have_gphy = 0; + u32 tmp; + + /* Do NOT do any device initialization here. + * Do it in wireless_core_init() instead. + * This function is for gathering basic information about the HW, only. + * Also some structs may be set up here. But most likely you want to + * have that in core_init(), too. + */ + + err = ssb_bus_powerup(bus, 0); + if (err) { + b43legacyerr(wl, "Bus powerup failed\n"); + goto out; + } + /* Get the PHY type. */ + if (dev->dev->id.revision >= 5) { + u32 tmshigh; + + tmshigh = ssb_read32(dev->dev, SSB_TMSHIGH); + have_gphy = !!(tmshigh & B43legacy_TMSHIGH_GPHY); + if (!have_gphy) + have_bphy = 1; + } else if (dev->dev->id.revision == 4) + have_gphy = 1; + else + have_bphy = 1; + + dev->phy.gmode = (have_gphy || have_bphy); + dev->phy.radio_on = true; + tmp = dev->phy.gmode ? B43legacy_TMSLOW_GMODE : 0; + b43legacy_wireless_core_reset(dev, tmp); + + err = b43legacy_phy_versioning(dev); + if (err) + goto err_powerdown; + /* Check if this device supports multiband. */ + if (!pdev || + (pdev->device != 0x4312 && + pdev->device != 0x4319 && + pdev->device != 0x4324)) { + /* No multiband support. */ + have_bphy = 0; + have_gphy = 0; + switch (dev->phy.type) { + case B43legacy_PHYTYPE_B: + have_bphy = 1; + break; + case B43legacy_PHYTYPE_G: + have_gphy = 1; + break; + default: + B43legacy_BUG_ON(1); + } + } + dev->phy.gmode = (have_gphy || have_bphy); + tmp = dev->phy.gmode ? B43legacy_TMSLOW_GMODE : 0; + b43legacy_wireless_core_reset(dev, tmp); + + err = b43legacy_validate_chipaccess(dev); + if (err) + goto err_powerdown; + err = b43legacy_setup_modes(dev, have_bphy, have_gphy); + if (err) + goto err_powerdown; + + /* Now set some default "current_dev" */ + if (!wl->current_dev) + wl->current_dev = dev; + INIT_WORK(&dev->restart_work, b43legacy_chip_reset); + + b43legacy_radio_turn_off(dev, 1); + b43legacy_switch_analog(dev, 0); + ssb_device_disable(dev->dev, 0); + ssb_bus_may_powerdown(bus); + +out: + return err; + +err_powerdown: + ssb_bus_may_powerdown(bus); + return err; +} + +static void b43legacy_one_core_detach(struct ssb_device *dev) +{ + struct b43legacy_wldev *wldev; + struct b43legacy_wl *wl; + + /* Do not cancel ieee80211-workqueue based work here. + * See comment in b43legacy_remove(). */ + + wldev = ssb_get_drvdata(dev); + wl = wldev->wl; + b43legacy_debugfs_remove_device(wldev); + b43legacy_wireless_core_detach(wldev); + list_del(&wldev->list); + wl->nr_devs--; + ssb_set_drvdata(dev, NULL); + kfree(wldev); +} + +static int b43legacy_one_core_attach(struct ssb_device *dev, + struct b43legacy_wl *wl) +{ + struct b43legacy_wldev *wldev; + int err = -ENOMEM; + + wldev = kzalloc(sizeof(*wldev), GFP_KERNEL); + if (!wldev) + goto out; + + wldev->dev = dev; + wldev->wl = wl; + b43legacy_set_status(wldev, B43legacy_STAT_UNINIT); + wldev->bad_frames_preempt = modparam_bad_frames_preempt; + tasklet_init(&wldev->isr_tasklet, + (void (*)(unsigned long))b43legacy_interrupt_tasklet, + (unsigned long)wldev); + if (modparam_pio) + wldev->__using_pio = true; + INIT_LIST_HEAD(&wldev->list); + + err = b43legacy_wireless_core_attach(wldev); + if (err) + goto err_kfree_wldev; + + list_add(&wldev->list, &wl->devlist); + wl->nr_devs++; + ssb_set_drvdata(dev, wldev); + b43legacy_debugfs_add_device(wldev); +out: + return err; + +err_kfree_wldev: + kfree(wldev); + return err; +} + +static void b43legacy_sprom_fixup(struct ssb_bus *bus) +{ + /* boardflags workarounds */ + if (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE && + bus->boardinfo.type == 0x4E && + bus->sprom.board_rev > 0x40) + bus->sprom.boardflags_lo |= B43legacy_BFL_PACTRL; +} + +static void b43legacy_wireless_exit(struct ssb_device *dev, + struct b43legacy_wl *wl) +{ + struct ieee80211_hw *hw = wl->hw; + + ssb_set_devtypedata(dev, NULL); + ieee80211_free_hw(hw); +} + +static int b43legacy_wireless_init(struct ssb_device *dev) +{ + struct ssb_sprom *sprom = &dev->bus->sprom; + struct ieee80211_hw *hw; + struct b43legacy_wl *wl; + int err = -ENOMEM; + int queue_num; + + b43legacy_sprom_fixup(dev->bus); + + hw = ieee80211_alloc_hw(sizeof(*wl), &b43legacy_hw_ops); + if (!hw) { + b43legacyerr(NULL, "Could not allocate ieee80211 device\n"); + goto out; + } + + /* fill hw info */ + ieee80211_hw_set(hw, RX_INCLUDES_FCS); + ieee80211_hw_set(hw, SIGNAL_DBM); + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_ADHOC); + hw->queues = 1; /* FIXME: hardware has more queues */ + hw->max_rates = 2; + SET_IEEE80211_DEV(hw, dev->dev); + if (is_valid_ether_addr(sprom->et1mac)) + SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac); + else + SET_IEEE80211_PERM_ADDR(hw, sprom->il0mac); + + /* Get and initialize struct b43legacy_wl */ + wl = hw_to_b43legacy_wl(hw); + memset(wl, 0, sizeof(*wl)); + wl->hw = hw; + spin_lock_init(&wl->irq_lock); + spin_lock_init(&wl->leds_lock); + mutex_init(&wl->mutex); + INIT_LIST_HEAD(&wl->devlist); + INIT_WORK(&wl->beacon_update_trigger, b43legacy_beacon_update_trigger_work); + INIT_WORK(&wl->tx_work, b43legacy_tx_work); + + /* Initialize queues and flags. */ + for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { + skb_queue_head_init(&wl->tx_queue[queue_num]); + wl->tx_queue_stopped[queue_num] = 0; + } + + ssb_set_devtypedata(dev, wl); + b43legacyinfo(wl, "Broadcom %04X WLAN found (core revision %u)\n", + dev->bus->chip_id, dev->id.revision); + err = 0; +out: + return err; +} + +static int b43legacy_probe(struct ssb_device *dev, + const struct ssb_device_id *id) +{ + struct b43legacy_wl *wl; + int err; + int first = 0; + + wl = ssb_get_devtypedata(dev); + if (!wl) { + /* Probing the first core - setup common struct b43legacy_wl */ + first = 1; + err = b43legacy_wireless_init(dev); + if (err) + goto out; + wl = ssb_get_devtypedata(dev); + B43legacy_WARN_ON(!wl); + } + err = b43legacy_one_core_attach(dev, wl); + if (err) + goto err_wireless_exit; + + /* setup and start work to load firmware */ + INIT_WORK(&wl->firmware_load, b43legacy_request_firmware); + schedule_work(&wl->firmware_load); + +out: + return err; + +err_wireless_exit: + if (first) + b43legacy_wireless_exit(dev, wl); + return err; +} + +static void b43legacy_remove(struct ssb_device *dev) +{ + struct b43legacy_wl *wl = ssb_get_devtypedata(dev); + struct b43legacy_wldev *wldev = ssb_get_drvdata(dev); + + /* We must cancel any work here before unregistering from ieee80211, + * 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) + return; /* NULL if fw never loaded */ + if (wl->current_dev == wldev) + ieee80211_unregister_hw(wl->hw); + + b43legacy_one_core_detach(dev); + + if (list_empty(&wl->devlist)) + /* Last core on the chip unregistered. + * We can destroy common struct b43legacy_wl. + */ + b43legacy_wireless_exit(dev, wl); +} + +/* Perform a hardware reset. This can be called from any context. */ +void b43legacy_controller_restart(struct b43legacy_wldev *dev, + const char *reason) +{ + /* Must avoid requeueing, if we are in shutdown. */ + if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) + return; + b43legacyinfo(dev->wl, "Controller RESET (%s) ...\n", reason); + ieee80211_queue_work(dev->wl->hw, &dev->restart_work); +} + +#ifdef CONFIG_PM + +static int b43legacy_suspend(struct ssb_device *dev, pm_message_t state) +{ + struct b43legacy_wldev *wldev = ssb_get_drvdata(dev); + struct b43legacy_wl *wl = wldev->wl; + + b43legacydbg(wl, "Suspending...\n"); + + mutex_lock(&wl->mutex); + wldev->suspend_init_status = b43legacy_status(wldev); + if (wldev->suspend_init_status >= B43legacy_STAT_STARTED) + b43legacy_wireless_core_stop(wldev); + if (wldev->suspend_init_status >= B43legacy_STAT_INITIALIZED) + b43legacy_wireless_core_exit(wldev); + mutex_unlock(&wl->mutex); + + b43legacydbg(wl, "Device suspended.\n"); + + return 0; +} + +static int b43legacy_resume(struct ssb_device *dev) +{ + struct b43legacy_wldev *wldev = ssb_get_drvdata(dev); + struct b43legacy_wl *wl = wldev->wl; + int err = 0; + + b43legacydbg(wl, "Resuming...\n"); + + mutex_lock(&wl->mutex); + if (wldev->suspend_init_status >= B43legacy_STAT_INITIALIZED) { + err = b43legacy_wireless_core_init(wldev); + if (err) { + b43legacyerr(wl, "Resume failed at core init\n"); + goto out; + } + } + if (wldev->suspend_init_status >= B43legacy_STAT_STARTED) { + err = b43legacy_wireless_core_start(wldev); + if (err) { + b43legacy_wireless_core_exit(wldev); + b43legacyerr(wl, "Resume failed at core start\n"); + goto out; + } + } + + b43legacydbg(wl, "Device resumed.\n"); +out: + mutex_unlock(&wl->mutex); + return err; +} + +#else /* CONFIG_PM */ +# define b43legacy_suspend NULL +# define b43legacy_resume NULL +#endif /* CONFIG_PM */ + +static struct ssb_driver b43legacy_ssb_driver = { + .name = KBUILD_MODNAME, + .id_table = b43legacy_ssb_tbl, + .probe = b43legacy_probe, + .remove = b43legacy_remove, + .suspend = b43legacy_suspend, + .resume = b43legacy_resume, +}; + +static void b43legacy_print_driverinfo(void) +{ + const char *feat_pci = "", *feat_leds = "", + *feat_pio = "", *feat_dma = ""; + +#ifdef CONFIG_B43LEGACY_PCI_AUTOSELECT + feat_pci = "P"; +#endif +#ifdef CONFIG_B43LEGACY_LEDS + feat_leds = "L"; +#endif +#ifdef CONFIG_B43LEGACY_PIO + feat_pio = "I"; +#endif +#ifdef CONFIG_B43LEGACY_DMA + feat_dma = "D"; +#endif + printk(KERN_INFO "Broadcom 43xx-legacy driver loaded " + "[ Features: %s%s%s%s ]\n", + feat_pci, feat_leds, feat_pio, feat_dma); +} + +static int __init b43legacy_init(void) +{ + int err; + + b43legacy_debugfs_init(); + + err = ssb_driver_register(&b43legacy_ssb_driver); + if (err) + goto err_dfs_exit; + + b43legacy_print_driverinfo(); + + return err; + +err_dfs_exit: + b43legacy_debugfs_exit(); + return err; +} + +static void __exit b43legacy_exit(void) +{ + ssb_driver_unregister(&b43legacy_ssb_driver); + b43legacy_debugfs_exit(); +} + +module_init(b43legacy_init) +module_exit(b43legacy_exit) diff --git a/drivers/net/wireless/broadcom/b43legacy/main.h b/drivers/net/wireless/broadcom/b43legacy/main.h new file mode 100644 index 000000000000..b74a058d7bac --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/main.h @@ -0,0 +1,127 @@ +/* + + Broadcom B43legacy wireless driver + + Copyright (c) 2005 Martin Langer , + Copyright (c) 2005 Stefano Brivio + Copyright (c) 2005, 2006 Michael Buesch + Copyright (c) 2005 Danny van Dyk + Copyright (c) 2005 Andreas Jaggi + Copyright (c) 2007 Larry Finger + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef B43legacy_MAIN_H_ +#define B43legacy_MAIN_H_ + +#include "b43legacy.h" + + +#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] +#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) +/* Magic helper macro to pad structures. Ignore those above. It's magic. */ +#define PAD_BYTES(nr_bytes) P4D_BYTES(__LINE__ , (nr_bytes)) + + +/* Lightweight function to convert a frequency (in Mhz) to a channel number. */ +static inline +u8 b43legacy_freq_to_channel_bg(int freq) +{ + u8 channel; + + if (freq == 2484) + channel = 14; + else + channel = (freq - 2407) / 5; + + return channel; +} +static inline +u8 b43legacy_freq_to_channel(struct b43legacy_wldev *dev, + int freq) +{ + return b43legacy_freq_to_channel_bg(freq); +} + +/* Lightweight function to convert a channel number to a frequency (in Mhz). */ +static inline +int b43legacy_channel_to_freq_bg(u8 channel) +{ + int freq; + + if (channel == 14) + freq = 2484; + else + freq = 2407 + (5 * channel); + + return freq; +} + +static inline +int b43legacy_channel_to_freq(struct b43legacy_wldev *dev, + u8 channel) +{ + return b43legacy_channel_to_freq_bg(channel); +} + +static inline +int b43legacy_is_cck_rate(int rate) +{ + return (rate == B43legacy_CCK_RATE_1MB || + rate == B43legacy_CCK_RATE_2MB || + rate == B43legacy_CCK_RATE_5MB || + rate == B43legacy_CCK_RATE_11MB); +} + +static inline +int b43legacy_is_ofdm_rate(int rate) +{ + return !b43legacy_is_cck_rate(rate); +} + +void b43legacy_tsf_read(struct b43legacy_wldev *dev, u64 *tsf); +void b43legacy_tsf_write(struct b43legacy_wldev *dev, u64 tsf); + +u32 b43legacy_shm_read32(struct b43legacy_wldev *dev, + u16 routing, u16 offset); +u16 b43legacy_shm_read16(struct b43legacy_wldev *dev, + u16 routing, u16 offset); +void b43legacy_shm_write32(struct b43legacy_wldev *dev, + u16 routing, u16 offset, + u32 value); +void b43legacy_shm_write16(struct b43legacy_wldev *dev, + u16 routing, u16 offset, + u16 value); + +u32 b43legacy_hf_read(struct b43legacy_wldev *dev); +void b43legacy_hf_write(struct b43legacy_wldev *dev, u32 value); + +void b43legacy_dummy_transmission(struct b43legacy_wldev *dev); + +void b43legacy_wireless_core_reset(struct b43legacy_wldev *dev, u32 flags); + +void b43legacy_mac_suspend(struct b43legacy_wldev *dev); +void b43legacy_mac_enable(struct b43legacy_wldev *dev); + +void b43legacy_controller_restart(struct b43legacy_wldev *dev, + const char *reason); + +#endif /* B43legacy_MAIN_H_ */ diff --git a/drivers/net/wireless/broadcom/b43legacy/phy.c b/drivers/net/wireless/broadcom/b43legacy/phy.c new file mode 100644 index 000000000000..995c7d0c212a --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/phy.c @@ -0,0 +1,2258 @@ +/* + + Broadcom B43legacy wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + Copyright (c) 2007 Larry Finger + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include +#include +#include +#include +#include + +#include "b43legacy.h" +#include "phy.h" +#include "main.h" +#include "radio.h" +#include "ilt.h" + + +static const s8 b43legacy_tssi2dbm_b_table[] = { + 0x4D, 0x4C, 0x4B, 0x4A, + 0x4A, 0x49, 0x48, 0x47, + 0x47, 0x46, 0x45, 0x45, + 0x44, 0x43, 0x42, 0x42, + 0x41, 0x40, 0x3F, 0x3E, + 0x3D, 0x3C, 0x3B, 0x3A, + 0x39, 0x38, 0x37, 0x36, + 0x35, 0x34, 0x32, 0x31, + 0x30, 0x2F, 0x2D, 0x2C, + 0x2B, 0x29, 0x28, 0x26, + 0x25, 0x23, 0x21, 0x1F, + 0x1D, 0x1A, 0x17, 0x14, + 0x10, 0x0C, 0x06, 0x00, + -7, -7, -7, -7, + -7, -7, -7, -7, + -7, -7, -7, -7, +}; + +static const s8 b43legacy_tssi2dbm_g_table[] = { + 77, 77, 77, 76, + 76, 76, 75, 75, + 74, 74, 73, 73, + 73, 72, 72, 71, + 71, 70, 70, 69, + 68, 68, 67, 67, + 66, 65, 65, 64, + 63, 63, 62, 61, + 60, 59, 58, 57, + 56, 55, 54, 53, + 52, 50, 49, 47, + 45, 43, 40, 37, + 33, 28, 22, 14, + 5, -7, -20, -20, + -20, -20, -20, -20, + -20, -20, -20, -20, +}; + +static void b43legacy_phy_initg(struct b43legacy_wldev *dev); + + +static inline +void b43legacy_voluntary_preempt(void) +{ + B43legacy_BUG_ON(!(!in_atomic() && !in_irq() && + !in_interrupt() && !irqs_disabled())); +#ifndef CONFIG_PREEMPT + cond_resched(); +#endif /* CONFIG_PREEMPT */ +} + +/* Lock the PHY registers against concurrent access from the microcode. + * This lock is nonrecursive. */ +void b43legacy_phy_lock(struct b43legacy_wldev *dev) +{ +#if B43legacy_DEBUG + B43legacy_WARN_ON(dev->phy.phy_locked); + dev->phy.phy_locked = 1; +#endif + + if (dev->dev->id.revision < 3) { + b43legacy_mac_suspend(dev); + } else { + if (!b43legacy_is_mode(dev->wl, NL80211_IFTYPE_AP)) + b43legacy_power_saving_ctl_bits(dev, -1, 1); + } +} + +void b43legacy_phy_unlock(struct b43legacy_wldev *dev) +{ +#if B43legacy_DEBUG + B43legacy_WARN_ON(!dev->phy.phy_locked); + dev->phy.phy_locked = 0; +#endif + + if (dev->dev->id.revision < 3) { + b43legacy_mac_enable(dev); + } else { + if (!b43legacy_is_mode(dev->wl, NL80211_IFTYPE_AP)) + b43legacy_power_saving_ctl_bits(dev, -1, -1); + } +} + +u16 b43legacy_phy_read(struct b43legacy_wldev *dev, u16 offset) +{ + b43legacy_write16(dev, B43legacy_MMIO_PHY_CONTROL, offset); + return b43legacy_read16(dev, B43legacy_MMIO_PHY_DATA); +} + +void b43legacy_phy_write(struct b43legacy_wldev *dev, u16 offset, u16 val) +{ + b43legacy_write16(dev, B43legacy_MMIO_PHY_CONTROL, offset); + mmiowb(); + b43legacy_write16(dev, B43legacy_MMIO_PHY_DATA, val); +} + +void b43legacy_phy_calibrate(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + + b43legacy_read32(dev, B43legacy_MMIO_MACCTL); /* Dummy read. */ + if (phy->calibrated) + return; + if (phy->type == B43legacy_PHYTYPE_G && phy->rev == 1) { + b43legacy_wireless_core_reset(dev, 0); + b43legacy_phy_initg(dev); + b43legacy_wireless_core_reset(dev, B43legacy_TMSLOW_GMODE); + } + phy->calibrated = 1; +} + +/* initialize B PHY power control + * as described in http://bcm-specs.sipsolutions.net/InitPowerControl + */ +static void b43legacy_phy_init_pctl(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 saved_batt = 0; + u16 saved_ratt = 0; + u16 saved_txctl1 = 0; + int must_reset_txpower = 0; + + B43legacy_BUG_ON(!(phy->type == B43legacy_PHYTYPE_B || + phy->type == B43legacy_PHYTYPE_G)); + if (is_bcm_board_vendor(dev) && + (dev->dev->bus->boardinfo.type == 0x0416)) + return; + + b43legacy_phy_write(dev, 0x0028, 0x8018); + b43legacy_write16(dev, 0x03E6, b43legacy_read16(dev, 0x03E6) & 0xFFDF); + + if (phy->type == B43legacy_PHYTYPE_G) { + if (!phy->gmode) + return; + b43legacy_phy_write(dev, 0x047A, 0xC111); + } + if (phy->savedpctlreg != 0xFFFF) + return; +#ifdef CONFIG_B43LEGACY_DEBUG + if (phy->manual_txpower_control) + return; +#endif + + if (phy->type == B43legacy_PHYTYPE_B && + phy->rev >= 2 && + phy->radio_ver == 0x2050) + b43legacy_radio_write16(dev, 0x0076, + b43legacy_radio_read16(dev, 0x0076) + | 0x0084); + else { + saved_batt = phy->bbatt; + saved_ratt = phy->rfatt; + saved_txctl1 = phy->txctl1; + if ((phy->radio_rev >= 6) && (phy->radio_rev <= 8) + && /*FIXME: incomplete specs for 5 < revision < 9 */ 0) + b43legacy_radio_set_txpower_bg(dev, 0xB, 0x1F, 0); + else + b43legacy_radio_set_txpower_bg(dev, 0xB, 9, 0); + must_reset_txpower = 1; + } + b43legacy_dummy_transmission(dev); + + phy->savedpctlreg = b43legacy_phy_read(dev, B43legacy_PHY_G_PCTL); + + if (must_reset_txpower) + b43legacy_radio_set_txpower_bg(dev, saved_batt, saved_ratt, + saved_txctl1); + else + b43legacy_radio_write16(dev, 0x0076, b43legacy_radio_read16(dev, + 0x0076) & 0xFF7B); + b43legacy_radio_clear_tssi(dev); +} + +static void b43legacy_phy_agcsetup(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 offset = 0x0000; + + if (phy->rev == 1) + offset = 0x4C00; + + b43legacy_ilt_write(dev, offset, 0x00FE); + b43legacy_ilt_write(dev, offset + 1, 0x000D); + b43legacy_ilt_write(dev, offset + 2, 0x0013); + b43legacy_ilt_write(dev, offset + 3, 0x0019); + + if (phy->rev == 1) { + b43legacy_ilt_write(dev, 0x1800, 0x2710); + b43legacy_ilt_write(dev, 0x1801, 0x9B83); + b43legacy_ilt_write(dev, 0x1802, 0x9B83); + b43legacy_ilt_write(dev, 0x1803, 0x0F8D); + b43legacy_phy_write(dev, 0x0455, 0x0004); + } + + b43legacy_phy_write(dev, 0x04A5, (b43legacy_phy_read(dev, 0x04A5) + & 0x00FF) | 0x5700); + b43legacy_phy_write(dev, 0x041A, (b43legacy_phy_read(dev, 0x041A) + & 0xFF80) | 0x000F); + b43legacy_phy_write(dev, 0x041A, (b43legacy_phy_read(dev, 0x041A) + & 0xC07F) | 0x2B80); + b43legacy_phy_write(dev, 0x048C, (b43legacy_phy_read(dev, 0x048C) + & 0xF0FF) | 0x0300); + + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + | 0x0008); + + b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) + & 0xFFF0) | 0x0008); + b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1) + & 0xF0FF) | 0x0600); + b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2) + & 0xF0FF) | 0x0700); + b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0) + & 0xF0FF) | 0x0100); + + if (phy->rev == 1) + b43legacy_phy_write(dev, 0x04A2, + (b43legacy_phy_read(dev, 0x04A2) + & 0xFFF0) | 0x0007); + + b43legacy_phy_write(dev, 0x0488, (b43legacy_phy_read(dev, 0x0488) + & 0xFF00) | 0x001C); + b43legacy_phy_write(dev, 0x0488, (b43legacy_phy_read(dev, 0x0488) + & 0xC0FF) | 0x0200); + b43legacy_phy_write(dev, 0x0496, (b43legacy_phy_read(dev, 0x0496) + & 0xFF00) | 0x001C); + b43legacy_phy_write(dev, 0x0489, (b43legacy_phy_read(dev, 0x0489) + & 0xFF00) | 0x0020); + b43legacy_phy_write(dev, 0x0489, (b43legacy_phy_read(dev, 0x0489) + & 0xC0FF) | 0x0200); + b43legacy_phy_write(dev, 0x0482, (b43legacy_phy_read(dev, 0x0482) + & 0xFF00) | 0x002E); + b43legacy_phy_write(dev, 0x0496, (b43legacy_phy_read(dev, 0x0496) + & 0x00FF) | 0x1A00); + b43legacy_phy_write(dev, 0x0481, (b43legacy_phy_read(dev, 0x0481) + & 0xFF00) | 0x0028); + b43legacy_phy_write(dev, 0x0481, (b43legacy_phy_read(dev, 0x0481) + & 0x00FF) | 0x2C00); + + if (phy->rev == 1) { + b43legacy_phy_write(dev, 0x0430, 0x092B); + b43legacy_phy_write(dev, 0x041B, + (b43legacy_phy_read(dev, 0x041B) + & 0xFFE1) | 0x0002); + } else { + b43legacy_phy_write(dev, 0x041B, + b43legacy_phy_read(dev, 0x041B) & 0xFFE1); + b43legacy_phy_write(dev, 0x041F, 0x287A); + b43legacy_phy_write(dev, 0x0420, + (b43legacy_phy_read(dev, 0x0420) + & 0xFFF0) | 0x0004); + } + + if (phy->rev > 2) { + b43legacy_phy_write(dev, 0x0422, 0x287A); + b43legacy_phy_write(dev, 0x0420, + (b43legacy_phy_read(dev, 0x0420) + & 0x0FFF) | 0x3000); + } + + b43legacy_phy_write(dev, 0x04A8, (b43legacy_phy_read(dev, 0x04A8) + & 0x8080) | 0x7874); + b43legacy_phy_write(dev, 0x048E, 0x1C00); + + if (phy->rev == 1) { + b43legacy_phy_write(dev, 0x04AB, + (b43legacy_phy_read(dev, 0x04AB) + & 0xF0FF) | 0x0600); + b43legacy_phy_write(dev, 0x048B, 0x005E); + b43legacy_phy_write(dev, 0x048C, + (b43legacy_phy_read(dev, 0x048C) & 0xFF00) + | 0x001E); + b43legacy_phy_write(dev, 0x048D, 0x0002); + } + + b43legacy_ilt_write(dev, offset + 0x0800, 0); + b43legacy_ilt_write(dev, offset + 0x0801, 7); + b43legacy_ilt_write(dev, offset + 0x0802, 16); + b43legacy_ilt_write(dev, offset + 0x0803, 28); + + if (phy->rev >= 6) { + b43legacy_phy_write(dev, 0x0426, + (b43legacy_phy_read(dev, 0x0426) & 0xFFFC)); + b43legacy_phy_write(dev, 0x0426, + (b43legacy_phy_read(dev, 0x0426) & 0xEFFF)); + } +} + +static void b43legacy_phy_setupg(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 i; + + B43legacy_BUG_ON(phy->type != B43legacy_PHYTYPE_G); + if (phy->rev == 1) { + b43legacy_phy_write(dev, 0x0406, 0x4F19); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, + (b43legacy_phy_read(dev, + B43legacy_PHY_G_CRS) & 0xFC3F) | 0x0340); + b43legacy_phy_write(dev, 0x042C, 0x005A); + b43legacy_phy_write(dev, 0x0427, 0x001A); + + for (i = 0; i < B43legacy_ILT_FINEFREQG_SIZE; i++) + b43legacy_ilt_write(dev, 0x5800 + i, + b43legacy_ilt_finefreqg[i]); + for (i = 0; i < B43legacy_ILT_NOISEG1_SIZE; i++) + b43legacy_ilt_write(dev, 0x1800 + i, + b43legacy_ilt_noiseg1[i]); + for (i = 0; i < B43legacy_ILT_ROTOR_SIZE; i++) + b43legacy_ilt_write32(dev, 0x2000 + i, + b43legacy_ilt_rotor[i]); + } else { + /* nrssi values are signed 6-bit values. Why 0x7654 here? */ + b43legacy_nrssi_hw_write(dev, 0xBA98, (s16)0x7654); + + if (phy->rev == 2) { + b43legacy_phy_write(dev, 0x04C0, 0x1861); + b43legacy_phy_write(dev, 0x04C1, 0x0271); + } else if (phy->rev > 2) { + b43legacy_phy_write(dev, 0x04C0, 0x0098); + b43legacy_phy_write(dev, 0x04C1, 0x0070); + b43legacy_phy_write(dev, 0x04C9, 0x0080); + } + b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, + 0x042B) | 0x800); + + for (i = 0; i < 64; i++) + b43legacy_ilt_write(dev, 0x4000 + i, i); + for (i = 0; i < B43legacy_ILT_NOISEG2_SIZE; i++) + b43legacy_ilt_write(dev, 0x1800 + i, + b43legacy_ilt_noiseg2[i]); + } + + if (phy->rev <= 2) + for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++) + b43legacy_ilt_write(dev, 0x1400 + i, + b43legacy_ilt_noisescaleg1[i]); + else if ((phy->rev >= 7) && (b43legacy_phy_read(dev, 0x0449) & 0x0200)) + for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++) + b43legacy_ilt_write(dev, 0x1400 + i, + b43legacy_ilt_noisescaleg3[i]); + else + for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++) + b43legacy_ilt_write(dev, 0x1400 + i, + b43legacy_ilt_noisescaleg2[i]); + + if (phy->rev == 2) + for (i = 0; i < B43legacy_ILT_SIGMASQR_SIZE; i++) + b43legacy_ilt_write(dev, 0x5000 + i, + b43legacy_ilt_sigmasqr1[i]); + else if ((phy->rev > 2) && (phy->rev <= 8)) + for (i = 0; i < B43legacy_ILT_SIGMASQR_SIZE; i++) + b43legacy_ilt_write(dev, 0x5000 + i, + b43legacy_ilt_sigmasqr2[i]); + + if (phy->rev == 1) { + for (i = 0; i < B43legacy_ILT_RETARD_SIZE; i++) + b43legacy_ilt_write32(dev, 0x2400 + i, + b43legacy_ilt_retard[i]); + for (i = 4; i < 20; i++) + b43legacy_ilt_write(dev, 0x5400 + i, 0x0020); + b43legacy_phy_agcsetup(dev); + + if (is_bcm_board_vendor(dev) && + (dev->dev->bus->boardinfo.type == 0x0416) && + (dev->dev->bus->sprom.board_rev == 0x0017)) + return; + + b43legacy_ilt_write(dev, 0x5001, 0x0002); + b43legacy_ilt_write(dev, 0x5002, 0x0001); + } else { + for (i = 0; i <= 0x20; i++) + b43legacy_ilt_write(dev, 0x1000 + i, 0x0820); + b43legacy_phy_agcsetup(dev); + b43legacy_phy_read(dev, 0x0400); /* dummy read */ + b43legacy_phy_write(dev, 0x0403, 0x1000); + b43legacy_ilt_write(dev, 0x3C02, 0x000F); + b43legacy_ilt_write(dev, 0x3C03, 0x0014); + + if (is_bcm_board_vendor(dev) && + (dev->dev->bus->boardinfo.type == 0x0416) && + (dev->dev->bus->sprom.board_rev == 0x0017)) + return; + + b43legacy_ilt_write(dev, 0x0401, 0x0002); + b43legacy_ilt_write(dev, 0x0402, 0x0001); + } +} + +/* Initialize the APHY portion of a GPHY. */ +static void b43legacy_phy_inita(struct b43legacy_wldev *dev) +{ + + might_sleep(); + + b43legacy_phy_setupg(dev); + if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_PACTRL) + b43legacy_phy_write(dev, 0x046E, 0x03CF); +} + +static void b43legacy_phy_initb2(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 offset; + int val; + + b43legacy_write16(dev, 0x03EC, 0x3F22); + b43legacy_phy_write(dev, 0x0020, 0x301C); + b43legacy_phy_write(dev, 0x0026, 0x0000); + b43legacy_phy_write(dev, 0x0030, 0x00C6); + b43legacy_phy_write(dev, 0x0088, 0x3E00); + val = 0x3C3D; + for (offset = 0x0089; offset < 0x00A7; offset++) { + b43legacy_phy_write(dev, offset, val); + val -= 0x0202; + } + b43legacy_phy_write(dev, 0x03E4, 0x3000); + b43legacy_radio_selectchannel(dev, phy->channel, 0); + if (phy->radio_ver != 0x2050) { + b43legacy_radio_write16(dev, 0x0075, 0x0080); + b43legacy_radio_write16(dev, 0x0079, 0x0081); + } + b43legacy_radio_write16(dev, 0x0050, 0x0020); + b43legacy_radio_write16(dev, 0x0050, 0x0023); + if (phy->radio_ver == 0x2050) { + b43legacy_radio_write16(dev, 0x0050, 0x0020); + b43legacy_radio_write16(dev, 0x005A, 0x0070); + b43legacy_radio_write16(dev, 0x005B, 0x007B); + b43legacy_radio_write16(dev, 0x005C, 0x00B0); + b43legacy_radio_write16(dev, 0x007A, 0x000F); + b43legacy_phy_write(dev, 0x0038, 0x0677); + b43legacy_radio_init2050(dev); + } + b43legacy_phy_write(dev, 0x0014, 0x0080); + b43legacy_phy_write(dev, 0x0032, 0x00CA); + b43legacy_phy_write(dev, 0x0032, 0x00CC); + b43legacy_phy_write(dev, 0x0035, 0x07C2); + b43legacy_phy_lo_b_measure(dev); + b43legacy_phy_write(dev, 0x0026, 0xCC00); + if (phy->radio_ver != 0x2050) + b43legacy_phy_write(dev, 0x0026, 0xCE00); + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x1000); + b43legacy_phy_write(dev, 0x002A, 0x88A3); + if (phy->radio_ver != 0x2050) + b43legacy_phy_write(dev, 0x002A, 0x88C2); + b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); + b43legacy_phy_init_pctl(dev); +} + +static void b43legacy_phy_initb4(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 offset; + u16 val; + + b43legacy_write16(dev, 0x03EC, 0x3F22); + b43legacy_phy_write(dev, 0x0020, 0x301C); + b43legacy_phy_write(dev, 0x0026, 0x0000); + b43legacy_phy_write(dev, 0x0030, 0x00C6); + b43legacy_phy_write(dev, 0x0088, 0x3E00); + val = 0x3C3D; + for (offset = 0x0089; offset < 0x00A7; offset++) { + b43legacy_phy_write(dev, offset, val); + val -= 0x0202; + } + b43legacy_phy_write(dev, 0x03E4, 0x3000); + b43legacy_radio_selectchannel(dev, phy->channel, 0); + if (phy->radio_ver != 0x2050) { + b43legacy_radio_write16(dev, 0x0075, 0x0080); + b43legacy_radio_write16(dev, 0x0079, 0x0081); + } + b43legacy_radio_write16(dev, 0x0050, 0x0020); + b43legacy_radio_write16(dev, 0x0050, 0x0023); + if (phy->radio_ver == 0x2050) { + b43legacy_radio_write16(dev, 0x0050, 0x0020); + b43legacy_radio_write16(dev, 0x005A, 0x0070); + b43legacy_radio_write16(dev, 0x005B, 0x007B); + b43legacy_radio_write16(dev, 0x005C, 0x00B0); + b43legacy_radio_write16(dev, 0x007A, 0x000F); + b43legacy_phy_write(dev, 0x0038, 0x0677); + b43legacy_radio_init2050(dev); + } + b43legacy_phy_write(dev, 0x0014, 0x0080); + b43legacy_phy_write(dev, 0x0032, 0x00CA); + if (phy->radio_ver == 0x2050) + b43legacy_phy_write(dev, 0x0032, 0x00E0); + b43legacy_phy_write(dev, 0x0035, 0x07C2); + + b43legacy_phy_lo_b_measure(dev); + + b43legacy_phy_write(dev, 0x0026, 0xCC00); + if (phy->radio_ver == 0x2050) + b43legacy_phy_write(dev, 0x0026, 0xCE00); + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x1100); + b43legacy_phy_write(dev, 0x002A, 0x88A3); + if (phy->radio_ver == 0x2050) + b43legacy_phy_write(dev, 0x002A, 0x88C2); + b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); + if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI) { + b43legacy_calc_nrssi_slope(dev); + b43legacy_calc_nrssi_threshold(dev); + } + b43legacy_phy_init_pctl(dev); +} + +static void b43legacy_phy_initb5(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 offset; + u16 value; + u8 old_channel; + + if (phy->analog == 1) + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + | 0x0050); + if (!is_bcm_board_vendor(dev) && + (dev->dev->bus->boardinfo.type != 0x0416)) { + value = 0x2120; + for (offset = 0x00A8 ; offset < 0x00C7; offset++) { + b43legacy_phy_write(dev, offset, value); + value += 0x0202; + } + } + b43legacy_phy_write(dev, 0x0035, + (b43legacy_phy_read(dev, 0x0035) & 0xF0FF) + | 0x0700); + if (phy->radio_ver == 0x2050) + b43legacy_phy_write(dev, 0x0038, 0x0667); + + if (phy->gmode) { + if (phy->radio_ver == 0x2050) { + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + | 0x0020); + b43legacy_radio_write16(dev, 0x0051, + b43legacy_radio_read16(dev, 0x0051) + | 0x0004); + } + b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO, 0x0000); + + b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802) + | 0x0100); + b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B) + | 0x2000); + + b43legacy_phy_write(dev, 0x001C, 0x186A); + + b43legacy_phy_write(dev, 0x0013, (b43legacy_phy_read(dev, + 0x0013) & 0x00FF) | 0x1900); + b43legacy_phy_write(dev, 0x0035, (b43legacy_phy_read(dev, + 0x0035) & 0xFFC0) | 0x0064); + b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev, + 0x005D) & 0xFF80) | 0x000A); + b43legacy_phy_write(dev, 0x5B, 0x0000); + b43legacy_phy_write(dev, 0x5C, 0x0000); + } + + if (dev->bad_frames_preempt) + b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, + b43legacy_phy_read(dev, + B43legacy_PHY_RADIO_BITFIELD) | (1 << 12)); + + if (phy->analog == 1) { + b43legacy_phy_write(dev, 0x0026, 0xCE00); + b43legacy_phy_write(dev, 0x0021, 0x3763); + b43legacy_phy_write(dev, 0x0022, 0x1BC3); + b43legacy_phy_write(dev, 0x0023, 0x06F9); + b43legacy_phy_write(dev, 0x0024, 0x037E); + } else + b43legacy_phy_write(dev, 0x0026, 0xCC00); + b43legacy_phy_write(dev, 0x0030, 0x00C6); + b43legacy_write16(dev, 0x03EC, 0x3F22); + + if (phy->analog == 1) + b43legacy_phy_write(dev, 0x0020, 0x3E1C); + else + b43legacy_phy_write(dev, 0x0020, 0x301C); + + if (phy->analog == 0) + b43legacy_write16(dev, 0x03E4, 0x3000); + + old_channel = (phy->channel == 0xFF) ? 1 : phy->channel; + /* Force to channel 7, even if not supported. */ + b43legacy_radio_selectchannel(dev, 7, 0); + + if (phy->radio_ver != 0x2050) { + b43legacy_radio_write16(dev, 0x0075, 0x0080); + b43legacy_radio_write16(dev, 0x0079, 0x0081); + } + + b43legacy_radio_write16(dev, 0x0050, 0x0020); + b43legacy_radio_write16(dev, 0x0050, 0x0023); + + if (phy->radio_ver == 0x2050) { + b43legacy_radio_write16(dev, 0x0050, 0x0020); + b43legacy_radio_write16(dev, 0x005A, 0x0070); + } + + b43legacy_radio_write16(dev, 0x005B, 0x007B); + b43legacy_radio_write16(dev, 0x005C, 0x00B0); + + b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev, + 0x007A) | 0x0007); + + b43legacy_radio_selectchannel(dev, old_channel, 0); + + b43legacy_phy_write(dev, 0x0014, 0x0080); + b43legacy_phy_write(dev, 0x0032, 0x00CA); + b43legacy_phy_write(dev, 0x002A, 0x88A3); + + b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); + + if (phy->radio_ver == 0x2050) + b43legacy_radio_write16(dev, 0x005D, 0x000D); + + b43legacy_write16(dev, 0x03E4, (b43legacy_read16(dev, 0x03E4) & + 0xFFC0) | 0x0004); +} + +static void b43legacy_phy_initb6(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 offset; + u16 val; + u8 old_channel; + + b43legacy_phy_write(dev, 0x003E, 0x817A); + b43legacy_radio_write16(dev, 0x007A, + (b43legacy_radio_read16(dev, 0x007A) | 0x0058)); + if (phy->radio_rev == 4 || + phy->radio_rev == 5) { + b43legacy_radio_write16(dev, 0x0051, 0x0037); + b43legacy_radio_write16(dev, 0x0052, 0x0070); + b43legacy_radio_write16(dev, 0x0053, 0x00B3); + b43legacy_radio_write16(dev, 0x0054, 0x009B); + b43legacy_radio_write16(dev, 0x005A, 0x0088); + b43legacy_radio_write16(dev, 0x005B, 0x0088); + b43legacy_radio_write16(dev, 0x005D, 0x0088); + b43legacy_radio_write16(dev, 0x005E, 0x0088); + b43legacy_radio_write16(dev, 0x007D, 0x0088); + b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET, + (b43legacy_shm_read32(dev, + B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET) + | 0x00000200)); + } + if (phy->radio_rev == 8) { + b43legacy_radio_write16(dev, 0x0051, 0x0000); + b43legacy_radio_write16(dev, 0x0052, 0x0040); + b43legacy_radio_write16(dev, 0x0053, 0x00B7); + b43legacy_radio_write16(dev, 0x0054, 0x0098); + b43legacy_radio_write16(dev, 0x005A, 0x0088); + b43legacy_radio_write16(dev, 0x005B, 0x006B); + b43legacy_radio_write16(dev, 0x005C, 0x000F); + if (dev->dev->bus->sprom.boardflags_lo & 0x8000) { + b43legacy_radio_write16(dev, 0x005D, 0x00FA); + b43legacy_radio_write16(dev, 0x005E, 0x00D8); + } else { + b43legacy_radio_write16(dev, 0x005D, 0x00F5); + b43legacy_radio_write16(dev, 0x005E, 0x00B8); + } + b43legacy_radio_write16(dev, 0x0073, 0x0003); + b43legacy_radio_write16(dev, 0x007D, 0x00A8); + b43legacy_radio_write16(dev, 0x007C, 0x0001); + b43legacy_radio_write16(dev, 0x007E, 0x0008); + } + val = 0x1E1F; + for (offset = 0x0088; offset < 0x0098; offset++) { + b43legacy_phy_write(dev, offset, val); + val -= 0x0202; + } + val = 0x3E3F; + for (offset = 0x0098; offset < 0x00A8; offset++) { + b43legacy_phy_write(dev, offset, val); + val -= 0x0202; + } + val = 0x2120; + for (offset = 0x00A8; offset < 0x00C8; offset++) { + b43legacy_phy_write(dev, offset, (val & 0x3F3F)); + val += 0x0202; + } + if (phy->type == B43legacy_PHYTYPE_G) { + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) | + 0x0020); + b43legacy_radio_write16(dev, 0x0051, + b43legacy_radio_read16(dev, 0x0051) | + 0x0004); + b43legacy_phy_write(dev, 0x0802, + b43legacy_phy_read(dev, 0x0802) | 0x0100); + b43legacy_phy_write(dev, 0x042B, + b43legacy_phy_read(dev, 0x042B) | 0x2000); + b43legacy_phy_write(dev, 0x5B, 0x0000); + b43legacy_phy_write(dev, 0x5C, 0x0000); + } + + old_channel = phy->channel; + if (old_channel >= 8) + b43legacy_radio_selectchannel(dev, 1, 0); + else + b43legacy_radio_selectchannel(dev, 13, 0); + + b43legacy_radio_write16(dev, 0x0050, 0x0020); + b43legacy_radio_write16(dev, 0x0050, 0x0023); + udelay(40); + if (phy->radio_rev < 6 || phy->radio_rev == 8) { + b43legacy_radio_write16(dev, 0x007C, + (b43legacy_radio_read16(dev, 0x007C) + | 0x0002)); + b43legacy_radio_write16(dev, 0x0050, 0x0020); + } + if (phy->radio_rev <= 2) { + b43legacy_radio_write16(dev, 0x0050, 0x0020); + b43legacy_radio_write16(dev, 0x005A, 0x0070); + b43legacy_radio_write16(dev, 0x005B, 0x007B); + b43legacy_radio_write16(dev, 0x005C, 0x00B0); + } + b43legacy_radio_write16(dev, 0x007A, + (b43legacy_radio_read16(dev, + 0x007A) & 0x00F8) | 0x0007); + + b43legacy_radio_selectchannel(dev, old_channel, 0); + + b43legacy_phy_write(dev, 0x0014, 0x0200); + if (phy->radio_rev >= 6) + b43legacy_phy_write(dev, 0x002A, 0x88C2); + else + b43legacy_phy_write(dev, 0x002A, 0x8AC0); + b43legacy_phy_write(dev, 0x0038, 0x0668); + b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); + if (phy->radio_rev == 4 || phy->radio_rev == 5) + b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev, + 0x005D) & 0xFF80) | 0x0003); + if (phy->radio_rev <= 2) + b43legacy_radio_write16(dev, 0x005D, 0x000D); + + if (phy->analog == 4) { + b43legacy_write16(dev, 0x03E4, 0x0009); + b43legacy_phy_write(dev, 0x61, b43legacy_phy_read(dev, 0x61) + & 0xFFF); + } else + b43legacy_phy_write(dev, 0x0002, (b43legacy_phy_read(dev, + 0x0002) & 0xFFC0) | 0x0004); + if (phy->type == B43legacy_PHYTYPE_G) + b43legacy_write16(dev, 0x03E6, 0x0); + if (phy->type == B43legacy_PHYTYPE_B) { + b43legacy_write16(dev, 0x03E6, 0x8140); + b43legacy_phy_write(dev, 0x0016, 0x0410); + b43legacy_phy_write(dev, 0x0017, 0x0820); + b43legacy_phy_write(dev, 0x0062, 0x0007); + b43legacy_radio_init2050(dev); + b43legacy_phy_lo_g_measure(dev); + if (dev->dev->bus->sprom.boardflags_lo & + B43legacy_BFL_RSSI) { + b43legacy_calc_nrssi_slope(dev); + b43legacy_calc_nrssi_threshold(dev); + } + b43legacy_phy_init_pctl(dev); + } +} + +static void b43legacy_calc_loopback_gain(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 backup_phy[15] = {0}; + u16 backup_radio[3]; + u16 backup_bband; + u16 i; + u16 loop1_cnt; + u16 loop1_done; + u16 loop1_omitted; + u16 loop2_done; + + backup_phy[0] = b43legacy_phy_read(dev, 0x0429); + backup_phy[1] = b43legacy_phy_read(dev, 0x0001); + backup_phy[2] = b43legacy_phy_read(dev, 0x0811); + backup_phy[3] = b43legacy_phy_read(dev, 0x0812); + if (phy->rev != 1) { + backup_phy[4] = b43legacy_phy_read(dev, 0x0814); + backup_phy[5] = b43legacy_phy_read(dev, 0x0815); + } + backup_phy[6] = b43legacy_phy_read(dev, 0x005A); + backup_phy[7] = b43legacy_phy_read(dev, 0x0059); + backup_phy[8] = b43legacy_phy_read(dev, 0x0058); + backup_phy[9] = b43legacy_phy_read(dev, 0x000A); + backup_phy[10] = b43legacy_phy_read(dev, 0x0003); + backup_phy[11] = b43legacy_phy_read(dev, 0x080F); + backup_phy[12] = b43legacy_phy_read(dev, 0x0810); + backup_phy[13] = b43legacy_phy_read(dev, 0x002B); + backup_phy[14] = b43legacy_phy_read(dev, 0x0015); + b43legacy_phy_read(dev, 0x002D); /* dummy read */ + backup_bband = phy->bbatt; + backup_radio[0] = b43legacy_radio_read16(dev, 0x0052); + backup_radio[1] = b43legacy_radio_read16(dev, 0x0043); + backup_radio[2] = b43legacy_radio_read16(dev, 0x007A); + + b43legacy_phy_write(dev, 0x0429, + b43legacy_phy_read(dev, 0x0429) & 0x3FFF); + b43legacy_phy_write(dev, 0x0001, + b43legacy_phy_read(dev, 0x0001) & 0x8000); + b43legacy_phy_write(dev, 0x0811, + b43legacy_phy_read(dev, 0x0811) | 0x0002); + b43legacy_phy_write(dev, 0x0812, + b43legacy_phy_read(dev, 0x0812) & 0xFFFD); + b43legacy_phy_write(dev, 0x0811, + b43legacy_phy_read(dev, 0x0811) | 0x0001); + b43legacy_phy_write(dev, 0x0812, + b43legacy_phy_read(dev, 0x0812) & 0xFFFE); + if (phy->rev != 1) { + b43legacy_phy_write(dev, 0x0814, + b43legacy_phy_read(dev, 0x0814) | 0x0001); + b43legacy_phy_write(dev, 0x0815, + b43legacy_phy_read(dev, 0x0815) & 0xFFFE); + b43legacy_phy_write(dev, 0x0814, + b43legacy_phy_read(dev, 0x0814) | 0x0002); + b43legacy_phy_write(dev, 0x0815, + b43legacy_phy_read(dev, 0x0815) & 0xFFFD); + } + b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) | + 0x000C); + b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) | + 0x000C); + + b43legacy_phy_write(dev, 0x0811, (b43legacy_phy_read(dev, 0x0811) + & 0xFFCF) | 0x0030); + b43legacy_phy_write(dev, 0x0812, (b43legacy_phy_read(dev, 0x0812) + & 0xFFCF) | 0x0010); + + b43legacy_phy_write(dev, 0x005A, 0x0780); + b43legacy_phy_write(dev, 0x0059, 0xC810); + b43legacy_phy_write(dev, 0x0058, 0x000D); + if (phy->analog == 0) + b43legacy_phy_write(dev, 0x0003, 0x0122); + else + b43legacy_phy_write(dev, 0x000A, + b43legacy_phy_read(dev, 0x000A) + | 0x2000); + if (phy->rev != 1) { + b43legacy_phy_write(dev, 0x0814, + b43legacy_phy_read(dev, 0x0814) | 0x0004); + b43legacy_phy_write(dev, 0x0815, + b43legacy_phy_read(dev, 0x0815) & 0xFFFB); + } + b43legacy_phy_write(dev, 0x0003, + (b43legacy_phy_read(dev, 0x0003) + & 0xFF9F) | 0x0040); + if (phy->radio_ver == 0x2050 && phy->radio_rev == 2) { + b43legacy_radio_write16(dev, 0x0052, 0x0000); + b43legacy_radio_write16(dev, 0x0043, + (b43legacy_radio_read16(dev, 0x0043) + & 0xFFF0) | 0x0009); + loop1_cnt = 9; + } else if (phy->radio_rev == 8) { + b43legacy_radio_write16(dev, 0x0043, 0x000F); + loop1_cnt = 15; + } else + loop1_cnt = 0; + + b43legacy_phy_set_baseband_attenuation(dev, 11); + + if (phy->rev >= 3) + b43legacy_phy_write(dev, 0x080F, 0xC020); + else + b43legacy_phy_write(dev, 0x080F, 0x8020); + b43legacy_phy_write(dev, 0x0810, 0x0000); + + b43legacy_phy_write(dev, 0x002B, + (b43legacy_phy_read(dev, 0x002B) + & 0xFFC0) | 0x0001); + b43legacy_phy_write(dev, 0x002B, + (b43legacy_phy_read(dev, 0x002B) + & 0xC0FF) | 0x0800); + b43legacy_phy_write(dev, 0x0811, + b43legacy_phy_read(dev, 0x0811) | 0x0100); + b43legacy_phy_write(dev, 0x0812, + b43legacy_phy_read(dev, 0x0812) & 0xCFFF); + if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_EXTLNA) { + if (phy->rev >= 7) { + b43legacy_phy_write(dev, 0x0811, + b43legacy_phy_read(dev, 0x0811) + | 0x0800); + b43legacy_phy_write(dev, 0x0812, + b43legacy_phy_read(dev, 0x0812) + | 0x8000); + } + } + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + & 0x00F7); + + for (i = 0; i < loop1_cnt; i++) { + b43legacy_radio_write16(dev, 0x0043, loop1_cnt); + b43legacy_phy_write(dev, 0x0812, + (b43legacy_phy_read(dev, 0x0812) + & 0xF0FF) | (i << 8)); + b43legacy_phy_write(dev, 0x0015, + (b43legacy_phy_read(dev, 0x0015) + & 0x0FFF) | 0xA000); + b43legacy_phy_write(dev, 0x0015, + (b43legacy_phy_read(dev, 0x0015) + & 0x0FFF) | 0xF000); + udelay(20); + if (b43legacy_phy_read(dev, 0x002D) >= 0x0DFC) + break; + } + loop1_done = i; + loop1_omitted = loop1_cnt - loop1_done; + + loop2_done = 0; + if (loop1_done >= 8) { + b43legacy_phy_write(dev, 0x0812, + b43legacy_phy_read(dev, 0x0812) + | 0x0030); + for (i = loop1_done - 8; i < 16; i++) { + b43legacy_phy_write(dev, 0x0812, + (b43legacy_phy_read(dev, 0x0812) + & 0xF0FF) | (i << 8)); + b43legacy_phy_write(dev, 0x0015, + (b43legacy_phy_read(dev, 0x0015) + & 0x0FFF) | 0xA000); + b43legacy_phy_write(dev, 0x0015, + (b43legacy_phy_read(dev, 0x0015) + & 0x0FFF) | 0xF000); + udelay(20); + if (b43legacy_phy_read(dev, 0x002D) >= 0x0DFC) + break; + } + } + + if (phy->rev != 1) { + b43legacy_phy_write(dev, 0x0814, backup_phy[4]); + b43legacy_phy_write(dev, 0x0815, backup_phy[5]); + } + b43legacy_phy_write(dev, 0x005A, backup_phy[6]); + b43legacy_phy_write(dev, 0x0059, backup_phy[7]); + b43legacy_phy_write(dev, 0x0058, backup_phy[8]); + b43legacy_phy_write(dev, 0x000A, backup_phy[9]); + b43legacy_phy_write(dev, 0x0003, backup_phy[10]); + b43legacy_phy_write(dev, 0x080F, backup_phy[11]); + b43legacy_phy_write(dev, 0x0810, backup_phy[12]); + b43legacy_phy_write(dev, 0x002B, backup_phy[13]); + b43legacy_phy_write(dev, 0x0015, backup_phy[14]); + + b43legacy_phy_set_baseband_attenuation(dev, backup_bband); + + b43legacy_radio_write16(dev, 0x0052, backup_radio[0]); + b43legacy_radio_write16(dev, 0x0043, backup_radio[1]); + b43legacy_radio_write16(dev, 0x007A, backup_radio[2]); + + b43legacy_phy_write(dev, 0x0811, backup_phy[2] | 0x0003); + udelay(10); + b43legacy_phy_write(dev, 0x0811, backup_phy[2]); + b43legacy_phy_write(dev, 0x0812, backup_phy[3]); + b43legacy_phy_write(dev, 0x0429, backup_phy[0]); + b43legacy_phy_write(dev, 0x0001, backup_phy[1]); + + phy->loopback_gain[0] = ((loop1_done * 6) - (loop1_omitted * 4)) - 11; + phy->loopback_gain[1] = (24 - (3 * loop2_done)) * 2; +} + +static void b43legacy_phy_initg(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 tmp; + + if (phy->rev == 1) + b43legacy_phy_initb5(dev); + else + b43legacy_phy_initb6(dev); + if (phy->rev >= 2 && phy->gmode) + b43legacy_phy_inita(dev); + + if (phy->rev >= 2) { + b43legacy_phy_write(dev, 0x0814, 0x0000); + b43legacy_phy_write(dev, 0x0815, 0x0000); + } + if (phy->rev == 2) { + b43legacy_phy_write(dev, 0x0811, 0x0000); + b43legacy_phy_write(dev, 0x0015, 0x00C0); + } + if (phy->rev > 5) { + b43legacy_phy_write(dev, 0x0811, 0x0400); + b43legacy_phy_write(dev, 0x0015, 0x00C0); + } + if (phy->gmode) { + tmp = b43legacy_phy_read(dev, 0x0400) & 0xFF; + if (tmp == 3) { + b43legacy_phy_write(dev, 0x04C2, 0x1816); + b43legacy_phy_write(dev, 0x04C3, 0x8606); + } + if (tmp == 4 || tmp == 5) { + b43legacy_phy_write(dev, 0x04C2, 0x1816); + b43legacy_phy_write(dev, 0x04C3, 0x8006); + b43legacy_phy_write(dev, 0x04CC, + (b43legacy_phy_read(dev, + 0x04CC) & 0x00FF) | + 0x1F00); + } + if (phy->rev >= 2) + b43legacy_phy_write(dev, 0x047E, 0x0078); + } + if (phy->radio_rev == 8) { + b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801) + | 0x0080); + b43legacy_phy_write(dev, 0x043E, b43legacy_phy_read(dev, 0x043E) + | 0x0004); + } + if (phy->rev >= 2 && phy->gmode) + b43legacy_calc_loopback_gain(dev); + if (phy->radio_rev != 8) { + if (phy->initval == 0xFFFF) + phy->initval = b43legacy_radio_init2050(dev); + else + b43legacy_radio_write16(dev, 0x0078, phy->initval); + } + if (phy->txctl2 == 0xFFFF) + b43legacy_phy_lo_g_measure(dev); + else { + if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) + b43legacy_radio_write16(dev, 0x0052, + (phy->txctl1 << 4) | + phy->txctl2); + else + b43legacy_radio_write16(dev, 0x0052, + (b43legacy_radio_read16(dev, + 0x0052) & 0xFFF0) | + phy->txctl1); + if (phy->rev >= 6) + b43legacy_phy_write(dev, 0x0036, + (b43legacy_phy_read(dev, 0x0036) + & 0x0FFF) | (phy->txctl2 << 12)); + if (dev->dev->bus->sprom.boardflags_lo & + B43legacy_BFL_PACTRL) + b43legacy_phy_write(dev, 0x002E, 0x8075); + else + b43legacy_phy_write(dev, 0x002E, 0x807F); + if (phy->rev < 2) + b43legacy_phy_write(dev, 0x002F, 0x0101); + else + b43legacy_phy_write(dev, 0x002F, 0x0202); + } + if (phy->gmode) { + b43legacy_phy_lo_adjust(dev, 0); + b43legacy_phy_write(dev, 0x080F, 0x8078); + } + + if (!(dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI)) { + /* The specs state to update the NRSSI LT with + * the value 0x7FFFFFFF here. I think that is some weird + * compiler optimization in the original driver. + * Essentially, what we do here is resetting all NRSSI LT + * entries to -32 (see the clamp_val() in nrssi_hw_update()) + */ + b43legacy_nrssi_hw_update(dev, 0xFFFF); + b43legacy_calc_nrssi_threshold(dev); + } else if (phy->gmode || phy->rev >= 2) { + if (phy->nrssi[0] == -1000) { + B43legacy_WARN_ON(phy->nrssi[1] != -1000); + b43legacy_calc_nrssi_slope(dev); + } else { + B43legacy_WARN_ON(phy->nrssi[1] == -1000); + b43legacy_calc_nrssi_threshold(dev); + } + } + if (phy->radio_rev == 8) + b43legacy_phy_write(dev, 0x0805, 0x3230); + b43legacy_phy_init_pctl(dev); + if (dev->dev->bus->chip_id == 0x4306 + && dev->dev->bus->chip_package == 2) { + b43legacy_phy_write(dev, 0x0429, + b43legacy_phy_read(dev, 0x0429) & 0xBFFF); + b43legacy_phy_write(dev, 0x04C3, + b43legacy_phy_read(dev, 0x04C3) & 0x7FFF); + } +} + +static u16 b43legacy_phy_lo_b_r15_loop(struct b43legacy_wldev *dev) +{ + int i; + u16 ret = 0; + unsigned long flags; + + local_irq_save(flags); + for (i = 0; i < 10; i++) { + b43legacy_phy_write(dev, 0x0015, 0xAFA0); + udelay(1); + b43legacy_phy_write(dev, 0x0015, 0xEFA0); + udelay(10); + b43legacy_phy_write(dev, 0x0015, 0xFFA0); + udelay(40); + ret += b43legacy_phy_read(dev, 0x002C); + } + local_irq_restore(flags); + b43legacy_voluntary_preempt(); + + return ret; +} + +void b43legacy_phy_lo_b_measure(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 regstack[12] = { 0 }; + u16 mls; + u16 fval; + int i; + int j; + + regstack[0] = b43legacy_phy_read(dev, 0x0015); + regstack[1] = b43legacy_radio_read16(dev, 0x0052) & 0xFFF0; + + if (phy->radio_ver == 0x2053) { + regstack[2] = b43legacy_phy_read(dev, 0x000A); + regstack[3] = b43legacy_phy_read(dev, 0x002A); + regstack[4] = b43legacy_phy_read(dev, 0x0035); + regstack[5] = b43legacy_phy_read(dev, 0x0003); + regstack[6] = b43legacy_phy_read(dev, 0x0001); + regstack[7] = b43legacy_phy_read(dev, 0x0030); + + regstack[8] = b43legacy_radio_read16(dev, 0x0043); + regstack[9] = b43legacy_radio_read16(dev, 0x007A); + regstack[10] = b43legacy_read16(dev, 0x03EC); + regstack[11] = b43legacy_radio_read16(dev, 0x0052) & 0x00F0; + + b43legacy_phy_write(dev, 0x0030, 0x00FF); + b43legacy_write16(dev, 0x03EC, 0x3F3F); + b43legacy_phy_write(dev, 0x0035, regstack[4] & 0xFF7F); + b43legacy_radio_write16(dev, 0x007A, regstack[9] & 0xFFF0); + } + b43legacy_phy_write(dev, 0x0015, 0xB000); + b43legacy_phy_write(dev, 0x002B, 0x0004); + + if (phy->radio_ver == 0x2053) { + b43legacy_phy_write(dev, 0x002B, 0x0203); + b43legacy_phy_write(dev, 0x002A, 0x08A3); + } + + phy->minlowsig[0] = 0xFFFF; + + for (i = 0; i < 4; i++) { + b43legacy_radio_write16(dev, 0x0052, regstack[1] | i); + b43legacy_phy_lo_b_r15_loop(dev); + } + for (i = 0; i < 10; i++) { + b43legacy_radio_write16(dev, 0x0052, regstack[1] | i); + mls = b43legacy_phy_lo_b_r15_loop(dev) / 10; + if (mls < phy->minlowsig[0]) { + phy->minlowsig[0] = mls; + phy->minlowsigpos[0] = i; + } + } + b43legacy_radio_write16(dev, 0x0052, regstack[1] + | phy->minlowsigpos[0]); + + phy->minlowsig[1] = 0xFFFF; + + for (i = -4; i < 5; i += 2) { + for (j = -4; j < 5; j += 2) { + if (j < 0) + fval = (0x0100 * i) + j + 0x0100; + else + fval = (0x0100 * i) + j; + b43legacy_phy_write(dev, 0x002F, fval); + mls = b43legacy_phy_lo_b_r15_loop(dev) / 10; + if (mls < phy->minlowsig[1]) { + phy->minlowsig[1] = mls; + phy->minlowsigpos[1] = fval; + } + } + } + phy->minlowsigpos[1] += 0x0101; + + b43legacy_phy_write(dev, 0x002F, phy->minlowsigpos[1]); + if (phy->radio_ver == 0x2053) { + b43legacy_phy_write(dev, 0x000A, regstack[2]); + b43legacy_phy_write(dev, 0x002A, regstack[3]); + b43legacy_phy_write(dev, 0x0035, regstack[4]); + b43legacy_phy_write(dev, 0x0003, regstack[5]); + b43legacy_phy_write(dev, 0x0001, regstack[6]); + b43legacy_phy_write(dev, 0x0030, regstack[7]); + + b43legacy_radio_write16(dev, 0x0043, regstack[8]); + b43legacy_radio_write16(dev, 0x007A, regstack[9]); + + b43legacy_radio_write16(dev, 0x0052, + (b43legacy_radio_read16(dev, 0x0052) + & 0x000F) | regstack[11]); + + b43legacy_write16(dev, 0x03EC, regstack[10]); + } + b43legacy_phy_write(dev, 0x0015, regstack[0]); +} + +static inline +u16 b43legacy_phy_lo_g_deviation_subval(struct b43legacy_wldev *dev, + u16 control) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 ret; + unsigned long flags; + + local_irq_save(flags); + if (phy->gmode) { + b43legacy_phy_write(dev, 0x15, 0xE300); + control <<= 8; + b43legacy_phy_write(dev, 0x0812, control | 0x00B0); + udelay(5); + b43legacy_phy_write(dev, 0x0812, control | 0x00B2); + udelay(2); + b43legacy_phy_write(dev, 0x0812, control | 0x00B3); + udelay(4); + b43legacy_phy_write(dev, 0x0015, 0xF300); + udelay(8); + } else { + b43legacy_phy_write(dev, 0x0015, control | 0xEFA0); + udelay(2); + b43legacy_phy_write(dev, 0x0015, control | 0xEFE0); + udelay(4); + b43legacy_phy_write(dev, 0x0015, control | 0xFFE0); + udelay(8); + } + ret = b43legacy_phy_read(dev, 0x002D); + local_irq_restore(flags); + b43legacy_voluntary_preempt(); + + return ret; +} + +static u32 b43legacy_phy_lo_g_singledeviation(struct b43legacy_wldev *dev, + u16 control) +{ + int i; + u32 ret = 0; + + for (i = 0; i < 8; i++) + ret += b43legacy_phy_lo_g_deviation_subval(dev, control); + + return ret; +} + +/* Write the LocalOscillator CONTROL */ +static inline +void b43legacy_lo_write(struct b43legacy_wldev *dev, + struct b43legacy_lopair *pair) +{ + u16 value; + + value = (u8)(pair->low); + value |= ((u8)(pair->high)) << 8; + +#ifdef CONFIG_B43LEGACY_DEBUG + /* Sanity check. */ + if (pair->low < -8 || pair->low > 8 || + pair->high < -8 || pair->high > 8) { + b43legacydbg(dev->wl, + "WARNING: Writing invalid LOpair " + "(low: %d, high: %d)\n", + pair->low, pair->high); + dump_stack(); + } +#endif + + b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, value); +} + +static inline +struct b43legacy_lopair *b43legacy_find_lopair(struct b43legacy_wldev *dev, + u16 bbatt, + u16 rfatt, + u16 tx) +{ + static const u8 dict[10] = { 11, 10, 11, 12, 13, 12, 13, 12, 13, 12 }; + struct b43legacy_phy *phy = &dev->phy; + + if (bbatt > 6) + bbatt = 6; + B43legacy_WARN_ON(rfatt >= 10); + + if (tx == 3) + return b43legacy_get_lopair(phy, rfatt, bbatt); + return b43legacy_get_lopair(phy, dict[rfatt], bbatt); +} + +static inline +struct b43legacy_lopair *b43legacy_current_lopair(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + + return b43legacy_find_lopair(dev, phy->bbatt, + phy->rfatt, phy->txctl1); +} + +/* Adjust B/G LO */ +void b43legacy_phy_lo_adjust(struct b43legacy_wldev *dev, int fixed) +{ + struct b43legacy_lopair *pair; + + if (fixed) { + /* Use fixed values. Only for initialization. */ + pair = b43legacy_find_lopair(dev, 2, 3, 0); + } else + pair = b43legacy_current_lopair(dev); + b43legacy_lo_write(dev, pair); +} + +static void b43legacy_phy_lo_g_measure_txctl2(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 txctl2 = 0; + u16 i; + u32 smallest; + u32 tmp; + + b43legacy_radio_write16(dev, 0x0052, 0x0000); + udelay(10); + smallest = b43legacy_phy_lo_g_singledeviation(dev, 0); + for (i = 0; i < 16; i++) { + b43legacy_radio_write16(dev, 0x0052, i); + udelay(10); + tmp = b43legacy_phy_lo_g_singledeviation(dev, 0); + if (tmp < smallest) { + smallest = tmp; + txctl2 = i; + } + } + phy->txctl2 = txctl2; +} + +static +void b43legacy_phy_lo_g_state(struct b43legacy_wldev *dev, + const struct b43legacy_lopair *in_pair, + struct b43legacy_lopair *out_pair, + u16 r27) +{ + static const struct b43legacy_lopair transitions[8] = { + { .high = 1, .low = 1, }, + { .high = 1, .low = 0, }, + { .high = 1, .low = -1, }, + { .high = 0, .low = -1, }, + { .high = -1, .low = -1, }, + { .high = -1, .low = 0, }, + { .high = -1, .low = 1, }, + { .high = 0, .low = 1, }, + }; + struct b43legacy_lopair lowest_transition = { + .high = in_pair->high, + .low = in_pair->low, + }; + struct b43legacy_lopair tmp_pair; + struct b43legacy_lopair transition; + int i = 12; + int state = 0; + int found_lower; + int j; + int begin; + int end; + u32 lowest_deviation; + u32 tmp; + + /* Note that in_pair and out_pair can point to the same pair. + * Be careful. */ + + b43legacy_lo_write(dev, &lowest_transition); + lowest_deviation = b43legacy_phy_lo_g_singledeviation(dev, r27); + do { + found_lower = 0; + B43legacy_WARN_ON(!(state >= 0 && state <= 8)); + if (state == 0) { + begin = 1; + end = 8; + } else if (state % 2 == 0) { + begin = state - 1; + end = state + 1; + } else { + begin = state - 2; + end = state + 2; + } + if (begin < 1) + begin += 8; + if (end > 8) + end -= 8; + + j = begin; + tmp_pair.high = lowest_transition.high; + tmp_pair.low = lowest_transition.low; + while (1) { + B43legacy_WARN_ON(!(j >= 1 && j <= 8)); + transition.high = tmp_pair.high + + transitions[j - 1].high; + transition.low = tmp_pair.low + transitions[j - 1].low; + if ((abs(transition.low) < 9) + && (abs(transition.high) < 9)) { + b43legacy_lo_write(dev, &transition); + tmp = b43legacy_phy_lo_g_singledeviation(dev, + r27); + if (tmp < lowest_deviation) { + lowest_deviation = tmp; + state = j; + found_lower = 1; + + lowest_transition.high = + transition.high; + lowest_transition.low = transition.low; + } + } + if (j == end) + break; + if (j == 8) + j = 1; + else + j++; + } + } while (i-- && found_lower); + + out_pair->high = lowest_transition.high; + out_pair->low = lowest_transition.low; +} + +/* Set the baseband attenuation value on chip. */ +void b43legacy_phy_set_baseband_attenuation(struct b43legacy_wldev *dev, + u16 bbatt) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 value; + + if (phy->analog == 0) { + value = (b43legacy_read16(dev, 0x03E6) & 0xFFF0); + value |= (bbatt & 0x000F); + b43legacy_write16(dev, 0x03E6, value); + return; + } + + if (phy->analog > 1) { + value = b43legacy_phy_read(dev, 0x0060) & 0xFFC3; + value |= (bbatt << 2) & 0x003C; + } else { + value = b43legacy_phy_read(dev, 0x0060) & 0xFF87; + value |= (bbatt << 3) & 0x0078; + } + b43legacy_phy_write(dev, 0x0060, value); +} + +/* http://bcm-specs.sipsolutions.net/LocalOscillator/Measure */ +void b43legacy_phy_lo_g_measure(struct b43legacy_wldev *dev) +{ + static const u8 pairorder[10] = { 3, 1, 5, 7, 9, 2, 0, 4, 6, 8 }; + const int is_initializing = (b43legacy_status(dev) + < B43legacy_STAT_STARTED); + struct b43legacy_phy *phy = &dev->phy; + u16 h; + u16 i; + u16 oldi = 0; + u16 j; + struct b43legacy_lopair control; + struct b43legacy_lopair *tmp_control; + u16 tmp; + u16 regstack[16] = { 0 }; + u8 oldchannel; + + /* XXX: What are these? */ + u8 r27 = 0; + u16 r31; + + oldchannel = phy->channel; + /* Setup */ + if (phy->gmode) { + regstack[0] = b43legacy_phy_read(dev, B43legacy_PHY_G_CRS); + regstack[1] = b43legacy_phy_read(dev, 0x0802); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0] + & 0x7FFF); + b43legacy_phy_write(dev, 0x0802, regstack[1] & 0xFFFC); + } + regstack[3] = b43legacy_read16(dev, 0x03E2); + b43legacy_write16(dev, 0x03E2, regstack[3] | 0x8000); + regstack[4] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); + regstack[5] = b43legacy_phy_read(dev, 0x15); + regstack[6] = b43legacy_phy_read(dev, 0x2A); + regstack[7] = b43legacy_phy_read(dev, 0x35); + regstack[8] = b43legacy_phy_read(dev, 0x60); + regstack[9] = b43legacy_radio_read16(dev, 0x43); + regstack[10] = b43legacy_radio_read16(dev, 0x7A); + regstack[11] = b43legacy_radio_read16(dev, 0x52); + if (phy->gmode) { + regstack[12] = b43legacy_phy_read(dev, 0x0811); + regstack[13] = b43legacy_phy_read(dev, 0x0812); + regstack[14] = b43legacy_phy_read(dev, 0x0814); + regstack[15] = b43legacy_phy_read(dev, 0x0815); + } + b43legacy_radio_selectchannel(dev, 6, 0); + if (phy->gmode) { + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0] + & 0x7FFF); + b43legacy_phy_write(dev, 0x0802, regstack[1] & 0xFFFC); + b43legacy_dummy_transmission(dev); + } + b43legacy_radio_write16(dev, 0x0043, 0x0006); + + b43legacy_phy_set_baseband_attenuation(dev, 2); + + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x0000); + b43legacy_phy_write(dev, 0x002E, 0x007F); + b43legacy_phy_write(dev, 0x080F, 0x0078); + b43legacy_phy_write(dev, 0x0035, regstack[7] & ~(1 << 7)); + b43legacy_radio_write16(dev, 0x007A, regstack[10] & 0xFFF0); + b43legacy_phy_write(dev, 0x002B, 0x0203); + b43legacy_phy_write(dev, 0x002A, 0x08A3); + if (phy->gmode) { + b43legacy_phy_write(dev, 0x0814, regstack[14] | 0x0003); + b43legacy_phy_write(dev, 0x0815, regstack[15] & 0xFFFC); + b43legacy_phy_write(dev, 0x0811, 0x01B3); + b43legacy_phy_write(dev, 0x0812, 0x00B2); + } + if (is_initializing) + b43legacy_phy_lo_g_measure_txctl2(dev); + b43legacy_phy_write(dev, 0x080F, 0x8078); + + /* Measure */ + control.low = 0; + control.high = 0; + for (h = 0; h < 10; h++) { + /* Loop over each possible RadioAttenuation (0-9) */ + i = pairorder[h]; + if (is_initializing) { + if (i == 3) { + control.low = 0; + control.high = 0; + } else if (((i % 2 == 1) && (oldi % 2 == 1)) || + ((i % 2 == 0) && (oldi % 2 == 0))) { + tmp_control = b43legacy_get_lopair(phy, oldi, + 0); + memcpy(&control, tmp_control, sizeof(control)); + } else { + tmp_control = b43legacy_get_lopair(phy, 3, 0); + memcpy(&control, tmp_control, sizeof(control)); + } + } + /* Loop over each possible BasebandAttenuation/2 */ + for (j = 0; j < 4; j++) { + if (is_initializing) { + tmp = i * 2 + j; + r27 = 0; + r31 = 0; + if (tmp > 14) { + r31 = 1; + if (tmp > 17) + r27 = 1; + if (tmp > 19) + r27 = 2; + } + } else { + tmp_control = b43legacy_get_lopair(phy, i, + j * 2); + if (!tmp_control->used) + continue; + memcpy(&control, tmp_control, sizeof(control)); + r27 = 3; + r31 = 0; + } + b43legacy_radio_write16(dev, 0x43, i); + b43legacy_radio_write16(dev, 0x52, phy->txctl2); + udelay(10); + b43legacy_voluntary_preempt(); + + b43legacy_phy_set_baseband_attenuation(dev, j * 2); + + tmp = (regstack[10] & 0xFFF0); + if (r31) + tmp |= 0x0008; + b43legacy_radio_write16(dev, 0x007A, tmp); + + tmp_control = b43legacy_get_lopair(phy, i, j * 2); + b43legacy_phy_lo_g_state(dev, &control, tmp_control, + r27); + } + oldi = i; + } + /* Loop over each possible RadioAttenuation (10-13) */ + for (i = 10; i < 14; i++) { + /* Loop over each possible BasebandAttenuation/2 */ + for (j = 0; j < 4; j++) { + if (is_initializing) { + tmp_control = b43legacy_get_lopair(phy, i - 9, + j * 2); + memcpy(&control, tmp_control, sizeof(control)); + /* FIXME: The next line is wrong, as the + * following if statement can never trigger. */ + tmp = (i - 9) * 2 + j - 5; + r27 = 0; + r31 = 0; + if (tmp > 14) { + r31 = 1; + if (tmp > 17) + r27 = 1; + if (tmp > 19) + r27 = 2; + } + } else { + tmp_control = b43legacy_get_lopair(phy, i - 9, + j * 2); + if (!tmp_control->used) + continue; + memcpy(&control, tmp_control, sizeof(control)); + r27 = 3; + r31 = 0; + } + b43legacy_radio_write16(dev, 0x43, i - 9); + /* FIXME: shouldn't txctl1 be zero in the next line + * and 3 in the loop above? */ + b43legacy_radio_write16(dev, 0x52, + phy->txctl2 + | (3/*txctl1*/ << 4)); + udelay(10); + b43legacy_voluntary_preempt(); + + b43legacy_phy_set_baseband_attenuation(dev, j * 2); + + tmp = (regstack[10] & 0xFFF0); + if (r31) + tmp |= 0x0008; + b43legacy_radio_write16(dev, 0x7A, tmp); + + tmp_control = b43legacy_get_lopair(phy, i, j * 2); + b43legacy_phy_lo_g_state(dev, &control, tmp_control, + r27); + } + } + + /* Restoration */ + if (phy->gmode) { + b43legacy_phy_write(dev, 0x0015, 0xE300); + b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA0); + udelay(5); + b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA2); + udelay(2); + b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA3); + b43legacy_voluntary_preempt(); + } else + b43legacy_phy_write(dev, 0x0015, r27 | 0xEFA0); + b43legacy_phy_lo_adjust(dev, is_initializing); + b43legacy_phy_write(dev, 0x002E, 0x807F); + if (phy->gmode) + b43legacy_phy_write(dev, 0x002F, 0x0202); + else + b43legacy_phy_write(dev, 0x002F, 0x0101); + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, regstack[4]); + b43legacy_phy_write(dev, 0x0015, regstack[5]); + b43legacy_phy_write(dev, 0x002A, regstack[6]); + b43legacy_phy_write(dev, 0x0035, regstack[7]); + b43legacy_phy_write(dev, 0x0060, regstack[8]); + b43legacy_radio_write16(dev, 0x0043, regstack[9]); + b43legacy_radio_write16(dev, 0x007A, regstack[10]); + regstack[11] &= 0x00F0; + regstack[11] |= (b43legacy_radio_read16(dev, 0x52) & 0x000F); + b43legacy_radio_write16(dev, 0x52, regstack[11]); + b43legacy_write16(dev, 0x03E2, regstack[3]); + if (phy->gmode) { + b43legacy_phy_write(dev, 0x0811, regstack[12]); + b43legacy_phy_write(dev, 0x0812, regstack[13]); + b43legacy_phy_write(dev, 0x0814, regstack[14]); + b43legacy_phy_write(dev, 0x0815, regstack[15]); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0]); + b43legacy_phy_write(dev, 0x0802, regstack[1]); + } + b43legacy_radio_selectchannel(dev, oldchannel, 1); + +#ifdef CONFIG_B43LEGACY_DEBUG + { + /* Sanity check for all lopairs. */ + for (i = 0; i < B43legacy_LO_COUNT; i++) { + tmp_control = phy->_lo_pairs + i; + if (tmp_control->low < -8 || tmp_control->low > 8 || + tmp_control->high < -8 || tmp_control->high > 8) + b43legacywarn(dev->wl, + "WARNING: Invalid LOpair (low: %d, high:" + " %d, index: %d)\n", + tmp_control->low, tmp_control->high, i); + } + } +#endif /* CONFIG_B43LEGACY_DEBUG */ +} + +static +void b43legacy_phy_lo_mark_current_used(struct b43legacy_wldev *dev) +{ + struct b43legacy_lopair *pair; + + pair = b43legacy_current_lopair(dev); + pair->used = 1; +} + +void b43legacy_phy_lo_mark_all_unused(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + struct b43legacy_lopair *pair; + int i; + + for (i = 0; i < B43legacy_LO_COUNT; i++) { + pair = phy->_lo_pairs + i; + pair->used = 0; + } +} + +/* http://bcm-specs.sipsolutions.net/EstimatePowerOut + * This function converts a TSSI value to dBm in Q5.2 + */ +static s8 b43legacy_phy_estimate_power_out(struct b43legacy_wldev *dev, s8 tssi) +{ + struct b43legacy_phy *phy = &dev->phy; + s8 dbm = 0; + s32 tmp; + + tmp = phy->idle_tssi; + tmp += tssi; + tmp -= phy->savedpctlreg; + + switch (phy->type) { + case B43legacy_PHYTYPE_B: + case B43legacy_PHYTYPE_G: + tmp = clamp_val(tmp, 0x00, 0x3F); + dbm = phy->tssi2dbm[tmp]; + break; + default: + B43legacy_BUG_ON(1); + } + + return dbm; +} + +/* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */ +void b43legacy_phy_xmitpower(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 tmp; + u16 txpower; + s8 v0; + s8 v1; + s8 v2; + s8 v3; + s8 average; + int max_pwr; + s16 desired_pwr; + s16 estimated_pwr; + s16 pwr_adjust; + s16 radio_att_delta; + s16 baseband_att_delta; + s16 radio_attenuation; + s16 baseband_attenuation; + + if (phy->savedpctlreg == 0xFFFF) + return; + if ((dev->dev->bus->boardinfo.type == 0x0416) && + is_bcm_board_vendor(dev)) + return; +#ifdef CONFIG_B43LEGACY_DEBUG + if (phy->manual_txpower_control) + return; +#endif + + B43legacy_BUG_ON(!(phy->type == B43legacy_PHYTYPE_B || + phy->type == B43legacy_PHYTYPE_G)); + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0058); + v0 = (s8)(tmp & 0x00FF); + v1 = (s8)((tmp & 0xFF00) >> 8); + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x005A); + v2 = (s8)(tmp & 0x00FF); + v3 = (s8)((tmp & 0xFF00) >> 8); + tmp = 0; + + if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) { + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + 0x0070); + v0 = (s8)(tmp & 0x00FF); + v1 = (s8)((tmp & 0xFF00) >> 8); + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, + 0x0072); + v2 = (s8)(tmp & 0x00FF); + v3 = (s8)((tmp & 0xFF00) >> 8); + if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) + return; + v0 = (v0 + 0x20) & 0x3F; + v1 = (v1 + 0x20) & 0x3F; + v2 = (v2 + 0x20) & 0x3F; + v3 = (v3 + 0x20) & 0x3F; + tmp = 1; + } + b43legacy_radio_clear_tssi(dev); + + average = (v0 + v1 + v2 + v3 + 2) / 4; + + if (tmp && (b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x005E) + & 0x8)) + average -= 13; + + estimated_pwr = b43legacy_phy_estimate_power_out(dev, average); + + max_pwr = dev->dev->bus->sprom.maxpwr_bg; + + if ((dev->dev->bus->sprom.boardflags_lo + & B43legacy_BFL_PACTRL) && + (phy->type == B43legacy_PHYTYPE_G)) + max_pwr -= 0x3; + if (unlikely(max_pwr <= 0)) { + b43legacywarn(dev->wl, "Invalid max-TX-power value in SPROM." + "\n"); + max_pwr = 74; /* fake it */ + dev->dev->bus->sprom.maxpwr_bg = max_pwr; + } + + /* Use regulatory information to get the maximum power. + * In the absence of such data from mac80211, we will use 20 dBm, which + * is the value for the EU, US, Canada, and most of the world. + * The regulatory maximum is reduced by the antenna gain (from sprom) + * and 1.5 dBm (a safety factor??). The result is in Q5.2 format + * which accounts for the factor of 4 */ +#define REG_MAX_PWR 20 + max_pwr = min(REG_MAX_PWR * 4 + - dev->dev->bus->sprom.antenna_gain.a0 + - 0x6, max_pwr); + + /* find the desired power in Q5.2 - power_level is in dBm + * and limit it - max_pwr is already in Q5.2 */ + desired_pwr = clamp_val(phy->power_level << 2, 0, max_pwr); + if (b43legacy_debug(dev, B43legacy_DBG_XMITPOWER)) + b43legacydbg(dev->wl, "Current TX power output: " Q52_FMT + " dBm, Desired TX power output: " Q52_FMT + " dBm\n", Q52_ARG(estimated_pwr), + Q52_ARG(desired_pwr)); + /* Check if we need to adjust the current power. The factor of 2 is + * for damping */ + pwr_adjust = (desired_pwr - estimated_pwr) / 2; + /* RF attenuation delta + * The minus sign is because lower attenuation => more power */ + radio_att_delta = -(pwr_adjust + 7) >> 3; + /* Baseband attenuation delta */ + baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta); + /* Do we need to adjust anything? */ + if ((radio_att_delta == 0) && (baseband_att_delta == 0)) { + b43legacy_phy_lo_mark_current_used(dev); + return; + } + + /* Calculate the new attenuation values. */ + baseband_attenuation = phy->bbatt; + baseband_attenuation += baseband_att_delta; + radio_attenuation = phy->rfatt; + radio_attenuation += radio_att_delta; + + /* Get baseband and radio attenuation values into permitted ranges. + * baseband 0-11, radio 0-9. + * Radio attenuation affects power level 4 times as much as baseband. + */ + if (radio_attenuation < 0) { + baseband_attenuation -= (4 * -radio_attenuation); + radio_attenuation = 0; + } else if (radio_attenuation > 9) { + baseband_attenuation += (4 * (radio_attenuation - 9)); + radio_attenuation = 9; + } else { + while (baseband_attenuation < 0 && radio_attenuation > 0) { + baseband_attenuation += 4; + radio_attenuation--; + } + while (baseband_attenuation > 11 && radio_attenuation < 9) { + baseband_attenuation -= 4; + radio_attenuation++; + } + } + baseband_attenuation = clamp_val(baseband_attenuation, 0, 11); + + txpower = phy->txctl1; + if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 2)) { + if (radio_attenuation <= 1) { + if (txpower == 0) { + txpower = 3; + radio_attenuation += 2; + baseband_attenuation += 2; + } else if (dev->dev->bus->sprom.boardflags_lo + & B43legacy_BFL_PACTRL) { + baseband_attenuation += 4 * + (radio_attenuation - 2); + radio_attenuation = 2; + } + } else if (radio_attenuation > 4 && txpower != 0) { + txpower = 0; + if (baseband_attenuation < 3) { + radio_attenuation -= 3; + baseband_attenuation += 2; + } else { + radio_attenuation -= 2; + baseband_attenuation -= 2; + } + } + } + /* Save the control values */ + phy->txctl1 = txpower; + baseband_attenuation = clamp_val(baseband_attenuation, 0, 11); + radio_attenuation = clamp_val(radio_attenuation, 0, 9); + phy->rfatt = radio_attenuation; + phy->bbatt = baseband_attenuation; + + /* Adjust the hardware */ + b43legacy_phy_lock(dev); + b43legacy_radio_lock(dev); + b43legacy_radio_set_txpower_bg(dev, baseband_attenuation, + radio_attenuation, txpower); + b43legacy_phy_lo_mark_current_used(dev); + b43legacy_radio_unlock(dev); + b43legacy_phy_unlock(dev); +} + +static inline +s32 b43legacy_tssi2dbm_ad(s32 num, s32 den) +{ + if (num < 0) + return num/den; + else + return (num+den/2)/den; +} + +static inline +s8 b43legacy_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2) +{ + s32 m1; + s32 m2; + s32 f = 256; + s32 q; + s32 delta; + s8 i = 0; + + m1 = b43legacy_tssi2dbm_ad(16 * pab0 + index * pab1, 32); + m2 = max(b43legacy_tssi2dbm_ad(32768 + index * pab2, 256), 1); + do { + if (i > 15) + return -EINVAL; + q = b43legacy_tssi2dbm_ad(f * 4096 - + b43legacy_tssi2dbm_ad(m2 * f, 16) * + f, 2048); + delta = abs(q - f); + f = q; + i++; + } while (delta >= 2); + entry[index] = clamp_val(b43legacy_tssi2dbm_ad(m1 * f, 8192), + -127, 128); + return 0; +} + +/* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */ +int b43legacy_phy_init_tssi2dbm_table(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + s16 pab0; + s16 pab1; + s16 pab2; + u8 idx; + s8 *dyn_tssi2dbm; + + B43legacy_WARN_ON(!(phy->type == B43legacy_PHYTYPE_B || + phy->type == B43legacy_PHYTYPE_G)); + pab0 = (s16)(dev->dev->bus->sprom.pa0b0); + pab1 = (s16)(dev->dev->bus->sprom.pa0b1); + pab2 = (s16)(dev->dev->bus->sprom.pa0b2); + + if ((dev->dev->bus->chip_id == 0x4301) && (phy->radio_ver != 0x2050)) { + phy->idle_tssi = 0x34; + phy->tssi2dbm = b43legacy_tssi2dbm_b_table; + return 0; + } + + if (pab0 != 0 && pab1 != 0 && pab2 != 0 && + pab0 != -1 && pab1 != -1 && pab2 != -1) { + /* The pabX values are set in SPROM. Use them. */ + if ((s8)dev->dev->bus->sprom.itssi_bg != 0 && + (s8)dev->dev->bus->sprom.itssi_bg != -1) + phy->idle_tssi = (s8)(dev->dev->bus->sprom. + itssi_bg); + else + phy->idle_tssi = 62; + dyn_tssi2dbm = kmalloc(64, GFP_KERNEL); + if (dyn_tssi2dbm == NULL) { + b43legacyerr(dev->wl, "Could not allocate memory " + "for tssi2dbm table\n"); + return -ENOMEM; + } + for (idx = 0; idx < 64; idx++) + if (b43legacy_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0, + pab1, pab2)) { + phy->tssi2dbm = NULL; + b43legacyerr(dev->wl, "Could not generate " + "tssi2dBm table\n"); + kfree(dyn_tssi2dbm); + return -ENODEV; + } + phy->tssi2dbm = dyn_tssi2dbm; + phy->dyn_tssi_tbl = 1; + } else { + /* pabX values not set in SPROM. */ + switch (phy->type) { + case B43legacy_PHYTYPE_B: + phy->idle_tssi = 0x34; + phy->tssi2dbm = b43legacy_tssi2dbm_b_table; + break; + case B43legacy_PHYTYPE_G: + phy->idle_tssi = 0x34; + phy->tssi2dbm = b43legacy_tssi2dbm_g_table; + break; + } + } + + return 0; +} + +int b43legacy_phy_init(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + int err = -ENODEV; + + switch (phy->type) { + case B43legacy_PHYTYPE_B: + switch (phy->rev) { + case 2: + b43legacy_phy_initb2(dev); + err = 0; + break; + case 4: + b43legacy_phy_initb4(dev); + err = 0; + break; + case 5: + b43legacy_phy_initb5(dev); + err = 0; + break; + case 6: + b43legacy_phy_initb6(dev); + err = 0; + break; + } + break; + case B43legacy_PHYTYPE_G: + b43legacy_phy_initg(dev); + err = 0; + break; + } + if (err) + b43legacyerr(dev->wl, "Unknown PHYTYPE found\n"); + + return err; +} + +void b43legacy_phy_set_antenna_diversity(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 antennadiv; + u16 offset; + u16 value; + u32 ucodeflags; + + antennadiv = phy->antenna_diversity; + + if (antennadiv == 0xFFFF) + antennadiv = 3; + B43legacy_WARN_ON(antennadiv > 3); + + ucodeflags = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET); + b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET, + ucodeflags & ~B43legacy_UCODEFLAG_AUTODIV); + + switch (phy->type) { + case B43legacy_PHYTYPE_G: + offset = 0x0400; + + if (antennadiv == 2) + value = (3/*automatic*/ << 7); + else + value = (antennadiv << 7); + b43legacy_phy_write(dev, offset + 1, + (b43legacy_phy_read(dev, offset + 1) + & 0x7E7F) | value); + + if (antennadiv >= 2) { + if (antennadiv == 2) + value = (antennadiv << 7); + else + value = (0/*force0*/ << 7); + b43legacy_phy_write(dev, offset + 0x2B, + (b43legacy_phy_read(dev, + offset + 0x2B) + & 0xFEFF) | value); + } + + if (phy->type == B43legacy_PHYTYPE_G) { + if (antennadiv >= 2) + b43legacy_phy_write(dev, 0x048C, + b43legacy_phy_read(dev, + 0x048C) | 0x2000); + else + b43legacy_phy_write(dev, 0x048C, + b43legacy_phy_read(dev, + 0x048C) & ~0x2000); + if (phy->rev >= 2) { + b43legacy_phy_write(dev, 0x0461, + b43legacy_phy_read(dev, + 0x0461) | 0x0010); + b43legacy_phy_write(dev, 0x04AD, + (b43legacy_phy_read(dev, + 0x04AD) + & 0x00FF) | 0x0015); + if (phy->rev == 2) + b43legacy_phy_write(dev, 0x0427, + 0x0008); + else + b43legacy_phy_write(dev, 0x0427, + (b43legacy_phy_read(dev, 0x0427) + & 0x00FF) | 0x0008); + } else if (phy->rev >= 6) + b43legacy_phy_write(dev, 0x049B, 0x00DC); + } else { + if (phy->rev < 3) + b43legacy_phy_write(dev, 0x002B, + (b43legacy_phy_read(dev, + 0x002B) & 0x00FF) + | 0x0024); + else { + b43legacy_phy_write(dev, 0x0061, + b43legacy_phy_read(dev, + 0x0061) | 0x0010); + if (phy->rev == 3) { + b43legacy_phy_write(dev, 0x0093, + 0x001D); + b43legacy_phy_write(dev, 0x0027, + 0x0008); + } else { + b43legacy_phy_write(dev, 0x0093, + 0x003A); + b43legacy_phy_write(dev, 0x0027, + (b43legacy_phy_read(dev, 0x0027) + & 0x00FF) | 0x0008); + } + } + } + break; + case B43legacy_PHYTYPE_B: + if (dev->dev->id.revision == 2) + value = (3/*automatic*/ << 7); + else + value = (antennadiv << 7); + b43legacy_phy_write(dev, 0x03E2, + (b43legacy_phy_read(dev, 0x03E2) + & 0xFE7F) | value); + break; + default: + B43legacy_WARN_ON(1); + } + + if (antennadiv >= 2) { + ucodeflags = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET); + b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET, + ucodeflags | B43legacy_UCODEFLAG_AUTODIV); + } + + phy->antenna_diversity = antennadiv; +} + +/* Set the PowerSavingControlBits. + * Bitvalues: + * 0 => unset the bit + * 1 => set the bit + * -1 => calculate the bit + */ +void b43legacy_power_saving_ctl_bits(struct b43legacy_wldev *dev, + int bit25, int bit26) +{ + int i; + u32 status; + +/* FIXME: Force 25 to off and 26 to on for now: */ +bit25 = 0; +bit26 = 1; + + if (bit25 == -1) { + /* TODO: If powersave is not off and FIXME is not set and we + * are not in adhoc and thus is not an AP and we arei + * associated, set bit 25 */ + } + if (bit26 == -1) { + /* TODO: If the device is awake or this is an AP, or we are + * scanning, or FIXME, or we are associated, or FIXME, + * or the latest PS-Poll packet sent was successful, + * set bit26 */ + } + status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + if (bit25) + status |= B43legacy_MACCTL_HWPS; + else + status &= ~B43legacy_MACCTL_HWPS; + if (bit26) + status |= B43legacy_MACCTL_AWAKE; + else + status &= ~B43legacy_MACCTL_AWAKE; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); + if (bit26 && dev->dev->id.revision >= 5) { + for (i = 0; i < 100; i++) { + if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, + 0x0040) != 4) + break; + udelay(10); + } + } +} diff --git a/drivers/net/wireless/broadcom/b43legacy/phy.h b/drivers/net/wireless/broadcom/b43legacy/phy.h new file mode 100644 index 000000000000..831a7a4760e5 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/phy.h @@ -0,0 +1,209 @@ +/* + + Broadcom B43legacy wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + Copyright (c) 2007 Larry Finger + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef B43legacy_PHY_H_ +#define B43legacy_PHY_H_ + +#include + +enum { + B43legacy_ANTENNA0, /* Antenna 0 */ + B43legacy_ANTENNA1, /* Antenna 0 */ + B43legacy_ANTENNA_AUTO1, /* Automatic, starting with antenna 1 */ + B43legacy_ANTENNA_AUTO0, /* Automatic, starting with antenna 0 */ + + B43legacy_ANTENNA_AUTO = B43legacy_ANTENNA_AUTO0, + B43legacy_ANTENNA_DEFAULT = B43legacy_ANTENNA_AUTO, +}; + +enum { + B43legacy_INTERFMODE_NONE, + B43legacy_INTERFMODE_NONWLAN, + B43legacy_INTERFMODE_MANUALWLAN, + B43legacy_INTERFMODE_AUTOWLAN, +}; + +/*** PHY Registers ***/ + +/* Routing */ +#define B43legacy_PHYROUTE_OFDM_GPHY 0x400 +#define B43legacy_PHYROUTE_EXT_GPHY 0x800 + +/* Base registers. */ +#define B43legacy_PHY_BASE(reg) (reg) +/* OFDM (A) registers of a G-PHY */ +#define B43legacy_PHY_OFDM(reg) ((reg) | B43legacy_PHYROUTE_OFDM_GPHY) +/* Extended G-PHY registers */ +#define B43legacy_PHY_EXTG(reg) ((reg) | B43legacy_PHYROUTE_EXT_GPHY) + + +/* Extended G-PHY Registers */ +#define B43legacy_PHY_CLASSCTL B43legacy_PHY_EXTG(0x02) /* Classify control */ +#define B43legacy_PHY_GTABCTL B43legacy_PHY_EXTG(0x03) /* G-PHY table control (see below) */ +#define B43legacy_PHY_GTABOFF 0x03FF /* G-PHY table offset (see below) */ +#define B43legacy_PHY_GTABNR 0xFC00 /* G-PHY table number (see below) */ +#define B43legacy_PHY_GTABNR_SHIFT 10 +#define B43legacy_PHY_GTABDATA B43legacy_PHY_EXTG(0x04) /* G-PHY table data */ +#define B43legacy_PHY_LO_MASK B43legacy_PHY_EXTG(0x0F) /* Local Oscillator control mask */ +#define B43legacy_PHY_LO_CTL B43legacy_PHY_EXTG(0x10) /* Local Oscillator control */ +#define B43legacy_PHY_RFOVER B43legacy_PHY_EXTG(0x11) /* RF override */ +#define B43legacy_PHY_RFOVERVAL B43legacy_PHY_EXTG(0x12) /* RF override value */ +/*** OFDM table numbers ***/ +#define B43legacy_OFDMTAB(number, offset) \ + (((number) << B43legacy_PHY_OTABLENR_SHIFT) \ + | (offset)) +#define B43legacy_OFDMTAB_AGC1 B43legacy_OFDMTAB(0x00, 0) +#define B43legacy_OFDMTAB_GAIN0 B43legacy_OFDMTAB(0x00, 0) +#define B43legacy_OFDMTAB_GAINX B43legacy_OFDMTAB(0x01, 0) +#define B43legacy_OFDMTAB_GAIN1 B43legacy_OFDMTAB(0x01, 4) +#define B43legacy_OFDMTAB_AGC3 B43legacy_OFDMTAB(0x02, 0) +#define B43legacy_OFDMTAB_GAIN2 B43legacy_OFDMTAB(0x02, 3) +#define B43legacy_OFDMTAB_LNAHPFGAIN1 B43legacy_OFDMTAB(0x03, 0) +#define B43legacy_OFDMTAB_WRSSI B43legacy_OFDMTAB(0x04, 0) +#define B43legacy_OFDMTAB_LNAHPFGAIN2 B43legacy_OFDMTAB(0x04, 0) +#define B43legacy_OFDMTAB_NOISESCALE B43legacy_OFDMTAB(0x05, 0) +#define B43legacy_OFDMTAB_AGC2 B43legacy_OFDMTAB(0x06, 0) +#define B43legacy_OFDMTAB_ROTOR B43legacy_OFDMTAB(0x08, 0) +#define B43legacy_OFDMTAB_ADVRETARD B43legacy_OFDMTAB(0x09, 0) +#define B43legacy_OFDMTAB_DAC B43legacy_OFDMTAB(0x0C, 0) +#define B43legacy_OFDMTAB_DC B43legacy_OFDMTAB(0x0E, 7) +#define B43legacy_OFDMTAB_PWRDYN2 B43legacy_OFDMTAB(0x0E, 12) +#define B43legacy_OFDMTAB_LNAGAIN B43legacy_OFDMTAB(0x0E, 13) + +#define B43legacy_OFDMTAB_LPFGAIN B43legacy_OFDMTAB(0x0F, 12) +#define B43legacy_OFDMTAB_RSSI B43legacy_OFDMTAB(0x10, 0) + +#define B43legacy_OFDMTAB_AGC1_R1 B43legacy_OFDMTAB(0x13, 0) +#define B43legacy_OFDMTAB_GAINX_R1 B43legacy_OFDMTAB(0x14, 0) +#define B43legacy_OFDMTAB_MINSIGSQ B43legacy_OFDMTAB(0x14, 1) +#define B43legacy_OFDMTAB_AGC3_R1 B43legacy_OFDMTAB(0x15, 0) +#define B43legacy_OFDMTAB_WRSSI_R1 B43legacy_OFDMTAB(0x15, 4) +#define B43legacy_OFDMTAB_TSSI B43legacy_OFDMTAB(0x15, 0) +#define B43legacy_OFDMTAB_DACRFPABB B43legacy_OFDMTAB(0x16, 0) +#define B43legacy_OFDMTAB_DACOFF B43legacy_OFDMTAB(0x17, 0) +#define B43legacy_OFDMTAB_DCBIAS B43legacy_OFDMTAB(0x18, 0) + +void b43legacy_put_attenuation_into_ranges(int *_bbatt, int *_rfatt); + +/* OFDM (A) PHY Registers */ +#define B43legacy_PHY_VERSION_OFDM B43legacy_PHY_OFDM(0x00) /* Versioning register for A-PHY */ +#define B43legacy_PHY_BBANDCFG B43legacy_PHY_OFDM(0x01) /* Baseband config */ +#define B43legacy_PHY_BBANDCFG_RXANT 0x180 /* RX Antenna selection */ +#define B43legacy_PHY_BBANDCFG_RXANT_SHIFT 7 +#define B43legacy_PHY_PWRDOWN B43legacy_PHY_OFDM(0x03) /* Powerdown */ +#define B43legacy_PHY_CRSTHRES1 B43legacy_PHY_OFDM(0x06) /* CRS Threshold 1 */ +#define B43legacy_PHY_LNAHPFCTL B43legacy_PHY_OFDM(0x1C) /* LNA/HPF control */ +#define B43legacy_PHY_ADIVRELATED B43legacy_PHY_OFDM(0x27) /* FIXME rename */ +#define B43legacy_PHY_CRS0 B43legacy_PHY_OFDM(0x29) +#define B43legacy_PHY_ANTDWELL B43legacy_PHY_OFDM(0x2B) /* Antenna dwell */ +#define B43legacy_PHY_ANTDWELL_AUTODIV1 0x0100 /* Automatic RX diversity start antenna */ +#define B43legacy_PHY_ENCORE B43legacy_PHY_OFDM(0x49) /* "Encore" (RangeMax / BroadRange) */ +#define B43legacy_PHY_ENCORE_EN 0x0200 /* Encore enable */ +#define B43legacy_PHY_LMS B43legacy_PHY_OFDM(0x55) +#define B43legacy_PHY_OFDM61 B43legacy_PHY_OFDM(0x61) /* FIXME rename */ +#define B43legacy_PHY_OFDM61_10 0x0010 /* FIXME rename */ +#define B43legacy_PHY_IQBAL B43legacy_PHY_OFDM(0x69) /* I/Q balance */ +#define B43legacy_PHY_OTABLECTL B43legacy_PHY_OFDM(0x72) /* OFDM table control (see below) */ +#define B43legacy_PHY_OTABLEOFF 0x03FF /* OFDM table offset (see below) */ +#define B43legacy_PHY_OTABLENR 0xFC00 /* OFDM table number (see below) */ +#define B43legacy_PHY_OTABLENR_SHIFT 10 +#define B43legacy_PHY_OTABLEI B43legacy_PHY_OFDM(0x73) /* OFDM table data I */ +#define B43legacy_PHY_OTABLEQ B43legacy_PHY_OFDM(0x74) /* OFDM table data Q */ +#define B43legacy_PHY_HPWR_TSSICTL B43legacy_PHY_OFDM(0x78) /* Hardware power TSSI control */ +#define B43legacy_PHY_NRSSITHRES B43legacy_PHY_OFDM(0x8A) /* NRSSI threshold */ +#define B43legacy_PHY_ANTWRSETT B43legacy_PHY_OFDM(0x8C) /* Antenna WR settle */ +#define B43legacy_PHY_ANTWRSETT_ARXDIV 0x2000 /* Automatic RX diversity enabled */ +#define B43legacy_PHY_CLIPPWRDOWNT B43legacy_PHY_OFDM(0x93) /* Clip powerdown threshold */ +#define B43legacy_PHY_OFDM9B B43legacy_PHY_OFDM(0x9B) /* FIXME rename */ +#define B43legacy_PHY_N1P1GAIN B43legacy_PHY_OFDM(0xA0) +#define B43legacy_PHY_P1P2GAIN B43legacy_PHY_OFDM(0xA1) +#define B43legacy_PHY_N1N2GAIN B43legacy_PHY_OFDM(0xA2) +#define B43legacy_PHY_CLIPTHRES B43legacy_PHY_OFDM(0xA3) +#define B43legacy_PHY_CLIPN1P2THRES B43legacy_PHY_OFDM(0xA4) +#define B43legacy_PHY_DIVSRCHIDX B43legacy_PHY_OFDM(0xA8) /* Divider search gain/index */ +#define B43legacy_PHY_CLIPP2THRES B43legacy_PHY_OFDM(0xA9) +#define B43legacy_PHY_CLIPP3THRES B43legacy_PHY_OFDM(0xAA) +#define B43legacy_PHY_DIVP1P2GAIN B43legacy_PHY_OFDM(0xAB) +#define B43legacy_PHY_DIVSRCHGAINBACK B43legacy_PHY_OFDM(0xAD) /* Divider search gain back */ +#define B43legacy_PHY_DIVSRCHGAINCHNG B43legacy_PHY_OFDM(0xAE) /* Divider search gain change */ +#define B43legacy_PHY_CRSTHRES1_R1 B43legacy_PHY_OFDM(0xC0) /* CRS Threshold 1 (rev 1 only) */ +#define B43legacy_PHY_CRSTHRES2_R1 B43legacy_PHY_OFDM(0xC1) /* CRS Threshold 2 (rev 1 only) */ +#define B43legacy_PHY_TSSIP_LTBASE B43legacy_PHY_OFDM(0x380) /* TSSI power lookup table base */ +#define B43legacy_PHY_DC_LTBASE B43legacy_PHY_OFDM(0x3A0) /* DC lookup table base */ +#define B43legacy_PHY_GAIN_LTBASE B43legacy_PHY_OFDM(0x3C0) /* Gain lookup table base */ + +void b43legacy_put_attenuation_into_ranges(int *_bbatt, int *_rfatt); + +/* Masks for the different PHY versioning registers. */ +#define B43legacy_PHYVER_ANALOG 0xF000 +#define B43legacy_PHYVER_ANALOG_SHIFT 12 +#define B43legacy_PHYVER_TYPE 0x0F00 +#define B43legacy_PHYVER_TYPE_SHIFT 8 +#define B43legacy_PHYVER_VERSION 0x00FF + +struct b43legacy_wldev; + +void b43legacy_phy_lock(struct b43legacy_wldev *dev); +void b43legacy_phy_unlock(struct b43legacy_wldev *dev); + +/* Card uses the loopback gain stuff */ +#define has_loopback_gain(phy) \ + (((phy)->rev > 1) || ((phy)->gmode)) + +u16 b43legacy_phy_read(struct b43legacy_wldev *dev, u16 offset); +void b43legacy_phy_write(struct b43legacy_wldev *dev, u16 offset, u16 val); + +int b43legacy_phy_init_tssi2dbm_table(struct b43legacy_wldev *dev); +int b43legacy_phy_init(struct b43legacy_wldev *dev); + +void b43legacy_set_rx_antenna(struct b43legacy_wldev *dev, int antenna); + +void b43legacy_phy_set_antenna_diversity(struct b43legacy_wldev *dev); +void b43legacy_phy_calibrate(struct b43legacy_wldev *dev); +int b43legacy_phy_connect(struct b43legacy_wldev *dev, int connect); + +void b43legacy_phy_lo_b_measure(struct b43legacy_wldev *dev); +void b43legacy_phy_lo_g_measure(struct b43legacy_wldev *dev); +void b43legacy_phy_xmitpower(struct b43legacy_wldev *dev); + +/* Adjust the LocalOscillator to the saved values. + * "fixed" is only set to 1 once in initialization. Set to 0 otherwise. + */ +void b43legacy_phy_lo_adjust(struct b43legacy_wldev *dev, int fixed); +void b43legacy_phy_lo_mark_all_unused(struct b43legacy_wldev *dev); + +void b43legacy_phy_set_baseband_attenuation(struct b43legacy_wldev *dev, + u16 baseband_attenuation); + +void b43legacy_power_saving_ctl_bits(struct b43legacy_wldev *dev, + int bit25, int bit26); + +#endif /* B43legacy_PHY_H_ */ diff --git a/drivers/net/wireless/broadcom/b43legacy/pio.c b/drivers/net/wireless/broadcom/b43legacy/pio.c new file mode 100644 index 000000000000..282eedec675e --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/pio.c @@ -0,0 +1,695 @@ +/* + + Broadcom B43legacy wireless driver + + PIO Transmission + + Copyright (c) 2005 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "b43legacy.h" +#include "pio.h" +#include "main.h" +#include "xmit.h" + +#include +#include + + +static void tx_start(struct b43legacy_pioqueue *queue) +{ + b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, + B43legacy_PIO_TXCTL_INIT); +} + +static void tx_octet(struct b43legacy_pioqueue *queue, + u8 octet) +{ + if (queue->need_workarounds) { + b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, octet); + b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, + B43legacy_PIO_TXCTL_WRITELO); + } else { + b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, + B43legacy_PIO_TXCTL_WRITELO); + b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, octet); + } +} + +static u16 tx_get_next_word(const u8 *txhdr, + const u8 *packet, + size_t txhdr_size, + unsigned int *pos) +{ + const u8 *source; + unsigned int i = *pos; + u16 ret; + + if (i < txhdr_size) + source = txhdr; + else { + source = packet; + i -= txhdr_size; + } + ret = le16_to_cpu(*((__le16 *)(source + i))); + *pos += 2; + + return ret; +} + +static void tx_data(struct b43legacy_pioqueue *queue, + u8 *txhdr, + const u8 *packet, + unsigned int octets) +{ + u16 data; + unsigned int i = 0; + + if (queue->need_workarounds) { + data = tx_get_next_word(txhdr, packet, + sizeof(struct b43legacy_txhdr_fw3), &i); + b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, data); + } + b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, + B43legacy_PIO_TXCTL_WRITELO | + B43legacy_PIO_TXCTL_WRITEHI); + while (i < octets - 1) { + data = tx_get_next_word(txhdr, packet, + sizeof(struct b43legacy_txhdr_fw3), &i); + b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, data); + } + if (octets % 2) + tx_octet(queue, packet[octets - + sizeof(struct b43legacy_txhdr_fw3) - 1]); +} + +static void tx_complete(struct b43legacy_pioqueue *queue, + struct sk_buff *skb) +{ + if (queue->need_workarounds) { + b43legacy_pio_write(queue, B43legacy_PIO_TXDATA, + skb->data[skb->len - 1]); + b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, + B43legacy_PIO_TXCTL_WRITELO | + B43legacy_PIO_TXCTL_COMPLETE); + } else + b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, + B43legacy_PIO_TXCTL_COMPLETE); +} + +static u16 generate_cookie(struct b43legacy_pioqueue *queue, + struct b43legacy_pio_txpacket *packet) +{ + u16 cookie = 0x0000; + int packetindex; + + /* We use the upper 4 bits for the PIO + * controller ID and the lower 12 bits + * for the packet index (in the cache). + */ + switch (queue->mmio_base) { + case B43legacy_MMIO_PIO1_BASE: + break; + case B43legacy_MMIO_PIO2_BASE: + cookie = 0x1000; + break; + case B43legacy_MMIO_PIO3_BASE: + cookie = 0x2000; + break; + case B43legacy_MMIO_PIO4_BASE: + cookie = 0x3000; + break; + default: + B43legacy_WARN_ON(1); + } + packetindex = pio_txpacket_getindex(packet); + B43legacy_WARN_ON(!(((u16)packetindex & 0xF000) == 0x0000)); + cookie |= (u16)packetindex; + + return cookie; +} + +static +struct b43legacy_pioqueue *parse_cookie(struct b43legacy_wldev *dev, + u16 cookie, + struct b43legacy_pio_txpacket **packet) +{ + struct b43legacy_pio *pio = &dev->pio; + struct b43legacy_pioqueue *queue = NULL; + int packetindex; + + switch (cookie & 0xF000) { + case 0x0000: + queue = pio->queue0; + break; + case 0x1000: + queue = pio->queue1; + break; + case 0x2000: + queue = pio->queue2; + break; + case 0x3000: + queue = pio->queue3; + break; + default: + B43legacy_WARN_ON(1); + } + packetindex = (cookie & 0x0FFF); + B43legacy_WARN_ON(!(packetindex >= 0 && packetindex + < B43legacy_PIO_MAXTXPACKETS)); + *packet = &(queue->tx_packets_cache[packetindex]); + + return queue; +} + +union txhdr_union { + struct b43legacy_txhdr_fw3 txhdr_fw3; +}; + +static int pio_tx_write_fragment(struct b43legacy_pioqueue *queue, + struct sk_buff *skb, + struct b43legacy_pio_txpacket *packet, + size_t txhdr_size) +{ + union txhdr_union txhdr_data; + u8 *txhdr = NULL; + unsigned int octets; + int err; + + txhdr = (u8 *)(&txhdr_data.txhdr_fw3); + + B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0); + err = b43legacy_generate_txhdr(queue->dev, + txhdr, skb->data, skb->len, + IEEE80211_SKB_CB(skb), + generate_cookie(queue, packet)); + if (err) + return err; + + tx_start(queue); + octets = skb->len + txhdr_size; + if (queue->need_workarounds) + octets--; + tx_data(queue, txhdr, (u8 *)skb->data, octets); + tx_complete(queue, skb); + + return 0; +} + +static void free_txpacket(struct b43legacy_pio_txpacket *packet, + int irq_context) +{ + struct b43legacy_pioqueue *queue = packet->queue; + + if (packet->skb) { + if (irq_context) + dev_kfree_skb_irq(packet->skb); + else + dev_kfree_skb(packet->skb); + } + list_move(&packet->list, &queue->txfree); + queue->nr_txfree++; +} + +static int pio_tx_packet(struct b43legacy_pio_txpacket *packet) +{ + struct b43legacy_pioqueue *queue = packet->queue; + struct sk_buff *skb = packet->skb; + u16 octets; + int err; + + octets = (u16)skb->len + sizeof(struct b43legacy_txhdr_fw3); + if (queue->tx_devq_size < octets) { + b43legacywarn(queue->dev->wl, "PIO queue too small. " + "Dropping packet.\n"); + /* Drop it silently (return success) */ + free_txpacket(packet, 1); + return 0; + } + B43legacy_WARN_ON(queue->tx_devq_packets > + B43legacy_PIO_MAXTXDEVQPACKETS); + B43legacy_WARN_ON(queue->tx_devq_used > queue->tx_devq_size); + /* Check if there is sufficient free space on the device + * TX queue. If not, return and let the TX tasklet + * retry later. + */ + if (queue->tx_devq_packets == B43legacy_PIO_MAXTXDEVQPACKETS) + return -EBUSY; + if (queue->tx_devq_used + octets > queue->tx_devq_size) + return -EBUSY; + /* Now poke the device. */ + err = pio_tx_write_fragment(queue, skb, packet, + sizeof(struct b43legacy_txhdr_fw3)); + if (unlikely(err == -ENOKEY)) { + /* Drop this packet, as we don't have the encryption key + * anymore and must not transmit it unencrypted. */ + free_txpacket(packet, 1); + return 0; + } + + /* Account for the packet size. + * (We must not overflow the device TX queue) + */ + queue->tx_devq_packets++; + queue->tx_devq_used += octets; + + /* Transmission started, everything ok, move the + * packet to the txrunning list. + */ + list_move_tail(&packet->list, &queue->txrunning); + + return 0; +} + +static void tx_tasklet(unsigned long d) +{ + struct b43legacy_pioqueue *queue = (struct b43legacy_pioqueue *)d; + struct b43legacy_wldev *dev = queue->dev; + unsigned long flags; + struct b43legacy_pio_txpacket *packet, *tmp_packet; + int err; + u16 txctl; + + spin_lock_irqsave(&dev->wl->irq_lock, flags); + if (queue->tx_frozen) + goto out_unlock; + txctl = b43legacy_pio_read(queue, B43legacy_PIO_TXCTL); + if (txctl & B43legacy_PIO_TXCTL_SUSPEND) + goto out_unlock; + + list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) { + /* Try to transmit the packet. This can fail, if + * the device queue is full. In case of failure, the + * packet is left in the txqueue. + * If transmission succeed, the packet is moved to txrunning. + * If it is impossible to transmit the packet, it + * is dropped. + */ + err = pio_tx_packet(packet); + if (err) + break; + } +out_unlock: + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); +} + +static void setup_txqueues(struct b43legacy_pioqueue *queue) +{ + struct b43legacy_pio_txpacket *packet; + int i; + + queue->nr_txfree = B43legacy_PIO_MAXTXPACKETS; + for (i = 0; i < B43legacy_PIO_MAXTXPACKETS; i++) { + packet = &(queue->tx_packets_cache[i]); + + packet->queue = queue; + INIT_LIST_HEAD(&packet->list); + + list_add(&packet->list, &queue->txfree); + } +} + +static +struct b43legacy_pioqueue *b43legacy_setup_pioqueue(struct b43legacy_wldev *dev, + u16 pio_mmio_base) +{ + struct b43legacy_pioqueue *queue; + u32 value; + u16 qsize; + + queue = kzalloc(sizeof(*queue), GFP_KERNEL); + if (!queue) + goto out; + + queue->dev = dev; + queue->mmio_base = pio_mmio_base; + queue->need_workarounds = (dev->dev->id.revision < 3); + + INIT_LIST_HEAD(&queue->txfree); + INIT_LIST_HEAD(&queue->txqueue); + INIT_LIST_HEAD(&queue->txrunning); + tasklet_init(&queue->txtask, tx_tasklet, + (unsigned long)queue); + + value = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + value &= ~B43legacy_MACCTL_BE; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value); + + qsize = b43legacy_read16(dev, queue->mmio_base + + B43legacy_PIO_TXQBUFSIZE); + if (qsize == 0) { + b43legacyerr(dev->wl, "This card does not support PIO " + "operation mode. Please use DMA mode " + "(module parameter pio=0).\n"); + goto err_freequeue; + } + if (qsize <= B43legacy_PIO_TXQADJUST) { + b43legacyerr(dev->wl, "PIO tx device-queue too small (%u)\n", + qsize); + goto err_freequeue; + } + qsize -= B43legacy_PIO_TXQADJUST; + queue->tx_devq_size = qsize; + + setup_txqueues(queue); + +out: + return queue; + +err_freequeue: + kfree(queue); + queue = NULL; + goto out; +} + +static void cancel_transfers(struct b43legacy_pioqueue *queue) +{ + struct b43legacy_pio_txpacket *packet, *tmp_packet; + + tasklet_kill(&queue->txtask); + + list_for_each_entry_safe(packet, tmp_packet, &queue->txrunning, list) + free_txpacket(packet, 0); + list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) + free_txpacket(packet, 0); +} + +static void b43legacy_destroy_pioqueue(struct b43legacy_pioqueue *queue) +{ + if (!queue) + return; + + cancel_transfers(queue); + kfree(queue); +} + +void b43legacy_pio_free(struct b43legacy_wldev *dev) +{ + struct b43legacy_pio *pio; + + if (!b43legacy_using_pio(dev)) + return; + pio = &dev->pio; + + b43legacy_destroy_pioqueue(pio->queue3); + pio->queue3 = NULL; + b43legacy_destroy_pioqueue(pio->queue2); + pio->queue2 = NULL; + b43legacy_destroy_pioqueue(pio->queue1); + pio->queue1 = NULL; + b43legacy_destroy_pioqueue(pio->queue0); + pio->queue0 = NULL; +} + +int b43legacy_pio_init(struct b43legacy_wldev *dev) +{ + struct b43legacy_pio *pio = &dev->pio; + struct b43legacy_pioqueue *queue; + int err = -ENOMEM; + + queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO1_BASE); + if (!queue) + goto out; + pio->queue0 = queue; + + queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO2_BASE); + if (!queue) + goto err_destroy0; + pio->queue1 = queue; + + queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO3_BASE); + if (!queue) + goto err_destroy1; + pio->queue2 = queue; + + queue = b43legacy_setup_pioqueue(dev, B43legacy_MMIO_PIO4_BASE); + if (!queue) + goto err_destroy2; + pio->queue3 = queue; + + if (dev->dev->id.revision < 3) + dev->irq_mask |= B43legacy_IRQ_PIO_WORKAROUND; + + b43legacydbg(dev->wl, "PIO initialized\n"); + err = 0; +out: + return err; + +err_destroy2: + b43legacy_destroy_pioqueue(pio->queue2); + pio->queue2 = NULL; +err_destroy1: + b43legacy_destroy_pioqueue(pio->queue1); + pio->queue1 = NULL; +err_destroy0: + b43legacy_destroy_pioqueue(pio->queue0); + pio->queue0 = NULL; + goto out; +} + +int b43legacy_pio_tx(struct b43legacy_wldev *dev, + struct sk_buff *skb) +{ + struct b43legacy_pioqueue *queue = dev->pio.queue1; + struct b43legacy_pio_txpacket *packet; + + B43legacy_WARN_ON(queue->tx_suspended); + B43legacy_WARN_ON(list_empty(&queue->txfree)); + + packet = list_entry(queue->txfree.next, struct b43legacy_pio_txpacket, + list); + packet->skb = skb; + + list_move_tail(&packet->list, &queue->txqueue); + queue->nr_txfree--; + B43legacy_WARN_ON(queue->nr_txfree >= B43legacy_PIO_MAXTXPACKETS); + + tasklet_schedule(&queue->txtask); + + return 0; +} + +void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, + const struct b43legacy_txstatus *status) +{ + struct b43legacy_pioqueue *queue; + struct b43legacy_pio_txpacket *packet; + struct ieee80211_tx_info *info; + int retry_limit; + + queue = parse_cookie(dev, status->cookie, &packet); + B43legacy_WARN_ON(!queue); + + if (!packet->skb) + return; + + queue->tx_devq_packets--; + queue->tx_devq_used -= (packet->skb->len + + sizeof(struct b43legacy_txhdr_fw3)); + + info = IEEE80211_SKB_CB(packet->skb); + + /* preserve the confiured retry limit before clearing the status + * The xmit function has overwritten the rc's value with the actual + * retry limit done by the hardware */ + retry_limit = info->status.rates[0].count; + ieee80211_tx_info_clear_status(info); + + if (status->acked) + info->flags |= IEEE80211_TX_STAT_ACK; + + if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) { + /* + * If the short retries (RTS, not data frame) have exceeded + * the limit, the hw will not have tried the selected rate, + * but will have used the fallback rate instead. + * Don't let the rate control count attempts for the selected + * rate in this case, otherwise the statistics will be off. + */ + info->status.rates[0].count = 0; + info->status.rates[1].count = status->frame_count; + } else { + if (status->frame_count > retry_limit) { + info->status.rates[0].count = retry_limit; + info->status.rates[1].count = status->frame_count - + retry_limit; + + } else { + info->status.rates[0].count = status->frame_count; + info->status.rates[1].idx = -1; + } + } + ieee80211_tx_status_irqsafe(dev->wl->hw, packet->skb); + packet->skb = NULL; + + free_txpacket(packet, 1); + /* If there are packets on the txqueue, poke the tasklet + * to transmit them. + */ + if (!list_empty(&queue->txqueue)) + tasklet_schedule(&queue->txtask); +} + +static void pio_rx_error(struct b43legacy_pioqueue *queue, + int clear_buffers, + const char *error) +{ + int i; + + b43legacyerr(queue->dev->wl, "PIO RX error: %s\n", error); + b43legacy_pio_write(queue, B43legacy_PIO_RXCTL, + B43legacy_PIO_RXCTL_READY); + if (clear_buffers) { + B43legacy_WARN_ON(queue->mmio_base != B43legacy_MMIO_PIO1_BASE); + for (i = 0; i < 15; i++) { + /* Dummy read. */ + b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); + } + } +} + +void b43legacy_pio_rx(struct b43legacy_pioqueue *queue) +{ + __le16 preamble[21] = { 0 }; + struct b43legacy_rxhdr_fw3 *rxhdr; + u16 tmp; + u16 len; + u16 macstat; + int i; + int preamble_readwords; + struct sk_buff *skb; + + tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXCTL); + if (!(tmp & B43legacy_PIO_RXCTL_DATAAVAILABLE)) + return; + b43legacy_pio_write(queue, B43legacy_PIO_RXCTL, + B43legacy_PIO_RXCTL_DATAAVAILABLE); + + for (i = 0; i < 10; i++) { + tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXCTL); + if (tmp & B43legacy_PIO_RXCTL_READY) + goto data_ready; + udelay(10); + } + b43legacydbg(queue->dev->wl, "PIO RX timed out\n"); + return; +data_ready: + + len = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); + if (unlikely(len > 0x700)) { + pio_rx_error(queue, 0, "len > 0x700"); + return; + } + if (unlikely(len == 0 && queue->mmio_base != + B43legacy_MMIO_PIO4_BASE)) { + pio_rx_error(queue, 0, "len == 0"); + return; + } + preamble[0] = cpu_to_le16(len); + if (queue->mmio_base == B43legacy_MMIO_PIO4_BASE) + preamble_readwords = 14 / sizeof(u16); + else + preamble_readwords = 18 / sizeof(u16); + for (i = 0; i < preamble_readwords; i++) { + tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); + preamble[i + 1] = cpu_to_le16(tmp); + } + rxhdr = (struct b43legacy_rxhdr_fw3 *)preamble; + macstat = le16_to_cpu(rxhdr->mac_status); + if (macstat & B43legacy_RX_MAC_FCSERR) { + pio_rx_error(queue, + (queue->mmio_base == B43legacy_MMIO_PIO1_BASE), + "Frame FCS error"); + return; + } + if (queue->mmio_base == B43legacy_MMIO_PIO4_BASE) { + /* We received an xmit status. */ + struct b43legacy_hwtxstatus *hw; + + hw = (struct b43legacy_hwtxstatus *)(preamble + 1); + b43legacy_handle_hwtxstatus(queue->dev, hw); + + return; + } + + skb = dev_alloc_skb(len); + if (unlikely(!skb)) { + pio_rx_error(queue, 1, "OOM"); + return; + } + skb_put(skb, len); + for (i = 0; i < len - 1; i += 2) { + tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); + *((__le16 *)(skb->data + i)) = cpu_to_le16(tmp); + } + if (len % 2) { + tmp = b43legacy_pio_read(queue, B43legacy_PIO_RXDATA); + skb->data[len - 1] = (tmp & 0x00FF); + } + b43legacy_rx(queue->dev, skb, rxhdr); +} + +void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue) +{ + b43legacy_power_saving_ctl_bits(queue->dev, -1, 1); + b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, + b43legacy_pio_read(queue, B43legacy_PIO_TXCTL) + | B43legacy_PIO_TXCTL_SUSPEND); +} + +void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue) +{ + b43legacy_pio_write(queue, B43legacy_PIO_TXCTL, + b43legacy_pio_read(queue, B43legacy_PIO_TXCTL) + & ~B43legacy_PIO_TXCTL_SUSPEND); + b43legacy_power_saving_ctl_bits(queue->dev, -1, -1); + tasklet_schedule(&queue->txtask); +} + +void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev) +{ + struct b43legacy_pio *pio; + + B43legacy_WARN_ON(!b43legacy_using_pio(dev)); + pio = &dev->pio; + pio->queue0->tx_frozen = 1; + pio->queue1->tx_frozen = 1; + pio->queue2->tx_frozen = 1; + pio->queue3->tx_frozen = 1; +} + +void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev) +{ + struct b43legacy_pio *pio; + + B43legacy_WARN_ON(!b43legacy_using_pio(dev)); + pio = &dev->pio; + pio->queue0->tx_frozen = 0; + pio->queue1->tx_frozen = 0; + pio->queue2->tx_frozen = 0; + pio->queue3->tx_frozen = 0; + if (!list_empty(&pio->queue0->txqueue)) + tasklet_schedule(&pio->queue0->txtask); + if (!list_empty(&pio->queue1->txqueue)) + tasklet_schedule(&pio->queue1->txtask); + if (!list_empty(&pio->queue2->txqueue)) + tasklet_schedule(&pio->queue2->txtask); + if (!list_empty(&pio->queue3->txqueue)) + tasklet_schedule(&pio->queue3->txtask); +} diff --git a/drivers/net/wireless/broadcom/b43legacy/pio.h b/drivers/net/wireless/broadcom/b43legacy/pio.h new file mode 100644 index 000000000000..8e6773ea6e75 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/pio.h @@ -0,0 +1,158 @@ +#ifndef B43legacy_PIO_H_ +#define B43legacy_PIO_H_ + +#include "b43legacy.h" + +#include +#include +#include + + +#define B43legacy_PIO_TXCTL 0x00 +#define B43legacy_PIO_TXDATA 0x02 +#define B43legacy_PIO_TXQBUFSIZE 0x04 +#define B43legacy_PIO_RXCTL 0x08 +#define B43legacy_PIO_RXDATA 0x0A + +#define B43legacy_PIO_TXCTL_WRITELO (1 << 0) +#define B43legacy_PIO_TXCTL_WRITEHI (1 << 1) +#define B43legacy_PIO_TXCTL_COMPLETE (1 << 2) +#define B43legacy_PIO_TXCTL_INIT (1 << 3) +#define B43legacy_PIO_TXCTL_SUSPEND (1 << 7) + +#define B43legacy_PIO_RXCTL_DATAAVAILABLE (1 << 0) +#define B43legacy_PIO_RXCTL_READY (1 << 1) + +/* PIO constants */ +#define B43legacy_PIO_MAXTXDEVQPACKETS 31 +#define B43legacy_PIO_TXQADJUST 80 + +/* PIO tuning knobs */ +#define B43legacy_PIO_MAXTXPACKETS 256 + + + +#ifdef CONFIG_B43LEGACY_PIO + + +struct b43legacy_pioqueue; +struct b43legacy_xmitstatus; + +struct b43legacy_pio_txpacket { + struct b43legacy_pioqueue *queue; + struct sk_buff *skb; + struct list_head list; +}; + +#define pio_txpacket_getindex(packet) ((int)((packet) - \ + (packet)->queue->tx_packets_cache)) + +struct b43legacy_pioqueue { + struct b43legacy_wldev *dev; + u16 mmio_base; + + bool tx_suspended; + bool tx_frozen; + bool need_workarounds; /* Workarounds needed for core.rev < 3 */ + + /* Adjusted size of the device internal TX buffer. */ + u16 tx_devq_size; + /* Used octets of the device internal TX buffer. */ + u16 tx_devq_used; + /* Used packet slots in the device internal TX buffer. */ + u8 tx_devq_packets; + /* Packets from the txfree list can + * be taken on incoming TX requests. + */ + struct list_head txfree; + unsigned int nr_txfree; + /* Packets on the txqueue are queued, + * but not completely written to the chip, yet. + */ + struct list_head txqueue; + /* Packets on the txrunning queue are completely + * posted to the device. We are waiting for the txstatus. + */ + struct list_head txrunning; + struct tasklet_struct txtask; + struct b43legacy_pio_txpacket + tx_packets_cache[B43legacy_PIO_MAXTXPACKETS]; +}; + +static inline +u16 b43legacy_pio_read(struct b43legacy_pioqueue *queue, + u16 offset) +{ + return b43legacy_read16(queue->dev, queue->mmio_base + offset); +} + +static inline +void b43legacy_pio_write(struct b43legacy_pioqueue *queue, + u16 offset, u16 value) +{ + b43legacy_write16(queue->dev, queue->mmio_base + offset, value); + mmiowb(); +} + + +int b43legacy_pio_init(struct b43legacy_wldev *dev); +void b43legacy_pio_free(struct b43legacy_wldev *dev); + +int b43legacy_pio_tx(struct b43legacy_wldev *dev, + struct sk_buff *skb); +void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, + const struct b43legacy_txstatus *status); +void b43legacy_pio_rx(struct b43legacy_pioqueue *queue); + +/* Suspend TX queue in hardware. */ +void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue); +void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue); +/* Suspend (freeze) the TX tasklet (software level). */ +void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev); +void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev); + +#else /* CONFIG_B43LEGACY_PIO */ + +static inline +int b43legacy_pio_init(struct b43legacy_wldev *dev) +{ + return 0; +} +static inline +void b43legacy_pio_free(struct b43legacy_wldev *dev) +{ +} +static inline +int b43legacy_pio_tx(struct b43legacy_wldev *dev, + struct sk_buff *skb) +{ + return 0; +} +static inline +void b43legacy_pio_handle_txstatus(struct b43legacy_wldev *dev, + const struct b43legacy_txstatus *status) +{ +} +static inline +void b43legacy_pio_rx(struct b43legacy_pioqueue *queue) +{ +} +static inline +void b43legacy_pio_tx_suspend(struct b43legacy_pioqueue *queue) +{ +} +static inline +void b43legacy_pio_tx_resume(struct b43legacy_pioqueue *queue) +{ +} +static inline +void b43legacy_pio_freeze_txqueues(struct b43legacy_wldev *dev) +{ +} +static inline +void b43legacy_pio_thaw_txqueues(struct b43legacy_wldev *dev) +{ +} + +#endif /* CONFIG_B43LEGACY_PIO */ +#endif /* B43legacy_PIO_H_ */ diff --git a/drivers/net/wireless/broadcom/b43legacy/radio.c b/drivers/net/wireless/broadcom/b43legacy/radio.c new file mode 100644 index 000000000000..9501420340a9 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/radio.c @@ -0,0 +1,2143 @@ +/* + + Broadcom B43legacy wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + Copyright (c) 2007 Larry Finger + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include + +#include "b43legacy.h" +#include "main.h" +#include "phy.h" +#include "radio.h" +#include "ilt.h" + + +/* Table for b43legacy_radio_calibrationvalue() */ +static const u16 rcc_table[16] = { + 0x0002, 0x0003, 0x0001, 0x000F, + 0x0006, 0x0007, 0x0005, 0x000F, + 0x000A, 0x000B, 0x0009, 0x000F, + 0x000E, 0x000F, 0x000D, 0x000F, +}; + +/* Reverse the bits of a 4bit value. + * Example: 1101 is flipped 1011 + */ +static u16 flip_4bit(u16 value) +{ + u16 flipped = 0x0000; + + B43legacy_BUG_ON(!((value & ~0x000F) == 0x0000)); + + flipped |= (value & 0x0001) << 3; + flipped |= (value & 0x0002) << 1; + flipped |= (value & 0x0004) >> 1; + flipped |= (value & 0x0008) >> 3; + + return flipped; +} + +/* Get the freq, as it has to be written to the device. */ +static inline +u16 channel2freq_bg(u8 channel) +{ + /* Frequencies are given as frequencies_bg[index] + 2.4GHz + * Starting with channel 1 + */ + static const u16 frequencies_bg[14] = { + 12, 17, 22, 27, + 32, 37, 42, 47, + 52, 57, 62, 67, + 72, 84, + }; + + if (unlikely(channel < 1 || channel > 14)) { + printk(KERN_INFO "b43legacy: Channel %d is out of range\n", + channel); + dump_stack(); + return 2412; + } + + return frequencies_bg[channel - 1]; +} + +void b43legacy_radio_lock(struct b43legacy_wldev *dev) +{ + u32 status; + + status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + B43legacy_WARN_ON(status & B43legacy_MACCTL_RADIOLOCK); + status |= B43legacy_MACCTL_RADIOLOCK; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); + mmiowb(); + udelay(10); +} + +void b43legacy_radio_unlock(struct b43legacy_wldev *dev) +{ + u32 status; + + b43legacy_read16(dev, B43legacy_MMIO_PHY_VER); /* dummy read */ + status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL); + B43legacy_WARN_ON(!(status & B43legacy_MACCTL_RADIOLOCK)); + status &= ~B43legacy_MACCTL_RADIOLOCK; + b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status); + mmiowb(); +} + +u16 b43legacy_radio_read16(struct b43legacy_wldev *dev, u16 offset) +{ + struct b43legacy_phy *phy = &dev->phy; + + switch (phy->type) { + case B43legacy_PHYTYPE_B: + if (phy->radio_ver == 0x2053) { + if (offset < 0x70) + offset += 0x80; + else if (offset < 0x80) + offset += 0x70; + } else if (phy->radio_ver == 0x2050) + offset |= 0x80; + else + B43legacy_WARN_ON(1); + break; + case B43legacy_PHYTYPE_G: + offset |= 0x80; + break; + default: + B43legacy_BUG_ON(1); + } + + b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset); + return b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_LOW); +} + +void b43legacy_radio_write16(struct b43legacy_wldev *dev, u16 offset, u16 val) +{ + b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset); + mmiowb(); + b43legacy_write16(dev, B43legacy_MMIO_RADIO_DATA_LOW, val); +} + +static void b43legacy_set_all_gains(struct b43legacy_wldev *dev, + s16 first, s16 second, s16 third) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 i; + u16 start = 0x08; + u16 end = 0x18; + u16 offset = 0x0400; + u16 tmp; + + if (phy->rev <= 1) { + offset = 0x5000; + start = 0x10; + end = 0x20; + } + + for (i = 0; i < 4; i++) + b43legacy_ilt_write(dev, offset + i, first); + + for (i = start; i < end; i++) + b43legacy_ilt_write(dev, offset + i, second); + + if (third != -1) { + tmp = ((u16)third << 14) | ((u16)third << 6); + b43legacy_phy_write(dev, 0x04A0, + (b43legacy_phy_read(dev, 0x04A0) & 0xBFBF) + | tmp); + b43legacy_phy_write(dev, 0x04A1, + (b43legacy_phy_read(dev, 0x04A1) & 0xBFBF) + | tmp); + b43legacy_phy_write(dev, 0x04A2, + (b43legacy_phy_read(dev, 0x04A2) & 0xBFBF) + | tmp); + } + b43legacy_dummy_transmission(dev); +} + +static void b43legacy_set_original_gains(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 i; + u16 tmp; + u16 offset = 0x0400; + u16 start = 0x0008; + u16 end = 0x0018; + + if (phy->rev <= 1) { + offset = 0x5000; + start = 0x0010; + end = 0x0020; + } + + for (i = 0; i < 4; i++) { + tmp = (i & 0xFFFC); + tmp |= (i & 0x0001) << 1; + tmp |= (i & 0x0002) >> 1; + + b43legacy_ilt_write(dev, offset + i, tmp); + } + + for (i = start; i < end; i++) + b43legacy_ilt_write(dev, offset + i, i - start); + + b43legacy_phy_write(dev, 0x04A0, + (b43legacy_phy_read(dev, 0x04A0) & 0xBFBF) + | 0x4040); + b43legacy_phy_write(dev, 0x04A1, + (b43legacy_phy_read(dev, 0x04A1) & 0xBFBF) + | 0x4040); + b43legacy_phy_write(dev, 0x04A2, + (b43legacy_phy_read(dev, 0x04A2) & 0xBFBF) + | 0x4000); + b43legacy_dummy_transmission(dev); +} + +/* Synthetic PU workaround */ +static void b43legacy_synth_pu_workaround(struct b43legacy_wldev *dev, + u8 channel) +{ + struct b43legacy_phy *phy = &dev->phy; + + might_sleep(); + + if (phy->radio_ver != 0x2050 || phy->radio_rev >= 6) + /* We do not need the workaround. */ + return; + + if (channel <= 10) + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, + channel2freq_bg(channel + 4)); + else + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, + channel2freq_bg(channel)); + msleep(1); + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, + channel2freq_bg(channel)); +} + +u8 b43legacy_radio_aci_detect(struct b43legacy_wldev *dev, u8 channel) +{ + struct b43legacy_phy *phy = &dev->phy; + u8 ret = 0; + u16 saved; + u16 rssi; + u16 temp; + int i; + int j = 0; + + saved = b43legacy_phy_read(dev, 0x0403); + b43legacy_radio_selectchannel(dev, channel, 0); + b43legacy_phy_write(dev, 0x0403, (saved & 0xFFF8) | 5); + if (phy->aci_hw_rssi) + rssi = b43legacy_phy_read(dev, 0x048A) & 0x3F; + else + rssi = saved & 0x3F; + /* clamp temp to signed 5bit */ + if (rssi > 32) + rssi -= 64; + for (i = 0; i < 100; i++) { + temp = (b43legacy_phy_read(dev, 0x047F) >> 8) & 0x3F; + if (temp > 32) + temp -= 64; + if (temp < rssi) + j++; + if (j >= 20) + ret = 1; + } + b43legacy_phy_write(dev, 0x0403, saved); + + return ret; +} + +u8 b43legacy_radio_aci_scan(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u8 ret[13]; + unsigned int channel = phy->channel; + unsigned int i; + unsigned int j; + unsigned int start; + unsigned int end; + + if (!((phy->type == B43legacy_PHYTYPE_G) && (phy->rev > 0))) + return 0; + + b43legacy_phy_lock(dev); + b43legacy_radio_lock(dev); + b43legacy_phy_write(dev, 0x0802, + b43legacy_phy_read(dev, 0x0802) & 0xFFFC); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, + b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) + & 0x7FFF); + b43legacy_set_all_gains(dev, 3, 8, 1); + + start = (channel - 5 > 0) ? channel - 5 : 1; + end = (channel + 5 < 14) ? channel + 5 : 13; + + for (i = start; i <= end; i++) { + if (abs(channel - i) > 2) + ret[i-1] = b43legacy_radio_aci_detect(dev, i); + } + b43legacy_radio_selectchannel(dev, channel, 0); + b43legacy_phy_write(dev, 0x0802, + (b43legacy_phy_read(dev, 0x0802) & 0xFFFC) + | 0x0003); + b43legacy_phy_write(dev, 0x0403, + b43legacy_phy_read(dev, 0x0403) & 0xFFF8); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, + b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) + | 0x8000); + b43legacy_set_original_gains(dev); + for (i = 0; i < 13; i++) { + if (!ret[i]) + continue; + end = (i + 5 < 13) ? i + 5 : 13; + for (j = i; j < end; j++) + ret[j] = 1; + } + b43legacy_radio_unlock(dev); + b43legacy_phy_unlock(dev); + + return ret[channel - 1]; +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +void b43legacy_nrssi_hw_write(struct b43legacy_wldev *dev, u16 offset, s16 val) +{ + b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset); + mmiowb(); + b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_DATA, (u16)val); +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +s16 b43legacy_nrssi_hw_read(struct b43legacy_wldev *dev, u16 offset) +{ + u16 val; + + b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset); + val = b43legacy_phy_read(dev, B43legacy_PHY_NRSSILT_DATA); + + return (s16)val; +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val) +{ + u16 i; + s16 tmp; + + for (i = 0; i < 64; i++) { + tmp = b43legacy_nrssi_hw_read(dev, i); + tmp -= val; + tmp = clamp_val(tmp, -32, 31); + b43legacy_nrssi_hw_write(dev, i, tmp); + } +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + s16 i; + s16 delta; + s32 tmp; + + delta = 0x1F - phy->nrssi[0]; + for (i = 0; i < 64; i++) { + tmp = (i - delta) * phy->nrssislope; + tmp /= 0x10000; + tmp += 0x3A; + tmp = clamp_val(tmp, 0, 0x3F); + phy->nrssi_lt[i] = tmp; + } +} + +static void b43legacy_calc_nrssi_offset(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 backup[20] = { 0 }; + s16 v47F; + u16 i; + u16 saved = 0xFFFF; + + backup[0] = b43legacy_phy_read(dev, 0x0001); + backup[1] = b43legacy_phy_read(dev, 0x0811); + backup[2] = b43legacy_phy_read(dev, 0x0812); + backup[3] = b43legacy_phy_read(dev, 0x0814); + backup[4] = b43legacy_phy_read(dev, 0x0815); + backup[5] = b43legacy_phy_read(dev, 0x005A); + backup[6] = b43legacy_phy_read(dev, 0x0059); + backup[7] = b43legacy_phy_read(dev, 0x0058); + backup[8] = b43legacy_phy_read(dev, 0x000A); + backup[9] = b43legacy_phy_read(dev, 0x0003); + backup[10] = b43legacy_radio_read16(dev, 0x007A); + backup[11] = b43legacy_radio_read16(dev, 0x0043); + + b43legacy_phy_write(dev, 0x0429, + b43legacy_phy_read(dev, 0x0429) & 0x7FFF); + b43legacy_phy_write(dev, 0x0001, + (b43legacy_phy_read(dev, 0x0001) & 0x3FFF) + | 0x4000); + b43legacy_phy_write(dev, 0x0811, + b43legacy_phy_read(dev, 0x0811) | 0x000C); + b43legacy_phy_write(dev, 0x0812, + (b43legacy_phy_read(dev, 0x0812) & 0xFFF3) + | 0x0004); + b43legacy_phy_write(dev, 0x0802, + b43legacy_phy_read(dev, 0x0802) & ~(0x1 | 0x2)); + if (phy->rev >= 6) { + backup[12] = b43legacy_phy_read(dev, 0x002E); + backup[13] = b43legacy_phy_read(dev, 0x002F); + backup[14] = b43legacy_phy_read(dev, 0x080F); + backup[15] = b43legacy_phy_read(dev, 0x0810); + backup[16] = b43legacy_phy_read(dev, 0x0801); + backup[17] = b43legacy_phy_read(dev, 0x0060); + backup[18] = b43legacy_phy_read(dev, 0x0014); + backup[19] = b43legacy_phy_read(dev, 0x0478); + + b43legacy_phy_write(dev, 0x002E, 0); + b43legacy_phy_write(dev, 0x002F, 0); + b43legacy_phy_write(dev, 0x080F, 0); + b43legacy_phy_write(dev, 0x0810, 0); + b43legacy_phy_write(dev, 0x0478, + b43legacy_phy_read(dev, 0x0478) | 0x0100); + b43legacy_phy_write(dev, 0x0801, + b43legacy_phy_read(dev, 0x0801) | 0x0040); + b43legacy_phy_write(dev, 0x0060, + b43legacy_phy_read(dev, 0x0060) | 0x0040); + b43legacy_phy_write(dev, 0x0014, + b43legacy_phy_read(dev, 0x0014) | 0x0200); + } + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) | 0x0070); + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) | 0x0080); + udelay(30); + + v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F == 31) { + for (i = 7; i >= 4; i--) { + b43legacy_radio_write16(dev, 0x007B, i); + udelay(20); + v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) + & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F < 31 && saved == 0xFFFF) + saved = i; + } + if (saved == 0xFFFF) + saved = 4; + } else { + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + & 0x007F); + b43legacy_phy_write(dev, 0x0814, + b43legacy_phy_read(dev, 0x0814) | 0x0001); + b43legacy_phy_write(dev, 0x0815, + b43legacy_phy_read(dev, 0x0815) & 0xFFFE); + b43legacy_phy_write(dev, 0x0811, + b43legacy_phy_read(dev, 0x0811) | 0x000C); + b43legacy_phy_write(dev, 0x0812, + b43legacy_phy_read(dev, 0x0812) | 0x000C); + b43legacy_phy_write(dev, 0x0811, + b43legacy_phy_read(dev, 0x0811) | 0x0030); + b43legacy_phy_write(dev, 0x0812, + b43legacy_phy_read(dev, 0x0812) | 0x0030); + b43legacy_phy_write(dev, 0x005A, 0x0480); + b43legacy_phy_write(dev, 0x0059, 0x0810); + b43legacy_phy_write(dev, 0x0058, 0x000D); + if (phy->analog == 0) + b43legacy_phy_write(dev, 0x0003, 0x0122); + else + b43legacy_phy_write(dev, 0x000A, + b43legacy_phy_read(dev, 0x000A) + | 0x2000); + b43legacy_phy_write(dev, 0x0814, + b43legacy_phy_read(dev, 0x0814) | 0x0004); + b43legacy_phy_write(dev, 0x0815, + b43legacy_phy_read(dev, 0x0815) & 0xFFFB); + b43legacy_phy_write(dev, 0x0003, + (b43legacy_phy_read(dev, 0x0003) & 0xFF9F) + | 0x0040); + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + | 0x000F); + b43legacy_set_all_gains(dev, 3, 0, 1); + b43legacy_radio_write16(dev, 0x0043, + (b43legacy_radio_read16(dev, 0x0043) + & 0x00F0) | 0x000F); + udelay(30); + v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F == -32) { + for (i = 0; i < 4; i++) { + b43legacy_radio_write16(dev, 0x007B, i); + udelay(20); + v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> + 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F > -31 && saved == 0xFFFF) + saved = i; + } + if (saved == 0xFFFF) + saved = 3; + } else + saved = 0; + } + b43legacy_radio_write16(dev, 0x007B, saved); + + if (phy->rev >= 6) { + b43legacy_phy_write(dev, 0x002E, backup[12]); + b43legacy_phy_write(dev, 0x002F, backup[13]); + b43legacy_phy_write(dev, 0x080F, backup[14]); + b43legacy_phy_write(dev, 0x0810, backup[15]); + } + b43legacy_phy_write(dev, 0x0814, backup[3]); + b43legacy_phy_write(dev, 0x0815, backup[4]); + b43legacy_phy_write(dev, 0x005A, backup[5]); + b43legacy_phy_write(dev, 0x0059, backup[6]); + b43legacy_phy_write(dev, 0x0058, backup[7]); + b43legacy_phy_write(dev, 0x000A, backup[8]); + b43legacy_phy_write(dev, 0x0003, backup[9]); + b43legacy_radio_write16(dev, 0x0043, backup[11]); + b43legacy_radio_write16(dev, 0x007A, backup[10]); + b43legacy_phy_write(dev, 0x0802, + b43legacy_phy_read(dev, 0x0802) | 0x1 | 0x2); + b43legacy_phy_write(dev, 0x0429, + b43legacy_phy_read(dev, 0x0429) | 0x8000); + b43legacy_set_original_gains(dev); + if (phy->rev >= 6) { + b43legacy_phy_write(dev, 0x0801, backup[16]); + b43legacy_phy_write(dev, 0x0060, backup[17]); + b43legacy_phy_write(dev, 0x0014, backup[18]); + b43legacy_phy_write(dev, 0x0478, backup[19]); + } + b43legacy_phy_write(dev, 0x0001, backup[0]); + b43legacy_phy_write(dev, 0x0812, backup[2]); + b43legacy_phy_write(dev, 0x0811, backup[1]); +} + +void b43legacy_calc_nrssi_slope(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 backup[18] = { 0 }; + u16 tmp; + s16 nrssi0; + s16 nrssi1; + + switch (phy->type) { + case B43legacy_PHYTYPE_B: + backup[0] = b43legacy_radio_read16(dev, 0x007A); + backup[1] = b43legacy_radio_read16(dev, 0x0052); + backup[2] = b43legacy_radio_read16(dev, 0x0043); + backup[3] = b43legacy_phy_read(dev, 0x0030); + backup[4] = b43legacy_phy_read(dev, 0x0026); + backup[5] = b43legacy_phy_read(dev, 0x0015); + backup[6] = b43legacy_phy_read(dev, 0x002A); + backup[7] = b43legacy_phy_read(dev, 0x0020); + backup[8] = b43legacy_phy_read(dev, 0x005A); + backup[9] = b43legacy_phy_read(dev, 0x0059); + backup[10] = b43legacy_phy_read(dev, 0x0058); + backup[11] = b43legacy_read16(dev, 0x03E2); + backup[12] = b43legacy_read16(dev, 0x03E6); + backup[13] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); + + tmp = b43legacy_radio_read16(dev, 0x007A); + tmp &= (phy->rev >= 5) ? 0x007F : 0x000F; + b43legacy_radio_write16(dev, 0x007A, tmp); + b43legacy_phy_write(dev, 0x0030, 0x00FF); + b43legacy_write16(dev, 0x03EC, 0x7F7F); + b43legacy_phy_write(dev, 0x0026, 0x0000); + b43legacy_phy_write(dev, 0x0015, + b43legacy_phy_read(dev, 0x0015) | 0x0020); + b43legacy_phy_write(dev, 0x002A, 0x08A3); + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + | 0x0080); + + nrssi0 = (s16)b43legacy_phy_read(dev, 0x0027); + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + & 0x007F); + if (phy->analog >= 2) + b43legacy_write16(dev, 0x03E6, 0x0040); + else if (phy->analog == 0) + b43legacy_write16(dev, 0x03E6, 0x0122); + else + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, + b43legacy_read16(dev, + B43legacy_MMIO_CHANNEL_EXT) & 0x2000); + b43legacy_phy_write(dev, 0x0020, 0x3F3F); + b43legacy_phy_write(dev, 0x0015, 0xF330); + b43legacy_radio_write16(dev, 0x005A, 0x0060); + b43legacy_radio_write16(dev, 0x0043, + b43legacy_radio_read16(dev, 0x0043) + & 0x00F0); + b43legacy_phy_write(dev, 0x005A, 0x0480); + b43legacy_phy_write(dev, 0x0059, 0x0810); + b43legacy_phy_write(dev, 0x0058, 0x000D); + udelay(20); + + nrssi1 = (s16)b43legacy_phy_read(dev, 0x0027); + b43legacy_phy_write(dev, 0x0030, backup[3]); + b43legacy_radio_write16(dev, 0x007A, backup[0]); + b43legacy_write16(dev, 0x03E2, backup[11]); + b43legacy_phy_write(dev, 0x0026, backup[4]); + b43legacy_phy_write(dev, 0x0015, backup[5]); + b43legacy_phy_write(dev, 0x002A, backup[6]); + b43legacy_synth_pu_workaround(dev, phy->channel); + if (phy->analog != 0) + b43legacy_write16(dev, 0x03F4, backup[13]); + + b43legacy_phy_write(dev, 0x0020, backup[7]); + b43legacy_phy_write(dev, 0x005A, backup[8]); + b43legacy_phy_write(dev, 0x0059, backup[9]); + b43legacy_phy_write(dev, 0x0058, backup[10]); + b43legacy_radio_write16(dev, 0x0052, backup[1]); + b43legacy_radio_write16(dev, 0x0043, backup[2]); + + if (nrssi0 == nrssi1) + phy->nrssislope = 0x00010000; + else + phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1); + + if (nrssi0 <= -4) { + phy->nrssi[0] = nrssi0; + phy->nrssi[1] = nrssi1; + } + break; + case B43legacy_PHYTYPE_G: + if (phy->radio_rev >= 9) + return; + if (phy->radio_rev == 8) + b43legacy_calc_nrssi_offset(dev); + + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, + b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) + & 0x7FFF); + b43legacy_phy_write(dev, 0x0802, + b43legacy_phy_read(dev, 0x0802) & 0xFFFC); + backup[7] = b43legacy_read16(dev, 0x03E2); + b43legacy_write16(dev, 0x03E2, + b43legacy_read16(dev, 0x03E2) | 0x8000); + backup[0] = b43legacy_radio_read16(dev, 0x007A); + backup[1] = b43legacy_radio_read16(dev, 0x0052); + backup[2] = b43legacy_radio_read16(dev, 0x0043); + backup[3] = b43legacy_phy_read(dev, 0x0015); + backup[4] = b43legacy_phy_read(dev, 0x005A); + backup[5] = b43legacy_phy_read(dev, 0x0059); + backup[6] = b43legacy_phy_read(dev, 0x0058); + backup[8] = b43legacy_read16(dev, 0x03E6); + backup[9] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); + if (phy->rev >= 3) { + backup[10] = b43legacy_phy_read(dev, 0x002E); + backup[11] = b43legacy_phy_read(dev, 0x002F); + backup[12] = b43legacy_phy_read(dev, 0x080F); + backup[13] = b43legacy_phy_read(dev, + B43legacy_PHY_G_LO_CONTROL); + backup[14] = b43legacy_phy_read(dev, 0x0801); + backup[15] = b43legacy_phy_read(dev, 0x0060); + backup[16] = b43legacy_phy_read(dev, 0x0014); + backup[17] = b43legacy_phy_read(dev, 0x0478); + b43legacy_phy_write(dev, 0x002E, 0); + b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, 0); + switch (phy->rev) { + case 4: case 6: case 7: + b43legacy_phy_write(dev, 0x0478, + b43legacy_phy_read(dev, + 0x0478) | 0x0100); + b43legacy_phy_write(dev, 0x0801, + b43legacy_phy_read(dev, + 0x0801) | 0x0040); + break; + case 3: case 5: + b43legacy_phy_write(dev, 0x0801, + b43legacy_phy_read(dev, + 0x0801) & 0xFFBF); + break; + } + b43legacy_phy_write(dev, 0x0060, + b43legacy_phy_read(dev, 0x0060) + | 0x0040); + b43legacy_phy_write(dev, 0x0014, + b43legacy_phy_read(dev, 0x0014) + | 0x0200); + } + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + | 0x0070); + b43legacy_set_all_gains(dev, 0, 8, 0); + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + & 0x00F7); + if (phy->rev >= 2) { + b43legacy_phy_write(dev, 0x0811, + (b43legacy_phy_read(dev, 0x0811) + & 0xFFCF) | 0x0030); + b43legacy_phy_write(dev, 0x0812, + (b43legacy_phy_read(dev, 0x0812) + & 0xFFCF) | 0x0010); + } + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + | 0x0080); + udelay(20); + + nrssi0 = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (nrssi0 >= 0x0020) + nrssi0 -= 0x0040; + + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + & 0x007F); + if (phy->analog >= 2) + b43legacy_phy_write(dev, 0x0003, + (b43legacy_phy_read(dev, 0x0003) + & 0xFF9F) | 0x0040); + + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, + b43legacy_read16(dev, + B43legacy_MMIO_CHANNEL_EXT) | 0x2000); + b43legacy_radio_write16(dev, 0x007A, + b43legacy_radio_read16(dev, 0x007A) + | 0x000F); + b43legacy_phy_write(dev, 0x0015, 0xF330); + if (phy->rev >= 2) { + b43legacy_phy_write(dev, 0x0812, + (b43legacy_phy_read(dev, 0x0812) + & 0xFFCF) | 0x0020); + b43legacy_phy_write(dev, 0x0811, + (b43legacy_phy_read(dev, 0x0811) + & 0xFFCF) | 0x0020); + } + + b43legacy_set_all_gains(dev, 3, 0, 1); + if (phy->radio_rev == 8) + b43legacy_radio_write16(dev, 0x0043, 0x001F); + else { + tmp = b43legacy_radio_read16(dev, 0x0052) & 0xFF0F; + b43legacy_radio_write16(dev, 0x0052, tmp | 0x0060); + tmp = b43legacy_radio_read16(dev, 0x0043) & 0xFFF0; + b43legacy_radio_write16(dev, 0x0043, tmp | 0x0009); + } + b43legacy_phy_write(dev, 0x005A, 0x0480); + b43legacy_phy_write(dev, 0x0059, 0x0810); + b43legacy_phy_write(dev, 0x0058, 0x000D); + udelay(20); + nrssi1 = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (nrssi1 >= 0x0020) + nrssi1 -= 0x0040; + if (nrssi0 == nrssi1) + phy->nrssislope = 0x00010000; + else + phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1); + if (nrssi0 >= -4) { + phy->nrssi[0] = nrssi1; + phy->nrssi[1] = nrssi0; + } + if (phy->rev >= 3) { + b43legacy_phy_write(dev, 0x002E, backup[10]); + b43legacy_phy_write(dev, 0x002F, backup[11]); + b43legacy_phy_write(dev, 0x080F, backup[12]); + b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, + backup[13]); + } + if (phy->rev >= 2) { + b43legacy_phy_write(dev, 0x0812, + b43legacy_phy_read(dev, 0x0812) + & 0xFFCF); + b43legacy_phy_write(dev, 0x0811, + b43legacy_phy_read(dev, 0x0811) + & 0xFFCF); + } + + b43legacy_radio_write16(dev, 0x007A, backup[0]); + b43legacy_radio_write16(dev, 0x0052, backup[1]); + b43legacy_radio_write16(dev, 0x0043, backup[2]); + b43legacy_write16(dev, 0x03E2, backup[7]); + b43legacy_write16(dev, 0x03E6, backup[8]); + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, backup[9]); + b43legacy_phy_write(dev, 0x0015, backup[3]); + b43legacy_phy_write(dev, 0x005A, backup[4]); + b43legacy_phy_write(dev, 0x0059, backup[5]); + b43legacy_phy_write(dev, 0x0058, backup[6]); + b43legacy_synth_pu_workaround(dev, phy->channel); + b43legacy_phy_write(dev, 0x0802, + b43legacy_phy_read(dev, 0x0802) | 0x0003); + b43legacy_set_original_gains(dev); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, + b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) + | 0x8000); + if (phy->rev >= 3) { + b43legacy_phy_write(dev, 0x0801, backup[14]); + b43legacy_phy_write(dev, 0x0060, backup[15]); + b43legacy_phy_write(dev, 0x0014, backup[16]); + b43legacy_phy_write(dev, 0x0478, backup[17]); + } + b43legacy_nrssi_mem_update(dev); + b43legacy_calc_nrssi_threshold(dev); + break; + default: + B43legacy_BUG_ON(1); + } +} + +void b43legacy_calc_nrssi_threshold(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + s32 threshold; + s32 a; + s32 b; + s16 tmp16; + u16 tmp_u16; + + switch (phy->type) { + case B43legacy_PHYTYPE_B: { + if (phy->radio_ver != 0x2050) + return; + if (!(dev->dev->bus->sprom.boardflags_lo & + B43legacy_BFL_RSSI)) + return; + + if (phy->radio_rev >= 6) { + threshold = (phy->nrssi[1] - phy->nrssi[0]) * 32; + threshold += 20 * (phy->nrssi[0] + 1); + threshold /= 40; + } else + threshold = phy->nrssi[1] - 5; + + threshold = clamp_val(threshold, 0, 0x3E); + b43legacy_phy_read(dev, 0x0020); /* dummy read */ + b43legacy_phy_write(dev, 0x0020, (((u16)threshold) << 8) + | 0x001C); + + if (phy->radio_rev >= 6) { + b43legacy_phy_write(dev, 0x0087, 0x0E0D); + b43legacy_phy_write(dev, 0x0086, 0x0C0B); + b43legacy_phy_write(dev, 0x0085, 0x0A09); + b43legacy_phy_write(dev, 0x0084, 0x0808); + b43legacy_phy_write(dev, 0x0083, 0x0808); + b43legacy_phy_write(dev, 0x0082, 0x0604); + b43legacy_phy_write(dev, 0x0081, 0x0302); + b43legacy_phy_write(dev, 0x0080, 0x0100); + } + break; + } + case B43legacy_PHYTYPE_G: + if (!phy->gmode || + !(dev->dev->bus->sprom.boardflags_lo & + B43legacy_BFL_RSSI)) { + tmp16 = b43legacy_nrssi_hw_read(dev, 0x20); + if (tmp16 >= 0x20) + tmp16 -= 0x40; + if (tmp16 < 3) + b43legacy_phy_write(dev, 0x048A, + (b43legacy_phy_read(dev, + 0x048A) & 0xF000) | 0x09EB); + else + b43legacy_phy_write(dev, 0x048A, + (b43legacy_phy_read(dev, + 0x048A) & 0xF000) | 0x0AED); + } else { + if (phy->interfmode == + B43legacy_RADIO_INTERFMODE_NONWLAN) { + a = 0xE; + b = 0xA; + } else if (!phy->aci_wlan_automatic && + phy->aci_enable) { + a = 0x13; + b = 0x12; + } else { + a = 0xE; + b = 0x11; + } + + a = a * (phy->nrssi[1] - phy->nrssi[0]); + a += (phy->nrssi[0] << 6); + if (a < 32) + a += 31; + else + a += 32; + a = a >> 6; + a = clamp_val(a, -31, 31); + + b = b * (phy->nrssi[1] - phy->nrssi[0]); + b += (phy->nrssi[0] << 6); + if (b < 32) + b += 31; + else + b += 32; + b = b >> 6; + b = clamp_val(b, -31, 31); + + tmp_u16 = b43legacy_phy_read(dev, 0x048A) & 0xF000; + tmp_u16 |= ((u32)b & 0x0000003F); + tmp_u16 |= (((u32)a & 0x0000003F) << 6); + b43legacy_phy_write(dev, 0x048A, tmp_u16); + } + break; + default: + B43legacy_BUG_ON(1); + } +} + +/* Stack implementation to save/restore values from the + * interference mitigation code. + * It is save to restore values in random order. + */ +static void _stack_save(u32 *_stackptr, size_t *stackidx, + u8 id, u16 offset, u16 value) +{ + u32 *stackptr = &(_stackptr[*stackidx]); + + B43legacy_WARN_ON(!((offset & 0xE000) == 0x0000)); + B43legacy_WARN_ON(!((id & 0xF8) == 0x00)); + *stackptr = offset; + *stackptr |= ((u32)id) << 13; + *stackptr |= ((u32)value) << 16; + (*stackidx)++; + B43legacy_WARN_ON(!(*stackidx < B43legacy_INTERFSTACK_SIZE)); +} + +static u16 _stack_restore(u32 *stackptr, + u8 id, u16 offset) +{ + size_t i; + + B43legacy_WARN_ON(!((offset & 0xE000) == 0x0000)); + B43legacy_WARN_ON(!((id & 0xF8) == 0x00)); + for (i = 0; i < B43legacy_INTERFSTACK_SIZE; i++, stackptr++) { + if ((*stackptr & 0x00001FFF) != offset) + continue; + if (((*stackptr & 0x00007000) >> 13) != id) + continue; + return ((*stackptr & 0xFFFF0000) >> 16); + } + B43legacy_BUG_ON(1); + + return 0; +} + +#define phy_stacksave(offset) \ + do { \ + _stack_save(stack, &stackidx, 0x1, (offset), \ + b43legacy_phy_read(dev, (offset))); \ + } while (0) +#define phy_stackrestore(offset) \ + do { \ + b43legacy_phy_write(dev, (offset), \ + _stack_restore(stack, 0x1, \ + (offset))); \ + } while (0) +#define radio_stacksave(offset) \ + do { \ + _stack_save(stack, &stackidx, 0x2, (offset), \ + b43legacy_radio_read16(dev, (offset))); \ + } while (0) +#define radio_stackrestore(offset) \ + do { \ + b43legacy_radio_write16(dev, (offset), \ + _stack_restore(stack, 0x2, \ + (offset))); \ + } while (0) +#define ilt_stacksave(offset) \ + do { \ + _stack_save(stack, &stackidx, 0x3, (offset), \ + b43legacy_ilt_read(dev, (offset))); \ + } while (0) +#define ilt_stackrestore(offset) \ + do { \ + b43legacy_ilt_write(dev, (offset), \ + _stack_restore(stack, 0x3, \ + (offset))); \ + } while (0) + +static void +b43legacy_radio_interference_mitigation_enable(struct b43legacy_wldev *dev, + int mode) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 tmp; + u16 flipped; + u32 tmp32; + size_t stackidx = 0; + u32 *stack = phy->interfstack; + + switch (mode) { + case B43legacy_RADIO_INTERFMODE_NONWLAN: + if (phy->rev != 1) { + b43legacy_phy_write(dev, 0x042B, + b43legacy_phy_read(dev, 0x042B) + | 0x0800); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, + b43legacy_phy_read(dev, + B43legacy_PHY_G_CRS) & ~0x4000); + break; + } + radio_stacksave(0x0078); + tmp = (b43legacy_radio_read16(dev, 0x0078) & 0x001E); + flipped = flip_4bit(tmp); + if (flipped < 10 && flipped >= 8) + flipped = 7; + else if (flipped >= 10) + flipped -= 3; + flipped = flip_4bit(flipped); + flipped = (flipped << 1) | 0x0020; + b43legacy_radio_write16(dev, 0x0078, flipped); + + b43legacy_calc_nrssi_threshold(dev); + + phy_stacksave(0x0406); + b43legacy_phy_write(dev, 0x0406, 0x7E28); + + b43legacy_phy_write(dev, 0x042B, + b43legacy_phy_read(dev, 0x042B) | 0x0800); + b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, + b43legacy_phy_read(dev, + B43legacy_PHY_RADIO_BITFIELD) | 0x1000); + + phy_stacksave(0x04A0); + b43legacy_phy_write(dev, 0x04A0, + (b43legacy_phy_read(dev, 0x04A0) & 0xC0C0) + | 0x0008); + phy_stacksave(0x04A1); + b43legacy_phy_write(dev, 0x04A1, + (b43legacy_phy_read(dev, 0x04A1) & 0xC0C0) + | 0x0605); + phy_stacksave(0x04A2); + b43legacy_phy_write(dev, 0x04A2, + (b43legacy_phy_read(dev, 0x04A2) & 0xC0C0) + | 0x0204); + phy_stacksave(0x04A8); + b43legacy_phy_write(dev, 0x04A8, + (b43legacy_phy_read(dev, 0x04A8) & 0xC0C0) + | 0x0803); + phy_stacksave(0x04AB); + b43legacy_phy_write(dev, 0x04AB, + (b43legacy_phy_read(dev, 0x04AB) & 0xC0C0) + | 0x0605); + + phy_stacksave(0x04A7); + b43legacy_phy_write(dev, 0x04A7, 0x0002); + phy_stacksave(0x04A3); + b43legacy_phy_write(dev, 0x04A3, 0x287A); + phy_stacksave(0x04A9); + b43legacy_phy_write(dev, 0x04A9, 0x2027); + phy_stacksave(0x0493); + b43legacy_phy_write(dev, 0x0493, 0x32F5); + phy_stacksave(0x04AA); + b43legacy_phy_write(dev, 0x04AA, 0x2027); + phy_stacksave(0x04AC); + b43legacy_phy_write(dev, 0x04AC, 0x32F5); + break; + case B43legacy_RADIO_INTERFMODE_MANUALWLAN: + if (b43legacy_phy_read(dev, 0x0033) & 0x0800) + break; + + phy->aci_enable = true; + + phy_stacksave(B43legacy_PHY_RADIO_BITFIELD); + phy_stacksave(B43legacy_PHY_G_CRS); + if (phy->rev < 2) + phy_stacksave(0x0406); + else { + phy_stacksave(0x04C0); + phy_stacksave(0x04C1); + } + phy_stacksave(0x0033); + phy_stacksave(0x04A7); + phy_stacksave(0x04A3); + phy_stacksave(0x04A9); + phy_stacksave(0x04AA); + phy_stacksave(0x04AC); + phy_stacksave(0x0493); + phy_stacksave(0x04A1); + phy_stacksave(0x04A0); + phy_stacksave(0x04A2); + phy_stacksave(0x048A); + phy_stacksave(0x04A8); + phy_stacksave(0x04AB); + if (phy->rev == 2) { + phy_stacksave(0x04AD); + phy_stacksave(0x04AE); + } else if (phy->rev >= 3) { + phy_stacksave(0x04AD); + phy_stacksave(0x0415); + phy_stacksave(0x0416); + phy_stacksave(0x0417); + ilt_stacksave(0x1A00 + 0x2); + ilt_stacksave(0x1A00 + 0x3); + } + phy_stacksave(0x042B); + phy_stacksave(0x048C); + + b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, + b43legacy_phy_read(dev, + B43legacy_PHY_RADIO_BITFIELD) & ~0x1000); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, + (b43legacy_phy_read(dev, + B43legacy_PHY_G_CRS) + & 0xFFFC) | 0x0002); + + b43legacy_phy_write(dev, 0x0033, 0x0800); + b43legacy_phy_write(dev, 0x04A3, 0x2027); + b43legacy_phy_write(dev, 0x04A9, 0x1CA8); + b43legacy_phy_write(dev, 0x0493, 0x287A); + b43legacy_phy_write(dev, 0x04AA, 0x1CA8); + b43legacy_phy_write(dev, 0x04AC, 0x287A); + + b43legacy_phy_write(dev, 0x04A0, + (b43legacy_phy_read(dev, 0x04A0) + & 0xFFC0) | 0x001A); + b43legacy_phy_write(dev, 0x04A7, 0x000D); + + if (phy->rev < 2) + b43legacy_phy_write(dev, 0x0406, 0xFF0D); + else if (phy->rev == 2) { + b43legacy_phy_write(dev, 0x04C0, 0xFFFF); + b43legacy_phy_write(dev, 0x04C1, 0x00A9); + } else { + b43legacy_phy_write(dev, 0x04C0, 0x00C1); + b43legacy_phy_write(dev, 0x04C1, 0x0059); + } + + b43legacy_phy_write(dev, 0x04A1, + (b43legacy_phy_read(dev, 0x04A1) + & 0xC0FF) | 0x1800); + b43legacy_phy_write(dev, 0x04A1, + (b43legacy_phy_read(dev, 0x04A1) + & 0xFFC0) | 0x0015); + b43legacy_phy_write(dev, 0x04A8, + (b43legacy_phy_read(dev, 0x04A8) + & 0xCFFF) | 0x1000); + b43legacy_phy_write(dev, 0x04A8, + (b43legacy_phy_read(dev, 0x04A8) + & 0xF0FF) | 0x0A00); + b43legacy_phy_write(dev, 0x04AB, + (b43legacy_phy_read(dev, 0x04AB) + & 0xCFFF) | 0x1000); + b43legacy_phy_write(dev, 0x04AB, + (b43legacy_phy_read(dev, 0x04AB) + & 0xF0FF) | 0x0800); + b43legacy_phy_write(dev, 0x04AB, + (b43legacy_phy_read(dev, 0x04AB) + & 0xFFCF) | 0x0010); + b43legacy_phy_write(dev, 0x04AB, + (b43legacy_phy_read(dev, 0x04AB) + & 0xFFF0) | 0x0005); + b43legacy_phy_write(dev, 0x04A8, + (b43legacy_phy_read(dev, 0x04A8) + & 0xFFCF) | 0x0010); + b43legacy_phy_write(dev, 0x04A8, + (b43legacy_phy_read(dev, 0x04A8) + & 0xFFF0) | 0x0006); + b43legacy_phy_write(dev, 0x04A2, + (b43legacy_phy_read(dev, 0x04A2) + & 0xF0FF) | 0x0800); + b43legacy_phy_write(dev, 0x04A0, + (b43legacy_phy_read(dev, 0x04A0) + & 0xF0FF) | 0x0500); + b43legacy_phy_write(dev, 0x04A2, + (b43legacy_phy_read(dev, 0x04A2) + & 0xFFF0) | 0x000B); + + if (phy->rev >= 3) { + b43legacy_phy_write(dev, 0x048A, + b43legacy_phy_read(dev, 0x048A) + & ~0x8000); + b43legacy_phy_write(dev, 0x0415, + (b43legacy_phy_read(dev, 0x0415) + & 0x8000) | 0x36D8); + b43legacy_phy_write(dev, 0x0416, + (b43legacy_phy_read(dev, 0x0416) + & 0x8000) | 0x36D8); + b43legacy_phy_write(dev, 0x0417, + (b43legacy_phy_read(dev, 0x0417) + & 0xFE00) | 0x016D); + } else { + b43legacy_phy_write(dev, 0x048A, + b43legacy_phy_read(dev, 0x048A) + | 0x1000); + b43legacy_phy_write(dev, 0x048A, + (b43legacy_phy_read(dev, 0x048A) + & 0x9FFF) | 0x2000); + tmp32 = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET); + if (!(tmp32 & 0x800)) { + tmp32 |= 0x800; + b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET, + tmp32); + } + } + if (phy->rev >= 2) + b43legacy_phy_write(dev, 0x042B, + b43legacy_phy_read(dev, 0x042B) + | 0x0800); + b43legacy_phy_write(dev, 0x048C, + (b43legacy_phy_read(dev, 0x048C) + & 0xF0FF) | 0x0200); + if (phy->rev == 2) { + b43legacy_phy_write(dev, 0x04AE, + (b43legacy_phy_read(dev, 0x04AE) + & 0xFF00) | 0x007F); + b43legacy_phy_write(dev, 0x04AD, + (b43legacy_phy_read(dev, 0x04AD) + & 0x00FF) | 0x1300); + } else if (phy->rev >= 6) { + b43legacy_ilt_write(dev, 0x1A00 + 0x3, 0x007F); + b43legacy_ilt_write(dev, 0x1A00 + 0x2, 0x007F); + b43legacy_phy_write(dev, 0x04AD, + b43legacy_phy_read(dev, 0x04AD) + & 0x00FF); + } + b43legacy_calc_nrssi_slope(dev); + break; + default: + B43legacy_BUG_ON(1); + } +} + +static void +b43legacy_radio_interference_mitigation_disable(struct b43legacy_wldev *dev, + int mode) +{ + struct b43legacy_phy *phy = &dev->phy; + u32 tmp32; + u32 *stack = phy->interfstack; + + switch (mode) { + case B43legacy_RADIO_INTERFMODE_NONWLAN: + if (phy->rev != 1) { + b43legacy_phy_write(dev, 0x042B, + b43legacy_phy_read(dev, 0x042B) + & ~0x0800); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, + b43legacy_phy_read(dev, + B43legacy_PHY_G_CRS) | 0x4000); + break; + } + phy_stackrestore(0x0078); + b43legacy_calc_nrssi_threshold(dev); + phy_stackrestore(0x0406); + b43legacy_phy_write(dev, 0x042B, + b43legacy_phy_read(dev, 0x042B) & ~0x0800); + if (!dev->bad_frames_preempt) + b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD, + b43legacy_phy_read(dev, + B43legacy_PHY_RADIO_BITFIELD) + & ~(1 << 11)); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, + b43legacy_phy_read(dev, B43legacy_PHY_G_CRS) + | 0x4000); + phy_stackrestore(0x04A0); + phy_stackrestore(0x04A1); + phy_stackrestore(0x04A2); + phy_stackrestore(0x04A8); + phy_stackrestore(0x04AB); + phy_stackrestore(0x04A7); + phy_stackrestore(0x04A3); + phy_stackrestore(0x04A9); + phy_stackrestore(0x0493); + phy_stackrestore(0x04AA); + phy_stackrestore(0x04AC); + break; + case B43legacy_RADIO_INTERFMODE_MANUALWLAN: + if (!(b43legacy_phy_read(dev, 0x0033) & 0x0800)) + break; + + phy->aci_enable = false; + + phy_stackrestore(B43legacy_PHY_RADIO_BITFIELD); + phy_stackrestore(B43legacy_PHY_G_CRS); + phy_stackrestore(0x0033); + phy_stackrestore(0x04A3); + phy_stackrestore(0x04A9); + phy_stackrestore(0x0493); + phy_stackrestore(0x04AA); + phy_stackrestore(0x04AC); + phy_stackrestore(0x04A0); + phy_stackrestore(0x04A7); + if (phy->rev >= 2) { + phy_stackrestore(0x04C0); + phy_stackrestore(0x04C1); + } else + phy_stackrestore(0x0406); + phy_stackrestore(0x04A1); + phy_stackrestore(0x04AB); + phy_stackrestore(0x04A8); + if (phy->rev == 2) { + phy_stackrestore(0x04AD); + phy_stackrestore(0x04AE); + } else if (phy->rev >= 3) { + phy_stackrestore(0x04AD); + phy_stackrestore(0x0415); + phy_stackrestore(0x0416); + phy_stackrestore(0x0417); + ilt_stackrestore(0x1A00 + 0x2); + ilt_stackrestore(0x1A00 + 0x3); + } + phy_stackrestore(0x04A2); + phy_stackrestore(0x04A8); + phy_stackrestore(0x042B); + phy_stackrestore(0x048C); + tmp32 = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET); + if (tmp32 & 0x800) { + tmp32 &= ~0x800; + b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET, + tmp32); + } + b43legacy_calc_nrssi_slope(dev); + break; + default: + B43legacy_BUG_ON(1); + } +} + +#undef phy_stacksave +#undef phy_stackrestore +#undef radio_stacksave +#undef radio_stackrestore +#undef ilt_stacksave +#undef ilt_stackrestore + +int b43legacy_radio_set_interference_mitigation(struct b43legacy_wldev *dev, + int mode) +{ + struct b43legacy_phy *phy = &dev->phy; + int currentmode; + + if ((phy->type != B43legacy_PHYTYPE_G) || + (phy->rev == 0) || (!phy->gmode)) + return -ENODEV; + + phy->aci_wlan_automatic = false; + switch (mode) { + case B43legacy_RADIO_INTERFMODE_AUTOWLAN: + phy->aci_wlan_automatic = true; + if (phy->aci_enable) + mode = B43legacy_RADIO_INTERFMODE_MANUALWLAN; + else + mode = B43legacy_RADIO_INTERFMODE_NONE; + break; + case B43legacy_RADIO_INTERFMODE_NONE: + case B43legacy_RADIO_INTERFMODE_NONWLAN: + case B43legacy_RADIO_INTERFMODE_MANUALWLAN: + break; + default: + return -EINVAL; + } + + currentmode = phy->interfmode; + if (currentmode == mode) + return 0; + if (currentmode != B43legacy_RADIO_INTERFMODE_NONE) + b43legacy_radio_interference_mitigation_disable(dev, + currentmode); + + if (mode == B43legacy_RADIO_INTERFMODE_NONE) { + phy->aci_enable = false; + phy->aci_hw_rssi = false; + } else + b43legacy_radio_interference_mitigation_enable(dev, mode); + phy->interfmode = mode; + + return 0; +} + +u16 b43legacy_radio_calibrationvalue(struct b43legacy_wldev *dev) +{ + u16 reg; + u16 index; + u16 ret; + + reg = b43legacy_radio_read16(dev, 0x0060); + index = (reg & 0x001E) >> 1; + ret = rcc_table[index] << 1; + ret |= (reg & 0x0001); + ret |= 0x0020; + + return ret; +} + +#define LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0)) +static u16 b43legacy_get_812_value(struct b43legacy_wldev *dev, u8 lpd) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 loop_or = 0; + u16 adj_loopback_gain = phy->loopback_gain[0]; + u8 loop; + u16 extern_lna_control; + + if (!phy->gmode) + return 0; + if (!has_loopback_gain(phy)) { + if (phy->rev < 7 || !(dev->dev->bus->sprom.boardflags_lo + & B43legacy_BFL_EXTLNA)) { + switch (lpd) { + case LPD(0, 1, 1): + return 0x0FB2; + case LPD(0, 0, 1): + return 0x00B2; + case LPD(1, 0, 1): + return 0x30B2; + case LPD(1, 0, 0): + return 0x30B3; + default: + B43legacy_BUG_ON(1); + } + } else { + switch (lpd) { + case LPD(0, 1, 1): + return 0x8FB2; + case LPD(0, 0, 1): + return 0x80B2; + case LPD(1, 0, 1): + return 0x20B2; + case LPD(1, 0, 0): + return 0x20B3; + default: + B43legacy_BUG_ON(1); + } + } + } else { + if (phy->radio_rev == 8) + adj_loopback_gain += 0x003E; + else + adj_loopback_gain += 0x0026; + if (adj_loopback_gain >= 0x46) { + adj_loopback_gain -= 0x46; + extern_lna_control = 0x3000; + } else if (adj_loopback_gain >= 0x3A) { + adj_loopback_gain -= 0x3A; + extern_lna_control = 0x2000; + } else if (adj_loopback_gain >= 0x2E) { + adj_loopback_gain -= 0x2E; + extern_lna_control = 0x1000; + } else { + adj_loopback_gain -= 0x10; + extern_lna_control = 0x0000; + } + for (loop = 0; loop < 16; loop++) { + u16 tmp = adj_loopback_gain - 6 * loop; + if (tmp < 6) + break; + } + + loop_or = (loop << 8) | extern_lna_control; + if (phy->rev >= 7 && dev->dev->bus->sprom.boardflags_lo + & B43legacy_BFL_EXTLNA) { + if (extern_lna_control) + loop_or |= 0x8000; + switch (lpd) { + case LPD(0, 1, 1): + return 0x8F92; + case LPD(0, 0, 1): + return (0x8092 | loop_or); + case LPD(1, 0, 1): + return (0x2092 | loop_or); + case LPD(1, 0, 0): + return (0x2093 | loop_or); + default: + B43legacy_BUG_ON(1); + } + } else { + switch (lpd) { + case LPD(0, 1, 1): + return 0x0F92; + case LPD(0, 0, 1): + case LPD(1, 0, 1): + return (0x0092 | loop_or); + case LPD(1, 0, 0): + return (0x0093 | loop_or); + default: + B43legacy_BUG_ON(1); + } + } + } + return 0; +} + +u16 b43legacy_radio_init2050(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 backup[21] = { 0 }; + u16 ret; + u16 i; + u16 j; + u32 tmp1 = 0; + u32 tmp2 = 0; + + backup[0] = b43legacy_radio_read16(dev, 0x0043); + backup[14] = b43legacy_radio_read16(dev, 0x0051); + backup[15] = b43legacy_radio_read16(dev, 0x0052); + backup[1] = b43legacy_phy_read(dev, 0x0015); + backup[16] = b43legacy_phy_read(dev, 0x005A); + backup[17] = b43legacy_phy_read(dev, 0x0059); + backup[18] = b43legacy_phy_read(dev, 0x0058); + if (phy->type == B43legacy_PHYTYPE_B) { + backup[2] = b43legacy_phy_read(dev, 0x0030); + backup[3] = b43legacy_read16(dev, 0x03EC); + b43legacy_phy_write(dev, 0x0030, 0x00FF); + b43legacy_write16(dev, 0x03EC, 0x3F3F); + } else { + if (phy->gmode) { + backup[4] = b43legacy_phy_read(dev, 0x0811); + backup[5] = b43legacy_phy_read(dev, 0x0812); + backup[6] = b43legacy_phy_read(dev, 0x0814); + backup[7] = b43legacy_phy_read(dev, 0x0815); + backup[8] = b43legacy_phy_read(dev, + B43legacy_PHY_G_CRS); + backup[9] = b43legacy_phy_read(dev, 0x0802); + b43legacy_phy_write(dev, 0x0814, + (b43legacy_phy_read(dev, 0x0814) + | 0x0003)); + b43legacy_phy_write(dev, 0x0815, + (b43legacy_phy_read(dev, 0x0815) + & 0xFFFC)); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, + (b43legacy_phy_read(dev, + B43legacy_PHY_G_CRS) & 0x7FFF)); + b43legacy_phy_write(dev, 0x0802, + (b43legacy_phy_read(dev, 0x0802) + & 0xFFFC)); + if (phy->rev > 1) { /* loopback gain enabled */ + backup[19] = b43legacy_phy_read(dev, 0x080F); + backup[20] = b43legacy_phy_read(dev, 0x0810); + if (phy->rev >= 3) + b43legacy_phy_write(dev, 0x080F, + 0xC020); + else + b43legacy_phy_write(dev, 0x080F, + 0x8020); + b43legacy_phy_write(dev, 0x0810, 0x0000); + } + b43legacy_phy_write(dev, 0x0812, + b43legacy_get_812_value(dev, + LPD(0, 1, 1))); + if (phy->rev < 7 || + !(dev->dev->bus->sprom.boardflags_lo + & B43legacy_BFL_EXTLNA)) + b43legacy_phy_write(dev, 0x0811, 0x01B3); + else + b43legacy_phy_write(dev, 0x0811, 0x09B3); + } + } + b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO, + (b43legacy_read16(dev, B43legacy_MMIO_PHY_RADIO) + | 0x8000)); + backup[10] = b43legacy_phy_read(dev, 0x0035); + b43legacy_phy_write(dev, 0x0035, + (b43legacy_phy_read(dev, 0x0035) & 0xFF7F)); + backup[11] = b43legacy_read16(dev, 0x03E6); + backup[12] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT); + + /* Initialization */ + if (phy->analog == 0) + b43legacy_write16(dev, 0x03E6, 0x0122); + else { + if (phy->analog >= 2) + b43legacy_phy_write(dev, 0x0003, + (b43legacy_phy_read(dev, 0x0003) + & 0xFFBF) | 0x0040); + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, + (b43legacy_read16(dev, + B43legacy_MMIO_CHANNEL_EXT) | 0x2000)); + } + + ret = b43legacy_radio_calibrationvalue(dev); + + if (phy->type == B43legacy_PHYTYPE_B) + b43legacy_radio_write16(dev, 0x0078, 0x0026); + + if (phy->gmode) + b43legacy_phy_write(dev, 0x0812, + b43legacy_get_812_value(dev, + LPD(0, 1, 1))); + b43legacy_phy_write(dev, 0x0015, 0xBFAF); + b43legacy_phy_write(dev, 0x002B, 0x1403); + if (phy->gmode) + b43legacy_phy_write(dev, 0x0812, + b43legacy_get_812_value(dev, + LPD(0, 0, 1))); + b43legacy_phy_write(dev, 0x0015, 0xBFA0); + b43legacy_radio_write16(dev, 0x0051, + (b43legacy_radio_read16(dev, 0x0051) + | 0x0004)); + if (phy->radio_rev == 8) + b43legacy_radio_write16(dev, 0x0043, 0x001F); + else { + b43legacy_radio_write16(dev, 0x0052, 0x0000); + b43legacy_radio_write16(dev, 0x0043, + (b43legacy_radio_read16(dev, 0x0043) + & 0xFFF0) | 0x0009); + } + b43legacy_phy_write(dev, 0x0058, 0x0000); + + for (i = 0; i < 16; i++) { + b43legacy_phy_write(dev, 0x005A, 0x0480); + b43legacy_phy_write(dev, 0x0059, 0xC810); + b43legacy_phy_write(dev, 0x0058, 0x000D); + if (phy->gmode) + b43legacy_phy_write(dev, 0x0812, + b43legacy_get_812_value(dev, + LPD(1, 0, 1))); + b43legacy_phy_write(dev, 0x0015, 0xAFB0); + udelay(10); + if (phy->gmode) + b43legacy_phy_write(dev, 0x0812, + b43legacy_get_812_value(dev, + LPD(1, 0, 1))); + b43legacy_phy_write(dev, 0x0015, 0xEFB0); + udelay(10); + if (phy->gmode) + b43legacy_phy_write(dev, 0x0812, + b43legacy_get_812_value(dev, + LPD(1, 0, 0))); + b43legacy_phy_write(dev, 0x0015, 0xFFF0); + udelay(20); + tmp1 += b43legacy_phy_read(dev, 0x002D); + b43legacy_phy_write(dev, 0x0058, 0x0000); + if (phy->gmode) + b43legacy_phy_write(dev, 0x0812, + b43legacy_get_812_value(dev, + LPD(1, 0, 1))); + b43legacy_phy_write(dev, 0x0015, 0xAFB0); + } + + tmp1++; + tmp1 >>= 9; + udelay(10); + b43legacy_phy_write(dev, 0x0058, 0x0000); + + for (i = 0; i < 16; i++) { + b43legacy_radio_write16(dev, 0x0078, (flip_4bit(i) << 1) + | 0x0020); + backup[13] = b43legacy_radio_read16(dev, 0x0078); + udelay(10); + for (j = 0; j < 16; j++) { + b43legacy_phy_write(dev, 0x005A, 0x0D80); + b43legacy_phy_write(dev, 0x0059, 0xC810); + b43legacy_phy_write(dev, 0x0058, 0x000D); + if (phy->gmode) + b43legacy_phy_write(dev, 0x0812, + b43legacy_get_812_value(dev, + LPD(1, 0, 1))); + b43legacy_phy_write(dev, 0x0015, 0xAFB0); + udelay(10); + if (phy->gmode) + b43legacy_phy_write(dev, 0x0812, + b43legacy_get_812_value(dev, + LPD(1, 0, 1))); + b43legacy_phy_write(dev, 0x0015, 0xEFB0); + udelay(10); + if (phy->gmode) + b43legacy_phy_write(dev, 0x0812, + b43legacy_get_812_value(dev, + LPD(1, 0, 0))); + b43legacy_phy_write(dev, 0x0015, 0xFFF0); + udelay(10); + tmp2 += b43legacy_phy_read(dev, 0x002D); + b43legacy_phy_write(dev, 0x0058, 0x0000); + if (phy->gmode) + b43legacy_phy_write(dev, 0x0812, + b43legacy_get_812_value(dev, + LPD(1, 0, 1))); + b43legacy_phy_write(dev, 0x0015, 0xAFB0); + } + tmp2++; + tmp2 >>= 8; + if (tmp1 < tmp2) + break; + } + + /* Restore the registers */ + b43legacy_phy_write(dev, 0x0015, backup[1]); + b43legacy_radio_write16(dev, 0x0051, backup[14]); + b43legacy_radio_write16(dev, 0x0052, backup[15]); + b43legacy_radio_write16(dev, 0x0043, backup[0]); + b43legacy_phy_write(dev, 0x005A, backup[16]); + b43legacy_phy_write(dev, 0x0059, backup[17]); + b43legacy_phy_write(dev, 0x0058, backup[18]); + b43legacy_write16(dev, 0x03E6, backup[11]); + if (phy->analog != 0) + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, backup[12]); + b43legacy_phy_write(dev, 0x0035, backup[10]); + b43legacy_radio_selectchannel(dev, phy->channel, 1); + if (phy->type == B43legacy_PHYTYPE_B) { + b43legacy_phy_write(dev, 0x0030, backup[2]); + b43legacy_write16(dev, 0x03EC, backup[3]); + } else { + if (phy->gmode) { + b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO, + (b43legacy_read16(dev, + B43legacy_MMIO_PHY_RADIO) & 0x7FFF)); + b43legacy_phy_write(dev, 0x0811, backup[4]); + b43legacy_phy_write(dev, 0x0812, backup[5]); + b43legacy_phy_write(dev, 0x0814, backup[6]); + b43legacy_phy_write(dev, 0x0815, backup[7]); + b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, + backup[8]); + b43legacy_phy_write(dev, 0x0802, backup[9]); + if (phy->rev > 1) { + b43legacy_phy_write(dev, 0x080F, backup[19]); + b43legacy_phy_write(dev, 0x0810, backup[20]); + } + } + } + if (i >= 15) + ret = backup[13]; + + return ret; +} + +static inline +u16 freq_r3A_value(u16 frequency) +{ + u16 value; + + if (frequency < 5091) + value = 0x0040; + else if (frequency < 5321) + value = 0x0000; + else if (frequency < 5806) + value = 0x0080; + else + value = 0x0040; + + return value; +} + +int b43legacy_radio_selectchannel(struct b43legacy_wldev *dev, + u8 channel, + int synthetic_pu_workaround) +{ + struct b43legacy_phy *phy = &dev->phy; + + if (channel == 0xFF) { + switch (phy->type) { + case B43legacy_PHYTYPE_B: + case B43legacy_PHYTYPE_G: + channel = B43legacy_RADIO_DEFAULT_CHANNEL_BG; + break; + default: + B43legacy_WARN_ON(1); + } + } + +/* TODO: Check if channel is valid - return -EINVAL if not */ + if (synthetic_pu_workaround) + b43legacy_synth_pu_workaround(dev, channel); + + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL, + channel2freq_bg(channel)); + + if (channel == 14) { + if (dev->dev->bus->sprom.country_code == 5) /* JAPAN) */ + b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET, + b43legacy_shm_read32(dev, + B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET) + & ~(1 << 7)); + else + b43legacy_shm_write32(dev, B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET, + b43legacy_shm_read32(dev, + B43legacy_SHM_SHARED, + B43legacy_UCODEFLAGS_OFFSET) + | (1 << 7)); + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, + b43legacy_read16(dev, + B43legacy_MMIO_CHANNEL_EXT) | (1 << 11)); + } else + b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, + b43legacy_read16(dev, + B43legacy_MMIO_CHANNEL_EXT) & 0xF7BF); + + phy->channel = channel; + /*XXX: Using the longer of 2 timeouts (8000 vs 2000 usecs). Specs states + * that 2000 usecs might suffice. */ + msleep(8); + + return 0; +} + +void b43legacy_radio_set_txantenna(struct b43legacy_wldev *dev, u32 val) +{ + u16 tmp; + + val <<= 8; + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0022) & 0xFCFF; + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0022, tmp | val); + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x03A8) & 0xFCFF; + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x03A8, tmp | val); + tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0054) & 0xFCFF; + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0054, tmp | val); +} + +/* http://bcm-specs.sipsolutions.net/TX_Gain_Base_Band */ +static u16 b43legacy_get_txgain_base_band(u16 txpower) +{ + u16 ret; + + B43legacy_WARN_ON(txpower > 63); + + if (txpower >= 54) + ret = 2; + else if (txpower >= 49) + ret = 4; + else if (txpower >= 44) + ret = 5; + else + ret = 6; + + return ret; +} + +/* http://bcm-specs.sipsolutions.net/TX_Gain_Radio_Frequency_Power_Amplifier */ +static u16 b43legacy_get_txgain_freq_power_amp(u16 txpower) +{ + u16 ret; + + B43legacy_WARN_ON(txpower > 63); + + if (txpower >= 32) + ret = 0; + else if (txpower >= 25) + ret = 1; + else if (txpower >= 20) + ret = 2; + else if (txpower >= 12) + ret = 3; + else + ret = 4; + + return ret; +} + +/* http://bcm-specs.sipsolutions.net/TX_Gain_Digital_Analog_Converter */ +static u16 b43legacy_get_txgain_dac(u16 txpower) +{ + u16 ret; + + B43legacy_WARN_ON(txpower > 63); + + if (txpower >= 54) + ret = txpower - 53; + else if (txpower >= 49) + ret = txpower - 42; + else if (txpower >= 44) + ret = txpower - 37; + else if (txpower >= 32) + ret = txpower - 32; + else if (txpower >= 25) + ret = txpower - 20; + else if (txpower >= 20) + ret = txpower - 13; + else if (txpower >= 12) + ret = txpower - 8; + else + ret = txpower; + + return ret; +} + +void b43legacy_radio_set_txpower_a(struct b43legacy_wldev *dev, u16 txpower) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 pamp; + u16 base; + u16 dac; + u16 ilt; + + txpower = clamp_val(txpower, 0, 63); + + pamp = b43legacy_get_txgain_freq_power_amp(txpower); + pamp <<= 5; + pamp &= 0x00E0; + b43legacy_phy_write(dev, 0x0019, pamp); + + base = b43legacy_get_txgain_base_band(txpower); + base &= 0x000F; + b43legacy_phy_write(dev, 0x0017, base | 0x0020); + + ilt = b43legacy_ilt_read(dev, 0x3001); + ilt &= 0x0007; + + dac = b43legacy_get_txgain_dac(txpower); + dac <<= 3; + dac |= ilt; + + b43legacy_ilt_write(dev, 0x3001, dac); + + phy->txpwr_offset = txpower; + + /* TODO: FuncPlaceholder (Adjust BB loft cancel) */ +} + +void b43legacy_radio_set_txpower_bg(struct b43legacy_wldev *dev, + u16 baseband_attenuation, + u16 radio_attenuation, + u16 txpower) +{ + struct b43legacy_phy *phy = &dev->phy; + + if (baseband_attenuation == 0xFFFF) + baseband_attenuation = phy->bbatt; + if (radio_attenuation == 0xFFFF) + radio_attenuation = phy->rfatt; + if (txpower == 0xFFFF) + txpower = phy->txctl1; + phy->bbatt = baseband_attenuation; + phy->rfatt = radio_attenuation; + phy->txctl1 = txpower; + + B43legacy_WARN_ON(baseband_attenuation > 11); + if (phy->radio_rev < 6) + B43legacy_WARN_ON(radio_attenuation > 9); + else + B43legacy_WARN_ON(radio_attenuation > 31); + B43legacy_WARN_ON(txpower > 7); + + b43legacy_phy_set_baseband_attenuation(dev, baseband_attenuation); + b43legacy_radio_write16(dev, 0x0043, radio_attenuation); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0064, + radio_attenuation); + if (phy->radio_ver == 0x2050) + b43legacy_radio_write16(dev, 0x0052, + (b43legacy_radio_read16(dev, 0x0052) + & ~0x0070) | ((txpower << 4) & 0x0070)); + /* FIXME: The spec is very weird and unclear here. */ + if (phy->type == B43legacy_PHYTYPE_G) + b43legacy_phy_lo_adjust(dev, 0); +} + +u16 b43legacy_default_baseband_attenuation(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + + if (phy->radio_ver == 0x2050 && phy->radio_rev < 6) + return 0; + return 2; +} + +u16 b43legacy_default_radio_attenuation(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + u16 att = 0xFFFF; + + switch (phy->radio_ver) { + case 0x2053: + switch (phy->radio_rev) { + case 1: + att = 6; + break; + } + break; + case 0x2050: + switch (phy->radio_rev) { + case 0: + att = 5; + break; + case 1: + if (phy->type == B43legacy_PHYTYPE_G) { + if (is_bcm_board_vendor(dev) && + dev->dev->bus->boardinfo.type == 0x421 && + dev->dev->bus->sprom.board_rev >= 30) + att = 3; + else if (is_bcm_board_vendor(dev) && + dev->dev->bus->boardinfo.type == 0x416) + att = 3; + else + att = 1; + } else { + if (is_bcm_board_vendor(dev) && + dev->dev->bus->boardinfo.type == 0x421 && + dev->dev->bus->sprom.board_rev >= 30) + att = 7; + else + att = 6; + } + break; + case 2: + if (phy->type == B43legacy_PHYTYPE_G) { + if (is_bcm_board_vendor(dev) && + dev->dev->bus->boardinfo.type == 0x421 && + dev->dev->bus->sprom.board_rev >= 30) + att = 3; + else if (is_bcm_board_vendor(dev) && + dev->dev->bus->boardinfo.type == + 0x416) + att = 5; + else if (dev->dev->bus->chip_id == 0x4320) + att = 4; + else + att = 3; + } else + att = 6; + break; + case 3: + att = 5; + break; + case 4: + case 5: + att = 1; + break; + case 6: + case 7: + att = 5; + break; + case 8: + att = 0x1A; + break; + case 9: + default: + att = 5; + } + } + if (is_bcm_board_vendor(dev) && + dev->dev->bus->boardinfo.type == 0x421) { + if (dev->dev->bus->sprom.board_rev < 0x43) + att = 2; + else if (dev->dev->bus->sprom.board_rev < 0x51) + att = 3; + } + if (att == 0xFFFF) + att = 5; + + return att; +} + +u16 b43legacy_default_txctl1(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + + if (phy->radio_ver != 0x2050) + return 0; + if (phy->radio_rev == 1) + return 3; + if (phy->radio_rev < 6) + return 2; + if (phy->radio_rev == 8) + return 1; + return 0; +} + +void b43legacy_radio_turn_on(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + int err; + u8 channel; + + might_sleep(); + + if (phy->radio_on) + return; + + switch (phy->type) { + case B43legacy_PHYTYPE_B: + case B43legacy_PHYTYPE_G: + b43legacy_phy_write(dev, 0x0015, 0x8000); + b43legacy_phy_write(dev, 0x0015, 0xCC00); + b43legacy_phy_write(dev, 0x0015, + (phy->gmode ? 0x00C0 : 0x0000)); + if (phy->radio_off_context.valid) { + /* Restore the RFover values. */ + b43legacy_phy_write(dev, B43legacy_PHY_RFOVER, + phy->radio_off_context.rfover); + b43legacy_phy_write(dev, B43legacy_PHY_RFOVERVAL, + phy->radio_off_context.rfoverval); + phy->radio_off_context.valid = false; + } + channel = phy->channel; + err = b43legacy_radio_selectchannel(dev, + B43legacy_RADIO_DEFAULT_CHANNEL_BG, 1); + err |= b43legacy_radio_selectchannel(dev, channel, 0); + B43legacy_WARN_ON(err); + break; + default: + B43legacy_BUG_ON(1); + } + phy->radio_on = true; +} + +void b43legacy_radio_turn_off(struct b43legacy_wldev *dev, bool force) +{ + struct b43legacy_phy *phy = &dev->phy; + + if (!phy->radio_on && !force) + return; + + if (phy->type == B43legacy_PHYTYPE_G && dev->dev->id.revision >= 5) { + u16 rfover, rfoverval; + + rfover = b43legacy_phy_read(dev, B43legacy_PHY_RFOVER); + rfoverval = b43legacy_phy_read(dev, B43legacy_PHY_RFOVERVAL); + if (!force) { + phy->radio_off_context.rfover = rfover; + phy->radio_off_context.rfoverval = rfoverval; + phy->radio_off_context.valid = true; + } + b43legacy_phy_write(dev, B43legacy_PHY_RFOVER, rfover | 0x008C); + b43legacy_phy_write(dev, B43legacy_PHY_RFOVERVAL, + rfoverval & 0xFF73); + } else + b43legacy_phy_write(dev, 0x0015, 0xAA00); + phy->radio_on = false; + b43legacydbg(dev->wl, "Radio initialized\n"); +} + +void b43legacy_radio_clear_tssi(struct b43legacy_wldev *dev) +{ + struct b43legacy_phy *phy = &dev->phy; + + switch (phy->type) { + case B43legacy_PHYTYPE_B: + case B43legacy_PHYTYPE_G: + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0058, + 0x7F7F); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x005a, + 0x7F7F); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0070, + 0x7F7F); + b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0072, + 0x7F7F); + break; + } +} diff --git a/drivers/net/wireless/broadcom/b43legacy/radio.h b/drivers/net/wireless/broadcom/b43legacy/radio.h new file mode 100644 index 000000000000..dd2976d1d561 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/radio.h @@ -0,0 +1,97 @@ +/* + + Broadcom B43legacy wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef B43legacy_RADIO_H_ +#define B43legacy_RADIO_H_ + +#include "b43legacy.h" + + +#define B43legacy_RADIO_DEFAULT_CHANNEL_BG 6 + +/* Force antenna 0. */ +#define B43legacy_RADIO_TXANTENNA_0 0 +/* Force antenna 1. */ +#define B43legacy_RADIO_TXANTENNA_1 1 +/* Use the RX antenna, that was selected for the most recently + * received good PLCP header. + */ +#define B43legacy_RADIO_TXANTENNA_LASTPLCP 3 +#define B43legacy_RADIO_TXANTENNA_DEFAULT B43legacy_RADIO_TXANTENNA_LASTPLCP + +#define B43legacy_RADIO_INTERFMODE_NONE 0 +#define B43legacy_RADIO_INTERFMODE_NONWLAN 1 +#define B43legacy_RADIO_INTERFMODE_MANUALWLAN 2 +#define B43legacy_RADIO_INTERFMODE_AUTOWLAN 3 + + +void b43legacy_radio_lock(struct b43legacy_wldev *dev); +void b43legacy_radio_unlock(struct b43legacy_wldev *dev); + +u16 b43legacy_radio_read16(struct b43legacy_wldev *dev, u16 offset); +void b43legacy_radio_write16(struct b43legacy_wldev *dev, u16 offset, u16 val); + +u16 b43legacy_radio_init2050(struct b43legacy_wldev *dev); + +void b43legacy_radio_turn_on(struct b43legacy_wldev *dev); +void b43legacy_radio_turn_off(struct b43legacy_wldev *dev, bool force); + +int b43legacy_radio_selectchannel(struct b43legacy_wldev *dev, u8 channel, + int synthetic_pu_workaround); + +void b43legacy_radio_set_txpower_a(struct b43legacy_wldev *dev, u16 txpower); +void b43legacy_radio_set_txpower_bg(struct b43legacy_wldev *dev, + u16 baseband_attenuation, u16 attenuation, + u16 txpower); + +u16 b43legacy_default_baseband_attenuation(struct b43legacy_wldev *dev); +u16 b43legacy_default_radio_attenuation(struct b43legacy_wldev *dev); +u16 b43legacy_default_txctl1(struct b43legacy_wldev *dev); + +void b43legacy_radio_set_txantenna(struct b43legacy_wldev *dev, u32 val); + +void b43legacy_radio_clear_tssi(struct b43legacy_wldev *dev); + +u8 b43legacy_radio_aci_detect(struct b43legacy_wldev *dev, u8 channel); +u8 b43legacy_radio_aci_scan(struct b43legacy_wldev *dev); + +int b43legacy_radio_set_interference_mitigation(struct b43legacy_wldev *dev, + int mode); + +void b43legacy_calc_nrssi_slope(struct b43legacy_wldev *dev); +void b43legacy_calc_nrssi_threshold(struct b43legacy_wldev *dev); +s16 b43legacy_nrssi_hw_read(struct b43legacy_wldev *dev, u16 offset); +void b43legacy_nrssi_hw_write(struct b43legacy_wldev *dev, u16 offset, s16 val); +void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val); +void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev); + +u16 b43legacy_radio_calibrationvalue(struct b43legacy_wldev *dev); + +#endif /* B43legacy_RADIO_H_ */ diff --git a/drivers/net/wireless/broadcom/b43legacy/rfkill.c b/drivers/net/wireless/broadcom/b43legacy/rfkill.c new file mode 100644 index 000000000000..7c1bdbc02569 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/rfkill.c @@ -0,0 +1,91 @@ +/* + + Broadcom B43 wireless driver + RFKILL support + + Copyright (c) 2007 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "radio.h" +#include "b43legacy.h" + + +/* Returns TRUE, if the radio is enabled in hardware. */ +bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev) +{ + if (dev->dev->id.revision >= 3) { + if (!(b43legacy_read32(dev, B43legacy_MMIO_RADIO_HWENABLED_HI) + & B43legacy_MMIO_RADIO_HWENABLED_HI_MASK)) + return true; + } else { + /* To prevent CPU fault on PPC, do not read a register + * unless the interface is started; however, on resume + * for hibernation, this routine is entered early. When + * that happens, unconditionally return TRUE. + */ + if (b43legacy_status(dev) < B43legacy_STAT_STARTED) + return true; + if (b43legacy_read16(dev, B43legacy_MMIO_RADIO_HWENABLED_LO) + & B43legacy_MMIO_RADIO_HWENABLED_LO_MASK) + return true; + } + return false; +} + +/* The poll callback for the hardware button. */ +void b43legacy_rfkill_poll(struct ieee80211_hw *hw) +{ + struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); + struct b43legacy_wldev *dev = wl->current_dev; + struct ssb_bus *bus = dev->dev->bus; + bool enabled; + bool brought_up = false; + + mutex_lock(&wl->mutex); + if (unlikely(b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)) { + if (ssb_bus_powerup(bus, 0)) { + mutex_unlock(&wl->mutex); + return; + } + ssb_device_enable(dev->dev, 0); + brought_up = true; + } + + enabled = b43legacy_is_hw_radio_enabled(dev); + + if (unlikely(enabled != dev->radio_hw_enable)) { + dev->radio_hw_enable = enabled; + b43legacyinfo(wl, "Radio hardware status changed to %s\n", + enabled ? "ENABLED" : "DISABLED"); + wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); + if (enabled != dev->phy.radio_on) { + if (enabled) + b43legacy_radio_turn_on(dev); + else + b43legacy_radio_turn_off(dev, 0); + } + } + + if (brought_up) { + ssb_device_disable(dev->dev, 0); + ssb_bus_may_powerdown(bus); + } + + mutex_unlock(&wl->mutex); +} diff --git a/drivers/net/wireless/broadcom/b43legacy/rfkill.h b/drivers/net/wireless/broadcom/b43legacy/rfkill.h new file mode 100644 index 000000000000..75585571c544 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/rfkill.h @@ -0,0 +1,11 @@ +#ifndef B43legacy_RFKILL_H_ +#define B43legacy_RFKILL_H_ + +struct ieee80211_hw; +struct b43legacy_wldev; + +void b43legacy_rfkill_poll(struct ieee80211_hw *hw); + +bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev); + +#endif /* B43legacy_RFKILL_H_ */ diff --git a/drivers/net/wireless/broadcom/b43legacy/sysfs.c b/drivers/net/wireless/broadcom/b43legacy/sysfs.c new file mode 100644 index 000000000000..2a1da15c913b --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/sysfs.c @@ -0,0 +1,238 @@ +/* + + Broadcom B43legacy wireless driver + + SYSFS support routines + + Copyright (c) 2006 Michael Buesch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "sysfs.h" +#include "b43legacy.h" +#include "main.h" +#include "phy.h" +#include "radio.h" + +#include + + +#define GENERIC_FILESIZE 64 + + +static int get_integer(const char *buf, size_t count) +{ + char tmp[10 + 1] = { 0 }; + int ret = -EINVAL; + + if (count == 0) + goto out; + count = min_t(size_t, count, 10); + memcpy(tmp, buf, count); + ret = simple_strtol(tmp, NULL, 10); +out: + return ret; +} + +static int get_boolean(const char *buf, size_t count) +{ + if (count != 0) { + if (buf[0] == '1') + return 1; + if (buf[0] == '0') + return 0; + if (count >= 4 && memcmp(buf, "true", 4) == 0) + return 1; + if (count >= 5 && memcmp(buf, "false", 5) == 0) + return 0; + if (count >= 3 && memcmp(buf, "yes", 3) == 0) + return 1; + if (count >= 2 && memcmp(buf, "no", 2) == 0) + return 0; + if (count >= 2 && memcmp(buf, "on", 2) == 0) + return 1; + if (count >= 3 && memcmp(buf, "off", 3) == 0) + return 0; + } + return -EINVAL; +} + +static ssize_t b43legacy_attr_interfmode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); + ssize_t count = 0; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + mutex_lock(&wldev->wl->mutex); + + switch (wldev->phy.interfmode) { + case B43legacy_INTERFMODE_NONE: + count = snprintf(buf, PAGE_SIZE, "0 (No Interference" + " Mitigation)\n"); + break; + case B43legacy_INTERFMODE_NONWLAN: + count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference" + " Mitigation)\n"); + break; + case B43legacy_INTERFMODE_MANUALWLAN: + count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference" + " Mitigation)\n"); + break; + default: + B43legacy_WARN_ON(1); + } + + mutex_unlock(&wldev->wl->mutex); + + return count; +} + +static ssize_t b43legacy_attr_interfmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); + unsigned long flags; + int err; + int mode; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + mode = get_integer(buf, count); + switch (mode) { + case 0: + mode = B43legacy_INTERFMODE_NONE; + break; + case 1: + mode = B43legacy_INTERFMODE_NONWLAN; + break; + case 2: + mode = B43legacy_INTERFMODE_MANUALWLAN; + break; + case 3: + mode = B43legacy_INTERFMODE_AUTOWLAN; + break; + default: + return -EINVAL; + } + + mutex_lock(&wldev->wl->mutex); + spin_lock_irqsave(&wldev->wl->irq_lock, flags); + + err = b43legacy_radio_set_interference_mitigation(wldev, mode); + if (err) + b43legacyerr(wldev->wl, "Interference Mitigation not " + "supported by device\n"); + mmiowb(); + spin_unlock_irqrestore(&wldev->wl->irq_lock, flags); + mutex_unlock(&wldev->wl->mutex); + + return err ? err : count; +} + +static DEVICE_ATTR(interference, 0644, + b43legacy_attr_interfmode_show, + b43legacy_attr_interfmode_store); + +static ssize_t b43legacy_attr_preamble_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); + ssize_t count; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + mutex_lock(&wldev->wl->mutex); + + if (wldev->short_preamble) + count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble" + " enabled)\n"); + else + count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble" + " disabled)\n"); + + mutex_unlock(&wldev->wl->mutex); + + return count; +} + +static ssize_t b43legacy_attr_preamble_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct b43legacy_wldev *wldev = dev_to_b43legacy_wldev(dev); + unsigned long flags; + int value; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + value = get_boolean(buf, count); + if (value < 0) + return value; + mutex_lock(&wldev->wl->mutex); + spin_lock_irqsave(&wldev->wl->irq_lock, flags); + + wldev->short_preamble = !!value; + + spin_unlock_irqrestore(&wldev->wl->irq_lock, flags); + mutex_unlock(&wldev->wl->mutex); + + return count; +} + +static DEVICE_ATTR(shortpreamble, 0644, + b43legacy_attr_preamble_show, + b43legacy_attr_preamble_store); + +int b43legacy_sysfs_register(struct b43legacy_wldev *wldev) +{ + struct device *dev = wldev->dev->dev; + int err; + + B43legacy_WARN_ON(b43legacy_status(wldev) != + B43legacy_STAT_INITIALIZED); + + err = device_create_file(dev, &dev_attr_interference); + if (err) + goto out; + err = device_create_file(dev, &dev_attr_shortpreamble); + if (err) + goto err_remove_interfmode; + +out: + return err; +err_remove_interfmode: + device_remove_file(dev, &dev_attr_interference); + goto out; +} + +void b43legacy_sysfs_unregister(struct b43legacy_wldev *wldev) +{ + struct device *dev = wldev->dev->dev; + + device_remove_file(dev, &dev_attr_shortpreamble); + device_remove_file(dev, &dev_attr_interference); +} diff --git a/drivers/net/wireless/broadcom/b43legacy/sysfs.h b/drivers/net/wireless/broadcom/b43legacy/sysfs.h new file mode 100644 index 000000000000..417d509803c7 --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/sysfs.h @@ -0,0 +1,9 @@ +#ifndef B43legacy_SYSFS_H_ +#define B43legacy_SYSFS_H_ + +struct b43legacy_wldev; + +int b43legacy_sysfs_register(struct b43legacy_wldev *dev); +void b43legacy_sysfs_unregister(struct b43legacy_wldev *dev); + +#endif /* B43legacy_SYSFS_H_ */ diff --git a/drivers/net/wireless/broadcom/b43legacy/xmit.c b/drivers/net/wireless/broadcom/b43legacy/xmit.c new file mode 100644 index 000000000000..34bf3f0b729f --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/xmit.c @@ -0,0 +1,664 @@ +/* + + Broadcom B43legacy wireless driver + + Transmission (TX/RX) related functions. + + Copyright (C) 2005 Martin Langer + Copyright (C) 2005 Stefano Brivio + Copyright (C) 2005, 2006 Michael Buesch + Copyright (C) 2005 Danny van Dyk + Copyright (C) 2005 Andreas Jaggi + Copyright (C) 2007 Larry Finger + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include + +#include "xmit.h" +#include "phy.h" +#include "dma.h" +#include "pio.h" + + +/* Extract the bitrate out of a CCK PLCP header. */ +static u8 b43legacy_plcp_get_bitrate_idx_cck(struct b43legacy_plcp_hdr6 *plcp) +{ + switch (plcp->raw[0]) { + case 0x0A: + return 0; + case 0x14: + return 1; + case 0x37: + return 2; + case 0x6E: + return 3; + } + B43legacy_BUG_ON(1); + return -1; +} + +/* Extract the bitrate out of an OFDM PLCP header. */ +static u8 b43legacy_plcp_get_bitrate_idx_ofdm(struct b43legacy_plcp_hdr6 *plcp, + bool aphy) +{ + int base = aphy ? 0 : 4; + + switch (plcp->raw[0] & 0xF) { + case 0xB: + return base + 0; + case 0xF: + return base + 1; + case 0xA: + return base + 2; + case 0xE: + return base + 3; + case 0x9: + return base + 4; + case 0xD: + return base + 5; + case 0x8: + return base + 6; + case 0xC: + return base + 7; + } + B43legacy_BUG_ON(1); + return -1; +} + +u8 b43legacy_plcp_get_ratecode_cck(const u8 bitrate) +{ + switch (bitrate) { + case B43legacy_CCK_RATE_1MB: + return 0x0A; + case B43legacy_CCK_RATE_2MB: + return 0x14; + case B43legacy_CCK_RATE_5MB: + return 0x37; + case B43legacy_CCK_RATE_11MB: + return 0x6E; + } + B43legacy_BUG_ON(1); + return 0; +} + +u8 b43legacy_plcp_get_ratecode_ofdm(const u8 bitrate) +{ + switch (bitrate) { + case B43legacy_OFDM_RATE_6MB: + return 0xB; + case B43legacy_OFDM_RATE_9MB: + return 0xF; + case B43legacy_OFDM_RATE_12MB: + return 0xA; + case B43legacy_OFDM_RATE_18MB: + return 0xE; + case B43legacy_OFDM_RATE_24MB: + return 0x9; + case B43legacy_OFDM_RATE_36MB: + return 0xD; + case B43legacy_OFDM_RATE_48MB: + return 0x8; + case B43legacy_OFDM_RATE_54MB: + return 0xC; + } + B43legacy_BUG_ON(1); + return 0; +} + +void b43legacy_generate_plcp_hdr(struct b43legacy_plcp_hdr4 *plcp, + const u16 octets, const u8 bitrate) +{ + __le32 *data = &(plcp->data); + __u8 *raw = plcp->raw; + + if (b43legacy_is_ofdm_rate(bitrate)) { + u16 d; + + d = b43legacy_plcp_get_ratecode_ofdm(bitrate); + B43legacy_WARN_ON(octets & 0xF000); + d |= (octets << 5); + *data = cpu_to_le32(d); + } else { + u32 plen; + + plen = octets * 16 / bitrate; + if ((octets * 16 % bitrate) > 0) { + plen++; + if ((bitrate == B43legacy_CCK_RATE_11MB) + && ((octets * 8 % 11) < 4)) + raw[1] = 0x84; + else + raw[1] = 0x04; + } else + raw[1] = 0x04; + *data |= cpu_to_le32(plen << 16); + raw[0] = b43legacy_plcp_get_ratecode_cck(bitrate); + } +} + +static u8 b43legacy_calc_fallback_rate(u8 bitrate) +{ + switch (bitrate) { + case B43legacy_CCK_RATE_1MB: + return B43legacy_CCK_RATE_1MB; + case B43legacy_CCK_RATE_2MB: + return B43legacy_CCK_RATE_1MB; + case B43legacy_CCK_RATE_5MB: + return B43legacy_CCK_RATE_2MB; + case B43legacy_CCK_RATE_11MB: + return B43legacy_CCK_RATE_5MB; + case B43legacy_OFDM_RATE_6MB: + return B43legacy_CCK_RATE_5MB; + case B43legacy_OFDM_RATE_9MB: + return B43legacy_OFDM_RATE_6MB; + case B43legacy_OFDM_RATE_12MB: + return B43legacy_OFDM_RATE_9MB; + case B43legacy_OFDM_RATE_18MB: + return B43legacy_OFDM_RATE_12MB; + case B43legacy_OFDM_RATE_24MB: + return B43legacy_OFDM_RATE_18MB; + case B43legacy_OFDM_RATE_36MB: + return B43legacy_OFDM_RATE_24MB; + case B43legacy_OFDM_RATE_48MB: + return B43legacy_OFDM_RATE_36MB; + case B43legacy_OFDM_RATE_54MB: + return B43legacy_OFDM_RATE_48MB; + } + B43legacy_BUG_ON(1); + return 0; +} + +static int generate_txhdr_fw3(struct b43legacy_wldev *dev, + struct b43legacy_txhdr_fw3 *txhdr, + const unsigned char *fragment_data, + unsigned int fragment_len, + struct ieee80211_tx_info *info, + u16 cookie) +{ + const struct ieee80211_hdr *wlhdr; + int use_encryption = !!info->control.hw_key; + u8 rate; + struct ieee80211_rate *rate_fb; + int rate_ofdm; + int rate_fb_ofdm; + unsigned int plcp_fragment_len; + u32 mac_ctl = 0; + u16 phy_ctl = 0; + struct ieee80211_rate *tx_rate; + struct ieee80211_tx_rate *rates; + + wlhdr = (const struct ieee80211_hdr *)fragment_data; + + memset(txhdr, 0, sizeof(*txhdr)); + + tx_rate = ieee80211_get_tx_rate(dev->wl->hw, info); + + rate = tx_rate->hw_value; + rate_ofdm = b43legacy_is_ofdm_rate(rate); + rate_fb = ieee80211_get_alt_retry_rate(dev->wl->hw, info, 0) ? : tx_rate; + rate_fb_ofdm = b43legacy_is_ofdm_rate(rate_fb->hw_value); + + txhdr->mac_frame_ctl = wlhdr->frame_control; + memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN); + + /* Calculate duration for fallback rate */ + if ((rate_fb->hw_value == rate) || + (wlhdr->duration_id & cpu_to_le16(0x8000)) || + (wlhdr->duration_id == cpu_to_le16(0))) { + /* If the fallback rate equals the normal rate or the + * dur_id field contains an AID, CFP magic or 0, + * use the original dur_id field. */ + txhdr->dur_fb = wlhdr->duration_id; + } else { + txhdr->dur_fb = ieee80211_generic_frame_duration(dev->wl->hw, + info->control.vif, + info->band, + fragment_len, + rate_fb); + } + + plcp_fragment_len = fragment_len + FCS_LEN; + if (use_encryption) { + u8 key_idx = info->control.hw_key->hw_key_idx; + struct b43legacy_key *key; + int wlhdr_len; + size_t iv_len; + + B43legacy_WARN_ON(key_idx >= dev->max_nr_keys); + key = &(dev->key[key_idx]); + + if (key->enabled) { + /* Hardware appends ICV. */ + plcp_fragment_len += info->control.hw_key->icv_len; + + key_idx = b43legacy_kidx_to_fw(dev, key_idx); + mac_ctl |= (key_idx << B43legacy_TX4_MAC_KEYIDX_SHIFT) & + B43legacy_TX4_MAC_KEYIDX; + mac_ctl |= (key->algorithm << + B43legacy_TX4_MAC_KEYALG_SHIFT) & + B43legacy_TX4_MAC_KEYALG; + wlhdr_len = ieee80211_hdrlen(wlhdr->frame_control); + iv_len = min_t(size_t, info->control.hw_key->iv_len, + ARRAY_SIZE(txhdr->iv)); + memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len); + } else { + /* This key is invalid. This might only happen + * in a short timeframe after machine resume before + * we were able to reconfigure keys. + * Drop this packet completely. Do not transmit it + * unencrypted to avoid leaking information. */ + return -ENOKEY; + } + } + b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) + (&txhdr->plcp), plcp_fragment_len, + rate); + b43legacy_generate_plcp_hdr(&txhdr->plcp_fb, plcp_fragment_len, + rate_fb->hw_value); + + /* PHY TX Control word */ + if (rate_ofdm) + phy_ctl |= B43legacy_TX4_PHY_ENC_OFDM; + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + phy_ctl |= B43legacy_TX4_PHY_SHORTPRMBL; + phy_ctl |= B43legacy_TX4_PHY_ANTLAST; + + /* MAC control */ + rates = info->control.rates; + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + mac_ctl |= B43legacy_TX4_MAC_ACK; + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + mac_ctl |= B43legacy_TX4_MAC_HWSEQ; + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + mac_ctl |= B43legacy_TX4_MAC_STMSDU; + if (rate_fb_ofdm) + mac_ctl |= B43legacy_TX4_MAC_FALLBACKOFDM; + + /* Overwrite rates[0].count to make the retry calculation + * in the tx status easier. need the actual retry limit to + * detect whether the fallback rate was used. + */ + if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || + (rates[0].count <= dev->wl->hw->conf.long_frame_max_tx_count)) { + rates[0].count = dev->wl->hw->conf.long_frame_max_tx_count; + mac_ctl |= B43legacy_TX4_MAC_LONGFRAME; + } else { + rates[0].count = dev->wl->hw->conf.short_frame_max_tx_count; + } + + /* Generate the RTS or CTS-to-self frame */ + if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || + (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) { + unsigned int len; + struct ieee80211_hdr *hdr; + int rts_rate; + int rts_rate_fb; + int rts_rate_fb_ofdm; + + rts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info)->hw_value; + rts_rate_fb = b43legacy_calc_fallback_rate(rts_rate); + rts_rate_fb_ofdm = b43legacy_is_ofdm_rate(rts_rate_fb); + if (rts_rate_fb_ofdm) + mac_ctl |= B43legacy_TX4_MAC_CTSFALLBACKOFDM; + + if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + ieee80211_ctstoself_get(dev->wl->hw, + info->control.vif, + fragment_data, + fragment_len, info, + (struct ieee80211_cts *) + (txhdr->rts_frame)); + mac_ctl |= B43legacy_TX4_MAC_SENDCTS; + len = sizeof(struct ieee80211_cts); + } else { + ieee80211_rts_get(dev->wl->hw, + info->control.vif, + fragment_data, fragment_len, info, + (struct ieee80211_rts *) + (txhdr->rts_frame)); + mac_ctl |= B43legacy_TX4_MAC_SENDRTS; + len = sizeof(struct ieee80211_rts); + } + len += FCS_LEN; + b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) + (&txhdr->rts_plcp), + len, rts_rate); + b43legacy_generate_plcp_hdr(&txhdr->rts_plcp_fb, + len, rts_rate_fb); + hdr = (struct ieee80211_hdr *)(&txhdr->rts_frame); + txhdr->rts_dur_fb = hdr->duration_id; + } + + /* Magic cookie */ + txhdr->cookie = cpu_to_le16(cookie); + + /* Apply the bitfields */ + txhdr->mac_ctl = cpu_to_le32(mac_ctl); + txhdr->phy_ctl = cpu_to_le16(phy_ctl); + + return 0; +} + +int b43legacy_generate_txhdr(struct b43legacy_wldev *dev, + u8 *txhdr, + const unsigned char *fragment_data, + unsigned int fragment_len, + struct ieee80211_tx_info *info, + u16 cookie) +{ + return generate_txhdr_fw3(dev, (struct b43legacy_txhdr_fw3 *)txhdr, + fragment_data, fragment_len, + info, cookie); +} + +static s8 b43legacy_rssi_postprocess(struct b43legacy_wldev *dev, + u8 in_rssi, int ofdm, + int adjust_2053, int adjust_2050) +{ + struct b43legacy_phy *phy = &dev->phy; + s32 tmp; + + switch (phy->radio_ver) { + case 0x2050: + if (ofdm) { + tmp = in_rssi; + if (tmp > 127) + tmp -= 256; + tmp *= 73; + tmp /= 64; + if (adjust_2050) + tmp += 25; + else + tmp -= 3; + } else { + if (dev->dev->bus->sprom.boardflags_lo + & B43legacy_BFL_RSSI) { + if (in_rssi > 63) + in_rssi = 63; + tmp = phy->nrssi_lt[in_rssi]; + tmp = 31 - tmp; + tmp *= -131; + tmp /= 128; + tmp -= 57; + } else { + tmp = in_rssi; + tmp = 31 - tmp; + tmp *= -149; + tmp /= 128; + tmp -= 68; + } + if (phy->type == B43legacy_PHYTYPE_G && + adjust_2050) + tmp += 25; + } + break; + case 0x2060: + if (in_rssi > 127) + tmp = in_rssi - 256; + else + tmp = in_rssi; + break; + default: + tmp = in_rssi; + tmp -= 11; + tmp *= 103; + tmp /= 64; + if (adjust_2053) + tmp -= 109; + else + tmp -= 83; + } + + return (s8)tmp; +} + +void b43legacy_rx(struct b43legacy_wldev *dev, + struct sk_buff *skb, + const void *_rxhdr) +{ + struct ieee80211_rx_status status; + struct b43legacy_plcp_hdr6 *plcp; + struct ieee80211_hdr *wlhdr; + const struct b43legacy_rxhdr_fw3 *rxhdr = _rxhdr; + __le16 fctl; + u16 phystat0; + u16 phystat3; + u16 chanstat; + u16 mactime; + u32 macstat; + u16 chanid; + u8 jssi; + int padding; + + memset(&status, 0, sizeof(status)); + + /* Get metadata about the frame from the header. */ + phystat0 = le16_to_cpu(rxhdr->phy_status0); + phystat3 = le16_to_cpu(rxhdr->phy_status3); + jssi = rxhdr->jssi; + macstat = le16_to_cpu(rxhdr->mac_status); + mactime = le16_to_cpu(rxhdr->mac_time); + chanstat = le16_to_cpu(rxhdr->channel); + + if (macstat & B43legacy_RX_MAC_FCSERR) + dev->wl->ieee_stats.dot11FCSErrorCount++; + + /* Skip PLCP and padding */ + padding = (macstat & B43legacy_RX_MAC_PADDING) ? 2 : 0; + if (unlikely(skb->len < (sizeof(struct b43legacy_plcp_hdr6) + + padding))) { + b43legacydbg(dev->wl, "RX: Packet size underrun (1)\n"); + goto drop; + } + plcp = (struct b43legacy_plcp_hdr6 *)(skb->data + padding); + skb_pull(skb, sizeof(struct b43legacy_plcp_hdr6) + padding); + /* The skb contains the Wireless Header + payload data now */ + if (unlikely(skb->len < (2+2+6/*minimum hdr*/ + FCS_LEN))) { + b43legacydbg(dev->wl, "RX: Packet size underrun (2)\n"); + goto drop; + } + wlhdr = (struct ieee80211_hdr *)(skb->data); + fctl = wlhdr->frame_control; + + if ((macstat & B43legacy_RX_MAC_DEC) && + !(macstat & B43legacy_RX_MAC_DECERR)) { + unsigned int keyidx; + int wlhdr_len; + int iv_len; + int icv_len; + + keyidx = ((macstat & B43legacy_RX_MAC_KEYIDX) + >> B43legacy_RX_MAC_KEYIDX_SHIFT); + /* We must adjust the key index here. We want the "physical" + * key index, but the ucode passed it slightly different. + */ + keyidx = b43legacy_kidx_to_raw(dev, keyidx); + B43legacy_WARN_ON(keyidx >= dev->max_nr_keys); + + if (dev->key[keyidx].algorithm != B43legacy_SEC_ALGO_NONE) { + /* Remove PROTECTED flag to mark it as decrypted. */ + B43legacy_WARN_ON(!ieee80211_has_protected(fctl)); + fctl &= ~cpu_to_le16(IEEE80211_FCTL_PROTECTED); + wlhdr->frame_control = fctl; + + wlhdr_len = ieee80211_hdrlen(fctl); + if (unlikely(skb->len < (wlhdr_len + 3))) { + b43legacydbg(dev->wl, "RX: Packet size" + " underrun3\n"); + goto drop; + } + if (skb->data[wlhdr_len + 3] & (1 << 5)) { + /* The Ext-IV Bit is set in the "KeyID" + * octet of the IV. + */ + iv_len = 8; + icv_len = 8; + } else { + iv_len = 4; + icv_len = 4; + } + if (unlikely(skb->len < (wlhdr_len + iv_len + + icv_len))) { + b43legacydbg(dev->wl, "RX: Packet size" + " underrun4\n"); + goto drop; + } + /* Remove the IV */ + memmove(skb->data + iv_len, skb->data, wlhdr_len); + skb_pull(skb, iv_len); + /* Remove the ICV */ + skb_trim(skb, skb->len - icv_len); + + status.flag |= RX_FLAG_DECRYPTED; + } + } + + status.signal = b43legacy_rssi_postprocess(dev, jssi, + (phystat0 & B43legacy_RX_PHYST0_OFDM), + (phystat0 & B43legacy_RX_PHYST0_GAINCTL), + (phystat3 & B43legacy_RX_PHYST3_TRSTATE)); + /* change to support A PHY */ + if (phystat0 & B43legacy_RX_PHYST0_OFDM) + status.rate_idx = b43legacy_plcp_get_bitrate_idx_ofdm(plcp, false); + else + status.rate_idx = b43legacy_plcp_get_bitrate_idx_cck(plcp); + status.antenna = !!(phystat0 & B43legacy_RX_PHYST0_ANT); + + /* + * All frames on monitor interfaces and beacons always need a full + * 64-bit timestamp. Monitor interfaces need it for diagnostic + * purposes and beacons for IBSS merging. + * This code assumes we get to process the packet within 16 bits + * of timestamp, i.e. about 65 milliseconds after the PHY received + * the first symbol. + */ + if (ieee80211_is_beacon(fctl) || dev->wl->radiotap_enabled) { + u16 low_mactime_now; + + b43legacy_tsf_read(dev, &status.mactime); + low_mactime_now = status.mactime; + status.mactime = status.mactime & ~0xFFFFULL; + status.mactime += mactime; + if (low_mactime_now <= mactime) + status.mactime -= 0x10000; + status.flag |= RX_FLAG_MACTIME_START; + } + + chanid = (chanstat & B43legacy_RX_CHAN_ID) >> + B43legacy_RX_CHAN_ID_SHIFT; + switch (chanstat & B43legacy_RX_CHAN_PHYTYPE) { + case B43legacy_PHYTYPE_B: + case B43legacy_PHYTYPE_G: + status.band = IEEE80211_BAND_2GHZ; + status.freq = chanid + 2400; + break; + default: + b43legacywarn(dev->wl, "Unexpected value for chanstat (0x%X)\n", + chanstat); + } + + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + ieee80211_rx_irqsafe(dev->wl->hw, skb); + + return; +drop: + b43legacydbg(dev->wl, "RX: Packet dropped\n"); + dev_kfree_skb_any(skb); +} + +void b43legacy_handle_txstatus(struct b43legacy_wldev *dev, + const struct b43legacy_txstatus *status) +{ + b43legacy_debugfs_log_txstat(dev, status); + + if (status->intermediate) + return; + if (status->for_ampdu) + return; + if (!status->acked) + dev->wl->ieee_stats.dot11ACKFailureCount++; + if (status->rts_count) { + if (status->rts_count == 0xF) /* FIXME */ + dev->wl->ieee_stats.dot11RTSFailureCount++; + else + dev->wl->ieee_stats.dot11RTSSuccessCount++; + } + + if (b43legacy_using_pio(dev)) + b43legacy_pio_handle_txstatus(dev, status); + else + b43legacy_dma_handle_txstatus(dev, status); +} + +/* Handle TX status report as received through DMA/PIO queues */ +void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev, + const struct b43legacy_hwtxstatus *hw) +{ + struct b43legacy_txstatus status; + u8 tmp; + + status.cookie = le16_to_cpu(hw->cookie); + status.seq = le16_to_cpu(hw->seq); + status.phy_stat = hw->phy_stat; + tmp = hw->count; + status.frame_count = (tmp >> 4); + status.rts_count = (tmp & 0x0F); + tmp = hw->flags << 1; + status.supp_reason = ((tmp & 0x1C) >> 2); + status.pm_indicated = !!(tmp & 0x80); + status.intermediate = !!(tmp & 0x40); + status.for_ampdu = !!(tmp & 0x20); + status.acked = !!(tmp & 0x02); + + b43legacy_handle_txstatus(dev, &status); +} + +/* Stop any TX operation on the device (suspend the hardware queues) */ +void b43legacy_tx_suspend(struct b43legacy_wldev *dev) +{ + if (b43legacy_using_pio(dev)) + b43legacy_pio_freeze_txqueues(dev); + else + b43legacy_dma_tx_suspend(dev); +} + +/* Resume any TX operation on the device (resume the hardware queues) */ +void b43legacy_tx_resume(struct b43legacy_wldev *dev) +{ + if (b43legacy_using_pio(dev)) + b43legacy_pio_thaw_txqueues(dev); + else + b43legacy_dma_tx_resume(dev); +} + +/* Initialize the QoS parameters */ +void b43legacy_qos_init(struct b43legacy_wldev *dev) +{ + /* FIXME: This function must probably be called from the mac80211 + * config callback. */ +return; + + b43legacy_hf_write(dev, b43legacy_hf_read(dev) | B43legacy_HF_EDCF); + /* FIXME kill magic */ + b43legacy_write16(dev, 0x688, + b43legacy_read16(dev, 0x688) | 0x4); + + + /*TODO: We might need some stack support here to get the values. */ +} diff --git a/drivers/net/wireless/broadcom/b43legacy/xmit.h b/drivers/net/wireless/broadcom/b43legacy/xmit.h new file mode 100644 index 000000000000..289db00a4a7b --- /dev/null +++ b/drivers/net/wireless/broadcom/b43legacy/xmit.h @@ -0,0 +1,261 @@ +#ifndef B43legacy_XMIT_H_ +#define B43legacy_XMIT_H_ + +#include "main.h" + + +#define _b43legacy_declare_plcp_hdr(size) \ + struct b43legacy_plcp_hdr##size { \ + union { \ + __le32 data; \ + __u8 raw[size]; \ + } __packed; \ + } __packed + +/* struct b43legacy_plcp_hdr4 */ +_b43legacy_declare_plcp_hdr(4); +/* struct b43legacy_plcp_hdr6 */ +_b43legacy_declare_plcp_hdr(6); + +#undef _b43legacy_declare_plcp_hdr + + +/* TX header for v3 firmware */ +struct b43legacy_txhdr_fw3 { + __le32 mac_ctl; /* MAC TX control */ + __le16 mac_frame_ctl; /* Copy of the FrameControl */ + __le16 tx_fes_time_norm; /* TX FES Time Normal */ + __le16 phy_ctl; /* PHY TX control */ + __u8 iv[16]; /* Encryption IV */ + __u8 tx_receiver[6]; /* TX Frame Receiver address */ + __le16 tx_fes_time_fb; /* TX FES Time Fallback */ + struct b43legacy_plcp_hdr4 rts_plcp_fb; /* RTS fallback PLCP */ + __le16 rts_dur_fb; /* RTS fallback duration */ + struct b43legacy_plcp_hdr4 plcp_fb; /* Fallback PLCP */ + __le16 dur_fb; /* Fallback duration */ + PAD_BYTES(2); + __le16 cookie; + __le16 unknown_scb_stuff; + struct b43legacy_plcp_hdr6 rts_plcp; /* RTS PLCP */ + __u8 rts_frame[18]; /* The RTS frame (if used) */ + struct b43legacy_plcp_hdr6 plcp; +} __packed; + +/* MAC TX control */ +#define B43legacy_TX4_MAC_KEYIDX 0x0FF00000 /* Security key index */ +#define B43legacy_TX4_MAC_KEYIDX_SHIFT 20 +#define B43legacy_TX4_MAC_KEYALG 0x00070000 /* Security key algorithm */ +#define B43legacy_TX4_MAC_KEYALG_SHIFT 16 +#define B43legacy_TX4_MAC_LIFETIME 0x00001000 +#define B43legacy_TX4_MAC_FRAMEBURST 0x00000800 +#define B43legacy_TX4_MAC_SENDCTS 0x00000400 +#define B43legacy_TX4_MAC_AMPDU 0x00000300 +#define B43legacy_TX4_MAC_AMPDU_SHIFT 8 +#define B43legacy_TX4_MAC_CTSFALLBACKOFDM 0x00000200 +#define B43legacy_TX4_MAC_FALLBACKOFDM 0x00000100 +#define B43legacy_TX4_MAC_5GHZ 0x00000080 +#define B43legacy_TX4_MAC_IGNPMQ 0x00000020 +#define B43legacy_TX4_MAC_HWSEQ 0x00000010 /* Use Hardware Seq No */ +#define B43legacy_TX4_MAC_STMSDU 0x00000008 /* Start MSDU */ +#define B43legacy_TX4_MAC_SENDRTS 0x00000004 +#define B43legacy_TX4_MAC_LONGFRAME 0x00000002 +#define B43legacy_TX4_MAC_ACK 0x00000001 + +/* Extra Frame Types */ +#define B43legacy_TX4_EFT_FBOFDM 0x0001 /* Data frame fb rate type */ +#define B43legacy_TX4_EFT_RTSOFDM 0x0004 /* RTS/CTS rate type */ +#define B43legacy_TX4_EFT_RTSFBOFDM 0x0010 /* RTS/CTS fallback rate type */ + +/* PHY TX control word */ +#define B43legacy_TX4_PHY_ENC 0x0003 /* Data frame encoding */ +#define B43legacy_TX4_PHY_ENC_CCK 0x0000 /* CCK */ +#define B43legacy_TX4_PHY_ENC_OFDM 0x0001 /* Data frame rate type */ +#define B43legacy_TX4_PHY_SHORTPRMBL 0x0010 /* Use short preamble */ +#define B43legacy_TX4_PHY_ANT 0x03C0 /* Antenna selection */ +#define B43legacy_TX4_PHY_ANT0 0x0000 /* Use antenna 0 */ +#define B43legacy_TX4_PHY_ANT1 0x0100 /* Use antenna 1 */ +#define B43legacy_TX4_PHY_ANTLAST 0x0300 /* Use last used antenna */ + + + +int b43legacy_generate_txhdr(struct b43legacy_wldev *dev, + u8 *txhdr, + const unsigned char *fragment_data, + unsigned int fragment_len, + struct ieee80211_tx_info *info, + u16 cookie); + + +/* Transmit Status */ +struct b43legacy_txstatus { + u16 cookie; /* The cookie from the txhdr */ + u16 seq; /* Sequence number */ + u8 phy_stat; /* PHY TX status */ + u8 frame_count; /* Frame transmit count */ + u8 rts_count; /* RTS transmit count */ + u8 supp_reason; /* Suppression reason */ + /* flags */ + u8 pm_indicated;/* PM mode indicated to AP */ + u8 intermediate;/* Intermediate status notification */ + u8 for_ampdu; /* Status is for an AMPDU (afterburner) */ + u8 acked; /* Wireless ACK received */ +}; + +/* txstatus supp_reason values */ +enum { + B43legacy_TXST_SUPP_NONE, /* Not suppressed */ + B43legacy_TXST_SUPP_PMQ, /* Suppressed due to PMQ entry */ + B43legacy_TXST_SUPP_FLUSH, /* Suppressed due to flush request */ + B43legacy_TXST_SUPP_PREV, /* Previous fragment failed */ + B43legacy_TXST_SUPP_CHAN, /* Channel mismatch */ + B43legacy_TXST_SUPP_LIFE, /* Lifetime expired */ + B43legacy_TXST_SUPP_UNDER, /* Buffer underflow */ + B43legacy_TXST_SUPP_ABNACK, /* Afterburner NACK */ +}; + +/* Transmit Status as received through DMA/PIO on old chips */ +struct b43legacy_hwtxstatus { + PAD_BYTES(4); + __le16 cookie; + u8 flags; + u8 count; + PAD_BYTES(2); + __le16 seq; + u8 phy_stat; + PAD_BYTES(1); +} __packed; + + +/* Receive header for v3 firmware. */ +struct b43legacy_rxhdr_fw3 { + __le16 frame_len; /* Frame length */ + PAD_BYTES(2); + __le16 phy_status0; /* PHY RX Status 0 */ + __u8 jssi; /* PHY RX Status 1: JSSI */ + __u8 sig_qual; /* PHY RX Status 1: Signal Quality */ + PAD_BYTES(2); /* PHY RX Status 2 */ + __le16 phy_status3; /* PHY RX Status 3 */ + __le16 mac_status; /* MAC RX status */ + __le16 mac_time; + __le16 channel; +} __packed; + + +/* PHY RX Status 0 */ +#define B43legacy_RX_PHYST0_GAINCTL 0x4000 /* Gain Control */ +#define B43legacy_RX_PHYST0_PLCPHCF 0x0200 +#define B43legacy_RX_PHYST0_PLCPFV 0x0100 +#define B43legacy_RX_PHYST0_SHORTPRMBL 0x0080 /* Recvd with Short Preamble */ +#define B43legacy_RX_PHYST0_LCRS 0x0040 +#define B43legacy_RX_PHYST0_ANT 0x0020 /* Antenna */ +#define B43legacy_RX_PHYST0_UNSRATE 0x0010 +#define B43legacy_RX_PHYST0_CLIP 0x000C +#define B43legacy_RX_PHYST0_CLIP_SHIFT 2 +#define B43legacy_RX_PHYST0_FTYPE 0x0003 /* Frame type */ +#define B43legacy_RX_PHYST0_CCK 0x0000 /* Frame type: CCK */ +#define B43legacy_RX_PHYST0_OFDM 0x0001 /* Frame type: OFDM */ +#define B43legacy_RX_PHYST0_PRE_N 0x0002 /* Pre-standard N-PHY frame */ +#define B43legacy_RX_PHYST0_STD_N 0x0003 /* Standard N-PHY frame */ + +/* PHY RX Status 2 */ +#define B43legacy_RX_PHYST2_LNAG 0xC000 /* LNA Gain */ +#define B43legacy_RX_PHYST2_LNAG_SHIFT 14 +#define B43legacy_RX_PHYST2_PNAG 0x3C00 /* PNA Gain */ +#define B43legacy_RX_PHYST2_PNAG_SHIFT 10 +#define B43legacy_RX_PHYST2_FOFF 0x03FF /* F offset */ + +/* PHY RX Status 3 */ +#define B43legacy_RX_PHYST3_DIGG 0x1800 /* DIG Gain */ +#define B43legacy_RX_PHYST3_DIGG_SHIFT 11 +#define B43legacy_RX_PHYST3_TRSTATE 0x0400 /* TR state */ + +/* MAC RX Status */ +#define B43legacy_RX_MAC_BEACONSENT 0x00008000 /* Beacon send flag */ +#define B43legacy_RX_MAC_KEYIDX 0x000007E0 /* Key index */ +#define B43legacy_RX_MAC_KEYIDX_SHIFT 5 +#define B43legacy_RX_MAC_DECERR 0x00000010 /* Decrypt error */ +#define B43legacy_RX_MAC_DEC 0x00000008 /* Decryption attempted */ +#define B43legacy_RX_MAC_PADDING 0x00000004 /* Pad bytes present */ +#define B43legacy_RX_MAC_RESP 0x00000002 /* Response frame xmitted */ +#define B43legacy_RX_MAC_FCSERR 0x00000001 /* FCS error */ + +/* RX channel */ +#define B43legacy_RX_CHAN_GAIN 0xFC00 /* Gain */ +#define B43legacy_RX_CHAN_GAIN_SHIFT 10 +#define B43legacy_RX_CHAN_ID 0x03FC /* Channel ID */ +#define B43legacy_RX_CHAN_ID_SHIFT 2 +#define B43legacy_RX_CHAN_PHYTYPE 0x0003 /* PHY type */ + + + +u8 b43legacy_plcp_get_ratecode_cck(const u8 bitrate); +u8 b43legacy_plcp_get_ratecode_ofdm(const u8 bitrate); + +void b43legacy_generate_plcp_hdr(struct b43legacy_plcp_hdr4 *plcp, + const u16 octets, const u8 bitrate); + +void b43legacy_rx(struct b43legacy_wldev *dev, + struct sk_buff *skb, + const void *_rxhdr); + +void b43legacy_handle_txstatus(struct b43legacy_wldev *dev, + const struct b43legacy_txstatus *status); + +void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev, + const struct b43legacy_hwtxstatus *hw); + +void b43legacy_tx_suspend(struct b43legacy_wldev *dev); +void b43legacy_tx_resume(struct b43legacy_wldev *dev); + + +#define B43legacy_NR_QOSPARMS 22 +enum { + B43legacy_QOSPARM_TXOP = 0, + B43legacy_QOSPARM_CWMIN, + B43legacy_QOSPARM_CWMAX, + B43legacy_QOSPARM_CWCUR, + B43legacy_QOSPARM_AIFS, + B43legacy_QOSPARM_BSLOTS, + B43legacy_QOSPARM_REGGAP, + B43legacy_QOSPARM_STATUS, +}; + +void b43legacy_qos_init(struct b43legacy_wldev *dev); + + +/* Helper functions for converting the key-table index from "firmware-format" + * to "raw-format" and back. The firmware API changed for this at some revision. + * We need to account for that here. */ +static inline +int b43legacy_new_kidx_api(struct b43legacy_wldev *dev) +{ + /* FIXME: Not sure the change was at rev 351 */ + return (dev->fw.rev >= 351); +} +static inline +u8 b43legacy_kidx_to_fw(struct b43legacy_wldev *dev, u8 raw_kidx) +{ + u8 firmware_kidx; + if (b43legacy_new_kidx_api(dev)) + firmware_kidx = raw_kidx; + else { + if (raw_kidx >= 4) /* Is per STA key? */ + firmware_kidx = raw_kidx - 4; + else + firmware_kidx = raw_kidx; /* TX default key */ + } + return firmware_kidx; +} +static inline +u8 b43legacy_kidx_to_raw(struct b43legacy_wldev *dev, u8 firmware_kidx) +{ + u8 raw_kidx; + if (b43legacy_new_kidx_api(dev)) + raw_kidx = firmware_kidx; + else + /* RX default keys or per STA keys */ + raw_kidx = firmware_kidx + 4; + return raw_kidx; +} + +#endif /* B43legacy_XMIT_H_ */ -- cgit v1.2.3-59-g8ed1b From 05491d2ccf20b20a1375303441fbbfbd12b24a4f Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 19:52:05 +0200 Subject: brcm80211: move under broadcom vendor directory Part of reorganising wireless drivers directory and Kconfig. Note that I had to edit Makefiles from subdirectories to use the new location. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 1 - drivers/net/wireless/Makefile | 3 - drivers/net/wireless/brcm80211/Kconfig | 87 - drivers/net/wireless/brcm80211/Makefile | 23 - drivers/net/wireless/brcm80211/brcmfmac/Makefile | 57 - drivers/net/wireless/brcm80211/brcmfmac/bcdc.c | 389 - drivers/net/wireless/brcm80211/brcmfmac/bcdc.h | 27 - drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 1380 - drivers/net/wireless/brcm80211/brcmfmac/btcoex.c | 497 - drivers/net/wireless/brcm80211/brcmfmac/btcoex.h | 29 - drivers/net/wireless/brcm80211/brcmfmac/bus.h | 248 - drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 6357 ---- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h | 504 - drivers/net/wireless/brcm80211/brcmfmac/chip.c | 1331 - drivers/net/wireless/brcm80211/brcmfmac/chip.h | 94 - drivers/net/wireless/brcm80211/brcmfmac/common.c | 198 - drivers/net/wireless/brcm80211/brcmfmac/common.h | 23 - .../net/wireless/brcm80211/brcmfmac/commonring.c | 252 - .../net/wireless/brcm80211/brcmfmac/commonring.h | 72 - drivers/net/wireless/brcm80211/brcmfmac/core.c | 1260 - drivers/net/wireless/brcm80211/brcmfmac/core.h | 221 - drivers/net/wireless/brcm80211/brcmfmac/debug.c | 111 - drivers/net/wireless/brcm80211/brcmfmac/debug.h | 139 - drivers/net/wireless/brcm80211/brcmfmac/feature.c | 172 - drivers/net/wireless/brcm80211/brcmfmac/feature.h | 94 - drivers/net/wireless/brcm80211/brcmfmac/firmware.c | 542 - drivers/net/wireless/brcm80211/brcmfmac/firmware.h | 47 - drivers/net/wireless/brcm80211/brcmfmac/flowring.c | 504 - drivers/net/wireless/brcm80211/brcmfmac/flowring.h | 84 - drivers/net/wireless/brcm80211/brcmfmac/fweh.c | 478 - drivers/net/wireless/brcm80211/brcmfmac/fweh.h | 289 - drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 420 - drivers/net/wireless/brcm80211/brcmfmac/fwil.h | 106 - .../net/wireless/brcm80211/brcmfmac/fwil_types.h | 637 - drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2271 -- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h | 33 - drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 1561 - drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h | 47 - drivers/net/wireless/brcm80211/brcmfmac/of.c | 59 - drivers/net/wireless/brcm80211/brcmfmac/of.h | 22 - drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 2401 -- drivers/net/wireless/brcm80211/brcmfmac/p2p.h | 187 - drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 2107 -- drivers/net/wireless/brcm80211/brcmfmac/pcie.h | 29 - drivers/net/wireless/brcm80211/brcmfmac/proto.c | 81 - drivers/net/wireless/brcm80211/brcmfmac/proto.h | 96 - drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 4376 --- drivers/net/wireless/brcm80211/brcmfmac/sdio.h | 377 - .../net/wireless/brcm80211/brcmfmac/tracepoint.c | 37 - .../net/wireless/brcm80211/brcmfmac/tracepoint.h | 152 - drivers/net/wireless/brcm80211/brcmfmac/usb.c | 1535 - drivers/net/wireless/brcm80211/brcmfmac/usb.h | 53 - drivers/net/wireless/brcm80211/brcmfmac/vendor.c | 127 - drivers/net/wireless/brcm80211/brcmfmac/vendor.h | 64 - drivers/net/wireless/brcm80211/brcmsmac/Makefile | 48 - drivers/net/wireless/brcm80211/brcmsmac/aiutils.c | 710 - drivers/net/wireless/brcm80211/brcmsmac/aiutils.h | 229 - drivers/net/wireless/brcm80211/brcmsmac/ampdu.c | 1144 - drivers/net/wireless/brcm80211/brcmsmac/ampdu.h | 53 - drivers/net/wireless/brcm80211/brcmsmac/antsel.c | 309 - drivers/net/wireless/brcm80211/brcmsmac/antsel.h | 27 - .../brcm80211/brcmsmac/brcms_trace_brcmsmac.h | 102 - .../brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h | 88 - .../brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h | 110 - .../brcm80211/brcmsmac/brcms_trace_events.c | 23 - .../brcm80211/brcmsmac/brcms_trace_events.h | 40 - drivers/net/wireless/brcm80211/brcmsmac/channel.c | 774 - drivers/net/wireless/brcm80211/brcmsmac/channel.h | 47 - drivers/net/wireless/brcm80211/brcmsmac/d11.h | 1902 -- drivers/net/wireless/brcm80211/brcmsmac/debug.c | 270 - drivers/net/wireless/brcm80211/brcmsmac/debug.h | 76 - drivers/net/wireless/brcm80211/brcmsmac/dma.c | 1564 - drivers/net/wireless/brcm80211/brcmsmac/dma.h | 125 - drivers/net/wireless/brcm80211/brcmsmac/led.c | 126 - drivers/net/wireless/brcm80211/brcmsmac/led.h | 36 - .../net/wireless/brcm80211/brcmsmac/mac80211_if.c | 1700 -- .../net/wireless/brcm80211/brcmsmac/mac80211_if.h | 113 - drivers/net/wireless/brcm80211/brcmsmac/main.c | 8139 ------ drivers/net/wireless/brcm80211/brcmsmac/main.h | 669 - .../net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c | 2953 -- .../net/wireless/brcm80211/brcmsmac/phy/phy_hal.h | 284 - .../net/wireless/brcm80211/brcmsmac/phy/phy_int.h | 1142 - .../net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c | 5247 ---- .../net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h | 121 - .../net/wireless/brcm80211/brcmsmac/phy/phy_n.c | 28721 ------------------- .../wireless/brcm80211/brcmsmac/phy/phy_qmath.c | 308 - .../wireless/brcm80211/brcmsmac/phy/phy_qmath.h | 42 - .../wireless/brcm80211/brcmsmac/phy/phy_radio.h | 1533 - .../net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h | 167 - .../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c | 3293 --- .../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h | 55 - .../net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c | 10630 ------- .../net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h | 50 - drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c | 216 - drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h | 172 - drivers/net/wireless/brcm80211/brcmsmac/pmu.c | 165 - drivers/net/wireless/brcm80211/brcmsmac/pmu.h | 26 - drivers/net/wireless/brcm80211/brcmsmac/pub.h | 341 - drivers/net/wireless/brcm80211/brcmsmac/rate.c | 514 - drivers/net/wireless/brcm80211/brcmsmac/rate.h | 245 - drivers/net/wireless/brcm80211/brcmsmac/scb.h | 81 - drivers/net/wireless/brcm80211/brcmsmac/stf.c | 440 - drivers/net/wireless/brcm80211/brcmsmac/stf.h | 37 - drivers/net/wireless/brcm80211/brcmsmac/types.h | 303 - .../net/wireless/brcm80211/brcmsmac/ucode_loader.c | 109 - .../net/wireless/brcm80211/brcmsmac/ucode_loader.h | 56 - drivers/net/wireless/brcm80211/brcmutil/Makefile | 23 - drivers/net/wireless/brcm80211/brcmutil/d11.c | 221 - drivers/net/wireless/brcm80211/brcmutil/utils.c | 339 - .../net/wireless/brcm80211/include/brcm_hw_ids.h | 93 - drivers/net/wireless/brcm80211/include/brcmu_d11.h | 145 - .../net/wireless/brcm80211/include/brcmu_utils.h | 227 - .../net/wireless/brcm80211/include/brcmu_wifi.h | 274 - .../net/wireless/brcm80211/include/chipcommon.h | 303 - drivers/net/wireless/brcm80211/include/defs.h | 105 - drivers/net/wireless/brcm80211/include/soc.h | 36 - drivers/net/wireless/broadcom/Kconfig | 1 + drivers/net/wireless/broadcom/Makefile | 3 + drivers/net/wireless/broadcom/brcm80211/Kconfig | 87 + drivers/net/wireless/broadcom/brcm80211/Makefile | 23 + .../wireless/broadcom/brcm80211/brcmfmac/Makefile | 57 + .../wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 389 + .../wireless/broadcom/brcm80211/brcmfmac/bcdc.h | 27 + .../wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 1380 + .../wireless/broadcom/brcm80211/brcmfmac/btcoex.c | 497 + .../wireless/broadcom/brcm80211/brcmfmac/btcoex.h | 29 + .../net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 248 + .../broadcom/brcm80211/brcmfmac/cfg80211.c | 6357 ++++ .../broadcom/brcm80211/brcmfmac/cfg80211.h | 504 + .../wireless/broadcom/brcm80211/brcmfmac/chip.c | 1331 + .../wireless/broadcom/brcm80211/brcmfmac/chip.h | 94 + .../wireless/broadcom/brcm80211/brcmfmac/common.c | 198 + .../wireless/broadcom/brcm80211/brcmfmac/common.h | 23 + .../broadcom/brcm80211/brcmfmac/commonring.c | 252 + .../broadcom/brcm80211/brcmfmac/commonring.h | 72 + .../wireless/broadcom/brcm80211/brcmfmac/core.c | 1260 + .../wireless/broadcom/brcm80211/brcmfmac/core.h | 221 + .../wireless/broadcom/brcm80211/brcmfmac/debug.c | 111 + .../wireless/broadcom/brcm80211/brcmfmac/debug.h | 139 + .../wireless/broadcom/brcm80211/brcmfmac/feature.c | 172 + .../wireless/broadcom/brcm80211/brcmfmac/feature.h | 94 + .../broadcom/brcm80211/brcmfmac/firmware.c | 542 + .../broadcom/brcm80211/brcmfmac/firmware.h | 47 + .../broadcom/brcm80211/brcmfmac/flowring.c | 504 + .../broadcom/brcm80211/brcmfmac/flowring.h | 84 + .../wireless/broadcom/brcm80211/brcmfmac/fweh.c | 478 + .../wireless/broadcom/brcm80211/brcmfmac/fweh.h | 289 + .../wireless/broadcom/brcm80211/brcmfmac/fwil.c | 420 + .../wireless/broadcom/brcm80211/brcmfmac/fwil.h | 106 + .../broadcom/brcm80211/brcmfmac/fwil_types.h | 637 + .../broadcom/brcm80211/brcmfmac/fwsignal.c | 2271 ++ .../broadcom/brcm80211/brcmfmac/fwsignal.h | 33 + .../wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 1561 + .../wireless/broadcom/brcm80211/brcmfmac/msgbuf.h | 47 + .../net/wireless/broadcom/brcm80211/brcmfmac/of.c | 59 + .../net/wireless/broadcom/brcm80211/brcmfmac/of.h | 22 + .../net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 2401 ++ .../net/wireless/broadcom/brcm80211/brcmfmac/p2p.h | 187 + .../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 2107 ++ .../wireless/broadcom/brcm80211/brcmfmac/pcie.h | 29 + .../wireless/broadcom/brcm80211/brcmfmac/proto.c | 81 + .../wireless/broadcom/brcm80211/brcmfmac/proto.h | 96 + .../wireless/broadcom/brcm80211/brcmfmac/sdio.c | 4376 +++ .../wireless/broadcom/brcm80211/brcmfmac/sdio.h | 377 + .../broadcom/brcm80211/brcmfmac/tracepoint.c | 37 + .../broadcom/brcm80211/brcmfmac/tracepoint.h | 152 + .../net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 1535 + .../net/wireless/broadcom/brcm80211/brcmfmac/usb.h | 53 + .../wireless/broadcom/brcm80211/brcmfmac/vendor.c | 127 + .../wireless/broadcom/brcm80211/brcmfmac/vendor.h | 64 + .../wireless/broadcom/brcm80211/brcmsmac/Makefile | 48 + .../wireless/broadcom/brcm80211/brcmsmac/aiutils.c | 710 + .../wireless/broadcom/brcm80211/brcmsmac/aiutils.h | 229 + .../wireless/broadcom/brcm80211/brcmsmac/ampdu.c | 1144 + .../wireless/broadcom/brcm80211/brcmsmac/ampdu.h | 53 + .../wireless/broadcom/brcm80211/brcmsmac/antsel.c | 309 + .../wireless/broadcom/brcm80211/brcmsmac/antsel.h | 27 + .../brcm80211/brcmsmac/brcms_trace_brcmsmac.h | 102 + .../brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h | 88 + .../brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h | 110 + .../brcm80211/brcmsmac/brcms_trace_events.c | 23 + .../brcm80211/brcmsmac/brcms_trace_events.h | 40 + .../wireless/broadcom/brcm80211/brcmsmac/channel.c | 774 + .../wireless/broadcom/brcm80211/brcmsmac/channel.h | 47 + .../net/wireless/broadcom/brcm80211/brcmsmac/d11.h | 1902 ++ .../wireless/broadcom/brcm80211/brcmsmac/debug.c | 270 + .../wireless/broadcom/brcm80211/brcmsmac/debug.h | 76 + .../net/wireless/broadcom/brcm80211/brcmsmac/dma.c | 1564 + .../net/wireless/broadcom/brcm80211/brcmsmac/dma.h | 125 + .../net/wireless/broadcom/brcm80211/brcmsmac/led.c | 126 + .../net/wireless/broadcom/brcm80211/brcmsmac/led.h | 36 + .../broadcom/brcm80211/brcmsmac/mac80211_if.c | 1700 ++ .../broadcom/brcm80211/brcmsmac/mac80211_if.h | 113 + .../wireless/broadcom/brcm80211/brcmsmac/main.c | 8139 ++++++ .../wireless/broadcom/brcm80211/brcmsmac/main.h | 669 + .../broadcom/brcm80211/brcmsmac/phy/phy_cmn.c | 2953 ++ .../broadcom/brcm80211/brcmsmac/phy/phy_hal.h | 284 + .../broadcom/brcm80211/brcmsmac/phy/phy_int.h | 1142 + .../broadcom/brcm80211/brcmsmac/phy/phy_lcn.c | 5247 ++++ .../broadcom/brcm80211/brcmsmac/phy/phy_lcn.h | 121 + .../broadcom/brcm80211/brcmsmac/phy/phy_n.c | 28721 +++++++++++++++++++ .../broadcom/brcm80211/brcmsmac/phy/phy_qmath.c | 308 + .../broadcom/brcm80211/brcmsmac/phy/phy_qmath.h | 42 + .../broadcom/brcm80211/brcmsmac/phy/phy_radio.h | 1533 + .../broadcom/brcm80211/brcmsmac/phy/phyreg_n.h | 167 + .../broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.c | 3293 +++ .../broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.h | 55 + .../broadcom/brcm80211/brcmsmac/phy/phytbl_n.c | 10630 +++++++ .../broadcom/brcm80211/brcmsmac/phy/phytbl_n.h | 50 + .../broadcom/brcm80211/brcmsmac/phy_shim.c | 216 + .../broadcom/brcm80211/brcmsmac/phy_shim.h | 172 + .../net/wireless/broadcom/brcm80211/brcmsmac/pmu.c | 165 + .../net/wireless/broadcom/brcm80211/brcmsmac/pmu.h | 26 + .../net/wireless/broadcom/brcm80211/brcmsmac/pub.h | 341 + .../wireless/broadcom/brcm80211/brcmsmac/rate.c | 514 + .../wireless/broadcom/brcm80211/brcmsmac/rate.h | 245 + .../net/wireless/broadcom/brcm80211/brcmsmac/scb.h | 81 + .../net/wireless/broadcom/brcm80211/brcmsmac/stf.c | 440 + .../net/wireless/broadcom/brcm80211/brcmsmac/stf.h | 37 + .../wireless/broadcom/brcm80211/brcmsmac/types.h | 303 + .../broadcom/brcm80211/brcmsmac/ucode_loader.c | 109 + .../broadcom/brcm80211/brcmsmac/ucode_loader.h | 56 + .../wireless/broadcom/brcm80211/brcmutil/Makefile | 23 + .../net/wireless/broadcom/brcm80211/brcmutil/d11.c | 221 + .../wireless/broadcom/brcm80211/brcmutil/utils.c | 339 + .../broadcom/brcm80211/include/brcm_hw_ids.h | 93 + .../broadcom/brcm80211/include/brcmu_d11.h | 145 + .../broadcom/brcm80211/include/brcmu_utils.h | 227 + .../broadcom/brcm80211/include/brcmu_wifi.h | 274 + .../broadcom/brcm80211/include/chipcommon.h | 303 + .../net/wireless/broadcom/brcm80211/include/defs.h | 105 + .../net/wireless/broadcom/brcm80211/include/soc.h | 36 + 233 files changed, 109733 insertions(+), 109733 deletions(-) delete mode 100644 drivers/net/wireless/brcm80211/Kconfig delete mode 100644 drivers/net/wireless/brcm80211/Makefile delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/Makefile delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/bcdc.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/bcdc.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/btcoex.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/btcoex.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/bus.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/chip.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/chip.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/common.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/common.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/commonring.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/commonring.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/core.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/core.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/debug.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/debug.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/feature.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/feature.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/firmware.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/firmware.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/flowring.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/flowring.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/fweh.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/fweh.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/fwil.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/fwil.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/of.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/of.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/p2p.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/p2p.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/pcie.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/pcie.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/proto.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/proto.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/sdio.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/sdio.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/usb.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/usb.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/vendor.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/vendor.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/Makefile delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/aiutils.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/aiutils.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/ampdu.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/ampdu.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/antsel.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/antsel.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/channel.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/channel.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/d11.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/debug.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/debug.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/dma.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/dma.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/led.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/led.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/main.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/main.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/pmu.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/pmu.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/pub.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/rate.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/rate.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/scb.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/stf.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/stf.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/types.h delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c delete mode 100644 drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h delete mode 100644 drivers/net/wireless/brcm80211/brcmutil/Makefile delete mode 100644 drivers/net/wireless/brcm80211/brcmutil/d11.c delete mode 100644 drivers/net/wireless/brcm80211/brcmutil/utils.c delete mode 100644 drivers/net/wireless/brcm80211/include/brcm_hw_ids.h delete mode 100644 drivers/net/wireless/brcm80211/include/brcmu_d11.h delete mode 100644 drivers/net/wireless/brcm80211/include/brcmu_utils.h delete mode 100644 drivers/net/wireless/brcm80211/include/brcmu_wifi.h delete mode 100644 drivers/net/wireless/brcm80211/include/chipcommon.h delete mode 100644 drivers/net/wireless/brcm80211/include/defs.h delete mode 100644 drivers/net/wireless/brcm80211/include/soc.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/Kconfig create mode 100644 drivers/net/wireless/broadcom/brcm80211/Makefile create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/d11.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_radio.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phyreg_n.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/pub.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/scb.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/types.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/include/defs.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/include/soc.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index dd20c0b1eb02..748ee82f2ebe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2390,7 +2390,7 @@ M: Hante Meuleman L: linux-wireless@vger.kernel.org L: brcm80211-dev-list@broadcom.com S: Supported -F: drivers/net/wireless/brcm80211/ +F: drivers/net/wireless/broadcom/brcm80211/ BROADCOM BNX2FC 10 GIGABIT FCOE DRIVER M: QLogic-Storage-Upstream@qlogic.com diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index e261df26b0dd..c627d9e41ede 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -159,7 +159,6 @@ config MWL8K will be called mwl8k. If unsure, say N. source "drivers/net/wireless/ath/Kconfig" -source "drivers/net/wireless/brcm80211/Kconfig" source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/ipw2x00/Kconfig" source "drivers/net/wireless/iwlwifi/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 13ac61737acd..41ebe5641c01 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -47,8 +47,5 @@ obj-$(CONFIG_WL_TI) += ti/ obj-$(CONFIG_MWIFIEX) += mwifiex/ -obj-$(CONFIG_BRCMFMAC) += brcm80211/ -obj-$(CONFIG_BRCMSMAC) += brcm80211/ - obj-$(CONFIG_CW1200) += cw1200/ obj-$(CONFIG_RSI_91X) += rsi/ diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig deleted file mode 100644 index ab42b1fea03c..000000000000 --- a/drivers/net/wireless/brcm80211/Kconfig +++ /dev/null @@ -1,87 +0,0 @@ -config BRCMUTIL - tristate - -config BRCMSMAC - tristate "Broadcom IEEE802.11n PCIe SoftMAC WLAN driver" - depends on MAC80211 - depends on BCMA_POSSIBLE - select BCMA - select NEW_LEDS if BCMA_DRIVER_GPIO - select LEDS_CLASS if BCMA_DRIVER_GPIO - select BRCMUTIL - select FW_LOADER - select CORDIC - ---help--- - This module adds support for PCIe wireless adapters based on Broadcom - IEEE802.11n SoftMAC chipsets. It also has WLAN led support, which will - be available if you select BCMA_DRIVER_GPIO. If you choose to build a - module, the driver will be called brcmsmac.ko. - -config BRCMFMAC - tristate "Broadcom IEEE802.11n embedded FullMAC WLAN driver" - depends on CFG80211 - select BRCMUTIL - ---help--- - This module adds support for embedded wireless adapters based on - Broadcom IEEE802.11n FullMAC chipsets. It has to work with at least - one of the bus interface support. If you choose to build a module, - it'll be called brcmfmac.ko. - -config BRCMFMAC_PROTO_BCDC - bool - -config BRCMFMAC_PROTO_MSGBUF - bool - -config BRCMFMAC_SDIO - bool "SDIO bus interface support for FullMAC driver" - depends on (MMC = y || MMC = BRCMFMAC) - depends on BRCMFMAC - select BRCMFMAC_PROTO_BCDC - select FW_LOADER - default y - ---help--- - This option enables the SDIO bus interface support for Broadcom - IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to - use the driver for a SDIO wireless card. - -config BRCMFMAC_USB - bool "USB bus interface support for FullMAC driver" - depends on (USB = y || USB = BRCMFMAC) - depends on BRCMFMAC - select BRCMFMAC_PROTO_BCDC - select FW_LOADER - ---help--- - This option enables the USB bus interface support for Broadcom - IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to - use the driver for an USB wireless card. - -config BRCMFMAC_PCIE - bool "PCIE bus interface support for FullMAC driver" - depends on BRCMFMAC - depends on PCI - depends on HAS_DMA - select BRCMFMAC_PROTO_MSGBUF - select FW_LOADER - ---help--- - This option enables the PCIE bus interface support for Broadcom - IEEE802.11ac embedded FullMAC WLAN driver. Say Y if you want to - use the driver for an PCIE wireless card. - -config BRCM_TRACING - bool "Broadcom device tracing" - depends on BRCMSMAC || BRCMFMAC - ---help--- - If you say Y here, the Broadcom wireless drivers will register - with ftrace to dump event information into the trace ringbuffer. - Tracing can be enabled at runtime to aid in debugging wireless - issues. This option adds a small amount of overhead when tracing - is disabled. If unsure, say Y to allow developers to better help - you when wireless problems occur. - -config BRCMDBG - bool "Broadcom driver debug functions" - depends on BRCMSMAC || BRCMFMAC - select WANT_DEV_COREDUMP - ---help--- - Selecting this enables additional code for debug purposes. diff --git a/drivers/net/wireless/brcm80211/Makefile b/drivers/net/wireless/brcm80211/Makefile deleted file mode 100644 index b987920e982e..000000000000 --- a/drivers/net/wireless/brcm80211/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# -# Makefile fragment for Broadcom 802.11n Networking Device Driver -# -# Copyright (c) 2010 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. - -# common flags -subdir-ccflags-$(CONFIG_BRCMDBG) += -DDEBUG - -obj-$(CONFIG_BRCMUTIL) += brcmutil/ -obj-$(CONFIG_BRCMFMAC) += brcmfmac/ -obj-$(CONFIG_BRCMSMAC) += brcmsmac/ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile deleted file mode 100644 index dc4c75083085..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -# -# Makefile fragment for Broadcom 802.11n Networking Device Driver -# -# Copyright (c) 2010 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. - -ccflags-y += \ - -Idrivers/net/wireless/brcm80211/brcmfmac \ - -Idrivers/net/wireless/brcm80211/include - -ccflags-y += -D__CHECK_ENDIAN__ - -obj-$(CONFIG_BRCMFMAC) += brcmfmac.o -brcmfmac-objs += \ - cfg80211.o \ - chip.o \ - fwil.o \ - fweh.o \ - fwsignal.o \ - p2p.o \ - proto.o \ - common.o \ - core.o \ - firmware.o \ - feature.o \ - btcoex.o \ - vendor.o -brcmfmac-$(CONFIG_BRCMFMAC_PROTO_BCDC) += \ - bcdc.o -brcmfmac-$(CONFIG_BRCMFMAC_PROTO_MSGBUF) += \ - commonring.o \ - flowring.o \ - msgbuf.o -brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ - sdio.o \ - bcmsdh.o -brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ - usb.o -brcmfmac-$(CONFIG_BRCMFMAC_PCIE) += \ - pcie.o -brcmfmac-$(CONFIG_BRCMDBG) += \ - debug.o -brcmfmac-$(CONFIG_BRCM_TRACING) += \ - tracepoint.o -brcmfmac-$(CONFIG_OF) += \ - of.o diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c deleted file mode 100644 index 288c84e7c56b..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (c) 2010 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. - */ - -/******************************************************************************* - * Communicates with the dongle by using dcmd codes. - * For certain dcmd codes, the dongle interprets string data from the host. - ******************************************************************************/ - -#include -#include - -#include -#include - -#include "core.h" -#include "bus.h" -#include "fwsignal.h" -#include "debug.h" -#include "tracepoint.h" -#include "proto.h" -#include "bcdc.h" - -struct brcmf_proto_bcdc_dcmd { - __le32 cmd; /* dongle command value */ - __le32 len; /* lower 16: output buflen; - * upper 16: input buflen (excludes header) */ - __le32 flags; /* flag defns given below */ - __le32 status; /* status code returned from the device */ -}; - -/* BCDC flag definitions */ -#define BCDC_DCMD_ERROR 0x01 /* 1=cmd failed */ -#define BCDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */ -#define BCDC_DCMD_IF_MASK 0xF000 /* I/F index */ -#define BCDC_DCMD_IF_SHIFT 12 -#define BCDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */ -#define BCDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */ -#define BCDC_DCMD_ID(flags) \ - (((flags) & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT) - -/* - * BCDC header - Broadcom specific extension of CDC. - * Used on data packets to convey priority across USB. - */ -#define BCDC_HEADER_LEN 4 -#define BCDC_PROTO_VER 2 /* Protocol version */ -#define BCDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */ -#define BCDC_FLAG_VER_SHIFT 4 /* Protocol version shift */ -#define BCDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */ -#define BCDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */ -#define BCDC_PRIORITY_MASK 0x7 -#define BCDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */ -#define BCDC_FLAG2_IF_SHIFT 0 - -#define BCDC_GET_IF_IDX(hdr) \ - ((int)((((hdr)->flags2) & BCDC_FLAG2_IF_MASK) >> BCDC_FLAG2_IF_SHIFT)) -#define BCDC_SET_IF_IDX(hdr, idx) \ - ((hdr)->flags2 = (((hdr)->flags2 & ~BCDC_FLAG2_IF_MASK) | \ - ((idx) << BCDC_FLAG2_IF_SHIFT))) - -/** - * struct brcmf_proto_bcdc_header - BCDC header format - * - * @flags: flags contain protocol and checksum info. - * @priority: 802.1d priority and USB flow control info (bit 4:7). - * @flags2: additional flags containing dongle interface index. - * @data_offset: start of packet data. header is following by firmware signals. - */ -struct brcmf_proto_bcdc_header { - u8 flags; - u8 priority; - u8 flags2; - u8 data_offset; -}; - -/* - * maximum length of firmware signal data between - * the BCDC header and packet data in the tx path. - */ -#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES 12 - -#define RETRIES 2 /* # of retries to retrieve matching dcmd response */ -#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE - * (amount of header tha might be added) - * plus any space that might be needed - * for bus alignment padding. - */ -struct brcmf_bcdc { - u16 reqid; - u8 bus_header[BUS_HEADER_LEN]; - struct brcmf_proto_bcdc_dcmd msg; - unsigned char buf[BRCMF_DCMD_MAXLEN]; -}; - - -static int -brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, - uint len, bool set) -{ - struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; - struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg; - u32 flags; - - brcmf_dbg(BCDC, "Enter\n"); - - memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd)); - - msg->cmd = cpu_to_le32(cmd); - msg->len = cpu_to_le32(len); - flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT); - if (set) - flags |= BCDC_DCMD_SET; - flags = (flags & ~BCDC_DCMD_IF_MASK) | - (ifidx << BCDC_DCMD_IF_SHIFT); - msg->flags = cpu_to_le32(flags); - - if (buf) - memcpy(bcdc->buf, buf, len); - - len += sizeof(*msg); - if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE) - len = BRCMF_TX_IOCTL_MAX_MSG_SIZE; - - /* Send request */ - return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len); -} - -static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len) -{ - int ret; - struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; - - brcmf_dbg(BCDC, "Enter\n"); - len += sizeof(struct brcmf_proto_bcdc_dcmd); - do { - ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg, - len); - if (ret < 0) - break; - } while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id); - - return ret; -} - -static int -brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, - void *buf, uint len) -{ - struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; - struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg; - void *info; - int ret = 0, retries = 0; - u32 id, flags; - - brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len); - - ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false); - if (ret < 0) { - brcmf_err("brcmf_proto_bcdc_msg failed w/status %d\n", - ret); - goto done; - } - -retry: - /* wait for interrupt and get first fragment */ - ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len); - if (ret < 0) - goto done; - - flags = le32_to_cpu(msg->flags); - id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT; - - if ((id < bcdc->reqid) && (++retries < RETRIES)) - goto retry; - if (id != bcdc->reqid) { - brcmf_err("%s: unexpected request id %d (expected %d)\n", - brcmf_ifname(drvr, ifidx), id, bcdc->reqid); - ret = -EINVAL; - goto done; - } - - /* Check info buffer */ - info = (void *)&msg[1]; - - /* Copy info buffer */ - if (buf) { - if (ret < (int)len) - len = ret; - memcpy(buf, info, len); - } - - /* Check the ERROR flag */ - if (flags & BCDC_DCMD_ERROR) - ret = le32_to_cpu(msg->status); - -done: - return ret; -} - -static int -brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, - void *buf, uint len) -{ - struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; - struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg; - int ret = 0; - u32 flags, id; - - brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len); - - ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true); - if (ret < 0) - goto done; - - ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len); - if (ret < 0) - goto done; - - flags = le32_to_cpu(msg->flags); - id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT; - - if (id != bcdc->reqid) { - brcmf_err("%s: unexpected request id %d (expected %d)\n", - brcmf_ifname(drvr, ifidx), id, bcdc->reqid); - ret = -EINVAL; - goto done; - } - - /* Check the ERROR flag */ - if (flags & BCDC_DCMD_ERROR) - ret = le32_to_cpu(msg->status); - -done: - return ret; -} - -static void -brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset, - struct sk_buff *pktbuf) -{ - struct brcmf_proto_bcdc_header *h; - - brcmf_dbg(BCDC, "Enter\n"); - - /* Push BDC header used to convey priority for buses that don't */ - skb_push(pktbuf, BCDC_HEADER_LEN); - - h = (struct brcmf_proto_bcdc_header *)(pktbuf->data); - - h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT); - if (pktbuf->ip_summed == CHECKSUM_PARTIAL) - h->flags |= BCDC_FLAG_SUM_NEEDED; - - h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK); - h->flags2 = 0; - h->data_offset = offset; - BCDC_SET_IF_IDX(h, ifidx); - trace_brcmf_bcdchdr(pktbuf->data); -} - -static int -brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, - struct sk_buff *pktbuf, struct brcmf_if **ifp) -{ - struct brcmf_proto_bcdc_header *h; - struct brcmf_if *tmp_if; - - brcmf_dbg(BCDC, "Enter\n"); - - /* Pop BCDC header used to convey priority for buses that don't */ - if (pktbuf->len <= BCDC_HEADER_LEN) { - brcmf_dbg(INFO, "rx data too short (%d <= %d)\n", - pktbuf->len, BCDC_HEADER_LEN); - return -EBADE; - } - - trace_brcmf_bcdchdr(pktbuf->data); - h = (struct brcmf_proto_bcdc_header *)(pktbuf->data); - - tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h)); - if (!tmp_if) { - brcmf_dbg(INFO, "no matching ifp found\n"); - return -EBADE; - } - if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) != - BCDC_PROTO_VER) { - brcmf_err("%s: non-BCDC packet received, flags 0x%x\n", - brcmf_ifname(drvr, tmp_if->ifidx), h->flags); - return -EBADE; - } - - if (h->flags & BCDC_FLAG_SUM_GOOD) { - brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n", - brcmf_ifname(drvr, tmp_if->ifidx), h->flags); - pktbuf->ip_summed = CHECKSUM_UNNECESSARY; - } - - pktbuf->priority = h->priority & BCDC_PRIORITY_MASK; - - skb_pull(pktbuf, BCDC_HEADER_LEN); - if (do_fws) - brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf); - else - skb_pull(pktbuf, h->data_offset << 2); - - if (pktbuf->len == 0) - return -ENODATA; - - *ifp = tmp_if; - return 0; -} - -static int -brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset, - struct sk_buff *pktbuf) -{ - brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf); - return brcmf_bus_txdata(drvr->bus_if, pktbuf); -} - -static void -brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, - enum proto_addr_mode addr_mode) -{ -} - -static void -brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx, - u8 peer[ETH_ALEN]) -{ -} - -static void -brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, - u8 peer[ETH_ALEN]) -{ -} - -int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) -{ - struct brcmf_bcdc *bcdc; - - bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC); - if (!bcdc) - goto fail; - - /* ensure that the msg buf directly follows the cdc msg struct */ - if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) { - brcmf_err("struct brcmf_proto_bcdc is not correctly defined\n"); - goto fail; - } - - drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull; - drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd; - drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd; - drvr->proto->txdata = brcmf_proto_bcdc_txdata; - drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode; - drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer; - drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer; - drvr->proto->pd = bcdc; - - drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; - drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + - sizeof(struct brcmf_proto_bcdc_dcmd); - return 0; - -fail: - kfree(bcdc); - return -ENOMEM; -} - -void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) -{ - kfree(drvr->proto->pd); - drvr->proto->pd = NULL; -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h deleted file mode 100644 index 6003179c0ceb..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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_BCDC_H -#define BRCMFMAC_BCDC_H - -#ifdef CONFIG_BRCMFMAC_PROTO_BCDC -int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr); -void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr); -#else -static inline int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { return 0; } -static inline void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) {} -#endif - -#endif /* BRCMFMAC_BCDC_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c deleted file mode 100644 index 410a6645d316..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* - * Copyright (c) 2010 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. - */ -/* ****************** SDIO CARD Interface Functions **************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include "chip.h" -#include "bus.h" -#include "debug.h" -#include "sdio.h" -#include "of.h" - -#define SDIOH_API_ACCESS_RETRY_LIMIT 2 - -#define DMA_ALIGN_MASK 0x03 - -#define SDIO_FUNC1_BLOCKSIZE 64 -#define SDIO_FUNC2_BLOCKSIZE 512 -/* Maximum milliseconds to wait for F2 to come up */ -#define SDIO_WAIT_F2RDY 3000 - -#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ -#define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */ - -struct brcmf_sdiod_freezer { - atomic_t freezing; - atomic_t thread_count; - u32 frozen_count; - wait_queue_head_t thread_freeze; - struct completion resumed; -}; - -static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; -module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); -MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); - -static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev_id); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - - brcmf_dbg(INTR, "OOB intr triggered\n"); - - /* out-of-band interrupt is level-triggered which won't - * be cleared until dpc - */ - if (sdiodev->irq_en) { - disable_irq_nosync(irq); - sdiodev->irq_en = false; - } - - brcmf_sdio_isr(sdiodev->bus); - - return IRQ_HANDLED; -} - -static void brcmf_sdiod_ib_irqhandler(struct sdio_func *func) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - - brcmf_dbg(INTR, "IB intr triggered\n"); - - brcmf_sdio_isr(sdiodev->bus); -} - -/* dummy handler for SDIO function 2 interrupt */ -static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func) -{ -} - -int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) -{ - int ret = 0; - u8 data; - u32 addr, gpiocontrol; - unsigned long flags; - - if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { - brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n", - sdiodev->pdata->oob_irq_nr); - ret = request_irq(sdiodev->pdata->oob_irq_nr, - brcmf_sdiod_oob_irqhandler, - sdiodev->pdata->oob_irq_flags, - "brcmf_oob_intr", - &sdiodev->func[1]->dev); - if (ret != 0) { - brcmf_err("request_irq failed %d\n", ret); - return ret; - } - sdiodev->oob_irq_requested = true; - spin_lock_init(&sdiodev->irq_en_lock); - spin_lock_irqsave(&sdiodev->irq_en_lock, flags); - sdiodev->irq_en = true; - spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags); - - ret = enable_irq_wake(sdiodev->pdata->oob_irq_nr); - if (ret != 0) { - brcmf_err("enable_irq_wake failed %d\n", ret); - return ret; - } - sdiodev->irq_wake = true; - - sdio_claim_host(sdiodev->func[1]); - - if (sdiodev->bus_if->chip == BRCM_CC_43362_CHIP_ID) { - /* assign GPIO to SDIO core */ - addr = CORE_CC_REG(SI_ENUM_BASE, gpiocontrol); - gpiocontrol = brcmf_sdiod_regrl(sdiodev, addr, &ret); - gpiocontrol |= 0x2; - brcmf_sdiod_regwl(sdiodev, addr, gpiocontrol, &ret); - - brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_SELECT, 0xf, - &ret); - brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_OUT, 0, &ret); - brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_EN, 0x2, &ret); - } - - /* must configure SDIO_CCCR_IENx to enable irq */ - data = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_IENx, &ret); - data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1; - brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret); - - /* redirect, configure and enable io for interrupt signal */ - data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE; - if (sdiodev->pdata->oob_irq_flags & IRQF_TRIGGER_HIGH) - data |= SDIO_SEPINT_ACT_HI; - brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret); - - sdio_release_host(sdiodev->func[1]); - } else { - brcmf_dbg(SDIO, "Entering\n"); - sdio_claim_host(sdiodev->func[1]); - sdio_claim_irq(sdiodev->func[1], brcmf_sdiod_ib_irqhandler); - sdio_claim_irq(sdiodev->func[2], brcmf_sdiod_dummy_irqhandler); - sdio_release_host(sdiodev->func[1]); - } - - return 0; -} - -int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev) -{ - brcmf_dbg(SDIO, "Entering\n"); - - if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { - sdio_claim_host(sdiodev->func[1]); - brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); - brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL); - sdio_release_host(sdiodev->func[1]); - - if (sdiodev->oob_irq_requested) { - sdiodev->oob_irq_requested = false; - if (sdiodev->irq_wake) { - disable_irq_wake(sdiodev->pdata->oob_irq_nr); - sdiodev->irq_wake = false; - } - free_irq(sdiodev->pdata->oob_irq_nr, - &sdiodev->func[1]->dev); - sdiodev->irq_en = false; - } - } else { - sdio_claim_host(sdiodev->func[1]); - sdio_release_irq(sdiodev->func[2]); - sdio_release_irq(sdiodev->func[1]); - sdio_release_host(sdiodev->func[1]); - } - - return 0; -} - -void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev, - enum brcmf_sdiod_state state) -{ - if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM || - state == sdiodev->state) - return; - - brcmf_dbg(TRACE, "%d -> %d\n", sdiodev->state, state); - switch (sdiodev->state) { - case BRCMF_SDIOD_DATA: - /* any other state means bus interface is down */ - brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN); - break; - case BRCMF_SDIOD_DOWN: - /* transition from DOWN to DATA means bus interface is up */ - if (state == BRCMF_SDIOD_DATA) - brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_UP); - break; - default: - break; - } - sdiodev->state = state; -} - -static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func, - uint regaddr, u8 byte) -{ - int err_ret; - - /* - * Can only directly write to some F0 registers. - * Handle CCCR_IENx and CCCR_ABORT command - * as a special case. - */ - if ((regaddr == SDIO_CCCR_ABORT) || - (regaddr == SDIO_CCCR_IENx)) - sdio_writeb(func, byte, regaddr, &err_ret); - else - sdio_f0_writeb(func, byte, regaddr, &err_ret); - - return err_ret; -} - -static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, - u32 addr, u8 regsz, void *data, bool write) -{ - struct sdio_func *func; - int ret; - - brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", - write, fn, addr, regsz); - - /* only allow byte access on F0 */ - if (WARN_ON(regsz > 1 && !fn)) - return -EINVAL; - func = sdiodev->func[fn]; - - switch (regsz) { - case sizeof(u8): - if (write) { - if (fn) - sdio_writeb(func, *(u8 *)data, addr, &ret); - else - ret = brcmf_sdiod_f0_writeb(func, addr, - *(u8 *)data); - } else { - if (fn) - *(u8 *)data = sdio_readb(func, addr, &ret); - else - *(u8 *)data = sdio_f0_readb(func, addr, &ret); - } - break; - case sizeof(u16): - if (write) - sdio_writew(func, *(u16 *)data, addr, &ret); - else - *(u16 *)data = sdio_readw(func, addr, &ret); - break; - case sizeof(u32): - if (write) - sdio_writel(func, *(u32 *)data, addr, &ret); - else - *(u32 *)data = sdio_readl(func, addr, &ret); - break; - default: - brcmf_err("invalid size: %d\n", regsz); - break; - } - - if (ret) - brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", - write ? "write" : "read", fn, addr, ret); - - return ret; -} - -static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, - u8 regsz, void *data, bool write) -{ - u8 func; - s32 retry = 0; - int ret; - - if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM) - return -ENOMEDIUM; - - /* - * figure out how to read the register based on address range - * 0x00 ~ 0x7FF: function 0 CCCR and FBR - * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers - * The rest: function 1 silicon backplane core registers - */ - if ((addr & ~REG_F0_REG_MASK) == 0) - func = SDIO_FUNC_0; - else - func = SDIO_FUNC_1; - - do { - if (!write) - memset(data, 0, regsz); - /* for retry wait for 1 ms till bus get settled down */ - if (retry) - usleep_range(1000, 2000); - ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz, - data, write); - } while (ret != 0 && ret != -ENOMEDIUM && - retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); - - if (ret == -ENOMEDIUM) - brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); - else if (ret != 0) { - /* - * SleepCSR register access can fail when - * waking up the device so reduce this noise - * in the logs. - */ - if (addr != SBSDIO_FUNC1_SLEEPCSR) - brcmf_err("failed to %s data F%d@0x%05x, err: %d\n", - write ? "write" : "read", func, addr, ret); - else - brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", - write ? "write" : "read", func, addr, ret); - } - return ret; -} - -static int -brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) -{ - int err = 0, i; - u8 addr[3]; - - if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM) - return -ENOMEDIUM; - - addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK; - addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK; - addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK; - - for (i = 0; i < 3; i++) { - err = brcmf_sdiod_regrw_helper(sdiodev, - SBSDIO_FUNC1_SBADDRLOW + i, - sizeof(u8), &addr[i], true); - if (err) { - brcmf_err("failed at addr: 0x%0x\n", - SBSDIO_FUNC1_SBADDRLOW + i); - break; - } - } - - return err; -} - -static int -brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr) -{ - uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK; - int err = 0; - - if (bar0 != sdiodev->sbwad) { - err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0); - if (err) - return err; - - sdiodev->sbwad = bar0; - } - - *addr &= SBSDIO_SB_OFT_ADDR_MASK; - - if (width == 4) - *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; - - return 0; -} - -u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) -{ - u8 data; - int retval; - - brcmf_dbg(SDIO, "addr:0x%08x\n", addr); - retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, - false); - brcmf_dbg(SDIO, "data:0x%02x\n", data); - - if (ret) - *ret = retval; - - return data; -} - -u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) -{ - u32 data; - int retval; - - brcmf_dbg(SDIO, "addr:0x%08x\n", addr); - retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr); - if (retval) - goto done; - retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, - false); - brcmf_dbg(SDIO, "data:0x%08x\n", data); - -done: - if (ret) - *ret = retval; - - return data; -} - -void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, - u8 data, int *ret) -{ - int retval; - - brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data); - retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, - true); - if (ret) - *ret = retval; -} - -void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, - u32 data, int *ret) -{ - int retval; - - brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data); - retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr); - if (retval) - goto done; - retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, - true); - -done: - if (ret) - *ret = retval; -} - -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; - - /* Single skb use the standard mmc interface */ - req_sz = pkt->len + 3; - req_sz &= (uint)~3; - - if (write) - err = sdio_memcpy_toio(sdiodev->func[fn], addr, - ((u8 *)(pkt->data)), req_sz); - else if (fn == 1) - err = sdio_memcpy_fromio(sdiodev->func[fn], ((u8 *)(pkt->data)), - addr, req_sz); - else - /* function 2 read is FIFO operation */ - err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr, - req_sz); - if (err == -ENOMEDIUM) - brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); - return err; -} - -/** - * brcmf_sdiod_sglist_rw - SDIO interface function for block data access - * @sdiodev: brcmfmac sdio device - * @fn: SDIO function number - * @write: direction flag - * @addr: dongle memory address as source/destination - * @pkt: skb pointer - * - * This function takes the respbonsibility as the interface function to MMC - * stack for block data access. It assumes that the skb passed down by the - * caller has already been padded and aligned. - */ -static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, - bool write, u32 addr, - struct sk_buff_head *pktlist) -{ - unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset; - unsigned int max_req_sz, orig_offset, dst_offset; - unsigned short max_seg_cnt, seg_sz; - unsigned char *pkt_data, *orig_data, *dst_data; - struct sk_buff *pkt_next = NULL, *local_pkt_next; - struct sk_buff_head local_list, *target_list; - struct mmc_request mmc_req; - struct mmc_command mmc_cmd; - struct mmc_data mmc_dat; - struct scatterlist *sgl; - int ret = 0; - - if (!pktlist->qlen) - return -EINVAL; - - target_list = pktlist; - /* for host with broken sg support, prepare a page aligned list */ - __skb_queue_head_init(&local_list); - if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) { - req_sz = 0; - skb_queue_walk(pktlist, pkt_next) - req_sz += pkt_next->len; - req_sz = ALIGN(req_sz, sdiodev->func[fn]->cur_blksize); - while (req_sz > PAGE_SIZE) { - pkt_next = brcmu_pkt_buf_get_skb(PAGE_SIZE); - if (pkt_next == NULL) { - ret = -ENOMEM; - goto exit; - } - __skb_queue_tail(&local_list, pkt_next); - req_sz -= PAGE_SIZE; - } - pkt_next = brcmu_pkt_buf_get_skb(req_sz); - if (pkt_next == NULL) { - ret = -ENOMEM; - goto exit; - } - __skb_queue_tail(&local_list, pkt_next); - target_list = &local_list; - } - - func_blk_sz = sdiodev->func[fn]->cur_blksize; - max_req_sz = sdiodev->max_request_size; - max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count, - target_list->qlen); - seg_sz = target_list->qlen; - pkt_offset = 0; - pkt_next = target_list->next; - - memset(&mmc_req, 0, sizeof(struct mmc_request)); - memset(&mmc_cmd, 0, sizeof(struct mmc_command)); - memset(&mmc_dat, 0, sizeof(struct mmc_data)); - - mmc_dat.sg = sdiodev->sgtable.sgl; - mmc_dat.blksz = func_blk_sz; - mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; - mmc_cmd.opcode = SD_IO_RW_EXTENDED; - mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ - mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */ - mmc_cmd.arg |= 1<<27; /* block mode */ - /* for function 1 the addr will be incremented */ - mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0; - mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; - mmc_req.cmd = &mmc_cmd; - mmc_req.data = &mmc_dat; - - while (seg_sz) { - req_sz = 0; - sg_cnt = 0; - sgl = sdiodev->sgtable.sgl; - /* prep sg table */ - while (pkt_next != (struct sk_buff *)target_list) { - pkt_data = pkt_next->data + pkt_offset; - sg_data_sz = pkt_next->len - pkt_offset; - if (sg_data_sz > sdiodev->max_segment_size) - sg_data_sz = sdiodev->max_segment_size; - if (sg_data_sz > max_req_sz - req_sz) - sg_data_sz = max_req_sz - req_sz; - - sg_set_buf(sgl, pkt_data, sg_data_sz); - - sg_cnt++; - sgl = sg_next(sgl); - req_sz += sg_data_sz; - pkt_offset += sg_data_sz; - if (pkt_offset == pkt_next->len) { - pkt_offset = 0; - pkt_next = pkt_next->next; - } - - if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt) - break; - } - seg_sz -= sg_cnt; - - if (req_sz % func_blk_sz != 0) { - brcmf_err("sg request length %u is not %u aligned\n", - req_sz, func_blk_sz); - ret = -ENOTBLK; - goto exit; - } - - mmc_dat.sg_len = sg_cnt; - mmc_dat.blocks = req_sz / func_blk_sz; - mmc_cmd.arg |= (addr & 0x1FFFF) << 9; /* address */ - mmc_cmd.arg |= mmc_dat.blocks & 0x1FF; /* block count */ - /* incrementing addr for function 1 */ - if (fn == 1) - addr += req_sz; - - mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card); - mmc_wait_for_req(sdiodev->func[fn]->card->host, &mmc_req); - - ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; - if (ret == -ENOMEDIUM) { - brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); - break; - } else if (ret != 0) { - brcmf_err("CMD53 sg block %s failed %d\n", - write ? "write" : "read", ret); - ret = -EIO; - break; - } - } - - if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) { - local_pkt_next = local_list.next; - orig_offset = 0; - skb_queue_walk(pktlist, pkt_next) { - dst_offset = 0; - do { - req_sz = local_pkt_next->len - orig_offset; - req_sz = min_t(uint, pkt_next->len - dst_offset, - req_sz); - orig_data = local_pkt_next->data + orig_offset; - dst_data = pkt_next->data + dst_offset; - memcpy(dst_data, orig_data, req_sz); - orig_offset += req_sz; - dst_offset += req_sz; - if (orig_offset == local_pkt_next->len) { - orig_offset = 0; - local_pkt_next = local_pkt_next->next; - } - if (dst_offset == pkt_next->len) - break; - } while (!skb_queue_empty(&local_list)); - } - } - -exit: - sg_init_table(sdiodev->sgtable.sgl, sdiodev->sgtable.orig_nents); - while ((pkt_next = __skb_dequeue(&local_list)) != NULL) - brcmu_pkt_buf_free_skb(pkt_next); - - return ret; -} - -int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes) -{ - struct sk_buff *mypkt; - int err; - - mypkt = brcmu_pkt_buf_get_skb(nbytes); - if (!mypkt) { - brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n", - nbytes); - return -EIO; - } - - err = brcmf_sdiod_recv_pkt(sdiodev, mypkt); - if (!err) - memcpy(buf, mypkt->data, nbytes); - - brcmu_pkt_buf_free_skb(mypkt); - return err; -} - -int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt) -{ - u32 addr = sdiodev->sbwad; - int err = 0; - - brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pkt->len); - - err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); - if (err) - goto done; - - err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, pkt); - -done: - return err; -} - -int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, - struct sk_buff_head *pktq, uint totlen) -{ - struct sk_buff *glom_skb; - struct sk_buff *skb; - u32 addr = sdiodev->sbwad; - int err = 0; - - brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", - addr, pktq->qlen); - - err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); - if (err) - goto done; - - if (pktq->qlen == 1) - err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, - pktq->next); - else if (!sdiodev->sg_support) { - glom_skb = brcmu_pkt_buf_get_skb(totlen); - if (!glom_skb) - return -ENOMEM; - err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, - glom_skb); - if (err) - goto done; - - skb_queue_walk(pktq, skb) { - memcpy(skb->data, glom_skb->data, skb->len); - skb_pull(glom_skb, skb->len); - } - } else - err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, false, addr, - pktq); - -done: - return err; -} - -int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes) -{ - struct sk_buff *mypkt; - u32 addr = sdiodev->sbwad; - int err; - - mypkt = brcmu_pkt_buf_get_skb(nbytes); - if (!mypkt) { - brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n", - nbytes); - return -EIO; - } - - memcpy(mypkt->data, buf, nbytes); - - err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); - - if (!err) - err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, addr, - mypkt); - - brcmu_pkt_buf_free_skb(mypkt); - return err; - -} - -int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev, - struct sk_buff_head *pktq) -{ - struct sk_buff *skb; - u32 addr = sdiodev->sbwad; - int err; - - brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pktq->qlen); - - err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); - if (err) - return err; - - if (pktq->qlen == 1 || !sdiodev->sg_support) - skb_queue_walk(pktq, skb) { - err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, - addr, skb); - if (err) - break; - } - else - err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, true, addr, - pktq); - - return err; -} - -int -brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, - u8 *data, uint size) -{ - int bcmerror = 0; - struct sk_buff *pkt; - u32 sdaddr; - uint dsize; - - dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); - pkt = dev_alloc_skb(dsize); - if (!pkt) { - brcmf_err("dev_alloc_skb failed: len %d\n", dsize); - return -EIO; - } - pkt->priority = 0; - - /* Determine initial transfer parameters */ - sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK; - if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK) - dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr); - else - dsize = size; - - sdio_claim_host(sdiodev->func[1]); - - /* Do the transfer(s) */ - while (size) { - /* Set the backplane window to include the start address */ - bcmerror = brcmf_sdiod_set_sbaddr_window(sdiodev, address); - if (bcmerror) - break; - - brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n", - write ? "write" : "read", dsize, - sdaddr, address & SBSDIO_SBWINDOW_MASK); - - sdaddr &= SBSDIO_SB_OFT_ADDR_MASK; - sdaddr |= SBSDIO_SB_ACCESS_2_4B_FLAG; - - skb_put(pkt, dsize); - if (write) - memcpy(pkt->data, data, dsize); - bcmerror = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_1, write, - sdaddr, pkt); - if (bcmerror) { - brcmf_err("membytes transfer failed\n"); - break; - } - if (!write) - memcpy(data, pkt->data, dsize); - skb_trim(pkt, 0); - - /* Adjust for next transfer (if any) */ - size -= dsize; - if (size) { - data += dsize; - address += dsize; - sdaddr = 0; - dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); - } - } - - dev_kfree_skb(pkt); - - /* Return the window to backplane enumeration space for core access */ - if (brcmf_sdiod_set_sbaddr_window(sdiodev, sdiodev->sbwad)) - brcmf_err("FAILED to set window back to 0x%x\n", - sdiodev->sbwad); - - sdio_release_host(sdiodev->func[1]); - - return bcmerror; -} - -int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn) -{ - char t_func = (char)fn; - brcmf_dbg(SDIO, "Enter\n"); - - /* issue abort cmd52 command through F0 */ - brcmf_sdiod_request_data(sdiodev, SDIO_FUNC_0, SDIO_CCCR_ABORT, - sizeof(t_func), &t_func, true); - - brcmf_dbg(SDIO, "Exit\n"); - return 0; -} - -static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) -{ - uint nents; - int err; - - if (!sdiodev->sg_support) - return; - - nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, brcmf_sdiod_txglomsz); - nents += (nents >> 4) + 1; - - WARN_ON(nents > sdiodev->max_segment_count); - - brcmf_dbg(TRACE, "nents=%d\n", nents); - err = sg_alloc_table(&sdiodev->sgtable, nents, GFP_KERNEL); - if (err < 0) { - brcmf_err("allocation failed: disable scatter-gather"); - sdiodev->sg_support = false; - } - - sdiodev->txglomsz = brcmf_sdiod_txglomsz; -} - -#ifdef CONFIG_PM_SLEEP -static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev) -{ - sdiodev->freezer = kzalloc(sizeof(*sdiodev->freezer), GFP_KERNEL); - if (!sdiodev->freezer) - return -ENOMEM; - atomic_set(&sdiodev->freezer->thread_count, 0); - atomic_set(&sdiodev->freezer->freezing, 0); - init_waitqueue_head(&sdiodev->freezer->thread_freeze); - init_completion(&sdiodev->freezer->resumed); - return 0; -} - -static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev) -{ - if (sdiodev->freezer) { - WARN_ON(atomic_read(&sdiodev->freezer->freezing)); - kfree(sdiodev->freezer); - } -} - -static int brcmf_sdiod_freezer_on(struct brcmf_sdio_dev *sdiodev) -{ - atomic_t *expect = &sdiodev->freezer->thread_count; - int res = 0; - - sdiodev->freezer->frozen_count = 0; - reinit_completion(&sdiodev->freezer->resumed); - atomic_set(&sdiodev->freezer->freezing, 1); - brcmf_sdio_trigger_dpc(sdiodev->bus); - wait_event(sdiodev->freezer->thread_freeze, - atomic_read(expect) == sdiodev->freezer->frozen_count); - sdio_claim_host(sdiodev->func[1]); - res = brcmf_sdio_sleep(sdiodev->bus, true); - sdio_release_host(sdiodev->func[1]); - return res; -} - -static void brcmf_sdiod_freezer_off(struct brcmf_sdio_dev *sdiodev) -{ - sdio_claim_host(sdiodev->func[1]); - brcmf_sdio_sleep(sdiodev->bus, false); - sdio_release_host(sdiodev->func[1]); - atomic_set(&sdiodev->freezer->freezing, 0); - complete_all(&sdiodev->freezer->resumed); -} - -bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev) -{ - return atomic_read(&sdiodev->freezer->freezing); -} - -void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev) -{ - if (!brcmf_sdiod_freezing(sdiodev)) - return; - sdiodev->freezer->frozen_count++; - wake_up(&sdiodev->freezer->thread_freeze); - wait_for_completion(&sdiodev->freezer->resumed); -} - -void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev) -{ - atomic_inc(&sdiodev->freezer->thread_count); -} - -void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev) -{ - atomic_dec(&sdiodev->freezer->thread_count); -} -#else -static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev) -{ - return 0; -} - -static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev) -{ -} -#endif /* CONFIG_PM_SLEEP */ - -static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) -{ - sdiodev->state = BRCMF_SDIOD_DOWN; - if (sdiodev->bus) { - brcmf_sdio_remove(sdiodev->bus); - sdiodev->bus = NULL; - } - - brcmf_sdiod_freezer_detach(sdiodev); - - /* Disable Function 2 */ - sdio_claim_host(sdiodev->func[2]); - sdio_disable_func(sdiodev->func[2]); - sdio_release_host(sdiodev->func[2]); - - /* Disable Function 1 */ - sdio_claim_host(sdiodev->func[1]); - sdio_disable_func(sdiodev->func[1]); - sdio_release_host(sdiodev->func[1]); - - sg_free_table(&sdiodev->sgtable); - sdiodev->sbwad = 0; - - pm_runtime_allow(sdiodev->func[1]->card->host->parent); - return 0; -} - -static void brcmf_sdiod_host_fixup(struct mmc_host *host) -{ - /* runtime-pm powers off the device */ - pm_runtime_forbid(host->parent); - /* avoid removal detection upon resume */ - host->caps |= MMC_CAP_NONREMOVABLE; -} - -static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) -{ - struct sdio_func *func; - struct mmc_host *host; - uint max_blocks; - int ret = 0; - - sdiodev->num_funcs = 2; - - sdio_claim_host(sdiodev->func[1]); - - ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE); - if (ret) { - brcmf_err("Failed to set F1 blocksize\n"); - sdio_release_host(sdiodev->func[1]); - goto out; - } - ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE); - if (ret) { - brcmf_err("Failed to set F2 blocksize\n"); - sdio_release_host(sdiodev->func[1]); - goto out; - } - - /* increase F2 timeout */ - sdiodev->func[2]->enable_timeout = SDIO_WAIT_F2RDY; - - /* Enable Function 1 */ - ret = sdio_enable_func(sdiodev->func[1]); - sdio_release_host(sdiodev->func[1]); - if (ret) { - brcmf_err("Failed to enable F1: err=%d\n", ret); - goto out; - } - - /* - * determine host related variables after brcmf_sdiod_probe() - * as func->cur_blksize is properly set and F2 init has been - * completed successfully. - */ - func = sdiodev->func[2]; - host = func->card->host; - sdiodev->sg_support = host->max_segs > 1; - max_blocks = min_t(uint, host->max_blk_count, 511u); - sdiodev->max_request_size = min_t(uint, host->max_req_size, - max_blocks * func->cur_blksize); - sdiodev->max_segment_count = min_t(uint, host->max_segs, - SG_MAX_SINGLE_ALLOC); - sdiodev->max_segment_size = host->max_seg_size; - - /* allocate scatter-gather table. sg support - * will be disabled upon allocation failure. - */ - brcmf_sdiod_sgtable_alloc(sdiodev); - - ret = brcmf_sdiod_freezer_attach(sdiodev); - if (ret) - goto out; - - /* try to attach to the target device */ - sdiodev->bus = brcmf_sdio_probe(sdiodev); - if (!sdiodev->bus) { - ret = -ENODEV; - goto out; - } - brcmf_sdiod_host_fixup(host); -out: - if (ret) - brcmf_sdiod_remove(sdiodev); - - return ret; -} - -#define BRCMF_SDIO_DEVICE(dev_id) \ - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)} - -/* devices we support, null terminated */ -static const struct sdio_device_id brcmf_sdmmc_ids[] = { - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), - { /* end: all zeroes */ } -}; -MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); - -static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; - - -static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev, - int val) -{ -#if IS_ENABLED(CONFIG_ACPI) - struct acpi_device *adev; - - adev = ACPI_COMPANION(dev); - if (adev) - adev->flags.power_manageable = 0; -#endif -} - -static int brcmf_ops_sdio_probe(struct sdio_func *func, - const struct sdio_device_id *id) -{ - int err; - struct brcmf_sdio_dev *sdiodev; - struct brcmf_bus *bus_if; - struct device *dev; - - brcmf_dbg(SDIO, "Enter\n"); - brcmf_dbg(SDIO, "Class=%x\n", func->class); - brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); - brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); - brcmf_dbg(SDIO, "Function#: %d\n", func->num); - - dev = &func->dev; - /* prohibit ACPI power management for this device */ - brcmf_sdiod_acpi_set_power_manageable(dev, 0); - - /* Consume func num 1 but dont do anything with it. */ - if (func->num == 1) - return 0; - - /* Ignore anything but func 2 */ - if (func->num != 2) - return -ENODEV; - - bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL); - if (!bus_if) - return -ENOMEM; - sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL); - if (!sdiodev) { - kfree(bus_if); - return -ENOMEM; - } - - /* store refs to functions used. mmc_card does - * not hold the F0 function pointer. - */ - sdiodev->func[0] = kmemdup(func, sizeof(*func), GFP_KERNEL); - sdiodev->func[0]->num = 0; - sdiodev->func[1] = func->card->sdio_func[0]; - sdiodev->func[2] = func; - - sdiodev->bus_if = bus_if; - bus_if->bus_priv.sdio = sdiodev; - bus_if->proto_type = BRCMF_PROTO_BCDC; - dev_set_drvdata(&func->dev, bus_if); - dev_set_drvdata(&sdiodev->func[1]->dev, bus_if); - sdiodev->dev = &sdiodev->func[1]->dev; - sdiodev->pdata = brcmfmac_sdio_pdata; - - if (!sdiodev->pdata) - brcmf_of_probe(sdiodev); - -#ifdef CONFIG_PM_SLEEP - /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ - * is true or when platform data OOB irq is true). - */ - if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) && - ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) || - (sdiodev->pdata && sdiodev->pdata->oob_irq_supported))) - bus_if->wowl_supported = true; -#endif - - brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN); - - brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n"); - err = brcmf_sdiod_probe(sdiodev); - if (err) { - brcmf_err("F2 error, probe failed %d...\n", err); - goto fail; - } - - brcmf_dbg(SDIO, "F2 init completed...\n"); - return 0; - -fail: - dev_set_drvdata(&func->dev, NULL); - dev_set_drvdata(&sdiodev->func[1]->dev, NULL); - kfree(sdiodev->func[0]); - kfree(sdiodev); - kfree(bus_if); - return err; -} - -static void brcmf_ops_sdio_remove(struct sdio_func *func) -{ - struct brcmf_bus *bus_if; - struct brcmf_sdio_dev *sdiodev; - - brcmf_dbg(SDIO, "Enter\n"); - brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); - brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); - brcmf_dbg(SDIO, "Function: %d\n", func->num); - - if (func->num != 1) - return; - - bus_if = dev_get_drvdata(&func->dev); - if (bus_if) { - sdiodev = bus_if->bus_priv.sdio; - brcmf_sdiod_remove(sdiodev); - - dev_set_drvdata(&sdiodev->func[1]->dev, NULL); - dev_set_drvdata(&sdiodev->func[2]->dev, NULL); - - kfree(bus_if); - kfree(sdiodev->func[0]); - kfree(sdiodev); - } - - brcmf_dbg(SDIO, "Exit\n"); -} - -void brcmf_sdio_wowl_config(struct device *dev, bool enabled) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - - brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled); - sdiodev->wowl_enabled = enabled; -} - -#ifdef CONFIG_PM_SLEEP -static int brcmf_ops_sdio_suspend(struct device *dev) -{ - struct sdio_func *func; - struct brcmf_bus *bus_if; - struct brcmf_sdio_dev *sdiodev; - mmc_pm_flag_t sdio_flags; - - func = container_of(dev, struct sdio_func, dev); - brcmf_dbg(SDIO, "Enter: F%d\n", func->num); - if (func->num != SDIO_FUNC_1) - return 0; - - - bus_if = dev_get_drvdata(dev); - sdiodev = bus_if->bus_priv.sdio; - - brcmf_sdiod_freezer_on(sdiodev); - brcmf_sdio_wd_timer(sdiodev->bus, 0); - - sdio_flags = MMC_PM_KEEP_POWER; - if (sdiodev->wowl_enabled) { - if (sdiodev->pdata->oob_irq_supported) - enable_irq_wake(sdiodev->pdata->oob_irq_nr); - else - sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; - } - if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) - brcmf_err("Failed to set pm_flags %x\n", sdio_flags); - return 0; -} - -static int brcmf_ops_sdio_resume(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct sdio_func *func = container_of(dev, struct sdio_func, dev); - - brcmf_dbg(SDIO, "Enter: F%d\n", func->num); - if (func->num != SDIO_FUNC_2) - return 0; - - brcmf_sdiod_freezer_off(sdiodev); - return 0; -} - -static const struct dev_pm_ops brcmf_sdio_pm_ops = { - .suspend = brcmf_ops_sdio_suspend, - .resume = brcmf_ops_sdio_resume, -}; -#endif /* CONFIG_PM_SLEEP */ - -static struct sdio_driver brcmf_sdmmc_driver = { - .probe = brcmf_ops_sdio_probe, - .remove = brcmf_ops_sdio_remove, - .name = BRCMFMAC_SDIO_PDATA_NAME, - .id_table = brcmf_sdmmc_ids, - .drv = { - .owner = THIS_MODULE, -#ifdef CONFIG_PM_SLEEP - .pm = &brcmf_sdio_pm_ops, -#endif /* CONFIG_PM_SLEEP */ - }, -}; - -static int __init brcmf_sdio_pd_probe(struct platform_device *pdev) -{ - brcmf_dbg(SDIO, "Enter\n"); - - brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev); - - if (brcmfmac_sdio_pdata->power_on) - brcmfmac_sdio_pdata->power_on(); - - return 0; -} - -static int brcmf_sdio_pd_remove(struct platform_device *pdev) -{ - brcmf_dbg(SDIO, "Enter\n"); - - if (brcmfmac_sdio_pdata->power_off) - brcmfmac_sdio_pdata->power_off(); - - sdio_unregister_driver(&brcmf_sdmmc_driver); - - return 0; -} - -static struct platform_driver brcmf_sdio_pd = { - .remove = brcmf_sdio_pd_remove, - .driver = { - .name = BRCMFMAC_SDIO_PDATA_NAME, - } -}; - -void brcmf_sdio_register(void) -{ - int ret; - - ret = sdio_register_driver(&brcmf_sdmmc_driver); - if (ret) - brcmf_err("sdio_register_driver failed: %d\n", ret); -} - -void brcmf_sdio_exit(void) -{ - brcmf_dbg(SDIO, "Enter\n"); - - if (brcmfmac_sdio_pdata) - platform_driver_unregister(&brcmf_sdio_pd); - else - sdio_unregister_driver(&brcmf_sdmmc_driver); -} - -void __init brcmf_sdio_init(void) -{ - int ret; - - brcmf_dbg(SDIO, "Enter\n"); - - ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe); - if (ret == -ENODEV) - brcmf_dbg(SDIO, "No platform data available.\n"); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c deleted file mode 100644 index 4e33f96b3dd1..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * 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 -#include -#include -#include "core.h" -#include "debug.h" -#include "fwil.h" -#include "fwil_types.h" -#include "btcoex.h" -#include "p2p.h" -#include "cfg80211.h" - -/* T1 start SCO/eSCO priority suppression */ -#define BRCMF_BTCOEX_OPPR_WIN_TIME 2000 - -/* BT registers values during DHCP */ -#define BRCMF_BT_DHCP_REG50 0x8022 -#define BRCMF_BT_DHCP_REG51 0 -#define BRCMF_BT_DHCP_REG64 0 -#define BRCMF_BT_DHCP_REG65 0 -#define BRCMF_BT_DHCP_REG71 0 -#define BRCMF_BT_DHCP_REG66 0x2710 -#define BRCMF_BT_DHCP_REG41 0x33 -#define BRCMF_BT_DHCP_REG68 0x190 - -/* number of samples for SCO detection */ -#define BRCMF_BT_SCO_SAMPLES 12 - -/** -* enum brcmf_btcoex_state - BT coex DHCP state machine states -* @BRCMF_BT_DHCP_IDLE: DCHP is idle -* @BRCMF_BT_DHCP_START: DHCP started, wait before -* boosting wifi priority -* @BRCMF_BT_DHCP_OPPR_WIN: graceful DHCP opportunity ended, -* boost wifi priority -* @BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: wifi priority boost end, -* restore defaults -*/ -enum brcmf_btcoex_state { - BRCMF_BT_DHCP_IDLE, - BRCMF_BT_DHCP_START, - BRCMF_BT_DHCP_OPPR_WIN, - BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT -}; - -/** - * struct brcmf_btcoex_info - BT coex related information - * @vif: interface for which request was done. - * @timer: timer for DHCP state machine - * @timeout: configured timeout. - * @timer_on: DHCP timer active - * @dhcp_done: DHCP finished before T1/T2 timer expiration - * @bt_state: DHCP state machine state - * @work: DHCP state machine work - * @cfg: driver private data for cfg80211 interface - * @reg66: saved value of btc_params 66 - * @reg41: saved value of btc_params 41 - * @reg68: saved value of btc_params 68 - * @saved_regs_part1: flag indicating regs 66,41,68 - * have been saved - * @reg51: saved value of btc_params 51 - * @reg64: saved value of btc_params 64 - * @reg65: saved value of btc_params 65 - * @reg71: saved value of btc_params 71 - * @saved_regs_part1: flag indicating regs 50,51,64,65,71 - * have been saved - */ -struct brcmf_btcoex_info { - struct brcmf_cfg80211_vif *vif; - struct timer_list timer; - u16 timeout; - bool timer_on; - bool dhcp_done; - enum brcmf_btcoex_state bt_state; - struct work_struct work; - struct brcmf_cfg80211_info *cfg; - u32 reg66; - u32 reg41; - u32 reg68; - bool saved_regs_part1; - u32 reg50; - u32 reg51; - u32 reg64; - u32 reg65; - u32 reg71; - bool saved_regs_part2; -}; - -/** - * brcmf_btcoex_params_write() - write btc_params firmware variable - * @ifp: interface - * @addr: btc_params register number - * @data: data to write - */ -static s32 brcmf_btcoex_params_write(struct brcmf_if *ifp, u32 addr, u32 data) -{ - struct { - __le32 addr; - __le32 data; - } reg_write; - - reg_write.addr = cpu_to_le32(addr); - reg_write.data = cpu_to_le32(data); - return brcmf_fil_iovar_data_set(ifp, "btc_params", - ®_write, sizeof(reg_write)); -} - -/** - * brcmf_btcoex_params_read() - read btc_params firmware variable - * @ifp: interface - * @addr: btc_params register number - * @data: read data - */ -static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data) -{ - *data = addr; - - return brcmf_fil_iovar_int_get(ifp, "btc_params", data); -} - -/** - * brcmf_btcoex_boost_wifi() - control BT SCO/eSCO parameters - * @btci: BT coex info - * @trump_sco: - * true - set SCO/eSCO parameters for compatibility - * during DHCP window - * false - restore saved parameter values - * - * Enhanced BT COEX settings for eSCO compatibility during DHCP window - */ -static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, - bool trump_sco) -{ - struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0); - - if (trump_sco && !btci->saved_regs_part2) { - /* this should reduce eSCO agressive - * retransmit w/o breaking it - */ - - /* save current */ - brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n"); - brcmf_btcoex_params_read(ifp, 50, &btci->reg50); - brcmf_btcoex_params_read(ifp, 51, &btci->reg51); - brcmf_btcoex_params_read(ifp, 64, &btci->reg64); - brcmf_btcoex_params_read(ifp, 65, &btci->reg65); - brcmf_btcoex_params_read(ifp, 71, &btci->reg71); - - btci->saved_regs_part2 = true; - brcmf_dbg(INFO, - "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", - btci->reg50, btci->reg51, btci->reg64, - btci->reg65, btci->reg71); - - /* pacify the eSco */ - brcmf_btcoex_params_write(ifp, 50, BRCMF_BT_DHCP_REG50); - brcmf_btcoex_params_write(ifp, 51, BRCMF_BT_DHCP_REG51); - brcmf_btcoex_params_write(ifp, 64, BRCMF_BT_DHCP_REG64); - brcmf_btcoex_params_write(ifp, 65, BRCMF_BT_DHCP_REG65); - brcmf_btcoex_params_write(ifp, 71, BRCMF_BT_DHCP_REG71); - - } else if (btci->saved_regs_part2) { - /* restore previously saved bt params */ - brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n"); - brcmf_btcoex_params_write(ifp, 50, btci->reg50); - brcmf_btcoex_params_write(ifp, 51, btci->reg51); - brcmf_btcoex_params_write(ifp, 64, btci->reg64); - brcmf_btcoex_params_write(ifp, 65, btci->reg65); - brcmf_btcoex_params_write(ifp, 71, btci->reg71); - - brcmf_dbg(INFO, - "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", - btci->reg50, btci->reg51, btci->reg64, - btci->reg65, btci->reg71); - - btci->saved_regs_part2 = false; - } else { - brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n"); - } -} - -/** - * brcmf_btcoex_is_sco_active() - check if SCO/eSCO is active - * @ifp: interface - * - * return: true if SCO/eSCO session is active - */ -static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp) -{ - int ioc_res = 0; - bool res = false; - int sco_id_cnt = 0; - u32 param27; - int i; - - for (i = 0; i < BRCMF_BT_SCO_SAMPLES; i++) { - ioc_res = brcmf_btcoex_params_read(ifp, 27, ¶m27); - - if (ioc_res < 0) { - brcmf_err("ioc read btc params error\n"); - break; - } - - brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27); - - if ((param27 & 0x6) == 2) { /* count both sco & esco */ - sco_id_cnt++; - } - - if (sco_id_cnt > 2) { - brcmf_dbg(INFO, - "sco/esco detected, pkt id_cnt:%d samples:%d\n", - sco_id_cnt, i); - res = true; - break; - } - } - brcmf_dbg(TRACE, "exit: result=%d\n", res); - return res; -} - -/** - * btcmf_btcoex_save_part1() - save first step parameters. - */ -static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci) -{ - struct brcmf_if *ifp = btci->vif->ifp; - - if (!btci->saved_regs_part1) { - /* Retrieve and save original reg value */ - brcmf_btcoex_params_read(ifp, 66, &btci->reg66); - brcmf_btcoex_params_read(ifp, 41, &btci->reg41); - brcmf_btcoex_params_read(ifp, 68, &btci->reg68); - btci->saved_regs_part1 = true; - brcmf_dbg(INFO, - "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n", - btci->reg66, btci->reg41, - btci->reg68); - } -} - -/** - * brcmf_btcoex_restore_part1() - restore first step parameters. - */ -static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci) -{ - struct brcmf_if *ifp; - - if (btci->saved_regs_part1) { - btci->saved_regs_part1 = false; - ifp = btci->vif->ifp; - brcmf_btcoex_params_write(ifp, 66, btci->reg66); - brcmf_btcoex_params_write(ifp, 41, btci->reg41); - brcmf_btcoex_params_write(ifp, 68, btci->reg68); - brcmf_dbg(INFO, - "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n", - btci->reg66, btci->reg41, - btci->reg68); - } -} - -/** - * brcmf_btcoex_timerfunc() - BT coex timer callback - */ -static void brcmf_btcoex_timerfunc(ulong data) -{ - struct brcmf_btcoex_info *bt_local = (struct brcmf_btcoex_info *)data; - brcmf_dbg(TRACE, "enter\n"); - - bt_local->timer_on = false; - schedule_work(&bt_local->work); -} - -/** - * brcmf_btcoex_handler() - BT coex state machine work handler - * @work: work - */ -static void brcmf_btcoex_handler(struct work_struct *work) -{ - struct brcmf_btcoex_info *btci; - btci = container_of(work, struct brcmf_btcoex_info, work); - if (btci->timer_on) { - btci->timer_on = false; - del_timer_sync(&btci->timer); - } - - switch (btci->bt_state) { - case BRCMF_BT_DHCP_START: - /* DHCP started provide OPPORTUNITY window - to get DHCP address - */ - brcmf_dbg(INFO, "DHCP started\n"); - btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN; - if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) { - mod_timer(&btci->timer, btci->timer.expires); - } else { - btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME; - mod_timer(&btci->timer, - jiffies + - msecs_to_jiffies(BRCMF_BTCOEX_OPPR_WIN_TIME)); - } - btci->timer_on = true; - break; - - case BRCMF_BT_DHCP_OPPR_WIN: - if (btci->dhcp_done) { - brcmf_dbg(INFO, "DHCP done before T1 expiration\n"); - goto idle; - } - - /* DHCP is not over yet, start lowering BT priority */ - brcmf_dbg(INFO, "DHCP T1:%d expired\n", - BRCMF_BTCOEX_OPPR_WIN_TIME); - brcmf_btcoex_boost_wifi(btci, true); - - btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT; - mod_timer(&btci->timer, - jiffies + msecs_to_jiffies(btci->timeout)); - btci->timer_on = true; - break; - - case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: - if (btci->dhcp_done) - brcmf_dbg(INFO, "DHCP done before T2 expiration\n"); - else - brcmf_dbg(INFO, "DHCP T2:%d expired\n", - BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT); - - goto idle; - - default: - brcmf_err("invalid state=%d !!!\n", btci->bt_state); - goto idle; - } - - return; - -idle: - btci->bt_state = BRCMF_BT_DHCP_IDLE; - btci->timer_on = false; - brcmf_btcoex_boost_wifi(btci, false); - cfg80211_crit_proto_stopped(&btci->vif->wdev, GFP_KERNEL); - brcmf_btcoex_restore_part1(btci); - btci->vif = NULL; -} - -/** - * brcmf_btcoex_attach() - initialize BT coex data - * @cfg: driver private cfg80211 data - * - * return: 0 on success - */ -int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_btcoex_info *btci = NULL; - brcmf_dbg(TRACE, "enter\n"); - - btci = kmalloc(sizeof(struct brcmf_btcoex_info), GFP_KERNEL); - if (!btci) - return -ENOMEM; - - btci->bt_state = BRCMF_BT_DHCP_IDLE; - - /* Set up timer for BT */ - btci->timer_on = false; - btci->timeout = BRCMF_BTCOEX_OPPR_WIN_TIME; - init_timer(&btci->timer); - btci->timer.data = (ulong)btci; - btci->timer.function = brcmf_btcoex_timerfunc; - btci->cfg = cfg; - btci->saved_regs_part1 = false; - btci->saved_regs_part2 = false; - - INIT_WORK(&btci->work, brcmf_btcoex_handler); - - cfg->btcoex = btci; - return 0; -} - -/** - * brcmf_btcoex_detach - clean BT coex data - * @cfg: driver private cfg80211 data - */ -void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg) -{ - brcmf_dbg(TRACE, "enter\n"); - - if (!cfg->btcoex) - return; - - if (cfg->btcoex->timer_on) { - cfg->btcoex->timer_on = false; - del_timer_sync(&cfg->btcoex->timer); - } - - cancel_work_sync(&cfg->btcoex->work); - - brcmf_btcoex_boost_wifi(cfg->btcoex, false); - brcmf_btcoex_restore_part1(cfg->btcoex); - - kfree(cfg->btcoex); - cfg->btcoex = NULL; -} - -static void brcmf_btcoex_dhcp_start(struct brcmf_btcoex_info *btci) -{ - struct brcmf_if *ifp = btci->vif->ifp; - - btcmf_btcoex_save_part1(btci); - /* set new regs values */ - brcmf_btcoex_params_write(ifp, 66, BRCMF_BT_DHCP_REG66); - brcmf_btcoex_params_write(ifp, 41, BRCMF_BT_DHCP_REG41); - brcmf_btcoex_params_write(ifp, 68, BRCMF_BT_DHCP_REG68); - btci->dhcp_done = false; - btci->bt_state = BRCMF_BT_DHCP_START; - schedule_work(&btci->work); - brcmf_dbg(TRACE, "enable BT DHCP Timer\n"); -} - -static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci) -{ - /* Stop any bt timer because DHCP session is done */ - btci->dhcp_done = true; - if (btci->timer_on) { - brcmf_dbg(INFO, "disable BT DHCP Timer\n"); - btci->timer_on = false; - del_timer_sync(&btci->timer); - - /* schedule worker if transition to IDLE is needed */ - if (btci->bt_state != BRCMF_BT_DHCP_IDLE) { - brcmf_dbg(INFO, "bt_state:%d\n", - btci->bt_state); - schedule_work(&btci->work); - } - } else { - /* Restore original values */ - brcmf_btcoex_restore_part1(btci); - } -} - -/** - * brcmf_btcoex_set_mode - set BT coex mode - * @cfg: driver private cfg80211 data - * @mode: Wifi-Bluetooth coexistence mode - * - * return: 0 on success - */ -int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, - enum brcmf_btcoex_mode mode, u16 duration) -{ - struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy); - struct brcmf_btcoex_info *btci = cfg->btcoex; - struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0); - - switch (mode) { - case BRCMF_BTCOEX_DISABLED: - brcmf_dbg(INFO, "DHCP session starts\n"); - if (btci->bt_state != BRCMF_BT_DHCP_IDLE) - return -EBUSY; - /* Start BT timer only for SCO connection */ - if (brcmf_btcoex_is_sco_active(ifp)) { - btci->timeout = duration; - btci->vif = vif; - brcmf_btcoex_dhcp_start(btci); - } - break; - - case BRCMF_BTCOEX_ENABLED: - brcmf_dbg(INFO, "DHCP session ends\n"); - if (btci->bt_state != BRCMF_BT_DHCP_IDLE && - vif == btci->vif) { - brcmf_btcoex_dhcp_end(btci); - } - break; - default: - brcmf_dbg(INFO, "Unknown mode, ignored\n"); - } - return 0; -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h deleted file mode 100644 index 19647c68aa9e..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 WL_BTCOEX_H_ -#define WL_BTCOEX_H_ - -enum brcmf_btcoex_mode { - BRCMF_BTCOEX_DISABLED, - BRCMF_BTCOEX_ENABLED -}; - -int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg); -void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg); -int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, - enum brcmf_btcoex_mode mode, u16 duration); - -#endif /* WL_BTCOEX_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/brcm80211/brcmfmac/bus.h deleted file mode 100644 index 230cad788ace..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2010 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_BUS_H -#define BRCMFMAC_BUS_H - -#include "debug.h" - -/* IDs of the 6 default common rings of msgbuf protocol */ -#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 -#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT 1 -#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE 2 -#define BRCMF_D2H_MSGRING_TX_COMPLETE 3 -#define BRCMF_D2H_MSGRING_RX_COMPLETE 4 - -#define BRCMF_NROF_H2D_COMMON_MSGRINGS 2 -#define BRCMF_NROF_D2H_COMMON_MSGRINGS 3 -#define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \ - BRCMF_NROF_D2H_COMMON_MSGRINGS) - -/* The level of bus communication with the dongle */ -enum brcmf_bus_state { - BRCMF_BUS_DOWN, /* Not ready for frame transfers */ - BRCMF_BUS_UP /* Ready for frame transfers */ -}; - -/* The level of bus communication with the dongle */ -enum brcmf_bus_protocol_type { - BRCMF_PROTO_BCDC, - BRCMF_PROTO_MSGBUF -}; - -struct brcmf_bus_dcmd { - char *name; - char *param; - int param_len; - struct list_head list; -}; - -/** - * struct brcmf_bus_ops - bus callback operations. - * - * @preinit: execute bus/device specific dongle init commands (optional). - * @init: prepare for communication with dongle. - * @stop: clear pending frames, disable data flow. - * @txdata: send a data frame to the dongle. When the data - * has been transferred, the common driver must be - * notified using brcmf_txcomplete(). The common - * driver calls this function with interrupts - * disabled. - * @txctl: transmit a control request message to dongle. - * @rxctl: receive a control response message from dongle. - * @gettxq: obtain a reference of bus transmit queue (optional). - * @wowl_config: specify if dongle is configured for wowl when going to suspend - * @get_ramsize: obtain size of device memory. - * @get_memdump: obtain device memory dump in provided buffer. - * - * This structure provides an abstract interface towards the - * bus specific driver. For control messages to common driver - * will assure there is only one active transaction. Unless - * indicated otherwise these callbacks are mandatory. - */ -struct brcmf_bus_ops { - int (*preinit)(struct device *dev); - void (*stop)(struct device *dev); - int (*txdata)(struct device *dev, struct sk_buff *skb); - int (*txctl)(struct device *dev, unsigned char *msg, uint len); - int (*rxctl)(struct device *dev, unsigned char *msg, uint len); - struct pktq * (*gettxq)(struct device *dev); - void (*wowl_config)(struct device *dev, bool enabled); - size_t (*get_ramsize)(struct device *dev); - int (*get_memdump)(struct device *dev, void *data, size_t len); -}; - - -/** - * struct brcmf_bus_msgbuf - bus ringbuf if in case of msgbuf. - * - * @commonrings: commonrings which are always there. - * @flowrings: commonrings which are dynamically created and destroyed for data. - * @rx_dataoffset: if set then all rx data has this this offset. - * @max_rxbufpost: maximum number of buffers to post for rx. - * @nrof_flowrings: number of flowrings. - */ -struct brcmf_bus_msgbuf { - struct brcmf_commonring *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; - struct brcmf_commonring **flowrings; - u32 rx_dataoffset; - u32 max_rxbufpost; - u32 nrof_flowrings; -}; - - -/** - * struct brcmf_bus - interface structure between common and bus layer - * - * @bus_priv: pointer to private bus device. - * @proto_type: protocol type, bcdc or msgbuf - * @dev: device pointer of bus device. - * @drvr: public driver information. - * @state: operational state of the bus interface. - * @maxctl: maximum size for rxctl request message. - * @tx_realloc: number of tx packets realloced for headroom. - * @dstats: dongle-based statistical data. - * @dcmd_list: bus/device specific dongle initialization commands. - * @chip: device identifier of the dongle chip. - * @wowl_supported: is wowl supported by bus driver. - * @chiprev: revision of the dongle chip. - */ -struct brcmf_bus { - union { - struct brcmf_sdio_dev *sdio; - struct brcmf_usbdev *usb; - struct brcmf_pciedev *pcie; - } bus_priv; - enum brcmf_bus_protocol_type proto_type; - struct device *dev; - struct brcmf_pub *drvr; - enum brcmf_bus_state state; - uint maxctl; - unsigned long tx_realloc; - u32 chip; - u32 chiprev; - bool always_use_fws_queue; - bool wowl_supported; - - struct brcmf_bus_ops *ops; - struct brcmf_bus_msgbuf *msgbuf; -}; - -/* - * callback wrappers - */ -static inline int brcmf_bus_preinit(struct brcmf_bus *bus) -{ - if (!bus->ops->preinit) - return 0; - return bus->ops->preinit(bus->dev); -} - -static inline void brcmf_bus_stop(struct brcmf_bus *bus) -{ - bus->ops->stop(bus->dev); -} - -static inline int brcmf_bus_txdata(struct brcmf_bus *bus, struct sk_buff *skb) -{ - return bus->ops->txdata(bus->dev, skb); -} - -static inline -int brcmf_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint len) -{ - return bus->ops->txctl(bus->dev, msg, len); -} - -static inline -int brcmf_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint len) -{ - return bus->ops->rxctl(bus->dev, msg, len); -} - -static inline -struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus) -{ - if (!bus->ops->gettxq) - return ERR_PTR(-ENOENT); - - return bus->ops->gettxq(bus->dev); -} - -static inline -void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled) -{ - if (bus->ops->wowl_config) - bus->ops->wowl_config(bus->dev, enabled); -} - -static inline size_t brcmf_bus_get_ramsize(struct brcmf_bus *bus) -{ - if (!bus->ops->get_ramsize) - return 0; - - return bus->ops->get_ramsize(bus->dev); -} - -static inline -int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len) -{ - if (!bus->ops->get_memdump) - return -EOPNOTSUPP; - - return bus->ops->get_memdump(bus->dev, data, len); -} - -/* - * interface functions from common layer - */ - -bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, - int prec); - -/* Receive frame for delivery to OS. Callee disposes of rxp. */ -void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp); - -/* Indication from bus module regarding presence/insertion of dongle. */ -int brcmf_attach(struct device *dev); -/* Indication from bus module regarding removal/absence of dongle */ -void brcmf_detach(struct device *dev); -/* Indication from bus module that dongle should be reset */ -void brcmf_dev_reset(struct device *dev); -/* Indication from bus module to change flow-control state */ -void brcmf_txflowblock(struct device *dev, bool state); - -/* Notify the bus has transferred the tx packet to firmware */ -void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); - -/* Configure the "global" bus state used by upper layers */ -void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state); - -int brcmf_bus_start(struct device *dev); -s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len); -void brcmf_bus_add_txhdrlen(struct device *dev, uint len); - -#ifdef CONFIG_BRCMFMAC_SDIO -void brcmf_sdio_exit(void); -void brcmf_sdio_init(void); -void brcmf_sdio_register(void); -#endif -#ifdef CONFIG_BRCMFMAC_USB -void brcmf_usb_exit(void); -void brcmf_usb_register(void); -#endif - -#endif /* BRCMFMAC_BUS_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c deleted file mode 100644 index deb5f78dcacc..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ /dev/null @@ -1,6357 +0,0 @@ -/* - * Copyright (c) 2010 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. - */ - -/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "core.h" -#include "debug.h" -#include "tracepoint.h" -#include "fwil_types.h" -#include "p2p.h" -#include "btcoex.h" -#include "cfg80211.h" -#include "feature.h" -#include "fwil.h" -#include "proto.h" -#include "vendor.h" -#include "bus.h" -#include "common.h" - -#define BRCMF_SCAN_IE_LEN_MAX 2048 -#define BRCMF_PNO_VERSION 2 -#define BRCMF_PNO_TIME 30 -#define BRCMF_PNO_REPEAT 4 -#define BRCMF_PNO_FREQ_EXPO_MAX 3 -#define BRCMF_PNO_MAX_PFN_COUNT 16 -#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6 -#define BRCMF_PNO_HIDDEN_BIT 2 -#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF -#define BRCMF_PNO_SCAN_COMPLETE 1 -#define BRCMF_PNO_SCAN_INCOMPLETE 0 - -#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */ -#define WPA_OUI_TYPE 1 -#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */ -#define WME_OUI_TYPE 2 -#define WPS_OUI_TYPE 4 - -#define VS_IE_FIXED_HDR_LEN 6 -#define WPA_IE_VERSION_LEN 2 -#define WPA_IE_MIN_OUI_LEN 4 -#define WPA_IE_SUITE_COUNT_LEN 2 - -#define WPA_CIPHER_NONE 0 /* None */ -#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */ -#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */ -#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */ -#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */ - -#define RSN_AKM_NONE 0 /* None (IBSS) */ -#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */ -#define RSN_AKM_PSK 2 /* Pre-shared Key */ -#define RSN_CAP_LEN 2 /* Length of RSN capabilities */ -#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C - -#define VNDR_IE_CMD_LEN 4 /* length of the set command - * string :"add", "del" (+ NUL) - */ -#define VNDR_IE_COUNT_OFFSET 4 -#define VNDR_IE_PKTFLAG_OFFSET 8 -#define VNDR_IE_VSIE_OFFSET 12 -#define VNDR_IE_HDR_SIZE 12 -#define VNDR_IE_PARSE_LIMIT 5 - -#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */ -#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */ - -#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 -#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 -#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20 - -#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ - (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) - -static bool check_vif_up(struct brcmf_cfg80211_vif *vif) -{ - if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) { - brcmf_dbg(INFO, "device is not ready : status (%lu)\n", - vif->sme_state); - return false; - } - return true; -} - -#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) -#define RATETAB_ENT(_rateid, _flags) \ - { \ - .bitrate = RATE_TO_BASE100KBPS(_rateid), \ - .hw_value = (_rateid), \ - .flags = (_flags), \ - } - -static struct ieee80211_rate __wl_rates[] = { - RATETAB_ENT(BRCM_RATE_1M, 0), - RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(BRCM_RATE_6M, 0), - RATETAB_ENT(BRCM_RATE_9M, 0), - RATETAB_ENT(BRCM_RATE_12M, 0), - RATETAB_ENT(BRCM_RATE_18M, 0), - RATETAB_ENT(BRCM_RATE_24M, 0), - RATETAB_ENT(BRCM_RATE_36M, 0), - RATETAB_ENT(BRCM_RATE_48M, 0), - RATETAB_ENT(BRCM_RATE_54M, 0), -}; - -#define wl_g_rates (__wl_rates + 0) -#define wl_g_rates_size ARRAY_SIZE(__wl_rates) -#define wl_a_rates (__wl_rates + 4) -#define wl_a_rates_size (wl_g_rates_size - 4) - -#define CHAN2G(_channel, _freq) { \ - .band = IEEE80211_BAND_2GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_channel), \ - .flags = IEEE80211_CHAN_DISABLED, \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} - -#define CHAN5G(_channel) { \ - .band = IEEE80211_BAND_5GHZ, \ - .center_freq = 5000 + (5 * (_channel)), \ - .hw_value = (_channel), \ - .flags = IEEE80211_CHAN_DISABLED, \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} - -static struct ieee80211_channel __wl_2ghz_channels[] = { - CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427), - CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447), - CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467), - CHAN2G(13, 2472), CHAN2G(14, 2484) -}; - -static struct ieee80211_channel __wl_5ghz_channels[] = { - CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42), - CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56), - CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108), - CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128), - CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149), - CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165) -}; - -/* Band templates duplicated per wiphy. The channel info - * above is added to the band during setup. - */ -static const struct ieee80211_supported_band __wl_band_2ghz = { - .band = IEEE80211_BAND_2GHZ, - .bitrates = wl_g_rates, - .n_bitrates = wl_g_rates_size, -}; - -static const struct ieee80211_supported_band __wl_band_5ghz = { - .band = IEEE80211_BAND_5GHZ, - .bitrates = wl_a_rates, - .n_bitrates = wl_a_rates_size, -}; - -/* This is to override regulatory domains defined in cfg80211 module (reg.c) - * By default world regulatory domain defined in reg.c puts the flags - * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165). - * With respect to these flags, wpa_supplicant doesn't * start p2p - * operations on 5GHz channels. All the changes in world regulatory - * domain are to be done here. - */ -static const struct ieee80211_regdomain brcmf_regdom = { - .n_reg_rules = 4, - .alpha2 = "99", - .reg_rules = { - /* IEEE 802.11b/g, channels 1..11 */ - REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), - /* If any */ - /* IEEE 802.11 channel 14 - Only JP enables - * this and for 802.11b only - */ - REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), - /* IEEE 802.11a, channel 36..64 */ - REG_RULE(5150-10, 5350+10, 80, 6, 20, 0), - /* IEEE 802.11a, channel 100..165 */ - REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), } -}; - -static const u32 __wl_cipher_suites[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, - WLAN_CIPHER_SUITE_AES_CMAC, -}; - -/* Vendor specific ie. id = 221, oui and type defines exact ie */ -struct brcmf_vs_tlv { - u8 id; - u8 len; - u8 oui[3]; - u8 oui_type; -}; - -struct parsed_vndr_ie_info { - u8 *ie_ptr; - u32 ie_len; /* total length including id & length field */ - struct brcmf_vs_tlv vndrie; -}; - -struct parsed_vndr_ies { - u32 count; - struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; -}; - -static int brcmf_roamoff; -module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); -MODULE_PARM_DESC(roamoff, "do not use internal roaming engine"); - - -static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, - struct cfg80211_chan_def *ch) -{ - struct brcmu_chan ch_inf; - s32 primary_offset; - - brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n", - ch->chan->center_freq, ch->center_freq1, ch->width); - ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1); - primary_offset = ch->center_freq1 - ch->chan->center_freq; - switch (ch->width) { - case NL80211_CHAN_WIDTH_20: - case NL80211_CHAN_WIDTH_20_NOHT: - ch_inf.bw = BRCMU_CHAN_BW_20; - WARN_ON(primary_offset != 0); - break; - case NL80211_CHAN_WIDTH_40: - ch_inf.bw = BRCMU_CHAN_BW_40; - if (primary_offset < 0) - ch_inf.sb = BRCMU_CHAN_SB_U; - else - ch_inf.sb = BRCMU_CHAN_SB_L; - break; - case NL80211_CHAN_WIDTH_80: - ch_inf.bw = BRCMU_CHAN_BW_80; - if (primary_offset < 0) { - if (primary_offset < -CH_10MHZ_APART) - ch_inf.sb = BRCMU_CHAN_SB_UU; - else - ch_inf.sb = BRCMU_CHAN_SB_UL; - } else { - if (primary_offset > CH_10MHZ_APART) - ch_inf.sb = BRCMU_CHAN_SB_LL; - else - ch_inf.sb = BRCMU_CHAN_SB_LU; - } - break; - case NL80211_CHAN_WIDTH_80P80: - case NL80211_CHAN_WIDTH_160: - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - default: - WARN_ON_ONCE(1); - } - switch (ch->chan->band) { - case IEEE80211_BAND_2GHZ: - ch_inf.band = BRCMU_CHAN_BAND_2G; - break; - case IEEE80211_BAND_5GHZ: - ch_inf.band = BRCMU_CHAN_BAND_5G; - break; - case IEEE80211_BAND_60GHZ: - default: - WARN_ON_ONCE(1); - } - d11inf->encchspec(&ch_inf); - - return ch_inf.chspec; -} - -u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, - struct ieee80211_channel *ch) -{ - struct brcmu_chan ch_inf; - - ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq); - ch_inf.bw = BRCMU_CHAN_BW_20; - d11inf->encchspec(&ch_inf); - - return ch_inf.chspec; -} - -/* Traverse a string of 1-byte tag/1-byte length/variable-length value - * triples, returning a pointer to the substring whose first element - * matches tag - */ -const struct brcmf_tlv * -brcmf_parse_tlvs(const void *buf, int buflen, uint key) -{ - const struct brcmf_tlv *elt = buf; - int totlen = buflen; - - /* find tagged parameter */ - while (totlen >= TLV_HDR_LEN) { - int len = elt->len; - - /* validate remaining totlen */ - if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN))) - return elt; - - elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN)); - totlen -= (len + TLV_HDR_LEN); - } - - return NULL; -} - -/* Is any of the tlvs the expected entry? If - * not update the tlvs buffer pointer/length. - */ -static bool -brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, - const u8 *oui, u32 oui_len, u8 type) -{ - /* If the contents match the OUI and the type */ - if (ie[TLV_LEN_OFF] >= oui_len + 1 && - !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) && - type == ie[TLV_BODY_OFF + oui_len]) { - return true; - } - - if (tlvs == NULL) - return false; - /* point to the next ie */ - ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN; - /* calculate the length of the rest of the buffer */ - *tlvs_len -= (int)(ie - *tlvs); - /* update the pointer to the start of the buffer */ - *tlvs = ie; - - return false; -} - -static struct brcmf_vs_tlv * -brcmf_find_wpaie(const u8 *parse, u32 len) -{ - const struct brcmf_tlv *ie; - - while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { - if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len, - WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE)) - return (struct brcmf_vs_tlv *)ie; - } - return NULL; -} - -static struct brcmf_vs_tlv * -brcmf_find_wpsie(const u8 *parse, u32 len) -{ - const struct brcmf_tlv *ie; - - while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { - if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, - WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE)) - return (struct brcmf_vs_tlv *)ie; - } - return NULL; -} - -static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg, - struct brcmf_cfg80211_vif *vif, - enum nl80211_iftype new_type) -{ - int iftype_num[NUM_NL80211_IFTYPES]; - struct brcmf_cfg80211_vif *pos; - - memset(&iftype_num[0], 0, sizeof(iftype_num)); - list_for_each_entry(pos, &cfg->vif_list, list) - if (pos == vif) - iftype_num[new_type]++; - else - iftype_num[pos->wdev.iftype]++; - - return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); -} - -static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg, - enum nl80211_iftype new_type) -{ - int iftype_num[NUM_NL80211_IFTYPES]; - struct brcmf_cfg80211_vif *pos; - - memset(&iftype_num[0], 0, sizeof(iftype_num)); - list_for_each_entry(pos, &cfg->vif_list, list) - iftype_num[pos->wdev.iftype]++; - - iftype_num[new_type]++; - return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); -} - -static void convert_key_from_CPU(struct brcmf_wsec_key *key, - struct brcmf_wsec_key_le *key_le) -{ - key_le->index = cpu_to_le32(key->index); - key_le->len = cpu_to_le32(key->len); - key_le->algo = cpu_to_le32(key->algo); - key_le->flags = cpu_to_le32(key->flags); - key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi); - key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo); - key_le->iv_initialized = cpu_to_le32(key->iv_initialized); - memcpy(key_le->data, key->data, sizeof(key->data)); - memcpy(key_le->ea, key->ea, sizeof(key->ea)); -} - -static int -send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key) -{ - int err; - struct brcmf_wsec_key_le key_le; - - convert_key_from_CPU(key, &key_le); - - brcmf_netdev_wait_pend8021x(ifp); - - err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le, - sizeof(key_le)); - - if (err) - brcmf_err("wsec_key error (%d)\n", err); - return err; -} - -static s32 -brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) -{ - s32 err; - u32 mode; - - if (enable) - mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY; - else - mode = 0; - - /* Try to set and enable ARP offload feature, this may fail, then it */ - /* is simply not supported and err 0 will be returned */ - err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode); - if (err) { - brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n", - mode, err); - err = 0; - } else { - err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable); - if (err) { - brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n", - enable, err); - err = 0; - } else - brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n", - enable, mode); - } - - return err; -} - -static void -brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) -{ - struct brcmf_cfg80211_vif *vif; - struct brcmf_if *ifp; - - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - ifp = vif->ifp; - - if ((wdev->iftype == NL80211_IFTYPE_ADHOC) || - (wdev->iftype == NL80211_IFTYPE_AP) || - (wdev->iftype == NL80211_IFTYPE_P2P_GO)) - brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, - ADDR_DIRECT); - else - brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, - ADDR_INDIRECT); -} - -static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) -{ - struct brcmf_mbss_ssid_le mbss_ssid_le; - int bsscfgidx; - int err; - - memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); - bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr); - if (bsscfgidx < 0) - return bsscfgidx; - - mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); - mbss_ssid_le.SSID_len = cpu_to_le32(5); - sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx); - - err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, - sizeof(mbss_ssid_le)); - if (err < 0) - brcmf_err("setting ssid failed %d\n", err); - - return err; -} - -/** - * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS - * - * @wiphy: wiphy device of new interface. - * @name: name of the new interface. - * @flags: not used. - * @params: contains mac address for AP device. - */ -static -struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, - u32 *flags, struct vif_params *params) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - struct brcmf_cfg80211_vif *vif; - int err; - - if (brcmf_cfg80211_vif_event_armed(cfg)) - return ERR_PTR(-EBUSY); - - brcmf_dbg(INFO, "Adding vif \"%s\"\n", name); - - vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false); - if (IS_ERR(vif)) - return (struct wireless_dev *)vif; - - brcmf_cfg80211_arm_vif_event(cfg, vif); - - err = brcmf_cfg80211_request_ap_if(ifp); - if (err) { - brcmf_cfg80211_arm_vif_event(cfg, NULL); - goto fail; - } - - /* wait for firmware event */ - err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, - msecs_to_jiffies(1500)); - brcmf_cfg80211_arm_vif_event(cfg, NULL); - if (!err) { - brcmf_err("timeout occurred\n"); - err = -EIO; - goto fail; - } - - /* interface created in firmware */ - ifp = vif->ifp; - if (!ifp) { - brcmf_err("no if pointer provided\n"); - err = -ENOENT; - goto fail; - } - - strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); - err = brcmf_net_attach(ifp, true); - if (err) { - brcmf_err("Registering netdevice failed\n"); - goto fail; - } - - return &ifp->vif->wdev; - -fail: - brcmf_free_vif(vif); - return ERR_PTR(err); -} - -static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) -{ - enum nl80211_iftype iftype; - - iftype = vif->wdev.iftype; - return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO; -} - -static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) -{ - return vif->wdev.iftype == NL80211_IFTYPE_ADHOC; -} - -static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, - const char *name, - unsigned char name_assign_type, - enum nl80211_iftype type, - u32 *flags, - struct vif_params *params) -{ - struct wireless_dev *wdev; - int err; - - brcmf_dbg(TRACE, "enter: %s type %d\n", name, type); - err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type); - if (err) { - brcmf_err("iface validation failed: err=%d\n", err); - return ERR_PTR(err); - } - switch (type) { - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_MESH_POINT: - return ERR_PTR(-EOPNOTSUPP); - case NL80211_IFTYPE_AP: - wdev = brcmf_ap_add_vif(wiphy, name, flags, params); - if (!IS_ERR(wdev)) - brcmf_cfg80211_update_proto_addr_mode(wdev); - return wdev; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_P2P_DEVICE: - wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params); - if (!IS_ERR(wdev)) - brcmf_cfg80211_update_proto_addr_mode(wdev); - return wdev; - case NL80211_IFTYPE_UNSPECIFIED: - default: - return ERR_PTR(-EINVAL); - } -} - -static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc) -{ - if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC)) - brcmf_set_mpc(ifp, mpc); -} - -void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) -{ - s32 err = 0; - - if (check_vif_up(ifp->vif)) { - err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc); - if (err) { - brcmf_err("fail to set mpc\n"); - return; - } - brcmf_dbg(INFO, "MPC : %d\n", mpc); - } -} - -s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp, bool aborted, - bool fw_abort) -{ - struct brcmf_scan_params_le params_le; - struct cfg80211_scan_request *scan_request; - s32 err = 0; - - brcmf_dbg(SCAN, "Enter\n"); - - /* clear scan request, because the FW abort can cause a second call */ - /* to this functon and might cause a double cfg80211_scan_done */ - scan_request = cfg->scan_request; - cfg->scan_request = NULL; - - if (timer_pending(&cfg->escan_timeout)) - del_timer_sync(&cfg->escan_timeout); - - if (fw_abort) { - /* Do a scan abort to stop the driver's scan engine */ - brcmf_dbg(SCAN, "ABORT scan in firmware\n"); - memset(¶ms_le, 0, sizeof(params_le)); - eth_broadcast_addr(params_le.bssid); - params_le.bss_type = DOT11_BSSTYPE_ANY; - params_le.scan_type = 0; - params_le.channel_num = cpu_to_le32(1); - params_le.nprobes = cpu_to_le32(1); - params_le.active_time = cpu_to_le32(-1); - params_le.passive_time = cpu_to_le32(-1); - params_le.home_time = cpu_to_le32(-1); - /* Scan is aborted by setting channel_list[0] to -1 */ - params_le.channel_list[0] = cpu_to_le16(-1); - /* E-Scan (or anyother type) can be aborted by SCAN */ - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, - ¶ms_le, sizeof(params_le)); - if (err) - brcmf_err("Scan abort failed\n"); - } - - brcmf_scan_config_mpc(ifp, 1); - - /* - * e-scan can be initiated by scheduled scan - * which takes precedence. - */ - if (cfg->sched_escan) { - brcmf_dbg(SCAN, "scheduled scan completed\n"); - cfg->sched_escan = false; - if (!aborted) - cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); - } else if (scan_request) { - brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", - aborted ? "Aborted" : "Done"); - cfg80211_scan_done(scan_request, aborted); - } - if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) - brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n"); - - return err; -} - -static -int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct net_device *ndev = wdev->netdev; - - /* vif event pending in firmware */ - if (brcmf_cfg80211_vif_event_armed(cfg)) - return -EBUSY; - - if (ndev) { - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) && - cfg->escan_info.ifp == netdev_priv(ndev)) - brcmf_notify_escan_complete(cfg, netdev_priv(ndev), - true, true); - - brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1); - } - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_MESH_POINT: - return -EOPNOTSUPP; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_P2P_DEVICE: - return brcmf_p2p_del_vif(wiphy, wdev); - case NL80211_IFTYPE_UNSPECIFIED: - default: - return -EINVAL; - } - return -EOPNOTSUPP; -} - -static s32 -brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_vif *vif = ifp->vif; - s32 infra = 0; - s32 ap = 0; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type); - - /* WAR: There are a number of p2p interface related problems which - * need to be handled initially (before doing the validate). - * wpa_supplicant tends to do iface changes on p2p device/client/go - * which are not always possible/allowed. However we need to return - * OK otherwise the wpa_supplicant wont start. The situation differs - * on configuration and setup (p2pon=1 module param). The first check - * is to see if the request is a change to station for p2p iface. - */ - if ((type == NL80211_IFTYPE_STATION) && - ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) || - (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) || - (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) { - brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n"); - /* Now depending on whether module param p2pon=1 was used the - * response needs to be either 0 or EOPNOTSUPP. The reason is - * that if p2pon=1 is used, but a newer supplicant is used then - * we should return an error, as this combination wont work. - * In other situations 0 is returned and supplicant will start - * normally. It will give a trace in cfg80211, but it is the - * only way to get it working. Unfortunately this will result - * in situation where we wont support new supplicant in - * combination with module param p2pon=1, but that is the way - * it is. If the user tries this then unloading of driver might - * fail/lock. - */ - if (cfg->p2p.p2pdev_dynamically) - return -EOPNOTSUPP; - else - return 0; - } - err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type); - if (err) { - brcmf_err("iface validation failed: err=%d\n", err); - return err; - } - switch (type) { - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_WDS: - brcmf_err("type (%d) : currently we do not support this type\n", - type); - return -EOPNOTSUPP; - case NL80211_IFTYPE_ADHOC: - infra = 0; - break; - case NL80211_IFTYPE_STATION: - infra = 1; - break; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: - ap = 1; - break; - default: - err = -EINVAL; - goto done; - } - - if (ap) { - if (type == NL80211_IFTYPE_P2P_GO) { - brcmf_dbg(INFO, "IF Type = P2P GO\n"); - err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO); - } - if (!err) { - brcmf_dbg(INFO, "IF Type = AP\n"); - } - } else { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra); - if (err) { - brcmf_err("WLC_SET_INFRA error (%d)\n", err); - err = -EAGAIN; - goto done; - } - brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ? - "Adhoc" : "Infra"); - } - ndev->ieee80211_ptr->iftype = type; - - brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); - -done: - brcmf_dbg(TRACE, "Exit\n"); - - return err; -} - -static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg, - struct brcmf_scan_params_le *params_le, - struct cfg80211_scan_request *request) -{ - u32 n_ssids; - u32 n_channels; - s32 i; - s32 offset; - u16 chanspec; - char *ptr; - struct brcmf_ssid_le ssid_le; - - eth_broadcast_addr(params_le->bssid); - params_le->bss_type = DOT11_BSSTYPE_ANY; - params_le->scan_type = 0; - params_le->channel_num = 0; - params_le->nprobes = cpu_to_le32(-1); - params_le->active_time = cpu_to_le32(-1); - params_le->passive_time = cpu_to_le32(-1); - params_le->home_time = cpu_to_le32(-1); - memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le)); - - /* if request is null exit so it will be all channel broadcast scan */ - if (!request) - return; - - n_ssids = request->n_ssids; - n_channels = request->n_channels; - /* Copy channel array if applicable */ - brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n", - n_channels); - if (n_channels > 0) { - for (i = 0; i < n_channels; i++) { - chanspec = channel_to_chanspec(&cfg->d11inf, - request->channels[i]); - brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n", - request->channels[i]->hw_value, chanspec); - params_le->channel_list[i] = cpu_to_le16(chanspec); - } - } else { - brcmf_dbg(SCAN, "Scanning all channels\n"); - } - /* Copy ssid array if applicable */ - brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids); - if (n_ssids > 0) { - offset = offsetof(struct brcmf_scan_params_le, channel_list) + - n_channels * sizeof(u16); - offset = roundup(offset, sizeof(u32)); - ptr = (char *)params_le + offset; - for (i = 0; i < n_ssids; i++) { - memset(&ssid_le, 0, sizeof(ssid_le)); - ssid_le.SSID_len = - cpu_to_le32(request->ssids[i].ssid_len); - memcpy(ssid_le.SSID, request->ssids[i].ssid, - request->ssids[i].ssid_len); - if (!ssid_le.SSID_len) - brcmf_dbg(SCAN, "%d: Broadcast scan\n", i); - else - brcmf_dbg(SCAN, "%d: scan for %s size =%d\n", - i, ssid_le.SSID, ssid_le.SSID_len); - memcpy(ptr, &ssid_le, sizeof(ssid_le)); - ptr += sizeof(ssid_le); - } - } else { - brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids); - if ((request->ssids) && request->ssids->ssid_len) { - brcmf_dbg(SCAN, "SSID %s len=%d\n", - params_le->ssid_le.SSID, - request->ssids->ssid_len); - params_le->ssid_le.SSID_len = - cpu_to_le32(request->ssids->ssid_len); - memcpy(¶ms_le->ssid_le.SSID, request->ssids->ssid, - request->ssids->ssid_len); - } - } - /* Adding mask to channel numbers */ - params_le->channel_num = - cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) | - (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK)); -} - -static s32 -brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, - struct cfg80211_scan_request *request, u16 action) -{ - s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + - offsetof(struct brcmf_escan_params_le, params_le); - struct brcmf_escan_params_le *params; - s32 err = 0; - - brcmf_dbg(SCAN, "E-SCAN START\n"); - - if (request != NULL) { - /* Allocate space for populating ssids in struct */ - params_size += sizeof(u32) * ((request->n_channels + 1) / 2); - - /* Allocate space for populating ssids in struct */ - params_size += sizeof(struct brcmf_ssid) * request->n_ssids; - } - - params = kzalloc(params_size, GFP_KERNEL); - if (!params) { - err = -ENOMEM; - goto exit; - } - BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN); - brcmf_escan_prep(cfg, ¶ms->params_le, request); - params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); - params->action = cpu_to_le16(action); - params->sync_id = cpu_to_le16(0x1234); - - err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size); - if (err) { - if (err == -EBUSY) - brcmf_dbg(INFO, "system busy : escan canceled\n"); - else - brcmf_err("error (%d)\n", err); - } - - kfree(params); -exit: - return err; -} - -static s32 -brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, - struct brcmf_if *ifp, struct cfg80211_scan_request *request) -{ - s32 err; - u32 passive_scan; - struct brcmf_scan_results *results; - struct escan_info *escan = &cfg->escan_info; - - brcmf_dbg(SCAN, "Enter\n"); - escan->ifp = ifp; - escan->wiphy = wiphy; - escan->escan_state = WL_ESCAN_STATE_SCANNING; - passive_scan = cfg->active_scan ? 0 : 1; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, - passive_scan); - if (err) { - brcmf_err("error (%d)\n", err); - return err; - } - brcmf_scan_config_mpc(ifp, 0); - results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; - results->version = 0; - results->count = 0; - results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE; - - err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START); - if (err) - brcmf_scan_config_mpc(ifp, 1); - return err; -} - -static s32 -brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, - struct cfg80211_scan_request *request, - struct cfg80211_ssid *this_ssid) -{ - struct brcmf_if *ifp = vif->ifp; - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct cfg80211_ssid *ssids; - struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int; - u32 passive_scan; - bool escan_req; - bool spec_scan; - s32 err; - u32 SSID_len; - - brcmf_dbg(SCAN, "START ESCAN\n"); - - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) { - brcmf_err("Scanning being aborted: status (%lu)\n", - cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { - brcmf_err("Scanning suppressed: status (%lu)\n", - cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) { - brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state); - return -EAGAIN; - } - - /* If scan req comes for p2p0, send it over primary I/F */ - if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) - vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; - - escan_req = false; - if (request) { - /* scan bss */ - ssids = request->ssids; - escan_req = true; - } else { - /* scan in ibss */ - /* we don't do escan in ibss */ - ssids = this_ssid; - } - - cfg->scan_request = request; - set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - if (escan_req) { - cfg->escan_info.run = brcmf_run_escan; - err = brcmf_p2p_scan_prep(wiphy, request, vif); - if (err) - goto scan_out; - - err = brcmf_do_escan(cfg, wiphy, vif->ifp, request); - if (err) - goto scan_out; - } else { - brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n", - ssids->ssid, ssids->ssid_len); - memset(&sr->ssid_le, 0, sizeof(sr->ssid_le)); - SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len); - sr->ssid_le.SSID_len = cpu_to_le32(0); - spec_scan = false; - if (SSID_len) { - memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len); - sr->ssid_le.SSID_len = cpu_to_le32(SSID_len); - spec_scan = true; - } else - brcmf_dbg(SCAN, "Broadcast scan\n"); - - passive_scan = cfg->active_scan ? 0 : 1; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, - passive_scan); - if (err) { - brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err); - goto scan_out; - } - brcmf_scan_config_mpc(ifp, 0); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, - &sr->ssid_le, sizeof(sr->ssid_le)); - if (err) { - if (err == -EBUSY) - brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n", - sr->ssid_le.SSID); - else - brcmf_err("WLC_SCAN error (%d)\n", err); - - brcmf_scan_config_mpc(ifp, 1); - goto scan_out; - } - } - - /* Arm scan timeout timer */ - mod_timer(&cfg->escan_timeout, jiffies + - WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000); - - return 0; - -scan_out: - clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - cfg->scan_request = NULL; - return err; -} - -static s32 -brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) -{ - struct brcmf_cfg80211_vif *vif; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev); - if (!check_vif_up(vif)) - return -EIO; - - err = brcmf_cfg80211_escan(wiphy, vif, request, NULL); - - if (err) - brcmf_err("scan error (%d)\n", err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold) -{ - s32 err = 0; - - err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh", - rts_threshold); - if (err) - brcmf_err("Error (%d)\n", err); - - return err; -} - -static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold) -{ - s32 err = 0; - - err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh", - frag_threshold); - if (err) - brcmf_err("Error (%d)\n", err); - - return err; -} - -static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l) -{ - s32 err = 0; - u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL); - - err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry); - if (err) { - brcmf_err("cmd (%d) , error (%d)\n", cmd, err); - return err; - } - return err; -} - -static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - if (changed & WIPHY_PARAM_RTS_THRESHOLD && - (cfg->conf->rts_threshold != wiphy->rts_threshold)) { - cfg->conf->rts_threshold = wiphy->rts_threshold; - err = brcmf_set_rts(ndev, cfg->conf->rts_threshold); - if (!err) - goto done; - } - if (changed & WIPHY_PARAM_FRAG_THRESHOLD && - (cfg->conf->frag_threshold != wiphy->frag_threshold)) { - cfg->conf->frag_threshold = wiphy->frag_threshold; - err = brcmf_set_frag(ndev, cfg->conf->frag_threshold); - if (!err) - goto done; - } - if (changed & WIPHY_PARAM_RETRY_LONG - && (cfg->conf->retry_long != wiphy->retry_long)) { - cfg->conf->retry_long = wiphy->retry_long; - err = brcmf_set_retry(ndev, cfg->conf->retry_long, true); - if (!err) - goto done; - } - if (changed & WIPHY_PARAM_RETRY_SHORT - && (cfg->conf->retry_short != wiphy->retry_short)) { - cfg->conf->retry_short = wiphy->retry_short; - err = brcmf_set_retry(ndev, cfg->conf->retry_short, false); - if (!err) - goto done; - } - -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof) -{ - memset(prof, 0, sizeof(*prof)); -} - -static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e) -{ - u16 reason; - - switch (e->event_code) { - case BRCMF_E_DEAUTH: - case BRCMF_E_DEAUTH_IND: - case BRCMF_E_DISASSOC_IND: - reason = e->reason; - break; - case BRCMF_E_LINK: - default: - reason = 0; - break; - } - return reason; -} - -static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy); - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - - if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) { - brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n "); - err = brcmf_fil_cmd_data_set(vif->ifp, - BRCMF_C_DISASSOC, NULL, 0); - if (err) { - brcmf_err("WLC_DISASSOC failed (%d)\n", err); - } - clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state); - cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0, - true, GFP_KERNEL); - - } - clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); - clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); - brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); - brcmf_dbg(TRACE, "Exit\n"); -} - -static s32 -brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_ibss_params *params) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct brcmf_join_params join_params; - size_t join_params_size = 0; - s32 err = 0; - s32 wsec = 0; - s32 bcnprd; - u16 chanspec; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - if (params->ssid) - brcmf_dbg(CONN, "SSID: %s\n", params->ssid); - else { - brcmf_dbg(CONN, "SSID: NULL, Not supported\n"); - return -EOPNOTSUPP; - } - - set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); - - if (params->bssid) - brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid); - else - brcmf_dbg(CONN, "No BSSID specified\n"); - - if (params->chandef.chan) - brcmf_dbg(CONN, "channel: %d\n", - params->chandef.chan->center_freq); - else - brcmf_dbg(CONN, "no channel specified\n"); - - if (params->channel_fixed) - brcmf_dbg(CONN, "fixed channel required\n"); - else - brcmf_dbg(CONN, "no fixed channel required\n"); - - if (params->ie && params->ie_len) - brcmf_dbg(CONN, "ie len: %d\n", params->ie_len); - else - brcmf_dbg(CONN, "no ie specified\n"); - - if (params->beacon_interval) - brcmf_dbg(CONN, "beacon interval: %d\n", - params->beacon_interval); - else - brcmf_dbg(CONN, "no beacon interval specified\n"); - - if (params->basic_rates) - brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates); - else - brcmf_dbg(CONN, "no basic rates specified\n"); - - if (params->privacy) - brcmf_dbg(CONN, "privacy required\n"); - else - brcmf_dbg(CONN, "no privacy required\n"); - - /* Configure Privacy for starter */ - if (params->privacy) - wsec |= WEP_ENABLED; - - err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec); - if (err) { - brcmf_err("wsec failed (%d)\n", err); - goto done; - } - - /* Configure Beacon Interval for starter */ - if (params->beacon_interval) - bcnprd = params->beacon_interval; - else - bcnprd = 100; - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd); - if (err) { - brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err); - goto done; - } - - /* Configure required join parameter */ - memset(&join_params, 0, sizeof(struct brcmf_join_params)); - - /* SSID */ - profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32); - memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len); - memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len); - join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); - join_params_size = sizeof(join_params.ssid_le); - - /* BSSID */ - if (params->bssid) { - memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN); - join_params_size = sizeof(join_params.ssid_le) + - BRCMF_ASSOC_PARAMS_FIXED_SIZE; - memcpy(profile->bssid, params->bssid, ETH_ALEN); - } else { - eth_broadcast_addr(join_params.params_le.bssid); - eth_zero_addr(profile->bssid); - } - - /* Channel */ - if (params->chandef.chan) { - u32 target_channel; - - cfg->channel = - ieee80211_frequency_to_channel( - params->chandef.chan->center_freq); - if (params->channel_fixed) { - /* adding chanspec */ - chanspec = chandef_to_chanspec(&cfg->d11inf, - ¶ms->chandef); - join_params.params_le.chanspec_list[0] = - cpu_to_le16(chanspec); - join_params.params_le.chanspec_num = cpu_to_le32(1); - join_params_size += sizeof(join_params.params_le); - } - - /* set channel for starter */ - target_channel = cfg->channel; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL, - target_channel); - if (err) { - brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err); - goto done; - } - } else - cfg->channel = 0; - - cfg->ibss_starter = false; - - - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, - &join_params, join_params_size); - if (err) { - brcmf_err("WLC_SET_SSID failed (%d)\n", err); - goto done; - } - -done: - if (err) - clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING); - - brcmf_dbg(TRACE, "Exit\n"); - - return 0; -} - -static s32 brcmf_set_wpa_version(struct net_device *ndev, - struct cfg80211_connect_params *sme) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); - struct brcmf_cfg80211_security *sec; - s32 val = 0; - s32 err = 0; - - if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) - val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; - else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) - val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; - else - val = WPA_AUTH_DISABLED; - brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val); - if (err) { - brcmf_err("set wpa_auth failed (%d)\n", err); - return err; - } - sec = &profile->sec; - sec->wpa_versions = sme->crypto.wpa_versions; - return err; -} - -static s32 brcmf_set_auth_type(struct net_device *ndev, - struct cfg80211_connect_params *sme) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); - struct brcmf_cfg80211_security *sec; - s32 val = 0; - s32 err = 0; - - switch (sme->auth_type) { - case NL80211_AUTHTYPE_OPEN_SYSTEM: - val = 0; - brcmf_dbg(CONN, "open system\n"); - break; - case NL80211_AUTHTYPE_SHARED_KEY: - val = 1; - brcmf_dbg(CONN, "shared key\n"); - break; - case NL80211_AUTHTYPE_AUTOMATIC: - val = 2; - brcmf_dbg(CONN, "automatic\n"); - break; - case NL80211_AUTHTYPE_NETWORK_EAP: - brcmf_dbg(CONN, "network eap\n"); - default: - val = 2; - brcmf_err("invalid auth type (%d)\n", sme->auth_type); - break; - } - - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val); - if (err) { - brcmf_err("set auth failed (%d)\n", err); - return err; - } - sec = &profile->sec; - sec->auth_type = sme->auth_type; - return err; -} - -static s32 -brcmf_set_wsec_mode(struct net_device *ndev, - struct cfg80211_connect_params *sme, bool mfp) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); - struct brcmf_cfg80211_security *sec; - s32 pval = 0; - s32 gval = 0; - s32 wsec; - s32 err = 0; - - if (sme->crypto.n_ciphers_pairwise) { - switch (sme->crypto.ciphers_pairwise[0]) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - pval = WEP_ENABLED; - break; - case WLAN_CIPHER_SUITE_TKIP: - pval = TKIP_ENABLED; - break; - case WLAN_CIPHER_SUITE_CCMP: - pval = AES_ENABLED; - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - pval = AES_ENABLED; - break; - default: - brcmf_err("invalid cipher pairwise (%d)\n", - sme->crypto.ciphers_pairwise[0]); - return -EINVAL; - } - } - if (sme->crypto.cipher_group) { - switch (sme->crypto.cipher_group) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - gval = WEP_ENABLED; - break; - case WLAN_CIPHER_SUITE_TKIP: - gval = TKIP_ENABLED; - break; - case WLAN_CIPHER_SUITE_CCMP: - gval = AES_ENABLED; - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - gval = AES_ENABLED; - break; - default: - brcmf_err("invalid cipher group (%d)\n", - sme->crypto.cipher_group); - return -EINVAL; - } - } - - brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval); - /* In case of privacy, but no security and WPS then simulate */ - /* setting AES. WPS-2.0 allows no security */ - if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval && - sme->privacy) - pval = AES_ENABLED; - - if (mfp) - wsec = pval | gval | MFP_CAPABLE; - else - wsec = pval | gval; - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec); - if (err) { - brcmf_err("error (%d)\n", err); - return err; - } - - sec = &profile->sec; - sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; - sec->cipher_group = sme->crypto.cipher_group; - - return err; -} - -static s32 -brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); - struct brcmf_cfg80211_security *sec; - s32 val = 0; - s32 err = 0; - - if (sme->crypto.n_akm_suites) { - err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), - "wpa_auth", &val); - if (err) { - brcmf_err("could not get wpa_auth (%d)\n", err); - return err; - } - if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { - switch (sme->crypto.akm_suites[0]) { - case WLAN_AKM_SUITE_8021X: - val = WPA_AUTH_UNSPECIFIED; - break; - case WLAN_AKM_SUITE_PSK: - val = WPA_AUTH_PSK; - break; - default: - brcmf_err("invalid cipher group (%d)\n", - sme->crypto.cipher_group); - return -EINVAL; - } - } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { - switch (sme->crypto.akm_suites[0]) { - case WLAN_AKM_SUITE_8021X: - val = WPA2_AUTH_UNSPECIFIED; - break; - case WLAN_AKM_SUITE_PSK: - val = WPA2_AUTH_PSK; - break; - default: - brcmf_err("invalid cipher group (%d)\n", - sme->crypto.cipher_group); - return -EINVAL; - } - } - - brcmf_dbg(CONN, "setting wpa_auth to %d\n", val); - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), - "wpa_auth", val); - if (err) { - brcmf_err("could not set wpa_auth (%d)\n", err); - return err; - } - } - sec = &profile->sec; - sec->wpa_auth = sme->crypto.akm_suites[0]; - - return err; -} - -static s32 -brcmf_set_sharedkey(struct net_device *ndev, - struct cfg80211_connect_params *sme) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); - struct brcmf_cfg80211_security *sec; - struct brcmf_wsec_key key; - s32 val; - s32 err = 0; - - brcmf_dbg(CONN, "key len (%d)\n", sme->key_len); - - if (sme->key_len == 0) - return 0; - - sec = &profile->sec; - brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n", - sec->wpa_versions, sec->cipher_pairwise); - - if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)) - return 0; - - if (!(sec->cipher_pairwise & - (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104))) - return 0; - - memset(&key, 0, sizeof(key)); - key.len = (u32) sme->key_len; - key.index = (u32) sme->key_idx; - if (key.len > sizeof(key.data)) { - brcmf_err("Too long key length (%u)\n", key.len); - return -EINVAL; - } - memcpy(key.data, sme->key, key.len); - key.flags = BRCMF_PRIMARY_KEY; - switch (sec->cipher_pairwise) { - case WLAN_CIPHER_SUITE_WEP40: - key.algo = CRYPTO_ALGO_WEP1; - break; - case WLAN_CIPHER_SUITE_WEP104: - key.algo = CRYPTO_ALGO_WEP128; - break; - default: - brcmf_err("Invalid algorithm (%d)\n", - sme->crypto.ciphers_pairwise[0]); - return -EINVAL; - } - /* Set the new key/index */ - brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n", - key.len, key.index, key.algo); - brcmf_dbg(CONN, "key \"%s\"\n", key.data); - err = send_key_to_dongle(netdev_priv(ndev), &key); - if (err) - return err; - - if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { - brcmf_dbg(CONN, "set auth_type to shared key\n"); - val = WL_AUTH_SHARED_KEY; /* shared key */ - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val); - if (err) - brcmf_err("set auth failed (%d)\n", err); - } - return err; -} - -static -enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp, - enum nl80211_auth_type type) -{ - if (type == NL80211_AUTHTYPE_AUTOMATIC && - brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) { - brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n"); - type = NL80211_AUTHTYPE_OPEN_SYSTEM; - } - return type; -} - -static s32 -brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_connect_params *sme) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct ieee80211_channel *chan = sme->channel; - struct brcmf_join_params join_params; - size_t join_params_size; - const struct brcmf_tlv *rsn_ie; - const struct brcmf_vs_tlv *wpa_ie; - const void *ie; - u32 ie_len; - struct brcmf_ext_join_params_le *ext_join_params; - u16 chanspec; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - if (!sme->ssid) { - brcmf_err("Invalid ssid\n"); - return -EOPNOTSUPP; - } - - if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) { - /* A normal (non P2P) connection request setup. */ - ie = NULL; - ie_len = 0; - /* find the WPA_IE */ - wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len); - if (wpa_ie) { - ie = wpa_ie; - ie_len = wpa_ie->len + TLV_HDR_LEN; - } else { - /* find the RSN_IE */ - rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, - sme->ie_len, - WLAN_EID_RSN); - if (rsn_ie) { - ie = rsn_ie; - ie_len = rsn_ie->len + TLV_HDR_LEN; - } - } - brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len); - } - - err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG, - sme->ie, sme->ie_len); - if (err) - brcmf_err("Set Assoc REQ IE Failed\n"); - else - brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n"); - - set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); - - if (chan) { - cfg->channel = - ieee80211_frequency_to_channel(chan->center_freq); - chanspec = channel_to_chanspec(&cfg->d11inf, chan); - brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n", - cfg->channel, chan->center_freq, chanspec); - } else { - cfg->channel = 0; - chanspec = 0; - } - - brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len); - - err = brcmf_set_wpa_version(ndev, sme); - if (err) { - brcmf_err("wl_set_wpa_version failed (%d)\n", err); - goto done; - } - - sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type); - err = brcmf_set_auth_type(ndev, sme); - if (err) { - brcmf_err("wl_set_auth_type failed (%d)\n", err); - goto done; - } - - err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED); - if (err) { - brcmf_err("wl_set_set_cipher failed (%d)\n", err); - goto done; - } - - err = brcmf_set_key_mgmt(ndev, sme); - if (err) { - brcmf_err("wl_set_key_mgmt failed (%d)\n", err); - goto done; - } - - err = brcmf_set_sharedkey(ndev, sme); - if (err) { - brcmf_err("brcmf_set_sharedkey failed (%d)\n", err); - goto done; - } - - profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID), - (u32)sme->ssid_len); - memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len); - if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { - profile->ssid.SSID[profile->ssid.SSID_len] = 0; - brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID, - profile->ssid.SSID_len); - } - - /* Join with specific BSSID and cached SSID - * If SSID is zero join based on BSSID only - */ - join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) + - offsetof(struct brcmf_assoc_params_le, chanspec_list); - if (cfg->channel) - join_params_size += sizeof(u16); - ext_join_params = kzalloc(join_params_size, GFP_KERNEL); - if (ext_join_params == NULL) { - err = -ENOMEM; - goto done; - } - ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); - memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, - profile->ssid.SSID_len); - - /* Set up join scan parameters */ - ext_join_params->scan_le.scan_type = -1; - ext_join_params->scan_le.home_time = cpu_to_le32(-1); - - if (sme->bssid) - memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN); - else - eth_broadcast_addr(ext_join_params->assoc_le.bssid); - - if (cfg->channel) { - ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1); - - ext_join_params->assoc_le.chanspec_list[0] = - cpu_to_le16(chanspec); - /* Increase dwell time to receive probe response or detect - * beacon from target AP at a noisy air only during connect - * command. - */ - ext_join_params->scan_le.active_time = - cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); - ext_join_params->scan_le.passive_time = - cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); - /* To sync with presence period of VSDB GO send probe request - * more frequently. Probe request will be stopped when it gets - * probe response from target AP/GO. - */ - ext_join_params->scan_le.nprobes = - cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS / - BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS); - } else { - ext_join_params->scan_le.active_time = cpu_to_le32(-1); - ext_join_params->scan_le.passive_time = cpu_to_le32(-1); - ext_join_params->scan_le.nprobes = cpu_to_le32(-1); - } - - err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params, - join_params_size); - kfree(ext_join_params); - if (!err) - /* This is it. join command worked, we are done */ - goto done; - - /* join command failed, fallback to set ssid */ - memset(&join_params, 0, sizeof(join_params)); - join_params_size = sizeof(join_params.ssid_le); - - memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len); - join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); - - if (sme->bssid) - memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN); - else - eth_broadcast_addr(join_params.params_le.bssid); - - if (cfg->channel) { - join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec); - join_params.params_le.chanspec_num = cpu_to_le32(1); - join_params_size += sizeof(join_params.params_le); - } - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, - &join_params, join_params_size); - if (err) - brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err); - -done: - if (err) - clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, - u16 reason_code) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct brcmf_scb_val_le scbval; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code); - if (!check_vif_up(ifp->vif)) - return -EIO; - - clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); - clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); - cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL); - - memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); - scbval.val = cpu_to_le32(reason_code); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC, - &scbval, sizeof(scbval)); - if (err) - brcmf_err("error (%d)\n", err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, - enum nl80211_tx_power_setting type, s32 mbm) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err; - s32 disable; - u32 qdbm = 127; - - brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm); - if (!check_vif_up(ifp->vif)) - return -EIO; - - switch (type) { - case NL80211_TX_POWER_AUTOMATIC: - break; - case NL80211_TX_POWER_LIMITED: - case NL80211_TX_POWER_FIXED: - if (mbm < 0) { - brcmf_err("TX_POWER_FIXED - dbm is negative\n"); - err = -EINVAL; - goto done; - } - qdbm = MBM_TO_DBM(4 * mbm); - if (qdbm > 127) - qdbm = 127; - qdbm |= WL_TXPWR_OVERRIDE; - break; - default: - brcmf_err("Unsupported type %d\n", type); - err = -EINVAL; - goto done; - } - /* Make sure radio is off or on as far as software is concerned */ - disable = WL_RADIO_SW_DISABLE << 16; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable); - if (err) - brcmf_err("WLC_SET_RADIO error (%d)\n", err); - - err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm); - if (err) - brcmf_err("qtxpower error (%d)\n", err); - -done: - brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE); - return err; -} - -static s32 -brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, - s32 *dbm) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(ndev); - s32 qdbm = 0; - s32 err; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm); - if (err) { - brcmf_err("error (%d)\n", err); - goto done; - } - *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4; - -done: - brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm); - return err; -} - -static s32 -brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool unicast, bool multicast) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - u32 index; - u32 wsec; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(CONN, "key index (%d)\n", key_idx); - if (!check_vif_up(ifp->vif)) - return -EIO; - - err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); - if (err) { - brcmf_err("WLC_GET_WSEC error (%d)\n", err); - goto done; - } - - if (wsec & WEP_ENABLED) { - /* Just select a new current key */ - index = key_idx; - err = brcmf_fil_cmd_int_set(ifp, - BRCMF_C_SET_KEY_PRIMARY, index); - if (err) - brcmf_err("error (%d)\n", err); - } -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, const u8 *mac_addr, struct key_params *params) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_wsec_key key; - s32 err = 0; - u8 keybuf[8]; - - memset(&key, 0, sizeof(key)); - key.index = (u32) key_idx; - /* Instead of bcast for ea address for default wep keys, - driver needs it to be Null */ - if (!is_multicast_ether_addr(mac_addr)) - memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN); - key.len = (u32) params->key_len; - /* check for key index change */ - if (key.len == 0) { - /* key delete */ - err = send_key_to_dongle(ifp, &key); - if (err) - brcmf_err("key delete error (%d)\n", err); - } else { - if (key.len > sizeof(key.data)) { - brcmf_err("Invalid key length (%d)\n", key.len); - return -EINVAL; - } - - brcmf_dbg(CONN, "Setting the key index %d\n", key.index); - memcpy(key.data, params->key, key.len); - - if (!brcmf_is_apmode(ifp->vif) && - (params->cipher == WLAN_CIPHER_SUITE_TKIP)) { - brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); - memcpy(keybuf, &key.data[24], sizeof(keybuf)); - memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); - memcpy(&key.data[16], keybuf, sizeof(keybuf)); - } - - /* if IW_ENCODE_EXT_RX_SEQ_VALID set */ - if (params->seq && params->seq_len == 6) { - /* rx iv */ - u8 *ivptr; - ivptr = (u8 *) params->seq; - key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | - (ivptr[3] << 8) | ivptr[2]; - key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; - key.iv_initialized = true; - } - - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - key.algo = CRYPTO_ALGO_WEP1; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); - break; - case WLAN_CIPHER_SUITE_WEP104: - key.algo = CRYPTO_ALGO_WEP128; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); - break; - case WLAN_CIPHER_SUITE_TKIP: - key.algo = CRYPTO_ALGO_TKIP; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - key.algo = CRYPTO_ALGO_AES_CCM; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); - break; - case WLAN_CIPHER_SUITE_CCMP: - key.algo = CRYPTO_ALGO_AES_CCM; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); - break; - default: - brcmf_err("Invalid cipher (0x%x)\n", params->cipher); - return -EINVAL; - } - err = send_key_to_dongle(ifp, &key); - if (err) - brcmf_err("wsec_key error (%d)\n", err); - } - return err; -} - -static s32 -brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool pairwise, const u8 *mac_addr, - struct key_params *params) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_wsec_key *key; - s32 val; - s32 wsec; - s32 err = 0; - u8 keybuf[8]; - - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(CONN, "key index (%d)\n", key_idx); - if (!check_vif_up(ifp->vif)) - return -EIO; - - if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { - /* we ignore this key index in this case */ - brcmf_err("invalid key index (%d)\n", key_idx); - return -EINVAL; - } - - if (mac_addr && - (params->cipher != WLAN_CIPHER_SUITE_WEP40) && - (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { - brcmf_dbg(TRACE, "Exit"); - return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params); - } - - key = &ifp->vif->profile.key[key_idx]; - memset(key, 0, sizeof(*key)); - - if (params->key_len > sizeof(key->data)) { - brcmf_err("Too long key length (%u)\n", params->key_len); - err = -EINVAL; - goto done; - } - key->len = params->key_len; - key->index = key_idx; - - memcpy(key->data, params->key, key->len); - - key->flags = BRCMF_PRIMARY_KEY; - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - key->algo = CRYPTO_ALGO_WEP1; - val = WEP_ENABLED; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); - break; - case WLAN_CIPHER_SUITE_WEP104: - key->algo = CRYPTO_ALGO_WEP128; - val = WEP_ENABLED; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); - break; - case WLAN_CIPHER_SUITE_TKIP: - if (!brcmf_is_apmode(ifp->vif)) { - brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); - memcpy(keybuf, &key->data[24], sizeof(keybuf)); - memcpy(&key->data[24], &key->data[16], sizeof(keybuf)); - memcpy(&key->data[16], keybuf, sizeof(keybuf)); - } - key->algo = CRYPTO_ALGO_TKIP; - val = TKIP_ENABLED; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - key->algo = CRYPTO_ALGO_AES_CCM; - val = AES_ENABLED; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); - break; - case WLAN_CIPHER_SUITE_CCMP: - key->algo = CRYPTO_ALGO_AES_CCM; - val = AES_ENABLED; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); - break; - default: - brcmf_err("Invalid cipher (0x%x)\n", params->cipher); - err = -EINVAL; - goto done; - } - - err = send_key_to_dongle(ifp, key); - if (err) - goto done; - - err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); - if (err) { - brcmf_err("get wsec error (%d)\n", err); - goto done; - } - wsec |= val; - err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); - if (err) { - brcmf_err("set wsec error (%d)\n", err); - goto done; - } - -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool pairwise, const u8 *mac_addr) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_wsec_key key; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { - /* we ignore this key index in this case */ - return -EINVAL; - } - - memset(&key, 0, sizeof(key)); - - key.index = (u32) key_idx; - key.flags = BRCMF_PRIMARY_KEY; - key.algo = CRYPTO_ALGO_OFF; - - brcmf_dbg(CONN, "key index (%d)\n", key_idx); - - /* Set the new key/index */ - err = send_key_to_dongle(ifp, &key); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, - void (*callback) (void *cookie, struct key_params * params)) -{ - struct key_params params; - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct brcmf_cfg80211_security *sec; - s32 wsec; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(CONN, "key index (%d)\n", key_idx); - if (!check_vif_up(ifp->vif)) - return -EIO; - - memset(¶ms, 0, sizeof(params)); - - err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); - if (err) { - brcmf_err("WLC_GET_WSEC error (%d)\n", err); - /* Ignore this error, may happen during DISASSOC */ - err = -EAGAIN; - goto done; - } - if (wsec & WEP_ENABLED) { - sec = &profile->sec; - if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { - params.cipher = WLAN_CIPHER_SUITE_WEP40; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); - } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) { - params.cipher = WLAN_CIPHER_SUITE_WEP104; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); - } - } else if (wsec & TKIP_ENABLED) { - params.cipher = WLAN_CIPHER_SUITE_TKIP; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); - } else if (wsec & AES_ENABLED) { - params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); - } else { - brcmf_err("Invalid algo (0x%x)\n", wsec); - err = -EINVAL; - goto done; - } - callback(cookie, ¶ms); - -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, - struct net_device *ndev, u8 key_idx) -{ - brcmf_dbg(INFO, "Not supported\n"); - - return -EOPNOTSUPP; -} - -static void -brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp) -{ - s32 err; - u8 key_idx; - struct brcmf_wsec_key *key; - s32 wsec; - - for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) { - key = &ifp->vif->profile.key[key_idx]; - if ((key->algo == CRYPTO_ALGO_WEP1) || - (key->algo == CRYPTO_ALGO_WEP128)) - break; - } - if (key_idx == BRCMF_MAX_DEFAULT_KEYS) - return; - - err = send_key_to_dongle(ifp, key); - if (err) { - brcmf_err("Setting WEP key failed (%d)\n", err); - return; - } - err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); - if (err) { - brcmf_err("get wsec error (%d)\n", err); - return; - } - wsec |= WEP_ENABLED; - err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); - if (err) - brcmf_err("set wsec error (%d)\n", err); -} - -static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si) -{ - struct nl80211_sta_flag_update *sfu; - - brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags); - si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS); - sfu = &si->sta_flags; - sfu->mask = BIT(NL80211_STA_FLAG_WME) | - BIT(NL80211_STA_FLAG_AUTHENTICATED) | - BIT(NL80211_STA_FLAG_ASSOCIATED) | - BIT(NL80211_STA_FLAG_AUTHORIZED); - if (fw_sta_flags & BRCMF_STA_WME) - sfu->set |= BIT(NL80211_STA_FLAG_WME); - if (fw_sta_flags & BRCMF_STA_AUTHE) - sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); - if (fw_sta_flags & BRCMF_STA_ASSOC) - sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED); - if (fw_sta_flags & BRCMF_STA_AUTHO) - sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED); -} - -static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si) -{ - struct { - __le32 len; - struct brcmf_bss_info_le bss_le; - } *buf; - u16 capability; - int err; - - buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); - if (!buf) - return; - - buf->len = cpu_to_le32(WL_BSS_INFO_MAX); - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf, - WL_BSS_INFO_MAX); - if (err) { - brcmf_err("Failed to get bss info (%d)\n", err); - return; - } - si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); - si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period); - si->bss_param.dtim_period = buf->bss_le.dtim_period; - capability = le16_to_cpu(buf->bss_le.capability); - if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT) - si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; - if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE) - si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; - if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) - si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; -} - -static s32 -brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, - const u8 *mac, struct station_info *sinfo) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err = 0; - struct brcmf_sta_info_le sta_info_le; - u32 sta_flags; - u32 is_tdls_peer; - s32 total_rssi; - s32 count_rssi; - u32 i; - - brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac); - if (!check_vif_up(ifp->vif)) - return -EIO; - - memset(&sta_info_le, 0, sizeof(sta_info_le)); - memcpy(&sta_info_le, mac, ETH_ALEN); - err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info", - &sta_info_le, - sizeof(sta_info_le)); - is_tdls_peer = !err; - if (err) { - err = brcmf_fil_iovar_data_get(ifp, "sta_info", - &sta_info_le, - sizeof(sta_info_le)); - if (err < 0) { - brcmf_err("GET STA INFO failed, %d\n", err); - goto done; - } - } - brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver)); - sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME); - sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000; - sta_flags = le32_to_cpu(sta_info_le.flags); - brcmf_convert_sta_flags(sta_flags, sinfo); - sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER); - if (is_tdls_peer) - sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); - else - sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); - if (sta_flags & BRCMF_STA_ASSOC) { - sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME); - sinfo->connected_time = le32_to_cpu(sta_info_le.in); - brcmf_fill_bss_param(ifp, sinfo); - } - if (sta_flags & BRCMF_STA_SCBSTATS) { - sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED); - sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures); - sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS); - sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts); - sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts); - sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS); - sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts); - sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts); - if (sinfo->tx_packets) { - sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); - sinfo->txrate.legacy = - le32_to_cpu(sta_info_le.tx_rate) / 100; - } - if (sinfo->rx_packets) { - sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); - sinfo->rxrate.legacy = - le32_to_cpu(sta_info_le.rx_rate) / 100; - } - if (le16_to_cpu(sta_info_le.ver) >= 4) { - sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES); - sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes); - sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES); - sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes); - } - total_rssi = 0; - count_rssi = 0; - for (i = 0; i < BRCMF_ANT_MAX; i++) { - if (sta_info_le.rssi[i]) { - sinfo->chain_signal_avg[count_rssi] = - sta_info_le.rssi[i]; - sinfo->chain_signal[count_rssi] = - sta_info_le.rssi[i]; - total_rssi += sta_info_le.rssi[i]; - count_rssi++; - } - } - if (count_rssi) { - sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL); - sinfo->chains = count_rssi; - - sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); - total_rssi /= count_rssi; - sinfo->signal = total_rssi; - } - } -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static int -brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev, - int idx, u8 *mac, struct station_info *sinfo) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err; - - brcmf_dbg(TRACE, "Enter, idx %d\n", idx); - - if (idx == 0) { - cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST); - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST, - &cfg->assoclist, - sizeof(cfg->assoclist)); - if (err) { - brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n", - err); - cfg->assoclist.count = 0; - return -EOPNOTSUPP; - } - } - if (idx < le32_to_cpu(cfg->assoclist.count)) { - memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN); - return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo); - } - return -ENOENT; -} - -static s32 -brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, - bool enabled, s32 timeout) -{ - s32 pm; - s32 err = 0; - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - - brcmf_dbg(TRACE, "Enter\n"); - - /* - * Powersave enable/disable request is coming from the - * cfg80211 even before the interface is up. In that - * scenario, driver will be storing the power save - * preference in cfg struct to apply this to - * FW later while initializing the dongle - */ - cfg->pwr_save = enabled; - if (!check_vif_up(ifp->vif)) { - - brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n"); - goto done; - } - - pm = enabled ? PM_FAST : PM_OFF; - /* Do not enable the power save after assoc if it is a p2p interface */ - if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) { - brcmf_dbg(INFO, "Do not enable power save for P2P clients\n"); - pm = PM_OFF; - } - brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled")); - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm); - if (err) { - if (err == -ENODEV) - brcmf_err("net_device is not ready yet\n"); - else - brcmf_err("error (%d)\n", err); - } -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, - struct brcmf_bss_info_le *bi) -{ - struct wiphy *wiphy = cfg_to_wiphy(cfg); - struct ieee80211_channel *notify_channel; - struct cfg80211_bss *bss; - struct ieee80211_supported_band *band; - struct brcmu_chan ch; - u16 channel; - u32 freq; - u16 notify_capability; - u16 notify_interval; - u8 *notify_ie; - size_t notify_ielen; - s32 notify_signal; - - if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) { - brcmf_err("Bss info is larger than buffer. Discarding\n"); - return 0; - } - - if (!bi->ctl_ch) { - ch.chspec = le16_to_cpu(bi->chanspec); - cfg->d11inf.decchspec(&ch); - bi->ctl_ch = ch.chnum; - } - channel = bi->ctl_ch; - - if (channel <= CH_MAX_2G_CHANNEL) - band = wiphy->bands[IEEE80211_BAND_2GHZ]; - else - band = wiphy->bands[IEEE80211_BAND_5GHZ]; - - freq = ieee80211_channel_to_frequency(channel, band->band); - notify_channel = ieee80211_get_channel(wiphy, freq); - - notify_capability = le16_to_cpu(bi->capability); - notify_interval = le16_to_cpu(bi->beacon_period); - notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); - notify_ielen = le32_to_cpu(bi->ie_length); - notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; - - brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID); - brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq); - brcmf_dbg(CONN, "Capability: %X\n", notify_capability); - brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval); - brcmf_dbg(CONN, "Signal: %d\n", notify_signal); - - bss = cfg80211_inform_bss(wiphy, notify_channel, - CFG80211_BSS_FTYPE_UNKNOWN, - (const u8 *)bi->BSSID, - 0, notify_capability, - notify_interval, notify_ie, - notify_ielen, notify_signal, - GFP_KERNEL); - - if (!bss) - return -ENOMEM; - - cfg80211_put_bss(wiphy, bss); - - return 0; -} - -static struct brcmf_bss_info_le * -next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss) -{ - if (bss == NULL) - return list->bss_info_le; - return (struct brcmf_bss_info_le *)((unsigned long)bss + - le32_to_cpu(bss->length)); -} - -static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_scan_results *bss_list; - struct brcmf_bss_info_le *bi = NULL; /* must be initialized */ - s32 err = 0; - int i; - - bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; - if (bss_list->count != 0 && - bss_list->version != BRCMF_BSS_INFO_VERSION) { - brcmf_err("Version %d != WL_BSS_INFO_VERSION\n", - bss_list->version); - return -EOPNOTSUPP; - } - brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count); - for (i = 0; i < bss_list->count; i++) { - bi = next_bss_le(bss_list, bi); - err = brcmf_inform_single_bss(cfg, bi); - if (err) - break; - } - return err; -} - -static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, const u8 *bssid) -{ - struct wiphy *wiphy = cfg_to_wiphy(cfg); - struct ieee80211_channel *notify_channel; - struct brcmf_bss_info_le *bi = NULL; - struct ieee80211_supported_band *band; - struct cfg80211_bss *bss; - struct brcmu_chan ch; - u8 *buf = NULL; - s32 err = 0; - u32 freq; - u16 notify_capability; - u16 notify_interval; - u8 *notify_ie; - size_t notify_ielen; - s32 notify_signal; - - brcmf_dbg(TRACE, "Enter\n"); - - buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); - if (buf == NULL) { - err = -ENOMEM; - goto CleanUp; - } - - *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); - - err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO, - buf, WL_BSS_INFO_MAX); - if (err) { - brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err); - goto CleanUp; - } - - bi = (struct brcmf_bss_info_le *)(buf + 4); - - ch.chspec = le16_to_cpu(bi->chanspec); - cfg->d11inf.decchspec(&ch); - - if (ch.band == BRCMU_CHAN_BAND_2G) - band = wiphy->bands[IEEE80211_BAND_2GHZ]; - else - band = wiphy->bands[IEEE80211_BAND_5GHZ]; - - freq = ieee80211_channel_to_frequency(ch.chnum, band->band); - notify_channel = ieee80211_get_channel(wiphy, freq); - - notify_capability = le16_to_cpu(bi->capability); - notify_interval = le16_to_cpu(bi->beacon_period); - notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); - notify_ielen = le32_to_cpu(bi->ie_length); - notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; - - brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq); - brcmf_dbg(CONN, "capability: %X\n", notify_capability); - brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval); - brcmf_dbg(CONN, "signal: %d\n", notify_signal); - - bss = cfg80211_inform_bss(wiphy, notify_channel, - CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0, - notify_capability, notify_interval, - notify_ie, notify_ielen, notify_signal, - GFP_KERNEL); - - if (!bss) { - err = -ENOMEM; - goto CleanUp; - } - - cfg80211_put_bss(wiphy, bss); - -CleanUp: - - kfree(buf); - - brcmf_dbg(TRACE, "Exit\n"); - - return err; -} - -static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev); - struct brcmf_bss_info_le *bi; - struct brcmf_ssid *ssid; - const struct brcmf_tlv *tim; - u16 beacon_interval; - u8 dtim_period; - size_t ie_len; - u8 *ie; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (brcmf_is_ibssmode(ifp->vif)) - return err; - - ssid = &profile->ssid; - - *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX); - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, - cfg->extra_buf, WL_EXTRA_BUF_MAX); - if (err) { - brcmf_err("Could not get bss info %d\n", err); - goto update_bss_info_out; - } - - bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4); - err = brcmf_inform_single_bss(cfg, bi); - if (err) - goto update_bss_info_out; - - ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset); - ie_len = le32_to_cpu(bi->ie_length); - beacon_interval = le16_to_cpu(bi->beacon_period); - - tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM); - if (tim) - dtim_period = tim->data[1]; - else { - /* - * active scan was done so we could not get dtim - * information out of probe response. - * so we speficially query dtim information to dongle. - */ - u32 var; - err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var); - if (err) { - brcmf_err("wl dtim_assoc failed (%d)\n", err); - goto update_bss_info_out; - } - dtim_period = (u8)var; - } - -update_bss_info_out: - brcmf_dbg(TRACE, "Exit"); - return err; -} - -void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg) -{ - struct escan_info *escan = &cfg->escan_info; - - set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); - if (cfg->scan_request) { - escan->escan_state = WL_ESCAN_STATE_IDLE; - brcmf_notify_escan_complete(cfg, escan->ifp, true, true); - } - clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); -} - -static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work) -{ - struct brcmf_cfg80211_info *cfg = - container_of(work, struct brcmf_cfg80211_info, - escan_timeout_work); - - brcmf_inform_bss(cfg); - brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true); -} - -static void brcmf_escan_timeout(unsigned long data) -{ - struct brcmf_cfg80211_info *cfg = - (struct brcmf_cfg80211_info *)data; - - if (cfg->scan_request) { - brcmf_err("timer expired\n"); - schedule_work(&cfg->escan_timeout_work); - } -} - -static s32 -brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg, - struct brcmf_bss_info_le *bss, - struct brcmf_bss_info_le *bss_info_le) -{ - struct brcmu_chan ch_bss, ch_bss_info_le; - - ch_bss.chspec = le16_to_cpu(bss->chanspec); - cfg->d11inf.decchspec(&ch_bss); - ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec); - cfg->d11inf.decchspec(&ch_bss_info_le); - - if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) && - ch_bss.band == ch_bss_info_le.band && - bss_info_le->SSID_len == bss->SSID_len && - !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) { - if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == - (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) { - s16 bss_rssi = le16_to_cpu(bss->RSSI); - s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI); - - /* preserve max RSSI if the measurements are - * both on-channel or both off-channel - */ - if (bss_info_rssi > bss_rssi) - bss->RSSI = bss_info_le->RSSI; - } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) && - (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) { - /* preserve the on-channel rssi measurement - * if the new measurement is off channel - */ - bss->RSSI = bss_info_le->RSSI; - bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL; - } - return 1; - } - return 0; -} - -static s32 -brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - s32 status; - struct brcmf_escan_result_le *escan_result_le; - struct brcmf_bss_info_le *bss_info_le; - struct brcmf_bss_info_le *bss = NULL; - u32 bi_length; - struct brcmf_scan_results *list; - u32 i; - bool aborted; - - status = e->status; - - if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx); - return -EPERM; - } - - if (status == BRCMF_E_STATUS_PARTIAL) { - brcmf_dbg(SCAN, "ESCAN Partial result\n"); - escan_result_le = (struct brcmf_escan_result_le *) data; - if (!escan_result_le) { - brcmf_err("Invalid escan result (NULL pointer)\n"); - goto exit; - } - if (le16_to_cpu(escan_result_le->bss_count) != 1) { - brcmf_err("Invalid bss_count %d: ignoring\n", - escan_result_le->bss_count); - goto exit; - } - bss_info_le = &escan_result_le->bss_info_le; - - if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le)) - goto exit; - - if (!cfg->scan_request) { - brcmf_dbg(SCAN, "result without cfg80211 request\n"); - goto exit; - } - - bi_length = le32_to_cpu(bss_info_le->length); - if (bi_length != (le32_to_cpu(escan_result_le->buflen) - - WL_ESCAN_RESULTS_FIXED_SIZE)) { - brcmf_err("Invalid bss_info length %d: ignoring\n", - bi_length); - goto exit; - } - - if (!(cfg_to_wiphy(cfg)->interface_modes & - BIT(NL80211_IFTYPE_ADHOC))) { - if (le16_to_cpu(bss_info_le->capability) & - WLAN_CAPABILITY_IBSS) { - brcmf_err("Ignoring IBSS result\n"); - goto exit; - } - } - - list = (struct brcmf_scan_results *) - cfg->escan_info.escan_buf; - if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) { - brcmf_err("Buffer is too small: ignoring\n"); - goto exit; - } - - for (i = 0; i < list->count; i++) { - bss = bss ? (struct brcmf_bss_info_le *) - ((unsigned char *)bss + - le32_to_cpu(bss->length)) : list->bss_info_le; - if (brcmf_compare_update_same_bss(cfg, bss, - bss_info_le)) - goto exit; - } - memcpy(&(cfg->escan_info.escan_buf[list->buflen]), - bss_info_le, bi_length); - list->version = le32_to_cpu(bss_info_le->version); - list->buflen += bi_length; - list->count++; - } else { - cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - if (brcmf_p2p_scan_finding_common_channel(cfg, NULL)) - goto exit; - if (cfg->scan_request) { - brcmf_inform_bss(cfg); - aborted = status != BRCMF_E_STATUS_SUCCESS; - brcmf_notify_escan_complete(cfg, ifp, aborted, false); - } else - brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n", - status); - } -exit: - return 0; -} - -static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) -{ - brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT, - brcmf_cfg80211_escan_handler); - cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - /* Init scan_timeout timer */ - init_timer(&cfg->escan_timeout); - cfg->escan_timeout.data = (unsigned long) cfg; - cfg->escan_timeout.function = brcmf_escan_timeout; - INIT_WORK(&cfg->escan_timeout_work, - brcmf_cfg80211_escan_timeout_worker); -} - -static __always_inline void brcmf_delay(u32 ms) -{ - if (ms < 1000 / HZ) { - cond_resched(); - mdelay(ms); - } else { - msleep(ms); - } -} - -static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], - u8 *pattern, u32 patternsize, u8 *mask, - u32 packet_offset) -{ - struct brcmf_fil_wowl_pattern_le *filter; - u32 masksize; - u32 patternoffset; - u8 *buf; - u32 bufsize; - s32 ret; - - masksize = (patternsize + 7) / 8; - patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize; - - bufsize = sizeof(*filter) + patternsize + masksize; - buf = kzalloc(bufsize, GFP_KERNEL); - if (!buf) - return -ENOMEM; - filter = (struct brcmf_fil_wowl_pattern_le *)buf; - - memcpy(filter->cmd, cmd, 4); - filter->masksize = cpu_to_le32(masksize); - filter->offset = cpu_to_le32(packet_offset); - filter->patternoffset = cpu_to_le32(patternoffset); - filter->patternsize = cpu_to_le32(patternsize); - filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP); - - if ((mask) && (masksize)) - memcpy(buf + sizeof(*filter), mask, masksize); - if ((pattern) && (patternsize)) - memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize); - - ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize); - - kfree(buf); - return ret; -} - -static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(ndev); - - brcmf_dbg(TRACE, "Enter\n"); - - if (cfg->wowl_enabled) { - brcmf_configure_arp_offload(ifp, true); - brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, - cfg->pre_wowl_pmmode); - brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); - brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); - cfg->wowl_enabled = false; - } - return 0; -} - -static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp, - struct cfg80211_wowlan *wowl) -{ - u32 wowl_config; - u32 i; - - brcmf_dbg(TRACE, "Suspend, wowl config.\n"); - - brcmf_configure_arp_offload(ifp, false); - brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode); - brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); - - wowl_config = 0; - if (wowl->disconnect) - wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR; - if (wowl->magic_pkt) - wowl_config |= BRCMF_WOWL_MAGIC; - if ((wowl->patterns) && (wowl->n_patterns)) { - wowl_config |= BRCMF_WOWL_NET; - for (i = 0; i < wowl->n_patterns; i++) { - brcmf_config_wowl_pattern(ifp, "add", - (u8 *)wowl->patterns[i].pattern, - wowl->patterns[i].pattern_len, - (u8 *)wowl->patterns[i].mask, - wowl->patterns[i].pkt_offset); - } - } - brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); - brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); - brcmf_bus_wowl_config(cfg->pub->bus_if, true); - cfg->wowl_enabled = true; -} - -static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, - struct cfg80211_wowlan *wowl) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_vif *vif; - - brcmf_dbg(TRACE, "Enter\n"); - - /* if the primary net_device is not READY there is nothing - * we can do but pray resume goes smoothly. - */ - if (!check_vif_up(ifp->vif)) - goto exit; - - /* end any scanning */ - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) - brcmf_abort_scanning(cfg); - - if (wowl == NULL) { - brcmf_bus_wowl_config(cfg->pub->bus_if, false); - list_for_each_entry(vif, &cfg->vif_list, list) { - if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) - continue; - /* While going to suspend if associated with AP - * disassociate from AP to save power while system is - * in suspended state - */ - brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED); - /* Make sure WPA_Supplicant receives all the event - * generated due to DISASSOC call to the fw to keep - * the state fw and WPA_Supplicant state consistent - */ - brcmf_delay(500); - } - /* Configure MPC */ - brcmf_set_mpc(ifp, 1); - - } else { - /* Configure WOWL paramaters */ - brcmf_configure_wowl(cfg, ifp, wowl); - } - -exit: - brcmf_dbg(TRACE, "Exit\n"); - /* clear any scanning activity */ - cfg->scan_status = 0; - return 0; -} - -static __used s32 -brcmf_update_pmklist(struct net_device *ndev, - struct brcmf_cfg80211_pmk_list *pmk_list, s32 err) -{ - int i, j; - u32 pmkid_len; - - pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid); - - brcmf_dbg(CONN, "No of elements %d\n", pmkid_len); - for (i = 0; i < pmkid_len; i++) { - brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i, - &pmk_list->pmkids.pmkid[i].BSSID); - for (j = 0; j < WLAN_PMKID_LEN; j++) - brcmf_dbg(CONN, "%02x\n", - pmk_list->pmkids.pmkid[i].PMKID[j]); - } - - if (!err) - brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info", - (char *)pmk_list, sizeof(*pmk_list)); - - return err; -} - -static s32 -brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_pmksa *pmksa) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct pmkid_list *pmkids = &cfg->pmk_list->pmkids; - s32 err = 0; - u32 pmkid_len, i; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - pmkid_len = le32_to_cpu(pmkids->npmkid); - for (i = 0; i < pmkid_len; i++) - if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN)) - break; - if (i < WL_NUM_PMKIDS_MAX) { - memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN); - memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); - if (i == pmkid_len) { - pmkid_len++; - pmkids->npmkid = cpu_to_le32(pmkid_len); - } - } else - err = -EINVAL; - - brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n", - pmkids->pmkid[pmkid_len].BSSID); - for (i = 0; i < WLAN_PMKID_LEN; i++) - brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]); - - err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_pmksa *pmksa) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct pmkid_list pmkid; - s32 err = 0; - u32 pmkid_len, i; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN); - memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); - - brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n", - &pmkid.pmkid[0].BSSID); - for (i = 0; i < WLAN_PMKID_LEN; i++) - brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]); - - pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid); - for (i = 0; i < pmkid_len; i++) - if (!memcmp - (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID, - ETH_ALEN)) - break; - - if ((pmkid_len > 0) - && (i < pmkid_len)) { - memset(&cfg->pmk_list->pmkids.pmkid[i], 0, - sizeof(struct pmkid)); - for (; i < (pmkid_len - 1); i++) { - memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID, - &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID, - ETH_ALEN); - memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID, - &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID, - WLAN_PMKID_LEN); - } - cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1); - } else - err = -EINVAL; - - err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; - -} - -static s32 -brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list)); - err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; - -} - -/* - * PFN result doesn't have all the info which are - * required by the supplicant - * (For e.g IEs) Do a target Escan so that sched scan results are reported - * via wl_inform_single_bss in the required format. Escan does require the - * scan request in the form of cfg80211_scan_request. For timebeing, create - * cfg80211_scan_request one out of the received PNO event. - */ -static s32 -brcmf_notify_sched_scan_results(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct brcmf_pno_net_info_le *netinfo, *netinfo_start; - struct cfg80211_scan_request *request = NULL; - struct cfg80211_ssid *ssid = NULL; - struct ieee80211_channel *channel = NULL; - struct wiphy *wiphy = cfg_to_wiphy(cfg); - int err = 0; - int channel_req = 0; - int band = 0; - struct brcmf_pno_scanresults_le *pfn_result; - u32 result_count; - u32 status; - - brcmf_dbg(SCAN, "Enter\n"); - - if (e->event_code == BRCMF_E_PFN_NET_LOST) { - brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n"); - return 0; - } - - pfn_result = (struct brcmf_pno_scanresults_le *)data; - result_count = le32_to_cpu(pfn_result->count); - status = le32_to_cpu(pfn_result->status); - - /* - * PFN event is limited to fit 512 bytes so we may get - * multiple NET_FOUND events. For now place a warning here. - */ - WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); - brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count); - if (result_count > 0) { - int i; - - request = kzalloc(sizeof(*request), GFP_KERNEL); - ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL); - channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL); - if (!request || !ssid || !channel) { - err = -ENOMEM; - goto out_err; - } - - request->wiphy = wiphy; - data += sizeof(struct brcmf_pno_scanresults_le); - netinfo_start = (struct brcmf_pno_net_info_le *)data; - - for (i = 0; i < result_count; i++) { - netinfo = &netinfo_start[i]; - if (!netinfo) { - brcmf_err("Invalid netinfo ptr. index: %d\n", - i); - err = -EINVAL; - goto out_err; - } - - brcmf_dbg(SCAN, "SSID:%s Channel:%d\n", - netinfo->SSID, netinfo->channel); - memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); - ssid[i].ssid_len = netinfo->SSID_len; - request->n_ssids++; - - channel_req = netinfo->channel; - if (channel_req <= CH_MAX_2G_CHANNEL) - band = NL80211_BAND_2GHZ; - else - band = NL80211_BAND_5GHZ; - channel[i].center_freq = - ieee80211_channel_to_frequency(channel_req, - band); - channel[i].band = band; - channel[i].flags |= IEEE80211_CHAN_NO_HT40; - request->channels[i] = &channel[i]; - request->n_channels++; - } - - /* assign parsed ssid array */ - if (request->n_ssids) - request->ssids = &ssid[0]; - - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - /* Abort any on-going scan */ - brcmf_abort_scanning(cfg); - } - - 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); - goto out_err; - } - cfg->sched_escan = true; - cfg->scan_request = request; - } else { - brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); - goto out_err; - } - - kfree(ssid); - kfree(channel); - kfree(request); - return 0; - -out_err: - kfree(ssid); - kfree(channel); - kfree(request); - cfg80211_sched_scan_stopped(wiphy); - return err; -} - -static int brcmf_dev_pno_clean(struct net_device *ndev) -{ - int ret; - - /* Disable pfn */ - ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0); - if (ret == 0) { - /* clear pfn */ - ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear", - NULL, 0); - } - if (ret < 0) - brcmf_err("failed code %d\n", ret); - - return ret; -} - -static int brcmf_dev_pno_config(struct net_device *ndev) -{ - struct brcmf_pno_param_le pfn_param; - - memset(&pfn_param, 0, sizeof(pfn_param)); - pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); - - /* set extra pno params */ - pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); - pfn_param.repeat = BRCMF_PNO_REPEAT; - pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; - - /* set up pno scan fr */ - pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); - - return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set", - &pfn_param, sizeof(pfn_param)); -} - -static int -brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, - struct net_device *ndev, - struct cfg80211_sched_scan_request *request) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct brcmf_pno_net_param_le pfn; - int i; - int ret = 0; - - brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", - request->n_match_sets, request->n_ssids); - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { - brcmf_err("Scanning suppressed: status (%lu)\n", - cfg->scan_status); - return -EAGAIN; - } - - if (!request->n_ssids || !request->n_match_sets) { - brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", - request->n_ssids); - return -EINVAL; - } - - if (request->n_ssids > 0) { - for (i = 0; i < request->n_ssids; i++) { - /* Active scan req for ssids */ - brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n", - request->ssids[i].ssid); - - /* - * match_set ssids is a supert set of n_ssid list, - * so we need not add these set seperately. - */ - } - } - - if (request->n_match_sets > 0) { - /* clean up everything */ - ret = brcmf_dev_pno_clean(ndev); - if (ret < 0) { - brcmf_err("failed error=%d\n", ret); - return ret; - } - - /* configure pno */ - ret = brcmf_dev_pno_config(ndev); - if (ret < 0) { - brcmf_err("PNO setup failed!! ret=%d\n", ret); - return -EINVAL; - } - - /* configure each match set */ - for (i = 0; i < request->n_match_sets; i++) { - struct cfg80211_ssid *ssid; - u32 ssid_len; - - ssid = &request->match_sets[i].ssid; - ssid_len = ssid->ssid_len; - - if (!ssid_len) { - brcmf_err("skip broadcast ssid\n"); - continue; - } - pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); - pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); - pfn.wsec = cpu_to_le32(0); - pfn.infra = cpu_to_le32(1); - pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); - pfn.ssid.SSID_len = cpu_to_le32(ssid_len); - memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len); - ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, - sizeof(pfn)); - brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", - ret == 0 ? "set" : "failed", ssid->ssid); - } - /* Enable the PNO */ - if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) { - brcmf_err("PNO enable failed!! ret=%d\n", ret); - return -EINVAL; - } - } else { - return -EINVAL; - } - - return 0; -} - -static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, - struct net_device *ndev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - - brcmf_dbg(SCAN, "enter\n"); - brcmf_dev_pno_clean(ndev); - if (cfg->sched_escan) - brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true); - return 0; -} - -static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp) -{ - s32 err; - - /* set auth */ - err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0); - if (err < 0) { - brcmf_err("auth error %d\n", err); - return err; - } - /* set wsec */ - err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0); - if (err < 0) { - brcmf_err("wsec error %d\n", err); - return err; - } - /* set upper-layer auth */ - err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE); - if (err < 0) { - brcmf_err("wpa_auth error %d\n", err); - return err; - } - - return 0; -} - -static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) -{ - if (is_rsn_ie) - return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0); - - return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0); -} - -static s32 -brcmf_configure_wpaie(struct brcmf_if *ifp, - const struct brcmf_vs_tlv *wpa_ie, - bool is_rsn_ie) -{ - u32 auth = 0; /* d11 open authentication */ - u16 count; - s32 err = 0; - s32 len = 0; - u32 i; - u32 wsec; - u32 pval = 0; - u32 gval = 0; - u32 wpa_auth = 0; - u32 offset; - u8 *data; - u16 rsn_cap; - u32 wme_bss_disable; - - brcmf_dbg(TRACE, "Enter\n"); - if (wpa_ie == NULL) - goto exit; - - len = wpa_ie->len + TLV_HDR_LEN; - data = (u8 *)wpa_ie; - offset = TLV_HDR_LEN; - if (!is_rsn_ie) - offset += VS_IE_FIXED_HDR_LEN; - else - offset += WPA_IE_VERSION_LEN; - - /* check for multicast cipher suite */ - if (offset + WPA_IE_MIN_OUI_LEN > len) { - err = -EINVAL; - brcmf_err("no multicast cipher suite\n"); - goto exit; - } - - if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { - err = -EINVAL; - brcmf_err("ivalid OUI\n"); - goto exit; - } - offset += TLV_OUI_LEN; - - /* pick up multicast cipher */ - switch (data[offset]) { - case WPA_CIPHER_NONE: - gval = 0; - break; - case WPA_CIPHER_WEP_40: - case WPA_CIPHER_WEP_104: - gval = WEP_ENABLED; - break; - case WPA_CIPHER_TKIP: - gval = TKIP_ENABLED; - break; - case WPA_CIPHER_AES_CCM: - gval = AES_ENABLED; - break; - default: - err = -EINVAL; - brcmf_err("Invalid multi cast cipher info\n"); - goto exit; - } - - offset++; - /* walk thru unicast cipher list and pick up what we recognize */ - count = data[offset] + (data[offset + 1] << 8); - offset += WPA_IE_SUITE_COUNT_LEN; - /* Check for unicast suite(s) */ - if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) { - err = -EINVAL; - brcmf_err("no unicast cipher suite\n"); - goto exit; - } - for (i = 0; i < count; i++) { - if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { - err = -EINVAL; - brcmf_err("ivalid OUI\n"); - goto exit; - } - offset += TLV_OUI_LEN; - switch (data[offset]) { - case WPA_CIPHER_NONE: - break; - case WPA_CIPHER_WEP_40: - case WPA_CIPHER_WEP_104: - pval |= WEP_ENABLED; - break; - case WPA_CIPHER_TKIP: - pval |= TKIP_ENABLED; - break; - case WPA_CIPHER_AES_CCM: - pval |= AES_ENABLED; - break; - default: - brcmf_err("Ivalid unicast security info\n"); - } - offset++; - } - /* walk thru auth management suite list and pick up what we recognize */ - count = data[offset] + (data[offset + 1] << 8); - offset += WPA_IE_SUITE_COUNT_LEN; - /* Check for auth key management suite(s) */ - if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) { - err = -EINVAL; - brcmf_err("no auth key mgmt suite\n"); - goto exit; - } - for (i = 0; i < count; i++) { - if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { - err = -EINVAL; - brcmf_err("ivalid OUI\n"); - goto exit; - } - offset += TLV_OUI_LEN; - switch (data[offset]) { - case RSN_AKM_NONE: - brcmf_dbg(TRACE, "RSN_AKM_NONE\n"); - wpa_auth |= WPA_AUTH_NONE; - break; - case RSN_AKM_UNSPECIFIED: - brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n"); - is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) : - (wpa_auth |= WPA_AUTH_UNSPECIFIED); - break; - case RSN_AKM_PSK: - brcmf_dbg(TRACE, "RSN_AKM_PSK\n"); - is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) : - (wpa_auth |= WPA_AUTH_PSK); - break; - default: - brcmf_err("Ivalid key mgmt info\n"); - } - offset++; - } - - if (is_rsn_ie) { - wme_bss_disable = 1; - if ((offset + RSN_CAP_LEN) <= len) { - rsn_cap = data[offset] + (data[offset + 1] << 8); - if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK) - wme_bss_disable = 0; - } - /* set wme_bss_disable to sync RSN Capabilities */ - err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable", - wme_bss_disable); - if (err < 0) { - brcmf_err("wme_bss_disable error %d\n", err); - goto exit; - } - } - /* FOR WPS , set SES_OW_ENABLED */ - wsec = (pval | gval | SES_OW_ENABLED); - - /* set auth */ - err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth); - if (err < 0) { - brcmf_err("auth error %d\n", err); - goto exit; - } - /* set wsec */ - err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); - if (err < 0) { - brcmf_err("wsec error %d\n", err); - goto exit; - } - /* set upper-layer auth */ - err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth); - if (err < 0) { - brcmf_err("wpa_auth error %d\n", err); - goto exit; - } - -exit: - return err; -} - -static s32 -brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len, - struct parsed_vndr_ies *vndr_ies) -{ - struct brcmf_vs_tlv *vndrie; - struct brcmf_tlv *ie; - struct parsed_vndr_ie_info *parsed_info; - s32 remaining_len; - - remaining_len = (s32)vndr_ie_len; - memset(vndr_ies, 0, sizeof(*vndr_ies)); - - ie = (struct brcmf_tlv *)vndr_ie_buf; - while (ie) { - if (ie->id != WLAN_EID_VENDOR_SPECIFIC) - goto next; - vndrie = (struct brcmf_vs_tlv *)ie; - /* len should be bigger than OUI length + one */ - if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) { - brcmf_err("invalid vndr ie. length is too small %d\n", - vndrie->len); - goto next; - } - /* if wpa or wme ie, do not add ie */ - if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) && - ((vndrie->oui_type == WPA_OUI_TYPE) || - (vndrie->oui_type == WME_OUI_TYPE))) { - brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n"); - goto next; - } - - parsed_info = &vndr_ies->ie_info[vndr_ies->count]; - - /* save vndr ie information */ - parsed_info->ie_ptr = (char *)vndrie; - parsed_info->ie_len = vndrie->len + TLV_HDR_LEN; - memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie)); - - vndr_ies->count++; - - brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n", - parsed_info->vndrie.oui[0], - parsed_info->vndrie.oui[1], - parsed_info->vndrie.oui[2], - parsed_info->vndrie.oui_type); - - if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT) - break; -next: - remaining_len -= (ie->len + TLV_HDR_LEN); - if (remaining_len <= TLV_HDR_LEN) - ie = NULL; - else - ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len + - TLV_HDR_LEN); - } - return 0; -} - -static u32 -brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd) -{ - - strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1); - iebuf[VNDR_IE_CMD_LEN - 1] = '\0'; - - put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]); - - put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]); - - memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len); - - return ie_len + VNDR_IE_HDR_SIZE; -} - -s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, - const u8 *vndr_ie_buf, u32 vndr_ie_len) -{ - struct brcmf_if *ifp; - struct vif_saved_ie *saved_ie; - s32 err = 0; - u8 *iovar_ie_buf; - u8 *curr_ie_buf; - u8 *mgmt_ie_buf = NULL; - int mgmt_ie_buf_len; - u32 *mgmt_ie_len; - u32 del_add_ie_buf_len = 0; - u32 total_ie_buf_len = 0; - u32 parsed_ie_buf_len = 0; - struct parsed_vndr_ies old_vndr_ies; - struct parsed_vndr_ies new_vndr_ies; - struct parsed_vndr_ie_info *vndrie_info; - s32 i; - u8 *ptr; - int remained_buf_len; - - if (!vif) - return -ENODEV; - ifp = vif->ifp; - saved_ie = &vif->saved_ie; - - brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag); - iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); - if (!iovar_ie_buf) - return -ENOMEM; - curr_ie_buf = iovar_ie_buf; - switch (pktflag) { - case BRCMF_VNDR_IE_PRBREQ_FLAG: - mgmt_ie_buf = saved_ie->probe_req_ie; - mgmt_ie_len = &saved_ie->probe_req_ie_len; - mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie); - break; - case BRCMF_VNDR_IE_PRBRSP_FLAG: - mgmt_ie_buf = saved_ie->probe_res_ie; - mgmt_ie_len = &saved_ie->probe_res_ie_len; - mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie); - break; - case BRCMF_VNDR_IE_BEACON_FLAG: - mgmt_ie_buf = saved_ie->beacon_ie; - mgmt_ie_len = &saved_ie->beacon_ie_len; - mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie); - break; - case BRCMF_VNDR_IE_ASSOCREQ_FLAG: - mgmt_ie_buf = saved_ie->assoc_req_ie; - mgmt_ie_len = &saved_ie->assoc_req_ie_len; - mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie); - break; - default: - err = -EPERM; - brcmf_err("not suitable type\n"); - goto exit; - } - - if (vndr_ie_len > mgmt_ie_buf_len) { - err = -ENOMEM; - brcmf_err("extra IE size too big\n"); - goto exit; - } - - /* parse and save new vndr_ie in curr_ie_buff before comparing it */ - if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) { - ptr = curr_ie_buf; - brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies); - for (i = 0; i < new_vndr_ies.count; i++) { - vndrie_info = &new_vndr_ies.ie_info[i]; - memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr, - vndrie_info->ie_len); - parsed_ie_buf_len += vndrie_info->ie_len; - } - } - - if (mgmt_ie_buf && *mgmt_ie_len) { - if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) && - (memcmp(mgmt_ie_buf, curr_ie_buf, - parsed_ie_buf_len) == 0)) { - brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n"); - goto exit; - } - - /* parse old vndr_ie */ - brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies); - - /* make a command to delete old ie */ - for (i = 0; i < old_vndr_ies.count; i++) { - vndrie_info = &old_vndr_ies.ie_info[i]; - - brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n", - vndrie_info->vndrie.id, - vndrie_info->vndrie.len, - vndrie_info->vndrie.oui[0], - vndrie_info->vndrie.oui[1], - vndrie_info->vndrie.oui[2]); - - del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag, - vndrie_info->ie_ptr, - vndrie_info->ie_len, - "del"); - curr_ie_buf += del_add_ie_buf_len; - total_ie_buf_len += del_add_ie_buf_len; - } - } - - *mgmt_ie_len = 0; - /* Add if there is any extra IE */ - if (mgmt_ie_buf && parsed_ie_buf_len) { - ptr = mgmt_ie_buf; - - remained_buf_len = mgmt_ie_buf_len; - - /* make a command to add new ie */ - for (i = 0; i < new_vndr_ies.count; i++) { - vndrie_info = &new_vndr_ies.ie_info[i]; - - /* verify remained buf size before copy data */ - if (remained_buf_len < (vndrie_info->vndrie.len + - VNDR_IE_VSIE_OFFSET)) { - brcmf_err("no space in mgmt_ie_buf: len left %d", - remained_buf_len); - break; - } - remained_buf_len -= (vndrie_info->ie_len + - VNDR_IE_VSIE_OFFSET); - - brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n", - vndrie_info->vndrie.id, - vndrie_info->vndrie.len, - vndrie_info->vndrie.oui[0], - vndrie_info->vndrie.oui[1], - vndrie_info->vndrie.oui[2]); - - del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag, - vndrie_info->ie_ptr, - vndrie_info->ie_len, - "add"); - - /* save the parsed IE in wl struct */ - memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr, - vndrie_info->ie_len); - *mgmt_ie_len += vndrie_info->ie_len; - - curr_ie_buf += del_add_ie_buf_len; - total_ie_buf_len += del_add_ie_buf_len; - } - } - if (total_ie_buf_len) { - err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf, - total_ie_buf_len); - if (err) - brcmf_err("vndr ie set error : %d\n", err); - } - -exit: - kfree(iovar_ie_buf); - return err; -} - -s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif) -{ - s32 pktflags[] = { - BRCMF_VNDR_IE_PRBREQ_FLAG, - BRCMF_VNDR_IE_PRBRSP_FLAG, - BRCMF_VNDR_IE_BEACON_FLAG - }; - int i; - - for (i = 0; i < ARRAY_SIZE(pktflags); i++) - brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0); - - memset(&vif->saved_ie, 0, sizeof(vif->saved_ie)); - return 0; -} - -static s32 -brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif, - struct cfg80211_beacon_data *beacon) -{ - s32 err; - - /* Set Beacon IEs to FW */ - err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG, - beacon->tail, beacon->tail_len); - if (err) { - brcmf_err("Set Beacon IE Failed\n"); - return err; - } - brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n"); - - /* Set Probe Response IEs to FW */ - err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG, - beacon->proberesp_ies, - beacon->proberesp_ies_len); - if (err) - brcmf_err("Set Probe Resp IE Failed\n"); - else - brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n"); - - return err; -} - -static s32 -brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_ap_settings *settings) -{ - s32 ie_offset; - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - const struct brcmf_tlv *ssid_ie; - const struct brcmf_tlv *country_ie; - struct brcmf_ssid_le ssid_le; - s32 err = -EPERM; - const struct brcmf_tlv *rsn_ie; - const struct brcmf_vs_tlv *wpa_ie; - struct brcmf_join_params join_params; - enum nl80211_iftype dev_role; - struct brcmf_fil_bss_enable_le bss_enable; - u16 chanspec; - bool mbss; - int is_11d; - - brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", - settings->chandef.chan->hw_value, - settings->chandef.center_freq1, settings->chandef.width, - settings->beacon_interval, settings->dtim_period); - brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", - settings->ssid, settings->ssid_len, settings->auth_type, - settings->inactivity_timeout); - dev_role = ifp->vif->wdev.iftype; - mbss = ifp->vif->mbss; - - /* store current 11d setting */ - brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d); - country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, - settings->beacon.tail_len, - WLAN_EID_COUNTRY); - is_11d = country_ie ? 1 : 0; - - memset(&ssid_le, 0, sizeof(ssid_le)); - if (settings->ssid == NULL || settings->ssid_len == 0) { - ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; - ssid_ie = brcmf_parse_tlvs( - (u8 *)&settings->beacon.head[ie_offset], - settings->beacon.head_len - ie_offset, - WLAN_EID_SSID); - if (!ssid_ie) - return -EINVAL; - - memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len); - ssid_le.SSID_len = cpu_to_le32(ssid_ie->len); - brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID); - } else { - memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len); - ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len); - } - - if (!mbss) { - brcmf_set_mpc(ifp, 0); - brcmf_configure_arp_offload(ifp, false); - } - - /* find the RSN_IE */ - rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, - settings->beacon.tail_len, WLAN_EID_RSN); - - /* find the WPA_IE */ - wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail, - settings->beacon.tail_len); - - if ((wpa_ie != NULL || rsn_ie != NULL)) { - brcmf_dbg(TRACE, "WPA(2) IE is found\n"); - if (wpa_ie != NULL) { - /* WPA IE */ - err = brcmf_configure_wpaie(ifp, wpa_ie, false); - if (err < 0) - goto exit; - } else { - struct brcmf_vs_tlv *tmp_ie; - - tmp_ie = (struct brcmf_vs_tlv *)rsn_ie; - - /* RSN IE */ - err = brcmf_configure_wpaie(ifp, tmp_ie, true); - if (err < 0) - goto exit; - } - } else { - brcmf_dbg(TRACE, "No WPA(2) IEs found\n"); - brcmf_configure_opensecurity(ifp); - } - - brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); - - if (!mbss) { - chanspec = chandef_to_chanspec(&cfg->d11inf, - &settings->chandef); - err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); - if (err < 0) { - brcmf_err("Set Channel failed: chspec=%d, %d\n", - chanspec, err); - goto exit; - } - - if (is_11d != ifp->vif->is_11d) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, - is_11d); - if (err < 0) { - brcmf_err("Regulatory Set Error, %d\n", err); - goto exit; - } - } - if (settings->beacon_interval) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, - settings->beacon_interval); - if (err < 0) { - brcmf_err("Beacon Interval Set Error, %d\n", - err); - goto exit; - } - } - if (settings->dtim_period) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD, - settings->dtim_period); - if (err < 0) { - brcmf_err("DTIM Interval Set Error, %d\n", err); - goto exit; - } - } - - if (dev_role == NL80211_IFTYPE_AP) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); - if (err < 0) { - brcmf_err("BRCMF_C_DOWN error %d\n", err); - goto exit; - } - brcmf_fil_iovar_int_set(ifp, "apsta", 0); - } - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1); - if (err < 0) { - brcmf_err("SET INFRA error %d\n", err); - goto exit; - } - } else if (WARN_ON(is_11d != ifp->vif->is_11d)) { - /* Multiple-BSS should use same 11d configuration */ - err = -EINVAL; - goto exit; - } - if (dev_role == NL80211_IFTYPE_AP) { - if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss)) - brcmf_fil_iovar_int_set(ifp, "mbss", 1); - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1); - if (err < 0) { - brcmf_err("setting AP mode failed %d\n", err); - goto exit; - } - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); - if (err < 0) { - brcmf_err("BRCMF_C_UP error (%d)\n", err); - goto exit; - } - /* On DOWN the firmware removes the WEP keys, reconfigure - * them if they were set. - */ - brcmf_cfg80211_reconfigure_wep(ifp); - - memset(&join_params, 0, sizeof(join_params)); - /* join parameters starts with ssid */ - memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le)); - /* create softap */ - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, - &join_params, sizeof(join_params)); - if (err < 0) { - brcmf_err("SET SSID error (%d)\n", err); - goto exit; - } - brcmf_dbg(TRACE, "AP mode configuration complete\n"); - } else { - err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le, - sizeof(ssid_le)); - if (err < 0) { - brcmf_err("setting ssid failed %d\n", err); - goto exit; - } - bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); - bss_enable.enable = cpu_to_le32(1); - err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, - sizeof(bss_enable)); - if (err < 0) { - brcmf_err("bss_enable config failed %d\n", err); - goto exit; - } - - brcmf_dbg(TRACE, "GO mode configuration complete\n"); - } - set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); - brcmf_net_setcarrier(ifp, true); - -exit: - if ((err) && (!mbss)) { - brcmf_set_mpc(ifp, 1); - brcmf_configure_arp_offload(ifp, true); - } - return err; -} - -static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err; - struct brcmf_fil_bss_enable_le bss_enable; - struct brcmf_join_params join_params; - - brcmf_dbg(TRACE, "Enter\n"); - - if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) { - /* Due to most likely deauths outstanding we sleep */ - /* first to make sure they get processed by fw. */ - msleep(400); - - if (ifp->vif->mbss) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); - return err; - } - - memset(&join_params, 0, sizeof(join_params)); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, - &join_params, sizeof(join_params)); - if (err < 0) - brcmf_err("SET SSID error (%d)\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); - if (err < 0) - brcmf_err("BRCMF_C_DOWN error %d\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); - if (err < 0) - brcmf_err("setting AP mode failed %d\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0); - if (err < 0) - brcmf_err("setting INFRA mode failed %d\n", err); - if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) - brcmf_fil_iovar_int_set(ifp, "mbss", 0); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, - ifp->vif->is_11d); - if (err < 0) - brcmf_err("restoring REGULATORY setting failed %d\n", - err); - /* Bring device back up so it can be used again */ - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); - if (err < 0) - brcmf_err("BRCMF_C_UP error %d\n", err); - } else { - bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); - bss_enable.enable = cpu_to_le32(0); - err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, - sizeof(bss_enable)); - if (err < 0) - brcmf_err("bss_enable config failed %d\n", err); - } - brcmf_set_mpc(ifp, 1); - brcmf_configure_arp_offload(ifp, true); - clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); - brcmf_net_setcarrier(ifp, false); - - return err; -} - -static s32 -brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_beacon_data *info) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err; - - brcmf_dbg(TRACE, "Enter\n"); - - err = brcmf_config_ap_mgmt_ie(ifp->vif, info); - - return err; -} - -static int -brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, - struct station_del_parameters *params) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_scb_val_le scbval; - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err; - - if (!params->mac) - return -EFAULT; - - brcmf_dbg(TRACE, "Enter %pM\n", params->mac); - - if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) - ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; - if (!check_vif_up(ifp->vif)) - return -EIO; - - memcpy(&scbval.ea, params->mac, ETH_ALEN); - scbval.val = cpu_to_le32(params->reason_code); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON, - &scbval, sizeof(scbval)); - if (err) - brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static int -brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev, - const u8 *mac, struct station_parameters *params) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err; - - brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac, - params->sta_flags_mask, params->sta_flags_set); - - /* Ignore all 00 MAC */ - if (is_zero_ether_addr(mac)) - return 0; - - if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) - return 0; - - if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE, - (void *)mac, ETH_ALEN); - else - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE, - (void *)mac, ETH_ALEN); - if (err < 0) - brcmf_err("Setting SCB (de-)authorize failed, %d\n", err); - - return err; -} - -static void -brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy, - struct wireless_dev *wdev, - u16 frame_type, bool reg) -{ - struct brcmf_cfg80211_vif *vif; - u16 mgmt_type; - - brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg); - - mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - if (reg) - vif->mgmt_rx_reg |= BIT(mgmt_type); - else - vif->mgmt_rx_reg &= ~BIT(mgmt_type); -} - - -static int -brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct ieee80211_channel *chan = params->chan; - const u8 *buf = params->buf; - size_t len = params->len; - const struct ieee80211_mgmt *mgmt; - struct brcmf_cfg80211_vif *vif; - s32 err = 0; - s32 ie_offset; - s32 ie_len; - struct brcmf_fil_action_frame_le *action_frame; - struct brcmf_fil_af_params_le *af_params; - bool ack; - s32 chan_nr; - u32 freq; - - brcmf_dbg(TRACE, "Enter\n"); - - *cookie = 0; - - mgmt = (const struct ieee80211_mgmt *)buf; - - if (!ieee80211_is_mgmt(mgmt->frame_control)) { - brcmf_err("Driver only allows MGMT packet type\n"); - return -EPERM; - } - - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - - if (ieee80211_is_probe_resp(mgmt->frame_control)) { - /* Right now the only reason to get a probe response */ - /* is for p2p listen response or for p2p GO from */ - /* wpa_supplicant. Unfortunately the probe is send */ - /* on primary ndev, while dongle wants it on the p2p */ - /* vif. Since this is only reason for a probe */ - /* response to be sent, the vif is taken from cfg. */ - /* If ever desired to send proberesp for non p2p */ - /* response then data should be checked for */ - /* "DIRECT-". Note in future supplicant will take */ - /* dedicated p2p wdev to do this and then this 'hack'*/ - /* is not needed anymore. */ - ie_offset = DOT11_MGMT_HDR_LEN + - DOT11_BCN_PRB_FIXED_LEN; - ie_len = len - ie_offset; - if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) - vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - err = brcmf_vif_set_mgmt_ie(vif, - BRCMF_VNDR_IE_PRBRSP_FLAG, - &buf[ie_offset], - ie_len); - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, - GFP_KERNEL); - } else if (ieee80211_is_action(mgmt->frame_control)) { - af_params = kzalloc(sizeof(*af_params), GFP_KERNEL); - if (af_params == NULL) { - brcmf_err("unable to allocate frame\n"); - err = -ENOMEM; - goto exit; - } - action_frame = &af_params->action_frame; - /* Add the packet Id */ - action_frame->packet_id = cpu_to_le32(*cookie); - /* Add BSSID */ - memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN); - memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); - /* Add the length exepted for 802.11 header */ - action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN); - /* Add the channel. Use the one specified as parameter if any or - * the current one (got from the firmware) otherwise - */ - if (chan) - freq = chan->center_freq; - else - brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, - &freq); - chan_nr = ieee80211_frequency_to_channel(freq); - af_params->channel = cpu_to_le32(chan_nr); - - memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], - le16_to_cpu(action_frame->len)); - - brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n", - *cookie, le16_to_cpu(action_frame->len), freq); - - ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg), - af_params); - - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, - GFP_KERNEL); - kfree(af_params); - } else { - brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control); - brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len); - } - -exit: - return err; -} - - -static int -brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, - struct wireless_dev *wdev, - u64 cookie) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_cfg80211_vif *vif; - int err = 0; - - brcmf_dbg(TRACE, "Enter p2p listen cancel\n"); - - vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - if (vif == NULL) { - brcmf_err("No p2p device available for probe response\n"); - err = -ENODEV; - goto exit; - } - brcmf_p2p_cancel_remain_on_channel(vif->ifp); -exit: - return err; -} - -static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy, - struct wireless_dev *wdev, - enum nl80211_crit_proto_id proto, - u16 duration) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_cfg80211_vif *vif; - - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - - /* only DHCP support for now */ - if (proto != NL80211_CRIT_PROTO_DHCP) - return -EINVAL; - - /* suppress and abort scanning */ - set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); - brcmf_abort_scanning(cfg); - - return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration); -} - -static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy, - struct wireless_dev *wdev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_cfg80211_vif *vif; - - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - - brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); - clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); -} - -static s32 -brcmf_notify_tdls_peer_event(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - switch (e->reason) { - case BRCMF_E_REASON_TDLS_PEER_DISCOVERED: - brcmf_dbg(TRACE, "TDLS Peer Discovered\n"); - break; - case BRCMF_E_REASON_TDLS_PEER_CONNECTED: - brcmf_dbg(TRACE, "TDLS Peer Connected\n"); - brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); - break; - case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED: - brcmf_dbg(TRACE, "TDLS Peer Disconnected\n"); - brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); - break; - } - - return 0; -} - -static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper) -{ - int ret; - - switch (oper) { - case NL80211_TDLS_DISCOVERY_REQ: - ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY; - break; - case NL80211_TDLS_SETUP: - ret = BRCMF_TDLS_MANUAL_EP_CREATE; - break; - case NL80211_TDLS_TEARDOWN: - ret = BRCMF_TDLS_MANUAL_EP_DELETE; - break; - default: - brcmf_err("unsupported operation: %d\n", oper); - ret = -EOPNOTSUPP; - } - return ret; -} - -static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy, - struct net_device *ndev, const u8 *peer, - enum nl80211_tdls_operation oper) -{ - struct brcmf_if *ifp; - struct brcmf_tdls_iovar_le info; - int ret = 0; - - ret = brcmf_convert_nl80211_tdls_oper(oper); - if (ret < 0) - return ret; - - ifp = netdev_priv(ndev); - memset(&info, 0, sizeof(info)); - info.mode = (u8)ret; - if (peer) - memcpy(info.ea, peer, ETH_ALEN); - - ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint", - &info, sizeof(info)); - if (ret < 0) - brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret); - - return ret; -} - -static struct cfg80211_ops wl_cfg80211_ops = { - .add_virtual_intf = brcmf_cfg80211_add_iface, - .del_virtual_intf = brcmf_cfg80211_del_iface, - .change_virtual_intf = brcmf_cfg80211_change_iface, - .scan = brcmf_cfg80211_scan, - .set_wiphy_params = brcmf_cfg80211_set_wiphy_params, - .join_ibss = brcmf_cfg80211_join_ibss, - .leave_ibss = brcmf_cfg80211_leave_ibss, - .get_station = brcmf_cfg80211_get_station, - .dump_station = brcmf_cfg80211_dump_station, - .set_tx_power = brcmf_cfg80211_set_tx_power, - .get_tx_power = brcmf_cfg80211_get_tx_power, - .add_key = brcmf_cfg80211_add_key, - .del_key = brcmf_cfg80211_del_key, - .get_key = brcmf_cfg80211_get_key, - .set_default_key = brcmf_cfg80211_config_default_key, - .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key, - .set_power_mgmt = brcmf_cfg80211_set_power_mgmt, - .connect = brcmf_cfg80211_connect, - .disconnect = brcmf_cfg80211_disconnect, - .suspend = brcmf_cfg80211_suspend, - .resume = brcmf_cfg80211_resume, - .set_pmksa = brcmf_cfg80211_set_pmksa, - .del_pmksa = brcmf_cfg80211_del_pmksa, - .flush_pmksa = brcmf_cfg80211_flush_pmksa, - .start_ap = brcmf_cfg80211_start_ap, - .stop_ap = brcmf_cfg80211_stop_ap, - .change_beacon = brcmf_cfg80211_change_beacon, - .del_station = brcmf_cfg80211_del_station, - .change_station = brcmf_cfg80211_change_station, - .sched_scan_start = brcmf_cfg80211_sched_scan_start, - .sched_scan_stop = brcmf_cfg80211_sched_scan_stop, - .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register, - .mgmt_tx = brcmf_cfg80211_mgmt_tx, - .remain_on_channel = brcmf_p2p_remain_on_channel, - .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel, - .start_p2p_device = brcmf_p2p_start_device, - .stop_p2p_device = brcmf_p2p_stop_device, - .crit_proto_start = brcmf_cfg80211_crit_proto_start, - .crit_proto_stop = brcmf_cfg80211_crit_proto_stop, - .tdls_oper = brcmf_cfg80211_tdls_oper, -}; - -struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, - enum nl80211_iftype type, - bool pm_block) -{ - struct brcmf_cfg80211_vif *vif_walk; - struct brcmf_cfg80211_vif *vif; - bool mbss; - - brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n", - sizeof(*vif)); - vif = kzalloc(sizeof(*vif), GFP_KERNEL); - if (!vif) - return ERR_PTR(-ENOMEM); - - vif->wdev.wiphy = cfg->wiphy; - vif->wdev.iftype = type; - - vif->pm_block = pm_block; - vif->roam_off = -1; - - brcmf_init_prof(&vif->profile); - - if (type == NL80211_IFTYPE_AP) { - mbss = false; - list_for_each_entry(vif_walk, &cfg->vif_list, list) { - if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) { - mbss = true; - break; - } - } - vif->mbss = mbss; - } - - list_add_tail(&vif->list, &cfg->vif_list); - return vif; -} - -void brcmf_free_vif(struct brcmf_cfg80211_vif *vif) -{ - list_del(&vif->list); - kfree(vif); -} - -void brcmf_cfg80211_free_netdev(struct net_device *ndev) -{ - struct brcmf_cfg80211_vif *vif; - struct brcmf_if *ifp; - - ifp = netdev_priv(ndev); - vif = ifp->vif; - - if (vif) - brcmf_free_vif(vif); - free_netdev(ndev); -} - -static bool brcmf_is_linkup(const struct brcmf_event_msg *e) -{ - u32 event = e->event_code; - u32 status = e->status; - - if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) { - brcmf_dbg(CONN, "Processing set ssid\n"); - return true; - } - - return false; -} - -static bool brcmf_is_linkdown(const struct brcmf_event_msg *e) -{ - u32 event = e->event_code; - u16 flags = e->flags; - - if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) || - (event == BRCMF_E_DISASSOC_IND) || - ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) { - brcmf_dbg(CONN, "Processing link down\n"); - return true; - } - return false; -} - -static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg, - const struct brcmf_event_msg *e) -{ - u32 event = e->event_code; - u32 status = e->status; - - if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) { - brcmf_dbg(CONN, "Processing Link %s & no network found\n", - e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down"); - return true; - } - - if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) { - brcmf_dbg(CONN, "Processing connecting & no network found\n"); - return true; - } - - return false; -} - -static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); - - kfree(conn_info->req_ie); - conn_info->req_ie = NULL; - conn_info->req_ie_len = 0; - kfree(conn_info->resp_ie); - conn_info->resp_ie = NULL; - conn_info->resp_ie_len = 0; -} - -static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp) -{ - struct brcmf_cfg80211_assoc_ielen_le *assoc_info; - struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); - u32 req_len; - u32 resp_len; - s32 err = 0; - - brcmf_clear_assoc_ies(cfg); - - err = brcmf_fil_iovar_data_get(ifp, "assoc_info", - cfg->extra_buf, WL_ASSOC_INFO_MAX); - if (err) { - brcmf_err("could not get assoc info (%d)\n", err); - return err; - } - assoc_info = - (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf; - req_len = le32_to_cpu(assoc_info->req_len); - resp_len = le32_to_cpu(assoc_info->resp_len); - if (req_len) { - err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies", - cfg->extra_buf, - WL_ASSOC_INFO_MAX); - if (err) { - brcmf_err("could not get assoc req (%d)\n", err); - return err; - } - conn_info->req_ie_len = req_len; - conn_info->req_ie = - kmemdup(cfg->extra_buf, conn_info->req_ie_len, - GFP_KERNEL); - } else { - conn_info->req_ie_len = 0; - conn_info->req_ie = NULL; - } - if (resp_len) { - err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies", - cfg->extra_buf, - WL_ASSOC_INFO_MAX); - if (err) { - brcmf_err("could not get assoc resp (%d)\n", err); - return err; - } - conn_info->resp_ie_len = resp_len; - conn_info->resp_ie = - kmemdup(cfg->extra_buf, conn_info->resp_ie_len, - GFP_KERNEL); - } else { - conn_info->resp_ie_len = 0; - conn_info->resp_ie = NULL; - } - brcmf_dbg(CONN, "req len (%d) resp len (%d)\n", - conn_info->req_ie_len, conn_info->resp_ie_len); - - return err; -} - -static s32 -brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, - const struct brcmf_event_msg *e) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); - struct wiphy *wiphy = cfg_to_wiphy(cfg); - struct ieee80211_channel *notify_channel = NULL; - struct ieee80211_supported_band *band; - struct brcmf_bss_info_le *bi; - struct brcmu_chan ch; - u32 freq; - s32 err = 0; - u8 *buf; - - brcmf_dbg(TRACE, "Enter\n"); - - brcmf_get_assoc_ies(cfg, ifp); - memcpy(profile->bssid, e->addr, ETH_ALEN); - brcmf_update_bss_info(cfg, ifp); - - buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); - if (buf == NULL) { - err = -ENOMEM; - goto done; - } - - /* data sent to dongle has to be little endian */ - *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, - buf, WL_BSS_INFO_MAX); - - if (err) - goto done; - - bi = (struct brcmf_bss_info_le *)(buf + 4); - ch.chspec = le16_to_cpu(bi->chanspec); - cfg->d11inf.decchspec(&ch); - - if (ch.band == BRCMU_CHAN_BAND_2G) - band = wiphy->bands[IEEE80211_BAND_2GHZ]; - else - band = wiphy->bands[IEEE80211_BAND_5GHZ]; - - freq = ieee80211_channel_to_frequency(ch.chnum, band->band); - notify_channel = ieee80211_get_channel(wiphy, freq); - -done: - kfree(buf); - cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid, - conn_info->req_ie, conn_info->req_ie_len, - conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); - brcmf_dbg(CONN, "Report roaming result\n"); - - set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, const struct brcmf_event_msg *e, - bool completed) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); - - brcmf_dbg(TRACE, "Enter\n"); - - if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING, - &ifp->vif->sme_state)) { - if (completed) { - brcmf_get_assoc_ies(cfg, ifp); - memcpy(profile->bssid, e->addr, ETH_ALEN); - brcmf_update_bss_info(cfg, ifp); - set_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state); - } - cfg80211_connect_result(ndev, - (u8 *)profile->bssid, - conn_info->req_ie, - conn_info->req_ie_len, - conn_info->resp_ie, - conn_info->resp_ie_len, - completed ? WLAN_STATUS_SUCCESS : - WLAN_STATUS_AUTH_TIMEOUT, - GFP_KERNEL); - brcmf_dbg(CONN, "Report connect result - connection %s\n", - completed ? "succeeded" : "failed"); - } - brcmf_dbg(TRACE, "Exit\n"); - return 0; -} - -static s32 -brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - static int generation; - u32 event = e->event_code; - u32 reason = e->reason; - struct station_info sinfo; - - brcmf_dbg(CONN, "event %d, reason %d\n", event, reason); - if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS && - ndev != cfg_to_ndev(cfg)) { - brcmf_dbg(CONN, "AP mode link down\n"); - complete(&cfg->vif_disabled); - if (ifp->vif->mbss) - brcmf_remove_interface(ifp); - return 0; - } - - if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) && - (reason == BRCMF_E_STATUS_SUCCESS)) { - memset(&sinfo, 0, sizeof(sinfo)); - if (!data) { - brcmf_err("No IEs present in ASSOC/REASSOC_IND"); - return -EINVAL; - } - sinfo.assoc_req_ies = data; - sinfo.assoc_req_ies_len = e->datalen; - generation++; - sinfo.generation = generation; - cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL); - } else if ((event == BRCMF_E_DISASSOC_IND) || - (event == BRCMF_E_DEAUTH_IND) || - (event == BRCMF_E_DEAUTH)) { - cfg80211_del_sta(ndev, e->addr, GFP_KERNEL); - } - return 0; -} - -static s32 -brcmf_notify_connect_status(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct net_device *ndev = ifp->ndev; - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct ieee80211_channel *chan; - s32 err = 0; - - if ((e->event_code == BRCMF_E_DEAUTH) || - (e->event_code == BRCMF_E_DEAUTH_IND) || - (e->event_code == BRCMF_E_DISASSOC_IND) || - ((e->event_code == BRCMF_E_LINK) && (!e->flags))) { - brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); - } - - if (brcmf_is_apmode(ifp->vif)) { - err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); - } else if (brcmf_is_linkup(e)) { - brcmf_dbg(CONN, "Linkup\n"); - if (brcmf_is_ibssmode(ifp->vif)) { - chan = ieee80211_get_channel(cfg->wiphy, cfg->channel); - memcpy(profile->bssid, e->addr, ETH_ALEN); - wl_inform_ibss(cfg, ndev, e->addr); - cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL); - clear_bit(BRCMF_VIF_STATUS_CONNECTING, - &ifp->vif->sme_state); - set_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state); - } else - brcmf_bss_connect_done(cfg, ndev, e, true); - brcmf_net_setcarrier(ifp, true); - } else if (brcmf_is_linkdown(e)) { - brcmf_dbg(CONN, "Linkdown\n"); - if (!brcmf_is_ibssmode(ifp->vif)) { - brcmf_bss_connect_done(cfg, ndev, e, false); - } - brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e)); - brcmf_init_prof(ndev_to_prof(ndev)); - if (ndev != cfg_to_ndev(cfg)) - complete(&cfg->vif_disabled); - brcmf_net_setcarrier(ifp, false); - } else if (brcmf_is_nonetwork(cfg, e)) { - if (brcmf_is_ibssmode(ifp->vif)) - clear_bit(BRCMF_VIF_STATUS_CONNECTING, - &ifp->vif->sme_state); - else - brcmf_bss_connect_done(cfg, ndev, e, false); - } - - return err; -} - -static s32 -brcmf_notify_roaming_status(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - u32 event = e->event_code; - u32 status = e->status; - - if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) { - if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) - brcmf_bss_roaming_done(cfg, ifp->ndev, e); - else - brcmf_bss_connect_done(cfg, ifp->ndev, e, true); - } - - return 0; -} - -static s32 -brcmf_notify_mic_status(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - u16 flags = e->flags; - enum nl80211_key_type key_type; - - if (flags & BRCMF_EVENT_MSG_GROUP) - key_type = NL80211_KEYTYPE_GROUP; - else - key_type = NL80211_KEYTYPE_PAIRWISE; - - cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1, - NULL, GFP_KERNEL); - - return 0; -} - -static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data; - struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; - struct brcmf_cfg80211_vif *vif; - - brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n", - ifevent->action, ifevent->flags, ifevent->ifidx, - ifevent->bssidx); - - mutex_lock(&event->vif_event_lock); - event->action = ifevent->action; - vif = event->vif; - - switch (ifevent->action) { - case BRCMF_E_IF_ADD: - /* waiting process may have timed out */ - if (!cfg->vif_event.vif) { - mutex_unlock(&event->vif_event_lock); - return -EBADF; - } - - ifp->vif = vif; - vif->ifp = ifp; - if (ifp->ndev) { - vif->wdev.netdev = ifp->ndev; - ifp->ndev->ieee80211_ptr = &vif->wdev; - SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy)); - } - mutex_unlock(&event->vif_event_lock); - wake_up(&event->vif_wq); - return 0; - - case BRCMF_E_IF_DEL: - mutex_unlock(&event->vif_event_lock); - /* event may not be upon user request */ - if (brcmf_cfg80211_vif_event_armed(cfg)) - wake_up(&event->vif_wq); - return 0; - - case BRCMF_E_IF_CHANGE: - mutex_unlock(&event->vif_event_lock); - wake_up(&event->vif_wq); - return 0; - - default: - mutex_unlock(&event->vif_event_lock); - break; - } - return -EINVAL; -} - -static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) -{ - conf->frag_threshold = (u32)-1; - conf->rts_threshold = (u32)-1; - conf->retry_short = (u32)-1; - conf->retry_long = (u32)-1; - conf->tx_power = -1; -} - -static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg) -{ - brcmf_fweh_register(cfg->pub, BRCMF_E_LINK, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM, - brcmf_notify_roaming_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR, - brcmf_notify_mic_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, - brcmf_notify_sched_scan_results); - brcmf_fweh_register(cfg->pub, BRCMF_E_IF, - brcmf_notify_vif_event); - brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG, - brcmf_p2p_notify_rx_mgmt_p2p_probereq); - brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE, - brcmf_p2p_notify_listen_complete); - brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX, - brcmf_p2p_notify_action_frame_rx); - brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE, - brcmf_p2p_notify_action_tx_complete); - brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE, - brcmf_p2p_notify_action_tx_complete); -} - -static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) -{ - kfree(cfg->conf); - cfg->conf = NULL; - kfree(cfg->escan_ioctl_buf); - cfg->escan_ioctl_buf = NULL; - kfree(cfg->extra_buf); - cfg->extra_buf = NULL; - kfree(cfg->pmk_list); - cfg->pmk_list = NULL; -} - -static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) -{ - cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL); - if (!cfg->conf) - goto init_priv_mem_out; - cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); - if (!cfg->escan_ioctl_buf) - goto init_priv_mem_out; - cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); - if (!cfg->extra_buf) - goto init_priv_mem_out; - cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL); - if (!cfg->pmk_list) - goto init_priv_mem_out; - - return 0; - -init_priv_mem_out: - brcmf_deinit_priv_mem(cfg); - - return -ENOMEM; -} - -static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) -{ - s32 err = 0; - - cfg->scan_request = NULL; - cfg->pwr_save = true; - cfg->active_scan = true; /* we do active scan per default */ - cfg->dongle_up = false; /* dongle is not up yet */ - err = brcmf_init_priv_mem(cfg); - if (err) - return err; - brcmf_register_event_handlers(cfg); - mutex_init(&cfg->usr_sync); - brcmf_init_escan(cfg); - brcmf_init_conf(cfg->conf); - init_completion(&cfg->vif_disabled); - return err; -} - -static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg) -{ - cfg->dongle_up = false; /* dongle down */ - brcmf_abort_scanning(cfg); - brcmf_deinit_priv_mem(cfg); -} - -static void init_vif_event(struct brcmf_cfg80211_vif_event *event) -{ - init_waitqueue_head(&event->vif_wq); - mutex_init(&event->vif_event_lock); -} - -static s32 -brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) -{ - s32 err = 0; - __le32 roamtrigger[2]; - __le32 roam_delta[2]; - - /* - * Setup timeout if Beacons are lost and roam is - * off to report link down - */ - if (brcmf_roamoff) { - err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout); - if (err) { - brcmf_err("bcn_timeout error (%d)\n", err); - goto dongle_rom_out; - } - } - - /* - * Enable/Disable built-in roaming to allow supplicant - * to take care of roaming - */ - brcmf_dbg(INFO, "Internal Roaming = %s\n", - brcmf_roamoff ? "Off" : "On"); - err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff)); - if (err) { - brcmf_err("roam_off error (%d)\n", err); - goto dongle_rom_out; - } - - roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL); - roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER, - (void *)roamtrigger, sizeof(roamtrigger)); - if (err) { - brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err); - goto dongle_rom_out; - } - - roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA); - roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA, - (void *)roam_delta, sizeof(roam_delta)); - if (err) { - brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err); - goto dongle_rom_out; - } - -dongle_rom_out: - return err; -} - -static s32 -brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time, - s32 scan_unassoc_time, s32 scan_passive_time) -{ - s32 err = 0; - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, - scan_assoc_time); - if (err) { - if (err == -EOPNOTSUPP) - brcmf_dbg(INFO, "Scan assoc time is not supported\n"); - else - brcmf_err("Scan assoc time error (%d)\n", err); - goto dongle_scantime_out; - } - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, - scan_unassoc_time); - if (err) { - if (err == -EOPNOTSUPP) - brcmf_dbg(INFO, "Scan unassoc time is not supported\n"); - else - brcmf_err("Scan unassoc time error (%d)\n", err); - goto dongle_scantime_out; - } - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME, - scan_passive_time); - if (err) { - if (err == -EOPNOTSUPP) - brcmf_dbg(INFO, "Scan passive time is not supported\n"); - else - brcmf_err("Scan passive time error (%d)\n", err); - goto dongle_scantime_out; - } - -dongle_scantime_out: - return err; -} - -static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel, - struct brcmu_chan *ch) -{ - u32 ht40_flag; - - ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40; - if (ch->sb == BRCMU_CHAN_SB_U) { - if (ht40_flag == IEEE80211_CHAN_NO_HT40) - channel->flags &= ~IEEE80211_CHAN_NO_HT40; - channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; - } else { - /* It should be one of - * IEEE80211_CHAN_NO_HT40 or - * IEEE80211_CHAN_NO_HT40PLUS - */ - channel->flags &= ~IEEE80211_CHAN_NO_HT40; - if (ht40_flag == IEEE80211_CHAN_NO_HT40) - channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; - } -} - -static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, - u32 bw_cap[]) -{ - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - struct ieee80211_supported_band *band; - struct ieee80211_channel *channel; - struct wiphy *wiphy; - struct brcmf_chanspec_list *list; - struct brcmu_chan ch; - int err; - u8 *pbuf; - u32 i, j; - u32 total; - u32 chaninfo; - u32 index; - - pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); - - if (pbuf == NULL) - return -ENOMEM; - - list = (struct brcmf_chanspec_list *)pbuf; - - err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, - BRCMF_DCMD_MEDLEN); - if (err) { - brcmf_err("get chanspecs error (%d)\n", err); - goto fail_pbuf; - } - - wiphy = cfg_to_wiphy(cfg); - band = wiphy->bands[IEEE80211_BAND_2GHZ]; - if (band) - for (i = 0; i < band->n_channels; i++) - band->channels[i].flags = IEEE80211_CHAN_DISABLED; - band = wiphy->bands[IEEE80211_BAND_5GHZ]; - if (band) - for (i = 0; i < band->n_channels; i++) - band->channels[i].flags = IEEE80211_CHAN_DISABLED; - - total = le32_to_cpu(list->count); - for (i = 0; i < total; i++) { - ch.chspec = (u16)le32_to_cpu(list->element[i]); - cfg->d11inf.decchspec(&ch); - - if (ch.band == BRCMU_CHAN_BAND_2G) { - band = wiphy->bands[IEEE80211_BAND_2GHZ]; - } else if (ch.band == BRCMU_CHAN_BAND_5G) { - band = wiphy->bands[IEEE80211_BAND_5GHZ]; - } else { - brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec); - continue; - } - if (!band) - continue; - if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) && - ch.bw == BRCMU_CHAN_BW_40) - continue; - if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) && - ch.bw == BRCMU_CHAN_BW_80) - continue; - - channel = band->channels; - index = band->n_channels; - for (j = 0; j < band->n_channels; j++) { - if (channel[j].hw_value == ch.chnum) { - index = j; - break; - } - } - channel[index].center_freq = - ieee80211_channel_to_frequency(ch.chnum, band->band); - channel[index].hw_value = ch.chnum; - - /* assuming the chanspecs order is HT20, - * HT40 upper, HT40 lower, and VHT80. - */ - if (ch.bw == BRCMU_CHAN_BW_80) { - channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ; - } else if (ch.bw == BRCMU_CHAN_BW_40) { - brcmf_update_bw40_channel_flag(&channel[index], &ch); - } else { - /* enable the channel and disable other bandwidths - * for now as mentioned order assure they are enabled - * for subsequent chanspecs. - */ - channel[index].flags = IEEE80211_CHAN_NO_HT40 | - IEEE80211_CHAN_NO_80MHZ; - ch.bw = BRCMU_CHAN_BW_20; - cfg->d11inf.encchspec(&ch); - chaninfo = ch.chspec; - err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info", - &chaninfo); - if (!err) { - if (chaninfo & WL_CHAN_RADAR) - channel[index].flags |= - (IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR); - if (chaninfo & WL_CHAN_PASSIVE) - channel[index].flags |= - IEEE80211_CHAN_NO_IR; - } - } - } - -fail_pbuf: - kfree(pbuf); - return err; -} - -static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - struct ieee80211_supported_band *band; - struct brcmf_fil_bwcap_le band_bwcap; - struct brcmf_chanspec_list *list; - u8 *pbuf; - u32 val; - int err; - struct brcmu_chan ch; - u32 num_chan; - int i, j; - - /* verify support for bw_cap command */ - val = WLC_BAND_5G; - err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); - - if (!err) { - /* only set 2G bandwidth using bw_cap command */ - band_bwcap.band = cpu_to_le32(WLC_BAND_2G); - band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ); - err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, - sizeof(band_bwcap)); - } else { - brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); - val = WLC_N_BW_40ALL; - err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); - } - - if (!err) { - /* update channel info in 2G band */ - pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); - - if (pbuf == NULL) - return -ENOMEM; - - ch.band = BRCMU_CHAN_BAND_2G; - ch.bw = BRCMU_CHAN_BW_40; - ch.sb = BRCMU_CHAN_SB_NONE; - ch.chnum = 0; - cfg->d11inf.encchspec(&ch); - - /* pass encoded chanspec in query */ - *(__le16 *)pbuf = cpu_to_le16(ch.chspec); - - err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, - BRCMF_DCMD_MEDLEN); - if (err) { - brcmf_err("get chanspecs error (%d)\n", err); - kfree(pbuf); - return err; - } - - band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ]; - list = (struct brcmf_chanspec_list *)pbuf; - num_chan = le32_to_cpu(list->count); - for (i = 0; i < num_chan; i++) { - ch.chspec = (u16)le32_to_cpu(list->element[i]); - cfg->d11inf.decchspec(&ch); - if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G)) - continue; - if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40)) - continue; - for (j = 0; j < band->n_channels; j++) { - if (band->channels[j].hw_value == ch.chnum) - break; - } - if (WARN_ON(j == band->n_channels)) - continue; - - brcmf_update_bw40_channel_flag(&band->channels[j], &ch); - } - kfree(pbuf); - } - return err; -} - -static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) -{ - u32 band, mimo_bwcap; - int err; - - band = WLC_BAND_2G; - err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); - if (!err) { - bw_cap[IEEE80211_BAND_2GHZ] = band; - band = WLC_BAND_5G; - err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); - if (!err) { - bw_cap[IEEE80211_BAND_5GHZ] = band; - return; - } - WARN_ON(1); - return; - } - brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n"); - mimo_bwcap = 0; - err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap); - if (err) - /* assume 20MHz if firmware does not give a clue */ - mimo_bwcap = WLC_N_BW_20ALL; - - switch (mimo_bwcap) { - case WLC_N_BW_40ALL: - bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT; - /* fall-thru */ - case WLC_N_BW_20IN2G_40IN5G: - bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT; - /* fall-thru */ - case WLC_N_BW_20ALL: - bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT; - bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT; - break; - default: - brcmf_err("invalid mimo_bw_cap value\n"); - } -} - -static void brcmf_update_ht_cap(struct ieee80211_supported_band *band, - u32 bw_cap[2], u32 nchain) -{ - band->ht_cap.ht_supported = true; - if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { - band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - } - band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; - band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; - band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; - memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); - band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; -} - -static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp) -{ - u16 mcs_map; - int i; - - for (i = 0, mcs_map = 0xFFFF; i < nchain; i++) - mcs_map = (mcs_map << 2) | supp; - - return cpu_to_le16(mcs_map); -} - -static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, - u32 bw_cap[2], u32 nchain) -{ - __le16 mcs_map; - - /* not allowed in 2.4G band */ - if (band->band == IEEE80211_BAND_2GHZ) - return; - - band->vht_cap.vht_supported = true; - /* 80MHz is mandatory */ - band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80; - if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) { - band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; - band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160; - } - /* all support 256-QAM */ - mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9); - band->vht_cap.vht_mcs.rx_mcs_map = mcs_map; - band->vht_cap.vht_mcs.tx_mcs_map = mcs_map; -} - -static int brcmf_setup_wiphybands(struct wiphy *wiphy) -{ - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - u32 nmode = 0; - u32 vhtmode = 0; - u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT }; - u32 rxchain; - u32 nchain; - int err; - s32 i; - struct ieee80211_supported_band *band; - - (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); - err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); - if (err) { - brcmf_err("nmode error (%d)\n", err); - } else { - brcmf_get_bwcap(ifp, bw_cap); - } - brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n", - nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ], - bw_cap[IEEE80211_BAND_5GHZ]); - - err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); - if (err) { - brcmf_err("rxchain error (%d)\n", err); - nchain = 1; - } else { - for (nchain = 0; rxchain; nchain++) - rxchain = rxchain & (rxchain - 1); - } - brcmf_dbg(INFO, "nchain=%d\n", nchain); - - err = brcmf_construct_chaninfo(cfg, bw_cap); - if (err) { - brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err); - return err; - } - - wiphy = cfg_to_wiphy(cfg); - for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) { - band = wiphy->bands[i]; - if (band == NULL) - continue; - - if (nmode) - brcmf_update_ht_cap(band, bw_cap, nchain); - if (vhtmode) - brcmf_update_vht_cap(band, bw_cap, nchain); - } - - return 0; -} - -static const struct ieee80211_txrx_stypes -brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { - [NL80211_IFTYPE_STATION] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - }, - [NL80211_IFTYPE_P2P_CLIENT] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - }, - [NL80211_IFTYPE_P2P_GO] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | - BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | - BIT(IEEE80211_STYPE_DISASSOC >> 4) | - BIT(IEEE80211_STYPE_AUTH >> 4) | - BIT(IEEE80211_STYPE_DEAUTH >> 4) | - BIT(IEEE80211_STYPE_ACTION >> 4) - }, - [NL80211_IFTYPE_P2P_DEVICE] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - } -}; - -/** - * brcmf_setup_ifmodes() - determine interface modes and combinations. - * - * @wiphy: wiphy object. - * @ifp: interface object needed for feat module api. - * - * The interface modes and combinations are determined dynamically here - * based on firmware functionality. - * - * no p2p and no mbss: - * - * #STA <= 1, #AP <= 1, channels = 1, 2 total - * - * no p2p and mbss: - * - * #STA <= 1, #AP <= 1, channels = 1, 2 total - * #AP <= 4, matching BI, channels = 1, 4 total - * - * p2p, no mchan, and mbss: - * - * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total - * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total - * #AP <= 4, matching BI, channels = 1, 4 total - * - * p2p, mchan, and mbss: - * - * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total - * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total - * #AP <= 4, matching BI, channels = 1, 4 total - */ -static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) -{ - struct ieee80211_iface_combination *combo = NULL; - struct ieee80211_iface_limit *c0_limits = NULL; - struct ieee80211_iface_limit *p2p_limits = NULL; - struct ieee80211_iface_limit *mbss_limits = NULL; - bool mbss, p2p; - int i, c, n_combos; - - mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS); - p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P); - - n_combos = 1 + !!p2p + !!mbss; - combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL); - if (!combo) - goto err; - - c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL); - if (!c0_limits) - goto err; - - if (p2p) { - p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL); - if (!p2p_limits) - goto err; - } - - if (mbss) { - mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL); - if (!mbss_limits) - goto err; - } - - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP); - - c = 0; - i = 0; - combo[c].num_different_channels = 1; - c0_limits[i].max = 1; - c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); - if (p2p) { - if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) - combo[c].num_different_channels = 2; - wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_DEVICE); - c0_limits[i].max = 1; - c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE); - c0_limits[i].max = 1; - c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO); - } else { - c0_limits[i].max = 1; - c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); - } - combo[c].max_interfaces = i; - combo[c].n_limits = i; - combo[c].limits = c0_limits; - - if (p2p) { - c++; - i = 0; - combo[c].num_different_channels = 1; - p2p_limits[i].max = 1; - p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION); - p2p_limits[i].max = 1; - p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP); - p2p_limits[i].max = 1; - p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT); - p2p_limits[i].max = 1; - p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE); - combo[c].max_interfaces = i; - combo[c].n_limits = i; - combo[c].limits = p2p_limits; - } - - if (mbss) { - c++; - combo[c].beacon_int_infra_match = true; - combo[c].num_different_channels = 1; - mbss_limits[0].max = 4; - mbss_limits[0].types = BIT(NL80211_IFTYPE_AP); - combo[c].max_interfaces = 4; - combo[c].n_limits = 1; - combo[c].limits = mbss_limits; - } - wiphy->n_iface_combinations = n_combos; - wiphy->iface_combinations = combo; - return 0; - -err: - kfree(c0_limits); - kfree(p2p_limits); - kfree(mbss_limits); - kfree(combo); - return -ENOMEM; -} - -static void brcmf_wiphy_pno_params(struct wiphy *wiphy) -{ - /* scheduled scan settings */ - wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; - wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; - wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; - wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; -} - -#ifdef CONFIG_PM -static const struct wiphy_wowlan_support brcmf_wowlan_support = { - .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, - .n_patterns = BRCMF_WOWL_MAXPATTERNS, - .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE, - .pattern_min_len = 1, - .max_pkt_offset = 1500, -}; -#endif - -static void brcmf_wiphy_wowl_params(struct wiphy *wiphy) -{ -#ifdef CONFIG_PM - /* wowl settings */ - wiphy->wowlan = &brcmf_wowlan_support; -#endif -} - -static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) -{ - struct brcmf_pub *drvr = ifp->drvr; - const struct ieee80211_iface_combination *combo; - struct ieee80211_supported_band *band; - u16 max_interfaces = 0; - __le32 bandlist[3]; - u32 n_bands; - int err, i; - - wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; - wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; - wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; - - err = brcmf_setup_ifmodes(wiphy, ifp); - if (err) - return err; - - for (i = 0, combo = wiphy->iface_combinations; - i < wiphy->n_iface_combinations; i++, combo++) { - max_interfaces = max(max_interfaces, combo->max_interfaces); - } - - for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses); - i++) { - u8 *addr = drvr->addresses[i].addr; - - memcpy(addr, drvr->mac, ETH_ALEN); - if (i) { - addr[0] |= BIT(1); - addr[ETH_ALEN - 1] ^= i; - } - } - wiphy->addresses = drvr->addresses; - wiphy->n_addresses = i; - - wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - wiphy->cipher_suites = __wl_cipher_suites; - wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); - wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | - WIPHY_FLAG_OFFCHAN_TX | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_SUPPORTS_TDLS; - if (!brcmf_roamoff) - wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; - wiphy->mgmt_stypes = brcmf_txrx_stypes; - wiphy->max_remain_on_channel_duration = 5000; - if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) - brcmf_wiphy_pno_params(wiphy); - - /* vendor commands/events support */ - wiphy->vendor_commands = brcmf_vendor_cmds; - wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; - - if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) - brcmf_wiphy_wowl_params(wiphy); - - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, - sizeof(bandlist)); - if (err) { - brcmf_err("could not obtain band info: err=%d\n", err); - return err; - } - /* first entry in bandlist is number of bands */ - n_bands = le32_to_cpu(bandlist[0]); - for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) { - if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) { - band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), - GFP_KERNEL); - if (!band) - return -ENOMEM; - - band->channels = kmemdup(&__wl_2ghz_channels, - sizeof(__wl_2ghz_channels), - GFP_KERNEL); - if (!band->channels) { - kfree(band); - return -ENOMEM; - } - - band->n_channels = ARRAY_SIZE(__wl_2ghz_channels); - wiphy->bands[IEEE80211_BAND_2GHZ] = band; - } - if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) { - band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz), - GFP_KERNEL); - if (!band) - return -ENOMEM; - - band->channels = kmemdup(&__wl_5ghz_channels, - sizeof(__wl_5ghz_channels), - GFP_KERNEL); - if (!band->channels) { - kfree(band); - return -ENOMEM; - } - - band->n_channels = ARRAY_SIZE(__wl_5ghz_channels); - wiphy->bands[IEEE80211_BAND_5GHZ] = band; - } - } - err = brcmf_setup_wiphybands(wiphy); - return err; -} - -static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) -{ - struct net_device *ndev; - struct wireless_dev *wdev; - struct brcmf_if *ifp; - s32 power_mode; - s32 err = 0; - - if (cfg->dongle_up) - return err; - - ndev = cfg_to_ndev(cfg); - wdev = ndev->ieee80211_ptr; - ifp = netdev_priv(ndev); - - /* make sure RF is ready for work */ - brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); - - brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME, - WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME); - - power_mode = cfg->pwr_save ? PM_FAST : PM_OFF; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode); - if (err) - goto default_conf_out; - brcmf_dbg(INFO, "power save set to %s\n", - (power_mode ? "enabled" : "disabled")); - - err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT); - if (err) - goto default_conf_out; - err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, - NULL, NULL); - if (err) - goto default_conf_out; - - brcmf_configure_arp_offload(ifp, true); - - cfg->dongle_up = true; -default_conf_out: - - return err; - -} - -static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp) -{ - set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); - - return brcmf_config_dongle(ifp->drvr->config); -} - -static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - - /* - * While going down, if associated with AP disassociate - * from AP to save power - */ - if (check_vif_up(ifp->vif)) { - brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED); - - /* Make sure WPA_Supplicant receives all the event - generated due to DISASSOC call to the fw to keep - the state fw and WPA_Supplicant state consistent - */ - brcmf_delay(500); - } - - brcmf_abort_scanning(cfg); - clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); - - return 0; -} - -s32 brcmf_cfg80211_up(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - s32 err = 0; - - mutex_lock(&cfg->usr_sync); - err = __brcmf_cfg80211_up(ifp); - mutex_unlock(&cfg->usr_sync); - - return err; -} - -s32 brcmf_cfg80211_down(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - s32 err = 0; - - mutex_lock(&cfg->usr_sync); - err = __brcmf_cfg80211_down(ifp); - mutex_unlock(&cfg->usr_sync); - - return err; -} - -enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp) -{ - struct wireless_dev *wdev = &ifp->vif->wdev; - - return wdev->iftype; -} - -bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, - unsigned long state) -{ - struct brcmf_cfg80211_vif *vif; - - list_for_each_entry(vif, &cfg->vif_list, list) { - if (test_bit(state, &vif->sme_state)) - return true; - } - return false; -} - -static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event, - u8 action) -{ - u8 evt_action; - - mutex_lock(&event->vif_event_lock); - evt_action = event->action; - mutex_unlock(&event->vif_event_lock); - return evt_action == action; -} - -void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, - struct brcmf_cfg80211_vif *vif) -{ - struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; - - mutex_lock(&event->vif_event_lock); - event->vif = vif; - event->action = 0; - mutex_unlock(&event->vif_event_lock); -} - -bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; - bool armed; - - mutex_lock(&event->vif_event_lock); - armed = event->vif != NULL; - mutex_unlock(&event->vif_event_lock); - - return armed; -} -int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, - u8 action, ulong timeout) -{ - struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; - - return wait_event_timeout(event->vif_wq, - vif_event_equals(event, action), timeout); -} - -static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *req) -{ - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - struct brcmf_fil_country_le ccreq; - int i; - - brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator, - req->alpha2[0], req->alpha2[1]); - - /* ignore non-ISO3166 country codes */ - for (i = 0; i < sizeof(req->alpha2); i++) - if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { - brcmf_err("not a ISO3166 code\n"); - return; - } - memset(&ccreq, 0, sizeof(ccreq)); - ccreq.rev = cpu_to_le32(-1); - memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2)); - if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) { - brcmf_err("firmware rejected country setting\n"); - return; - } - brcmf_setup_wiphybands(wiphy); -} - -static void brcmf_free_wiphy(struct wiphy *wiphy) -{ - int i; - - if (!wiphy) - return; - - if (wiphy->iface_combinations) { - for (i = 0; i < wiphy->n_iface_combinations; i++) - kfree(wiphy->iface_combinations[i].limits); - } - kfree(wiphy->iface_combinations); - if (wiphy->bands[IEEE80211_BAND_2GHZ]) { - kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); - kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); - } - if (wiphy->bands[IEEE80211_BAND_5GHZ]) { - kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels); - kfree(wiphy->bands[IEEE80211_BAND_5GHZ]); - } - wiphy_free(wiphy); -} - -struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev, - bool p2pdev_forced) -{ - struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev; - struct brcmf_cfg80211_info *cfg; - struct wiphy *wiphy; - struct brcmf_cfg80211_vif *vif; - struct brcmf_if *ifp; - s32 err = 0; - s32 io_type; - u16 *cap = NULL; - - if (!ndev) { - brcmf_err("ndev is invalid\n"); - return NULL; - } - - ifp = netdev_priv(ndev); - wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); - if (!wiphy) { - brcmf_err("Could not allocate wiphy device\n"); - return NULL; - } - memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN); - set_wiphy_dev(wiphy, busdev); - - cfg = wiphy_priv(wiphy); - cfg->wiphy = wiphy; - cfg->pub = drvr; - init_vif_event(&cfg->vif_event); - INIT_LIST_HEAD(&cfg->vif_list); - - vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false); - if (IS_ERR(vif)) - goto wiphy_out; - - vif->ifp = ifp; - vif->wdev.netdev = ndev; - ndev->ieee80211_ptr = &vif->wdev; - SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); - - err = wl_init_priv(cfg); - if (err) { - brcmf_err("Failed to init iwm_priv (%d)\n", err); - brcmf_free_vif(vif); - goto wiphy_out; - } - ifp->vif = vif; - - /* determine d11 io type before wiphy setup */ - err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type); - if (err) { - brcmf_err("Failed to get D11 version (%d)\n", err); - goto priv_out; - } - cfg->d11inf.io_type = (u8)io_type; - brcmu_d11_attach(&cfg->d11inf); - - err = brcmf_setup_wiphy(wiphy, ifp); - if (err < 0) - goto priv_out; - - brcmf_dbg(INFO, "Registering custom regulatory\n"); - wiphy->reg_notifier = brcmf_cfg80211_reg_notifier; - wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); - - /* firmware defaults to 40MHz disabled in 2G band. We signal - * cfg80211 here that we do and have it decide we can enable - * it. But first check if device does support 2G operation. - */ - if (wiphy->bands[IEEE80211_BAND_2GHZ]) { - cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap; - *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - } - err = wiphy_register(wiphy); - if (err < 0) { - brcmf_err("Could not register wiphy device (%d)\n", err); - goto priv_out; - } - - /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), - * setup 40MHz in 2GHz band and enable OBSS scanning. - */ - if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) { - err = brcmf_enable_bw40_2g(cfg); - if (!err) - err = brcmf_fil_iovar_int_set(ifp, "obss_coex", - BRCMF_OBSS_COEX_AUTO); - else - *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - } - /* p2p might require that "if-events" get processed by fweh. So - * activate the already registered event handlers now and activate - * the rest when initialization has completed. drvr->config needs to - * be assigned before activating events. - */ - drvr->config = cfg; - err = brcmf_fweh_activate_events(ifp); - if (err) { - brcmf_err("FWEH activation failed (%d)\n", err); - goto wiphy_unreg_out; - } - - err = brcmf_p2p_attach(cfg, p2pdev_forced); - if (err) { - brcmf_err("P2P initilisation failed (%d)\n", err); - goto wiphy_unreg_out; - } - err = brcmf_btcoex_attach(cfg); - if (err) { - brcmf_err("BT-coex initialisation failed (%d)\n", err); - brcmf_p2p_detach(&cfg->p2p); - goto wiphy_unreg_out; - } - - err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); - if (err) { - brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); - wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; - } else { - brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT, - brcmf_notify_tdls_peer_event); - } - - /* (re-) activate FWEH event handling */ - err = brcmf_fweh_activate_events(ifp); - if (err) { - brcmf_err("FWEH activation failed (%d)\n", err); - goto wiphy_unreg_out; - } - - return cfg; - -wiphy_unreg_out: - wiphy_unregister(cfg->wiphy); -priv_out: - wl_deinit_priv(cfg); - brcmf_free_vif(vif); - ifp->vif = NULL; -wiphy_out: - brcmf_free_wiphy(wiphy); - return NULL; -} - -void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) -{ - if (!cfg) - return; - - brcmf_btcoex_detach(cfg); - wiphy_unregister(cfg->wiphy); - wl_deinit_priv(cfg); - brcmf_free_wiphy(cfg->wiphy); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h deleted file mode 100644 index 6a878c8f883f..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Copyright (c) 2010 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_CFG80211_H -#define BRCMFMAC_CFG80211_H - -/* for brcmu_d11inf */ -#include - -#define WL_NUM_SCAN_MAX 10 -#define WL_NUM_PMKIDS_MAX MAXPMKID -#define WL_TLV_INFO_MAX 1024 -#define WL_BSS_INFO_MAX 2048 -#define WL_ASSOC_INFO_MAX 512 /* assoc related fil max buf */ -#define WL_EXTRA_BUF_MAX 2048 -#define WL_ROAM_TRIGGER_LEVEL -75 -#define WL_ROAM_DELTA 20 -#define WL_BEACON_TIMEOUT 3 - -#define WL_SCAN_CHANNEL_TIME 40 -#define WL_SCAN_UNASSOC_TIME 40 -#define WL_SCAN_PASSIVE_TIME 120 - -#define WL_ESCAN_BUF_SIZE (1024 * 64) -#define WL_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */ - -#define WL_ESCAN_ACTION_START 1 -#define WL_ESCAN_ACTION_CONTINUE 2 -#define WL_ESCAN_ACTION_ABORT 3 - -#define WL_AUTH_SHARED_KEY 1 /* d11 shared authentication */ -#define IE_MAX_LEN 512 - -/* IE TLV processing */ -#define TLV_LEN_OFF 1 /* length offset */ -#define TLV_HDR_LEN 2 /* header length */ -#define TLV_BODY_OFF 2 /* body offset */ -#define TLV_OUI_LEN 3 /* oui id length */ - -/* 802.11 Mgmt Packet flags */ -#define BRCMF_VNDR_IE_BEACON_FLAG 0x1 -#define BRCMF_VNDR_IE_PRBRSP_FLAG 0x2 -#define BRCMF_VNDR_IE_ASSOCRSP_FLAG 0x4 -#define BRCMF_VNDR_IE_AUTHRSP_FLAG 0x8 -#define BRCMF_VNDR_IE_PRBREQ_FLAG 0x10 -#define BRCMF_VNDR_IE_ASSOCREQ_FLAG 0x20 -/* vendor IE in IW advertisement protocol ID field */ -#define BRCMF_VNDR_IE_IWAPID_FLAG 0x40 -/* allow custom IE id */ -#define BRCMF_VNDR_IE_CUSTOM_FLAG 0x100 - -/* P2P Action Frames flags (spec ordered) */ -#define BRCMF_VNDR_IE_GONREQ_FLAG 0x001000 -#define BRCMF_VNDR_IE_GONRSP_FLAG 0x002000 -#define BRCMF_VNDR_IE_GONCFM_FLAG 0x004000 -#define BRCMF_VNDR_IE_INVREQ_FLAG 0x008000 -#define BRCMF_VNDR_IE_INVRSP_FLAG 0x010000 -#define BRCMF_VNDR_IE_DISREQ_FLAG 0x020000 -#define BRCMF_VNDR_IE_DISRSP_FLAG 0x040000 -#define BRCMF_VNDR_IE_PRDREQ_FLAG 0x080000 -#define BRCMF_VNDR_IE_PRDRSP_FLAG 0x100000 - -#define BRCMF_VNDR_IE_P2PAF_SHIFT 12 - -#define BRCMF_MAX_DEFAULT_KEYS 4 - - -/** - * enum brcmf_scan_status - scan engine status - * - * @BRCMF_SCAN_STATUS_BUSY: scanning in progress on dongle. - * @BRCMF_SCAN_STATUS_ABORT: scan being aborted on dongle. - * @BRCMF_SCAN_STATUS_SUPPRESS: scanning is suppressed in driver. - */ -enum brcmf_scan_status { - BRCMF_SCAN_STATUS_BUSY, - BRCMF_SCAN_STATUS_ABORT, - BRCMF_SCAN_STATUS_SUPPRESS, -}; - -/* dongle configuration */ -struct brcmf_cfg80211_conf { - u32 frag_threshold; - u32 rts_threshold; - u32 retry_short; - u32 retry_long; - s32 tx_power; - struct ieee80211_channel channel; -}; - -/* basic structure of scan request */ -struct brcmf_cfg80211_scan_req { - struct brcmf_ssid_le ssid_le; -}; - -/* basic structure of information element */ -struct brcmf_cfg80211_ie { - u16 offset; - u8 buf[WL_TLV_INFO_MAX]; -}; - -/* security information with currently associated ap */ -struct brcmf_cfg80211_security { - u32 wpa_versions; - u32 auth_type; - u32 cipher_pairwise; - u32 cipher_group; - u32 wpa_auth; -}; - -/** - * struct brcmf_cfg80211_profile - profile information. - * - * @ssid: ssid of associated/associating ap. - * @bssid: bssid of joined/joining ibss. - * @sec: security information. - * @key: key information - */ -struct brcmf_cfg80211_profile { - struct brcmf_ssid ssid; - u8 bssid[ETH_ALEN]; - struct brcmf_cfg80211_security sec; - struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS]; -}; - -/** - * enum brcmf_vif_status - bit indices for vif status. - * - * @BRCMF_VIF_STATUS_READY: ready for operation. - * @BRCMF_VIF_STATUS_CONNECTING: connect/join in progress. - * @BRCMF_VIF_STATUS_CONNECTED: connected/joined succesfully. - * @BRCMF_VIF_STATUS_DISCONNECTING: disconnect/disable in progress. - * @BRCMF_VIF_STATUS_AP_CREATED: AP operation started. - */ -enum brcmf_vif_status { - BRCMF_VIF_STATUS_READY, - BRCMF_VIF_STATUS_CONNECTING, - BRCMF_VIF_STATUS_CONNECTED, - BRCMF_VIF_STATUS_DISCONNECTING, - BRCMF_VIF_STATUS_AP_CREATED -}; - -/** - * struct vif_saved_ie - holds saved IEs for a virtual interface. - * - * @probe_req_ie: IE info for probe request. - * @probe_res_ie: IE info for probe response. - * @beacon_ie: IE info for beacon frame. - * @probe_req_ie_len: IE info length for probe request. - * @probe_res_ie_len: IE info length for probe response. - * @beacon_ie_len: IE info length for beacon frame. - */ -struct vif_saved_ie { - u8 probe_req_ie[IE_MAX_LEN]; - u8 probe_res_ie[IE_MAX_LEN]; - u8 beacon_ie[IE_MAX_LEN]; - u8 assoc_req_ie[IE_MAX_LEN]; - u32 probe_req_ie_len; - u32 probe_res_ie_len; - u32 beacon_ie_len; - u32 assoc_req_ie_len; -}; - -/** - * struct brcmf_cfg80211_vif - virtual interface specific information. - * - * @ifp: lower layer interface pointer - * @wdev: wireless device. - * @profile: profile information. - * @roam_off: roaming state. - * @sme_state: SME state using enum brcmf_vif_status bits. - * @pm_block: power-management blocked. - * @list: linked list. - * @mgmt_rx_reg: registered rx mgmt frame types. - * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P). - */ -struct brcmf_cfg80211_vif { - struct brcmf_if *ifp; - struct wireless_dev wdev; - struct brcmf_cfg80211_profile profile; - s32 roam_off; - unsigned long sme_state; - bool pm_block; - struct vif_saved_ie saved_ie; - struct list_head list; - u16 mgmt_rx_reg; - bool mbss; - int is_11d; -}; - -/* association inform */ -struct brcmf_cfg80211_connect_info { - u8 *req_ie; - s32 req_ie_len; - u8 *resp_ie; - s32 resp_ie_len; -}; - -/* assoc ie length */ -struct brcmf_cfg80211_assoc_ielen_le { - __le32 req_len; - __le32 resp_len; -}; - -/* wpa2 pmk list */ -struct brcmf_cfg80211_pmk_list { - struct pmkid_list pmkids; - struct pmkid foo[MAXPMKID - 1]; -}; - -/* dongle escan state */ -enum wl_escan_state { - WL_ESCAN_STATE_IDLE, - WL_ESCAN_STATE_SCANNING -}; - -struct escan_info { - u32 escan_state; - u8 escan_buf[WL_ESCAN_BUF_SIZE]; - struct wiphy *wiphy; - struct brcmf_if *ifp; - s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, - struct cfg80211_scan_request *request, u16 action); -}; - -/** - * struct brcmf_pno_param_le - PNO scan configuration parameters - * - * @version: PNO parameters version. - * @scan_freq: scan frequency. - * @lost_network_timeout: #sec. to declare discovered network as lost. - * @flags: Bit field to control features of PFN such as sort criteria auto - * enable switch and background scan. - * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort - * criteria. - * @bestn: number of best networks in each scan. - * @mscan: number of scans recorded. - * @repeat: minimum number of scan intervals before scan frequency changes - * in adaptive scan. - * @exp: exponent of 2 for maximum scan interval. - * @slow_freq: slow scan period. - */ -struct brcmf_pno_param_le { - __le32 version; - __le32 scan_freq; - __le32 lost_network_timeout; - __le16 flags; - __le16 rssi_margin; - u8 bestn; - u8 mscan; - u8 repeat; - u8 exp; - __le32 slow_freq; -}; - -/** - * struct brcmf_pno_net_param_le - scan parameters per preferred network. - * - * @ssid: ssid name and its length. - * @flags: bit2: hidden. - * @infra: BSS vs IBSS. - * @auth: Open vs Closed. - * @wpa_auth: WPA type. - * @wsec: wsec value. - */ -struct brcmf_pno_net_param_le { - struct brcmf_ssid_le ssid; - __le32 flags; - __le32 infra; - __le32 auth; - __le32 wpa_auth; - __le32 wsec; -}; - -/** - * struct brcmf_pno_net_info_le - information per found network. - * - * @bssid: BSS network identifier. - * @channel: channel number only. - * @SSID_len: length of ssid. - * @SSID: ssid characters. - * @RSSI: receive signal strength (in dBm). - * @timestamp: age in seconds. - */ -struct brcmf_pno_net_info_le { - u8 bssid[ETH_ALEN]; - u8 channel; - u8 SSID_len; - u8 SSID[32]; - __le16 RSSI; - __le16 timestamp; -}; - -/** - * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event. - * - * @version: PNO version identifier. - * @status: indicates completion status of PNO scan. - * @count: amount of brcmf_pno_net_info_le entries appended. - */ -struct brcmf_pno_scanresults_le { - __le32 version; - __le32 status; - __le32 count; -}; - -/** - * struct brcmf_cfg80211_vif_event - virtual interface event information. - * - * @vif_wq: waitqueue awaiting interface event from firmware. - * @vif_event_lock: protects other members in this structure. - * @vif_complete: completion for net attach. - * @action: either add, change, or delete. - * @vif: virtual interface object related to the event. - */ -struct brcmf_cfg80211_vif_event { - wait_queue_head_t vif_wq; - struct mutex vif_event_lock; - u8 action; - struct brcmf_cfg80211_vif *vif; -}; - -/** - * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface - * - * @wiphy: wiphy object for cfg80211 interface. - * @conf: dongle configuration. - * @p2p: peer-to-peer specific information. - * @btcoex: Bluetooth coexistence information. - * @scan_request: cfg80211 scan request object. - * @usr_sync: mainly for dongle up/down synchronization. - * @bss_list: bss_list holding scanned ap information. - * @scan_req_int: internal scan request object. - * @bss_info: bss information for cfg80211 layer. - * @ie: information element object for internal purpose. - * @conn_info: association info. - * @pmk_list: wpa2 pmk list. - * @scan_status: scan activity on the dongle. - * @pub: common driver information. - * @channel: current channel. - * @active_scan: current scan mode. - * @sched_escan: e-scan for scheduled scan support running. - * @ibss_starter: indicates this sta is ibss starter. - * @pwr_save: indicate whether dongle to support power save mode. - * @dongle_up: indicate whether dongle up or not. - * @roam_on: on/off switch for dongle self-roaming. - * @scan_tried: indicates if first scan attempted. - * @dcmd_buf: dcmd buffer. - * @extra_buf: mainly to grab assoc information. - * @debugfsdir: debugfs folder for this device. - * @escan_info: escan information. - * @escan_timeout: Timer for catch scan timeout. - * @escan_timeout_work: scan timeout worker. - * @escan_ioctl_buf: dongle command buffer for escan commands. - * @vif_list: linked list of vif instances. - * @vif_cnt: number of vif instances. - * @vif_event: vif event signalling. - * @wowl_enabled; set during suspend, is wowl used. - * @pre_wowl_pmmode: intermediate storage of pm mode during wowl. - */ -struct brcmf_cfg80211_info { - struct wiphy *wiphy; - struct brcmf_cfg80211_conf *conf; - struct brcmf_p2p_info p2p; - struct brcmf_btcoex_info *btcoex; - struct cfg80211_scan_request *scan_request; - struct mutex usr_sync; - struct brcmf_cfg80211_scan_req scan_req_int; - struct wl_cfg80211_bss_info *bss_info; - struct brcmf_cfg80211_ie ie; - struct brcmf_cfg80211_connect_info conn_info; - struct brcmf_cfg80211_pmk_list *pmk_list; - unsigned long scan_status; - struct brcmf_pub *pub; - u32 channel; - bool active_scan; - bool sched_escan; - bool ibss_starter; - bool pwr_save; - bool dongle_up; - bool scan_tried; - u8 *dcmd_buf; - u8 *extra_buf; - struct dentry *debugfsdir; - struct escan_info escan_info; - struct timer_list escan_timeout; - struct work_struct escan_timeout_work; - u8 *escan_ioctl_buf; - struct list_head vif_list; - struct brcmf_cfg80211_vif_event vif_event; - struct completion vif_disabled; - struct brcmu_d11inf d11inf; - bool wowl_enabled; - u32 pre_wowl_pmmode; - struct brcmf_assoclist_le assoclist; -}; - -/** - * struct brcmf_tlv - tag_ID/length/value_buffer tuple. - * - * @id: tag identifier. - * @len: number of bytes in value buffer. - * @data: value buffer. - */ -struct brcmf_tlv { - u8 id; - u8 len; - u8 data[1]; -}; - -static inline struct wiphy *cfg_to_wiphy(struct brcmf_cfg80211_info *cfg) -{ - return cfg->wiphy; -} - -static inline struct brcmf_cfg80211_info *wiphy_to_cfg(struct wiphy *w) -{ - return (struct brcmf_cfg80211_info *)(wiphy_priv(w)); -} - -static inline struct brcmf_cfg80211_info *wdev_to_cfg(struct wireless_dev *wd) -{ - return (struct brcmf_cfg80211_info *)(wdev_priv(wd)); -} - -static inline -struct net_device *cfg_to_ndev(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_vif *vif; - vif = list_first_entry(&cfg->vif_list, struct brcmf_cfg80211_vif, list); - return vif->wdev.netdev; -} - -static inline struct brcmf_cfg80211_info *ndev_to_cfg(struct net_device *ndev) -{ - return wdev_to_cfg(ndev->ieee80211_ptr); -} - -static inline struct brcmf_cfg80211_profile *ndev_to_prof(struct net_device *nd) -{ - struct brcmf_if *ifp = netdev_priv(nd); - return &ifp->vif->profile; -} - -static inline struct brcmf_cfg80211_vif *ndev_to_vif(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - return ifp->vif; -} - -static inline struct -brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg) -{ - return &cfg->conn_info; -} - -struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev, - bool p2pdev_forced); -void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg); -s32 brcmf_cfg80211_up(struct net_device *ndev); -s32 brcmf_cfg80211_down(struct net_device *ndev); -enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp); - -struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, - enum nl80211_iftype type, - bool pm_block); -void brcmf_free_vif(struct brcmf_cfg80211_vif *vif); - -s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, - const u8 *vndr_ie_buf, u32 vndr_ie_len); -s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif); -const struct brcmf_tlv * -brcmf_parse_tlvs(const void *buf, int buflen, uint key); -u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, - struct ieee80211_channel *ch); -bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, - unsigned long state); -void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, - struct brcmf_cfg80211_vif *vif); -bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg); -int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, - u8 action, ulong timeout); -s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp, bool aborted, - bool fw_abort); -void brcmf_set_mpc(struct brcmf_if *ndev, int mpc); -void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg); -void brcmf_cfg80211_free_netdev(struct net_device *ndev); - -#endif /* BRCMFMAC_CFG80211_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c deleted file mode 100644 index f04833db2fd0..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ /dev/null @@ -1,1331 +0,0 @@ -/* - * Copyright (c) 2014 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 -#include -#include - -#include -#include -#include -#include -#include -#include "debug.h" -#include "chip.h" - -/* SOC Interconnect types (aka chip types) */ -#define SOCI_SB 0 -#define SOCI_AI 1 - -/* PL-368 DMP definitions */ -#define DMP_DESC_TYPE_MSK 0x0000000F -#define DMP_DESC_EMPTY 0x00000000 -#define DMP_DESC_VALID 0x00000001 -#define DMP_DESC_COMPONENT 0x00000001 -#define DMP_DESC_MASTER_PORT 0x00000003 -#define DMP_DESC_ADDRESS 0x00000005 -#define DMP_DESC_ADDRSIZE_GT32 0x00000008 -#define DMP_DESC_EOT 0x0000000F - -#define DMP_COMP_DESIGNER 0xFFF00000 -#define DMP_COMP_DESIGNER_S 20 -#define DMP_COMP_PARTNUM 0x000FFF00 -#define DMP_COMP_PARTNUM_S 8 -#define DMP_COMP_CLASS 0x000000F0 -#define DMP_COMP_CLASS_S 4 -#define DMP_COMP_REVISION 0xFF000000 -#define DMP_COMP_REVISION_S 24 -#define DMP_COMP_NUM_SWRAP 0x00F80000 -#define DMP_COMP_NUM_SWRAP_S 19 -#define DMP_COMP_NUM_MWRAP 0x0007C000 -#define DMP_COMP_NUM_MWRAP_S 14 -#define DMP_COMP_NUM_SPORT 0x00003E00 -#define DMP_COMP_NUM_SPORT_S 9 -#define DMP_COMP_NUM_MPORT 0x000001F0 -#define DMP_COMP_NUM_MPORT_S 4 - -#define DMP_MASTER_PORT_UID 0x0000FF00 -#define DMP_MASTER_PORT_UID_S 8 -#define DMP_MASTER_PORT_NUM 0x000000F0 -#define DMP_MASTER_PORT_NUM_S 4 - -#define DMP_SLAVE_ADDR_BASE 0xFFFFF000 -#define DMP_SLAVE_ADDR_BASE_S 12 -#define DMP_SLAVE_PORT_NUM 0x00000F00 -#define DMP_SLAVE_PORT_NUM_S 8 -#define DMP_SLAVE_TYPE 0x000000C0 -#define DMP_SLAVE_TYPE_S 6 -#define DMP_SLAVE_TYPE_SLAVE 0 -#define DMP_SLAVE_TYPE_BRIDGE 1 -#define DMP_SLAVE_TYPE_SWRAP 2 -#define DMP_SLAVE_TYPE_MWRAP 3 -#define DMP_SLAVE_SIZE_TYPE 0x00000030 -#define DMP_SLAVE_SIZE_TYPE_S 4 -#define DMP_SLAVE_SIZE_4K 0 -#define DMP_SLAVE_SIZE_8K 1 -#define DMP_SLAVE_SIZE_16K 2 -#define DMP_SLAVE_SIZE_DESC 3 - -/* EROM CompIdentB */ -#define CIB_REV_MASK 0xff000000 -#define CIB_REV_SHIFT 24 - -/* 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 - -/* chip core base & ramsize */ -/* bcm4329 */ -/* SDIO device core, ID 0x829 */ -#define BCM4329_CORE_BUS_BASE 0x18011000 -/* internal memory core, ID 0x80e */ -#define BCM4329_CORE_SOCRAM_BASE 0x18003000 -/* ARM Cortex M3 core, ID 0x82a */ -#define BCM4329_CORE_ARM_BASE 0x18002000 - -/* Max possibly supported memory size (limited by IO mapped memory) */ -#define BRCMF_CHIP_MAX_MEMSIZE (4 * 1024 * 1024) - -#define CORE_SB(base, field) \ - (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) -#define SBCOREREV(sbidh) \ - ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ - ((sbidh) & SSB_IDHIGH_RCLO)) - -struct sbconfig { - u32 PAD[2]; - u32 sbipsflag; /* initiator port ocp slave flag */ - u32 PAD[3]; - u32 sbtpsflag; /* target port ocp slave flag */ - u32 PAD[11]; - u32 sbtmerrloga; /* (sonics >= 2.3) */ - u32 PAD; - u32 sbtmerrlog; /* (sonics >= 2.3) */ - u32 PAD[3]; - u32 sbadmatch3; /* address match3 */ - u32 PAD; - u32 sbadmatch2; /* address match2 */ - u32 PAD; - u32 sbadmatch1; /* address match1 */ - u32 PAD[7]; - u32 sbimstate; /* initiator agent state */ - u32 sbintvec; /* interrupt mask */ - u32 sbtmstatelow; /* target state */ - u32 sbtmstatehigh; /* target state */ - u32 sbbwa0; /* bandwidth allocation table0 */ - u32 PAD; - u32 sbimconfiglow; /* initiator configuration */ - u32 sbimconfighigh; /* initiator configuration */ - u32 sbadmatch0; /* address match0 */ - u32 PAD; - u32 sbtmconfiglow; /* target configuration */ - u32 sbtmconfighigh; /* target configuration */ - u32 sbbconfig; /* broadcast configuration */ - u32 PAD; - u32 sbbstate; /* broadcast state */ - u32 PAD[3]; - u32 sbactcnfg; /* activate configuration */ - u32 PAD[3]; - u32 sbflagst; /* current sbflags */ - u32 PAD[3]; - u32 sbidlow; /* identification */ - u32 sbidhigh; /* identification */ -}; - -/* bankidx and bankinfo reg defines corerev >= 8 */ -#define SOCRAM_BANKINFO_RETNTRAM_MASK 0x00010000 -#define SOCRAM_BANKINFO_SZMASK 0x0000007f -#define SOCRAM_BANKIDX_ROM_MASK 0x00000100 - -#define SOCRAM_BANKIDX_MEMTYPE_SHIFT 8 -/* socram bankinfo memtype */ -#define SOCRAM_MEMTYPE_RAM 0 -#define SOCRAM_MEMTYPE_R0M 1 -#define SOCRAM_MEMTYPE_DEVRAM 2 - -#define SOCRAM_BANKINFO_SZBASE 8192 -#define SRCI_LSS_MASK 0x00f00000 -#define SRCI_LSS_SHIFT 20 -#define SRCI_SRNB_MASK 0xf0 -#define SRCI_SRNB_SHIFT 4 -#define SRCI_SRBSZ_MASK 0xf -#define SRCI_SRBSZ_SHIFT 0 -#define SR_BSZ_BASE 14 - -struct sbsocramregs { - u32 coreinfo; - u32 bwalloc; - u32 extracoreinfo; - u32 biststat; - u32 bankidx; - u32 standbyctrl; - - u32 errlogstatus; /* rev 6 */ - u32 errlogaddr; /* rev 6 */ - /* used for patching rev 3 & 5 */ - u32 cambankidx; - u32 cambankstandbyctrl; - u32 cambankpatchctrl; - u32 cambankpatchtblbaseaddr; - u32 cambankcmdreg; - u32 cambankdatareg; - u32 cambankmaskreg; - u32 PAD[1]; - u32 bankinfo; /* corev 8 */ - u32 bankpda; - u32 PAD[14]; - u32 extmemconfig; - u32 extmemparitycsr; - u32 extmemparityerrdata; - u32 extmemparityerrcnt; - u32 extmemwrctrlandsize; - u32 PAD[84]; - u32 workaround; - u32 pwrctl; /* corerev >= 2 */ - u32 PAD[133]; - u32 sr_control; /* corerev >= 15 */ - u32 sr_status; /* corerev >= 15 */ - u32 sr_address; /* corerev >= 15 */ - u32 sr_data; /* corerev >= 15 */ -}; - -#define SOCRAMREGOFFS(_f) offsetof(struct sbsocramregs, _f) -#define SYSMEMREGOFFS(_f) offsetof(struct sbsocramregs, _f) - -#define ARMCR4_CAP (0x04) -#define ARMCR4_BANKIDX (0x40) -#define ARMCR4_BANKINFO (0x44) -#define ARMCR4_BANKPDA (0x4C) - -#define ARMCR4_TCBBNB_MASK 0xf0 -#define ARMCR4_TCBBNB_SHIFT 4 -#define ARMCR4_TCBANB_MASK 0xf -#define ARMCR4_TCBANB_SHIFT 0 - -#define ARMCR4_BSZ_MASK 0x3f -#define ARMCR4_BSZ_MULT 8192 - -struct brcmf_core_priv { - struct brcmf_core pub; - u32 wrapbase; - struct list_head list; - struct brcmf_chip_priv *chip; -}; - -struct brcmf_chip_priv { - struct brcmf_chip pub; - const struct brcmf_buscore_ops *ops; - void *ctx; - /* assured first core is chipcommon, second core is buscore */ - struct list_head cores; - u16 num_cores; - - bool (*iscoreup)(struct brcmf_core_priv *core); - void (*coredisable)(struct brcmf_core_priv *core, u32 prereset, - u32 reset); - void (*resetcore)(struct brcmf_core_priv *core, u32 prereset, u32 reset, - u32 postreset); -}; - -static void brcmf_chip_sb_corerev(struct brcmf_chip_priv *ci, - struct brcmf_core *core) -{ - u32 regdata; - - regdata = ci->ops->read32(ci->ctx, CORE_SB(core->base, sbidhigh)); - core->rev = SBCOREREV(regdata); -} - -static bool brcmf_chip_sb_iscoreup(struct brcmf_core_priv *core) -{ - struct brcmf_chip_priv *ci; - u32 regdata; - u32 address; - - ci = core->chip; - address = CORE_SB(core->pub.base, sbtmstatelow); - regdata = ci->ops->read32(ci->ctx, address); - regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | - SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); - return SSB_TMSLOW_CLOCK == regdata; -} - -static bool brcmf_chip_ai_iscoreup(struct brcmf_core_priv *core) -{ - struct brcmf_chip_priv *ci; - u32 regdata; - bool ret; - - ci = core->chip; - regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); - ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; - - regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL); - ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); - - return ret; -} - -static void brcmf_chip_sb_coredisable(struct brcmf_core_priv *core, - u32 prereset, u32 reset) -{ - struct brcmf_chip_priv *ci; - u32 val, base; - - ci = core->chip; - base = core->pub.base; - val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); - if (val & SSB_TMSLOW_RESET) - return; - - val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); - if ((val & SSB_TMSLOW_CLOCK) != 0) { - /* - * set target reject and spin until busy is clear - * (preserve core-specific bits) - */ - val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); - ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), - val | SSB_TMSLOW_REJECT); - - val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); - udelay(1); - SPINWAIT((ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)) - & SSB_TMSHIGH_BUSY), 100000); - - val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)); - if (val & SSB_TMSHIGH_BUSY) - brcmf_err("core state still busy\n"); - - val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow)); - if (val & SSB_IDLOW_INITIATOR) { - val = ci->ops->read32(ci->ctx, - CORE_SB(base, sbimstate)); - val |= SSB_IMSTATE_REJECT; - ci->ops->write32(ci->ctx, - CORE_SB(base, sbimstate), val); - val = ci->ops->read32(ci->ctx, - CORE_SB(base, sbimstate)); - udelay(1); - SPINWAIT((ci->ops->read32(ci->ctx, - CORE_SB(base, sbimstate)) & - SSB_IMSTATE_BUSY), 100000); - } - - /* set reset and reject while enabling the clocks */ - val = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | - SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; - ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), val); - val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); - udelay(10); - - /* clear the initiator reject bit */ - val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow)); - if (val & SSB_IDLOW_INITIATOR) { - val = ci->ops->read32(ci->ctx, - CORE_SB(base, sbimstate)); - val &= ~SSB_IMSTATE_REJECT; - ci->ops->write32(ci->ctx, - CORE_SB(base, sbimstate), val); - } - } - - /* leave reset and reject asserted */ - ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), - (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET)); - udelay(1); -} - -static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core, - u32 prereset, u32 reset) -{ - struct brcmf_chip_priv *ci; - u32 regdata; - - ci = core->chip; - - /* if core is already in reset, skip reset */ - regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL); - if ((regdata & BCMA_RESET_CTL_RESET) != 0) - goto in_reset_configure; - - /* configure reset */ - ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, - prereset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK); - ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); - - /* put in reset */ - ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, - BCMA_RESET_CTL_RESET); - usleep_range(10, 20); - - /* wait till reset is 1 */ - SPINWAIT(ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) != - BCMA_RESET_CTL_RESET, 300); - -in_reset_configure: - /* in-reset configure */ - ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, - reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK); - ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); -} - -static void brcmf_chip_sb_resetcore(struct brcmf_core_priv *core, u32 prereset, - u32 reset, u32 postreset) -{ - struct brcmf_chip_priv *ci; - u32 regdata; - u32 base; - - ci = core->chip; - base = core->pub.base; - /* - * Must do the disable sequence first to work for - * arbitrary current core state. - */ - brcmf_chip_sb_coredisable(core, 0, 0); - - /* - * Now do the initialization sequence. - * set reset while enabling the clock and - * forcing them on throughout the core - */ - ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), - SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | - SSB_TMSLOW_RESET); - regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); - udelay(1); - - /* clear any serror */ - regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)); - if (regdata & SSB_TMSHIGH_SERR) - ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatehigh), 0); - - regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbimstate)); - if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) { - regdata &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO); - ci->ops->write32(ci->ctx, CORE_SB(base, sbimstate), regdata); - } - - /* clear reset and allow it to propagate throughout the core */ - ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), - SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK); - regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); - udelay(1); - - /* leave clock enabled */ - ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), - SSB_TMSLOW_CLOCK); - regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); - udelay(1); -} - -static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset, - u32 reset, u32 postreset) -{ - struct brcmf_chip_priv *ci; - int count; - - ci = core->chip; - - /* must disable first to work for arbitrary current core state */ - brcmf_chip_ai_coredisable(core, prereset, reset); - - count = 0; - while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) & - BCMA_RESET_CTL_RESET) { - ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, 0); - count++; - if (count > 50) - break; - usleep_range(40, 60); - } - - ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, - postreset | BCMA_IOCTL_CLK); - ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); -} - -static char *brcmf_chip_name(uint chipid, char *buf, uint len) -{ - const char *fmt; - - fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; - snprintf(buf, len, fmt, chipid); - return buf; -} - -static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci, - u16 coreid, u32 base, - u32 wrapbase) -{ - struct brcmf_core_priv *core; - - core = kzalloc(sizeof(*core), GFP_KERNEL); - if (!core) - return ERR_PTR(-ENOMEM); - - core->pub.id = coreid; - core->pub.base = base; - core->chip = ci; - core->wrapbase = wrapbase; - - list_add_tail(&core->list, &ci->cores); - return &core->pub; -} - -/* safety check for chipinfo */ -static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) -{ - struct brcmf_core_priv *core; - bool need_socram = false; - bool has_socram = false; - bool cpu_found = false; - int idx = 1; - - list_for_each_entry(core, &ci->cores, list) { - brcmf_dbg(INFO, " [%-2d] core 0x%x:%-2d base 0x%08x wrap 0x%08x\n", - idx++, core->pub.id, core->pub.rev, core->pub.base, - core->wrapbase); - - switch (core->pub.id) { - case BCMA_CORE_ARM_CM3: - cpu_found = true; - need_socram = true; - break; - case BCMA_CORE_INTERNAL_MEM: - has_socram = true; - break; - case BCMA_CORE_ARM_CR4: - cpu_found = true; - break; - case BCMA_CORE_ARM_CA7: - cpu_found = true; - break; - default: - break; - } - } - - if (!cpu_found) { - brcmf_err("CPU core not detected\n"); - return -ENXIO; - } - /* check RAM core presence for ARM CM3 core */ - if (need_socram && !has_socram) { - brcmf_err("RAM core not provided with ARM CM3 core\n"); - return -ENODEV; - } - return 0; -} - -static u32 brcmf_chip_core_read32(struct brcmf_core_priv *core, u16 reg) -{ - return core->chip->ops->read32(core->chip->ctx, core->pub.base + reg); -} - -static void brcmf_chip_core_write32(struct brcmf_core_priv *core, - u16 reg, u32 val) -{ - core->chip->ops->write32(core->chip->ctx, core->pub.base + reg, val); -} - -static bool brcmf_chip_socram_banksize(struct brcmf_core_priv *core, u8 idx, - u32 *banksize) -{ - u32 bankinfo; - u32 bankidx = (SOCRAM_MEMTYPE_RAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); - - bankidx |= idx; - brcmf_chip_core_write32(core, SOCRAMREGOFFS(bankidx), bankidx); - bankinfo = brcmf_chip_core_read32(core, SOCRAMREGOFFS(bankinfo)); - *banksize = (bankinfo & SOCRAM_BANKINFO_SZMASK) + 1; - *banksize *= SOCRAM_BANKINFO_SZBASE; - return !!(bankinfo & SOCRAM_BANKINFO_RETNTRAM_MASK); -} - -static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, - u32 *srsize) -{ - u32 coreinfo; - uint nb, banksize, lss; - bool retent; - int i; - - *ramsize = 0; - *srsize = 0; - - if (WARN_ON(sr->pub.rev < 4)) - return; - - if (!brcmf_chip_iscoreup(&sr->pub)) - brcmf_chip_resetcore(&sr->pub, 0, 0, 0); - - /* Get info for determining size */ - coreinfo = brcmf_chip_core_read32(sr, SOCRAMREGOFFS(coreinfo)); - nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; - - if ((sr->pub.rev <= 7) || (sr->pub.rev == 12)) { - banksize = (coreinfo & SRCI_SRBSZ_MASK); - lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT; - if (lss != 0) - nb--; - *ramsize = nb * (1 << (banksize + SR_BSZ_BASE)); - if (lss != 0) - *ramsize += (1 << ((lss - 1) + SR_BSZ_BASE)); - } else { - nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; - for (i = 0; i < nb; i++) { - retent = brcmf_chip_socram_banksize(sr, i, &banksize); - *ramsize += banksize; - if (retent) - *srsize += banksize; - } - } - - /* hardcoded save&restore memory sizes */ - switch (sr->chip->pub.chip) { - case BRCM_CC_4334_CHIP_ID: - if (sr->chip->pub.chiprev < 2) - *srsize = (32 * 1024); - break; - case BRCM_CC_43430_CHIP_ID: - /* assume sr for now as we can not check - * firmware sr capability at this point. - */ - *srsize = (64 * 1024); - break; - default: - break; - } -} - -/** Return the SYS MEM size */ -static u32 brcmf_chip_sysmem_ramsize(struct brcmf_core_priv *sysmem) -{ - u32 memsize = 0; - u32 coreinfo; - u32 idx; - u32 nb; - u32 banksize; - - if (!brcmf_chip_iscoreup(&sysmem->pub)) - brcmf_chip_resetcore(&sysmem->pub, 0, 0, 0); - - coreinfo = brcmf_chip_core_read32(sysmem, SYSMEMREGOFFS(coreinfo)); - nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; - - for (idx = 0; idx < nb; idx++) { - brcmf_chip_socram_banksize(sysmem, idx, &banksize); - memsize += banksize; - } - - return memsize; -} - -/** Return the TCM-RAM size of the ARMCR4 core. */ -static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4) -{ - u32 corecap; - u32 memsize = 0; - u32 nab; - u32 nbb; - u32 totb; - u32 bxinfo; - u32 idx; - - corecap = brcmf_chip_core_read32(cr4, ARMCR4_CAP); - - nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT; - nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT; - totb = nab + nbb; - - for (idx = 0; idx < totb; idx++) { - brcmf_chip_core_write32(cr4, ARMCR4_BANKIDX, idx); - bxinfo = brcmf_chip_core_read32(cr4, ARMCR4_BANKINFO); - memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT; - } - - return memsize; -} - -static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) -{ - switch (ci->pub.chip) { - case BRCM_CC_4345_CHIP_ID: - return 0x198000; - case BRCM_CC_4335_CHIP_ID: - case BRCM_CC_4339_CHIP_ID: - case BRCM_CC_4350_CHIP_ID: - case BRCM_CC_4354_CHIP_ID: - case BRCM_CC_4356_CHIP_ID: - case BRCM_CC_43567_CHIP_ID: - case BRCM_CC_43569_CHIP_ID: - case BRCM_CC_43570_CHIP_ID: - case BRCM_CC_4358_CHIP_ID: - case BRCM_CC_43602_CHIP_ID: - case BRCM_CC_4371_CHIP_ID: - return 0x180000; - case BRCM_CC_4365_CHIP_ID: - case BRCM_CC_4366_CHIP_ID: - return 0x200000; - default: - brcmf_err("unknown chip: %s\n", ci->pub.name); - break; - } - return 0; -} - -static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) -{ - struct brcmf_core_priv *mem_core; - struct brcmf_core *mem; - - mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_ARM_CR4); - if (mem) { - mem_core = container_of(mem, struct brcmf_core_priv, pub); - ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core); - ci->pub.rambase = brcmf_chip_tcm_rambase(ci); - if (!ci->pub.rambase) { - brcmf_err("RAM base not provided with ARM CR4 core\n"); - return -EINVAL; - } - } else { - mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_SYS_MEM); - if (mem) { - mem_core = container_of(mem, struct brcmf_core_priv, - pub); - ci->pub.ramsize = brcmf_chip_sysmem_ramsize(mem_core); - ci->pub.rambase = brcmf_chip_tcm_rambase(ci); - if (!ci->pub.rambase) { - brcmf_err("RAM base not provided with ARM CA7 core\n"); - return -EINVAL; - } - } else { - mem = brcmf_chip_get_core(&ci->pub, - BCMA_CORE_INTERNAL_MEM); - if (!mem) { - brcmf_err("No memory cores found\n"); - return -ENOMEM; - } - mem_core = container_of(mem, struct brcmf_core_priv, - pub); - brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize, - &ci->pub.srsize); - } - } - brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n", - ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize, - ci->pub.srsize, ci->pub.srsize); - - if (!ci->pub.ramsize) { - brcmf_err("RAM size is undetermined\n"); - return -ENOMEM; - } - - if (ci->pub.ramsize > BRCMF_CHIP_MAX_MEMSIZE) { - brcmf_err("RAM size is incorrect\n"); - return -ENOMEM; - } - - return 0; -} - -static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr, - u8 *type) -{ - u32 val; - - /* read next descriptor */ - val = ci->ops->read32(ci->ctx, *eromaddr); - *eromaddr += 4; - - if (!type) - return val; - - /* determine descriptor type */ - *type = (val & DMP_DESC_TYPE_MSK); - if ((*type & ~DMP_DESC_ADDRSIZE_GT32) == DMP_DESC_ADDRESS) - *type = DMP_DESC_ADDRESS; - - return val; -} - -static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr, - u32 *regbase, u32 *wrapbase) -{ - u8 desc; - u32 val; - u8 mpnum = 0; - u8 stype, sztype, wraptype; - - *regbase = 0; - *wrapbase = 0; - - val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc); - if (desc == DMP_DESC_MASTER_PORT) { - mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S; - wraptype = DMP_SLAVE_TYPE_MWRAP; - } else if (desc == DMP_DESC_ADDRESS) { - /* revert erom address */ - *eromaddr -= 4; - wraptype = DMP_SLAVE_TYPE_SWRAP; - } else { - *eromaddr -= 4; - return -EILSEQ; - } - - do { - /* locate address descriptor */ - do { - val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc); - /* unexpected table end */ - if (desc == DMP_DESC_EOT) { - *eromaddr -= 4; - return -EFAULT; - } - } while (desc != DMP_DESC_ADDRESS); - - /* skip upper 32-bit address descriptor */ - if (val & DMP_DESC_ADDRSIZE_GT32) - brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); - - sztype = (val & DMP_SLAVE_SIZE_TYPE) >> DMP_SLAVE_SIZE_TYPE_S; - - /* next size descriptor can be skipped */ - if (sztype == DMP_SLAVE_SIZE_DESC) { - val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); - /* skip upper size descriptor if present */ - if (val & DMP_DESC_ADDRSIZE_GT32) - brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); - } - - /* only look for 4K register regions */ - if (sztype != DMP_SLAVE_SIZE_4K) - continue; - - stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S; - - /* only regular slave and wrapper */ - if (*regbase == 0 && stype == DMP_SLAVE_TYPE_SLAVE) - *regbase = val & DMP_SLAVE_ADDR_BASE; - if (*wrapbase == 0 && stype == wraptype) - *wrapbase = val & DMP_SLAVE_ADDR_BASE; - } while (*regbase == 0 || *wrapbase == 0); - - return 0; -} - -static -int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci) -{ - struct brcmf_core *core; - u32 eromaddr; - u8 desc_type = 0; - u32 val; - u16 id; - u8 nmp, nsp, nmw, nsw, rev; - u32 base, wrap; - int err; - - eromaddr = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, eromptr)); - - while (desc_type != DMP_DESC_EOT) { - val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type); - if (!(val & DMP_DESC_VALID)) - continue; - - if (desc_type == DMP_DESC_EMPTY) - continue; - - /* need a component descriptor */ - if (desc_type != DMP_DESC_COMPONENT) - continue; - - id = (val & DMP_COMP_PARTNUM) >> DMP_COMP_PARTNUM_S; - - /* next descriptor must be component as well */ - val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type); - if (WARN_ON((val & DMP_DESC_TYPE_MSK) != DMP_DESC_COMPONENT)) - return -EFAULT; - - /* only look at cores with master port(s) */ - nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S; - nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S; - nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S; - nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S; - rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S; - - /* need core with ports */ - if (nmw + nsw == 0) - continue; - - /* try to obtain register address info */ - err = brcmf_chip_dmp_get_regaddr(ci, &eromaddr, &base, &wrap); - if (err) - continue; - - /* finally a core to be added */ - core = brcmf_chip_add_core(ci, id, base, wrap); - if (IS_ERR(core)) - return PTR_ERR(core); - - core->rev = rev; - } - - return 0; -} - -static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) -{ - struct brcmf_core *core; - u32 regdata; - u32 socitype; - int ret; - - /* Get CC core rev - * 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. - */ - regdata = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, chipid)); - ci->pub.chip = regdata & CID_ID_MASK; - ci->pub.chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; - socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; - - brcmf_chip_name(ci->pub.chip, ci->pub.name, sizeof(ci->pub.name)); - brcmf_dbg(INFO, "found %s chip: BCM%s, rev=%d\n", - socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name, - ci->pub.chiprev); - - if (socitype == SOCI_SB) { - if (ci->pub.chip != BRCM_CC_4329_CHIP_ID) { - brcmf_err("SB chip is not supported\n"); - return -ENODEV; - } - ci->iscoreup = brcmf_chip_sb_iscoreup; - ci->coredisable = brcmf_chip_sb_coredisable; - ci->resetcore = brcmf_chip_sb_resetcore; - - core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON, - SI_ENUM_BASE, 0); - brcmf_chip_sb_corerev(ci, core); - core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, - BCM4329_CORE_BUS_BASE, 0); - brcmf_chip_sb_corerev(ci, core); - core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, - BCM4329_CORE_SOCRAM_BASE, 0); - brcmf_chip_sb_corerev(ci, core); - core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, - BCM4329_CORE_ARM_BASE, 0); - brcmf_chip_sb_corerev(ci, core); - - core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0); - brcmf_chip_sb_corerev(ci, core); - } else if (socitype == SOCI_AI) { - ci->iscoreup = brcmf_chip_ai_iscoreup; - ci->coredisable = brcmf_chip_ai_coredisable; - ci->resetcore = brcmf_chip_ai_resetcore; - - brcmf_chip_dmp_erom_scan(ci); - } else { - brcmf_err("chip backplane type %u is not supported\n", - socitype); - return -ENODEV; - } - - ret = brcmf_chip_cores_check(ci); - if (ret) - return ret; - - /* assure chip is passive for core access */ - brcmf_chip_set_passive(&ci->pub); - - /* Call bus specific reset function now. Cores have been determined - * but further access may require a chip specific reset at this point. - */ - if (ci->ops->reset) { - ci->ops->reset(ci->ctx, &ci->pub); - brcmf_chip_set_passive(&ci->pub); - } - - return brcmf_chip_get_raminfo(ci); -} - -static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) -{ - struct brcmf_core *core; - struct brcmf_core_priv *cpu; - u32 val; - - - core = brcmf_chip_get_core(&chip->pub, id); - if (!core) - return; - - switch (id) { - case BCMA_CORE_ARM_CM3: - brcmf_chip_coredisable(core, 0, 0); - break; - case BCMA_CORE_ARM_CR4: - case BCMA_CORE_ARM_CA7: - cpu = container_of(core, struct brcmf_core_priv, pub); - - /* clear all IOCTL bits except HALT bit */ - val = chip->ops->read32(chip->ctx, cpu->wrapbase + BCMA_IOCTL); - val &= ARMCR4_BCMA_IOCTL_CPUHALT; - brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT, - ARMCR4_BCMA_IOCTL_CPUHALT); - break; - default: - brcmf_err("unknown id: %u\n", id); - break; - } -} - -static int brcmf_chip_setup(struct brcmf_chip_priv *chip) -{ - struct brcmf_chip *pub; - struct brcmf_core_priv *cc; - u32 base; - u32 val; - int ret = 0; - - pub = &chip->pub; - cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list); - base = cc->pub.base; - - /* get chipcommon capabilites */ - pub->cc_caps = chip->ops->read32(chip->ctx, - CORE_CC_REG(base, capabilities)); - - /* get pmu caps & rev */ - if (pub->cc_caps & CC_CAP_PMU) { - val = chip->ops->read32(chip->ctx, - CORE_CC_REG(base, pmucapabilities)); - pub->pmurev = val & PCAP_REV_MASK; - pub->pmucaps = val; - } - - brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, pmucaps=0x%x\n", - cc->pub.rev, pub->pmurev, pub->pmucaps); - - /* execute bus core specific setup */ - if (chip->ops->setup) - ret = chip->ops->setup(chip->ctx, pub); - - return ret; -} - -struct brcmf_chip *brcmf_chip_attach(void *ctx, - const struct brcmf_buscore_ops *ops) -{ - struct brcmf_chip_priv *chip; - int err = 0; - - if (WARN_ON(!ops->read32)) - err = -EINVAL; - if (WARN_ON(!ops->write32)) - err = -EINVAL; - if (WARN_ON(!ops->prepare)) - err = -EINVAL; - if (WARN_ON(!ops->activate)) - err = -EINVAL; - if (err < 0) - return ERR_PTR(-EINVAL); - - chip = kzalloc(sizeof(*chip), GFP_KERNEL); - if (!chip) - return ERR_PTR(-ENOMEM); - - INIT_LIST_HEAD(&chip->cores); - chip->num_cores = 0; - chip->ops = ops; - chip->ctx = ctx; - - err = ops->prepare(ctx); - if (err < 0) - goto fail; - - err = brcmf_chip_recognition(chip); - if (err < 0) - goto fail; - - err = brcmf_chip_setup(chip); - if (err < 0) - goto fail; - - return &chip->pub; - -fail: - brcmf_chip_detach(&chip->pub); - return ERR_PTR(err); -} - -void brcmf_chip_detach(struct brcmf_chip *pub) -{ - struct brcmf_chip_priv *chip; - struct brcmf_core_priv *core; - struct brcmf_core_priv *tmp; - - chip = container_of(pub, struct brcmf_chip_priv, pub); - list_for_each_entry_safe(core, tmp, &chip->cores, list) { - list_del(&core->list); - kfree(core); - } - kfree(chip); -} - -struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid) -{ - struct brcmf_chip_priv *chip; - struct brcmf_core_priv *core; - - chip = container_of(pub, struct brcmf_chip_priv, pub); - list_for_each_entry(core, &chip->cores, list) - if (core->pub.id == coreid) - return &core->pub; - - return NULL; -} - -struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *pub) -{ - struct brcmf_chip_priv *chip; - struct brcmf_core_priv *cc; - - chip = container_of(pub, struct brcmf_chip_priv, pub); - cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list); - if (WARN_ON(!cc || cc->pub.id != BCMA_CORE_CHIPCOMMON)) - return brcmf_chip_get_core(pub, BCMA_CORE_CHIPCOMMON); - return &cc->pub; -} - -bool brcmf_chip_iscoreup(struct brcmf_core *pub) -{ - struct brcmf_core_priv *core; - - core = container_of(pub, struct brcmf_core_priv, pub); - return core->chip->iscoreup(core); -} - -void brcmf_chip_coredisable(struct brcmf_core *pub, u32 prereset, u32 reset) -{ - struct brcmf_core_priv *core; - - core = container_of(pub, struct brcmf_core_priv, pub); - core->chip->coredisable(core, prereset, reset); -} - -void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset, - u32 postreset) -{ - struct brcmf_core_priv *core; - - core = container_of(pub, struct brcmf_core_priv, pub); - core->chip->resetcore(core, prereset, reset, postreset); -} - -static void -brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) -{ - struct brcmf_core *core; - struct brcmf_core_priv *sr; - - brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); - core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); - brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | - D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN); - core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); - brcmf_chip_resetcore(core, 0, 0, 0); - - /* disable bank #3 remap for this device */ - if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) { - sr = container_of(core, struct brcmf_core_priv, pub); - brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3); - brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0); - } -} - -static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip) -{ - struct brcmf_core *core; - - core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); - if (!brcmf_chip_iscoreup(core)) { - brcmf_err("SOCRAM core is down after reset?\n"); - return false; - } - - chip->ops->activate(chip->ctx, &chip->pub, 0); - - core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3); - brcmf_chip_resetcore(core, 0, 0, 0); - - return true; -} - -static inline void -brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip) -{ - struct brcmf_core *core; - - brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); - - core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); - brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | - D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN); -} - -static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec) -{ - struct brcmf_core *core; - - chip->ops->activate(chip->ctx, &chip->pub, rstvec); - - /* restore ARM */ - core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4); - brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0); - - return true; -} - -static inline void -brcmf_chip_ca7_set_passive(struct brcmf_chip_priv *chip) -{ - struct brcmf_core *core; - - brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CA7); - - core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); - brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | - D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN); -} - -static bool brcmf_chip_ca7_set_active(struct brcmf_chip_priv *chip, u32 rstvec) -{ - struct brcmf_core *core; - - chip->ops->activate(chip->ctx, &chip->pub, rstvec); - - /* restore ARM */ - core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CA7); - brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0); - - return true; -} - -void brcmf_chip_set_passive(struct brcmf_chip *pub) -{ - struct brcmf_chip_priv *chip; - struct brcmf_core *arm; - - brcmf_dbg(TRACE, "Enter\n"); - - chip = container_of(pub, struct brcmf_chip_priv, pub); - arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); - if (arm) { - brcmf_chip_cr4_set_passive(chip); - return; - } - arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7); - if (arm) { - brcmf_chip_ca7_set_passive(chip); - return; - } - arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); - if (arm) { - brcmf_chip_cm3_set_passive(chip); - return; - } -} - -bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec) -{ - struct brcmf_chip_priv *chip; - struct brcmf_core *arm; - - brcmf_dbg(TRACE, "Enter\n"); - - chip = container_of(pub, struct brcmf_chip_priv, pub); - arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); - if (arm) - return brcmf_chip_cr4_set_active(chip, rstvec); - arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7); - if (arm) - return brcmf_chip_ca7_set_active(chip, rstvec); - arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); - if (arm) - return brcmf_chip_cm3_set_active(chip); - - return false; -} - -bool brcmf_chip_sr_capable(struct brcmf_chip *pub) -{ - u32 base, addr, reg, pmu_cc3_mask = ~0; - struct brcmf_chip_priv *chip; - - brcmf_dbg(TRACE, "Enter\n"); - - /* old chips with PMU version less than 17 don't support save restore */ - if (pub->pmurev < 17) - return false; - - base = brcmf_chip_get_chipcommon(pub)->base; - chip = container_of(pub, struct brcmf_chip_priv, pub); - - switch (pub->chip) { - case BRCM_CC_4354_CHIP_ID: - /* explicitly check SR engine enable bit */ - pmu_cc3_mask = BIT(2); - /* fall-through */ - case BRCM_CC_43241_CHIP_ID: - case BRCM_CC_4335_CHIP_ID: - case BRCM_CC_4339_CHIP_ID: - /* read PMU chipcontrol register 3 */ - addr = CORE_CC_REG(base, chipcontrol_addr); - chip->ops->write32(chip->ctx, addr, 3); - addr = CORE_CC_REG(base, chipcontrol_data); - reg = chip->ops->read32(chip->ctx, addr); - return (reg & pmu_cc3_mask) != 0; - case BRCM_CC_43430_CHIP_ID: - addr = CORE_CC_REG(base, sr_control1); - reg = chip->ops->read32(chip->ctx, addr); - return reg != 0; - default: - addr = CORE_CC_REG(base, pmucapabilities_ext); - reg = chip->ops->read32(chip->ctx, addr); - if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0) - return false; - - addr = CORE_CC_REG(base, retention_ctl); - reg = chip->ops->read32(chip->ctx, addr); - return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK | - PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; - } -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h deleted file mode 100644 index f6b5feea23d2..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2014 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 BRCMF_CHIP_H -#define BRCMF_CHIP_H - -#include - -#define CORE_CC_REG(base, field) \ - (base + offsetof(struct chipcregs, field)) - -/** - * struct brcmf_chip - chip level information. - * - * @chip: chip identifier. - * @chiprev: chip revision. - * @cc_caps: chipcommon core capabilities. - * @pmucaps: PMU capabilities. - * @pmurev: PMU revision. - * @rambase: RAM base address (only applicable for ARM CR4 chips). - * @ramsize: amount of RAM on chip including retention. - * @srsize: amount of retention RAM on chip. - * @name: string representation of the chip identifier. - */ -struct brcmf_chip { - u32 chip; - u32 chiprev; - u32 cc_caps; - u32 pmucaps; - u32 pmurev; - u32 rambase; - u32 ramsize; - u32 srsize; - char name[8]; -}; - -/** - * struct brcmf_core - core related information. - * - * @id: core identifier. - * @rev: core revision. - * @base: base address of core register space. - */ -struct brcmf_core { - u16 id; - u16 rev; - u32 base; -}; - -/** - * struct brcmf_buscore_ops - buscore specific callbacks. - * - * @read32: read 32-bit value over bus. - * @write32: write 32-bit value over bus. - * @prepare: prepare bus for core configuration. - * @setup: bus-specific core setup. - * @active: chip becomes active. - * The callback should use the provided @rstvec when non-zero. - */ -struct brcmf_buscore_ops { - u32 (*read32)(void *ctx, u32 addr); - void (*write32)(void *ctx, u32 addr, u32 value); - int (*prepare)(void *ctx); - int (*reset)(void *ctx, struct brcmf_chip *chip); - int (*setup)(void *ctx, struct brcmf_chip *chip); - void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec); -}; - -struct brcmf_chip *brcmf_chip_attach(void *ctx, - const struct brcmf_buscore_ops *ops); -void brcmf_chip_detach(struct brcmf_chip *chip); -struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid); -struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip); -bool brcmf_chip_iscoreup(struct brcmf_core *core); -void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset); -void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset, - u32 postreset); -void brcmf_chip_set_passive(struct brcmf_chip *ci); -bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec); -bool brcmf_chip_sr_capable(struct brcmf_chip *pub); - -#endif /* BRCMF_AXIDMP_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c deleted file mode 100644 index fe54844c75e0..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/common.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2010 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 -#include -#include "core.h" -#include "bus.h" -#include "debug.h" -#include "fwil.h" -#include "fwil_types.h" -#include "tracepoint.h" -#include "common.h" - -const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - -#define BRCMF_DEFAULT_BCN_TIMEOUT 3 -#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 -#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 - -/* boost value for RSSI_DELTA in preferred join selection */ -#define BRCMF_JOIN_PREF_RSSI_BOOST 8 - -int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) -{ - s8 eventmask[BRCMF_EVENTING_MASK_LEN]; - u8 buf[BRCMF_DCMD_SMLEN]; - struct brcmf_join_pref_params join_pref_params[2]; - struct brcmf_rev_info_le revinfo; - struct brcmf_rev_info *ri; - char *ptr; - s32 err; - - /* retreive mac address */ - err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, - sizeof(ifp->mac_addr)); - if (err < 0) { - brcmf_err("Retreiving cur_etheraddr failed, %d\n", err); - goto done; - } - memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac)); - - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, - &revinfo, sizeof(revinfo)); - ri = &ifp->drvr->revinfo; - if (err < 0) { - brcmf_err("retrieving revision info failed, %d\n", err); - } else { - ri->vendorid = le32_to_cpu(revinfo.vendorid); - ri->deviceid = le32_to_cpu(revinfo.deviceid); - ri->radiorev = le32_to_cpu(revinfo.radiorev); - ri->chiprev = le32_to_cpu(revinfo.chiprev); - ri->corerev = le32_to_cpu(revinfo.corerev); - ri->boardid = le32_to_cpu(revinfo.boardid); - ri->boardvendor = le32_to_cpu(revinfo.boardvendor); - ri->boardrev = le32_to_cpu(revinfo.boardrev); - ri->driverrev = le32_to_cpu(revinfo.driverrev); - ri->ucoderev = le32_to_cpu(revinfo.ucoderev); - ri->bus = le32_to_cpu(revinfo.bus); - ri->chipnum = le32_to_cpu(revinfo.chipnum); - ri->phytype = le32_to_cpu(revinfo.phytype); - ri->phyrev = le32_to_cpu(revinfo.phyrev); - ri->anarev = le32_to_cpu(revinfo.anarev); - ri->chippkg = le32_to_cpu(revinfo.chippkg); - ri->nvramrev = le32_to_cpu(revinfo.nvramrev); - } - ri->result = err; - - /* query for 'ver' to get version info from firmware */ - memset(buf, 0, sizeof(buf)); - strcpy(buf, "ver"); - err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf)); - if (err < 0) { - brcmf_err("Retreiving version information failed, %d\n", - err); - goto done; - } - ptr = (char *)buf; - strsep(&ptr, "\n"); - - /* Print fw version info */ - brcmf_err("Firmware version = %s\n", buf); - - /* locate firmware version number for ethtool */ - ptr = strrchr(buf, ' ') + 1; - strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver)); - - /* set mpc */ - err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); - if (err) { - brcmf_err("failed setting mpc\n"); - goto done; - } - - /* - * Setup timeout if Beacons are lost and roam is off to report - * link down - */ - err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", - BRCMF_DEFAULT_BCN_TIMEOUT); - if (err) { - brcmf_err("bcn_timeout error (%d)\n", err); - goto done; - } - - /* Enable/Disable build-in roaming to allowed ext supplicant to take - * of romaing - */ - err = brcmf_fil_iovar_int_set(ifp, "roam_off", 1); - if (err) { - brcmf_err("roam_off error (%d)\n", err); - goto done; - } - - /* Setup join_pref to select target by RSSI(with boost on 5GHz) */ - join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA; - join_pref_params[0].len = 2; - join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST; - join_pref_params[0].band = WLC_BAND_5G; - join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI; - join_pref_params[1].len = 2; - join_pref_params[1].rssi_gain = 0; - join_pref_params[1].band = 0; - err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params, - sizeof(join_pref_params)); - if (err) - brcmf_err("Set join_pref error (%d)\n", err); - - /* Setup event_msgs, enable E_IF */ - err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask, - BRCMF_EVENTING_MASK_LEN); - if (err) { - brcmf_err("Get event_msgs error (%d)\n", err); - goto done; - } - setbit(eventmask, BRCMF_E_IF); - err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask, - BRCMF_EVENTING_MASK_LEN); - if (err) { - brcmf_err("Set event_msgs error (%d)\n", err); - goto done; - } - - /* Setup default scan channel time */ - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, - BRCMF_DEFAULT_SCAN_CHANNEL_TIME); - if (err) { - brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n", - err); - goto done; - } - - /* Setup default scan unassoc time */ - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, - BRCMF_DEFAULT_SCAN_UNASSOC_TIME); - if (err) { - brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n", - err); - goto done; - } - - /* do bus specific preinit here */ - err = brcmf_bus_preinit(ifp->drvr->bus_if); -done: - return err; -} - -#if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG) -void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) -{ - struct va_format vaf = { - .fmt = fmt, - }; - va_list args; - - va_start(args, fmt); - vaf.va = &args; - if (brcmf_msg_level & level) - pr_debug("%s %pV", func, &vaf); - trace_brcmf_dbg(level, func, &vaf); - va_end(args); -} -#endif diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.h b/drivers/net/wireless/brcm80211/brcmfmac/common.h deleted file mode 100644 index 21c7488b4732..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/common.h +++ /dev/null @@ -1,23 +0,0 @@ -/* Copyright (c) 2014 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_COMMON_H -#define BRCMFMAC_COMMON_H - -extern const u8 ALLFFMAC[ETH_ALEN]; - -/* Sets dongle media info (drv_version, mac address). */ -int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); - -#endif /* BRCMFMAC_COMMON_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c deleted file mode 100644 index 7b0e52195a85..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c +++ /dev/null @@ -1,252 +0,0 @@ -/* Copyright (c) 2014 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 - -#include "core.h" -#include "commonring.h" - -void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, - int (*cr_ring_bell)(void *ctx), - int (*cr_update_rptr)(void *ctx), - int (*cr_update_wptr)(void *ctx), - int (*cr_write_rptr)(void *ctx), - int (*cr_write_wptr)(void *ctx), void *ctx) -{ - commonring->cr_ring_bell = cr_ring_bell; - commonring->cr_update_rptr = cr_update_rptr; - commonring->cr_update_wptr = cr_update_wptr; - commonring->cr_write_rptr = cr_write_rptr; - commonring->cr_write_wptr = cr_write_wptr; - commonring->cr_ctx = ctx; -} - - -void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth, - u16 item_len, void *buf_addr) -{ - commonring->depth = depth; - commonring->item_len = item_len; - commonring->buf_addr = buf_addr; - if (!commonring->inited) { - spin_lock_init(&commonring->lock); - commonring->inited = true; - } - commonring->r_ptr = 0; - if (commonring->cr_write_rptr) - commonring->cr_write_rptr(commonring->cr_ctx); - commonring->w_ptr = 0; - if (commonring->cr_write_wptr) - commonring->cr_write_wptr(commonring->cr_ctx); - commonring->f_ptr = 0; -} - - -void brcmf_commonring_lock(struct brcmf_commonring *commonring) - __acquires(&commonring->lock) -{ - unsigned long flags; - - spin_lock_irqsave(&commonring->lock, flags); - commonring->flags = flags; -} - - -void brcmf_commonring_unlock(struct brcmf_commonring *commonring) - __releases(&commonring->lock) -{ - spin_unlock_irqrestore(&commonring->lock, commonring->flags); -} - - -bool brcmf_commonring_write_available(struct brcmf_commonring *commonring) -{ - u16 available; - bool retry = true; - -again: - if (commonring->r_ptr <= commonring->w_ptr) - available = commonring->depth - commonring->w_ptr + - commonring->r_ptr; - else - available = commonring->r_ptr - commonring->w_ptr; - - if (available > 1) { - if (!commonring->was_full) - return true; - if (available > commonring->depth / 8) { - commonring->was_full = false; - return true; - } - if (retry) { - if (commonring->cr_update_rptr) - commonring->cr_update_rptr(commonring->cr_ctx); - retry = false; - goto again; - } - return false; - } - - if (retry) { - if (commonring->cr_update_rptr) - commonring->cr_update_rptr(commonring->cr_ctx); - retry = false; - goto again; - } - - commonring->was_full = true; - return false; -} - - -void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring) -{ - void *ret_ptr; - u16 available; - bool retry = true; - -again: - if (commonring->r_ptr <= commonring->w_ptr) - available = commonring->depth - commonring->w_ptr + - commonring->r_ptr; - else - available = commonring->r_ptr - commonring->w_ptr; - - if (available > 1) { - ret_ptr = commonring->buf_addr + - (commonring->w_ptr * commonring->item_len); - commonring->w_ptr++; - if (commonring->w_ptr == commonring->depth) - commonring->w_ptr = 0; - return ret_ptr; - } - - if (retry) { - if (commonring->cr_update_rptr) - commonring->cr_update_rptr(commonring->cr_ctx); - retry = false; - goto again; - } - - commonring->was_full = true; - return NULL; -} - - -void * -brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring, - u16 n_items, u16 *alloced) -{ - void *ret_ptr; - u16 available; - bool retry = true; - -again: - if (commonring->r_ptr <= commonring->w_ptr) - available = commonring->depth - commonring->w_ptr + - commonring->r_ptr; - else - available = commonring->r_ptr - commonring->w_ptr; - - if (available > 1) { - ret_ptr = commonring->buf_addr + - (commonring->w_ptr * commonring->item_len); - *alloced = min_t(u16, n_items, available - 1); - if (*alloced + commonring->w_ptr > commonring->depth) - *alloced = commonring->depth - commonring->w_ptr; - commonring->w_ptr += *alloced; - if (commonring->w_ptr == commonring->depth) - commonring->w_ptr = 0; - return ret_ptr; - } - - if (retry) { - if (commonring->cr_update_rptr) - commonring->cr_update_rptr(commonring->cr_ctx); - retry = false; - goto again; - } - - commonring->was_full = true; - return NULL; -} - - -int brcmf_commonring_write_complete(struct brcmf_commonring *commonring) -{ - void *address; - - address = commonring->buf_addr; - address += (commonring->f_ptr * commonring->item_len); - if (commonring->f_ptr > commonring->w_ptr) { - address = commonring->buf_addr; - commonring->f_ptr = 0; - } - - commonring->f_ptr = commonring->w_ptr; - - if (commonring->cr_write_wptr) - commonring->cr_write_wptr(commonring->cr_ctx); - if (commonring->cr_ring_bell) - return commonring->cr_ring_bell(commonring->cr_ctx); - - return -EIO; -} - - -void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring, - u16 n_items) -{ - if (commonring->w_ptr == 0) - commonring->w_ptr = commonring->depth - n_items; - else - commonring->w_ptr -= n_items; -} - - -void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, - u16 *n_items) -{ - if (commonring->cr_update_wptr) - commonring->cr_update_wptr(commonring->cr_ctx); - - *n_items = (commonring->w_ptr >= commonring->r_ptr) ? - (commonring->w_ptr - commonring->r_ptr) : - (commonring->depth - commonring->r_ptr); - - if (*n_items == 0) - return NULL; - - return commonring->buf_addr + - (commonring->r_ptr * commonring->item_len); -} - - -int brcmf_commonring_read_complete(struct brcmf_commonring *commonring, - u16 n_items) -{ - commonring->r_ptr += n_items; - if (commonring->r_ptr == commonring->depth) - commonring->r_ptr = 0; - - if (commonring->cr_write_rptr) - return commonring->cr_write_rptr(commonring->cr_ctx); - - return -EIO; -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h deleted file mode 100644 index b85033611c8d..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright (c) 2014 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_COMMONRING_H -#define BRCMFMAC_COMMONRING_H - - -struct brcmf_commonring { - u16 r_ptr; - u16 w_ptr; - u16 f_ptr; - u16 depth; - u16 item_len; - - void *buf_addr; - - int (*cr_ring_bell)(void *ctx); - int (*cr_update_rptr)(void *ctx); - int (*cr_update_wptr)(void *ctx); - int (*cr_write_rptr)(void *ctx); - int (*cr_write_wptr)(void *ctx); - - void *cr_ctx; - - spinlock_t lock; - unsigned long flags; - bool inited; - bool was_full; - - atomic_t outstanding_tx; -}; - - -void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, - int (*cr_ring_bell)(void *ctx), - int (*cr_update_rptr)(void *ctx), - int (*cr_update_wptr)(void *ctx), - int (*cr_write_rptr)(void *ctx), - int (*cr_write_wptr)(void *ctx), void *ctx); -void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth, - u16 item_len, void *buf_addr); -void brcmf_commonring_lock(struct brcmf_commonring *commonring); -void brcmf_commonring_unlock(struct brcmf_commonring *commonring); -bool brcmf_commonring_write_available(struct brcmf_commonring *commonring); -void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring); -void * -brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring, - u16 n_items, u16 *alloced); -int brcmf_commonring_write_complete(struct brcmf_commonring *commonring); -void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring, - u16 n_items); -void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, - u16 *n_items); -int brcmf_commonring_read_complete(struct brcmf_commonring *commonring, - u16 n_items); - -#define brcmf_commonring_n_items(commonring) (commonring->depth) -#define brcmf_commonring_len_item(commonring) (commonring->item_len) - - -#endif /* BRCMFMAC_COMMONRING_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c deleted file mode 100644 index b5ab98ee1445..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ /dev/null @@ -1,1260 +0,0 @@ -/* - * Copyright (c) 2010 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 -#include -#include -#include - -#include "core.h" -#include "bus.h" -#include "debug.h" -#include "fwil_types.h" -#include "p2p.h" -#include "cfg80211.h" -#include "fwil.h" -#include "fwsignal.h" -#include "feature.h" -#include "proto.h" -#include "pcie.h" -#include "common.h" - -MODULE_AUTHOR("Broadcom Corporation"); -MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); -MODULE_LICENSE("Dual BSD/GPL"); - -#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */ - -/* AMPDU rx reordering definitions */ -#define BRCMF_RXREORDER_FLOWID_OFFSET 0 -#define BRCMF_RXREORDER_MAXIDX_OFFSET 2 -#define BRCMF_RXREORDER_FLAGS_OFFSET 4 -#define BRCMF_RXREORDER_CURIDX_OFFSET 6 -#define BRCMF_RXREORDER_EXPIDX_OFFSET 8 - -#define BRCMF_RXREORDER_DEL_FLOW 0x01 -#define BRCMF_RXREORDER_FLUSH_ALL 0x02 -#define BRCMF_RXREORDER_CURIDX_VALID 0x04 -#define BRCMF_RXREORDER_EXPIDX_VALID 0x08 -#define BRCMF_RXREORDER_NEW_HOLE 0x10 - -#define BRCMF_BSSIDX_INVALID -1 - -/* Error bits */ -int brcmf_msg_level; -module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); -MODULE_PARM_DESC(debug, "level of debug output"); - -/* P2P0 enable */ -static int brcmf_p2p_enable; -module_param_named(p2pon, brcmf_p2p_enable, int, 0); -MODULE_PARM_DESC(p2pon, "enable legacy p2p management functionality"); - -char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) -{ - if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { - brcmf_err("ifidx %d out of range\n", ifidx); - return ""; - } - - if (drvr->iflist[ifidx] == NULL) { - brcmf_err("null i/f %d\n", ifidx); - return ""; - } - - if (drvr->iflist[ifidx]->ndev) - return drvr->iflist[ifidx]->ndev->name; - - return ""; -} - -struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx) -{ - struct brcmf_if *ifp; - s32 bssidx; - - if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { - brcmf_err("ifidx %d out of range\n", ifidx); - return NULL; - } - - ifp = NULL; - bssidx = drvr->if2bss[ifidx]; - if (bssidx >= 0) - ifp = drvr->iflist[bssidx]; - - return ifp; -} - -static void _brcmf_set_multicast_list(struct work_struct *work) -{ - struct brcmf_if *ifp; - struct net_device *ndev; - struct netdev_hw_addr *ha; - u32 cmd_value, cnt; - __le32 cnt_le; - char *buf, *bufp; - u32 buflen; - s32 err; - - ifp = container_of(work, struct brcmf_if, multicast_work); - - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); - - ndev = ifp->ndev; - - /* Determine initial value of allmulti flag */ - cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false; - - /* Send down the multicast list first. */ - cnt = netdev_mc_count(ndev); - buflen = sizeof(cnt) + (cnt * ETH_ALEN); - buf = kmalloc(buflen, GFP_ATOMIC); - if (!buf) - return; - bufp = buf; - - cnt_le = cpu_to_le32(cnt); - memcpy(bufp, &cnt_le, sizeof(cnt_le)); - bufp += sizeof(cnt_le); - - netdev_for_each_mc_addr(ha, ndev) { - if (!cnt) - break; - memcpy(bufp, ha->addr, ETH_ALEN); - bufp += ETH_ALEN; - cnt--; - } - - err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen); - if (err < 0) { - brcmf_err("Setting mcast_list failed, %d\n", err); - cmd_value = cnt ? true : cmd_value; - } - - kfree(buf); - - /* - * Now send the allmulti setting. This is based on the setting in the - * net_device flags, but might be modified above to be turned on if we - * were trying to set some addresses and dongle rejected it... - */ - err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value); - if (err < 0) - brcmf_err("Setting allmulti failed, %d\n", err); - - /*Finally, pick up the PROMISC flag */ - cmd_value = (ndev->flags & IFF_PROMISC) ? true : false; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value); - if (err < 0) - brcmf_err("Setting BRCMF_C_SET_PROMISC failed, %d\n", - err); -} - -static void -_brcmf_set_mac_address(struct work_struct *work) -{ - struct brcmf_if *ifp; - s32 err; - - ifp = container_of(work, struct brcmf_if, setmacaddr_work); - - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); - - err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr, - ETH_ALEN); - if (err < 0) { - brcmf_err("Setting cur_etheraddr failed, %d\n", err); - } else { - brcmf_dbg(TRACE, "MAC address updated to %pM\n", - ifp->mac_addr); - memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN); - } -} - -static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct sockaddr *sa = (struct sockaddr *)addr; - - memcpy(&ifp->mac_addr, sa->sa_data, ETH_ALEN); - schedule_work(&ifp->setmacaddr_work); - return 0; -} - -static void brcmf_netdev_set_multicast_list(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - - schedule_work(&ifp->multicast_work); -} - -static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, - struct net_device *ndev) -{ - int ret; - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_pub *drvr = ifp->drvr; - struct ethhdr *eh = (struct ethhdr *)(skb->data); - - brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); - - /* Can the device send data? */ - if (drvr->bus_if->state != BRCMF_BUS_UP) { - brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state); - netif_stop_queue(ndev); - dev_kfree_skb(skb); - ret = -ENODEV; - goto done; - } - - if (!drvr->iflist[ifp->bssidx]) { - brcmf_err("bad ifidx %d\n", ifp->bssidx); - netif_stop_queue(ndev); - dev_kfree_skb(skb); - ret = -ENODEV; - goto done; - } - - /* Make sure there's enough room for any header */ - if (skb_headroom(skb) < drvr->hdrlen) { - struct sk_buff *skb2; - - brcmf_dbg(INFO, "%s: insufficient headroom\n", - brcmf_ifname(drvr, ifp->bssidx)); - drvr->bus_if->tx_realloc++; - skb2 = skb_realloc_headroom(skb, drvr->hdrlen); - dev_kfree_skb(skb); - skb = skb2; - if (skb == NULL) { - brcmf_err("%s: skb_realloc_headroom failed\n", - brcmf_ifname(drvr, ifp->bssidx)); - ret = -ENOMEM; - goto done; - } - } - - /* validate length for ether packet */ - if (skb->len < sizeof(*eh)) { - ret = -EINVAL; - dev_kfree_skb(skb); - goto done; - } - - if (eh->h_proto == htons(ETH_P_PAE)) - atomic_inc(&ifp->pend_8021x_cnt); - - ret = brcmf_fws_process_skb(ifp, skb); - -done: - if (ret) { - ifp->stats.tx_dropped++; - } else { - ifp->stats.tx_packets++; - ifp->stats.tx_bytes += skb->len; - } - - /* Return ok: we always eat the packet */ - return NETDEV_TX_OK; -} - -void brcmf_txflowblock_if(struct brcmf_if *ifp, - enum brcmf_netif_stop_reason reason, bool state) -{ - unsigned long flags; - - if (!ifp || !ifp->ndev) - return; - - brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n", - ifp->bssidx, ifp->netif_stop, reason, state); - - spin_lock_irqsave(&ifp->netif_stop_lock, flags); - if (state) { - if (!ifp->netif_stop) - netif_stop_queue(ifp->ndev); - ifp->netif_stop |= reason; - } else { - ifp->netif_stop &= ~reason; - if (!ifp->netif_stop) - netif_wake_queue(ifp->ndev); - } - spin_unlock_irqrestore(&ifp->netif_stop_lock, flags); -} - -void brcmf_txflowblock(struct device *dev, bool state) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - brcmf_dbg(TRACE, "Enter\n"); - - brcmf_fws_bus_blocked(drvr, state); -} - -void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) -{ - skb->dev = ifp->ndev; - skb->protocol = eth_type_trans(skb, skb->dev); - - if (skb->pkt_type == PACKET_MULTICAST) - ifp->stats.multicast++; - - /* Process special event packets */ - brcmf_fweh_process_skb(ifp->drvr, skb); - - if (!(ifp->ndev->flags & IFF_UP)) { - brcmu_pkt_buf_free_skb(skb); - return; - } - - ifp->stats.rx_bytes += skb->len; - ifp->stats.rx_packets++; - - brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol)); - if (in_interrupt()) - netif_rx(skb); - else - /* If the receive is not processed inside an ISR, - * the softirqd must be woken explicitly to service - * the NET_RX_SOFTIRQ. This is handled by netif_rx_ni(). - */ - netif_rx_ni(skb); -} - -static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi, - u8 start, u8 end, - struct sk_buff_head *skb_list) -{ - /* initialize return list */ - __skb_queue_head_init(skb_list); - - if (rfi->pend_pkts == 0) { - brcmf_dbg(INFO, "no packets in reorder queue\n"); - return; - } - - do { - if (rfi->pktslots[start]) { - __skb_queue_tail(skb_list, rfi->pktslots[start]); - rfi->pktslots[start] = NULL; - } - start++; - if (start > rfi->max_idx) - start = 0; - } while (start != end); - rfi->pend_pkts -= skb_queue_len(skb_list); -} - -static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data, - struct sk_buff *pkt) -{ - u8 flow_id, max_idx, cur_idx, exp_idx, end_idx; - struct brcmf_ampdu_rx_reorder *rfi; - struct sk_buff_head reorder_list; - struct sk_buff *pnext; - u8 flags; - u32 buf_size; - - flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET]; - flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET]; - - /* validate flags and flow id */ - if (flags == 0xFF) { - brcmf_err("invalid flags...so ignore this packet\n"); - brcmf_netif_rx(ifp, pkt); - return; - } - - rfi = ifp->drvr->reorder_flows[flow_id]; - if (flags & BRCMF_RXREORDER_DEL_FLOW) { - brcmf_dbg(INFO, "flow-%d: delete\n", - flow_id); - - if (rfi == NULL) { - brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n", - flow_id); - brcmf_netif_rx(ifp, pkt); - return; - } - - brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx, - &reorder_list); - /* add the last packet */ - __skb_queue_tail(&reorder_list, pkt); - kfree(rfi); - ifp->drvr->reorder_flows[flow_id] = NULL; - goto netif_rx; - } - /* from here on we need a flow reorder instance */ - if (rfi == NULL) { - buf_size = sizeof(*rfi); - max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET]; - - buf_size += (max_idx + 1) * sizeof(pkt); - - /* allocate space for flow reorder info */ - brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n", - flow_id, max_idx); - rfi = kzalloc(buf_size, GFP_ATOMIC); - if (rfi == NULL) { - brcmf_err("failed to alloc buffer\n"); - brcmf_netif_rx(ifp, pkt); - return; - } - - ifp->drvr->reorder_flows[flow_id] = rfi; - rfi->pktslots = (struct sk_buff **)(rfi+1); - rfi->max_idx = max_idx; - } - if (flags & BRCMF_RXREORDER_NEW_HOLE) { - if (rfi->pend_pkts) { - brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, - rfi->exp_idx, - &reorder_list); - WARN_ON(rfi->pend_pkts); - } else { - __skb_queue_head_init(&reorder_list); - } - rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET]; - rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; - rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET]; - rfi->pktslots[rfi->cur_idx] = pkt; - rfi->pend_pkts++; - brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n", - flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts); - } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) { - cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET]; - exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; - - if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) { - /* still in the current hole */ - /* enqueue the current on the buffer chain */ - if (rfi->pktslots[cur_idx] != NULL) { - brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n"); - brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]); - rfi->pktslots[cur_idx] = NULL; - } - rfi->pktslots[cur_idx] = pkt; - rfi->pend_pkts++; - rfi->cur_idx = cur_idx; - brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n", - flow_id, cur_idx, exp_idx, rfi->pend_pkts); - - /* can return now as there is no reorder - * list to process. - */ - return; - } - if (rfi->exp_idx == cur_idx) { - if (rfi->pktslots[cur_idx] != NULL) { - brcmf_dbg(INFO, "error buffer pending..free it\n"); - brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]); - rfi->pktslots[cur_idx] = NULL; - } - rfi->pktslots[cur_idx] = pkt; - rfi->pend_pkts++; - - /* got the expected one. flush from current to expected - * and update expected - */ - brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n", - flow_id, cur_idx, exp_idx, rfi->pend_pkts); - - rfi->cur_idx = cur_idx; - rfi->exp_idx = exp_idx; - - brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx, - &reorder_list); - brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n", - flow_id, skb_queue_len(&reorder_list), - rfi->pend_pkts); - } else { - u8 end_idx; - - brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n", - flow_id, flags, rfi->cur_idx, rfi->exp_idx, - cur_idx, exp_idx); - if (flags & BRCMF_RXREORDER_FLUSH_ALL) - end_idx = rfi->exp_idx; - else - end_idx = exp_idx; - - /* flush pkts first */ - brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx, - &reorder_list); - - if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) { - __skb_queue_tail(&reorder_list, pkt); - } else { - rfi->pktslots[cur_idx] = pkt; - rfi->pend_pkts++; - } - rfi->exp_idx = exp_idx; - rfi->cur_idx = cur_idx; - } - } else { - /* explicity window move updating the expected index */ - exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; - - brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n", - flow_id, flags, rfi->exp_idx, exp_idx); - if (flags & BRCMF_RXREORDER_FLUSH_ALL) - end_idx = rfi->exp_idx; - else - end_idx = exp_idx; - - brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx, - &reorder_list); - __skb_queue_tail(&reorder_list, pkt); - /* set the new expected idx */ - rfi->exp_idx = exp_idx; - } -netif_rx: - skb_queue_walk_safe(&reorder_list, pkt, pnext) { - __skb_unlink(pkt, &reorder_list); - brcmf_netif_rx(ifp, pkt); - } -} - -void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) -{ - struct brcmf_if *ifp; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - struct brcmf_skb_reorder_data *rd; - int ret; - - brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb); - - /* process and remove protocol-specific header */ - ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp); - - if (ret || !ifp || !ifp->ndev) { - if (ret != -ENODATA && ifp) - ifp->stats.rx_errors++; - brcmu_pkt_buf_free_skb(skb); - return; - } - - rd = (struct brcmf_skb_reorder_data *)skb->cb; - if (rd->reorder) - brcmf_rxreorder_process_info(ifp, rd->reorder, skb); - else - brcmf_netif_rx(ifp, skb); -} - -void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success) -{ - struct ethhdr *eh; - u16 type; - - eh = (struct ethhdr *)(txp->data); - type = ntohs(eh->h_proto); - - if (type == ETH_P_PAE) { - atomic_dec(&ifp->pend_8021x_cnt); - if (waitqueue_active(&ifp->pend_8021x_wait)) - wake_up(&ifp->pend_8021x_wait); - } - - if (!success) - ifp->stats.tx_errors++; - - brcmu_pkt_buf_free_skb(txp); -} - -void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - struct brcmf_if *ifp; - - /* await txstatus signal for firmware if active */ - if (brcmf_fws_fc_active(drvr->fws)) { - if (!success) - brcmf_fws_bustxfail(drvr->fws, txp); - } else { - if (brcmf_proto_hdrpull(drvr, false, txp, &ifp)) - brcmu_pkt_buf_free_skb(txp); - else - brcmf_txfinalize(ifp, txp, success); - } -} - -static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); - - return &ifp->stats; -} - -static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, - struct ethtool_drvinfo *info) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_pub *drvr = ifp->drvr; - char drev[BRCMU_DOTREV_LEN] = "n/a"; - - if (drvr->revinfo.result == 0) - brcmu_dotrev_str(drvr->revinfo.driverrev, drev); - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strlcpy(info->version, drev, sizeof(info->version)); - strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version)); - strlcpy(info->bus_info, dev_name(drvr->bus_if->dev), - sizeof(info->bus_info)); -} - -static const struct ethtool_ops brcmf_ethtool_ops = { - .get_drvinfo = brcmf_ethtool_get_drvinfo, -}; - -static int brcmf_netdev_stop(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); - - brcmf_cfg80211_down(ndev); - - brcmf_net_setcarrier(ifp, false); - - return 0; -} - -static int brcmf_netdev_open(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_pub *drvr = ifp->drvr; - struct brcmf_bus *bus_if = drvr->bus_if; - u32 toe_ol; - - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); - - /* If bus is not ready, can't continue */ - if (bus_if->state != BRCMF_BUS_UP) { - brcmf_err("failed bus is not ready\n"); - return -EAGAIN; - } - - atomic_set(&ifp->pend_8021x_cnt, 0); - - /* Get current TOE mode from dongle */ - if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0 - && (toe_ol & TOE_TX_CSUM_OL) != 0) - ndev->features |= NETIF_F_IP_CSUM; - else - ndev->features &= ~NETIF_F_IP_CSUM; - - if (brcmf_cfg80211_up(ndev)) { - brcmf_err("failed to bring up cfg80211\n"); - return -EIO; - } - - /* Clear, carrier, set when connected or AP mode. */ - netif_carrier_off(ndev); - return 0; -} - -static const struct net_device_ops brcmf_netdev_ops_pri = { - .ndo_open = brcmf_netdev_open, - .ndo_stop = brcmf_netdev_stop, - .ndo_get_stats = brcmf_netdev_get_stats, - .ndo_start_xmit = brcmf_netdev_start_xmit, - .ndo_set_mac_address = brcmf_netdev_set_mac_address, - .ndo_set_rx_mode = brcmf_netdev_set_multicast_list -}; - -int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) -{ - struct brcmf_pub *drvr = ifp->drvr; - struct net_device *ndev; - s32 err; - - brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx, - ifp->mac_addr); - ndev = ifp->ndev; - - /* set appropriate operations */ - ndev->netdev_ops = &brcmf_netdev_ops_pri; - - ndev->hard_header_len += drvr->hdrlen; - ndev->ethtool_ops = &brcmf_ethtool_ops; - - drvr->rxsz = ndev->mtu + ndev->hard_header_len + - drvr->hdrlen; - - /* set the mac address */ - memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN); - - INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address); - INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list); - - if (rtnl_locked) - err = register_netdevice(ndev); - else - err = register_netdev(ndev); - if (err != 0) { - brcmf_err("couldn't register the net device\n"); - goto fail; - } - - brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); - return 0; - -fail: - drvr->iflist[ifp->bssidx] = NULL; - ndev->netdev_ops = NULL; - free_netdev(ndev); - return -EBADE; -} - -static void brcmf_net_detach(struct net_device *ndev) -{ - if (ndev->reg_state == NETREG_REGISTERED) - unregister_netdev(ndev); - else - brcmf_cfg80211_free_netdev(ndev); -} - -void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on) -{ - struct net_device *ndev; - - brcmf_dbg(TRACE, "Enter, idx=%d carrier=%d\n", ifp->bssidx, on); - - ndev = ifp->ndev; - brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_DISCONNECTED, !on); - if (on) { - if (!netif_carrier_ok(ndev)) - netif_carrier_on(ndev); - - } else { - if (netif_carrier_ok(ndev)) - netif_carrier_off(ndev); - } -} - -static int brcmf_net_p2p_open(struct net_device *ndev) -{ - brcmf_dbg(TRACE, "Enter\n"); - - return brcmf_cfg80211_up(ndev); -} - -static int brcmf_net_p2p_stop(struct net_device *ndev) -{ - brcmf_dbg(TRACE, "Enter\n"); - - return brcmf_cfg80211_down(ndev); -} - -static netdev_tx_t brcmf_net_p2p_start_xmit(struct sk_buff *skb, - struct net_device *ndev) -{ - if (skb) - dev_kfree_skb_any(skb); - - return NETDEV_TX_OK; -} - -static const struct net_device_ops brcmf_netdev_ops_p2p = { - .ndo_open = brcmf_net_p2p_open, - .ndo_stop = brcmf_net_p2p_stop, - .ndo_start_xmit = brcmf_net_p2p_start_xmit -}; - -static int brcmf_net_p2p_attach(struct brcmf_if *ifp) -{ - struct net_device *ndev; - - brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx, - ifp->mac_addr); - ndev = ifp->ndev; - - ndev->netdev_ops = &brcmf_netdev_ops_p2p; - - /* set the mac address */ - memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN); - - if (register_netdev(ndev) != 0) { - brcmf_err("couldn't register the p2p net device\n"); - goto fail; - } - - brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); - - return 0; - -fail: - ifp->drvr->iflist[ifp->bssidx] = NULL; - ndev->netdev_ops = NULL; - free_netdev(ndev); - return -EBADE; -} - -struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, - bool is_p2pdev, char *name, u8 *mac_addr) -{ - struct brcmf_if *ifp; - struct net_device *ndev; - - brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifidx); - - ifp = drvr->iflist[bssidx]; - /* - * Delete the existing interface before overwriting it - * in case we missed the BRCMF_E_IF_DEL event. - */ - if (ifp) { - brcmf_err("ERROR: netdev:%s already exists\n", - ifp->ndev->name); - if (ifidx) { - netif_stop_queue(ifp->ndev); - brcmf_net_detach(ifp->ndev); - drvr->iflist[bssidx] = NULL; - } else { - brcmf_err("ignore IF event\n"); - return ERR_PTR(-EINVAL); - } - } - - if (!brcmf_p2p_enable && is_p2pdev) { - /* this is P2P_DEVICE interface */ - brcmf_dbg(INFO, "allocate non-netdev interface\n"); - ifp = kzalloc(sizeof(*ifp), GFP_KERNEL); - if (!ifp) - return ERR_PTR(-ENOMEM); - } else { - brcmf_dbg(INFO, "allocate netdev interface\n"); - /* Allocate netdev, including space for private structure */ - ndev = alloc_netdev(sizeof(*ifp), is_p2pdev ? "p2p%d" : name, - NET_NAME_UNKNOWN, ether_setup); - if (!ndev) - return ERR_PTR(-ENOMEM); - - ndev->destructor = brcmf_cfg80211_free_netdev; - ifp = netdev_priv(ndev); - ifp->ndev = ndev; - /* store mapping ifidx to bssidx */ - if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID) - drvr->if2bss[ifidx] = bssidx; - } - - ifp->drvr = drvr; - drvr->iflist[bssidx] = ifp; - ifp->ifidx = ifidx; - ifp->bssidx = bssidx; - - init_waitqueue_head(&ifp->pend_8021x_wait); - spin_lock_init(&ifp->netif_stop_lock); - - if (mac_addr != NULL) - memcpy(ifp->mac_addr, mac_addr, ETH_ALEN); - - brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n", - current->pid, name, ifp->mac_addr); - - return ifp; -} - -static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) -{ - struct brcmf_if *ifp; - - ifp = drvr->iflist[bssidx]; - drvr->iflist[bssidx] = NULL; - if (!ifp) { - brcmf_err("Null interface, idx=%d\n", bssidx); - return; - } - brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx); - if (drvr->if2bss[ifp->ifidx] == bssidx) - drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID; - if (ifp->ndev) { - if (bssidx == 0) { - if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { - rtnl_lock(); - brcmf_netdev_stop(ifp->ndev); - rtnl_unlock(); - } - } else { - netif_stop_queue(ifp->ndev); - } - - if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { - cancel_work_sync(&ifp->setmacaddr_work); - cancel_work_sync(&ifp->multicast_work); - } - brcmf_net_detach(ifp->ndev); - } else { - /* Only p2p device interfaces which get dynamically created - * end up here. In this case the p2p module should be informed - * about the removal of the interface within the firmware. If - * not then p2p commands towards the firmware will cause some - * serious troublesome side effects. The p2p module will clean - * up the ifp if needed. - */ - brcmf_p2p_ifp_removed(ifp); - kfree(ifp); - } -} - -void brcmf_remove_interface(struct brcmf_if *ifp) -{ - if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp)) - return; - brcmf_dbg(TRACE, "Enter, bssidx=%d, ifidx=%d\n", ifp->bssidx, - ifp->ifidx); - brcmf_fws_del_interface(ifp); - brcmf_del_if(ifp->drvr, ifp->bssidx); -} - -int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr) -{ - int ifidx; - int bsscfgidx; - bool available; - int highest; - - available = false; - bsscfgidx = 2; - highest = 2; - for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) { - if (drvr->iflist[ifidx]) { - if (drvr->iflist[ifidx]->bssidx == bsscfgidx) - bsscfgidx = highest + 1; - else if (drvr->iflist[ifidx]->bssidx > highest) - highest = drvr->iflist[ifidx]->bssidx; - } else { - available = true; - } - } - - return available ? bsscfgidx : -ENOMEM; -} - -int brcmf_attach(struct device *dev) -{ - struct brcmf_pub *drvr = NULL; - int ret = 0; - int i; - - brcmf_dbg(TRACE, "Enter\n"); - - /* Allocate primary brcmf_info */ - drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC); - if (!drvr) - return -ENOMEM; - - for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++) - drvr->if2bss[i] = BRCMF_BSSIDX_INVALID; - - mutex_init(&drvr->proto_block); - - /* Link to bus module */ - drvr->hdrlen = 0; - drvr->bus_if = dev_get_drvdata(dev); - drvr->bus_if->drvr = drvr; - - /* attach debug facilities */ - brcmf_debug_attach(drvr); - - /* Attach and link in the protocol */ - ret = brcmf_proto_attach(drvr); - if (ret != 0) { - brcmf_err("brcmf_prot_attach failed\n"); - goto fail; - } - - /* attach firmware event handler */ - brcmf_fweh_attach(drvr); - - return ret; - -fail: - brcmf_detach(dev); - - return ret; -} - -static int brcmf_revinfo_read(struct seq_file *s, void *data) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(s->private); - struct brcmf_rev_info *ri = &bus_if->drvr->revinfo; - char drev[BRCMU_DOTREV_LEN]; - char brev[BRCMU_BOARDREV_LEN]; - - seq_printf(s, "vendorid: 0x%04x\n", ri->vendorid); - seq_printf(s, "deviceid: 0x%04x\n", ri->deviceid); - seq_printf(s, "radiorev: %s\n", brcmu_dotrev_str(ri->radiorev, drev)); - seq_printf(s, "chipnum: %u (%x)\n", ri->chipnum, ri->chipnum); - seq_printf(s, "chiprev: %u\n", ri->chiprev); - seq_printf(s, "chippkg: %u\n", ri->chippkg); - seq_printf(s, "corerev: %u\n", ri->corerev); - seq_printf(s, "boardid: 0x%04x\n", ri->boardid); - seq_printf(s, "boardvendor: 0x%04x\n", ri->boardvendor); - seq_printf(s, "boardrev: %s\n", brcmu_boardrev_str(ri->boardrev, brev)); - seq_printf(s, "driverrev: %s\n", brcmu_dotrev_str(ri->driverrev, drev)); - seq_printf(s, "ucoderev: %u\n", ri->ucoderev); - seq_printf(s, "bus: %u\n", ri->bus); - seq_printf(s, "phytype: %u\n", ri->phytype); - seq_printf(s, "phyrev: %u\n", ri->phyrev); - seq_printf(s, "anarev: %u\n", ri->anarev); - seq_printf(s, "nvramrev: %08x\n", ri->nvramrev); - - return 0; -} - -int brcmf_bus_start(struct device *dev) -{ - int ret = -1; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - struct brcmf_if *ifp; - struct brcmf_if *p2p_ifp; - - brcmf_dbg(TRACE, "\n"); - - /* add primary networking interface */ - ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL); - if (IS_ERR(ifp)) - return PTR_ERR(ifp); - - p2p_ifp = NULL; - - /* signal bus ready */ - brcmf_bus_change_state(bus_if, BRCMF_BUS_UP); - - /* Bus is ready, do any initialization */ - ret = brcmf_c_preinit_dcmds(ifp); - if (ret < 0) - goto fail; - - brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read); - - /* assure we have chipid before feature attach */ - if (!bus_if->chip) { - bus_if->chip = drvr->revinfo.chipnum; - bus_if->chiprev = drvr->revinfo.chiprev; - brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n", - bus_if->chip, bus_if->chip, bus_if->chiprev); - } - brcmf_feat_attach(drvr); - - ret = brcmf_fws_init(drvr); - if (ret < 0) - goto fail; - - brcmf_fws_add_interface(ifp); - - drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev, - brcmf_p2p_enable); - if (drvr->config == NULL) { - ret = -ENOMEM; - goto fail; - } - - ret = brcmf_net_attach(ifp, false); - - if ((!ret) && (brcmf_p2p_enable)) { - p2p_ifp = drvr->iflist[1]; - if (p2p_ifp) - ret = brcmf_net_p2p_attach(p2p_ifp); - } -fail: - if (ret < 0) { - brcmf_err("failed: %d\n", ret); - if (drvr->config) { - brcmf_cfg80211_detach(drvr->config); - drvr->config = NULL; - } - if (drvr->fws) { - brcmf_fws_del_interface(ifp); - brcmf_fws_deinit(drvr); - } - if (ifp) - brcmf_net_detach(ifp->ndev); - if (p2p_ifp) - brcmf_net_detach(p2p_ifp->ndev); - return ret; - } - return 0; -} - -void brcmf_bus_add_txhdrlen(struct device *dev, uint len) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - if (drvr) { - drvr->hdrlen += len; - } -} - -static void brcmf_bus_detach(struct brcmf_pub *drvr) -{ - brcmf_dbg(TRACE, "Enter\n"); - - if (drvr) { - /* Stop the bus module */ - brcmf_bus_stop(drvr->bus_if); - } -} - -void brcmf_dev_reset(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - if (drvr == NULL) - return; - - if (drvr->iflist[0]) - brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1); -} - -void brcmf_detach(struct device *dev) -{ - s32 i; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - brcmf_dbg(TRACE, "Enter\n"); - - if (drvr == NULL) - return; - - /* stop firmware event handling */ - brcmf_fweh_detach(drvr); - if (drvr->config) - brcmf_p2p_detach(&drvr->config->p2p); - - brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN); - - /* make sure primary interface removed last */ - for (i = BRCMF_MAX_IFS-1; i > -1; i--) - brcmf_remove_interface(drvr->iflist[i]); - - brcmf_cfg80211_detach(drvr->config); - - brcmf_fws_deinit(drvr); - - brcmf_bus_detach(drvr); - - brcmf_proto_detach(drvr); - - brcmf_debug_detach(drvr); - bus_if->drvr = NULL; - kfree(drvr); -} - -s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_if *ifp = bus_if->drvr->iflist[0]; - - return brcmf_fil_iovar_data_set(ifp, name, data, len); -} - -static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp) -{ - return atomic_read(&ifp->pend_8021x_cnt); -} - -int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp) -{ - int err; - - err = wait_event_timeout(ifp->pend_8021x_wait, - !brcmf_get_pend_8021x_cnt(ifp), - msecs_to_jiffies(MAX_WAIT_FOR_8021X_TX)); - - WARN_ON(!err); - - return !err; -} - -void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state) -{ - struct brcmf_pub *drvr = bus->drvr; - struct net_device *ndev; - int ifidx; - - brcmf_dbg(TRACE, "%d -> %d\n", bus->state, state); - bus->state = state; - - if (state == BRCMF_BUS_UP) { - for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) { - if ((drvr->iflist[ifidx]) && - (drvr->iflist[ifidx]->ndev)) { - ndev = drvr->iflist[ifidx]->ndev; - if (netif_queue_stopped(ndev)) - netif_wake_queue(ndev); - } - } - } -} - -static void brcmf_driver_register(struct work_struct *work) -{ -#ifdef CONFIG_BRCMFMAC_SDIO - brcmf_sdio_register(); -#endif -#ifdef CONFIG_BRCMFMAC_USB - brcmf_usb_register(); -#endif -#ifdef CONFIG_BRCMFMAC_PCIE - brcmf_pcie_register(); -#endif -} -static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register); - -static int __init brcmfmac_module_init(void) -{ - brcmf_debugfs_init(); -#ifdef CONFIG_BRCMFMAC_SDIO - brcmf_sdio_init(); -#endif - if (!schedule_work(&brcmf_driver_work)) - return -EBUSY; - - return 0; -} - -static void __exit brcmfmac_module_exit(void) -{ - cancel_work_sync(&brcmf_driver_work); - -#ifdef CONFIG_BRCMFMAC_SDIO - brcmf_sdio_exit(); -#endif -#ifdef CONFIG_BRCMFMAC_USB - brcmf_usb_exit(); -#endif -#ifdef CONFIG_BRCMFMAC_PCIE - brcmf_pcie_exit(); -#endif - brcmf_debugfs_exit(); -} - -module_init(brcmfmac_module_init); -module_exit(brcmfmac_module_exit); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h deleted file mode 100644 index 2f9101b2ad34..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (c) 2010 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. - */ - -/**************** - * Common types * - */ - -#ifndef BRCMFMAC_CORE_H -#define BRCMFMAC_CORE_H - -#include -#include "fweh.h" - -#define TOE_TX_CSUM_OL 0x00000001 -#define TOE_RX_CSUM_OL 0x00000002 - -/* For supporting multiple interfaces */ -#define BRCMF_MAX_IFS 16 - -/* Small, medium and maximum buffer size for dcmd - */ -#define BRCMF_DCMD_SMLEN 256 -#define BRCMF_DCMD_MEDLEN 1536 -#define BRCMF_DCMD_MAXLEN 8192 - -/* IOCTL from host to device are limited in lenght. A device can only handle - * ethernet frame size. This limitation is to be applied by protocol layer. - */ -#define BRCMF_TX_IOCTL_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN) - -#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS 256 - -/* Length of firmware version string stored for - * ethtool driver info which uses 32 bytes as well. - */ -#define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32 - -/** - * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info - * - * @pktslots: dynamic allocated array for ordering AMPDU packets. - * @flow_id: AMPDU flow identifier. - * @cur_idx: last AMPDU index from firmware. - * @exp_idx: expected next AMPDU index. - * @max_idx: maximum amount of packets per AMPDU. - * @pend_pkts: number of packets currently in @pktslots. - */ -struct brcmf_ampdu_rx_reorder { - struct sk_buff **pktslots; - u8 flow_id; - u8 cur_idx; - u8 exp_idx; - u8 max_idx; - u8 pend_pkts; -}; - -/* Forward decls for struct brcmf_pub (see below) */ -struct brcmf_proto; /* device communication protocol info */ -struct brcmf_cfg80211_dev; /* cfg80211 device info */ -struct brcmf_fws_info; /* firmware signalling info */ - -/* - * struct brcmf_rev_info - * - * The result field stores the error code of the - * revision info request from firmware. For the - * other fields see struct brcmf_rev_info_le in - * fwil_types.h - */ -struct brcmf_rev_info { - int result; - u32 vendorid; - u32 deviceid; - u32 radiorev; - u32 chiprev; - u32 corerev; - u32 boardid; - u32 boardvendor; - u32 boardrev; - u32 driverrev; - u32 ucoderev; - u32 bus; - u32 chipnum; - u32 phytype; - u32 phyrev; - u32 anarev; - u32 chippkg; - u32 nvramrev; -}; - -/* Common structure for module and instance linkage */ -struct brcmf_pub { - /* Linkage ponters */ - struct brcmf_bus *bus_if; - struct brcmf_proto *proto; - struct brcmf_cfg80211_info *config; - - /* Internal brcmf items */ - uint hdrlen; /* Total BRCMF header length (proto + bus) */ - uint rxsz; /* Rx buffer size bus module should use */ - - /* Dongle media info */ - char fwver[BRCMF_DRIVER_FIRMWARE_VERSION_LEN]; - u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */ - - /* Multicast data packets sent to dongle */ - unsigned long tx_multicast; - - struct mac_address addresses[BRCMF_MAX_IFS]; - - struct brcmf_if *iflist[BRCMF_MAX_IFS]; - s32 if2bss[BRCMF_MAX_IFS]; - - struct mutex proto_block; - unsigned char proto_buf[BRCMF_DCMD_MAXLEN]; - - struct brcmf_fweh_info fweh; - - struct brcmf_fws_info *fws; - - struct brcmf_ampdu_rx_reorder - *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS]; - - u32 feat_flags; - u32 chip_quirks; - - struct brcmf_rev_info revinfo; -#ifdef DEBUG - struct dentry *dbgfs_dir; -#endif -}; - -/* forward declarations */ -struct brcmf_cfg80211_vif; -struct brcmf_fws_mac_descriptor; - -/** - * enum brcmf_netif_stop_reason - reason for stopping netif queue. - * - * @BRCMF_NETIF_STOP_REASON_FWS_FC: - * netif stopped due to firmware signalling flow control. - * @BRCMF_NETIF_STOP_REASON_FLOW: - * netif stopped due to flowring full. - * @BRCMF_NETIF_STOP_REASON_DISCONNECTED: - * netif stopped due to not being connected (STA mode). - */ -enum brcmf_netif_stop_reason { - BRCMF_NETIF_STOP_REASON_FWS_FC = BIT(0), - BRCMF_NETIF_STOP_REASON_FLOW = BIT(1), - BRCMF_NETIF_STOP_REASON_DISCONNECTED = BIT(2) -}; - -/** - * struct brcmf_if - interface control information. - * - * @drvr: points to device related information. - * @vif: points to cfg80211 specific interface information. - * @ndev: associated network device. - * @stats: interface specific network statistics. - * @setmacaddr_work: worker object for setting mac address. - * @multicast_work: worker object for multicast provisioning. - * @fws_desc: interface specific firmware-signalling descriptor. - * @ifidx: interface index in device firmware. - * @bssidx: index of bss associated with this interface. - * @mac_addr: assigned mac address. - * @netif_stop: bitmap indicates reason why netif queues are stopped. - * @netif_stop_lock: spinlock for update netif_stop from multiple sources. - * @pend_8021x_cnt: tracks outstanding number of 802.1x frames. - * @pend_8021x_wait: used for signalling change in count. - */ -struct brcmf_if { - struct brcmf_pub *drvr; - struct brcmf_cfg80211_vif *vif; - struct net_device *ndev; - struct net_device_stats stats; - struct work_struct setmacaddr_work; - struct work_struct multicast_work; - struct brcmf_fws_mac_descriptor *fws_desc; - int ifidx; - s32 bssidx; - u8 mac_addr[ETH_ALEN]; - u8 netif_stop; - spinlock_t netif_stop_lock; - atomic_t pend_8021x_cnt; - wait_queue_head_t pend_8021x_wait; -}; - -struct brcmf_skb_reorder_data { - u8 *reorder; -}; - -int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); - -/* Return pointer to interface name */ -char *brcmf_ifname(struct brcmf_pub *drvr, int idx); -struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx); -int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); -struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, - bool is_p2pdev, char *name, u8 *mac_addr); -void brcmf_remove_interface(struct brcmf_if *ifp); -int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr); -void brcmf_txflowblock_if(struct brcmf_if *ifp, - enum brcmf_netif_stop_reason reason, bool state); -void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success); -void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb); -void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on); - -#endif /* BRCMFMAC_CORE_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/brcm80211/brcmfmac/debug.c deleted file mode 100644 index 1299dccc78b4..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2012 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 - -#include -#include -#include "core.h" -#include "bus.h" -#include "fweh.h" -#include "debug.h" - -static struct dentry *root_folder; - -static int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, - size_t len) -{ - void *dump; - size_t ramsize; - - ramsize = brcmf_bus_get_ramsize(bus); - if (ramsize) { - dump = vzalloc(len + ramsize); - if (!dump) - return -ENOMEM; - memcpy(dump, data, len); - brcmf_bus_get_memdump(bus, dump + len, ramsize); - dev_coredumpv(bus->dev, dump, len + ramsize, GFP_KERNEL); - } - return 0; -} - -static int brcmf_debug_psm_watchdog_notify(struct brcmf_if *ifp, - const struct brcmf_event_msg *evtmsg, - void *data) -{ - brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); - - return brcmf_debug_create_memdump(ifp->drvr->bus_if, data, - evtmsg->datalen); -} - -void brcmf_debugfs_init(void) -{ - root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL); - if (IS_ERR(root_folder)) - root_folder = NULL; -} - -void brcmf_debugfs_exit(void) -{ - if (!root_folder) - return; - - debugfs_remove_recursive(root_folder); - root_folder = NULL; -} - -int brcmf_debug_attach(struct brcmf_pub *drvr) -{ - struct device *dev = drvr->bus_if->dev; - - if (!root_folder) - return -ENODEV; - - drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder); - if (IS_ERR(drvr->dbgfs_dir)) - return PTR_ERR(drvr->dbgfs_dir); - - - return brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG, - brcmf_debug_psm_watchdog_notify); -} - -void brcmf_debug_detach(struct brcmf_pub *drvr) -{ - brcmf_fweh_unregister(drvr, BRCMF_E_PSM_WATCHDOG); - - if (!IS_ERR_OR_NULL(drvr->dbgfs_dir)) - debugfs_remove_recursive(drvr->dbgfs_dir); -} - -struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) -{ - return drvr->dbgfs_dir; -} - -int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, - int (*read_fn)(struct seq_file *seq, void *data)) -{ - struct dentry *e; - - e = debugfs_create_devm_seqfile(drvr->bus_if->dev, fn, - drvr->dbgfs_dir, read_fn); - return PTR_ERR_OR_ZERO(e); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/brcm80211/brcmfmac/debug.h deleted file mode 100644 index d0d9676f7f9d..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/debug.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2010 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_DEBUG_H -#define BRCMFMAC_DEBUG_H - -/* message levels */ -#define BRCMF_TRACE_VAL 0x00000002 -#define BRCMF_INFO_VAL 0x00000004 -#define BRCMF_DATA_VAL 0x00000008 -#define BRCMF_CTL_VAL 0x00000010 -#define BRCMF_TIMER_VAL 0x00000020 -#define BRCMF_HDRS_VAL 0x00000040 -#define BRCMF_BYTES_VAL 0x00000080 -#define BRCMF_INTR_VAL 0x00000100 -#define BRCMF_GLOM_VAL 0x00000200 -#define BRCMF_EVENT_VAL 0x00000400 -#define BRCMF_BTA_VAL 0x00000800 -#define BRCMF_FIL_VAL 0x00001000 -#define BRCMF_USB_VAL 0x00002000 -#define BRCMF_SCAN_VAL 0x00004000 -#define BRCMF_CONN_VAL 0x00008000 -#define BRCMF_BCDC_VAL 0x00010000 -#define BRCMF_SDIO_VAL 0x00020000 -#define BRCMF_MSGBUF_VAL 0x00040000 -#define BRCMF_PCIE_VAL 0x00080000 -#define BRCMF_FWCON_VAL 0x00100000 - -/* set default print format */ -#undef pr_fmt -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -/* Macro for error messages. net_ratelimit() is used when driver - * debugging is not selected. When debugging the driver error - * messages are as important as other tracing or even more so. - */ -#ifndef CONFIG_BRCM_TRACING -#ifdef CONFIG_BRCMDBG -#define brcmf_err(fmt, ...) pr_err("%s: " fmt, __func__, ##__VA_ARGS__) -#else -#define brcmf_err(fmt, ...) \ - do { \ - if (net_ratelimit()) \ - pr_err("%s: " fmt, __func__, ##__VA_ARGS__); \ - } while (0) -#endif -#else -__printf(2, 3) -void __brcmf_err(const char *func, const char *fmt, ...); -#define brcmf_err(fmt, ...) \ - __brcmf_err(__func__, fmt, ##__VA_ARGS__) -#endif - -#if defined(DEBUG) || defined(CONFIG_BRCM_TRACING) -__printf(3, 4) -void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...); -#define brcmf_dbg(level, fmt, ...) \ -do { \ - __brcmf_dbg(BRCMF_##level##_VAL, __func__, \ - fmt, ##__VA_ARGS__); \ -} while (0) -#define BRCMF_DATA_ON() (brcmf_msg_level & BRCMF_DATA_VAL) -#define BRCMF_CTL_ON() (brcmf_msg_level & BRCMF_CTL_VAL) -#define BRCMF_HDRS_ON() (brcmf_msg_level & BRCMF_HDRS_VAL) -#define BRCMF_BYTES_ON() (brcmf_msg_level & BRCMF_BYTES_VAL) -#define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL) -#define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL) -#define BRCMF_FIL_ON() (brcmf_msg_level & BRCMF_FIL_VAL) -#define BRCMF_FWCON_ON() (brcmf_msg_level & BRCMF_FWCON_VAL) - -#else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ - -#define brcmf_dbg(level, fmt, ...) no_printk(fmt, ##__VA_ARGS__) - -#define BRCMF_DATA_ON() 0 -#define BRCMF_CTL_ON() 0 -#define BRCMF_HDRS_ON() 0 -#define BRCMF_BYTES_ON() 0 -#define BRCMF_GLOM_ON() 0 -#define BRCMF_EVENT_ON() 0 -#define BRCMF_FIL_ON() 0 -#define BRCMF_FWCON_ON() 0 - -#endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ - -#define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \ -do { \ - trace_brcmf_hexdump((void *)data, len); \ - if (test) \ - brcmu_dbg_hex_dump(data, len, fmt, ##__VA_ARGS__); \ -} while (0) - -extern int brcmf_msg_level; - -struct brcmf_pub; -#ifdef DEBUG -void brcmf_debugfs_init(void); -void brcmf_debugfs_exit(void); -int brcmf_debug_attach(struct brcmf_pub *drvr); -void brcmf_debug_detach(struct brcmf_pub *drvr); -struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); -int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, - int (*read_fn)(struct seq_file *seq, void *data)); -#else -static inline void brcmf_debugfs_init(void) -{ -} -static inline void brcmf_debugfs_exit(void) -{ -} -static inline int brcmf_debug_attach(struct brcmf_pub *drvr) -{ - return 0; -} -static inline void brcmf_debug_detach(struct brcmf_pub *drvr) -{ -} -static inline -int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, - int (*read_fn)(struct seq_file *seq, void *data)) -{ - return 0; -} -#endif - -#endif /* BRCMFMAC_DEBUG_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c deleted file mode 100644 index 44bb30636690..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2014 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 "core.h" -#include "bus.h" -#include "debug.h" -#include "fwil.h" -#include "feature.h" - - -/* Module param feature_disable (global for all devices) */ -static int brcmf_feature_disable; -module_param_named(feature_disable, brcmf_feature_disable, int, 0); -MODULE_PARM_DESC(feature_disable, "Disable features"); - -/* - * expand feature list to array of feature strings. - */ -#define BRCMF_FEAT_DEF(_f) \ - #_f, -static const char *brcmf_feat_names[] = { - BRCMF_FEAT_LIST -}; -#undef BRCMF_FEAT_DEF - -#ifdef DEBUG -/* - * expand quirk list to array of quirk strings. - */ -#define BRCMF_QUIRK_DEF(_q) \ - #_q, -static const char * const brcmf_quirk_names[] = { - BRCMF_QUIRK_LIST -}; -#undef BRCMF_QUIRK_DEF - -/** - * brcmf_feat_debugfs_read() - expose feature info to debugfs. - * - * @seq: sequence for debugfs entry. - * @data: raw data pointer. - */ -static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); - u32 feats = bus_if->drvr->feat_flags; - u32 quirks = bus_if->drvr->chip_quirks; - int id; - - seq_printf(seq, "Features: %08x\n", feats); - for (id = 0; id < BRCMF_FEAT_LAST; id++) - if (feats & BIT(id)) - seq_printf(seq, "\t%s\n", brcmf_feat_names[id]); - seq_printf(seq, "\nQuirks: %08x\n", quirks); - for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++) - if (quirks & BIT(id)) - seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]); - return 0; -} -#else -static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data) -{ - return 0; -} -#endif /* DEBUG */ - -/** - * brcmf_feat_iovar_int_get() - determine feature through iovar query. - * - * @ifp: interface to query. - * @id: feature id. - * @name: iovar name. - */ -static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp, - enum brcmf_feat_id id, char *name) -{ - u32 data; - int err; - - err = brcmf_fil_iovar_int_get(ifp, name, &data); - if (err == 0) { - brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); - ifp->drvr->feat_flags |= BIT(id); - } else { - brcmf_dbg(TRACE, "%s feature check failed: %d\n", - brcmf_feat_names[id], err); - } -} - -/** - * brcmf_feat_iovar_int_set() - determine feature through iovar set. - * - * @ifp: interface to query. - * @id: feature id. - * @name: iovar name. - */ -static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp, - enum brcmf_feat_id id, char *name, u32 val) -{ - int err; - - err = brcmf_fil_iovar_int_set(ifp, name, val); - if (err == 0) { - brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); - ifp->drvr->feat_flags |= BIT(id); - } else { - brcmf_dbg(TRACE, "%s feature check failed: %d\n", - brcmf_feat_names[id], err); - } -} - -void brcmf_feat_attach(struct brcmf_pub *drvr) -{ - struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); - - brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); - brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); - if (drvr->bus_if->wowl_supported) - brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); - if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) - brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); - brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p"); - - if (brcmf_feature_disable) { - brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", - ifp->drvr->feat_flags, brcmf_feature_disable); - ifp->drvr->feat_flags &= ~brcmf_feature_disable; - } - - /* set chip related quirks */ - switch (drvr->bus_if->chip) { - case BRCM_CC_43236_CHIP_ID: - drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH); - break; - case BRCM_CC_4329_CHIP_ID: - drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC); - break; - default: - /* no quirks */ - break; - } - - brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read); -} - -bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id) -{ - return (ifp->drvr->feat_flags & BIT(id)); -} - -bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp, - enum brcmf_feat_quirk quirk) -{ - return (ifp->drvr->chip_quirks & BIT(quirk)); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h deleted file mode 100644 index 6b381f799f22..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2014 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 _BRCMF_FEATURE_H -#define _BRCMF_FEATURE_H - -/* - * Features: - * - * MBSS: multiple BSSID support (eg. guest network in AP mode). - * MCHAN: multi-channel for concurrent P2P. - * PNO: preferred network offload. - * WOWL: Wake-On-WLAN. - * P2P: peer-to-peer - */ -#define BRCMF_FEAT_LIST \ - BRCMF_FEAT_DEF(MBSS) \ - BRCMF_FEAT_DEF(MCHAN) \ - BRCMF_FEAT_DEF(PNO) \ - BRCMF_FEAT_DEF(WOWL) \ - BRCMF_FEAT_DEF(P2P) -/* - * Quirks: - * - * AUTO_AUTH: workaround needed for automatic authentication type. - * NEED_MPC: driver needs to disable MPC during scanning operation. - */ -#define BRCMF_QUIRK_LIST \ - BRCMF_QUIRK_DEF(AUTO_AUTH) \ - BRCMF_QUIRK_DEF(NEED_MPC) - -#define BRCMF_FEAT_DEF(_f) \ - BRCMF_FEAT_ ## _f, -/* - * expand feature list to enumeration. - */ -enum brcmf_feat_id { - BRCMF_FEAT_LIST - BRCMF_FEAT_LAST -}; -#undef BRCMF_FEAT_DEF - -#define BRCMF_QUIRK_DEF(_q) \ - BRCMF_FEAT_QUIRK_ ## _q, -/* - * expand quirk list to enumeration. - */ -enum brcmf_feat_quirk { - BRCMF_QUIRK_LIST - BRCMF_FEAT_QUIRK_LAST -}; -#undef BRCMF_QUIRK_DEF - -/** - * brcmf_feat_attach() - determine features and quirks. - * - * @drvr: driver instance. - */ -void brcmf_feat_attach(struct brcmf_pub *drvr); - -/** - * brcmf_feat_is_enabled() - query feature. - * - * @ifp: interface instance. - * @id: feature id to check. - * - * Return: true is feature is enabled; otherwise false. - */ -bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id); - -/** - * brcmf_feat_is_quirk_enabled() - query chip quirk. - * - * @ifp: interface instance. - * @quirk: quirk id to check. - * - * Return: true is quirk is enabled; otherwise false. - */ -bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp, - enum brcmf_feat_quirk quirk); - -#endif /* _BRCMF_FEATURE_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c deleted file mode 100644 index 4248f3c80e78..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - * 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 -#include -#include - -#include "debug.h" -#include "firmware.h" - -#define BRCMF_FW_MAX_NVRAM_SIZE 64000 -#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */ -#define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */ - -char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; -module_param_string(alternative_fw_path, brcmf_firmware_path, - BRCMF_FW_PATH_LEN, 0440); - -enum nvram_parser_state { - IDLE, - KEY, - VALUE, - COMMENT, - END -}; - -/** - * struct nvram_parser - internal info for parser. - * - * @state: current parser state. - * @data: input buffer being parsed. - * @nvram: output buffer with parse result. - * @nvram_len: lenght of parse result. - * @line: current line. - * @column: current column in line. - * @pos: byte offset in input buffer. - * @entry: start position of key,value entry. - * @multi_dev_v1: detect pcie multi device v1 (compressed). - * @multi_dev_v2: detect pcie multi device v2. - */ -struct nvram_parser { - enum nvram_parser_state state; - const u8 *data; - u8 *nvram; - u32 nvram_len; - u32 line; - u32 column; - u32 pos; - u32 entry; - bool multi_dev_v1; - bool multi_dev_v2; -}; - -/** - * is_nvram_char() - check if char is a valid one for NVRAM entry - * - * It accepts all printable ASCII chars except for '#' which opens a comment. - * Please note that ' ' (space) while accepted is not a valid key name char. - */ -static bool is_nvram_char(char c) -{ - /* comment marker excluded */ - if (c == '#') - return false; - - /* key and value may have any other readable character */ - return (c >= 0x20 && c < 0x7f); -} - -static bool is_whitespace(char c) -{ - return (c == ' ' || c == '\r' || c == '\n' || c == '\t'); -} - -static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp) -{ - char c; - - c = nvp->data[nvp->pos]; - if (c == '\n') - return COMMENT; - if (is_whitespace(c)) - goto proceed; - if (c == '#') - return COMMENT; - if (is_nvram_char(c)) { - nvp->entry = nvp->pos; - return KEY; - } - brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n", - nvp->line, nvp->column); -proceed: - nvp->column++; - nvp->pos++; - return IDLE; -} - -static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp) -{ - enum nvram_parser_state st = nvp->state; - char c; - - c = nvp->data[nvp->pos]; - if (c == '=') { - /* ignore RAW1 by treating as comment */ - if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0) - st = COMMENT; - else - st = VALUE; - if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0) - nvp->multi_dev_v1 = true; - if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0) - nvp->multi_dev_v2 = true; - } else if (!is_nvram_char(c) || c == ' ') { - brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", - nvp->line, nvp->column); - return COMMENT; - } - - nvp->column++; - nvp->pos++; - return st; -} - -static enum nvram_parser_state -brcmf_nvram_handle_value(struct nvram_parser *nvp) -{ - char c; - char *skv; - char *ekv; - u32 cplen; - - c = nvp->data[nvp->pos]; - if (!is_nvram_char(c)) { - /* key,value pair complete */ - ekv = (u8 *)&nvp->data[nvp->pos]; - skv = (u8 *)&nvp->data[nvp->entry]; - cplen = ekv - skv; - if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE) - return END; - /* copy to output buffer */ - memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen); - nvp->nvram_len += cplen; - nvp->nvram[nvp->nvram_len] = '\0'; - nvp->nvram_len++; - return IDLE; - } - nvp->pos++; - nvp->column++; - return VALUE; -} - -static enum nvram_parser_state -brcmf_nvram_handle_comment(struct nvram_parser *nvp) -{ - char *eoc, *sol; - - sol = (char *)&nvp->data[nvp->pos]; - eoc = strchr(sol, '\n'); - if (!eoc) { - eoc = strchr(sol, '\0'); - if (!eoc) - return END; - } - - /* eat all moving to next line */ - nvp->line++; - nvp->column = 1; - nvp->pos += (eoc - sol) + 1; - return IDLE; -} - -static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp) -{ - /* final state */ - return END; -} - -static enum nvram_parser_state -(*nv_parser_states[])(struct nvram_parser *nvp) = { - brcmf_nvram_handle_idle, - brcmf_nvram_handle_key, - brcmf_nvram_handle_value, - brcmf_nvram_handle_comment, - brcmf_nvram_handle_end -}; - -static int brcmf_init_nvram_parser(struct nvram_parser *nvp, - const u8 *data, size_t data_len) -{ - size_t size; - - memset(nvp, 0, sizeof(*nvp)); - nvp->data = data; - /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */ - if (data_len > BRCMF_FW_MAX_NVRAM_SIZE) - size = BRCMF_FW_MAX_NVRAM_SIZE; - else - size = data_len; - /* Alloc for extra 0 byte + roundup by 4 + length field */ - size += 1 + 3 + sizeof(u32); - nvp->nvram = kzalloc(size, GFP_KERNEL); - if (!nvp->nvram) - return -ENOMEM; - - nvp->line = 1; - nvp->column = 1; - return 0; -} - -/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple - * devices. Strip it down for one device, use domain_nr/bus_nr to determine - * which data is to be returned. v1 is the version where nvram is stored - * compressed and "devpath" maps to index for valid entries. - */ -static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr, - u16 bus_nr) -{ - /* Device path with a leading '=' key-value separator */ - char pci_path[] = "=pci/?/?"; - size_t pci_len; - char pcie_path[] = "=pcie/?/?"; - size_t pcie_len; - - u32 i, j; - bool found; - u8 *nvram; - u8 id; - - nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); - if (!nvram) - goto fail; - - /* min length: devpath0=pcie/1/4/ + 0:x=y */ - if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6) - goto fail; - - /* First search for the devpathX and see if it is the configuration - * for domain_nr/bus_nr. Search complete nvp - */ - snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr, - bus_nr); - pci_len = strlen(pci_path); - snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr, - bus_nr); - pcie_len = strlen(pcie_path); - found = false; - i = 0; - while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) { - /* Format: devpathX=pcie/Y/Z/ - * Y = domain_nr, Z = bus_nr, X = virtual ID - */ - if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 && - (!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) || - !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) { - id = nvp->nvram[i + 7] - '0'; - found = true; - break; - } - while (nvp->nvram[i] != 0) - i++; - i++; - } - if (!found) - goto fail; - - /* Now copy all valid entries, release old nvram and assign new one */ - i = 0; - j = 0; - while (i < nvp->nvram_len) { - if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) { - i += 2; - while (nvp->nvram[i] != 0) { - nvram[j] = nvp->nvram[i]; - i++; - j++; - } - nvram[j] = 0; - j++; - } - while (nvp->nvram[i] != 0) - i++; - i++; - } - kfree(nvp->nvram); - nvp->nvram = nvram; - nvp->nvram_len = j; - return; - -fail: - kfree(nvram); - nvp->nvram_len = 0; -} - -/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple - * devices. Strip it down for one device, use domain_nr/bus_nr to determine - * which data is to be returned. v2 is the version where nvram is stored - * uncompressed, all relevant valid entries are identified by - * pcie/domain_nr/bus_nr: - */ -static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr, - u16 bus_nr) -{ - char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN]; - size_t len; - u32 i, j; - u8 *nvram; - - nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); - if (!nvram) - goto fail; - - /* Copy all valid entries, release old nvram and assign new one. - * Valid entries are of type pcie/X/Y/ where X = domain_nr and - * Y = bus_nr. - */ - snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr); - len = strlen(prefix); - i = 0; - j = 0; - while (i < nvp->nvram_len - len) { - if (strncmp(&nvp->nvram[i], prefix, len) == 0) { - i += len; - while (nvp->nvram[i] != 0) { - nvram[j] = nvp->nvram[i]; - i++; - j++; - } - nvram[j] = 0; - j++; - } - while (nvp->nvram[i] != 0) - i++; - i++; - } - kfree(nvp->nvram); - nvp->nvram = nvram; - nvp->nvram_len = j; - return; -fail: - kfree(nvram); - nvp->nvram_len = 0; -} - -/* brcmf_nvram_strip :Takes a buffer of "=\n" lines read from a fil - * 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. - */ -static void *brcmf_fw_nvram_strip(const u8 *data, size_t data_len, - u32 *new_length, u16 domain_nr, u16 bus_nr) -{ - struct nvram_parser nvp; - u32 pad; - u32 token; - __le32 token_le; - - if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0) - return NULL; - - while (nvp.pos < data_len) { - nvp.state = nv_parser_states[nvp.state](&nvp); - if (nvp.state == END) - break; - } - if (nvp.multi_dev_v1) - brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr); - else if (nvp.multi_dev_v2) - brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr); - - if (nvp.nvram_len == 0) { - kfree(nvp.nvram); - return NULL; - } - - pad = nvp.nvram_len; - *new_length = roundup(nvp.nvram_len + 1, 4); - while (pad != *new_length) { - nvp.nvram[pad] = 0; - pad++; - } - - token = *new_length / 4; - token = (~token << 16) | (token & 0x0000FFFF); - token_le = cpu_to_le32(token); - - memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le)); - *new_length += sizeof(token_le); - - return nvp.nvram; -} - -void brcmf_fw_nvram_free(void *nvram) -{ - kfree(nvram); -} - -struct brcmf_fw { - struct device *dev; - u16 flags; - const struct firmware *code; - const char *nvram_name; - u16 domain_nr; - u16 bus_nr; - void (*done)(struct device *dev, const struct firmware *fw, - void *nvram_image, u32 nvram_len); -}; - -static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) -{ - struct brcmf_fw *fwctx = ctx; - u32 nvram_length = 0; - void *nvram = NULL; - u8 *data = NULL; - size_t data_len; - bool raw_nvram; - - brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); - if (fw && fw->data) { - data = (u8 *)fw->data; - data_len = fw->size; - raw_nvram = false; - } else { - data = bcm47xx_nvram_get_contents(&data_len); - if (!data && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) - goto fail; - raw_nvram = true; - } - - if (data) - nvram = brcmf_fw_nvram_strip(data, data_len, &nvram_length, - fwctx->domain_nr, fwctx->bus_nr); - - if (raw_nvram) - bcm47xx_nvram_release_contents(data); - if (fw) - release_firmware(fw); - if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) - goto fail; - - fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length); - kfree(fwctx); - return; - -fail: - brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); - release_firmware(fwctx->code); - device_release_driver(fwctx->dev); - kfree(fwctx); -} - -static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx) -{ - struct brcmf_fw *fwctx = ctx; - int ret; - - brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); - if (!fw) - goto fail; - - /* only requested code so done here */ - if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) { - fwctx->done(fwctx->dev, fw, NULL, 0); - kfree(fwctx); - return; - } - fwctx->code = fw; - ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name, - fwctx->dev, GFP_KERNEL, fwctx, - brcmf_fw_request_nvram_done); - - if (!ret) - return; - - brcmf_fw_request_nvram_done(NULL, fwctx); - return; - -fail: - brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); - device_release_driver(fwctx->dev); - kfree(fwctx); -} - -int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, - const char *code, const char *nvram, - void (*fw_cb)(struct device *dev, - const struct firmware *fw, - void *nvram_image, u32 nvram_len), - u16 domain_nr, u16 bus_nr) -{ - struct brcmf_fw *fwctx; - - brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev)); - if (!fw_cb || !code) - return -EINVAL; - - if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram) - return -EINVAL; - - fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL); - if (!fwctx) - return -ENOMEM; - - fwctx->dev = dev; - fwctx->flags = flags; - fwctx->done = fw_cb; - if (flags & BRCMF_FW_REQUEST_NVRAM) - fwctx->nvram_name = nvram; - fwctx->domain_nr = domain_nr; - fwctx->bus_nr = bus_nr; - - return request_firmware_nowait(THIS_MODULE, true, code, dev, - GFP_KERNEL, fwctx, - brcmf_fw_request_code_done); -} - -int brcmf_fw_get_firmwares(struct device *dev, u16 flags, - const char *code, const char *nvram, - void (*fw_cb)(struct device *dev, - const struct firmware *fw, - void *nvram_image, u32 nvram_len)) -{ - return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0, - 0); -} - diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h deleted file mode 100644 index 604dd48ab4e0..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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_FIRMWARE_H -#define BRCMFMAC_FIRMWARE_H - -#define BRCMF_FW_REQUEST 0x000F -#define BRCMF_FW_REQUEST_NVRAM 0x0001 -#define BRCMF_FW_REQ_FLAGS 0x00F0 -#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010 - -#define BRCMF_FW_PATH_LEN 256 -#define BRCMF_FW_NAME_LEN 32 - -extern char brcmf_firmware_path[]; - -void brcmf_fw_nvram_free(void *nvram); -/* - * Request firmware(s) asynchronously. When the asynchronous request - * fails it will not use the callback, but call device_release_driver() - * instead which will call the driver .remove() callback. - */ -int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, - const char *code, const char *nvram, - void (*fw_cb)(struct device *dev, - const struct firmware *fw, - void *nvram_image, u32 nvram_len), - u16 domain_nr, u16 bus_nr); -int brcmf_fw_get_firmwares(struct device *dev, u16 flags, - const char *code, const char *nvram, - void (*fw_cb)(struct device *dev, - const struct firmware *fw, - void *nvram_image, u32 nvram_len)); - -#endif /* BRCMFMAC_FIRMWARE_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c deleted file mode 100644 index 2ca783fa50cf..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ /dev/null @@ -1,504 +0,0 @@ -/* Copyright (c) 2014 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 - -#include "core.h" -#include "debug.h" -#include "bus.h" -#include "proto.h" -#include "flowring.h" -#include "msgbuf.h" -#include "common.h" - - -#define BRCMF_FLOWRING_HIGH 1024 -#define BRCMF_FLOWRING_LOW (BRCMF_FLOWRING_HIGH - 256) -#define BRCMF_FLOWRING_INVALID_IFIDX 0xff - -#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16) -#define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16) - -static const u8 brcmf_flowring_prio2fifo[] = { - 1, - 0, - 0, - 1, - 2, - 2, - 3, - 3 -}; - - -static bool -brcmf_flowring_is_tdls_mac(struct brcmf_flowring *flow, u8 mac[ETH_ALEN]) -{ - struct brcmf_flowring_tdls_entry *search; - - search = flow->tdls_entry; - - while (search) { - if (memcmp(search->mac, mac, ETH_ALEN) == 0) - return true; - search = search->next; - } - - return false; -} - - -u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN], - u8 prio, u8 ifidx) -{ - struct brcmf_flowring_hash *hash; - u8 hash_idx; - u32 i; - bool found; - bool sta; - u8 fifo; - u8 *mac; - - fifo = brcmf_flowring_prio2fifo[prio]; - sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); - mac = da; - if ((!sta) && (is_multicast_ether_addr(da))) { - mac = (u8 *)ALLFFMAC; - fifo = 0; - } - if ((sta) && (flow->tdls_active) && - (brcmf_flowring_is_tdls_mac(flow, da))) { - sta = false; - } - hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : - BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); - found = false; - hash = flow->hash; - for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { - if ((sta || (memcmp(hash[hash_idx].mac, mac, ETH_ALEN) == 0)) && - (hash[hash_idx].fifo == fifo) && - (hash[hash_idx].ifidx == ifidx)) { - found = true; - break; - } - hash_idx++; - } - if (found) - return hash[hash_idx].flowid; - - return BRCMF_FLOWRING_INVALID_ID; -} - - -u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], - u8 prio, u8 ifidx) -{ - struct brcmf_flowring_ring *ring; - struct brcmf_flowring_hash *hash; - u8 hash_idx; - u32 i; - bool found; - u8 fifo; - bool sta; - u8 *mac; - - fifo = brcmf_flowring_prio2fifo[prio]; - sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); - mac = da; - if ((!sta) && (is_multicast_ether_addr(da))) { - mac = (u8 *)ALLFFMAC; - fifo = 0; - } - if ((sta) && (flow->tdls_active) && - (brcmf_flowring_is_tdls_mac(flow, da))) { - sta = false; - } - hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : - BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); - found = false; - hash = flow->hash; - for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { - if ((hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX) && - (is_zero_ether_addr(hash[hash_idx].mac))) { - found = true; - break; - } - hash_idx++; - } - if (found) { - for (i = 0; i < flow->nrofrings; i++) { - if (flow->rings[i] == NULL) - break; - } - if (i == flow->nrofrings) - return -ENOMEM; - - ring = kzalloc(sizeof(*ring), GFP_ATOMIC); - if (!ring) - return -ENOMEM; - - memcpy(hash[hash_idx].mac, mac, ETH_ALEN); - hash[hash_idx].fifo = fifo; - hash[hash_idx].ifidx = ifidx; - hash[hash_idx].flowid = i; - - ring->hash_id = hash_idx; - ring->status = RING_CLOSED; - skb_queue_head_init(&ring->skblist); - flow->rings[i] = ring; - - return i; - } - return BRCMF_FLOWRING_INVALID_ID; -} - - -u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid) -{ - struct brcmf_flowring_ring *ring; - - ring = flow->rings[flowid]; - - return flow->hash[ring->hash_id].fifo; -} - - -static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid, - bool blocked) -{ - struct brcmf_flowring_ring *ring; - struct brcmf_bus *bus_if; - struct brcmf_pub *drvr; - struct brcmf_if *ifp; - bool currently_blocked; - int i; - u8 ifidx; - unsigned long flags; - - spin_lock_irqsave(&flow->block_lock, flags); - - ring = flow->rings[flowid]; - if (ring->blocked == blocked) { - spin_unlock_irqrestore(&flow->block_lock, flags); - return; - } - ifidx = brcmf_flowring_ifidx_get(flow, flowid); - - currently_blocked = false; - for (i = 0; i < flow->nrofrings; i++) { - if ((flow->rings[i]) && (i != flowid)) { - ring = flow->rings[i]; - if ((ring->status == RING_OPEN) && - (brcmf_flowring_ifidx_get(flow, i) == ifidx)) { - if (ring->blocked) { - currently_blocked = true; - break; - } - } - } - } - flow->rings[flowid]->blocked = blocked; - if (currently_blocked) { - spin_unlock_irqrestore(&flow->block_lock, flags); - return; - } - - bus_if = dev_get_drvdata(flow->dev); - drvr = bus_if->drvr; - ifp = brcmf_get_ifp(drvr, ifidx); - brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked); - - spin_unlock_irqrestore(&flow->block_lock, flags); -} - - -void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) -{ - struct brcmf_flowring_ring *ring; - u8 hash_idx; - struct sk_buff *skb; - - ring = flow->rings[flowid]; - if (!ring) - return; - brcmf_flowring_block(flow, flowid, false); - hash_idx = ring->hash_id; - flow->hash[hash_idx].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; - eth_zero_addr(flow->hash[hash_idx].mac); - flow->rings[flowid] = NULL; - - skb = skb_dequeue(&ring->skblist); - while (skb) { - brcmu_pkt_buf_free_skb(skb); - skb = skb_dequeue(&ring->skblist); - } - - kfree(ring); -} - - -u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, - struct sk_buff *skb) -{ - struct brcmf_flowring_ring *ring; - - ring = flow->rings[flowid]; - - skb_queue_tail(&ring->skblist, skb); - - if (!ring->blocked && - (skb_queue_len(&ring->skblist) > BRCMF_FLOWRING_HIGH)) { - brcmf_flowring_block(flow, flowid, true); - brcmf_dbg(MSGBUF, "Flowcontrol: BLOCK for ring %d\n", flowid); - /* To prevent (work around) possible race condition, check - * queue len again. It is also possible to use locking to - * protect, but that is undesirable for every enqueue and - * dequeue. This simple check will solve a possible race - * condition if it occurs. - */ - if (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW) - brcmf_flowring_block(flow, flowid, false); - } - return skb_queue_len(&ring->skblist); -} - - -struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid) -{ - struct brcmf_flowring_ring *ring; - struct sk_buff *skb; - - ring = flow->rings[flowid]; - if (ring->status != RING_OPEN) - return NULL; - - skb = skb_dequeue(&ring->skblist); - - if (ring->blocked && - (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)) { - brcmf_flowring_block(flow, flowid, false); - brcmf_dbg(MSGBUF, "Flowcontrol: OPEN for ring %d\n", flowid); - } - - return skb; -} - - -void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, - struct sk_buff *skb) -{ - struct brcmf_flowring_ring *ring; - - ring = flow->rings[flowid]; - - skb_queue_head(&ring->skblist, skb); -} - - -u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid) -{ - struct brcmf_flowring_ring *ring; - - ring = flow->rings[flowid]; - if (!ring) - return 0; - - if (ring->status != RING_OPEN) - return 0; - - return skb_queue_len(&ring->skblist); -} - - -void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid) -{ - struct brcmf_flowring_ring *ring; - - ring = flow->rings[flowid]; - if (!ring) { - brcmf_err("Ring NULL, for flowid %d\n", flowid); - return; - } - - ring->status = RING_OPEN; -} - - -u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid) -{ - struct brcmf_flowring_ring *ring; - u8 hash_idx; - - ring = flow->rings[flowid]; - hash_idx = ring->hash_id; - - return flow->hash[hash_idx].ifidx; -} - - -struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) -{ - struct brcmf_flowring *flow; - u32 i; - - flow = kzalloc(sizeof(*flow), GFP_KERNEL); - if (flow) { - flow->dev = dev; - flow->nrofrings = nrofrings; - spin_lock_init(&flow->block_lock); - for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++) - flow->addr_mode[i] = ADDR_INDIRECT; - for (i = 0; i < ARRAY_SIZE(flow->hash); i++) - flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; - flow->rings = kcalloc(nrofrings, sizeof(*flow->rings), - GFP_KERNEL); - if (!flow->rings) { - kfree(flow); - flow = NULL; - } - } - - return flow; -} - - -void brcmf_flowring_detach(struct brcmf_flowring *flow) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); - struct brcmf_pub *drvr = bus_if->drvr; - struct brcmf_flowring_tdls_entry *search; - struct brcmf_flowring_tdls_entry *remove; - u8 flowid; - - for (flowid = 0; flowid < flow->nrofrings; flowid++) { - if (flow->rings[flowid]) - brcmf_msgbuf_delete_flowring(drvr, flowid); - } - - search = flow->tdls_entry; - while (search) { - remove = search; - search = search->next; - kfree(remove); - } - kfree(flow->rings); - kfree(flow); -} - - -void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, - enum proto_addr_mode addr_mode) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); - struct brcmf_pub *drvr = bus_if->drvr; - u32 i; - u8 flowid; - - if (flow->addr_mode[ifidx] != addr_mode) { - for (i = 0; i < ARRAY_SIZE(flow->hash); i++) { - if (flow->hash[i].ifidx == ifidx) { - flowid = flow->hash[i].flowid; - if (flow->rings[flowid]->status != RING_OPEN) - continue; - flow->rings[flowid]->status = RING_CLOSING; - brcmf_msgbuf_delete_flowring(drvr, flowid); - } - } - flow->addr_mode[ifidx] = addr_mode; - } -} - - -void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, - u8 peer[ETH_ALEN]) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); - struct brcmf_pub *drvr = bus_if->drvr; - struct brcmf_flowring_hash *hash; - struct brcmf_flowring_tdls_entry *prev; - struct brcmf_flowring_tdls_entry *search; - u32 i; - u8 flowid; - bool sta; - - sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); - - search = flow->tdls_entry; - prev = NULL; - while (search) { - if (memcmp(search->mac, peer, ETH_ALEN) == 0) { - sta = false; - break; - } - prev = search; - search = search->next; - } - - hash = flow->hash; - for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { - if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) && - (hash[i].ifidx == ifidx)) { - flowid = flow->hash[i].flowid; - if (flow->rings[flowid]->status == RING_OPEN) { - flow->rings[flowid]->status = RING_CLOSING; - brcmf_msgbuf_delete_flowring(drvr, flowid); - } - } - } - - if (search) { - if (prev) - prev->next = search->next; - else - flow->tdls_entry = search->next; - kfree(search); - if (flow->tdls_entry == NULL) - flow->tdls_active = false; - } -} - - -void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx, - u8 peer[ETH_ALEN]) -{ - struct brcmf_flowring_tdls_entry *tdls_entry; - struct brcmf_flowring_tdls_entry *search; - - tdls_entry = kzalloc(sizeof(*tdls_entry), GFP_ATOMIC); - if (tdls_entry == NULL) - return; - - memcpy(tdls_entry->mac, peer, ETH_ALEN); - tdls_entry->next = NULL; - if (flow->tdls_entry == NULL) { - flow->tdls_entry = tdls_entry; - } else { - search = flow->tdls_entry; - if (memcmp(search->mac, peer, ETH_ALEN) == 0) - return; - while (search->next) { - search = search->next; - if (memcmp(search->mac, peer, ETH_ALEN) == 0) - return; - } - search->next = tdls_entry; - } - - flow->tdls_active = true; -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h deleted file mode 100644 index 95fd1c9675d1..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright (c) 2014 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_FLOWRING_H -#define BRCMFMAC_FLOWRING_H - - -#define BRCMF_FLOWRING_HASHSIZE 256 -#define BRCMF_FLOWRING_INVALID_ID 0xFFFFFFFF - - -struct brcmf_flowring_hash { - u8 mac[ETH_ALEN]; - u8 fifo; - u8 ifidx; - u8 flowid; -}; - -enum ring_status { - RING_CLOSED, - RING_CLOSING, - RING_OPEN -}; - -struct brcmf_flowring_ring { - u16 hash_id; - bool blocked; - enum ring_status status; - struct sk_buff_head skblist; -}; - -struct brcmf_flowring_tdls_entry { - u8 mac[ETH_ALEN]; - struct brcmf_flowring_tdls_entry *next; -}; - -struct brcmf_flowring { - struct device *dev; - struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE]; - struct brcmf_flowring_ring **rings; - spinlock_t block_lock; - enum proto_addr_mode addr_mode[BRCMF_MAX_IFS]; - u16 nrofrings; - bool tdls_active; - struct brcmf_flowring_tdls_entry *tdls_entry; -}; - - -u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN], - u8 prio, u8 ifidx); -u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], - u8 prio, u8 ifidx); -void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid); -void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid); -u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid); -u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, - struct sk_buff *skb); -struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid); -void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, - struct sk_buff *skb); -u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid); -u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid); -struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings); -void brcmf_flowring_detach(struct brcmf_flowring *flow); -void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, - enum proto_addr_mode addr_mode); -void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, - u8 peer[ETH_ALEN]); -void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx, - u8 peer[ETH_ALEN]); - - -#endif /* BRCMFMAC_FLOWRING_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c deleted file mode 100644 index 3878b6f6cfce..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright (c) 2012 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 "brcmu_wifi.h" -#include "brcmu_utils.h" - -#include "core.h" -#include "debug.h" -#include "tracepoint.h" -#include "fwsignal.h" -#include "fweh.h" -#include "fwil.h" - -/** - * struct brcm_ethhdr - broadcom specific ether header. - * - * @subtype: subtype for this packet. - * @length: TODO: length of appended data. - * @version: version indication. - * @oui: OUI of this packet. - * @usr_subtype: subtype for this OUI. - */ -struct brcm_ethhdr { - __be16 subtype; - __be16 length; - u8 version; - u8 oui[3]; - __be16 usr_subtype; -} __packed; - -struct brcmf_event_msg_be { - __be16 version; - __be16 flags; - __be32 event_type; - __be32 status; - __be32 reason; - __be32 auth_type; - __be32 datalen; - u8 addr[ETH_ALEN]; - char ifname[IFNAMSIZ]; - u8 ifidx; - u8 bsscfgidx; -} __packed; - -/** - * struct brcmf_event - contents of broadcom event packet. - * - * @eth: standard ether header. - * @hdr: broadcom specific ether header. - * @msg: common part of the actual event message. - */ -struct brcmf_event { - struct ethhdr eth; - struct brcm_ethhdr hdr; - struct brcmf_event_msg_be msg; -} __packed; - -/** - * struct brcmf_fweh_queue_item - event item on event queue. - * - * @q: list element for queuing. - * @code: event code. - * @ifidx: interface index related to this event. - * @ifaddr: ethernet address for interface. - * @emsg: common parameters of the firmware event message. - * @data: event specific data part of the firmware event. - */ -struct brcmf_fweh_queue_item { - struct list_head q; - enum brcmf_fweh_event_code code; - u8 ifidx; - u8 ifaddr[ETH_ALEN]; - struct brcmf_event_msg_be emsg; - u8 data[0]; -}; - -/** - * struct brcmf_fweh_event_name - code, name mapping entry. - */ -struct brcmf_fweh_event_name { - enum brcmf_fweh_event_code code; - const char *name; -}; - -#ifdef DEBUG -#define BRCMF_ENUM_DEF(id, val) \ - { val, #id }, - -/* array for mapping code to event name */ -static struct brcmf_fweh_event_name fweh_event_names[] = { - BRCMF_FWEH_EVENT_ENUM_DEFLIST -}; -#undef BRCMF_ENUM_DEF - -/** - * brcmf_fweh_event_name() - returns name for given event code. - * - * @code: code to lookup. - */ -static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) -{ - int i; - for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) { - if (fweh_event_names[i].code == code) - return fweh_event_names[i].name; - } - return "unknown"; -} -#else -static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) -{ - return "nodebug"; -} -#endif - -/** - * brcmf_fweh_queue_event() - create and queue event. - * - * @fweh: firmware event handling info. - * @event: event queue entry. - */ -static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh, - struct brcmf_fweh_queue_item *event) -{ - ulong flags; - - spin_lock_irqsave(&fweh->evt_q_lock, flags); - list_add_tail(&event->q, &fweh->event_q); - spin_unlock_irqrestore(&fweh->evt_q_lock, flags); - schedule_work(&fweh->event_work); -} - -static int brcmf_fweh_call_event_handler(struct brcmf_if *ifp, - enum brcmf_fweh_event_code code, - struct brcmf_event_msg *emsg, - void *data) -{ - struct brcmf_fweh_info *fweh; - int err = -EINVAL; - - if (ifp) { - fweh = &ifp->drvr->fweh; - - /* handle the event if valid interface and handler */ - if (fweh->evt_handler[code]) - err = fweh->evt_handler[code](ifp, emsg, data); - else - brcmf_err("unhandled event %d ignored\n", code); - } else { - brcmf_err("no interface object\n"); - } - return err; -} - -/** - * brcmf_fweh_handle_if_event() - handle IF event. - * - * @drvr: driver information object. - * @item: queue entry. - * @ifpp: interface object (may change upon ADD action). - */ -static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, - struct brcmf_event_msg *emsg, - void *data) -{ - struct brcmf_if_event *ifevent = data; - struct brcmf_if *ifp; - bool is_p2pdev; - int err = 0; - - brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n", - ifevent->action, ifevent->ifidx, ifevent->bssidx, - ifevent->flags, ifevent->role); - - /* The P2P Device interface event must not be ignored contrary to what - * firmware tells us. Older firmware uses p2p noif, with sta role. - * This should be accepted when p2pdev_setup is ongoing. TDLS setup will - * use the same ifevent and should be ignored. - */ - is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) && - (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT || - ((ifevent->role == BRCMF_E_IF_ROLE_STA) && - (drvr->fweh.p2pdev_setup_ongoing)))); - if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) { - brcmf_dbg(EVENT, "event can be ignored\n"); - return; - } - if (ifevent->ifidx >= BRCMF_MAX_IFS) { - brcmf_err("invalid interface index: %u\n", ifevent->ifidx); - return; - } - - ifp = drvr->iflist[ifevent->bssidx]; - - if (ifevent->action == BRCMF_E_IF_ADD) { - brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname, - emsg->addr); - ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx, - is_p2pdev, emsg->ifname, emsg->addr); - if (IS_ERR(ifp)) - return; - if (!is_p2pdev) - brcmf_fws_add_interface(ifp); - if (!drvr->fweh.evt_handler[BRCMF_E_IF]) - if (brcmf_net_attach(ifp, false) < 0) - return; - } - - if (ifp && ifevent->action == BRCMF_E_IF_CHANGE) - brcmf_fws_reset_interface(ifp); - - err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data); - - if (ifp && ifevent->action == BRCMF_E_IF_DEL) - brcmf_remove_interface(ifp); -} - -/** - * brcmf_fweh_dequeue_event() - get event from the queue. - * - * @fweh: firmware event handling info. - */ -static struct brcmf_fweh_queue_item * -brcmf_fweh_dequeue_event(struct brcmf_fweh_info *fweh) -{ - struct brcmf_fweh_queue_item *event = NULL; - ulong flags; - - spin_lock_irqsave(&fweh->evt_q_lock, flags); - if (!list_empty(&fweh->event_q)) { - event = list_first_entry(&fweh->event_q, - struct brcmf_fweh_queue_item, q); - list_del(&event->q); - } - spin_unlock_irqrestore(&fweh->evt_q_lock, flags); - - return event; -} - -/** - * brcmf_fweh_event_worker() - firmware event worker. - * - * @work: worker object. - */ -static void brcmf_fweh_event_worker(struct work_struct *work) -{ - struct brcmf_pub *drvr; - struct brcmf_if *ifp; - struct brcmf_fweh_info *fweh; - struct brcmf_fweh_queue_item *event; - int err = 0; - struct brcmf_event_msg_be *emsg_be; - struct brcmf_event_msg emsg; - - fweh = container_of(work, struct brcmf_fweh_info, event_work); - drvr = container_of(fweh, struct brcmf_pub, fweh); - - while ((event = brcmf_fweh_dequeue_event(fweh))) { - brcmf_dbg(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM\n", - brcmf_fweh_event_name(event->code), event->code, - event->emsg.ifidx, event->emsg.bsscfgidx, - event->emsg.addr); - - /* convert event message */ - emsg_be = &event->emsg; - emsg.version = be16_to_cpu(emsg_be->version); - emsg.flags = be16_to_cpu(emsg_be->flags); - emsg.event_code = event->code; - emsg.status = be32_to_cpu(emsg_be->status); - emsg.reason = be32_to_cpu(emsg_be->reason); - emsg.auth_type = be32_to_cpu(emsg_be->auth_type); - emsg.datalen = be32_to_cpu(emsg_be->datalen); - memcpy(emsg.addr, emsg_be->addr, ETH_ALEN); - memcpy(emsg.ifname, emsg_be->ifname, sizeof(emsg.ifname)); - emsg.ifidx = emsg_be->ifidx; - emsg.bsscfgidx = emsg_be->bsscfgidx; - - brcmf_dbg(EVENT, " version %u flags %u status %u reason %u\n", - emsg.version, emsg.flags, emsg.status, emsg.reason); - brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data, - min_t(u32, emsg.datalen, 64), - "event payload, len=%d\n", emsg.datalen); - - /* special handling of interface event */ - if (event->code == BRCMF_E_IF) { - brcmf_fweh_handle_if_event(drvr, &emsg, event->data); - goto event_free; - } - - if (event->code == BRCMF_E_TDLS_PEER_EVENT) - ifp = drvr->iflist[0]; - else - ifp = drvr->iflist[emsg.bsscfgidx]; - err = brcmf_fweh_call_event_handler(ifp, event->code, &emsg, - event->data); - if (err) { - brcmf_err("event handler failed (%d)\n", - event->code); - err = 0; - } -event_free: - kfree(event); - } -} - -/** - * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not). - * - * @ifp: ifp on which setup is taking place or finished. - * @ongoing: p2p device setup in progress (or not). - */ -void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing) -{ - ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing; -} - -/** - * brcmf_fweh_attach() - initialize firmware event handling. - * - * @drvr: driver information object. - */ -void brcmf_fweh_attach(struct brcmf_pub *drvr) -{ - struct brcmf_fweh_info *fweh = &drvr->fweh; - INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker); - spin_lock_init(&fweh->evt_q_lock); - INIT_LIST_HEAD(&fweh->event_q); -} - -/** - * brcmf_fweh_detach() - cleanup firmware event handling. - * - * @drvr: driver information object. - */ -void brcmf_fweh_detach(struct brcmf_pub *drvr) -{ - struct brcmf_fweh_info *fweh = &drvr->fweh; - struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); - s8 eventmask[BRCMF_EVENTING_MASK_LEN]; - - if (ifp) { - /* clear all events */ - memset(eventmask, 0, BRCMF_EVENTING_MASK_LEN); - (void)brcmf_fil_iovar_data_set(ifp, "event_msgs", - eventmask, - BRCMF_EVENTING_MASK_LEN); - } - /* cancel the worker */ - cancel_work_sync(&fweh->event_work); - WARN_ON(!list_empty(&fweh->event_q)); - memset(fweh->evt_handler, 0, sizeof(fweh->evt_handler)); -} - -/** - * brcmf_fweh_register() - register handler for given event code. - * - * @drvr: driver information object. - * @code: event code. - * @handler: handler for the given event code. - */ -int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code, - brcmf_fweh_handler_t handler) -{ - if (drvr->fweh.evt_handler[code]) { - brcmf_err("event code %d already registered\n", code); - return -ENOSPC; - } - drvr->fweh.evt_handler[code] = handler; - brcmf_dbg(TRACE, "event handler registered for %s\n", - brcmf_fweh_event_name(code)); - return 0; -} - -/** - * brcmf_fweh_unregister() - remove handler for given code. - * - * @drvr: driver information object. - * @code: event code. - */ -void brcmf_fweh_unregister(struct brcmf_pub *drvr, - enum brcmf_fweh_event_code code) -{ - brcmf_dbg(TRACE, "event handler cleared for %s\n", - brcmf_fweh_event_name(code)); - drvr->fweh.evt_handler[code] = NULL; -} - -/** - * brcmf_fweh_activate_events() - enables firmware events registered. - * - * @ifp: primary interface object. - */ -int brcmf_fweh_activate_events(struct brcmf_if *ifp) -{ - int i, err; - s8 eventmask[BRCMF_EVENTING_MASK_LEN]; - - for (i = 0; i < BRCMF_E_LAST; i++) { - if (ifp->drvr->fweh.evt_handler[i]) { - brcmf_dbg(EVENT, "enable event %s\n", - brcmf_fweh_event_name(i)); - setbit(eventmask, i); - } - } - - /* want to handle IF event as well */ - brcmf_dbg(EVENT, "enable event IF\n"); - setbit(eventmask, BRCMF_E_IF); - - err = brcmf_fil_iovar_data_set(ifp, "event_msgs", - eventmask, BRCMF_EVENTING_MASK_LEN); - if (err) - brcmf_err("Set event_msgs error (%d)\n", err); - - return err; -} - -/** - * brcmf_fweh_process_event() - process skb as firmware event. - * - * @drvr: driver information object. - * @event_packet: event packet to process. - * - * If the packet buffer contains a firmware event message it will - * dispatch the event to a registered handler (using worker). - */ -void brcmf_fweh_process_event(struct brcmf_pub *drvr, - struct brcmf_event *event_packet) -{ - enum brcmf_fweh_event_code code; - struct brcmf_fweh_info *fweh = &drvr->fweh; - struct brcmf_fweh_queue_item *event; - gfp_t alloc_flag = GFP_KERNEL; - void *data; - u32 datalen; - - /* get event info */ - code = get_unaligned_be32(&event_packet->msg.event_type); - datalen = get_unaligned_be32(&event_packet->msg.datalen); - data = &event_packet[1]; - - if (code >= BRCMF_E_LAST) - return; - - if (code != BRCMF_E_IF && !fweh->evt_handler[code]) - return; - - if (in_interrupt()) - alloc_flag = GFP_ATOMIC; - - event = kzalloc(sizeof(*event) + datalen, alloc_flag); - if (!event) - return; - - event->code = code; - event->ifidx = event_packet->msg.ifidx; - - /* use memcpy to get aligned event message */ - memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg)); - memcpy(event->data, data, datalen); - memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN); - - brcmf_fweh_queue_event(fweh, event); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h deleted file mode 100644 index d9a942842382..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (c) 2012 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 FWEH_H_ -#define FWEH_H_ - -#include -#include -#include -#include - -/* formward declarations */ -struct brcmf_pub; -struct brcmf_if; -struct brcmf_cfg80211_info; -struct brcmf_event; - -/* list of firmware events */ -#define BRCMF_FWEH_EVENT_ENUM_DEFLIST \ - BRCMF_ENUM_DEF(SET_SSID, 0) \ - BRCMF_ENUM_DEF(JOIN, 1) \ - BRCMF_ENUM_DEF(START, 2) \ - BRCMF_ENUM_DEF(AUTH, 3) \ - BRCMF_ENUM_DEF(AUTH_IND, 4) \ - BRCMF_ENUM_DEF(DEAUTH, 5) \ - BRCMF_ENUM_DEF(DEAUTH_IND, 6) \ - BRCMF_ENUM_DEF(ASSOC, 7) \ - BRCMF_ENUM_DEF(ASSOC_IND, 8) \ - BRCMF_ENUM_DEF(REASSOC, 9) \ - BRCMF_ENUM_DEF(REASSOC_IND, 10) \ - BRCMF_ENUM_DEF(DISASSOC, 11) \ - BRCMF_ENUM_DEF(DISASSOC_IND, 12) \ - BRCMF_ENUM_DEF(QUIET_START, 13) \ - BRCMF_ENUM_DEF(QUIET_END, 14) \ - BRCMF_ENUM_DEF(BEACON_RX, 15) \ - BRCMF_ENUM_DEF(LINK, 16) \ - BRCMF_ENUM_DEF(MIC_ERROR, 17) \ - BRCMF_ENUM_DEF(NDIS_LINK, 18) \ - BRCMF_ENUM_DEF(ROAM, 19) \ - BRCMF_ENUM_DEF(TXFAIL, 20) \ - BRCMF_ENUM_DEF(PMKID_CACHE, 21) \ - BRCMF_ENUM_DEF(RETROGRADE_TSF, 22) \ - BRCMF_ENUM_DEF(PRUNE, 23) \ - BRCMF_ENUM_DEF(AUTOAUTH, 24) \ - BRCMF_ENUM_DEF(EAPOL_MSG, 25) \ - BRCMF_ENUM_DEF(SCAN_COMPLETE, 26) \ - BRCMF_ENUM_DEF(ADDTS_IND, 27) \ - BRCMF_ENUM_DEF(DELTS_IND, 28) \ - BRCMF_ENUM_DEF(BCNSENT_IND, 29) \ - BRCMF_ENUM_DEF(BCNRX_MSG, 30) \ - BRCMF_ENUM_DEF(BCNLOST_MSG, 31) \ - BRCMF_ENUM_DEF(ROAM_PREP, 32) \ - BRCMF_ENUM_DEF(PFN_NET_FOUND, 33) \ - BRCMF_ENUM_DEF(PFN_NET_LOST, 34) \ - BRCMF_ENUM_DEF(RESET_COMPLETE, 35) \ - BRCMF_ENUM_DEF(JOIN_START, 36) \ - BRCMF_ENUM_DEF(ROAM_START, 37) \ - BRCMF_ENUM_DEF(ASSOC_START, 38) \ - BRCMF_ENUM_DEF(IBSS_ASSOC, 39) \ - BRCMF_ENUM_DEF(RADIO, 40) \ - BRCMF_ENUM_DEF(PSM_WATCHDOG, 41) \ - BRCMF_ENUM_DEF(PROBREQ_MSG, 44) \ - BRCMF_ENUM_DEF(SCAN_CONFIRM_IND, 45) \ - BRCMF_ENUM_DEF(PSK_SUP, 46) \ - BRCMF_ENUM_DEF(COUNTRY_CODE_CHANGED, 47) \ - BRCMF_ENUM_DEF(EXCEEDED_MEDIUM_TIME, 48) \ - BRCMF_ENUM_DEF(ICV_ERROR, 49) \ - BRCMF_ENUM_DEF(UNICAST_DECODE_ERROR, 50) \ - BRCMF_ENUM_DEF(MULTICAST_DECODE_ERROR, 51) \ - BRCMF_ENUM_DEF(TRACE, 52) \ - BRCMF_ENUM_DEF(IF, 54) \ - BRCMF_ENUM_DEF(P2P_DISC_LISTEN_COMPLETE, 55) \ - BRCMF_ENUM_DEF(RSSI, 56) \ - BRCMF_ENUM_DEF(EXTLOG_MSG, 58) \ - BRCMF_ENUM_DEF(ACTION_FRAME, 59) \ - BRCMF_ENUM_DEF(ACTION_FRAME_COMPLETE, 60) \ - BRCMF_ENUM_DEF(PRE_ASSOC_IND, 61) \ - BRCMF_ENUM_DEF(PRE_REASSOC_IND, 62) \ - BRCMF_ENUM_DEF(CHANNEL_ADOPTED, 63) \ - BRCMF_ENUM_DEF(AP_STARTED, 64) \ - BRCMF_ENUM_DEF(DFS_AP_STOP, 65) \ - BRCMF_ENUM_DEF(DFS_AP_RESUME, 66) \ - BRCMF_ENUM_DEF(ESCAN_RESULT, 69) \ - BRCMF_ENUM_DEF(ACTION_FRAME_OFF_CHAN_COMPLETE, 70) \ - BRCMF_ENUM_DEF(PROBERESP_MSG, 71) \ - BRCMF_ENUM_DEF(P2P_PROBEREQ_MSG, 72) \ - BRCMF_ENUM_DEF(DCS_REQUEST, 73) \ - BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \ - BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \ - BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \ - BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) - -#define BRCMF_ENUM_DEF(id, val) \ - BRCMF_E_##id = (val), - -/* firmware event codes sent by the dongle */ -enum brcmf_fweh_event_code { - BRCMF_FWEH_EVENT_ENUM_DEFLIST - /* this determines event mask length which must match - * minimum length check in device firmware so it is - * hard-coded here. - */ - BRCMF_E_LAST = 139 -}; -#undef BRCMF_ENUM_DEF - -#define BRCMF_EVENTING_MASK_LEN DIV_ROUND_UP(BRCMF_E_LAST, 8) - -/* flags field values in struct brcmf_event_msg */ -#define BRCMF_EVENT_MSG_LINK 0x01 -#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02 -#define BRCMF_EVENT_MSG_GROUP 0x04 - -/* status field values in struct brcmf_event_msg */ -#define BRCMF_E_STATUS_SUCCESS 0 -#define BRCMF_E_STATUS_FAIL 1 -#define BRCMF_E_STATUS_TIMEOUT 2 -#define BRCMF_E_STATUS_NO_NETWORKS 3 -#define BRCMF_E_STATUS_ABORT 4 -#define BRCMF_E_STATUS_NO_ACK 5 -#define BRCMF_E_STATUS_UNSOLICITED 6 -#define BRCMF_E_STATUS_ATTEMPT 7 -#define BRCMF_E_STATUS_PARTIAL 8 -#define BRCMF_E_STATUS_NEWSCAN 9 -#define BRCMF_E_STATUS_NEWASSOC 10 -#define BRCMF_E_STATUS_11HQUIET 11 -#define BRCMF_E_STATUS_SUPPRESS 12 -#define BRCMF_E_STATUS_NOCHANS 13 -#define BRCMF_E_STATUS_CS_ABORT 15 -#define BRCMF_E_STATUS_ERROR 16 - -/* reason field values in struct brcmf_event_msg */ -#define BRCMF_E_REASON_INITIAL_ASSOC 0 -#define BRCMF_E_REASON_LOW_RSSI 1 -#define BRCMF_E_REASON_DEAUTH 2 -#define BRCMF_E_REASON_DISASSOC 3 -#define BRCMF_E_REASON_BCNS_LOST 4 -#define BRCMF_E_REASON_MINTXRATE 9 -#define BRCMF_E_REASON_TXFAIL 10 - -#define BRCMF_E_REASON_LINK_BSSCFG_DIS 4 -#define BRCMF_E_REASON_FAST_ROAM_FAILED 5 -#define BRCMF_E_REASON_DIRECTED_ROAM 6 -#define BRCMF_E_REASON_TSPEC_REJECTED 7 -#define BRCMF_E_REASON_BETTER_AP 8 - -#define BRCMF_E_REASON_TDLS_PEER_DISCOVERED 0 -#define BRCMF_E_REASON_TDLS_PEER_CONNECTED 1 -#define BRCMF_E_REASON_TDLS_PEER_DISCONNECTED 2 - -/* action field values for brcmf_ifevent */ -#define BRCMF_E_IF_ADD 1 -#define BRCMF_E_IF_DEL 2 -#define BRCMF_E_IF_CHANGE 3 - -/* flag field values for brcmf_ifevent */ -#define BRCMF_E_IF_FLAG_NOIF 1 - -/* role field values for brcmf_ifevent */ -#define BRCMF_E_IF_ROLE_STA 0 -#define BRCMF_E_IF_ROLE_AP 1 -#define BRCMF_E_IF_ROLE_WDS 2 -#define BRCMF_E_IF_ROLE_P2P_GO 3 -#define BRCMF_E_IF_ROLE_P2P_CLIENT 4 - -/** - * definitions for event packet validation. - */ -#define BRCMF_EVENT_OUI_OFFSET 19 -#define BRCM_OUI "\x00\x10\x18" -#define DOT11_OUI_LEN 3 -#define BCMILCP_BCM_SUBTYPE_EVENT 1 - - -/** - * struct brcmf_event_msg - firmware event message. - * - * @version: version information. - * @flags: event flags. - * @event_code: firmware event code. - * @status: status information. - * @reason: reason code. - * @auth_type: authentication type. - * @datalen: lenght of event data buffer. - * @addr: ether address. - * @ifname: interface name. - * @ifidx: interface index. - * @bsscfgidx: bsscfg index. - */ -struct brcmf_event_msg { - u16 version; - u16 flags; - u32 event_code; - u32 status; - u32 reason; - s32 auth_type; - u32 datalen; - u8 addr[ETH_ALEN]; - char ifname[IFNAMSIZ]; - u8 ifidx; - u8 bsscfgidx; -}; - -struct brcmf_if_event { - u8 ifidx; - u8 action; - u8 flags; - u8 bssidx; - u8 role; -}; - -typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp, - const struct brcmf_event_msg *evtmsg, - void *data); - -/** - * struct brcmf_fweh_info - firmware event handling information. - * - * @p2pdev_setup_ongoing: P2P device creation in progress. - * @event_work: event worker. - * @evt_q_lock: lock for event queue protection. - * @event_q: event queue. - * @evt_handler: registered event handlers. - */ -struct brcmf_fweh_info { - bool p2pdev_setup_ongoing; - struct work_struct event_work; - spinlock_t evt_q_lock; - struct list_head event_q; - int (*evt_handler[BRCMF_E_LAST])(struct brcmf_if *ifp, - const struct brcmf_event_msg *evtmsg, - void *data); -}; - -void brcmf_fweh_attach(struct brcmf_pub *drvr); -void brcmf_fweh_detach(struct brcmf_pub *drvr); -int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code, - int (*handler)(struct brcmf_if *ifp, - const struct brcmf_event_msg *evtmsg, - void *data)); -void brcmf_fweh_unregister(struct brcmf_pub *drvr, - enum brcmf_fweh_event_code code); -int brcmf_fweh_activate_events(struct brcmf_if *ifp); -void brcmf_fweh_process_event(struct brcmf_pub *drvr, - struct brcmf_event *event_packet); -void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing); - -static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, - struct sk_buff *skb) -{ - struct brcmf_event *event_packet; - u8 *data; - u16 usr_stype; - - /* only process events when protocol matches */ - if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL)) - return; - - /* check for BRCM oui match */ - event_packet = (struct brcmf_event *)skb_mac_header(skb); - data = (u8 *)event_packet; - data += BRCMF_EVENT_OUI_OFFSET; - if (memcmp(BRCM_OUI, data, DOT11_OUI_LEN)) - return; - - /* final match on usr_subtype */ - data += DOT11_OUI_LEN; - usr_stype = get_unaligned_be16(data); - if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT) - return; - - brcmf_fweh_process_event(drvr, event_packet); -} - -#endif /* FWEH_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c deleted file mode 100644 index dcfa0bb149ce..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright (c) 2012 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. - */ - -/* FWIL is the Firmware Interface Layer. In this module the support functions - * are located to set and get variables to and from the firmware. - */ - -#include -#include -#include -#include -#include "core.h" -#include "bus.h" -#include "debug.h" -#include "tracepoint.h" -#include "fwil.h" -#include "proto.h" - - -#define MAX_HEX_DUMP_LEN 64 - -#ifdef DEBUG -static const char * const brcmf_fil_errstr[] = { - "BCME_OK", - "BCME_ERROR", - "BCME_BADARG", - "BCME_BADOPTION", - "BCME_NOTUP", - "BCME_NOTDOWN", - "BCME_NOTAP", - "BCME_NOTSTA", - "BCME_BADKEYIDX", - "BCME_RADIOOFF", - "BCME_NOTBANDLOCKED", - "BCME_NOCLK", - "BCME_BADRATESET", - "BCME_BADBAND", - "BCME_BUFTOOSHORT", - "BCME_BUFTOOLONG", - "BCME_BUSY", - "BCME_NOTASSOCIATED", - "BCME_BADSSIDLEN", - "BCME_OUTOFRANGECHAN", - "BCME_BADCHAN", - "BCME_BADADDR", - "BCME_NORESOURCE", - "BCME_UNSUPPORTED", - "BCME_BADLEN", - "BCME_NOTREADY", - "BCME_EPERM", - "BCME_NOMEM", - "BCME_ASSOCIATED", - "BCME_RANGE", - "BCME_NOTFOUND", - "BCME_WME_NOT_ENABLED", - "BCME_TSPEC_NOTFOUND", - "BCME_ACM_NOTSUPPORTED", - "BCME_NOT_WME_ASSOCIATION", - "BCME_SDIO_ERROR", - "BCME_DONGLE_DOWN", - "BCME_VERSION", - "BCME_TXFAIL", - "BCME_RXFAIL", - "BCME_NODEVICE", - "BCME_NMODE_DISABLED", - "BCME_NONRESIDENT", - "BCME_SCANREJECT", - "BCME_USAGE_ERROR", - "BCME_IOCTL_ERROR", - "BCME_SERIAL_PORT_ERR", - "BCME_DISABLED", - "BCME_DECERR", - "BCME_ENCERR", - "BCME_MICERR", - "BCME_REPLAY", - "BCME_IE_NOTFOUND", -}; - -static const char *brcmf_fil_get_errstr(u32 err) -{ - if (err >= ARRAY_SIZE(brcmf_fil_errstr)) - return "(unknown)"; - - return brcmf_fil_errstr[err]; -} -#else -static const char *brcmf_fil_get_errstr(u32 err) -{ - return ""; -} -#endif /* DEBUG */ - -static s32 -brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) -{ - struct brcmf_pub *drvr = ifp->drvr; - s32 err; - - if (drvr->bus_if->state != BRCMF_BUS_UP) { - brcmf_err("bus is down. we have nothing to do.\n"); - return -EIO; - } - - if (data != NULL) - len = min_t(uint, len, BRCMF_DCMD_MAXLEN); - if (set) - err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd, data, len); - else - err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len); - - if (err >= 0) - return 0; - - brcmf_dbg(FIL, "Failed: %s (%d)\n", - brcmf_fil_get_errstr((u32)(-err)), err); - return -EBADE; -} - -s32 -brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) -{ - s32 err; - - mutex_lock(&ifp->drvr->proto_block); - - brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); - - err = brcmf_fil_cmd_data(ifp, cmd, data, len, true); - mutex_unlock(&ifp->drvr->proto_block); - - return err; -} - -s32 -brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) -{ - s32 err; - - mutex_lock(&ifp->drvr->proto_block); - err = brcmf_fil_cmd_data(ifp, cmd, data, len, false); - - brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); - - mutex_unlock(&ifp->drvr->proto_block); - - return err; -} - - -s32 -brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data) -{ - s32 err; - __le32 data_le = cpu_to_le32(data); - - mutex_lock(&ifp->drvr->proto_block); - brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data); - err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true); - mutex_unlock(&ifp->drvr->proto_block); - - return err; -} - -s32 -brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) -{ - s32 err; - __le32 data_le = cpu_to_le32(*data); - - mutex_lock(&ifp->drvr->proto_block); - err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false); - mutex_unlock(&ifp->drvr->proto_block); - *data = le32_to_cpu(data_le); - brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data); - - return err; -} - -static u32 -brcmf_create_iovar(char *name, const char *data, u32 datalen, - char *buf, u32 buflen) -{ - u32 len; - - len = strlen(name) + 1; - - if ((len + datalen) > buflen) - return 0; - - memcpy(buf, name, len); - - /* append data onto the end of the name string */ - if (data && datalen) - memcpy(&buf[len], data, datalen); - - return len + datalen; -} - - -s32 -brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, - u32 len) -{ - struct brcmf_pub *drvr = ifp->drvr; - s32 err; - u32 buflen; - - mutex_lock(&drvr->proto_block); - - brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); - - buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, - sizeof(drvr->proto_buf)); - if (buflen) { - err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, - buflen, true); - } else { - err = -EPERM; - brcmf_err("Creating iovar failed\n"); - } - - mutex_unlock(&drvr->proto_block); - return err; -} - -s32 -brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, - u32 len) -{ - struct brcmf_pub *drvr = ifp->drvr; - s32 err; - u32 buflen; - - mutex_lock(&drvr->proto_block); - - buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, - sizeof(drvr->proto_buf)); - if (buflen) { - err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, - buflen, false); - if (err == 0) - memcpy(data, drvr->proto_buf, len); - } else { - err = -EPERM; - brcmf_err("Creating iovar failed\n"); - } - - brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); - - mutex_unlock(&drvr->proto_block); - return err; -} - -s32 -brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data) -{ - __le32 data_le = cpu_to_le32(data); - - return brcmf_fil_iovar_data_set(ifp, name, &data_le, sizeof(data_le)); -} - -s32 -brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data) -{ - __le32 data_le = cpu_to_le32(*data); - s32 err; - - err = brcmf_fil_iovar_data_get(ifp, name, &data_le, sizeof(data_le)); - if (err == 0) - *data = le32_to_cpu(data_le); - return err; -} - -static u32 -brcmf_create_bsscfg(s32 bssidx, char *name, char *data, u32 datalen, char *buf, - u32 buflen) -{ - const s8 *prefix = "bsscfg:"; - s8 *p; - u32 prefixlen; - u32 namelen; - u32 iolen; - __le32 bssidx_le; - - if (bssidx == 0) - return brcmf_create_iovar(name, data, datalen, buf, buflen); - - prefixlen = strlen(prefix); - namelen = strlen(name) + 1; /* lengh of iovar name + null */ - iolen = prefixlen + namelen + sizeof(bssidx_le) + datalen; - - if (buflen < iolen) { - brcmf_err("buffer is too short\n"); - return 0; - } - - p = buf; - - /* copy prefix, no null */ - memcpy(p, prefix, prefixlen); - p += prefixlen; - - /* copy iovar name including null */ - memcpy(p, name, namelen); - p += namelen; - - /* bss config index as first data */ - bssidx_le = cpu_to_le32(bssidx); - memcpy(p, &bssidx_le, sizeof(bssidx_le)); - p += sizeof(bssidx_le); - - /* parameter buffer follows */ - if (datalen) - memcpy(p, data, datalen); - - return iolen; -} - -s32 -brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, - void *data, u32 len) -{ - struct brcmf_pub *drvr = ifp->drvr; - s32 err; - u32 buflen; - - mutex_lock(&drvr->proto_block); - - brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, - ifp->bssidx, name, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); - - buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, - drvr->proto_buf, sizeof(drvr->proto_buf)); - if (buflen) { - err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, - buflen, true); - } else { - err = -EPERM; - brcmf_err("Creating bsscfg failed\n"); - } - - mutex_unlock(&drvr->proto_block); - return err; -} - -s32 -brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, - void *data, u32 len) -{ - struct brcmf_pub *drvr = ifp->drvr; - s32 err; - u32 buflen; - - mutex_lock(&drvr->proto_block); - - buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, - drvr->proto_buf, sizeof(drvr->proto_buf)); - if (buflen) { - err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, - buflen, false); - if (err == 0) - memcpy(data, drvr->proto_buf, len); - } else { - err = -EPERM; - brcmf_err("Creating bsscfg failed\n"); - } - brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, - ifp->bssidx, name, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); - - mutex_unlock(&drvr->proto_block); - return err; - -} - -s32 -brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data) -{ - __le32 data_le = cpu_to_le32(data); - - return brcmf_fil_bsscfg_data_set(ifp, name, &data_le, - sizeof(data_le)); -} - -s32 -brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data) -{ - __le32 data_le = cpu_to_le32(*data); - s32 err; - - err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le, - sizeof(data_le)); - if (err == 0) - *data = le32_to_cpu(data_le); - return err; -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h deleted file mode 100644 index b20fc0f82a48..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2012 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 _fwil_h_ -#define _fwil_h_ - -/******************************************************************************* - * Dongle command codes that are interpreted by firmware - ******************************************************************************/ -#define BRCMF_C_GET_VERSION 1 -#define BRCMF_C_UP 2 -#define BRCMF_C_DOWN 3 -#define BRCMF_C_SET_PROMISC 10 -#define BRCMF_C_GET_RATE 12 -#define BRCMF_C_GET_INFRA 19 -#define BRCMF_C_SET_INFRA 20 -#define BRCMF_C_GET_AUTH 21 -#define BRCMF_C_SET_AUTH 22 -#define BRCMF_C_GET_BSSID 23 -#define BRCMF_C_GET_SSID 25 -#define BRCMF_C_SET_SSID 26 -#define BRCMF_C_TERMINATED 28 -#define BRCMF_C_GET_CHANNEL 29 -#define BRCMF_C_SET_CHANNEL 30 -#define BRCMF_C_GET_SRL 31 -#define BRCMF_C_SET_SRL 32 -#define BRCMF_C_GET_LRL 33 -#define BRCMF_C_SET_LRL 34 -#define BRCMF_C_GET_RADIO 37 -#define BRCMF_C_SET_RADIO 38 -#define BRCMF_C_GET_PHYTYPE 39 -#define BRCMF_C_SET_KEY 45 -#define BRCMF_C_GET_REGULATORY 46 -#define BRCMF_C_SET_REGULATORY 47 -#define BRCMF_C_SET_PASSIVE_SCAN 49 -#define BRCMF_C_SCAN 50 -#define BRCMF_C_SCAN_RESULTS 51 -#define BRCMF_C_DISASSOC 52 -#define BRCMF_C_REASSOC 53 -#define BRCMF_C_SET_ROAM_TRIGGER 55 -#define BRCMF_C_SET_ROAM_DELTA 57 -#define BRCMF_C_GET_BCNPRD 75 -#define BRCMF_C_SET_BCNPRD 76 -#define BRCMF_C_GET_DTIMPRD 77 -#define BRCMF_C_SET_DTIMPRD 78 -#define BRCMF_C_SET_COUNTRY 84 -#define BRCMF_C_GET_PM 85 -#define BRCMF_C_SET_PM 86 -#define BRCMF_C_GET_REVINFO 98 -#define BRCMF_C_GET_CURR_RATESET 114 -#define BRCMF_C_GET_AP 117 -#define BRCMF_C_SET_AP 118 -#define BRCMF_C_SET_SCB_AUTHORIZE 121 -#define BRCMF_C_SET_SCB_DEAUTHORIZE 122 -#define BRCMF_C_GET_RSSI 127 -#define BRCMF_C_GET_WSEC 133 -#define BRCMF_C_SET_WSEC 134 -#define BRCMF_C_GET_PHY_NOISE 135 -#define BRCMF_C_GET_BSS_INFO 136 -#define BRCMF_C_GET_BANDLIST 140 -#define BRCMF_C_SET_SCB_TIMEOUT 158 -#define BRCMF_C_GET_ASSOCLIST 159 -#define BRCMF_C_GET_PHYLIST 180 -#define BRCMF_C_SET_SCAN_CHANNEL_TIME 185 -#define BRCMF_C_SET_SCAN_UNASSOC_TIME 187 -#define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201 -#define BRCMF_C_GET_VALID_CHANNELS 217 -#define BRCMF_C_GET_KEY_PRIMARY 235 -#define BRCMF_C_SET_KEY_PRIMARY 236 -#define BRCMF_C_SET_SCAN_PASSIVE_TIME 258 -#define BRCMF_C_GET_VAR 262 -#define BRCMF_C_SET_VAR 263 - -s32 brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); -s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); -s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data); -s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data); - -s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, - u32 len); -s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, - u32 len); -s32 brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data); -s32 brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data); - -s32 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, void *data, - u32 len); -s32 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, void *data, - u32 len); -s32 brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data); -s32 brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data); - -#endif /* _fwil_h_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h deleted file mode 100644 index daa427b46712..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ /dev/null @@ -1,637 +0,0 @@ -/* - * Copyright (c) 2012 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 FWIL_TYPES_H_ -#define FWIL_TYPES_H_ - -#include - - -#define BRCMF_FIL_ACTION_FRAME_SIZE 1800 - -/* ARP Offload feature flags for arp_ol iovar */ -#define BRCMF_ARP_OL_AGENT 0x00000001 -#define BRCMF_ARP_OL_SNOOP 0x00000002 -#define BRCMF_ARP_OL_HOST_AUTO_REPLY 0x00000004 -#define BRCMF_ARP_OL_PEER_AUTO_REPLY 0x00000008 - -#define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */ -#define BRCMF_BSS_RSSI_ON_CHANNEL 0x0002 - -#define BRCMF_STA_WME 0x00000002 /* WMM association */ -#define BRCMF_STA_AUTHE 0x00000008 /* Authenticated */ -#define BRCMF_STA_ASSOC 0x00000010 /* Associated */ -#define BRCMF_STA_AUTHO 0x00000020 /* Authorized */ -#define BRCMF_STA_SCBSTATS 0x00004000 /* Per STA debug stats */ - -/* size of brcmf_scan_params not including variable length array */ -#define BRCMF_SCAN_PARAMS_FIXED_SIZE 64 - -/* masks for channel and ssid count */ -#define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff -#define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16 - -/* primary (ie tx) key */ -#define BRCMF_PRIMARY_KEY (1 << 1) -#define DOT11_BSSTYPE_ANY 2 -#define BRCMF_ESCAN_REQ_VERSION 1 - -#define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */ - -/* OBSS Coex Auto/On/Off */ -#define BRCMF_OBSS_COEX_AUTO (-1) -#define BRCMF_OBSS_COEX_OFF 0 -#define BRCMF_OBSS_COEX_ON 1 - -/* WOWL bits */ -/* Wakeup on Magic packet: */ -#define BRCMF_WOWL_MAGIC (1 << 0) -/* Wakeup on Netpattern */ -#define BRCMF_WOWL_NET (1 << 1) -/* Wakeup on loss-of-link due to Disassoc/Deauth: */ -#define BRCMF_WOWL_DIS (1 << 2) -/* Wakeup on retrograde TSF: */ -#define BRCMF_WOWL_RETR (1 << 3) -/* Wakeup on loss of beacon: */ -#define BRCMF_WOWL_BCN (1 << 4) -/* Wakeup after test: */ -#define BRCMF_WOWL_TST (1 << 5) -/* Wakeup after PTK refresh: */ -#define BRCMF_WOWL_M1 (1 << 6) -/* Wakeup after receipt of EAP-Identity Req: */ -#define BRCMF_WOWL_EAPID (1 << 7) -/* Wakeind via PME(0) or GPIO(1): */ -#define BRCMF_WOWL_PME_GPIO (1 << 8) -/* need tkip phase 1 key to be updated by the driver: */ -#define BRCMF_WOWL_NEEDTKIP1 (1 << 9) -/* enable wakeup if GTK fails: */ -#define BRCMF_WOWL_GTK_FAILURE (1 << 10) -/* support extended magic packets: */ -#define BRCMF_WOWL_EXTMAGPAT (1 << 11) -/* support ARP/NS/keepalive offloading: */ -#define BRCMF_WOWL_ARPOFFLOAD (1 << 12) -/* read protocol version for EAPOL frames: */ -#define BRCMF_WOWL_WPA2 (1 << 13) -/* If the bit is set, use key rotaton: */ -#define BRCMF_WOWL_KEYROT (1 << 14) -/* If the bit is set, frm received was bcast frame: */ -#define BRCMF_WOWL_BCAST (1 << 15) -/* If the bit is set, scan offload is enabled: */ -#define BRCMF_WOWL_SCANOL (1 << 16) -/* Wakeup on tcpkeep alive timeout: */ -#define BRCMF_WOWL_TCPKEEP_TIME (1 << 17) -/* Wakeup on mDNS Conflict Resolution: */ -#define BRCMF_WOWL_MDNS_CONFLICT (1 << 18) -/* Wakeup on mDNS Service Connect: */ -#define BRCMF_WOWL_MDNS_SERVICE (1 << 19) -/* tcp keepalive got data: */ -#define BRCMF_WOWL_TCPKEEP_DATA (1 << 20) -/* Firmware died in wowl mode: */ -#define BRCMF_WOWL_FW_HALT (1 << 21) -/* Enable detection of radio button changes: */ -#define BRCMF_WOWL_ENAB_HWRADIO (1 << 22) -/* Offloads detected MIC failure(s): */ -#define BRCMF_WOWL_MIC_FAIL (1 << 23) -/* Wakeup in Unassociated state (Net/Magic Pattern): */ -#define BRCMF_WOWL_UNASSOC (1 << 24) -/* Wakeup if received matched secured pattern: */ -#define BRCMF_WOWL_SECURE (1 << 25) -/* Link Down indication in WoWL mode: */ -#define BRCMF_WOWL_LINKDOWN (1 << 31) - -#define BRCMF_WOWL_MAXPATTERNS 8 -#define BRCMF_WOWL_MAXPATTERNSIZE 128 - -#define BRCMF_COUNTRY_BUF_SZ 4 -#define BRCMF_ANT_MAX 4 - -#define BRCMF_MAX_ASSOCLIST 128 - -/* join preference types for join_pref iovar */ -enum brcmf_join_pref_types { - BRCMF_JOIN_PREF_RSSI = 1, - BRCMF_JOIN_PREF_WPA, - BRCMF_JOIN_PREF_BAND, - BRCMF_JOIN_PREF_RSSI_DELTA, -}; - -enum brcmf_fil_p2p_if_types { - BRCMF_FIL_P2P_IF_CLIENT, - BRCMF_FIL_P2P_IF_GO, - BRCMF_FIL_P2P_IF_DYNBCN_GO, - BRCMF_FIL_P2P_IF_DEV, -}; - -enum brcmf_wowl_pattern_type { - BRCMF_WOWL_PATTERN_TYPE_BITMAP = 0, - BRCMF_WOWL_PATTERN_TYPE_ARP, - BRCMF_WOWL_PATTERN_TYPE_NA -}; - -struct brcmf_fil_p2p_if_le { - u8 addr[ETH_ALEN]; - __le16 type; - __le16 chspec; -}; - -struct brcmf_fil_chan_info_le { - __le32 hw_channel; - __le32 target_channel; - __le32 scan_channel; -}; - -struct brcmf_fil_action_frame_le { - u8 da[ETH_ALEN]; - __le16 len; - __le32 packet_id; - u8 data[BRCMF_FIL_ACTION_FRAME_SIZE]; -}; - -struct brcmf_fil_af_params_le { - __le32 channel; - __le32 dwell_time; - u8 bssid[ETH_ALEN]; - u8 pad[2]; - struct brcmf_fil_action_frame_le action_frame; -}; - -struct brcmf_fil_bss_enable_le { - __le32 bsscfg_idx; - __le32 enable; -}; - -struct brcmf_fil_bwcap_le { - __le32 band; - __le32 bw_cap; -}; - -/** - * struct tdls_iovar - common structure for tdls iovars. - * - * @ea: ether address of peer station. - * @mode: mode value depending on specific tdls iovar. - * @chanspec: channel specification. - * @pad: unused (for future use). - */ -struct brcmf_tdls_iovar_le { - u8 ea[ETH_ALEN]; /* Station address */ - u8 mode; /* mode: depends on iovar */ - __le16 chanspec; - __le32 pad; /* future */ -}; - -enum brcmf_tdls_manual_ep_ops { - BRCMF_TDLS_MANUAL_EP_CREATE = 1, - BRCMF_TDLS_MANUAL_EP_DELETE = 3, - BRCMF_TDLS_MANUAL_EP_DISCOVERY = 6 -}; - -/* Pattern matching filter. Specifies an offset within received packets to - * start matching, the pattern to match, the size of the pattern, and a bitmask - * that indicates which bits within the pattern should be matched. - */ -struct brcmf_pkt_filter_pattern_le { - /* - * Offset within received packet to start pattern matching. - * Offset '0' is the first byte of the ethernet header. - */ - __le32 offset; - /* Size of the pattern. Bitmask must be the same size.*/ - __le32 size_bytes; - /* - * Variable length mask and pattern data. mask starts at offset 0. - * Pattern immediately follows mask. - */ - u8 mask_and_pattern[1]; -}; - -/* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */ -struct brcmf_pkt_filter_le { - __le32 id; /* Unique filter id, specified by app. */ - __le32 type; /* Filter type (WL_PKT_FILTER_TYPE_xxx). */ - __le32 negate_match; /* Negate the result of filter matches */ - union { /* Filter definitions */ - struct brcmf_pkt_filter_pattern_le pattern; /* Filter pattern */ - } u; -}; - -/* IOVAR "pkt_filter_enable" parameter. */ -struct brcmf_pkt_filter_enable_le { - __le32 id; /* Unique filter id */ - __le32 enable; /* Enable/disable bool */ -}; - -/* BSS info structure - * Applications MUST CHECK ie_offset field and length field to access IEs and - * next bss_info structure in a vector (in struct brcmf_scan_results) - */ -struct brcmf_bss_info_le { - __le32 version; /* version field */ - __le32 length; /* byte length of data in this record, - * starting at version and including IEs - */ - u8 BSSID[ETH_ALEN]; - __le16 beacon_period; /* units are Kusec */ - __le16 capability; /* Capability information */ - u8 SSID_len; - u8 SSID[32]; - struct { - __le32 count; /* # rates in this set */ - u8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */ - } rateset; /* supported rates */ - __le16 chanspec; /* chanspec for bss */ - __le16 atim_window; /* units are Kusec */ - u8 dtim_period; /* DTIM period */ - __le16 RSSI; /* receive signal strength (in dBm) */ - s8 phy_noise; /* noise (in dBm) */ - - u8 n_cap; /* BSS is 802.11N Capable */ - /* 802.11N BSS Capabilities (based on HT_CAP_*): */ - __le32 nbss_cap; - u8 ctl_ch; /* 802.11N BSS control channel number */ - __le32 reserved32[1]; /* Reserved for expansion of BSS properties */ - u8 flags; /* flags */ - u8 reserved[3]; /* Reserved for expansion of BSS properties */ - u8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */ - - __le16 ie_offset; /* offset at which IEs start, from beginning */ - __le32 ie_length; /* byte length of Information Elements */ - __le16 SNR; /* average SNR of during frame reception */ - /* Add new fields here */ - /* variable length Information Elements */ -}; - -struct brcm_rateset_le { - /* # rates in this set */ - __le32 count; - /* rates in 500kbps units w/hi bit set if basic */ - u8 rates[BRCMF_MAXRATES_IN_SET]; -}; - -struct brcmf_ssid { - u32 SSID_len; - unsigned char SSID[32]; -}; - -struct brcmf_ssid_le { - __le32 SSID_len; - unsigned char SSID[32]; -}; - -struct brcmf_scan_params_le { - struct brcmf_ssid_le ssid_le; /* default: {0, ""} */ - u8 bssid[ETH_ALEN]; /* default: bcast */ - s8 bss_type; /* default: any, - * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT - */ - u8 scan_type; /* flags, 0 use default */ - __le32 nprobes; /* -1 use default, number of probes per channel */ - __le32 active_time; /* -1 use default, dwell time per channel for - * active scanning - */ - __le32 passive_time; /* -1 use default, dwell time per channel - * for passive scanning - */ - __le32 home_time; /* -1 use default, dwell time for the - * home channel between channel scans - */ - __le32 channel_num; /* count of channels and ssids that follow - * - * low half is count of channels in - * channel_list, 0 means default (use all - * available channels) - * - * high half is entries in struct brcmf_ssid - * array that follows channel_list, aligned for - * s32 (4 bytes) meaning an odd channel count - * implies a 2-byte pad between end of - * channel_list and first ssid - * - * if ssid count is zero, single ssid in the - * fixed parameter portion is assumed, otherwise - * ssid in the fixed portion is ignored - */ - __le16 channel_list[1]; /* list of chanspecs */ -}; - -struct brcmf_scan_results { - u32 buflen; - u32 version; - u32 count; - struct brcmf_bss_info_le bss_info_le[]; -}; - -struct brcmf_escan_params_le { - __le32 version; - __le16 action; - __le16 sync_id; - struct brcmf_scan_params_le params_le; -}; - -struct brcmf_escan_result_le { - __le32 buflen; - __le32 version; - __le16 sync_id; - __le16 bss_count; - struct brcmf_bss_info_le bss_info_le; -}; - -#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(struct brcmf_escan_result_le) - \ - sizeof(struct brcmf_bss_info_le)) - -/* used for association with a specific BSSID and chanspec list */ -struct brcmf_assoc_params_le { - /* 00:00:00:00:00:00: broadcast scan */ - u8 bssid[ETH_ALEN]; - /* 0: all available channels, otherwise count of chanspecs in - * chanspec_list */ - __le32 chanspec_num; - /* list of chanspecs */ - __le16 chanspec_list[1]; -}; - -/** - * struct join_pref params - parameters for preferred join selection. - * - * @type: preference type (see enum brcmf_join_pref_types). - * @len: length of bytes following (currently always 2). - * @rssi_gain: signal gain for selection (only when @type is RSSI_DELTA). - * @band: band to which selection preference applies. - * This is used if @type is BAND or RSSI_DELTA. - */ -struct brcmf_join_pref_params { - u8 type; - u8 len; - u8 rssi_gain; - u8 band; -}; - -/* used for join with or without a specific bssid and channel list */ -struct brcmf_join_params { - struct brcmf_ssid_le ssid_le; - struct brcmf_assoc_params_le params_le; -}; - -/* scan params for extended join */ -struct brcmf_join_scan_params_le { - u8 scan_type; /* 0 use default, active or passive scan */ - __le32 nprobes; /* -1 use default, nr of probes per channel */ - __le32 active_time; /* -1 use default, dwell time per channel for - * active scanning - */ - __le32 passive_time; /* -1 use default, dwell time per channel - * for passive scanning - */ - __le32 home_time; /* -1 use default, dwell time for the home - * channel between channel scans - */ -}; - -/* extended join params */ -struct brcmf_ext_join_params_le { - struct brcmf_ssid_le ssid_le; /* {0, ""}: wildcard scan */ - struct brcmf_join_scan_params_le scan_le; - struct brcmf_assoc_params_le assoc_le; -}; - -struct brcmf_wsec_key { - u32 index; /* key index */ - u32 len; /* key length */ - u8 data[WLAN_MAX_KEY_LEN]; /* key data */ - u32 pad_1[18]; - u32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ - u32 flags; /* misc flags */ - u32 pad_2[3]; - u32 iv_initialized; /* has IV been initialized already? */ - u32 pad_3; - /* Rx IV */ - struct { - u32 hi; /* upper 32 bits of IV */ - u16 lo; /* lower 16 bits of IV */ - } rxiv; - u32 pad_4[2]; - u8 ea[ETH_ALEN]; /* per station */ -}; - -/* - * dongle requires same struct as above but with fields in little endian order - */ -struct brcmf_wsec_key_le { - __le32 index; /* key index */ - __le32 len; /* key length */ - u8 data[WLAN_MAX_KEY_LEN]; /* key data */ - __le32 pad_1[18]; - __le32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ - __le32 flags; /* misc flags */ - __le32 pad_2[3]; - __le32 iv_initialized; /* has IV been initialized already? */ - __le32 pad_3; - /* Rx IV */ - struct { - __le32 hi; /* upper 32 bits of IV */ - __le16 lo; /* lower 16 bits of IV */ - } rxiv; - __le32 pad_4[2]; - u8 ea[ETH_ALEN]; /* per station */ -}; - -/* Used to get specific STA parameters */ -struct brcmf_scb_val_le { - __le32 val; - u8 ea[ETH_ALEN]; -}; - -/* channel encoding */ -struct brcmf_channel_info_le { - __le32 hw_channel; - __le32 target_channel; - __le32 scan_channel; -}; - -struct brcmf_sta_info_le { - __le16 ver; /* version of this struct */ - __le16 len; /* length in bytes of this structure */ - __le16 cap; /* sta's advertised capabilities */ - __le32 flags; /* flags defined below */ - __le32 idle; /* time since data pkt rx'd from sta */ - u8 ea[ETH_ALEN]; /* Station address */ - __le32 count; /* # rates in this set */ - u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */ - /* w/hi bit set if basic */ - __le32 in; /* seconds elapsed since associated */ - __le32 listen_interval_inms; /* Min Listen interval in ms for STA */ - __le32 tx_pkts; /* # of packets transmitted */ - __le32 tx_failures; /* # of packets failed */ - __le32 rx_ucast_pkts; /* # of unicast packets received */ - __le32 rx_mcast_pkts; /* # of multicast packets received */ - __le32 tx_rate; /* Rate of last successful tx frame */ - __le32 rx_rate; /* Rate of last successful rx frame */ - __le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */ - __le32 rx_decrypt_failures; /* # of packet decrypted failed */ - __le32 tx_tot_pkts; /* # of tx pkts (ucast + mcast) */ - __le32 rx_tot_pkts; /* # of data packets recvd (uni + mcast) */ - __le32 tx_mcast_pkts; /* # of mcast pkts txed */ - __le64 tx_tot_bytes; /* data bytes txed (ucast + mcast) */ - __le64 rx_tot_bytes; /* data bytes recvd (ucast + mcast) */ - __le64 tx_ucast_bytes; /* data bytes txed (ucast) */ - __le64 tx_mcast_bytes; /* # data bytes txed (mcast) */ - __le64 rx_ucast_bytes; /* data bytes recvd (ucast) */ - __le64 rx_mcast_bytes; /* data bytes recvd (mcast) */ - s8 rssi[BRCMF_ANT_MAX]; /* per antenna rssi */ - s8 nf[BRCMF_ANT_MAX]; /* per antenna noise floor */ - __le16 aid; /* association ID */ - __le16 ht_capabilities; /* advertised ht caps */ - __le16 vht_flags; /* converted vht flags */ - __le32 tx_pkts_retry_cnt; /* # of frames where a retry was - * exhausted. - */ - __le32 tx_pkts_retry_exhausted; /* # of user frames where a retry - * was exhausted - */ - s8 rx_lastpkt_rssi[BRCMF_ANT_MAX]; /* Per antenna RSSI of last - * received data frame. - */ - /* TX WLAN retry/failure statistics: - * Separated for host requested frames and locally generated frames. - * Include unicast frame only where the retries/failures can be counted. - */ - __le32 tx_pkts_total; /* # user frames sent successfully */ - __le32 tx_pkts_retries; /* # user frames retries */ - __le32 tx_pkts_fw_total; /* # FW generated sent successfully */ - __le32 tx_pkts_fw_retries; /* # retries for FW generated frames */ - __le32 tx_pkts_fw_retry_exhausted; /* # FW generated where a retry - * was exhausted - */ - __le32 rx_pkts_retried; /* # rx with retry bit set */ - __le32 tx_rate_fallback; /* lowest fallback TX rate */ -}; - -struct brcmf_chanspec_list { - __le32 count; /* # of entries */ - __le32 element[1]; /* variable length uint32 list */ -}; - -/* - * WLC_E_PROBRESP_MSG - * WLC_E_P2P_PROBREQ_MSG - * WLC_E_ACTION_FRAME_RX - */ -struct brcmf_rx_mgmt_data { - __be16 version; - __be16 chanspec; - __be32 rssi; - __be32 mactime; - __be32 rate; -}; - -/** - * struct brcmf_fil_wowl_pattern_le - wowl pattern configuration struct. - * - * @cmd: "add", "del" or "clr". - * @masksize: Size of the mask in #of bytes - * @offset: Pattern byte offset in packet - * @patternoffset: Offset of start of pattern. Starting from field masksize. - * @patternsize: Size of the pattern itself in #of bytes - * @id: id - * @reasonsize: Size of the wakeup reason code - * @type: Type of pattern (enum brcmf_wowl_pattern_type) - */ -struct brcmf_fil_wowl_pattern_le { - u8 cmd[4]; - __le32 masksize; - __le32 offset; - __le32 patternoffset; - __le32 patternsize; - __le32 id; - __le32 reasonsize; - __le32 type; - /* u8 mask[] - Mask follows the structure above */ - /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */ -}; - -struct brcmf_mbss_ssid_le { - __le32 bsscfgidx; - __le32 SSID_len; - unsigned char SSID[32]; -}; - -/** - * struct brcmf_fil_country_le - country configuration structure. - * - * @country_abbrev: null-terminated country code used in the country IE. - * @rev: revision specifier for ccode. on set, -1 indicates unspecified. - * @ccode: null-terminated built-in country code. - */ -struct brcmf_fil_country_le { - char country_abbrev[BRCMF_COUNTRY_BUF_SZ]; - __le32 rev; - char ccode[BRCMF_COUNTRY_BUF_SZ]; -}; - -/** - * struct brcmf_rev_info_le - device revision info. - * - * @vendorid: PCI vendor id. - * @deviceid: device id of chip. - * @radiorev: radio revision. - * @chiprev: chip revision. - * @corerev: core revision. - * @boardid: board identifier (usu. PCI sub-device id). - * @boardvendor: board vendor (usu. PCI sub-vendor id). - * @boardrev: board revision. - * @driverrev: driver version. - * @ucoderev: microcode version. - * @bus: bus type. - * @chipnum: chip number. - * @phytype: phy type. - * @phyrev: phy revision. - * @anarev: anacore rev. - * @chippkg: chip package info. - * @nvramrev: nvram revision number. - */ -struct brcmf_rev_info_le { - __le32 vendorid; - __le32 deviceid; - __le32 radiorev; - __le32 chiprev; - __le32 corerev; - __le32 boardid; - __le32 boardvendor; - __le32 boardrev; - __le32 driverrev; - __le32 ucoderev; - __le32 bus; - __le32 chipnum; - __le32 phytype; - __le32 phyrev; - __le32 anarev; - __le32 chippkg; - __le32 nvramrev; -}; - -/** - * struct brcmf_assoclist_le - request assoc list. - * - * @count: indicates number of stations. - * @mac: MAC addresses of stations. - */ -struct brcmf_assoclist_le { - __le32 count; - u8 mac[BRCMF_MAX_ASSOCLIST][ETH_ALEN]; -}; - -#endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c deleted file mode 100644 index 086cac3f86d6..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ /dev/null @@ -1,2271 +0,0 @@ -/* - * Copyright (c) 2010 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include "core.h" -#include "debug.h" -#include "bus.h" -#include "fwil.h" -#include "fwil_types.h" -#include "fweh.h" -#include "fwsignal.h" -#include "p2p.h" -#include "cfg80211.h" -#include "proto.h" - -/** - * DOC: Firmware Signalling - * - * Firmware can send signals to host and vice versa, which are passed in the - * data packets using TLV based header. This signalling layer is on top of the - * BDC bus protocol layer. - */ - -/* - * single definition for firmware-driver flow control tlv's. - * - * each tlv is specified by BRCMF_FWS_TLV_DEF(name, ID, length). - * A length value 0 indicates variable length tlv. - */ -#define BRCMF_FWS_TLV_DEFLIST \ - BRCMF_FWS_TLV_DEF(MAC_OPEN, 1, 1) \ - BRCMF_FWS_TLV_DEF(MAC_CLOSE, 2, 1) \ - BRCMF_FWS_TLV_DEF(MAC_REQUEST_CREDIT, 3, 2) \ - BRCMF_FWS_TLV_DEF(TXSTATUS, 4, 4) \ - BRCMF_FWS_TLV_DEF(PKTTAG, 5, 4) \ - BRCMF_FWS_TLV_DEF(MACDESC_ADD, 6, 8) \ - BRCMF_FWS_TLV_DEF(MACDESC_DEL, 7, 8) \ - BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \ - BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \ - BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \ - BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 6) \ - BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \ - BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \ - BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \ - BRCMF_FWS_TLV_DEF(TRANS_ID, 18, 6) \ - BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \ - BRCMF_FWS_TLV_DEF(FILLER, 255, 0) - -/* - * enum brcmf_fws_tlv_type - definition of tlv identifiers. - */ -#define BRCMF_FWS_TLV_DEF(name, id, len) \ - BRCMF_FWS_TYPE_ ## name = id, -enum brcmf_fws_tlv_type { - BRCMF_FWS_TLV_DEFLIST - BRCMF_FWS_TYPE_INVALID -}; -#undef BRCMF_FWS_TLV_DEF - -/* - * enum brcmf_fws_tlv_len - definition of tlv lengths. - */ -#define BRCMF_FWS_TLV_DEF(name, id, len) \ - BRCMF_FWS_TYPE_ ## name ## _LEN = (len), -enum brcmf_fws_tlv_len { - BRCMF_FWS_TLV_DEFLIST -}; -#undef BRCMF_FWS_TLV_DEF - -#ifdef DEBUG -/* - * brcmf_fws_tlv_names - array of tlv names. - */ -#define BRCMF_FWS_TLV_DEF(name, id, len) \ - { id, #name }, -static struct { - enum brcmf_fws_tlv_type id; - const char *name; -} brcmf_fws_tlv_names[] = { - BRCMF_FWS_TLV_DEFLIST -}; -#undef BRCMF_FWS_TLV_DEF - - -static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++) - if (brcmf_fws_tlv_names[i].id == id) - return brcmf_fws_tlv_names[i].name; - - return "INVALID"; -} -#else -static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) -{ - return "NODEBUG"; -} -#endif /* DEBUG */ - -/* - * The PKTTAG tlv has additional bytes when firmware-signalling - * mode has REUSESEQ flag set. - */ -#define BRCMF_FWS_TYPE_SEQ_LEN 2 - -/* - * flags used to enable tlv signalling from firmware. - */ -#define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001 -#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002 -#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004 -#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008 -#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010 -#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 -#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040 - -#define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32 -#define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff - -#define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0 -#define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1 -#define BRCMF_FWS_FLOWCONTROL_HIWATER 128 -#define BRCMF_FWS_FLOWCONTROL_LOWATER 64 - -#define BRCMF_FWS_PSQ_PREC_COUNT ((BRCMF_FWS_FIFO_COUNT + 1) * 2) -#define BRCMF_FWS_PSQ_LEN 256 - -#define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01 -#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02 - -#define BRCMF_FWS_RET_OK_NOSCHEDULE 0 -#define BRCMF_FWS_RET_OK_SCHEDULE 1 - -#define BRCMF_FWS_MODE_REUSESEQ_SHIFT 3 /* seq reuse */ -#define BRCMF_FWS_MODE_SET_REUSESEQ(x, val) ((x) = \ - ((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \ - (((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) -#define BRCMF_FWS_MODE_GET_REUSESEQ(x) \ - (((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1) - -/** - * enum brcmf_fws_skb_state - indicates processing state of skb. - * - * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver. - * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue. - * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware. - * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info. - */ -enum brcmf_fws_skb_state { - BRCMF_FWS_SKBSTATE_NEW, - BRCMF_FWS_SKBSTATE_DELAYED, - BRCMF_FWS_SKBSTATE_SUPPRESSED, - BRCMF_FWS_SKBSTATE_TIM -}; - -/** - * struct brcmf_skbuff_cb - control buffer associated with skbuff. - * - * @bus_flags: 2 bytes reserved for bus specific parameters - * @if_flags: holds interface index and packet related flags. - * @htod: host to device packet identifier (used in PKTTAG tlv). - * @htod_seq: this 16-bit is original seq number for every suppress packet. - * @state: transmit state of the packet. - * @mac: descriptor related to destination for this packet. - * - * This information is stored in control buffer struct sk_buff::cb, which - * provides 48 bytes of storage so this structure should not exceed that. - */ -struct brcmf_skbuff_cb { - u16 bus_flags; - u16 if_flags; - u32 htod; - u16 htod_seq; - enum brcmf_fws_skb_state state; - struct brcmf_fws_mac_descriptor *mac; -}; - -/* - * macro casting skbuff control buffer to struct brcmf_skbuff_cb. - */ -#define brcmf_skbcb(skb) ((struct brcmf_skbuff_cb *)((skb)->cb)) - -/* - * sk_buff control if flags - * - * b[11] - packet sent upon firmware request. - * b[10] - packet only contains signalling data. - * b[9] - packet is a tx packet. - * b[8] - packet used requested credit - * b[7] - interface in AP mode. - * b[3:0] - interface index. - */ -#define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800 -#define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT 11 -#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK 0x0400 -#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT 10 -#define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK 0x0200 -#define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT 9 -#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK 0x0100 -#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT 8 -#define BRCMF_SKB_IF_FLAGS_IF_AP_MASK 0x0080 -#define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT 7 -#define BRCMF_SKB_IF_FLAGS_INDEX_MASK 0x000f -#define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT 0 - -#define brcmf_skb_if_flags_set_field(skb, field, value) \ - brcmu_maskset16(&(brcmf_skbcb(skb)->if_flags), \ - BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \ - BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT, (value)) -#define brcmf_skb_if_flags_get_field(skb, field) \ - brcmu_maskget16(brcmf_skbcb(skb)->if_flags, \ - BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \ - BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT) - -/* - * sk_buff control packet identifier - * - * 32-bit packet identifier used in PKTTAG tlv from host to dongle. - * - * - Generated at the host (e.g. dhd) - * - Seen as a generic sequence number by firmware except for the flags field. - * - * Generation : b[31] => generation number for this packet [host->fw] - * OR, current generation number [fw->host] - * Flags : b[30:27] => command, status flags - * FIFO-AC : b[26:24] => AC-FIFO id - * h-slot : b[23:8] => hanger-slot - * freerun : b[7:0] => A free running counter - */ -#define BRCMF_SKB_HTOD_TAG_GENERATION_MASK 0x80000000 -#define BRCMF_SKB_HTOD_TAG_GENERATION_SHIFT 31 -#define BRCMF_SKB_HTOD_TAG_FLAGS_MASK 0x78000000 -#define BRCMF_SKB_HTOD_TAG_FLAGS_SHIFT 27 -#define BRCMF_SKB_HTOD_TAG_FIFO_MASK 0x07000000 -#define BRCMF_SKB_HTOD_TAG_FIFO_SHIFT 24 -#define BRCMF_SKB_HTOD_TAG_HSLOT_MASK 0x00ffff00 -#define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT 8 -#define BRCMF_SKB_HTOD_TAG_FREERUN_MASK 0x000000ff -#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT 0 - -#define brcmf_skb_htod_tag_set_field(skb, field, value) \ - brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \ - BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \ - BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT, (value)) -#define brcmf_skb_htod_tag_get_field(skb, field) \ - brcmu_maskget32(brcmf_skbcb(skb)->htod, \ - BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \ - BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT) - -#define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK 0x2000 -#define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT 13 -#define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK 0x1000 -#define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT 12 -#define BRCMF_SKB_HTOD_SEQ_NR_MASK 0x0fff -#define BRCMF_SKB_HTOD_SEQ_NR_SHIFT 0 - -#define brcmf_skb_htod_seq_set_field(skb, field, value) \ - brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \ - BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \ - BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value)) -#define brcmf_skb_htod_seq_get_field(skb, field) \ - brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \ - BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \ - BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT) - -#define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000 -#define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31 -#define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000 -#define BRCMF_FWS_TXSTAT_FLAGS_SHIFT 27 -#define BRCMF_FWS_TXSTAT_FIFO_MASK 0x07000000 -#define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24 -#define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00 -#define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8 -#define BRCMF_FWS_TXSTAT_FREERUN_MASK 0x000000FF -#define BRCMF_FWS_TXSTAT_FREERUN_SHIFT 0 - -#define brcmf_txstatus_get_field(txs, field) \ - brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \ - BRCMF_FWS_TXSTAT_ ## field ## _SHIFT) - -/* How long to defer borrowing in jiffies */ -#define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10) - -/** - * enum brcmf_fws_fifo - fifo indices used by dongle firmware. - * - * @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background. - * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic. - * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic. - * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic. - * @BRCMF_FWS_FIFO_AC_VO: fifo for voice traffic. - * @BRCMF_FWS_FIFO_BCMC: fifo for broadcast/multicast (AP only). - * @BRCMF_FWS_FIFO_ATIM: fifo for ATIM (AP only). - * @BRCMF_FWS_FIFO_COUNT: number of fifos. - */ -enum brcmf_fws_fifo { - BRCMF_FWS_FIFO_FIRST, - BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST, - BRCMF_FWS_FIFO_AC_BE, - BRCMF_FWS_FIFO_AC_VI, - BRCMF_FWS_FIFO_AC_VO, - BRCMF_FWS_FIFO_BCMC, - BRCMF_FWS_FIFO_ATIM, - BRCMF_FWS_FIFO_COUNT -}; - -/** - * enum brcmf_fws_txstatus - txstatus flag values. - * - * @BRCMF_FWS_TXSTATUS_DISCARD: - * host is free to discard the packet. - * @BRCMF_FWS_TXSTATUS_CORE_SUPPRESS: - * 802.11 core suppressed the packet. - * @BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS: - * firmware suppress the packet as device is already in PS mode. - * @BRCMF_FWS_TXSTATUS_FW_TOSSED: - * firmware tossed the packet. - * @BRCMF_FWS_TXSTATUS_HOST_TOSSED: - * host tossed the packet. - */ -enum brcmf_fws_txstatus { - BRCMF_FWS_TXSTATUS_DISCARD, - BRCMF_FWS_TXSTATUS_CORE_SUPPRESS, - BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS, - BRCMF_FWS_TXSTATUS_FW_TOSSED, - BRCMF_FWS_TXSTATUS_HOST_TOSSED -}; - -enum brcmf_fws_fcmode { - BRCMF_FWS_FCMODE_NONE, - BRCMF_FWS_FCMODE_IMPLIED_CREDIT, - BRCMF_FWS_FCMODE_EXPLICIT_CREDIT -}; - -enum brcmf_fws_mac_desc_state { - BRCMF_FWS_STATE_OPEN = 1, - BRCMF_FWS_STATE_CLOSE -}; - -/** - * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface - * - * @occupied: slot is in use. - * @mac_handle: handle for mac entry determined by firmware. - * @interface_id: interface index. - * @state: current state. - * @suppressed: mac entry is suppressed. - * @generation: generation bit. - * @ac_bitmap: ac queue bitmap. - * @requested_credit: credits requested by firmware. - * @ea: ethernet address. - * @seq: per-node free-running sequence. - * @psq: power-save queue. - * @transit_count: packet in transit to firmware. - */ -struct brcmf_fws_mac_descriptor { - char name[16]; - u8 occupied; - u8 mac_handle; - u8 interface_id; - u8 state; - bool suppressed; - u8 generation; - u8 ac_bitmap; - u8 requested_credit; - u8 requested_packet; - u8 ea[ETH_ALEN]; - u8 seq[BRCMF_FWS_FIFO_COUNT]; - struct pktq psq; - int transit_count; - int suppr_transit_count; - bool send_tim_signal; - u8 traffic_pending_bmp; - u8 traffic_lastreported_bmp; -}; - -#define BRCMF_FWS_HANGER_MAXITEMS 1024 - -/** - * enum brcmf_fws_hanger_item_state - state of hanger item. - * - * @BRCMF_FWS_HANGER_ITEM_STATE_FREE: item is free for use. - * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE: item is in use. - * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed. - */ -enum brcmf_fws_hanger_item_state { - BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1, - BRCMF_FWS_HANGER_ITEM_STATE_INUSE, - BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED -}; - - -/** - * struct brcmf_fws_hanger_item - single entry for tx pending packet. - * - * @state: entry is either free or occupied. - * @pkt: packet itself. - */ -struct brcmf_fws_hanger_item { - enum brcmf_fws_hanger_item_state state; - struct sk_buff *pkt; -}; - -/** - * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus. - * - * @pushed: packets pushed to await txstatus. - * @popped: packets popped upon handling txstatus. - * @failed_to_push: packets that could not be pushed. - * @failed_to_pop: packets that could not be popped. - * @failed_slotfind: packets for which failed to find an entry. - * @slot_pos: last returned item index for a free entry. - * @items: array of hanger items. - */ -struct brcmf_fws_hanger { - u32 pushed; - u32 popped; - u32 failed_to_push; - u32 failed_to_pop; - u32 failed_slotfind; - u32 slot_pos; - struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS]; -}; - -struct brcmf_fws_macdesc_table { - struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE]; - struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS]; - struct brcmf_fws_mac_descriptor other; -}; - -struct brcmf_fws_stats { - u32 tlv_parse_failed; - u32 tlv_invalid_type; - u32 header_only_pkt; - u32 header_pulls; - u32 pkt2bus; - u32 send_pkts[5]; - u32 requested_sent[5]; - u32 generic_error; - u32 mac_update_failed; - u32 mac_ps_update_failed; - u32 if_update_failed; - u32 packet_request_failed; - u32 credit_request_failed; - u32 rollback_success; - u32 rollback_failed; - u32 delayq_full_error; - u32 supprq_full_error; - u32 txs_indicate; - u32 txs_discard; - u32 txs_supp_core; - u32 txs_supp_ps; - u32 txs_tossed; - u32 txs_host_tossed; - u32 bus_flow_block; - u32 fws_flow_block; -}; - -struct brcmf_fws_info { - struct brcmf_pub *drvr; - spinlock_t spinlock; - ulong flags; - struct brcmf_fws_stats stats; - struct brcmf_fws_hanger hanger; - enum brcmf_fws_fcmode fcmode; - bool fw_signals; - bool bcmc_credit_check; - struct brcmf_fws_macdesc_table desc; - struct workqueue_struct *fws_wq; - struct work_struct fws_dequeue_work; - u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT]; - int fifo_credit[BRCMF_FWS_FIFO_COUNT]; - int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1]; - int deq_node_pos[BRCMF_FWS_FIFO_COUNT]; - u32 fifo_credit_map; - u32 fifo_delay_map; - unsigned long borrow_defer_timestamp; - bool bus_flow_blocked; - bool creditmap_received; - u8 mode; - bool avoid_queueing; -}; - -/* - * brcmf_fws_prio2fifo - mapping from 802.1d priority to firmware fifo index. - */ -static const int brcmf_fws_prio2fifo[] = { - BRCMF_FWS_FIFO_AC_BE, - BRCMF_FWS_FIFO_AC_BK, - BRCMF_FWS_FIFO_AC_BK, - BRCMF_FWS_FIFO_AC_BE, - BRCMF_FWS_FIFO_AC_VI, - BRCMF_FWS_FIFO_AC_VI, - BRCMF_FWS_FIFO_AC_VO, - BRCMF_FWS_FIFO_AC_VO -}; - -static int fcmode; -module_param(fcmode, int, S_IRUSR); -MODULE_PARM_DESC(fcmode, "mode of firmware signalled flow control"); - -#define BRCMF_FWS_TLV_DEF(name, id, len) \ - case BRCMF_FWS_TYPE_ ## name: \ - return len; - -/** - * brcmf_fws_get_tlv_len() - returns defined length for given tlv id. - * - * @fws: firmware-signalling information. - * @id: identifier of the TLV. - * - * Return: the specified length for the given TLV; Otherwise -EINVAL. - */ -static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws, - enum brcmf_fws_tlv_type id) -{ - switch (id) { - BRCMF_FWS_TLV_DEFLIST - default: - fws->stats.tlv_invalid_type++; - break; - } - return -EINVAL; -} -#undef BRCMF_FWS_TLV_DEF - -static void brcmf_fws_lock(struct brcmf_fws_info *fws) - __acquires(&fws->spinlock) -{ - spin_lock_irqsave(&fws->spinlock, fws->flags); -} - -static void brcmf_fws_unlock(struct brcmf_fws_info *fws) - __releases(&fws->spinlock) -{ - spin_unlock_irqrestore(&fws->spinlock, fws->flags); -} - -static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg) -{ - u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); - return ifidx == *(int *)arg; -} - -static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, - int ifidx) -{ - bool (*matchfn)(struct sk_buff *, void *) = NULL; - struct sk_buff *skb; - int prec; - - if (ifidx != -1) - matchfn = brcmf_fws_ifidx_match; - for (prec = 0; prec < q->num_prec; prec++) { - skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); - while (skb) { - brcmu_pkt_buf_free_skb(skb); - skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); - } - } -} - -static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger) -{ - int i; - - memset(hanger, 0, sizeof(*hanger)); - for (i = 0; i < ARRAY_SIZE(hanger->items); i++) - hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; -} - -static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) -{ - u32 i; - - i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS; - - while (i != h->slot_pos) { - if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { - h->slot_pos = i; - goto done; - } - i++; - if (i == BRCMF_FWS_HANGER_MAXITEMS) - i = 0; - } - brcmf_err("all slots occupied\n"); - h->failed_slotfind++; - i = BRCMF_FWS_HANGER_MAXITEMS; -done: - return i; -} - -static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, - struct sk_buff *pkt, u32 slot_id) -{ - if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) - return -ENOENT; - - if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) { - brcmf_err("slot is not free\n"); - h->failed_to_push++; - return -EINVAL; - } - - h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE; - h->items[slot_id].pkt = pkt; - h->pushed++; - return 0; -} - -static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, - u32 slot_id, struct sk_buff **pktout, - bool remove_item) -{ - if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) - return -ENOENT; - - if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { - brcmf_err("entry not in use\n"); - h->failed_to_pop++; - return -EINVAL; - } - - *pktout = h->items[slot_id].pkt; - if (remove_item) { - h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; - h->items[slot_id].pkt = NULL; - h->popped++; - } - return 0; -} - -static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, - u32 slot_id) -{ - if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) - return -ENOENT; - - if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { - brcmf_err("entry not in use\n"); - return -EINVAL; - } - - h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED; - return 0; -} - -static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws, - bool (*fn)(struct sk_buff *, void *), - int ifidx) -{ - struct brcmf_fws_hanger *h = &fws->hanger; - struct sk_buff *skb; - int i; - enum brcmf_fws_hanger_item_state s; - - for (i = 0; i < ARRAY_SIZE(h->items); i++) { - s = h->items[i].state; - if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE || - s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) { - skb = h->items[i].pkt; - if (fn == NULL || fn(skb, &ifidx)) { - /* suppress packets freed from psq */ - if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE) - brcmu_pkt_buf_free_skb(skb); - h->items[i].state = - BRCMF_FWS_HANGER_ITEM_STATE_FREE; - } - } - } -} - -static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws, - struct brcmf_fws_mac_descriptor *desc) -{ - if (desc == &fws->desc.other) - strlcpy(desc->name, "MAC-OTHER", sizeof(desc->name)); - else if (desc->mac_handle) - scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d", - desc->mac_handle, desc->interface_id); - else - scnprintf(desc->name, sizeof(desc->name), "MACIF:%d", - desc->interface_id); -} - -static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc, - u8 *addr, u8 ifidx) -{ - brcmf_dbg(TRACE, - "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx); - desc->occupied = 1; - desc->state = BRCMF_FWS_STATE_OPEN; - desc->requested_credit = 0; - desc->requested_packet = 0; - /* depending on use may need ifp->bssidx instead */ - desc->interface_id = ifidx; - desc->ac_bitmap = 0xff; /* update this when handling APSD */ - if (addr) - memcpy(&desc->ea[0], addr, ETH_ALEN); -} - -static -void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc) -{ - brcmf_dbg(TRACE, - "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id); - desc->occupied = 0; - desc->state = BRCMF_FWS_STATE_CLOSE; - desc->requested_credit = 0; - desc->requested_packet = 0; -} - -static struct brcmf_fws_mac_descriptor * -brcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea) -{ - struct brcmf_fws_mac_descriptor *entry; - int i; - - if (ea == NULL) - return ERR_PTR(-EINVAL); - - entry = &fws->desc.nodes[0]; - for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) { - if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN)) - return entry; - entry++; - } - - return ERR_PTR(-ENOENT); -} - -static struct brcmf_fws_mac_descriptor* -brcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da) -{ - struct brcmf_fws_mac_descriptor *entry = &fws->desc.other; - bool multicast; - - multicast = is_multicast_ether_addr(da); - - /* Multicast destination, STA and P2P clients get the interface entry. - * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations - * have their own entry. - */ - if (multicast && ifp->fws_desc) { - entry = ifp->fws_desc; - goto done; - } - - entry = brcmf_fws_macdesc_lookup(fws, da); - if (IS_ERR(entry)) - entry = ifp->fws_desc; - -done: - return entry; -} - -static bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws, - struct brcmf_fws_mac_descriptor *entry, - int fifo) -{ - struct brcmf_fws_mac_descriptor *if_entry; - bool closed; - - /* for unique destination entries the related interface - * may be closed. - */ - if (entry->mac_handle) { - if_entry = &fws->desc.iface[entry->interface_id]; - if (if_entry->state == BRCMF_FWS_STATE_CLOSE) - return true; - } - /* an entry is closed when the state is closed and - * the firmware did not request anything. - */ - closed = entry->state == BRCMF_FWS_STATE_CLOSE && - !entry->requested_credit && !entry->requested_packet; - - /* Or firmware does not allow traffic for given fifo */ - return closed || !(entry->ac_bitmap & BIT(fifo)); -} - -static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws, - struct brcmf_fws_mac_descriptor *entry, - int ifidx) -{ - if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) { - brcmf_fws_psq_flush(fws, &entry->psq, ifidx); - entry->occupied = !!(entry->psq.len); - } -} - -static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws, - bool (*fn)(struct sk_buff *, void *), - int ifidx) -{ - struct brcmf_fws_hanger_item *hi; - struct pktq *txq; - struct sk_buff *skb; - int prec; - u32 hslot; - - txq = brcmf_bus_gettxq(fws->drvr->bus_if); - if (IS_ERR(txq)) { - brcmf_dbg(TRACE, "no txq to clean up\n"); - return; - } - - for (prec = 0; prec < txq->num_prec; prec++) { - skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx); - while (skb) { - hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - hi = &fws->hanger.items[hslot]; - WARN_ON(skb != hi->pkt); - hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; - brcmu_pkt_buf_free_skb(skb); - skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx); - } - } -} - -static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) -{ - int i; - struct brcmf_fws_mac_descriptor *table; - bool (*matchfn)(struct sk_buff *, void *) = NULL; - - if (fws == NULL) - return; - - if (ifidx != -1) - matchfn = brcmf_fws_ifidx_match; - - /* cleanup individual nodes */ - table = &fws->desc.nodes[0]; - for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) - brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx); - - brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx); - brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx); - brcmf_fws_hanger_cleanup(fws, matchfn, ifidx); -} - -static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) -{ - struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; - u8 *wlh; - u16 data_offset = 0; - u8 fillers; - __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); - __le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq); - - brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n", - entry->name, brcmf_skb_if_flags_get_field(skb, INDEX), - (le32_to_cpu(pkttag) >> 8) & 0xffff, - brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq); - if (entry->send_tim_signal) - data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; - if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) - data_offset += BRCMF_FWS_TYPE_SEQ_LEN; - /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ - data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; - fillers = round_up(data_offset, 4) - data_offset; - data_offset += fillers; - - skb_push(skb, data_offset); - wlh = skb->data; - - wlh[0] = BRCMF_FWS_TYPE_PKTTAG; - wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN; - memcpy(&wlh[2], &pkttag, sizeof(pkttag)); - if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) { - wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN; - memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq, - sizeof(pktseq)); - } - wlh += wlh[1] + 2; - - if (entry->send_tim_signal) { - entry->send_tim_signal = 0; - wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP; - wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; - wlh[2] = entry->mac_handle; - wlh[3] = entry->traffic_pending_bmp; - brcmf_dbg(TRACE, "adding TIM info: handle %d bmp 0x%X\n", - entry->mac_handle, entry->traffic_pending_bmp); - wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2; - entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; - } - if (fillers) - memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); - - return (u8)(data_offset >> 2); -} - -static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, - struct brcmf_fws_mac_descriptor *entry, - int fifo, bool send_immediately) -{ - struct sk_buff *skb; - struct brcmf_skbuff_cb *skcb; - s32 err; - u32 len; - u8 data_offset; - int ifidx; - - /* check delayedQ and suppressQ in one call using bitmap */ - if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0) - entry->traffic_pending_bmp &= ~NBITVAL(fifo); - else - entry->traffic_pending_bmp |= NBITVAL(fifo); - - entry->send_tim_signal = false; - if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) - entry->send_tim_signal = true; - if (send_immediately && entry->send_tim_signal && - entry->state == BRCMF_FWS_STATE_CLOSE) { - /* create a dummy packet and sent that. The traffic */ - /* bitmap info will automatically be attached to that packet */ - len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 + - BRCMF_FWS_TYPE_SEQ_LEN + - BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 + - 4 + fws->drvr->hdrlen; - skb = brcmu_pkt_buf_get_skb(len); - if (skb == NULL) - return false; - skb_pull(skb, len); - skcb = brcmf_skbcb(skb); - skcb->mac = entry; - skcb->state = BRCMF_FWS_SKBSTATE_TIM; - skcb->htod = 0; - skcb->htod_seq = 0; - data_offset = brcmf_fws_hdrpush(fws, skb); - ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); - brcmf_fws_unlock(fws); - err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb); - brcmf_fws_lock(fws); - if (err) - brcmu_pkt_buf_free_skb(skb); - return true; - } - return false; -} - -static void -brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq, - u8 if_id) -{ - struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id); - - if (WARN_ON(!ifp)) - return; - - if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && - pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER) - brcmf_txflowblock_if(ifp, - BRCMF_NETIF_STOP_REASON_FWS_FC, false); - if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && - pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) { - fws->stats.fws_flow_block++; - brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true); - } - return; -} - -static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi) -{ - brcmf_dbg(CTL, "rssi %d\n", rssi); - return 0; -} - -static -int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) -{ - struct brcmf_fws_mac_descriptor *entry, *existing; - u8 mac_handle; - u8 ifidx; - u8 *addr; - - mac_handle = *data++; - ifidx = *data++; - addr = data; - - entry = &fws->desc.nodes[mac_handle & 0x1F]; - if (type == BRCMF_FWS_TYPE_MACDESC_DEL) { - if (entry->occupied) { - brcmf_dbg(TRACE, "deleting %s mac %pM\n", - entry->name, addr); - brcmf_fws_lock(fws); - brcmf_fws_macdesc_cleanup(fws, entry, -1); - brcmf_fws_macdesc_deinit(entry); - brcmf_fws_unlock(fws); - } else - fws->stats.mac_update_failed++; - return 0; - } - - existing = brcmf_fws_macdesc_lookup(fws, addr); - if (IS_ERR(existing)) { - if (!entry->occupied) { - brcmf_fws_lock(fws); - entry->mac_handle = mac_handle; - brcmf_fws_macdesc_init(entry, addr, ifidx); - brcmf_fws_macdesc_set_name(fws, entry); - brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, - BRCMF_FWS_PSQ_LEN); - brcmf_fws_unlock(fws); - brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr); - } else { - fws->stats.mac_update_failed++; - } - } else { - if (entry != existing) { - brcmf_dbg(TRACE, "copy mac %s\n", existing->name); - brcmf_fws_lock(fws); - memcpy(entry, existing, - offsetof(struct brcmf_fws_mac_descriptor, psq)); - entry->mac_handle = mac_handle; - brcmf_fws_macdesc_deinit(existing); - brcmf_fws_macdesc_set_name(fws, entry); - brcmf_fws_unlock(fws); - brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name, - addr); - } else { - brcmf_dbg(TRACE, "use existing\n"); - WARN_ON(entry->mac_handle != mac_handle); - /* TODO: what should we do here: continue, reinit, .. */ - } - } - return 0; -} - -static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws, - u8 type, u8 *data) -{ - struct brcmf_fws_mac_descriptor *entry; - u8 mac_handle; - int ret; - - mac_handle = data[0]; - entry = &fws->desc.nodes[mac_handle & 0x1F]; - if (!entry->occupied) { - fws->stats.mac_ps_update_failed++; - return -ESRCH; - } - brcmf_fws_lock(fws); - /* a state update should wipe old credits */ - entry->requested_credit = 0; - entry->requested_packet = 0; - if (type == BRCMF_FWS_TYPE_MAC_OPEN) { - entry->state = BRCMF_FWS_STATE_OPEN; - ret = BRCMF_FWS_RET_OK_SCHEDULE; - } else { - entry->state = BRCMF_FWS_STATE_CLOSE; - brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false); - brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false); - brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false); - brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true); - ret = BRCMF_FWS_RET_OK_NOSCHEDULE; - } - brcmf_fws_unlock(fws); - return ret; -} - -static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws, - u8 type, u8 *data) -{ - struct brcmf_fws_mac_descriptor *entry; - u8 ifidx; - int ret; - - ifidx = data[0]; - - if (ifidx >= BRCMF_MAX_IFS) { - ret = -ERANGE; - goto fail; - } - - entry = &fws->desc.iface[ifidx]; - if (!entry->occupied) { - ret = -ESRCH; - goto fail; - } - - brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type, - entry->name); - brcmf_fws_lock(fws); - switch (type) { - case BRCMF_FWS_TYPE_INTERFACE_OPEN: - entry->state = BRCMF_FWS_STATE_OPEN; - ret = BRCMF_FWS_RET_OK_SCHEDULE; - break; - case BRCMF_FWS_TYPE_INTERFACE_CLOSE: - entry->state = BRCMF_FWS_STATE_CLOSE; - ret = BRCMF_FWS_RET_OK_NOSCHEDULE; - break; - default: - ret = -EINVAL; - brcmf_fws_unlock(fws); - goto fail; - } - brcmf_fws_unlock(fws); - return ret; - -fail: - fws->stats.if_update_failed++; - return ret; -} - -static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, - u8 *data) -{ - struct brcmf_fws_mac_descriptor *entry; - - entry = &fws->desc.nodes[data[1] & 0x1F]; - if (!entry->occupied) { - if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT) - fws->stats.credit_request_failed++; - else - fws->stats.packet_request_failed++; - return -ESRCH; - } - - brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n", - brcmf_fws_get_tlv_name(type), type, entry->name, - data[0], data[2]); - brcmf_fws_lock(fws); - if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT) - entry->requested_credit = data[0]; - else - entry->requested_packet = data[0]; - - entry->ac_bitmap = data[2]; - brcmf_fws_unlock(fws); - return BRCMF_FWS_RET_OK_SCHEDULE; -} - -static void -brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry, - struct sk_buff *skb) -{ - if (entry->requested_credit > 0) { - entry->requested_credit--; - brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); - brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1); - if (entry->state != BRCMF_FWS_STATE_CLOSE) - brcmf_err("requested credit set while mac not closed!\n"); - } else if (entry->requested_packet > 0) { - entry->requested_packet--; - brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); - brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0); - if (entry->state != BRCMF_FWS_STATE_CLOSE) - brcmf_err("requested packet set while mac not closed!\n"); - } else { - brcmf_skb_if_flags_set_field(skb, REQUESTED, 0); - brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0); - } -} - -static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb) -{ - struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; - - if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) && - (entry->state == BRCMF_FWS_STATE_CLOSE)) - entry->requested_credit++; -} - -static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, - u8 fifo, u8 credits) -{ - int lender_ac; - int *borrowed; - int *fifo_credit; - - if (!credits) - return; - - fws->fifo_credit_map |= 1 << fifo; - - if ((fifo == BRCMF_FWS_FIFO_AC_BE) && - (fws->credits_borrowed[0])) { - for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0; - lender_ac--) { - borrowed = &fws->credits_borrowed[lender_ac]; - if (*borrowed) { - fws->fifo_credit_map |= (1 << lender_ac); - fifo_credit = &fws->fifo_credit[lender_ac]; - if (*borrowed >= credits) { - *borrowed -= credits; - *fifo_credit += credits; - return; - } else { - credits -= *borrowed; - *fifo_credit += *borrowed; - *borrowed = 0; - } - } - } - } - - fws->fifo_credit[fifo] += credits; -} - -static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws) -{ - /* only schedule dequeue when there are credits for delayed traffic */ - if ((fws->fifo_credit_map & fws->fifo_delay_map) || - (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map)) - queue_work(fws->fws_wq, &fws->fws_dequeue_work); -} - -static int brcmf_fws_enq(struct brcmf_fws_info *fws, - enum brcmf_fws_skb_state state, int fifo, - struct sk_buff *p) -{ - int prec = 2 * fifo; - u32 *qfull_stat = &fws->stats.delayq_full_error; - struct brcmf_fws_mac_descriptor *entry; - struct pktq *pq; - struct sk_buff_head *queue; - struct sk_buff *p_head; - struct sk_buff *p_tail; - u32 fr_new; - u32 fr_compare; - - entry = brcmf_skbcb(p)->mac; - if (entry == NULL) { - brcmf_err("no mac descriptor found for skb %p\n", p); - return -ENOENT; - } - - brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p); - if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { - prec += 1; - qfull_stat = &fws->stats.supprq_full_error; - - /* Fix out of order delivery of frames. Dont assume frame */ - /* can be inserted at the end, but look for correct position */ - pq = &entry->psq; - if (pktq_full(pq) || pktq_pfull(pq, prec)) { - *qfull_stat += 1; - return -ENFILE; - } - queue = &pq->q[prec].skblist; - - p_head = skb_peek(queue); - p_tail = skb_peek_tail(queue); - fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN); - - while (p_head != p_tail) { - fr_compare = brcmf_skb_htod_tag_get_field(p_tail, - FREERUN); - /* be sure to handle wrap of 256 */ - if (((fr_new > fr_compare) && - ((fr_new - fr_compare) < 128)) || - ((fr_new < fr_compare) && - ((fr_compare - fr_new) > 128))) - break; - p_tail = skb_queue_prev(queue, p_tail); - } - /* Position found. Determine what to do */ - if (p_tail == NULL) { - /* empty list */ - __skb_queue_tail(queue, p); - } else { - fr_compare = brcmf_skb_htod_tag_get_field(p_tail, - FREERUN); - if (((fr_new > fr_compare) && - ((fr_new - fr_compare) < 128)) || - ((fr_new < fr_compare) && - ((fr_compare - fr_new) > 128))) { - /* After tail */ - __skb_queue_after(queue, p_tail, p); - } else { - /* Before tail */ - __skb_insert(p, p_tail->prev, p_tail, queue); - } - } - - /* Complete the counters and statistics */ - pq->len++; - if (pq->hi_prec < prec) - pq->hi_prec = (u8) prec; - } else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) { - *qfull_stat += 1; - return -ENFILE; - } - - /* increment total enqueued packet count */ - fws->fifo_delay_map |= 1 << fifo; - fws->fifo_enqpkt[fifo]++; - - /* update the sk_buff state */ - brcmf_skbcb(p)->state = state; - - /* - * A packet has been pushed so update traffic - * availability bitmap, if applicable - */ - brcmf_fws_tim_update(fws, entry, fifo, true); - brcmf_fws_flow_control_check(fws, &entry->psq, - brcmf_skb_if_flags_get_field(p, INDEX)); - return 0; -} - -static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) -{ - struct brcmf_fws_mac_descriptor *table; - struct brcmf_fws_mac_descriptor *entry; - struct sk_buff *p; - int num_nodes; - int node_pos; - int prec_out; - int pmsk; - int i; - - table = (struct brcmf_fws_mac_descriptor *)&fws->desc; - num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor); - node_pos = fws->deq_node_pos[fifo]; - - for (i = 0; i < num_nodes; i++) { - entry = &table[(node_pos + i) % num_nodes]; - if (!entry->occupied || - brcmf_fws_macdesc_closed(fws, entry, fifo)) - continue; - - if (entry->suppressed) - pmsk = 2; - else - pmsk = 3; - p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out); - if (p == NULL) { - if (entry->suppressed) { - if (entry->suppr_transit_count) - continue; - entry->suppressed = false; - p = brcmu_pktq_mdeq(&entry->psq, - 1 << (fifo * 2), &prec_out); - } - } - if (p == NULL) - continue; - - brcmf_fws_macdesc_use_req_credit(entry, p); - - /* move dequeue position to ensure fair round-robin */ - fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes; - brcmf_fws_flow_control_check(fws, &entry->psq, - brcmf_skb_if_flags_get_field(p, - INDEX) - ); - /* - * A packet has been picked up, update traffic - * availability bitmap, if applicable - */ - brcmf_fws_tim_update(fws, entry, fifo, false); - - /* - * decrement total enqueued fifo packets and - * clear delay bitmap if done. - */ - fws->fifo_enqpkt[fifo]--; - if (fws->fifo_enqpkt[fifo] == 0) - fws->fifo_delay_map &= ~(1 << fifo); - goto done; - } - p = NULL; -done: - brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p); - return p; -} - -static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *skb, - u32 genbit, u16 seq) -{ - struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; - u32 hslot; - int ret; - - hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - - /* this packet was suppressed */ - if (!entry->suppressed) { - entry->suppressed = true; - entry->suppr_transit_count = entry->transit_count; - brcmf_dbg(DATA, "suppress %s: transit %d\n", - entry->name, entry->transit_count); - } - - entry->generation = genbit; - - brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit); - brcmf_skbcb(skb)->htod_seq = seq; - if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) { - brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1); - brcmf_skb_htod_seq_set_field(skb, FROMFW, 0); - } else { - brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0); - } - ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb); - - if (ret != 0) { - /* suppress q is full drop this packet */ - brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true); - } else { - /* Mark suppressed to avoid a double free during wlfc cleanup */ - brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot); - } - - return ret; -} - -static int -brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, - u32 genbit, u16 seq) -{ - u32 fifo; - int ret; - bool remove_from_hanger = true; - struct sk_buff *skb; - struct brcmf_skbuff_cb *skcb; - struct brcmf_fws_mac_descriptor *entry = NULL; - struct brcmf_if *ifp; - - brcmf_dbg(DATA, "flags %d\n", flags); - - if (flags == BRCMF_FWS_TXSTATUS_DISCARD) - fws->stats.txs_discard++; - else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) { - fws->stats.txs_supp_core++; - remove_from_hanger = false; - } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) { - fws->stats.txs_supp_ps++; - remove_from_hanger = false; - } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED) - fws->stats.txs_tossed++; - else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED) - fws->stats.txs_host_tossed++; - else - brcmf_err("unexpected txstatus\n"); - - ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, - remove_from_hanger); - if (ret != 0) { - brcmf_err("no packet in hanger slot: hslot=%d\n", hslot); - return ret; - } - - skcb = brcmf_skbcb(skb); - entry = skcb->mac; - if (WARN_ON(!entry)) { - brcmu_pkt_buf_free_skb(skb); - return -EINVAL; - } - entry->transit_count--; - if (entry->suppressed && entry->suppr_transit_count) - entry->suppr_transit_count--; - - brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name, flags, - skcb->htod, seq); - - /* pick up the implicit credit from this packet */ - fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); - if ((fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT) || - (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) || - (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) { - brcmf_fws_return_credits(fws, fifo, 1); - brcmf_fws_schedule_deq(fws); - } - brcmf_fws_macdesc_return_req_credit(skb); - - ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp); - if (ret) { - brcmu_pkt_buf_free_skb(skb); - return -EINVAL; - } - if (!remove_from_hanger) - ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, - genbit, seq); - if (remove_from_hanger || ret) - brcmf_txfinalize(ifp, skb, true); - - return 0; -} - -static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws, - u8 *data) -{ - int i; - - if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) { - brcmf_dbg(INFO, "ignored\n"); - return BRCMF_FWS_RET_OK_NOSCHEDULE; - } - - brcmf_dbg(DATA, "enter: data %pM\n", data); - brcmf_fws_lock(fws); - for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++) - brcmf_fws_return_credits(fws, i, data[i]); - - brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map, - fws->fifo_delay_map); - brcmf_fws_unlock(fws); - return BRCMF_FWS_RET_OK_SCHEDULE; -} - -static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) -{ - __le32 status_le; - __le16 seq_le; - u32 status; - u32 hslot; - u32 genbit; - u8 flags; - u16 seq; - - fws->stats.txs_indicate++; - memcpy(&status_le, data, sizeof(status_le)); - status = le32_to_cpu(status_le); - flags = brcmf_txstatus_get_field(status, FLAGS); - hslot = brcmf_txstatus_get_field(status, HSLOT); - genbit = brcmf_txstatus_get_field(status, GENERATION); - if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) { - memcpy(&seq_le, &data[BRCMF_FWS_TYPE_PKTTAG_LEN], - sizeof(seq_le)); - seq = le16_to_cpu(seq_le); - } else { - seq = 0; - } - - brcmf_fws_lock(fws); - brcmf_fws_txs_process(fws, flags, hslot, genbit, seq); - brcmf_fws_unlock(fws); - return BRCMF_FWS_RET_OK_NOSCHEDULE; -} - -static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) -{ - __le32 timestamp; - - memcpy(×tamp, &data[2], sizeof(timestamp)); - brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1], - le32_to_cpu(timestamp)); - return 0; -} - -static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, - void *data) -{ - struct brcmf_fws_info *fws = ifp->drvr->fws; - int i; - u8 *credits = data; - - if (e->datalen < BRCMF_FWS_FIFO_COUNT) { - brcmf_err("event payload too small (%d)\n", e->datalen); - return -EINVAL; - } - if (fws->creditmap_received) - return 0; - - fws->creditmap_received = true; - - brcmf_dbg(TRACE, "enter: credits %pM\n", credits); - brcmf_fws_lock(fws); - for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) { - if (*credits) - fws->fifo_credit_map |= 1 << i; - else - fws->fifo_credit_map &= ~(1 << i); - fws->fifo_credit[i] = *credits++; - } - brcmf_fws_schedule_deq(fws); - brcmf_fws_unlock(fws); - return 0; -} - -static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, - void *data) -{ - struct brcmf_fws_info *fws = ifp->drvr->fws; - - brcmf_fws_lock(fws); - if (fws) - fws->bcmc_credit_check = true; - brcmf_fws_unlock(fws); - return 0; -} - -void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb) -{ - struct brcmf_skb_reorder_data *rd; - struct brcmf_fws_info *fws = ifp->drvr->fws; - u8 *signal_data; - s16 data_len; - u8 type; - u8 len; - u8 *data; - s32 status; - s32 err; - - brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n", - ifp->ifidx, skb->len, siglen); - - WARN_ON(siglen > skb->len); - - if (!siglen) - return; - /* if flow control disabled, skip to packet data and leave */ - if ((!fws) || (!fws->fw_signals)) { - skb_pull(skb, siglen); - return; - } - - fws->stats.header_pulls++; - data_len = siglen; - signal_data = skb->data; - - status = BRCMF_FWS_RET_OK_NOSCHEDULE; - while (data_len > 0) { - /* extract tlv info */ - type = signal_data[0]; - - /* FILLER type is actually not a TLV, but - * a single byte that can be skipped. - */ - if (type == BRCMF_FWS_TYPE_FILLER) { - signal_data += 1; - data_len -= 1; - continue; - } - len = signal_data[1]; - data = signal_data + 2; - - brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n", - brcmf_fws_get_tlv_name(type), type, len, - brcmf_fws_get_tlv_len(fws, type)); - - /* abort parsing when length invalid */ - if (data_len < len + 2) - break; - - if (len < brcmf_fws_get_tlv_len(fws, type)) - break; - - err = BRCMF_FWS_RET_OK_NOSCHEDULE; - switch (type) { - case BRCMF_FWS_TYPE_COMP_TXSTATUS: - break; - case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS: - rd = (struct brcmf_skb_reorder_data *)skb->cb; - rd->reorder = data; - break; - case BRCMF_FWS_TYPE_MACDESC_ADD: - case BRCMF_FWS_TYPE_MACDESC_DEL: - brcmf_fws_macdesc_indicate(fws, type, data); - break; - case BRCMF_FWS_TYPE_MAC_OPEN: - case BRCMF_FWS_TYPE_MAC_CLOSE: - err = brcmf_fws_macdesc_state_indicate(fws, type, data); - break; - case BRCMF_FWS_TYPE_INTERFACE_OPEN: - case BRCMF_FWS_TYPE_INTERFACE_CLOSE: - err = brcmf_fws_interface_state_indicate(fws, type, - data); - break; - case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT: - case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET: - err = brcmf_fws_request_indicate(fws, type, data); - break; - case BRCMF_FWS_TYPE_TXSTATUS: - brcmf_fws_txstatus_indicate(fws, data); - break; - case BRCMF_FWS_TYPE_FIFO_CREDITBACK: - err = brcmf_fws_fifocreditback_indicate(fws, data); - break; - case BRCMF_FWS_TYPE_RSSI: - brcmf_fws_rssi_indicate(fws, *data); - break; - case BRCMF_FWS_TYPE_TRANS_ID: - brcmf_fws_dbg_seqnum_check(fws, data); - break; - case BRCMF_FWS_TYPE_PKTTAG: - case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP: - default: - fws->stats.tlv_invalid_type++; - break; - } - if (err == BRCMF_FWS_RET_OK_SCHEDULE) - status = BRCMF_FWS_RET_OK_SCHEDULE; - signal_data += len + 2; - data_len -= len + 2; - } - - if (data_len != 0) - fws->stats.tlv_parse_failed++; - - if (status == BRCMF_FWS_RET_OK_SCHEDULE) - brcmf_fws_schedule_deq(fws); - - /* signalling processing result does - * not affect the actual ethernet packet. - */ - skb_pull(skb, siglen); - - /* this may be a signal-only packet - */ - if (skb->len == 0) - fws->stats.header_only_pkt++; -} - -static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *p) -{ - struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); - struct brcmf_fws_mac_descriptor *entry = skcb->mac; - u8 flags; - - if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED) - brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation); - flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST; - if (brcmf_skb_if_flags_get_field(p, REQUESTED)) { - /* - * Indicate that this packet is being sent in response to an - * explicit request from the firmware side. - */ - flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED; - } - brcmf_skb_htod_tag_set_field(p, FLAGS, flags); - return brcmf_fws_hdrpush(fws, p); -} - -static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, - struct sk_buff *skb, int fifo) -{ - struct brcmf_fws_mac_descriptor *entry; - struct sk_buff *pktout; - int qidx, hslot; - int rc = 0; - - entry = brcmf_skbcb(skb)->mac; - if (entry->occupied) { - qidx = 2 * fifo; - if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED) - qidx++; - - pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb); - if (pktout == NULL) { - brcmf_err("%s queue %d full\n", entry->name, qidx); - rc = -ENOSPC; - } - } else { - brcmf_err("%s entry removed\n", entry->name); - rc = -ENOENT; - } - - if (rc) { - fws->stats.rollback_failed++; - hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, - hslot, 0, 0); - } else { - fws->stats.rollback_success++; - brcmf_fws_return_credits(fws, fifo, 1); - brcmf_fws_macdesc_return_req_credit(skb); - } -} - -static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws) -{ - int lender_ac; - - if (time_after(fws->borrow_defer_timestamp, jiffies)) { - fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE); - return -ENAVAIL; - } - - for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) { - if (fws->fifo_credit[lender_ac]) { - fws->credits_borrowed[lender_ac]++; - fws->fifo_credit[lender_ac]--; - if (fws->fifo_credit[lender_ac] == 0) - fws->fifo_credit_map &= ~(1 << lender_ac); - fws->fifo_credit_map |= (1 << BRCMF_FWS_FIFO_AC_BE); - brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac); - return 0; - } - } - fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE); - return -ENAVAIL; -} - -static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *skb) -{ - struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); - struct brcmf_fws_mac_descriptor *entry; - int rc; - u8 ifidx; - u8 data_offset; - - entry = skcb->mac; - if (IS_ERR(entry)) - return PTR_ERR(entry); - - data_offset = brcmf_fws_precommit_skb(fws, fifo, skb); - entry->transit_count++; - if (entry->suppressed) - entry->suppr_transit_count++; - ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); - brcmf_fws_unlock(fws); - rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb); - brcmf_fws_lock(fws); - brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name, - skcb->if_flags, skcb->htod, rc); - if (rc < 0) { - entry->transit_count--; - if (entry->suppressed) - entry->suppr_transit_count--; - (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL); - goto rollback; - } - - fws->stats.pkt2bus++; - fws->stats.send_pkts[fifo]++; - if (brcmf_skb_if_flags_get_field(skb, REQUESTED)) - fws->stats.requested_sent[fifo]++; - - return rc; - -rollback: - brcmf_fws_rollback_toq(fws, skb, fifo); - return rc; -} - -static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p, - int fifo) -{ - struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); - int rc, hslot; - - skcb->htod = 0; - skcb->htod_seq = 0; - hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger); - brcmf_skb_htod_tag_set_field(p, HSLOT, hslot); - brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]); - brcmf_skb_htod_tag_set_field(p, FIFO, fifo); - rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot); - if (!rc) - skcb->mac->seq[fifo]++; - else - fws->stats.generic_error++; - return rc; -} - -int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) -{ - struct brcmf_pub *drvr = ifp->drvr; - struct brcmf_fws_info *fws = drvr->fws; - struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); - struct ethhdr *eh = (struct ethhdr *)(skb->data); - int fifo = BRCMF_FWS_FIFO_BCMC; - bool multicast = is_multicast_ether_addr(eh->h_dest); - int rc = 0; - - brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto)); - /* determine the priority */ - if (!skb->priority) - skb->priority = cfg80211_classify8021d(skb, NULL); - - drvr->tx_multicast += !!multicast; - - if (fws->avoid_queueing) { - rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb); - if (rc < 0) - brcmf_txfinalize(ifp, skb, false); - return rc; - } - - /* set control buffer information */ - skcb->if_flags = 0; - skcb->state = BRCMF_FWS_SKBSTATE_NEW; - brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx); - if (!multicast) - fifo = brcmf_fws_prio2fifo[skb->priority]; - - brcmf_fws_lock(fws); - if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC) - fws->borrow_defer_timestamp = jiffies + - BRCMF_FWS_BORROW_DEFER_PERIOD; - - skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest); - brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name, - eh->h_dest, multicast, fifo); - if (!brcmf_fws_assign_htod(fws, skb, fifo)) { - brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb); - brcmf_fws_schedule_deq(fws); - } else { - brcmf_err("drop skb: no hanger slot\n"); - brcmf_txfinalize(ifp, skb, false); - rc = -ENOMEM; - } - brcmf_fws_unlock(fws); - - return rc; -} - -void brcmf_fws_reset_interface(struct brcmf_if *ifp) -{ - struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; - - brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); - if (!entry) - return; - - brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx); -} - -void brcmf_fws_add_interface(struct brcmf_if *ifp) -{ - struct brcmf_fws_info *fws = ifp->drvr->fws; - struct brcmf_fws_mac_descriptor *entry; - - if (!ifp->ndev) - return; - - entry = &fws->desc.iface[ifp->ifidx]; - ifp->fws_desc = entry; - brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx); - brcmf_fws_macdesc_set_name(fws, entry); - brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, - BRCMF_FWS_PSQ_LEN); - brcmf_dbg(TRACE, "added %s\n", entry->name); -} - -void brcmf_fws_del_interface(struct brcmf_if *ifp) -{ - struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; - - if (!entry) - return; - - brcmf_fws_lock(ifp->drvr->fws); - ifp->fws_desc = NULL; - brcmf_dbg(TRACE, "deleting %s\n", entry->name); - brcmf_fws_macdesc_deinit(entry); - brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx); - brcmf_fws_unlock(ifp->drvr->fws); -} - -static void brcmf_fws_dequeue_worker(struct work_struct *worker) -{ - struct brcmf_fws_info *fws; - struct brcmf_pub *drvr; - struct sk_buff *skb; - int fifo; - u32 hslot; - u32 ifidx; - int ret; - - fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); - drvr = fws->drvr; - - brcmf_fws_lock(fws); - for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked; - fifo--) { - if (!brcmf_fws_fc_active(fws)) { - while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) { - hslot = brcmf_skb_htod_tag_get_field(skb, - HSLOT); - brcmf_fws_hanger_poppkt(&fws->hanger, hslot, - &skb, true); - ifidx = brcmf_skb_if_flags_get_field(skb, - INDEX); - /* Use proto layer to send data frame */ - brcmf_fws_unlock(fws); - ret = brcmf_proto_txdata(drvr, ifidx, 0, skb); - brcmf_fws_lock(fws); - if (ret < 0) - brcmf_txfinalize(brcmf_get_ifp(drvr, - ifidx), - skb, false); - if (fws->bus_flow_blocked) - break; - } - continue; - } - while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) && - (fifo == BRCMF_FWS_FIFO_BCMC))) { - skb = brcmf_fws_deq(fws, fifo); - if (!skb) - break; - fws->fifo_credit[fifo]--; - if (brcmf_fws_commit_skb(fws, fifo, skb)) - break; - if (fws->bus_flow_blocked) - break; - } - if ((fifo == BRCMF_FWS_FIFO_AC_BE) && - (fws->fifo_credit[fifo] == 0) && - (!fws->bus_flow_blocked)) { - while (brcmf_fws_borrow_credit(fws) == 0) { - skb = brcmf_fws_deq(fws, fifo); - if (!skb) { - brcmf_fws_return_credits(fws, fifo, 1); - break; - } - if (brcmf_fws_commit_skb(fws, fifo, skb)) - break; - if (fws->bus_flow_blocked) - break; - } - } - } - brcmf_fws_unlock(fws); -} - -#ifdef DEBUG -static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); - struct brcmf_fws_stats *fwstats = &bus_if->drvr->fws->stats; - - seq_printf(seq, - "header_pulls: %u\n" - "header_only_pkt: %u\n" - "tlv_parse_failed: %u\n" - "tlv_invalid_type: %u\n" - "mac_update_fails: %u\n" - "ps_update_fails: %u\n" - "if_update_fails: %u\n" - "pkt2bus: %u\n" - "generic_error: %u\n" - "rollback_success: %u\n" - "rollback_failed: %u\n" - "delayq_full: %u\n" - "supprq_full: %u\n" - "txs_indicate: %u\n" - "txs_discard: %u\n" - "txs_suppr_core: %u\n" - "txs_suppr_ps: %u\n" - "txs_tossed: %u\n" - "txs_host_tossed: %u\n" - "bus_flow_block: %u\n" - "fws_flow_block: %u\n" - "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" - "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", - fwstats->header_pulls, - fwstats->header_only_pkt, - fwstats->tlv_parse_failed, - fwstats->tlv_invalid_type, - fwstats->mac_update_failed, - fwstats->mac_ps_update_failed, - fwstats->if_update_failed, - fwstats->pkt2bus, - fwstats->generic_error, - fwstats->rollback_success, - fwstats->rollback_failed, - fwstats->delayq_full_error, - fwstats->supprq_full_error, - fwstats->txs_indicate, - fwstats->txs_discard, - fwstats->txs_supp_core, - fwstats->txs_supp_ps, - fwstats->txs_tossed, - fwstats->txs_host_tossed, - fwstats->bus_flow_block, - fwstats->fws_flow_block, - fwstats->send_pkts[0], fwstats->send_pkts[1], - fwstats->send_pkts[2], fwstats->send_pkts[3], - fwstats->send_pkts[4], - fwstats->requested_sent[0], - fwstats->requested_sent[1], - fwstats->requested_sent[2], - fwstats->requested_sent[3], - fwstats->requested_sent[4]); - - return 0; -} -#else -static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) -{ - return 0; -} -#endif - -int brcmf_fws_init(struct brcmf_pub *drvr) -{ - struct brcmf_fws_info *fws; - struct brcmf_if *ifp; - u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS; - int rc; - u32 mode; - - drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL); - if (!drvr->fws) { - rc = -ENOMEM; - goto fail; - } - - fws = drvr->fws; - - spin_lock_init(&fws->spinlock); - - /* set linkage back */ - fws->drvr = drvr; - fws->fcmode = fcmode; - - if ((drvr->bus_if->always_use_fws_queue == false) && - (fcmode == BRCMF_FWS_FCMODE_NONE)) { - fws->avoid_queueing = true; - brcmf_dbg(INFO, "FWS queueing will be avoided\n"); - return 0; - } - - fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq"); - if (fws->fws_wq == NULL) { - brcmf_err("workqueue creation failed\n"); - rc = -EBADF; - goto fail; - } - INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker); - - /* enable firmware signalling if fcmode active */ - if (fws->fcmode != BRCMF_FWS_FCMODE_NONE) - tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS | - BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS | - BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE | - BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE; - - rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP, - brcmf_fws_notify_credit_map); - if (rc < 0) { - brcmf_err("register credit map handler failed\n"); - goto fail; - } - rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT, - brcmf_fws_notify_bcmc_credit_support); - if (rc < 0) { - brcmf_err("register bcmc credit handler failed\n"); - brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP); - goto fail; - } - - /* Setting the iovar may fail if feature is unsupported - * so leave the rc as is so driver initialization can - * continue. Set mode back to none indicating not enabled. - */ - fws->fw_signals = true; - ifp = brcmf_get_ifp(drvr, 0); - if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) { - brcmf_err("failed to set bdcv2 tlv signaling\n"); - fws->fcmode = BRCMF_FWS_FCMODE_NONE; - fws->fw_signals = false; - } - - if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1)) - brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n"); - - /* Enable seq number reuse, if supported */ - if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) { - if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) { - mode = 0; - BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1); - if (brcmf_fil_iovar_int_set(ifp, - "wlfc_mode", mode) == 0) { - BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1); - } - } - } - - brcmf_fws_hanger_init(&fws->hanger); - brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0); - brcmf_fws_macdesc_set_name(fws, &fws->desc.other); - brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT, - BRCMF_FWS_PSQ_LEN); - - /* create debugfs file for statistics */ - brcmf_debugfs_add_entry(drvr, "fws_stats", - brcmf_debugfs_fws_stats_read); - - brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n", - fws->fw_signals ? "enabled" : "disabled", tlv); - return 0; - -fail: - brcmf_fws_deinit(drvr); - return rc; -} - -void brcmf_fws_deinit(struct brcmf_pub *drvr) -{ - struct brcmf_fws_info *fws = drvr->fws; - - if (!fws) - return; - - if (drvr->fws->fws_wq) - destroy_workqueue(drvr->fws->fws_wq); - - /* cleanup */ - brcmf_fws_lock(fws); - brcmf_fws_cleanup(fws, -1); - drvr->fws = NULL; - brcmf_fws_unlock(fws); - - /* free top structure */ - kfree(fws); -} - -bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) -{ - if (!fws->creditmap_received) - return false; - - return fws->fcmode != BRCMF_FWS_FCMODE_NONE; -} - -void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) -{ - u32 hslot; - - if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) { - brcmu_pkt_buf_free_skb(skb); - return; - } - brcmf_fws_lock(fws); - hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0); - brcmf_fws_unlock(fws); -} - -void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked) -{ - struct brcmf_fws_info *fws = drvr->fws; - - fws->bus_flow_blocked = flow_blocked; - if (!flow_blocked) - brcmf_fws_schedule_deq(fws); - else - fws->stats.bus_flow_block++; -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h deleted file mode 100644 index a36bac17eafd..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2012 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 FWSIGNAL_H_ -#define FWSIGNAL_H_ - -int brcmf_fws_init(struct brcmf_pub *drvr); -void brcmf_fws_deinit(struct brcmf_pub *drvr); -bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); -void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb); -int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb); - -void brcmf_fws_reset_interface(struct brcmf_if *ifp); -void brcmf_fws_add_interface(struct brcmf_if *ifp); -void brcmf_fws_del_interface(struct brcmf_if *ifp); -void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb); -void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked); - -#endif /* FWSIGNAL_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c deleted file mode 100644 index 44e618f9d890..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ /dev/null @@ -1,1561 +0,0 @@ -/* Copyright (c) 2014 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. - */ - -/******************************************************************************* - * Communicates with the dongle by using dcmd codes. - * For certain dcmd codes, the dongle interprets string data from the host. - ******************************************************************************/ - -#include -#include - -#include -#include - -#include "core.h" -#include "debug.h" -#include "proto.h" -#include "msgbuf.h" -#include "commonring.h" -#include "flowring.h" -#include "bus.h" -#include "tracepoint.h" - - -#define MSGBUF_IOCTL_RESP_TIMEOUT 2000 - -#define MSGBUF_TYPE_GEN_STATUS 0x1 -#define MSGBUF_TYPE_RING_STATUS 0x2 -#define MSGBUF_TYPE_FLOW_RING_CREATE 0x3 -#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT 0x4 -#define MSGBUF_TYPE_FLOW_RING_DELETE 0x5 -#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT 0x6 -#define MSGBUF_TYPE_FLOW_RING_FLUSH 0x7 -#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT 0x8 -#define MSGBUF_TYPE_IOCTLPTR_REQ 0x9 -#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK 0xA -#define MSGBUF_TYPE_IOCTLRESP_BUF_POST 0xB -#define MSGBUF_TYPE_IOCTL_CMPLT 0xC -#define MSGBUF_TYPE_EVENT_BUF_POST 0xD -#define MSGBUF_TYPE_WL_EVENT 0xE -#define MSGBUF_TYPE_TX_POST 0xF -#define MSGBUF_TYPE_TX_STATUS 0x10 -#define MSGBUF_TYPE_RXBUF_POST 0x11 -#define MSGBUF_TYPE_RX_CMPLT 0x12 -#define MSGBUF_TYPE_LPBK_DMAXFER 0x13 -#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14 - -#define NR_TX_PKTIDS 2048 -#define NR_RX_PKTIDS 1024 - -#define BRCMF_IOCTL_REQ_PKTID 0xFFFE - -#define BRCMF_MSGBUF_MAX_PKT_SIZE 2048 -#define BRCMF_MSGBUF_RXBUFPOST_THRESHOLD 32 -#define BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST 8 -#define BRCMF_MSGBUF_MAX_EVENTBUF_POST 8 - -#define BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3 0x01 -#define BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT 5 - -#define BRCMF_MSGBUF_TX_FLUSH_CNT1 32 -#define BRCMF_MSGBUF_TX_FLUSH_CNT2 96 - -#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 96 -#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32 -#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48 - - -struct msgbuf_common_hdr { - u8 msgtype; - u8 ifidx; - u8 flags; - u8 rsvd0; - __le32 request_id; -}; - -struct msgbuf_buf_addr { - __le32 low_addr; - __le32 high_addr; -}; - -struct msgbuf_ioctl_req_hdr { - struct msgbuf_common_hdr msg; - __le32 cmd; - __le16 trans_id; - __le16 input_buf_len; - __le16 output_buf_len; - __le16 rsvd0[3]; - struct msgbuf_buf_addr req_buf_addr; - __le32 rsvd1[2]; -}; - -struct msgbuf_tx_msghdr { - struct msgbuf_common_hdr msg; - u8 txhdr[ETH_HLEN]; - u8 flags; - u8 seg_cnt; - struct msgbuf_buf_addr metadata_buf_addr; - struct msgbuf_buf_addr data_buf_addr; - __le16 metadata_buf_len; - __le16 data_len; - __le32 rsvd0; -}; - -struct msgbuf_rx_bufpost { - struct msgbuf_common_hdr msg; - __le16 metadata_buf_len; - __le16 data_buf_len; - __le32 rsvd0; - struct msgbuf_buf_addr metadata_buf_addr; - struct msgbuf_buf_addr data_buf_addr; -}; - -struct msgbuf_rx_ioctl_resp_or_event { - struct msgbuf_common_hdr msg; - __le16 host_buf_len; - __le16 rsvd0[3]; - struct msgbuf_buf_addr host_buf_addr; - __le32 rsvd1[4]; -}; - -struct msgbuf_completion_hdr { - __le16 status; - __le16 flow_ring_id; -}; - -struct msgbuf_rx_event { - struct msgbuf_common_hdr msg; - struct msgbuf_completion_hdr compl_hdr; - __le16 event_data_len; - __le16 seqnum; - __le16 rsvd0[4]; -}; - -struct msgbuf_ioctl_resp_hdr { - struct msgbuf_common_hdr msg; - struct msgbuf_completion_hdr compl_hdr; - __le16 resp_len; - __le16 trans_id; - __le32 cmd; - __le32 rsvd0; -}; - -struct msgbuf_tx_status { - struct msgbuf_common_hdr msg; - struct msgbuf_completion_hdr compl_hdr; - __le16 metadata_len; - __le16 tx_status; -}; - -struct msgbuf_rx_complete { - struct msgbuf_common_hdr msg; - struct msgbuf_completion_hdr compl_hdr; - __le16 metadata_len; - __le16 data_len; - __le16 data_offset; - __le16 flags; - __le32 rx_status_0; - __le32 rx_status_1; - __le32 rsvd0; -}; - -struct msgbuf_tx_flowring_create_req { - struct msgbuf_common_hdr msg; - u8 da[ETH_ALEN]; - u8 sa[ETH_ALEN]; - u8 tid; - u8 if_flags; - __le16 flow_ring_id; - u8 tc; - u8 priority; - __le16 int_vector; - __le16 max_items; - __le16 len_item; - struct msgbuf_buf_addr flow_ring_addr; -}; - -struct msgbuf_tx_flowring_delete_req { - struct msgbuf_common_hdr msg; - __le16 flow_ring_id; - __le16 reason; - __le32 rsvd0[7]; -}; - -struct msgbuf_flowring_create_resp { - struct msgbuf_common_hdr msg; - struct msgbuf_completion_hdr compl_hdr; - __le32 rsvd0[3]; -}; - -struct msgbuf_flowring_delete_resp { - struct msgbuf_common_hdr msg; - struct msgbuf_completion_hdr compl_hdr; - __le32 rsvd0[3]; -}; - -struct msgbuf_flowring_flush_resp { - struct msgbuf_common_hdr msg; - struct msgbuf_completion_hdr compl_hdr; - __le32 rsvd0[3]; -}; - -struct brcmf_msgbuf_work_item { - struct list_head queue; - u32 flowid; - int ifidx; - u8 sa[ETH_ALEN]; - u8 da[ETH_ALEN]; -}; - -struct brcmf_msgbuf { - struct brcmf_pub *drvr; - - struct brcmf_commonring **commonrings; - struct brcmf_commonring **flowrings; - dma_addr_t *flowring_dma_handle; - u16 nrof_flowrings; - - u16 rx_dataoffset; - u32 max_rxbufpost; - u16 rx_metadata_offset; - u32 rxbufpost; - - u32 max_ioctlrespbuf; - u32 cur_ioctlrespbuf; - u32 max_eventbuf; - u32 cur_eventbuf; - - void *ioctbuf; - dma_addr_t ioctbuf_handle; - u32 ioctbuf_phys_hi; - u32 ioctbuf_phys_lo; - int ioctl_resp_status; - u32 ioctl_resp_ret_len; - u32 ioctl_resp_pktid; - - u16 data_seq_no; - u16 ioctl_seq_no; - u32 reqid; - wait_queue_head_t ioctl_resp_wait; - bool ctl_completed; - - struct brcmf_msgbuf_pktids *tx_pktids; - struct brcmf_msgbuf_pktids *rx_pktids; - struct brcmf_flowring *flow; - - struct workqueue_struct *txflow_wq; - struct work_struct txflow_work; - unsigned long *flow_map; - unsigned long *txstatus_done_map; - - struct work_struct flowring_work; - spinlock_t flowring_work_lock; - struct list_head work_queue; -}; - -struct brcmf_msgbuf_pktid { - atomic_t allocated; - u16 data_offset; - struct sk_buff *skb; - dma_addr_t physaddr; -}; - -struct brcmf_msgbuf_pktids { - u32 array_size; - u32 last_allocated_idx; - enum dma_data_direction direction; - struct brcmf_msgbuf_pktid *array; -}; - -static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf); - - -static struct brcmf_msgbuf_pktids * -brcmf_msgbuf_init_pktids(u32 nr_array_entries, - enum dma_data_direction direction) -{ - struct brcmf_msgbuf_pktid *array; - struct brcmf_msgbuf_pktids *pktids; - - array = kcalloc(nr_array_entries, sizeof(*array), GFP_KERNEL); - if (!array) - return NULL; - - pktids = kzalloc(sizeof(*pktids), GFP_KERNEL); - if (!pktids) { - kfree(array); - return NULL; - } - pktids->array = array; - pktids->array_size = nr_array_entries; - - return pktids; -} - - -static int -brcmf_msgbuf_alloc_pktid(struct device *dev, - struct brcmf_msgbuf_pktids *pktids, - struct sk_buff *skb, u16 data_offset, - dma_addr_t *physaddr, u32 *idx) -{ - struct brcmf_msgbuf_pktid *array; - u32 count; - - array = pktids->array; - - *physaddr = dma_map_single(dev, skb->data + data_offset, - skb->len - data_offset, pktids->direction); - - if (dma_mapping_error(dev, *physaddr)) { - brcmf_err("dma_map_single failed !!\n"); - return -ENOMEM; - } - - *idx = pktids->last_allocated_idx; - - count = 0; - do { - (*idx)++; - if (*idx == pktids->array_size) - *idx = 0; - if (array[*idx].allocated.counter == 0) - if (atomic_cmpxchg(&array[*idx].allocated, 0, 1) == 0) - break; - count++; - } while (count < pktids->array_size); - - if (count == pktids->array_size) - return -ENOMEM; - - array[*idx].data_offset = data_offset; - array[*idx].physaddr = *physaddr; - array[*idx].skb = skb; - - pktids->last_allocated_idx = *idx; - - return 0; -} - - -static struct sk_buff * -brcmf_msgbuf_get_pktid(struct device *dev, struct brcmf_msgbuf_pktids *pktids, - u32 idx) -{ - struct brcmf_msgbuf_pktid *pktid; - struct sk_buff *skb; - - if (idx >= pktids->array_size) { - brcmf_err("Invalid packet id %d (max %d)\n", idx, - pktids->array_size); - return NULL; - } - if (pktids->array[idx].allocated.counter) { - pktid = &pktids->array[idx]; - dma_unmap_single(dev, pktid->physaddr, - pktid->skb->len - pktid->data_offset, - pktids->direction); - skb = pktid->skb; - pktid->allocated.counter = 0; - return skb; - } else { - brcmf_err("Invalid packet id %d (not in use)\n", idx); - } - - return NULL; -} - - -static void -brcmf_msgbuf_release_array(struct device *dev, - struct brcmf_msgbuf_pktids *pktids) -{ - struct brcmf_msgbuf_pktid *array; - struct brcmf_msgbuf_pktid *pktid; - u32 count; - - array = pktids->array; - count = 0; - do { - if (array[count].allocated.counter) { - pktid = &array[count]; - dma_unmap_single(dev, pktid->physaddr, - pktid->skb->len - pktid->data_offset, - pktids->direction); - brcmu_pkt_buf_free_skb(pktid->skb); - } - count++; - } while (count < pktids->array_size); - - kfree(array); - kfree(pktids); -} - - -static void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf) -{ - if (msgbuf->rx_pktids) - brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev, - msgbuf->rx_pktids); - if (msgbuf->tx_pktids) - brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev, - msgbuf->tx_pktids); -} - - -static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx, - uint cmd, void *buf, uint len) -{ - struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - struct brcmf_commonring *commonring; - struct msgbuf_ioctl_req_hdr *request; - u16 buf_len; - void *ret_ptr; - int err; - - commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; - brcmf_commonring_lock(commonring); - ret_ptr = brcmf_commonring_reserve_for_write(commonring); - if (!ret_ptr) { - brcmf_err("Failed to reserve space in commonring\n"); - brcmf_commonring_unlock(commonring); - return -ENOMEM; - } - - msgbuf->reqid++; - - request = (struct msgbuf_ioctl_req_hdr *)ret_ptr; - request->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ; - request->msg.ifidx = (u8)ifidx; - request->msg.flags = 0; - request->msg.request_id = cpu_to_le32(BRCMF_IOCTL_REQ_PKTID); - request->cmd = cpu_to_le32(cmd); - request->output_buf_len = cpu_to_le16(len); - request->trans_id = cpu_to_le16(msgbuf->reqid); - - buf_len = min_t(u16, len, BRCMF_TX_IOCTL_MAX_MSG_SIZE); - request->input_buf_len = cpu_to_le16(buf_len); - request->req_buf_addr.high_addr = cpu_to_le32(msgbuf->ioctbuf_phys_hi); - request->req_buf_addr.low_addr = cpu_to_le32(msgbuf->ioctbuf_phys_lo); - if (buf) - memcpy(msgbuf->ioctbuf, buf, buf_len); - else - memset(msgbuf->ioctbuf, 0, buf_len); - - err = brcmf_commonring_write_complete(commonring); - brcmf_commonring_unlock(commonring); - - return err; -} - - -static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf) -{ - return wait_event_timeout(msgbuf->ioctl_resp_wait, - msgbuf->ctl_completed, - msecs_to_jiffies(MSGBUF_IOCTL_RESP_TIMEOUT)); -} - - -static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf) -{ - msgbuf->ctl_completed = true; - if (waitqueue_active(&msgbuf->ioctl_resp_wait)) - wake_up(&msgbuf->ioctl_resp_wait); -} - - -static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx, - uint cmd, void *buf, uint len) -{ - struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - struct sk_buff *skb = NULL; - int timeout; - int err; - - brcmf_dbg(MSGBUF, "ifidx=%d, cmd=%d, len=%d\n", ifidx, cmd, len); - msgbuf->ctl_completed = false; - err = brcmf_msgbuf_tx_ioctl(drvr, ifidx, cmd, buf, len); - if (err) - return err; - - timeout = brcmf_msgbuf_ioctl_resp_wait(msgbuf); - if (!timeout) { - brcmf_err("Timeout on response for query command\n"); - return -EIO; - } - - skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, - msgbuf->rx_pktids, - msgbuf->ioctl_resp_pktid); - if (msgbuf->ioctl_resp_ret_len != 0) { - if (!skb) - return -EBADF; - - memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ? - len : msgbuf->ioctl_resp_ret_len); - } - brcmu_pkt_buf_free_skb(skb); - - return msgbuf->ioctl_resp_status; -} - - -static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx, - uint cmd, void *buf, uint len) -{ - return brcmf_msgbuf_query_dcmd(drvr, ifidx, cmd, buf, len); -} - - -static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws, - struct sk_buff *skb, struct brcmf_if **ifp) -{ - return -ENODEV; -} - - -static void -brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid) -{ - u32 dma_sz; - void *dma_buf; - - brcmf_dbg(MSGBUF, "Removing flowring %d\n", flowid); - - dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE; - dma_buf = msgbuf->flowrings[flowid]->buf_addr; - dma_free_coherent(msgbuf->drvr->bus_if->dev, dma_sz, dma_buf, - msgbuf->flowring_dma_handle[flowid]); - - brcmf_flowring_delete(msgbuf->flow, flowid); -} - - -static struct brcmf_msgbuf_work_item * -brcmf_msgbuf_dequeue_work(struct brcmf_msgbuf *msgbuf) -{ - struct brcmf_msgbuf_work_item *work = NULL; - ulong flags; - - spin_lock_irqsave(&msgbuf->flowring_work_lock, flags); - if (!list_empty(&msgbuf->work_queue)) { - work = list_first_entry(&msgbuf->work_queue, - struct brcmf_msgbuf_work_item, queue); - list_del(&work->queue); - } - spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags); - - return work; -} - - -static u32 -brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf, - struct brcmf_msgbuf_work_item *work) -{ - struct msgbuf_tx_flowring_create_req *create; - struct brcmf_commonring *commonring; - void *ret_ptr; - u32 flowid; - void *dma_buf; - u32 dma_sz; - u64 address; - int err; - - flowid = work->flowid; - dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE; - dma_buf = dma_alloc_coherent(msgbuf->drvr->bus_if->dev, dma_sz, - &msgbuf->flowring_dma_handle[flowid], - GFP_KERNEL); - if (!dma_buf) { - brcmf_err("dma_alloc_coherent failed\n"); - brcmf_flowring_delete(msgbuf->flow, flowid); - return BRCMF_FLOWRING_INVALID_ID; - } - - brcmf_commonring_config(msgbuf->flowrings[flowid], - BRCMF_H2D_TXFLOWRING_MAX_ITEM, - BRCMF_H2D_TXFLOWRING_ITEMSIZE, dma_buf); - - commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; - brcmf_commonring_lock(commonring); - ret_ptr = brcmf_commonring_reserve_for_write(commonring); - if (!ret_ptr) { - brcmf_err("Failed to reserve space in commonring\n"); - brcmf_commonring_unlock(commonring); - brcmf_msgbuf_remove_flowring(msgbuf, flowid); - return BRCMF_FLOWRING_INVALID_ID; - } - - create = (struct msgbuf_tx_flowring_create_req *)ret_ptr; - create->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE; - create->msg.ifidx = work->ifidx; - create->msg.request_id = 0; - create->tid = brcmf_flowring_tid(msgbuf->flow, flowid); - create->flow_ring_id = cpu_to_le16(flowid + - BRCMF_NROF_H2D_COMMON_MSGRINGS); - memcpy(create->sa, work->sa, ETH_ALEN); - memcpy(create->da, work->da, ETH_ALEN); - address = (u64)msgbuf->flowring_dma_handle[flowid]; - create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32); - create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff); - create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM); - create->len_item = cpu_to_le16(BRCMF_H2D_TXFLOWRING_ITEMSIZE); - - brcmf_dbg(MSGBUF, "Send Flow Create Req flow ID %d for peer %pM prio %d ifindex %d\n", - flowid, work->da, create->tid, work->ifidx); - - err = brcmf_commonring_write_complete(commonring); - brcmf_commonring_unlock(commonring); - if (err) { - brcmf_err("Failed to write commonring\n"); - brcmf_msgbuf_remove_flowring(msgbuf, flowid); - return BRCMF_FLOWRING_INVALID_ID; - } - - return flowid; -} - - -static void brcmf_msgbuf_flowring_worker(struct work_struct *work) -{ - struct brcmf_msgbuf *msgbuf; - struct brcmf_msgbuf_work_item *create; - - msgbuf = container_of(work, struct brcmf_msgbuf, flowring_work); - - while ((create = brcmf_msgbuf_dequeue_work(msgbuf))) { - brcmf_msgbuf_flowring_create_worker(msgbuf, create); - kfree(create); - } -} - - -static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, - struct sk_buff *skb) -{ - struct brcmf_msgbuf_work_item *create; - struct ethhdr *eh = (struct ethhdr *)(skb->data); - u32 flowid; - ulong flags; - - create = kzalloc(sizeof(*create), GFP_ATOMIC); - if (create == NULL) - return BRCMF_FLOWRING_INVALID_ID; - - flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest, - skb->priority, ifidx); - if (flowid == BRCMF_FLOWRING_INVALID_ID) { - kfree(create); - return flowid; - } - - create->flowid = flowid; - create->ifidx = ifidx; - memcpy(create->sa, eh->h_source, ETH_ALEN); - memcpy(create->da, eh->h_dest, ETH_ALEN); - - spin_lock_irqsave(&msgbuf->flowring_work_lock, flags); - list_add_tail(&create->queue, &msgbuf->work_queue); - spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags); - schedule_work(&msgbuf->flowring_work); - - return flowid; -} - - -static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) -{ - struct brcmf_flowring *flow = msgbuf->flow; - struct brcmf_commonring *commonring; - void *ret_ptr; - u32 count; - struct sk_buff *skb; - dma_addr_t physaddr; - u32 pktid; - struct msgbuf_tx_msghdr *tx_msghdr; - u64 address; - - commonring = msgbuf->flowrings[flowid]; - if (!brcmf_commonring_write_available(commonring)) - return; - - brcmf_commonring_lock(commonring); - - count = BRCMF_MSGBUF_TX_FLUSH_CNT2 - BRCMF_MSGBUF_TX_FLUSH_CNT1; - while (brcmf_flowring_qlen(flow, flowid)) { - skb = brcmf_flowring_dequeue(flow, flowid); - if (skb == NULL) { - brcmf_err("No SKB, but qlen %d\n", - brcmf_flowring_qlen(flow, flowid)); - break; - } - skb_orphan(skb); - if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, - msgbuf->tx_pktids, skb, ETH_HLEN, - &physaddr, &pktid)) { - brcmf_flowring_reinsert(flow, flowid, skb); - brcmf_err("No PKTID available !!\n"); - break; - } - ret_ptr = brcmf_commonring_reserve_for_write(commonring); - if (!ret_ptr) { - brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, - msgbuf->tx_pktids, pktid); - brcmf_flowring_reinsert(flow, flowid, skb); - break; - } - count++; - - tx_msghdr = (struct msgbuf_tx_msghdr *)ret_ptr; - - tx_msghdr->msg.msgtype = MSGBUF_TYPE_TX_POST; - tx_msghdr->msg.request_id = cpu_to_le32(pktid); - tx_msghdr->msg.ifidx = brcmf_flowring_ifidx_get(flow, flowid); - tx_msghdr->flags = BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3; - tx_msghdr->flags |= (skb->priority & 0x07) << - BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT; - tx_msghdr->seg_cnt = 1; - memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN); - tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN); - address = (u64)physaddr; - tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32); - tx_msghdr->data_buf_addr.low_addr = - cpu_to_le32(address & 0xffffffff); - tx_msghdr->metadata_buf_len = 0; - tx_msghdr->metadata_buf_addr.high_addr = 0; - tx_msghdr->metadata_buf_addr.low_addr = 0; - atomic_inc(&commonring->outstanding_tx); - if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) { - brcmf_commonring_write_complete(commonring); - count = 0; - } - } - if (count) - brcmf_commonring_write_complete(commonring); - brcmf_commonring_unlock(commonring); -} - - -static void brcmf_msgbuf_txflow_worker(struct work_struct *worker) -{ - struct brcmf_msgbuf *msgbuf; - u32 flowid; - - msgbuf = container_of(worker, struct brcmf_msgbuf, txflow_work); - for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->nrof_flowrings) { - clear_bit(flowid, msgbuf->flow_map); - brcmf_msgbuf_txflow(msgbuf, flowid); - } -} - - -static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid, - bool force) -{ - struct brcmf_commonring *commonring; - - set_bit(flowid, msgbuf->flow_map); - commonring = msgbuf->flowrings[flowid]; - if ((force) || (atomic_read(&commonring->outstanding_tx) < - BRCMF_MSGBUF_DELAY_TXWORKER_THRS)) - queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work); - - return 0; -} - - -static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, - u8 offset, struct sk_buff *skb) -{ - struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - struct brcmf_flowring *flow = msgbuf->flow; - struct ethhdr *eh = (struct ethhdr *)(skb->data); - u32 flowid; - u32 queue_count; - bool force; - - flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx); - if (flowid == BRCMF_FLOWRING_INVALID_ID) { - flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb); - if (flowid == BRCMF_FLOWRING_INVALID_ID) - return -ENOMEM; - } - queue_count = brcmf_flowring_enqueue(flow, flowid, skb); - force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0); - brcmf_msgbuf_schedule_txdata(msgbuf, flowid, force); - - return 0; -} - - -static void -brcmf_msgbuf_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, - enum proto_addr_mode addr_mode) -{ - struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - - brcmf_flowring_configure_addr_mode(msgbuf->flow, ifidx, addr_mode); -} - - -static void -brcmf_msgbuf_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) -{ - struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - - brcmf_flowring_delete_peer(msgbuf->flow, ifidx, peer); -} - - -static void -brcmf_msgbuf_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) -{ - struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - - brcmf_flowring_add_tdls_peer(msgbuf->flow, ifidx, peer); -} - - -static void -brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf) -{ - struct msgbuf_ioctl_resp_hdr *ioctl_resp; - - ioctl_resp = (struct msgbuf_ioctl_resp_hdr *)buf; - - msgbuf->ioctl_resp_status = - (s16)le16_to_cpu(ioctl_resp->compl_hdr.status); - msgbuf->ioctl_resp_ret_len = le16_to_cpu(ioctl_resp->resp_len); - msgbuf->ioctl_resp_pktid = le32_to_cpu(ioctl_resp->msg.request_id); - - brcmf_msgbuf_ioctl_resp_wake(msgbuf); - - if (msgbuf->cur_ioctlrespbuf) - msgbuf->cur_ioctlrespbuf--; - brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf); -} - - -static void -brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) -{ - struct brcmf_commonring *commonring; - struct msgbuf_tx_status *tx_status; - u32 idx; - struct sk_buff *skb; - u16 flowid; - - tx_status = (struct msgbuf_tx_status *)buf; - idx = le32_to_cpu(tx_status->msg.request_id); - flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id); - flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; - skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, - msgbuf->tx_pktids, idx); - if (!skb) - return; - - set_bit(flowid, msgbuf->txstatus_done_map); - commonring = msgbuf->flowrings[flowid]; - atomic_dec(&commonring->outstanding_tx); - - brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx), - skb, true); -} - - -static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) -{ - struct brcmf_commonring *commonring; - void *ret_ptr; - struct sk_buff *skb; - u16 alloced; - u32 pktlen; - dma_addr_t physaddr; - struct msgbuf_rx_bufpost *rx_bufpost; - u64 address; - u32 pktid; - u32 i; - - commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT]; - ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring, - count, - &alloced); - if (!ret_ptr) { - brcmf_dbg(MSGBUF, "Failed to reserve space in commonring\n"); - return 0; - } - - for (i = 0; i < alloced; i++) { - rx_bufpost = (struct msgbuf_rx_bufpost *)ret_ptr; - memset(rx_bufpost, 0, sizeof(*rx_bufpost)); - - skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE); - - if (skb == NULL) { - brcmf_err("Failed to alloc SKB\n"); - brcmf_commonring_write_cancel(commonring, alloced - i); - break; - } - - pktlen = skb->len; - if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, - msgbuf->rx_pktids, skb, 0, - &physaddr, &pktid)) { - dev_kfree_skb_any(skb); - brcmf_err("No PKTID available !!\n"); - brcmf_commonring_write_cancel(commonring, alloced - i); - break; - } - - if (msgbuf->rx_metadata_offset) { - address = (u64)physaddr; - rx_bufpost->metadata_buf_len = - cpu_to_le16(msgbuf->rx_metadata_offset); - rx_bufpost->metadata_buf_addr.high_addr = - cpu_to_le32(address >> 32); - rx_bufpost->metadata_buf_addr.low_addr = - cpu_to_le32(address & 0xffffffff); - - skb_pull(skb, msgbuf->rx_metadata_offset); - pktlen = skb->len; - physaddr += msgbuf->rx_metadata_offset; - } - rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST; - rx_bufpost->msg.request_id = cpu_to_le32(pktid); - - address = (u64)physaddr; - rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen); - rx_bufpost->data_buf_addr.high_addr = - cpu_to_le32(address >> 32); - rx_bufpost->data_buf_addr.low_addr = - cpu_to_le32(address & 0xffffffff); - - ret_ptr += brcmf_commonring_len_item(commonring); - } - - if (i) - brcmf_commonring_write_complete(commonring); - - return i; -} - - -static void -brcmf_msgbuf_rxbuf_data_fill(struct brcmf_msgbuf *msgbuf) -{ - u32 fillbufs; - u32 retcount; - - fillbufs = msgbuf->max_rxbufpost - msgbuf->rxbufpost; - - while (fillbufs) { - retcount = brcmf_msgbuf_rxbuf_data_post(msgbuf, fillbufs); - if (!retcount) - break; - msgbuf->rxbufpost += retcount; - fillbufs -= retcount; - } -} - - -static void -brcmf_msgbuf_update_rxbufpost_count(struct brcmf_msgbuf *msgbuf, u16 rxcnt) -{ - msgbuf->rxbufpost -= rxcnt; - if (msgbuf->rxbufpost <= (msgbuf->max_rxbufpost - - BRCMF_MSGBUF_RXBUFPOST_THRESHOLD)) - brcmf_msgbuf_rxbuf_data_fill(msgbuf); -} - - -static u32 -brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf, - u32 count) -{ - struct brcmf_commonring *commonring; - void *ret_ptr; - struct sk_buff *skb; - u16 alloced; - u32 pktlen; - dma_addr_t physaddr; - struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost; - u64 address; - u32 pktid; - u32 i; - - commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; - brcmf_commonring_lock(commonring); - ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring, - count, - &alloced); - if (!ret_ptr) { - brcmf_err("Failed to reserve space in commonring\n"); - brcmf_commonring_unlock(commonring); - return 0; - } - - for (i = 0; i < alloced; i++) { - rx_bufpost = (struct msgbuf_rx_ioctl_resp_or_event *)ret_ptr; - memset(rx_bufpost, 0, sizeof(*rx_bufpost)); - - skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE); - - if (skb == NULL) { - brcmf_err("Failed to alloc SKB\n"); - brcmf_commonring_write_cancel(commonring, alloced - i); - break; - } - - pktlen = skb->len; - if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, - msgbuf->rx_pktids, skb, 0, - &physaddr, &pktid)) { - dev_kfree_skb_any(skb); - brcmf_err("No PKTID available !!\n"); - brcmf_commonring_write_cancel(commonring, alloced - i); - break; - } - if (event_buf) - rx_bufpost->msg.msgtype = MSGBUF_TYPE_EVENT_BUF_POST; - else - rx_bufpost->msg.msgtype = - MSGBUF_TYPE_IOCTLRESP_BUF_POST; - rx_bufpost->msg.request_id = cpu_to_le32(pktid); - - address = (u64)physaddr; - rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen); - rx_bufpost->host_buf_addr.high_addr = - cpu_to_le32(address >> 32); - rx_bufpost->host_buf_addr.low_addr = - cpu_to_le32(address & 0xffffffff); - - ret_ptr += brcmf_commonring_len_item(commonring); - } - - if (i) - brcmf_commonring_write_complete(commonring); - - brcmf_commonring_unlock(commonring); - - return i; -} - - -static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf) -{ - u32 count; - - count = msgbuf->max_ioctlrespbuf - msgbuf->cur_ioctlrespbuf; - count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, false, count); - msgbuf->cur_ioctlrespbuf += count; -} - - -static void brcmf_msgbuf_rxbuf_event_post(struct brcmf_msgbuf *msgbuf) -{ - u32 count; - - count = msgbuf->max_eventbuf - msgbuf->cur_eventbuf; - count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, true, count); - msgbuf->cur_eventbuf += count; -} - - -static void -brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb, - u8 ifidx) -{ - struct brcmf_if *ifp; - - ifp = brcmf_get_ifp(msgbuf->drvr, ifidx); - if (!ifp || !ifp->ndev) { - brcmf_err("Received pkt for invalid ifidx %d\n", ifidx); - brcmu_pkt_buf_free_skb(skb); - return; - } - brcmf_netif_rx(ifp, skb); -} - - -static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf) -{ - struct msgbuf_rx_event *event; - u32 idx; - u16 buflen; - struct sk_buff *skb; - - event = (struct msgbuf_rx_event *)buf; - idx = le32_to_cpu(event->msg.request_id); - buflen = le16_to_cpu(event->event_data_len); - - if (msgbuf->cur_eventbuf) - msgbuf->cur_eventbuf--; - brcmf_msgbuf_rxbuf_event_post(msgbuf); - - skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, - msgbuf->rx_pktids, idx); - if (!skb) - return; - - if (msgbuf->rx_dataoffset) - skb_pull(skb, msgbuf->rx_dataoffset); - - skb_trim(skb, buflen); - - brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx); -} - - -static void -brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) -{ - struct msgbuf_rx_complete *rx_complete; - struct sk_buff *skb; - u16 data_offset; - u16 buflen; - u32 idx; - - brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1); - - rx_complete = (struct msgbuf_rx_complete *)buf; - data_offset = le16_to_cpu(rx_complete->data_offset); - buflen = le16_to_cpu(rx_complete->data_len); - idx = le32_to_cpu(rx_complete->msg.request_id); - - skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, - msgbuf->rx_pktids, idx); - if (!skb) - return; - - if (data_offset) - skb_pull(skb, data_offset); - else if (msgbuf->rx_dataoffset) - skb_pull(skb, msgbuf->rx_dataoffset); - - skb_trim(skb, buflen); - - brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx); -} - - -static void -brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf, - void *buf) -{ - struct msgbuf_flowring_create_resp *flowring_create_resp; - u16 status; - u16 flowid; - - flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf; - - flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id); - flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; - status = le16_to_cpu(flowring_create_resp->compl_hdr.status); - - if (status) { - brcmf_err("Flowring creation failed, code %d\n", status); - brcmf_msgbuf_remove_flowring(msgbuf, flowid); - return; - } - brcmf_dbg(MSGBUF, "Flowring %d Create response status %d\n", flowid, - status); - - brcmf_flowring_open(msgbuf->flow, flowid); - - brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true); -} - - -static void -brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf, - void *buf) -{ - struct msgbuf_flowring_delete_resp *flowring_delete_resp; - u16 status; - u16 flowid; - - flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf; - - flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id); - flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; - status = le16_to_cpu(flowring_delete_resp->compl_hdr.status); - - if (status) { - brcmf_err("Flowring deletion failed, code %d\n", status); - brcmf_flowring_delete(msgbuf->flow, flowid); - return; - } - brcmf_dbg(MSGBUF, "Flowring %d Delete response status %d\n", flowid, - status); - - brcmf_msgbuf_remove_flowring(msgbuf, flowid); -} - - -static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf) -{ - struct msgbuf_common_hdr *msg; - - msg = (struct msgbuf_common_hdr *)buf; - switch (msg->msgtype) { - case MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT: - brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT\n"); - brcmf_msgbuf_process_flow_ring_create_response(msgbuf, buf); - break; - case MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT: - brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT\n"); - brcmf_msgbuf_process_flow_ring_delete_response(msgbuf, buf); - break; - case MSGBUF_TYPE_IOCTLPTR_REQ_ACK: - brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTLPTR_REQ_ACK\n"); - break; - case MSGBUF_TYPE_IOCTL_CMPLT: - brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTL_CMPLT\n"); - brcmf_msgbuf_process_ioctl_complete(msgbuf, buf); - break; - case MSGBUF_TYPE_WL_EVENT: - brcmf_dbg(MSGBUF, "MSGBUF_TYPE_WL_EVENT\n"); - brcmf_msgbuf_process_event(msgbuf, buf); - break; - case MSGBUF_TYPE_TX_STATUS: - brcmf_dbg(MSGBUF, "MSGBUF_TYPE_TX_STATUS\n"); - brcmf_msgbuf_process_txstatus(msgbuf, buf); - break; - case MSGBUF_TYPE_RX_CMPLT: - brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n"); - brcmf_msgbuf_process_rx_complete(msgbuf, buf); - break; - default: - brcmf_err("Unsupported msgtype %d\n", msg->msgtype); - break; - } -} - - -static void brcmf_msgbuf_process_rx(struct brcmf_msgbuf *msgbuf, - struct brcmf_commonring *commonring) -{ - void *buf; - u16 count; - u16 processed; - -again: - buf = brcmf_commonring_get_read_ptr(commonring, &count); - if (buf == NULL) - return; - - processed = 0; - while (count) { - brcmf_msgbuf_process_msgtype(msgbuf, - buf + msgbuf->rx_dataoffset); - buf += brcmf_commonring_len_item(commonring); - processed++; - if (processed == BRCMF_MSGBUF_UPDATE_RX_PTR_THRS) { - brcmf_commonring_read_complete(commonring, processed); - processed = 0; - } - count--; - } - if (processed) - brcmf_commonring_read_complete(commonring, processed); - - if (commonring->r_ptr == 0) - goto again; -} - - -int brcmf_proto_msgbuf_rx_trigger(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - struct brcmf_commonring *commonring; - void *buf; - u32 flowid; - int qlen; - - buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE]; - brcmf_msgbuf_process_rx(msgbuf, buf); - buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE]; - brcmf_msgbuf_process_rx(msgbuf, buf); - buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE]; - brcmf_msgbuf_process_rx(msgbuf, buf); - - for_each_set_bit(flowid, msgbuf->txstatus_done_map, - msgbuf->nrof_flowrings) { - clear_bit(flowid, msgbuf->txstatus_done_map); - commonring = msgbuf->flowrings[flowid]; - qlen = brcmf_flowring_qlen(msgbuf->flow, flowid); - if ((qlen > BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) || - ((qlen) && (atomic_read(&commonring->outstanding_tx) < - BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS))) - brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true); - } - - return 0; -} - - -void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid) -{ - struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - struct msgbuf_tx_flowring_delete_req *delete; - struct brcmf_commonring *commonring; - void *ret_ptr; - u8 ifidx; - int err; - - commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; - brcmf_commonring_lock(commonring); - ret_ptr = brcmf_commonring_reserve_for_write(commonring); - if (!ret_ptr) { - brcmf_err("FW unaware, flowring will be removed !!\n"); - brcmf_commonring_unlock(commonring); - brcmf_msgbuf_remove_flowring(msgbuf, flowid); - return; - } - - delete = (struct msgbuf_tx_flowring_delete_req *)ret_ptr; - - ifidx = brcmf_flowring_ifidx_get(msgbuf->flow, flowid); - - delete->msg.msgtype = MSGBUF_TYPE_FLOW_RING_DELETE; - delete->msg.ifidx = ifidx; - delete->msg.request_id = 0; - - delete->flow_ring_id = cpu_to_le16(flowid + - BRCMF_NROF_H2D_COMMON_MSGRINGS); - delete->reason = 0; - - brcmf_dbg(MSGBUF, "Send Flow Delete Req flow ID %d, ifindex %d\n", - flowid, ifidx); - - err = brcmf_commonring_write_complete(commonring); - brcmf_commonring_unlock(commonring); - if (err) { - brcmf_err("Failed to submit RING_DELETE, flowring will be removed\n"); - brcmf_msgbuf_remove_flowring(msgbuf, flowid); - } -} - -#ifdef DEBUG -static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); - struct brcmf_pub *drvr = bus_if->drvr; - struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - struct brcmf_commonring *commonring; - u16 i; - struct brcmf_flowring_ring *ring; - struct brcmf_flowring_hash *hash; - - commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; - seq_printf(seq, "h2d_ctl_submit: rp %4u, wp %4u, depth %4u\n", - commonring->r_ptr, commonring->w_ptr, commonring->depth); - commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT]; - seq_printf(seq, "h2d_rx_submit: rp %4u, wp %4u, depth %4u\n", - commonring->r_ptr, commonring->w_ptr, commonring->depth); - commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE]; - seq_printf(seq, "d2h_ctl_cmplt: rp %4u, wp %4u, depth %4u\n", - commonring->r_ptr, commonring->w_ptr, commonring->depth); - commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE]; - seq_printf(seq, "d2h_tx_cmplt: rp %4u, wp %4u, depth %4u\n", - commonring->r_ptr, commonring->w_ptr, commonring->depth); - commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE]; - seq_printf(seq, "d2h_rx_cmplt: rp %4u, wp %4u, depth %4u\n", - commonring->r_ptr, commonring->w_ptr, commonring->depth); - - seq_printf(seq, "\nh2d_flowrings: depth %u\n", - BRCMF_H2D_TXFLOWRING_MAX_ITEM); - seq_puts(seq, "Active flowrings:\n"); - hash = msgbuf->flow->hash; - for (i = 0; i < msgbuf->flow->nrofrings; i++) { - if (!msgbuf->flow->rings[i]) - continue; - ring = msgbuf->flow->rings[i]; - if (ring->status != RING_OPEN) - continue; - commonring = msgbuf->flowrings[i]; - hash = &msgbuf->flow->hash[ring->hash_id]; - seq_printf(seq, "id %3u: rp %4u, wp %4u, qlen %4u, blocked %u\n" - " ifidx %u, fifo %u, da %pM\n", - i, commonring->r_ptr, commonring->w_ptr, - skb_queue_len(&ring->skblist), ring->blocked, - hash->ifidx, hash->fifo, hash->mac); - } - - return 0; -} -#else -static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data) -{ - return 0; -} -#endif - -int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) -{ - struct brcmf_bus_msgbuf *if_msgbuf; - struct brcmf_msgbuf *msgbuf; - u64 address; - u32 count; - - if_msgbuf = drvr->bus_if->msgbuf; - msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL); - if (!msgbuf) - goto fail; - - msgbuf->txflow_wq = create_singlethread_workqueue("msgbuf_txflow"); - if (msgbuf->txflow_wq == NULL) { - brcmf_err("workqueue creation failed\n"); - goto fail; - } - INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker); - count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings); - count = count * sizeof(unsigned long); - msgbuf->flow_map = kzalloc(count, GFP_KERNEL); - if (!msgbuf->flow_map) - goto fail; - - msgbuf->txstatus_done_map = kzalloc(count, GFP_KERNEL); - if (!msgbuf->txstatus_done_map) - goto fail; - - msgbuf->drvr = drvr; - msgbuf->ioctbuf = dma_alloc_coherent(drvr->bus_if->dev, - BRCMF_TX_IOCTL_MAX_MSG_SIZE, - &msgbuf->ioctbuf_handle, - GFP_KERNEL); - if (!msgbuf->ioctbuf) - goto fail; - address = (u64)msgbuf->ioctbuf_handle; - msgbuf->ioctbuf_phys_hi = address >> 32; - msgbuf->ioctbuf_phys_lo = address & 0xffffffff; - - drvr->proto->hdrpull = brcmf_msgbuf_hdrpull; - drvr->proto->query_dcmd = brcmf_msgbuf_query_dcmd; - drvr->proto->set_dcmd = brcmf_msgbuf_set_dcmd; - drvr->proto->txdata = brcmf_msgbuf_txdata; - drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode; - drvr->proto->delete_peer = brcmf_msgbuf_delete_peer; - drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer; - drvr->proto->pd = msgbuf; - - init_waitqueue_head(&msgbuf->ioctl_resp_wait); - - msgbuf->commonrings = - (struct brcmf_commonring **)if_msgbuf->commonrings; - msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings; - msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings; - msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings * - sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL); - if (!msgbuf->flowring_dma_handle) - goto fail; - - msgbuf->rx_dataoffset = if_msgbuf->rx_dataoffset; - msgbuf->max_rxbufpost = if_msgbuf->max_rxbufpost; - - msgbuf->max_ioctlrespbuf = BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST; - msgbuf->max_eventbuf = BRCMF_MSGBUF_MAX_EVENTBUF_POST; - - msgbuf->tx_pktids = brcmf_msgbuf_init_pktids(NR_TX_PKTIDS, - DMA_TO_DEVICE); - if (!msgbuf->tx_pktids) - goto fail; - msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS, - DMA_FROM_DEVICE); - if (!msgbuf->rx_pktids) - goto fail; - - msgbuf->flow = brcmf_flowring_attach(drvr->bus_if->dev, - if_msgbuf->nrof_flowrings); - if (!msgbuf->flow) - goto fail; - - - brcmf_dbg(MSGBUF, "Feeding buffers, rx data %d, rx event %d, rx ioctl resp %d\n", - msgbuf->max_rxbufpost, msgbuf->max_eventbuf, - msgbuf->max_ioctlrespbuf); - count = 0; - do { - brcmf_msgbuf_rxbuf_data_fill(msgbuf); - if (msgbuf->max_rxbufpost != msgbuf->rxbufpost) - msleep(10); - else - break; - count++; - } while (count < 10); - brcmf_msgbuf_rxbuf_event_post(msgbuf); - brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf); - - INIT_WORK(&msgbuf->flowring_work, brcmf_msgbuf_flowring_worker); - spin_lock_init(&msgbuf->flowring_work_lock); - INIT_LIST_HEAD(&msgbuf->work_queue); - - brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read); - - return 0; - -fail: - if (msgbuf) { - kfree(msgbuf->flow_map); - kfree(msgbuf->txstatus_done_map); - brcmf_msgbuf_release_pktids(msgbuf); - kfree(msgbuf->flowring_dma_handle); - if (msgbuf->ioctbuf) - dma_free_coherent(drvr->bus_if->dev, - BRCMF_TX_IOCTL_MAX_MSG_SIZE, - msgbuf->ioctbuf, - msgbuf->ioctbuf_handle); - kfree(msgbuf); - } - return -ENOMEM; -} - - -void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) -{ - struct brcmf_msgbuf *msgbuf; - struct brcmf_msgbuf_work_item *work; - - brcmf_dbg(TRACE, "Enter\n"); - if (drvr->proto->pd) { - msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - cancel_work_sync(&msgbuf->flowring_work); - while (!list_empty(&msgbuf->work_queue)) { - work = list_first_entry(&msgbuf->work_queue, - struct brcmf_msgbuf_work_item, - queue); - list_del(&work->queue); - kfree(work); - } - kfree(msgbuf->flow_map); - kfree(msgbuf->txstatus_done_map); - if (msgbuf->txflow_wq) - destroy_workqueue(msgbuf->txflow_wq); - - brcmf_flowring_detach(msgbuf->flow); - dma_free_coherent(drvr->bus_if->dev, - BRCMF_TX_IOCTL_MAX_MSG_SIZE, - msgbuf->ioctbuf, msgbuf->ioctbuf_handle); - brcmf_msgbuf_release_pktids(msgbuf); - kfree(msgbuf->flowring_dma_handle); - kfree(msgbuf); - drvr->proto->pd = NULL; - } -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h deleted file mode 100644 index 3d513e407e3d..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (c) 2014 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_MSGBUF_H -#define BRCMFMAC_MSGBUF_H - -#ifdef CONFIG_BRCMFMAC_PROTO_MSGBUF - -#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 64 -#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 512 -#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 64 -#define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM 1024 -#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 512 -#define BRCMF_H2D_TXFLOWRING_MAX_ITEM 512 - -#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE 40 -#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE 32 -#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE 24 -#define BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE 16 -#define BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE 32 -#define BRCMF_H2D_TXFLOWRING_ITEMSIZE 48 - - -int brcmf_proto_msgbuf_rx_trigger(struct device *dev); -void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid); -int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr); -void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr); -#else -static inline int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) -{ - return 0; -} -static inline void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) {} -#endif - -#endif /* BRCMFMAC_MSGBUF_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c deleted file mode 100644 index 03f35e0c52ca..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/of.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2014 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 -#include -#include - -#include -#include "debug.h" -#include "sdio.h" - -void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) -{ - struct device *dev = sdiodev->dev; - struct device_node *np = dev->of_node; - int irq; - u32 irqf; - u32 val; - - if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac")) - return; - - sdiodev->pdata = devm_kzalloc(dev, sizeof(*sdiodev->pdata), GFP_KERNEL); - if (!sdiodev->pdata) - return; - - if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) - sdiodev->pdata->drive_strength = val; - - /* make sure there are interrupts defined in the node */ - if (!of_find_property(np, "interrupts", NULL)) - return; - - irq = irq_of_parse_and_map(np, 0); - if (!irq) { - brcmf_err("interrupt could not be mapped\n"); - return; - } - irqf = irqd_get_trigger_type(irq_get_irq_data(irq)); - - sdiodev->pdata->oob_irq_supported = true; - sdiodev->pdata->oob_irq_nr = irq; - sdiodev->pdata->oob_irq_flags = irqf; -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.h b/drivers/net/wireless/brcm80211/brcmfmac/of.h deleted file mode 100644 index 5f7c3550deda..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/of.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2014 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. - */ -#ifdef CONFIG_OF -void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev); -#else -static void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) -{ -} -#endif /* CONFIG_OF */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c deleted file mode 100644 index d224b3dd72ed..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ /dev/null @@ -1,2401 +0,0 @@ -/* - * Copyright (c) 2012 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 -#include - -#include -#include -#include -#include "core.h" -#include "debug.h" -#include "fwil.h" -#include "fwil_types.h" -#include "p2p.h" -#include "cfg80211.h" - -/* parameters used for p2p escan */ -#define P2PAPI_SCAN_NPROBES 1 -#define P2PAPI_SCAN_DWELL_TIME_MS 80 -#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40 -#define P2PAPI_SCAN_HOME_TIME_MS 60 -#define P2PAPI_SCAN_NPROBS_TIME_MS 30 -#define P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS 100 -#define WL_SCAN_CONNECT_DWELL_TIME_MS 200 -#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20 - -#define BRCMF_P2P_WILDCARD_SSID "DIRECT-" -#define BRCMF_P2P_WILDCARD_SSID_LEN (sizeof(BRCMF_P2P_WILDCARD_SSID) - 1) - -#define SOCIAL_CHAN_1 1 -#define SOCIAL_CHAN_2 6 -#define SOCIAL_CHAN_3 11 -#define IS_P2P_SOCIAL_CHANNEL(channel) ((channel == SOCIAL_CHAN_1) || \ - (channel == SOCIAL_CHAN_2) || \ - (channel == SOCIAL_CHAN_3)) -#define BRCMF_P2P_TEMP_CHAN SOCIAL_CHAN_3 -#define SOCIAL_CHAN_CNT 3 -#define AF_PEER_SEARCH_CNT 2 - -#define BRCMF_SCB_TIMEOUT_VALUE 20 - -#define P2P_VER 9 /* P2P version: 9=WiFi P2P v1.0 */ -#define P2P_PUB_AF_CATEGORY 0x04 -#define P2P_PUB_AF_ACTION 0x09 -#define P2P_AF_CATEGORY 0x7f -#define P2P_OUI "\x50\x6F\x9A" /* P2P OUI */ -#define P2P_OUI_LEN 3 /* P2P OUI length */ - -/* Action Frame Constants */ -#define DOT11_ACTION_HDR_LEN 2 /* action frame category + action */ -#define DOT11_ACTION_CAT_OFF 0 /* category offset */ -#define DOT11_ACTION_ACT_OFF 1 /* action offset */ - -#define P2P_AF_DWELL_TIME 200 -#define P2P_AF_MIN_DWELL_TIME 100 -#define P2P_AF_MED_DWELL_TIME 400 -#define P2P_AF_LONG_DWELL_TIME 1000 -#define P2P_AF_TX_MAX_RETRY 1 -#define P2P_AF_MAX_WAIT_TIME 2000 -#define P2P_INVALID_CHANNEL -1 -#define P2P_CHANNEL_SYNC_RETRY 5 -#define P2P_AF_FRM_SCAN_MAX_WAIT 1500 -#define P2P_DEFAULT_SLEEP_TIME_VSDB 200 - -/* WiFi P2P Public Action Frame OUI Subtypes */ -#define P2P_PAF_GON_REQ 0 /* Group Owner Negotiation Req */ -#define P2P_PAF_GON_RSP 1 /* Group Owner Negotiation Rsp */ -#define P2P_PAF_GON_CONF 2 /* Group Owner Negotiation Confirm */ -#define P2P_PAF_INVITE_REQ 3 /* P2P Invitation Request */ -#define P2P_PAF_INVITE_RSP 4 /* P2P Invitation Response */ -#define P2P_PAF_DEVDIS_REQ 5 /* Device Discoverability Request */ -#define P2P_PAF_DEVDIS_RSP 6 /* Device Discoverability Response */ -#define P2P_PAF_PROVDIS_REQ 7 /* Provision Discovery Request */ -#define P2P_PAF_PROVDIS_RSP 8 /* Provision Discovery Response */ -#define P2P_PAF_SUBTYPE_INVALID 255 /* Invalid Subtype */ - -/* WiFi P2P Action Frame OUI Subtypes */ -#define P2P_AF_NOTICE_OF_ABSENCE 0 /* Notice of Absence */ -#define P2P_AF_PRESENCE_REQ 1 /* P2P Presence Request */ -#define P2P_AF_PRESENCE_RSP 2 /* P2P Presence Response */ -#define P2P_AF_GO_DISC_REQ 3 /* GO Discoverability Request */ - -/* P2P Service Discovery related */ -#define P2PSD_ACTION_CATEGORY 0x04 /* Public action frame */ -#define P2PSD_ACTION_ID_GAS_IREQ 0x0a /* GAS Initial Request AF */ -#define P2PSD_ACTION_ID_GAS_IRESP 0x0b /* GAS Initial Response AF */ -#define P2PSD_ACTION_ID_GAS_CREQ 0x0c /* GAS Comback Request AF */ -#define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comback Response AF */ - -/** - * struct brcmf_p2p_disc_st_le - set discovery state in firmware. - * - * @state: requested discovery state (see enum brcmf_p2p_disc_state). - * @chspec: channel parameter for %WL_P2P_DISC_ST_LISTEN state. - * @dwell: dwell time in ms for %WL_P2P_DISC_ST_LISTEN state. - */ -struct brcmf_p2p_disc_st_le { - u8 state; - __le16 chspec; - __le16 dwell; -}; - -/** - * enum brcmf_p2p_disc_state - P2P discovery state values - * - * @WL_P2P_DISC_ST_SCAN: P2P discovery with wildcard SSID and P2P IE. - * @WL_P2P_DISC_ST_LISTEN: P2P discovery off-channel for specified time. - * @WL_P2P_DISC_ST_SEARCH: P2P discovery with P2P wildcard SSID and P2P IE. - */ -enum brcmf_p2p_disc_state { - WL_P2P_DISC_ST_SCAN, - WL_P2P_DISC_ST_LISTEN, - WL_P2P_DISC_ST_SEARCH -}; - -/** - * struct brcmf_p2p_scan_le - P2P specific scan request. - * - * @type: type of scan method requested (values: 'E' or 'S'). - * @reserved: reserved (ignored). - * @eparams: parameters used for type 'E'. - * @sparams: parameters used for type 'S'. - */ -struct brcmf_p2p_scan_le { - u8 type; - u8 reserved[3]; - union { - struct brcmf_escan_params_le eparams; - struct brcmf_scan_params_le sparams; - }; -}; - -/** - * struct brcmf_p2p_pub_act_frame - WiFi P2P Public Action Frame - * - * @category: P2P_PUB_AF_CATEGORY - * @action: P2P_PUB_AF_ACTION - * @oui[3]: P2P_OUI - * @oui_type: OUI type - P2P_VER - * @subtype: OUI subtype - P2P_TYPE_* - * @dialog_token: nonzero, identifies req/rsp transaction - * @elts[1]: Variable length information elements. - */ -struct brcmf_p2p_pub_act_frame { - u8 category; - u8 action; - u8 oui[3]; - u8 oui_type; - u8 subtype; - u8 dialog_token; - u8 elts[1]; -}; - -/** - * struct brcmf_p2p_action_frame - WiFi P2P Action Frame - * - * @category: P2P_AF_CATEGORY - * @OUI[3]: OUI - P2P_OUI - * @type: OUI Type - P2P_VER - * @subtype: OUI Subtype - P2P_AF_* - * @dialog_token: nonzero, identifies req/resp tranaction - * @elts[1]: Variable length information elements. - */ -struct brcmf_p2p_action_frame { - u8 category; - u8 oui[3]; - u8 type; - u8 subtype; - u8 dialog_token; - u8 elts[1]; -}; - -/** - * struct brcmf_p2psd_gas_pub_act_frame - Wi-Fi GAS Public Action Frame - * - * @category: 0x04 Public Action Frame - * @action: 0x6c Advertisement Protocol - * @dialog_token: nonzero, identifies req/rsp transaction - * @query_data[1]: Query Data. SD gas ireq SD gas iresp - */ -struct brcmf_p2psd_gas_pub_act_frame { - u8 category; - u8 action; - u8 dialog_token; - u8 query_data[1]; -}; - -/** - * struct brcmf_config_af_params - Action Frame Parameters for tx. - * - * @mpc_onoff: To make sure to send successfully action frame, we have to - * turn off mpc 0: off, 1: on, (-1): do nothing - * @search_channel: 1: search peer's channel to send af - * extra_listen: keep the dwell time to get af response frame. - */ -struct brcmf_config_af_params { - s32 mpc_onoff; - bool search_channel; - bool extra_listen; -}; - -/** - * brcmf_p2p_is_pub_action() - true if p2p public type frame. - * - * @frame: action frame data. - * @frame_len: length of action frame data. - * - * Determine if action frame is p2p public action type - */ -static bool brcmf_p2p_is_pub_action(void *frame, u32 frame_len) -{ - struct brcmf_p2p_pub_act_frame *pact_frm; - - if (frame == NULL) - return false; - - pact_frm = (struct brcmf_p2p_pub_act_frame *)frame; - if (frame_len < sizeof(struct brcmf_p2p_pub_act_frame) - 1) - return false; - - if (pact_frm->category == P2P_PUB_AF_CATEGORY && - pact_frm->action == P2P_PUB_AF_ACTION && - pact_frm->oui_type == P2P_VER && - memcmp(pact_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0) - return true; - - return false; -} - -/** - * brcmf_p2p_is_p2p_action() - true if p2p action type frame. - * - * @frame: action frame data. - * @frame_len: length of action frame data. - * - * Determine if action frame is p2p action type - */ -static bool brcmf_p2p_is_p2p_action(void *frame, u32 frame_len) -{ - struct brcmf_p2p_action_frame *act_frm; - - if (frame == NULL) - return false; - - act_frm = (struct brcmf_p2p_action_frame *)frame; - if (frame_len < sizeof(struct brcmf_p2p_action_frame) - 1) - return false; - - if (act_frm->category == P2P_AF_CATEGORY && - act_frm->type == P2P_VER && - memcmp(act_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0) - return true; - - return false; -} - -/** - * brcmf_p2p_is_gas_action() - true if p2p gas action type frame. - * - * @frame: action frame data. - * @frame_len: length of action frame data. - * - * Determine if action frame is p2p gas action type - */ -static bool brcmf_p2p_is_gas_action(void *frame, u32 frame_len) -{ - struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm; - - if (frame == NULL) - return false; - - sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame; - if (frame_len < sizeof(struct brcmf_p2psd_gas_pub_act_frame) - 1) - return false; - - if (sd_act_frm->category != P2PSD_ACTION_CATEGORY) - return false; - - if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ || - sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP || - sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ || - sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP) - return true; - - return false; -} - -/** - * brcmf_p2p_print_actframe() - debug print routine. - * - * @tx: Received or to be transmitted - * @frame: action frame data. - * @frame_len: length of action frame data. - * - * Print information about the p2p action frame - */ - -#ifdef DEBUG - -static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len) -{ - struct brcmf_p2p_pub_act_frame *pact_frm; - struct brcmf_p2p_action_frame *act_frm; - struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm; - - if (!frame || frame_len <= 2) - return; - - if (brcmf_p2p_is_pub_action(frame, frame_len)) { - pact_frm = (struct brcmf_p2p_pub_act_frame *)frame; - switch (pact_frm->subtype) { - case P2P_PAF_GON_REQ: - brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Req Frame\n", - (tx) ? "TX" : "RX"); - break; - case P2P_PAF_GON_RSP: - brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Rsp Frame\n", - (tx) ? "TX" : "RX"); - break; - case P2P_PAF_GON_CONF: - brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Confirm Frame\n", - (tx) ? "TX" : "RX"); - break; - case P2P_PAF_INVITE_REQ: - brcmf_dbg(TRACE, "%s P2P Invitation Request Frame\n", - (tx) ? "TX" : "RX"); - break; - case P2P_PAF_INVITE_RSP: - brcmf_dbg(TRACE, "%s P2P Invitation Response Frame\n", - (tx) ? "TX" : "RX"); - break; - case P2P_PAF_DEVDIS_REQ: - brcmf_dbg(TRACE, "%s P2P Device Discoverability Request Frame\n", - (tx) ? "TX" : "RX"); - break; - case P2P_PAF_DEVDIS_RSP: - brcmf_dbg(TRACE, "%s P2P Device Discoverability Response Frame\n", - (tx) ? "TX" : "RX"); - break; - case P2P_PAF_PROVDIS_REQ: - brcmf_dbg(TRACE, "%s P2P Provision Discovery Request Frame\n", - (tx) ? "TX" : "RX"); - break; - case P2P_PAF_PROVDIS_RSP: - brcmf_dbg(TRACE, "%s P2P Provision Discovery Response Frame\n", - (tx) ? "TX" : "RX"); - break; - default: - brcmf_dbg(TRACE, "%s Unknown P2P Public Action Frame\n", - (tx) ? "TX" : "RX"); - break; - } - } else if (brcmf_p2p_is_p2p_action(frame, frame_len)) { - act_frm = (struct brcmf_p2p_action_frame *)frame; - switch (act_frm->subtype) { - case P2P_AF_NOTICE_OF_ABSENCE: - brcmf_dbg(TRACE, "%s P2P Notice of Absence Frame\n", - (tx) ? "TX" : "RX"); - break; - case P2P_AF_PRESENCE_REQ: - brcmf_dbg(TRACE, "%s P2P Presence Request Frame\n", - (tx) ? "TX" : "RX"); - break; - case P2P_AF_PRESENCE_RSP: - brcmf_dbg(TRACE, "%s P2P Presence Response Frame\n", - (tx) ? "TX" : "RX"); - break; - case P2P_AF_GO_DISC_REQ: - brcmf_dbg(TRACE, "%s P2P Discoverability Request Frame\n", - (tx) ? "TX" : "RX"); - break; - default: - brcmf_dbg(TRACE, "%s Unknown P2P Action Frame\n", - (tx) ? "TX" : "RX"); - } - - } else if (brcmf_p2p_is_gas_action(frame, frame_len)) { - sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame; - switch (sd_act_frm->action) { - case P2PSD_ACTION_ID_GAS_IREQ: - brcmf_dbg(TRACE, "%s P2P GAS Initial Request\n", - (tx) ? "TX" : "RX"); - break; - case P2PSD_ACTION_ID_GAS_IRESP: - brcmf_dbg(TRACE, "%s P2P GAS Initial Response\n", - (tx) ? "TX" : "RX"); - break; - case P2PSD_ACTION_ID_GAS_CREQ: - brcmf_dbg(TRACE, "%s P2P GAS Comback Request\n", - (tx) ? "TX" : "RX"); - break; - case P2PSD_ACTION_ID_GAS_CRESP: - brcmf_dbg(TRACE, "%s P2P GAS Comback Response\n", - (tx) ? "TX" : "RX"); - break; - default: - brcmf_dbg(TRACE, "%s Unknown P2P GAS Frame\n", - (tx) ? "TX" : "RX"); - break; - } - } -} - -#else - -static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len) -{ -} - -#endif - - -/** - * brcmf_p2p_set_firmware() - prepare firmware for peer-to-peer operation. - * - * @ifp: ifp to use for iovars (primary). - * @p2p_mac: mac address to configure for p2p_da_override - */ -static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac) -{ - s32 ret = 0; - - brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); - brcmf_fil_iovar_int_set(ifp, "apsta", 1); - brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); - - /* In case of COB type, firmware has default mac address - * After Initializing firmware, we have to set current mac address to - * firmware for P2P device address. This must be done with discovery - * disabled. - */ - brcmf_fil_iovar_int_set(ifp, "p2p_disc", 0); - - ret = brcmf_fil_iovar_data_set(ifp, "p2p_da_override", p2p_mac, - ETH_ALEN); - if (ret) - brcmf_err("failed to update device address ret %d\n", ret); - - return ret; -} - -/** - * brcmf_p2p_generate_bss_mac() - derive mac addresses for P2P. - * - * @p2p: P2P specific data. - * @dev_addr: optional device address. - * - * P2P needs mac addresses for P2P device and interface. If no device - * address it specified, these are derived from the primary net device, ie. - * the permanent ethernet address of the device. - */ -static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr) -{ - struct brcmf_if *pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; - bool local_admin = false; - - if (!dev_addr || is_zero_ether_addr(dev_addr)) { - dev_addr = pri_ifp->mac_addr; - local_admin = true; - } - - /* Generate the P2P Device Address. This consists of the device's - * primary MAC address with the locally administered bit set. - */ - memcpy(p2p->dev_addr, dev_addr, ETH_ALEN); - if (local_admin) - p2p->dev_addr[0] |= 0x02; - - /* Generate the P2P Interface Address. If the discovery and connection - * BSSCFGs need to simultaneously co-exist, then this address must be - * different from the P2P Device Address, but also locally administered. - */ - memcpy(p2p->int_addr, p2p->dev_addr, ETH_ALEN); - p2p->int_addr[0] |= 0x02; - p2p->int_addr[4] ^= 0x80; -} - -/** - * brcmf_p2p_scan_is_p2p_request() - is cfg80211 scan request a P2P scan. - * - * @request: the scan request as received from cfg80211. - * - * returns true if one of the ssids in the request matches the - * P2P wildcard ssid; otherwise returns false. - */ -static bool brcmf_p2p_scan_is_p2p_request(struct cfg80211_scan_request *request) -{ - struct cfg80211_ssid *ssids = request->ssids; - int i; - - for (i = 0; i < request->n_ssids; i++) { - if (ssids[i].ssid_len != BRCMF_P2P_WILDCARD_SSID_LEN) - continue; - - brcmf_dbg(INFO, "comparing ssid \"%s\"", ssids[i].ssid); - if (!memcmp(BRCMF_P2P_WILDCARD_SSID, ssids[i].ssid, - BRCMF_P2P_WILDCARD_SSID_LEN)) - return true; - } - return false; -} - -/** - * brcmf_p2p_set_discover_state - set discover state in firmware. - * - * @ifp: low-level interface object. - * @state: discover state to set. - * @chanspec: channel parameters (for state @WL_P2P_DISC_ST_LISTEN only). - * @listen_ms: duration to listen (for state @WL_P2P_DISC_ST_LISTEN only). - */ -static s32 brcmf_p2p_set_discover_state(struct brcmf_if *ifp, u8 state, - u16 chanspec, u16 listen_ms) -{ - struct brcmf_p2p_disc_st_le discover_state; - s32 ret = 0; - brcmf_dbg(TRACE, "enter\n"); - - discover_state.state = state; - discover_state.chspec = cpu_to_le16(chanspec); - discover_state.dwell = cpu_to_le16(listen_ms); - ret = brcmf_fil_bsscfg_data_set(ifp, "p2p_state", &discover_state, - sizeof(discover_state)); - return ret; -} - -/** - * brcmf_p2p_deinit_discovery() - disable P2P device discovery. - * - * @p2p: P2P specific data. - * - * Resets the discovery state and disables it in firmware. - */ -static s32 brcmf_p2p_deinit_discovery(struct brcmf_p2p_info *p2p) -{ - struct brcmf_cfg80211_vif *vif; - - brcmf_dbg(TRACE, "enter\n"); - - /* Set the discovery state to SCAN */ - vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - (void)brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0); - - /* Disable P2P discovery in the firmware */ - vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; - (void)brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 0); - - return 0; -} - -/** - * brcmf_p2p_enable_discovery() - initialize and configure discovery. - * - * @p2p: P2P specific data. - * - * Initializes the discovery device and configure the virtual interface. - */ -static int brcmf_p2p_enable_discovery(struct brcmf_p2p_info *p2p) -{ - struct brcmf_cfg80211_vif *vif; - s32 ret = 0; - - brcmf_dbg(TRACE, "enter\n"); - vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - if (!vif) { - brcmf_err("P2P config device not available\n"); - ret = -EPERM; - goto exit; - } - - if (test_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status)) { - brcmf_dbg(INFO, "P2P config device already configured\n"); - goto exit; - } - - /* Re-initialize P2P Discovery in the firmware */ - vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; - ret = brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 1); - if (ret < 0) { - brcmf_err("set p2p_disc error\n"); - goto exit; - } - vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - ret = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0); - if (ret < 0) { - brcmf_err("unable to set WL_P2P_DISC_ST_SCAN\n"); - goto exit; - } - - /* - * Set wsec to any non-zero value in the discovery bsscfg - * to ensure our P2P probe responses have the privacy bit - * set in the 802.11 WPA IE. Some peer devices may not - * initiate WPS with us if this bit is not set. - */ - ret = brcmf_fil_bsscfg_int_set(vif->ifp, "wsec", AES_ENABLED); - if (ret < 0) { - brcmf_err("wsec error %d\n", ret); - goto exit; - } - - set_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status); -exit: - return ret; -} - -/** - * brcmf_p2p_escan() - initiate a P2P scan. - * - * @p2p: P2P specific data. - * @num_chans: number of channels to scan. - * @chanspecs: channel parameters for @num_chans channels. - * @search_state: P2P discover state to use. - * @action: scan action to pass to firmware. - * @bss_type: type of P2P bss. - */ -static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans, - u16 chanspecs[], s32 search_state, u16 action, - enum p2p_bss_type bss_type) -{ - s32 ret = 0; - s32 memsize = offsetof(struct brcmf_p2p_scan_le, - eparams.params_le.channel_list); - s32 nprobes; - s32 active; - u32 i; - u8 *memblk; - struct brcmf_cfg80211_vif *vif; - struct brcmf_p2p_scan_le *p2p_params; - struct brcmf_scan_params_le *sparams; - struct brcmf_ssid ssid; - - memsize += num_chans * sizeof(__le16); - memblk = kzalloc(memsize, GFP_KERNEL); - if (!memblk) - return -ENOMEM; - - vif = p2p->bss_idx[bss_type].vif; - if (vif == NULL) { - brcmf_err("no vif for bss type %d\n", bss_type); - ret = -EINVAL; - goto exit; - } - - switch (search_state) { - case WL_P2P_DISC_ST_SEARCH: - /* - * If we in SEARCH STATE, we don't need to set SSID explictly - * because dongle use P2P WILDCARD internally by default - */ - /* use null ssid */ - ssid.SSID_len = 0; - memset(ssid.SSID, 0, sizeof(ssid.SSID)); - break; - case WL_P2P_DISC_ST_SCAN: - /* - * wpa_supplicant has p2p_find command with type social or - * progressive. For progressive, we need to set the ssid to - * P2P WILDCARD because we just do broadcast scan unless - * setting SSID. - */ - ssid.SSID_len = BRCMF_P2P_WILDCARD_SSID_LEN; - memcpy(ssid.SSID, BRCMF_P2P_WILDCARD_SSID, ssid.SSID_len); - break; - default: - brcmf_err(" invalid search state %d\n", search_state); - ret = -EINVAL; - goto exit; - } - - brcmf_p2p_set_discover_state(vif->ifp, search_state, 0, 0); - - /* - * set p2p scan parameters. - */ - p2p_params = (struct brcmf_p2p_scan_le *)memblk; - p2p_params->type = 'E'; - - /* determine the scan engine parameters */ - sparams = &p2p_params->eparams.params_le; - sparams->bss_type = DOT11_BSSTYPE_ANY; - if (p2p->cfg->active_scan) - sparams->scan_type = 0; - else - sparams->scan_type = 1; - - eth_broadcast_addr(sparams->bssid); - if (ssid.SSID_len) - memcpy(sparams->ssid_le.SSID, ssid.SSID, ssid.SSID_len); - sparams->ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len); - sparams->home_time = cpu_to_le32(P2PAPI_SCAN_HOME_TIME_MS); - - /* - * SOCIAL_CHAN_CNT + 1 takes care of the Progressive scan - * supported by the supplicant. - */ - if (num_chans == SOCIAL_CHAN_CNT || num_chans == (SOCIAL_CHAN_CNT + 1)) - active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS; - else if (num_chans == AF_PEER_SEARCH_CNT) - active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS; - else if (brcmf_get_vif_state_any(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED)) - active = -1; - else - active = P2PAPI_SCAN_DWELL_TIME_MS; - - /* Override scan params to find a peer for a connection */ - if (num_chans == 1) { - active = WL_SCAN_CONNECT_DWELL_TIME_MS; - /* WAR to sync with presence period of VSDB GO. - * send probe request more frequently - */ - nprobes = active / WL_SCAN_JOIN_PROBE_INTERVAL_MS; - } else { - nprobes = active / P2PAPI_SCAN_NPROBS_TIME_MS; - } - - if (nprobes <= 0) - nprobes = 1; - - brcmf_dbg(INFO, "nprobes # %d, active_time %d\n", nprobes, active); - sparams->active_time = cpu_to_le32(active); - sparams->nprobes = cpu_to_le32(nprobes); - sparams->passive_time = cpu_to_le32(-1); - sparams->channel_num = cpu_to_le32(num_chans & - BRCMF_SCAN_PARAMS_COUNT_MASK); - for (i = 0; i < num_chans; i++) - sparams->channel_list[i] = cpu_to_le16(chanspecs[i]); - - /* set the escan specific parameters */ - p2p_params->eparams.version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); - p2p_params->eparams.action = cpu_to_le16(action); - p2p_params->eparams.sync_id = cpu_to_le16(0x1234); - /* perform p2p scan on primary device */ - ret = brcmf_fil_bsscfg_data_set(vif->ifp, "p2p_scan", memblk, memsize); - if (!ret) - set_bit(BRCMF_SCAN_STATUS_BUSY, &p2p->cfg->scan_status); -exit: - kfree(memblk); - return ret; -} - -/** - * brcmf_p2p_run_escan() - escan callback for peer-to-peer. - * - * @cfg: driver private data for cfg80211 interface. - * @ndev: net device for which scan is requested. - * @request: scan request from cfg80211. - * @action: scan action. - * - * Determines the P2P discovery state based to scan request parameters and - * validates the channels in the request. - */ -static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp, - struct cfg80211_scan_request *request, - u16 action) -{ - struct brcmf_p2p_info *p2p = &cfg->p2p; - s32 err = 0; - s32 search_state = WL_P2P_DISC_ST_SCAN; - struct brcmf_cfg80211_vif *vif; - struct net_device *dev = NULL; - int i, num_nodfs = 0; - u16 *chanspecs; - - brcmf_dbg(TRACE, "enter\n"); - - if (!request) { - err = -EINVAL; - goto exit; - } - - if (request->n_channels) { - chanspecs = kcalloc(request->n_channels, sizeof(*chanspecs), - GFP_KERNEL); - if (!chanspecs) { - err = -ENOMEM; - goto exit; - } - vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif; - if (vif) - dev = vif->wdev.netdev; - if (request->n_channels == 3 && - request->channels[0]->hw_value == SOCIAL_CHAN_1 && - request->channels[1]->hw_value == SOCIAL_CHAN_2 && - request->channels[2]->hw_value == SOCIAL_CHAN_3) { - /* SOCIAL CHANNELS 1, 6, 11 */ - search_state = WL_P2P_DISC_ST_SEARCH; - brcmf_dbg(INFO, "P2P SEARCH PHASE START\n"); - } else if (dev != NULL && - vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) { - /* If you are already a GO, then do SEARCH only */ - brcmf_dbg(INFO, "Already a GO. Do SEARCH Only\n"); - search_state = WL_P2P_DISC_ST_SEARCH; - } else { - brcmf_dbg(INFO, "P2P SCAN STATE START\n"); - } - - /* - * no P2P scanning on passive or DFS channels. - */ - for (i = 0; i < request->n_channels; i++) { - struct ieee80211_channel *chan = request->channels[i]; - - if (chan->flags & (IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR)) - continue; - - chanspecs[i] = channel_to_chanspec(&p2p->cfg->d11inf, - chan); - brcmf_dbg(INFO, "%d: chan=%d, channel spec=%x\n", - num_nodfs, chan->hw_value, chanspecs[i]); - num_nodfs++; - } - err = brcmf_p2p_escan(p2p, num_nodfs, chanspecs, search_state, - action, P2PAPI_BSSCFG_DEVICE); - kfree(chanspecs); - } -exit: - if (err) - brcmf_err("error (%d)\n", err); - return err; -} - - -/** - * brcmf_p2p_find_listen_channel() - find listen channel in ie string. - * - * @ie: string of information elements. - * @ie_len: length of string. - * - * Scan ie for p2p ie and look for attribute 6 channel. If available determine - * channel and return it. - */ -static s32 brcmf_p2p_find_listen_channel(const u8 *ie, u32 ie_len) -{ - u8 channel_ie[5]; - s32 listen_channel; - s32 err; - - err = cfg80211_get_p2p_attr(ie, ie_len, - IEEE80211_P2P_ATTR_LISTEN_CHANNEL, - channel_ie, sizeof(channel_ie)); - if (err < 0) - return err; - - /* listen channel subel length format: */ - /* 3(country) + 1(op. class) + 1(chan num) */ - listen_channel = (s32)channel_ie[3 + 1]; - - if (listen_channel == SOCIAL_CHAN_1 || - listen_channel == SOCIAL_CHAN_2 || - listen_channel == SOCIAL_CHAN_3) { - brcmf_dbg(INFO, "Found my Listen Channel %d\n", listen_channel); - return listen_channel; - } - - return -EPERM; -} - - -/** - * brcmf_p2p_scan_prep() - prepare scan based on request. - * - * @wiphy: wiphy device. - * @request: scan request from cfg80211. - * @vif: vif on which scan request is to be executed. - * - * Prepare the scan appropriately for type of scan requested. Overrides the - * escan .run() callback for peer-to-peer scanning. - */ -int brcmf_p2p_scan_prep(struct wiphy *wiphy, - struct cfg80211_scan_request *request, - struct brcmf_cfg80211_vif *vif) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_p2p_info *p2p = &cfg->p2p; - int err = 0; - - if (brcmf_p2p_scan_is_p2p_request(request)) { - /* find my listen channel */ - err = brcmf_p2p_find_listen_channel(request->ie, - request->ie_len); - if (err < 0) - return err; - - p2p->afx_hdl.my_listen_chan = err; - - clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); - brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n"); - - err = brcmf_p2p_enable_discovery(p2p); - if (err) - return err; - - vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - - /* override .run_escan() callback. */ - cfg->escan_info.run = brcmf_p2p_run_escan; - } - err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG, - request->ie, request->ie_len); - return err; -} - - -/** - * brcmf_p2p_discover_listen() - set firmware to discover listen state. - * - * @p2p: p2p device. - * @channel: channel nr for discover listen. - * @duration: time in ms to stay on channel. - * - */ -static s32 -brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration) -{ - struct brcmf_cfg80211_vif *vif; - struct brcmu_chan ch; - s32 err = 0; - - vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - if (!vif) { - brcmf_err("Discovery is not set, so we have nothing to do\n"); - err = -EPERM; - goto exit; - } - - if (test_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status)) { - brcmf_err("Previous LISTEN is not completed yet\n"); - /* WAR: prevent cookie mismatch in wpa_supplicant return OK */ - goto exit; - } - - ch.chnum = channel; - ch.bw = BRCMU_CHAN_BW_20; - p2p->cfg->d11inf.encchspec(&ch); - err = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_LISTEN, - ch.chspec, (u16)duration); - if (!err) { - set_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status); - p2p->remain_on_channel_cookie++; - } -exit: - return err; -} - - -/** - * brcmf_p2p_remain_on_channel() - put device on channel and stay there. - * - * @wiphy: wiphy device. - * @channel: channel to stay on. - * @duration: time in ms to remain on channel. - * - */ -int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, - struct ieee80211_channel *channel, - unsigned int duration, u64 *cookie) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_p2p_info *p2p = &cfg->p2p; - s32 err; - u16 channel_nr; - - channel_nr = ieee80211_frequency_to_channel(channel->center_freq); - brcmf_dbg(TRACE, "Enter, channel: %d, duration ms (%d)\n", channel_nr, - duration); - - err = brcmf_p2p_enable_discovery(p2p); - if (err) - goto exit; - err = brcmf_p2p_discover_listen(p2p, channel_nr, duration); - if (err) - goto exit; - - memcpy(&p2p->remain_on_channel, channel, sizeof(*channel)); - *cookie = p2p->remain_on_channel_cookie; - cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL); - -exit: - return err; -} - - -/** - * brcmf_p2p_notify_listen_complete() - p2p listen has completed. - * - * @ifp: interfac control. - * @e: event message. Not used, to make it usable for fweh event dispatcher. - * @data: payload of message. Not used. - * - */ -int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, - void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct brcmf_p2p_info *p2p = &cfg->p2p; - - brcmf_dbg(TRACE, "Enter\n"); - if (test_and_clear_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, - &p2p->status)) { - if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN, - &p2p->status)) { - clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, - &p2p->status); - brcmf_dbg(INFO, "Listen DONE, wake up wait_next_af\n"); - complete(&p2p->wait_next_af); - } - - cfg80211_remain_on_channel_expired(&ifp->vif->wdev, - p2p->remain_on_channel_cookie, - &p2p->remain_on_channel, - GFP_KERNEL); - } - return 0; -} - - -/** - * brcmf_p2p_cancel_remain_on_channel() - cancel p2p listen state. - * - * @ifp: interfac control. - * - */ -void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp) -{ - if (!ifp) - return; - brcmf_p2p_set_discover_state(ifp, WL_P2P_DISC_ST_SCAN, 0, 0); - brcmf_p2p_notify_listen_complete(ifp, NULL, NULL); -} - - -/** - * brcmf_p2p_act_frm_search() - search function for action frame. - * - * @p2p: p2p device. - * channel: channel on which action frame is to be trasmitted. - * - * search function to reach at common channel to send action frame. When - * channel is 0 then all social channels will be used to send af - */ -static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel) -{ - s32 err; - u32 channel_cnt; - u16 *default_chan_list; - u32 i; - struct brcmu_chan ch; - - brcmf_dbg(TRACE, "Enter\n"); - - if (channel) - channel_cnt = AF_PEER_SEARCH_CNT; - else - channel_cnt = SOCIAL_CHAN_CNT; - default_chan_list = kzalloc(channel_cnt * sizeof(*default_chan_list), - GFP_KERNEL); - if (default_chan_list == NULL) { - brcmf_err("channel list allocation failed\n"); - err = -ENOMEM; - goto exit; - } - ch.bw = BRCMU_CHAN_BW_20; - if (channel) { - ch.chnum = channel; - p2p->cfg->d11inf.encchspec(&ch); - /* insert same channel to the chan_list */ - for (i = 0; i < channel_cnt; i++) - default_chan_list[i] = ch.chspec; - } else { - ch.chnum = SOCIAL_CHAN_1; - p2p->cfg->d11inf.encchspec(&ch); - default_chan_list[0] = ch.chspec; - ch.chnum = SOCIAL_CHAN_2; - p2p->cfg->d11inf.encchspec(&ch); - default_chan_list[1] = ch.chspec; - ch.chnum = SOCIAL_CHAN_3; - p2p->cfg->d11inf.encchspec(&ch); - default_chan_list[2] = ch.chspec; - } - err = brcmf_p2p_escan(p2p, channel_cnt, default_chan_list, - WL_P2P_DISC_ST_SEARCH, WL_ESCAN_ACTION_START, - P2PAPI_BSSCFG_DEVICE); - kfree(default_chan_list); -exit: - return err; -} - - -/** - * brcmf_p2p_afx_handler() - afx worker thread. - * - * @work: - * - */ -static void brcmf_p2p_afx_handler(struct work_struct *work) -{ - struct afx_hdl *afx_hdl = container_of(work, struct afx_hdl, afx_work); - struct brcmf_p2p_info *p2p = container_of(afx_hdl, - struct brcmf_p2p_info, - afx_hdl); - s32 err; - - if (!afx_hdl->is_active) - return; - - if (afx_hdl->is_listen && afx_hdl->my_listen_chan) - /* 100ms ~ 300ms */ - err = brcmf_p2p_discover_listen(p2p, afx_hdl->my_listen_chan, - 100 * (1 + prandom_u32() % 3)); - else - err = brcmf_p2p_act_frm_search(p2p, afx_hdl->peer_listen_chan); - - if (err) { - brcmf_err("ERROR occurred! value is (%d)\n", err); - if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, - &p2p->status)) - complete(&afx_hdl->act_frm_scan); - } -} - - -/** - * brcmf_p2p_af_searching_channel() - search channel. - * - * @p2p: p2p device info struct. - * - */ -static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) -{ - struct afx_hdl *afx_hdl = &p2p->afx_hdl; - struct brcmf_cfg80211_vif *pri_vif; - unsigned long duration; - s32 retry; - - brcmf_dbg(TRACE, "Enter\n"); - - pri_vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; - - reinit_completion(&afx_hdl->act_frm_scan); - set_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status); - afx_hdl->is_active = true; - afx_hdl->peer_chan = P2P_INVALID_CHANNEL; - - /* Loop to wait until we find a peer's channel or the - * pending action frame tx is cancelled. - */ - retry = 0; - duration = msecs_to_jiffies(P2P_AF_FRM_SCAN_MAX_WAIT); - while ((retry < P2P_CHANNEL_SYNC_RETRY) && - (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)) { - afx_hdl->is_listen = false; - brcmf_dbg(TRACE, "Scheduling action frame for sending.. (%d)\n", - retry); - /* search peer on peer's listen channel */ - schedule_work(&afx_hdl->afx_work); - wait_for_completion_timeout(&afx_hdl->act_frm_scan, duration); - if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) || - (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, - &p2p->status))) - break; - - if (afx_hdl->my_listen_chan) { - brcmf_dbg(TRACE, "Scheduling listen peer, channel=%d\n", - afx_hdl->my_listen_chan); - /* listen on my listen channel */ - afx_hdl->is_listen = true; - schedule_work(&afx_hdl->afx_work); - wait_for_completion_timeout(&afx_hdl->act_frm_scan, - duration); - } - if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) || - (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, - &p2p->status))) - break; - retry++; - - /* if sta is connected or connecting, sleep for a while before - * retry af tx or finding a peer - */ - if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &pri_vif->sme_state) || - test_bit(BRCMF_VIF_STATUS_CONNECTING, &pri_vif->sme_state)) - msleep(P2P_DEFAULT_SLEEP_TIME_VSDB); - } - - brcmf_dbg(TRACE, "Completed search/listen peer_chan=%d\n", - afx_hdl->peer_chan); - afx_hdl->is_active = false; - - clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status); - - return afx_hdl->peer_chan; -} - - -/** - * brcmf_p2p_scan_finding_common_channel() - was escan used for finding channel - * - * @cfg: common configuration struct. - * @bi: bss info struct, result from scan. - * - */ -bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, - struct brcmf_bss_info_le *bi) - -{ - struct brcmf_p2p_info *p2p = &cfg->p2p; - struct afx_hdl *afx_hdl = &p2p->afx_hdl; - struct brcmu_chan ch; - u8 *ie; - s32 err; - u8 p2p_dev_addr[ETH_ALEN]; - - if (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status)) - return false; - - if (bi == NULL) { - brcmf_dbg(TRACE, "ACTION FRAME SCAN Done\n"); - if (afx_hdl->peer_chan == P2P_INVALID_CHANNEL) - complete(&afx_hdl->act_frm_scan); - return true; - } - - ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset); - memset(p2p_dev_addr, 0, sizeof(p2p_dev_addr)); - err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length), - IEEE80211_P2P_ATTR_DEVICE_INFO, - p2p_dev_addr, sizeof(p2p_dev_addr)); - if (err < 0) - err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length), - IEEE80211_P2P_ATTR_DEVICE_ID, - p2p_dev_addr, sizeof(p2p_dev_addr)); - if ((err >= 0) && - (ether_addr_equal(p2p_dev_addr, afx_hdl->tx_dst_addr))) { - if (!bi->ctl_ch) { - ch.chspec = le16_to_cpu(bi->chanspec); - cfg->d11inf.decchspec(&ch); - bi->ctl_ch = ch.chnum; - } - afx_hdl->peer_chan = bi->ctl_ch; - brcmf_dbg(TRACE, "ACTION FRAME SCAN : Peer %pM found, channel : %d\n", - afx_hdl->tx_dst_addr, afx_hdl->peer_chan); - complete(&afx_hdl->act_frm_scan); - } - return true; -} - -/** - * brcmf_p2p_stop_wait_next_action_frame() - finish scan if af tx complete. - * - * @cfg: common configuration struct. - * - */ -static void -brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_p2p_info *p2p = &cfg->p2p; - struct brcmf_if *ifp = cfg->escan_info.ifp; - - if (test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status) && - (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status) || - test_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status))) { - brcmf_dbg(TRACE, "*** Wake UP ** abort actframe iovar\n"); - /* if channel is not zero, "actfame" uses off channel scan. - * So abort scan for off channel completion. - */ - if (p2p->af_sent_channel) - brcmf_notify_escan_complete(cfg, ifp, true, true); - } else if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN, - &p2p->status)) { - brcmf_dbg(TRACE, "*** Wake UP ** abort listen for next af frame\n"); - /* So abort scan to cancel listen */ - brcmf_notify_escan_complete(cfg, ifp, true, true); - } -} - - -/** - * brcmf_p2p_gon_req_collision() - Check if go negotiaton collission - * - * @p2p: p2p device info struct. - * - * return true if recevied action frame is to be dropped. - */ -static bool -brcmf_p2p_gon_req_collision(struct brcmf_p2p_info *p2p, u8 *mac) -{ - struct brcmf_cfg80211_info *cfg = p2p->cfg; - struct brcmf_if *ifp; - - brcmf_dbg(TRACE, "Enter\n"); - - if (!test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) || - !p2p->gon_req_action) - return false; - - brcmf_dbg(TRACE, "GO Negotiation Request COLLISION !!!\n"); - /* if sa(peer) addr is less than da(my) addr, then this device - * process peer's gon request and block to send gon req. - * if not (sa addr > da addr), - * this device will process gon request and drop gon req of peer. - */ - ifp = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->ifp; - if (memcmp(mac, ifp->mac_addr, ETH_ALEN) < 0) { - brcmf_dbg(INFO, "Block transmit gon req !!!\n"); - p2p->block_gon_req_tx = true; - /* if we are finding a common channel for sending af, - * do not scan more to block to send current gon req - */ - if (test_and_clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, - &p2p->status)) - complete(&p2p->afx_hdl.act_frm_scan); - if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, - &p2p->status)) - brcmf_p2p_stop_wait_next_action_frame(cfg); - return false; - } - - /* drop gon request of peer to process gon request by this device. */ - brcmf_dbg(INFO, "Drop received gon req !!!\n"); - - return true; -} - - -/** - * brcmf_p2p_notify_action_frame_rx() - received action frame. - * - * @ifp: interfac control. - * @e: event message. Not used, to make it usable for fweh event dispatcher. - * @data: payload of message, containing action frame data. - * - */ -int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, - void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct brcmf_p2p_info *p2p = &cfg->p2p; - struct afx_hdl *afx_hdl = &p2p->afx_hdl; - struct wireless_dev *wdev; - u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data); - struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data; - u8 *frame = (u8 *)(rxframe + 1); - struct brcmf_p2p_pub_act_frame *act_frm; - struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm; - struct brcmu_chan ch; - struct ieee80211_mgmt *mgmt_frame; - s32 freq; - u16 mgmt_type; - u8 action; - - ch.chspec = be16_to_cpu(rxframe->chanspec); - cfg->d11inf.decchspec(&ch); - /* Check if wpa_supplicant has registered for this frame */ - brcmf_dbg(INFO, "ifp->vif->mgmt_rx_reg %04x\n", ifp->vif->mgmt_rx_reg); - mgmt_type = (IEEE80211_STYPE_ACTION & IEEE80211_FCTL_STYPE) >> 4; - if ((ifp->vif->mgmt_rx_reg & BIT(mgmt_type)) == 0) - return 0; - - brcmf_p2p_print_actframe(false, frame, mgmt_frame_len); - - action = P2P_PAF_SUBTYPE_INVALID; - if (brcmf_p2p_is_pub_action(frame, mgmt_frame_len)) { - act_frm = (struct brcmf_p2p_pub_act_frame *)frame; - action = act_frm->subtype; - if ((action == P2P_PAF_GON_REQ) && - (brcmf_p2p_gon_req_collision(p2p, (u8 *)e->addr))) { - if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, - &p2p->status) && - (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) { - afx_hdl->peer_chan = ch.chnum; - brcmf_dbg(INFO, "GON request: Peer found, channel=%d\n", - afx_hdl->peer_chan); - complete(&afx_hdl->act_frm_scan); - } - return 0; - } - /* After complete GO Negotiation, roll back to mpc mode */ - if ((action == P2P_PAF_GON_CONF) || - (action == P2P_PAF_PROVDIS_RSP)) - brcmf_set_mpc(ifp, 1); - if (action == P2P_PAF_GON_CONF) { - brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n"); - clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); - } - } else if (brcmf_p2p_is_gas_action(frame, mgmt_frame_len)) { - sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame; - action = sd_act_frm->action; - } - - if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) && - (p2p->next_af_subtype == action)) { - brcmf_dbg(TRACE, "We got a right next frame! (%d)\n", action); - clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, - &p2p->status); - /* Stop waiting for next AF. */ - brcmf_p2p_stop_wait_next_action_frame(cfg); - } - - mgmt_frame = kzalloc(offsetof(struct ieee80211_mgmt, u) + - mgmt_frame_len, GFP_KERNEL); - if (!mgmt_frame) { - brcmf_err("No memory available for action frame\n"); - return -ENOMEM; - } - memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN); - brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid, - ETH_ALEN); - memcpy(mgmt_frame->sa, e->addr, ETH_ALEN); - mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_ACTION); - memcpy(&mgmt_frame->u, frame, mgmt_frame_len); - mgmt_frame_len += offsetof(struct ieee80211_mgmt, u); - - freq = ieee80211_channel_to_frequency(ch.chnum, - ch.band == BRCMU_CHAN_BAND_2G ? - IEEE80211_BAND_2GHZ : - IEEE80211_BAND_5GHZ); - - wdev = &ifp->vif->wdev; - cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0); - - kfree(mgmt_frame); - return 0; -} - - -/** - * brcmf_p2p_notify_action_tx_complete() - transmit action frame complete - * - * @ifp: interfac control. - * @e: event message. Not used, to make it usable for fweh event dispatcher. - * @data: not used. - * - */ -int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, - void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct brcmf_p2p_info *p2p = &cfg->p2p; - - brcmf_dbg(INFO, "Enter: event %s, status=%d\n", - e->event_code == BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE ? - "ACTION_FRAME_OFF_CHAN_COMPLETE" : "ACTION_FRAME_COMPLETE", - e->status); - - if (!test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status)) - return 0; - - if (e->event_code == BRCMF_E_ACTION_FRAME_COMPLETE) { - if (e->status == BRCMF_E_STATUS_SUCCESS) - set_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, - &p2p->status); - else { - set_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status); - /* If there is no ack, we don't need to wait for - * WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE event - */ - brcmf_p2p_stop_wait_next_action_frame(cfg); - } - - } else { - complete(&p2p->send_af_done); - } - return 0; -} - - -/** - * brcmf_p2p_tx_action_frame() - send action frame over fil. - * - * @p2p: p2p info struct for vif. - * @af_params: action frame data/info. - * - * Send an action frame immediately without doing channel synchronization. - * - * This function waits for a completion event before returning. - * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action - * frame is transmitted. - */ -static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, - struct brcmf_fil_af_params_le *af_params) -{ - struct brcmf_cfg80211_vif *vif; - s32 err = 0; - s32 timeout = 0; - - brcmf_dbg(TRACE, "Enter\n"); - - reinit_completion(&p2p->send_af_done); - clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status); - clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status); - - vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params, - sizeof(*af_params)); - if (err) { - brcmf_err(" sending action frame has failed\n"); - goto exit; - } - - p2p->af_sent_channel = le32_to_cpu(af_params->channel); - p2p->af_tx_sent_jiffies = jiffies; - - timeout = wait_for_completion_timeout(&p2p->send_af_done, - msecs_to_jiffies(P2P_AF_MAX_WAIT_TIME)); - - if (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status)) { - brcmf_dbg(TRACE, "TX action frame operation is success\n"); - } else { - err = -EIO; - brcmf_dbg(TRACE, "TX action frame operation has failed\n"); - } - /* clear status bit for action tx */ - clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status); - clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status); - -exit: - return err; -} - - -/** - * brcmf_p2p_pub_af_tx() - public action frame tx routine. - * - * @cfg: driver private data for cfg80211 interface. - * @af_params: action frame data/info. - * @config_af_params: configuration data for action frame. - * - * routine which transmits ation frame public type. - */ -static s32 brcmf_p2p_pub_af_tx(struct brcmf_cfg80211_info *cfg, - struct brcmf_fil_af_params_le *af_params, - struct brcmf_config_af_params *config_af_params) -{ - struct brcmf_p2p_info *p2p = &cfg->p2p; - struct brcmf_fil_action_frame_le *action_frame; - struct brcmf_p2p_pub_act_frame *act_frm; - s32 err = 0; - u16 ie_len; - - action_frame = &af_params->action_frame; - act_frm = (struct brcmf_p2p_pub_act_frame *)(action_frame->data); - - config_af_params->extra_listen = true; - - switch (act_frm->subtype) { - case P2P_PAF_GON_REQ: - brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status set\n"); - set_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); - config_af_params->mpc_onoff = 0; - config_af_params->search_channel = true; - p2p->next_af_subtype = act_frm->subtype + 1; - p2p->gon_req_action = true; - /* increase dwell time to wait for RESP frame */ - af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME); - break; - case P2P_PAF_GON_RSP: - p2p->next_af_subtype = act_frm->subtype + 1; - /* increase dwell time to wait for CONF frame */ - af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME); - break; - case P2P_PAF_GON_CONF: - /* If we reached till GO Neg confirmation reset the filter */ - brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n"); - clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); - /* turn on mpc again if go nego is done */ - config_af_params->mpc_onoff = 1; - /* minimize dwell time */ - af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME); - config_af_params->extra_listen = false; - break; - case P2P_PAF_INVITE_REQ: - config_af_params->search_channel = true; - p2p->next_af_subtype = act_frm->subtype + 1; - /* increase dwell time */ - af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME); - break; - case P2P_PAF_INVITE_RSP: - /* minimize dwell time */ - af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME); - config_af_params->extra_listen = false; - break; - case P2P_PAF_DEVDIS_REQ: - config_af_params->search_channel = true; - p2p->next_af_subtype = act_frm->subtype + 1; - /* maximize dwell time to wait for RESP frame */ - af_params->dwell_time = cpu_to_le32(P2P_AF_LONG_DWELL_TIME); - break; - case P2P_PAF_DEVDIS_RSP: - /* minimize dwell time */ - af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME); - config_af_params->extra_listen = false; - break; - case P2P_PAF_PROVDIS_REQ: - ie_len = le16_to_cpu(action_frame->len) - - offsetof(struct brcmf_p2p_pub_act_frame, elts); - if (cfg80211_get_p2p_attr(&act_frm->elts[0], ie_len, - IEEE80211_P2P_ATTR_GROUP_ID, - NULL, 0) < 0) - config_af_params->search_channel = true; - config_af_params->mpc_onoff = 0; - p2p->next_af_subtype = act_frm->subtype + 1; - /* increase dwell time to wait for RESP frame */ - af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME); - break; - case P2P_PAF_PROVDIS_RSP: - /* wpa_supplicant send go nego req right after prov disc */ - p2p->next_af_subtype = P2P_PAF_GON_REQ; - /* increase dwell time to MED level */ - af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME); - config_af_params->extra_listen = false; - break; - default: - brcmf_err("Unknown p2p pub act frame subtype: %d\n", - act_frm->subtype); - err = -EINVAL; - } - return err; -} - -/** - * brcmf_p2p_send_action_frame() - send action frame . - * - * @cfg: driver private data for cfg80211 interface. - * @ndev: net device to transmit on. - * @af_params: configuration data for action frame. - */ -bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, - struct brcmf_fil_af_params_le *af_params) -{ - struct brcmf_p2p_info *p2p = &cfg->p2p; - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_fil_action_frame_le *action_frame; - struct brcmf_config_af_params config_af_params; - struct afx_hdl *afx_hdl = &p2p->afx_hdl; - u16 action_frame_len; - bool ack = false; - u8 category; - u8 action; - s32 tx_retry; - s32 extra_listen_time; - uint delta_ms; - - action_frame = &af_params->action_frame; - action_frame_len = le16_to_cpu(action_frame->len); - - brcmf_p2p_print_actframe(true, action_frame->data, action_frame_len); - - /* Add the default dwell time. Dwell time to stay off-channel */ - /* to wait for a response action frame after transmitting an */ - /* GO Negotiation action frame */ - af_params->dwell_time = cpu_to_le32(P2P_AF_DWELL_TIME); - - category = action_frame->data[DOT11_ACTION_CAT_OFF]; - action = action_frame->data[DOT11_ACTION_ACT_OFF]; - - /* initialize variables */ - p2p->next_af_subtype = P2P_PAF_SUBTYPE_INVALID; - p2p->gon_req_action = false; - - /* config parameters */ - config_af_params.mpc_onoff = -1; - config_af_params.search_channel = false; - config_af_params.extra_listen = false; - - if (brcmf_p2p_is_pub_action(action_frame->data, action_frame_len)) { - /* p2p public action frame process */ - if (brcmf_p2p_pub_af_tx(cfg, af_params, &config_af_params)) { - /* Just send unknown subtype frame with */ - /* default parameters. */ - brcmf_err("P2P Public action frame, unknown subtype.\n"); - } - } else if (brcmf_p2p_is_gas_action(action_frame->data, - action_frame_len)) { - /* service discovery process */ - if (action == P2PSD_ACTION_ID_GAS_IREQ || - action == P2PSD_ACTION_ID_GAS_CREQ) { - /* configure service discovery query frame */ - config_af_params.search_channel = true; - - /* save next af suptype to cancel */ - /* remaining dwell time */ - p2p->next_af_subtype = action + 1; - - af_params->dwell_time = - cpu_to_le32(P2P_AF_MED_DWELL_TIME); - } else if (action == P2PSD_ACTION_ID_GAS_IRESP || - action == P2PSD_ACTION_ID_GAS_CRESP) { - /* configure service discovery response frame */ - af_params->dwell_time = - cpu_to_le32(P2P_AF_MIN_DWELL_TIME); - } else { - brcmf_err("Unknown action type: %d\n", action); - goto exit; - } - } else if (brcmf_p2p_is_p2p_action(action_frame->data, - action_frame_len)) { - /* do not configure anything. it will be */ - /* sent with a default configuration */ - } else { - brcmf_err("Unknown Frame: category 0x%x, action 0x%x\n", - category, action); - return false; - } - - /* if connecting on primary iface, sleep for a while before sending - * af tx for VSDB - */ - if (test_bit(BRCMF_VIF_STATUS_CONNECTING, - &p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->sme_state)) - msleep(50); - - /* if scan is ongoing, abort current scan. */ - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) - brcmf_abort_scanning(cfg); - - memcpy(afx_hdl->tx_dst_addr, action_frame->da, ETH_ALEN); - - /* To make sure to send successfully action frame, turn off mpc */ - if (config_af_params.mpc_onoff == 0) - brcmf_set_mpc(ifp, 0); - - /* set status and destination address before sending af */ - if (p2p->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) { - /* set status to cancel the remained dwell time in rx process */ - set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status); - } - - p2p->af_sent_channel = 0; - set_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status); - /* validate channel and p2p ies */ - if (config_af_params.search_channel && - IS_P2P_SOCIAL_CHANNEL(le32_to_cpu(af_params->channel)) && - p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->saved_ie.probe_req_ie_len) { - afx_hdl = &p2p->afx_hdl; - afx_hdl->peer_listen_chan = le32_to_cpu(af_params->channel); - - if (brcmf_p2p_af_searching_channel(p2p) == - P2P_INVALID_CHANNEL) { - brcmf_err("Couldn't find peer's channel.\n"); - goto exit; - } - - /* Abort scan even for VSDB scenarios. Scan gets aborted in - * firmware but after the check of piggyback algorithm. To take - * care of current piggback algo, lets abort the scan here - * itself. - */ - brcmf_notify_escan_complete(cfg, ifp, true, true); - - /* update channel */ - af_params->channel = cpu_to_le32(afx_hdl->peer_chan); - } - - tx_retry = 0; - while (!p2p->block_gon_req_tx && - (ack == false) && (tx_retry < P2P_AF_TX_MAX_RETRY)) { - ack = !brcmf_p2p_tx_action_frame(p2p, af_params); - tx_retry++; - } - if (ack == false) { - brcmf_err("Failed to send Action Frame(retry %d)\n", tx_retry); - clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); - } - -exit: - clear_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status); - - /* WAR: sometimes dongle does not keep the dwell time of 'actframe'. - * if we coundn't get the next action response frame and dongle does - * not keep the dwell time, go to listen state again to get next action - * response frame. - */ - if (ack && config_af_params.extra_listen && !p2p->block_gon_req_tx && - test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) && - p2p->af_sent_channel == afx_hdl->my_listen_chan) { - delta_ms = jiffies_to_msecs(jiffies - p2p->af_tx_sent_jiffies); - if (le32_to_cpu(af_params->dwell_time) > delta_ms) - extra_listen_time = le32_to_cpu(af_params->dwell_time) - - delta_ms; - else - extra_listen_time = 0; - if (extra_listen_time > 50) { - set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN, - &p2p->status); - brcmf_dbg(INFO, "Wait more time! actual af time:%d, calculated extra listen:%d\n", - le32_to_cpu(af_params->dwell_time), - extra_listen_time); - extra_listen_time += 100; - if (!brcmf_p2p_discover_listen(p2p, - p2p->af_sent_channel, - extra_listen_time)) { - unsigned long duration; - - extra_listen_time += 100; - duration = msecs_to_jiffies(extra_listen_time); - wait_for_completion_timeout(&p2p->wait_next_af, - duration); - } - clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN, - &p2p->status); - } - } - - if (p2p->block_gon_req_tx) { - /* if ack is true, supplicant will wait more time(100ms). - * so we will return it as a success to get more time . - */ - p2p->block_gon_req_tx = false; - ack = true; - } - - clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status); - /* if all done, turn mpc on again */ - if (config_af_params.mpc_onoff == 1) - brcmf_set_mpc(ifp, 1); - - return ack; -} - -/** - * brcmf_p2p_notify_rx_mgmt_p2p_probereq() - Event handler for p2p probe req. - * - * @ifp: interface pointer for which event was received. - * @e: even message. - * @data: payload of event message (probe request). - */ -s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, - void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct brcmf_p2p_info *p2p = &cfg->p2p; - struct afx_hdl *afx_hdl = &p2p->afx_hdl; - struct brcmf_cfg80211_vif *vif = ifp->vif; - struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data; - u16 chanspec = be16_to_cpu(rxframe->chanspec); - struct brcmu_chan ch; - u8 *mgmt_frame; - u32 mgmt_frame_len; - s32 freq; - u16 mgmt_type; - - brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code, - e->reason); - - ch.chspec = be16_to_cpu(rxframe->chanspec); - cfg->d11inf.decchspec(&ch); - - if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) && - (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) { - afx_hdl->peer_chan = ch.chnum; - brcmf_dbg(INFO, "PROBE REQUEST: Peer found, channel=%d\n", - afx_hdl->peer_chan); - complete(&afx_hdl->act_frm_scan); - } - - /* Firmware sends us two proberesponses for each idx one. At the */ - /* moment anything but bsscfgidx 0 is passed up to supplicant */ - if (e->bsscfgidx == 0) - return 0; - - /* Filter any P2P probe reqs arriving during the GO-NEG Phase */ - if (test_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status)) { - brcmf_dbg(INFO, "Filtering P2P probe_req in GO-NEG phase\n"); - return 0; - } - - /* Check if wpa_supplicant has registered for this frame */ - brcmf_dbg(INFO, "vif->mgmt_rx_reg %04x\n", vif->mgmt_rx_reg); - mgmt_type = (IEEE80211_STYPE_PROBE_REQ & IEEE80211_FCTL_STYPE) >> 4; - if ((vif->mgmt_rx_reg & BIT(mgmt_type)) == 0) - return 0; - - mgmt_frame = (u8 *)(rxframe + 1); - mgmt_frame_len = e->datalen - sizeof(*rxframe); - freq = ieee80211_channel_to_frequency(ch.chnum, - ch.band == BRCMU_CHAN_BAND_2G ? - IEEE80211_BAND_2GHZ : - IEEE80211_BAND_5GHZ); - - cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0); - - brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n", - mgmt_frame_len, e->datalen, chanspec, freq); - - return 0; -} - - -/** - * brcmf_p2p_get_current_chanspec() - Get current operation channel. - * - * @p2p: P2P specific data. - * @chanspec: chanspec to be returned. - */ -static void brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info *p2p, - u16 *chanspec) -{ - struct brcmf_if *ifp; - u8 mac_addr[ETH_ALEN]; - struct brcmu_chan ch; - struct brcmf_bss_info_le *bi; - u8 *buf; - - ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; - - if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mac_addr, - ETH_ALEN) == 0) { - buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); - if (buf != NULL) { - *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); - if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, - buf, WL_BSS_INFO_MAX) == 0) { - bi = (struct brcmf_bss_info_le *)(buf + 4); - *chanspec = le16_to_cpu(bi->chanspec); - kfree(buf); - return; - } - kfree(buf); - } - } - /* Use default channel for P2P */ - ch.chnum = BRCMF_P2P_TEMP_CHAN; - ch.bw = BRCMU_CHAN_BW_20; - p2p->cfg->d11inf.encchspec(&ch); - *chanspec = ch.chspec; -} - -/** - * Change a P2P Role. - * Parameters: - * @mac: MAC address of the BSS to change a role - * Returns 0 if success. - */ -int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, - enum brcmf_fil_p2p_if_types if_type) -{ - struct brcmf_p2p_info *p2p = &cfg->p2p; - struct brcmf_cfg80211_vif *vif; - struct brcmf_fil_p2p_if_le if_request; - s32 err; - u16 chanspec; - - brcmf_dbg(TRACE, "Enter\n"); - - vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; - if (!vif) { - brcmf_err("vif for P2PAPI_BSSCFG_PRIMARY does not exist\n"); - return -EPERM; - } - brcmf_notify_escan_complete(cfg, vif->ifp, true, true); - vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif; - if (!vif) { - brcmf_err("vif for P2PAPI_BSSCFG_CONNECTION does not exist\n"); - return -EPERM; - } - brcmf_set_mpc(vif->ifp, 0); - - /* In concurrency case, STA may be already associated in a particular */ - /* channel. so retrieve the current channel of primary interface and */ - /* then start the virtual interface on that. */ - brcmf_p2p_get_current_chanspec(p2p, &chanspec); - - if_request.type = cpu_to_le16((u16)if_type); - if_request.chspec = cpu_to_le16(chanspec); - memcpy(if_request.addr, p2p->int_addr, sizeof(if_request.addr)); - - brcmf_cfg80211_arm_vif_event(cfg, vif); - err = brcmf_fil_iovar_data_set(vif->ifp, "p2p_ifupd", &if_request, - sizeof(if_request)); - if (err) { - brcmf_err("p2p_ifupd FAILED, err=%d\n", err); - brcmf_cfg80211_arm_vif_event(cfg, NULL); - return err; - } - err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_CHANGE, - msecs_to_jiffies(1500)); - brcmf_cfg80211_arm_vif_event(cfg, NULL); - if (!err) { - brcmf_err("No BRCMF_E_IF_CHANGE event received\n"); - return -EIO; - } - - err = brcmf_fil_cmd_int_set(vif->ifp, BRCMF_C_SET_SCB_TIMEOUT, - BRCMF_SCB_TIMEOUT_VALUE); - - return err; -} - -static int brcmf_p2p_request_p2p_if(struct brcmf_p2p_info *p2p, - struct brcmf_if *ifp, u8 ea[ETH_ALEN], - enum brcmf_fil_p2p_if_types iftype) -{ - struct brcmf_fil_p2p_if_le if_request; - int err; - u16 chanspec; - - /* we need a default channel */ - brcmf_p2p_get_current_chanspec(p2p, &chanspec); - - /* fill the firmware request */ - memcpy(if_request.addr, ea, ETH_ALEN); - if_request.type = cpu_to_le16((u16)iftype); - if_request.chspec = cpu_to_le16(chanspec); - - err = brcmf_fil_iovar_data_set(ifp, "p2p_ifadd", &if_request, - sizeof(if_request)); - if (err) - return err; - - return err; -} - -static int brcmf_p2p_disable_p2p_if(struct brcmf_cfg80211_vif *vif) -{ - struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev); - struct net_device *pri_ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(pri_ndev); - u8 *addr = vif->wdev.netdev->dev_addr; - - return brcmf_fil_iovar_data_set(ifp, "p2p_ifdis", addr, ETH_ALEN); -} - -static int brcmf_p2p_release_p2p_if(struct brcmf_cfg80211_vif *vif) -{ - struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev); - struct net_device *pri_ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(pri_ndev); - u8 *addr = vif->wdev.netdev->dev_addr; - - return brcmf_fil_iovar_data_set(ifp, "p2p_ifdel", addr, ETH_ALEN); -} - -/** - * brcmf_p2p_create_p2pdev() - create a P2P_DEVICE virtual interface. - * - * @p2p: P2P specific data. - * @wiphy: wiphy device of new interface. - * @addr: mac address for this new interface. - */ -static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, - struct wiphy *wiphy, - u8 *addr) -{ - struct brcmf_cfg80211_vif *p2p_vif; - struct brcmf_if *p2p_ifp; - struct brcmf_if *pri_ifp; - int err; - u32 bssidx; - - if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif) - return ERR_PTR(-ENOSPC); - - p2p_vif = brcmf_alloc_vif(p2p->cfg, NL80211_IFTYPE_P2P_DEVICE, - false); - if (IS_ERR(p2p_vif)) { - brcmf_err("could not create discovery vif\n"); - return (struct wireless_dev *)p2p_vif; - } - - pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; - brcmf_p2p_generate_bss_mac(p2p, addr); - brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr); - - brcmf_cfg80211_arm_vif_event(p2p->cfg, p2p_vif); - brcmf_fweh_p2pdev_setup(pri_ifp, true); - - /* Initialize P2P Discovery in the firmware */ - err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1); - if (err < 0) { - brcmf_err("set p2p_disc error\n"); - brcmf_fweh_p2pdev_setup(pri_ifp, false); - brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); - goto fail; - } - - /* wait for firmware event */ - err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD, - msecs_to_jiffies(1500)); - brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); - brcmf_fweh_p2pdev_setup(pri_ifp, false); - if (!err) { - brcmf_err("timeout occurred\n"); - err = -EIO; - goto fail; - } - - /* discovery interface created */ - p2p_ifp = p2p_vif->ifp; - p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif; - memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN); - memcpy(&p2p_vif->wdev.address, p2p->dev_addr, sizeof(p2p->dev_addr)); - - /* verify bsscfg index for P2P discovery */ - err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx); - if (err < 0) { - brcmf_err("retrieving discover bsscfg index failed\n"); - goto fail; - } - - WARN_ON(p2p_ifp->bssidx != bssidx); - - init_completion(&p2p->send_af_done); - INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler); - init_completion(&p2p->afx_hdl.act_frm_scan); - init_completion(&p2p->wait_next_af); - - return &p2p_vif->wdev; - -fail: - brcmf_free_vif(p2p_vif); - return ERR_PTR(err); -} - -/** - * brcmf_p2p_add_vif() - create a new P2P virtual interface. - * - * @wiphy: wiphy device of new interface. - * @name: name of the new interface. - * @name_assign_type: origin of the interface name - * @type: nl80211 interface type. - * @flags: not used. - * @params: contains mac address for P2P device. - */ -struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, - unsigned char name_assign_type, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - struct brcmf_cfg80211_vif *vif; - enum brcmf_fil_p2p_if_types iftype; - int err; - - if (brcmf_cfg80211_vif_event_armed(cfg)) - return ERR_PTR(-EBUSY); - - brcmf_dbg(INFO, "adding vif \"%s\" (type=%d)\n", name, type); - - switch (type) { - case NL80211_IFTYPE_P2P_CLIENT: - iftype = BRCMF_FIL_P2P_IF_CLIENT; - break; - case NL80211_IFTYPE_P2P_GO: - iftype = BRCMF_FIL_P2P_IF_GO; - break; - case NL80211_IFTYPE_P2P_DEVICE: - return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy, - params->macaddr); - default: - return ERR_PTR(-EOPNOTSUPP); - } - - vif = brcmf_alloc_vif(cfg, type, false); - if (IS_ERR(vif)) - return (struct wireless_dev *)vif; - brcmf_cfg80211_arm_vif_event(cfg, vif); - - err = brcmf_p2p_request_p2p_if(&cfg->p2p, ifp, cfg->p2p.int_addr, - iftype); - if (err) { - brcmf_cfg80211_arm_vif_event(cfg, NULL); - goto fail; - } - - /* wait for firmware event */ - err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, - msecs_to_jiffies(1500)); - brcmf_cfg80211_arm_vif_event(cfg, NULL); - if (!err) { - brcmf_err("timeout occurred\n"); - err = -EIO; - goto fail; - } - - /* interface created in firmware */ - ifp = vif->ifp; - if (!ifp) { - brcmf_err("no if pointer provided\n"); - err = -ENOENT; - goto fail; - } - - strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); - ifp->ndev->name_assign_type = name_assign_type; - err = brcmf_net_attach(ifp, true); - if (err) { - brcmf_err("Registering netdevice failed\n"); - goto fail; - } - - cfg->p2p.bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = vif; - /* Disable firmware roaming for P2P interface */ - brcmf_fil_iovar_int_set(ifp, "roam_off", 1); - if (iftype == BRCMF_FIL_P2P_IF_GO) { - /* set station timeout for p2p */ - brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCB_TIMEOUT, - BRCMF_SCB_TIMEOUT_VALUE); - } - return &ifp->vif->wdev; - -fail: - brcmf_free_vif(vif); - return ERR_PTR(err); -} - -/** - * brcmf_p2p_del_vif() - delete a P2P virtual interface. - * - * @wiphy: wiphy device of interface. - * @wdev: wireless device of interface. - */ -int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct brcmf_p2p_info *p2p = &cfg->p2p; - struct brcmf_cfg80211_vif *vif; - unsigned long jiffie_timeout = msecs_to_jiffies(1500); - bool wait_for_disable = false; - int err; - - brcmf_dbg(TRACE, "delete P2P vif\n"); - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - - brcmf_cfg80211_arm_vif_event(cfg, vif); - switch (vif->wdev.iftype) { - case NL80211_IFTYPE_P2P_CLIENT: - if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state)) - wait_for_disable = true; - break; - - case NL80211_IFTYPE_P2P_GO: - if (!brcmf_p2p_disable_p2p_if(vif)) - wait_for_disable = true; - break; - - case NL80211_IFTYPE_P2P_DEVICE: - if (!p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif) - return 0; - brcmf_p2p_cancel_remain_on_channel(vif->ifp); - brcmf_p2p_deinit_discovery(p2p); - default: - return -ENOTSUPP; - } - - clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); - brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n"); - - if (wait_for_disable) - wait_for_completion_timeout(&cfg->vif_disabled, - msecs_to_jiffies(500)); - - err = 0; - if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) { - brcmf_vif_clear_mgmt_ies(vif); - err = brcmf_p2p_release_p2p_if(vif); - } - if (!err) { - /* wait for firmware event */ - err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL, - jiffie_timeout); - if (!err) - err = -EIO; - else - err = 0; - } - if (err) - brcmf_remove_interface(vif->ifp); - - brcmf_cfg80211_arm_vif_event(cfg, NULL); - if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) - p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; - - return err; -} - -void brcmf_p2p_ifp_removed(struct brcmf_if *ifp) -{ - struct brcmf_cfg80211_info *cfg; - struct brcmf_cfg80211_vif *vif; - - brcmf_dbg(INFO, "P2P: device interface removed\n"); - vif = ifp->vif; - cfg = wdev_to_cfg(&vif->wdev); - cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; - rtnl_lock(); - cfg80211_unregister_wdev(&vif->wdev); - rtnl_unlock(); - brcmf_free_vif(vif); -} - -int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_p2p_info *p2p = &cfg->p2p; - struct brcmf_cfg80211_vif *vif; - int err; - - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - mutex_lock(&cfg->usr_sync); - err = brcmf_p2p_enable_discovery(p2p); - if (!err) - set_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); - mutex_unlock(&cfg->usr_sync); - return err; -} - -void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_p2p_info *p2p = &cfg->p2p; - struct brcmf_cfg80211_vif *vif; - - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - /* This call can be result of the unregister_wdev call. In that case - * we dont want to do anything anymore. Just return. The config vif - * will have been cleared at this point. - */ - if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) { - mutex_lock(&cfg->usr_sync); - /* Set the discovery state to SCAN */ - (void)brcmf_p2p_set_discover_state(vif->ifp, - WL_P2P_DISC_ST_SCAN, 0, 0); - brcmf_abort_scanning(cfg); - clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); - mutex_unlock(&cfg->usr_sync); - } -} - -/** - * brcmf_p2p_attach() - attach for P2P. - * - * @cfg: driver private data for cfg80211 interface. - * @p2pdev_forced: create p2p device interface at attach. - */ -s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced) -{ - struct brcmf_p2p_info *p2p; - struct brcmf_if *pri_ifp; - s32 err = 0; - void *err_ptr; - - p2p = &cfg->p2p; - p2p->cfg = cfg; - - pri_ifp = brcmf_get_ifp(cfg->pub, 0); - p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif; - - if (p2pdev_forced) { - err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL); - if (IS_ERR(err_ptr)) { - brcmf_err("P2P device creation failed.\n"); - err = PTR_ERR(err_ptr); - } - } else { - p2p->p2pdev_dynamically = true; - } - return err; -} - -/** - * brcmf_p2p_detach() - detach P2P. - * - * @p2p: P2P specific data. - */ -void brcmf_p2p_detach(struct brcmf_p2p_info *p2p) -{ - struct brcmf_cfg80211_vif *vif; - - vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - if (vif != NULL) { - brcmf_p2p_cancel_remain_on_channel(vif->ifp); - brcmf_p2p_deinit_discovery(p2p); - brcmf_remove_interface(vif->ifp); - } - /* just set it all to zero */ - memset(p2p, 0, sizeof(*p2p)); -} - diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/brcm80211/brcmfmac/p2p.h deleted file mode 100644 index 5d49059021a9..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2012 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 WL_CFGP2P_H_ -#define WL_CFGP2P_H_ - -#include - -struct brcmf_cfg80211_info; - -/** - * enum p2p_bss_type - different type of BSS configurations. - * - * @P2PAPI_BSSCFG_PRIMARY: maps to driver's primary bsscfg. - * @P2PAPI_BSSCFG_DEVICE: maps to driver's P2P device discovery bsscfg. - * @P2PAPI_BSSCFG_CONNECTION: maps to driver's P2P connection bsscfg. - * @P2PAPI_BSSCFG_MAX: used for range checking. - */ -enum p2p_bss_type { - P2PAPI_BSSCFG_PRIMARY, /* maps to driver's primary bsscfg */ - P2PAPI_BSSCFG_DEVICE, /* maps to driver's P2P device discovery bsscfg */ - P2PAPI_BSSCFG_CONNECTION, /* maps to driver's P2P connection bsscfg */ - P2PAPI_BSSCFG_MAX -}; - -/** - * struct p2p_bss - peer-to-peer bss related information. - * - * @vif: virtual interface of this P2P bss. - * @private_data: TBD - */ -struct p2p_bss { - struct brcmf_cfg80211_vif *vif; - void *private_data; -}; - -/** - * enum brcmf_p2p_status - P2P specific dongle status. - * - * @BRCMF_P2P_STATUS_IF_ADD: peer-to-peer vif add sent to dongle. - * @BRCMF_P2P_STATUS_IF_DEL: NOT-USED? - * @BRCMF_P2P_STATUS_IF_DELETING: peer-to-peer vif delete sent to dongle. - * @BRCMF_P2P_STATUS_IF_CHANGING: peer-to-peer vif change sent to dongle. - * @BRCMF_P2P_STATUS_IF_CHANGED: peer-to-peer vif change completed on dongle. - * @BRCMF_P2P_STATUS_ACTION_TX_COMPLETED: action frame tx completed. - * @BRCMF_P2P_STATUS_ACTION_TX_NOACK: action frame tx not acked. - * @BRCMF_P2P_STATUS_GO_NEG_PHASE: P2P GO negotiation ongoing. - * @BRCMF_P2P_STATUS_DISCOVER_LISTEN: P2P listen, remaining on channel. - * @BRCMF_P2P_STATUS_SENDING_ACT_FRAME: In the process of sending action frame. - * @BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN: extra listen time for af tx. - * @BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME: waiting for action frame response. - * @BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL: search channel for AF active. - */ -enum brcmf_p2p_status { - BRCMF_P2P_STATUS_ENABLED, - BRCMF_P2P_STATUS_IF_ADD, - BRCMF_P2P_STATUS_IF_DEL, - BRCMF_P2P_STATUS_IF_DELETING, - BRCMF_P2P_STATUS_IF_CHANGING, - BRCMF_P2P_STATUS_IF_CHANGED, - BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, - BRCMF_P2P_STATUS_ACTION_TX_NOACK, - BRCMF_P2P_STATUS_GO_NEG_PHASE, - BRCMF_P2P_STATUS_DISCOVER_LISTEN, - BRCMF_P2P_STATUS_SENDING_ACT_FRAME, - BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN, - BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, - BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL -}; - -/** - * struct afx_hdl - action frame off channel storage. - * - * @afx_work: worker thread for searching channel - * @act_frm_scan: thread synchronizing struct. - * @is_active: channel searching active. - * @peer_chan: current channel. - * @is_listen: sets mode for afx worker. - * @my_listen_chan: this peers listen channel. - * @peer_listen_chan: remote peers listen channel. - * @tx_dst_addr: mac address where tx af should be sent to. - */ -struct afx_hdl { - struct work_struct afx_work; - struct completion act_frm_scan; - bool is_active; - s32 peer_chan; - bool is_listen; - u16 my_listen_chan; - u16 peer_listen_chan; - u8 tx_dst_addr[ETH_ALEN]; -}; - -/** - * struct brcmf_p2p_info - p2p specific driver information. - * - * @cfg: driver private data for cfg80211 interface. - * @status: status of P2P (see enum brcmf_p2p_status). - * @dev_addr: P2P device address. - * @int_addr: P2P interface address. - * @bss_idx: informate for P2P bss types. - * @listen_timer: timer for @WL_P2P_DISC_ST_LISTEN discover state. - * @ssid: ssid for P2P GO. - * @listen_channel: channel for @WL_P2P_DISC_ST_LISTEN discover state. - * @remain_on_channel: contains copy of struct used by cfg80211. - * @remain_on_channel_cookie: cookie counter for remain on channel cmd - * @next_af_subtype: expected action frame subtype. - * @send_af_done: indication that action frame tx is complete. - * @afx_hdl: action frame search handler info. - * @af_sent_channel: channel action frame is sent. - * @af_tx_sent_jiffies: jiffies time when af tx was transmitted. - * @wait_next_af: thread synchronizing struct. - * @gon_req_action: about to send go negotiation requets frame. - * @block_gon_req_tx: drop tx go negotiation requets frame. - * @p2pdev_dynamically: is p2p device if created by module param or supplicant. - */ -struct brcmf_p2p_info { - struct brcmf_cfg80211_info *cfg; - unsigned long status; - u8 dev_addr[ETH_ALEN]; - u8 int_addr[ETH_ALEN]; - struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX]; - struct timer_list listen_timer; - struct brcmf_ssid ssid; - u8 listen_channel; - struct ieee80211_channel remain_on_channel; - u32 remain_on_channel_cookie; - u8 next_af_subtype; - struct completion send_af_done; - struct afx_hdl afx_hdl; - u32 af_sent_channel; - unsigned long af_tx_sent_jiffies; - struct completion wait_next_af; - bool gon_req_action; - bool block_gon_req_tx; - bool p2pdev_dynamically; -}; - -s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced); -void brcmf_p2p_detach(struct brcmf_p2p_info *p2p); -struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, - unsigned char name_assign_type, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params); -int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev); -int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, - enum brcmf_fil_p2p_if_types if_type); -void brcmf_p2p_ifp_removed(struct brcmf_if *ifp); -int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev); -void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev); -int brcmf_p2p_scan_prep(struct wiphy *wiphy, - struct cfg80211_scan_request *request, - struct brcmf_cfg80211_vif *vif); -int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, - struct ieee80211_channel *channel, - unsigned int duration, u64 *cookie); -int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, - void *data); -void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp); -int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, - void *data); -int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, - void *data); -bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, - struct brcmf_fil_af_params_le *af_params); -bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, - struct brcmf_bss_info_le *bi); -s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, - void *data); -#endif /* WL_CFGP2P_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c deleted file mode 100644 index 83d804221715..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ /dev/null @@ -1,2107 +0,0 @@ -/* Copyright (c) 2014 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "debug.h" -#include "bus.h" -#include "commonring.h" -#include "msgbuf.h" -#include "pcie.h" -#include "firmware.h" -#include "chip.h" - - -enum brcmf_pcie_state { - BRCMFMAC_PCIE_STATE_DOWN, - BRCMFMAC_PCIE_STATE_UP -}; - - -#define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin" -#define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt" -#define BRCMF_PCIE_4350_FW_NAME "brcm/brcmfmac4350-pcie.bin" -#define BRCMF_PCIE_4350_NVRAM_NAME "brcm/brcmfmac4350-pcie.txt" -#define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin" -#define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt" -#define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin" -#define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt" -#define BRCMF_PCIE_4358_FW_NAME "brcm/brcmfmac4358-pcie.bin" -#define BRCMF_PCIE_4358_NVRAM_NAME "brcm/brcmfmac4358-pcie.txt" -#define BRCMF_PCIE_4365_FW_NAME "brcm/brcmfmac4365b-pcie.bin" -#define BRCMF_PCIE_4365_NVRAM_NAME "brcm/brcmfmac4365b-pcie.txt" -#define BRCMF_PCIE_4366_FW_NAME "brcm/brcmfmac4366b-pcie.bin" -#define BRCMF_PCIE_4366_NVRAM_NAME "brcm/brcmfmac4366b-pcie.txt" -#define BRCMF_PCIE_4371_FW_NAME "brcm/brcmfmac4371-pcie.bin" -#define BRCMF_PCIE_4371_NVRAM_NAME "brcm/brcmfmac4371-pcie.txt" - -#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ - -#define BRCMF_PCIE_TCM_MAP_SIZE (4096 * 1024) -#define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024) - -/* backplane addres space accessed by BAR0 */ -#define BRCMF_PCIE_BAR0_WINDOW 0x80 -#define BRCMF_PCIE_BAR0_REG_SIZE 0x1000 -#define BRCMF_PCIE_BAR0_WRAPPERBASE 0x70 - -#define BRCMF_PCIE_BAR0_WRAPBASE_DMP_OFFSET 0x1000 -#define BRCMF_PCIE_BARO_PCIE_ENUM_OFFSET 0x2000 - -#define BRCMF_PCIE_ARMCR4REG_BANKIDX 0x40 -#define BRCMF_PCIE_ARMCR4REG_BANKPDA 0x4C - -#define BRCMF_PCIE_REG_INTSTATUS 0x90 -#define BRCMF_PCIE_REG_INTMASK 0x94 -#define BRCMF_PCIE_REG_SBMBX 0x98 - -#define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC - -#define BRCMF_PCIE_PCIE2REG_INTMASK 0x24 -#define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48 -#define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C -#define BRCMF_PCIE_PCIE2REG_CONFIGADDR 0x120 -#define BRCMF_PCIE_PCIE2REG_CONFIGDATA 0x124 -#define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX 0x140 - -#define BRCMF_PCIE_GENREV1 1 -#define BRCMF_PCIE_GENREV2 2 - -#define BRCMF_PCIE2_INTA 0x01 -#define BRCMF_PCIE2_INTB 0x02 - -#define BRCMF_PCIE_INT_0 0x01 -#define BRCMF_PCIE_INT_1 0x02 -#define BRCMF_PCIE_INT_DEF (BRCMF_PCIE_INT_0 | \ - BRCMF_PCIE_INT_1) - -#define BRCMF_PCIE_MB_INT_FN0_0 0x0100 -#define BRCMF_PCIE_MB_INT_FN0_1 0x0200 -#define BRCMF_PCIE_MB_INT_D2H0_DB0 0x10000 -#define BRCMF_PCIE_MB_INT_D2H0_DB1 0x20000 -#define BRCMF_PCIE_MB_INT_D2H1_DB0 0x40000 -#define BRCMF_PCIE_MB_INT_D2H1_DB1 0x80000 -#define BRCMF_PCIE_MB_INT_D2H2_DB0 0x100000 -#define BRCMF_PCIE_MB_INT_D2H2_DB1 0x200000 -#define BRCMF_PCIE_MB_INT_D2H3_DB0 0x400000 -#define BRCMF_PCIE_MB_INT_D2H3_DB1 0x800000 - -#define BRCMF_PCIE_MB_INT_D2H_DB (BRCMF_PCIE_MB_INT_D2H0_DB0 | \ - BRCMF_PCIE_MB_INT_D2H0_DB1 | \ - BRCMF_PCIE_MB_INT_D2H1_DB0 | \ - BRCMF_PCIE_MB_INT_D2H1_DB1 | \ - BRCMF_PCIE_MB_INT_D2H2_DB0 | \ - BRCMF_PCIE_MB_INT_D2H2_DB1 | \ - BRCMF_PCIE_MB_INT_D2H3_DB0 | \ - BRCMF_PCIE_MB_INT_D2H3_DB1) - -#define BRCMF_PCIE_MIN_SHARED_VERSION 5 -#define BRCMF_PCIE_MAX_SHARED_VERSION 5 -#define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF -#define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000 -#define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000 - -#define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000 -#define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000 - -#define BRCMF_SHARED_MAX_RXBUFPOST_OFFSET 34 -#define BRCMF_SHARED_RING_BASE_OFFSET 52 -#define BRCMF_SHARED_RX_DATAOFFSET_OFFSET 36 -#define BRCMF_SHARED_CONSOLE_ADDR_OFFSET 20 -#define BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET 40 -#define BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET 44 -#define BRCMF_SHARED_RING_INFO_ADDR_OFFSET 48 -#define BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET 52 -#define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET 56 -#define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET 64 -#define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET 68 - -#define BRCMF_RING_H2D_RING_COUNT_OFFSET 0 -#define BRCMF_RING_D2H_RING_COUNT_OFFSET 1 -#define BRCMF_RING_H2D_RING_MEM_OFFSET 4 -#define BRCMF_RING_H2D_RING_STATE_OFFSET 8 - -#define BRCMF_RING_MEM_BASE_ADDR_OFFSET 8 -#define BRCMF_RING_MAX_ITEM_OFFSET 4 -#define BRCMF_RING_LEN_ITEMS_OFFSET 6 -#define BRCMF_RING_MEM_SZ 16 -#define BRCMF_RING_STATE_SZ 8 - -#define BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET 4 -#define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET 8 -#define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET 12 -#define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET 16 -#define BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET 20 -#define BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET 28 -#define BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET 36 -#define BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET 44 -#define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET 0 -#define BRCMF_SHARED_RING_MAX_SUB_QUEUES 52 - -#define BRCMF_DEF_MAX_RXBUFPOST 255 - -#define BRCMF_CONSOLE_BUFADDR_OFFSET 8 -#define BRCMF_CONSOLE_BUFSIZE_OFFSET 12 -#define BRCMF_CONSOLE_WRITEIDX_OFFSET 16 - -#define BRCMF_DMA_D2H_SCRATCH_BUF_LEN 8 -#define BRCMF_DMA_D2H_RINGUPD_BUF_LEN 1024 - -#define BRCMF_D2H_DEV_D3_ACK 0x00000001 -#define BRCMF_D2H_DEV_DS_ENTER_REQ 0x00000002 -#define BRCMF_D2H_DEV_DS_EXIT_NOTE 0x00000004 - -#define BRCMF_H2D_HOST_D3_INFORM 0x00000001 -#define BRCMF_H2D_HOST_DS_ACK 0x00000002 -#define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008 -#define BRCMF_H2D_HOST_D0_INFORM 0x00000010 - -#define BRCMF_PCIE_MBDATA_TIMEOUT 2000 - -#define BRCMF_PCIE_CFGREG_STATUS_CMD 0x4 -#define BRCMF_PCIE_CFGREG_PM_CSR 0x4C -#define BRCMF_PCIE_CFGREG_MSI_CAP 0x58 -#define BRCMF_PCIE_CFGREG_MSI_ADDR_L 0x5C -#define BRCMF_PCIE_CFGREG_MSI_ADDR_H 0x60 -#define BRCMF_PCIE_CFGREG_MSI_DATA 0x64 -#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL 0xBC -#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2 0xDC -#define BRCMF_PCIE_CFGREG_RBAR_CTRL 0x228 -#define BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1 0x248 -#define BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG 0x4E0 -#define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4 -#define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3 - - -MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4350_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4350_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4365_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4365_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4366_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4366_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4371_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4371_NVRAM_NAME); - - -struct brcmf_pcie_console { - u32 base_addr; - u32 buf_addr; - u32 bufsize; - u32 read_idx; - u8 log_str[256]; - u8 log_idx; -}; - -struct brcmf_pcie_shared_info { - u32 tcm_base_address; - u32 flags; - struct brcmf_pcie_ringbuf *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; - struct brcmf_pcie_ringbuf *flowrings; - u16 max_rxbufpost; - u32 nrof_flowrings; - u32 rx_dataoffset; - u32 htod_mb_data_addr; - u32 dtoh_mb_data_addr; - u32 ring_info_addr; - struct brcmf_pcie_console console; - void *scratch; - dma_addr_t scratch_dmahandle; - void *ringupd; - dma_addr_t ringupd_dmahandle; -}; - -struct brcmf_pcie_core_info { - u32 base; - u32 wrapbase; -}; - -struct brcmf_pciedev_info { - enum brcmf_pcie_state state; - bool in_irq; - bool irq_requested; - struct pci_dev *pdev; - char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; - char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; - void __iomem *regs; - void __iomem *tcm; - u32 tcm_size; - u32 ram_base; - u32 ram_size; - struct brcmf_chip *ci; - u32 coreid; - u32 generic_corerev; - struct brcmf_pcie_shared_info shared; - void (*ringbell)(struct brcmf_pciedev_info *devinfo); - wait_queue_head_t mbdata_resp_wait; - bool mbdata_completed; - bool irq_allocated; - bool wowl_enabled; - u8 dma_idx_sz; - void *idxbuf; - u32 idxbuf_sz; - dma_addr_t idxbuf_dmahandle; - u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset); - void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset, - u16 value); -}; - -struct brcmf_pcie_ringbuf { - struct brcmf_commonring commonring; - dma_addr_t dma_handle; - u32 w_idx_addr; - u32 r_idx_addr; - struct brcmf_pciedev_info *devinfo; - u8 id; -}; - - -static const u32 brcmf_ring_max_item[BRCMF_NROF_COMMON_MSGRINGS] = { - BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM, - BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM, - BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM, - BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM, - BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM -}; - -static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = { - BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE, - BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE, - BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE, - BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE, - BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE -}; - - -static u32 -brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) -{ - void __iomem *address = devinfo->regs + reg_offset; - - return (ioread32(address)); -} - - -static void -brcmf_pcie_write_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset, - u32 value) -{ - void __iomem *address = devinfo->regs + reg_offset; - - iowrite32(value, address); -} - - -static u8 -brcmf_pcie_read_tcm8(struct brcmf_pciedev_info *devinfo, u32 mem_offset) -{ - void __iomem *address = devinfo->tcm + mem_offset; - - return (ioread8(address)); -} - - -static u16 -brcmf_pcie_read_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset) -{ - void __iomem *address = devinfo->tcm + mem_offset; - - return (ioread16(address)); -} - - -static void -brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset, - u16 value) -{ - void __iomem *address = devinfo->tcm + mem_offset; - - iowrite16(value, address); -} - - -static u16 -brcmf_pcie_read_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset) -{ - u16 *address = devinfo->idxbuf + mem_offset; - - return (*(address)); -} - - -static void -brcmf_pcie_write_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset, - u16 value) -{ - u16 *address = devinfo->idxbuf + mem_offset; - - *(address) = value; -} - - -static u32 -brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) -{ - void __iomem *address = devinfo->tcm + mem_offset; - - return (ioread32(address)); -} - - -static void -brcmf_pcie_write_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, - u32 value) -{ - void __iomem *address = devinfo->tcm + mem_offset; - - iowrite32(value, address); -} - - -static u32 -brcmf_pcie_read_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) -{ - void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; - - return (ioread32(addr)); -} - - -static void -brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, - u32 value) -{ - void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; - - iowrite32(value, addr); -} - - -static void -brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset, - void *srcaddr, u32 len) -{ - void __iomem *address = devinfo->tcm + mem_offset; - __le32 *src32; - __le16 *src16; - u8 *src8; - - if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) { - if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) { - src8 = (u8 *)srcaddr; - while (len) { - iowrite8(*src8, address); - address++; - src8++; - len--; - } - } else { - len = len / 2; - src16 = (__le16 *)srcaddr; - while (len) { - iowrite16(le16_to_cpu(*src16), address); - address += 2; - src16++; - len--; - } - } - } else { - len = len / 4; - src32 = (__le32 *)srcaddr; - while (len) { - iowrite32(le32_to_cpu(*src32), address); - address += 4; - src32++; - len--; - } - } -} - - -static void -brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset, - void *dstaddr, u32 len) -{ - void __iomem *address = devinfo->tcm + mem_offset; - __le32 *dst32; - __le16 *dst16; - u8 *dst8; - - if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) { - if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) { - dst8 = (u8 *)dstaddr; - while (len) { - *dst8 = ioread8(address); - address++; - dst8++; - len--; - } - } else { - len = len / 2; - dst16 = (__le16 *)dstaddr; - while (len) { - *dst16 = cpu_to_le16(ioread16(address)); - address += 2; - dst16++; - len--; - } - } - } else { - len = len / 4; - dst32 = (__le32 *)dstaddr; - while (len) { - *dst32 = cpu_to_le32(ioread32(address)); - address += 4; - dst32++; - len--; - } - } -} - - -#define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \ - CHIPCREGOFFS(reg), value) - - -static void -brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid) -{ - const struct pci_dev *pdev = devinfo->pdev; - struct brcmf_core *core; - u32 bar0_win; - - core = brcmf_chip_get_core(devinfo->ci, coreid); - if (core) { - bar0_win = core->base; - pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, bar0_win); - if (pci_read_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, - &bar0_win) == 0) { - if (bar0_win != core->base) { - bar0_win = core->base; - pci_write_config_dword(pdev, - BRCMF_PCIE_BAR0_WINDOW, - bar0_win); - } - } - } else { - brcmf_err("Unsupported core selected %x\n", coreid); - } -} - - -static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) -{ - struct brcmf_core *core; - u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD, - BRCMF_PCIE_CFGREG_PM_CSR, - BRCMF_PCIE_CFGREG_MSI_CAP, - BRCMF_PCIE_CFGREG_MSI_ADDR_L, - BRCMF_PCIE_CFGREG_MSI_ADDR_H, - BRCMF_PCIE_CFGREG_MSI_DATA, - BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2, - BRCMF_PCIE_CFGREG_RBAR_CTRL, - BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1, - BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG, - BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG }; - u32 i; - u32 val; - u32 lsc; - - if (!devinfo->ci) - return; - - /* Disable ASPM */ - brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, - &lsc); - val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB); - pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, - val); - - /* Watchdog reset */ - brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON); - WRITECC32(devinfo, watchdog, 4); - msleep(100); - - /* Restore ASPM */ - brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, - lsc); - - core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2); - if (core->rev <= 13) { - for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) { - brcmf_pcie_write_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_CONFIGADDR, - cfg_offset[i]); - val = brcmf_pcie_read_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_CONFIGDATA); - brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n", - cfg_offset[i], val); - brcmf_pcie_write_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_CONFIGDATA, - val); - } - } -} - - -static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) -{ - u32 config; - - brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - /* BAR1 window may not be sized properly */ - brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0); - config = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, config); - - device_wakeup_enable(&devinfo->pdev->dev); -} - - -static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) -{ - if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { - brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, - 5); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA, - 0); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, - 7); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA, - 0); - } - return 0; -} - - -static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, - u32 resetintr) -{ - struct brcmf_core *core; - - if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { - core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_INTERNAL_MEM); - brcmf_chip_resetcore(core, 0, 0, 0); - } - - return !brcmf_chip_set_active(devinfo->ci, resetintr); -} - - -static int -brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data) -{ - struct brcmf_pcie_shared_info *shared; - u32 addr; - u32 cur_htod_mb_data; - u32 i; - - shared = &devinfo->shared; - addr = shared->htod_mb_data_addr; - cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); - - if (cur_htod_mb_data != 0) - brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n", - cur_htod_mb_data); - - i = 0; - while (cur_htod_mb_data != 0) { - msleep(10); - i++; - if (i > 100) - return -EIO; - cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); - } - - brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); - pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); - pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); - - return 0; -} - - -static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) -{ - struct brcmf_pcie_shared_info *shared; - u32 addr; - u32 dtoh_mb_data; - - shared = &devinfo->shared; - addr = shared->dtoh_mb_data_addr; - dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); - - if (!dtoh_mb_data) - return; - - brcmf_pcie_write_tcm32(devinfo, addr, 0); - - brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data); - if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) { - brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n"); - brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_DS_ACK); - brcmf_dbg(PCIE, "D2H_MB_DATA: sent DEEP SLEEP ACK\n"); - } - if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) - brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); - if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) { - brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n"); - if (waitqueue_active(&devinfo->mbdata_resp_wait)) { - devinfo->mbdata_completed = true; - wake_up(&devinfo->mbdata_resp_wait); - } - } -} - - -static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) -{ - struct brcmf_pcie_shared_info *shared; - struct brcmf_pcie_console *console; - u32 addr; - - shared = &devinfo->shared; - console = &shared->console; - addr = shared->tcm_base_address + BRCMF_SHARED_CONSOLE_ADDR_OFFSET; - console->base_addr = brcmf_pcie_read_tcm32(devinfo, addr); - - addr = console->base_addr + BRCMF_CONSOLE_BUFADDR_OFFSET; - console->buf_addr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET; - console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr); - - brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n", - console->base_addr, console->buf_addr, console->bufsize); -} - - -static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) -{ - struct brcmf_pcie_console *console; - u32 addr; - u8 ch; - u32 newidx; - - if (!BRCMF_FWCON_ON()) - return; - - console = &devinfo->shared.console; - addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET; - newidx = brcmf_pcie_read_tcm32(devinfo, addr); - while (newidx != console->read_idx) { - addr = console->buf_addr + console->read_idx; - ch = brcmf_pcie_read_tcm8(devinfo, addr); - console->read_idx++; - if (console->read_idx == console->bufsize) - console->read_idx = 0; - if (ch == '\r') - continue; - console->log_str[console->log_idx] = ch; - console->log_idx++; - if ((ch != '\n') && - (console->log_idx == (sizeof(console->log_str) - 2))) { - ch = '\n'; - console->log_str[console->log_idx] = ch; - console->log_idx++; - } - if (ch == '\n') { - console->log_str[console->log_idx] = 0; - pr_debug("CONSOLE: %s", console->log_str); - console->log_idx = 0; - } - } -} - - -static __used void brcmf_pcie_ringbell_v1(struct brcmf_pciedev_info *devinfo) -{ - u32 reg_value; - - brcmf_dbg(PCIE, "RING !\n"); - reg_value = brcmf_pcie_read_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_MAILBOXINT); - reg_value |= BRCMF_PCIE2_INTB; - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, - reg_value); -} - - -static void brcmf_pcie_ringbell_v2(struct brcmf_pciedev_info *devinfo) -{ - brcmf_dbg(PCIE, "RING !\n"); - /* Any arbitrary value will do, lets use 1 */ - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1); -} - - -static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo) -{ - if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) - pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK, - 0); - else - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, - 0); -} - - -static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo) -{ - if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) - pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK, - BRCMF_PCIE_INT_DEF); - else - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, - BRCMF_PCIE_MB_INT_D2H_DB | - BRCMF_PCIE_MB_INT_FN0_0 | - BRCMF_PCIE_MB_INT_FN0_1); -} - - -static irqreturn_t brcmf_pcie_quick_check_isr_v1(int irq, void *arg) -{ - struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; - u32 status; - - status = 0; - pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTSTATUS, &status); - if (status) { - brcmf_pcie_intr_disable(devinfo); - brcmf_dbg(PCIE, "Enter\n"); - return IRQ_WAKE_THREAD; - } - return IRQ_NONE; -} - - -static irqreturn_t brcmf_pcie_quick_check_isr_v2(int irq, void *arg) -{ - struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; - - if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT)) { - brcmf_pcie_intr_disable(devinfo); - brcmf_dbg(PCIE, "Enter\n"); - return IRQ_WAKE_THREAD; - } - return IRQ_NONE; -} - - -static irqreturn_t brcmf_pcie_isr_thread_v1(int irq, void *arg) -{ - struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; - const struct pci_dev *pdev = devinfo->pdev; - u32 status; - - devinfo->in_irq = true; - status = 0; - pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status); - brcmf_dbg(PCIE, "Enter %x\n", status); - if (status) { - pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status); - if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) - brcmf_proto_msgbuf_rx_trigger(&devinfo->pdev->dev); - } - if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) - brcmf_pcie_intr_enable(devinfo); - devinfo->in_irq = false; - return IRQ_HANDLED; -} - - -static irqreturn_t brcmf_pcie_isr_thread_v2(int irq, void *arg) -{ - struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; - u32 status; - - devinfo->in_irq = true; - status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); - brcmf_dbg(PCIE, "Enter %x\n", status); - if (status) { - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, - status); - if (status & (BRCMF_PCIE_MB_INT_FN0_0 | - BRCMF_PCIE_MB_INT_FN0_1)) - brcmf_pcie_handle_mb_data(devinfo); - if (status & BRCMF_PCIE_MB_INT_D2H_DB) { - if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) - brcmf_proto_msgbuf_rx_trigger( - &devinfo->pdev->dev); - } - } - brcmf_pcie_bus_console_read(devinfo); - if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) - brcmf_pcie_intr_enable(devinfo); - devinfo->in_irq = false; - return IRQ_HANDLED; -} - - -static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo) -{ - struct pci_dev *pdev; - - pdev = devinfo->pdev; - - brcmf_pcie_intr_disable(devinfo); - - brcmf_dbg(PCIE, "Enter\n"); - /* is it a v1 or v2 implementation */ - devinfo->irq_requested = false; - pci_enable_msi(pdev); - if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { - if (request_threaded_irq(pdev->irq, - brcmf_pcie_quick_check_isr_v1, - brcmf_pcie_isr_thread_v1, - IRQF_SHARED, "brcmf_pcie_intr", - devinfo)) { - pci_disable_msi(pdev); - brcmf_err("Failed to request IRQ %d\n", pdev->irq); - return -EIO; - } - } else { - if (request_threaded_irq(pdev->irq, - brcmf_pcie_quick_check_isr_v2, - brcmf_pcie_isr_thread_v2, - IRQF_SHARED, "brcmf_pcie_intr", - devinfo)) { - pci_disable_msi(pdev); - brcmf_err("Failed to request IRQ %d\n", pdev->irq); - return -EIO; - } - } - devinfo->irq_requested = true; - devinfo->irq_allocated = true; - return 0; -} - - -static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo) -{ - struct pci_dev *pdev; - u32 status; - u32 count; - - if (!devinfo->irq_allocated) - return; - - pdev = devinfo->pdev; - - brcmf_pcie_intr_disable(devinfo); - if (!devinfo->irq_requested) - return; - devinfo->irq_requested = false; - free_irq(pdev->irq, devinfo); - pci_disable_msi(pdev); - - msleep(50); - count = 0; - while ((devinfo->in_irq) && (count < 20)) { - msleep(50); - count++; - } - if (devinfo->in_irq) - brcmf_err("Still in IRQ (processing) !!!\n"); - - if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { - status = 0; - pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status); - pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status); - } else { - status = brcmf_pcie_read_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_MAILBOXINT); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, - status); - } - devinfo->irq_allocated = false; -} - - -static int brcmf_pcie_ring_mb_write_rptr(void *ctx) -{ - struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; - struct brcmf_pciedev_info *devinfo = ring->devinfo; - struct brcmf_commonring *commonring = &ring->commonring; - - if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) - return -EIO; - - brcmf_dbg(PCIE, "W r_ptr %d (%d), ring %d\n", commonring->r_ptr, - commonring->w_ptr, ring->id); - - devinfo->write_ptr(devinfo, ring->r_idx_addr, commonring->r_ptr); - - return 0; -} - - -static int brcmf_pcie_ring_mb_write_wptr(void *ctx) -{ - struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; - struct brcmf_pciedev_info *devinfo = ring->devinfo; - struct brcmf_commonring *commonring = &ring->commonring; - - if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) - return -EIO; - - brcmf_dbg(PCIE, "W w_ptr %d (%d), ring %d\n", commonring->w_ptr, - commonring->r_ptr, ring->id); - - devinfo->write_ptr(devinfo, ring->w_idx_addr, commonring->w_ptr); - - return 0; -} - - -static int brcmf_pcie_ring_mb_ring_bell(void *ctx) -{ - struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; - struct brcmf_pciedev_info *devinfo = ring->devinfo; - - if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) - return -EIO; - - devinfo->ringbell(devinfo); - - return 0; -} - - -static int brcmf_pcie_ring_mb_update_rptr(void *ctx) -{ - struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; - struct brcmf_pciedev_info *devinfo = ring->devinfo; - struct brcmf_commonring *commonring = &ring->commonring; - - if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) - return -EIO; - - commonring->r_ptr = devinfo->read_ptr(devinfo, ring->r_idx_addr); - - brcmf_dbg(PCIE, "R r_ptr %d (%d), ring %d\n", commonring->r_ptr, - commonring->w_ptr, ring->id); - - return 0; -} - - -static int brcmf_pcie_ring_mb_update_wptr(void *ctx) -{ - struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; - struct brcmf_pciedev_info *devinfo = ring->devinfo; - struct brcmf_commonring *commonring = &ring->commonring; - - if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) - return -EIO; - - commonring->w_ptr = devinfo->read_ptr(devinfo, ring->w_idx_addr); - - brcmf_dbg(PCIE, "R w_ptr %d (%d), ring %d\n", commonring->w_ptr, - commonring->r_ptr, ring->id); - - return 0; -} - - -static void * -brcmf_pcie_init_dmabuffer_for_device(struct brcmf_pciedev_info *devinfo, - u32 size, u32 tcm_dma_phys_addr, - dma_addr_t *dma_handle) -{ - void *ring; - u64 address; - - ring = dma_alloc_coherent(&devinfo->pdev->dev, size, dma_handle, - GFP_KERNEL); - if (!ring) - return NULL; - - address = (u64)*dma_handle; - brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr, - address & 0xffffffff); - brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr + 4, address >> 32); - - memset(ring, 0, size); - - return (ring); -} - - -static struct brcmf_pcie_ringbuf * -brcmf_pcie_alloc_dma_and_ring(struct brcmf_pciedev_info *devinfo, u32 ring_id, - u32 tcm_ring_phys_addr) -{ - void *dma_buf; - dma_addr_t dma_handle; - struct brcmf_pcie_ringbuf *ring; - u32 size; - u32 addr; - - size = brcmf_ring_max_item[ring_id] * brcmf_ring_itemsize[ring_id]; - dma_buf = brcmf_pcie_init_dmabuffer_for_device(devinfo, size, - tcm_ring_phys_addr + BRCMF_RING_MEM_BASE_ADDR_OFFSET, - &dma_handle); - if (!dma_buf) - return NULL; - - addr = tcm_ring_phys_addr + BRCMF_RING_MAX_ITEM_OFFSET; - brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_max_item[ring_id]); - addr = tcm_ring_phys_addr + BRCMF_RING_LEN_ITEMS_OFFSET; - brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_itemsize[ring_id]); - - ring = kzalloc(sizeof(*ring), GFP_KERNEL); - if (!ring) { - dma_free_coherent(&devinfo->pdev->dev, size, dma_buf, - dma_handle); - return NULL; - } - brcmf_commonring_config(&ring->commonring, brcmf_ring_max_item[ring_id], - brcmf_ring_itemsize[ring_id], dma_buf); - ring->dma_handle = dma_handle; - ring->devinfo = devinfo; - brcmf_commonring_register_cb(&ring->commonring, - brcmf_pcie_ring_mb_ring_bell, - brcmf_pcie_ring_mb_update_rptr, - brcmf_pcie_ring_mb_update_wptr, - brcmf_pcie_ring_mb_write_rptr, - brcmf_pcie_ring_mb_write_wptr, ring); - - return (ring); -} - - -static void brcmf_pcie_release_ringbuffer(struct device *dev, - struct brcmf_pcie_ringbuf *ring) -{ - void *dma_buf; - u32 size; - - if (!ring) - return; - - dma_buf = ring->commonring.buf_addr; - if (dma_buf) { - size = ring->commonring.depth * ring->commonring.item_len; - dma_free_coherent(dev, size, dma_buf, ring->dma_handle); - } - kfree(ring); -} - - -static void brcmf_pcie_release_ringbuffers(struct brcmf_pciedev_info *devinfo) -{ - u32 i; - - for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++) { - brcmf_pcie_release_ringbuffer(&devinfo->pdev->dev, - devinfo->shared.commonrings[i]); - devinfo->shared.commonrings[i] = NULL; - } - kfree(devinfo->shared.flowrings); - devinfo->shared.flowrings = NULL; - if (devinfo->idxbuf) { - dma_free_coherent(&devinfo->pdev->dev, - devinfo->idxbuf_sz, - devinfo->idxbuf, - devinfo->idxbuf_dmahandle); - devinfo->idxbuf = NULL; - } -} - - -static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) -{ - struct brcmf_pcie_ringbuf *ring; - struct brcmf_pcie_ringbuf *rings; - u32 ring_addr; - u32 d2h_w_idx_ptr; - u32 d2h_r_idx_ptr; - u32 h2d_w_idx_ptr; - u32 h2d_r_idx_ptr; - u32 addr; - u32 ring_mem_ptr; - u32 i; - u64 address; - u32 bufsz; - u16 max_sub_queues; - u8 idx_offset; - - ring_addr = devinfo->shared.ring_info_addr; - brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr); - addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; - max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); - - if (devinfo->dma_idx_sz != 0) { - bufsz = (BRCMF_NROF_D2H_COMMON_MSGRINGS + max_sub_queues) * - devinfo->dma_idx_sz * 2; - devinfo->idxbuf = dma_alloc_coherent(&devinfo->pdev->dev, bufsz, - &devinfo->idxbuf_dmahandle, - GFP_KERNEL); - if (!devinfo->idxbuf) - devinfo->dma_idx_sz = 0; - } - - if (devinfo->dma_idx_sz == 0) { - addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; - d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; - d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; - h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; - h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - idx_offset = sizeof(u32); - devinfo->write_ptr = brcmf_pcie_write_tcm16; - devinfo->read_ptr = brcmf_pcie_read_tcm16; - brcmf_dbg(PCIE, "Using TCM indices\n"); - } else { - memset(devinfo->idxbuf, 0, bufsz); - devinfo->idxbuf_sz = bufsz; - idx_offset = devinfo->dma_idx_sz; - devinfo->write_ptr = brcmf_pcie_write_idx; - devinfo->read_ptr = brcmf_pcie_read_idx; - - h2d_w_idx_ptr = 0; - addr = ring_addr + BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET; - address = (u64)devinfo->idxbuf_dmahandle; - brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); - brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); - - h2d_r_idx_ptr = h2d_w_idx_ptr + max_sub_queues * idx_offset; - addr = ring_addr + BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET; - address += max_sub_queues * idx_offset; - brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); - brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); - - d2h_w_idx_ptr = h2d_r_idx_ptr + max_sub_queues * idx_offset; - addr = ring_addr + BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET; - address += max_sub_queues * idx_offset; - brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); - brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); - - d2h_r_idx_ptr = d2h_w_idx_ptr + - BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; - addr = ring_addr + BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET; - address += BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; - brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); - brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); - brcmf_dbg(PCIE, "Using host memory indices\n"); - } - - addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET; - ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - - for (i = 0; i < BRCMF_NROF_H2D_COMMON_MSGRINGS; i++) { - ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr); - if (!ring) - goto fail; - ring->w_idx_addr = h2d_w_idx_ptr; - ring->r_idx_addr = h2d_r_idx_ptr; - ring->id = i; - devinfo->shared.commonrings[i] = ring; - - h2d_w_idx_ptr += idx_offset; - h2d_r_idx_ptr += idx_offset; - ring_mem_ptr += BRCMF_RING_MEM_SZ; - } - - for (i = BRCMF_NROF_H2D_COMMON_MSGRINGS; - i < BRCMF_NROF_COMMON_MSGRINGS; i++) { - ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr); - if (!ring) - goto fail; - ring->w_idx_addr = d2h_w_idx_ptr; - ring->r_idx_addr = d2h_r_idx_ptr; - ring->id = i; - devinfo->shared.commonrings[i] = ring; - - d2h_w_idx_ptr += idx_offset; - d2h_r_idx_ptr += idx_offset; - ring_mem_ptr += BRCMF_RING_MEM_SZ; - } - - devinfo->shared.nrof_flowrings = - max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS; - rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring), - GFP_KERNEL); - if (!rings) - goto fail; - - brcmf_dbg(PCIE, "Nr of flowrings is %d\n", - devinfo->shared.nrof_flowrings); - - for (i = 0; i < devinfo->shared.nrof_flowrings; i++) { - ring = &rings[i]; - ring->devinfo = devinfo; - ring->id = i + BRCMF_NROF_COMMON_MSGRINGS; - brcmf_commonring_register_cb(&ring->commonring, - brcmf_pcie_ring_mb_ring_bell, - brcmf_pcie_ring_mb_update_rptr, - brcmf_pcie_ring_mb_update_wptr, - brcmf_pcie_ring_mb_write_rptr, - brcmf_pcie_ring_mb_write_wptr, - ring); - ring->w_idx_addr = h2d_w_idx_ptr; - ring->r_idx_addr = h2d_r_idx_ptr; - h2d_w_idx_ptr += idx_offset; - h2d_r_idx_ptr += idx_offset; - } - devinfo->shared.flowrings = rings; - - return 0; - -fail: - brcmf_err("Allocating ring buffers failed\n"); - brcmf_pcie_release_ringbuffers(devinfo); - return -ENOMEM; -} - - -static void -brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo) -{ - if (devinfo->shared.scratch) - dma_free_coherent(&devinfo->pdev->dev, - BRCMF_DMA_D2H_SCRATCH_BUF_LEN, - devinfo->shared.scratch, - devinfo->shared.scratch_dmahandle); - if (devinfo->shared.ringupd) - dma_free_coherent(&devinfo->pdev->dev, - BRCMF_DMA_D2H_RINGUPD_BUF_LEN, - devinfo->shared.ringupd, - devinfo->shared.ringupd_dmahandle); -} - -static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) -{ - u64 address; - u32 addr; - - devinfo->shared.scratch = dma_alloc_coherent(&devinfo->pdev->dev, - BRCMF_DMA_D2H_SCRATCH_BUF_LEN, - &devinfo->shared.scratch_dmahandle, GFP_KERNEL); - if (!devinfo->shared.scratch) - goto fail; - - memset(devinfo->shared.scratch, 0, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); - - addr = devinfo->shared.tcm_base_address + - BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET; - address = (u64)devinfo->shared.scratch_dmahandle; - brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); - brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); - addr = devinfo->shared.tcm_base_address + - BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET; - brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); - - devinfo->shared.ringupd = dma_alloc_coherent(&devinfo->pdev->dev, - BRCMF_DMA_D2H_RINGUPD_BUF_LEN, - &devinfo->shared.ringupd_dmahandle, GFP_KERNEL); - if (!devinfo->shared.ringupd) - goto fail; - - memset(devinfo->shared.ringupd, 0, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); - - addr = devinfo->shared.tcm_base_address + - BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET; - address = (u64)devinfo->shared.ringupd_dmahandle; - brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); - brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); - addr = devinfo->shared.tcm_base_address + - BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET; - brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); - return 0; - -fail: - brcmf_err("Allocating scratch buffers failed\n"); - brcmf_pcie_release_scratchbuffers(devinfo); - return -ENOMEM; -} - - -static void brcmf_pcie_down(struct device *dev) -{ -} - - -static int brcmf_pcie_tx(struct device *dev, struct sk_buff *skb) -{ - return 0; -} - - -static int brcmf_pcie_tx_ctlpkt(struct device *dev, unsigned char *msg, - uint len) -{ - return 0; -} - - -static int brcmf_pcie_rx_ctlpkt(struct device *dev, unsigned char *msg, - uint len) -{ - return 0; -} - - -static void brcmf_pcie_wowl_config(struct device *dev, bool enabled) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; - struct brcmf_pciedev_info *devinfo = buspub->devinfo; - - brcmf_dbg(PCIE, "Configuring WOWL, enabled=%d\n", enabled); - devinfo->wowl_enabled = enabled; - if (enabled) - device_set_wakeup_enable(&devinfo->pdev->dev, true); - else - device_set_wakeup_enable(&devinfo->pdev->dev, false); -} - - -static size_t brcmf_pcie_get_ramsize(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; - struct brcmf_pciedev_info *devinfo = buspub->devinfo; - - return devinfo->ci->ramsize - devinfo->ci->srsize; -} - - -static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; - struct brcmf_pciedev_info *devinfo = buspub->devinfo; - - brcmf_dbg(PCIE, "dump at 0x%08X: len=%zu\n", devinfo->ci->rambase, len); - brcmf_pcie_copy_dev_tomem(devinfo, devinfo->ci->rambase, data, len); - return 0; -} - - -static struct brcmf_bus_ops brcmf_pcie_bus_ops = { - .txdata = brcmf_pcie_tx, - .stop = brcmf_pcie_down, - .txctl = brcmf_pcie_tx_ctlpkt, - .rxctl = brcmf_pcie_rx_ctlpkt, - .wowl_config = brcmf_pcie_wowl_config, - .get_ramsize = brcmf_pcie_get_ramsize, - .get_memdump = brcmf_pcie_get_memdump, -}; - - -static int -brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, - u32 sharedram_addr) -{ - struct brcmf_pcie_shared_info *shared; - u32 addr; - u32 version; - - shared = &devinfo->shared; - shared->tcm_base_address = sharedram_addr; - - shared->flags = brcmf_pcie_read_tcm32(devinfo, sharedram_addr); - version = shared->flags & BRCMF_PCIE_SHARED_VERSION_MASK; - brcmf_dbg(PCIE, "PCIe protocol version %d\n", version); - if ((version > BRCMF_PCIE_MAX_SHARED_VERSION) || - (version < BRCMF_PCIE_MIN_SHARED_VERSION)) { - brcmf_err("Unsupported PCIE version %d\n", version); - return -EINVAL; - } - - /* check firmware support dma indicies */ - if (shared->flags & BRCMF_PCIE_SHARED_DMA_INDEX) { - if (shared->flags & BRCMF_PCIE_SHARED_DMA_2B_IDX) - devinfo->dma_idx_sz = sizeof(u16); - else - devinfo->dma_idx_sz = sizeof(u32); - } - - addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET; - shared->max_rxbufpost = brcmf_pcie_read_tcm16(devinfo, addr); - if (shared->max_rxbufpost == 0) - shared->max_rxbufpost = BRCMF_DEF_MAX_RXBUFPOST; - - addr = sharedram_addr + BRCMF_SHARED_RX_DATAOFFSET_OFFSET; - shared->rx_dataoffset = brcmf_pcie_read_tcm32(devinfo, addr); - - addr = sharedram_addr + BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET; - shared->htod_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr); - - addr = sharedram_addr + BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET; - shared->dtoh_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr); - - addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET; - shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr); - - brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n", - shared->max_rxbufpost, shared->rx_dataoffset); - - brcmf_pcie_bus_console_init(devinfo); - - return 0; -} - - -static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) -{ - char *fw_name; - char *nvram_name; - uint fw_len, nv_len; - char end; - - brcmf_dbg(PCIE, "Enter, chip 0x%04x chiprev %d\n", devinfo->ci->chip, - devinfo->ci->chiprev); - - switch (devinfo->ci->chip) { - case BRCM_CC_43602_CHIP_ID: - fw_name = BRCMF_PCIE_43602_FW_NAME; - nvram_name = BRCMF_PCIE_43602_NVRAM_NAME; - break; - case BRCM_CC_4350_CHIP_ID: - fw_name = BRCMF_PCIE_4350_FW_NAME; - nvram_name = BRCMF_PCIE_4350_NVRAM_NAME; - break; - case BRCM_CC_4356_CHIP_ID: - fw_name = BRCMF_PCIE_4356_FW_NAME; - nvram_name = BRCMF_PCIE_4356_NVRAM_NAME; - break; - case BRCM_CC_43567_CHIP_ID: - case BRCM_CC_43569_CHIP_ID: - case BRCM_CC_43570_CHIP_ID: - fw_name = BRCMF_PCIE_43570_FW_NAME; - nvram_name = BRCMF_PCIE_43570_NVRAM_NAME; - break; - case BRCM_CC_4358_CHIP_ID: - fw_name = BRCMF_PCIE_4358_FW_NAME; - nvram_name = BRCMF_PCIE_4358_NVRAM_NAME; - break; - case BRCM_CC_4365_CHIP_ID: - fw_name = BRCMF_PCIE_4365_FW_NAME; - nvram_name = BRCMF_PCIE_4365_NVRAM_NAME; - break; - case BRCM_CC_4366_CHIP_ID: - fw_name = BRCMF_PCIE_4366_FW_NAME; - nvram_name = BRCMF_PCIE_4366_NVRAM_NAME; - break; - case BRCM_CC_4371_CHIP_ID: - fw_name = BRCMF_PCIE_4371_FW_NAME; - nvram_name = BRCMF_PCIE_4371_NVRAM_NAME; - break; - default: - brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip); - return -ENODEV; - } - - fw_len = sizeof(devinfo->fw_name) - 1; - nv_len = sizeof(devinfo->nvram_name) - 1; - /* check if firmware path is provided by module parameter */ - if (brcmf_firmware_path[0] != '\0') { - strncpy(devinfo->fw_name, brcmf_firmware_path, fw_len); - strncpy(devinfo->nvram_name, brcmf_firmware_path, nv_len); - fw_len -= strlen(devinfo->fw_name); - nv_len -= strlen(devinfo->nvram_name); - - end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; - if (end != '/') { - strncat(devinfo->fw_name, "/", fw_len); - strncat(devinfo->nvram_name, "/", nv_len); - fw_len--; - nv_len--; - } - } - strncat(devinfo->fw_name, fw_name, fw_len); - strncat(devinfo->nvram_name, nvram_name, nv_len); - - return 0; -} - - -static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, - const struct firmware *fw, void *nvram, - u32 nvram_len) -{ - u32 sharedram_addr; - u32 sharedram_addr_written; - u32 loop_counter; - int err; - u32 address; - u32 resetintr; - - devinfo->ringbell = brcmf_pcie_ringbell_v2; - devinfo->generic_corerev = BRCMF_PCIE_GENREV2; - - brcmf_dbg(PCIE, "Halt ARM.\n"); - err = brcmf_pcie_enter_download_state(devinfo); - if (err) - return err; - - brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name); - brcmf_pcie_copy_mem_todev(devinfo, devinfo->ci->rambase, - (void *)fw->data, fw->size); - - resetintr = get_unaligned_le32(fw->data); - release_firmware(fw); - - /* reset last 4 bytes of RAM address. to be used for shared - * area. This identifies when FW is running - */ - brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, 0); - - if (nvram) { - brcmf_dbg(PCIE, "Download NVRAM %s\n", devinfo->nvram_name); - address = devinfo->ci->rambase + devinfo->ci->ramsize - - nvram_len; - brcmf_pcie_copy_mem_todev(devinfo, address, nvram, nvram_len); - brcmf_fw_nvram_free(nvram); - } else { - brcmf_dbg(PCIE, "No matching NVRAM file found %s\n", - devinfo->nvram_name); - } - - sharedram_addr_written = brcmf_pcie_read_ram32(devinfo, - devinfo->ci->ramsize - - 4); - brcmf_dbg(PCIE, "Bring ARM in running state\n"); - err = brcmf_pcie_exit_download_state(devinfo, resetintr); - if (err) - return err; - - brcmf_dbg(PCIE, "Wait for FW init\n"); - sharedram_addr = sharedram_addr_written; - loop_counter = BRCMF_PCIE_FW_UP_TIMEOUT / 50; - while ((sharedram_addr == sharedram_addr_written) && (loop_counter)) { - msleep(50); - sharedram_addr = brcmf_pcie_read_ram32(devinfo, - devinfo->ci->ramsize - - 4); - loop_counter--; - } - if (sharedram_addr == sharedram_addr_written) { - brcmf_err("FW failed to initialize\n"); - return -ENODEV; - } - brcmf_dbg(PCIE, "Shared RAM addr: 0x%08x\n", sharedram_addr); - - return (brcmf_pcie_init_share_ram_info(devinfo, sharedram_addr)); -} - - -static int brcmf_pcie_get_resource(struct brcmf_pciedev_info *devinfo) -{ - struct pci_dev *pdev; - int err; - phys_addr_t bar0_addr, bar1_addr; - ulong bar1_size; - - pdev = devinfo->pdev; - - err = pci_enable_device(pdev); - if (err) { - brcmf_err("pci_enable_device failed err=%d\n", err); - return err; - } - - pci_set_master(pdev); - - /* Bar-0 mapped address */ - bar0_addr = pci_resource_start(pdev, 0); - /* Bar-1 mapped address */ - bar1_addr = pci_resource_start(pdev, 2); - /* read Bar-1 mapped memory range */ - bar1_size = pci_resource_len(pdev, 2); - if ((bar1_size == 0) || (bar1_addr == 0)) { - brcmf_err("BAR1 Not enabled, device size=%ld, addr=%#016llx\n", - bar1_size, (unsigned long long)bar1_addr); - return -EINVAL; - } - - devinfo->regs = ioremap_nocache(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE); - devinfo->tcm = ioremap_nocache(bar1_addr, BRCMF_PCIE_TCM_MAP_SIZE); - devinfo->tcm_size = BRCMF_PCIE_TCM_MAP_SIZE; - - if (!devinfo->regs || !devinfo->tcm) { - brcmf_err("ioremap() failed (%p,%p)\n", devinfo->regs, - devinfo->tcm); - return -EINVAL; - } - brcmf_dbg(PCIE, "Phys addr : reg space = %p base addr %#016llx\n", - devinfo->regs, (unsigned long long)bar0_addr); - brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx\n", - devinfo->tcm, (unsigned long long)bar1_addr); - - return 0; -} - - -static void brcmf_pcie_release_resource(struct brcmf_pciedev_info *devinfo) -{ - if (devinfo->tcm) - iounmap(devinfo->tcm); - if (devinfo->regs) - iounmap(devinfo->regs); - - pci_disable_device(devinfo->pdev); -} - - -static int brcmf_pcie_attach_bus(struct device *dev) -{ - int ret; - - /* Attach to the common driver interface */ - ret = brcmf_attach(dev); - if (ret) { - brcmf_err("brcmf_attach failed\n"); - } else { - ret = brcmf_bus_start(dev); - if (ret) - brcmf_err("dongle is not responding\n"); - } - - return ret; -} - - -static u32 brcmf_pcie_buscore_prep_addr(const struct pci_dev *pdev, u32 addr) -{ - u32 ret_addr; - - ret_addr = addr & (BRCMF_PCIE_BAR0_REG_SIZE - 1); - addr &= ~(BRCMF_PCIE_BAR0_REG_SIZE - 1); - pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, addr); - - return ret_addr; -} - - -static u32 brcmf_pcie_buscore_read32(void *ctx, u32 addr) -{ - struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; - - addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr); - return brcmf_pcie_read_reg32(devinfo, addr); -} - - -static void brcmf_pcie_buscore_write32(void *ctx, u32 addr, u32 value) -{ - struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; - - addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr); - brcmf_pcie_write_reg32(devinfo, addr, value); -} - - -static int brcmf_pcie_buscoreprep(void *ctx) -{ - return brcmf_pcie_get_resource(ctx); -} - - -static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip) -{ - struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; - u32 val; - - devinfo->ci = chip; - brcmf_pcie_reset_device(devinfo); - - val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); - if (val != 0xffffffff) - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, - val); - - return 0; -} - - -static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, - u32 rstvec) -{ - struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; - - brcmf_pcie_write_tcm32(devinfo, 0, rstvec); -} - - -static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { - .prepare = brcmf_pcie_buscoreprep, - .reset = brcmf_pcie_buscore_reset, - .activate = brcmf_pcie_buscore_activate, - .read32 = brcmf_pcie_buscore_read32, - .write32 = brcmf_pcie_buscore_write32, -}; - -static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw, - void *nvram, u32 nvram_len) -{ - struct brcmf_bus *bus = dev_get_drvdata(dev); - struct brcmf_pciedev *pcie_bus_dev = bus->bus_priv.pcie; - struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; - struct brcmf_commonring **flowrings; - int ret; - u32 i; - - brcmf_pcie_attach(devinfo); - - ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len); - if (ret) - goto fail; - - devinfo->state = BRCMFMAC_PCIE_STATE_UP; - - ret = brcmf_pcie_init_ringbuffers(devinfo); - if (ret) - goto fail; - - ret = brcmf_pcie_init_scratchbuffers(devinfo); - if (ret) - goto fail; - - brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - ret = brcmf_pcie_request_irq(devinfo); - if (ret) - goto fail; - - /* hook the commonrings in the bus structure. */ - for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++) - bus->msgbuf->commonrings[i] = - &devinfo->shared.commonrings[i]->commonring; - - flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*flowrings), - GFP_KERNEL); - if (!flowrings) - goto fail; - - for (i = 0; i < devinfo->shared.nrof_flowrings; i++) - flowrings[i] = &devinfo->shared.flowrings[i].commonring; - bus->msgbuf->flowrings = flowrings; - - bus->msgbuf->rx_dataoffset = devinfo->shared.rx_dataoffset; - bus->msgbuf->max_rxbufpost = devinfo->shared.max_rxbufpost; - bus->msgbuf->nrof_flowrings = devinfo->shared.nrof_flowrings; - - init_waitqueue_head(&devinfo->mbdata_resp_wait); - - brcmf_pcie_intr_enable(devinfo); - if (brcmf_pcie_attach_bus(bus->dev) == 0) - return; - - brcmf_pcie_bus_console_read(devinfo); - -fail: - device_release_driver(dev); -} - -static int -brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - int ret; - struct brcmf_pciedev_info *devinfo; - struct brcmf_pciedev *pcie_bus_dev; - struct brcmf_bus *bus; - u16 domain_nr; - u16 bus_nr; - - domain_nr = pci_domain_nr(pdev->bus) + 1; - bus_nr = pdev->bus->number; - brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device, - domain_nr, bus_nr); - - ret = -ENOMEM; - devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); - if (devinfo == NULL) - return ret; - - devinfo->pdev = pdev; - pcie_bus_dev = NULL; - devinfo->ci = brcmf_chip_attach(devinfo, &brcmf_pcie_buscore_ops); - if (IS_ERR(devinfo->ci)) { - ret = PTR_ERR(devinfo->ci); - devinfo->ci = NULL; - goto fail; - } - - pcie_bus_dev = kzalloc(sizeof(*pcie_bus_dev), GFP_KERNEL); - if (pcie_bus_dev == NULL) { - ret = -ENOMEM; - goto fail; - } - - bus = kzalloc(sizeof(*bus), GFP_KERNEL); - if (!bus) { - ret = -ENOMEM; - goto fail; - } - bus->msgbuf = kzalloc(sizeof(*bus->msgbuf), GFP_KERNEL); - if (!bus->msgbuf) { - ret = -ENOMEM; - kfree(bus); - goto fail; - } - - /* hook it all together. */ - pcie_bus_dev->devinfo = devinfo; - pcie_bus_dev->bus = bus; - bus->dev = &pdev->dev; - bus->bus_priv.pcie = pcie_bus_dev; - bus->ops = &brcmf_pcie_bus_ops; - bus->proto_type = BRCMF_PROTO_MSGBUF; - bus->chip = devinfo->coreid; - bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot); - dev_set_drvdata(&pdev->dev, bus); - - ret = brcmf_pcie_get_fwnames(devinfo); - if (ret) - goto fail_bus; - - ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM | - BRCMF_FW_REQ_NV_OPTIONAL, - devinfo->fw_name, devinfo->nvram_name, - brcmf_pcie_setup, domain_nr, bus_nr); - if (ret == 0) - return 0; -fail_bus: - kfree(bus->msgbuf); - kfree(bus); -fail: - brcmf_err("failed %x:%x\n", pdev->vendor, pdev->device); - brcmf_pcie_release_resource(devinfo); - if (devinfo->ci) - brcmf_chip_detach(devinfo->ci); - kfree(pcie_bus_dev); - kfree(devinfo); - return ret; -} - - -static void -brcmf_pcie_remove(struct pci_dev *pdev) -{ - struct brcmf_pciedev_info *devinfo; - struct brcmf_bus *bus; - - brcmf_dbg(PCIE, "Enter\n"); - - bus = dev_get_drvdata(&pdev->dev); - if (bus == NULL) - return; - - devinfo = bus->bus_priv.pcie->devinfo; - - devinfo->state = BRCMFMAC_PCIE_STATE_DOWN; - if (devinfo->ci) - brcmf_pcie_intr_disable(devinfo); - - brcmf_detach(&pdev->dev); - - kfree(bus->bus_priv.pcie); - kfree(bus->msgbuf->flowrings); - kfree(bus->msgbuf); - kfree(bus); - - brcmf_pcie_release_irq(devinfo); - brcmf_pcie_release_scratchbuffers(devinfo); - brcmf_pcie_release_ringbuffers(devinfo); - brcmf_pcie_reset_device(devinfo); - brcmf_pcie_release_resource(devinfo); - - if (devinfo->ci) - brcmf_chip_detach(devinfo->ci); - - kfree(devinfo); - dev_set_drvdata(&pdev->dev, NULL); -} - - -#ifdef CONFIG_PM - - -static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct brcmf_pciedev_info *devinfo; - struct brcmf_bus *bus; - int err; - - brcmf_dbg(PCIE, "Enter, state=%d, pdev=%p\n", state.event, pdev); - - bus = dev_get_drvdata(&pdev->dev); - devinfo = bus->bus_priv.pcie->devinfo; - - brcmf_bus_change_state(bus, BRCMF_BUS_DOWN); - - devinfo->mbdata_completed = false; - brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D3_INFORM); - - wait_event_timeout(devinfo->mbdata_resp_wait, - devinfo->mbdata_completed, - msecs_to_jiffies(BRCMF_PCIE_MBDATA_TIMEOUT)); - if (!devinfo->mbdata_completed) { - brcmf_err("Timeout on response for entering D3 substate\n"); - return -EIO; - } - brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM_IN_USE); - - err = pci_save_state(pdev); - if (err) - brcmf_err("pci_save_state failed, err=%d\n", err); - if ((err) || (!devinfo->wowl_enabled)) { - brcmf_chip_detach(devinfo->ci); - devinfo->ci = NULL; - brcmf_pcie_remove(pdev); - return 0; - } - - return pci_prepare_to_sleep(pdev); -} - -static int brcmf_pcie_resume(struct pci_dev *pdev) -{ - struct brcmf_pciedev_info *devinfo; - struct brcmf_bus *bus; - int err; - - bus = dev_get_drvdata(&pdev->dev); - brcmf_dbg(PCIE, "Enter, pdev=%p, bus=%p\n", pdev, bus); - - err = pci_set_power_state(pdev, PCI_D0); - if (err) { - brcmf_err("pci_set_power_state failed, err=%d\n", err); - goto cleanup; - } - pci_restore_state(pdev); - pci_enable_wake(pdev, PCI_D3hot, false); - pci_enable_wake(pdev, PCI_D3cold, false); - - /* Check if device is still up and running, if so we are ready */ - if (bus) { - devinfo = bus->bus_priv.pcie->devinfo; - if (brcmf_pcie_read_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_INTMASK) != 0) { - if (brcmf_pcie_send_mb_data(devinfo, - BRCMF_H2D_HOST_D0_INFORM)) - goto cleanup; - brcmf_dbg(PCIE, "Hot resume, continue....\n"); - brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - brcmf_bus_change_state(bus, BRCMF_BUS_UP); - brcmf_pcie_intr_enable(devinfo); - return 0; - } - } - -cleanup: - if (bus) { - devinfo = bus->bus_priv.pcie->devinfo; - brcmf_chip_detach(devinfo->ci); - devinfo->ci = NULL; - brcmf_pcie_remove(pdev); - } - err = brcmf_pcie_probe(pdev, NULL); - if (err) - brcmf_err("probe after resume failed, err=%d\n", err); - - return err; -} - - -#endif /* CONFIG_PM */ - - -#define BRCMF_PCIE_DEVICE(dev_id) { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\ - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } - -static struct pci_device_id brcmf_pcie_devid_table[] = { - BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID), - BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID), - { /* end: all zeroes */ } -}; - - -MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table); - - -static struct pci_driver brcmf_pciedrvr = { - .node = {}, - .name = KBUILD_MODNAME, - .id_table = brcmf_pcie_devid_table, - .probe = brcmf_pcie_probe, - .remove = brcmf_pcie_remove, -#ifdef CONFIG_PM - .suspend = brcmf_pcie_suspend, - .resume = brcmf_pcie_resume -#endif /* CONFIG_PM */ -}; - - -void brcmf_pcie_register(void) -{ - int err; - - brcmf_dbg(PCIE, "Enter\n"); - err = pci_register_driver(&brcmf_pciedrvr); - if (err) - brcmf_err("PCIE driver registration failed, err=%d\n", err); -} - - -void brcmf_pcie_exit(void) -{ - brcmf_dbg(PCIE, "Enter\n"); - pci_unregister_driver(&brcmf_pciedrvr); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/brcm80211/brcmfmac/pcie.h deleted file mode 100644 index 6edaaf8ef5ce..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (c) 2014 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_PCIE_H -#define BRCMFMAC_PCIE_H - - -struct brcmf_pciedev { - struct brcmf_bus *bus; - struct brcmf_pciedev_info *devinfo; -}; - - -void brcmf_pcie_exit(void); -void brcmf_pcie_register(void); - - -#endif /* BRCMFMAC_PCIE_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c deleted file mode 100644 index 26b68c367f57..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 -#include "core.h" -#include "bus.h" -#include "debug.h" -#include "proto.h" -#include "bcdc.h" -#include "msgbuf.h" - - -int brcmf_proto_attach(struct brcmf_pub *drvr) -{ - struct brcmf_proto *proto; - - brcmf_dbg(TRACE, "Enter\n"); - - proto = kzalloc(sizeof(*proto), GFP_ATOMIC); - if (!proto) - goto fail; - - drvr->proto = proto; - - if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC) { - if (brcmf_proto_bcdc_attach(drvr)) - goto fail; - } else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF) { - if (brcmf_proto_msgbuf_attach(drvr)) - goto fail; - } else { - brcmf_err("Unsupported proto type %d\n", - drvr->bus_if->proto_type); - goto fail; - } - if ((proto->txdata == NULL) || (proto->hdrpull == NULL) || - (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL) || - (proto->configure_addr_mode == NULL) || - (proto->delete_peer == NULL) || (proto->add_tdls_peer == NULL)) { - brcmf_err("Not all proto handlers have been installed\n"); - goto fail; - } - return 0; - -fail: - kfree(proto); - drvr->proto = NULL; - return -ENOMEM; -} - -void brcmf_proto_detach(struct brcmf_pub *drvr) -{ - brcmf_dbg(TRACE, "Enter\n"); - - if (drvr->proto) { - if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC) - brcmf_proto_bcdc_detach(drvr); - else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF) - brcmf_proto_msgbuf_detach(drvr); - kfree(drvr->proto); - drvr->proto = NULL; - } -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/brcm80211/brcmfmac/proto.h deleted file mode 100644 index d55119d36755..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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_PROTO_H -#define BRCMFMAC_PROTO_H - - -enum proto_addr_mode { - ADDR_INDIRECT = 0, - ADDR_DIRECT -}; - - -struct brcmf_proto { - int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, - struct sk_buff *skb, struct brcmf_if **ifp); - int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, - void *buf, uint len); - int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, - uint len); - int (*txdata)(struct brcmf_pub *drvr, int ifidx, u8 offset, - struct sk_buff *skb); - void (*configure_addr_mode)(struct brcmf_pub *drvr, int ifidx, - enum proto_addr_mode addr_mode); - void (*delete_peer)(struct brcmf_pub *drvr, int ifidx, - u8 peer[ETH_ALEN]); - void (*add_tdls_peer)(struct brcmf_pub *drvr, int ifidx, - u8 peer[ETH_ALEN]); - void *pd; -}; - - -int brcmf_proto_attach(struct brcmf_pub *drvr); -void brcmf_proto_detach(struct brcmf_pub *drvr); - -static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, - struct sk_buff *skb, - struct brcmf_if **ifp) -{ - struct brcmf_if *tmp = NULL; - - /* assure protocol is always called with - * non-null initialized pointer. - */ - if (ifp) - *ifp = NULL; - else - ifp = &tmp; - return drvr->proto->hdrpull(drvr, do_fws, skb, ifp); -} -static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx, - uint cmd, void *buf, uint len) -{ - return drvr->proto->query_dcmd(drvr, ifidx, cmd, buf, len); -} -static inline int brcmf_proto_set_dcmd(struct brcmf_pub *drvr, int ifidx, - uint cmd, void *buf, uint len) -{ - return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len); -} -static inline int brcmf_proto_txdata(struct brcmf_pub *drvr, int ifidx, - u8 offset, struct sk_buff *skb) -{ - return drvr->proto->txdata(drvr, ifidx, offset, skb); -} -static inline void -brcmf_proto_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, - enum proto_addr_mode addr_mode) -{ - drvr->proto->configure_addr_mode(drvr, ifidx, addr_mode); -} -static inline void -brcmf_proto_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) -{ - drvr->proto->delete_peer(drvr, ifidx, peer); -} -static inline void -brcmf_proto_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) -{ - drvr->proto->add_tdls_peer(drvr, ifidx, peer); -} - - -#endif /* BRCMFMAC_PROTO_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c deleted file mode 100644 index 7e74ac3ad815..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ /dev/null @@ -1,4376 +0,0 @@ -/* - * Copyright (c) 2010 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sdio.h" -#include "chip.h" -#include "firmware.h" - -#define DCMD_RESP_TIMEOUT 2000 /* In milli second */ -#define CTL_DONE_TIMEOUT 2000 /* In milli second */ - -#ifdef DEBUG - -#define BRCMF_TRAP_INFO_SIZE 80 - -#define CBUF_LEN (128) - -/* Device console log buffer state */ -#define CONSOLE_BUFFER_MAX 2024 - -struct rte_log_le { - __le32 buf; /* Can't be pointer on (64-bit) hosts */ - __le32 buf_size; - __le32 idx; - char *_buf_compat; /* Redundant pointer for backward compat. */ -}; - -struct rte_console { - /* Virtual UART - * When there is no UART (e.g. Quickturn), - * the host should write a complete - * input line directly into cbuf and then write - * the length into vcons_in. - * This may also be used when there is a real UART - * (at risk of conflicting with - * the real UART). vcons_out is currently unused. - */ - uint vcons_in; - uint vcons_out; - - /* Output (logging) buffer - * Console output is written to a ring buffer log_buf at index log_idx. - * The host may read the output when it sees log_idx advance. - * Output will be lost if the output wraps around faster than the host - * polls. - */ - struct rte_log_le log_le; - - /* Console input line buffer - * Characters are read one at a time into cbuf - * until is received, then - * the buffer is processed as a command line. - * Also used for virtual UART. - */ - uint cbuf_idx; - char cbuf[CBUF_LEN]; -}; - -#endif /* DEBUG */ -#include - -#include "bus.h" -#include "debug.h" -#include "tracepoint.h" - -#define TXQLEN 2048 /* bulk tx queue length */ -#define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */ -#define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */ -#define PRIOMASK 7 - -#define TXRETRIES 2 /* # of retries for tx frames */ - -#define BRCMF_RXBOUND 50 /* Default for max rx frames in - one scheduling */ - -#define BRCMF_TXBOUND 20 /* Default for max tx frames in - one scheduling */ - -#define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */ - -#define MEMBLOCK 2048 /* Block size used for downloading - of dongle image */ -#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold - biggest possible glom */ - -#define BRCMF_FIRSTREAD (1 << 6) - -#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */ - -/* SBSDIO_DEVICE_CTL */ - -/* 1: device will assert busy signal when receiving CMD53 */ -#define SBSDIO_DEVCTL_SETBUSY 0x01 -/* 1: assertion of sdio interrupt is synchronous to the sdio clock */ -#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02 -/* 1: mask all interrupts to host except the chipActive (rev 8) */ -#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04 -/* 1: isolate internal sdio signals, put external pads in tri-state; requires - * sdio bus power cycle to clear (rev 9) */ -#define SBSDIO_DEVCTL_PADS_ISO 0x08 -/* Force SD->SB reset mapping (rev 11) */ -#define SBSDIO_DEVCTL_SB_RST_CTL 0x30 -/* Determined by CoreControl bit */ -#define SBSDIO_DEVCTL_RST_CORECTL 0x00 -/* Force backplane reset */ -#define SBSDIO_DEVCTL_RST_BPRESET 0x10 -/* Force no backplane reset */ -#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20 - -/* direct(mapped) cis space */ - -/* MAPPED common CIS address */ -#define SBSDIO_CIS_BASE_COMMON 0x1000 -/* maximum bytes in one CIS */ -#define SBSDIO_CIS_SIZE_LIMIT 0x200 -/* cis offset addr is < 17 bits */ -#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF - -/* manfid tuple length, include tuple, link bytes */ -#define SBSDIO_CIS_MANFID_TUPLE_LEN 6 - -#define CORE_BUS_REG(base, field) \ - (base + offsetof(struct sdpcmd_regs, field)) - -/* SDIO function 1 register CHIPCLKCSR */ -/* Force ALP request to backplane */ -#define SBSDIO_FORCE_ALP 0x01 -/* Force HT request to backplane */ -#define SBSDIO_FORCE_HT 0x02 -/* Force ILP request to backplane */ -#define SBSDIO_FORCE_ILP 0x04 -/* Make ALP ready (power up xtal) */ -#define SBSDIO_ALP_AVAIL_REQ 0x08 -/* Make HT ready (power up PLL) */ -#define SBSDIO_HT_AVAIL_REQ 0x10 -/* Squelch clock requests from HW */ -#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 -/* Status: ALP is ready */ -#define SBSDIO_ALP_AVAIL 0x40 -/* Status: HT is ready */ -#define SBSDIO_HT_AVAIL 0x80 -#define SBSDIO_CSR_MASK 0x1F -#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) -#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) -#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) -#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) -#define SBSDIO_CLKAV(regval, alponly) \ - (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) - -/* intstatus */ -#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ -#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ -#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */ -#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */ -#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */ -#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */ -#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */ -#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */ -#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */ -#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */ -#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */ -#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */ -#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */ -#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */ -#define I_PC (1 << 10) /* descriptor error */ -#define I_PD (1 << 11) /* data error */ -#define I_DE (1 << 12) /* Descriptor protocol Error */ -#define I_RU (1 << 13) /* Receive descriptor Underflow */ -#define I_RO (1 << 14) /* Receive fifo Overflow */ -#define I_XU (1 << 15) /* Transmit fifo Underflow */ -#define I_RI (1 << 16) /* Receive Interrupt */ -#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */ -#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */ -#define I_XI (1 << 24) /* Transmit Interrupt */ -#define I_RF_TERM (1 << 25) /* Read Frame Terminate */ -#define I_WF_TERM (1 << 26) /* Write Frame Terminate */ -#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */ -#define I_SBINT (1 << 28) /* sbintstatus Interrupt */ -#define I_CHIPACTIVE (1 << 29) /* chip from doze to active state */ -#define I_SRESET (1 << 30) /* CCCR RES interrupt */ -#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */ -#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU) -#define I_DMA (I_RI | I_XI | I_ERRORS) - -/* corecontrol */ -#define CC_CISRDY (1 << 0) /* CIS Ready */ -#define CC_BPRESEN (1 << 1) /* CCCR RES signal */ -#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */ -#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation */ -#define CC_XMTDATAAVAIL_MODE (1 << 4) -#define CC_XMTDATAAVAIL_CTRL (1 << 5) - -/* SDA_FRAMECTRL */ -#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */ -#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */ -#define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */ -#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */ - -/* - * Software allocation of To SB Mailbox resources - */ - -/* tosbmailbox bits corresponding to intstatus bits */ -#define SMB_NAK (1 << 0) /* Frame NAK */ -#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */ -#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */ -#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */ - -/* tosbmailboxdata */ -#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */ - -/* - * Software allocation of To Host Mailbox resources - */ - -/* intstatus bits */ -#define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */ -#define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */ -#define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */ -#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */ - -/* tohostmailboxdata */ -#define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */ -#define HMB_DATA_DEVREADY 2 /* talk to host after enable */ -#define HMB_DATA_FC 4 /* per prio flowcontrol update flag */ -#define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */ - -#define HMB_DATA_FCDATA_MASK 0xff000000 -#define HMB_DATA_FCDATA_SHIFT 24 - -#define HMB_DATA_VERSION_MASK 0x00ff0000 -#define HMB_DATA_VERSION_SHIFT 16 - -/* - * Software-defined protocol header - */ - -/* Current protocol version */ -#define SDPCM_PROT_VERSION 4 - -/* - * Shared structure between dongle and the host. - * The structure contains pointers to trap or assert information. - */ -#define SDPCM_SHARED_VERSION 0x0003 -#define SDPCM_SHARED_VERSION_MASK 0x00FF -#define SDPCM_SHARED_ASSERT_BUILT 0x0100 -#define SDPCM_SHARED_ASSERT 0x0200 -#define SDPCM_SHARED_TRAP 0x0400 - -/* Space for header read, limit for data packets */ -#define MAX_HDR_READ (1 << 6) -#define MAX_RX_DATASZ 2048 - -/* Bump up limit on waiting for HT to account for first startup; - * if the image is doing a CRC calculation before programming the PMU - * for HT availability, it could take a couple hundred ms more, so - * max out at a 1 second (1000000us). - */ -#undef PMU_MAX_TRANSITION_DLY -#define PMU_MAX_TRANSITION_DLY 1000000 - -/* Value for ChipClockCSR during initial setup */ -#define BRCMF_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \ - SBSDIO_ALP_AVAIL_REQ) - -/* Flags for SDH calls */ -#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) - -#define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change - * when idle - */ -#define BRCMF_IDLE_INTERVAL 1 - -#define KSO_WAIT_US 50 -#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US) - -/* - * Conversion of 802.1D priority to precedence level - */ -static uint prio2prec(u32 prio) -{ - return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ? - (prio^2) : prio; -} - -#ifdef DEBUG -/* Device console log buffer state */ -struct brcmf_console { - uint count; /* Poll interval msec counter */ - uint log_addr; /* Log struct address (fixed) */ - struct rte_log_le log_le; /* Log struct (host copy) */ - uint bufsize; /* Size of log buffer */ - u8 *buf; /* Log buffer (host copy) */ - uint last; /* Last buffer read index */ -}; - -struct brcmf_trap_info { - __le32 type; - __le32 epc; - __le32 cpsr; - __le32 spsr; - __le32 r0; /* a1 */ - __le32 r1; /* a2 */ - __le32 r2; /* a3 */ - __le32 r3; /* a4 */ - __le32 r4; /* v1 */ - __le32 r5; /* v2 */ - __le32 r6; /* v3 */ - __le32 r7; /* v4 */ - __le32 r8; /* v5 */ - __le32 r9; /* sb/v6 */ - __le32 r10; /* sl/v7 */ - __le32 r11; /* fp/v8 */ - __le32 r12; /* ip */ - __le32 r13; /* sp */ - __le32 r14; /* lr */ - __le32 pc; /* r15 */ -}; -#endif /* DEBUG */ - -struct sdpcm_shared { - u32 flags; - u32 trap_addr; - u32 assert_exp_addr; - u32 assert_file_addr; - u32 assert_line; - u32 console_addr; /* Address of struct rte_console */ - u32 msgtrace_addr; - u8 tag[32]; - u32 brpt_addr; -}; - -struct sdpcm_shared_le { - __le32 flags; - __le32 trap_addr; - __le32 assert_exp_addr; - __le32 assert_file_addr; - __le32 assert_line; - __le32 console_addr; /* Address of struct rte_console */ - __le32 msgtrace_addr; - u8 tag[32]; - __le32 brpt_addr; -}; - -/* dongle SDIO bus specific header info */ -struct brcmf_sdio_hdrinfo { - u8 seq_num; - u8 channel; - u16 len; - u16 len_left; - u16 len_nxtfrm; - u8 dat_offset; - bool lastfrm; - u16 tail_pad; -}; - -/* - * hold counter variables - */ -struct brcmf_sdio_count { - uint intrcount; /* Count of device interrupt callbacks */ - uint lastintrs; /* Count as of last watchdog timer */ - uint pollcnt; /* Count of active polls */ - uint regfails; /* Count of R_REG failures */ - uint tx_sderrs; /* Count of tx attempts with sd errors */ - uint fcqueued; /* Tx packets that got queued */ - uint rxrtx; /* Count of rtx requests (NAK to dongle) */ - uint rx_toolong; /* Receive frames too long to receive */ - uint rxc_errors; /* SDIO errors when reading control frames */ - uint rx_hdrfail; /* SDIO errors on header reads */ - uint rx_badhdr; /* Bad received headers (roosync?) */ - uint rx_badseq; /* Mismatched rx sequence number */ - uint fc_rcvd; /* Number of flow-control events received */ - uint fc_xoff; /* Number which turned on flow-control */ - uint fc_xon; /* Number which turned off flow-control */ - uint rxglomfail; /* Failed deglom attempts */ - uint rxglomframes; /* Number of glom frames (superframes) */ - uint rxglompkts; /* Number of packets from glom frames */ - uint f2rxhdrs; /* Number of header reads */ - uint f2rxdata; /* Number of frame data reads */ - uint f2txdata; /* Number of f2 frame writes */ - uint f1regdata; /* Number of f1 register accesses */ - uint tickcnt; /* Number of watchdog been schedule */ - ulong tx_ctlerrs; /* Err of sending ctrl frames */ - ulong tx_ctlpkts; /* Ctrl frames sent to dongle */ - ulong rx_ctlerrs; /* Err of processing rx ctrl frames */ - ulong rx_ctlpkts; /* Ctrl frames processed from dongle */ - ulong rx_readahead_cnt; /* packets where header read-ahead was used */ -}; - -/* misc chip info needed by some of the routines */ -/* Private data for SDIO bus interaction */ -struct brcmf_sdio { - struct brcmf_sdio_dev *sdiodev; /* sdio device handler */ - struct brcmf_chip *ci; /* Chip info struct */ - - u32 hostintmask; /* Copy of Host Interrupt Mask */ - atomic_t intstatus; /* Intstatus bits (events) pending */ - atomic_t fcstate; /* State of dongle flow-control */ - - uint blocksize; /* Block size of SDIO transfers */ - uint roundup; /* Max roundup limit */ - - struct pktq txq; /* Queue length used for flow-control */ - u8 flowcontrol; /* per prio flow control bitmask */ - u8 tx_seq; /* Transmit sequence number (next) */ - u8 tx_max; /* Maximum transmit sequence allowed */ - - u8 *hdrbuf; /* buffer for handling rx frame */ - u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */ - u8 rx_seq; /* Receive sequence number (expected) */ - struct brcmf_sdio_hdrinfo cur_read; - /* info of current read frame */ - bool rxskip; /* Skip receive (awaiting NAK ACK) */ - bool rxpending; /* Data frame pending in dongle */ - - uint rxbound; /* Rx frames to read before resched */ - uint txbound; /* Tx frames to send before resched */ - uint txminmax; - - struct sk_buff *glomd; /* Packet containing glomming descriptor */ - struct sk_buff_head glom; /* Packet list for glommed superframe */ - uint glomerr; /* Glom packet read errors */ - - u8 *rxbuf; /* Buffer for receiving control packets */ - uint rxblen; /* Allocated length of rxbuf */ - u8 *rxctl; /* Aligned pointer into rxbuf */ - u8 *rxctl_orig; /* pointer for freeing rxctl */ - uint rxlen; /* Length of valid data in buffer */ - spinlock_t rxctl_lock; /* protection lock for ctrl frame resources */ - - u8 sdpcm_ver; /* Bus protocol reported by dongle */ - - bool intr; /* Use interrupts */ - bool poll; /* Use polling */ - atomic_t ipend; /* Device interrupt is pending */ - uint spurious; /* Count of spurious interrupts */ - uint pollrate; /* Ticks between device polls */ - uint polltick; /* Tick counter */ - -#ifdef DEBUG - uint console_interval; - struct brcmf_console console; /* Console output polling support */ - uint console_addr; /* Console address from shared struct */ -#endif /* DEBUG */ - - uint clkstate; /* State of sd and backplane clock(s) */ - s32 idletime; /* Control for activity timeout */ - s32 idlecount; /* Activity timeout counter */ - s32 idleclock; /* How to set bus driver when idle */ - bool rxflow_mode; /* Rx flow control mode */ - bool rxflow; /* Is rx flow control on */ - bool alp_only; /* Don't use HT clock (ALP only) */ - - u8 *ctrl_frame_buf; - u16 ctrl_frame_len; - bool ctrl_frame_stat; - int ctrl_frame_err; - - spinlock_t txq_lock; /* protect bus->txq */ - wait_queue_head_t ctrl_wait; - wait_queue_head_t dcmd_resp_wait; - - struct timer_list timer; - struct completion watchdog_wait; - struct task_struct *watchdog_tsk; - bool wd_timer_valid; - uint save_ms; - - struct workqueue_struct *brcmf_wq; - struct work_struct datawork; - bool dpc_triggered; - bool dpc_running; - - bool txoff; /* Transmit flow-controlled */ - struct brcmf_sdio_count sdcnt; - bool sr_enabled; /* SaveRestore enabled */ - bool sleeping; - - u8 tx_hdrlen; /* sdio bus header length for tx packet */ - bool txglom; /* host tx glomming enable flag */ - u16 head_align; /* buffer pointer alignment */ - u16 sgentry_align; /* scatter-gather buffer alignment */ -}; - -/* clkstate */ -#define CLK_NONE 0 -#define CLK_SDONLY 1 -#define CLK_PENDING 2 -#define CLK_AVAIL 3 - -#ifdef DEBUG -static int qcount[NUMPRIO]; -#endif /* DEBUG */ - -#define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */ - -#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL) - -/* Retry count for register access failures */ -static const uint retry_limit = 2; - -/* Limit on rounding up frames */ -static const uint max_roundup = 512; - -#define ALIGNMENT 4 - -enum brcmf_sdio_frmtype { - BRCMF_SDIO_FT_NORMAL, - BRCMF_SDIO_FT_SUPER, - BRCMF_SDIO_FT_SUB, -}; - -#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) - -/* SDIO Pad drive strength to select value mappings */ -struct sdiod_drive_str { - u8 strength; /* Pad Drive Strength in mA */ - u8 sel; /* Chip-specific select value */ -}; - -/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ -static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { - {32, 0x6}, - {26, 0x7}, - {22, 0x4}, - {16, 0x5}, - {12, 0x2}, - {8, 0x3}, - {4, 0x0}, - {0, 0x1} -}; - -/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ -static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = { - {6, 0x7}, - {5, 0x6}, - {4, 0x5}, - {3, 0x4}, - {2, 0x2}, - {1, 0x1}, - {0, 0x0} -}; - -/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ -static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = { - {3, 0x3}, - {2, 0x2}, - {1, 0x1}, - {0, 0x0} }; - -/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */ -static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { - {16, 0x7}, - {12, 0x5}, - {8, 0x3}, - {4, 0x1} -}; - -#define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin" -#define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt" -#define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin" -#define BCM43241B0_NVRAM_NAME "brcm/brcmfmac43241b0-sdio.txt" -#define BCM43241B4_FIRMWARE_NAME "brcm/brcmfmac43241b4-sdio.bin" -#define BCM43241B4_NVRAM_NAME "brcm/brcmfmac43241b4-sdio.txt" -#define BCM43241B5_FIRMWARE_NAME "brcm/brcmfmac43241b5-sdio.bin" -#define BCM43241B5_NVRAM_NAME "brcm/brcmfmac43241b5-sdio.txt" -#define BCM4329_FIRMWARE_NAME "brcm/brcmfmac4329-sdio.bin" -#define BCM4329_NVRAM_NAME "brcm/brcmfmac4329-sdio.txt" -#define BCM4330_FIRMWARE_NAME "brcm/brcmfmac4330-sdio.bin" -#define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt" -#define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin" -#define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt" -#define BCM43340_FIRMWARE_NAME "brcm/brcmfmac43340-sdio.bin" -#define BCM43340_NVRAM_NAME "brcm/brcmfmac43340-sdio.txt" -#define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin" -#define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt" -#define BCM43362_FIRMWARE_NAME "brcm/brcmfmac43362-sdio.bin" -#define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" -#define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" -#define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" -#define BCM43430_FIRMWARE_NAME "brcm/brcmfmac43430-sdio.bin" -#define BCM43430_NVRAM_NAME "brcm/brcmfmac43430-sdio.txt" -#define BCM43455_FIRMWARE_NAME "brcm/brcmfmac43455-sdio.bin" -#define BCM43455_NVRAM_NAME "brcm/brcmfmac43455-sdio.txt" -#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" -#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" - -MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43143_NVRAM_NAME); -MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); -MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); -MODULE_FIRMWARE(BCM43241B5_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43241B5_NVRAM_NAME); -MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4329_NVRAM_NAME); -MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4330_NVRAM_NAME); -MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4334_NVRAM_NAME); -MODULE_FIRMWARE(BCM43340_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43340_NVRAM_NAME); -MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4335_NVRAM_NAME); -MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43362_NVRAM_NAME); -MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4339_NVRAM_NAME); -MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43430_NVRAM_NAME); -MODULE_FIRMWARE(BCM43455_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43455_NVRAM_NAME); -MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4354_NVRAM_NAME); - -struct brcmf_firmware_names { - u32 chipid; - u32 revmsk; - const char *bin; - const char *nv; -}; - -enum brcmf_firmware_type { - BRCMF_FIRMWARE_BIN, - BRCMF_FIRMWARE_NVRAM -}; - -#define BRCMF_FIRMWARE_NVRAM(name) \ - name ## _FIRMWARE_NAME, name ## _NVRAM_NAME - -static const struct brcmf_firmware_names brcmf_fwname_data[] = { - { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, - { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, - { BRCM_CC_43241_CHIP_ID, 0x00000020, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, - { BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43241B5) }, - { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, - { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, - { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, - { BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43340) }, - { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, - { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, - { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, - { BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) }, - { BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43455) }, - { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } -}; - -static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, - struct brcmf_sdio_dev *sdiodev) -{ - int i; - char end; - - for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { - if (brcmf_fwname_data[i].chipid == ci->chip && - brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) - break; - } - - if (i == ARRAY_SIZE(brcmf_fwname_data)) { - brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev); - return -ENODEV; - } - - /* check if firmware path is provided by module parameter */ - if (brcmf_firmware_path[0] != '\0') { - strlcpy(sdiodev->fw_name, brcmf_firmware_path, - sizeof(sdiodev->fw_name)); - strlcpy(sdiodev->nvram_name, brcmf_firmware_path, - sizeof(sdiodev->nvram_name)); - - end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; - if (end != '/') { - strlcat(sdiodev->fw_name, "/", - sizeof(sdiodev->fw_name)); - strlcat(sdiodev->nvram_name, "/", - sizeof(sdiodev->nvram_name)); - } - } - strlcat(sdiodev->fw_name, brcmf_fwname_data[i].bin, - sizeof(sdiodev->fw_name)); - strlcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, - sizeof(sdiodev->nvram_name)); - - return 0; -} - -static void pkt_align(struct sk_buff *p, int len, int align) -{ - uint datalign; - datalign = (unsigned long)(p->data); - datalign = roundup(datalign, (align)) - datalign; - if (datalign) - skb_pull(p, datalign); - __skb_trim(p, len); -} - -/* To check if there's window offered */ -static bool data_ok(struct brcmf_sdio *bus) -{ - return (u8)(bus->tx_max - bus->tx_seq) != 0 && - ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0; -} - -/* - * Reads a register in the SDIO hardware block. This block occupies a series of - * adresses on the 32 bit backplane bus. - */ -static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) -{ - struct brcmf_core *core; - int ret; - - core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); - *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret); - - return ret; -} - -static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) -{ - struct brcmf_core *core; - int ret; - - core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); - brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret); - - return ret; -} - -static int -brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) -{ - u8 wr_val = 0, rd_val, cmp_val, bmask; - int err = 0; - int try_cnt = 0; - - brcmf_dbg(TRACE, "Enter: on=%d\n", on); - - wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); - /* 1st KSO write goes to AOS wake up core if device is asleep */ - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - wr_val, &err); - - if (on) { - /* device WAKEUP through KSO: - * write bit 0 & read back until - * both bits 0 (kso bit) & 1 (dev on status) are set - */ - cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | - SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK; - bmask = cmp_val; - usleep_range(2000, 3000); - } else { - /* Put device to sleep, turn off KSO */ - cmp_val = 0; - /* only check for bit0, bit1(dev on status) may not - * get cleared right away - */ - bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK; - } - - do { - /* reliable KSO bit set/clr: - * the sdiod sleep write access is synced to PMU 32khz clk - * just one write attempt may fail, - * read it back until it matches written value - */ - rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - &err); - if (((rd_val & bmask) == cmp_val) && !err) - break; - - udelay(KSO_WAIT_US); - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - wr_val, &err); - } while (try_cnt++ < MAX_KSO_ATTEMPTS); - - if (try_cnt > 2) - brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt, - rd_val, err); - - if (try_cnt > MAX_KSO_ATTEMPTS) - brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err); - - return err; -} - -#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) - -/* Turn backplane clock on or off */ -static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok) -{ - int err; - u8 clkctl, clkreq, devctl; - unsigned long timeout; - - brcmf_dbg(SDIO, "Enter\n"); - - clkctl = 0; - - if (bus->sr_enabled) { - bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY); - return 0; - } - - if (on) { - /* Request HT Avail */ - clkreq = - bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - clkreq, &err); - if (err) { - brcmf_err("HT Avail request error: %d\n", err); - return -EBADE; - } - - /* Check current status */ - clkctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - if (err) { - brcmf_err("HT Avail read error: %d\n", err); - return -EBADE; - } - - /* Go to pending and await interrupt if appropriate */ - if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) { - /* Allow only clock-available interrupt */ - devctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); - if (err) { - brcmf_err("Devctl error setting CA: %d\n", - err); - return -EBADE; - } - - devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); - brcmf_dbg(SDIO, "CLKCTL: set PENDING\n"); - bus->clkstate = CLK_PENDING; - - return 0; - } else if (bus->clkstate == CLK_PENDING) { - /* Cancel CA-only interrupt filter */ - devctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); - devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); - } - - /* Otherwise, wait here (polling) for HT Avail */ - timeout = jiffies + - msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000); - while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { - clkctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, - &err); - if (time_after(jiffies, timeout)) - break; - else - usleep_range(5000, 10000); - } - if (err) { - brcmf_err("HT Avail request error: %d\n", err); - return -EBADE; - } - if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { - brcmf_err("HT Avail timeout (%d): clkctl 0x%02x\n", - PMU_MAX_TRANSITION_DLY, clkctl); - return -EBADE; - } - - /* Mark clock available */ - bus->clkstate = CLK_AVAIL; - brcmf_dbg(SDIO, "CLKCTL: turned ON\n"); - -#if defined(DEBUG) - if (!bus->alp_only) { - if (SBSDIO_ALPONLY(clkctl)) - brcmf_err("HT Clock should be on\n"); - } -#endif /* defined (DEBUG) */ - - } else { - clkreq = 0; - - if (bus->clkstate == CLK_PENDING) { - /* Cancel CA-only interrupt filter */ - devctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); - devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); - } - - bus->clkstate = CLK_SDONLY; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - clkreq, &err); - brcmf_dbg(SDIO, "CLKCTL: turned OFF\n"); - if (err) { - brcmf_err("Failed access turning clock off: %d\n", - err); - return -EBADE; - } - } - return 0; -} - -/* Change idle/active SD state */ -static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on) -{ - brcmf_dbg(SDIO, "Enter\n"); - - if (on) - bus->clkstate = CLK_SDONLY; - else - bus->clkstate = CLK_NONE; - - return 0; -} - -/* Transition SD and backplane clock readiness */ -static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) -{ -#ifdef DEBUG - uint oldstate = bus->clkstate; -#endif /* DEBUG */ - - brcmf_dbg(SDIO, "Enter\n"); - - /* Early exit if we're already there */ - if (bus->clkstate == target) - return 0; - - switch (target) { - case CLK_AVAIL: - /* Make sure SD clock is available */ - if (bus->clkstate == CLK_NONE) - brcmf_sdio_sdclk(bus, true); - /* Now request HT Avail on the backplane */ - brcmf_sdio_htclk(bus, true, pendok); - break; - - case CLK_SDONLY: - /* Remove HT request, or bring up SD clock */ - if (bus->clkstate == CLK_NONE) - brcmf_sdio_sdclk(bus, true); - else if (bus->clkstate == CLK_AVAIL) - brcmf_sdio_htclk(bus, false, false); - else - brcmf_err("request for %d -> %d\n", - bus->clkstate, target); - break; - - case CLK_NONE: - /* Make sure to remove HT request */ - if (bus->clkstate == CLK_AVAIL) - brcmf_sdio_htclk(bus, false, false); - /* Now remove the SD clock */ - brcmf_sdio_sdclk(bus, false); - break; - } -#ifdef DEBUG - brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate); -#endif /* DEBUG */ - - return 0; -} - -static int -brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) -{ - int err = 0; - u8 clkcsr; - - brcmf_dbg(SDIO, "Enter: request %s currently %s\n", - (sleep ? "SLEEP" : "WAKE"), - (bus->sleeping ? "SLEEP" : "WAKE")); - - /* If SR is enabled control bus state with KSO */ - if (bus->sr_enabled) { - /* Done if we're already in the requested state */ - if (sleep == bus->sleeping) - goto end; - - /* Going to sleep */ - if (sleep) { - clkcsr = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, - &err); - if ((clkcsr & SBSDIO_CSR_MASK) == 0) { - brcmf_dbg(SDIO, "no clock, set ALP\n"); - brcmf_sdiod_regwb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, - SBSDIO_ALP_AVAIL_REQ, &err); - } - err = brcmf_sdio_kso_control(bus, false); - } else { - err = brcmf_sdio_kso_control(bus, true); - } - if (err) { - brcmf_err("error while changing bus sleep state %d\n", - err); - goto done; - } - } - -end: - /* control clocks */ - if (sleep) { - if (!bus->sr_enabled) - brcmf_sdio_clkctl(bus, CLK_NONE, pendok); - } else { - brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - } - bus->sleeping = sleep; - brcmf_dbg(SDIO, "new state %s\n", - (sleep ? "SLEEP" : "WAKE")); -done: - brcmf_dbg(SDIO, "Exit: err=%d\n", err); - return err; - -} - -#ifdef DEBUG -static inline bool brcmf_sdio_valid_shared_address(u32 addr) -{ - return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); -} - -static int brcmf_sdio_readshared(struct brcmf_sdio *bus, - struct sdpcm_shared *sh) -{ - u32 addr = 0; - int rv; - u32 shaddr = 0; - struct sdpcm_shared_le sh_le; - __le32 addr_le; - - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - - /* - * Read last word in socram to determine - * address of sdpcm_shared structure - */ - shaddr = bus->ci->rambase + bus->ci->ramsize - 4; - if (!bus->ci->rambase && brcmf_chip_sr_capable(bus->ci)) - shaddr -= bus->ci->srsize; - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, - (u8 *)&addr_le, 4); - if (rv < 0) - goto fail; - - /* - * Check if addr is valid. - * NVRAM length at the end of memory should have been overwritten. - */ - addr = le32_to_cpu(addr_le); - if (!brcmf_sdio_valid_shared_address(addr)) { - brcmf_err("invalid sdpcm_shared address 0x%08X\n", addr); - rv = -EINVAL; - goto fail; - } - - brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr); - - /* Read hndrte_shared structure */ - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, - sizeof(struct sdpcm_shared_le)); - if (rv < 0) - goto fail; - - sdio_release_host(bus->sdiodev->func[1]); - - /* Endianness */ - sh->flags = le32_to_cpu(sh_le.flags); - sh->trap_addr = le32_to_cpu(sh_le.trap_addr); - sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); - sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); - sh->assert_line = le32_to_cpu(sh_le.assert_line); - sh->console_addr = le32_to_cpu(sh_le.console_addr); - sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); - - if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { - brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", - SDPCM_SHARED_VERSION, - sh->flags & SDPCM_SHARED_VERSION_MASK); - return -EPROTO; - } - return 0; - -fail: - brcmf_err("unable to obtain sdpcm_shared info: rv=%d (addr=0x%x)\n", - rv, addr); - sdio_release_host(bus->sdiodev->func[1]); - return rv; -} - -static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) -{ - struct sdpcm_shared sh; - - if (brcmf_sdio_readshared(bus, &sh) == 0) - bus->console_addr = sh.console_addr; -} -#else -static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) -{ -} -#endif /* DEBUG */ - -static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) -{ - u32 intstatus = 0; - u32 hmb_data; - u8 fcbits; - int ret; - - brcmf_dbg(SDIO, "Enter\n"); - - /* Read mailbox data and ack that we did so */ - ret = r_sdreg32(bus, &hmb_data, - offsetof(struct sdpcmd_regs, tohostmailboxdata)); - - if (ret == 0) - w_sdreg32(bus, SMB_INT_ACK, - offsetof(struct sdpcmd_regs, tosbmailbox)); - bus->sdcnt.f1regdata += 2; - - /* Dongle recomposed rx frames, accept them again */ - if (hmb_data & HMB_DATA_NAKHANDLED) { - brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n", - bus->rx_seq); - if (!bus->rxskip) - brcmf_err("unexpected NAKHANDLED!\n"); - - bus->rxskip = false; - intstatus |= I_HMB_FRAME_IND; - } - - /* - * DEVREADY does not occur with gSPI. - */ - if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) { - bus->sdpcm_ver = - (hmb_data & HMB_DATA_VERSION_MASK) >> - HMB_DATA_VERSION_SHIFT; - if (bus->sdpcm_ver != SDPCM_PROT_VERSION) - brcmf_err("Version mismatch, dongle reports %d, " - "expecting %d\n", - bus->sdpcm_ver, SDPCM_PROT_VERSION); - else - brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n", - bus->sdpcm_ver); - - /* - * Retrieve console state address now that firmware should have - * updated it. - */ - brcmf_sdio_get_console_addr(bus); - } - - /* - * Flow Control has been moved into the RX headers and this out of band - * method isn't used any more. - * remaining backward compatible with older dongles. - */ - if (hmb_data & HMB_DATA_FC) { - fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >> - HMB_DATA_FCDATA_SHIFT; - - if (fcbits & ~bus->flowcontrol) - bus->sdcnt.fc_xoff++; - - if (bus->flowcontrol & ~fcbits) - bus->sdcnt.fc_xon++; - - bus->sdcnt.fc_rcvd++; - bus->flowcontrol = fcbits; - } - - /* Shouldn't be any others */ - if (hmb_data & ~(HMB_DATA_DEVREADY | - HMB_DATA_NAKHANDLED | - HMB_DATA_FC | - HMB_DATA_FWREADY | - HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) - brcmf_err("Unknown mailbox data content: 0x%02x\n", - hmb_data); - - return intstatus; -} - -static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) -{ - uint retries = 0; - u16 lastrbc; - u8 hi, lo; - int err; - - brcmf_err("%sterminate frame%s\n", - abort ? "abort command, " : "", - rtx ? ", send NAK" : ""); - - if (abort) - brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_RF_TERM, &err); - bus->sdcnt.f1regdata++; - - /* Wait until the packet has been flushed (device/FIFO stable) */ - for (lastrbc = retries = 0xffff; retries > 0; retries--) { - hi = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_RFRAMEBCHI, &err); - lo = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_RFRAMEBCLO, &err); - bus->sdcnt.f1regdata += 2; - - if ((hi == 0) && (lo == 0)) - break; - - if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) { - brcmf_err("count growing: last 0x%04x now 0x%04x\n", - lastrbc, (hi << 8) + lo); - } - lastrbc = (hi << 8) + lo; - } - - if (!retries) - brcmf_err("count never zeroed: last 0x%04x\n", lastrbc); - else - brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries); - - if (rtx) { - bus->sdcnt.rxrtx++; - err = w_sdreg32(bus, SMB_NAK, - offsetof(struct sdpcmd_regs, tosbmailbox)); - - bus->sdcnt.f1regdata++; - if (err == 0) - bus->rxskip = true; - } - - /* Clear partial in any case */ - bus->cur_read.len = 0; -} - -static void brcmf_sdio_txfail(struct brcmf_sdio *bus) -{ - struct brcmf_sdio_dev *sdiodev = bus->sdiodev; - u8 i, hi, lo; - - /* On failure, abort the command and terminate the frame */ - brcmf_err("sdio error, abort command and terminate frame\n"); - bus->sdcnt.tx_sderrs++; - - brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2); - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->sdcnt.f1regdata += 2; - if ((hi == 0) && (lo == 0)) - break; - } -} - -/* return total length of buffer chain */ -static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus) -{ - struct sk_buff *p; - uint total; - - total = 0; - skb_queue_walk(&bus->glom, p) - total += p->len; - return total; -} - -static void brcmf_sdio_free_glom(struct brcmf_sdio *bus) -{ - struct sk_buff *cur, *next; - - skb_queue_walk_safe(&bus->glom, cur, next) { - skb_unlink(cur, &bus->glom); - brcmu_pkt_buf_free_skb(cur); - } -} - -/** - * brcmfmac sdio bus specific header - * This is the lowest layer header wrapped on the packets transmitted between - * host and WiFi dongle which contains information needed for SDIO core and - * firmware - * - * It consists of 3 parts: hardware header, hardware extension header and - * software header - * hardware header (frame tag) - 4 bytes - * Byte 0~1: Frame length - * Byte 2~3: Checksum, bit-wise inverse of frame length - * hardware extension header - 8 bytes - * Tx glom mode only, N/A for Rx or normal Tx - * Byte 0~1: Packet length excluding hw frame tag - * Byte 2: Reserved - * Byte 3: Frame flags, bit 0: last frame indication - * Byte 4~5: Reserved - * Byte 6~7: Tail padding length - * software header - 8 bytes - * Byte 0: Rx/Tx sequence number - * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag - * Byte 2: Length of next data frame, reserved for Tx - * Byte 3: Data offset - * Byte 4: Flow control bits, reserved for Tx - * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet - * Byte 6~7: Reserved - */ -#define SDPCM_HWHDR_LEN 4 -#define SDPCM_HWEXT_LEN 8 -#define SDPCM_SWHDR_LEN 8 -#define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN) -/* software header */ -#define SDPCM_SEQ_MASK 0x000000ff -#define SDPCM_SEQ_WRAP 256 -#define SDPCM_CHANNEL_MASK 0x00000f00 -#define SDPCM_CHANNEL_SHIFT 8 -#define SDPCM_CONTROL_CHANNEL 0 /* Control */ -#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication */ -#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv */ -#define SDPCM_GLOM_CHANNEL 3 /* Coalesced packets */ -#define SDPCM_TEST_CHANNEL 15 /* Test/debug packets */ -#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80) -#define SDPCM_NEXTLEN_MASK 0x00ff0000 -#define SDPCM_NEXTLEN_SHIFT 16 -#define SDPCM_DOFFSET_MASK 0xff000000 -#define SDPCM_DOFFSET_SHIFT 24 -#define SDPCM_FCMASK_MASK 0x000000ff -#define SDPCM_WINDOW_MASK 0x0000ff00 -#define SDPCM_WINDOW_SHIFT 8 - -static inline u8 brcmf_sdio_getdatoffset(u8 *swheader) -{ - u32 hdrvalue; - hdrvalue = *(u32 *)swheader; - return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT); -} - -static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, - struct brcmf_sdio_hdrinfo *rd, - enum brcmf_sdio_frmtype type) -{ - u16 len, checksum; - u8 rx_seq, fc, tx_seq_max; - u32 swheader; - - trace_brcmf_sdpcm_hdr(SDPCM_RX, header); - - /* hw header */ - len = get_unaligned_le16(header); - checksum = get_unaligned_le16(header + sizeof(u16)); - /* All zero means no more to read */ - if (!(len | checksum)) { - bus->rxpending = false; - return -ENODATA; - } - if ((u16)(~(len ^ checksum))) { - brcmf_err("HW header checksum error\n"); - bus->sdcnt.rx_badhdr++; - brcmf_sdio_rxfail(bus, false, false); - return -EIO; - } - if (len < SDPCM_HDRLEN) { - brcmf_err("HW header length error\n"); - return -EPROTO; - } - if (type == BRCMF_SDIO_FT_SUPER && - (roundup(len, bus->blocksize) != rd->len)) { - brcmf_err("HW superframe header length error\n"); - return -EPROTO; - } - if (type == BRCMF_SDIO_FT_SUB && len > rd->len) { - brcmf_err("HW subframe header length error\n"); - return -EPROTO; - } - rd->len = len; - - /* software header */ - header += SDPCM_HWHDR_LEN; - swheader = le32_to_cpu(*(__le32 *)header); - if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) { - brcmf_err("Glom descriptor found in superframe head\n"); - rd->len = 0; - return -EINVAL; - } - rx_seq = (u8)(swheader & SDPCM_SEQ_MASK); - rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT; - if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL && - type != BRCMF_SDIO_FT_SUPER) { - brcmf_err("HW header length too long\n"); - bus->sdcnt.rx_toolong++; - brcmf_sdio_rxfail(bus, false, false); - rd->len = 0; - return -EPROTO; - } - if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) { - brcmf_err("Wrong channel for superframe\n"); - rd->len = 0; - return -EINVAL; - } - if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL && - rd->channel != SDPCM_EVENT_CHANNEL) { - brcmf_err("Wrong channel for subframe\n"); - rd->len = 0; - return -EINVAL; - } - rd->dat_offset = brcmf_sdio_getdatoffset(header); - if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { - brcmf_err("seq %d: bad data offset\n", rx_seq); - bus->sdcnt.rx_badhdr++; - brcmf_sdio_rxfail(bus, false, false); - rd->len = 0; - return -ENXIO; - } - if (rd->seq_num != rx_seq) { - brcmf_err("seq %d: sequence number error, expect %d\n", - rx_seq, rd->seq_num); - bus->sdcnt.rx_badseq++; - rd->seq_num = rx_seq; - } - /* no need to check the reset for subframe */ - if (type == BRCMF_SDIO_FT_SUB) - return 0; - rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT; - if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) { - /* only warm for NON glom packet */ - if (rd->channel != SDPCM_GLOM_CHANNEL) - brcmf_err("seq %d: next length error\n", rx_seq); - rd->len_nxtfrm = 0; - } - swheader = le32_to_cpu(*(__le32 *)(header + 4)); - fc = swheader & SDPCM_FCMASK_MASK; - if (bus->flowcontrol != fc) { - if (~bus->flowcontrol & fc) - bus->sdcnt.fc_xoff++; - if (bus->flowcontrol & ~fc) - bus->sdcnt.fc_xon++; - bus->sdcnt.fc_rcvd++; - bus->flowcontrol = fc; - } - tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT; - if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) { - brcmf_err("seq %d: max tx seq number error\n", rx_seq); - tx_seq_max = bus->tx_seq + 2; - } - bus->tx_max = tx_seq_max; - - return 0; -} - -static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length) -{ - *(__le16 *)header = cpu_to_le16(frm_length); - *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length); -} - -static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header, - struct brcmf_sdio_hdrinfo *hd_info) -{ - u32 hdrval; - u8 hdr_offset; - - brcmf_sdio_update_hwhdr(header, hd_info->len); - hdr_offset = SDPCM_HWHDR_LEN; - - if (bus->txglom) { - hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24); - *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); - hdrval = (u16)hd_info->tail_pad << 16; - *(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval); - hdr_offset += SDPCM_HWEXT_LEN; - } - - hdrval = hd_info->seq_num; - hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) & - SDPCM_CHANNEL_MASK; - hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) & - SDPCM_DOFFSET_MASK; - *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); - *(((__le32 *)(header + hdr_offset)) + 1) = 0; - trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header); -} - -static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) -{ - u16 dlen, totlen; - u8 *dptr, num = 0; - u16 sublen; - struct sk_buff *pfirst, *pnext; - - int errcode; - u8 doff, sfdoff; - - struct brcmf_sdio_hdrinfo rd_new; - - /* If packets, issue read(s) and send up packet chain */ - /* Return sequence numbers consumed? */ - - brcmf_dbg(SDIO, "start: glomd %p glom %p\n", - bus->glomd, skb_peek(&bus->glom)); - - /* If there's a descriptor, generate the packet chain */ - if (bus->glomd) { - pfirst = pnext = NULL; - dlen = (u16) (bus->glomd->len); - dptr = bus->glomd->data; - if (!dlen || (dlen & 1)) { - brcmf_err("bad glomd len(%d), ignore descriptor\n", - dlen); - dlen = 0; - } - - for (totlen = num = 0; dlen; num++) { - /* Get (and move past) next length */ - sublen = get_unaligned_le16(dptr); - dlen -= sizeof(u16); - dptr += sizeof(u16); - if ((sublen < SDPCM_HDRLEN) || - ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) { - brcmf_err("descriptor len %d bad: %d\n", - num, sublen); - pnext = NULL; - break; - } - if (sublen % bus->sgentry_align) { - brcmf_err("sublen %d not multiple of %d\n", - sublen, bus->sgentry_align); - } - totlen += sublen; - - /* For last frame, adjust read len so total - is a block multiple */ - if (!dlen) { - sublen += - (roundup(totlen, bus->blocksize) - totlen); - totlen = roundup(totlen, bus->blocksize); - } - - /* Allocate/chain packet for next subframe */ - pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align); - if (pnext == NULL) { - brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n", - num, sublen); - break; - } - skb_queue_tail(&bus->glom, pnext); - - /* Adhere to start alignment requirements */ - pkt_align(pnext, sublen, bus->sgentry_align); - } - - /* If all allocations succeeded, save packet chain - in bus structure */ - if (pnext) { - brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n", - totlen, num); - if (BRCMF_GLOM_ON() && bus->cur_read.len && - totlen != bus->cur_read.len) { - brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n", - bus->cur_read.len, totlen, rxseq); - } - pfirst = pnext = NULL; - } else { - brcmf_sdio_free_glom(bus); - num = 0; - } - - /* Done with descriptor packet */ - brcmu_pkt_buf_free_skb(bus->glomd); - bus->glomd = NULL; - bus->cur_read.len = 0; - } - - /* Ok -- either we just generated a packet chain, - or had one from before */ - if (!skb_queue_empty(&bus->glom)) { - if (BRCMF_GLOM_ON()) { - brcmf_dbg(GLOM, "try superframe read, packet chain:\n"); - skb_queue_walk(&bus->glom, pnext) { - brcmf_dbg(GLOM, " %p: %p len 0x%04x (%d)\n", - pnext, (u8 *) (pnext->data), - pnext->len, pnext->len); - } - } - - pfirst = skb_peek(&bus->glom); - dlen = (u16) brcmf_sdio_glom_len(bus); - - /* Do an SDIO read for the superframe. Configurable iovar to - * read directly into the chained packet, or allocate a large - * packet and and copy into the chain. - */ - sdio_claim_host(bus->sdiodev->func[1]); - errcode = brcmf_sdiod_recv_chain(bus->sdiodev, - &bus->glom, dlen); - sdio_release_host(bus->sdiodev->func[1]); - bus->sdcnt.f2rxdata++; - - /* On failure, kill the superframe, allow a couple retries */ - if (errcode < 0) { - brcmf_err("glom read of %d bytes failed: %d\n", - dlen, errcode); - - sdio_claim_host(bus->sdiodev->func[1]); - if (bus->glomerr++ < 3) { - brcmf_sdio_rxfail(bus, true, true); - } else { - bus->glomerr = 0; - brcmf_sdio_rxfail(bus, true, false); - bus->sdcnt.rxglomfail++; - brcmf_sdio_free_glom(bus); - } - sdio_release_host(bus->sdiodev->func[1]); - return 0; - } - - brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), - pfirst->data, min_t(int, pfirst->len, 48), - "SUPERFRAME:\n"); - - rd_new.seq_num = rxseq; - rd_new.len = dlen; - sdio_claim_host(bus->sdiodev->func[1]); - errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new, - BRCMF_SDIO_FT_SUPER); - sdio_release_host(bus->sdiodev->func[1]); - bus->cur_read.len = rd_new.len_nxtfrm << 4; - - /* Remove superframe header, remember offset */ - skb_pull(pfirst, rd_new.dat_offset); - sfdoff = rd_new.dat_offset; - num = 0; - - /* Validate all the subframe headers */ - skb_queue_walk(&bus->glom, pnext) { - /* leave when invalid subframe is found */ - if (errcode) - break; - - rd_new.len = pnext->len; - rd_new.seq_num = rxseq++; - sdio_claim_host(bus->sdiodev->func[1]); - errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new, - BRCMF_SDIO_FT_SUB); - sdio_release_host(bus->sdiodev->func[1]); - brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), - pnext->data, 32, "subframe:\n"); - - num++; - } - - if (errcode) { - /* Terminate frame on error, request - a couple retries */ - sdio_claim_host(bus->sdiodev->func[1]); - if (bus->glomerr++ < 3) { - /* Restore superframe header space */ - skb_push(pfirst, sfdoff); - brcmf_sdio_rxfail(bus, true, true); - } else { - bus->glomerr = 0; - brcmf_sdio_rxfail(bus, true, false); - bus->sdcnt.rxglomfail++; - brcmf_sdio_free_glom(bus); - } - sdio_release_host(bus->sdiodev->func[1]); - bus->cur_read.len = 0; - return 0; - } - - /* Basic SD framing looks ok - process each packet (header) */ - - skb_queue_walk_safe(&bus->glom, pfirst, pnext) { - dptr = (u8 *) (pfirst->data); - sublen = get_unaligned_le16(dptr); - doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]); - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), - dptr, pfirst->len, - "Rx Subframe Data:\n"); - - __skb_trim(pfirst, sublen); - skb_pull(pfirst, doff); - - if (pfirst->len == 0) { - skb_unlink(pfirst, &bus->glom); - brcmu_pkt_buf_free_skb(pfirst); - continue; - } - - brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), - pfirst->data, - min_t(int, pfirst->len, 32), - "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n", - bus->glom.qlen, pfirst, pfirst->data, - pfirst->len, pfirst->next, - pfirst->prev); - skb_unlink(pfirst, &bus->glom); - brcmf_rx_frame(bus->sdiodev->dev, pfirst); - bus->sdcnt.rxglompkts++; - } - - bus->sdcnt.rxglomframes++; - } - return num; -} - -static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, - bool *pending) -{ - DECLARE_WAITQUEUE(wait, current); - int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT); - - /* Wait until control frame is available */ - add_wait_queue(&bus->dcmd_resp_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - while (!(*condition) && (!signal_pending(current) && timeout)) - timeout = schedule_timeout(timeout); - - if (signal_pending(current)) - *pending = true; - - set_current_state(TASK_RUNNING); - remove_wait_queue(&bus->dcmd_resp_wait, &wait); - - return timeout; -} - -static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus) -{ - if (waitqueue_active(&bus->dcmd_resp_wait)) - wake_up_interruptible(&bus->dcmd_resp_wait); - - return 0; -} -static void -brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) -{ - uint rdlen, pad; - u8 *buf = NULL, *rbuf; - int sdret; - - brcmf_dbg(TRACE, "Enter\n"); - - if (bus->rxblen) - buf = vzalloc(bus->rxblen); - if (!buf) - goto done; - - rbuf = bus->rxbuf; - pad = ((unsigned long)rbuf % bus->head_align); - if (pad) - rbuf += (bus->head_align - pad); - - /* Copy the already-read portion over */ - memcpy(buf, hdr, BRCMF_FIRSTREAD); - if (len <= BRCMF_FIRSTREAD) - goto gotpkt; - - /* Raise rdlen to next SDIO block to avoid tail command */ - rdlen = len - BRCMF_FIRSTREAD; - if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { - pad = bus->blocksize - (rdlen % bus->blocksize); - if ((pad <= bus->roundup) && (pad < bus->blocksize) && - ((len + pad) < bus->sdiodev->bus_if->maxctl)) - rdlen += pad; - } else if (rdlen % bus->head_align) { - rdlen += bus->head_align - (rdlen % bus->head_align); - } - - /* Drop if the read is too big or it exceeds our maximum */ - if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) { - brcmf_err("%d-byte control read exceeds %d-byte buffer\n", - rdlen, bus->sdiodev->bus_if->maxctl); - brcmf_sdio_rxfail(bus, false, false); - goto done; - } - - if ((len - doff) > bus->sdiodev->bus_if->maxctl) { - brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", - len, len - doff, bus->sdiodev->bus_if->maxctl); - bus->sdcnt.rx_toolong++; - brcmf_sdio_rxfail(bus, false, false); - goto done; - } - - /* Read remain of frame body */ - sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen); - bus->sdcnt.f2rxdata++; - - /* Control frame failures need retransmission */ - if (sdret < 0) { - brcmf_err("read %d control bytes failed: %d\n", - rdlen, sdret); - bus->sdcnt.rxc_errors++; - brcmf_sdio_rxfail(bus, true, true); - goto done; - } else - memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen); - -gotpkt: - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), - buf, len, "RxCtrl:\n"); - - /* Point to valid data and indicate its length */ - spin_lock_bh(&bus->rxctl_lock); - if (bus->rxctl) { - brcmf_err("last control frame is being processed.\n"); - spin_unlock_bh(&bus->rxctl_lock); - vfree(buf); - goto done; - } - bus->rxctl = buf + doff; - bus->rxctl_orig = buf; - bus->rxlen = len - doff; - spin_unlock_bh(&bus->rxctl_lock); - -done: - /* Awake any waiters */ - brcmf_sdio_dcmd_resp_wake(bus); -} - -/* Pad read to blocksize for efficiency */ -static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) -{ - if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) { - *pad = bus->blocksize - (*rdlen % bus->blocksize); - if (*pad <= bus->roundup && *pad < bus->blocksize && - *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ) - *rdlen += *pad; - } else if (*rdlen % bus->head_align) { - *rdlen += bus->head_align - (*rdlen % bus->head_align); - } -} - -static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) -{ - struct sk_buff *pkt; /* Packet for event or data frames */ - u16 pad; /* Number of pad bytes to read */ - uint rxleft = 0; /* Remaining number of frames allowed */ - int ret; /* Return code from calls */ - uint rxcount = 0; /* Total frames read */ - struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new; - u8 head_read = 0; - - brcmf_dbg(TRACE, "Enter\n"); - - /* Not finished unless we encounter no more frames indication */ - bus->rxpending = true; - - for (rd->seq_num = bus->rx_seq, rxleft = maxframes; - !bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_SDIOD_DATA; - rd->seq_num++, rxleft--) { - - /* Handle glomming separately */ - if (bus->glomd || !skb_queue_empty(&bus->glom)) { - u8 cnt; - brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n", - bus->glomd, skb_peek(&bus->glom)); - cnt = brcmf_sdio_rxglom(bus, rd->seq_num); - brcmf_dbg(GLOM, "rxglom returned %d\n", cnt); - rd->seq_num += cnt - 1; - rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; - continue; - } - - rd->len_left = rd->len; - /* read header first for unknow frame length */ - sdio_claim_host(bus->sdiodev->func[1]); - if (!rd->len) { - ret = brcmf_sdiod_recv_buf(bus->sdiodev, - bus->rxhdr, BRCMF_FIRSTREAD); - bus->sdcnt.f2rxhdrs++; - if (ret < 0) { - brcmf_err("RXHEADER FAILED: %d\n", - ret); - bus->sdcnt.rx_hdrfail++; - brcmf_sdio_rxfail(bus, true, true); - sdio_release_host(bus->sdiodev->func[1]); - continue; - } - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(), - bus->rxhdr, SDPCM_HDRLEN, - "RxHdr:\n"); - - if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd, - BRCMF_SDIO_FT_NORMAL)) { - sdio_release_host(bus->sdiodev->func[1]); - if (!bus->rxpending) - break; - else - continue; - } - - if (rd->channel == SDPCM_CONTROL_CHANNEL) { - brcmf_sdio_read_control(bus, bus->rxhdr, - rd->len, - rd->dat_offset); - /* prepare the descriptor for the next read */ - rd->len = rd->len_nxtfrm << 4; - rd->len_nxtfrm = 0; - /* treat all packet as event if we don't know */ - rd->channel = SDPCM_EVENT_CHANNEL; - sdio_release_host(bus->sdiodev->func[1]); - continue; - } - rd->len_left = rd->len > BRCMF_FIRSTREAD ? - rd->len - BRCMF_FIRSTREAD : 0; - head_read = BRCMF_FIRSTREAD; - } - - brcmf_sdio_pad(bus, &pad, &rd->len_left); - - pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read + - bus->head_align); - if (!pkt) { - /* Give up on data, request rtx of events */ - brcmf_err("brcmu_pkt_buf_get_skb failed\n"); - brcmf_sdio_rxfail(bus, false, - RETRYCHAN(rd->channel)); - sdio_release_host(bus->sdiodev->func[1]); - continue; - } - skb_pull(pkt, head_read); - pkt_align(pkt, rd->len_left, bus->head_align); - - ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt); - bus->sdcnt.f2rxdata++; - sdio_release_host(bus->sdiodev->func[1]); - - if (ret < 0) { - brcmf_err("read %d bytes from channel %d failed: %d\n", - rd->len, rd->channel, ret); - brcmu_pkt_buf_free_skb(pkt); - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_rxfail(bus, true, - RETRYCHAN(rd->channel)); - sdio_release_host(bus->sdiodev->func[1]); - continue; - } - - if (head_read) { - skb_push(pkt, head_read); - memcpy(pkt->data, bus->rxhdr, head_read); - head_read = 0; - } else { - memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN); - rd_new.seq_num = rd->seq_num; - sdio_claim_host(bus->sdiodev->func[1]); - if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new, - BRCMF_SDIO_FT_NORMAL)) { - rd->len = 0; - brcmu_pkt_buf_free_skb(pkt); - } - bus->sdcnt.rx_readahead_cnt++; - if (rd->len != roundup(rd_new.len, 16)) { - brcmf_err("frame length mismatch:read %d, should be %d\n", - rd->len, - roundup(rd_new.len, 16) >> 4); - rd->len = 0; - brcmf_sdio_rxfail(bus, true, true); - sdio_release_host(bus->sdiodev->func[1]); - brcmu_pkt_buf_free_skb(pkt); - continue; - } - sdio_release_host(bus->sdiodev->func[1]); - rd->len_nxtfrm = rd_new.len_nxtfrm; - rd->channel = rd_new.channel; - rd->dat_offset = rd_new.dat_offset; - - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && - BRCMF_DATA_ON()) && - BRCMF_HDRS_ON(), - bus->rxhdr, SDPCM_HDRLEN, - "RxHdr:\n"); - - if (rd_new.channel == SDPCM_CONTROL_CHANNEL) { - brcmf_err("readahead on control packet %d?\n", - rd_new.seq_num); - /* Force retry w/normal header read */ - rd->len = 0; - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_rxfail(bus, false, true); - sdio_release_host(bus->sdiodev->func[1]); - brcmu_pkt_buf_free_skb(pkt); - continue; - } - } - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), - pkt->data, rd->len, "Rx Data:\n"); - - /* Save superframe descriptor and allocate packet frame */ - if (rd->channel == SDPCM_GLOM_CHANNEL) { - if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) { - brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n", - rd->len); - brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), - pkt->data, rd->len, - "Glom Data:\n"); - __skb_trim(pkt, rd->len); - skb_pull(pkt, SDPCM_HDRLEN); - bus->glomd = pkt; - } else { - brcmf_err("%s: glom superframe w/o " - "descriptor!\n", __func__); - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_rxfail(bus, false, false); - sdio_release_host(bus->sdiodev->func[1]); - } - /* prepare the descriptor for the next read */ - rd->len = rd->len_nxtfrm << 4; - rd->len_nxtfrm = 0; - /* treat all packet as event if we don't know */ - rd->channel = SDPCM_EVENT_CHANNEL; - continue; - } - - /* Fill in packet len and prio, deliver upward */ - __skb_trim(pkt, rd->len); - skb_pull(pkt, rd->dat_offset); - - /* prepare the descriptor for the next read */ - rd->len = rd->len_nxtfrm << 4; - rd->len_nxtfrm = 0; - /* treat all packet as event if we don't know */ - rd->channel = SDPCM_EVENT_CHANNEL; - - if (pkt->len == 0) { - brcmu_pkt_buf_free_skb(pkt); - continue; - } - - brcmf_rx_frame(bus->sdiodev->dev, pkt); - } - - rxcount = maxframes - rxleft; - /* Message if we hit the limit */ - if (!rxleft) - brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes); - else - brcmf_dbg(DATA, "processed %d frames\n", rxcount); - /* Back off rxseq if awaiting rtx, update rx_seq */ - if (bus->rxskip) - rd->seq_num--; - bus->rx_seq = rd->seq_num; - - return rxcount; -} - -static void -brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus) -{ - if (waitqueue_active(&bus->ctrl_wait)) - wake_up_interruptible(&bus->ctrl_wait); - return; -} - -static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt) -{ - u16 head_pad; - u8 *dat_buf; - - dat_buf = (u8 *)(pkt->data); - - /* Check head padding */ - head_pad = ((unsigned long)dat_buf % bus->head_align); - if (head_pad) { - if (skb_headroom(pkt) < head_pad) { - bus->sdiodev->bus_if->tx_realloc++; - head_pad = 0; - if (skb_cow(pkt, head_pad)) - return -ENOMEM; - } - skb_push(pkt, head_pad); - dat_buf = (u8 *)(pkt->data); - memset(dat_buf, 0, head_pad + bus->tx_hdrlen); - } - return head_pad; -} - -/** - * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for - * bus layer usage. - */ -/* flag marking a dummy skb added for DMA alignment requirement */ -#define ALIGN_SKB_FLAG 0x8000 -/* bit mask of data length chopped from the previous packet */ -#define ALIGN_SKB_CHOP_LEN_MASK 0x7fff - -static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, - struct sk_buff_head *pktq, - struct sk_buff *pkt, u16 total_len) -{ - struct brcmf_sdio_dev *sdiodev; - struct sk_buff *pkt_pad; - u16 tail_pad, tail_chop, chain_pad; - unsigned int blksize; - bool lastfrm; - int ntail, ret; - - sdiodev = bus->sdiodev; - blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize; - /* sg entry alignment should be a divisor of block size */ - WARN_ON(blksize % bus->sgentry_align); - - /* Check tail padding */ - lastfrm = skb_queue_is_last(pktq, pkt); - tail_pad = 0; - tail_chop = pkt->len % bus->sgentry_align; - if (tail_chop) - tail_pad = bus->sgentry_align - tail_chop; - chain_pad = (total_len + tail_pad) % blksize; - if (lastfrm && chain_pad) - tail_pad += blksize - chain_pad; - if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) { - pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop + - bus->head_align); - if (pkt_pad == NULL) - return -ENOMEM; - ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad); - if (unlikely(ret < 0)) { - kfree_skb(pkt_pad); - return ret; - } - memcpy(pkt_pad->data, - pkt->data + pkt->len - tail_chop, - tail_chop); - *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; - skb_trim(pkt, pkt->len - tail_chop); - skb_trim(pkt_pad, tail_pad + tail_chop); - __skb_queue_after(pktq, pkt, pkt_pad); - } else { - ntail = pkt->data_len + tail_pad - - (pkt->end - pkt->tail); - if (skb_cloned(pkt) || ntail > 0) - if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC)) - return -ENOMEM; - if (skb_linearize(pkt)) - return -ENOMEM; - __skb_put(pkt, tail_pad); - } - - return tail_pad; -} - -/** - * brcmf_sdio_txpkt_prep - packet preparation for transmit - * @bus: brcmf_sdio structure pointer - * @pktq: packet list pointer - * @chan: virtual channel to transmit the packet - * - * Processes to be applied to the packet - * - Align data buffer pointer - * - Align data buffer length - * - Prepare header - * Return: negative value if there is error - */ -static int -brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, - uint chan) -{ - u16 head_pad, total_len; - struct sk_buff *pkt_next; - u8 txseq; - int ret; - struct brcmf_sdio_hdrinfo hd_info = {0}; - - txseq = bus->tx_seq; - total_len = 0; - skb_queue_walk(pktq, pkt_next) { - /* alignment packet inserted in previous - * loop cycle can be skipped as it is - * already properly aligned and does not - * need an sdpcm header. - */ - if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG) - continue; - - /* align packet data pointer */ - ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next); - if (ret < 0) - return ret; - head_pad = (u16)ret; - if (head_pad) - memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad); - - total_len += pkt_next->len; - - hd_info.len = pkt_next->len; - hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next); - if (bus->txglom && pktq->qlen > 1) { - ret = brcmf_sdio_txpkt_prep_sg(bus, pktq, - pkt_next, total_len); - if (ret < 0) - return ret; - hd_info.tail_pad = (u16)ret; - total_len += (u16)ret; - } - - hd_info.channel = chan; - hd_info.dat_offset = head_pad + bus->tx_hdrlen; - hd_info.seq_num = txseq++; - - /* Now fill the header */ - brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info); - - if (BRCMF_BYTES_ON() && - ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || - (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) - brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len, - "Tx Frame:\n"); - else if (BRCMF_HDRS_ON()) - brcmf_dbg_hex_dump(true, pkt_next->data, - head_pad + bus->tx_hdrlen, - "Tx Header:\n"); - } - /* Hardware length tag of the first packet should be total - * length of the chain (including padding) - */ - if (bus->txglom) - brcmf_sdio_update_hwhdr(pktq->next->data, total_len); - return 0; -} - -/** - * brcmf_sdio_txpkt_postp - packet post processing for transmit - * @bus: brcmf_sdio structure pointer - * @pktq: packet list pointer - * - * Processes to be applied to the packet - * - Remove head padding - * - Remove tail padding - */ -static void -brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) -{ - u8 *hdr; - u32 dat_offset; - u16 tail_pad; - u16 dummy_flags, chop_len; - struct sk_buff *pkt_next, *tmp, *pkt_prev; - - skb_queue_walk_safe(pktq, pkt_next, tmp) { - dummy_flags = *(u16 *)(pkt_next->cb); - if (dummy_flags & ALIGN_SKB_FLAG) { - chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK; - if (chop_len) { - pkt_prev = pkt_next->prev; - skb_put(pkt_prev, chop_len); - } - __skb_unlink(pkt_next, pktq); - brcmu_pkt_buf_free_skb(pkt_next); - } else { - hdr = pkt_next->data + bus->tx_hdrlen - SDPCM_SWHDR_LEN; - dat_offset = le32_to_cpu(*(__le32 *)hdr); - dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >> - SDPCM_DOFFSET_SHIFT; - skb_pull(pkt_next, dat_offset); - if (bus->txglom) { - tail_pad = le16_to_cpu(*(__le16 *)(hdr - 2)); - skb_trim(pkt_next, pkt_next->len - tail_pad); - } - } - } -} - -/* Writes a HW/SW header into the packet and sends it. */ -/* Assumes: (a) header space already there, (b) caller holds lock */ -static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, - uint chan) -{ - int ret; - struct sk_buff *pkt_next, *tmp; - - brcmf_dbg(TRACE, "Enter\n"); - - ret = brcmf_sdio_txpkt_prep(bus, pktq, chan); - if (ret) - goto done; - - sdio_claim_host(bus->sdiodev->func[1]); - ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq); - bus->sdcnt.f2txdata++; - - if (ret < 0) - brcmf_sdio_txfail(bus); - - sdio_release_host(bus->sdiodev->func[1]); - -done: - brcmf_sdio_txpkt_postp(bus, pktq); - if (ret == 0) - bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP; - skb_queue_walk_safe(pktq, pkt_next, tmp) { - __skb_unlink(pkt_next, pktq); - brcmf_txcomplete(bus->sdiodev->dev, pkt_next, ret == 0); - } - return ret; -} - -static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) -{ - struct sk_buff *pkt; - struct sk_buff_head pktq; - u32 intstatus = 0; - int ret = 0, prec_out, i; - uint cnt = 0; - u8 tx_prec_map, pkt_num; - - brcmf_dbg(TRACE, "Enter\n"); - - tx_prec_map = ~bus->flowcontrol; - - /* Send frames until the limit or some other event */ - for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { - pkt_num = 1; - if (bus->txglom) - pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, - bus->sdiodev->txglomsz); - pkt_num = min_t(u32, pkt_num, - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)); - __skb_queue_head_init(&pktq); - spin_lock_bh(&bus->txq_lock); - for (i = 0; i < pkt_num; i++) { - pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, - &prec_out); - if (pkt == NULL) - break; - __skb_queue_tail(&pktq, pkt); - } - spin_unlock_bh(&bus->txq_lock); - if (i == 0) - break; - - ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); - - cnt += i; - - /* In poll mode, need to check for other events */ - if (!bus->intr) { - /* Check device status, signal pending interrupt */ - sdio_claim_host(bus->sdiodev->func[1]); - ret = r_sdreg32(bus, &intstatus, - offsetof(struct sdpcmd_regs, - intstatus)); - sdio_release_host(bus->sdiodev->func[1]); - bus->sdcnt.f2txdata++; - if (ret != 0) - break; - if (intstatus & bus->hostintmask) - atomic_set(&bus->ipend, 1); - } - } - - /* Deflow-control stack if needed */ - if ((bus->sdiodev->state == BRCMF_SDIOD_DATA) && - bus->txoff && (pktq_len(&bus->txq) < TXLOW)) { - bus->txoff = false; - brcmf_txflowblock(bus->sdiodev->dev, false); - } - - return cnt; -} - -static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len) -{ - u8 doff; - u16 pad; - uint retries = 0; - struct brcmf_sdio_hdrinfo hd_info = {0}; - int ret; - - brcmf_dbg(TRACE, "Enter\n"); - - /* Back the pointer to make room for bus header */ - frame -= bus->tx_hdrlen; - len += bus->tx_hdrlen; - - /* Add alignment padding (optional for ctl frames) */ - doff = ((unsigned long)frame % bus->head_align); - if (doff) { - frame -= doff; - len += doff; - memset(frame + bus->tx_hdrlen, 0, doff); - } - - /* Round send length to next SDIO block */ - pad = 0; - if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { - pad = bus->blocksize - (len % bus->blocksize); - if ((pad > bus->roundup) || (pad >= bus->blocksize)) - pad = 0; - } else if (len % bus->head_align) { - pad = bus->head_align - (len % bus->head_align); - } - len += pad; - - hd_info.len = len - pad; - hd_info.channel = SDPCM_CONTROL_CHANNEL; - hd_info.dat_offset = doff + bus->tx_hdrlen; - hd_info.seq_num = bus->tx_seq; - hd_info.lastfrm = true; - hd_info.tail_pad = pad; - brcmf_sdio_hdpack(bus, frame, &hd_info); - - if (bus->txglom) - brcmf_sdio_update_hwhdr(frame, len); - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), - frame, len, "Tx Frame:\n"); - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && - BRCMF_HDRS_ON(), - frame, min_t(u16, len, 16), "TxHdr:\n"); - - do { - ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); - - if (ret < 0) - brcmf_sdio_txfail(bus); - else - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; - } while (ret < 0 && retries++ < TXRETRIES); - - return ret; -} - -static void brcmf_sdio_bus_stop(struct device *dev) -{ - u32 local_hostintmask; - u8 saveclk; - int err; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - - brcmf_dbg(TRACE, "Enter\n"); - - if (bus->watchdog_tsk) { - send_sig(SIGTERM, bus->watchdog_tsk, 1); - kthread_stop(bus->watchdog_tsk); - bus->watchdog_tsk = NULL; - } - - if (sdiodev->state != BRCMF_SDIOD_NOMEDIUM) { - 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); - - /* Turn off the bus (F2), free any pending packets */ - brcmf_dbg(INTR, "disable SDIO interrupts\n"); - sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); - - /* Clear any pending interrupts now that F2 is disabled */ - w_sdreg32(bus, local_hostintmask, - offsetof(struct sdpcmd_regs, intstatus)); - - sdio_release_host(sdiodev->func[1]); - } - /* Clear the data packet queues */ - brcmu_pktq_flush(&bus->txq, true, NULL, NULL); - - /* Clear any held glomming stuff */ - brcmu_pkt_buf_free_skb(bus->glomd); - brcmf_sdio_free_glom(bus); - - /* Clear rx control and wake any waiters */ - spin_lock_bh(&bus->rxctl_lock); - bus->rxlen = 0; - spin_unlock_bh(&bus->rxctl_lock); - brcmf_sdio_dcmd_resp_wake(bus); - - /* Reset some F2 state stuff */ - bus->rxskip = false; - bus->tx_seq = bus->rx_seq = 0; -} - -static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) -{ - unsigned long flags; - - if (bus->sdiodev->oob_irq_requested) { - spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags); - if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) { - enable_irq(bus->sdiodev->pdata->oob_irq_nr); - bus->sdiodev->irq_en = true; - } - spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags); - } -} - -static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) -{ - struct brcmf_core *buscore; - u32 addr; - unsigned long val; - int ret; - - buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); - addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); - - val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); - bus->sdcnt.f1regdata++; - if (ret != 0) - return ret; - - val &= bus->hostintmask; - atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); - - /* Clear interrupts */ - if (val) { - brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); - bus->sdcnt.f1regdata++; - atomic_or(val, &bus->intstatus); - } - - return ret; -} - -static void brcmf_sdio_dpc(struct brcmf_sdio *bus) -{ - u32 newstatus = 0; - unsigned long intstatus; - uint txlimit = bus->txbound; /* Tx frames to send before resched */ - uint framecnt; /* Temporary counter of tx/rx frames */ - int err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - - sdio_claim_host(bus->sdiodev->func[1]); - - /* If waiting for HTAVAIL, check status */ - if (!bus->sr_enabled && bus->clkstate == CLK_PENDING) { - u8 clkctl, devctl = 0; - -#ifdef DEBUG - /* Check for inconsistent device control */ - devctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); -#endif /* DEBUG */ - - /* Read CSR, if clock on switch to AVAIL, else ignore */ - clkctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - - brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", - devctl, clkctl); - - if (SBSDIO_HTAV(clkctl)) { - devctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); - devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); - bus->clkstate = CLK_AVAIL; - } - } - - /* Make sure backplane clock is on */ - brcmf_sdio_bus_sleep(bus, false, true); - - /* Pending interrupt indicates new device status */ - if (atomic_read(&bus->ipend) > 0) { - atomic_set(&bus->ipend, 0); - err = brcmf_sdio_intr_rstatus(bus); - } - - /* Start with leftover status bits */ - intstatus = atomic_xchg(&bus->intstatus, 0); - - /* Handle flow-control change: read new state in case our ack - * crossed another change interrupt. If change still set, assume - * FC ON for safety, let next loop through do the debounce. - */ - if (intstatus & I_HMB_FC_CHANGE) { - intstatus &= ~I_HMB_FC_CHANGE; - err = w_sdreg32(bus, I_HMB_FC_CHANGE, - offsetof(struct sdpcmd_regs, intstatus)); - - err = r_sdreg32(bus, &newstatus, - offsetof(struct sdpcmd_regs, intstatus)); - bus->sdcnt.f1regdata += 2; - atomic_set(&bus->fcstate, - !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE))); - intstatus |= (newstatus & bus->hostintmask); - } - - /* Handle host mailbox indication */ - if (intstatus & I_HMB_HOST_INT) { - intstatus &= ~I_HMB_HOST_INT; - intstatus |= brcmf_sdio_hostmail(bus); - } - - sdio_release_host(bus->sdiodev->func[1]); - - /* Generally don't ask for these, can get CRC errors... */ - if (intstatus & I_WR_OOSYNC) { - brcmf_err("Dongle reports WR_OOSYNC\n"); - intstatus &= ~I_WR_OOSYNC; - } - - if (intstatus & I_RD_OOSYNC) { - brcmf_err("Dongle reports RD_OOSYNC\n"); - intstatus &= ~I_RD_OOSYNC; - } - - if (intstatus & I_SBINT) { - brcmf_err("Dongle reports SBINT\n"); - intstatus &= ~I_SBINT; - } - - /* Would be active due to wake-wlan in gSPI */ - if (intstatus & I_CHIPACTIVE) { - brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n"); - intstatus &= ~I_CHIPACTIVE; - } - - /* Ignore frame indications if rxskip is set */ - if (bus->rxskip) - intstatus &= ~I_HMB_FRAME_IND; - - /* On frame indication, read available frames */ - if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) { - brcmf_sdio_readframes(bus, bus->rxbound); - if (!bus->rxpending) - intstatus &= ~I_HMB_FRAME_IND; - } - - /* Keep still-pending events for next scheduling */ - if (intstatus) - atomic_or(intstatus, &bus->intstatus); - - brcmf_sdio_clrintr(bus); - - if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && - data_ok(bus)) { - sdio_claim_host(bus->sdiodev->func[1]); - if (bus->ctrl_frame_stat) { - err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, - bus->ctrl_frame_len); - bus->ctrl_frame_err = err; - wmb(); - bus->ctrl_frame_stat = false; - } - sdio_release_host(bus->sdiodev->func[1]); - brcmf_sdio_wait_event_wakeup(bus); - } - /* Send queued frames (limit 1 if rx may still be pending) */ - if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && - data_ok(bus)) { - framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : - txlimit; - brcmf_sdio_sendfromq(bus, framecnt); - } - - if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) { - brcmf_err("failed backplane access over SDIO, halting operation\n"); - atomic_set(&bus->intstatus, 0); - if (bus->ctrl_frame_stat) { - sdio_claim_host(bus->sdiodev->func[1]); - if (bus->ctrl_frame_stat) { - bus->ctrl_frame_err = -ENODEV; - wmb(); - bus->ctrl_frame_stat = false; - brcmf_sdio_wait_event_wakeup(bus); - } - sdio_release_host(bus->sdiodev->func[1]); - } - } else if (atomic_read(&bus->intstatus) || - atomic_read(&bus->ipend) > 0 || - (!atomic_read(&bus->fcstate) && - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus))) { - bus->dpc_triggered = true; - } -} - -static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - - return &bus->txq; -} - -static bool brcmf_sdio_prec_enq(struct pktq *q, struct sk_buff *pkt, int prec) -{ - struct sk_buff *p; - int eprec = -1; /* precedence to evict from */ - - /* Fast case, precedence queue is not full and we are also not - * exceeding total queue length - */ - if (!pktq_pfull(q, prec) && !pktq_full(q)) { - brcmu_pktq_penq(q, prec, pkt); - return true; - } - - /* Determine precedence from which to evict packet, if any */ - if (pktq_pfull(q, prec)) { - eprec = prec; - } else if (pktq_full(q)) { - p = brcmu_pktq_peek_tail(q, &eprec); - if (eprec > prec) - return false; - } - - /* Evict if needed */ - if (eprec >= 0) { - /* Detect queueing to unconfigured precedence */ - if (eprec == prec) - return false; /* refuse newer (incoming) packet */ - /* Evict packet according to discard policy */ - p = brcmu_pktq_pdeq_tail(q, eprec); - if (p == NULL) - brcmf_err("brcmu_pktq_pdeq_tail() failed\n"); - brcmu_pkt_buf_free_skb(p); - } - - /* Enqueue */ - p = brcmu_pktq_penq(q, prec, pkt); - if (p == NULL) - brcmf_err("brcmu_pktq_penq() failed\n"); - - return p != NULL; -} - -static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) -{ - int ret = -EBADE; - uint prec; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - - brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); - if (sdiodev->state != BRCMF_SDIOD_DATA) - return -EIO; - - /* Add space for the header */ - skb_push(pkt, bus->tx_hdrlen); - /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */ - - prec = prio2prec((pkt->priority & PRIOMASK)); - - /* Check for existing queue, current flow-control, - pending event, or pending clock */ - brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq)); - bus->sdcnt.fcqueued++; - - /* Priority based enq */ - spin_lock_bh(&bus->txq_lock); - /* reset bus_flags in packet cb */ - *(u16 *)(pkt->cb) = 0; - if (!brcmf_sdio_prec_enq(&bus->txq, pkt, prec)) { - skb_pull(pkt, bus->tx_hdrlen); - brcmf_err("out of bus->txq !!!\n"); - ret = -ENOSR; - } else { - ret = 0; - } - - if (pktq_len(&bus->txq) >= TXHI) { - bus->txoff = true; - brcmf_txflowblock(dev, true); - } - spin_unlock_bh(&bus->txq_lock); - -#ifdef DEBUG - if (pktq_plen(&bus->txq, prec) > qcount[prec]) - qcount[prec] = pktq_plen(&bus->txq, prec); -#endif - - brcmf_sdio_trigger_dpc(bus); - return ret; -} - -#ifdef DEBUG -#define CONSOLE_LINE_MAX 192 - -static int brcmf_sdio_readconsole(struct brcmf_sdio *bus) -{ - struct brcmf_console *c = &bus->console; - u8 line[CONSOLE_LINE_MAX], ch; - u32 n, idx, addr; - int rv; - - /* Don't do anything until FWREADY updates console address */ - if (bus->console_addr == 0) - return 0; - - /* Read console log struct */ - addr = bus->console_addr + offsetof(struct rte_console, log_le); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le, - sizeof(c->log_le)); - if (rv < 0) - return rv; - - /* Allocate console buffer (one time only) */ - if (c->buf == NULL) { - c->bufsize = le32_to_cpu(c->log_le.buf_size); - c->buf = kmalloc(c->bufsize, GFP_ATOMIC); - if (c->buf == NULL) - return -ENOMEM; - } - - idx = le32_to_cpu(c->log_le.idx); - - /* Protect against corrupt value */ - if (idx > c->bufsize) - return -EBADE; - - /* Skip reading the console buffer if the index pointer - has not moved */ - if (idx == c->last) - return 0; - - /* Read the console buffer */ - addr = le32_to_cpu(c->log_le.buf); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize); - if (rv < 0) - return rv; - - while (c->last != idx) { - for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { - if (c->last == idx) { - /* This would output a partial line. - * Instead, back up - * the buffer pointer and output this - * line next time around. - */ - if (c->last >= n) - c->last -= n; - else - c->last = c->bufsize - n; - goto break2; - } - ch = c->buf[c->last]; - c->last = (c->last + 1) % c->bufsize; - if (ch == '\n') - break; - line[n] = ch; - } - - if (n > 0) { - if (line[n - 1] == '\r') - n--; - line[n] = 0; - pr_debug("CONSOLE: %s\n", line); - } - } -break2: - - return 0; -} -#endif /* DEBUG */ - -static int -brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - int ret; - - brcmf_dbg(TRACE, "Enter\n"); - if (sdiodev->state != BRCMF_SDIOD_DATA) - return -EIO; - - /* Send from dpc */ - bus->ctrl_frame_buf = msg; - bus->ctrl_frame_len = msglen; - wmb(); - bus->ctrl_frame_stat = true; - - brcmf_sdio_trigger_dpc(bus); - wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, - msecs_to_jiffies(CTL_DONE_TIMEOUT)); - ret = 0; - if (bus->ctrl_frame_stat) { - sdio_claim_host(bus->sdiodev->func[1]); - if (bus->ctrl_frame_stat) { - brcmf_dbg(SDIO, "ctrl_frame timeout\n"); - bus->ctrl_frame_stat = false; - ret = -ETIMEDOUT; - } - sdio_release_host(bus->sdiodev->func[1]); - } - if (!ret) { - brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n", - bus->ctrl_frame_err); - rmb(); - ret = bus->ctrl_frame_err; - } - - if (ret) - bus->sdcnt.tx_ctlerrs++; - else - bus->sdcnt.tx_ctlpkts++; - - return ret; -} - -#ifdef DEBUG -static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus, - struct sdpcm_shared *sh) -{ - u32 addr, console_ptr, console_size, console_index; - char *conbuf = NULL; - __le32 sh_val; - int rv; - - /* obtain console information from device memory */ - addr = sh->console_addr + offsetof(struct rte_console, log_le); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); - if (rv < 0) - return rv; - console_ptr = le32_to_cpu(sh_val); - - addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); - if (rv < 0) - return rv; - console_size = le32_to_cpu(sh_val); - - addr = sh->console_addr + offsetof(struct rte_console, log_le.idx); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); - if (rv < 0) - return rv; - console_index = le32_to_cpu(sh_val); - - /* allocate buffer for console data */ - if (console_size <= CONSOLE_BUFFER_MAX) - conbuf = vzalloc(console_size+1); - - if (!conbuf) - return -ENOMEM; - - /* obtain the console data from device */ - conbuf[console_size] = '\0'; - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf, - console_size); - if (rv < 0) - goto done; - - rv = seq_write(seq, conbuf + console_index, - console_size - console_index); - if (rv < 0) - goto done; - - if (console_index > 0) - rv = seq_write(seq, conbuf, console_index - 1); - -done: - vfree(conbuf); - return rv; -} - -static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus, - struct sdpcm_shared *sh) -{ - int error; - struct brcmf_trap_info tr; - - if ((sh->flags & SDPCM_SHARED_TRAP) == 0) { - brcmf_dbg(INFO, "no trap in firmware\n"); - return 0; - } - - error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr, - sizeof(struct brcmf_trap_info)); - if (error < 0) - return error; - - seq_printf(seq, - "dongle trap info: type 0x%x @ epc 0x%08x\n" - " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" - " lr 0x%08x pc 0x%08x offset 0x%x\n" - " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" - " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", - le32_to_cpu(tr.type), le32_to_cpu(tr.epc), - le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), - le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), - le32_to_cpu(tr.pc), sh->trap_addr, - le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), - le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), - le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), - le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); - - return 0; -} - -static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus, - struct sdpcm_shared *sh) -{ - int error = 0; - char file[80] = "?"; - char expr[80] = ""; - - if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { - brcmf_dbg(INFO, "firmware not built with -assert\n"); - return 0; - } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) { - brcmf_dbg(INFO, "no assert in dongle\n"); - return 0; - } - - sdio_claim_host(bus->sdiodev->func[1]); - if (sh->assert_file_addr != 0) { - error = brcmf_sdiod_ramrw(bus->sdiodev, false, - sh->assert_file_addr, (u8 *)file, 80); - if (error < 0) - return error; - } - if (sh->assert_exp_addr != 0) { - error = brcmf_sdiod_ramrw(bus->sdiodev, false, - sh->assert_exp_addr, (u8 *)expr, 80); - if (error < 0) - return error; - } - sdio_release_host(bus->sdiodev->func[1]); - - seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n", - file, sh->assert_line, expr); - return 0; -} - -static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) -{ - int error; - struct sdpcm_shared sh; - - error = brcmf_sdio_readshared(bus, &sh); - - if (error < 0) - return error; - - if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) - brcmf_dbg(INFO, "firmware not built with -assert\n"); - else if (sh.flags & SDPCM_SHARED_ASSERT) - brcmf_err("assertion in dongle\n"); - - if (sh.flags & SDPCM_SHARED_TRAP) - brcmf_err("firmware trap in dongle\n"); - - return 0; -} - -static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus) -{ - int error = 0; - struct sdpcm_shared sh; - - error = brcmf_sdio_readshared(bus, &sh); - if (error < 0) - goto done; - - error = brcmf_sdio_assert_info(seq, bus, &sh); - if (error < 0) - goto done; - - error = brcmf_sdio_trap_info(seq, bus, &sh); - if (error < 0) - goto done; - - error = brcmf_sdio_dump_console(seq, bus, &sh); - -done: - return error; -} - -static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); - struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus; - - return brcmf_sdio_died_dump(seq, bus); -} - -static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt; - - seq_printf(seq, - "intrcount: %u\nlastintrs: %u\n" - "pollcnt: %u\nregfails: %u\n" - "tx_sderrs: %u\nfcqueued: %u\n" - "rxrtx: %u\nrx_toolong: %u\n" - "rxc_errors: %u\nrx_hdrfail: %u\n" - "rx_badhdr: %u\nrx_badseq: %u\n" - "fc_rcvd: %u\nfc_xoff: %u\n" - "fc_xon: %u\nrxglomfail: %u\n" - "rxglomframes: %u\nrxglompkts: %u\n" - "f2rxhdrs: %u\nf2rxdata: %u\n" - "f2txdata: %u\nf1regdata: %u\n" - "tickcnt: %u\ntx_ctlerrs: %lu\n" - "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" - "rx_ctlpkts: %lu\nrx_readahead: %lu\n", - sdcnt->intrcount, sdcnt->lastintrs, - sdcnt->pollcnt, sdcnt->regfails, - sdcnt->tx_sderrs, sdcnt->fcqueued, - sdcnt->rxrtx, sdcnt->rx_toolong, - sdcnt->rxc_errors, sdcnt->rx_hdrfail, - sdcnt->rx_badhdr, sdcnt->rx_badseq, - sdcnt->fc_rcvd, sdcnt->fc_xoff, - sdcnt->fc_xon, sdcnt->rxglomfail, - sdcnt->rxglomframes, sdcnt->rxglompkts, - sdcnt->f2rxhdrs, sdcnt->f2rxdata, - sdcnt->f2txdata, sdcnt->f1regdata, - sdcnt->tickcnt, sdcnt->tx_ctlerrs, - sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, - sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); - - return 0; -} - -static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) -{ - struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr; - struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); - - if (IS_ERR_OR_NULL(dentry)) - return; - - bus->console_interval = BRCMF_CONSOLE; - - brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read); - brcmf_debugfs_add_entry(drvr, "counters", - brcmf_debugfs_sdio_count_read); - debugfs_create_u32("console_interval", 0644, dentry, - &bus->console_interval); -} -#else -static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) -{ - return 0; -} - -static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) -{ -} -#endif /* DEBUG */ - -static int -brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) -{ - int timeleft; - uint rxlen = 0; - bool pending; - u8 *buf; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - - brcmf_dbg(TRACE, "Enter\n"); - if (sdiodev->state != BRCMF_SDIOD_DATA) - return -EIO; - - /* Wait until control frame is available */ - timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending); - - spin_lock_bh(&bus->rxctl_lock); - rxlen = bus->rxlen; - memcpy(msg, bus->rxctl, min(msglen, rxlen)); - bus->rxctl = NULL; - buf = bus->rxctl_orig; - bus->rxctl_orig = NULL; - bus->rxlen = 0; - spin_unlock_bh(&bus->rxctl_lock); - vfree(buf); - - if (rxlen) { - brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n", - rxlen, msglen); - } else if (timeleft == 0) { - brcmf_err("resumed on timeout\n"); - brcmf_sdio_checkdied(bus); - } else if (pending) { - brcmf_dbg(CTL, "cancelled\n"); - return -ERESTARTSYS; - } else { - brcmf_dbg(CTL, "resumed for unknown reason?\n"); - brcmf_sdio_checkdied(bus); - } - - if (rxlen) - bus->sdcnt.rx_ctlpkts++; - else - bus->sdcnt.rx_ctlerrs++; - - return rxlen ? (int)rxlen : -ETIMEDOUT; -} - -#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) -{ - int err; - - brcmf_dbg(TRACE, "Enter\n"); - - err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase, - (u8 *)fw->data, fw->size); - if (err) - brcmf_err("error %d on writing %d membytes at 0x%08x\n", - err, (int)fw->size, bus->ci->rambase); - else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, - (u8 *)fw->data, fw->size)) - err = -EIO; - - return err; -} - -static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, - void *vars, u32 varsz) -{ - int address; - int err; - - brcmf_dbg(TRACE, "Enter\n"); - - 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; - - return err; -} - -static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, - const struct firmware *fw, - void *nvram, u32 nvlen) -{ - int bcmerror = -EFAULT; - u32 rstvec; - - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_clkctl(bus, CLK_AVAIL, false); - - 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"); - brcmf_fw_nvram_free(nvram); - goto err; - } - - bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen); - brcmf_fw_nvram_free(nvram); - if (bcmerror) { - brcmf_err("dongle nvram file download failed\n"); - goto err; - } - - /* Take arm out of reset */ - if (!brcmf_chip_set_active(bus->ci, rstvec)) { - brcmf_err("error getting out of ARM core reset\n"); - goto err; - } - - /* Allow full data communication using DPC from now on. */ - brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA); - bcmerror = 0; - -err: - brcmf_sdio_clkctl(bus, CLK_SDONLY, false); - sdio_release_host(bus->sdiodev->func[1]); - return bcmerror; -} - -static void brcmf_sdio_sr_init(struct brcmf_sdio *bus) -{ - int err = 0; - u8 val; - - brcmf_dbg(TRACE, "Enter\n"); - - val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err); - if (err) { - brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n"); - return; - } - - val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err); - if (err) { - brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n"); - return; - } - - /* Add CMD14 Support */ - brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP, - (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | - SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT), - &err); - if (err) { - brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n"); - return; - } - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - SBSDIO_FORCE_HT, &err); - if (err) { - brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n"); - return; - } - - /* set flag */ - bus->sr_enabled = true; - brcmf_dbg(INFO, "SR enabled\n"); -} - -/* enable KSO bit */ -static int brcmf_sdio_kso_init(struct brcmf_sdio *bus) -{ - u8 val; - int err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - - /* KSO bit added in SDIO core rev 12 */ - if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) - return 0; - - val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err); - if (err) { - brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n"); - return err; - } - - if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) { - val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN << - SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - val, &err); - if (err) { - brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n"); - return err; - } - } - - return 0; -} - - -static int brcmf_sdio_bus_preinit(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - uint pad_size; - u32 value; - int err; - - /* the commands below use the terms tx and rx from - * a device perspective, ie. bus:txglom affects the - * bus transfers from device to host. - */ - if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) { - /* for sdio core rev < 12, disable txgloming */ - value = 0; - err = brcmf_iovar_data_set(dev, "bus:txglom", &value, - sizeof(u32)); - } else { - /* otherwise, set txglomalign */ - value = 4; - if (sdiodev->pdata) - value = sdiodev->pdata->sd_sgentry_align; - /* SDIO ADMA requires at least 32 bit alignment */ - value = max_t(u32, value, 4); - err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value, - sizeof(u32)); - } - - if (err < 0) - goto done; - - bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; - if (sdiodev->sg_support) { - bus->txglom = false; - value = 1; - pad_size = bus->sdiodev->func[2]->cur_blksize << 1; - err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom", - &value, sizeof(u32)); - if (err < 0) { - /* bus:rxglom is allowed to fail */ - err = 0; - } else { - bus->txglom = true; - bus->tx_hdrlen += SDPCM_HWEXT_LEN; - } - } - brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen); - -done: - return err; -} - -static size_t brcmf_sdio_bus_get_ramsize(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - - return bus->ci->ramsize - bus->ci->srsize; -} - -static int brcmf_sdio_bus_get_memdump(struct device *dev, void *data, - size_t mem_size) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - int err; - int address; - int offset; - int len; - - brcmf_dbg(INFO, "dump at 0x%08x: size=%zu\n", bus->ci->rambase, - mem_size); - - address = bus->ci->rambase; - offset = err = 0; - sdio_claim_host(sdiodev->func[1]); - while (offset < mem_size) { - len = ((offset + MEMBLOCK) < mem_size) ? MEMBLOCK : - mem_size - offset; - err = brcmf_sdiod_ramrw(sdiodev, false, address, data, len); - if (err) { - brcmf_err("error %d on reading %d membytes at 0x%08x\n", - err, len, address); - goto done; - } - data += len; - offset += len; - address += len; - } - -done: - sdio_release_host(sdiodev->func[1]); - return err; -} - -void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus) -{ - if (!bus->dpc_triggered) { - bus->dpc_triggered = true; - queue_work(bus->brcmf_wq, &bus->datawork); - } -} - -void brcmf_sdio_isr(struct brcmf_sdio *bus) -{ - brcmf_dbg(TRACE, "Enter\n"); - - if (!bus) { - brcmf_err("bus is null pointer, exiting\n"); - return; - } - - /* Count the interrupt call */ - bus->sdcnt.intrcount++; - if (in_interrupt()) - atomic_set(&bus->ipend, 1); - else - if (brcmf_sdio_intr_rstatus(bus)) { - brcmf_err("failed backplane access\n"); - } - - /* Disable additional interrupts (is this needed now)? */ - if (!bus->intr) - brcmf_err("isr w/o interrupt configured!\n"); - - bus->dpc_triggered = true; - queue_work(bus->brcmf_wq, &bus->datawork); -} - -static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) -{ - brcmf_dbg(TIMER, "Enter\n"); - - /* Poll period: check device if appropriate. */ - if (!bus->sr_enabled && - bus->poll && (++bus->polltick >= bus->pollrate)) { - u32 intstatus = 0; - - /* Reset poll tick */ - bus->polltick = 0; - - /* Check device if no interrupts */ - if (!bus->intr || - (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { - - if (!bus->dpc_triggered) { - u8 devpend; - - sdio_claim_host(bus->sdiodev->func[1]); - devpend = brcmf_sdiod_regrb(bus->sdiodev, - SDIO_CCCR_INTx, - NULL); - sdio_release_host(bus->sdiodev->func[1]); - intstatus = devpend & (INTR_STATUS_FUNC1 | - INTR_STATUS_FUNC2); - } - - /* If there is something, make like the ISR and - schedule the DPC */ - if (intstatus) { - bus->sdcnt.pollcnt++; - atomic_set(&bus->ipend, 1); - - bus->dpc_triggered = true; - queue_work(bus->brcmf_wq, &bus->datawork); - } - } - - /* Update interrupt tracking */ - bus->sdcnt.lastintrs = bus->sdcnt.intrcount; - } -#ifdef DEBUG - /* Poll for console output periodically */ - if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() && - bus->console_interval != 0) { - bus->console.count += BRCMF_WD_POLL_MS; - if (bus->console.count >= bus->console_interval) { - bus->console.count -= bus->console_interval; - sdio_claim_host(bus->sdiodev->func[1]); - /* Make sure backplane clock is on */ - brcmf_sdio_bus_sleep(bus, false, false); - if (brcmf_sdio_readconsole(bus) < 0) - /* stop on error */ - bus->console_interval = 0; - sdio_release_host(bus->sdiodev->func[1]); - } - } -#endif /* DEBUG */ - - /* On idle timeout clear activity flag and/or turn off clock */ - if (!bus->dpc_triggered) { - rmb(); - if ((!bus->dpc_running) && (bus->idletime > 0) && - (bus->clkstate == CLK_AVAIL)) { - bus->idlecount++; - if (bus->idlecount > bus->idletime) { - brcmf_dbg(SDIO, "idle\n"); - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_wd_timer(bus, 0); - bus->idlecount = 0; - brcmf_sdio_bus_sleep(bus, true, false); - sdio_release_host(bus->sdiodev->func[1]); - } - } else { - bus->idlecount = 0; - } - } else { - bus->idlecount = 0; - } -} - -static void brcmf_sdio_dataworker(struct work_struct *work) -{ - struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio, - datawork); - - bus->dpc_running = true; - wmb(); - while (ACCESS_ONCE(bus->dpc_triggered)) { - bus->dpc_triggered = false; - brcmf_sdio_dpc(bus); - bus->idlecount = 0; - } - bus->dpc_running = false; - if (brcmf_sdiod_freezing(bus->sdiodev)) { - brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN); - brcmf_sdiod_try_freeze(bus->sdiodev); - brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA); - } -} - -static void -brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u32 drivestrength) -{ - const struct sdiod_drive_str *str_tab = NULL; - u32 str_mask; - u32 str_shift; - u32 base; - u32 i; - u32 drivestrength_sel = 0; - u32 cc_data_temp; - u32 addr; - - if (!(ci->cc_caps & CC_CAP_PMU)) - return; - - switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { - case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12): - str_tab = sdiod_drvstr_tab1_1v8; - str_mask = 0x00003800; - str_shift = 11; - break; - case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17): - str_tab = sdiod_drvstr_tab6_1v8; - str_mask = 0x00001800; - str_shift = 11; - break; - case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17): - /* note: 43143 does not support tristate */ - i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; - if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { - str_tab = sdiod_drvstr_tab2_3v3; - str_mask = 0x00000007; - str_shift = 0; - } else - brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", - ci->name, drivestrength); - break; - case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13): - str_tab = sdiod_drive_strength_tab5_1v8; - str_mask = 0x00003800; - str_shift = 11; - break; - default: - brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", - ci->name, ci->chiprev, ci->pmurev); - break; - } - - if (str_tab != NULL) { - for (i = 0; str_tab[i].strength != 0; i++) { - if (drivestrength >= str_tab[i].strength) { - drivestrength_sel = str_tab[i].sel; - break; - } - } - base = brcmf_chip_get_chipcommon(ci)->base; - addr = CORE_CC_REG(base, chipcontrol_addr); - brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); - cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); - cc_data_temp &= ~str_mask; - drivestrength_sel <<= str_shift; - cc_data_temp |= drivestrength_sel; - brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); - - brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", - str_tab[i].strength, drivestrength, cc_data_temp); - } -} - -static int brcmf_sdio_buscoreprep(void *ctx) -{ - struct brcmf_sdio_dev *sdiodev = ctx; - int err = 0; - u8 clkval, clkset; - - /* Try forcing SDIO core to do ALPAvail request only */ - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); - if (err) { - brcmf_err("error writing for HT off\n"); - return err; - } - - /* If register supported, wait for ALPAvail and then force ALP */ - /* This may take up to 15 milliseconds */ - clkval = brcmf_sdiod_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL); - - if ((clkval & ~SBSDIO_AVBITS) != clkset) { - brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", - clkset, clkval); - return -EACCES; - } - - SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL)), - !SBSDIO_ALPAV(clkval)), - PMU_MAX_TRANSITION_DLY); - if (!SBSDIO_ALPAV(clkval)) { - brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", - clkval); - return -EBUSY; - } - - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); - udelay(65); - - /* Also, disable the extra SDIO pull-ups */ - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); - - return 0; -} - -static void brcmf_sdio_buscore_activate(void *ctx, struct brcmf_chip *chip, - u32 rstvec) -{ - struct brcmf_sdio_dev *sdiodev = ctx; - struct brcmf_core *core; - u32 reg_addr; - - /* clear all interrupts */ - core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV); - reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus); - brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); - - if (rstvec) - /* Write reset vector to address 0 */ - brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, - sizeof(rstvec)); -} - -static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) -{ - struct brcmf_sdio_dev *sdiodev = ctx; - u32 val, rev; - - val = brcmf_sdiod_regrl(sdiodev, addr, NULL); - if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && - addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { - rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; - if (rev >= 2) { - val &= ~CID_ID_MASK; - val |= BRCM_CC_4339_CHIP_ID; - } - } - return val; -} - -static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) -{ - struct brcmf_sdio_dev *sdiodev = ctx; - - brcmf_sdiod_regwl(sdiodev, addr, val, NULL); -} - -static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { - .prepare = brcmf_sdio_buscoreprep, - .activate = brcmf_sdio_buscore_activate, - .read32 = brcmf_sdio_buscore_read32, - .write32 = brcmf_sdio_buscore_write32, -}; - -static bool -brcmf_sdio_probe_attach(struct brcmf_sdio *bus) -{ - u8 clkctl = 0; - int err = 0; - int reg_addr; - u32 reg_val; - u32 drivestrength; - - sdio_claim_host(bus->sdiodev->func[1]); - - pr_debug("F1 signature read @0x18000000=0x%4x\n", - brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); - - /* - * Force PLL off until brcmf_chip_attach() - * programs PLL control regs - */ - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - BRCMF_INIT_CLKCTL1, &err); - if (!err) - clkctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - - if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) { - brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", - err, BRCMF_INIT_CLKCTL1, clkctl); - goto fail; - } - - bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); - if (IS_ERR(bus->ci)) { - brcmf_err("brcmf_chip_attach failed!\n"); - bus->ci = NULL; - goto fail; - } - - if (brcmf_sdio_kso_init(bus)) { - brcmf_err("error enabling KSO\n"); - goto fail; - } - - if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength)) - drivestrength = bus->sdiodev->pdata->drive_strength; - else - drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; - brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); - - /* Set card control so an SDIO card reset does a WLAN backplane reset */ - reg_val = brcmf_sdiod_regrb(bus->sdiodev, - SDIO_CCCR_BRCM_CARDCTRL, &err); - if (err) - goto fail; - - reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET; - - brcmf_sdiod_regwb(bus->sdiodev, - SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); - if (err) - goto fail; - - /* set PMUControl so a backplane reset does PMU state reload */ - reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base, - pmucontrol); - reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err); - if (err) - goto fail; - - reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); - - brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err); - if (err) - goto fail; - - sdio_release_host(bus->sdiodev->func[1]); - - brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); - - /* allocate header buffer */ - bus->hdrbuf = kzalloc(MAX_HDR_READ + bus->head_align, GFP_KERNEL); - if (!bus->hdrbuf) - return false; - /* Locate an appropriately-aligned portion of hdrbuf */ - bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0], - bus->head_align); - - /* Set the poll and/or interrupt flags */ - bus->intr = true; - bus->poll = false; - if (bus->poll) - bus->pollrate = 1; - - return true; - -fail: - sdio_release_host(bus->sdiodev->func[1]); - return false; -} - -static int -brcmf_sdio_watchdog_thread(void *data) -{ - struct brcmf_sdio *bus = (struct brcmf_sdio *)data; - int wait; - - allow_signal(SIGTERM); - /* Run until signal received */ - brcmf_sdiod_freezer_count(bus->sdiodev); - while (1) { - if (kthread_should_stop()) - break; - brcmf_sdiod_freezer_uncount(bus->sdiodev); - wait = wait_for_completion_interruptible(&bus->watchdog_wait); - brcmf_sdiod_freezer_count(bus->sdiodev); - brcmf_sdiod_try_freeze(bus->sdiodev); - if (!wait) { - brcmf_sdio_bus_watchdog(bus); - /* Count the tick for reference */ - bus->sdcnt.tickcnt++; - reinit_completion(&bus->watchdog_wait); - } else - break; - } - return 0; -} - -static void -brcmf_sdio_watchdog(unsigned long data) -{ - struct brcmf_sdio *bus = (struct brcmf_sdio *)data; - - if (bus->watchdog_tsk) { - complete(&bus->watchdog_wait); - /* Reschedule the watchdog */ - if (bus->wd_timer_valid) - mod_timer(&bus->timer, - jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); - } -} - -static struct brcmf_bus_ops brcmf_sdio_bus_ops = { - .stop = brcmf_sdio_bus_stop, - .preinit = brcmf_sdio_bus_preinit, - .txdata = brcmf_sdio_bus_txdata, - .txctl = brcmf_sdio_bus_txctl, - .rxctl = brcmf_sdio_bus_rxctl, - .gettxq = brcmf_sdio_bus_gettxq, - .wowl_config = brcmf_sdio_wowl_config, - .get_ramsize = brcmf_sdio_bus_get_ramsize, - .get_memdump = brcmf_sdio_bus_get_memdump, -}; - -static void brcmf_sdio_firmware_callback(struct device *dev, - const struct firmware *code, - void *nvram, u32 nvram_len) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - int err = 0; - u8 saveclk; - - brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev)); - - if (!bus_if->drvr) - return; - - /* try to download image and nvram to the dongle */ - bus->alp_only = true; - err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); - if (err) - goto fail; - bus->alp_only = false; - - /* Start the watchdog timer */ - bus->sdcnt.tickcnt = 0; - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - - sdio_claim_host(sdiodev->func[1]); - - /* Make sure backplane clock is on, needed to generate F2 interrupt */ - brcmf_sdio_clkctl(bus, CLK_AVAIL, false); - if (bus->clkstate != CLK_AVAIL) - goto release; - - /* Force clocks on backplane to be sure 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); - goto release; - } - - /* Enable function 2 (frame transfers) */ - w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, - offsetof(struct sdpcmd_regs, tosbmailboxdata)); - err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]); - - - brcmf_dbg(INFO, "enable F2: err=%d\n", err); - - /* If F2 successfully enabled, set core and enable interrupts */ - if (!err) { - /* Set up the interrupt mask and enable interrupts */ - bus->hostintmask = HOSTINTMASK; - w_sdreg32(bus, bus->hostintmask, - offsetof(struct sdpcmd_regs, hostintmask)); - - brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err); - } else { - /* Disable F2 again */ - sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); - goto release; - } - - if (brcmf_chip_sr_capable(bus->ci)) { - brcmf_sdio_sr_init(bus); - } else { - /* Restore previous clock setting */ - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - saveclk, &err); - } - - if (err == 0) { - err = brcmf_sdiod_intr_register(sdiodev); - if (err != 0) - brcmf_err("intr register failed:%d\n", err); - } - - /* If we didn't come up, turn off backplane clock */ - if (err != 0) - brcmf_sdio_clkctl(bus, CLK_NONE, false); - - sdio_release_host(sdiodev->func[1]); - - err = brcmf_bus_start(dev); - if (err != 0) { - brcmf_err("dongle is not responding\n"); - goto fail; - } - return; - -release: - sdio_release_host(sdiodev->func[1]); -fail: - brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err); - device_release_driver(dev); -} - -struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) -{ - int ret; - struct brcmf_sdio *bus; - struct workqueue_struct *wq; - - brcmf_dbg(TRACE, "Enter\n"); - - /* Allocate private bus interface state */ - bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC); - if (!bus) - goto fail; - - bus->sdiodev = sdiodev; - sdiodev->bus = bus; - skb_queue_head_init(&bus->glom); - bus->txbound = BRCMF_TXBOUND; - bus->rxbound = BRCMF_RXBOUND; - bus->txminmax = BRCMF_TXMINMAX; - bus->tx_seq = SDPCM_SEQ_WRAP - 1; - - /* platform specific configuration: - * alignments must be at least 4 bytes for ADMA - */ - bus->head_align = ALIGNMENT; - bus->sgentry_align = ALIGNMENT; - if (sdiodev->pdata) { - if (sdiodev->pdata->sd_head_align > ALIGNMENT) - bus->head_align = sdiodev->pdata->sd_head_align; - if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT) - bus->sgentry_align = sdiodev->pdata->sd_sgentry_align; - } - - /* single-threaded workqueue */ - wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM, - dev_name(&sdiodev->func[1]->dev)); - if (!wq) { - brcmf_err("insufficient memory to create txworkqueue\n"); - goto fail; - } - brcmf_sdiod_freezer_count(sdiodev); - INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); - bus->brcmf_wq = wq; - - /* attempt to attach to the dongle */ - if (!(brcmf_sdio_probe_attach(bus))) { - brcmf_err("brcmf_sdio_probe_attach failed\n"); - goto fail; - } - - spin_lock_init(&bus->rxctl_lock); - spin_lock_init(&bus->txq_lock); - init_waitqueue_head(&bus->ctrl_wait); - init_waitqueue_head(&bus->dcmd_resp_wait); - - /* Set up the watchdog timer */ - init_timer(&bus->timer); - bus->timer.data = (unsigned long)bus; - bus->timer.function = brcmf_sdio_watchdog; - - /* Initialize watchdog thread */ - init_completion(&bus->watchdog_wait); - bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread, - bus, "brcmf_wdog/%s", - dev_name(&sdiodev->func[1]->dev)); - if (IS_ERR(bus->watchdog_tsk)) { - pr_warn("brcmf_watchdog thread failed to start\n"); - bus->watchdog_tsk = NULL; - } - /* Initialize DPC thread */ - bus->dpc_triggered = false; - bus->dpc_running = false; - - /* Assign bus interface call back */ - bus->sdiodev->bus_if->dev = bus->sdiodev->dev; - bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops; - bus->sdiodev->bus_if->chip = bus->ci->chip; - bus->sdiodev->bus_if->chiprev = bus->ci->chiprev; - - /* default sdio bus header length for tx packet */ - bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; - - /* Attach to the common layer, reserve hdr space */ - ret = brcmf_attach(bus->sdiodev->dev); - if (ret != 0) { - brcmf_err("brcmf_attach failed\n"); - goto fail; - } - - /* Query the F2 block size, set roundup accordingly */ - bus->blocksize = bus->sdiodev->func[2]->cur_blksize; - bus->roundup = min(max_roundup, bus->blocksize); - - /* Allocate buffers */ - if (bus->sdiodev->bus_if->maxctl) { - bus->sdiodev->bus_if->maxctl += bus->roundup; - bus->rxblen = - roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), - ALIGNMENT) + bus->head_align; - bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC); - if (!(bus->rxbuf)) { - brcmf_err("rxbuf allocation failed\n"); - goto fail; - } - } - - sdio_claim_host(bus->sdiodev->func[1]); - - /* Disable F2 to clear any intermediate frame state on the dongle */ - sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); - - bus->rxflow = false; - - /* Done with backplane-dependent accesses, can drop clock... */ - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); - - sdio_release_host(bus->sdiodev->func[1]); - - /* ...and initialize clock/power states */ - bus->clkstate = CLK_SDONLY; - bus->idletime = BRCMF_IDLE_INTERVAL; - bus->idleclock = BRCMF_IDLE_ACTIVE; - - /* SR state */ - bus->sr_enabled = false; - - brcmf_sdio_debugfs_create(bus); - brcmf_dbg(INFO, "completed!!\n"); - - ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev); - if (ret) - goto fail; - - ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, - sdiodev->fw_name, sdiodev->nvram_name, - brcmf_sdio_firmware_callback); - if (ret != 0) { - brcmf_err("async firmware request failed: %d\n", ret); - goto fail; - } - - return bus; - -fail: - brcmf_sdio_remove(bus); - return NULL; -} - -/* Detach and free everything */ -void brcmf_sdio_remove(struct brcmf_sdio *bus) -{ - brcmf_dbg(TRACE, "Enter\n"); - - if (bus) { - /* De-register interrupt handler */ - brcmf_sdiod_intr_unregister(bus->sdiodev); - - brcmf_detach(bus->sdiodev->dev); - - cancel_work_sync(&bus->datawork); - if (bus->brcmf_wq) - destroy_workqueue(bus->brcmf_wq); - - if (bus->ci) { - if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) { - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_wd_timer(bus, 0); - brcmf_sdio_clkctl(bus, CLK_AVAIL, false); - /* Leave the device in state where it is - * 'passive'. This is done by resetting all - * necessary cores. - */ - msleep(20); - brcmf_chip_set_passive(bus->ci); - brcmf_sdio_clkctl(bus, CLK_NONE, false); - sdio_release_host(bus->sdiodev->func[1]); - } - brcmf_chip_detach(bus->ci); - } - - kfree(bus->rxbuf); - kfree(bus->hdrbuf); - kfree(bus); - } - - brcmf_dbg(TRACE, "Disconnected\n"); -} - -void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) -{ - /* Totally stop the timer */ - if (!wdtick && bus->wd_timer_valid) { - del_timer_sync(&bus->timer); - bus->wd_timer_valid = false; - bus->save_ms = wdtick; - return; - } - - /* don't start the wd until fw is loaded */ - if (bus->sdiodev->state != BRCMF_SDIOD_DATA) - return; - - if (wdtick) { - if (bus->save_ms != BRCMF_WD_POLL_MS) { - if (bus->wd_timer_valid) - /* Stop timer and restart at new value */ - del_timer_sync(&bus->timer); - - /* Create timer again when watchdog period is - dynamically changed or in the first instance - */ - bus->timer.expires = - jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS); - add_timer(&bus->timer); - - } else { - /* Re arm the timer, at last watchdog period */ - mod_timer(&bus->timer, - jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); - } - - bus->wd_timer_valid = true; - bus->save_ms = wdtick; - } -} - -int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep) -{ - int ret; - - sdio_claim_host(bus->sdiodev->func[1]); - ret = brcmf_sdio_bus_sleep(bus, sleep, false); - sdio_release_host(bus->sdiodev->func[1]); - - return ret; -} - diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h deleted file mode 100644 index 7328478b2d7b..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright (c) 2010 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_SDIO_H -#define BRCMFMAC_SDIO_H - -#include -#include -#include "firmware.h" - -#define SDIO_FUNC_0 0 -#define SDIO_FUNC_1 1 -#define SDIO_FUNC_2 2 - -#define SDIOD_FBR_SIZE 0x100 - -/* io_en */ -#define SDIO_FUNC_ENABLE_1 0x02 -#define SDIO_FUNC_ENABLE_2 0x04 - -/* io_rdys */ -#define SDIO_FUNC_READY_1 0x02 -#define SDIO_FUNC_READY_2 0x04 - -/* intr_status */ -#define INTR_STATUS_FUNC1 0x2 -#define INTR_STATUS_FUNC2 0x4 - -/* Maximum number of I/O funcs */ -#define SDIOD_MAX_IOFUNCS 7 - -/* mask of register map */ -#define REG_F0_REG_MASK 0x7FF -#define REG_F1_MISC_MASK 0x1FFFF - -/* as of sdiod rev 0, supports 3 functions */ -#define SBSDIO_NUM_FUNCTION 3 - -/* function 0 vendor specific CCCR registers */ -#define SDIO_CCCR_BRCM_CARDCAP 0xf0 -#define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02 -#define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04 -#define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08 -#define SDIO_CCCR_BRCM_CARDCTRL 0xf1 -#define SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02 -#define SDIO_CCCR_BRCM_SEPINT 0xf2 - -#define SDIO_SEPINT_MASK 0x01 -#define SDIO_SEPINT_OE 0x02 -#define SDIO_SEPINT_ACT_HI 0x04 - -/* function 1 miscellaneous registers */ - -/* sprom command and status */ -#define SBSDIO_SPROM_CS 0x10000 -/* sprom info register */ -#define SBSDIO_SPROM_INFO 0x10001 -/* sprom indirect access data byte 0 */ -#define SBSDIO_SPROM_DATA_LOW 0x10002 -/* sprom indirect access data byte 1 */ -#define SBSDIO_SPROM_DATA_HIGH 0x10003 -/* sprom indirect access addr byte 0 */ -#define SBSDIO_SPROM_ADDR_LOW 0x10004 -/* gpio select */ -#define SBSDIO_GPIO_SELECT 0x10005 -/* gpio output */ -#define SBSDIO_GPIO_OUT 0x10006 -/* gpio enable */ -#define SBSDIO_GPIO_EN 0x10007 -/* rev < 7, watermark for sdio device */ -#define SBSDIO_WATERMARK 0x10008 -/* control busy signal generation */ -#define SBSDIO_DEVICE_CTL 0x10009 - -/* SB Address Window Low (b15) */ -#define SBSDIO_FUNC1_SBADDRLOW 0x1000A -/* SB Address Window Mid (b23:b16) */ -#define SBSDIO_FUNC1_SBADDRMID 0x1000B -/* SB Address Window High (b31:b24) */ -#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C -/* Frame Control (frame term/abort) */ -#define SBSDIO_FUNC1_FRAMECTRL 0x1000D -/* ChipClockCSR (ALP/HT ctl/status) */ -#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E -/* SdioPullUp (on cmd, d0-d2) */ -#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F -/* Write Frame Byte Count Low */ -#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019 -/* Write Frame Byte Count High */ -#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A -/* Read Frame Byte Count Low */ -#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B -/* Read Frame Byte Count High */ -#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C -/* MesBusyCtl (rev 11) */ -#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D -/* Sdio Core Rev 12 */ -#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E -#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1 -#define SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT 0 -#define SBSDIO_FUNC1_WCTRL_HTWAIT_MASK 0x2 -#define SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT 1 -#define SBSDIO_FUNC1_SLEEPCSR 0x1001F -#define SBSDIO_FUNC1_SLEEPCSR_KSO_MASK 0x1 -#define SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT 0 -#define SBSDIO_FUNC1_SLEEPCSR_KSO_EN 1 -#define SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK 0x2 -#define SBSDIO_FUNC1_SLEEPCSR_DEVON_SHIFT 1 - -#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */ -#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001F /* f1 misc register end */ - -/* function 1 OCP space */ - -/* sb offset addr is <= 15 bits, 32k */ -#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF -#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000 -/* with b15, maps to 32-bit SB access */ -#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000 - -/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */ - -#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */ -#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */ -#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */ -/* Address bits from SBADDR regs */ -#define SBSDIO_SBWINDOW_MASK 0xffff8000 - -#define SDIOH_READ 0 /* Read request */ -#define SDIOH_WRITE 1 /* Write request */ - -#define SDIOH_DATA_FIX 0 /* Fixed addressing */ -#define SDIOH_DATA_INC 1 /* Incremental addressing */ - -/* internal return code */ -#define SUCCESS 0 -#define ERROR 1 - -/* Packet alignment for most efficient SDIO (can change based on platform) */ -#define BRCMF_SDALIGN (1 << 6) - -/* watchdog polling interval in ms */ -#define BRCMF_WD_POLL_MS 10 - -/** - * enum brcmf_sdiod_state - the state of the bus. - * - * @BRCMF_SDIOD_DOWN: Device can be accessed, no DPC. - * @BRCMF_SDIOD_DATA: Ready for data transfers, DPC enabled. - * @BRCMF_SDIOD_NOMEDIUM: No medium access to dongle possible. - */ -enum brcmf_sdiod_state { - BRCMF_SDIOD_DOWN, - BRCMF_SDIOD_DATA, - BRCMF_SDIOD_NOMEDIUM -}; - -struct brcmf_sdreg { - int func; - int offset; - int value; -}; - -struct brcmf_sdio; -struct brcmf_sdiod_freezer; - -struct brcmf_sdio_dev { - struct sdio_func *func[SDIO_MAX_FUNCS]; - u8 num_funcs; /* Supported funcs on client */ - u32 sbwad; /* Save backplane window address */ - struct brcmf_sdio *bus; - struct device *dev; - struct brcmf_bus *bus_if; - struct brcmfmac_sdio_platform_data *pdata; - bool oob_irq_requested; - bool irq_en; /* irq enable flags */ - spinlock_t irq_en_lock; - bool irq_wake; /* irq wake enable flags */ - bool sg_support; - uint max_request_size; - ushort max_segment_count; - uint max_segment_size; - uint txglomsz; - struct sg_table sgtable; - char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; - char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; - bool wowl_enabled; - enum brcmf_sdiod_state state; - struct brcmf_sdiod_freezer *freezer; -}; - -/* sdio core registers */ -struct sdpcmd_regs { - u32 corecontrol; /* 0x00, rev8 */ - u32 corestatus; /* rev8 */ - u32 PAD[1]; - u32 biststatus; /* rev8 */ - - /* PCMCIA access */ - u16 pcmciamesportaladdr; /* 0x010, rev8 */ - u16 PAD[1]; - u16 pcmciamesportalmask; /* rev8 */ - u16 PAD[1]; - u16 pcmciawrframebc; /* rev8 */ - u16 PAD[1]; - u16 pcmciaunderflowtimer; /* rev8 */ - u16 PAD[1]; - - /* interrupt */ - u32 intstatus; /* 0x020, rev8 */ - u32 hostintmask; /* rev8 */ - u32 intmask; /* rev8 */ - u32 sbintstatus; /* rev8 */ - u32 sbintmask; /* rev8 */ - u32 funcintmask; /* rev4 */ - u32 PAD[2]; - u32 tosbmailbox; /* 0x040, rev8 */ - u32 tohostmailbox; /* rev8 */ - u32 tosbmailboxdata; /* rev8 */ - u32 tohostmailboxdata; /* rev8 */ - - /* synchronized access to registers in SDIO clock domain */ - u32 sdioaccess; /* 0x050, rev8 */ - u32 PAD[3]; - - /* PCMCIA frame control */ - u8 pcmciaframectrl; /* 0x060, rev8 */ - u8 PAD[3]; - u8 pcmciawatermark; /* rev8 */ - u8 PAD[155]; - - /* interrupt batching control */ - u32 intrcvlazy; /* 0x100, rev8 */ - u32 PAD[3]; - - /* counters */ - u32 cmd52rd; /* 0x110, rev8 */ - u32 cmd52wr; /* rev8 */ - u32 cmd53rd; /* rev8 */ - u32 cmd53wr; /* rev8 */ - u32 abort; /* rev8 */ - u32 datacrcerror; /* rev8 */ - u32 rdoutofsync; /* rev8 */ - u32 wroutofsync; /* rev8 */ - u32 writebusy; /* rev8 */ - u32 readwait; /* rev8 */ - u32 readterm; /* rev8 */ - u32 writeterm; /* rev8 */ - u32 PAD[40]; - u32 clockctlstatus; /* rev8 */ - u32 PAD[7]; - - u32 PAD[128]; /* DMA engines */ - - /* SDIO/PCMCIA CIS region */ - char cis[512]; /* 0x400-0x5ff, rev6 */ - - /* PCMCIA function control registers */ - char pcmciafcr[256]; /* 0x600-6ff, rev6 */ - u16 PAD[55]; - - /* PCMCIA backplane access */ - u16 backplanecsr; /* 0x76E, rev6 */ - u16 backplaneaddr0; /* rev6 */ - u16 backplaneaddr1; /* rev6 */ - u16 backplaneaddr2; /* rev6 */ - u16 backplaneaddr3; /* rev6 */ - u16 backplanedata0; /* rev6 */ - u16 backplanedata1; /* rev6 */ - u16 backplanedata2; /* rev6 */ - u16 backplanedata3; /* rev6 */ - u16 PAD[31]; - - /* sprom "size" & "blank" info */ - u16 spromstatus; /* 0x7BE, rev2 */ - u32 PAD[464]; - - u16 PAD[0x80]; -}; - -/* Register/deregister interrupt handler. */ -int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev); -int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev); - -/* sdio device register access interface */ -u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); -u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); -void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, - int *ret); -void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, - int *ret); - -/* Buffer transfer to/from device (client) core via cmd53. - * fn: function number - * flags: backplane width, address increment, sync/async - * buf: pointer to memory data buffer - * nbytes: number of bytes to transfer to/from buf - * pkt: pointer to packet associated with buf (if any) - * complete: callback function for command completion (async only) - * handle: handle for completion callback (first arg in callback) - * Returns 0 or error code. - * NOTE: Async operation is not currently supported. - */ -int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev, - struct sk_buff_head *pktq); -int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); - -int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt); -int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); -int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, - struct sk_buff_head *pktq, uint totlen); - -/* Flags bits */ - -/* Four-byte target (backplane) width (vs. two-byte) */ -#define SDIO_REQ_4BYTE 0x1 -/* Fixed address (FIFO) (vs. incrementing address) */ -#define SDIO_REQ_FIXED 0x2 - -/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only). - * rw: read or write (0/1) - * addr: direct SDIO address - * buf: pointer to memory data buffer - * nbytes: number of bytes to transfer to/from buf - * Returns 0 or error code. - */ -int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, - u8 *data, uint size); - -/* Issue an abort to the specified function */ -int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn); -void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev, - enum brcmf_sdiod_state state); -#ifdef CONFIG_PM_SLEEP -bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev); -void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev); -void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev); -void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev); -#else -static inline bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev) -{ - return false; -} -static inline void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev) -{ -} -static inline void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev) -{ -} -static inline void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev) -{ -} -#endif /* CONFIG_PM_SLEEP */ - -struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); -void brcmf_sdio_remove(struct brcmf_sdio *bus); -void brcmf_sdio_isr(struct brcmf_sdio *bus); - -void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); -void brcmf_sdio_wowl_config(struct device *dev, bool enabled); -int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep); -void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus); - -#endif /* BRCMFMAC_SDIO_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c deleted file mode 100644 index a10f35c5eb3d..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2012 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 /* bug in tracepoint.h, it should include this */ - -#ifndef __CHECKER__ -#define CREATE_TRACE_POINTS -#include "tracepoint.h" - -void __brcmf_err(const char *func, const char *fmt, ...) -{ - struct va_format vaf = { - .fmt = fmt, - }; - va_list args; - - va_start(args, fmt); - vaf.va = &args; - pr_err("%s: %pV", func, &vaf); - trace_brcmf_err(func, &vaf); - va_end(args); -} - -#endif diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h deleted file mode 100644 index 4d7d51f95716..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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. - */ -#if !defined(BRCMF_TRACEPOINT_H_) || defined(TRACE_HEADER_MULTI_READ) -#define BRCMF_TRACEPOINT_H_ - -#include -#include - -#ifndef CONFIG_BRCM_TRACING - -#undef TRACE_EVENT -#define TRACE_EVENT(name, proto, ...) \ -static inline void trace_ ## name(proto) {} - -#undef DECLARE_EVENT_CLASS -#define DECLARE_EVENT_CLASS(...) - -#undef DEFINE_EVENT -#define DEFINE_EVENT(evt_class, name, proto, ...) \ -static inline void trace_ ## name(proto) {} - -#endif /* CONFIG_BRCM_TRACING */ - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM brcmfmac - -#define MAX_MSG_LEN 100 - -TRACE_EVENT(brcmf_err, - TP_PROTO(const char *func, struct va_format *vaf), - TP_ARGS(func, vaf), - TP_STRUCT__entry( - __string(func, func) - __dynamic_array(char, msg, MAX_MSG_LEN) - ), - TP_fast_assign( - __assign_str(func, func); - WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), - MAX_MSG_LEN, vaf->fmt, - *vaf->va) >= MAX_MSG_LEN); - ), - TP_printk("%s: %s", __get_str(func), __get_str(msg)) -); - -TRACE_EVENT(brcmf_dbg, - TP_PROTO(u32 level, const char *func, struct va_format *vaf), - TP_ARGS(level, func, vaf), - TP_STRUCT__entry( - __field(u32, level) - __string(func, func) - __dynamic_array(char, msg, MAX_MSG_LEN) - ), - TP_fast_assign( - __entry->level = level; - __assign_str(func, func); - WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), - MAX_MSG_LEN, vaf->fmt, - *vaf->va) >= MAX_MSG_LEN); - ), - TP_printk("%s: %s", __get_str(func), __get_str(msg)) -); - -TRACE_EVENT(brcmf_hexdump, - TP_PROTO(void *data, size_t len), - TP_ARGS(data, len), - TP_STRUCT__entry( - __field(unsigned long, len) - __field(unsigned long, addr) - __dynamic_array(u8, hdata, len) - ), - TP_fast_assign( - __entry->len = len; - __entry->addr = (unsigned long)data; - memcpy(__get_dynamic_array(hdata), data, len); - ), - TP_printk("hexdump [addr=%lx, length=%lu]", __entry->addr, __entry->len) -); - -TRACE_EVENT(brcmf_bcdchdr, - TP_PROTO(void *data), - TP_ARGS(data), - TP_STRUCT__entry( - __field(u8, flags) - __field(u8, prio) - __field(u8, flags2) - __field(u32, siglen) - __dynamic_array(u8, signal, *((u8 *)data + 3) * 4) - ), - TP_fast_assign( - __entry->flags = *(u8 *)data; - __entry->prio = *((u8 *)data + 1); - __entry->flags2 = *((u8 *)data + 2); - __entry->siglen = *((u8 *)data + 3) * 4; - memcpy(__get_dynamic_array(signal), - (u8 *)data + 4, __entry->siglen); - ), - TP_printk("bcdc: prio=%d siglen=%d", __entry->prio, __entry->siglen) -); - -#ifndef SDPCM_RX -#define SDPCM_RX 0 -#endif -#ifndef SDPCM_TX -#define SDPCM_TX 1 -#endif -#ifndef SDPCM_GLOM -#define SDPCM_GLOM 2 -#endif - -TRACE_EVENT(brcmf_sdpcm_hdr, - TP_PROTO(u8 dir, void *data), - TP_ARGS(dir, data), - TP_STRUCT__entry( - __field(u8, dir) - __field(u16, len) - __dynamic_array(u8, hdr, dir == SDPCM_GLOM ? 20 : 12) - ), - TP_fast_assign( - memcpy(__get_dynamic_array(hdr), data, dir == SDPCM_GLOM ? 20 : 12); - __entry->len = *(u8 *)data | (*((u8 *)data + 1) << 8); - __entry->dir = dir; - ), - TP_printk("sdpcm: %s len %u, seq %d", - __entry->dir == SDPCM_RX ? "RX" : "TX", - __entry->len, ((u8 *)__get_dynamic_array(hdr))[4]) -); - -#ifdef CONFIG_BRCM_TRACING - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE tracepoint - -#include - -#endif /* CONFIG_BRCM_TRACING */ - -#endif /* BRCMF_TRACEPOINT_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c deleted file mode 100644 index 689e64d004bc..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ /dev/null @@ -1,1535 +0,0 @@ -/* - * Copyright (c) 2011 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 -#include - -#include -#include -#include -#include "bus.h" -#include "debug.h" -#include "firmware.h" -#include "usb.h" - - -#define IOCTL_RESP_TIMEOUT 2000 - -#define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */ -#define BRCMF_USB_RESET_GETVER_LOOP_CNT 10 - -#define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle - has boot up */ -#define BRCMF_USB_NRXQ 50 -#define BRCMF_USB_NTXQ 50 - -#define BRCMF_USB_CBCTL_WRITE 0 -#define BRCMF_USB_CBCTL_READ 1 -#define BRCMF_USB_MAX_PKT_SIZE 1600 - -#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin" -#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" -#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" -#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin" - -#define TRX_MAGIC 0x30524448 /* "HDR0" */ -#define TRX_MAX_OFFSET 3 /* Max number of file offsets */ -#define TRX_UNCOMP_IMAGE 0x20 /* Trx holds uncompressed img */ -#define TRX_RDL_CHUNK 1500 /* size of each dl transfer */ -#define TRX_OFFSETS_DLFWLEN_IDX 0 - -/* Control messages: bRequest values */ -#define DL_GETSTATE 0 /* returns the rdl_state_t struct */ -#define DL_CHECK_CRC 1 /* currently unused */ -#define DL_GO 2 /* execute downloaded image */ -#define DL_START 3 /* initialize dl state */ -#define DL_REBOOT 4 /* reboot the device in 2 seconds */ -#define DL_GETVER 5 /* returns the bootrom_id_t struct */ -#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset - * event to occur in 2 seconds. It is the - * responsibility of the downloaded code to - * clear this event - */ -#define DL_EXEC 7 /* jump to a supplied address */ -#define DL_RESETCFG 8 /* To support single enum on dongle - * - Not used by bootloader - */ -#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup - * if resp unavailable - */ - -/* states */ -#define DL_WAITING 0 /* waiting to rx first pkt */ -#define DL_READY 1 /* hdr was good, waiting for more of the - * compressed image - */ -#define DL_BAD_HDR 2 /* hdr was corrupted */ -#define DL_BAD_CRC 3 /* compressed image was corrupted */ -#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */ -#define DL_START_FAIL 5 /* failed to initialize correctly */ -#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM - * value - */ -#define DL_IMAGE_TOOBIG 7 /* firmware image too big */ - - -struct trx_header_le { - __le32 magic; /* "HDR0" */ - __le32 len; /* Length of file including header */ - __le32 crc32; /* CRC from flag_version to end of file */ - __le32 flag_version; /* 0:15 flags, 16:31 version */ - __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of - * header - */ -}; - -struct rdl_state_le { - __le32 state; - __le32 bytes; -}; - -struct bootrom_id_le { - __le32 chip; /* Chip id */ - __le32 chiprev; /* Chip rev */ - __le32 ramsize; /* Size of RAM */ - __le32 remapbase; /* Current remap base address */ - __le32 boardtype; /* Type of board */ - __le32 boardrev; /* Board revision */ -}; - -struct brcmf_usb_image { - struct list_head list; - s8 *fwname; - u8 *image; - int image_len; -}; - -struct brcmf_usbdev_info { - struct brcmf_usbdev bus_pub; /* MUST BE FIRST */ - spinlock_t qlock; - struct list_head rx_freeq; - struct list_head rx_postq; - struct list_head tx_freeq; - struct list_head tx_postq; - uint rx_pipe, tx_pipe; - - int rx_low_watermark; - int tx_low_watermark; - int tx_high_watermark; - int tx_freecount; - bool tx_flowblock; - spinlock_t tx_flowblock_lock; - - struct brcmf_usbreq *tx_reqs; - struct brcmf_usbreq *rx_reqs; - - const u8 *image; /* buffer for combine fw and nvram */ - int image_len; - - struct usb_device *usbdev; - struct device *dev; - struct mutex dev_init_lock; - - int ctl_in_pipe, ctl_out_pipe; - struct urb *ctl_urb; /* URB for control endpoint */ - struct usb_ctrlrequest ctl_write; - struct usb_ctrlrequest ctl_read; - u32 ctl_urb_actual_length; - int ctl_urb_status; - int ctl_completed; - wait_queue_head_t ioctl_resp_wait; - ulong ctl_op; - u8 ifnum; - - struct urb *bulk_urb; /* used for FW download */ - - bool wowl_enabled; -}; - -static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, - struct brcmf_usbreq *req); - -static struct brcmf_usbdev *brcmf_usb_get_buspub(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - return bus_if->bus_priv.usb; -} - -static struct brcmf_usbdev_info *brcmf_usb_get_businfo(struct device *dev) -{ - return brcmf_usb_get_buspub(dev)->devinfo; -} - -static int brcmf_usb_ioctl_resp_wait(struct brcmf_usbdev_info *devinfo) -{ - return wait_event_timeout(devinfo->ioctl_resp_wait, - devinfo->ctl_completed, - msecs_to_jiffies(IOCTL_RESP_TIMEOUT)); -} - -static void brcmf_usb_ioctl_resp_wake(struct brcmf_usbdev_info *devinfo) -{ - if (waitqueue_active(&devinfo->ioctl_resp_wait)) - wake_up(&devinfo->ioctl_resp_wait); -} - -static void -brcmf_usb_ctl_complete(struct brcmf_usbdev_info *devinfo, int type, int status) -{ - brcmf_dbg(USB, "Enter, status=%d\n", status); - - if (unlikely(devinfo == NULL)) - return; - - if (type == BRCMF_USB_CBCTL_READ) { - if (status == 0) - devinfo->bus_pub.stats.rx_ctlpkts++; - else - devinfo->bus_pub.stats.rx_ctlerrs++; - } else if (type == BRCMF_USB_CBCTL_WRITE) { - if (status == 0) - devinfo->bus_pub.stats.tx_ctlpkts++; - else - devinfo->bus_pub.stats.tx_ctlerrs++; - } - - devinfo->ctl_urb_status = status; - devinfo->ctl_completed = true; - brcmf_usb_ioctl_resp_wake(devinfo); -} - -static void -brcmf_usb_ctlread_complete(struct urb *urb) -{ - struct brcmf_usbdev_info *devinfo = - (struct brcmf_usbdev_info *)urb->context; - - brcmf_dbg(USB, "Enter\n"); - devinfo->ctl_urb_actual_length = urb->actual_length; - brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_READ, - urb->status); -} - -static void -brcmf_usb_ctlwrite_complete(struct urb *urb) -{ - struct brcmf_usbdev_info *devinfo = - (struct brcmf_usbdev_info *)urb->context; - - brcmf_dbg(USB, "Enter\n"); - brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_WRITE, - urb->status); -} - -static int -brcmf_usb_send_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len) -{ - int ret; - u16 size; - - brcmf_dbg(USB, "Enter\n"); - if (devinfo == NULL || buf == NULL || - len == 0 || devinfo->ctl_urb == NULL) - return -EINVAL; - - size = len; - devinfo->ctl_write.wLength = cpu_to_le16p(&size); - devinfo->ctl_urb->transfer_buffer_length = size; - devinfo->ctl_urb_status = 0; - devinfo->ctl_urb_actual_length = 0; - - usb_fill_control_urb(devinfo->ctl_urb, - devinfo->usbdev, - devinfo->ctl_out_pipe, - (unsigned char *) &devinfo->ctl_write, - buf, size, - (usb_complete_t)brcmf_usb_ctlwrite_complete, - devinfo); - - ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); - if (ret < 0) - brcmf_err("usb_submit_urb failed %d\n", ret); - - return ret; -} - -static int -brcmf_usb_recv_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len) -{ - int ret; - u16 size; - - brcmf_dbg(USB, "Enter\n"); - if ((devinfo == NULL) || (buf == NULL) || (len == 0) - || (devinfo->ctl_urb == NULL)) - return -EINVAL; - - size = len; - devinfo->ctl_read.wLength = cpu_to_le16p(&size); - devinfo->ctl_urb->transfer_buffer_length = size; - - devinfo->ctl_read.bRequestType = USB_DIR_IN - | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - devinfo->ctl_read.bRequest = 1; - - usb_fill_control_urb(devinfo->ctl_urb, - devinfo->usbdev, - devinfo->ctl_in_pipe, - (unsigned char *) &devinfo->ctl_read, - buf, size, - (usb_complete_t)brcmf_usb_ctlread_complete, - devinfo); - - ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); - if (ret < 0) - brcmf_err("usb_submit_urb failed %d\n", ret); - - return ret; -} - -static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len) -{ - int err = 0; - int timeout = 0; - struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); - - brcmf_dbg(USB, "Enter\n"); - if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) - return -EIO; - - if (test_and_set_bit(0, &devinfo->ctl_op)) - return -EIO; - - devinfo->ctl_completed = false; - err = brcmf_usb_send_ctl(devinfo, buf, len); - if (err) { - brcmf_err("fail %d bytes: %d\n", err, len); - clear_bit(0, &devinfo->ctl_op); - return err; - } - timeout = brcmf_usb_ioctl_resp_wait(devinfo); - clear_bit(0, &devinfo->ctl_op); - if (!timeout) { - brcmf_err("Txctl wait timed out\n"); - err = -EIO; - } - return err; -} - -static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len) -{ - int err = 0; - int timeout = 0; - struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); - - brcmf_dbg(USB, "Enter\n"); - if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) - return -EIO; - - if (test_and_set_bit(0, &devinfo->ctl_op)) - return -EIO; - - devinfo->ctl_completed = false; - err = brcmf_usb_recv_ctl(devinfo, buf, len); - if (err) { - brcmf_err("fail %d bytes: %d\n", err, len); - clear_bit(0, &devinfo->ctl_op); - return err; - } - timeout = brcmf_usb_ioctl_resp_wait(devinfo); - err = devinfo->ctl_urb_status; - clear_bit(0, &devinfo->ctl_op); - if (!timeout) { - brcmf_err("rxctl wait timed out\n"); - err = -EIO; - } - if (!err) - return devinfo->ctl_urb_actual_length; - else - return err; -} - -static struct brcmf_usbreq *brcmf_usb_deq(struct brcmf_usbdev_info *devinfo, - struct list_head *q, int *counter) -{ - unsigned long flags; - struct brcmf_usbreq *req; - spin_lock_irqsave(&devinfo->qlock, flags); - if (list_empty(q)) { - spin_unlock_irqrestore(&devinfo->qlock, flags); - return NULL; - } - req = list_entry(q->next, struct brcmf_usbreq, list); - list_del_init(q->next); - if (counter) - (*counter)--; - spin_unlock_irqrestore(&devinfo->qlock, flags); - return req; - -} - -static void brcmf_usb_enq(struct brcmf_usbdev_info *devinfo, - struct list_head *q, struct brcmf_usbreq *req, - int *counter) -{ - unsigned long flags; - spin_lock_irqsave(&devinfo->qlock, flags); - list_add_tail(&req->list, q); - if (counter) - (*counter)++; - spin_unlock_irqrestore(&devinfo->qlock, flags); -} - -static struct brcmf_usbreq * -brcmf_usbdev_qinit(struct list_head *q, int qsize) -{ - int i; - struct brcmf_usbreq *req, *reqs; - - reqs = kcalloc(qsize, sizeof(struct brcmf_usbreq), GFP_ATOMIC); - if (reqs == NULL) - return NULL; - - req = reqs; - - for (i = 0; i < qsize; i++) { - req->urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!req->urb) - goto fail; - - INIT_LIST_HEAD(&req->list); - list_add_tail(&req->list, q); - req++; - } - return reqs; -fail: - brcmf_err("fail!\n"); - while (!list_empty(q)) { - req = list_entry(q->next, struct brcmf_usbreq, list); - if (req) - usb_free_urb(req->urb); - list_del(q->next); - } - return NULL; - -} - -static void brcmf_usb_free_q(struct list_head *q, bool pending) -{ - struct brcmf_usbreq *req, *next; - int i = 0; - list_for_each_entry_safe(req, next, q, list) { - if (!req->urb) { - brcmf_err("bad req\n"); - break; - } - i++; - if (pending) { - usb_kill_urb(req->urb); - } else { - usb_free_urb(req->urb); - list_del_init(&req->list); - } - } -} - -static void brcmf_usb_del_fromq(struct brcmf_usbdev_info *devinfo, - struct brcmf_usbreq *req) -{ - unsigned long flags; - - spin_lock_irqsave(&devinfo->qlock, flags); - list_del_init(&req->list); - spin_unlock_irqrestore(&devinfo->qlock, flags); -} - - -static void brcmf_usb_tx_complete(struct urb *urb) -{ - struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; - struct brcmf_usbdev_info *devinfo = req->devinfo; - unsigned long flags; - - brcmf_dbg(USB, "Enter, urb->status=%d, skb=%p\n", urb->status, - req->skb); - brcmf_usb_del_fromq(devinfo, req); - - brcmf_txcomplete(devinfo->dev, req->skb, urb->status == 0); - req->skb = NULL; - brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount); - spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags); - if (devinfo->tx_freecount > devinfo->tx_high_watermark && - devinfo->tx_flowblock) { - brcmf_txflowblock(devinfo->dev, false); - devinfo->tx_flowblock = false; - } - spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags); -} - -static void brcmf_usb_rx_complete(struct urb *urb) -{ - struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; - struct brcmf_usbdev_info *devinfo = req->devinfo; - struct sk_buff *skb; - - brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); - brcmf_usb_del_fromq(devinfo, req); - skb = req->skb; - req->skb = NULL; - - /* zero lenght packets indicate usb "failure". Do not refill */ - if (urb->status != 0 || !urb->actual_length) { - brcmu_pkt_buf_free_skb(skb); - brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); - return; - } - - if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { - skb_put(skb, urb->actual_length); - brcmf_rx_frame(devinfo->dev, skb); - brcmf_usb_rx_refill(devinfo, req); - } else { - brcmu_pkt_buf_free_skb(skb); - brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); - } - return; - -} - -static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, - struct brcmf_usbreq *req) -{ - struct sk_buff *skb; - int ret; - - if (!req || !devinfo) - return; - - skb = dev_alloc_skb(devinfo->bus_pub.bus_mtu); - if (!skb) { - brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); - return; - } - req->skb = skb; - - usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->rx_pipe, - skb->data, skb_tailroom(skb), brcmf_usb_rx_complete, - req); - req->devinfo = devinfo; - brcmf_usb_enq(devinfo, &devinfo->rx_postq, req, NULL); - - ret = usb_submit_urb(req->urb, GFP_ATOMIC); - if (ret) { - brcmf_usb_del_fromq(devinfo, req); - brcmu_pkt_buf_free_skb(req->skb); - req->skb = NULL; - brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); - } - return; -} - -static void brcmf_usb_rx_fill_all(struct brcmf_usbdev_info *devinfo) -{ - struct brcmf_usbreq *req; - - if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) { - brcmf_err("bus is not up=%d\n", devinfo->bus_pub.state); - return; - } - while ((req = brcmf_usb_deq(devinfo, &devinfo->rx_freeq, NULL)) != NULL) - brcmf_usb_rx_refill(devinfo, req); -} - -static void -brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state) -{ - struct brcmf_bus *bcmf_bus = devinfo->bus_pub.bus; - int old_state; - - brcmf_dbg(USB, "Enter, current state=%d, new state=%d\n", - devinfo->bus_pub.state, state); - - if (devinfo->bus_pub.state == state) - return; - - old_state = devinfo->bus_pub.state; - devinfo->bus_pub.state = state; - - /* update state of upper layer */ - if (state == BRCMFMAC_USB_STATE_DOWN) { - brcmf_dbg(USB, "DBUS is down\n"); - brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DOWN); - } else if (state == BRCMFMAC_USB_STATE_UP) { - brcmf_dbg(USB, "DBUS is up\n"); - brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_UP); - } else { - brcmf_dbg(USB, "DBUS current state=%d\n", state); - } -} - -static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) -{ - struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); - struct brcmf_usbreq *req; - int ret; - unsigned long flags; - - brcmf_dbg(USB, "Enter, skb=%p\n", skb); - if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) { - ret = -EIO; - goto fail; - } - - req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq, - &devinfo->tx_freecount); - if (!req) { - brcmf_err("no req to send\n"); - ret = -ENOMEM; - goto fail; - } - - req->skb = skb; - req->devinfo = devinfo; - usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe, - skb->data, skb->len, brcmf_usb_tx_complete, req); - req->urb->transfer_flags |= URB_ZERO_PACKET; - brcmf_usb_enq(devinfo, &devinfo->tx_postq, req, NULL); - ret = usb_submit_urb(req->urb, GFP_ATOMIC); - if (ret) { - brcmf_err("brcmf_usb_tx usb_submit_urb FAILED\n"); - brcmf_usb_del_fromq(devinfo, req); - req->skb = NULL; - brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, - &devinfo->tx_freecount); - goto fail; - } - - spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags); - if (devinfo->tx_freecount < devinfo->tx_low_watermark && - !devinfo->tx_flowblock) { - brcmf_txflowblock(dev, true); - devinfo->tx_flowblock = true; - } - spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags); - return 0; - -fail: - return ret; -} - - -static int brcmf_usb_up(struct device *dev) -{ - struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); - - brcmf_dbg(USB, "Enter\n"); - if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) - return 0; - - /* Success, indicate devinfo is fully up */ - brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP); - - if (devinfo->ctl_urb) { - devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0); - devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0); - - /* CTL Write */ - devinfo->ctl_write.bRequestType = - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - devinfo->ctl_write.bRequest = 0; - devinfo->ctl_write.wValue = cpu_to_le16(0); - devinfo->ctl_write.wIndex = cpu_to_le16(devinfo->ifnum); - - /* CTL Read */ - devinfo->ctl_read.bRequestType = - USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - devinfo->ctl_read.bRequest = 1; - devinfo->ctl_read.wValue = cpu_to_le16(0); - devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum); - } - brcmf_usb_rx_fill_all(devinfo); - return 0; -} - -static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo) -{ - if (devinfo->ctl_urb) - usb_kill_urb(devinfo->ctl_urb); - if (devinfo->bulk_urb) - usb_kill_urb(devinfo->bulk_urb); - brcmf_usb_free_q(&devinfo->tx_postq, true); - brcmf_usb_free_q(&devinfo->rx_postq, true); -} - -static void brcmf_usb_down(struct device *dev) -{ - struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); - - brcmf_dbg(USB, "Enter\n"); - if (devinfo == NULL) - return; - - if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN) - return; - - brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN); - - brcmf_cancel_all_urbs(devinfo); -} - -static void -brcmf_usb_sync_complete(struct urb *urb) -{ - struct brcmf_usbdev_info *devinfo = - (struct brcmf_usbdev_info *)urb->context; - - devinfo->ctl_completed = true; - brcmf_usb_ioctl_resp_wake(devinfo); -} - -static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, - void *buffer, int buflen) -{ - int ret; - char *tmpbuf; - u16 size; - - if ((!devinfo) || (devinfo->ctl_urb == NULL)) - return -EINVAL; - - tmpbuf = kmalloc(buflen, GFP_ATOMIC); - if (!tmpbuf) - return -ENOMEM; - - size = buflen; - devinfo->ctl_urb->transfer_buffer_length = size; - - devinfo->ctl_read.wLength = cpu_to_le16p(&size); - devinfo->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_VENDOR | - USB_RECIP_INTERFACE; - devinfo->ctl_read.bRequest = cmd; - - usb_fill_control_urb(devinfo->ctl_urb, - devinfo->usbdev, - usb_rcvctrlpipe(devinfo->usbdev, 0), - (unsigned char *) &devinfo->ctl_read, - (void *) tmpbuf, size, - (usb_complete_t)brcmf_usb_sync_complete, devinfo); - - devinfo->ctl_completed = false; - ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); - if (ret < 0) { - brcmf_err("usb_submit_urb failed %d\n", ret); - goto finalize; - } - - if (!brcmf_usb_ioctl_resp_wait(devinfo)) { - usb_kill_urb(devinfo->ctl_urb); - ret = -ETIMEDOUT; - } else { - memcpy(buffer, tmpbuf, buflen); - } - -finalize: - kfree(tmpbuf); - return ret; -} - -static bool -brcmf_usb_dlneeded(struct brcmf_usbdev_info *devinfo) -{ - struct bootrom_id_le id; - u32 chipid, chiprev; - - brcmf_dbg(USB, "Enter\n"); - - if (devinfo == NULL) - return false; - - /* Check if firmware downloaded already by querying runtime ID */ - id.chip = cpu_to_le32(0xDEAD); - brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id)); - - chipid = le32_to_cpu(id.chip); - chiprev = le32_to_cpu(id.chiprev); - - if ((chipid & 0x4300) == 0x4300) - brcmf_dbg(USB, "chip %x rev 0x%x\n", chipid, chiprev); - else - brcmf_dbg(USB, "chip %d rev 0x%x\n", chipid, chiprev); - if (chipid == BRCMF_POSTBOOT_ID) { - brcmf_dbg(USB, "firmware already downloaded\n"); - brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id)); - return false; - } else { - devinfo->bus_pub.devid = chipid; - devinfo->bus_pub.chiprev = chiprev; - } - return true; -} - -static int -brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo) -{ - struct bootrom_id_le id; - u32 loop_cnt; - int err; - - brcmf_dbg(USB, "Enter\n"); - - loop_cnt = 0; - do { - mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT); - loop_cnt++; - id.chip = cpu_to_le32(0xDEAD); /* Get the ID */ - err = brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id)); - if ((err) && (err != -ETIMEDOUT)) - return err; - if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) - break; - } while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT); - - if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) { - brcmf_dbg(USB, "postboot chip 0x%x/rev 0x%x\n", - le32_to_cpu(id.chip), le32_to_cpu(id.chiprev)); - - brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id)); - return 0; - } else { - brcmf_err("Cannot talk to Dongle. Firmware is not UP, %d ms\n", - BRCMF_USB_RESET_GETVER_SPINWAIT * loop_cnt); - return -EINVAL; - } -} - - -static int -brcmf_usb_dl_send_bulk(struct brcmf_usbdev_info *devinfo, void *buffer, int len) -{ - int ret; - - if ((devinfo == NULL) || (devinfo->bulk_urb == NULL)) - return -EINVAL; - - /* Prepare the URB */ - usb_fill_bulk_urb(devinfo->bulk_urb, devinfo->usbdev, - devinfo->tx_pipe, buffer, len, - (usb_complete_t)brcmf_usb_sync_complete, devinfo); - - devinfo->bulk_urb->transfer_flags |= URB_ZERO_PACKET; - - devinfo->ctl_completed = false; - ret = usb_submit_urb(devinfo->bulk_urb, GFP_ATOMIC); - if (ret) { - brcmf_err("usb_submit_urb failed %d\n", ret); - return ret; - } - ret = brcmf_usb_ioctl_resp_wait(devinfo); - return (ret == 0); -} - -static int -brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) -{ - unsigned int sendlen, sent, dllen; - char *bulkchunk = NULL, *dlpos; - struct rdl_state_le state; - u32 rdlstate, rdlbytes; - int err = 0; - - brcmf_dbg(USB, "Enter, fw %p, len %d\n", fw, fwlen); - - bulkchunk = kmalloc(TRX_RDL_CHUNK, GFP_ATOMIC); - if (bulkchunk == NULL) { - err = -ENOMEM; - goto fail; - } - - /* 1) Prepare USB boot loader for runtime image */ - brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state)); - - rdlstate = le32_to_cpu(state.state); - rdlbytes = le32_to_cpu(state.bytes); - - /* 2) Check we are in the Waiting state */ - if (rdlstate != DL_WAITING) { - brcmf_err("Failed to DL_START\n"); - err = -EINVAL; - goto fail; - } - sent = 0; - dlpos = fw; - dllen = fwlen; - - /* Get chip id and rev */ - while (rdlbytes != dllen) { - /* Wait until the usb device reports it received all - * the bytes we sent */ - if ((rdlbytes == sent) && (rdlbytes != dllen)) { - if ((dllen-sent) < TRX_RDL_CHUNK) - sendlen = dllen-sent; - else - sendlen = TRX_RDL_CHUNK; - - /* simply avoid having to send a ZLP by ensuring we - * never have an even - * multiple of 64 - */ - if (!(sendlen % 64)) - sendlen -= 4; - - /* send data */ - memcpy(bulkchunk, dlpos, sendlen); - if (brcmf_usb_dl_send_bulk(devinfo, bulkchunk, - sendlen)) { - brcmf_err("send_bulk failed\n"); - err = -EINVAL; - goto fail; - } - - dlpos += sendlen; - sent += sendlen; - } - err = brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, - sizeof(state)); - if (err) { - brcmf_err("DL_GETSTATE Failed\n"); - goto fail; - } - - rdlstate = le32_to_cpu(state.state); - rdlbytes = le32_to_cpu(state.bytes); - - /* restart if an error is reported */ - if (rdlstate == DL_BAD_HDR || rdlstate == DL_BAD_CRC) { - brcmf_err("Bad Hdr or Bad CRC state %d\n", - rdlstate); - err = -EINVAL; - goto fail; - } - } - -fail: - kfree(bulkchunk); - brcmf_dbg(USB, "Exit, err=%d\n", err); - return err; -} - -static int brcmf_usb_dlstart(struct brcmf_usbdev_info *devinfo, u8 *fw, int len) -{ - int err; - - brcmf_dbg(USB, "Enter\n"); - - if (devinfo == NULL) - return -EINVAL; - - if (devinfo->bus_pub.devid == 0xDEAD) - return -EINVAL; - - err = brcmf_usb_dl_writeimage(devinfo, fw, len); - if (err == 0) - devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_DONE; - else - devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_FAIL; - brcmf_dbg(USB, "Exit, err=%d\n", err); - - return err; -} - -static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo) -{ - struct rdl_state_le state; - - brcmf_dbg(USB, "Enter\n"); - if (!devinfo) - return -EINVAL; - - if (devinfo->bus_pub.devid == 0xDEAD) - return -EINVAL; - - /* Check we are runnable */ - state.state = 0; - brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(state)); - - /* Start the image */ - if (state.state == cpu_to_le32(DL_RUNNABLE)) { - if (brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(state))) - return -ENODEV; - if (brcmf_usb_resetcfg(devinfo)) - return -ENODEV; - /* The Dongle may go for re-enumeration. */ - } else { - brcmf_err("Dongle not runnable\n"); - return -EINVAL; - } - brcmf_dbg(USB, "Exit\n"); - return 0; -} - -static bool brcmf_usb_chip_support(int chipid, int chiprev) -{ - switch(chipid) { - case BRCM_CC_43143_CHIP_ID: - return true; - case BRCM_CC_43235_CHIP_ID: - case BRCM_CC_43236_CHIP_ID: - case BRCM_CC_43238_CHIP_ID: - return (chiprev == 3); - case BRCM_CC_43242_CHIP_ID: - return true; - case BRCM_CC_43566_CHIP_ID: - case BRCM_CC_43569_CHIP_ID: - return true; - default: - break; - } - return false; -} - -static int -brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo) -{ - int devid, chiprev; - int err; - - brcmf_dbg(USB, "Enter\n"); - if (devinfo == NULL) - return -ENODEV; - - devid = devinfo->bus_pub.devid; - chiprev = devinfo->bus_pub.chiprev; - - if (!brcmf_usb_chip_support(devid, chiprev)) { - brcmf_err("unsupported chip %d rev %d\n", - devid, chiprev); - return -EINVAL; - } - - if (!devinfo->image) { - brcmf_err("No firmware!\n"); - return -ENOENT; - } - - err = brcmf_usb_dlstart(devinfo, - (u8 *)devinfo->image, devinfo->image_len); - if (err == 0) - err = brcmf_usb_dlrun(devinfo); - return err; -} - - -static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo) -{ - brcmf_dbg(USB, "Enter, devinfo %p\n", devinfo); - - /* free the URBS */ - brcmf_usb_free_q(&devinfo->rx_freeq, false); - brcmf_usb_free_q(&devinfo->tx_freeq, false); - - usb_free_urb(devinfo->ctl_urb); - usb_free_urb(devinfo->bulk_urb); - - kfree(devinfo->tx_reqs); - kfree(devinfo->rx_reqs); -} - - -static int check_file(const u8 *headers) -{ - struct trx_header_le *trx; - int actual_len = -1; - - brcmf_dbg(USB, "Enter\n"); - /* Extract trx header */ - trx = (struct trx_header_le *) headers; - if (trx->magic != cpu_to_le32(TRX_MAGIC)) - return -1; - - headers += sizeof(struct trx_header_le); - - if (le32_to_cpu(trx->flag_version) & TRX_UNCOMP_IMAGE) { - actual_len = le32_to_cpu(trx->offsets[TRX_OFFSETS_DLFWLEN_IDX]); - return actual_len + sizeof(struct trx_header_le); - } - return -1; -} - -static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) -{ - switch (devinfo->bus_pub.devid) { - case BRCM_CC_43143_CHIP_ID: - return BRCMF_USB_43143_FW_NAME; - case BRCM_CC_43235_CHIP_ID: - case BRCM_CC_43236_CHIP_ID: - case BRCM_CC_43238_CHIP_ID: - return BRCMF_USB_43236_FW_NAME; - case BRCM_CC_43242_CHIP_ID: - return BRCMF_USB_43242_FW_NAME; - case BRCM_CC_43566_CHIP_ID: - case BRCM_CC_43569_CHIP_ID: - return BRCMF_USB_43569_FW_NAME; - default: - return NULL; - } -} - - -static -struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, - int nrxq, int ntxq) -{ - brcmf_dbg(USB, "Enter\n"); - - devinfo->bus_pub.nrxq = nrxq; - devinfo->rx_low_watermark = nrxq / 2; - devinfo->bus_pub.devinfo = devinfo; - devinfo->bus_pub.ntxq = ntxq; - devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DOWN; - - /* flow control when too many tx urbs posted */ - devinfo->tx_low_watermark = ntxq / 4; - devinfo->tx_high_watermark = devinfo->tx_low_watermark * 3; - devinfo->bus_pub.bus_mtu = BRCMF_USB_MAX_PKT_SIZE; - - /* Initialize other structure content */ - init_waitqueue_head(&devinfo->ioctl_resp_wait); - - /* Initialize the spinlocks */ - spin_lock_init(&devinfo->qlock); - spin_lock_init(&devinfo->tx_flowblock_lock); - - INIT_LIST_HEAD(&devinfo->rx_freeq); - INIT_LIST_HEAD(&devinfo->rx_postq); - - INIT_LIST_HEAD(&devinfo->tx_freeq); - INIT_LIST_HEAD(&devinfo->tx_postq); - - devinfo->tx_flowblock = false; - - devinfo->rx_reqs = brcmf_usbdev_qinit(&devinfo->rx_freeq, nrxq); - if (!devinfo->rx_reqs) - goto error; - - devinfo->tx_reqs = brcmf_usbdev_qinit(&devinfo->tx_freeq, ntxq); - if (!devinfo->tx_reqs) - goto error; - devinfo->tx_freecount = ntxq; - - devinfo->ctl_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!devinfo->ctl_urb) { - brcmf_err("usb_alloc_urb (ctl) failed\n"); - goto error; - } - devinfo->bulk_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!devinfo->bulk_urb) { - brcmf_err("usb_alloc_urb (bulk) failed\n"); - goto error; - } - - return &devinfo->bus_pub; - -error: - brcmf_err("failed!\n"); - brcmf_usb_detach(devinfo); - return NULL; -} - -static void brcmf_usb_wowl_config(struct device *dev, bool enabled) -{ - struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); - - brcmf_dbg(USB, "Configuring WOWL, enabled=%d\n", enabled); - devinfo->wowl_enabled = enabled; - if (enabled) - device_set_wakeup_enable(devinfo->dev, true); - else - device_set_wakeup_enable(devinfo->dev, false); -} - -static struct brcmf_bus_ops brcmf_usb_bus_ops = { - .txdata = brcmf_usb_tx, - .stop = brcmf_usb_down, - .txctl = brcmf_usb_tx_ctlpkt, - .rxctl = brcmf_usb_rx_ctlpkt, - .wowl_config = brcmf_usb_wowl_config, -}; - -static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo) -{ - int ret; - - /* Attach to the common driver interface */ - ret = brcmf_attach(devinfo->dev); - if (ret) { - brcmf_err("brcmf_attach failed\n"); - return ret; - } - - ret = brcmf_usb_up(devinfo->dev); - if (ret) - goto fail; - - ret = brcmf_bus_start(devinfo->dev); - if (ret) - goto fail; - - return 0; -fail: - brcmf_detach(devinfo->dev); - return ret; -} - -static void brcmf_usb_probe_phase2(struct device *dev, - const struct firmware *fw, - void *nvram, u32 nvlen) -{ - struct brcmf_bus *bus = dev_get_drvdata(dev); - struct brcmf_usbdev_info *devinfo; - int ret; - - brcmf_dbg(USB, "Start fw downloading\n"); - - devinfo = bus->bus_priv.usb->devinfo; - ret = check_file(fw->data); - if (ret < 0) { - brcmf_err("invalid firmware\n"); - release_firmware(fw); - goto error; - } - - devinfo->image = fw->data; - devinfo->image_len = fw->size; - - ret = brcmf_usb_fw_download(devinfo); - release_firmware(fw); - if (ret) - goto error; - - ret = brcmf_usb_bus_setup(devinfo); - if (ret) - goto error; - - mutex_unlock(&devinfo->dev_init_lock); - return; -error: - brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret); - mutex_unlock(&devinfo->dev_init_lock); - device_release_driver(dev); -} - -static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) -{ - struct brcmf_bus *bus = NULL; - struct brcmf_usbdev *bus_pub = NULL; - struct device *dev = devinfo->dev; - int ret; - - brcmf_dbg(USB, "Enter\n"); - bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ); - if (!bus_pub) - return -ENODEV; - - bus = kzalloc(sizeof(struct brcmf_bus), GFP_ATOMIC); - if (!bus) { - ret = -ENOMEM; - goto fail; - } - - bus->dev = dev; - bus_pub->bus = bus; - bus->bus_priv.usb = bus_pub; - dev_set_drvdata(dev, bus); - bus->ops = &brcmf_usb_bus_ops; - bus->proto_type = BRCMF_PROTO_BCDC; - bus->always_use_fws_queue = true; -#ifdef CONFIG_PM - bus->wowl_supported = true; -#endif - - if (!brcmf_usb_dlneeded(devinfo)) { - ret = brcmf_usb_bus_setup(devinfo); - if (ret) - goto fail; - /* we are done */ - mutex_unlock(&devinfo->dev_init_lock); - return 0; - } - bus->chip = bus_pub->devid; - bus->chiprev = bus_pub->chiprev; - - /* request firmware here */ - ret = brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), - NULL, brcmf_usb_probe_phase2); - if (ret) { - brcmf_err("firmware request failed: %d\n", ret); - goto fail; - } - - return 0; - -fail: - /* Release resources in reverse order */ - kfree(bus); - brcmf_usb_detach(devinfo); - return ret; -} - -static void -brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo) -{ - if (!devinfo) - return; - brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo); - - brcmf_detach(devinfo->dev); - kfree(devinfo->bus_pub.bus); - brcmf_usb_detach(devinfo); -} - -static int -brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *usb = interface_to_usbdev(intf); - struct brcmf_usbdev_info *devinfo; - struct usb_interface_descriptor *desc; - struct usb_endpoint_descriptor *endpoint; - int ret = 0; - u32 num_of_eps; - u8 endpoint_num, ep; - - brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct); - - devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC); - if (devinfo == NULL) - return -ENOMEM; - - devinfo->usbdev = usb; - devinfo->dev = &usb->dev; - /* Take an init lock, to protect for disconnect while still loading. - * Necessary because of the asynchronous firmware load construction - */ - mutex_init(&devinfo->dev_init_lock); - mutex_lock(&devinfo->dev_init_lock); - - usb_set_intfdata(intf, devinfo); - - /* Check that the device supports only one configuration */ - if (usb->descriptor.bNumConfigurations != 1) { - brcmf_err("Number of configurations: %d not supported\n", - usb->descriptor.bNumConfigurations); - ret = -ENODEV; - goto fail; - } - - if ((usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) && - (usb->descriptor.bDeviceClass != USB_CLASS_MISC) && - (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS_CONTROLLER)) { - brcmf_err("Device class: 0x%x not supported\n", - usb->descriptor.bDeviceClass); - ret = -ENODEV; - goto fail; - } - - desc = &intf->altsetting[0].desc; - if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || - (desc->bInterfaceSubClass != 2) || - (desc->bInterfaceProtocol != 0xff)) { - brcmf_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n", - desc->bInterfaceNumber, desc->bInterfaceClass, - desc->bInterfaceSubClass, desc->bInterfaceProtocol); - ret = -ENODEV; - goto fail; - } - - num_of_eps = desc->bNumEndpoints; - for (ep = 0; ep < num_of_eps; ep++) { - endpoint = &intf->altsetting[0].endpoint[ep].desc; - endpoint_num = usb_endpoint_num(endpoint); - if (!usb_endpoint_xfer_bulk(endpoint)) - continue; - if (usb_endpoint_dir_in(endpoint)) { - if (!devinfo->rx_pipe) - devinfo->rx_pipe = - usb_rcvbulkpipe(usb, endpoint_num); - } else { - if (!devinfo->tx_pipe) - devinfo->tx_pipe = - usb_sndbulkpipe(usb, endpoint_num); - } - } - if (devinfo->rx_pipe == 0) { - brcmf_err("No RX (in) Bulk EP found\n"); - ret = -ENODEV; - goto fail; - } - if (devinfo->tx_pipe == 0) { - brcmf_err("No TX (out) Bulk EP found\n"); - ret = -ENODEV; - goto fail; - } - - devinfo->ifnum = desc->bInterfaceNumber; - - if (usb->speed == USB_SPEED_SUPER) - brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n"); - else if (usb->speed == USB_SPEED_HIGH) - brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n"); - else - brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n"); - - ret = brcmf_usb_probe_cb(devinfo); - if (ret) - goto fail; - - /* Success */ - return 0; - -fail: - mutex_unlock(&devinfo->dev_init_lock); - kfree(devinfo); - usb_set_intfdata(intf, NULL); - return ret; -} - -static void -brcmf_usb_disconnect(struct usb_interface *intf) -{ - struct brcmf_usbdev_info *devinfo; - - brcmf_dbg(USB, "Enter\n"); - devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf); - - if (devinfo) { - mutex_lock(&devinfo->dev_init_lock); - /* Make sure that devinfo still exists. Firmware probe routines - * may have released the device and cleared the intfdata. - */ - if (!usb_get_intfdata(intf)) - goto done; - - brcmf_usb_disconnect_cb(devinfo); - kfree(devinfo); - } -done: - brcmf_dbg(USB, "Exit\n"); -} - -/* - * only need to signal the bus being down and update the state. - */ -static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state) -{ - struct usb_device *usb = interface_to_usbdev(intf); - struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); - - brcmf_dbg(USB, "Enter\n"); - devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP; - if (devinfo->wowl_enabled) - brcmf_cancel_all_urbs(devinfo); - else - brcmf_detach(&usb->dev); - return 0; -} - -/* - * (re-) start the bus. - */ -static int brcmf_usb_resume(struct usb_interface *intf) -{ - struct usb_device *usb = interface_to_usbdev(intf); - struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); - - brcmf_dbg(USB, "Enter\n"); - if (!devinfo->wowl_enabled) - return brcmf_usb_bus_setup(devinfo); - - devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP; - brcmf_usb_rx_fill_all(devinfo); - return 0; -} - -static int brcmf_usb_reset_resume(struct usb_interface *intf) -{ - struct usb_device *usb = interface_to_usbdev(intf); - struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); - - brcmf_dbg(USB, "Enter\n"); - - return brcmf_fw_get_firmwares(&usb->dev, 0, - brcmf_usb_get_fwname(devinfo), NULL, - brcmf_usb_probe_phase2); -} - -#define BRCMF_USB_DEVICE(dev_id) \ - { USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id) } - -static struct usb_device_id brcmf_usb_devid_table[] = { - BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID), - BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID), - BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID), - BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID), - /* special entry for device with firmware loaded and running */ - BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID), - { /* end: all zeroes */ } -}; - -MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table); -MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME); -MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME); -MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME); -MODULE_FIRMWARE(BRCMF_USB_43569_FW_NAME); - -static struct usb_driver brcmf_usbdrvr = { - .name = KBUILD_MODNAME, - .probe = brcmf_usb_probe, - .disconnect = brcmf_usb_disconnect, - .id_table = brcmf_usb_devid_table, - .suspend = brcmf_usb_suspend, - .resume = brcmf_usb_resume, - .reset_resume = brcmf_usb_reset_resume, - .supports_autosuspend = 1, - .disable_hub_initiated_lpm = 1, -}; - -static int brcmf_usb_reset_device(struct device *dev, void *notused) -{ - /* device past is the usb interface so we - * need to use parent here. - */ - brcmf_dev_reset(dev->parent); - return 0; -} - -void brcmf_usb_exit(void) -{ - struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver; - int ret; - - brcmf_dbg(USB, "Enter\n"); - ret = driver_for_each_device(drv, NULL, NULL, - brcmf_usb_reset_device); - usb_deregister(&brcmf_usbdrvr); -} - -void brcmf_usb_register(void) -{ - brcmf_dbg(USB, "Enter\n"); - usb_register(&brcmf_usbdrvr); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.h b/drivers/net/wireless/brcm80211/brcmfmac/usb.h deleted file mode 100644 index f483a8c9945b..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2011 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_USB_H -#define BRCMFMAC_USB_H - -enum brcmf_usb_state { - BRCMFMAC_USB_STATE_DOWN, - BRCMFMAC_USB_STATE_DL_FAIL, - BRCMFMAC_USB_STATE_DL_DONE, - BRCMFMAC_USB_STATE_UP, - BRCMFMAC_USB_STATE_SLEEP -}; - -struct brcmf_stats { - u32 tx_ctlpkts; - u32 tx_ctlerrs; - u32 rx_ctlpkts; - u32 rx_ctlerrs; -}; - -struct brcmf_usbdev { - struct brcmf_bus *bus; - struct brcmf_usbdev_info *devinfo; - enum brcmf_usb_state state; - struct brcmf_stats stats; - int ntxq, nrxq, rxsize; - u32 bus_mtu; - int devid; - int chiprev; /* chip revsion number */ -}; - -/* IO Request Block (IRB) */ -struct brcmf_usbreq { - struct list_head list; - struct brcmf_usbdev_info *devinfo; - struct urb *urb; - struct sk_buff *skb; -}; - -#endif /* BRCMFMAC_USB_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c deleted file mode 100644 index 8eff2753abad..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2014 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 -#include "fwil_types.h" -#include "core.h" -#include "p2p.h" -#include "debug.h" -#include "cfg80211.h" -#include "vendor.h" -#include "fwil.h" - -static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, - struct wireless_dev *wdev, - const void *data, int len) -{ - struct brcmf_cfg80211_vif *vif; - struct brcmf_if *ifp; - const struct brcmf_vndr_dcmd_hdr *cmdhdr = data; - struct sk_buff *reply; - int ret, payload, ret_len; - void *dcmd_buf = NULL, *wr_pointer; - u16 msglen, maxmsglen = PAGE_SIZE - 0x100; - - if (len < sizeof(*cmdhdr)) { - brcmf_err("vendor command too short: %d\n", len); - return -EINVAL; - } - - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - ifp = vif->ifp; - - brcmf_dbg(TRACE, "ifidx=%d, cmd=%d\n", ifp->ifidx, cmdhdr->cmd); - - if (cmdhdr->offset > len) { - brcmf_err("bad buffer offset %d > %d\n", cmdhdr->offset, len); - return -EINVAL; - } - - len -= cmdhdr->offset; - ret_len = cmdhdr->len; - if (ret_len > 0 || len > 0) { - if (len > BRCMF_DCMD_MAXLEN) { - brcmf_err("oversize input buffer %d\n", len); - len = BRCMF_DCMD_MAXLEN; - } - if (ret_len > BRCMF_DCMD_MAXLEN) { - brcmf_err("oversize return buffer %d\n", ret_len); - ret_len = BRCMF_DCMD_MAXLEN; - } - payload = max(ret_len, len) + 1; - dcmd_buf = vzalloc(payload); - if (NULL == dcmd_buf) - return -ENOMEM; - - memcpy(dcmd_buf, (void *)cmdhdr + cmdhdr->offset, len); - *(char *)(dcmd_buf + len) = '\0'; - } - - if (cmdhdr->set) - ret = brcmf_fil_cmd_data_set(ifp, cmdhdr->cmd, dcmd_buf, - ret_len); - else - ret = brcmf_fil_cmd_data_get(ifp, cmdhdr->cmd, dcmd_buf, - ret_len); - if (ret != 0) - goto exit; - - wr_pointer = dcmd_buf; - while (ret_len > 0) { - msglen = ret_len > maxmsglen ? maxmsglen : ret_len; - ret_len -= msglen; - payload = msglen + sizeof(msglen); - reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload); - if (NULL == reply) { - ret = -ENOMEM; - break; - } - - if (nla_put(reply, BRCMF_NLATTR_DATA, msglen, wr_pointer) || - nla_put_u16(reply, BRCMF_NLATTR_LEN, msglen)) { - kfree_skb(reply); - ret = -ENOBUFS; - break; - } - - ret = cfg80211_vendor_cmd_reply(reply); - if (ret) - break; - - wr_pointer += msglen; - } - -exit: - vfree(dcmd_buf); - - return ret; -} - -const struct wiphy_vendor_command brcmf_vendor_cmds[] = { - { - { - .vendor_id = BROADCOM_OUI, - .subcmd = BRCMF_VNDR_CMDS_DCMD - }, - .flags = WIPHY_VENDOR_CMD_NEED_WDEV | - WIPHY_VENDOR_CMD_NEED_NETDEV, - .doit = brcmf_cfg80211_vndr_cmds_dcmd_handler - }, -}; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/brcm80211/brcmfmac/vendor.h deleted file mode 100644 index 061b7bfa2e1c..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2014 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 _vendor_h_ -#define _vendor_h_ - -#define BROADCOM_OUI 0x001018 - -enum brcmf_vndr_cmds { - BRCMF_VNDR_CMDS_UNSPEC, - BRCMF_VNDR_CMDS_DCMD, - BRCMF_VNDR_CMDS_LAST -}; - -/** - * enum brcmf_nlattrs - nl80211 message attributes - * - * @BRCMF_NLATTR_LEN: message body length - * @BRCMF_NLATTR_DATA: message body - */ -enum brcmf_nlattrs { - BRCMF_NLATTR_UNSPEC, - - BRCMF_NLATTR_LEN, - BRCMF_NLATTR_DATA, - - __BRCMF_NLATTR_AFTER_LAST, - BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1 -}; - -/** - * struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd - * support - * - * @cmd: common dongle cmd definition - * @len: length of expecting return buffer - * @offset: offset of data buffer - * @set: get or set request(optional) - * @magic: magic number for verification - */ -struct brcmf_vndr_dcmd_hdr { - uint cmd; - int len; - uint offset; - uint set; - uint magic; -}; - -extern const struct wiphy_vendor_command brcmf_vendor_cmds[]; - -#endif /* _vendor_h_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/Makefile b/drivers/net/wireless/brcm80211/brcmsmac/Makefile deleted file mode 100644 index 32464acccd90..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# -# Makefile fragment for Broadcom 802.11n Networking Device Driver -# -# Copyright (c) 2010 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. - -ccflags-y := \ - -D__CHECK_ENDIAN__ \ - -Idrivers/net/wireless/brcm80211/brcmsmac \ - -Idrivers/net/wireless/brcm80211/brcmsmac/phy \ - -Idrivers/net/wireless/brcm80211/include - -brcmsmac-y := \ - mac80211_if.o \ - ucode_loader.o \ - ampdu.o \ - antsel.o \ - channel.o \ - main.o \ - phy_shim.o \ - pmu.o \ - rate.o \ - stf.o \ - aiutils.o \ - phy/phy_cmn.o \ - phy/phy_lcn.o \ - phy/phy_n.o \ - phy/phytbl_lcn.o \ - phy/phytbl_n.o \ - phy/phy_qmath.o \ - dma.o \ - brcms_trace_events.o \ - debug.o - -brcmsmac-$(CONFIG_BCMA_DRIVER_GPIO) += led.o - -obj-$(CONFIG_BRCMSMAC) += brcmsmac.o diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c deleted file mode 100644 index 53365977bfd6..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c +++ /dev/null @@ -1,710 +0,0 @@ -/* - * Copyright (c) 2010 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. - * - * File contents: support functions for PCI/PCIe - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include - -#include -#include -#include -#include -#include -#include "types.h" -#include "pub.h" -#include "pmu.h" -#include "aiutils.h" - -/* slow_clk_ctl */ - /* slow clock source mask */ -#define SCC_SS_MASK 0x00000007 - /* source of slow clock is LPO */ -#define SCC_SS_LPO 0x00000000 - /* source of slow clock is crystal */ -#define SCC_SS_XTAL 0x00000001 - /* source of slow clock is PCI */ -#define SCC_SS_PCI 0x00000002 - /* LPOFreqSel, 1: 160Khz, 0: 32KHz */ -#define SCC_LF 0x00000200 - /* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */ -#define SCC_LP 0x00000400 - /* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */ -#define SCC_FS 0x00000800 - /* IgnorePllOffReq, 1/0: - * power logic ignores/honors PLL clock disable requests from core - */ -#define SCC_IP 0x00001000 - /* XtalControlEn, 1/0: - * power logic does/doesn't disable crystal when appropriate - */ -#define SCC_XC 0x00002000 - /* XtalPU (RO), 1/0: crystal running/disabled */ -#define SCC_XP 0x00004000 - /* ClockDivider (SlowClk = 1/(4+divisor)) */ -#define SCC_CD_MASK 0xffff0000 -#define SCC_CD_SHIFT 16 - -/* system_clk_ctl */ - /* ILPen: Enable Idle Low Power */ -#define SYCC_IE 0x00000001 - /* ALPen: Enable Active Low Power */ -#define SYCC_AE 0x00000002 - /* ForcePLLOn */ -#define SYCC_FP 0x00000004 - /* Force ALP (or HT if ALPen is not set */ -#define SYCC_AR 0x00000008 - /* Force HT */ -#define SYCC_HR 0x00000010 - /* ClkDiv (ILP = 1/(4 * (divisor + 1)) */ -#define SYCC_CD_MASK 0xffff0000 -#define SYCC_CD_SHIFT 16 - -#define CST4329_SPROM_OTP_SEL_MASK 0x00000003 - /* OTP is powered up, use def. CIS, no SPROM */ -#define CST4329_DEFCIS_SEL 0 - /* OTP is powered up, SPROM is present */ -#define CST4329_SPROM_SEL 1 - /* OTP is powered up, no SPROM */ -#define CST4329_OTP_SEL 2 - /* OTP is powered down, SPROM is present */ -#define CST4329_OTP_PWRDN 3 - -#define CST4329_SPI_SDIO_MODE_MASK 0x00000004 -#define CST4329_SPI_SDIO_MODE_SHIFT 2 - -/* 43224 chip-specific ChipControl register bits */ -#define CCTRL43224_GPIO_TOGGLE 0x8000 - /* 12 mA drive strength */ -#define CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 - /* 12 mA drive strength for later 43224s */ -#define CCTRL_43224B0_12MA_LED_DRIVE 0xF0 - -/* 43236 Chip specific ChipStatus register bits */ -#define CST43236_SFLASH_MASK 0x00000040 -#define CST43236_OTP_MASK 0x00000080 -#define CST43236_HSIC_MASK 0x00000100 /* USB/HSIC */ -#define CST43236_BP_CLK 0x00000200 /* 120/96Mbps */ -#define CST43236_BOOT_MASK 0x00001800 -#define CST43236_BOOT_SHIFT 11 -#define CST43236_BOOT_FROM_SRAM 0 /* boot from SRAM, ARM in reset */ -#define CST43236_BOOT_FROM_ROM 1 /* boot from ROM */ -#define CST43236_BOOT_FROM_FLASH 2 /* boot from FLASH */ -#define CST43236_BOOT_FROM_INVALID 3 - -/* 4331 chip-specific ChipControl register bits */ - /* 0 disable */ -#define CCTRL4331_BT_COEXIST (1<<0) - /* 0 SECI is disabled (JTAG functional) */ -#define CCTRL4331_SECI (1<<1) - /* 0 disable */ -#define CCTRL4331_EXT_LNA (1<<2) - /* sprom/gpio13-15 mux */ -#define CCTRL4331_SPROM_GPIO13_15 (1<<3) - /* 0 ext pa disable, 1 ext pa enabled */ -#define CCTRL4331_EXTPA_EN (1<<4) - /* set drive out GPIO_CLK on sprom_cs pin */ -#define CCTRL4331_GPIOCLK_ON_SPROMCS (1<<5) - /* use sprom_cs pin as PCIE mdio interface */ -#define CCTRL4331_PCIE_MDIO_ON_SPROMCS (1<<6) - /* aband extpa will be at gpio2/5 and sprom_dout */ -#define CCTRL4331_EXTPA_ON_GPIO2_5 (1<<7) - /* override core control on pipe_AuxClkEnable */ -#define CCTRL4331_OVR_PIPEAUXCLKEN (1<<8) - /* override core control on pipe_AuxPowerDown */ -#define CCTRL4331_OVR_PIPEAUXPWRDOWN (1<<9) - /* pcie_auxclkenable */ -#define CCTRL4331_PCIE_AUXCLKEN (1<<10) - /* pcie_pipe_pllpowerdown */ -#define CCTRL4331_PCIE_PIPE_PLLDOWN (1<<11) - /* enable bt_shd0 at gpio4 */ -#define CCTRL4331_BT_SHD0_ON_GPIO4 (1<<16) - /* enable bt_shd1 at gpio5 */ -#define CCTRL4331_BT_SHD1_ON_GPIO5 (1<<17) - -/* 4331 Chip specific ChipStatus register bits */ - /* crystal frequency 20/40Mhz */ -#define CST4331_XTAL_FREQ 0x00000001 -#define CST4331_SPROM_PRESENT 0x00000002 -#define CST4331_OTP_PRESENT 0x00000004 -#define CST4331_LDO_RF 0x00000008 -#define CST4331_LDO_PAR 0x00000010 - -/* 4319 chip-specific ChipStatus register bits */ -#define CST4319_SPI_CPULESSUSB 0x00000001 -#define CST4319_SPI_CLK_POL 0x00000002 -#define CST4319_SPI_CLK_PH 0x00000008 - /* gpio [7:6], SDIO CIS selection */ -#define CST4319_SPROM_OTP_SEL_MASK 0x000000c0 -#define CST4319_SPROM_OTP_SEL_SHIFT 6 - /* use default CIS, OTP is powered up */ -#define CST4319_DEFCIS_SEL 0x00000000 - /* use SPROM, OTP is powered up */ -#define CST4319_SPROM_SEL 0x00000040 - /* use OTP, OTP is powered up */ -#define CST4319_OTP_SEL 0x00000080 - /* use SPROM, OTP is powered down */ -#define CST4319_OTP_PWRDN 0x000000c0 - /* gpio [8], sdio/usb mode */ -#define CST4319_SDIO_USB_MODE 0x00000100 -#define CST4319_REMAP_SEL_MASK 0x00000600 -#define CST4319_ILPDIV_EN 0x00000800 -#define CST4319_XTAL_PD_POL 0x00001000 -#define CST4319_LPO_SEL 0x00002000 -#define CST4319_RES_INIT_MODE 0x0000c000 - /* PALDO is configured with external PNP */ -#define CST4319_PALDO_EXTPNP 0x00010000 -#define CST4319_CBUCK_MODE_MASK 0x00060000 -#define CST4319_CBUCK_MODE_BURST 0x00020000 -#define CST4319_CBUCK_MODE_LPBURST 0x00060000 -#define CST4319_RCAL_VALID 0x01000000 -#define CST4319_RCAL_VALUE_MASK 0x3e000000 -#define CST4319_RCAL_VALUE_SHIFT 25 - -/* 4336 chip-specific ChipStatus register bits */ -#define CST4336_SPI_MODE_MASK 0x00000001 -#define CST4336_SPROM_PRESENT 0x00000002 -#define CST4336_OTP_PRESENT 0x00000004 -#define CST4336_ARMREMAP_0 0x00000008 -#define CST4336_ILPDIV_EN_MASK 0x00000010 -#define CST4336_ILPDIV_EN_SHIFT 4 -#define CST4336_XTAL_PD_POL_MASK 0x00000020 -#define CST4336_XTAL_PD_POL_SHIFT 5 -#define CST4336_LPO_SEL_MASK 0x00000040 -#define CST4336_LPO_SEL_SHIFT 6 -#define CST4336_RES_INIT_MODE_MASK 0x00000180 -#define CST4336_RES_INIT_MODE_SHIFT 7 -#define CST4336_CBUCK_MODE_MASK 0x00000600 -#define CST4336_CBUCK_MODE_SHIFT 9 - -/* 4313 chip-specific ChipStatus register bits */ -#define CST4313_SPROM_PRESENT 1 -#define CST4313_OTP_PRESENT 2 -#define CST4313_SPROM_OTP_SEL_MASK 0x00000002 -#define CST4313_SPROM_OTP_SEL_SHIFT 0 - -/* 4313 Chip specific ChipControl register bits */ - /* 12 mA drive strengh for later 4313 */ -#define CCTRL_4313_12MA_LED_DRIVE 0x00000007 - -/* Manufacturer Ids */ -#define MFGID_ARM 0x43b -#define MFGID_BRCM 0x4bf -#define MFGID_MIPS 0x4a7 - -/* Enumeration ROM registers */ -#define ER_EROMENTRY 0x000 -#define ER_REMAPCONTROL 0xe00 -#define ER_REMAPSELECT 0xe04 -#define ER_MASTERSELECT 0xe10 -#define ER_ITCR 0xf00 -#define ER_ITIP 0xf04 - -/* Erom entries */ -#define ER_TAG 0xe -#define ER_TAG1 0x6 -#define ER_VALID 1 -#define ER_CI 0 -#define ER_MP 2 -#define ER_ADD 4 -#define ER_END 0xe -#define ER_BAD 0xffffffff - -/* EROM CompIdentA */ -#define CIA_MFG_MASK 0xfff00000 -#define CIA_MFG_SHIFT 20 -#define CIA_CID_MASK 0x000fff00 -#define CIA_CID_SHIFT 8 -#define CIA_CCL_MASK 0x000000f0 -#define CIA_CCL_SHIFT 4 - -/* EROM CompIdentB */ -#define CIB_REV_MASK 0xff000000 -#define CIB_REV_SHIFT 24 -#define CIB_NSW_MASK 0x00f80000 -#define CIB_NSW_SHIFT 19 -#define CIB_NMW_MASK 0x0007c000 -#define CIB_NMW_SHIFT 14 -#define CIB_NSP_MASK 0x00003e00 -#define CIB_NSP_SHIFT 9 -#define CIB_NMP_MASK 0x000001f0 -#define CIB_NMP_SHIFT 4 - -/* EROM AddrDesc */ -#define AD_ADDR_MASK 0xfffff000 -#define AD_SP_MASK 0x00000f00 -#define AD_SP_SHIFT 8 -#define AD_ST_MASK 0x000000c0 -#define AD_ST_SHIFT 6 -#define AD_ST_SLAVE 0x00000000 -#define AD_ST_BRIDGE 0x00000040 -#define AD_ST_SWRAP 0x00000080 -#define AD_ST_MWRAP 0x000000c0 -#define AD_SZ_MASK 0x00000030 -#define AD_SZ_SHIFT 4 -#define AD_SZ_4K 0x00000000 -#define AD_SZ_8K 0x00000010 -#define AD_SZ_16K 0x00000020 -#define AD_SZ_SZD 0x00000030 -#define AD_AG32 0x00000008 -#define AD_ADDR_ALIGN 0x00000fff -#define AD_SZ_BASE 0x00001000 /* 4KB */ - -/* EROM SizeDesc */ -#define SD_SZ_MASK 0xfffff000 -#define SD_SG32 0x00000008 -#define SD_SZ_ALIGN 0x00000fff - -/* PCI config space bit 4 for 4306c0 slow clock source */ -#define PCI_CFG_GPIO_SCS 0x10 -/* PCI config space GPIO 14 for Xtal power-up */ -#define PCI_CFG_GPIO_XTAL 0x40 -/* PCI config space GPIO 15 for PLL power-down */ -#define PCI_CFG_GPIO_PLL 0x80 - -/* power control defines */ -#define PLL_DELAY 150 /* us pll on delay */ -#define FREF_DELAY 200 /* us fref change delay */ -#define XTAL_ON_DELAY 1000 /* us crystal power-on delay */ - -/* resetctrl */ -#define AIRC_RESET 1 - -#define NOREV -1 /* Invalid rev */ - -/* GPIO Based LED powersave defines */ -#define DEFAULT_GPIO_ONTIME 10 /* Default: 10% on */ -#define DEFAULT_GPIO_OFFTIME 90 /* Default: 10% on */ - -/* When Srom support present, fields in sromcontrol */ -#define SRC_START 0x80000000 -#define SRC_BUSY 0x80000000 -#define SRC_OPCODE 0x60000000 -#define SRC_OP_READ 0x00000000 -#define SRC_OP_WRITE 0x20000000 -#define SRC_OP_WRDIS 0x40000000 -#define SRC_OP_WREN 0x60000000 -#define SRC_OTPSEL 0x00000010 -#define SRC_LOCK 0x00000008 -#define SRC_SIZE_MASK 0x00000006 -#define SRC_SIZE_1K 0x00000000 -#define SRC_SIZE_4K 0x00000002 -#define SRC_SIZE_16K 0x00000004 -#define SRC_SIZE_SHIFT 1 -#define SRC_PRESENT 0x00000001 - -/* External PA enable mask */ -#define GPIO_CTRL_EPA_EN_MASK 0x40 - -#define DEFAULT_GPIOTIMERVAL \ - ((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME) - -#define BADIDX (SI_MAXCORES + 1) - -#define IS_SIM(chippkg) \ - ((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID)) - -#define GOODCOREADDR(x, b) \ - (((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \ - IS_ALIGNED((x), SI_CORE_SIZE)) - -struct aidmp { - u32 oobselina30; /* 0x000 */ - u32 oobselina74; /* 0x004 */ - u32 PAD[6]; - u32 oobselinb30; /* 0x020 */ - u32 oobselinb74; /* 0x024 */ - u32 PAD[6]; - u32 oobselinc30; /* 0x040 */ - u32 oobselinc74; /* 0x044 */ - u32 PAD[6]; - u32 oobselind30; /* 0x060 */ - u32 oobselind74; /* 0x064 */ - u32 PAD[38]; - u32 oobselouta30; /* 0x100 */ - u32 oobselouta74; /* 0x104 */ - u32 PAD[6]; - u32 oobseloutb30; /* 0x120 */ - u32 oobseloutb74; /* 0x124 */ - u32 PAD[6]; - u32 oobseloutc30; /* 0x140 */ - u32 oobseloutc74; /* 0x144 */ - u32 PAD[6]; - u32 oobseloutd30; /* 0x160 */ - u32 oobseloutd74; /* 0x164 */ - u32 PAD[38]; - u32 oobsynca; /* 0x200 */ - u32 oobseloutaen; /* 0x204 */ - u32 PAD[6]; - u32 oobsyncb; /* 0x220 */ - u32 oobseloutben; /* 0x224 */ - u32 PAD[6]; - u32 oobsyncc; /* 0x240 */ - u32 oobseloutcen; /* 0x244 */ - u32 PAD[6]; - u32 oobsyncd; /* 0x260 */ - u32 oobseloutden; /* 0x264 */ - u32 PAD[38]; - u32 oobaextwidth; /* 0x300 */ - u32 oobainwidth; /* 0x304 */ - u32 oobaoutwidth; /* 0x308 */ - u32 PAD[5]; - u32 oobbextwidth; /* 0x320 */ - u32 oobbinwidth; /* 0x324 */ - u32 oobboutwidth; /* 0x328 */ - u32 PAD[5]; - u32 oobcextwidth; /* 0x340 */ - u32 oobcinwidth; /* 0x344 */ - u32 oobcoutwidth; /* 0x348 */ - u32 PAD[5]; - u32 oobdextwidth; /* 0x360 */ - u32 oobdinwidth; /* 0x364 */ - u32 oobdoutwidth; /* 0x368 */ - u32 PAD[37]; - u32 ioctrlset; /* 0x400 */ - u32 ioctrlclear; /* 0x404 */ - u32 ioctrl; /* 0x408 */ - u32 PAD[61]; - u32 iostatus; /* 0x500 */ - u32 PAD[127]; - u32 ioctrlwidth; /* 0x700 */ - u32 iostatuswidth; /* 0x704 */ - u32 PAD[62]; - u32 resetctrl; /* 0x800 */ - u32 resetstatus; /* 0x804 */ - u32 resetreadid; /* 0x808 */ - u32 resetwriteid; /* 0x80c */ - u32 PAD[60]; - u32 errlogctrl; /* 0x900 */ - u32 errlogdone; /* 0x904 */ - u32 errlogstatus; /* 0x908 */ - u32 errlogaddrlo; /* 0x90c */ - u32 errlogaddrhi; /* 0x910 */ - u32 errlogid; /* 0x914 */ - u32 errloguser; /* 0x918 */ - u32 errlogflags; /* 0x91c */ - u32 PAD[56]; - u32 intstatus; /* 0xa00 */ - u32 PAD[127]; - u32 config; /* 0xe00 */ - u32 PAD[63]; - u32 itcr; /* 0xf00 */ - u32 PAD[3]; - u32 itipooba; /* 0xf10 */ - u32 itipoobb; /* 0xf14 */ - u32 itipoobc; /* 0xf18 */ - u32 itipoobd; /* 0xf1c */ - u32 PAD[4]; - u32 itipoobaout; /* 0xf30 */ - u32 itipoobbout; /* 0xf34 */ - u32 itipoobcout; /* 0xf38 */ - u32 itipoobdout; /* 0xf3c */ - u32 PAD[4]; - u32 itopooba; /* 0xf50 */ - u32 itopoobb; /* 0xf54 */ - u32 itopoobc; /* 0xf58 */ - u32 itopoobd; /* 0xf5c */ - u32 PAD[4]; - u32 itopoobain; /* 0xf70 */ - u32 itopoobbin; /* 0xf74 */ - u32 itopoobcin; /* 0xf78 */ - u32 itopoobdin; /* 0xf7c */ - u32 PAD[4]; - u32 itopreset; /* 0xf90 */ - u32 PAD[15]; - u32 peripherialid4; /* 0xfd0 */ - u32 peripherialid5; /* 0xfd4 */ - u32 peripherialid6; /* 0xfd8 */ - u32 peripherialid7; /* 0xfdc */ - u32 peripherialid0; /* 0xfe0 */ - u32 peripherialid1; /* 0xfe4 */ - u32 peripherialid2; /* 0xfe8 */ - u32 peripherialid3; /* 0xfec */ - u32 componentid0; /* 0xff0 */ - u32 componentid1; /* 0xff4 */ - u32 componentid2; /* 0xff8 */ - u32 componentid3; /* 0xffc */ -}; - -static bool -ai_buscore_setup(struct si_info *sii, struct bcma_device *cc) -{ - /* no cores found, bail out */ - if (cc->bus->nr_cores == 0) - return false; - - /* get chipcommon rev */ - sii->pub.ccrev = cc->id.rev; - - /* get chipcommon chipstatus */ - sii->chipst = bcma_read32(cc, CHIPCREGOFFS(chipstatus)); - - /* get chipcommon capabilites */ - sii->pub.cccaps = bcma_read32(cc, CHIPCREGOFFS(capabilities)); - - /* get pmu rev and caps */ - if (ai_get_cccaps(&sii->pub) & CC_CAP_PMU) { - sii->pub.pmucaps = bcma_read32(cc, - CHIPCREGOFFS(pmucapabilities)); - sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK; - } - - return true; -} - -static struct si_info *ai_doattach(struct si_info *sii, - struct bcma_bus *pbus) -{ - struct si_pub *sih = &sii->pub; - struct bcma_device *cc; - - sii->icbus = pbus; - sii->pcibus = pbus->host_pci; - - /* switch to Chipcommon core */ - cc = pbus->drv_cc.core; - - sih->chip = pbus->chipinfo.id; - sih->chiprev = pbus->chipinfo.rev; - sih->chippkg = pbus->chipinfo.pkg; - sih->boardvendor = pbus->boardinfo.vendor; - sih->boardtype = pbus->boardinfo.type; - - if (!ai_buscore_setup(sii, cc)) - goto exit; - - /* === NVRAM, clock is ready === */ - bcma_write32(cc, CHIPCREGOFFS(gpiopullup), 0); - bcma_write32(cc, CHIPCREGOFFS(gpiopulldown), 0); - - /* PMU specific initializations */ - if (ai_get_cccaps(sih) & CC_CAP_PMU) { - (void)si_pmu_measure_alpclk(sih); - } - - return sii; - - exit: - - return NULL; -} - -/* - * Allocate a si handle and do the attach. - */ -struct si_pub * -ai_attach(struct bcma_bus *pbus) -{ - struct si_info *sii; - - /* alloc struct si_info */ - sii = kzalloc(sizeof(struct si_info), GFP_ATOMIC); - if (sii == NULL) - return NULL; - - if (ai_doattach(sii, pbus) == NULL) { - kfree(sii); - return NULL; - } - - return (struct si_pub *) sii; -} - -/* may be called with core in reset */ -void ai_detach(struct si_pub *sih) -{ - struct si_info *sii; - - sii = container_of(sih, struct si_info, pub); - - if (sii == NULL) - return; - - kfree(sii); -} - -/* - * read/modify chipcommon core register. - */ -uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val) -{ - struct bcma_device *cc; - u32 w; - struct si_info *sii; - - sii = container_of(sih, struct si_info, pub); - cc = sii->icbus->drv_cc.core; - - /* mask and set */ - if (mask || val) - bcma_maskset32(cc, regoff, ~mask, val); - - /* readback */ - w = bcma_read32(cc, regoff); - - return w; -} - -/* return the slow clock source - LPO, XTAL, or PCI */ -static uint ai_slowclk_src(struct si_pub *sih, struct bcma_device *cc) -{ - return SCC_SS_XTAL; -} - -/* -* return the ILP (slowclock) min or max frequency -* precondition: we've established the chip has dynamic clk control -*/ -static uint ai_slowclk_freq(struct si_pub *sih, bool max_freq, - struct bcma_device *cc) -{ - uint div; - - /* Chipc rev 10 is InstaClock */ - div = bcma_read32(cc, CHIPCREGOFFS(system_clk_ctl)); - div = 4 * ((div >> SYCC_CD_SHIFT) + 1); - return max_freq ? XTALMAXFREQ : (XTALMINFREQ / div); -} - -static void -ai_clkctl_setdelay(struct si_pub *sih, struct bcma_device *cc) -{ - uint slowmaxfreq, pll_delay, slowclk; - uint pll_on_delay, fref_sel_delay; - - pll_delay = PLL_DELAY; - - /* - * If the slow clock is not sourced by the xtal then - * add the xtal_on_delay since the xtal will also be - * powered down by dynamic clk control logic. - */ - - slowclk = ai_slowclk_src(sih, cc); - if (slowclk != SCC_SS_XTAL) - pll_delay += XTAL_ON_DELAY; - - /* Starting with 4318 it is ILP that is used for the delays */ - slowmaxfreq = - ai_slowclk_freq(sih, false, cc); - - pll_on_delay = ((slowmaxfreq * pll_delay) + 999999) / 1000000; - fref_sel_delay = ((slowmaxfreq * FREF_DELAY) + 999999) / 1000000; - - bcma_write32(cc, CHIPCREGOFFS(pll_on_delay), pll_on_delay); - bcma_write32(cc, CHIPCREGOFFS(fref_sel_delay), fref_sel_delay); -} - -/* initialize power control delay registers */ -void ai_clkctl_init(struct si_pub *sih) -{ - struct si_info *sii = container_of(sih, struct si_info, pub); - struct bcma_device *cc; - - if (!(ai_get_cccaps(sih) & CC_CAP_PWR_CTL)) - return; - - cc = sii->icbus->drv_cc.core; - if (cc == NULL) - return; - - /* set all Instaclk chip ILP to 1 MHz */ - bcma_maskset32(cc, CHIPCREGOFFS(system_clk_ctl), SYCC_CD_MASK, - (ILP_DIV_1MHZ << SYCC_CD_SHIFT)); - - ai_clkctl_setdelay(sih, cc); -} - -/* - * return the value suitable for writing to the - * dot11 core FAST_PWRUP_DELAY register - */ -u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih) -{ - struct si_info *sii; - struct bcma_device *cc; - uint slowminfreq; - u16 fpdelay; - - sii = container_of(sih, struct si_info, pub); - if (ai_get_cccaps(sih) & CC_CAP_PMU) { - fpdelay = si_pmu_fast_pwrup_delay(sih); - return fpdelay; - } - - if (!(ai_get_cccaps(sih) & CC_CAP_PWR_CTL)) - return 0; - - fpdelay = 0; - cc = sii->icbus->drv_cc.core; - if (cc) { - slowminfreq = ai_slowclk_freq(sih, false, cc); - fpdelay = (((bcma_read32(cc, CHIPCREGOFFS(pll_on_delay)) + 2) - * 1000000) + (slowminfreq - 1)) / slowminfreq; - } - return fpdelay; -} - -/* - * clock control policy function throught chipcommon - * - * set dynamic clk control mode (forceslow, forcefast, dynamic) - * returns true if we are forcing fast clock - * this is a wrapper over the next internal function - * to allow flexible policy settings for outside caller - */ -bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode) -{ - struct si_info *sii; - struct bcma_device *cc; - - sii = container_of(sih, struct si_info, pub); - - cc = sii->icbus->drv_cc.core; - bcma_core_set_clockmode(cc, mode); - return mode == BCMA_CLKMODE_FAST; -} - -/* Enable BT-COEX & Ex-PA for 4313 */ -void ai_epa_4313war(struct si_pub *sih) -{ - struct si_info *sii = container_of(sih, struct si_info, pub); - struct bcma_device *cc; - - cc = sii->icbus->drv_cc.core; - - /* EPA Fix */ - bcma_set32(cc, CHIPCREGOFFS(gpiocontrol), GPIO_CTRL_EPA_EN_MASK); -} - -/* check if the device is removed */ -bool ai_deviceremoved(struct si_pub *sih) -{ - u32 w = 0; - struct si_info *sii; - - sii = container_of(sih, struct si_info, pub); - - if (sii->icbus->hosttype != BCMA_HOSTTYPE_PCI) - return false; - - pci_read_config_dword(sii->pcibus, PCI_VENDOR_ID, &w); - if ((w & 0xFFFF) != PCI_VENDOR_ID_BROADCOM) - return true; - - return false; -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h deleted file mode 100644 index 2d08c155c23b..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (c) 2011 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 _BRCM_AIUTILS_H_ -#define _BRCM_AIUTILS_H_ - -#include - -#include "types.h" - -/* - * SOC Interconnect Address Map. - * All regions may not exist on all chips. - */ -/* each core gets 4Kbytes for registers */ -#define SI_CORE_SIZE 0x1000 -/* - * Max cores (this is arbitrary, for software - * convenience and could be changed if we - * make any larger chips - */ -#define SI_MAXCORES 16 - -/* Client Mode sb2pcitranslation2 size in bytes */ -#define SI_PCI_DMA_SZ 0x40000000 - -/* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), high 32 bits */ -#define SI_PCIE_DMA_H32 0x80000000 - -/* chipcommon being the first core: */ -#define SI_CC_IDX 0 - -/* SOC Interconnect types (aka chip types) */ -#define SOCI_AI 1 - -/* A register that is common to all cores to - * communicate w/PMU regarding clock control. - */ -#define SI_CLK_CTL_ST 0x1e0 /* clock control and status */ - -/* clk_ctl_st register */ -#define CCS_FORCEALP 0x00000001 /* force ALP request */ -#define CCS_FORCEHT 0x00000002 /* force HT request */ -#define CCS_FORCEILP 0x00000004 /* force ILP request */ -#define CCS_ALPAREQ 0x00000008 /* ALP Avail Request */ -#define CCS_HTAREQ 0x00000010 /* HT Avail Request */ -#define CCS_FORCEHWREQOFF 0x00000020 /* Force HW Clock Request Off */ -#define CCS_ERSRC_REQ_MASK 0x00000700 /* external resource requests */ -#define CCS_ERSRC_REQ_SHIFT 8 -#define CCS_ALPAVAIL 0x00010000 /* ALP is available */ -#define CCS_HTAVAIL 0x00020000 /* HT is available */ -#define CCS_BP_ON_APL 0x00040000 /* RO: running on ALP clock */ -#define CCS_BP_ON_HT 0x00080000 /* RO: running on HT clock */ -#define CCS_ERSRC_STS_MASK 0x07000000 /* external resource status */ -#define CCS_ERSRC_STS_SHIFT 24 - -/* HT avail in chipc and pcmcia on 4328a0 */ -#define CCS0_HTAVAIL 0x00010000 -/* ALP avail in chipc and pcmcia on 4328a0 */ -#define CCS0_ALPAVAIL 0x00020000 - -/* Not really related to SOC Interconnect, but a couple of software - * conventions for the use the flash space: - */ - -/* Minumum amount of flash we support */ -#define FLASH_MIN 0x00020000 /* Minimum flash size */ - -#define CC_SROM_OTP 0x800 /* SROM/OTP address space */ - -/* gpiotimerval */ -#define GPIO_ONTIME_SHIFT 16 - -/* Fields in clkdiv */ -#define CLKD_OTP 0x000f0000 -#define CLKD_OTP_SHIFT 16 - -/* dynamic clock control defines */ -#define LPOMINFREQ 25000 /* low power oscillator min */ -#define LPOMAXFREQ 43000 /* low power oscillator max */ -#define XTALMINFREQ 19800000 /* 20 MHz - 1% */ -#define XTALMAXFREQ 20200000 /* 20 MHz + 1% */ -#define PCIMINFREQ 25000000 /* 25 MHz */ -#define PCIMAXFREQ 34000000 /* 33 MHz + fudge */ - -#define ILP_DIV_5MHZ 0 /* ILP = 5 MHz */ -#define ILP_DIV_1MHZ 4 /* ILP = 1 MHz */ - -/* clkctl xtal what flags */ -#define XTAL 0x1 /* primary crystal oscillator (2050) */ -#define PLL 0x2 /* main chip pll */ - -/* GPIO usage priorities */ -#define GPIO_DRV_PRIORITY 0 /* Driver */ -#define GPIO_APP_PRIORITY 1 /* Application */ -#define GPIO_HI_PRIORITY 2 /* Highest priority. Ignore GPIO - * reservation - */ - -/* GPIO pull up/down */ -#define GPIO_PULLUP 0 -#define GPIO_PULLDN 1 - -/* GPIO event regtype */ -#define GPIO_REGEVT 0 /* GPIO register event */ -#define GPIO_REGEVT_INTMSK 1 /* GPIO register event int mask */ -#define GPIO_REGEVT_INTPOL 2 /* GPIO register event int polarity */ - -/* device path */ -#define SI_DEVPATH_BUFSZ 16 /* min buffer size in bytes */ - -/* SI routine enumeration: to be used by update function with multiple hooks */ -#define SI_DOATTACH 1 -#define SI_PCIDOWN 2 -#define SI_PCIUP 3 - -/* - * Data structure to export all chip specific common variables - * public (read-only) portion of aiutils handle returned by si_attach() - */ -struct si_pub { - int ccrev; /* chip common core rev */ - u32 cccaps; /* chip common capabilities */ - int pmurev; /* pmu core rev */ - u32 pmucaps; /* pmu capabilities */ - uint boardtype; /* board type */ - uint boardvendor; /* board vendor */ - uint chip; /* chip number */ - uint chiprev; /* chip revision */ - uint chippkg; /* chip package option */ -}; - -struct pci_dev; - -struct gpioh_item { - void *arg; - bool level; - void (*handler) (u32 stat, void *arg); - u32 event; - struct gpioh_item *next; -}; - -/* misc si info needed by some of the routines */ -struct si_info { - struct si_pub pub; /* back plane public state (must be first) */ - struct bcma_bus *icbus; /* handle to soc interconnect bus */ - struct pci_dev *pcibus; /* handle to pci bus */ - - u32 chipst; /* chip status */ -}; - -/* - * Many of the routines below take an 'sih' handle as their first arg. - * Allocate this by calling si_attach(). Free it by calling si_detach(). - * At any one time, the sih is logically focused on one particular si core - * (the "current core"). - * Use si_setcore() or si_setcoreidx() to change the association to another core - */ - - -/* AMBA Interconnect exported externs */ -u32 ai_core_cflags(struct bcma_device *core, u32 mask, u32 val); - -/* === exported functions === */ -struct si_pub *ai_attach(struct bcma_bus *pbus); -void ai_detach(struct si_pub *sih); -uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val); -void ai_clkctl_init(struct si_pub *sih); -u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih); -bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode); -bool ai_deviceremoved(struct si_pub *sih); - -/* Enable Ex-PA for 4313 */ -void ai_epa_4313war(struct si_pub *sih); - -static inline u32 ai_get_cccaps(struct si_pub *sih) -{ - return sih->cccaps; -} - -static inline int ai_get_pmurev(struct si_pub *sih) -{ - return sih->pmurev; -} - -static inline u32 ai_get_pmucaps(struct si_pub *sih) -{ - return sih->pmucaps; -} - -static inline uint ai_get_boardtype(struct si_pub *sih) -{ - return sih->boardtype; -} - -static inline uint ai_get_boardvendor(struct si_pub *sih) -{ - return sih->boardvendor; -} - -static inline uint ai_get_chip_id(struct si_pub *sih) -{ - return sih->chip; -} - -static inline uint ai_get_chiprev(struct si_pub *sih) -{ - return sih->chiprev; -} - -static inline uint ai_get_chippkg(struct si_pub *sih) -{ - return sih->chippkg; -} - -#endif /* _BRCM_AIUTILS_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c deleted file mode 100644 index fa391e4eb098..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c +++ /dev/null @@ -1,1144 +0,0 @@ -/* - * Copyright (c) 2010 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 "rate.h" -#include "scb.h" -#include "phy/phy_hal.h" -#include "antsel.h" -#include "main.h" -#include "ampdu.h" -#include "debug.h" -#include "brcms_trace_events.h" - -/* max number of mpdus in an ampdu */ -#define AMPDU_MAX_MPDU 32 -/* max number of mpdus in an ampdu to a legacy */ -#define AMPDU_NUM_MPDU_LEGACY 16 -/* max Tx ba window size (in pdu) */ -#define AMPDU_TX_BA_MAX_WSIZE 64 -/* default Tx ba window size (in pdu) */ -#define AMPDU_TX_BA_DEF_WSIZE 64 -/* default Rx ba window size (in pdu) */ -#define AMPDU_RX_BA_DEF_WSIZE 64 -/* max Rx ba window size (in pdu) */ -#define AMPDU_RX_BA_MAX_WSIZE 64 -/* max dur of tx ampdu (in msec) */ -#define AMPDU_MAX_DUR 5 -/* default tx retry limit */ -#define AMPDU_DEF_RETRY_LIMIT 5 -/* default tx retry limit at reg rate */ -#define AMPDU_DEF_RR_RETRY_LIMIT 2 -/* default ffpld reserved bytes */ -#define AMPDU_DEF_FFPLD_RSVD 2048 -/* # of inis to be freed on detach */ -#define AMPDU_INI_FREE 10 -/* max # of mpdus released at a time */ -#define AMPDU_SCB_MAX_RELEASE 20 - -#define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */ -#define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu - * without underflows - */ -#define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */ -#define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */ -#define FFPLD_PLD_INCR 1000 /* increments in bytes */ -#define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we - * accumulate between resets. - */ - -#define AMPDU_DELIMITER_LEN 4 - -/* max allowed number of mpdus in an ampdu (2 streams) */ -#define AMPDU_NUM_MPDU 16 - -#define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE) - -/* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */ -#define AMPDU_MAX_MPDU_OVERHEAD (FCS_LEN + DOT11_ICV_AES_LEN +\ - AMPDU_DELIMITER_LEN + 3\ - + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN) - -/* modulo add/sub, bound = 2^k */ -#define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1)) -#define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1)) - -/* structure to hold tx fifo information and pre-loading state - * counters specific to tx underflows of ampdus - * some counters might be redundant with the ones in wlc or ampdu structures. - * This allows to maintain a specific state independently of - * how often and/or when the wlc counters are updated. - * - * ampdu_pld_size: number of bytes to be pre-loaded - * mcs2ampdu_table: per-mcs max # of mpdus in an ampdu - * prev_txfunfl: num of underflows last read from the HW macstats counter - * accum_txfunfl: num of underflows since we modified pld params - * accum_txampdu: num of tx ampdu since we modified pld params - * prev_txampdu: previous reading of tx ampdu - * dmaxferrate: estimated dma avg xfer rate in kbits/sec - */ -struct brcms_fifo_info { - u16 ampdu_pld_size; - u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; - u16 prev_txfunfl; - u32 accum_txfunfl; - u32 accum_txampdu; - u32 prev_txampdu; - u32 dmaxferrate; -}; - -/* AMPDU module specific state - * - * wlc: pointer to main wlc structure - * scb_handle: scb cubby handle to retrieve data from scb - * ini_enable: per-tid initiator enable/disable of ampdu - * ba_tx_wsize: Tx ba window size (in pdu) - * ba_rx_wsize: Rx ba window size (in pdu) - * retry_limit: mpdu transmit retry limit - * rr_retry_limit: mpdu transmit retry limit at regular rate - * retry_limit_tid: per-tid mpdu transmit retry limit - * rr_retry_limit_tid: per-tid mpdu transmit retry limit at regular rate - * mpdu_density: min mpdu spacing (0-7) ==> 2^(x-1)/8 usec - * max_pdu: max pdus allowed in ampdu - * dur: max duration of an ampdu (in msec) - * rx_factor: maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes - * ffpld_rsvd: number of bytes to reserve for preload - * max_txlen: max size of ampdu per mcs, bw and sgi - * mfbr: enable multiple fallback rate - * tx_max_funl: underflows should be kept such that - * (tx_max_funfl*underflows) < tx frames - * fifo_tb: table of fifo infos - */ -struct ampdu_info { - struct brcms_c_info *wlc; - int scb_handle; - u8 ini_enable[AMPDU_MAX_SCB_TID]; - u8 ba_tx_wsize; - u8 ba_rx_wsize; - u8 retry_limit; - u8 rr_retry_limit; - u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; - u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID]; - u8 mpdu_density; - s8 max_pdu; - u8 dur; - u8 rx_factor; - u32 ffpld_rsvd; - u32 max_txlen[MCS_TABLE_SIZE][2][2]; - bool mfbr; - u32 tx_max_funl; - struct brcms_fifo_info fifo_tb[NUM_FFPLD_FIFO]; -}; - -/* used for flushing ampdu packets */ -struct cb_del_ampdu_pars { - struct ieee80211_sta *sta; - u16 tid; -}; - -static void brcms_c_scb_ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur) -{ - u32 rate, mcs; - - for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) { - /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */ - /* 20MHz, No SGI */ - rate = mcs_2_rate(mcs, false, false); - ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3; - /* 40 MHz, No SGI */ - rate = mcs_2_rate(mcs, true, false); - ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3; - /* 20MHz, SGI */ - rate = mcs_2_rate(mcs, false, true); - ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3; - /* 40 MHz, SGI */ - rate = mcs_2_rate(mcs, true, true); - ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3; - } -} - -static bool brcms_c_ampdu_cap(struct ampdu_info *ampdu) -{ - if (BRCMS_PHY_11N_CAP(ampdu->wlc->band)) - return true; - else - return false; -} - -static int brcms_c_ampdu_set(struct ampdu_info *ampdu, bool on) -{ - struct brcms_c_info *wlc = ampdu->wlc; - struct bcma_device *core = wlc->hw->d11core; - - wlc->pub->_ampdu = false; - - if (on) { - if (!(wlc->pub->_n_enab & SUPPORT_11N)) { - brcms_err(core, "wl%d: driver not nmode enabled\n", - wlc->pub->unit); - return -ENOTSUPP; - } - if (!brcms_c_ampdu_cap(ampdu)) { - brcms_err(core, "wl%d: device not ampdu capable\n", - wlc->pub->unit); - return -ENOTSUPP; - } - wlc->pub->_ampdu = on; - } - - return 0; -} - -static void brcms_c_ffpld_init(struct ampdu_info *ampdu) -{ - int i, j; - struct brcms_fifo_info *fifo; - - for (j = 0; j < NUM_FFPLD_FIFO; j++) { - fifo = (ampdu->fifo_tb + j); - fifo->ampdu_pld_size = 0; - for (i = 0; i <= FFPLD_MAX_MCS; i++) - fifo->mcs2ampdu_table[i] = 255; - fifo->dmaxferrate = 0; - fifo->accum_txampdu = 0; - fifo->prev_txfunfl = 0; - fifo->accum_txfunfl = 0; - - } -} - -struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc) -{ - struct ampdu_info *ampdu; - int i; - - ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC); - if (!ampdu) - return NULL; - - ampdu->wlc = wlc; - - for (i = 0; i < AMPDU_MAX_SCB_TID; i++) - ampdu->ini_enable[i] = true; - /* Disable ampdu for VO by default */ - ampdu->ini_enable[PRIO_8021D_VO] = false; - ampdu->ini_enable[PRIO_8021D_NC] = false; - - /* Disable ampdu for BK by default since not enough fifo space */ - ampdu->ini_enable[PRIO_8021D_NONE] = false; - ampdu->ini_enable[PRIO_8021D_BK] = false; - - ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE; - ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE; - ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY; - ampdu->max_pdu = AUTO; - ampdu->dur = AMPDU_MAX_DUR; - - ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD; - /* - * bump max ampdu rcv size to 64k for all 11n - * devices except 4321A0 and 4321A1 - */ - if (BRCMS_ISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2)) - ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_32K; - else - ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_64K; - ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT; - ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT; - - for (i = 0; i < AMPDU_MAX_SCB_TID; i++) { - ampdu->retry_limit_tid[i] = ampdu->retry_limit; - ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit; - } - - brcms_c_scb_ampdu_update_max_txlen(ampdu, ampdu->dur); - ampdu->mfbr = false; - /* try to set ampdu to the default value */ - brcms_c_ampdu_set(ampdu, wlc->pub->_ampdu); - - ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL; - brcms_c_ffpld_init(ampdu); - - return ampdu; -} - -void brcms_c_ampdu_detach(struct ampdu_info *ampdu) -{ - kfree(ampdu); -} - -static void brcms_c_scb_ampdu_update_config(struct ampdu_info *ampdu, - struct scb *scb) -{ - struct scb_ampdu *scb_ampdu = &scb->scb_ampdu; - int i; - - scb_ampdu->max_pdu = AMPDU_NUM_MPDU; - - /* go back to legacy size if some preloading is occurring */ - for (i = 0; i < NUM_FFPLD_FIFO; i++) { - if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR) - scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY; - } - - /* apply user override */ - if (ampdu->max_pdu != AUTO) - scb_ampdu->max_pdu = (u8) ampdu->max_pdu; - - scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, - AMPDU_SCB_MAX_RELEASE); - - if (scb_ampdu->max_rx_ampdu_bytes) - scb_ampdu->release = min_t(u8, scb_ampdu->release, - scb_ampdu->max_rx_ampdu_bytes / 1600); - - scb_ampdu->release = min(scb_ampdu->release, - ampdu->fifo_tb[TX_AC_BE_FIFO]. - mcs2ampdu_table[FFPLD_MAX_MCS]); -} - -static void brcms_c_scb_ampdu_update_config_all(struct ampdu_info *ampdu) -{ - brcms_c_scb_ampdu_update_config(ampdu, &du->wlc->pri_scb); -} - -static void brcms_c_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f) -{ - int i; - u32 phy_rate, dma_rate, tmp; - u8 max_mpdu; - struct brcms_fifo_info *fifo = (ampdu->fifo_tb + f); - - /* recompute the dma rate */ - /* note : we divide/multiply by 100 to avoid integer overflows */ - max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], - AMPDU_NUM_MPDU_LEGACY); - phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false); - dma_rate = - (((phy_rate / 100) * - (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size)) - / (max_mpdu * FFPLD_MPDU_SIZE)) * 100; - fifo->dmaxferrate = dma_rate; - - /* fill up the mcs2ampdu table; do not recalc the last mcs */ - dma_rate = dma_rate >> 7; - for (i = 0; i < FFPLD_MAX_MCS; i++) { - /* shifting to keep it within integer range */ - phy_rate = mcs_2_rate(i, true, false) >> 7; - if (phy_rate > dma_rate) { - tmp = ((fifo->ampdu_pld_size * phy_rate) / - ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1; - tmp = min_t(u32, tmp, 255); - fifo->mcs2ampdu_table[i] = (u8) tmp; - } - } -} - -/* evaluate the dma transfer rate using the tx underflows as feedback. - * If necessary, increase tx fifo preloading. If not enough, - * decrease maximum ampdu size for each mcs till underflows stop - * Return 1 if pre-loading not active, -1 if not an underflow event, - * 0 if pre-loading module took care of the event. - */ -static int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid) -{ - struct ampdu_info *ampdu = wlc->ampdu; - u32 phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false); - u32 txunfl_ratio; - u8 max_mpdu; - u32 current_ampdu_cnt = 0; - u16 max_pld_size; - u32 new_txunfl; - struct brcms_fifo_info *fifo = (ampdu->fifo_tb + fid); - uint xmtfifo_sz; - u16 cur_txunfl; - - /* return if we got here for a different reason than underflows */ - cur_txunfl = brcms_b_read_shm(wlc->hw, - M_UCODE_MACSTAT + - offsetof(struct macstat, txfunfl[fid])); - new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl); - if (new_txunfl == 0) { - brcms_dbg_ht(wlc->hw->d11core, - "TX status FRAG set but no tx underflows\n"); - return -1; - } - fifo->prev_txfunfl = cur_txunfl; - - if (!ampdu->tx_max_funl) - return 1; - - /* check if fifo is big enough */ - if (brcms_b_xmtfifo_sz_get(wlc->hw, fid, &xmtfifo_sz)) - return -1; - - if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd) - return 1; - - max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd; - fifo->accum_txfunfl += new_txunfl; - - /* we need to wait for at least 10 underflows */ - if (fifo->accum_txfunfl < 10) - return 0; - - brcms_dbg_ht(wlc->hw->d11core, "ampdu_count %d tx_underflows %d\n", - current_ampdu_cnt, fifo->accum_txfunfl); - - /* - compute the current ratio of tx unfl per ampdu. - When the current ampdu count becomes too - big while the ratio remains small, we reset - the current count in order to not - introduce too big of a latency in detecting a - large amount of tx underflows later. - */ - - txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl; - - if (txunfl_ratio > ampdu->tx_max_funl) { - if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) - fifo->accum_txfunfl = 0; - - return 0; - } - max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], - AMPDU_NUM_MPDU_LEGACY); - - /* In case max value max_pdu is already lower than - the fifo depth, there is nothing more we can do. - */ - - if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) { - fifo->accum_txfunfl = 0; - return 0; - } - - if (fifo->ampdu_pld_size < max_pld_size) { - - /* increment by TX_FIFO_PLD_INC bytes */ - fifo->ampdu_pld_size += FFPLD_PLD_INCR; - if (fifo->ampdu_pld_size > max_pld_size) - fifo->ampdu_pld_size = max_pld_size; - - /* update scb release size */ - brcms_c_scb_ampdu_update_config_all(ampdu); - - /* - * compute a new dma xfer rate for max_mpdu @ max mcs. - * This is the minimum dma rate that can achieve no - * underflow condition for the current mpdu size. - * - * note : we divide/multiply by 100 to avoid integer overflows - */ - fifo->dmaxferrate = - (((phy_rate / 100) * - (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size)) - / (max_mpdu * FFPLD_MPDU_SIZE)) * 100; - - brcms_dbg_ht(wlc->hw->d11core, - "DMA estimated transfer rate %d; " - "pre-load size %d\n", - fifo->dmaxferrate, fifo->ampdu_pld_size); - } else { - - /* decrease ampdu size */ - if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) { - if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255) - fifo->mcs2ampdu_table[FFPLD_MAX_MCS] = - AMPDU_NUM_MPDU_LEGACY - 1; - else - fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1; - - /* recompute the table */ - brcms_c_ffpld_calc_mcs2ampdu_table(ampdu, fid); - - /* update scb release size */ - brcms_c_scb_ampdu_update_config_all(ampdu); - } - } - fifo->accum_txfunfl = 0; - return 0; -} - -void -brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, - u8 ba_wsize, /* negotiated ba window size (in pdu) */ - uint max_rx_ampdu_bytes) /* from ht_cap in beacon */ -{ - struct scb_ampdu *scb_ampdu; - struct scb_ampdu_tid_ini *ini; - struct ampdu_info *ampdu = wlc->ampdu; - struct scb *scb = &wlc->pri_scb; - scb_ampdu = &scb->scb_ampdu; - - if (!ampdu->ini_enable[tid]) { - brcms_err(wlc->hw->d11core, "%s: Rejecting tid %d\n", - __func__, tid); - return; - } - - ini = &scb_ampdu->ini[tid]; - ini->tid = tid; - ini->scb = scb_ampdu->scb; - ini->ba_wsize = ba_wsize; - scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes; -} - -void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, - struct brcms_c_info *wlc) -{ - session->wlc = wlc; - skb_queue_head_init(&session->skb_list); - session->max_ampdu_len = 0; /* determined from first MPDU */ - session->max_ampdu_frames = 0; /* determined from first MPDU */ - session->ampdu_len = 0; - session->dma_len = 0; -} - -/* - * Preps the given packet for AMPDU based on the session data. If the - * frame cannot be accomodated in the current session, -ENOSPC is - * returned. - */ -int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, - struct sk_buff *p) -{ - struct brcms_c_info *wlc = session->wlc; - struct ampdu_info *ampdu = wlc->ampdu; - struct scb *scb = &wlc->pri_scb; - struct scb_ampdu *scb_ampdu = &scb->scb_ampdu; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p); - struct ieee80211_tx_rate *txrate = tx_info->status.rates; - struct d11txh *txh = (struct d11txh *)p->data; - unsigned ampdu_frames; - u8 ndelim, tid; - u8 *plcp; - uint len; - u16 mcl; - bool fbr_iscck; - bool rr; - - ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; - plcp = (u8 *)(txh + 1); - fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03); - len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : - BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); - len = roundup(len, 4) + (ndelim + 1) * AMPDU_DELIMITER_LEN; - - ampdu_frames = skb_queue_len(&session->skb_list); - if (ampdu_frames != 0) { - struct sk_buff *first; - - if (ampdu_frames + 1 > session->max_ampdu_frames || - session->ampdu_len + len > session->max_ampdu_len) - return -ENOSPC; - - /* - * We aren't really out of space if the new frame is of - * a different priority, but we want the same behaviour - * so return -ENOSPC anyway. - * - * XXX: The old AMPDU code did this, but is it really - * necessary? - */ - first = skb_peek(&session->skb_list); - if (p->priority != first->priority) - return -ENOSPC; - } - - /* - * Now that we're sure this frame can be accomodated, update the - * session information. - */ - session->ampdu_len += len; - session->dma_len += p->len; - - tid = (u8)p->priority; - - /* Handle retry limits */ - if (txrate[0].count <= ampdu->rr_retry_limit_tid[tid]) { - txrate[0].count++; - rr = true; - } else { - txrate[1].count++; - rr = false; - } - - if (ampdu_frames == 0) { - u8 plcp0, plcp3, is40, sgi, mcs; - uint fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; - struct brcms_fifo_info *f = &du->fifo_tb[fifo]; - - if (rr) { - plcp0 = plcp[0]; - plcp3 = plcp[3]; - } else { - plcp0 = txh->FragPLCPFallback[0]; - plcp3 = txh->FragPLCPFallback[3]; - - } - - /* Limit AMPDU size based on MCS */ - is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; - sgi = plcp3_issgi(plcp3) ? 1 : 0; - mcs = plcp0 & ~MIMO_PLCP_40MHZ; - session->max_ampdu_len = min(scb_ampdu->max_rx_ampdu_bytes, - ampdu->max_txlen[mcs][is40][sgi]); - - session->max_ampdu_frames = scb_ampdu->max_pdu; - if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { - session->max_ampdu_frames = - min_t(u16, f->mcs2ampdu_table[mcs], - session->max_ampdu_frames); - } - } - - /* - * Treat all frames as "middle" frames of AMPDU here. First and - * last frames must be fixed up after all MPDUs have been prepped. - */ - mcl = le16_to_cpu(txh->MacTxControlLow); - mcl &= ~TXC_AMPDU_MASK; - mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT); - mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS); - txh->MacTxControlLow = cpu_to_le16(mcl); - txh->PreloadSize = 0; /* always default to 0 */ - - skb_queue_tail(&session->skb_list, p); - - return 0; -} - -void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session) -{ - struct brcms_c_info *wlc = session->wlc; - struct ampdu_info *ampdu = wlc->ampdu; - struct sk_buff *first, *last; - struct d11txh *txh; - struct ieee80211_tx_info *tx_info; - struct ieee80211_tx_rate *txrate; - u8 ndelim; - u8 *plcp; - uint len; - uint fifo; - struct brcms_fifo_info *f; - u16 mcl; - bool fbr; - bool fbr_iscck; - struct ieee80211_rts *rts; - bool use_rts = false, use_cts = false; - u16 dma_len = session->dma_len; - u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; - u32 rspec = 0, rspec_fallback = 0; - u32 rts_rspec = 0, rts_rspec_fallback = 0; - u8 plcp0, plcp3, is40, sgi, mcs; - u16 mch; - u8 preamble_type = BRCMS_GF_PREAMBLE; - u8 fbr_preamble_type = BRCMS_GF_PREAMBLE; - u8 rts_preamble_type = BRCMS_LONG_PREAMBLE; - u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE; - - if (skb_queue_empty(&session->skb_list)) - return; - - first = skb_peek(&session->skb_list); - last = skb_peek_tail(&session->skb_list); - - /* Need to fix up last MPDU first to adjust AMPDU length */ - txh = (struct d11txh *)last->data; - fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; - f = &du->fifo_tb[fifo]; - - mcl = le16_to_cpu(txh->MacTxControlLow); - mcl &= ~TXC_AMPDU_MASK; - mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT); - txh->MacTxControlLow = cpu_to_le16(mcl); - - /* remove the null delimiter after last mpdu */ - ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; - txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0; - session->ampdu_len -= ndelim * AMPDU_DELIMITER_LEN; - - /* remove the pad len from last mpdu */ - fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0); - len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : - BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); - session->ampdu_len -= roundup(len, 4) - len; - - /* Now fix up the first MPDU */ - tx_info = IEEE80211_SKB_CB(first); - txrate = tx_info->status.rates; - txh = (struct d11txh *)first->data; - plcp = (u8 *)(txh + 1); - rts = (struct ieee80211_rts *)&txh->rts_frame; - - mcl = le16_to_cpu(txh->MacTxControlLow); - /* If only one MPDU leave it marked as last */ - if (first != last) { - mcl &= ~TXC_AMPDU_MASK; - mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT); - } - mcl |= TXC_STARTMSDU; - if (ieee80211_is_rts(rts->frame_control)) { - mcl |= TXC_SENDRTS; - use_rts = true; - } - if (ieee80211_is_cts(rts->frame_control)) { - mcl |= TXC_SENDCTS; - use_cts = true; - } - txh->MacTxControlLow = cpu_to_le16(mcl); - - fbr = txrate[1].count > 0; - if (!fbr) { - plcp0 = plcp[0]; - plcp3 = plcp[3]; - } else { - plcp0 = txh->FragPLCPFallback[0]; - plcp3 = txh->FragPLCPFallback[3]; - } - is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; - sgi = plcp3_issgi(plcp3) ? 1 : 0; - mcs = plcp0 & ~MIMO_PLCP_40MHZ; - - if (is40) { - if (CHSPEC_SB_UPPER(wlc_phy_chanspec_get(wlc->band->pi))) - mimo_ctlchbw = PHY_TXC1_BW_20MHZ_UP; - else - mimo_ctlchbw = PHY_TXC1_BW_20MHZ; - } - - /* rebuild the rspec and rspec_fallback */ - rspec = RSPEC_MIMORATE; - rspec |= plcp[0] & ~MIMO_PLCP_40MHZ; - if (plcp[0] & MIMO_PLCP_40MHZ) - rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); - - fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03); - if (fbr_iscck) { - rspec_fallback = - cck_rspec(cck_phy2mac_rate(txh->FragPLCPFallback[0])); - } else { - rspec_fallback = RSPEC_MIMORATE; - rspec_fallback |= txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ; - if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ) - rspec_fallback |= PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT; - } - - if (use_rts || use_cts) { - rts_rspec = - brcms_c_rspec_to_rts_rspec(wlc, rspec, - false, mimo_ctlchbw); - rts_rspec_fallback = - brcms_c_rspec_to_rts_rspec(wlc, rspec_fallback, - false, mimo_ctlchbw); - } - - BRCMS_SET_MIMO_PLCP_LEN(plcp, session->ampdu_len); - /* mark plcp to indicate ampdu */ - BRCMS_SET_MIMO_PLCP_AMPDU(plcp); - - /* reset the mixed mode header durations */ - if (txh->MModeLen) { - u16 mmodelen = brcms_c_calc_lsig_len(wlc, rspec, - session->ampdu_len); - txh->MModeLen = cpu_to_le16(mmodelen); - preamble_type = BRCMS_MM_PREAMBLE; - } - if (txh->MModeFbrLen) { - u16 mmfbrlen = brcms_c_calc_lsig_len(wlc, rspec_fallback, - session->ampdu_len); - txh->MModeFbrLen = cpu_to_le16(mmfbrlen); - fbr_preamble_type = BRCMS_MM_PREAMBLE; - } - - /* set the preload length */ - if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { - dma_len = min(dma_len, f->ampdu_pld_size); - txh->PreloadSize = cpu_to_le16(dma_len); - } else { - txh->PreloadSize = 0; - } - - mch = le16_to_cpu(txh->MacTxControlHigh); - - /* update RTS dur fields */ - if (use_rts || use_cts) { - u16 durid; - if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) == - TXC_PREAMBLE_RTS_MAIN_SHORT) - rts_preamble_type = BRCMS_SHORT_PREAMBLE; - - if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) == - TXC_PREAMBLE_RTS_FB_SHORT) - rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE; - - durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec, - rspec, rts_preamble_type, - preamble_type, - session->ampdu_len, true); - rts->duration = cpu_to_le16(durid); - durid = brcms_c_compute_rtscts_dur(wlc, use_cts, - rts_rspec_fallback, - rspec_fallback, - rts_fbr_preamble_type, - fbr_preamble_type, - session->ampdu_len, true); - txh->RTSDurFallback = cpu_to_le16(durid); - /* set TxFesTimeNormal */ - txh->TxFesTimeNormal = rts->duration; - /* set fallback rate version of TxFesTimeNormal */ - txh->TxFesTimeFallback = txh->RTSDurFallback; - } - - /* set flag and plcp for fallback rate */ - if (fbr) { - mch |= TXC_AMPDU_FBR; - txh->MacTxControlHigh = cpu_to_le16(mch); - BRCMS_SET_MIMO_PLCP_AMPDU(plcp); - BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback); - } - - brcms_dbg_ht(wlc->hw->d11core, "wl%d: count %d ampdu_len %d\n", - wlc->pub->unit, skb_queue_len(&session->skb_list), - session->ampdu_len); -} - -static void -brcms_c_ampdu_rate_status(struct brcms_c_info *wlc, - struct ieee80211_tx_info *tx_info, - struct tx_status *txs, u8 mcs) -{ - struct ieee80211_tx_rate *txrate = tx_info->status.rates; - int i; - - /* clear the rest of the rates */ - for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) { - txrate[i].idx = -1; - txrate[i].count = 0; - } -} - -static void -brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, - struct sk_buff *p, struct tx_status *txs, - u32 s1, u32 s2) -{ - struct scb_ampdu *scb_ampdu; - struct brcms_c_info *wlc = ampdu->wlc; - struct scb_ampdu_tid_ini *ini; - u8 bitmap[8], queue, tid; - struct d11txh *txh; - u8 *plcp; - struct ieee80211_hdr *h; - u16 seq, start_seq = 0, bindex, index, mcl; - u8 mcs = 0; - bool ba_recd = false, ack_recd = false; - u8 suc_mpdu = 0, tot_mpdu = 0; - uint supr_status; - bool update_rate = true, retry = true, tx_error = false; - u16 mimoantsel = 0; - u8 antselid = 0; - u8 retry_limit, rr_retry_limit; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p); - -#ifdef DEBUG - u8 hole[AMPDU_MAX_MPDU]; - memset(hole, 0, sizeof(hole)); -#endif - - scb_ampdu = &scb->scb_ampdu; - tid = (u8) (p->priority); - - ini = &scb_ampdu->ini[tid]; - retry_limit = ampdu->retry_limit_tid[tid]; - rr_retry_limit = ampdu->rr_retry_limit_tid[tid]; - memset(bitmap, 0, sizeof(bitmap)); - queue = txs->frameid & TXFID_QUEUE_MASK; - supr_status = txs->status & TX_STATUS_SUPR_MASK; - - if (txs->status & TX_STATUS_ACK_RCV) { - if (TX_STATUS_SUPR_UF == supr_status) - update_rate = false; - - WARN_ON(!(txs->status & TX_STATUS_INTERMEDIATE)); - start_seq = txs->sequence >> SEQNUM_SHIFT; - bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >> - TX_STATUS_BA_BMAP03_SHIFT; - - WARN_ON(s1 & TX_STATUS_INTERMEDIATE); - WARN_ON(!(s1 & TX_STATUS_AMPDU)); - - bitmap[0] |= - (s1 & TX_STATUS_BA_BMAP47_MASK) << - TX_STATUS_BA_BMAP47_SHIFT; - bitmap[1] = (s1 >> 8) & 0xff; - bitmap[2] = (s1 >> 16) & 0xff; - bitmap[3] = (s1 >> 24) & 0xff; - - bitmap[4] = s2 & 0xff; - bitmap[5] = (s2 >> 8) & 0xff; - bitmap[6] = (s2 >> 16) & 0xff; - bitmap[7] = (s2 >> 24) & 0xff; - - ba_recd = true; - } else { - if (supr_status) { - update_rate = false; - if (supr_status == TX_STATUS_SUPR_BADCH) { - brcms_dbg_ht(wlc->hw->d11core, - "%s: Pkt tx suppressed, illegal channel possibly %d\n", - __func__, CHSPEC_CHANNEL( - wlc->default_bss->chanspec)); - } else { - if (supr_status != TX_STATUS_SUPR_FRAG) - brcms_err(wlc->hw->d11core, - "%s: supr_status 0x%x\n", - __func__, supr_status); - } - /* no need to retry for badch; will fail again */ - if (supr_status == TX_STATUS_SUPR_BADCH || - supr_status == TX_STATUS_SUPR_EXPTIME) { - retry = false; - } else if (supr_status == TX_STATUS_SUPR_EXPTIME) { - /* TX underflow: - * try tuning pre-loading or ampdu size - */ - } else if (supr_status == TX_STATUS_SUPR_FRAG) { - /* - * if there were underflows, but pre-loading - * is not active, notify rate adaptation. - */ - if (brcms_c_ffpld_check_txfunfl(wlc, queue) > 0) - tx_error = true; - } - } else if (txs->phyerr) { - update_rate = false; - brcms_dbg_ht(wlc->hw->d11core, - "%s: ampdu tx phy error (0x%x)\n", - __func__, txs->phyerr); - } - } - - /* loop through all pkts and retry if not acked */ - while (p) { - tx_info = IEEE80211_SKB_CB(p); - txh = (struct d11txh *) p->data; - mcl = le16_to_cpu(txh->MacTxControlLow); - plcp = (u8 *) (txh + 1); - h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN); - seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT; - - trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh)); - - if (tot_mpdu == 0) { - mcs = plcp[0] & MIMO_PLCP_MCS_MASK; - mimoantsel = le16_to_cpu(txh->ABI_MimoAntSel); - } - - index = TX_SEQ_TO_INDEX(seq); - ack_recd = false; - if (ba_recd) { - bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX); - brcms_dbg_ht(wlc->hw->d11core, - "tid %d seq %d, start_seq %d, bindex %d set %d, index %d\n", - tid, seq, start_seq, bindex, - isset(bitmap, bindex), index); - /* if acked then clear bit and free packet */ - if ((bindex < AMPDU_TX_BA_MAX_WSIZE) - && isset(bitmap, bindex)) { - ini->txretry[index] = 0; - - /* - * ampdu_ack_len: - * number of acked aggregated frames - */ - /* ampdu_len: number of aggregated frames */ - brcms_c_ampdu_rate_status(wlc, tx_info, txs, - mcs); - tx_info->flags |= IEEE80211_TX_STAT_ACK; - tx_info->flags |= IEEE80211_TX_STAT_AMPDU; - tx_info->status.ampdu_ack_len = - tx_info->status.ampdu_len = 1; - - skb_pull(p, D11_PHY_HDR_LEN); - skb_pull(p, D11_TXH_LEN); - - ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, - p); - ack_recd = true; - suc_mpdu++; - } - } - /* either retransmit or send bar if ack not recd */ - if (!ack_recd) { - if (retry && (ini->txretry[index] < (int)retry_limit)) { - int ret; - ini->txretry[index]++; - ret = brcms_c_txfifo(wlc, queue, p); - /* - * We shouldn't be out of space in the DMA - * ring here since we're reinserting a frame - * that was just pulled out. - */ - WARN_ONCE(ret, "queue %d out of txds\n", queue); - } else { - /* Retry timeout */ - ieee80211_tx_info_clear_status(tx_info); - tx_info->status.ampdu_ack_len = 0; - tx_info->status.ampdu_len = 1; - tx_info->flags |= - IEEE80211_TX_STAT_AMPDU_NO_BACK; - skb_pull(p, D11_PHY_HDR_LEN); - skb_pull(p, D11_TXH_LEN); - brcms_dbg_ht(wlc->hw->d11core, - "BA Timeout, seq %d\n", - seq); - ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, - p); - } - } - tot_mpdu++; - - /* break out if last packet of ampdu */ - if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) == - TXC_AMPDU_LAST) - break; - - p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); - } - - /* update rate state */ - antselid = brcms_c_antsel_antsel2id(wlc->asi, mimoantsel); -} - -void -brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, - struct sk_buff *p, struct tx_status *txs) -{ - struct scb_ampdu *scb_ampdu; - struct brcms_c_info *wlc = ampdu->wlc; - struct scb_ampdu_tid_ini *ini; - u32 s1 = 0, s2 = 0; - struct ieee80211_tx_info *tx_info; - - tx_info = IEEE80211_SKB_CB(p); - - /* BMAC_NOTE: For the split driver, second level txstatus comes later - * So if the ACK was received then wait for the second level else just - * call the first one - */ - if (txs->status & TX_STATUS_ACK_RCV) { - u8 status_delay = 0; - - /* wait till the next 8 bytes of txstatus is available */ - s1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus)); - while ((s1 & TXS_V) == 0) { - udelay(1); - status_delay++; - if (status_delay > 10) - return; /* error condition */ - s1 = bcma_read32(wlc->hw->d11core, - D11REGOFFS(frmtxstatus)); - } - - s2 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus2)); - } - - if (scb) { - scb_ampdu = &scb->scb_ampdu; - ini = &scb_ampdu->ini[p->priority]; - brcms_c_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2); - } else { - /* loop through all pkts and free */ - u8 queue = txs->frameid & TXFID_QUEUE_MASK; - struct d11txh *txh; - u16 mcl; - while (p) { - tx_info = IEEE80211_SKB_CB(p); - txh = (struct d11txh *) p->data; - trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, - sizeof(*txh)); - mcl = le16_to_cpu(txh->MacTxControlLow); - brcmu_pkt_buf_free_skb(p); - /* break out if last packet of ampdu */ - if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) == - TXC_AMPDU_LAST) - break; - p = dma_getnexttxp(wlc->hw->di[queue], - DMA_RANGE_TRANSMITTED); - } - } -} - -void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc) -{ - char template[T_RAM_ACCESS_SZ * 2]; - - /* driver needs to write the ta in the template; ta is at offset 16 */ - memset(template, 0, sizeof(template)); - memcpy(template, wlc->pub->cur_etheraddr, ETH_ALEN); - brcms_b_write_template_ram(wlc->hw, (T_BA_TPL_BASE + 16), - (T_RAM_ACCESS_SZ * 2), - template); -} - -bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid) -{ - return wlc->ampdu->ini_enable[tid]; -} - -void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu) -{ - struct brcms_c_info *wlc = ampdu->wlc; - - /* - * Extend ucode internal watchdog timer to - * match larger received frames - */ - if ((ampdu->rx_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) == - IEEE80211_HT_MAX_AMPDU_64K) { - brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX); - brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX); - } else { - brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF); - brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF); - } -} - -/* - * callback function that helps invalidating ampdu packets in a DMA queue - */ -static void dma_cb_fn_ampdu(void *txi, void *arg_a) -{ - struct ieee80211_sta *sta = arg_a; - struct ieee80211_tx_info *tx_info = (struct ieee80211_tx_info *)txi; - - if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && - (tx_info->rate_driver_data[0] == sta || sta == NULL)) - tx_info->rate_driver_data[0] = NULL; -} - -/* - * When a remote party is no longer available for ampdu communication, any - * pending tx ampdu packets in the driver have to be flushed. - */ -void brcms_c_ampdu_flush(struct brcms_c_info *wlc, - struct ieee80211_sta *sta, u16 tid) -{ - brcms_c_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu); -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h deleted file mode 100644 index 03bdcf29bd50..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_AMPDU_H_ -#define _BRCM_AMPDU_H_ - -/* - * Data structure representing an in-progress session for accumulating - * frames for AMPDU. - * - * wlc: pointer to common driver data - * skb_list: queue of skb's for AMPDU - * max_ampdu_len: maximum length for this AMPDU - * max_ampdu_frames: maximum number of frames for this AMPDU - * ampdu_len: total number of bytes accumulated for this AMPDU - * dma_len: DMA length of this AMPDU - */ -struct brcms_ampdu_session { - struct brcms_c_info *wlc; - struct sk_buff_head skb_list; - unsigned max_ampdu_len; - u16 max_ampdu_frames; - u16 ampdu_len; - u16 dma_len; -}; - -void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, - struct brcms_c_info *wlc); -int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, - struct sk_buff *p); -void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session); - -struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc); -void brcms_c_ampdu_detach(struct ampdu_info *ampdu); -void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, - struct sk_buff *p, struct tx_status *txs); -void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc); -void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu); - -#endif /* _BRCM_AMPDU_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/antsel.c b/drivers/net/wireless/brcm80211/brcmsmac/antsel.c deleted file mode 100644 index 54c616919590..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/antsel.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (c) 2010 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 "types.h" -#include "main.h" -#include "phy_shim.h" -#include "antsel.h" -#include "debug.h" - -#define ANT_SELCFG_AUTO 0x80 /* bit indicates antenna sel AUTO */ -#define ANT_SELCFG_MASK 0x33 /* antenna configuration mask */ -#define ANT_SELCFG_TX_UNICAST 0 /* unicast tx antenna configuration */ -#define ANT_SELCFG_RX_UNICAST 1 /* unicast rx antenna configuration */ -#define ANT_SELCFG_TX_DEF 2 /* default tx antenna configuration */ -#define ANT_SELCFG_RX_DEF 3 /* default rx antenna configuration */ - -/* useful macros */ -#define BRCMS_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf) -#define BRCMS_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf) -#define BRCMS_ANTIDX_11N(ant) (((BRCMS_ANTSEL_11N_0(ant)) << 2) +\ - (BRCMS_ANTSEL_11N_1(ant))) -#define BRCMS_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) -#define BRCMS_ANTSEL_11N(ant) ((ant) & ANT_SELCFG_MASK) - -/* antenna switch */ -/* defines for no boardlevel antenna diversity */ -#define ANT_SELCFG_DEF_2x2 0x01 /* default antenna configuration */ - -/* 2x3 antdiv defines and tables for GPIO communication */ -#define ANT_SELCFG_NUM_2x3 3 -#define ANT_SELCFG_DEF_2x3 0x01 /* default antenna configuration */ - -/* 2x4 antdiv rev4 defines and tables for GPIO communication */ -#define ANT_SELCFG_NUM_2x4 4 -#define ANT_SELCFG_DEF_2x4 0x02 /* default antenna configuration */ - -static const u16 mimo_2x4_div_antselpat_tbl[] = { - 0, 0, 0x9, 0xa, /* ant0: 0 ant1: 2,3 */ - 0, 0, 0x5, 0x6, /* ant0: 1 ant1: 2,3 */ - 0, 0, 0, 0, /* n.a. */ - 0, 0, 0, 0 /* n.a. */ -}; - -static const u8 mimo_2x4_div_antselid_tbl[16] = { - 0, 0, 0, 0, 0, 2, 3, 0, - 0, 0, 1, 0, 0, 0, 0, 0 /* pat to antselid */ -}; - -static const u16 mimo_2x3_div_antselpat_tbl[] = { - 16, 0, 1, 16, /* ant0: 0 ant1: 1,2 */ - 16, 16, 16, 16, /* n.a. */ - 16, 2, 16, 16, /* ant0: 2 ant1: 1 */ - 16, 16, 16, 16 /* n.a. */ -}; - -static const u8 mimo_2x3_div_antselid_tbl[16] = { - 0, 1, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 /* pat to antselid */ -}; - -/* boardlevel antenna selection: init antenna selection structure */ -static void -brcms_c_antsel_init_cfg(struct antsel_info *asi, struct brcms_antselcfg *antsel, - bool auto_sel) -{ - if (asi->antsel_type == ANTSEL_2x3) { - u8 antcfg_def = ANT_SELCFG_DEF_2x3 | - ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0); - antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def; - antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def; - antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def; - antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def; - antsel->num_antcfg = ANT_SELCFG_NUM_2x3; - - } else if (asi->antsel_type == ANTSEL_2x4) { - - antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4; - antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4; - antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4; - antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4; - antsel->num_antcfg = ANT_SELCFG_NUM_2x4; - - } else { /* no antenna selection available */ - - antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2; - antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2; - antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2; - antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2; - antsel->num_antcfg = 0; - } -} - -struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc) -{ - struct antsel_info *asi; - struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; - - asi = kzalloc(sizeof(struct antsel_info), GFP_ATOMIC); - if (!asi) - return NULL; - - asi->wlc = wlc; - asi->pub = wlc->pub; - asi->antsel_type = ANTSEL_NA; - asi->antsel_avail = false; - asi->antsel_antswitch = sprom->antswitch; - - if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) { - switch (asi->antsel_antswitch) { - case ANTSWITCH_TYPE_1: - case ANTSWITCH_TYPE_2: - case ANTSWITCH_TYPE_3: - /* 4321/2 board with 2x3 switch logic */ - asi->antsel_type = ANTSEL_2x3; - /* Antenna selection availability */ - if ((sprom->ant_available_bg == 7) || - (sprom->ant_available_a == 7)) { - asi->antsel_avail = true; - } else if ( - sprom->ant_available_bg == 3 || - sprom->ant_available_a == 3) { - asi->antsel_avail = false; - } else { - asi->antsel_avail = false; - brcms_err(wlc->hw->d11core, - "antsel_attach: 2o3 " - "board cfg invalid\n"); - } - - break; - default: - break; - } - } else if ((asi->pub->sromrev == 4) && - (sprom->ant_available_bg == 7) && - (sprom->ant_available_a == 0)) { - /* hack to match old 4321CB2 cards with 2of3 antenna switch */ - asi->antsel_type = ANTSEL_2x3; - asi->antsel_avail = true; - } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) { - asi->antsel_type = ANTSEL_2x4; - asi->antsel_avail = true; - } - - /* Set the antenna selection type for the low driver */ - brcms_b_antsel_type_set(wlc->hw, asi->antsel_type); - - /* Init (auto/manual) antenna selection */ - brcms_c_antsel_init_cfg(asi, &asi->antcfg_11n, true); - brcms_c_antsel_init_cfg(asi, &asi->antcfg_cur, true); - - return asi; -} - -void brcms_c_antsel_detach(struct antsel_info *asi) -{ - kfree(asi); -} - -/* - * boardlevel antenna selection: - * convert ant_cfg to mimo_antsel (ucode interface) - */ -static u16 brcms_c_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg) -{ - u8 idx = BRCMS_ANTIDX_11N(BRCMS_ANTSEL_11N(ant_cfg)); - u16 mimo_antsel = 0; - - if (asi->antsel_type == ANTSEL_2x4) { - /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ - mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf); - return mimo_antsel; - - } else if (asi->antsel_type == ANTSEL_2x3) { - /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ - mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf); - return mimo_antsel; - } - - return mimo_antsel; -} - -/* boardlevel antenna selection: ucode interface control */ -static int brcms_c_antsel_cfgupd(struct antsel_info *asi, - struct brcms_antselcfg *antsel) -{ - struct brcms_c_info *wlc = asi->wlc; - u8 ant_cfg; - u16 mimo_antsel; - - /* 1) Update TX antconfig for all frames that are not unicast data - * (aka default TX) - */ - ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF]; - mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg); - brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_TXDFLT, mimo_antsel); - /* - * Update driver stats for currently selected - * default tx/rx antenna config - */ - asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg; - - /* 2) Update RX antconfig for all frames that are not unicast data - * (aka default RX) - */ - ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF]; - mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg); - brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_RXDFLT, mimo_antsel); - /* - * Update driver stats for currently selected - * default tx/rx antenna config - */ - asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg; - - return 0; -} - -void brcms_c_antsel_init(struct antsel_info *asi) -{ - if ((asi->antsel_type == ANTSEL_2x3) || - (asi->antsel_type == ANTSEL_2x4)) - brcms_c_antsel_cfgupd(asi, &asi->antcfg_11n); -} - -/* boardlevel antenna selection: convert id to ant_cfg */ -static u8 brcms_c_antsel_id2antcfg(struct antsel_info *asi, u8 id) -{ - u8 antcfg = ANT_SELCFG_DEF_2x2; - - if (asi->antsel_type == ANTSEL_2x4) { - /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ - antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2)); - return antcfg; - - } else if (asi->antsel_type == ANTSEL_2x3) { - /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ - antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1)); - return antcfg; - } - - return antcfg; -} - -void -brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel, - u8 antselid, u8 fbantselid, u8 *antcfg, - u8 *fbantcfg) -{ - u8 ant; - - /* if use default, assign it and return */ - if (usedef) { - *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF]; - *fbantcfg = *antcfg; - return; - } - - if (!sel) { - *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; - *fbantcfg = *antcfg; - - } else { - ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; - if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) { - *antcfg = brcms_c_antsel_id2antcfg(asi, antselid); - *fbantcfg = brcms_c_antsel_id2antcfg(asi, fbantselid); - } else { - *antcfg = - asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; - *fbantcfg = *antcfg; - } - } - return; -} - -/* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */ -u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel) -{ - u8 antselid = 0; - - if (asi->antsel_type == ANTSEL_2x4) { - /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ - antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)]; - return antselid; - - } else if (asi->antsel_type == ANTSEL_2x3) { - /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ - antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)]; - return antselid; - } - - return antselid; -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/antsel.h b/drivers/net/wireless/brcm80211/brcmsmac/antsel.h deleted file mode 100644 index a3d487ab1964..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/antsel.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_ANTSEL_H_ -#define _BRCM_ANTSEL_H_ - -struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc); -void brcms_c_antsel_detach(struct antsel_info *asi); -void brcms_c_antsel_init(struct antsel_info *asi); -void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel, - u8 id, u8 fbid, u8 *antcfg, u8 *fbantcfg); -u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel); - -#endif /* _BRCM_ANTSEL_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h deleted file mode 100644 index a0da3248b942..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2011 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. - */ - -#if !defined(__TRACE_BRCMSMAC_H) || defined(TRACE_HEADER_MULTI_READ) -#define __TRACE_BRCMSMAC_H - -#include - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM brcmsmac - -/* - * We define a tracepoint, its arguments, its printk format and its - * 'fast binary record' layout. - */ -TRACE_EVENT(brcms_timer, - /* TPPROTO is the prototype of the function called by this tracepoint */ - TP_PROTO(struct brcms_timer *t), - /* - * TPARGS(firstarg, p) are the parameters names, same as found in the - * prototype. - */ - TP_ARGS(t), - /* - * Fast binary tracing: define the trace record via TP_STRUCT__entry(). - * You can think about it like a regular C structure local variable - * definition. - */ - TP_STRUCT__entry( - __field(uint, ms) - __field(uint, set) - __field(uint, periodic) - ), - TP_fast_assign( - __entry->ms = t->ms; - __entry->set = t->set; - __entry->periodic = t->periodic; - ), - TP_printk( - "ms=%u set=%u periodic=%u", - __entry->ms, __entry->set, __entry->periodic - ) -); - -TRACE_EVENT(brcms_dpc, - TP_PROTO(unsigned long data), - TP_ARGS(data), - TP_STRUCT__entry( - __field(unsigned long, data) - ), - TP_fast_assign( - __entry->data = data; - ), - TP_printk( - "data=%p", - (void *)__entry->data - ) -); - -TRACE_EVENT(brcms_macintstatus, - TP_PROTO(const struct device *dev, int in_isr, u32 macintstatus, - u32 mask), - TP_ARGS(dev, in_isr, macintstatus, mask), - TP_STRUCT__entry( - __string(dev, dev_name(dev)) - __field(int, in_isr) - __field(u32, macintstatus) - __field(u32, mask) - ), - TP_fast_assign( - __assign_str(dev, dev_name(dev)); - __entry->in_isr = in_isr; - __entry->macintstatus = macintstatus; - __entry->mask = mask; - ), - TP_printk("[%s] in_isr=%d macintstatus=%#x mask=%#x", __get_str(dev), - __entry->in_isr, __entry->macintstatus, __entry->mask) -); -#endif /* __TRACE_BRCMSMAC_H */ - -#ifdef CONFIG_BRCM_TRACING - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac -#include - -#endif /* CONFIG_BRCM_TRACING */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h deleted file mode 100644 index 0e8a69ab909f..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2011 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. - */ - -#if !defined(__TRACE_BRCMSMAC_MSG_H) || defined(TRACE_HEADER_MULTI_READ) -#define __TRACE_BRCMSMAC_MSG_H - -#include - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM brcmsmac_msg - -#define MAX_MSG_LEN 100 - -DECLARE_EVENT_CLASS(brcms_msg_event, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf), - TP_STRUCT__entry( - __dynamic_array(char, msg, MAX_MSG_LEN) - ), - TP_fast_assign( - WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), - MAX_MSG_LEN, vaf->fmt, - *vaf->va) >= MAX_MSG_LEN); - ), - TP_printk("%s", __get_str(msg)) -); - -DEFINE_EVENT(brcms_msg_event, brcms_info, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(brcms_msg_event, brcms_warn, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(brcms_msg_event, brcms_err, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(brcms_msg_event, brcms_crit, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -TRACE_EVENT(brcms_dbg, - TP_PROTO(u32 level, const char *func, struct va_format *vaf), - TP_ARGS(level, func, vaf), - TP_STRUCT__entry( - __field(u32, level) - __string(func, func) - __dynamic_array(char, msg, MAX_MSG_LEN) - ), - TP_fast_assign( - __entry->level = level; - __assign_str(func, func); - WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), - MAX_MSG_LEN, vaf->fmt, - *vaf->va) >= MAX_MSG_LEN); - ), - TP_printk("%s: %s", __get_str(func), __get_str(msg)) -); -#endif /* __TRACE_BRCMSMAC_MSG_H */ - -#ifdef CONFIG_BRCM_TRACING - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac_msg -#include - -#endif /* CONFIG_BRCM_TRACING */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h deleted file mode 100644 index cf2cc070f1e5..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2011 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. - */ - -#if !defined(__TRACE_BRCMSMAC_TX_H) || defined(TRACE_HEADER_MULTI_READ) -#define __TRACE_BRCMSMAC_TX_H - -#include - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM brcmsmac_tx - -TRACE_EVENT(brcms_txdesc, - TP_PROTO(const struct device *dev, - void *txh, size_t txh_len), - TP_ARGS(dev, txh, txh_len), - TP_STRUCT__entry( - __string(dev, dev_name(dev)) - __dynamic_array(u8, txh, txh_len) - ), - TP_fast_assign( - __assign_str(dev, dev_name(dev)); - memcpy(__get_dynamic_array(txh), txh, txh_len); - ), - TP_printk("[%s] txdesc", __get_str(dev)) -); - -TRACE_EVENT(brcms_txstatus, - TP_PROTO(const struct device *dev, u16 framelen, u16 frameid, - u16 status, u16 lasttxtime, u16 sequence, u16 phyerr, - u16 ackphyrxsh), - TP_ARGS(dev, framelen, frameid, status, lasttxtime, sequence, phyerr, - ackphyrxsh), - TP_STRUCT__entry( - __string(dev, dev_name(dev)) - __field(u16, framelen) - __field(u16, frameid) - __field(u16, status) - __field(u16, lasttxtime) - __field(u16, sequence) - __field(u16, phyerr) - __field(u16, ackphyrxsh) - ), - TP_fast_assign( - __assign_str(dev, dev_name(dev)); - __entry->framelen = framelen; - __entry->frameid = frameid; - __entry->status = status; - __entry->lasttxtime = lasttxtime; - __entry->sequence = sequence; - __entry->phyerr = phyerr; - __entry->ackphyrxsh = ackphyrxsh; - ), - TP_printk("[%s] FrameId %#04x TxStatus %#04x LastTxTime %#04x " - "Seq %#04x PHYTxStatus %#04x RxAck %#04x", - __get_str(dev), __entry->frameid, __entry->status, - __entry->lasttxtime, __entry->sequence, __entry->phyerr, - __entry->ackphyrxsh) -); - -TRACE_EVENT(brcms_ampdu_session, - TP_PROTO(const struct device *dev, unsigned max_ampdu_len, - u16 max_ampdu_frames, u16 ampdu_len, u16 ampdu_frames, - u16 dma_len), - TP_ARGS(dev, max_ampdu_len, max_ampdu_frames, ampdu_len, ampdu_frames, - dma_len), - TP_STRUCT__entry( - __string(dev, dev_name(dev)) - __field(unsigned, max_ampdu_len) - __field(u16, max_ampdu_frames) - __field(u16, ampdu_len) - __field(u16, ampdu_frames) - __field(u16, dma_len) - ), - TP_fast_assign( - __assign_str(dev, dev_name(dev)); - __entry->max_ampdu_len = max_ampdu_len; - __entry->max_ampdu_frames = max_ampdu_frames; - __entry->ampdu_len = ampdu_len; - __entry->ampdu_frames = ampdu_frames; - __entry->dma_len = dma_len; - ), - TP_printk("[%s] ampdu session max_len=%u max_frames=%u len=%u frames=%u dma_len=%u", - __get_str(dev), __entry->max_ampdu_len, - __entry->max_ampdu_frames, __entry->ampdu_len, - __entry->ampdu_frames, __entry->dma_len) -); -#endif /* __TRACE_BRCMSMAC_TX_H */ - -#ifdef CONFIG_BRCM_TRACING - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac_tx -#include - -#endif /* CONFIG_BRCM_TRACING */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c deleted file mode 100644 index 52fc9eeb5fa5..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2011 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 /* bug in tracepoint.h, it should include this */ - -#ifndef __CHECKER__ -#include "mac80211_if.h" -#define CREATE_TRACE_POINTS -#include "brcms_trace_events.h" -#endif diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h deleted file mode 100644 index cbf2f06436fc..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011 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 __BRCMS_TRACE_EVENTS_H -#define __BRCMS_TRACE_EVENTS_H - -#include -#include -#include -#include "mac80211_if.h" - -#ifndef CONFIG_BRCM_TRACING -#undef TRACE_EVENT -#define TRACE_EVENT(name, proto, ...) \ -static inline void trace_ ## name(proto) {} -#undef DECLARE_EVENT_CLASS -#define DECLARE_EVENT_CLASS(...) -#undef DEFINE_EVENT -#define DEFINE_EVENT(evt_class, name, proto, ...) \ -static inline void trace_ ## name(proto) {} -#endif - -#include "brcms_trace_brcmsmac.h" -#include "brcms_trace_brcmsmac_tx.h" -#include "brcms_trace_brcmsmac_msg.h" - -#endif /* __TRACE_BRCMSMAC_H */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c deleted file mode 100644 index 635ae034c7e5..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ /dev/null @@ -1,774 +0,0 @@ -/* - * Copyright (c) 2010 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 - -#include -#include "pub.h" -#include "phy/phy_hal.h" -#include "main.h" -#include "stf.h" -#include "channel.h" -#include "mac80211_if.h" -#include "debug.h" - -/* QDB() macro takes a dB value and converts to a quarter dB value */ -#define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR) - -#define LOCALE_MIMO_IDX_bn 0 -#define LOCALE_MIMO_IDX_11n 0 - -/* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */ -#define BRCMS_MAXPWR_MIMO_TBL_SIZE 14 - -/* maxpwr mapping to 5GHz band channels: - * maxpwr[0] - channels [34-48] - * maxpwr[1] - channels [52-60] - * maxpwr[2] - channels [62-64] - * maxpwr[3] - channels [100-140] - * maxpwr[4] - channels [149-165] - */ -#define BAND_5G_PWR_LVLS 5 /* 5 power levels for 5G */ - -#define LC(id) LOCALE_MIMO_IDX_ ## id - -#define LOCALES(mimo2, mimo5) \ - {LC(mimo2), LC(mimo5)} - -/* macro to get 5 GHz channel group index for tx power */ -#define CHANNEL_POWER_IDX_5G(c) (((c) < 52) ? 0 : \ - (((c) < 62) ? 1 : \ - (((c) < 100) ? 2 : \ - (((c) < 149) ? 3 : 4)))) - -#define BRCM_2GHZ_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 19, 0) -#define BRCM_2GHZ_2467_2472 REG_RULE(2467-10, 2472+10, 20, 0, 19, \ - NL80211_RRF_NO_IR) - -#define BRCM_5GHZ_5180_5240 REG_RULE(5180-10, 5240+10, 40, 0, 21, \ - NL80211_RRF_NO_IR) -#define BRCM_5GHZ_5260_5320 REG_RULE(5260-10, 5320+10, 40, 0, 21, \ - NL80211_RRF_DFS | \ - NL80211_RRF_NO_IR) -#define BRCM_5GHZ_5500_5700 REG_RULE(5500-10, 5700+10, 40, 0, 21, \ - NL80211_RRF_DFS | \ - NL80211_RRF_NO_IR) -#define BRCM_5GHZ_5745_5825 REG_RULE(5745-10, 5825+10, 40, 0, 21, \ - NL80211_RRF_NO_IR) - -static const struct ieee80211_regdomain brcms_regdom_x2 = { - .n_reg_rules = 6, - .alpha2 = "X2", - .reg_rules = { - BRCM_2GHZ_2412_2462, - BRCM_2GHZ_2467_2472, - BRCM_5GHZ_5180_5240, - BRCM_5GHZ_5260_5320, - BRCM_5GHZ_5500_5700, - BRCM_5GHZ_5745_5825, - } -}; - - /* locale per-channel tx power limits for MIMO frames - * maxpwr arrays are index by channel for 2.4 GHz limits, and - * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel) - */ -struct locale_mimo_info { - /* tx 20 MHz power limits, qdBm units */ - s8 maxpwr20[BRCMS_MAXPWR_MIMO_TBL_SIZE]; - /* tx 40 MHz power limits, qdBm units */ - s8 maxpwr40[BRCMS_MAXPWR_MIMO_TBL_SIZE]; -}; - -/* Country names and abbreviations with locale defined from ISO 3166 */ -struct country_info { - const u8 locale_mimo_2G; /* 2.4G mimo info */ - const u8 locale_mimo_5G; /* 5G mimo info */ -}; - -struct brcms_regd { - struct country_info country; - const struct ieee80211_regdomain *regdomain; -}; - -struct brcms_cm_info { - struct brcms_pub *pub; - struct brcms_c_info *wlc; - const struct brcms_regd *world_regd; -}; - -/* - * MIMO Locale Definitions - 2.4 GHz - */ -static const struct locale_mimo_info locale_bn = { - {QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), - QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), - QDB(13), QDB(13), QDB(13)}, - {0, 0, QDB(13), QDB(13), QDB(13), - QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), - QDB(13), 0, 0}, -}; - -static const struct locale_mimo_info *g_mimo_2g_table[] = { - &locale_bn -}; - -/* - * MIMO Locale Definitions - 5 GHz - */ -static const struct locale_mimo_info locale_11n = { - { /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)}, - {QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)}, -}; - -static const struct locale_mimo_info *g_mimo_5g_table[] = { - &locale_11n -}; - -static const struct brcms_regd cntry_locales[] = { - /* Worldwide RoW 2, must always be at index 0 */ - { - .country = LOCALES(bn, 11n), - .regdomain = &brcms_regdom_x2, - }, -}; - -static const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx) -{ - if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table)) - return NULL; - - return g_mimo_2g_table[locale_idx]; -} - -static const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx) -{ - if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table)) - return NULL; - - return g_mimo_5g_table[locale_idx]; -} - -/* - * Indicates whether the country provided is valid to pass - * to cfg80211 or not. - * - * returns true if valid; false if not. - */ -static bool brcms_c_country_valid(const char *ccode) -{ - /* - * only allow ascii alpha uppercase for the first 2 - * chars. - */ - if (!((0x80 & ccode[0]) == 0 && ccode[0] >= 0x41 && ccode[0] <= 0x5A && - (0x80 & ccode[1]) == 0 && ccode[1] >= 0x41 && ccode[1] <= 0x5A)) - return false; - - /* - * do not match ISO 3166-1 user assigned country codes - * that may be in the driver table - */ - if (!strcmp("AA", ccode) || /* AA */ - !strcmp("ZZ", ccode) || /* ZZ */ - ccode[0] == 'X' || /* XA - XZ */ - (ccode[0] == 'Q' && /* QM - QZ */ - (ccode[1] >= 'M' && ccode[1] <= 'Z'))) - return false; - - if (!strcmp("NA", ccode)) - return false; - - return true; -} - -static const struct brcms_regd *brcms_world_regd(const char *regdom, int len) -{ - const struct brcms_regd *regd = NULL; - int i; - - for (i = 0; i < ARRAY_SIZE(cntry_locales); i++) { - if (!strncmp(regdom, cntry_locales[i].regdomain->alpha2, len)) { - regd = &cntry_locales[i]; - break; - } - } - - return regd; -} - -static const struct brcms_regd *brcms_default_world_regd(void) -{ - return &cntry_locales[0]; -} - -/* JP, J1 - J10 are Japan ccodes */ -static bool brcms_c_japan_ccode(const char *ccode) -{ - return (ccode[0] == 'J' && - (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9'))); -} - -static void -brcms_c_channel_min_txpower_limits_with_local_constraint( - struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr, - u8 local_constraint_qdbm) -{ - int j; - - /* CCK Rates */ - for (j = 0; j < WL_TX_POWER_CCK_NUM; j++) - txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm); - - /* 20 MHz Legacy OFDM SISO */ - for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++) - txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm); - - /* 20 MHz Legacy OFDM CDD */ - for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) - txpwr->ofdm_cdd[j] = - min(txpwr->ofdm_cdd[j], local_constraint_qdbm); - - /* 40 MHz Legacy OFDM SISO */ - for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) - txpwr->ofdm_40_siso[j] = - min(txpwr->ofdm_40_siso[j], local_constraint_qdbm); - - /* 40 MHz Legacy OFDM CDD */ - for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) - txpwr->ofdm_40_cdd[j] = - min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm); - - /* 20MHz MCS 0-7 SISO */ - for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) - txpwr->mcs_20_siso[j] = - min(txpwr->mcs_20_siso[j], local_constraint_qdbm); - - /* 20MHz MCS 0-7 CDD */ - for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) - txpwr->mcs_20_cdd[j] = - min(txpwr->mcs_20_cdd[j], local_constraint_qdbm); - - /* 20MHz MCS 0-7 STBC */ - for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) - txpwr->mcs_20_stbc[j] = - min(txpwr->mcs_20_stbc[j], local_constraint_qdbm); - - /* 20MHz MCS 8-15 MIMO */ - for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++) - txpwr->mcs_20_mimo[j] = - min(txpwr->mcs_20_mimo[j], local_constraint_qdbm); - - /* 40MHz MCS 0-7 SISO */ - for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) - txpwr->mcs_40_siso[j] = - min(txpwr->mcs_40_siso[j], local_constraint_qdbm); - - /* 40MHz MCS 0-7 CDD */ - for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) - txpwr->mcs_40_cdd[j] = - min(txpwr->mcs_40_cdd[j], local_constraint_qdbm); - - /* 40MHz MCS 0-7 STBC */ - for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) - txpwr->mcs_40_stbc[j] = - min(txpwr->mcs_40_stbc[j], local_constraint_qdbm); - - /* 40MHz MCS 8-15 MIMO */ - for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++) - txpwr->mcs_40_mimo[j] = - min(txpwr->mcs_40_mimo[j], local_constraint_qdbm); - - /* 40MHz MCS 32 */ - txpwr->mcs32 = min(txpwr->mcs32, local_constraint_qdbm); - -} - -/* - * set the driver's current country and regulatory information - * using a country code as the source. Look up built in country - * information found with the country code. - */ -static void -brcms_c_set_country(struct brcms_cm_info *wlc_cm, - const struct brcms_regd *regd) -{ - struct brcms_c_info *wlc = wlc_cm->wlc; - - if ((wlc->pub->_n_enab & SUPPORT_11N) != - wlc->protection->nmode_user) - brcms_c_set_nmode(wlc); - - brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); - brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); - - brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); - - return; -} - -struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) -{ - struct brcms_cm_info *wlc_cm; - struct brcms_pub *pub = wlc->pub; - struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; - const char *ccode = sprom->alpha2; - int ccode_len = sizeof(sprom->alpha2); - - wlc_cm = kzalloc(sizeof(struct brcms_cm_info), GFP_ATOMIC); - if (wlc_cm == NULL) - return NULL; - wlc_cm->pub = pub; - wlc_cm->wlc = wlc; - wlc->cmi = wlc_cm; - - /* store the country code for passing up as a regulatory hint */ - wlc_cm->world_regd = brcms_world_regd(ccode, ccode_len); - if (brcms_c_country_valid(ccode)) - strncpy(wlc->pub->srom_ccode, ccode, ccode_len); - - /* - * If no custom world domain is found in the SROM, use the - * default "X2" domain. - */ - if (!wlc_cm->world_regd) { - wlc_cm->world_regd = brcms_default_world_regd(); - ccode = wlc_cm->world_regd->regdomain->alpha2; - ccode_len = BRCM_CNTRY_BUF_SZ - 1; - } - - /* save default country for exiting 11d regulatory mode */ - strncpy(wlc->country_default, ccode, ccode_len); - - /* initialize autocountry_default to driver default */ - strncpy(wlc->autocountry_default, ccode, ccode_len); - - brcms_c_set_country(wlc_cm, wlc_cm->world_regd); - - return wlc_cm; -} - -void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm) -{ - kfree(wlc_cm); -} - -void -brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, - u8 local_constraint_qdbm) -{ - struct brcms_c_info *wlc = wlc_cm->wlc; - struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan; - struct txpwr_limits txpwr; - - brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr); - - brcms_c_channel_min_txpower_limits_with_local_constraint( - wlc_cm, &txpwr, local_constraint_qdbm - ); - - /* set or restore gmode as required by regulatory */ - if (ch->flags & IEEE80211_CHAN_NO_OFDM) - brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false); - else - brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); - - brcms_b_set_chanspec(wlc->hw, chanspec, - !!(ch->flags & IEEE80211_CHAN_NO_IR), - &txpwr); -} - -void -brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, - struct txpwr_limits *txpwr) -{ - struct brcms_c_info *wlc = wlc_cm->wlc; - struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan; - uint i; - uint chan; - int maxpwr; - int delta; - const struct country_info *country; - struct brcms_band *band; - int conducted_max = BRCMS_TXPWR_MAX; - const struct locale_mimo_info *li_mimo; - int maxpwr20, maxpwr40; - int maxpwr_idx; - uint j; - - memset(txpwr, 0, sizeof(struct txpwr_limits)); - - if (WARN_ON(!ch)) - return; - - country = &wlc_cm->world_regd->country; - - chan = CHSPEC_CHANNEL(chanspec); - band = wlc->bandstate[chspec_bandunit(chanspec)]; - li_mimo = (band->bandtype == BRCM_BAND_5G) ? - brcms_c_get_mimo_5g(country->locale_mimo_5G) : - brcms_c_get_mimo_2g(country->locale_mimo_2G); - - delta = band->antgain; - - if (band->bandtype == BRCM_BAND_2G) - conducted_max = QDB(22); - - maxpwr = QDB(ch->max_power) - delta; - maxpwr = max(maxpwr, 0); - maxpwr = min(maxpwr, conducted_max); - - /* CCK txpwr limits for 2.4G band */ - if (band->bandtype == BRCM_BAND_2G) { - for (i = 0; i < BRCMS_NUM_RATES_CCK; i++) - txpwr->cck[i] = (u8) maxpwr; - } - - for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) { - txpwr->ofdm[i] = (u8) maxpwr; - - /* - * OFDM 40 MHz SISO has the same power as the corresponding - * MCS0-7 rate unless overriden by the locale specific code. - * We set this value to 0 as a flag (presumably 0 dBm isn't - * a possibility) and then copy the MCS0-7 value to the 40 MHz - * value if it wasn't explicitly set. - */ - txpwr->ofdm_40_siso[i] = 0; - - txpwr->ofdm_cdd[i] = (u8) maxpwr; - - txpwr->ofdm_40_cdd[i] = 0; - } - - delta = 0; - if (band->antgain > QDB(6)) - delta = band->antgain - QDB(6); /* Excess over 6 dB */ - - if (band->bandtype == BRCM_BAND_2G) - maxpwr_idx = (chan - 1); - else - maxpwr_idx = CHANNEL_POWER_IDX_5G(chan); - - maxpwr20 = li_mimo->maxpwr20[maxpwr_idx]; - maxpwr40 = li_mimo->maxpwr40[maxpwr_idx]; - - maxpwr20 = maxpwr20 - delta; - maxpwr20 = max(maxpwr20, 0); - maxpwr40 = maxpwr40 - delta; - maxpwr40 = max(maxpwr40, 0); - - /* Fill in the MCS 0-7 (SISO) rates */ - for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { - - /* - * 20 MHz has the same power as the corresponding OFDM rate - * unless overriden by the locale specific code. - */ - txpwr->mcs_20_siso[i] = txpwr->ofdm[i]; - txpwr->mcs_40_siso[i] = 0; - } - - /* Fill in the MCS 0-7 CDD rates */ - for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { - txpwr->mcs_20_cdd[i] = (u8) maxpwr20; - txpwr->mcs_40_cdd[i] = (u8) maxpwr40; - } - - /* - * These locales have SISO expressed in the - * table and override CDD later - */ - if (li_mimo == &locale_bn) { - if (li_mimo == &locale_bn) { - maxpwr20 = QDB(16); - maxpwr40 = 0; - - if (chan >= 3 && chan <= 11) - maxpwr40 = QDB(16); - } - - for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { - txpwr->mcs_20_siso[i] = (u8) maxpwr20; - txpwr->mcs_40_siso[i] = (u8) maxpwr40; - } - } - - /* Fill in the MCS 0-7 STBC rates */ - for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { - txpwr->mcs_20_stbc[i] = 0; - txpwr->mcs_40_stbc[i] = 0; - } - - /* Fill in the MCS 8-15 SDM rates */ - for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) { - txpwr->mcs_20_mimo[i] = (u8) maxpwr20; - txpwr->mcs_40_mimo[i] = (u8) maxpwr40; - } - - /* Fill in MCS32 */ - txpwr->mcs32 = (u8) maxpwr40; - - for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) { - if (txpwr->ofdm_40_cdd[i] == 0) - txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j]; - if (i == 0) { - i = i + 1; - if (txpwr->ofdm_40_cdd[i] == 0) - txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j]; - } - } - - /* - * Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO - * value if it wasn't provided explicitly. - */ - for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { - if (txpwr->mcs_40_siso[i] == 0) - txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i]; - } - - for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) { - if (txpwr->ofdm_40_siso[i] == 0) - txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j]; - if (i == 0) { - i = i + 1; - if (txpwr->ofdm_40_siso[i] == 0) - txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j]; - } - } - - /* - * Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding - * STBC values if they weren't provided explicitly. - */ - for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { - if (txpwr->mcs_20_stbc[i] == 0) - txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i]; - - if (txpwr->mcs_40_stbc[i] == 0) - txpwr->mcs_40_stbc[i] = txpwr->mcs_40_cdd[i]; - } - - return; -} - -/* - * Verify the chanspec is using a legal set of parameters, i.e. that the - * chanspec specified a band, bw, ctl_sb and channel and that the - * combination could be legal given any set of circumstances. - * RETURNS: true is the chanspec is malformed, false if it looks good. - */ -static bool brcms_c_chspec_malformed(u16 chanspec) -{ - /* must be 2G or 5G band */ - if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec)) - return true; - /* must be 20 or 40 bandwidth */ - if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec)) - return true; - - /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */ - if (CHSPEC_IS20(chanspec)) { - if (!CHSPEC_SB_NONE(chanspec)) - return true; - } else if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) { - return true; - } - - return false; -} - -/* - * Validate the chanspec for this locale, for 40MHZ we need to also - * check that the sidebands are valid 20MZH channels in this locale - * and they are also a legal HT combination - */ -static bool -brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec) -{ - struct brcms_c_info *wlc = wlc_cm->wlc; - u8 channel = CHSPEC_CHANNEL(chspec); - - /* check the chanspec */ - if (brcms_c_chspec_malformed(chspec)) { - brcms_err(wlc->hw->d11core, "wl%d: malformed chanspec 0x%x\n", - wlc->pub->unit, chspec); - return false; - } - - if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) != - chspec_bandunit(chspec)) - return false; - - return true; -} - -bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec) -{ - return brcms_c_valid_chanspec_ext(wlc_cm, chspec); -} - -static bool brcms_is_radar_freq(u16 center_freq) -{ - return center_freq >= 5260 && center_freq <= 5700; -} - -static void brcms_reg_apply_radar_flags(struct wiphy *wiphy) -{ - struct ieee80211_supported_band *sband; - struct ieee80211_channel *ch; - int i; - - sband = wiphy->bands[IEEE80211_BAND_5GHZ]; - if (!sband) - return; - - for (i = 0; i < sband->n_channels; i++) { - ch = &sband->channels[i]; - - if (!brcms_is_radar_freq(ch->center_freq)) - continue; - - /* - * All channels in this range should be passive and have - * DFS enabled. - */ - if (!(ch->flags & IEEE80211_CHAN_DISABLED)) - ch->flags |= IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | - IEEE80211_CHAN_NO_IR; - } -} - -static void -brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, - enum nl80211_reg_initiator initiator) -{ - struct ieee80211_supported_band *sband; - struct ieee80211_channel *ch; - const struct ieee80211_reg_rule *rule; - int band, i; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - sband = wiphy->bands[band]; - if (!sband) - continue; - - for (i = 0; i < sband->n_channels; i++) { - ch = &sband->channels[i]; - - if (ch->flags & - (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR)) - continue; - - if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - rule = freq_reg_info(wiphy, - MHZ_TO_KHZ(ch->center_freq)); - if (IS_ERR(rule)) - continue; - - if (!(rule->flags & NL80211_RRF_NO_IR)) - ch->flags &= ~IEEE80211_CHAN_NO_IR; - } else if (ch->beacon_found) { - ch->flags &= ~IEEE80211_CHAN_NO_IR; - } - } - } -} - -static void brcms_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); - struct brcms_info *wl = hw->priv; - struct brcms_c_info *wlc = wl->wlc; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *ch; - int band, i; - bool ch_found = false; - - brcms_reg_apply_radar_flags(wiphy); - - if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) - brcms_reg_apply_beaconing_flags(wiphy, request->initiator); - - /* Disable radio if all channels disallowed by regulatory */ - for (band = 0; !ch_found && band < IEEE80211_NUM_BANDS; band++) { - sband = wiphy->bands[band]; - if (!sband) - continue; - - for (i = 0; !ch_found && i < sband->n_channels; i++) { - ch = &sband->channels[i]; - - if (!(ch->flags & IEEE80211_CHAN_DISABLED)) - ch_found = true; - } - } - - if (ch_found) { - mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); - } else { - mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); - brcms_err(wlc->hw->d11core, - "wl%d: %s: no valid channel for \"%s\"\n", - wlc->pub->unit, __func__, request->alpha2); - } - - if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G) - wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi, - brcms_c_japan_ccode(request->alpha2)); -} - -void brcms_c_regd_init(struct brcms_c_info *wlc) -{ - struct wiphy *wiphy = wlc->wiphy; - const struct brcms_regd *regd = wlc->cmi->world_regd; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *ch; - struct brcms_chanvec sup_chan; - struct brcms_band *band; - int band_idx, i; - - /* Disable any channels not supported by the phy */ - for (band_idx = 0; band_idx < wlc->pub->_nbands; band_idx++) { - band = wlc->bandstate[band_idx]; - - wlc_phy_chanspec_band_validch(band->pi, band->bandtype, - &sup_chan); - - if (band_idx == BAND_2G_INDEX) - sband = wiphy->bands[IEEE80211_BAND_2GHZ]; - else - sband = wiphy->bands[IEEE80211_BAND_5GHZ]; - - for (i = 0; i < sband->n_channels; i++) { - ch = &sband->channels[i]; - if (!isset(sup_chan.vec, ch->hw_value)) - ch->flags |= IEEE80211_CHAN_DISABLED; - } - } - - wlc->wiphy->reg_notifier = brcms_reg_notifier; - wlc->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | - REGULATORY_STRICT_REG; - wiphy_apply_custom_regulatory(wlc->wiphy, regd->regdomain); - brcms_reg_apply_beaconing_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER); -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.h b/drivers/net/wireless/brcm80211/brcmsmac/channel.h deleted file mode 100644 index 39dd3a5b2979..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_CHANNEL_H_ -#define _BRCM_CHANNEL_H_ - -/* conversion for phy txpwr calculations that use .25 dB units */ -#define BRCMS_TXPWR_DB_FACTOR 4 - -/* bits for locale_info flags */ -#define BRCMS_PEAK_CONDUCTED 0x00 /* Peak for locals */ -#define BRCMS_EIRP 0x01 /* Flag for EIRP */ -#define BRCMS_DFS_TPC 0x02 /* Flag for DFS TPC */ -#define BRCMS_NO_OFDM 0x04 /* Flag for No OFDM */ -#define BRCMS_NO_40MHZ 0x08 /* Flag for No MIMO 40MHz */ -#define BRCMS_NO_MIMO 0x10 /* Flag for No MIMO, 20 or 40 MHz */ -#define BRCMS_RADAR_TYPE_EU 0x20 /* Flag for EU */ -#define BRCMS_DFS_FCC BRCMS_DFS_TPC /* Flag for DFS FCC */ - -#define BRCMS_DFS_EU (BRCMS_DFS_TPC | BRCMS_RADAR_TYPE_EU) /* Flag for DFS EU */ - -struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc); - -void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm); - -bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec); - -void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, - struct txpwr_limits *txpwr); -void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, - u8 local_constraint_qdbm); -void brcms_c_regd_init(struct brcms_c_info *wlc); - -#endif /* _WLC_CHANNEL_H */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/d11.h b/drivers/net/wireless/brcm80211/brcmsmac/d11.h deleted file mode 100644 index 9035cc4d6ff3..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/d11.h +++ /dev/null @@ -1,1902 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_D11_H_ -#define _BRCM_D11_H_ - -#include - -#include -#include "pub.h" -#include "dma.h" - -/* RX FIFO numbers */ -#define RX_FIFO 0 /* data and ctl frames */ -#define RX_TXSTATUS_FIFO 3 /* RX fifo for tx status packages */ - -/* TX FIFO numbers using WME Access Category */ -#define TX_AC_BK_FIFO 0 /* Background TX FIFO */ -#define TX_AC_BE_FIFO 1 /* Best-Effort TX FIFO */ -#define TX_AC_VI_FIFO 2 /* Video TX FIFO */ -#define TX_AC_VO_FIFO 3 /* Voice TX FIFO */ -#define TX_BCMC_FIFO 4 /* Broadcast/Multicast TX FIFO */ -#define TX_ATIM_FIFO 5 /* TX fifo for ATIM window info */ - -/* Addr is byte address used by SW; offset is word offset used by uCode */ - -/* Per AC TX limit settings */ -#define M_AC_TXLMT_BASE_ADDR (0x180 * 2) -#define M_AC_TXLMT_ADDR(_ac) (M_AC_TXLMT_BASE_ADDR + (2 * (_ac))) - -/* Legacy TX FIFO numbers */ -#define TX_DATA_FIFO TX_AC_BE_FIFO -#define TX_CTL_FIFO TX_AC_VO_FIFO - -#define WL_RSSI_ANT_MAX 4 /* max possible rx antennas */ - -struct intctrlregs { - u32 intstatus; - u32 intmask; -}; - -/* PIO structure, - * support two PIO format: 2 bytes access and 4 bytes access - * basic FIFO register set is per channel(transmit or receive) - * a pair of channels is defined for convenience - */ -/* 2byte-wide pio register set per channel(xmt or rcv) */ -struct pio2regs { - u16 fifocontrol; - u16 fifodata; - u16 fifofree; /* only valid in xmt channel, not in rcv channel */ - u16 PAD; -}; - -/* a pair of pio channels(tx and rx) */ -struct pio2regp { - struct pio2regs tx; - struct pio2regs rx; -}; - -/* 4byte-wide pio register set per channel(xmt or rcv) */ -struct pio4regs { - u32 fifocontrol; - u32 fifodata; -}; - -/* a pair of pio channels(tx and rx) */ -struct pio4regp { - struct pio4regs tx; - struct pio4regs rx; -}; - -/* read: 32-bit register that can be read as 32-bit or as 2 16-bit - * write: only low 16b-it half can be written - */ -union pmqreg { - u32 pmqhostdata; /* read only! */ - struct { - u16 pmqctrlstatus; /* read/write */ - u16 PAD; - } w; -}; - -struct fifo64 { - struct dma64regs dmaxmt; /* dma tx */ - struct pio4regs piotx; /* pio tx */ - struct dma64regs dmarcv; /* dma rx */ - struct pio4regs piorx; /* pio rx */ -}; - -/* - * Host Interface Registers - */ -struct d11regs { - /* Device Control ("semi-standard host registers") */ - u32 PAD[3]; /* 0x0 - 0x8 */ - u32 biststatus; /* 0xC */ - u32 biststatus2; /* 0x10 */ - u32 PAD; /* 0x14 */ - u32 gptimer; /* 0x18 */ - u32 usectimer; /* 0x1c *//* for corerev >= 26 */ - - /* Interrupt Control *//* 0x20 */ - struct intctrlregs intctrlregs[8]; - - u32 PAD[40]; /* 0x60 - 0xFC */ - - u32 intrcvlazy[4]; /* 0x100 - 0x10C */ - - u32 PAD[4]; /* 0x110 - 0x11c */ - - u32 maccontrol; /* 0x120 */ - u32 maccommand; /* 0x124 */ - u32 macintstatus; /* 0x128 */ - u32 macintmask; /* 0x12C */ - - /* Transmit Template Access */ - u32 tplatewrptr; /* 0x130 */ - u32 tplatewrdata; /* 0x134 */ - u32 PAD[2]; /* 0x138 - 0x13C */ - - /* PMQ registers */ - union pmqreg pmqreg; /* 0x140 */ - u32 pmqpatl; /* 0x144 */ - u32 pmqpath; /* 0x148 */ - u32 PAD; /* 0x14C */ - - u32 chnstatus; /* 0x150 */ - u32 psmdebug; /* 0x154 */ - u32 phydebug; /* 0x158 */ - u32 machwcap; /* 0x15C */ - - /* Extended Internal Objects */ - u32 objaddr; /* 0x160 */ - u32 objdata; /* 0x164 */ - u32 PAD[2]; /* 0x168 - 0x16c */ - - u32 frmtxstatus; /* 0x170 */ - u32 frmtxstatus2; /* 0x174 */ - u32 PAD[2]; /* 0x178 - 0x17c */ - - /* TSF host access */ - u32 tsf_timerlow; /* 0x180 */ - u32 tsf_timerhigh; /* 0x184 */ - u32 tsf_cfprep; /* 0x188 */ - u32 tsf_cfpstart; /* 0x18c */ - u32 tsf_cfpmaxdur32; /* 0x190 */ - u32 PAD[3]; /* 0x194 - 0x19c */ - - u32 maccontrol1; /* 0x1a0 */ - u32 machwcap1; /* 0x1a4 */ - u32 PAD[14]; /* 0x1a8 - 0x1dc */ - - /* Clock control and hardware workarounds*/ - u32 clk_ctl_st; /* 0x1e0 */ - u32 hw_war; - u32 d11_phypllctl; /* the phypll request/avail bits are - * moved to clk_ctl_st - */ - u32 PAD[5]; /* 0x1ec - 0x1fc */ - - /* 0x200-0x37F dma/pio registers */ - struct fifo64 fifo64regs[6]; - - /* FIFO diagnostic port access */ - struct dma32diag dmafifo; /* 0x380 - 0x38C */ - - u32 aggfifocnt; /* 0x390 */ - u32 aggfifodata; /* 0x394 */ - u32 PAD[16]; /* 0x398 - 0x3d4 */ - u16 radioregaddr; /* 0x3d8 */ - u16 radioregdata; /* 0x3da */ - - /* - * time delay between the change on rf disable input and - * radio shutdown - */ - u32 rfdisabledly; /* 0x3DC */ - - /* PHY register access */ - u16 phyversion; /* 0x3e0 - 0x0 */ - u16 phybbconfig; /* 0x3e2 - 0x1 */ - u16 phyadcbias; /* 0x3e4 - 0x2 Bphy only */ - u16 phyanacore; /* 0x3e6 - 0x3 pwwrdwn on aphy */ - u16 phyrxstatus0; /* 0x3e8 - 0x4 */ - u16 phyrxstatus1; /* 0x3ea - 0x5 */ - u16 phycrsth; /* 0x3ec - 0x6 */ - u16 phytxerror; /* 0x3ee - 0x7 */ - u16 phychannel; /* 0x3f0 - 0x8 */ - u16 PAD[1]; /* 0x3f2 - 0x9 */ - u16 phytest; /* 0x3f4 - 0xa */ - u16 phy4waddr; /* 0x3f6 - 0xb */ - u16 phy4wdatahi; /* 0x3f8 - 0xc */ - u16 phy4wdatalo; /* 0x3fa - 0xd */ - u16 phyregaddr; /* 0x3fc - 0xe */ - u16 phyregdata; /* 0x3fe - 0xf */ - - /* IHR *//* 0x400 - 0x7FE */ - - /* RXE Block */ - u16 PAD[3]; /* 0x400 - 0x406 */ - u16 rcv_fifo_ctl; /* 0x406 */ - u16 PAD; /* 0x408 - 0x40a */ - u16 rcv_frm_cnt; /* 0x40a */ - u16 PAD[4]; /* 0x40a - 0x414 */ - u16 rssi; /* 0x414 */ - u16 PAD[5]; /* 0x414 - 0x420 */ - u16 rcm_ctl; /* 0x420 */ - u16 rcm_mat_data; /* 0x422 */ - u16 rcm_mat_mask; /* 0x424 */ - u16 rcm_mat_dly; /* 0x426 */ - u16 rcm_cond_mask_l; /* 0x428 */ - u16 rcm_cond_mask_h; /* 0x42A */ - u16 rcm_cond_dly; /* 0x42C */ - u16 PAD[1]; /* 0x42E */ - u16 ext_ihr_addr; /* 0x430 */ - u16 ext_ihr_data; /* 0x432 */ - u16 rxe_phyrs_2; /* 0x434 */ - u16 rxe_phyrs_3; /* 0x436 */ - u16 phy_mode; /* 0x438 */ - u16 rcmta_ctl; /* 0x43a */ - u16 rcmta_size; /* 0x43c */ - u16 rcmta_addr0; /* 0x43e */ - u16 rcmta_addr1; /* 0x440 */ - u16 rcmta_addr2; /* 0x442 */ - u16 PAD[30]; /* 0x444 - 0x480 */ - - /* PSM Block *//* 0x480 - 0x500 */ - - u16 PAD; /* 0x480 */ - u16 psm_maccontrol_h; /* 0x482 */ - u16 psm_macintstatus_l; /* 0x484 */ - u16 psm_macintstatus_h; /* 0x486 */ - u16 psm_macintmask_l; /* 0x488 */ - u16 psm_macintmask_h; /* 0x48A */ - u16 PAD; /* 0x48C */ - u16 psm_maccommand; /* 0x48E */ - u16 psm_brc; /* 0x490 */ - u16 psm_phy_hdr_param; /* 0x492 */ - u16 psm_postcard; /* 0x494 */ - u16 psm_pcard_loc_l; /* 0x496 */ - u16 psm_pcard_loc_h; /* 0x498 */ - u16 psm_gpio_in; /* 0x49A */ - u16 psm_gpio_out; /* 0x49C */ - u16 psm_gpio_oe; /* 0x49E */ - - u16 psm_bred_0; /* 0x4A0 */ - u16 psm_bred_1; /* 0x4A2 */ - u16 psm_bred_2; /* 0x4A4 */ - u16 psm_bred_3; /* 0x4A6 */ - u16 psm_brcl_0; /* 0x4A8 */ - u16 psm_brcl_1; /* 0x4AA */ - u16 psm_brcl_2; /* 0x4AC */ - u16 psm_brcl_3; /* 0x4AE */ - u16 psm_brpo_0; /* 0x4B0 */ - u16 psm_brpo_1; /* 0x4B2 */ - u16 psm_brpo_2; /* 0x4B4 */ - u16 psm_brpo_3; /* 0x4B6 */ - u16 psm_brwk_0; /* 0x4B8 */ - u16 psm_brwk_1; /* 0x4BA */ - u16 psm_brwk_2; /* 0x4BC */ - u16 psm_brwk_3; /* 0x4BE */ - - u16 psm_base_0; /* 0x4C0 */ - u16 psm_base_1; /* 0x4C2 */ - u16 psm_base_2; /* 0x4C4 */ - u16 psm_base_3; /* 0x4C6 */ - u16 psm_base_4; /* 0x4C8 */ - u16 psm_base_5; /* 0x4CA */ - u16 psm_base_6; /* 0x4CC */ - u16 psm_pc_reg_0; /* 0x4CE */ - u16 psm_pc_reg_1; /* 0x4D0 */ - u16 psm_pc_reg_2; /* 0x4D2 */ - u16 psm_pc_reg_3; /* 0x4D4 */ - u16 PAD[0xD]; /* 0x4D6 - 0x4DE */ - u16 psm_corectlsts; /* 0x4f0 *//* Corerev >= 13 */ - u16 PAD[0x7]; /* 0x4f2 - 0x4fE */ - - /* TXE0 Block *//* 0x500 - 0x580 */ - u16 txe_ctl; /* 0x500 */ - u16 txe_aux; /* 0x502 */ - u16 txe_ts_loc; /* 0x504 */ - u16 txe_time_out; /* 0x506 */ - u16 txe_wm_0; /* 0x508 */ - u16 txe_wm_1; /* 0x50A */ - u16 txe_phyctl; /* 0x50C */ - u16 txe_status; /* 0x50E */ - u16 txe_mmplcp0; /* 0x510 */ - u16 txe_mmplcp1; /* 0x512 */ - u16 txe_phyctl1; /* 0x514 */ - - u16 PAD[0x05]; /* 0x510 - 0x51E */ - - /* Transmit control */ - u16 xmtfifodef; /* 0x520 */ - u16 xmtfifo_frame_cnt; /* 0x522 *//* Corerev >= 16 */ - u16 xmtfifo_byte_cnt; /* 0x524 *//* Corerev >= 16 */ - u16 xmtfifo_head; /* 0x526 *//* Corerev >= 16 */ - u16 xmtfifo_rd_ptr; /* 0x528 *//* Corerev >= 16 */ - u16 xmtfifo_wr_ptr; /* 0x52A *//* Corerev >= 16 */ - u16 xmtfifodef1; /* 0x52C *//* Corerev >= 16 */ - - u16 PAD[0x09]; /* 0x52E - 0x53E */ - - u16 xmtfifocmd; /* 0x540 */ - u16 xmtfifoflush; /* 0x542 */ - u16 xmtfifothresh; /* 0x544 */ - u16 xmtfifordy; /* 0x546 */ - u16 xmtfifoprirdy; /* 0x548 */ - u16 xmtfiforqpri; /* 0x54A */ - u16 xmttplatetxptr; /* 0x54C */ - u16 PAD; /* 0x54E */ - u16 xmttplateptr; /* 0x550 */ - u16 smpl_clct_strptr; /* 0x552 *//* Corerev >= 22 */ - u16 smpl_clct_stpptr; /* 0x554 *//* Corerev >= 22 */ - u16 smpl_clct_curptr; /* 0x556 *//* Corerev >= 22 */ - u16 PAD[0x04]; /* 0x558 - 0x55E */ - u16 xmttplatedatalo; /* 0x560 */ - u16 xmttplatedatahi; /* 0x562 */ - - u16 PAD[2]; /* 0x564 - 0x566 */ - - u16 xmtsel; /* 0x568 */ - u16 xmttxcnt; /* 0x56A */ - u16 xmttxshmaddr; /* 0x56C */ - - u16 PAD[0x09]; /* 0x56E - 0x57E */ - - /* TXE1 Block */ - u16 PAD[0x40]; /* 0x580 - 0x5FE */ - - /* TSF Block */ - u16 PAD[0X02]; /* 0x600 - 0x602 */ - u16 tsf_cfpstrt_l; /* 0x604 */ - u16 tsf_cfpstrt_h; /* 0x606 */ - u16 PAD[0X05]; /* 0x608 - 0x610 */ - u16 tsf_cfppretbtt; /* 0x612 */ - u16 PAD[0XD]; /* 0x614 - 0x62C */ - u16 tsf_clk_frac_l; /* 0x62E */ - u16 tsf_clk_frac_h; /* 0x630 */ - u16 PAD[0X14]; /* 0x632 - 0x658 */ - u16 tsf_random; /* 0x65A */ - u16 PAD[0x05]; /* 0x65C - 0x664 */ - /* GPTimer 2 registers */ - u16 tsf_gpt2_stat; /* 0x666 */ - u16 tsf_gpt2_ctr_l; /* 0x668 */ - u16 tsf_gpt2_ctr_h; /* 0x66A */ - u16 tsf_gpt2_val_l; /* 0x66C */ - u16 tsf_gpt2_val_h; /* 0x66E */ - u16 tsf_gptall_stat; /* 0x670 */ - u16 PAD[0x07]; /* 0x672 - 0x67E */ - - /* IFS Block */ - u16 ifs_sifs_rx_tx_tx; /* 0x680 */ - u16 ifs_sifs_nav_tx; /* 0x682 */ - u16 ifs_slot; /* 0x684 */ - u16 PAD; /* 0x686 */ - u16 ifs_ctl; /* 0x688 */ - u16 PAD[0x3]; /* 0x68a - 0x68F */ - u16 ifsstat; /* 0x690 */ - u16 ifsmedbusyctl; /* 0x692 */ - u16 iftxdur; /* 0x694 */ - u16 PAD[0x3]; /* 0x696 - 0x69b */ - /* EDCF support in dot11macs */ - u16 ifs_aifsn; /* 0x69c */ - u16 ifs_ctl1; /* 0x69e */ - - /* slow clock registers */ - u16 scc_ctl; /* 0x6a0 */ - u16 scc_timer_l; /* 0x6a2 */ - u16 scc_timer_h; /* 0x6a4 */ - u16 scc_frac; /* 0x6a6 */ - u16 scc_fastpwrup_dly; /* 0x6a8 */ - u16 scc_per; /* 0x6aa */ - u16 scc_per_frac; /* 0x6ac */ - u16 scc_cal_timer_l; /* 0x6ae */ - u16 scc_cal_timer_h; /* 0x6b0 */ - u16 PAD; /* 0x6b2 */ - - u16 PAD[0x26]; - - /* NAV Block */ - u16 nav_ctl; /* 0x700 */ - u16 navstat; /* 0x702 */ - u16 PAD[0x3e]; /* 0x702 - 0x77E */ - - /* WEP/PMQ Block *//* 0x780 - 0x7FE */ - u16 PAD[0x20]; /* 0x780 - 0x7BE */ - - u16 wepctl; /* 0x7C0 */ - u16 wepivloc; /* 0x7C2 */ - u16 wepivkey; /* 0x7C4 */ - u16 wepwkey; /* 0x7C6 */ - - u16 PAD[4]; /* 0x7C8 - 0x7CE */ - u16 pcmctl; /* 0X7D0 */ - u16 pcmstat; /* 0X7D2 */ - u16 PAD[6]; /* 0x7D4 - 0x7DE */ - - u16 pmqctl; /* 0x7E0 */ - u16 pmqstatus; /* 0x7E2 */ - u16 pmqpat0; /* 0x7E4 */ - u16 pmqpat1; /* 0x7E6 */ - u16 pmqpat2; /* 0x7E8 */ - - u16 pmqdat; /* 0x7EA */ - u16 pmqdator; /* 0x7EC */ - u16 pmqhst; /* 0x7EE */ - u16 pmqpath0; /* 0x7F0 */ - u16 pmqpath1; /* 0x7F2 */ - u16 pmqpath2; /* 0x7F4 */ - u16 pmqdath; /* 0x7F6 */ - - u16 PAD[0x04]; /* 0x7F8 - 0x7FE */ - - /* SHM *//* 0x800 - 0xEFE */ - u16 PAD[0x380]; /* 0x800 - 0xEFE */ -}; - -/* d11 register field offset */ -#define D11REGOFFS(field) offsetof(struct d11regs, field) - -#define PIHR_BASE 0x0400 /* byte address of packed IHR region */ - -/* biststatus */ -#define BT_DONE (1U << 31) /* bist done */ -#define BT_B2S (1 << 30) /* bist2 ram summary bit */ - -/* intstatus and intmask */ -#define I_PC (1 << 10) /* pci descriptor error */ -#define I_PD (1 << 11) /* pci data error */ -#define I_DE (1 << 12) /* descriptor protocol error */ -#define I_RU (1 << 13) /* receive descriptor underflow */ -#define I_RO (1 << 14) /* receive fifo overflow */ -#define I_XU (1 << 15) /* transmit fifo underflow */ -#define I_RI (1 << 16) /* receive interrupt */ -#define I_XI (1 << 24) /* transmit interrupt */ - -/* interrupt receive lazy */ -#define IRL_TO_MASK 0x00ffffff /* timeout */ -#define IRL_FC_MASK 0xff000000 /* frame count */ -#define IRL_FC_SHIFT 24 /* frame count */ - -/*== maccontrol register ==*/ -#define MCTL_GMODE (1U << 31) -#define MCTL_DISCARD_PMQ (1 << 30) -#define MCTL_TBTTHOLD (1 << 28) -#define MCTL_WAKE (1 << 26) -#define MCTL_HPS (1 << 25) -#define MCTL_PROMISC (1 << 24) -#define MCTL_KEEPBADFCS (1 << 23) -#define MCTL_KEEPCONTROL (1 << 22) -#define MCTL_PHYLOCK (1 << 21) -#define MCTL_BCNS_PROMISC (1 << 20) -#define MCTL_LOCK_RADIO (1 << 19) -#define MCTL_AP (1 << 18) -#define MCTL_INFRA (1 << 17) -#define MCTL_BIGEND (1 << 16) -#define MCTL_GPOUT_SEL_MASK (3 << 14) -#define MCTL_GPOUT_SEL_SHIFT 14 -#define MCTL_EN_PSMDBG (1 << 13) -#define MCTL_IHR_EN (1 << 10) -#define MCTL_SHM_UPPER (1 << 9) -#define MCTL_SHM_EN (1 << 8) -#define MCTL_PSM_JMP_0 (1 << 2) -#define MCTL_PSM_RUN (1 << 1) -#define MCTL_EN_MAC (1 << 0) - -/*== maccommand register ==*/ -#define MCMD_BCN0VLD (1 << 0) -#define MCMD_BCN1VLD (1 << 1) -#define MCMD_DIRFRMQVAL (1 << 2) -#define MCMD_CCA (1 << 3) -#define MCMD_BG_NOISE (1 << 4) -#define MCMD_SKIP_SHMINIT (1 << 5) /* only used for simulation */ -#define MCMD_SAMPLECOLL MCMD_SKIP_SHMINIT /* reuse for sample collect */ - -/*== macintstatus/macintmask ==*/ -/* gracefully suspended */ -#define MI_MACSSPNDD (1 << 0) -/* beacon template available */ -#define MI_BCNTPL (1 << 1) -/* TBTT indication */ -#define MI_TBTT (1 << 2) -/* beacon successfully tx'd */ -#define MI_BCNSUCCESS (1 << 3) -/* beacon canceled (IBSS) */ -#define MI_BCNCANCLD (1 << 4) -/* end of ATIM-window (IBSS) */ -#define MI_ATIMWINEND (1 << 5) -/* PMQ entries available */ -#define MI_PMQ (1 << 6) -/* non-specific gen-stat bits that are set by PSM */ -#define MI_NSPECGEN_0 (1 << 7) -/* non-specific gen-stat bits that are set by PSM */ -#define MI_NSPECGEN_1 (1 << 8) -/* MAC level Tx error */ -#define MI_MACTXERR (1 << 9) -/* non-specific gen-stat bits that are set by PSM */ -#define MI_NSPECGEN_3 (1 << 10) -/* PHY Tx error */ -#define MI_PHYTXERR (1 << 11) -/* Power Management Event */ -#define MI_PME (1 << 12) -/* General-purpose timer0 */ -#define MI_GP0 (1 << 13) -/* General-purpose timer1 */ -#define MI_GP1 (1 << 14) -/* (ORed) DMA-interrupts */ -#define MI_DMAINT (1 << 15) -/* MAC has completed a TX FIFO Suspend/Flush */ -#define MI_TXSTOP (1 << 16) -/* MAC has completed a CCA measurement */ -#define MI_CCA (1 << 17) -/* MAC has collected background noise samples */ -#define MI_BG_NOISE (1 << 18) -/* MBSS DTIM TBTT indication */ -#define MI_DTIM_TBTT (1 << 19) -/* Probe response queue needs attention */ -#define MI_PRQ (1 << 20) -/* Radio/PHY has been powered back up. */ -#define MI_PWRUP (1 << 21) -#define MI_RESERVED3 (1 << 22) -#define MI_RESERVED2 (1 << 23) -#define MI_RESERVED1 (1 << 25) -/* MAC detected change on RF Disable input*/ -#define MI_RFDISABLE (1 << 28) -/* MAC has completed a TX */ -#define MI_TFS (1 << 29) -/* A phy status change wrt G mode */ -#define MI_PHYCHANGED (1 << 30) -/* general purpose timeout */ -#define MI_TO (1U << 31) - -/* Mac capabilities registers */ -/*== machwcap ==*/ -#define MCAP_TKIPMIC 0x80000000 /* TKIP MIC hardware present */ - -/*== pmqhost data ==*/ -/* data entry of head pmq entry */ -#define PMQH_DATA_MASK 0xffff0000 -/* PM entry for BSS config */ -#define PMQH_BSSCFG 0x00100000 -/* PM Mode OFF: power save off */ -#define PMQH_PMOFF 0x00010000 -/* PM Mode ON: power save on */ -#define PMQH_PMON 0x00020000 -/* Dis-associated or De-authenticated */ -#define PMQH_DASAT 0x00040000 -/* ATIM not acknowledged */ -#define PMQH_ATIMFAIL 0x00080000 -/* delete head entry */ -#define PMQH_DEL_ENTRY 0x00000001 -/* delete head entry to cur read pointer -1 */ -#define PMQH_DEL_MULT 0x00000002 -/* pmq overflow indication */ -#define PMQH_OFLO 0x00000004 -/* entries are present in pmq */ -#define PMQH_NOT_EMPTY 0x00000008 - -/*== phydebug ==*/ -/* phy is asserting carrier sense */ -#define PDBG_CRS (1 << 0) -/* phy is taking xmit byte from mac this cycle */ -#define PDBG_TXA (1 << 1) -/* mac is instructing the phy to transmit a frame */ -#define PDBG_TXF (1 << 2) -/* phy is signalling a transmit Error to the mac */ -#define PDBG_TXE (1 << 3) -/* phy detected the end of a valid frame preamble */ -#define PDBG_RXF (1 << 4) -/* phy detected the end of a valid PLCP header */ -#define PDBG_RXS (1 << 5) -/* rx start not asserted */ -#define PDBG_RXFRG (1 << 6) -/* mac is taking receive byte from phy this cycle */ -#define PDBG_RXV (1 << 7) -/* RF portion of the radio is disabled */ -#define PDBG_RFD (1 << 16) - -/*== objaddr register ==*/ -#define OBJADDR_SEL_MASK 0x000F0000 -#define OBJADDR_UCM_SEL 0x00000000 -#define OBJADDR_SHM_SEL 0x00010000 -#define OBJADDR_SCR_SEL 0x00020000 -#define OBJADDR_IHR_SEL 0x00030000 -#define OBJADDR_RCMTA_SEL 0x00040000 -#define OBJADDR_SRCHM_SEL 0x00060000 -#define OBJADDR_WINC 0x01000000 -#define OBJADDR_RINC 0x02000000 -#define OBJADDR_AUTO_INC 0x03000000 - -#define WEP_PCMADDR 0x07d4 -#define WEP_PCMDATA 0x07d6 - -/*== frmtxstatus ==*/ -#define TXS_V (1 << 0) /* valid bit */ -#define TXS_STATUS_MASK 0xffff -#define TXS_FID_MASK 0xffff0000 -#define TXS_FID_SHIFT 16 - -/*== frmtxstatus2 ==*/ -#define TXS_SEQ_MASK 0xffff -#define TXS_PTX_MASK 0xff0000 -#define TXS_PTX_SHIFT 16 -#define TXS_MU_MASK 0x01000000 -#define TXS_MU_SHIFT 24 - -/*== clk_ctl_st ==*/ -#define CCS_ERSRC_REQ_D11PLL 0x00000100 /* d11 core pll request */ -#define CCS_ERSRC_REQ_PHYPLL 0x00000200 /* PHY pll request */ -#define CCS_ERSRC_AVAIL_D11PLL 0x01000000 /* d11 core pll available */ -#define CCS_ERSRC_AVAIL_PHYPLL 0x02000000 /* PHY pll available */ - -/* HT Cloclk Ctrl and Clock Avail for 4313 */ -#define CCS_ERSRC_REQ_HT 0x00000010 /* HT avail request */ -#define CCS_ERSRC_AVAIL_HT 0x00020000 /* HT clock available */ - -/* tsf_cfprep register */ -#define CFPREP_CBI_MASK 0xffffffc0 -#define CFPREP_CBI_SHIFT 6 -#define CFPREP_CFPP 0x00000001 - -/* tx fifo sizes values are in terms of 256 byte blocks */ -#define TXFIFOCMD_RESET_MASK (1 << 15) /* reset */ -#define TXFIFOCMD_FIFOSEL_SHIFT 8 /* fifo */ -#define TXFIFO_FIFOTOP_SHIFT 8 /* fifo start */ - -#define TXFIFO_START_BLK16 65 /* Base address + 32 * 512 B/P */ -#define TXFIFO_START_BLK 6 /* Base address + 6 * 256 B */ -#define TXFIFO_SIZE_UNIT 256 /* one unit corresponds to 256 bytes */ -#define MBSS16_TEMPLMEM_MINBLKS 65 /* one unit corresponds to 256 bytes */ - -/*== phy versions (PhyVersion:Revision field) ==*/ -/* analog block version */ -#define PV_AV_MASK 0xf000 -/* analog block version bitfield offset */ -#define PV_AV_SHIFT 12 -/* phy type */ -#define PV_PT_MASK 0x0f00 -/* phy type bitfield offset */ -#define PV_PT_SHIFT 8 -/* phy version */ -#define PV_PV_MASK 0x000f -#define PHY_TYPE(v) ((v & PV_PT_MASK) >> PV_PT_SHIFT) - -/*== phy types (PhyVersion:PhyType field) ==*/ -#define PHY_TYPE_N 4 /* N-Phy value */ -#define PHY_TYPE_SSN 6 /* SSLPN-Phy value */ -#define PHY_TYPE_LCN 8 /* LCN-Phy value */ -#define PHY_TYPE_LCNXN 9 /* LCNXN-Phy value */ -#define PHY_TYPE_NULL 0xf /* Invalid Phy value */ - -/*== analog types (PhyVersion:AnalogType field) ==*/ -#define ANA_11N_013 5 - -/* 802.11a PLCP header def */ -struct ofdm_phy_hdr { - u8 rlpt[3]; /* rate, length, parity, tail */ - u16 service; - u8 pad; -} __packed; - -#define D11A_PHY_HDR_GRATE(phdr) ((phdr)->rlpt[0] & 0x0f) -#define D11A_PHY_HDR_GRES(phdr) (((phdr)->rlpt[0] >> 4) & 0x01) -#define D11A_PHY_HDR_GLENGTH(phdr) (((u32 *)((phdr)->rlpt) >> 5) & 0x0fff) -#define D11A_PHY_HDR_GPARITY(phdr) (((phdr)->rlpt[3] >> 1) & 0x01) -#define D11A_PHY_HDR_GTAIL(phdr) (((phdr)->rlpt[3] >> 2) & 0x3f) - -/* rate encoded per 802.11a-1999 sec 17.3.4.1 */ -#define D11A_PHY_HDR_SRATE(phdr, rate) \ - ((phdr)->rlpt[0] = ((phdr)->rlpt[0] & 0xf0) | ((rate) & 0xf)) -/* set reserved field to zero */ -#define D11A_PHY_HDR_SRES(phdr) ((phdr)->rlpt[0] &= 0xef) -/* length is number of octets in PSDU */ -#define D11A_PHY_HDR_SLENGTH(phdr, length) \ - (*(u32 *)((phdr)->rlpt) = *(u32 *)((phdr)->rlpt) | \ - (((length) & 0x0fff) << 5)) -/* set the tail to all zeros */ -#define D11A_PHY_HDR_STAIL(phdr) ((phdr)->rlpt[3] &= 0x03) - -#define D11A_PHY_HDR_LEN_L 3 /* low-rate part of PLCP header */ -#define D11A_PHY_HDR_LEN_R 2 /* high-rate part of PLCP header */ - -#define D11A_PHY_TX_DELAY (2) /* 2.1 usec */ - -#define D11A_PHY_HDR_TIME (4) /* low-rate part of PLCP header */ -#define D11A_PHY_PRE_TIME (16) -#define D11A_PHY_PREHDR_TIME (D11A_PHY_PRE_TIME + D11A_PHY_HDR_TIME) - -/* 802.11b PLCP header def */ -struct cck_phy_hdr { - u8 signal; - u8 service; - u16 length; - u16 crc; -} __packed; - -#define D11B_PHY_HDR_LEN 6 - -#define D11B_PHY_TX_DELAY (3) /* 3.4 usec */ - -#define D11B_PHY_LHDR_TIME (D11B_PHY_HDR_LEN << 3) -#define D11B_PHY_LPRE_TIME (144) -#define D11B_PHY_LPREHDR_TIME (D11B_PHY_LPRE_TIME + D11B_PHY_LHDR_TIME) - -#define D11B_PHY_SHDR_TIME (D11B_PHY_LHDR_TIME >> 1) -#define D11B_PHY_SPRE_TIME (D11B_PHY_LPRE_TIME >> 1) -#define D11B_PHY_SPREHDR_TIME (D11B_PHY_SPRE_TIME + D11B_PHY_SHDR_TIME) - -#define D11B_PLCP_SIGNAL_LOCKED (1 << 2) -#define D11B_PLCP_SIGNAL_LE (1 << 7) - -#define MIMO_PLCP_MCS_MASK 0x7f /* mcs index */ -#define MIMO_PLCP_40MHZ 0x80 /* 40 Hz frame */ -#define MIMO_PLCP_AMPDU 0x08 /* ampdu */ - -#define BRCMS_GET_CCK_PLCP_LEN(plcp) (plcp[4] + (plcp[5] << 8)) -#define BRCMS_GET_MIMO_PLCP_LEN(plcp) (plcp[1] + (plcp[2] << 8)) -#define BRCMS_SET_MIMO_PLCP_LEN(plcp, len) \ - do { \ - plcp[1] = len & 0xff; \ - plcp[2] = ((len >> 8) & 0xff); \ - } while (0) - -#define BRCMS_SET_MIMO_PLCP_AMPDU(plcp) (plcp[3] |= MIMO_PLCP_AMPDU) -#define BRCMS_CLR_MIMO_PLCP_AMPDU(plcp) (plcp[3] &= ~MIMO_PLCP_AMPDU) -#define BRCMS_IS_MIMO_PLCP_AMPDU(plcp) (plcp[3] & MIMO_PLCP_AMPDU) - -/* - * The dot11a PLCP header is 5 bytes. To simplify the software (so that we - * don't need e.g. different tx DMA headers for 11a and 11b), the PLCP header - * has padding added in the ucode. - */ -#define D11_PHY_HDR_LEN 6 - -/* TX DMA buffer header */ -struct d11txh { - __le16 MacTxControlLow; /* 0x0 */ - __le16 MacTxControlHigh; /* 0x1 */ - __le16 MacFrameControl; /* 0x2 */ - __le16 TxFesTimeNormal; /* 0x3 */ - __le16 PhyTxControlWord; /* 0x4 */ - __le16 PhyTxControlWord_1; /* 0x5 */ - __le16 PhyTxControlWord_1_Fbr; /* 0x6 */ - __le16 PhyTxControlWord_1_Rts; /* 0x7 */ - __le16 PhyTxControlWord_1_FbrRts; /* 0x8 */ - __le16 MainRates; /* 0x9 */ - __le16 XtraFrameTypes; /* 0xa */ - u8 IV[16]; /* 0x0b - 0x12 */ - u8 TxFrameRA[6]; /* 0x13 - 0x15 */ - __le16 TxFesTimeFallback; /* 0x16 */ - u8 RTSPLCPFallback[6]; /* 0x17 - 0x19 */ - __le16 RTSDurFallback; /* 0x1a */ - u8 FragPLCPFallback[6]; /* 0x1b - 1d */ - __le16 FragDurFallback; /* 0x1e */ - __le16 MModeLen; /* 0x1f */ - __le16 MModeFbrLen; /* 0x20 */ - __le16 TstampLow; /* 0x21 */ - __le16 TstampHigh; /* 0x22 */ - __le16 ABI_MimoAntSel; /* 0x23 */ - __le16 PreloadSize; /* 0x24 */ - __le16 AmpduSeqCtl; /* 0x25 */ - __le16 TxFrameID; /* 0x26 */ - __le16 TxStatus; /* 0x27 */ - __le16 MaxNMpdus; /* 0x28 */ - __le16 MaxABytes_MRT; /* 0x29 */ - __le16 MaxABytes_FBR; /* 0x2a */ - __le16 MinMBytes; /* 0x2b */ - u8 RTSPhyHeader[D11_PHY_HDR_LEN]; /* 0x2c - 0x2e */ - struct ieee80211_rts rts_frame; /* 0x2f - 0x36 */ - u16 PAD; /* 0x37 */ -} __packed; - -#define D11_TXH_LEN 112 /* bytes */ - -/* Frame Types */ -#define FT_CCK 0 -#define FT_OFDM 1 -#define FT_HT 2 -#define FT_N 3 - -/* - * Position of MPDU inside A-MPDU; indicated with bits 10:9 - * of MacTxControlLow - */ -#define TXC_AMPDU_SHIFT 9 /* shift for ampdu settings */ -#define TXC_AMPDU_NONE 0 /* Regular MPDU, not an A-MPDU */ -#define TXC_AMPDU_FIRST 1 /* first MPDU of an A-MPDU */ -#define TXC_AMPDU_MIDDLE 2 /* intermediate MPDU of an A-MPDU */ -#define TXC_AMPDU_LAST 3 /* last (or single) MPDU of an A-MPDU */ - -/*== MacTxControlLow ==*/ -#define TXC_AMIC 0x8000 -#define TXC_SENDCTS 0x0800 -#define TXC_AMPDU_MASK 0x0600 -#define TXC_BW_40 0x0100 -#define TXC_FREQBAND_5G 0x0080 -#define TXC_DFCS 0x0040 -#define TXC_IGNOREPMQ 0x0020 -#define TXC_HWSEQ 0x0010 -#define TXC_STARTMSDU 0x0008 -#define TXC_SENDRTS 0x0004 -#define TXC_LONGFRAME 0x0002 -#define TXC_IMMEDACK 0x0001 - -/*== MacTxControlHigh ==*/ -/* RTS fallback preamble type 1 = SHORT 0 = LONG */ -#define TXC_PREAMBLE_RTS_FB_SHORT 0x8000 -/* RTS main rate preamble type 1 = SHORT 0 = LONG */ -#define TXC_PREAMBLE_RTS_MAIN_SHORT 0x4000 -/* - * Main fallback rate preamble type - * 1 = SHORT for OFDM/GF for MIMO - * 0 = LONG for CCK/MM for MIMO - */ -#define TXC_PREAMBLE_DATA_FB_SHORT 0x2000 - -/* TXC_PREAMBLE_DATA_MAIN is in PhyTxControl bit 5 */ -/* use fallback rate for this AMPDU */ -#define TXC_AMPDU_FBR 0x1000 -#define TXC_SECKEY_MASK 0x0FF0 -#define TXC_SECKEY_SHIFT 4 -/* Use alternate txpwr defined at loc. M_ALT_TXPWR_IDX */ -#define TXC_ALT_TXPWR 0x0008 -#define TXC_SECTYPE_MASK 0x0007 -#define TXC_SECTYPE_SHIFT 0 - -/* Null delimiter for Fallback rate */ -#define AMPDU_FBR_NULL_DELIM 5 /* Location of Null delimiter count for AMPDU */ - -/* PhyTxControl for Mimophy */ -#define PHY_TXC_PWR_MASK 0xFC00 -#define PHY_TXC_PWR_SHIFT 10 -#define PHY_TXC_ANT_MASK 0x03C0 /* bit 6, 7, 8, 9 */ -#define PHY_TXC_ANT_SHIFT 6 -#define PHY_TXC_ANT_0_1 0x00C0 /* auto, last rx */ -#define PHY_TXC_LCNPHY_ANT_LAST 0x0000 -#define PHY_TXC_ANT_3 0x0200 /* virtual antenna 3 */ -#define PHY_TXC_ANT_2 0x0100 /* virtual antenna 2 */ -#define PHY_TXC_ANT_1 0x0080 /* virtual antenna 1 */ -#define PHY_TXC_ANT_0 0x0040 /* virtual antenna 0 */ -#define PHY_TXC_SHORT_HDR 0x0010 - -#define PHY_TXC_OLD_ANT_0 0x0000 -#define PHY_TXC_OLD_ANT_1 0x0100 -#define PHY_TXC_OLD_ANT_LAST 0x0300 - -/* PhyTxControl_1 for Mimophy */ -#define PHY_TXC1_BW_MASK 0x0007 -#define PHY_TXC1_BW_10MHZ 0 -#define PHY_TXC1_BW_10MHZ_UP 1 -#define PHY_TXC1_BW_20MHZ 2 -#define PHY_TXC1_BW_20MHZ_UP 3 -#define PHY_TXC1_BW_40MHZ 4 -#define PHY_TXC1_BW_40MHZ_DUP 5 -#define PHY_TXC1_MODE_SHIFT 3 -#define PHY_TXC1_MODE_MASK 0x0038 -#define PHY_TXC1_MODE_SISO 0 -#define PHY_TXC1_MODE_CDD 1 -#define PHY_TXC1_MODE_STBC 2 -#define PHY_TXC1_MODE_SDM 3 - -/* PhyTxControl for HTphy that are different from Mimophy */ -#define PHY_TXC_HTANT_MASK 0x3fC0 /* bits 6-13 */ - -/* XtraFrameTypes */ -#define XFTS_RTS_FT_SHIFT 2 -#define XFTS_FBRRTS_FT_SHIFT 4 -#define XFTS_CHANNEL_SHIFT 8 - -/* Antenna diversity bit in ant_wr_settle */ -#define PHY_AWS_ANTDIV 0x2000 - -/* IFS ctl */ -#define IFS_USEEDCF (1 << 2) - -/* IFS ctl1 */ -#define IFS_CTL1_EDCRS (1 << 3) -#define IFS_CTL1_EDCRS_20L (1 << 4) -#define IFS_CTL1_EDCRS_40 (1 << 5) - -/* ABI_MimoAntSel */ -#define ABI_MAS_ADDR_BMP_IDX_MASK 0x0f00 -#define ABI_MAS_ADDR_BMP_IDX_SHIFT 8 -#define ABI_MAS_FBR_ANT_PTN_MASK 0x00f0 -#define ABI_MAS_FBR_ANT_PTN_SHIFT 4 -#define ABI_MAS_MRT_ANT_PTN_MASK 0x000f - -/* tx status packet */ -struct tx_status { - u16 framelen; - u16 PAD; - u16 frameid; - u16 status; - u16 lasttxtime; - u16 sequence; - u16 phyerr; - u16 ackphyrxsh; -} __packed; - -#define TXSTATUS_LEN 16 - -/* status field bit definitions */ -#define TX_STATUS_FRM_RTX_MASK 0xF000 -#define TX_STATUS_FRM_RTX_SHIFT 12 -#define TX_STATUS_RTS_RTX_MASK 0x0F00 -#define TX_STATUS_RTS_RTX_SHIFT 8 -#define TX_STATUS_MASK 0x00FE -#define TX_STATUS_PMINDCTD (1 << 7) /* PM mode indicated to AP */ -#define TX_STATUS_INTERMEDIATE (1 << 6) /* intermediate or 1st ampdu pkg */ -#define TX_STATUS_AMPDU (1 << 5) /* AMPDU status */ -#define TX_STATUS_SUPR_MASK 0x1C /* suppress status bits (4:2) */ -#define TX_STATUS_SUPR_SHIFT 2 -#define TX_STATUS_ACK_RCV (1 << 1) /* ACK received */ -#define TX_STATUS_VALID (1 << 0) /* Tx status valid */ -#define TX_STATUS_NO_ACK 0 - -/* suppress status reason codes */ -#define TX_STATUS_SUPR_PMQ (1 << 2) /* PMQ entry */ -#define TX_STATUS_SUPR_FLUSH (2 << 2) /* flush request */ -#define TX_STATUS_SUPR_FRAG (3 << 2) /* previous frag failure */ -#define TX_STATUS_SUPR_TBTT (3 << 2) /* SHARED: Probe resp supr for TBTT */ -#define TX_STATUS_SUPR_BADCH (4 << 2) /* channel mismatch */ -#define TX_STATUS_SUPR_EXPTIME (5 << 2) /* lifetime expiry */ -#define TX_STATUS_SUPR_UF (6 << 2) /* underflow */ - -/* Unexpected tx status for rate update */ -#define TX_STATUS_UNEXP(status) \ - ((((status) & TX_STATUS_INTERMEDIATE) != 0) && \ - TX_STATUS_UNEXP_AMPDU(status)) - -/* Unexpected tx status for A-MPDU rate update */ -#define TX_STATUS_UNEXP_AMPDU(status) \ - ((((status) & TX_STATUS_SUPR_MASK) != 0) && \ - (((status) & TX_STATUS_SUPR_MASK) != TX_STATUS_SUPR_EXPTIME)) - -#define TX_STATUS_BA_BMAP03_MASK 0xF000 /* ba bitmap 0:3 in 1st pkg */ -#define TX_STATUS_BA_BMAP03_SHIFT 12 /* ba bitmap 0:3 in 1st pkg */ -#define TX_STATUS_BA_BMAP47_MASK 0x001E /* ba bitmap 4:7 in 2nd pkg */ -#define TX_STATUS_BA_BMAP47_SHIFT 3 /* ba bitmap 4:7 in 2nd pkg */ - -/* RXE (Receive Engine) */ - -/* RCM_CTL */ -#define RCM_INC_MASK_H 0x0080 -#define RCM_INC_MASK_L 0x0040 -#define RCM_INC_DATA 0x0020 -#define RCM_INDEX_MASK 0x001F -#define RCM_SIZE 15 - -#define RCM_MAC_OFFSET 0 /* current MAC address */ -#define RCM_BSSID_OFFSET 3 /* current BSSID address */ -#define RCM_F_BSSID_0_OFFSET 6 /* foreign BSS CFP tracking */ -#define RCM_F_BSSID_1_OFFSET 9 /* foreign BSS CFP tracking */ -#define RCM_F_BSSID_2_OFFSET 12 /* foreign BSS CFP tracking */ - -#define RCM_WEP_TA0_OFFSET 16 -#define RCM_WEP_TA1_OFFSET 19 -#define RCM_WEP_TA2_OFFSET 22 -#define RCM_WEP_TA3_OFFSET 25 - -/* PSM Block */ - -/* psm_phy_hdr_param bits */ -#define MAC_PHY_RESET 1 -#define MAC_PHY_CLOCK_EN 2 -#define MAC_PHY_FORCE_CLK 4 - -/* WEP Block */ - -/* WEP_WKEY */ -#define WKEY_START (1 << 8) -#define WKEY_SEL_MASK 0x1F - -/* WEP data formats */ - -/* the number of RCMTA entries */ -#define RCMTA_SIZE 50 - -#define M_ADDR_BMP_BLK (0x37e * 2) -#define M_ADDR_BMP_BLK_SZ 12 - -#define ADDR_BMP_RA (1 << 0) /* Receiver Address (RA) */ -#define ADDR_BMP_TA (1 << 1) /* Transmitter Address (TA) */ -#define ADDR_BMP_BSSID (1 << 2) /* BSSID */ -#define ADDR_BMP_AP (1 << 3) /* Infra-BSS Access Point */ -#define ADDR_BMP_STA (1 << 4) /* Infra-BSS Station */ -#define ADDR_BMP_RESERVED1 (1 << 5) -#define ADDR_BMP_RESERVED2 (1 << 6) -#define ADDR_BMP_RESERVED3 (1 << 7) -#define ADDR_BMP_BSS_IDX_MASK (3 << 8) /* BSS control block index */ -#define ADDR_BMP_BSS_IDX_SHIFT 8 - -#define WSEC_MAX_RCMTA_KEYS 54 - -/* max keys in M_TKMICKEYS_BLK */ -#define WSEC_MAX_TKMIC_ENGINE_KEYS 12 /* 8 + 4 default */ - -/* max RXE match registers */ -#define WSEC_MAX_RXE_KEYS 4 - -/* SECKINDXALGO (Security Key Index & Algorithm Block) word format */ -/* SKL (Security Key Lookup) */ -#define SKL_ALGO_MASK 0x0007 -#define SKL_ALGO_SHIFT 0 -#define SKL_KEYID_MASK 0x0008 -#define SKL_KEYID_SHIFT 3 -#define SKL_INDEX_MASK 0x03F0 -#define SKL_INDEX_SHIFT 4 -#define SKL_GRP_ALGO_MASK 0x1c00 -#define SKL_GRP_ALGO_SHIFT 10 - -/* additional bits defined for IBSS group key support */ -#define SKL_IBSS_INDEX_MASK 0x01F0 -#define SKL_IBSS_INDEX_SHIFT 4 -#define SKL_IBSS_KEYID1_MASK 0x0600 -#define SKL_IBSS_KEYID1_SHIFT 9 -#define SKL_IBSS_KEYID2_MASK 0x1800 -#define SKL_IBSS_KEYID2_SHIFT 11 -#define SKL_IBSS_KEYALGO_MASK 0xE000 -#define SKL_IBSS_KEYALGO_SHIFT 13 - -#define WSEC_MODE_OFF 0 -#define WSEC_MODE_HW 1 -#define WSEC_MODE_SW 2 - -#define WSEC_ALGO_OFF 0 -#define WSEC_ALGO_WEP1 1 -#define WSEC_ALGO_TKIP 2 -#define WSEC_ALGO_AES 3 -#define WSEC_ALGO_WEP128 4 -#define WSEC_ALGO_AES_LEGACY 5 -#define WSEC_ALGO_NALG 6 - -#define AES_MODE_NONE 0 -#define AES_MODE_CCM 1 - -/* WEP_CTL (Rev 0) */ -#define WECR0_KEYREG_SHIFT 0 -#define WECR0_KEYREG_MASK 0x7 -#define WECR0_DECRYPT (1 << 3) -#define WECR0_IVINLINE (1 << 4) -#define WECR0_WEPALG_SHIFT 5 -#define WECR0_WEPALG_MASK (0x7 << 5) -#define WECR0_WKEYSEL_SHIFT 8 -#define WECR0_WKEYSEL_MASK (0x7 << 8) -#define WECR0_WKEYSTART (1 << 11) -#define WECR0_WEPINIT (1 << 14) -#define WECR0_ICVERR (1 << 15) - -/* Frame template map byte offsets */ -#define T_ACTS_TPL_BASE (0) -#define T_NULL_TPL_BASE (0xc * 2) -#define T_QNULL_TPL_BASE (0x1c * 2) -#define T_RR_TPL_BASE (0x2c * 2) -#define T_BCN0_TPL_BASE (0x34 * 2) -#define T_PRS_TPL_BASE (0x134 * 2) -#define T_BCN1_TPL_BASE (0x234 * 2) -#define T_TX_FIFO_TXRAM_BASE (T_ACTS_TPL_BASE + \ - (TXFIFO_START_BLK * TXFIFO_SIZE_UNIT)) - -#define T_BA_TPL_BASE T_QNULL_TPL_BASE /* template area for BA */ - -#define T_RAM_ACCESS_SZ 4 /* template ram is 4 byte access only */ - -/* Shared Mem byte offsets */ - -/* Location where the ucode expects the corerev */ -#define M_MACHW_VER (0x00b * 2) - -/* Location where the ucode expects the MAC capabilities */ -#define M_MACHW_CAP_L (0x060 * 2) -#define M_MACHW_CAP_H (0x061 * 2) - -/* WME shared memory */ -#define M_EDCF_STATUS_OFF (0x007 * 2) -#define M_TXF_CUR_INDEX (0x018 * 2) -#define M_EDCF_QINFO (0x120 * 2) - -/* PS-mode related parameters */ -#define M_DOT11_SLOT (0x008 * 2) -#define M_DOT11_DTIMPERIOD (0x009 * 2) -#define M_NOSLPZNATDTIM (0x026 * 2) - -/* Beacon-related parameters */ -#define M_BCN0_FRM_BYTESZ (0x00c * 2) /* Bcn 0 template length */ -#define M_BCN1_FRM_BYTESZ (0x00d * 2) /* Bcn 1 template length */ -#define M_BCN_TXTSF_OFFSET (0x00e * 2) -#define M_TIMBPOS_INBEACON (0x00f * 2) -#define M_SFRMTXCNTFBRTHSD (0x022 * 2) -#define M_LFRMTXCNTFBRTHSD (0x023 * 2) -#define M_BCN_PCTLWD (0x02a * 2) -#define M_BCN_LI (0x05b * 2) /* beacon listen interval */ - -/* MAX Rx Frame len */ -#define M_MAXRXFRM_LEN (0x010 * 2) - -/* ACK/CTS related params */ -#define M_RSP_PCTLWD (0x011 * 2) - -/* Hardware Power Control */ -#define M_TXPWR_N (0x012 * 2) -#define M_TXPWR_TARGET (0x013 * 2) -#define M_TXPWR_MAX (0x014 * 2) -#define M_TXPWR_CUR (0x019 * 2) - -/* Rx-related parameters */ -#define M_RX_PAD_DATA_OFFSET (0x01a * 2) - -/* WEP Shared mem data */ -#define M_SEC_DEFIVLOC (0x01e * 2) -#define M_SEC_VALNUMSOFTMCHTA (0x01f * 2) -#define M_PHYVER (0x028 * 2) -#define M_PHYTYPE (0x029 * 2) -#define M_SECRXKEYS_PTR (0x02b * 2) -#define M_TKMICKEYS_PTR (0x059 * 2) -#define M_SECKINDXALGO_BLK (0x2ea * 2) -#define M_SECKINDXALGO_BLK_SZ 54 -#define M_SECPSMRXTAMCH_BLK (0x2fa * 2) -#define M_TKIP_TSC_TTAK (0x18c * 2) -#define D11_MAX_KEY_SIZE 16 - -#define M_MAX_ANTCNT (0x02e * 2) /* antenna swap threshold */ - -/* Probe response related parameters */ -#define M_SSIDLEN (0x024 * 2) -#define M_PRB_RESP_FRM_LEN (0x025 * 2) -#define M_PRS_MAXTIME (0x03a * 2) -#define M_SSID (0xb0 * 2) -#define M_CTXPRS_BLK (0xc0 * 2) -#define C_CTX_PCTLWD_POS (0x4 * 2) - -/* Delta between OFDM and CCK power in CCK power boost mode */ -#define M_OFDM_OFFSET (0x027 * 2) - -/* TSSI for last 4 11b/g CCK packets transmitted */ -#define M_B_TSSI_0 (0x02c * 2) -#define M_B_TSSI_1 (0x02d * 2) - -/* Host flags to turn on ucode options */ -#define M_HOST_FLAGS1 (0x02f * 2) -#define M_HOST_FLAGS2 (0x030 * 2) -#define M_HOST_FLAGS3 (0x031 * 2) -#define M_HOST_FLAGS4 (0x03c * 2) -#define M_HOST_FLAGS5 (0x06a * 2) -#define M_HOST_FLAGS_SZ 16 - -#define M_RADAR_REG (0x033 * 2) - -/* TSSI for last 4 11a OFDM packets transmitted */ -#define M_A_TSSI_0 (0x034 * 2) -#define M_A_TSSI_1 (0x035 * 2) - -/* noise interference measurement */ -#define M_NOISE_IF_COUNT (0x034 * 2) -#define M_NOISE_IF_TIMEOUT (0x035 * 2) - -#define M_RF_RX_SP_REG1 (0x036 * 2) - -/* TSSI for last 4 11g OFDM packets transmitted */ -#define M_G_TSSI_0 (0x038 * 2) -#define M_G_TSSI_1 (0x039 * 2) - -/* Background noise measure */ -#define M_JSSI_0 (0x44 * 2) -#define M_JSSI_1 (0x45 * 2) -#define M_JSSI_AUX (0x46 * 2) - -#define M_CUR_2050_RADIOCODE (0x47 * 2) - -/* TX fifo sizes */ -#define M_FIFOSIZE0 (0x4c * 2) -#define M_FIFOSIZE1 (0x4d * 2) -#define M_FIFOSIZE2 (0x4e * 2) -#define M_FIFOSIZE3 (0x4f * 2) -#define D11_MAX_TX_FRMS 32 /* max frames allowed in tx fifo */ - -/* Current channel number plus upper bits */ -#define M_CURCHANNEL (0x50 * 2) -#define D11_CURCHANNEL_5G 0x0100; -#define D11_CURCHANNEL_40 0x0200; -#define D11_CURCHANNEL_MAX 0x00FF; - -/* last posted frameid on the bcmc fifo */ -#define M_BCMC_FID (0x54 * 2) -#define INVALIDFID 0xffff - -/* extended beacon phyctl bytes for 11N */ -#define M_BCN_PCTL1WD (0x058 * 2) - -/* idle busy ratio to duty_cycle requirement */ -#define M_TX_IDLE_BUSY_RATIO_X_16_CCK (0x52 * 2) -#define M_TX_IDLE_BUSY_RATIO_X_16_OFDM (0x5A * 2) - -/* CW RSSI for LCNPHY */ -#define M_LCN_RSSI_0 0x1332 -#define M_LCN_RSSI_1 0x1338 -#define M_LCN_RSSI_2 0x133e -#define M_LCN_RSSI_3 0x1344 - -/* SNR for LCNPHY */ -#define M_LCN_SNR_A_0 0x1334 -#define M_LCN_SNR_B_0 0x1336 - -#define M_LCN_SNR_A_1 0x133a -#define M_LCN_SNR_B_1 0x133c - -#define M_LCN_SNR_A_2 0x1340 -#define M_LCN_SNR_B_2 0x1342 - -#define M_LCN_SNR_A_3 0x1346 -#define M_LCN_SNR_B_3 0x1348 - -#define M_LCN_LAST_RESET (81*2) -#define M_LCN_LAST_LOC (63*2) -#define M_LCNPHY_RESET_STATUS (4902) -#define M_LCNPHY_DSC_TIME (0x98d*2) -#define M_LCNPHY_RESET_CNT_DSC (0x98b*2) -#define M_LCNPHY_RESET_CNT (0x98c*2) - -/* Rate table offsets */ -#define M_RT_DIRMAP_A (0xe0 * 2) -#define M_RT_BBRSMAP_A (0xf0 * 2) -#define M_RT_DIRMAP_B (0x100 * 2) -#define M_RT_BBRSMAP_B (0x110 * 2) - -/* Rate table entry offsets */ -#define M_RT_PRS_PLCP_POS 10 -#define M_RT_PRS_DUR_POS 16 -#define M_RT_OFDM_PCTL1_POS 18 - -#define M_20IN40_IQ (0x380 * 2) - -/* SHM locations where ucode stores the current power index */ -#define M_CURR_IDX1 (0x384 * 2) -#define M_CURR_IDX2 (0x387 * 2) - -#define M_BSCALE_ANT0 (0x5e * 2) -#define M_BSCALE_ANT1 (0x5f * 2) - -/* Antenna Diversity Testing */ -#define M_MIMO_ANTSEL_RXDFLT (0x63 * 2) -#define M_ANTSEL_CLKDIV (0x61 * 2) -#define M_MIMO_ANTSEL_TXDFLT (0x64 * 2) - -#define M_MIMO_MAXSYM (0x5d * 2) -#define MIMO_MAXSYM_DEF 0x8000 /* 32k */ -#define MIMO_MAXSYM_MAX 0xffff /* 64k */ - -#define M_WATCHDOG_8TU (0x1e * 2) -#define WATCHDOG_8TU_DEF 5 -#define WATCHDOG_8TU_MAX 10 - -/* Manufacturing Test Variables */ -/* PER test mode */ -#define M_PKTENG_CTRL (0x6c * 2) -/* IFS for TX mode */ -#define M_PKTENG_IFS (0x6d * 2) -/* Lower word of tx frmcnt/rx lostcnt */ -#define M_PKTENG_FRMCNT_LO (0x6e * 2) -/* Upper word of tx frmcnt/rx lostcnt */ -#define M_PKTENG_FRMCNT_HI (0x6f * 2) - -/* Index variation in vbat ripple */ -#define M_LCN_PWR_IDX_MAX (0x67 * 2) /* highest index read by ucode */ -#define M_LCN_PWR_IDX_MIN (0x66 * 2) /* lowest index read by ucode */ - -/* M_PKTENG_CTRL bit definitions */ -#define M_PKTENG_MODE_TX 0x0001 -#define M_PKTENG_MODE_TX_RIFS 0x0004 -#define M_PKTENG_MODE_TX_CTS 0x0008 -#define M_PKTENG_MODE_RX 0x0002 -#define M_PKTENG_MODE_RX_WITH_ACK 0x0402 -#define M_PKTENG_MODE_MASK 0x0003 -/* TX frames indicated in the frmcnt reg */ -#define M_PKTENG_FRMCNT_VLD 0x0100 - -/* Sample Collect parameters (bitmap and type) */ -/* Trigger bitmap for sample collect */ -#define M_SMPL_COL_BMP (0x37d * 2) -/* Sample collect type */ -#define M_SMPL_COL_CTL (0x3b2 * 2) - -#define ANTSEL_CLKDIV_4MHZ 6 -#define MIMO_ANTSEL_BUSY 0x4000 /* bit 14 (busy) */ -#define MIMO_ANTSEL_SEL 0x8000 /* bit 15 write the value */ -#define MIMO_ANTSEL_WAIT 50 /* 50us wait */ -#define MIMO_ANTSEL_OVERRIDE 0x8000 /* flag */ - -struct shm_acparams { - u16 txop; - u16 cwmin; - u16 cwmax; - u16 cwcur; - u16 aifs; - u16 bslots; - u16 reggap; - u16 status; - u16 rsvd[8]; -} __packed; -#define M_EDCF_QLEN (16 * 2) - -#define WME_STATUS_NEWAC (1 << 8) - -/* M_HOST_FLAGS */ -#define MHFMAX 5 /* Number of valid hostflag half-word (u16) */ -#define MHF1 0 /* Hostflag 1 index */ -#define MHF2 1 /* Hostflag 2 index */ -#define MHF3 2 /* Hostflag 3 index */ -#define MHF4 3 /* Hostflag 4 index */ -#define MHF5 4 /* Hostflag 5 index */ - -/* Flags in M_HOST_FLAGS */ -/* Enable ucode antenna diversity help */ -#define MHF1_ANTDIV 0x0001 -/* Enable EDCF access control */ -#define MHF1_EDCF 0x0100 -#define MHF1_IQSWAP_WAR 0x0200 -/* Disable Slow clock request, for corerev < 11 */ -#define MHF1_FORCEFASTCLK 0x0400 - -/* Flags in M_HOST_FLAGS2 */ - -/* Flush BCMC FIFO immediately */ -#define MHF2_TXBCMC_NOW 0x0040 -/* Enable ucode/hw power control */ -#define MHF2_HWPWRCTL 0x0080 -#define MHF2_NPHY40MHZ_WAR 0x0800 - -/* Flags in M_HOST_FLAGS3 */ -/* enabled mimo antenna selection */ -#define MHF3_ANTSEL_EN 0x0001 -/* antenna selection mode: 0: 2x3, 1: 2x4 */ -#define MHF3_ANTSEL_MODE 0x0002 -#define MHF3_RESERVED1 0x0004 -#define MHF3_RESERVED2 0x0008 -#define MHF3_NPHY_MLADV_WAR 0x0010 - -/* Flags in M_HOST_FLAGS4 */ -/* force bphy Tx on core 0 (board level WAR) */ -#define MHF4_BPHY_TXCORE0 0x0080 -/* for 4313A0 FEM boards */ -#define MHF4_EXTPA_ENABLE 0x4000 - -/* Flags in M_HOST_FLAGS5 */ -#define MHF5_4313_GPIOCTRL 0x0001 -#define MHF5_RESERVED1 0x0002 -#define MHF5_RESERVED2 0x0004 -/* Radio power setting for ucode */ -#define M_RADIO_PWR (0x32 * 2) - -/* phy noise recorded by ucode right after tx */ -#define M_PHY_NOISE (0x037 * 2) -#define PHY_NOISE_MASK 0x00ff - -/* - * Receive Frame Data Header for 802.11b DCF-only frames - * - * RxFrameSize: Actual byte length of the frame data received - * PAD: padding (not used) - * PhyRxStatus_0: PhyRxStatus 15:0 - * PhyRxStatus_1: PhyRxStatus 31:16 - * PhyRxStatus_2: PhyRxStatus 47:32 - * PhyRxStatus_3: PhyRxStatus 63:48 - * PhyRxStatus_4: PhyRxStatus 79:64 - * PhyRxStatus_5: PhyRxStatus 95:80 - * RxStatus1: MAC Rx Status - * RxStatus2: extended MAC Rx status - * RxTSFTime: RxTSFTime time of first MAC symbol + M_PHY_PLCPRX_DLY - * RxChan: gain code, channel radio code, and phy type - */ -struct d11rxhdr_le { - __le16 RxFrameSize; - u16 PAD; - __le16 PhyRxStatus_0; - __le16 PhyRxStatus_1; - __le16 PhyRxStatus_2; - __le16 PhyRxStatus_3; - __le16 PhyRxStatus_4; - __le16 PhyRxStatus_5; - __le16 RxStatus1; - __le16 RxStatus2; - __le16 RxTSFTime; - __le16 RxChan; -} __packed; - -struct d11rxhdr { - u16 RxFrameSize; - u16 PAD; - u16 PhyRxStatus_0; - u16 PhyRxStatus_1; - u16 PhyRxStatus_2; - u16 PhyRxStatus_3; - u16 PhyRxStatus_4; - u16 PhyRxStatus_5; - u16 RxStatus1; - u16 RxStatus2; - u16 RxTSFTime; - u16 RxChan; -} __packed; - -/* PhyRxStatus_0: */ -/* NPHY only: CCK, OFDM, preN, N */ -#define PRXS0_FT_MASK 0x0003 -/* NPHY only: clip count adjustment steps by AGC */ -#define PRXS0_CLIP_MASK 0x000C -#define PRXS0_CLIP_SHIFT 2 -/* PHY received a frame with unsupported rate */ -#define PRXS0_UNSRATE 0x0010 -/* GPHY: rx ant, NPHY: upper sideband */ -#define PRXS0_RXANT_UPSUBBAND 0x0020 -/* CCK frame only: lost crs during cck frame reception */ -#define PRXS0_LCRS 0x0040 -/* Short Preamble */ -#define PRXS0_SHORTH 0x0080 -/* PLCP violation */ -#define PRXS0_PLCPFV 0x0100 -/* PLCP header integrity check failed */ -#define PRXS0_PLCPHCF 0x0200 -/* legacy PHY gain control */ -#define PRXS0_GAIN_CTL 0x4000 -/* NPHY: Antennas used for received frame, bitmask */ -#define PRXS0_ANTSEL_MASK 0xF000 -#define PRXS0_ANTSEL_SHIFT 0x12 - -/* subfield PRXS0_FT_MASK */ -#define PRXS0_CCK 0x0000 -/* valid only for G phy, use rxh->RxChan for A phy */ -#define PRXS0_OFDM 0x0001 -#define PRXS0_PREN 0x0002 -#define PRXS0_STDN 0x0003 - -/* subfield PRXS0_ANTSEL_MASK */ -#define PRXS0_ANTSEL_0 0x0 /* antenna 0 is used */ -#define PRXS0_ANTSEL_1 0x2 /* antenna 1 is used */ -#define PRXS0_ANTSEL_2 0x4 /* antenna 2 is used */ -#define PRXS0_ANTSEL_3 0x8 /* antenna 3 is used */ - -/* PhyRxStatus_1: */ -#define PRXS1_JSSI_MASK 0x00FF -#define PRXS1_JSSI_SHIFT 0 -#define PRXS1_SQ_MASK 0xFF00 -#define PRXS1_SQ_SHIFT 8 - -/* nphy PhyRxStatus_1: */ -#define PRXS1_nphy_PWR0_MASK 0x00FF -#define PRXS1_nphy_PWR1_MASK 0xFF00 - -/* HTPHY Rx Status defines */ -/* htphy PhyRxStatus_0: those bit are overlapped with PhyRxStatus_0 */ -#define PRXS0_BAND 0x0400 /* 0 = 2.4G, 1 = 5G */ -#define PRXS0_RSVD 0x0800 /* reserved; set to 0 */ -#define PRXS0_UNUSED 0xF000 /* unused and not defined; set to 0 */ - -/* htphy PhyRxStatus_1: */ -/* core enables for {3..0}, 0=disabled, 1=enabled */ -#define PRXS1_HTPHY_CORE_MASK 0x000F -/* antenna configation */ -#define PRXS1_HTPHY_ANTCFG_MASK 0x00F0 -/* Mixmode PLCP Length low byte mask */ -#define PRXS1_HTPHY_MMPLCPLenL_MASK 0xFF00 - -/* htphy PhyRxStatus_2: */ -/* Mixmode PLCP Length high byte maskw */ -#define PRXS2_HTPHY_MMPLCPLenH_MASK 0x000F -/* Mixmode PLCP rate mask */ -#define PRXS2_HTPHY_MMPLCH_RATE_MASK 0x00F0 -/* Rx power on core 0 */ -#define PRXS2_HTPHY_RXPWR_ANT0 0xFF00 - -/* htphy PhyRxStatus_3: */ -/* Rx power on core 1 */ -#define PRXS3_HTPHY_RXPWR_ANT1 0x00FF -/* Rx power on core 2 */ -#define PRXS3_HTPHY_RXPWR_ANT2 0xFF00 - -/* htphy PhyRxStatus_4: */ -/* Rx power on core 3 */ -#define PRXS4_HTPHY_RXPWR_ANT3 0x00FF -/* Coarse frequency offset */ -#define PRXS4_HTPHY_CFO 0xFF00 - -/* htphy PhyRxStatus_5: */ -/* Fine frequency offset */ -#define PRXS5_HTPHY_FFO 0x00FF -/* Advance Retard */ -#define PRXS5_HTPHY_AR 0xFF00 - -#define HTPHY_MMPLCPLen(rxs) \ - ((((rxs)->PhyRxStatus_1 & PRXS1_HTPHY_MMPLCPLenL_MASK) >> 8) | \ - (((rxs)->PhyRxStatus_2 & PRXS2_HTPHY_MMPLCPLenH_MASK) << 8)) -/* Get Rx power on core 0 */ -#define HTPHY_RXPWR_ANT0(rxs) \ - ((((rxs)->PhyRxStatus_2) & PRXS2_HTPHY_RXPWR_ANT0) >> 8) -/* Get Rx power on core 1 */ -#define HTPHY_RXPWR_ANT1(rxs) \ - (((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT1) -/* Get Rx power on core 2 */ -#define HTPHY_RXPWR_ANT2(rxs) \ - ((((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT2) >> 8) - -/* ucode RxStatus1: */ -#define RXS_BCNSENT 0x8000 -#define RXS_SECKINDX_MASK 0x07e0 -#define RXS_SECKINDX_SHIFT 5 -#define RXS_DECERR (1 << 4) -#define RXS_DECATMPT (1 << 3) -/* PAD bytes to make IP data 4 bytes aligned */ -#define RXS_PBPRES (1 << 2) -#define RXS_RESPFRAMETX (1 << 1) -#define RXS_FCSERR (1 << 0) - -/* ucode RxStatus2: */ -#define RXS_AMSDU_MASK 1 -#define RXS_AGGTYPE_MASK 0x6 -#define RXS_AGGTYPE_SHIFT 1 -#define RXS_PHYRXST_VALID (1 << 8) -#define RXS_RXANT_MASK 0x3 -#define RXS_RXANT_SHIFT 12 - -/* RxChan */ -#define RXS_CHAN_40 0x1000 -#define RXS_CHAN_5G 0x0800 -#define RXS_CHAN_ID_MASK 0x07f8 -#define RXS_CHAN_ID_SHIFT 3 -#define RXS_CHAN_PHYTYPE_MASK 0x0007 -#define RXS_CHAN_PHYTYPE_SHIFT 0 - -/* Index of attenuations used during ucode power control. */ -#define M_PWRIND_BLKS (0x184 * 2) -#define M_PWRIND_MAP0 (M_PWRIND_BLKS + 0x0) -#define M_PWRIND_MAP1 (M_PWRIND_BLKS + 0x2) -#define M_PWRIND_MAP2 (M_PWRIND_BLKS + 0x4) -#define M_PWRIND_MAP3 (M_PWRIND_BLKS + 0x6) -/* M_PWRIND_MAP(core) macro */ -#define M_PWRIND_MAP(core) (M_PWRIND_BLKS + ((core)<<1)) - -/* PSM SHM variable offsets */ -#define M_PSM_SOFT_REGS 0x0 -#define M_BOM_REV_MAJOR (M_PSM_SOFT_REGS + 0x0) -#define M_BOM_REV_MINOR (M_PSM_SOFT_REGS + 0x2) -#define M_UCODE_DBGST (M_PSM_SOFT_REGS + 0x40) /* ucode debug status code */ -#define M_UCODE_MACSTAT (M_PSM_SOFT_REGS + 0xE0) /* macstat counters */ - -#define M_AGING_THRSH (0x3e * 2) /* max time waiting for medium before tx */ -#define M_MBURST_SIZE (0x40 * 2) /* max frames in a frameburst */ -#define M_MBURST_TXOP (0x41 * 2) /* max frameburst TXOP in unit of us */ -#define M_SYNTHPU_DLY (0x4a * 2) /* pre-wakeup for synthpu, default: 500 */ -#define M_PRETBTT (0x4b * 2) - -/* offset to the target txpwr */ -#define M_ALT_TXPWR_IDX (M_PSM_SOFT_REGS + (0x3b * 2)) -#define M_PHY_TX_FLT_PTR (M_PSM_SOFT_REGS + (0x3d * 2)) -#define M_CTS_DURATION (M_PSM_SOFT_REGS + (0x5c * 2)) -#define M_LP_RCCAL_OVR (M_PSM_SOFT_REGS + (0x6b * 2)) - -/* PKTENG Rx Stats Block */ -#define M_RXSTATS_BLK_PTR (M_PSM_SOFT_REGS + (0x65 * 2)) - -/* ucode debug status codes */ -/* not valid really */ -#define DBGST_INACTIVE 0 -/* after zeroing SHM, before suspending at init */ -#define DBGST_INIT 1 -/* "normal" state */ -#define DBGST_ACTIVE 2 -/* suspended */ -#define DBGST_SUSPENDED 3 -/* asleep (PS mode) */ -#define DBGST_ASLEEP 4 - -/* Scratch Reg defs */ -enum _ePsmScratchPadRegDefinitions { - S_RSV0 = 0, - S_RSV1, - S_RSV2, - - /* offset 0x03: scratch registers for Dot11-contants */ - S_DOT11_CWMIN, /* CW-minimum */ - S_DOT11_CWMAX, /* CW-maximum */ - S_DOT11_CWCUR, /* CW-current */ - S_DOT11_SRC_LMT, /* short retry count limit */ - S_DOT11_LRC_LMT, /* long retry count limit */ - S_DOT11_DTIMCOUNT, /* DTIM-count */ - - /* offset 0x09: Tx-side scratch registers */ - S_SEQ_NUM, /* hardware sequence number reg */ - S_SEQ_NUM_FRAG, /* seq num for frags (at the start of MSDU) */ - S_FRMRETX_CNT, /* frame retx count */ - S_SSRC, /* Station short retry count */ - S_SLRC, /* Station long retry count */ - S_EXP_RSP, /* Expected response frame */ - S_OLD_BREM, /* Remaining backoff ctr */ - S_OLD_CWWIN, /* saved-off CW-cur */ - S_TXECTL, /* TXE-Ctl word constructed in scr-pad */ - S_CTXTST, /* frm type-subtype as read from Tx-descr */ - - /* offset 0x13: Rx-side scratch registers */ - S_RXTST, /* Type and subtype in Rxframe */ - - /* Global state register */ - S_STREG, /* state storage actual bit maps below */ - - S_TXPWR_SUM, /* Tx power control: accumulator */ - S_TXPWR_ITER, /* Tx power control: iteration */ - S_RX_FRMTYPE, /* Rate and PHY type for frames */ - S_THIS_AGG, /* Size of this AGG (A-MSDU) */ - - S_KEYINDX, - S_RXFRMLEN, /* Receive MPDU length in bytes */ - - /* offset 0x1B: Receive TSF time stored in SCR */ - S_RXTSFTMRVAL_WD3, /* TSF value at the start of rx */ - S_RXTSFTMRVAL_WD2, /* TSF value at the start of rx */ - S_RXTSFTMRVAL_WD1, /* TSF value at the start of rx */ - S_RXTSFTMRVAL_WD0, /* TSF value at the start of rx */ - S_RXSSN, /* Received start seq number for A-MPDU BA */ - S_RXQOSFLD, /* Rx-QoS field (if present) */ - - /* offset 0x21: Scratch pad regs used in microcode as temp storage */ - S_TMP0, /* stmp0 */ - S_TMP1, /* stmp1 */ - S_TMP2, /* stmp2 */ - S_TMP3, /* stmp3 */ - S_TMP4, /* stmp4 */ - S_TMP5, /* stmp5 */ - S_PRQPENALTY_CTR, /* Probe response queue penalty counter */ - S_ANTCNT, /* unsuccessful attempts on current ant. */ - S_SYMBOL, /* flag for possible symbol ctl frames */ - S_RXTP, /* rx frame type */ - S_STREG2, /* extra state storage */ - S_STREG3, /* even more extra state storage */ - S_STREG4, /* ... */ - S_STREG5, /* remember to initialize it to zero */ - - S_ADJPWR_IDX, - S_CUR_PTR, /* Temp pointer for A-MPDU re-Tx SHM table */ - S_REVID4, /* 0x33 */ - S_INDX, /* 0x34 */ - S_ADDR0, /* 0x35 */ - S_ADDR1, /* 0x36 */ - S_ADDR2, /* 0x37 */ - S_ADDR3, /* 0x38 */ - S_ADDR4, /* 0x39 */ - S_ADDR5, /* 0x3A */ - S_TMP6, /* 0x3B */ - S_KEYINDX_BU, /* Backup for Key index */ - S_MFGTEST_TMP0, /* Temp regs used for RX test calculations */ - S_RXESN, /* Received end sequence number for A-MPDU BA */ - S_STREG6, /* 0x3F */ -}; - -#define S_BEACON_INDX S_OLD_BREM -#define S_PRS_INDX S_OLD_CWWIN -#define S_PHYTYPE S_SSRC -#define S_PHYVER S_SLRC - -/* IHR SLOW_CTRL values */ -#define SLOW_CTRL_PDE (1 << 0) -#define SLOW_CTRL_FD (1 << 8) - -/* ucode mac statistic counters in shared memory */ -struct macstat { - u16 txallfrm; /* 0x80 */ - u16 txrtsfrm; /* 0x82 */ - u16 txctsfrm; /* 0x84 */ - u16 txackfrm; /* 0x86 */ - u16 txdnlfrm; /* 0x88 */ - u16 txbcnfrm; /* 0x8a */ - u16 txfunfl[8]; /* 0x8c - 0x9b */ - u16 txtplunfl; /* 0x9c */ - u16 txphyerr; /* 0x9e */ - u16 pktengrxducast; /* 0xa0 */ - u16 pktengrxdmcast; /* 0xa2 */ - u16 rxfrmtoolong; /* 0xa4 */ - u16 rxfrmtooshrt; /* 0xa6 */ - u16 rxinvmachdr; /* 0xa8 */ - u16 rxbadfcs; /* 0xaa */ - u16 rxbadplcp; /* 0xac */ - u16 rxcrsglitch; /* 0xae */ - u16 rxstrt; /* 0xb0 */ - u16 rxdfrmucastmbss; /* 0xb2 */ - u16 rxmfrmucastmbss; /* 0xb4 */ - u16 rxcfrmucast; /* 0xb6 */ - u16 rxrtsucast; /* 0xb8 */ - u16 rxctsucast; /* 0xba */ - u16 rxackucast; /* 0xbc */ - u16 rxdfrmocast; /* 0xbe */ - u16 rxmfrmocast; /* 0xc0 */ - u16 rxcfrmocast; /* 0xc2 */ - u16 rxrtsocast; /* 0xc4 */ - u16 rxctsocast; /* 0xc6 */ - u16 rxdfrmmcast; /* 0xc8 */ - u16 rxmfrmmcast; /* 0xca */ - u16 rxcfrmmcast; /* 0xcc */ - u16 rxbeaconmbss; /* 0xce */ - u16 rxdfrmucastobss; /* 0xd0 */ - u16 rxbeaconobss; /* 0xd2 */ - u16 rxrsptmout; /* 0xd4 */ - u16 bcntxcancl; /* 0xd6 */ - u16 PAD; - u16 rxf0ovfl; /* 0xda */ - u16 rxf1ovfl; /* 0xdc */ - u16 rxf2ovfl; /* 0xde */ - u16 txsfovfl; /* 0xe0 */ - u16 pmqovfl; /* 0xe2 */ - u16 rxcgprqfrm; /* 0xe4 */ - u16 rxcgprsqovfl; /* 0xe6 */ - u16 txcgprsfail; /* 0xe8 */ - u16 txcgprssuc; /* 0xea */ - u16 prs_timeout; /* 0xec */ - u16 rxnack; - u16 frmscons; - u16 txnack; - u16 txglitch_nack; - u16 txburst; /* 0xf6 # tx bursts */ - u16 bphy_rxcrsglitch; /* bphy rx crs glitch */ - u16 phywatchdog; /* 0xfa # of phy watchdog events */ - u16 PAD; - u16 bphy_badplcp; /* bphy bad plcp */ -}; - -/* dot11 core-specific control flags */ -#define SICF_PCLKE 0x0004 /* PHY clock enable */ -#define SICF_PRST 0x0008 /* PHY reset */ -#define SICF_MPCLKE 0x0010 /* MAC PHY clockcontrol enable */ -#define SICF_FREF 0x0020 /* PLL FreqRefSelect */ -/* NOTE: the following bw bits only apply when the core is attached - * to a NPHY - */ -#define SICF_BWMASK 0x00c0 /* phy clock mask (b6 & b7) */ -#define SICF_BW40 0x0080 /* 40MHz BW (160MHz phyclk) */ -#define SICF_BW20 0x0040 /* 20MHz BW (80MHz phyclk) */ -#define SICF_BW10 0x0000 /* 10MHz BW (40MHz phyclk) */ -#define SICF_GMODE 0x2000 /* gmode enable */ - -/* dot11 core-specific status flags */ -#define SISF_2G_PHY 0x0001 /* 2.4G capable phy */ -#define SISF_5G_PHY 0x0002 /* 5G capable phy */ -#define SISF_FCLKA 0x0004 /* FastClkAvailable */ -#define SISF_DB_PHY 0x0008 /* Dualband phy */ - -/* === End of MAC reg, Beginning of PHY(b/a/g/n) reg === */ -/* radio and LPPHY regs are separated */ - -#define BPHY_REG_OFT_BASE 0x0 -/* offsets for indirect access to bphy registers */ -#define BPHY_BB_CONFIG 0x01 -#define BPHY_ADCBIAS 0x02 -#define BPHY_ANACORE 0x03 -#define BPHY_PHYCRSTH 0x06 -#define BPHY_TEST 0x0a -#define BPHY_PA_TX_TO 0x10 -#define BPHY_SYNTH_DC_TO 0x11 -#define BPHY_PA_TX_TIME_UP 0x12 -#define BPHY_RX_FLTR_TIME_UP 0x13 -#define BPHY_TX_POWER_OVERRIDE 0x14 -#define BPHY_RF_OVERRIDE 0x15 -#define BPHY_RF_TR_LOOKUP1 0x16 -#define BPHY_RF_TR_LOOKUP2 0x17 -#define BPHY_COEFFS 0x18 -#define BPHY_PLL_OUT 0x19 -#define BPHY_REFRESH_MAIN 0x1a -#define BPHY_REFRESH_TO0 0x1b -#define BPHY_REFRESH_TO1 0x1c -#define BPHY_RSSI_TRESH 0x20 -#define BPHY_IQ_TRESH_HH 0x21 -#define BPHY_IQ_TRESH_H 0x22 -#define BPHY_IQ_TRESH_L 0x23 -#define BPHY_IQ_TRESH_LL 0x24 -#define BPHY_GAIN 0x25 -#define BPHY_LNA_GAIN_RANGE 0x26 -#define BPHY_JSSI 0x27 -#define BPHY_TSSI_CTL 0x28 -#define BPHY_TSSI 0x29 -#define BPHY_TR_LOSS_CTL 0x2a -#define BPHY_LO_LEAKAGE 0x2b -#define BPHY_LO_RSSI_ACC 0x2c -#define BPHY_LO_IQMAG_ACC 0x2d -#define BPHY_TX_DC_OFF1 0x2e -#define BPHY_TX_DC_OFF2 0x2f -#define BPHY_PEAK_CNT_THRESH 0x30 -#define BPHY_FREQ_OFFSET 0x31 -#define BPHY_DIVERSITY_CTL 0x32 -#define BPHY_PEAK_ENERGY_LO 0x33 -#define BPHY_PEAK_ENERGY_HI 0x34 -#define BPHY_SYNC_CTL 0x35 -#define BPHY_TX_PWR_CTRL 0x36 -#define BPHY_TX_EST_PWR 0x37 -#define BPHY_STEP 0x38 -#define BPHY_WARMUP 0x39 -#define BPHY_LMS_CFF_READ 0x3a -#define BPHY_LMS_COEFF_I 0x3b -#define BPHY_LMS_COEFF_Q 0x3c -#define BPHY_SIG_POW 0x3d -#define BPHY_RFDC_CANCEL_CTL 0x3e -#define BPHY_HDR_TYPE 0x40 -#define BPHY_SFD_TO 0x41 -#define BPHY_SFD_CTL 0x42 -#define BPHY_DEBUG 0x43 -#define BPHY_RX_DELAY_COMP 0x44 -#define BPHY_CRS_DROP_TO 0x45 -#define BPHY_SHORT_SFD_NZEROS 0x46 -#define BPHY_DSSS_COEFF1 0x48 -#define BPHY_DSSS_COEFF2 0x49 -#define BPHY_CCK_COEFF1 0x4a -#define BPHY_CCK_COEFF2 0x4b -#define BPHY_TR_CORR 0x4c -#define BPHY_ANGLE_SCALE 0x4d -#define BPHY_TX_PWR_BASE_IDX 0x4e -#define BPHY_OPTIONAL_MODES2 0x4f -#define BPHY_CCK_LMS_STEP 0x50 -#define BPHY_BYPASS 0x51 -#define BPHY_CCK_DELAY_LONG 0x52 -#define BPHY_CCK_DELAY_SHORT 0x53 -#define BPHY_PPROC_CHAN_DELAY 0x54 -#define BPHY_DDFS_ENABLE 0x58 -#define BPHY_PHASE_SCALE 0x59 -#define BPHY_FREQ_CONTROL 0x5a -#define BPHY_LNA_GAIN_RANGE_10 0x5b -#define BPHY_LNA_GAIN_RANGE_32 0x5c -#define BPHY_OPTIONAL_MODES 0x5d -#define BPHY_RX_STATUS2 0x5e -#define BPHY_RX_STATUS3 0x5f -#define BPHY_DAC_CONTROL 0x60 -#define BPHY_ANA11G_FILT_CTRL 0x62 -#define BPHY_REFRESH_CTRL 0x64 -#define BPHY_RF_OVERRIDE2 0x65 -#define BPHY_SPUR_CANCEL_CTRL 0x66 -#define BPHY_FINE_DIGIGAIN_CTRL 0x67 -#define BPHY_RSSI_LUT 0x88 -#define BPHY_RSSI_LUT_END 0xa7 -#define BPHY_TSSI_LUT 0xa8 -#define BPHY_TSSI_LUT_END 0xc7 -#define BPHY_TSSI2PWR_LUT 0x380 -#define BPHY_TSSI2PWR_LUT_END 0x39f -#define BPHY_LOCOMP_LUT 0x3a0 -#define BPHY_LOCOMP_LUT_END 0x3bf -#define BPHY_TXGAIN_LUT 0x3c0 -#define BPHY_TXGAIN_LUT_END 0x3ff - -/* Bits in BB_CONFIG: */ -#define PHY_BBC_ANT_MASK 0x0180 -#define PHY_BBC_ANT_SHIFT 7 -#define BB_DARWIN 0x1000 -#define BBCFG_RESETCCA 0x4000 -#define BBCFG_RESETRX 0x8000 - -/* Bits in phytest(0x0a): */ -#define TST_DDFS 0x2000 -#define TST_TXFILT1 0x0800 -#define TST_UNSCRAM 0x0400 -#define TST_CARR_SUPP 0x0200 -#define TST_DC_COMP_LOOP 0x0100 -#define TST_LOOPBACK 0x0080 -#define TST_TXFILT0 0x0040 -#define TST_TXTEST_ENABLE 0x0020 -#define TST_TXTEST_RATE 0x0018 -#define TST_TXTEST_PHASE 0x0007 - -/* phytest txTestRate values */ -#define TST_TXTEST_RATE_1MBPS 0 -#define TST_TXTEST_RATE_2MBPS 1 -#define TST_TXTEST_RATE_5_5MBPS 2 -#define TST_TXTEST_RATE_11MBPS 3 -#define TST_TXTEST_RATE_SHIFT 3 - -#define SHM_BYT_CNT 0x2 /* IHR location */ -#define MAX_BYT_CNT 0x600 /* Maximum frame len */ - -struct d11cnt { - u32 txfrag; - u32 txmulti; - u32 txfail; - u32 txretry; - u32 txretrie; - u32 rxdup; - u32 txrts; - u32 txnocts; - u32 txnoack; - u32 rxfrag; - u32 rxmulti; - u32 rxcrc; - u32 txfrmsnt; - u32 rxundec; -}; - -#endif /* _BRCM_D11_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/brcm80211/brcmsmac/debug.c deleted file mode 100644 index 7a1fbb2e3a71..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/debug.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (c) 2012 Broadcom Corporation - * Copyright (c) 2012 Canonical Ltd. - * - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include "types.h" -#include "main.h" -#include "debug.h" -#include "brcms_trace_events.h" -#include "phy/phy_int.h" - -static struct dentry *root_folder; - -void brcms_debugfs_init(void) -{ - root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL); - if (IS_ERR(root_folder)) - root_folder = NULL; -} - -void brcms_debugfs_exit(void) -{ - if (!root_folder) - return; - - debugfs_remove_recursive(root_folder); - root_folder = NULL; -} - -int brcms_debugfs_attach(struct brcms_pub *drvr) -{ - if (!root_folder) - return -ENODEV; - - drvr->dbgfs_dir = debugfs_create_dir( - dev_name(&drvr->wlc->hw->d11core->dev), root_folder); - return PTR_ERR_OR_ZERO(drvr->dbgfs_dir); -} - -void brcms_debugfs_detach(struct brcms_pub *drvr) -{ - if (!IS_ERR_OR_NULL(drvr->dbgfs_dir)) - debugfs_remove_recursive(drvr->dbgfs_dir); -} - -struct dentry *brcms_debugfs_get_devdir(struct brcms_pub *drvr) -{ - return drvr->dbgfs_dir; -} - -static -int brcms_debugfs_hardware_read(struct seq_file *s, void *data) -{ - struct brcms_pub *drvr = s->private; - struct brcms_hardware *hw = drvr->wlc->hw; - struct bcma_device *core = hw->d11core; - struct bcma_bus *bus = core->bus; - char boardrev[BRCMU_BOARDREV_LEN]; - - seq_printf(s, "chipnum 0x%x\n" - "chiprev 0x%x\n" - "chippackage 0x%x\n" - "corerev 0x%x\n" - "boardid 0x%x\n" - "boardvendor 0x%x\n" - "boardrev %s\n" - "boardflags 0x%x\n" - "boardflags2 0x%x\n" - "ucoderev 0x%x\n" - "radiorev 0x%x\n" - "phytype 0x%x\n" - "phyrev 0x%x\n" - "anarev 0x%x\n" - "nvramrev %d\n", - bus->chipinfo.id, bus->chipinfo.rev, bus->chipinfo.pkg, - core->id.rev, bus->boardinfo.type, bus->boardinfo.vendor, - brcmu_boardrev_str(hw->boardrev, boardrev), - drvr->wlc->hw->boardflags, drvr->wlc->hw->boardflags2, - drvr->wlc->ucode_rev, hw->band->radiorev, - hw->band->phytype, hw->band->phyrev, hw->band->pi->ana_rev, - hw->sromrev); - return 0; -} - -static int brcms_debugfs_macstat_read(struct seq_file *s, void *data) -{ - struct brcms_pub *drvr = s->private; - struct brcms_info *wl = drvr->ieee_hw->priv; - struct macstat stats; - int i; - - spin_lock_bh(&wl->lock); - stats = *(drvr->wlc->core->macstat_snapshot); - spin_unlock_bh(&wl->lock); - - seq_printf(s, "txallfrm: %d\n", stats.txallfrm); - seq_printf(s, "txrtsfrm: %d\n", stats.txrtsfrm); - seq_printf(s, "txctsfrm: %d\n", stats.txctsfrm); - seq_printf(s, "txackfrm: %d\n", stats.txackfrm); - seq_printf(s, "txdnlfrm: %d\n", stats.txdnlfrm); - seq_printf(s, "txbcnfrm: %d\n", stats.txbcnfrm); - seq_printf(s, "txfunfl[8]:"); - for (i = 0; i < ARRAY_SIZE(stats.txfunfl); i++) - seq_printf(s, " %d", stats.txfunfl[i]); - seq_printf(s, "\ntxtplunfl: %d\n", stats.txtplunfl); - seq_printf(s, "txphyerr: %d\n", stats.txphyerr); - seq_printf(s, "pktengrxducast: %d\n", stats.pktengrxducast); - seq_printf(s, "pktengrxdmcast: %d\n", stats.pktengrxdmcast); - seq_printf(s, "rxfrmtoolong: %d\n", stats.rxfrmtoolong); - seq_printf(s, "rxfrmtooshrt: %d\n", stats.rxfrmtooshrt); - seq_printf(s, "rxinvmachdr: %d\n", stats.rxinvmachdr); - seq_printf(s, "rxbadfcs: %d\n", stats.rxbadfcs); - seq_printf(s, "rxbadplcp: %d\n", stats.rxbadplcp); - seq_printf(s, "rxcrsglitch: %d\n", stats.rxcrsglitch); - seq_printf(s, "rxstrt: %d\n", stats.rxstrt); - seq_printf(s, "rxdfrmucastmbss: %d\n", stats.rxdfrmucastmbss); - seq_printf(s, "rxmfrmucastmbss: %d\n", stats.rxmfrmucastmbss); - seq_printf(s, "rxcfrmucast: %d\n", stats.rxcfrmucast); - seq_printf(s, "rxrtsucast: %d\n", stats.rxrtsucast); - seq_printf(s, "rxctsucast: %d\n", stats.rxctsucast); - seq_printf(s, "rxackucast: %d\n", stats.rxackucast); - seq_printf(s, "rxdfrmocast: %d\n", stats.rxdfrmocast); - seq_printf(s, "rxmfrmocast: %d\n", stats.rxmfrmocast); - seq_printf(s, "rxcfrmocast: %d\n", stats.rxcfrmocast); - seq_printf(s, "rxrtsocast: %d\n", stats.rxrtsocast); - seq_printf(s, "rxctsocast: %d\n", stats.rxctsocast); - seq_printf(s, "rxdfrmmcast: %d\n", stats.rxdfrmmcast); - seq_printf(s, "rxmfrmmcast: %d\n", stats.rxmfrmmcast); - seq_printf(s, "rxcfrmmcast: %d\n", stats.rxcfrmmcast); - seq_printf(s, "rxbeaconmbss: %d\n", stats.rxbeaconmbss); - seq_printf(s, "rxdfrmucastobss: %d\n", stats.rxdfrmucastobss); - seq_printf(s, "rxbeaconobss: %d\n", stats.rxbeaconobss); - seq_printf(s, "rxrsptmout: %d\n", stats.rxrsptmout); - seq_printf(s, "bcntxcancl: %d\n", stats.bcntxcancl); - seq_printf(s, "rxf0ovfl: %d\n", stats.rxf0ovfl); - seq_printf(s, "rxf1ovfl: %d\n", stats.rxf1ovfl); - seq_printf(s, "rxf2ovfl: %d\n", stats.rxf2ovfl); - seq_printf(s, "txsfovfl: %d\n", stats.txsfovfl); - seq_printf(s, "pmqovfl: %d\n", stats.pmqovfl); - seq_printf(s, "rxcgprqfrm: %d\n", stats.rxcgprqfrm); - seq_printf(s, "rxcgprsqovfl: %d\n", stats.rxcgprsqovfl); - seq_printf(s, "txcgprsfail: %d\n", stats.txcgprsfail); - seq_printf(s, "txcgprssuc: %d\n", stats.txcgprssuc); - seq_printf(s, "prs_timeout: %d\n", stats.prs_timeout); - seq_printf(s, "rxnack: %d\n", stats.rxnack); - seq_printf(s, "frmscons: %d\n", stats.frmscons); - seq_printf(s, "txnack: %d\n", stats.txnack); - seq_printf(s, "txglitch_nack: %d\n", stats.txglitch_nack); - seq_printf(s, "txburst: %d\n", stats.txburst); - seq_printf(s, "bphy_rxcrsglitch: %d\n", stats.bphy_rxcrsglitch); - seq_printf(s, "phywatchdog: %d\n", stats.phywatchdog); - seq_printf(s, "bphy_badplcp: %d\n", stats.bphy_badplcp); - return 0; -} - -struct brcms_debugfs_entry { - int (*read)(struct seq_file *seq, void *data); - struct brcms_pub *drvr; -}; - -static int brcms_debugfs_entry_open(struct inode *inode, struct file *f) -{ - struct brcms_debugfs_entry *entry = inode->i_private; - - return single_open(f, entry->read, entry->drvr); -} - -static const struct file_operations brcms_debugfs_def_ops = { - .owner = THIS_MODULE, - .open = brcms_debugfs_entry_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek -}; - -static int -brcms_debugfs_add_entry(struct brcms_pub *drvr, const char *fn, - int (*read_fn)(struct seq_file *seq, void *data)) -{ - struct device *dev = &drvr->wlc->hw->d11core->dev; - struct dentry *dentry = drvr->dbgfs_dir; - struct brcms_debugfs_entry *entry; - - if (IS_ERR_OR_NULL(dentry)) - return -ENOENT; - - entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - entry->read = read_fn; - entry->drvr = drvr; - - dentry = debugfs_create_file(fn, S_IRUGO, dentry, entry, - &brcms_debugfs_def_ops); - - return PTR_ERR_OR_ZERO(dentry); -} - -void brcms_debugfs_create_files(struct brcms_pub *drvr) -{ - if (IS_ERR_OR_NULL(drvr->dbgfs_dir)) - return; - - brcms_debugfs_add_entry(drvr, "hardware", brcms_debugfs_hardware_read); - brcms_debugfs_add_entry(drvr, "macstat", brcms_debugfs_macstat_read); -} - -#define __brcms_fn(fn) \ -void __brcms_ ##fn(struct device *dev, const char *fmt, ...) \ -{ \ - struct va_format vaf = { \ - .fmt = fmt, \ - }; \ - va_list args; \ - \ - va_start(args, fmt); \ - vaf.va = &args; \ - dev_ ##fn(dev, "%pV", &vaf); \ - trace_brcms_ ##fn(&vaf); \ - va_end(args); \ -} - -__brcms_fn(info) -__brcms_fn(warn) -__brcms_fn(err) -__brcms_fn(crit) - -#if defined(CONFIG_BRCMDBG) || defined(CONFIG_BRCM_TRACING) -void __brcms_dbg(struct device *dev, u32 level, const char *func, - const char *fmt, ...) -{ - struct va_format vaf = { - .fmt = fmt, - }; - va_list args; - - va_start(args, fmt); - vaf.va = &args; -#ifdef CONFIG_BRCMDBG - if ((brcm_msg_level & level) && net_ratelimit()) - dev_err(dev, "%s %pV", func, &vaf); -#endif - trace_brcms_dbg(level, func, &vaf); - va_end(args); -} -#endif diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.h b/drivers/net/wireless/brcm80211/brcmsmac/debug.h deleted file mode 100644 index 822781cf15d4..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/debug.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2012 Broadcom Corporation - * Copyright (c) 2012 Canonical Ltd. - * - * 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 _BRCMS_DEBUG_H_ -#define _BRCMS_DEBUG_H_ - -#include -#include -#include -#include -#include "main.h" -#include "mac80211_if.h" - -__printf(2, 3) -void __brcms_info(struct device *dev, const char *fmt, ...); -__printf(2, 3) -void __brcms_warn(struct device *dev, const char *fmt, ...); -__printf(2, 3) -void __brcms_err(struct device *dev, const char *fmt, ...); -__printf(2, 3) -void __brcms_crit(struct device *dev, const char *fmt, ...); - -#if defined(CONFIG_BRCMDBG) || defined(CONFIG_BRCM_TRACING) -__printf(4, 5) -void __brcms_dbg(struct device *dev, u32 level, const char *func, - const char *fmt, ...); -#else -static inline __printf(4, 5) -void __brcms_dbg(struct device *dev, u32 level, const char *func, - const char *fmt, ...) -{ -} -#endif - -/* - * Debug macros cannot be used when wlc is uninitialized. Generally - * this means any code that could run before brcms_c_attach() has - * returned successfully probably shouldn't use the following macros. - */ - -#define brcms_dbg(core, l, f, a...) __brcms_dbg(&(core)->dev, l, __func__, f, ##a) -#define brcms_info(core, f, a...) __brcms_info(&(core)->dev, f, ##a) -#define brcms_warn(core, f, a...) __brcms_warn(&(core)->dev, f, ##a) -#define brcms_err(core, f, a...) __brcms_err(&(core)->dev, f, ##a) -#define brcms_crit(core, f, a...) __brcms_crit(&(core)->dev, f, ##a) - -#define brcms_dbg_info(core, f, a...) brcms_dbg(core, BRCM_DL_INFO, f, ##a) -#define brcms_dbg_mac80211(core, f, a...) brcms_dbg(core, BRCM_DL_MAC80211, f, ##a) -#define brcms_dbg_rx(core, f, a...) brcms_dbg(core, BRCM_DL_RX, f, ##a) -#define brcms_dbg_tx(core, f, a...) brcms_dbg(core, BRCM_DL_TX, f, ##a) -#define brcms_dbg_int(core, f, a...) brcms_dbg(core, BRCM_DL_INT, f, ##a) -#define brcms_dbg_dma(core, f, a...) brcms_dbg(core, BRCM_DL_DMA, f, ##a) -#define brcms_dbg_ht(core, f, a...) brcms_dbg(core, BRCM_DL_HT, f, ##a) - -struct brcms_pub; -void brcms_debugfs_init(void); -void brcms_debugfs_exit(void); -int brcms_debugfs_attach(struct brcms_pub *drvr); -void brcms_debugfs_detach(struct brcms_pub *drvr); -struct dentry *brcms_debugfs_get_devdir(struct brcms_pub *drvr); -void brcms_debugfs_create_files(struct brcms_pub *drvr); - -#endif /* _BRCMS_DEBUG_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c deleted file mode 100644 index 796f5f9d5d5a..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c +++ /dev/null @@ -1,1564 +0,0 @@ -/* - * Copyright (c) 2010 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 -#include - -#include -#include -#include "types.h" -#include "main.h" -#include "dma.h" -#include "soc.h" -#include "scb.h" -#include "ampdu.h" -#include "debug.h" -#include "brcms_trace_events.h" - -/* - * dma register field offset calculation - */ -#define DMA64REGOFFS(field) offsetof(struct dma64regs, field) -#define DMA64TXREGOFFS(di, field) (di->d64txregbase + DMA64REGOFFS(field)) -#define DMA64RXREGOFFS(di, field) (di->d64rxregbase + DMA64REGOFFS(field)) - -/* - * DMA hardware requires each descriptor ring to be 8kB aligned, and fit within - * a contiguous 8kB physical address. - */ -#define D64RINGALIGN_BITS 13 -#define D64MAXRINGSZ (1 << D64RINGALIGN_BITS) -#define D64RINGALIGN (1 << D64RINGALIGN_BITS) - -#define D64MAXDD (D64MAXRINGSZ / sizeof(struct dma64desc)) - -/* transmit channel control */ -#define D64_XC_XE 0x00000001 /* transmit enable */ -#define D64_XC_SE 0x00000002 /* transmit suspend request */ -#define D64_XC_LE 0x00000004 /* loopback enable */ -#define D64_XC_FL 0x00000010 /* flush request */ -#define D64_XC_PD 0x00000800 /* parity check disable */ -#define D64_XC_AE 0x00030000 /* address extension bits */ -#define D64_XC_AE_SHIFT 16 - -/* transmit descriptor table pointer */ -#define D64_XP_LD_MASK 0x00000fff /* last valid descriptor */ - -/* transmit channel status */ -#define D64_XS0_CD_MASK 0x00001fff /* current descriptor pointer */ -#define D64_XS0_XS_MASK 0xf0000000 /* transmit state */ -#define D64_XS0_XS_SHIFT 28 -#define D64_XS0_XS_DISABLED 0x00000000 /* disabled */ -#define D64_XS0_XS_ACTIVE 0x10000000 /* active */ -#define D64_XS0_XS_IDLE 0x20000000 /* idle wait */ -#define D64_XS0_XS_STOPPED 0x30000000 /* stopped */ -#define D64_XS0_XS_SUSP 0x40000000 /* suspend pending */ - -#define D64_XS1_AD_MASK 0x00001fff /* active descriptor */ -#define D64_XS1_XE_MASK 0xf0000000 /* transmit errors */ -#define D64_XS1_XE_SHIFT 28 -#define D64_XS1_XE_NOERR 0x00000000 /* no error */ -#define D64_XS1_XE_DPE 0x10000000 /* descriptor protocol error */ -#define D64_XS1_XE_DFU 0x20000000 /* data fifo underrun */ -#define D64_XS1_XE_DTE 0x30000000 /* data transfer error */ -#define D64_XS1_XE_DESRE 0x40000000 /* descriptor read error */ -#define D64_XS1_XE_COREE 0x50000000 /* core error */ - -/* receive channel control */ -/* receive enable */ -#define D64_RC_RE 0x00000001 -/* receive frame offset */ -#define D64_RC_RO_MASK 0x000000fe -#define D64_RC_RO_SHIFT 1 -/* direct fifo receive (pio) mode */ -#define D64_RC_FM 0x00000100 -/* separate rx header descriptor enable */ -#define D64_RC_SH 0x00000200 -/* overflow continue */ -#define D64_RC_OC 0x00000400 -/* parity check disable */ -#define D64_RC_PD 0x00000800 -/* address extension bits */ -#define D64_RC_AE 0x00030000 -#define D64_RC_AE_SHIFT 16 - -/* flags for dma controller */ -/* partity enable */ -#define DMA_CTRL_PEN (1 << 0) -/* rx overflow continue */ -#define DMA_CTRL_ROC (1 << 1) -/* allow rx scatter to multiple descriptors */ -#define DMA_CTRL_RXMULTI (1 << 2) -/* Unframed Rx/Tx data */ -#define DMA_CTRL_UNFRAMED (1 << 3) - -/* receive descriptor table pointer */ -#define D64_RP_LD_MASK 0x00000fff /* last valid descriptor */ - -/* receive channel status */ -#define D64_RS0_CD_MASK 0x00001fff /* current descriptor pointer */ -#define D64_RS0_RS_MASK 0xf0000000 /* receive state */ -#define D64_RS0_RS_SHIFT 28 -#define D64_RS0_RS_DISABLED 0x00000000 /* disabled */ -#define D64_RS0_RS_ACTIVE 0x10000000 /* active */ -#define D64_RS0_RS_IDLE 0x20000000 /* idle wait */ -#define D64_RS0_RS_STOPPED 0x30000000 /* stopped */ -#define D64_RS0_RS_SUSP 0x40000000 /* suspend pending */ - -#define D64_RS1_AD_MASK 0x0001ffff /* active descriptor */ -#define D64_RS1_RE_MASK 0xf0000000 /* receive errors */ -#define D64_RS1_RE_SHIFT 28 -#define D64_RS1_RE_NOERR 0x00000000 /* no error */ -#define D64_RS1_RE_DPO 0x10000000 /* descriptor protocol error */ -#define D64_RS1_RE_DFU 0x20000000 /* data fifo overflow */ -#define D64_RS1_RE_DTE 0x30000000 /* data transfer error */ -#define D64_RS1_RE_DESRE 0x40000000 /* descriptor read error */ -#define D64_RS1_RE_COREE 0x50000000 /* core error */ - -/* fifoaddr */ -#define D64_FA_OFF_MASK 0xffff /* offset */ -#define D64_FA_SEL_MASK 0xf0000 /* select */ -#define D64_FA_SEL_SHIFT 16 -#define D64_FA_SEL_XDD 0x00000 /* transmit dma data */ -#define D64_FA_SEL_XDP 0x10000 /* transmit dma pointers */ -#define D64_FA_SEL_RDD 0x40000 /* receive dma data */ -#define D64_FA_SEL_RDP 0x50000 /* receive dma pointers */ -#define D64_FA_SEL_XFD 0x80000 /* transmit fifo data */ -#define D64_FA_SEL_XFP 0x90000 /* transmit fifo pointers */ -#define D64_FA_SEL_RFD 0xc0000 /* receive fifo data */ -#define D64_FA_SEL_RFP 0xd0000 /* receive fifo pointers */ -#define D64_FA_SEL_RSD 0xe0000 /* receive frame status data */ -#define D64_FA_SEL_RSP 0xf0000 /* receive frame status pointers */ - -/* descriptor control flags 1 */ -#define D64_CTRL_COREFLAGS 0x0ff00000 /* core specific flags */ -#define D64_CTRL1_EOT ((u32)1 << 28) /* end of descriptor table */ -#define D64_CTRL1_IOC ((u32)1 << 29) /* interrupt on completion */ -#define D64_CTRL1_EOF ((u32)1 << 30) /* end of frame */ -#define D64_CTRL1_SOF ((u32)1 << 31) /* start of frame */ - -/* descriptor control flags 2 */ -/* buffer byte count. real data len must <= 16KB */ -#define D64_CTRL2_BC_MASK 0x00007fff -/* address extension bits */ -#define D64_CTRL2_AE 0x00030000 -#define D64_CTRL2_AE_SHIFT 16 -/* parity bit */ -#define D64_CTRL2_PARITY 0x00040000 - -/* control flags in the range [27:20] are core-specific and not defined here */ -#define D64_CTRL_CORE_MASK 0x0ff00000 - -#define D64_RX_FRM_STS_LEN 0x0000ffff /* frame length mask */ -#define D64_RX_FRM_STS_OVFL 0x00800000 /* RxOverFlow */ -#define D64_RX_FRM_STS_DSCRCNT 0x0f000000 /* no. of descriptors used - 1 */ -#define D64_RX_FRM_STS_DATATYPE 0xf0000000 /* core-dependent data type */ - -/* - * packet headroom necessary to accommodate the largest header - * in the system, (i.e TXOFF). By doing, we avoid the need to - * allocate an extra buffer for the header when bridging to WL. - * There is a compile time check in wlc.c which ensure that this - * value is at least as big as TXOFF. This value is used in - * dma_rxfill(). - */ - -#define BCMEXTRAHDROOM 172 - -#define MAXNAMEL 8 /* 8 char names */ - -/* macros to convert between byte offsets and indexes */ -#define B2I(bytes, type) ((bytes) / sizeof(type)) -#define I2B(index, type) ((index) * sizeof(type)) - -#define PCI32ADDR_HIGH 0xc0000000 /* address[31:30] */ -#define PCI32ADDR_HIGH_SHIFT 30 /* address[31:30] */ - -#define PCI64ADDR_HIGH 0x80000000 /* address[63] */ -#define PCI64ADDR_HIGH_SHIFT 31 /* address[63] */ - -/* - * DMA Descriptor - * Descriptors are only read by the hardware, never written back. - */ -struct dma64desc { - __le32 ctrl1; /* misc control bits & bufcount */ - __le32 ctrl2; /* buffer count and address extension */ - __le32 addrlow; /* memory address of the date buffer, bits 31:0 */ - __le32 addrhigh; /* memory address of the date buffer, bits 63:32 */ -}; - -/* dma engine software state */ -struct dma_info { - struct dma_pub dma; /* exported structure */ - char name[MAXNAMEL]; /* callers name for diag msgs */ - - struct bcma_device *core; - struct device *dmadev; - - /* session information for AMPDU */ - struct brcms_ampdu_session ampdu_session; - - bool dma64; /* this dma engine is operating in 64-bit mode */ - bool addrext; /* this dma engine supports DmaExtendedAddrChanges */ - - /* 64-bit dma tx engine registers */ - uint d64txregbase; - /* 64-bit dma rx engine registers */ - uint d64rxregbase; - /* pointer to dma64 tx descriptor ring */ - struct dma64desc *txd64; - /* pointer to dma64 rx descriptor ring */ - struct dma64desc *rxd64; - - u16 dmadesc_align; /* alignment requirement for dma descriptors */ - - u16 ntxd; /* # tx descriptors tunable */ - u16 txin; /* index of next descriptor to reclaim */ - u16 txout; /* index of next descriptor to post */ - /* pointer to parallel array of pointers to packets */ - struct sk_buff **txp; - /* Aligned physical address of descriptor ring */ - dma_addr_t txdpa; - /* Original physical address of descriptor ring */ - dma_addr_t txdpaorig; - u16 txdalign; /* #bytes added to alloc'd mem to align txd */ - u32 txdalloc; /* #bytes allocated for the ring */ - u32 xmtptrbase; /* When using unaligned descriptors, the ptr register - * is not just an index, it needs all 13 bits to be - * an offset from the addr register. - */ - - u16 nrxd; /* # rx descriptors tunable */ - u16 rxin; /* index of next descriptor to reclaim */ - u16 rxout; /* index of next descriptor to post */ - /* pointer to parallel array of pointers to packets */ - struct sk_buff **rxp; - /* Aligned physical address of descriptor ring */ - dma_addr_t rxdpa; - /* Original physical address of descriptor ring */ - dma_addr_t rxdpaorig; - u16 rxdalign; /* #bytes added to alloc'd mem to align rxd */ - u32 rxdalloc; /* #bytes allocated for the ring */ - u32 rcvptrbase; /* Base for ptr reg when using unaligned descriptors */ - - /* tunables */ - unsigned int rxbufsize; /* rx buffer size in bytes, not including - * the extra headroom - */ - uint rxextrahdrroom; /* extra rx headroom, reverseved to assist upper - * stack, e.g. some rx pkt buffers will be - * bridged to tx side without byte copying. - * The extra headroom needs to be large enough - * to fit txheader needs. Some dongle driver may - * not need it. - */ - uint nrxpost; /* # rx buffers to keep posted */ - unsigned int rxoffset; /* rxcontrol offset */ - /* add to get dma address of descriptor ring, low 32 bits */ - uint ddoffsetlow; - /* high 32 bits */ - uint ddoffsethigh; - /* add to get dma address of data buffer, low 32 bits */ - uint dataoffsetlow; - /* high 32 bits */ - uint dataoffsethigh; - /* descriptor base need to be aligned or not */ - bool aligndesc_4k; -}; - -/* Check for odd number of 1's */ -static u32 parity32(__le32 data) -{ - /* no swap needed for counting 1's */ - u32 par_data = *(u32 *)&data; - - par_data ^= par_data >> 16; - par_data ^= par_data >> 8; - par_data ^= par_data >> 4; - par_data ^= par_data >> 2; - par_data ^= par_data >> 1; - - return par_data & 1; -} - -static bool dma64_dd_parity(struct dma64desc *dd) -{ - return parity32(dd->addrlow ^ dd->addrhigh ^ dd->ctrl1 ^ dd->ctrl2); -} - -/* descriptor bumping functions */ - -static uint xxd(uint x, uint n) -{ - return x & (n - 1); /* faster than %, but n must be power of 2 */ -} - -static uint txd(struct dma_info *di, uint x) -{ - return xxd(x, di->ntxd); -} - -static uint rxd(struct dma_info *di, uint x) -{ - return xxd(x, di->nrxd); -} - -static uint nexttxd(struct dma_info *di, uint i) -{ - return txd(di, i + 1); -} - -static uint prevtxd(struct dma_info *di, uint i) -{ - return txd(di, i - 1); -} - -static uint nextrxd(struct dma_info *di, uint i) -{ - return rxd(di, i + 1); -} - -static uint ntxdactive(struct dma_info *di, uint h, uint t) -{ - return txd(di, t-h); -} - -static uint nrxdactive(struct dma_info *di, uint h, uint t) -{ - return rxd(di, t-h); -} - -static uint _dma_ctrlflags(struct dma_info *di, uint mask, uint flags) -{ - uint dmactrlflags; - - if (di == NULL) - return 0; - - dmactrlflags = di->dma.dmactrlflags; - dmactrlflags &= ~mask; - dmactrlflags |= flags; - - /* If trying to enable parity, check if parity is actually supported */ - if (dmactrlflags & DMA_CTRL_PEN) { - u32 control; - - control = bcma_read32(di->core, DMA64TXREGOFFS(di, control)); - bcma_write32(di->core, DMA64TXREGOFFS(di, control), - control | D64_XC_PD); - if (bcma_read32(di->core, DMA64TXREGOFFS(di, control)) & - D64_XC_PD) - /* We *can* disable it so it is supported, - * restore control register - */ - bcma_write32(di->core, DMA64TXREGOFFS(di, control), - control); - else - /* Not supported, don't allow it to be enabled */ - dmactrlflags &= ~DMA_CTRL_PEN; - } - - di->dma.dmactrlflags = dmactrlflags; - - return dmactrlflags; -} - -static bool _dma64_addrext(struct dma_info *di, uint ctrl_offset) -{ - u32 w; - bcma_set32(di->core, ctrl_offset, D64_XC_AE); - w = bcma_read32(di->core, ctrl_offset); - bcma_mask32(di->core, ctrl_offset, ~D64_XC_AE); - return (w & D64_XC_AE) == D64_XC_AE; -} - -/* - * return true if this dma engine supports DmaExtendedAddrChanges, - * otherwise false - */ -static bool _dma_isaddrext(struct dma_info *di) -{ - /* DMA64 supports full 32- or 64-bit operation. AE is always valid */ - - /* not all tx or rx channel are available */ - if (di->d64txregbase != 0) { - if (!_dma64_addrext(di, DMA64TXREGOFFS(di, control))) - brcms_dbg_dma(di->core, - "%s: DMA64 tx doesn't have AE set\n", - di->name); - return true; - } else if (di->d64rxregbase != 0) { - if (!_dma64_addrext(di, DMA64RXREGOFFS(di, control))) - brcms_dbg_dma(di->core, - "%s: DMA64 rx doesn't have AE set\n", - di->name); - return true; - } - - return false; -} - -static bool _dma_descriptor_align(struct dma_info *di) -{ - u32 addrl; - - /* Check to see if the descriptors need to be aligned on 4K/8K or not */ - if (di->d64txregbase != 0) { - bcma_write32(di->core, DMA64TXREGOFFS(di, addrlow), 0xff0); - addrl = bcma_read32(di->core, DMA64TXREGOFFS(di, addrlow)); - if (addrl != 0) - return false; - } else if (di->d64rxregbase != 0) { - bcma_write32(di->core, DMA64RXREGOFFS(di, addrlow), 0xff0); - addrl = bcma_read32(di->core, DMA64RXREGOFFS(di, addrlow)); - if (addrl != 0) - return false; - } - return true; -} - -/* - * Descriptor table must start at the DMA hardware dictated alignment, so - * allocated memory must be large enough to support this requirement. - */ -static void *dma_alloc_consistent(struct dma_info *di, uint size, - u16 align_bits, uint *alloced, - dma_addr_t *pap) -{ - if (align_bits) { - u16 align = (1 << align_bits); - if (!IS_ALIGNED(PAGE_SIZE, align)) - size += align; - *alloced = size; - } - return dma_alloc_coherent(di->dmadev, size, pap, GFP_ATOMIC); -} - -static -u8 dma_align_sizetobits(uint size) -{ - u8 bitpos = 0; - while (size >>= 1) - bitpos++; - return bitpos; -} - -/* This function ensures that the DMA descriptor ring will not get allocated - * across Page boundary. If the allocation is done across the page boundary - * at the first time, then it is freed and the allocation is done at - * descriptor ring size aligned location. This will ensure that the ring will - * not cross page boundary - */ -static void *dma_ringalloc(struct dma_info *di, u32 boundary, uint size, - u16 *alignbits, uint *alloced, - dma_addr_t *descpa) -{ - void *va; - u32 desc_strtaddr; - u32 alignbytes = 1 << *alignbits; - - va = dma_alloc_consistent(di, size, *alignbits, alloced, descpa); - - if (NULL == va) - return NULL; - - desc_strtaddr = (u32) roundup((unsigned long)va, alignbytes); - if (((desc_strtaddr + size - 1) & boundary) != (desc_strtaddr - & boundary)) { - *alignbits = dma_align_sizetobits(size); - dma_free_coherent(di->dmadev, size, va, *descpa); - va = dma_alloc_consistent(di, size, *alignbits, - alloced, descpa); - } - return va; -} - -static bool dma64_alloc(struct dma_info *di, uint direction) -{ - u16 size; - uint ddlen; - void *va; - uint alloced = 0; - u16 align; - u16 align_bits; - - ddlen = sizeof(struct dma64desc); - - size = (direction == DMA_TX) ? (di->ntxd * ddlen) : (di->nrxd * ddlen); - align_bits = di->dmadesc_align; - align = (1 << align_bits); - - if (direction == DMA_TX) { - va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits, - &alloced, &di->txdpaorig); - if (va == NULL) { - brcms_dbg_dma(di->core, - "%s: DMA_ALLOC_CONSISTENT(ntxd) failed\n", - di->name); - return false; - } - align = (1 << align_bits); - di->txd64 = (struct dma64desc *) - roundup((unsigned long)va, align); - di->txdalign = (uint) ((s8 *)di->txd64 - (s8 *) va); - di->txdpa = di->txdpaorig + di->txdalign; - di->txdalloc = alloced; - } else { - va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits, - &alloced, &di->rxdpaorig); - if (va == NULL) { - brcms_dbg_dma(di->core, - "%s: DMA_ALLOC_CONSISTENT(nrxd) failed\n", - di->name); - return false; - } - align = (1 << align_bits); - di->rxd64 = (struct dma64desc *) - roundup((unsigned long)va, align); - di->rxdalign = (uint) ((s8 *)di->rxd64 - (s8 *) va); - di->rxdpa = di->rxdpaorig + di->rxdalign; - di->rxdalloc = alloced; - } - - return true; -} - -static bool _dma_alloc(struct dma_info *di, uint direction) -{ - return dma64_alloc(di, direction); -} - -struct dma_pub *dma_attach(char *name, struct brcms_c_info *wlc, - uint txregbase, uint rxregbase, uint ntxd, uint nrxd, - uint rxbufsize, int rxextheadroom, - uint nrxpost, uint rxoffset) -{ - struct si_pub *sih = wlc->hw->sih; - struct bcma_device *core = wlc->hw->d11core; - struct dma_info *di; - u8 rev = core->id.rev; - uint size; - struct si_info *sii = container_of(sih, struct si_info, pub); - - /* allocate private info structure */ - di = kzalloc(sizeof(struct dma_info), GFP_ATOMIC); - if (di == NULL) - return NULL; - - di->dma64 = - ((bcma_aread32(core, BCMA_IOST) & SISF_DMA64) == SISF_DMA64); - - /* init dma reg info */ - di->core = core; - di->d64txregbase = txregbase; - di->d64rxregbase = rxregbase; - - /* - * Default flags (which can be changed by the driver calling - * dma_ctrlflags before enable): For backwards compatibility - * both Rx Overflow Continue and Parity are DISABLED. - */ - _dma_ctrlflags(di, DMA_CTRL_ROC | DMA_CTRL_PEN, 0); - - brcms_dbg_dma(di->core, "%s: %s flags 0x%x ntxd %d nrxd %d " - "rxbufsize %d rxextheadroom %d nrxpost %d rxoffset %d " - "txregbase %u rxregbase %u\n", name, "DMA64", - di->dma.dmactrlflags, ntxd, nrxd, rxbufsize, - rxextheadroom, nrxpost, rxoffset, txregbase, rxregbase); - - /* make a private copy of our callers name */ - strncpy(di->name, name, MAXNAMEL); - di->name[MAXNAMEL - 1] = '\0'; - - di->dmadev = core->dma_dev; - - /* save tunables */ - di->ntxd = (u16) ntxd; - di->nrxd = (u16) nrxd; - - /* the actual dma size doesn't include the extra headroom */ - di->rxextrahdrroom = - (rxextheadroom == -1) ? BCMEXTRAHDROOM : rxextheadroom; - if (rxbufsize > BCMEXTRAHDROOM) - di->rxbufsize = (u16) (rxbufsize - di->rxextrahdrroom); - else - di->rxbufsize = (u16) rxbufsize; - - di->nrxpost = (u16) nrxpost; - di->rxoffset = (u8) rxoffset; - - /* - * figure out the DMA physical address offset for dd and data - * PCI/PCIE: they map silicon backplace address to zero - * based memory, need offset - * Other bus: use zero SI_BUS BIGENDIAN kludge: use sdram - * swapped region for data buffer, not descriptor - */ - di->ddoffsetlow = 0; - di->dataoffsetlow = 0; - /* for pci bus, add offset */ - if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) { - /* add offset for pcie with DMA64 bus */ - di->ddoffsetlow = 0; - di->ddoffsethigh = SI_PCIE_DMA_H32; - } - di->dataoffsetlow = di->ddoffsetlow; - di->dataoffsethigh = di->ddoffsethigh; - - /* WAR64450 : DMACtl.Addr ext fields are not supported in SDIOD core. */ - if ((core->id.id == BCMA_CORE_SDIO_DEV) - && ((rev > 0) && (rev <= 2))) - di->addrext = false; - else if ((core->id.id == BCMA_CORE_I2S) && - ((rev == 0) || (rev == 1))) - di->addrext = false; - else - di->addrext = _dma_isaddrext(di); - - /* does the descriptor need to be aligned and if yes, on 4K/8K or not */ - di->aligndesc_4k = _dma_descriptor_align(di); - if (di->aligndesc_4k) { - di->dmadesc_align = D64RINGALIGN_BITS; - if ((ntxd < D64MAXDD / 2) && (nrxd < D64MAXDD / 2)) - /* for smaller dd table, HW relax alignment reqmnt */ - di->dmadesc_align = D64RINGALIGN_BITS - 1; - } else { - di->dmadesc_align = 4; /* 16 byte alignment */ - } - - brcms_dbg_dma(di->core, "DMA descriptor align_needed %d, align %d\n", - di->aligndesc_4k, di->dmadesc_align); - - /* allocate tx packet pointer vector */ - if (ntxd) { - size = ntxd * sizeof(void *); - di->txp = kzalloc(size, GFP_ATOMIC); - if (di->txp == NULL) - goto fail; - } - - /* allocate rx packet pointer vector */ - if (nrxd) { - size = nrxd * sizeof(void *); - di->rxp = kzalloc(size, GFP_ATOMIC); - if (di->rxp == NULL) - goto fail; - } - - /* - * allocate transmit descriptor ring, only need ntxd descriptors - * but it must be aligned - */ - if (ntxd) { - if (!_dma_alloc(di, DMA_TX)) - goto fail; - } - - /* - * allocate receive descriptor ring, only need nrxd descriptors - * but it must be aligned - */ - if (nrxd) { - if (!_dma_alloc(di, DMA_RX)) - goto fail; - } - - if ((di->ddoffsetlow != 0) && !di->addrext) { - if (di->txdpa > SI_PCI_DMA_SZ) { - brcms_dbg_dma(di->core, - "%s: txdpa 0x%x: addrext not supported\n", - di->name, (u32)di->txdpa); - goto fail; - } - if (di->rxdpa > SI_PCI_DMA_SZ) { - brcms_dbg_dma(di->core, - "%s: rxdpa 0x%x: addrext not supported\n", - di->name, (u32)di->rxdpa); - goto fail; - } - } - - /* Initialize AMPDU session */ - brcms_c_ampdu_reset_session(&di->ampdu_session, wlc); - - brcms_dbg_dma(di->core, - "ddoffsetlow 0x%x ddoffsethigh 0x%x dataoffsetlow 0x%x dataoffsethigh 0x%x addrext %d\n", - di->ddoffsetlow, di->ddoffsethigh, - di->dataoffsetlow, di->dataoffsethigh, - di->addrext); - - return (struct dma_pub *) di; - - fail: - dma_detach((struct dma_pub *)di); - return NULL; -} - -static inline void -dma64_dd_upd(struct dma_info *di, struct dma64desc *ddring, - dma_addr_t pa, uint outidx, u32 *flags, u32 bufcount) -{ - u32 ctrl2 = bufcount & D64_CTRL2_BC_MASK; - - /* PCI bus with big(>1G) physical address, use address extension */ - if ((di->dataoffsetlow == 0) || !(pa & PCI32ADDR_HIGH)) { - ddring[outidx].addrlow = cpu_to_le32(pa + di->dataoffsetlow); - ddring[outidx].addrhigh = cpu_to_le32(di->dataoffsethigh); - ddring[outidx].ctrl1 = cpu_to_le32(*flags); - ddring[outidx].ctrl2 = cpu_to_le32(ctrl2); - } else { - /* address extension for 32-bit PCI */ - u32 ae; - - ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT; - pa &= ~PCI32ADDR_HIGH; - - ctrl2 |= (ae << D64_CTRL2_AE_SHIFT) & D64_CTRL2_AE; - ddring[outidx].addrlow = cpu_to_le32(pa + di->dataoffsetlow); - ddring[outidx].addrhigh = cpu_to_le32(di->dataoffsethigh); - ddring[outidx].ctrl1 = cpu_to_le32(*flags); - ddring[outidx].ctrl2 = cpu_to_le32(ctrl2); - } - if (di->dma.dmactrlflags & DMA_CTRL_PEN) { - if (dma64_dd_parity(&ddring[outidx])) - ddring[outidx].ctrl2 = - cpu_to_le32(ctrl2 | D64_CTRL2_PARITY); - } -} - -/* !! may be called with core in reset */ -void dma_detach(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - - brcms_dbg_dma(di->core, "%s:\n", di->name); - - /* free dma descriptor rings */ - if (di->txd64) - dma_free_coherent(di->dmadev, di->txdalloc, - ((s8 *)di->txd64 - di->txdalign), - (di->txdpaorig)); - if (di->rxd64) - dma_free_coherent(di->dmadev, di->rxdalloc, - ((s8 *)di->rxd64 - di->rxdalign), - (di->rxdpaorig)); - - /* free packet pointer vectors */ - kfree(di->txp); - kfree(di->rxp); - - /* free our private info structure */ - kfree(di); - -} - -/* initialize descriptor table base address */ -static void -_dma_ddtable_init(struct dma_info *di, uint direction, dma_addr_t pa) -{ - if (!di->aligndesc_4k) { - if (direction == DMA_TX) - di->xmtptrbase = pa; - else - di->rcvptrbase = pa; - } - - if ((di->ddoffsetlow == 0) - || !(pa & PCI32ADDR_HIGH)) { - if (direction == DMA_TX) { - bcma_write32(di->core, DMA64TXREGOFFS(di, addrlow), - pa + di->ddoffsetlow); - bcma_write32(di->core, DMA64TXREGOFFS(di, addrhigh), - di->ddoffsethigh); - } else { - bcma_write32(di->core, DMA64RXREGOFFS(di, addrlow), - pa + di->ddoffsetlow); - bcma_write32(di->core, DMA64RXREGOFFS(di, addrhigh), - di->ddoffsethigh); - } - } else { - /* DMA64 32bits address extension */ - u32 ae; - - /* shift the high bit(s) from pa to ae */ - ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT; - pa &= ~PCI32ADDR_HIGH; - - if (direction == DMA_TX) { - bcma_write32(di->core, DMA64TXREGOFFS(di, addrlow), - pa + di->ddoffsetlow); - bcma_write32(di->core, DMA64TXREGOFFS(di, addrhigh), - di->ddoffsethigh); - bcma_maskset32(di->core, DMA64TXREGOFFS(di, control), - D64_XC_AE, (ae << D64_XC_AE_SHIFT)); - } else { - bcma_write32(di->core, DMA64RXREGOFFS(di, addrlow), - pa + di->ddoffsetlow); - bcma_write32(di->core, DMA64RXREGOFFS(di, addrhigh), - di->ddoffsethigh); - bcma_maskset32(di->core, DMA64RXREGOFFS(di, control), - D64_RC_AE, (ae << D64_RC_AE_SHIFT)); - } - } -} - -static void _dma_rxenable(struct dma_info *di) -{ - uint dmactrlflags = di->dma.dmactrlflags; - u32 control; - - brcms_dbg_dma(di->core, "%s:\n", di->name); - - control = D64_RC_RE | (bcma_read32(di->core, - DMA64RXREGOFFS(di, control)) & - D64_RC_AE); - - if ((dmactrlflags & DMA_CTRL_PEN) == 0) - control |= D64_RC_PD; - - if (dmactrlflags & DMA_CTRL_ROC) - control |= D64_RC_OC; - - bcma_write32(di->core, DMA64RXREGOFFS(di, control), - ((di->rxoffset << D64_RC_RO_SHIFT) | control)); -} - -void dma_rxinit(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - - brcms_dbg_dma(di->core, "%s:\n", di->name); - - if (di->nrxd == 0) - return; - - di->rxin = di->rxout = 0; - - /* clear rx descriptor ring */ - memset(di->rxd64, '\0', di->nrxd * sizeof(struct dma64desc)); - - /* DMA engine with out alignment requirement requires table to be inited - * before enabling the engine - */ - if (!di->aligndesc_4k) - _dma_ddtable_init(di, DMA_RX, di->rxdpa); - - _dma_rxenable(di); - - if (di->aligndesc_4k) - _dma_ddtable_init(di, DMA_RX, di->rxdpa); -} - -static struct sk_buff *dma64_getnextrxp(struct dma_info *di, bool forceall) -{ - uint i, curr; - struct sk_buff *rxp; - dma_addr_t pa; - - i = di->rxin; - - /* return if no packets posted */ - if (i == di->rxout) - return NULL; - - curr = - B2I(((bcma_read32(di->core, - DMA64RXREGOFFS(di, status0)) & D64_RS0_CD_MASK) - - di->rcvptrbase) & D64_RS0_CD_MASK, struct dma64desc); - - /* ignore curr if forceall */ - if (!forceall && (i == curr)) - return NULL; - - /* get the packet pointer that corresponds to the rx descriptor */ - rxp = di->rxp[i]; - di->rxp[i] = NULL; - - pa = le32_to_cpu(di->rxd64[i].addrlow) - di->dataoffsetlow; - - /* clear this packet from the descriptor ring */ - dma_unmap_single(di->dmadev, pa, di->rxbufsize, DMA_FROM_DEVICE); - - di->rxd64[i].addrlow = cpu_to_le32(0xdeadbeef); - di->rxd64[i].addrhigh = cpu_to_le32(0xdeadbeef); - - di->rxin = nextrxd(di, i); - - return rxp; -} - -static struct sk_buff *_dma_getnextrxp(struct dma_info *di, bool forceall) -{ - if (di->nrxd == 0) - return NULL; - - return dma64_getnextrxp(di, forceall); -} - -/* - * !! rx entry routine - * returns the number packages in the next frame, or 0 if there are no more - * if DMA_CTRL_RXMULTI is defined, DMA scattering(multiple buffers) is - * supported with pkts chain - * otherwise, it's treated as giant pkt and will be tossed. - * The DMA scattering starts with normal DMA header, followed by first - * buffer data. After it reaches the max size of buffer, the data continues - * in next DMA descriptor buffer WITHOUT DMA header - */ -int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - struct sk_buff_head dma_frames; - struct sk_buff *p, *next; - uint len; - uint pkt_len; - int resid = 0; - int pktcnt = 1; - - skb_queue_head_init(&dma_frames); - next_frame: - p = _dma_getnextrxp(di, false); - if (p == NULL) - return 0; - - len = le16_to_cpu(*(__le16 *) (p->data)); - brcms_dbg_dma(di->core, "%s: dma_rx len %d\n", di->name, len); - dma_spin_for_len(len, p); - - /* set actual length */ - pkt_len = min((di->rxoffset + len), di->rxbufsize); - __skb_trim(p, pkt_len); - skb_queue_tail(&dma_frames, p); - resid = len - (di->rxbufsize - di->rxoffset); - - /* check for single or multi-buffer rx */ - if (resid > 0) { - while ((resid > 0) && (p = _dma_getnextrxp(di, false))) { - pkt_len = min_t(uint, resid, di->rxbufsize); - __skb_trim(p, pkt_len); - skb_queue_tail(&dma_frames, p); - resid -= di->rxbufsize; - pktcnt++; - } - -#ifdef DEBUG - if (resid > 0) { - uint cur; - cur = - B2I(((bcma_read32(di->core, - DMA64RXREGOFFS(di, status0)) & - D64_RS0_CD_MASK) - di->rcvptrbase) & - D64_RS0_CD_MASK, struct dma64desc); - brcms_dbg_dma(di->core, - "rxin %d rxout %d, hw_curr %d\n", - di->rxin, di->rxout, cur); - } -#endif /* DEBUG */ - - if ((di->dma.dmactrlflags & DMA_CTRL_RXMULTI) == 0) { - brcms_dbg_dma(di->core, "%s: bad frame length (%d)\n", - di->name, len); - skb_queue_walk_safe(&dma_frames, p, next) { - skb_unlink(p, &dma_frames); - brcmu_pkt_buf_free_skb(p); - } - di->dma.rxgiants++; - pktcnt = 1; - goto next_frame; - } - } - - skb_queue_splice_tail(&dma_frames, skb_list); - return pktcnt; -} - -static bool dma64_rxidle(struct dma_info *di) -{ - brcms_dbg_dma(di->core, "%s:\n", di->name); - - if (di->nrxd == 0) - return true; - - return ((bcma_read32(di->core, - DMA64RXREGOFFS(di, status0)) & D64_RS0_CD_MASK) == - (bcma_read32(di->core, DMA64RXREGOFFS(di, ptr)) & - D64_RS0_CD_MASK)); -} - -static bool dma64_txidle(struct dma_info *di) -{ - if (di->ntxd == 0) - return true; - - return ((bcma_read32(di->core, - DMA64TXREGOFFS(di, status0)) & D64_XS0_CD_MASK) == - (bcma_read32(di->core, DMA64TXREGOFFS(di, ptr)) & - D64_XS0_CD_MASK)); -} - -/* - * post receive buffers - * Return false if refill failed completely or dma mapping failed. The ring - * is empty, which will stall the rx dma and user might want to call rxfill - * again asap. This is unlikely to happen on a memory-rich NIC, but often on - * memory-constrained dongle. - */ -bool dma_rxfill(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - struct sk_buff *p; - u16 rxin, rxout; - u32 flags = 0; - uint n; - uint i; - dma_addr_t pa; - uint extra_offset = 0; - bool ring_empty; - - ring_empty = false; - - /* - * Determine how many receive buffers we're lacking - * from the full complement, allocate, initialize, - * and post them, then update the chip rx lastdscr. - */ - - rxin = di->rxin; - rxout = di->rxout; - - n = di->nrxpost - nrxdactive(di, rxin, rxout); - - brcms_dbg_dma(di->core, "%s: post %d\n", di->name, n); - - if (di->rxbufsize > BCMEXTRAHDROOM) - extra_offset = di->rxextrahdrroom; - - for (i = 0; i < n; i++) { - /* - * the di->rxbufsize doesn't include the extra headroom, - * we need to add it to the size to be allocated - */ - p = brcmu_pkt_buf_get_skb(di->rxbufsize + extra_offset); - - if (p == NULL) { - brcms_dbg_dma(di->core, "%s: out of rxbufs\n", - di->name); - if (i == 0 && dma64_rxidle(di)) { - brcms_dbg_dma(di->core, "%s: ring is empty !\n", - di->name); - ring_empty = true; - } - di->dma.rxnobuf++; - break; - } - /* reserve an extra headroom, if applicable */ - if (extra_offset) - skb_pull(p, extra_offset); - - /* Do a cached write instead of uncached write since DMA_MAP - * will flush the cache. - */ - *(u32 *) (p->data) = 0; - - pa = dma_map_single(di->dmadev, p->data, di->rxbufsize, - DMA_FROM_DEVICE); - if (dma_mapping_error(di->dmadev, pa)) - return false; - - /* save the free packet pointer */ - di->rxp[rxout] = p; - - /* reset flags for each descriptor */ - flags = 0; - if (rxout == (di->nrxd - 1)) - flags = D64_CTRL1_EOT; - - dma64_dd_upd(di, di->rxd64, pa, rxout, &flags, - di->rxbufsize); - rxout = nextrxd(di, rxout); - } - - di->rxout = rxout; - - /* update the chip lastdscr pointer */ - bcma_write32(di->core, DMA64RXREGOFFS(di, ptr), - di->rcvptrbase + I2B(rxout, struct dma64desc)); - - return ring_empty; -} - -void dma_rxreclaim(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - struct sk_buff *p; - - brcms_dbg_dma(di->core, "%s:\n", di->name); - - while ((p = _dma_getnextrxp(di, true))) - brcmu_pkt_buf_free_skb(p); -} - -void dma_counterreset(struct dma_pub *pub) -{ - /* reset all software counters */ - pub->rxgiants = 0; - pub->rxnobuf = 0; - pub->txnobuf = 0; -} - -/* get the address of the var in order to change later */ -unsigned long dma_getvar(struct dma_pub *pub, const char *name) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - - if (!strcmp(name, "&txavail")) - return (unsigned long)&(di->dma.txavail); - return 0; -} - -/* 64-bit DMA functions */ - -void dma_txinit(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - u32 control = D64_XC_XE; - - brcms_dbg_dma(di->core, "%s:\n", di->name); - - if (di->ntxd == 0) - return; - - di->txin = di->txout = 0; - di->dma.txavail = di->ntxd - 1; - - /* clear tx descriptor ring */ - memset(di->txd64, '\0', (di->ntxd * sizeof(struct dma64desc))); - - /* DMA engine with out alignment requirement requires table to be inited - * before enabling the engine - */ - if (!di->aligndesc_4k) - _dma_ddtable_init(di, DMA_TX, di->txdpa); - - if ((di->dma.dmactrlflags & DMA_CTRL_PEN) == 0) - control |= D64_XC_PD; - bcma_set32(di->core, DMA64TXREGOFFS(di, control), control); - - /* DMA engine with alignment requirement requires table to be inited - * before enabling the engine - */ - if (di->aligndesc_4k) - _dma_ddtable_init(di, DMA_TX, di->txdpa); -} - -void dma_txsuspend(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - - brcms_dbg_dma(di->core, "%s:\n", di->name); - - if (di->ntxd == 0) - return; - - bcma_set32(di->core, DMA64TXREGOFFS(di, control), D64_XC_SE); -} - -void dma_txresume(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - - brcms_dbg_dma(di->core, "%s:\n", di->name); - - if (di->ntxd == 0) - return; - - bcma_mask32(di->core, DMA64TXREGOFFS(di, control), ~D64_XC_SE); -} - -bool dma_txsuspended(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - - return (di->ntxd == 0) || - ((bcma_read32(di->core, - DMA64TXREGOFFS(di, control)) & D64_XC_SE) == - D64_XC_SE); -} - -void dma_txreclaim(struct dma_pub *pub, enum txd_range range) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - struct sk_buff *p; - - brcms_dbg_dma(di->core, "%s: %s\n", - di->name, - range == DMA_RANGE_ALL ? "all" : - range == DMA_RANGE_TRANSMITTED ? "transmitted" : - "transferred"); - - if (di->txin == di->txout) - return; - - while ((p = dma_getnexttxp(pub, range))) { - /* For unframed data, we don't have any packets to free */ - if (!(di->dma.dmactrlflags & DMA_CTRL_UNFRAMED)) - brcmu_pkt_buf_free_skb(p); - } -} - -bool dma_txreset(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - u32 status; - - if (di->ntxd == 0) - return true; - - /* suspend tx DMA first */ - bcma_write32(di->core, DMA64TXREGOFFS(di, control), D64_XC_SE); - SPINWAIT(((status = - (bcma_read32(di->core, DMA64TXREGOFFS(di, status0)) & - D64_XS0_XS_MASK)) != D64_XS0_XS_DISABLED) && - (status != D64_XS0_XS_IDLE) && (status != D64_XS0_XS_STOPPED), - 10000); - - bcma_write32(di->core, DMA64TXREGOFFS(di, control), 0); - SPINWAIT(((status = - (bcma_read32(di->core, DMA64TXREGOFFS(di, status0)) & - D64_XS0_XS_MASK)) != D64_XS0_XS_DISABLED), 10000); - - /* wait for the last transaction to complete */ - udelay(300); - - return status == D64_XS0_XS_DISABLED; -} - -bool dma_rxreset(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - u32 status; - - if (di->nrxd == 0) - return true; - - bcma_write32(di->core, DMA64RXREGOFFS(di, control), 0); - SPINWAIT(((status = - (bcma_read32(di->core, DMA64RXREGOFFS(di, status0)) & - D64_RS0_RS_MASK)) != D64_RS0_RS_DISABLED), 10000); - - return status == D64_RS0_RS_DISABLED; -} - -static void dma_txenq(struct dma_info *di, struct sk_buff *p) -{ - unsigned char *data; - uint len; - u16 txout; - u32 flags = 0; - dma_addr_t pa; - - txout = di->txout; - - if (WARN_ON(nexttxd(di, txout) == di->txin)) - return; - - /* - * obtain and initialize transmit descriptor entry. - */ - data = p->data; - len = p->len; - - /* get physical address of buffer start */ - pa = dma_map_single(di->dmadev, data, len, DMA_TO_DEVICE); - /* if mapping failed, free skb */ - if (dma_mapping_error(di->dmadev, pa)) { - brcmu_pkt_buf_free_skb(p); - return; - } - /* With a DMA segment list, Descriptor table is filled - * using the segment list instead of looping over - * buffers in multi-chain DMA. Therefore, EOF for SGLIST - * is when end of segment list is reached. - */ - flags = D64_CTRL1_SOF | D64_CTRL1_IOC | D64_CTRL1_EOF; - if (txout == (di->ntxd - 1)) - flags |= D64_CTRL1_EOT; - - dma64_dd_upd(di, di->txd64, pa, txout, &flags, len); - - txout = nexttxd(di, txout); - - /* save the packet */ - di->txp[prevtxd(di, txout)] = p; - - /* bump the tx descriptor index */ - di->txout = txout; -} - -static void ampdu_finalize(struct dma_info *di) -{ - struct brcms_ampdu_session *session = &di->ampdu_session; - struct sk_buff *p; - - trace_brcms_ampdu_session(&session->wlc->hw->d11core->dev, - session->max_ampdu_len, - session->max_ampdu_frames, - session->ampdu_len, - skb_queue_len(&session->skb_list), - session->dma_len); - - if (WARN_ON(skb_queue_empty(&session->skb_list))) - return; - - brcms_c_ampdu_finalize(session); - - while (!skb_queue_empty(&session->skb_list)) { - p = skb_dequeue(&session->skb_list); - dma_txenq(di, p); - } - - bcma_write32(di->core, DMA64TXREGOFFS(di, ptr), - di->xmtptrbase + I2B(di->txout, struct dma64desc)); - brcms_c_ampdu_reset_session(session, session->wlc); -} - -static void prep_ampdu_frame(struct dma_info *di, struct sk_buff *p) -{ - struct brcms_ampdu_session *session = &di->ampdu_session; - int ret; - - ret = brcms_c_ampdu_add_frame(session, p); - if (ret == -ENOSPC) { - /* - * AMPDU cannot accomodate this frame. Close out the in- - * progress AMPDU session and start a new one. - */ - ampdu_finalize(di); - ret = brcms_c_ampdu_add_frame(session, p); - } - - WARN_ON(ret); -} - -/* Update count of available tx descriptors based on current DMA state */ -static void dma_update_txavail(struct dma_info *di) -{ - /* - * Available space is number of descriptors less the number of - * active descriptors and the number of queued AMPDU frames. - */ - di->dma.txavail = di->ntxd - ntxdactive(di, di->txin, di->txout) - - skb_queue_len(&di->ampdu_session.skb_list) - 1; -} - -/* - * !! tx entry routine - * WARNING: call must check the return value for error. - * the error(toss frames) could be fatal and cause many subsequent hard - * to debug problems - */ -int dma_txfast(struct brcms_c_info *wlc, struct dma_pub *pub, - struct sk_buff *p) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - struct brcms_ampdu_session *session = &di->ampdu_session; - struct ieee80211_tx_info *tx_info; - bool is_ampdu; - - /* no use to transmit a zero length packet */ - if (p->len == 0) - return 0; - - /* return nonzero if out of tx descriptors */ - if (di->dma.txavail == 0 || nexttxd(di, di->txout) == di->txin) - goto outoftxd; - - tx_info = IEEE80211_SKB_CB(p); - is_ampdu = tx_info->flags & IEEE80211_TX_CTL_AMPDU; - if (is_ampdu) - prep_ampdu_frame(di, p); - else - dma_txenq(di, p); - - /* tx flow control */ - dma_update_txavail(di); - - /* kick the chip */ - if (is_ampdu) { - /* - * Start sending data if we've got a full AMPDU, there's - * no more space in the DMA ring, or the ring isn't - * currently transmitting. - */ - if (skb_queue_len(&session->skb_list) == session->max_ampdu_frames || - di->dma.txavail == 0 || dma64_txidle(di)) - ampdu_finalize(di); - } else { - bcma_write32(di->core, DMA64TXREGOFFS(di, ptr), - di->xmtptrbase + I2B(di->txout, struct dma64desc)); - } - - return 0; - - outoftxd: - brcms_dbg_dma(di->core, "%s: out of txds !!!\n", di->name); - brcmu_pkt_buf_free_skb(p); - di->dma.txavail = 0; - di->dma.txnobuf++; - return -ENOSPC; -} - -void dma_txflush(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - struct brcms_ampdu_session *session = &di->ampdu_session; - - if (!skb_queue_empty(&session->skb_list)) - ampdu_finalize(di); -} - -int dma_txpending(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - return ntxdactive(di, di->txin, di->txout); -} - -/* - * If we have an active AMPDU session and are not transmitting, - * this function will force tx to start. - */ -void dma_kick_tx(struct dma_pub *pub) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - struct brcms_ampdu_session *session = &di->ampdu_session; - - if (!skb_queue_empty(&session->skb_list) && dma64_txidle(di)) - ampdu_finalize(di); -} - -/* - * Reclaim next completed txd (txds if using chained buffers) in the range - * specified and return associated packet. - * If range is DMA_RANGE_TRANSMITTED, reclaim descriptors that have be - * transmitted as noted by the hardware "CurrDescr" pointer. - * If range is DMA_RANGE_TRANSFERED, reclaim descriptors that have be - * transferred by the DMA as noted by the hardware "ActiveDescr" pointer. - * If range is DMA_RANGE_ALL, reclaim all txd(s) posted to the ring and - * return associated packet regardless of the value of hardware pointers. - */ -struct sk_buff *dma_getnexttxp(struct dma_pub *pub, enum txd_range range) -{ - struct dma_info *di = container_of(pub, struct dma_info, dma); - u16 start, end, i; - u16 active_desc; - struct sk_buff *txp; - - brcms_dbg_dma(di->core, "%s: %s\n", - di->name, - range == DMA_RANGE_ALL ? "all" : - range == DMA_RANGE_TRANSMITTED ? "transmitted" : - "transferred"); - - if (di->ntxd == 0) - return NULL; - - txp = NULL; - - start = di->txin; - if (range == DMA_RANGE_ALL) - end = di->txout; - else { - end = (u16) (B2I(((bcma_read32(di->core, - DMA64TXREGOFFS(di, status0)) & - D64_XS0_CD_MASK) - di->xmtptrbase) & - D64_XS0_CD_MASK, struct dma64desc)); - - if (range == DMA_RANGE_TRANSFERED) { - active_desc = - (u16)(bcma_read32(di->core, - DMA64TXREGOFFS(di, status1)) & - D64_XS1_AD_MASK); - active_desc = - (active_desc - di->xmtptrbase) & D64_XS0_CD_MASK; - active_desc = B2I(active_desc, struct dma64desc); - if (end != active_desc) - end = prevtxd(di, active_desc); - } - } - - if ((start == 0) && (end > di->txout)) - goto bogus; - - for (i = start; i != end && !txp; i = nexttxd(di, i)) { - dma_addr_t pa; - uint size; - - pa = le32_to_cpu(di->txd64[i].addrlow) - di->dataoffsetlow; - - size = - (le32_to_cpu(di->txd64[i].ctrl2) & - D64_CTRL2_BC_MASK); - - di->txd64[i].addrlow = cpu_to_le32(0xdeadbeef); - di->txd64[i].addrhigh = cpu_to_le32(0xdeadbeef); - - txp = di->txp[i]; - di->txp[i] = NULL; - - dma_unmap_single(di->dmadev, pa, size, DMA_TO_DEVICE); - } - - di->txin = i; - - /* tx flow control */ - dma_update_txavail(di); - - return txp; - - bogus: - brcms_dbg_dma(di->core, "bogus curr: start %d end %d txout %d\n", - start, end, di->txout); - return NULL; -} - -/* - * Mac80211 initiated actions sometimes require packets in the DMA queue to be - * modified. The modified portion of the packet is not under control of the DMA - * engine. This function calls a caller-supplied function for each packet in - * the caller specified dma chain. - */ -void dma_walk_packets(struct dma_pub *dmah, void (*callback_fnc) - (void *pkt, void *arg_a), void *arg_a) -{ - struct dma_info *di = container_of(dmah, struct dma_info, dma); - uint i = di->txin; - uint end = di->txout; - struct sk_buff *skb; - struct ieee80211_tx_info *tx_info; - - while (i != end) { - skb = di->txp[i]; - if (skb != NULL) { - tx_info = (struct ieee80211_tx_info *)skb->cb; - (callback_fnc)(tx_info, arg_a); - } - i = nexttxd(di, i); - } -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.h b/drivers/net/wireless/brcm80211/brcmsmac/dma.h deleted file mode 100644 index ff5b80b09046..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/dma.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_DMA_H_ -#define _BRCM_DMA_H_ - -#include -#include -#include "types.h" /* forward structure declarations */ - -/* map/unmap direction */ -#define DMA_TX 1 /* TX direction for DMA */ -#define DMA_RX 2 /* RX direction for DMA */ - -/* DMA structure: - * support two DMA engines: 32 bits address or 64 bit addressing - * basic DMA register set is per channel(transmit or receive) - * a pair of channels is defined for convenience - */ - -/* 32 bits addressing */ - -struct dma32diag { /* diag access */ - u32 fifoaddr; /* diag address */ - u32 fifodatalow; /* low 32bits of data */ - u32 fifodatahigh; /* high 32bits of data */ - u32 pad; /* reserved */ -}; - -/* 64 bits addressing */ - -/* dma registers per channel(xmt or rcv) */ -struct dma64regs { - u32 control; /* enable, et al */ - u32 ptr; /* last descriptor posted to chip */ - u32 addrlow; /* desc ring base address low 32-bits (8K aligned) */ - u32 addrhigh; /* desc ring base address bits 63:32 (8K aligned) */ - u32 status0; /* current descriptor, xmt state */ - u32 status1; /* active descriptor, xmt error */ -}; - -/* range param for dma_getnexttxp() and dma_txreclaim */ -enum txd_range { - DMA_RANGE_ALL = 1, - DMA_RANGE_TRANSMITTED, - DMA_RANGE_TRANSFERED -}; - -/* - * Exported data structure (read-only) - */ -/* export structure */ -struct dma_pub { - uint txavail; /* # free tx descriptors */ - uint dmactrlflags; /* dma control flags */ - - /* rx error counters */ - uint rxgiants; /* rx giant frames */ - uint rxnobuf; /* rx out of dma descriptors */ - /* tx error counters */ - uint txnobuf; /* tx out of dma descriptors */ -}; - -extern struct dma_pub *dma_attach(char *name, struct brcms_c_info *wlc, - uint txregbase, uint rxregbase, - uint ntxd, uint nrxd, - uint rxbufsize, int rxextheadroom, - uint nrxpost, uint rxoffset); - -void dma_rxinit(struct dma_pub *pub); -int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list); -bool dma_rxfill(struct dma_pub *pub); -bool dma_rxreset(struct dma_pub *pub); -bool dma_txreset(struct dma_pub *pub); -void dma_txinit(struct dma_pub *pub); -int dma_txfast(struct brcms_c_info *wlc, struct dma_pub *pub, - struct sk_buff *p0); -void dma_txflush(struct dma_pub *pub); -int dma_txpending(struct dma_pub *pub); -void dma_kick_tx(struct dma_pub *pub); -void dma_txsuspend(struct dma_pub *pub); -bool dma_txsuspended(struct dma_pub *pub); -void dma_txresume(struct dma_pub *pub); -void dma_txreclaim(struct dma_pub *pub, enum txd_range range); -void dma_rxreclaim(struct dma_pub *pub); -void dma_detach(struct dma_pub *pub); -unsigned long dma_getvar(struct dma_pub *pub, const char *name); -struct sk_buff *dma_getnexttxp(struct dma_pub *pub, enum txd_range range); -void dma_counterreset(struct dma_pub *pub); - -void dma_walk_packets(struct dma_pub *dmah, void (*callback_fnc) - (void *pkt, void *arg_a), void *arg_a); - -/* - * DMA(Bug) on bcm47xx chips seems to declare that the packet is ready, but - * the packet length is not updated yet (by DMA) on the expected time. - * Workaround is to hold processor till DMA updates the length, and stay off - * the bus to allow DMA update the length in buffer - */ -static inline void dma_spin_for_len(uint len, struct sk_buff *head) -{ -#if defined(CONFIG_BCM47XX) - if (!len) { - while (!(len = *(u16 *) KSEG1ADDR(head->data))) - udelay(1); - - *(u16 *) (head->data) = cpu_to_le16((u16) len); - } -#endif /* defined(CONFIG_BCM47XX) */ -} - -#endif /* _BRCM_DMA_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/led.c b/drivers/net/wireless/brcm80211/brcmsmac/led.c deleted file mode 100644 index 74b17cecb189..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/led.c +++ /dev/null @@ -1,126 +0,0 @@ -#include -#include -#include - -#include "mac80211_if.h" -#include "pub.h" -#include "main.h" -#include "led.h" - - /* number of leds */ -#define BRCMS_LED_NO 4 - /* behavior mask */ -#define BRCMS_LED_BEH_MASK 0x7f - /* activelow (polarity) bit */ -#define BRCMS_LED_AL_MASK 0x80 - /* radio enabled */ -#define BRCMS_LED_RADIO 3 - -static void brcms_radio_led_ctrl(struct brcms_info *wl, bool state) -{ - if (wl->radio_led.gpio == -1) - return; - - if (wl->radio_led.active_low) - state = !state; - - if (state) - gpio_set_value(wl->radio_led.gpio, 1); - else - gpio_set_value(wl->radio_led.gpio, 0); -} - - -/* Callback from the LED subsystem. */ -static void brcms_led_brightness_set(struct led_classdev *led_dev, - enum led_brightness brightness) -{ - struct brcms_info *wl = container_of(led_dev, - struct brcms_info, led_dev); - brcms_radio_led_ctrl(wl, brightness); -} - -void brcms_led_unregister(struct brcms_info *wl) -{ - if (wl->led_dev.dev) - led_classdev_unregister(&wl->led_dev); - if (wl->radio_led.gpio != -1) - gpio_free(wl->radio_led.gpio); -} - -int brcms_led_register(struct brcms_info *wl) -{ - int i, err; - struct brcms_led *radio_led = &wl->radio_led; - /* get CC core */ - struct bcma_drv_cc *cc_drv = &wl->wlc->hw->d11core->bus->drv_cc; - struct gpio_chip *bcma_gpio = &cc_drv->gpio; - struct ssb_sprom *sprom = &wl->wlc->hw->d11core->bus->sprom; - u8 *leds[] = { &sprom->gpio0, - &sprom->gpio1, - &sprom->gpio2, - &sprom->gpio3 }; - unsigned gpio = -1; - bool active_low = false; - - /* none by default */ - radio_led->gpio = -1; - radio_led->active_low = false; - - if (!bcma_gpio || !gpio_is_valid(bcma_gpio->base)) - return -ENODEV; - - /* find radio enabled LED */ - for (i = 0; i < BRCMS_LED_NO; i++) { - u8 led = *leds[i]; - if ((led & BRCMS_LED_BEH_MASK) == BRCMS_LED_RADIO) { - gpio = bcma_gpio->base + i; - if (led & BRCMS_LED_AL_MASK) - active_low = true; - break; - } - } - - if (gpio == -1 || !gpio_is_valid(gpio)) - return -ENODEV; - - /* request and configure LED gpio */ - err = gpio_request_one(gpio, - active_low ? GPIOF_OUT_INIT_HIGH - : GPIOF_OUT_INIT_LOW, - "radio on"); - if (err) { - wiphy_err(wl->wiphy, "requesting led gpio %d failed (err: %d)\n", - gpio, err); - return err; - } - err = gpio_direction_output(gpio, 1); - if (err) { - wiphy_err(wl->wiphy, "cannot set led gpio %d to output (err: %d)\n", - gpio, err); - return err; - } - - snprintf(wl->radio_led.name, sizeof(wl->radio_led.name), - "brcmsmac-%s:radio", wiphy_name(wl->wiphy)); - - wl->led_dev.name = wl->radio_led.name; - wl->led_dev.default_trigger = - ieee80211_get_radio_led_name(wl->pub->ieee_hw); - wl->led_dev.brightness_set = brcms_led_brightness_set; - err = led_classdev_register(wiphy_dev(wl->wiphy), &wl->led_dev); - - if (err) { - wiphy_err(wl->wiphy, "cannot register led device: %s (err: %d)\n", - wl->radio_led.name, err); - return err; - } - - wiphy_info(wl->wiphy, "registered radio enabled led device: %s gpio: %d\n", - wl->radio_led.name, - gpio); - radio_led->gpio = gpio; - radio_led->active_low = active_low; - - return 0; -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/led.h b/drivers/net/wireless/brcm80211/brcmsmac/led.h deleted file mode 100644 index 17a0b1f5dbcf..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/led.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2012 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 _BRCM_LED_H_ -#define _BRCM_LED_H_ -struct brcms_led { - char name[32]; - unsigned gpio; - bool active_low; -}; - -#ifdef CONFIG_BCMA_DRIVER_GPIO -void brcms_led_unregister(struct brcms_info *wl); -int brcms_led_register(struct brcms_info *wl); -#else -static inline void brcms_led_unregister(struct brcms_info *wl) {}; -static inline int brcms_led_register(struct brcms_info *wl) -{ - return -ENOTSUPP; -}; -#endif - -#endif /* _BRCM_LED_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c deleted file mode 100644 index bec2dc1ca2e4..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ /dev/null @@ -1,1700 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * Copyright (c) 2013 Hauke Mehrtens - * - * 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. - */ - -#define __UNDEF_NO_VERSION__ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include "phy/phy_int.h" -#include "d11.h" -#include "channel.h" -#include "scb.h" -#include "pub.h" -#include "ucode_loader.h" -#include "mac80211_if.h" -#include "main.h" -#include "debug.h" -#include "led.h" - -#define N_TX_QUEUES 4 /* #tx queues on mac80211<->driver interface */ -#define BRCMS_FLUSH_TIMEOUT 500 /* msec */ - -/* Flags we support */ -#define MAC_FILTERS (FIF_ALLMULTI | \ - FIF_FCSFAIL | \ - FIF_CONTROL | \ - FIF_OTHER_BSS | \ - FIF_BCN_PRBRESP_PROMISC | \ - FIF_PSPOLL) - -#define CHAN2GHZ(channel, freqency, chflags) { \ - .band = IEEE80211_BAND_2GHZ, \ - .center_freq = (freqency), \ - .hw_value = (channel), \ - .flags = chflags, \ - .max_antenna_gain = 0, \ - .max_power = 19, \ -} - -#define CHAN5GHZ(channel, chflags) { \ - .band = IEEE80211_BAND_5GHZ, \ - .center_freq = 5000 + 5*(channel), \ - .hw_value = (channel), \ - .flags = chflags, \ - .max_antenna_gain = 0, \ - .max_power = 21, \ -} - -#define RATE(rate100m, _flags) { \ - .bitrate = (rate100m), \ - .flags = (_flags), \ - .hw_value = (rate100m / 5), \ -} - -struct firmware_hdr { - __le32 offset; - __le32 len; - __le32 idx; -}; - -static const char * const brcms_firmwares[MAX_FW_IMAGES] = { - "brcm/bcm43xx", - NULL -}; - -static int n_adapters_found; - -MODULE_AUTHOR("Broadcom Corporation"); -MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver."); -MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards"); -MODULE_LICENSE("Dual BSD/GPL"); -/* This needs to be adjusted when brcms_firmwares changes */ -MODULE_FIRMWARE("brcm/bcm43xx-0.fw"); -MODULE_FIRMWARE("brcm/bcm43xx_hdr-0.fw"); - -/* recognized BCMA Core IDs */ -static struct bcma_device_id brcms_coreid_table[] = { - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 17, BCMA_ANY_CLASS), - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 23, BCMA_ANY_CLASS), - BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 24, BCMA_ANY_CLASS), - {}, -}; -MODULE_DEVICE_TABLE(bcma, brcms_coreid_table); - -#if defined(CONFIG_BRCMDBG) -/* - * Module parameter for setting the debug message level. Available - * flags are specified by the BRCM_DL_* macros in - * drivers/net/wireless/brcm80211/include/defs.h. - */ -module_param_named(debug, brcm_msg_level, uint, S_IRUGO | S_IWUSR); -#endif - -static struct ieee80211_channel brcms_2ghz_chantable[] = { - CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS), - CHAN2GHZ(2, 2417, IEEE80211_CHAN_NO_HT40MINUS), - CHAN2GHZ(3, 2422, IEEE80211_CHAN_NO_HT40MINUS), - CHAN2GHZ(4, 2427, IEEE80211_CHAN_NO_HT40MINUS), - CHAN2GHZ(5, 2432, 0), - CHAN2GHZ(6, 2437, 0), - CHAN2GHZ(7, 2442, 0), - CHAN2GHZ(8, 2447, IEEE80211_CHAN_NO_HT40PLUS), - CHAN2GHZ(9, 2452, IEEE80211_CHAN_NO_HT40PLUS), - CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS), - CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS), - CHAN2GHZ(12, 2467, - IEEE80211_CHAN_NO_IR | - IEEE80211_CHAN_NO_HT40PLUS), - CHAN2GHZ(13, 2472, - IEEE80211_CHAN_NO_IR | - IEEE80211_CHAN_NO_HT40PLUS), - CHAN2GHZ(14, 2484, - IEEE80211_CHAN_NO_IR | - IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS | - IEEE80211_CHAN_NO_OFDM) -}; - -static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = { - /* UNII-1 */ - CHAN5GHZ(36, IEEE80211_CHAN_NO_HT40MINUS), - CHAN5GHZ(40, IEEE80211_CHAN_NO_HT40PLUS), - CHAN5GHZ(44, IEEE80211_CHAN_NO_HT40MINUS), - CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS), - /* UNII-2 */ - CHAN5GHZ(52, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), - CHAN5GHZ(56, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), - CHAN5GHZ(60, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), - CHAN5GHZ(64, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), - /* MID */ - CHAN5GHZ(100, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), - CHAN5GHZ(104, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), - CHAN5GHZ(108, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), - CHAN5GHZ(112, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), - CHAN5GHZ(116, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), - CHAN5GHZ(120, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), - CHAN5GHZ(124, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), - CHAN5GHZ(128, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), - CHAN5GHZ(132, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), - CHAN5GHZ(136, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), - CHAN5GHZ(140, - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS | - IEEE80211_CHAN_NO_HT40MINUS), - /* UNII-3 */ - CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS), - CHAN5GHZ(153, IEEE80211_CHAN_NO_HT40PLUS), - CHAN5GHZ(157, IEEE80211_CHAN_NO_HT40MINUS), - CHAN5GHZ(161, IEEE80211_CHAN_NO_HT40PLUS), - CHAN5GHZ(165, IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS) -}; - -/* - * The rate table is used for both 2.4G and 5G rates. The - * latter being a subset as it does not support CCK rates. - */ -static struct ieee80211_rate legacy_ratetable[] = { - RATE(10, 0), - RATE(20, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(55, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(110, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(60, 0), - RATE(90, 0), - RATE(120, 0), - RATE(180, 0), - RATE(240, 0), - RATE(360, 0), - RATE(480, 0), - RATE(540, 0), -}; - -static const struct ieee80211_supported_band brcms_band_2GHz_nphy_template = { - .band = IEEE80211_BAND_2GHZ, - .channels = brcms_2ghz_chantable, - .n_channels = ARRAY_SIZE(brcms_2ghz_chantable), - .bitrates = legacy_ratetable, - .n_bitrates = ARRAY_SIZE(legacy_ratetable), - .ht_cap = { - /* from include/linux/ieee80211.h */ - .cap = IEEE80211_HT_CAP_GRN_FLD | - IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40, - .ht_supported = true, - .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, - .ampdu_density = AMPDU_DEF_MPDU_DENSITY, - .mcs = { - /* placeholders for now */ - .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}, - .rx_highest = cpu_to_le16(500), - .tx_params = IEEE80211_HT_MCS_TX_DEFINED} - } -}; - -static const struct ieee80211_supported_band brcms_band_5GHz_nphy_template = { - .band = IEEE80211_BAND_5GHZ, - .channels = brcms_5ghz_nphy_chantable, - .n_channels = ARRAY_SIZE(brcms_5ghz_nphy_chantable), - .bitrates = legacy_ratetable + BRCMS_LEGACY_5G_RATE_OFFSET, - .n_bitrates = ARRAY_SIZE(legacy_ratetable) - - BRCMS_LEGACY_5G_RATE_OFFSET, - .ht_cap = { - .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | - IEEE80211_HT_CAP_SGI_40, - .ht_supported = true, - .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, - .ampdu_density = AMPDU_DEF_MPDU_DENSITY, - .mcs = { - /* placeholders for now */ - .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}, - .rx_highest = cpu_to_le16(500), - .tx_params = IEEE80211_HT_MCS_TX_DEFINED} - } -}; - -/* flags the given rate in rateset as requested */ -static void brcms_set_basic_rate(struct brcm_rateset *rs, u16 rate, bool is_br) -{ - u32 i; - - for (i = 0; i < rs->count; i++) { - if (rate != (rs->rates[i] & 0x7f)) - continue; - - if (is_br) - rs->rates[i] |= BRCMS_RATE_FLAG; - else - rs->rates[i] &= BRCMS_RATE_MASK; - return; - } -} - -/** - * This function frees the WL per-device resources. - * - * This function frees resources owned by the WL device pointed to - * by the wl parameter. - * - * precondition: can both be called locked and unlocked - * - */ -static void brcms_free(struct brcms_info *wl) -{ - struct brcms_timer *t, *next; - - /* free ucode data */ - if (wl->fw.fw_cnt) - brcms_ucode_data_free(&wl->ucode); - if (wl->irq) - free_irq(wl->irq, wl); - - /* kill dpc */ - tasklet_kill(&wl->tasklet); - - if (wl->pub) { - brcms_debugfs_detach(wl->pub); - brcms_c_module_unregister(wl->pub, "linux", wl); - } - - /* free common resources */ - if (wl->wlc) { - brcms_c_detach(wl->wlc); - wl->wlc = NULL; - wl->pub = NULL; - } - - /* virtual interface deletion is deferred so we cannot spinwait */ - - /* wait for all pending callbacks to complete */ - while (atomic_read(&wl->callbacks) > 0) - schedule(); - - /* free timers */ - for (t = wl->timers; t; t = next) { - next = t->next; -#ifdef DEBUG - kfree(t->name); -#endif - kfree(t); - } -} - -/* -* called from both kernel as from this kernel module (error flow on attach) -* precondition: perimeter lock is not acquired. -*/ -static void brcms_remove(struct bcma_device *pdev) -{ - struct ieee80211_hw *hw = bcma_get_drvdata(pdev); - struct brcms_info *wl = hw->priv; - - if (wl->wlc) { - brcms_led_unregister(wl); - wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false); - wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); - ieee80211_unregister_hw(hw); - } - - brcms_free(wl); - - bcma_set_drvdata(pdev, NULL); - ieee80211_free_hw(hw); -} - -/* - * Precondition: Since this function is called in brcms_pci_probe() context, - * no locking is required. - */ -static void brcms_release_fw(struct brcms_info *wl) -{ - int i; - for (i = 0; i < MAX_FW_IMAGES; i++) { - release_firmware(wl->fw.fw_bin[i]); - release_firmware(wl->fw.fw_hdr[i]); - } -} - -/* - * Precondition: Since this function is called in brcms_pci_probe() context, - * no locking is required. - */ -static int brcms_request_fw(struct brcms_info *wl, struct bcma_device *pdev) -{ - int status; - struct device *device = &pdev->dev; - char fw_name[100]; - int i; - - memset(&wl->fw, 0, sizeof(struct brcms_firmware)); - for (i = 0; i < MAX_FW_IMAGES; i++) { - if (brcms_firmwares[i] == NULL) - break; - sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i], - UCODE_LOADER_API_VER); - status = request_firmware(&wl->fw.fw_bin[i], fw_name, device); - if (status) { - wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n", - KBUILD_MODNAME, fw_name); - return status; - } - sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i], - UCODE_LOADER_API_VER); - status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device); - if (status) { - wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n", - KBUILD_MODNAME, fw_name); - return status; - } - wl->fw.hdr_num_entries[i] = - wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr)); - } - wl->fw.fw_cnt = i; - status = brcms_ucode_data_init(wl, &wl->ucode); - brcms_release_fw(wl); - return status; -} - -static void brcms_ops_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct brcms_info *wl = hw->priv; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - - spin_lock_bh(&wl->lock); - if (!wl->pub->up) { - brcms_err(wl->wlc->hw->d11core, "ops->tx called while down\n"); - kfree_skb(skb); - goto done; - } - if (brcms_c_sendpkt_mac80211(wl->wlc, skb, hw)) - tx_info->rate_driver_data[0] = control->sta; - done: - spin_unlock_bh(&wl->lock); -} - -static int brcms_ops_start(struct ieee80211_hw *hw) -{ - struct brcms_info *wl = hw->priv; - bool blocked; - int err; - - if (!wl->ucode.bcm43xx_bomminor) { - err = brcms_request_fw(wl, wl->wlc->hw->d11core); - if (err) - return -ENOENT; - } - - ieee80211_wake_queues(hw); - spin_lock_bh(&wl->lock); - blocked = brcms_rfkill_set_hw_state(wl); - spin_unlock_bh(&wl->lock); - if (!blocked) - wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); - - spin_lock_bh(&wl->lock); - /* avoid acknowledging frames before a non-monitor device is added */ - wl->mute_tx = true; - - if (!wl->pub->up) - if (!blocked) - err = brcms_up(wl); - else - err = -ERFKILL; - else - err = -ENODEV; - spin_unlock_bh(&wl->lock); - - if (err != 0) - brcms_err(wl->wlc->hw->d11core, "%s: brcms_up() returned %d\n", - __func__, err); - - bcma_core_pci_power_save(wl->wlc->hw->d11core->bus, true); - return err; -} - -static void brcms_ops_stop(struct ieee80211_hw *hw) -{ - struct brcms_info *wl = hw->priv; - int status; - - ieee80211_stop_queues(hw); - - if (wl->wlc == NULL) - return; - - spin_lock_bh(&wl->lock); - status = brcms_c_chipmatch(wl->wlc->hw->d11core); - spin_unlock_bh(&wl->lock); - if (!status) { - brcms_err(wl->wlc->hw->d11core, - "wl: brcms_ops_stop: chipmatch failed\n"); - return; - } - - bcma_core_pci_power_save(wl->wlc->hw->d11core->bus, false); - - /* put driver in down state */ - spin_lock_bh(&wl->lock); - brcms_down(wl); - spin_unlock_bh(&wl->lock); -} - -static int -brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct brcms_info *wl = hw->priv; - - /* Just STA, AP and ADHOC for now */ - if (vif->type != NL80211_IFTYPE_STATION && - vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_ADHOC) { - brcms_err(wl->wlc->hw->d11core, - "%s: Attempt to add type %d, only STA, AP and AdHoc for now\n", - __func__, vif->type); - return -EOPNOTSUPP; - } - - spin_lock_bh(&wl->lock); - wl->mute_tx = false; - brcms_c_mute(wl->wlc, false); - if (vif->type == NL80211_IFTYPE_STATION) - brcms_c_start_station(wl->wlc, vif->addr); - else if (vif->type == NL80211_IFTYPE_AP) - brcms_c_start_ap(wl->wlc, vif->addr, vif->bss_conf.bssid, - vif->bss_conf.ssid, vif->bss_conf.ssid_len); - else if (vif->type == NL80211_IFTYPE_ADHOC) - brcms_c_start_adhoc(wl->wlc, vif->addr); - spin_unlock_bh(&wl->lock); - - return 0; -} - -static void -brcms_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ -} - -static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed) -{ - struct ieee80211_conf *conf = &hw->conf; - struct brcms_info *wl = hw->priv; - struct bcma_device *core = wl->wlc->hw->d11core; - int err = 0; - int new_int; - - spin_lock_bh(&wl->lock); - if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { - brcms_c_set_beacon_listen_interval(wl->wlc, - conf->listen_interval); - } - if (changed & IEEE80211_CONF_CHANGE_MONITOR) - brcms_dbg_info(core, "%s: change monitor mode: %s\n", - __func__, conf->flags & IEEE80211_CONF_MONITOR ? - "true" : "false"); - if (changed & IEEE80211_CONF_CHANGE_PS) - brcms_err(core, "%s: change power-save mode: %s (implement)\n", - __func__, conf->flags & IEEE80211_CONF_PS ? - "true" : "false"); - - if (changed & IEEE80211_CONF_CHANGE_POWER) { - err = brcms_c_set_tx_power(wl->wlc, conf->power_level); - if (err < 0) { - brcms_err(core, "%s: Error setting power_level\n", - __func__); - goto config_out; - } - new_int = brcms_c_get_tx_power(wl->wlc); - if (new_int != conf->power_level) - brcms_err(core, - "%s: Power level req != actual, %d %d\n", - __func__, conf->power_level, - new_int); - } - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - if (conf->chandef.width == NL80211_CHAN_WIDTH_20 || - conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) - err = brcms_c_set_channel(wl->wlc, - conf->chandef.chan->hw_value); - else - err = -ENOTSUPP; - } - if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) - err = brcms_c_set_rate_limit(wl->wlc, - conf->short_frame_max_tx_count, - conf->long_frame_max_tx_count); - - config_out: - spin_unlock_bh(&wl->lock); - return err; -} - -static void -brcms_ops_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, u32 changed) -{ - struct brcms_info *wl = hw->priv; - struct bcma_device *core = wl->wlc->hw->d11core; - - if (changed & BSS_CHANGED_ASSOC) { - /* association status changed (associated/disassociated) - * also implies a change in the AID. - */ - brcms_err(core, "%s: %s: %sassociated\n", KBUILD_MODNAME, - __func__, info->assoc ? "" : "dis"); - spin_lock_bh(&wl->lock); - brcms_c_associate_upd(wl->wlc, info->assoc); - spin_unlock_bh(&wl->lock); - } - if (changed & BSS_CHANGED_ERP_SLOT) { - s8 val; - - /* slot timing changed */ - if (info->use_short_slot) - val = 1; - else - val = 0; - spin_lock_bh(&wl->lock); - brcms_c_set_shortslot_override(wl->wlc, val); - spin_unlock_bh(&wl->lock); - } - - if (changed & BSS_CHANGED_HT) { - /* 802.11n parameters changed */ - u16 mode = info->ht_operation_mode; - - spin_lock_bh(&wl->lock); - brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_CFG, - mode & IEEE80211_HT_OP_MODE_PROTECTION); - brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_NONGF, - mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_OBSS, - mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT); - spin_unlock_bh(&wl->lock); - } - if (changed & BSS_CHANGED_BASIC_RATES) { - struct ieee80211_supported_band *bi; - u32 br_mask, i; - u16 rate; - struct brcm_rateset rs; - int error; - - /* retrieve the current rates */ - spin_lock_bh(&wl->lock); - brcms_c_get_current_rateset(wl->wlc, &rs); - spin_unlock_bh(&wl->lock); - - br_mask = info->basic_rates; - bi = hw->wiphy->bands[brcms_c_get_curband(wl->wlc)]; - for (i = 0; i < bi->n_bitrates; i++) { - /* convert to internal rate value */ - rate = (bi->bitrates[i].bitrate << 1) / 10; - - /* set/clear basic rate flag */ - brcms_set_basic_rate(&rs, rate, br_mask & 1); - br_mask >>= 1; - } - - /* update the rate set */ - spin_lock_bh(&wl->lock); - error = brcms_c_set_rateset(wl->wlc, &rs); - spin_unlock_bh(&wl->lock); - if (error) - brcms_err(core, "changing basic rates failed: %d\n", - error); - } - if (changed & BSS_CHANGED_BEACON_INT) { - /* Beacon interval changed */ - spin_lock_bh(&wl->lock); - brcms_c_set_beacon_period(wl->wlc, info->beacon_int); - spin_unlock_bh(&wl->lock); - } - if (changed & BSS_CHANGED_BSSID) { - /* BSSID changed, for whatever reason (IBSS and managed mode) */ - spin_lock_bh(&wl->lock); - brcms_c_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET, info->bssid); - spin_unlock_bh(&wl->lock); - } - if (changed & BSS_CHANGED_SSID) { - /* BSSID changed, for whatever reason (IBSS and managed mode) */ - spin_lock_bh(&wl->lock); - brcms_c_set_ssid(wl->wlc, info->ssid, info->ssid_len); - spin_unlock_bh(&wl->lock); - } - if (changed & BSS_CHANGED_BEACON) { - /* Beacon data changed, retrieve new beacon (beaconing modes) */ - struct sk_buff *beacon; - u16 tim_offset = 0; - - spin_lock_bh(&wl->lock); - beacon = ieee80211_beacon_get_tim(hw, vif, &tim_offset, NULL); - brcms_c_set_new_beacon(wl->wlc, beacon, tim_offset, - info->dtim_period); - spin_unlock_bh(&wl->lock); - } - - if (changed & BSS_CHANGED_AP_PROBE_RESP) { - struct sk_buff *probe_resp; - - spin_lock_bh(&wl->lock); - probe_resp = ieee80211_proberesp_get(hw, vif); - brcms_c_set_new_probe_resp(wl->wlc, probe_resp); - spin_unlock_bh(&wl->lock); - } - - if (changed & BSS_CHANGED_BEACON_ENABLED) { - /* Beaconing should be enabled/disabled (beaconing modes) */ - brcms_err(core, "%s: Beacon enabled: %s\n", __func__, - info->enable_beacon ? "true" : "false"); - if (info->enable_beacon && - hw->wiphy->flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) { - brcms_c_enable_probe_resp(wl->wlc, true); - } else { - brcms_c_enable_probe_resp(wl->wlc, false); - } - } - - if (changed & BSS_CHANGED_CQM) { - /* Connection quality monitor config changed */ - brcms_err(core, "%s: cqm change: threshold %d, hys %d " - " (implement)\n", __func__, info->cqm_rssi_thold, - info->cqm_rssi_hyst); - } - - if (changed & BSS_CHANGED_IBSS) { - /* IBSS join status changed */ - brcms_err(core, "%s: IBSS joined: %s (implement)\n", - __func__, info->ibss_joined ? "true" : "false"); - } - - if (changed & BSS_CHANGED_ARP_FILTER) { - /* Hardware ARP filter address list or state changed */ - brcms_err(core, "%s: arp filtering: %d addresses" - " (implement)\n", __func__, info->arp_addr_cnt); - } - - if (changed & BSS_CHANGED_QOS) { - /* - * QoS for this association was enabled/disabled. - * Note that it is only ever disabled for station mode. - */ - brcms_err(core, "%s: qos enabled: %s (implement)\n", - __func__, info->qos ? "true" : "false"); - } - return; -} - -static void -brcms_ops_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, u64 multicast) -{ - struct brcms_info *wl = hw->priv; - struct bcma_device *core = wl->wlc->hw->d11core; - - changed_flags &= MAC_FILTERS; - *total_flags &= MAC_FILTERS; - - if (changed_flags & FIF_ALLMULTI) - brcms_dbg_info(core, "FIF_ALLMULTI\n"); - if (changed_flags & FIF_FCSFAIL) - brcms_dbg_info(core, "FIF_FCSFAIL\n"); - if (changed_flags & FIF_CONTROL) - brcms_dbg_info(core, "FIF_CONTROL\n"); - if (changed_flags & FIF_OTHER_BSS) - brcms_dbg_info(core, "FIF_OTHER_BSS\n"); - if (changed_flags & FIF_PSPOLL) - brcms_dbg_info(core, "FIF_PSPOLL\n"); - if (changed_flags & FIF_BCN_PRBRESP_PROMISC) - brcms_dbg_info(core, "FIF_BCN_PRBRESP_PROMISC\n"); - - spin_lock_bh(&wl->lock); - brcms_c_mac_promisc(wl->wlc, *total_flags); - spin_unlock_bh(&wl->lock); - return; -} - -static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const u8 *mac_addr) -{ - struct brcms_info *wl = hw->priv; - spin_lock_bh(&wl->lock); - brcms_c_scan_start(wl->wlc); - spin_unlock_bh(&wl->lock); - return; -} - -static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct brcms_info *wl = hw->priv; - spin_lock_bh(&wl->lock); - brcms_c_scan_stop(wl->wlc); - spin_unlock_bh(&wl->lock); - return; -} - -static int -brcms_ops_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct brcms_info *wl = hw->priv; - - spin_lock_bh(&wl->lock); - brcms_c_wme_setparams(wl->wlc, queue, params, true); - spin_unlock_bh(&wl->lock); - - return 0; -} - -static int -brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct brcms_info *wl = hw->priv; - struct scb *scb = &wl->wlc->pri_scb; - - brcms_c_init_scb(scb); - - wl->pub->global_ampdu = &(scb->scb_ampdu); - wl->pub->global_ampdu->scb = scb; - wl->pub->global_ampdu->max_pdu = 16; - - /* - * minstrel_ht initiates addBA on our behalf by calling - * ieee80211_start_tx_ba_session() - */ - return 0; -} - -static int -brcms_ops_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) -{ - struct brcms_info *wl = hw->priv; - struct scb *scb = &wl->wlc->pri_scb; - int status; - - if (WARN_ON(scb->magic != SCB_MAGIC)) - return -EIDRM; - switch (action) { - case IEEE80211_AMPDU_RX_START: - break; - case IEEE80211_AMPDU_RX_STOP: - break; - case IEEE80211_AMPDU_TX_START: - spin_lock_bh(&wl->lock); - status = brcms_c_aggregatable(wl->wlc, tid); - spin_unlock_bh(&wl->lock); - if (!status) { - brcms_err(wl->wlc->hw->d11core, - "START: tid %d is not agg\'able\n", tid); - return -EINVAL; - } - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - - case IEEE80211_AMPDU_TX_STOP_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - spin_lock_bh(&wl->lock); - brcms_c_ampdu_flush(wl->wlc, sta, tid); - spin_unlock_bh(&wl->lock); - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - /* - * BA window size from ADDBA response ('buf_size') defines how - * many outstanding MPDUs are allowed for the BA stream by - * recipient and traffic class. 'ampdu_factor' gives maximum - * AMPDU size. - */ - spin_lock_bh(&wl->lock); - brcms_c_ampdu_tx_operational(wl->wlc, tid, buf_size, - (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + - sta->ht_cap.ampdu_factor)) - 1); - spin_unlock_bh(&wl->lock); - /* Power save wakeup */ - break; - default: - brcms_err(wl->wlc->hw->d11core, - "%s: Invalid command, ignoring\n", __func__); - } - - return 0; -} - -static void brcms_ops_rfkill_poll(struct ieee80211_hw *hw) -{ - struct brcms_info *wl = hw->priv; - bool blocked; - - spin_lock_bh(&wl->lock); - blocked = brcms_c_check_radio_disabled(wl->wlc); - spin_unlock_bh(&wl->lock); - - wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked); -} - -static bool brcms_tx_flush_completed(struct brcms_info *wl) -{ - bool result; - - spin_lock_bh(&wl->lock); - result = brcms_c_tx_flush_completed(wl->wlc); - spin_unlock_bh(&wl->lock); - return result; -} - -static void brcms_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 queues, bool drop) -{ - struct brcms_info *wl = hw->priv; - int ret; - - no_printk("%s: drop = %s\n", __func__, drop ? "true" : "false"); - - ret = wait_event_timeout(wl->tx_flush_wq, - brcms_tx_flush_completed(wl), - msecs_to_jiffies(BRCMS_FLUSH_TIMEOUT)); - - brcms_dbg_mac80211(wl->wlc->hw->d11core, - "ret=%d\n", jiffies_to_msecs(ret)); -} - -static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct brcms_info *wl = hw->priv; - u64 tsf; - - spin_lock_bh(&wl->lock); - tsf = brcms_c_tsf_get(wl->wlc); - spin_unlock_bh(&wl->lock); - - return tsf; -} - -static void brcms_ops_set_tsf(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u64 tsf) -{ - struct brcms_info *wl = hw->priv; - - spin_lock_bh(&wl->lock); - brcms_c_tsf_set(wl->wlc, tsf); - spin_unlock_bh(&wl->lock); -} - -static const struct ieee80211_ops brcms_ops = { - .tx = brcms_ops_tx, - .start = brcms_ops_start, - .stop = brcms_ops_stop, - .add_interface = brcms_ops_add_interface, - .remove_interface = brcms_ops_remove_interface, - .config = brcms_ops_config, - .bss_info_changed = brcms_ops_bss_info_changed, - .configure_filter = brcms_ops_configure_filter, - .sw_scan_start = brcms_ops_sw_scan_start, - .sw_scan_complete = brcms_ops_sw_scan_complete, - .conf_tx = brcms_ops_conf_tx, - .sta_add = brcms_ops_sta_add, - .ampdu_action = brcms_ops_ampdu_action, - .rfkill_poll = brcms_ops_rfkill_poll, - .flush = brcms_ops_flush, - .get_tsf = brcms_ops_get_tsf, - .set_tsf = brcms_ops_set_tsf, -}; - -void brcms_dpc(unsigned long data) -{ - struct brcms_info *wl; - - wl = (struct brcms_info *) data; - - spin_lock_bh(&wl->lock); - - /* call the common second level interrupt handler */ - if (wl->pub->up) { - if (wl->resched) { - unsigned long flags; - - spin_lock_irqsave(&wl->isr_lock, flags); - brcms_c_intrsupd(wl->wlc); - spin_unlock_irqrestore(&wl->isr_lock, flags); - } - - wl->resched = brcms_c_dpc(wl->wlc, true); - } - - /* brcms_c_dpc() may bring the driver down */ - if (!wl->pub->up) - goto done; - - /* re-schedule dpc */ - if (wl->resched) - tasklet_schedule(&wl->tasklet); - else - /* re-enable interrupts */ - brcms_intrson(wl); - - done: - spin_unlock_bh(&wl->lock); - wake_up(&wl->tx_flush_wq); -} - -static irqreturn_t brcms_isr(int irq, void *dev_id) -{ - struct brcms_info *wl; - irqreturn_t ret = IRQ_NONE; - - wl = (struct brcms_info *) dev_id; - - spin_lock(&wl->isr_lock); - - /* call common first level interrupt handler */ - if (brcms_c_isr(wl->wlc)) { - /* schedule second level handler */ - tasklet_schedule(&wl->tasklet); - ret = IRQ_HANDLED; - } - - spin_unlock(&wl->isr_lock); - - return ret; -} - -/* - * is called in brcms_pci_probe() context, therefore no locking required. - */ -static int ieee_hw_rate_init(struct ieee80211_hw *hw) -{ - struct brcms_info *wl = hw->priv; - struct brcms_c_info *wlc = wl->wlc; - struct ieee80211_supported_band *band; - int has_5g = 0; - u16 phy_type; - - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; - - phy_type = brcms_c_get_phy_type(wl->wlc, 0); - if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) { - band = &wlc->bandstate[BAND_2G_INDEX]->band; - *band = brcms_band_2GHz_nphy_template; - if (phy_type == PHY_TYPE_LCN) { - /* Single stream */ - band->ht_cap.mcs.rx_mask[1] = 0; - band->ht_cap.mcs.rx_highest = cpu_to_le16(72); - } - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band; - } else { - return -EPERM; - } - - /* Assume all bands use the same phy. True for 11n devices. */ - if (wl->pub->_nbands > 1) { - has_5g++; - if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) { - band = &wlc->bandstate[BAND_5G_INDEX]->band; - *band = brcms_band_5GHz_nphy_template; - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band; - } else { - return -EPERM; - } - } - return 0; -} - -/* - * is called in brcms_pci_probe() context, therefore no locking required. - */ -static int ieee_hw_init(struct ieee80211_hw *hw) -{ - ieee80211_hw_set(hw, AMPDU_AGGREGATION); - ieee80211_hw_set(hw, SIGNAL_DBM); - ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); - - hw->extra_tx_headroom = brcms_c_get_header_len(); - hw->queues = N_TX_QUEUES; - hw->max_rates = 2; /* Primary rate and 1 fallback rate */ - - /* channel change time is dependent on chip and band */ - hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_ADHOC); - - /* - * deactivate sending probe responses by ucude, because this will - * cause problems when WPS is used. - * - * hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; - */ - - hw->rate_control_algorithm = "minstrel_ht"; - - hw->sta_data_size = 0; - return ieee_hw_rate_init(hw); -} - -/** - * attach to the WL device. - * - * Attach to the WL device identified by vendor and device parameters. - * regs is a host accessible memory address pointing to WL device registers. - * - * is called in brcms_bcma_probe() context, therefore no locking required. - */ -static struct brcms_info *brcms_attach(struct bcma_device *pdev) -{ - struct brcms_info *wl = NULL; - int unit, err; - struct ieee80211_hw *hw; - u8 perm[ETH_ALEN]; - - unit = n_adapters_found; - err = 0; - - if (unit < 0) - return NULL; - - /* allocate private info */ - hw = bcma_get_drvdata(pdev); - if (hw != NULL) - wl = hw->priv; - if (WARN_ON(hw == NULL) || WARN_ON(wl == NULL)) - return NULL; - wl->wiphy = hw->wiphy; - - atomic_set(&wl->callbacks, 0); - - init_waitqueue_head(&wl->tx_flush_wq); - - /* setup the bottom half handler */ - tasklet_init(&wl->tasklet, brcms_dpc, (unsigned long) wl); - - spin_lock_init(&wl->lock); - spin_lock_init(&wl->isr_lock); - - /* common load-time initialization */ - wl->wlc = brcms_c_attach((void *)wl, pdev, unit, false, &err); - if (!wl->wlc) { - wiphy_err(wl->wiphy, "%s: attach() failed with code %d\n", - KBUILD_MODNAME, err); - goto fail; - } - wl->pub = brcms_c_pub(wl->wlc); - - wl->pub->ieee_hw = hw; - - /* register our interrupt handler */ - if (request_irq(pdev->irq, brcms_isr, - IRQF_SHARED, KBUILD_MODNAME, wl)) { - wiphy_err(wl->wiphy, "wl%d: request_irq() failed\n", unit); - goto fail; - } - wl->irq = pdev->irq; - - /* register module */ - brcms_c_module_register(wl->pub, "linux", wl, NULL); - - if (ieee_hw_init(hw)) { - wiphy_err(wl->wiphy, "wl%d: %s: ieee_hw_init failed!\n", unit, - __func__); - goto fail; - } - - brcms_c_regd_init(wl->wlc); - - memcpy(perm, &wl->pub->cur_etheraddr, ETH_ALEN); - if (WARN_ON(!is_valid_ether_addr(perm))) - goto fail; - SET_IEEE80211_PERM_ADDR(hw, perm); - - err = ieee80211_register_hw(hw); - if (err) - wiphy_err(wl->wiphy, "%s: ieee80211_register_hw failed, status" - "%d\n", __func__, err); - - if (wl->pub->srom_ccode[0] && - regulatory_hint(wl->wiphy, wl->pub->srom_ccode)) - wiphy_err(wl->wiphy, "%s: regulatory hint failed\n", __func__); - - brcms_debugfs_attach(wl->pub); - brcms_debugfs_create_files(wl->pub); - n_adapters_found++; - return wl; - -fail: - brcms_free(wl); - return NULL; -} - - - -/** - * determines if a device is a WL device, and if so, attaches it. - * - * This function determines if a device pointed to by pdev is a WL device, - * and if so, performs a brcms_attach() on it. - * - * Perimeter lock is initialized in the course of this function. - */ -static int brcms_bcma_probe(struct bcma_device *pdev) -{ - struct brcms_info *wl; - struct ieee80211_hw *hw; - - dev_info(&pdev->dev, "mfg %x core %x rev %d class %d irq %d\n", - pdev->id.manuf, pdev->id.id, pdev->id.rev, pdev->id.class, - pdev->irq); - - if ((pdev->id.manuf != BCMA_MANUF_BCM) || - (pdev->id.id != BCMA_CORE_80211)) - return -ENODEV; - - hw = ieee80211_alloc_hw(sizeof(struct brcms_info), &brcms_ops); - if (!hw) { - pr_err("%s: ieee80211_alloc_hw failed\n", __func__); - return -ENOMEM; - } - - SET_IEEE80211_DEV(hw, &pdev->dev); - - bcma_set_drvdata(pdev, hw); - - memset(hw->priv, 0, sizeof(*wl)); - - wl = brcms_attach(pdev); - if (!wl) { - pr_err("%s: brcms_attach failed!\n", __func__); - return -ENODEV; - } - brcms_led_register(wl); - - return 0; -} - -static int brcms_suspend(struct bcma_device *pdev) -{ - struct brcms_info *wl; - struct ieee80211_hw *hw; - - hw = bcma_get_drvdata(pdev); - wl = hw->priv; - if (!wl) { - pr_err("%s: %s: no driver private struct!\n", KBUILD_MODNAME, - __func__); - return -ENODEV; - } - - /* only need to flag hw is down for proper resume */ - spin_lock_bh(&wl->lock); - wl->pub->hw_up = false; - spin_unlock_bh(&wl->lock); - - brcms_dbg_info(wl->wlc->hw->d11core, "brcms_suspend ok\n"); - - return 0; -} - -static int brcms_resume(struct bcma_device *pdev) -{ - return 0; -} - -static struct bcma_driver brcms_bcma_driver = { - .name = KBUILD_MODNAME, - .probe = brcms_bcma_probe, - .suspend = brcms_suspend, - .resume = brcms_resume, - .remove = brcms_remove, - .id_table = brcms_coreid_table, -}; - -/** - * This is the main entry point for the brcmsmac driver. - * - * This function is scheduled upon module initialization and - * does the driver registration, which result in brcms_bcma_probe() - * call resulting in the driver bringup. - */ -static void brcms_driver_init(struct work_struct *work) -{ - int error; - - error = bcma_driver_register(&brcms_bcma_driver); - if (error) - pr_err("%s: register returned %d\n", __func__, error); -} - -static DECLARE_WORK(brcms_driver_work, brcms_driver_init); - -static int __init brcms_module_init(void) -{ - brcms_debugfs_init(); - if (!schedule_work(&brcms_driver_work)) - return -EBUSY; - - return 0; -} - -/** - * This function unloads the brcmsmac driver from the system. - * - * This function unconditionally unloads the brcmsmac driver module from the - * system. - * - */ -static void __exit brcms_module_exit(void) -{ - cancel_work_sync(&brcms_driver_work); - bcma_driver_unregister(&brcms_bcma_driver); - brcms_debugfs_exit(); -} - -module_init(brcms_module_init); -module_exit(brcms_module_exit); - -/* - * precondition: perimeter lock has been acquired - */ -void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, - bool state, int prio) -{ - brcms_err(wl->wlc->hw->d11core, "Shouldn't be here %s\n", __func__); -} - -/* - * precondition: perimeter lock has been acquired - */ -void brcms_init(struct brcms_info *wl) -{ - brcms_dbg_info(wl->wlc->hw->d11core, "Initializing wl%d\n", - wl->pub->unit); - brcms_reset(wl); - brcms_c_init(wl->wlc, wl->mute_tx); -} - -/* - * precondition: perimeter lock has been acquired - */ -uint brcms_reset(struct brcms_info *wl) -{ - brcms_dbg_info(wl->wlc->hw->d11core, "Resetting wl%d\n", wl->pub->unit); - brcms_c_reset(wl->wlc); - - /* dpc will not be rescheduled */ - wl->resched = false; - - /* inform publicly that interface is down */ - wl->pub->up = false; - - return 0; -} - -void brcms_fatal_error(struct brcms_info *wl) -{ - brcms_err(wl->wlc->hw->d11core, "wl%d: fatal error, reinitializing\n", - wl->wlc->pub->unit); - brcms_reset(wl); - ieee80211_restart_hw(wl->pub->ieee_hw); -} - -/* - * These are interrupt on/off entry points. Disable interrupts - * during interrupt state transition. - */ -void brcms_intrson(struct brcms_info *wl) -{ - unsigned long flags; - - spin_lock_irqsave(&wl->isr_lock, flags); - brcms_c_intrson(wl->wlc); - spin_unlock_irqrestore(&wl->isr_lock, flags); -} - -u32 brcms_intrsoff(struct brcms_info *wl) -{ - unsigned long flags; - u32 status; - - spin_lock_irqsave(&wl->isr_lock, flags); - status = brcms_c_intrsoff(wl->wlc); - spin_unlock_irqrestore(&wl->isr_lock, flags); - return status; -} - -void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask) -{ - unsigned long flags; - - spin_lock_irqsave(&wl->isr_lock, flags); - brcms_c_intrsrestore(wl->wlc, macintmask); - spin_unlock_irqrestore(&wl->isr_lock, flags); -} - -/* - * precondition: perimeter lock has been acquired - */ -int brcms_up(struct brcms_info *wl) -{ - int error = 0; - - if (wl->pub->up) - return 0; - - error = brcms_c_up(wl->wlc); - - return error; -} - -/* - * precondition: perimeter lock has been acquired - */ -void brcms_down(struct brcms_info *wl) -{ - uint callbacks, ret_val = 0; - - /* call common down function */ - ret_val = brcms_c_down(wl->wlc); - callbacks = atomic_read(&wl->callbacks) - ret_val; - - /* wait for down callbacks to complete */ - spin_unlock_bh(&wl->lock); - - /* For HIGH_only driver, it's important to actually schedule other work, - * not just spin wait since everything runs at schedule level - */ - SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000); - - spin_lock_bh(&wl->lock); -} - -/* -* precondition: perimeter lock is not acquired - */ -static void _brcms_timer(struct work_struct *work) -{ - struct brcms_timer *t = container_of(work, struct brcms_timer, - dly_wrk.work); - - spin_lock_bh(&t->wl->lock); - - if (t->set) { - if (t->periodic) { - atomic_inc(&t->wl->callbacks); - ieee80211_queue_delayed_work(t->wl->pub->ieee_hw, - &t->dly_wrk, - msecs_to_jiffies(t->ms)); - } else { - t->set = false; - } - - t->fn(t->arg); - } - - atomic_dec(&t->wl->callbacks); - - spin_unlock_bh(&t->wl->lock); -} - -/* - * Adds a timer to the list. Caller supplies a timer function. - * Is called from wlc. - * - * precondition: perimeter lock has been acquired - */ -struct brcms_timer *brcms_init_timer(struct brcms_info *wl, - void (*fn) (void *arg), - void *arg, const char *name) -{ - struct brcms_timer *t; - - t = kzalloc(sizeof(struct brcms_timer), GFP_ATOMIC); - if (!t) - return NULL; - - INIT_DELAYED_WORK(&t->dly_wrk, _brcms_timer); - t->wl = wl; - t->fn = fn; - t->arg = arg; - t->next = wl->timers; - wl->timers = t; - -#ifdef DEBUG - t->name = kstrdup(name, GFP_ATOMIC); -#endif - - return t; -} - -/* - * adds only the kernel timer since it's going to be more accurate - * as well as it's easier to make it periodic - * - * precondition: perimeter lock has been acquired - */ -void brcms_add_timer(struct brcms_timer *t, uint ms, int periodic) -{ - struct ieee80211_hw *hw = t->wl->pub->ieee_hw; - -#ifdef DEBUG - if (t->set) - brcms_dbg_info(t->wl->wlc->hw->d11core, - "%s: Already set. Name: %s, per %d\n", - __func__, t->name, periodic); -#endif - t->ms = ms; - t->periodic = (bool) periodic; - if (!t->set) { - t->set = true; - atomic_inc(&t->wl->callbacks); - } - - ieee80211_queue_delayed_work(hw, &t->dly_wrk, msecs_to_jiffies(ms)); -} - -/* - * return true if timer successfully deleted, false if still pending - * - * precondition: perimeter lock has been acquired - */ -bool brcms_del_timer(struct brcms_timer *t) -{ - if (t->set) { - t->set = false; - if (!cancel_delayed_work(&t->dly_wrk)) - return false; - - atomic_dec(&t->wl->callbacks); - } - - return true; -} - -/* - * precondition: perimeter lock has been acquired - */ -void brcms_free_timer(struct brcms_timer *t) -{ - struct brcms_info *wl = t->wl; - struct brcms_timer *tmp; - - /* delete the timer in case it is active */ - brcms_del_timer(t); - - if (wl->timers == t) { - wl->timers = wl->timers->next; -#ifdef DEBUG - kfree(t->name); -#endif - kfree(t); - return; - - } - - tmp = wl->timers; - while (tmp) { - if (tmp->next == t) { - tmp->next = t->next; -#ifdef DEBUG - kfree(t->name); -#endif - kfree(t); - return; - } - tmp = tmp->next; - } - -} - -/* - * precondition: perimeter lock has been acquired - */ -int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx) -{ - int i, entry; - const u8 *pdata; - struct firmware_hdr *hdr; - for (i = 0; i < wl->fw.fw_cnt; i++) { - hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data; - for (entry = 0; entry < wl->fw.hdr_num_entries[i]; - entry++, hdr++) { - u32 len = le32_to_cpu(hdr->len); - if (le32_to_cpu(hdr->idx) == idx) { - pdata = wl->fw.fw_bin[i]->data + - le32_to_cpu(hdr->offset); - *pbuf = kmemdup(pdata, len, GFP_ATOMIC); - if (*pbuf == NULL) - goto fail; - - return 0; - } - } - } - brcms_err(wl->wlc->hw->d11core, - "ERROR: ucode buf tag:%d can not be found!\n", idx); - *pbuf = NULL; -fail: - return -ENODATA; -} - -/* - * Precondition: Since this function is called in brcms_bcma_probe() context, - * no locking is required. - */ -int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, u32 idx) -{ - int i, entry; - const u8 *pdata; - struct firmware_hdr *hdr; - for (i = 0; i < wl->fw.fw_cnt; i++) { - hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data; - for (entry = 0; entry < wl->fw.hdr_num_entries[i]; - entry++, hdr++) { - if (le32_to_cpu(hdr->idx) == idx) { - pdata = wl->fw.fw_bin[i]->data + - le32_to_cpu(hdr->offset); - if (le32_to_cpu(hdr->len) != 4) { - brcms_err(wl->wlc->hw->d11core, - "ERROR: fw hdr len\n"); - return -ENOMSG; - } - *n_bytes = le32_to_cpu(*((__le32 *) pdata)); - return 0; - } - } - } - brcms_err(wl->wlc->hw->d11core, - "ERROR: ucode tag:%d can not be found!\n", idx); - return -ENOMSG; -} - -/* - * precondition: can both be called locked and unlocked - */ -void brcms_ucode_free_buf(void *p) -{ - kfree(p); -} - -/* - * checks validity of all firmware images loaded from user space - * - * Precondition: Since this function is called in brcms_bcma_probe() context, - * no locking is required. - */ -int brcms_check_firmwares(struct brcms_info *wl) -{ - int i; - int entry; - int rc = 0; - const struct firmware *fw; - const struct firmware *fw_hdr; - struct firmware_hdr *ucode_hdr; - for (i = 0; i < MAX_FW_IMAGES && rc == 0; i++) { - fw = wl->fw.fw_bin[i]; - fw_hdr = wl->fw.fw_hdr[i]; - if (fw == NULL && fw_hdr == NULL) { - break; - } else if (fw == NULL || fw_hdr == NULL) { - wiphy_err(wl->wiphy, "%s: invalid bin/hdr fw\n", - __func__); - rc = -EBADF; - } else if (fw_hdr->size % sizeof(struct firmware_hdr)) { - wiphy_err(wl->wiphy, "%s: non integral fw hdr file " - "size %zu/%zu\n", __func__, fw_hdr->size, - sizeof(struct firmware_hdr)); - rc = -EBADF; - } else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) { - wiphy_err(wl->wiphy, "%s: out of bounds fw file size %zu\n", - __func__, fw->size); - rc = -EBADF; - } else { - /* check if ucode section overruns firmware image */ - ucode_hdr = (struct firmware_hdr *)fw_hdr->data; - for (entry = 0; entry < wl->fw.hdr_num_entries[i] && - !rc; entry++, ucode_hdr++) { - if (le32_to_cpu(ucode_hdr->offset) + - le32_to_cpu(ucode_hdr->len) > - fw->size) { - wiphy_err(wl->wiphy, - "%s: conflicting bin/hdr\n", - __func__); - rc = -EBADF; - } - } - } - } - if (rc == 0 && wl->fw.fw_cnt != i) { - wiphy_err(wl->wiphy, "%s: invalid fw_cnt=%d\n", __func__, - wl->fw.fw_cnt); - rc = -EBADF; - } - return rc; -} - -/* - * precondition: perimeter lock has been acquired - */ -bool brcms_rfkill_set_hw_state(struct brcms_info *wl) -{ - bool blocked = brcms_c_check_radio_disabled(wl->wlc); - - spin_unlock_bh(&wl->lock); - wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked); - if (blocked) - wiphy_rfkill_start_polling(wl->pub->ieee_hw->wiphy); - spin_lock_bh(&wl->lock); - return blocked; -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h deleted file mode 100644 index 198053dfc310..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_MAC80211_IF_H_ -#define _BRCM_MAC80211_IF_H_ - -#include -#include -#include -#include - -#include "ucode_loader.h" -#include "led.h" -/* - * Starting index for 5G rates in the - * legacy rate table. - */ -#define BRCMS_LEGACY_5G_RATE_OFFSET 4 - -/* softmac ioctl definitions */ -#define BRCMS_SET_SHORTSLOT_OVERRIDE 146 - -struct brcms_timer { - struct delayed_work dly_wrk; - struct brcms_info *wl; - void (*fn) (void *); /* function called upon expiration */ - void *arg; /* fixed argument provided to called function */ - uint ms; - bool periodic; - bool set; /* indicates if timer is active */ - struct brcms_timer *next; /* for freeing on unload */ -#ifdef DEBUG - char *name; /* Description of the timer */ -#endif -}; - -struct brcms_if { - uint subunit; /* WDS/BSS unit */ - struct pci_dev *pci_dev; -}; - -#define MAX_FW_IMAGES 4 -struct brcms_firmware { - u32 fw_cnt; - const struct firmware *fw_bin[MAX_FW_IMAGES]; - const struct firmware *fw_hdr[MAX_FW_IMAGES]; - u32 hdr_num_entries[MAX_FW_IMAGES]; -}; - -struct brcms_info { - struct brcms_pub *pub; /* pointer to public wlc state */ - struct brcms_c_info *wlc; /* pointer to private common data */ - u32 magic; - - int irq; - - spinlock_t lock; /* per-device perimeter lock */ - spinlock_t isr_lock; /* per-device ISR synchronization lock */ - - /* tx flush */ - wait_queue_head_t tx_flush_wq; - - /* timer related fields */ - atomic_t callbacks; /* # outstanding callback functions */ - struct brcms_timer *timers; /* timer cleanup queue */ - - struct tasklet_struct tasklet; /* dpc tasklet */ - bool resched; /* dpc needs to be and is rescheduled */ - struct brcms_firmware fw; - struct wiphy *wiphy; - struct brcms_ucode ucode; - bool mute_tx; - struct brcms_led radio_led; - struct led_classdev led_dev; -}; - -/* misc callbacks */ -void brcms_init(struct brcms_info *wl); -uint brcms_reset(struct brcms_info *wl); -void brcms_intrson(struct brcms_info *wl); -u32 brcms_intrsoff(struct brcms_info *wl); -void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask); -int brcms_up(struct brcms_info *wl); -void brcms_down(struct brcms_info *wl); -void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, - bool state, int prio); -bool brcms_rfkill_set_hw_state(struct brcms_info *wl); - -/* timer functions */ -struct brcms_timer *brcms_init_timer(struct brcms_info *wl, - void (*fn) (void *arg), void *arg, - const char *name); -void brcms_free_timer(struct brcms_timer *timer); -void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic); -bool brcms_del_timer(struct brcms_timer *timer); -void brcms_dpc(unsigned long data); -void brcms_timer(struct brcms_timer *t); -void brcms_fatal_error(struct brcms_info *wl); - -#endif /* _BRCM_MAC80211_IF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c deleted file mode 100644 index 218cbc8bf3a7..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ /dev/null @@ -1,8139 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * Copyright (c) 2013 Hauke Mehrtens - * - * 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. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include "rate.h" -#include "scb.h" -#include "phy/phy_hal.h" -#include "channel.h" -#include "antsel.h" -#include "stf.h" -#include "ampdu.h" -#include "mac80211_if.h" -#include "ucode_loader.h" -#include "main.h" -#include "soc.h" -#include "dma.h" -#include "debug.h" -#include "brcms_trace_events.h" - -/* watchdog timer, in unit of ms */ -#define TIMER_INTERVAL_WATCHDOG 1000 -/* radio monitor timer, in unit of ms */ -#define TIMER_INTERVAL_RADIOCHK 800 - -/* beacon interval, in unit of 1024TU */ -#define BEACON_INTERVAL_DEFAULT 100 - -/* n-mode support capability */ -/* 2x2 includes both 1x1 & 2x2 devices - * reserved #define 2 for future when we want to separate 1x1 & 2x2 and - * control it independently - */ -#define WL_11N_2x2 1 -#define WL_11N_3x3 3 -#define WL_11N_4x4 4 - -#define EDCF_ACI_MASK 0x60 -#define EDCF_ACI_SHIFT 5 -#define EDCF_ECWMIN_MASK 0x0f -#define EDCF_ECWMAX_SHIFT 4 -#define EDCF_AIFSN_MASK 0x0f -#define EDCF_AIFSN_MAX 15 -#define EDCF_ECWMAX_MASK 0xf0 - -#define EDCF_AC_BE_TXOP_STA 0x0000 -#define EDCF_AC_BK_TXOP_STA 0x0000 -#define EDCF_AC_VO_ACI_STA 0x62 -#define EDCF_AC_VO_ECW_STA 0x32 -#define EDCF_AC_VI_ACI_STA 0x42 -#define EDCF_AC_VI_ECW_STA 0x43 -#define EDCF_AC_BK_ECW_STA 0xA4 -#define EDCF_AC_VI_TXOP_STA 0x005e -#define EDCF_AC_VO_TXOP_STA 0x002f -#define EDCF_AC_BE_ACI_STA 0x03 -#define EDCF_AC_BE_ECW_STA 0xA4 -#define EDCF_AC_BK_ACI_STA 0x27 -#define EDCF_AC_VO_TXOP_AP 0x002f - -#define EDCF_TXOP2USEC(txop) ((txop) << 5) -#define EDCF_ECW2CW(exp) ((1 << (exp)) - 1) - -#define APHY_SYMBOL_TIME 4 -#define APHY_PREAMBLE_TIME 16 -#define APHY_SIGNAL_TIME 4 -#define APHY_SIFS_TIME 16 -#define APHY_SERVICE_NBITS 16 -#define APHY_TAIL_NBITS 6 -#define BPHY_SIFS_TIME 10 -#define BPHY_PLCP_SHORT_TIME 96 - -#define PREN_PREAMBLE 24 -#define PREN_MM_EXT 12 -#define PREN_PREAMBLE_EXT 4 - -#define DOT11_MAC_HDR_LEN 24 -#define DOT11_ACK_LEN 10 -#define DOT11_BA_LEN 4 -#define DOT11_OFDM_SIGNAL_EXTENSION 6 -#define DOT11_MIN_FRAG_LEN 256 -#define DOT11_RTS_LEN 16 -#define DOT11_CTS_LEN 10 -#define DOT11_BA_BITMAP_LEN 128 -#define DOT11_MAXNUMFRAGS 16 -#define DOT11_MAX_FRAG_LEN 2346 - -#define BPHY_PLCP_TIME 192 -#define RIFS_11N_TIME 2 - -/* length of the BCN template area */ -#define BCN_TMPL_LEN 512 - -/* brcms_bss_info flag bit values */ -#define BRCMS_BSS_HT 0x0020 /* BSS is HT (MIMO) capable */ - -/* chip rx buffer offset */ -#define BRCMS_HWRXOFF 38 - -/* rfdisable delay timer 500 ms, runs of ALP clock */ -#define RFDISABLE_DEFAULT 10000000 - -#define BRCMS_TEMPSENSE_PERIOD 10 /* 10 second timeout */ - -/* synthpu_dly times in us */ -#define SYNTHPU_DLY_APHY_US 3700 -#define SYNTHPU_DLY_BPHY_US 1050 -#define SYNTHPU_DLY_NPHY_US 2048 -#define SYNTHPU_DLY_LPPHY_US 300 - -#define ANTCNT 10 /* vanilla M_MAX_ANTCNT val */ - -/* Per-AC retry limit register definitions; uses defs.h bitfield macros */ -#define EDCF_SHORT_S 0 -#define EDCF_SFB_S 4 -#define EDCF_LONG_S 8 -#define EDCF_LFB_S 12 -#define EDCF_SHORT_M BITFIELD_MASK(4) -#define EDCF_SFB_M BITFIELD_MASK(4) -#define EDCF_LONG_M BITFIELD_MASK(4) -#define EDCF_LFB_M BITFIELD_MASK(4) - -#define RETRY_SHORT_DEF 7 /* Default Short retry Limit */ -#define RETRY_SHORT_MAX 255 /* Maximum Short retry Limit */ -#define RETRY_LONG_DEF 4 /* Default Long retry count */ -#define RETRY_SHORT_FB 3 /* Short count for fb rate */ -#define RETRY_LONG_FB 2 /* Long count for fb rate */ - -#define APHY_CWMIN 15 -#define PHY_CWMAX 1023 - -#define EDCF_AIFSN_MIN 1 - -#define FRAGNUM_MASK 0xF - -#define APHY_SLOT_TIME 9 -#define BPHY_SLOT_TIME 20 - -#define WL_SPURAVOID_OFF 0 -#define WL_SPURAVOID_ON1 1 -#define WL_SPURAVOID_ON2 2 - -/* invalid core flags, use the saved coreflags */ -#define BRCMS_USE_COREFLAGS 0xffffffff - -/* values for PLCPHdr_override */ -#define BRCMS_PLCP_AUTO -1 -#define BRCMS_PLCP_SHORT 0 -#define BRCMS_PLCP_LONG 1 - -/* values for g_protection_override and n_protection_override */ -#define BRCMS_PROTECTION_AUTO -1 -#define BRCMS_PROTECTION_OFF 0 -#define BRCMS_PROTECTION_ON 1 -#define BRCMS_PROTECTION_MMHDR_ONLY 2 -#define BRCMS_PROTECTION_CTS_ONLY 3 - -/* values for g_protection_control and n_protection_control */ -#define BRCMS_PROTECTION_CTL_OFF 0 -#define BRCMS_PROTECTION_CTL_LOCAL 1 -#define BRCMS_PROTECTION_CTL_OVERLAP 2 - -/* values for n_protection */ -#define BRCMS_N_PROTECTION_OFF 0 -#define BRCMS_N_PROTECTION_OPTIONAL 1 -#define BRCMS_N_PROTECTION_20IN40 2 -#define BRCMS_N_PROTECTION_MIXEDMODE 3 - -/* values for band specific 40MHz capabilities */ -#define BRCMS_N_BW_20ALL 0 -#define BRCMS_N_BW_40ALL 1 -#define BRCMS_N_BW_20IN2G_40IN5G 2 - -/* bitflags for SGI support (sgi_rx iovar) */ -#define BRCMS_N_SGI_20 0x01 -#define BRCMS_N_SGI_40 0x02 - -/* defines used by the nrate iovar */ -/* MSC in use,indicates b0-6 holds an mcs */ -#define NRATE_MCS_INUSE 0x00000080 -/* rate/mcs value */ -#define NRATE_RATE_MASK 0x0000007f -/* stf mode mask: siso, cdd, stbc, sdm */ -#define NRATE_STF_MASK 0x0000ff00 -/* stf mode shift */ -#define NRATE_STF_SHIFT 8 -/* bit indicate to override mcs only */ -#define NRATE_OVERRIDE_MCS_ONLY 0x40000000 -#define NRATE_SGI_MASK 0x00800000 /* sgi mode */ -#define NRATE_SGI_SHIFT 23 /* sgi mode */ -#define NRATE_LDPC_CODING 0x00400000 /* adv coding in use */ -#define NRATE_LDPC_SHIFT 22 /* ldpc shift */ - -#define NRATE_STF_SISO 0 /* stf mode SISO */ -#define NRATE_STF_CDD 1 /* stf mode CDD */ -#define NRATE_STF_STBC 2 /* stf mode STBC */ -#define NRATE_STF_SDM 3 /* stf mode SDM */ - -#define MAX_DMA_SEGS 4 - -/* # of entries in Tx FIFO */ -#define NTXD 64 -/* Max # of entries in Rx FIFO based on 4kb page size */ -#define NRXD 256 - -/* Amount of headroom to leave in Tx FIFO */ -#define TX_HEADROOM 4 - -/* try to keep this # rbufs posted to the chip */ -#define NRXBUFPOST 32 - -/* max # frames to process in brcms_c_recv() */ -#define RXBND 8 -/* max # tx status to process in wlc_txstatus() */ -#define TXSBND 8 - -/* brcmu_format_flags() bit description structure */ -struct brcms_c_bit_desc { - u32 bit; - const char *name; -}; - -/* - * The following table lists the buffer memory allocated to xmt fifos in HW. - * the size is in units of 256bytes(one block), total size is HW dependent - * ucode has default fifo partition, sw can overwrite if necessary - * - * This is documented in twiki under the topic UcodeTxFifo. Please ensure - * the twiki is updated before making changes. - */ - -/* Starting corerev for the fifo size table */ -#define XMTFIFOTBL_STARTREV 17 - -struct d11init { - __le16 addr; - __le16 size; - __le32 value; -}; - -struct edcf_acparam { - u8 ACI; - u8 ECW; - u16 TXOP; -} __packed; - -/* debug/trace */ -uint brcm_msg_level; - -/* TX FIFO number to WME/802.1E Access Category */ -static const u8 wme_fifo2ac[] = { - IEEE80211_AC_BK, - IEEE80211_AC_BE, - IEEE80211_AC_VI, - IEEE80211_AC_VO, - IEEE80211_AC_BE, - IEEE80211_AC_BE -}; - -/* ieee80211 Access Category to TX FIFO number */ -static const u8 wme_ac2fifo[] = { - TX_AC_VO_FIFO, - TX_AC_VI_FIFO, - TX_AC_BE_FIFO, - TX_AC_BK_FIFO -}; - -static const u16 xmtfifo_sz[][NFIFO] = { - /* corerev 17: 5120, 49152, 49152, 5376, 4352, 1280 */ - {20, 192, 192, 21, 17, 5}, - /* corerev 18: */ - {0, 0, 0, 0, 0, 0}, - /* corerev 19: */ - {0, 0, 0, 0, 0, 0}, - /* corerev 20: 5120, 49152, 49152, 5376, 4352, 1280 */ - {20, 192, 192, 21, 17, 5}, - /* corerev 21: 2304, 14848, 5632, 3584, 3584, 1280 */ - {9, 58, 22, 14, 14, 5}, - /* corerev 22: 5120, 49152, 49152, 5376, 4352, 1280 */ - {20, 192, 192, 21, 17, 5}, - /* corerev 23: 5120, 49152, 49152, 5376, 4352, 1280 */ - {20, 192, 192, 21, 17, 5}, - /* corerev 24: 2304, 14848, 5632, 3584, 3584, 1280 */ - {9, 58, 22, 14, 14, 5}, - /* corerev 25: */ - {0, 0, 0, 0, 0, 0}, - /* corerev 26: */ - {0, 0, 0, 0, 0, 0}, - /* corerev 27: */ - {0, 0, 0, 0, 0, 0}, - /* corerev 28: 2304, 14848, 5632, 3584, 3584, 1280 */ - {9, 58, 22, 14, 14, 5}, -}; - -#ifdef DEBUG -static const char * const fifo_names[] = { - "AC_BK", "AC_BE", "AC_VI", "AC_VO", "BCMC", "ATIM" }; -#else -static const char fifo_names[6][1]; -#endif - -#ifdef DEBUG -/* pointer to most recently allocated wl/wlc */ -static struct brcms_c_info *wlc_info_dbg = (struct brcms_c_info *) (NULL); -#endif - -/* Mapping of ieee80211 AC numbers to tx fifos */ -static const u8 ac_to_fifo_mapping[IEEE80211_NUM_ACS] = { - [IEEE80211_AC_VO] = TX_AC_VO_FIFO, - [IEEE80211_AC_VI] = TX_AC_VI_FIFO, - [IEEE80211_AC_BE] = TX_AC_BE_FIFO, - [IEEE80211_AC_BK] = TX_AC_BK_FIFO, -}; - -/* Mapping of tx fifos to ieee80211 AC numbers */ -static const u8 fifo_to_ac_mapping[IEEE80211_NUM_ACS] = { - [TX_AC_BK_FIFO] = IEEE80211_AC_BK, - [TX_AC_BE_FIFO] = IEEE80211_AC_BE, - [TX_AC_VI_FIFO] = IEEE80211_AC_VI, - [TX_AC_VO_FIFO] = IEEE80211_AC_VO, -}; - -static u8 brcms_ac_to_fifo(u8 ac) -{ - if (ac >= ARRAY_SIZE(ac_to_fifo_mapping)) - return TX_AC_BE_FIFO; - return ac_to_fifo_mapping[ac]; -} - -static u8 brcms_fifo_to_ac(u8 fifo) -{ - if (fifo >= ARRAY_SIZE(fifo_to_ac_mapping)) - return IEEE80211_AC_BE; - return fifo_to_ac_mapping[fifo]; -} - -/* Find basic rate for a given rate */ -static u8 brcms_basic_rate(struct brcms_c_info *wlc, u32 rspec) -{ - if (is_mcs_rate(rspec)) - return wlc->band->basic_rate[mcs_table[rspec & RSPEC_RATE_MASK] - .leg_ofdm]; - return wlc->band->basic_rate[rspec & RSPEC_RATE_MASK]; -} - -static u16 frametype(u32 rspec, u8 mimoframe) -{ - if (is_mcs_rate(rspec)) - return mimoframe; - return is_cck_rate(rspec) ? FT_CCK : FT_OFDM; -} - -/* currently the best mechanism for determining SIFS is the band in use */ -static u16 get_sifs(struct brcms_band *band) -{ - return band->bandtype == BRCM_BAND_5G ? APHY_SIFS_TIME : - BPHY_SIFS_TIME; -} - -/* - * Detect Card removed. - * Even checking an sbconfig register read will not false trigger when the core - * is in reset it breaks CF address mechanism. Accessing gphy phyversion will - * cause SB error if aphy is in reset on 4306B0-DB. Need a simple accessible - * reg with fixed 0/1 pattern (some platforms return all 0). - * If clocks are present, call the sb routine which will figure out if the - * device is removed. - */ -static bool brcms_deviceremoved(struct brcms_c_info *wlc) -{ - u32 macctrl; - - if (!wlc->hw->clk) - return ai_deviceremoved(wlc->hw->sih); - macctrl = bcma_read32(wlc->hw->d11core, - D11REGOFFS(maccontrol)); - return (macctrl & (MCTL_PSM_JMP_0 | MCTL_IHR_EN)) != MCTL_IHR_EN; -} - -/* sum the individual fifo tx pending packet counts */ -static int brcms_txpktpendtot(struct brcms_c_info *wlc) -{ - int i; - int pending = 0; - - for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++) - if (wlc->hw->di[i]) - pending += dma_txpending(wlc->hw->di[i]); - return pending; -} - -static bool brcms_is_mband_unlocked(struct brcms_c_info *wlc) -{ - return wlc->pub->_nbands > 1 && !wlc->bandlocked; -} - -static int brcms_chspec_bw(u16 chanspec) -{ - if (CHSPEC_IS40(chanspec)) - return BRCMS_40_MHZ; - if (CHSPEC_IS20(chanspec)) - return BRCMS_20_MHZ; - - return BRCMS_10_MHZ; -} - -static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg) -{ - if (cfg == NULL) - return; - - kfree(cfg->current_bss); - kfree(cfg); -} - -static void brcms_c_detach_mfree(struct brcms_c_info *wlc) -{ - if (wlc == NULL) - return; - - brcms_c_bsscfg_mfree(wlc->bsscfg); - kfree(wlc->pub); - kfree(wlc->modulecb); - kfree(wlc->default_bss); - kfree(wlc->protection); - kfree(wlc->stf); - kfree(wlc->bandstate[0]); - if (wlc->corestate) - kfree(wlc->corestate->macstat_snapshot); - kfree(wlc->corestate); - if (wlc->hw) - kfree(wlc->hw->bandstate[0]); - kfree(wlc->hw); - if (wlc->beacon) - dev_kfree_skb_any(wlc->beacon); - if (wlc->probe_resp) - dev_kfree_skb_any(wlc->probe_resp); - - kfree(wlc); -} - -static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit) -{ - struct brcms_bss_cfg *cfg; - - cfg = kzalloc(sizeof(struct brcms_bss_cfg), GFP_ATOMIC); - if (cfg == NULL) - goto fail; - - cfg->current_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC); - if (cfg->current_bss == NULL) - goto fail; - - return cfg; - - fail: - brcms_c_bsscfg_mfree(cfg); - return NULL; -} - -static struct brcms_c_info * -brcms_c_attach_malloc(uint unit, uint *err, uint devid) -{ - struct brcms_c_info *wlc; - - wlc = kzalloc(sizeof(struct brcms_c_info), GFP_ATOMIC); - if (wlc == NULL) { - *err = 1002; - goto fail; - } - - /* allocate struct brcms_c_pub state structure */ - wlc->pub = kzalloc(sizeof(struct brcms_pub), GFP_ATOMIC); - if (wlc->pub == NULL) { - *err = 1003; - goto fail; - } - wlc->pub->wlc = wlc; - - /* allocate struct brcms_hardware state structure */ - - wlc->hw = kzalloc(sizeof(struct brcms_hardware), GFP_ATOMIC); - if (wlc->hw == NULL) { - *err = 1005; - goto fail; - } - wlc->hw->wlc = wlc; - - wlc->hw->bandstate[0] = - kzalloc(sizeof(struct brcms_hw_band) * MAXBANDS, GFP_ATOMIC); - if (wlc->hw->bandstate[0] == NULL) { - *err = 1006; - goto fail; - } else { - int i; - - for (i = 1; i < MAXBANDS; i++) - wlc->hw->bandstate[i] = (struct brcms_hw_band *) - ((unsigned long)wlc->hw->bandstate[0] + - (sizeof(struct brcms_hw_band) * i)); - } - - wlc->modulecb = - kzalloc(sizeof(struct modulecb) * BRCMS_MAXMODULES, GFP_ATOMIC); - if (wlc->modulecb == NULL) { - *err = 1009; - goto fail; - } - - wlc->default_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC); - if (wlc->default_bss == NULL) { - *err = 1010; - goto fail; - } - - wlc->bsscfg = brcms_c_bsscfg_malloc(unit); - if (wlc->bsscfg == NULL) { - *err = 1011; - goto fail; - } - - wlc->protection = kzalloc(sizeof(struct brcms_protection), - GFP_ATOMIC); - if (wlc->protection == NULL) { - *err = 1016; - goto fail; - } - - wlc->stf = kzalloc(sizeof(struct brcms_stf), GFP_ATOMIC); - if (wlc->stf == NULL) { - *err = 1017; - goto fail; - } - - wlc->bandstate[0] = - kzalloc(sizeof(struct brcms_band)*MAXBANDS, GFP_ATOMIC); - if (wlc->bandstate[0] == NULL) { - *err = 1025; - goto fail; - } else { - int i; - - for (i = 1; i < MAXBANDS; i++) - wlc->bandstate[i] = (struct brcms_band *) - ((unsigned long)wlc->bandstate[0] - + (sizeof(struct brcms_band)*i)); - } - - wlc->corestate = kzalloc(sizeof(struct brcms_core), GFP_ATOMIC); - if (wlc->corestate == NULL) { - *err = 1026; - goto fail; - } - - wlc->corestate->macstat_snapshot = - kzalloc(sizeof(struct macstat), GFP_ATOMIC); - if (wlc->corestate->macstat_snapshot == NULL) { - *err = 1027; - goto fail; - } - - return wlc; - - fail: - brcms_c_detach_mfree(wlc); - return NULL; -} - -/* - * Update the slot timing for standard 11b/g (20us slots) - * or shortslot 11g (9us slots) - * The PSM needs to be suspended for this call. - */ -static void brcms_b_update_slot_timing(struct brcms_hardware *wlc_hw, - bool shortslot) -{ - struct bcma_device *core = wlc_hw->d11core; - - if (shortslot) { - /* 11g short slot: 11a timing */ - bcma_write16(core, D11REGOFFS(ifs_slot), 0x0207); - brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, APHY_SLOT_TIME); - } else { - /* 11g long slot: 11b timing */ - bcma_write16(core, D11REGOFFS(ifs_slot), 0x0212); - brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, BPHY_SLOT_TIME); - } -} - -/* - * calculate frame duration of a given rate and length, return - * time in usec unit - */ -static uint brcms_c_calc_frame_time(struct brcms_c_info *wlc, u32 ratespec, - u8 preamble_type, uint mac_len) -{ - uint nsyms, dur = 0, Ndps, kNdps; - uint rate = rspec2rate(ratespec); - - if (rate == 0) { - brcms_err(wlc->hw->d11core, "wl%d: WAR: using rate of 1 mbps\n", - wlc->pub->unit); - rate = BRCM_RATE_1M; - } - - if (is_mcs_rate(ratespec)) { - uint mcs = ratespec & RSPEC_RATE_MASK; - int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); - - dur = PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT); - if (preamble_type == BRCMS_MM_PREAMBLE) - dur += PREN_MM_EXT; - /* 1000Ndbps = kbps * 4 */ - kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), - rspec_issgi(ratespec)) * 4; - - if (rspec_stc(ratespec) == 0) - nsyms = - CEIL((APHY_SERVICE_NBITS + 8 * mac_len + - APHY_TAIL_NBITS) * 1000, kNdps); - else - /* STBC needs to have even number of symbols */ - nsyms = - 2 * - CEIL((APHY_SERVICE_NBITS + 8 * mac_len + - APHY_TAIL_NBITS) * 1000, 2 * kNdps); - - dur += APHY_SYMBOL_TIME * nsyms; - if (wlc->band->bandtype == BRCM_BAND_2G) - dur += DOT11_OFDM_SIGNAL_EXTENSION; - } else if (is_ofdm_rate(rate)) { - dur = APHY_PREAMBLE_TIME; - dur += APHY_SIGNAL_TIME; - /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */ - Ndps = rate * 2; - /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */ - nsyms = - CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS), - Ndps); - dur += APHY_SYMBOL_TIME * nsyms; - if (wlc->band->bandtype == BRCM_BAND_2G) - dur += DOT11_OFDM_SIGNAL_EXTENSION; - } else { - /* - * calc # bits * 2 so factor of 2 in rate (1/2 mbps) - * will divide out - */ - mac_len = mac_len * 8 * 2; - /* calc ceiling of bits/rate = microseconds of air time */ - dur = (mac_len + rate - 1) / rate; - if (preamble_type & BRCMS_SHORT_PREAMBLE) - dur += BPHY_PLCP_SHORT_TIME; - else - dur += BPHY_PLCP_TIME; - } - return dur; -} - -static void brcms_c_write_inits(struct brcms_hardware *wlc_hw, - const struct d11init *inits) -{ - struct bcma_device *core = wlc_hw->d11core; - int i; - uint offset; - u16 size; - u32 value; - - brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); - - for (i = 0; inits[i].addr != cpu_to_le16(0xffff); i++) { - size = le16_to_cpu(inits[i].size); - offset = le16_to_cpu(inits[i].addr); - value = le32_to_cpu(inits[i].value); - if (size == 2) - bcma_write16(core, offset, value); - else if (size == 4) - bcma_write32(core, offset, value); - else - break; - } -} - -static void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs) -{ - u8 idx; - u16 addr[] = { - M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4, - M_HOST_FLAGS5 - }; - - for (idx = 0; idx < MHFMAX; idx++) - brcms_b_write_shm(wlc_hw, addr[idx], mhfs[idx]); -} - -static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw) -{ - struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; - - /* init microcode host flags */ - brcms_c_write_mhf(wlc_hw, wlc_hw->band->mhfs); - - /* do band-specific ucode IHR, SHM, and SCR inits */ - if (D11REV_IS(wlc_hw->corerev, 17) || D11REV_IS(wlc_hw->corerev, 23)) { - if (BRCMS_ISNPHY(wlc_hw->band)) - brcms_c_write_inits(wlc_hw, ucode->d11n0bsinitvals16); - else - brcms_err(wlc_hw->d11core, - "%s: wl%d: unsupported phy in corerev %d\n", - __func__, wlc_hw->unit, - wlc_hw->corerev); - } else { - if (D11REV_IS(wlc_hw->corerev, 24)) { - if (BRCMS_ISLCNPHY(wlc_hw->band)) - brcms_c_write_inits(wlc_hw, - ucode->d11lcn0bsinitvals24); - else - brcms_err(wlc_hw->d11core, - "%s: wl%d: unsupported phy in core rev %d\n", - __func__, wlc_hw->unit, - wlc_hw->corerev); - } else { - brcms_err(wlc_hw->d11core, - "%s: wl%d: unsupported corerev %d\n", - __func__, wlc_hw->unit, wlc_hw->corerev); - } - } -} - -static void brcms_b_core_ioctl(struct brcms_hardware *wlc_hw, u32 m, u32 v) -{ - struct bcma_device *core = wlc_hw->d11core; - u32 ioctl = bcma_aread32(core, BCMA_IOCTL) & ~m; - - bcma_awrite32(core, BCMA_IOCTL, ioctl | v); -} - -static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk) -{ - brcms_dbg_info(wlc_hw->d11core, "wl%d: clk %d\n", wlc_hw->unit, clk); - - wlc_hw->phyclk = clk; - - if (OFF == clk) { /* clear gmode bit, put phy into reset */ - - brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC | SICF_GMODE), - (SICF_PRST | SICF_FGC)); - udelay(1); - brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC), SICF_PRST); - udelay(1); - - } else { /* take phy out of reset */ - - brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC), SICF_FGC); - udelay(1); - brcms_b_core_ioctl(wlc_hw, SICF_FGC, 0); - udelay(1); - - } -} - -/* low-level band switch utility routine */ -static void brcms_c_setxband(struct brcms_hardware *wlc_hw, uint bandunit) -{ - brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: bandunit %d\n", wlc_hw->unit, - bandunit); - - wlc_hw->band = wlc_hw->bandstate[bandunit]; - - /* - * BMAC_NOTE: - * until we eliminate need for wlc->band refs in low level code - */ - wlc_hw->wlc->band = wlc_hw->wlc->bandstate[bandunit]; - - /* set gmode core flag */ - if (wlc_hw->sbclk && !wlc_hw->noreset) { - u32 gmode = 0; - - if (bandunit == 0) - gmode = SICF_GMODE; - - brcms_b_core_ioctl(wlc_hw, SICF_GMODE, gmode); - } -} - -/* switch to new band but leave it inactive */ -static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - u32 macintmask; - u32 macctrl; - - brcms_dbg_mac80211(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); - macctrl = bcma_read32(wlc_hw->d11core, - D11REGOFFS(maccontrol)); - WARN_ON((macctrl & MCTL_EN_MAC) != 0); - - /* disable interrupts */ - macintmask = brcms_intrsoff(wlc->wl); - - /* radio off */ - wlc_phy_switch_radio(wlc_hw->band->pi, OFF); - - brcms_b_core_phy_clk(wlc_hw, OFF); - - brcms_c_setxband(wlc_hw, bandunit); - - return macintmask; -} - -/* process an individual struct tx_status */ -static bool -brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) -{ - struct sk_buff *p = NULL; - uint queue = NFIFO; - struct dma_pub *dma = NULL; - struct d11txh *txh = NULL; - struct scb *scb = NULL; - bool free_pdu; - int tx_rts, tx_frame_count, tx_rts_count; - uint totlen, supr_status; - bool lastframe; - struct ieee80211_hdr *h; - u16 mcl; - struct ieee80211_tx_info *tx_info; - struct ieee80211_tx_rate *txrate; - int i; - bool fatal = true; - - trace_brcms_txstatus(&wlc->hw->d11core->dev, txs->framelen, - txs->frameid, txs->status, txs->lasttxtime, - txs->sequence, txs->phyerr, txs->ackphyrxsh); - - /* discard intermediate indications for ucode with one legitimate case: - * e.g. if "useRTS" is set. ucode did a successful rts/cts exchange, - * but the subsequent tx of DATA failed. so it will start rts/cts - * from the beginning (resetting the rts transmission count) - */ - if (!(txs->status & TX_STATUS_AMPDU) - && (txs->status & TX_STATUS_INTERMEDIATE)) { - brcms_dbg_tx(wlc->hw->d11core, "INTERMEDIATE but not AMPDU\n"); - fatal = false; - goto out; - } - - queue = txs->frameid & TXFID_QUEUE_MASK; - if (queue >= NFIFO) { - brcms_err(wlc->hw->d11core, "queue %u >= NFIFO\n", queue); - goto out; - } - - dma = wlc->hw->di[queue]; - - p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); - if (p == NULL) { - brcms_err(wlc->hw->d11core, "dma_getnexttxp returned null!\n"); - goto out; - } - - txh = (struct d11txh *) (p->data); - mcl = le16_to_cpu(txh->MacTxControlLow); - - if (txs->phyerr) - brcms_dbg_tx(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n", - txs->phyerr, txh->MainRates); - - if (txs->frameid != le16_to_cpu(txh->TxFrameID)) { - brcms_err(wlc->hw->d11core, "frameid != txh->TxFrameID\n"); - goto out; - } - tx_info = IEEE80211_SKB_CB(p); - h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN); - - if (tx_info->rate_driver_data[0]) - scb = &wlc->pri_scb; - - if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { - brcms_c_ampdu_dotxstatus(wlc->ampdu, scb, p, txs); - fatal = false; - goto out; - } - - /* - * brcms_c_ampdu_dotxstatus() will trace tx descriptors for AMPDU - * frames; this traces them for the rest. - */ - trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh)); - - supr_status = txs->status & TX_STATUS_SUPR_MASK; - if (supr_status == TX_STATUS_SUPR_BADCH) { - unsigned xfts = le16_to_cpu(txh->XtraFrameTypes); - brcms_dbg_tx(wlc->hw->d11core, - "Pkt tx suppressed, dest chan %u, current %d\n", - (xfts >> XFTS_CHANNEL_SHIFT) & 0xff, - CHSPEC_CHANNEL(wlc->default_bss->chanspec)); - } - - tx_rts = le16_to_cpu(txh->MacTxControlLow) & TXC_SENDRTS; - tx_frame_count = - (txs->status & TX_STATUS_FRM_RTX_MASK) >> TX_STATUS_FRM_RTX_SHIFT; - tx_rts_count = - (txs->status & TX_STATUS_RTS_RTX_MASK) >> TX_STATUS_RTS_RTX_SHIFT; - - lastframe = !ieee80211_has_morefrags(h->frame_control); - - if (!lastframe) { - brcms_err(wlc->hw->d11core, "Not last frame!\n"); - } else { - /* - * Set information to be consumed by Minstrel ht. - * - * The "fallback limit" is the number of tx attempts a given - * MPDU is sent at the "primary" rate. Tx attempts beyond that - * limit are sent at the "secondary" rate. - * A 'short frame' does not exceed RTS treshold. - */ - u16 sfbl, /* Short Frame Rate Fallback Limit */ - lfbl, /* Long Frame Rate Fallback Limit */ - fbl; - - if (queue < IEEE80211_NUM_ACS) { - sfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]], - EDCF_SFB); - lfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]], - EDCF_LFB); - } else { - sfbl = wlc->SFBL; - lfbl = wlc->LFBL; - } - - txrate = tx_info->status.rates; - if (txrate[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) - fbl = lfbl; - else - fbl = sfbl; - - ieee80211_tx_info_clear_status(tx_info); - - if ((tx_frame_count > fbl) && (txrate[1].idx >= 0)) { - /* - * rate selection requested a fallback rate - * and we used it - */ - txrate[0].count = fbl; - txrate[1].count = tx_frame_count - fbl; - } else { - /* - * rate selection did not request fallback rate, or - * we didn't need it - */ - txrate[0].count = tx_frame_count; - /* - * rc80211_minstrel.c:minstrel_tx_status() expects - * unused rates to be marked with idx = -1 - */ - txrate[1].idx = -1; - txrate[1].count = 0; - } - - /* clear the rest of the rates */ - for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) { - txrate[i].idx = -1; - txrate[i].count = 0; - } - - if (txs->status & TX_STATUS_ACK_RCV) - tx_info->flags |= IEEE80211_TX_STAT_ACK; - } - - totlen = p->len; - free_pdu = true; - - if (lastframe) { - /* remove PLCP & Broadcom tx descriptor header */ - skb_pull(p, D11_PHY_HDR_LEN); - skb_pull(p, D11_TXH_LEN); - ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p); - } else { - brcms_err(wlc->hw->d11core, - "%s: Not last frame => not calling tx_status\n", - __func__); - } - - fatal = false; - - out: - if (fatal) { - if (txh) - trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, - sizeof(*txh)); - brcmu_pkt_buf_free_skb(p); - } - - if (dma && queue < NFIFO) { - u16 ac_queue = brcms_fifo_to_ac(queue); - if (dma->txavail > TX_HEADROOM && queue < TX_BCMC_FIFO && - ieee80211_queue_stopped(wlc->pub->ieee_hw, ac_queue)) - ieee80211_wake_queue(wlc->pub->ieee_hw, ac_queue); - dma_kick_tx(dma); - } - - return fatal; -} - -/* process tx completion events in BMAC - * Return true if more tx status need to be processed. false otherwise. - */ -static bool -brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) -{ - struct bcma_device *core; - struct tx_status txstatus, *txs; - u32 s1, s2; - uint n = 0; - /* - * Param 'max_tx_num' indicates max. # tx status to process before - * break out. - */ - uint max_tx_num = bound ? TXSBND : -1; - - txs = &txstatus; - core = wlc_hw->d11core; - *fatal = false; - - while (n < max_tx_num) { - s1 = bcma_read32(core, D11REGOFFS(frmtxstatus)); - if (s1 == 0xffffffff) { - brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, - __func__); - *fatal = true; - return false; - } - /* only process when valid */ - if (!(s1 & TXS_V)) - break; - - s2 = bcma_read32(core, D11REGOFFS(frmtxstatus2)); - txs->status = s1 & TXS_STATUS_MASK; - txs->frameid = (s1 & TXS_FID_MASK) >> TXS_FID_SHIFT; - txs->sequence = s2 & TXS_SEQ_MASK; - txs->phyerr = (s2 & TXS_PTX_MASK) >> TXS_PTX_SHIFT; - txs->lasttxtime = 0; - - *fatal = brcms_c_dotxstatus(wlc_hw->wlc, txs); - if (*fatal == true) - return false; - n++; - } - - return n >= max_tx_num; -} - -static void brcms_c_tbtt(struct brcms_c_info *wlc) -{ - if (wlc->bsscfg->type == BRCMS_TYPE_ADHOC) - /* - * DirFrmQ is now valid...defer setting until end - * of ATIM window - */ - wlc->qvalid |= MCMD_DIRFRMQVAL; -} - -/* set initial host flags value */ -static void -brcms_c_mhfdef(struct brcms_c_info *wlc, u16 *mhfs, u16 mhf2_init) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - - memset(mhfs, 0, MHFMAX * sizeof(u16)); - - mhfs[MHF2] |= mhf2_init; - - /* prohibit use of slowclock on multifunction boards */ - if (wlc_hw->boardflags & BFL_NOPLLDOWN) - mhfs[MHF1] |= MHF1_FORCEFASTCLK; - - if (BRCMS_ISNPHY(wlc_hw->band) && NREV_LT(wlc_hw->band->phyrev, 2)) { - mhfs[MHF2] |= MHF2_NPHY40MHZ_WAR; - mhfs[MHF1] |= MHF1_IQSWAP_WAR; - } -} - -static uint -dmareg(uint direction, uint fifonum) -{ - if (direction == DMA_TX) - return offsetof(struct d11regs, fifo64regs[fifonum].dmaxmt); - return offsetof(struct d11regs, fifo64regs[fifonum].dmarcv); -} - -static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme) -{ - uint i; - char name[8]; - /* - * ucode host flag 2 needed for pio mode, independent of band and fifo - */ - u16 pio_mhf2 = 0; - struct brcms_hardware *wlc_hw = wlc->hw; - uint unit = wlc_hw->unit; - - /* name and offsets for dma_attach */ - snprintf(name, sizeof(name), "wl%d", unit); - - if (wlc_hw->di[0] == NULL) { /* Init FIFOs */ - int dma_attach_err = 0; - - /* - * FIFO 0 - * TX: TX_AC_BK_FIFO (TX AC Background data packets) - * RX: RX_FIFO (RX data packets) - */ - wlc_hw->di[0] = dma_attach(name, wlc, - (wme ? dmareg(DMA_TX, 0) : 0), - dmareg(DMA_RX, 0), - (wme ? NTXD : 0), NRXD, - RXBUFSZ, -1, NRXBUFPOST, - BRCMS_HWRXOFF); - dma_attach_err |= (NULL == wlc_hw->di[0]); - - /* - * FIFO 1 - * TX: TX_AC_BE_FIFO (TX AC Best-Effort data packets) - * (legacy) TX_DATA_FIFO (TX data packets) - * RX: UNUSED - */ - wlc_hw->di[1] = dma_attach(name, wlc, - dmareg(DMA_TX, 1), 0, - NTXD, 0, 0, -1, 0, 0); - dma_attach_err |= (NULL == wlc_hw->di[1]); - - /* - * FIFO 2 - * TX: TX_AC_VI_FIFO (TX AC Video data packets) - * RX: UNUSED - */ - wlc_hw->di[2] = dma_attach(name, wlc, - dmareg(DMA_TX, 2), 0, - NTXD, 0, 0, -1, 0, 0); - dma_attach_err |= (NULL == wlc_hw->di[2]); - /* - * FIFO 3 - * TX: TX_AC_VO_FIFO (TX AC Voice data packets) - * (legacy) TX_CTL_FIFO (TX control & mgmt packets) - */ - wlc_hw->di[3] = dma_attach(name, wlc, - dmareg(DMA_TX, 3), - 0, NTXD, 0, 0, -1, - 0, 0); - dma_attach_err |= (NULL == wlc_hw->di[3]); -/* Cleaner to leave this as if with AP defined */ - - if (dma_attach_err) { - brcms_err(wlc_hw->d11core, - "wl%d: wlc_attach: dma_attach failed\n", - unit); - return false; - } - - /* get pointer to dma engine tx flow control variable */ - for (i = 0; i < NFIFO; i++) - if (wlc_hw->di[i]) - wlc_hw->txavail[i] = - (uint *) dma_getvar(wlc_hw->di[i], - "&txavail"); - } - - /* initial ucode host flags */ - brcms_c_mhfdef(wlc, wlc_hw->band->mhfs, pio_mhf2); - - return true; -} - -static void brcms_b_detach_dmapio(struct brcms_hardware *wlc_hw) -{ - uint j; - - for (j = 0; j < NFIFO; j++) { - if (wlc_hw->di[j]) { - dma_detach(wlc_hw->di[j]); - wlc_hw->di[j] = NULL; - } - } -} - -/* - * Initialize brcms_c_info default values ... - * may get overrides later in this function - * BMAC_NOTES, move low out and resolve the dangling ones - */ -static void brcms_b_info_init(struct brcms_hardware *wlc_hw) -{ - struct brcms_c_info *wlc = wlc_hw->wlc; - - /* set default sw macintmask value */ - wlc->defmacintmask = DEF_MACINTMASK; - - /* various 802.11g modes */ - wlc_hw->shortslot = false; - - wlc_hw->SFBL = RETRY_SHORT_FB; - wlc_hw->LFBL = RETRY_LONG_FB; - - /* default mac retry limits */ - wlc_hw->SRL = RETRY_SHORT_DEF; - wlc_hw->LRL = RETRY_LONG_DEF; - wlc_hw->chanspec = ch20mhz_chspec(1); -} - -static void brcms_b_wait_for_wake(struct brcms_hardware *wlc_hw) -{ - /* delay before first read of ucode state */ - udelay(40); - - /* wait until ucode is no longer asleep */ - SPINWAIT((brcms_b_read_shm(wlc_hw, M_UCODE_DBGST) == - DBGST_ASLEEP), wlc_hw->wlc->fastpwrup_dly); -} - -/* control chip clock to save power, enable dynamic clock or force fast clock */ -static void brcms_b_clkctl_clk(struct brcms_hardware *wlc_hw, enum bcma_clkmode mode) -{ - if (ai_get_cccaps(wlc_hw->sih) & CC_CAP_PMU) { - /* new chips with PMU, CCS_FORCEHT will distribute the HT clock - * on backplane, but mac core will still run on ALP(not HT) when - * it enters powersave mode, which means the FCA bit may not be - * set. Should wakeup mac if driver wants it to run on HT. - */ - - if (wlc_hw->clk) { - if (mode == BCMA_CLKMODE_FAST) { - bcma_set32(wlc_hw->d11core, - D11REGOFFS(clk_ctl_st), - CCS_FORCEHT); - - udelay(64); - - SPINWAIT( - ((bcma_read32(wlc_hw->d11core, - D11REGOFFS(clk_ctl_st)) & - CCS_HTAVAIL) == 0), - PMU_MAX_TRANSITION_DLY); - WARN_ON(!(bcma_read32(wlc_hw->d11core, - D11REGOFFS(clk_ctl_st)) & - CCS_HTAVAIL)); - } else { - if ((ai_get_pmurev(wlc_hw->sih) == 0) && - (bcma_read32(wlc_hw->d11core, - D11REGOFFS(clk_ctl_st)) & - (CCS_FORCEHT | CCS_HTAREQ))) - SPINWAIT( - ((bcma_read32(wlc_hw->d11core, - offsetof(struct d11regs, - clk_ctl_st)) & - CCS_HTAVAIL) == 0), - PMU_MAX_TRANSITION_DLY); - bcma_mask32(wlc_hw->d11core, - D11REGOFFS(clk_ctl_st), - ~CCS_FORCEHT); - } - } - wlc_hw->forcefastclk = (mode == BCMA_CLKMODE_FAST); - } else { - - /* old chips w/o PMU, force HT through cc, - * then use FCA to verify mac is running fast clock - */ - - wlc_hw->forcefastclk = ai_clkctl_cc(wlc_hw->sih, mode); - - /* check fast clock is available (if core is not in reset) */ - if (wlc_hw->forcefastclk && wlc_hw->clk) - WARN_ON(!(bcma_aread32(wlc_hw->d11core, BCMA_IOST) & - SISF_FCLKA)); - - /* - * keep the ucode wake bit on if forcefastclk is on since we - * do not want ucode to put us back to slow clock when it dozes - * for PM mode. Code below matches the wake override bit with - * current forcefastclk state. Only setting bit in wake_override - * instead of waking ucode immediately since old code had this - * behavior. Older code set wlc->forcefastclk but only had the - * wake happen if the wakup_ucode work (protected by an up - * check) was executed just below. - */ - if (wlc_hw->forcefastclk) - mboolset(wlc_hw->wake_override, - BRCMS_WAKE_OVERRIDE_FORCEFAST); - else - mboolclr(wlc_hw->wake_override, - BRCMS_WAKE_OVERRIDE_FORCEFAST); - } -} - -/* set or clear ucode host flag bits - * it has an optimization for no-change write - * it only writes through shared memory when the core has clock; - * pre-CLK changes should use wlc_write_mhf to get around the optimization - * - * - * bands values are: BRCM_BAND_AUTO <--- Current band only - * BRCM_BAND_5G <--- 5G band only - * BRCM_BAND_2G <--- 2G band only - * BRCM_BAND_ALL <--- All bands - */ -void -brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val, - int bands) -{ - u16 save; - u16 addr[MHFMAX] = { - M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4, - M_HOST_FLAGS5 - }; - struct brcms_hw_band *band; - - if ((val & ~mask) || idx >= MHFMAX) - return; /* error condition */ - - switch (bands) { - /* Current band only or all bands, - * then set the band to current band - */ - case BRCM_BAND_AUTO: - case BRCM_BAND_ALL: - band = wlc_hw->band; - break; - case BRCM_BAND_5G: - band = wlc_hw->bandstate[BAND_5G_INDEX]; - break; - case BRCM_BAND_2G: - band = wlc_hw->bandstate[BAND_2G_INDEX]; - break; - default: - band = NULL; /* error condition */ - } - - if (band) { - save = band->mhfs[idx]; - band->mhfs[idx] = (band->mhfs[idx] & ~mask) | val; - - /* optimization: only write through if changed, and - * changed band is the current band - */ - if (wlc_hw->clk && (band->mhfs[idx] != save) - && (band == wlc_hw->band)) - brcms_b_write_shm(wlc_hw, addr[idx], - (u16) band->mhfs[idx]); - } - - if (bands == BRCM_BAND_ALL) { - wlc_hw->bandstate[0]->mhfs[idx] = - (wlc_hw->bandstate[0]->mhfs[idx] & ~mask) | val; - wlc_hw->bandstate[1]->mhfs[idx] = - (wlc_hw->bandstate[1]->mhfs[idx] & ~mask) | val; - } -} - -/* set the maccontrol register to desired reset state and - * initialize the sw cache of the register - */ -static void brcms_c_mctrl_reset(struct brcms_hardware *wlc_hw) -{ - /* IHR accesses are always enabled, PSM disabled, HPS off and WAKE on */ - wlc_hw->maccontrol = 0; - wlc_hw->suspended_fifos = 0; - wlc_hw->wake_override = 0; - wlc_hw->mute_override = 0; - brcms_b_mctrl(wlc_hw, ~0, MCTL_IHR_EN | MCTL_WAKE); -} - -/* - * write the software state of maccontrol and - * overrides to the maccontrol register - */ -static void brcms_c_mctrl_write(struct brcms_hardware *wlc_hw) -{ - u32 maccontrol = wlc_hw->maccontrol; - - /* OR in the wake bit if overridden */ - if (wlc_hw->wake_override) - maccontrol |= MCTL_WAKE; - - /* set AP and INFRA bits for mute if needed */ - if (wlc_hw->mute_override) { - maccontrol &= ~(MCTL_AP); - maccontrol |= MCTL_INFRA; - } - - bcma_write32(wlc_hw->d11core, D11REGOFFS(maccontrol), - maccontrol); -} - -/* set or clear maccontrol bits */ -void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val) -{ - u32 maccontrol; - u32 new_maccontrol; - - if (val & ~mask) - return; /* error condition */ - maccontrol = wlc_hw->maccontrol; - new_maccontrol = (maccontrol & ~mask) | val; - - /* if the new maccontrol value is the same as the old, nothing to do */ - if (new_maccontrol == maccontrol) - return; - - /* something changed, cache the new value */ - wlc_hw->maccontrol = new_maccontrol; - - /* write the new values with overrides applied */ - brcms_c_mctrl_write(wlc_hw); -} - -void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw, - u32 override_bit) -{ - if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) { - mboolset(wlc_hw->wake_override, override_bit); - return; - } - - mboolset(wlc_hw->wake_override, override_bit); - - brcms_c_mctrl_write(wlc_hw); - brcms_b_wait_for_wake(wlc_hw); -} - -void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw, - u32 override_bit) -{ - mboolclr(wlc_hw->wake_override, override_bit); - - if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) - return; - - brcms_c_mctrl_write(wlc_hw); -} - -/* When driver needs ucode to stop beaconing, it has to make sure that - * MCTL_AP is clear and MCTL_INFRA is set - * Mode MCTL_AP MCTL_INFRA - * AP 1 1 - * STA 0 1 <--- This will ensure no beacons - * IBSS 0 0 - */ -static void brcms_c_ucode_mute_override_set(struct brcms_hardware *wlc_hw) -{ - wlc_hw->mute_override = 1; - - /* if maccontrol already has AP == 0 and INFRA == 1 without this - * override, then there is no change to write - */ - if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA) - return; - - brcms_c_mctrl_write(wlc_hw); -} - -/* Clear the override on AP and INFRA bits */ -static void brcms_c_ucode_mute_override_clear(struct brcms_hardware *wlc_hw) -{ - if (wlc_hw->mute_override == 0) - return; - - wlc_hw->mute_override = 0; - - /* if maccontrol already has AP == 0 and INFRA == 1 without this - * override, then there is no change to write - */ - if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA) - return; - - brcms_c_mctrl_write(wlc_hw); -} - -/* - * Write a MAC address to the given match reg offset in the RXE match engine. - */ -static void -brcms_b_set_addrmatch(struct brcms_hardware *wlc_hw, int match_reg_offset, - const u8 *addr) -{ - struct bcma_device *core = wlc_hw->d11core; - u16 mac_l; - u16 mac_m; - u16 mac_h; - - brcms_dbg_rx(core, "wl%d: brcms_b_set_addrmatch\n", wlc_hw->unit); - - mac_l = addr[0] | (addr[1] << 8); - mac_m = addr[2] | (addr[3] << 8); - mac_h = addr[4] | (addr[5] << 8); - - /* enter the MAC addr into the RXE match registers */ - bcma_write16(core, D11REGOFFS(rcm_ctl), - RCM_INC_DATA | match_reg_offset); - bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_l); - bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_m); - bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_h); -} - -void -brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, int len, - void *buf) -{ - struct bcma_device *core = wlc_hw->d11core; - u32 word; - __le32 word_le; - __be32 word_be; - bool be_bit; - brcms_dbg_info(core, "wl%d\n", wlc_hw->unit); - - bcma_write32(core, D11REGOFFS(tplatewrptr), offset); - - /* if MCTL_BIGEND bit set in mac control register, - * the chip swaps data in fifo, as well as data in - * template ram - */ - be_bit = (bcma_read32(core, D11REGOFFS(maccontrol)) & MCTL_BIGEND) != 0; - - while (len > 0) { - memcpy(&word, buf, sizeof(u32)); - - if (be_bit) { - word_be = cpu_to_be32(word); - word = *(u32 *)&word_be; - } else { - word_le = cpu_to_le32(word); - word = *(u32 *)&word_le; - } - - bcma_write32(core, D11REGOFFS(tplatewrdata), word); - - buf = (u8 *) buf + sizeof(u32); - len -= sizeof(u32); - } -} - -static void brcms_b_set_cwmin(struct brcms_hardware *wlc_hw, u16 newmin) -{ - wlc_hw->band->CWmin = newmin; - - bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), - OBJADDR_SCR_SEL | S_DOT11_CWMIN); - (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); - bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), newmin); -} - -static void brcms_b_set_cwmax(struct brcms_hardware *wlc_hw, u16 newmax) -{ - wlc_hw->band->CWmax = newmax; - - bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), - OBJADDR_SCR_SEL | S_DOT11_CWMAX); - (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); - bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), newmax); -} - -void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw) -{ - bool fastclk; - - /* request FAST clock if not on */ - fastclk = wlc_hw->forcefastclk; - if (!fastclk) - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); - - wlc_phy_bw_state_set(wlc_hw->band->pi, bw); - - brcms_b_phy_reset(wlc_hw); - wlc_phy_init(wlc_hw->band->pi, wlc_phy_chanspec_get(wlc_hw->band->pi)); - - /* restore the clk */ - if (!fastclk) - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); -} - -static void brcms_b_upd_synthpu(struct brcms_hardware *wlc_hw) -{ - u16 v; - struct brcms_c_info *wlc = wlc_hw->wlc; - /* update SYNTHPU_DLY */ - - if (BRCMS_ISLCNPHY(wlc->band)) - v = SYNTHPU_DLY_LPPHY_US; - else if (BRCMS_ISNPHY(wlc->band) && (NREV_GE(wlc->band->phyrev, 3))) - v = SYNTHPU_DLY_NPHY_US; - else - v = SYNTHPU_DLY_BPHY_US; - - brcms_b_write_shm(wlc_hw, M_SYNTHPU_DLY, v); -} - -static void brcms_c_ucode_txant_set(struct brcms_hardware *wlc_hw) -{ - u16 phyctl; - u16 phytxant = wlc_hw->bmac_phytxant; - u16 mask = PHY_TXC_ANT_MASK; - - /* set the Probe Response frame phy control word */ - phyctl = brcms_b_read_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS); - phyctl = (phyctl & ~mask) | phytxant; - brcms_b_write_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS, phyctl); - - /* set the Response (ACK/CTS) frame phy control word */ - phyctl = brcms_b_read_shm(wlc_hw, M_RSP_PCTLWD); - phyctl = (phyctl & ~mask) | phytxant; - brcms_b_write_shm(wlc_hw, M_RSP_PCTLWD, phyctl); -} - -static u16 brcms_b_ofdm_ratetable_offset(struct brcms_hardware *wlc_hw, - u8 rate) -{ - uint i; - u8 plcp_rate = 0; - struct plcp_signal_rate_lookup { - u8 rate; - u8 signal_rate; - }; - /* OFDM RATE sub-field of PLCP SIGNAL field, per 802.11 sec 17.3.4.1 */ - const struct plcp_signal_rate_lookup rate_lookup[] = { - {BRCM_RATE_6M, 0xB}, - {BRCM_RATE_9M, 0xF}, - {BRCM_RATE_12M, 0xA}, - {BRCM_RATE_18M, 0xE}, - {BRCM_RATE_24M, 0x9}, - {BRCM_RATE_36M, 0xD}, - {BRCM_RATE_48M, 0x8}, - {BRCM_RATE_54M, 0xC} - }; - - for (i = 0; i < ARRAY_SIZE(rate_lookup); i++) { - if (rate == rate_lookup[i].rate) { - plcp_rate = rate_lookup[i].signal_rate; - break; - } - } - - /* Find the SHM pointer to the rate table entry by looking in the - * Direct-map Table - */ - return 2 * brcms_b_read_shm(wlc_hw, M_RT_DIRMAP_A + (plcp_rate * 2)); -} - -static void brcms_upd_ofdm_pctl1_table(struct brcms_hardware *wlc_hw) -{ - u8 rate; - u8 rates[8] = { - BRCM_RATE_6M, BRCM_RATE_9M, BRCM_RATE_12M, BRCM_RATE_18M, - BRCM_RATE_24M, BRCM_RATE_36M, BRCM_RATE_48M, BRCM_RATE_54M - }; - u16 entry_ptr; - u16 pctl1; - uint i; - - if (!BRCMS_PHY_11N_CAP(wlc_hw->band)) - return; - - /* walk the phy rate table and update the entries */ - for (i = 0; i < ARRAY_SIZE(rates); i++) { - rate = rates[i]; - - entry_ptr = brcms_b_ofdm_ratetable_offset(wlc_hw, rate); - - /* read the SHM Rate Table entry OFDM PCTL1 values */ - pctl1 = - brcms_b_read_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS); - - /* modify the value */ - pctl1 &= ~PHY_TXC1_MODE_MASK; - pctl1 |= (wlc_hw->hw_stf_ss_opmode << PHY_TXC1_MODE_SHIFT); - - /* Update the SHM Rate Table entry OFDM PCTL1 values */ - brcms_b_write_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS, - pctl1); - } -} - -/* band-specific init */ -static void brcms_b_bsinit(struct brcms_c_info *wlc, u16 chanspec) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - - brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: bandunit %d\n", wlc_hw->unit, - wlc_hw->band->bandunit); - - brcms_c_ucode_bsinit(wlc_hw); - - wlc_phy_init(wlc_hw->band->pi, chanspec); - - brcms_c_ucode_txant_set(wlc_hw); - - /* - * cwmin is band-specific, update hardware - * with value for current band - */ - brcms_b_set_cwmin(wlc_hw, wlc_hw->band->CWmin); - brcms_b_set_cwmax(wlc_hw, wlc_hw->band->CWmax); - - brcms_b_update_slot_timing(wlc_hw, - wlc_hw->band->bandtype == BRCM_BAND_5G ? - true : wlc_hw->shortslot); - - /* write phytype and phyvers */ - brcms_b_write_shm(wlc_hw, M_PHYTYPE, (u16) wlc_hw->band->phytype); - brcms_b_write_shm(wlc_hw, M_PHYVER, (u16) wlc_hw->band->phyrev); - - /* - * initialize the txphyctl1 rate table since - * shmem is shared between bands - */ - brcms_upd_ofdm_pctl1_table(wlc_hw); - - brcms_b_upd_synthpu(wlc_hw); -} - -/* Perform a soft reset of the PHY PLL */ -void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw) -{ - ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_addr), - ~0, 0); - udelay(1); - ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), - 0x4, 0); - udelay(1); - ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), - 0x4, 4); - udelay(1); - ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), - 0x4, 0); - udelay(1); -} - -/* light way to turn on phy clock without reset for NPHY only - * refer to brcms_b_core_phy_clk for full version - */ -void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk) -{ - /* support(necessary for NPHY and HYPHY) only */ - if (!BRCMS_ISNPHY(wlc_hw->band)) - return; - - if (ON == clk) - brcms_b_core_ioctl(wlc_hw, SICF_FGC, SICF_FGC); - else - brcms_b_core_ioctl(wlc_hw, SICF_FGC, 0); - -} - -void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk) -{ - if (ON == clk) - brcms_b_core_ioctl(wlc_hw, SICF_MPCLKE, SICF_MPCLKE); - else - brcms_b_core_ioctl(wlc_hw, SICF_MPCLKE, 0); -} - -void brcms_b_phy_reset(struct brcms_hardware *wlc_hw) -{ - struct brcms_phy_pub *pih = wlc_hw->band->pi; - u32 phy_bw_clkbits; - bool phy_in_reset = false; - - brcms_dbg_info(wlc_hw->d11core, "wl%d: reset phy\n", wlc_hw->unit); - - if (pih == NULL) - return; - - phy_bw_clkbits = wlc_phy_clk_bwbits(wlc_hw->band->pi); - - /* Specific reset sequence required for NPHY rev 3 and 4 */ - if (BRCMS_ISNPHY(wlc_hw->band) && NREV_GE(wlc_hw->band->phyrev, 3) && - NREV_LE(wlc_hw->band->phyrev, 4)) { - /* Set the PHY bandwidth */ - brcms_b_core_ioctl(wlc_hw, SICF_BWMASK, phy_bw_clkbits); - - udelay(1); - - /* Perform a soft reset of the PHY PLL */ - brcms_b_core_phypll_reset(wlc_hw); - - /* reset the PHY */ - brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_PCLKE), - (SICF_PRST | SICF_PCLKE)); - phy_in_reset = true; - } else { - brcms_b_core_ioctl(wlc_hw, - (SICF_PRST | SICF_PCLKE | SICF_BWMASK), - (SICF_PRST | SICF_PCLKE | phy_bw_clkbits)); - } - - udelay(2); - brcms_b_core_phy_clk(wlc_hw, ON); - - if (pih) - wlc_phy_anacore(pih, ON); -} - -/* switch to and initialize new band */ -static void brcms_b_setband(struct brcms_hardware *wlc_hw, uint bandunit, - u16 chanspec) { - struct brcms_c_info *wlc = wlc_hw->wlc; - u32 macintmask; - - /* Enable the d11 core before accessing it */ - if (!bcma_core_is_enabled(wlc_hw->d11core)) { - bcma_core_enable(wlc_hw->d11core, 0); - brcms_c_mctrl_reset(wlc_hw); - } - - macintmask = brcms_c_setband_inact(wlc, bandunit); - - if (!wlc_hw->up) - return; - - brcms_b_core_phy_clk(wlc_hw, ON); - - /* band-specific initializations */ - brcms_b_bsinit(wlc, chanspec); - - /* - * If there are any pending software interrupt bits, - * then replace these with a harmless nonzero value - * so brcms_c_dpc() will re-enable interrupts when done. - */ - if (wlc->macintstatus) - wlc->macintstatus = MI_DMAINT; - - /* restore macintmask */ - brcms_intrsrestore(wlc->wl, macintmask); - - /* ucode should still be suspended.. */ - WARN_ON((bcma_read32(wlc_hw->d11core, D11REGOFFS(maccontrol)) & - MCTL_EN_MAC) != 0); -} - -static bool brcms_c_isgoodchip(struct brcms_hardware *wlc_hw) -{ - - /* reject unsupported corerev */ - if (!CONF_HAS(D11CONF, wlc_hw->corerev)) { - wiphy_err(wlc_hw->wlc->wiphy, "unsupported core rev %d\n", - wlc_hw->corerev); - return false; - } - - return true; -} - -/* Validate some board info parameters */ -static bool brcms_c_validboardtype(struct brcms_hardware *wlc_hw) -{ - uint boardrev = wlc_hw->boardrev; - - /* 4 bits each for board type, major, minor, and tiny version */ - uint brt = (boardrev & 0xf000) >> 12; - uint b0 = (boardrev & 0xf00) >> 8; - uint b1 = (boardrev & 0xf0) >> 4; - uint b2 = boardrev & 0xf; - - /* voards from other vendors are always considered valid */ - if (ai_get_boardvendor(wlc_hw->sih) != PCI_VENDOR_ID_BROADCOM) - return true; - - /* do some boardrev sanity checks when boardvendor is Broadcom */ - if (boardrev == 0) - return false; - - if (boardrev <= 0xff) - return true; - - if ((brt > 2) || (brt == 0) || (b0 > 9) || (b0 == 0) || (b1 > 9) - || (b2 > 9)) - return false; - - return true; -} - -static void brcms_c_get_macaddr(struct brcms_hardware *wlc_hw, u8 etheraddr[ETH_ALEN]) -{ - struct ssb_sprom *sprom = &wlc_hw->d11core->bus->sprom; - - /* If macaddr exists, use it (Sromrev4, CIS, ...). */ - if (!is_zero_ether_addr(sprom->il0mac)) { - memcpy(etheraddr, sprom->il0mac, ETH_ALEN); - return; - } - - if (wlc_hw->_nbands > 1) - memcpy(etheraddr, sprom->et1mac, ETH_ALEN); - else - memcpy(etheraddr, sprom->il0mac, ETH_ALEN); -} - -/* power both the pll and external oscillator on/off */ -static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want) -{ - brcms_dbg_info(wlc_hw->d11core, "wl%d: want %d\n", wlc_hw->unit, want); - - /* - * dont power down if plldown is false or - * we must poll hw radio disable - */ - if (!want && wlc_hw->pllreq) - return; - - wlc_hw->sbclk = want; - if (!wlc_hw->sbclk) { - wlc_hw->clk = false; - if (wlc_hw->band && wlc_hw->band->pi) - wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); - } -} - -/* - * Return true if radio is disabled, otherwise false. - * hw radio disable signal is an external pin, users activate it asynchronously - * this function could be called when driver is down and w/o clock - * it operates on different registers depending on corerev and boardflag. - */ -static bool brcms_b_radio_read_hwdisabled(struct brcms_hardware *wlc_hw) -{ - bool v, clk, xtal; - u32 flags = 0; - - xtal = wlc_hw->sbclk; - if (!xtal) - brcms_b_xtal(wlc_hw, ON); - - /* may need to take core out of reset first */ - clk = wlc_hw->clk; - if (!clk) { - /* - * mac no longer enables phyclk automatically when driver - * accesses phyreg throughput mac. This can be skipped since - * only mac reg is accessed below - */ - if (D11REV_GE(wlc_hw->corerev, 18)) - flags |= SICF_PCLKE; - - /* - * TODO: test suspend/resume - * - * AI chip doesn't restore bar0win2 on - * hibernation/resume, need sw fixup - */ - - bcma_core_enable(wlc_hw->d11core, flags); - brcms_c_mctrl_reset(wlc_hw); - } - - v = ((bcma_read32(wlc_hw->d11core, - D11REGOFFS(phydebug)) & PDBG_RFD) != 0); - - /* put core back into reset */ - if (!clk) - bcma_core_disable(wlc_hw->d11core, 0); - - if (!xtal) - brcms_b_xtal(wlc_hw, OFF); - - return v; -} - -static bool wlc_dma_rxreset(struct brcms_hardware *wlc_hw, uint fifo) -{ - struct dma_pub *di = wlc_hw->di[fifo]; - return dma_rxreset(di); -} - -/* d11 core reset - * ensure fask clock during reset - * reset dma - * reset d11(out of reset) - * reset phy(out of reset) - * clear software macintstatus for fresh new start - * one testing hack wlc_hw->noreset will bypass the d11/phy reset - */ -void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags) -{ - uint i; - bool fastclk; - - if (flags == BRCMS_USE_COREFLAGS) - flags = (wlc_hw->band->pi ? wlc_hw->band->core_flags : 0); - - brcms_dbg_info(wlc_hw->d11core, "wl%d: core reset\n", wlc_hw->unit); - - /* request FAST clock if not on */ - fastclk = wlc_hw->forcefastclk; - if (!fastclk) - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); - - /* reset the dma engines except first time thru */ - if (bcma_core_is_enabled(wlc_hw->d11core)) { - for (i = 0; i < NFIFO; i++) - if ((wlc_hw->di[i]) && (!dma_txreset(wlc_hw->di[i]))) - brcms_err(wlc_hw->d11core, "wl%d: %s: " - "dma_txreset[%d]: cannot stop dma\n", - wlc_hw->unit, __func__, i); - - if ((wlc_hw->di[RX_FIFO]) - && (!wlc_dma_rxreset(wlc_hw, RX_FIFO))) - brcms_err(wlc_hw->d11core, "wl%d: %s: dma_rxreset" - "[%d]: cannot stop dma\n", - wlc_hw->unit, __func__, RX_FIFO); - } - /* if noreset, just stop the psm and return */ - if (wlc_hw->noreset) { - wlc_hw->wlc->macintstatus = 0; /* skip wl_dpc after down */ - brcms_b_mctrl(wlc_hw, MCTL_PSM_RUN | MCTL_EN_MAC, 0); - return; - } - - /* - * mac no longer enables phyclk automatically when driver accesses - * phyreg throughput mac, AND phy_reset is skipped at early stage when - * band->pi is invalid. need to enable PHY CLK - */ - if (D11REV_GE(wlc_hw->corerev, 18)) - flags |= SICF_PCLKE; - - /* - * reset the core - * In chips with PMU, the fastclk request goes through d11 core - * reg 0x1e0, which is cleared by the core_reset. have to re-request it. - * - * This adds some delay and we can optimize it by also requesting - * fastclk through chipcommon during this period if necessary. But - * that has to work coordinate with other driver like mips/arm since - * they may touch chipcommon as well. - */ - wlc_hw->clk = false; - bcma_core_enable(wlc_hw->d11core, flags); - wlc_hw->clk = true; - if (wlc_hw->band && wlc_hw->band->pi) - wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, true); - - brcms_c_mctrl_reset(wlc_hw); - - if (ai_get_cccaps(wlc_hw->sih) & CC_CAP_PMU) - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); - - brcms_b_phy_reset(wlc_hw); - - /* turn on PHY_PLL */ - brcms_b_core_phypll_ctl(wlc_hw, true); - - /* clear sw intstatus */ - wlc_hw->wlc->macintstatus = 0; - - /* restore the clk setting */ - if (!fastclk) - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); -} - -/* txfifo sizes needs to be modified(increased) since the newer cores - * have more memory. - */ -static void brcms_b_corerev_fifofixup(struct brcms_hardware *wlc_hw) -{ - struct bcma_device *core = wlc_hw->d11core; - u16 fifo_nu; - u16 txfifo_startblk = TXFIFO_START_BLK, txfifo_endblk; - u16 txfifo_def, txfifo_def1; - u16 txfifo_cmd; - - /* tx fifos start at TXFIFO_START_BLK from the Base address */ - txfifo_startblk = TXFIFO_START_BLK; - - /* sequence of operations: reset fifo, set fifo size, reset fifo */ - for (fifo_nu = 0; fifo_nu < NFIFO; fifo_nu++) { - - txfifo_endblk = txfifo_startblk + wlc_hw->xmtfifo_sz[fifo_nu]; - txfifo_def = (txfifo_startblk & 0xff) | - (((txfifo_endblk - 1) & 0xff) << TXFIFO_FIFOTOP_SHIFT); - txfifo_def1 = ((txfifo_startblk >> 8) & 0x1) | - ((((txfifo_endblk - - 1) >> 8) & 0x1) << TXFIFO_FIFOTOP_SHIFT); - txfifo_cmd = - TXFIFOCMD_RESET_MASK | (fifo_nu << TXFIFOCMD_FIFOSEL_SHIFT); - - bcma_write16(core, D11REGOFFS(xmtfifocmd), txfifo_cmd); - bcma_write16(core, D11REGOFFS(xmtfifodef), txfifo_def); - bcma_write16(core, D11REGOFFS(xmtfifodef1), txfifo_def1); - - bcma_write16(core, D11REGOFFS(xmtfifocmd), txfifo_cmd); - - txfifo_startblk += wlc_hw->xmtfifo_sz[fifo_nu]; - } - /* - * need to propagate to shm location to be in sync since ucode/hw won't - * do this - */ - brcms_b_write_shm(wlc_hw, M_FIFOSIZE0, - wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]); - brcms_b_write_shm(wlc_hw, M_FIFOSIZE1, - wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]); - brcms_b_write_shm(wlc_hw, M_FIFOSIZE2, - ((wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO] << 8) | wlc_hw-> - xmtfifo_sz[TX_AC_BK_FIFO])); - brcms_b_write_shm(wlc_hw, M_FIFOSIZE3, - ((wlc_hw->xmtfifo_sz[TX_ATIM_FIFO] << 8) | wlc_hw-> - xmtfifo_sz[TX_BCMC_FIFO])); -} - -/* This function is used for changing the tsf frac register - * If spur avoidance mode is off, the mac freq will be 80/120/160Mhz - * If spur avoidance mode is on1, the mac freq will be 82/123/164Mhz - * If spur avoidance mode is on2, the mac freq will be 84/126/168Mhz - * HTPHY Formula is 2^26/freq(MHz) e.g. - * For spuron2 - 126MHz -> 2^26/126 = 532610.0 - * - 532610 = 0x82082 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x2082 - * For spuron: 123MHz -> 2^26/123 = 545600.5 - * - 545601 = 0x85341 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x5341 - * For spur off: 120MHz -> 2^26/120 = 559240.5 - * - 559241 = 0x88889 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x8889 - */ - -void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode) -{ - struct bcma_device *core = wlc_hw->d11core; - - if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43224) || - (ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43225)) { - if (spurmode == WL_SPURAVOID_ON2) { /* 126Mhz */ - bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x2082); - bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); - } else if (spurmode == WL_SPURAVOID_ON1) { /* 123Mhz */ - bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x5341); - bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); - } else { /* 120Mhz */ - bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x8889); - bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); - } - } else if (BRCMS_ISLCNPHY(wlc_hw->band)) { - if (spurmode == WL_SPURAVOID_ON1) { /* 82Mhz */ - bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x7CE0); - bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0xC); - } else { /* 80Mhz */ - bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0xCCCD); - bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0xC); - } - } -} - -void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr) -{ - memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr)); - wlc->bsscfg->type = BRCMS_TYPE_STATION; -} - -void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, const u8 *bssid, - u8 *ssid, size_t ssid_len) -{ - brcms_c_set_ssid(wlc, ssid, ssid_len); - - memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr)); - memcpy(wlc->bsscfg->BSSID, bssid, sizeof(wlc->bsscfg->BSSID)); - wlc->bsscfg->type = BRCMS_TYPE_AP; - - brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, MCTL_AP | MCTL_INFRA); -} - -void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr) -{ - memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr)); - wlc->bsscfg->type = BRCMS_TYPE_ADHOC; - - brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, 0); -} - -/* Initialize GPIOs that are controlled by D11 core */ -static void brcms_c_gpio_init(struct brcms_c_info *wlc) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - u32 gc, gm; - - /* use GPIO select 0 to get all gpio signals from the gpio out reg */ - brcms_b_mctrl(wlc_hw, MCTL_GPOUT_SEL_MASK, 0); - - /* - * Common GPIO setup: - * G0 = LED 0 = WLAN Activity - * G1 = LED 1 = WLAN 2.4 GHz Radio State - * G2 = LED 2 = WLAN 5 GHz Radio State - * G4 = radio disable input (HI enabled, LO disabled) - */ - - gc = gm = 0; - - /* Allocate GPIOs for mimo antenna diversity feature */ - if (wlc_hw->antsel_type == ANTSEL_2x3) { - /* Enable antenna diversity, use 2x3 mode */ - brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN, - MHF3_ANTSEL_EN, BRCM_BAND_ALL); - brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, - MHF3_ANTSEL_MODE, BRCM_BAND_ALL); - - /* init superswitch control */ - wlc_phy_antsel_init(wlc_hw->band->pi, false); - - } else if (wlc_hw->antsel_type == ANTSEL_2x4) { - gm |= gc |= (BOARD_GPIO_12 | BOARD_GPIO_13); - /* - * The board itself is powered by these GPIOs - * (when not sending pattern) so set them high - */ - bcma_set16(wlc_hw->d11core, D11REGOFFS(psm_gpio_oe), - (BOARD_GPIO_12 | BOARD_GPIO_13)); - bcma_set16(wlc_hw->d11core, D11REGOFFS(psm_gpio_out), - (BOARD_GPIO_12 | BOARD_GPIO_13)); - - /* Enable antenna diversity, use 2x4 mode */ - brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN, - MHF3_ANTSEL_EN, BRCM_BAND_ALL); - brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, 0, - BRCM_BAND_ALL); - - /* Configure the desired clock to be 4Mhz */ - brcms_b_write_shm(wlc_hw, M_ANTSEL_CLKDIV, - ANTSEL_CLKDIV_4MHZ); - } - - /* - * gpio 9 controls the PA. ucode is responsible - * for wiggling out and oe - */ - if (wlc_hw->boardflags & BFL_PACTRL) - gm |= gc |= BOARD_GPIO_PACTRL; - - /* apply to gpiocontrol register */ - bcma_chipco_gpio_control(&wlc_hw->d11core->bus->drv_cc, gm, gc); -} - -static void brcms_ucode_write(struct brcms_hardware *wlc_hw, - const __le32 ucode[], const size_t nbytes) -{ - struct bcma_device *core = wlc_hw->d11core; - uint i; - uint count; - - brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); - - count = (nbytes / sizeof(u32)); - - bcma_write32(core, D11REGOFFS(objaddr), - OBJADDR_AUTO_INC | OBJADDR_UCM_SEL); - (void)bcma_read32(core, D11REGOFFS(objaddr)); - for (i = 0; i < count; i++) - bcma_write32(core, D11REGOFFS(objdata), le32_to_cpu(ucode[i])); - -} - -static void brcms_ucode_download(struct brcms_hardware *wlc_hw) -{ - struct brcms_c_info *wlc; - struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; - - wlc = wlc_hw->wlc; - - if (wlc_hw->ucode_loaded) - return; - - if (D11REV_IS(wlc_hw->corerev, 17) || D11REV_IS(wlc_hw->corerev, 23)) { - if (BRCMS_ISNPHY(wlc_hw->band)) { - brcms_ucode_write(wlc_hw, ucode->bcm43xx_16_mimo, - ucode->bcm43xx_16_mimosz); - wlc_hw->ucode_loaded = true; - } else - brcms_err(wlc_hw->d11core, - "%s: wl%d: unsupported phy in corerev %d\n", - __func__, wlc_hw->unit, wlc_hw->corerev); - } else if (D11REV_IS(wlc_hw->corerev, 24)) { - if (BRCMS_ISLCNPHY(wlc_hw->band)) { - brcms_ucode_write(wlc_hw, ucode->bcm43xx_24_lcn, - ucode->bcm43xx_24_lcnsz); - wlc_hw->ucode_loaded = true; - } else { - brcms_err(wlc_hw->d11core, - "%s: wl%d: unsupported phy in corerev %d\n", - __func__, wlc_hw->unit, wlc_hw->corerev); - } - } -} - -void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant) -{ - /* update sw state */ - wlc_hw->bmac_phytxant = phytxant; - - /* push to ucode if up */ - if (!wlc_hw->up) - return; - brcms_c_ucode_txant_set(wlc_hw); - -} - -u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw) -{ - return (u16) wlc_hw->wlc->stf->txant; -} - -void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type) -{ - wlc_hw->antsel_type = antsel_type; - - /* Update the antsel type for phy module to use */ - wlc_phy_antsel_type_set(wlc_hw->band->pi, antsel_type); -} - -static void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw) -{ - bool fatal = false; - uint unit; - uint intstatus, idx; - struct bcma_device *core = wlc_hw->d11core; - - unit = wlc_hw->unit; - - for (idx = 0; idx < NFIFO; idx++) { - /* read intstatus register and ignore any non-error bits */ - intstatus = - bcma_read32(core, - D11REGOFFS(intctrlregs[idx].intstatus)) & - I_ERRORS; - if (!intstatus) - continue; - - brcms_dbg_int(core, "wl%d: intstatus%d 0x%x\n", - unit, idx, intstatus); - - if (intstatus & I_RO) { - brcms_err(core, "wl%d: fifo %d: receive fifo " - "overflow\n", unit, idx); - fatal = true; - } - - if (intstatus & I_PC) { - brcms_err(core, "wl%d: fifo %d: descriptor error\n", - unit, idx); - fatal = true; - } - - if (intstatus & I_PD) { - brcms_err(core, "wl%d: fifo %d: data error\n", unit, - idx); - fatal = true; - } - - if (intstatus & I_DE) { - brcms_err(core, "wl%d: fifo %d: descriptor protocol " - "error\n", unit, idx); - fatal = true; - } - - if (intstatus & I_RU) - brcms_err(core, "wl%d: fifo %d: receive descriptor " - "underflow\n", idx, unit); - - if (intstatus & I_XU) { - brcms_err(core, "wl%d: fifo %d: transmit fifo " - "underflow\n", idx, unit); - fatal = true; - } - - if (fatal) { - brcms_fatal_error(wlc_hw->wlc->wl); /* big hammer */ - break; - } else - bcma_write32(core, - D11REGOFFS(intctrlregs[idx].intstatus), - intstatus); - } -} - -void brcms_c_intrson(struct brcms_c_info *wlc) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - wlc->macintmask = wlc->defmacintmask; - bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), wlc->macintmask); -} - -u32 brcms_c_intrsoff(struct brcms_c_info *wlc) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - u32 macintmask; - - if (!wlc_hw->clk) - return 0; - - macintmask = wlc->macintmask; /* isr can still happen */ - - bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), 0); - (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(macintmask)); - udelay(1); /* ensure int line is no longer driven */ - wlc->macintmask = 0; - - /* return previous macintmask; resolve race between us and our isr */ - return wlc->macintstatus ? 0 : macintmask; -} - -void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - if (!wlc_hw->clk) - return; - - wlc->macintmask = macintmask; - bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), wlc->macintmask); -} - -/* assumes that the d11 MAC is enabled */ -static void brcms_b_tx_fifo_suspend(struct brcms_hardware *wlc_hw, - uint tx_fifo) -{ - u8 fifo = 1 << tx_fifo; - - /* Two clients of this code, 11h Quiet period and scanning. */ - - /* only suspend if not already suspended */ - if ((wlc_hw->suspended_fifos & fifo) == fifo) - return; - - /* force the core awake only if not already */ - if (wlc_hw->suspended_fifos == 0) - brcms_c_ucode_wake_override_set(wlc_hw, - BRCMS_WAKE_OVERRIDE_TXFIFO); - - wlc_hw->suspended_fifos |= fifo; - - if (wlc_hw->di[tx_fifo]) { - /* - * Suspending AMPDU transmissions in the middle can cause - * underflow which may result in mismatch between ucode and - * driver so suspend the mac before suspending the FIFO - */ - if (BRCMS_PHY_11N_CAP(wlc_hw->band)) - brcms_c_suspend_mac_and_wait(wlc_hw->wlc); - - dma_txsuspend(wlc_hw->di[tx_fifo]); - - if (BRCMS_PHY_11N_CAP(wlc_hw->band)) - brcms_c_enable_mac(wlc_hw->wlc); - } -} - -static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw, - uint tx_fifo) -{ - /* BMAC_NOTE: BRCMS_TX_FIFO_ENAB is done in brcms_c_dpc() for DMA case - * but need to be done here for PIO otherwise the watchdog will catch - * the inconsistency and fire - */ - /* Two clients of this code, 11h Quiet period and scanning. */ - if (wlc_hw->di[tx_fifo]) - dma_txresume(wlc_hw->di[tx_fifo]); - - /* allow core to sleep again */ - if (wlc_hw->suspended_fifos == 0) - return; - else { - wlc_hw->suspended_fifos &= ~(1 << tx_fifo); - if (wlc_hw->suspended_fifos == 0) - brcms_c_ucode_wake_override_clear(wlc_hw, - BRCMS_WAKE_OVERRIDE_TXFIFO); - } -} - -/* precondition: requires the mac core to be enabled */ -static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx) -{ - static const u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; - u8 *ethaddr = wlc_hw->wlc->pub->cur_etheraddr; - - if (mute_tx) { - /* suspend tx fifos */ - brcms_b_tx_fifo_suspend(wlc_hw, TX_DATA_FIFO); - brcms_b_tx_fifo_suspend(wlc_hw, TX_CTL_FIFO); - brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_BK_FIFO); - brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_VI_FIFO); - - /* zero the address match register so we do not send ACKs */ - brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, null_ether_addr); - } else { - /* resume tx fifos */ - brcms_b_tx_fifo_resume(wlc_hw, TX_DATA_FIFO); - brcms_b_tx_fifo_resume(wlc_hw, TX_CTL_FIFO); - brcms_b_tx_fifo_resume(wlc_hw, TX_AC_BK_FIFO); - brcms_b_tx_fifo_resume(wlc_hw, TX_AC_VI_FIFO); - - /* Restore address */ - brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, ethaddr); - } - - wlc_phy_mute_upd(wlc_hw->band->pi, mute_tx, 0); - - if (mute_tx) - brcms_c_ucode_mute_override_set(wlc_hw); - else - brcms_c_ucode_mute_override_clear(wlc_hw); -} - -void -brcms_c_mute(struct brcms_c_info *wlc, bool mute_tx) -{ - brcms_b_mute(wlc->hw, mute_tx); -} - -/* - * Read and clear macintmask and macintstatus and intstatus registers. - * This routine should be called with interrupts off - * Return: - * -1 if brcms_deviceremoved(wlc) evaluates to true; - * 0 if the interrupt is not for us, or we are in some special cases; - * device interrupt status bits otherwise. - */ -static inline u32 wlc_intstatus(struct brcms_c_info *wlc, bool in_isr) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - struct bcma_device *core = wlc_hw->d11core; - u32 macintstatus, mask; - - /* macintstatus includes a DMA interrupt summary bit */ - macintstatus = bcma_read32(core, D11REGOFFS(macintstatus)); - mask = in_isr ? wlc->macintmask : wlc->defmacintmask; - - trace_brcms_macintstatus(&core->dev, in_isr, macintstatus, mask); - - /* detect cardbus removed, in power down(suspend) and in reset */ - if (brcms_deviceremoved(wlc)) - return -1; - - /* brcms_deviceremoved() succeeds even when the core is still resetting, - * handle that case here. - */ - if (macintstatus == 0xffffffff) - return 0; - - /* defer unsolicited interrupts */ - macintstatus &= mask; - - /* if not for us */ - if (macintstatus == 0) - return 0; - - /* turn off the interrupts */ - bcma_write32(core, D11REGOFFS(macintmask), 0); - (void)bcma_read32(core, D11REGOFFS(macintmask)); - wlc->macintmask = 0; - - /* clear device interrupts */ - bcma_write32(core, D11REGOFFS(macintstatus), macintstatus); - - /* MI_DMAINT is indication of non-zero intstatus */ - if (macintstatus & MI_DMAINT) - /* - * only fifo interrupt enabled is I_RI in - * RX_FIFO. If MI_DMAINT is set, assume it - * is set and clear the interrupt. - */ - bcma_write32(core, D11REGOFFS(intctrlregs[RX_FIFO].intstatus), - DEF_RXINTMASK); - - return macintstatus; -} - -/* Update wlc->macintstatus and wlc->intstatus[]. */ -/* Return true if they are updated successfully. false otherwise */ -bool brcms_c_intrsupd(struct brcms_c_info *wlc) -{ - u32 macintstatus; - - /* read and clear macintstatus and intstatus registers */ - macintstatus = wlc_intstatus(wlc, false); - - /* device is removed */ - if (macintstatus == 0xffffffff) - return false; - - /* update interrupt status in software */ - wlc->macintstatus |= macintstatus; - - return true; -} - -/* - * First-level interrupt processing. - * Return true if this was our interrupt - * and if further brcms_c_dpc() processing is required, - * false otherwise. - */ -bool brcms_c_isr(struct brcms_c_info *wlc) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - u32 macintstatus; - - if (!wlc_hw->up || !wlc->macintmask) - return false; - - /* read and clear macintstatus and intstatus registers */ - macintstatus = wlc_intstatus(wlc, true); - - if (macintstatus == 0xffffffff) { - brcms_err(wlc_hw->d11core, - "DEVICEREMOVED detected in the ISR code path\n"); - return false; - } - - /* it is not for us */ - if (macintstatus == 0) - return false; - - /* save interrupt status bits */ - wlc->macintstatus = macintstatus; - - return true; - -} - -void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - struct bcma_device *core = wlc_hw->d11core; - u32 mc, mi; - - brcms_dbg_mac80211(core, "wl%d: bandunit %d\n", wlc_hw->unit, - wlc_hw->band->bandunit); - - /* - * Track overlapping suspend requests - */ - wlc_hw->mac_suspend_depth++; - if (wlc_hw->mac_suspend_depth > 1) - return; - - /* force the core awake */ - brcms_c_ucode_wake_override_set(wlc_hw, BRCMS_WAKE_OVERRIDE_MACSUSPEND); - - mc = bcma_read32(core, D11REGOFFS(maccontrol)); - - if (mc == 0xffffffff) { - brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, - __func__); - brcms_down(wlc->wl); - return; - } - WARN_ON(mc & MCTL_PSM_JMP_0); - WARN_ON(!(mc & MCTL_PSM_RUN)); - WARN_ON(!(mc & MCTL_EN_MAC)); - - mi = bcma_read32(core, D11REGOFFS(macintstatus)); - if (mi == 0xffffffff) { - brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, - __func__); - brcms_down(wlc->wl); - return; - } - WARN_ON(mi & MI_MACSSPNDD); - - brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, 0); - - SPINWAIT(!(bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD), - BRCMS_MAX_MAC_SUSPEND); - - if (!(bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD)) { - brcms_err(core, "wl%d: wlc_suspend_mac_and_wait: waited %d uS" - " and MI_MACSSPNDD is still not on.\n", - wlc_hw->unit, BRCMS_MAX_MAC_SUSPEND); - brcms_err(core, "wl%d: psmdebug 0x%08x, phydebug 0x%08x, " - "psm_brc 0x%04x\n", wlc_hw->unit, - bcma_read32(core, D11REGOFFS(psmdebug)), - bcma_read32(core, D11REGOFFS(phydebug)), - bcma_read16(core, D11REGOFFS(psm_brc))); - } - - mc = bcma_read32(core, D11REGOFFS(maccontrol)); - if (mc == 0xffffffff) { - brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, - __func__); - brcms_down(wlc->wl); - return; - } - WARN_ON(mc & MCTL_PSM_JMP_0); - WARN_ON(!(mc & MCTL_PSM_RUN)); - WARN_ON(mc & MCTL_EN_MAC); -} - -void brcms_c_enable_mac(struct brcms_c_info *wlc) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - struct bcma_device *core = wlc_hw->d11core; - u32 mc, mi; - - brcms_dbg_mac80211(core, "wl%d: bandunit %d\n", wlc_hw->unit, - wlc->band->bandunit); - - /* - * Track overlapping suspend requests - */ - wlc_hw->mac_suspend_depth--; - if (wlc_hw->mac_suspend_depth > 0) - return; - - mc = bcma_read32(core, D11REGOFFS(maccontrol)); - WARN_ON(mc & MCTL_PSM_JMP_0); - WARN_ON(mc & MCTL_EN_MAC); - WARN_ON(!(mc & MCTL_PSM_RUN)); - - brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, MCTL_EN_MAC); - bcma_write32(core, D11REGOFFS(macintstatus), MI_MACSSPNDD); - - mc = bcma_read32(core, D11REGOFFS(maccontrol)); - WARN_ON(mc & MCTL_PSM_JMP_0); - WARN_ON(!(mc & MCTL_EN_MAC)); - WARN_ON(!(mc & MCTL_PSM_RUN)); - - mi = bcma_read32(core, D11REGOFFS(macintstatus)); - WARN_ON(mi & MI_MACSSPNDD); - - brcms_c_ucode_wake_override_clear(wlc_hw, - BRCMS_WAKE_OVERRIDE_MACSUSPEND); -} - -void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode) -{ - wlc_hw->hw_stf_ss_opmode = stf_mode; - - if (wlc_hw->clk) - brcms_upd_ofdm_pctl1_table(wlc_hw); -} - -static bool brcms_b_validate_chip_access(struct brcms_hardware *wlc_hw) -{ - struct bcma_device *core = wlc_hw->d11core; - u32 w, val; - struct wiphy *wiphy = wlc_hw->wlc->wiphy; - - /* Validate dchip register access */ - - bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); - (void)bcma_read32(core, D11REGOFFS(objaddr)); - w = bcma_read32(core, D11REGOFFS(objdata)); - - /* Can we write and read back a 32bit register? */ - bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); - (void)bcma_read32(core, D11REGOFFS(objaddr)); - bcma_write32(core, D11REGOFFS(objdata), (u32) 0xaa5555aa); - - bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); - (void)bcma_read32(core, D11REGOFFS(objaddr)); - val = bcma_read32(core, D11REGOFFS(objdata)); - if (val != (u32) 0xaa5555aa) { - wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, " - "expected 0xaa5555aa\n", wlc_hw->unit, val); - return false; - } - - bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); - (void)bcma_read32(core, D11REGOFFS(objaddr)); - bcma_write32(core, D11REGOFFS(objdata), (u32) 0x55aaaa55); - - bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); - (void)bcma_read32(core, D11REGOFFS(objaddr)); - val = bcma_read32(core, D11REGOFFS(objdata)); - if (val != (u32) 0x55aaaa55) { - wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, " - "expected 0x55aaaa55\n", wlc_hw->unit, val); - return false; - } - - bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); - (void)bcma_read32(core, D11REGOFFS(objaddr)); - bcma_write32(core, D11REGOFFS(objdata), w); - - /* clear CFPStart */ - bcma_write32(core, D11REGOFFS(tsf_cfpstart), 0); - - w = bcma_read32(core, D11REGOFFS(maccontrol)); - if ((w != (MCTL_IHR_EN | MCTL_WAKE)) && - (w != (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE))) { - wiphy_err(wiphy, "wl%d: validate_chip_access: maccontrol = " - "0x%x, expected 0x%x or 0x%x\n", wlc_hw->unit, w, - (MCTL_IHR_EN | MCTL_WAKE), - (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE)); - return false; - } - - return true; -} - -#define PHYPLL_WAIT_US 100000 - -void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on) -{ - struct bcma_device *core = wlc_hw->d11core; - u32 tmp; - - brcms_dbg_info(core, "wl%d\n", wlc_hw->unit); - - tmp = 0; - - if (on) { - if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM4313)) { - bcma_set32(core, D11REGOFFS(clk_ctl_st), - CCS_ERSRC_REQ_HT | - CCS_ERSRC_REQ_D11PLL | - CCS_ERSRC_REQ_PHYPLL); - SPINWAIT((bcma_read32(core, D11REGOFFS(clk_ctl_st)) & - CCS_ERSRC_AVAIL_HT) != CCS_ERSRC_AVAIL_HT, - PHYPLL_WAIT_US); - - tmp = bcma_read32(core, D11REGOFFS(clk_ctl_st)); - if ((tmp & CCS_ERSRC_AVAIL_HT) != CCS_ERSRC_AVAIL_HT) - brcms_err(core, "%s: turn on PHY PLL failed\n", - __func__); - } else { - bcma_set32(core, D11REGOFFS(clk_ctl_st), - tmp | CCS_ERSRC_REQ_D11PLL | - CCS_ERSRC_REQ_PHYPLL); - SPINWAIT((bcma_read32(core, D11REGOFFS(clk_ctl_st)) & - (CCS_ERSRC_AVAIL_D11PLL | - CCS_ERSRC_AVAIL_PHYPLL)) != - (CCS_ERSRC_AVAIL_D11PLL | - CCS_ERSRC_AVAIL_PHYPLL), PHYPLL_WAIT_US); - - tmp = bcma_read32(core, D11REGOFFS(clk_ctl_st)); - if ((tmp & - (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) - != - (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) - brcms_err(core, "%s: turn on PHY PLL failed\n", - __func__); - } - } else { - /* - * Since the PLL may be shared, other cores can still - * be requesting it; so we'll deassert the request but - * not wait for status to comply. - */ - bcma_mask32(core, D11REGOFFS(clk_ctl_st), - ~CCS_ERSRC_REQ_PHYPLL); - (void)bcma_read32(core, D11REGOFFS(clk_ctl_st)); - } -} - -static void brcms_c_coredisable(struct brcms_hardware *wlc_hw) -{ - bool dev_gone; - - brcms_dbg_info(wlc_hw->d11core, "wl%d: disable core\n", wlc_hw->unit); - - dev_gone = brcms_deviceremoved(wlc_hw->wlc); - - if (dev_gone) - return; - - if (wlc_hw->noreset) - return; - - /* radio off */ - wlc_phy_switch_radio(wlc_hw->band->pi, OFF); - - /* turn off analog core */ - wlc_phy_anacore(wlc_hw->band->pi, OFF); - - /* turn off PHYPLL to save power */ - brcms_b_core_phypll_ctl(wlc_hw, false); - - wlc_hw->clk = false; - bcma_core_disable(wlc_hw->d11core, 0); - wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); -} - -static void brcms_c_flushqueues(struct brcms_c_info *wlc) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - uint i; - - /* free any posted tx packets */ - for (i = 0; i < NFIFO; i++) { - if (wlc_hw->di[i]) { - dma_txreclaim(wlc_hw->di[i], DMA_RANGE_ALL); - if (i < TX_BCMC_FIFO) - ieee80211_wake_queue(wlc->pub->ieee_hw, - brcms_fifo_to_ac(i)); - } - } - - /* free any posted rx packets */ - dma_rxreclaim(wlc_hw->di[RX_FIFO]); -} - -static u16 -brcms_b_read_objmem(struct brcms_hardware *wlc_hw, uint offset, u32 sel) -{ - struct bcma_device *core = wlc_hw->d11core; - u16 objoff = D11REGOFFS(objdata); - - bcma_write32(core, D11REGOFFS(objaddr), sel | (offset >> 2)); - (void)bcma_read32(core, D11REGOFFS(objaddr)); - if (offset & 2) - objoff += 2; - - return bcma_read16(core, objoff); -} - -static void -brcms_b_write_objmem(struct brcms_hardware *wlc_hw, uint offset, u16 v, - u32 sel) -{ - struct bcma_device *core = wlc_hw->d11core; - u16 objoff = D11REGOFFS(objdata); - - bcma_write32(core, D11REGOFFS(objaddr), sel | (offset >> 2)); - (void)bcma_read32(core, D11REGOFFS(objaddr)); - if (offset & 2) - objoff += 2; - - bcma_wflush16(core, objoff, v); -} - -/* - * Read a single u16 from shared memory. - * SHM 'offset' needs to be an even address - */ -u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset) -{ - return brcms_b_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL); -} - -/* - * Write a single u16 to shared memory. - * SHM 'offset' needs to be an even address - */ -void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v) -{ - brcms_b_write_objmem(wlc_hw, offset, v, OBJADDR_SHM_SEL); -} - -/* - * Copy a buffer to shared memory of specified type . - * SHM 'offset' needs to be an even address and - * Buffer length 'len' must be an even number of bytes - * 'sel' selects the type of memory - */ -void -brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset, - const void *buf, int len, u32 sel) -{ - u16 v; - const u8 *p = (const u8 *)buf; - int i; - - if (len <= 0 || (offset & 1) || (len & 1)) - return; - - for (i = 0; i < len; i += 2) { - v = p[i] | (p[i + 1] << 8); - brcms_b_write_objmem(wlc_hw, offset + i, v, sel); - } -} - -/* - * Copy a piece of shared memory of specified type to a buffer . - * SHM 'offset' needs to be an even address and - * Buffer length 'len' must be an even number of bytes - * 'sel' selects the type of memory - */ -void -brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, void *buf, - int len, u32 sel) -{ - u16 v; - u8 *p = (u8 *) buf; - int i; - - if (len <= 0 || (offset & 1) || (len & 1)) - return; - - for (i = 0; i < len; i += 2) { - v = brcms_b_read_objmem(wlc_hw, offset + i, sel); - p[i] = v & 0xFF; - p[i + 1] = (v >> 8) & 0xFF; - } -} - -/* Copy a buffer to shared memory. - * SHM 'offset' needs to be an even address and - * Buffer length 'len' must be an even number of bytes - */ -static void brcms_c_copyto_shm(struct brcms_c_info *wlc, uint offset, - const void *buf, int len) -{ - brcms_b_copyto_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL); -} - -static void brcms_b_retrylimit_upd(struct brcms_hardware *wlc_hw, - u16 SRL, u16 LRL) -{ - wlc_hw->SRL = SRL; - wlc_hw->LRL = LRL; - - /* write retry limit to SCR, shouldn't need to suspend */ - if (wlc_hw->up) { - bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), - OBJADDR_SCR_SEL | S_DOT11_SRC_LMT); - (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); - bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), wlc_hw->SRL); - bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), - OBJADDR_SCR_SEL | S_DOT11_LRC_LMT); - (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); - bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), wlc_hw->LRL); - } -} - -static void brcms_b_pllreq(struct brcms_hardware *wlc_hw, bool set, u32 req_bit) -{ - if (set) { - if (mboolisset(wlc_hw->pllreq, req_bit)) - return; - - mboolset(wlc_hw->pllreq, req_bit); - - if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) { - if (!wlc_hw->sbclk) - brcms_b_xtal(wlc_hw, ON); - } - } else { - if (!mboolisset(wlc_hw->pllreq, req_bit)) - return; - - mboolclr(wlc_hw->pllreq, req_bit); - - if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) { - if (wlc_hw->sbclk) - brcms_b_xtal(wlc_hw, OFF); - } - } -} - -static void brcms_b_antsel_set(struct brcms_hardware *wlc_hw, u32 antsel_avail) -{ - wlc_hw->antsel_avail = antsel_avail; -} - -/* - * conditions under which the PM bit should be set in outgoing frames - * and STAY_AWAKE is meaningful - */ -static bool brcms_c_ps_allowed(struct brcms_c_info *wlc) -{ - /* not supporting PS so always return false for now */ - return false; -} - -static void brcms_c_statsupd(struct brcms_c_info *wlc) -{ - int i; - struct macstat *macstats; -#ifdef DEBUG - u16 delta; - u16 rxf0ovfl; - u16 txfunfl[NFIFO]; -#endif /* DEBUG */ - - /* if driver down, make no sense to update stats */ - if (!wlc->pub->up) - return; - - macstats = wlc->core->macstat_snapshot; - -#ifdef DEBUG - /* save last rx fifo 0 overflow count */ - rxf0ovfl = macstats->rxf0ovfl; - - /* save last tx fifo underflow count */ - for (i = 0; i < NFIFO; i++) - txfunfl[i] = macstats->txfunfl[i]; -#endif /* DEBUG */ - - /* Read mac stats from contiguous shared memory */ - brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, macstats, - sizeof(*macstats), OBJADDR_SHM_SEL); - -#ifdef DEBUG - /* check for rx fifo 0 overflow */ - delta = (u16)(macstats->rxf0ovfl - rxf0ovfl); - if (delta) - brcms_err(wlc->hw->d11core, "wl%d: %u rx fifo 0 overflows!\n", - wlc->pub->unit, delta); - - /* check for tx fifo underflows */ - for (i = 0; i < NFIFO; i++) { - delta = macstats->txfunfl[i] - txfunfl[i]; - if (delta) - brcms_err(wlc->hw->d11core, - "wl%d: %u tx fifo %d underflows!\n", - wlc->pub->unit, delta, i); - } -#endif /* DEBUG */ - - /* merge counters from dma module */ - for (i = 0; i < NFIFO; i++) { - if (wlc->hw->di[i]) - dma_counterreset(wlc->hw->di[i]); - } -} - -static void brcms_b_reset(struct brcms_hardware *wlc_hw) -{ - /* reset the core */ - if (!brcms_deviceremoved(wlc_hw->wlc)) - brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); - - /* purge the dma rings */ - brcms_c_flushqueues(wlc_hw->wlc); -} - -void brcms_c_reset(struct brcms_c_info *wlc) -{ - brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); - - /* slurp up hw mac counters before core reset */ - brcms_c_statsupd(wlc); - - /* reset our snapshot of macstat counters */ - memset(wlc->core->macstat_snapshot, 0, sizeof(struct macstat)); - - brcms_b_reset(wlc->hw); -} - -void brcms_c_init_scb(struct scb *scb) -{ - int i; - - memset(scb, 0, sizeof(struct scb)); - scb->flags = SCB_WMECAP | SCB_HTCAP; - for (i = 0; i < NUMPRIO; i++) { - scb->seqnum[i] = 0; - scb->seqctl[i] = 0xFFFF; - } - - scb->seqctl_nonqos = 0xFFFF; - scb->magic = SCB_MAGIC; -} - -/* d11 core init - * reset PSM - * download ucode/PCM - * let ucode run to suspended - * download ucode inits - * config other core registers - * init dma - */ -static void brcms_b_coreinit(struct brcms_c_info *wlc) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - struct bcma_device *core = wlc_hw->d11core; - u32 sflags; - u32 bcnint_us; - uint i = 0; - bool fifosz_fixup = false; - int err = 0; - u16 buf[NFIFO]; - struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; - - brcms_dbg_info(core, "wl%d: core init\n", wlc_hw->unit); - - /* reset PSM */ - brcms_b_mctrl(wlc_hw, ~0, (MCTL_IHR_EN | MCTL_PSM_JMP_0 | MCTL_WAKE)); - - brcms_ucode_download(wlc_hw); - /* - * FIFOSZ fixup. driver wants to controls the fifo allocation. - */ - fifosz_fixup = true; - - /* let the PSM run to the suspended state, set mode to BSS STA */ - bcma_write32(core, D11REGOFFS(macintstatus), -1); - brcms_b_mctrl(wlc_hw, ~0, - (MCTL_IHR_EN | MCTL_INFRA | MCTL_PSM_RUN | MCTL_WAKE)); - - /* wait for ucode to self-suspend after auto-init */ - SPINWAIT(((bcma_read32(core, D11REGOFFS(macintstatus)) & - MI_MACSSPNDD) == 0), 1000 * 1000); - if ((bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD) == 0) - brcms_err(core, "wl%d: wlc_coreinit: ucode did not self-" - "suspend!\n", wlc_hw->unit); - - brcms_c_gpio_init(wlc); - - sflags = bcma_aread32(core, BCMA_IOST); - - if (D11REV_IS(wlc_hw->corerev, 17) || D11REV_IS(wlc_hw->corerev, 23)) { - if (BRCMS_ISNPHY(wlc_hw->band)) - brcms_c_write_inits(wlc_hw, ucode->d11n0initvals16); - else - brcms_err(core, "%s: wl%d: unsupported phy in corerev" - " %d\n", __func__, wlc_hw->unit, - wlc_hw->corerev); - } else if (D11REV_IS(wlc_hw->corerev, 24)) { - if (BRCMS_ISLCNPHY(wlc_hw->band)) - brcms_c_write_inits(wlc_hw, ucode->d11lcn0initvals24); - else - brcms_err(core, "%s: wl%d: unsupported phy in corerev" - " %d\n", __func__, wlc_hw->unit, - wlc_hw->corerev); - } else { - brcms_err(core, "%s: wl%d: unsupported corerev %d\n", - __func__, wlc_hw->unit, wlc_hw->corerev); - } - - /* For old ucode, txfifo sizes needs to be modified(increased) */ - if (fifosz_fixup) - brcms_b_corerev_fifofixup(wlc_hw); - - /* check txfifo allocations match between ucode and driver */ - buf[TX_AC_BE_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE0); - if (buf[TX_AC_BE_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]) { - i = TX_AC_BE_FIFO; - err = -1; - } - buf[TX_AC_VI_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE1); - if (buf[TX_AC_VI_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]) { - i = TX_AC_VI_FIFO; - err = -1; - } - buf[TX_AC_BK_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE2); - buf[TX_AC_VO_FIFO] = (buf[TX_AC_BK_FIFO] >> 8) & 0xff; - buf[TX_AC_BK_FIFO] &= 0xff; - if (buf[TX_AC_BK_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BK_FIFO]) { - i = TX_AC_BK_FIFO; - err = -1; - } - if (buf[TX_AC_VO_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO]) { - i = TX_AC_VO_FIFO; - err = -1; - } - buf[TX_BCMC_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE3); - buf[TX_ATIM_FIFO] = (buf[TX_BCMC_FIFO] >> 8) & 0xff; - buf[TX_BCMC_FIFO] &= 0xff; - if (buf[TX_BCMC_FIFO] != wlc_hw->xmtfifo_sz[TX_BCMC_FIFO]) { - i = TX_BCMC_FIFO; - err = -1; - } - if (buf[TX_ATIM_FIFO] != wlc_hw->xmtfifo_sz[TX_ATIM_FIFO]) { - i = TX_ATIM_FIFO; - err = -1; - } - if (err != 0) - brcms_err(core, "wlc_coreinit: txfifo mismatch: ucode size %d" - " driver size %d index %d\n", buf[i], - wlc_hw->xmtfifo_sz[i], i); - - /* make sure we can still talk to the mac */ - WARN_ON(bcma_read32(core, D11REGOFFS(maccontrol)) == 0xffffffff); - - /* band-specific inits done by wlc_bsinit() */ - - /* Set up frame burst size and antenna swap threshold init values */ - brcms_b_write_shm(wlc_hw, M_MBURST_SIZE, MAXTXFRAMEBURST); - brcms_b_write_shm(wlc_hw, M_MAX_ANTCNT, ANTCNT); - - /* enable one rx interrupt per received frame */ - bcma_write32(core, D11REGOFFS(intrcvlazy[0]), (1 << IRL_FC_SHIFT)); - - /* set the station mode (BSS STA) */ - brcms_b_mctrl(wlc_hw, - (MCTL_INFRA | MCTL_DISCARD_PMQ | MCTL_AP), - (MCTL_INFRA | MCTL_DISCARD_PMQ)); - - /* set up Beacon interval */ - bcnint_us = 0x8000 << 10; - bcma_write32(core, D11REGOFFS(tsf_cfprep), - (bcnint_us << CFPREP_CBI_SHIFT)); - bcma_write32(core, D11REGOFFS(tsf_cfpstart), bcnint_us); - bcma_write32(core, D11REGOFFS(macintstatus), MI_GP1); - - /* write interrupt mask */ - bcma_write32(core, D11REGOFFS(intctrlregs[RX_FIFO].intmask), - DEF_RXINTMASK); - - /* allow the MAC to control the PHY clock (dynamic on/off) */ - brcms_b_macphyclk_set(wlc_hw, ON); - - /* program dynamic clock control fast powerup delay register */ - wlc->fastpwrup_dly = ai_clkctl_fast_pwrup_delay(wlc_hw->sih); - bcma_write16(core, D11REGOFFS(scc_fastpwrup_dly), wlc->fastpwrup_dly); - - /* tell the ucode the corerev */ - brcms_b_write_shm(wlc_hw, M_MACHW_VER, (u16) wlc_hw->corerev); - - /* tell the ucode MAC capabilities */ - brcms_b_write_shm(wlc_hw, M_MACHW_CAP_L, - (u16) (wlc_hw->machwcap & 0xffff)); - brcms_b_write_shm(wlc_hw, M_MACHW_CAP_H, - (u16) ((wlc_hw-> - machwcap >> 16) & 0xffff)); - - /* write retry limits to SCR, this done after PSM init */ - bcma_write32(core, D11REGOFFS(objaddr), - OBJADDR_SCR_SEL | S_DOT11_SRC_LMT); - (void)bcma_read32(core, D11REGOFFS(objaddr)); - bcma_write32(core, D11REGOFFS(objdata), wlc_hw->SRL); - bcma_write32(core, D11REGOFFS(objaddr), - OBJADDR_SCR_SEL | S_DOT11_LRC_LMT); - (void)bcma_read32(core, D11REGOFFS(objaddr)); - bcma_write32(core, D11REGOFFS(objdata), wlc_hw->LRL); - - /* write rate fallback retry limits */ - brcms_b_write_shm(wlc_hw, M_SFRMTXCNTFBRTHSD, wlc_hw->SFBL); - brcms_b_write_shm(wlc_hw, M_LFRMTXCNTFBRTHSD, wlc_hw->LFBL); - - bcma_mask16(core, D11REGOFFS(ifs_ctl), 0x0FFF); - bcma_write16(core, D11REGOFFS(ifs_aifsn), EDCF_AIFSN_MIN); - - /* init the tx dma engines */ - for (i = 0; i < NFIFO; i++) { - if (wlc_hw->di[i]) - dma_txinit(wlc_hw->di[i]); - } - - /* init the rx dma engine(s) and post receive buffers */ - dma_rxinit(wlc_hw->di[RX_FIFO]); - dma_rxfill(wlc_hw->di[RX_FIFO]); -} - -void -static brcms_b_init(struct brcms_hardware *wlc_hw, u16 chanspec) { - u32 macintmask; - bool fastclk; - struct brcms_c_info *wlc = wlc_hw->wlc; - - /* request FAST clock if not on */ - fastclk = wlc_hw->forcefastclk; - if (!fastclk) - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); - - /* disable interrupts */ - macintmask = brcms_intrsoff(wlc->wl); - - /* set up the specified band and chanspec */ - brcms_c_setxband(wlc_hw, chspec_bandunit(chanspec)); - wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec); - - /* do one-time phy inits and calibration */ - wlc_phy_cal_init(wlc_hw->band->pi); - - /* core-specific initialization */ - brcms_b_coreinit(wlc); - - /* band-specific inits */ - brcms_b_bsinit(wlc, chanspec); - - /* restore macintmask */ - brcms_intrsrestore(wlc->wl, macintmask); - - /* seed wake_override with BRCMS_WAKE_OVERRIDE_MACSUSPEND since the mac - * is suspended and brcms_c_enable_mac() will clear this override bit. - */ - mboolset(wlc_hw->wake_override, BRCMS_WAKE_OVERRIDE_MACSUSPEND); - - /* - * initialize mac_suspend_depth to 1 to match ucode - * initial suspended state - */ - wlc_hw->mac_suspend_depth = 1; - - /* restore the clk */ - if (!fastclk) - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); -} - -static void brcms_c_set_phy_chanspec(struct brcms_c_info *wlc, - u16 chanspec) -{ - /* Save our copy of the chanspec */ - wlc->chanspec = chanspec; - - /* Set the chanspec and power limits for this locale */ - brcms_c_channel_set_chanspec(wlc->cmi, chanspec, BRCMS_TXPWR_MAX); - - if (wlc->stf->ss_algosel_auto) - brcms_c_stf_ss_algo_channel_get(wlc, &wlc->stf->ss_algo_channel, - chanspec); - - brcms_c_stf_ss_update(wlc, wlc->band); -} - -static void -brcms_default_rateset(struct brcms_c_info *wlc, struct brcms_c_rateset *rs) -{ - brcms_c_rateset_default(rs, NULL, wlc->band->phytype, - wlc->band->bandtype, false, BRCMS_RATE_MASK_FULL, - (bool) (wlc->pub->_n_enab & SUPPORT_11N), - brcms_chspec_bw(wlc->default_bss->chanspec), - wlc->stf->txstreams); -} - -/* derive wlc->band->basic_rate[] table from 'rateset' */ -static void brcms_c_rate_lookup_init(struct brcms_c_info *wlc, - struct brcms_c_rateset *rateset) -{ - u8 rate; - u8 mandatory; - u8 cck_basic = 0; - u8 ofdm_basic = 0; - u8 *br = wlc->band->basic_rate; - uint i; - - /* incoming rates are in 500kbps units as in 802.11 Supported Rates */ - memset(br, 0, BRCM_MAXRATE + 1); - - /* For each basic rate in the rates list, make an entry in the - * best basic lookup. - */ - for (i = 0; i < rateset->count; i++) { - /* only make an entry for a basic rate */ - if (!(rateset->rates[i] & BRCMS_RATE_FLAG)) - continue; - - /* mask off basic bit */ - rate = (rateset->rates[i] & BRCMS_RATE_MASK); - - if (rate > BRCM_MAXRATE) { - brcms_err(wlc->hw->d11core, "brcms_c_rate_lookup_init: " - "invalid rate 0x%X in rate set\n", - rateset->rates[i]); - continue; - } - - br[rate] = rate; - } - - /* The rate lookup table now has non-zero entries for each - * basic rate, equal to the basic rate: br[basicN] = basicN - * - * To look up the best basic rate corresponding to any - * particular rate, code can use the basic_rate table - * like this - * - * basic_rate = wlc->band->basic_rate[tx_rate] - * - * Make sure there is a best basic rate entry for - * every rate by walking up the table from low rates - * to high, filling in holes in the lookup table - */ - - for (i = 0; i < wlc->band->hw_rateset.count; i++) { - rate = wlc->band->hw_rateset.rates[i]; - - if (br[rate] != 0) { - /* This rate is a basic rate. - * Keep track of the best basic rate so far by - * modulation type. - */ - if (is_ofdm_rate(rate)) - ofdm_basic = rate; - else - cck_basic = rate; - - continue; - } - - /* This rate is not a basic rate so figure out the - * best basic rate less than this rate and fill in - * the hole in the table - */ - - br[rate] = is_ofdm_rate(rate) ? ofdm_basic : cck_basic; - - if (br[rate] != 0) - continue; - - if (is_ofdm_rate(rate)) { - /* - * In 11g and 11a, the OFDM mandatory rates - * are 6, 12, and 24 Mbps - */ - if (rate >= BRCM_RATE_24M) - mandatory = BRCM_RATE_24M; - else if (rate >= BRCM_RATE_12M) - mandatory = BRCM_RATE_12M; - else - mandatory = BRCM_RATE_6M; - } else { - /* In 11b, all CCK rates are mandatory 1 - 11 Mbps */ - mandatory = rate; - } - - br[rate] = mandatory; - } -} - -static void brcms_c_bandinit_ordered(struct brcms_c_info *wlc, - u16 chanspec) -{ - struct brcms_c_rateset default_rateset; - uint parkband; - uint i, band_order[2]; - - /* - * We might have been bandlocked during down and the chip - * power-cycled (hibernate). Figure out the right band to park on - */ - if (wlc->bandlocked || wlc->pub->_nbands == 1) { - /* updated in brcms_c_bandlock() */ - parkband = wlc->band->bandunit; - band_order[0] = band_order[1] = parkband; - } else { - /* park on the band of the specified chanspec */ - parkband = chspec_bandunit(chanspec); - - /* order so that parkband initialize last */ - band_order[0] = parkband ^ 1; - band_order[1] = parkband; - } - - /* make each band operational, software state init */ - for (i = 0; i < wlc->pub->_nbands; i++) { - uint j = band_order[i]; - - wlc->band = wlc->bandstate[j]; - - brcms_default_rateset(wlc, &default_rateset); - - /* fill in hw_rate */ - brcms_c_rateset_filter(&default_rateset, &wlc->band->hw_rateset, - false, BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK, - (bool) (wlc->pub->_n_enab & SUPPORT_11N)); - - /* init basic rate lookup */ - brcms_c_rate_lookup_init(wlc, &default_rateset); - } - - /* sync up phy/radio chanspec */ - brcms_c_set_phy_chanspec(wlc, chanspec); -} - -/* - * Set or clear filtering related maccontrol bits based on - * specified filter flags - */ -void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags) -{ - u32 promisc_bits = 0; - - wlc->filter_flags = filter_flags; - - if (filter_flags & FIF_OTHER_BSS) - promisc_bits |= MCTL_PROMISC; - - if (filter_flags & FIF_BCN_PRBRESP_PROMISC) - promisc_bits |= MCTL_BCNS_PROMISC; - - if (filter_flags & FIF_FCSFAIL) - promisc_bits |= MCTL_KEEPBADFCS; - - if (filter_flags & (FIF_CONTROL | FIF_PSPOLL)) - promisc_bits |= MCTL_KEEPCONTROL; - - brcms_b_mctrl(wlc->hw, - MCTL_PROMISC | MCTL_BCNS_PROMISC | - MCTL_KEEPCONTROL | MCTL_KEEPBADFCS, - promisc_bits); -} - -/* - * ucode, hwmac update - * Channel dependent updates for ucode and hw - */ -static void brcms_c_ucode_mac_upd(struct brcms_c_info *wlc) -{ - /* enable or disable any active IBSSs depending on whether or not - * we are on the home channel - */ - if (wlc->home_chanspec == wlc_phy_chanspec_get(wlc->band->pi)) { - if (wlc->pub->associated) { - /* - * BMAC_NOTE: This is something that should be fixed - * in ucode inits. I think that the ucode inits set - * up the bcn templates and shm values with a bogus - * beacon. This should not be done in the inits. If - * ucode needs to set up a beacon for testing, the - * test routines should write it down, not expect the - * inits to populate a bogus beacon. - */ - if (BRCMS_PHY_11N_CAP(wlc->band)) - brcms_b_write_shm(wlc->hw, - M_BCN_TXTSF_OFFSET, 0); - } - } else { - /* disable an active IBSS if we are not on the home channel */ - } -} - -static void brcms_c_write_rate_shm(struct brcms_c_info *wlc, u8 rate, - u8 basic_rate) -{ - u8 phy_rate, index; - u8 basic_phy_rate, basic_index; - u16 dir_table, basic_table; - u16 basic_ptr; - - /* Shared memory address for the table we are reading */ - dir_table = is_ofdm_rate(basic_rate) ? M_RT_DIRMAP_A : M_RT_DIRMAP_B; - - /* Shared memory address for the table we are writing */ - basic_table = is_ofdm_rate(rate) ? M_RT_BBRSMAP_A : M_RT_BBRSMAP_B; - - /* - * for a given rate, the LS-nibble of the PLCP SIGNAL field is - * the index into the rate table. - */ - phy_rate = rate_info[rate] & BRCMS_RATE_MASK; - basic_phy_rate = rate_info[basic_rate] & BRCMS_RATE_MASK; - index = phy_rate & 0xf; - basic_index = basic_phy_rate & 0xf; - - /* Find the SHM pointer to the ACK rate entry by looking in the - * Direct-map Table - */ - basic_ptr = brcms_b_read_shm(wlc->hw, (dir_table + basic_index * 2)); - - /* Update the SHM BSS-basic-rate-set mapping table with the pointer - * to the correct basic rate for the given incoming rate - */ - brcms_b_write_shm(wlc->hw, (basic_table + index * 2), basic_ptr); -} - -static const struct brcms_c_rateset * -brcms_c_rateset_get_hwrs(struct brcms_c_info *wlc) -{ - const struct brcms_c_rateset *rs_dflt; - - if (BRCMS_PHY_11N_CAP(wlc->band)) { - if (wlc->band->bandtype == BRCM_BAND_5G) - rs_dflt = &ofdm_mimo_rates; - else - rs_dflt = &cck_ofdm_mimo_rates; - } else if (wlc->band->gmode) - rs_dflt = &cck_ofdm_rates; - else - rs_dflt = &cck_rates; - - return rs_dflt; -} - -static void brcms_c_set_ratetable(struct brcms_c_info *wlc) -{ - const struct brcms_c_rateset *rs_dflt; - struct brcms_c_rateset rs; - u8 rate, basic_rate; - uint i; - - rs_dflt = brcms_c_rateset_get_hwrs(wlc); - - brcms_c_rateset_copy(rs_dflt, &rs); - brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); - - /* walk the phy rate table and update SHM basic rate lookup table */ - for (i = 0; i < rs.count; i++) { - rate = rs.rates[i] & BRCMS_RATE_MASK; - - /* for a given rate brcms_basic_rate returns the rate at - * which a response ACK/CTS should be sent. - */ - basic_rate = brcms_basic_rate(wlc, rate); - if (basic_rate == 0) - /* This should only happen if we are using a - * restricted rateset. - */ - basic_rate = rs.rates[0] & BRCMS_RATE_MASK; - - brcms_c_write_rate_shm(wlc, rate, basic_rate); - } -} - -/* band-specific init */ -static void brcms_c_bsinit(struct brcms_c_info *wlc) -{ - brcms_dbg_info(wlc->hw->d11core, "wl%d: bandunit %d\n", - wlc->pub->unit, wlc->band->bandunit); - - /* write ucode ACK/CTS rate table */ - brcms_c_set_ratetable(wlc); - - /* update some band specific mac configuration */ - brcms_c_ucode_mac_upd(wlc); - - /* init antenna selection */ - brcms_c_antsel_init(wlc->asi); - -} - -/* formula: IDLE_BUSY_RATIO_X_16 = (100-duty_cycle)/duty_cycle*16 */ -static int -brcms_c_duty_cycle_set(struct brcms_c_info *wlc, int duty_cycle, bool isOFDM, - bool writeToShm) -{ - int idle_busy_ratio_x_16 = 0; - uint offset = - isOFDM ? M_TX_IDLE_BUSY_RATIO_X_16_OFDM : - M_TX_IDLE_BUSY_RATIO_X_16_CCK; - if (duty_cycle > 100 || duty_cycle < 0) { - brcms_err(wlc->hw->d11core, - "wl%d: duty cycle value off limit\n", - wlc->pub->unit); - return -EINVAL; - } - if (duty_cycle) - idle_busy_ratio_x_16 = (100 - duty_cycle) * 16 / duty_cycle; - /* Only write to shared memory when wl is up */ - if (writeToShm) - brcms_b_write_shm(wlc->hw, offset, (u16) idle_busy_ratio_x_16); - - if (isOFDM) - wlc->tx_duty_cycle_ofdm = (u16) duty_cycle; - else - wlc->tx_duty_cycle_cck = (u16) duty_cycle; - - return 0; -} - -/* push sw hps and wake state through hardware */ -static void brcms_c_set_ps_ctrl(struct brcms_c_info *wlc) -{ - u32 v1, v2; - bool hps; - bool awake_before; - - hps = brcms_c_ps_allowed(wlc); - - brcms_dbg_mac80211(wlc->hw->d11core, "wl%d: hps %d\n", wlc->pub->unit, - hps); - - v1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); - v2 = MCTL_WAKE; - if (hps) - v2 |= MCTL_HPS; - - brcms_b_mctrl(wlc->hw, MCTL_WAKE | MCTL_HPS, v2); - - awake_before = ((v1 & MCTL_WAKE) || ((v1 & MCTL_HPS) == 0)); - - if (!awake_before) - brcms_b_wait_for_wake(wlc->hw); -} - -/* - * Write this BSS config's MAC address to core. - * Updates RXE match engine. - */ -static int brcms_c_set_mac(struct brcms_bss_cfg *bsscfg) -{ - int err = 0; - struct brcms_c_info *wlc = bsscfg->wlc; - - /* enter the MAC addr into the RXE match registers */ - brcms_c_set_addrmatch(wlc, RCM_MAC_OFFSET, wlc->pub->cur_etheraddr); - - brcms_c_ampdu_macaddr_upd(wlc); - - return err; -} - -/* Write the BSS config's BSSID address to core (set_bssid in d11procs.tcl). - * Updates RXE match engine. - */ -static void brcms_c_set_bssid(struct brcms_bss_cfg *bsscfg) -{ - /* we need to update BSSID in RXE match registers */ - brcms_c_set_addrmatch(bsscfg->wlc, RCM_BSSID_OFFSET, bsscfg->BSSID); -} - -void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, size_t ssid_len) -{ - u8 len = min_t(u8, sizeof(wlc->bsscfg->SSID), ssid_len); - memset(wlc->bsscfg->SSID, 0, sizeof(wlc->bsscfg->SSID)); - - memcpy(wlc->bsscfg->SSID, ssid, len); - wlc->bsscfg->SSID_len = len; -} - -static void brcms_b_set_shortslot(struct brcms_hardware *wlc_hw, bool shortslot) -{ - wlc_hw->shortslot = shortslot; - - if (wlc_hw->band->bandtype == BRCM_BAND_2G && wlc_hw->up) { - brcms_c_suspend_mac_and_wait(wlc_hw->wlc); - brcms_b_update_slot_timing(wlc_hw, shortslot); - brcms_c_enable_mac(wlc_hw->wlc); - } -} - -/* - * Suspend the the MAC and update the slot timing - * for standard 11b/g (20us slots) or shortslot 11g (9us slots). - */ -static void brcms_c_switch_shortslot(struct brcms_c_info *wlc, bool shortslot) -{ - /* use the override if it is set */ - if (wlc->shortslot_override != BRCMS_SHORTSLOT_AUTO) - shortslot = (wlc->shortslot_override == BRCMS_SHORTSLOT_ON); - - if (wlc->shortslot == shortslot) - return; - - wlc->shortslot = shortslot; - - brcms_b_set_shortslot(wlc->hw, shortslot); -} - -static void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec) -{ - if (wlc->home_chanspec != chanspec) { - wlc->home_chanspec = chanspec; - - if (wlc->pub->associated) - wlc->bsscfg->current_bss->chanspec = chanspec; - } -} - -void -brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, - bool mute_tx, struct txpwr_limits *txpwr) -{ - uint bandunit; - - brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: 0x%x\n", wlc_hw->unit, - chanspec); - - wlc_hw->chanspec = chanspec; - - /* Switch bands if necessary */ - if (wlc_hw->_nbands > 1) { - bandunit = chspec_bandunit(chanspec); - if (wlc_hw->band->bandunit != bandunit) { - /* brcms_b_setband disables other bandunit, - * use light band switch if not up yet - */ - if (wlc_hw->up) { - wlc_phy_chanspec_radio_set(wlc_hw-> - bandstate[bandunit]-> - pi, chanspec); - brcms_b_setband(wlc_hw, bandunit, chanspec); - } else { - brcms_c_setxband(wlc_hw, bandunit); - } - } - } - - wlc_phy_initcal_enable(wlc_hw->band->pi, !mute_tx); - - if (!wlc_hw->up) { - if (wlc_hw->clk) - wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, - chanspec); - wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec); - } else { - wlc_phy_chanspec_set(wlc_hw->band->pi, chanspec); - wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, chanspec); - - /* Update muting of the channel */ - brcms_b_mute(wlc_hw, mute_tx); - } -} - -/* switch to and initialize new band */ -static void brcms_c_setband(struct brcms_c_info *wlc, - uint bandunit) -{ - wlc->band = wlc->bandstate[bandunit]; - - if (!wlc->pub->up) - return; - - /* wait for at least one beacon before entering sleeping state */ - brcms_c_set_ps_ctrl(wlc); - - /* band-specific initializations */ - brcms_c_bsinit(wlc); -} - -static void brcms_c_set_chanspec(struct brcms_c_info *wlc, u16 chanspec) -{ - uint bandunit; - bool switchband = false; - u16 old_chanspec = wlc->chanspec; - - if (!brcms_c_valid_chanspec_db(wlc->cmi, chanspec)) { - brcms_err(wlc->hw->d11core, "wl%d: %s: Bad channel %d\n", - wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec)); - return; - } - - /* Switch bands if necessary */ - if (wlc->pub->_nbands > 1) { - bandunit = chspec_bandunit(chanspec); - if (wlc->band->bandunit != bandunit || wlc->bandinit_pending) { - switchband = true; - if (wlc->bandlocked) { - brcms_err(wlc->hw->d11core, - "wl%d: %s: chspec %d band is locked!\n", - wlc->pub->unit, __func__, - CHSPEC_CHANNEL(chanspec)); - return; - } - /* - * should the setband call come after the - * brcms_b_chanspec() ? if the setband updates - * (brcms_c_bsinit) use low level calls to inspect and - * set state, the state inspected may be from the wrong - * band, or the following brcms_b_set_chanspec() may - * undo the work. - */ - brcms_c_setband(wlc, bandunit); - } - } - - /* sync up phy/radio chanspec */ - brcms_c_set_phy_chanspec(wlc, chanspec); - - /* init antenna selection */ - if (brcms_chspec_bw(old_chanspec) != brcms_chspec_bw(chanspec)) { - brcms_c_antsel_init(wlc->asi); - - /* Fix the hardware rateset based on bw. - * Mainly add MCS32 for 40Mhz, remove MCS 32 for 20Mhz - */ - brcms_c_rateset_bw_mcs_filter(&wlc->band->hw_rateset, - wlc->band->mimo_cap_40 ? brcms_chspec_bw(chanspec) : 0); - } - - /* update some mac configuration since chanspec changed */ - brcms_c_ucode_mac_upd(wlc); -} - -/* - * This function changes the phytxctl for beacon based on current - * beacon ratespec AND txant setting as per this table: - * ratespec CCK ant = wlc->stf->txant - * OFDM ant = 3 - */ -void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, - u32 bcn_rspec) -{ - u16 phyctl; - u16 phytxant = wlc->stf->phytxant; - u16 mask = PHY_TXC_ANT_MASK; - - /* for non-siso rates or default setting, use the available chains */ - if (BRCMS_PHY_11N_CAP(wlc->band)) - phytxant = brcms_c_stf_phytxchain_sel(wlc, bcn_rspec); - - phyctl = brcms_b_read_shm(wlc->hw, M_BCN_PCTLWD); - phyctl = (phyctl & ~mask) | phytxant; - brcms_b_write_shm(wlc->hw, M_BCN_PCTLWD, phyctl); -} - -/* - * centralized protection config change function to simplify debugging, no - * consistency checking this should be called only on changes to avoid overhead - * in periodic function - */ -void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val) -{ - /* - * Cannot use brcms_dbg_* here because this function is called - * before wlc is sufficiently initialized. - */ - BCMMSG(wlc->wiphy, "idx %d, val %d\n", idx, val); - - switch (idx) { - case BRCMS_PROT_G_SPEC: - wlc->protection->_g = (bool) val; - break; - case BRCMS_PROT_G_OVR: - wlc->protection->g_override = (s8) val; - break; - case BRCMS_PROT_G_USER: - wlc->protection->gmode_user = (u8) val; - break; - case BRCMS_PROT_OVERLAP: - wlc->protection->overlap = (s8) val; - break; - case BRCMS_PROT_N_USER: - wlc->protection->nmode_user = (s8) val; - break; - case BRCMS_PROT_N_CFG: - wlc->protection->n_cfg = (s8) val; - break; - case BRCMS_PROT_N_CFG_OVR: - wlc->protection->n_cfg_override = (s8) val; - break; - case BRCMS_PROT_N_NONGF: - wlc->protection->nongf = (bool) val; - break; - case BRCMS_PROT_N_NONGF_OVR: - wlc->protection->nongf_override = (s8) val; - break; - case BRCMS_PROT_N_PAM_OVR: - wlc->protection->n_pam_override = (s8) val; - break; - case BRCMS_PROT_N_OBSS: - wlc->protection->n_obss = (bool) val; - break; - - default: - break; - } - -} - -static void brcms_c_ht_update_sgi_rx(struct brcms_c_info *wlc, int val) -{ - if (wlc->pub->up) { - brcms_c_update_beacon(wlc); - brcms_c_update_probe_resp(wlc, true); - } -} - -static void brcms_c_ht_update_ldpc(struct brcms_c_info *wlc, s8 val) -{ - wlc->stf->ldpc = val; - - if (wlc->pub->up) { - brcms_c_update_beacon(wlc); - brcms_c_update_probe_resp(wlc, true); - wlc_phy_ldpc_override_set(wlc->band->pi, (val ? true : false)); - } -} - -void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, - const struct ieee80211_tx_queue_params *params, - bool suspend) -{ - int i; - struct shm_acparams acp_shm; - u16 *shm_entry; - - /* Only apply params if the core is out of reset and has clocks */ - if (!wlc->clk) { - brcms_err(wlc->hw->d11core, "wl%d: %s : no-clock\n", - wlc->pub->unit, __func__); - return; - } - - memset(&acp_shm, 0, sizeof(struct shm_acparams)); - /* fill in shm ac params struct */ - acp_shm.txop = params->txop; - /* convert from units of 32us to us for ucode */ - wlc->edcf_txop[aci & 0x3] = acp_shm.txop = - EDCF_TXOP2USEC(acp_shm.txop); - acp_shm.aifs = (params->aifs & EDCF_AIFSN_MASK); - - if (aci == IEEE80211_AC_VI && acp_shm.txop == 0 - && acp_shm.aifs < EDCF_AIFSN_MAX) - acp_shm.aifs++; - - if (acp_shm.aifs < EDCF_AIFSN_MIN - || acp_shm.aifs > EDCF_AIFSN_MAX) { - brcms_err(wlc->hw->d11core, "wl%d: edcf_setparams: bad " - "aifs %d\n", wlc->pub->unit, acp_shm.aifs); - } else { - acp_shm.cwmin = params->cw_min; - acp_shm.cwmax = params->cw_max; - acp_shm.cwcur = acp_shm.cwmin; - acp_shm.bslots = - bcma_read16(wlc->hw->d11core, D11REGOFFS(tsf_random)) & - acp_shm.cwcur; - acp_shm.reggap = acp_shm.bslots + acp_shm.aifs; - /* Indicate the new params to the ucode */ - acp_shm.status = brcms_b_read_shm(wlc->hw, (M_EDCF_QINFO + - wme_ac2fifo[aci] * - M_EDCF_QLEN + - M_EDCF_STATUS_OFF)); - acp_shm.status |= WME_STATUS_NEWAC; - - /* Fill in shm acparam table */ - shm_entry = (u16 *) &acp_shm; - for (i = 0; i < (int)sizeof(struct shm_acparams); i += 2) - brcms_b_write_shm(wlc->hw, - M_EDCF_QINFO + - wme_ac2fifo[aci] * M_EDCF_QLEN + i, - *shm_entry++); - } - - if (suspend) - brcms_c_suspend_mac_and_wait(wlc); - - brcms_c_update_beacon(wlc); - brcms_c_update_probe_resp(wlc, false); - - if (suspend) - brcms_c_enable_mac(wlc); -} - -static void brcms_c_edcf_setparams(struct brcms_c_info *wlc, bool suspend) -{ - u16 aci; - int i_ac; - struct ieee80211_tx_queue_params txq_pars; - static const struct edcf_acparam default_edcf_acparams[] = { - {EDCF_AC_BE_ACI_STA, EDCF_AC_BE_ECW_STA, EDCF_AC_BE_TXOP_STA}, - {EDCF_AC_BK_ACI_STA, EDCF_AC_BK_ECW_STA, EDCF_AC_BK_TXOP_STA}, - {EDCF_AC_VI_ACI_STA, EDCF_AC_VI_ECW_STA, EDCF_AC_VI_TXOP_STA}, - {EDCF_AC_VO_ACI_STA, EDCF_AC_VO_ECW_STA, EDCF_AC_VO_TXOP_STA} - }; /* ucode needs these parameters during its initialization */ - const struct edcf_acparam *edcf_acp = &default_edcf_acparams[0]; - - for (i_ac = 0; i_ac < IEEE80211_NUM_ACS; i_ac++, edcf_acp++) { - /* find out which ac this set of params applies to */ - aci = (edcf_acp->ACI & EDCF_ACI_MASK) >> EDCF_ACI_SHIFT; - - /* fill in shm ac params struct */ - txq_pars.txop = edcf_acp->TXOP; - txq_pars.aifs = edcf_acp->ACI; - - /* CWmin = 2^(ECWmin) - 1 */ - txq_pars.cw_min = EDCF_ECW2CW(edcf_acp->ECW & EDCF_ECWMIN_MASK); - /* CWmax = 2^(ECWmax) - 1 */ - txq_pars.cw_max = EDCF_ECW2CW((edcf_acp->ECW & EDCF_ECWMAX_MASK) - >> EDCF_ECWMAX_SHIFT); - brcms_c_wme_setparams(wlc, aci, &txq_pars, suspend); - } - - if (suspend) { - brcms_c_suspend_mac_and_wait(wlc); - brcms_c_enable_mac(wlc); - } -} - -static void brcms_c_radio_monitor_start(struct brcms_c_info *wlc) -{ - /* Don't start the timer if HWRADIO feature is disabled */ - if (wlc->radio_monitor) - return; - - wlc->radio_monitor = true; - brcms_b_pllreq(wlc->hw, true, BRCMS_PLLREQ_RADIO_MON); - brcms_add_timer(wlc->radio_timer, TIMER_INTERVAL_RADIOCHK, true); -} - -static bool brcms_c_radio_monitor_stop(struct brcms_c_info *wlc) -{ - if (!wlc->radio_monitor) - return true; - - wlc->radio_monitor = false; - brcms_b_pllreq(wlc->hw, false, BRCMS_PLLREQ_RADIO_MON); - return brcms_del_timer(wlc->radio_timer); -} - -/* read hwdisable state and propagate to wlc flag */ -static void brcms_c_radio_hwdisable_upd(struct brcms_c_info *wlc) -{ - if (wlc->pub->hw_off) - return; - - if (brcms_b_radio_read_hwdisabled(wlc->hw)) - mboolset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE); - else - mboolclr(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE); -} - -/* update hwradio status and return it */ -bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc) -{ - brcms_c_radio_hwdisable_upd(wlc); - - return mboolisset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE) ? - true : false; -} - -/* periodical query hw radio button while driver is "down" */ -static void brcms_c_radio_timer(void *arg) -{ - struct brcms_c_info *wlc = (struct brcms_c_info *) arg; - - if (brcms_deviceremoved(wlc)) { - brcms_err(wlc->hw->d11core, "wl%d: %s: dead chip\n", - wlc->pub->unit, __func__); - brcms_down(wlc->wl); - return; - } - - brcms_c_radio_hwdisable_upd(wlc); -} - -/* common low-level watchdog code */ -static void brcms_b_watchdog(struct brcms_c_info *wlc) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - - if (!wlc_hw->up) - return; - - /* increment second count */ - wlc_hw->now++; - - /* Check for FIFO error interrupts */ - brcms_b_fifoerrors(wlc_hw); - - /* make sure RX dma has buffers */ - dma_rxfill(wlc->hw->di[RX_FIFO]); - - wlc_phy_watchdog(wlc_hw->band->pi); -} - -/* common watchdog code */ -static void brcms_c_watchdog(struct brcms_c_info *wlc) -{ - brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); - - if (!wlc->pub->up) - return; - - if (brcms_deviceremoved(wlc)) { - brcms_err(wlc->hw->d11core, "wl%d: %s: dead chip\n", - wlc->pub->unit, __func__); - brcms_down(wlc->wl); - return; - } - - /* increment second count */ - wlc->pub->now++; - - brcms_c_radio_hwdisable_upd(wlc); - /* if radio is disable, driver may be down, quit here */ - if (wlc->pub->radio_disabled) - return; - - brcms_b_watchdog(wlc); - - /* - * occasionally sample mac stat counters to - * detect 16-bit counter wrap - */ - if ((wlc->pub->now % SW_TIMER_MAC_STAT_UPD) == 0) - brcms_c_statsupd(wlc); - - if (BRCMS_ISNPHY(wlc->band) && - ((wlc->pub->now - wlc->tempsense_lasttime) >= - BRCMS_TEMPSENSE_PERIOD)) { - wlc->tempsense_lasttime = wlc->pub->now; - brcms_c_tempsense_upd(wlc); - } -} - -static void brcms_c_watchdog_by_timer(void *arg) -{ - struct brcms_c_info *wlc = (struct brcms_c_info *) arg; - - brcms_c_watchdog(wlc); -} - -static bool brcms_c_timers_init(struct brcms_c_info *wlc, int unit) -{ - wlc->wdtimer = brcms_init_timer(wlc->wl, brcms_c_watchdog_by_timer, - wlc, "watchdog"); - if (!wlc->wdtimer) { - wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for wdtimer " - "failed\n", unit); - goto fail; - } - - wlc->radio_timer = brcms_init_timer(wlc->wl, brcms_c_radio_timer, - wlc, "radio"); - if (!wlc->radio_timer) { - wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for radio_timer " - "failed\n", unit); - goto fail; - } - - return true; - - fail: - return false; -} - -/* - * Initialize brcms_c_info default values ... - * may get overrides later in this function - */ -static void brcms_c_info_init(struct brcms_c_info *wlc, int unit) -{ - int i; - - /* Save our copy of the chanspec */ - wlc->chanspec = ch20mhz_chspec(1); - - /* various 802.11g modes */ - wlc->shortslot = false; - wlc->shortslot_override = BRCMS_SHORTSLOT_AUTO; - - brcms_c_protection_upd(wlc, BRCMS_PROT_G_OVR, BRCMS_PROTECTION_AUTO); - brcms_c_protection_upd(wlc, BRCMS_PROT_G_SPEC, false); - - brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG_OVR, - BRCMS_PROTECTION_AUTO); - brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG, BRCMS_N_PROTECTION_OFF); - brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF_OVR, - BRCMS_PROTECTION_AUTO); - brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF, false); - brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, AUTO); - - brcms_c_protection_upd(wlc, BRCMS_PROT_OVERLAP, - BRCMS_PROTECTION_CTL_OVERLAP); - - /* 802.11g draft 4.0 NonERP elt advertisement */ - wlc->include_legacy_erp = true; - - wlc->stf->ant_rx_ovr = ANT_RX_DIV_DEF; - wlc->stf->txant = ANT_TX_DEF; - - wlc->prb_resp_timeout = BRCMS_PRB_RESP_TIMEOUT; - - wlc->usr_fragthresh = DOT11_DEFAULT_FRAG_LEN; - for (i = 0; i < NFIFO; i++) - wlc->fragthresh[i] = DOT11_DEFAULT_FRAG_LEN; - wlc->RTSThresh = DOT11_DEFAULT_RTS_LEN; - - /* default rate fallback retry limits */ - wlc->SFBL = RETRY_SHORT_FB; - wlc->LFBL = RETRY_LONG_FB; - - /* default mac retry limits */ - wlc->SRL = RETRY_SHORT_DEF; - wlc->LRL = RETRY_LONG_DEF; - - /* WME QoS mode is Auto by default */ - wlc->pub->_ampdu = AMPDU_AGG_HOST; -} - -static uint brcms_c_attach_module(struct brcms_c_info *wlc) -{ - uint err = 0; - uint unit; - unit = wlc->pub->unit; - - wlc->asi = brcms_c_antsel_attach(wlc); - if (wlc->asi == NULL) { - wiphy_err(wlc->wiphy, "wl%d: attach: antsel_attach " - "failed\n", unit); - err = 44; - goto fail; - } - - wlc->ampdu = brcms_c_ampdu_attach(wlc); - if (wlc->ampdu == NULL) { - wiphy_err(wlc->wiphy, "wl%d: attach: ampdu_attach " - "failed\n", unit); - err = 50; - goto fail; - } - - if ((brcms_c_stf_attach(wlc) != 0)) { - wiphy_err(wlc->wiphy, "wl%d: attach: stf_attach " - "failed\n", unit); - err = 68; - goto fail; - } - fail: - return err; -} - -struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc) -{ - return wlc->pub; -} - -/* low level attach - * run backplane attach, init nvram - * run phy attach - * initialize software state for each core and band - * put the whole chip in reset(driver down state), no clock - */ -static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core, - uint unit, bool piomode) -{ - struct brcms_hardware *wlc_hw; - uint err = 0; - uint j; - bool wme = false; - struct shared_phy_params sha_params; - struct wiphy *wiphy = wlc->wiphy; - struct pci_dev *pcidev = core->bus->host_pci; - struct ssb_sprom *sprom = &core->bus->sprom; - - if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) - brcms_dbg_info(core, "wl%d: vendor 0x%x device 0x%x\n", unit, - pcidev->vendor, - pcidev->device); - else - brcms_dbg_info(core, "wl%d: vendor 0x%x device 0x%x\n", unit, - core->bus->boardinfo.vendor, - core->bus->boardinfo.type); - - wme = true; - - wlc_hw = wlc->hw; - wlc_hw->wlc = wlc; - wlc_hw->unit = unit; - wlc_hw->band = wlc_hw->bandstate[0]; - wlc_hw->_piomode = piomode; - - /* populate struct brcms_hardware with default values */ - brcms_b_info_init(wlc_hw); - - /* - * Do the hardware portion of the attach. Also initialize software - * state that depends on the particular hardware we are running. - */ - wlc_hw->sih = ai_attach(core->bus); - if (wlc_hw->sih == NULL) { - wiphy_err(wiphy, "wl%d: brcms_b_attach: si_attach failed\n", - unit); - err = 11; - goto fail; - } - - /* verify again the device is supported */ - if (!brcms_c_chipmatch(core)) { - wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported device\n", - unit); - err = 12; - goto fail; - } - - if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) { - wlc_hw->vendorid = pcidev->vendor; - wlc_hw->deviceid = pcidev->device; - } else { - wlc_hw->vendorid = core->bus->boardinfo.vendor; - wlc_hw->deviceid = core->bus->boardinfo.type; - } - - wlc_hw->d11core = core; - wlc_hw->corerev = core->id.rev; - - /* validate chip, chiprev and corerev */ - if (!brcms_c_isgoodchip(wlc_hw)) { - err = 13; - goto fail; - } - - /* initialize power control registers */ - ai_clkctl_init(wlc_hw->sih); - - /* request fastclock and force fastclock for the rest of attach - * bring the d11 core out of reset. - * For PMU chips, the first wlc_clkctl_clk is no-op since core-clk - * is still false; But it will be called again inside wlc_corereset, - * after d11 is out of reset. - */ - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); - brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); - - if (!brcms_b_validate_chip_access(wlc_hw)) { - wiphy_err(wiphy, "wl%d: brcms_b_attach: validate_chip_access " - "failed\n", unit); - err = 14; - goto fail; - } - - /* get the board rev, used just below */ - j = sprom->board_rev; - /* promote srom boardrev of 0xFF to 1 */ - if (j == BOARDREV_PROMOTABLE) - j = BOARDREV_PROMOTED; - wlc_hw->boardrev = (u16) j; - if (!brcms_c_validboardtype(wlc_hw)) { - wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported Broadcom " - "board type (0x%x)" " or revision level (0x%x)\n", - unit, ai_get_boardtype(wlc_hw->sih), - wlc_hw->boardrev); - err = 15; - goto fail; - } - wlc_hw->sromrev = sprom->revision; - wlc_hw->boardflags = sprom->boardflags_lo + (sprom->boardflags_hi << 16); - wlc_hw->boardflags2 = sprom->boardflags2_lo + (sprom->boardflags2_hi << 16); - - if (wlc_hw->boardflags & BFL_NOPLLDOWN) - brcms_b_pllreq(wlc_hw, true, BRCMS_PLLREQ_SHARED); - - /* check device id(srom, nvram etc.) to set bands */ - if (wlc_hw->deviceid == BCM43224_D11N_ID || - wlc_hw->deviceid == BCM43224_D11N_ID_VEN1 || - wlc_hw->deviceid == BCM43224_CHIP_ID) - /* Dualband boards */ - wlc_hw->_nbands = 2; - else - wlc_hw->_nbands = 1; - - if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43225)) - wlc_hw->_nbands = 1; - - /* BMAC_NOTE: remove init of pub values when brcms_c_attach() - * unconditionally does the init of these values - */ - wlc->vendorid = wlc_hw->vendorid; - wlc->deviceid = wlc_hw->deviceid; - wlc->pub->sih = wlc_hw->sih; - wlc->pub->corerev = wlc_hw->corerev; - wlc->pub->sromrev = wlc_hw->sromrev; - wlc->pub->boardrev = wlc_hw->boardrev; - wlc->pub->boardflags = wlc_hw->boardflags; - wlc->pub->boardflags2 = wlc_hw->boardflags2; - wlc->pub->_nbands = wlc_hw->_nbands; - - wlc_hw->physhim = wlc_phy_shim_attach(wlc_hw, wlc->wl, wlc); - - if (wlc_hw->physhim == NULL) { - wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_shim_attach " - "failed\n", unit); - err = 25; - goto fail; - } - - /* pass all the parameters to wlc_phy_shared_attach in one struct */ - sha_params.sih = wlc_hw->sih; - sha_params.physhim = wlc_hw->physhim; - sha_params.unit = unit; - sha_params.corerev = wlc_hw->corerev; - sha_params.vid = wlc_hw->vendorid; - sha_params.did = wlc_hw->deviceid; - sha_params.chip = ai_get_chip_id(wlc_hw->sih); - sha_params.chiprev = ai_get_chiprev(wlc_hw->sih); - sha_params.chippkg = ai_get_chippkg(wlc_hw->sih); - sha_params.sromrev = wlc_hw->sromrev; - sha_params.boardtype = ai_get_boardtype(wlc_hw->sih); - sha_params.boardrev = wlc_hw->boardrev; - sha_params.boardflags = wlc_hw->boardflags; - sha_params.boardflags2 = wlc_hw->boardflags2; - - /* alloc and save pointer to shared phy state area */ - wlc_hw->phy_sh = wlc_phy_shared_attach(&sha_params); - if (!wlc_hw->phy_sh) { - err = 16; - goto fail; - } - - /* initialize software state for each core and band */ - for (j = 0; j < wlc_hw->_nbands; j++) { - /* - * band0 is always 2.4Ghz - * band1, if present, is 5Ghz - */ - - brcms_c_setxband(wlc_hw, j); - - wlc_hw->band->bandunit = j; - wlc_hw->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G; - wlc->band->bandunit = j; - wlc->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G; - wlc->core->coreidx = core->core_index; - - wlc_hw->machwcap = bcma_read32(core, D11REGOFFS(machwcap)); - wlc_hw->machwcap_backup = wlc_hw->machwcap; - - /* init tx fifo size */ - WARN_ON(wlc_hw->corerev < XMTFIFOTBL_STARTREV || - (wlc_hw->corerev - XMTFIFOTBL_STARTREV) > - ARRAY_SIZE(xmtfifo_sz)); - wlc_hw->xmtfifo_sz = - xmtfifo_sz[(wlc_hw->corerev - XMTFIFOTBL_STARTREV)]; - WARN_ON(!wlc_hw->xmtfifo_sz[0]); - - /* Get a phy for this band */ - wlc_hw->band->pi = - wlc_phy_attach(wlc_hw->phy_sh, core, - wlc_hw->band->bandtype, - wlc->wiphy); - if (wlc_hw->band->pi == NULL) { - wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_" - "attach failed\n", unit); - err = 17; - goto fail; - } - - wlc_phy_machwcap_set(wlc_hw->band->pi, wlc_hw->machwcap); - - wlc_phy_get_phyversion(wlc_hw->band->pi, &wlc_hw->band->phytype, - &wlc_hw->band->phyrev, - &wlc_hw->band->radioid, - &wlc_hw->band->radiorev); - wlc_hw->band->abgphy_encore = - wlc_phy_get_encore(wlc_hw->band->pi); - wlc->band->abgphy_encore = wlc_phy_get_encore(wlc_hw->band->pi); - wlc_hw->band->core_flags = - wlc_phy_get_coreflags(wlc_hw->band->pi); - - /* verify good phy_type & supported phy revision */ - if (BRCMS_ISNPHY(wlc_hw->band)) { - if (NCONF_HAS(wlc_hw->band->phyrev)) - goto good_phy; - else - goto bad_phy; - } else if (BRCMS_ISLCNPHY(wlc_hw->band)) { - if (LCNCONF_HAS(wlc_hw->band->phyrev)) - goto good_phy; - else - goto bad_phy; - } else { - bad_phy: - wiphy_err(wiphy, "wl%d: brcms_b_attach: unsupported " - "phy type/rev (%d/%d)\n", unit, - wlc_hw->band->phytype, wlc_hw->band->phyrev); - err = 18; - goto fail; - } - - good_phy: - /* - * BMAC_NOTE: wlc->band->pi should not be set below and should - * be done in the high level attach. However we can not make - * that change until all low level access is changed to - * wlc_hw->band->pi. Instead do the wlc->band->pi init below, - * keeping wlc_hw->band->pi as well for incremental update of - * low level fns, and cut over low only init when all fns - * updated. - */ - wlc->band->pi = wlc_hw->band->pi; - wlc->band->phytype = wlc_hw->band->phytype; - wlc->band->phyrev = wlc_hw->band->phyrev; - wlc->band->radioid = wlc_hw->band->radioid; - wlc->band->radiorev = wlc_hw->band->radiorev; - brcms_dbg_info(core, "wl%d: phy %u/%u radio %x/%u\n", unit, - wlc->band->phytype, wlc->band->phyrev, - wlc->band->radioid, wlc->band->radiorev); - /* default contention windows size limits */ - wlc_hw->band->CWmin = APHY_CWMIN; - wlc_hw->band->CWmax = PHY_CWMAX; - - if (!brcms_b_attach_dmapio(wlc, j, wme)) { - err = 19; - goto fail; - } - } - - /* disable core to match driver "down" state */ - brcms_c_coredisable(wlc_hw); - - /* Match driver "down" state */ - bcma_host_pci_down(wlc_hw->d11core->bus); - - /* turn off pll and xtal to match driver "down" state */ - brcms_b_xtal(wlc_hw, OFF); - - /* ******************************************************************* - * The hardware is in the DOWN state at this point. D11 core - * or cores are in reset with clocks off, and the board PLLs - * are off if possible. - * - * Beyond this point, wlc->sbclk == false and chip registers - * should not be touched. - ********************************************************************* - */ - - /* init etheraddr state variables */ - brcms_c_get_macaddr(wlc_hw, wlc_hw->etheraddr); - - if (is_broadcast_ether_addr(wlc_hw->etheraddr) || - is_zero_ether_addr(wlc_hw->etheraddr)) { - wiphy_err(wiphy, "wl%d: brcms_b_attach: bad macaddr\n", - unit); - err = 22; - goto fail; - } - - brcms_dbg_info(wlc_hw->d11core, "deviceid 0x%x nbands %d board 0x%x\n", - wlc_hw->deviceid, wlc_hw->_nbands, - ai_get_boardtype(wlc_hw->sih)); - - return err; - - fail: - wiphy_err(wiphy, "wl%d: brcms_b_attach: failed with err %d\n", unit, - err); - return err; -} - -static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc) -{ - int aa; - uint unit; - int bandtype; - struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; - - unit = wlc->pub->unit; - bandtype = wlc->band->bandtype; - - /* get antennas available */ - if (bandtype == BRCM_BAND_5G) - aa = sprom->ant_available_a; - else - aa = sprom->ant_available_bg; - - if ((aa < 1) || (aa > 15)) { - wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in" - " srom (0x%x), using 3\n", unit, __func__, aa); - aa = 3; - } - - /* reset the defaults if we have a single antenna */ - if (aa == 1) { - wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_0; - wlc->stf->txant = ANT_TX_FORCE_0; - } else if (aa == 2) { - wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_1; - wlc->stf->txant = ANT_TX_FORCE_1; - } else { - } - - /* Compute Antenna Gain */ - if (bandtype == BRCM_BAND_5G) - wlc->band->antgain = sprom->antenna_gain.a1; - else - wlc->band->antgain = sprom->antenna_gain.a0; - - return true; -} - -static void brcms_c_bss_default_init(struct brcms_c_info *wlc) -{ - u16 chanspec; - struct brcms_band *band; - struct brcms_bss_info *bi = wlc->default_bss; - - /* init default and target BSS with some sane initial values */ - memset(bi, 0, sizeof(*bi)); - bi->beacon_period = BEACON_INTERVAL_DEFAULT; - - /* fill the default channel as the first valid channel - * starting from the 2G channels - */ - chanspec = ch20mhz_chspec(1); - wlc->home_chanspec = bi->chanspec = chanspec; - - /* find the band of our default channel */ - band = wlc->band; - if (wlc->pub->_nbands > 1 && - band->bandunit != chspec_bandunit(chanspec)) - band = wlc->bandstate[OTHERBANDUNIT(wlc)]; - - /* init bss rates to the band specific default rate set */ - brcms_c_rateset_default(&bi->rateset, NULL, band->phytype, - band->bandtype, false, BRCMS_RATE_MASK_FULL, - (bool) (wlc->pub->_n_enab & SUPPORT_11N), - brcms_chspec_bw(chanspec), wlc->stf->txstreams); - - if (wlc->pub->_n_enab & SUPPORT_11N) - bi->flags |= BRCMS_BSS_HT; -} - -static void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap) -{ - uint i; - struct brcms_band *band; - - for (i = 0; i < wlc->pub->_nbands; i++) { - band = wlc->bandstate[i]; - if (band->bandtype == BRCM_BAND_5G) { - if ((bwcap == BRCMS_N_BW_40ALL) - || (bwcap == BRCMS_N_BW_20IN2G_40IN5G)) - band->mimo_cap_40 = true; - else - band->mimo_cap_40 = false; - } else { - if (bwcap == BRCMS_N_BW_40ALL) - band->mimo_cap_40 = true; - else - band->mimo_cap_40 = false; - } - } -} - -static void brcms_c_timers_deinit(struct brcms_c_info *wlc) -{ - /* free timer state */ - if (wlc->wdtimer) { - brcms_free_timer(wlc->wdtimer); - wlc->wdtimer = NULL; - } - if (wlc->radio_timer) { - brcms_free_timer(wlc->radio_timer); - wlc->radio_timer = NULL; - } -} - -static void brcms_c_detach_module(struct brcms_c_info *wlc) -{ - if (wlc->asi) { - brcms_c_antsel_detach(wlc->asi); - wlc->asi = NULL; - } - - if (wlc->ampdu) { - brcms_c_ampdu_detach(wlc->ampdu); - wlc->ampdu = NULL; - } - - brcms_c_stf_detach(wlc); -} - -/* - * low level detach - */ -static void brcms_b_detach(struct brcms_c_info *wlc) -{ - uint i; - struct brcms_hw_band *band; - struct brcms_hardware *wlc_hw = wlc->hw; - - brcms_b_detach_dmapio(wlc_hw); - - band = wlc_hw->band; - for (i = 0; i < wlc_hw->_nbands; i++) { - if (band->pi) { - /* Detach this band's phy */ - wlc_phy_detach(band->pi); - band->pi = NULL; - } - band = wlc_hw->bandstate[OTHERBANDUNIT(wlc)]; - } - - /* Free shared phy state */ - kfree(wlc_hw->phy_sh); - - wlc_phy_shim_detach(wlc_hw->physhim); - - if (wlc_hw->sih) { - ai_detach(wlc_hw->sih); - wlc_hw->sih = NULL; - } -} - -/* - * Return a count of the number of driver callbacks still pending. - * - * General policy is that brcms_c_detach can only dealloc/free software states. - * It can NOT touch hardware registers since the d11core may be in reset and - * clock may not be available. - * One exception is sb register access, which is possible if crystal is turned - * on after "down" state, driver should avoid software timer with the exception - * of radio_monitor. - */ -uint brcms_c_detach(struct brcms_c_info *wlc) -{ - uint callbacks; - - if (wlc == NULL) - return 0; - - brcms_b_detach(wlc); - - /* delete software timers */ - callbacks = 0; - if (!brcms_c_radio_monitor_stop(wlc)) - callbacks++; - - brcms_c_channel_mgr_detach(wlc->cmi); - - brcms_c_timers_deinit(wlc); - - brcms_c_detach_module(wlc); - - brcms_c_detach_mfree(wlc); - return callbacks; -} - -/* update state that depends on the current value of "ap" */ -static void brcms_c_ap_upd(struct brcms_c_info *wlc) -{ - /* STA-BSS; short capable */ - wlc->PLCPHdr_override = BRCMS_PLCP_SHORT; -} - -/* Initialize just the hardware when coming out of POR or S3/S5 system states */ -static void brcms_b_hw_up(struct brcms_hardware *wlc_hw) -{ - if (wlc_hw->wlc->pub->hw_up) - return; - - brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); - - /* - * Enable pll and xtal, initialize the power control registers, - * and force fastclock for the remainder of brcms_c_up(). - */ - brcms_b_xtal(wlc_hw, ON); - ai_clkctl_init(wlc_hw->sih); - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); - - /* - * TODO: test suspend/resume - * - * AI chip doesn't restore bar0win2 on - * hibernation/resume, need sw fixup - */ - - /* - * Inform phy that a POR reset has occurred so - * it does a complete phy init - */ - wlc_phy_por_inform(wlc_hw->band->pi); - - wlc_hw->ucode_loaded = false; - wlc_hw->wlc->pub->hw_up = true; - - if ((wlc_hw->boardflags & BFL_FEM) - && (ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM4313)) { - if (! - (wlc_hw->boardrev >= 0x1250 - && (wlc_hw->boardflags & BFL_FEM_BT))) - ai_epa_4313war(wlc_hw->sih); - } -} - -static int brcms_b_up_prep(struct brcms_hardware *wlc_hw) -{ - brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); - - /* - * Enable pll and xtal, initialize the power control registers, - * and force fastclock for the remainder of brcms_c_up(). - */ - brcms_b_xtal(wlc_hw, ON); - ai_clkctl_init(wlc_hw->sih); - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); - - /* - * Configure pci/pcmcia here instead of in brcms_c_attach() - * to allow mfg hotswap: down, hotswap (chip power cycle), up. - */ - bcma_host_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core, - true); - - /* - * Need to read the hwradio status here to cover the case where the - * system is loaded with the hw radio disabled. We do not want to - * bring the driver up in this case. - */ - if (brcms_b_radio_read_hwdisabled(wlc_hw)) { - /* put SB PCI in down state again */ - bcma_host_pci_down(wlc_hw->d11core->bus); - brcms_b_xtal(wlc_hw, OFF); - return -ENOMEDIUM; - } - - bcma_host_pci_up(wlc_hw->d11core->bus); - - /* reset the d11 core */ - brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); - - return 0; -} - -static int brcms_b_up_finish(struct brcms_hardware *wlc_hw) -{ - wlc_hw->up = true; - wlc_phy_hw_state_upd(wlc_hw->band->pi, true); - - /* FULLY enable dynamic power control and d11 core interrupt */ - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); - brcms_intrson(wlc_hw->wlc->wl); - return 0; -} - -/* - * Write WME tunable parameters for retransmit/max rate - * from wlc struct to ucode - */ -static void brcms_c_wme_retries_write(struct brcms_c_info *wlc) -{ - int ac; - - /* Need clock to do this */ - if (!wlc->clk) - return; - - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - brcms_b_write_shm(wlc->hw, M_AC_TXLMT_ADDR(ac), - wlc->wme_retries[ac]); -} - -/* make interface operational */ -int brcms_c_up(struct brcms_c_info *wlc) -{ - struct ieee80211_channel *ch; - - brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); - - /* HW is turned off so don't try to access it */ - if (wlc->pub->hw_off || brcms_deviceremoved(wlc)) - return -ENOMEDIUM; - - if (!wlc->pub->hw_up) { - brcms_b_hw_up(wlc->hw); - wlc->pub->hw_up = true; - } - - if ((wlc->pub->boardflags & BFL_FEM) - && (ai_get_chip_id(wlc->hw->sih) == BCMA_CHIP_ID_BCM4313)) { - if (wlc->pub->boardrev >= 0x1250 - && (wlc->pub->boardflags & BFL_FEM_BT)) - brcms_b_mhf(wlc->hw, MHF5, MHF5_4313_GPIOCTRL, - MHF5_4313_GPIOCTRL, BRCM_BAND_ALL); - else - brcms_b_mhf(wlc->hw, MHF4, MHF4_EXTPA_ENABLE, - MHF4_EXTPA_ENABLE, BRCM_BAND_ALL); - } - - /* - * Need to read the hwradio status here to cover the case where the - * system is loaded with the hw radio disabled. We do not want to bring - * the driver up in this case. If radio is disabled, abort up, lower - * power, start radio timer and return 0(for NDIS) don't call - * radio_update to avoid looping brcms_c_up. - * - * brcms_b_up_prep() returns either 0 or -BCME_RADIOOFF only - */ - if (!wlc->pub->radio_disabled) { - int status = brcms_b_up_prep(wlc->hw); - if (status == -ENOMEDIUM) { - if (!mboolisset - (wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE)) { - struct brcms_bss_cfg *bsscfg = wlc->bsscfg; - mboolset(wlc->pub->radio_disabled, - WL_RADIO_HW_DISABLE); - if (bsscfg->type == BRCMS_TYPE_STATION || - bsscfg->type == BRCMS_TYPE_ADHOC) - brcms_err(wlc->hw->d11core, - "wl%d: up: rfdisable -> " - "bsscfg_disable()\n", - wlc->pub->unit); - } - } - } - - if (wlc->pub->radio_disabled) { - brcms_c_radio_monitor_start(wlc); - return 0; - } - - /* brcms_b_up_prep has done brcms_c_corereset(). so clk is on, set it */ - wlc->clk = true; - - brcms_c_radio_monitor_stop(wlc); - - /* Set EDCF hostflags */ - brcms_b_mhf(wlc->hw, MHF1, MHF1_EDCF, MHF1_EDCF, BRCM_BAND_ALL); - - brcms_init(wlc->wl); - wlc->pub->up = true; - - if (wlc->bandinit_pending) { - ch = wlc->pub->ieee_hw->conf.chandef.chan; - brcms_c_suspend_mac_and_wait(wlc); - brcms_c_set_chanspec(wlc, ch20mhz_chspec(ch->hw_value)); - wlc->bandinit_pending = false; - brcms_c_enable_mac(wlc); - } - - brcms_b_up_finish(wlc->hw); - - /* Program the TX wme params with the current settings */ - brcms_c_wme_retries_write(wlc); - - /* start one second watchdog timer */ - brcms_add_timer(wlc->wdtimer, TIMER_INTERVAL_WATCHDOG, true); - wlc->WDarmed = true; - - /* ensure antenna config is up to date */ - brcms_c_stf_phy_txant_upd(wlc); - /* ensure LDPC config is in sync */ - brcms_c_ht_update_ldpc(wlc, wlc->stf->ldpc); - - return 0; -} - -static uint brcms_c_down_del_timer(struct brcms_c_info *wlc) -{ - uint callbacks = 0; - - return callbacks; -} - -static int brcms_b_bmac_down_prep(struct brcms_hardware *wlc_hw) -{ - bool dev_gone; - uint callbacks = 0; - - if (!wlc_hw->up) - return callbacks; - - dev_gone = brcms_deviceremoved(wlc_hw->wlc); - - /* disable interrupts */ - if (dev_gone) - wlc_hw->wlc->macintmask = 0; - else { - /* now disable interrupts */ - brcms_intrsoff(wlc_hw->wlc->wl); - - /* ensure we're running on the pll clock again */ - brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); - } - /* down phy at the last of this stage */ - callbacks += wlc_phy_down(wlc_hw->band->pi); - - return callbacks; -} - -static int brcms_b_down_finish(struct brcms_hardware *wlc_hw) -{ - uint callbacks = 0; - bool dev_gone; - - if (!wlc_hw->up) - return callbacks; - - wlc_hw->up = false; - wlc_phy_hw_state_upd(wlc_hw->band->pi, false); - - dev_gone = brcms_deviceremoved(wlc_hw->wlc); - - if (dev_gone) { - wlc_hw->sbclk = false; - wlc_hw->clk = false; - wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); - - /* reclaim any posted packets */ - brcms_c_flushqueues(wlc_hw->wlc); - } else { - - /* Reset and disable the core */ - if (bcma_core_is_enabled(wlc_hw->d11core)) { - if (bcma_read32(wlc_hw->d11core, - D11REGOFFS(maccontrol)) & MCTL_EN_MAC) - brcms_c_suspend_mac_and_wait(wlc_hw->wlc); - callbacks += brcms_reset(wlc_hw->wlc->wl); - brcms_c_coredisable(wlc_hw); - } - - /* turn off primary xtal and pll */ - if (!wlc_hw->noreset) { - bcma_host_pci_down(wlc_hw->d11core->bus); - brcms_b_xtal(wlc_hw, OFF); - } - } - - return callbacks; -} - -/* - * Mark the interface nonoperational, stop the software mechanisms, - * disable the hardware, free any transient buffer state. - * Return a count of the number of driver callbacks still pending. - */ -uint brcms_c_down(struct brcms_c_info *wlc) -{ - - uint callbacks = 0; - int i; - bool dev_gone = false; - - brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); - - /* check if we are already in the going down path */ - if (wlc->going_down) { - brcms_err(wlc->hw->d11core, - "wl%d: %s: Driver going down so return\n", - wlc->pub->unit, __func__); - return 0; - } - if (!wlc->pub->up) - return callbacks; - - wlc->going_down = true; - - callbacks += brcms_b_bmac_down_prep(wlc->hw); - - dev_gone = brcms_deviceremoved(wlc); - - /* Call any registered down handlers */ - for (i = 0; i < BRCMS_MAXMODULES; i++) { - if (wlc->modulecb[i].down_fn) - callbacks += - wlc->modulecb[i].down_fn(wlc->modulecb[i].hdl); - } - - /* cancel the watchdog timer */ - if (wlc->WDarmed) { - if (!brcms_del_timer(wlc->wdtimer)) - callbacks++; - wlc->WDarmed = false; - } - /* cancel all other timers */ - callbacks += brcms_c_down_del_timer(wlc); - - wlc->pub->up = false; - - wlc_phy_mute_upd(wlc->band->pi, false, PHY_MUTE_ALL); - - callbacks += brcms_b_down_finish(wlc->hw); - - /* brcms_b_down_finish has done brcms_c_coredisable(). so clk is off */ - wlc->clk = false; - - wlc->going_down = false; - return callbacks; -} - -/* Set the current gmode configuration */ -int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config) -{ - int ret = 0; - uint i; - struct brcms_c_rateset rs; - /* Default to 54g Auto */ - /* Advertise and use shortslot (-1/0/1 Auto/Off/On) */ - s8 shortslot = BRCMS_SHORTSLOT_AUTO; - bool shortslot_restrict = false; /* Restrict association to stations - * that support shortslot - */ - bool ofdm_basic = false; /* Make 6, 12, and 24 basic rates */ - /* Advertise and use short preambles (-1/0/1 Auto/Off/On) */ - int preamble = BRCMS_PLCP_LONG; - bool preamble_restrict = false; /* Restrict association to stations - * that support short preambles - */ - struct brcms_band *band; - - /* if N-support is enabled, allow Gmode set as long as requested - * Gmode is not GMODE_LEGACY_B - */ - if ((wlc->pub->_n_enab & SUPPORT_11N) && gmode == GMODE_LEGACY_B) - return -ENOTSUPP; - - /* verify that we are dealing with 2G band and grab the band pointer */ - if (wlc->band->bandtype == BRCM_BAND_2G) - band = wlc->band; - else if ((wlc->pub->_nbands > 1) && - (wlc->bandstate[OTHERBANDUNIT(wlc)]->bandtype == BRCM_BAND_2G)) - band = wlc->bandstate[OTHERBANDUNIT(wlc)]; - else - return -EINVAL; - - /* update configuration value */ - if (config) - brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, gmode); - - /* Clear rateset override */ - memset(&rs, 0, sizeof(rs)); - - switch (gmode) { - case GMODE_LEGACY_B: - shortslot = BRCMS_SHORTSLOT_OFF; - brcms_c_rateset_copy(&gphy_legacy_rates, &rs); - - break; - - case GMODE_LRS: - break; - - case GMODE_AUTO: - /* Accept defaults */ - break; - - case GMODE_ONLY: - ofdm_basic = true; - preamble = BRCMS_PLCP_SHORT; - preamble_restrict = true; - break; - - case GMODE_PERFORMANCE: - shortslot = BRCMS_SHORTSLOT_ON; - shortslot_restrict = true; - ofdm_basic = true; - preamble = BRCMS_PLCP_SHORT; - preamble_restrict = true; - break; - - default: - /* Error */ - brcms_err(wlc->hw->d11core, "wl%d: %s: invalid gmode %d\n", - wlc->pub->unit, __func__, gmode); - return -ENOTSUPP; - } - - band->gmode = gmode; - - wlc->shortslot_override = shortslot; - - /* Use the default 11g rateset */ - if (!rs.count) - brcms_c_rateset_copy(&cck_ofdm_rates, &rs); - - if (ofdm_basic) { - for (i = 0; i < rs.count; i++) { - if (rs.rates[i] == BRCM_RATE_6M - || rs.rates[i] == BRCM_RATE_12M - || rs.rates[i] == BRCM_RATE_24M) - rs.rates[i] |= BRCMS_RATE_FLAG; - } - } - - /* Set default bss rateset */ - wlc->default_bss->rateset.count = rs.count; - memcpy(wlc->default_bss->rateset.rates, rs.rates, - sizeof(wlc->default_bss->rateset.rates)); - - return ret; -} - -int brcms_c_set_nmode(struct brcms_c_info *wlc) -{ - uint i; - s32 nmode = AUTO; - - if (wlc->stf->txstreams == WL_11N_3x3) - nmode = WL_11N_3x3; - else - nmode = WL_11N_2x2; - - /* force GMODE_AUTO if NMODE is ON */ - brcms_c_set_gmode(wlc, GMODE_AUTO, true); - if (nmode == WL_11N_3x3) - wlc->pub->_n_enab = SUPPORT_HT; - else - wlc->pub->_n_enab = SUPPORT_11N; - wlc->default_bss->flags |= BRCMS_BSS_HT; - /* add the mcs rates to the default and hw ratesets */ - brcms_c_rateset_mcs_build(&wlc->default_bss->rateset, - wlc->stf->txstreams); - for (i = 0; i < wlc->pub->_nbands; i++) - memcpy(wlc->bandstate[i]->hw_rateset.mcs, - wlc->default_bss->rateset.mcs, MCSSET_LEN); - - return 0; -} - -static int -brcms_c_set_internal_rateset(struct brcms_c_info *wlc, - struct brcms_c_rateset *rs_arg) -{ - struct brcms_c_rateset rs, new; - uint bandunit; - - memcpy(&rs, rs_arg, sizeof(struct brcms_c_rateset)); - - /* check for bad count value */ - if ((rs.count == 0) || (rs.count > BRCMS_NUMRATES)) - return -EINVAL; - - /* try the current band */ - bandunit = wlc->band->bandunit; - memcpy(&new, &rs, sizeof(struct brcms_c_rateset)); - if (brcms_c_rate_hwrs_filter_sort_validate - (&new, &wlc->bandstate[bandunit]->hw_rateset, true, - wlc->stf->txstreams)) - goto good; - - /* try the other band */ - if (brcms_is_mband_unlocked(wlc)) { - bandunit = OTHERBANDUNIT(wlc); - memcpy(&new, &rs, sizeof(struct brcms_c_rateset)); - if (brcms_c_rate_hwrs_filter_sort_validate(&new, - &wlc-> - bandstate[bandunit]-> - hw_rateset, true, - wlc->stf->txstreams)) - goto good; - } - - return -EBADE; - - good: - /* apply new rateset */ - memcpy(&wlc->default_bss->rateset, &new, - sizeof(struct brcms_c_rateset)); - memcpy(&wlc->bandstate[bandunit]->defrateset, &new, - sizeof(struct brcms_c_rateset)); - return 0; -} - -static void brcms_c_ofdm_rateset_war(struct brcms_c_info *wlc) -{ - u8 r; - bool war = false; - - if (wlc->pub->associated) - r = wlc->bsscfg->current_bss->rateset.rates[0]; - else - r = wlc->default_bss->rateset.rates[0]; - - wlc_phy_ofdm_rateset_war(wlc->band->pi, war); -} - -int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel) -{ - u16 chspec = ch20mhz_chspec(channel); - - if (channel < 0 || channel > MAXCHANNEL) - return -EINVAL; - - if (!brcms_c_valid_chanspec_db(wlc->cmi, chspec)) - return -EINVAL; - - - if (!wlc->pub->up && brcms_is_mband_unlocked(wlc)) { - if (wlc->band->bandunit != chspec_bandunit(chspec)) - wlc->bandinit_pending = true; - else - wlc->bandinit_pending = false; - } - - wlc->default_bss->chanspec = chspec; - /* brcms_c_BSSinit() will sanitize the rateset before - * using it.. */ - if (wlc->pub->up && (wlc_phy_chanspec_get(wlc->band->pi) != chspec)) { - brcms_c_set_home_chanspec(wlc, chspec); - brcms_c_suspend_mac_and_wait(wlc); - brcms_c_set_chanspec(wlc, chspec); - brcms_c_enable_mac(wlc); - } - return 0; -} - -int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl) -{ - int ac; - - if (srl < 1 || srl > RETRY_SHORT_MAX || - lrl < 1 || lrl > RETRY_SHORT_MAX) - return -EINVAL; - - wlc->SRL = srl; - wlc->LRL = lrl; - - brcms_b_retrylimit_upd(wlc->hw, wlc->SRL, wlc->LRL); - - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], - EDCF_SHORT, wlc->SRL); - wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], - EDCF_LONG, wlc->LRL); - } - brcms_c_wme_retries_write(wlc); - - return 0; -} - -void brcms_c_get_current_rateset(struct brcms_c_info *wlc, - struct brcm_rateset *currs) -{ - struct brcms_c_rateset *rs; - - if (wlc->pub->associated) - rs = &wlc->bsscfg->current_bss->rateset; - else - rs = &wlc->default_bss->rateset; - - /* Copy only legacy rateset section */ - currs->count = rs->count; - memcpy(&currs->rates, &rs->rates, rs->count); -} - -int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs) -{ - struct brcms_c_rateset internal_rs; - int bcmerror; - - if (rs->count > BRCMS_NUMRATES) - return -ENOBUFS; - - memset(&internal_rs, 0, sizeof(internal_rs)); - - /* Copy only legacy rateset section */ - internal_rs.count = rs->count; - memcpy(&internal_rs.rates, &rs->rates, internal_rs.count); - - /* merge rateset coming in with the current mcsset */ - if (wlc->pub->_n_enab & SUPPORT_11N) { - struct brcms_bss_info *mcsset_bss; - if (wlc->pub->associated) - mcsset_bss = wlc->bsscfg->current_bss; - else - mcsset_bss = wlc->default_bss; - memcpy(internal_rs.mcs, &mcsset_bss->rateset.mcs[0], - MCSSET_LEN); - } - - bcmerror = brcms_c_set_internal_rateset(wlc, &internal_rs); - if (!bcmerror) - brcms_c_ofdm_rateset_war(wlc); - - return bcmerror; -} - -static void brcms_c_time_lock(struct brcms_c_info *wlc) -{ - bcma_set32(wlc->hw->d11core, D11REGOFFS(maccontrol), MCTL_TBTTHOLD); - /* Commit the write */ - bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); -} - -static void brcms_c_time_unlock(struct brcms_c_info *wlc) -{ - bcma_mask32(wlc->hw->d11core, D11REGOFFS(maccontrol), ~MCTL_TBTTHOLD); - /* Commit the write */ - bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); -} - -int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period) -{ - u32 bcnint_us; - - if (period == 0) - return -EINVAL; - - wlc->default_bss->beacon_period = period; - - bcnint_us = period << 10; - brcms_c_time_lock(wlc); - bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_cfprep), - (bcnint_us << CFPREP_CBI_SHIFT)); - bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_cfpstart), bcnint_us); - brcms_c_time_unlock(wlc); - - return 0; -} - -u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx) -{ - return wlc->band->phytype; -} - -void brcms_c_set_shortslot_override(struct brcms_c_info *wlc, s8 sslot_override) -{ - wlc->shortslot_override = sslot_override; - - /* - * shortslot is an 11g feature, so no more work if we are - * currently on the 5G band - */ - if (wlc->band->bandtype == BRCM_BAND_5G) - return; - - if (wlc->pub->up && wlc->pub->associated) { - /* let watchdog or beacon processing update shortslot */ - } else if (wlc->pub->up) { - /* unassociated shortslot is off */ - brcms_c_switch_shortslot(wlc, false); - } else { - /* driver is down, so just update the brcms_c_info - * value */ - if (wlc->shortslot_override == BRCMS_SHORTSLOT_AUTO) - wlc->shortslot = false; - else - wlc->shortslot = - (wlc->shortslot_override == - BRCMS_SHORTSLOT_ON); - } -} - -/* - * register watchdog and down handlers. - */ -int brcms_c_module_register(struct brcms_pub *pub, - const char *name, struct brcms_info *hdl, - int (*d_fn)(void *handle)) -{ - struct brcms_c_info *wlc = (struct brcms_c_info *) pub->wlc; - int i; - - /* find an empty entry and just add, no duplication check! */ - for (i = 0; i < BRCMS_MAXMODULES; i++) { - if (wlc->modulecb[i].name[0] == '\0') { - strncpy(wlc->modulecb[i].name, name, - sizeof(wlc->modulecb[i].name) - 1); - wlc->modulecb[i].hdl = hdl; - wlc->modulecb[i].down_fn = d_fn; - return 0; - } - } - - return -ENOSR; -} - -/* unregister module callbacks */ -int brcms_c_module_unregister(struct brcms_pub *pub, const char *name, - struct brcms_info *hdl) -{ - struct brcms_c_info *wlc = (struct brcms_c_info *) pub->wlc; - int i; - - if (wlc == NULL) - return -ENODATA; - - for (i = 0; i < BRCMS_MAXMODULES; i++) { - if (!strcmp(wlc->modulecb[i].name, name) && - (wlc->modulecb[i].hdl == hdl)) { - memset(&wlc->modulecb[i], 0, sizeof(wlc->modulecb[i])); - return 0; - } - } - - /* table not found! */ - return -ENODATA; -} - -static bool brcms_c_chipmatch_pci(struct bcma_device *core) -{ - struct pci_dev *pcidev = core->bus->host_pci; - u16 vendor = pcidev->vendor; - u16 device = pcidev->device; - - if (vendor != PCI_VENDOR_ID_BROADCOM) { - pr_err("unknown vendor id %04x\n", vendor); - return false; - } - - if (device == BCM43224_D11N_ID_VEN1 || device == BCM43224_CHIP_ID) - return true; - if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID)) - return true; - if (device == BCM4313_D11N2G_ID || device == BCM4313_CHIP_ID) - return true; - if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID)) - return true; - - pr_err("unknown device id %04x\n", device); - return false; -} - -static bool brcms_c_chipmatch_soc(struct bcma_device *core) -{ - struct bcma_chipinfo *chipinfo = &core->bus->chipinfo; - - if (chipinfo->id == BCMA_CHIP_ID_BCM4716) - return true; - - pr_err("unknown chip id %04x\n", chipinfo->id); - return false; -} - -bool brcms_c_chipmatch(struct bcma_device *core) -{ - switch (core->bus->hosttype) { - case BCMA_HOSTTYPE_PCI: - return brcms_c_chipmatch_pci(core); - case BCMA_HOSTTYPE_SOC: - return brcms_c_chipmatch_soc(core); - default: - pr_err("unknown host type: %i\n", core->bus->hosttype); - return false; - } -} - -u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate) -{ - u16 table_ptr; - u8 phy_rate, index; - - /* get the phy specific rate encoding for the PLCP SIGNAL field */ - if (is_ofdm_rate(rate)) - table_ptr = M_RT_DIRMAP_A; - else - table_ptr = M_RT_DIRMAP_B; - - /* for a given rate, the LS-nibble of the PLCP SIGNAL field is - * the index into the rate table. - */ - phy_rate = rate_info[rate] & BRCMS_RATE_MASK; - index = phy_rate & 0xf; - - /* Find the SHM pointer to the rate table entry by looking in the - * Direct-map Table - */ - return 2 * brcms_b_read_shm(wlc_hw, table_ptr + (index * 2)); -} - -/* - * bcmc_fid_generate: - * Generate frame ID for a BCMC packet. The frag field is not used - * for MC frames so is used as part of the sequence number. - */ -static inline u16 -bcmc_fid_generate(struct brcms_c_info *wlc, struct brcms_bss_cfg *bsscfg, - struct d11txh *txh) -{ - u16 frameid; - - frameid = le16_to_cpu(txh->TxFrameID) & ~(TXFID_SEQ_MASK | - TXFID_QUEUE_MASK); - frameid |= - (((wlc-> - mc_fid_counter++) << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) | - TX_BCMC_FIFO; - - return frameid; -} - -static uint -brcms_c_calc_ack_time(struct brcms_c_info *wlc, u32 rspec, - u8 preamble_type) -{ - uint dur = 0; - - /* - * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that - * is less than or equal to the rate of the immediately previous - * frame in the FES - */ - rspec = brcms_basic_rate(wlc, rspec); - /* ACK frame len == 14 == 2(fc) + 2(dur) + 6(ra) + 4(fcs) */ - dur = - brcms_c_calc_frame_time(wlc, rspec, preamble_type, - (DOT11_ACK_LEN + FCS_LEN)); - return dur; -} - -static uint -brcms_c_calc_cts_time(struct brcms_c_info *wlc, u32 rspec, - u8 preamble_type) -{ - return brcms_c_calc_ack_time(wlc, rspec, preamble_type); -} - -static uint -brcms_c_calc_ba_time(struct brcms_c_info *wlc, u32 rspec, - u8 preamble_type) -{ - /* - * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that - * is less than or equal to the rate of the immediately previous - * frame in the FES - */ - rspec = brcms_basic_rate(wlc, rspec); - /* BA len == 32 == 16(ctl hdr) + 4(ba len) + 8(bitmap) + 4(fcs) */ - return brcms_c_calc_frame_time(wlc, rspec, preamble_type, - (DOT11_BA_LEN + DOT11_BA_BITMAP_LEN + - FCS_LEN)); -} - -/* brcms_c_compute_frame_dur() - * - * Calculate the 802.11 MAC header DUR field for MPDU - * DUR for a single frame = 1 SIFS + 1 ACK - * DUR for a frame with following frags = 3 SIFS + 2 ACK + next frag time - * - * rate MPDU rate in unit of 500kbps - * next_frag_len next MPDU length in bytes - * preamble_type use short/GF or long/MM PLCP header - */ -static u16 -brcms_c_compute_frame_dur(struct brcms_c_info *wlc, u32 rate, - u8 preamble_type, uint next_frag_len) -{ - u16 dur, sifs; - - sifs = get_sifs(wlc->band); - - dur = sifs; - dur += (u16) brcms_c_calc_ack_time(wlc, rate, preamble_type); - - if (next_frag_len) { - /* Double the current DUR to get 2 SIFS + 2 ACKs */ - dur *= 2; - /* add another SIFS and the frag time */ - dur += sifs; - dur += - (u16) brcms_c_calc_frame_time(wlc, rate, preamble_type, - next_frag_len); - } - return dur; -} - -/* The opposite of brcms_c_calc_frame_time */ -static uint -brcms_c_calc_frame_len(struct brcms_c_info *wlc, u32 ratespec, - u8 preamble_type, uint dur) -{ - uint nsyms, mac_len, Ndps, kNdps; - uint rate = rspec2rate(ratespec); - - if (is_mcs_rate(ratespec)) { - uint mcs = ratespec & RSPEC_RATE_MASK; - int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); - dur -= PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT); - /* payload calculation matches that of regular ofdm */ - if (wlc->band->bandtype == BRCM_BAND_2G) - dur -= DOT11_OFDM_SIGNAL_EXTENSION; - /* kNdbps = kbps * 4 */ - kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), - rspec_issgi(ratespec)) * 4; - nsyms = dur / APHY_SYMBOL_TIME; - mac_len = - ((nsyms * kNdps) - - ((APHY_SERVICE_NBITS + APHY_TAIL_NBITS) * 1000)) / 8000; - } else if (is_ofdm_rate(ratespec)) { - dur -= APHY_PREAMBLE_TIME; - dur -= APHY_SIGNAL_TIME; - /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */ - Ndps = rate * 2; - nsyms = dur / APHY_SYMBOL_TIME; - mac_len = - ((nsyms * Ndps) - - (APHY_SERVICE_NBITS + APHY_TAIL_NBITS)) / 8; - } else { - if (preamble_type & BRCMS_SHORT_PREAMBLE) - dur -= BPHY_PLCP_SHORT_TIME; - else - dur -= BPHY_PLCP_TIME; - mac_len = dur * rate; - /* divide out factor of 2 in rate (1/2 mbps) */ - mac_len = mac_len / 8 / 2; - } - return mac_len; -} - -/* - * Return true if the specified rate is supported by the specified band. - * BRCM_BAND_AUTO indicates the current band. - */ -static bool brcms_c_valid_rate(struct brcms_c_info *wlc, u32 rspec, int band, - bool verbose) -{ - struct brcms_c_rateset *hw_rateset; - uint i; - - if ((band == BRCM_BAND_AUTO) || (band == wlc->band->bandtype)) - hw_rateset = &wlc->band->hw_rateset; - else if (wlc->pub->_nbands > 1) - hw_rateset = &wlc->bandstate[OTHERBANDUNIT(wlc)]->hw_rateset; - else - /* other band specified and we are a single band device */ - return false; - - /* check if this is a mimo rate */ - if (is_mcs_rate(rspec)) { - if ((rspec & RSPEC_RATE_MASK) >= MCS_TABLE_SIZE) - goto error; - - return isset(hw_rateset->mcs, (rspec & RSPEC_RATE_MASK)); - } - - for (i = 0; i < hw_rateset->count; i++) - if (hw_rateset->rates[i] == rspec2rate(rspec)) - return true; - error: - if (verbose) - brcms_err(wlc->hw->d11core, "wl%d: valid_rate: rate spec 0x%x " - "not in hw_rateset\n", wlc->pub->unit, rspec); - - return false; -} - -static u32 -mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band, - u32 int_val) -{ - struct bcma_device *core = wlc->hw->d11core; - u8 stf = (int_val & NRATE_STF_MASK) >> NRATE_STF_SHIFT; - u8 rate = int_val & NRATE_RATE_MASK; - u32 rspec; - bool ismcs = ((int_val & NRATE_MCS_INUSE) == NRATE_MCS_INUSE); - bool issgi = ((int_val & NRATE_SGI_MASK) >> NRATE_SGI_SHIFT); - bool override_mcs_only = ((int_val & NRATE_OVERRIDE_MCS_ONLY) - == NRATE_OVERRIDE_MCS_ONLY); - int bcmerror = 0; - - if (!ismcs) - return (u32) rate; - - /* validate the combination of rate/mcs/stf is allowed */ - if ((wlc->pub->_n_enab & SUPPORT_11N) && ismcs) { - /* mcs only allowed when nmode */ - if (stf > PHY_TXC1_MODE_SDM) { - brcms_err(core, "wl%d: %s: Invalid stf\n", - wlc->pub->unit, __func__); - bcmerror = -EINVAL; - goto done; - } - - /* mcs 32 is a special case, DUP mode 40 only */ - if (rate == 32) { - if (!CHSPEC_IS40(wlc->home_chanspec) || - ((stf != PHY_TXC1_MODE_SISO) - && (stf != PHY_TXC1_MODE_CDD))) { - brcms_err(core, "wl%d: %s: Invalid mcs 32\n", - wlc->pub->unit, __func__); - bcmerror = -EINVAL; - goto done; - } - /* mcs > 7 must use stf SDM */ - } else if (rate > HIGHEST_SINGLE_STREAM_MCS) { - /* mcs > 7 must use stf SDM */ - if (stf != PHY_TXC1_MODE_SDM) { - brcms_dbg_mac80211(core, "wl%d: enabling " - "SDM mode for mcs %d\n", - wlc->pub->unit, rate); - stf = PHY_TXC1_MODE_SDM; - } - } else { - /* - * MCS 0-7 may use SISO, CDD, and for - * phy_rev >= 3 STBC - */ - if ((stf > PHY_TXC1_MODE_STBC) || - (!BRCMS_STBC_CAP_PHY(wlc) - && (stf == PHY_TXC1_MODE_STBC))) { - brcms_err(core, "wl%d: %s: Invalid STBC\n", - wlc->pub->unit, __func__); - bcmerror = -EINVAL; - goto done; - } - } - } else if (is_ofdm_rate(rate)) { - if ((stf != PHY_TXC1_MODE_CDD) && (stf != PHY_TXC1_MODE_SISO)) { - brcms_err(core, "wl%d: %s: Invalid OFDM\n", - wlc->pub->unit, __func__); - bcmerror = -EINVAL; - goto done; - } - } else if (is_cck_rate(rate)) { - if ((cur_band->bandtype != BRCM_BAND_2G) - || (stf != PHY_TXC1_MODE_SISO)) { - brcms_err(core, "wl%d: %s: Invalid CCK\n", - wlc->pub->unit, __func__); - bcmerror = -EINVAL; - goto done; - } - } else { - brcms_err(core, "wl%d: %s: Unknown rate type\n", - wlc->pub->unit, __func__); - bcmerror = -EINVAL; - goto done; - } - /* make sure multiple antennae are available for non-siso rates */ - if ((stf != PHY_TXC1_MODE_SISO) && (wlc->stf->txstreams == 1)) { - brcms_err(core, "wl%d: %s: SISO antenna but !SISO " - "request\n", wlc->pub->unit, __func__); - bcmerror = -EINVAL; - goto done; - } - - rspec = rate; - if (ismcs) { - rspec |= RSPEC_MIMORATE; - /* For STBC populate the STC field of the ratespec */ - if (stf == PHY_TXC1_MODE_STBC) { - u8 stc; - stc = 1; /* Nss for single stream is always 1 */ - rspec |= (stc << RSPEC_STC_SHIFT); - } - } - - rspec |= (stf << RSPEC_STF_SHIFT); - - if (override_mcs_only) - rspec |= RSPEC_OVERRIDE_MCS_ONLY; - - if (issgi) - rspec |= RSPEC_SHORT_GI; - - if ((rate != 0) - && !brcms_c_valid_rate(wlc, rspec, cur_band->bandtype, true)) - return rate; - - return rspec; -done: - return rate; -} - -/* - * Compute PLCP, but only requires actual rate and length of pkt. - * Rate is given in the driver standard multiple of 500 kbps. - * le is set for 11 Mbps rate if necessary. - * Broken out for PRQ. - */ - -static void brcms_c_cck_plcp_set(struct brcms_c_info *wlc, int rate_500, - uint length, u8 *plcp) -{ - u16 usec = 0; - u8 le = 0; - - switch (rate_500) { - case BRCM_RATE_1M: - usec = length << 3; - break; - case BRCM_RATE_2M: - usec = length << 2; - break; - case BRCM_RATE_5M5: - usec = (length << 4) / 11; - if ((length << 4) - (usec * 11) > 0) - usec++; - break; - case BRCM_RATE_11M: - usec = (length << 3) / 11; - if ((length << 3) - (usec * 11) > 0) { - usec++; - if ((usec * 11) - (length << 3) >= 8) - le = D11B_PLCP_SIGNAL_LE; - } - break; - - default: - brcms_err(wlc->hw->d11core, - "brcms_c_cck_plcp_set: unsupported rate %d\n", - rate_500); - rate_500 = BRCM_RATE_1M; - usec = length << 3; - break; - } - /* PLCP signal byte */ - plcp[0] = rate_500 * 5; /* r (500kbps) * 5 == r (100kbps) */ - /* PLCP service byte */ - plcp[1] = (u8) (le | D11B_PLCP_SIGNAL_LOCKED); - /* PLCP length u16, little endian */ - plcp[2] = usec & 0xff; - plcp[3] = (usec >> 8) & 0xff; - /* PLCP CRC16 */ - plcp[4] = 0; - plcp[5] = 0; -} - -/* Rate: 802.11 rate code, length: PSDU length in octets */ -static void brcms_c_compute_mimo_plcp(u32 rspec, uint length, u8 *plcp) -{ - u8 mcs = (u8) (rspec & RSPEC_RATE_MASK); - plcp[0] = mcs; - if (rspec_is40mhz(rspec) || (mcs == 32)) - plcp[0] |= MIMO_PLCP_40MHZ; - BRCMS_SET_MIMO_PLCP_LEN(plcp, length); - plcp[3] = rspec_mimoplcp3(rspec); /* rspec already holds this byte */ - plcp[3] |= 0x7; /* set smoothing, not sounding ppdu & reserved */ - plcp[4] = 0; /* number of extension spatial streams bit 0 & 1 */ - plcp[5] = 0; -} - -/* Rate: 802.11 rate code, length: PSDU length in octets */ -static void -brcms_c_compute_ofdm_plcp(u32 rspec, u32 length, u8 *plcp) -{ - u8 rate_signal; - u32 tmp = 0; - int rate = rspec2rate(rspec); - - /* - * encode rate per 802.11a-1999 sec 17.3.4.1, with lsb - * transmitted first - */ - rate_signal = rate_info[rate] & BRCMS_RATE_MASK; - memset(plcp, 0, D11_PHY_HDR_LEN); - D11A_PHY_HDR_SRATE((struct ofdm_phy_hdr *) plcp, rate_signal); - - tmp = (length & 0xfff) << 5; - plcp[2] |= (tmp >> 16) & 0xff; - plcp[1] |= (tmp >> 8) & 0xff; - plcp[0] |= tmp & 0xff; -} - -/* Rate: 802.11 rate code, length: PSDU length in octets */ -static void brcms_c_compute_cck_plcp(struct brcms_c_info *wlc, u32 rspec, - uint length, u8 *plcp) -{ - int rate = rspec2rate(rspec); - - brcms_c_cck_plcp_set(wlc, rate, length, plcp); -} - -static void -brcms_c_compute_plcp(struct brcms_c_info *wlc, u32 rspec, - uint length, u8 *plcp) -{ - if (is_mcs_rate(rspec)) - brcms_c_compute_mimo_plcp(rspec, length, plcp); - else if (is_ofdm_rate(rspec)) - brcms_c_compute_ofdm_plcp(rspec, length, plcp); - else - brcms_c_compute_cck_plcp(wlc, rspec, length, plcp); -} - -/* brcms_c_compute_rtscts_dur() - * - * Calculate the 802.11 MAC header DUR field for an RTS or CTS frame - * DUR for normal RTS/CTS w/ frame = 3 SIFS + 1 CTS + next frame time + 1 ACK - * DUR for CTS-TO-SELF w/ frame = 2 SIFS + next frame time + 1 ACK - * - * cts cts-to-self or rts/cts - * rts_rate rts or cts rate in unit of 500kbps - * rate next MPDU rate in unit of 500kbps - * frame_len next MPDU frame length in bytes - */ -u16 -brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only, - u32 rts_rate, - u32 frame_rate, u8 rts_preamble_type, - u8 frame_preamble_type, uint frame_len, bool ba) -{ - u16 dur, sifs; - - sifs = get_sifs(wlc->band); - - if (!cts_only) { - /* RTS/CTS */ - dur = 3 * sifs; - dur += - (u16) brcms_c_calc_cts_time(wlc, rts_rate, - rts_preamble_type); - } else { - /* CTS-TO-SELF */ - dur = 2 * sifs; - } - - dur += - (u16) brcms_c_calc_frame_time(wlc, frame_rate, frame_preamble_type, - frame_len); - if (ba) - dur += - (u16) brcms_c_calc_ba_time(wlc, frame_rate, - BRCMS_SHORT_PREAMBLE); - else - dur += - (u16) brcms_c_calc_ack_time(wlc, frame_rate, - frame_preamble_type); - return dur; -} - -static u16 brcms_c_phytxctl1_calc(struct brcms_c_info *wlc, u32 rspec) -{ - u16 phyctl1 = 0; - u16 bw; - - if (BRCMS_ISLCNPHY(wlc->band)) { - bw = PHY_TXC1_BW_20MHZ; - } else { - bw = rspec_get_bw(rspec); - /* 10Mhz is not supported yet */ - if (bw < PHY_TXC1_BW_20MHZ) { - brcms_err(wlc->hw->d11core, "phytxctl1_calc: bw %d is " - "not supported yet, set to 20L\n", bw); - bw = PHY_TXC1_BW_20MHZ; - } - } - - if (is_mcs_rate(rspec)) { - uint mcs = rspec & RSPEC_RATE_MASK; - - /* bw, stf, coding-type is part of rspec_phytxbyte2 returns */ - phyctl1 = rspec_phytxbyte2(rspec); - /* set the upper byte of phyctl1 */ - phyctl1 |= (mcs_table[mcs].tx_phy_ctl3 << 8); - } else if (is_cck_rate(rspec) && !BRCMS_ISLCNPHY(wlc->band) - && !BRCMS_ISSSLPNPHY(wlc->band)) { - /* - * In CCK mode LPPHY overloads OFDM Modulation bits with CCK - * Data Rate. Eventually MIMOPHY would also be converted to - * this format - */ - /* 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */ - phyctl1 = (bw | (rspec_stf(rspec) << PHY_TXC1_MODE_SHIFT)); - } else { /* legacy OFDM/CCK */ - s16 phycfg; - /* get the phyctl byte from rate phycfg table */ - phycfg = brcms_c_rate_legacy_phyctl(rspec2rate(rspec)); - if (phycfg == -1) { - brcms_err(wlc->hw->d11core, "phytxctl1_calc: wrong " - "legacy OFDM/CCK rate\n"); - phycfg = 0; - } - /* set the upper byte of phyctl1 */ - phyctl1 = - (bw | (phycfg << 8) | - (rspec_stf(rspec) << PHY_TXC1_MODE_SHIFT)); - } - return phyctl1; -} - -/* - * Add struct d11txh, struct cck_phy_hdr. - * - * 'p' data must start with 802.11 MAC header - * 'p' must allow enough bytes of local headers to be "pushed" onto the packet - * - * headroom == D11_PHY_HDR_LEN + D11_TXH_LEN (D11_TXH_LEN is now 104 bytes) - * - */ -static u16 -brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw, - struct sk_buff *p, struct scb *scb, uint frag, - uint nfrags, uint queue, uint next_frag_len) -{ - struct ieee80211_hdr *h; - struct d11txh *txh; - u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN]; - int len, phylen, rts_phylen; - u16 mch, phyctl, xfts, mainrates; - u16 seq = 0, mcl = 0, status = 0, frameid = 0; - u32 rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; - u32 rts_rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; - bool use_rts = false; - bool use_cts = false; - bool use_rifs = false; - bool short_preamble[2] = { false, false }; - u8 preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; - u8 rts_preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; - u8 *rts_plcp, rts_plcp_fallback[D11_PHY_HDR_LEN]; - struct ieee80211_rts *rts = NULL; - bool qos; - uint ac; - bool hwtkmic = false; - u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; -#define ANTCFG_NONE 0xFF - u8 antcfg = ANTCFG_NONE; - u8 fbantcfg = ANTCFG_NONE; - uint phyctl1_stf = 0; - u16 durid = 0; - struct ieee80211_tx_rate *txrate[2]; - int k; - struct ieee80211_tx_info *tx_info; - bool is_mcs; - u16 mimo_txbw; - u8 mimo_preamble_type; - - /* locate 802.11 MAC header */ - h = (struct ieee80211_hdr *)(p->data); - qos = ieee80211_is_data_qos(h->frame_control); - - /* compute length of frame in bytes for use in PLCP computations */ - len = p->len; - phylen = len + FCS_LEN; - - /* Get tx_info */ - tx_info = IEEE80211_SKB_CB(p); - - /* add PLCP */ - plcp = skb_push(p, D11_PHY_HDR_LEN); - - /* add Broadcom tx descriptor header */ - txh = (struct d11txh *) skb_push(p, D11_TXH_LEN); - memset(txh, 0, D11_TXH_LEN); - - /* setup frameid */ - if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - /* non-AP STA should never use BCMC queue */ - if (queue == TX_BCMC_FIFO) { - brcms_err(wlc->hw->d11core, - "wl%d: %s: ASSERT queue == TX_BCMC!\n", - wlc->pub->unit, __func__); - frameid = bcmc_fid_generate(wlc, NULL, txh); - } else { - /* Increment the counter for first fragment */ - if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) - scb->seqnum[p->priority]++; - - /* extract fragment number from frame first */ - seq = le16_to_cpu(h->seq_ctrl) & FRAGNUM_MASK; - seq |= (scb->seqnum[p->priority] << SEQNUM_SHIFT); - h->seq_ctrl = cpu_to_le16(seq); - - frameid = ((seq << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) | - (queue & TXFID_QUEUE_MASK); - } - } - frameid |= queue & TXFID_QUEUE_MASK; - - /* set the ignpmq bit for all pkts tx'd in PS mode and for beacons */ - if (ieee80211_is_beacon(h->frame_control)) - mcl |= TXC_IGNOREPMQ; - - txrate[0] = tx_info->control.rates; - txrate[1] = txrate[0] + 1; - - /* - * if rate control algorithm didn't give us a fallback - * rate, use the primary rate - */ - if (txrate[1]->idx < 0) - txrate[1] = txrate[0]; - - for (k = 0; k < hw->max_rates; k++) { - is_mcs = txrate[k]->flags & IEEE80211_TX_RC_MCS ? true : false; - if (!is_mcs) { - if ((txrate[k]->idx >= 0) - && (txrate[k]->idx < - hw->wiphy->bands[tx_info->band]->n_bitrates)) { - rspec[k] = - hw->wiphy->bands[tx_info->band]-> - bitrates[txrate[k]->idx].hw_value; - short_preamble[k] = - txrate[k]-> - flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ? - true : false; - } else { - rspec[k] = BRCM_RATE_1M; - } - } else { - rspec[k] = mac80211_wlc_set_nrate(wlc, wlc->band, - NRATE_MCS_INUSE | txrate[k]->idx); - } - - /* - * Currently only support same setting for primay and - * fallback rates. Unify flags for each rate into a - * single value for the frame - */ - use_rts |= - txrate[k]-> - flags & IEEE80211_TX_RC_USE_RTS_CTS ? true : false; - use_cts |= - txrate[k]-> - flags & IEEE80211_TX_RC_USE_CTS_PROTECT ? true : false; - - - /* - * (1) RATE: - * determine and validate primary rate - * and fallback rates - */ - if (!rspec_active(rspec[k])) { - rspec[k] = BRCM_RATE_1M; - } else { - if (!is_multicast_ether_addr(h->addr1)) { - /* set tx antenna config */ - brcms_c_antsel_antcfg_get(wlc->asi, false, - false, 0, 0, &antcfg, &fbantcfg); - } - } - } - - phyctl1_stf = wlc->stf->ss_opmode; - - if (wlc->pub->_n_enab & SUPPORT_11N) { - for (k = 0; k < hw->max_rates; k++) { - /* - * apply siso/cdd to single stream mcs's or ofdm - * if rspec is auto selected - */ - if (((is_mcs_rate(rspec[k]) && - is_single_stream(rspec[k] & RSPEC_RATE_MASK)) || - is_ofdm_rate(rspec[k])) - && ((rspec[k] & RSPEC_OVERRIDE_MCS_ONLY) - || !(rspec[k] & RSPEC_OVERRIDE))) { - rspec[k] &= ~(RSPEC_STF_MASK | RSPEC_STC_MASK); - - /* For SISO MCS use STBC if possible */ - if (is_mcs_rate(rspec[k]) - && BRCMS_STF_SS_STBC_TX(wlc, scb)) { - u8 stc; - - /* Nss for single stream is always 1 */ - stc = 1; - rspec[k] |= (PHY_TXC1_MODE_STBC << - RSPEC_STF_SHIFT) | - (stc << RSPEC_STC_SHIFT); - } else - rspec[k] |= - (phyctl1_stf << RSPEC_STF_SHIFT); - } - - /* - * Is the phy configured to use 40MHZ frames? If - * so then pick the desired txbw - */ - if (brcms_chspec_bw(wlc->chanspec) == BRCMS_40_MHZ) { - /* default txbw is 20in40 SB */ - mimo_ctlchbw = mimo_txbw = - CHSPEC_SB_UPPER(wlc_phy_chanspec_get( - wlc->band->pi)) - ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ; - - if (is_mcs_rate(rspec[k])) { - /* mcs 32 must be 40b/w DUP */ - if ((rspec[k] & RSPEC_RATE_MASK) - == 32) { - mimo_txbw = - PHY_TXC1_BW_40MHZ_DUP; - /* use override */ - } else if (wlc->mimo_40txbw != AUTO) - mimo_txbw = wlc->mimo_40txbw; - /* else check if dst is using 40 Mhz */ - else if (scb->flags & SCB_IS40) - mimo_txbw = PHY_TXC1_BW_40MHZ; - } else if (is_ofdm_rate(rspec[k])) { - if (wlc->ofdm_40txbw != AUTO) - mimo_txbw = wlc->ofdm_40txbw; - } else if (wlc->cck_40txbw != AUTO) { - mimo_txbw = wlc->cck_40txbw; - } - } else { - /* - * mcs32 is 40 b/w only. - * This is possible for probe packets on - * a STA during SCAN - */ - if ((rspec[k] & RSPEC_RATE_MASK) == 32) - /* mcs 0 */ - rspec[k] = RSPEC_MIMORATE; - - mimo_txbw = PHY_TXC1_BW_20MHZ; - } - - /* Set channel width */ - rspec[k] &= ~RSPEC_BW_MASK; - if ((k == 0) || ((k > 0) && is_mcs_rate(rspec[k]))) - rspec[k] |= (mimo_txbw << RSPEC_BW_SHIFT); - else - rspec[k] |= (mimo_ctlchbw << RSPEC_BW_SHIFT); - - /* Disable short GI, not supported yet */ - rspec[k] &= ~RSPEC_SHORT_GI; - - mimo_preamble_type = BRCMS_MM_PREAMBLE; - if (txrate[k]->flags & IEEE80211_TX_RC_GREEN_FIELD) - mimo_preamble_type = BRCMS_GF_PREAMBLE; - - if ((txrate[k]->flags & IEEE80211_TX_RC_MCS) - && (!is_mcs_rate(rspec[k]))) { - brcms_warn(wlc->hw->d11core, - "wl%d: %s: IEEE80211_TX_RC_MCS != is_mcs_rate(rspec)\n", - wlc->pub->unit, __func__); - } - - if (is_mcs_rate(rspec[k])) { - preamble_type[k] = mimo_preamble_type; - - /* - * if SGI is selected, then forced mm - * for single stream - */ - if ((rspec[k] & RSPEC_SHORT_GI) - && is_single_stream(rspec[k] & - RSPEC_RATE_MASK)) - preamble_type[k] = BRCMS_MM_PREAMBLE; - } - - /* should be better conditionalized */ - if (!is_mcs_rate(rspec[0]) - && (tx_info->control.rates[0]. - flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)) - preamble_type[k] = BRCMS_SHORT_PREAMBLE; - } - } else { - for (k = 0; k < hw->max_rates; k++) { - /* Set ctrlchbw as 20Mhz */ - rspec[k] &= ~RSPEC_BW_MASK; - rspec[k] |= (PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT); - - /* for nphy, stf of ofdm frames must follow policies */ - if (BRCMS_ISNPHY(wlc->band) && is_ofdm_rate(rspec[k])) { - rspec[k] &= ~RSPEC_STF_MASK; - rspec[k] |= phyctl1_stf << RSPEC_STF_SHIFT; - } - } - } - - /* Reset these for use with AMPDU's */ - txrate[0]->count = 0; - txrate[1]->count = 0; - - /* (2) PROTECTION, may change rspec */ - if ((ieee80211_is_data(h->frame_control) || - ieee80211_is_mgmt(h->frame_control)) && - (phylen > wlc->RTSThresh) && !is_multicast_ether_addr(h->addr1)) - use_rts = true; - - /* (3) PLCP: determine PLCP header and MAC duration, - * fill struct d11txh */ - brcms_c_compute_plcp(wlc, rspec[0], phylen, plcp); - brcms_c_compute_plcp(wlc, rspec[1], phylen, plcp_fallback); - memcpy(&txh->FragPLCPFallback, - plcp_fallback, sizeof(txh->FragPLCPFallback)); - - /* Length field now put in CCK FBR CRC field */ - if (is_cck_rate(rspec[1])) { - txh->FragPLCPFallback[4] = phylen & 0xff; - txh->FragPLCPFallback[5] = (phylen & 0xff00) >> 8; - } - - /* MIMO-RATE: need validation ?? */ - mainrates = is_ofdm_rate(rspec[0]) ? - D11A_PHY_HDR_GRATE((struct ofdm_phy_hdr *) plcp) : - plcp[0]; - - /* DUR field for main rate */ - if (!ieee80211_is_pspoll(h->frame_control) && - !is_multicast_ether_addr(h->addr1) && !use_rifs) { - durid = - brcms_c_compute_frame_dur(wlc, rspec[0], preamble_type[0], - next_frag_len); - h->duration_id = cpu_to_le16(durid); - } else if (use_rifs) { - /* NAV protect to end of next max packet size */ - durid = - (u16) brcms_c_calc_frame_time(wlc, rspec[0], - preamble_type[0], - DOT11_MAX_FRAG_LEN); - durid += RIFS_11N_TIME; - h->duration_id = cpu_to_le16(durid); - } - - /* DUR field for fallback rate */ - if (ieee80211_is_pspoll(h->frame_control)) - txh->FragDurFallback = h->duration_id; - else if (is_multicast_ether_addr(h->addr1) || use_rifs) - txh->FragDurFallback = 0; - else { - durid = brcms_c_compute_frame_dur(wlc, rspec[1], - preamble_type[1], next_frag_len); - txh->FragDurFallback = cpu_to_le16(durid); - } - - /* (4) MAC-HDR: MacTxControlLow */ - if (frag == 0) - mcl |= TXC_STARTMSDU; - - if (!is_multicast_ether_addr(h->addr1)) - mcl |= TXC_IMMEDACK; - - if (wlc->band->bandtype == BRCM_BAND_5G) - mcl |= TXC_FREQBAND_5G; - - if (CHSPEC_IS40(wlc_phy_chanspec_get(wlc->band->pi))) - mcl |= TXC_BW_40; - - /* set AMIC bit if using hardware TKIP MIC */ - if (hwtkmic) - mcl |= TXC_AMIC; - - txh->MacTxControlLow = cpu_to_le16(mcl); - - /* MacTxControlHigh */ - mch = 0; - - /* Set fallback rate preamble type */ - if ((preamble_type[1] == BRCMS_SHORT_PREAMBLE) || - (preamble_type[1] == BRCMS_GF_PREAMBLE)) { - if (rspec2rate(rspec[1]) != BRCM_RATE_1M) - mch |= TXC_PREAMBLE_DATA_FB_SHORT; - } - - /* MacFrameControl */ - memcpy(&txh->MacFrameControl, &h->frame_control, sizeof(u16)); - txh->TxFesTimeNormal = cpu_to_le16(0); - - txh->TxFesTimeFallback = cpu_to_le16(0); - - /* TxFrameRA */ - memcpy(&txh->TxFrameRA, &h->addr1, ETH_ALEN); - - /* TxFrameID */ - txh->TxFrameID = cpu_to_le16(frameid); - - /* - * TxStatus, Note the case of recreating the first frag of a suppressed - * frame then we may need to reset the retry cnt's via the status reg - */ - txh->TxStatus = cpu_to_le16(status); - - /* - * extra fields for ucode AMPDU aggregation, the new fields are added to - * the END of previous structure so that it's compatible in driver. - */ - txh->MaxNMpdus = cpu_to_le16(0); - txh->MaxABytes_MRT = cpu_to_le16(0); - txh->MaxABytes_FBR = cpu_to_le16(0); - txh->MinMBytes = cpu_to_le16(0); - - /* (5) RTS/CTS: determine RTS/CTS PLCP header and MAC duration, - * furnish struct d11txh */ - /* RTS PLCP header and RTS frame */ - if (use_rts || use_cts) { - if (use_rts && use_cts) - use_cts = false; - - for (k = 0; k < 2; k++) { - rts_rspec[k] = brcms_c_rspec_to_rts_rspec(wlc, rspec[k], - false, - mimo_ctlchbw); - } - - if (!is_ofdm_rate(rts_rspec[0]) && - !((rspec2rate(rts_rspec[0]) == BRCM_RATE_1M) || - (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) { - rts_preamble_type[0] = BRCMS_SHORT_PREAMBLE; - mch |= TXC_PREAMBLE_RTS_MAIN_SHORT; - } - - if (!is_ofdm_rate(rts_rspec[1]) && - !((rspec2rate(rts_rspec[1]) == BRCM_RATE_1M) || - (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) { - rts_preamble_type[1] = BRCMS_SHORT_PREAMBLE; - mch |= TXC_PREAMBLE_RTS_FB_SHORT; - } - - /* RTS/CTS additions to MacTxControlLow */ - if (use_cts) { - txh->MacTxControlLow |= cpu_to_le16(TXC_SENDCTS); - } else { - txh->MacTxControlLow |= cpu_to_le16(TXC_SENDRTS); - txh->MacTxControlLow |= cpu_to_le16(TXC_LONGFRAME); - } - - /* RTS PLCP header */ - rts_plcp = txh->RTSPhyHeader; - if (use_cts) - rts_phylen = DOT11_CTS_LEN + FCS_LEN; - else - rts_phylen = DOT11_RTS_LEN + FCS_LEN; - - brcms_c_compute_plcp(wlc, rts_rspec[0], rts_phylen, rts_plcp); - - /* fallback rate version of RTS PLCP header */ - brcms_c_compute_plcp(wlc, rts_rspec[1], rts_phylen, - rts_plcp_fallback); - memcpy(&txh->RTSPLCPFallback, rts_plcp_fallback, - sizeof(txh->RTSPLCPFallback)); - - /* RTS frame fields... */ - rts = (struct ieee80211_rts *)&txh->rts_frame; - - durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec[0], - rspec[0], rts_preamble_type[0], - preamble_type[0], phylen, false); - rts->duration = cpu_to_le16(durid); - /* fallback rate version of RTS DUR field */ - durid = brcms_c_compute_rtscts_dur(wlc, use_cts, - rts_rspec[1], rspec[1], - rts_preamble_type[1], - preamble_type[1], phylen, false); - txh->RTSDurFallback = cpu_to_le16(durid); - - if (use_cts) { - rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_CTS); - - memcpy(&rts->ra, &h->addr2, ETH_ALEN); - } else { - rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_RTS); - - memcpy(&rts->ra, &h->addr1, 2 * ETH_ALEN); - } - - /* mainrate - * low 8 bits: main frag rate/mcs, - * high 8 bits: rts/cts rate/mcs - */ - mainrates |= (is_ofdm_rate(rts_rspec[0]) ? - D11A_PHY_HDR_GRATE( - (struct ofdm_phy_hdr *) rts_plcp) : - rts_plcp[0]) << 8; - } else { - memset(txh->RTSPhyHeader, 0, D11_PHY_HDR_LEN); - memset(&txh->rts_frame, 0, sizeof(struct ieee80211_rts)); - memset(txh->RTSPLCPFallback, 0, sizeof(txh->RTSPLCPFallback)); - txh->RTSDurFallback = 0; - } - -#ifdef SUPPORT_40MHZ - /* add null delimiter count */ - if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && is_mcs_rate(rspec)) - txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = - brcm_c_ampdu_null_delim_cnt(wlc->ampdu, scb, rspec, phylen); - -#endif - - /* - * Now that RTS/RTS FB preamble types are updated, write - * the final value - */ - txh->MacTxControlHigh = cpu_to_le16(mch); - - /* - * MainRates (both the rts and frag plcp rates have - * been calculated now) - */ - txh->MainRates = cpu_to_le16(mainrates); - - /* XtraFrameTypes */ - xfts = frametype(rspec[1], wlc->mimoft); - xfts |= (frametype(rts_rspec[0], wlc->mimoft) << XFTS_RTS_FT_SHIFT); - xfts |= (frametype(rts_rspec[1], wlc->mimoft) << XFTS_FBRRTS_FT_SHIFT); - xfts |= CHSPEC_CHANNEL(wlc_phy_chanspec_get(wlc->band->pi)) << - XFTS_CHANNEL_SHIFT; - txh->XtraFrameTypes = cpu_to_le16(xfts); - - /* PhyTxControlWord */ - phyctl = frametype(rspec[0], wlc->mimoft); - if ((preamble_type[0] == BRCMS_SHORT_PREAMBLE) || - (preamble_type[0] == BRCMS_GF_PREAMBLE)) { - if (rspec2rate(rspec[0]) != BRCM_RATE_1M) - phyctl |= PHY_TXC_SHORT_HDR; - } - - /* phytxant is properly bit shifted */ - phyctl |= brcms_c_stf_d11hdrs_phyctl_txant(wlc, rspec[0]); - txh->PhyTxControlWord = cpu_to_le16(phyctl); - - /* PhyTxControlWord_1 */ - if (BRCMS_PHY_11N_CAP(wlc->band)) { - u16 phyctl1 = 0; - - phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[0]); - txh->PhyTxControlWord_1 = cpu_to_le16(phyctl1); - phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[1]); - txh->PhyTxControlWord_1_Fbr = cpu_to_le16(phyctl1); - - if (use_rts || use_cts) { - phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[0]); - txh->PhyTxControlWord_1_Rts = cpu_to_le16(phyctl1); - phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[1]); - txh->PhyTxControlWord_1_FbrRts = cpu_to_le16(phyctl1); - } - - /* - * For mcs frames, if mixedmode(overloaded with long preamble) - * is going to be set, fill in non-zero MModeLen and/or - * MModeFbrLen it will be unnecessary if they are separated - */ - if (is_mcs_rate(rspec[0]) && - (preamble_type[0] == BRCMS_MM_PREAMBLE)) { - u16 mmodelen = - brcms_c_calc_lsig_len(wlc, rspec[0], phylen); - txh->MModeLen = cpu_to_le16(mmodelen); - } - - if (is_mcs_rate(rspec[1]) && - (preamble_type[1] == BRCMS_MM_PREAMBLE)) { - u16 mmodefbrlen = - brcms_c_calc_lsig_len(wlc, rspec[1], phylen); - txh->MModeFbrLen = cpu_to_le16(mmodefbrlen); - } - } - - ac = skb_get_queue_mapping(p); - if ((scb->flags & SCB_WMECAP) && qos && wlc->edcf_txop[ac]) { - uint frag_dur, dur, dur_fallback; - - /* WME: Update TXOP threshold */ - if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU) && frag == 0) { - frag_dur = - brcms_c_calc_frame_time(wlc, rspec[0], - preamble_type[0], phylen); - - if (rts) { - /* 1 RTS or CTS-to-self frame */ - dur = - brcms_c_calc_cts_time(wlc, rts_rspec[0], - rts_preamble_type[0]); - dur_fallback = - brcms_c_calc_cts_time(wlc, rts_rspec[1], - rts_preamble_type[1]); - /* (SIFS + CTS) + SIFS + frame + SIFS + ACK */ - dur += le16_to_cpu(rts->duration); - dur_fallback += - le16_to_cpu(txh->RTSDurFallback); - } else if (use_rifs) { - dur = frag_dur; - dur_fallback = 0; - } else { - /* frame + SIFS + ACK */ - dur = frag_dur; - dur += - brcms_c_compute_frame_dur(wlc, rspec[0], - preamble_type[0], 0); - - dur_fallback = - brcms_c_calc_frame_time(wlc, rspec[1], - preamble_type[1], - phylen); - dur_fallback += - brcms_c_compute_frame_dur(wlc, rspec[1], - preamble_type[1], 0); - } - /* NEED to set TxFesTimeNormal (hard) */ - txh->TxFesTimeNormal = cpu_to_le16((u16) dur); - /* - * NEED to set fallback rate version of - * TxFesTimeNormal (hard) - */ - txh->TxFesTimeFallback = - cpu_to_le16((u16) dur_fallback); - - /* - * update txop byte threshold (txop minus intraframe - * overhead) - */ - if (wlc->edcf_txop[ac] >= (dur - frag_dur)) { - uint newfragthresh; - - newfragthresh = - brcms_c_calc_frame_len(wlc, - rspec[0], preamble_type[0], - (wlc->edcf_txop[ac] - - (dur - frag_dur))); - /* range bound the fragthreshold */ - if (newfragthresh < DOT11_MIN_FRAG_LEN) - newfragthresh = - DOT11_MIN_FRAG_LEN; - else if (newfragthresh > - wlc->usr_fragthresh) - newfragthresh = - wlc->usr_fragthresh; - /* update the fragthresh and do txc update */ - if (wlc->fragthresh[queue] != - (u16) newfragthresh) - wlc->fragthresh[queue] = - (u16) newfragthresh; - } else { - brcms_warn(wlc->hw->d11core, - "wl%d: %s txop invalid for rate %d\n", - wlc->pub->unit, fifo_names[queue], - rspec2rate(rspec[0])); - } - - if (dur > wlc->edcf_txop[ac]) - brcms_warn(wlc->hw->d11core, - "wl%d: %s: %s txop exceeded phylen %d/%d dur %d/%d\n", - wlc->pub->unit, __func__, - fifo_names[queue], - phylen, wlc->fragthresh[queue], - dur, wlc->edcf_txop[ac]); - } - } - - return 0; -} - -static int brcms_c_tx(struct brcms_c_info *wlc, struct sk_buff *skb) -{ - struct dma_pub *dma; - int fifo, ret = -ENOSPC; - struct d11txh *txh; - u16 frameid = INVALIDFID; - - fifo = brcms_ac_to_fifo(skb_get_queue_mapping(skb)); - dma = wlc->hw->di[fifo]; - txh = (struct d11txh *)(skb->data); - - if (dma->txavail == 0) { - /* - * We sometimes get a frame from mac80211 after stopping - * the queues. This only ever seems to be a single frame - * and is seems likely to be a race. TX_HEADROOM should - * ensure that we have enough space to handle these stray - * packets, so warn if there isn't. If we're out of space - * in the tx ring and the tx queue isn't stopped then - * we've really got a bug; warn loudly if that happens. - */ - brcms_warn(wlc->hw->d11core, - "Received frame for tx with no space in DMA ring\n"); - WARN_ON(!ieee80211_queue_stopped(wlc->pub->ieee_hw, - skb_get_queue_mapping(skb))); - return -ENOSPC; - } - - /* When a BC/MC frame is being committed to the BCMC fifo - * via DMA (NOT PIO), update ucode or BSS info as appropriate. - */ - if (fifo == TX_BCMC_FIFO) - frameid = le16_to_cpu(txh->TxFrameID); - - /* Commit BCMC sequence number in the SHM frame ID location */ - if (frameid != INVALIDFID) { - /* - * To inform the ucode of the last mcast frame posted - * so that it can clear moredata bit - */ - brcms_b_write_shm(wlc->hw, M_BCMC_FID, frameid); - } - - ret = brcms_c_txfifo(wlc, fifo, skb); - /* - * The only reason for brcms_c_txfifo to fail is because - * there weren't any DMA descriptors, but we've already - * checked for that. So if it does fail yell loudly. - */ - WARN_ON_ONCE(ret); - - return ret; -} - -bool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu, - struct ieee80211_hw *hw) -{ - uint fifo; - struct scb *scb = &wlc->pri_scb; - - fifo = brcms_ac_to_fifo(skb_get_queue_mapping(sdu)); - brcms_c_d11hdrs_mac80211(wlc, hw, sdu, scb, 0, 1, fifo, 0); - if (!brcms_c_tx(wlc, sdu)) - return true; - - /* packet discarded */ - dev_kfree_skb_any(sdu); - return false; -} - -int -brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p) -{ - struct dma_pub *dma = wlc->hw->di[fifo]; - int ret; - u16 queue; - - ret = dma_txfast(wlc, dma, p); - if (ret < 0) - wiphy_err(wlc->wiphy, "txfifo: fatal, toss frames !!!\n"); - - /* - * Stop queue if DMA ring is full. Reserve some free descriptors, - * as we sometimes receive a frame from mac80211 after the queues - * are stopped. - */ - queue = skb_get_queue_mapping(p); - if (dma->txavail <= TX_HEADROOM && fifo < TX_BCMC_FIFO && - !ieee80211_queue_stopped(wlc->pub->ieee_hw, queue)) - ieee80211_stop_queue(wlc->pub->ieee_hw, queue); - - return ret; -} - -u32 -brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, - bool use_rspec, u16 mimo_ctlchbw) -{ - u32 rts_rspec = 0; - - if (use_rspec) - /* use frame rate as rts rate */ - rts_rspec = rspec; - else if (wlc->band->gmode && wlc->protection->_g && !is_cck_rate(rspec)) - /* Use 11Mbps as the g protection RTS target rate and fallback. - * Use the brcms_basic_rate() lookup to find the best basic rate - * under the target in case 11 Mbps is not Basic. - * 6 and 9 Mbps are not usually selected by rate selection, but - * even if the OFDM rate we are protecting is 6 or 9 Mbps, 11 - * is more robust. - */ - rts_rspec = brcms_basic_rate(wlc, BRCM_RATE_11M); - else - /* calculate RTS rate and fallback rate based on the frame rate - * RTS must be sent at a basic rate since it is a - * control frame, sec 9.6 of 802.11 spec - */ - rts_rspec = brcms_basic_rate(wlc, rspec); - - if (BRCMS_PHY_11N_CAP(wlc->band)) { - /* set rts txbw to correct side band */ - rts_rspec &= ~RSPEC_BW_MASK; - - /* - * if rspec/rspec_fallback is 40MHz, then send RTS on both - * 20MHz channel (DUP), otherwise send RTS on control channel - */ - if (rspec_is40mhz(rspec) && !is_cck_rate(rts_rspec)) - rts_rspec |= (PHY_TXC1_BW_40MHZ_DUP << RSPEC_BW_SHIFT); - else - rts_rspec |= (mimo_ctlchbw << RSPEC_BW_SHIFT); - - /* pick siso/cdd as default for ofdm */ - if (is_ofdm_rate(rts_rspec)) { - rts_rspec &= ~RSPEC_STF_MASK; - rts_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT); - } - } - return rts_rspec; -} - -/* Update beacon listen interval in shared memory */ -static void brcms_c_bcn_li_upd(struct brcms_c_info *wlc) -{ - /* wake up every DTIM is the default */ - if (wlc->bcn_li_dtim == 1) - brcms_b_write_shm(wlc->hw, M_BCN_LI, 0); - else - brcms_b_write_shm(wlc->hw, M_BCN_LI, - (wlc->bcn_li_dtim << 8) | wlc->bcn_li_bcn); -} - -static void -brcms_b_read_tsf(struct brcms_hardware *wlc_hw, u32 *tsf_l_ptr, - u32 *tsf_h_ptr) -{ - struct bcma_device *core = wlc_hw->d11core; - - /* read the tsf timer low, then high to get an atomic read */ - *tsf_l_ptr = bcma_read32(core, D11REGOFFS(tsf_timerlow)); - *tsf_h_ptr = bcma_read32(core, D11REGOFFS(tsf_timerhigh)); -} - -/* - * recover 64bit TSF value from the 16bit TSF value in the rx header - * given the assumption that the TSF passed in header is within 65ms - * of the current tsf. - * - * 6 5 4 4 3 2 1 - * 3.......6.......8.......0.......2.......4.......6.......8......0 - * |<---------- tsf_h ----------->||<--- tsf_l -->||<-RxTSFTime ->| - * - * The RxTSFTime are the lowest 16 bits and provided by the ucode. The - * tsf_l is filled in by brcms_b_recv, which is done earlier in the - * receive call sequence after rx interrupt. Only the higher 16 bits - * are used. Finally, the tsf_h is read from the tsf register. - */ -static u64 brcms_c_recover_tsf64(struct brcms_c_info *wlc, - struct d11rxhdr *rxh) -{ - u32 tsf_h, tsf_l; - u16 rx_tsf_0_15, rx_tsf_16_31; - - brcms_b_read_tsf(wlc->hw, &tsf_l, &tsf_h); - - rx_tsf_16_31 = (u16)(tsf_l >> 16); - rx_tsf_0_15 = rxh->RxTSFTime; - - /* - * a greater tsf time indicates the low 16 bits of - * tsf_l wrapped, so decrement the high 16 bits. - */ - if ((u16)tsf_l < rx_tsf_0_15) { - rx_tsf_16_31 -= 1; - if (rx_tsf_16_31 == 0xffff) - tsf_h -= 1; - } - - return ((u64)tsf_h << 32) | (((u32)rx_tsf_16_31 << 16) + rx_tsf_0_15); -} - -static void -prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, - struct sk_buff *p, - struct ieee80211_rx_status *rx_status) -{ - int channel; - u32 rspec; - unsigned char *plcp; - - /* fill in TSF and flag its presence */ - rx_status->mactime = brcms_c_recover_tsf64(wlc, rxh); - rx_status->flag |= RX_FLAG_MACTIME_START; - - channel = BRCMS_CHAN_CHANNEL(rxh->RxChan); - - rx_status->band = - channel > 14 ? IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; - rx_status->freq = - ieee80211_channel_to_frequency(channel, rx_status->band); - - rx_status->signal = wlc_phy_rssi_compute(wlc->hw->band->pi, rxh); - - /* noise */ - /* qual */ - rx_status->antenna = - (rxh->PhyRxStatus_0 & PRXS0_RXANT_UPSUBBAND) ? 1 : 0; - - plcp = p->data; - - rspec = brcms_c_compute_rspec(rxh, plcp); - if (is_mcs_rate(rspec)) { - rx_status->rate_idx = rspec & RSPEC_RATE_MASK; - rx_status->flag |= RX_FLAG_HT; - if (rspec_is40mhz(rspec)) - rx_status->flag |= RX_FLAG_40MHZ; - } else { - switch (rspec2rate(rspec)) { - case BRCM_RATE_1M: - rx_status->rate_idx = 0; - break; - case BRCM_RATE_2M: - rx_status->rate_idx = 1; - break; - case BRCM_RATE_5M5: - rx_status->rate_idx = 2; - break; - case BRCM_RATE_11M: - rx_status->rate_idx = 3; - break; - case BRCM_RATE_6M: - rx_status->rate_idx = 4; - break; - case BRCM_RATE_9M: - rx_status->rate_idx = 5; - break; - case BRCM_RATE_12M: - rx_status->rate_idx = 6; - break; - case BRCM_RATE_18M: - rx_status->rate_idx = 7; - break; - case BRCM_RATE_24M: - rx_status->rate_idx = 8; - break; - case BRCM_RATE_36M: - rx_status->rate_idx = 9; - break; - case BRCM_RATE_48M: - rx_status->rate_idx = 10; - break; - case BRCM_RATE_54M: - rx_status->rate_idx = 11; - break; - default: - brcms_err(wlc->hw->d11core, - "%s: Unknown rate\n", __func__); - } - - /* - * For 5GHz, we should decrease the index as it is - * a subset of the 2.4G rates. See bitrates field - * of brcms_band_5GHz_nphy (in mac80211_if.c). - */ - if (rx_status->band == IEEE80211_BAND_5GHZ) - rx_status->rate_idx -= BRCMS_LEGACY_5G_RATE_OFFSET; - - /* Determine short preamble and rate_idx */ - if (is_cck_rate(rspec)) { - if (rxh->PhyRxStatus_0 & PRXS0_SHORTH) - rx_status->flag |= RX_FLAG_SHORTPRE; - } else if (is_ofdm_rate(rspec)) { - rx_status->flag |= RX_FLAG_SHORTPRE; - } else { - brcms_err(wlc->hw->d11core, "%s: Unknown modulation\n", - __func__); - } - } - - if (plcp3_issgi(plcp[3])) - rx_status->flag |= RX_FLAG_SHORT_GI; - - if (rxh->RxStatus1 & RXS_DECERR) { - rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC; - brcms_err(wlc->hw->d11core, "%s: RX_FLAG_FAILED_PLCP_CRC\n", - __func__); - } - if (rxh->RxStatus1 & RXS_FCSERR) { - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - brcms_err(wlc->hw->d11core, "%s: RX_FLAG_FAILED_FCS_CRC\n", - __func__); - } -} - -static void -brcms_c_recvctl(struct brcms_c_info *wlc, struct d11rxhdr *rxh, - struct sk_buff *p) -{ - int len_mpdu; - struct ieee80211_rx_status rx_status; - struct ieee80211_hdr *hdr; - - memset(&rx_status, 0, sizeof(rx_status)); - prep_mac80211_status(wlc, rxh, p, &rx_status); - - /* mac header+body length, exclude CRC and plcp header */ - len_mpdu = p->len - D11_PHY_HDR_LEN - FCS_LEN; - skb_pull(p, D11_PHY_HDR_LEN); - __skb_trim(p, len_mpdu); - - /* unmute transmit */ - if (wlc->hw->suspended_fifos) { - hdr = (struct ieee80211_hdr *)p->data; - if (ieee80211_is_beacon(hdr->frame_control)) - brcms_b_mute(wlc->hw, false); - } - - memcpy(IEEE80211_SKB_RXCB(p), &rx_status, sizeof(rx_status)); - ieee80211_rx_irqsafe(wlc->pub->ieee_hw, p); -} - -/* calculate frame duration for Mixed-mode L-SIG spoofing, return - * number of bytes goes in the length field - * - * Formula given by HT PHY Spec v 1.13 - * len = 3(nsyms + nstream + 3) - 3 - */ -u16 -brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, - uint mac_len) -{ - uint nsyms, len = 0, kNdps; - - if (is_mcs_rate(ratespec)) { - uint mcs = ratespec & RSPEC_RATE_MASK; - int tot_streams = (mcs_2_txstreams(mcs) + 1) + - rspec_stc(ratespec); - - /* - * the payload duration calculation matches that - * of regular ofdm - */ - /* 1000Ndbps = kbps * 4 */ - kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), - rspec_issgi(ratespec)) * 4; - - if (rspec_stc(ratespec) == 0) - nsyms = - CEIL((APHY_SERVICE_NBITS + 8 * mac_len + - APHY_TAIL_NBITS) * 1000, kNdps); - else - /* STBC needs to have even number of symbols */ - nsyms = - 2 * - CEIL((APHY_SERVICE_NBITS + 8 * mac_len + - APHY_TAIL_NBITS) * 1000, 2 * kNdps); - - /* (+3) account for HT-SIG(2) and HT-STF(1) */ - nsyms += (tot_streams + 3); - /* - * 3 bytes/symbol @ legacy 6Mbps rate - * (-3) excluding service bits and tail bits - */ - len = (3 * nsyms) - 3; - } - - return (u16) len; -} - -static void -brcms_c_mod_prb_rsp_rate_table(struct brcms_c_info *wlc, uint frame_len) -{ - const struct brcms_c_rateset *rs_dflt; - struct brcms_c_rateset rs; - u8 rate; - u16 entry_ptr; - u8 plcp[D11_PHY_HDR_LEN]; - u16 dur, sifs; - uint i; - - sifs = get_sifs(wlc->band); - - rs_dflt = brcms_c_rateset_get_hwrs(wlc); - - brcms_c_rateset_copy(rs_dflt, &rs); - brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); - - /* - * walk the phy rate table and update MAC core SHM - * basic rate table entries - */ - for (i = 0; i < rs.count; i++) { - rate = rs.rates[i] & BRCMS_RATE_MASK; - - entry_ptr = brcms_b_rate_shm_offset(wlc->hw, rate); - - /* Calculate the Probe Response PLCP for the given rate */ - brcms_c_compute_plcp(wlc, rate, frame_len, plcp); - - /* - * Calculate the duration of the Probe Response - * frame plus SIFS for the MAC - */ - dur = (u16) brcms_c_calc_frame_time(wlc, rate, - BRCMS_LONG_PREAMBLE, frame_len); - dur += sifs; - - /* Update the SHM Rate Table entry Probe Response values */ - brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS, - (u16) (plcp[0] + (plcp[1] << 8))); - brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS + 2, - (u16) (plcp[2] + (plcp[3] << 8))); - brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_DUR_POS, dur); - } -} - -int brcms_c_get_header_len(void) -{ - return TXOFF; -} - -static void brcms_c_beacon_write(struct brcms_c_info *wlc, - struct sk_buff *beacon, u16 tim_offset, - u16 dtim_period, bool bcn0, bool bcn1) -{ - size_t len; - struct ieee80211_tx_info *tx_info; - struct brcms_hardware *wlc_hw = wlc->hw; - struct ieee80211_hw *ieee_hw = brcms_c_pub(wlc)->ieee_hw; - - /* Get tx_info */ - tx_info = IEEE80211_SKB_CB(beacon); - - len = min_t(size_t, beacon->len, BCN_TMPL_LEN); - wlc->bcn_rspec = ieee80211_get_tx_rate(ieee_hw, tx_info)->hw_value; - - brcms_c_compute_plcp(wlc, wlc->bcn_rspec, - len + FCS_LEN - D11_PHY_HDR_LEN, beacon->data); - - /* "Regular" and 16 MBSS but not for 4 MBSS */ - /* Update the phytxctl for the beacon based on the rspec */ - brcms_c_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec); - - if (bcn0) { - /* write the probe response into the template region */ - brcms_b_write_template_ram(wlc_hw, T_BCN0_TPL_BASE, - (len + 3) & ~3, beacon->data); - - /* write beacon length to SCR */ - brcms_b_write_shm(wlc_hw, M_BCN0_FRM_BYTESZ, (u16) len); - } - if (bcn1) { - /* write the probe response into the template region */ - brcms_b_write_template_ram(wlc_hw, T_BCN1_TPL_BASE, - (len + 3) & ~3, beacon->data); - - /* write beacon length to SCR */ - brcms_b_write_shm(wlc_hw, M_BCN1_FRM_BYTESZ, (u16) len); - } - - if (tim_offset != 0) { - brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON, - tim_offset + D11B_PHY_HDR_LEN); - brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, dtim_period); - } else { - brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON, - len + D11B_PHY_HDR_LEN); - brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, 0); - } -} - -static void brcms_c_update_beacon_hw(struct brcms_c_info *wlc, - struct sk_buff *beacon, u16 tim_offset, - u16 dtim_period) -{ - struct brcms_hardware *wlc_hw = wlc->hw; - struct bcma_device *core = wlc_hw->d11core; - - /* Hardware beaconing for this config */ - u32 both_valid = MCMD_BCN0VLD | MCMD_BCN1VLD; - - /* Check if both templates are in use, if so sched. an interrupt - * that will call back into this routine - */ - if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid) - /* clear any previous status */ - bcma_write32(core, D11REGOFFS(macintstatus), MI_BCNTPL); - - if (wlc->beacon_template_virgin) { - wlc->beacon_template_virgin = false; - brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true, - true); - /* mark beacon0 valid */ - bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD); - return; - } - - /* Check that after scheduling the interrupt both of the - * templates are still busy. if not clear the int. & remask - */ - if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid) { - wlc->defmacintmask |= MI_BCNTPL; - return; - } - - if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN0VLD)) { - brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true, - false); - /* mark beacon0 valid */ - bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD); - return; - } - if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN1VLD)) { - brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, - false, true); - /* mark beacon0 valid */ - bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN1VLD); - return; - } - return; -} - -/* - * Update all beacons for the system. - */ -void brcms_c_update_beacon(struct brcms_c_info *wlc) -{ - struct brcms_bss_cfg *bsscfg = wlc->bsscfg; - - if (wlc->pub->up && (bsscfg->type == BRCMS_TYPE_AP || - bsscfg->type == BRCMS_TYPE_ADHOC)) { - /* Clear the soft intmask */ - wlc->defmacintmask &= ~MI_BCNTPL; - if (!wlc->beacon) - return; - brcms_c_update_beacon_hw(wlc, wlc->beacon, - wlc->beacon_tim_offset, - wlc->beacon_dtim_period); - } -} - -void brcms_c_set_new_beacon(struct brcms_c_info *wlc, struct sk_buff *beacon, - u16 tim_offset, u16 dtim_period) -{ - if (!beacon) - return; - if (wlc->beacon) - dev_kfree_skb_any(wlc->beacon); - wlc->beacon = beacon; - - /* add PLCP */ - skb_push(wlc->beacon, D11_PHY_HDR_LEN); - wlc->beacon_tim_offset = tim_offset; - wlc->beacon_dtim_period = dtim_period; - brcms_c_update_beacon(wlc); -} - -void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc, - struct sk_buff *probe_resp) -{ - if (!probe_resp) - return; - if (wlc->probe_resp) - dev_kfree_skb_any(wlc->probe_resp); - wlc->probe_resp = probe_resp; - - /* add PLCP */ - skb_push(wlc->probe_resp, D11_PHY_HDR_LEN); - brcms_c_update_probe_resp(wlc, false); -} - -void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable) -{ - /* - * prevent ucode from sending probe responses by setting the timeout - * to 1, it can not send it in that time frame. - */ - wlc->prb_resp_timeout = enable ? BRCMS_PRB_RESP_TIMEOUT : 1; - brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout); - /* TODO: if (enable) => also deactivate receiving of probe request */ -} - -/* Write ssid into shared memory */ -static void -brcms_c_shm_ssid_upd(struct brcms_c_info *wlc, struct brcms_bss_cfg *cfg) -{ - u8 *ssidptr = cfg->SSID; - u16 base = M_SSID; - u8 ssidbuf[IEEE80211_MAX_SSID_LEN]; - - /* padding the ssid with zero and copy it into shm */ - memset(ssidbuf, 0, IEEE80211_MAX_SSID_LEN); - memcpy(ssidbuf, ssidptr, cfg->SSID_len); - - brcms_c_copyto_shm(wlc, base, ssidbuf, IEEE80211_MAX_SSID_LEN); - brcms_b_write_shm(wlc->hw, M_SSIDLEN, (u16) cfg->SSID_len); -} - -static void -brcms_c_bss_update_probe_resp(struct brcms_c_info *wlc, - struct brcms_bss_cfg *cfg, - struct sk_buff *probe_resp, - bool suspend) -{ - int len; - - len = min_t(size_t, probe_resp->len, BCN_TMPL_LEN); - - if (suspend) - brcms_c_suspend_mac_and_wait(wlc); - - /* write the probe response into the template region */ - brcms_b_write_template_ram(wlc->hw, T_PRS_TPL_BASE, - (len + 3) & ~3, probe_resp->data); - - /* write the length of the probe response frame (+PLCP/-FCS) */ - brcms_b_write_shm(wlc->hw, M_PRB_RESP_FRM_LEN, (u16) len); - - /* write the SSID and SSID length */ - brcms_c_shm_ssid_upd(wlc, cfg); - - /* - * Write PLCP headers and durations for probe response frames - * at all rates. Use the actual frame length covered by the - * PLCP header for the call to brcms_c_mod_prb_rsp_rate_table() - * by subtracting the PLCP len and adding the FCS. - */ - brcms_c_mod_prb_rsp_rate_table(wlc, - (u16)len + FCS_LEN - D11_PHY_HDR_LEN); - - if (suspend) - brcms_c_enable_mac(wlc); -} - -void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend) -{ - struct brcms_bss_cfg *bsscfg = wlc->bsscfg; - - /* update AP or IBSS probe responses */ - if (wlc->pub->up && (bsscfg->type == BRCMS_TYPE_AP || - bsscfg->type == BRCMS_TYPE_ADHOC)) { - if (!wlc->probe_resp) - return; - brcms_c_bss_update_probe_resp(wlc, bsscfg, wlc->probe_resp, - suspend); - } -} - -int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, - uint *blocks) -{ - if (fifo >= NFIFO) - return -EINVAL; - - *blocks = wlc_hw->xmtfifo_sz[fifo]; - - return 0; -} - -void -brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset, - const u8 *addr) -{ - brcms_b_set_addrmatch(wlc->hw, match_reg_offset, addr); - if (match_reg_offset == RCM_BSSID_OFFSET) - memcpy(wlc->bsscfg->BSSID, addr, ETH_ALEN); -} - -/* - * Flag 'scan in progress' to withhold dynamic phy calibration - */ -void brcms_c_scan_start(struct brcms_c_info *wlc) -{ - wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, true); -} - -void brcms_c_scan_stop(struct brcms_c_info *wlc) -{ - wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, false); -} - -void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state) -{ - wlc->pub->associated = state; -} - -/* - * When a remote STA/AP is removed by Mac80211, or when it can no longer accept - * AMPDU traffic, packets pending in hardware have to be invalidated so that - * when later on hardware releases them, they can be handled appropriately. - */ -void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, - struct ieee80211_sta *sta, - void (*dma_callback_fn)) -{ - struct dma_pub *dmah; - int i; - for (i = 0; i < NFIFO; i++) { - dmah = hw->di[i]; - if (dmah != NULL) - dma_walk_packets(dmah, dma_callback_fn, sta); - } -} - -int brcms_c_get_curband(struct brcms_c_info *wlc) -{ - return wlc->band->bandunit; -} - -bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc) -{ - int i; - - /* Kick DMA to send any pending AMPDU */ - for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++) - if (wlc->hw->di[i]) - dma_kick_tx(wlc->hw->di[i]); - - return !brcms_txpktpendtot(wlc); -} - -void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval) -{ - wlc->bcn_li_bcn = interval; - if (wlc->pub->up) - brcms_c_bcn_li_upd(wlc); -} - -u64 brcms_c_tsf_get(struct brcms_c_info *wlc) -{ - u32 tsf_h, tsf_l; - u64 tsf; - - brcms_b_read_tsf(wlc->hw, &tsf_l, &tsf_h); - - tsf = tsf_h; - tsf <<= 32; - tsf |= tsf_l; - - return tsf; -} - -void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf) -{ - u32 tsf_h, tsf_l; - - brcms_c_time_lock(wlc); - - tsf_l = tsf; - tsf_h = (tsf >> 32); - - /* read the tsf timer low, then high to get an atomic read */ - bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_timerlow), tsf_l); - bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_timerhigh), tsf_h); - - brcms_c_time_unlock(wlc); -} - -int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr) -{ - uint qdbm; - - /* Remove override bit and clip to max qdbm value */ - qdbm = min_t(uint, txpwr * BRCMS_TXPWR_DB_FACTOR, 0xff); - return wlc_phy_txpower_set(wlc->band->pi, qdbm, false); -} - -int brcms_c_get_tx_power(struct brcms_c_info *wlc) -{ - uint qdbm; - bool override; - - wlc_phy_txpower_get(wlc->band->pi, &qdbm, &override); - - /* Return qdbm units */ - return (int)(qdbm / BRCMS_TXPWR_DB_FACTOR); -} - -/* Process received frames */ -/* - * Return true if more frames need to be processed. false otherwise. - * Param 'bound' indicates max. # frames to process before break out. - */ -static void brcms_c_recv(struct brcms_c_info *wlc, struct sk_buff *p) -{ - struct d11rxhdr *rxh; - struct ieee80211_hdr *h; - uint len; - bool is_amsdu; - - /* frame starts with rxhdr */ - rxh = (struct d11rxhdr *) (p->data); - - /* strip off rxhdr */ - skb_pull(p, BRCMS_HWRXOFF); - - /* MAC inserts 2 pad bytes for a4 headers or QoS or A-MSDU subframes */ - if (rxh->RxStatus1 & RXS_PBPRES) { - if (p->len < 2) { - brcms_err(wlc->hw->d11core, - "wl%d: recv: rcvd runt of len %d\n", - wlc->pub->unit, p->len); - goto toss; - } - skb_pull(p, 2); - } - - h = (struct ieee80211_hdr *)(p->data + D11_PHY_HDR_LEN); - len = p->len; - - if (rxh->RxStatus1 & RXS_FCSERR) { - if (!(wlc->filter_flags & FIF_FCSFAIL)) - goto toss; - } - - /* check received pkt has at least frame control field */ - if (len < D11_PHY_HDR_LEN + sizeof(h->frame_control)) - goto toss; - - /* not supporting A-MSDU */ - is_amsdu = rxh->RxStatus2 & RXS_AMSDU_MASK; - if (is_amsdu) - goto toss; - - brcms_c_recvctl(wlc, rxh, p); - return; - - toss: - brcmu_pkt_buf_free_skb(p); -} - -/* Process received frames */ -/* - * Return true if more frames need to be processed. false otherwise. - * Param 'bound' indicates max. # frames to process before break out. - */ -static bool -brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound) -{ - struct sk_buff *p; - struct sk_buff *next = NULL; - struct sk_buff_head recv_frames; - - uint n = 0; - uint bound_limit = bound ? RXBND : -1; - bool morepending = false; - - skb_queue_head_init(&recv_frames); - - /* gather received frames */ - do { - /* !give others some time to run! */ - if (n >= bound_limit) - break; - - morepending = dma_rx(wlc_hw->di[fifo], &recv_frames); - n++; - } while (morepending); - - /* post more rbufs */ - dma_rxfill(wlc_hw->di[fifo]); - - /* process each frame */ - skb_queue_walk_safe(&recv_frames, p, next) { - struct d11rxhdr_le *rxh_le; - struct d11rxhdr *rxh; - - skb_unlink(p, &recv_frames); - rxh_le = (struct d11rxhdr_le *)p->data; - rxh = (struct d11rxhdr *)p->data; - - /* fixup rx header endianness */ - rxh->RxFrameSize = le16_to_cpu(rxh_le->RxFrameSize); - rxh->PhyRxStatus_0 = le16_to_cpu(rxh_le->PhyRxStatus_0); - rxh->PhyRxStatus_1 = le16_to_cpu(rxh_le->PhyRxStatus_1); - rxh->PhyRxStatus_2 = le16_to_cpu(rxh_le->PhyRxStatus_2); - rxh->PhyRxStatus_3 = le16_to_cpu(rxh_le->PhyRxStatus_3); - rxh->PhyRxStatus_4 = le16_to_cpu(rxh_le->PhyRxStatus_4); - rxh->PhyRxStatus_5 = le16_to_cpu(rxh_le->PhyRxStatus_5); - rxh->RxStatus1 = le16_to_cpu(rxh_le->RxStatus1); - rxh->RxStatus2 = le16_to_cpu(rxh_le->RxStatus2); - rxh->RxTSFTime = le16_to_cpu(rxh_le->RxTSFTime); - rxh->RxChan = le16_to_cpu(rxh_le->RxChan); - - brcms_c_recv(wlc_hw->wlc, p); - } - - return morepending; -} - -/* second-level interrupt processing - * Return true if another dpc needs to be re-scheduled. false otherwise. - * Param 'bounded' indicates if applicable loops should be bounded. - */ -bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) -{ - u32 macintstatus; - struct brcms_hardware *wlc_hw = wlc->hw; - struct bcma_device *core = wlc_hw->d11core; - - if (brcms_deviceremoved(wlc)) { - brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, - __func__); - brcms_down(wlc->wl); - return false; - } - - /* grab and clear the saved software intstatus bits */ - macintstatus = wlc->macintstatus; - wlc->macintstatus = 0; - - brcms_dbg_int(core, "wl%d: macintstatus 0x%x\n", - wlc_hw->unit, macintstatus); - - WARN_ON(macintstatus & MI_PRQ); /* PRQ Interrupt in non-MBSS */ - - /* tx status */ - if (macintstatus & MI_TFS) { - bool fatal; - if (brcms_b_txstatus(wlc->hw, bounded, &fatal)) - wlc->macintstatus |= MI_TFS; - if (fatal) { - brcms_err(core, "MI_TFS: fatal\n"); - goto fatal; - } - } - - if (macintstatus & (MI_TBTT | MI_DTIM_TBTT)) - brcms_c_tbtt(wlc); - - /* ATIM window end */ - if (macintstatus & MI_ATIMWINEND) { - brcms_dbg_info(core, "end of ATIM window\n"); - bcma_set32(core, D11REGOFFS(maccommand), wlc->qvalid); - wlc->qvalid = 0; - } - - /* - * received data or control frame, MI_DMAINT is - * indication of RX_FIFO interrupt - */ - if (macintstatus & MI_DMAINT) - if (brcms_b_recv(wlc_hw, RX_FIFO, bounded)) - wlc->macintstatus |= MI_DMAINT; - - /* noise sample collected */ - if (macintstatus & MI_BG_NOISE) - wlc_phy_noise_sample_intr(wlc_hw->band->pi); - - if (macintstatus & MI_GP0) { - brcms_err(core, "wl%d: PSM microcode watchdog fired at %d " - "(seconds). Resetting.\n", wlc_hw->unit, wlc_hw->now); - - printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n", - __func__, ai_get_chip_id(wlc_hw->sih), - ai_get_chiprev(wlc_hw->sih)); - brcms_fatal_error(wlc_hw->wlc->wl); - } - - /* gptimer timeout */ - if (macintstatus & MI_TO) - bcma_write32(core, D11REGOFFS(gptimer), 0); - - if (macintstatus & MI_RFDISABLE) { - brcms_dbg_info(core, "wl%d: BMAC Detected a change on the" - " RF Disable Input\n", wlc_hw->unit); - brcms_rfkill_set_hw_state(wlc->wl); - } - - /* BCN template is available */ - if (macintstatus & MI_BCNTPL) - brcms_c_update_beacon(wlc); - - /* it isn't done and needs to be resched if macintstatus is non-zero */ - return wlc->macintstatus != 0; - - fatal: - brcms_fatal_error(wlc_hw->wlc->wl); - return wlc->macintstatus != 0; -} - -void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx) -{ - struct bcma_device *core = wlc->hw->d11core; - struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan; - u16 chanspec; - - brcms_dbg_info(core, "wl%d\n", wlc->pub->unit); - - chanspec = ch20mhz_chspec(ch->hw_value); - - brcms_b_init(wlc->hw, chanspec); - - /* update beacon listen interval */ - brcms_c_bcn_li_upd(wlc); - - /* write ethernet address to core */ - brcms_c_set_mac(wlc->bsscfg); - brcms_c_set_bssid(wlc->bsscfg); - - /* Update tsf_cfprep if associated and up */ - if (wlc->pub->associated && wlc->pub->up) { - u32 bi; - - /* get beacon period and convert to uS */ - bi = wlc->bsscfg->current_bss->beacon_period << 10; - /* - * update since init path would reset - * to default value - */ - bcma_write32(core, D11REGOFFS(tsf_cfprep), - bi << CFPREP_CBI_SHIFT); - - /* Update maccontrol PM related bits */ - brcms_c_set_ps_ctrl(wlc); - } - - brcms_c_bandinit_ordered(wlc, chanspec); - - /* init probe response timeout */ - brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout); - - /* init max burst txop (framebursting) */ - brcms_b_write_shm(wlc->hw, M_MBURST_TXOP, - (wlc-> - _rifs ? (EDCF_AC_VO_TXOP_AP << 5) : MAXFRAMEBURST_TXOP)); - - /* initialize maximum allowed duty cycle */ - brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_ofdm, true, true); - brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_cck, false, true); - - /* - * Update some shared memory locations related to - * max AMPDU size allowed to received - */ - brcms_c_ampdu_shm_upd(wlc->ampdu); - - /* band-specific inits */ - brcms_c_bsinit(wlc); - - /* Enable EDCF mode (while the MAC is suspended) */ - bcma_set16(core, D11REGOFFS(ifs_ctl), IFS_USEEDCF); - brcms_c_edcf_setparams(wlc, false); - - /* read the ucode version if we have not yet done so */ - if (wlc->ucode_rev == 0) { - u16 rev; - u16 patch; - - rev = brcms_b_read_shm(wlc->hw, M_BOM_REV_MAJOR); - patch = brcms_b_read_shm(wlc->hw, M_BOM_REV_MINOR); - wlc->ucode_rev = (rev << NBITS(u16)) | patch; - snprintf(wlc->wiphy->fw_version, - sizeof(wlc->wiphy->fw_version), "%u.%u", rev, patch); - } - - /* ..now really unleash hell (allow the MAC out of suspend) */ - brcms_c_enable_mac(wlc); - - /* suspend the tx fifos and mute the phy for preism cac time */ - if (mute_tx) - brcms_b_mute(wlc->hw, true); - - /* enable the RF Disable Delay timer */ - bcma_write32(core, D11REGOFFS(rfdisabledly), RFDISABLE_DEFAULT); - - /* - * Initialize WME parameters; if they haven't been set by some other - * mechanism (IOVar, etc) then read them from the hardware. - */ - if (GFIELD(wlc->wme_retries[0], EDCF_SHORT) == 0) { - /* Uninitialized; read from HW */ - int ac; - - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - wlc->wme_retries[ac] = - brcms_b_read_shm(wlc->hw, M_AC_TXLMT_ADDR(ac)); - } -} - -/* - * The common driver entry routine. Error codes should be unique - */ -struct brcms_c_info * -brcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit, - bool piomode, uint *perr) -{ - struct brcms_c_info *wlc; - uint err = 0; - uint i, j; - struct brcms_pub *pub; - - /* allocate struct brcms_c_info state and its substructures */ - wlc = brcms_c_attach_malloc(unit, &err, 0); - if (wlc == NULL) - goto fail; - wlc->wiphy = wl->wiphy; - pub = wlc->pub; - -#if defined(DEBUG) - wlc_info_dbg = wlc; -#endif - - wlc->band = wlc->bandstate[0]; - wlc->core = wlc->corestate; - wlc->wl = wl; - pub->unit = unit; - pub->_piomode = piomode; - wlc->bandinit_pending = false; - wlc->beacon_template_virgin = true; - - /* populate struct brcms_c_info with default values */ - brcms_c_info_init(wlc, unit); - - /* update sta/ap related parameters */ - brcms_c_ap_upd(wlc); - - /* - * low level attach steps(all hw accesses go - * inside, no more in rest of the attach) - */ - err = brcms_b_attach(wlc, core, unit, piomode); - if (err) - goto fail; - - brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, OFF); - - pub->phy_11ncapable = BRCMS_PHY_11N_CAP(wlc->band); - - /* disable allowed duty cycle */ - wlc->tx_duty_cycle_ofdm = 0; - wlc->tx_duty_cycle_cck = 0; - - brcms_c_stf_phy_chain_calc(wlc); - - /* txchain 1: txant 0, txchain 2: txant 1 */ - if (BRCMS_ISNPHY(wlc->band) && (wlc->stf->txstreams == 1)) - wlc->stf->txant = wlc->stf->hw_txchain - 1; - - /* push to BMAC driver */ - wlc_phy_stf_chain_init(wlc->band->pi, wlc->stf->hw_txchain, - wlc->stf->hw_rxchain); - - /* pull up some info resulting from the low attach */ - for (i = 0; i < NFIFO; i++) - wlc->core->txavail[i] = wlc->hw->txavail[i]; - - memcpy(&wlc->perm_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); - memcpy(&pub->cur_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); - - for (j = 0; j < wlc->pub->_nbands; j++) { - wlc->band = wlc->bandstate[j]; - - if (!brcms_c_attach_stf_ant_init(wlc)) { - err = 24; - goto fail; - } - - /* default contention windows size limits */ - wlc->band->CWmin = APHY_CWMIN; - wlc->band->CWmax = PHY_CWMAX; - - /* init gmode value */ - if (wlc->band->bandtype == BRCM_BAND_2G) { - wlc->band->gmode = GMODE_AUTO; - brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, - wlc->band->gmode); - } - - /* init _n_enab supported mode */ - if (BRCMS_PHY_11N_CAP(wlc->band)) { - pub->_n_enab = SUPPORT_11N; - brcms_c_protection_upd(wlc, BRCMS_PROT_N_USER, - ((pub->_n_enab == - SUPPORT_11N) ? WL_11N_2x2 : - WL_11N_3x3)); - } - - /* init per-band default rateset, depend on band->gmode */ - brcms_default_rateset(wlc, &wlc->band->defrateset); - - /* fill in hw_rateset */ - brcms_c_rateset_filter(&wlc->band->defrateset, - &wlc->band->hw_rateset, false, - BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK, - (bool) (wlc->pub->_n_enab & SUPPORT_11N)); - } - - /* - * update antenna config due to - * wlc->stf->txant/txchain/ant_rx_ovr change - */ - brcms_c_stf_phy_txant_upd(wlc); - - /* attach each modules */ - err = brcms_c_attach_module(wlc); - if (err != 0) - goto fail; - - if (!brcms_c_timers_init(wlc, unit)) { - wiphy_err(wl->wiphy, "wl%d: %s: init_timer failed\n", unit, - __func__); - err = 32; - goto fail; - } - - /* depend on rateset, gmode */ - wlc->cmi = brcms_c_channel_mgr_attach(wlc); - if (!wlc->cmi) { - wiphy_err(wl->wiphy, "wl%d: %s: channel_mgr_attach failed" - "\n", unit, __func__); - err = 33; - goto fail; - } - - /* init default when all parameters are ready, i.e. ->rateset */ - brcms_c_bss_default_init(wlc); - - /* - * Complete the wlc default state initializations.. - */ - - wlc->bsscfg->wlc = wlc; - - wlc->mimoft = FT_HT; - wlc->mimo_40txbw = AUTO; - wlc->ofdm_40txbw = AUTO; - wlc->cck_40txbw = AUTO; - brcms_c_update_mimo_band_bwcap(wlc, BRCMS_N_BW_20IN2G_40IN5G); - - /* Set default values of SGI */ - if (BRCMS_SGI_CAP_PHY(wlc)) { - brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | - BRCMS_N_SGI_40)); - } else if (BRCMS_ISSSLPNPHY(wlc->band)) { - brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | - BRCMS_N_SGI_40)); - } else { - brcms_c_ht_update_sgi_rx(wlc, 0); - } - - brcms_b_antsel_set(wlc->hw, wlc->asi->antsel_avail); - - if (perr) - *perr = 0; - - return wlc; - - fail: - wiphy_err(wl->wiphy, "wl%d: %s: failed with err %d\n", - unit, __func__, err); - if (wlc) - brcms_c_detach(wlc); - - if (perr) - *perr = err; - return NULL; -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.h b/drivers/net/wireless/brcm80211/brcmsmac/main.h deleted file mode 100644 index c4d135cff04a..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.h +++ /dev/null @@ -1,669 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_MAIN_H_ -#define _BRCM_MAIN_H_ - -#include - -#include -#include "types.h" -#include "d11.h" -#include "scb.h" - -#define INVCHANNEL 255 /* invalid channel */ - -/* max # brcms_c_module_register() calls */ -#define BRCMS_MAXMODULES 22 - -#define SEQNUM_SHIFT 4 -#define SEQNUM_MAX 0x1000 - -#define NTXRATE 64 /* # tx MPDUs rate is reported for */ - -/* Maximum wait time for a MAC suspend */ -/* uS: 83mS is max packet time (64KB ampdu @ 6Mbps) */ -#define BRCMS_MAX_MAC_SUSPEND 83000 - -/* responses for probe requests older that this are tossed, zero to disable */ -#define BRCMS_PRB_RESP_TIMEOUT 0 /* Disable probe response timeout */ - -/* transmit buffer max headroom for protocol headers */ -#define TXOFF (D11_TXH_LEN + D11_PHY_HDR_LEN) - -/* Macros for doing definition and get/set of bitfields - * Usage example, e.g. a three-bit field (bits 4-6): - * #define _M BITFIELD_MASK(3) - * #define _S 4 - * ... - * regval = R_REG(osh, ®s->regfoo); - * field = GFIELD(regval, ); - * regval = SFIELD(regval, , 1); - * W_REG(osh, ®s->regfoo, regval); - */ -#define BITFIELD_MASK(width) \ - (((unsigned)1 << (width)) - 1) -#define GFIELD(val, field) \ - (((val) >> field ## _S) & field ## _M) -#define SFIELD(val, field, bits) \ - (((val) & (~(field ## _M << field ## _S))) | \ - ((unsigned)(bits) << field ## _S)) - -#define SW_TIMER_MAC_STAT_UPD 30 /* periodic MAC stats update */ - -/* max # supported core revisions (0 .. MAXCOREREV - 1) */ -#define MAXCOREREV 28 - -/* Double check that unsupported cores are not enabled */ -#if CONF_MSK(D11CONF, 0x4f) || CONF_GE(D11CONF, MAXCOREREV) -#error "Configuration for D11CONF includes unsupported versions." -#endif /* Bad versions */ - -/* values for shortslot_override */ -#define BRCMS_SHORTSLOT_AUTO -1 /* Driver will manage Shortslot setting */ -#define BRCMS_SHORTSLOT_OFF 0 /* Turn off short slot */ -#define BRCMS_SHORTSLOT_ON 1 /* Turn on short slot */ - -/* value for short/long and mixmode/greenfield preamble */ -#define BRCMS_LONG_PREAMBLE (0) -#define BRCMS_SHORT_PREAMBLE (1 << 0) -#define BRCMS_GF_PREAMBLE (1 << 1) -#define BRCMS_MM_PREAMBLE (1 << 2) -#define BRCMS_IS_MIMO_PREAMBLE(_pre) (((_pre) == BRCMS_GF_PREAMBLE) || \ - ((_pre) == BRCMS_MM_PREAMBLE)) - -/* TxFrameID */ -/* seq and frag bits: SEQNUM_SHIFT, FRAGNUM_MASK (802.11.h) */ -/* rate epoch bits: TXFID_RATE_SHIFT, TXFID_RATE_MASK ((wlc_rate.c) */ -#define TXFID_QUEUE_MASK 0x0007 /* Bits 0-2 */ -#define TXFID_SEQ_MASK 0x7FE0 /* Bits 5-15 */ -#define TXFID_SEQ_SHIFT 5 /* Number of bit shifts */ -#define TXFID_RATE_PROBE_MASK 0x8000 /* Bit 15 for rate probe */ -#define TXFID_RATE_MASK 0x0018 /* Mask for bits 3 and 4 */ -#define TXFID_RATE_SHIFT 3 /* Shift 3 bits for rate mask */ - -/* promote boardrev */ -#define BOARDREV_PROMOTABLE 0xFF /* from */ -#define BOARDREV_PROMOTED 1 /* to */ - -#define DATA_BLOCK_TX_SUPR (1 << 4) - -/* Ucode MCTL_WAKE override bits */ -#define BRCMS_WAKE_OVERRIDE_CLKCTL 0x01 -#define BRCMS_WAKE_OVERRIDE_PHYREG 0x02 -#define BRCMS_WAKE_OVERRIDE_MACSUSPEND 0x04 -#define BRCMS_WAKE_OVERRIDE_TXFIFO 0x08 -#define BRCMS_WAKE_OVERRIDE_FORCEFAST 0x10 - -/* stuff pulled in from wlc.c */ - -/* Interrupt bit error summary. Don't include I_RU: we refill DMA at other - * times; and if we run out, constant I_RU interrupts may cause lockup. We - * will still get error counts from rx0ovfl. - */ -#define I_ERRORS (I_PC | I_PD | I_DE | I_RO | I_XU) -/* default software intmasks */ -#define DEF_RXINTMASK (I_RI) /* enable rx int on rxfifo only */ -#define DEF_MACINTMASK (MI_TXSTOP | MI_TBTT | MI_ATIMWINEND | MI_PMQ | \ - MI_PHYTXERR | MI_DMAINT | MI_TFS | MI_BG_NOISE | \ - MI_CCA | MI_TO | MI_GP0 | MI_RFDISABLE | MI_PWRUP) - -#define MAXTXPKTS 6 /* max # pkts pending */ - -/* frameburst */ -#define MAXTXFRAMEBURST 8 /* vanilla xpress mode: max frames/burst */ -#define MAXFRAMEBURST_TXOP 10000 /* Frameburst TXOP in usec */ - -#define NFIFO 6 /* # tx/rx fifopairs */ - -/* PLL requests */ - -/* pll is shared on old chips */ -#define BRCMS_PLLREQ_SHARED 0x1 -/* hold pll for radio monitor register checking */ -#define BRCMS_PLLREQ_RADIO_MON 0x2 -/* hold/release pll for some short operation */ -#define BRCMS_PLLREQ_FLIP 0x4 - -#define CHANNEL_BANDUNIT(wlc, ch) \ - (((ch) <= CH_MAX_2G_CHANNEL) ? BAND_2G_INDEX : BAND_5G_INDEX) - -#define OTHERBANDUNIT(wlc) \ - ((uint)((wlc)->band->bandunit ? BAND_2G_INDEX : BAND_5G_INDEX)) - -/* - * 802.11 protection information - * - * _g: use g spec protection, driver internal. - * g_override: override for use of g spec protection. - * gmode_user: user config gmode, operating band->gmode is different. - * overlap: Overlap BSS/IBSS protection for both 11g and 11n. - * nmode_user: user config nmode, operating pub->nmode is different. - * n_cfg: use OFDM protection on MIMO frames. - * n_cfg_override: override for use of N protection. - * nongf: non-GF present protection. - * nongf_override: override for use of GF protection. - * n_pam_override: override for preamble: MM or GF. - * n_obss: indicated OBSS Non-HT STA present. -*/ -struct brcms_protection { - bool _g; - s8 g_override; - u8 gmode_user; - s8 overlap; - s8 nmode_user; - s8 n_cfg; - s8 n_cfg_override; - bool nongf; - s8 nongf_override; - s8 n_pam_override; - bool n_obss; -}; - -/* - * anything affecting the single/dual streams/antenna operation - * - * hw_txchain: HW txchain bitmap cfg. - * txchain: txchain bitmap being used. - * txstreams: number of txchains being used. - * hw_rxchain: HW rxchain bitmap cfg. - * rxchain: rxchain bitmap being used. - * rxstreams: number of rxchains being used. - * ant_rx_ovr: rx antenna override. - * txant: userTx antenna setting. - * phytxant: phyTx antenna setting in txheader. - * ss_opmode: singlestream Operational mode, 0:siso; 1:cdd. - * ss_algosel_auto: if true, use wlc->stf->ss_algo_channel; - * else use wlc->band->stf->ss_mode_band. - * ss_algo_channel: ss based on per-channel algo: 0: SISO, 1: CDD 2: STBC. - * rxchain_restore_delay: delay time to restore default rxchain. - * ldpc: AUTO/ON/OFF ldpc cap supported. - * txcore[MAX_STREAMS_SUPPORTED + 1]: bitmap of selected core for each Nsts. - * spatial_policy: - */ -struct brcms_stf { - u8 hw_txchain; - u8 txchain; - u8 txstreams; - u8 hw_rxchain; - u8 rxchain; - u8 rxstreams; - u8 ant_rx_ovr; - s8 txant; - u16 phytxant; - u8 ss_opmode; - bool ss_algosel_auto; - u16 ss_algo_channel; - u8 rxchain_restore_delay; - s8 ldpc; - u8 txcore[MAX_STREAMS_SUPPORTED + 1]; - s8 spatial_policy; -}; - -#define BRCMS_STF_SS_STBC_TX(wlc, scb) \ - (((wlc)->stf->txstreams > 1) && (((wlc)->band->band_stf_stbc_tx == ON) \ - || (((scb)->flags & SCB_STBCCAP) && \ - (wlc)->band->band_stf_stbc_tx == AUTO && \ - isset(&((wlc)->stf->ss_algo_channel), PHY_TXC1_MODE_STBC)))) - -#define BRCMS_STBC_CAP_PHY(wlc) (BRCMS_ISNPHY(wlc->band) && \ - NREV_GE(wlc->band->phyrev, 3)) - -#define BRCMS_SGI_CAP_PHY(wlc) ((BRCMS_ISNPHY(wlc->band) && \ - NREV_GE(wlc->band->phyrev, 3)) || \ - BRCMS_ISLCNPHY(wlc->band)) - -#define BRCMS_CHAN_PHYTYPE(x) (((x) & RXS_CHAN_PHYTYPE_MASK) \ - >> RXS_CHAN_PHYTYPE_SHIFT) -#define BRCMS_CHAN_CHANNEL(x) (((x) & RXS_CHAN_ID_MASK) \ - >> RXS_CHAN_ID_SHIFT) - -/* - * core state (mac) - */ -struct brcms_core { - uint coreidx; /* # sb enumerated core */ - - /* fifo */ - uint *txavail[NFIFO]; /* # tx descriptors available */ - - struct macstat *macstat_snapshot; /* mac hw prev read values */ -}; - -/* - * band state (phy+ana+radio) - */ -struct brcms_band { - int bandtype; /* BRCM_BAND_2G, BRCM_BAND_5G */ - uint bandunit; /* bandstate[] index */ - - u16 phytype; /* phytype */ - u16 phyrev; - u16 radioid; - u16 radiorev; - struct brcms_phy_pub *pi; /* pointer to phy specific information */ - bool abgphy_encore; - - u8 gmode; /* currently active gmode */ - - struct scb *hwrs_scb; /* permanent scb for hw rateset */ - - /* band-specific copy of default_bss.rateset */ - struct brcms_c_rateset defrateset; - - u8 band_stf_ss_mode; /* Configured STF type, 0:siso; 1:cdd */ - s8 band_stf_stbc_tx; /* STBC TX 0:off; 1:force on; -1:auto */ - /* rates supported by chip (phy-specific) */ - struct brcms_c_rateset hw_rateset; - u8 basic_rate[BRCM_MAXRATE + 1]; /* basic rates indexed by rate */ - bool mimo_cap_40; /* 40 MHz cap enabled on this band */ - s8 antgain; /* antenna gain from srom */ - - u16 CWmin; /* minimum size of contention window, in unit of aSlotTime */ - u16 CWmax; /* maximum size of contention window, in unit of aSlotTime */ - struct ieee80211_supported_band band; -}; - -/* module control blocks */ -struct modulecb { - /* module name : NULL indicates empty array member */ - char name[32]; - /* handle passed when handler 'doiovar' is called */ - struct brcms_info *hdl; - - int (*down_fn)(void *handle); /* down handler. Note: the int returned - * by the down function is a count of the - * number of timers that could not be - * freed. - */ - -}; - -struct brcms_hw_band { - int bandtype; /* BRCM_BAND_2G, BRCM_BAND_5G */ - uint bandunit; /* bandstate[] index */ - u16 mhfs[MHFMAX]; /* MHF array shadow */ - u8 bandhw_stf_ss_mode; /* HW configured STF type, 0:siso; 1:cdd */ - u16 CWmin; - u16 CWmax; - u32 core_flags; - - u16 phytype; /* phytype */ - u16 phyrev; - u16 radioid; - u16 radiorev; - struct brcms_phy_pub *pi; /* pointer to phy specific information */ - bool abgphy_encore; -}; - -struct brcms_hardware { - bool _piomode; /* true if pio mode */ - struct brcms_c_info *wlc; - - /* fifo */ - struct dma_pub *di[NFIFO]; /* dma handles, per fifo */ - - uint unit; /* device instance number */ - - /* version info */ - u16 vendorid; /* PCI vendor id */ - u16 deviceid; /* PCI device id */ - uint corerev; /* core revision */ - u8 sromrev; /* version # of the srom */ - u16 boardrev; /* version # of particular board */ - u32 boardflags; /* Board specific flags from srom */ - u32 boardflags2; /* More board flags if sromrev >= 4 */ - u32 machwcap; /* MAC capabilities */ - u32 machwcap_backup; /* backup of machwcap */ - - struct si_pub *sih; /* SI handle (cookie for siutils calls) */ - struct bcma_device *d11core; /* pointer to 802.11 core */ - struct phy_shim_info *physhim; /* phy shim layer handler */ - struct shared_phy *phy_sh; /* pointer to shared phy state */ - struct brcms_hw_band *band;/* pointer to active per-band state */ - /* band state per phy/radio */ - struct brcms_hw_band *bandstate[MAXBANDS]; - u16 bmac_phytxant; /* cache of high phytxant state */ - bool shortslot; /* currently using 11g ShortSlot timing */ - u16 SRL; /* 802.11 dot11ShortRetryLimit */ - u16 LRL; /* 802.11 dot11LongRetryLimit */ - u16 SFBL; /* Short Frame Rate Fallback Limit */ - u16 LFBL; /* Long Frame Rate Fallback Limit */ - - bool up; /* d11 hardware up and running */ - uint now; /* # elapsed seconds */ - uint _nbands; /* # bands supported */ - u16 chanspec; /* bmac chanspec shadow */ - - uint *txavail[NFIFO]; /* # tx descriptors available */ - const u16 *xmtfifo_sz; /* fifo size in 256B for each xmt fifo */ - - u32 pllreq; /* pll requests to keep PLL on */ - - u8 suspended_fifos; /* Which TX fifo to remain awake for */ - u32 maccontrol; /* Cached value of maccontrol */ - uint mac_suspend_depth; /* current depth of mac_suspend levels */ - u32 wake_override; /* bit flags to force MAC to WAKE mode */ - u32 mute_override; /* Prevent ucode from sending beacons */ - u8 etheraddr[ETH_ALEN]; /* currently configured ethernet address */ - bool noreset; /* true= do not reset hw, used by WLC_OUT */ - bool forcefastclk; /* true if h/w is forcing to use fast clk */ - bool clk; /* core is out of reset and has clock */ - bool sbclk; /* sb has clock */ - bool phyclk; /* phy is out of reset and has clock */ - - bool ucode_loaded; /* true after ucode downloaded */ - - - u8 hw_stf_ss_opmode; /* STF single stream operation mode */ - u8 antsel_type; /* Type of boardlevel mimo antenna switch-logic - * 0 = N/A, 1 = 2x4 board, 2 = 2x3 CB2 board - */ - u32 antsel_avail; /* - * put struct antsel_info here if more info is - * needed - */ -}; - -/* - * Principal common driver data structure. - * - * pub: pointer to driver public state. - * wl: pointer to specific private state. - * hw: HW related state. - * clkreq_override: setting for clkreq for PCIE : Auto, 0, 1. - * fastpwrup_dly: time in us needed to bring up d11 fast clock. - * macintstatus: bit channel between isr and dpc. - * macintmask: sw runtime master macintmask value. - * defmacintmask: default "on" macintmask value. - * clk: core is out of reset and has clock. - * core: pointer to active io core. - * band: pointer to active per-band state. - * corestate: per-core state (one per hw core). - * bandstate: per-band state (one per phy/radio). - * qvalid: DirFrmQValid and BcMcFrmQValid. - * ampdu: ampdu module handler. - * asi: antsel module handler. - * cmi: channel manager module handler. - * vendorid: PCI vendor id. - * deviceid: PCI device id. - * ucode_rev: microcode revision. - * machwcap: MAC capabilities, BMAC shadow. - * perm_etheraddr: original sprom local ethernet address. - * bandlocked: disable auto multi-band switching. - * bandinit_pending: track band init in auto band. - * radio_monitor: radio timer is running. - * going_down: down path intermediate variable. - * wdtimer: timer for watchdog routine. - * radio_timer: timer for hw radio button monitor routine. - * monitor: monitor (MPDU sniffing) mode. - * bcnmisc_monitor: bcns promisc mode override for monitor. - * _rifs: enable per-packet rifs. - * bcn_li_bcn: beacon listen interval in # beacons. - * bcn_li_dtim: beacon listen interval in # dtims. - * WDarmed: watchdog timer is armed. - * WDlast: last time wlc_watchdog() was called. - * edcf_txop[IEEE80211_NUM_ACS]: current txop for each ac. - * wme_retries: per-AC retry limits. - * bsscfg: set of BSS configurations, idx 0 is default and always valid. - * cfg: the primary bsscfg (can be AP or STA). - * modulecb: - * mimoft: SIGN or 11N. - * cck_40txbw: 11N, cck tx b/w override when in 40MHZ mode. - * ofdm_40txbw: 11N, ofdm tx b/w override when in 40MHZ mode. - * mimo_40txbw: 11N, mimo tx b/w override when in 40MHZ mode. - * default_bss: configured BSS parameters. - * mc_fid_counter: BC/MC FIFO frame ID counter. - * country_default: saved country for leaving 802.11d auto-country mode. - * autocountry_default: initial country for 802.11d auto-country mode. - * prb_resp_timeout: do not send prb resp if request older - * than this, 0 = disable. - * home_chanspec: shared home chanspec. - * chanspec: target operational channel. - * usr_fragthresh: user configured fragmentation threshold. - * fragthresh[NFIFO]: per-fifo fragmentation thresholds. - * RTSThresh: 802.11 dot11RTSThreshold. - * SRL: 802.11 dot11ShortRetryLimit. - * LRL: 802.11 dot11LongRetryLimit. - * SFBL: Short Frame Rate Fallback Limit. - * LFBL: Long Frame Rate Fallback Limit. - * shortslot: currently using 11g ShortSlot timing. - * shortslot_override: 11g ShortSlot override. - * include_legacy_erp: include Legacy ERP info elt ID 47 as well as g ID 42. - * PLCPHdr_override: 802.11b Preamble Type override. - * stf: - * bcn_rspec: save bcn ratespec purpose. - * tempsense_lasttime; - * tx_duty_cycle_ofdm: maximum allowed duty cycle for OFDM. - * tx_duty_cycle_cck: maximum allowed duty cycle for CCK. - * wiphy: - * pri_scb: primary Station Control Block - */ -struct brcms_c_info { - struct brcms_pub *pub; - struct brcms_info *wl; - struct brcms_hardware *hw; - - /* clock */ - u16 fastpwrup_dly; - - /* interrupt */ - u32 macintstatus; - u32 macintmask; - u32 defmacintmask; - - bool clk; - - /* multiband */ - struct brcms_core *core; - struct brcms_band *band; - struct brcms_core *corestate; - struct brcms_band *bandstate[MAXBANDS]; - - /* packet queue */ - uint qvalid; - - struct ampdu_info *ampdu; - struct antsel_info *asi; - struct brcms_cm_info *cmi; - - u16 vendorid; - u16 deviceid; - uint ucode_rev; - - u8 perm_etheraddr[ETH_ALEN]; - - bool bandlocked; - bool bandinit_pending; - - bool radio_monitor; - bool going_down; - - bool beacon_template_virgin; - - struct brcms_timer *wdtimer; - struct brcms_timer *radio_timer; - - /* promiscuous */ - uint filter_flags; - - /* driver feature */ - bool _rifs; - - /* AP-STA synchronization, power save */ - u8 bcn_li_bcn; - u8 bcn_li_dtim; - - bool WDarmed; - u32 WDlast; - - /* WME */ - u16 edcf_txop[IEEE80211_NUM_ACS]; - - u16 wme_retries[IEEE80211_NUM_ACS]; - - struct brcms_bss_cfg *bsscfg; - - struct modulecb *modulecb; - - u8 mimoft; - s8 cck_40txbw; - s8 ofdm_40txbw; - s8 mimo_40txbw; - - struct brcms_bss_info *default_bss; - - u16 mc_fid_counter; - - char country_default[BRCM_CNTRY_BUF_SZ]; - char autocountry_default[BRCM_CNTRY_BUF_SZ]; - u16 prb_resp_timeout; - - u16 home_chanspec; - - /* PHY parameters */ - u16 chanspec; - u16 usr_fragthresh; - u16 fragthresh[NFIFO]; - u16 RTSThresh; - u16 SRL; - u16 LRL; - u16 SFBL; - u16 LFBL; - - /* network config */ - bool shortslot; - s8 shortslot_override; - bool include_legacy_erp; - - struct brcms_protection *protection; - s8 PLCPHdr_override; - - struct brcms_stf *stf; - - u32 bcn_rspec; - - uint tempsense_lasttime; - - u16 tx_duty_cycle_ofdm; - u16 tx_duty_cycle_cck; - - struct wiphy *wiphy; - struct scb pri_scb; - - struct sk_buff *beacon; - u16 beacon_tim_offset; - u16 beacon_dtim_period; - struct sk_buff *probe_resp; -}; - -/* antsel module specific state */ -struct antsel_info { - struct brcms_c_info *wlc; /* pointer to main wlc structure */ - struct brcms_pub *pub; /* pointer to public fn */ - u8 antsel_type; /* Type of boardlevel mimo antenna switch-logic - * 0 = N/A, 1 = 2x4 board, 2 = 2x3 CB2 board - */ - u8 antsel_antswitch; /* board level antenna switch type */ - bool antsel_avail; /* Ant selection availability (SROM based) */ - struct brcms_antselcfg antcfg_11n; /* antenna configuration */ - struct brcms_antselcfg antcfg_cur; /* current antenna config (auto) */ -}; - -enum brcms_bss_type { - BRCMS_TYPE_STATION, - BRCMS_TYPE_AP, - BRCMS_TYPE_ADHOC, -}; - -/* - * BSS configuration state - * - * wlc: wlc to which this bsscfg belongs to. - * type: interface type - * SSID_len: the length of SSID - * SSID: SSID string - * - * - * BSSID: BSSID (associated) - * cur_etheraddr: h/w address - * flags: BSSCFG flags; see below - * - * current_bss: BSS parms in ASSOCIATED state - * - * - * ID: 'unique' ID of this bsscfg, assigned at bsscfg allocation - */ -struct brcms_bss_cfg { - struct brcms_c_info *wlc; - enum brcms_bss_type type; - u8 SSID_len; - u8 SSID[IEEE80211_MAX_SSID_LEN]; - u8 BSSID[ETH_ALEN]; - struct brcms_bss_info *current_bss; -}; - -int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p); -int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, - uint *blocks); - -int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config); -void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags); -u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, uint mac_len); -u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, - bool use_rspec, u16 mimo_ctlchbw); -u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only, - u32 rts_rate, u32 frame_rate, - u8 rts_preamble_type, u8 frame_preamble_type, - uint frame_len, bool ba); -void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, - struct ieee80211_sta *sta, void (*dma_callback_fn)); -void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend); -int brcms_c_set_nmode(struct brcms_c_info *wlc); -void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, u32 bcn_rate); -void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type); -void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, - bool mute, struct txpwr_limits *txpwr); -void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v); -u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset); -void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val, - int bands); -void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags); -void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val); -void brcms_b_phy_reset(struct brcms_hardware *wlc_hw); -void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw); -void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw); -void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw, - u32 override_bit); -void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw, - u32 override_bit); -void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, - int len, void *buf); -u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate); -void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset, - const void *buf, int len, u32 sel); -void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, - void *buf, int len, u32 sel); -void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode); -u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw); -void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk); -void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk); -void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on); -void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant); -void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode); -void brcms_c_init_scb(struct scb *scb); - -#endif /* _BRCM_MAIN_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c deleted file mode 100644 index 1c4e9dd57960..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c +++ /dev/null @@ -1,2953 +0,0 @@ -/* - * Copyright (c) 2010 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 -#include -#include -#include -#include -#include "phy_hal.h" -#include "phy_int.h" -#include "phy_radio.h" -#include "phy_lcn.h" -#include "phyreg_n.h" - -#define VALID_N_RADIO(radioid) ((radioid == BCM2055_ID) || \ - (radioid == BCM2056_ID) || \ - (radioid == BCM2057_ID)) - -#define VALID_LCN_RADIO(radioid) (radioid == BCM2064_ID) - -#define VALID_RADIO(pi, radioid) ( \ - (ISNPHY(pi) ? VALID_N_RADIO(radioid) : false) || \ - (ISLCNPHY(pi) ? VALID_LCN_RADIO(radioid) : false)) - -/* basic mux operation - can be optimized on several architectures */ -#define MUX(pred, true, false) ((pred) ? (true) : (false)) - -/* modulo inc/dec - assumes x E [0, bound - 1] */ -#define MODINC(x, bound) MUX((x) == (bound) - 1, 0, (x) + 1) - -/* modulo inc/dec, bound = 2^k */ -#define MODDEC_POW2(x, bound) (((x) - 1) & ((bound) - 1)) -#define MODINC_POW2(x, bound) (((x) + 1) & ((bound) - 1)) - -struct chan_info_basic { - u16 chan; - u16 freq; -}; - -static const struct chan_info_basic chan_info_all[] = { - {1, 2412}, - {2, 2417}, - {3, 2422}, - {4, 2427}, - {5, 2432}, - {6, 2437}, - {7, 2442}, - {8, 2447}, - {9, 2452}, - {10, 2457}, - {11, 2462}, - {12, 2467}, - {13, 2472}, - {14, 2484}, - - {34, 5170}, - {38, 5190}, - {42, 5210}, - {46, 5230}, - - {36, 5180}, - {40, 5200}, - {44, 5220}, - {48, 5240}, - {52, 5260}, - {56, 5280}, - {60, 5300}, - {64, 5320}, - - {100, 5500}, - {104, 5520}, - {108, 5540}, - {112, 5560}, - {116, 5580}, - {120, 5600}, - {124, 5620}, - {128, 5640}, - {132, 5660}, - {136, 5680}, - {140, 5700}, - - {149, 5745}, - {153, 5765}, - {157, 5785}, - {161, 5805}, - {165, 5825}, - - {184, 4920}, - {188, 4940}, - {192, 4960}, - {196, 4980}, - {200, 5000}, - {204, 5020}, - {208, 5040}, - {212, 5060}, - {216, 5080} -}; - -static const u8 ofdm_rate_lookup[] = { - - BRCM_RATE_48M, - BRCM_RATE_24M, - BRCM_RATE_12M, - BRCM_RATE_6M, - BRCM_RATE_54M, - BRCM_RATE_36M, - BRCM_RATE_18M, - BRCM_RATE_9M -}; - -#define PHY_WREG_LIMIT 24 - -void wlc_phyreg_enter(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - wlapi_bmac_ucode_wake_override_phyreg_set(pi->sh->physhim); -} - -void wlc_phyreg_exit(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - wlapi_bmac_ucode_wake_override_phyreg_clear(pi->sh->physhim); -} - -void wlc_radioreg_enter(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - wlapi_bmac_mctrl(pi->sh->physhim, MCTL_LOCK_RADIO, MCTL_LOCK_RADIO); - - udelay(10); -} - -void wlc_radioreg_exit(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - (void)bcma_read16(pi->d11core, D11REGOFFS(phyversion)); - pi->phy_wreg = 0; - wlapi_bmac_mctrl(pi->sh->physhim, MCTL_LOCK_RADIO, 0); -} - -u16 read_radio_reg(struct brcms_phy *pi, u16 addr) -{ - u16 data; - - if ((addr == RADIO_IDCODE)) - return 0xffff; - - switch (pi->pubpi.phy_type) { - case PHY_TYPE_N: - if (!CONF_HAS(PHYTYPE, PHY_TYPE_N)) - break; - if (NREV_GE(pi->pubpi.phy_rev, 7)) - addr |= RADIO_2057_READ_OFF; - else - addr |= RADIO_2055_READ_OFF; - break; - - case PHY_TYPE_LCN: - if (!CONF_HAS(PHYTYPE, PHY_TYPE_LCN)) - break; - addr |= RADIO_2064_READ_OFF; - break; - - default: - break; - } - - if ((D11REV_GE(pi->sh->corerev, 24)) || - (D11REV_IS(pi->sh->corerev, 22) - && (pi->pubpi.phy_type != PHY_TYPE_SSN))) { - bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), addr); - data = bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); - } else { - bcma_wflush16(pi->d11core, D11REGOFFS(phy4waddr), addr); - data = bcma_read16(pi->d11core, D11REGOFFS(phy4wdatalo)); - } - pi->phy_wreg = 0; - - return data; -} - -void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val) -{ - if ((D11REV_GE(pi->sh->corerev, 24)) || - (D11REV_IS(pi->sh->corerev, 22) - && (pi->pubpi.phy_type != PHY_TYPE_SSN))) { - - bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), addr); - bcma_write16(pi->d11core, D11REGOFFS(radioregdata), val); - } else { - bcma_wflush16(pi->d11core, D11REGOFFS(phy4waddr), addr); - bcma_write16(pi->d11core, D11REGOFFS(phy4wdatalo), val); - } - - if ((pi->d11core->bus->hosttype == BCMA_HOSTTYPE_PCI) && - (++pi->phy_wreg >= pi->phy_wreg_limit)) { - (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); - pi->phy_wreg = 0; - } -} - -static u32 read_radio_id(struct brcms_phy *pi) -{ - u32 id; - - if (D11REV_GE(pi->sh->corerev, 24)) { - u32 b0, b1, b2; - - bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), 0); - b0 = (u32) bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); - bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), 1); - b1 = (u32) bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); - bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), 2); - b2 = (u32) bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); - - id = ((b0 & 0xf) << 28) | (((b2 << 8) | b1) << 12) | ((b0 >> 4) - & 0xf); - } else { - bcma_wflush16(pi->d11core, D11REGOFFS(phy4waddr), RADIO_IDCODE); - id = (u32) bcma_read16(pi->d11core, D11REGOFFS(phy4wdatalo)); - id |= (u32) bcma_read16(pi->d11core, - D11REGOFFS(phy4wdatahi)) << 16; - } - pi->phy_wreg = 0; - return id; -} - -void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val) -{ - u16 rval; - - rval = read_radio_reg(pi, addr); - write_radio_reg(pi, addr, (rval & val)); -} - -void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val) -{ - u16 rval; - - rval = read_radio_reg(pi, addr); - write_radio_reg(pi, addr, (rval | val)); -} - -void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask) -{ - u16 rval; - - rval = read_radio_reg(pi, addr); - write_radio_reg(pi, addr, (rval ^ mask)); -} - -void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val) -{ - u16 rval; - - rval = read_radio_reg(pi, addr); - write_radio_reg(pi, addr, (rval & ~mask) | (val & mask)); -} - -void write_phy_channel_reg(struct brcms_phy *pi, uint val) -{ - bcma_write16(pi->d11core, D11REGOFFS(phychannel), val); -} - -u16 read_phy_reg(struct brcms_phy *pi, u16 addr) -{ - bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); - - pi->phy_wreg = 0; - return bcma_read16(pi->d11core, D11REGOFFS(phyregdata)); -} - -void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val) -{ -#ifdef CONFIG_BCM47XX - bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); - bcma_write16(pi->d11core, D11REGOFFS(phyregdata), val); - if (addr == 0x72) - (void)bcma_read16(pi->d11core, D11REGOFFS(phyregdata)); -#else - bcma_write32(pi->d11core, D11REGOFFS(phyregaddr), addr | (val << 16)); - if ((pi->d11core->bus->hosttype == BCMA_HOSTTYPE_PCI) && - (++pi->phy_wreg >= pi->phy_wreg_limit)) { - pi->phy_wreg = 0; - (void)bcma_read16(pi->d11core, D11REGOFFS(phyversion)); - } -#endif -} - -void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val) -{ - bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); - bcma_mask16(pi->d11core, D11REGOFFS(phyregdata), val); - pi->phy_wreg = 0; -} - -void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val) -{ - bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); - bcma_set16(pi->d11core, D11REGOFFS(phyregdata), val); - pi->phy_wreg = 0; -} - -void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val) -{ - val &= mask; - bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); - bcma_maskset16(pi->d11core, D11REGOFFS(phyregdata), ~mask, val); - pi->phy_wreg = 0; -} - -static void wlc_set_phy_uninitted(struct brcms_phy *pi) -{ - int i, j; - - pi->initialized = false; - - pi->tx_vos = 0xffff; - pi->nrssi_table_delta = 0x7fffffff; - pi->rc_cal = 0xffff; - pi->mintxbias = 0xffff; - pi->txpwridx = -1; - if (ISNPHY(pi)) { - pi->phy_spuravoid = SPURAVOID_DISABLE; - - if (NREV_GE(pi->pubpi.phy_rev, 3) - && NREV_LT(pi->pubpi.phy_rev, 7)) - pi->phy_spuravoid = SPURAVOID_AUTO; - - pi->nphy_papd_skip = 0; - pi->nphy_papd_epsilon_offset[0] = 0xf588; - pi->nphy_papd_epsilon_offset[1] = 0xf588; - pi->nphy_txpwr_idx[0] = 128; - pi->nphy_txpwr_idx[1] = 128; - pi->nphy_txpwrindex[0].index_internal = 40; - pi->nphy_txpwrindex[1].index_internal = 40; - pi->phy_pabias = 0; - } else { - pi->phy_spuravoid = SPURAVOID_AUTO; - } - pi->radiopwr = 0xffff; - for (i = 0; i < STATIC_NUM_RF; i++) { - for (j = 0; j < STATIC_NUM_BB; j++) - pi->stats_11b_txpower[i][j] = -1; - } -} - -struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp) -{ - struct shared_phy *sh; - - sh = kzalloc(sizeof(struct shared_phy), GFP_ATOMIC); - if (sh == NULL) - return NULL; - - sh->physhim = shp->physhim; - sh->unit = shp->unit; - sh->corerev = shp->corerev; - - sh->vid = shp->vid; - sh->did = shp->did; - sh->chip = shp->chip; - sh->chiprev = shp->chiprev; - sh->chippkg = shp->chippkg; - sh->sromrev = shp->sromrev; - sh->boardtype = shp->boardtype; - sh->boardrev = shp->boardrev; - sh->boardflags = shp->boardflags; - sh->boardflags2 = shp->boardflags2; - - sh->fast_timer = PHY_SW_TIMER_FAST; - sh->slow_timer = PHY_SW_TIMER_SLOW; - sh->glacial_timer = PHY_SW_TIMER_GLACIAL; - - sh->rssi_mode = RSSI_ANT_MERGE_MAX; - - return sh; -} - -static void wlc_phy_timercb_phycal(struct brcms_phy *pi) -{ - uint delay = 5; - - if (PHY_PERICAL_MPHASE_PENDING(pi)) { - if (!pi->sh->up) { - wlc_phy_cal_perical_mphase_reset(pi); - return; - } - - if (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi)) { - - delay = 1000; - wlc_phy_cal_perical_mphase_restart(pi); - } else - wlc_phy_cal_perical_nphy_run(pi, PHY_PERICAL_AUTO); - wlapi_add_timer(pi->phycal_timer, delay, 0); - return; - } - -} - -static u32 wlc_phy_get_radio_ver(struct brcms_phy *pi) -{ - u32 ver; - - ver = read_radio_id(pi); - - return ver; -} - -struct brcms_phy_pub * -wlc_phy_attach(struct shared_phy *sh, struct bcma_device *d11core, - int bandtype, struct wiphy *wiphy) -{ - struct brcms_phy *pi; - u32 sflags = 0; - uint phyversion; - u32 idcode; - int i; - - if (D11REV_IS(sh->corerev, 4)) - sflags = SISF_2G_PHY | SISF_5G_PHY; - else - sflags = bcma_aread32(d11core, BCMA_IOST); - - if (bandtype == BRCM_BAND_5G) { - if ((sflags & (SISF_5G_PHY | SISF_DB_PHY)) == 0) - return NULL; - } - - pi = sh->phy_head; - if ((sflags & SISF_DB_PHY) && pi) { - wlapi_bmac_corereset(pi->sh->physhim, pi->pubpi.coreflags); - pi->refcnt++; - return &pi->pubpi_ro; - } - - pi = kzalloc(sizeof(struct brcms_phy), GFP_ATOMIC); - if (pi == NULL) - return NULL; - pi->wiphy = wiphy; - pi->d11core = d11core; - pi->sh = sh; - pi->phy_init_por = true; - pi->phy_wreg_limit = PHY_WREG_LIMIT; - - pi->txpwr_percent = 100; - - pi->do_initcal = true; - - pi->phycal_tempdelta = 0; - - if (bandtype == BRCM_BAND_2G && (sflags & SISF_2G_PHY)) - pi->pubpi.coreflags = SICF_GMODE; - - wlapi_bmac_corereset(pi->sh->physhim, pi->pubpi.coreflags); - phyversion = bcma_read16(pi->d11core, D11REGOFFS(phyversion)); - - pi->pubpi.phy_type = PHY_TYPE(phyversion); - pi->pubpi.phy_rev = phyversion & PV_PV_MASK; - - if (pi->pubpi.phy_type == PHY_TYPE_LCNXN) { - pi->pubpi.phy_type = PHY_TYPE_N; - pi->pubpi.phy_rev += LCNXN_BASEREV; - } - pi->pubpi.phy_corenum = PHY_CORE_NUM_2; - pi->pubpi.ana_rev = (phyversion & PV_AV_MASK) >> PV_AV_SHIFT; - - if (pi->pubpi.phy_type != PHY_TYPE_N && - pi->pubpi.phy_type != PHY_TYPE_LCN) - goto err; - - if (bandtype == BRCM_BAND_5G) { - if (!ISNPHY(pi)) - goto err; - } else if (!ISNPHY(pi) && !ISLCNPHY(pi)) { - goto err; - } - - wlc_phy_anacore((struct brcms_phy_pub *) pi, ON); - - idcode = wlc_phy_get_radio_ver(pi); - pi->pubpi.radioid = - (idcode & IDCODE_ID_MASK) >> IDCODE_ID_SHIFT; - pi->pubpi.radiorev = - (idcode & IDCODE_REV_MASK) >> IDCODE_REV_SHIFT; - pi->pubpi.radiover = - (idcode & IDCODE_VER_MASK) >> IDCODE_VER_SHIFT; - if (!VALID_RADIO(pi, pi->pubpi.radioid)) - goto err; - - wlc_phy_switch_radio((struct brcms_phy_pub *) pi, OFF); - - wlc_set_phy_uninitted(pi); - - pi->bw = WL_CHANSPEC_BW_20; - pi->radio_chanspec = (bandtype == BRCM_BAND_2G) ? - ch20mhz_chspec(1) : ch20mhz_chspec(36); - - pi->rxiq_samps = PHY_NOISE_SAMPLE_LOG_NUM_NPHY; - pi->rxiq_antsel = ANT_RX_DIV_DEF; - - pi->watchdog_override = true; - - pi->cal_type_override = PHY_PERICAL_AUTO; - - pi->nphy_saved_noisevars.bufcount = 0; - - if (ISNPHY(pi)) - pi->min_txpower = PHY_TXPWR_MIN_NPHY; - else - pi->min_txpower = PHY_TXPWR_MIN; - - pi->sh->phyrxchain = 0x3; - - pi->rx2tx_biasentry = -1; - - pi->phy_txcore_disable_temp = PHY_CHAIN_TX_DISABLE_TEMP; - pi->phy_txcore_enable_temp = - PHY_CHAIN_TX_DISABLE_TEMP - PHY_HYSTERESIS_DELTATEMP; - pi->phy_tempsense_offset = 0; - pi->phy_txcore_heatedup = false; - - pi->nphy_lastcal_temp = -50; - - pi->phynoise_polling = true; - if (ISNPHY(pi) || ISLCNPHY(pi)) - pi->phynoise_polling = false; - - for (i = 0; i < TXP_NUM_RATES; i++) { - pi->txpwr_limit[i] = BRCMS_TXPWR_MAX; - pi->txpwr_env_limit[i] = BRCMS_TXPWR_MAX; - pi->tx_user_target[i] = BRCMS_TXPWR_MAX; - } - - pi->radiopwr_override = RADIOPWR_OVERRIDE_DEF; - - pi->user_txpwr_at_rfport = false; - - if (ISNPHY(pi)) { - - pi->phycal_timer = wlapi_init_timer(pi->sh->physhim, - wlc_phy_timercb_phycal, - pi, "phycal"); - if (!pi->phycal_timer) - goto err; - - if (!wlc_phy_attach_nphy(pi)) - goto err; - - } else if (ISLCNPHY(pi)) { - if (!wlc_phy_attach_lcnphy(pi)) - goto err; - - } - - pi->refcnt++; - pi->next = pi->sh->phy_head; - sh->phy_head = pi; - - memcpy(&pi->pubpi_ro, &pi->pubpi, sizeof(struct brcms_phy_pub)); - - return &pi->pubpi_ro; - -err: - kfree(pi); - return NULL; -} - -void wlc_phy_detach(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - if (pih) { - if (--pi->refcnt) - return; - - if (pi->phycal_timer) { - wlapi_free_timer(pi->phycal_timer); - pi->phycal_timer = NULL; - } - - if (pi->sh->phy_head == pi) - pi->sh->phy_head = pi->next; - else if (pi->sh->phy_head->next == pi) - pi->sh->phy_head->next = NULL; - - if (pi->pi_fptr.detach) - (pi->pi_fptr.detach)(pi); - - kfree(pi); - } -} - -bool -wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype, u16 *phyrev, - u16 *radioid, u16 *radiover) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - *phytype = (u16) pi->pubpi.phy_type; - *phyrev = (u16) pi->pubpi.phy_rev; - *radioid = pi->pubpi.radioid; - *radiover = pi->pubpi.radiorev; - - return true; -} - -bool wlc_phy_get_encore(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - return pi->pubpi.abgphy_encore; -} - -u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - return pi->pubpi.coreflags; -} - -void wlc_phy_anacore(struct brcms_phy_pub *pih, bool on) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - if (ISNPHY(pi)) { - if (on) { - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - write_phy_reg(pi, 0xa6, 0x0d); - write_phy_reg(pi, 0x8f, 0x0); - write_phy_reg(pi, 0xa7, 0x0d); - write_phy_reg(pi, 0xa5, 0x0); - } else { - write_phy_reg(pi, 0xa5, 0x0); - } - } else { - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - write_phy_reg(pi, 0x8f, 0x07ff); - write_phy_reg(pi, 0xa6, 0x0fd); - write_phy_reg(pi, 0xa5, 0x07ff); - write_phy_reg(pi, 0xa7, 0x0fd); - } else { - write_phy_reg(pi, 0xa5, 0x7fff); - } - } - } else if (ISLCNPHY(pi)) { - if (on) { - and_phy_reg(pi, 0x43b, - ~((0x1 << 0) | (0x1 << 1) | (0x1 << 2))); - } else { - or_phy_reg(pi, 0x43c, - (0x1 << 0) | (0x1 << 1) | (0x1 << 2)); - or_phy_reg(pi, 0x43b, - (0x1 << 0) | (0x1 << 1) | (0x1 << 2)); - } - } -} - -u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - u32 phy_bw_clkbits = 0; - - if (pi && (ISNPHY(pi) || ISLCNPHY(pi))) { - switch (pi->bw) { - case WL_CHANSPEC_BW_10: - phy_bw_clkbits = SICF_BW10; - break; - case WL_CHANSPEC_BW_20: - phy_bw_clkbits = SICF_BW20; - break; - case WL_CHANSPEC_BW_40: - phy_bw_clkbits = SICF_BW40; - break; - default: - break; - } - } - - return phy_bw_clkbits; -} - -void wlc_phy_por_inform(struct brcms_phy_pub *ppi) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - pi->phy_init_por = true; -} - -void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - pi->edcrs_threshold_lock = lock; - - write_phy_reg(pi, 0x22c, 0x46b); - write_phy_reg(pi, 0x22d, 0x46b); - write_phy_reg(pi, 0x22e, 0x3c0); - write_phy_reg(pi, 0x22f, 0x3c0); -} - -void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - pi->do_initcal = initcal; -} - -void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *pih, bool newstate) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - if (!pi || !pi->sh) - return; - - pi->sh->clk = newstate; -} - -void wlc_phy_hw_state_upd(struct brcms_phy_pub *pih, bool newstate) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - if (!pi || !pi->sh) - return; - - pi->sh->up = newstate; -} - -void wlc_phy_init(struct brcms_phy_pub *pih, u16 chanspec) -{ - u32 mc; - void (*phy_init)(struct brcms_phy *) = NULL; - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - if (pi->init_in_progress) - return; - - pi->init_in_progress = true; - - pi->radio_chanspec = chanspec; - - mc = bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); - if (WARN(mc & MCTL_EN_MAC, "HW error MAC running on init")) - return; - - if (!(pi->measure_hold & PHY_HOLD_FOR_SCAN)) - pi->measure_hold |= PHY_HOLD_FOR_NOT_ASSOC; - - if (WARN(!(bcma_aread32(pi->d11core, BCMA_IOST) & SISF_FCLKA), - "HW error SISF_FCLKA\n")) - return; - - phy_init = pi->pi_fptr.init; - - if (phy_init == NULL) - return; - - wlc_phy_anacore(pih, ON); - - if (CHSPEC_BW(pi->radio_chanspec) != pi->bw) - wlapi_bmac_bw_set(pi->sh->physhim, - CHSPEC_BW(pi->radio_chanspec)); - - pi->nphy_gain_boost = true; - - wlc_phy_switch_radio((struct brcms_phy_pub *) pi, ON); - - (*phy_init)(pi); - - pi->phy_init_por = false; - - if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) - wlc_phy_do_dummy_tx(pi, true, OFF); - - if (!(ISNPHY(pi))) - wlc_phy_txpower_update_shm(pi); - - wlc_phy_ant_rxdiv_set((struct brcms_phy_pub *) pi, pi->sh->rx_antdiv); - - pi->init_in_progress = false; -} - -void wlc_phy_cal_init(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - void (*cal_init)(struct brcms_phy *) = NULL; - - if (WARN((bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & - MCTL_EN_MAC) != 0, "HW error: MAC enabled during phy cal\n")) - return; - - if (!pi->initialized) { - cal_init = pi->pi_fptr.calinit; - if (cal_init) - (*cal_init)(pi); - - pi->initialized = true; - } -} - -int wlc_phy_down(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - int callbacks = 0; - - if (pi->phycal_timer - && !wlapi_del_timer(pi->phycal_timer)) - callbacks++; - - pi->nphy_iqcal_chanspec_2G = 0; - pi->nphy_iqcal_chanspec_5G = 0; - - return callbacks; -} - -void -wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, uint tbl_offset, - u16 tblAddr, u16 tblDataHi, u16 tblDataLo) -{ - write_phy_reg(pi, tblAddr, (tbl_id << 10) | tbl_offset); - - pi->tbl_data_hi = tblDataHi; - pi->tbl_data_lo = tblDataLo; - - if (pi->sh->chip == BCMA_CHIP_ID_BCM43224 && - pi->sh->chiprev == 1) { - pi->tbl_addr = tblAddr; - pi->tbl_save_id = tbl_id; - pi->tbl_save_offset = tbl_offset; - } -} - -void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val) -{ - if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) && - (pi->sh->chiprev == 1) && - (pi->tbl_save_id == NPHY_TBL_ID_ANTSWCTRLLUT)) { - read_phy_reg(pi, pi->tbl_data_lo); - - write_phy_reg(pi, pi->tbl_addr, - (pi->tbl_save_id << 10) | pi->tbl_save_offset); - pi->tbl_save_offset++; - } - - if (width == 32) { - write_phy_reg(pi, pi->tbl_data_hi, (u16) (val >> 16)); - write_phy_reg(pi, pi->tbl_data_lo, (u16) val); - } else { - write_phy_reg(pi, pi->tbl_data_lo, (u16) val); - } -} - -void -wlc_phy_write_table(struct brcms_phy *pi, const struct phytbl_info *ptbl_info, - u16 tblAddr, u16 tblDataHi, u16 tblDataLo) -{ - uint idx; - uint tbl_id = ptbl_info->tbl_id; - uint tbl_offset = ptbl_info->tbl_offset; - uint tbl_width = ptbl_info->tbl_width; - const u8 *ptbl_8b = (const u8 *)ptbl_info->tbl_ptr; - const u16 *ptbl_16b = (const u16 *)ptbl_info->tbl_ptr; - const u32 *ptbl_32b = (const u32 *)ptbl_info->tbl_ptr; - - write_phy_reg(pi, tblAddr, (tbl_id << 10) | tbl_offset); - - for (idx = 0; idx < ptbl_info->tbl_len; idx++) { - - if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) && - (pi->sh->chiprev == 1) && - (tbl_id == NPHY_TBL_ID_ANTSWCTRLLUT)) { - read_phy_reg(pi, tblDataLo); - - write_phy_reg(pi, tblAddr, - (tbl_id << 10) | (tbl_offset + idx)); - } - - if (tbl_width == 32) { - write_phy_reg(pi, tblDataHi, - (u16) (ptbl_32b[idx] >> 16)); - write_phy_reg(pi, tblDataLo, (u16) ptbl_32b[idx]); - } else if (tbl_width == 16) { - write_phy_reg(pi, tblDataLo, ptbl_16b[idx]); - } else { - write_phy_reg(pi, tblDataLo, ptbl_8b[idx]); - } - } -} - -void -wlc_phy_read_table(struct brcms_phy *pi, const struct phytbl_info *ptbl_info, - u16 tblAddr, u16 tblDataHi, u16 tblDataLo) -{ - uint idx; - uint tbl_id = ptbl_info->tbl_id; - uint tbl_offset = ptbl_info->tbl_offset; - uint tbl_width = ptbl_info->tbl_width; - u8 *ptbl_8b = (u8 *)ptbl_info->tbl_ptr; - u16 *ptbl_16b = (u16 *)ptbl_info->tbl_ptr; - u32 *ptbl_32b = (u32 *)ptbl_info->tbl_ptr; - - write_phy_reg(pi, tblAddr, (tbl_id << 10) | tbl_offset); - - for (idx = 0; idx < ptbl_info->tbl_len; idx++) { - - if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) && - (pi->sh->chiprev == 1)) { - (void)read_phy_reg(pi, tblDataLo); - - write_phy_reg(pi, tblAddr, - (tbl_id << 10) | (tbl_offset + idx)); - } - - if (tbl_width == 32) { - ptbl_32b[idx] = read_phy_reg(pi, tblDataLo); - ptbl_32b[idx] |= (read_phy_reg(pi, tblDataHi) << 16); - } else if (tbl_width == 16) { - ptbl_16b[idx] = read_phy_reg(pi, tblDataLo); - } else { - ptbl_8b[idx] = (u8) read_phy_reg(pi, tblDataLo); - } - } -} - -uint -wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi, - struct radio_20xx_regs *radioregs) -{ - uint i = 0; - - do { - if (radioregs[i].do_init) - write_radio_reg(pi, radioregs[i].address, - (u16) radioregs[i].init); - - i++; - } while (radioregs[i].address != 0xffff); - - return i; -} - -uint -wlc_phy_init_radio_regs(struct brcms_phy *pi, - const struct radio_regs *radioregs, - u16 core_offset) -{ - uint i = 0; - uint count = 0; - - do { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - if (radioregs[i].do_init_a) { - write_radio_reg(pi, - radioregs[i]. - address | core_offset, - (u16) radioregs[i].init_a); - if (ISNPHY(pi) && (++count % 4 == 0)) - BRCMS_PHY_WAR_PR51571(pi); - } - } else { - if (radioregs[i].do_init_g) { - write_radio_reg(pi, - radioregs[i]. - address | core_offset, - (u16) radioregs[i].init_g); - if (ISNPHY(pi) && (++count % 4 == 0)) - BRCMS_PHY_WAR_PR51571(pi); - } - } - - i++; - } while (radioregs[i].address != 0xffff); - - return i; -} - -void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on) -{ -#define DUMMY_PKT_LEN 20 - struct bcma_device *core = pi->d11core; - int i, count; - u8 ofdmpkt[DUMMY_PKT_LEN] = { - 0xcc, 0x01, 0x02, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 - }; - u8 cckpkt[DUMMY_PKT_LEN] = { - 0x6e, 0x84, 0x0b, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 - }; - u32 *dummypkt; - - dummypkt = (u32 *) (ofdm ? ofdmpkt : cckpkt); - wlapi_bmac_write_template_ram(pi->sh->physhim, 0, DUMMY_PKT_LEN, - dummypkt); - - bcma_write16(core, D11REGOFFS(xmtsel), 0); - - if (D11REV_GE(pi->sh->corerev, 11)) - bcma_write16(core, D11REGOFFS(wepctl), 0x100); - else - bcma_write16(core, D11REGOFFS(wepctl), 0); - - bcma_write16(core, D11REGOFFS(txe_phyctl), - (ofdm ? 1 : 0) | PHY_TXC_ANT_0); - if (ISNPHY(pi) || ISLCNPHY(pi)) - bcma_write16(core, D11REGOFFS(txe_phyctl1), 0x1A02); - - bcma_write16(core, D11REGOFFS(txe_wm_0), 0); - bcma_write16(core, D11REGOFFS(txe_wm_1), 0); - - bcma_write16(core, D11REGOFFS(xmttplatetxptr), 0); - bcma_write16(core, D11REGOFFS(xmttxcnt), DUMMY_PKT_LEN); - - bcma_write16(core, D11REGOFFS(xmtsel), - ((8 << 8) | (1 << 5) | (1 << 2) | 2)); - - bcma_write16(core, D11REGOFFS(txe_ctl), 0); - - if (!pa_on) { - if (ISNPHY(pi)) - wlc_phy_pa_override_nphy(pi, OFF); - } - - if (ISNPHY(pi) || ISLCNPHY(pi)) - bcma_write16(core, D11REGOFFS(txe_aux), 0xD0); - else - bcma_write16(core, D11REGOFFS(txe_aux), ((1 << 5) | (1 << 4))); - - (void)bcma_read16(core, D11REGOFFS(txe_aux)); - - i = 0; - count = ofdm ? 30 : 250; - while ((i++ < count) - && (bcma_read16(core, D11REGOFFS(txe_status)) & (1 << 7))) - udelay(10); - - i = 0; - - while ((i++ < 10) && - ((bcma_read16(core, D11REGOFFS(txe_status)) & (1 << 10)) == 0)) - udelay(10); - - i = 0; - - while ((i++ < 10) && - ((bcma_read16(core, D11REGOFFS(ifsstat)) & (1 << 8)))) - udelay(10); - - if (!pa_on) { - if (ISNPHY(pi)) - wlc_phy_pa_override_nphy(pi, ON); - } -} - -void wlc_phy_hold_upd(struct brcms_phy_pub *pih, u32 id, bool set) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - if (set) - mboolset(pi->measure_hold, id); - else - mboolclr(pi->measure_hold, id); - - return; -} - -void wlc_phy_mute_upd(struct brcms_phy_pub *pih, bool mute, u32 flags) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - if (mute) - mboolset(pi->measure_hold, PHY_HOLD_FOR_MUTE); - else - mboolclr(pi->measure_hold, PHY_HOLD_FOR_MUTE); - - if (!mute && (flags & PHY_MUTE_FOR_PREISM)) - pi->nphy_perical_last = pi->sh->now - pi->sh->glacial_timer; - return; -} - -void wlc_phy_clear_tssi(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - if (ISNPHY(pi)) { - return; - } else { - wlapi_bmac_write_shm(pi->sh->physhim, M_B_TSSI_0, NULL_TSSI_W); - wlapi_bmac_write_shm(pi->sh->physhim, M_B_TSSI_1, NULL_TSSI_W); - wlapi_bmac_write_shm(pi->sh->physhim, M_G_TSSI_0, NULL_TSSI_W); - wlapi_bmac_write_shm(pi->sh->physhim, M_G_TSSI_1, NULL_TSSI_W); - } -} - -static bool wlc_phy_cal_txpower_recalc_sw(struct brcms_phy *pi) -{ - return false; -} - -void wlc_phy_switch_radio(struct brcms_phy_pub *pih, bool on) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); - - if (ISNPHY(pi)) { - wlc_phy_switch_radio_nphy(pi, on); - } else if (ISLCNPHY(pi)) { - if (on) { - and_phy_reg(pi, 0x44c, - ~((0x1 << 8) | - (0x1 << 9) | - (0x1 << 10) | (0x1 << 11) | (0x1 << 12))); - and_phy_reg(pi, 0x4b0, ~((0x1 << 3) | (0x1 << 11))); - and_phy_reg(pi, 0x4f9, ~(0x1 << 3)); - } else { - and_phy_reg(pi, 0x44d, - ~((0x1 << 10) | - (0x1 << 11) | - (0x1 << 12) | (0x1 << 13) | (0x1 << 14))); - or_phy_reg(pi, 0x44c, - (0x1 << 8) | - (0x1 << 9) | - (0x1 << 10) | (0x1 << 11) | (0x1 << 12)); - - and_phy_reg(pi, 0x4b7, ~((0x7f << 8))); - and_phy_reg(pi, 0x4b1, ~((0x1 << 13))); - or_phy_reg(pi, 0x4b0, (0x1 << 3) | (0x1 << 11)); - and_phy_reg(pi, 0x4fa, ~((0x1 << 3))); - or_phy_reg(pi, 0x4f9, (0x1 << 3)); - } - } -} - -u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - return pi->bw; -} - -void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - pi->bw = bw; -} - -void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, u16 newch) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - pi->radio_chanspec = newch; - -} - -u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - return pi->radio_chanspec; -} - -void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, u16 chanspec) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - u16 m_cur_channel; - void (*chanspec_set)(struct brcms_phy *, u16) = NULL; - m_cur_channel = CHSPEC_CHANNEL(chanspec); - if (CHSPEC_IS5G(chanspec)) - m_cur_channel |= D11_CURCHANNEL_5G; - if (CHSPEC_IS40(chanspec)) - m_cur_channel |= D11_CURCHANNEL_40; - wlapi_bmac_write_shm(pi->sh->physhim, M_CURCHANNEL, m_cur_channel); - - chanspec_set = pi->pi_fptr.chanset; - if (chanspec_set) - (*chanspec_set)(pi, chanspec); - -} - -int wlc_phy_chanspec_freq2bandrange_lpssn(uint freq) -{ - int range = -1; - - if (freq < 2500) - range = WL_CHAN_FREQ_RANGE_2G; - else if (freq <= 5320) - range = WL_CHAN_FREQ_RANGE_5GL; - else if (freq <= 5700) - range = WL_CHAN_FREQ_RANGE_5GM; - else - range = WL_CHAN_FREQ_RANGE_5GH; - - return range; -} - -int wlc_phy_chanspec_bandrange_get(struct brcms_phy *pi, u16 chanspec) -{ - int range = -1; - uint channel = CHSPEC_CHANNEL(chanspec); - uint freq = wlc_phy_channel2freq(channel); - - if (ISNPHY(pi)) - range = wlc_phy_get_chan_freq_range_nphy(pi, channel); - else if (ISLCNPHY(pi)) - range = wlc_phy_chanspec_freq2bandrange_lpssn(freq); - - return range; -} - -void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi, - bool wide_filter) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - pi->channel_14_wide_filter = wide_filter; - -} - -int wlc_phy_channel2freq(uint channel) -{ - uint i; - - for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) - if (chan_info_all[i].chan == channel) - return chan_info_all[i].freq; - return 0; -} - -void -wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band, - struct brcms_chanvec *channels) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - uint i; - uint channel; - - memset(channels, 0, sizeof(struct brcms_chanvec)); - - for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) { - channel = chan_info_all[i].chan; - - if ((pi->a_band_high_disable) && (channel >= FIRST_REF5_CHANNUM) - && (channel <= LAST_REF5_CHANNUM)) - continue; - - if ((band == BRCM_BAND_2G && channel <= CH_MAX_2G_CHANNEL) || - (band == BRCM_BAND_5G && channel > CH_MAX_2G_CHANNEL)) - setbit(channels->vec, channel); - } -} - -u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, uint band) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - uint i; - uint channel; - u16 chspec; - - for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) { - channel = chan_info_all[i].chan; - - if (ISNPHY(pi) && pi->bw == WL_CHANSPEC_BW_40) { - uint j; - - for (j = 0; j < ARRAY_SIZE(chan_info_all); j++) { - if (chan_info_all[j].chan == - channel + CH_10MHZ_APART) - break; - } - - if (j == ARRAY_SIZE(chan_info_all)) - continue; - - channel = upper_20_sb(channel); - chspec = channel | WL_CHANSPEC_BW_40 | - WL_CHANSPEC_CTL_SB_LOWER; - if (band == BRCM_BAND_2G) - chspec |= WL_CHANSPEC_BAND_2G; - else - chspec |= WL_CHANSPEC_BAND_5G; - } else - chspec = ch20mhz_chspec(channel); - - if ((pi->a_band_high_disable) && (channel >= FIRST_REF5_CHANNUM) - && (channel <= LAST_REF5_CHANNUM)) - continue; - - if ((band == BRCM_BAND_2G && channel <= CH_MAX_2G_CHANNEL) || - (band == BRCM_BAND_5G && channel > CH_MAX_2G_CHANNEL)) - return chspec; - } - - return (u16) INVCHANSPEC; -} - -int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, bool *override) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - *qdbm = pi->tx_user_target[0]; - if (override != NULL) - *override = pi->txpwroverride; - return 0; -} - -void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi, - struct txpwr_limits *txpwr) -{ - bool mac_enabled = false; - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - memcpy(&pi->tx_user_target[TXP_FIRST_CCK], - &txpwr->cck[0], BRCMS_NUM_RATES_CCK); - - memcpy(&pi->tx_user_target[TXP_FIRST_OFDM], - &txpwr->ofdm[0], BRCMS_NUM_RATES_OFDM); - memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_20_CDD], - &txpwr->ofdm_cdd[0], BRCMS_NUM_RATES_OFDM); - - memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_40_SISO], - &txpwr->ofdm_40_siso[0], BRCMS_NUM_RATES_OFDM); - memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_40_CDD], - &txpwr->ofdm_40_cdd[0], BRCMS_NUM_RATES_OFDM); - - memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_SISO], - &txpwr->mcs_20_siso[0], BRCMS_NUM_RATES_MCS_1_STREAM); - memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_CDD], - &txpwr->mcs_20_cdd[0], BRCMS_NUM_RATES_MCS_1_STREAM); - memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_STBC], - &txpwr->mcs_20_stbc[0], BRCMS_NUM_RATES_MCS_1_STREAM); - memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_SDM], - &txpwr->mcs_20_mimo[0], BRCMS_NUM_RATES_MCS_2_STREAM); - - memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_SISO], - &txpwr->mcs_40_siso[0], BRCMS_NUM_RATES_MCS_1_STREAM); - memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_CDD], - &txpwr->mcs_40_cdd[0], BRCMS_NUM_RATES_MCS_1_STREAM); - memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_STBC], - &txpwr->mcs_40_stbc[0], BRCMS_NUM_RATES_MCS_1_STREAM); - memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_SDM], - &txpwr->mcs_40_mimo[0], BRCMS_NUM_RATES_MCS_2_STREAM); - - if (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC) - mac_enabled = true; - - if (mac_enabled) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - - wlc_phy_txpower_recalc_target(pi); - wlc_phy_cal_txpower_recalc_sw(pi); - - if (mac_enabled) - wlapi_enable_mac(pi->sh->physhim); -} - -int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, bool override) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - int i; - - if (qdbm > 127) - return -EINVAL; - - for (i = 0; i < TXP_NUM_RATES; i++) - pi->tx_user_target[i] = (u8) qdbm; - - pi->txpwroverride = false; - - if (pi->sh->up) { - if (!SCAN_INPROG_PHY(pi)) { - bool suspend; - - suspend = (0 == (bcma_read32(pi->d11core, - D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - - if (!suspend) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - - wlc_phy_txpower_recalc_target(pi); - wlc_phy_cal_txpower_recalc_sw(pi); - - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); - } - } - return 0; -} - -void -wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint channel, u8 *min_pwr, - u8 *max_pwr, int txp_rate_idx) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - uint i; - - *min_pwr = pi->min_txpower * BRCMS_TXPWR_DB_FACTOR; - - if (ISNPHY(pi)) { - if (txp_rate_idx < 0) - txp_rate_idx = TXP_FIRST_CCK; - wlc_phy_txpower_sromlimit_get_nphy(pi, channel, max_pwr, - (u8) txp_rate_idx); - - } else if ((channel <= CH_MAX_2G_CHANNEL)) { - if (txp_rate_idx < 0) - txp_rate_idx = TXP_FIRST_CCK; - *max_pwr = pi->tx_srom_max_rate_2g[txp_rate_idx]; - } else { - - *max_pwr = BRCMS_TXPWR_MAX; - - if (txp_rate_idx < 0) - txp_rate_idx = TXP_FIRST_OFDM; - - for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) { - if (channel == chan_info_all[i].chan) - break; - } - - if (pi->hwtxpwr) { - *max_pwr = pi->hwtxpwr[i]; - } else { - - if ((i >= FIRST_MID_5G_CHAN) && (i <= LAST_MID_5G_CHAN)) - *max_pwr = - pi->tx_srom_max_rate_5g_mid[txp_rate_idx]; - if ((i >= FIRST_HIGH_5G_CHAN) - && (i <= LAST_HIGH_5G_CHAN)) - *max_pwr = - pi->tx_srom_max_rate_5g_hi[txp_rate_idx]; - if ((i >= FIRST_LOW_5G_CHAN) && (i <= LAST_LOW_5G_CHAN)) - *max_pwr = - pi->tx_srom_max_rate_5g_low[txp_rate_idx]; - } - } -} - -void -wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, uint chan, - u8 *max_txpwr, u8 *min_txpwr) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - u8 tx_pwr_max = 0; - u8 tx_pwr_min = 255; - u8 max_num_rate; - u8 maxtxpwr, mintxpwr, rate, pactrl; - - pactrl = 0; - - max_num_rate = ISNPHY(pi) ? TXP_NUM_RATES : - ISLCNPHY(pi) ? (TXP_LAST_SISO_MCS_20 + - 1) : (TXP_LAST_OFDM + 1); - - for (rate = 0; rate < max_num_rate; rate++) { - - wlc_phy_txpower_sromlimit(ppi, chan, &mintxpwr, &maxtxpwr, - rate); - - maxtxpwr = (maxtxpwr > pactrl) ? (maxtxpwr - pactrl) : 0; - - maxtxpwr = (maxtxpwr > 6) ? (maxtxpwr - 6) : 0; - - tx_pwr_max = max(tx_pwr_max, maxtxpwr); - tx_pwr_min = min(tx_pwr_min, maxtxpwr); - } - *max_txpwr = tx_pwr_max; - *min_txpwr = tx_pwr_min; -} - -void -wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, uint bandunit, - s32 *max_pwr, s32 *min_pwr, u32 *step_pwr) -{ - return; -} - -u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - return pi->tx_power_min; -} - -u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - return pi->tx_power_max; -} - -static s8 wlc_phy_env_measure_vbat(struct brcms_phy *pi) -{ - if (ISLCNPHY(pi)) - return wlc_lcnphy_vbatsense(pi, 0); - else - return 0; -} - -static s8 wlc_phy_env_measure_temperature(struct brcms_phy *pi) -{ - if (ISLCNPHY(pi)) - return wlc_lcnphy_tempsense_degree(pi, 0); - else - return 0; -} - -static void wlc_phy_upd_env_txpwr_rate_limits(struct brcms_phy *pi, u32 band) -{ - u8 i; - s8 temp, vbat; - - for (i = 0; i < TXP_NUM_RATES; i++) - pi->txpwr_env_limit[i] = BRCMS_TXPWR_MAX; - - vbat = wlc_phy_env_measure_vbat(pi); - temp = wlc_phy_env_measure_temperature(pi); - -} - -static s8 -wlc_user_txpwr_antport_to_rfport(struct brcms_phy *pi, uint chan, u32 band, - u8 rate) -{ - return 0; -} - -void wlc_phy_txpower_recalc_target(struct brcms_phy *pi) -{ - u8 maxtxpwr, mintxpwr, rate, pactrl; - uint target_chan; - u8 tx_pwr_target[TXP_NUM_RATES]; - u8 tx_pwr_max = 0; - u8 tx_pwr_min = 255; - u8 tx_pwr_max_rate_ind = 0; - u8 max_num_rate; - u8 start_rate = 0; - u16 chspec; - u32 band = CHSPEC2BAND(pi->radio_chanspec); - void (*txpwr_recalc_fn)(struct brcms_phy *) = NULL; - - chspec = pi->radio_chanspec; - if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) - target_chan = CHSPEC_CHANNEL(chspec); - else if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) - target_chan = upper_20_sb(CHSPEC_CHANNEL(chspec)); - else - target_chan = lower_20_sb(CHSPEC_CHANNEL(chspec)); - - pactrl = 0; - if (ISLCNPHY(pi)) { - u32 offset_mcs, i; - - if (CHSPEC_IS40(pi->radio_chanspec)) { - offset_mcs = pi->mcs40_po; - for (i = TXP_FIRST_SISO_MCS_20; - i <= TXP_LAST_SISO_MCS_20; i++) { - pi->tx_srom_max_rate_2g[i - 8] = - pi->tx_srom_max_2g - - ((offset_mcs & 0xf) * 2); - offset_mcs >>= 4; - } - } else { - offset_mcs = pi->mcs20_po; - for (i = TXP_FIRST_SISO_MCS_20; - i <= TXP_LAST_SISO_MCS_20; i++) { - pi->tx_srom_max_rate_2g[i - 8] = - pi->tx_srom_max_2g - - ((offset_mcs & 0xf) * 2); - offset_mcs >>= 4; - } - } - } - - max_num_rate = ((ISNPHY(pi)) ? (TXP_NUM_RATES) : - ((ISLCNPHY(pi)) ? - (TXP_LAST_SISO_MCS_20 + 1) : (TXP_LAST_OFDM + 1))); - - wlc_phy_upd_env_txpwr_rate_limits(pi, band); - - for (rate = start_rate; rate < max_num_rate; rate++) { - - tx_pwr_target[rate] = pi->tx_user_target[rate]; - - if (pi->user_txpwr_at_rfport) - tx_pwr_target[rate] += - wlc_user_txpwr_antport_to_rfport(pi, - target_chan, - band, - rate); - - wlc_phy_txpower_sromlimit((struct brcms_phy_pub *) pi, - target_chan, - &mintxpwr, &maxtxpwr, rate); - - maxtxpwr = min(maxtxpwr, pi->txpwr_limit[rate]); - - maxtxpwr = (maxtxpwr > pactrl) ? (maxtxpwr - pactrl) : 0; - - maxtxpwr = (maxtxpwr > 6) ? (maxtxpwr - 6) : 0; - - maxtxpwr = min(maxtxpwr, tx_pwr_target[rate]); - - if (pi->txpwr_percent <= 100) - maxtxpwr = (maxtxpwr * pi->txpwr_percent) / 100; - - tx_pwr_target[rate] = max(maxtxpwr, mintxpwr); - - tx_pwr_target[rate] = - min(tx_pwr_target[rate], pi->txpwr_env_limit[rate]); - - if (tx_pwr_target[rate] > tx_pwr_max) - tx_pwr_max_rate_ind = rate; - - tx_pwr_max = max(tx_pwr_max, tx_pwr_target[rate]); - tx_pwr_min = min(tx_pwr_min, tx_pwr_target[rate]); - } - - memset(pi->tx_power_offset, 0, sizeof(pi->tx_power_offset)); - pi->tx_power_max = tx_pwr_max; - pi->tx_power_min = tx_pwr_min; - pi->tx_power_max_rate_ind = tx_pwr_max_rate_ind; - for (rate = 0; rate < max_num_rate; rate++) { - - pi->tx_power_target[rate] = tx_pwr_target[rate]; - - if (!pi->hwpwrctrl || ISNPHY(pi)) - pi->tx_power_offset[rate] = - pi->tx_power_max - pi->tx_power_target[rate]; - else - pi->tx_power_offset[rate] = - pi->tx_power_target[rate] - pi->tx_power_min; - } - - txpwr_recalc_fn = pi->pi_fptr.txpwrrecalc; - if (txpwr_recalc_fn) - (*txpwr_recalc_fn)(pi); -} - -static void -wlc_phy_txpower_reg_limit_calc(struct brcms_phy *pi, struct txpwr_limits *txpwr, - u16 chanspec) -{ - u8 tmp_txpwr_limit[2 * BRCMS_NUM_RATES_OFDM]; - u8 *txpwr_ptr1 = NULL, *txpwr_ptr2 = NULL; - int rate_start_index = 0, rate1, rate2, k; - - for (rate1 = WL_TX_POWER_CCK_FIRST, rate2 = 0; - rate2 < WL_TX_POWER_CCK_NUM; rate1++, rate2++) - pi->txpwr_limit[rate1] = txpwr->cck[rate2]; - - for (rate1 = WL_TX_POWER_OFDM_FIRST, rate2 = 0; - rate2 < WL_TX_POWER_OFDM_NUM; rate1++, rate2++) - pi->txpwr_limit[rate1] = txpwr->ofdm[rate2]; - - if (ISNPHY(pi)) { - - for (k = 0; k < 4; k++) { - switch (k) { - case 0: - - txpwr_ptr1 = txpwr->mcs_20_siso; - txpwr_ptr2 = txpwr->ofdm; - rate_start_index = WL_TX_POWER_OFDM_FIRST; - break; - case 1: - - txpwr_ptr1 = txpwr->mcs_20_cdd; - txpwr_ptr2 = txpwr->ofdm_cdd; - rate_start_index = WL_TX_POWER_OFDM20_CDD_FIRST; - break; - case 2: - - txpwr_ptr1 = txpwr->mcs_40_siso; - txpwr_ptr2 = txpwr->ofdm_40_siso; - rate_start_index = - WL_TX_POWER_OFDM40_SISO_FIRST; - break; - case 3: - - txpwr_ptr1 = txpwr->mcs_40_cdd; - txpwr_ptr2 = txpwr->ofdm_40_cdd; - rate_start_index = WL_TX_POWER_OFDM40_CDD_FIRST; - break; - } - - for (rate2 = 0; rate2 < BRCMS_NUM_RATES_OFDM; - rate2++) { - tmp_txpwr_limit[rate2] = 0; - tmp_txpwr_limit[BRCMS_NUM_RATES_OFDM + rate2] = - txpwr_ptr1[rate2]; - } - wlc_phy_mcs_to_ofdm_powers_nphy( - tmp_txpwr_limit, 0, - BRCMS_NUM_RATES_OFDM - - 1, BRCMS_NUM_RATES_OFDM); - for (rate1 = rate_start_index, rate2 = 0; - rate2 < BRCMS_NUM_RATES_OFDM; rate1++, rate2++) - pi->txpwr_limit[rate1] = - min(txpwr_ptr2[rate2], - tmp_txpwr_limit[rate2]); - } - - for (k = 0; k < 4; k++) { - switch (k) { - case 0: - - txpwr_ptr1 = txpwr->ofdm; - txpwr_ptr2 = txpwr->mcs_20_siso; - rate_start_index = WL_TX_POWER_MCS20_SISO_FIRST; - break; - case 1: - - txpwr_ptr1 = txpwr->ofdm_cdd; - txpwr_ptr2 = txpwr->mcs_20_cdd; - rate_start_index = WL_TX_POWER_MCS20_CDD_FIRST; - break; - case 2: - - txpwr_ptr1 = txpwr->ofdm_40_siso; - txpwr_ptr2 = txpwr->mcs_40_siso; - rate_start_index = WL_TX_POWER_MCS40_SISO_FIRST; - break; - case 3: - - txpwr_ptr1 = txpwr->ofdm_40_cdd; - txpwr_ptr2 = txpwr->mcs_40_cdd; - rate_start_index = WL_TX_POWER_MCS40_CDD_FIRST; - break; - } - for (rate2 = 0; rate2 < BRCMS_NUM_RATES_OFDM; - rate2++) { - tmp_txpwr_limit[rate2] = 0; - tmp_txpwr_limit[BRCMS_NUM_RATES_OFDM + rate2] = - txpwr_ptr1[rate2]; - } - wlc_phy_ofdm_to_mcs_powers_nphy( - tmp_txpwr_limit, 0, - BRCMS_NUM_RATES_OFDM - - 1, BRCMS_NUM_RATES_OFDM); - for (rate1 = rate_start_index, rate2 = 0; - rate2 < BRCMS_NUM_RATES_MCS_1_STREAM; - rate1++, rate2++) - pi->txpwr_limit[rate1] = - min(txpwr_ptr2[rate2], - tmp_txpwr_limit[rate2]); - } - - for (k = 0; k < 2; k++) { - switch (k) { - case 0: - - rate_start_index = WL_TX_POWER_MCS20_STBC_FIRST; - txpwr_ptr1 = txpwr->mcs_20_stbc; - break; - case 1: - - rate_start_index = WL_TX_POWER_MCS40_STBC_FIRST; - txpwr_ptr1 = txpwr->mcs_40_stbc; - break; - } - for (rate1 = rate_start_index, rate2 = 0; - rate2 < BRCMS_NUM_RATES_MCS_1_STREAM; - rate1++, rate2++) - pi->txpwr_limit[rate1] = txpwr_ptr1[rate2]; - } - - for (k = 0; k < 2; k++) { - switch (k) { - case 0: - - rate_start_index = WL_TX_POWER_MCS20_SDM_FIRST; - txpwr_ptr1 = txpwr->mcs_20_mimo; - break; - case 1: - - rate_start_index = WL_TX_POWER_MCS40_SDM_FIRST; - txpwr_ptr1 = txpwr->mcs_40_mimo; - break; - } - for (rate1 = rate_start_index, rate2 = 0; - rate2 < BRCMS_NUM_RATES_MCS_2_STREAM; - rate1++, rate2++) - pi->txpwr_limit[rate1] = txpwr_ptr1[rate2]; - } - - pi->txpwr_limit[WL_TX_POWER_MCS_32] = txpwr->mcs32; - - pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST] = - min(pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST], - pi->txpwr_limit[WL_TX_POWER_MCS_32]); - pi->txpwr_limit[WL_TX_POWER_MCS_32] = - pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST]; - } -} - -void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, u8 txpwr_percent) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - pi->txpwr_percent = txpwr_percent; -} - -void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - pi->sh->machwcap = machwcap; -} - -void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - u16 rxc; - rxc = 0; - - if (start_end == ON) { - if (!ISNPHY(pi)) - return; - - if (NREV_IS(pi->pubpi.phy_rev, 3) - || NREV_IS(pi->pubpi.phy_rev, 4)) { - bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), - 0xa0); - bcma_set16(pi->d11core, D11REGOFFS(phyregdata), - 0x1 << 15); - } - } else { - if (NREV_IS(pi->pubpi.phy_rev, 3) - || NREV_IS(pi->pubpi.phy_rev, 4)) { - bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), - 0xa0); - bcma_write16(pi->d11core, D11REGOFFS(phyregdata), rxc); - } - - wlc_phy_por_inform(ppi); - } -} - -void -wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, struct txpwr_limits *txpwr, - u16 chanspec) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - wlc_phy_txpower_reg_limit_calc(pi, txpwr, chanspec); - - if (ISLCNPHY(pi)) { - int i, j; - for (i = TXP_FIRST_OFDM_20_CDD, j = 0; - j < BRCMS_NUM_RATES_MCS_1_STREAM; i++, j++) { - if (txpwr->mcs_20_siso[j]) - pi->txpwr_limit[i] = txpwr->mcs_20_siso[j]; - else - pi->txpwr_limit[i] = txpwr->ofdm[j]; - } - } - - wlapi_suspend_mac_and_wait(pi->sh->physhim); - - wlc_phy_txpower_recalc_target(pi); - wlc_phy_cal_txpower_recalc_sw(pi); - wlapi_enable_mac(pi->sh->physhim); -} - -void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - pi->ofdm_rateset_war = war; -} - -void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, bool bf_preempt) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - pi->bf_preempt_4306 = bf_preempt; -} - -void wlc_phy_txpower_update_shm(struct brcms_phy *pi) -{ - int j; - if (ISNPHY(pi)) - return; - - if (!pi->sh->clk) - return; - - if (pi->hwpwrctrl) { - u16 offset; - - wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_MAX, 63); - wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_N, - 1 << NUM_TSSI_FRAMES); - - wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_TARGET, - pi->tx_power_min << NUM_TSSI_FRAMES); - - wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_CUR, - pi->hwpwr_txcur); - - for (j = TXP_FIRST_OFDM; j <= TXP_LAST_OFDM; j++) { - const u8 ucode_ofdm_rates[] = { - 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c - }; - offset = wlapi_bmac_rate_shm_offset( - pi->sh->physhim, - ucode_ofdm_rates[j - TXP_FIRST_OFDM]); - wlapi_bmac_write_shm(pi->sh->physhim, offset + 6, - pi->tx_power_offset[j]); - wlapi_bmac_write_shm(pi->sh->physhim, offset + 14, - -(pi->tx_power_offset[j] / 2)); - } - - wlapi_bmac_mhf(pi->sh->physhim, MHF2, MHF2_HWPWRCTL, - MHF2_HWPWRCTL, BRCM_BAND_ALL); - } else { - int i; - - for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++) - pi->tx_power_offset[i] = - (u8) roundup(pi->tx_power_offset[i], 8); - wlapi_bmac_write_shm(pi->sh->physhim, M_OFDM_OFFSET, - (u16) - ((pi->tx_power_offset[TXP_FIRST_OFDM] - + 7) >> 3)); - } -} - -bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - if (ISNPHY(pi)) - return pi->nphy_txpwrctrl; - else - return pi->hwpwrctrl; -} - -void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, bool hwpwrctrl) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - bool suspend; - - if (!pi->hwpwrctrl_capable) - return; - - pi->hwpwrctrl = hwpwrctrl; - pi->nphy_txpwrctrl = hwpwrctrl; - pi->txpwrctrl = hwpwrctrl; - - if (ISNPHY(pi)) { - suspend = (0 == (bcma_read32(pi->d11core, - D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!suspend) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - - wlc_phy_txpwrctrl_enable_nphy(pi, pi->nphy_txpwrctrl); - if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) - wlc_phy_txpwr_fixpower_nphy(pi); - else - mod_phy_reg(pi, 0x1e7, (0x7f << 0), - pi->saved_txpwr_idx); - - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); - } -} - -void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi) -{ - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - pi->ipa2g_on = (pi->srom_fem2g.extpagain == 2); - pi->ipa5g_on = (pi->srom_fem5g.extpagain == 2); - } else { - pi->ipa2g_on = false; - pi->ipa5g_on = false; - } -} - -static u32 wlc_phy_txpower_est_power_nphy(struct brcms_phy *pi) -{ - s16 tx0_status, tx1_status; - u16 estPower1, estPower2; - u8 pwr0, pwr1, adj_pwr0, adj_pwr1; - u32 est_pwr; - - estPower1 = read_phy_reg(pi, 0x118); - estPower2 = read_phy_reg(pi, 0x119); - - if ((estPower1 & (0x1 << 8)) == (0x1 << 8)) - pwr0 = (u8) (estPower1 & (0xff << 0)) >> 0; - else - pwr0 = 0x80; - - if ((estPower2 & (0x1 << 8)) == (0x1 << 8)) - pwr1 = (u8) (estPower2 & (0xff << 0)) >> 0; - else - pwr1 = 0x80; - - tx0_status = read_phy_reg(pi, 0x1ed); - tx1_status = read_phy_reg(pi, 0x1ee); - - if ((tx0_status & (0x1 << 15)) == (0x1 << 15)) - adj_pwr0 = (u8) (tx0_status & (0xff << 0)) >> 0; - else - adj_pwr0 = 0x80; - if ((tx1_status & (0x1 << 15)) == (0x1 << 15)) - adj_pwr1 = (u8) (tx1_status & (0xff << 0)) >> 0; - else - adj_pwr1 = 0x80; - - est_pwr = (u32) ((pwr0 << 24) | (pwr1 << 16) | (adj_pwr0 << 8) | - adj_pwr1); - - return est_pwr; -} - -void -wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi, struct tx_power *power, - uint channel) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - uint rate, num_rates; - u8 min_pwr, max_pwr; - -#if WL_TX_POWER_RATES != TXP_NUM_RATES -#error "struct tx_power out of sync with this fn" -#endif - - if (ISNPHY(pi)) { - power->rf_cores = 2; - power->flags |= (WL_TX_POWER_F_MIMO); - if (pi->nphy_txpwrctrl == PHY_TPC_HW_ON) - power->flags |= - (WL_TX_POWER_F_ENABLED | WL_TX_POWER_F_HW); - } else if (ISLCNPHY(pi)) { - power->rf_cores = 1; - power->flags |= (WL_TX_POWER_F_SISO); - if (pi->radiopwr_override == RADIOPWR_OVERRIDE_DEF) - power->flags |= WL_TX_POWER_F_ENABLED; - if (pi->hwpwrctrl) - power->flags |= WL_TX_POWER_F_HW; - } - - num_rates = ((ISNPHY(pi)) ? (TXP_NUM_RATES) : - ((ISLCNPHY(pi)) ? - (TXP_LAST_OFDM_20_CDD + 1) : (TXP_LAST_OFDM + 1))); - - for (rate = 0; rate < num_rates; rate++) { - power->user_limit[rate] = pi->tx_user_target[rate]; - wlc_phy_txpower_sromlimit(ppi, channel, &min_pwr, &max_pwr, - rate); - power->board_limit[rate] = (u8) max_pwr; - power->target[rate] = pi->tx_power_target[rate]; - } - - if (ISNPHY(pi)) { - u32 est_pout; - - wlapi_suspend_mac_and_wait(pi->sh->physhim); - wlc_phyreg_enter((struct brcms_phy_pub *) pi); - est_pout = wlc_phy_txpower_est_power_nphy(pi); - wlc_phyreg_exit((struct brcms_phy_pub *) pi); - wlapi_enable_mac(pi->sh->physhim); - - power->est_Pout[0] = (est_pout >> 8) & 0xff; - power->est_Pout[1] = est_pout & 0xff; - - power->est_Pout_act[0] = est_pout >> 24; - power->est_Pout_act[1] = (est_pout >> 16) & 0xff; - - if (power->est_Pout[0] == 0x80) - power->est_Pout[0] = 0; - if (power->est_Pout[1] == 0x80) - power->est_Pout[1] = 0; - - if (power->est_Pout_act[0] == 0x80) - power->est_Pout_act[0] = 0; - if (power->est_Pout_act[1] == 0x80) - power->est_Pout_act[1] = 0; - - power->est_Pout_cck = 0; - - power->tx_power_max[0] = pi->tx_power_max; - power->tx_power_max[1] = pi->tx_power_max; - - power->tx_power_max_rate_ind[0] = pi->tx_power_max_rate_ind; - power->tx_power_max_rate_ind[1] = pi->tx_power_max_rate_ind; - } else if (pi->hwpwrctrl && pi->sh->up) { - - wlc_phyreg_enter(ppi); - if (ISLCNPHY(pi)) { - - power->tx_power_max[0] = pi->tx_power_max; - power->tx_power_max[1] = pi->tx_power_max; - - power->tx_power_max_rate_ind[0] = - pi->tx_power_max_rate_ind; - power->tx_power_max_rate_ind[1] = - pi->tx_power_max_rate_ind; - - if (wlc_phy_tpc_isenabled_lcnphy(pi)) - power->flags |= - (WL_TX_POWER_F_HW | - WL_TX_POWER_F_ENABLED); - else - power->flags &= - ~(WL_TX_POWER_F_HW | - WL_TX_POWER_F_ENABLED); - - wlc_lcnphy_get_tssi(pi, (s8 *) &power->est_Pout[0], - (s8 *) &power->est_Pout_cck); - } - wlc_phyreg_exit(ppi); - } -} - -void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - pi->antsel_type = antsel_type; -} - -bool wlc_phy_test_ison(struct brcms_phy_pub *ppi) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - return pi->phytest_on; -} - -void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - bool suspend; - - pi->sh->rx_antdiv = val; - - if (!(ISNPHY(pi) && D11REV_IS(pi->sh->corerev, 16))) { - if (val > ANT_RX_DIV_FORCE_1) - wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_ANTDIV, - MHF1_ANTDIV, BRCM_BAND_ALL); - else - wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_ANTDIV, 0, - BRCM_BAND_ALL); - } - - if (ISNPHY(pi)) - return; - - if (!pi->sh->clk) - return; - - suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!suspend) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - - if (ISLCNPHY(pi)) { - if (val > ANT_RX_DIV_FORCE_1) { - mod_phy_reg(pi, 0x410, (0x1 << 1), 0x01 << 1); - mod_phy_reg(pi, 0x410, - (0x1 << 0), - ((ANT_RX_DIV_START_1 == val) ? 1 : 0) << 0); - } else { - mod_phy_reg(pi, 0x410, (0x1 << 1), 0x00 << 1); - mod_phy_reg(pi, 0x410, (0x1 << 0), (u16) val << 0); - } - } - - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); - - return; -} - -static bool -wlc_phy_noise_calc_phy(struct brcms_phy *pi, u32 *cmplx_pwr, s8 *pwr_ant) -{ - s8 cmplx_pwr_dbm[PHY_CORE_MAX]; - u8 i; - - memset((u8 *) cmplx_pwr_dbm, 0, sizeof(cmplx_pwr_dbm)); - wlc_phy_compute_dB(cmplx_pwr, cmplx_pwr_dbm, pi->pubpi.phy_corenum); - - for (i = 0; i < pi->pubpi.phy_corenum; i++) { - if (NREV_GE(pi->pubpi.phy_rev, 3)) - cmplx_pwr_dbm[i] += (s8) PHY_NOISE_OFFSETFACT_4322; - else - - cmplx_pwr_dbm[i] += (s8) (16 - (15) * 3 - 70); - } - - for (i = 0; i < pi->pubpi.phy_corenum; i++) { - pi->nphy_noise_win[i][pi->nphy_noise_index] = cmplx_pwr_dbm[i]; - pwr_ant[i] = cmplx_pwr_dbm[i]; - } - pi->nphy_noise_index = - MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ); - return true; -} - -static void wlc_phy_noise_cb(struct brcms_phy *pi, u8 channel, s8 noise_dbm) -{ - if (!pi->phynoise_state) - return; - - if (pi->phynoise_state & PHY_NOISE_STATE_MON) { - if (pi->phynoise_chan_watchdog == channel) { - pi->sh->phy_noise_window[pi->sh->phy_noise_index] = - noise_dbm; - pi->sh->phy_noise_index = - MODINC(pi->sh->phy_noise_index, MA_WINDOW_SZ); - } - pi->phynoise_state &= ~PHY_NOISE_STATE_MON; - } - - if (pi->phynoise_state & PHY_NOISE_STATE_EXTERNAL) - pi->phynoise_state &= ~PHY_NOISE_STATE_EXTERNAL; - -} - -static s8 wlc_phy_noise_read_shmem(struct brcms_phy *pi) -{ - u32 cmplx_pwr[PHY_CORE_MAX]; - s8 noise_dbm_ant[PHY_CORE_MAX]; - u16 lo, hi; - u32 cmplx_pwr_tot = 0; - s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; - u8 idx, core; - - memset((u8 *) cmplx_pwr, 0, sizeof(cmplx_pwr)); - memset((u8 *) noise_dbm_ant, 0, sizeof(noise_dbm_ant)); - - for (idx = 0, core = 0; core < pi->pubpi.phy_corenum; idx += 2, - core++) { - lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP(idx)); - hi = wlapi_bmac_read_shm(pi->sh->physhim, - M_PWRIND_MAP(idx + 1)); - cmplx_pwr[core] = (hi << 16) + lo; - cmplx_pwr_tot += cmplx_pwr[core]; - if (cmplx_pwr[core] == 0) - noise_dbm_ant[core] = PHY_NOISE_FIXED_VAL_NPHY; - else - cmplx_pwr[core] >>= PHY_NOISE_SAMPLE_LOG_NUM_UCODE; - } - - if (cmplx_pwr_tot != 0) - wlc_phy_noise_calc_phy(pi, cmplx_pwr, noise_dbm_ant); - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - pi->nphy_noise_win[core][pi->nphy_noise_index] = - noise_dbm_ant[core]; - - if (noise_dbm_ant[core] > noise_dbm) - noise_dbm = noise_dbm_ant[core]; - } - pi->nphy_noise_index = - MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ); - - return noise_dbm; - -} - -void wlc_phy_noise_sample_intr(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - u16 jssi_aux; - u8 channel = 0; - s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; - - if (ISLCNPHY(pi)) { - u32 cmplx_pwr, cmplx_pwr0, cmplx_pwr1; - u16 lo, hi; - s32 pwr_offset_dB, gain_dB; - u16 status_0, status_1; - - jssi_aux = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_AUX); - channel = jssi_aux & D11_CURCHANNEL_MAX; - - lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP0); - hi = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP1); - cmplx_pwr0 = (hi << 16) + lo; - - lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP2); - hi = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP3); - cmplx_pwr1 = (hi << 16) + lo; - cmplx_pwr = (cmplx_pwr0 + cmplx_pwr1) >> 6; - - status_0 = 0x44; - status_1 = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_0); - if ((cmplx_pwr > 0 && cmplx_pwr < 500) - && ((status_1 & 0xc000) == 0x4000)) { - - wlc_phy_compute_dB(&cmplx_pwr, &noise_dbm, - pi->pubpi.phy_corenum); - pwr_offset_dB = (read_phy_reg(pi, 0x434) & 0xFF); - if (pwr_offset_dB > 127) - pwr_offset_dB -= 256; - - noise_dbm += (s8) (pwr_offset_dB - 30); - - gain_dB = (status_0 & 0x1ff); - noise_dbm -= (s8) (gain_dB); - } else { - noise_dbm = PHY_NOISE_FIXED_VAL_LCNPHY; - } - } else if (ISNPHY(pi)) { - - jssi_aux = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_AUX); - channel = jssi_aux & D11_CURCHANNEL_MAX; - - noise_dbm = wlc_phy_noise_read_shmem(pi); - } - - wlc_phy_noise_cb(pi, channel, noise_dbm); - -} - -static void -wlc_phy_noise_sample_request(struct brcms_phy_pub *pih, u8 reason, u8 ch) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; - bool sampling_in_progress = (pi->phynoise_state != 0); - bool wait_for_intr = true; - - switch (reason) { - case PHY_NOISE_SAMPLE_MON: - pi->phynoise_chan_watchdog = ch; - pi->phynoise_state |= PHY_NOISE_STATE_MON; - break; - - case PHY_NOISE_SAMPLE_EXTERNAL: - pi->phynoise_state |= PHY_NOISE_STATE_EXTERNAL; - break; - - default: - break; - } - - if (sampling_in_progress) - return; - - pi->phynoise_now = pi->sh->now; - - if (pi->phy_fixed_noise) { - if (ISNPHY(pi)) { - pi->nphy_noise_win[WL_ANT_IDX_1][pi->nphy_noise_index] = - PHY_NOISE_FIXED_VAL_NPHY; - pi->nphy_noise_win[WL_ANT_IDX_2][pi->nphy_noise_index] = - PHY_NOISE_FIXED_VAL_NPHY; - pi->nphy_noise_index = MODINC_POW2(pi->nphy_noise_index, - PHY_NOISE_WINDOW_SZ); - noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; - } else { - noise_dbm = PHY_NOISE_FIXED_VAL; - } - - wait_for_intr = false; - goto done; - } - - if (ISLCNPHY(pi)) { - if (!pi->phynoise_polling - || (reason == PHY_NOISE_SAMPLE_EXTERNAL)) { - wlapi_bmac_write_shm(pi->sh->physhim, M_JSSI_0, 0); - wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP0, 0); - wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP1, 0); - wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP2, 0); - wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP3, 0); - - bcma_set32(pi->d11core, D11REGOFFS(maccommand), - MCMD_BG_NOISE); - } else { - wlapi_suspend_mac_and_wait(pi->sh->physhim); - wlc_lcnphy_deaf_mode(pi, (bool) 0); - noise_dbm = (s8) wlc_lcnphy_rx_signal_power(pi, 20); - wlc_lcnphy_deaf_mode(pi, (bool) 1); - wlapi_enable_mac(pi->sh->physhim); - wait_for_intr = false; - } - } else if (ISNPHY(pi)) { - if (!pi->phynoise_polling - || (reason == PHY_NOISE_SAMPLE_EXTERNAL)) { - - wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP0, 0); - wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP1, 0); - wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP2, 0); - wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP3, 0); - - bcma_set32(pi->d11core, D11REGOFFS(maccommand), - MCMD_BG_NOISE); - } else { - struct phy_iq_est est[PHY_CORE_MAX]; - u32 cmplx_pwr[PHY_CORE_MAX]; - s8 noise_dbm_ant[PHY_CORE_MAX]; - u16 log_num_samps, num_samps, classif_state = 0; - u8 wait_time = 32; - u8 wait_crs = 0; - u8 i; - - memset((u8 *) est, 0, sizeof(est)); - memset((u8 *) cmplx_pwr, 0, sizeof(cmplx_pwr)); - memset((u8 *) noise_dbm_ant, 0, sizeof(noise_dbm_ant)); - - log_num_samps = PHY_NOISE_SAMPLE_LOG_NUM_NPHY; - num_samps = 1 << log_num_samps; - - wlapi_suspend_mac_and_wait(pi->sh->physhim); - classif_state = wlc_phy_classifier_nphy(pi, 0, 0); - wlc_phy_classifier_nphy(pi, 3, 0); - wlc_phy_rx_iq_est_nphy(pi, est, num_samps, wait_time, - wait_crs); - wlc_phy_classifier_nphy(pi, (0x7 << 0), classif_state); - wlapi_enable_mac(pi->sh->physhim); - - for (i = 0; i < pi->pubpi.phy_corenum; i++) - cmplx_pwr[i] = (est[i].i_pwr + est[i].q_pwr) >> - log_num_samps; - - wlc_phy_noise_calc_phy(pi, cmplx_pwr, noise_dbm_ant); - - for (i = 0; i < pi->pubpi.phy_corenum; i++) { - pi->nphy_noise_win[i][pi->nphy_noise_index] = - noise_dbm_ant[i]; - - if (noise_dbm_ant[i] > noise_dbm) - noise_dbm = noise_dbm_ant[i]; - } - pi->nphy_noise_index = MODINC_POW2(pi->nphy_noise_index, - PHY_NOISE_WINDOW_SZ); - - wait_for_intr = false; - } - } - -done: - - if (!wait_for_intr) - wlc_phy_noise_cb(pi, ch, noise_dbm); - -} - -void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *pih) -{ - u8 channel; - - channel = CHSPEC_CHANNEL(wlc_phy_chanspec_get(pih)); - - wlc_phy_noise_sample_request(pih, PHY_NOISE_SAMPLE_EXTERNAL, channel); -} - -static const s8 lcnphy_gain_index_offset_for_pkt_rssi[] = { - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 9, - 10, - 8, - 8, - 7, - 7, - 1, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 1, - 1, - 0, - 0, - 0, - 0 -}; - -void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_cmplx_pwr_dB, u8 core) -{ - u8 msb, secondmsb, i; - u32 tmp; - - for (i = 0; i < core; i++) { - secondmsb = 0; - tmp = cmplx_pwr[i]; - msb = fls(tmp); - if (msb) - secondmsb = (u8) ((tmp >> (--msb - 1)) & 1); - p_cmplx_pwr_dB[i] = (s8) (3 * msb + 2 * secondmsb); - } -} - -int wlc_phy_rssi_compute(struct brcms_phy_pub *pih, - struct d11rxhdr *rxh) -{ - int rssi = rxh->PhyRxStatus_1 & PRXS1_JSSI_MASK; - uint radioid = pih->radioid; - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - if ((pi->sh->corerev >= 11) - && !(rxh->RxStatus2 & RXS_PHYRXST_VALID)) { - rssi = BRCMS_RSSI_INVALID; - goto end; - } - - if (ISLCNPHY(pi)) { - u8 gidx = (rxh->PhyRxStatus_2 & 0xFC00) >> 10; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - if (rssi > 127) - rssi -= 256; - - rssi = rssi + lcnphy_gain_index_offset_for_pkt_rssi[gidx]; - if ((rssi > -46) && (gidx > 18)) - rssi = rssi + 7; - - rssi = rssi + pi_lcn->lcnphy_pkteng_rssi_slope; - - rssi = rssi + 2; - - } - - if (ISLCNPHY(pi)) { - if (rssi > 127) - rssi -= 256; - } else if (radioid == BCM2055_ID || radioid == BCM2056_ID - || radioid == BCM2057_ID) { - rssi = wlc_phy_rssi_compute_nphy(pi, rxh); - } - -end: - return rssi; -} - -void wlc_phy_freqtrack_start(struct brcms_phy_pub *pih) -{ - return; -} - -void wlc_phy_freqtrack_end(struct brcms_phy_pub *pih) -{ - return; -} - -void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag) -{ - struct brcms_phy *pi; - pi = (struct brcms_phy *) ppi; - - if (ISLCNPHY(pi)) - wlc_lcnphy_deaf_mode(pi, true); - else if (ISNPHY(pi)) - wlc_nphy_deaf_mode(pi, true); -} - -void wlc_phy_watchdog(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - bool delay_phy_cal = false; - pi->sh->now++; - - if (!pi->watchdog_override) - return; - - if (!(SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi))) - wlc_phy_noise_sample_request((struct brcms_phy_pub *) pi, - PHY_NOISE_SAMPLE_MON, - CHSPEC_CHANNEL(pi-> - radio_chanspec)); - - if (pi->phynoise_state && (pi->sh->now - pi->phynoise_now) > 5) - pi->phynoise_state = 0; - - if ((!pi->phycal_txpower) || - ((pi->sh->now - pi->phycal_txpower) >= pi->sh->fast_timer)) { - - if (!SCAN_INPROG_PHY(pi) && wlc_phy_cal_txpower_recalc_sw(pi)) - pi->phycal_txpower = pi->sh->now; - } - - if ((SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi) - || ASSOC_INPROG_PHY(pi))) - return; - - if (ISNPHY(pi) && !pi->disable_percal && !delay_phy_cal) { - - if ((pi->nphy_perical != PHY_PERICAL_DISABLE) && - (pi->nphy_perical != PHY_PERICAL_MANUAL) && - ((pi->sh->now - pi->nphy_perical_last) >= - pi->sh->glacial_timer)) - wlc_phy_cal_perical((struct brcms_phy_pub *) pi, - PHY_PERICAL_WATCHDOG); - - wlc_phy_txpwr_papd_cal_nphy(pi); - } - - if (ISLCNPHY(pi)) { - if (pi->phy_forcecal || - ((pi->sh->now - pi->phy_lastcal) >= - pi->sh->glacial_timer)) { - if (!(SCAN_RM_IN_PROGRESS(pi) || ASSOC_INPROG_PHY(pi))) - wlc_lcnphy_calib_modes( - pi, - LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL); - if (! - (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi) - || ASSOC_INPROG_PHY(pi) - || pi->carrier_suppr_disable - || pi->disable_percal)) - wlc_lcnphy_calib_modes(pi, - PHY_PERICAL_WATCHDOG); - } - } -} - -void wlc_phy_BSSinit(struct brcms_phy_pub *pih, bool bonlyap, int rssi) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - uint i; - uint k; - - for (i = 0; i < MA_WINDOW_SZ; i++) - pi->sh->phy_noise_window[i] = (s8) (rssi & 0xff); - if (ISLCNPHY(pi)) { - for (i = 0; i < MA_WINDOW_SZ; i++) - pi->sh->phy_noise_window[i] = - PHY_NOISE_FIXED_VAL_LCNPHY; - } - pi->sh->phy_noise_index = 0; - - for (i = 0; i < PHY_NOISE_WINDOW_SZ; i++) { - for (k = WL_ANT_IDX_1; k < WL_ANT_RX_MAX; k++) - pi->nphy_noise_win[k][i] = PHY_NOISE_FIXED_VAL_NPHY; - } - pi->nphy_noise_index = 0; -} - -void -wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, s32 *eps_imag) -{ - *eps_imag = (epsilon >> 13); - if (*eps_imag > 0xfff) - *eps_imag -= 0x2000; - - *eps_real = (epsilon & 0x1fff); - if (*eps_real > 0xfff) - *eps_real -= 0x2000; -} - -void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi) -{ - wlapi_del_timer(pi->phycal_timer); - - pi->cal_type_override = PHY_PERICAL_AUTO; - pi->mphase_cal_phase_id = MPHASE_CAL_STATE_IDLE; - pi->mphase_txcal_cmdidx = 0; -} - -static void -wlc_phy_cal_perical_mphase_schedule(struct brcms_phy *pi, uint delay) -{ - - if ((pi->nphy_perical != PHY_PERICAL_MPHASE) && - (pi->nphy_perical != PHY_PERICAL_MANUAL)) - return; - - wlapi_del_timer(pi->phycal_timer); - - pi->mphase_cal_phase_id = MPHASE_CAL_STATE_INIT; - wlapi_add_timer(pi->phycal_timer, delay, 0); -} - -void wlc_phy_cal_perical(struct brcms_phy_pub *pih, u8 reason) -{ - s16 nphy_currtemp = 0; - s16 delta_temp = 0; - bool do_periodic_cal = true; - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - if (!ISNPHY(pi)) - return; - - if ((pi->nphy_perical == PHY_PERICAL_DISABLE) || - (pi->nphy_perical == PHY_PERICAL_MANUAL)) - return; - - switch (reason) { - case PHY_PERICAL_DRIVERUP: - break; - - case PHY_PERICAL_PHYINIT: - if (pi->nphy_perical == PHY_PERICAL_MPHASE) { - if (PHY_PERICAL_MPHASE_PENDING(pi)) - wlc_phy_cal_perical_mphase_reset(pi); - - wlc_phy_cal_perical_mphase_schedule( - pi, - PHY_PERICAL_INIT_DELAY); - } - break; - - case PHY_PERICAL_JOIN_BSS: - case PHY_PERICAL_START_IBSS: - case PHY_PERICAL_UP_BSS: - if ((pi->nphy_perical == PHY_PERICAL_MPHASE) && - PHY_PERICAL_MPHASE_PENDING(pi)) - wlc_phy_cal_perical_mphase_reset(pi); - - pi->first_cal_after_assoc = true; - - pi->cal_type_override = PHY_PERICAL_FULL; - - if (pi->phycal_tempdelta) - pi->nphy_lastcal_temp = wlc_phy_tempsense_nphy(pi); - - wlc_phy_cal_perical_nphy_run(pi, PHY_PERICAL_FULL); - break; - - case PHY_PERICAL_WATCHDOG: - if (pi->phycal_tempdelta) { - nphy_currtemp = wlc_phy_tempsense_nphy(pi); - delta_temp = - (nphy_currtemp > pi->nphy_lastcal_temp) ? - nphy_currtemp - pi->nphy_lastcal_temp : - pi->nphy_lastcal_temp - nphy_currtemp; - - if ((delta_temp < (s16) pi->phycal_tempdelta) && - (pi->nphy_txiqlocal_chanspec == - pi->radio_chanspec)) - do_periodic_cal = false; - else - pi->nphy_lastcal_temp = nphy_currtemp; - } - - if (do_periodic_cal) { - if (pi->nphy_perical == PHY_PERICAL_MPHASE) { - if (!PHY_PERICAL_MPHASE_PENDING(pi)) - wlc_phy_cal_perical_mphase_schedule( - pi, - PHY_PERICAL_WDOG_DELAY); - } else if (pi->nphy_perical == PHY_PERICAL_SPHASE) - wlc_phy_cal_perical_nphy_run(pi, - PHY_PERICAL_AUTO); - } - break; - default: - break; - } -} - -void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi) -{ - pi->mphase_cal_phase_id = MPHASE_CAL_STATE_INIT; - pi->mphase_txcal_cmdidx = 0; -} - -u8 wlc_phy_nbits(s32 value) -{ - s32 abs_val; - u8 nbits = 0; - - abs_val = abs(value); - while ((abs_val >> nbits) > 0) - nbits++; - - return nbits; -} - -void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - pi->sh->hw_phytxchain = txchain; - pi->sh->hw_phyrxchain = rxchain; - pi->sh->phytxchain = txchain; - pi->sh->phyrxchain = rxchain; - pi->pubpi.phy_corenum = (u8)hweight8(pi->sh->phyrxchain); -} - -void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - pi->sh->phytxchain = txchain; - - if (ISNPHY(pi)) - wlc_phy_rxcore_setstate_nphy(pih, rxchain); - - pi->pubpi.phy_corenum = (u8)hweight8(pi->sh->phyrxchain); -} - -void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, u8 *rxchain) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - *txchain = pi->sh->phytxchain; - *rxchain = pi->sh->phyrxchain; -} - -u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih) -{ - s16 nphy_currtemp; - u8 active_bitmap; - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - active_bitmap = (pi->phy_txcore_heatedup) ? 0x31 : 0x33; - - if (!pi->watchdog_override) - return active_bitmap; - - if (NREV_GE(pi->pubpi.phy_rev, 6)) { - wlapi_suspend_mac_and_wait(pi->sh->physhim); - nphy_currtemp = wlc_phy_tempsense_nphy(pi); - wlapi_enable_mac(pi->sh->physhim); - - if (!pi->phy_txcore_heatedup) { - if (nphy_currtemp >= pi->phy_txcore_disable_temp) { - active_bitmap &= 0xFD; - pi->phy_txcore_heatedup = true; - } - } else { - if (nphy_currtemp <= pi->phy_txcore_enable_temp) { - active_bitmap |= 0x2; - pi->phy_txcore_heatedup = false; - } - } - } - - return active_bitmap; -} - -s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, u16 chanspec) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - u8 siso_mcs_id, cdd_mcs_id; - - siso_mcs_id = - (CHSPEC_IS40(chanspec)) ? TXP_FIRST_MCS_40_SISO : - TXP_FIRST_MCS_20_SISO; - cdd_mcs_id = - (CHSPEC_IS40(chanspec)) ? TXP_FIRST_MCS_40_CDD : - TXP_FIRST_MCS_20_CDD; - - if (pi->tx_power_target[siso_mcs_id] > - (pi->tx_power_target[cdd_mcs_id] + 12)) - return PHY_TXC1_MODE_SISO; - else - return PHY_TXC1_MODE_CDD; -} - -const u8 *wlc_phy_get_ofdm_rate_lookup(void) -{ - return ofdm_rate_lookup; -} - -void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode) -{ - if ((pi->sh->chip == BCMA_CHIP_ID_BCM4313) && - (pi->sh->boardflags & BFL_FEM)) { - if (mode) { - u16 txant = 0; - txant = wlapi_bmac_get_txant(pi->sh->physhim); - if (txant == 1) { - mod_phy_reg(pi, 0x44d, (0x1 << 2), (1) << 2); - - mod_phy_reg(pi, 0x44c, (0x1 << 2), (1) << 2); - - } - - bcma_chipco_gpio_control(&pi->d11core->bus->drv_cc, - 0x0, 0x0); - bcma_chipco_gpio_out(&pi->d11core->bus->drv_cc, - ~0x40, 0x40); - bcma_chipco_gpio_outen(&pi->d11core->bus->drv_cc, - ~0x40, 0x40); - } else { - mod_phy_reg(pi, 0x44c, (0x1 << 2), (0) << 2); - - mod_phy_reg(pi, 0x44d, (0x1 << 2), (0) << 2); - - bcma_chipco_gpio_out(&pi->d11core->bus->drv_cc, - ~0x40, 0x00); - bcma_chipco_gpio_outen(&pi->d11core->bus->drv_cc, - ~0x40, 0x00); - bcma_chipco_gpio_control(&pi->d11core->bus->drv_cc, - 0x0, 0x40); - } - } -} - -void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool ldpc) -{ - return; -} - -void -wlc_phy_get_pwrdet_offsets(struct brcms_phy *pi, s8 *cckoffset, s8 *ofdmoffset) -{ - *cckoffset = 0; - *ofdmoffset = 0; -} - -s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, u16 chanspec) -{ - - return rssi; -} - -bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *ppi) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - if (ISNPHY(pi)) - return wlc_phy_n_txpower_ipa_ison(pi); - else - return false; -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h deleted file mode 100644 index 4d3734f48d9c..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (c) 2010 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. - */ - -/* - * phy_hal.h: functionality exported from the phy to higher layers - */ - -#ifndef _BRCM_PHY_HAL_H_ -#define _BRCM_PHY_HAL_H_ - -#include -#include -#include - -#define IDCODE_VER_MASK 0x0000000f -#define IDCODE_VER_SHIFT 0 -#define IDCODE_MFG_MASK 0x00000fff -#define IDCODE_MFG_SHIFT 0 -#define IDCODE_ID_MASK 0x0ffff000 -#define IDCODE_ID_SHIFT 12 -#define IDCODE_REV_MASK 0xf0000000 -#define IDCODE_REV_SHIFT 28 - -#define NORADIO_ID 0xe4f5 -#define NORADIO_IDCODE 0x4e4f5246 - -#define BCM2055_ID 0x2055 -#define BCM2055_IDCODE 0x02055000 -#define BCM2055A0_IDCODE 0x1205517f - -#define BCM2056_ID 0x2056 -#define BCM2056_IDCODE 0x02056000 -#define BCM2056A0_IDCODE 0x1205617f - -#define BCM2057_ID 0x2057 -#define BCM2057_IDCODE 0x02057000 -#define BCM2057A0_IDCODE 0x1205717f - -#define BCM2064_ID 0x2064 -#define BCM2064_IDCODE 0x02064000 -#define BCM2064A0_IDCODE 0x0206417f - -#define PHY_TPC_HW_OFF false -#define PHY_TPC_HW_ON true - -#define PHY_PERICAL_DRIVERUP 1 -#define PHY_PERICAL_WATCHDOG 2 -#define PHY_PERICAL_PHYINIT 3 -#define PHY_PERICAL_JOIN_BSS 4 -#define PHY_PERICAL_START_IBSS 5 -#define PHY_PERICAL_UP_BSS 6 -#define PHY_PERICAL_CHAN 7 -#define PHY_FULLCAL 8 - -#define PHY_PERICAL_DISABLE 0 -#define PHY_PERICAL_SPHASE 1 -#define PHY_PERICAL_MPHASE 2 -#define PHY_PERICAL_MANUAL 3 - -#define PHY_HOLD_FOR_ASSOC 1 -#define PHY_HOLD_FOR_SCAN 2 -#define PHY_HOLD_FOR_RM 4 -#define PHY_HOLD_FOR_PLT 8 -#define PHY_HOLD_FOR_MUTE 16 -#define PHY_HOLD_FOR_NOT_ASSOC 0x20 - -#define PHY_MUTE_FOR_PREISM 1 -#define PHY_MUTE_ALL 0xffffffff - -#define PHY_NOISE_FIXED_VAL (-95) -#define PHY_NOISE_FIXED_VAL_NPHY (-92) -#define PHY_NOISE_FIXED_VAL_LCNPHY (-92) - -#define PHY_MODE_CAL 0x0002 -#define PHY_MODE_NOISEM 0x0004 - -#define BRCMS_TXPWR_DB_FACTOR 4 - -/* a large TX Power as an init value to factor out of min() calculations, - * keep low enough to fit in an s8, units are .25 dBm - */ -#define BRCMS_TXPWR_MAX (127) /* ~32 dBm = 1,500 mW */ - -#define BRCMS_NUM_RATES_CCK 4 -#define BRCMS_NUM_RATES_OFDM 8 -#define BRCMS_NUM_RATES_MCS_1_STREAM 8 -#define BRCMS_NUM_RATES_MCS_2_STREAM 8 -#define BRCMS_NUM_RATES_MCS_3_STREAM 8 -#define BRCMS_NUM_RATES_MCS_4_STREAM 8 - -#define BRCMS_RSSI_INVALID 0 /* invalid RSSI value */ - -struct d11regs; -struct phy_shim_info; - -struct txpwr_limits { - u8 cck[BRCMS_NUM_RATES_CCK]; - u8 ofdm[BRCMS_NUM_RATES_OFDM]; - - u8 ofdm_cdd[BRCMS_NUM_RATES_OFDM]; - - u8 ofdm_40_siso[BRCMS_NUM_RATES_OFDM]; - u8 ofdm_40_cdd[BRCMS_NUM_RATES_OFDM]; - - u8 mcs_20_siso[BRCMS_NUM_RATES_MCS_1_STREAM]; - u8 mcs_20_cdd[BRCMS_NUM_RATES_MCS_1_STREAM]; - u8 mcs_20_stbc[BRCMS_NUM_RATES_MCS_1_STREAM]; - u8 mcs_20_mimo[BRCMS_NUM_RATES_MCS_2_STREAM]; - - u8 mcs_40_siso[BRCMS_NUM_RATES_MCS_1_STREAM]; - u8 mcs_40_cdd[BRCMS_NUM_RATES_MCS_1_STREAM]; - u8 mcs_40_stbc[BRCMS_NUM_RATES_MCS_1_STREAM]; - u8 mcs_40_mimo[BRCMS_NUM_RATES_MCS_2_STREAM]; - u8 mcs32; -}; - -struct tx_power { - u32 flags; - u16 chanspec; /* txpwr report for this channel */ - u16 local_chanspec; /* channel on which we are associated */ - u8 local_max; /* local max according to the AP */ - u8 local_constraint; /* local constraint according to the AP */ - s8 antgain[2]; /* Ant gain for each band - from SROM */ - u8 rf_cores; /* count of RF Cores being reported */ - u8 est_Pout[4]; /* Latest tx power out estimate per RF chain */ - u8 est_Pout_act[4]; /* Latest tx power out estimate per RF chain - * without adjustment */ - u8 est_Pout_cck; /* Latest CCK tx power out estimate */ - u8 tx_power_max[4]; /* Maximum target power among all rates */ - /* Index of the rate with the max target power */ - u8 tx_power_max_rate_ind[4]; - /* User limit */ - u8 user_limit[WL_TX_POWER_RATES]; - /* Regulatory power limit */ - u8 reg_limit[WL_TX_POWER_RATES]; - /* Max power board can support (SROM) */ - u8 board_limit[WL_TX_POWER_RATES]; - /* Latest target power */ - u8 target[WL_TX_POWER_RATES]; -}; - -struct tx_inst_power { - u8 txpwr_est_Pout[2]; /* Latest estimate for 2.4 and 5 Ghz */ - u8 txpwr_est_Pout_gofdm; /* Pwr estimate for 2.4 OFDM */ -}; - -struct brcms_chanvec { - u8 vec[MAXCHANNEL / NBBY]; -}; - -struct shared_phy_params { - struct si_pub *sih; - struct phy_shim_info *physhim; - uint unit; - uint corerev; - u16 vid; - u16 did; - uint chip; - uint chiprev; - uint chippkg; - uint sromrev; - uint boardtype; - uint boardrev; - u32 boardflags; - u32 boardflags2; -}; - - -struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp); -struct brcms_phy_pub *wlc_phy_attach(struct shared_phy *sh, - struct bcma_device *d11core, int bandtype, - struct wiphy *wiphy); -void wlc_phy_detach(struct brcms_phy_pub *ppi); - -bool wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype, - u16 *phyrev, u16 *radioid, u16 *radiover); -bool wlc_phy_get_encore(struct brcms_phy_pub *pih); -u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih); - -void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *ppi, bool newstate); -void wlc_phy_hw_state_upd(struct brcms_phy_pub *ppi, bool newstate); -void wlc_phy_init(struct brcms_phy_pub *ppi, u16 chanspec); -void wlc_phy_watchdog(struct brcms_phy_pub *ppi); -int wlc_phy_down(struct brcms_phy_pub *ppi); -u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih); -void wlc_phy_cal_init(struct brcms_phy_pub *ppi); -void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init); - -void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, u16 chanspec); -u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi); -void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, u16 newch); -u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi); -void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw); - -int wlc_phy_rssi_compute(struct brcms_phy_pub *pih, struct d11rxhdr *rxh); -void wlc_phy_por_inform(struct brcms_phy_pub *ppi); -void wlc_phy_noise_sample_intr(struct brcms_phy_pub *ppi); -bool wlc_phy_bist_check_phy(struct brcms_phy_pub *ppi); - -void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag); - -void wlc_phy_switch_radio(struct brcms_phy_pub *ppi, bool on); -void wlc_phy_anacore(struct brcms_phy_pub *ppi, bool on); - - -void wlc_phy_BSSinit(struct brcms_phy_pub *ppi, bool bonlyap, int rssi); - -void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi, - bool wide_filter); -void wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band, - struct brcms_chanvec *channels); -u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, uint band); - -void wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint chan, u8 *_min_, - u8 *_max_, int rate); -void wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, uint chan, - u8 *_max_, u8 *_min_); -void wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, uint band, - s32 *, s32 *, u32 *); -void wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, struct txpwr_limits *, - u16 chanspec); -int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, bool *override); -int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, bool override); -void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi, - struct txpwr_limits *); -bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi); -void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, bool hwpwrctrl); -u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi); -u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi); -bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *pih); - -void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain); -void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain); -void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, u8 *rxchain); -u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih); -s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, u16 chanspec); -void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool val); - -void wlc_phy_cal_perical(struct brcms_phy_pub *ppi, u8 reason); -void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *ppi); -void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock); -void wlc_phy_cal_papd_recal(struct brcms_phy_pub *ppi); - -void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val); -void wlc_phy_clear_tssi(struct brcms_phy_pub *ppi); -void wlc_phy_hold_upd(struct brcms_phy_pub *ppi, u32 id, bool val); -void wlc_phy_mute_upd(struct brcms_phy_pub *ppi, bool val, u32 flags); - -void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type); - -void wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi, - struct tx_power *power, uint channel); - -void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal); -bool wlc_phy_test_ison(struct brcms_phy_pub *ppi); -void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, u8 txpwr_percent); -void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war); -void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, bool bf_preempt); -void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap); - -void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end); - -void wlc_phy_freqtrack_start(struct brcms_phy_pub *ppi); -void wlc_phy_freqtrack_end(struct brcms_phy_pub *ppi); - -const u8 *wlc_phy_get_ofdm_rate_lookup(void); - -s8 wlc_phy_get_tx_power_offset_by_mcs(struct brcms_phy_pub *ppi, - u8 mcs_offset); -s8 wlc_phy_get_tx_power_offset(struct brcms_phy_pub *ppi, u8 tbl_offset); -#endif /* _BRCM_PHY_HAL_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h deleted file mode 100644 index 4960f7d26804..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h +++ /dev/null @@ -1,1142 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_PHY_INT_H_ -#define _BRCM_PHY_INT_H_ - -#include -#include -#include - -#define PHY_VERSION { 1, 82, 8, 0 } - -#define LCNXN_BASEREV 16 - -struct phy_shim_info; - -struct brcms_phy_srom_fem { - /* TSSI positive slope, 1: positive, 0: negative */ - u8 tssipos; - /* Ext PA gain-type: full-gain: 0, pa-lite: 1, no_pa: 2 */ - u8 extpagain; - /* support 32 combinations of different Pdet dynamic ranges */ - u8 pdetrange; - /* TR switch isolation */ - u8 triso; - /* antswctrl lookup table configuration: 32 possible choices */ - u8 antswctrllut; -}; - -#define ISNPHY(pi) PHYTYPE_IS((pi)->pubpi.phy_type, PHY_TYPE_N) -#define ISLCNPHY(pi) PHYTYPE_IS((pi)->pubpi.phy_type, PHY_TYPE_LCN) - -#define PHY_GET_RFATTN(rfgain) ((rfgain) & 0x0f) -#define PHY_GET_PADMIX(rfgain) (((rfgain) & 0x10) >> 4) -#define PHY_GET_RFGAINID(rfattn, padmix, width) ((rfattn) + ((padmix)*(width))) -#define PHY_SAT(x, n) ((x) > ((1<<((n)-1))-1) ? ((1<<((n)-1))-1) : \ - ((x) < -(1<<((n)-1)) ? -(1<<((n)-1)) : (x))) -#define PHY_SHIFT_ROUND(x, n) ((x) >= 0 ? ((x)+(1<<((n)-1)))>>(n) : (x)>>(n)) -#define PHY_HW_ROUND(x, s) ((x >> s) + ((x >> (s-1)) & (s != 0))) - -#define CH_5G_GROUP 3 -#define A_LOW_CHANS 0 -#define A_MID_CHANS 1 -#define A_HIGH_CHANS 2 -#define CH_2G_GROUP 1 -#define G_ALL_CHANS 0 - -#define FIRST_REF5_CHANNUM 149 -#define LAST_REF5_CHANNUM 165 -#define FIRST_5G_CHAN 14 -#define LAST_5G_CHAN 50 -#define FIRST_MID_5G_CHAN 14 -#define LAST_MID_5G_CHAN 35 -#define FIRST_HIGH_5G_CHAN 36 -#define LAST_HIGH_5G_CHAN 41 -#define FIRST_LOW_5G_CHAN 42 -#define LAST_LOW_5G_CHAN 50 - -#define BASE_LOW_5G_CHAN 4900 -#define BASE_MID_5G_CHAN 5100 -#define BASE_HIGH_5G_CHAN 5500 - -#define CHAN5G_FREQ(chan) (5000 + chan*5) -#define CHAN2G_FREQ(chan) (2407 + chan*5) - -#define TXP_FIRST_CCK 0 -#define TXP_LAST_CCK 3 -#define TXP_FIRST_OFDM 4 -#define TXP_LAST_OFDM 11 -#define TXP_FIRST_OFDM_20_CDD 12 -#define TXP_LAST_OFDM_20_CDD 19 -#define TXP_FIRST_MCS_20_SISO 20 -#define TXP_LAST_MCS_20_SISO 27 -#define TXP_FIRST_MCS_20_CDD 28 -#define TXP_LAST_MCS_20_CDD 35 -#define TXP_FIRST_MCS_20_STBC 36 -#define TXP_LAST_MCS_20_STBC 43 -#define TXP_FIRST_MCS_20_SDM 44 -#define TXP_LAST_MCS_20_SDM 51 -#define TXP_FIRST_OFDM_40_SISO 52 -#define TXP_LAST_OFDM_40_SISO 59 -#define TXP_FIRST_OFDM_40_CDD 60 -#define TXP_LAST_OFDM_40_CDD 67 -#define TXP_FIRST_MCS_40_SISO 68 -#define TXP_LAST_MCS_40_SISO 75 -#define TXP_FIRST_MCS_40_CDD 76 -#define TXP_LAST_MCS_40_CDD 83 -#define TXP_FIRST_MCS_40_STBC 84 -#define TXP_LAST_MCS_40_STBC 91 -#define TXP_FIRST_MCS_40_SDM 92 -#define TXP_LAST_MCS_40_SDM 99 -#define TXP_MCS_32 100 -#define TXP_NUM_RATES 101 -#define ADJ_PWR_TBL_LEN 84 - -#define TXP_FIRST_SISO_MCS_20 20 -#define TXP_LAST_SISO_MCS_20 27 - -#define PHY_CORE_NUM_1 1 -#define PHY_CORE_NUM_2 2 -#define PHY_CORE_NUM_3 3 -#define PHY_CORE_NUM_4 4 -#define PHY_CORE_MAX PHY_CORE_NUM_4 -#define PHY_CORE_0 0 -#define PHY_CORE_1 1 -#define PHY_CORE_2 2 -#define PHY_CORE_3 3 - -#define MA_WINDOW_SZ 8 - -#define PHY_NOISE_SAMPLE_MON 1 -#define PHY_NOISE_SAMPLE_EXTERNAL 2 -#define PHY_NOISE_WINDOW_SZ 16 -#define PHY_NOISE_GLITCH_INIT_MA 10 -#define PHY_NOISE_GLITCH_INIT_MA_BADPlCP 10 -#define PHY_NOISE_STATE_MON 0x1 -#define PHY_NOISE_STATE_EXTERNAL 0x2 -#define PHY_NOISE_SAMPLE_LOG_NUM_NPHY 10 -#define PHY_NOISE_SAMPLE_LOG_NUM_UCODE 9 - -#define PHY_NOISE_OFFSETFACT_4322 (-103) -#define PHY_NOISE_MA_WINDOW_SZ 2 - -#define PHY_RSSI_TABLE_SIZE 64 -#define RSSI_ANT_MERGE_MAX 0 -#define RSSI_ANT_MERGE_MIN 1 -#define RSSI_ANT_MERGE_AVG 2 - -#define PHY_TSSI_TABLE_SIZE 64 -#define APHY_TSSI_TABLE_SIZE 256 -#define TX_GAIN_TABLE_LENGTH 64 -#define DEFAULT_11A_TXP_IDX 24 -#define NUM_TSSI_FRAMES 4 -#define NULL_TSSI 0x7f -#define NULL_TSSI_W 0x7f7f - -#define PHY_PAPD_EPS_TBL_SIZE_LCNPHY 64 - -#define LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL 9 - -#define PHY_TXPWR_MIN 10 -#define PHY_TXPWR_MIN_NPHY 8 -#define RADIOPWR_OVERRIDE_DEF (-1) - -#define PWRTBL_NUM_COEFF 3 - -#define SPURAVOID_DISABLE 0 -#define SPURAVOID_AUTO 1 -#define SPURAVOID_FORCEON 2 -#define SPURAVOID_FORCEON2 3 - -#define PHY_SW_TIMER_FAST 15 -#define PHY_SW_TIMER_SLOW 60 -#define PHY_SW_TIMER_GLACIAL 120 - -#define PHY_PERICAL_AUTO 0 -#define PHY_PERICAL_FULL 1 -#define PHY_PERICAL_PARTIAL 2 - -#define PHY_PERICAL_NODELAY 0 -#define PHY_PERICAL_INIT_DELAY 5 -#define PHY_PERICAL_ASSOC_DELAY 5 -#define PHY_PERICAL_WDOG_DELAY 5 - -#define MPHASE_TXCAL_NUMCMDS 2 - -#define PHY_PERICAL_MPHASE_PENDING(pi) \ - (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_IDLE) - -enum { - MPHASE_CAL_STATE_IDLE = 0, - MPHASE_CAL_STATE_INIT = 1, - MPHASE_CAL_STATE_TXPHASE0, - MPHASE_CAL_STATE_TXPHASE1, - MPHASE_CAL_STATE_TXPHASE2, - MPHASE_CAL_STATE_TXPHASE3, - MPHASE_CAL_STATE_TXPHASE4, - MPHASE_CAL_STATE_TXPHASE5, - MPHASE_CAL_STATE_PAPDCAL, - MPHASE_CAL_STATE_RXCAL, - MPHASE_CAL_STATE_RSSICAL, - MPHASE_CAL_STATE_IDLETSSI -}; - -enum phy_cal_mode { - CAL_FULL, - CAL_RECAL, - CAL_CURRECAL, - CAL_DIGCAL, - CAL_GCTRL, - CAL_SOFT, - CAL_DIGLO -}; - -#define RDR_NTIERS 1 -#define RDR_TIER_SIZE 64 -#define RDR_LIST_SIZE (512/3) -#define RDR_EPOCH_SIZE 40 -#define RDR_NANTENNAS 2 -#define RDR_NTIER_SIZE RDR_LIST_SIZE -#define RDR_LP_BUFFER_SIZE 64 -#define LP_LEN_HIS_SIZE 10 - -#define STATIC_NUM_RF 32 -#define STATIC_NUM_BB 9 - -#define BB_MULT_MASK 0x0000ffff -#define BB_MULT_VALID_MASK 0x80000000 - -#define CORDIC_AG 39797 -#define CORDIC_NI 18 -#define FIXED(X) ((s32)((X) << 16)) - -#define FLOAT(X) \ - (((X) >= 0) ? ((((X) >> 15) + 1) >> 1) : -((((-(X)) >> 15) + 1) >> 1)) - -#define PHY_CHAIN_TX_DISABLE_TEMP 115 -#define PHY_HYSTERESIS_DELTATEMP 5 - -#define SCAN_INPROG_PHY(pi) \ - (mboolisset(pi->measure_hold, PHY_HOLD_FOR_SCAN)) - -#define PLT_INPROG_PHY(pi) (mboolisset(pi->measure_hold, PHY_HOLD_FOR_PLT)) - -#define ASSOC_INPROG_PHY(pi) \ - (mboolisset(pi->measure_hold, PHY_HOLD_FOR_ASSOC)) - -#define SCAN_RM_IN_PROGRESS(pi) \ - (mboolisset(pi->measure_hold, PHY_HOLD_FOR_SCAN | PHY_HOLD_FOR_RM)) - -#define PHY_MUTED(pi) \ - (mboolisset(pi->measure_hold, PHY_HOLD_FOR_MUTE)) - -#define PUB_NOT_ASSOC(pi) \ - (mboolisset(pi->measure_hold, PHY_HOLD_FOR_NOT_ASSOC)) - -struct phy_table_info { - uint table; - int q; - uint max; -}; - -struct phytbl_info { - const void *tbl_ptr; - u32 tbl_len; - u32 tbl_id; - u32 tbl_offset; - u32 tbl_width; -}; - -struct interference_info { - u8 curr_home_channel; - u16 crsminpwrthld_40_stored; - u16 crsminpwrthld_20L_stored; - u16 crsminpwrthld_20U_stored; - u16 init_gain_code_core1_stored; - u16 init_gain_code_core2_stored; - u16 init_gain_codeb_core1_stored; - u16 init_gain_codeb_core2_stored; - u16 init_gain_table_stored[4]; - - u16 clip1_hi_gain_code_core1_stored; - u16 clip1_hi_gain_code_core2_stored; - u16 clip1_hi_gain_codeb_core1_stored; - u16 clip1_hi_gain_codeb_core2_stored; - u16 nb_clip_thresh_core1_stored; - u16 nb_clip_thresh_core2_stored; - u16 init_ofdmlna2gainchange_stored[4]; - u16 init_ccklna2gainchange_stored[4]; - u16 clip1_lo_gain_code_core1_stored; - u16 clip1_lo_gain_code_core2_stored; - u16 clip1_lo_gain_codeb_core1_stored; - u16 clip1_lo_gain_codeb_core2_stored; - u16 w1_clip_thresh_core1_stored; - u16 w1_clip_thresh_core2_stored; - u16 radio_2056_core1_rssi_gain_stored; - u16 radio_2056_core2_rssi_gain_stored; - u16 energy_drop_timeout_len_stored; - - u16 ed_crs40_assertthld0_stored; - u16 ed_crs40_assertthld1_stored; - u16 ed_crs40_deassertthld0_stored; - u16 ed_crs40_deassertthld1_stored; - u16 ed_crs20L_assertthld0_stored; - u16 ed_crs20L_assertthld1_stored; - u16 ed_crs20L_deassertthld0_stored; - u16 ed_crs20L_deassertthld1_stored; - u16 ed_crs20U_assertthld0_stored; - u16 ed_crs20U_assertthld1_stored; - u16 ed_crs20U_deassertthld0_stored; - u16 ed_crs20U_deassertthld1_stored; - - u16 badplcp_ma; - u16 badplcp_ma_previous; - u16 badplcp_ma_total; - u16 badplcp_ma_list[MA_WINDOW_SZ]; - int badplcp_ma_index; - s16 pre_badplcp_cnt; - s16 bphy_pre_badplcp_cnt; - - u16 init_gain_core1; - u16 init_gain_core2; - u16 init_gainb_core1; - u16 init_gainb_core2; - u16 init_gain_rfseq[4]; - - u16 crsminpwr0; - u16 crsminpwrl0; - u16 crsminpwru0; - - s16 crsminpwr_index; - - u16 radio_2057_core1_rssi_wb1a_gc_stored; - u16 radio_2057_core2_rssi_wb1a_gc_stored; - u16 radio_2057_core1_rssi_wb1g_gc_stored; - u16 radio_2057_core2_rssi_wb1g_gc_stored; - u16 radio_2057_core1_rssi_wb2_gc_stored; - u16 radio_2057_core2_rssi_wb2_gc_stored; - u16 radio_2057_core1_rssi_nb_gc_stored; - u16 radio_2057_core2_rssi_nb_gc_stored; -}; - -struct aci_save_gphy { - u16 rc_cal_ovr; - u16 phycrsth1; - u16 phycrsth2; - u16 init_n1p1_gain; - u16 p1_p2_gain; - u16 n1_n2_gain; - u16 n1_p1_gain; - u16 div_search_gain; - u16 div_p1_p2_gain; - u16 div_search_gn_change; - u16 table_7_2; - u16 table_7_3; - u16 cckshbits_gnref; - u16 clip_thresh; - u16 clip2_thresh; - u16 clip3_thresh; - u16 clip_p2_thresh; - u16 clip_pwdn_thresh; - u16 clip_n1p1_thresh; - u16 clip_n1_pwdn_thresh; - u16 bbconfig; - u16 cthr_sthr_shdin; - u16 energy; - u16 clip_p1_p2_thresh; - u16 threshold; - u16 reg15; - u16 reg16; - u16 reg17; - u16 div_srch_idx; - u16 div_srch_p1_p2; - u16 div_srch_gn_back; - u16 ant_dwell; - u16 ant_wr_settle; -}; - -struct lo_complex_abgphy_info { - s8 i; - s8 q; -}; - -struct nphy_iq_comp { - s16 a0; - s16 b0; - s16 a1; - s16 b1; -}; - -struct nphy_txpwrindex { - s8 index; - s8 index_internal; - s8 index_internal_save; - u16 AfectrlOverride; - u16 AfeCtrlDacGain; - u16 rad_gain; - u8 bbmult; - u16 iqcomp_a; - u16 iqcomp_b; - u16 locomp; -}; - -struct txiqcal_cache { - - u16 txcal_coeffs_2G[8]; - u16 txcal_radio_regs_2G[8]; - struct nphy_iq_comp rxcal_coeffs_2G; - - u16 txcal_coeffs_5G[8]; - u16 txcal_radio_regs_5G[8]; - struct nphy_iq_comp rxcal_coeffs_5G; -}; - -struct nphy_pwrctrl { - s8 max_pwr_2g; - s8 idle_targ_2g; - s16 pwrdet_2g_a1; - s16 pwrdet_2g_b0; - s16 pwrdet_2g_b1; - s8 max_pwr_5gm; - s8 idle_targ_5gm; - s8 max_pwr_5gh; - s8 max_pwr_5gl; - s16 pwrdet_5gm_a1; - s16 pwrdet_5gm_b0; - s16 pwrdet_5gm_b1; - s16 pwrdet_5gl_a1; - s16 pwrdet_5gl_b0; - s16 pwrdet_5gl_b1; - s16 pwrdet_5gh_a1; - s16 pwrdet_5gh_b0; - s16 pwrdet_5gh_b1; - s8 idle_targ_5gl; - s8 idle_targ_5gh; - s8 idle_tssi_2g; - s8 idle_tssi_5g; - s8 idle_tssi; - s16 a1; - s16 b0; - s16 b1; -}; - -struct nphy_txgains { - u16 txlpf[2]; - u16 txgm[2]; - u16 pga[2]; - u16 pad[2]; - u16 ipa[2]; -}; - -#define PHY_NOISEVAR_BUFSIZE 10 - -struct nphy_noisevar_buf { - int bufcount; - int tone_id[PHY_NOISEVAR_BUFSIZE]; - u32 noise_vars[PHY_NOISEVAR_BUFSIZE]; - u32 min_noise_vars[PHY_NOISEVAR_BUFSIZE]; -}; - -struct rssical_cache { - u16 rssical_radio_regs_2G[2]; - u16 rssical_phyregs_2G[12]; - - u16 rssical_radio_regs_5G[2]; - u16 rssical_phyregs_5G[12]; -}; - -struct lcnphy_cal_results { - - u16 txiqlocal_a; - u16 txiqlocal_b; - u16 txiqlocal_didq; - u8 txiqlocal_ei0; - u8 txiqlocal_eq0; - u8 txiqlocal_fi0; - u8 txiqlocal_fq0; - - u16 txiqlocal_bestcoeffs[11]; - u16 txiqlocal_bestcoeffs_valid; - - u32 papd_eps_tbl[PHY_PAPD_EPS_TBL_SIZE_LCNPHY]; - u16 analog_gain_ref; - u16 lut_begin; - u16 lut_end; - u16 lut_step; - u16 rxcompdbm; - u16 papdctrl; - u16 sslpnCalibClkEnCtrl; - - u16 rxiqcal_coeff_a0; - u16 rxiqcal_coeff_b0; -}; - -struct shared_phy { - struct brcms_phy *phy_head; - uint unit; - struct phy_shim_info *physhim; - uint corerev; - u32 machwcap; - bool up; - bool clk; - uint now; - u16 vid; - u16 did; - uint chip; - uint chiprev; - uint chippkg; - uint sromrev; - uint boardtype; - uint boardrev; - u32 boardflags; - u32 boardflags2; - uint fast_timer; - uint slow_timer; - uint glacial_timer; - u8 rx_antdiv; - s8 phy_noise_window[MA_WINDOW_SZ]; - uint phy_noise_index; - u8 hw_phytxchain; - u8 hw_phyrxchain; - u8 phytxchain; - u8 phyrxchain; - u8 rssi_mode; - bool _rifs_phy; -}; - -struct brcms_phy_pub { - uint phy_type; - uint phy_rev; - u8 phy_corenum; - u16 radioid; - u8 radiorev; - u8 radiover; - - uint coreflags; - uint ana_rev; - bool abgphy_encore; -}; - -struct phy_func_ptr { - void (*init)(struct brcms_phy *); - void (*calinit)(struct brcms_phy *); - void (*chanset)(struct brcms_phy *, u16 chanspec); - void (*txpwrrecalc)(struct brcms_phy *); - int (*longtrn)(struct brcms_phy *, int); - void (*txiqccget)(struct brcms_phy *, u16 *, u16 *); - void (*txiqccset)(struct brcms_phy *, u16, u16); - u16 (*txloccget)(struct brcms_phy *); - void (*radioloftget)(struct brcms_phy *, u8 *, u8 *, u8 *, u8 *); - void (*carrsuppr)(struct brcms_phy *); - s32 (*rxsigpwr)(struct brcms_phy *, s32); - void (*detach)(struct brcms_phy *); -}; - -struct brcms_phy { - struct brcms_phy_pub pubpi_ro; - struct shared_phy *sh; - struct phy_func_ptr pi_fptr; - - union { - struct brcms_phy_lcnphy *pi_lcnphy; - } u; - bool user_txpwr_at_rfport; - - struct bcma_device *d11core; - struct brcms_phy *next; - struct brcms_phy_pub pubpi; - - bool do_initcal; - bool phytest_on; - bool ofdm_rateset_war; - bool bf_preempt_4306; - u16 radio_chanspec; - u8 antsel_type; - u16 bw; - u8 txpwr_percent; - bool phy_init_por; - - bool init_in_progress; - bool initialized; - bool sbtml_gm; - uint refcnt; - bool watchdog_override; - u8 phynoise_state; - uint phynoise_now; - int phynoise_chan_watchdog; - bool phynoise_polling; - bool disable_percal; - u32 measure_hold; - - s16 txpa_2g[PWRTBL_NUM_COEFF]; - s16 txpa_2g_low_temp[PWRTBL_NUM_COEFF]; - s16 txpa_2g_high_temp[PWRTBL_NUM_COEFF]; - s16 txpa_5g_low[PWRTBL_NUM_COEFF]; - s16 txpa_5g_mid[PWRTBL_NUM_COEFF]; - s16 txpa_5g_hi[PWRTBL_NUM_COEFF]; - - u8 tx_srom_max_2g; - u8 tx_srom_max_5g_low; - u8 tx_srom_max_5g_mid; - u8 tx_srom_max_5g_hi; - u8 tx_srom_max_rate_2g[TXP_NUM_RATES]; - u8 tx_srom_max_rate_5g_low[TXP_NUM_RATES]; - u8 tx_srom_max_rate_5g_mid[TXP_NUM_RATES]; - u8 tx_srom_max_rate_5g_hi[TXP_NUM_RATES]; - u8 tx_user_target[TXP_NUM_RATES]; - s8 tx_power_offset[TXP_NUM_RATES]; - u8 tx_power_target[TXP_NUM_RATES]; - - struct brcms_phy_srom_fem srom_fem2g; - struct brcms_phy_srom_fem srom_fem5g; - - u8 tx_power_max; - u8 tx_power_max_rate_ind; - bool hwpwrctrl; - u8 nphy_txpwrctrl; - s8 nphy_txrx_chain; - bool phy_5g_pwrgain; - - u16 phy_wreg; - u16 phy_wreg_limit; - - s8 n_preamble_override; - u8 antswitch; - u8 aa2g, aa5g; - - s8 idle_tssi[CH_5G_GROUP]; - s8 target_idle_tssi; - s8 txpwr_est_Pout; - u8 tx_power_min; - u8 txpwr_limit[TXP_NUM_RATES]; - u8 txpwr_env_limit[TXP_NUM_RATES]; - u8 adj_pwr_tbl_nphy[ADJ_PWR_TBL_LEN]; - - bool channel_14_wide_filter; - - bool txpwroverride; - bool txpwridx_override_aphy; - s16 radiopwr_override; - u16 hwpwr_txcur; - u8 saved_txpwr_idx; - - bool edcrs_threshold_lock; - - u32 tr_R_gain_val; - u32 tr_T_gain_val; - - s16 ofdm_analog_filt_bw_override; - s16 cck_analog_filt_bw_override; - s16 ofdm_rccal_override; - s16 cck_rccal_override; - u16 extlna_type; - - uint interference_mode_crs_time; - u16 crsglitch_prev; - bool interference_mode_crs; - - u32 phy_tx_tone_freq; - uint phy_lastcal; - bool phy_forcecal; - bool phy_fixed_noise; - u32 xtalfreq; - u8 pdiv; - s8 carrier_suppr_disable; - - bool phy_bphy_evm; - bool phy_bphy_rfcs; - s8 phy_scraminit; - u8 phy_gpiosel; - - s16 phy_txcore_disable_temp; - s16 phy_txcore_enable_temp; - s8 phy_tempsense_offset; - bool phy_txcore_heatedup; - - u16 radiopwr; - u16 bb_atten; - u16 txctl1; - - u16 mintxbias; - u16 mintxmag; - struct lo_complex_abgphy_info gphy_locomp_iq - [STATIC_NUM_RF][STATIC_NUM_BB]; - s8 stats_11b_txpower[STATIC_NUM_RF][STATIC_NUM_BB]; - u16 gain_table[TX_GAIN_TABLE_LENGTH]; - bool loopback_gain; - s16 max_lpback_gain_hdB; - s16 trsw_rx_gain_hdB; - u8 power_vec[8]; - - u16 rc_cal; - int nrssi_table_delta; - int nrssi_slope_scale; - int nrssi_slope_offset; - int min_rssi; - int max_rssi; - - s8 txpwridx; - u8 min_txpower; - - u8 a_band_high_disable; - - u16 tx_vos; - u16 global_tx_bb_dc_bias_loft; - - int rf_max; - int bb_max; - int rf_list_size; - int bb_list_size; - u16 *rf_attn_list; - u16 *bb_attn_list; - u16 padmix_mask; - u16 padmix_reg; - u16 *txmag_list; - uint txmag_len; - bool txmag_enable; - - s8 *a_tssi_to_dbm; - s8 *m_tssi_to_dbm; - s8 *l_tssi_to_dbm; - s8 *h_tssi_to_dbm; - u8 *hwtxpwr; - - u16 freqtrack_saved_regs[2]; - int cur_interference_mode; - bool hwpwrctrl_capable; - bool temppwrctrl_capable; - - uint phycal_nslope; - uint phycal_noffset; - uint phycal_mlo; - uint phycal_txpower; - - u8 phy_aa2g; - - bool nphy_tableloaded; - s8 nphy_rssisel; - u32 nphy_bb_mult_save; - u16 nphy_txiqlocal_bestc[11]; - bool nphy_txiqlocal_coeffsvalid; - struct nphy_txpwrindex nphy_txpwrindex[PHY_CORE_NUM_2]; - struct nphy_pwrctrl nphy_pwrctrl_info[PHY_CORE_NUM_2]; - u16 cck2gpo; - u32 ofdm2gpo; - u32 ofdm5gpo; - u32 ofdm5glpo; - u32 ofdm5ghpo; - u8 bw402gpo; - u8 bw405gpo; - u8 bw405glpo; - u8 bw405ghpo; - u8 cdd2gpo; - u8 cdd5gpo; - u8 cdd5glpo; - u8 cdd5ghpo; - u8 stbc2gpo; - u8 stbc5gpo; - u8 stbc5glpo; - u8 stbc5ghpo; - u8 bwdup2gpo; - u8 bwdup5gpo; - u8 bwdup5glpo; - u8 bwdup5ghpo; - u16 mcs2gpo[8]; - u16 mcs5gpo[8]; - u16 mcs5glpo[8]; - u16 mcs5ghpo[8]; - u32 nphy_rxcalparams; - - u8 phy_spuravoid; - bool phy_isspuravoid; - - u8 phy_pabias; - u8 nphy_papd_skip; - u8 nphy_tssi_slope; - - s16 nphy_noise_win[PHY_CORE_MAX][PHY_NOISE_WINDOW_SZ]; - u8 nphy_noise_index; - - bool nphy_gain_boost; - bool nphy_elna_gain_config; - u16 old_bphy_test; - u16 old_bphy_testcontrol; - - bool phyhang_avoid; - - bool rssical_nphy; - u8 nphy_perical; - uint nphy_perical_last; - u8 cal_type_override; - u8 mphase_cal_phase_id; - u8 mphase_txcal_cmdidx; - u8 mphase_txcal_numcmds; - u16 mphase_txcal_bestcoeffs[11]; - u16 nphy_txiqlocal_chanspec; - u16 nphy_iqcal_chanspec_2G; - u16 nphy_iqcal_chanspec_5G; - u16 nphy_rssical_chanspec_2G; - u16 nphy_rssical_chanspec_5G; - struct wlapi_timer *phycal_timer; - bool use_int_tx_iqlo_cal_nphy; - bool internal_tx_iqlo_cal_tapoff_intpa_nphy; - s16 nphy_lastcal_temp; - - struct txiqcal_cache calibration_cache; - struct rssical_cache rssical_cache; - - u8 nphy_txpwr_idx[2]; - u8 nphy_papd_cal_type; - uint nphy_papd_last_cal; - u16 nphy_papd_tx_gain_at_last_cal[2]; - u8 nphy_papd_cal_gain_index[2]; - s16 nphy_papd_epsilon_offset[2]; - bool nphy_papd_recal_enable; - u32 nphy_papd_recal_counter; - bool nphy_force_papd_cal; - bool nphy_papdcomp; - bool ipa2g_on; - bool ipa5g_on; - - u16 classifier_state; - u16 clip_state[2]; - uint nphy_deaf_count; - u8 rxiq_samps; - u8 rxiq_antsel; - - u16 rfctrlIntc1_save; - u16 rfctrlIntc2_save; - bool first_cal_after_assoc; - u16 tx_rx_cal_radio_saveregs[22]; - u16 tx_rx_cal_phy_saveregs[15]; - - u8 nphy_cal_orig_pwr_idx[2]; - u8 nphy_txcal_pwr_idx[2]; - u8 nphy_rxcal_pwr_idx[2]; - u16 nphy_cal_orig_tx_gain[2]; - struct nphy_txgains nphy_cal_target_gain; - u16 nphy_txcal_bbmult; - u16 nphy_gmval; - - u16 nphy_saved_bbconf; - - bool nphy_gband_spurwar_en; - bool nphy_gband_spurwar2_en; - bool nphy_aband_spurwar_en; - u16 nphy_rccal_value; - u16 nphy_crsminpwr[3]; - struct nphy_noisevar_buf nphy_saved_noisevars; - bool nphy_anarxlpf_adjusted; - bool nphy_crsminpwr_adjusted; - bool nphy_noisevars_adjusted; - - bool nphy_rxcal_active; - u16 radar_percal_mask; - bool dfs_lp_buffer_nphy; - - u16 nphy_fineclockgatecontrol; - - s8 rx2tx_biasentry; - - u16 crsminpwr0; - u16 crsminpwrl0; - u16 crsminpwru0; - s16 noise_crsminpwr_index; - u16 init_gain_core1; - u16 init_gain_core2; - u16 init_gainb_core1; - u16 init_gainb_core2; - u8 aci_noise_curr_channel; - u16 init_gain_rfseq[4]; - - bool radio_is_on; - - bool nphy_sample_play_lpf_bw_ctl_ovr; - - u16 tbl_data_hi; - u16 tbl_data_lo; - u16 tbl_addr; - - uint tbl_save_id; - uint tbl_save_offset; - - u8 txpwrctrl; - s8 txpwrindex[PHY_CORE_MAX]; - - u8 phycal_tempdelta; - u32 mcs20_po; - u32 mcs40_po; - struct wiphy *wiphy; -}; - -struct cs32 { - s32 q; - s32 i; -}; - -struct radio_regs { - u16 address; - u32 init_a; - u32 init_g; - u8 do_init_a; - u8 do_init_g; -}; - -struct radio_20xx_regs { - u16 address; - u8 init; - u8 do_init; -}; - -struct lcnphy_radio_regs { - u16 address; - u8 init_a; - u8 init_g; - u8 do_init_a; - u8 do_init_g; -}; - -u16 read_phy_reg(struct brcms_phy *pi, u16 addr); -void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); -void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); -void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); -void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); - -u16 read_radio_reg(struct brcms_phy *pi, u16 addr); -void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); -void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); -void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); -void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask); - -void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); - -void wlc_phyreg_enter(struct brcms_phy_pub *pih); -void wlc_phyreg_exit(struct brcms_phy_pub *pih); -void wlc_radioreg_enter(struct brcms_phy_pub *pih); -void wlc_radioreg_exit(struct brcms_phy_pub *pih); - -void wlc_phy_read_table(struct brcms_phy *pi, - const struct phytbl_info *ptbl_info, - u16 tblAddr, u16 tblDataHi, u16 tblDatalo); -void wlc_phy_write_table(struct brcms_phy *pi, - const struct phytbl_info *ptbl_info, - u16 tblAddr, u16 tblDataHi, u16 tblDatalo); -void wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, uint tbl_offset, - u16 tblAddr, u16 tblDataHi, u16 tblDataLo); -void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val); - -void write_phy_channel_reg(struct brcms_phy *pi, uint val); -void wlc_phy_txpower_update_shm(struct brcms_phy *pi); - -u8 wlc_phy_nbits(s32 value); -void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_dB, u8 core); - -uint wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi, - struct radio_20xx_regs *radioregs); -uint wlc_phy_init_radio_regs(struct brcms_phy *pi, - const struct radio_regs *radioregs, - u16 core_offset); - -void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi); - -void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on); -void wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, s32 *eps_imag); - -void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi); -void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi); - -bool wlc_phy_attach_nphy(struct brcms_phy *pi); -bool wlc_phy_attach_lcnphy(struct brcms_phy *pi); - -void wlc_phy_detach_lcnphy(struct brcms_phy *pi); - -void wlc_phy_init_nphy(struct brcms_phy *pi); -void wlc_phy_init_lcnphy(struct brcms_phy *pi); - -void wlc_phy_cal_init_nphy(struct brcms_phy *pi); -void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi); - -void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, u16 chanspec); -void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec); -void wlc_phy_chanspec_set_fixup_lcnphy(struct brcms_phy *pi, u16 chanspec); -int wlc_phy_channel2freq(uint channel); -int wlc_phy_chanspec_freq2bandrange_lpssn(uint); -int wlc_phy_chanspec_bandrange_get(struct brcms_phy *, u16 chanspec); - -void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode); -s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi); - -void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi); -void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi); -void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi); - -void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index); -void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable); -void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi); -void wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val, - bool iqcalmode); - -void wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan, - u8 *max_pwr, u8 rate_id); -void wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start, - u8 rate_mcs_end, u8 rate_ofdm_start); -void wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, u8 rate_ofdm_start, - u8 rate_ofdm_end, u8 rate_mcs_start); - -u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode); -s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode); -s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode); -s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode); -void wlc_phy_carrier_suppress_lcnphy(struct brcms_phy *pi); -void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel); -void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode); -void wlc_2064_vco_cal(struct brcms_phy *pi); - -void wlc_phy_txpower_recalc_target(struct brcms_phy *pi); - -#define LCNPHY_TBL_ID_PAPDCOMPDELTATBL 0x18 -#define LCNPHY_TX_POWER_TABLE_SIZE 128 -#define LCNPHY_MAX_TX_POWER_INDEX (LCNPHY_TX_POWER_TABLE_SIZE - 1) -#define LCNPHY_TBL_ID_TXPWRCTL 0x07 -#define LCNPHY_TX_PWR_CTRL_OFF 0 -#define LCNPHY_TX_PWR_CTRL_SW (0x1 << 15) -#define LCNPHY_TX_PWR_CTRL_HW ((0x1 << 15) | \ - (0x1 << 14) | \ - (0x1 << 13)) - -#define LCNPHY_TX_PWR_CTRL_TEMPBASED 0xE001 - -void wlc_lcnphy_write_table(struct brcms_phy *pi, - const struct phytbl_info *pti); -void wlc_lcnphy_read_table(struct brcms_phy *pi, struct phytbl_info *pti); -void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b); -void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq); -void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b); -u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi); -void wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, u8 *ei0, u8 *eq0, u8 *fi0, - u8 *fq0); -void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode); -void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode); -bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi); -void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi); -s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1); -void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, s8 *cck_pwr); -void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi); - -s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index); - -#define NPHY_MAX_HPVGA1_INDEX 10 -#define NPHY_DEF_HPVGA1_INDEXLIMIT 7 - -struct phy_iq_est { - s32 iq_prod; - u32 i_pwr; - u32 q_pwr; -}; - -void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, bool enable); -void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode); - -#define wlc_phy_write_table_nphy(pi, pti) \ - wlc_phy_write_table(pi, pti, 0x72, 0x74, 0x73) - -#define wlc_phy_read_table_nphy(pi, pti) \ - wlc_phy_read_table(pi, pti, 0x72, 0x74, 0x73) - -#define wlc_nphy_table_addr(pi, id, off) \ - wlc_phy_table_addr((pi), (id), (off), 0x72, 0x74, 0x73) - -#define wlc_nphy_table_data_write(pi, w, v) \ - wlc_phy_table_data_write((pi), (w), (v)) - -void wlc_phy_table_read_nphy(struct brcms_phy *pi, u32, u32 l, u32 o, u32 w, - void *d); -void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32, u32, - const void *); - -#define PHY_IPA(pi) \ - ((pi->ipa2g_on && CHSPEC_IS2G(pi->radio_chanspec)) || \ - (pi->ipa5g_on && CHSPEC_IS5G(pi->radio_chanspec))) - -#define BRCMS_PHY_WAR_PR51571(pi) \ - if (NREV_LT((pi)->pubpi.phy_rev, 3)) \ - (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) - -void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype); -void wlc_phy_aci_reset_nphy(struct brcms_phy *pi); -void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en); - -u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint chan); -void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on); - -void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi); - -void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd); -s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi); - -u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val); - -void wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est, - u16 num_samps, u8 wait_time, u8 wait_for_crs); - -void wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write, - struct nphy_iq_comp *comp); -void wlc_phy_aci_and_noise_reduction_nphy(struct brcms_phy *pi); - -void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, u8 rxcore_bitmask); -u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih); - -void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type); -void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi); -void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi); -void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi); -u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi); - -struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi); -int wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, - struct nphy_txgains target_gain, bool full, bool m); -int wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, - u8 type, bool d); -void wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask, - s8 txpwrindex, bool res); -void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core, u8 rssi_type); -int wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type, - s32 *rssi_buf, u8 nsamps); -void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi); -int wlc_phy_aci_scan_nphy(struct brcms_phy *pi); -void wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, s32 dBm_targetpower, - bool debug); -int wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, u8 mode, - u8, bool); -void wlc_phy_stopplayback_nphy(struct brcms_phy *pi); -void wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf, - u8 num_samps); -void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi); - -int wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, struct d11rxhdr *rxh); - -#define NPHY_TESTPATTERN_BPHY_EVM 0 -#define NPHY_TESTPATTERN_BPHY_RFCS 1 - -void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs); - -void wlc_phy_get_pwrdet_offsets(struct brcms_phy *pi, s8 *cckoffset, - s8 *ofdmoffset); -s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, u16 chanspec); - -bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pih); -#endif /* _BRCM_PHY_INT_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c deleted file mode 100644 index 93d4cde0eb31..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c +++ /dev/null @@ -1,5247 +0,0 @@ -/* - * Copyright (c) 2010 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 -#include -#include -#include "phy_qmath.h" -#include "phy_hal.h" -#include "phy_radio.h" -#include "phytbl_lcn.h" -#include "phy_lcn.h" - -#define PLL_2064_NDIV 90 -#define PLL_2064_LOW_END_VCO 3000 -#define PLL_2064_LOW_END_KVCO 27 -#define PLL_2064_HIGH_END_VCO 4200 -#define PLL_2064_HIGH_END_KVCO 68 -#define PLL_2064_LOOP_BW_DOUBLER 200 -#define PLL_2064_D30_DOUBLER 10500 -#define PLL_2064_LOOP_BW 260 -#define PLL_2064_D30 8000 -#define PLL_2064_CAL_REF_TO 8 -#define PLL_2064_MHZ 1000000 -#define PLL_2064_OPEN_LOOP_DELAY 5 - -#define TEMPSENSE 1 -#define VBATSENSE 2 - -#define NOISE_IF_UPD_CHK_INTERVAL 1 -#define NOISE_IF_UPD_RST_INTERVAL 60 -#define NOISE_IF_UPD_THRESHOLD_CNT 1 -#define NOISE_IF_UPD_TRHRESHOLD 50 -#define NOISE_IF_UPD_TIMEOUT 1000 -#define NOISE_IF_OFF 0 -#define NOISE_IF_CHK 1 -#define NOISE_IF_ON 2 - -#define PAPD_BLANKING_PROFILE 3 -#define PAPD2LUT 0 -#define PAPD_CORR_NORM 0 -#define PAPD_BLANKING_THRESHOLD 0 -#define PAPD_STOP_AFTER_LAST_UPDATE 0 - -#define LCN_TARGET_PWR 60 - -#define LCN_VBAT_OFFSET_433X 34649679 -#define LCN_VBAT_SLOPE_433X 8258032 - -#define LCN_VBAT_SCALE_NOM 53 -#define LCN_VBAT_SCALE_DEN 432 - -#define LCN_TEMPSENSE_OFFSET 80812 -#define LCN_TEMPSENSE_DEN 2647 - -#define LCN_BW_LMT 200 -#define LCN_CUR_LMT 1250 -#define LCN_MULT 1 -#define LCN_VCO_DIV 30 -#define LCN_OFFSET 680 -#define LCN_FACT 490 -#define LCN_CUR_DIV 2640 - -#define LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT \ - (0 + 8) -#define LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK \ - (0x7f << LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT) - -#define LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_SHIFT \ - (0 + 8) -#define LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_MASK \ - (0x7f << LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_SHIFT) - -#define wlc_lcnphy_enable_tx_gain_override(pi) \ - wlc_lcnphy_set_tx_gain_override(pi, true) -#define wlc_lcnphy_disable_tx_gain_override(pi) \ - wlc_lcnphy_set_tx_gain_override(pi, false) - -#define wlc_lcnphy_iqcal_active(pi) \ - (read_phy_reg((pi), 0x451) & \ - ((0x1 << 15) | (0x1 << 14))) - -#define txpwrctrl_off(pi) (0x7 != ((read_phy_reg(pi, 0x4a4) & 0xE000) >> 13)) -#define wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) \ - (pi->temppwrctrl_capable) -#define wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi) \ - (pi->hwpwrctrl_capable) - -#define SWCTRL_BT_TX 0x18 -#define SWCTRL_OVR_DISABLE 0x40 - -#define AFE_CLK_INIT_MODE_TXRX2X 1 -#define AFE_CLK_INIT_MODE_PAPD 0 - -#define LCNPHY_TBL_ID_IQLOCAL 0x00 - -#define LCNPHY_TBL_ID_RFSEQ 0x08 -#define LCNPHY_TBL_ID_GAIN_IDX 0x0d -#define LCNPHY_TBL_ID_SW_CTRL 0x0f -#define LCNPHY_TBL_ID_GAIN_TBL 0x12 -#define LCNPHY_TBL_ID_SPUR 0x14 -#define LCNPHY_TBL_ID_SAMPLEPLAY 0x15 -#define LCNPHY_TBL_ID_SAMPLEPLAY1 0x16 - -#define LCNPHY_TX_PWR_CTRL_RATE_OFFSET 832 -#define LCNPHY_TX_PWR_CTRL_MAC_OFFSET 128 -#define LCNPHY_TX_PWR_CTRL_GAIN_OFFSET 192 -#define LCNPHY_TX_PWR_CTRL_IQ_OFFSET 320 -#define LCNPHY_TX_PWR_CTRL_LO_OFFSET 448 -#define LCNPHY_TX_PWR_CTRL_PWR_OFFSET 576 - -#define LCNPHY_TX_PWR_CTRL_START_INDEX_2G_4313 140 - -#define LCNPHY_TX_PWR_CTRL_START_NPT 1 -#define LCNPHY_TX_PWR_CTRL_MAX_NPT 7 - -#define LCNPHY_NOISE_SAMPLES_DEFAULT 5000 - -#define LCNPHY_ACI_DETECT_START 1 -#define LCNPHY_ACI_DETECT_PROGRESS 2 -#define LCNPHY_ACI_DETECT_STOP 3 - -#define LCNPHY_ACI_CRSHIFRMLO_TRSH 100 -#define LCNPHY_ACI_GLITCH_TRSH 2000 -#define LCNPHY_ACI_TMOUT 250 -#define LCNPHY_ACI_DETECT_TIMEOUT 2 -#define LCNPHY_ACI_START_DELAY 0 - -#define wlc_lcnphy_tx_gain_override_enabled(pi) \ - (0 != (read_phy_reg((pi), 0x43b) & (0x1 << 6))) - -#define wlc_lcnphy_total_tx_frames(pi) \ - wlapi_bmac_read_shm((pi)->sh->physhim, M_UCODE_MACSTAT + \ - offsetof(struct macstat, txallfrm)) - -struct lcnphy_txgains { - u16 gm_gain; - u16 pga_gain; - u16 pad_gain; - u16 dac_gain; -}; - -enum lcnphy_cal_mode { - LCNPHY_CAL_FULL, - LCNPHY_CAL_RECAL, - LCNPHY_CAL_CURRECAL, - LCNPHY_CAL_DIGCAL, - LCNPHY_CAL_GCTRL -}; - -struct lcnphy_rx_iqcomp { - u8 chan; - s16 a; - s16 b; -}; - -struct lcnphy_spb_tone { - s16 re; - s16 im; -}; - -struct lcnphy_unsign16_struct { - u16 re; - u16 im; -}; - -struct lcnphy_iq_est { - u32 iq_prod; - u32 i_pwr; - u32 q_pwr; -}; - -struct lcnphy_sfo_cfg { - u16 ptcentreTs20; - u16 ptcentreFactor; -}; - -enum lcnphy_papd_cal_type { - LCNPHY_PAPD_CAL_CW, - LCNPHY_PAPD_CAL_OFDM -}; - -typedef u16 iqcal_gain_params_lcnphy[9]; - -static const iqcal_gain_params_lcnphy tbl_iqcal_gainparams_lcnphy_2G[] = { - {0, 0, 0, 0, 0, 0, 0, 0, 0}, -}; - -static const iqcal_gain_params_lcnphy *tbl_iqcal_gainparams_lcnphy[1] = { - tbl_iqcal_gainparams_lcnphy_2G, -}; - -static const u16 iqcal_gainparams_numgains_lcnphy[1] = { - ARRAY_SIZE(tbl_iqcal_gainparams_lcnphy_2G), -}; - -static const struct lcnphy_sfo_cfg lcnphy_sfo_cfg[] = { - {965, 1087}, - {967, 1085}, - {969, 1082}, - {971, 1080}, - {973, 1078}, - {975, 1076}, - {977, 1073}, - {979, 1071}, - {981, 1069}, - {983, 1067}, - {985, 1065}, - {987, 1063}, - {989, 1060}, - {994, 1055} -}; - -static const -u16 lcnphy_iqcal_loft_gainladder[] = { - ((2 << 8) | 0), - ((3 << 8) | 0), - ((4 << 8) | 0), - ((6 << 8) | 0), - ((8 << 8) | 0), - ((11 << 8) | 0), - ((16 << 8) | 0), - ((16 << 8) | 1), - ((16 << 8) | 2), - ((16 << 8) | 3), - ((16 << 8) | 4), - ((16 << 8) | 5), - ((16 << 8) | 6), - ((16 << 8) | 7), - ((23 << 8) | 7), - ((32 << 8) | 7), - ((45 << 8) | 7), - ((64 << 8) | 7), - ((91 << 8) | 7), - ((128 << 8) | 7) -}; - -static const -u16 lcnphy_iqcal_ir_gainladder[] = { - ((1 << 8) | 0), - ((2 << 8) | 0), - ((4 << 8) | 0), - ((6 << 8) | 0), - ((8 << 8) | 0), - ((11 << 8) | 0), - ((16 << 8) | 0), - ((23 << 8) | 0), - ((32 << 8) | 0), - ((45 << 8) | 0), - ((64 << 8) | 0), - ((64 << 8) | 1), - ((64 << 8) | 2), - ((64 << 8) | 3), - ((64 << 8) | 4), - ((64 << 8) | 5), - ((64 << 8) | 6), - ((64 << 8) | 7), - ((91 << 8) | 7), - ((128 << 8) | 7) -}; - -static const -struct lcnphy_spb_tone lcnphy_spb_tone_3750[] = { - {88, 0}, - {73, 49}, - {34, 81}, - {-17, 86}, - {-62, 62}, - {-86, 17}, - {-81, -34}, - {-49, -73}, - {0, -88}, - {49, -73}, - {81, -34}, - {86, 17}, - {62, 62}, - {17, 86}, - {-34, 81}, - {-73, 49}, - {-88, 0}, - {-73, -49}, - {-34, -81}, - {17, -86}, - {62, -62}, - {86, -17}, - {81, 34}, - {49, 73}, - {0, 88}, - {-49, 73}, - {-81, 34}, - {-86, -17}, - {-62, -62}, - {-17, -86}, - {34, -81}, - {73, -49}, -}; - -static const -u16 iqlo_loopback_rf_regs[20] = { - RADIO_2064_REG036, - RADIO_2064_REG11A, - RADIO_2064_REG03A, - RADIO_2064_REG025, - RADIO_2064_REG028, - RADIO_2064_REG005, - RADIO_2064_REG112, - RADIO_2064_REG0FF, - RADIO_2064_REG11F, - RADIO_2064_REG00B, - RADIO_2064_REG113, - RADIO_2064_REG007, - RADIO_2064_REG0FC, - RADIO_2064_REG0FD, - RADIO_2064_REG012, - RADIO_2064_REG057, - RADIO_2064_REG059, - RADIO_2064_REG05C, - RADIO_2064_REG078, - RADIO_2064_REG092, -}; - -static const -u16 tempsense_phy_regs[14] = { - 0x503, - 0x4a4, - 0x4d0, - 0x4d9, - 0x4da, - 0x4a6, - 0x938, - 0x939, - 0x4d8, - 0x4d0, - 0x4d7, - 0x4a5, - 0x40d, - 0x4a2, -}; - -static const -u16 rxiq_cal_rf_reg[11] = { - RADIO_2064_REG098, - RADIO_2064_REG116, - RADIO_2064_REG12C, - RADIO_2064_REG06A, - RADIO_2064_REG00B, - RADIO_2064_REG01B, - RADIO_2064_REG113, - RADIO_2064_REG01D, - RADIO_2064_REG114, - RADIO_2064_REG02E, - RADIO_2064_REG12A, -}; - -static const -struct lcnphy_rx_iqcomp lcnphy_rx_iqcomp_table_rev0[] = { - {1, 0, 0}, - {2, 0, 0}, - {3, 0, 0}, - {4, 0, 0}, - {5, 0, 0}, - {6, 0, 0}, - {7, 0, 0}, - {8, 0, 0}, - {9, 0, 0}, - {10, 0, 0}, - {11, 0, 0}, - {12, 0, 0}, - {13, 0, 0}, - {14, 0, 0}, - {34, 0, 0}, - {38, 0, 0}, - {42, 0, 0}, - {46, 0, 0}, - {36, 0, 0}, - {40, 0, 0}, - {44, 0, 0}, - {48, 0, 0}, - {52, 0, 0}, - {56, 0, 0}, - {60, 0, 0}, - {64, 0, 0}, - {100, 0, 0}, - {104, 0, 0}, - {108, 0, 0}, - {112, 0, 0}, - {116, 0, 0}, - {120, 0, 0}, - {124, 0, 0}, - {128, 0, 0}, - {132, 0, 0}, - {136, 0, 0}, - {140, 0, 0}, - {149, 0, 0}, - {153, 0, 0}, - {157, 0, 0}, - {161, 0, 0}, - {165, 0, 0}, - {184, 0, 0}, - {188, 0, 0}, - {192, 0, 0}, - {196, 0, 0}, - {200, 0, 0}, - {204, 0, 0}, - {208, 0, 0}, - {212, 0, 0}, - {216, 0, 0}, -}; - -static const u32 lcnphy_23bitgaincode_table[] = { - 0x200100, - 0x200200, - 0x200004, - 0x200014, - 0x200024, - 0x200034, - 0x200134, - 0x200234, - 0x200334, - 0x200434, - 0x200037, - 0x200137, - 0x200237, - 0x200337, - 0x200437, - 0x000035, - 0x000135, - 0x000235, - 0x000037, - 0x000137, - 0x000237, - 0x000337, - 0x00013f, - 0x00023f, - 0x00033f, - 0x00034f, - 0x00044f, - 0x00144f, - 0x00244f, - 0x00254f, - 0x00354f, - 0x00454f, - 0x00464f, - 0x01464f, - 0x02464f, - 0x03464f, - 0x04464f, -}; - -static const s8 lcnphy_gain_table[] = { - -16, - -13, - 10, - 7, - 4, - 0, - 3, - 6, - 9, - 12, - 15, - 18, - 21, - 24, - 27, - 30, - 33, - 36, - 39, - 42, - 45, - 48, - 50, - 53, - 56, - 59, - 62, - 65, - 68, - 71, - 74, - 77, - 80, - 83, - 86, - 89, - 92, -}; - -static const s8 lcnphy_gain_index_offset_for_rssi[] = { - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 8, - 7, - 7, - 6, - 7, - 7, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 3, - 3, - 3, - 3, - 3, - 3, - 4, - 2, - 2, - 2, - 2, - 2, - 2, - -1, - -2, - -2, - -2 -}; - -struct chan_info_2064_lcnphy { - uint chan; - uint freq; - u8 logen_buftune; - u8 logen_rccr_tx; - u8 txrf_mix_tune_ctrl; - u8 pa_input_tune_g; - u8 logen_rccr_rx; - u8 pa_rxrf_lna1_freq_tune; - u8 pa_rxrf_lna2_freq_tune; - u8 rxrf_rxrf_spare1; -}; - -static const struct chan_info_2064_lcnphy chan_info_2064_lcnphy[] = { - {1, 2412, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {2, 2417, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {3, 2422, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {4, 2427, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {5, 2432, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {6, 2437, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {7, 2442, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {8, 2447, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {9, 2452, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {10, 2457, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {11, 2462, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {12, 2467, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {13, 2472, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, - {14, 2484, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, -}; - -static const struct lcnphy_radio_regs lcnphy_radio_regs_2064[] = { - {0x00, 0, 0, 0, 0}, - {0x01, 0x64, 0x64, 0, 0}, - {0x02, 0x20, 0x20, 0, 0}, - {0x03, 0x66, 0x66, 0, 0}, - {0x04, 0xf8, 0xf8, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0x10, 0x10, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0x37, 0x37, 0, 0}, - {0x0B, 0x6, 0x6, 0, 0}, - {0x0C, 0x55, 0x55, 0, 0}, - {0x0D, 0x8b, 0x8b, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0x5, 0x5, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0xe, 0xe, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0xb, 0xb, 0, 0}, - {0x14, 0x2, 0x2, 0, 0}, - {0x15, 0x12, 0x12, 0, 0}, - {0x16, 0x12, 0x12, 0, 0}, - {0x17, 0xc, 0xc, 0, 0}, - {0x18, 0xc, 0xc, 0, 0}, - {0x19, 0xc, 0xc, 0, 0}, - {0x1A, 0x8, 0x8, 0, 0}, - {0x1B, 0x2, 0x2, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0x1, 0x1, 0, 0}, - {0x1E, 0x12, 0x12, 0, 0}, - {0x1F, 0x6e, 0x6e, 0, 0}, - {0x20, 0x2, 0x2, 0, 0}, - {0x21, 0x23, 0x23, 0, 0}, - {0x22, 0x8, 0x8, 0, 0}, - {0x23, 0, 0, 0, 0}, - {0x24, 0, 0, 0, 0}, - {0x25, 0xc, 0xc, 0, 0}, - {0x26, 0x33, 0x33, 0, 0}, - {0x27, 0x55, 0x55, 0, 0}, - {0x28, 0, 0, 0, 0}, - {0x29, 0x30, 0x30, 0, 0}, - {0x2A, 0xb, 0xb, 0, 0}, - {0x2B, 0x1b, 0x1b, 0, 0}, - {0x2C, 0x3, 0x3, 0, 0}, - {0x2D, 0x1b, 0x1b, 0, 0}, - {0x2E, 0, 0, 0, 0}, - {0x2F, 0x20, 0x20, 0, 0}, - {0x30, 0xa, 0xa, 0, 0}, - {0x31, 0, 0, 0, 0}, - {0x32, 0x62, 0x62, 0, 0}, - {0x33, 0x19, 0x19, 0, 0}, - {0x34, 0x33, 0x33, 0, 0}, - {0x35, 0x77, 0x77, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0x70, 0x70, 0, 0}, - {0x38, 0x3, 0x3, 0, 0}, - {0x39, 0xf, 0xf, 0, 0}, - {0x3A, 0x6, 0x6, 0, 0}, - {0x3B, 0xcf, 0xcf, 0, 0}, - {0x3C, 0x1a, 0x1a, 0, 0}, - {0x3D, 0x6, 0x6, 0, 0}, - {0x3E, 0x42, 0x42, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0xfb, 0xfb, 0, 0}, - {0x41, 0x9a, 0x9a, 0, 0}, - {0x42, 0x7a, 0x7a, 0, 0}, - {0x43, 0x29, 0x29, 0, 0}, - {0x44, 0, 0, 0, 0}, - {0x45, 0x8, 0x8, 0, 0}, - {0x46, 0xce, 0xce, 0, 0}, - {0x47, 0x27, 0x27, 0, 0}, - {0x48, 0x62, 0x62, 0, 0}, - {0x49, 0x6, 0x6, 0, 0}, - {0x4A, 0x58, 0x58, 0, 0}, - {0x4B, 0xf7, 0xf7, 0, 0}, - {0x4C, 0, 0, 0, 0}, - {0x4D, 0xb3, 0xb3, 0, 0}, - {0x4E, 0, 0, 0, 0}, - {0x4F, 0x2, 0x2, 0, 0}, - {0x50, 0, 0, 0, 0}, - {0x51, 0x9, 0x9, 0, 0}, - {0x52, 0x5, 0x5, 0, 0}, - {0x53, 0x17, 0x17, 0, 0}, - {0x54, 0x38, 0x38, 0, 0}, - {0x55, 0, 0, 0, 0}, - {0x56, 0, 0, 0, 0}, - {0x57, 0xb, 0xb, 0, 0}, - {0x58, 0, 0, 0, 0}, - {0x59, 0, 0, 0, 0}, - {0x5A, 0, 0, 0, 0}, - {0x5B, 0, 0, 0, 0}, - {0x5C, 0, 0, 0, 0}, - {0x5D, 0, 0, 0, 0}, - {0x5E, 0x88, 0x88, 0, 0}, - {0x5F, 0xcc, 0xcc, 0, 0}, - {0x60, 0x74, 0x74, 0, 0}, - {0x61, 0x74, 0x74, 0, 0}, - {0x62, 0x74, 0x74, 0, 0}, - {0x63, 0x44, 0x44, 0, 0}, - {0x64, 0x77, 0x77, 0, 0}, - {0x65, 0x44, 0x44, 0, 0}, - {0x66, 0x77, 0x77, 0, 0}, - {0x67, 0x55, 0x55, 0, 0}, - {0x68, 0x77, 0x77, 0, 0}, - {0x69, 0x77, 0x77, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0x7f, 0x7f, 0, 0}, - {0x6C, 0x8, 0x8, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0x88, 0x88, 0, 0}, - {0x6F, 0x66, 0x66, 0, 0}, - {0x70, 0x66, 0x66, 0, 0}, - {0x71, 0x28, 0x28, 0, 0}, - {0x72, 0x55, 0x55, 0, 0}, - {0x73, 0x4, 0x4, 0, 0}, - {0x74, 0, 0, 0, 0}, - {0x75, 0, 0, 0, 0}, - {0x76, 0, 0, 0, 0}, - {0x77, 0x1, 0x1, 0, 0}, - {0x78, 0xd6, 0xd6, 0, 0}, - {0x79, 0, 0, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0xb4, 0xb4, 0, 0}, - {0x84, 0x1, 0x1, 0, 0}, - {0x85, 0x20, 0x20, 0, 0}, - {0x86, 0x5, 0x5, 0, 0}, - {0x87, 0xff, 0xff, 0, 0}, - {0x88, 0x7, 0x7, 0, 0}, - {0x89, 0x77, 0x77, 0, 0}, - {0x8A, 0x77, 0x77, 0, 0}, - {0x8B, 0x77, 0x77, 0, 0}, - {0x8C, 0x77, 0x77, 0, 0}, - {0x8D, 0x8, 0x8, 0, 0}, - {0x8E, 0xa, 0xa, 0, 0}, - {0x8F, 0x8, 0x8, 0, 0}, - {0x90, 0x18, 0x18, 0, 0}, - {0x91, 0x5, 0x5, 0, 0}, - {0x92, 0x1f, 0x1f, 0, 0}, - {0x93, 0x10, 0x10, 0, 0}, - {0x94, 0x3, 0x3, 0, 0}, - {0x95, 0, 0, 0, 0}, - {0x96, 0, 0, 0, 0}, - {0x97, 0xaa, 0xaa, 0, 0}, - {0x98, 0, 0, 0, 0}, - {0x99, 0x23, 0x23, 0, 0}, - {0x9A, 0x7, 0x7, 0, 0}, - {0x9B, 0xf, 0xf, 0, 0}, - {0x9C, 0x10, 0x10, 0, 0}, - {0x9D, 0x3, 0x3, 0, 0}, - {0x9E, 0x4, 0x4, 0, 0}, - {0x9F, 0x20, 0x20, 0, 0}, - {0xA0, 0, 0, 0, 0}, - {0xA1, 0, 0, 0, 0}, - {0xA2, 0, 0, 0, 0}, - {0xA3, 0, 0, 0, 0}, - {0xA4, 0x1, 0x1, 0, 0}, - {0xA5, 0x77, 0x77, 0, 0}, - {0xA6, 0x77, 0x77, 0, 0}, - {0xA7, 0x77, 0x77, 0, 0}, - {0xA8, 0x77, 0x77, 0, 0}, - {0xA9, 0x8c, 0x8c, 0, 0}, - {0xAA, 0x88, 0x88, 0, 0}, - {0xAB, 0x78, 0x78, 0, 0}, - {0xAC, 0x57, 0x57, 0, 0}, - {0xAD, 0x88, 0x88, 0, 0}, - {0xAE, 0, 0, 0, 0}, - {0xAF, 0x8, 0x8, 0, 0}, - {0xB0, 0x88, 0x88, 0, 0}, - {0xB1, 0, 0, 0, 0}, - {0xB2, 0x1b, 0x1b, 0, 0}, - {0xB3, 0x3, 0x3, 0, 0}, - {0xB4, 0x24, 0x24, 0, 0}, - {0xB5, 0x3, 0x3, 0, 0}, - {0xB6, 0x1b, 0x1b, 0, 0}, - {0xB7, 0x24, 0x24, 0, 0}, - {0xB8, 0x3, 0x3, 0, 0}, - {0xB9, 0, 0, 0, 0}, - {0xBA, 0xaa, 0xaa, 0, 0}, - {0xBB, 0, 0, 0, 0}, - {0xBC, 0x4, 0x4, 0, 0}, - {0xBD, 0, 0, 0, 0}, - {0xBE, 0x8, 0x8, 0, 0}, - {0xBF, 0x11, 0x11, 0, 0}, - {0xC0, 0, 0, 0, 0}, - {0xC1, 0, 0, 0, 0}, - {0xC2, 0x62, 0x62, 0, 0}, - {0xC3, 0x1e, 0x1e, 0, 0}, - {0xC4, 0x33, 0x33, 0, 0}, - {0xC5, 0x37, 0x37, 0, 0}, - {0xC6, 0, 0, 0, 0}, - {0xC7, 0x70, 0x70, 0, 0}, - {0xC8, 0x1e, 0x1e, 0, 0}, - {0xC9, 0x6, 0x6, 0, 0}, - {0xCA, 0x4, 0x4, 0, 0}, - {0xCB, 0x2f, 0x2f, 0, 0}, - {0xCC, 0xf, 0xf, 0, 0}, - {0xCD, 0, 0, 0, 0}, - {0xCE, 0xff, 0xff, 0, 0}, - {0xCF, 0x8, 0x8, 0, 0}, - {0xD0, 0x3f, 0x3f, 0, 0}, - {0xD1, 0x3f, 0x3f, 0, 0}, - {0xD2, 0x3f, 0x3f, 0, 0}, - {0xD3, 0, 0, 0, 0}, - {0xD4, 0, 0, 0, 0}, - {0xD5, 0, 0, 0, 0}, - {0xD6, 0xcc, 0xcc, 0, 0}, - {0xD7, 0, 0, 0, 0}, - {0xD8, 0x8, 0x8, 0, 0}, - {0xD9, 0x8, 0x8, 0, 0}, - {0xDA, 0x8, 0x8, 0, 0}, - {0xDB, 0x11, 0x11, 0, 0}, - {0xDC, 0, 0, 0, 0}, - {0xDD, 0x87, 0x87, 0, 0}, - {0xDE, 0x88, 0x88, 0, 0}, - {0xDF, 0x8, 0x8, 0, 0}, - {0xE0, 0x8, 0x8, 0, 0}, - {0xE1, 0x8, 0x8, 0, 0}, - {0xE2, 0, 0, 0, 0}, - {0xE3, 0, 0, 0, 0}, - {0xE4, 0, 0, 0, 0}, - {0xE5, 0xf5, 0xf5, 0, 0}, - {0xE6, 0x30, 0x30, 0, 0}, - {0xE7, 0x1, 0x1, 0, 0}, - {0xE8, 0, 0, 0, 0}, - {0xE9, 0xff, 0xff, 0, 0}, - {0xEA, 0, 0, 0, 0}, - {0xEB, 0, 0, 0, 0}, - {0xEC, 0x22, 0x22, 0, 0}, - {0xED, 0, 0, 0, 0}, - {0xEE, 0, 0, 0, 0}, - {0xEF, 0, 0, 0, 0}, - {0xF0, 0x3, 0x3, 0, 0}, - {0xF1, 0x1, 0x1, 0, 0}, - {0xF2, 0, 0, 0, 0}, - {0xF3, 0, 0, 0, 0}, - {0xF4, 0, 0, 0, 0}, - {0xF5, 0, 0, 0, 0}, - {0xF6, 0, 0, 0, 0}, - {0xF7, 0x6, 0x6, 0, 0}, - {0xF8, 0, 0, 0, 0}, - {0xF9, 0, 0, 0, 0}, - {0xFA, 0x40, 0x40, 0, 0}, - {0xFB, 0, 0, 0, 0}, - {0xFC, 0x1, 0x1, 0, 0}, - {0xFD, 0x80, 0x80, 0, 0}, - {0xFE, 0x2, 0x2, 0, 0}, - {0xFF, 0x10, 0x10, 0, 0}, - {0x100, 0x2, 0x2, 0, 0}, - {0x101, 0x1e, 0x1e, 0, 0}, - {0x102, 0x1e, 0x1e, 0, 0}, - {0x103, 0, 0, 0, 0}, - {0x104, 0x1f, 0x1f, 0, 0}, - {0x105, 0, 0x8, 0, 1}, - {0x106, 0x2a, 0x2a, 0, 0}, - {0x107, 0xf, 0xf, 0, 0}, - {0x108, 0, 0, 0, 0}, - {0x109, 0, 0, 0, 0}, - {0x10A, 0, 0, 0, 0}, - {0x10B, 0, 0, 0, 0}, - {0x10C, 0, 0, 0, 0}, - {0x10D, 0, 0, 0, 0}, - {0x10E, 0, 0, 0, 0}, - {0x10F, 0, 0, 0, 0}, - {0x110, 0, 0, 0, 0}, - {0x111, 0, 0, 0, 0}, - {0x112, 0, 0, 0, 0}, - {0x113, 0, 0, 0, 0}, - {0x114, 0, 0, 0, 0}, - {0x115, 0, 0, 0, 0}, - {0x116, 0, 0, 0, 0}, - {0x117, 0, 0, 0, 0}, - {0x118, 0, 0, 0, 0}, - {0x119, 0, 0, 0, 0}, - {0x11A, 0, 0, 0, 0}, - {0x11B, 0, 0, 0, 0}, - {0x11C, 0x1, 0x1, 0, 0}, - {0x11D, 0, 0, 0, 0}, - {0x11E, 0, 0, 0, 0}, - {0x11F, 0, 0, 0, 0}, - {0x120, 0, 0, 0, 0}, - {0x121, 0, 0, 0, 0}, - {0x122, 0x80, 0x80, 0, 0}, - {0x123, 0, 0, 0, 0}, - {0x124, 0xf8, 0xf8, 0, 0}, - {0x125, 0, 0, 0, 0}, - {0x126, 0, 0, 0, 0}, - {0x127, 0, 0, 0, 0}, - {0x128, 0, 0, 0, 0}, - {0x129, 0, 0, 0, 0}, - {0x12A, 0, 0, 0, 0}, - {0x12B, 0, 0, 0, 0}, - {0x12C, 0, 0, 0, 0}, - {0x12D, 0, 0, 0, 0}, - {0x12E, 0, 0, 0, 0}, - {0x12F, 0, 0, 0, 0}, - {0x130, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -#define LCNPHY_NUM_DIG_FILT_COEFFS 16 -#define LCNPHY_NUM_TX_DIG_FILTERS_CCK 13 - -static const u16 LCNPHY_txdigfiltcoeffs_cck[LCNPHY_NUM_TX_DIG_FILTERS_CCK] - [LCNPHY_NUM_DIG_FILT_COEFFS + 1] = { - {0, 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778, 1582, 64, - 128, 64,}, - {1, 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608, 1863, 93, - 167, 93,}, - {2, 1, 415, 1874, 64, 128, 64, 792, 1656, 192, 384, 192, 778, 1582, 64, - 128, 64,}, - {3, 1, 302, 1841, 129, 258, 129, 658, 1720, 205, 410, 205, 754, 1760, - 170, 340, 170,}, - {20, 1, 360, 1884, 242, 1734, 242, 752, 1720, 205, 1845, 205, 767, 1760, - 256, 185, 256,}, - {21, 1, 360, 1884, 149, 1874, 149, 752, 1720, 205, 1883, 205, 767, 1760, - 256, 273, 256,}, - {22, 1, 360, 1884, 98, 1948, 98, 752, 1720, 205, 1924, 205, 767, 1760, - 256, 352, 256,}, - {23, 1, 350, 1884, 116, 1966, 116, 752, 1720, 205, 2008, 205, 767, 1760, - 128, 233, 128,}, - {24, 1, 325, 1884, 32, 40, 32, 756, 1720, 256, 471, 256, 766, 1760, 256, - 1881, 256,}, - {25, 1, 299, 1884, 51, 64, 51, 736, 1720, 256, 471, 256, 765, 1760, 256, - 1881, 256,}, - {26, 1, 277, 1943, 39, 117, 88, 637, 1838, 64, 192, 144, 614, 1864, 128, - 384, 288,}, - {27, 1, 245, 1943, 49, 147, 110, 626, 1838, 256, 768, 576, 613, 1864, - 128, 384, 288,}, - {30, 1, 302, 1841, 61, 122, 61, 658, 1720, 205, 410, 205, 754, 1760, - 170, 340, 170,}, -}; - -#define LCNPHY_NUM_TX_DIG_FILTERS_OFDM 3 -static const u16 LCNPHY_txdigfiltcoeffs_ofdm[LCNPHY_NUM_TX_DIG_FILTERS_OFDM] - [LCNPHY_NUM_DIG_FILT_COEFFS + 1] = { - {0, 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, - 0x278, 0xfea0, 0x80, 0x100, 0x80,}, - {1, 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50, - 750, 0xFE2B, 212, 0xFFCE, 212,}, - {2, 0, 375, 0xFF16, 37, 76, 37, 799, 0xFE74, 32, 20, 32, 748, - 0xFEF2, 128, 0xFFE2, 128} -}; - -#define wlc_lcnphy_set_start_tx_pwr_idx(pi, idx) \ - mod_phy_reg(pi, 0x4a4, \ - (0x1ff << 0), \ - (u16)(idx) << 0) - -#define wlc_lcnphy_set_tx_pwr_npt(pi, npt) \ - mod_phy_reg(pi, 0x4a5, \ - (0x7 << 8), \ - (u16)(npt) << 8) - -#define wlc_lcnphy_get_tx_pwr_ctrl(pi) \ - (read_phy_reg((pi), 0x4a4) & \ - ((0x1 << 15) | \ - (0x1 << 14) | \ - (0x1 << 13))) - -#define wlc_lcnphy_get_tx_pwr_npt(pi) \ - ((read_phy_reg(pi, 0x4a5) & \ - (0x7 << 8)) >> \ - 8) - -#define wlc_lcnphy_get_current_tx_pwr_idx_if_pwrctrl_on(pi) \ - (read_phy_reg(pi, 0x473) & 0x1ff) - -#define wlc_lcnphy_get_target_tx_pwr(pi) \ - ((read_phy_reg(pi, 0x4a7) & \ - (0xff << 0)) >> \ - 0) - -#define wlc_lcnphy_set_target_tx_pwr(pi, target) \ - mod_phy_reg(pi, 0x4a7, \ - (0xff << 0), \ - (u16)(target) << 0) - -#define wlc_radio_2064_rcal_done(pi) \ - (0 != (read_radio_reg(pi, RADIO_2064_REG05C) & 0x20)) - -#define tempsense_done(pi) \ - (0x8000 == (read_phy_reg(pi, 0x476) & 0x8000)) - -#define LCNPHY_IQLOCC_READ(val) \ - ((u8)(-(s8)(((val) & 0xf0) >> 4) + (s8)((val) & 0x0f))) - -#define FIXED_TXPWR 78 -#define LCNPHY_TEMPSENSE(val) ((s16)((val > 255) ? (val - 512) : val)) - -void wlc_lcnphy_write_table(struct brcms_phy *pi, const struct phytbl_info *pti) -{ - wlc_phy_write_table(pi, pti, 0x455, 0x457, 0x456); -} - -void wlc_lcnphy_read_table(struct brcms_phy *pi, struct phytbl_info *pti) -{ - wlc_phy_read_table(pi, pti, 0x455, 0x457, 0x456); -} - -static void -wlc_lcnphy_common_read_table(struct brcms_phy *pi, u32 tbl_id, - const u16 *tbl_ptr, u32 tbl_len, - u32 tbl_width, u32 tbl_offset) -{ - struct phytbl_info tab; - tab.tbl_id = tbl_id; - tab.tbl_ptr = tbl_ptr; - tab.tbl_len = tbl_len; - tab.tbl_width = tbl_width; - tab.tbl_offset = tbl_offset; - wlc_lcnphy_read_table(pi, &tab); -} - -static void -wlc_lcnphy_common_write_table(struct brcms_phy *pi, u32 tbl_id, - const u16 *tbl_ptr, u32 tbl_len, - u32 tbl_width, u32 tbl_offset) -{ - - struct phytbl_info tab; - tab.tbl_id = tbl_id; - tab.tbl_ptr = tbl_ptr; - tab.tbl_len = tbl_len; - tab.tbl_width = tbl_width; - tab.tbl_offset = tbl_offset; - wlc_lcnphy_write_table(pi, &tab); -} - -static u32 -wlc_lcnphy_qdiv_roundup(u32 dividend, u32 divisor, u8 precision) -{ - u32 quotient, remainder, roundup, rbit; - - quotient = dividend / divisor; - remainder = dividend % divisor; - rbit = divisor & 1; - roundup = (divisor >> 1) + rbit; - - while (precision--) { - quotient <<= 1; - if (remainder >= roundup) { - quotient++; - remainder = ((remainder - roundup) << 1) + rbit; - } else { - remainder <<= 1; - } - } - - if (remainder >= roundup) - quotient++; - - return quotient; -} - -static int wlc_lcnphy_calc_floor(s16 coeff_x, int type) -{ - int k; - k = 0; - if (type == 0) { - if (coeff_x < 0) - k = (coeff_x - 1) / 2; - else - k = coeff_x / 2; - } - - if (type == 1) { - if ((coeff_x + 1) < 0) - k = (coeff_x) / 2; - else - k = (coeff_x + 1) / 2; - } - return k; -} - -static void -wlc_lcnphy_get_tx_gain(struct brcms_phy *pi, struct lcnphy_txgains *gains) -{ - u16 dac_gain, rfgain0, rfgain1; - - dac_gain = read_phy_reg(pi, 0x439) >> 0; - gains->dac_gain = (dac_gain & 0x380) >> 7; - - rfgain0 = (read_phy_reg(pi, 0x4b5) & (0xffff << 0)) >> 0; - rfgain1 = (read_phy_reg(pi, 0x4fb) & (0x7fff << 0)) >> 0; - - gains->gm_gain = rfgain0 & 0xff; - gains->pga_gain = (rfgain0 >> 8) & 0xff; - gains->pad_gain = rfgain1 & 0xff; -} - - -static void wlc_lcnphy_set_dac_gain(struct brcms_phy *pi, u16 dac_gain) -{ - u16 dac_ctrl; - - dac_ctrl = (read_phy_reg(pi, 0x439) >> 0); - dac_ctrl = dac_ctrl & 0xc7f; - dac_ctrl = dac_ctrl | (dac_gain << 7); - mod_phy_reg(pi, 0x439, (0xfff << 0), (dac_ctrl) << 0); - -} - -static void wlc_lcnphy_set_tx_gain_override(struct brcms_phy *pi, bool bEnable) -{ - u16 bit = bEnable ? 1 : 0; - - mod_phy_reg(pi, 0x4b0, (0x1 << 7), bit << 7); - - mod_phy_reg(pi, 0x4b0, (0x1 << 14), bit << 14); - - mod_phy_reg(pi, 0x43b, (0x1 << 6), bit << 6); -} - -static void -wlc_lcnphy_rx_gain_override_enable(struct brcms_phy *pi, bool enable) -{ - u16 ebit = enable ? 1 : 0; - - mod_phy_reg(pi, 0x4b0, (0x1 << 8), ebit << 8); - - mod_phy_reg(pi, 0x44c, (0x1 << 0), ebit << 0); - - if (LCNREV_LT(pi->pubpi.phy_rev, 2)) { - mod_phy_reg(pi, 0x44c, (0x1 << 4), ebit << 4); - mod_phy_reg(pi, 0x44c, (0x1 << 6), ebit << 6); - mod_phy_reg(pi, 0x4b0, (0x1 << 5), ebit << 5); - mod_phy_reg(pi, 0x4b0, (0x1 << 6), ebit << 6); - } else { - mod_phy_reg(pi, 0x4b0, (0x1 << 12), ebit << 12); - mod_phy_reg(pi, 0x4b0, (0x1 << 13), ebit << 13); - mod_phy_reg(pi, 0x4b0, (0x1 << 5), ebit << 5); - } - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - mod_phy_reg(pi, 0x4b0, (0x1 << 10), ebit << 10); - mod_phy_reg(pi, 0x4e5, (0x1 << 3), ebit << 3); - } -} - -static void -wlc_lcnphy_set_rx_gain_by_distribution(struct brcms_phy *pi, - u16 trsw, - u16 ext_lna, - u16 biq2, - u16 biq1, - u16 tia, u16 lna2, u16 lna1) -{ - u16 gain0_15, gain16_19; - - gain16_19 = biq2 & 0xf; - gain0_15 = ((biq1 & 0xf) << 12) | - ((tia & 0xf) << 8) | - ((lna2 & 0x3) << 6) | - ((lna2 & 0x3) << 4) | - ((lna1 & 0x3) << 2) | - ((lna1 & 0x3) << 0); - - mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0); - mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0); - mod_phy_reg(pi, 0x4b1, (0x3 << 11), lna1 << 11); - - if (LCNREV_LT(pi->pubpi.phy_rev, 2)) { - mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9); - mod_phy_reg(pi, 0x4b1, (0x1 << 10), ext_lna << 10); - } else { - mod_phy_reg(pi, 0x4b1, (0x1 << 10), 0 << 10); - - mod_phy_reg(pi, 0x4b1, (0x1 << 15), 0 << 15); - - mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9); - } - - mod_phy_reg(pi, 0x44d, (0x1 << 0), (!trsw) << 0); - -} - -static void wlc_lcnphy_set_trsw_override(struct brcms_phy *pi, bool tx, bool rx) -{ - - mod_phy_reg(pi, 0x44d, - (0x1 << 1) | - (0x1 << 0), (tx ? (0x1 << 1) : 0) | (rx ? (0x1 << 0) : 0)); - - or_phy_reg(pi, 0x44c, (0x1 << 1) | (0x1 << 0)); -} - -static void wlc_lcnphy_clear_trsw_override(struct brcms_phy *pi) -{ - - and_phy_reg(pi, 0x44c, (u16) ~((0x1 << 1) | (0x1 << 0))); -} - -static void wlc_lcnphy_set_rx_iq_comp(struct brcms_phy *pi, u16 a, u16 b) -{ - mod_phy_reg(pi, 0x645, (0x3ff << 0), (a) << 0); - - mod_phy_reg(pi, 0x646, (0x3ff << 0), (b) << 0); - - mod_phy_reg(pi, 0x647, (0x3ff << 0), (a) << 0); - - mod_phy_reg(pi, 0x648, (0x3ff << 0), (b) << 0); - - mod_phy_reg(pi, 0x649, (0x3ff << 0), (a) << 0); - - mod_phy_reg(pi, 0x64a, (0x3ff << 0), (b) << 0); - -} - -static bool -wlc_lcnphy_rx_iq_est(struct brcms_phy *pi, - u16 num_samps, - u8 wait_time, struct lcnphy_iq_est *iq_est) -{ - int wait_count = 0; - bool result = true; - u8 phybw40; - phybw40 = CHSPEC_IS40(pi->radio_chanspec); - - mod_phy_reg(pi, 0x6da, (0x1 << 5), (1) << 5); - - mod_phy_reg(pi, 0x410, (0x1 << 3), (0) << 3); - - mod_phy_reg(pi, 0x482, (0xffff << 0), (num_samps) << 0); - - mod_phy_reg(pi, 0x481, (0xff << 0), ((u16) wait_time) << 0); - - mod_phy_reg(pi, 0x481, (0x1 << 8), (0) << 8); - - mod_phy_reg(pi, 0x481, (0x1 << 9), (1) << 9); - - while (read_phy_reg(pi, 0x481) & (0x1 << 9)) { - - if (wait_count > (10 * 500)) { - result = false; - goto cleanup; - } - udelay(100); - wait_count++; - } - - iq_est->iq_prod = ((u32) read_phy_reg(pi, 0x483) << 16) | - (u32) read_phy_reg(pi, 0x484); - iq_est->i_pwr = ((u32) read_phy_reg(pi, 0x485) << 16) | - (u32) read_phy_reg(pi, 0x486); - iq_est->q_pwr = ((u32) read_phy_reg(pi, 0x487) << 16) | - (u32) read_phy_reg(pi, 0x488); - -cleanup: - mod_phy_reg(pi, 0x410, (0x1 << 3), (1) << 3); - - mod_phy_reg(pi, 0x6da, (0x1 << 5), (0) << 5); - - return result; -} - -static bool wlc_lcnphy_calc_rx_iq_comp(struct brcms_phy *pi, u16 num_samps) -{ -#define LCNPHY_MIN_RXIQ_PWR 2 - bool result; - u16 a0_new, b0_new; - struct lcnphy_iq_est iq_est = { 0, 0, 0 }; - s32 a, b, temp; - s16 iq_nbits, qq_nbits, arsh, brsh; - s32 iq; - u32 ii, qq; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - a0_new = ((read_phy_reg(pi, 0x645) & (0x3ff << 0)) >> 0); - b0_new = ((read_phy_reg(pi, 0x646) & (0x3ff << 0)) >> 0); - mod_phy_reg(pi, 0x6d1, (0x1 << 2), (0) << 2); - - mod_phy_reg(pi, 0x64b, (0x1 << 6), (1) << 6); - - wlc_lcnphy_set_rx_iq_comp(pi, 0, 0); - - result = wlc_lcnphy_rx_iq_est(pi, num_samps, 32, &iq_est); - if (!result) - goto cleanup; - - iq = (s32) iq_est.iq_prod; - ii = iq_est.i_pwr; - qq = iq_est.q_pwr; - - if ((ii + qq) < LCNPHY_MIN_RXIQ_PWR) { - result = false; - goto cleanup; - } - - iq_nbits = wlc_phy_nbits(iq); - qq_nbits = wlc_phy_nbits(qq); - - arsh = 10 - (30 - iq_nbits); - if (arsh >= 0) { - a = (-(iq << (30 - iq_nbits)) + (ii >> (1 + arsh))); - temp = (s32) (ii >> arsh); - if (temp == 0) - return false; - } else { - a = (-(iq << (30 - iq_nbits)) + (ii << (-1 - arsh))); - temp = (s32) (ii << -arsh); - if (temp == 0) - return false; - } - a /= temp; - brsh = qq_nbits - 31 + 20; - if (brsh >= 0) { - b = (qq << (31 - qq_nbits)); - temp = (s32) (ii >> brsh); - if (temp == 0) - return false; - } else { - b = (qq << (31 - qq_nbits)); - temp = (s32) (ii << -brsh); - if (temp == 0) - return false; - } - b /= temp; - b -= a * a; - b = (s32) int_sqrt((unsigned long) b); - b -= (1 << 10); - a0_new = (u16) (a & 0x3ff); - b0_new = (u16) (b & 0x3ff); -cleanup: - - wlc_lcnphy_set_rx_iq_comp(pi, a0_new, b0_new); - - mod_phy_reg(pi, 0x64b, (0x1 << 0), (1) << 0); - - mod_phy_reg(pi, 0x64b, (0x1 << 3), (1) << 3); - - pi_lcn->lcnphy_cal_results.rxiqcal_coeff_a0 = a0_new; - pi_lcn->lcnphy_cal_results.rxiqcal_coeff_b0 = b0_new; - - return result; -} - -static u32 wlc_lcnphy_measure_digital_power(struct brcms_phy *pi, u16 nsamples) -{ - struct lcnphy_iq_est iq_est = { 0, 0, 0 }; - - if (!wlc_lcnphy_rx_iq_est(pi, nsamples, 32, &iq_est)) - return 0; - return (iq_est.i_pwr + iq_est.q_pwr) / nsamples; -} - -static bool wlc_lcnphy_rx_iq_cal_gain(struct brcms_phy *pi, u16 biq1_gain, - u16 tia_gain, u16 lna2_gain) -{ - u32 i_thresh_l, q_thresh_l; - u32 i_thresh_h, q_thresh_h; - struct lcnphy_iq_est iq_est_h, iq_est_l; - - wlc_lcnphy_set_rx_gain_by_distribution(pi, 0, 0, 0, biq1_gain, tia_gain, - lna2_gain, 0); - - wlc_lcnphy_rx_gain_override_enable(pi, true); - wlc_lcnphy_start_tx_tone(pi, 2000, (40 >> 1), 0); - udelay(500); - write_radio_reg(pi, RADIO_2064_REG112, 0); - if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_l)) - return false; - - wlc_lcnphy_start_tx_tone(pi, 2000, 40, 0); - udelay(500); - write_radio_reg(pi, RADIO_2064_REG112, 0); - if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_h)) - return false; - - i_thresh_l = (iq_est_l.i_pwr << 1); - i_thresh_h = (iq_est_l.i_pwr << 2) + iq_est_l.i_pwr; - - q_thresh_l = (iq_est_l.q_pwr << 1); - q_thresh_h = (iq_est_l.q_pwr << 2) + iq_est_l.q_pwr; - if ((iq_est_h.i_pwr > i_thresh_l) && - (iq_est_h.i_pwr < i_thresh_h) && - (iq_est_h.q_pwr > q_thresh_l) && - (iq_est_h.q_pwr < q_thresh_h)) - return true; - - return false; -} - -static bool -wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi, - const struct lcnphy_rx_iqcomp *iqcomp, - int iqcomp_sz, bool tx_switch, bool rx_switch, int module, - int tx_gain_idx) -{ - struct lcnphy_txgains old_gains; - u16 tx_pwr_ctrl; - u8 tx_gain_index_old = 0; - bool result = false, tx_gain_override_old = false; - u16 i, Core1TxControl_old, RFOverride0_old, - RFOverrideVal0_old, rfoverride2_old, rfoverride2val_old, - rfoverride3_old, rfoverride3val_old, rfoverride4_old, - rfoverride4val_old, afectrlovr_old, afectrlovrval_old; - int tia_gain, lna2_gain, biq1_gain; - bool set_gain; - u16 old_sslpnCalibClkEnCtrl, old_sslpnRxFeClkEnCtrl; - u16 values_to_save[11]; - s16 *ptr; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - ptr = kmalloc(sizeof(s16) * 131, GFP_ATOMIC); - if (NULL == ptr) - return false; - if (module == 2) { - while (iqcomp_sz--) { - if (iqcomp[iqcomp_sz].chan == - CHSPEC_CHANNEL(pi->radio_chanspec)) { - wlc_lcnphy_set_rx_iq_comp(pi, - (u16) - iqcomp[iqcomp_sz].a, - (u16) - iqcomp[iqcomp_sz].b); - result = true; - break; - } - } - goto cal_done; - } - - WARN_ON(module != 1); - tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); - - for (i = 0; i < 11; i++) - values_to_save[i] = - read_radio_reg(pi, rxiq_cal_rf_reg[i]); - Core1TxControl_old = read_phy_reg(pi, 0x631); - - or_phy_reg(pi, 0x631, 0x0015); - - RFOverride0_old = read_phy_reg(pi, 0x44c); - RFOverrideVal0_old = read_phy_reg(pi, 0x44d); - rfoverride2_old = read_phy_reg(pi, 0x4b0); - rfoverride2val_old = read_phy_reg(pi, 0x4b1); - rfoverride3_old = read_phy_reg(pi, 0x4f9); - rfoverride3val_old = read_phy_reg(pi, 0x4fa); - rfoverride4_old = read_phy_reg(pi, 0x938); - rfoverride4val_old = read_phy_reg(pi, 0x939); - afectrlovr_old = read_phy_reg(pi, 0x43b); - afectrlovrval_old = read_phy_reg(pi, 0x43c); - old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); - old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db); - - tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi); - if (tx_gain_override_old) { - wlc_lcnphy_get_tx_gain(pi, &old_gains); - tx_gain_index_old = pi_lcn->lcnphy_current_index; - } - - wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx); - - mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0); - - mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1); - mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1); - - write_radio_reg(pi, RADIO_2064_REG116, 0x06); - write_radio_reg(pi, RADIO_2064_REG12C, 0x07); - write_radio_reg(pi, RADIO_2064_REG06A, 0xd3); - write_radio_reg(pi, RADIO_2064_REG098, 0x03); - write_radio_reg(pi, RADIO_2064_REG00B, 0x7); - mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4); - write_radio_reg(pi, RADIO_2064_REG01D, 0x01); - write_radio_reg(pi, RADIO_2064_REG114, 0x01); - write_radio_reg(pi, RADIO_2064_REG02E, 0x10); - write_radio_reg(pi, RADIO_2064_REG12A, 0x08); - - mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0); - mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1); - mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1); - mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2); - mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2); - mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3); - mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3); - mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5); - mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5); - - mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0); - - write_phy_reg(pi, 0x6da, 0xffff); - or_phy_reg(pi, 0x6db, 0x3); - - wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch); - for (lna2_gain = 3; lna2_gain >= 0; lna2_gain--) { - for (tia_gain = 4; tia_gain >= 0; tia_gain--) { - for (biq1_gain = 6; biq1_gain >= 0; biq1_gain--) { - set_gain = wlc_lcnphy_rx_iq_cal_gain(pi, - (u16) - biq1_gain, - (u16) - tia_gain, - (u16) - lna2_gain); - if (!set_gain) - continue; - - result = wlc_lcnphy_calc_rx_iq_comp(pi, 1024); - goto stop_tone; - } - } - } - -stop_tone: - wlc_lcnphy_stop_tx_tone(pi); - - write_phy_reg(pi, 0x631, Core1TxControl_old); - - write_phy_reg(pi, 0x44c, RFOverrideVal0_old); - write_phy_reg(pi, 0x44d, RFOverrideVal0_old); - write_phy_reg(pi, 0x4b0, rfoverride2_old); - write_phy_reg(pi, 0x4b1, rfoverride2val_old); - write_phy_reg(pi, 0x4f9, rfoverride3_old); - write_phy_reg(pi, 0x4fa, rfoverride3val_old); - write_phy_reg(pi, 0x938, rfoverride4_old); - write_phy_reg(pi, 0x939, rfoverride4val_old); - write_phy_reg(pi, 0x43b, afectrlovr_old); - write_phy_reg(pi, 0x43c, afectrlovrval_old); - write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl); - write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl); - - wlc_lcnphy_clear_trsw_override(pi); - - mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2); - - for (i = 0; i < 11; i++) - write_radio_reg(pi, rxiq_cal_rf_reg[i], - values_to_save[i]); - - if (tx_gain_override_old) - wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old); - else - wlc_lcnphy_disable_tx_gain_override(pi); - - wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl); - wlc_lcnphy_rx_gain_override_enable(pi, false); - -cal_done: - kfree(ptr); - return result; -} - -s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi) -{ - s8 index; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - if (txpwrctrl_off(pi)) - index = pi_lcn->lcnphy_current_index; - else if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) - index = (s8) (wlc_lcnphy_get_current_tx_pwr_idx_if_pwrctrl_on( - pi) / 2); - else - index = pi_lcn->lcnphy_current_index; - return index; -} - -void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel) -{ - u16 afectrlovr, afectrlovrval; - afectrlovr = read_phy_reg(pi, 0x43b); - afectrlovrval = read_phy_reg(pi, 0x43c); - if (channel != 0) { - mod_phy_reg(pi, 0x43b, (0x1 << 1), (1) << 1); - - mod_phy_reg(pi, 0x43c, (0x1 << 1), (0) << 1); - - mod_phy_reg(pi, 0x43b, (0x1 << 4), (1) << 4); - - mod_phy_reg(pi, 0x43c, (0x1 << 6), (0) << 6); - - write_phy_reg(pi, 0x44b, 0xffff); - wlc_lcnphy_tx_pu(pi, 1); - - mod_phy_reg(pi, 0x634, (0xff << 8), (0) << 8); - - or_phy_reg(pi, 0x6da, 0x0080); - - or_phy_reg(pi, 0x00a, 0x228); - } else { - and_phy_reg(pi, 0x00a, ~(0x228)); - - and_phy_reg(pi, 0x6da, 0xFF7F); - write_phy_reg(pi, 0x43b, afectrlovr); - write_phy_reg(pi, 0x43c, afectrlovrval); - } -} - -static void wlc_lcnphy_toggle_afe_pwdn(struct brcms_phy *pi) -{ - u16 save_AfeCtrlOvrVal, save_AfeCtrlOvr; - - save_AfeCtrlOvrVal = read_phy_reg(pi, 0x43c); - save_AfeCtrlOvr = read_phy_reg(pi, 0x43b); - - write_phy_reg(pi, 0x43c, save_AfeCtrlOvrVal | 0x1); - write_phy_reg(pi, 0x43b, save_AfeCtrlOvr | 0x1); - - write_phy_reg(pi, 0x43c, save_AfeCtrlOvrVal & 0xfffe); - write_phy_reg(pi, 0x43b, save_AfeCtrlOvr & 0xfffe); - - write_phy_reg(pi, 0x43c, save_AfeCtrlOvrVal); - write_phy_reg(pi, 0x43b, save_AfeCtrlOvr); -} - -static void -wlc_lcnphy_txrx_spur_avoidance_mode(struct brcms_phy *pi, bool enable) -{ - if (enable) { - write_phy_reg(pi, 0x942, 0x7); - write_phy_reg(pi, 0x93b, ((1 << 13) + 23)); - write_phy_reg(pi, 0x93c, ((1 << 13) + 1989)); - - write_phy_reg(pi, 0x44a, 0x084); - write_phy_reg(pi, 0x44a, 0x080); - write_phy_reg(pi, 0x6d3, 0x2222); - write_phy_reg(pi, 0x6d3, 0x2220); - } else { - write_phy_reg(pi, 0x942, 0x0); - write_phy_reg(pi, 0x93b, ((0 << 13) + 23)); - write_phy_reg(pi, 0x93c, ((0 << 13) + 1989)); - } - wlapi_switch_macfreq(pi->sh->physhim, enable); -} - -static void -wlc_lcnphy_set_chanspec_tweaks(struct brcms_phy *pi, u16 chanspec) -{ - u8 channel = CHSPEC_CHANNEL(chanspec); - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - if (channel == 14) - mod_phy_reg(pi, 0x448, (0x3 << 8), (2) << 8); - else - mod_phy_reg(pi, 0x448, (0x3 << 8), (1) << 8); - - pi_lcn->lcnphy_bandedge_corr = 2; - if (channel == 1) - pi_lcn->lcnphy_bandedge_corr = 4; - - if (channel == 1 || channel == 2 || channel == 3 || - channel == 4 || channel == 9 || - channel == 10 || channel == 11 || channel == 12) { - bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x2, - 0x03000c04); - bcma_chipco_pll_maskset(&pi->d11core->bus->drv_cc, 0x3, - ~0x00ffffff, 0x0); - bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x4, - 0x200005c0); - - bcma_cc_set32(&pi->d11core->bus->drv_cc, BCMA_CC_PMU_CTL, - BCMA_CC_PMU_CTL_PLL_UPD); - write_phy_reg(pi, 0x942, 0); - wlc_lcnphy_txrx_spur_avoidance_mode(pi, false); - pi_lcn->lcnphy_spurmod = false; - mod_phy_reg(pi, 0x424, (0xff << 8), (0x1b) << 8); - - write_phy_reg(pi, 0x425, 0x5907); - } else { - bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x2, - 0x03140c04); - bcma_chipco_pll_maskset(&pi->d11core->bus->drv_cc, 0x3, - ~0x00ffffff, 0x333333); - bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x4, - 0x202c2820); - - bcma_cc_set32(&pi->d11core->bus->drv_cc, BCMA_CC_PMU_CTL, - BCMA_CC_PMU_CTL_PLL_UPD); - write_phy_reg(pi, 0x942, 0); - wlc_lcnphy_txrx_spur_avoidance_mode(pi, true); - - pi_lcn->lcnphy_spurmod = false; - mod_phy_reg(pi, 0x424, (0xff << 8), (0x1f) << 8); - - write_phy_reg(pi, 0x425, 0x590a); - } - - or_phy_reg(pi, 0x44a, 0x44); - write_phy_reg(pi, 0x44a, 0x80); -} - -static void -wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel) -{ - uint i; - const struct chan_info_2064_lcnphy *ci; - u8 rfpll_doubler = 0; - u8 pll_pwrup, pll_pwrup_ovr; - s32 qFxtal, qFref, qFvco, qFcal; - u8 d15, d16, f16, e44, e45; - u32 div_int, div_frac, fvco3, fpfd, fref3, fcal_div; - u16 loop_bw, d30, setCount; - - u8 h29, h28_ten, e30, h30_ten, cp_current; - u16 g30, d28; - - ci = &chan_info_2064_lcnphy[0]; - rfpll_doubler = 1; - - mod_radio_reg(pi, RADIO_2064_REG09D, 0x4, 0x1 << 2); - - write_radio_reg(pi, RADIO_2064_REG09E, 0xf); - if (!rfpll_doubler) { - loop_bw = PLL_2064_LOOP_BW; - d30 = PLL_2064_D30; - } else { - loop_bw = PLL_2064_LOOP_BW_DOUBLER; - d30 = PLL_2064_D30_DOUBLER; - } - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - for (i = 0; i < ARRAY_SIZE(chan_info_2064_lcnphy); i++) - if (chan_info_2064_lcnphy[i].chan == channel) - break; - - if (i >= ARRAY_SIZE(chan_info_2064_lcnphy)) - return; - - ci = &chan_info_2064_lcnphy[i]; - } - - write_radio_reg(pi, RADIO_2064_REG02A, ci->logen_buftune); - - mod_radio_reg(pi, RADIO_2064_REG030, 0x3, ci->logen_rccr_tx); - - mod_radio_reg(pi, RADIO_2064_REG091, 0x3, ci->txrf_mix_tune_ctrl); - - mod_radio_reg(pi, RADIO_2064_REG038, 0xf, ci->pa_input_tune_g); - - mod_radio_reg(pi, RADIO_2064_REG030, 0x3 << 2, - (ci->logen_rccr_rx) << 2); - - mod_radio_reg(pi, RADIO_2064_REG05E, 0xf, ci->pa_rxrf_lna1_freq_tune); - - mod_radio_reg(pi, RADIO_2064_REG05E, (0xf) << 4, - (ci->pa_rxrf_lna2_freq_tune) << 4); - - write_radio_reg(pi, RADIO_2064_REG06C, ci->rxrf_rxrf_spare1); - - pll_pwrup = (u8) read_radio_reg(pi, RADIO_2064_REG044); - pll_pwrup_ovr = (u8) read_radio_reg(pi, RADIO_2064_REG12B); - - or_radio_reg(pi, RADIO_2064_REG044, 0x07); - - or_radio_reg(pi, RADIO_2064_REG12B, (0x07) << 1); - e44 = 0; - e45 = 0; - - fpfd = rfpll_doubler ? (pi->xtalfreq << 1) : (pi->xtalfreq); - if (pi->xtalfreq > 26000000) - e44 = 1; - if (pi->xtalfreq > 52000000) - e45 = 1; - if (e44 == 0) - fcal_div = 1; - else if (e45 == 0) - fcal_div = 2; - else - fcal_div = 4; - fvco3 = (ci->freq * 3); - fref3 = 2 * fpfd; - - qFxtal = wlc_lcnphy_qdiv_roundup(pi->xtalfreq, PLL_2064_MHZ, 16); - qFref = wlc_lcnphy_qdiv_roundup(fpfd, PLL_2064_MHZ, 16); - qFcal = pi->xtalfreq * fcal_div / PLL_2064_MHZ; - qFvco = wlc_lcnphy_qdiv_roundup(fvco3, 2, 16); - - write_radio_reg(pi, RADIO_2064_REG04F, 0x02); - - d15 = (pi->xtalfreq * fcal_div * 4 / 5) / PLL_2064_MHZ - 1; - write_radio_reg(pi, RADIO_2064_REG052, (0x07 & (d15 >> 2))); - write_radio_reg(pi, RADIO_2064_REG053, (d15 & 0x3) << 5); - - d16 = (qFcal * 8 / (d15 + 1)) - 1; - write_radio_reg(pi, RADIO_2064_REG051, d16); - - f16 = ((d16 + 1) * (d15 + 1)) / qFcal; - setCount = f16 * 3 * (ci->freq) / 32 - 1; - mod_radio_reg(pi, RADIO_2064_REG053, (0x0f << 0), - (u8) (setCount >> 8)); - - or_radio_reg(pi, RADIO_2064_REG053, 0x10); - write_radio_reg(pi, RADIO_2064_REG054, (u8) (setCount & 0xff)); - - div_int = ((fvco3 * (PLL_2064_MHZ >> 4)) / fref3) << 4; - - div_frac = ((fvco3 * (PLL_2064_MHZ >> 4)) % fref3) << 4; - while (div_frac >= fref3) { - div_int++; - div_frac -= fref3; - } - div_frac = wlc_lcnphy_qdiv_roundup(div_frac, fref3, 20); - - mod_radio_reg(pi, RADIO_2064_REG045, (0x1f << 0), - (u8) (div_int >> 4)); - mod_radio_reg(pi, RADIO_2064_REG046, (0x1f << 4), - (u8) (div_int << 4)); - mod_radio_reg(pi, RADIO_2064_REG046, (0x0f << 0), - (u8) (div_frac >> 16)); - write_radio_reg(pi, RADIO_2064_REG047, (u8) (div_frac >> 8) & 0xff); - write_radio_reg(pi, RADIO_2064_REG048, (u8) div_frac & 0xff); - - write_radio_reg(pi, RADIO_2064_REG040, 0xfb); - - write_radio_reg(pi, RADIO_2064_REG041, 0x9A); - write_radio_reg(pi, RADIO_2064_REG042, 0xA3); - write_radio_reg(pi, RADIO_2064_REG043, 0x0C); - - h29 = LCN_BW_LMT / loop_bw; - d28 = (((PLL_2064_HIGH_END_KVCO - PLL_2064_LOW_END_KVCO) * - (fvco3 / 2 - PLL_2064_LOW_END_VCO)) / - (PLL_2064_HIGH_END_VCO - PLL_2064_LOW_END_VCO)) - + PLL_2064_LOW_END_KVCO; - h28_ten = (d28 * 10) / LCN_VCO_DIV; - e30 = (d30 - LCN_OFFSET) / LCN_FACT; - g30 = LCN_OFFSET + (e30 * LCN_FACT); - h30_ten = (g30 * 10) / LCN_CUR_DIV; - cp_current = ((LCN_CUR_LMT * h29 * LCN_MULT * 100) / h28_ten) / h30_ten; - mod_radio_reg(pi, RADIO_2064_REG03C, 0x3f, cp_current); - - if (channel >= 1 && channel <= 5) - write_radio_reg(pi, RADIO_2064_REG03C, 0x8); - else - write_radio_reg(pi, RADIO_2064_REG03C, 0x7); - write_radio_reg(pi, RADIO_2064_REG03D, 0x3); - - mod_radio_reg(pi, RADIO_2064_REG044, 0x0c, 0x0c); - udelay(1); - - wlc_2064_vco_cal(pi); - - write_radio_reg(pi, RADIO_2064_REG044, pll_pwrup); - write_radio_reg(pi, RADIO_2064_REG12B, pll_pwrup_ovr); - if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { - write_radio_reg(pi, RADIO_2064_REG038, 3); - write_radio_reg(pi, RADIO_2064_REG091, 7); - } - - if (!(pi->sh->boardflags & BFL_FEM)) { - static const u8 reg038[14] = { - 0xd, 0xe, 0xd, 0xd, 0xd, 0xc, 0xa, - 0xb, 0xb, 0x3, 0x3, 0x2, 0x0, 0x0 - }; - - write_radio_reg(pi, RADIO_2064_REG02A, 0xf); - write_radio_reg(pi, RADIO_2064_REG091, 0x3); - write_radio_reg(pi, RADIO_2064_REG038, 0x3); - - write_radio_reg(pi, RADIO_2064_REG038, reg038[channel - 1]); - } -} - -static int -wlc_lcnphy_load_tx_iir_filter(struct brcms_phy *pi, bool is_ofdm, s16 filt_type) -{ - s16 filt_index = -1; - int j; - - u16 addr[] = { - 0x910, - 0x91e, - 0x91f, - 0x924, - 0x925, - 0x926, - 0x920, - 0x921, - 0x927, - 0x928, - 0x929, - 0x922, - 0x923, - 0x930, - 0x931, - 0x932 - }; - - u16 addr_ofdm[] = { - 0x90f, - 0x900, - 0x901, - 0x906, - 0x907, - 0x908, - 0x902, - 0x903, - 0x909, - 0x90a, - 0x90b, - 0x904, - 0x905, - 0x90c, - 0x90d, - 0x90e - }; - - if (!is_ofdm) { - for (j = 0; j < LCNPHY_NUM_TX_DIG_FILTERS_CCK; j++) { - if (filt_type == LCNPHY_txdigfiltcoeffs_cck[j][0]) { - filt_index = (s16) j; - break; - } - } - - if (filt_index != -1) { - for (j = 0; j < LCNPHY_NUM_DIG_FILT_COEFFS; j++) - write_phy_reg(pi, addr[j], - LCNPHY_txdigfiltcoeffs_cck - [filt_index][j + 1]); - } - } else { - for (j = 0; j < LCNPHY_NUM_TX_DIG_FILTERS_OFDM; j++) { - if (filt_type == LCNPHY_txdigfiltcoeffs_ofdm[j][0]) { - filt_index = (s16) j; - break; - } - } - - if (filt_index != -1) { - for (j = 0; j < LCNPHY_NUM_DIG_FILT_COEFFS; j++) - write_phy_reg(pi, addr_ofdm[j], - LCNPHY_txdigfiltcoeffs_ofdm - [filt_index][j + 1]); - } - } - - return (filt_index != -1) ? 0 : -1; -} - -static u16 wlc_lcnphy_get_pa_gain(struct brcms_phy *pi) -{ - u16 pa_gain; - - pa_gain = (read_phy_reg(pi, 0x4fb) & - LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK) >> - LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT; - - return pa_gain; -} - -static void wlc_lcnphy_set_tx_gain(struct brcms_phy *pi, - struct lcnphy_txgains *target_gains) -{ - u16 pa_gain = wlc_lcnphy_get_pa_gain(pi); - - mod_phy_reg( - pi, 0x4b5, - (0xffff << 0), - ((target_gains->gm_gain) | - (target_gains->pga_gain << 8)) << - 0); - mod_phy_reg(pi, 0x4fb, - (0x7fff << 0), - ((target_gains->pad_gain) | (pa_gain << 8)) << 0); - - mod_phy_reg( - pi, 0x4fc, - (0xffff << 0), - ((target_gains->gm_gain) | - (target_gains->pga_gain << 8)) << - 0); - mod_phy_reg(pi, 0x4fd, - (0x7fff << 0), - ((target_gains->pad_gain) | (pa_gain << 8)) << 0); - - wlc_lcnphy_set_dac_gain(pi, target_gains->dac_gain); - - wlc_lcnphy_enable_tx_gain_override(pi); -} - -static u8 wlc_lcnphy_get_bbmult(struct brcms_phy *pi) -{ - u16 m0m1; - struct phytbl_info tab; - - tab.tbl_ptr = &m0m1; - tab.tbl_len = 1; - tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; - tab.tbl_offset = 87; - tab.tbl_width = 16; - wlc_lcnphy_read_table(pi, &tab); - - return (u8) ((m0m1 & 0xff00) >> 8); -} - -static void wlc_lcnphy_set_bbmult(struct brcms_phy *pi, u8 m0) -{ - u16 m0m1 = (u16) m0 << 8; - struct phytbl_info tab; - - tab.tbl_ptr = &m0m1; - tab.tbl_len = 1; - tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; - tab.tbl_offset = 87; - tab.tbl_width = 16; - wlc_lcnphy_write_table(pi, &tab); -} - -static void wlc_lcnphy_clear_tx_power_offsets(struct brcms_phy *pi) -{ - u32 data_buf[64]; - struct phytbl_info tab; - - memset(data_buf, 0, sizeof(data_buf)); - - tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; - tab.tbl_width = 32; - tab.tbl_ptr = data_buf; - - if (!wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { - - tab.tbl_len = 30; - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; - wlc_lcnphy_write_table(pi, &tab); - } - - tab.tbl_len = 64; - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_MAC_OFFSET; - wlc_lcnphy_write_table(pi, &tab); -} - -enum lcnphy_tssi_mode { - LCNPHY_TSSI_PRE_PA, - LCNPHY_TSSI_POST_PA, - LCNPHY_TSSI_EXT -}; - -static void -wlc_lcnphy_set_tssi_mux(struct brcms_phy *pi, enum lcnphy_tssi_mode pos) -{ - mod_phy_reg(pi, 0x4d7, (0x1 << 0), (0x1) << 0); - - mod_phy_reg(pi, 0x4d7, (0x1 << 6), (1) << 6); - - if (LCNPHY_TSSI_POST_PA == pos) { - mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0) << 2); - - mod_phy_reg(pi, 0x4d9, (0x1 << 3), (1) << 3); - - if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { - mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4); - } else { - mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0x1); - mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8); - mod_radio_reg(pi, RADIO_2064_REG028, 0x1, 0x0); - mod_radio_reg(pi, RADIO_2064_REG11A, 0x4, 1<<2); - mod_radio_reg(pi, RADIO_2064_REG036, 0x10, 0x0); - mod_radio_reg(pi, RADIO_2064_REG11A, 0x10, 1<<4); - mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0); - mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x77); - mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0xe<<1); - mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 1<<7); - mod_radio_reg(pi, RADIO_2064_REG005, 0x7, 1<<1); - mod_radio_reg(pi, RADIO_2064_REG029, 0xf0, 0<<4); - } - } else { - mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0x1) << 2); - - mod_phy_reg(pi, 0x4d9, (0x1 << 3), (0) << 3); - - if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { - mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4); - } else { - mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0); - mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8); - } - } - mod_phy_reg(pi, 0x637, (0x3 << 14), (0) << 14); - - if (LCNPHY_TSSI_EXT == pos) { - write_radio_reg(pi, RADIO_2064_REG07F, 1); - mod_radio_reg(pi, RADIO_2064_REG005, 0x7, 0x2); - mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 0x1 << 7); - mod_radio_reg(pi, RADIO_2064_REG028, 0x1f, 0x3); - } -} - -static u16 wlc_lcnphy_rfseq_tbl_adc_pwrup(struct brcms_phy *pi) -{ - u16 N1, N2, N3, N4, N5, N6, N; - N1 = ((read_phy_reg(pi, 0x4a5) & (0xff << 0)) - >> 0); - N2 = 1 << ((read_phy_reg(pi, 0x4a5) & (0x7 << 12)) - >> 12); - N3 = ((read_phy_reg(pi, 0x40d) & (0xff << 0)) - >> 0); - N4 = 1 << ((read_phy_reg(pi, 0x40d) & (0x7 << 8)) - >> 8); - N5 = ((read_phy_reg(pi, 0x4a2) & (0xff << 0)) - >> 0); - N6 = 1 << ((read_phy_reg(pi, 0x4a2) & (0x7 << 8)) - >> 8); - N = 2 * (N1 + N2 + N3 + N4 + 2 * (N5 + N6)) + 80; - if (N < 1600) - N = 1600; - return N; -} - -static void wlc_lcnphy_pwrctrl_rssiparams(struct brcms_phy *pi) -{ - u16 auxpga_vmid, auxpga_vmid_temp, auxpga_gain_temp; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - auxpga_vmid = (2 << 8) | - (pi_lcn->lcnphy_rssi_vc << 4) | pi_lcn->lcnphy_rssi_vf; - auxpga_vmid_temp = (2 << 8) | (8 << 4) | 4; - auxpga_gain_temp = 2; - - mod_phy_reg(pi, 0x4d8, (0x1 << 0), (0) << 0); - - mod_phy_reg(pi, 0x4d8, (0x1 << 1), (0) << 1); - - mod_phy_reg(pi, 0x4d7, (0x1 << 3), (0) << 3); - - mod_phy_reg(pi, 0x4db, - (0x3ff << 0) | - (0x7 << 12), - (auxpga_vmid << 0) | (pi_lcn->lcnphy_rssi_gs << 12)); - - mod_phy_reg(pi, 0x4dc, - (0x3ff << 0) | - (0x7 << 12), - (auxpga_vmid << 0) | (pi_lcn->lcnphy_rssi_gs << 12)); - - mod_phy_reg(pi, 0x40a, - (0x3ff << 0) | - (0x7 << 12), - (auxpga_vmid << 0) | (pi_lcn->lcnphy_rssi_gs << 12)); - - mod_phy_reg(pi, 0x40b, - (0x3ff << 0) | - (0x7 << 12), - (auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12)); - - mod_phy_reg(pi, 0x40c, - (0x3ff << 0) | - (0x7 << 12), - (auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12)); - - mod_radio_reg(pi, RADIO_2064_REG082, (1 << 5), (1 << 5)); - mod_radio_reg(pi, RADIO_2064_REG07C, (1 << 0), (1 << 0)); -} - -static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi) -{ - struct phytbl_info tab; - u32 rfseq, ind; - enum lcnphy_tssi_mode mode; - u8 tssi_sel; - - if (pi->sh->boardflags & BFL_FEM) { - tssi_sel = 0x1; - mode = LCNPHY_TSSI_EXT; - } else { - tssi_sel = 0xe; - mode = LCNPHY_TSSI_POST_PA; - } - tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; - tab.tbl_width = 32; - tab.tbl_ptr = &ind; - tab.tbl_len = 1; - tab.tbl_offset = 0; - for (ind = 0; ind < 128; ind++) { - wlc_lcnphy_write_table(pi, &tab); - tab.tbl_offset++; - } - tab.tbl_offset = 704; - for (ind = 0; ind < 128; ind++) { - wlc_lcnphy_write_table(pi, &tab); - tab.tbl_offset++; - } - mod_phy_reg(pi, 0x503, (0x1 << 0), (0) << 0); - - mod_phy_reg(pi, 0x503, (0x1 << 2), (0) << 2); - - mod_phy_reg(pi, 0x503, (0x1 << 4), (1) << 4); - - wlc_lcnphy_set_tssi_mux(pi, mode); - mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14); - - mod_phy_reg(pi, 0x4a4, (0x1 << 15), (1) << 15); - - mod_phy_reg(pi, 0x4d0, (0x1 << 5), (0) << 5); - - mod_phy_reg(pi, 0x4a4, (0x1ff << 0), (0) << 0); - - mod_phy_reg(pi, 0x4a5, (0xff << 0), (255) << 0); - - mod_phy_reg(pi, 0x4a5, (0x7 << 12), (5) << 12); - - mod_phy_reg(pi, 0x4a5, (0x7 << 8), (0) << 8); - - mod_phy_reg(pi, 0x40d, (0xff << 0), (64) << 0); - - mod_phy_reg(pi, 0x40d, (0x7 << 8), (4) << 8); - - mod_phy_reg(pi, 0x4a2, (0xff << 0), (64) << 0); - - mod_phy_reg(pi, 0x4a2, (0x7 << 8), (4) << 8); - - mod_phy_reg(pi, 0x4d0, (0x1ff << 6), (0) << 6); - - mod_phy_reg(pi, 0x4a8, (0xff << 0), (0x1) << 0); - - wlc_lcnphy_clear_tx_power_offsets(pi); - - mod_phy_reg(pi, 0x4a6, (0x1 << 15), (1) << 15); - - mod_phy_reg(pi, 0x4a6, (0x1ff << 0), (0xff) << 0); - - mod_phy_reg(pi, 0x49a, (0x1ff << 0), (0xff) << 0); - - if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { - mod_radio_reg(pi, RADIO_2064_REG028, 0xf, tssi_sel); - mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4); - } else { - mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, tssi_sel << 1); - mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1); - mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 1 << 3); - } - - write_radio_reg(pi, RADIO_2064_REG025, 0xc); - - if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { - mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1); - } else { - if (CHSPEC_IS2G(pi->radio_chanspec)) - mod_radio_reg(pi, RADIO_2064_REG03A, 0x2, 1 << 1); - else - mod_radio_reg(pi, RADIO_2064_REG03A, 0x2, 0 << 1); - } - - if (LCNREV_IS(pi->pubpi.phy_rev, 2)) - mod_radio_reg(pi, RADIO_2064_REG03A, 0x2, 1 << 1); - else - mod_radio_reg(pi, RADIO_2064_REG03A, 0x4, 1 << 2); - - mod_radio_reg(pi, RADIO_2064_REG11A, 0x1, 1 << 0); - - mod_radio_reg(pi, RADIO_2064_REG005, 0x8, 1 << 3); - - if (!wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) - mod_phy_reg(pi, 0x4d7, - (0x1 << 3) | (0x7 << 12), 0 << 3 | 2 << 12); - - rfseq = wlc_lcnphy_rfseq_tbl_adc_pwrup(pi); - tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; - tab.tbl_width = 16; - tab.tbl_ptr = &rfseq; - tab.tbl_len = 1; - tab.tbl_offset = 6; - wlc_lcnphy_write_table(pi, &tab); - - mod_phy_reg(pi, 0x938, (0x1 << 2), (1) << 2); - - mod_phy_reg(pi, 0x939, (0x1 << 2), (1) << 2); - - mod_phy_reg(pi, 0x4a4, (0x1 << 12), (1) << 12); - - mod_phy_reg(pi, 0x4d7, (0x1 << 2), (1) << 2); - - mod_phy_reg(pi, 0x4d7, (0xf << 8), (0) << 8); - - mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x0); - mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0); - mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8); - - wlc_lcnphy_pwrctrl_rssiparams(pi); -} - -void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi) -{ - u16 tx_cnt, tx_total, npt; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - tx_total = wlc_lcnphy_total_tx_frames(pi); - tx_cnt = tx_total - pi_lcn->lcnphy_tssi_tx_cnt; - npt = wlc_lcnphy_get_tx_pwr_npt(pi); - - if (tx_cnt > (1 << npt)) { - - pi_lcn->lcnphy_tssi_tx_cnt = tx_total; - - pi_lcn->lcnphy_tssi_idx = wlc_lcnphy_get_current_tx_pwr_idx(pi); - pi_lcn->lcnphy_tssi_npt = npt; - - } -} - -s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1) -{ - s32 a, b, p; - - a = 32768 + (a1 * tssi); - b = (1024 * b0) + (64 * b1 * tssi); - p = ((2 * b) + a) / (2 * a); - - return p; -} - -static void wlc_lcnphy_txpower_reset_npt(struct brcms_phy *pi) -{ - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) - return; - - pi_lcn->lcnphy_tssi_idx = LCNPHY_TX_PWR_CTRL_START_INDEX_2G_4313; - pi_lcn->lcnphy_tssi_npt = LCNPHY_TX_PWR_CTRL_START_NPT; -} - -void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi) -{ - struct phytbl_info tab; - u32 rate_table[BRCMS_NUM_RATES_CCK + BRCMS_NUM_RATES_OFDM + - BRCMS_NUM_RATES_MCS_1_STREAM]; - uint i, j; - if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) - return; - - for (i = 0, j = 0; i < ARRAY_SIZE(rate_table); i++, j++) { - - if (i == BRCMS_NUM_RATES_CCK + BRCMS_NUM_RATES_OFDM) - j = TXP_FIRST_MCS_20_SISO; - - rate_table[i] = (u32) ((s32) (-pi->tx_power_offset[j])); - } - - tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; - tab.tbl_width = 32; - tab.tbl_len = ARRAY_SIZE(rate_table); - tab.tbl_ptr = rate_table; - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; - wlc_lcnphy_write_table(pi, &tab); - - if (wlc_lcnphy_get_target_tx_pwr(pi) != pi->tx_power_min) { - wlc_lcnphy_set_target_tx_pwr(pi, pi->tx_power_min); - - wlc_lcnphy_txpower_reset_npt(pi); - } -} - -static void wlc_lcnphy_set_tx_pwr_soft_ctrl(struct brcms_phy *pi, s8 index) -{ - u32 cck_offset[4] = { 22, 22, 22, 22 }; - u32 ofdm_offset, reg_offset_cck; - int i; - u16 index2; - struct phytbl_info tab; - - if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) - return; - - mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0x1) << 14); - - mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0x0) << 14); - - or_phy_reg(pi, 0x6da, 0x0040); - - reg_offset_cck = 0; - for (i = 0; i < 4; i++) - cck_offset[i] -= reg_offset_cck; - tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; - tab.tbl_width = 32; - tab.tbl_len = 4; - tab.tbl_ptr = cck_offset; - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; - wlc_lcnphy_write_table(pi, &tab); - ofdm_offset = 0; - tab.tbl_len = 1; - tab.tbl_ptr = &ofdm_offset; - for (i = 836; i < 862; i++) { - tab.tbl_offset = i; - wlc_lcnphy_write_table(pi, &tab); - } - - mod_phy_reg(pi, 0x4a4, (0x1 << 15), (0x1) << 15); - - mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0x1) << 14); - - mod_phy_reg(pi, 0x4a4, (0x1 << 13), (0x1) << 13); - - mod_phy_reg(pi, 0x4b0, (0x1 << 7), (0) << 7); - - mod_phy_reg(pi, 0x43b, (0x1 << 6), (0) << 6); - - mod_phy_reg(pi, 0x4a9, (0x1 << 15), (1) << 15); - - index2 = (u16) (index * 2); - mod_phy_reg(pi, 0x4a9, (0x1ff << 0), (index2) << 0); - - mod_phy_reg(pi, 0x6a3, (0x1 << 4), (0) << 4); - -} - -static s8 wlc_lcnphy_tempcompensated_txpwrctrl(struct brcms_phy *pi) -{ - s8 index, delta_brd, delta_temp, new_index, tempcorrx; - s16 manp, meas_temp, temp_diff; - bool neg = false; - u16 temp; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) - return pi_lcn->lcnphy_current_index; - - index = FIXED_TXPWR; - - if (pi_lcn->lcnphy_tempsense_slope == 0) - return index; - - temp = (u16) wlc_lcnphy_tempsense(pi, 0); - meas_temp = LCNPHY_TEMPSENSE(temp); - - if (pi->tx_power_min != 0) - delta_brd = (pi_lcn->lcnphy_measPower - pi->tx_power_min); - else - delta_brd = 0; - - manp = LCNPHY_TEMPSENSE(pi_lcn->lcnphy_rawtempsense); - temp_diff = manp - meas_temp; - if (temp_diff < 0) { - neg = true; - temp_diff = -temp_diff; - } - - delta_temp = (s8) wlc_lcnphy_qdiv_roundup((u32) (temp_diff * 192), - (u32) (pi_lcn-> - lcnphy_tempsense_slope - * 10), 0); - if (neg) - delta_temp = -delta_temp; - - if (pi_lcn->lcnphy_tempsense_option == 3 - && LCNREV_IS(pi->pubpi.phy_rev, 0)) - delta_temp = 0; - if (pi_lcn->lcnphy_tempcorrx > 31) - tempcorrx = (s8) (pi_lcn->lcnphy_tempcorrx - 64); - else - tempcorrx = (s8) pi_lcn->lcnphy_tempcorrx; - if (LCNREV_IS(pi->pubpi.phy_rev, 1)) - tempcorrx = 4; - new_index = - index + delta_brd + delta_temp - pi_lcn->lcnphy_bandedge_corr; - new_index += tempcorrx; - - if (LCNREV_IS(pi->pubpi.phy_rev, 1)) - index = 127; - - if (new_index < 0 || new_index > 126) - return index; - - return new_index; -} - -static u16 wlc_lcnphy_set_tx_pwr_ctrl_mode(struct brcms_phy *pi, u16 mode) -{ - - u16 current_mode = mode; - if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) && - mode == LCNPHY_TX_PWR_CTRL_HW) - current_mode = LCNPHY_TX_PWR_CTRL_TEMPBASED; - if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi) && - mode == LCNPHY_TX_PWR_CTRL_TEMPBASED) - current_mode = LCNPHY_TX_PWR_CTRL_HW; - return current_mode; -} - -void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode) -{ - u16 old_mode = wlc_lcnphy_get_tx_pwr_ctrl(pi); - s8 index; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - mode = wlc_lcnphy_set_tx_pwr_ctrl_mode(pi, mode); - old_mode = wlc_lcnphy_set_tx_pwr_ctrl_mode(pi, old_mode); - - mod_phy_reg(pi, 0x6da, (0x1 << 6), - ((LCNPHY_TX_PWR_CTRL_HW == mode) ? 1 : 0) << 6); - - mod_phy_reg(pi, 0x6a3, (0x1 << 4), - ((LCNPHY_TX_PWR_CTRL_HW == mode) ? 0 : 1) << 4); - - if (old_mode != mode) { - if (LCNPHY_TX_PWR_CTRL_HW == old_mode) { - - wlc_lcnphy_tx_pwr_update_npt(pi); - - wlc_lcnphy_clear_tx_power_offsets(pi); - } - if (LCNPHY_TX_PWR_CTRL_HW == mode) { - - wlc_lcnphy_txpower_recalc_target(pi); - - wlc_lcnphy_set_start_tx_pwr_idx(pi, - pi_lcn-> - lcnphy_tssi_idx); - wlc_lcnphy_set_tx_pwr_npt(pi, pi_lcn->lcnphy_tssi_npt); - mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 0); - - pi_lcn->lcnphy_tssi_tx_cnt = - wlc_lcnphy_total_tx_frames(pi); - - wlc_lcnphy_disable_tx_gain_override(pi); - pi_lcn->lcnphy_tx_power_idx_override = -1; - } else - wlc_lcnphy_enable_tx_gain_override(pi); - - mod_phy_reg(pi, 0x4a4, - ((0x1 << 15) | (0x1 << 14) | (0x1 << 13)), mode); - if (mode == LCNPHY_TX_PWR_CTRL_TEMPBASED) { - index = wlc_lcnphy_tempcompensated_txpwrctrl(pi); - wlc_lcnphy_set_tx_pwr_soft_ctrl(pi, index); - pi_lcn->lcnphy_current_index = (s8) - ((read_phy_reg(pi, - 0x4a9) & - 0xFF) / 2); - } - } -} - -static void -wlc_lcnphy_tx_iqlo_loopback(struct brcms_phy *pi, u16 *values_to_save) -{ - u16 vmid; - int i; - for (i = 0; i < 20; i++) - values_to_save[i] = - read_radio_reg(pi, iqlo_loopback_rf_regs[i]); - - mod_phy_reg(pi, 0x44c, (0x1 << 12), 1 << 12); - mod_phy_reg(pi, 0x44d, (0x1 << 14), 1 << 14); - - mod_phy_reg(pi, 0x44c, (0x1 << 11), 1 << 11); - mod_phy_reg(pi, 0x44d, (0x1 << 13), 0 << 13); - - mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1); - mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1); - - mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0); - - if (LCNREV_IS(pi->pubpi.phy_rev, 2)) - and_radio_reg(pi, RADIO_2064_REG03A, 0xFD); - else - and_radio_reg(pi, RADIO_2064_REG03A, 0xF9); - or_radio_reg(pi, RADIO_2064_REG11A, 0x1); - - or_radio_reg(pi, RADIO_2064_REG036, 0x01); - or_radio_reg(pi, RADIO_2064_REG11A, 0x18); - udelay(20); - - if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { - if (CHSPEC_IS5G(pi->radio_chanspec)) - mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0); - else - or_radio_reg(pi, RADIO_2064_REG03A, 1); - } else { - if (CHSPEC_IS5G(pi->radio_chanspec)) - mod_radio_reg(pi, RADIO_2064_REG03A, 3, 1); - else - or_radio_reg(pi, RADIO_2064_REG03A, 0x3); - } - - udelay(20); - - write_radio_reg(pi, RADIO_2064_REG025, 0xF); - if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { - if (CHSPEC_IS5G(pi->radio_chanspec)) - mod_radio_reg(pi, RADIO_2064_REG028, 0xF, 0x4); - else - mod_radio_reg(pi, RADIO_2064_REG028, 0xF, 0x6); - } else { - if (CHSPEC_IS5G(pi->radio_chanspec)) - mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0x4 << 1); - else - mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0x6 << 1); - } - - udelay(20); - - write_radio_reg(pi, RADIO_2064_REG005, 0x8); - or_radio_reg(pi, RADIO_2064_REG112, 0x80); - udelay(20); - - or_radio_reg(pi, RADIO_2064_REG0FF, 0x10); - or_radio_reg(pi, RADIO_2064_REG11F, 0x44); - udelay(20); - - or_radio_reg(pi, RADIO_2064_REG00B, 0x7); - or_radio_reg(pi, RADIO_2064_REG113, 0x10); - udelay(20); - - write_radio_reg(pi, RADIO_2064_REG007, 0x1); - udelay(20); - - vmid = 0x2A6; - mod_radio_reg(pi, RADIO_2064_REG0FC, 0x3 << 0, (vmid >> 8) & 0x3); - write_radio_reg(pi, RADIO_2064_REG0FD, (vmid & 0xff)); - or_radio_reg(pi, RADIO_2064_REG11F, 0x44); - udelay(20); - - or_radio_reg(pi, RADIO_2064_REG0FF, 0x10); - udelay(20); - write_radio_reg(pi, RADIO_2064_REG012, 0x02); - or_radio_reg(pi, RADIO_2064_REG112, 0x06); - write_radio_reg(pi, RADIO_2064_REG036, 0x11); - write_radio_reg(pi, RADIO_2064_REG059, 0xcc); - write_radio_reg(pi, RADIO_2064_REG05C, 0x2e); - write_radio_reg(pi, RADIO_2064_REG078, 0xd7); - write_radio_reg(pi, RADIO_2064_REG092, 0x15); -} - -static bool wlc_lcnphy_iqcal_wait(struct brcms_phy *pi) -{ - uint delay_count = 0; - - while (wlc_lcnphy_iqcal_active(pi)) { - udelay(100); - delay_count++; - - if (delay_count > (10 * 500)) - break; - } - - return (0 == wlc_lcnphy_iqcal_active(pi)); -} - -static void -wlc_lcnphy_tx_iqlo_loopback_cleanup(struct brcms_phy *pi, u16 *values_to_save) -{ - int i; - - and_phy_reg(pi, 0x44c, 0x0 >> 11); - - and_phy_reg(pi, 0x43b, 0xC); - - for (i = 0; i < 20; i++) - write_radio_reg(pi, iqlo_loopback_rf_regs[i], - values_to_save[i]); -} - -static void -wlc_lcnphy_tx_iqlo_cal(struct brcms_phy *pi, - struct lcnphy_txgains *target_gains, - enum lcnphy_cal_mode cal_mode, bool keep_tone) -{ - - struct lcnphy_txgains cal_gains, temp_gains; - u16 hash; - u8 band_idx; - int j; - u16 ncorr_override[5]; - u16 syst_coeffs[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; - - u16 commands_fullcal[] = { - 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234 - }; - - u16 commands_recal[] = { - 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234 - }; - - u16 command_nums_fullcal[] = { - 0x7a97, 0x7a97, 0x7a97, 0x7a87, 0x7a87, 0x7b97 - }; - - u16 command_nums_recal[] = { - 0x7a97, 0x7a97, 0x7a97, 0x7a87, 0x7a87, 0x7b97 - }; - u16 *command_nums = command_nums_fullcal; - - u16 *start_coeffs = NULL, *cal_cmds = NULL, cal_type, diq_start; - u16 tx_pwr_ctrl_old, save_txpwrctrlrfctrl2; - u16 save_sslpnCalibClkEnCtrl, save_sslpnRxFeClkEnCtrl; - bool tx_gain_override_old; - struct lcnphy_txgains old_gains; - uint i, n_cal_cmds = 0, n_cal_start = 0; - u16 *values_to_save; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - values_to_save = kmalloc(sizeof(u16) * 20, GFP_ATOMIC); - if (NULL == values_to_save) - return; - - save_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db); - save_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); - - or_phy_reg(pi, 0x6da, 0x40); - or_phy_reg(pi, 0x6db, 0x3); - - switch (cal_mode) { - case LCNPHY_CAL_FULL: - start_coeffs = syst_coeffs; - cal_cmds = commands_fullcal; - n_cal_cmds = ARRAY_SIZE(commands_fullcal); - break; - - case LCNPHY_CAL_RECAL: - start_coeffs = syst_coeffs; - cal_cmds = commands_recal; - n_cal_cmds = ARRAY_SIZE(commands_recal); - command_nums = command_nums_recal; - break; - - default: - break; - } - - wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, - start_coeffs, 11, 16, 64); - - write_phy_reg(pi, 0x6da, 0xffff); - mod_phy_reg(pi, 0x503, (0x1 << 3), (1) << 3); - - tx_pwr_ctrl_old = wlc_lcnphy_get_tx_pwr_ctrl(pi); - - mod_phy_reg(pi, 0x4a4, (0x1 << 12), (1) << 12); - - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); - - save_txpwrctrlrfctrl2 = read_phy_reg(pi, 0x4db); - - mod_phy_reg(pi, 0x4db, (0x3ff << 0), (0x2a6) << 0); - - mod_phy_reg(pi, 0x4db, (0x7 << 12), (2) << 12); - - wlc_lcnphy_tx_iqlo_loopback(pi, values_to_save); - - tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi); - if (tx_gain_override_old) - wlc_lcnphy_get_tx_gain(pi, &old_gains); - - if (!target_gains) { - if (!tx_gain_override_old) - wlc_lcnphy_set_tx_pwr_by_index(pi, - pi_lcn->lcnphy_tssi_idx); - wlc_lcnphy_get_tx_gain(pi, &temp_gains); - target_gains = &temp_gains; - } - - hash = (target_gains->gm_gain << 8) | - (target_gains->pga_gain << 4) | (target_gains->pad_gain); - - band_idx = (CHSPEC_IS5G(pi->radio_chanspec) ? 1 : 0); - - cal_gains = *target_gains; - memset(ncorr_override, 0, sizeof(ncorr_override)); - for (j = 0; j < iqcal_gainparams_numgains_lcnphy[band_idx]; j++) { - if (hash == tbl_iqcal_gainparams_lcnphy[band_idx][j][0]) { - cal_gains.gm_gain = - tbl_iqcal_gainparams_lcnphy[band_idx][j][1]; - cal_gains.pga_gain = - tbl_iqcal_gainparams_lcnphy[band_idx][j][2]; - cal_gains.pad_gain = - tbl_iqcal_gainparams_lcnphy[band_idx][j][3]; - memcpy(ncorr_override, - &tbl_iqcal_gainparams_lcnphy[band_idx][j][3], - sizeof(ncorr_override)); - break; - } - } - - wlc_lcnphy_set_tx_gain(pi, &cal_gains); - - write_phy_reg(pi, 0x453, 0xaa9); - write_phy_reg(pi, 0x93d, 0xc0); - - wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, - lcnphy_iqcal_loft_gainladder, - ARRAY_SIZE(lcnphy_iqcal_loft_gainladder), - 16, 0); - - wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, - lcnphy_iqcal_ir_gainladder, - ARRAY_SIZE( - lcnphy_iqcal_ir_gainladder), 16, - 32); - - if (pi->phy_tx_tone_freq) { - - wlc_lcnphy_stop_tx_tone(pi); - udelay(5); - wlc_lcnphy_start_tx_tone(pi, 3750, 88, 1); - } else { - wlc_lcnphy_start_tx_tone(pi, 3750, 88, 1); - } - - write_phy_reg(pi, 0x6da, 0xffff); - - for (i = n_cal_start; i < n_cal_cmds; i++) { - u16 zero_diq = 0; - u16 best_coeffs[11]; - u16 command_num; - - cal_type = (cal_cmds[i] & 0x0f00) >> 8; - - command_num = command_nums[i]; - if (ncorr_override[cal_type]) - command_num = - ncorr_override[cal_type] << 8 | (command_num & - 0xff); - - write_phy_reg(pi, 0x452, command_num); - - if ((cal_type == 3) || (cal_type == 4)) { - wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, - &diq_start, 1, 16, 69); - - wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, - &zero_diq, 1, 16, 69); - } - - write_phy_reg(pi, 0x451, cal_cmds[i]); - - if (!wlc_lcnphy_iqcal_wait(pi)) - goto cleanup; - - wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, - best_coeffs, - ARRAY_SIZE(best_coeffs), 16, 96); - wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, - best_coeffs, - ARRAY_SIZE(best_coeffs), 16, 64); - - if ((cal_type == 3) || (cal_type == 4)) - wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, - &diq_start, 1, 16, 69); - wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, - pi_lcn->lcnphy_cal_results. - txiqlocal_bestcoeffs, - ARRAY_SIZE(pi_lcn-> - lcnphy_cal_results. - txiqlocal_bestcoeffs), - 16, 96); - } - - wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, - pi_lcn->lcnphy_cal_results. - txiqlocal_bestcoeffs, - ARRAY_SIZE(pi_lcn->lcnphy_cal_results. - txiqlocal_bestcoeffs), 16, 96); - pi_lcn->lcnphy_cal_results.txiqlocal_bestcoeffs_valid = true; - - wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, - &pi_lcn->lcnphy_cal_results. - txiqlocal_bestcoeffs[0], 4, 16, 80); - - wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, - &pi_lcn->lcnphy_cal_results. - txiqlocal_bestcoeffs[5], 2, 16, 85); - -cleanup: - wlc_lcnphy_tx_iqlo_loopback_cleanup(pi, values_to_save); - kfree(values_to_save); - - if (!keep_tone) - wlc_lcnphy_stop_tx_tone(pi); - - write_phy_reg(pi, 0x4db, save_txpwrctrlrfctrl2); - - write_phy_reg(pi, 0x453, 0); - - if (tx_gain_override_old) - wlc_lcnphy_set_tx_gain(pi, &old_gains); - wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl_old); - - write_phy_reg(pi, 0x6da, save_sslpnCalibClkEnCtrl); - write_phy_reg(pi, 0x6db, save_sslpnRxFeClkEnCtrl); - -} - -static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi) -{ - bool suspend, tx_gain_override_old; - struct lcnphy_txgains old_gains; - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - u16 idleTssi, idleTssi0_2C, idleTssi0_OB, idleTssi0_regvalue_OB, - idleTssi0_regvalue_2C; - u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); - u16 SAVE_lpfgain = read_radio_reg(pi, RADIO_2064_REG112); - u16 SAVE_jtag_bb_afe_switch = - read_radio_reg(pi, RADIO_2064_REG007) & 1; - u16 SAVE_jtag_auxpga = read_radio_reg(pi, RADIO_2064_REG0FF) & 0x10; - u16 SAVE_iqadc_aux_en = read_radio_reg(pi, RADIO_2064_REG11F) & 4; - u8 SAVE_bbmult = wlc_lcnphy_get_bbmult(pi); - - idleTssi = read_phy_reg(pi, 0x4ab); - suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!suspend) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); - - tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi); - wlc_lcnphy_get_tx_gain(pi, &old_gains); - - wlc_lcnphy_enable_tx_gain_override(pi); - wlc_lcnphy_set_tx_pwr_by_index(pi, 127); - write_radio_reg(pi, RADIO_2064_REG112, 0x6); - mod_radio_reg(pi, RADIO_2064_REG007, 0x1, 1); - mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 1 << 4); - mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 1 << 2); - wlc_lcnphy_tssi_setup(pi); - - mod_phy_reg(pi, 0x4d7, (0x1 << 0), (1 << 0)); - mod_phy_reg(pi, 0x4d7, (0x1 << 6), (1 << 6)); - - wlc_lcnphy_set_bbmult(pi, 0x0); - - wlc_phy_do_dummy_tx(pi, true, OFF); - idleTssi = ((read_phy_reg(pi, 0x4ab) & (0x1ff << 0)) - >> 0); - - idleTssi0_2C = ((read_phy_reg(pi, 0x63e) & (0x1ff << 0)) - >> 0); - - if (idleTssi0_2C >= 256) - idleTssi0_OB = idleTssi0_2C - 256; - else - idleTssi0_OB = idleTssi0_2C + 256; - - idleTssi0_regvalue_OB = idleTssi0_OB; - if (idleTssi0_regvalue_OB >= 256) - idleTssi0_regvalue_2C = idleTssi0_regvalue_OB - 256; - else - idleTssi0_regvalue_2C = idleTssi0_regvalue_OB + 256; - mod_phy_reg(pi, 0x4a6, (0x1ff << 0), (idleTssi0_regvalue_2C) << 0); - - mod_phy_reg(pi, 0x44c, (0x1 << 12), (0) << 12); - - wlc_lcnphy_set_bbmult(pi, SAVE_bbmult); - wlc_lcnphy_set_tx_gain_override(pi, tx_gain_override_old); - wlc_lcnphy_set_tx_gain(pi, &old_gains); - wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl); - - write_radio_reg(pi, RADIO_2064_REG112, SAVE_lpfgain); - mod_radio_reg(pi, RADIO_2064_REG007, 0x1, SAVE_jtag_bb_afe_switch); - mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, SAVE_jtag_auxpga); - mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, SAVE_iqadc_aux_en); - mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 1 << 7); - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); -} - -static void wlc_lcnphy_vbat_temp_sense_setup(struct brcms_phy *pi, u8 mode) -{ - bool suspend; - u16 save_txpwrCtrlEn; - u8 auxpga_vmidcourse, auxpga_vmidfine, auxpga_gain; - u16 auxpga_vmid; - struct phytbl_info tab; - u32 val; - u8 save_reg007, save_reg0FF, save_reg11F, save_reg005, save_reg025, - save_reg112; - u16 values_to_save[14]; - s8 index; - int i; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - udelay(999); - - save_reg007 = (u8) read_radio_reg(pi, RADIO_2064_REG007); - save_reg0FF = (u8) read_radio_reg(pi, RADIO_2064_REG0FF); - save_reg11F = (u8) read_radio_reg(pi, RADIO_2064_REG11F); - save_reg005 = (u8) read_radio_reg(pi, RADIO_2064_REG005); - save_reg025 = (u8) read_radio_reg(pi, RADIO_2064_REG025); - save_reg112 = (u8) read_radio_reg(pi, RADIO_2064_REG112); - - for (i = 0; i < 14; i++) - values_to_save[i] = read_phy_reg(pi, tempsense_phy_regs[i]); - suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!suspend) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - save_txpwrCtrlEn = read_radio_reg(pi, 0x4a4); - - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); - index = pi_lcn->lcnphy_current_index; - wlc_lcnphy_set_tx_pwr_by_index(pi, 127); - mod_radio_reg(pi, RADIO_2064_REG007, 0x1, 0x1); - mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 0x1 << 4); - mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 0x1 << 2); - mod_phy_reg(pi, 0x503, (0x1 << 0), (0) << 0); - - mod_phy_reg(pi, 0x503, (0x1 << 2), (0) << 2); - - mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14); - - mod_phy_reg(pi, 0x4a4, (0x1 << 15), (0) << 15); - - mod_phy_reg(pi, 0x4d0, (0x1 << 5), (0) << 5); - - mod_phy_reg(pi, 0x4a5, (0xff << 0), (255) << 0); - - mod_phy_reg(pi, 0x4a5, (0x7 << 12), (5) << 12); - - mod_phy_reg(pi, 0x4a5, (0x7 << 8), (0) << 8); - - mod_phy_reg(pi, 0x40d, (0xff << 0), (64) << 0); - - mod_phy_reg(pi, 0x40d, (0x7 << 8), (6) << 8); - - mod_phy_reg(pi, 0x4a2, (0xff << 0), (64) << 0); - - mod_phy_reg(pi, 0x4a2, (0x7 << 8), (6) << 8); - - mod_phy_reg(pi, 0x4d9, (0x7 << 4), (2) << 4); - - mod_phy_reg(pi, 0x4d9, (0x7 << 8), (3) << 8); - - mod_phy_reg(pi, 0x4d9, (0x7 << 12), (1) << 12); - - mod_phy_reg(pi, 0x4da, (0x1 << 12), (0) << 12); - - mod_phy_reg(pi, 0x4da, (0x1 << 13), (1) << 13); - - mod_phy_reg(pi, 0x4a6, (0x1 << 15), (1) << 15); - - write_radio_reg(pi, RADIO_2064_REG025, 0xC); - - mod_radio_reg(pi, RADIO_2064_REG005, 0x8, 0x1 << 3); - - mod_phy_reg(pi, 0x938, (0x1 << 2), (1) << 2); - - mod_phy_reg(pi, 0x939, (0x1 << 2), (1) << 2); - - mod_phy_reg(pi, 0x4a4, (0x1 << 12), (1) << 12); - - val = wlc_lcnphy_rfseq_tbl_adc_pwrup(pi); - tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; - tab.tbl_width = 16; - tab.tbl_len = 1; - tab.tbl_ptr = &val; - tab.tbl_offset = 6; - wlc_lcnphy_write_table(pi, &tab); - if (mode == TEMPSENSE) { - mod_phy_reg(pi, 0x4d7, (0x1 << 3), (1) << 3); - - mod_phy_reg(pi, 0x4d7, (0x7 << 12), (1) << 12); - - auxpga_vmidcourse = 8; - auxpga_vmidfine = 0x4; - auxpga_gain = 2; - mod_radio_reg(pi, RADIO_2064_REG082, 0x20, 1 << 5); - } else { - mod_phy_reg(pi, 0x4d7, (0x1 << 3), (1) << 3); - - mod_phy_reg(pi, 0x4d7, (0x7 << 12), (3) << 12); - - auxpga_vmidcourse = 7; - auxpga_vmidfine = 0xa; - auxpga_gain = 2; - } - auxpga_vmid = - (u16) ((2 << 8) | (auxpga_vmidcourse << 4) | auxpga_vmidfine); - mod_phy_reg(pi, 0x4d8, (0x1 << 0), (1) << 0); - - mod_phy_reg(pi, 0x4d8, (0x3ff << 2), (auxpga_vmid) << 2); - - mod_phy_reg(pi, 0x4d8, (0x1 << 1), (1) << 1); - - mod_phy_reg(pi, 0x4d8, (0x7 << 12), (auxpga_gain) << 12); - - mod_phy_reg(pi, 0x4d0, (0x1 << 5), (1) << 5); - - write_radio_reg(pi, RADIO_2064_REG112, 0x6); - - wlc_phy_do_dummy_tx(pi, true, OFF); - if (!tempsense_done(pi)) - udelay(10); - - write_radio_reg(pi, RADIO_2064_REG007, (u16) save_reg007); - write_radio_reg(pi, RADIO_2064_REG0FF, (u16) save_reg0FF); - write_radio_reg(pi, RADIO_2064_REG11F, (u16) save_reg11F); - write_radio_reg(pi, RADIO_2064_REG005, (u16) save_reg005); - write_radio_reg(pi, RADIO_2064_REG025, (u16) save_reg025); - write_radio_reg(pi, RADIO_2064_REG112, (u16) save_reg112); - for (i = 0; i < 14; i++) - write_phy_reg(pi, tempsense_phy_regs[i], values_to_save[i]); - wlc_lcnphy_set_tx_pwr_by_index(pi, (int)index); - - write_radio_reg(pi, 0x4a4, save_txpwrCtrlEn); - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); - udelay(999); -} - -static void wlc_lcnphy_tx_pwr_ctrl_init(struct brcms_phy_pub *ppi) -{ - struct lcnphy_txgains tx_gains; - u8 bbmult; - struct phytbl_info tab; - s32 a1, b0, b1; - s32 tssi, pwr, maxtargetpwr, mintargetpwr; - bool suspend; - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - - suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!suspend) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - - if (!pi->hwpwrctrl_capable) { - if (CHSPEC_IS2G(pi->radio_chanspec)) { - tx_gains.gm_gain = 4; - tx_gains.pga_gain = 12; - tx_gains.pad_gain = 12; - tx_gains.dac_gain = 0; - - bbmult = 150; - } else { - tx_gains.gm_gain = 7; - tx_gains.pga_gain = 15; - tx_gains.pad_gain = 14; - tx_gains.dac_gain = 0; - - bbmult = 150; - } - wlc_lcnphy_set_tx_gain(pi, &tx_gains); - wlc_lcnphy_set_bbmult(pi, bbmult); - wlc_lcnphy_vbat_temp_sense_setup(pi, TEMPSENSE); - } else { - - wlc_lcnphy_idle_tssi_est(ppi); - - wlc_lcnphy_clear_tx_power_offsets(pi); - - b0 = pi->txpa_2g[0]; - b1 = pi->txpa_2g[1]; - a1 = pi->txpa_2g[2]; - maxtargetpwr = wlc_lcnphy_tssi2dbm(10, a1, b0, b1); - mintargetpwr = wlc_lcnphy_tssi2dbm(125, a1, b0, b1); - - tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; - tab.tbl_width = 32; - tab.tbl_ptr = &pwr; - tab.tbl_len = 1; - tab.tbl_offset = 0; - for (tssi = 0; tssi < 128; tssi++) { - pwr = wlc_lcnphy_tssi2dbm(tssi, a1, b0, b1); - - pwr = (pwr < mintargetpwr) ? mintargetpwr : pwr; - wlc_lcnphy_write_table(pi, &tab); - tab.tbl_offset++; - } - mod_phy_reg(pi, 0x4d0, (0x1 << 0), (0) << 0); - mod_phy_reg(pi, 0x4d3, (0xff << 0), (0) << 0); - mod_phy_reg(pi, 0x4d3, (0xff << 8), (0) << 8); - mod_phy_reg(pi, 0x4d0, (0x1 << 4), (0) << 4); - mod_phy_reg(pi, 0x4d0, (0x1 << 2), (0) << 2); - - mod_phy_reg(pi, 0x410, (0x1 << 7), (0) << 7); - - write_phy_reg(pi, 0x4a8, 10); - - wlc_lcnphy_set_target_tx_pwr(pi, LCN_TARGET_PWR); - - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_HW); - } - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); -} - -static void wlc_lcnphy_set_pa_gain(struct brcms_phy *pi, u16 gain) -{ - mod_phy_reg(pi, 0x4fb, - LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK, - gain << LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT); - mod_phy_reg(pi, 0x4fd, - LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_MASK, - gain << LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_SHIFT); -} - -void -wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, - u8 *ei0, u8 *eq0, u8 *fi0, u8 *fq0) -{ - *ei0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG089)); - *eq0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG08A)); - *fi0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG08B)); - *fq0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG08C)); -} - -void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b) -{ - struct phytbl_info tab; - u16 iqcc[2]; - - iqcc[0] = a; - iqcc[1] = b; - - tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; - tab.tbl_width = 16; - tab.tbl_ptr = iqcc; - tab.tbl_len = 2; - tab.tbl_offset = 80; - wlc_lcnphy_write_table(pi, &tab); -} - -void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq) -{ - struct phytbl_info tab; - - tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; - tab.tbl_width = 16; - tab.tbl_ptr = &didq; - tab.tbl_len = 1; - tab.tbl_offset = 85; - wlc_lcnphy_write_table(pi, &tab); -} - -void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index) -{ - struct phytbl_info tab; - u16 a, b; - u8 bb_mult; - u32 bbmultiqcomp, txgain, locoeffs, rfpower; - struct lcnphy_txgains gains; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - pi_lcn->lcnphy_tx_power_idx_override = (s8) index; - pi_lcn->lcnphy_current_index = (u8) index; - - tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; - tab.tbl_width = 32; - tab.tbl_len = 1; - - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); - - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + index; - tab.tbl_ptr = &bbmultiqcomp; - wlc_lcnphy_read_table(pi, &tab); - - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + index; - tab.tbl_width = 32; - tab.tbl_ptr = &txgain; - wlc_lcnphy_read_table(pi, &tab); - - gains.gm_gain = (u16) (txgain & 0xff); - gains.pga_gain = (u16) (txgain >> 8) & 0xff; - gains.pad_gain = (u16) (txgain >> 16) & 0xff; - gains.dac_gain = (u16) (bbmultiqcomp >> 28) & 0x07; - wlc_lcnphy_set_tx_gain(pi, &gains); - wlc_lcnphy_set_pa_gain(pi, (u16) (txgain >> 24) & 0x7f); - - bb_mult = (u8) ((bbmultiqcomp >> 20) & 0xff); - wlc_lcnphy_set_bbmult(pi, bb_mult); - - wlc_lcnphy_enable_tx_gain_override(pi); - - if (!wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { - - a = (u16) ((bbmultiqcomp >> 10) & 0x3ff); - b = (u16) (bbmultiqcomp & 0x3ff); - wlc_lcnphy_set_tx_iqcc(pi, a, b); - - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_LO_OFFSET + index; - tab.tbl_ptr = &locoeffs; - wlc_lcnphy_read_table(pi, &tab); - - wlc_lcnphy_set_tx_locc(pi, (u16) locoeffs); - - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_PWR_OFFSET + index; - tab.tbl_ptr = &rfpower; - wlc_lcnphy_read_table(pi, &tab); - mod_phy_reg(pi, 0x6a6, (0x1fff << 0), (rfpower * 8) << 0); - - } -} - -static void wlc_lcnphy_clear_papd_comptable(struct brcms_phy *pi) -{ - u32 j; - struct phytbl_info tab; - u32 temp_offset[128]; - tab.tbl_ptr = temp_offset; - tab.tbl_len = 128; - tab.tbl_id = LCNPHY_TBL_ID_PAPDCOMPDELTATBL; - tab.tbl_width = 32; - tab.tbl_offset = 0; - - memset(temp_offset, 0, sizeof(temp_offset)); - for (j = 1; j < 128; j += 2) - temp_offset[j] = 0x80000; - - wlc_lcnphy_write_table(pi, &tab); - return; -} - -void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable) -{ - if (!bEnable) { - - and_phy_reg(pi, 0x43b, ~(u16) ((0x1 << 1) | (0x1 << 4))); - - mod_phy_reg(pi, 0x43c, (0x1 << 1), 1 << 1); - - and_phy_reg(pi, 0x44c, - ~(u16) ((0x1 << 3) | - (0x1 << 5) | - (0x1 << 12) | - (0x1 << 0) | (0x1 << 1) | (0x1 << 2))); - - and_phy_reg(pi, 0x44d, - ~(u16) ((0x1 << 3) | (0x1 << 5) | (0x1 << 14))); - mod_phy_reg(pi, 0x44d, (0x1 << 2), 1 << 2); - - mod_phy_reg(pi, 0x44d, (0x1 << 1) | (0x1 << 0), (0x1 << 0)); - - and_phy_reg(pi, 0x4f9, - ~(u16) ((0x1 << 0) | (0x1 << 1) | (0x1 << 2))); - - and_phy_reg(pi, 0x4fa, - ~(u16) ((0x1 << 0) | (0x1 << 1) | (0x1 << 2))); - } else { - - mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1); - mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1); - - mod_phy_reg(pi, 0x43b, (0x1 << 4), 1 << 4); - mod_phy_reg(pi, 0x43c, (0x1 << 6), 0 << 6); - - mod_phy_reg(pi, 0x44c, (0x1 << 12), 1 << 12); - mod_phy_reg(pi, 0x44d, (0x1 << 14), 1 << 14); - - wlc_lcnphy_set_trsw_override(pi, true, false); - - mod_phy_reg(pi, 0x44d, (0x1 << 2), 0 << 2); - mod_phy_reg(pi, 0x44c, (0x1 << 2), 1 << 2); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - - mod_phy_reg(pi, 0x44c, (0x1 << 3), 1 << 3); - mod_phy_reg(pi, 0x44d, (0x1 << 3), 1 << 3); - - mod_phy_reg(pi, 0x44c, (0x1 << 5), 1 << 5); - mod_phy_reg(pi, 0x44d, (0x1 << 5), 0 << 5); - - mod_phy_reg(pi, 0x4f9, (0x1 << 1), 1 << 1); - mod_phy_reg(pi, 0x4fa, (0x1 << 1), 1 << 1); - - mod_phy_reg(pi, 0x4f9, (0x1 << 2), 1 << 2); - mod_phy_reg(pi, 0x4fa, (0x1 << 2), 1 << 2); - - mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0x4fa, (0x1 << 0), 1 << 0); - } else { - - mod_phy_reg(pi, 0x44c, (0x1 << 3), 1 << 3); - mod_phy_reg(pi, 0x44d, (0x1 << 3), 0 << 3); - - mod_phy_reg(pi, 0x44c, (0x1 << 5), 1 << 5); - mod_phy_reg(pi, 0x44d, (0x1 << 5), 1 << 5); - - mod_phy_reg(pi, 0x4f9, (0x1 << 1), 1 << 1); - mod_phy_reg(pi, 0x4fa, (0x1 << 1), 0 << 1); - - mod_phy_reg(pi, 0x4f9, (0x1 << 2), 1 << 2); - mod_phy_reg(pi, 0x4fa, (0x1 << 2), 0 << 2); - - mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0); - } - } -} - -static void -wlc_lcnphy_run_samples(struct brcms_phy *pi, - u16 num_samps, - u16 num_loops, u16 wait, bool iqcalmode) -{ - - or_phy_reg(pi, 0x6da, 0x8080); - - mod_phy_reg(pi, 0x642, (0x7f << 0), (num_samps - 1) << 0); - if (num_loops != 0xffff) - num_loops--; - mod_phy_reg(pi, 0x640, (0xffff << 0), num_loops << 0); - - mod_phy_reg(pi, 0x641, (0xffff << 0), wait << 0); - - if (iqcalmode) { - - and_phy_reg(pi, 0x453, (u16) ~(0x1 << 15)); - or_phy_reg(pi, 0x453, (0x1 << 15)); - } else { - write_phy_reg(pi, 0x63f, 1); - wlc_lcnphy_tx_pu(pi, 1); - } - - or_radio_reg(pi, RADIO_2064_REG112, 0x6); -} - -void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode) -{ - - u8 phybw40; - phybw40 = CHSPEC_IS40(pi->radio_chanspec); - - if (LCNREV_LT(pi->pubpi.phy_rev, 2)) { - mod_phy_reg(pi, 0x4b0, (0x1 << 5), (mode) << 5); - mod_phy_reg(pi, 0x4b1, (0x1 << 9), 0 << 9); - } else { - mod_phy_reg(pi, 0x4b0, (0x1 << 5), (mode) << 5); - mod_phy_reg(pi, 0x4b1, (0x1 << 9), 0 << 9); - } - - if (phybw40 == 0) { - mod_phy_reg((pi), 0x410, - (0x1 << 6) | - (0x1 << 5), - ((CHSPEC_IS2G( - pi->radio_chanspec)) ? (!mode) : 0) << - 6 | (!mode) << 5); - mod_phy_reg(pi, 0x410, (0x1 << 7), (mode) << 7); - } -} - -void -wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val, - bool iqcalmode) -{ - u8 phy_bw; - u16 num_samps, t, k; - u32 bw; - s32 theta = 0, rot = 0; - struct cordic_iq tone_samp; - u32 data_buf[64]; - u16 i_samp, q_samp; - struct phytbl_info tab; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - pi->phy_tx_tone_freq = f_kHz; - - wlc_lcnphy_deaf_mode(pi, true); - - phy_bw = 40; - if (pi_lcn->lcnphy_spurmod) { - write_phy_reg(pi, 0x942, 0x2); - write_phy_reg(pi, 0x93b, 0x0); - write_phy_reg(pi, 0x93c, 0x0); - wlc_lcnphy_txrx_spur_avoidance_mode(pi, false); - } - - if (f_kHz) { - k = 1; - do { - bw = phy_bw * 1000 * k; - num_samps = bw / abs(f_kHz); - k++; - } while ((num_samps * (u32) (abs(f_kHz))) != bw); - } else - num_samps = 2; - - rot = ((f_kHz * 36) / phy_bw) / 100; - theta = 0; - - for (t = 0; t < num_samps; t++) { - - tone_samp = cordic_calc_iq(theta); - - theta += rot; - - i_samp = (u16) (FLOAT(tone_samp.i * max_val) & 0x3ff); - q_samp = (u16) (FLOAT(tone_samp.q * max_val) & 0x3ff); - data_buf[t] = (i_samp << 10) | q_samp; - } - - mod_phy_reg(pi, 0x6d6, (0x3 << 0), 0 << 0); - - mod_phy_reg(pi, 0x6da, (0x1 << 3), 1 << 3); - - tab.tbl_ptr = data_buf; - tab.tbl_len = num_samps; - tab.tbl_id = LCNPHY_TBL_ID_SAMPLEPLAY; - tab.tbl_offset = 0; - tab.tbl_width = 32; - wlc_lcnphy_write_table(pi, &tab); - - wlc_lcnphy_run_samples(pi, num_samps, 0xffff, 0, iqcalmode); -} - -void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi) -{ - s16 playback_status; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - pi->phy_tx_tone_freq = 0; - if (pi_lcn->lcnphy_spurmod) { - write_phy_reg(pi, 0x942, 0x7); - write_phy_reg(pi, 0x93b, 0x2017); - write_phy_reg(pi, 0x93c, 0x27c5); - wlc_lcnphy_txrx_spur_avoidance_mode(pi, true); - } - - playback_status = read_phy_reg(pi, 0x644); - if (playback_status & (0x1 << 0)) { - wlc_lcnphy_tx_pu(pi, 0); - mod_phy_reg(pi, 0x63f, (0x1 << 1), 1 << 1); - } else if (playback_status & (0x1 << 1)) - mod_phy_reg(pi, 0x453, (0x1 << 15), 0 << 15); - - mod_phy_reg(pi, 0x6d6, (0x3 << 0), 1 << 0); - - mod_phy_reg(pi, 0x6da, (0x1 << 3), 0 << 3); - - mod_phy_reg(pi, 0x6da, (0x1 << 7), 0 << 7); - - and_radio_reg(pi, RADIO_2064_REG112, 0xFFF9); - - wlc_lcnphy_deaf_mode(pi, false); -} - -static void -wlc_lcnphy_set_cc(struct brcms_phy *pi, int cal_type, s16 coeff_x, s16 coeff_y) -{ - u16 di0dq0; - u16 x, y, data_rf; - int k; - switch (cal_type) { - case 0: - wlc_lcnphy_set_tx_iqcc(pi, coeff_x, coeff_y); - break; - case 2: - di0dq0 = (coeff_x & 0xff) << 8 | (coeff_y & 0xff); - wlc_lcnphy_set_tx_locc(pi, di0dq0); - break; - case 3: - k = wlc_lcnphy_calc_floor(coeff_x, 0); - y = 8 + k; - k = wlc_lcnphy_calc_floor(coeff_x, 1); - x = 8 - k; - data_rf = (x * 16 + y); - write_radio_reg(pi, RADIO_2064_REG089, data_rf); - k = wlc_lcnphy_calc_floor(coeff_y, 0); - y = 8 + k; - k = wlc_lcnphy_calc_floor(coeff_y, 1); - x = 8 - k; - data_rf = (x * 16 + y); - write_radio_reg(pi, RADIO_2064_REG08A, data_rf); - break; - case 4: - k = wlc_lcnphy_calc_floor(coeff_x, 0); - y = 8 + k; - k = wlc_lcnphy_calc_floor(coeff_x, 1); - x = 8 - k; - data_rf = (x * 16 + y); - write_radio_reg(pi, RADIO_2064_REG08B, data_rf); - k = wlc_lcnphy_calc_floor(coeff_y, 0); - y = 8 + k; - k = wlc_lcnphy_calc_floor(coeff_y, 1); - x = 8 - k; - data_rf = (x * 16 + y); - write_radio_reg(pi, RADIO_2064_REG08C, data_rf); - break; - } -} - -static struct lcnphy_unsign16_struct -wlc_lcnphy_get_cc(struct brcms_phy *pi, int cal_type) -{ - u16 a, b, didq; - u8 di0, dq0, ei, eq, fi, fq; - struct lcnphy_unsign16_struct cc; - cc.re = 0; - cc.im = 0; - switch (cal_type) { - case 0: - wlc_lcnphy_get_tx_iqcc(pi, &a, &b); - cc.re = a; - cc.im = b; - break; - case 2: - didq = wlc_lcnphy_get_tx_locc(pi); - di0 = (((didq & 0xff00) << 16) >> 24); - dq0 = (((didq & 0x00ff) << 24) >> 24); - cc.re = (u16) di0; - cc.im = (u16) dq0; - break; - case 3: - wlc_lcnphy_get_radio_loft(pi, &ei, &eq, &fi, &fq); - cc.re = (u16) ei; - cc.im = (u16) eq; - break; - case 4: - wlc_lcnphy_get_radio_loft(pi, &ei, &eq, &fi, &fq); - cc.re = (u16) fi; - cc.im = (u16) fq; - break; - } - return cc; -} - -static void -wlc_lcnphy_samp_cap(struct brcms_phy *pi, int clip_detect_algo, u16 thresh, - s16 *ptr, int mode) -{ - u32 curval1, curval2, stpptr, curptr, strptr, val; - u16 sslpnCalibClkEnCtrl, timer; - u16 old_sslpnCalibClkEnCtrl; - s16 imag, real; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - timer = 0; - old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); - - curval1 = bcma_read16(pi->d11core, D11REGOFFS(psm_corectlsts)); - ptr[130] = 0; - bcma_write16(pi->d11core, D11REGOFFS(psm_corectlsts), - ((1 << 6) | curval1)); - - bcma_write16(pi->d11core, D11REGOFFS(smpl_clct_strptr), 0x7E00); - bcma_write16(pi->d11core, D11REGOFFS(smpl_clct_stpptr), 0x8000); - udelay(20); - curval2 = bcma_read16(pi->d11core, D11REGOFFS(psm_phy_hdr_param)); - bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), - curval2 | 0x30); - - write_phy_reg(pi, 0x555, 0x0); - write_phy_reg(pi, 0x5a6, 0x5); - - write_phy_reg(pi, 0x5a2, (u16) (mode | mode << 6)); - write_phy_reg(pi, 0x5cf, 3); - write_phy_reg(pi, 0x5a5, 0x3); - write_phy_reg(pi, 0x583, 0x0); - write_phy_reg(pi, 0x584, 0x0); - write_phy_reg(pi, 0x585, 0x0fff); - write_phy_reg(pi, 0x586, 0x0000); - - write_phy_reg(pi, 0x580, 0x4501); - - sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); - write_phy_reg(pi, 0x6da, (u32) (sslpnCalibClkEnCtrl | 0x2008)); - stpptr = bcma_read16(pi->d11core, D11REGOFFS(smpl_clct_stpptr)); - curptr = bcma_read16(pi->d11core, D11REGOFFS(smpl_clct_curptr)); - do { - udelay(10); - curptr = bcma_read16(pi->d11core, D11REGOFFS(smpl_clct_curptr)); - timer++; - } while ((curptr != stpptr) && (timer < 500)); - - bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), 0x2); - strptr = 0x7E00; - bcma_write32(pi->d11core, D11REGOFFS(tplatewrptr), strptr); - while (strptr < 0x8000) { - val = bcma_read32(pi->d11core, D11REGOFFS(tplatewrdata)); - imag = ((val >> 16) & 0x3ff); - real = ((val) & 0x3ff); - if (imag > 511) - imag -= 1024; - - if (real > 511) - real -= 1024; - - if (pi_lcn->lcnphy_iqcal_swp_dis) - ptr[(strptr - 0x7E00) / 4] = real; - else - ptr[(strptr - 0x7E00) / 4] = imag; - - if (clip_detect_algo) { - if (imag > thresh || imag < -thresh) { - strptr = 0x8000; - ptr[130] = 1; - } - } - - strptr += 4; - } - - write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl); - bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), curval2); - bcma_write16(pi->d11core, D11REGOFFS(psm_corectlsts), curval1); -} - -static void -wlc_lcnphy_a1(struct brcms_phy *pi, int cal_type, int num_levels, - int step_size_lg2) -{ - const struct lcnphy_spb_tone *phy_c1; - struct lcnphy_spb_tone phy_c2; - struct lcnphy_unsign16_struct phy_c3; - int phy_c4, phy_c5, k, l, j, phy_c6; - u16 phy_c7, phy_c8, phy_c9; - s16 phy_c10, phy_c11, phy_c12, phy_c13, phy_c14, phy_c15, phy_c16; - s16 *ptr, phy_c17; - s32 phy_c18, phy_c19; - u32 phy_c20, phy_c21; - bool phy_c22, phy_c23, phy_c24, phy_c25; - u16 phy_c26, phy_c27; - u16 phy_c28, phy_c29, phy_c30; - u16 phy_c31; - u16 *phy_c32; - phy_c21 = 0; - phy_c10 = phy_c13 = phy_c14 = phy_c8 = 0; - ptr = kmalloc(sizeof(s16) * 131, GFP_ATOMIC); - if (NULL == ptr) - return; - - phy_c32 = kmalloc(sizeof(u16) * 20, GFP_ATOMIC); - if (NULL == phy_c32) { - kfree(ptr); - return; - } - phy_c26 = read_phy_reg(pi, 0x6da); - phy_c27 = read_phy_reg(pi, 0x6db); - phy_c31 = read_radio_reg(pi, RADIO_2064_REG026); - write_phy_reg(pi, 0x93d, 0xC0); - - wlc_lcnphy_start_tx_tone(pi, 3750, 88, 0); - write_phy_reg(pi, 0x6da, 0xffff); - or_phy_reg(pi, 0x6db, 0x3); - - wlc_lcnphy_tx_iqlo_loopback(pi, phy_c32); - udelay(500); - phy_c28 = read_phy_reg(pi, 0x938); - phy_c29 = read_phy_reg(pi, 0x4d7); - phy_c30 = read_phy_reg(pi, 0x4d8); - or_phy_reg(pi, 0x938, 0x1 << 2); - or_phy_reg(pi, 0x4d7, 0x1 << 2); - or_phy_reg(pi, 0x4d7, 0x1 << 3); - mod_phy_reg(pi, 0x4d7, (0x7 << 12), 0x2 << 12); - or_phy_reg(pi, 0x4d8, 1 << 0); - or_phy_reg(pi, 0x4d8, 1 << 1); - mod_phy_reg(pi, 0x4d8, (0x3ff << 2), 0x23A << 2); - mod_phy_reg(pi, 0x4d8, (0x7 << 12), 0x7 << 12); - phy_c1 = &lcnphy_spb_tone_3750[0]; - phy_c4 = 32; - - if (num_levels == 0) { - if (cal_type != 0) - num_levels = 4; - else - num_levels = 9; - } - if (step_size_lg2 == 0) { - if (cal_type != 0) - step_size_lg2 = 3; - else - step_size_lg2 = 8; - } - - phy_c7 = (1 << step_size_lg2); - phy_c3 = wlc_lcnphy_get_cc(pi, cal_type); - phy_c15 = (s16) phy_c3.re; - phy_c16 = (s16) phy_c3.im; - if (cal_type == 2) { - if (phy_c3.re > 127) - phy_c15 = phy_c3.re - 256; - if (phy_c3.im > 127) - phy_c16 = phy_c3.im - 256; - } - wlc_lcnphy_set_cc(pi, cal_type, phy_c15, phy_c16); - udelay(20); - for (phy_c8 = 0; phy_c7 != 0 && phy_c8 < num_levels; phy_c8++) { - phy_c23 = true; - phy_c22 = false; - switch (cal_type) { - case 0: - phy_c10 = 511; - break; - case 2: - phy_c10 = 127; - break; - case 3: - phy_c10 = 15; - break; - case 4: - phy_c10 = 15; - break; - } - - phy_c9 = read_phy_reg(pi, 0x93d); - phy_c9 = 2 * phy_c9; - phy_c24 = false; - phy_c5 = 7; - phy_c25 = true; - while (1) { - write_radio_reg(pi, RADIO_2064_REG026, - (phy_c5 & 0x7) | ((phy_c5 & 0x7) << 4)); - udelay(50); - phy_c22 = false; - ptr[130] = 0; - wlc_lcnphy_samp_cap(pi, 1, phy_c9, &ptr[0], 2); - if (ptr[130] == 1) - phy_c22 = true; - if (phy_c22) - phy_c5 -= 1; - if ((phy_c22 != phy_c24) && (!phy_c25)) - break; - if (!phy_c22) - phy_c5 += 1; - if (phy_c5 <= 0 || phy_c5 >= 7) - break; - phy_c24 = phy_c22; - phy_c25 = false; - } - - if (phy_c5 < 0) - phy_c5 = 0; - else if (phy_c5 > 7) - phy_c5 = 7; - - for (k = -phy_c7; k <= phy_c7; k += phy_c7) { - for (l = -phy_c7; l <= phy_c7; l += phy_c7) { - phy_c11 = phy_c15 + k; - phy_c12 = phy_c16 + l; - - if (phy_c11 < -phy_c10) - phy_c11 = -phy_c10; - else if (phy_c11 > phy_c10) - phy_c11 = phy_c10; - if (phy_c12 < -phy_c10) - phy_c12 = -phy_c10; - else if (phy_c12 > phy_c10) - phy_c12 = phy_c10; - wlc_lcnphy_set_cc(pi, cal_type, phy_c11, - phy_c12); - udelay(20); - wlc_lcnphy_samp_cap(pi, 0, 0, ptr, 2); - - phy_c18 = 0; - phy_c19 = 0; - for (j = 0; j < 128; j++) { - if (cal_type != 0) - phy_c6 = j % phy_c4; - else - phy_c6 = (2 * j) % phy_c4; - - phy_c2.re = phy_c1[phy_c6].re; - phy_c2.im = phy_c1[phy_c6].im; - phy_c17 = ptr[j]; - phy_c18 = phy_c18 + phy_c17 * phy_c2.re; - phy_c19 = phy_c19 + phy_c17 * phy_c2.im; - } - - phy_c18 = phy_c18 >> 10; - phy_c19 = phy_c19 >> 10; - phy_c20 = ((phy_c18 * phy_c18) + - (phy_c19 * phy_c19)); - - if (phy_c23 || phy_c20 < phy_c21) { - phy_c21 = phy_c20; - phy_c13 = phy_c11; - phy_c14 = phy_c12; - } - phy_c23 = false; - } - } - phy_c23 = true; - phy_c15 = phy_c13; - phy_c16 = phy_c14; - phy_c7 = phy_c7 >> 1; - wlc_lcnphy_set_cc(pi, cal_type, phy_c15, phy_c16); - udelay(20); - } - goto cleanup; -cleanup: - wlc_lcnphy_tx_iqlo_loopback_cleanup(pi, phy_c32); - wlc_lcnphy_stop_tx_tone(pi); - write_phy_reg(pi, 0x6da, phy_c26); - write_phy_reg(pi, 0x6db, phy_c27); - write_phy_reg(pi, 0x938, phy_c28); - write_phy_reg(pi, 0x4d7, phy_c29); - write_phy_reg(pi, 0x4d8, phy_c30); - write_radio_reg(pi, RADIO_2064_REG026, phy_c31); - - kfree(phy_c32); - kfree(ptr); -} - -void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b) -{ - u16 iqcc[2]; - struct phytbl_info tab; - - tab.tbl_ptr = iqcc; - tab.tbl_len = 2; - tab.tbl_id = 0; - tab.tbl_offset = 80; - tab.tbl_width = 16; - wlc_lcnphy_read_table(pi, &tab); - - *a = iqcc[0]; - *b = iqcc[1]; -} - -static void wlc_lcnphy_tx_iqlo_soft_cal_full(struct brcms_phy *pi) -{ - struct lcnphy_unsign16_struct iqcc0, locc2, locc3, locc4; - - wlc_lcnphy_set_cc(pi, 0, 0, 0); - wlc_lcnphy_set_cc(pi, 2, 0, 0); - wlc_lcnphy_set_cc(pi, 3, 0, 0); - wlc_lcnphy_set_cc(pi, 4, 0, 0); - - wlc_lcnphy_a1(pi, 4, 0, 0); - wlc_lcnphy_a1(pi, 3, 0, 0); - wlc_lcnphy_a1(pi, 2, 3, 2); - wlc_lcnphy_a1(pi, 0, 5, 8); - wlc_lcnphy_a1(pi, 2, 2, 1); - wlc_lcnphy_a1(pi, 0, 4, 3); - - iqcc0 = wlc_lcnphy_get_cc(pi, 0); - locc2 = wlc_lcnphy_get_cc(pi, 2); - locc3 = wlc_lcnphy_get_cc(pi, 3); - locc4 = wlc_lcnphy_get_cc(pi, 4); -} - -u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi) -{ - struct phytbl_info tab; - u16 didq; - - tab.tbl_id = 0; - tab.tbl_width = 16; - tab.tbl_ptr = &didq; - tab.tbl_len = 1; - tab.tbl_offset = 85; - wlc_lcnphy_read_table(pi, &tab); - - return didq; -} - -static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi) -{ - - struct lcnphy_txgains target_gains, old_gains; - u8 save_bb_mult; - u16 a, b, didq, save_pa_gain = 0; - uint idx, SAVE_txpwrindex = 0xFF; - u32 val; - u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); - struct phytbl_info tab; - u8 ei0, eq0, fi0, fq0; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - wlc_lcnphy_get_tx_gain(pi, &old_gains); - save_pa_gain = wlc_lcnphy_get_pa_gain(pi); - - save_bb_mult = wlc_lcnphy_get_bbmult(pi); - - if (SAVE_txpwrctrl == LCNPHY_TX_PWR_CTRL_OFF) - SAVE_txpwrindex = wlc_lcnphy_get_current_tx_pwr_idx(pi); - - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); - - target_gains.gm_gain = 7; - target_gains.pga_gain = 0; - target_gains.pad_gain = 21; - target_gains.dac_gain = 0; - wlc_lcnphy_set_tx_gain(pi, &target_gains); - - if (LCNREV_IS(pi->pubpi.phy_rev, 1) || pi_lcn->lcnphy_hw_iqcal_en) { - - wlc_lcnphy_set_tx_pwr_by_index(pi, 30); - - wlc_lcnphy_tx_iqlo_cal(pi, &target_gains, - (pi_lcn-> - lcnphy_recal ? LCNPHY_CAL_RECAL : - LCNPHY_CAL_FULL), false); - } else { - wlc_lcnphy_set_tx_pwr_by_index(pi, 16); - wlc_lcnphy_tx_iqlo_soft_cal_full(pi); - } - - wlc_lcnphy_get_radio_loft(pi, &ei0, &eq0, &fi0, &fq0); - if ((abs((s8) fi0) == 15) && (abs((s8) fq0) == 15)) { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - target_gains.gm_gain = 255; - target_gains.pga_gain = 255; - target_gains.pad_gain = 0xf0; - target_gains.dac_gain = 0; - } else { - target_gains.gm_gain = 7; - target_gains.pga_gain = 45; - target_gains.pad_gain = 186; - target_gains.dac_gain = 0; - } - - if (LCNREV_IS(pi->pubpi.phy_rev, 1) - || pi_lcn->lcnphy_hw_iqcal_en) { - - target_gains.pga_gain = 0; - target_gains.pad_gain = 30; - wlc_lcnphy_set_tx_pwr_by_index(pi, 16); - wlc_lcnphy_tx_iqlo_cal(pi, &target_gains, - LCNPHY_CAL_FULL, false); - } else { - wlc_lcnphy_tx_iqlo_soft_cal_full(pi); - } - } - - wlc_lcnphy_get_tx_iqcc(pi, &a, &b); - - didq = wlc_lcnphy_get_tx_locc(pi); - - tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; - tab.tbl_width = 32; - tab.tbl_ptr = &val; - - tab.tbl_len = 1; - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; - - for (idx = 0; idx < 128; idx++) { - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + idx; - - wlc_lcnphy_read_table(pi, &tab); - val = (val & 0xfff00000) | - ((u32) (a & 0x3FF) << 10) | (b & 0x3ff); - wlc_lcnphy_write_table(pi, &tab); - - val = didq; - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_LO_OFFSET + idx; - wlc_lcnphy_write_table(pi, &tab); - } - - pi_lcn->lcnphy_cal_results.txiqlocal_a = a; - pi_lcn->lcnphy_cal_results.txiqlocal_b = b; - pi_lcn->lcnphy_cal_results.txiqlocal_didq = didq; - pi_lcn->lcnphy_cal_results.txiqlocal_ei0 = ei0; - pi_lcn->lcnphy_cal_results.txiqlocal_eq0 = eq0; - pi_lcn->lcnphy_cal_results.txiqlocal_fi0 = fi0; - pi_lcn->lcnphy_cal_results.txiqlocal_fq0 = fq0; - - wlc_lcnphy_set_bbmult(pi, save_bb_mult); - wlc_lcnphy_set_pa_gain(pi, save_pa_gain); - wlc_lcnphy_set_tx_gain(pi, &old_gains); - - if (SAVE_txpwrctrl != LCNPHY_TX_PWR_CTRL_OFF) - wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl); - else - wlc_lcnphy_set_tx_pwr_by_index(pi, SAVE_txpwrindex); -} - -s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode) -{ - u16 tempsenseval1, tempsenseval2; - s16 avg = 0; - bool suspend = false; - - if (mode == 1) { - suspend = (0 == (bcma_read32(pi->d11core, - D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!suspend) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - wlc_lcnphy_vbat_temp_sense_setup(pi, TEMPSENSE); - } - tempsenseval1 = read_phy_reg(pi, 0x476) & 0x1FF; - tempsenseval2 = read_phy_reg(pi, 0x477) & 0x1FF; - - if (tempsenseval1 > 255) - avg = (s16) (tempsenseval1 - 512); - else - avg = (s16) tempsenseval1; - - if (tempsenseval2 > 255) - avg += (s16) (tempsenseval2 - 512); - else - avg += (s16) tempsenseval2; - - avg /= 2; - - if (mode == 1) { - - mod_phy_reg(pi, 0x448, (0x1 << 14), (1) << 14); - - udelay(100); - mod_phy_reg(pi, 0x448, (0x1 << 14), (0) << 14); - - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); - } - return avg; -} - -u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode) -{ - u16 tempsenseval1, tempsenseval2; - s32 avg = 0; - bool suspend = false; - u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - if (mode == 1) { - suspend = (0 == (bcma_read32(pi->d11core, - D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!suspend) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - wlc_lcnphy_vbat_temp_sense_setup(pi, TEMPSENSE); - } - tempsenseval1 = read_phy_reg(pi, 0x476) & 0x1FF; - tempsenseval2 = read_phy_reg(pi, 0x477) & 0x1FF; - - if (tempsenseval1 > 255) - avg = (int)(tempsenseval1 - 512); - else - avg = (int)tempsenseval1; - - if (pi_lcn->lcnphy_tempsense_option == 1 || pi->hwpwrctrl_capable) { - if (tempsenseval2 > 255) - avg = (int)(avg - tempsenseval2 + 512); - else - avg = (int)(avg - tempsenseval2); - } else { - if (tempsenseval2 > 255) - avg = (int)(avg + tempsenseval2 - 512); - else - avg = (int)(avg + tempsenseval2); - avg = avg / 2; - } - if (avg < 0) - avg = avg + 512; - - if (pi_lcn->lcnphy_tempsense_option == 2) - avg = tempsenseval1; - - if (mode) - wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl); - - if (mode == 1) { - - mod_phy_reg(pi, 0x448, (0x1 << 14), (1) << 14); - - udelay(100); - mod_phy_reg(pi, 0x448, (0x1 << 14), (0) << 14); - - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); - } - return (u16) avg; -} - -s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode) -{ - s32 degree = wlc_lcnphy_tempsense_new(pi, mode); - degree = - ((degree << - 10) + LCN_TEMPSENSE_OFFSET + (LCN_TEMPSENSE_DEN >> 1)) - / LCN_TEMPSENSE_DEN; - return (s8) degree; -} - -s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode) -{ - u16 vbatsenseval; - s32 avg = 0; - bool suspend = false; - - if (mode == 1) { - suspend = (0 == (bcma_read32(pi->d11core, - D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!suspend) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - wlc_lcnphy_vbat_temp_sense_setup(pi, VBATSENSE); - } - - vbatsenseval = read_phy_reg(pi, 0x475) & 0x1FF; - - if (vbatsenseval > 255) - avg = (s32) (vbatsenseval - 512); - else - avg = (s32) vbatsenseval; - - avg = (avg * LCN_VBAT_SCALE_NOM + - (LCN_VBAT_SCALE_DEN >> 1)) / LCN_VBAT_SCALE_DEN; - - if (mode == 1) { - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); - } - return (s8) avg; -} - -static void wlc_lcnphy_afe_clk_init(struct brcms_phy *pi, u8 mode) -{ - u8 phybw40; - phybw40 = CHSPEC_IS40(pi->radio_chanspec); - - mod_phy_reg(pi, 0x6d1, (0x1 << 7), (1) << 7); - - if (((mode == AFE_CLK_INIT_MODE_PAPD) && (phybw40 == 0)) || - (mode == AFE_CLK_INIT_MODE_TXRX2X)) - write_phy_reg(pi, 0x6d0, 0x7); - - wlc_lcnphy_toggle_afe_pwdn(pi); -} - -static void wlc_lcnphy_temp_adj(struct brcms_phy *pi) -{ -} - -static void wlc_lcnphy_glacial_timer_based_cal(struct brcms_phy *pi) -{ - bool suspend; - s8 index; - u16 SAVE_pwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!suspend) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - wlc_lcnphy_deaf_mode(pi, true); - pi->phy_lastcal = pi->sh->now; - pi->phy_forcecal = false; - index = pi_lcn->lcnphy_current_index; - - wlc_lcnphy_txpwrtbl_iqlo_cal(pi); - - wlc_lcnphy_set_tx_pwr_by_index(pi, index); - wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_pwrctrl); - wlc_lcnphy_deaf_mode(pi, false); - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); - -} - -static void wlc_lcnphy_periodic_cal(struct brcms_phy *pi) -{ - bool suspend, full_cal; - const struct lcnphy_rx_iqcomp *rx_iqcomp; - int rx_iqcomp_sz; - u16 SAVE_pwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); - s8 index; - struct phytbl_info tab; - s32 a1, b0, b1; - s32 tssi, pwr, maxtargetpwr, mintargetpwr; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - pi->phy_lastcal = pi->sh->now; - pi->phy_forcecal = false; - full_cal = - (pi_lcn->lcnphy_full_cal_channel != - CHSPEC_CHANNEL(pi->radio_chanspec)); - pi_lcn->lcnphy_full_cal_channel = CHSPEC_CHANNEL(pi->radio_chanspec); - index = pi_lcn->lcnphy_current_index; - - suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!suspend) { - wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, 10000); - wlapi_suspend_mac_and_wait(pi->sh->physhim); - } - - wlc_lcnphy_deaf_mode(pi, true); - - wlc_lcnphy_txpwrtbl_iqlo_cal(pi); - - rx_iqcomp = lcnphy_rx_iqcomp_table_rev0; - rx_iqcomp_sz = ARRAY_SIZE(lcnphy_rx_iqcomp_table_rev0); - - if (LCNREV_IS(pi->pubpi.phy_rev, 1)) - wlc_lcnphy_rx_iq_cal(pi, NULL, 0, true, false, 1, 40); - else - wlc_lcnphy_rx_iq_cal(pi, NULL, 0, true, false, 1, 127); - - if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) { - - wlc_lcnphy_idle_tssi_est((struct brcms_phy_pub *) pi); - - b0 = pi->txpa_2g[0]; - b1 = pi->txpa_2g[1]; - a1 = pi->txpa_2g[2]; - maxtargetpwr = wlc_lcnphy_tssi2dbm(10, a1, b0, b1); - mintargetpwr = wlc_lcnphy_tssi2dbm(125, a1, b0, b1); - - tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; - tab.tbl_width = 32; - tab.tbl_ptr = &pwr; - tab.tbl_len = 1; - tab.tbl_offset = 0; - for (tssi = 0; tssi < 128; tssi++) { - pwr = wlc_lcnphy_tssi2dbm(tssi, a1, b0, b1); - pwr = (pwr < mintargetpwr) ? mintargetpwr : pwr; - wlc_lcnphy_write_table(pi, &tab); - tab.tbl_offset++; - } - } - - wlc_lcnphy_set_tx_pwr_by_index(pi, index); - wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_pwrctrl); - wlc_lcnphy_deaf_mode(pi, false); - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); -} - -void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode) -{ - u16 temp_new; - int temp1, temp2, temp_diff; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - switch (mode) { - case PHY_PERICAL_CHAN: - break; - case PHY_FULLCAL: - wlc_lcnphy_periodic_cal(pi); - break; - case PHY_PERICAL_PHYINIT: - wlc_lcnphy_periodic_cal(pi); - break; - case PHY_PERICAL_WATCHDOG: - if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { - temp_new = wlc_lcnphy_tempsense(pi, 0); - temp1 = LCNPHY_TEMPSENSE(temp_new); - temp2 = LCNPHY_TEMPSENSE(pi_lcn->lcnphy_cal_temper); - temp_diff = temp1 - temp2; - if ((pi_lcn->lcnphy_cal_counter > 90) || - (temp_diff > 60) || (temp_diff < -60)) { - wlc_lcnphy_glacial_timer_based_cal(pi); - wlc_2064_vco_cal(pi); - pi_lcn->lcnphy_cal_temper = temp_new; - pi_lcn->lcnphy_cal_counter = 0; - } else - pi_lcn->lcnphy_cal_counter++; - } - break; - case LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL: - if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) - wlc_lcnphy_tx_power_adjustment( - (struct brcms_phy_pub *) pi); - break; - } -} - -void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, s8 *cck_pwr) -{ - s8 cck_offset; - u16 status; - status = (read_phy_reg(pi, 0x4ab)); - if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi) && - (status & (0x1 << 15))) { - *ofdm_pwr = (s8) (((read_phy_reg(pi, 0x4ab) & (0x1ff << 0)) - >> 0) >> 1); - - if (wlc_phy_tpc_isenabled_lcnphy(pi)) - cck_offset = pi->tx_power_offset[TXP_FIRST_CCK]; - else - cck_offset = 0; - - *cck_pwr = *ofdm_pwr + cck_offset; - } else { - *cck_pwr = 0; - *ofdm_pwr = 0; - } -} - -void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi) -{ - return; - -} - -void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi) -{ - s8 index; - u16 index2; - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); - if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) && - SAVE_txpwrctrl) { - index = wlc_lcnphy_tempcompensated_txpwrctrl(pi); - index2 = (u16) (index * 2); - mod_phy_reg(pi, 0x4a9, (0x1ff << 0), (index2) << 0); - - pi_lcn->lcnphy_current_index = - (s8)((read_phy_reg(pi, 0x4a9) & 0xFF) / 2); - } -} - -static void -wlc_lcnphy_load_tx_gain_table(struct brcms_phy *pi, - const struct lcnphy_tx_gain_tbl_entry *gain_table) -{ - u32 j; - struct phytbl_info tab; - u32 val; - u16 pa_gain; - u16 gm_gain; - - if (pi->sh->boardflags & BFL_FEM) - pa_gain = 0x10; - else - pa_gain = 0x60; - tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; - tab.tbl_width = 32; - tab.tbl_len = 1; - tab.tbl_ptr = &val; - - /* fixed gm_gain value for iPA */ - gm_gain = 15; - for (j = 0; j < 128; j++) { - if (pi->sh->boardflags & BFL_FEM) - gm_gain = gain_table[j].gm; - val = (((u32) pa_gain << 24) | - (gain_table[j].pad << 16) | - (gain_table[j].pga << 8) | gm_gain); - - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + j; - wlc_lcnphy_write_table(pi, &tab); - - val = (gain_table[j].dac << 28) | (gain_table[j].bb_mult << 20); - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + j; - wlc_lcnphy_write_table(pi, &tab); - } -} - -static void wlc_lcnphy_load_rfpower(struct brcms_phy *pi) -{ - struct phytbl_info tab; - u32 val, bbmult, rfgain; - u8 index; - u8 scale_factor = 1; - s16 temp, temp1, temp2, qQ, qQ1, qQ2, shift; - - tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; - tab.tbl_width = 32; - tab.tbl_len = 1; - - for (index = 0; index < 128; index++) { - tab.tbl_ptr = &bbmult; - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + index; - wlc_lcnphy_read_table(pi, &tab); - bbmult = bbmult >> 20; - - tab.tbl_ptr = &rfgain; - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + index; - wlc_lcnphy_read_table(pi, &tab); - - qm_log10((s32) (bbmult), 0, &temp1, &qQ1); - qm_log10((s32) (1 << 6), 0, &temp2, &qQ2); - - if (qQ1 < qQ2) { - temp2 = qm_shr16(temp2, qQ2 - qQ1); - qQ = qQ1; - } else { - temp1 = qm_shr16(temp1, qQ1 - qQ2); - qQ = qQ2; - } - temp = qm_sub16(temp1, temp2); - - if (qQ >= 4) - shift = qQ - 4; - else - shift = 4 - qQ; - - val = (((index << shift) + (5 * temp) + - (1 << (scale_factor + shift - 3))) >> (scale_factor + - shift - 2)); - - tab.tbl_ptr = &val; - tab.tbl_offset = LCNPHY_TX_PWR_CTRL_PWR_OFFSET + index; - wlc_lcnphy_write_table(pi, &tab); - } -} - -static void wlc_lcnphy_bu_tweaks(struct brcms_phy *pi) -{ - or_phy_reg(pi, 0x805, 0x1); - - mod_phy_reg(pi, 0x42f, (0x7 << 0), (0x3) << 0); - - mod_phy_reg(pi, 0x030, (0x7 << 0), (0x3) << 0); - - write_phy_reg(pi, 0x414, 0x1e10); - write_phy_reg(pi, 0x415, 0x0640); - - mod_phy_reg(pi, 0x4df, (0xff << 8), -9 << 8); - - or_phy_reg(pi, 0x44a, 0x44); - write_phy_reg(pi, 0x44a, 0x80); - mod_phy_reg(pi, 0x434, (0xff << 0), (0xFD) << 0); - - mod_phy_reg(pi, 0x420, (0xff << 0), (16) << 0); - - if (!(pi->sh->boardrev < 0x1204)) - mod_radio_reg(pi, RADIO_2064_REG09B, 0xF0, 0xF0); - - write_phy_reg(pi, 0x7d6, 0x0902); - mod_phy_reg(pi, 0x429, (0xf << 0), (0x9) << 0); - - mod_phy_reg(pi, 0x429, (0x3f << 4), (0xe) << 4); - - if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { - mod_phy_reg(pi, 0x423, (0xff << 0), (0x46) << 0); - - mod_phy_reg(pi, 0x411, (0xff << 0), (1) << 0); - - mod_phy_reg(pi, 0x434, (0xff << 0), (0xFF) << 0); - - mod_phy_reg(pi, 0x656, (0xf << 0), (2) << 0); - - mod_phy_reg(pi, 0x44d, (0x1 << 2), (1) << 2); - - mod_radio_reg(pi, RADIO_2064_REG0F7, 0x4, 0x4); - mod_radio_reg(pi, RADIO_2064_REG0F1, 0x3, 0); - mod_radio_reg(pi, RADIO_2064_REG0F2, 0xF8, 0x90); - mod_radio_reg(pi, RADIO_2064_REG0F3, 0x3, 0x2); - mod_radio_reg(pi, RADIO_2064_REG0F3, 0xf0, 0xa0); - - mod_radio_reg(pi, RADIO_2064_REG11F, 0x2, 0x2); - - wlc_lcnphy_clear_tx_power_offsets(pi); - mod_phy_reg(pi, 0x4d0, (0x1ff << 6), (10) << 6); - - } -} - -static void wlc_lcnphy_rcal(struct brcms_phy *pi) -{ - u8 rcal_value; - - and_radio_reg(pi, RADIO_2064_REG05B, 0xfD); - - or_radio_reg(pi, RADIO_2064_REG004, 0x40); - or_radio_reg(pi, RADIO_2064_REG120, 0x10); - - or_radio_reg(pi, RADIO_2064_REG078, 0x80); - or_radio_reg(pi, RADIO_2064_REG129, 0x02); - - or_radio_reg(pi, RADIO_2064_REG057, 0x01); - - or_radio_reg(pi, RADIO_2064_REG05B, 0x02); - mdelay(5); - SPINWAIT(!wlc_radio_2064_rcal_done(pi), 10 * 1000 * 1000); - - if (wlc_radio_2064_rcal_done(pi)) { - rcal_value = (u8) read_radio_reg(pi, RADIO_2064_REG05C); - rcal_value = rcal_value & 0x1f; - } - - and_radio_reg(pi, RADIO_2064_REG05B, 0xfD); - - and_radio_reg(pi, RADIO_2064_REG057, 0xFE); -} - -static void wlc_lcnphy_rc_cal(struct brcms_phy *pi) -{ - u8 dflt_rc_cal_val; - u16 flt_val; - - dflt_rc_cal_val = 7; - if (LCNREV_IS(pi->pubpi.phy_rev, 1)) - dflt_rc_cal_val = 11; - flt_val = - (dflt_rc_cal_val << 10) | (dflt_rc_cal_val << 5) | - (dflt_rc_cal_val); - write_phy_reg(pi, 0x933, flt_val); - write_phy_reg(pi, 0x934, flt_val); - write_phy_reg(pi, 0x935, flt_val); - write_phy_reg(pi, 0x936, flt_val); - write_phy_reg(pi, 0x937, (flt_val & 0x1FF)); - - return; -} - -static void wlc_radio_2064_init(struct brcms_phy *pi) -{ - u32 i; - const struct lcnphy_radio_regs *lcnphyregs = NULL; - - lcnphyregs = lcnphy_radio_regs_2064; - - for (i = 0; lcnphyregs[i].address != 0xffff; i++) - if (CHSPEC_IS5G(pi->radio_chanspec) && lcnphyregs[i].do_init_a) - write_radio_reg(pi, - ((lcnphyregs[i].address & 0x3fff) | - RADIO_DEFAULT_CORE), - (u16) lcnphyregs[i].init_a); - else if (lcnphyregs[i].do_init_g) - write_radio_reg(pi, - ((lcnphyregs[i].address & 0x3fff) | - RADIO_DEFAULT_CORE), - (u16) lcnphyregs[i].init_g); - - write_radio_reg(pi, RADIO_2064_REG032, 0x62); - write_radio_reg(pi, RADIO_2064_REG033, 0x19); - - write_radio_reg(pi, RADIO_2064_REG090, 0x10); - - write_radio_reg(pi, RADIO_2064_REG010, 0x00); - - if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { - - write_radio_reg(pi, RADIO_2064_REG060, 0x7f); - write_radio_reg(pi, RADIO_2064_REG061, 0x72); - write_radio_reg(pi, RADIO_2064_REG062, 0x7f); - } - - write_radio_reg(pi, RADIO_2064_REG01D, 0x02); - write_radio_reg(pi, RADIO_2064_REG01E, 0x06); - - mod_phy_reg(pi, 0x4ea, (0x7 << 0), 0 << 0); - - mod_phy_reg(pi, 0x4ea, (0x7 << 3), 1 << 3); - - mod_phy_reg(pi, 0x4ea, (0x7 << 6), 2 << 6); - - mod_phy_reg(pi, 0x4ea, (0x7 << 9), 3 << 9); - - mod_phy_reg(pi, 0x4ea, (0x7 << 12), 4 << 12); - - write_phy_reg(pi, 0x4ea, 0x4688); - - if (pi->sh->boardflags & BFL_FEM) - mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0); - else - mod_phy_reg(pi, 0x4eb, (0x7 << 0), 3 << 0); - - mod_phy_reg(pi, 0x4eb, (0x7 << 6), 0 << 6); - - mod_phy_reg(pi, 0x46a, (0xffff << 0), 25 << 0); - - wlc_lcnphy_set_tx_locc(pi, 0); - - wlc_lcnphy_rcal(pi); - - wlc_lcnphy_rc_cal(pi); - - if (!(pi->sh->boardflags & BFL_FEM)) { - write_radio_reg(pi, RADIO_2064_REG032, 0x6f); - write_radio_reg(pi, RADIO_2064_REG033, 0x19); - write_radio_reg(pi, RADIO_2064_REG039, 0xe); - } - -} - -static void wlc_lcnphy_radio_init(struct brcms_phy *pi) -{ - wlc_radio_2064_init(pi); -} - -static void wlc_lcnphy_tbl_init(struct brcms_phy *pi) -{ - uint idx; - u8 phybw40; - struct phytbl_info tab; - const struct phytbl_info *tb; - u32 val; - - phybw40 = CHSPEC_IS40(pi->radio_chanspec); - - for (idx = 0; idx < dot11lcnphytbl_info_sz_rev0; idx++) - wlc_lcnphy_write_table(pi, &dot11lcnphytbl_info_rev0[idx]); - - if (pi->sh->boardflags & BFL_FEM_BT) { - tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; - tab.tbl_width = 16; - tab.tbl_ptr = &val; - tab.tbl_len = 1; - val = 100; - tab.tbl_offset = 4; - wlc_lcnphy_write_table(pi, &tab); - } - - if (!(pi->sh->boardflags & BFL_FEM)) { - tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; - tab.tbl_width = 16; - tab.tbl_ptr = &val; - tab.tbl_len = 1; - - val = 150; - tab.tbl_offset = 0; - wlc_lcnphy_write_table(pi, &tab); - - val = 220; - tab.tbl_offset = 1; - wlc_lcnphy_write_table(pi, &tab); - } - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if (pi->sh->boardflags & BFL_FEM) - wlc_lcnphy_load_tx_gain_table( - pi, - dot11lcnphy_2GHz_extPA_gaintable_rev0); - else - wlc_lcnphy_load_tx_gain_table( - pi, - dot11lcnphy_2GHz_gaintable_rev0); - } - - if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { - int l; - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - l = dot11lcnphytbl_rx_gain_info_2G_rev2_sz; - if (pi->sh->boardflags & BFL_EXTLNA) - tb = dot11lcnphytbl_rx_gain_info_extlna_2G_rev2; - else - tb = dot11lcnphytbl_rx_gain_info_2G_rev2; - } else { - l = dot11lcnphytbl_rx_gain_info_5G_rev2_sz; - if (pi->sh->boardflags & BFL_EXTLNA_5GHz) - tb = dot11lcnphytbl_rx_gain_info_extlna_5G_rev2; - else - tb = dot11lcnphytbl_rx_gain_info_5G_rev2; - } - - for (idx = 0; idx < l; idx++) - wlc_lcnphy_write_table(pi, &tb[idx]); - } - - if (pi->sh->boardflags & BFL_FEM) { - if (pi->sh->boardflags & BFL_FEM_BT) { - if (pi->sh->boardrev < 0x1250) - tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa; - else - tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250; - } else { - tb = &dot11lcn_sw_ctrl_tbl_info_4313_epa; - } - } else { - if (pi->sh->boardflags & BFL_FEM_BT) - tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa; - else - tb = &dot11lcn_sw_ctrl_tbl_info_4313; - } - wlc_lcnphy_write_table(pi, tb); - wlc_lcnphy_load_rfpower(pi); - - wlc_lcnphy_clear_papd_comptable(pi); -} - -static void wlc_lcnphy_rev0_baseband_init(struct brcms_phy *pi) -{ - u16 afectrl1; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - write_radio_reg(pi, RADIO_2064_REG11C, 0x0); - - write_phy_reg(pi, 0x43b, 0x0); - write_phy_reg(pi, 0x43c, 0x0); - write_phy_reg(pi, 0x44c, 0x0); - write_phy_reg(pi, 0x4e6, 0x0); - write_phy_reg(pi, 0x4f9, 0x0); - write_phy_reg(pi, 0x4b0, 0x0); - write_phy_reg(pi, 0x938, 0x0); - write_phy_reg(pi, 0x4b0, 0x0); - write_phy_reg(pi, 0x44e, 0); - - or_phy_reg(pi, 0x567, 0x03); - - or_phy_reg(pi, 0x44a, 0x44); - write_phy_reg(pi, 0x44a, 0x80); - - if (!(pi->sh->boardflags & BFL_FEM)) - wlc_lcnphy_set_tx_pwr_by_index(pi, 52); - - if (0) { - afectrl1 = 0; - afectrl1 = (u16) ((pi_lcn->lcnphy_rssi_vf) | - (pi_lcn->lcnphy_rssi_vc << 4) | - (pi_lcn->lcnphy_rssi_gs << 10)); - write_phy_reg(pi, 0x43e, afectrl1); - } - - mod_phy_reg(pi, 0x634, (0xff << 0), 0xC << 0); - if (pi->sh->boardflags & BFL_FEM) { - mod_phy_reg(pi, 0x634, (0xff << 0), 0xA << 0); - - write_phy_reg(pi, 0x910, 0x1); - } - - mod_phy_reg(pi, 0x448, (0x3 << 8), 1 << 8); - mod_phy_reg(pi, 0x608, (0xff << 0), 0x17 << 0); - mod_phy_reg(pi, 0x604, (0x7ff << 0), 0x3EA << 0); - -} - -static void wlc_lcnphy_rev2_baseband_init(struct brcms_phy *pi) -{ - if (CHSPEC_IS5G(pi->radio_chanspec)) { - mod_phy_reg(pi, 0x416, (0xff << 0), 80 << 0); - mod_phy_reg(pi, 0x416, (0xff << 8), 80 << 8); - } -} - -static void wlc_lcnphy_agc_temp_init(struct brcms_phy *pi) -{ - s16 temp; - struct phytbl_info tab; - u32 tableBuffer[2]; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - temp = (s16) read_phy_reg(pi, 0x4df); - pi_lcn->lcnphy_ofdmgainidxtableoffset = (temp & (0xff << 0)) >> 0; - - if (pi_lcn->lcnphy_ofdmgainidxtableoffset > 127) - pi_lcn->lcnphy_ofdmgainidxtableoffset -= 256; - - pi_lcn->lcnphy_dsssgainidxtableoffset = (temp & (0xff << 8)) >> 8; - - if (pi_lcn->lcnphy_dsssgainidxtableoffset > 127) - pi_lcn->lcnphy_dsssgainidxtableoffset -= 256; - - tab.tbl_ptr = tableBuffer; - tab.tbl_len = 2; - tab.tbl_id = 17; - tab.tbl_offset = 59; - tab.tbl_width = 32; - wlc_lcnphy_read_table(pi, &tab); - - if (tableBuffer[0] > 63) - tableBuffer[0] -= 128; - pi_lcn->lcnphy_tr_R_gain_val = tableBuffer[0]; - - if (tableBuffer[1] > 63) - tableBuffer[1] -= 128; - pi_lcn->lcnphy_tr_T_gain_val = tableBuffer[1]; - - temp = (s16) (read_phy_reg(pi, 0x434) & (0xff << 0)); - if (temp > 127) - temp -= 256; - pi_lcn->lcnphy_input_pwr_offset_db = (s8) temp; - - pi_lcn->lcnphy_Med_Low_Gain_db = - (read_phy_reg(pi, 0x424) & (0xff << 8)) >> 8; - pi_lcn->lcnphy_Very_Low_Gain_db = - (read_phy_reg(pi, 0x425) & (0xff << 0)) >> 0; - - tab.tbl_ptr = tableBuffer; - tab.tbl_len = 2; - tab.tbl_id = LCNPHY_TBL_ID_GAIN_IDX; - tab.tbl_offset = 28; - tab.tbl_width = 32; - wlc_lcnphy_read_table(pi, &tab); - - pi_lcn->lcnphy_gain_idx_14_lowword = tableBuffer[0]; - pi_lcn->lcnphy_gain_idx_14_hiword = tableBuffer[1]; - -} - -static void wlc_lcnphy_baseband_init(struct brcms_phy *pi) -{ - - wlc_lcnphy_tbl_init(pi); - wlc_lcnphy_rev0_baseband_init(pi); - if (LCNREV_IS(pi->pubpi.phy_rev, 2)) - wlc_lcnphy_rev2_baseband_init(pi); - wlc_lcnphy_bu_tweaks(pi); -} - -void wlc_phy_init_lcnphy(struct brcms_phy *pi) -{ - u8 phybw40; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - phybw40 = CHSPEC_IS40(pi->radio_chanspec); - - pi_lcn->lcnphy_cal_counter = 0; - pi_lcn->lcnphy_cal_temper = pi_lcn->lcnphy_rawtempsense; - - or_phy_reg(pi, 0x44a, 0x80); - and_phy_reg(pi, 0x44a, 0x7f); - - wlc_lcnphy_afe_clk_init(pi, AFE_CLK_INIT_MODE_TXRX2X); - - write_phy_reg(pi, 0x60a, 160); - - write_phy_reg(pi, 0x46a, 25); - - wlc_lcnphy_baseband_init(pi); - - wlc_lcnphy_radio_init(pi); - - if (CHSPEC_IS2G(pi->radio_chanspec)) - wlc_lcnphy_tx_pwr_ctrl_init((struct brcms_phy_pub *) pi); - - wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, pi->radio_chanspec); - - bcma_chipco_regctl_maskset(&pi->d11core->bus->drv_cc, 0, ~0xf, 0x9); - - bcma_chipco_chipctl_maskset(&pi->d11core->bus->drv_cc, 0, 0x0, - 0x03CDDDDD); - - if ((pi->sh->boardflags & BFL_FEM) - && wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) - wlc_lcnphy_set_tx_pwr_by_index(pi, FIXED_TXPWR); - - wlc_lcnphy_agc_temp_init(pi); - - wlc_lcnphy_temp_adj(pi); - - mod_phy_reg(pi, 0x448, (0x1 << 14), (1) << 14); - - udelay(100); - mod_phy_reg(pi, 0x448, (0x1 << 14), (0) << 14); - - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_HW); - pi_lcn->lcnphy_noise_samples = LCNPHY_NOISE_SAMPLES_DEFAULT; - wlc_lcnphy_calib_modes(pi, PHY_PERICAL_PHYINIT); -} - -static bool wlc_phy_txpwr_srom_read_lcnphy(struct brcms_phy *pi) -{ - s8 txpwr = 0; - int i; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - struct ssb_sprom *sprom = &pi->d11core->bus->sprom; - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - u16 cckpo = 0; - u32 offset_ofdm, offset_mcs; - - pi_lcn->lcnphy_tr_isolation_mid = sprom->fem.ghz2.tr_iso; - - pi_lcn->lcnphy_rx_power_offset = sprom->rxpo2g; - - pi->txpa_2g[0] = sprom->pa0b0; - pi->txpa_2g[1] = sprom->pa0b1; - pi->txpa_2g[2] = sprom->pa0b2; - - pi_lcn->lcnphy_rssi_vf = sprom->rssismf2g; - pi_lcn->lcnphy_rssi_vc = sprom->rssismc2g; - pi_lcn->lcnphy_rssi_gs = sprom->rssisav2g; - - pi_lcn->lcnphy_rssi_vf_lowtemp = pi_lcn->lcnphy_rssi_vf; - pi_lcn->lcnphy_rssi_vc_lowtemp = pi_lcn->lcnphy_rssi_vc; - pi_lcn->lcnphy_rssi_gs_lowtemp = pi_lcn->lcnphy_rssi_gs; - - pi_lcn->lcnphy_rssi_vf_hightemp = pi_lcn->lcnphy_rssi_vf; - pi_lcn->lcnphy_rssi_vc_hightemp = pi_lcn->lcnphy_rssi_vc; - pi_lcn->lcnphy_rssi_gs_hightemp = pi_lcn->lcnphy_rssi_gs; - - txpwr = sprom->core_pwr_info[0].maxpwr_2g; - pi->tx_srom_max_2g = txpwr; - - for (i = 0; i < PWRTBL_NUM_COEFF; i++) { - pi->txpa_2g_low_temp[i] = pi->txpa_2g[i]; - pi->txpa_2g_high_temp[i] = pi->txpa_2g[i]; - } - - cckpo = sprom->cck2gpo; - offset_ofdm = sprom->ofdm2gpo; - if (cckpo) { - uint max_pwr_chan = txpwr; - - for (i = TXP_FIRST_CCK; i <= TXP_LAST_CCK; i++) { - pi->tx_srom_max_rate_2g[i] = - max_pwr_chan - ((cckpo & 0xf) * 2); - cckpo >>= 4; - } - - for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++) { - pi->tx_srom_max_rate_2g[i] = - max_pwr_chan - - ((offset_ofdm & 0xf) * 2); - offset_ofdm >>= 4; - } - } else { - u8 opo = 0; - - opo = sprom->opo; - - for (i = TXP_FIRST_CCK; i <= TXP_LAST_CCK; i++) - pi->tx_srom_max_rate_2g[i] = txpwr; - - for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++) { - pi->tx_srom_max_rate_2g[i] = txpwr - - ((offset_ofdm & 0xf) * 2); - offset_ofdm >>= 4; - } - offset_mcs = sprom->mcs2gpo[1] << 16; - offset_mcs |= sprom->mcs2gpo[0]; - pi_lcn->lcnphy_mcs20_po = offset_mcs; - for (i = TXP_FIRST_SISO_MCS_20; - i <= TXP_LAST_SISO_MCS_20; i++) { - pi->tx_srom_max_rate_2g[i] = - txpwr - ((offset_mcs & 0xf) * 2); - offset_mcs >>= 4; - } - } - - pi_lcn->lcnphy_rawtempsense = sprom->rawtempsense; - pi_lcn->lcnphy_measPower = sprom->measpower; - pi_lcn->lcnphy_tempsense_slope = sprom->tempsense_slope; - pi_lcn->lcnphy_hw_iqcal_en = sprom->hw_iqcal_en; - pi_lcn->lcnphy_iqcal_swp_dis = sprom->iqcal_swp_dis; - pi_lcn->lcnphy_tempcorrx = sprom->tempcorrx; - pi_lcn->lcnphy_tempsense_option = sprom->tempsense_option; - pi_lcn->lcnphy_freqoffset_corr = sprom->freqoffset_corr; - if (sprom->ant_available_bg > 1) - wlc_phy_ant_rxdiv_set((struct brcms_phy_pub *) pi, - sprom->ant_available_bg); - } - pi_lcn->lcnphy_cck_dig_filt_type = -1; - - return true; -} - -void wlc_2064_vco_cal(struct brcms_phy *pi) -{ - u8 calnrst; - - mod_radio_reg(pi, RADIO_2064_REG057, 1 << 3, 1 << 3); - calnrst = (u8) read_radio_reg(pi, RADIO_2064_REG056) & 0xf8; - write_radio_reg(pi, RADIO_2064_REG056, calnrst); - udelay(1); - write_radio_reg(pi, RADIO_2064_REG056, calnrst | 0x03); - udelay(1); - write_radio_reg(pi, RADIO_2064_REG056, calnrst | 0x07); - udelay(300); - mod_radio_reg(pi, RADIO_2064_REG057, 1 << 3, 0); -} - -bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi) -{ - if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) - return false; - else - return (LCNPHY_TX_PWR_CTRL_HW == - wlc_lcnphy_get_tx_pwr_ctrl((pi))); -} - -void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi) -{ - u16 pwr_ctrl; - if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { - wlc_lcnphy_calib_modes(pi, LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL); - } else if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) { - pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); - wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); - wlc_lcnphy_txpower_recalc_target(pi); - wlc_lcnphy_set_tx_pwr_ctrl(pi, pwr_ctrl); - } -} - -void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec) -{ - u8 channel = CHSPEC_CHANNEL(chanspec); - - wlc_phy_chanspec_radio_set((struct brcms_phy_pub *)pi, chanspec); - - wlc_lcnphy_set_chanspec_tweaks(pi, pi->radio_chanspec); - - or_phy_reg(pi, 0x44a, 0x44); - write_phy_reg(pi, 0x44a, 0x80); - - wlc_lcnphy_radio_2064_channel_tune_4313(pi, channel); - udelay(1000); - - wlc_lcnphy_toggle_afe_pwdn(pi); - - write_phy_reg(pi, 0x657, lcnphy_sfo_cfg[channel - 1].ptcentreTs20); - write_phy_reg(pi, 0x658, lcnphy_sfo_cfg[channel - 1].ptcentreFactor); - - if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) { - mod_phy_reg(pi, 0x448, (0x3 << 8), (2) << 8); - - wlc_lcnphy_load_tx_iir_filter(pi, false, 3); - } else { - mod_phy_reg(pi, 0x448, (0x3 << 8), (1) << 8); - - wlc_lcnphy_load_tx_iir_filter(pi, false, 2); - } - - if (pi->sh->boardflags & BFL_FEM) - wlc_lcnphy_load_tx_iir_filter(pi, true, 0); - else - wlc_lcnphy_load_tx_iir_filter(pi, true, 3); - - mod_phy_reg(pi, 0x4eb, (0x7 << 3), (1) << 3); - if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) - wlc_lcnphy_tssi_setup(pi); -} - -void wlc_phy_detach_lcnphy(struct brcms_phy *pi) -{ - kfree(pi->u.pi_lcnphy); -} - -bool wlc_phy_attach_lcnphy(struct brcms_phy *pi) -{ - struct brcms_phy_lcnphy *pi_lcn; - - pi->u.pi_lcnphy = kzalloc(sizeof(struct brcms_phy_lcnphy), GFP_ATOMIC); - if (pi->u.pi_lcnphy == NULL) - return false; - - pi_lcn = pi->u.pi_lcnphy; - - if (0 == (pi->sh->boardflags & BFL_NOPA)) { - pi->hwpwrctrl = true; - pi->hwpwrctrl_capable = true; - } - - pi->xtalfreq = bcma_chipco_get_alp_clock(&pi->d11core->bus->drv_cc); - pi_lcn->lcnphy_papd_rxGnCtrl_init = 0; - - pi->pi_fptr.init = wlc_phy_init_lcnphy; - pi->pi_fptr.calinit = wlc_phy_cal_init_lcnphy; - pi->pi_fptr.chanset = wlc_phy_chanspec_set_lcnphy; - pi->pi_fptr.txpwrrecalc = wlc_phy_txpower_recalc_target_lcnphy; - pi->pi_fptr.txiqccget = wlc_lcnphy_get_tx_iqcc; - pi->pi_fptr.txiqccset = wlc_lcnphy_set_tx_iqcc; - pi->pi_fptr.txloccget = wlc_lcnphy_get_tx_locc; - pi->pi_fptr.radioloftget = wlc_lcnphy_get_radio_loft; - pi->pi_fptr.detach = wlc_phy_detach_lcnphy; - - if (!wlc_phy_txpwr_srom_read_lcnphy(pi)) - return false; - - if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { - if (pi_lcn->lcnphy_tempsense_option == 3) { - pi->hwpwrctrl = true; - pi->hwpwrctrl_capable = true; - pi->temppwrctrl_capable = false; - } else { - pi->hwpwrctrl = false; - pi->hwpwrctrl_capable = false; - pi->temppwrctrl_capable = true; - } - } - - return true; -} - -static void wlc_lcnphy_set_rx_gain(struct brcms_phy *pi, u32 gain) -{ - u16 trsw, ext_lna, lna1, lna2, tia, biq0, biq1, gain0_15, gain16_19; - - trsw = (gain & ((u32) 1 << 28)) ? 0 : 1; - ext_lna = (u16) (gain >> 29) & 0x01; - lna1 = (u16) (gain >> 0) & 0x0f; - lna2 = (u16) (gain >> 4) & 0x0f; - tia = (u16) (gain >> 8) & 0xf; - biq0 = (u16) (gain >> 12) & 0xf; - biq1 = (u16) (gain >> 16) & 0xf; - - gain0_15 = (u16) ((lna1 & 0x3) | ((lna1 & 0x3) << 2) | - ((lna2 & 0x3) << 4) | ((lna2 & 0x3) << 6) | - ((tia & 0xf) << 8) | ((biq0 & 0xf) << 12)); - gain16_19 = biq1; - - mod_phy_reg(pi, 0x44d, (0x1 << 0), trsw << 0); - mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9); - mod_phy_reg(pi, 0x4b1, (0x1 << 10), ext_lna << 10); - mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0); - mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - mod_phy_reg(pi, 0x4b1, (0x3 << 11), lna1 << 11); - mod_phy_reg(pi, 0x4e6, (0x3 << 3), lna1 << 3); - } - wlc_lcnphy_rx_gain_override_enable(pi, true); -} - -static u32 wlc_lcnphy_get_receive_power(struct brcms_phy *pi, s32 *gain_index) -{ - u32 received_power = 0; - s32 max_index = 0; - u32 gain_code = 0; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - max_index = 36; - if (*gain_index >= 0) - gain_code = lcnphy_23bitgaincode_table[*gain_index]; - - if (-1 == *gain_index) { - *gain_index = 0; - while ((*gain_index <= (s32) max_index) - && (received_power < 700)) { - wlc_lcnphy_set_rx_gain(pi, - lcnphy_23bitgaincode_table - [*gain_index]); - received_power = - wlc_lcnphy_measure_digital_power( - pi, - pi_lcn-> - lcnphy_noise_samples); - (*gain_index)++; - } - (*gain_index)--; - } else { - wlc_lcnphy_set_rx_gain(pi, gain_code); - received_power = - wlc_lcnphy_measure_digital_power(pi, - pi_lcn-> - lcnphy_noise_samples); - } - - return received_power; -} - -s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index) -{ - s32 gain = 0; - s32 nominal_power_db; - s32 log_val, gain_mismatch, desired_gain, input_power_offset_db, - input_power_db; - s32 received_power, temperature; - u32 power; - u32 msb1, msb2, val1, val2, diff1, diff2; - uint freq; - struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; - - received_power = wlc_lcnphy_get_receive_power(pi, &gain_index); - - gain = lcnphy_gain_table[gain_index]; - - nominal_power_db = read_phy_reg(pi, 0x425) >> 8; - - power = (received_power * 16); - msb1 = ffs(power) - 1; - msb2 = msb1 + 1; - val1 = 1 << msb1; - val2 = 1 << msb2; - diff1 = (power - val1); - diff2 = (val2 - power); - if (diff1 < diff2) - log_val = msb1; - else - log_val = msb2; - - log_val = log_val * 3; - - gain_mismatch = (nominal_power_db / 2) - (log_val); - - desired_gain = gain + gain_mismatch; - - input_power_offset_db = read_phy_reg(pi, 0x434) & 0xFF; - - if (input_power_offset_db > 127) - input_power_offset_db -= 256; - - input_power_db = input_power_offset_db - desired_gain; - - input_power_db = - input_power_db + lcnphy_gain_index_offset_for_rssi[gain_index]; - - freq = wlc_phy_channel2freq(CHSPEC_CHANNEL(pi->radio_chanspec)); - if ((freq > 2427) && (freq <= 2467)) - input_power_db = input_power_db - 1; - - temperature = pi_lcn->lcnphy_lastsensed_temperature; - - if ((temperature - 15) < -30) - input_power_db = - input_power_db + - (((temperature - 10 - 25) * 286) >> 12) - - 7; - else if ((temperature - 15) < 4) - input_power_db = - input_power_db + - (((temperature - 10 - 25) * 286) >> 12) - - 3; - else - input_power_db = input_power_db + - (((temperature - 10 - 25) * 286) >> 12); - - wlc_lcnphy_rx_gain_override_enable(pi, 0); - - return input_power_db; -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h deleted file mode 100644 index f4a8ab09da43..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_PHY_LCN_H_ -#define _BRCM_PHY_LCN_H_ - -#include - -struct brcms_phy_lcnphy { - int lcnphy_txrf_sp_9_override; - u8 lcnphy_full_cal_channel; - u8 lcnphy_cal_counter; - u16 lcnphy_cal_temper; - bool lcnphy_recal; - - u8 lcnphy_rc_cap; - u32 lcnphy_mcs20_po; - - u8 lcnphy_tr_isolation_mid; - u8 lcnphy_tr_isolation_low; - u8 lcnphy_tr_isolation_hi; - - u8 lcnphy_bx_arch; - u8 lcnphy_rx_power_offset; - u8 lcnphy_rssi_vf; - u8 lcnphy_rssi_vc; - u8 lcnphy_rssi_gs; - u8 lcnphy_tssi_val; - u8 lcnphy_rssi_vf_lowtemp; - u8 lcnphy_rssi_vc_lowtemp; - u8 lcnphy_rssi_gs_lowtemp; - - u8 lcnphy_rssi_vf_hightemp; - u8 lcnphy_rssi_vc_hightemp; - u8 lcnphy_rssi_gs_hightemp; - - s16 lcnphy_pa0b0; - s16 lcnphy_pa0b1; - s16 lcnphy_pa0b2; - - u16 lcnphy_rawtempsense; - u8 lcnphy_measPower; - u8 lcnphy_tempsense_slope; - u8 lcnphy_freqoffset_corr; - u8 lcnphy_tempsense_option; - u8 lcnphy_tempcorrx; - bool lcnphy_iqcal_swp_dis; - bool lcnphy_hw_iqcal_en; - uint lcnphy_bandedge_corr; - bool lcnphy_spurmod; - u16 lcnphy_tssi_tx_cnt; - u16 lcnphy_tssi_idx; - u16 lcnphy_tssi_npt; - - u16 lcnphy_target_tx_freq; - s8 lcnphy_tx_power_idx_override; - u16 lcnphy_noise_samples; - - u32 lcnphy_papdRxGnIdx; - u32 lcnphy_papd_rxGnCtrl_init; - - u32 lcnphy_gain_idx_14_lowword; - u32 lcnphy_gain_idx_14_hiword; - u32 lcnphy_gain_idx_27_lowword; - u32 lcnphy_gain_idx_27_hiword; - s16 lcnphy_ofdmgainidxtableoffset; - s16 lcnphy_dsssgainidxtableoffset; - u32 lcnphy_tr_R_gain_val; - u32 lcnphy_tr_T_gain_val; - s8 lcnphy_input_pwr_offset_db; - u16 lcnphy_Med_Low_Gain_db; - u16 lcnphy_Very_Low_Gain_db; - s8 lcnphy_lastsensed_temperature; - s8 lcnphy_pkteng_rssi_slope; - u8 lcnphy_saved_tx_user_target[TXP_NUM_RATES]; - u8 lcnphy_volt_winner; - u8 lcnphy_volt_low; - u8 lcnphy_54_48_36_24mbps_backoff; - u8 lcnphy_11n_backoff; - u8 lcnphy_lowerofdm; - u8 lcnphy_cck; - u8 lcnphy_psat_2pt3_detected; - s32 lcnphy_lowest_Re_div_Im; - s8 lcnphy_final_papd_cal_idx; - u16 lcnphy_extstxctrl4; - u16 lcnphy_extstxctrl0; - u16 lcnphy_extstxctrl1; - s16 lcnphy_cck_dig_filt_type; - s16 lcnphy_ofdm_dig_filt_type; - struct lcnphy_cal_results lcnphy_cal_results; - - u8 lcnphy_psat_pwr; - u8 lcnphy_psat_indx; - s32 lcnphy_min_phase; - u8 lcnphy_final_idx; - u8 lcnphy_start_idx; - u8 lcnphy_current_index; - u16 lcnphy_logen_buf_1; - u16 lcnphy_local_ovr_2; - u16 lcnphy_local_oval_6; - u16 lcnphy_local_oval_5; - u16 lcnphy_logen_mixer_1; - - u8 lcnphy_aci_stat; - uint lcnphy_aci_start_time; - s8 lcnphy_tx_power_offset[TXP_NUM_RATES]; -}; -#endif /* _BRCM_PHY_LCN_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c deleted file mode 100644 index 99dac9b8a082..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c +++ /dev/null @@ -1,28721 +0,0 @@ -/* - * Copyright (c) 2010 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. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include "phy_int.h" -#include "phy_hal.h" -#include "phy_radio.h" -#include "phyreg_n.h" -#include "phytbl_n.h" -#include "soc.h" - -#define READ_RADIO_REG2(pi, radio_type, jspace, core, reg_name) \ - read_radio_reg(pi, radio_type##_##jspace##_##reg_name | \ - ((core == PHY_CORE_0) ? \ - radio_type##_##jspace##0 : \ - radio_type##_##jspace##1)) - -#define WRITE_RADIO_REG2(pi, radio_type, jspace, core, reg_name, value) \ - write_radio_reg(pi, radio_type##_##jspace##_##reg_name | \ - ((core == PHY_CORE_0) ? \ - radio_type##_##jspace##0 : \ - radio_type##_##jspace##1), value) - -#define WRITE_RADIO_SYN(pi, radio_type, reg_name, value) \ - write_radio_reg(pi, radio_type##_##SYN##_##reg_name, value) - -#define READ_RADIO_REG3(pi, radio_type, jspace, core, reg_name) \ - read_radio_reg(pi, ((core == PHY_CORE_0) ? \ - radio_type##_##jspace##0##_##reg_name : \ - radio_type##_##jspace##1##_##reg_name)) - -#define WRITE_RADIO_REG3(pi, radio_type, jspace, core, reg_name, value) \ - write_radio_reg(pi, ((core == PHY_CORE_0) ? \ - radio_type##_##jspace##0##_##reg_name : \ - radio_type##_##jspace##1##_##reg_name), \ - value) - -#define READ_RADIO_REG4(pi, radio_type, jspace, core, reg_name) \ - read_radio_reg(pi, ((core == PHY_CORE_0) ? \ - radio_type##_##reg_name##_##jspace##0 : \ - radio_type##_##reg_name##_##jspace##1)) - -#define WRITE_RADIO_REG4(pi, radio_type, jspace, core, reg_name, value) \ - write_radio_reg(pi, ((core == PHY_CORE_0) ? \ - radio_type##_##reg_name##_##jspace##0 : \ - radio_type##_##reg_name##_##jspace##1), \ - value) - -#define NPHY_ACI_MAX_UNDETECT_WINDOW_SZ 40 -#define NPHY_ACI_CHANNEL_DELTA 5 -#define NPHY_ACI_CHANNEL_SKIP 4 -#define NPHY_ACI_40MHZ_CHANNEL_DELTA 6 -#define NPHY_ACI_40MHZ_CHANNEL_SKIP 5 -#define NPHY_ACI_40MHZ_CHANNEL_DELTA_GE_REV3 6 -#define NPHY_ACI_40MHZ_CHANNEL_SKIP_GE_REV3 5 -#define NPHY_ACI_CHANNEL_DELTA_GE_REV3 4 -#define NPHY_ACI_CHANNEL_SKIP_GE_REV3 3 - -#define NPHY_NOISE_NOASSOC_GLITCH_TH_UP 2 - -#define NPHY_NOISE_NOASSOC_GLITCH_TH_DN 8 - -#define NPHY_NOISE_ASSOC_GLITCH_TH_UP 2 - -#define NPHY_NOISE_ASSOC_GLITCH_TH_DN 8 - -#define NPHY_NOISE_ASSOC_ACI_GLITCH_TH_UP 2 - -#define NPHY_NOISE_ASSOC_ACI_GLITCH_TH_DN 8 - -#define NPHY_NOISE_NOASSOC_ENTER_TH 400 - -#define NPHY_NOISE_ASSOC_ENTER_TH 400 - -#define NPHY_NOISE_ASSOC_RX_GLITCH_BADPLCP_ENTER_TH 400 - -#define NPHY_NOISE_CRSMINPWR_ARRAY_MAX_INDEX 44 -#define NPHY_NOISE_CRSMINPWR_ARRAY_MAX_INDEX_REV_7 56 - -#define NPHY_NOISE_NOASSOC_CRSIDX_INCR 16 - -#define NPHY_NOISE_ASSOC_CRSIDX_INCR 8 - -#define NPHY_IS_SROM_REINTERPRET NREV_GE(pi->pubpi.phy_rev, 5) - -#define NPHY_RSSICAL_MAXREAD 31 - -#define NPHY_RSSICAL_NPOLL 8 -#define NPHY_RSSICAL_MAXD (1<<20) -#define NPHY_MIN_RXIQ_PWR 2 - -#define NPHY_RSSICAL_W1_TARGET 25 -#define NPHY_RSSICAL_W2_TARGET NPHY_RSSICAL_W1_TARGET -#define NPHY_RSSICAL_NB_TARGET 0 - -#define NPHY_RSSICAL_W1_TARGET_REV3 29 -#define NPHY_RSSICAL_W2_TARGET_REV3 NPHY_RSSICAL_W1_TARGET_REV3 - -#define NPHY_CALSANITY_RSSI_NB_MAX_POS 9 -#define NPHY_CALSANITY_RSSI_NB_MAX_NEG -9 -#define NPHY_CALSANITY_RSSI_W1_MAX_POS 12 -#define NPHY_CALSANITY_RSSI_W1_MAX_NEG (NPHY_RSSICAL_W1_TARGET - \ - NPHY_RSSICAL_MAXREAD) -#define NPHY_CALSANITY_RSSI_W2_MAX_POS NPHY_CALSANITY_RSSI_W1_MAX_POS -#define NPHY_CALSANITY_RSSI_W2_MAX_NEG (NPHY_RSSICAL_W2_TARGET - \ - NPHY_RSSICAL_MAXREAD) -#define NPHY_RSSI_SXT(x) ((s8) (-((x) & 0x20) + ((x) & 0x1f))) -#define NPHY_RSSI_NB_VIOL(x) (((x) > NPHY_CALSANITY_RSSI_NB_MAX_POS) || \ - ((x) < NPHY_CALSANITY_RSSI_NB_MAX_NEG)) -#define NPHY_RSSI_W1_VIOL(x) (((x) > NPHY_CALSANITY_RSSI_W1_MAX_POS) || \ - ((x) < NPHY_CALSANITY_RSSI_W1_MAX_NEG)) -#define NPHY_RSSI_W2_VIOL(x) (((x) > NPHY_CALSANITY_RSSI_W2_MAX_POS) || \ - ((x) < NPHY_CALSANITY_RSSI_W2_MAX_NEG)) - -#define NPHY_IQCAL_NUMGAINS 9 -#define NPHY_N_GCTL 0x66 - -#define NPHY_PAPD_EPS_TBL_SIZE 64 -#define NPHY_PAPD_SCL_TBL_SIZE 64 -#define NPHY_NUM_DIG_FILT_COEFFS 15 - -#define NPHY_PAPD_COMP_OFF 0 -#define NPHY_PAPD_COMP_ON 1 - -#define NPHY_SROM_TEMPSHIFT 32 -#define NPHY_SROM_MAXTEMPOFFSET 16 -#define NPHY_SROM_MINTEMPOFFSET -16 - -#define NPHY_CAL_MAXTEMPDELTA 64 - -#define NPHY_NOISEVAR_TBLLEN40 256 -#define NPHY_NOISEVAR_TBLLEN20 128 - -#define NPHY_ANARXLPFBW_REDUCTIONFACT 7 - -#define NPHY_ADJUSTED_MINCRSPOWER 0x1e - -/* 5357 Chip specific ChipControl register bits */ -#define CCTRL5357_EXTPA (1<<14) /* extPA in ChipControl 1, bit 14 */ -#define CCTRL5357_ANT_MUX_2o3 (1<<15) /* 2o3 in ChipControl 1, bit 15 */ - -#define NPHY_CAL_TSSISAMPS 64 -#define NPHY_TEST_TONE_FREQ_40MHz 4000 -#define NPHY_TEST_TONE_FREQ_20MHz 2500 - -#define MAX_205x_RCAL_WAITLOOPS 10000 - -#define NPHY_RXCAL_TONEAMP 181 -#define NPHY_RXCAL_TONEFREQ_40MHz 4000 -#define NPHY_RXCAL_TONEFREQ_20MHz 2000 - -#define TXFILT_SHAPING_OFDM20 0 -#define TXFILT_SHAPING_OFDM40 1 -#define TXFILT_SHAPING_CCK 2 -#define TXFILT_DEFAULT_OFDM20 3 -#define TXFILT_DEFAULT_OFDM40 4 - -struct nphy_iqcal_params { - u16 txlpf; - u16 txgm; - u16 pga; - u16 pad; - u16 ipa; - u16 cal_gain; - u16 ncorr[5]; -}; - -struct nphy_txiqcal_ladder { - u8 percent; - u8 g_env; -}; - -struct nphy_ipa_txcalgains { - struct nphy_txgains gains; - bool useindex; - u8 index; -}; - -struct nphy_papd_restore_state { - u16 fbmix[2]; - u16 vga_master[2]; - u16 intpa_master[2]; - u16 afectrl[2]; - u16 afeoverride[2]; - u16 pwrup[2]; - u16 atten[2]; - u16 mm; -}; - -struct nphy_ipa_txrxgain { - u16 hpvga; - u16 lpf_biq1; - u16 lpf_biq0; - u16 lna2; - u16 lna1; - s8 txpwrindex; -}; - -#define NPHY_IPA_RXCAL_MAXGAININDEX (6 - 1) - -static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_5GHz[] = { - {0, 0, 0, 0, 0, 100}, - {0, 0, 0, 0, 0, 50}, - {0, 0, 0, 0, 0, -1}, - {0, 0, 0, 3, 0, -1}, - {0, 0, 3, 3, 0, -1}, - {0, 2, 3, 3, 0, -1} -}; - -static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_2GHz[] = { - {0, 0, 0, 0, 0, 128}, - {0, 0, 0, 0, 0, 70}, - {0, 0, 0, 0, 0, 20}, - {0, 0, 0, 3, 0, 20}, - {0, 0, 3, 3, 0, 20}, - {0, 2, 3, 3, 0, 20} -}; - -static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_5GHz_rev7[] = { - {0, 0, 0, 0, 0, 100}, - {0, 0, 0, 0, 0, 50}, - {0, 0, 0, 0, 0, -1}, - {0, 0, 0, 3, 0, -1}, - {0, 0, 3, 3, 0, -1}, - {0, 0, 5, 3, 0, -1} -}; - -static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_2GHz_rev7[] = { - {0, 0, 0, 0, 0, 10}, - {0, 0, 0, 1, 0, 10}, - {0, 0, 1, 2, 0, 10}, - {0, 0, 1, 3, 0, 10}, - {0, 0, 4, 3, 0, 10}, - {0, 0, 6, 3, 0, 10} -}; - -enum { - NPHY_RXCAL_GAIN_INIT = 0, - NPHY_RXCAL_GAIN_UP, - NPHY_RXCAL_GAIN_DOWN -}; - -#define wlc_phy_get_papd_nphy(pi) \ - (read_phy_reg((pi), 0x1e7) & \ - ((0x1 << 15) | \ - (0x1 << 14) | \ - (0x1 << 13))) - -static const u16 NPHY_IPA_REV4_txdigi_filtcoeffs[][NPHY_NUM_DIG_FILT_COEFFS] = { - {-377, 137, -407, 208, -1527, 956, 93, 186, 93, - 230, -44, 230, 201, -191, 201}, - {-77, 20, -98, 49, -93, 60, 56, 111, 56, 26, -5, - 26, 34, -32, 34}, - {-360, 164, -376, 164, -1533, 576, 308, -314, 308, - 121, -73, 121, 91, 124, 91}, - {-295, 200, -363, 142, -1391, 826, 151, 301, 151, - 151, 301, 151, 602, -752, 602}, - {-92, 58, -96, 49, -104, 44, 17, 35, 17, - 12, 25, 12, 13, 27, 13}, - {-375, 136, -399, 209, -1479, 949, 130, 260, 130, - 230, -44, 230, 201, -191, 201}, - {0xed9, 0xc8, 0xe95, 0x8e, 0xa91, 0x33a, 0x97, 0x12d, 0x97, - 0x97, 0x12d, 0x97, 0x25a, 0xd10, 0x25a} -}; - -struct chan_info_nphy_2055 { - u16 chan; - u16 freq; - uint unknown; - u8 RF_pll_ref; - u8 RF_rf_pll_mod1; - u8 RF_rf_pll_mod0; - u8 RF_vco_cap_tail; - u8 RF_vco_cal1; - u8 RF_vco_cal2; - u8 RF_pll_lf_c1; - u8 RF_pll_lf_r1; - u8 RF_pll_lf_c2; - u8 RF_lgbuf_cen_buf; - u8 RF_lgen_tune1; - u8 RF_lgen_tune2; - u8 RF_core1_lgbuf_a_tune; - u8 RF_core1_lgbuf_g_tune; - u8 RF_core1_rxrf_reg1; - u8 RF_core1_tx_pga_pad_tn; - u8 RF_core1_tx_mx_bgtrim; - u8 RF_core2_lgbuf_a_tune; - u8 RF_core2_lgbuf_g_tune; - u8 RF_core2_rxrf_reg1; - u8 RF_core2_tx_pga_pad_tn; - u8 RF_core2_tx_mx_bgtrim; - u16 PHY_BW1a; - u16 PHY_BW2; - u16 PHY_BW3; - u16 PHY_BW4; - u16 PHY_BW5; - u16 PHY_BW6; -}; - -struct chan_info_nphy_radio205x { - u16 chan; - u16 freq; - u8 RF_SYN_pll_vcocal1; - u8 RF_SYN_pll_vcocal2; - u8 RF_SYN_pll_refdiv; - u8 RF_SYN_pll_mmd2; - u8 RF_SYN_pll_mmd1; - u8 RF_SYN_pll_loopfilter1; - u8 RF_SYN_pll_loopfilter2; - u8 RF_SYN_pll_loopfilter3; - u8 RF_SYN_pll_loopfilter4; - u8 RF_SYN_pll_loopfilter5; - u8 RF_SYN_reserved_addr27; - u8 RF_SYN_reserved_addr28; - u8 RF_SYN_reserved_addr29; - u8 RF_SYN_logen_VCOBUF1; - u8 RF_SYN_logen_MIXER2; - u8 RF_SYN_logen_BUF3; - u8 RF_SYN_logen_BUF4; - u8 RF_RX0_lnaa_tune; - u8 RF_RX0_lnag_tune; - u8 RF_TX0_intpaa_boost_tune; - u8 RF_TX0_intpag_boost_tune; - u8 RF_TX0_pada_boost_tune; - u8 RF_TX0_padg_boost_tune; - u8 RF_TX0_pgaa_boost_tune; - u8 RF_TX0_pgag_boost_tune; - u8 RF_TX0_mixa_boost_tune; - u8 RF_TX0_mixg_boost_tune; - u8 RF_RX1_lnaa_tune; - u8 RF_RX1_lnag_tune; - u8 RF_TX1_intpaa_boost_tune; - u8 RF_TX1_intpag_boost_tune; - u8 RF_TX1_pada_boost_tune; - u8 RF_TX1_padg_boost_tune; - u8 RF_TX1_pgaa_boost_tune; - u8 RF_TX1_pgag_boost_tune; - u8 RF_TX1_mixa_boost_tune; - u8 RF_TX1_mixg_boost_tune; - u16 PHY_BW1a; - u16 PHY_BW2; - u16 PHY_BW3; - u16 PHY_BW4; - u16 PHY_BW5; - u16 PHY_BW6; -}; - -struct chan_info_nphy_radio2057 { - u16 chan; - u16 freq; - u8 RF_vcocal_countval0; - u8 RF_vcocal_countval1; - u8 RF_rfpll_refmaster_sparextalsize; - u8 RF_rfpll_loopfilter_r1; - u8 RF_rfpll_loopfilter_c2; - u8 RF_rfpll_loopfilter_c1; - u8 RF_cp_kpd_idac; - u8 RF_rfpll_mmd0; - u8 RF_rfpll_mmd1; - u8 RF_vcobuf_tune; - u8 RF_logen_mx2g_tune; - u8 RF_logen_mx5g_tune; - u8 RF_logen_indbuf2g_tune; - u8 RF_logen_indbuf5g_tune; - u8 RF_txmix2g_tune_boost_pu_core0; - u8 RF_pad2g_tune_pus_core0; - u8 RF_pga_boost_tune_core0; - u8 RF_txmix5g_boost_tune_core0; - u8 RF_pad5g_tune_misc_pus_core0; - u8 RF_lna2g_tune_core0; - u8 RF_lna5g_tune_core0; - u8 RF_txmix2g_tune_boost_pu_core1; - u8 RF_pad2g_tune_pus_core1; - u8 RF_pga_boost_tune_core1; - u8 RF_txmix5g_boost_tune_core1; - u8 RF_pad5g_tune_misc_pus_core1; - u8 RF_lna2g_tune_core1; - u8 RF_lna5g_tune_core1; - u16 PHY_BW1a; - u16 PHY_BW2; - u16 PHY_BW3; - u16 PHY_BW4; - u16 PHY_BW5; - u16 PHY_BW6; -}; - -struct chan_info_nphy_radio2057_rev5 { - u16 chan; - u16 freq; - u8 RF_vcocal_countval0; - u8 RF_vcocal_countval1; - u8 RF_rfpll_refmaster_sparextalsize; - u8 RF_rfpll_loopfilter_r1; - u8 RF_rfpll_loopfilter_c2; - u8 RF_rfpll_loopfilter_c1; - u8 RF_cp_kpd_idac; - u8 RF_rfpll_mmd0; - u8 RF_rfpll_mmd1; - u8 RF_vcobuf_tune; - u8 RF_logen_mx2g_tune; - u8 RF_logen_indbuf2g_tune; - u8 RF_txmix2g_tune_boost_pu_core0; - u8 RF_pad2g_tune_pus_core0; - u8 RF_lna2g_tune_core0; - u8 RF_txmix2g_tune_boost_pu_core1; - u8 RF_pad2g_tune_pus_core1; - u8 RF_lna2g_tune_core1; - u16 PHY_BW1a; - u16 PHY_BW2; - u16 PHY_BW3; - u16 PHY_BW4; - u16 PHY_BW5; - u16 PHY_BW6; -}; - -struct nphy_sfo_cfg { - u16 PHY_BW1a; - u16 PHY_BW2; - u16 PHY_BW3; - u16 PHY_BW4; - u16 PHY_BW5; - u16 PHY_BW6; -}; - -static const struct chan_info_nphy_2055 chan_info_nphy_2055[] = { - { - 184, 4920, 3280, 0x71, 0x01, 0xEC, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7B4, 0x7B0, 0x7AC, 0x214, 0x215, 0x216}, - { - 186, 4930, 3287, 0x71, 0x01, 0xED, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7B8, 0x7B4, 0x7B0, 0x213, 0x214, 0x215}, - { - 188, 4940, 3293, 0x71, 0x01, 0xEE, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7BC, 0x7B8, 0x7B4, 0x212, 0x213, 0x214}, - { - 190, 4950, 3300, 0x71, 0x01, 0xEF, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7C0, 0x7BC, 0x7B8, 0x211, 0x212, 0x213}, - { - 192, 4960, 3307, 0x71, 0x01, 0xF0, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7C4, 0x7C0, 0x7BC, 0x20F, 0x211, 0x212}, - { - 194, 4970, 3313, 0x71, 0x01, 0xF1, 0x0F, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7C8, 0x7C4, 0x7C0, 0x20E, 0x20F, 0x211}, - { - 196, 4980, 3320, 0x71, 0x01, 0xF2, 0x0E, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7CC, 0x7C8, 0x7C4, 0x20D, 0x20E, 0x20F}, - { - 198, 4990, 3327, 0x71, 0x01, 0xF3, 0x0E, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7D0, 0x7CC, 0x7C8, 0x20C, 0x20D, 0x20E}, - { - 200, 5000, 3333, 0x71, 0x01, 0xF4, 0x0E, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7D4, 0x7D0, 0x7CC, 0x20B, 0x20C, 0x20D}, - { - 202, 5010, 3340, 0x71, 0x01, 0xF5, 0x0E, 0xFF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7D8, 0x7D4, 0x7D0, 0x20A, 0x20B, 0x20C}, - { - 204, 5020, 3347, 0x71, 0x01, 0xF6, 0x0E, 0xF7, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7DC, 0x7D8, 0x7D4, 0x209, 0x20A, 0x20B}, - { - 206, 5030, 3353, 0x71, 0x01, 0xF7, 0x0E, 0xF7, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7E0, 0x7DC, 0x7D8, 0x208, 0x209, 0x20A}, - { - 208, 5040, 3360, 0x71, 0x01, 0xF8, 0x0D, 0xEF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7E4, 0x7E0, 0x7DC, 0x207, 0x208, 0x209}, - { - 210, 5050, 3367, 0x71, 0x01, 0xF9, 0x0D, 0xEF, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, - 0x0F, 0x8F, 0x7E8, 0x7E4, 0x7E0, 0x206, 0x207, 0x208}, - { - 212, 5060, 3373, 0x71, 0x01, 0xFA, 0x0D, 0xE6, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, 0x8E, 0xFF, 0x00, 0x0E, - 0x0F, 0x8E, 0x7EC, 0x7E8, 0x7E4, 0x205, 0x206, 0x207}, - { - 214, 5070, 3380, 0x71, 0x01, 0xFB, 0x0D, 0xE6, 0x01, 0x04, 0x0A, - 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, 0x8E, 0xFF, 0x00, 0x0E, - 0x0F, 0x8E, 0x7F0, 0x7EC, 0x7E8, 0x204, 0x205, 0x206}, - { - 216, 5080, 3387, 0x71, 0x01, 0xFC, 0x0D, 0xDE, 0x01, 0x04, 0x0A, - 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, 0x8D, 0xEE, 0x00, 0x0E, - 0x0F, 0x8D, 0x7F4, 0x7F0, 0x7EC, 0x203, 0x204, 0x205}, - { - 218, 5090, 3393, 0x71, 0x01, 0xFD, 0x0D, 0xDE, 0x01, 0x04, 0x0A, - 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, 0x8D, 0xEE, 0x00, 0x0E, - 0x0F, 0x8D, 0x7F8, 0x7F4, 0x7F0, 0x202, 0x203, 0x204}, - { - 220, 5100, 3400, 0x71, 0x01, 0xFE, 0x0C, 0xD6, 0x01, 0x04, 0x0A, - 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, 0x8D, 0xEE, 0x00, 0x0D, - 0x0F, 0x8D, 0x7FC, 0x7F8, 0x7F4, 0x201, 0x202, 0x203}, - { - 222, 5110, 3407, 0x71, 0x01, 0xFF, 0x0C, 0xD6, 0x01, 0x04, 0x0A, - 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, 0x8D, 0xEE, 0x00, 0x0D, - 0x0F, 0x8D, 0x800, 0x7FC, 0x7F8, 0x200, 0x201, 0x202}, - { - 224, 5120, 3413, 0x71, 0x02, 0x00, 0x0C, 0xCE, 0x01, 0x04, 0x0A, - 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, 0x8C, 0xDD, 0x00, 0x0D, - 0x0F, 0x8C, 0x804, 0x800, 0x7FC, 0x1FF, 0x200, 0x201}, - { - 226, 5130, 3420, 0x71, 0x02, 0x01, 0x0C, 0xCE, 0x01, 0x04, 0x0A, - 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, 0x8C, 0xDD, 0x00, 0x0D, - 0x0F, 0x8C, 0x808, 0x804, 0x800, 0x1FE, 0x1FF, 0x200}, - { - 228, 5140, 3427, 0x71, 0x02, 0x02, 0x0C, 0xC6, 0x01, 0x04, 0x0A, - 0x00, 0x8D, 0x99, 0x99, 0xDD, 0x00, 0x0C, 0x0E, 0x8B, 0xDD, 0x00, 0x0C, - 0x0E, 0x8B, 0x80C, 0x808, 0x804, 0x1FD, 0x1FE, 0x1FF}, - { - 32, 5160, 3440, 0x71, 0x02, 0x04, 0x0B, 0xBE, 0x01, 0x04, 0x0A, - 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, 0x8A, 0xCC, 0x00, 0x0B, - 0x0D, 0x8A, 0x814, 0x810, 0x80C, 0x1FB, 0x1FC, 0x1FD}, - { - 34, 5170, 3447, 0x71, 0x02, 0x05, 0x0B, 0xBE, 0x01, 0x04, 0x0A, - 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, 0x8A, 0xCC, 0x00, 0x0B, - 0x0D, 0x8A, 0x818, 0x814, 0x810, 0x1FA, 0x1FB, 0x1FC}, - { - 36, 5180, 3453, 0x71, 0x02, 0x06, 0x0B, 0xB6, 0x01, 0x04, 0x0A, - 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, 0x89, 0xCC, 0x00, 0x0B, - 0x0C, 0x89, 0x81C, 0x818, 0x814, 0x1F9, 0x1FA, 0x1FB}, - { - 38, 5190, 3460, 0x71, 0x02, 0x07, 0x0B, 0xB6, 0x01, 0x04, 0x0A, - 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, 0x89, 0xCC, 0x00, 0x0B, - 0x0C, 0x89, 0x820, 0x81C, 0x818, 0x1F8, 0x1F9, 0x1FA}, - { - 40, 5200, 3467, 0x71, 0x02, 0x08, 0x0B, 0xAF, 0x01, 0x04, 0x0A, - 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, 0x89, 0xBB, 0x00, 0x0A, - 0x0B, 0x89, 0x824, 0x820, 0x81C, 0x1F7, 0x1F8, 0x1F9}, - { - 42, 5210, 3473, 0x71, 0x02, 0x09, 0x0B, 0xAF, 0x01, 0x04, 0x0A, - 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, 0x89, 0xBB, 0x00, 0x0A, - 0x0B, 0x89, 0x828, 0x824, 0x820, 0x1F6, 0x1F7, 0x1F8}, - { - 44, 5220, 3480, 0x71, 0x02, 0x0A, 0x0A, 0xA7, 0x01, 0x04, 0x0A, - 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, 0x88, 0xBB, 0x00, 0x09, - 0x0A, 0x88, 0x82C, 0x828, 0x824, 0x1F5, 0x1F6, 0x1F7}, - { - 46, 5230, 3487, 0x71, 0x02, 0x0B, 0x0A, 0xA7, 0x01, 0x04, 0x0A, - 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, 0x88, 0xBB, 0x00, 0x09, - 0x0A, 0x88, 0x830, 0x82C, 0x828, 0x1F4, 0x1F5, 0x1F6}, - { - 48, 5240, 3493, 0x71, 0x02, 0x0C, 0x0A, 0xA0, 0x01, 0x04, 0x0A, - 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, 0x87, 0xAA, 0x00, 0x09, - 0x0A, 0x87, 0x834, 0x830, 0x82C, 0x1F3, 0x1F4, 0x1F5}, - { - 50, 5250, 3500, 0x71, 0x02, 0x0D, 0x0A, 0xA0, 0x01, 0x04, 0x0A, - 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, 0x87, 0xAA, 0x00, 0x09, - 0x0A, 0x87, 0x838, 0x834, 0x830, 0x1F2, 0x1F3, 0x1F4}, - { - 52, 5260, 3507, 0x71, 0x02, 0x0E, 0x0A, 0x98, 0x01, 0x04, 0x0A, - 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, 0x87, 0xAA, 0x00, 0x08, - 0x09, 0x87, 0x83C, 0x838, 0x834, 0x1F1, 0x1F2, 0x1F3}, - { - 54, 5270, 3513, 0x71, 0x02, 0x0F, 0x0A, 0x98, 0x01, 0x04, 0x0A, - 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, 0x87, 0xAA, 0x00, 0x08, - 0x09, 0x87, 0x840, 0x83C, 0x838, 0x1F0, 0x1F1, 0x1F2}, - { - 56, 5280, 3520, 0x71, 0x02, 0x10, 0x09, 0x91, 0x01, 0x04, 0x0A, - 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, 0x86, 0x99, 0x00, 0x08, - 0x08, 0x86, 0x844, 0x840, 0x83C, 0x1F0, 0x1F0, 0x1F1}, - { - 58, 5290, 3527, 0x71, 0x02, 0x11, 0x09, 0x91, 0x01, 0x04, 0x0A, - 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, 0x86, 0x99, 0x00, 0x08, - 0x08, 0x86, 0x848, 0x844, 0x840, 0x1EF, 0x1F0, 0x1F0}, - { - 60, 5300, 3533, 0x71, 0x02, 0x12, 0x09, 0x8A, 0x01, 0x04, 0x0A, - 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, 0x85, 0x99, 0x00, 0x08, - 0x07, 0x85, 0x84C, 0x848, 0x844, 0x1EE, 0x1EF, 0x1F0}, - { - 62, 5310, 3540, 0x71, 0x02, 0x13, 0x09, 0x8A, 0x01, 0x04, 0x0A, - 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, 0x85, 0x99, 0x00, 0x08, - 0x07, 0x85, 0x850, 0x84C, 0x848, 0x1ED, 0x1EE, 0x1EF}, - { - 64, 5320, 3547, 0x71, 0x02, 0x14, 0x09, 0x83, 0x01, 0x04, 0x0A, - 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, 0x84, 0x88, 0x00, 0x07, - 0x07, 0x84, 0x854, 0x850, 0x84C, 0x1EC, 0x1ED, 0x1EE}, - { - 66, 5330, 3553, 0x71, 0x02, 0x15, 0x09, 0x83, 0x01, 0x04, 0x0A, - 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, 0x84, 0x88, 0x00, 0x07, - 0x07, 0x84, 0x858, 0x854, 0x850, 0x1EB, 0x1EC, 0x1ED}, - { - 68, 5340, 3560, 0x71, 0x02, 0x16, 0x08, 0x7C, 0x01, 0x04, 0x0A, - 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, 0x84, 0x88, 0x00, 0x07, - 0x06, 0x84, 0x85C, 0x858, 0x854, 0x1EA, 0x1EB, 0x1EC}, - { - 70, 5350, 3567, 0x71, 0x02, 0x17, 0x08, 0x7C, 0x01, 0x04, 0x0A, - 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, 0x84, 0x88, 0x00, 0x07, - 0x06, 0x84, 0x860, 0x85C, 0x858, 0x1E9, 0x1EA, 0x1EB}, - { - 72, 5360, 3573, 0x71, 0x02, 0x18, 0x08, 0x75, 0x01, 0x04, 0x0A, - 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, 0x83, 0x77, 0x00, 0x06, - 0x05, 0x83, 0x864, 0x860, 0x85C, 0x1E8, 0x1E9, 0x1EA}, - { - 74, 5370, 3580, 0x71, 0x02, 0x19, 0x08, 0x75, 0x01, 0x04, 0x0A, - 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, 0x83, 0x77, 0x00, 0x06, - 0x05, 0x83, 0x868, 0x864, 0x860, 0x1E7, 0x1E8, 0x1E9}, - { - 76, 5380, 3587, 0x71, 0x02, 0x1A, 0x08, 0x6E, 0x01, 0x04, 0x0A, - 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, 0x82, 0x77, 0x00, 0x06, - 0x04, 0x82, 0x86C, 0x868, 0x864, 0x1E6, 0x1E7, 0x1E8}, - { - 78, 5390, 3593, 0x71, 0x02, 0x1B, 0x08, 0x6E, 0x01, 0x04, 0x0A, - 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, 0x82, 0x77, 0x00, 0x06, - 0x04, 0x82, 0x870, 0x86C, 0x868, 0x1E5, 0x1E6, 0x1E7}, - { - 80, 5400, 3600, 0x71, 0x02, 0x1C, 0x07, 0x67, 0x01, 0x04, 0x0A, - 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, 0x81, 0x66, 0x00, 0x05, - 0x04, 0x81, 0x874, 0x870, 0x86C, 0x1E5, 0x1E5, 0x1E6}, - { - 82, 5410, 3607, 0x71, 0x02, 0x1D, 0x07, 0x67, 0x01, 0x04, 0x0A, - 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, 0x81, 0x66, 0x00, 0x05, - 0x04, 0x81, 0x878, 0x874, 0x870, 0x1E4, 0x1E5, 0x1E5}, - { - 84, 5420, 3613, 0x71, 0x02, 0x1E, 0x07, 0x61, 0x01, 0x04, 0x0A, - 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, 0x80, 0x66, 0x00, 0x05, - 0x03, 0x80, 0x87C, 0x878, 0x874, 0x1E3, 0x1E4, 0x1E5}, - { - 86, 5430, 3620, 0x71, 0x02, 0x1F, 0x07, 0x61, 0x01, 0x04, 0x0A, - 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, 0x80, 0x66, 0x00, 0x05, - 0x03, 0x80, 0x880, 0x87C, 0x878, 0x1E2, 0x1E3, 0x1E4}, - { - 88, 5440, 3627, 0x71, 0x02, 0x20, 0x07, 0x5A, 0x01, 0x04, 0x0A, - 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, 0x80, 0x55, 0x00, 0x04, - 0x02, 0x80, 0x884, 0x880, 0x87C, 0x1E1, 0x1E2, 0x1E3}, - { - 90, 5450, 3633, 0x71, 0x02, 0x21, 0x07, 0x5A, 0x01, 0x04, 0x0A, - 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, 0x80, 0x55, 0x00, 0x04, - 0x02, 0x80, 0x888, 0x884, 0x880, 0x1E0, 0x1E1, 0x1E2}, - { - 92, 5460, 3640, 0x71, 0x02, 0x22, 0x06, 0x53, 0x01, 0x04, 0x0A, - 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, 0x80, 0x55, 0x00, 0x04, - 0x01, 0x80, 0x88C, 0x888, 0x884, 0x1DF, 0x1E0, 0x1E1}, - { - 94, 5470, 3647, 0x71, 0x02, 0x23, 0x06, 0x53, 0x01, 0x04, 0x0A, - 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, 0x80, 0x55, 0x00, 0x04, - 0x01, 0x80, 0x890, 0x88C, 0x888, 0x1DE, 0x1DF, 0x1E0}, - { - 96, 5480, 3653, 0x71, 0x02, 0x24, 0x06, 0x4D, 0x01, 0x04, 0x0A, - 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, - 0x00, 0x80, 0x894, 0x890, 0x88C, 0x1DD, 0x1DE, 0x1DF}, - { - 98, 5490, 3660, 0x71, 0x02, 0x25, 0x06, 0x4D, 0x01, 0x04, 0x0A, - 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, - 0x00, 0x80, 0x898, 0x894, 0x890, 0x1DD, 0x1DD, 0x1DE}, - { - 100, 5500, 3667, 0x71, 0x02, 0x26, 0x06, 0x47, 0x01, 0x04, 0x0A, - 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, - 0x00, 0x80, 0x89C, 0x898, 0x894, 0x1DC, 0x1DD, 0x1DD}, - { - 102, 5510, 3673, 0x71, 0x02, 0x27, 0x06, 0x47, 0x01, 0x04, 0x0A, - 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, - 0x00, 0x80, 0x8A0, 0x89C, 0x898, 0x1DB, 0x1DC, 0x1DD}, - { - 104, 5520, 3680, 0x71, 0x02, 0x28, 0x05, 0x40, 0x01, 0x04, 0x0A, - 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, - 0x00, 0x80, 0x8A4, 0x8A0, 0x89C, 0x1DA, 0x1DB, 0x1DC}, - { - 106, 5530, 3687, 0x71, 0x02, 0x29, 0x05, 0x40, 0x01, 0x04, 0x0A, - 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, - 0x00, 0x80, 0x8A8, 0x8A4, 0x8A0, 0x1D9, 0x1DA, 0x1DB}, - { - 108, 5540, 3693, 0x71, 0x02, 0x2A, 0x05, 0x3A, 0x01, 0x04, 0x0A, - 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, - 0x00, 0x80, 0x8AC, 0x8A8, 0x8A4, 0x1D8, 0x1D9, 0x1DA}, - { - 110, 5550, 3700, 0x71, 0x02, 0x2B, 0x05, 0x3A, 0x01, 0x04, 0x0A, - 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, - 0x00, 0x80, 0x8B0, 0x8AC, 0x8A8, 0x1D7, 0x1D8, 0x1D9}, - { - 112, 5560, 3707, 0x71, 0x02, 0x2C, 0x05, 0x34, 0x01, 0x04, 0x0A, - 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, - 0x00, 0x80, 0x8B4, 0x8B0, 0x8AC, 0x1D7, 0x1D7, 0x1D8}, - { - 114, 5570, 3713, 0x71, 0x02, 0x2D, 0x05, 0x34, 0x01, 0x04, 0x0A, - 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, - 0x00, 0x80, 0x8B8, 0x8B4, 0x8B0, 0x1D6, 0x1D7, 0x1D7}, - { - 116, 5580, 3720, 0x71, 0x02, 0x2E, 0x04, 0x2E, 0x01, 0x04, 0x0A, - 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, - 0x00, 0x80, 0x8BC, 0x8B8, 0x8B4, 0x1D5, 0x1D6, 0x1D7}, - { - 118, 5590, 3727, 0x71, 0x02, 0x2F, 0x04, 0x2E, 0x01, 0x04, 0x0A, - 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, - 0x00, 0x80, 0x8C0, 0x8BC, 0x8B8, 0x1D4, 0x1D5, 0x1D6}, - { - 120, 5600, 3733, 0x71, 0x02, 0x30, 0x04, 0x28, 0x01, 0x04, 0x0A, - 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, 0x80, 0x11, 0x00, 0x01, - 0x00, 0x80, 0x8C4, 0x8C0, 0x8BC, 0x1D3, 0x1D4, 0x1D5}, - { - 122, 5610, 3740, 0x71, 0x02, 0x31, 0x04, 0x28, 0x01, 0x04, 0x0A, - 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, 0x80, 0x11, 0x00, 0x01, - 0x00, 0x80, 0x8C8, 0x8C4, 0x8C0, 0x1D2, 0x1D3, 0x1D4}, - { - 124, 5620, 3747, 0x71, 0x02, 0x32, 0x04, 0x21, 0x01, 0x04, 0x0A, - 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, - 0x00, 0x80, 0x8CC, 0x8C8, 0x8C4, 0x1D2, 0x1D2, 0x1D3}, - { - 126, 5630, 3753, 0x71, 0x02, 0x33, 0x04, 0x21, 0x01, 0x04, 0x0A, - 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, - 0x00, 0x80, 0x8D0, 0x8CC, 0x8C8, 0x1D1, 0x1D2, 0x1D2}, - { - 128, 5640, 3760, 0x71, 0x02, 0x34, 0x03, 0x1C, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8D4, 0x8D0, 0x8CC, 0x1D0, 0x1D1, 0x1D2}, - { - 130, 5650, 3767, 0x71, 0x02, 0x35, 0x03, 0x1C, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8D8, 0x8D4, 0x8D0, 0x1CF, 0x1D0, 0x1D1}, - { - 132, 5660, 3773, 0x71, 0x02, 0x36, 0x03, 0x16, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8DC, 0x8D8, 0x8D4, 0x1CE, 0x1CF, 0x1D0}, - { - 134, 5670, 3780, 0x71, 0x02, 0x37, 0x03, 0x16, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8E0, 0x8DC, 0x8D8, 0x1CE, 0x1CE, 0x1CF}, - { - 136, 5680, 3787, 0x71, 0x02, 0x38, 0x03, 0x10, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8E4, 0x8E0, 0x8DC, 0x1CD, 0x1CE, 0x1CE}, - { - 138, 5690, 3793, 0x71, 0x02, 0x39, 0x03, 0x10, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8E8, 0x8E4, 0x8E0, 0x1CC, 0x1CD, 0x1CE}, - { - 140, 5700, 3800, 0x71, 0x02, 0x3A, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8EC, 0x8E8, 0x8E4, 0x1CB, 0x1CC, 0x1CD}, - { - 142, 5710, 3807, 0x71, 0x02, 0x3B, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8F0, 0x8EC, 0x8E8, 0x1CA, 0x1CB, 0x1CC}, - { - 144, 5720, 3813, 0x71, 0x02, 0x3C, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8F4, 0x8F0, 0x8EC, 0x1C9, 0x1CA, 0x1CB}, - { - 145, 5725, 3817, 0x72, 0x04, 0x79, 0x02, 0x03, 0x01, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8F6, 0x8F2, 0x8EE, 0x1C9, 0x1CA, 0x1CB}, - { - 146, 5730, 3820, 0x71, 0x02, 0x3D, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8F8, 0x8F4, 0x8F0, 0x1C9, 0x1C9, 0x1CA}, - { - 147, 5735, 3823, 0x72, 0x04, 0x7B, 0x02, 0x03, 0x01, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8FA, 0x8F6, 0x8F2, 0x1C8, 0x1C9, 0x1CA}, - { - 148, 5740, 3827, 0x71, 0x02, 0x3E, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8FC, 0x8F8, 0x8F4, 0x1C8, 0x1C9, 0x1C9}, - { - 149, 5745, 3830, 0x72, 0x04, 0x7D, 0x02, 0xFE, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x8FE, 0x8FA, 0x8F6, 0x1C8, 0x1C8, 0x1C9}, - { - 150, 5750, 3833, 0x71, 0x02, 0x3F, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x900, 0x8FC, 0x8F8, 0x1C7, 0x1C8, 0x1C9}, - { - 151, 5755, 3837, 0x72, 0x04, 0x7F, 0x02, 0xFE, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x902, 0x8FE, 0x8FA, 0x1C7, 0x1C8, 0x1C8}, - { - 152, 5760, 3840, 0x71, 0x02, 0x40, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x904, 0x900, 0x8FC, 0x1C6, 0x1C7, 0x1C8}, - { - 153, 5765, 3843, 0x72, 0x04, 0x81, 0x02, 0xF8, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x906, 0x902, 0x8FE, 0x1C6, 0x1C7, 0x1C8}, - { - 154, 5770, 3847, 0x71, 0x02, 0x41, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x908, 0x904, 0x900, 0x1C6, 0x1C6, 0x1C7}, - { - 155, 5775, 3850, 0x72, 0x04, 0x83, 0x02, 0xF8, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x90A, 0x906, 0x902, 0x1C5, 0x1C6, 0x1C7}, - { - 156, 5780, 3853, 0x71, 0x02, 0x42, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x90C, 0x908, 0x904, 0x1C5, 0x1C6, 0x1C6}, - { - 157, 5785, 3857, 0x72, 0x04, 0x85, 0x02, 0xF2, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x90E, 0x90A, 0x906, 0x1C4, 0x1C5, 0x1C6}, - { - 158, 5790, 3860, 0x71, 0x02, 0x43, 0x02, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x910, 0x90C, 0x908, 0x1C4, 0x1C5, 0x1C6}, - { - 159, 5795, 3863, 0x72, 0x04, 0x87, 0x02, 0xF2, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x912, 0x90E, 0x90A, 0x1C4, 0x1C4, 0x1C5}, - { - 160, 5800, 3867, 0x71, 0x02, 0x44, 0x01, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x914, 0x910, 0x90C, 0x1C3, 0x1C4, 0x1C5}, - { - 161, 5805, 3870, 0x72, 0x04, 0x89, 0x01, 0xED, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x916, 0x912, 0x90E, 0x1C3, 0x1C4, 0x1C4}, - { - 162, 5810, 3873, 0x71, 0x02, 0x45, 0x01, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x918, 0x914, 0x910, 0x1C2, 0x1C3, 0x1C4}, - { - 163, 5815, 3877, 0x72, 0x04, 0x8B, 0x01, 0xED, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x91A, 0x916, 0x912, 0x1C2, 0x1C3, 0x1C4}, - { - 164, 5820, 3880, 0x71, 0x02, 0x46, 0x01, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x91C, 0x918, 0x914, 0x1C2, 0x1C2, 0x1C3}, - { - 165, 5825, 3883, 0x72, 0x04, 0x8D, 0x01, 0xED, 0x00, 0x03, 0x14, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x91E, 0x91A, 0x916, 0x1C1, 0x1C2, 0x1C3}, - { - 166, 5830, 3887, 0x71, 0x02, 0x47, 0x01, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x920, 0x91C, 0x918, 0x1C1, 0x1C2, 0x1C2}, - { - 168, 5840, 3893, 0x71, 0x02, 0x48, 0x01, 0x0A, 0x01, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x924, 0x920, 0x91C, 0x1C0, 0x1C1, 0x1C2}, - { - 170, 5850, 3900, 0x71, 0x02, 0x49, 0x01, 0xE0, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x928, 0x924, 0x920, 0x1BF, 0x1C0, 0x1C1}, - { - 172, 5860, 3907, 0x71, 0x02, 0x4A, 0x01, 0xDE, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x92C, 0x928, 0x924, 0x1BF, 0x1BF, 0x1C0}, - { - 174, 5870, 3913, 0x71, 0x02, 0x4B, 0x00, 0xDB, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x930, 0x92C, 0x928, 0x1BE, 0x1BF, 0x1BF}, - { - 176, 5880, 3920, 0x71, 0x02, 0x4C, 0x00, 0xD8, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x934, 0x930, 0x92C, 0x1BD, 0x1BE, 0x1BF}, - { - 178, 5890, 3927, 0x71, 0x02, 0x4D, 0x00, 0xD6, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x938, 0x934, 0x930, 0x1BC, 0x1BD, 0x1BE}, - { - 180, 5900, 3933, 0x71, 0x02, 0x4E, 0x00, 0xD3, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x93C, 0x938, 0x934, 0x1BC, 0x1BC, 0x1BD}, - { - 182, 5910, 3940, 0x71, 0x02, 0x4F, 0x00, 0xD6, 0x00, 0x04, 0x0A, - 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x940, 0x93C, 0x938, 0x1BB, 0x1BC, 0x1BC}, - { - 1, 2412, 3216, 0x73, 0x09, 0x6C, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0D, 0x0C, 0x80, 0xFF, 0x88, 0x0D, - 0x0C, 0x80, 0x3C9, 0x3C5, 0x3C1, 0x43A, 0x43F, 0x443}, - { - 2, 2417, 3223, 0x73, 0x09, 0x71, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0B, 0x80, 0xFF, 0x88, 0x0C, - 0x0B, 0x80, 0x3CB, 0x3C7, 0x3C3, 0x438, 0x43D, 0x441}, - { - 3, 2422, 3229, 0x73, 0x09, 0x76, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, 0x80, 0xFF, 0x88, 0x0C, - 0x0A, 0x80, 0x3CD, 0x3C9, 0x3C5, 0x436, 0x43A, 0x43F}, - { - 4, 2427, 3236, 0x73, 0x09, 0x7B, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, 0x80, 0xFF, 0x88, 0x0C, - 0x0A, 0x80, 0x3CF, 0x3CB, 0x3C7, 0x434, 0x438, 0x43D}, - { - 5, 2432, 3243, 0x73, 0x09, 0x80, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x09, 0x80, 0xFF, 0x88, 0x0C, - 0x09, 0x80, 0x3D1, 0x3CD, 0x3C9, 0x431, 0x436, 0x43A}, - { - 6, 2437, 3249, 0x73, 0x09, 0x85, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0B, 0x08, 0x80, 0xFF, 0x88, 0x0B, - 0x08, 0x80, 0x3D3, 0x3CF, 0x3CB, 0x42F, 0x434, 0x438}, - { - 7, 2442, 3256, 0x73, 0x09, 0x8A, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x07, 0x80, 0xFF, 0x88, 0x0A, - 0x07, 0x80, 0x3D5, 0x3D1, 0x3CD, 0x42D, 0x431, 0x436}, - { - 8, 2447, 3263, 0x73, 0x09, 0x8F, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x06, 0x80, 0xFF, 0x88, 0x0A, - 0x06, 0x80, 0x3D7, 0x3D3, 0x3CF, 0x42B, 0x42F, 0x434}, - { - 9, 2452, 3269, 0x73, 0x09, 0x94, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x09, 0x06, 0x80, 0xFF, 0x88, 0x09, - 0x06, 0x80, 0x3D9, 0x3D5, 0x3D1, 0x429, 0x42D, 0x431}, - { - 10, 2457, 3276, 0x73, 0x09, 0x99, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x05, 0x80, 0xFF, 0x88, 0x08, - 0x05, 0x80, 0x3DB, 0x3D7, 0x3D3, 0x427, 0x42B, 0x42F}, - { - 11, 2462, 3283, 0x73, 0x09, 0x9E, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x04, 0x80, 0xFF, 0x88, 0x08, - 0x04, 0x80, 0x3DD, 0x3D9, 0x3D5, 0x424, 0x429, 0x42D}, - { - 12, 2467, 3289, 0x73, 0x09, 0xA3, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x03, 0x80, 0xFF, 0x88, 0x08, - 0x03, 0x80, 0x3DF, 0x3DB, 0x3D7, 0x422, 0x427, 0x42B}, - { - 13, 2472, 3296, 0x73, 0x09, 0xA8, 0x0F, 0x00, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x03, 0x80, 0xFF, 0x88, 0x07, - 0x03, 0x80, 0x3E1, 0x3DD, 0x3D9, 0x420, 0x424, 0x429}, - { - 14, 2484, 3312, 0x73, 0x09, 0xB4, 0x0F, 0xFF, 0x01, 0x07, 0x15, - 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x01, 0x80, 0xFF, 0x88, 0x07, - 0x01, 0x80, 0x3E6, 0x3E2, 0x3DE, 0x41B, 0x41F, 0x424} -}; - -static const struct chan_info_nphy_radio205x chan_info_nphyrev3_2056[] = { - { - 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, - { - 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, - { - 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, - { - 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, - { - 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, - { - 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, - { - 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, - { - 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, - { - 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, - { - 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, - { - 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, - { - 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, - { - 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, - { - 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, - { - 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, - { - 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, - { - 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, - { - 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, - { - 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xff, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, - { - 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, - { - 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, - { - 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, - { - 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, - { - 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, - { - 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, - { - 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, - { - 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xfc, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, - { - 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, - { - 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, - { - 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, - { - 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, - { - 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, - { - 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, - { - 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, - { - 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, - { - 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, - { - 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xfc, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, - { - 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfc, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, - { - 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, - { - 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, - { - 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, - { - 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, - { - 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, - { - 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, - { - 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, - { - 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, - { - 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x8f, 0x00, 0x05, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xfa, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, - { - 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, - { - 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, - { - 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, - { - 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, - { - 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7e, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00, 0x7e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, - { - 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7d, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xfa, 0x00, 0x7d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xfa, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, - { - 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xf8, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, - { - 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xf8, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, - { - 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5d, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xf8, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, - { - 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5c, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x08, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, - 0x00, 0xf8, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, - { - 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x5c, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, - { - 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, - { - 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, - { - 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, - { - 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, - { - 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, - { - 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2b, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x2b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, - { - 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2a, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x2a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, - { - 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, - { - 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, - { - 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x1a, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, - { - 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, - { - 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, - { - 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, - { - 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, - { - 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf8, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, - { - 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf6, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, - { - 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf6, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, - { - 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf6, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, - { - 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x07, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf6, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, - { - 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf6, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, - { - 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, - { - 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, - { - 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, - { - 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, - { - 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, - { - 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, - { - 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, - { - 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, - { - 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, - { - 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, - { - 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, - { - 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, - { - 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, - { - 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, - { - 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, - { - 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, - { - 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, - { - 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, - { - 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, - { - 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, - { - 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, - { - 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, - { - 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, - { - 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, - { - 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, - { - 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf4, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, - { - 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf2, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf2, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, - { - 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf2, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, - { - 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf2, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, - { - 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, - 0x00, 0xf2, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, - { - 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x05, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, - 0x00, 0xf2, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, - { - 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x05, 0x00, 0xf2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, - 0x00, 0xf2, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, - { - 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, - { - 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, - { - 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, - { - 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfd, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, - { - 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfb, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, - { - 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, - { - 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, - { - 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf7, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, - { - 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf6, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0f, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, - { - 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf5, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0d, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, - { - 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0d, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, - { - 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf3, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0d, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, - { - 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0d, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, - { - 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x05, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf0, 0x00, 0x05, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0d, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} -}; - -static const struct chan_info_nphy_radio205x chan_info_nphyrev4_2056_A1[] = { - { - 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, - { - 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, - { - 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, - { - 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, - { - 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, - { - 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, - { - 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, - { - 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, - { - 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, - { - 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, - { - 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, - { - 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, - { - 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, - { - 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, - { - 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, - { - 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, - { - 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, - { - 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xff, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, - { - 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, - { - 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, - { - 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, - { - 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, - { - 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, - { - 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, - { - 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, - { - 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, - { - 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfe, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, - { - 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, - { - 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, - { - 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, - { - 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, - { - 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, - { - 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, - { - 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, - { - 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, - { - 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, - { - 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfc, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, - { - 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, - { - 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, - { - 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, - { - 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, - { - 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, - { - 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, - { - 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, - { - 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, - { - 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, - { - 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x8f, 0x00, 0x08, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xfa, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, - { - 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, - { - 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, - { - 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, - { - 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, - { - 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7e, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x7e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, - { - 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7d, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x7d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, - { - 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, - { - 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, - { - 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5d, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, - { - 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5c, 0x00, 0x07, 0x00, 0x7f, - 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, - 0x00, 0xf8, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, - { - 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x5c, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x5c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, - { - 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, - { - 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, - { - 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, - { - 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, - { - 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, - { - 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2b, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x2b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, - { - 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2a, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x2a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, - { - 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, - { - 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, - 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, - 0x00, 0xf6, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, - { - 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x1a, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x1a, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, - { - 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, - { - 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, - { - 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, - { - 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, - { - 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, - { - 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, - { - 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, - { - 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, - { - 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x07, 0x00, 0x04, 0x00, 0x7f, - 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, - 0x00, 0xf4, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, - { - 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, - { - 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, - { - 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, - { - 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, - { - 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, - { - 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, - { - 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, - { - 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, - { - 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, - { - 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, - { - 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, - { - 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, - { - 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, - { - 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, - { - 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, - { - 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, - { - 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, - { - 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, - 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, - 0x00, 0xf2, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, - { - 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, - { - 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, - { - 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, - { - 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, - { - 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, - { - 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, - { - 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, - { - 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, - { - 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, - { - 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, - { - 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, - { - 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, - { - 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, - 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, - 0x00, 0xf0, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, - { - 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf0, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, - { - 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x07, 0x00, 0xf0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, - 0x00, 0xf0, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, - { - 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, - { - 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, - { - 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, - { - 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfd, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, - { - 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfb, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, - { - 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, - { - 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf8, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, - { - 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf7, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, - { - 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf6, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, - { - 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf5, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, - { - 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, - { - 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf3, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, - { - 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, - { - 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x04, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0e, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} -}; - -static const struct chan_info_nphy_radio205x chan_info_nphyrev5_2056v5[] = { - { - 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0f, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, - { - 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, - { - 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, - { - 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, - { - 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, - { - 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, - { - 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, - { - 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, - { - 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, - { - 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, - { - 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, - { - 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, - { - 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, - { - 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, - { - 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, - { - 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, - { - 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, - { - 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, - { - 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, - { - 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, - { - 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, - { - 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, - { - 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, - 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, - { - 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, - { - 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, - { - 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, - { - 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, - { - 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, - { - 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, - { - 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, - { - 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xea, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x6e, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, - { - 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x6d, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, - { - 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x6d, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, - { - 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xd9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x6d, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, - { - 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xd8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, - { - 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, - { - 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, - { - 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, - { - 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, - { - 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, - { - 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb7, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6b, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, - { - 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb7, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6b, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, - { - 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa7, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x6b, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, - { - 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x6b, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, - { - 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x5b, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, - { - 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x96, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x5a, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, - { - 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x5a, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, - { - 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x5a, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, - { - 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x5a, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, - { - 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x5a, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, - { - 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x85, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x59, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, - { - 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x59, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, - { - 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x59, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, - { - 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, - { - 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x74, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, - { - 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, - { - 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, - { - 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x78, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, - { - 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x78, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, - { - 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x78, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, - { - 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x63, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x78, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, - { - 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x77, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, - { - 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x77, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, - { - 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x77, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, - { - 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x76, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, - { - 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x76, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, - { - 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x76, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, - { - 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x76, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, - { - 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x76, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, - { - 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x76, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, - { - 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x76, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, - { - 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x75, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, - { - 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x75, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, - { - 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x75, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, - { - 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x74, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, - { - 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x74, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, - { - 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x74, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, - { - 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x74, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, - { - 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x74, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, - { - 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x74, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, - { - 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x74, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, - { - 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x84, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, - { - 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x83, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, - { - 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x83, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, - { - 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x83, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, - { - 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x83, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, - { - 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x83, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, - { - 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x83, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, - { - 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, - { - 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, - { - 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, - { - 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, - { - 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, - { - 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, - { - 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, - { - 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, - { - 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, - { - 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, - { - 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, - { - 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, - { - 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x82, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, - { - 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x72, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, - { - 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x72, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, - { - 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x72, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, - { - 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x72, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, - { - 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x71, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, - { - 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x71, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, - { - 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x71, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, - { - 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x71, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, - { - 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x71, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, - { - 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0b, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, - { - 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, - { - 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, - { - 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, - { - 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, - { - 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, - { - 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, - { - 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x08, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, - { - 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x07, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, - { - 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, - { - 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x05, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, - { - 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, - { - 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, - { - 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} -}; - -static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v6[] = { - { - 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, - { - 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, - { - 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, - { - 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, - { - 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, - { - 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, - { - 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, - { - 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, - { - 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, - { - 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, - { - 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, - { - 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, - { - 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, - { - 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, - { - 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, - { - 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, - { - 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, - { - 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, - { - 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, - { - 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, - { - 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, - { - 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, - { - 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, - { - 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, - { - 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, - { - 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, - { - 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, - { - 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, - { - 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, - { - 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, - { - 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, - { - 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, - { - 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, - { - 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, - { - 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, - { - 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, - { - 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, - { - 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, - { - 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, - { - 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, - { - 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, - { - 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, - { - 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, - { - 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, - { - 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, - { - 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, - { - 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, - { - 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, - { - 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, - { - 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, - { - 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, - { - 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, - { - 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, - { - 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, - { - 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, - { - 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, - { - 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, - { - 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, - { - 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, - { - 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, - { - 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, - { - 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, - { - 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, - { - 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, - { - 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, - { - 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, - { - 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, - { - 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, - { - 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, - { - 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, - { - 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, - { - 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, - { - 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, - { - 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, - { - 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, - { - 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, - { - 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, - { - 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, - { - 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, - { - 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, - { - 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, - { - 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, - { - 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, - { - 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, - { - 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, - { - 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6d, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, - { - 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, - { - 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, - { - 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, - { - 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, - { - 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, - { - 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, - { - 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, - { - 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, - { - 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, - { - 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, - { - 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, - { - 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, - { - 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, - { - 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, - { - 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, - { - 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, - { - 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, - { - 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, - { - 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, - { - 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, - { - 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, - { - 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, - { - 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, - { - 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, - { - 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, - { - 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, - { - 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, - { - 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, - { - 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, - { - 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, - { - 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, - { - 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, - { - 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, - { - 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, - { - 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, - { - 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, - { - 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, - { - 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} -}; - -static const struct chan_info_nphy_radio205x chan_info_nphyrev5n6_2056v7[] = { - { - 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0f, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, - { - 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, - { - 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, - { - 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, - { - 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, - { - 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, - { - 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, - { - 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, - { - 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, - { - 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, - { - 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, - { - 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, - { - 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, - { - 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, - { - 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, - 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, - { - 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, - { - 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, - { - 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, - { - 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, - { - 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, - { - 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, - { - 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, - 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, - { - 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, - 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, - { - 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, - { - 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, - { - 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, - { - 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, - { - 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, - { - 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, - { - 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xfa, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, - 0x00, 0x6e, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, - { - 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xea, 0x00, 0x06, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x6e, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, - { - 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xe9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x6d, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, - { - 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xe9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x6d, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, - { - 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xd9, 0x00, 0x05, 0x00, 0x70, - 0x00, 0x08, 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, - 0x00, 0x6d, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, - { - 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xd8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, - { - 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, - { - 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, - { - 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, - { - 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, - { - 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb8, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6c, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, - { - 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xb7, 0x00, 0x04, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6b, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, - { - 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xb7, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, - 0x00, 0x6b, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, - { - 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa7, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x6b, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, - { - 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x6b, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, - { - 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x7b, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, - { - 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x96, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x7a, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, - { - 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x7a, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, - { - 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, - 0x00, 0x7a, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, - { - 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x7a, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, - { - 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x95, 0x00, 0x03, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x7a, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, - { - 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x85, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x79, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, - { - 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x79, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, - { - 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, - 0x00, 0x79, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, - { - 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x79, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, - { - 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x74, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x79, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, - { - 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x78, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, - { - 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x78, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, - { - 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x78, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, - { - 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x78, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, - { - 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, - 0x00, 0x78, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, - { - 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x63, 0x00, 0x01, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x78, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, - { - 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x77, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, - { - 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x77, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, - { - 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, - 0x00, 0x77, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, - { - 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x52, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x76, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, - { - 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x52, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x86, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, - { - 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x86, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, - { - 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x86, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, - { - 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x86, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, - { - 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x86, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, - { - 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x86, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, - { - 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x51, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x02, 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, - 0x00, 0x85, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, - { - 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x85, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, - { - 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x85, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, - { - 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x84, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, - { - 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x84, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, - { - 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, - { - 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, - { - 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, - { - 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, - { - 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, - { - 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x01, 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, - 0x00, 0x94, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, - { - 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, - { - 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, - { - 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, - { - 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, - { - 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x30, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, - { - 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x93, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, - { - 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, - { - 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, - { - 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, - { - 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, - { - 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, - { - 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, - { - 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, - { - 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, - { - 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, - { - 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, - { - 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, - { - 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, - { - 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, - { - 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, - { - 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, - { - 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, - { - 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x92, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, - { - 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, - { - 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, - { - 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, - { - 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, - { - 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, - 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, - 0x00, 0x91, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, - { - 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0b, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, - { - 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, - { - 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0f, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, - { - 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, - { - 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, - { - 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x76, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, - { - 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x66, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, - { - 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x55, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, - { - 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0e, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, - { - 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, - { - 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, - { - 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, - { - 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, - { - 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0d, 0x00, 0x08, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} -}; - -static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v8[] = { - { - 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, - { - 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, - { - 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, - { - 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, - { - 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, - { - 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, - { - 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, - { - 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, - { - 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, - { - 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, - { - 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, - { - 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, - { - 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, - { - 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, - { - 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, - { - 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, - { - 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, - { - 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, - { - 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, - { - 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, - { - 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, - { - 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, - { - 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, - { - 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, - { - 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, - { - 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, - { - 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, - { - 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, - { - 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, - { - 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, - { - 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, - { - 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, - { - 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, - { - 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, - { - 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, - { - 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, - { - 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, - { - 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, - { - 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, - { - 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, - { - 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, - { - 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, - { - 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, - { - 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, - { - 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, - { - 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, - { - 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, - { - 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, - { - 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, - { - 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, - { - 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, - { - 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, - { - 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, - { - 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, - { - 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, - { - 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, - { - 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, - { - 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, - { - 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, - { - 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, - { - 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, - { - 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, - { - 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, - { - 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, - { - 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, - { - 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, - { - 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, - { - 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, - { - 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, - { - 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, - { - 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, - { - 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, - { - 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, - { - 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, - { - 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, - { - 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, - { - 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, - { - 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, - { - 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, - { - 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, - { - 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, - { - 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, - { - 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, - { - 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, - { - 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, - { - 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6d, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, - { - 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, - { - 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, - { - 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, - { - 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, - { - 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, - { - 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, - { - 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, - { - 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, - { - 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, - { - 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, - { - 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, - { - 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, - { - 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, - { - 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, - { - 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, - { - 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, - { - 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, - { - 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, - { - 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, - { - 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, - { - 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, - { - 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, - { - 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, - { - 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, - { - 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, - { - 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, - { - 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, - { - 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, - { - 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, - { - 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, - { - 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, - { - 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, - { - 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, - { - 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, - { - 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, - { - 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, - { - 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, - { - 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} -}; - -static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v11[] = { - { - 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, - { - 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, - { - 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, - { - 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, - { - 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, - { - 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, - { - 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, - { - 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, - { - 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, - { - 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, - { - 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, - { - 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, - { - 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, - { - 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, - { - 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, - { - 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, - { - 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, - { - 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, - { - 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, - { - 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, - { - 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, - { - 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, - { - 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, - 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, - 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, - { - 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, - { - 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, - { - 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, - 0x00, 0x6f, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, - { - 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, - { - 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, - { - 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, - { - 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, - { - 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, - { - 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, - { - 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, - { - 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, - 0x00, 0x6f, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, - { - 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, - { - 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, - { - 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, - { - 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, - { - 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, - { - 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, - 0x00, 0x6f, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, - { - 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, - { - 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, - { - 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, - 0x00, 0x6f, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, - { - 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, - { - 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, - { - 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, - { - 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, - { - 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, - { - 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, - { - 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, - { - 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, - 0x00, 0x6f, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, - { - 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, - { - 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, - { - 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, - { - 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, - { - 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, - { - 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, - { - 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, - { - 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, - { - 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, - { - 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, - { - 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, - { - 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, - { - 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, - { - 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x09, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, - 0x00, 0x6f, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, - { - 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, - { - 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, - { - 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, - { - 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, - 0x00, 0x6f, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, - { - 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, - { - 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, - { - 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, - { - 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x07, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, - 0x00, 0x6f, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, - { - 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, - { - 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, - { - 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, - { - 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6f, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, - { - 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, - { - 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, - { - 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, - { - 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x05, 0x05, 0x02, 0x15, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, - { - 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6e, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, - { - 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x05, 0x05, 0x02, 0x15, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, - { - 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, - { - 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x05, 0x05, 0x02, 0x15, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, - 0x00, 0x6d, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, - { - 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6d, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, - { - 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x05, 0x05, 0x02, 0x15, 0x01, - 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, - { - 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, - { - 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x05, 0x05, 0x02, 0x15, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6c, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, - { - 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, - { - 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x05, 0x05, 0x02, 0x15, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, - { - 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, - { - 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x05, 0x05, 0x02, 0x15, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, - { - 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, - { - 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x05, 0x05, 0x02, 0x15, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, - { - 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6b, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, - { - 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x05, 0x05, 0x02, 0x15, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, - { - 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, - { - 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x05, 0x05, 0x02, 0x15, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, - { - 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x6a, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, - { - 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x05, 0x05, 0x02, 0x15, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, - { - 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x00, 0x69, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, - { - 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, - { - 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, - { - 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x69, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, - { - 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, - { - 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, - { - 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, - { - 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, - { - 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x02, 0x0c, 0x01, - 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, - 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, - 0x00, 0x68, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, - { - 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, - { - 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, - { - 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0b, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, - { - 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, - { - 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, - { - 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, - { - 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, - { - 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, - { - 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, - { - 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x0a, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, - { - 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, - { - 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, - { - 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, - { - 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x06, 0x06, 0x04, 0x2b, 0x01, - 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, - 0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} -}; - -static const struct chan_info_nphy_radio2057 chan_info_nphyrev7_2057_rev4[] = { - { - 184, 4920, 0x68, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xec, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07b4, 0x07b0, 0x07ac, 0x0214, - 0x0215, - 0x0216, - }, - { - 186, 4930, 0x6b, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xed, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07b8, 0x07b4, 0x07b0, 0x0213, - 0x0214, - 0x0215, - }, - { - 188, 4940, 0x6e, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xee, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07bc, 0x07b8, 0x07b4, 0x0212, - 0x0213, - 0x0214, - }, - { - 190, 4950, 0x72, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xef, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07c0, 0x07bc, 0x07b8, 0x0211, - 0x0212, - 0x0213, - }, - { - 192, 4960, 0x75, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf0, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07c4, 0x07c0, 0x07bc, 0x020f, - 0x0211, - 0x0212, - }, - { - 194, 4970, 0x78, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf1, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07c8, 0x07c4, 0x07c0, 0x020e, - 0x020f, - 0x0211, - }, - { - 196, 4980, 0x7c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf2, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07cc, 0x07c8, 0x07c4, 0x020d, - 0x020e, - 0x020f, - }, - { - 198, 4990, 0x7f, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf3, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07d0, 0x07cc, 0x07c8, 0x020c, - 0x020d, - 0x020e, - }, - { - 200, 5000, 0x82, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf4, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07d4, 0x07d0, 0x07cc, 0x020b, - 0x020c, - 0x020d, - }, - { - 202, 5010, 0x86, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf5, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07d8, 0x07d4, 0x07d0, 0x020a, - 0x020b, - 0x020c, - }, - { - 204, 5020, 0x89, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf6, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07dc, 0x07d8, 0x07d4, 0x0209, - 0x020a, - 0x020b, - }, - { - 206, 5030, 0x8c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf7, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07e0, 0x07dc, 0x07d8, 0x0208, - 0x0209, - 0x020a, - }, - { - 208, 5040, 0x90, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf8, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07e4, 0x07e0, 0x07dc, 0x0207, - 0x0208, - 0x0209, - }, - { - 210, 5050, 0x93, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf9, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07e8, 0x07e4, 0x07e0, 0x0206, - 0x0207, - 0x0208, - }, - { - 212, 5060, 0x96, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfa, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xe3, 0x00, 0xef, 0x00, - 0x00, 0x0f, 0x0f, 0xe3, 0x00, 0xef, 0x07ec, 0x07e8, 0x07e4, 0x0205, - 0x0206, - 0x0207, - }, - { - 214, 5070, 0x9a, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfb, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x00, - 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x07f0, 0x07ec, 0x07e8, 0x0204, - 0x0205, - 0x0206, - }, - { - 216, 5080, 0x9d, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfc, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x00, - 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x07f4, 0x07f0, 0x07ec, 0x0203, - 0x0204, - 0x0205, - }, - { - 218, 5090, 0xa0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfd, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, - 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x07f8, 0x07f4, 0x07f0, 0x0202, - 0x0203, - 0x0204, - }, - { - 220, 5100, 0xa4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfe, 0x01, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, - 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x07fc, 0x07f8, 0x07f4, 0x0201, - 0x0202, - 0x0203, - }, - { - 222, 5110, 0xa7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xff, 0x01, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, - 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x0800, 0x07fc, 0x07f8, 0x0200, - 0x0201, - 0x0202, - }, - { - 224, 5120, 0xaa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x00, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, - 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x0804, 0x0800, 0x07fc, 0x01ff, - 0x0200, - 0x0201, - }, - { - 226, 5130, 0xae, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x01, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, - 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x0808, 0x0804, 0x0800, 0x01fe, - 0x01ff, - 0x0200, - }, - { - 228, 5140, 0xb1, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x02, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0e, 0xe3, 0x00, 0xd6, 0x00, - 0x00, 0x0e, 0x0e, 0xe3, 0x00, 0xd6, 0x080c, 0x0808, 0x0804, 0x01fd, - 0x01fe, - 0x01ff, - }, - { - 32, 5160, 0xb8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x04, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x00, - 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x0814, 0x0810, 0x080c, 0x01fb, - 0x01fc, - 0x01fd, - }, - { - 34, 5170, 0xbb, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x05, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x00, - 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x0818, 0x0814, 0x0810, 0x01fa, - 0x01fb, - 0x01fc, - }, - { - 36, 5180, 0xbe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x06, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, - 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x081c, 0x0818, 0x0814, 0x01f9, - 0x01fa, - 0x01fb, - }, - { - 38, 5190, 0xc2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x07, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, - 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x0820, 0x081c, 0x0818, 0x01f8, - 0x01f9, - 0x01fa, - }, - { - 40, 5200, 0xc5, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x08, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, - 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x0824, 0x0820, 0x081c, 0x01f7, - 0x01f8, - 0x01f9, - }, - { - 42, 5210, 0xc8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x09, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, - 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x0828, 0x0824, 0x0820, 0x01f6, - 0x01f7, - 0x01f8, - }, - { - 44, 5220, 0xcc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0a, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, - 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x082c, 0x0828, 0x0824, 0x01f5, - 0x01f6, - 0x01f7, - }, - { - 46, 5230, 0xcf, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0b, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, - 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x0830, 0x082c, 0x0828, 0x01f4, - 0x01f5, - 0x01f6, - }, - { - 48, 5240, 0xd2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0c, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, - 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x0834, 0x0830, 0x082c, 0x01f3, - 0x01f4, - 0x01f5, - }, - { - 50, 5250, 0xd6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0d, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, - 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x0838, 0x0834, 0x0830, 0x01f2, - 0x01f3, - 0x01f4, - }, - { - 52, 5260, 0xd9, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0e, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x00, - 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x083c, 0x0838, 0x0834, 0x01f1, - 0x01f2, - 0x01f3, - }, - { - 54, 5270, 0xdc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0f, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x00, - 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x0840, 0x083c, 0x0838, 0x01f0, - 0x01f1, - 0x01f2, - }, - { - 56, 5280, 0xe0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x10, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x00, - 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x0844, 0x0840, 0x083c, 0x01f0, - 0x01f0, - 0x01f1, - }, - { - 58, 5290, 0xe3, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x11, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x00, - 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x0848, 0x0844, 0x0840, 0x01ef, - 0x01f0, - 0x01f0, - }, - { - 60, 5300, 0xe6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x12, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x00, - 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x084c, 0x0848, 0x0844, 0x01ee, - 0x01ef, - 0x01f0, - }, - { - 62, 5310, 0xea, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x13, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x00, - 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x0850, 0x084c, 0x0848, 0x01ed, - 0x01ee, - 0x01ef, - }, - { - 64, 5320, 0xed, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x14, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x00, - 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x0854, 0x0850, 0x084c, 0x01ec, - 0x01ed, - 0x01ee, - }, - { - 66, 5330, 0xf0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x15, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x00, - 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x0858, 0x0854, 0x0850, 0x01eb, - 0x01ec, - 0x01ed, - }, - { - 68, 5340, 0xf4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x16, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0c, 0xc3, 0x00, 0xa1, 0x00, - 0x00, 0x0a, 0x0c, 0xc3, 0x00, 0xa1, 0x085c, 0x0858, 0x0854, 0x01ea, - 0x01eb, - 0x01ec, - }, - { - 70, 5350, 0xf7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x17, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, - 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x0860, 0x085c, 0x0858, 0x01e9, - 0x01ea, - 0x01eb, - }, - { - 72, 5360, 0xfa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x18, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, - 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x0864, 0x0860, 0x085c, 0x01e8, - 0x01e9, - 0x01ea, - }, - { - 74, 5370, 0xfe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x19, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, - 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x0868, 0x0864, 0x0860, 0x01e7, - 0x01e8, - 0x01e9, - }, - { - 76, 5380, 0x01, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1a, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, - 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x086c, 0x0868, 0x0864, 0x01e6, - 0x01e7, - 0x01e8, - }, - { - 78, 5390, 0x04, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1b, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0a, 0xa3, 0x00, 0xa1, 0x00, - 0x00, 0x0a, 0x0a, 0xa3, 0x00, 0xa1, 0x0870, 0x086c, 0x0868, 0x01e5, - 0x01e6, - 0x01e7, - }, - { - 80, 5400, 0x08, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1c, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x00, - 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x0874, 0x0870, 0x086c, 0x01e5, - 0x01e5, - 0x01e6, - }, - { - 82, 5410, 0x0b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1d, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x00, - 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x0878, 0x0874, 0x0870, 0x01e4, - 0x01e5, - 0x01e5, - }, - { - 84, 5420, 0x0e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1e, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0xa3, 0x00, 0x90, 0x00, - 0x00, 0x09, 0x09, 0xa3, 0x00, 0x90, 0x087c, 0x0878, 0x0874, 0x01e3, - 0x01e4, - 0x01e5, - }, - { - 86, 5430, 0x12, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1f, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x00, - 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x0880, 0x087c, 0x0878, 0x01e2, - 0x01e3, - 0x01e4, - }, - { - 88, 5440, 0x15, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x20, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x00, - 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x0884, 0x0880, 0x087c, 0x01e1, - 0x01e2, - 0x01e3, - }, - { - 90, 5450, 0x18, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x21, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x00, - 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x0888, 0x0884, 0x0880, 0x01e0, - 0x01e1, - 0x01e2, - }, - { - 92, 5460, 0x1c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x22, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x08, 0x93, 0x00, 0x90, 0x00, - 0x00, 0x08, 0x08, 0x93, 0x00, 0x90, 0x088c, 0x0888, 0x0884, 0x01df, - 0x01e0, - 0x01e1, - }, - { - 94, 5470, 0x1f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x23, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x08, 0x93, 0x00, 0x60, 0x00, - 0x00, 0x08, 0x08, 0x93, 0x00, 0x60, 0x0890, 0x088c, 0x0888, 0x01de, - 0x01df, - 0x01e0, - }, - { - 96, 5480, 0x22, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x24, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, - 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x0894, 0x0890, 0x088c, 0x01dd, - 0x01de, - 0x01df, - }, - { - 98, 5490, 0x26, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x25, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, - 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x0898, 0x0894, 0x0890, 0x01dd, - 0x01dd, - 0x01de, - }, - { - 100, 5500, 0x29, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x26, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, - 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x089c, 0x0898, 0x0894, 0x01dc, - 0x01dd, - 0x01dd, - }, - { - 102, 5510, 0x2c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x27, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, - 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x08a0, 0x089c, 0x0898, 0x01db, - 0x01dc, - 0x01dd, - }, - { - 104, 5520, 0x30, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x28, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x00, - 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x08a4, 0x08a0, 0x089c, 0x01da, - 0x01db, - 0x01dc, - }, - { - 106, 5530, 0x33, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x29, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x00, - 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x08a8, 0x08a4, 0x08a0, 0x01d9, - 0x01da, - 0x01db, - }, - { - 108, 5540, 0x36, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2a, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x00, - 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x08ac, 0x08a8, 0x08a4, 0x01d8, - 0x01d9, - 0x01da, - }, - { - 110, 5550, 0x3a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2b, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x00, - 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x08b0, 0x08ac, 0x08a8, 0x01d7, - 0x01d8, - 0x01d9, - }, - { - 112, 5560, 0x3d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2c, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x00, - 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x08b4, 0x08b0, 0x08ac, 0x01d7, - 0x01d7, - 0x01d8, - }, - { - 114, 5570, 0x40, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2d, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x00, - 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x08b8, 0x08b4, 0x08b0, 0x01d6, - 0x01d7, - 0x01d7, - }, - { - 116, 5580, 0x44, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2e, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x07, 0x05, 0x83, 0x00, 0x60, 0x00, - 0x00, 0x07, 0x05, 0x83, 0x00, 0x60, 0x08bc, 0x08b8, 0x08b4, 0x01d5, - 0x01d6, - 0x01d7, - }, - { - 118, 5590, 0x47, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2f, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x07, 0x04, 0x83, 0x00, 0x60, 0x00, - 0x00, 0x07, 0x04, 0x83, 0x00, 0x60, 0x08c0, 0x08bc, 0x08b8, 0x01d4, - 0x01d5, - 0x01d6, - }, - { - 120, 5600, 0x4a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x30, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x07, 0x04, 0x73, 0x00, 0x30, 0x00, - 0x00, 0x07, 0x04, 0x73, 0x00, 0x30, 0x08c4, 0x08c0, 0x08bc, 0x01d3, - 0x01d4, - 0x01d5, - }, - { - 122, 5610, 0x4e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x31, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, - 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08c8, 0x08c4, 0x08c0, 0x01d2, - 0x01d3, - 0x01d4, - }, - { - 124, 5620, 0x51, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x32, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, - 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08cc, 0x08c8, 0x08c4, 0x01d2, - 0x01d2, - 0x01d3, - }, - { - 126, 5630, 0x54, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x33, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, - 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08d0, 0x08cc, 0x08c8, 0x01d1, - 0x01d2, - 0x01d2, - }, - { - 128, 5640, 0x58, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x34, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, - 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08d4, 0x08d0, 0x08cc, 0x01d0, - 0x01d1, - 0x01d2, - }, - { - 130, 5650, 0x5b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x35, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x00, - 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x08d8, 0x08d4, 0x08d0, 0x01cf, - 0x01d0, - 0x01d1, - }, - { - 132, 5660, 0x5e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x36, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x00, - 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x08dc, 0x08d8, 0x08d4, 0x01ce, - 0x01cf, - 0x01d0, - }, - { - 134, 5670, 0x62, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x37, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x03, 0x63, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x03, 0x63, 0x00, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, - 0x01ce, - 0x01cf, - }, - { - 136, 5680, 0x65, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x38, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, - 0x01ce, - 0x01ce, - }, - { - 138, 5690, 0x68, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x39, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, - 0x01cd, - 0x01ce, - }, - { - 140, 5700, 0x6c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3a, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, - 0x01cc, - 0x01cd, - }, - { - 142, 5710, 0x6f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3b, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, - 0x01cb, - 0x01cc, - }, - { - 144, 5720, 0x72, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3c, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, - 0x01ca, - 0x01cb, - }, - { - 145, 5725, 0x74, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x79, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x05, 0x01, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x01, 0x53, 0x00, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, - 0x01ca, - 0x01cb, - }, - { - 146, 5730, 0x76, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3d, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, - 0x01c9, - 0x01ca, - }, - { - 147, 5735, 0x77, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7b, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, - 0x01c9, - 0x01ca, - }, - { - 148, 5740, 0x79, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3e, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, - 0x01c9, - 0x01c9, - }, - { - 149, 5745, 0x7b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7d, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, - 0x01c8, - 0x01c9, - }, - { - 150, 5750, 0x7c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3f, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, - 0x01c8, - 0x01c9, - }, - { - 151, 5755, 0x7e, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7f, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, - 0x01c8, - 0x01c8, - }, - { - 152, 5760, 0x80, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x40, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, - 0x01c7, - 0x01c8, - }, - { - 153, 5765, 0x81, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x81, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, - 0x01c7, - 0x01c8, - }, - { - 154, 5770, 0x83, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x41, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, - 0x01c6, - 0x01c7, - }, - { - 155, 5775, 0x85, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x83, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, - 0x01c6, - 0x01c7, - }, - { - 156, 5780, 0x86, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x42, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x03, 0x01, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x01, 0x43, 0x00, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, - 0x01c6, - 0x01c6, - }, - { - 157, 5785, 0x88, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x85, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, - 0x01c5, - 0x01c6, - }, - { - 158, 5790, 0x8a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x43, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, - 0x01c5, - 0x01c6, - }, - { - 159, 5795, 0x8b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x87, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, - 0x01c4, - 0x01c5, - }, - { - 160, 5800, 0x8d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x44, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, - 0x01c4, - 0x01c5, - }, - { - 161, 5805, 0x8f, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x89, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, - 0x01c4, - 0x01c4, - }, - { - 162, 5810, 0x90, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x45, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, - 0x01c3, - 0x01c4, - }, - { - 163, 5815, 0x92, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8b, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, - 0x01c3, - 0x01c4, - }, - { - 164, 5820, 0x94, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x46, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, - 0x01c2, - 0x01c3, - }, - { - 165, 5825, 0x95, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8d, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, - 0x01c2, - 0x01c3, - }, - { - 166, 5830, 0x97, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x47, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, - 0x01c2, - 0x01c2, - }, - { - 168, 5840, 0x9a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x48, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, - 0x01c1, - 0x01c2, - }, - { - 170, 5850, 0x9e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x49, 0x02, 0x04, - 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, - 0x01c0, - 0x01c1, - }, - { - 172, 5860, 0xa1, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4a, 0x02, 0x04, - 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, - 0x01bf, - 0x01c0, - }, - { - 174, 5870, 0xa4, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4b, 0x02, 0x04, - 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, - 0x01bf, - 0x01bf, - }, - { - 176, 5880, 0xa8, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4c, 0x02, 0x03, - 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, - 0x01be, - 0x01bf, - }, - { - 178, 5890, 0xab, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4d, 0x02, 0x03, - 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, - 0x01bd, - 0x01be, - }, - { - 180, 5900, 0xae, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4e, 0x02, 0x03, - 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, - 0x01bc, - 0x01bd, - }, - { - 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0f, - 0x0a, 0x00, 0x0a, 0x00, 0x71, 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, - 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03c9, 0x03c5, 0x03c1, 0x043a, - 0x043f, - 0x0443, - }, - { - 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0f, - 0x0a, 0x00, 0x0a, 0x00, 0x71, 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, - 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cb, 0x03c7, 0x03c3, 0x0438, - 0x043d, - 0x0441, - }, - { - 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0f, - 0x09, 0x00, 0x09, 0x00, 0x71, 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, - 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cd, 0x03c9, 0x03c5, 0x0436, - 0x043a, - 0x043f, - }, - { - 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0f, - 0x09, 0x00, 0x09, 0x00, 0x71, 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, - 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cf, 0x03cb, 0x03c7, 0x0434, - 0x0438, - 0x043d, - }, - { - 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0f, - 0x08, 0x00, 0x08, 0x00, 0x51, 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x51, - 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d1, 0x03cd, 0x03c9, 0x0431, - 0x0436, - 0x043a, - }, - { - 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0f, - 0x08, 0x00, 0x08, 0x00, 0x51, 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x51, - 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d3, 0x03cf, 0x03cb, 0x042f, - 0x0434, - 0x0438, - }, - { - 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0f, - 0x07, 0x00, 0x07, 0x00, 0x51, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x51, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d5, 0x03d1, 0x03cd, 0x042d, - 0x0431, - 0x0436, - }, - { - 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0f, - 0x07, 0x00, 0x07, 0x00, 0x31, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d7, 0x03d3, 0x03cf, 0x042b, - 0x042f, - 0x0434, - }, - { - 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0f, - 0x07, 0x00, 0x07, 0x00, 0x31, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d9, 0x03d5, 0x03d1, 0x0429, - 0x042d, - 0x0431, - }, - { - 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0f, - 0x06, 0x00, 0x06, 0x00, 0x31, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, - 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03db, 0x03d7, 0x03d3, 0x0427, - 0x042b, - 0x042f, - }, - { - 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0f, - 0x06, 0x00, 0x06, 0x00, 0x31, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, - 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03dd, 0x03d9, 0x03d5, 0x0424, - 0x0429, - 0x042d, - }, - { - 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0f, - 0x05, 0x00, 0x05, 0x00, 0x11, 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x11, - 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03df, 0x03db, 0x03d7, 0x0422, - 0x0427, - 0x042b, - }, - { - 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0f, - 0x05, 0x00, 0x05, 0x00, 0x11, 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x11, - 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03e1, 0x03dd, 0x03d9, 0x0420, - 0x0424, - 0x0429, - }, - { - 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0f, - 0x04, 0x00, 0x04, 0x00, 0x11, 0x43, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x11, - 0x43, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03e6, 0x03e2, 0x03de, 0x041b, - 0x041f, - 0x0424} -}; - -static const struct chan_info_nphy_radio2057_rev5 -chan_info_nphyrev8_2057_rev5[] = { - { - 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0d, - 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03c9, 0x03c5, 0x03c1, - 0x043a, 0x043f, 0x0443}, - { - 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0d, - 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03cb, 0x03c7, 0x03c3, - 0x0438, 0x043d, 0x0441}, - { - 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0d, - 0x08, 0x0e, 0x61, 0x03, 0xef, 0x61, 0x03, 0xef, 0x03cd, 0x03c9, 0x03c5, - 0x0436, 0x043a, 0x043f}, - { - 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0c, - 0x08, 0x0e, 0x61, 0x03, 0xdf, 0x61, 0x03, 0xdf, 0x03cf, 0x03cb, 0x03c7, - 0x0434, 0x0438, 0x043d}, - { - 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0c, - 0x07, 0x0d, 0x61, 0x03, 0xcf, 0x61, 0x03, 0xcf, 0x03d1, 0x03cd, 0x03c9, - 0x0431, 0x0436, 0x043a}, - { - 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0c, - 0x07, 0x0d, 0x61, 0x03, 0xbf, 0x61, 0x03, 0xbf, 0x03d3, 0x03cf, 0x03cb, - 0x042f, 0x0434, 0x0438}, - { - 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0b, - 0x07, 0x0d, 0x61, 0x03, 0xaf, 0x61, 0x03, 0xaf, 0x03d5, 0x03d1, 0x03cd, - 0x042d, 0x0431, 0x0436}, - { - 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0b, - 0x07, 0x0d, 0x61, 0x03, 0x9f, 0x61, 0x03, 0x9f, 0x03d7, 0x03d3, 0x03cf, - 0x042b, 0x042f, 0x0434}, - { - 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0b, - 0x07, 0x0d, 0x61, 0x03, 0x8f, 0x61, 0x03, 0x8f, 0x03d9, 0x03d5, 0x03d1, - 0x0429, 0x042d, 0x0431}, - { - 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0b, - 0x07, 0x0c, 0x61, 0x03, 0x7f, 0x61, 0x03, 0x7f, 0x03db, 0x03d7, 0x03d3, - 0x0427, 0x042b, 0x042f}, - { - 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0b, - 0x07, 0x0c, 0x61, 0x03, 0x6f, 0x61, 0x03, 0x6f, 0x03dd, 0x03d9, 0x03d5, - 0x0424, 0x0429, 0x042d}, - { - 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0b, - 0x06, 0x0c, 0x61, 0x03, 0x5f, 0x61, 0x03, 0x5f, 0x03df, 0x03db, 0x03d7, - 0x0422, 0x0427, 0x042b}, - { - 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0a, - 0x06, 0x0b, 0x61, 0x03, 0x4f, 0x61, 0x03, 0x4f, 0x03e1, 0x03dd, 0x03d9, - 0x0420, 0x0424, 0x0429}, - { - 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0a, - 0x06, 0x0b, 0x61, 0x03, 0x3f, 0x61, 0x03, 0x3f, 0x03e6, 0x03e2, 0x03de, - 0x041b, 0x041f, 0x0424} -}; - -static const struct chan_info_nphy_radio2057_rev5 -chan_info_nphyrev9_2057_rev5v1[] = { - { - 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0d, - 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03c9, 0x03c5, 0x03c1, - 0x043a, 0x043f, 0x0443}, - { - 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0d, - 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03cb, 0x03c7, 0x03c3, - 0x0438, 0x043d, 0x0441}, - { - 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0d, - 0x08, 0x0e, 0x61, 0x03, 0xef, 0x61, 0x03, 0xef, 0x03cd, 0x03c9, 0x03c5, - 0x0436, 0x043a, 0x043f}, - { - 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0c, - 0x08, 0x0e, 0x61, 0x03, 0xdf, 0x61, 0x03, 0xdf, 0x03cf, 0x03cb, 0x03c7, - 0x0434, 0x0438, 0x043d}, - { - 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0c, - 0x07, 0x0d, 0x61, 0x03, 0xcf, 0x61, 0x03, 0xcf, 0x03d1, 0x03cd, 0x03c9, - 0x0431, 0x0436, 0x043a}, - { - 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0c, - 0x07, 0x0d, 0x61, 0x03, 0xbf, 0x61, 0x03, 0xbf, 0x03d3, 0x03cf, 0x03cb, - 0x042f, 0x0434, 0x0438}, - { - 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0b, - 0x07, 0x0d, 0x61, 0x03, 0xaf, 0x61, 0x03, 0xaf, 0x03d5, 0x03d1, 0x03cd, - 0x042d, 0x0431, 0x0436}, - { - 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0b, - 0x07, 0x0d, 0x61, 0x03, 0x9f, 0x61, 0x03, 0x9f, 0x03d7, 0x03d3, 0x03cf, - 0x042b, 0x042f, 0x0434}, - { - 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0b, - 0x07, 0x0d, 0x61, 0x03, 0x8f, 0x61, 0x03, 0x8f, 0x03d9, 0x03d5, 0x03d1, - 0x0429, 0x042d, 0x0431}, - { - 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0b, - 0x07, 0x0c, 0x61, 0x03, 0x7f, 0x61, 0x03, 0x7f, 0x03db, 0x03d7, 0x03d3, - 0x0427, 0x042b, 0x042f}, - { - 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0b, - 0x07, 0x0c, 0x61, 0x03, 0x6f, 0x61, 0x03, 0x6f, 0x03dd, 0x03d9, 0x03d5, - 0x0424, 0x0429, 0x042d}, - { - 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0b, - 0x06, 0x0c, 0x61, 0x03, 0x5f, 0x61, 0x03, 0x5f, 0x03df, 0x03db, 0x03d7, - 0x0422, 0x0427, 0x042b}, - { - 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0a, - 0x06, 0x0b, 0x61, 0x03, 0x4f, 0x61, 0x03, 0x4f, 0x03e1, 0x03dd, 0x03d9, - 0x0420, 0x0424, 0x0429}, - { - 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0a, - 0x06, 0x0b, 0x61, 0x03, 0x3f, 0x61, 0x03, 0x3f, 0x03e6, 0x03e2, 0x03de, - 0x041b, 0x041f, 0x0424} -}; - -static const struct chan_info_nphy_radio2057 chan_info_nphyrev8_2057_rev7[] = { - { - 184, 4920, 0x68, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xec, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07b4, 0x07b0, 0x07ac, 0x0214, - 0x0215, - 0x0216}, - { - 186, 4930, 0x6b, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xed, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07b8, 0x07b4, 0x07b0, 0x0213, - 0x0214, - 0x0215}, - { - 188, 4940, 0x6e, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xee, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07bc, 0x07b8, 0x07b4, 0x0212, - 0x0213, - 0x0214}, - { - 190, 4950, 0x72, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xef, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c0, 0x07bc, 0x07b8, 0x0211, - 0x0212, - 0x0213}, - { - 192, 4960, 0x75, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf0, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c4, 0x07c0, 0x07bc, 0x020f, - 0x0211, - 0x0212}, - { - 194, 4970, 0x78, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf1, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c8, 0x07c4, 0x07c0, 0x020e, - 0x020f, - 0x0211}, - { - 196, 4980, 0x7c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf2, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07cc, 0x07c8, 0x07c4, 0x020d, - 0x020e, - 0x020f}, - { - 198, 4990, 0x7f, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf3, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07d0, 0x07cc, 0x07c8, 0x020c, - 0x020d, - 0x020e}, - { - 200, 5000, 0x82, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf4, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d4, 0x07d0, 0x07cc, 0x020b, - 0x020c, - 0x020d}, - { - 202, 5010, 0x86, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf5, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d8, 0x07d4, 0x07d0, 0x020a, - 0x020b, - 0x020c}, - { - 204, 5020, 0x89, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf6, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07dc, 0x07d8, 0x07d4, 0x0209, - 0x020a, - 0x020b}, - { - 206, 5030, 0x8c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf7, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e0, 0x07dc, 0x07d8, 0x0208, - 0x0209, - 0x020a}, - { - 208, 5040, 0x90, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf8, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e4, 0x07e0, 0x07dc, 0x0207, - 0x0208, - 0x0209}, - { - 210, 5050, 0x93, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf9, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e8, 0x07e4, 0x07e0, 0x0206, - 0x0207, - 0x0208}, - { - 212, 5060, 0x96, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfa, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07ec, 0x07e8, 0x07e4, 0x0205, - 0x0206, - 0x0207}, - { - 214, 5070, 0x9a, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfb, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f0, 0x07ec, 0x07e8, 0x0204, - 0x0205, - 0x0206}, - { - 216, 5080, 0x9d, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfc, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f4, 0x07f0, 0x07ec, 0x0203, - 0x0204, - 0x0205}, - { - 218, 5090, 0xa0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfd, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f8, 0x07f4, 0x07f0, 0x0202, - 0x0203, - 0x0204}, - { - 220, 5100, 0xa4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfe, 0x01, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x07fc, 0x07f8, 0x07f4, 0x0201, - 0x0202, - 0x0203}, - { - 222, 5110, 0xa7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xff, 0x01, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0800, 0x07fc, 0x07f8, 0x0200, - 0x0201, - 0x0202}, - { - 224, 5120, 0xaa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x00, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0804, 0x0800, 0x07fc, 0x01ff, - 0x0200, - 0x0201}, - { - 226, 5130, 0xae, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x01, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0808, 0x0804, 0x0800, 0x01fe, - 0x01ff, - 0x0200}, - { - 228, 5140, 0xb1, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x02, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x080c, 0x0808, 0x0804, 0x01fd, - 0x01fe, - 0x01ff}, - { - 32, 5160, 0xb8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x04, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0814, 0x0810, 0x080c, 0x01fb, - 0x01fc, - 0x01fd}, - { - 34, 5170, 0xbb, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x05, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0818, 0x0814, 0x0810, 0x01fa, - 0x01fb, - 0x01fc}, - { - 36, 5180, 0xbe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x06, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x081c, 0x0818, 0x0814, 0x01f9, - 0x01fa, - 0x01fb}, - { - 38, 5190, 0xc2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x07, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0820, 0x081c, 0x0818, 0x01f8, - 0x01f9, - 0x01fa}, - { - 40, 5200, 0xc5, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x08, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0824, 0x0820, 0x081c, 0x01f7, - 0x01f8, - 0x01f9}, - { - 42, 5210, 0xc8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x09, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0828, 0x0824, 0x0820, 0x01f6, - 0x01f7, - 0x01f8}, - { - 44, 5220, 0xcc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0a, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x082c, 0x0828, 0x0824, 0x01f5, - 0x01f6, - 0x01f7}, - { - 46, 5230, 0xcf, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0b, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0830, 0x082c, 0x0828, 0x01f4, - 0x01f5, - 0x01f6}, - { - 48, 5240, 0xd2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0c, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0834, 0x0830, 0x082c, 0x01f3, - 0x01f4, - 0x01f5}, - { - 50, 5250, 0xd6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0d, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0838, 0x0834, 0x0830, 0x01f2, - 0x01f3, - 0x01f4}, - { - 52, 5260, 0xd9, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0e, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x083c, 0x0838, 0x0834, 0x01f1, - 0x01f2, - 0x01f3}, - { - 54, 5270, 0xdc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0f, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0840, 0x083c, 0x0838, 0x01f0, - 0x01f1, - 0x01f2}, - { - 56, 5280, 0xe0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x10, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0844, 0x0840, 0x083c, 0x01f0, - 0x01f0, - 0x01f1}, - { - 58, 5290, 0xe3, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x11, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0848, 0x0844, 0x0840, 0x01ef, - 0x01f0, - 0x01f0}, - { - 60, 5300, 0xe6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x12, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x084c, 0x0848, 0x0844, 0x01ee, - 0x01ef, - 0x01f0}, - { - 62, 5310, 0xea, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x13, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0850, 0x084c, 0x0848, 0x01ed, - 0x01ee, - 0x01ef}, - { - 64, 5320, 0xed, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x14, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0854, 0x0850, 0x084c, 0x01ec, - 0x01ed, - 0x01ee}, - { - 66, 5330, 0xf0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x15, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0858, 0x0854, 0x0850, 0x01eb, - 0x01ec, - 0x01ed}, - { - 68, 5340, 0xf4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x16, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x085c, 0x0858, 0x0854, 0x01ea, - 0x01eb, - 0x01ec}, - { - 70, 5350, 0xf7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x17, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0860, 0x085c, 0x0858, 0x01e9, - 0x01ea, - 0x01eb}, - { - 72, 5360, 0xfa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x18, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0864, 0x0860, 0x085c, 0x01e8, - 0x01e9, - 0x01ea}, - { - 74, 5370, 0xfe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x19, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0868, 0x0864, 0x0860, 0x01e7, - 0x01e8, - 0x01e9}, - { - 76, 5380, 0x01, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1a, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x086c, 0x0868, 0x0864, 0x01e6, - 0x01e7, - 0x01e8}, - { - 78, 5390, 0x04, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1b, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0870, 0x086c, 0x0868, 0x01e5, - 0x01e6, - 0x01e7}, - { - 80, 5400, 0x08, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1c, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0874, 0x0870, 0x086c, 0x01e5, - 0x01e5, - 0x01e6}, - { - 82, 5410, 0x0b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1d, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0878, 0x0874, 0x0870, 0x01e4, - 0x01e5, - 0x01e5}, - { - 84, 5420, 0x0e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1e, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x087c, 0x0878, 0x0874, 0x01e3, - 0x01e4, - 0x01e5}, - { - 86, 5430, 0x12, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1f, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0880, 0x087c, 0x0878, 0x01e2, - 0x01e3, - 0x01e4}, - { - 88, 5440, 0x15, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x20, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0884, 0x0880, 0x087c, 0x01e1, - 0x01e2, - 0x01e3}, - { - 90, 5450, 0x18, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x21, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0888, 0x0884, 0x0880, 0x01e0, - 0x01e1, - 0x01e2}, - { - 92, 5460, 0x1c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x22, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x088c, 0x0888, 0x0884, 0x01df, - 0x01e0, - 0x01e1}, - { - 94, 5470, 0x1f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x23, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0890, 0x088c, 0x0888, 0x01de, - 0x01df, - 0x01e0}, - { - 96, 5480, 0x22, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x24, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0894, 0x0890, 0x088c, 0x01dd, - 0x01de, - 0x01df}, - { - 98, 5490, 0x26, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x25, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0898, 0x0894, 0x0890, 0x01dd, - 0x01dd, - 0x01de}, - { - 100, 5500, 0x29, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x26, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x089c, 0x0898, 0x0894, 0x01dc, - 0x01dd, - 0x01dd}, - { - 102, 5510, 0x2c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x27, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a0, 0x089c, 0x0898, 0x01db, - 0x01dc, - 0x01dd}, - { - 104, 5520, 0x30, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x28, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a4, 0x08a0, 0x089c, 0x01da, - 0x01db, - 0x01dc}, - { - 106, 5530, 0x33, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x29, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a8, 0x08a4, 0x08a0, 0x01d9, - 0x01da, - 0x01db}, - { - 108, 5540, 0x36, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2a, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08ac, 0x08a8, 0x08a4, 0x01d8, - 0x01d9, - 0x01da}, - { - 110, 5550, 0x3a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2b, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b0, 0x08ac, 0x08a8, 0x01d7, - 0x01d8, - 0x01d9}, - { - 112, 5560, 0x3d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2c, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b4, 0x08b0, 0x08ac, 0x01d7, - 0x01d7, - 0x01d8}, - { - 114, 5570, 0x40, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2d, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b8, 0x08b4, 0x08b0, 0x01d6, - 0x01d7, - 0x01d7}, - { - 116, 5580, 0x44, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2e, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08bc, 0x08b8, 0x08b4, 0x01d5, - 0x01d6, - 0x01d7}, - { - 118, 5590, 0x47, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2f, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08c0, 0x08bc, 0x08b8, 0x01d4, - 0x01d5, - 0x01d6}, - { - 120, 5600, 0x4a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x30, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c4, 0x08c0, 0x08bc, 0x01d3, - 0x01d4, - 0x01d5}, - { - 122, 5610, 0x4e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x31, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c8, 0x08c4, 0x08c0, 0x01d2, - 0x01d3, - 0x01d4}, - { - 124, 5620, 0x51, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x32, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08cc, 0x08c8, 0x08c4, 0x01d2, - 0x01d2, - 0x01d3}, - { - 126, 5630, 0x54, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x33, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d0, 0x08cc, 0x08c8, 0x01d1, - 0x01d2, - 0x01d2}, - { - 128, 5640, 0x58, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x34, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d4, 0x08d0, 0x08cc, 0x01d0, - 0x01d1, - 0x01d2}, - { - 130, 5650, 0x5b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x35, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08d8, 0x08d4, 0x08d0, 0x01cf, - 0x01d0, - 0x01d1}, - { - 132, 5660, 0x5e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x36, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08dc, 0x08d8, 0x08d4, 0x01ce, - 0x01cf, - 0x01d0}, - { - 134, 5670, 0x62, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x37, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08e0, 0x08dc, 0x08d8, 0x01ce, - 0x01ce, - 0x01cf}, - { - 136, 5680, 0x65, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x38, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e4, 0x08e0, 0x08dc, 0x01cd, - 0x01ce, - 0x01ce}, - { - 138, 5690, 0x68, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x39, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e8, 0x08e4, 0x08e0, 0x01cc, - 0x01cd, - 0x01ce}, - { - 140, 5700, 0x6c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3a, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08ec, 0x08e8, 0x08e4, 0x01cb, - 0x01cc, - 0x01cd}, - { - 142, 5710, 0x6f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3b, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f0, 0x08ec, 0x08e8, 0x01ca, - 0x01cb, - 0x01cc}, - { - 144, 5720, 0x72, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3c, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f4, 0x08f0, 0x08ec, 0x01c9, - 0x01ca, - 0x01cb}, - { - 145, 5725, 0x74, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x79, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f6, 0x08f2, 0x08ee, 0x01c9, - 0x01ca, - 0x01cb}, - { - 146, 5730, 0x76, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3d, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f8, 0x08f4, 0x08f0, 0x01c9, - 0x01c9, - 0x01ca}, - { - 147, 5735, 0x77, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7b, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fa, 0x08f6, 0x08f2, 0x01c8, - 0x01c9, - 0x01ca}, - { - 148, 5740, 0x79, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3e, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fc, 0x08f8, 0x08f4, 0x01c8, - 0x01c9, - 0x01c9}, - { - 149, 5745, 0x7b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7d, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fe, 0x08fa, 0x08f6, 0x01c8, - 0x01c8, - 0x01c9}, - { - 150, 5750, 0x7c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3f, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, - 0x01c8, - 0x01c9}, - { - 151, 5755, 0x7e, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7f, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, - 0x01c8, - 0x01c8}, - { - 152, 5760, 0x80, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x40, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, - 0x01c7, - 0x01c8}, - { - 153, 5765, 0x81, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x81, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, - 0x01c7, - 0x01c8}, - { - 154, 5770, 0x83, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x41, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, - 0x01c6, - 0x01c7}, - { - 155, 5775, 0x85, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x83, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, - 0x01c6, - 0x01c7}, - { - 156, 5780, 0x86, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x42, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, - 0x01c6, - 0x01c6}, - { - 157, 5785, 0x88, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x85, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, - 0x01c5, - 0x01c6}, - { - 158, 5790, 0x8a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x43, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, - 0x01c5, - 0x01c6}, - { - 159, 5795, 0x8b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x87, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, - 0x01c4, - 0x01c5}, - { - 160, 5800, 0x8d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x44, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, - 0x01c4, - 0x01c5}, - { - 161, 5805, 0x8f, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x89, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, - 0x01c4, - 0x01c4}, - { - 162, 5810, 0x90, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x45, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, - 0x01c3, - 0x01c4}, - { - 163, 5815, 0x92, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8b, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, - 0x01c3, - 0x01c4}, - { - 164, 5820, 0x94, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x46, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, - 0x01c2, - 0x01c3}, - { - 165, 5825, 0x95, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8d, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, - 0x01c2, - 0x01c3}, - { - 166, 5830, 0x97, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x47, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, - 0x01c2, - 0x01c2}, - { - 168, 5840, 0x9a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x48, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, - 0x01c1, - 0x01c2}, - { - 170, 5850, 0x9e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x49, 0x02, 0x04, - 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, - 0x01c0, - 0x01c1}, - { - 172, 5860, 0xa1, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4a, 0x02, 0x04, - 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, - 0x01bf, - 0x01c0}, - { - 174, 5870, 0xa4, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4b, 0x02, 0x04, - 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, - 0x01bf, - 0x01bf}, - { - 176, 5880, 0xa8, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4c, 0x02, 0x03, - 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, - 0x01be, - 0x01bf}, - { - 178, 5890, 0xab, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4d, 0x02, 0x03, - 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, - 0x01bd, - 0x01be}, - { - 180, 5900, 0xae, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4e, 0x02, 0x03, - 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, - 0x01bc, - 0x01bd}, - { - 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0f, - 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03c9, 0x03c5, 0x03c1, 0x043a, - 0x043f, - 0x0443}, - { - 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0f, - 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cb, 0x03c7, 0x03c3, 0x0438, - 0x043d, - 0x0441}, - { - 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0f, - 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cd, 0x03c9, 0x03c5, 0x0436, - 0x043a, - 0x043f}, - { - 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0f, - 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cf, 0x03cb, 0x03c7, 0x0434, - 0x0438, - 0x043d}, - { - 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0f, - 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d1, 0x03cd, 0x03c9, 0x0431, - 0x0436, - 0x043a}, - { - 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0f, - 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d3, 0x03cf, 0x03cb, 0x042f, - 0x0434, - 0x0438}, - { - 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0f, - 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d5, 0x03d1, 0x03cd, 0x042d, - 0x0431, - 0x0436}, - { - 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0f, - 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d7, 0x03d3, 0x03cf, 0x042b, - 0x042f, - 0x0434}, - { - 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0f, - 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d9, 0x03d5, 0x03d1, 0x0429, - 0x042d, - 0x0431}, - { - 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0f, - 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03db, 0x03d7, 0x03d3, 0x0427, - 0x042b, - 0x042f}, - { - 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0f, - 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03dd, 0x03d9, 0x03d5, 0x0424, - 0x0429, - 0x042d}, - { - 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0f, - 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03df, 0x03db, 0x03d7, 0x0422, - 0x0427, - 0x042b}, - { - 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0f, - 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03e1, 0x03dd, 0x03d9, 0x0420, - 0x0424, - 0x0429}, - { - 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0f, - 0x04, 0x00, 0x04, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03e6, 0x03e2, 0x03de, 0x041b, - 0x041f, - 0x0424} -}; - -static const struct chan_info_nphy_radio2057 chan_info_nphyrev8_2057_rev8[] = { - { - 186, 4930, 0x6b, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xed, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07b8, 0x07b4, 0x07b0, 0x0213, - 0x0214, - 0x0215}, - { - 188, 4940, 0x6e, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xee, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07bc, 0x07b8, 0x07b4, 0x0212, - 0x0213, - 0x0214}, - { - 190, 4950, 0x72, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xef, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c0, 0x07bc, 0x07b8, 0x0211, - 0x0212, - 0x0213}, - { - 192, 4960, 0x75, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf0, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c4, 0x07c0, 0x07bc, 0x020f, - 0x0211, - 0x0212}, - { - 194, 4970, 0x78, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf1, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c8, 0x07c4, 0x07c0, 0x020e, - 0x020f, - 0x0211}, - { - 196, 4980, 0x7c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf2, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07cc, 0x07c8, 0x07c4, 0x020d, - 0x020e, - 0x020f}, - { - 198, 4990, 0x7f, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf3, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07d0, 0x07cc, 0x07c8, 0x020c, - 0x020d, - 0x020e}, - { - 200, 5000, 0x82, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf4, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d4, 0x07d0, 0x07cc, 0x020b, - 0x020c, - 0x020d}, - { - 202, 5010, 0x86, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf5, 0x01, 0x0f, - 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d8, 0x07d4, 0x07d0, 0x020a, - 0x020b, - 0x020c}, - { - 204, 5020, 0x89, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf6, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07dc, 0x07d8, 0x07d4, 0x0209, - 0x020a, - 0x020b}, - { - 206, 5030, 0x8c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf7, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e0, 0x07dc, 0x07d8, 0x0208, - 0x0209, - 0x020a}, - { - 208, 5040, 0x90, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf8, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e4, 0x07e0, 0x07dc, 0x0207, - 0x0208, - 0x0209}, - { - 210, 5050, 0x93, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf9, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e8, 0x07e4, 0x07e0, 0x0206, - 0x0207, - 0x0208}, - { - 212, 5060, 0x96, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfa, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07ec, 0x07e8, 0x07e4, 0x0205, - 0x0206, - 0x0207}, - { - 214, 5070, 0x9a, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfb, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f0, 0x07ec, 0x07e8, 0x0204, - 0x0205, - 0x0206}, - { - 216, 5080, 0x9d, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfc, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f4, 0x07f0, 0x07ec, 0x0203, - 0x0204, - 0x0205}, - { - 218, 5090, 0xa0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfd, 0x01, 0x0e, - 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, - 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f8, 0x07f4, 0x07f0, 0x0202, - 0x0203, - 0x0204}, - { - 220, 5100, 0xa4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfe, 0x01, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x07fc, 0x07f8, 0x07f4, 0x0201, - 0x0202, - 0x0203}, - { - 222, 5110, 0xa7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xff, 0x01, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0800, 0x07fc, 0x07f8, 0x0200, - 0x0201, - 0x0202}, - { - 224, 5120, 0xaa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x00, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0804, 0x0800, 0x07fc, 0x01ff, - 0x0200, - 0x0201}, - { - 226, 5130, 0xae, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x01, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0808, 0x0804, 0x0800, 0x01fe, - 0x01ff, - 0x0200}, - { - 228, 5140, 0xb1, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x02, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x080c, 0x0808, 0x0804, 0x01fd, - 0x01fe, - 0x01ff}, - { - 32, 5160, 0xb8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x04, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0814, 0x0810, 0x080c, 0x01fb, - 0x01fc, - 0x01fd}, - { - 34, 5170, 0xbb, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x05, 0x02, 0x0d, - 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0818, 0x0814, 0x0810, 0x01fa, - 0x01fb, - 0x01fc}, - { - 36, 5180, 0xbe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x06, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x081c, 0x0818, 0x0814, 0x01f9, - 0x01fa, - 0x01fb}, - { - 38, 5190, 0xc2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x07, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, - 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0820, 0x081c, 0x0818, 0x01f8, - 0x01f9, - 0x01fa}, - { - 40, 5200, 0xc5, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x08, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0824, 0x0820, 0x081c, 0x01f7, - 0x01f8, - 0x01f9}, - { - 42, 5210, 0xc8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x09, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0828, 0x0824, 0x0820, 0x01f6, - 0x01f7, - 0x01f8}, - { - 44, 5220, 0xcc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0a, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x082c, 0x0828, 0x0824, 0x01f5, - 0x01f6, - 0x01f7}, - { - 46, 5230, 0xcf, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0b, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0830, 0x082c, 0x0828, 0x01f4, - 0x01f5, - 0x01f6}, - { - 48, 5240, 0xd2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0c, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0834, 0x0830, 0x082c, 0x01f3, - 0x01f4, - 0x01f5}, - { - 50, 5250, 0xd6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0d, 0x02, 0x0c, - 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0838, 0x0834, 0x0830, 0x01f2, - 0x01f3, - 0x01f4}, - { - 52, 5260, 0xd9, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0e, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x083c, 0x0838, 0x0834, 0x01f1, - 0x01f2, - 0x01f3}, - { - 54, 5270, 0xdc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0f, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0840, 0x083c, 0x0838, 0x01f0, - 0x01f1, - 0x01f2}, - { - 56, 5280, 0xe0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x10, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0844, 0x0840, 0x083c, 0x01f0, - 0x01f0, - 0x01f1}, - { - 58, 5290, 0xe3, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x11, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, - 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0848, 0x0844, 0x0840, 0x01ef, - 0x01f0, - 0x01f0}, - { - 60, 5300, 0xe6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x12, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x084c, 0x0848, 0x0844, 0x01ee, - 0x01ef, - 0x01f0}, - { - 62, 5310, 0xea, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x13, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0850, 0x084c, 0x0848, 0x01ed, - 0x01ee, - 0x01ef}, - { - 64, 5320, 0xed, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x14, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0854, 0x0850, 0x084c, 0x01ec, - 0x01ed, - 0x01ee}, - { - 66, 5330, 0xf0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x15, 0x02, 0x0b, - 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0858, 0x0854, 0x0850, 0x01eb, - 0x01ec, - 0x01ed}, - { - 68, 5340, 0xf4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x16, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x085c, 0x0858, 0x0854, 0x01ea, - 0x01eb, - 0x01ec}, - { - 70, 5350, 0xf7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x17, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0860, 0x085c, 0x0858, 0x01e9, - 0x01ea, - 0x01eb}, - { - 72, 5360, 0xfa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x18, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0864, 0x0860, 0x085c, 0x01e8, - 0x01e9, - 0x01ea}, - { - 74, 5370, 0xfe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x19, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0868, 0x0864, 0x0860, 0x01e7, - 0x01e8, - 0x01e9}, - { - 76, 5380, 0x01, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1a, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x086c, 0x0868, 0x0864, 0x01e6, - 0x01e7, - 0x01e8}, - { - 78, 5390, 0x04, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1b, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, - 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0870, 0x086c, 0x0868, 0x01e5, - 0x01e6, - 0x01e7}, - { - 80, 5400, 0x08, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1c, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0874, 0x0870, 0x086c, 0x01e5, - 0x01e5, - 0x01e6}, - { - 82, 5410, 0x0b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1d, 0x02, 0x0a, - 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0878, 0x0874, 0x0870, 0x01e4, - 0x01e5, - 0x01e5}, - { - 84, 5420, 0x0e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1e, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x087c, 0x0878, 0x0874, 0x01e3, - 0x01e4, - 0x01e5}, - { - 86, 5430, 0x12, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1f, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0880, 0x087c, 0x0878, 0x01e2, - 0x01e3, - 0x01e4}, - { - 88, 5440, 0x15, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x20, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0884, 0x0880, 0x087c, 0x01e1, - 0x01e2, - 0x01e3}, - { - 90, 5450, 0x18, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x21, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0888, 0x0884, 0x0880, 0x01e0, - 0x01e1, - 0x01e2}, - { - 92, 5460, 0x1c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x22, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x088c, 0x0888, 0x0884, 0x01df, - 0x01e0, - 0x01e1}, - { - 94, 5470, 0x1f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x23, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0890, 0x088c, 0x0888, 0x01de, - 0x01df, - 0x01e0}, - { - 96, 5480, 0x22, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x24, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0894, 0x0890, 0x088c, 0x01dd, - 0x01de, - 0x01df}, - { - 98, 5490, 0x26, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x25, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, - 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0898, 0x0894, 0x0890, 0x01dd, - 0x01dd, - 0x01de}, - { - 100, 5500, 0x29, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x26, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x089c, 0x0898, 0x0894, 0x01dc, - 0x01dd, - 0x01dd}, - { - 102, 5510, 0x2c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x27, 0x02, 0x09, - 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a0, 0x089c, 0x0898, 0x01db, - 0x01dc, - 0x01dd}, - { - 104, 5520, 0x30, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x28, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a4, 0x08a0, 0x089c, 0x01da, - 0x01db, - 0x01dc}, - { - 106, 5530, 0x33, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x29, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a8, 0x08a4, 0x08a0, 0x01d9, - 0x01da, - 0x01db}, - { - 108, 5540, 0x36, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2a, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08ac, 0x08a8, 0x08a4, 0x01d8, - 0x01d9, - 0x01da}, - { - 110, 5550, 0x3a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2b, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b0, 0x08ac, 0x08a8, 0x01d7, - 0x01d8, - 0x01d9}, - { - 112, 5560, 0x3d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2c, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b4, 0x08b0, 0x08ac, 0x01d7, - 0x01d7, - 0x01d8}, - { - 114, 5570, 0x40, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2d, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b8, 0x08b4, 0x08b0, 0x01d6, - 0x01d7, - 0x01d7}, - { - 116, 5580, 0x44, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2e, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08bc, 0x08b8, 0x08b4, 0x01d5, - 0x01d6, - 0x01d7}, - { - 118, 5590, 0x47, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2f, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, - 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08c0, 0x08bc, 0x08b8, 0x01d4, - 0x01d5, - 0x01d6}, - { - 120, 5600, 0x4a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x30, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c4, 0x08c0, 0x08bc, 0x01d3, - 0x01d4, - 0x01d5}, - { - 122, 5610, 0x4e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x31, 0x02, 0x08, - 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c8, 0x08c4, 0x08c0, 0x01d2, - 0x01d3, - 0x01d4}, - { - 124, 5620, 0x51, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x32, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08cc, 0x08c8, 0x08c4, 0x01d2, - 0x01d2, - 0x01d3}, - { - 126, 5630, 0x54, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x33, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d0, 0x08cc, 0x08c8, 0x01d1, - 0x01d2, - 0x01d2}, - { - 128, 5640, 0x58, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x34, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d4, 0x08d0, 0x08cc, 0x01d0, - 0x01d1, - 0x01d2}, - { - 130, 5650, 0x5b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x35, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08d8, 0x08d4, 0x08d0, 0x01cf, - 0x01d0, - 0x01d1}, - { - 132, 5660, 0x5e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x36, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08dc, 0x08d8, 0x08d4, 0x01ce, - 0x01cf, - 0x01d0}, - { - 134, 5670, 0x62, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x37, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08e0, 0x08dc, 0x08d8, 0x01ce, - 0x01ce, - 0x01cf}, - { - 136, 5680, 0x65, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x38, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e4, 0x08e0, 0x08dc, 0x01cd, - 0x01ce, - 0x01ce}, - { - 138, 5690, 0x68, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x39, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, - 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e8, 0x08e4, 0x08e0, 0x01cc, - 0x01cd, - 0x01ce}, - { - 140, 5700, 0x6c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3a, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08ec, 0x08e8, 0x08e4, 0x01cb, - 0x01cc, - 0x01cd}, - { - 142, 5710, 0x6f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3b, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f0, 0x08ec, 0x08e8, 0x01ca, - 0x01cb, - 0x01cc}, - { - 144, 5720, 0x72, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3c, 0x02, 0x07, - 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f4, 0x08f0, 0x08ec, 0x01c9, - 0x01ca, - 0x01cb}, - { - 145, 5725, 0x74, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x79, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f6, 0x08f2, 0x08ee, 0x01c9, - 0x01ca, - 0x01cb}, - { - 146, 5730, 0x76, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3d, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f8, 0x08f4, 0x08f0, 0x01c9, - 0x01c9, - 0x01ca}, - { - 147, 5735, 0x77, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7b, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fa, 0x08f6, 0x08f2, 0x01c8, - 0x01c9, - 0x01ca}, - { - 148, 5740, 0x79, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3e, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fc, 0x08f8, 0x08f4, 0x01c8, - 0x01c9, - 0x01c9}, - { - 149, 5745, 0x7b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7d, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fe, 0x08fa, 0x08f6, 0x01c8, - 0x01c8, - 0x01c9}, - { - 150, 5750, 0x7c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3f, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, - 0x01c8, - 0x01c9}, - { - 151, 5755, 0x7e, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7f, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, - 0x01c8, - 0x01c8}, - { - 152, 5760, 0x80, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x40, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, - 0x01c7, - 0x01c8}, - { - 153, 5765, 0x81, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x81, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, - 0x01c7, - 0x01c8}, - { - 154, 5770, 0x83, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x41, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, - 0x01c6, - 0x01c7}, - { - 155, 5775, 0x85, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x83, 0x04, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, - 0x01c6, - 0x01c7}, - { - 156, 5780, 0x86, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x42, 0x02, 0x06, - 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, - 0x01c6, - 0x01c6}, - { - 157, 5785, 0x88, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x85, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, - 0x01c5, - 0x01c6}, - { - 158, 5790, 0x8a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x43, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, - 0x01c5, - 0x01c6}, - { - 159, 5795, 0x8b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x87, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, - 0x01c4, - 0x01c5}, - { - 160, 5800, 0x8d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x44, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, - 0x01c4, - 0x01c5}, - { - 161, 5805, 0x8f, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x89, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, - 0x01c4, - 0x01c4}, - { - 162, 5810, 0x90, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x45, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, - 0x01c3, - 0x01c4}, - { - 163, 5815, 0x92, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8b, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, - 0x01c3, - 0x01c4}, - { - 164, 5820, 0x94, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x46, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, - 0x01c2, - 0x01c3}, - { - 165, 5825, 0x95, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8d, 0x04, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, - 0x01c2, - 0x01c3}, - { - 166, 5830, 0x97, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x47, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, - 0x01c2, - 0x01c2}, - { - 168, 5840, 0x9a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x48, 0x02, 0x05, - 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, - 0x01c1, - 0x01c2}, - { - 170, 5850, 0x9e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x49, 0x02, 0x04, - 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, - 0x01c0, - 0x01c1}, - { - 172, 5860, 0xa1, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4a, 0x02, 0x04, - 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, - 0x01bf, - 0x01c0}, - { - 174, 5870, 0xa4, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4b, 0x02, 0x04, - 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, - 0x01bf, - 0x01bf}, - { - 176, 5880, 0xa8, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4c, 0x02, 0x03, - 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, - 0x01be, - 0x01bf}, - { - 178, 5890, 0xab, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4d, 0x02, 0x03, - 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, - 0x01bd, - 0x01be}, - { - 180, 5900, 0xae, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4e, 0x02, 0x03, - 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, - 0x01bc, - 0x01bd}, - { - 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0f, - 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03c9, 0x03c5, 0x03c1, 0x043a, - 0x043f, - 0x0443}, - { - 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0f, - 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cb, 0x03c7, 0x03c3, 0x0438, - 0x043d, - 0x0441}, - { - 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0f, - 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cd, 0x03c9, 0x03c5, 0x0436, - 0x043a, - 0x043f}, - { - 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0f, - 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cf, 0x03cb, 0x03c7, 0x0434, - 0x0438, - 0x043d}, - { - 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0f, - 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d1, 0x03cd, 0x03c9, 0x0431, - 0x0436, - 0x043a}, - { - 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0f, - 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d3, 0x03cf, 0x03cb, 0x042f, - 0x0434, - 0x0438}, - { - 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0f, - 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d5, 0x03d1, 0x03cd, 0x042d, - 0x0431, - 0x0436}, - { - 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0f, - 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d7, 0x03d3, 0x03cf, 0x042b, - 0x042f, - 0x0434}, - { - 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0f, - 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d9, 0x03d5, 0x03d1, 0x0429, - 0x042d, - 0x0431}, - { - 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0f, - 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03db, 0x03d7, 0x03d3, 0x0427, - 0x042b, - 0x042f}, - { - 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0f, - 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03dd, 0x03d9, 0x03d5, 0x0424, - 0x0429, - 0x042d}, - { - 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0f, - 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03df, 0x03db, 0x03d7, 0x0422, - 0x0427, - 0x042b}, - { - 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0f, - 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03e1, 0x03dd, 0x03d9, 0x0420, - 0x0424, - 0x0429}, - { - 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0f, - 0x04, 0x00, 0x04, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x61, - 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03e6, 0x03e2, 0x03de, 0x041b, - 0x041f, - 0x0424} -}; - -static struct radio_regs regs_2055[] = { - {0x02, 0x80, 0x80, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0x27, 0x27, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0x27, 0x27, 0, 0}, - {0x07, 0x7f, 0x7f, 1, 1}, - {0x08, 0x7, 0x7, 1, 1}, - {0x09, 0x7f, 0x7f, 1, 1}, - {0x0A, 0x7, 0x7, 1, 1}, - {0x0B, 0x15, 0x15, 0, 0}, - {0x0C, 0x15, 0x15, 0, 0}, - {0x0D, 0x4f, 0x4f, 1, 1}, - {0x0E, 0x5, 0x5, 1, 1}, - {0x0F, 0x4f, 0x4f, 1, 1}, - {0x10, 0x5, 0x5, 1, 1}, - {0x11, 0xd0, 0xd0, 0, 0}, - {0x12, 0x2, 0x2, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0x40, 0x40, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0xc0, 0xc0, 0, 0}, - {0x1E, 0xff, 0xff, 0, 0}, - {0x1F, 0xc0, 0xc0, 0, 0}, - {0x20, 0xff, 0xff, 0, 0}, - {0x21, 0xc0, 0xc0, 0, 0}, - {0x22, 0, 0, 0, 0}, - {0x23, 0x2c, 0x2c, 0, 0}, - {0x24, 0, 0, 0, 0}, - {0x25, 0, 0, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0, 0, 0, 0}, - {0x28, 0, 0, 0, 0}, - {0x29, 0, 0, 0, 0}, - {0x2A, 0, 0, 0, 0}, - {0x2B, 0, 0, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0xa4, 0xa4, 0, 0}, - {0x2E, 0x38, 0x38, 0, 0}, - {0x2F, 0, 0, 0, 0}, - {0x30, 0x4, 0x4, 1, 1}, - {0x31, 0, 0, 0, 0}, - {0x32, 0xa, 0xa, 0, 0}, - {0x33, 0x87, 0x87, 0, 0}, - {0x34, 0x9, 0x9, 0, 0}, - {0x35, 0x70, 0x70, 0, 0}, - {0x36, 0x11, 0x11, 0, 0}, - {0x37, 0x18, 0x18, 1, 1}, - {0x38, 0x6, 0x6, 0, 0}, - {0x39, 0x4, 0x4, 1, 1}, - {0x3A, 0x6, 0x6, 0, 0}, - {0x3B, 0x9e, 0x9e, 0, 0}, - {0x3C, 0x9, 0x9, 0, 0}, - {0x3D, 0xc8, 0xc8, 1, 1}, - {0x3E, 0x88, 0x88, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0, 0, 0, 0}, - {0x42, 0x1, 0x1, 0, 0}, - {0x43, 0x2, 0x2, 0, 0}, - {0x44, 0x96, 0x96, 0, 0}, - {0x45, 0x3e, 0x3e, 0, 0}, - {0x46, 0x3e, 0x3e, 0, 0}, - {0x47, 0x13, 0x13, 0, 0}, - {0x48, 0x2, 0x2, 0, 0}, - {0x49, 0x15, 0x15, 0, 0}, - {0x4A, 0x7, 0x7, 0, 0}, - {0x4B, 0, 0, 0, 0}, - {0x4C, 0, 0, 0, 0}, - {0x4D, 0, 0, 0, 0}, - {0x4E, 0, 0, 0, 0}, - {0x4F, 0, 0, 0, 0}, - {0x50, 0x8, 0x8, 0, 0}, - {0x51, 0x8, 0x8, 0, 0}, - {0x52, 0x6, 0x6, 0, 0}, - {0x53, 0x84, 0x84, 1, 1}, - {0x54, 0xc3, 0xc3, 0, 0}, - {0x55, 0x8f, 0x8f, 0, 0}, - {0x56, 0xff, 0xff, 0, 0}, - {0x57, 0xff, 0xff, 0, 0}, - {0x58, 0x88, 0x88, 0, 0}, - {0x59, 0x88, 0x88, 0, 0}, - {0x5A, 0, 0, 0, 0}, - {0x5B, 0xcc, 0xcc, 0, 0}, - {0x5C, 0x6, 0x6, 0, 0}, - {0x5D, 0x80, 0x80, 0, 0}, - {0x5E, 0x80, 0x80, 0, 0}, - {0x5F, 0xf8, 0xf8, 0, 0}, - {0x60, 0x88, 0x88, 0, 0}, - {0x61, 0x88, 0x88, 0, 0}, - {0x62, 0x88, 0x8, 1, 1}, - {0x63, 0x88, 0x88, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0x1, 0x1, 1, 1}, - {0x66, 0x8a, 0x8a, 0, 0}, - {0x67, 0x8, 0x8, 0, 0}, - {0x68, 0x83, 0x83, 0, 0}, - {0x69, 0x6, 0x6, 0, 0}, - {0x6A, 0xa0, 0xa0, 0, 0}, - {0x6B, 0xa, 0xa, 0, 0}, - {0x6C, 0x87, 0x87, 1, 1}, - {0x6D, 0x2a, 0x2a, 0, 0}, - {0x6E, 0x2a, 0x2a, 0, 0}, - {0x6F, 0x2a, 0x2a, 0, 0}, - {0x70, 0x2a, 0x2a, 0, 0}, - {0x71, 0x18, 0x18, 0, 0}, - {0x72, 0x6a, 0x6a, 1, 1}, - {0x73, 0xab, 0xab, 1, 1}, - {0x74, 0x13, 0x13, 1, 1}, - {0x75, 0xc1, 0xc1, 1, 1}, - {0x76, 0xaa, 0xaa, 1, 1}, - {0x77, 0x87, 0x87, 1, 1}, - {0x78, 0, 0, 0, 0}, - {0x79, 0x6, 0x6, 0, 0}, - {0x7A, 0x7, 0x7, 0, 0}, - {0x7B, 0x7, 0x7, 0, 0}, - {0x7C, 0x15, 0x15, 0, 0}, - {0x7D, 0x55, 0x55, 0, 0}, - {0x7E, 0x97, 0x97, 1, 1}, - {0x7F, 0x8, 0x8, 0, 0}, - {0x80, 0x14, 0x14, 1, 1}, - {0x81, 0x33, 0x33, 0, 0}, - {0x82, 0x88, 0x88, 0, 0}, - {0x83, 0x6, 0x6, 0, 0}, - {0x84, 0x3, 0x3, 1, 1}, - {0x85, 0xa, 0xa, 0, 0}, - {0x86, 0x3, 0x3, 1, 1}, - {0x87, 0x2a, 0x2a, 0, 0}, - {0x88, 0xa4, 0xa4, 0, 0}, - {0x89, 0x18, 0x18, 0, 0}, - {0x8A, 0x28, 0x28, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0x4a, 0x4a, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0xf8, 0xf8, 0, 0}, - {0x8F, 0x88, 0x88, 0, 0}, - {0x90, 0x88, 0x88, 0, 0}, - {0x91, 0x88, 0x8, 1, 1}, - {0x92, 0x88, 0x88, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0x1, 0x1, 1, 1}, - {0x95, 0x8a, 0x8a, 0, 0}, - {0x96, 0x8, 0x8, 0, 0}, - {0x97, 0x83, 0x83, 0, 0}, - {0x98, 0x6, 0x6, 0, 0}, - {0x99, 0xa0, 0xa0, 0, 0}, - {0x9A, 0xa, 0xa, 0, 0}, - {0x9B, 0x87, 0x87, 1, 1}, - {0x9C, 0x2a, 0x2a, 0, 0}, - {0x9D, 0x2a, 0x2a, 0, 0}, - {0x9E, 0x2a, 0x2a, 0, 0}, - {0x9F, 0x2a, 0x2a, 0, 0}, - {0xA0, 0x18, 0x18, 0, 0}, - {0xA1, 0x6a, 0x6a, 1, 1}, - {0xA2, 0xab, 0xab, 1, 1}, - {0xA3, 0x13, 0x13, 1, 1}, - {0xA4, 0xc1, 0xc1, 1, 1}, - {0xA5, 0xaa, 0xaa, 1, 1}, - {0xA6, 0x87, 0x87, 1, 1}, - {0xA7, 0, 0, 0, 0}, - {0xA8, 0x6, 0x6, 0, 0}, - {0xA9, 0x7, 0x7, 0, 0}, - {0xAA, 0x7, 0x7, 0, 0}, - {0xAB, 0x15, 0x15, 0, 0}, - {0xAC, 0x55, 0x55, 0, 0}, - {0xAD, 0x97, 0x97, 1, 1}, - {0xAE, 0x8, 0x8, 0, 0}, - {0xAF, 0x14, 0x14, 1, 1}, - {0xB0, 0x33, 0x33, 0, 0}, - {0xB1, 0x88, 0x88, 0, 0}, - {0xB2, 0x6, 0x6, 0, 0}, - {0xB3, 0x3, 0x3, 1, 1}, - {0xB4, 0xa, 0xa, 0, 0}, - {0xB5, 0x3, 0x3, 1, 1}, - {0xB6, 0x2a, 0x2a, 0, 0}, - {0xB7, 0xa4, 0xa4, 0, 0}, - {0xB8, 0x18, 0x18, 0, 0}, - {0xB9, 0x28, 0x28, 0, 0}, - {0xBA, 0, 0, 0, 0}, - {0xBB, 0x4a, 0x4a, 0, 0}, - {0xBC, 0, 0, 0, 0}, - {0xBD, 0x71, 0x71, 0, 0}, - {0xBE, 0x72, 0x72, 0, 0}, - {0xBF, 0x73, 0x73, 0, 0}, - {0xC0, 0x74, 0x74, 0, 0}, - {0xC1, 0x75, 0x75, 0, 0}, - {0xC2, 0x76, 0x76, 0, 0}, - {0xC3, 0x77, 0x77, 0, 0}, - {0xC4, 0x78, 0x78, 0, 0}, - {0xC5, 0x79, 0x79, 0, 0}, - {0xC6, 0x7a, 0x7a, 0, 0}, - {0xC7, 0, 0, 0, 0}, - {0xC8, 0, 0, 0, 0}, - {0xC9, 0, 0, 0, 0}, - {0xCA, 0, 0, 0, 0}, - {0xCB, 0, 0, 0, 0}, - {0xCC, 0, 0, 0, 0}, - {0xCD, 0, 0, 0, 0}, - {0xCE, 0x6, 0x6, 0, 0}, - {0xCF, 0, 0, 0, 0}, - {0xD0, 0, 0, 0, 0}, - {0xD1, 0x18, 0x18, 0, 0}, - {0xD2, 0x88, 0x88, 0, 0}, - {0xD3, 0, 0, 0, 0}, - {0xD4, 0, 0, 0, 0}, - {0xD5, 0, 0, 0, 0}, - {0xD6, 0, 0, 0, 0}, - {0xD7, 0, 0, 0, 0}, - {0xD8, 0, 0, 0, 0}, - {0xD9, 0, 0, 0, 0}, - {0xDA, 0x6, 0x6, 0, 0}, - {0xDB, 0, 0, 0, 0}, - {0xDC, 0, 0, 0, 0}, - {0xDD, 0x18, 0x18, 0, 0}, - {0xDE, 0x88, 0x88, 0, 0}, - {0xDF, 0, 0, 0, 0}, - {0xE0, 0, 0, 0, 0}, - {0xE1, 0, 0, 0, 0}, - {0xE2, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0}, -}; - -static struct radio_regs regs_SYN_2056[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0x1, 0x1, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0x60, 0x60, 0, 0}, - {0x23, 0x6, 0x6, 0, 0}, - {0x24, 0xc, 0xc, 0, 0}, - {0x25, 0, 0, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0, 0, 0, 0}, - {0x28, 0x1, 0x1, 0, 0}, - {0x29, 0, 0, 0, 0}, - {0x2A, 0, 0, 0, 0}, - {0x2B, 0, 0, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0xd, 0xd, 0, 0}, - {0x2F, 0x1f, 0x1f, 0, 0}, - {0x30, 0x15, 0x15, 0, 0}, - {0x31, 0xf, 0xf, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0, 0, 0, 0}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0, 0, 0, 0}, - {0x38, 0, 0, 0, 0}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0, 0, 0, 0}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x13, 0x13, 0, 0}, - {0x3D, 0xf, 0xf, 0, 0}, - {0x3E, 0x18, 0x18, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x20, 0x20, 0, 0}, - {0x42, 0x20, 0x20, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x77, 0x77, 0, 0}, - {0x45, 0x7, 0x7, 0, 0}, - {0x46, 0x1, 0x1, 0, 0}, - {0x47, 0x4, 0x4, 0, 0}, - {0x48, 0xf, 0xf, 0, 0}, - {0x49, 0x30, 0x30, 0, 0}, - {0x4A, 0x32, 0x32, 0, 0}, - {0x4B, 0xd, 0xd, 0, 0}, - {0x4C, 0xd, 0xd, 0, 0}, - {0x4D, 0x4, 0x4, 0, 0}, - {0x4E, 0x6, 0x6, 0, 0}, - {0x4F, 0x1, 0x1, 0, 0}, - {0x50, 0x1c, 0x1c, 0, 0}, - {0x51, 0x2, 0x2, 0, 0}, - {0x52, 0x2, 0x2, 0, 0}, - {0x53, 0xf7, 0xf7, 1, 1}, - {0x54, 0xb4, 0xb4, 0, 0}, - {0x55, 0xd2, 0xd2, 0, 0}, - {0x56, 0, 0, 0, 0}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x4, 0x4, 0, 0}, - {0x59, 0x96, 0x96, 0, 0}, - {0x5A, 0x3e, 0x3e, 0, 0}, - {0x5B, 0x3e, 0x3e, 0, 0}, - {0x5C, 0x13, 0x13, 0, 0}, - {0x5D, 0x2, 0x2, 0, 0}, - {0x5E, 0, 0, 0, 0}, - {0x5F, 0x7, 0x7, 0, 0}, - {0x60, 0x7, 0x7, 1, 1}, - {0x61, 0x8, 0x8, 0, 0}, - {0x62, 0x3, 0x3, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0x40, 0x40, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0x1, 0x1, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0x60, 0x60, 0, 0}, - {0x71, 0x66, 0x66, 0, 0}, - {0x72, 0xc, 0xc, 0, 0}, - {0x73, 0x66, 0x66, 0, 0}, - {0x74, 0x8f, 0x8f, 1, 1}, - {0x75, 0, 0, 0, 0}, - {0x76, 0xcc, 0xcc, 0, 0}, - {0x77, 0x1, 0x1, 0, 0}, - {0x78, 0x66, 0x66, 0, 0}, - {0x79, 0x66, 0x66, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0xff, 0xff, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0x95, 0, 0, 0, 0}, - {0x96, 0, 0, 0, 0}, - {0x97, 0, 0, 0, 0}, - {0x98, 0, 0, 0, 0}, - {0x99, 0, 0, 0, 0}, - {0x9A, 0, 0, 0, 0}, - {0x9B, 0, 0, 0, 0}, - {0x9C, 0, 0, 0, 0}, - {0x9D, 0, 0, 0, 0}, - {0x9E, 0, 0, 0, 0}, - {0x9F, 0x6, 0x6, 0, 0}, - {0xA0, 0x66, 0x66, 0, 0}, - {0xA1, 0x66, 0x66, 0, 0}, - {0xA2, 0x66, 0x66, 0, 0}, - {0xA3, 0x66, 0x66, 0, 0}, - {0xA4, 0x66, 0x66, 0, 0}, - {0xA5, 0x66, 0x66, 0, 0}, - {0xA6, 0x66, 0x66, 0, 0}, - {0xA7, 0x66, 0x66, 0, 0}, - {0xA8, 0x66, 0x66, 0, 0}, - {0xA9, 0x66, 0x66, 0, 0}, - {0xAA, 0x66, 0x66, 0, 0}, - {0xAB, 0x66, 0x66, 0, 0}, - {0xAC, 0x66, 0x66, 0, 0}, - {0xAD, 0x66, 0x66, 0, 0}, - {0xAE, 0x66, 0x66, 0, 0}, - {0xAF, 0x66, 0x66, 0, 0}, - {0xB0, 0x66, 0x66, 0, 0}, - {0xB1, 0x66, 0x66, 0, 0}, - {0xB2, 0x66, 0x66, 0, 0}, - {0xB3, 0xa, 0xa, 0, 0}, - {0xB4, 0, 0, 0, 0}, - {0xB5, 0, 0, 0, 0}, - {0xB6, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_TX_2056[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0x88, 0x88, 0, 0}, - {0x22, 0x88, 0x88, 0, 0}, - {0x23, 0x88, 0x88, 0, 0}, - {0x24, 0x88, 0x88, 0, 0}, - {0x25, 0xc, 0xc, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0x3, 0x3, 0, 0}, - {0x28, 0, 0, 0, 0}, - {0x29, 0x3, 0x3, 0, 0}, - {0x2A, 0x37, 0x37, 0, 0}, - {0x2B, 0x3, 0x3, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0x1, 0x1, 0, 0}, - {0x2F, 0x1, 0x1, 0, 0}, - {0x30, 0, 0, 0, 0}, - {0x31, 0, 0, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0x11, 0x11, 0, 0}, - {0x34, 0x11, 0x11, 0, 0}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0x3, 0x3, 0, 0}, - {0x38, 0xf, 0xf, 0, 0}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0x2d, 0x2d, 0, 0}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x6e, 0x6e, 0, 0}, - {0x3D, 0xf0, 0xf0, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x3, 0x3, 0, 0}, - {0x42, 0x3, 0x3, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x1e, 0x1e, 0, 0}, - {0x45, 0, 0, 0, 0}, - {0x46, 0x6e, 0x6e, 0, 0}, - {0x47, 0xf0, 0xf0, 1, 1}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x2, 0x2, 0, 0}, - {0x4A, 0xff, 0xff, 1, 1}, - {0x4B, 0xc, 0xc, 0, 0}, - {0x4C, 0, 0, 0, 0}, - {0x4D, 0x38, 0x38, 0, 0}, - {0x4E, 0x70, 0x70, 1, 1}, - {0x4F, 0x2, 0x2, 0, 0}, - {0x50, 0x88, 0x88, 0, 0}, - {0x51, 0xc, 0xc, 0, 0}, - {0x52, 0, 0, 0, 0}, - {0x53, 0x8, 0x8, 0, 0}, - {0x54, 0x70, 0x70, 1, 1}, - {0x55, 0x2, 0x2, 0, 0}, - {0x56, 0xff, 0xff, 1, 1}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x83, 0x83, 0, 0}, - {0x59, 0x77, 0x77, 1, 1}, - {0x5A, 0, 0, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x88, 0x88, 0, 0}, - {0x5D, 0, 0, 0, 0}, - {0x5E, 0x8, 0x8, 0, 0}, - {0x5F, 0x77, 0x77, 1, 1}, - {0x60, 0x1, 0x1, 0, 0}, - {0x61, 0, 0, 0, 0}, - {0x62, 0x7, 0x7, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0x7, 0x7, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0x74, 0x74, 1, 1}, - {0x68, 0, 0, 0, 0}, - {0x69, 0xa, 0xa, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0x2, 0x2, 0, 0}, - {0x72, 0, 0, 0, 0}, - {0x73, 0, 0, 0, 0}, - {0x74, 0xe, 0xe, 0, 0}, - {0x75, 0xe, 0xe, 0, 0}, - {0x76, 0xe, 0xe, 0, 0}, - {0x77, 0x13, 0x13, 0, 0}, - {0x78, 0x13, 0x13, 0, 0}, - {0x79, 0x1b, 0x1b, 0, 0}, - {0x7A, 0x1b, 0x1b, 0, 0}, - {0x7B, 0x55, 0x55, 0, 0}, - {0x7C, 0x5b, 0x5b, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_RX_2056[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0x3, 0x3, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0, 0, 0, 0}, - {0x23, 0x90, 0x90, 0, 0}, - {0x24, 0x55, 0x55, 0, 0}, - {0x25, 0x15, 0x15, 0, 0}, - {0x26, 0x5, 0x5, 0, 0}, - {0x27, 0x15, 0x15, 0, 0}, - {0x28, 0x5, 0x5, 0, 0}, - {0x29, 0x20, 0x20, 0, 0}, - {0x2A, 0x11, 0x11, 0, 0}, - {0x2B, 0x90, 0x90, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0x88, 0x88, 0, 0}, - {0x2E, 0x32, 0x32, 0, 0}, - {0x2F, 0x77, 0x77, 0, 0}, - {0x30, 0x17, 0x17, 1, 1}, - {0x31, 0xff, 0xff, 1, 1}, - {0x32, 0x20, 0x20, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0x88, 0x88, 0, 0}, - {0x35, 0x32, 0x32, 0, 0}, - {0x36, 0x77, 0x77, 0, 0}, - {0x37, 0x17, 0x17, 1, 1}, - {0x38, 0xf0, 0xf0, 1, 1}, - {0x39, 0x20, 0x20, 0, 0}, - {0x3A, 0x8, 0x8, 0, 0}, - {0x3B, 0x99, 0x99, 0, 0}, - {0x3C, 0, 0, 0, 0}, - {0x3D, 0x44, 0x44, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0x44, 0x44, 0, 0}, - {0x40, 0xf, 0xf, 1, 1}, - {0x41, 0x6, 0x6, 0, 0}, - {0x42, 0x4, 0x4, 0, 0}, - {0x43, 0x50, 0x50, 1, 1}, - {0x44, 0x8, 0x8, 0, 0}, - {0x45, 0x99, 0x99, 0, 0}, - {0x46, 0, 0, 0, 0}, - {0x47, 0x11, 0x11, 0, 0}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x44, 0x44, 0, 0}, - {0x4A, 0x7, 0x7, 0, 0}, - {0x4B, 0x6, 0x6, 0, 0}, - {0x4C, 0x4, 0x4, 0, 0}, - {0x4D, 0, 0, 0, 0}, - {0x4E, 0, 0, 0, 0}, - {0x4F, 0x66, 0x66, 0, 0}, - {0x50, 0x66, 0x66, 0, 0}, - {0x51, 0x57, 0x57, 0, 0}, - {0x52, 0x57, 0x57, 0, 0}, - {0x53, 0x44, 0x44, 0, 0}, - {0x54, 0, 0, 0, 0}, - {0x55, 0, 0, 0, 0}, - {0x56, 0x8, 0x8, 0, 0}, - {0x57, 0x8, 0x8, 0, 0}, - {0x58, 0x7, 0x7, 0, 0}, - {0x59, 0x22, 0x22, 0, 0}, - {0x5A, 0x22, 0x22, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x23, 0x23, 0, 0}, - {0x5D, 0x7, 0x7, 0, 0}, - {0x5E, 0x55, 0x55, 0, 0}, - {0x5F, 0x23, 0x23, 0, 0}, - {0x60, 0x41, 0x41, 0, 0}, - {0x61, 0x1, 0x1, 0, 0}, - {0x62, 0xa, 0xa, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0, 0, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0xc, 0xc, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0, 0, 0, 0}, - {0x72, 0x22, 0x22, 0, 0}, - {0x73, 0x22, 0x22, 0, 0}, - {0x74, 0x2, 0x2, 0, 0}, - {0x75, 0xa, 0xa, 0, 0}, - {0x76, 0x1, 0x1, 0, 0}, - {0x77, 0x22, 0x22, 0, 0}, - {0x78, 0x30, 0x30, 0, 0}, - {0x79, 0, 0, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_SYN_2056_A1[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0x1, 0x1, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0x60, 0x60, 0, 0}, - {0x23, 0x6, 0x6, 0, 0}, - {0x24, 0xc, 0xc, 0, 0}, - {0x25, 0, 0, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0, 0, 0, 0}, - {0x28, 0x1, 0x1, 0, 0}, - {0x29, 0, 0, 0, 0}, - {0x2A, 0, 0, 0, 0}, - {0x2B, 0, 0, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0xd, 0xd, 0, 0}, - {0x2F, 0x1f, 0x1f, 0, 0}, - {0x30, 0x15, 0x15, 0, 0}, - {0x31, 0xf, 0xf, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0, 0, 0, 0}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0, 0, 0, 0}, - {0x38, 0, 0, 0, 0}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0, 0, 0, 0}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x13, 0x13, 0, 0}, - {0x3D, 0xf, 0xf, 0, 0}, - {0x3E, 0x18, 0x18, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x20, 0x20, 0, 0}, - {0x42, 0x20, 0x20, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x77, 0x77, 0, 0}, - {0x45, 0x7, 0x7, 0, 0}, - {0x46, 0x1, 0x1, 0, 0}, - {0x47, 0x4, 0x4, 0, 0}, - {0x48, 0xf, 0xf, 0, 0}, - {0x49, 0x30, 0x30, 0, 0}, - {0x4A, 0x32, 0x32, 0, 0}, - {0x4B, 0xd, 0xd, 0, 0}, - {0x4C, 0xd, 0xd, 0, 0}, - {0x4D, 0x4, 0x4, 0, 0}, - {0x4E, 0x6, 0x6, 0, 0}, - {0x4F, 0x1, 0x1, 0, 0}, - {0x50, 0x1c, 0x1c, 0, 0}, - {0x51, 0x2, 0x2, 0, 0}, - {0x52, 0x2, 0x2, 0, 0}, - {0x53, 0xf7, 0xf7, 1, 1}, - {0x54, 0xb4, 0xb4, 0, 0}, - {0x55, 0xd2, 0xd2, 0, 0}, - {0x56, 0, 0, 0, 0}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x4, 0x4, 0, 0}, - {0x59, 0x96, 0x96, 0, 0}, - {0x5A, 0x3e, 0x3e, 0, 0}, - {0x5B, 0x3e, 0x3e, 0, 0}, - {0x5C, 0x13, 0x13, 0, 0}, - {0x5D, 0x2, 0x2, 0, 0}, - {0x5E, 0, 0, 0, 0}, - {0x5F, 0x7, 0x7, 0, 0}, - {0x60, 0x7, 0x7, 1, 1}, - {0x61, 0x8, 0x8, 0, 0}, - {0x62, 0x3, 0x3, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0x40, 0x40, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0x1, 0x1, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0x60, 0x60, 0, 0}, - {0x71, 0x66, 0x66, 0, 0}, - {0x72, 0xc, 0xc, 0, 0}, - {0x73, 0x66, 0x66, 0, 0}, - {0x74, 0x8f, 0x8f, 1, 1}, - {0x75, 0, 0, 0, 0}, - {0x76, 0xcc, 0xcc, 0, 0}, - {0x77, 0x1, 0x1, 0, 0}, - {0x78, 0x66, 0x66, 0, 0}, - {0x79, 0x66, 0x66, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0xff, 0xff, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0x95, 0, 0, 0, 0}, - {0x96, 0, 0, 0, 0}, - {0x97, 0, 0, 0, 0}, - {0x98, 0, 0, 0, 0}, - {0x99, 0, 0, 0, 0}, - {0x9A, 0, 0, 0, 0}, - {0x9B, 0, 0, 0, 0}, - {0x9C, 0, 0, 0, 0}, - {0x9D, 0, 0, 0, 0}, - {0x9E, 0, 0, 0, 0}, - {0x9F, 0x6, 0x6, 0, 0}, - {0xA0, 0x66, 0x66, 0, 0}, - {0xA1, 0x66, 0x66, 0, 0}, - {0xA2, 0x66, 0x66, 0, 0}, - {0xA3, 0x66, 0x66, 0, 0}, - {0xA4, 0x66, 0x66, 0, 0}, - {0xA5, 0x66, 0x66, 0, 0}, - {0xA6, 0x66, 0x66, 0, 0}, - {0xA7, 0x66, 0x66, 0, 0}, - {0xA8, 0x66, 0x66, 0, 0}, - {0xA9, 0x66, 0x66, 0, 0}, - {0xAA, 0x66, 0x66, 0, 0}, - {0xAB, 0x66, 0x66, 0, 0}, - {0xAC, 0x66, 0x66, 0, 0}, - {0xAD, 0x66, 0x66, 0, 0}, - {0xAE, 0x66, 0x66, 0, 0}, - {0xAF, 0x66, 0x66, 0, 0}, - {0xB0, 0x66, 0x66, 0, 0}, - {0xB1, 0x66, 0x66, 0, 0}, - {0xB2, 0x66, 0x66, 0, 0}, - {0xB3, 0xa, 0xa, 0, 0}, - {0xB4, 0, 0, 0, 0}, - {0xB5, 0, 0, 0, 0}, - {0xB6, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_TX_2056_A1[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0x88, 0x88, 0, 0}, - {0x22, 0x88, 0x88, 0, 0}, - {0x23, 0x88, 0x88, 0, 0}, - {0x24, 0x88, 0x88, 0, 0}, - {0x25, 0xc, 0xc, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0x3, 0x3, 0, 0}, - {0x28, 0, 0, 0, 0}, - {0x29, 0x3, 0x3, 0, 0}, - {0x2A, 0x37, 0x37, 0, 0}, - {0x2B, 0x3, 0x3, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0x1, 0x1, 0, 0}, - {0x2F, 0x1, 0x1, 0, 0}, - {0x30, 0, 0, 0, 0}, - {0x31, 0, 0, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0x11, 0x11, 0, 0}, - {0x34, 0x11, 0x11, 0, 0}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0x3, 0x3, 0, 0}, - {0x38, 0xf, 0xf, 0, 0}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0x2d, 0x2d, 0, 0}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x6e, 0x6e, 0, 0}, - {0x3D, 0xf0, 0xf0, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x3, 0x3, 0, 0}, - {0x42, 0x3, 0x3, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x1e, 0x1e, 0, 0}, - {0x45, 0, 0, 0, 0}, - {0x46, 0x6e, 0x6e, 0, 0}, - {0x47, 0xf0, 0xf0, 1, 1}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x2, 0x2, 0, 0}, - {0x4A, 0xff, 0xff, 1, 1}, - {0x4B, 0xc, 0xc, 0, 0}, - {0x4C, 0, 0, 0, 0}, - {0x4D, 0x38, 0x38, 0, 0}, - {0x4E, 0x70, 0x70, 1, 1}, - {0x4F, 0x2, 0x2, 0, 0}, - {0x50, 0x88, 0x88, 0, 0}, - {0x51, 0xc, 0xc, 0, 0}, - {0x52, 0, 0, 0, 0}, - {0x53, 0x8, 0x8, 0, 0}, - {0x54, 0x70, 0x70, 1, 1}, - {0x55, 0x2, 0x2, 0, 0}, - {0x56, 0xff, 0xff, 1, 1}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x83, 0x83, 0, 0}, - {0x59, 0x77, 0x77, 1, 1}, - {0x5A, 0, 0, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x88, 0x88, 0, 0}, - {0x5D, 0, 0, 0, 0}, - {0x5E, 0x8, 0x8, 0, 0}, - {0x5F, 0x77, 0x77, 1, 1}, - {0x60, 0x1, 0x1, 0, 0}, - {0x61, 0, 0, 0, 0}, - {0x62, 0x7, 0x7, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0x7, 0x7, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0x72, 0x72, 1, 1}, - {0x68, 0, 0, 0, 0}, - {0x69, 0xa, 0xa, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0x2, 0x2, 0, 0}, - {0x72, 0, 0, 0, 0}, - {0x73, 0, 0, 0, 0}, - {0x74, 0xe, 0xe, 0, 0}, - {0x75, 0xe, 0xe, 0, 0}, - {0x76, 0xe, 0xe, 0, 0}, - {0x77, 0x13, 0x13, 0, 0}, - {0x78, 0x13, 0x13, 0, 0}, - {0x79, 0x1b, 0x1b, 0, 0}, - {0x7A, 0x1b, 0x1b, 0, 0}, - {0x7B, 0x55, 0x55, 0, 0}, - {0x7C, 0x5b, 0x5b, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_RX_2056_A1[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0x3, 0x3, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0, 0, 0, 0}, - {0x23, 0x90, 0x90, 0, 0}, - {0x24, 0x55, 0x55, 0, 0}, - {0x25, 0x15, 0x15, 0, 0}, - {0x26, 0x5, 0x5, 0, 0}, - {0x27, 0x15, 0x15, 0, 0}, - {0x28, 0x5, 0x5, 0, 0}, - {0x29, 0x20, 0x20, 0, 0}, - {0x2A, 0x11, 0x11, 0, 0}, - {0x2B, 0x90, 0x90, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0x88, 0x88, 0, 0}, - {0x2E, 0x32, 0x32, 0, 0}, - {0x2F, 0x77, 0x77, 0, 0}, - {0x30, 0x17, 0x17, 1, 1}, - {0x31, 0xff, 0xff, 1, 1}, - {0x32, 0x20, 0x20, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0x88, 0x88, 0, 0}, - {0x35, 0x32, 0x32, 0, 0}, - {0x36, 0x77, 0x77, 0, 0}, - {0x37, 0x17, 0x17, 1, 1}, - {0x38, 0xf0, 0xf0, 1, 1}, - {0x39, 0x20, 0x20, 0, 0}, - {0x3A, 0x8, 0x8, 0, 0}, - {0x3B, 0x55, 0x55, 1, 1}, - {0x3C, 0, 0, 0, 0}, - {0x3D, 0x44, 0x44, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0x44, 0x44, 0, 0}, - {0x40, 0xf, 0xf, 1, 1}, - {0x41, 0x6, 0x6, 0, 0}, - {0x42, 0x4, 0x4, 0, 0}, - {0x43, 0x50, 0x50, 1, 1}, - {0x44, 0x8, 0x8, 0, 0}, - {0x45, 0x55, 0x55, 1, 1}, - {0x46, 0, 0, 0, 0}, - {0x47, 0x11, 0x11, 0, 0}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x44, 0x44, 0, 0}, - {0x4A, 0x7, 0x7, 0, 0}, - {0x4B, 0x6, 0x6, 0, 0}, - {0x4C, 0x4, 0x4, 0, 0}, - {0x4D, 0, 0, 0, 0}, - {0x4E, 0, 0, 0, 0}, - {0x4F, 0x26, 0x26, 1, 1}, - {0x50, 0x26, 0x26, 1, 1}, - {0x51, 0xf, 0xf, 1, 1}, - {0x52, 0xf, 0xf, 1, 1}, - {0x53, 0x44, 0x44, 0, 0}, - {0x54, 0, 0, 0, 0}, - {0x55, 0, 0, 0, 0}, - {0x56, 0x8, 0x8, 0, 0}, - {0x57, 0x8, 0x8, 0, 0}, - {0x58, 0x7, 0x7, 0, 0}, - {0x59, 0x22, 0x22, 0, 0}, - {0x5A, 0x22, 0x22, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x2f, 0x2f, 1, 1}, - {0x5D, 0x7, 0x7, 0, 0}, - {0x5E, 0x55, 0x55, 0, 0}, - {0x5F, 0x23, 0x23, 0, 0}, - {0x60, 0x41, 0x41, 0, 0}, - {0x61, 0x1, 0x1, 0, 0}, - {0x62, 0xa, 0xa, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0, 0, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0xc, 0xc, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0, 0, 0, 0}, - {0x72, 0x22, 0x22, 0, 0}, - {0x73, 0x22, 0x22, 0, 0}, - {0x74, 0, 0, 1, 1}, - {0x75, 0xa, 0xa, 0, 0}, - {0x76, 0x1, 0x1, 0, 0}, - {0x77, 0x22, 0x22, 0, 0}, - {0x78, 0x30, 0x30, 0, 0}, - {0x79, 0, 0, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_SYN_2056_rev5[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0x1, 0x1, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0x60, 0x60, 0, 0}, - {0x23, 0x6, 0x6, 0, 0}, - {0x24, 0xc, 0xc, 0, 0}, - {0x25, 0, 0, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0, 0, 0, 0}, - {0x28, 0x1, 0x1, 0, 0}, - {0x29, 0, 0, 0, 0}, - {0x2A, 0, 0, 0, 0}, - {0x2B, 0, 0, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0, 0, 0, 0}, - {0x2F, 0x1f, 0x1f, 0, 0}, - {0x30, 0x15, 0x15, 0, 0}, - {0x31, 0xf, 0xf, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0, 0, 0, 0}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0, 0, 0, 0}, - {0x38, 0, 0, 0, 0}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0, 0, 0, 0}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x13, 0x13, 0, 0}, - {0x3D, 0xf, 0xf, 0, 0}, - {0x3E, 0x18, 0x18, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x20, 0x20, 0, 0}, - {0x42, 0x20, 0x20, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x77, 0x77, 0, 0}, - {0x45, 0x7, 0x7, 0, 0}, - {0x46, 0x1, 0x1, 0, 0}, - {0x47, 0x4, 0x4, 0, 0}, - {0x48, 0xf, 0xf, 0, 0}, - {0x49, 0x30, 0x30, 0, 0}, - {0x4A, 0x32, 0x32, 0, 0}, - {0x4B, 0xd, 0xd, 0, 0}, - {0x4C, 0xd, 0xd, 0, 0}, - {0x4D, 0x4, 0x4, 0, 0}, - {0x4E, 0x6, 0x6, 0, 0}, - {0x4F, 0x1, 0x1, 0, 0}, - {0x50, 0x1c, 0x1c, 0, 0}, - {0x51, 0x2, 0x2, 0, 0}, - {0x52, 0x2, 0x2, 0, 0}, - {0x53, 0xf7, 0xf7, 1, 1}, - {0x54, 0xb4, 0xb4, 0, 0}, - {0x55, 0xd2, 0xd2, 0, 0}, - {0x56, 0, 0, 0, 0}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x4, 0x4, 0, 0}, - {0x59, 0x96, 0x96, 0, 0}, - {0x5A, 0x3e, 0x3e, 0, 0}, - {0x5B, 0x3e, 0x3e, 0, 0}, - {0x5C, 0x13, 0x13, 0, 0}, - {0x5D, 0x2, 0x2, 0, 0}, - {0x5E, 0, 0, 0, 0}, - {0x5F, 0x7, 0x7, 0, 0}, - {0x60, 0x7, 0x7, 1, 1}, - {0x61, 0x8, 0x8, 0, 0}, - {0x62, 0x3, 0x3, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0x40, 0x40, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0x1, 0x1, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0x60, 0x60, 0, 0}, - {0x71, 0x66, 0x66, 0, 0}, - {0x72, 0xc, 0xc, 0, 0}, - {0x73, 0x66, 0x66, 0, 0}, - {0x74, 0x8f, 0x8f, 1, 1}, - {0x75, 0, 0, 0, 0}, - {0x76, 0xcc, 0xcc, 0, 0}, - {0x77, 0x1, 0x1, 0, 0}, - {0x78, 0x66, 0x66, 0, 0}, - {0x79, 0x66, 0x66, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0xff, 0xff, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0x95, 0, 0, 0, 0}, - {0x96, 0, 0, 0, 0}, - {0x97, 0, 0, 0, 0}, - {0x98, 0, 0, 0, 0}, - {0x99, 0, 0, 0, 0}, - {0x9A, 0, 0, 0, 0}, - {0x9B, 0, 0, 0, 0}, - {0x9C, 0, 0, 0, 0}, - {0x9D, 0, 0, 0, 0}, - {0x9E, 0, 0, 0, 0}, - {0x9F, 0x6, 0x6, 0, 0}, - {0xA0, 0x66, 0x66, 0, 0}, - {0xA1, 0x66, 0x66, 0, 0}, - {0xA2, 0x66, 0x66, 0, 0}, - {0xA3, 0x66, 0x66, 0, 0}, - {0xA4, 0x66, 0x66, 0, 0}, - {0xA5, 0x66, 0x66, 0, 0}, - {0xA6, 0x66, 0x66, 0, 0}, - {0xA7, 0x66, 0x66, 0, 0}, - {0xA8, 0x66, 0x66, 0, 0}, - {0xA9, 0x66, 0x66, 0, 0}, - {0xAA, 0x66, 0x66, 0, 0}, - {0xAB, 0x66, 0x66, 0, 0}, - {0xAC, 0x66, 0x66, 0, 0}, - {0xAD, 0x66, 0x66, 0, 0}, - {0xAE, 0x66, 0x66, 0, 0}, - {0xAF, 0x66, 0x66, 0, 0}, - {0xB0, 0x66, 0x66, 0, 0}, - {0xB1, 0x66, 0x66, 0, 0}, - {0xB2, 0x66, 0x66, 0, 0}, - {0xB3, 0xa, 0xa, 0, 0}, - {0xB4, 0, 0, 0, 0}, - {0xB5, 0, 0, 0, 0}, - {0xB6, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_TX_2056_rev5[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0x88, 0x88, 0, 0}, - {0x22, 0x88, 0x88, 0, 0}, - {0x23, 0x88, 0x88, 0, 0}, - {0x24, 0x88, 0x88, 0, 0}, - {0x25, 0xc, 0xc, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0x3, 0x3, 0, 0}, - {0x28, 0, 0, 0, 0}, - {0x29, 0x3, 0x3, 0, 0}, - {0x2A, 0x37, 0x37, 0, 0}, - {0x2B, 0x3, 0x3, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0x1, 0x1, 0, 0}, - {0x2F, 0x1, 0x1, 0, 0}, - {0x30, 0, 0, 0, 0}, - {0x31, 0, 0, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0x11, 0x11, 0, 0}, - {0x34, 0x11, 0x11, 0, 0}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0x3, 0x3, 0, 0}, - {0x38, 0xf, 0xf, 0, 0}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0x2d, 0x2d, 0, 0}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x6e, 0x6e, 0, 0}, - {0x3D, 0xf0, 0xf0, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x3, 0x3, 0, 0}, - {0x42, 0x3, 0x3, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x1e, 0x1e, 0, 0}, - {0x45, 0, 0, 0, 0}, - {0x46, 0x6e, 0x6e, 0, 0}, - {0x47, 0xf0, 0xf0, 1, 1}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x2, 0x2, 0, 0}, - {0x4A, 0xff, 0xff, 1, 1}, - {0x4B, 0xc, 0xc, 0, 0}, - {0x4C, 0, 0, 0, 0}, - {0x4D, 0x38, 0x38, 0, 0}, - {0x4E, 0x70, 0x70, 1, 1}, - {0x4F, 0x2, 0x2, 0, 0}, - {0x50, 0x88, 0x88, 0, 0}, - {0x51, 0xc, 0xc, 0, 0}, - {0x52, 0, 0, 0, 0}, - {0x53, 0x8, 0x8, 0, 0}, - {0x54, 0x70, 0x70, 1, 1}, - {0x55, 0x2, 0x2, 0, 0}, - {0x56, 0xff, 0xff, 1, 1}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x83, 0x83, 0, 0}, - {0x59, 0x77, 0x77, 1, 1}, - {0x5A, 0, 0, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x88, 0x88, 0, 0}, - {0x5D, 0, 0, 0, 0}, - {0x5E, 0x8, 0x8, 0, 0}, - {0x5F, 0x77, 0x77, 1, 1}, - {0x60, 0x1, 0x1, 0, 0}, - {0x61, 0, 0, 0, 0}, - {0x62, 0x7, 0x7, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0x7, 0x7, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 1, 1}, - {0x68, 0, 0, 0, 0}, - {0x69, 0xa, 0xa, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0x2, 0x2, 0, 0}, - {0x72, 0, 0, 0, 0}, - {0x73, 0, 0, 0, 0}, - {0x74, 0xe, 0xe, 0, 0}, - {0x75, 0xe, 0xe, 0, 0}, - {0x76, 0xe, 0xe, 0, 0}, - {0x77, 0x13, 0x13, 0, 0}, - {0x78, 0x13, 0x13, 0, 0}, - {0x79, 0x1b, 0x1b, 0, 0}, - {0x7A, 0x1b, 0x1b, 0, 0}, - {0x7B, 0x55, 0x55, 0, 0}, - {0x7C, 0x5b, 0x5b, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0x70, 0x70, 0, 0}, - {0x94, 0x70, 0x70, 0, 0}, - {0x95, 0x71, 0x71, 1, 1}, - {0x96, 0x71, 0x71, 1, 1}, - {0x97, 0x72, 0x72, 1, 1}, - {0x98, 0x73, 0x73, 1, 1}, - {0x99, 0x74, 0x74, 1, 1}, - {0x9A, 0x75, 0x75, 1, 1}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_RX_2056_rev5[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0x3, 0x3, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0, 0, 0, 0}, - {0x23, 0x90, 0x90, 0, 0}, - {0x24, 0x55, 0x55, 0, 0}, - {0x25, 0x15, 0x15, 0, 0}, - {0x26, 0x5, 0x5, 0, 0}, - {0x27, 0x15, 0x15, 0, 0}, - {0x28, 0x5, 0x5, 0, 0}, - {0x29, 0x20, 0x20, 0, 0}, - {0x2A, 0x11, 0x11, 0, 0}, - {0x2B, 0x90, 0x90, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0x88, 0x88, 0, 0}, - {0x2E, 0x32, 0x32, 0, 0}, - {0x2F, 0x77, 0x77, 0, 0}, - {0x30, 0x17, 0x17, 1, 1}, - {0x31, 0xff, 0xff, 1, 1}, - {0x32, 0x20, 0x20, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0x88, 0x88, 0, 0}, - {0x35, 0x32, 0x32, 0, 0}, - {0x36, 0x77, 0x77, 0, 0}, - {0x37, 0x17, 0x17, 1, 1}, - {0x38, 0xf0, 0xf0, 1, 1}, - {0x39, 0x20, 0x20, 0, 0}, - {0x3A, 0x8, 0x8, 0, 0}, - {0x3B, 0x55, 0x55, 1, 1}, - {0x3C, 0, 0, 0, 0}, - {0x3D, 0x88, 0x88, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0, 0, 1, 1}, - {0x40, 0x7, 0x7, 1, 1}, - {0x41, 0x6, 0x6, 0, 0}, - {0x42, 0x4, 0x4, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x8, 0x8, 0, 0}, - {0x45, 0x55, 0x55, 1, 1}, - {0x46, 0, 0, 0, 0}, - {0x47, 0x11, 0x11, 0, 0}, - {0x48, 0, 0, 0, 0}, - {0x49, 0, 0, 1, 1}, - {0x4A, 0x7, 0x7, 0, 0}, - {0x4B, 0x6, 0x6, 0, 0}, - {0x4C, 0x4, 0x4, 0, 0}, - {0x4D, 0, 0, 0, 0}, - {0x4E, 0, 0, 0, 0}, - {0x4F, 0x26, 0x26, 1, 1}, - {0x50, 0x26, 0x26, 1, 1}, - {0x51, 0xf, 0xf, 1, 1}, - {0x52, 0xf, 0xf, 1, 1}, - {0x53, 0x44, 0x44, 0, 0}, - {0x54, 0, 0, 0, 0}, - {0x55, 0, 0, 0, 0}, - {0x56, 0x8, 0x8, 0, 0}, - {0x57, 0x8, 0x8, 0, 0}, - {0x58, 0x7, 0x7, 0, 0}, - {0x59, 0x22, 0x22, 0, 0}, - {0x5A, 0x22, 0x22, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x4, 0x4, 1, 1}, - {0x5D, 0x7, 0x7, 0, 0}, - {0x5E, 0x55, 0x55, 0, 0}, - {0x5F, 0x23, 0x23, 0, 0}, - {0x60, 0x41, 0x41, 0, 0}, - {0x61, 0x1, 0x1, 0, 0}, - {0x62, 0xa, 0xa, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0, 0, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0xc, 0xc, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0, 0, 0, 0}, - {0x72, 0x22, 0x22, 0, 0}, - {0x73, 0x22, 0x22, 0, 0}, - {0x74, 0, 0, 1, 1}, - {0x75, 0xa, 0xa, 0, 0}, - {0x76, 0x1, 0x1, 0, 0}, - {0x77, 0x22, 0x22, 0, 0}, - {0x78, 0x30, 0x30, 0, 0}, - {0x79, 0, 0, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_SYN_2056_rev6[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0x1, 0x1, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0x60, 0x60, 0, 0}, - {0x23, 0x6, 0x6, 0, 0}, - {0x24, 0xc, 0xc, 0, 0}, - {0x25, 0, 0, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0, 0, 0, 0}, - {0x28, 0x1, 0x1, 0, 0}, - {0x29, 0, 0, 0, 0}, - {0x2A, 0, 0, 0, 0}, - {0x2B, 0, 0, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0, 0, 0, 0}, - {0x2F, 0x1f, 0x1f, 0, 0}, - {0x30, 0x15, 0x15, 0, 0}, - {0x31, 0xf, 0xf, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0, 0, 0, 0}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0, 0, 0, 0}, - {0x38, 0, 0, 0, 0}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0, 0, 0, 0}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x13, 0x13, 0, 0}, - {0x3D, 0xf, 0xf, 0, 0}, - {0x3E, 0x18, 0x18, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x20, 0x20, 0, 0}, - {0x42, 0x20, 0x20, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x77, 0x77, 0, 0}, - {0x45, 0x7, 0x7, 0, 0}, - {0x46, 0x1, 0x1, 0, 0}, - {0x47, 0x4, 0x4, 0, 0}, - {0x48, 0xf, 0xf, 0, 0}, - {0x49, 0x30, 0x30, 0, 0}, - {0x4A, 0x32, 0x32, 0, 0}, - {0x4B, 0xd, 0xd, 0, 0}, - {0x4C, 0xd, 0xd, 0, 0}, - {0x4D, 0x4, 0x4, 0, 0}, - {0x4E, 0x6, 0x6, 0, 0}, - {0x4F, 0x1, 0x1, 0, 0}, - {0x50, 0x1c, 0x1c, 0, 0}, - {0x51, 0x2, 0x2, 0, 0}, - {0x52, 0x2, 0x2, 0, 0}, - {0x53, 0xf7, 0xf7, 1, 1}, - {0x54, 0xb4, 0xb4, 0, 0}, - {0x55, 0xd2, 0xd2, 0, 0}, - {0x56, 0, 0, 0, 0}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x4, 0x4, 0, 0}, - {0x59, 0x96, 0x96, 0, 0}, - {0x5A, 0x3e, 0x3e, 0, 0}, - {0x5B, 0x3e, 0x3e, 0, 0}, - {0x5C, 0x13, 0x13, 0, 0}, - {0x5D, 0x2, 0x2, 0, 0}, - {0x5E, 0, 0, 0, 0}, - {0x5F, 0x7, 0x7, 0, 0}, - {0x60, 0x7, 0x7, 1, 1}, - {0x61, 0x8, 0x8, 0, 0}, - {0x62, 0x3, 0x3, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0x40, 0x40, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0x1, 0x1, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0x60, 0x60, 0, 0}, - {0x71, 0x66, 0x66, 0, 0}, - {0x72, 0xc, 0xc, 0, 0}, - {0x73, 0x66, 0x66, 0, 0}, - {0x74, 0x8f, 0x8f, 1, 1}, - {0x75, 0, 0, 0, 0}, - {0x76, 0xcc, 0xcc, 0, 0}, - {0x77, 0x1, 0x1, 0, 0}, - {0x78, 0x66, 0x66, 0, 0}, - {0x79, 0x66, 0x66, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0xff, 0xff, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0x95, 0, 0, 0, 0}, - {0x96, 0, 0, 0, 0}, - {0x97, 0, 0, 0, 0}, - {0x98, 0, 0, 0, 0}, - {0x99, 0, 0, 0, 0}, - {0x9A, 0, 0, 0, 0}, - {0x9B, 0, 0, 0, 0}, - {0x9C, 0, 0, 0, 0}, - {0x9D, 0, 0, 0, 0}, - {0x9E, 0, 0, 0, 0}, - {0x9F, 0x6, 0x6, 0, 0}, - {0xA0, 0x66, 0x66, 0, 0}, - {0xA1, 0x66, 0x66, 0, 0}, - {0xA2, 0x66, 0x66, 0, 0}, - {0xA3, 0x66, 0x66, 0, 0}, - {0xA4, 0x66, 0x66, 0, 0}, - {0xA5, 0x66, 0x66, 0, 0}, - {0xA6, 0x66, 0x66, 0, 0}, - {0xA7, 0x66, 0x66, 0, 0}, - {0xA8, 0x66, 0x66, 0, 0}, - {0xA9, 0x66, 0x66, 0, 0}, - {0xAA, 0x66, 0x66, 0, 0}, - {0xAB, 0x66, 0x66, 0, 0}, - {0xAC, 0x66, 0x66, 0, 0}, - {0xAD, 0x66, 0x66, 0, 0}, - {0xAE, 0x66, 0x66, 0, 0}, - {0xAF, 0x66, 0x66, 0, 0}, - {0xB0, 0x66, 0x66, 0, 0}, - {0xB1, 0x66, 0x66, 0, 0}, - {0xB2, 0x66, 0x66, 0, 0}, - {0xB3, 0xa, 0xa, 0, 0}, - {0xB4, 0, 0, 0, 0}, - {0xB5, 0, 0, 0, 0}, - {0xB6, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_TX_2056_rev6[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0x88, 0x88, 0, 0}, - {0x22, 0x88, 0x88, 0, 0}, - {0x23, 0x88, 0x88, 0, 0}, - {0x24, 0x88, 0x88, 0, 0}, - {0x25, 0xc, 0xc, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0x3, 0x3, 0, 0}, - {0x28, 0, 0, 0, 0}, - {0x29, 0x3, 0x3, 0, 0}, - {0x2A, 0x37, 0x37, 0, 0}, - {0x2B, 0x3, 0x3, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0x1, 0x1, 0, 0}, - {0x2F, 0x1, 0x1, 0, 0}, - {0x30, 0, 0, 0, 0}, - {0x31, 0, 0, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0x11, 0x11, 0, 0}, - {0x34, 0xee, 0xee, 1, 1}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0x3, 0x3, 0, 0}, - {0x38, 0x50, 0x50, 1, 1}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0x50, 0x50, 1, 1}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x6e, 0x6e, 0, 0}, - {0x3D, 0xf0, 0xf0, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x3, 0x3, 0, 0}, - {0x42, 0x3, 0x3, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x1e, 0x1e, 0, 0}, - {0x45, 0, 0, 0, 0}, - {0x46, 0x6e, 0x6e, 0, 0}, - {0x47, 0xf0, 0xf0, 1, 1}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x2, 0x2, 0, 0}, - {0x4A, 0xff, 0xff, 1, 1}, - {0x4B, 0xc, 0xc, 0, 0}, - {0x4C, 0, 0, 0, 0}, - {0x4D, 0x38, 0x38, 0, 0}, - {0x4E, 0x70, 0x70, 1, 1}, - {0x4F, 0x2, 0x2, 0, 0}, - {0x50, 0x88, 0x88, 0, 0}, - {0x51, 0xc, 0xc, 0, 0}, - {0x52, 0, 0, 0, 0}, - {0x53, 0x8, 0x8, 0, 0}, - {0x54, 0x70, 0x70, 1, 1}, - {0x55, 0x2, 0x2, 0, 0}, - {0x56, 0xff, 0xff, 1, 1}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x83, 0x83, 0, 0}, - {0x59, 0x77, 0x77, 1, 1}, - {0x5A, 0, 0, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x88, 0x88, 0, 0}, - {0x5D, 0, 0, 0, 0}, - {0x5E, 0x8, 0x8, 0, 0}, - {0x5F, 0x77, 0x77, 1, 1}, - {0x60, 0x1, 0x1, 0, 0}, - {0x61, 0, 0, 0, 0}, - {0x62, 0x7, 0x7, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0x7, 0x7, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 1, 1}, - {0x68, 0, 0, 0, 0}, - {0x69, 0xa, 0xa, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0x2, 0x2, 0, 0}, - {0x72, 0, 0, 0, 0}, - {0x73, 0, 0, 0, 0}, - {0x74, 0xe, 0xe, 0, 0}, - {0x75, 0xe, 0xe, 0, 0}, - {0x76, 0xe, 0xe, 0, 0}, - {0x77, 0x13, 0x13, 0, 0}, - {0x78, 0x13, 0x13, 0, 0}, - {0x79, 0x1b, 0x1b, 0, 0}, - {0x7A, 0x1b, 0x1b, 0, 0}, - {0x7B, 0x55, 0x55, 0, 0}, - {0x7C, 0x5b, 0x5b, 0, 0}, - {0x7D, 0x30, 0x30, 1, 1}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0x70, 0x70, 0, 0}, - {0x94, 0x70, 0x70, 0, 0}, - {0x95, 0x70, 0x70, 0, 0}, - {0x96, 0x70, 0x70, 0, 0}, - {0x97, 0x70, 0x70, 0, 0}, - {0x98, 0x70, 0x70, 0, 0}, - {0x99, 0x70, 0x70, 0, 0}, - {0x9A, 0x70, 0x70, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_RX_2056_rev6[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0x3, 0x3, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0, 0, 0, 0}, - {0x23, 0x90, 0x90, 0, 0}, - {0x24, 0x55, 0x55, 0, 0}, - {0x25, 0x15, 0x15, 0, 0}, - {0x26, 0x5, 0x5, 0, 0}, - {0x27, 0x15, 0x15, 0, 0}, - {0x28, 0x5, 0x5, 0, 0}, - {0x29, 0x20, 0x20, 0, 0}, - {0x2A, 0x11, 0x11, 0, 0}, - {0x2B, 0x90, 0x90, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0x88, 0x88, 0, 0}, - {0x2E, 0x32, 0x32, 0, 0}, - {0x2F, 0x77, 0x77, 0, 0}, - {0x30, 0x17, 0x17, 1, 1}, - {0x31, 0xff, 0xff, 1, 1}, - {0x32, 0x20, 0x20, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0x88, 0x88, 0, 0}, - {0x35, 0x32, 0x32, 0, 0}, - {0x36, 0x77, 0x77, 0, 0}, - {0x37, 0x17, 0x17, 1, 1}, - {0x38, 0xf0, 0xf0, 1, 1}, - {0x39, 0x20, 0x20, 0, 0}, - {0x3A, 0x8, 0x8, 0, 0}, - {0x3B, 0x55, 0x55, 1, 1}, - {0x3C, 0, 0, 0, 0}, - {0x3D, 0x88, 0x88, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0x44, 0x44, 0, 0}, - {0x40, 0x7, 0x7, 1, 1}, - {0x41, 0x6, 0x6, 0, 0}, - {0x42, 0x4, 0x4, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x8, 0x8, 0, 0}, - {0x45, 0x55, 0x55, 1, 1}, - {0x46, 0, 0, 0, 0}, - {0x47, 0x11, 0x11, 0, 0}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x44, 0x44, 0, 0}, - {0x4A, 0x7, 0x7, 0, 0}, - {0x4B, 0x6, 0x6, 0, 0}, - {0x4C, 0x4, 0x4, 0, 0}, - {0x4D, 0, 0, 0, 0}, - {0x4E, 0, 0, 0, 0}, - {0x4F, 0x26, 0x26, 1, 1}, - {0x50, 0x26, 0x26, 1, 1}, - {0x51, 0xf, 0xf, 1, 1}, - {0x52, 0xf, 0xf, 1, 1}, - {0x53, 0x44, 0x44, 0, 0}, - {0x54, 0, 0, 0, 0}, - {0x55, 0, 0, 0, 0}, - {0x56, 0x8, 0x8, 0, 0}, - {0x57, 0x8, 0x8, 0, 0}, - {0x58, 0x7, 0x7, 0, 0}, - {0x59, 0x22, 0x22, 0, 0}, - {0x5A, 0x22, 0x22, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x4, 0x4, 1, 1}, - {0x5D, 0x7, 0x7, 0, 0}, - {0x5E, 0x55, 0x55, 0, 0}, - {0x5F, 0x23, 0x23, 0, 0}, - {0x60, 0x41, 0x41, 0, 0}, - {0x61, 0x1, 0x1, 0, 0}, - {0x62, 0xa, 0xa, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0, 0, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0xc, 0xc, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0, 0, 0, 0}, - {0x72, 0x22, 0x22, 0, 0}, - {0x73, 0x22, 0x22, 0, 0}, - {0x74, 0, 0, 1, 1}, - {0x75, 0xa, 0xa, 0, 0}, - {0x76, 0x1, 0x1, 0, 0}, - {0x77, 0x22, 0x22, 0, 0}, - {0x78, 0x30, 0x30, 0, 0}, - {0x79, 0, 0, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0x5, 0x5, 1, 1}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0} -}; - -static struct radio_regs regs_SYN_2056_rev7[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0x1, 0x1, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0x60, 0x60, 0, 0}, - {0x23, 0x6, 0x6, 0, 0}, - {0x24, 0xc, 0xc, 0, 0}, - {0x25, 0, 0, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0, 0, 0, 0}, - {0x28, 0x1, 0x1, 0, 0}, - {0x29, 0, 0, 0, 0}, - {0x2A, 0, 0, 0, 0}, - {0x2B, 0, 0, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0, 0, 0, 0}, - {0x2F, 0x1f, 0x1f, 0, 0}, - {0x30, 0x15, 0x15, 0, 0}, - {0x31, 0xf, 0xf, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0, 0, 0, 0}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0, 0, 0, 0}, - {0x38, 0, 0, 0, 0}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0, 0, 0, 0}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x13, 0x13, 0, 0}, - {0x3D, 0xf, 0xf, 0, 0}, - {0x3E, 0x18, 0x18, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x20, 0x20, 0, 0}, - {0x42, 0x20, 0x20, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x77, 0x77, 0, 0}, - {0x45, 0x7, 0x7, 0, 0}, - {0x46, 0x1, 0x1, 0, 0}, - {0x47, 0x4, 0x4, 0, 0}, - {0x48, 0xf, 0xf, 0, 0}, - {0x49, 0x30, 0x30, 0, 0}, - {0x4A, 0x32, 0x32, 0, 0}, - {0x4B, 0xd, 0xd, 0, 0}, - {0x4C, 0xd, 0xd, 0, 0}, - {0x4D, 0x4, 0x4, 0, 0}, - {0x4E, 0x6, 0x6, 0, 0}, - {0x4F, 0x1, 0x1, 0, 0}, - {0x50, 0x1c, 0x1c, 0, 0}, - {0x51, 0x2, 0x2, 0, 0}, - {0x52, 0x2, 0x2, 0, 0}, - {0x53, 0xf7, 0xf7, 1, 1}, - {0x54, 0xb4, 0xb4, 0, 0}, - {0x55, 0xd2, 0xd2, 0, 0}, - {0x56, 0, 0, 0, 0}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x4, 0x4, 0, 0}, - {0x59, 0x96, 0x96, 0, 0}, - {0x5A, 0x3e, 0x3e, 0, 0}, - {0x5B, 0x3e, 0x3e, 0, 0}, - {0x5C, 0x13, 0x13, 0, 0}, - {0x5D, 0x2, 0x2, 0, 0}, - {0x5E, 0, 0, 0, 0}, - {0x5F, 0x7, 0x7, 0, 0}, - {0x60, 0x7, 0x7, 1, 1}, - {0x61, 0x8, 0x8, 0, 0}, - {0x62, 0x3, 0x3, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0x40, 0x40, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0x1, 0x1, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0x60, 0x60, 0, 0}, - {0x71, 0x66, 0x66, 0, 0}, - {0x72, 0xc, 0xc, 0, 0}, - {0x73, 0x66, 0x66, 0, 0}, - {0x74, 0x8f, 0x8f, 1, 1}, - {0x75, 0, 0, 0, 0}, - {0x76, 0xcc, 0xcc, 0, 0}, - {0x77, 0x1, 0x1, 0, 0}, - {0x78, 0x66, 0x66, 0, 0}, - {0x79, 0x66, 0x66, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0xff, 0xff, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0x95, 0, 0, 0, 0}, - {0x96, 0, 0, 0, 0}, - {0x97, 0, 0, 0, 0}, - {0x98, 0, 0, 0, 0}, - {0x99, 0, 0, 0, 0}, - {0x9A, 0, 0, 0, 0}, - {0x9B, 0, 0, 0, 0}, - {0x9C, 0, 0, 0, 0}, - {0x9D, 0, 0, 0, 0}, - {0x9E, 0, 0, 0, 0}, - {0x9F, 0x6, 0x6, 0, 0}, - {0xA0, 0x66, 0x66, 0, 0}, - {0xA1, 0x66, 0x66, 0, 0}, - {0xA2, 0x66, 0x66, 0, 0}, - {0xA3, 0x66, 0x66, 0, 0}, - {0xA4, 0x66, 0x66, 0, 0}, - {0xA5, 0x66, 0x66, 0, 0}, - {0xA6, 0x66, 0x66, 0, 0}, - {0xA7, 0x66, 0x66, 0, 0}, - {0xA8, 0x66, 0x66, 0, 0}, - {0xA9, 0x66, 0x66, 0, 0}, - {0xAA, 0x66, 0x66, 0, 0}, - {0xAB, 0x66, 0x66, 0, 0}, - {0xAC, 0x66, 0x66, 0, 0}, - {0xAD, 0x66, 0x66, 0, 0}, - {0xAE, 0x66, 0x66, 0, 0}, - {0xAF, 0x66, 0x66, 0, 0}, - {0xB0, 0x66, 0x66, 0, 0}, - {0xB1, 0x66, 0x66, 0, 0}, - {0xB2, 0x66, 0x66, 0, 0}, - {0xB3, 0xa, 0xa, 0, 0}, - {0xB4, 0, 0, 0, 0}, - {0xB5, 0, 0, 0, 0}, - {0xB6, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0}, -}; - -static struct radio_regs regs_TX_2056_rev7[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0x88, 0x88, 0, 0}, - {0x22, 0x88, 0x88, 0, 0}, - {0x23, 0x88, 0x88, 0, 0}, - {0x24, 0x88, 0x88, 0, 0}, - {0x25, 0xc, 0xc, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0x3, 0x3, 0, 0}, - {0x28, 0, 0, 0, 0}, - {0x29, 0x3, 0x3, 0, 0}, - {0x2A, 0x37, 0x37, 0, 0}, - {0x2B, 0x3, 0x3, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0x1, 0x1, 0, 0}, - {0x2F, 0x1, 0x1, 0, 0}, - {0x30, 0, 0, 0, 0}, - {0x31, 0, 0, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0x11, 0x11, 0, 0}, - {0x34, 0xee, 0xee, 1, 1}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0x3, 0x3, 0, 0}, - {0x38, 0x50, 0x50, 1, 1}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0x50, 0x50, 1, 1}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x6e, 0x6e, 0, 0}, - {0x3D, 0xf0, 0xf0, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x3, 0x3, 0, 0}, - {0x42, 0x3, 0x3, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x1e, 0x1e, 0, 0}, - {0x45, 0, 0, 0, 0}, - {0x46, 0x6e, 0x6e, 0, 0}, - {0x47, 0xf0, 0xf0, 1, 1}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x2, 0x2, 0, 0}, - {0x4A, 0xff, 0xff, 1, 1}, - {0x4B, 0xc, 0xc, 0, 0}, - {0x4C, 0, 0, 0, 0}, - {0x4D, 0x38, 0x38, 0, 0}, - {0x4E, 0x70, 0x70, 1, 1}, - {0x4F, 0x2, 0x2, 0, 0}, - {0x50, 0x88, 0x88, 0, 0}, - {0x51, 0xc, 0xc, 0, 0}, - {0x52, 0, 0, 0, 0}, - {0x53, 0x8, 0x8, 0, 0}, - {0x54, 0x70, 0x70, 1, 1}, - {0x55, 0x2, 0x2, 0, 0}, - {0x56, 0xff, 0xff, 1, 1}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x83, 0x83, 0, 0}, - {0x59, 0x77, 0x77, 1, 1}, - {0x5A, 0, 0, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x88, 0x88, 0, 0}, - {0x5D, 0, 0, 0, 0}, - {0x5E, 0x8, 0x8, 0, 0}, - {0x5F, 0x77, 0x77, 1, 1}, - {0x60, 0x1, 0x1, 0, 0}, - {0x61, 0, 0, 0, 0}, - {0x62, 0x7, 0x7, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0x7, 0x7, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 1, 1}, - {0x68, 0, 0, 0, 0}, - {0x69, 0xa, 0xa, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0x2, 0x2, 0, 0}, - {0x72, 0, 0, 0, 0}, - {0x73, 0, 0, 0, 0}, - {0x74, 0xe, 0xe, 0, 0}, - {0x75, 0xe, 0xe, 0, 0}, - {0x76, 0xe, 0xe, 0, 0}, - {0x77, 0x13, 0x13, 0, 0}, - {0x78, 0x13, 0x13, 0, 0}, - {0x79, 0x1b, 0x1b, 0, 0}, - {0x7A, 0x1b, 0x1b, 0, 0}, - {0x7B, 0x55, 0x55, 0, 0}, - {0x7C, 0x5b, 0x5b, 0, 0}, - {0x7D, 0x30, 0x30, 1, 1}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0x70, 0x70, 0, 0}, - {0x94, 0x70, 0x70, 0, 0}, - {0x95, 0x71, 0x71, 1, 1}, - {0x96, 0x71, 0x71, 1, 1}, - {0x97, 0x72, 0x72, 1, 1}, - {0x98, 0x73, 0x73, 1, 1}, - {0x99, 0x74, 0x74, 1, 1}, - {0x9A, 0x75, 0x75, 1, 1}, - {0xFFFF, 0, 0, 0, 0}, -}; - -static struct radio_regs regs_RX_2056_rev7[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0x3, 0x3, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0, 0, 0, 0}, - {0x23, 0x90, 0x90, 0, 0}, - {0x24, 0x55, 0x55, 0, 0}, - {0x25, 0x15, 0x15, 0, 0}, - {0x26, 0x5, 0x5, 0, 0}, - {0x27, 0x15, 0x15, 0, 0}, - {0x28, 0x5, 0x5, 0, 0}, - {0x29, 0x20, 0x20, 0, 0}, - {0x2A, 0x11, 0x11, 0, 0}, - {0x2B, 0x90, 0x90, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0x88, 0x88, 0, 0}, - {0x2E, 0x32, 0x32, 0, 0}, - {0x2F, 0x77, 0x77, 0, 0}, - {0x30, 0x17, 0x17, 1, 1}, - {0x31, 0xff, 0xff, 1, 1}, - {0x32, 0x20, 0x20, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0x88, 0x88, 0, 0}, - {0x35, 0x32, 0x32, 0, 0}, - {0x36, 0x77, 0x77, 0, 0}, - {0x37, 0x17, 0x17, 1, 1}, - {0x38, 0xf0, 0xf0, 1, 1}, - {0x39, 0x20, 0x20, 0, 0}, - {0x3A, 0x8, 0x8, 0, 0}, - {0x3B, 0x55, 0x55, 1, 1}, - {0x3C, 0, 0, 0, 0}, - {0x3D, 0x88, 0x88, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0, 0, 1, 1}, - {0x40, 0x7, 0x7, 1, 1}, - {0x41, 0x6, 0x6, 0, 0}, - {0x42, 0x4, 0x4, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x8, 0x8, 0, 0}, - {0x45, 0x55, 0x55, 1, 1}, - {0x46, 0, 0, 0, 0}, - {0x47, 0x11, 0x11, 0, 0}, - {0x48, 0, 0, 0, 0}, - {0x49, 0, 0, 1, 1}, - {0x4A, 0x7, 0x7, 0, 0}, - {0x4B, 0x6, 0x6, 0, 0}, - {0x4C, 0x4, 0x4, 0, 0}, - {0x4D, 0, 0, 0, 0}, - {0x4E, 0, 0, 0, 0}, - {0x4F, 0x26, 0x26, 1, 1}, - {0x50, 0x26, 0x26, 1, 1}, - {0x51, 0xf, 0xf, 1, 1}, - {0x52, 0xf, 0xf, 1, 1}, - {0x53, 0x44, 0x44, 0, 0}, - {0x54, 0, 0, 0, 0}, - {0x55, 0, 0, 0, 0}, - {0x56, 0x8, 0x8, 0, 0}, - {0x57, 0x8, 0x8, 0, 0}, - {0x58, 0x7, 0x7, 0, 0}, - {0x59, 0x22, 0x22, 0, 0}, - {0x5A, 0x22, 0x22, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x4, 0x4, 1, 1}, - {0x5D, 0x7, 0x7, 0, 0}, - {0x5E, 0x55, 0x55, 0, 0}, - {0x5F, 0x23, 0x23, 0, 0}, - {0x60, 0x41, 0x41, 0, 0}, - {0x61, 0x1, 0x1, 0, 0}, - {0x62, 0xa, 0xa, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0, 0, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0xc, 0xc, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0, 0, 0, 0}, - {0x72, 0x22, 0x22, 0, 0}, - {0x73, 0x22, 0x22, 0, 0}, - {0x74, 0, 0, 1, 1}, - {0x75, 0xa, 0xa, 0, 0}, - {0x76, 0x1, 0x1, 0, 0}, - {0x77, 0x22, 0x22, 0, 0}, - {0x78, 0x30, 0x30, 0, 0}, - {0x79, 0, 0, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0}, -}; - -static struct radio_regs regs_SYN_2056_rev8[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0x1, 0x1, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0x60, 0x60, 0, 0}, - {0x23, 0x6, 0x6, 0, 0}, - {0x24, 0xc, 0xc, 0, 0}, - {0x25, 0, 0, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0, 0, 0, 0}, - {0x28, 0x1, 0x1, 0, 0}, - {0x29, 0, 0, 0, 0}, - {0x2A, 0, 0, 0, 0}, - {0x2B, 0, 0, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0, 0, 0, 0}, - {0x2F, 0x1f, 0x1f, 0, 0}, - {0x30, 0x15, 0x15, 0, 0}, - {0x31, 0xf, 0xf, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0, 0, 0, 0}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0, 0, 0, 0}, - {0x38, 0, 0, 0, 0}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0, 0, 0, 0}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x13, 0x13, 0, 0}, - {0x3D, 0xf, 0xf, 0, 0}, - {0x3E, 0x18, 0x18, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x20, 0x20, 0, 0}, - {0x42, 0x20, 0x20, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x77, 0x77, 0, 0}, - {0x45, 0x7, 0x7, 0, 0}, - {0x46, 0x1, 0x1, 0, 0}, - {0x47, 0x4, 0x4, 0, 0}, - {0x48, 0xf, 0xf, 0, 0}, - {0x49, 0x30, 0x30, 0, 0}, - {0x4A, 0x32, 0x32, 0, 0}, - {0x4B, 0xd, 0xd, 0, 0}, - {0x4C, 0xd, 0xd, 0, 0}, - {0x4D, 0x4, 0x4, 0, 0}, - {0x4E, 0x6, 0x6, 0, 0}, - {0x4F, 0x1, 0x1, 0, 0}, - {0x50, 0x1c, 0x1c, 0, 0}, - {0x51, 0x2, 0x2, 0, 0}, - {0x52, 0x2, 0x2, 0, 0}, - {0x53, 0xf7, 0xf7, 1, 1}, - {0x54, 0xb4, 0xb4, 0, 0}, - {0x55, 0xd2, 0xd2, 0, 0}, - {0x56, 0, 0, 0, 0}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x4, 0x4, 0, 0}, - {0x59, 0x96, 0x96, 0, 0}, - {0x5A, 0x3e, 0x3e, 0, 0}, - {0x5B, 0x3e, 0x3e, 0, 0}, - {0x5C, 0x13, 0x13, 0, 0}, - {0x5D, 0x2, 0x2, 0, 0}, - {0x5E, 0, 0, 0, 0}, - {0x5F, 0x7, 0x7, 0, 0}, - {0x60, 0x7, 0x7, 1, 1}, - {0x61, 0x8, 0x8, 0, 0}, - {0x62, 0x3, 0x3, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0x40, 0x40, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0x1, 0x1, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0x60, 0x60, 0, 0}, - {0x71, 0x66, 0x66, 0, 0}, - {0x72, 0xc, 0xc, 0, 0}, - {0x73, 0x66, 0x66, 0, 0}, - {0x74, 0x8f, 0x8f, 1, 1}, - {0x75, 0, 0, 0, 0}, - {0x76, 0xcc, 0xcc, 0, 0}, - {0x77, 0x1, 0x1, 0, 0}, - {0x78, 0x66, 0x66, 0, 0}, - {0x79, 0x66, 0x66, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0xff, 0xff, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0x95, 0, 0, 0, 0}, - {0x96, 0, 0, 0, 0}, - {0x97, 0, 0, 0, 0}, - {0x98, 0, 0, 0, 0}, - {0x99, 0, 0, 0, 0}, - {0x9A, 0, 0, 0, 0}, - {0x9B, 0, 0, 0, 0}, - {0x9C, 0, 0, 0, 0}, - {0x9D, 0, 0, 0, 0}, - {0x9E, 0, 0, 0, 0}, - {0x9F, 0x6, 0x6, 0, 0}, - {0xA0, 0x66, 0x66, 0, 0}, - {0xA1, 0x66, 0x66, 0, 0}, - {0xA2, 0x66, 0x66, 0, 0}, - {0xA3, 0x66, 0x66, 0, 0}, - {0xA4, 0x66, 0x66, 0, 0}, - {0xA5, 0x66, 0x66, 0, 0}, - {0xA6, 0x66, 0x66, 0, 0}, - {0xA7, 0x66, 0x66, 0, 0}, - {0xA8, 0x66, 0x66, 0, 0}, - {0xA9, 0x66, 0x66, 0, 0}, - {0xAA, 0x66, 0x66, 0, 0}, - {0xAB, 0x66, 0x66, 0, 0}, - {0xAC, 0x66, 0x66, 0, 0}, - {0xAD, 0x66, 0x66, 0, 0}, - {0xAE, 0x66, 0x66, 0, 0}, - {0xAF, 0x66, 0x66, 0, 0}, - {0xB0, 0x66, 0x66, 0, 0}, - {0xB1, 0x66, 0x66, 0, 0}, - {0xB2, 0x66, 0x66, 0, 0}, - {0xB3, 0xa, 0xa, 0, 0}, - {0xB4, 0, 0, 0, 0}, - {0xB5, 0, 0, 0, 0}, - {0xB6, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0}, -}; - -static struct radio_regs regs_TX_2056_rev8[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0x88, 0x88, 0, 0}, - {0x22, 0x88, 0x88, 0, 0}, - {0x23, 0x88, 0x88, 0, 0}, - {0x24, 0x88, 0x88, 0, 0}, - {0x25, 0xc, 0xc, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0x3, 0x3, 0, 0}, - {0x28, 0, 0, 0, 0}, - {0x29, 0x3, 0x3, 0, 0}, - {0x2A, 0x37, 0x37, 0, 0}, - {0x2B, 0x3, 0x3, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0x1, 0x1, 0, 0}, - {0x2F, 0x1, 0x1, 0, 0}, - {0x30, 0, 0, 0, 0}, - {0x31, 0, 0, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0x11, 0x11, 0, 0}, - {0x34, 0xee, 0xee, 1, 1}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0x3, 0x3, 0, 0}, - {0x38, 0x50, 0x50, 1, 1}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0x50, 0x50, 1, 1}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x6e, 0x6e, 0, 0}, - {0x3D, 0xf0, 0xf0, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x3, 0x3, 0, 0}, - {0x42, 0x3, 0x3, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x1e, 0x1e, 0, 0}, - {0x45, 0, 0, 0, 0}, - {0x46, 0x6e, 0x6e, 0, 0}, - {0x47, 0xf0, 0xf0, 1, 1}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x2, 0x2, 0, 0}, - {0x4A, 0xff, 0xff, 1, 1}, - {0x4B, 0xc, 0xc, 0, 0}, - {0x4C, 0, 0, 0, 0}, - {0x4D, 0x38, 0x38, 0, 0}, - {0x4E, 0x70, 0x70, 1, 1}, - {0x4F, 0x2, 0x2, 0, 0}, - {0x50, 0x88, 0x88, 0, 0}, - {0x51, 0xc, 0xc, 0, 0}, - {0x52, 0, 0, 0, 0}, - {0x53, 0x8, 0x8, 0, 0}, - {0x54, 0x70, 0x70, 1, 1}, - {0x55, 0x2, 0x2, 0, 0}, - {0x56, 0xff, 0xff, 1, 1}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x83, 0x83, 0, 0}, - {0x59, 0x77, 0x77, 1, 1}, - {0x5A, 0, 0, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x88, 0x88, 0, 0}, - {0x5D, 0, 0, 0, 0}, - {0x5E, 0x8, 0x8, 0, 0}, - {0x5F, 0x77, 0x77, 1, 1}, - {0x60, 0x1, 0x1, 0, 0}, - {0x61, 0, 0, 0, 0}, - {0x62, 0x7, 0x7, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0x7, 0x7, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 1, 1}, - {0x68, 0, 0, 0, 0}, - {0x69, 0xa, 0xa, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0x2, 0x2, 0, 0}, - {0x72, 0, 0, 0, 0}, - {0x73, 0, 0, 0, 0}, - {0x74, 0xe, 0xe, 0, 0}, - {0x75, 0xe, 0xe, 0, 0}, - {0x76, 0xe, 0xe, 0, 0}, - {0x77, 0x13, 0x13, 0, 0}, - {0x78, 0x13, 0x13, 0, 0}, - {0x79, 0x1b, 0x1b, 0, 0}, - {0x7A, 0x1b, 0x1b, 0, 0}, - {0x7B, 0x55, 0x55, 0, 0}, - {0x7C, 0x5b, 0x5b, 0, 0}, - {0x7D, 0x30, 0x30, 1, 1}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0x70, 0x70, 0, 0}, - {0x94, 0x70, 0x70, 0, 0}, - {0x95, 0x70, 0x70, 0, 0}, - {0x96, 0x70, 0x70, 0, 0}, - {0x97, 0x70, 0x70, 0, 0}, - {0x98, 0x70, 0x70, 0, 0}, - {0x99, 0x70, 0x70, 0, 0}, - {0x9A, 0x70, 0x70, 0, 0}, - {0xFFFF, 0, 0, 0, 0}, -}; - -static struct radio_regs regs_RX_2056_rev8[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0x3, 0x3, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0, 0, 0, 0}, - {0x23, 0x90, 0x90, 0, 0}, - {0x24, 0x55, 0x55, 0, 0}, - {0x25, 0x15, 0x15, 0, 0}, - {0x26, 0x5, 0x5, 0, 0}, - {0x27, 0x15, 0x15, 0, 0}, - {0x28, 0x5, 0x5, 0, 0}, - {0x29, 0x20, 0x20, 0, 0}, - {0x2A, 0x11, 0x11, 0, 0}, - {0x2B, 0x90, 0x90, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0x88, 0x88, 0, 0}, - {0x2E, 0x32, 0x32, 0, 0}, - {0x2F, 0x77, 0x77, 0, 0}, - {0x30, 0x17, 0x17, 1, 1}, - {0x31, 0xff, 0xff, 1, 1}, - {0x32, 0x20, 0x20, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0x88, 0x88, 0, 0}, - {0x35, 0x32, 0x32, 0, 0}, - {0x36, 0x77, 0x77, 0, 0}, - {0x37, 0x17, 0x17, 1, 1}, - {0x38, 0xf0, 0xf0, 1, 1}, - {0x39, 0x20, 0x20, 0, 0}, - {0x3A, 0x8, 0x8, 0, 0}, - {0x3B, 0x55, 0x55, 1, 1}, - {0x3C, 0, 0, 0, 0}, - {0x3D, 0x88, 0x88, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0x44, 0x44, 0, 0}, - {0x40, 0x7, 0x7, 1, 1}, - {0x41, 0x6, 0x6, 0, 0}, - {0x42, 0x4, 0x4, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x8, 0x8, 0, 0}, - {0x45, 0x55, 0x55, 1, 1}, - {0x46, 0, 0, 0, 0}, - {0x47, 0x11, 0x11, 0, 0}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x44, 0x44, 0, 0}, - {0x4A, 0x7, 0x7, 0, 0}, - {0x4B, 0x6, 0x6, 0, 0}, - {0x4C, 0x4, 0x4, 0, 0}, - {0x4D, 0, 0, 0, 0}, - {0x4E, 0, 0, 0, 0}, - {0x4F, 0x26, 0x26, 1, 1}, - {0x50, 0x26, 0x26, 1, 1}, - {0x51, 0xf, 0xf, 1, 1}, - {0x52, 0xf, 0xf, 1, 1}, - {0x53, 0x44, 0x44, 0, 0}, - {0x54, 0, 0, 0, 0}, - {0x55, 0, 0, 0, 0}, - {0x56, 0x8, 0x8, 0, 0}, - {0x57, 0x8, 0x8, 0, 0}, - {0x58, 0x7, 0x7, 0, 0}, - {0x59, 0x22, 0x22, 0, 0}, - {0x5A, 0x22, 0x22, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x4, 0x4, 1, 1}, - {0x5D, 0x7, 0x7, 0, 0}, - {0x5E, 0x55, 0x55, 0, 0}, - {0x5F, 0x23, 0x23, 0, 0}, - {0x60, 0x41, 0x41, 0, 0}, - {0x61, 0x1, 0x1, 0, 0}, - {0x62, 0xa, 0xa, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0, 0, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0xc, 0xc, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0, 0, 0, 0}, - {0x72, 0x22, 0x22, 0, 0}, - {0x73, 0x22, 0x22, 0, 0}, - {0x74, 0, 0, 1, 1}, - {0x75, 0xa, 0xa, 0, 0}, - {0x76, 0x1, 0x1, 0, 0}, - {0x77, 0x22, 0x22, 0, 0}, - {0x78, 0x30, 0x30, 0, 0}, - {0x79, 0, 0, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0x5, 0x5, 1, 1}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0}, -}; - -static const struct radio_regs regs_SYN_2056_rev11[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0x1, 0x1, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0x60, 0x60, 0, 0}, - {0x23, 0x6, 0x6, 0, 0}, - {0x24, 0xc, 0xc, 0, 0}, - {0x25, 0, 0, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0, 0, 0, 0}, - {0x28, 0x1, 0x1, 0, 0}, - {0x29, 0, 0, 0, 0}, - {0x2A, 0, 0, 0, 0}, - {0x2B, 0, 0, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0, 0, 0, 0}, - {0x2F, 0x1f, 0x1f, 0, 0}, - {0x30, 0x15, 0x15, 0, 0}, - {0x31, 0xf, 0xf, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0, 0, 0, 0}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0, 0, 0, 0}, - {0x38, 0, 0, 0, 0}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0, 0, 0, 0}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x13, 0x13, 0, 0}, - {0x3D, 0xf, 0xf, 0, 0}, - {0x3E, 0x18, 0x18, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x20, 0x20, 0, 0}, - {0x42, 0x20, 0x20, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x77, 0x77, 0, 0}, - {0x45, 0x7, 0x7, 0, 0}, - {0x46, 0x1, 0x1, 0, 0}, - {0x47, 0x6, 0x6, 1, 1}, - {0x48, 0xf, 0xf, 0, 0}, - {0x49, 0x3f, 0x3f, 1, 1}, - {0x4A, 0x32, 0x32, 0, 0}, - {0x4B, 0x6, 0x6, 1, 1}, - {0x4C, 0x6, 0x6, 1, 1}, - {0x4D, 0x4, 0x4, 0, 0}, - {0x4E, 0x2b, 0x2b, 1, 1}, - {0x4F, 0x1, 0x1, 0, 0}, - {0x50, 0x1c, 0x1c, 0, 0}, - {0x51, 0x2, 0x2, 0, 0}, - {0x52, 0x2, 0x2, 0, 0}, - {0x53, 0xf7, 0xf7, 1, 1}, - {0x54, 0xb4, 0xb4, 0, 0}, - {0x55, 0xd2, 0xd2, 0, 0}, - {0x56, 0, 0, 0, 0}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x4, 0x4, 0, 0}, - {0x59, 0x96, 0x96, 0, 0}, - {0x5A, 0x3e, 0x3e, 0, 0}, - {0x5B, 0x3e, 0x3e, 0, 0}, - {0x5C, 0x13, 0x13, 0, 0}, - {0x5D, 0x2, 0x2, 0, 0}, - {0x5E, 0, 0, 0, 0}, - {0x5F, 0x7, 0x7, 0, 0}, - {0x60, 0x7, 0x7, 1, 1}, - {0x61, 0x8, 0x8, 0, 0}, - {0x62, 0x3, 0x3, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0x40, 0x40, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0x1, 0x1, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0x60, 0x60, 0, 0}, - {0x71, 0x66, 0x66, 0, 0}, - {0x72, 0xc, 0xc, 0, 0}, - {0x73, 0x66, 0x66, 0, 0}, - {0x74, 0x8f, 0x8f, 1, 1}, - {0x75, 0, 0, 0, 0}, - {0x76, 0xcc, 0xcc, 0, 0}, - {0x77, 0x1, 0x1, 0, 0}, - {0x78, 0x66, 0x66, 0, 0}, - {0x79, 0x66, 0x66, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0, 0, 0, 0}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0xff, 0xff, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0x95, 0, 0, 0, 0}, - {0x96, 0, 0, 0, 0}, - {0x97, 0, 0, 0, 0}, - {0x98, 0, 0, 0, 0}, - {0x99, 0, 0, 0, 0}, - {0x9A, 0, 0, 0, 0}, - {0x9B, 0, 0, 0, 0}, - {0x9C, 0, 0, 0, 0}, - {0x9D, 0, 0, 0, 0}, - {0x9E, 0, 0, 0, 0}, - {0x9F, 0x6, 0x6, 0, 0}, - {0xA0, 0x66, 0x66, 0, 0}, - {0xA1, 0x66, 0x66, 0, 0}, - {0xA2, 0x66, 0x66, 0, 0}, - {0xA3, 0x66, 0x66, 0, 0}, - {0xA4, 0x66, 0x66, 0, 0}, - {0xA5, 0x66, 0x66, 0, 0}, - {0xA6, 0x66, 0x66, 0, 0}, - {0xA7, 0x66, 0x66, 0, 0}, - {0xA8, 0x66, 0x66, 0, 0}, - {0xA9, 0x66, 0x66, 0, 0}, - {0xAA, 0x66, 0x66, 0, 0}, - {0xAB, 0x66, 0x66, 0, 0}, - {0xAC, 0x66, 0x66, 0, 0}, - {0xAD, 0x66, 0x66, 0, 0}, - {0xAE, 0x66, 0x66, 0, 0}, - {0xAF, 0x66, 0x66, 0, 0}, - {0xB0, 0x66, 0x66, 0, 0}, - {0xB1, 0x66, 0x66, 0, 0}, - {0xB2, 0x66, 0x66, 0, 0}, - {0xB3, 0xa, 0xa, 0, 0}, - {0xB4, 0, 0, 0, 0}, - {0xB5, 0, 0, 0, 0}, - {0xB6, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0}, -}; - -static const struct radio_regs regs_TX_2056_rev11[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0, 0, 0, 0}, - {0x21, 0x88, 0x88, 0, 0}, - {0x22, 0x88, 0x88, 0, 0}, - {0x23, 0x88, 0x88, 0, 0}, - {0x24, 0x88, 0x88, 0, 0}, - {0x25, 0xc, 0xc, 0, 0}, - {0x26, 0, 0, 0, 0}, - {0x27, 0x3, 0x3, 0, 0}, - {0x28, 0, 0, 0, 0}, - {0x29, 0x3, 0x3, 0, 0}, - {0x2A, 0x37, 0x37, 0, 0}, - {0x2B, 0x3, 0x3, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0, 0, 0, 0}, - {0x2E, 0x1, 0x1, 0, 0}, - {0x2F, 0x1, 0x1, 0, 0}, - {0x30, 0, 0, 0, 0}, - {0x31, 0, 0, 0, 0}, - {0x32, 0, 0, 0, 0}, - {0x33, 0x11, 0x11, 0, 0}, - {0x34, 0xee, 0xee, 1, 1}, - {0x35, 0, 0, 0, 0}, - {0x36, 0, 0, 0, 0}, - {0x37, 0x3, 0x3, 0, 0}, - {0x38, 0x50, 0x50, 1, 1}, - {0x39, 0, 0, 0, 0}, - {0x3A, 0x50, 0x50, 1, 1}, - {0x3B, 0, 0, 0, 0}, - {0x3C, 0x6e, 0x6e, 0, 0}, - {0x3D, 0xf0, 0xf0, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0, 0, 0, 0}, - {0x40, 0, 0, 0, 0}, - {0x41, 0x3, 0x3, 0, 0}, - {0x42, 0x3, 0x3, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x1e, 0x1e, 0, 0}, - {0x45, 0, 0, 0, 0}, - {0x46, 0x6e, 0x6e, 0, 0}, - {0x47, 0xf0, 0xf0, 1, 1}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x2, 0x2, 0, 0}, - {0x4A, 0xff, 0xff, 1, 1}, - {0x4B, 0xc, 0xc, 0, 0}, - {0x4C, 0, 0, 0, 0}, - {0x4D, 0x38, 0x38, 0, 0}, - {0x4E, 0x70, 0x70, 1, 1}, - {0x4F, 0x2, 0x2, 0, 0}, - {0x50, 0x88, 0x88, 0, 0}, - {0x51, 0xc, 0xc, 0, 0}, - {0x52, 0, 0, 0, 0}, - {0x53, 0x8, 0x8, 0, 0}, - {0x54, 0x70, 0x70, 1, 1}, - {0x55, 0x2, 0x2, 0, 0}, - {0x56, 0xff, 0xff, 1, 1}, - {0x57, 0, 0, 0, 0}, - {0x58, 0x83, 0x83, 0, 0}, - {0x59, 0x77, 0x77, 1, 1}, - {0x5A, 0, 0, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x88, 0x88, 0, 0}, - {0x5D, 0, 0, 0, 0}, - {0x5E, 0x8, 0x8, 0, 0}, - {0x5F, 0x77, 0x77, 1, 1}, - {0x60, 0x1, 0x1, 0, 0}, - {0x61, 0, 0, 0, 0}, - {0x62, 0x7, 0x7, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0x7, 0x7, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 1, 1}, - {0x68, 0, 0, 0, 0}, - {0x69, 0xa, 0xa, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0, 0, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0x2, 0x2, 0, 0}, - {0x72, 0, 0, 0, 0}, - {0x73, 0, 0, 0, 0}, - {0x74, 0xe, 0xe, 0, 0}, - {0x75, 0xe, 0xe, 0, 0}, - {0x76, 0xe, 0xe, 0, 0}, - {0x77, 0x13, 0x13, 0, 0}, - {0x78, 0x13, 0x13, 0, 0}, - {0x79, 0x1b, 0x1b, 0, 0}, - {0x7A, 0x1b, 0x1b, 0, 0}, - {0x7B, 0x55, 0x55, 0, 0}, - {0x7C, 0x5b, 0x5b, 0, 0}, - {0x7D, 0x30, 0x30, 1, 1}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0x70, 0x70, 0, 0}, - {0x94, 0x70, 0x70, 0, 0}, - {0x95, 0x70, 0x70, 0, 0}, - {0x96, 0x70, 0x70, 0, 0}, - {0x97, 0x70, 0x70, 0, 0}, - {0x98, 0x70, 0x70, 0, 0}, - {0x99, 0x70, 0x70, 0, 0}, - {0x9A, 0x70, 0x70, 0, 0}, - {0xFFFF, 0, 0, 0, 0}, -}; - -static const struct radio_regs regs_RX_2056_rev11[] = { - {0x02, 0, 0, 0, 0}, - {0x03, 0, 0, 0, 0}, - {0x04, 0, 0, 0, 0}, - {0x05, 0, 0, 0, 0}, - {0x06, 0, 0, 0, 0}, - {0x07, 0, 0, 0, 0}, - {0x08, 0, 0, 0, 0}, - {0x09, 0, 0, 0, 0}, - {0x0A, 0, 0, 0, 0}, - {0x0B, 0, 0, 0, 0}, - {0x0C, 0, 0, 0, 0}, - {0x0D, 0, 0, 0, 0}, - {0x0E, 0, 0, 0, 0}, - {0x0F, 0, 0, 0, 0}, - {0x10, 0, 0, 0, 0}, - {0x11, 0, 0, 0, 0}, - {0x12, 0, 0, 0, 0}, - {0x13, 0, 0, 0, 0}, - {0x14, 0, 0, 0, 0}, - {0x15, 0, 0, 0, 0}, - {0x16, 0, 0, 0, 0}, - {0x17, 0, 0, 0, 0}, - {0x18, 0, 0, 0, 0}, - {0x19, 0, 0, 0, 0}, - {0x1A, 0, 0, 0, 0}, - {0x1B, 0, 0, 0, 0}, - {0x1C, 0, 0, 0, 0}, - {0x1D, 0, 0, 0, 0}, - {0x1E, 0, 0, 0, 0}, - {0x1F, 0, 0, 0, 0}, - {0x20, 0x3, 0x3, 0, 0}, - {0x21, 0, 0, 0, 0}, - {0x22, 0, 0, 0, 0}, - {0x23, 0x90, 0x90, 0, 0}, - {0x24, 0x55, 0x55, 0, 0}, - {0x25, 0x15, 0x15, 0, 0}, - {0x26, 0x5, 0x5, 0, 0}, - {0x27, 0x15, 0x15, 0, 0}, - {0x28, 0x5, 0x5, 0, 0}, - {0x29, 0x20, 0x20, 0, 0}, - {0x2A, 0x11, 0x11, 0, 0}, - {0x2B, 0x90, 0x90, 0, 0}, - {0x2C, 0, 0, 0, 0}, - {0x2D, 0x88, 0x88, 0, 0}, - {0x2E, 0x32, 0x32, 0, 0}, - {0x2F, 0x77, 0x77, 0, 0}, - {0x30, 0x17, 0x17, 1, 1}, - {0x31, 0xff, 0xff, 1, 1}, - {0x32, 0x20, 0x20, 0, 0}, - {0x33, 0, 0, 0, 0}, - {0x34, 0x88, 0x88, 0, 0}, - {0x35, 0x32, 0x32, 0, 0}, - {0x36, 0x77, 0x77, 0, 0}, - {0x37, 0x17, 0x17, 1, 1}, - {0x38, 0xf0, 0xf0, 1, 1}, - {0x39, 0x20, 0x20, 0, 0}, - {0x3A, 0x8, 0x8, 0, 0}, - {0x3B, 0x55, 0x55, 1, 1}, - {0x3C, 0, 0, 0, 0}, - {0x3D, 0x88, 0x88, 1, 1}, - {0x3E, 0, 0, 0, 0}, - {0x3F, 0x44, 0x44, 0, 0}, - {0x40, 0x7, 0x7, 1, 1}, - {0x41, 0x6, 0x6, 0, 0}, - {0x42, 0x4, 0x4, 0, 0}, - {0x43, 0, 0, 0, 0}, - {0x44, 0x8, 0x8, 0, 0}, - {0x45, 0x55, 0x55, 1, 1}, - {0x46, 0, 0, 0, 0}, - {0x47, 0x11, 0x11, 0, 0}, - {0x48, 0, 0, 0, 0}, - {0x49, 0x44, 0x44, 0, 0}, - {0x4A, 0x7, 0x7, 0, 0}, - {0x4B, 0x6, 0x6, 0, 0}, - {0x4C, 0x4, 0x4, 0, 0}, - {0x4D, 0, 0, 0, 0}, - {0x4E, 0, 0, 0, 0}, - {0x4F, 0x26, 0x26, 1, 1}, - {0x50, 0x26, 0x26, 1, 1}, - {0x51, 0xf, 0xf, 1, 1}, - {0x52, 0xf, 0xf, 1, 1}, - {0x53, 0x44, 0x44, 0, 0}, - {0x54, 0, 0, 0, 0}, - {0x55, 0, 0, 0, 0}, - {0x56, 0x8, 0x8, 0, 0}, - {0x57, 0x8, 0x8, 0, 0}, - {0x58, 0x7, 0x7, 0, 0}, - {0x59, 0x22, 0x22, 0, 0}, - {0x5A, 0x22, 0x22, 0, 0}, - {0x5B, 0x2, 0x2, 0, 0}, - {0x5C, 0x4, 0x4, 1, 1}, - {0x5D, 0x7, 0x7, 0, 0}, - {0x5E, 0x55, 0x55, 0, 0}, - {0x5F, 0x23, 0x23, 0, 0}, - {0x60, 0x41, 0x41, 0, 0}, - {0x61, 0x1, 0x1, 0, 0}, - {0x62, 0xa, 0xa, 0, 0}, - {0x63, 0, 0, 0, 0}, - {0x64, 0, 0, 0, 0}, - {0x65, 0, 0, 0, 0}, - {0x66, 0, 0, 0, 0}, - {0x67, 0, 0, 0, 0}, - {0x68, 0, 0, 0, 0}, - {0x69, 0, 0, 0, 0}, - {0x6A, 0, 0, 0, 0}, - {0x6B, 0xc, 0xc, 0, 0}, - {0x6C, 0, 0, 0, 0}, - {0x6D, 0, 0, 0, 0}, - {0x6E, 0, 0, 0, 0}, - {0x6F, 0, 0, 0, 0}, - {0x70, 0, 0, 0, 0}, - {0x71, 0, 0, 0, 0}, - {0x72, 0x22, 0x22, 0, 0}, - {0x73, 0x22, 0x22, 0, 0}, - {0x74, 0, 0, 1, 1}, - {0x75, 0xa, 0xa, 0, 0}, - {0x76, 0x1, 0x1, 0, 0}, - {0x77, 0x22, 0x22, 0, 0}, - {0x78, 0x30, 0x30, 0, 0}, - {0x79, 0, 0, 0, 0}, - {0x7A, 0, 0, 0, 0}, - {0x7B, 0, 0, 0, 0}, - {0x7C, 0, 0, 0, 0}, - {0x7D, 0x5, 0x5, 1, 1}, - {0x7E, 0, 0, 0, 0}, - {0x7F, 0, 0, 0, 0}, - {0x80, 0, 0, 0, 0}, - {0x81, 0, 0, 0, 0}, - {0x82, 0, 0, 0, 0}, - {0x83, 0, 0, 0, 0}, - {0x84, 0, 0, 0, 0}, - {0x85, 0, 0, 0, 0}, - {0x86, 0, 0, 0, 0}, - {0x87, 0, 0, 0, 0}, - {0x88, 0, 0, 0, 0}, - {0x89, 0, 0, 0, 0}, - {0x8A, 0, 0, 0, 0}, - {0x8B, 0, 0, 0, 0}, - {0x8C, 0, 0, 0, 0}, - {0x8D, 0, 0, 0, 0}, - {0x8E, 0, 0, 0, 0}, - {0x8F, 0, 0, 0, 0}, - {0x90, 0, 0, 0, 0}, - {0x91, 0, 0, 0, 0}, - {0x92, 0, 0, 0, 0}, - {0x93, 0, 0, 0, 0}, - {0x94, 0, 0, 0, 0}, - {0xFFFF, 0, 0, 0, 0}, -}; - -static struct radio_20xx_regs regs_2057_rev4[] = { - {0x00, 0x84, 0}, - {0x01, 0, 0}, - {0x02, 0x60, 0}, - {0x03, 0x1f, 0}, - {0x04, 0x4, 0}, - {0x05, 0x2, 0}, - {0x06, 0x1, 0}, - {0x07, 0x1, 0}, - {0x08, 0x1, 0}, - {0x09, 0x69, 0}, - {0x0A, 0x66, 0}, - {0x0B, 0x6, 0}, - {0x0C, 0x18, 0}, - {0x0D, 0x3, 0}, - {0x0E, 0x20, 1}, - {0x0F, 0x20, 0}, - {0x10, 0, 0}, - {0x11, 0x7c, 0}, - {0x12, 0x42, 0}, - {0x13, 0xbd, 0}, - {0x14, 0x7, 0}, - {0x15, 0xf7, 0}, - {0x16, 0x8, 0}, - {0x17, 0x17, 0}, - {0x18, 0x7, 0}, - {0x19, 0, 0}, - {0x1A, 0x2, 0}, - {0x1B, 0x13, 0}, - {0x1C, 0x3e, 0}, - {0x1D, 0x3e, 0}, - {0x1E, 0x96, 0}, - {0x1F, 0x4, 0}, - {0x20, 0, 0}, - {0x21, 0, 0}, - {0x22, 0x17, 0}, - {0x23, 0x4, 0}, - {0x24, 0x1, 0}, - {0x25, 0x6, 0}, - {0x26, 0x4, 0}, - {0x27, 0xd, 0}, - {0x28, 0xd, 0}, - {0x29, 0x30, 0}, - {0x2A, 0x32, 0}, - {0x2B, 0x8, 0}, - {0x2C, 0x1c, 0}, - {0x2D, 0x2, 0}, - {0x2E, 0x4, 0}, - {0x2F, 0x7f, 0}, - {0x30, 0x27, 0}, - {0x31, 0, 1}, - {0x32, 0, 1}, - {0x33, 0, 1}, - {0x34, 0, 0}, - {0x35, 0x26, 1}, - {0x36, 0x18, 0}, - {0x37, 0x7, 0}, - {0x38, 0x66, 0}, - {0x39, 0x66, 0}, - {0x3A, 0x66, 0}, - {0x3B, 0x66, 0}, - {0x3C, 0xff, 1}, - {0x3D, 0xff, 1}, - {0x3E, 0xff, 1}, - {0x3F, 0xff, 1}, - {0x40, 0x16, 0}, - {0x41, 0x7, 0}, - {0x42, 0x19, 0}, - {0x43, 0x7, 0}, - {0x44, 0x6, 0}, - {0x45, 0x3, 0}, - {0x46, 0x1, 0}, - {0x47, 0x7, 0}, - {0x48, 0x33, 0}, - {0x49, 0x5, 0}, - {0x4A, 0x77, 0}, - {0x4B, 0x66, 0}, - {0x4C, 0x66, 0}, - {0x4D, 0, 0}, - {0x4E, 0x4, 0}, - {0x4F, 0xc, 0}, - {0x50, 0, 0}, - {0x51, 0x75, 0}, - {0x56, 0x7, 0}, - {0x57, 0, 0}, - {0x58, 0, 0}, - {0x59, 0xa8, 0}, - {0x5A, 0, 0}, - {0x5B, 0x1f, 0}, - {0x5C, 0x30, 0}, - {0x5D, 0x1, 0}, - {0x5E, 0x30, 0}, - {0x5F, 0x70, 0}, - {0x60, 0, 0}, - {0x61, 0, 0}, - {0x62, 0x33, 1}, - {0x63, 0x19, 0}, - {0x64, 0x62, 0}, - {0x65, 0, 0}, - {0x66, 0x11, 0}, - {0x69, 0, 0}, - {0x6A, 0x7e, 0}, - {0x6B, 0x3f, 0}, - {0x6C, 0x7f, 0}, - {0x6D, 0x78, 0}, - {0x6E, 0xc8, 0}, - {0x6F, 0x88, 0}, - {0x70, 0x8, 0}, - {0x71, 0xf, 0}, - {0x72, 0xbc, 0}, - {0x73, 0x8, 0}, - {0x74, 0x60, 0}, - {0x75, 0x1e, 0}, - {0x76, 0x70, 0}, - {0x77, 0, 0}, - {0x78, 0, 0}, - {0x79, 0, 0}, - {0x7A, 0x33, 0}, - {0x7B, 0x1e, 0}, - {0x7C, 0x62, 0}, - {0x7D, 0x11, 0}, - {0x80, 0x3c, 0}, - {0x81, 0x9c, 0}, - {0x82, 0xa, 0}, - {0x83, 0x9d, 0}, - {0x84, 0xa, 0}, - {0x85, 0, 0}, - {0x86, 0x40, 0}, - {0x87, 0x40, 0}, - {0x88, 0x88, 0}, - {0x89, 0x10, 0}, - {0x8A, 0xf0, 1}, - {0x8B, 0x10, 1}, - {0x8C, 0xf0, 1}, - {0x8D, 0, 0}, - {0x8E, 0, 0}, - {0x8F, 0x10, 0}, - {0x90, 0x55, 0}, - {0x91, 0x3f, 1}, - {0x92, 0x36, 1}, - {0x93, 0, 0}, - {0x94, 0, 0}, - {0x95, 0, 0}, - {0x96, 0x87, 0}, - {0x97, 0x11, 0}, - {0x98, 0, 0}, - {0x99, 0x33, 0}, - {0x9A, 0x88, 0}, - {0x9B, 0, 0}, - {0x9C, 0x87, 0}, - {0x9D, 0x11, 0}, - {0x9E, 0, 0}, - {0x9F, 0x33, 0}, - {0xA0, 0x88, 0}, - {0xA1, 0xe1, 0}, - {0xA2, 0x3f, 0}, - {0xA3, 0x44, 0}, - {0xA4, 0x8c, 1}, - {0xA5, 0x6d, 0}, - {0xA6, 0x22, 0}, - {0xA7, 0xbe, 0}, - {0xA8, 0x55, 1}, - {0xA9, 0xc, 0}, - {0xAA, 0xc, 0}, - {0xAB, 0xaa, 0}, - {0xAC, 0x2, 0}, - {0xAD, 0, 0}, - {0xAE, 0x10, 0}, - {0xAF, 0x1, 1}, - {0xB0, 0, 0}, - {0xB1, 0, 0}, - {0xB2, 0x80, 0}, - {0xB3, 0x60, 0}, - {0xB4, 0x44, 0}, - {0xB5, 0x55, 0}, - {0xB6, 0x1, 0}, - {0xB7, 0x55, 0}, - {0xB8, 0x1, 0}, - {0xB9, 0x5, 0}, - {0xBA, 0x55, 0}, - {0xBB, 0x55, 0}, - {0xC1, 0, 0}, - {0xC2, 0, 0}, - {0xC3, 0, 0}, - {0xC4, 0, 0}, - {0xC5, 0, 0}, - {0xC6, 0, 0}, - {0xC7, 0, 0}, - {0xC8, 0, 0}, - {0xC9, 0, 0}, - {0xCA, 0, 0}, - {0xCB, 0, 0}, - {0xCC, 0, 0}, - {0xCD, 0, 0}, - {0xCE, 0x5e, 0}, - {0xCF, 0xc, 0}, - {0xD0, 0xc, 0}, - {0xD1, 0xc, 0}, - {0xD2, 0, 0}, - {0xD3, 0x2b, 0}, - {0xD4, 0xc, 0}, - {0xD5, 0, 0}, - {0xD6, 0x75, 0}, - {0xDB, 0x7, 0}, - {0xDC, 0, 0}, - {0xDD, 0, 0}, - {0xDE, 0xa8, 0}, - {0xDF, 0, 0}, - {0xE0, 0x1f, 0}, - {0xE1, 0x30, 0}, - {0xE2, 0x1, 0}, - {0xE3, 0x30, 0}, - {0xE4, 0x70, 0}, - {0xE5, 0, 0}, - {0xE6, 0, 0}, - {0xE7, 0x33, 0}, - {0xE8, 0x19, 0}, - {0xE9, 0x62, 0}, - {0xEA, 0, 0}, - {0xEB, 0x11, 0}, - {0xEE, 0, 0}, - {0xEF, 0x7e, 0}, - {0xF0, 0x3f, 0}, - {0xF1, 0x7f, 0}, - {0xF2, 0x78, 0}, - {0xF3, 0xc8, 0}, - {0xF4, 0x88, 0}, - {0xF5, 0x8, 0}, - {0xF6, 0xf, 0}, - {0xF7, 0xbc, 0}, - {0xF8, 0x8, 0}, - {0xF9, 0x60, 0}, - {0xFA, 0x1e, 0}, - {0xFB, 0x70, 0}, - {0xFC, 0, 0}, - {0xFD, 0, 0}, - {0xFE, 0, 0}, - {0xFF, 0x33, 0}, - {0x100, 0x1e, 0}, - {0x101, 0x62, 0}, - {0x102, 0x11, 0}, - {0x105, 0x3c, 0}, - {0x106, 0x9c, 0}, - {0x107, 0xa, 0}, - {0x108, 0x9d, 0}, - {0x109, 0xa, 0}, - {0x10A, 0, 0}, - {0x10B, 0x40, 0}, - {0x10C, 0x40, 0}, - {0x10D, 0x88, 0}, - {0x10E, 0x10, 0}, - {0x10F, 0xf0, 1}, - {0x110, 0x10, 1}, - {0x111, 0xf0, 1}, - {0x112, 0, 0}, - {0x113, 0, 0}, - {0x114, 0x10, 0}, - {0x115, 0x55, 0}, - {0x116, 0x3f, 1}, - {0x117, 0x36, 1}, - {0x118, 0, 0}, - {0x119, 0, 0}, - {0x11A, 0, 0}, - {0x11B, 0x87, 0}, - {0x11C, 0x11, 0}, - {0x11D, 0, 0}, - {0x11E, 0x33, 0}, - {0x11F, 0x88, 0}, - {0x120, 0, 0}, - {0x121, 0x87, 0}, - {0x122, 0x11, 0}, - {0x123, 0, 0}, - {0x124, 0x33, 0}, - {0x125, 0x88, 0}, - {0x126, 0xe1, 0}, - {0x127, 0x3f, 0}, - {0x128, 0x44, 0}, - {0x129, 0x8c, 1}, - {0x12A, 0x6d, 0}, - {0x12B, 0x22, 0}, - {0x12C, 0xbe, 0}, - {0x12D, 0x55, 1}, - {0x12E, 0xc, 0}, - {0x12F, 0xc, 0}, - {0x130, 0xaa, 0}, - {0x131, 0x2, 0}, - {0x132, 0, 0}, - {0x133, 0x10, 0}, - {0x134, 0x1, 1}, - {0x135, 0, 0}, - {0x136, 0, 0}, - {0x137, 0x80, 0}, - {0x138, 0x60, 0}, - {0x139, 0x44, 0}, - {0x13A, 0x55, 0}, - {0x13B, 0x1, 0}, - {0x13C, 0x55, 0}, - {0x13D, 0x1, 0}, - {0x13E, 0x5, 0}, - {0x13F, 0x55, 0}, - {0x140, 0x55, 0}, - {0x146, 0, 0}, - {0x147, 0, 0}, - {0x148, 0, 0}, - {0x149, 0, 0}, - {0x14A, 0, 0}, - {0x14B, 0, 0}, - {0x14C, 0, 0}, - {0x14D, 0, 0}, - {0x14E, 0, 0}, - {0x14F, 0, 0}, - {0x150, 0, 0}, - {0x151, 0, 0}, - {0x152, 0, 0}, - {0x153, 0, 0}, - {0x154, 0xc, 0}, - {0x155, 0xc, 0}, - {0x156, 0xc, 0}, - {0x157, 0, 0}, - {0x158, 0x2b, 0}, - {0x159, 0x84, 0}, - {0x15A, 0x15, 0}, - {0x15B, 0xf, 0}, - {0x15C, 0, 0}, - {0x15D, 0, 0}, - {0x15E, 0, 1}, - {0x15F, 0, 1}, - {0x160, 0, 1}, - {0x161, 0, 1}, - {0x162, 0, 1}, - {0x163, 0, 1}, - {0x164, 0, 0}, - {0x165, 0, 0}, - {0x166, 0, 0}, - {0x167, 0, 0}, - {0x168, 0, 0}, - {0x169, 0x2, 1}, - {0x16A, 0, 1}, - {0x16B, 0, 1}, - {0x16C, 0, 1}, - {0x16D, 0, 0}, - {0x170, 0, 0}, - {0x171, 0x77, 0}, - {0x172, 0x77, 0}, - {0x173, 0x77, 0}, - {0x174, 0x77, 0}, - {0x175, 0, 0}, - {0x176, 0x3, 0}, - {0x177, 0x37, 0}, - {0x178, 0x3, 0}, - {0x179, 0, 0}, - {0x17A, 0x21, 0}, - {0x17B, 0x21, 0}, - {0x17C, 0, 0}, - {0x17D, 0xaa, 0}, - {0x17E, 0, 0}, - {0x17F, 0xaa, 0}, - {0x180, 0, 0}, - {0x190, 0, 0}, - {0x191, 0x77, 0}, - {0x192, 0x77, 0}, - {0x193, 0x77, 0}, - {0x194, 0x77, 0}, - {0x195, 0, 0}, - {0x196, 0x3, 0}, - {0x197, 0x37, 0}, - {0x198, 0x3, 0}, - {0x199, 0, 0}, - {0x19A, 0x21, 0}, - {0x19B, 0x21, 0}, - {0x19C, 0, 0}, - {0x19D, 0xaa, 0}, - {0x19E, 0, 0}, - {0x19F, 0xaa, 0}, - {0x1A0, 0, 0}, - {0x1A1, 0x2, 0}, - {0x1A2, 0xf, 0}, - {0x1A3, 0xf, 0}, - {0x1A4, 0, 1}, - {0x1A5, 0, 1}, - {0x1A6, 0, 1}, - {0x1A7, 0x2, 0}, - {0x1A8, 0xf, 0}, - {0x1A9, 0xf, 0}, - {0x1AA, 0, 1}, - {0x1AB, 0, 1}, - {0x1AC, 0, 1}, - {0xFFFF, 0, 0}, -}; - -static struct radio_20xx_regs regs_2057_rev5[] = { - {0x00, 0, 1}, - {0x01, 0x57, 1}, - {0x02, 0x20, 1}, - {0x03, 0x1f, 0}, - {0x04, 0x4, 0}, - {0x05, 0x2, 0}, - {0x06, 0x1, 0}, - {0x07, 0x1, 0}, - {0x08, 0x1, 0}, - {0x09, 0x69, 0}, - {0x0A, 0x66, 0}, - {0x0B, 0x6, 0}, - {0x0C, 0x18, 0}, - {0x0D, 0x3, 0}, - {0x0E, 0x20, 0}, - {0x0F, 0x20, 0}, - {0x10, 0, 0}, - {0x11, 0x7c, 0}, - {0x12, 0x42, 0}, - {0x13, 0xbd, 0}, - {0x14, 0x7, 0}, - {0x15, 0x87, 0}, - {0x16, 0x8, 0}, - {0x17, 0x17, 0}, - {0x18, 0x7, 0}, - {0x19, 0, 0}, - {0x1A, 0x2, 0}, - {0x1B, 0x13, 0}, - {0x1C, 0x3e, 0}, - {0x1D, 0x3e, 0}, - {0x1E, 0x96, 0}, - {0x1F, 0x4, 0}, - {0x20, 0, 0}, - {0x21, 0, 0}, - {0x22, 0x17, 0}, - {0x23, 0x6, 1}, - {0x24, 0x1, 0}, - {0x25, 0x6, 0}, - {0x26, 0x4, 0}, - {0x27, 0xd, 0}, - {0x28, 0xd, 0}, - {0x29, 0x30, 0}, - {0x2A, 0x32, 0}, - {0x2B, 0x8, 0}, - {0x2C, 0x1c, 0}, - {0x2D, 0x2, 0}, - {0x2E, 0x4, 0}, - {0x2F, 0x7f, 0}, - {0x30, 0x27, 0}, - {0x31, 0, 1}, - {0x32, 0, 1}, - {0x33, 0, 1}, - {0x34, 0, 0}, - {0x35, 0x20, 0}, - {0x36, 0x18, 0}, - {0x37, 0x7, 0}, - {0x38, 0x66, 0}, - {0x39, 0x66, 0}, - {0x3C, 0xff, 0}, - {0x3D, 0xff, 0}, - {0x40, 0x16, 0}, - {0x41, 0x7, 0}, - {0x45, 0x3, 0}, - {0x46, 0x1, 0}, - {0x47, 0x7, 0}, - {0x4B, 0x66, 0}, - {0x4C, 0x66, 0}, - {0x4D, 0, 0}, - {0x4E, 0x4, 0}, - {0x4F, 0xc, 0}, - {0x50, 0, 0}, - {0x51, 0x70, 1}, - {0x56, 0x7, 0}, - {0x57, 0, 0}, - {0x58, 0, 0}, - {0x59, 0x88, 1}, - {0x5A, 0, 0}, - {0x5B, 0x1f, 0}, - {0x5C, 0x20, 1}, - {0x5D, 0x1, 0}, - {0x5E, 0x30, 0}, - {0x5F, 0x70, 0}, - {0x60, 0, 0}, - {0x61, 0, 0}, - {0x62, 0x33, 1}, - {0x63, 0xf, 1}, - {0x64, 0xf, 1}, - {0x65, 0, 0}, - {0x66, 0x11, 0}, - {0x80, 0x3c, 0}, - {0x81, 0x1, 1}, - {0x82, 0xa, 0}, - {0x85, 0, 0}, - {0x86, 0x40, 0}, - {0x87, 0x40, 0}, - {0x88, 0x88, 0}, - {0x89, 0x10, 0}, - {0x8A, 0xf0, 0}, - {0x8B, 0x10, 0}, - {0x8C, 0xf0, 0}, - {0x8F, 0x10, 0}, - {0x90, 0x55, 0}, - {0x91, 0x3f, 1}, - {0x92, 0x36, 1}, - {0x93, 0, 0}, - {0x94, 0, 0}, - {0x95, 0, 0}, - {0x96, 0x87, 0}, - {0x97, 0x11, 0}, - {0x98, 0, 0}, - {0x99, 0x33, 0}, - {0x9A, 0x88, 0}, - {0xA1, 0x20, 1}, - {0xA2, 0x3f, 0}, - {0xA3, 0x44, 0}, - {0xA4, 0x8c, 0}, - {0xA5, 0x6c, 0}, - {0xA6, 0x22, 0}, - {0xA7, 0xbe, 0}, - {0xA8, 0x55, 0}, - {0xAA, 0xc, 0}, - {0xAB, 0xaa, 0}, - {0xAC, 0x2, 0}, - {0xAD, 0, 0}, - {0xAE, 0x10, 0}, - {0xAF, 0x1, 0}, - {0xB0, 0, 0}, - {0xB1, 0, 0}, - {0xB2, 0x80, 0}, - {0xB3, 0x60, 0}, - {0xB4, 0x44, 0}, - {0xB5, 0x55, 0}, - {0xB6, 0x1, 0}, - {0xB7, 0x55, 0}, - {0xB8, 0x1, 0}, - {0xB9, 0x5, 0}, - {0xBA, 0x55, 0}, - {0xBB, 0x55, 0}, - {0xC3, 0, 0}, - {0xC4, 0, 0}, - {0xC5, 0, 0}, - {0xC6, 0, 0}, - {0xC7, 0, 0}, - {0xC8, 0, 0}, - {0xC9, 0, 0}, - {0xCA, 0, 0}, - {0xCB, 0, 0}, - {0xCD, 0, 0}, - {0xCE, 0x5e, 0}, - {0xCF, 0xc, 0}, - {0xD0, 0xc, 0}, - {0xD1, 0xc, 0}, - {0xD2, 0, 0}, - {0xD3, 0x2b, 0}, - {0xD4, 0xc, 0}, - {0xD5, 0, 0}, - {0xD6, 0x70, 1}, - {0xDB, 0x7, 0}, - {0xDC, 0, 0}, - {0xDD, 0, 0}, - {0xDE, 0x88, 1}, - {0xDF, 0, 0}, - {0xE0, 0x1f, 0}, - {0xE1, 0x20, 1}, - {0xE2, 0x1, 0}, - {0xE3, 0x30, 0}, - {0xE4, 0x70, 0}, - {0xE5, 0, 0}, - {0xE6, 0, 0}, - {0xE7, 0x33, 0}, - {0xE8, 0xf, 1}, - {0xE9, 0xf, 1}, - {0xEA, 0, 0}, - {0xEB, 0x11, 0}, - {0x105, 0x3c, 0}, - {0x106, 0x1, 1}, - {0x107, 0xa, 0}, - {0x10A, 0, 0}, - {0x10B, 0x40, 0}, - {0x10C, 0x40, 0}, - {0x10D, 0x88, 0}, - {0x10E, 0x10, 0}, - {0x10F, 0xf0, 0}, - {0x110, 0x10, 0}, - {0x111, 0xf0, 0}, - {0x114, 0x10, 0}, - {0x115, 0x55, 0}, - {0x116, 0x3f, 1}, - {0x117, 0x36, 1}, - {0x118, 0, 0}, - {0x119, 0, 0}, - {0x11A, 0, 0}, - {0x11B, 0x87, 0}, - {0x11C, 0x11, 0}, - {0x11D, 0, 0}, - {0x11E, 0x33, 0}, - {0x11F, 0x88, 0}, - {0x126, 0x20, 1}, - {0x127, 0x3f, 0}, - {0x128, 0x44, 0}, - {0x129, 0x8c, 0}, - {0x12A, 0x6c, 0}, - {0x12B, 0x22, 0}, - {0x12C, 0xbe, 0}, - {0x12D, 0x55, 0}, - {0x12F, 0xc, 0}, - {0x130, 0xaa, 0}, - {0x131, 0x2, 0}, - {0x132, 0, 0}, - {0x133, 0x10, 0}, - {0x134, 0x1, 0}, - {0x135, 0, 0}, - {0x136, 0, 0}, - {0x137, 0x80, 0}, - {0x138, 0x60, 0}, - {0x139, 0x44, 0}, - {0x13A, 0x55, 0}, - {0x13B, 0x1, 0}, - {0x13C, 0x55, 0}, - {0x13D, 0x1, 0}, - {0x13E, 0x5, 0}, - {0x13F, 0x55, 0}, - {0x140, 0x55, 0}, - {0x148, 0, 0}, - {0x149, 0, 0}, - {0x14A, 0, 0}, - {0x14B, 0, 0}, - {0x14C, 0, 0}, - {0x14D, 0, 0}, - {0x14E, 0, 0}, - {0x14F, 0, 0}, - {0x150, 0, 0}, - {0x154, 0xc, 0}, - {0x155, 0xc, 0}, - {0x156, 0xc, 0}, - {0x157, 0, 0}, - {0x158, 0x2b, 0}, - {0x159, 0x84, 0}, - {0x15A, 0x15, 0}, - {0x15B, 0xf, 0}, - {0x15C, 0, 0}, - {0x15D, 0, 0}, - {0x15E, 0, 1}, - {0x15F, 0, 1}, - {0x160, 0, 1}, - {0x161, 0, 1}, - {0x162, 0, 1}, - {0x163, 0, 1}, - {0x164, 0, 0}, - {0x165, 0, 0}, - {0x166, 0, 0}, - {0x167, 0, 0}, - {0x168, 0, 0}, - {0x169, 0, 0}, - {0x16A, 0, 1}, - {0x16B, 0, 1}, - {0x16C, 0, 1}, - {0x16D, 0, 0}, - {0x170, 0, 0}, - {0x171, 0x77, 0}, - {0x172, 0x77, 0}, - {0x173, 0x77, 0}, - {0x174, 0x77, 0}, - {0x175, 0, 0}, - {0x176, 0x3, 0}, - {0x177, 0x37, 0}, - {0x178, 0x3, 0}, - {0x179, 0, 0}, - {0x17B, 0x21, 0}, - {0x17C, 0, 0}, - {0x17D, 0xaa, 0}, - {0x17E, 0, 0}, - {0x190, 0, 0}, - {0x191, 0x77, 0}, - {0x192, 0x77, 0}, - {0x193, 0x77, 0}, - {0x194, 0x77, 0}, - {0x195, 0, 0}, - {0x196, 0x3, 0}, - {0x197, 0x37, 0}, - {0x198, 0x3, 0}, - {0x199, 0, 0}, - {0x19B, 0x21, 0}, - {0x19C, 0, 0}, - {0x19D, 0xaa, 0}, - {0x19E, 0, 0}, - {0x1A1, 0x2, 0}, - {0x1A2, 0xf, 0}, - {0x1A3, 0xf, 0}, - {0x1A4, 0, 1}, - {0x1A5, 0, 1}, - {0x1A6, 0, 1}, - {0x1A7, 0x2, 0}, - {0x1A8, 0xf, 0}, - {0x1A9, 0xf, 0}, - {0x1AA, 0, 1}, - {0x1AB, 0, 1}, - {0x1AC, 0, 1}, - {0x1AD, 0x84, 0}, - {0x1AE, 0x60, 0}, - {0x1AF, 0x47, 0}, - {0x1B0, 0x47, 0}, - {0x1B1, 0, 0}, - {0x1B2, 0, 0}, - {0x1B3, 0, 0}, - {0x1B4, 0, 0}, - {0x1B5, 0, 0}, - {0x1B6, 0, 0}, - {0x1B7, 0xc, 1}, - {0x1B8, 0, 0}, - {0x1B9, 0, 0}, - {0x1BA, 0, 0}, - {0x1BB, 0, 0}, - {0x1BC, 0, 0}, - {0x1BD, 0, 0}, - {0x1BE, 0, 0}, - {0x1BF, 0, 0}, - {0x1C0, 0, 0}, - {0x1C1, 0x1, 1}, - {0x1C2, 0x80, 1}, - {0x1C3, 0, 0}, - {0x1C4, 0, 0}, - {0x1C5, 0, 0}, - {0x1C6, 0, 0}, - {0x1C7, 0, 0}, - {0x1C8, 0, 0}, - {0x1C9, 0, 0}, - {0x1CA, 0, 0}, - {0xFFFF, 0, 0} -}; - -static struct radio_20xx_regs regs_2057_rev5v1[] = { - {0x00, 0x15, 1}, - {0x01, 0x57, 1}, - {0x02, 0x20, 1}, - {0x03, 0x1f, 0}, - {0x04, 0x4, 0}, - {0x05, 0x2, 0}, - {0x06, 0x1, 0}, - {0x07, 0x1, 0}, - {0x08, 0x1, 0}, - {0x09, 0x69, 0}, - {0x0A, 0x66, 0}, - {0x0B, 0x6, 0}, - {0x0C, 0x18, 0}, - {0x0D, 0x3, 0}, - {0x0E, 0x20, 0}, - {0x0F, 0x20, 0}, - {0x10, 0, 0}, - {0x11, 0x7c, 0}, - {0x12, 0x42, 0}, - {0x13, 0xbd, 0}, - {0x14, 0x7, 0}, - {0x15, 0x87, 0}, - {0x16, 0x8, 0}, - {0x17, 0x17, 0}, - {0x18, 0x7, 0}, - {0x19, 0, 0}, - {0x1A, 0x2, 0}, - {0x1B, 0x13, 0}, - {0x1C, 0x3e, 0}, - {0x1D, 0x3e, 0}, - {0x1E, 0x96, 0}, - {0x1F, 0x4, 0}, - {0x20, 0, 0}, - {0x21, 0, 0}, - {0x22, 0x17, 0}, - {0x23, 0x6, 1}, - {0x24, 0x1, 0}, - {0x25, 0x6, 0}, - {0x26, 0x4, 0}, - {0x27, 0xd, 0}, - {0x28, 0xd, 0}, - {0x29, 0x30, 0}, - {0x2A, 0x32, 0}, - {0x2B, 0x8, 0}, - {0x2C, 0x1c, 0}, - {0x2D, 0x2, 0}, - {0x2E, 0x4, 0}, - {0x2F, 0x7f, 0}, - {0x30, 0x27, 0}, - {0x31, 0, 1}, - {0x32, 0, 1}, - {0x33, 0, 1}, - {0x34, 0, 0}, - {0x35, 0x20, 0}, - {0x36, 0x18, 0}, - {0x37, 0x7, 0}, - {0x38, 0x66, 0}, - {0x39, 0x66, 0}, - {0x3C, 0xff, 0}, - {0x3D, 0xff, 0}, - {0x40, 0x16, 0}, - {0x41, 0x7, 0}, - {0x45, 0x3, 0}, - {0x46, 0x1, 0}, - {0x47, 0x7, 0}, - {0x4B, 0x66, 0}, - {0x4C, 0x66, 0}, - {0x4D, 0, 0}, - {0x4E, 0x4, 0}, - {0x4F, 0xc, 0}, - {0x50, 0, 0}, - {0x51, 0x70, 1}, - {0x56, 0x7, 0}, - {0x57, 0, 0}, - {0x58, 0, 0}, - {0x59, 0x88, 1}, - {0x5A, 0, 0}, - {0x5B, 0x1f, 0}, - {0x5C, 0x20, 1}, - {0x5D, 0x1, 0}, - {0x5E, 0x30, 0}, - {0x5F, 0x70, 0}, - {0x60, 0, 0}, - {0x61, 0, 0}, - {0x62, 0x33, 1}, - {0x63, 0xf, 1}, - {0x64, 0xf, 1}, - {0x65, 0, 0}, - {0x66, 0x11, 0}, - {0x80, 0x3c, 0}, - {0x81, 0x1, 1}, - {0x82, 0xa, 0}, - {0x85, 0, 0}, - {0x86, 0x40, 0}, - {0x87, 0x40, 0}, - {0x88, 0x88, 0}, - {0x89, 0x10, 0}, - {0x8A, 0xf0, 0}, - {0x8B, 0x10, 0}, - {0x8C, 0xf0, 0}, - {0x8F, 0x10, 0}, - {0x90, 0x55, 0}, - {0x91, 0x3f, 1}, - {0x92, 0x36, 1}, - {0x93, 0, 0}, - {0x94, 0, 0}, - {0x95, 0, 0}, - {0x96, 0x87, 0}, - {0x97, 0x11, 0}, - {0x98, 0, 0}, - {0x99, 0x33, 0}, - {0x9A, 0x88, 0}, - {0xA1, 0x20, 1}, - {0xA2, 0x3f, 0}, - {0xA3, 0x44, 0}, - {0xA4, 0x8c, 0}, - {0xA5, 0x6c, 0}, - {0xA6, 0x22, 0}, - {0xA7, 0xbe, 0}, - {0xA8, 0x55, 0}, - {0xAA, 0xc, 0}, - {0xAB, 0xaa, 0}, - {0xAC, 0x2, 0}, - {0xAD, 0, 0}, - {0xAE, 0x10, 0}, - {0xAF, 0x1, 0}, - {0xB0, 0, 0}, - {0xB1, 0, 0}, - {0xB2, 0x80, 0}, - {0xB3, 0x60, 0}, - {0xB4, 0x44, 0}, - {0xB5, 0x55, 0}, - {0xB6, 0x1, 0}, - {0xB7, 0x55, 0}, - {0xB8, 0x1, 0}, - {0xB9, 0x5, 0}, - {0xBA, 0x55, 0}, - {0xBB, 0x55, 0}, - {0xC3, 0, 0}, - {0xC4, 0, 0}, - {0xC5, 0, 0}, - {0xC6, 0, 0}, - {0xC7, 0, 0}, - {0xC8, 0, 0}, - {0xC9, 0x1, 1}, - {0xCA, 0, 0}, - {0xCB, 0, 0}, - {0xCD, 0, 0}, - {0xCE, 0x5e, 0}, - {0xCF, 0xc, 0}, - {0xD0, 0xc, 0}, - {0xD1, 0xc, 0}, - {0xD2, 0, 0}, - {0xD3, 0x2b, 0}, - {0xD4, 0xc, 0}, - {0xD5, 0, 0}, - {0xD6, 0x70, 1}, - {0xDB, 0x7, 0}, - {0xDC, 0, 0}, - {0xDD, 0, 0}, - {0xDE, 0x88, 1}, - {0xDF, 0, 0}, - {0xE0, 0x1f, 0}, - {0xE1, 0x20, 1}, - {0xE2, 0x1, 0}, - {0xE3, 0x30, 0}, - {0xE4, 0x70, 0}, - {0xE5, 0, 0}, - {0xE6, 0, 0}, - {0xE7, 0x33, 0}, - {0xE8, 0xf, 1}, - {0xE9, 0xf, 1}, - {0xEA, 0, 0}, - {0xEB, 0x11, 0}, - {0x105, 0x3c, 0}, - {0x106, 0x1, 1}, - {0x107, 0xa, 0}, - {0x10A, 0, 0}, - {0x10B, 0x40, 0}, - {0x10C, 0x40, 0}, - {0x10D, 0x88, 0}, - {0x10E, 0x10, 0}, - {0x10F, 0xf0, 0}, - {0x110, 0x10, 0}, - {0x111, 0xf0, 0}, - {0x114, 0x10, 0}, - {0x115, 0x55, 0}, - {0x116, 0x3f, 1}, - {0x117, 0x36, 1}, - {0x118, 0, 0}, - {0x119, 0, 0}, - {0x11A, 0, 0}, - {0x11B, 0x87, 0}, - {0x11C, 0x11, 0}, - {0x11D, 0, 0}, - {0x11E, 0x33, 0}, - {0x11F, 0x88, 0}, - {0x126, 0x20, 1}, - {0x127, 0x3f, 0}, - {0x128, 0x44, 0}, - {0x129, 0x8c, 0}, - {0x12A, 0x6c, 0}, - {0x12B, 0x22, 0}, - {0x12C, 0xbe, 0}, - {0x12D, 0x55, 0}, - {0x12F, 0xc, 0}, - {0x130, 0xaa, 0}, - {0x131, 0x2, 0}, - {0x132, 0, 0}, - {0x133, 0x10, 0}, - {0x134, 0x1, 0}, - {0x135, 0, 0}, - {0x136, 0, 0}, - {0x137, 0x80, 0}, - {0x138, 0x60, 0}, - {0x139, 0x44, 0}, - {0x13A, 0x55, 0}, - {0x13B, 0x1, 0}, - {0x13C, 0x55, 0}, - {0x13D, 0x1, 0}, - {0x13E, 0x5, 0}, - {0x13F, 0x55, 0}, - {0x140, 0x55, 0}, - {0x148, 0, 0}, - {0x149, 0, 0}, - {0x14A, 0, 0}, - {0x14B, 0, 0}, - {0x14C, 0, 0}, - {0x14D, 0, 0}, - {0x14E, 0x1, 1}, - {0x14F, 0, 0}, - {0x150, 0, 0}, - {0x154, 0xc, 0}, - {0x155, 0xc, 0}, - {0x156, 0xc, 0}, - {0x157, 0, 0}, - {0x158, 0x2b, 0}, - {0x159, 0x84, 0}, - {0x15A, 0x15, 0}, - {0x15B, 0xf, 0}, - {0x15C, 0, 0}, - {0x15D, 0, 0}, - {0x15E, 0, 1}, - {0x15F, 0, 1}, - {0x160, 0, 1}, - {0x161, 0, 1}, - {0x162, 0, 1}, - {0x163, 0, 1}, - {0x164, 0, 0}, - {0x165, 0, 0}, - {0x166, 0, 0}, - {0x167, 0, 0}, - {0x168, 0, 0}, - {0x169, 0, 0}, - {0x16A, 0, 1}, - {0x16B, 0, 1}, - {0x16C, 0, 1}, - {0x16D, 0, 0}, - {0x170, 0, 0}, - {0x171, 0x77, 0}, - {0x172, 0x77, 0}, - {0x173, 0x77, 0}, - {0x174, 0x77, 0}, - {0x175, 0, 0}, - {0x176, 0x3, 0}, - {0x177, 0x37, 0}, - {0x178, 0x3, 0}, - {0x179, 0, 0}, - {0x17B, 0x21, 0}, - {0x17C, 0, 0}, - {0x17D, 0xaa, 0}, - {0x17E, 0, 0}, - {0x190, 0, 0}, - {0x191, 0x77, 0}, - {0x192, 0x77, 0}, - {0x193, 0x77, 0}, - {0x194, 0x77, 0}, - {0x195, 0, 0}, - {0x196, 0x3, 0}, - {0x197, 0x37, 0}, - {0x198, 0x3, 0}, - {0x199, 0, 0}, - {0x19B, 0x21, 0}, - {0x19C, 0, 0}, - {0x19D, 0xaa, 0}, - {0x19E, 0, 0}, - {0x1A1, 0x2, 0}, - {0x1A2, 0xf, 0}, - {0x1A3, 0xf, 0}, - {0x1A4, 0, 1}, - {0x1A5, 0, 1}, - {0x1A6, 0, 1}, - {0x1A7, 0x2, 0}, - {0x1A8, 0xf, 0}, - {0x1A9, 0xf, 0}, - {0x1AA, 0, 1}, - {0x1AB, 0, 1}, - {0x1AC, 0, 1}, - {0x1AD, 0x84, 0}, - {0x1AE, 0x60, 0}, - {0x1AF, 0x47, 0}, - {0x1B0, 0x47, 0}, - {0x1B1, 0, 0}, - {0x1B2, 0, 0}, - {0x1B3, 0, 0}, - {0x1B4, 0, 0}, - {0x1B5, 0, 0}, - {0x1B6, 0, 0}, - {0x1B7, 0xc, 1}, - {0x1B8, 0, 0}, - {0x1B9, 0, 0}, - {0x1BA, 0, 0}, - {0x1BB, 0, 0}, - {0x1BC, 0, 0}, - {0x1BD, 0, 0}, - {0x1BE, 0, 0}, - {0x1BF, 0, 0}, - {0x1C0, 0, 0}, - {0x1C1, 0x1, 1}, - {0x1C2, 0x80, 1}, - {0x1C3, 0, 0}, - {0x1C4, 0, 0}, - {0x1C5, 0, 0}, - {0x1C6, 0, 0}, - {0x1C7, 0, 0}, - {0x1C8, 0, 0}, - {0x1C9, 0, 0}, - {0x1CA, 0, 0}, - {0xFFFF, 0, 0} -}; - -static struct radio_20xx_regs regs_2057_rev7[] = { - {0x00, 0, 1}, - {0x01, 0x57, 1}, - {0x02, 0x20, 1}, - {0x03, 0x1f, 0}, - {0x04, 0x4, 0}, - {0x05, 0x2, 0}, - {0x06, 0x1, 0}, - {0x07, 0x1, 0}, - {0x08, 0x1, 0}, - {0x09, 0x69, 0}, - {0x0A, 0x66, 0}, - {0x0B, 0x6, 0}, - {0x0C, 0x18, 0}, - {0x0D, 0x3, 0}, - {0x0E, 0x20, 0}, - {0x0F, 0x20, 0}, - {0x10, 0, 0}, - {0x11, 0x7c, 0}, - {0x12, 0x42, 0}, - {0x13, 0xbd, 0}, - {0x14, 0x7, 0}, - {0x15, 0x87, 0}, - {0x16, 0x8, 0}, - {0x17, 0x17, 0}, - {0x18, 0x7, 0}, - {0x19, 0, 0}, - {0x1A, 0x2, 0}, - {0x1B, 0x13, 0}, - {0x1C, 0x3e, 0}, - {0x1D, 0x3e, 0}, - {0x1E, 0x96, 0}, - {0x1F, 0x4, 0}, - {0x20, 0, 0}, - {0x21, 0, 0}, - {0x22, 0x17, 0}, - {0x23, 0x6, 0}, - {0x24, 0x1, 0}, - {0x25, 0x6, 0}, - {0x26, 0x4, 0}, - {0x27, 0xd, 0}, - {0x28, 0xd, 0}, - {0x29, 0x30, 0}, - {0x2A, 0x32, 0}, - {0x2B, 0x8, 0}, - {0x2C, 0x1c, 0}, - {0x2D, 0x2, 0}, - {0x2E, 0x4, 0}, - {0x2F, 0x7f, 0}, - {0x30, 0x27, 0}, - {0x31, 0, 1}, - {0x32, 0, 1}, - {0x33, 0, 1}, - {0x34, 0, 0}, - {0x35, 0x20, 0}, - {0x36, 0x18, 0}, - {0x37, 0x7, 0}, - {0x38, 0x66, 0}, - {0x39, 0x66, 0}, - {0x3A, 0x66, 0}, - {0x3B, 0x66, 0}, - {0x3C, 0xff, 0}, - {0x3D, 0xff, 0}, - {0x3E, 0xff, 0}, - {0x3F, 0xff, 0}, - {0x40, 0x16, 0}, - {0x41, 0x7, 0}, - {0x42, 0x19, 0}, - {0x43, 0x7, 0}, - {0x44, 0x6, 0}, - {0x45, 0x3, 0}, - {0x46, 0x1, 0}, - {0x47, 0x7, 0}, - {0x48, 0x33, 0}, - {0x49, 0x5, 0}, - {0x4A, 0x77, 0}, - {0x4B, 0x66, 0}, - {0x4C, 0x66, 0}, - {0x4D, 0, 0}, - {0x4E, 0x4, 0}, - {0x4F, 0xc, 0}, - {0x50, 0, 0}, - {0x51, 0x70, 1}, - {0x56, 0x7, 0}, - {0x57, 0, 0}, - {0x58, 0, 0}, - {0x59, 0x88, 1}, - {0x5A, 0, 0}, - {0x5B, 0x1f, 0}, - {0x5C, 0x20, 1}, - {0x5D, 0x1, 0}, - {0x5E, 0x30, 0}, - {0x5F, 0x70, 0}, - {0x60, 0, 0}, - {0x61, 0, 0}, - {0x62, 0x33, 1}, - {0x63, 0xf, 1}, - {0x64, 0x13, 1}, - {0x65, 0, 0}, - {0x66, 0xee, 1}, - {0x69, 0, 0}, - {0x6A, 0x7e, 0}, - {0x6B, 0x3f, 0}, - {0x6C, 0x7f, 0}, - {0x6D, 0x78, 0}, - {0x6E, 0x58, 1}, - {0x6F, 0x88, 0}, - {0x70, 0x8, 0}, - {0x71, 0xf, 0}, - {0x72, 0xbc, 0}, - {0x73, 0x8, 0}, - {0x74, 0x60, 0}, - {0x75, 0x13, 1}, - {0x76, 0x70, 0}, - {0x77, 0, 0}, - {0x78, 0, 0}, - {0x79, 0, 0}, - {0x7A, 0x33, 0}, - {0x7B, 0x13, 1}, - {0x7C, 0x14, 1}, - {0x7D, 0xee, 1}, - {0x80, 0x3c, 0}, - {0x81, 0x1, 1}, - {0x82, 0xa, 0}, - {0x83, 0x9d, 0}, - {0x84, 0xa, 0}, - {0x85, 0, 0}, - {0x86, 0x40, 0}, - {0x87, 0x40, 0}, - {0x88, 0x88, 0}, - {0x89, 0x10, 0}, - {0x8A, 0xf0, 0}, - {0x8B, 0x10, 0}, - {0x8C, 0xf0, 0}, - {0x8D, 0, 0}, - {0x8E, 0, 0}, - {0x8F, 0x10, 0}, - {0x90, 0x55, 0}, - {0x91, 0x3f, 1}, - {0x92, 0x36, 1}, - {0x93, 0, 0}, - {0x94, 0, 0}, - {0x95, 0, 0}, - {0x96, 0x87, 0}, - {0x97, 0x11, 0}, - {0x98, 0, 0}, - {0x99, 0x33, 0}, - {0x9A, 0x88, 0}, - {0x9B, 0, 0}, - {0x9C, 0x87, 0}, - {0x9D, 0x11, 0}, - {0x9E, 0, 0}, - {0x9F, 0x33, 0}, - {0xA0, 0x88, 0}, - {0xA1, 0x20, 1}, - {0xA2, 0x3f, 0}, - {0xA3, 0x44, 0}, - {0xA4, 0x8c, 0}, - {0xA5, 0x6c, 0}, - {0xA6, 0x22, 0}, - {0xA7, 0xbe, 0}, - {0xA8, 0x55, 0}, - {0xAA, 0xc, 0}, - {0xAB, 0xaa, 0}, - {0xAC, 0x2, 0}, - {0xAD, 0, 0}, - {0xAE, 0x10, 0}, - {0xAF, 0x1, 0}, - {0xB0, 0, 0}, - {0xB1, 0, 0}, - {0xB2, 0x80, 0}, - {0xB3, 0x60, 0}, - {0xB4, 0x44, 0}, - {0xB5, 0x55, 0}, - {0xB6, 0x1, 0}, - {0xB7, 0x55, 0}, - {0xB8, 0x1, 0}, - {0xB9, 0x5, 0}, - {0xBA, 0x55, 0}, - {0xBB, 0x55, 0}, - {0xC1, 0, 0}, - {0xC2, 0, 0}, - {0xC3, 0, 0}, - {0xC4, 0, 0}, - {0xC5, 0, 0}, - {0xC6, 0, 0}, - {0xC7, 0, 0}, - {0xC8, 0, 0}, - {0xC9, 0, 0}, - {0xCA, 0, 0}, - {0xCB, 0, 0}, - {0xCC, 0, 0}, - {0xCD, 0, 0}, - {0xCE, 0x5e, 0}, - {0xCF, 0xc, 0}, - {0xD0, 0xc, 0}, - {0xD1, 0xc, 0}, - {0xD2, 0, 0}, - {0xD3, 0x2b, 0}, - {0xD4, 0xc, 0}, - {0xD5, 0, 0}, - {0xD6, 0x70, 1}, - {0xDB, 0x7, 0}, - {0xDC, 0, 0}, - {0xDD, 0, 0}, - {0xDE, 0x88, 1}, - {0xDF, 0, 0}, - {0xE0, 0x1f, 0}, - {0xE1, 0x20, 1}, - {0xE2, 0x1, 0}, - {0xE3, 0x30, 0}, - {0xE4, 0x70, 0}, - {0xE5, 0, 0}, - {0xE6, 0, 0}, - {0xE7, 0x33, 0}, - {0xE8, 0xf, 1}, - {0xE9, 0x13, 1}, - {0xEA, 0, 0}, - {0xEB, 0xee, 1}, - {0xEE, 0, 0}, - {0xEF, 0x7e, 0}, - {0xF0, 0x3f, 0}, - {0xF1, 0x7f, 0}, - {0xF2, 0x78, 0}, - {0xF3, 0x58, 1}, - {0xF4, 0x88, 0}, - {0xF5, 0x8, 0}, - {0xF6, 0xf, 0}, - {0xF7, 0xbc, 0}, - {0xF8, 0x8, 0}, - {0xF9, 0x60, 0}, - {0xFA, 0x13, 1}, - {0xFB, 0x70, 0}, - {0xFC, 0, 0}, - {0xFD, 0, 0}, - {0xFE, 0, 0}, - {0xFF, 0x33, 0}, - {0x100, 0x13, 1}, - {0x101, 0x14, 1}, - {0x102, 0xee, 1}, - {0x105, 0x3c, 0}, - {0x106, 0x1, 1}, - {0x107, 0xa, 0}, - {0x108, 0x9d, 0}, - {0x109, 0xa, 0}, - {0x10A, 0, 0}, - {0x10B, 0x40, 0}, - {0x10C, 0x40, 0}, - {0x10D, 0x88, 0}, - {0x10E, 0x10, 0}, - {0x10F, 0xf0, 0}, - {0x110, 0x10, 0}, - {0x111, 0xf0, 0}, - {0x112, 0, 0}, - {0x113, 0, 0}, - {0x114, 0x10, 0}, - {0x115, 0x55, 0}, - {0x116, 0x3f, 1}, - {0x117, 0x36, 1}, - {0x118, 0, 0}, - {0x119, 0, 0}, - {0x11A, 0, 0}, - {0x11B, 0x87, 0}, - {0x11C, 0x11, 0}, - {0x11D, 0, 0}, - {0x11E, 0x33, 0}, - {0x11F, 0x88, 0}, - {0x120, 0, 0}, - {0x121, 0x87, 0}, - {0x122, 0x11, 0}, - {0x123, 0, 0}, - {0x124, 0x33, 0}, - {0x125, 0x88, 0}, - {0x126, 0x20, 1}, - {0x127, 0x3f, 0}, - {0x128, 0x44, 0}, - {0x129, 0x8c, 0}, - {0x12A, 0x6c, 0}, - {0x12B, 0x22, 0}, - {0x12C, 0xbe, 0}, - {0x12D, 0x55, 0}, - {0x12F, 0xc, 0}, - {0x130, 0xaa, 0}, - {0x131, 0x2, 0}, - {0x132, 0, 0}, - {0x133, 0x10, 0}, - {0x134, 0x1, 0}, - {0x135, 0, 0}, - {0x136, 0, 0}, - {0x137, 0x80, 0}, - {0x138, 0x60, 0}, - {0x139, 0x44, 0}, - {0x13A, 0x55, 0}, - {0x13B, 0x1, 0}, - {0x13C, 0x55, 0}, - {0x13D, 0x1, 0}, - {0x13E, 0x5, 0}, - {0x13F, 0x55, 0}, - {0x140, 0x55, 0}, - {0x146, 0, 0}, - {0x147, 0, 0}, - {0x148, 0, 0}, - {0x149, 0, 0}, - {0x14A, 0, 0}, - {0x14B, 0, 0}, - {0x14C, 0, 0}, - {0x14D, 0, 0}, - {0x14E, 0, 0}, - {0x14F, 0, 0}, - {0x150, 0, 0}, - {0x151, 0, 0}, - {0x154, 0xc, 0}, - {0x155, 0xc, 0}, - {0x156, 0xc, 0}, - {0x157, 0, 0}, - {0x158, 0x2b, 0}, - {0x159, 0x84, 0}, - {0x15A, 0x15, 0}, - {0x15B, 0xf, 0}, - {0x15C, 0, 0}, - {0x15D, 0, 0}, - {0x15E, 0, 1}, - {0x15F, 0, 1}, - {0x160, 0, 1}, - {0x161, 0, 1}, - {0x162, 0, 1}, - {0x163, 0, 1}, - {0x164, 0, 0}, - {0x165, 0, 0}, - {0x166, 0, 0}, - {0x167, 0, 0}, - {0x168, 0, 0}, - {0x169, 0, 0}, - {0x16A, 0, 1}, - {0x16B, 0, 1}, - {0x16C, 0, 1}, - {0x16D, 0, 0}, - {0x170, 0, 0}, - {0x171, 0x77, 0}, - {0x172, 0x77, 0}, - {0x173, 0x77, 0}, - {0x174, 0x77, 0}, - {0x175, 0, 0}, - {0x176, 0x3, 0}, - {0x177, 0x37, 0}, - {0x178, 0x3, 0}, - {0x179, 0, 0}, - {0x17A, 0x21, 0}, - {0x17B, 0x21, 0}, - {0x17C, 0, 0}, - {0x17D, 0xaa, 0}, - {0x17E, 0, 0}, - {0x17F, 0xaa, 0}, - {0x180, 0, 0}, - {0x190, 0, 0}, - {0x191, 0x77, 0}, - {0x192, 0x77, 0}, - {0x193, 0x77, 0}, - {0x194, 0x77, 0}, - {0x195, 0, 0}, - {0x196, 0x3, 0}, - {0x197, 0x37, 0}, - {0x198, 0x3, 0}, - {0x199, 0, 0}, - {0x19A, 0x21, 0}, - {0x19B, 0x21, 0}, - {0x19C, 0, 0}, - {0x19D, 0xaa, 0}, - {0x19E, 0, 0}, - {0x19F, 0xaa, 0}, - {0x1A0, 0, 0}, - {0x1A1, 0x2, 0}, - {0x1A2, 0xf, 0}, - {0x1A3, 0xf, 0}, - {0x1A4, 0, 1}, - {0x1A5, 0, 1}, - {0x1A6, 0, 1}, - {0x1A7, 0x2, 0}, - {0x1A8, 0xf, 0}, - {0x1A9, 0xf, 0}, - {0x1AA, 0, 1}, - {0x1AB, 0, 1}, - {0x1AC, 0, 1}, - {0x1AD, 0x84, 0}, - {0x1AE, 0x60, 0}, - {0x1AF, 0x47, 0}, - {0x1B0, 0x47, 0}, - {0x1B1, 0, 0}, - {0x1B2, 0, 0}, - {0x1B3, 0, 0}, - {0x1B4, 0, 0}, - {0x1B5, 0, 0}, - {0x1B6, 0, 0}, - {0x1B7, 0x5, 1}, - {0x1B8, 0, 0}, - {0x1B9, 0, 0}, - {0x1BA, 0, 0}, - {0x1BB, 0, 0}, - {0x1BC, 0, 0}, - {0x1BD, 0, 0}, - {0x1BE, 0, 0}, - {0x1BF, 0, 0}, - {0x1C0, 0, 0}, - {0x1C1, 0, 0}, - {0x1C2, 0xa0, 1}, - {0x1C3, 0, 0}, - {0x1C4, 0, 0}, - {0x1C5, 0, 0}, - {0x1C6, 0, 0}, - {0x1C7, 0, 0}, - {0x1C8, 0, 0}, - {0x1C9, 0, 0}, - {0x1CA, 0, 0}, - {0xFFFF, 0, 0} -}; - -static struct radio_20xx_regs regs_2057_rev8[] = { - {0x00, 0x8, 1}, - {0x01, 0x57, 1}, - {0x02, 0x20, 1}, - {0x03, 0x1f, 0}, - {0x04, 0x4, 0}, - {0x05, 0x2, 0}, - {0x06, 0x1, 0}, - {0x07, 0x1, 0}, - {0x08, 0x1, 0}, - {0x09, 0x69, 0}, - {0x0A, 0x66, 0}, - {0x0B, 0x6, 0}, - {0x0C, 0x18, 0}, - {0x0D, 0x3, 0}, - {0x0E, 0x20, 0}, - {0x0F, 0x20, 0}, - {0x10, 0, 0}, - {0x11, 0x7c, 0}, - {0x12, 0x42, 0}, - {0x13, 0xbd, 0}, - {0x14, 0x7, 0}, - {0x15, 0x87, 0}, - {0x16, 0x8, 0}, - {0x17, 0x17, 0}, - {0x18, 0x7, 0}, - {0x19, 0, 0}, - {0x1A, 0x2, 0}, - {0x1B, 0x13, 0}, - {0x1C, 0x3e, 0}, - {0x1D, 0x3e, 0}, - {0x1E, 0x96, 0}, - {0x1F, 0x4, 0}, - {0x20, 0, 0}, - {0x21, 0, 0}, - {0x22, 0x17, 0}, - {0x23, 0x6, 0}, - {0x24, 0x1, 0}, - {0x25, 0x6, 0}, - {0x26, 0x4, 0}, - {0x27, 0xd, 0}, - {0x28, 0xd, 0}, - {0x29, 0x30, 0}, - {0x2A, 0x32, 0}, - {0x2B, 0x8, 0}, - {0x2C, 0x1c, 0}, - {0x2D, 0x2, 0}, - {0x2E, 0x4, 0}, - {0x2F, 0x7f, 0}, - {0x30, 0x27, 0}, - {0x31, 0, 1}, - {0x32, 0, 1}, - {0x33, 0, 1}, - {0x34, 0, 0}, - {0x35, 0x20, 0}, - {0x36, 0x18, 0}, - {0x37, 0x7, 0}, - {0x38, 0x66, 0}, - {0x39, 0x66, 0}, - {0x3A, 0x66, 0}, - {0x3B, 0x66, 0}, - {0x3C, 0xff, 0}, - {0x3D, 0xff, 0}, - {0x3E, 0xff, 0}, - {0x3F, 0xff, 0}, - {0x40, 0x16, 0}, - {0x41, 0x7, 0}, - {0x42, 0x19, 0}, - {0x43, 0x7, 0}, - {0x44, 0x6, 0}, - {0x45, 0x3, 0}, - {0x46, 0x1, 0}, - {0x47, 0x7, 0}, - {0x48, 0x33, 0}, - {0x49, 0x5, 0}, - {0x4A, 0x77, 0}, - {0x4B, 0x66, 0}, - {0x4C, 0x66, 0}, - {0x4D, 0, 0}, - {0x4E, 0x4, 0}, - {0x4F, 0xc, 0}, - {0x50, 0, 0}, - {0x51, 0x70, 1}, - {0x56, 0x7, 0}, - {0x57, 0, 0}, - {0x58, 0, 0}, - {0x59, 0x88, 1}, - {0x5A, 0, 0}, - {0x5B, 0x1f, 0}, - {0x5C, 0x20, 1}, - {0x5D, 0x1, 0}, - {0x5E, 0x30, 0}, - {0x5F, 0x70, 0}, - {0x60, 0, 0}, - {0x61, 0, 0}, - {0x62, 0x33, 1}, - {0x63, 0xf, 1}, - {0x64, 0xf, 1}, - {0x65, 0, 0}, - {0x66, 0x11, 0}, - {0x69, 0, 0}, - {0x6A, 0x7e, 0}, - {0x6B, 0x3f, 0}, - {0x6C, 0x7f, 0}, - {0x6D, 0x78, 0}, - {0x6E, 0x58, 1}, - {0x6F, 0x88, 0}, - {0x70, 0x8, 0}, - {0x71, 0xf, 0}, - {0x72, 0xbc, 0}, - {0x73, 0x8, 0}, - {0x74, 0x60, 0}, - {0x75, 0x13, 1}, - {0x76, 0x70, 0}, - {0x77, 0, 0}, - {0x78, 0, 0}, - {0x79, 0, 0}, - {0x7A, 0x33, 0}, - {0x7B, 0x13, 1}, - {0x7C, 0xf, 1}, - {0x7D, 0xee, 1}, - {0x80, 0x3c, 0}, - {0x81, 0x1, 1}, - {0x82, 0xa, 0}, - {0x83, 0x9d, 0}, - {0x84, 0xa, 0}, - {0x85, 0, 0}, - {0x86, 0x40, 0}, - {0x87, 0x40, 0}, - {0x88, 0x88, 0}, - {0x89, 0x10, 0}, - {0x8A, 0xf0, 0}, - {0x8B, 0x10, 0}, - {0x8C, 0xf0, 0}, - {0x8D, 0, 0}, - {0x8E, 0, 0}, - {0x8F, 0x10, 0}, - {0x90, 0x55, 0}, - {0x91, 0x3f, 1}, - {0x92, 0x36, 1}, - {0x93, 0, 0}, - {0x94, 0, 0}, - {0x95, 0, 0}, - {0x96, 0x87, 0}, - {0x97, 0x11, 0}, - {0x98, 0, 0}, - {0x99, 0x33, 0}, - {0x9A, 0x88, 0}, - {0x9B, 0, 0}, - {0x9C, 0x87, 0}, - {0x9D, 0x11, 0}, - {0x9E, 0, 0}, - {0x9F, 0x33, 0}, - {0xA0, 0x88, 0}, - {0xA1, 0x20, 1}, - {0xA2, 0x3f, 0}, - {0xA3, 0x44, 0}, - {0xA4, 0x8c, 0}, - {0xA5, 0x6c, 0}, - {0xA6, 0x22, 0}, - {0xA7, 0xbe, 0}, - {0xA8, 0x55, 0}, - {0xAA, 0xc, 0}, - {0xAB, 0xaa, 0}, - {0xAC, 0x2, 0}, - {0xAD, 0, 0}, - {0xAE, 0x10, 0}, - {0xAF, 0x1, 0}, - {0xB0, 0, 0}, - {0xB1, 0, 0}, - {0xB2, 0x80, 0}, - {0xB3, 0x60, 0}, - {0xB4, 0x44, 0}, - {0xB5, 0x55, 0}, - {0xB6, 0x1, 0}, - {0xB7, 0x55, 0}, - {0xB8, 0x1, 0}, - {0xB9, 0x5, 0}, - {0xBA, 0x55, 0}, - {0xBB, 0x55, 0}, - {0xC1, 0, 0}, - {0xC2, 0, 0}, - {0xC3, 0, 0}, - {0xC4, 0, 0}, - {0xC5, 0, 0}, - {0xC6, 0, 0}, - {0xC7, 0, 0}, - {0xC8, 0, 0}, - {0xC9, 0x1, 1}, - {0xCA, 0, 0}, - {0xCB, 0, 0}, - {0xCC, 0, 0}, - {0xCD, 0, 0}, - {0xCE, 0x5e, 0}, - {0xCF, 0xc, 0}, - {0xD0, 0xc, 0}, - {0xD1, 0xc, 0}, - {0xD2, 0, 0}, - {0xD3, 0x2b, 0}, - {0xD4, 0xc, 0}, - {0xD5, 0, 0}, - {0xD6, 0x70, 1}, - {0xDB, 0x7, 0}, - {0xDC, 0, 0}, - {0xDD, 0, 0}, - {0xDE, 0x88, 1}, - {0xDF, 0, 0}, - {0xE0, 0x1f, 0}, - {0xE1, 0x20, 1}, - {0xE2, 0x1, 0}, - {0xE3, 0x30, 0}, - {0xE4, 0x70, 0}, - {0xE5, 0, 0}, - {0xE6, 0, 0}, - {0xE7, 0x33, 0}, - {0xE8, 0xf, 1}, - {0xE9, 0xf, 1}, - {0xEA, 0, 0}, - {0xEB, 0x11, 0}, - {0xEE, 0, 0}, - {0xEF, 0x7e, 0}, - {0xF0, 0x3f, 0}, - {0xF1, 0x7f, 0}, - {0xF2, 0x78, 0}, - {0xF3, 0x58, 1}, - {0xF4, 0x88, 0}, - {0xF5, 0x8, 0}, - {0xF6, 0xf, 0}, - {0xF7, 0xbc, 0}, - {0xF8, 0x8, 0}, - {0xF9, 0x60, 0}, - {0xFA, 0x13, 1}, - {0xFB, 0x70, 0}, - {0xFC, 0, 0}, - {0xFD, 0, 0}, - {0xFE, 0, 0}, - {0xFF, 0x33, 0}, - {0x100, 0x13, 1}, - {0x101, 0xf, 1}, - {0x102, 0xee, 1}, - {0x105, 0x3c, 0}, - {0x106, 0x1, 1}, - {0x107, 0xa, 0}, - {0x108, 0x9d, 0}, - {0x109, 0xa, 0}, - {0x10A, 0, 0}, - {0x10B, 0x40, 0}, - {0x10C, 0x40, 0}, - {0x10D, 0x88, 0}, - {0x10E, 0x10, 0}, - {0x10F, 0xf0, 0}, - {0x110, 0x10, 0}, - {0x111, 0xf0, 0}, - {0x112, 0, 0}, - {0x113, 0, 0}, - {0x114, 0x10, 0}, - {0x115, 0x55, 0}, - {0x116, 0x3f, 1}, - {0x117, 0x36, 1}, - {0x118, 0, 0}, - {0x119, 0, 0}, - {0x11A, 0, 0}, - {0x11B, 0x87, 0}, - {0x11C, 0x11, 0}, - {0x11D, 0, 0}, - {0x11E, 0x33, 0}, - {0x11F, 0x88, 0}, - {0x120, 0, 0}, - {0x121, 0x87, 0}, - {0x122, 0x11, 0}, - {0x123, 0, 0}, - {0x124, 0x33, 0}, - {0x125, 0x88, 0}, - {0x126, 0x20, 1}, - {0x127, 0x3f, 0}, - {0x128, 0x44, 0}, - {0x129, 0x8c, 0}, - {0x12A, 0x6c, 0}, - {0x12B, 0x22, 0}, - {0x12C, 0xbe, 0}, - {0x12D, 0x55, 0}, - {0x12F, 0xc, 0}, - {0x130, 0xaa, 0}, - {0x131, 0x2, 0}, - {0x132, 0, 0}, - {0x133, 0x10, 0}, - {0x134, 0x1, 0}, - {0x135, 0, 0}, - {0x136, 0, 0}, - {0x137, 0x80, 0}, - {0x138, 0x60, 0}, - {0x139, 0x44, 0}, - {0x13A, 0x55, 0}, - {0x13B, 0x1, 0}, - {0x13C, 0x55, 0}, - {0x13D, 0x1, 0}, - {0x13E, 0x5, 0}, - {0x13F, 0x55, 0}, - {0x140, 0x55, 0}, - {0x146, 0, 0}, - {0x147, 0, 0}, - {0x148, 0, 0}, - {0x149, 0, 0}, - {0x14A, 0, 0}, - {0x14B, 0, 0}, - {0x14C, 0, 0}, - {0x14D, 0, 0}, - {0x14E, 0x1, 1}, - {0x14F, 0, 0}, - {0x150, 0, 0}, - {0x151, 0, 0}, - {0x154, 0xc, 0}, - {0x155, 0xc, 0}, - {0x156, 0xc, 0}, - {0x157, 0, 0}, - {0x158, 0x2b, 0}, - {0x159, 0x84, 0}, - {0x15A, 0x15, 0}, - {0x15B, 0xf, 0}, - {0x15C, 0, 0}, - {0x15D, 0, 0}, - {0x15E, 0, 1}, - {0x15F, 0, 1}, - {0x160, 0, 1}, - {0x161, 0, 1}, - {0x162, 0, 1}, - {0x163, 0, 1}, - {0x164, 0, 0}, - {0x165, 0, 0}, - {0x166, 0, 0}, - {0x167, 0, 0}, - {0x168, 0, 0}, - {0x169, 0, 0}, - {0x16A, 0, 1}, - {0x16B, 0, 1}, - {0x16C, 0, 1}, - {0x16D, 0, 0}, - {0x170, 0, 0}, - {0x171, 0x77, 0}, - {0x172, 0x77, 0}, - {0x173, 0x77, 0}, - {0x174, 0x77, 0}, - {0x175, 0, 0}, - {0x176, 0x3, 0}, - {0x177, 0x37, 0}, - {0x178, 0x3, 0}, - {0x179, 0, 0}, - {0x17A, 0x21, 0}, - {0x17B, 0x21, 0}, - {0x17C, 0, 0}, - {0x17D, 0xaa, 0}, - {0x17E, 0, 0}, - {0x17F, 0xaa, 0}, - {0x180, 0, 0}, - {0x190, 0, 0}, - {0x191, 0x77, 0}, - {0x192, 0x77, 0}, - {0x193, 0x77, 0}, - {0x194, 0x77, 0}, - {0x195, 0, 0}, - {0x196, 0x3, 0}, - {0x197, 0x37, 0}, - {0x198, 0x3, 0}, - {0x199, 0, 0}, - {0x19A, 0x21, 0}, - {0x19B, 0x21, 0}, - {0x19C, 0, 0}, - {0x19D, 0xaa, 0}, - {0x19E, 0, 0}, - {0x19F, 0xaa, 0}, - {0x1A0, 0, 0}, - {0x1A1, 0x2, 0}, - {0x1A2, 0xf, 0}, - {0x1A3, 0xf, 0}, - {0x1A4, 0, 1}, - {0x1A5, 0, 1}, - {0x1A6, 0, 1}, - {0x1A7, 0x2, 0}, - {0x1A8, 0xf, 0}, - {0x1A9, 0xf, 0}, - {0x1AA, 0, 1}, - {0x1AB, 0, 1}, - {0x1AC, 0, 1}, - {0x1AD, 0x84, 0}, - {0x1AE, 0x60, 0}, - {0x1AF, 0x47, 0}, - {0x1B0, 0x47, 0}, - {0x1B1, 0, 0}, - {0x1B2, 0, 0}, - {0x1B3, 0, 0}, - {0x1B4, 0, 0}, - {0x1B5, 0, 0}, - {0x1B6, 0, 0}, - {0x1B7, 0x5, 1}, - {0x1B8, 0, 0}, - {0x1B9, 0, 0}, - {0x1BA, 0, 0}, - {0x1BB, 0, 0}, - {0x1BC, 0, 0}, - {0x1BD, 0, 0}, - {0x1BE, 0, 0}, - {0x1BF, 0, 0}, - {0x1C0, 0, 0}, - {0x1C1, 0, 0}, - {0x1C2, 0xa0, 1}, - {0x1C3, 0, 0}, - {0x1C4, 0, 0}, - {0x1C5, 0, 0}, - {0x1C6, 0, 0}, - {0x1C7, 0, 0}, - {0x1C8, 0, 0}, - {0x1C9, 0, 0}, - {0x1CA, 0, 0}, - {0xFFFF, 0, 0} -}; - -static s16 nphy_def_lnagains[] = { -2, 10, 19, 25 }; - -static s32 nphy_lnagain_est0[] = { -315, 40370 }; -static s32 nphy_lnagain_est1[] = { -224, 23242 }; - -static const u16 tbl_iqcal_gainparams_nphy[2][NPHY_IQCAL_NUMGAINS][8] = { - { - {0x000, 0, 0, 2, 0x69, 0x69, 0x69, 0x69}, - {0x700, 7, 0, 0, 0x69, 0x69, 0x69, 0x69}, - {0x710, 7, 1, 0, 0x68, 0x68, 0x68, 0x68}, - {0x720, 7, 2, 0, 0x67, 0x67, 0x67, 0x67}, - {0x730, 7, 3, 0, 0x66, 0x66, 0x66, 0x66}, - {0x740, 7, 4, 0, 0x65, 0x65, 0x65, 0x65}, - {0x741, 7, 4, 1, 0x65, 0x65, 0x65, 0x65}, - {0x742, 7, 4, 2, 0x65, 0x65, 0x65, 0x65}, - {0x743, 7, 4, 3, 0x65, 0x65, 0x65, 0x65} - }, - { - {0x000, 7, 0, 0, 0x79, 0x79, 0x79, 0x79}, - {0x700, 7, 0, 0, 0x79, 0x79, 0x79, 0x79}, - {0x710, 7, 1, 0, 0x79, 0x79, 0x79, 0x79}, - {0x720, 7, 2, 0, 0x78, 0x78, 0x78, 0x78}, - {0x730, 7, 3, 0, 0x78, 0x78, 0x78, 0x78}, - {0x740, 7, 4, 0, 0x78, 0x78, 0x78, 0x78}, - {0x741, 7, 4, 1, 0x78, 0x78, 0x78, 0x78}, - {0x742, 7, 4, 2, 0x78, 0x78, 0x78, 0x78}, - {0x743, 7, 4, 3, 0x78, 0x78, 0x78, 0x78} - } -}; - -static const u32 nphy_tpc_txgain[] = { - 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, - 0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44, - 0x03c82a42, 0x03c82944, 0x03c82942, 0x03c82844, - 0x03c82842, 0x03c42b44, 0x03c42b42, 0x03c42a44, - 0x03c42a42, 0x03c42944, 0x03c42942, 0x03c42844, - 0x03c42842, 0x03c42744, 0x03c42742, 0x03c42644, - 0x03c42642, 0x03c42544, 0x03c42542, 0x03c42444, - 0x03c42442, 0x03c02b44, 0x03c02b42, 0x03c02a44, - 0x03c02a42, 0x03c02944, 0x03c02942, 0x03c02844, - 0x03c02842, 0x03c02744, 0x03c02742, 0x03b02b44, - 0x03b02b42, 0x03b02a44, 0x03b02a42, 0x03b02944, - 0x03b02942, 0x03b02844, 0x03b02842, 0x03b02744, - 0x03b02742, 0x03b02644, 0x03b02642, 0x03b02544, - 0x03b02542, 0x03a02b44, 0x03a02b42, 0x03a02a44, - 0x03a02a42, 0x03a02944, 0x03a02942, 0x03a02844, - 0x03a02842, 0x03a02744, 0x03a02742, 0x03902b44, - 0x03902b42, 0x03902a44, 0x03902a42, 0x03902944, - 0x03902942, 0x03902844, 0x03902842, 0x03902744, - 0x03902742, 0x03902644, 0x03902642, 0x03902544, - 0x03902542, 0x03802b44, 0x03802b42, 0x03802a44, - 0x03802a42, 0x03802944, 0x03802942, 0x03802844, - 0x03802842, 0x03802744, 0x03802742, 0x03802644, - 0x03802642, 0x03802544, 0x03802542, 0x03802444, - 0x03802442, 0x03802344, 0x03802342, 0x03802244, - 0x03802242, 0x03802144, 0x03802142, 0x03802044, - 0x03802042, 0x03801f44, 0x03801f42, 0x03801e44, - 0x03801e42, 0x03801d44, 0x03801d42, 0x03801c44, - 0x03801c42, 0x03801b44, 0x03801b42, 0x03801a44, - 0x03801a42, 0x03801944, 0x03801942, 0x03801844, - 0x03801842, 0x03801744, 0x03801742, 0x03801644, - 0x03801642, 0x03801544, 0x03801542, 0x03801444, - 0x03801442, 0x03801344, 0x03801342, 0x00002b00 -}; - -static const u16 nphy_tpc_loscale[] = { - 256, 256, 271, 271, 287, 256, 256, 271, - 271, 287, 287, 304, 304, 256, 256, 271, - 271, 287, 287, 304, 304, 322, 322, 341, - 341, 362, 362, 383, 383, 256, 256, 271, - 271, 287, 287, 304, 304, 322, 322, 256, - 256, 271, 271, 287, 287, 304, 304, 322, - 322, 341, 341, 362, 362, 256, 256, 271, - 271, 287, 287, 304, 304, 322, 322, 256, - 256, 271, 271, 287, 287, 304, 304, 322, - 322, 341, 341, 362, 362, 256, 256, 271, - 271, 287, 287, 304, 304, 322, 322, 341, - 341, 362, 362, 383, 383, 406, 406, 430, - 430, 455, 455, 482, 482, 511, 511, 541, - 541, 573, 573, 607, 607, 643, 643, 681, - 681, 722, 722, 764, 764, 810, 810, 858, - 858, 908, 908, 962, 962, 1019, 1019, 256 -}; - -static u32 nphy_tpc_txgain_ipa[] = { - 0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029, - 0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025, - 0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029, - 0x5ef70028, 0x5ef70027, 0x5ef70026, 0x5ef70025, - 0x5df7002d, 0x5df7002b, 0x5df7002a, 0x5df70029, - 0x5df70028, 0x5df70027, 0x5df70026, 0x5df70025, - 0x5cf7002d, 0x5cf7002b, 0x5cf7002a, 0x5cf70029, - 0x5cf70028, 0x5cf70027, 0x5cf70026, 0x5cf70025, - 0x5bf7002d, 0x5bf7002b, 0x5bf7002a, 0x5bf70029, - 0x5bf70028, 0x5bf70027, 0x5bf70026, 0x5bf70025, - 0x5af7002d, 0x5af7002b, 0x5af7002a, 0x5af70029, - 0x5af70028, 0x5af70027, 0x5af70026, 0x5af70025, - 0x59f7002d, 0x59f7002b, 0x59f7002a, 0x59f70029, - 0x59f70028, 0x59f70027, 0x59f70026, 0x59f70025, - 0x58f7002d, 0x58f7002b, 0x58f7002a, 0x58f70029, - 0x58f70028, 0x58f70027, 0x58f70026, 0x58f70025, - 0x57f7002d, 0x57f7002b, 0x57f7002a, 0x57f70029, - 0x57f70028, 0x57f70027, 0x57f70026, 0x57f70025, - 0x56f7002d, 0x56f7002b, 0x56f7002a, 0x56f70029, - 0x56f70028, 0x56f70027, 0x56f70026, 0x56f70025, - 0x55f7002d, 0x55f7002b, 0x55f7002a, 0x55f70029, - 0x55f70028, 0x55f70027, 0x55f70026, 0x55f70025, - 0x54f7002d, 0x54f7002b, 0x54f7002a, 0x54f70029, - 0x54f70028, 0x54f70027, 0x54f70026, 0x54f70025, - 0x53f7002d, 0x53f7002b, 0x53f7002a, 0x53f70029, - 0x53f70028, 0x53f70027, 0x53f70026, 0x53f70025, - 0x52f7002d, 0x52f7002b, 0x52f7002a, 0x52f70029, - 0x52f70028, 0x52f70027, 0x52f70026, 0x52f70025, - 0x51f7002d, 0x51f7002b, 0x51f7002a, 0x51f70029, - 0x51f70028, 0x51f70027, 0x51f70026, 0x51f70025, - 0x50f7002d, 0x50f7002b, 0x50f7002a, 0x50f70029, - 0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025 -}; - -static u32 nphy_tpc_txgain_ipa_rev5[] = { - 0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029, - 0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025, - 0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029, - 0x1ef70028, 0x1ef70027, 0x1ef70026, 0x1ef70025, - 0x1df7002d, 0x1df7002b, 0x1df7002a, 0x1df70029, - 0x1df70028, 0x1df70027, 0x1df70026, 0x1df70025, - 0x1cf7002d, 0x1cf7002b, 0x1cf7002a, 0x1cf70029, - 0x1cf70028, 0x1cf70027, 0x1cf70026, 0x1cf70025, - 0x1bf7002d, 0x1bf7002b, 0x1bf7002a, 0x1bf70029, - 0x1bf70028, 0x1bf70027, 0x1bf70026, 0x1bf70025, - 0x1af7002d, 0x1af7002b, 0x1af7002a, 0x1af70029, - 0x1af70028, 0x1af70027, 0x1af70026, 0x1af70025, - 0x19f7002d, 0x19f7002b, 0x19f7002a, 0x19f70029, - 0x19f70028, 0x19f70027, 0x19f70026, 0x19f70025, - 0x18f7002d, 0x18f7002b, 0x18f7002a, 0x18f70029, - 0x18f70028, 0x18f70027, 0x18f70026, 0x18f70025, - 0x17f7002d, 0x17f7002b, 0x17f7002a, 0x17f70029, - 0x17f70028, 0x17f70027, 0x17f70026, 0x17f70025, - 0x16f7002d, 0x16f7002b, 0x16f7002a, 0x16f70029, - 0x16f70028, 0x16f70027, 0x16f70026, 0x16f70025, - 0x15f7002d, 0x15f7002b, 0x15f7002a, 0x15f70029, - 0x15f70028, 0x15f70027, 0x15f70026, 0x15f70025, - 0x14f7002d, 0x14f7002b, 0x14f7002a, 0x14f70029, - 0x14f70028, 0x14f70027, 0x14f70026, 0x14f70025, - 0x13f7002d, 0x13f7002b, 0x13f7002a, 0x13f70029, - 0x13f70028, 0x13f70027, 0x13f70026, 0x13f70025, - 0x12f7002d, 0x12f7002b, 0x12f7002a, 0x12f70029, - 0x12f70028, 0x12f70027, 0x12f70026, 0x12f70025, - 0x11f7002d, 0x11f7002b, 0x11f7002a, 0x11f70029, - 0x11f70028, 0x11f70027, 0x11f70026, 0x11f70025, - 0x10f7002d, 0x10f7002b, 0x10f7002a, 0x10f70029, - 0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025 -}; - -static u32 nphy_tpc_txgain_ipa_rev6[] = { - 0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029, - 0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025, - 0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029, - 0x0ef70028, 0x0ef70027, 0x0ef70026, 0x0ef70025, - 0x0df7002d, 0x0df7002b, 0x0df7002a, 0x0df70029, - 0x0df70028, 0x0df70027, 0x0df70026, 0x0df70025, - 0x0cf7002d, 0x0cf7002b, 0x0cf7002a, 0x0cf70029, - 0x0cf70028, 0x0cf70027, 0x0cf70026, 0x0cf70025, - 0x0bf7002d, 0x0bf7002b, 0x0bf7002a, 0x0bf70029, - 0x0bf70028, 0x0bf70027, 0x0bf70026, 0x0bf70025, - 0x0af7002d, 0x0af7002b, 0x0af7002a, 0x0af70029, - 0x0af70028, 0x0af70027, 0x0af70026, 0x0af70025, - 0x09f7002d, 0x09f7002b, 0x09f7002a, 0x09f70029, - 0x09f70028, 0x09f70027, 0x09f70026, 0x09f70025, - 0x08f7002d, 0x08f7002b, 0x08f7002a, 0x08f70029, - 0x08f70028, 0x08f70027, 0x08f70026, 0x08f70025, - 0x07f7002d, 0x07f7002b, 0x07f7002a, 0x07f70029, - 0x07f70028, 0x07f70027, 0x07f70026, 0x07f70025, - 0x06f7002d, 0x06f7002b, 0x06f7002a, 0x06f70029, - 0x06f70028, 0x06f70027, 0x06f70026, 0x06f70025, - 0x05f7002d, 0x05f7002b, 0x05f7002a, 0x05f70029, - 0x05f70028, 0x05f70027, 0x05f70026, 0x05f70025, - 0x04f7002d, 0x04f7002b, 0x04f7002a, 0x04f70029, - 0x04f70028, 0x04f70027, 0x04f70026, 0x04f70025, - 0x03f7002d, 0x03f7002b, 0x03f7002a, 0x03f70029, - 0x03f70028, 0x03f70027, 0x03f70026, 0x03f70025, - 0x02f7002d, 0x02f7002b, 0x02f7002a, 0x02f70029, - 0x02f70028, 0x02f70027, 0x02f70026, 0x02f70025, - 0x01f7002d, 0x01f7002b, 0x01f7002a, 0x01f70029, - 0x01f70028, 0x01f70027, 0x01f70026, 0x01f70025, - 0x00f7002d, 0x00f7002b, 0x00f7002a, 0x00f70029, - 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025 -}; - -static u32 nphy_tpc_txgain_ipa_2g_2057rev3[] = { - 0x70ff0040, 0x70f7003e, 0x70ef003b, 0x70e70039, - 0x70df0037, 0x70d70036, 0x70cf0033, 0x70c70032, - 0x70bf0031, 0x70b7002f, 0x70af002e, 0x70a7002d, - 0x709f002d, 0x7097002c, 0x708f002c, 0x7087002c, - 0x707f002b, 0x7077002c, 0x706f002c, 0x7067002d, - 0x705f002e, 0x705f002b, 0x705f0029, 0x7057002a, - 0x70570028, 0x704f002a, 0x7047002c, 0x7047002a, - 0x70470028, 0x70470026, 0x70470024, 0x70470022, - 0x7047001f, 0x70370027, 0x70370024, 0x70370022, - 0x70370020, 0x7037001f, 0x7037001d, 0x7037001b, - 0x7037001a, 0x70370018, 0x70370017, 0x7027001e, - 0x7027001d, 0x7027001a, 0x701f0024, 0x701f0022, - 0x701f0020, 0x701f001f, 0x701f001d, 0x701f001b, - 0x701f001a, 0x701f0018, 0x701f0017, 0x701f0015, - 0x701f0014, 0x701f0013, 0x701f0012, 0x701f0011, - 0x70170019, 0x70170018, 0x70170016, 0x70170015, - 0x70170014, 0x70170013, 0x70170012, 0x70170010, - 0x70170010, 0x7017000f, 0x700f001d, 0x700f001b, - 0x700f001a, 0x700f0018, 0x700f0017, 0x700f0015, - 0x700f0015, 0x700f0013, 0x700f0013, 0x700f0011, - 0x700f0010, 0x700f0010, 0x700f000f, 0x700f000e, - 0x700f000d, 0x700f000c, 0x700f000b, 0x700f000b, - 0x700f000b, 0x700f000a, 0x700f0009, 0x700f0009, - 0x700f0009, 0x700f0008, 0x700f0007, 0x700f0007, - 0x700f0006, 0x700f0006, 0x700f0006, 0x700f0006, - 0x700f0005, 0x700f0005, 0x700f0005, 0x700f0004, - 0x700f0004, 0x700f0004, 0x700f0004, 0x700f0004, - 0x700f0004, 0x700f0003, 0x700f0003, 0x700f0003, - 0x700f0003, 0x700f0002, 0x700f0002, 0x700f0002, - 0x700f0002, 0x700f0002, 0x700f0002, 0x700f0001, - 0x700f0001, 0x700f0001, 0x700f0001, 0x700f0001, - 0x700f0001, 0x700f0001, 0x700f0001, 0x700f0001 -}; - -static u32 nphy_tpc_txgain_ipa_2g_2057rev4n6[] = { - 0xf0ff0040, 0xf0f7003e, 0xf0ef003b, 0xf0e70039, - 0xf0df0037, 0xf0d70036, 0xf0cf0033, 0xf0c70032, - 0xf0bf0031, 0xf0b7002f, 0xf0af002e, 0xf0a7002d, - 0xf09f002d, 0xf097002c, 0xf08f002c, 0xf087002c, - 0xf07f002b, 0xf077002c, 0xf06f002c, 0xf067002d, - 0xf05f002e, 0xf05f002b, 0xf05f0029, 0xf057002a, - 0xf0570028, 0xf04f002a, 0xf047002c, 0xf047002a, - 0xf0470028, 0xf0470026, 0xf0470024, 0xf0470022, - 0xf047001f, 0xf0370027, 0xf0370024, 0xf0370022, - 0xf0370020, 0xf037001f, 0xf037001d, 0xf037001b, - 0xf037001a, 0xf0370018, 0xf0370017, 0xf027001e, - 0xf027001d, 0xf027001a, 0xf01f0024, 0xf01f0022, - 0xf01f0020, 0xf01f001f, 0xf01f001d, 0xf01f001b, - 0xf01f001a, 0xf01f0018, 0xf01f0017, 0xf01f0015, - 0xf01f0014, 0xf01f0013, 0xf01f0012, 0xf01f0011, - 0xf0170019, 0xf0170018, 0xf0170016, 0xf0170015, - 0xf0170014, 0xf0170013, 0xf0170012, 0xf0170010, - 0xf0170010, 0xf017000f, 0xf00f001d, 0xf00f001b, - 0xf00f001a, 0xf00f0018, 0xf00f0017, 0xf00f0015, - 0xf00f0015, 0xf00f0013, 0xf00f0013, 0xf00f0011, - 0xf00f0010, 0xf00f0010, 0xf00f000f, 0xf00f000e, - 0xf00f000d, 0xf00f000c, 0xf00f000b, 0xf00f000b, - 0xf00f000b, 0xf00f000a, 0xf00f0009, 0xf00f0009, - 0xf00f0009, 0xf00f0008, 0xf00f0007, 0xf00f0007, - 0xf00f0006, 0xf00f0006, 0xf00f0006, 0xf00f0006, - 0xf00f0005, 0xf00f0005, 0xf00f0005, 0xf00f0004, - 0xf00f0004, 0xf00f0004, 0xf00f0004, 0xf00f0004, - 0xf00f0004, 0xf00f0003, 0xf00f0003, 0xf00f0003, - 0xf00f0003, 0xf00f0002, 0xf00f0002, 0xf00f0002, - 0xf00f0002, 0xf00f0002, 0xf00f0002, 0xf00f0001, - 0xf00f0001, 0xf00f0001, 0xf00f0001, 0xf00f0001, - 0xf00f0001, 0xf00f0001, 0xf00f0001, 0xf00f0001 -}; - -static u32 nphy_tpc_txgain_ipa_2g_2057rev5[] = { - 0x30ff0031, 0x30e70031, 0x30e7002e, 0x30cf002e, - 0x30bf002e, 0x30af002e, 0x309f002f, 0x307f0033, - 0x307f0031, 0x307f002e, 0x3077002e, 0x306f002e, - 0x3067002e, 0x305f002f, 0x30570030, 0x3057002d, - 0x304f002e, 0x30470031, 0x3047002e, 0x3047002c, - 0x30470029, 0x303f002c, 0x303f0029, 0x3037002d, - 0x3037002a, 0x30370028, 0x302f002c, 0x302f002a, - 0x302f0028, 0x302f0026, 0x3027002c, 0x30270029, - 0x30270027, 0x30270025, 0x30270023, 0x301f002c, - 0x301f002a, 0x301f0028, 0x301f0025, 0x301f0024, - 0x301f0022, 0x301f001f, 0x3017002d, 0x3017002b, - 0x30170028, 0x30170026, 0x30170024, 0x30170022, - 0x30170020, 0x3017001e, 0x3017001d, 0x3017001b, - 0x3017001a, 0x30170018, 0x30170017, 0x30170015, - 0x300f002c, 0x300f0029, 0x300f0027, 0x300f0024, - 0x300f0022, 0x300f0021, 0x300f001f, 0x300f001d, - 0x300f001b, 0x300f001a, 0x300f0018, 0x300f0017, - 0x300f0016, 0x300f0015, 0x300f0115, 0x300f0215, - 0x300f0315, 0x300f0415, 0x300f0515, 0x300f0615, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715 -}; - -static u32 nphy_tpc_txgain_ipa_2g_2057rev7[] = { - 0x30ff0031, 0x30e70031, 0x30e7002e, 0x30cf002e, - 0x30bf002e, 0x30af002e, 0x309f002f, 0x307f0033, - 0x307f0031, 0x307f002e, 0x3077002e, 0x306f002e, - 0x3067002e, 0x305f002f, 0x30570030, 0x3057002d, - 0x304f002e, 0x30470031, 0x3047002e, 0x3047002c, - 0x30470029, 0x303f002c, 0x303f0029, 0x3037002d, - 0x3037002a, 0x30370028, 0x302f002c, 0x302f002a, - 0x302f0028, 0x302f0026, 0x3027002c, 0x30270029, - 0x30270027, 0x30270025, 0x30270023, 0x301f002c, - 0x301f002a, 0x301f0028, 0x301f0025, 0x301f0024, - 0x301f0022, 0x301f001f, 0x3017002d, 0x3017002b, - 0x30170028, 0x30170026, 0x30170024, 0x30170022, - 0x30170020, 0x3017001e, 0x3017001d, 0x3017001b, - 0x3017001a, 0x30170018, 0x30170017, 0x30170015, - 0x300f002c, 0x300f0029, 0x300f0027, 0x300f0024, - 0x300f0022, 0x300f0021, 0x300f001f, 0x300f001d, - 0x300f001b, 0x300f001a, 0x300f0018, 0x300f0017, - 0x300f0016, 0x300f0015, 0x300f0115, 0x300f0215, - 0x300f0315, 0x300f0415, 0x300f0515, 0x300f0615, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, - 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715 -}; - -static u32 nphy_tpc_txgain_ipa_5g[] = { - 0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031, - 0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b, - 0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027, - 0x7ff70026, 0x7ff70024, 0x7ff70023, 0x7ff70022, - 0x7ef70028, 0x7ef70027, 0x7ef70026, 0x7ef70025, - 0x7ef70024, 0x7ef70023, 0x7df70028, 0x7df70027, - 0x7df70026, 0x7df70025, 0x7df70024, 0x7df70023, - 0x7df70022, 0x7cf70029, 0x7cf70028, 0x7cf70027, - 0x7cf70026, 0x7cf70025, 0x7cf70023, 0x7cf70022, - 0x7bf70029, 0x7bf70028, 0x7bf70026, 0x7bf70025, - 0x7bf70024, 0x7bf70023, 0x7bf70022, 0x7bf70021, - 0x7af70029, 0x7af70028, 0x7af70027, 0x7af70026, - 0x7af70025, 0x7af70024, 0x7af70023, 0x7af70022, - 0x79f70029, 0x79f70028, 0x79f70027, 0x79f70026, - 0x79f70025, 0x79f70024, 0x79f70023, 0x79f70022, - 0x78f70029, 0x78f70028, 0x78f70027, 0x78f70026, - 0x78f70025, 0x78f70024, 0x78f70023, 0x78f70022, - 0x77f70029, 0x77f70028, 0x77f70027, 0x77f70026, - 0x77f70025, 0x77f70024, 0x77f70023, 0x77f70022, - 0x76f70029, 0x76f70028, 0x76f70027, 0x76f70026, - 0x76f70024, 0x76f70023, 0x76f70022, 0x76f70021, - 0x75f70029, 0x75f70028, 0x75f70027, 0x75f70026, - 0x75f70025, 0x75f70024, 0x75f70023, 0x74f70029, - 0x74f70028, 0x74f70026, 0x74f70025, 0x74f70024, - 0x74f70023, 0x74f70022, 0x73f70029, 0x73f70027, - 0x73f70026, 0x73f70025, 0x73f70024, 0x73f70023, - 0x73f70022, 0x72f70028, 0x72f70027, 0x72f70026, - 0x72f70025, 0x72f70024, 0x72f70023, 0x72f70022, - 0x71f70028, 0x71f70027, 0x71f70026, 0x71f70025, - 0x71f70024, 0x71f70023, 0x70f70028, 0x70f70027, - 0x70f70026, 0x70f70024, 0x70f70023, 0x70f70022, - 0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f -}; - -static u32 nphy_tpc_txgain_ipa_5g_2057[] = { - 0x7f7f0044, 0x7f7f0040, 0x7f7f003c, 0x7f7f0039, - 0x7f7f0036, 0x7e7f003c, 0x7e7f0038, 0x7e7f0035, - 0x7d7f003c, 0x7d7f0039, 0x7d7f0036, 0x7d7f0033, - 0x7c7f003b, 0x7c7f0037, 0x7c7f0034, 0x7b7f003a, - 0x7b7f0036, 0x7b7f0033, 0x7a7f003c, 0x7a7f0039, - 0x7a7f0036, 0x7a7f0033, 0x797f003b, 0x797f0038, - 0x797f0035, 0x797f0032, 0x787f003b, 0x787f0038, - 0x787f0035, 0x787f0032, 0x777f003a, 0x777f0037, - 0x777f0034, 0x777f0031, 0x767f003a, 0x767f0036, - 0x767f0033, 0x767f0031, 0x757f003a, 0x757f0037, - 0x757f0034, 0x747f003c, 0x747f0039, 0x747f0036, - 0x747f0033, 0x737f003b, 0x737f0038, 0x737f0035, - 0x737f0032, 0x727f0039, 0x727f0036, 0x727f0033, - 0x727f0030, 0x717f003a, 0x717f0037, 0x717f0034, - 0x707f003b, 0x707f0038, 0x707f0035, 0x707f0032, - 0x707f002f, 0x707f002d, 0x707f002a, 0x707f0028, - 0x707f0025, 0x707f0023, 0x707f0021, 0x707f0020, - 0x707f001e, 0x707f001c, 0x707f001b, 0x707f0019, - 0x707f0018, 0x707f0016, 0x707f0015, 0x707f0014, - 0x707f0013, 0x707f0012, 0x707f0011, 0x707f0010, - 0x707f000f, 0x707f000e, 0x707f000d, 0x707f000d, - 0x707f000c, 0x707f000b, 0x707f000b, 0x707f000a, - 0x707f0009, 0x707f0009, 0x707f0008, 0x707f0008, - 0x707f0007, 0x707f0007, 0x707f0007, 0x707f0006, - 0x707f0006, 0x707f0006, 0x707f0005, 0x707f0005, - 0x707f0005, 0x707f0004, 0x707f0004, 0x707f0004, - 0x707f0004, 0x707f0004, 0x707f0003, 0x707f0003, - 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, - 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, - 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, - 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001, - 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001 -}; - -static u32 nphy_tpc_txgain_ipa_5g_2057rev7[] = { - 0x6f7f0031, 0x6f7f002e, 0x6f7f002c, 0x6f7f002a, - 0x6f7f0027, 0x6e7f002e, 0x6e7f002c, 0x6e7f002a, - 0x6d7f0030, 0x6d7f002d, 0x6d7f002a, 0x6d7f0028, - 0x6c7f0030, 0x6c7f002d, 0x6c7f002b, 0x6b7f002e, - 0x6b7f002c, 0x6b7f002a, 0x6b7f0027, 0x6a7f002e, - 0x6a7f002c, 0x6a7f002a, 0x697f0030, 0x697f002e, - 0x697f002b, 0x697f0029, 0x687f002f, 0x687f002d, - 0x687f002a, 0x687f0027, 0x677f002f, 0x677f002d, - 0x677f002a, 0x667f0031, 0x667f002e, 0x667f002c, - 0x667f002a, 0x657f0030, 0x657f002e, 0x657f002b, - 0x657f0029, 0x647f0030, 0x647f002d, 0x647f002b, - 0x647f0029, 0x637f002f, 0x637f002d, 0x637f002a, - 0x627f0030, 0x627f002d, 0x627f002b, 0x627f0029, - 0x617f0030, 0x617f002e, 0x617f002b, 0x617f0029, - 0x607f002f, 0x607f002d, 0x607f002a, 0x607f0027, - 0x607f0026, 0x607f0023, 0x607f0021, 0x607f0020, - 0x607f001e, 0x607f001c, 0x607f001a, 0x607f0019, - 0x607f0018, 0x607f0016, 0x607f0015, 0x607f0014, - 0x607f0012, 0x607f0012, 0x607f0011, 0x607f000f, - 0x607f000f, 0x607f000e, 0x607f000d, 0x607f000c, - 0x607f000c, 0x607f000b, 0x607f000b, 0x607f000a, - 0x607f0009, 0x607f0009, 0x607f0008, 0x607f0008, - 0x607f0008, 0x607f0007, 0x607f0007, 0x607f0006, - 0x607f0006, 0x607f0005, 0x607f0005, 0x607f0005, - 0x607f0005, 0x607f0005, 0x607f0004, 0x607f0004, - 0x607f0004, 0x607f0004, 0x607f0003, 0x607f0003, - 0x607f0003, 0x607f0003, 0x607f0002, 0x607f0002, - 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, - 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, - 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, - 0x607f0002, 0x607f0001, 0x607f0001, 0x607f0001, - 0x607f0001, 0x607f0001, 0x607f0001, 0x607f0001 -}; - -static s8 nphy_papd_pga_gain_delta_ipa_2g[] = { - -114, -108, -98, -91, -84, -78, -70, -62, - -54, -46, -39, -31, -23, -15, -8, 0 -}; - -static s8 nphy_papd_pga_gain_delta_ipa_5g[] = { - -100, -95, -89, -83, -77, -70, -63, -56, - -48, -41, -33, -25, -19, -12, -6, 0 -}; - -static s16 nphy_papd_padgain_dlt_2g_2057rev3n4[] = { - -159, -113, -86, -72, -62, -54, -48, -43, - -39, -35, -31, -28, -25, -23, -20, -18, - -17, -15, -13, -11, -10, -8, -7, -6, - -5, -4, -3, -3, -2, -1, -1, 0 -}; - -static s16 nphy_papd_padgain_dlt_2g_2057rev5[] = { - -109, -109, -82, -68, -58, -50, -44, -39, - -35, -31, -28, -26, -23, -21, -19, -17, - -16, -14, -13, -11, -10, -9, -8, -7, - -5, -5, -4, -3, -2, -1, -1, 0 -}; - -static s16 nphy_papd_padgain_dlt_2g_2057rev7[] = { - -122, -122, -95, -80, -69, -61, -54, -49, - -43, -39, -35, -32, -28, -26, -23, -21, - -18, -16, -15, -13, -11, -10, -8, -7, - -6, -5, -4, -3, -2, -1, -1, 0 -}; - -static s8 nphy_papd_pgagain_dlt_5g_2057[] = { - -107, -101, -92, -85, -78, -71, -62, -55, - -47, -39, -32, -24, -19, -12, -6, 0 -}; - -static s8 nphy_papd_pgagain_dlt_5g_2057rev7[] = { - -110, -104, -95, -88, -81, -74, -66, -58, - -50, -44, -36, -28, -23, -15, -8, 0 -}; - -static u8 pad_gain_codes_used_2057rev5[] = { - 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, - 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 -}; - -static u8 pad_gain_codes_used_2057rev7[] = { - 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, - 5, 4, 3, 2, 1 -}; - -static u8 pad_all_gain_codes_2057[] = { - 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, - 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, - 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, - 1, 0 -}; - -static u8 pga_all_gain_codes_2057[] = { - 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 -}; - -static u32 nphy_papd_scaltbl[] = { - 0x0ae2002f, 0x0a3b0032, 0x09a70035, 0x09220038, - 0x0887003c, 0x081f003f, 0x07a20043, 0x07340047, - 0x06d2004b, 0x067a004f, 0x06170054, 0x05bf0059, - 0x0571005e, 0x051e0064, 0x04d3006a, 0x04910070, - 0x044c0077, 0x040f007e, 0x03d90085, 0x03a1008d, - 0x036f0095, 0x033d009e, 0x030b00a8, 0x02e000b2, - 0x02b900bc, 0x029200c7, 0x026d00d3, 0x024900e0, - 0x022900ed, 0x020a00fb, 0x01ec010a, 0x01d0011a, - 0x01b7012a, 0x019e013c, 0x0187014f, 0x01720162, - 0x015d0177, 0x0149018e, 0x013701a5, 0x012601be, - 0x011501d9, 0x010501f5, 0x00f70212, 0x00e90232, - 0x00dc0253, 0x00d00276, 0x00c4029c, 0x00b902c3, - 0x00af02ed, 0x00a5031a, 0x009c0349, 0x0093037a, - 0x008b03af, 0x008303e7, 0x007c0422, 0x00750461, - 0x006e04a3, 0x006804ea, 0x00620534, 0x005d0583, - 0x005805d7, 0x0053062f, 0x004e068d, 0x004a06f1 -}; - -static u32 nphy_tpc_txgain_rev3[] = { - 0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e, - 0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037, - 0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e, - 0x1e41003c, 0x1e41003b, 0x1e410039, 0x1e410037, - 0x1d410044, 0x1d410042, 0x1d410040, 0x1d41003e, - 0x1d41003c, 0x1d41003b, 0x1d410039, 0x1d410037, - 0x1c410044, 0x1c410042, 0x1c410040, 0x1c41003e, - 0x1c41003c, 0x1c41003b, 0x1c410039, 0x1c410037, - 0x1b410044, 0x1b410042, 0x1b410040, 0x1b41003e, - 0x1b41003c, 0x1b41003b, 0x1b410039, 0x1b410037, - 0x1a410044, 0x1a410042, 0x1a410040, 0x1a41003e, - 0x1a41003c, 0x1a41003b, 0x1a410039, 0x1a410037, - 0x19410044, 0x19410042, 0x19410040, 0x1941003e, - 0x1941003c, 0x1941003b, 0x19410039, 0x19410037, - 0x18410044, 0x18410042, 0x18410040, 0x1841003e, - 0x1841003c, 0x1841003b, 0x18410039, 0x18410037, - 0x17410044, 0x17410042, 0x17410040, 0x1741003e, - 0x1741003c, 0x1741003b, 0x17410039, 0x17410037, - 0x16410044, 0x16410042, 0x16410040, 0x1641003e, - 0x1641003c, 0x1641003b, 0x16410039, 0x16410037, - 0x15410044, 0x15410042, 0x15410040, 0x1541003e, - 0x1541003c, 0x1541003b, 0x15410039, 0x15410037, - 0x14410044, 0x14410042, 0x14410040, 0x1441003e, - 0x1441003c, 0x1441003b, 0x14410039, 0x14410037, - 0x13410044, 0x13410042, 0x13410040, 0x1341003e, - 0x1341003c, 0x1341003b, 0x13410039, 0x13410037, - 0x12410044, 0x12410042, 0x12410040, 0x1241003e, - 0x1241003c, 0x1241003b, 0x12410039, 0x12410037, - 0x11410044, 0x11410042, 0x11410040, 0x1141003e, - 0x1141003c, 0x1141003b, 0x11410039, 0x11410037, - 0x10410044, 0x10410042, 0x10410040, 0x1041003e, - 0x1041003c, 0x1041003b, 0x10410039, 0x10410037 -}; - -static u32 nphy_tpc_txgain_HiPwrEPA[] = { - 0x0f410044, 0x0f410042, 0x0f410040, 0x0f41003e, - 0x0f41003c, 0x0f41003b, 0x0f410039, 0x0f410037, - 0x0e410044, 0x0e410042, 0x0e410040, 0x0e41003e, - 0x0e41003c, 0x0e41003b, 0x0e410039, 0x0e410037, - 0x0d410044, 0x0d410042, 0x0d410040, 0x0d41003e, - 0x0d41003c, 0x0d41003b, 0x0d410039, 0x0d410037, - 0x0c410044, 0x0c410042, 0x0c410040, 0x0c41003e, - 0x0c41003c, 0x0c41003b, 0x0c410039, 0x0c410037, - 0x0b410044, 0x0b410042, 0x0b410040, 0x0b41003e, - 0x0b41003c, 0x0b41003b, 0x0b410039, 0x0b410037, - 0x0a410044, 0x0a410042, 0x0a410040, 0x0a41003e, - 0x0a41003c, 0x0a41003b, 0x0a410039, 0x0a410037, - 0x09410044, 0x09410042, 0x09410040, 0x0941003e, - 0x0941003c, 0x0941003b, 0x09410039, 0x09410037, - 0x08410044, 0x08410042, 0x08410040, 0x0841003e, - 0x0841003c, 0x0841003b, 0x08410039, 0x08410037, - 0x07410044, 0x07410042, 0x07410040, 0x0741003e, - 0x0741003c, 0x0741003b, 0x07410039, 0x07410037, - 0x06410044, 0x06410042, 0x06410040, 0x0641003e, - 0x0641003c, 0x0641003b, 0x06410039, 0x06410037, - 0x05410044, 0x05410042, 0x05410040, 0x0541003e, - 0x0541003c, 0x0541003b, 0x05410039, 0x05410037, - 0x04410044, 0x04410042, 0x04410040, 0x0441003e, - 0x0441003c, 0x0441003b, 0x04410039, 0x04410037, - 0x03410044, 0x03410042, 0x03410040, 0x0341003e, - 0x0341003c, 0x0341003b, 0x03410039, 0x03410037, - 0x02410044, 0x02410042, 0x02410040, 0x0241003e, - 0x0241003c, 0x0241003b, 0x02410039, 0x02410037, - 0x01410044, 0x01410042, 0x01410040, 0x0141003e, - 0x0141003c, 0x0141003b, 0x01410039, 0x01410037, - 0x00410044, 0x00410042, 0x00410040, 0x0041003e, - 0x0041003c, 0x0041003b, 0x00410039, 0x00410037 -}; - -static u32 nphy_tpc_txgain_epa_2057rev3[] = { - 0x80f90040, 0x80e10040, 0x80e1003c, 0x80c9003d, - 0x80b9003c, 0x80a9003d, 0x80a1003c, 0x8099003b, - 0x8091003b, 0x8089003a, 0x8081003a, 0x80790039, - 0x80710039, 0x8069003a, 0x8061003b, 0x8059003d, - 0x8051003f, 0x80490042, 0x8049003e, 0x8049003b, - 0x8041003e, 0x8041003b, 0x8039003e, 0x8039003b, - 0x80390038, 0x80390035, 0x8031003a, 0x80310036, - 0x80310033, 0x8029003a, 0x80290037, 0x80290034, - 0x80290031, 0x80210039, 0x80210036, 0x80210033, - 0x80210030, 0x8019003c, 0x80190039, 0x80190036, - 0x80190033, 0x80190030, 0x8019002d, 0x8019002b, - 0x80190028, 0x8011003a, 0x80110036, 0x80110033, - 0x80110030, 0x8011002e, 0x8011002b, 0x80110029, - 0x80110027, 0x80110024, 0x80110022, 0x80110020, - 0x8011001f, 0x8011001d, 0x8009003a, 0x80090037, - 0x80090034, 0x80090031, 0x8009002e, 0x8009002c, - 0x80090029, 0x80090027, 0x80090025, 0x80090023, - 0x80090021, 0x8009001f, 0x8009001d, 0x8009011d, - 0x8009021d, 0x8009031d, 0x8009041d, 0x8009051d, - 0x8009061d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, - 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d -}; - -static u32 nphy_tpc_txgain_epa_2057rev5[] = { - 0x10f90040, 0x10e10040, 0x10e1003c, 0x10c9003d, - 0x10b9003c, 0x10a9003d, 0x10a1003c, 0x1099003b, - 0x1091003b, 0x1089003a, 0x1081003a, 0x10790039, - 0x10710039, 0x1069003a, 0x1061003b, 0x1059003d, - 0x1051003f, 0x10490042, 0x1049003e, 0x1049003b, - 0x1041003e, 0x1041003b, 0x1039003e, 0x1039003b, - 0x10390038, 0x10390035, 0x1031003a, 0x10310036, - 0x10310033, 0x1029003a, 0x10290037, 0x10290034, - 0x10290031, 0x10210039, 0x10210036, 0x10210033, - 0x10210030, 0x1019003c, 0x10190039, 0x10190036, - 0x10190033, 0x10190030, 0x1019002d, 0x1019002b, - 0x10190028, 0x1011003a, 0x10110036, 0x10110033, - 0x10110030, 0x1011002e, 0x1011002b, 0x10110029, - 0x10110027, 0x10110024, 0x10110022, 0x10110020, - 0x1011001f, 0x1011001d, 0x1009003a, 0x10090037, - 0x10090034, 0x10090031, 0x1009002e, 0x1009002c, - 0x10090029, 0x10090027, 0x10090025, 0x10090023, - 0x10090021, 0x1009001f, 0x1009001d, 0x1009001b, - 0x1009001a, 0x10090018, 0x10090017, 0x10090016, - 0x10090015, 0x10090013, 0x10090012, 0x10090011, - 0x10090010, 0x1009000f, 0x1009000f, 0x1009000e, - 0x1009000d, 0x1009000c, 0x1009000c, 0x1009000b, - 0x1009000a, 0x1009000a, 0x10090009, 0x10090009, - 0x10090008, 0x10090008, 0x10090007, 0x10090007, - 0x10090007, 0x10090006, 0x10090006, 0x10090005, - 0x10090005, 0x10090005, 0x10090005, 0x10090004, - 0x10090004, 0x10090004, 0x10090004, 0x10090003, - 0x10090003, 0x10090003, 0x10090003, 0x10090003, - 0x10090003, 0x10090002, 0x10090002, 0x10090002, - 0x10090002, 0x10090002, 0x10090002, 0x10090002, - 0x10090002, 0x10090002, 0x10090001, 0x10090001, - 0x10090001, 0x10090001, 0x10090001, 0x10090001 -}; - -static u32 nphy_tpc_5GHz_txgain_rev3[] = { - 0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e, - 0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037, - 0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e, - 0xcef7003c, 0xcef7003b, 0xcef70039, 0xcef70037, - 0xcdf70044, 0xcdf70042, 0xcdf70040, 0xcdf7003e, - 0xcdf7003c, 0xcdf7003b, 0xcdf70039, 0xcdf70037, - 0xccf70044, 0xccf70042, 0xccf70040, 0xccf7003e, - 0xccf7003c, 0xccf7003b, 0xccf70039, 0xccf70037, - 0xcbf70044, 0xcbf70042, 0xcbf70040, 0xcbf7003e, - 0xcbf7003c, 0xcbf7003b, 0xcbf70039, 0xcbf70037, - 0xcaf70044, 0xcaf70042, 0xcaf70040, 0xcaf7003e, - 0xcaf7003c, 0xcaf7003b, 0xcaf70039, 0xcaf70037, - 0xc9f70044, 0xc9f70042, 0xc9f70040, 0xc9f7003e, - 0xc9f7003c, 0xc9f7003b, 0xc9f70039, 0xc9f70037, - 0xc8f70044, 0xc8f70042, 0xc8f70040, 0xc8f7003e, - 0xc8f7003c, 0xc8f7003b, 0xc8f70039, 0xc8f70037, - 0xc7f70044, 0xc7f70042, 0xc7f70040, 0xc7f7003e, - 0xc7f7003c, 0xc7f7003b, 0xc7f70039, 0xc7f70037, - 0xc6f70044, 0xc6f70042, 0xc6f70040, 0xc6f7003e, - 0xc6f7003c, 0xc6f7003b, 0xc6f70039, 0xc6f70037, - 0xc5f70044, 0xc5f70042, 0xc5f70040, 0xc5f7003e, - 0xc5f7003c, 0xc5f7003b, 0xc5f70039, 0xc5f70037, - 0xc4f70044, 0xc4f70042, 0xc4f70040, 0xc4f7003e, - 0xc4f7003c, 0xc4f7003b, 0xc4f70039, 0xc4f70037, - 0xc3f70044, 0xc3f70042, 0xc3f70040, 0xc3f7003e, - 0xc3f7003c, 0xc3f7003b, 0xc3f70039, 0xc3f70037, - 0xc2f70044, 0xc2f70042, 0xc2f70040, 0xc2f7003e, - 0xc2f7003c, 0xc2f7003b, 0xc2f70039, 0xc2f70037, - 0xc1f70044, 0xc1f70042, 0xc1f70040, 0xc1f7003e, - 0xc1f7003c, 0xc1f7003b, 0xc1f70039, 0xc1f70037, - 0xc0f70044, 0xc0f70042, 0xc0f70040, 0xc0f7003e, - 0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037 -}; - -static u32 nphy_tpc_5GHz_txgain_rev4[] = { - 0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e, - 0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037, - 0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e, - 0x2ef2003c, 0x2ef2003b, 0x2ef20039, 0x2ef20037, - 0x2df20044, 0x2df20042, 0x2df20040, 0x2df2003e, - 0x2df2003c, 0x2df2003b, 0x2df20039, 0x2df20037, - 0x2cf20044, 0x2cf20042, 0x2cf20040, 0x2cf2003e, - 0x2cf2003c, 0x2cf2003b, 0x2cf20039, 0x2cf20037, - 0x2bf20044, 0x2bf20042, 0x2bf20040, 0x2bf2003e, - 0x2bf2003c, 0x2bf2003b, 0x2bf20039, 0x2bf20037, - 0x2af20044, 0x2af20042, 0x2af20040, 0x2af2003e, - 0x2af2003c, 0x2af2003b, 0x2af20039, 0x2af20037, - 0x29f20044, 0x29f20042, 0x29f20040, 0x29f2003e, - 0x29f2003c, 0x29f2003b, 0x29f20039, 0x29f20037, - 0x28f20044, 0x28f20042, 0x28f20040, 0x28f2003e, - 0x28f2003c, 0x28f2003b, 0x28f20039, 0x28f20037, - 0x27f20044, 0x27f20042, 0x27f20040, 0x27f2003e, - 0x27f2003c, 0x27f2003b, 0x27f20039, 0x27f20037, - 0x26f20044, 0x26f20042, 0x26f20040, 0x26f2003e, - 0x26f2003c, 0x26f2003b, 0x26f20039, 0x26f20037, - 0x25f20044, 0x25f20042, 0x25f20040, 0x25f2003e, - 0x25f2003c, 0x25f2003b, 0x25f20039, 0x25f20037, - 0x24f20044, 0x24f20042, 0x24f20040, 0x24f2003e, - 0x24f2003c, 0x24f2003b, 0x24f20039, 0x24f20038, - 0x23f20041, 0x23f20040, 0x23f2003f, 0x23f2003e, - 0x23f2003c, 0x23f2003b, 0x23f20039, 0x23f20037, - 0x22f20044, 0x22f20042, 0x22f20040, 0x22f2003e, - 0x22f2003c, 0x22f2003b, 0x22f20039, 0x22f20037, - 0x21f20044, 0x21f20042, 0x21f20040, 0x21f2003e, - 0x21f2003c, 0x21f2003b, 0x21f20039, 0x21f20037, - 0x20d20043, 0x20d20041, 0x20d2003e, 0x20d2003c, - 0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034 -}; - -static u32 nphy_tpc_5GHz_txgain_rev5[] = { - 0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044, - 0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c, - 0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e, - 0x0e62003c, 0x0e62003d, 0x0e62003b, 0x0e62003a, - 0x0d620043, 0x0d620041, 0x0d620040, 0x0d62003e, - 0x0d62003d, 0x0d62003c, 0x0d62003b, 0x0d62003a, - 0x0c620041, 0x0c620040, 0x0c62003f, 0x0c62003e, - 0x0c62003c, 0x0c62003b, 0x0c620039, 0x0c620037, - 0x0b620046, 0x0b620044, 0x0b620042, 0x0b620040, - 0x0b62003e, 0x0b62003c, 0x0b62003b, 0x0b62003a, - 0x0a620041, 0x0a620040, 0x0a62003e, 0x0a62003c, - 0x0a62003b, 0x0a62003a, 0x0a620039, 0x0a620038, - 0x0962003e, 0x0962003d, 0x0962003c, 0x0962003b, - 0x09620039, 0x09620037, 0x09620035, 0x09620033, - 0x08620044, 0x08620042, 0x08620040, 0x0862003e, - 0x0862003c, 0x0862003b, 0x0862003a, 0x08620039, - 0x07620043, 0x07620042, 0x07620040, 0x0762003f, - 0x0762003d, 0x0762003b, 0x0762003a, 0x07620039, - 0x0662003e, 0x0662003d, 0x0662003c, 0x0662003b, - 0x06620039, 0x06620037, 0x06620035, 0x06620033, - 0x05620046, 0x05620044, 0x05620042, 0x05620040, - 0x0562003e, 0x0562003c, 0x0562003b, 0x05620039, - 0x04620044, 0x04620042, 0x04620040, 0x0462003e, - 0x0462003c, 0x0462003b, 0x04620039, 0x04620038, - 0x0362003c, 0x0362003b, 0x0362003a, 0x03620039, - 0x03620038, 0x03620037, 0x03620035, 0x03620033, - 0x0262004c, 0x0262004a, 0x02620048, 0x02620047, - 0x02620046, 0x02620044, 0x02620043, 0x02620042, - 0x0162004a, 0x01620048, 0x01620046, 0x01620044, - 0x01620043, 0x01620042, 0x01620041, 0x01620040, - 0x00620042, 0x00620040, 0x0062003e, 0x0062003c, - 0x0062003b, 0x00620039, 0x00620037, 0x00620035 -}; - -static u32 nphy_tpc_5GHz_txgain_HiPwrEPA[] = { - 0x2ff10044, 0x2ff10042, 0x2ff10040, 0x2ff1003e, - 0x2ff1003c, 0x2ff1003b, 0x2ff10039, 0x2ff10037, - 0x2ef10044, 0x2ef10042, 0x2ef10040, 0x2ef1003e, - 0x2ef1003c, 0x2ef1003b, 0x2ef10039, 0x2ef10037, - 0x2df10044, 0x2df10042, 0x2df10040, 0x2df1003e, - 0x2df1003c, 0x2df1003b, 0x2df10039, 0x2df10037, - 0x2cf10044, 0x2cf10042, 0x2cf10040, 0x2cf1003e, - 0x2cf1003c, 0x2cf1003b, 0x2cf10039, 0x2cf10037, - 0x2bf10044, 0x2bf10042, 0x2bf10040, 0x2bf1003e, - 0x2bf1003c, 0x2bf1003b, 0x2bf10039, 0x2bf10037, - 0x2af10044, 0x2af10042, 0x2af10040, 0x2af1003e, - 0x2af1003c, 0x2af1003b, 0x2af10039, 0x2af10037, - 0x29f10044, 0x29f10042, 0x29f10040, 0x29f1003e, - 0x29f1003c, 0x29f1003b, 0x29f10039, 0x29f10037, - 0x28f10044, 0x28f10042, 0x28f10040, 0x28f1003e, - 0x28f1003c, 0x28f1003b, 0x28f10039, 0x28f10037, - 0x27f10044, 0x27f10042, 0x27f10040, 0x27f1003e, - 0x27f1003c, 0x27f1003b, 0x27f10039, 0x27f10037, - 0x26f10044, 0x26f10042, 0x26f10040, 0x26f1003e, - 0x26f1003c, 0x26f1003b, 0x26f10039, 0x26f10037, - 0x25f10044, 0x25f10042, 0x25f10040, 0x25f1003e, - 0x25f1003c, 0x25f1003b, 0x25f10039, 0x25f10037, - 0x24f10044, 0x24f10042, 0x24f10040, 0x24f1003e, - 0x24f1003c, 0x24f1003b, 0x24f10039, 0x24f10038, - 0x23f10041, 0x23f10040, 0x23f1003f, 0x23f1003e, - 0x23f1003c, 0x23f1003b, 0x23f10039, 0x23f10037, - 0x22f10044, 0x22f10042, 0x22f10040, 0x22f1003e, - 0x22f1003c, 0x22f1003b, 0x22f10039, 0x22f10037, - 0x21f10044, 0x21f10042, 0x21f10040, 0x21f1003e, - 0x21f1003c, 0x21f1003b, 0x21f10039, 0x21f10037, - 0x20d10043, 0x20d10041, 0x20d1003e, 0x20d1003c, - 0x20d1003a, 0x20d10038, 0x20d10036, 0x20d10034 -}; - -static u8 ant_sw_ctrl_tbl_rev8_2o3[] = { 0x14, 0x18 }; -static u8 ant_sw_ctrl_tbl_rev8[] = { 0x4, 0x8, 0x4, 0x8, 0x11, 0x12 }; -static u8 ant_sw_ctrl_tbl_rev8_2057v7_core0[] = { - 0x09, 0x0a, 0x15, 0x16, 0x09, 0x0a -}; -static u8 ant_sw_ctrl_tbl_rev8_2057v7_core1[] = { - 0x09, 0x0a, 0x09, 0x0a, 0x15, 0x16 -}; - -bool wlc_phy_bist_check_phy(struct brcms_phy_pub *pih) -{ - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - u32 phybist0, phybist1, phybist2, phybist3, phybist4; - - if (NREV_GE(pi->pubpi.phy_rev, 16)) - return true; - - phybist0 = read_phy_reg(pi, 0x0e); - phybist1 = read_phy_reg(pi, 0x0f); - phybist2 = read_phy_reg(pi, 0xea); - phybist3 = read_phy_reg(pi, 0xeb); - phybist4 = read_phy_reg(pi, 0x156); - - if ((phybist0 == 0) && (phybist1 == 0x4000) && (phybist2 == 0x1fe0) && - (phybist3 == 0) && (phybist4 == 0)) - return true; - - return false; -} - -static void wlc_phy_bphy_init_nphy(struct brcms_phy *pi) -{ - u16 addr, val; - - val = 0x1e1f; - for (addr = (NPHY_TO_BPHY_OFF + BPHY_RSSI_LUT); - addr <= (NPHY_TO_BPHY_OFF + BPHY_RSSI_LUT_END); addr++) { - write_phy_reg(pi, addr, val); - if (addr == (NPHY_TO_BPHY_OFF + 0x97)) - val = 0x3e3f; - else - val -= 0x0202; - } - - write_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_STEP, 0x668); -} - -void -wlc_phy_table_write_nphy(struct brcms_phy *pi, u32 id, u32 len, u32 offset, - u32 width, const void *data) -{ - struct phytbl_info tbl; - - tbl.tbl_id = id; - tbl.tbl_len = len; - tbl.tbl_offset = offset; - tbl.tbl_width = width; - tbl.tbl_ptr = data; - wlc_phy_write_table_nphy(pi, &tbl); -} - -void -wlc_phy_table_read_nphy(struct brcms_phy *pi, u32 id, u32 len, u32 offset, - u32 width, void *data) -{ - struct phytbl_info tbl; - - tbl.tbl_id = id; - tbl.tbl_len = len; - tbl.tbl_offset = offset; - tbl.tbl_width = width; - tbl.tbl_ptr = data; - wlc_phy_read_table_nphy(pi, &tbl); -} - -static void -wlc_phy_static_table_download_nphy(struct brcms_phy *pi) -{ - uint idx; - - if (NREV_GE(pi->pubpi.phy_rev, 16)) { - for (idx = 0; idx < mimophytbl_info_sz_rev16; idx++) - wlc_phy_write_table_nphy(pi, - &mimophytbl_info_rev16[idx]); - } else if (NREV_GE(pi->pubpi.phy_rev, 7)) { - for (idx = 0; idx < mimophytbl_info_sz_rev7; idx++) - wlc_phy_write_table_nphy(pi, - &mimophytbl_info_rev7[idx]); - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - for (idx = 0; idx < mimophytbl_info_sz_rev3; idx++) - wlc_phy_write_table_nphy(pi, - &mimophytbl_info_rev3[idx]); - } else { - for (idx = 0; idx < mimophytbl_info_sz_rev0; idx++) - wlc_phy_write_table_nphy(pi, - &mimophytbl_info_rev0[idx]); - } -} - -static void wlc_phy_tbl_init_nphy(struct brcms_phy *pi) -{ - uint idx = 0; - u8 antswctrllut; - - if (pi->phy_init_por) - wlc_phy_static_table_download_nphy(pi); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - antswctrllut = CHSPEC_IS2G(pi->radio_chanspec) ? - pi->srom_fem2g.antswctrllut : pi->srom_fem5g. - antswctrllut; - - switch (antswctrllut) { - case 0: - - break; - - case 1: - - if (pi->aa2g == 7) - wlc_phy_table_write_nphy( - pi, - NPHY_TBL_ID_ANTSWCTRLLUT, - 2, 0x21, 8, - &ant_sw_ctrl_tbl_rev8_2o3[0]); - else - wlc_phy_table_write_nphy( - pi, - NPHY_TBL_ID_ANTSWCTRLLUT, - 2, 0x21, 8, - &ant_sw_ctrl_tbl_rev8 - [0]); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 2, 0x25, 8, - &ant_sw_ctrl_tbl_rev8[2]); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 2, 0x29, 8, - &ant_sw_ctrl_tbl_rev8[4]); - break; - - case 2: - - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 2, 0x1, 8, - &ant_sw_ctrl_tbl_rev8_2057v7_core0[0]); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 2, 0x5, 8, - &ant_sw_ctrl_tbl_rev8_2057v7_core0[2]); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 2, 0x9, 8, - &ant_sw_ctrl_tbl_rev8_2057v7_core0[4]); - - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 2, 0x21, 8, - &ant_sw_ctrl_tbl_rev8_2057v7_core1[0]); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 2, 0x25, 8, - &ant_sw_ctrl_tbl_rev8_2057v7_core1[2]); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 2, 0x29, 8, - &ant_sw_ctrl_tbl_rev8_2057v7_core1[4]); - break; - - default: - break; - } - - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - for (idx = 0; idx < mimophytbl_info_sz_rev3_volatile; idx++) { - - if (idx == ANT_SWCTRL_TBL_REV3_IDX) { - antswctrllut = - CHSPEC_IS2G(pi->radio_chanspec) ? - pi->srom_fem2g.antswctrllut : - pi->srom_fem5g.antswctrllut; - switch (antswctrllut) { - case 0: - wlc_phy_write_table_nphy( - pi, - &mimophytbl_info_rev3_volatile - [idx]); - break; - case 1: - wlc_phy_write_table_nphy( - pi, - &mimophytbl_info_rev3_volatile1 - [idx]); - break; - case 2: - wlc_phy_write_table_nphy( - pi, - &mimophytbl_info_rev3_volatile2 - [idx]); - break; - case 3: - wlc_phy_write_table_nphy( - pi, - &mimophytbl_info_rev3_volatile3 - [idx]); - break; - default: - break; - } - } else { - wlc_phy_write_table_nphy( - pi, - &mimophytbl_info_rev3_volatile[idx]); - } - } - } else { - for (idx = 0; idx < mimophytbl_info_sz_rev0_volatile; idx++) - wlc_phy_write_table_nphy(pi, - &mimophytbl_info_rev0_volatile - [idx]); - } -} - -static void -wlc_phy_write_txmacreg_nphy(struct brcms_phy *pi, u16 holdoff, u16 delay) -{ - write_phy_reg(pi, 0x77, holdoff); - write_phy_reg(pi, 0xb4, delay); -} - -void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs) -{ - u16 holdoff, delay; - - if (rifs) { - - holdoff = 0x10; - delay = 0x258; - } else { - - holdoff = 0x15; - delay = 0x320; - } - - wlc_phy_write_txmacreg_nphy(pi, holdoff, delay); - - if (pi->sh && (pi->sh->_rifs_phy != rifs)) - pi->sh->_rifs_phy = rifs; -} - -static void wlc_phy_txpwrctrl_config_nphy(struct brcms_phy *pi) -{ - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - pi->nphy_txpwrctrl = PHY_TPC_HW_ON; - pi->phy_5g_pwrgain = true; - return; - } - - pi->nphy_txpwrctrl = PHY_TPC_HW_OFF; - pi->phy_5g_pwrgain = false; - - if ((pi->sh->boardflags2 & BFL2_TXPWRCTRL_EN) && - NREV_GE(pi->pubpi.phy_rev, 2) && (pi->sh->sromrev >= 4)) - pi->nphy_txpwrctrl = PHY_TPC_HW_ON; - else if ((pi->sh->sromrev >= 4) - && (pi->sh->boardflags2 & BFL2_5G_PWRGAIN)) - pi->phy_5g_pwrgain = true; -} - -static void wlc_phy_txpwr_srom_read_ppr_nphy(struct brcms_phy *pi) -{ - u16 bw40po, cddpo, stbcpo, bwduppo; - uint band_num; - struct ssb_sprom *sprom = &pi->d11core->bus->sprom; - - if (pi->sh->sromrev >= 9) - return; - - bw40po = sprom->bw40po; - pi->bw402gpo = bw40po & 0xf; - pi->bw405gpo = (bw40po & 0xf0) >> 4; - pi->bw405glpo = (bw40po & 0xf00) >> 8; - pi->bw405ghpo = (bw40po & 0xf000) >> 12; - - cddpo = sprom->cddpo; - pi->cdd2gpo = cddpo & 0xf; - pi->cdd5gpo = (cddpo & 0xf0) >> 4; - pi->cdd5glpo = (cddpo & 0xf00) >> 8; - pi->cdd5ghpo = (cddpo & 0xf000) >> 12; - - stbcpo = sprom->stbcpo; - pi->stbc2gpo = stbcpo & 0xf; - pi->stbc5gpo = (stbcpo & 0xf0) >> 4; - pi->stbc5glpo = (stbcpo & 0xf00) >> 8; - pi->stbc5ghpo = (stbcpo & 0xf000) >> 12; - - bwduppo = sprom->bwduppo; - pi->bwdup2gpo = bwduppo & 0xf; - pi->bwdup5gpo = (bwduppo & 0xf0) >> 4; - pi->bwdup5glpo = (bwduppo & 0xf00) >> 8; - pi->bwdup5ghpo = (bwduppo & 0xf000) >> 12; - - for (band_num = 0; band_num < (CH_2G_GROUP + CH_5G_GROUP); - band_num++) { - switch (band_num) { - case 0: - pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_2g = - sprom->core_pwr_info[0].maxpwr_2g; - pi->nphy_pwrctrl_info[PHY_CORE_1].max_pwr_2g = - sprom->core_pwr_info[1].maxpwr_2g; - pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_a1 = - sprom->core_pwr_info[0].pa_2g[0]; - pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_a1 = - sprom->core_pwr_info[1].pa_2g[0]; - pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_b0 = - sprom->core_pwr_info[0].pa_2g[1]; - pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_b0 = - sprom->core_pwr_info[1].pa_2g[1]; - pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_b1 = - sprom->core_pwr_info[0].pa_2g[2]; - pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_b1 = - sprom->core_pwr_info[1].pa_2g[2]; - pi->nphy_pwrctrl_info[PHY_CORE_0].idle_targ_2g = - sprom->core_pwr_info[0].itssi_2g; - pi->nphy_pwrctrl_info[PHY_CORE_1].idle_targ_2g = - sprom->core_pwr_info[1].itssi_2g; - - pi->cck2gpo = sprom->cck2gpo; - - pi->ofdm2gpo = sprom->ofdm2gpo; - - pi->mcs2gpo[0] = sprom->mcs2gpo[0]; - pi->mcs2gpo[1] = sprom->mcs2gpo[1]; - pi->mcs2gpo[2] = sprom->mcs2gpo[2]; - pi->mcs2gpo[3] = sprom->mcs2gpo[3]; - pi->mcs2gpo[4] = sprom->mcs2gpo[4]; - pi->mcs2gpo[5] = sprom->mcs2gpo[5]; - pi->mcs2gpo[6] = sprom->mcs2gpo[6]; - pi->mcs2gpo[7] = sprom->mcs2gpo[7]; - break; - case 1: - - pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_5gm = - sprom->core_pwr_info[0].maxpwr_5g; - pi->nphy_pwrctrl_info[PHY_CORE_1].max_pwr_5gm = - sprom->core_pwr_info[1].maxpwr_5g; - pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_a1 = - sprom->core_pwr_info[0].pa_5g[0]; - pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_a1 = - sprom->core_pwr_info[1].pa_5g[0]; - pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_b0 = - sprom->core_pwr_info[0].pa_5g[1]; - pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_b0 = - sprom->core_pwr_info[1].pa_5g[1]; - pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_b1 = - sprom->core_pwr_info[0].pa_5g[2]; - pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_b1 = - sprom->core_pwr_info[1].pa_5g[2]; - pi->nphy_pwrctrl_info[PHY_CORE_0].idle_targ_5gm = - sprom->core_pwr_info[0].itssi_5g; - pi->nphy_pwrctrl_info[PHY_CORE_1].idle_targ_5gm = - sprom->core_pwr_info[1].itssi_5g; - - pi->ofdm5gpo = sprom->ofdm5gpo; - - pi->mcs5gpo[0] = sprom->mcs5gpo[0]; - pi->mcs5gpo[1] = sprom->mcs5gpo[1]; - pi->mcs5gpo[2] = sprom->mcs5gpo[2]; - pi->mcs5gpo[3] = sprom->mcs5gpo[3]; - pi->mcs5gpo[4] = sprom->mcs5gpo[4]; - pi->mcs5gpo[5] = sprom->mcs5gpo[5]; - pi->mcs5gpo[6] = sprom->mcs5gpo[6]; - pi->mcs5gpo[7] = sprom->mcs5gpo[7]; - break; - case 2: - - pi->nphy_pwrctrl_info[0].max_pwr_5gl = - sprom->core_pwr_info[0].maxpwr_5gl; - pi->nphy_pwrctrl_info[1].max_pwr_5gl = - sprom->core_pwr_info[1].maxpwr_5gl; - pi->nphy_pwrctrl_info[0].pwrdet_5gl_a1 = - sprom->core_pwr_info[0].pa_5gl[0]; - pi->nphy_pwrctrl_info[1].pwrdet_5gl_a1 = - sprom->core_pwr_info[1].pa_5gl[0]; - pi->nphy_pwrctrl_info[0].pwrdet_5gl_b0 = - sprom->core_pwr_info[0].pa_5gl[1]; - pi->nphy_pwrctrl_info[1].pwrdet_5gl_b0 = - sprom->core_pwr_info[1].pa_5gl[1]; - pi->nphy_pwrctrl_info[0].pwrdet_5gl_b1 = - sprom->core_pwr_info[0].pa_5gl[2]; - pi->nphy_pwrctrl_info[1].pwrdet_5gl_b1 = - sprom->core_pwr_info[1].pa_5gl[2]; - pi->nphy_pwrctrl_info[0].idle_targ_5gl = 0; - pi->nphy_pwrctrl_info[1].idle_targ_5gl = 0; - - pi->ofdm5glpo = sprom->ofdm5glpo; - - pi->mcs5glpo[0] = sprom->mcs5glpo[0]; - pi->mcs5glpo[1] = sprom->mcs5glpo[1]; - pi->mcs5glpo[2] = sprom->mcs5glpo[2]; - pi->mcs5glpo[3] = sprom->mcs5glpo[3]; - pi->mcs5glpo[4] = sprom->mcs5glpo[4]; - pi->mcs5glpo[5] = sprom->mcs5glpo[5]; - pi->mcs5glpo[6] = sprom->mcs5glpo[6]; - pi->mcs5glpo[7] = sprom->mcs5glpo[7]; - break; - case 3: - - pi->nphy_pwrctrl_info[0].max_pwr_5gh = - sprom->core_pwr_info[0].maxpwr_5gh; - pi->nphy_pwrctrl_info[1].max_pwr_5gh = - sprom->core_pwr_info[1].maxpwr_5gh; - pi->nphy_pwrctrl_info[0].pwrdet_5gh_a1 = - sprom->core_pwr_info[0].pa_5gh[0]; - pi->nphy_pwrctrl_info[1].pwrdet_5gh_a1 = - sprom->core_pwr_info[1].pa_5gh[0]; - pi->nphy_pwrctrl_info[0].pwrdet_5gh_b0 = - sprom->core_pwr_info[0].pa_5gh[1]; - pi->nphy_pwrctrl_info[1].pwrdet_5gh_b0 = - sprom->core_pwr_info[1].pa_5gh[1]; - pi->nphy_pwrctrl_info[0].pwrdet_5gh_b1 = - sprom->core_pwr_info[0].pa_5gh[2]; - pi->nphy_pwrctrl_info[1].pwrdet_5gh_b1 = - sprom->core_pwr_info[1].pa_5gh[2]; - pi->nphy_pwrctrl_info[0].idle_targ_5gh = 0; - pi->nphy_pwrctrl_info[1].idle_targ_5gh = 0; - - pi->ofdm5ghpo = sprom->ofdm5ghpo; - - pi->mcs5ghpo[0] = sprom->mcs5ghpo[0]; - pi->mcs5ghpo[1] = sprom->mcs5ghpo[1]; - pi->mcs5ghpo[2] = sprom->mcs5ghpo[2]; - pi->mcs5ghpo[3] = sprom->mcs5ghpo[3]; - pi->mcs5ghpo[4] = sprom->mcs5ghpo[4]; - pi->mcs5ghpo[5] = sprom->mcs5ghpo[5]; - pi->mcs5ghpo[6] = sprom->mcs5ghpo[6]; - pi->mcs5ghpo[7] = sprom->mcs5ghpo[7]; - break; - } - } - - wlc_phy_txpwr_apply_nphy(pi); -} - -static bool wlc_phy_txpwr_srom_read_nphy(struct brcms_phy *pi) -{ - struct ssb_sprom *sprom = &pi->d11core->bus->sprom; - - pi->antswitch = sprom->antswitch; - pi->aa2g = sprom->ant_available_bg; - pi->aa5g = sprom->ant_available_a; - - pi->srom_fem2g.tssipos = sprom->fem.ghz2.tssipos; - pi->srom_fem2g.extpagain = sprom->fem.ghz2.extpa_gain; - pi->srom_fem2g.pdetrange = sprom->fem.ghz2.pdet_range; - pi->srom_fem2g.triso = sprom->fem.ghz2.tr_iso; - pi->srom_fem2g.antswctrllut = sprom->fem.ghz2.antswlut; - - pi->srom_fem5g.tssipos = sprom->fem.ghz5.tssipos; - pi->srom_fem5g.extpagain = sprom->fem.ghz5.extpa_gain; - pi->srom_fem5g.pdetrange = sprom->fem.ghz5.pdet_range; - pi->srom_fem5g.triso = sprom->fem.ghz5.tr_iso; - if (sprom->fem.ghz5.antswlut) - pi->srom_fem5g.antswctrllut = sprom->fem.ghz5.antswlut; - else - pi->srom_fem5g.antswctrllut = sprom->fem.ghz2.antswlut; - - wlc_phy_txpower_ipa_upd(pi); - - pi->phy_txcore_disable_temp = sprom->tempthresh; - if (pi->phy_txcore_disable_temp == 0) - pi->phy_txcore_disable_temp = PHY_CHAIN_TX_DISABLE_TEMP; - - pi->phy_tempsense_offset = sprom->tempoffset; - if (pi->phy_tempsense_offset != 0) { - if (pi->phy_tempsense_offset > - (NPHY_SROM_TEMPSHIFT + NPHY_SROM_MAXTEMPOFFSET)) - pi->phy_tempsense_offset = NPHY_SROM_MAXTEMPOFFSET; - else if (pi->phy_tempsense_offset < (NPHY_SROM_TEMPSHIFT + - NPHY_SROM_MINTEMPOFFSET)) - pi->phy_tempsense_offset = NPHY_SROM_MINTEMPOFFSET; - else - pi->phy_tempsense_offset -= NPHY_SROM_TEMPSHIFT; - } - - pi->phy_txcore_enable_temp = - pi->phy_txcore_disable_temp - PHY_HYSTERESIS_DELTATEMP; - - pi->phycal_tempdelta = sprom->phycal_tempdelta; - if (pi->phycal_tempdelta > NPHY_CAL_MAXTEMPDELTA) - pi->phycal_tempdelta = 0; - - wlc_phy_txpwr_srom_read_ppr_nphy(pi); - - return true; -} - -bool wlc_phy_attach_nphy(struct brcms_phy *pi) -{ - uint i; - - if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 6)) - pi->phyhang_avoid = true; - - if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { - pi->nphy_gband_spurwar_en = true; - if (pi->sh->boardflags2 & BFL2_SPUR_WAR) - pi->nphy_aband_spurwar_en = true; - } - if (NREV_GE(pi->pubpi.phy_rev, 6) && NREV_LT(pi->pubpi.phy_rev, 7)) { - if (pi->sh->boardflags2 & BFL2_2G_SPUR_WAR) - pi->nphy_gband_spurwar2_en = true; - } - - pi->n_preamble_override = AUTO; - if (NREV_IS(pi->pubpi.phy_rev, 3) || NREV_IS(pi->pubpi.phy_rev, 4)) - pi->n_preamble_override = BRCMS_N_PREAMBLE_MIXEDMODE; - - pi->nphy_txrx_chain = AUTO; - pi->phy_scraminit = AUTO; - - pi->nphy_rxcalparams = 0x010100B5; - - pi->nphy_perical = PHY_PERICAL_MPHASE; - pi->mphase_cal_phase_id = MPHASE_CAL_STATE_IDLE; - pi->mphase_txcal_numcmds = MPHASE_TXCAL_NUMCMDS; - - pi->nphy_gain_boost = true; - pi->nphy_elna_gain_config = false; - pi->radio_is_on = false; - - for (i = 0; i < pi->pubpi.phy_corenum; i++) - pi->nphy_txpwrindex[i].index = AUTO; - - wlc_phy_txpwrctrl_config_nphy(pi); - if (pi->nphy_txpwrctrl == PHY_TPC_HW_ON) - pi->hwpwrctrl_capable = true; - - pi->pi_fptr.init = wlc_phy_init_nphy; - pi->pi_fptr.calinit = wlc_phy_cal_init_nphy; - pi->pi_fptr.chanset = wlc_phy_chanspec_set_nphy; - pi->pi_fptr.txpwrrecalc = wlc_phy_txpower_recalc_target_nphy; - - if (!wlc_phy_txpwr_srom_read_nphy(pi)) - return false; - - return true; -} - -static s32 get_rf_pwr_offset(struct brcms_phy *pi, s16 pga_gn, s16 pad_gn) -{ - s32 rfpwr_offset = 0; - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if ((pi->pubpi.radiorev == 3) || - (pi->pubpi.radiorev == 4) || - (pi->pubpi.radiorev == 6)) - rfpwr_offset = (s16) - nphy_papd_padgain_dlt_2g_2057rev3n4 - [pad_gn]; - else if (pi->pubpi.radiorev == 5) - rfpwr_offset = (s16) - nphy_papd_padgain_dlt_2g_2057rev5 - [pad_gn]; - else if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == - 8)) - rfpwr_offset = (s16) - nphy_papd_padgain_dlt_2g_2057rev7 - [pad_gn]; - } else { - if ((pi->pubpi.radiorev == 3) || - (pi->pubpi.radiorev == 4) || - (pi->pubpi.radiorev == 6)) - rfpwr_offset = (s16) - nphy_papd_pgagain_dlt_5g_2057 - [pga_gn]; - else if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == - 8)) - rfpwr_offset = (s16) - nphy_papd_pgagain_dlt_5g_2057rev7 - [pga_gn]; - } - return rfpwr_offset; -} - -static void wlc_phy_update_mimoconfig_nphy(struct brcms_phy *pi, s32 preamble) -{ - bool gf_preamble = false; - u16 val; - - if (preamble == BRCMS_N_PREAMBLE_GF) - gf_preamble = true; - - val = read_phy_reg(pi, 0xed); - - val |= RX_GF_MM_AUTO; - val &= ~RX_GF_OR_MM; - if (gf_preamble) - val |= RX_GF_OR_MM; - - write_phy_reg(pi, 0xed, val); -} - -static void wlc_phy_ipa_set_tx_digi_filts_nphy(struct brcms_phy *pi) -{ - int j, type; - u16 addr_offset[] = { 0x186, 0x195, 0x2c5}; - - for (type = 0; type < 3; type++) { - for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) - write_phy_reg(pi, addr_offset[type] + j, - NPHY_IPA_REV4_txdigi_filtcoeffs[type][j]); - } - - if (pi->bw == WL_CHANSPEC_BW_40) { - for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) - write_phy_reg(pi, 0x186 + j, - NPHY_IPA_REV4_txdigi_filtcoeffs[3][j]); - } else { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) - write_phy_reg(pi, 0x186 + j, - NPHY_IPA_REV4_txdigi_filtcoeffs[5][j]); - } - - if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) { - for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) - write_phy_reg(pi, 0x2c5 + j, - NPHY_IPA_REV4_txdigi_filtcoeffs[6][j]); - } - } -} - -static void wlc_phy_ipa_restore_tx_digi_filts_nphy(struct brcms_phy *pi) -{ - int j; - - if (pi->bw == WL_CHANSPEC_BW_40) { - for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) - write_phy_reg(pi, 0x195 + j, - NPHY_IPA_REV4_txdigi_filtcoeffs[4][j]); - } else { - for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) - write_phy_reg(pi, 0x186 + j, - NPHY_IPA_REV4_txdigi_filtcoeffs[3][j]); - } -} - -static void -wlc_phy_set_rfseq_nphy(struct brcms_phy *pi, u8 cmd, u8 *events, u8 *dlys, - u8 len) -{ - u32 t1_offset, t2_offset; - u8 ctr; - u8 end_event = - NREV_GE(pi->pubpi.phy_rev, - 3) ? NPHY_REV3_RFSEQ_CMD_END : NPHY_RFSEQ_CMD_END; - u8 end_dly = 1; - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - t1_offset = cmd << 4; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, len, t1_offset, 8, - events); - t2_offset = t1_offset + 0x080; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, len, t2_offset, 8, - dlys); - - for (ctr = len; ctr < 16; ctr++) { - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, - t1_offset + ctr, 8, &end_event); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, - t2_offset + ctr, 8, &end_dly); - } - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -static u16 wlc_phy_read_lpf_bw_ctl_nphy(struct brcms_phy *pi, u16 offset) -{ - u16 lpf_bw_ctl_val = 0; - u16 rx2tx_lpf_rc_lut_offset = 0; - - if (offset == 0) { - if (CHSPEC_IS40(pi->radio_chanspec)) - rx2tx_lpf_rc_lut_offset = 0x159; - else - rx2tx_lpf_rc_lut_offset = 0x154; - } else { - rx2tx_lpf_rc_lut_offset = offset; - } - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, - (u32) rx2tx_lpf_rc_lut_offset, 16, - &lpf_bw_ctl_val); - - lpf_bw_ctl_val = lpf_bw_ctl_val & 0x7; - - return lpf_bw_ctl_val; -} - -static void -wlc_phy_rfctrl_override_nphy_rev7(struct brcms_phy *pi, u16 field, u16 value, - u8 core_mask, u8 off, u8 override_id) -{ - u8 core_num; - u16 addr = 0, en_addr = 0, val_addr = 0, en_mask = 0, val_mask = 0; - u8 val_shift = 0; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - en_mask = field; - for (core_num = 0; core_num < 2; core_num++) { - if (override_id == NPHY_REV7_RFCTRLOVERRIDE_ID0) { - - switch (field) { - case (0x1 << 2): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : - 0x7d; - val_mask = (0x1 << 1); - val_shift = 1; - break; - case (0x1 << 3): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : - 0x7d; - val_mask = (0x1 << 2); - val_shift = 2; - break; - case (0x1 << 4): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : - 0x7d; - val_mask = (0x1 << 4); - val_shift = 4; - break; - case (0x1 << 5): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : - 0x7d; - val_mask = (0x1 << 5); - val_shift = 5; - break; - case (0x1 << 6): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : - 0x7d; - val_mask = (0x1 << 6); - val_shift = 6; - break; - case (0x1 << 7): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : - 0x7d; - val_mask = (0x1 << 7); - val_shift = 7; - break; - case (0x1 << 10): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0xf8 : - 0xfa; - val_mask = (0x7 << 4); - val_shift = 4; - break; - case (0x1 << 11): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7b : - 0x7e; - val_mask = (0xffff << 0); - val_shift = 0; - break; - case (0x1 << 12): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7c : - 0x7f; - val_mask = (0xffff << 0); - val_shift = 0; - break; - case (0x3 << 13): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x348 : - 0x349; - val_mask = (0xff << 0); - val_shift = 0; - break; - case (0x1 << 13): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x348 : - 0x349; - val_mask = (0xf << 0); - val_shift = 0; - break; - default: - addr = 0xffff; - break; - } - } else if (override_id == - NPHY_REV7_RFCTRLOVERRIDE_ID1) { - - switch (field) { - case (0x1 << 1): - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x1 << 1); - val_shift = 1; - break; - case (0x1 << 3): - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x1 << 3); - val_shift = 3; - break; - case (0x1 << 5): - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x1 << 5); - val_shift = 5; - break; - case (0x1 << 4): - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x1 << 4); - val_shift = 4; - break; - case (0x1 << 2): - - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x1 << 2); - val_shift = 2; - break; - case (0x1 << 7): - - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x7 << 8); - val_shift = 8; - break; - case (0x1 << 11): - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x1 << 14); - val_shift = 14; - break; - case (0x1 << 10): - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x1 << 13); - val_shift = 13; - break; - case (0x1 << 9): - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x1 << 12); - val_shift = 12; - break; - case (0x1 << 8): - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x1 << 11); - val_shift = 11; - break; - case (0x1 << 6): - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x1 << 6); - val_shift = 6; - break; - case (0x1 << 0): - en_addr = (core_num == 0) ? 0x342 : - 0x343; - val_addr = (core_num == 0) ? 0x340 : - 0x341; - val_mask = (0x1 << 0); - val_shift = 0; - break; - default: - addr = 0xffff; - break; - } - } else if (override_id == - NPHY_REV7_RFCTRLOVERRIDE_ID2) { - - switch (field) { - case (0x1 << 3): - en_addr = (core_num == 0) ? 0x346 : - 0x347; - val_addr = (core_num == 0) ? 0x344 : - 0x345; - val_mask = (0x1 << 3); - val_shift = 3; - break; - case (0x1 << 1): - en_addr = (core_num == 0) ? 0x346 : - 0x347; - val_addr = (core_num == 0) ? 0x344 : - 0x345; - val_mask = (0x1 << 1); - val_shift = 1; - break; - case (0x1 << 0): - en_addr = (core_num == 0) ? 0x346 : - 0x347; - val_addr = (core_num == 0) ? 0x344 : - 0x345; - val_mask = (0x1 << 0); - val_shift = 0; - break; - case (0x1 << 2): - en_addr = (core_num == 0) ? 0x346 : - 0x347; - val_addr = (core_num == 0) ? 0x344 : - 0x345; - val_mask = (0x1 << 2); - val_shift = 2; - break; - case (0x1 << 4): - en_addr = (core_num == 0) ? 0x346 : - 0x347; - val_addr = (core_num == 0) ? 0x344 : - 0x345; - val_mask = (0x1 << 4); - val_shift = 4; - break; - default: - addr = 0xffff; - break; - } - } - - if (off) { - and_phy_reg(pi, en_addr, ~en_mask); - and_phy_reg(pi, val_addr, ~val_mask); - } else { - - if ((core_mask == 0) - || (core_mask & (1 << core_num))) { - or_phy_reg(pi, en_addr, en_mask); - - if (addr != 0xffff) - mod_phy_reg(pi, val_addr, - val_mask, - (value << - val_shift)); - } - } - } - } -} - -static void wlc_phy_adjust_lnagaintbl_nphy(struct brcms_phy *pi) -{ - uint core; - int ctr; - s16 gain_delta[2]; - u8 curr_channel; - u16 minmax_gain[2]; - u16 regval[4]; - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - if (pi->nphy_gain_boost) { - if ((CHSPEC_IS2G(pi->radio_chanspec))) { - - gain_delta[0] = 6; - gain_delta[1] = 6; - } else { - - curr_channel = CHSPEC_CHANNEL(pi->radio_chanspec); - gain_delta[0] = - (s16) - PHY_HW_ROUND(((nphy_lnagain_est0[0] * - curr_channel) + - nphy_lnagain_est0[1]), 13); - gain_delta[1] = - (s16) - PHY_HW_ROUND(((nphy_lnagain_est1[0] * - curr_channel) + - nphy_lnagain_est1[1]), 13); - } - } else { - - gain_delta[0] = 0; - gain_delta[1] = 0; - } - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - if (pi->nphy_elna_gain_config) { - - regval[0] = nphy_def_lnagains[2] + gain_delta[core]; - regval[1] = nphy_def_lnagains[3] + gain_delta[core]; - regval[2] = nphy_def_lnagains[3] + gain_delta[core]; - regval[3] = nphy_def_lnagains[3] + gain_delta[core]; - } else { - for (ctr = 0; ctr < 4; ctr++) - regval[ctr] = - nphy_def_lnagains[ctr] + - gain_delta[core]; - } - wlc_phy_table_write_nphy(pi, core, 4, 8, 16, regval); - - minmax_gain[core] = - (u16) (nphy_def_lnagains[2] + gain_delta[core] + 4); - } - - mod_phy_reg(pi, 0x1e, (0xff << 0), (minmax_gain[0] << 0)); - mod_phy_reg(pi, 0x34, (0xff << 0), (minmax_gain[1] << 0)); - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -static void -wlc_phy_war_force_trsw_to_R_cliplo_nphy(struct brcms_phy *pi, u8 core) -{ - if (core == PHY_CORE_0) { - write_phy_reg(pi, 0x38, 0x4); - if (CHSPEC_IS2G(pi->radio_chanspec)) - write_phy_reg(pi, 0x37, 0x0060); - else - write_phy_reg(pi, 0x37, 0x1080); - } else if (core == PHY_CORE_1) { - write_phy_reg(pi, 0x2ae, 0x4); - if (CHSPEC_IS2G(pi->radio_chanspec)) - write_phy_reg(pi, 0x2ad, 0x0060); - else - write_phy_reg(pi, 0x2ad, 0x1080); - } -} - -static void wlc_phy_war_txchain_upd_nphy(struct brcms_phy *pi, u8 txchain) -{ - u8 txchain0, txchain1; - - txchain0 = txchain & 0x1; - txchain1 = (txchain & 0x2) >> 1; - if (!txchain0) - wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_0); - - if (!txchain1) - wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_1); -} - -static void wlc_phy_workarounds_nphy_gainctrl_2057_rev5(struct brcms_phy *pi) -{ - s8 lna1_gain_db[] = { 8, 13, 17, 22 }; - s8 lna2_gain_db[] = { -2, 7, 11, 15 }; - s8 tia_gain_db[] = { -4, -1, 2, 5, 5, 5, 5, 5, 5, 5 }; - s8 tia_gainbits[] = { - 0x0, 0x01, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }; - - mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); - mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); - - mod_phy_reg(pi, 0x289, (0xff << 0), (0x46 << 0)); - - mod_phy_reg(pi, 0x283, (0xff << 0), (0x3c << 0)); - mod_phy_reg(pi, 0x280, (0xff << 0), (0x3c << 0)); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x8, 8, - lna1_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x8, 8, - lna1_gain_db); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10, 8, - lna2_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10, 8, - lna2_gain_db); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, 8, - tia_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, 8, - tia_gain_db); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, 8, - tia_gainbits); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, 8, - tia_gainbits); - - write_phy_reg(pi, 0x37, 0x74); - write_phy_reg(pi, 0x2ad, 0x74); - write_phy_reg(pi, 0x38, 0x18); - write_phy_reg(pi, 0x2ae, 0x18); - - write_phy_reg(pi, 0x2b, 0xe8); - write_phy_reg(pi, 0x41, 0xe8); - - if (CHSPEC_IS20(pi->radio_chanspec)) { - - mod_phy_reg(pi, 0x300, (0x3f << 0), (0x12 << 0)); - mod_phy_reg(pi, 0x301, (0x3f << 0), (0x12 << 0)); - } else { - - mod_phy_reg(pi, 0x300, (0x3f << 0), (0x10 << 0)); - mod_phy_reg(pi, 0x301, (0x3f << 0), (0x10 << 0)); - } -} - -static void wlc_phy_workarounds_nphy_gainctrl_2057_rev6(struct brcms_phy *pi) -{ - u16 currband; - s8 lna1G_gain_db_rev7[] = { 9, 14, 19, 24 }; - s8 *lna1_gain_db = NULL; - s8 *lna1_gain_db_2 = NULL; - s8 *lna2_gain_db = NULL; - s8 tiaA_gain_db_rev7[] = { -9, -6, -3, 0, 3, 3, 3, 3, 3, 3 }; - s8 *tia_gain_db; - s8 tiaA_gainbits_rev7[] = { 0, 1, 2, 3, 4, 4, 4, 4, 4, 4 }; - s8 *tia_gainbits; - u16 rfseqA_init_gain_rev7[] = { 0x624f, 0x624f }; - u16 *rfseq_init_gain; - u16 init_gaincode; - u16 clip1hi_gaincode; - u16 clip1md_gaincode = 0; - u16 clip1md_gaincode_B; - u16 clip1lo_gaincode; - u16 clip1lo_gaincode_B; - u8 crsminl_th = 0; - u8 crsminu_th; - u16 nbclip_th = 0; - u8 w1clip_th; - u16 freq; - s8 nvar_baseline_offset0 = 0, nvar_baseline_offset1 = 0; - u8 chg_nbclip_th = 0; - - mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); - mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); - - currband = read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand; - if (currband == 0) { - - lna1_gain_db = lna1G_gain_db_rev7; - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, 8, - lna1_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, 8, - lna1_gain_db); - - mod_phy_reg(pi, 0x283, (0xff << 0), (0x40 << 0)); - - if (CHSPEC_IS40(pi->radio_chanspec)) { - mod_phy_reg(pi, 0x280, (0xff << 0), (0x3e << 0)); - mod_phy_reg(pi, 0x283, (0xff << 0), (0x3e << 0)); - } - - mod_phy_reg(pi, 0x289, (0xff << 0), (0x46 << 0)); - - if (CHSPEC_IS20(pi->radio_chanspec)) { - mod_phy_reg(pi, 0x300, (0x3f << 0), (13 << 0)); - mod_phy_reg(pi, 0x301, (0x3f << 0), (13 << 0)); - } - } else { - - init_gaincode = 0x9e; - clip1hi_gaincode = 0x9e; - clip1md_gaincode_B = 0x24; - clip1lo_gaincode = 0x8a; - clip1lo_gaincode_B = 8; - rfseq_init_gain = rfseqA_init_gain_rev7; - - tia_gain_db = tiaA_gain_db_rev7; - tia_gainbits = tiaA_gainbits_rev7; - - freq = CHAN5G_FREQ(CHSPEC_CHANNEL(pi->radio_chanspec)); - if (CHSPEC_IS20(pi->radio_chanspec)) { - - w1clip_th = 25; - clip1md_gaincode = 0x82; - - if ((freq <= 5080) || (freq == 5825)) { - - s8 lna1A_gain_db_rev7[] = { 11, 16, 20, 24 }; - s8 lna1A_gain_db_2_rev7[] = { - 11, 17, 22, 25}; - s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 }; - - crsminu_th = 0x3e; - lna1_gain_db = lna1A_gain_db_rev7; - lna1_gain_db_2 = lna1A_gain_db_2_rev7; - lna2_gain_db = lna2A_gain_db_rev7; - } else if ((freq >= 5500) && (freq <= 5700)) { - - s8 lna1A_gain_db_rev7[] = { 11, 17, 21, 25 }; - s8 lna1A_gain_db_2_rev7[] = { - 12, 18, 22, 26}; - s8 lna2A_gain_db_rev7[] = { 1, 8, 12, 16 }; - - crsminu_th = 0x45; - clip1md_gaincode_B = 0x14; - nbclip_th = 0xff; - chg_nbclip_th = 1; - lna1_gain_db = lna1A_gain_db_rev7; - lna1_gain_db_2 = lna1A_gain_db_2_rev7; - lna2_gain_db = lna2A_gain_db_rev7; - } else { - - s8 lna1A_gain_db_rev7[] = { 12, 18, 22, 26 }; - s8 lna1A_gain_db_2_rev7[] = { - 12, 18, 22, 26}; - s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 }; - - crsminu_th = 0x41; - lna1_gain_db = lna1A_gain_db_rev7; - lna1_gain_db_2 = lna1A_gain_db_2_rev7; - lna2_gain_db = lna2A_gain_db_rev7; - } - - if (freq <= 4920) { - nvar_baseline_offset0 = 5; - nvar_baseline_offset1 = 5; - } else if ((freq > 4920) && (freq <= 5320)) { - nvar_baseline_offset0 = 3; - nvar_baseline_offset1 = 5; - } else if ((freq > 5320) && (freq <= 5700)) { - nvar_baseline_offset0 = 3; - nvar_baseline_offset1 = 2; - } else { - nvar_baseline_offset0 = 4; - nvar_baseline_offset1 = 0; - } - } else { - - crsminu_th = 0x3a; - crsminl_th = 0x3a; - w1clip_th = 20; - - if ((freq >= 4920) && (freq <= 5320)) { - nvar_baseline_offset0 = 4; - nvar_baseline_offset1 = 5; - } else if ((freq > 5320) && (freq <= 5550)) { - nvar_baseline_offset0 = 4; - nvar_baseline_offset1 = 2; - } else { - nvar_baseline_offset0 = 5; - nvar_baseline_offset1 = 3; - } - } - - write_phy_reg(pi, 0x20, init_gaincode); - write_phy_reg(pi, 0x2a7, init_gaincode); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, - pi->pubpi.phy_corenum, 0x106, 16, - rfseq_init_gain); - - write_phy_reg(pi, 0x22, clip1hi_gaincode); - write_phy_reg(pi, 0x2a9, clip1hi_gaincode); - - write_phy_reg(pi, 0x36, clip1md_gaincode_B); - write_phy_reg(pi, 0x2ac, clip1md_gaincode_B); - - write_phy_reg(pi, 0x37, clip1lo_gaincode); - write_phy_reg(pi, 0x2ad, clip1lo_gaincode); - write_phy_reg(pi, 0x38, clip1lo_gaincode_B); - write_phy_reg(pi, 0x2ae, clip1lo_gaincode_B); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, 8, - tia_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, 8, - tia_gain_db); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, 8, - tia_gainbits); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, 8, - tia_gainbits); - - mod_phy_reg(pi, 0x283, (0xff << 0), (crsminu_th << 0)); - - if (chg_nbclip_th == 1) { - write_phy_reg(pi, 0x2b, nbclip_th); - write_phy_reg(pi, 0x41, nbclip_th); - } - - mod_phy_reg(pi, 0x300, (0x3f << 0), (w1clip_th << 0)); - mod_phy_reg(pi, 0x301, (0x3f << 0), (w1clip_th << 0)); - - mod_phy_reg(pi, 0x2e4, - (0x3f << 0), (nvar_baseline_offset0 << 0)); - - mod_phy_reg(pi, 0x2e4, - (0x3f << 6), (nvar_baseline_offset1 << 6)); - - if (CHSPEC_IS20(pi->radio_chanspec)) { - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, 8, - lna1_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, 8, - lna1_gain_db_2); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10, - 8, lna2_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10, - 8, lna2_gain_db); - - write_phy_reg(pi, 0x24, clip1md_gaincode); - write_phy_reg(pi, 0x2ab, clip1md_gaincode); - } else { - mod_phy_reg(pi, 0x280, (0xff << 0), (crsminl_th << 0)); - } - } -} - -static void wlc_phy_workarounds_nphy_gainctrl(struct brcms_phy *pi) -{ - u16 w1th, hpf_code, currband; - int ctr; - u8 rfseq_updategainu_events[] = { - NPHY_RFSEQ_CMD_RX_GAIN, - NPHY_RFSEQ_CMD_CLR_HIQ_DIS, - NPHY_RFSEQ_CMD_SET_HPF_BW - }; - u8 rfseq_updategainu_dlys[] = { 10, 30, 1 }; - s8 lna1G_gain_db[] = { 7, 11, 16, 23 }; - s8 lna1G_gain_db_rev4[] = { 8, 12, 17, 25 }; - s8 lna1G_gain_db_rev5[] = { 9, 13, 18, 26 }; - s8 lna1G_gain_db_rev6[] = { 8, 13, 18, 25 }; - s8 lna1G_gain_db_rev6_224B0[] = { 10, 14, 19, 27 }; - s8 lna1A_gain_db[] = { 7, 11, 17, 23 }; - s8 lna1A_gain_db_rev4[] = { 8, 12, 18, 23 }; - s8 lna1A_gain_db_rev5[] = { 6, 10, 16, 21 }; - s8 lna1A_gain_db_rev6[] = { 6, 10, 16, 21 }; - s8 *lna1_gain_db = NULL; - s8 lna2G_gain_db[] = { -5, 6, 10, 14 }; - s8 lna2G_gain_db_rev5[] = { -3, 7, 11, 16 }; - s8 lna2G_gain_db_rev6[] = { -5, 6, 10, 14 }; - s8 lna2G_gain_db_rev6_224B0[] = { -5, 6, 10, 15 }; - s8 lna2A_gain_db[] = { -6, 2, 6, 10 }; - s8 lna2A_gain_db_rev4[] = { -5, 2, 6, 10 }; - s8 lna2A_gain_db_rev5[] = { -7, 0, 4, 8 }; - s8 lna2A_gain_db_rev6[] = { -7, 0, 4, 8 }; - s8 *lna2_gain_db = NULL; - s8 tiaG_gain_db[] = { - 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }; - s8 tiaA_gain_db[] = { - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13 }; - s8 tiaA_gain_db_rev4[] = { - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; - s8 tiaA_gain_db_rev5[] = { - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; - s8 tiaA_gain_db_rev6[] = { - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; - s8 *tia_gain_db; - s8 tiaG_gainbits[] = { - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }; - s8 tiaA_gainbits[] = { - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 }; - s8 tiaA_gainbits_rev4[] = { - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; - s8 tiaA_gainbits_rev5[] = { - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; - s8 tiaA_gainbits_rev6[] = { - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; - s8 *tia_gainbits; - s8 lpf_gain_db[] = { 0x00, 0x06, 0x0c, 0x12, 0x12, 0x12 }; - s8 lpf_gainbits[] = { 0x00, 0x01, 0x02, 0x03, 0x03, 0x03 }; - u16 rfseqG_init_gain[] = { 0x613f, 0x613f, 0x613f, 0x613f }; - u16 rfseqG_init_gain_rev4[] = { 0x513f, 0x513f, 0x513f, 0x513f }; - u16 rfseqG_init_gain_rev5[] = { 0x413f, 0x413f, 0x413f, 0x413f }; - u16 rfseqG_init_gain_rev5_elna[] = { - 0x013f, 0x013f, 0x013f, 0x013f }; - u16 rfseqG_init_gain_rev6[] = { 0x513f, 0x513f }; - u16 rfseqG_init_gain_rev6_224B0[] = { 0x413f, 0x413f }; - u16 rfseqG_init_gain_rev6_elna[] = { 0x113f, 0x113f }; - u16 rfseqA_init_gain[] = { 0x516f, 0x516f, 0x516f, 0x516f }; - u16 rfseqA_init_gain_rev4[] = { 0x614f, 0x614f, 0x614f, 0x614f }; - u16 rfseqA_init_gain_rev4_elna[] = { - 0x314f, 0x314f, 0x314f, 0x314f }; - u16 rfseqA_init_gain_rev5[] = { 0x714f, 0x714f, 0x714f, 0x714f }; - u16 rfseqA_init_gain_rev6[] = { 0x714f, 0x714f }; - u16 *rfseq_init_gain; - u16 initG_gaincode = 0x627e; - u16 initG_gaincode_rev4 = 0x527e; - u16 initG_gaincode_rev5 = 0x427e; - u16 initG_gaincode_rev5_elna = 0x027e; - u16 initG_gaincode_rev6 = 0x527e; - u16 initG_gaincode_rev6_224B0 = 0x427e; - u16 initG_gaincode_rev6_elna = 0x127e; - u16 initA_gaincode = 0x52de; - u16 initA_gaincode_rev4 = 0x629e; - u16 initA_gaincode_rev4_elna = 0x329e; - u16 initA_gaincode_rev5 = 0x729e; - u16 initA_gaincode_rev6 = 0x729e; - u16 init_gaincode; - u16 clip1hiG_gaincode = 0x107e; - u16 clip1hiG_gaincode_rev4 = 0x007e; - u16 clip1hiG_gaincode_rev5 = 0x1076; - u16 clip1hiG_gaincode_rev6 = 0x007e; - u16 clip1hiA_gaincode = 0x00de; - u16 clip1hiA_gaincode_rev4 = 0x029e; - u16 clip1hiA_gaincode_rev5 = 0x029e; - u16 clip1hiA_gaincode_rev6 = 0x029e; - u16 clip1hi_gaincode; - u16 clip1mdG_gaincode = 0x0066; - u16 clip1mdA_gaincode = 0x00ca; - u16 clip1mdA_gaincode_rev4 = 0x1084; - u16 clip1mdA_gaincode_rev5 = 0x2084; - u16 clip1mdA_gaincode_rev6 = 0x2084; - u16 clip1md_gaincode = 0; - u16 clip1loG_gaincode = 0x0074; - u16 clip1loG_gaincode_rev5[] = { - 0x0062, 0x0064, 0x006a, 0x106a, 0x106c, 0x1074, 0x107c, 0x207c - }; - u16 clip1loG_gaincode_rev6[] = { - 0x106a, 0x106c, 0x1074, 0x107c, 0x007e, 0x107e, 0x207e, 0x307e - }; - u16 clip1loG_gaincode_rev6_224B0 = 0x1074; - u16 clip1loA_gaincode = 0x00cc; - u16 clip1loA_gaincode_rev4 = 0x0086; - u16 clip1loA_gaincode_rev5 = 0x2086; - u16 clip1loA_gaincode_rev6 = 0x2086; - u16 clip1lo_gaincode; - u8 crsminG_th = 0x18; - u8 crsminG_th_rev5 = 0x18; - u8 crsminG_th_rev6 = 0x18; - u8 crsminA_th = 0x1e; - u8 crsminA_th_rev4 = 0x24; - u8 crsminA_th_rev5 = 0x24; - u8 crsminA_th_rev6 = 0x24; - u8 crsmin_th; - u8 crsminlG_th = 0x18; - u8 crsminlG_th_rev5 = 0x18; - u8 crsminlG_th_rev6 = 0x18; - u8 crsminlA_th = 0x1e; - u8 crsminlA_th_rev4 = 0x24; - u8 crsminlA_th_rev5 = 0x24; - u8 crsminlA_th_rev6 = 0x24; - u8 crsminl_th = 0; - u8 crsminuG_th = 0x18; - u8 crsminuG_th_rev5 = 0x18; - u8 crsminuG_th_rev6 = 0x18; - u8 crsminuA_th = 0x1e; - u8 crsminuA_th_rev4 = 0x24; - u8 crsminuA_th_rev5 = 0x24; - u8 crsminuA_th_rev6 = 0x24; - u8 crsminuA_th_rev6_224B0 = 0x2d; - u8 crsminu_th; - u16 nbclipG_th = 0x20d; - u16 nbclipG_th_rev4 = 0x1a1; - u16 nbclipG_th_rev5 = 0x1d0; - u16 nbclipG_th_rev6 = 0x1d0; - u16 nbclipA_th = 0x1a1; - u16 nbclipA_th_rev4 = 0x107; - u16 nbclipA_th_rev5 = 0x0a9; - u16 nbclipA_th_rev6 = 0x0f0; - u16 nbclip_th = 0; - u8 w1clipG_th = 5; - u8 w1clipG_th_rev5 = 9; - u8 w1clipG_th_rev6 = 5; - u8 w1clipA_th = 25, w1clip_th; - u8 rssi_gain_default = 0x50; - u8 rssiG_gain_rev6_224B0 = 0x50; - u8 rssiA_gain_rev5 = 0x90; - u8 rssiA_gain_rev6 = 0x90; - u8 rssi_gain; - u16 regval[21]; - u8 triso; - - triso = (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g.triso : - pi->srom_fem2g.triso; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if (pi->pubpi.radiorev == 5) { - wlc_phy_workarounds_nphy_gainctrl_2057_rev5(pi); - } else if (pi->pubpi.radiorev == 7) { - wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi); - - mod_phy_reg(pi, 0x283, (0xff << 0), (0x44 << 0)); - mod_phy_reg(pi, 0x280, (0xff << 0), (0x44 << 0)); - - } else if ((pi->pubpi.radiorev == 3) - || (pi->pubpi.radiorev == 8)) { - wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi); - - if (pi->pubpi.radiorev == 8) { - mod_phy_reg(pi, 0x283, - (0xff << 0), (0x44 << 0)); - mod_phy_reg(pi, 0x280, - (0xff << 0), (0x44 << 0)); - } - } else { - wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi); - } - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - - mod_phy_reg(pi, 0xa0, (0x1 << 6), (1 << 6)); - - mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); - mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); - - currband = - read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand; - if (currband == 0) { - if (NREV_GE(pi->pubpi.phy_rev, 6)) { - if (pi->pubpi.radiorev == 11) { - lna1_gain_db = lna1G_gain_db_rev6_224B0; - lna2_gain_db = lna2G_gain_db_rev6_224B0; - rfseq_init_gain = - rfseqG_init_gain_rev6_224B0; - init_gaincode = - initG_gaincode_rev6_224B0; - clip1hi_gaincode = - clip1hiG_gaincode_rev6; - clip1lo_gaincode = - clip1loG_gaincode_rev6_224B0; - nbclip_th = nbclipG_th_rev6; - w1clip_th = w1clipG_th_rev6; - crsmin_th = crsminG_th_rev6; - crsminl_th = crsminlG_th_rev6; - crsminu_th = crsminuG_th_rev6; - rssi_gain = rssiG_gain_rev6_224B0; - } else { - lna1_gain_db = lna1G_gain_db_rev6; - lna2_gain_db = lna2G_gain_db_rev6; - if (pi->sh->boardflags & BFL_EXTLNA) { - - rfseq_init_gain = - rfseqG_init_gain_rev6_elna; - init_gaincode = - initG_gaincode_rev6_elna; - } else { - rfseq_init_gain = - rfseqG_init_gain_rev6; - init_gaincode = - initG_gaincode_rev6; - } - clip1hi_gaincode = - clip1hiG_gaincode_rev6; - switch (triso) { - case 0: - clip1lo_gaincode = - clip1loG_gaincode_rev6 - [0]; - break; - case 1: - clip1lo_gaincode = - clip1loG_gaincode_rev6 - [1]; - break; - case 2: - clip1lo_gaincode = - clip1loG_gaincode_rev6 - [2]; - break; - case 3: - default: - - clip1lo_gaincode = - clip1loG_gaincode_rev6 - [3]; - break; - case 4: - clip1lo_gaincode = - clip1loG_gaincode_rev6 - [4]; - break; - case 5: - clip1lo_gaincode = - clip1loG_gaincode_rev6 - [5]; - break; - case 6: - clip1lo_gaincode = - clip1loG_gaincode_rev6 - [6]; - break; - case 7: - clip1lo_gaincode = - clip1loG_gaincode_rev6 - [7]; - break; - } - nbclip_th = nbclipG_th_rev6; - w1clip_th = w1clipG_th_rev6; - crsmin_th = crsminG_th_rev6; - crsminl_th = crsminlG_th_rev6; - crsminu_th = crsminuG_th_rev6; - rssi_gain = rssi_gain_default; - } - } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { - lna1_gain_db = lna1G_gain_db_rev5; - lna2_gain_db = lna2G_gain_db_rev5; - if (pi->sh->boardflags & BFL_EXTLNA) { - - rfseq_init_gain = - rfseqG_init_gain_rev5_elna; - init_gaincode = - initG_gaincode_rev5_elna; - } else { - rfseq_init_gain = rfseqG_init_gain_rev5; - init_gaincode = initG_gaincode_rev5; - } - clip1hi_gaincode = clip1hiG_gaincode_rev5; - switch (triso) { - case 0: - clip1lo_gaincode = - clip1loG_gaincode_rev5[0]; - break; - case 1: - clip1lo_gaincode = - clip1loG_gaincode_rev5[1]; - break; - case 2: - clip1lo_gaincode = - clip1loG_gaincode_rev5[2]; - break; - case 3: - - clip1lo_gaincode = - clip1loG_gaincode_rev5[3]; - break; - case 4: - clip1lo_gaincode = - clip1loG_gaincode_rev5[4]; - break; - case 5: - clip1lo_gaincode = - clip1loG_gaincode_rev5[5]; - break; - case 6: - clip1lo_gaincode = - clip1loG_gaincode_rev5[6]; - break; - case 7: - clip1lo_gaincode = - clip1loG_gaincode_rev5[7]; - break; - default: - clip1lo_gaincode = - clip1loG_gaincode_rev5[3]; - break; - } - nbclip_th = nbclipG_th_rev5; - w1clip_th = w1clipG_th_rev5; - crsmin_th = crsminG_th_rev5; - crsminl_th = crsminlG_th_rev5; - crsminu_th = crsminuG_th_rev5; - rssi_gain = rssi_gain_default; - } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { - lna1_gain_db = lna1G_gain_db_rev4; - lna2_gain_db = lna2G_gain_db; - rfseq_init_gain = rfseqG_init_gain_rev4; - init_gaincode = initG_gaincode_rev4; - clip1hi_gaincode = clip1hiG_gaincode_rev4; - clip1lo_gaincode = clip1loG_gaincode; - nbclip_th = nbclipG_th_rev4; - w1clip_th = w1clipG_th; - crsmin_th = crsminG_th; - crsminl_th = crsminlG_th; - crsminu_th = crsminuG_th; - rssi_gain = rssi_gain_default; - } else { - lna1_gain_db = lna1G_gain_db; - lna2_gain_db = lna2G_gain_db; - rfseq_init_gain = rfseqG_init_gain; - init_gaincode = initG_gaincode; - clip1hi_gaincode = clip1hiG_gaincode; - clip1lo_gaincode = clip1loG_gaincode; - nbclip_th = nbclipG_th; - w1clip_th = w1clipG_th; - crsmin_th = crsminG_th; - crsminl_th = crsminlG_th; - crsminu_th = crsminuG_th; - rssi_gain = rssi_gain_default; - } - tia_gain_db = tiaG_gain_db; - tia_gainbits = tiaG_gainbits; - clip1md_gaincode = clip1mdG_gaincode; - } else { - if (NREV_GE(pi->pubpi.phy_rev, 6)) { - lna1_gain_db = lna1A_gain_db_rev6; - lna2_gain_db = lna2A_gain_db_rev6; - tia_gain_db = tiaA_gain_db_rev6; - tia_gainbits = tiaA_gainbits_rev6; - rfseq_init_gain = rfseqA_init_gain_rev6; - init_gaincode = initA_gaincode_rev6; - clip1hi_gaincode = clip1hiA_gaincode_rev6; - clip1md_gaincode = clip1mdA_gaincode_rev6; - clip1lo_gaincode = clip1loA_gaincode_rev6; - crsmin_th = crsminA_th_rev6; - crsminl_th = crsminlA_th_rev6; - if ((pi->pubpi.radiorev == 11) && - (CHSPEC_IS40(pi->radio_chanspec) == 0)) - crsminu_th = crsminuA_th_rev6_224B0; - else - crsminu_th = crsminuA_th_rev6; - - nbclip_th = nbclipA_th_rev6; - rssi_gain = rssiA_gain_rev6; - } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { - lna1_gain_db = lna1A_gain_db_rev5; - lna2_gain_db = lna2A_gain_db_rev5; - tia_gain_db = tiaA_gain_db_rev5; - tia_gainbits = tiaA_gainbits_rev5; - rfseq_init_gain = rfseqA_init_gain_rev5; - init_gaincode = initA_gaincode_rev5; - clip1hi_gaincode = clip1hiA_gaincode_rev5; - clip1md_gaincode = clip1mdA_gaincode_rev5; - clip1lo_gaincode = clip1loA_gaincode_rev5; - crsmin_th = crsminA_th_rev5; - crsminl_th = crsminlA_th_rev5; - crsminu_th = crsminuA_th_rev5; - nbclip_th = nbclipA_th_rev5; - rssi_gain = rssiA_gain_rev5; - } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { - lna1_gain_db = lna1A_gain_db_rev4; - lna2_gain_db = lna2A_gain_db_rev4; - tia_gain_db = tiaA_gain_db_rev4; - tia_gainbits = tiaA_gainbits_rev4; - if (pi->sh->boardflags & BFL_EXTLNA_5GHz) { - - rfseq_init_gain = - rfseqA_init_gain_rev4_elna; - init_gaincode = - initA_gaincode_rev4_elna; - } else { - rfseq_init_gain = rfseqA_init_gain_rev4; - init_gaincode = initA_gaincode_rev4; - } - clip1hi_gaincode = clip1hiA_gaincode_rev4; - clip1md_gaincode = clip1mdA_gaincode_rev4; - clip1lo_gaincode = clip1loA_gaincode_rev4; - crsmin_th = crsminA_th_rev4; - crsminl_th = crsminlA_th_rev4; - crsminu_th = crsminuA_th_rev4; - nbclip_th = nbclipA_th_rev4; - rssi_gain = rssi_gain_default; - } else { - lna1_gain_db = lna1A_gain_db; - lna2_gain_db = lna2A_gain_db; - tia_gain_db = tiaA_gain_db; - tia_gainbits = tiaA_gainbits; - rfseq_init_gain = rfseqA_init_gain; - init_gaincode = initA_gaincode; - clip1hi_gaincode = clip1hiA_gaincode; - clip1md_gaincode = clip1mdA_gaincode; - clip1lo_gaincode = clip1loA_gaincode; - crsmin_th = crsminA_th; - crsminl_th = crsminlA_th; - crsminu_th = crsminuA_th; - nbclip_th = nbclipA_th; - rssi_gain = rssi_gain_default; - } - w1clip_th = w1clipA_th; - } - - write_radio_reg(pi, - (RADIO_2056_RX_BIASPOLE_LNAG1_IDAC | - RADIO_2056_RX0), 0x17); - write_radio_reg(pi, - (RADIO_2056_RX_BIASPOLE_LNAG1_IDAC | - RADIO_2056_RX1), 0x17); - - write_radio_reg(pi, (RADIO_2056_RX_LNAG2_IDAC | RADIO_2056_RX0), - 0xf0); - write_radio_reg(pi, (RADIO_2056_RX_LNAG2_IDAC | RADIO_2056_RX1), - 0xf0); - - write_radio_reg(pi, (RADIO_2056_RX_RSSI_POLE | RADIO_2056_RX0), - 0x0); - write_radio_reg(pi, (RADIO_2056_RX_RSSI_POLE | RADIO_2056_RX1), - 0x0); - - write_radio_reg(pi, (RADIO_2056_RX_RSSI_GAIN | RADIO_2056_RX0), - rssi_gain); - write_radio_reg(pi, (RADIO_2056_RX_RSSI_GAIN | RADIO_2056_RX1), - rssi_gain); - - write_radio_reg(pi, - (RADIO_2056_RX_BIASPOLE_LNAA1_IDAC | - RADIO_2056_RX0), 0x17); - write_radio_reg(pi, - (RADIO_2056_RX_BIASPOLE_LNAA1_IDAC | - RADIO_2056_RX1), 0x17); - - write_radio_reg(pi, (RADIO_2056_RX_LNAA2_IDAC | RADIO_2056_RX0), - 0xFF); - write_radio_reg(pi, (RADIO_2056_RX_LNAA2_IDAC | RADIO_2056_RX1), - 0xFF); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, - 8, lna1_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, - 8, lna1_gain_db); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10, - 8, lna2_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10, - 8, lna2_gain_db); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, - 8, tia_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, - 8, tia_gain_db); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, - 8, tia_gainbits); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, - 8, tia_gainbits); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 6, 0x40, - 8, &lpf_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 6, 0x40, - 8, &lpf_gain_db); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 6, 0x40, - 8, &lpf_gainbits); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 6, 0x40, - 8, &lpf_gainbits); - - write_phy_reg(pi, 0x20, init_gaincode); - write_phy_reg(pi, 0x2a7, init_gaincode); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, - pi->pubpi.phy_corenum, 0x106, 16, - rfseq_init_gain); - - write_phy_reg(pi, 0x22, clip1hi_gaincode); - write_phy_reg(pi, 0x2a9, clip1hi_gaincode); - - write_phy_reg(pi, 0x24, clip1md_gaincode); - write_phy_reg(pi, 0x2ab, clip1md_gaincode); - - write_phy_reg(pi, 0x37, clip1lo_gaincode); - write_phy_reg(pi, 0x2ad, clip1lo_gaincode); - - mod_phy_reg(pi, 0x27d, (0xff << 0), (crsmin_th << 0)); - mod_phy_reg(pi, 0x280, (0xff << 0), (crsminl_th << 0)); - mod_phy_reg(pi, 0x283, (0xff << 0), (crsminu_th << 0)); - - write_phy_reg(pi, 0x2b, nbclip_th); - write_phy_reg(pi, 0x41, nbclip_th); - - mod_phy_reg(pi, 0x27, (0x3f << 0), (w1clip_th << 0)); - mod_phy_reg(pi, 0x3d, (0x3f << 0), (w1clip_th << 0)); - - write_phy_reg(pi, 0x150, 0x809c); - - } else { - - mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); - mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); - - write_phy_reg(pi, 0x2b, 0x84); - write_phy_reg(pi, 0x41, 0x84); - - if (CHSPEC_IS20(pi->radio_chanspec)) { - write_phy_reg(pi, 0x6b, 0x2b); - write_phy_reg(pi, 0x6c, 0x2b); - write_phy_reg(pi, 0x6d, 0x9); - write_phy_reg(pi, 0x6e, 0x9); - } - - w1th = NPHY_RSSICAL_W1_TARGET - 4; - mod_phy_reg(pi, 0x27, (0x3f << 0), (w1th << 0)); - mod_phy_reg(pi, 0x3d, (0x3f << 0), (w1th << 0)); - - if (CHSPEC_IS20(pi->radio_chanspec)) { - mod_phy_reg(pi, 0x1c, (0x1f << 0), (0x1 << 0)); - mod_phy_reg(pi, 0x32, (0x1f << 0), (0x1 << 0)); - - mod_phy_reg(pi, 0x1d, (0x1f << 0), (0x1 << 0)); - mod_phy_reg(pi, 0x33, (0x1f << 0), (0x1 << 0)); - } - - write_phy_reg(pi, 0x150, 0x809c); - - if (pi->nphy_gain_boost) - if ((CHSPEC_IS2G(pi->radio_chanspec)) && - (CHSPEC_IS40(pi->radio_chanspec))) - hpf_code = 4; - else - hpf_code = 5; - else if (CHSPEC_IS40(pi->radio_chanspec)) - hpf_code = 6; - else - hpf_code = 7; - - mod_phy_reg(pi, 0x20, (0x1f << 7), (hpf_code << 7)); - mod_phy_reg(pi, 0x36, (0x1f << 7), (hpf_code << 7)); - - for (ctr = 0; ctr < 4; ctr++) - regval[ctr] = (hpf_code << 8) | 0x7c; - wlc_phy_table_write_nphy(pi, 7, 4, 0x106, 16, regval); - - wlc_phy_adjust_lnagaintbl_nphy(pi); - - if (pi->nphy_elna_gain_config) { - regval[0] = 0; - regval[1] = 1; - regval[2] = 1; - regval[3] = 1; - wlc_phy_table_write_nphy(pi, 2, 4, 8, 16, regval); - wlc_phy_table_write_nphy(pi, 3, 4, 8, 16, regval); - - for (ctr = 0; ctr < 4; ctr++) - regval[ctr] = (hpf_code << 8) | 0x74; - wlc_phy_table_write_nphy(pi, 7, 4, 0x106, 16, regval); - } - - if (NREV_IS(pi->pubpi.phy_rev, 2)) { - for (ctr = 0; ctr < 21; ctr++) - regval[ctr] = 3 * ctr; - wlc_phy_table_write_nphy(pi, 0, 21, 32, 16, regval); - wlc_phy_table_write_nphy(pi, 1, 21, 32, 16, regval); - - for (ctr = 0; ctr < 21; ctr++) - regval[ctr] = (u16) ctr; - wlc_phy_table_write_nphy(pi, 2, 21, 32, 16, regval); - wlc_phy_table_write_nphy(pi, 3, 21, 32, 16, regval); - } - - wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_UPDATEGAINU, - rfseq_updategainu_events, - rfseq_updategainu_dlys, - sizeof(rfseq_updategainu_events) / - sizeof(rfseq_updategainu_events[0])); - - mod_phy_reg(pi, 0x153, (0xff << 8), (90 << 8)); - - if (CHSPEC_IS2G(pi->radio_chanspec)) - mod_phy_reg(pi, - (NPHY_TO_BPHY_OFF + BPHY_OPTIONAL_MODES), - 0x7f, 0x4); - } -} - -static void wlc_phy_workarounds_nphy(struct brcms_phy *pi) -{ - u8 rfseq_rx2tx_events[] = { - NPHY_RFSEQ_CMD_NOP, - NPHY_RFSEQ_CMD_RXG_FBW, - NPHY_RFSEQ_CMD_TR_SWITCH, - NPHY_RFSEQ_CMD_CLR_HIQ_DIS, - NPHY_RFSEQ_CMD_RXPD_TXPD, - NPHY_RFSEQ_CMD_TX_GAIN, - NPHY_RFSEQ_CMD_EXT_PA - }; - u8 rfseq_rx2tx_dlys[] = { 8, 6, 6, 2, 4, 60, 1 }; - u8 rfseq_tx2rx_events[] = { - NPHY_RFSEQ_CMD_NOP, - NPHY_RFSEQ_CMD_EXT_PA, - NPHY_RFSEQ_CMD_TX_GAIN, - NPHY_RFSEQ_CMD_RXPD_TXPD, - NPHY_RFSEQ_CMD_TR_SWITCH, - NPHY_RFSEQ_CMD_RXG_FBW, - NPHY_RFSEQ_CMD_CLR_HIQ_DIS - }; - u8 rfseq_tx2rx_dlys[] = { 8, 6, 2, 4, 4, 6, 1 }; - u8 rfseq_tx2rx_events_rev3[] = { - NPHY_REV3_RFSEQ_CMD_EXT_PA, - NPHY_REV3_RFSEQ_CMD_INT_PA_PU, - NPHY_REV3_RFSEQ_CMD_TX_GAIN, - NPHY_REV3_RFSEQ_CMD_RXPD_TXPD, - NPHY_REV3_RFSEQ_CMD_TR_SWITCH, - NPHY_REV3_RFSEQ_CMD_RXG_FBW, - NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS, - NPHY_REV3_RFSEQ_CMD_END - }; - u8 rfseq_tx2rx_dlys_rev3[] = { 8, 4, 2, 2, 4, 4, 6, 1 }; - u8 rfseq_rx2tx_events_rev3[] = { - NPHY_REV3_RFSEQ_CMD_NOP, - NPHY_REV3_RFSEQ_CMD_RXG_FBW, - NPHY_REV3_RFSEQ_CMD_TR_SWITCH, - NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS, - NPHY_REV3_RFSEQ_CMD_RXPD_TXPD, - NPHY_REV3_RFSEQ_CMD_TX_GAIN, - NPHY_REV3_RFSEQ_CMD_INT_PA_PU, - NPHY_REV3_RFSEQ_CMD_EXT_PA, - NPHY_REV3_RFSEQ_CMD_END - }; - u8 rfseq_rx2tx_dlys_rev3[] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 }; - - u8 rfseq_rx2tx_events_rev3_ipa[] = { - NPHY_REV3_RFSEQ_CMD_NOP, - NPHY_REV3_RFSEQ_CMD_RXG_FBW, - NPHY_REV3_RFSEQ_CMD_TR_SWITCH, - NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS, - NPHY_REV3_RFSEQ_CMD_RXPD_TXPD, - NPHY_REV3_RFSEQ_CMD_TX_GAIN, - NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS, - NPHY_REV3_RFSEQ_CMD_INT_PA_PU, - NPHY_REV3_RFSEQ_CMD_END - }; - u8 rfseq_rx2tx_dlys_rev3_ipa[] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 }; - u16 rfseq_rx2tx_dacbufpu_rev7[] = { 0x10f, 0x10f }; - - s16 alpha0, alpha1, alpha2; - s16 beta0, beta1, beta2; - u32 leg_data_weights, ht_data_weights, nss1_data_weights, - stbc_data_weights; - u8 chan_freq_range = 0; - u16 dac_control = 0x0002; - u16 aux_adc_vmid_rev7_core0[] = { 0x8e, 0x96, 0x96, 0x96 }; - u16 aux_adc_vmid_rev7_core1[] = { 0x8f, 0x9f, 0x9f, 0x96 }; - u16 aux_adc_vmid_rev4[] = { 0xa2, 0xb4, 0xb4, 0x89 }; - u16 aux_adc_vmid_rev3[] = { 0xa2, 0xb4, 0xb4, 0x89 }; - u16 *aux_adc_vmid; - u16 aux_adc_gain_rev7[] = { 0x02, 0x02, 0x02, 0x02 }; - u16 aux_adc_gain_rev4[] = { 0x02, 0x02, 0x02, 0x00 }; - u16 aux_adc_gain_rev3[] = { 0x02, 0x02, 0x02, 0x00 }; - u16 *aux_adc_gain; - u16 sk_adc_vmid[] = { 0xb4, 0xb4, 0xb4, 0x24 }; - u16 sk_adc_gain[] = { 0x02, 0x02, 0x02, 0x02 }; - s32 min_nvar_val = 0x18d; - s32 min_nvar_offset_6mbps = 20; - u8 pdetrange; - u8 triso; - u16 regval; - u16 afectrl_adc_ctrl1_rev7 = 0x20; - u16 afectrl_adc_ctrl2_rev7 = 0x0; - u16 rfseq_rx2tx_lpf_h_hpc_rev7 = 0x77; - u16 rfseq_tx2rx_lpf_h_hpc_rev7 = 0x77; - u16 rfseq_pktgn_lpf_h_hpc_rev7 = 0x77; - u16 rfseq_htpktgn_lpf_hpc_rev7[] = { 0x77, 0x11, 0x11 }; - u16 rfseq_pktgn_lpf_hpc_rev7[] = { 0x11, 0x11 }; - u16 rfseq_cckpktgn_lpf_hpc_rev7[] = { 0x11, 0x11 }; - u16 ipalvlshift_3p3_war_en = 0; - u16 rccal_bcap_val, rccal_scap_val; - u16 rccal_tx20_11b_bcap = 0; - u16 rccal_tx20_11b_scap = 0; - u16 rccal_tx20_11n_bcap = 0; - u16 rccal_tx20_11n_scap = 0; - u16 rccal_tx40_11n_bcap = 0; - u16 rccal_tx40_11n_scap = 0; - u16 rx2tx_lpf_rc_lut_tx20_11b = 0; - u16 rx2tx_lpf_rc_lut_tx20_11n = 0; - u16 rx2tx_lpf_rc_lut_tx40_11n = 0; - u16 tx_lpf_bw_ofdm_20mhz = 0; - u16 tx_lpf_bw_ofdm_40mhz = 0; - u16 tx_lpf_bw_11b = 0; - u16 ipa2g_mainbias, ipa2g_casconv, ipa2g_biasfilt; - u16 txgm_idac_bleed = 0; - bool rccal_ovrd = false; - u16 freq; - int coreNum; - - if (CHSPEC_IS5G(pi->radio_chanspec)) - wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_cck_en, 0); - else - wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_cck_en, 1); - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - or_phy_reg(pi, 0xb1, NPHY_IQFlip_ADC1 | NPHY_IQFlip_ADC2); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - if (NREV_IS(pi->pubpi.phy_rev, 7)) { - mod_phy_reg(pi, 0x221, (0x1 << 4), (1 << 4)); - - mod_phy_reg(pi, 0x160, (0x7f << 0), (32 << 0)); - mod_phy_reg(pi, 0x160, (0x7f << 8), (39 << 8)); - mod_phy_reg(pi, 0x161, (0x7f << 0), (46 << 0)); - mod_phy_reg(pi, 0x161, (0x7f << 8), (51 << 8)); - mod_phy_reg(pi, 0x162, (0x7f << 0), (55 << 0)); - mod_phy_reg(pi, 0x162, (0x7f << 8), (58 << 8)); - mod_phy_reg(pi, 0x163, (0x7f << 0), (60 << 0)); - mod_phy_reg(pi, 0x163, (0x7f << 8), (62 << 8)); - mod_phy_reg(pi, 0x164, (0x7f << 0), (62 << 0)); - mod_phy_reg(pi, 0x164, (0x7f << 8), (63 << 8)); - mod_phy_reg(pi, 0x165, (0x7f << 0), (63 << 0)); - mod_phy_reg(pi, 0x165, (0x7f << 8), (64 << 8)); - mod_phy_reg(pi, 0x166, (0x7f << 0), (64 << 0)); - mod_phy_reg(pi, 0x166, (0x7f << 8), (64 << 8)); - mod_phy_reg(pi, 0x167, (0x7f << 0), (64 << 0)); - mod_phy_reg(pi, 0x167, (0x7f << 8), (64 << 8)); - } - - if (NREV_LE(pi->pubpi.phy_rev, 8)) { - write_phy_reg(pi, 0x23f, 0x1b0); - write_phy_reg(pi, 0x240, 0x1b0); - } - - if (NREV_GE(pi->pubpi.phy_rev, 8)) - mod_phy_reg(pi, 0xbd, (0xff << 0), (114 << 0)); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x00, 16, - &dac_control); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x10, 16, - &dac_control); - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, - 1, 0, 32, &leg_data_weights); - leg_data_weights = leg_data_weights & 0xffffff; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, - 1, 0, 32, &leg_data_weights); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, - 2, 0x15e, 16, - rfseq_rx2tx_dacbufpu_rev7); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x16e, 16, - rfseq_rx2tx_dacbufpu_rev7); - - if (PHY_IPA(pi)) - wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX, - rfseq_rx2tx_events_rev3_ipa, - rfseq_rx2tx_dlys_rev3_ipa, - ARRAY_SIZE(rfseq_rx2tx_events_rev3_ipa)); - - mod_phy_reg(pi, 0x299, (0x3 << 14), (0x1 << 14)); - mod_phy_reg(pi, 0x29d, (0x3 << 14), (0x1 << 14)); - - tx_lpf_bw_ofdm_20mhz = wlc_phy_read_lpf_bw_ctl_nphy(pi, 0x154); - tx_lpf_bw_ofdm_40mhz = wlc_phy_read_lpf_bw_ctl_nphy(pi, 0x159); - tx_lpf_bw_11b = wlc_phy_read_lpf_bw_ctl_nphy(pi, 0x152); - - if (PHY_IPA(pi)) { - - if (((pi->pubpi.radiorev == 5) - && (CHSPEC_IS40(pi->radio_chanspec) == 1)) - || (pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) { - - rccal_bcap_val = - read_radio_reg( - pi, - RADIO_2057_RCCAL_BCAP_VAL); - rccal_scap_val = - read_radio_reg( - pi, - RADIO_2057_RCCAL_SCAP_VAL); - - rccal_tx20_11b_bcap = rccal_bcap_val; - rccal_tx20_11b_scap = rccal_scap_val; - - if ((pi->pubpi.radiorev == 5) && - (CHSPEC_IS40(pi->radio_chanspec) == 1)) { - - rccal_tx20_11n_bcap = rccal_bcap_val; - rccal_tx20_11n_scap = rccal_scap_val; - rccal_tx40_11n_bcap = 0xc; - rccal_tx40_11n_scap = 0xc; - - rccal_ovrd = true; - - } else if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) { - - tx_lpf_bw_ofdm_20mhz = 4; - tx_lpf_bw_11b = 1; - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - rccal_tx20_11n_bcap = 0xc; - rccal_tx20_11n_scap = 0xc; - rccal_tx40_11n_bcap = 0xa; - rccal_tx40_11n_scap = 0xa; - } else { - rccal_tx20_11n_bcap = 0x14; - rccal_tx20_11n_scap = 0x14; - rccal_tx40_11n_bcap = 0xf; - rccal_tx40_11n_scap = 0xf; - } - - rccal_ovrd = true; - } - } - - } else { - - if (pi->pubpi.radiorev == 5) { - - tx_lpf_bw_ofdm_20mhz = 1; - tx_lpf_bw_ofdm_40mhz = 3; - - rccal_bcap_val = - read_radio_reg( - pi, - RADIO_2057_RCCAL_BCAP_VAL); - rccal_scap_val = - read_radio_reg( - pi, - RADIO_2057_RCCAL_SCAP_VAL); - - rccal_tx20_11b_bcap = rccal_bcap_val; - rccal_tx20_11b_scap = rccal_scap_val; - - rccal_tx20_11n_bcap = 0x13; - rccal_tx20_11n_scap = 0x11; - rccal_tx40_11n_bcap = 0x13; - rccal_tx40_11n_scap = 0x11; - - rccal_ovrd = true; - } - } - - if (rccal_ovrd) { - - rx2tx_lpf_rc_lut_tx20_11b = - (rccal_tx20_11b_bcap << 8) | - (rccal_tx20_11b_scap << 3) | - tx_lpf_bw_11b; - rx2tx_lpf_rc_lut_tx20_11n = - (rccal_tx20_11n_bcap << 8) | - (rccal_tx20_11n_scap << 3) | - tx_lpf_bw_ofdm_20mhz; - rx2tx_lpf_rc_lut_tx40_11n = - (rccal_tx40_11n_bcap << 8) | - (rccal_tx40_11n_scap << 3) | - tx_lpf_bw_ofdm_40mhz; - - for (coreNum = 0; coreNum <= 1; coreNum++) { - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_RFSEQ, - 1, - 0x152 + coreNum * 0x10, - 16, - &rx2tx_lpf_rc_lut_tx20_11b); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_RFSEQ, - 1, - 0x153 + coreNum * 0x10, - 16, - &rx2tx_lpf_rc_lut_tx20_11n); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_RFSEQ, - 1, - 0x154 + coreNum * 0x10, - 16, - &rx2tx_lpf_rc_lut_tx20_11n); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_RFSEQ, - 1, - 0x155 + coreNum * 0x10, - 16, - &rx2tx_lpf_rc_lut_tx40_11n); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_RFSEQ, - 1, - 0x156 + coreNum * 0x10, - 16, - &rx2tx_lpf_rc_lut_tx40_11n); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_RFSEQ, - 1, - 0x157 + coreNum * 0x10, - 16, - &rx2tx_lpf_rc_lut_tx40_11n); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_RFSEQ, - 1, - 0x158 + coreNum * 0x10, - 16, - &rx2tx_lpf_rc_lut_tx40_11n); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_RFSEQ, - 1, - 0x159 + coreNum * 0x10, - 16, - &rx2tx_lpf_rc_lut_tx40_11n); - } - - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 4), - 1, 0x3, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID2); - } - - write_phy_reg(pi, 0x32f, 0x3); - - if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 2), - 1, 0x3, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - - if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || - (pi->pubpi.radiorev == 6)) { - if ((pi->sh->sromrev >= 8) - && (pi->sh->boardflags2 & BFL2_IPALVLSHIFT_3P3)) - ipalvlshift_3p3_war_en = 1; - - if (ipalvlshift_3p3_war_en) { - write_radio_reg(pi, RADIO_2057_GPAIO_CONFIG, - 0x5); - write_radio_reg(pi, RADIO_2057_GPAIO_SEL1, - 0x30); - write_radio_reg(pi, RADIO_2057_GPAIO_SEL0, 0x0); - or_radio_reg(pi, - RADIO_2057_RXTXBIAS_CONFIG_CORE0, - 0x1); - or_radio_reg(pi, - RADIO_2057_RXTXBIAS_CONFIG_CORE1, - 0x1); - - ipa2g_mainbias = 0x1f; - - ipa2g_casconv = 0x6f; - - ipa2g_biasfilt = 0xaa; - } else { - - ipa2g_mainbias = 0x2b; - - ipa2g_casconv = 0x7f; - - ipa2g_biasfilt = 0xee; - } - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - for (coreNum = 0; coreNum <= 1; coreNum++) { - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, - coreNum, IPA2G_IMAIN, - ipa2g_mainbias); - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, - coreNum, IPA2G_CASCONV, - ipa2g_casconv); - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, - coreNum, - IPA2G_BIAS_FILTER, - ipa2g_biasfilt); - } - } - } - - if (PHY_IPA(pi)) { - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if ((pi->pubpi.radiorev == 3) - || (pi->pubpi.radiorev == 4) - || (pi->pubpi.radiorev == 6)) - txgm_idac_bleed = 0x7f; - - for (coreNum = 0; coreNum <= 1; coreNum++) { - if (txgm_idac_bleed != 0) - WRITE_RADIO_REG4( - pi, RADIO_2057, - CORE, coreNum, - TXGM_IDAC_BLEED, - txgm_idac_bleed); - } - - if (pi->pubpi.radiorev == 5) { - - for (coreNum = 0; coreNum <= 1; - coreNum++) { - WRITE_RADIO_REG4(pi, RADIO_2057, - CORE, coreNum, - IPA2G_CASCONV, - 0x13); - WRITE_RADIO_REG4(pi, RADIO_2057, - CORE, coreNum, - IPA2G_IMAIN, - 0x1f); - WRITE_RADIO_REG4( - pi, RADIO_2057, - CORE, coreNum, - IPA2G_BIAS_FILTER, - 0xee); - WRITE_RADIO_REG4(pi, RADIO_2057, - CORE, coreNum, - PAD2G_IDACS, - 0x8a); - WRITE_RADIO_REG4( - pi, RADIO_2057, - CORE, coreNum, - PAD_BIAS_FILTER_BWS, - 0x3e); - } - - } else if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) { - - if (CHSPEC_IS40(pi->radio_chanspec) == - 0) { - WRITE_RADIO_REG4(pi, RADIO_2057, - CORE, 0, - IPA2G_IMAIN, - 0x14); - WRITE_RADIO_REG4(pi, RADIO_2057, - CORE, 1, - IPA2G_IMAIN, - 0x12); - } else { - WRITE_RADIO_REG4(pi, RADIO_2057, - CORE, 0, - IPA2G_IMAIN, - 0x16); - WRITE_RADIO_REG4(pi, RADIO_2057, - CORE, 1, - IPA2G_IMAIN, - 0x16); - } - } - - } else { - freq = CHAN5G_FREQ(CHSPEC_CHANNEL( - pi->radio_chanspec)); - if (((freq >= 5180) && (freq <= 5230)) - || ((freq >= 5745) && (freq <= 5805))) { - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, - 0, IPA5G_BIAS_FILTER, - 0xff); - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, - 1, IPA5G_BIAS_FILTER, - 0xff); - } - } - } else { - - if (pi->pubpi.radiorev != 5) { - for (coreNum = 0; coreNum <= 1; coreNum++) { - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, - coreNum, - TXMIX2G_TUNE_BOOST_PU, - 0x61); - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, - coreNum, - TXGM_IDAC_BLEED, 0x70); - } - } - } - - if (pi->pubpi.radiorev == 4) { - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, - 0x05, 16, - &afectrl_adc_ctrl1_rev7); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, - 0x15, 16, - &afectrl_adc_ctrl1_rev7); - - for (coreNum = 0; coreNum <= 1; coreNum++) { - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, - AFE_VCM_CAL_MASTER, 0x0); - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, - AFE_SET_VCM_I, 0x3f); - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, - AFE_SET_VCM_Q, 0x3f); - } - } else { - mod_phy_reg(pi, 0xa6, (0x1 << 2), (0x1 << 2)); - mod_phy_reg(pi, 0x8f, (0x1 << 2), (0x1 << 2)); - mod_phy_reg(pi, 0xa7, (0x1 << 2), (0x1 << 2)); - mod_phy_reg(pi, 0xa5, (0x1 << 2), (0x1 << 2)); - - mod_phy_reg(pi, 0xa6, (0x1 << 0), 0); - mod_phy_reg(pi, 0x8f, (0x1 << 0), (0x1 << 0)); - mod_phy_reg(pi, 0xa7, (0x1 << 0), 0); - mod_phy_reg(pi, 0xa5, (0x1 << 0), (0x1 << 0)); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, - 0x05, 16, - &afectrl_adc_ctrl2_rev7); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, - 0x15, 16, - &afectrl_adc_ctrl2_rev7); - - mod_phy_reg(pi, 0xa6, (0x1 << 2), 0); - mod_phy_reg(pi, 0x8f, (0x1 << 2), 0); - mod_phy_reg(pi, 0xa7, (0x1 << 2), 0); - mod_phy_reg(pi, 0xa5, (0x1 << 2), 0); - } - - write_phy_reg(pi, 0x6a, 0x2); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 256, 32, - &min_nvar_offset_6mbps); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x138, 16, - &rfseq_pktgn_lpf_hpc_rev7); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, 0x141, 16, - &rfseq_pktgn_lpf_h_hpc_rev7); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 3, 0x133, 16, - &rfseq_htpktgn_lpf_hpc_rev7); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x146, 16, - &rfseq_cckpktgn_lpf_hpc_rev7); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, 0x123, 16, - &rfseq_tx2rx_lpf_h_hpc_rev7); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, 0x12A, 16, - &rfseq_rx2tx_lpf_h_hpc_rev7); - - if (CHSPEC_IS40(pi->radio_chanspec) == 0) { - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, - 32, &min_nvar_val); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, - 127, 32, &min_nvar_val); - } else { - min_nvar_val = noise_var_tbl_rev7[3]; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, - 32, &min_nvar_val); - - min_nvar_val = noise_var_tbl_rev7[127]; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, - 127, 32, &min_nvar_val); - } - - wlc_phy_workarounds_nphy_gainctrl(pi); - - pdetrange = - (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g. - pdetrange : pi->srom_fem2g.pdetrange; - - if (pdetrange == 0) { - chan_freq_range = - wlc_phy_get_chan_freq_range_nphy(pi, 0); - if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { - aux_adc_vmid_rev7_core0[3] = 0x70; - aux_adc_vmid_rev7_core1[3] = 0x70; - aux_adc_gain_rev7[3] = 2; - } else { - aux_adc_vmid_rev7_core0[3] = 0x80; - aux_adc_vmid_rev7_core1[3] = 0x80; - aux_adc_gain_rev7[3] = 3; - } - } else if (pdetrange == 1) { - if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { - aux_adc_vmid_rev7_core0[3] = 0x7c; - aux_adc_vmid_rev7_core1[3] = 0x7c; - aux_adc_gain_rev7[3] = 2; - } else { - aux_adc_vmid_rev7_core0[3] = 0x8c; - aux_adc_vmid_rev7_core1[3] = 0x8c; - aux_adc_gain_rev7[3] = 1; - } - } else if (pdetrange == 2) { - if (pi->pubpi.radioid == BCM2057_ID) { - if ((pi->pubpi.radiorev == 5) - || (pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) { - if (chan_freq_range == - WL_CHAN_FREQ_RANGE_2G) { - aux_adc_vmid_rev7_core0[3] = - 0x8c; - aux_adc_vmid_rev7_core1[3] = - 0x8c; - aux_adc_gain_rev7[3] = 0; - } else { - aux_adc_vmid_rev7_core0[3] = - 0x96; - aux_adc_vmid_rev7_core1[3] = - 0x96; - aux_adc_gain_rev7[3] = 0; - } - } - } - - } else if (pdetrange == 3) { - if (chan_freq_range == WL_CHAN_FREQ_RANGE_2G) { - aux_adc_vmid_rev7_core0[3] = 0x89; - aux_adc_vmid_rev7_core1[3] = 0x89; - aux_adc_gain_rev7[3] = 0; - } - - } else if (pdetrange == 5) { - - if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { - aux_adc_vmid_rev7_core0[3] = 0x80; - aux_adc_vmid_rev7_core1[3] = 0x80; - aux_adc_gain_rev7[3] = 3; - } else { - aux_adc_vmid_rev7_core0[3] = 0x70; - aux_adc_vmid_rev7_core1[3] = 0x70; - aux_adc_gain_rev7[3] = 2; - } - } - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x08, 16, - &aux_adc_vmid_rev7_core0); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x18, 16, - &aux_adc_vmid_rev7_core1); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x0c, 16, - &aux_adc_gain_rev7); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x1c, 16, - &aux_adc_gain_rev7); - - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - - write_phy_reg(pi, 0x23f, 0x1f8); - write_phy_reg(pi, 0x240, 0x1f8); - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, - 1, 0, 32, &leg_data_weights); - leg_data_weights = leg_data_weights & 0xffffff; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, - 1, 0, 32, &leg_data_weights); - - alpha0 = 293; - alpha1 = 435; - alpha2 = 261; - beta0 = 366; - beta1 = 205; - beta2 = 32; - write_phy_reg(pi, 0x145, alpha0); - write_phy_reg(pi, 0x146, alpha1); - write_phy_reg(pi, 0x147, alpha2); - write_phy_reg(pi, 0x148, beta0); - write_phy_reg(pi, 0x149, beta1); - write_phy_reg(pi, 0x14a, beta2); - - write_phy_reg(pi, 0x38, 0xC); - write_phy_reg(pi, 0x2ae, 0xC); - - wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_TX2RX, - rfseq_tx2rx_events_rev3, - rfseq_tx2rx_dlys_rev3, - ARRAY_SIZE(rfseq_tx2rx_events_rev3)); - - if (PHY_IPA(pi)) - wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX, - rfseq_rx2tx_events_rev3_ipa, - rfseq_rx2tx_dlys_rev3_ipa, - ARRAY_SIZE(rfseq_rx2tx_events_rev3_ipa)); - - if ((pi->sh->hw_phyrxchain != 0x3) && - (pi->sh->hw_phyrxchain != pi->sh->hw_phytxchain)) { - - if (PHY_IPA(pi)) { - rfseq_rx2tx_dlys_rev3[5] = 59; - rfseq_rx2tx_dlys_rev3[6] = 1; - rfseq_rx2tx_events_rev3[7] = - NPHY_REV3_RFSEQ_CMD_END; - } - - wlc_phy_set_rfseq_nphy( - pi, NPHY_RFSEQ_RX2TX, - rfseq_rx2tx_events_rev3, - rfseq_rx2tx_dlys_rev3, - ARRAY_SIZE(rfseq_rx2tx_events_rev3)); - } - - if (CHSPEC_IS2G(pi->radio_chanspec)) - write_phy_reg(pi, 0x6a, 0x2); - else - write_phy_reg(pi, 0x6a, 0x9c40); - - mod_phy_reg(pi, 0x294, (0xf << 8), (7 << 8)); - - if (CHSPEC_IS40(pi->radio_chanspec) == 0) { - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, - 32, &min_nvar_val); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, - 127, 32, &min_nvar_val); - } else { - min_nvar_val = noise_var_tbl_rev3[3]; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, - 32, &min_nvar_val); - - min_nvar_val = noise_var_tbl_rev3[127]; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, - 127, 32, &min_nvar_val); - } - - wlc_phy_workarounds_nphy_gainctrl(pi); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x00, 16, - &dac_control); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x10, 16, - &dac_control); - - pdetrange = - (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g. - pdetrange : pi->srom_fem2g.pdetrange; - - if (pdetrange == 0) { - if (NREV_GE(pi->pubpi.phy_rev, 4)) { - aux_adc_vmid = aux_adc_vmid_rev4; - aux_adc_gain = aux_adc_gain_rev4; - } else { - aux_adc_vmid = aux_adc_vmid_rev3; - aux_adc_gain = aux_adc_gain_rev3; - } - chan_freq_range = - wlc_phy_get_chan_freq_range_nphy(pi, 0); - if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { - switch (chan_freq_range) { - case WL_CHAN_FREQ_RANGE_5GL: - aux_adc_vmid[3] = 0x89; - aux_adc_gain[3] = 0; - break; - case WL_CHAN_FREQ_RANGE_5GM: - aux_adc_vmid[3] = 0x89; - aux_adc_gain[3] = 0; - break; - case WL_CHAN_FREQ_RANGE_5GH: - aux_adc_vmid[3] = 0x89; - aux_adc_gain[3] = 0; - break; - default: - break; - } - } - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x08, 16, aux_adc_vmid); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x18, 16, aux_adc_vmid); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x0c, 16, aux_adc_gain); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x1c, 16, aux_adc_gain); - } else if (pdetrange == 1) { - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x08, 16, sk_adc_vmid); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x18, 16, sk_adc_vmid); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x0c, 16, sk_adc_gain); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x1c, 16, sk_adc_gain); - } else if (pdetrange == 2) { - - u16 bcm_adc_vmid[] = { 0xa2, 0xb4, 0xb4, 0x74 }; - u16 bcm_adc_gain[] = { 0x02, 0x02, 0x02, 0x04 }; - - if (NREV_GE(pi->pubpi.phy_rev, 6)) { - chan_freq_range = - wlc_phy_get_chan_freq_range_nphy(pi, 0); - if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { - bcm_adc_vmid[3] = 0x8e; - bcm_adc_gain[3] = 0x03; - } else { - bcm_adc_vmid[3] = 0x94; - bcm_adc_gain[3] = 0x03; - } - } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { - bcm_adc_vmid[3] = 0x84; - bcm_adc_gain[3] = 0x02; - } - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x08, 16, bcm_adc_vmid); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x18, 16, bcm_adc_vmid); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x0c, 16, bcm_adc_gain); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x1c, 16, bcm_adc_gain); - } else if (pdetrange == 3) { - chan_freq_range = - wlc_phy_get_chan_freq_range_nphy(pi, 0); - if ((NREV_GE(pi->pubpi.phy_rev, 4)) - && (chan_freq_range == WL_CHAN_FREQ_RANGE_2G)) { - - u16 auxadc_vmid[] = { - 0xa2, 0xb4, 0xb4, 0x270 - }; - u16 auxadc_gain[] = { - 0x02, 0x02, 0x02, 0x00 - }; - - wlc_phy_table_write_nphy(pi, - NPHY_TBL_ID_AFECTRL, 4, - 0x08, 16, auxadc_vmid); - wlc_phy_table_write_nphy(pi, - NPHY_TBL_ID_AFECTRL, 4, - 0x18, 16, auxadc_vmid); - wlc_phy_table_write_nphy(pi, - NPHY_TBL_ID_AFECTRL, 4, - 0x0c, 16, auxadc_gain); - wlc_phy_table_write_nphy(pi, - NPHY_TBL_ID_AFECTRL, 4, - 0x1c, 16, auxadc_gain); - } - } else if ((pdetrange == 4) || (pdetrange == 5)) { - u16 bcm_adc_vmid[] = { 0xa2, 0xb4, 0xb4, 0x0 }; - u16 bcm_adc_gain[] = { 0x02, 0x02, 0x02, 0x0 }; - u16 Vmid[2], Av[2]; - - chan_freq_range = - wlc_phy_get_chan_freq_range_nphy(pi, 0); - if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { - Vmid[0] = (pdetrange == 4) ? 0x8e : 0x89; - Vmid[1] = (pdetrange == 4) ? 0x96 : 0x89; - Av[0] = (pdetrange == 4) ? 2 : 0; - Av[1] = (pdetrange == 4) ? 2 : 0; - } else { - Vmid[0] = (pdetrange == 4) ? 0x89 : 0x74; - Vmid[1] = (pdetrange == 4) ? 0x8b : 0x70; - Av[0] = (pdetrange == 4) ? 2 : 0; - Av[1] = (pdetrange == 4) ? 2 : 0; - } - - bcm_adc_vmid[3] = Vmid[0]; - bcm_adc_gain[3] = Av[0]; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x08, 16, bcm_adc_vmid); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x0c, 16, bcm_adc_gain); - - bcm_adc_vmid[3] = Vmid[1]; - bcm_adc_gain[3] = Av[1]; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x18, 16, bcm_adc_vmid); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, - 0x1c, 16, bcm_adc_gain); - } - - write_radio_reg(pi, - (RADIO_2056_RX_MIXA_MAST_BIAS | RADIO_2056_RX0), - 0x0); - write_radio_reg(pi, - (RADIO_2056_RX_MIXA_MAST_BIAS | RADIO_2056_RX1), - 0x0); - - write_radio_reg(pi, - (RADIO_2056_RX_MIXA_BIAS_MAIN | RADIO_2056_RX0), - 0x6); - write_radio_reg(pi, - (RADIO_2056_RX_MIXA_BIAS_MAIN | RADIO_2056_RX1), - 0x6); - - write_radio_reg(pi, - (RADIO_2056_RX_MIXA_BIAS_AUX | RADIO_2056_RX0), - 0x7); - write_radio_reg(pi, - (RADIO_2056_RX_MIXA_BIAS_AUX | RADIO_2056_RX1), - 0x7); - - write_radio_reg(pi, - (RADIO_2056_RX_MIXA_LOB_BIAS | RADIO_2056_RX0), - 0x88); - write_radio_reg(pi, - (RADIO_2056_RX_MIXA_LOB_BIAS | RADIO_2056_RX1), - 0x88); - - write_radio_reg(pi, - (RADIO_2056_RX_MIXA_CMFB_IDAC | RADIO_2056_RX0), - 0x0); - write_radio_reg(pi, - (RADIO_2056_RX_MIXA_CMFB_IDAC | RADIO_2056_RX1), - 0x0); - - write_radio_reg(pi, - (RADIO_2056_RX_MIXG_CMFB_IDAC | RADIO_2056_RX0), - 0x0); - write_radio_reg(pi, - (RADIO_2056_RX_MIXG_CMFB_IDAC | RADIO_2056_RX1), - 0x0); - - triso = - (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g. - triso : pi->srom_fem2g.triso; - if (triso == 7) { - wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_0); - wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_1); - } - - wlc_phy_war_txchain_upd_nphy(pi, pi->sh->hw_phytxchain); - - if (((pi->sh->boardflags2 & BFL2_APLL_WAR) && - (CHSPEC_IS5G(pi->radio_chanspec))) || - (((pi->sh->boardflags2 & BFL2_GPLL_WAR) || - (pi->sh->boardflags2 & BFL2_GPLL_WAR2)) && - (CHSPEC_IS2G(pi->radio_chanspec)))) { - nss1_data_weights = 0x00088888; - ht_data_weights = 0x00088888; - stbc_data_weights = 0x00088888; - } else { - nss1_data_weights = 0x88888888; - ht_data_weights = 0x88888888; - stbc_data_weights = 0x88888888; - } - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, - 1, 1, 32, &nss1_data_weights); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, - 1, 2, 32, &ht_data_weights); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, - 1, 3, 32, &stbc_data_weights); - - if (NREV_IS(pi->pubpi.phy_rev, 4)) { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - write_radio_reg(pi, - RADIO_2056_TX_GMBB_IDAC | - RADIO_2056_TX0, 0x70); - write_radio_reg(pi, - RADIO_2056_TX_GMBB_IDAC | - RADIO_2056_TX1, 0x70); - } - } - - if (!pi->edcrs_threshold_lock) { - write_phy_reg(pi, 0x224, 0x3eb); - write_phy_reg(pi, 0x225, 0x3eb); - write_phy_reg(pi, 0x226, 0x341); - write_phy_reg(pi, 0x227, 0x341); - write_phy_reg(pi, 0x228, 0x42b); - write_phy_reg(pi, 0x229, 0x42b); - write_phy_reg(pi, 0x22a, 0x381); - write_phy_reg(pi, 0x22b, 0x381); - write_phy_reg(pi, 0x22c, 0x42b); - write_phy_reg(pi, 0x22d, 0x42b); - write_phy_reg(pi, 0x22e, 0x381); - write_phy_reg(pi, 0x22f, 0x381); - } - - if (NREV_GE(pi->pubpi.phy_rev, 6)) { - - if (pi->sh->boardflags2 & BFL2_SINGLEANT_CCK) - wlapi_bmac_mhf(pi->sh->physhim, MHF4, - MHF4_BPHY_TXCORE0, - MHF4_BPHY_TXCORE0, BRCM_BAND_ALL); - } - } else { - - if (pi->sh->boardflags2 & BFL2_SKWRKFEM_BRD || - (pi->sh->boardtype == 0x8b)) { - uint i; - u8 war_dlys[] = { 1, 6, 6, 2, 4, 20, 1 }; - for (i = 0; i < ARRAY_SIZE(rfseq_rx2tx_dlys); i++) - rfseq_rx2tx_dlys[i] = war_dlys[i]; - } - - if (CHSPEC_IS5G(pi->radio_chanspec) && pi->phy_5g_pwrgain) { - and_radio_reg(pi, RADIO_2055_CORE1_TX_RF_SPARE, 0xf7); - and_radio_reg(pi, RADIO_2055_CORE2_TX_RF_SPARE, 0xf7); - } else { - or_radio_reg(pi, RADIO_2055_CORE1_TX_RF_SPARE, 0x8); - or_radio_reg(pi, RADIO_2055_CORE2_TX_RF_SPARE, 0x8); - } - - regval = 0x000a; - wlc_phy_table_write_nphy(pi, 8, 1, 0, 16, ®val); - wlc_phy_table_write_nphy(pi, 8, 1, 0x10, 16, ®val); - - if (NREV_LT(pi->pubpi.phy_rev, 3)) { - regval = 0xcdaa; - wlc_phy_table_write_nphy(pi, 8, 1, 0x02, 16, ®val); - wlc_phy_table_write_nphy(pi, 8, 1, 0x12, 16, ®val); - } - - if (NREV_LT(pi->pubpi.phy_rev, 2)) { - regval = 0x0000; - wlc_phy_table_write_nphy(pi, 8, 1, 0x08, 16, ®val); - wlc_phy_table_write_nphy(pi, 8, 1, 0x18, 16, ®val); - - regval = 0x7aab; - wlc_phy_table_write_nphy(pi, 8, 1, 0x07, 16, ®val); - wlc_phy_table_write_nphy(pi, 8, 1, 0x17, 16, ®val); - - regval = 0x0800; - wlc_phy_table_write_nphy(pi, 8, 1, 0x06, 16, ®val); - wlc_phy_table_write_nphy(pi, 8, 1, 0x16, 16, ®val); - } - - write_phy_reg(pi, 0xf8, 0x02d8); - write_phy_reg(pi, 0xf9, 0x0301); - write_phy_reg(pi, 0xfa, 0x02d8); - write_phy_reg(pi, 0xfb, 0x0301); - - wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX, rfseq_rx2tx_events, - rfseq_rx2tx_dlys, - ARRAY_SIZE(rfseq_rx2tx_events)); - - wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_TX2RX, rfseq_tx2rx_events, - rfseq_tx2rx_dlys, - ARRAY_SIZE(rfseq_tx2rx_events)); - - wlc_phy_workarounds_nphy_gainctrl(pi); - - if (NREV_LT(pi->pubpi.phy_rev, 2)) { - - if (read_phy_reg(pi, 0xa0) & NPHY_MLenable) - wlapi_bmac_mhf(pi->sh->physhim, MHF3, - MHF3_NPHY_MLADV_WAR, - MHF3_NPHY_MLADV_WAR, - BRCM_BAND_ALL); - - } else if (NREV_IS(pi->pubpi.phy_rev, 2)) { - write_phy_reg(pi, 0x1e3, 0x0); - write_phy_reg(pi, 0x1e4, 0x0); - } - - if (NREV_LT(pi->pubpi.phy_rev, 2)) - mod_phy_reg(pi, 0x90, (0x1 << 7), 0); - - alpha0 = 293; - alpha1 = 435; - alpha2 = 261; - beta0 = 366; - beta1 = 205; - beta2 = 32; - write_phy_reg(pi, 0x145, alpha0); - write_phy_reg(pi, 0x146, alpha1); - write_phy_reg(pi, 0x147, alpha2); - write_phy_reg(pi, 0x148, beta0); - write_phy_reg(pi, 0x149, beta1); - write_phy_reg(pi, 0x14a, beta2); - - if (NREV_LT(pi->pubpi.phy_rev, 3)) { - mod_phy_reg(pi, 0x142, (0xf << 12), 0); - - write_phy_reg(pi, 0x192, 0xb5); - write_phy_reg(pi, 0x193, 0xa4); - write_phy_reg(pi, 0x194, 0x0); - } - - if (NREV_IS(pi->pubpi.phy_rev, 2)) - mod_phy_reg(pi, 0x221, - NPHY_FORCESIG_DECODEGATEDCLKS, - NPHY_FORCESIG_DECODEGATEDCLKS); - } - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -static void wlc_phy_extpa_set_tx_digi_filts_nphy(struct brcms_phy *pi) -{ - int j, type = 2; - u16 addr_offset = 0x2c5; - - for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) - write_phy_reg(pi, addr_offset + j, - NPHY_IPA_REV4_txdigi_filtcoeffs[type][j]); -} - -static void wlc_phy_clip_det_nphy(struct brcms_phy *pi, u8 write, u16 *vals) -{ - - if (write == 0) { - vals[0] = read_phy_reg(pi, 0x2c); - vals[1] = read_phy_reg(pi, 0x42); - } else { - write_phy_reg(pi, 0x2c, vals[0]); - write_phy_reg(pi, 0x42, vals[1]); - } -} - -static void wlc_phy_ipa_internal_tssi_setup_nphy(struct brcms_phy *pi) -{ - u8 core; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - if (CHSPEC_IS2G(pi->radio_chanspec)) { - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TX_SSI_MASTER, 0x5); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TX_SSI_MUX, 0xe); - - if (pi->pubpi.radiorev != 5) - WRITE_RADIO_REG3(pi, RADIO_2057, TX, - core, TSSIA, 0); - - if (!NREV_IS(pi->pubpi.phy_rev, 7)) - WRITE_RADIO_REG3(pi, RADIO_2057, TX, - core, TSSIG, 0x1); - else - WRITE_RADIO_REG3(pi, RADIO_2057, TX, - core, TSSIG, 0x31); - } else { - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TX_SSI_MASTER, 0x9); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TX_SSI_MUX, 0xc); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TSSIG, 0); - - if (pi->pubpi.radiorev != 5) { - if (!NREV_IS(pi->pubpi.phy_rev, 7)) - WRITE_RADIO_REG3(pi, RADIO_2057, - TX, core, - TSSIA, 0x1); - else - WRITE_RADIO_REG3(pi, RADIO_2057, - TX, core, - TSSIA, 0x31); - } - } - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_VCM_HG, - 0); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_IDAC, - 0); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM, - 0x3); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_MISC1, - 0x0); - } - } else { - WRITE_RADIO_SYN(pi, RADIO_2056, RESERVED_ADDR31, - (CHSPEC_IS2G(pi->radio_chanspec)) ? 0x128 : - 0x80); - WRITE_RADIO_SYN(pi, RADIO_2056, RESERVED_ADDR30, 0x0); - WRITE_RADIO_SYN(pi, RADIO_2056, GPIO_MASTER1, 0x29); - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, IQCAL_VCM_HG, - 0x0); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, IQCAL_IDAC, - 0x0); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_VCM, - 0x3); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TX_AMP_DET, - 0x0); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC1, - 0x8); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC2, - 0x0); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC3, - 0x0); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - TX_SSI_MASTER, 0x5); - - if (pi->pubpi.radiorev != 5) - WRITE_RADIO_REG2(pi, RADIO_2056, TX, - core, TSSIA, 0x0); - if (NREV_GE(pi->pubpi.phy_rev, 5)) - WRITE_RADIO_REG2(pi, RADIO_2056, TX, - core, TSSIG, 0x31); - else - WRITE_RADIO_REG2(pi, RADIO_2056, TX, - core, TSSIG, 0x11); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - TX_SSI_MUX, 0xe); - } else { - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - TX_SSI_MASTER, 0x9); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - TSSIA, 0x31); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - TSSIG, 0x0); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - TX_SSI_MUX, 0xc); - } - } - } -} - -static void -wlc_phy_rfctrl_override_nphy(struct brcms_phy *pi, u16 field, u16 value, - u8 core_mask, u8 off) -{ - u8 core_num; - u16 addr = 0, mask = 0, en_addr = 0, val_addr = 0, en_mask = - 0, val_mask = 0; - u8 shift = 0, val_shift = 0; - - if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { - - en_mask = field; - for (core_num = 0; core_num < 2; core_num++) { - - switch (field) { - case (0x1 << 1): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : 0x7d; - val_mask = (0x1 << 0); - val_shift = 0; - break; - case (0x1 << 2): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : 0x7d; - val_mask = (0x1 << 1); - val_shift = 1; - break; - case (0x1 << 3): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : 0x7d; - val_mask = (0x1 << 2); - val_shift = 2; - break; - case (0x1 << 4): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : 0x7d; - val_mask = (0x1 << 4); - val_shift = 4; - break; - case (0x1 << 5): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : 0x7d; - val_mask = (0x1 << 5); - val_shift = 5; - break; - case (0x1 << 6): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : 0x7d; - val_mask = (0x1 << 6); - val_shift = 6; - break; - case (0x1 << 7): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : 0x7d; - val_mask = (0x1 << 7); - val_shift = 7; - break; - case (0x1 << 8): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : 0x7d; - val_mask = (0x7 << 8); - val_shift = 8; - break; - case (0x1 << 11): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7a : 0x7d; - val_mask = (0x7 << 13); - val_shift = 13; - break; - - case (0x1 << 9): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0xf8 : 0xfa; - val_mask = (0x7 << 0); - val_shift = 0; - break; - - case (0x1 << 10): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0xf8 : 0xfa; - val_mask = (0x7 << 4); - val_shift = 4; - break; - - case (0x1 << 12): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7b : 0x7e; - val_mask = (0xffff << 0); - val_shift = 0; - break; - case (0x1 << 13): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0x7c : 0x7f; - val_mask = (0xffff << 0); - val_shift = 0; - break; - case (0x1 << 14): - en_addr = (core_num == 0) ? 0xe7 : 0xec; - val_addr = (core_num == 0) ? 0xf9 : 0xfb; - val_mask = (0x3 << 6); - val_shift = 6; - break; - case (0x1 << 0): - en_addr = (core_num == 0) ? 0xe5 : 0xe6; - val_addr = (core_num == 0) ? 0xf9 : 0xfb; - val_mask = (0x1 << 15); - val_shift = 15; - break; - default: - addr = 0xffff; - break; - } - - if (off) { - and_phy_reg(pi, en_addr, ~en_mask); - and_phy_reg(pi, val_addr, ~val_mask); - } else { - - if ((core_mask == 0) - || (core_mask & (1 << core_num))) { - or_phy_reg(pi, en_addr, en_mask); - - if (addr != 0xffff) - mod_phy_reg(pi, val_addr, - val_mask, - (value << - val_shift)); - } - } - } - } else { - - if (off) { - and_phy_reg(pi, 0xec, ~field); - value = 0x0; - } else { - or_phy_reg(pi, 0xec, field); - } - - for (core_num = 0; core_num < 2; core_num++) { - - switch (field) { - case (0x1 << 1): - case (0x1 << 9): - case (0x1 << 12): - case (0x1 << 13): - case (0x1 << 14): - addr = 0x78; - - core_mask = 0x1; - break; - case (0x1 << 2): - case (0x1 << 3): - case (0x1 << 4): - case (0x1 << 5): - case (0x1 << 6): - case (0x1 << 7): - case (0x1 << 8): - addr = (core_num == 0) ? 0x7a : 0x7d; - break; - case (0x1 << 10): - addr = (core_num == 0) ? 0x7b : 0x7e; - break; - case (0x1 << 11): - addr = (core_num == 0) ? 0x7c : 0x7f; - break; - default: - addr = 0xffff; - } - - switch (field) { - case (0x1 << 1): - mask = (0x7 << 3); - shift = 3; - break; - case (0x1 << 9): - mask = (0x1 << 2); - shift = 2; - break; - case (0x1 << 12): - mask = (0x1 << 8); - shift = 8; - break; - case (0x1 << 13): - mask = (0x1 << 9); - shift = 9; - break; - case (0x1 << 14): - mask = (0xf << 12); - shift = 12; - break; - case (0x1 << 2): - mask = (0x1 << 0); - shift = 0; - break; - case (0x1 << 3): - mask = (0x1 << 1); - shift = 1; - break; - case (0x1 << 4): - mask = (0x1 << 2); - shift = 2; - break; - case (0x1 << 5): - mask = (0x3 << 4); - shift = 4; - break; - case (0x1 << 6): - mask = (0x3 << 6); - shift = 6; - break; - case (0x1 << 7): - mask = (0x1 << 8); - shift = 8; - break; - case (0x1 << 8): - mask = (0x1 << 9); - shift = 9; - break; - case (0x1 << 10): - mask = 0x1fff; - shift = 0x0; - break; - case (0x1 << 11): - mask = 0x1fff; - shift = 0x0; - break; - default: - mask = 0x0; - shift = 0x0; - break; - } - - if ((addr != 0xffff) && (core_mask & (1 << core_num))) - mod_phy_reg(pi, addr, mask, (value << shift)); - } - - or_phy_reg(pi, 0xec, (0x1 << 0)); - or_phy_reg(pi, 0x78, (0x1 << 0)); - udelay(1); - and_phy_reg(pi, 0xec, ~(0x1 << 0)); - } -} - -static void wlc_phy_txpwrctrl_idle_tssi_nphy(struct brcms_phy *pi) -{ - s32 rssi_buf[4]; - s32 int_val; - - if (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi) || PHY_MUTED(pi)) - - return; - - if (PHY_IPA(pi)) - wlc_phy_ipa_internal_tssi_setup_nphy(pi); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12), - 0, 0x3, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - else if (NREV_GE(pi->pubpi.phy_rev, 3)) - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 3, 0); - - wlc_phy_stopplayback_nphy(pi); - - wlc_phy_tx_tone_nphy(pi, 4000, 0, 0, 0, false); - - udelay(20); - int_val = - wlc_phy_poll_rssi_nphy(pi, (u8) NPHY_RSSI_SEL_TSSI_2G, rssi_buf, - 1); - wlc_phy_stopplayback_nphy(pi); - wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_OFF, 0); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12), - 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - else if (NREV_GE(pi->pubpi.phy_rev, 3)) - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 3, 1); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - - pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_2g = - (u8) ((int_val >> 24) & 0xff); - pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_5g = - (u8) ((int_val >> 24) & 0xff); - - pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_2g = - (u8) ((int_val >> 8) & 0xff); - pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_5g = - (u8) ((int_val >> 8) & 0xff); - } else { - pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_2g = - (u8) ((int_val >> 24) & 0xff); - - pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_2g = - (u8) ((int_val >> 8) & 0xff); - - pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_5g = - (u8) ((int_val >> 16) & 0xff); - pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_5g = - (u8) ((int_val) & 0xff); - } - -} - -static void wlc_phy_txpwr_limit_to_tbl_nphy(struct brcms_phy *pi) -{ - u8 idx, idx2, i, delta_ind; - - for (idx = TXP_FIRST_CCK; idx <= TXP_LAST_CCK; idx++) - pi->adj_pwr_tbl_nphy[idx] = pi->tx_power_offset[idx]; - - for (i = 0; i < 4; i++) { - idx2 = 0; - - delta_ind = 0; - - switch (i) { - case 0: - - if (CHSPEC_IS40(pi->radio_chanspec) - && NPHY_IS_SROM_REINTERPRET) { - idx = TXP_FIRST_MCS_40_SISO; - } else { - idx = (CHSPEC_IS40(pi->radio_chanspec)) ? - TXP_FIRST_OFDM_40_SISO : TXP_FIRST_OFDM; - delta_ind = 1; - } - break; - - case 1: - - idx = (CHSPEC_IS40(pi->radio_chanspec)) ? - TXP_FIRST_MCS_40_CDD : TXP_FIRST_MCS_20_CDD; - break; - - case 2: - - idx = (CHSPEC_IS40(pi->radio_chanspec)) ? - TXP_FIRST_MCS_40_STBC : TXP_FIRST_MCS_20_STBC; - break; - - case 3: - - idx = (CHSPEC_IS40(pi->radio_chanspec)) ? - TXP_FIRST_MCS_40_SDM : TXP_FIRST_MCS_20_SDM; - break; - } - - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - idx = idx + delta_ind; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx++]; - - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx++]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx++]; - - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx++]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx++]; - - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx++]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - idx = idx + 1 - delta_ind; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = - pi->tx_power_offset[idx]; - } -} - -static void wlc_phy_txpwrctrl_pwr_setup_nphy(struct brcms_phy *pi) -{ - u32 idx; - s16 a1[2], b0[2], b1[2]; - s8 target_pwr_qtrdbm[2]; - s32 num, den, pwr_est; - u8 chan_freq_range; - u8 idle_tssi[2]; - u32 tbl_id, tbl_len, tbl_offset; - u32 regval[64]; - u8 core; - - if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) { - wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK); - (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); - udelay(1); - } - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - or_phy_reg(pi, 0x122, (0x1 << 0)); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) - and_phy_reg(pi, 0x1e7, (u16) (~(0x1 << 15))); - else - or_phy_reg(pi, 0x1e7, (0x1 << 15)); - - if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) - wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0); - - if (pi->sh->sromrev < 4) { - idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g; - idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g; - a1[0] = -424; - a1[1] = -424; - b0[0] = 5612; - b0[1] = 5612; - b1[1] = -1393; - b1[0] = -1393; - } else { - - chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); - switch (chan_freq_range) { - case WL_CHAN_FREQ_RANGE_2G: - idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g; - idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g; - a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_a1; - a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_a1; - b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_b0; - b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_b0; - b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_b1; - b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_b1; - break; - case WL_CHAN_FREQ_RANGE_5GL: - idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g; - idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g; - a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_a1; - a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_a1; - b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_b0; - b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_b0; - b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_b1; - b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_b1; - break; - case WL_CHAN_FREQ_RANGE_5GM: - idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g; - idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g; - a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_a1; - a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_a1; - b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_b0; - b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_b0; - b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_b1; - b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_b1; - break; - case WL_CHAN_FREQ_RANGE_5GH: - idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g; - idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g; - a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_a1; - a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_a1; - b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_b0; - b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_b0; - b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_b1; - b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_b1; - break; - default: - idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g; - idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g; - a1[0] = -424; - a1[1] = -424; - b0[0] = 5612; - b0[1] = 5612; - b1[1] = -1393; - b1[0] = -1393; - break; - } - } - - /* use the provided transmit power */ - target_pwr_qtrdbm[0] = (s8) pi->tx_power_max; - target_pwr_qtrdbm[1] = (s8) pi->tx_power_max; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - if (pi->srom_fem2g.tssipos) - or_phy_reg(pi, 0x1e9, (0x1 << 14)); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - for (core = 0; core <= 1; core++) { - if (PHY_IPA(pi)) { - if (CHSPEC_IS2G(pi->radio_chanspec)) - WRITE_RADIO_REG3(pi, RADIO_2057, - TX, core, - TX_SSI_MUX, - 0xe); - else - WRITE_RADIO_REG3(pi, RADIO_2057, - TX, core, - TX_SSI_MUX, - 0xc); - } - } - } else { - if (PHY_IPA(pi)) { - - write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | - RADIO_2056_TX0, - (CHSPEC_IS5G - (pi->radio_chanspec)) ? - 0xc : 0xe); - write_radio_reg(pi, - RADIO_2056_TX_TX_SSI_MUX | - RADIO_2056_TX1, - (CHSPEC_IS5G - (pi->radio_chanspec)) ? - 0xc : 0xe); - } else { - - write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | - RADIO_2056_TX0, 0x11); - write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | - RADIO_2056_TX1, 0x11); - } - } - } - - if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) { - wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK); - (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); - udelay(1); - } - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - mod_phy_reg(pi, 0x1e7, (0x7f << 0), - (NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 << 0)); - else - mod_phy_reg(pi, 0x1e7, (0x7f << 0), - (NPHY_TxPwrCtrlCmd_pwrIndex_init << 0)); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - mod_phy_reg(pi, 0x222, (0xff << 0), - (NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 << 0)); - else if (NREV_GT(pi->pubpi.phy_rev, 1)) - mod_phy_reg(pi, 0x222, (0xff << 0), - (NPHY_TxPwrCtrlCmd_pwrIndex_init << 0)); - - if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) - wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0); - - write_phy_reg(pi, 0x1e8, (0x3 << 8) | (240 << 0)); - - write_phy_reg(pi, 0x1e9, - (1 << 15) | (idle_tssi[0] << 0) | (idle_tssi[1] << 8)); - - write_phy_reg(pi, 0x1ea, - (target_pwr_qtrdbm[0] << 0) | - (target_pwr_qtrdbm[1] << 8)); - - tbl_len = 64; - tbl_offset = 0; - for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL; - tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) { - - for (idx = 0; idx < tbl_len; idx++) { - num = 8 * - (16 * b0[tbl_id - 26] + b1[tbl_id - 26] * idx); - den = 32768 + a1[tbl_id - 26] * idx; - pwr_est = max(((4 * num + den / 2) / den), -8); - if (NREV_LT(pi->pubpi.phy_rev, 3)) { - if (idx <= - (uint) (31 - idle_tssi[tbl_id - 26] + 1)) - pwr_est = - max(pwr_est, - target_pwr_qtrdbm - [tbl_id - 26] + 1); - } - regval[idx] = (u32) pwr_est; - } - wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32, - regval); - } - - wlc_phy_txpwr_limit_to_tbl_nphy(pi); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 84, 64, 8, - pi->adj_pwr_tbl_nphy); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 84, 64, 8, - pi->adj_pwr_tbl_nphy); - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -static u32 *wlc_phy_get_ipa_gaintbl_nphy(struct brcms_phy *pi) -{ - u32 *tx_pwrctrl_tbl = NULL; - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if ((pi->pubpi.radiorev == 4) - || (pi->pubpi.radiorev == 6)) - tx_pwrctrl_tbl = - nphy_tpc_txgain_ipa_2g_2057rev4n6; - else if (pi->pubpi.radiorev == 3) - tx_pwrctrl_tbl = - nphy_tpc_txgain_ipa_2g_2057rev3; - else if (pi->pubpi.radiorev == 5) - tx_pwrctrl_tbl = - nphy_tpc_txgain_ipa_2g_2057rev5; - else if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) - tx_pwrctrl_tbl = - nphy_tpc_txgain_ipa_2g_2057rev7; - } else if (NREV_IS(pi->pubpi.phy_rev, 6)) { - tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev6; - if (pi->sh->chip == BCMA_CHIP_ID_BCM47162) - tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev5; - } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { - tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev5; - } else { - tx_pwrctrl_tbl = nphy_tpc_txgain_ipa; - } - } else { - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if ((pi->pubpi.radiorev == 3) || - (pi->pubpi.radiorev == 4) || - (pi->pubpi.radiorev == 6)) - tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_5g_2057; - else if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) - tx_pwrctrl_tbl = - nphy_tpc_txgain_ipa_5g_2057rev7; - } else { - tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_5g; - } - } - - return tx_pwrctrl_tbl; -} - -static void wlc_phy_restore_rssical_nphy(struct brcms_phy *pi) -{ - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if (pi->nphy_rssical_chanspec_2G == 0) - return; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0, - RADIO_2057_VCM_MASK, - pi->rssical_cache. - rssical_radio_regs_2G[0]); - mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1, - RADIO_2057_VCM_MASK, - pi->rssical_cache. - rssical_radio_regs_2G[1]); - } else { - mod_radio_reg(pi, - RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0, - RADIO_2056_VCM_MASK, - pi->rssical_cache. - rssical_radio_regs_2G[0]); - mod_radio_reg(pi, - RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1, - RADIO_2056_VCM_MASK, - pi->rssical_cache. - rssical_radio_regs_2G[1]); - } - - write_phy_reg(pi, 0x1a6, - pi->rssical_cache.rssical_phyregs_2G[0]); - write_phy_reg(pi, 0x1ac, - pi->rssical_cache.rssical_phyregs_2G[1]); - write_phy_reg(pi, 0x1b2, - pi->rssical_cache.rssical_phyregs_2G[2]); - write_phy_reg(pi, 0x1b8, - pi->rssical_cache.rssical_phyregs_2G[3]); - write_phy_reg(pi, 0x1a4, - pi->rssical_cache.rssical_phyregs_2G[4]); - write_phy_reg(pi, 0x1aa, - pi->rssical_cache.rssical_phyregs_2G[5]); - write_phy_reg(pi, 0x1b0, - pi->rssical_cache.rssical_phyregs_2G[6]); - write_phy_reg(pi, 0x1b6, - pi->rssical_cache.rssical_phyregs_2G[7]); - write_phy_reg(pi, 0x1a5, - pi->rssical_cache.rssical_phyregs_2G[8]); - write_phy_reg(pi, 0x1ab, - pi->rssical_cache.rssical_phyregs_2G[9]); - write_phy_reg(pi, 0x1b1, - pi->rssical_cache.rssical_phyregs_2G[10]); - write_phy_reg(pi, 0x1b7, - pi->rssical_cache.rssical_phyregs_2G[11]); - - } else { - if (pi->nphy_rssical_chanspec_5G == 0) - return; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0, - RADIO_2057_VCM_MASK, - pi->rssical_cache. - rssical_radio_regs_5G[0]); - mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1, - RADIO_2057_VCM_MASK, - pi->rssical_cache. - rssical_radio_regs_5G[1]); - } else { - mod_radio_reg(pi, - RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0, - RADIO_2056_VCM_MASK, - pi->rssical_cache. - rssical_radio_regs_5G[0]); - mod_radio_reg(pi, - RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1, - RADIO_2056_VCM_MASK, - pi->rssical_cache. - rssical_radio_regs_5G[1]); - } - - write_phy_reg(pi, 0x1a6, - pi->rssical_cache.rssical_phyregs_5G[0]); - write_phy_reg(pi, 0x1ac, - pi->rssical_cache.rssical_phyregs_5G[1]); - write_phy_reg(pi, 0x1b2, - pi->rssical_cache.rssical_phyregs_5G[2]); - write_phy_reg(pi, 0x1b8, - pi->rssical_cache.rssical_phyregs_5G[3]); - write_phy_reg(pi, 0x1a4, - pi->rssical_cache.rssical_phyregs_5G[4]); - write_phy_reg(pi, 0x1aa, - pi->rssical_cache.rssical_phyregs_5G[5]); - write_phy_reg(pi, 0x1b0, - pi->rssical_cache.rssical_phyregs_5G[6]); - write_phy_reg(pi, 0x1b6, - pi->rssical_cache.rssical_phyregs_5G[7]); - write_phy_reg(pi, 0x1a5, - pi->rssical_cache.rssical_phyregs_5G[8]); - write_phy_reg(pi, 0x1ab, - pi->rssical_cache.rssical_phyregs_5G[9]); - write_phy_reg(pi, 0x1b1, - pi->rssical_cache.rssical_phyregs_5G[10]); - write_phy_reg(pi, 0x1b7, - pi->rssical_cache.rssical_phyregs_5G[11]); - } -} - -static void wlc_phy_internal_cal_txgain_nphy(struct brcms_phy *pi) -{ - u16 txcal_gain[2]; - - pi->nphy_txcal_pwr_idx[0] = pi->nphy_cal_orig_pwr_idx[0]; - pi->nphy_txcal_pwr_idx[1] = pi->nphy_cal_orig_pwr_idx[0]; - wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_cal_orig_pwr_idx[0], true); - wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_cal_orig_pwr_idx[1], true); - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, - txcal_gain); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - txcal_gain[0] = (txcal_gain[0] & 0xF000) | 0x0F40; - txcal_gain[1] = (txcal_gain[1] & 0xF000) | 0x0F40; - } else { - txcal_gain[0] = (txcal_gain[0] & 0xF000) | 0x0F60; - txcal_gain[1] = (txcal_gain[1] & 0xF000) | 0x0F60; - } - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, - txcal_gain); -} - -static void wlc_phy_precal_txgain_nphy(struct brcms_phy *pi) -{ - bool save_bbmult = false; - u8 txcal_index_2057_rev5n7 = 0; - u8 txcal_index_2057_rev3n4n6 = 10; - - if (pi->use_int_tx_iqlo_cal_nphy) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if ((pi->pubpi.radiorev == 3) || - (pi->pubpi.radiorev == 4) || - (pi->pubpi.radiorev == 6)) { - - pi->nphy_txcal_pwr_idx[0] = - txcal_index_2057_rev3n4n6; - pi->nphy_txcal_pwr_idx[1] = - txcal_index_2057_rev3n4n6; - wlc_phy_txpwr_index_nphy( - pi, 3, - txcal_index_2057_rev3n4n6, - false); - } else { - - pi->nphy_txcal_pwr_idx[0] = - txcal_index_2057_rev5n7; - pi->nphy_txcal_pwr_idx[1] = - txcal_index_2057_rev5n7; - wlc_phy_txpwr_index_nphy( - pi, 3, - txcal_index_2057_rev5n7, - false); - } - save_bbmult = true; - - } else if (NREV_LT(pi->pubpi.phy_rev, 5)) { - wlc_phy_cal_txgainctrl_nphy(pi, 11, false); - if (pi->sh->hw_phytxchain != 3) { - pi->nphy_txcal_pwr_idx[1] = - pi->nphy_txcal_pwr_idx[0]; - wlc_phy_txpwr_index_nphy(pi, 3, - pi-> - nphy_txcal_pwr_idx[0], - true); - save_bbmult = true; - } - - } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { - if (PHY_IPA(pi)) { - if (CHSPEC_IS2G(pi->radio_chanspec)) { - wlc_phy_cal_txgainctrl_nphy(pi, 12, - false); - } else { - pi->nphy_txcal_pwr_idx[0] = 80; - pi->nphy_txcal_pwr_idx[1] = 80; - wlc_phy_txpwr_index_nphy(pi, 3, 80, - false); - save_bbmult = true; - } - } else { - wlc_phy_internal_cal_txgain_nphy(pi); - save_bbmult = true; - } - - } else if (NREV_IS(pi->pubpi.phy_rev, 6)) { - if (PHY_IPA(pi)) { - if (CHSPEC_IS2G(pi->radio_chanspec)) - wlc_phy_cal_txgainctrl_nphy(pi, 12, - false); - else - wlc_phy_cal_txgainctrl_nphy(pi, 14, - false); - } else { - wlc_phy_internal_cal_txgain_nphy(pi); - save_bbmult = true; - } - } - - } else { - wlc_phy_cal_txgainctrl_nphy(pi, 10, false); - } - - if (save_bbmult) - wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, - &pi->nphy_txcal_bbmult); -} - -static void -wlc_phy_rfctrlintc_override_nphy(struct brcms_phy *pi, u8 field, u16 value, - u8 core_code) -{ - u16 mask; - u16 val; - u8 core; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - if (core_code == RADIO_MIMO_CORESEL_CORE1 - && core == PHY_CORE_1) - continue; - else if (core_code == RADIO_MIMO_CORESEL_CORE2 - && core == PHY_CORE_0) - continue; - - if (NREV_LT(pi->pubpi.phy_rev, 7)) { - - mask = (0x1 << 10); - val = 1 << 10; - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : - 0x92, mask, val); - } - - if (field == NPHY_RfctrlIntc_override_OFF) { - - write_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : - 0x92, 0); - - wlc_phy_force_rfseq_nphy(pi, - NPHY_RFSEQ_RESET2RX); - } else if (field == NPHY_RfctrlIntc_override_TRSW) { - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - mask = (0x1 << 6) | (0x1 << 7); - - val = value << 6; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 : 0x92, - mask, val); - - or_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 : 0x92, - (0x1 << 10)); - - and_phy_reg(pi, 0x2ff, (u16) - ~(0x3 << 14)); - or_phy_reg(pi, 0x2ff, (0x1 << 13)); - or_phy_reg(pi, 0x2ff, (0x1 << 0)); - } else { - - mask = (0x1 << 6) | - (0x1 << 7) | - (0x1 << 8) | (0x1 << 9); - val = value << 6; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 : 0x92, - mask, val); - - mask = (0x1 << 0); - val = 1 << 0; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0xe7 : 0xec, - mask, val); - - mask = (core == PHY_CORE_0) ? - (0x1 << 0) : (0x1 << 1); - val = 1 << ((core == PHY_CORE_0) ? - 0 : 1); - mod_phy_reg(pi, 0x78, mask, val); - - SPINWAIT(((read_phy_reg(pi, 0x78) & val) - != 0), 10000); - if (WARN(read_phy_reg(pi, 0x78) & val, - "HW error: override failed")) - return; - - mask = (0x1 << 0); - val = 0 << 0; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0xe7 : 0xec, - mask, val); - } - } else if (field == NPHY_RfctrlIntc_override_PA) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - mask = (0x1 << 4) | (0x1 << 5); - - if (CHSPEC_IS5G(pi->radio_chanspec)) - val = value << 5; - else - val = value << 4; - - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 : 0x92, - mask, val); - - or_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 : 0x92, - (0x1 << 12)); - } else { - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - mask = (0x1 << 5); - val = value << 5; - } else { - mask = (0x1 << 4); - val = value << 4; - } - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 : 0x92, - mask, val); - } - } else if (field == - NPHY_RfctrlIntc_override_EXT_LNA_PU) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - - mask = (0x1 << 0); - val = value << 0; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 - : 0x92, mask, val); - - mask = (0x1 << 2); - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 - : 0x92, mask, 0); - } else { - - mask = (0x1 << 2); - val = value << 2; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 - : 0x92, mask, val); - - mask = (0x1 << 0); - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 - : 0x92, mask, 0); - } - - mask = (0x1 << 11); - val = 1 << 11; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 : 0x92, - mask, val); - } else { - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - mask = (0x1 << 0); - val = value << 0; - } else { - mask = (0x1 << 2); - val = value << 2; - } - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 : 0x92, - mask, val); - } - } else if (field == - NPHY_RfctrlIntc_override_EXT_LNA_GAIN) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - - mask = (0x1 << 1); - val = value << 1; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 - : 0x92, mask, val); - - mask = (0x1 << 3); - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 - : 0x92, mask, 0); - } else { - - mask = (0x1 << 3); - val = value << 3; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 - : 0x92, mask, val); - - mask = (0x1 << 1); - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 - : 0x92, mask, 0); - } - - mask = (0x1 << 11); - val = 1 << 11; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 : 0x92, - mask, val); - } else { - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - mask = (0x1 << 1); - val = value << 1; - } else { - mask = (0x1 << 3); - val = value << 3; - } - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x91 : 0x92, - mask, val); - } - } - } - } -} - -void -wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, s32 dBm_targetpower, - bool debug) -{ - int gainctrl_loopidx; - uint core; - u16 m0m1, curr_m0m1; - s32 delta_power; - s32 txpwrindex; - s32 qdBm_power[2]; - u16 orig_BBConfig; - u16 phy_saveregs[4]; - u32 freq_test; - u16 ampl_test = 250; - uint stepsize; - bool phyhang_avoid_state = false; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - stepsize = 2; - else - stepsize = 1; - - if (CHSPEC_IS40(pi->radio_chanspec)) - freq_test = 5000; - else - freq_test = 2500; - - wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_cal_orig_pwr_idx[0], true); - wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_cal_orig_pwr_idx[1], true); - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - phyhang_avoid_state = pi->phyhang_avoid; - pi->phyhang_avoid = false; - - phy_saveregs[0] = read_phy_reg(pi, 0x91); - phy_saveregs[1] = read_phy_reg(pi, 0x92); - phy_saveregs[2] = read_phy_reg(pi, 0xe7); - phy_saveregs[3] = read_phy_reg(pi, 0xec); - wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_PA, 1, - RADIO_MIMO_CORESEL_CORE1 | - RADIO_MIMO_CORESEL_CORE2); - - if (!debug) { - wlc_phy_rfctrlintc_override_nphy(pi, - NPHY_RfctrlIntc_override_TRSW, - 0x2, RADIO_MIMO_CORESEL_CORE1); - wlc_phy_rfctrlintc_override_nphy(pi, - NPHY_RfctrlIntc_override_TRSW, - 0x8, RADIO_MIMO_CORESEL_CORE2); - } else { - wlc_phy_rfctrlintc_override_nphy(pi, - NPHY_RfctrlIntc_override_TRSW, - 0x1, RADIO_MIMO_CORESEL_CORE1); - wlc_phy_rfctrlintc_override_nphy(pi, - NPHY_RfctrlIntc_override_TRSW, - 0x7, RADIO_MIMO_CORESEL_CORE2); - } - - orig_BBConfig = read_phy_reg(pi, 0x01); - mod_phy_reg(pi, 0x01, (0x1 << 15), 0); - - wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m0m1); - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - txpwrindex = (s32) pi->nphy_cal_orig_pwr_idx[core]; - - for (gainctrl_loopidx = 0; gainctrl_loopidx < 2; - gainctrl_loopidx++) { - wlc_phy_tx_tone_nphy(pi, freq_test, ampl_test, 0, 0, - false); - - if (core == PHY_CORE_0) - curr_m0m1 = m0m1 & 0xff00; - else - curr_m0m1 = m0m1 & 0x00ff; - - wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &curr_m0m1); - wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &curr_m0m1); - - udelay(50); - - wlc_phy_est_tonepwr_nphy(pi, qdBm_power, - NPHY_CAL_TSSISAMPS); - - pi->nphy_bb_mult_save = 0; - wlc_phy_stopplayback_nphy(pi); - - delta_power = (dBm_targetpower * 4) - qdBm_power[core]; - - txpwrindex -= stepsize * delta_power; - if (txpwrindex < 0) - txpwrindex = 0; - else if (txpwrindex > 127) - txpwrindex = 127; - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - if (NREV_IS(pi->pubpi.phy_rev, 4) && - (pi->srom_fem5g.extpagain == 3)) { - if (txpwrindex < 30) - txpwrindex = 30; - } - } else { - if (NREV_GE(pi->pubpi.phy_rev, 5) && - (pi->srom_fem2g.extpagain == 3)) { - if (txpwrindex < 50) - txpwrindex = 50; - } - } - - wlc_phy_txpwr_index_nphy(pi, (1 << core), - (u8) txpwrindex, true); - } - - pi->nphy_txcal_pwr_idx[core] = (u8) txpwrindex; - - if (debug) { - u16 radio_gain; - u16 dbg_m0m1; - - wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &dbg_m0m1); - - wlc_phy_tx_tone_nphy(pi, freq_test, ampl_test, 0, 0, - false); - - wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &dbg_m0m1); - wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &dbg_m0m1); - - udelay(100); - - wlc_phy_est_tonepwr_nphy(pi, qdBm_power, - NPHY_CAL_TSSISAMPS); - - wlc_phy_table_read_nphy(pi, 7, 1, (0x110 + core), 16, - &radio_gain); - - mdelay(4000); - pi->nphy_bb_mult_save = 0; - wlc_phy_stopplayback_nphy(pi); - } - } - - wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_txcal_pwr_idx[0], true); - wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_txcal_pwr_idx[1], true); - - wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &pi->nphy_txcal_bbmult); - - write_phy_reg(pi, 0x01, orig_BBConfig); - - write_phy_reg(pi, 0x91, phy_saveregs[0]); - write_phy_reg(pi, 0x92, phy_saveregs[1]); - write_phy_reg(pi, 0xe7, phy_saveregs[2]); - write_phy_reg(pi, 0xec, phy_saveregs[3]); - - pi->phyhang_avoid = phyhang_avoid_state; - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -static void wlc_phy_savecal_nphy(struct brcms_phy *pi) -{ - void *tbl_ptr; - int coreNum; - u16 *txcal_radio_regs = NULL; - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - - wlc_phy_rx_iq_coeffs_nphy(pi, 0, - &pi->calibration_cache. - rxcal_coeffs_2G); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - txcal_radio_regs = - pi->calibration_cache.txcal_radio_regs_2G; - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - - pi->calibration_cache.txcal_radio_regs_2G[0] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_I | - RADIO_2056_TX0); - pi->calibration_cache.txcal_radio_regs_2G[1] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_Q | - RADIO_2056_TX0); - pi->calibration_cache.txcal_radio_regs_2G[2] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_I | - RADIO_2056_TX1); - pi->calibration_cache.txcal_radio_regs_2G[3] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_Q | - RADIO_2056_TX1); - - pi->calibration_cache.txcal_radio_regs_2G[4] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_I | - RADIO_2056_TX0); - pi->calibration_cache.txcal_radio_regs_2G[5] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_Q | - RADIO_2056_TX0); - pi->calibration_cache.txcal_radio_regs_2G[6] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_I | - RADIO_2056_TX1); - pi->calibration_cache.txcal_radio_regs_2G[7] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_Q | - RADIO_2056_TX1); - } else { - pi->calibration_cache.txcal_radio_regs_2G[0] = - read_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL); - pi->calibration_cache.txcal_radio_regs_2G[1] = - read_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL); - pi->calibration_cache.txcal_radio_regs_2G[2] = - read_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM); - pi->calibration_cache.txcal_radio_regs_2G[3] = - read_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM); - } - - pi->nphy_iqcal_chanspec_2G = pi->radio_chanspec; - tbl_ptr = pi->calibration_cache.txcal_coeffs_2G; - } else { - - wlc_phy_rx_iq_coeffs_nphy(pi, 0, - &pi->calibration_cache. - rxcal_coeffs_5G); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - txcal_radio_regs = - pi->calibration_cache.txcal_radio_regs_5G; - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - - pi->calibration_cache.txcal_radio_regs_5G[0] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_I | - RADIO_2056_TX0); - pi->calibration_cache.txcal_radio_regs_5G[1] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_Q | - RADIO_2056_TX0); - pi->calibration_cache.txcal_radio_regs_5G[2] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_I | - RADIO_2056_TX1); - pi->calibration_cache.txcal_radio_regs_5G[3] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_Q | - RADIO_2056_TX1); - - pi->calibration_cache.txcal_radio_regs_5G[4] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_I | - RADIO_2056_TX0); - pi->calibration_cache.txcal_radio_regs_5G[5] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_Q | - RADIO_2056_TX0); - pi->calibration_cache.txcal_radio_regs_5G[6] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_I | - RADIO_2056_TX1); - pi->calibration_cache.txcal_radio_regs_5G[7] = - read_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_Q | - RADIO_2056_TX1); - } else { - pi->calibration_cache.txcal_radio_regs_5G[0] = - read_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL); - pi->calibration_cache.txcal_radio_regs_5G[1] = - read_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL); - pi->calibration_cache.txcal_radio_regs_5G[2] = - read_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM); - pi->calibration_cache.txcal_radio_regs_5G[3] = - read_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM); - } - - pi->nphy_iqcal_chanspec_5G = pi->radio_chanspec; - tbl_ptr = pi->calibration_cache.txcal_coeffs_5G; - } - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - for (coreNum = 0; coreNum <= 1; coreNum++) { - - txcal_radio_regs[2 * coreNum] = - READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, - LOFT_FINE_I); - txcal_radio_regs[2 * coreNum + 1] = - READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, - LOFT_FINE_Q); - - txcal_radio_regs[2 * coreNum + 4] = - READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, - LOFT_COARSE_I); - txcal_radio_regs[2 * coreNum + 5] = - READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, - LOFT_COARSE_Q); - } - } - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 8, 80, 16, tbl_ptr); - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -static void wlc_phy_tx_iq_war_nphy(struct brcms_phy *pi) -{ - struct nphy_iq_comp tx_comp; - - wlc_phy_table_read_nphy(pi, 15, 4, 0x50, 16, &tx_comp); - - wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ, tx_comp.a0); - wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 2, tx_comp.b0); - wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 4, tx_comp.a1); - wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 6, tx_comp.b1); -} - -static void wlc_phy_restorecal_nphy(struct brcms_phy *pi) -{ - u16 *loft_comp; - u16 txcal_coeffs_bphy[4]; - u16 *tbl_ptr; - int coreNum; - u16 *txcal_radio_regs = NULL; - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if (pi->nphy_iqcal_chanspec_2G == 0) - return; - - tbl_ptr = pi->calibration_cache.txcal_coeffs_2G; - loft_comp = &pi->calibration_cache.txcal_coeffs_2G[5]; - } else { - if (pi->nphy_iqcal_chanspec_5G == 0) - return; - - tbl_ptr = pi->calibration_cache.txcal_coeffs_5G; - loft_comp = &pi->calibration_cache.txcal_coeffs_5G[5]; - } - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 80, 16, tbl_ptr); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - txcal_coeffs_bphy[0] = tbl_ptr[0]; - txcal_coeffs_bphy[1] = tbl_ptr[1]; - txcal_coeffs_bphy[2] = tbl_ptr[2]; - txcal_coeffs_bphy[3] = tbl_ptr[3]; - } else { - txcal_coeffs_bphy[0] = 0; - txcal_coeffs_bphy[1] = 0; - txcal_coeffs_bphy[2] = 0; - txcal_coeffs_bphy[3] = 0; - } - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 88, 16, - txcal_coeffs_bphy); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 85, 16, loft_comp); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 93, 16, loft_comp); - - if (NREV_LT(pi->pubpi.phy_rev, 2)) - wlc_phy_tx_iq_war_nphy(pi); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - txcal_radio_regs = - pi->calibration_cache.txcal_radio_regs_2G; - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - - write_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_I | - RADIO_2056_TX0, - pi->calibration_cache. - txcal_radio_regs_2G[0]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_Q | - RADIO_2056_TX0, - pi->calibration_cache. - txcal_radio_regs_2G[1]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_I | - RADIO_2056_TX1, - pi->calibration_cache. - txcal_radio_regs_2G[2]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_Q | - RADIO_2056_TX1, - pi->calibration_cache. - txcal_radio_regs_2G[3]); - - write_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_I | - RADIO_2056_TX0, - pi->calibration_cache. - txcal_radio_regs_2G[4]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_Q | - RADIO_2056_TX0, - pi->calibration_cache. - txcal_radio_regs_2G[5]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_I | - RADIO_2056_TX1, - pi->calibration_cache. - txcal_radio_regs_2G[6]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_Q | - RADIO_2056_TX1, - pi->calibration_cache. - txcal_radio_regs_2G[7]); - } else { - write_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL, - pi->calibration_cache. - txcal_radio_regs_2G[0]); - write_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL, - pi->calibration_cache. - txcal_radio_regs_2G[1]); - write_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, - pi->calibration_cache. - txcal_radio_regs_2G[2]); - write_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, - pi->calibration_cache. - txcal_radio_regs_2G[3]); - } - - wlc_phy_rx_iq_coeffs_nphy(pi, 1, - &pi->calibration_cache. - rxcal_coeffs_2G); - } else { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - txcal_radio_regs = - pi->calibration_cache.txcal_radio_regs_5G; - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - - write_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_I | - RADIO_2056_TX0, - pi->calibration_cache. - txcal_radio_regs_5G[0]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_Q | - RADIO_2056_TX0, - pi->calibration_cache. - txcal_radio_regs_5G[1]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_I | - RADIO_2056_TX1, - pi->calibration_cache. - txcal_radio_regs_5G[2]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_FINE_Q | - RADIO_2056_TX1, - pi->calibration_cache. - txcal_radio_regs_5G[3]); - - write_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_I | - RADIO_2056_TX0, - pi->calibration_cache. - txcal_radio_regs_5G[4]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_Q | - RADIO_2056_TX0, - pi->calibration_cache. - txcal_radio_regs_5G[5]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_I | - RADIO_2056_TX1, - pi->calibration_cache. - txcal_radio_regs_5G[6]); - write_radio_reg(pi, - RADIO_2056_TX_LOFT_COARSE_Q | - RADIO_2056_TX1, - pi->calibration_cache. - txcal_radio_regs_5G[7]); - } else { - write_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL, - pi->calibration_cache. - txcal_radio_regs_5G[0]); - write_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL, - pi->calibration_cache. - txcal_radio_regs_5G[1]); - write_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, - pi->calibration_cache. - txcal_radio_regs_5G[2]); - write_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, - pi->calibration_cache. - txcal_radio_regs_5G[3]); - } - - wlc_phy_rx_iq_coeffs_nphy(pi, 1, - &pi->calibration_cache. - rxcal_coeffs_5G); - } - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - for (coreNum = 0; coreNum <= 1; coreNum++) { - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, - LOFT_FINE_I, - txcal_radio_regs[2 * coreNum]); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, - LOFT_FINE_Q, - txcal_radio_regs[2 * coreNum + 1]); - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, - LOFT_COARSE_I, - txcal_radio_regs[2 * coreNum + 4]); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, - LOFT_COARSE_Q, - txcal_radio_regs[2 * coreNum + 5]); - } - } -} - -static void wlc_phy_txpwrctrl_coeff_setup_nphy(struct brcms_phy *pi) -{ - u32 idx; - u16 iqloCalbuf[7]; - u32 iqcomp, locomp, curr_locomp; - s8 locomp_i, locomp_q; - s8 curr_locomp_i, curr_locomp_q; - u32 tbl_id, tbl_len, tbl_offset; - u32 regval[128]; - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - wlc_phy_table_read_nphy(pi, 15, 7, 80, 16, iqloCalbuf); - - tbl_len = 128; - tbl_offset = 320; - for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL; - tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) { - iqcomp = - (tbl_id == - 26) ? (((u32) (iqloCalbuf[0] & 0x3ff)) << 10) | - (iqloCalbuf[1] & 0x3ff) - : (((u32) (iqloCalbuf[2] & 0x3ff)) << 10) | - (iqloCalbuf[3] & 0x3ff); - - for (idx = 0; idx < tbl_len; idx++) - regval[idx] = iqcomp; - wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32, - regval); - } - - tbl_offset = 448; - for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL; - tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) { - - locomp = - (u32) ((tbl_id == 26) ? iqloCalbuf[5] : iqloCalbuf[6]); - locomp_i = (s8) ((locomp >> 8) & 0xff); - locomp_q = (s8) ((locomp) & 0xff); - for (idx = 0; idx < tbl_len; idx++) { - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - curr_locomp_i = locomp_i; - curr_locomp_q = locomp_q; - } else { - curr_locomp_i = (s8) ((locomp_i * - nphy_tpc_loscale[idx] + - 128) >> 8); - curr_locomp_q = - (s8) ((locomp_q * - nphy_tpc_loscale[idx] + - 128) >> 8); - } - curr_locomp = (u32) ((curr_locomp_i & 0xff) << 8); - curr_locomp |= (u32) (curr_locomp_q & 0xff); - regval[idx] = curr_locomp; - } - wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32, - regval); - } - - if (NREV_LT(pi->pubpi.phy_rev, 2)) { - - wlapi_bmac_write_shm(pi->sh->physhim, M_CURR_IDX1, 0xFFFF); - wlapi_bmac_write_shm(pi->sh->physhim, M_CURR_IDX2, 0xFFFF); - } - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -static void wlc_phy_txlpfbw_nphy(struct brcms_phy *pi) -{ - u8 tx_lpf_bw = 0; - - if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { - if (CHSPEC_IS40(pi->radio_chanspec)) - tx_lpf_bw = 3; - else - tx_lpf_bw = 1; - - if (PHY_IPA(pi)) { - if (CHSPEC_IS40(pi->radio_chanspec)) - tx_lpf_bw = 5; - else - tx_lpf_bw = 4; - } - - write_phy_reg(pi, 0xe8, - (tx_lpf_bw << 0) | - (tx_lpf_bw << 3) | - (tx_lpf_bw << 6) | (tx_lpf_bw << 9)); - - if (PHY_IPA(pi)) { - - if (CHSPEC_IS40(pi->radio_chanspec)) - tx_lpf_bw = 4; - else - tx_lpf_bw = 1; - - write_phy_reg(pi, 0xe9, - (tx_lpf_bw << 0) | - (tx_lpf_bw << 3) | - (tx_lpf_bw << 6) | (tx_lpf_bw << 9)); - } - } -} - -static void -wlc_phy_adjust_rx_analpfbw_nphy(struct brcms_phy *pi, u16 reduction_factr) -{ - if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { - if ((CHSPEC_CHANNEL(pi->radio_chanspec) == 11) && - CHSPEC_IS40(pi->radio_chanspec)) { - if (!pi->nphy_anarxlpf_adjusted) { - write_radio_reg(pi, - (RADIO_2056_RX_RXLPF_RCCAL_LPC | - RADIO_2056_RX0), - ((pi->nphy_rccal_value + - reduction_factr) | 0x80)); - - pi->nphy_anarxlpf_adjusted = true; - } - } else { - if (pi->nphy_anarxlpf_adjusted) { - write_radio_reg(pi, - (RADIO_2056_RX_RXLPF_RCCAL_LPC | - RADIO_2056_RX0), - (pi->nphy_rccal_value | 0x80)); - - pi->nphy_anarxlpf_adjusted = false; - } - } - } -} - -static void -wlc_phy_adjust_min_noisevar_nphy(struct brcms_phy *pi, int ntones, - int *tone_id_buf, u32 *noise_var_buf) -{ - int i; - u32 offset; - int tone_id; - int tbllen = - CHSPEC_IS40(pi->radio_chanspec) ? - NPHY_NOISEVAR_TBLLEN40 : NPHY_NOISEVAR_TBLLEN20; - - if (pi->nphy_noisevars_adjusted) { - for (i = 0; i < pi->nphy_saved_noisevars.bufcount; i++) { - tone_id = pi->nphy_saved_noisevars.tone_id[i]; - offset = (tone_id >= 0) ? - ((tone_id * - 2) + 1) : (tbllen + (tone_id * 2) + 1); - wlc_phy_table_write_nphy( - pi, NPHY_TBL_ID_NOISEVAR, 1, - offset, 32, - &pi->nphy_saved_noisevars.min_noise_vars[i]); - } - - pi->nphy_saved_noisevars.bufcount = 0; - pi->nphy_noisevars_adjusted = false; - } - - if ((noise_var_buf != NULL) && (tone_id_buf != NULL)) { - pi->nphy_saved_noisevars.bufcount = 0; - - for (i = 0; i < ntones; i++) { - tone_id = tone_id_buf[i]; - offset = (tone_id >= 0) ? - ((tone_id * 2) + 1) : - (tbllen + (tone_id * 2) + 1); - pi->nphy_saved_noisevars.tone_id[i] = tone_id; - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, - offset, 32, - &pi->nphy_saved_noisevars. - min_noise_vars[i]); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, - offset, 32, &noise_var_buf[i]); - pi->nphy_saved_noisevars.bufcount++; - } - - pi->nphy_noisevars_adjusted = true; - } -} - -static void wlc_phy_adjust_crsminpwr_nphy(struct brcms_phy *pi, u8 minpwr) -{ - u16 regval; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - if ((CHSPEC_CHANNEL(pi->radio_chanspec) == 11) && - CHSPEC_IS40(pi->radio_chanspec)) { - if (!pi->nphy_crsminpwr_adjusted) { - regval = read_phy_reg(pi, 0x27d); - pi->nphy_crsminpwr[0] = regval & 0xff; - regval &= 0xff00; - regval |= (u16) minpwr; - write_phy_reg(pi, 0x27d, regval); - - regval = read_phy_reg(pi, 0x280); - pi->nphy_crsminpwr[1] = regval & 0xff; - regval &= 0xff00; - regval |= (u16) minpwr; - write_phy_reg(pi, 0x280, regval); - - regval = read_phy_reg(pi, 0x283); - pi->nphy_crsminpwr[2] = regval & 0xff; - regval &= 0xff00; - regval |= (u16) minpwr; - write_phy_reg(pi, 0x283, regval); - - pi->nphy_crsminpwr_adjusted = true; - } - } else { - if (pi->nphy_crsminpwr_adjusted) { - regval = read_phy_reg(pi, 0x27d); - regval &= 0xff00; - regval |= pi->nphy_crsminpwr[0]; - write_phy_reg(pi, 0x27d, regval); - - regval = read_phy_reg(pi, 0x280); - regval &= 0xff00; - regval |= pi->nphy_crsminpwr[1]; - write_phy_reg(pi, 0x280, regval); - - regval = read_phy_reg(pi, 0x283); - regval &= 0xff00; - regval |= pi->nphy_crsminpwr[2]; - write_phy_reg(pi, 0x283, regval); - - pi->nphy_crsminpwr_adjusted = false; - } - } - } -} - -static void wlc_phy_spurwar_nphy(struct brcms_phy *pi) -{ - u16 cur_channel = 0; - int nphy_adj_tone_id_buf[] = { 57, 58 }; - u32 nphy_adj_noise_var_buf[] = { 0x3ff, 0x3ff }; - bool isAdjustNoiseVar = false; - uint numTonesAdjust = 0; - u32 tempval = 0; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - cur_channel = CHSPEC_CHANNEL(pi->radio_chanspec); - - if (pi->nphy_gband_spurwar_en) { - - wlc_phy_adjust_rx_analpfbw_nphy( - pi, - NPHY_ANARXLPFBW_REDUCTIONFACT); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if ((cur_channel == 11) - && CHSPEC_IS40(pi->radio_chanspec)) - wlc_phy_adjust_min_noisevar_nphy( - pi, 2, - nphy_adj_tone_id_buf, - nphy_adj_noise_var_buf); - else - wlc_phy_adjust_min_noisevar_nphy(pi, 0, - NULL, - NULL); - } - - wlc_phy_adjust_crsminpwr_nphy(pi, - NPHY_ADJUSTED_MINCRSPOWER); - } - - if ((pi->nphy_gband_spurwar2_en) - && CHSPEC_IS2G(pi->radio_chanspec)) { - - if (CHSPEC_IS40(pi->radio_chanspec)) { - switch (cur_channel) { - case 3: - nphy_adj_tone_id_buf[0] = 57; - nphy_adj_tone_id_buf[1] = 58; - nphy_adj_noise_var_buf[0] = 0x22f; - nphy_adj_noise_var_buf[1] = 0x25f; - isAdjustNoiseVar = true; - break; - case 4: - nphy_adj_tone_id_buf[0] = 41; - nphy_adj_tone_id_buf[1] = 42; - nphy_adj_noise_var_buf[0] = 0x22f; - nphy_adj_noise_var_buf[1] = 0x25f; - isAdjustNoiseVar = true; - break; - case 5: - nphy_adj_tone_id_buf[0] = 25; - nphy_adj_tone_id_buf[1] = 26; - nphy_adj_noise_var_buf[0] = 0x24f; - nphy_adj_noise_var_buf[1] = 0x25f; - isAdjustNoiseVar = true; - break; - case 6: - nphy_adj_tone_id_buf[0] = 9; - nphy_adj_tone_id_buf[1] = 10; - nphy_adj_noise_var_buf[0] = 0x22f; - nphy_adj_noise_var_buf[1] = 0x24f; - isAdjustNoiseVar = true; - break; - case 7: - nphy_adj_tone_id_buf[0] = 121; - nphy_adj_tone_id_buf[1] = 122; - nphy_adj_noise_var_buf[0] = 0x18f; - nphy_adj_noise_var_buf[1] = 0x24f; - isAdjustNoiseVar = true; - break; - case 8: - nphy_adj_tone_id_buf[0] = 105; - nphy_adj_tone_id_buf[1] = 106; - nphy_adj_noise_var_buf[0] = 0x22f; - nphy_adj_noise_var_buf[1] = 0x25f; - isAdjustNoiseVar = true; - break; - case 9: - nphy_adj_tone_id_buf[0] = 89; - nphy_adj_tone_id_buf[1] = 90; - nphy_adj_noise_var_buf[0] = 0x22f; - nphy_adj_noise_var_buf[1] = 0x24f; - isAdjustNoiseVar = true; - break; - case 10: - nphy_adj_tone_id_buf[0] = 73; - nphy_adj_tone_id_buf[1] = 74; - nphy_adj_noise_var_buf[0] = 0x22f; - nphy_adj_noise_var_buf[1] = 0x24f; - isAdjustNoiseVar = true; - break; - default: - isAdjustNoiseVar = false; - break; - } - } - - if (isAdjustNoiseVar) { - numTonesAdjust = ARRAY_SIZE(nphy_adj_tone_id_buf); - - wlc_phy_adjust_min_noisevar_nphy( - pi, - numTonesAdjust, - nphy_adj_tone_id_buf, - nphy_adj_noise_var_buf); - - tempval = 0; - - } else { - wlc_phy_adjust_min_noisevar_nphy(pi, 0, NULL, - NULL); - } - } - - if ((pi->nphy_aband_spurwar_en) && - (CHSPEC_IS5G(pi->radio_chanspec))) { - switch (cur_channel) { - case 54: - nphy_adj_tone_id_buf[0] = 32; - nphy_adj_noise_var_buf[0] = 0x25f; - break; - case 38: - case 102: - case 118: - if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) && - (pi->sh->chippkg == BCMA_PKG_ID_BCM4717)) { - nphy_adj_tone_id_buf[0] = 32; - nphy_adj_noise_var_buf[0] = 0x21f; - } else { - nphy_adj_tone_id_buf[0] = 0; - nphy_adj_noise_var_buf[0] = 0x0; - } - break; - case 134: - nphy_adj_tone_id_buf[0] = 32; - nphy_adj_noise_var_buf[0] = 0x21f; - break; - case 151: - nphy_adj_tone_id_buf[0] = 16; - nphy_adj_noise_var_buf[0] = 0x23f; - break; - case 153: - case 161: - nphy_adj_tone_id_buf[0] = 48; - nphy_adj_noise_var_buf[0] = 0x23f; - break; - default: - nphy_adj_tone_id_buf[0] = 0; - nphy_adj_noise_var_buf[0] = 0x0; - break; - } - - if (nphy_adj_tone_id_buf[0] - && nphy_adj_noise_var_buf[0]) - wlc_phy_adjust_min_noisevar_nphy( - pi, 1, - nphy_adj_tone_id_buf, - nphy_adj_noise_var_buf); - else - wlc_phy_adjust_min_noisevar_nphy(pi, 0, NULL, - NULL); - } - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); - } -} - -void wlc_phy_init_nphy(struct brcms_phy *pi) -{ - u16 val; - u16 clip1_ths[2]; - struct nphy_txgains target_gain; - u8 tx_pwr_ctrl_state; - bool do_nphy_cal = false; - uint core; - u32 d11_clk_ctl_st; - bool do_rssi_cal = false; - - core = 0; - - if (!(pi->measure_hold & PHY_HOLD_FOR_SCAN)) - pi->measure_hold |= PHY_HOLD_FOR_NOT_ASSOC; - - if ((ISNPHY(pi)) && (NREV_GE(pi->pubpi.phy_rev, 5)) && - ((pi->sh->chippkg == BCMA_PKG_ID_BCM4717) || - (pi->sh->chippkg == BCMA_PKG_ID_BCM4718))) { - if ((pi->sh->boardflags & BFL_EXTLNA) && - (CHSPEC_IS2G(pi->radio_chanspec))) - bcma_cc_set32(&pi->d11core->bus->drv_cc, - BCMA_CC_CHIPCTL, 0x40); - } - - if ((!PHY_IPA(pi)) && (pi->sh->chip == BCMA_CHIP_ID_BCM5357)) - bcma_chipco_chipctl_maskset(&pi->d11core->bus->drv_cc, 1, - ~CCTRL5357_EXTPA, CCTRL5357_EXTPA); - - if ((pi->nphy_gband_spurwar2_en) && CHSPEC_IS2G(pi->radio_chanspec) && - CHSPEC_IS40(pi->radio_chanspec)) { - - d11_clk_ctl_st = bcma_read32(pi->d11core, - D11REGOFFS(clk_ctl_st)); - bcma_mask32(pi->d11core, D11REGOFFS(clk_ctl_st), - ~(CCS_FORCEHT | CCS_HTAREQ)); - - bcma_write32(pi->d11core, D11REGOFFS(clk_ctl_st), - d11_clk_ctl_st); - } - - pi->use_int_tx_iqlo_cal_nphy = - (PHY_IPA(pi) || - (NREV_GE(pi->pubpi.phy_rev, 7) || - (NREV_GE(pi->pubpi.phy_rev, 5) - && pi->sh->boardflags2 & BFL2_INTERNDET_TXIQCAL))); - - pi->internal_tx_iqlo_cal_tapoff_intpa_nphy = false; - - pi->nphy_deaf_count = 0; - - wlc_phy_tbl_init_nphy(pi); - - pi->nphy_crsminpwr_adjusted = false; - pi->nphy_noisevars_adjusted = false; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - write_phy_reg(pi, 0xe7, 0); - write_phy_reg(pi, 0xec, 0); - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - write_phy_reg(pi, 0x342, 0); - write_phy_reg(pi, 0x343, 0); - write_phy_reg(pi, 0x346, 0); - write_phy_reg(pi, 0x347, 0); - } - write_phy_reg(pi, 0xe5, 0); - write_phy_reg(pi, 0xe6, 0); - } else { - write_phy_reg(pi, 0xec, 0); - } - - write_phy_reg(pi, 0x91, 0); - write_phy_reg(pi, 0x92, 0); - if (NREV_LT(pi->pubpi.phy_rev, 6)) { - write_phy_reg(pi, 0x93, 0); - write_phy_reg(pi, 0x94, 0); - } - - and_phy_reg(pi, 0xa1, ~3); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - write_phy_reg(pi, 0x8f, 0); - write_phy_reg(pi, 0xa5, 0); - } else { - write_phy_reg(pi, 0xa5, 0); - } - - if (NREV_IS(pi->pubpi.phy_rev, 2)) - mod_phy_reg(pi, 0xdc, 0x00ff, 0x3b); - else if (NREV_LT(pi->pubpi.phy_rev, 2)) - mod_phy_reg(pi, 0xdc, 0x00ff, 0x40); - - write_phy_reg(pi, 0x203, 32); - write_phy_reg(pi, 0x201, 32); - - if (pi->sh->boardflags2 & BFL2_SKWRKFEM_BRD) - write_phy_reg(pi, 0x20d, 160); - else - write_phy_reg(pi, 0x20d, 184); - - write_phy_reg(pi, 0x13a, 200); - - write_phy_reg(pi, 0x70, 80); - - write_phy_reg(pi, 0x1ff, 48); - - if (NREV_LT(pi->pubpi.phy_rev, 8)) - wlc_phy_update_mimoconfig_nphy(pi, pi->n_preamble_override); - - wlc_phy_stf_chain_upd_nphy(pi); - - if (NREV_LT(pi->pubpi.phy_rev, 2)) { - write_phy_reg(pi, 0x180, 0xaa8); - write_phy_reg(pi, 0x181, 0x9a4); - } - - if (PHY_IPA(pi)) { - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (1) << 0); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x298 : - 0x29c, (0x1ff << 7), - (pi->nphy_papd_epsilon_offset[core]) << 7); - - } - - wlc_phy_ipa_set_tx_digi_filts_nphy(pi); - } else if (NREV_GE(pi->pubpi.phy_rev, 5)) { - wlc_phy_extpa_set_tx_digi_filts_nphy(pi); - } - - wlc_phy_workarounds_nphy(pi); - - wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON); - - val = read_phy_reg(pi, 0x01); - write_phy_reg(pi, 0x01, val | BBCFG_RESETCCA); - write_phy_reg(pi, 0x01, val & (~BBCFG_RESETCCA)); - wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF); - - wlapi_bmac_macphyclk_set(pi->sh->physhim, ON); - - wlc_phy_pa_override_nphy(pi, OFF); - wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX); - wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); - wlc_phy_pa_override_nphy(pi, ON); - - wlc_phy_classifier_nphy(pi, 0, 0); - wlc_phy_clip_det_nphy(pi, 0, clip1_ths); - - if (CHSPEC_IS2G(pi->radio_chanspec)) - wlc_phy_bphy_init_nphy(pi); - - tx_pwr_ctrl_state = pi->nphy_txpwrctrl; - wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); - - wlc_phy_txpwr_fixpower_nphy(pi); - - wlc_phy_txpwrctrl_idle_tssi_nphy(pi); - - wlc_phy_txpwrctrl_pwr_setup_nphy(pi); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - u32 *tx_pwrctrl_tbl = NULL; - u16 idx; - s16 pga_gn = 0; - s16 pad_gn = 0; - s32 rfpwr_offset; - - if (PHY_IPA(pi)) { - tx_pwrctrl_tbl = wlc_phy_get_ipa_gaintbl_nphy(pi); - } else { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - if (NREV_IS(pi->pubpi.phy_rev, 3)) - tx_pwrctrl_tbl = - nphy_tpc_5GHz_txgain_rev3; - else if (NREV_IS(pi->pubpi.phy_rev, 4)) - tx_pwrctrl_tbl = - (pi->srom_fem5g.extpagain == - 3) ? - nphy_tpc_5GHz_txgain_HiPwrEPA : - nphy_tpc_5GHz_txgain_rev4; - else - tx_pwrctrl_tbl = - nphy_tpc_5GHz_txgain_rev5; - } else { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if (pi->pubpi.radiorev == 5) - tx_pwrctrl_tbl = - nphy_tpc_txgain_epa_2057rev5; - else if (pi->pubpi.radiorev == 3) - tx_pwrctrl_tbl = - nphy_tpc_txgain_epa_2057rev3; - } else { - if (NREV_GE(pi->pubpi.phy_rev, 5) && - (pi->srom_fem2g.extpagain == 3)) - tx_pwrctrl_tbl = - nphy_tpc_txgain_HiPwrEPA; - else - tx_pwrctrl_tbl = - nphy_tpc_txgain_rev3; - } - } - } - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 128, - 192, 32, tx_pwrctrl_tbl); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 128, - 192, 32, tx_pwrctrl_tbl); - - pi->nphy_gmval = (u16) ((*tx_pwrctrl_tbl >> 16) & 0x7000); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - for (idx = 0; idx < 128; idx++) { - pga_gn = (tx_pwrctrl_tbl[idx] >> 24) & 0xf; - pad_gn = (tx_pwrctrl_tbl[idx] >> 19) & 0x1f; - rfpwr_offset = get_rf_pwr_offset(pi, pga_gn, - pad_gn); - wlc_phy_table_write_nphy( - pi, - NPHY_TBL_ID_CORE1TXPWRCTL, - 1, 576 + idx, 32, - &rfpwr_offset); - wlc_phy_table_write_nphy( - pi, - NPHY_TBL_ID_CORE2TXPWRCTL, - 1, 576 + idx, 32, - &rfpwr_offset); - } - } else { - - for (idx = 0; idx < 128; idx++) { - pga_gn = (tx_pwrctrl_tbl[idx] >> 24) & 0xf; - if (CHSPEC_IS2G(pi->radio_chanspec)) - rfpwr_offset = (s16) - nphy_papd_pga_gain_delta_ipa_2g - [pga_gn]; - else - rfpwr_offset = (s16) - nphy_papd_pga_gain_delta_ipa_5g - [pga_gn]; - - wlc_phy_table_write_nphy( - pi, - NPHY_TBL_ID_CORE1TXPWRCTL, - 1, 576 + idx, 32, - &rfpwr_offset); - wlc_phy_table_write_nphy( - pi, - NPHY_TBL_ID_CORE2TXPWRCTL, - 1, 576 + idx, 32, - &rfpwr_offset); - } - - } - } else { - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 128, - 192, 32, nphy_tpc_txgain); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 128, - 192, 32, nphy_tpc_txgain); - } - - if (pi->sh->phyrxchain != 0x3) - wlc_phy_rxcore_setstate_nphy((struct brcms_phy_pub *) pi, - pi->sh->phyrxchain); - - if (PHY_PERICAL_MPHASE_PENDING(pi)) - wlc_phy_cal_perical_mphase_restart(pi); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - do_rssi_cal = (CHSPEC_IS2G(pi->radio_chanspec)) ? - (pi->nphy_rssical_chanspec_2G == 0) : - (pi->nphy_rssical_chanspec_5G == 0); - - if (do_rssi_cal) - wlc_phy_rssi_cal_nphy(pi); - else - wlc_phy_restore_rssical_nphy(pi); - } else { - wlc_phy_rssi_cal_nphy(pi); - } - - if (!SCAN_RM_IN_PROGRESS(pi)) - do_nphy_cal = (CHSPEC_IS2G(pi->radio_chanspec)) ? - (pi->nphy_iqcal_chanspec_2G == 0) : - (pi->nphy_iqcal_chanspec_5G == 0); - - if (!pi->do_initcal) - do_nphy_cal = false; - - if (do_nphy_cal) { - - target_gain = wlc_phy_get_tx_gain_nphy(pi); - - if (pi->antsel_type == ANTSEL_2x3) - wlc_phy_antsel_init((struct brcms_phy_pub *) pi, - true); - - if (pi->nphy_perical != PHY_PERICAL_MPHASE) { - wlc_phy_rssi_cal_nphy(pi); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - pi->nphy_cal_orig_pwr_idx[0] = - pi->nphy_txpwrindex[PHY_CORE_0] - . - index_internal; - pi->nphy_cal_orig_pwr_idx[1] = - pi->nphy_txpwrindex[PHY_CORE_1] - . - index_internal; - - wlc_phy_precal_txgain_nphy(pi); - target_gain = - wlc_phy_get_tx_gain_nphy(pi); - } - - if (wlc_phy_cal_txiqlo_nphy - (pi, target_gain, true, - false) == 0) { - if (wlc_phy_cal_rxiq_nphy - (pi, target_gain, 2, - false) == 0) - wlc_phy_savecal_nphy(pi); - - } - } else if (pi->mphase_cal_phase_id == - MPHASE_CAL_STATE_IDLE) { - wlc_phy_cal_perical((struct brcms_phy_pub *) pi, - PHY_PERICAL_PHYINIT); - } - } else { - wlc_phy_restorecal_nphy(pi); - } - - wlc_phy_txpwrctrl_coeff_setup_nphy(pi); - - wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); - - wlc_phy_nphy_tkip_rifs_war(pi, pi->sh->_rifs_phy); - - if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LE(pi->pubpi.phy_rev, 6)) - - write_phy_reg(pi, 0x70, 50); - - wlc_phy_txlpfbw_nphy(pi); - - wlc_phy_spurwar_nphy(pi); - -} - -static void wlc_phy_resetcca_nphy(struct brcms_phy *pi) -{ - u16 val; - - wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON); - - val = read_phy_reg(pi, 0x01); - write_phy_reg(pi, 0x01, val | BBCFG_RESETCCA); - udelay(1); - write_phy_reg(pi, 0x01, val & (~BBCFG_RESETCCA)); - - wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF); - - wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); -} - -void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en) -{ - u16 rfctrlintc_override_val; - - if (!en) { - - pi->rfctrlIntc1_save = read_phy_reg(pi, 0x91); - pi->rfctrlIntc2_save = read_phy_reg(pi, 0x92); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - rfctrlintc_override_val = 0x1480; - else if (NREV_GE(pi->pubpi.phy_rev, 3)) - rfctrlintc_override_val = - CHSPEC_IS5G(pi->radio_chanspec) ? 0x600 : 0x480; - else - rfctrlintc_override_val = - CHSPEC_IS5G(pi->radio_chanspec) ? 0x180 : 0x120; - - write_phy_reg(pi, 0x91, rfctrlintc_override_val); - write_phy_reg(pi, 0x92, rfctrlintc_override_val); - } else { - write_phy_reg(pi, 0x91, pi->rfctrlIntc1_save); - write_phy_reg(pi, 0x92, pi->rfctrlIntc2_save); - } - -} - -void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi) -{ - - u16 txrx_chain = - (NPHY_RfseqCoreActv_TxRxChain0 | NPHY_RfseqCoreActv_TxRxChain1); - bool CoreActv_override = false; - - if (pi->nphy_txrx_chain == BRCMS_N_TXRX_CHAIN0) { - txrx_chain = NPHY_RfseqCoreActv_TxRxChain0; - CoreActv_override = true; - - if (NREV_LE(pi->pubpi.phy_rev, 2)) - and_phy_reg(pi, 0xa0, ~0x20); - } else if (pi->nphy_txrx_chain == BRCMS_N_TXRX_CHAIN1) { - txrx_chain = NPHY_RfseqCoreActv_TxRxChain1; - CoreActv_override = true; - - if (NREV_LE(pi->pubpi.phy_rev, 2)) - or_phy_reg(pi, 0xa0, 0x20); - } - - mod_phy_reg(pi, 0xa2, ((0xf << 0) | (0xf << 4)), txrx_chain); - - if (CoreActv_override) { - pi->nphy_perical = PHY_PERICAL_DISABLE; - or_phy_reg(pi, 0xa1, NPHY_RfseqMode_CoreActv_override); - } else { - pi->nphy_perical = PHY_PERICAL_MPHASE; - and_phy_reg(pi, 0xa1, ~NPHY_RfseqMode_CoreActv_override); - } -} - -void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, u8 rxcore_bitmask) -{ - u16 regval; - u16 tbl_buf[16]; - uint i; - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - u16 tbl_opcode; - bool suspend; - - pi->sh->phyrxchain = rxcore_bitmask; - - if (!pi->sh->clk) - return; - - suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!suspend) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - regval = read_phy_reg(pi, 0xa2); - regval &= ~(0xf << 4); - regval |= ((u16) (rxcore_bitmask & 0x3)) << 4; - write_phy_reg(pi, 0xa2, regval); - - if ((rxcore_bitmask & 0x3) != 0x3) { - - write_phy_reg(pi, 0x20e, 1); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - if (pi->rx2tx_biasentry == -1) { - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, - ARRAY_SIZE(tbl_buf), 80, - 16, tbl_buf); - - for (i = 0; i < ARRAY_SIZE(tbl_buf); i++) { - if (tbl_buf[i] == - NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS) { - pi->rx2tx_biasentry = (u8) i; - tbl_opcode = - NPHY_REV3_RFSEQ_CMD_NOP; - wlc_phy_table_write_nphy( - pi, - NPHY_TBL_ID_RFSEQ, - 1, i, - 16, - &tbl_opcode); - break; - } else if (tbl_buf[i] == - NPHY_REV3_RFSEQ_CMD_END) - break; - } - } - } - } else { - - write_phy_reg(pi, 0x20e, 30); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - if (pi->rx2tx_biasentry != -1) { - tbl_opcode = NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, - 1, pi->rx2tx_biasentry, - 16, &tbl_opcode); - pi->rx2tx_biasentry = -1; - } - } - } - - wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); - - if (!suspend) - wlapi_enable_mac(pi->sh->physhim); -} - -u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih) -{ - u16 regval, rxen_bits; - struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); - - regval = read_phy_reg(pi, 0xa2); - rxen_bits = (regval >> 4) & 0xf; - - return (u8) rxen_bits; -} - -bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pi) -{ - return PHY_IPA(pi); -} - -void wlc_phy_cal_init_nphy(struct brcms_phy *pi) -{ -} - -static void wlc_phy_radio_preinit_205x(struct brcms_phy *pi) -{ - - and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU); - and_phy_reg(pi, 0x78, RFCC_OE_POR_FORCE); - - or_phy_reg(pi, 0x78, ~RFCC_OE_POR_FORCE); - or_phy_reg(pi, 0x78, RFCC_CHIP0_PU); - -} - -static void wlc_phy_radio_init_2057(struct brcms_phy *pi) -{ - struct radio_20xx_regs *regs_2057_ptr = NULL; - - if (NREV_IS(pi->pubpi.phy_rev, 7)) { - regs_2057_ptr = regs_2057_rev4; - } else if (NREV_IS(pi->pubpi.phy_rev, 8) - || NREV_IS(pi->pubpi.phy_rev, 9)) { - switch (pi->pubpi.radiorev) { - case 5: - - if (NREV_IS(pi->pubpi.phy_rev, 8)) - regs_2057_ptr = regs_2057_rev5; - else if (NREV_IS(pi->pubpi.phy_rev, 9)) - regs_2057_ptr = regs_2057_rev5v1; - break; - - case 7: - - regs_2057_ptr = regs_2057_rev7; - break; - - case 8: - - regs_2057_ptr = regs_2057_rev8; - break; - - default: - break; - } - } - - wlc_phy_init_radio_regs_allbands(pi, regs_2057_ptr); -} - -static u16 wlc_phy_radio205x_rcal(struct brcms_phy *pi) -{ - u16 rcal_reg = 0; - int i; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - if (pi->pubpi.radiorev == 5) { - - and_phy_reg(pi, 0x342, ~(0x1 << 1)); - - udelay(10); - - mod_radio_reg(pi, RADIO_2057_IQTEST_SEL_PU, 0x1, 0x1); - mod_radio_reg(pi, RADIO_2057v7_IQTEST_SEL_PU2, 0x2, - 0x1); - } - mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x1, 0x1); - - udelay(10); - - mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x3, 0x3); - - for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { - rcal_reg = read_radio_reg(pi, RADIO_2057_RCAL_STATUS); - if (rcal_reg & 0x1) - break; - - udelay(100); - } - - if (WARN(i == MAX_205x_RCAL_WAITLOOPS, - "HW error: radio calib2")) - return 0; - - mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x2, 0x0); - - rcal_reg = read_radio_reg(pi, RADIO_2057_RCAL_STATUS) & 0x3e; - - mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x1, 0x0); - if (pi->pubpi.radiorev == 5) { - - mod_radio_reg(pi, RADIO_2057_IQTEST_SEL_PU, 0x1, 0x0); - mod_radio_reg(pi, RADIO_2057v7_IQTEST_SEL_PU2, 0x2, - 0x0); - } - - if ((pi->pubpi.radiorev <= 4) || (pi->pubpi.radiorev == 6)) { - - mod_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, 0x3c, - rcal_reg); - mod_radio_reg(pi, RADIO_2057_BANDGAP_RCAL_TRIM, 0xf0, - rcal_reg << 2); - } - - } else if (NREV_IS(pi->pubpi.phy_rev, 3)) { - u16 savereg; - - savereg = - read_radio_reg( - pi, - RADIO_2056_SYN_PLL_MAST2 | - RADIO_2056_SYN); - write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN, - savereg | 0x7); - udelay(10); - - write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, - 0x1); - udelay(10); - - write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, - 0x9); - - for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { - rcal_reg = read_radio_reg( - pi, - RADIO_2056_SYN_RCAL_CODE_OUT | - RADIO_2056_SYN); - if (rcal_reg & 0x80) - break; - - udelay(100); - } - - if (WARN(i == MAX_205x_RCAL_WAITLOOPS, - "HW error: radio calib3")) - return 0; - - write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, - 0x1); - - rcal_reg = - read_radio_reg(pi, - RADIO_2056_SYN_RCAL_CODE_OUT | - RADIO_2056_SYN); - - write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, - 0x0); - - write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN, - savereg); - - return rcal_reg & 0x1f; - } - return rcal_reg & 0x3e; -} - -static u16 wlc_phy_radio2057_rccal(struct brcms_phy *pi) -{ - u16 rccal_valid; - int i; - bool chip43226_6362A0; - - chip43226_6362A0 = ((pi->pubpi.radiorev == 3) - || (pi->pubpi.radiorev == 4) - || (pi->pubpi.radiorev == 6)); - - rccal_valid = 0; - if (chip43226_6362A0) { - write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x61); - write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xc0); - } else { - write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x61); - - write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xe9); - } - write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e); - write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55); - - for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { - rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP); - if (rccal_valid & 0x2) - break; - - udelay(500); - } - - write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15); - - rccal_valid = 0; - if (chip43226_6362A0) { - write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x69); - write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xb0); - } else { - write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x69); - - write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xd5); - } - write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e); - write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55); - - for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { - rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP); - if (rccal_valid & 0x2) - break; - - udelay(500); - } - - write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15); - - rccal_valid = 0; - if (chip43226_6362A0) { - write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x73); - - write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x28); - write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xb0); - } else { - write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x73); - write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e); - write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0x99); - } - write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55); - - for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { - rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP); - if (rccal_valid & 0x2) - break; - - udelay(500); - } - - if (WARN(!(rccal_valid & 0x2), "HW error: radio calib4")) - return 0; - - write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15); - - return rccal_valid; -} - -static void wlc_phy_radio_postinit_2057(struct brcms_phy *pi) -{ - - mod_radio_reg(pi, RADIO_2057_XTALPUOVR_PINCTRL, 0x1, 0x1); - - mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x78, 0x78); - mod_radio_reg(pi, RADIO_2057_XTAL_CONFIG2, 0x80, 0x80); - mdelay(2); - mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x78, 0x0); - mod_radio_reg(pi, RADIO_2057_XTAL_CONFIG2, 0x80, 0x0); - - if (pi->phy_init_por) { - wlc_phy_radio205x_rcal(pi); - wlc_phy_radio2057_rccal(pi); - } - - mod_radio_reg(pi, RADIO_2057_RFPLL_MASTER, 0x8, 0x0); -} - -static void wlc_phy_radio_init_2056(struct brcms_phy *pi) -{ - const struct radio_regs *regs_SYN_2056_ptr = NULL; - const struct radio_regs *regs_TX_2056_ptr = NULL; - const struct radio_regs *regs_RX_2056_ptr = NULL; - - if (NREV_IS(pi->pubpi.phy_rev, 3)) { - regs_SYN_2056_ptr = regs_SYN_2056; - regs_TX_2056_ptr = regs_TX_2056; - regs_RX_2056_ptr = regs_RX_2056; - } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { - regs_SYN_2056_ptr = regs_SYN_2056_A1; - regs_TX_2056_ptr = regs_TX_2056_A1; - regs_RX_2056_ptr = regs_RX_2056_A1; - } else { - switch (pi->pubpi.radiorev) { - case 5: - regs_SYN_2056_ptr = regs_SYN_2056_rev5; - regs_TX_2056_ptr = regs_TX_2056_rev5; - regs_RX_2056_ptr = regs_RX_2056_rev5; - break; - - case 6: - regs_SYN_2056_ptr = regs_SYN_2056_rev6; - regs_TX_2056_ptr = regs_TX_2056_rev6; - regs_RX_2056_ptr = regs_RX_2056_rev6; - break; - - case 7: - case 9: - regs_SYN_2056_ptr = regs_SYN_2056_rev7; - regs_TX_2056_ptr = regs_TX_2056_rev7; - regs_RX_2056_ptr = regs_RX_2056_rev7; - break; - - case 8: - regs_SYN_2056_ptr = regs_SYN_2056_rev8; - regs_TX_2056_ptr = regs_TX_2056_rev8; - regs_RX_2056_ptr = regs_RX_2056_rev8; - break; - - case 11: - regs_SYN_2056_ptr = regs_SYN_2056_rev11; - regs_TX_2056_ptr = regs_TX_2056_rev11; - regs_RX_2056_ptr = regs_RX_2056_rev11; - break; - - default: - break; - } - } - - wlc_phy_init_radio_regs(pi, regs_SYN_2056_ptr, (u16) RADIO_2056_SYN); - - wlc_phy_init_radio_regs(pi, regs_TX_2056_ptr, (u16) RADIO_2056_TX0); - - wlc_phy_init_radio_regs(pi, regs_TX_2056_ptr, (u16) RADIO_2056_TX1); - - wlc_phy_init_radio_regs(pi, regs_RX_2056_ptr, (u16) RADIO_2056_RX0); - - wlc_phy_init_radio_regs(pi, regs_RX_2056_ptr, (u16) RADIO_2056_RX1); -} - -static void wlc_phy_radio_postinit_2056(struct brcms_phy *pi) -{ - mod_radio_reg(pi, RADIO_2056_SYN_COM_CTRL, 0xb, 0xb); - - mod_radio_reg(pi, RADIO_2056_SYN_COM_PU, 0x2, 0x2); - mod_radio_reg(pi, RADIO_2056_SYN_COM_RESET, 0x2, 0x2); - udelay(1000); - mod_radio_reg(pi, RADIO_2056_SYN_COM_RESET, 0x2, 0x0); - - if ((pi->sh->boardflags2 & BFL2_LEGACY) - || (pi->sh->boardflags2 & BFL2_XTALBUFOUTEN)) - mod_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2, 0xf4, 0x0); - else - mod_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2, 0xfc, 0x0); - - mod_radio_reg(pi, RADIO_2056_SYN_RCCAL_CTRL0, 0x1, 0x0); - - if (pi->phy_init_por) - wlc_phy_radio205x_rcal(pi); -} - -static void wlc_phy_radio_preinit_2055(struct brcms_phy *pi) -{ - - and_phy_reg(pi, 0x78, ~RFCC_POR_FORCE); - or_phy_reg(pi, 0x78, RFCC_CHIP0_PU | RFCC_OE_POR_FORCE); - - or_phy_reg(pi, 0x78, RFCC_POR_FORCE); -} - -static void wlc_phy_radio_init_2055(struct brcms_phy *pi) -{ - wlc_phy_init_radio_regs(pi, regs_2055, RADIO_DEFAULT_CORE); -} - -static void wlc_phy_radio_postinit_2055(struct brcms_phy *pi) -{ - - and_radio_reg(pi, RADIO_2055_MASTER_CNTRL1, - ~(RADIO_2055_JTAGCTRL_MASK | RADIO_2055_JTAGSYNC_MASK)); - - if (((pi->sh->sromrev >= 4) - && !(pi->sh->boardflags2 & BFL2_RXBB_INT_REG_DIS)) - || ((pi->sh->sromrev < 4))) { - and_radio_reg(pi, RADIO_2055_CORE1_RXBB_REGULATOR, 0x7F); - and_radio_reg(pi, RADIO_2055_CORE2_RXBB_REGULATOR, 0x7F); - } - - mod_radio_reg(pi, RADIO_2055_RRCCAL_N_OPT_SEL, 0x3F, 0x2C); - write_radio_reg(pi, RADIO_2055_CAL_MISC, 0x3C); - - and_radio_reg(pi, RADIO_2055_CAL_MISC, - ~(RADIO_2055_RRCAL_START | RADIO_2055_RRCAL_RST_N)); - - or_radio_reg(pi, RADIO_2055_CAL_LPO_CNTRL, RADIO_2055_CAL_LPO_ENABLE); - - or_radio_reg(pi, RADIO_2055_CAL_MISC, RADIO_2055_RRCAL_RST_N); - - udelay(1000); - - or_radio_reg(pi, RADIO_2055_CAL_MISC, RADIO_2055_RRCAL_START); - - SPINWAIT(((read_radio_reg(pi, RADIO_2055_CAL_COUNTER_OUT2) & - RADIO_2055_RCAL_DONE) != RADIO_2055_RCAL_DONE), 2000); - - if (WARN((read_radio_reg(pi, RADIO_2055_CAL_COUNTER_OUT2) & - RADIO_2055_RCAL_DONE) != RADIO_2055_RCAL_DONE, - "HW error: radio calibration1\n")) - return; - - and_radio_reg(pi, RADIO_2055_CAL_LPO_CNTRL, - ~(RADIO_2055_CAL_LPO_ENABLE)); - - wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, pi->radio_chanspec); - - write_radio_reg(pi, RADIO_2055_CORE1_RXBB_LPF, 9); - write_radio_reg(pi, RADIO_2055_CORE2_RXBB_LPF, 9); - - write_radio_reg(pi, RADIO_2055_CORE1_RXBB_MIDAC_HIPAS, 0x83); - write_radio_reg(pi, RADIO_2055_CORE2_RXBB_MIDAC_HIPAS, 0x83); - - mod_radio_reg(pi, RADIO_2055_CORE1_LNA_GAINBST, - RADIO_2055_GAINBST_VAL_MASK, RADIO_2055_GAINBST_CODE); - mod_radio_reg(pi, RADIO_2055_CORE2_LNA_GAINBST, - RADIO_2055_GAINBST_VAL_MASK, RADIO_2055_GAINBST_CODE); - if (pi->nphy_gain_boost) { - and_radio_reg(pi, RADIO_2055_CORE1_RXRF_SPC1, - ~(RADIO_2055_GAINBST_DISABLE)); - and_radio_reg(pi, RADIO_2055_CORE2_RXRF_SPC1, - ~(RADIO_2055_GAINBST_DISABLE)); - } else { - or_radio_reg(pi, RADIO_2055_CORE1_RXRF_SPC1, - RADIO_2055_GAINBST_DISABLE); - or_radio_reg(pi, RADIO_2055_CORE2_RXRF_SPC1, - RADIO_2055_GAINBST_DISABLE); - } - - udelay(2); -} - -void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on) -{ - if (on) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if (!pi->radio_is_on) { - wlc_phy_radio_preinit_205x(pi); - wlc_phy_radio_init_2057(pi); - wlc_phy_radio_postinit_2057(pi); - } - - wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, - pi->radio_chanspec); - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - wlc_phy_radio_preinit_205x(pi); - wlc_phy_radio_init_2056(pi); - wlc_phy_radio_postinit_2056(pi); - - wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, - pi->radio_chanspec); - } else { - wlc_phy_radio_preinit_2055(pi); - wlc_phy_radio_init_2055(pi); - wlc_phy_radio_postinit_2055(pi); - } - - pi->radio_is_on = true; - - } else { - - if (NREV_GE(pi->pubpi.phy_rev, 3) - && NREV_LT(pi->pubpi.phy_rev, 7)) { - and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU); - mod_radio_reg(pi, RADIO_2056_SYN_COM_PU, 0x2, 0x0); - - write_radio_reg(pi, - RADIO_2056_TX_PADA_BOOST_TUNE | - RADIO_2056_TX0, 0); - write_radio_reg(pi, - RADIO_2056_TX_PADG_BOOST_TUNE | - RADIO_2056_TX0, 0); - write_radio_reg(pi, - RADIO_2056_TX_PGAA_BOOST_TUNE | - RADIO_2056_TX0, 0); - write_radio_reg(pi, - RADIO_2056_TX_PGAG_BOOST_TUNE | - RADIO_2056_TX0, 0); - mod_radio_reg(pi, - RADIO_2056_TX_MIXA_BOOST_TUNE | - RADIO_2056_TX0, 0xf0, 0); - write_radio_reg(pi, - RADIO_2056_TX_MIXG_BOOST_TUNE | - RADIO_2056_TX0, 0); - - write_radio_reg(pi, - RADIO_2056_TX_PADA_BOOST_TUNE | - RADIO_2056_TX1, 0); - write_radio_reg(pi, - RADIO_2056_TX_PADG_BOOST_TUNE | - RADIO_2056_TX1, 0); - write_radio_reg(pi, - RADIO_2056_TX_PGAA_BOOST_TUNE | - RADIO_2056_TX1, 0); - write_radio_reg(pi, - RADIO_2056_TX_PGAG_BOOST_TUNE | - RADIO_2056_TX1, 0); - mod_radio_reg(pi, - RADIO_2056_TX_MIXA_BOOST_TUNE | - RADIO_2056_TX1, 0xf0, 0); - write_radio_reg(pi, - RADIO_2056_TX_MIXG_BOOST_TUNE | - RADIO_2056_TX1, 0); - - pi->radio_is_on = false; - } - - if (NREV_GE(pi->pubpi.phy_rev, 8)) { - and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU); - pi->radio_is_on = false; - } - - } -} - -static bool -wlc_phy_chan2freq_nphy(struct brcms_phy *pi, uint channel, int *f, - const struct chan_info_nphy_radio2057 **t0, - const struct chan_info_nphy_radio205x **t1, - const struct chan_info_nphy_radio2057_rev5 **t2, - const struct chan_info_nphy_2055 **t3) -{ - uint i; - const struct chan_info_nphy_radio2057 *chan_info_tbl_p_0 = NULL; - const struct chan_info_nphy_radio205x *chan_info_tbl_p_1 = NULL; - const struct chan_info_nphy_radio2057_rev5 *chan_info_tbl_p_2 = NULL; - u32 tbl_len = 0; - - int freq = 0; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - if (NREV_IS(pi->pubpi.phy_rev, 7)) { - - chan_info_tbl_p_0 = chan_info_nphyrev7_2057_rev4; - tbl_len = ARRAY_SIZE(chan_info_nphyrev7_2057_rev4); - - } else if (NREV_IS(pi->pubpi.phy_rev, 8) - || NREV_IS(pi->pubpi.phy_rev, 9)) { - switch (pi->pubpi.radiorev) { - - case 5: - - if (pi->pubpi.radiover == 0x0) { - - chan_info_tbl_p_2 = - chan_info_nphyrev8_2057_rev5; - tbl_len = ARRAY_SIZE( - chan_info_nphyrev8_2057_rev5); - - } else if (pi->pubpi.radiover == 0x1) { - - chan_info_tbl_p_2 = - chan_info_nphyrev9_2057_rev5v1; - tbl_len = ARRAY_SIZE( - chan_info_nphyrev9_2057_rev5v1); - - } - break; - - case 7: - chan_info_tbl_p_0 = - chan_info_nphyrev8_2057_rev7; - tbl_len = ARRAY_SIZE( - chan_info_nphyrev8_2057_rev7); - break; - - case 8: - chan_info_tbl_p_0 = - chan_info_nphyrev8_2057_rev8; - tbl_len = ARRAY_SIZE( - chan_info_nphyrev8_2057_rev8); - break; - - default: - break; - } - } else if (NREV_IS(pi->pubpi.phy_rev, 16)) { - - chan_info_tbl_p_0 = chan_info_nphyrev8_2057_rev8; - tbl_len = ARRAY_SIZE(chan_info_nphyrev8_2057_rev8); - } else { - goto fail; - } - - for (i = 0; i < tbl_len; i++) { - if (pi->pubpi.radiorev == 5) { - - if (chan_info_tbl_p_2[i].chan == channel) - break; - } else { - - if (chan_info_tbl_p_0[i].chan == channel) - break; - } - } - - if (i >= tbl_len) - goto fail; - - if (pi->pubpi.radiorev == 5) { - *t2 = &chan_info_tbl_p_2[i]; - freq = chan_info_tbl_p_2[i].freq; - } else { - *t0 = &chan_info_tbl_p_0[i]; - freq = chan_info_tbl_p_0[i].freq; - } - - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - if (NREV_IS(pi->pubpi.phy_rev, 3)) { - chan_info_tbl_p_1 = chan_info_nphyrev3_2056; - tbl_len = ARRAY_SIZE(chan_info_nphyrev3_2056); - } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { - chan_info_tbl_p_1 = chan_info_nphyrev4_2056_A1; - tbl_len = ARRAY_SIZE(chan_info_nphyrev4_2056_A1); - } else if (NREV_IS(pi->pubpi.phy_rev, 5) - || NREV_IS(pi->pubpi.phy_rev, 6)) { - switch (pi->pubpi.radiorev) { - case 5: - chan_info_tbl_p_1 = chan_info_nphyrev5_2056v5; - tbl_len = ARRAY_SIZE(chan_info_nphyrev5_2056v5); - break; - case 6: - chan_info_tbl_p_1 = chan_info_nphyrev6_2056v6; - tbl_len = ARRAY_SIZE(chan_info_nphyrev6_2056v6); - break; - case 7: - case 9: - chan_info_tbl_p_1 = chan_info_nphyrev5n6_2056v7; - tbl_len = - ARRAY_SIZE(chan_info_nphyrev5n6_2056v7); - break; - case 8: - chan_info_tbl_p_1 = chan_info_nphyrev6_2056v8; - tbl_len = ARRAY_SIZE(chan_info_nphyrev6_2056v8); - break; - case 11: - chan_info_tbl_p_1 = chan_info_nphyrev6_2056v11; - tbl_len = ARRAY_SIZE( - chan_info_nphyrev6_2056v11); - break; - default: - break; - } - } - - for (i = 0; i < tbl_len; i++) { - if (chan_info_tbl_p_1[i].chan == channel) - break; - } - - if (i >= tbl_len) - goto fail; - - *t1 = &chan_info_tbl_p_1[i]; - freq = chan_info_tbl_p_1[i].freq; - - } else { - for (i = 0; i < ARRAY_SIZE(chan_info_nphy_2055); i++) - if (chan_info_nphy_2055[i].chan == channel) - break; - - if (i >= ARRAY_SIZE(chan_info_nphy_2055)) - goto fail; - - *t3 = &chan_info_nphy_2055[i]; - freq = chan_info_nphy_2055[i].freq; - } - - *f = freq; - return true; - -fail: - *f = WL_CHAN_FREQ_RANGE_2G; - return false; -} - -u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint channel) -{ - int freq; - const struct chan_info_nphy_radio2057 *t0 = NULL; - const struct chan_info_nphy_radio205x *t1 = NULL; - const struct chan_info_nphy_radio2057_rev5 *t2 = NULL; - const struct chan_info_nphy_2055 *t3 = NULL; - - if (channel == 0) - channel = CHSPEC_CHANNEL(pi->radio_chanspec); - - wlc_phy_chan2freq_nphy(pi, channel, &freq, &t0, &t1, &t2, &t3); - - if (CHSPEC_IS2G(pi->radio_chanspec)) - return WL_CHAN_FREQ_RANGE_2G; - - if ((freq >= BASE_LOW_5G_CHAN) && (freq < BASE_MID_5G_CHAN)) - return WL_CHAN_FREQ_RANGE_5GL; - else if ((freq >= BASE_MID_5G_CHAN) && (freq < BASE_HIGH_5G_CHAN)) - return WL_CHAN_FREQ_RANGE_5GM; - else - return WL_CHAN_FREQ_RANGE_5GH; -} - -static void -wlc_phy_chanspec_radio2055_setup(struct brcms_phy *pi, - const struct chan_info_nphy_2055 *ci) -{ - - write_radio_reg(pi, RADIO_2055_PLL_REF, ci->RF_pll_ref); - write_radio_reg(pi, RADIO_2055_RF_PLL_MOD0, ci->RF_rf_pll_mod0); - write_radio_reg(pi, RADIO_2055_RF_PLL_MOD1, ci->RF_rf_pll_mod1); - write_radio_reg(pi, RADIO_2055_VCO_CAP_TAIL, ci->RF_vco_cap_tail); - - BRCMS_PHY_WAR_PR51571(pi); - - write_radio_reg(pi, RADIO_2055_VCO_CAL1, ci->RF_vco_cal1); - write_radio_reg(pi, RADIO_2055_VCO_CAL2, ci->RF_vco_cal2); - write_radio_reg(pi, RADIO_2055_PLL_LF_C1, ci->RF_pll_lf_c1); - write_radio_reg(pi, RADIO_2055_PLL_LF_R1, ci->RF_pll_lf_r1); - - BRCMS_PHY_WAR_PR51571(pi); - - write_radio_reg(pi, RADIO_2055_PLL_LF_C2, ci->RF_pll_lf_c2); - write_radio_reg(pi, RADIO_2055_LGBUF_CEN_BUF, ci->RF_lgbuf_cen_buf); - write_radio_reg(pi, RADIO_2055_LGEN_TUNE1, ci->RF_lgen_tune1); - write_radio_reg(pi, RADIO_2055_LGEN_TUNE2, ci->RF_lgen_tune2); - - BRCMS_PHY_WAR_PR51571(pi); - - write_radio_reg(pi, RADIO_2055_CORE1_LGBUF_A_TUNE, - ci->RF_core1_lgbuf_a_tune); - write_radio_reg(pi, RADIO_2055_CORE1_LGBUF_G_TUNE, - ci->RF_core1_lgbuf_g_tune); - write_radio_reg(pi, RADIO_2055_CORE1_RXRF_REG1, ci->RF_core1_rxrf_reg1); - write_radio_reg(pi, RADIO_2055_CORE1_TX_PGA_PAD_TN, - ci->RF_core1_tx_pga_pad_tn); - - BRCMS_PHY_WAR_PR51571(pi); - - write_radio_reg(pi, RADIO_2055_CORE1_TX_MX_BGTRIM, - ci->RF_core1_tx_mx_bgtrim); - write_radio_reg(pi, RADIO_2055_CORE2_LGBUF_A_TUNE, - ci->RF_core2_lgbuf_a_tune); - write_radio_reg(pi, RADIO_2055_CORE2_LGBUF_G_TUNE, - ci->RF_core2_lgbuf_g_tune); - write_radio_reg(pi, RADIO_2055_CORE2_RXRF_REG1, ci->RF_core2_rxrf_reg1); - - BRCMS_PHY_WAR_PR51571(pi); - - write_radio_reg(pi, RADIO_2055_CORE2_TX_PGA_PAD_TN, - ci->RF_core2_tx_pga_pad_tn); - write_radio_reg(pi, RADIO_2055_CORE2_TX_MX_BGTRIM, - ci->RF_core2_tx_mx_bgtrim); - - udelay(50); - - write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x05); - write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x45); - - BRCMS_PHY_WAR_PR51571(pi); - - write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x65); - - udelay(300); -} - -static void -wlc_phy_chanspec_radio2056_setup(struct brcms_phy *pi, - const struct chan_info_nphy_radio205x *ci) -{ - const struct radio_regs *regs_SYN_2056_ptr = NULL; - - write_radio_reg(pi, - RADIO_2056_SYN_PLL_VCOCAL1 | RADIO_2056_SYN, - ci->RF_SYN_pll_vcocal1); - write_radio_reg(pi, RADIO_2056_SYN_PLL_VCOCAL2 | RADIO_2056_SYN, - ci->RF_SYN_pll_vcocal2); - write_radio_reg(pi, RADIO_2056_SYN_PLL_REFDIV | RADIO_2056_SYN, - ci->RF_SYN_pll_refdiv); - write_radio_reg(pi, RADIO_2056_SYN_PLL_MMD2 | RADIO_2056_SYN, - ci->RF_SYN_pll_mmd2); - write_radio_reg(pi, RADIO_2056_SYN_PLL_MMD1 | RADIO_2056_SYN, - ci->RF_SYN_pll_mmd1); - write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER1 | RADIO_2056_SYN, - ci->RF_SYN_pll_loopfilter1); - write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER2 | RADIO_2056_SYN, - ci->RF_SYN_pll_loopfilter2); - write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER3 | RADIO_2056_SYN, - ci->RF_SYN_pll_loopfilter3); - write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER4 | RADIO_2056_SYN, - ci->RF_SYN_pll_loopfilter4); - write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER5 | RADIO_2056_SYN, - ci->RF_SYN_pll_loopfilter5); - write_radio_reg(pi, RADIO_2056_SYN_RESERVED_ADDR27 | RADIO_2056_SYN, - ci->RF_SYN_reserved_addr27); - write_radio_reg(pi, RADIO_2056_SYN_RESERVED_ADDR28 | RADIO_2056_SYN, - ci->RF_SYN_reserved_addr28); - write_radio_reg(pi, RADIO_2056_SYN_RESERVED_ADDR29 | RADIO_2056_SYN, - ci->RF_SYN_reserved_addr29); - write_radio_reg(pi, RADIO_2056_SYN_LOGEN_VCOBUF1 | RADIO_2056_SYN, - ci->RF_SYN_logen_VCOBUF1); - write_radio_reg(pi, RADIO_2056_SYN_LOGEN_MIXER2 | RADIO_2056_SYN, - ci->RF_SYN_logen_MIXER2); - write_radio_reg(pi, RADIO_2056_SYN_LOGEN_BUF3 | RADIO_2056_SYN, - ci->RF_SYN_logen_BUF3); - write_radio_reg(pi, RADIO_2056_SYN_LOGEN_BUF4 | RADIO_2056_SYN, - ci->RF_SYN_logen_BUF4); - - write_radio_reg(pi, - RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX0, - ci->RF_RX0_lnaa_tune); - write_radio_reg(pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX0, - ci->RF_RX0_lnag_tune); - write_radio_reg(pi, RADIO_2056_TX_INTPAA_BOOST_TUNE | RADIO_2056_TX0, - ci->RF_TX0_intpaa_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_INTPAG_BOOST_TUNE | RADIO_2056_TX0, - ci->RF_TX0_intpag_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_PADA_BOOST_TUNE | RADIO_2056_TX0, - ci->RF_TX0_pada_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_PADG_BOOST_TUNE | RADIO_2056_TX0, - ci->RF_TX0_padg_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_PGAA_BOOST_TUNE | RADIO_2056_TX0, - ci->RF_TX0_pgaa_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_PGAG_BOOST_TUNE | RADIO_2056_TX0, - ci->RF_TX0_pgag_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_MIXA_BOOST_TUNE | RADIO_2056_TX0, - ci->RF_TX0_mixa_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_MIXG_BOOST_TUNE | RADIO_2056_TX0, - ci->RF_TX0_mixg_boost_tune); - - write_radio_reg(pi, - RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX1, - ci->RF_RX1_lnaa_tune); - write_radio_reg(pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX1, - ci->RF_RX1_lnag_tune); - write_radio_reg(pi, RADIO_2056_TX_INTPAA_BOOST_TUNE | RADIO_2056_TX1, - ci->RF_TX1_intpaa_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_INTPAG_BOOST_TUNE | RADIO_2056_TX1, - ci->RF_TX1_intpag_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_PADA_BOOST_TUNE | RADIO_2056_TX1, - ci->RF_TX1_pada_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_PADG_BOOST_TUNE | RADIO_2056_TX1, - ci->RF_TX1_padg_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_PGAA_BOOST_TUNE | RADIO_2056_TX1, - ci->RF_TX1_pgaa_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_PGAG_BOOST_TUNE | RADIO_2056_TX1, - ci->RF_TX1_pgag_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_MIXA_BOOST_TUNE | RADIO_2056_TX1, - ci->RF_TX1_mixa_boost_tune); - write_radio_reg(pi, RADIO_2056_TX_MIXG_BOOST_TUNE | RADIO_2056_TX1, - ci->RF_TX1_mixg_boost_tune); - - if (NREV_IS(pi->pubpi.phy_rev, 3)) - regs_SYN_2056_ptr = regs_SYN_2056; - else if (NREV_IS(pi->pubpi.phy_rev, 4)) - regs_SYN_2056_ptr = regs_SYN_2056_A1; - else { - switch (pi->pubpi.radiorev) { - case 5: - regs_SYN_2056_ptr = regs_SYN_2056_rev5; - break; - case 6: - regs_SYN_2056_ptr = regs_SYN_2056_rev6; - break; - case 7: - case 9: - regs_SYN_2056_ptr = regs_SYN_2056_rev7; - break; - case 8: - regs_SYN_2056_ptr = regs_SYN_2056_rev8; - break; - case 11: - regs_SYN_2056_ptr = regs_SYN_2056_rev11; - break; - } - } - if (CHSPEC_IS2G(pi->radio_chanspec)) - write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | - RADIO_2056_SYN, - (u16) regs_SYN_2056_ptr[0x49 - 2].init_g); - else - write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | - RADIO_2056_SYN, - (u16) regs_SYN_2056_ptr[0x49 - 2].init_a); - - if (pi->sh->boardflags2 & BFL2_GPLL_WAR) { - if (CHSPEC_IS2G(pi->radio_chanspec)) { - write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER1 | - RADIO_2056_SYN, 0x1f); - write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER2 | - RADIO_2056_SYN, 0x1f); - - if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || - (pi->sh->chip == BCMA_CHIP_ID_BCM47162)) { - write_radio_reg(pi, - RADIO_2056_SYN_PLL_LOOPFILTER4 | - RADIO_2056_SYN, 0x14); - write_radio_reg(pi, - RADIO_2056_SYN_PLL_CP2 | - RADIO_2056_SYN, 0x00); - } else { - write_radio_reg(pi, - RADIO_2056_SYN_PLL_LOOPFILTER4 | - RADIO_2056_SYN, 0xb); - write_radio_reg(pi, - RADIO_2056_SYN_PLL_CP2 | - RADIO_2056_SYN, 0x14); - } - } - } - - if ((pi->sh->boardflags2 & BFL2_GPLL_WAR2) && - (CHSPEC_IS2G(pi->radio_chanspec))) { - write_radio_reg(pi, - RADIO_2056_SYN_PLL_LOOPFILTER1 | RADIO_2056_SYN, - 0x1f); - write_radio_reg(pi, - RADIO_2056_SYN_PLL_LOOPFILTER2 | RADIO_2056_SYN, - 0x1f); - write_radio_reg(pi, - RADIO_2056_SYN_PLL_LOOPFILTER4 | RADIO_2056_SYN, - 0xb); - write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | RADIO_2056_SYN, - 0x20); - } - - if (pi->sh->boardflags2 & BFL2_APLL_WAR) { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER1 | - RADIO_2056_SYN, 0x1f); - write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER2 | - RADIO_2056_SYN, 0x1f); - write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER4 | - RADIO_2056_SYN, 0x5); - write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | - RADIO_2056_SYN, 0xc); - } - } - - if (PHY_IPA(pi) && CHSPEC_IS2G(pi->radio_chanspec)) { - u16 pag_boost_tune; - u16 padg_boost_tune; - u16 pgag_boost_tune; - u16 mixg_boost_tune; - u16 bias, cascbias; - uint core; - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - - if (NREV_GE(pi->pubpi.phy_rev, 5)) { - - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - PADG_IDAC, 0xcc); - - if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || - (pi->sh->chip == BCMA_CHIP_ID_BCM47162)) { - bias = 0x40; - cascbias = 0x45; - pag_boost_tune = 0x5; - pgag_boost_tune = 0x33; - padg_boost_tune = 0x77; - mixg_boost_tune = 0x55; - } else { - bias = 0x25; - cascbias = 0x20; - - if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224 || - pi->sh->chip == BCMA_CHIP_ID_BCM43225) && - pi->sh->chippkg == BCMA_PKG_ID_BCM43224_FAB_SMIC) { - bias = 0x2a; - cascbias = 0x38; - } - - pag_boost_tune = 0x4; - pgag_boost_tune = 0x03; - padg_boost_tune = 0x77; - mixg_boost_tune = 0x65; - } - - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAG_IMAIN_STAT, bias); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAG_IAUX_STAT, bias); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAG_CASCBIAS, cascbias); - - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAG_BOOST_TUNE, - pag_boost_tune); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - PGAG_BOOST_TUNE, - pgag_boost_tune); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - PADG_BOOST_TUNE, - padg_boost_tune); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - MIXG_BOOST_TUNE, - mixg_boost_tune); - } else { - - bias = (pi->bw == WL_CHANSPEC_BW_40) ? - 0x40 : 0x20; - - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAG_IMAIN_STAT, bias); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAG_IAUX_STAT, bias); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAG_CASCBIAS, 0x30); - } - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, PA_SPARE1, - 0xee); - } - } - - if (PHY_IPA(pi) && NREV_IS(pi->pubpi.phy_rev, 6) - && CHSPEC_IS5G(pi->radio_chanspec)) { - u16 paa_boost_tune; - u16 pada_boost_tune; - u16 pgaa_boost_tune; - u16 mixa_boost_tune; - u16 freq, pabias, cascbias; - uint core; - - freq = CHAN5G_FREQ(CHSPEC_CHANNEL(pi->radio_chanspec)); - - if (freq < 5150) { - - paa_boost_tune = 0xa; - pada_boost_tune = 0x77; - pgaa_boost_tune = 0xf; - mixa_boost_tune = 0xf; - } else if (freq < 5340) { - - paa_boost_tune = 0x8; - pada_boost_tune = 0x77; - pgaa_boost_tune = 0xfb; - mixa_boost_tune = 0xf; - } else if (freq < 5650) { - - paa_boost_tune = 0x0; - pada_boost_tune = 0x77; - pgaa_boost_tune = 0xb; - mixa_boost_tune = 0xf; - } else { - - paa_boost_tune = 0x0; - pada_boost_tune = 0x77; - if (freq != 5825) - pgaa_boost_tune = -(int)(freq - 18) / 36 + 168; - else - pgaa_boost_tune = 6; - - mixa_boost_tune = 0xf; - } - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAA_BOOST_TUNE, paa_boost_tune); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - PADA_BOOST_TUNE, pada_boost_tune); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - PGAA_BOOST_TUNE, pgaa_boost_tune); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - MIXA_BOOST_TUNE, mixa_boost_tune); - - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - TXSPARE1, 0x30); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - PA_SPARE2, 0xee); - - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - PADA_CASCBIAS, 0x3); - - cascbias = 0x30; - - if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224 || - pi->sh->chip == BCMA_CHIP_ID_BCM43225) && - pi->sh->chippkg == BCMA_PKG_ID_BCM43224_FAB_SMIC) - cascbias = 0x35; - - pabias = (pi->phy_pabias == 0) ? 0x30 : pi->phy_pabias; - - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAA_IAUX_STAT, pabias); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAA_IMAIN_STAT, pabias); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAA_CASCBIAS, cascbias); - } - } - - udelay(50); - - wlc_phy_radio205x_vcocal_nphy(pi); -} - -void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi) -{ - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_EN, 0x01, 0x0); - mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x04, 0x0); - mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x04, - (1 << 2)); - mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_EN, 0x01, 0x01); - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - write_radio_reg(pi, RADIO_2056_SYN_PLL_VCOCAL12, 0x0); - write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x38); - write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x18); - write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x38); - write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x39); - } - - udelay(300); -} - -static void -wlc_phy_chanspec_radio2057_setup( - struct brcms_phy *pi, - const struct chan_info_nphy_radio2057 *ci, - const struct chan_info_nphy_radio2057_rev5 * - ci2) -{ - int coreNum; - u16 txmix2g_tune_boost_pu = 0; - u16 pad2g_tune_pus = 0; - - if (pi->pubpi.radiorev == 5) { - - write_radio_reg(pi, - RADIO_2057_VCOCAL_COUNTVAL0, - ci2->RF_vcocal_countval0); - write_radio_reg(pi, RADIO_2057_VCOCAL_COUNTVAL1, - ci2->RF_vcocal_countval1); - write_radio_reg(pi, RADIO_2057_RFPLL_REFMASTER_SPAREXTALSIZE, - ci2->RF_rfpll_refmaster_sparextalsize); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, - ci2->RF_rfpll_loopfilter_r1); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, - ci2->RF_rfpll_loopfilter_c2); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, - ci2->RF_rfpll_loopfilter_c1); - write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, - ci2->RF_cp_kpd_idac); - write_radio_reg(pi, RADIO_2057_RFPLL_MMD0, ci2->RF_rfpll_mmd0); - write_radio_reg(pi, RADIO_2057_RFPLL_MMD1, ci2->RF_rfpll_mmd1); - write_radio_reg(pi, - RADIO_2057_VCOBUF_TUNE, ci2->RF_vcobuf_tune); - write_radio_reg(pi, - RADIO_2057_LOGEN_MX2G_TUNE, - ci2->RF_logen_mx2g_tune); - write_radio_reg(pi, RADIO_2057_LOGEN_INDBUF2G_TUNE, - ci2->RF_logen_indbuf2g_tune); - - write_radio_reg(pi, - RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE0, - ci2->RF_txmix2g_tune_boost_pu_core0); - write_radio_reg(pi, - RADIO_2057_PAD2G_TUNE_PUS_CORE0, - ci2->RF_pad2g_tune_pus_core0); - write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE0, - ci2->RF_lna2g_tune_core0); - - write_radio_reg(pi, - RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE1, - ci2->RF_txmix2g_tune_boost_pu_core1); - write_radio_reg(pi, - RADIO_2057_PAD2G_TUNE_PUS_CORE1, - ci2->RF_pad2g_tune_pus_core1); - write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE1, - ci2->RF_lna2g_tune_core1); - - } else { - - write_radio_reg(pi, - RADIO_2057_VCOCAL_COUNTVAL0, - ci->RF_vcocal_countval0); - write_radio_reg(pi, RADIO_2057_VCOCAL_COUNTVAL1, - ci->RF_vcocal_countval1); - write_radio_reg(pi, RADIO_2057_RFPLL_REFMASTER_SPAREXTALSIZE, - ci->RF_rfpll_refmaster_sparextalsize); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, - ci->RF_rfpll_loopfilter_r1); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, - ci->RF_rfpll_loopfilter_c2); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, - ci->RF_rfpll_loopfilter_c1); - write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, ci->RF_cp_kpd_idac); - write_radio_reg(pi, RADIO_2057_RFPLL_MMD0, ci->RF_rfpll_mmd0); - write_radio_reg(pi, RADIO_2057_RFPLL_MMD1, ci->RF_rfpll_mmd1); - write_radio_reg(pi, RADIO_2057_VCOBUF_TUNE, ci->RF_vcobuf_tune); - write_radio_reg(pi, - RADIO_2057_LOGEN_MX2G_TUNE, - ci->RF_logen_mx2g_tune); - write_radio_reg(pi, RADIO_2057_LOGEN_MX5G_TUNE, - ci->RF_logen_mx5g_tune); - write_radio_reg(pi, RADIO_2057_LOGEN_INDBUF2G_TUNE, - ci->RF_logen_indbuf2g_tune); - write_radio_reg(pi, RADIO_2057_LOGEN_INDBUF5G_TUNE, - ci->RF_logen_indbuf5g_tune); - - write_radio_reg(pi, - RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE0, - ci->RF_txmix2g_tune_boost_pu_core0); - write_radio_reg(pi, - RADIO_2057_PAD2G_TUNE_PUS_CORE0, - ci->RF_pad2g_tune_pus_core0); - write_radio_reg(pi, RADIO_2057_PGA_BOOST_TUNE_CORE0, - ci->RF_pga_boost_tune_core0); - write_radio_reg(pi, RADIO_2057_TXMIX5G_BOOST_TUNE_CORE0, - ci->RF_txmix5g_boost_tune_core0); - write_radio_reg(pi, RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE0, - ci->RF_pad5g_tune_misc_pus_core0); - write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE0, - ci->RF_lna2g_tune_core0); - write_radio_reg(pi, RADIO_2057_LNA5G_TUNE_CORE0, - ci->RF_lna5g_tune_core0); - - write_radio_reg(pi, - RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE1, - ci->RF_txmix2g_tune_boost_pu_core1); - write_radio_reg(pi, - RADIO_2057_PAD2G_TUNE_PUS_CORE1, - ci->RF_pad2g_tune_pus_core1); - write_radio_reg(pi, RADIO_2057_PGA_BOOST_TUNE_CORE1, - ci->RF_pga_boost_tune_core1); - write_radio_reg(pi, RADIO_2057_TXMIX5G_BOOST_TUNE_CORE1, - ci->RF_txmix5g_boost_tune_core1); - write_radio_reg(pi, RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE1, - ci->RF_pad5g_tune_misc_pus_core1); - write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE1, - ci->RF_lna2g_tune_core1); - write_radio_reg(pi, RADIO_2057_LNA5G_TUNE_CORE1, - ci->RF_lna5g_tune_core1); - } - - if ((pi->pubpi.radiorev <= 4) || (pi->pubpi.radiorev == 6)) { - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, - 0x3f); - write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x3f); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, - 0x8); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, - 0x8); - } else { - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, - 0x1f); - write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x3f); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, - 0x8); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, - 0x8); - } - } else if ((pi->pubpi.radiorev == 5) || (pi->pubpi.radiorev == 7) || - (pi->pubpi.radiorev == 8)) { - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, - 0x1b); - write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x30); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, - 0xa); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, - 0xa); - } else { - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, - 0x1f); - write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x3f); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, - 0x8); - write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, - 0x8); - } - - } - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if (PHY_IPA(pi)) { - if (pi->pubpi.radiorev == 3) - txmix2g_tune_boost_pu = 0x6b; - - if (pi->pubpi.radiorev == 5) - pad2g_tune_pus = 0x73; - - } else { - if (pi->pubpi.radiorev != 5) { - pad2g_tune_pus = 0x3; - - txmix2g_tune_boost_pu = 0x61; - } - } - - for (coreNum = 0; coreNum <= 1; coreNum++) { - - if (txmix2g_tune_boost_pu != 0) - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, - TXMIX2G_TUNE_BOOST_PU, - txmix2g_tune_boost_pu); - - if (pad2g_tune_pus != 0) - WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, - PAD2G_TUNE_PUS, - pad2g_tune_pus); - } - } - - udelay(50); - - wlc_phy_radio205x_vcocal_nphy(pi); -} - -static void -wlc_phy_chanspec_nphy_setup(struct brcms_phy *pi, u16 chanspec, - const struct nphy_sfo_cfg *ci) -{ - u16 val; - - val = read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand; - if (CHSPEC_IS5G(chanspec) && !val) { - - val = bcma_read16(pi->d11core, D11REGOFFS(psm_phy_hdr_param)); - bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), - (val | MAC_PHY_FORCE_CLK)); - - or_phy_reg(pi, (NPHY_TO_BPHY_OFF + BPHY_BB_CONFIG), - (BBCFG_RESETCCA | BBCFG_RESETRX)); - - bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), val); - - or_phy_reg(pi, 0x09, NPHY_BandControl_currentBand); - } else if (!CHSPEC_IS5G(chanspec) && val) { - - and_phy_reg(pi, 0x09, ~NPHY_BandControl_currentBand); - - val = bcma_read16(pi->d11core, D11REGOFFS(psm_phy_hdr_param)); - bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), - (val | MAC_PHY_FORCE_CLK)); - - and_phy_reg(pi, (NPHY_TO_BPHY_OFF + BPHY_BB_CONFIG), - (u16) (~(BBCFG_RESETCCA | BBCFG_RESETRX))); - - bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), val); - } - - write_phy_reg(pi, 0x1ce, ci->PHY_BW1a); - write_phy_reg(pi, 0x1cf, ci->PHY_BW2); - write_phy_reg(pi, 0x1d0, ci->PHY_BW3); - - write_phy_reg(pi, 0x1d1, ci->PHY_BW4); - write_phy_reg(pi, 0x1d2, ci->PHY_BW5); - write_phy_reg(pi, 0x1d3, ci->PHY_BW6); - - if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) { - wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_ofdm_en, 0); - - or_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_TEST, 0x800); - } else { - wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_ofdm_en, - NPHY_ClassifierCtrl_ofdm_en); - - if (CHSPEC_IS2G(chanspec)) - and_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_TEST, ~0x840); - } - - if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) - wlc_phy_txpwr_fixpower_nphy(pi); - - if (NREV_LT(pi->pubpi.phy_rev, 3)) - wlc_phy_adjust_lnagaintbl_nphy(pi); - - wlc_phy_txlpfbw_nphy(pi); - - if (NREV_GE(pi->pubpi.phy_rev, 3) - && (pi->phy_spuravoid != SPURAVOID_DISABLE)) { - u8 spuravoid = 0; - - val = CHSPEC_CHANNEL(chanspec); - if (!CHSPEC_IS40(pi->radio_chanspec)) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if ((val == 13) || (val == 14) || (val == 153)) - spuravoid = 1; - } else if (((val >= 5) && (val <= 8)) || (val == 13) - || (val == 14)) { - spuravoid = 1; - } - } else if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if (val == 54) - spuravoid = 1; - } else if (pi->nphy_aband_spurwar_en && - ((val == 38) || (val == 102) || (val == 118))) { - if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) - && (pi->sh->chippkg == BCMA_PKG_ID_BCM4717)) { - spuravoid = 0; - } else { - spuravoid = 1; - } - } - - if (pi->phy_spuravoid == SPURAVOID_FORCEON) - spuravoid = 1; - - if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || - (pi->sh->chip == BCMA_CHIP_ID_BCM43225)) { - bcma_pmu_spuravoid_pllupdate(&pi->d11core->bus->drv_cc, - spuravoid); - } else { - wlapi_bmac_core_phypll_ctl(pi->sh->physhim, false); - bcma_pmu_spuravoid_pllupdate(&pi->d11core->bus->drv_cc, - spuravoid); - wlapi_bmac_core_phypll_ctl(pi->sh->physhim, true); - } - - if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) || - (pi->sh->chip == BCMA_CHIP_ID_BCM43225)) { - if (spuravoid == 1) { - bcma_write16(pi->d11core, - D11REGOFFS(tsf_clk_frac_l), - 0x5341); - bcma_write16(pi->d11core, - D11REGOFFS(tsf_clk_frac_h), 0x8); - } else { - bcma_write16(pi->d11core, - D11REGOFFS(tsf_clk_frac_l), - 0x8889); - bcma_write16(pi->d11core, - D11REGOFFS(tsf_clk_frac_h), 0x8); - } - } - - if (!((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || - (pi->sh->chip == BCMA_CHIP_ID_BCM47162))) - wlapi_bmac_core_phypll_reset(pi->sh->physhim); - - mod_phy_reg(pi, 0x01, (0x1 << 15), - ((spuravoid > 0) ? (0x1 << 15) : 0)); - - wlc_phy_resetcca_nphy(pi); - - pi->phy_isspuravoid = (spuravoid > 0); - } - - if (NREV_LT(pi->pubpi.phy_rev, 7)) - write_phy_reg(pi, 0x17e, 0x3830); - - wlc_phy_spurwar_nphy(pi); -} - -void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, u16 chanspec) -{ - int freq; - const struct chan_info_nphy_radio2057 *t0 = NULL; - const struct chan_info_nphy_radio205x *t1 = NULL; - const struct chan_info_nphy_radio2057_rev5 *t2 = NULL; - const struct chan_info_nphy_2055 *t3 = NULL; - - if (!wlc_phy_chan2freq_nphy - (pi, CHSPEC_CHANNEL(chanspec), &freq, &t0, &t1, &t2, &t3)) - return; - - wlc_phy_chanspec_radio_set((struct brcms_phy_pub *) pi, chanspec); - - if (CHSPEC_BW(chanspec) != pi->bw) - wlapi_bmac_bw_set(pi->sh->physhim, CHSPEC_BW(chanspec)); - - if (CHSPEC_IS40(chanspec)) { - if (CHSPEC_SB_UPPER(chanspec)) { - or_phy_reg(pi, 0xa0, BPHY_BAND_SEL_UP20); - if (NREV_GE(pi->pubpi.phy_rev, 7)) - or_phy_reg(pi, 0x310, PRIM_SEL_UP20); - } else { - and_phy_reg(pi, 0xa0, ~BPHY_BAND_SEL_UP20); - if (NREV_GE(pi->pubpi.phy_rev, 7)) - and_phy_reg(pi, 0x310, - (~PRIM_SEL_UP20 & 0xffff)); - } - } - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - if ((pi->pubpi.radiorev <= 4) - || (pi->pubpi.radiorev == 6)) { - mod_radio_reg(pi, RADIO_2057_TIA_CONFIG_CORE0, - 0x2, - (CHSPEC_IS5G(chanspec) ? (1 << 1) - : 0)); - mod_radio_reg(pi, RADIO_2057_TIA_CONFIG_CORE1, - 0x2, - (CHSPEC_IS5G(chanspec) ? (1 << 1) - : 0)); - } - - wlc_phy_chanspec_radio2057_setup(pi, t0, t2); - wlc_phy_chanspec_nphy_setup(pi, chanspec, - (pi->pubpi.radiorev == 5) ? - (const struct nphy_sfo_cfg *)&(t2->PHY_BW1a) : - (const struct nphy_sfo_cfg *)&(t0->PHY_BW1a)); - - } else { - - mod_radio_reg(pi, - RADIO_2056_SYN_COM_CTRL | RADIO_2056_SYN, - 0x4, - (CHSPEC_IS5G(chanspec) ? (0x1 << 2) : 0)); - wlc_phy_chanspec_radio2056_setup(pi, t1); - - wlc_phy_chanspec_nphy_setup(pi, chanspec, - (const struct nphy_sfo_cfg *) &(t1->PHY_BW1a)); - } - - } else { - - mod_radio_reg(pi, RADIO_2055_MASTER_CNTRL1, 0x70, - (CHSPEC_IS5G(chanspec) ? (0x02 << 4) - : (0x05 << 4))); - - wlc_phy_chanspec_radio2055_setup(pi, t3); - wlc_phy_chanspec_nphy_setup(pi, chanspec, - (const struct nphy_sfo_cfg *) - &(t3->PHY_BW1a)); - } - -} - -void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init) -{ - struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); - u16 mask = 0xfc00; - u32 mc = 0; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - return; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - u16 v0 = 0x211, v1 = 0x222, v2 = 0x144, v3 = 0x188; - - if (!lut_init) - return; - - if (pi->srom_fem2g.antswctrllut == 0) { - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 1, 0x02, 16, &v0); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 1, 0x03, 16, &v1); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 1, 0x08, 16, &v2); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 1, 0x0C, 16, &v3); - } - - if (pi->srom_fem5g.antswctrllut == 0) { - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 1, 0x12, 16, &v0); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 1, 0x13, 16, &v1); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 1, 0x18, 16, &v2); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, - 1, 0x1C, 16, &v3); - } - } else { - - write_phy_reg(pi, 0xc8, 0x0); - write_phy_reg(pi, 0xc9, 0x0); - - bcma_chipco_gpio_control(&pi->d11core->bus->drv_cc, mask, mask); - - mc = bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); - mc &= ~MCTL_GPOUT_SEL_MASK; - bcma_write32(pi->d11core, D11REGOFFS(maccontrol), mc); - - bcma_set16(pi->d11core, D11REGOFFS(psm_gpio_oe), mask); - - bcma_mask16(pi->d11core, D11REGOFFS(psm_gpio_out), ~mask); - - if (lut_init) { - write_phy_reg(pi, 0xf8, 0x02d8); - write_phy_reg(pi, 0xf9, 0x0301); - write_phy_reg(pi, 0xfa, 0x02d8); - write_phy_reg(pi, 0xfb, 0x0301); - } - } -} - -u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val) -{ - u16 curr_ctl, new_ctl; - bool suspended = false; - - if (D11REV_IS(pi->sh->corerev, 16)) { - suspended = (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & - MCTL_EN_MAC) ? false : true; - if (!suspended) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - } - - curr_ctl = read_phy_reg(pi, 0xb0) & (0x7 << 0); - - new_ctl = (curr_ctl & (~mask)) | (val & mask); - - mod_phy_reg(pi, 0xb0, (0x7 << 0), new_ctl); - - if (D11REV_IS(pi->sh->corerev, 16) && !suspended) - wlapi_enable_mac(pi->sh->physhim); - - return new_ctl; -} - -void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd) -{ - u16 trigger_mask, status_mask; - u16 orig_RfseqCoreActv; - - switch (cmd) { - case NPHY_RFSEQ_RX2TX: - trigger_mask = NPHY_RfseqTrigger_rx2tx; - status_mask = NPHY_RfseqStatus_rx2tx; - break; - case NPHY_RFSEQ_TX2RX: - trigger_mask = NPHY_RfseqTrigger_tx2rx; - status_mask = NPHY_RfseqStatus_tx2rx; - break; - case NPHY_RFSEQ_RESET2RX: - trigger_mask = NPHY_RfseqTrigger_reset2rx; - status_mask = NPHY_RfseqStatus_reset2rx; - break; - case NPHY_RFSEQ_UPDATEGAINH: - trigger_mask = NPHY_RfseqTrigger_updategainh; - status_mask = NPHY_RfseqStatus_updategainh; - break; - case NPHY_RFSEQ_UPDATEGAINL: - trigger_mask = NPHY_RfseqTrigger_updategainl; - status_mask = NPHY_RfseqStatus_updategainl; - break; - case NPHY_RFSEQ_UPDATEGAINU: - trigger_mask = NPHY_RfseqTrigger_updategainu; - status_mask = NPHY_RfseqStatus_updategainu; - break; - default: - return; - } - - orig_RfseqCoreActv = read_phy_reg(pi, 0xa1); - or_phy_reg(pi, 0xa1, - (NPHY_RfseqMode_CoreActv_override | - NPHY_RfseqMode_Trigger_override)); - or_phy_reg(pi, 0xa3, trigger_mask); - SPINWAIT((read_phy_reg(pi, 0xa4) & status_mask), 200000); - write_phy_reg(pi, 0xa1, orig_RfseqCoreActv); - WARN(read_phy_reg(pi, 0xa4) & status_mask, "HW error in rf"); -} - -static void -wlc_phy_rfctrl_override_1tomany_nphy(struct brcms_phy *pi, u16 cmd, u16 value, - u8 core_mask, u8 off) -{ - u16 rfmxgain = 0, lpfgain = 0; - u16 tgain = 0; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - switch (cmd) { - case NPHY_REV7_RfctrlOverride_cmd_rxrf_pu: - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 5), - value, core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 4), value, - core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 3), value, - core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - break; - case NPHY_REV7_RfctrlOverride_cmd_rx_pu: - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 2), - value, core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 1), value, - core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 0), value, - core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 1), value, - core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID2); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 11), 0, - core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - break; - case NPHY_REV7_RfctrlOverride_cmd_tx_pu: - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 2), - value, core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 1), value, - core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 0), value, - core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID2); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 2), value, - core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID2); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 11), 1, - core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - break; - case NPHY_REV7_RfctrlOverride_cmd_rxgain: - rfmxgain = value & 0x000ff; - lpfgain = value & 0x0ff00; - lpfgain = lpfgain >> 8; - - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 11), - rfmxgain, core_mask, - off, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x3 << 13), - lpfgain, core_mask, - off, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - break; - case NPHY_REV7_RfctrlOverride_cmd_txgain: - tgain = value & 0x7fff; - lpfgain = value & 0x8000; - lpfgain = lpfgain >> 14; - - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 12), - tgain, core_mask, off, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 13), - lpfgain, core_mask, - off, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - break; - } - } -} - -static void -wlc_phy_scale_offset_rssi_nphy(struct brcms_phy *pi, u16 scale, s8 offset, - u8 coresel, u8 rail, u8 rssi_type) -{ - u16 valuetostuff; - - offset = (offset > NPHY_RSSICAL_MAXREAD) ? - NPHY_RSSICAL_MAXREAD : offset; - offset = (offset < (-NPHY_RSSICAL_MAXREAD - 1)) ? - -NPHY_RSSICAL_MAXREAD - 1 : offset; - - valuetostuff = ((scale & 0x3f) << 8) | (offset & 0x3f); - - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_NB)) - write_phy_reg(pi, 0x1a6, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_NB)) - write_phy_reg(pi, 0x1ac, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_NB)) - write_phy_reg(pi, 0x1b2, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_NB)) - write_phy_reg(pi, 0x1b8, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W1)) - write_phy_reg(pi, 0x1a4, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W1)) - write_phy_reg(pi, 0x1aa, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W1)) - write_phy_reg(pi, 0x1b0, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W1)) - write_phy_reg(pi, 0x1b6, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W2)) - write_phy_reg(pi, 0x1a5, valuetostuff); - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W2)) - write_phy_reg(pi, 0x1ab, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W2)) - write_phy_reg(pi, 0x1b1, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W2)) - write_phy_reg(pi, 0x1b7, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_TBD)) - write_phy_reg(pi, 0x1a7, valuetostuff); - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_TBD)) - write_phy_reg(pi, 0x1ad, valuetostuff); - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_TBD)) - write_phy_reg(pi, 0x1b3, valuetostuff); - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_TBD)) - write_phy_reg(pi, 0x1b9, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_IQ)) - write_phy_reg(pi, 0x1a8, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_IQ)) - write_phy_reg(pi, 0x1ae, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_IQ)) - write_phy_reg(pi, 0x1b4, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_IQ)) - write_phy_reg(pi, 0x1ba, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rssi_type == NPHY_RSSI_SEL_TSSI_2G)) - write_phy_reg(pi, 0x1a9, valuetostuff); - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rssi_type == NPHY_RSSI_SEL_TSSI_2G)) - write_phy_reg(pi, 0x1b5, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE1) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rssi_type == NPHY_RSSI_SEL_TSSI_5G)) - write_phy_reg(pi, 0x1af, valuetostuff); - - if (((coresel == RADIO_MIMO_CORESEL_CORE2) || - (coresel == RADIO_MIMO_CORESEL_ALLRX)) && - (rssi_type == NPHY_RSSI_SEL_TSSI_5G)) - write_phy_reg(pi, 0x1bb, valuetostuff); -} - -static void brcms_phy_wr_tx_mux(struct brcms_phy *pi, u8 core) -{ - if (PHY_IPA(pi)) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) - write_radio_reg(pi, - ((core == PHY_CORE_0) ? - RADIO_2057_TX0_TX_SSI_MUX : - RADIO_2057_TX1_TX_SSI_MUX), - (CHSPEC_IS5G(pi->radio_chanspec) ? - 0xc : 0xe)); - else - write_radio_reg(pi, - RADIO_2056_TX_TX_SSI_MUX | - ((core == PHY_CORE_0) ? - RADIO_2056_TX0 : RADIO_2056_TX1), - (CHSPEC_IS5G(pi->radio_chanspec) ? - 0xc : 0xe)); - } else { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - write_radio_reg(pi, - ((core == PHY_CORE_0) ? - RADIO_2057_TX0_TX_SSI_MUX : - RADIO_2057_TX1_TX_SSI_MUX), - 0x11); - - if (pi->pubpi.radioid == BCM2057_ID) - write_radio_reg(pi, - RADIO_2057_IQTEST_SEL_PU, 0x1); - - } else { - write_radio_reg(pi, - RADIO_2056_TX_TX_SSI_MUX | - ((core == PHY_CORE_0) ? - RADIO_2056_TX0 : RADIO_2056_TX1), - 0x11); - } - } -} - -void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core_code, u8 rssi_type) -{ - u16 mask, val; - u16 afectrlovr_rssi_val, rfctrlcmd_rxen_val, rfctrlcmd_coresel_val, - startseq; - u16 rfctrlovr_rssi_val, rfctrlovr_rxen_val, rfctrlovr_coresel_val, - rfctrlovr_trigger_val; - u16 afectrlovr_rssi_mask, rfctrlcmd_mask, rfctrlovr_mask; - u16 rfctrlcmd_val, rfctrlovr_val; - u8 core; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - if (core_code == RADIO_MIMO_CORESEL_OFF) { - mod_phy_reg(pi, 0x8f, (0x1 << 9), 0); - mod_phy_reg(pi, 0xa5, (0x1 << 9), 0); - - mod_phy_reg(pi, 0xa6, (0x3 << 8), 0); - mod_phy_reg(pi, 0xa7, (0x3 << 8), 0); - - mod_phy_reg(pi, 0xe5, (0x1 << 5), 0); - mod_phy_reg(pi, 0xe6, (0x1 << 5), 0); - - mask = (0x1 << 2) | - (0x1 << 3) | (0x1 << 4) | (0x1 << 5); - mod_phy_reg(pi, 0xf9, mask, 0); - mod_phy_reg(pi, 0xfb, mask, 0); - - } else { - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - if (core_code == RADIO_MIMO_CORESEL_CORE1 - && core == PHY_CORE_1) - continue; - else if (core_code == RADIO_MIMO_CORESEL_CORE2 - && core == PHY_CORE_0) - continue; - - mod_phy_reg(pi, (core == PHY_CORE_0) ? - 0x8f : 0xa5, (0x1 << 9), 1 << 9); - - if (rssi_type == NPHY_RSSI_SEL_W1 || - rssi_type == NPHY_RSSI_SEL_W2 || - rssi_type == NPHY_RSSI_SEL_NB) { - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0xa6 : 0xa7, - (0x3 << 8), 0); - - mask = (0x1 << 2) | - (0x1 << 3) | - (0x1 << 4) | (0x1 << 5); - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0xf9 : 0xfb, - mask, 0); - - if (rssi_type == NPHY_RSSI_SEL_W1) { - if (CHSPEC_IS5G( - pi->radio_chanspec)) { - mask = (0x1 << 2); - val = 1 << 2; - } else { - mask = (0x1 << 3); - val = 1 << 3; - } - } else if (rssi_type == - NPHY_RSSI_SEL_W2) { - mask = (0x1 << 4); - val = 1 << 4; - } else { - mask = (0x1 << 5); - val = 1 << 5; - } - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0xf9 : 0xfb, - mask, val); - - mask = (0x1 << 5); - val = 1 << 5; - mod_phy_reg(pi, (core == PHY_CORE_0) ? - 0xe5 : 0xe6, mask, val); - } else { - if (rssi_type == NPHY_RSSI_SEL_TBD) { - mask = (0x3 << 8); - val = 1 << 8; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0xa6 - : 0xa7, mask, val); - mask = (0x3 << 10); - val = 1 << 10; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0xa6 - : 0xa7, mask, val); - } else if (rssi_type == - NPHY_RSSI_SEL_IQ) { - mask = (0x3 << 8); - val = 2 << 8; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0xa6 - : 0xa7, mask, val); - mask = (0x3 << 10); - val = 2 << 10; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0xa6 - : 0xa7, mask, val); - } else { - mask = (0x3 << 8); - val = 3 << 8; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0xa6 - : 0xa7, mask, val); - mask = (0x3 << 10); - val = 3 << 10; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0xa6 - : 0xa7, mask, val); - brcms_phy_wr_tx_mux(pi, core); - afectrlovr_rssi_val = 1 << 9; - mod_phy_reg(pi, - (core == - PHY_CORE_0) ? 0x8f - : 0xa5, (0x1 << 9), - afectrlovr_rssi_val); - } - } - } - } - } else { - - if ((rssi_type == NPHY_RSSI_SEL_W1) || - (rssi_type == NPHY_RSSI_SEL_W2) || - (rssi_type == NPHY_RSSI_SEL_NB)) - val = 0x0; - else if (rssi_type == NPHY_RSSI_SEL_TBD) - val = 0x1; - else if (rssi_type == NPHY_RSSI_SEL_IQ) - val = 0x2; - else - val = 0x3; - - mask = ((0x3 << 12) | (0x3 << 14)); - val = (val << 12) | (val << 14); - mod_phy_reg(pi, 0xa6, mask, val); - mod_phy_reg(pi, 0xa7, mask, val); - - if ((rssi_type == NPHY_RSSI_SEL_W1) || - (rssi_type == NPHY_RSSI_SEL_W2) || - (rssi_type == NPHY_RSSI_SEL_NB)) { - if (rssi_type == NPHY_RSSI_SEL_W1) - val = 0x1; - if (rssi_type == NPHY_RSSI_SEL_W2) - val = 0x2; - if (rssi_type == NPHY_RSSI_SEL_NB) - val = 0x3; - - mask = (0x3 << 4); - val = (val << 4); - mod_phy_reg(pi, 0x7a, mask, val); - mod_phy_reg(pi, 0x7d, mask, val); - } - - if (core_code == RADIO_MIMO_CORESEL_OFF) { - afectrlovr_rssi_val = 0; - rfctrlcmd_rxen_val = 0; - rfctrlcmd_coresel_val = 0; - rfctrlovr_rssi_val = 0; - rfctrlovr_rxen_val = 0; - rfctrlovr_coresel_val = 0; - rfctrlovr_trigger_val = 0; - startseq = 0; - } else { - afectrlovr_rssi_val = 1; - rfctrlcmd_rxen_val = 1; - rfctrlcmd_coresel_val = core_code; - rfctrlovr_rssi_val = 1; - rfctrlovr_rxen_val = 1; - rfctrlovr_coresel_val = 1; - rfctrlovr_trigger_val = 1; - startseq = 1; - } - - afectrlovr_rssi_mask = ((0x1 << 12) | (0x1 << 13)); - afectrlovr_rssi_val = (afectrlovr_rssi_val << - 12) | (afectrlovr_rssi_val << 13); - mod_phy_reg(pi, 0xa5, afectrlovr_rssi_mask, - afectrlovr_rssi_val); - - if ((rssi_type == NPHY_RSSI_SEL_W1) || - (rssi_type == NPHY_RSSI_SEL_W2) || - (rssi_type == NPHY_RSSI_SEL_NB)) { - rfctrlcmd_mask = ((0x1 << 8) | (0x7 << 3)); - rfctrlcmd_val = (rfctrlcmd_rxen_val << 8) | - (rfctrlcmd_coresel_val << 3); - - rfctrlovr_mask = ((0x1 << 5) | - (0x1 << 12) | - (0x1 << 1) | (0x1 << 0)); - rfctrlovr_val = (rfctrlovr_rssi_val << - 5) | - (rfctrlovr_rxen_val << 12) | - (rfctrlovr_coresel_val << 1) | - (rfctrlovr_trigger_val << 0); - - mod_phy_reg(pi, 0x78, rfctrlcmd_mask, rfctrlcmd_val); - mod_phy_reg(pi, 0xec, rfctrlovr_mask, rfctrlovr_val); - - mod_phy_reg(pi, 0x78, (0x1 << 0), (startseq << 0)); - udelay(20); - - mod_phy_reg(pi, 0xec, (0x1 << 0), 0); - } - } -} - -int -wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type, s32 *rssi_buf, - u8 nsamps) -{ - s16 rssi0, rssi1; - u16 afectrlCore1_save = 0; - u16 afectrlCore2_save = 0; - u16 afectrlOverride1_save = 0; - u16 afectrlOverride2_save = 0; - u16 rfctrlOverrideAux0_save = 0; - u16 rfctrlOverrideAux1_save = 0; - u16 rfctrlMiscReg1_save = 0; - u16 rfctrlMiscReg2_save = 0; - u16 rfctrlcmd_save = 0; - u16 rfctrloverride_save = 0; - u16 rfctrlrssiothers1_save = 0; - u16 rfctrlrssiothers2_save = 0; - s8 tmp_buf[4]; - u8 ctr = 0, samp = 0; - s32 rssi_out_val; - u16 gpiosel_orig; - - afectrlCore1_save = read_phy_reg(pi, 0xa6); - afectrlCore2_save = read_phy_reg(pi, 0xa7); - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - rfctrlMiscReg1_save = read_phy_reg(pi, 0xf9); - rfctrlMiscReg2_save = read_phy_reg(pi, 0xfb); - afectrlOverride1_save = read_phy_reg(pi, 0x8f); - afectrlOverride2_save = read_phy_reg(pi, 0xa5); - rfctrlOverrideAux0_save = read_phy_reg(pi, 0xe5); - rfctrlOverrideAux1_save = read_phy_reg(pi, 0xe6); - } else { - afectrlOverride1_save = read_phy_reg(pi, 0xa5); - rfctrlcmd_save = read_phy_reg(pi, 0x78); - rfctrloverride_save = read_phy_reg(pi, 0xec); - rfctrlrssiothers1_save = read_phy_reg(pi, 0x7a); - rfctrlrssiothers2_save = read_phy_reg(pi, 0x7d); - } - - wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_ALLRX, rssi_type); - - gpiosel_orig = read_phy_reg(pi, 0xca); - if (NREV_LT(pi->pubpi.phy_rev, 2)) - write_phy_reg(pi, 0xca, 5); - - for (ctr = 0; ctr < 4; ctr++) - rssi_buf[ctr] = 0; - - for (samp = 0; samp < nsamps; samp++) { - if (NREV_LT(pi->pubpi.phy_rev, 2)) { - rssi0 = read_phy_reg(pi, 0x1c9); - rssi1 = read_phy_reg(pi, 0x1ca); - } else { - rssi0 = read_phy_reg(pi, 0x219); - rssi1 = read_phy_reg(pi, 0x21a); - } - - ctr = 0; - tmp_buf[ctr++] = ((s8) ((rssi0 & 0x3f) << 2)) >> 2; - tmp_buf[ctr++] = ((s8) (((rssi0 >> 8) & 0x3f) << 2)) >> 2; - tmp_buf[ctr++] = ((s8) ((rssi1 & 0x3f) << 2)) >> 2; - tmp_buf[ctr++] = ((s8) (((rssi1 >> 8) & 0x3f) << 2)) >> 2; - - for (ctr = 0; ctr < 4; ctr++) - rssi_buf[ctr] += tmp_buf[ctr]; - - } - - rssi_out_val = rssi_buf[3] & 0xff; - rssi_out_val |= (rssi_buf[2] & 0xff) << 8; - rssi_out_val |= (rssi_buf[1] & 0xff) << 16; - rssi_out_val |= (rssi_buf[0] & 0xff) << 24; - - if (NREV_LT(pi->pubpi.phy_rev, 2)) - write_phy_reg(pi, 0xca, gpiosel_orig); - - write_phy_reg(pi, 0xa6, afectrlCore1_save); - write_phy_reg(pi, 0xa7, afectrlCore2_save); - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - write_phy_reg(pi, 0xf9, rfctrlMiscReg1_save); - write_phy_reg(pi, 0xfb, rfctrlMiscReg2_save); - write_phy_reg(pi, 0x8f, afectrlOverride1_save); - write_phy_reg(pi, 0xa5, afectrlOverride2_save); - write_phy_reg(pi, 0xe5, rfctrlOverrideAux0_save); - write_phy_reg(pi, 0xe6, rfctrlOverrideAux1_save); - } else { - write_phy_reg(pi, 0xa5, afectrlOverride1_save); - write_phy_reg(pi, 0x78, rfctrlcmd_save); - write_phy_reg(pi, 0xec, rfctrloverride_save); - write_phy_reg(pi, 0x7a, rfctrlrssiothers1_save); - write_phy_reg(pi, 0x7d, rfctrlrssiothers2_save); - } - - return rssi_out_val; -} - -s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi) -{ - u16 core1_txrf_iqcal1_save, core1_txrf_iqcal2_save; - u16 core2_txrf_iqcal1_save, core2_txrf_iqcal2_save; - u16 pwrdet_rxtx_core1_save; - u16 pwrdet_rxtx_core2_save; - u16 afectrlCore1_save; - u16 afectrlCore2_save; - u16 afectrlOverride_save; - u16 afectrlOverride2_save; - u16 pd_pll_ts_save; - u16 gpioSel_save; - s32 radio_temp[4]; - s32 radio_temp2[4]; - u16 syn_tempprocsense_save; - s16 offset = 0; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - u16 auxADC_Vmid, auxADC_Av, auxADC_Vmid_save, auxADC_Av_save; - u16 auxADC_rssi_ctrlL_save, auxADC_rssi_ctrlH_save; - u16 auxADC_rssi_ctrlL, auxADC_rssi_ctrlH; - s32 auxADC_Vl; - u16 RfctrlOverride5_save, RfctrlOverride6_save; - u16 RfctrlMiscReg5_save, RfctrlMiscReg6_save; - u16 RSSIMultCoef0QPowerDet_save; - u16 tempsense_Rcal; - - syn_tempprocsense_save = - read_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG); - - afectrlCore1_save = read_phy_reg(pi, 0xa6); - afectrlCore2_save = read_phy_reg(pi, 0xa7); - afectrlOverride_save = read_phy_reg(pi, 0x8f); - afectrlOverride2_save = read_phy_reg(pi, 0xa5); - RSSIMultCoef0QPowerDet_save = read_phy_reg(pi, 0x1ae); - RfctrlOverride5_save = read_phy_reg(pi, 0x346); - RfctrlOverride6_save = read_phy_reg(pi, 0x347); - RfctrlMiscReg5_save = read_phy_reg(pi, 0x344); - RfctrlMiscReg6_save = read_phy_reg(pi, 0x345); - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, - &auxADC_Vmid_save); - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, - &auxADC_Av_save); - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x02, 16, - &auxADC_rssi_ctrlL_save); - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x03, 16, - &auxADC_rssi_ctrlH_save); - - write_phy_reg(pi, 0x1ae, 0x0); - - auxADC_rssi_ctrlL = 0x0; - auxADC_rssi_ctrlH = 0x20; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x02, 16, - &auxADC_rssi_ctrlL); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x03, 16, - &auxADC_rssi_ctrlH); - - tempsense_Rcal = syn_tempprocsense_save & 0x1c; - - write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, - tempsense_Rcal | 0x01); - - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), - 1, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID2); - mod_phy_reg(pi, 0xa6, (0x1 << 7), 0); - mod_phy_reg(pi, 0xa7, (0x1 << 7), 0); - mod_phy_reg(pi, 0x8f, (0x1 << 7), (0x1 << 7)); - mod_phy_reg(pi, 0xa5, (0x1 << 7), (0x1 << 7)); - - mod_phy_reg(pi, 0xa6, (0x1 << 2), (0x1 << 2)); - mod_phy_reg(pi, 0xa7, (0x1 << 2), (0x1 << 2)); - mod_phy_reg(pi, 0x8f, (0x1 << 2), (0x1 << 2)); - mod_phy_reg(pi, 0xa5, (0x1 << 2), (0x1 << 2)); - udelay(5); - mod_phy_reg(pi, 0xa6, (0x1 << 2), 0); - mod_phy_reg(pi, 0xa7, (0x1 << 2), 0); - mod_phy_reg(pi, 0xa6, (0x1 << 3), 0); - mod_phy_reg(pi, 0xa7, (0x1 << 3), 0); - mod_phy_reg(pi, 0x8f, (0x1 << 3), (0x1 << 3)); - mod_phy_reg(pi, 0xa5, (0x1 << 3), (0x1 << 3)); - mod_phy_reg(pi, 0xa6, (0x1 << 6), 0); - mod_phy_reg(pi, 0xa7, (0x1 << 6), 0); - mod_phy_reg(pi, 0x8f, (0x1 << 6), (0x1 << 6)); - mod_phy_reg(pi, 0xa5, (0x1 << 6), (0x1 << 6)); - - auxADC_Vmid = 0xA3; - auxADC_Av = 0x0; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, - &auxADC_Vmid); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, - &auxADC_Av); - - udelay(3); - - wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); - write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, - tempsense_Rcal | 0x03); - - udelay(5); - wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); - - auxADC_Av = 0x7; - if (radio_temp[1] + radio_temp2[1] < -30) { - auxADC_Vmid = 0x45; - auxADC_Vl = 263; - } else if (radio_temp[1] + radio_temp2[1] < -9) { - auxADC_Vmid = 0x200; - auxADC_Vl = 467; - } else if (radio_temp[1] + radio_temp2[1] < 11) { - auxADC_Vmid = 0x266; - auxADC_Vl = 634; - } else { - auxADC_Vmid = 0x2D5; - auxADC_Vl = 816; - } - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, - &auxADC_Vmid); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, - &auxADC_Av); - - udelay(3); - - wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); - write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, - tempsense_Rcal | 0x01); - - udelay(5); - wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); - - write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, - syn_tempprocsense_save); - - write_phy_reg(pi, 0xa6, afectrlCore1_save); - write_phy_reg(pi, 0xa7, afectrlCore2_save); - write_phy_reg(pi, 0x8f, afectrlOverride_save); - write_phy_reg(pi, 0xa5, afectrlOverride2_save); - write_phy_reg(pi, 0x1ae, RSSIMultCoef0QPowerDet_save); - write_phy_reg(pi, 0x346, RfctrlOverride5_save); - write_phy_reg(pi, 0x347, RfctrlOverride6_save); - write_phy_reg(pi, 0x344, RfctrlMiscReg5_save); - write_phy_reg(pi, 0x345, RfctrlMiscReg5_save); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, - &auxADC_Vmid_save); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, - &auxADC_Av_save); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x02, 16, - &auxADC_rssi_ctrlL_save); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x03, 16, - &auxADC_rssi_ctrlH_save); - - if (pi->sh->chip == BCMA_CHIP_ID_BCM5357) { - radio_temp[0] = (193 * (radio_temp[1] + radio_temp2[1]) - + 88 * (auxADC_Vl) - 27111 + - 128) / 256; - } else { - radio_temp[0] = (179 * (radio_temp[1] + radio_temp2[1]) - + 82 * (auxADC_Vl) - 28861 + - 128) / 256; - } - - offset = (s16) pi->phy_tempsense_offset; - - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - syn_tempprocsense_save = - read_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE); - - afectrlCore1_save = read_phy_reg(pi, 0xa6); - afectrlCore2_save = read_phy_reg(pi, 0xa7); - afectrlOverride_save = read_phy_reg(pi, 0x8f); - afectrlOverride2_save = read_phy_reg(pi, 0xa5); - gpioSel_save = read_phy_reg(pi, 0xca); - - write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x01); - - wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); - if (NREV_LT(pi->pubpi.phy_rev, 7)) - write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x05); - - wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); - if (NREV_GE(pi->pubpi.phy_rev, 7)) - write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, 0x01); - else - write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x01); - - radio_temp[0] = - (126 * (radio_temp[1] + radio_temp2[1]) + 3987) / 64; - - write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, - syn_tempprocsense_save); - - write_phy_reg(pi, 0xca, gpioSel_save); - write_phy_reg(pi, 0xa6, afectrlCore1_save); - write_phy_reg(pi, 0xa7, afectrlCore2_save); - write_phy_reg(pi, 0x8f, afectrlOverride_save); - write_phy_reg(pi, 0xa5, afectrlOverride2_save); - - offset = (s16) pi->phy_tempsense_offset; - } else { - - pwrdet_rxtx_core1_save = - read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1); - pwrdet_rxtx_core2_save = - read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2); - core1_txrf_iqcal1_save = - read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1); - core1_txrf_iqcal2_save = - read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2); - core2_txrf_iqcal1_save = - read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1); - core2_txrf_iqcal2_save = - read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2); - pd_pll_ts_save = read_radio_reg(pi, RADIO_2055_PD_PLL_TS); - - afectrlCore1_save = read_phy_reg(pi, 0xa6); - afectrlCore2_save = read_phy_reg(pi, 0xa7); - afectrlOverride_save = read_phy_reg(pi, 0xa5); - gpioSel_save = read_phy_reg(pi, 0xca); - - write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, 0x01); - write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, 0x01); - write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, 0x08); - write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, 0x08); - write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, 0x04); - write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, 0x04); - write_radio_reg(pi, RADIO_2055_PD_PLL_TS, 0x00); - - wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); - xor_radio_reg(pi, RADIO_2055_CAL_TS, 0x80); - - wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); - xor_radio_reg(pi, RADIO_2055_CAL_TS, 0x80); - - wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); - xor_radio_reg(pi, RADIO_2055_CAL_TS, 0x80); - - radio_temp[0] = (radio_temp[0] + radio_temp2[0]); - radio_temp[1] = (radio_temp[1] + radio_temp2[1]); - radio_temp[2] = (radio_temp[2] + radio_temp2[2]); - radio_temp[3] = (radio_temp[3] + radio_temp2[3]); - - radio_temp[0] = - (radio_temp[0] + radio_temp[1] + radio_temp[2] + - radio_temp[3]); - - radio_temp[0] = - (radio_temp[0] + - (8 * 32)) * (950 - 350) / 63 + (350 * 8); - - radio_temp[0] = (radio_temp[0] - (8 * 420)) / 38; - - write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, - pwrdet_rxtx_core1_save); - write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, - pwrdet_rxtx_core2_save); - write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, - core1_txrf_iqcal1_save); - write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, - core2_txrf_iqcal1_save); - write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, - core1_txrf_iqcal2_save); - write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, - core2_txrf_iqcal2_save); - write_radio_reg(pi, RADIO_2055_PD_PLL_TS, pd_pll_ts_save); - - write_phy_reg(pi, 0xca, gpioSel_save); - write_phy_reg(pi, 0xa6, afectrlCore1_save); - write_phy_reg(pi, 0xa7, afectrlCore2_save); - write_phy_reg(pi, 0xa5, afectrlOverride_save); - } - - return (s16) radio_temp[0] + offset; -} - -static void -wlc_phy_set_rssi_2055_vcm(struct brcms_phy *pi, u8 rssi_type, u8 *vcm_buf) -{ - u8 core; - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - if (rssi_type == NPHY_RSSI_SEL_NB) { - if (core == PHY_CORE_0) { - mod_radio_reg(pi, - RADIO_2055_CORE1_B0_NBRSSI_VCM, - RADIO_2055_NBRSSI_VCM_I_MASK, - vcm_buf[2 * - core] << - RADIO_2055_NBRSSI_VCM_I_SHIFT); - mod_radio_reg(pi, - RADIO_2055_CORE1_RXBB_RSSI_CTRL5, - RADIO_2055_NBRSSI_VCM_Q_MASK, - vcm_buf[2 * core + - 1] << - RADIO_2055_NBRSSI_VCM_Q_SHIFT); - } else { - mod_radio_reg(pi, - RADIO_2055_CORE2_B0_NBRSSI_VCM, - RADIO_2055_NBRSSI_VCM_I_MASK, - vcm_buf[2 * - core] << - RADIO_2055_NBRSSI_VCM_I_SHIFT); - mod_radio_reg(pi, - RADIO_2055_CORE2_RXBB_RSSI_CTRL5, - RADIO_2055_NBRSSI_VCM_Q_MASK, - vcm_buf[2 * core + - 1] << - RADIO_2055_NBRSSI_VCM_Q_SHIFT); - } - } else { - if (core == PHY_CORE_0) - mod_radio_reg(pi, - RADIO_2055_CORE1_RXBB_RSSI_CTRL5, - RADIO_2055_WBRSSI_VCM_IQ_MASK, - vcm_buf[2 * - core] << - RADIO_2055_WBRSSI_VCM_IQ_SHIFT); - else - mod_radio_reg(pi, - RADIO_2055_CORE2_RXBB_RSSI_CTRL5, - RADIO_2055_WBRSSI_VCM_IQ_MASK, - vcm_buf[2 * - core] << - RADIO_2055_WBRSSI_VCM_IQ_SHIFT); - } - } -} - -static void wlc_phy_rssi_cal_nphy_rev3(struct brcms_phy *pi) -{ - u16 classif_state; - u16 clip_state[2]; - u16 clip_off[] = { 0xffff, 0xffff }; - s32 target_code; - u8 vcm, min_vcm; - u8 vcm_final = 0; - u8 result_idx; - s32 poll_results[8][4] = { - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0} - }; - s32 poll_result_core[4] = { 0, 0, 0, 0 }; - s32 min_d = NPHY_RSSICAL_MAXD, curr_d; - s32 fine_digital_offset[4]; - s32 poll_results_min[4] = { 0, 0, 0, 0 }; - s32 min_poll; - u8 vcm_level_max; - u8 core; - u8 wb_cnt; - u8 rssi_type; - u16 NPHY_Rfctrlintc1_save, NPHY_Rfctrlintc2_save; - u16 NPHY_AfectrlOverride1_save, NPHY_AfectrlOverride2_save; - u16 NPHY_AfectrlCore1_save, NPHY_AfectrlCore2_save; - u16 NPHY_RfctrlOverride0_save, NPHY_RfctrlOverride1_save; - u16 NPHY_RfctrlOverrideAux0_save, NPHY_RfctrlOverrideAux1_save; - u16 NPHY_RfctrlCmd_save; - u16 NPHY_RfctrlMiscReg1_save, NPHY_RfctrlMiscReg2_save; - u16 NPHY_RfctrlRSSIOTHERS1_save, NPHY_RfctrlRSSIOTHERS2_save; - u8 rxcore_state; - u16 NPHY_REV7_RfctrlOverride3_save, NPHY_REV7_RfctrlOverride4_save; - u16 NPHY_REV7_RfctrlOverride5_save, NPHY_REV7_RfctrlOverride6_save; - u16 NPHY_REV7_RfctrlMiscReg3_save, NPHY_REV7_RfctrlMiscReg4_save; - u16 NPHY_REV7_RfctrlMiscReg5_save, NPHY_REV7_RfctrlMiscReg6_save; - - NPHY_REV7_RfctrlOverride3_save = - NPHY_REV7_RfctrlOverride4_save = - NPHY_REV7_RfctrlOverride5_save = - NPHY_REV7_RfctrlOverride6_save = - NPHY_REV7_RfctrlMiscReg3_save = - NPHY_REV7_RfctrlMiscReg4_save = - NPHY_REV7_RfctrlMiscReg5_save = - NPHY_REV7_RfctrlMiscReg6_save = 0; - - classif_state = wlc_phy_classifier_nphy(pi, 0, 0); - wlc_phy_classifier_nphy(pi, (0x7 << 0), 4); - wlc_phy_clip_det_nphy(pi, 0, clip_state); - wlc_phy_clip_det_nphy(pi, 1, clip_off); - - NPHY_Rfctrlintc1_save = read_phy_reg(pi, 0x91); - NPHY_Rfctrlintc2_save = read_phy_reg(pi, 0x92); - NPHY_AfectrlOverride1_save = read_phy_reg(pi, 0x8f); - NPHY_AfectrlOverride2_save = read_phy_reg(pi, 0xa5); - NPHY_AfectrlCore1_save = read_phy_reg(pi, 0xa6); - NPHY_AfectrlCore2_save = read_phy_reg(pi, 0xa7); - NPHY_RfctrlOverride0_save = read_phy_reg(pi, 0xe7); - NPHY_RfctrlOverride1_save = read_phy_reg(pi, 0xec); - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - NPHY_REV7_RfctrlOverride3_save = read_phy_reg(pi, 0x342); - NPHY_REV7_RfctrlOverride4_save = read_phy_reg(pi, 0x343); - NPHY_REV7_RfctrlOverride5_save = read_phy_reg(pi, 0x346); - NPHY_REV7_RfctrlOverride6_save = read_phy_reg(pi, 0x347); - } - NPHY_RfctrlOverrideAux0_save = read_phy_reg(pi, 0xe5); - NPHY_RfctrlOverrideAux1_save = read_phy_reg(pi, 0xe6); - NPHY_RfctrlCmd_save = read_phy_reg(pi, 0x78); - NPHY_RfctrlMiscReg1_save = read_phy_reg(pi, 0xf9); - NPHY_RfctrlMiscReg2_save = read_phy_reg(pi, 0xfb); - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - NPHY_REV7_RfctrlMiscReg3_save = read_phy_reg(pi, 0x340); - NPHY_REV7_RfctrlMiscReg4_save = read_phy_reg(pi, 0x341); - NPHY_REV7_RfctrlMiscReg5_save = read_phy_reg(pi, 0x344); - NPHY_REV7_RfctrlMiscReg6_save = read_phy_reg(pi, 0x345); - } - NPHY_RfctrlRSSIOTHERS1_save = read_phy_reg(pi, 0x7a); - NPHY_RfctrlRSSIOTHERS2_save = read_phy_reg(pi, 0x7d); - - wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_OFF, 0, - RADIO_MIMO_CORESEL_ALLRXTX); - wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, 1, - RADIO_MIMO_CORESEL_ALLRXTX); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - wlc_phy_rfctrl_override_1tomany_nphy( - pi, - NPHY_REV7_RfctrlOverride_cmd_rxrf_pu, - 0, 0, 0); - else - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0, 0); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - wlc_phy_rfctrl_override_1tomany_nphy( - pi, - NPHY_REV7_RfctrlOverride_cmd_rx_pu, - 1, 0, 0); - else - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 1, 0, 0); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7), - 1, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 6), 1, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - } else { - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 7), 1, 0, 0); - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 6), 1, 0, 0); - } - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 5), - 0, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 4), 1, 0, - 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - } else { - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 5), 0, 0, 0); - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 4), 1, 0, 0); - } - - } else { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 4), - 0, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 5), 1, 0, - 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - } else { - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 4), 0, 0, 0); - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 5), 1, 0, 0); - } - } - - rxcore_state = wlc_phy_rxcore_getstate_nphy( - (struct brcms_phy_pub *) pi); - - vcm_level_max = 8; - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - - if ((rxcore_state & (1 << core)) == 0) - continue; - - wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, - core == - PHY_CORE_0 ? - RADIO_MIMO_CORESEL_CORE1 : - RADIO_MIMO_CORESEL_CORE2, - NPHY_RAIL_I, NPHY_RSSI_SEL_NB); - wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, - core == - PHY_CORE_0 ? - RADIO_MIMO_CORESEL_CORE1 : - RADIO_MIMO_CORESEL_CORE2, - NPHY_RAIL_Q, NPHY_RSSI_SEL_NB); - - for (vcm = 0; vcm < vcm_level_max; vcm++) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) - mod_radio_reg(pi, (core == PHY_CORE_0) ? - RADIO_2057_NB_MASTER_CORE0 : - RADIO_2057_NB_MASTER_CORE1, - RADIO_2057_VCM_MASK, vcm); - else - mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | - ((core == - PHY_CORE_0) ? RADIO_2056_RX0 : - RADIO_2056_RX1), - RADIO_2056_VCM_MASK, - vcm << RADIO_2056_RSSI_VCM_SHIFT); - - wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_NB, - &poll_results[vcm][0], - NPHY_RSSICAL_NPOLL); - } - - for (result_idx = 0; result_idx < 4; result_idx++) { - if ((core == result_idx / 2) && - (result_idx % 2 == 0)) { - - min_d = NPHY_RSSICAL_MAXD; - min_vcm = 0; - min_poll = - NPHY_RSSICAL_MAXREAD * - NPHY_RSSICAL_NPOLL + 1; - for (vcm = 0; vcm < vcm_level_max; vcm++) { - curr_d = - poll_results[vcm][result_idx] * - poll_results[vcm][result_idx] + - poll_results[vcm][result_idx + - 1] * - poll_results[vcm][result_idx + - 1]; - if (curr_d < min_d) { - min_d = curr_d; - min_vcm = vcm; - } - if (poll_results[vcm][result_idx] < - min_poll) - min_poll = - poll_results[vcm] - [result_idx]; - } - vcm_final = min_vcm; - poll_results_min[result_idx] = min_poll; - } - } - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - mod_radio_reg(pi, (core == PHY_CORE_0) ? - RADIO_2057_NB_MASTER_CORE0 : - RADIO_2057_NB_MASTER_CORE1, - RADIO_2057_VCM_MASK, vcm_final); - else - mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | - ((core == - PHY_CORE_0) ? RADIO_2056_RX0 : - RADIO_2056_RX1), RADIO_2056_VCM_MASK, - vcm_final << RADIO_2056_RSSI_VCM_SHIFT); - - for (result_idx = 0; result_idx < 4; result_idx++) { - if (core == result_idx / 2) { - fine_digital_offset[result_idx] = - (NPHY_RSSICAL_NB_TARGET * - NPHY_RSSICAL_NPOLL) - - poll_results[vcm_final][result_idx]; - if (fine_digital_offset[result_idx] < 0) { - fine_digital_offset[result_idx] = - abs(fine_digital_offset - [result_idx]); - fine_digital_offset[result_idx] += - (NPHY_RSSICAL_NPOLL / 2); - fine_digital_offset[result_idx] /= - NPHY_RSSICAL_NPOLL; - fine_digital_offset[result_idx] = - -fine_digital_offset[ - result_idx]; - } else { - fine_digital_offset[result_idx] += - (NPHY_RSSICAL_NPOLL / 2); - fine_digital_offset[result_idx] /= - NPHY_RSSICAL_NPOLL; - } - - if (poll_results_min[result_idx] == - NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL) - fine_digital_offset[result_idx] = - (NPHY_RSSICAL_NB_TARGET - - NPHY_RSSICAL_MAXREAD - 1); - - wlc_phy_scale_offset_rssi_nphy( - pi, 0x0, - (s8) - fine_digital_offset - [result_idx], - (result_idx / 2 == 0) ? - RADIO_MIMO_CORESEL_CORE1 : - RADIO_MIMO_CORESEL_CORE2, - (result_idx % 2 == 0) ? - NPHY_RAIL_I : NPHY_RAIL_Q, - NPHY_RSSI_SEL_NB); - } - } - - } - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - - if ((rxcore_state & (1 << core)) == 0) - continue; - - for (wb_cnt = 0; wb_cnt < 2; wb_cnt++) { - if (wb_cnt == 0) { - rssi_type = NPHY_RSSI_SEL_W1; - target_code = NPHY_RSSICAL_W1_TARGET_REV3; - } else { - rssi_type = NPHY_RSSI_SEL_W2; - target_code = NPHY_RSSICAL_W2_TARGET_REV3; - } - - wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, - core == - PHY_CORE_0 ? - RADIO_MIMO_CORESEL_CORE1 - : - RADIO_MIMO_CORESEL_CORE2, - NPHY_RAIL_I, rssi_type); - wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, - core == - PHY_CORE_0 ? - RADIO_MIMO_CORESEL_CORE1 - : - RADIO_MIMO_CORESEL_CORE2, - NPHY_RAIL_Q, rssi_type); - - wlc_phy_poll_rssi_nphy(pi, rssi_type, poll_result_core, - NPHY_RSSICAL_NPOLL); - - for (result_idx = 0; result_idx < 4; result_idx++) { - if (core == result_idx / 2) { - fine_digital_offset[result_idx] = - (target_code * - NPHY_RSSICAL_NPOLL) - - poll_result_core[result_idx]; - if (fine_digital_offset[result_idx] < - 0) { - fine_digital_offset[result_idx] - = abs( - fine_digital_offset - [result_idx]); - fine_digital_offset[result_idx] - += (NPHY_RSSICAL_NPOLL - / 2); - fine_digital_offset[result_idx] - /= NPHY_RSSICAL_NPOLL; - fine_digital_offset[result_idx] - = -fine_digital_offset - [result_idx]; - } else { - fine_digital_offset[result_idx] - += (NPHY_RSSICAL_NPOLL - / 2); - fine_digital_offset[result_idx] - /= NPHY_RSSICAL_NPOLL; - } - - wlc_phy_scale_offset_rssi_nphy( - pi, 0x0, - (s8) - fine_digital_offset - [core * - 2], - (core == PHY_CORE_0) ? - RADIO_MIMO_CORESEL_CORE1 : - RADIO_MIMO_CORESEL_CORE2, - (result_idx % 2 == 0) ? - NPHY_RAIL_I : - NPHY_RAIL_Q, - rssi_type); - } - } - - } - } - - write_phy_reg(pi, 0x91, NPHY_Rfctrlintc1_save); - write_phy_reg(pi, 0x92, NPHY_Rfctrlintc2_save); - - wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); - - mod_phy_reg(pi, 0xe7, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0x78, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0xe7, (0x1 << 0), 0); - - mod_phy_reg(pi, 0xec, (0x1 << 0), 1 << 0); - mod_phy_reg(pi, 0x78, (0x1 << 1), 1 << 1); - mod_phy_reg(pi, 0xec, (0x1 << 0), 0); - - write_phy_reg(pi, 0x8f, NPHY_AfectrlOverride1_save); - write_phy_reg(pi, 0xa5, NPHY_AfectrlOverride2_save); - write_phy_reg(pi, 0xa6, NPHY_AfectrlCore1_save); - write_phy_reg(pi, 0xa7, NPHY_AfectrlCore2_save); - write_phy_reg(pi, 0xe7, NPHY_RfctrlOverride0_save); - write_phy_reg(pi, 0xec, NPHY_RfctrlOverride1_save); - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - write_phy_reg(pi, 0x342, NPHY_REV7_RfctrlOverride3_save); - write_phy_reg(pi, 0x343, NPHY_REV7_RfctrlOverride4_save); - write_phy_reg(pi, 0x346, NPHY_REV7_RfctrlOverride5_save); - write_phy_reg(pi, 0x347, NPHY_REV7_RfctrlOverride6_save); - } - write_phy_reg(pi, 0xe5, NPHY_RfctrlOverrideAux0_save); - write_phy_reg(pi, 0xe6, NPHY_RfctrlOverrideAux1_save); - write_phy_reg(pi, 0x78, NPHY_RfctrlCmd_save); - write_phy_reg(pi, 0xf9, NPHY_RfctrlMiscReg1_save); - write_phy_reg(pi, 0xfb, NPHY_RfctrlMiscReg2_save); - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - write_phy_reg(pi, 0x340, NPHY_REV7_RfctrlMiscReg3_save); - write_phy_reg(pi, 0x341, NPHY_REV7_RfctrlMiscReg4_save); - write_phy_reg(pi, 0x344, NPHY_REV7_RfctrlMiscReg5_save); - write_phy_reg(pi, 0x345, NPHY_REV7_RfctrlMiscReg6_save); - } - write_phy_reg(pi, 0x7a, NPHY_RfctrlRSSIOTHERS1_save); - write_phy_reg(pi, 0x7d, NPHY_RfctrlRSSIOTHERS2_save); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - pi->rssical_cache.rssical_radio_regs_2G[0] = - read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0); - pi->rssical_cache.rssical_radio_regs_2G[1] = - read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1); - } else { - pi->rssical_cache.rssical_radio_regs_2G[0] = - read_radio_reg(pi, - RADIO_2056_RX_RSSI_MISC | - RADIO_2056_RX0); - pi->rssical_cache.rssical_radio_regs_2G[1] = - read_radio_reg(pi, - RADIO_2056_RX_RSSI_MISC | - RADIO_2056_RX1); - } - - pi->rssical_cache.rssical_phyregs_2G[0] = - read_phy_reg(pi, 0x1a6); - pi->rssical_cache.rssical_phyregs_2G[1] = - read_phy_reg(pi, 0x1ac); - pi->rssical_cache.rssical_phyregs_2G[2] = - read_phy_reg(pi, 0x1b2); - pi->rssical_cache.rssical_phyregs_2G[3] = - read_phy_reg(pi, 0x1b8); - pi->rssical_cache.rssical_phyregs_2G[4] = - read_phy_reg(pi, 0x1a4); - pi->rssical_cache.rssical_phyregs_2G[5] = - read_phy_reg(pi, 0x1aa); - pi->rssical_cache.rssical_phyregs_2G[6] = - read_phy_reg(pi, 0x1b0); - pi->rssical_cache.rssical_phyregs_2G[7] = - read_phy_reg(pi, 0x1b6); - pi->rssical_cache.rssical_phyregs_2G[8] = - read_phy_reg(pi, 0x1a5); - pi->rssical_cache.rssical_phyregs_2G[9] = - read_phy_reg(pi, 0x1ab); - pi->rssical_cache.rssical_phyregs_2G[10] = - read_phy_reg(pi, 0x1b1); - pi->rssical_cache.rssical_phyregs_2G[11] = - read_phy_reg(pi, 0x1b7); - - pi->nphy_rssical_chanspec_2G = pi->radio_chanspec; - } else { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - pi->rssical_cache.rssical_radio_regs_5G[0] = - read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0); - pi->rssical_cache.rssical_radio_regs_5G[1] = - read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1); - } else { - pi->rssical_cache.rssical_radio_regs_5G[0] = - read_radio_reg(pi, - RADIO_2056_RX_RSSI_MISC | - RADIO_2056_RX0); - pi->rssical_cache.rssical_radio_regs_5G[1] = - read_radio_reg(pi, - RADIO_2056_RX_RSSI_MISC | - RADIO_2056_RX1); - } - - pi->rssical_cache.rssical_phyregs_5G[0] = - read_phy_reg(pi, 0x1a6); - pi->rssical_cache.rssical_phyregs_5G[1] = - read_phy_reg(pi, 0x1ac); - pi->rssical_cache.rssical_phyregs_5G[2] = - read_phy_reg(pi, 0x1b2); - pi->rssical_cache.rssical_phyregs_5G[3] = - read_phy_reg(pi, 0x1b8); - pi->rssical_cache.rssical_phyregs_5G[4] = - read_phy_reg(pi, 0x1a4); - pi->rssical_cache.rssical_phyregs_5G[5] = - read_phy_reg(pi, 0x1aa); - pi->rssical_cache.rssical_phyregs_5G[6] = - read_phy_reg(pi, 0x1b0); - pi->rssical_cache.rssical_phyregs_5G[7] = - read_phy_reg(pi, 0x1b6); - pi->rssical_cache.rssical_phyregs_5G[8] = - read_phy_reg(pi, 0x1a5); - pi->rssical_cache.rssical_phyregs_5G[9] = - read_phy_reg(pi, 0x1ab); - pi->rssical_cache.rssical_phyregs_5G[10] = - read_phy_reg(pi, 0x1b1); - pi->rssical_cache.rssical_phyregs_5G[11] = - read_phy_reg(pi, 0x1b7); - - pi->nphy_rssical_chanspec_5G = pi->radio_chanspec; - } - - wlc_phy_classifier_nphy(pi, (0x7 << 0), classif_state); - wlc_phy_clip_det_nphy(pi, 1, clip_state); -} - -static void wlc_phy_rssi_cal_nphy_rev2(struct brcms_phy *pi, u8 rssi_type) -{ - s32 target_code; - u16 classif_state; - u16 clip_state[2]; - u16 rssi_ctrl_state[2], pd_state[2]; - u16 rfctrlintc_state[2], rfpdcorerxtx_state[2]; - u16 rfctrlintc_override_val; - u16 clip_off[] = { 0xffff, 0xffff }; - u16 rf_pd_val, pd_mask, rssi_ctrl_mask; - u8 vcm, min_vcm, vcm_tmp[4]; - u8 vcm_final[4] = { 0, 0, 0, 0 }; - u8 result_idx, ctr; - s32 poll_results[4][4] = { - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0} - }; - s32 poll_miniq[4][2] = { - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} - }; - s32 min_d, curr_d; - s32 fine_digital_offset[4]; - s32 poll_results_min[4] = { 0, 0, 0, 0 }; - s32 min_poll; - - switch (rssi_type) { - case NPHY_RSSI_SEL_NB: - target_code = NPHY_RSSICAL_NB_TARGET; - break; - case NPHY_RSSI_SEL_W1: - target_code = NPHY_RSSICAL_W1_TARGET; - break; - case NPHY_RSSI_SEL_W2: - target_code = NPHY_RSSICAL_W2_TARGET; - break; - default: - return; - } - - classif_state = wlc_phy_classifier_nphy(pi, 0, 0); - wlc_phy_classifier_nphy(pi, (0x7 << 0), 4); - wlc_phy_clip_det_nphy(pi, 0, clip_state); - wlc_phy_clip_det_nphy(pi, 1, clip_off); - - rf_pd_val = (rssi_type == NPHY_RSSI_SEL_NB) ? 0x6 : 0x4; - rfctrlintc_override_val = - CHSPEC_IS5G(pi->radio_chanspec) ? 0x140 : 0x110; - - rfctrlintc_state[0] = read_phy_reg(pi, 0x91); - rfpdcorerxtx_state[0] = read_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX); - write_phy_reg(pi, 0x91, rfctrlintc_override_val); - write_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX, rf_pd_val); - - rfctrlintc_state[1] = read_phy_reg(pi, 0x92); - rfpdcorerxtx_state[1] = read_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX); - write_phy_reg(pi, 0x92, rfctrlintc_override_val); - write_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX, rf_pd_val); - - pd_mask = RADIO_2055_NBRSSI_PD | RADIO_2055_WBRSSI_G1_PD | - RADIO_2055_WBRSSI_G2_PD; - pd_state[0] = - read_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC) & pd_mask; - pd_state[1] = - read_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC) & pd_mask; - mod_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC, pd_mask, 0); - mod_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC, pd_mask, 0); - rssi_ctrl_mask = RADIO_2055_NBRSSI_SEL | RADIO_2055_WBRSSI_G1_SEL | - RADIO_2055_WBRSSI_G2_SEL; - rssi_ctrl_state[0] = - read_radio_reg(pi, RADIO_2055_SP_RSSI_CORE1) & rssi_ctrl_mask; - rssi_ctrl_state[1] = - read_radio_reg(pi, RADIO_2055_SP_RSSI_CORE2) & rssi_ctrl_mask; - wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_ALLRX, rssi_type); - - wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, RADIO_MIMO_CORESEL_ALLRX, - NPHY_RAIL_I, rssi_type); - wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, RADIO_MIMO_CORESEL_ALLRX, - NPHY_RAIL_Q, rssi_type); - - for (vcm = 0; vcm < 4; vcm++) { - - vcm_tmp[0] = vcm_tmp[1] = vcm_tmp[2] = vcm_tmp[3] = vcm; - if (rssi_type != NPHY_RSSI_SEL_W2) - wlc_phy_set_rssi_2055_vcm(pi, rssi_type, vcm_tmp); - - wlc_phy_poll_rssi_nphy(pi, rssi_type, &poll_results[vcm][0], - NPHY_RSSICAL_NPOLL); - - if ((rssi_type == NPHY_RSSI_SEL_W1) - || (rssi_type == NPHY_RSSI_SEL_W2)) { - for (ctr = 0; ctr < 2; ctr++) - poll_miniq[vcm][ctr] = - min(poll_results[vcm][ctr * 2 + 0], - poll_results[vcm][ctr * 2 + 1]); - } - } - - for (result_idx = 0; result_idx < 4; result_idx++) { - min_d = NPHY_RSSICAL_MAXD; - min_vcm = 0; - min_poll = NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL + 1; - for (vcm = 0; vcm < 4; vcm++) { - curr_d = abs(((rssi_type == NPHY_RSSI_SEL_NB) ? - poll_results[vcm][result_idx] : - poll_miniq[vcm][result_idx / 2]) - - (target_code * NPHY_RSSICAL_NPOLL)); - if (curr_d < min_d) { - min_d = curr_d; - min_vcm = vcm; - } - if (poll_results[vcm][result_idx] < min_poll) - min_poll = poll_results[vcm][result_idx]; - } - vcm_final[result_idx] = min_vcm; - poll_results_min[result_idx] = min_poll; - } - - if (rssi_type != NPHY_RSSI_SEL_W2) - wlc_phy_set_rssi_2055_vcm(pi, rssi_type, vcm_final); - - for (result_idx = 0; result_idx < 4; result_idx++) { - fine_digital_offset[result_idx] = - (target_code * NPHY_RSSICAL_NPOLL) - - poll_results[vcm_final[result_idx]][result_idx]; - if (fine_digital_offset[result_idx] < 0) { - fine_digital_offset[result_idx] = - abs(fine_digital_offset[result_idx]); - fine_digital_offset[result_idx] += - (NPHY_RSSICAL_NPOLL / 2); - fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL; - fine_digital_offset[result_idx] = - -fine_digital_offset[result_idx]; - } else { - fine_digital_offset[result_idx] += - (NPHY_RSSICAL_NPOLL / 2); - fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL; - } - - if (poll_results_min[result_idx] == - NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL) - fine_digital_offset[result_idx] = - (target_code - NPHY_RSSICAL_MAXREAD - 1); - - wlc_phy_scale_offset_rssi_nphy(pi, 0x0, - (s8) - fine_digital_offset[result_idx], - (result_idx / 2 == - 0) ? RADIO_MIMO_CORESEL_CORE1 : - RADIO_MIMO_CORESEL_CORE2, - (result_idx % 2 == - 0) ? NPHY_RAIL_I : NPHY_RAIL_Q, - rssi_type); - } - - mod_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC, pd_mask, pd_state[0]); - mod_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC, pd_mask, pd_state[1]); - if (rssi_ctrl_state[0] == RADIO_2055_NBRSSI_SEL) - wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1, - NPHY_RSSI_SEL_NB); - else if (rssi_ctrl_state[0] == RADIO_2055_WBRSSI_G1_SEL) - wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1, - NPHY_RSSI_SEL_W1); - else /* RADIO_2055_WBRSSI_G2_SEL */ - wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1, - NPHY_RSSI_SEL_W2); - if (rssi_ctrl_state[1] == RADIO_2055_NBRSSI_SEL) - wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2, - NPHY_RSSI_SEL_NB); - else if (rssi_ctrl_state[1] == RADIO_2055_WBRSSI_G1_SEL) - wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2, - NPHY_RSSI_SEL_W1); - else /* RADIO_2055_WBRSSI_G1_SEL */ - wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2, - NPHY_RSSI_SEL_W2); - wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_OFF, rssi_type); - - write_phy_reg(pi, 0x91, rfctrlintc_state[0]); - write_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX, rfpdcorerxtx_state[0]); - write_phy_reg(pi, 0x92, rfctrlintc_state[1]); - write_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX, rfpdcorerxtx_state[1]); - - wlc_phy_classifier_nphy(pi, (0x7 << 0), classif_state); - wlc_phy_clip_det_nphy(pi, 1, clip_state); - - wlc_phy_resetcca_nphy(pi); -} - -void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi) -{ - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - wlc_phy_rssi_cal_nphy_rev3(pi); - } else { - wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_NB); - wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_W1); - wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_W2); - } -} - -int -wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, struct d11rxhdr *rxh) -{ - s16 rxpwr, rxpwr0, rxpwr1; - s16 phyRx0_l, phyRx2_l; - - rxpwr = 0; - rxpwr0 = rxh->PhyRxStatus_1 & PRXS1_nphy_PWR0_MASK; - rxpwr1 = (rxh->PhyRxStatus_1 & PRXS1_nphy_PWR1_MASK) >> 8; - - if (rxpwr0 > 127) - rxpwr0 -= 256; - if (rxpwr1 > 127) - rxpwr1 -= 256; - - phyRx0_l = rxh->PhyRxStatus_0 & 0x00ff; - phyRx2_l = rxh->PhyRxStatus_2 & 0x00ff; - if (phyRx2_l > 127) - phyRx2_l -= 256; - - if (((rxpwr0 == 16) || (rxpwr0 == 32))) { - rxpwr0 = rxpwr1; - rxpwr1 = phyRx2_l; - } - - if (pi->sh->rssi_mode == RSSI_ANT_MERGE_MAX) - rxpwr = (rxpwr0 > rxpwr1) ? rxpwr0 : rxpwr1; - else if (pi->sh->rssi_mode == RSSI_ANT_MERGE_MIN) - rxpwr = (rxpwr0 < rxpwr1) ? rxpwr0 : rxpwr1; - else if (pi->sh->rssi_mode == RSSI_ANT_MERGE_AVG) - rxpwr = (rxpwr0 + rxpwr1) >> 1; - - return rxpwr; -} - -static void -wlc_phy_loadsampletable_nphy(struct brcms_phy *pi, struct cordic_iq *tone_buf, - u16 num_samps) -{ - u16 t; - u32 *data_buf = NULL; - - data_buf = kmalloc(sizeof(u32) * num_samps, GFP_ATOMIC); - if (data_buf == NULL) - return; - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - for (t = 0; t < num_samps; t++) - data_buf[t] = ((((unsigned int)tone_buf[t].i) & 0x3ff) << 10) | - (((unsigned int)tone_buf[t].q) & 0x3ff); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SAMPLEPLAY, num_samps, 0, 32, - data_buf); - - kfree(data_buf); - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -static u16 -wlc_phy_gen_load_samples_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, - u8 dac_test_mode) -{ - u8 phy_bw, is_phybw40; - u16 num_samps, t, spur; - s32 theta = 0, rot = 0; - u32 tbl_len; - struct cordic_iq *tone_buf = NULL; - - is_phybw40 = CHSPEC_IS40(pi->radio_chanspec); - phy_bw = (is_phybw40 == 1) ? 40 : 20; - tbl_len = (phy_bw << 3); - - if (dac_test_mode == 1) { - spur = read_phy_reg(pi, 0x01); - spur = (spur >> 15) & 1; - phy_bw = (spur == 1) ? 82 : 80; - phy_bw = (is_phybw40 == 1) ? (phy_bw << 1) : phy_bw; - - tbl_len = (phy_bw << 1); - } - - tone_buf = kmalloc(sizeof(struct cordic_iq) * tbl_len, GFP_ATOMIC); - if (tone_buf == NULL) - return 0; - - num_samps = (u16) tbl_len; - rot = ((f_kHz * 36) / phy_bw) / 100; - theta = 0; - - for (t = 0; t < num_samps; t++) { - - tone_buf[t] = cordic_calc_iq(theta); - - theta += rot; - - tone_buf[t].q = (s32) FLOAT(tone_buf[t].q * max_val); - tone_buf[t].i = (s32) FLOAT(tone_buf[t].i * max_val); - } - - wlc_phy_loadsampletable_nphy(pi, tone_buf, num_samps); - - kfree(tone_buf); - - return num_samps; -} - -static void -wlc_phy_runsamples_nphy(struct brcms_phy *pi, u16 num_samps, u16 loops, - u16 wait, u8 iqmode, u8 dac_test_mode, - bool modify_bbmult) -{ - u16 bb_mult; - u8 phy_bw, sample_cmd; - u16 orig_RfseqCoreActv; - u16 lpf_bw_ctl_override3, lpf_bw_ctl_override4, lpf_bw_ctl_miscreg3, - lpf_bw_ctl_miscreg4; - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - phy_bw = 20; - if (CHSPEC_IS40(pi->radio_chanspec)) - phy_bw = 40; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - lpf_bw_ctl_override3 = read_phy_reg(pi, 0x342) & (0x1 << 7); - lpf_bw_ctl_override4 = read_phy_reg(pi, 0x343) & (0x1 << 7); - if (lpf_bw_ctl_override3 | lpf_bw_ctl_override4) { - lpf_bw_ctl_miscreg3 = read_phy_reg(pi, 0x340) & - (0x7 << 8); - lpf_bw_ctl_miscreg4 = read_phy_reg(pi, 0x341) & - (0x7 << 8); - } else { - wlc_phy_rfctrl_override_nphy_rev7( - pi, - (0x1 << 7), - wlc_phy_read_lpf_bw_ctl_nphy - (pi, - 0), 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - - pi->nphy_sample_play_lpf_bw_ctl_ovr = true; - - lpf_bw_ctl_miscreg3 = read_phy_reg(pi, 0x340) & - (0x7 << 8); - lpf_bw_ctl_miscreg4 = read_phy_reg(pi, 0x341) & - (0x7 << 8); - } - } - - if ((pi->nphy_bb_mult_save & BB_MULT_VALID_MASK) == 0) { - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 87, 16, - &bb_mult); - pi->nphy_bb_mult_save = - BB_MULT_VALID_MASK | (bb_mult & BB_MULT_MASK); - } - - if (modify_bbmult) { - bb_mult = (phy_bw == 20) ? 100 : 71; - bb_mult = (bb_mult << 8) + bb_mult; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 87, 16, - &bb_mult); - } - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); - - write_phy_reg(pi, 0xc6, num_samps - 1); - - if (loops != 0xffff) - write_phy_reg(pi, 0xc4, loops - 1); - else - write_phy_reg(pi, 0xc4, loops); - - write_phy_reg(pi, 0xc5, wait); - - orig_RfseqCoreActv = read_phy_reg(pi, 0xa1); - or_phy_reg(pi, 0xa1, NPHY_RfseqMode_CoreActv_override); - if (iqmode) { - - and_phy_reg(pi, 0xc2, 0x7FFF); - - or_phy_reg(pi, 0xc2, 0x8000); - } else { - - sample_cmd = (dac_test_mode == 1) ? 0x5 : 0x1; - write_phy_reg(pi, 0xc3, sample_cmd); - } - - SPINWAIT(((read_phy_reg(pi, 0xa4) & 0x1) == 1), 1000); - - write_phy_reg(pi, 0xa1, orig_RfseqCoreActv); -} - -int -wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, - u8 iqmode, u8 dac_test_mode, bool modify_bbmult) -{ - u16 num_samps; - u16 loops = 0xffff; - u16 wait = 0; - - num_samps = wlc_phy_gen_load_samples_nphy(pi, f_kHz, max_val, - dac_test_mode); - if (num_samps == 0) - return -EBADE; - - wlc_phy_runsamples_nphy(pi, num_samps, loops, wait, iqmode, - dac_test_mode, modify_bbmult); - - return 0; -} - -void wlc_phy_stopplayback_nphy(struct brcms_phy *pi) -{ - u16 playback_status; - u16 bb_mult; - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - playback_status = read_phy_reg(pi, 0xc7); - if (playback_status & 0x1) - or_phy_reg(pi, 0xc3, NPHY_sampleCmd_STOP); - else if (playback_status & 0x2) - and_phy_reg(pi, 0xc2, - (u16) ~NPHY_iqloCalCmdGctl_IQLO_CAL_EN); - - and_phy_reg(pi, 0xc3, (u16) ~(0x1 << 2)); - - if ((pi->nphy_bb_mult_save & BB_MULT_VALID_MASK) != 0) { - - bb_mult = pi->nphy_bb_mult_save & BB_MULT_MASK; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 87, 16, - &bb_mult); - - pi->nphy_bb_mult_save = 0; - } - - if (NREV_IS(pi->pubpi.phy_rev, 7) || NREV_GE(pi->pubpi.phy_rev, 8)) { - if (pi->nphy_sample_play_lpf_bw_ctl_ovr) { - wlc_phy_rfctrl_override_nphy_rev7( - pi, - (0x1 << 7), - 0, 0, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - pi->nphy_sample_play_lpf_bw_ctl_ovr = false; - } - } - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -static u32 *brcms_phy_get_tx_pwrctrl_tbl(struct brcms_phy *pi) -{ - u32 *tx_pwrctrl_tbl = NULL; - uint phyrev = pi->pubpi.phy_rev; - - if (PHY_IPA(pi)) { - tx_pwrctrl_tbl = - wlc_phy_get_ipa_gaintbl_nphy(pi); - } else { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - if (NREV_IS(phyrev, 3)) - tx_pwrctrl_tbl = nphy_tpc_5GHz_txgain_rev3; - else if (NREV_IS(phyrev, 4)) - tx_pwrctrl_tbl = - (pi->srom_fem5g.extpagain == 3) ? - nphy_tpc_5GHz_txgain_HiPwrEPA : - nphy_tpc_5GHz_txgain_rev4; - else - tx_pwrctrl_tbl = nphy_tpc_5GHz_txgain_rev5; - } else { - if (NREV_GE(phyrev, 7)) { - if (pi->pubpi.radiorev == 3) - tx_pwrctrl_tbl = - nphy_tpc_txgain_epa_2057rev3; - else if (pi->pubpi.radiorev == 5) - tx_pwrctrl_tbl = - nphy_tpc_txgain_epa_2057rev5; - } else { - if (NREV_GE(phyrev, 5) && - (pi->srom_fem2g.extpagain == 3)) - tx_pwrctrl_tbl = - nphy_tpc_txgain_HiPwrEPA; - else - tx_pwrctrl_tbl = - nphy_tpc_txgain_rev3; - } - } - } - return tx_pwrctrl_tbl; -} - -struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi) -{ - u16 base_idx[2], curr_gain[2]; - u8 core_no; - struct nphy_txgains target_gain; - u32 *tx_pwrctrl_tbl = NULL; - - if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) { - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, - curr_gain); - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); - - for (core_no = 0; core_no < 2; core_no++) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - target_gain.ipa[core_no] = - curr_gain[core_no] & 0x0007; - target_gain.pad[core_no] = - ((curr_gain[core_no] & 0x00F8) >> 3); - target_gain.pga[core_no] = - ((curr_gain[core_no] & 0x0F00) >> 8); - target_gain.txgm[core_no] = - ((curr_gain[core_no] & 0x7000) >> 12); - target_gain.txlpf[core_no] = - ((curr_gain[core_no] & 0x8000) >> 15); - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - target_gain.ipa[core_no] = - curr_gain[core_no] & 0x000F; - target_gain.pad[core_no] = - ((curr_gain[core_no] & 0x00F0) >> 4); - target_gain.pga[core_no] = - ((curr_gain[core_no] & 0x0F00) >> 8); - target_gain.txgm[core_no] = - ((curr_gain[core_no] & 0x7000) >> 12); - } else { - target_gain.ipa[core_no] = - curr_gain[core_no] & 0x0003; - target_gain.pad[core_no] = - ((curr_gain[core_no] & 0x000C) >> 2); - target_gain.pga[core_no] = - ((curr_gain[core_no] & 0x0070) >> 4); - target_gain.txgm[core_no] = - ((curr_gain[core_no] & 0x0380) >> 7); - } - } - } else { - uint phyrev = pi->pubpi.phy_rev; - - base_idx[0] = (read_phy_reg(pi, 0x1ed) >> 8) & 0x7f; - base_idx[1] = (read_phy_reg(pi, 0x1ee) >> 8) & 0x7f; - for (core_no = 0; core_no < 2; core_no++) { - if (NREV_GE(phyrev, 3)) { - tx_pwrctrl_tbl = - brcms_phy_get_tx_pwrctrl_tbl(pi); - if (NREV_GE(phyrev, 7)) { - target_gain.ipa[core_no] = - (tx_pwrctrl_tbl - [base_idx[core_no]] - >> 16) & 0x7; - target_gain.pad[core_no] = - (tx_pwrctrl_tbl - [base_idx[core_no]] - >> 19) & 0x1f; - target_gain.pga[core_no] = - (tx_pwrctrl_tbl - [base_idx[core_no]] - >> 24) & 0xf; - target_gain.txgm[core_no] = - (tx_pwrctrl_tbl - [base_idx[core_no]] - >> 28) & 0x7; - target_gain.txlpf[core_no] = - (tx_pwrctrl_tbl - [base_idx[core_no]] - >> 31) & 0x1; - } else { - target_gain.ipa[core_no] = - (tx_pwrctrl_tbl - [base_idx[core_no]] - >> 16) & 0xf; - target_gain.pad[core_no] = - (tx_pwrctrl_tbl - [base_idx[core_no]] - >> 20) & 0xf; - target_gain.pga[core_no] = - (tx_pwrctrl_tbl - [base_idx[core_no]] - >> 24) & 0xf; - target_gain.txgm[core_no] = - (tx_pwrctrl_tbl - [base_idx[core_no]] - >> 28) & 0x7; - } - } else { - target_gain.ipa[core_no] = - (nphy_tpc_txgain[base_idx[core_no]] >> - 16) & 0x3; - target_gain.pad[core_no] = - (nphy_tpc_txgain[base_idx[core_no]] >> - 18) & 0x3; - target_gain.pga[core_no] = - (nphy_tpc_txgain[base_idx[core_no]] >> - 20) & 0x7; - target_gain.txgm[core_no] = - (nphy_tpc_txgain[base_idx[core_no]] >> - 23) & 0x7; - } - } - } - - return target_gain; -} - -static void -wlc_phy_iqcal_gainparams_nphy(struct brcms_phy *pi, u16 core_no, - struct nphy_txgains target_gain, - struct nphy_iqcal_params *params) -{ - u8 k; - int idx; - u16 gain_index; - u8 band_idx = (CHSPEC_IS5G(pi->radio_chanspec) ? 1 : 0); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) - params->txlpf = target_gain.txlpf[core_no]; - - params->txgm = target_gain.txgm[core_no]; - params->pga = target_gain.pga[core_no]; - params->pad = target_gain.pad[core_no]; - params->ipa = target_gain.ipa[core_no]; - if (NREV_GE(pi->pubpi.phy_rev, 7)) - params->cal_gain = - ((params->txlpf << 15) | (params->txgm << 12) | - (params->pga << 8) | - (params->pad << 3) | (params->ipa)); - else - params->cal_gain = - ((params->txgm << 12) | (params->pga << 8) | - (params->pad << 4) | (params->ipa)); - - params->ncorr[0] = 0x79; - params->ncorr[1] = 0x79; - params->ncorr[2] = 0x79; - params->ncorr[3] = 0x79; - params->ncorr[4] = 0x79; - } else { - - gain_index = ((target_gain.pad[core_no] << 0) | - (target_gain.pga[core_no] << 4) | - (target_gain.txgm[core_no] << 8)); - - idx = -1; - for (k = 0; k < NPHY_IQCAL_NUMGAINS; k++) { - if (tbl_iqcal_gainparams_nphy[band_idx][k][0] == - gain_index) { - idx = k; - break; - } - } - - params->txgm = tbl_iqcal_gainparams_nphy[band_idx][k][1]; - params->pga = tbl_iqcal_gainparams_nphy[band_idx][k][2]; - params->pad = tbl_iqcal_gainparams_nphy[band_idx][k][3]; - params->cal_gain = ((params->txgm << 7) | (params->pga << 4) | - (params->pad << 2)); - params->ncorr[0] = tbl_iqcal_gainparams_nphy[band_idx][k][4]; - params->ncorr[1] = tbl_iqcal_gainparams_nphy[band_idx][k][5]; - params->ncorr[2] = tbl_iqcal_gainparams_nphy[band_idx][k][6]; - params->ncorr[3] = tbl_iqcal_gainparams_nphy[band_idx][k][7]; - } -} - -static void wlc_phy_txcal_radio_setup_nphy(struct brcms_phy *pi) -{ - u16 jtag_core, core; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - for (core = 0; core <= 1; core++) { - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 0] = - READ_RADIO_REG3(pi, RADIO_2057, TX, core, - TX_SSI_MASTER); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 1] = - READ_RADIO_REG3(pi, RADIO_2057, TX, core, - IQCAL_VCM_HG); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 2] = - READ_RADIO_REG3(pi, RADIO_2057, TX, core, - IQCAL_IDAC); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 3] = - READ_RADIO_REG3(pi, RADIO_2057, TX, core, - TSSI_VCM); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 4] = 0; - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 5] = - READ_RADIO_REG3(pi, RADIO_2057, TX, core, - TX_SSI_MUX); - - if (pi->pubpi.radiorev != 5) - pi->tx_rx_cal_radio_saveregs[(core * 11) + 6] = - READ_RADIO_REG3(pi, RADIO_2057, TX, - core, - TSSIA); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 7] = - READ_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 8] = - READ_RADIO_REG3(pi, RADIO_2057, TX, core, - TSSI_MISC1); - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TX_SSI_MASTER, 0x0a); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - IQCAL_VCM_HG, 0x43); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - IQCAL_IDAC, 0x55); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TSSI_VCM, 0x00); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TSSIG, 0x00); - if (pi->use_int_tx_iqlo_cal_nphy) { - WRITE_RADIO_REG3(pi, RADIO_2057, TX, - core, TX_SSI_MUX, 0x4); - if (!(pi-> - internal_tx_iqlo_cal_tapoff_intpa_nphy)) - WRITE_RADIO_REG3(pi, RADIO_2057, - TX, core, - TSSIA, 0x31); - else - WRITE_RADIO_REG3(pi, RADIO_2057, - TX, core, - TSSIA, 0x21); - } - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TSSI_MISC1, 0x00); - } else { - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TX_SSI_MASTER, 0x06); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - IQCAL_VCM_HG, 0x43); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - IQCAL_IDAC, 0x55); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TSSI_VCM, 0x00); - - if (pi->pubpi.radiorev != 5) - WRITE_RADIO_REG3(pi, RADIO_2057, TX, - core, TSSIA, 0x00); - if (pi->use_int_tx_iqlo_cal_nphy) { - WRITE_RADIO_REG3(pi, RADIO_2057, TX, - core, TX_SSI_MUX, - 0x06); - if (!(pi-> - internal_tx_iqlo_cal_tapoff_intpa_nphy)) - WRITE_RADIO_REG3(pi, RADIO_2057, - TX, core, - TSSIG, 0x31); - else - WRITE_RADIO_REG3(pi, RADIO_2057, - TX, core, - TSSIG, 0x21); - } - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TSSI_MISC1, 0x00); - } - } - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - - for (core = 0; core <= 1; core++) { - jtag_core = - (core == - PHY_CORE_0) ? RADIO_2056_TX0 : RADIO_2056_TX1; - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 0] = - read_radio_reg(pi, - RADIO_2056_TX_TX_SSI_MASTER | - jtag_core); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 1] = - read_radio_reg(pi, - RADIO_2056_TX_IQCAL_VCM_HG | - jtag_core); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 2] = - read_radio_reg(pi, - RADIO_2056_TX_IQCAL_IDAC | - jtag_core); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 3] = - read_radio_reg( - pi, - RADIO_2056_TX_TSSI_VCM | - jtag_core); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 4] = - read_radio_reg(pi, - RADIO_2056_TX_TX_AMP_DET | - jtag_core); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 5] = - read_radio_reg(pi, - RADIO_2056_TX_TX_SSI_MUX | - jtag_core); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 6] = - read_radio_reg(pi, - RADIO_2056_TX_TSSIA | jtag_core); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 7] = - read_radio_reg(pi, - RADIO_2056_TX_TSSIG | jtag_core); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 8] = - read_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC1 | - jtag_core); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 9] = - read_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC2 | - jtag_core); - - pi->tx_rx_cal_radio_saveregs[(core * 11) + 10] = - read_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC3 | - jtag_core); - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - write_radio_reg(pi, - RADIO_2056_TX_TX_SSI_MASTER | - jtag_core, 0x0a); - write_radio_reg(pi, - RADIO_2056_TX_IQCAL_VCM_HG | - jtag_core, 0x40); - write_radio_reg(pi, - RADIO_2056_TX_IQCAL_IDAC | - jtag_core, 0x55); - write_radio_reg(pi, - RADIO_2056_TX_TSSI_VCM | - jtag_core, 0x00); - write_radio_reg(pi, - RADIO_2056_TX_TX_AMP_DET | - jtag_core, 0x00); - - if (PHY_IPA(pi)) { - write_radio_reg( - pi, - RADIO_2056_TX_TX_SSI_MUX - | jtag_core, 0x4); - write_radio_reg(pi, - RADIO_2056_TX_TSSIA | - jtag_core, 0x1); - } else { - write_radio_reg( - pi, - RADIO_2056_TX_TX_SSI_MUX - | jtag_core, 0x00); - write_radio_reg(pi, - RADIO_2056_TX_TSSIA | - jtag_core, 0x2f); - } - write_radio_reg(pi, - RADIO_2056_TX_TSSIG | jtag_core, - 0x00); - write_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC1 | - jtag_core, 0x00); - - write_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC2 | - jtag_core, 0x00); - write_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC3 | - jtag_core, 0x00); - } else { - write_radio_reg(pi, - RADIO_2056_TX_TX_SSI_MASTER | - jtag_core, 0x06); - write_radio_reg(pi, - RADIO_2056_TX_IQCAL_VCM_HG | - jtag_core, 0x40); - write_radio_reg(pi, - RADIO_2056_TX_IQCAL_IDAC | - jtag_core, 0x55); - write_radio_reg(pi, - RADIO_2056_TX_TSSI_VCM | - jtag_core, 0x00); - write_radio_reg(pi, - RADIO_2056_TX_TX_AMP_DET | - jtag_core, 0x00); - write_radio_reg(pi, - RADIO_2056_TX_TSSIA | jtag_core, - 0x00); - - if (PHY_IPA(pi)) { - - write_radio_reg( - pi, - RADIO_2056_TX_TX_SSI_MUX - | jtag_core, 0x06); - if (NREV_LT(pi->pubpi.phy_rev, 5)) - write_radio_reg( - pi, - RADIO_2056_TX_TSSIG - | jtag_core, - 0x11); - else - write_radio_reg( - pi, - RADIO_2056_TX_TSSIG - | jtag_core, - 0x1); - } else { - write_radio_reg( - pi, - RADIO_2056_TX_TX_SSI_MUX - | jtag_core, 0x00); - write_radio_reg(pi, - RADIO_2056_TX_TSSIG | - jtag_core, 0x20); - } - - write_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC1 | - jtag_core, 0x00); - write_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC2 | - jtag_core, 0x00); - write_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC3 | - jtag_core, 0x00); - } - } - } else { - - pi->tx_rx_cal_radio_saveregs[0] = - read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1); - write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, 0x29); - pi->tx_rx_cal_radio_saveregs[1] = - read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2); - write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, 0x54); - - pi->tx_rx_cal_radio_saveregs[2] = - read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1); - write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, 0x29); - pi->tx_rx_cal_radio_saveregs[3] = - read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2); - write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, 0x54); - - pi->tx_rx_cal_radio_saveregs[4] = - read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1); - pi->tx_rx_cal_radio_saveregs[5] = - read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2); - - if ((read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand) == - 0) { - - write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, 0x04); - write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, 0x04); - } else { - - write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, 0x20); - write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, 0x20); - } - - if (NREV_LT(pi->pubpi.phy_rev, 2)) { - - or_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, 0x20); - or_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, 0x20); - } else { - - and_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, 0xdf); - and_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, 0xdf); - } - } -} - -static void wlc_phy_txcal_radio_cleanup_nphy(struct brcms_phy *pi) -{ - u16 jtag_core, core; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - for (core = 0; core <= 1; core++) { - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TX_SSI_MASTER, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 0]); - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_VCM_HG, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 1]); - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_IDAC, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 2]); - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 3]); - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MUX, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 5]); - - if (pi->pubpi.radiorev != 5) - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TSSIA, - pi->tx_rx_cal_radio_saveregs - [(core * 11) + 6]); - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 7]); - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_MISC1, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 8]); - } - } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { - for (core = 0; core <= 1; core++) { - jtag_core = (core == PHY_CORE_0) ? - RADIO_2056_TX0 : RADIO_2056_TX1; - - write_radio_reg(pi, - RADIO_2056_TX_TX_SSI_MASTER | jtag_core, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 0]); - - write_radio_reg(pi, - RADIO_2056_TX_IQCAL_VCM_HG | jtag_core, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 1]); - - write_radio_reg(pi, - RADIO_2056_TX_IQCAL_IDAC | jtag_core, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 2]); - - write_radio_reg(pi, RADIO_2056_TX_TSSI_VCM | jtag_core, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 3]); - - write_radio_reg(pi, - RADIO_2056_TX_TX_AMP_DET | jtag_core, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 4]); - - write_radio_reg(pi, - RADIO_2056_TX_TX_SSI_MUX | jtag_core, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 5]); - - write_radio_reg(pi, RADIO_2056_TX_TSSIA | jtag_core, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 6]); - - write_radio_reg(pi, RADIO_2056_TX_TSSIG | jtag_core, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 7]); - - write_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC1 | jtag_core, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 8]); - - write_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC2 | jtag_core, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 9]); - - write_radio_reg(pi, - RADIO_2056_TX_TSSI_MISC3 | jtag_core, - pi-> - tx_rx_cal_radio_saveregs[(core * 11) + - 10]); - } - } else { - - write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, - pi->tx_rx_cal_radio_saveregs[0]); - write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, - pi->tx_rx_cal_radio_saveregs[1]); - write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, - pi->tx_rx_cal_radio_saveregs[2]); - write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, - pi->tx_rx_cal_radio_saveregs[3]); - write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, - pi->tx_rx_cal_radio_saveregs[4]); - write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, - pi->tx_rx_cal_radio_saveregs[5]); - } -} - -static void wlc_phy_txcal_physetup_nphy(struct brcms_phy *pi) -{ - u16 val, mask; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - pi->tx_rx_cal_phy_saveregs[0] = read_phy_reg(pi, 0xa6); - pi->tx_rx_cal_phy_saveregs[1] = read_phy_reg(pi, 0xa7); - - mask = ((0x3 << 8) | (0x3 << 10)); - val = (0x2 << 8); - val |= (0x2 << 10); - mod_phy_reg(pi, 0xa6, mask, val); - mod_phy_reg(pi, 0xa7, mask, val); - - val = read_phy_reg(pi, 0x8f); - pi->tx_rx_cal_phy_saveregs[2] = val; - val |= ((0x1 << 9) | (0x1 << 10)); - write_phy_reg(pi, 0x8f, val); - - val = read_phy_reg(pi, 0xa5); - pi->tx_rx_cal_phy_saveregs[3] = val; - val |= ((0x1 << 9) | (0x1 << 10)); - write_phy_reg(pi, 0xa5, val); - - pi->tx_rx_cal_phy_saveregs[4] = read_phy_reg(pi, 0x01); - mod_phy_reg(pi, 0x01, (0x1 << 15), 0); - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 3, 16, - &val); - pi->tx_rx_cal_phy_saveregs[5] = val; - val = 0; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 3, 16, - &val); - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 19, 16, - &val); - pi->tx_rx_cal_phy_saveregs[6] = val; - val = 0; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 19, 16, - &val); - - pi->tx_rx_cal_phy_saveregs[7] = read_phy_reg(pi, 0x91); - pi->tx_rx_cal_phy_saveregs[8] = read_phy_reg(pi, 0x92); - - if (!(pi->use_int_tx_iqlo_cal_nphy)) - wlc_phy_rfctrlintc_override_nphy( - pi, - NPHY_RfctrlIntc_override_PA, - 1, - RADIO_MIMO_CORESEL_CORE1 - | - RADIO_MIMO_CORESEL_CORE2); - else - wlc_phy_rfctrlintc_override_nphy( - pi, - NPHY_RfctrlIntc_override_PA, - 0, - RADIO_MIMO_CORESEL_CORE1 - | - RADIO_MIMO_CORESEL_CORE2); - - wlc_phy_rfctrlintc_override_nphy(pi, - NPHY_RfctrlIntc_override_TRSW, - 0x2, RADIO_MIMO_CORESEL_CORE1); - wlc_phy_rfctrlintc_override_nphy(pi, - NPHY_RfctrlIntc_override_TRSW, - 0x8, RADIO_MIMO_CORESEL_CORE2); - - pi->tx_rx_cal_phy_saveregs[9] = read_phy_reg(pi, 0x297); - pi->tx_rx_cal_phy_saveregs[10] = read_phy_reg(pi, 0x29b); - mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (0) << 0); - - mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (0) << 0); - - if (NREV_IS(pi->pubpi.phy_rev, 7) - || NREV_GE(pi->pubpi.phy_rev, 8)) - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 7), - wlc_phy_read_lpf_bw_ctl_nphy - (pi, - 0), 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - - if (pi->use_int_tx_iqlo_cal_nphy - && !(pi->internal_tx_iqlo_cal_tapoff_intpa_nphy)) { - - if (NREV_IS(pi->pubpi.phy_rev, 7)) { - - mod_radio_reg(pi, RADIO_2057_OVR_REG0, 1 << 4, - 1 << 4); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - mod_radio_reg( - pi, - RADIO_2057_PAD2G_TUNE_PUS_CORE0, - 1, 0); - mod_radio_reg( - pi, - RADIO_2057_PAD2G_TUNE_PUS_CORE1, - 1, 0); - } else { - mod_radio_reg( - pi, - RADIO_2057_IPA5G_CASCOFFV_PU_CORE0, - 1, 0); - mod_radio_reg( - pi, - RADIO_2057_IPA5G_CASCOFFV_PU_CORE1, - 1, 0); - } - } else if (NREV_GE(pi->pubpi.phy_rev, 8)) { - wlc_phy_rfctrl_override_nphy_rev7( - pi, - (0x1 << 3), 0, - 0x3, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - } - } - } else { - pi->tx_rx_cal_phy_saveregs[0] = read_phy_reg(pi, 0xa6); - pi->tx_rx_cal_phy_saveregs[1] = read_phy_reg(pi, 0xa7); - - mask = ((0x3 << 12) | (0x3 << 14)); - val = (0x2 << 12); - val |= (0x2 << 14); - mod_phy_reg(pi, 0xa6, mask, val); - mod_phy_reg(pi, 0xa7, mask, val); - - val = read_phy_reg(pi, 0xa5); - pi->tx_rx_cal_phy_saveregs[2] = val; - val |= ((0x1 << 12) | (0x1 << 13)); - write_phy_reg(pi, 0xa5, val); - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 2, 16, - &val); - pi->tx_rx_cal_phy_saveregs[3] = val; - val |= 0x2000; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 2, 16, - &val); - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 18, 16, - &val); - pi->tx_rx_cal_phy_saveregs[4] = val; - val |= 0x2000; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 18, 16, - &val); - - pi->tx_rx_cal_phy_saveregs[5] = read_phy_reg(pi, 0x91); - pi->tx_rx_cal_phy_saveregs[6] = read_phy_reg(pi, 0x92); - val = CHSPEC_IS5G(pi->radio_chanspec) ? 0x180 : 0x120; - write_phy_reg(pi, 0x91, val); - write_phy_reg(pi, 0x92, val); - } -} - -static void wlc_phy_txcal_phycleanup_nphy(struct brcms_phy *pi) -{ - u16 mask; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - write_phy_reg(pi, 0xa6, pi->tx_rx_cal_phy_saveregs[0]); - write_phy_reg(pi, 0xa7, pi->tx_rx_cal_phy_saveregs[1]); - write_phy_reg(pi, 0x8f, pi->tx_rx_cal_phy_saveregs[2]); - write_phy_reg(pi, 0xa5, pi->tx_rx_cal_phy_saveregs[3]); - write_phy_reg(pi, 0x01, pi->tx_rx_cal_phy_saveregs[4]); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 3, 16, - &pi->tx_rx_cal_phy_saveregs[5]); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 19, 16, - &pi->tx_rx_cal_phy_saveregs[6]); - - write_phy_reg(pi, 0x91, pi->tx_rx_cal_phy_saveregs[7]); - write_phy_reg(pi, 0x92, pi->tx_rx_cal_phy_saveregs[8]); - - write_phy_reg(pi, 0x297, pi->tx_rx_cal_phy_saveregs[9]); - write_phy_reg(pi, 0x29b, pi->tx_rx_cal_phy_saveregs[10]); - - if (NREV_IS(pi->pubpi.phy_rev, 7) - || NREV_GE(pi->pubpi.phy_rev, 8)) - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 7), 0, 0, - 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - - wlc_phy_resetcca_nphy(pi); - - if (pi->use_int_tx_iqlo_cal_nphy - && !(pi->internal_tx_iqlo_cal_tapoff_intpa_nphy)) { - - if (NREV_IS(pi->pubpi.phy_rev, 7)) { - if (CHSPEC_IS2G(pi->radio_chanspec)) { - mod_radio_reg( - pi, - RADIO_2057_PAD2G_TUNE_PUS_CORE0, - 1, 1); - mod_radio_reg( - pi, - RADIO_2057_PAD2G_TUNE_PUS_CORE1, - 1, 1); - } else { - mod_radio_reg( - pi, - RADIO_2057_IPA5G_CASCOFFV_PU_CORE0, - 1, 1); - mod_radio_reg( - pi, - RADIO_2057_IPA5G_CASCOFFV_PU_CORE1, - 1, 1); - } - - mod_radio_reg(pi, RADIO_2057_OVR_REG0, 1 << 4, - 0); - } else if (NREV_GE(pi->pubpi.phy_rev, 8)) { - wlc_phy_rfctrl_override_nphy_rev7( - pi, - (0x1 << 3), 0, - 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - } - } - } else { - mask = ((0x3 << 12) | (0x3 << 14)); - mod_phy_reg(pi, 0xa6, mask, pi->tx_rx_cal_phy_saveregs[0]); - mod_phy_reg(pi, 0xa7, mask, pi->tx_rx_cal_phy_saveregs[1]); - write_phy_reg(pi, 0xa5, pi->tx_rx_cal_phy_saveregs[2]); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 2, 16, - &pi->tx_rx_cal_phy_saveregs[3]); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 18, 16, - &pi->tx_rx_cal_phy_saveregs[4]); - - write_phy_reg(pi, 0x91, pi->tx_rx_cal_phy_saveregs[5]); - write_phy_reg(pi, 0x92, pi->tx_rx_cal_phy_saveregs[6]); - } -} - -void -wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf, u8 num_samps) -{ - u16 tssi_reg; - s32 temp, pwrindex[2]; - s32 idle_tssi[2]; - s32 rssi_buf[4]; - s32 tssival[2]; - u8 tssi_type; - - tssi_reg = read_phy_reg(pi, 0x1e9); - - temp = (s32) (tssi_reg & 0x3f); - idle_tssi[0] = (temp <= 31) ? temp : (temp - 64); - - temp = (s32) ((tssi_reg >> 8) & 0x3f); - idle_tssi[1] = (temp <= 31) ? temp : (temp - 64); - - tssi_type = - CHSPEC_IS5G(pi->radio_chanspec) ? - (u8)NPHY_RSSI_SEL_TSSI_5G : (u8)NPHY_RSSI_SEL_TSSI_2G; - - wlc_phy_poll_rssi_nphy(pi, tssi_type, rssi_buf, num_samps); - - tssival[0] = rssi_buf[0] / ((s32) num_samps); - tssival[1] = rssi_buf[2] / ((s32) num_samps); - - pwrindex[0] = idle_tssi[0] - tssival[0] + 64; - pwrindex[1] = idle_tssi[1] - tssival[1] + 64; - - if (pwrindex[0] < 0) - pwrindex[0] = 0; - else if (pwrindex[0] > 63) - pwrindex[0] = 63; - - if (pwrindex[1] < 0) - pwrindex[1] = 0; - else if (pwrindex[1] > 63) - pwrindex[1] = 63; - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 1, - (u32) pwrindex[0], 32, &qdBm_pwrbuf[0]); - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 1, - (u32) pwrindex[1], 32, &qdBm_pwrbuf[1]); -} - -static void wlc_phy_update_txcal_ladder_nphy(struct brcms_phy *pi, u16 core) -{ - int index; - u32 bbmult_scale; - u16 bbmult; - u16 tblentry; - - struct nphy_txiqcal_ladder ladder_lo[] = { - {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0}, - {25, 0}, {25, 1}, {25, 2}, {25, 3}, {25, 4}, {25, 5}, - {25, 6}, {25, 7}, {35, 7}, {50, 7}, {71, 7}, {100, 7} - }; - - struct nphy_txiqcal_ladder ladder_iq[] = { - {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0}, - {25, 0}, {35, 0}, {50, 0}, {71, 0}, {100, 0}, {100, 1}, - {100, 2}, {100, 3}, {100, 4}, {100, 5}, {100, 6}, {100, 7} - }; - - bbmult = (core == PHY_CORE_0) ? - ((pi->nphy_txcal_bbmult >> 8) & 0xff) : - (pi->nphy_txcal_bbmult & 0xff); - - for (index = 0; index < 18; index++) { - bbmult_scale = ladder_lo[index].percent * bbmult; - bbmult_scale /= 100; - - tblentry = - ((bbmult_scale & 0xff) << 8) | ladder_lo[index].g_env; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, index, 16, - &tblentry); - - bbmult_scale = ladder_iq[index].percent * bbmult; - bbmult_scale /= 100; - - tblentry = - ((bbmult_scale & 0xff) << 8) | ladder_iq[index].g_env; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, index + 32, - 16, &tblentry); - } -} - -static u8 wlc_phy_txpwr_idx_cur_get_nphy(struct brcms_phy *pi, u8 core) -{ - u16 tmp; - tmp = read_phy_reg(pi, ((core == PHY_CORE_0) ? 0x1ed : 0x1ee)); - - tmp = (tmp & (0x7f << 8)) >> 8; - return (u8) tmp; -} - -static void -wlc_phy_txpwr_idx_cur_set_nphy(struct brcms_phy *pi, u8 idx0, u8 idx1) -{ - mod_phy_reg(pi, 0x1e7, (0x7f << 0), idx0); - - if (NREV_GT(pi->pubpi.phy_rev, 1)) - mod_phy_reg(pi, 0x222, (0xff << 0), idx1); -} - -static u16 wlc_phy_ipa_get_bbmult_nphy(struct brcms_phy *pi) -{ - u16 m0m1; - - wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m0m1); - - return m0m1; -} - -static void wlc_phy_ipa_set_bbmult_nphy(struct brcms_phy *pi, u8 m0, u8 m1) -{ - u16 m0m1 = (u16) ((m0 << 8) | m1); - - wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m0m1); - wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &m0m1); -} - -static void -wlc_phy_papd_cal_setup_nphy(struct brcms_phy *pi, - struct nphy_papd_restore_state *state, u8 core) -{ - s32 tone_freq; - u8 off_core; - u16 mixgain = 0; - - off_core = core ^ 0x1; - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - if (NREV_IS(pi->pubpi.phy_rev, 7) - || NREV_GE(pi->pubpi.phy_rev, 8)) - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 7), - wlc_phy_read_lpf_bw_ctl_nphy - (pi, - 0), 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if (pi->pubpi.radiorev == 5) - mixgain = (core == 0) ? 0x20 : 0x00; - else if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) - mixgain = 0x00; - else if ((pi->pubpi.radiorev <= 4) - || (pi->pubpi.radiorev == 6)) - mixgain = 0x00; - } else { - if ((pi->pubpi.radiorev == 4) || - (pi->pubpi.radiorev == 6)) - mixgain = 0x50; - else if ((pi->pubpi.radiorev == 3) - || (pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) - mixgain = 0x0; - } - - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), - mixgain, (1 << core), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - - wlc_phy_rfctrl_override_1tomany_nphy( - pi, - NPHY_REV7_RfctrlOverride_cmd_tx_pu, - 1, (1 << core), 0); - wlc_phy_rfctrl_override_1tomany_nphy( - pi, - NPHY_REV7_RfctrlOverride_cmd_tx_pu, - 0, (1 << off_core), 0); - - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), - 0, 0x3, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 1, - (1 << core), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, - (1 << core), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, - (1 << core), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID2); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 8), 0, - (1 << core), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 1, - (1 << core), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 0, - (1 << core), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 1, - (1 << core), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), - 0, (1 << core), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), 0, - (1 << core), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - - state->afectrl[core] = read_phy_reg(pi, (core == PHY_CORE_0) ? - 0xa6 : 0xa7); - state->afeoverride[core] = - read_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5); - state->afectrl[off_core] = - read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa7 : 0xa6); - state->afeoverride[off_core] = - read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa5 : 0x8f); - - mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa6 : 0xa7), - (0x1 << 2), 0); - mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : - 0xa5), (0x1 << 2), (0x1 << 2)); - - mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa7 : 0xa6), - (0x1 << 2), (0x1 << 2)); - mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa5 : - 0x8f), (0x1 << 2), (0x1 << 2)); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - state->pwrup[core] = - READ_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_2G_PWRUP); - state->atten[core] = - READ_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_2G_ATTEN); - state->pwrup[off_core] = - READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, - TXRXCOUPLE_2G_PWRUP); - state->atten[off_core] = - READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, - TXRXCOUPLE_2G_ATTEN); - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_2G_PWRUP, 0xc); - - if ((pi->pubpi.radiorev == 3) || - (pi->pubpi.radiorev == 4) || - (pi->pubpi.radiorev == 6)) - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_2G_ATTEN, 0xf0); - else if (pi->pubpi.radiorev == 5) - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_2G_ATTEN, - (core == 0) ? 0xf7 : 0xf2); - else if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_2G_ATTEN, 0xf0); - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, - TXRXCOUPLE_2G_PWRUP, 0x0); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, - TXRXCOUPLE_2G_ATTEN, 0xff); - } else { - state->pwrup[core] = - READ_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_5G_PWRUP); - state->atten[core] = - READ_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_5G_ATTEN); - state->pwrup[off_core] = - READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, - TXRXCOUPLE_5G_PWRUP); - state->atten[off_core] = - READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, - TXRXCOUPLE_5G_ATTEN); - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_5G_PWRUP, 0xc); - - if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_5G_ATTEN, 0xf4); - - else - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_5G_ATTEN, 0xf0); - - WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, - TXRXCOUPLE_5G_PWRUP, 0x0); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, - TXRXCOUPLE_5G_ATTEN, 0xff); - } - - tone_freq = 4000; - - wlc_phy_tx_tone_nphy(pi, tone_freq, 181, 0, 0, false); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 13), (1) << 13); - - mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_OFF) << 0); - - mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 13), (0) << 13); - - } else { - - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 0); - - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 1, 0, 0); - - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0x3, 0); - - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 2), 1, 0x3, 0); - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 1, 0x3, 0); - - state->afectrl[core] = read_phy_reg(pi, (core == PHY_CORE_0) ? - 0xa6 : 0xa7); - state->afeoverride[core] = - read_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5); - - mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa6 : 0xa7), - (0x1 << 0) | (0x1 << 1) | (0x1 << 2), 0); - mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : - 0xa5), - (0x1 << 0) | - (0x1 << 1) | - (0x1 << 2), (0x1 << 0) | (0x1 << 1) | (0x1 << 2)); - - state->vga_master[core] = - READ_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER); - WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER, 0x2b); - if (CHSPEC_IS2G(pi->radio_chanspec)) { - state->fbmix[core] = - READ_RADIO_REG2(pi, RADIO_2056, RX, core, - TXFBMIX_G); - state->intpa_master[core] = - READ_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAG_MASTER); - - WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_G, - 0x03); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAG_MASTER, 0x04); - } else { - state->fbmix[core] = - READ_RADIO_REG2(pi, RADIO_2056, RX, core, - TXFBMIX_A); - state->intpa_master[core] = - READ_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAA_MASTER); - - WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_A, - 0x03); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAA_MASTER, 0x04); - - } - - tone_freq = 4000; - - wlc_phy_tx_tone_nphy(pi, tone_freq, 181, 0, 0, false); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (1) << 0); - - mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (0) << 0); - - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 0); - } -} - -static void -wlc_phy_papd_cal_cleanup_nphy(struct brcms_phy *pi, - struct nphy_papd_restore_state *state) -{ - u8 core; - - wlc_phy_stopplayback_nphy(pi); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_2G_PWRUP, 0); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_2G_ATTEN, - state->atten[core]); - } else { - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_5G_PWRUP, 0); - WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, - TXRXCOUPLE_5G_ATTEN, - state->atten[core]); - } - } - - if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 2), - 1, 0x3, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - else - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 2), - 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), - 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID2); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID2); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 1, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12), 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 1, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID2); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 8), 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 1, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 1, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), 0, 0x3, 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - - write_phy_reg(pi, (core == PHY_CORE_0) ? - 0xa6 : 0xa7, state->afectrl[core]); - write_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : - 0xa5, state->afeoverride[core]); - } - - wlc_phy_ipa_set_bbmult_nphy(pi, (state->mm >> 8) & 0xff, - (state->mm & 0xff)); - - if (NREV_IS(pi->pubpi.phy_rev, 7) - || NREV_GE(pi->pubpi.phy_rev, 8)) - wlc_phy_rfctrl_override_nphy_rev7( - pi, (0x1 << 7), 0, 0, - 1, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - } else { - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 1); - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 0x3, 1); - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0x3, 1); - - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 2), 0, 0x3, 1); - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 0, 0x3, 1); - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - - WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER, - state->vga_master[core]); - if (CHSPEC_IS2G(pi->radio_chanspec)) { - WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, - TXFBMIX_G, state->fbmix[core]); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAG_MASTER, - state->intpa_master[core]); - } else { - WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, - TXFBMIX_A, state->fbmix[core]); - WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, - INTPAA_MASTER, - state->intpa_master[core]); - } - - write_phy_reg(pi, (core == PHY_CORE_0) ? - 0xa6 : 0xa7, state->afectrl[core]); - write_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : - 0xa5, state->afeoverride[core]); - } - - wlc_phy_ipa_set_bbmult_nphy(pi, (state->mm >> 8) & 0xff, - (state->mm & 0xff)); - - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 1); - } -} - -static void -wlc_phy_a1_nphy(struct brcms_phy *pi, u8 core, u32 winsz, u32 start, - u32 end) -{ - u32 *buf, *src, *dst, sz; - - sz = end - start + 1; - - buf = kmalloc(2 * sizeof(u32) * NPHY_PAPD_EPS_TBL_SIZE, GFP_ATOMIC); - if (NULL == buf) - return; - - src = buf; - dst = buf + NPHY_PAPD_EPS_TBL_SIZE; - - wlc_phy_table_read_nphy(pi, - (core == - PHY_CORE_0 ? NPHY_TBL_ID_EPSILONTBL0 : - NPHY_TBL_ID_EPSILONTBL1), - NPHY_PAPD_EPS_TBL_SIZE, 0, 32, src); - - do { - u32 phy_a1, phy_a2; - s32 phy_a3, phy_a4, phy_a5, phy_a6, phy_a7; - - phy_a1 = end - min(end, (winsz >> 1)); - phy_a2 = min_t(u32, NPHY_PAPD_EPS_TBL_SIZE - 1, - end + (winsz >> 1)); - phy_a3 = phy_a2 - phy_a1 + 1; - phy_a6 = 0; - phy_a7 = 0; - - do { - wlc_phy_papd_decode_epsilon(src[phy_a2], &phy_a4, - &phy_a5); - phy_a6 += phy_a4; - phy_a7 += phy_a5; - } while (phy_a2-- != phy_a1); - - phy_a6 /= phy_a3; - phy_a7 /= phy_a3; - dst[end] = ((u32) phy_a7 << 13) | ((u32) phy_a6 & 0x1fff); - } while (end-- != start); - - wlc_phy_table_write_nphy(pi, - (core == - PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 : - NPHY_TBL_ID_EPSILONTBL1, sz, start, 32, dst); - - kfree(buf); -} - -static void -wlc_phy_a2_nphy(struct brcms_phy *pi, struct nphy_ipa_txcalgains *txgains, - enum phy_cal_mode cal_mode, u8 core) -{ - u16 phy_a1, phy_a2, phy_a3; - u16 phy_a4, phy_a5; - bool phy_a6; - u8 phy_a7, m[2]; - u32 phy_a8 = 0; - struct nphy_txgains phy_a9; - - if (NREV_LT(pi->pubpi.phy_rev, 3)) - return; - - phy_a7 = (core == PHY_CORE_0) ? 1 : 0; - - phy_a6 = ((cal_mode == CAL_GCTRL) - || (cal_mode == CAL_SOFT)) ? true : false; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - phy_a9 = wlc_phy_get_tx_gain_nphy(pi); - - if (CHSPEC_IS2G(pi->radio_chanspec)) - phy_a5 = ((phy_a9.txlpf[core] << 15) | - (phy_a9.txgm[core] << 12) | - (phy_a9.pga[core] << 8) | - (txgains->gains.pad[core] << 3) | - (phy_a9.ipa[core])); - else - phy_a5 = ((phy_a9.txlpf[core] << 15) | - (phy_a9.txgm[core] << 12) | - (txgains->gains.pga[core] << 8) | - (phy_a9.pad[core] << 3) | (phy_a9.ipa[core])); - - wlc_phy_rfctrl_override_1tomany_nphy( - pi, - NPHY_REV7_RfctrlOverride_cmd_txgain, - phy_a5, (1 << core), 0); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if ((pi->pubpi.radiorev <= 4) - || (pi->pubpi.radiorev == 6)) - m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? - 60 : 79; - else - m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? - 45 : 64; - } else { - m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 75 : 107; - } - - m[phy_a7] = 0; - wlc_phy_ipa_set_bbmult_nphy(pi, m[0], m[1]); - - phy_a2 = 63; - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if ((pi->pubpi.radiorev == 4) - || (pi->pubpi.radiorev == 6)) { - phy_a1 = 30; - phy_a3 = 30; - } else { - phy_a1 = 25; - phy_a3 = 25; - } - } else { - if ((pi->pubpi.radiorev == 5) - || (pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) { - phy_a1 = 25; - phy_a3 = 25; - } else { - phy_a1 = 35; - phy_a3 = 35; - } - } - - if (cal_mode == CAL_GCTRL) { - if ((pi->pubpi.radiorev == 5) - && (CHSPEC_IS2G(pi->radio_chanspec))) - phy_a1 = 55; - else if (((pi->pubpi.radiorev == 7) && - (CHSPEC_IS2G(pi->radio_chanspec))) || - ((pi->pubpi.radiorev == 8) && - (CHSPEC_IS2G(pi->radio_chanspec)))) - phy_a1 = 60; - else - phy_a1 = 63; - - } else if ((cal_mode != CAL_FULL) && (cal_mode != CAL_SOFT)) { - - phy_a1 = 35; - phy_a3 = 35; - } - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (1) << 0); - - mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (0) << 0); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 13), (1) << 13); - - mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 13), (0) << 13); - - write_phy_reg(pi, 0x2a1, 0x80); - write_phy_reg(pi, 0x2a2, 0x100); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x7 << 4), (11) << 4); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x7 << 8), (11) << 8); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x7 << 0), (0x3) << 0); - - write_phy_reg(pi, 0x2e5, 0x20); - - mod_phy_reg(pi, 0x2a0, (0x3f << 0), (phy_a3) << 0); - - mod_phy_reg(pi, 0x29f, (0x3f << 0), (phy_a1) << 0); - - mod_phy_reg(pi, 0x29f, (0x3f << 8), (phy_a2) << 8); - - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), - 1, ((core == 0) ? 1 : 2), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), - 0, ((core == 0) ? 2 : 1), 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - - write_phy_reg(pi, 0x2be, 1); - SPINWAIT(read_phy_reg(pi, 0x2be), 10 * 1000 * 1000); - - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), - 0, 0x3, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - - wlc_phy_table_write_nphy(pi, - (core == - PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 - : NPHY_TBL_ID_EPSILONTBL1, 1, phy_a3, - 32, &phy_a8); - - if (cal_mode != CAL_GCTRL) { - if (CHSPEC_IS5G(pi->radio_chanspec)) - wlc_phy_a1_nphy(pi, core, 5, 0, 35); - } - - wlc_phy_rfctrl_override_1tomany_nphy( - pi, - NPHY_REV7_RfctrlOverride_cmd_txgain, - phy_a5, (1 << core), 1); - - } else { - - if (txgains) { - if (txgains->useindex) { - phy_a4 = 15 - ((txgains->index) >> 3); - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if (NREV_GE(pi->pubpi.phy_rev, 6) && - pi->sh->chip == BCMA_CHIP_ID_BCM47162) { - phy_a5 = 0x10f7 | (phy_a4 << 8); - } else if (NREV_GE(pi->pubpi.phy_rev, 6)) { - phy_a5 = 0x00f7 | (phy_a4 << 8); - } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { - phy_a5 = 0x10f7 | (phy_a4 << 8); - } else { - phy_a5 = 0x50f7 | (phy_a4 << 8); - } - } else { - phy_a5 = 0x70f7 | (phy_a4 << 8); - } - wlc_phy_rfctrl_override_nphy(pi, - (0x1 << 13), - phy_a5, - (1 << core), 0); - } else { - wlc_phy_rfctrl_override_nphy(pi, - (0x1 << 13), - 0x5bf7, - (1 << core), 0); - } - } - - if (CHSPEC_IS2G(pi->radio_chanspec)) - m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 45 : 64; - else - m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 75 : 107; - - m[phy_a7] = 0; - wlc_phy_ipa_set_bbmult_nphy(pi, m[0], m[1]); - - phy_a2 = 63; - - if (cal_mode == CAL_FULL) { - phy_a1 = 25; - phy_a3 = 25; - } else if (cal_mode == CAL_SOFT) { - phy_a1 = 25; - phy_a3 = 25; - } else if (cal_mode == CAL_GCTRL) { - phy_a1 = 63; - phy_a3 = 25; - } else { - - phy_a1 = 25; - phy_a3 = 25; - } - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (1) << 0); - - mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (0) << 0); - - if (NREV_GE(pi->pubpi.phy_rev, 6)) { - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 13), (1) << 13); - - mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 13), (0) << 13); - - write_phy_reg(pi, 0x2a1, 0x20); - write_phy_reg(pi, 0x2a2, 0x60); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0xf << 4), (9) << 4); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0xf << 8), (9) << 8); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0xf << 0), (0x2) << 0); - - write_phy_reg(pi, 0x2e5, 0x20); - } else { - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 11), (1) << 11); - - mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 11), (0) << 11); - - write_phy_reg(pi, 0x2a1, 0x80); - write_phy_reg(pi, 0x2a2, 0x600); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x7 << 4), (0) << 4); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x7 << 8), (0) << 8); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x7 << 0), (0x3) << 0); - - mod_phy_reg(pi, 0x2a0, (0x3f << 8), (0x20) << 8); - - } - - mod_phy_reg(pi, 0x2a0, (0x3f << 0), (phy_a3) << 0); - - mod_phy_reg(pi, 0x29f, (0x3f << 0), (phy_a1) << 0); - - mod_phy_reg(pi, 0x29f, (0x3f << 8), (phy_a2) << 8); - - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 1, 0x3, 0); - - write_phy_reg(pi, 0x2be, 1); - SPINWAIT(read_phy_reg(pi, 0x2be), 10 * 1000 * 1000); - - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 0); - - wlc_phy_table_write_nphy(pi, - (core == - PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 - : NPHY_TBL_ID_EPSILONTBL1, 1, phy_a3, - 32, &phy_a8); - - if (cal_mode != CAL_GCTRL) - wlc_phy_a1_nphy(pi, core, 5, 0, 40); - } -} - -static u8 wlc_phy_a3_nphy(struct brcms_phy *pi, u8 start_gain, u8 core) -{ - int phy_a1; - int phy_a2; - bool phy_a3; - struct nphy_ipa_txcalgains phy_a4; - bool phy_a5 = false; - bool phy_a6 = true; - s32 phy_a7, phy_a8; - u32 phy_a9; - int phy_a10; - bool phy_a11 = false; - int phy_a12; - u8 phy_a13 = 0; - u8 phy_a14; - u8 *phy_a15 = NULL; - - phy_a4.useindex = true; - phy_a12 = start_gain; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - phy_a2 = 20; - phy_a1 = 1; - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if (pi->pubpi.radiorev == 5) { - - phy_a15 = pad_gain_codes_used_2057rev5; - phy_a13 = - ARRAY_SIZE(pad_gain_codes_used_2057rev5) - 1; - - } else if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) { - - phy_a15 = pad_gain_codes_used_2057rev7; - phy_a13 = - ARRAY_SIZE(pad_gain_codes_used_2057rev7) - 1; - - } else { - - phy_a15 = pad_all_gain_codes_2057; - phy_a13 = ARRAY_SIZE(pad_all_gain_codes_2057) - - 1; - } - - } else { - - phy_a15 = pga_all_gain_codes_2057; - phy_a13 = ARRAY_SIZE(pga_all_gain_codes_2057) - 1; - } - - phy_a14 = 0; - - for (phy_a10 = 0; phy_a10 < phy_a2; phy_a10++) { - if (CHSPEC_IS2G(pi->radio_chanspec)) - phy_a4.gains.pad[core] = - (u16) phy_a15[phy_a12]; - else - phy_a4.gains.pga[core] = - (u16) phy_a15[phy_a12]; - - wlc_phy_a2_nphy(pi, &phy_a4, CAL_GCTRL, core); - - wlc_phy_table_read_nphy(pi, - (core == - PHY_CORE_0 ? - NPHY_TBL_ID_EPSILONTBL0 : - NPHY_TBL_ID_EPSILONTBL1), 1, - 63, 32, &phy_a9); - - wlc_phy_papd_decode_epsilon(phy_a9, &phy_a7, &phy_a8); - - phy_a3 = ((phy_a7 == 4095) || (phy_a7 == -4096) || - (phy_a8 == 4095) || (phy_a8 == -4096)); - - if (!phy_a6 && (phy_a3 != phy_a5)) { - if (!phy_a3) - phy_a12 -= (u8) phy_a1; - - phy_a11 = true; - break; - } - - if (phy_a3) - phy_a12 += (u8) phy_a1; - else - phy_a12 -= (u8) phy_a1; - - if ((phy_a12 < phy_a14) || (phy_a12 > phy_a13)) { - if (phy_a12 < phy_a14) - phy_a12 = phy_a14; - else - phy_a12 = phy_a13; - - phy_a11 = true; - break; - } - - phy_a6 = false; - phy_a5 = phy_a3; - } - - } else { - phy_a2 = 10; - phy_a1 = 8; - for (phy_a10 = 0; phy_a10 < phy_a2; phy_a10++) { - phy_a4.index = (u8) phy_a12; - wlc_phy_a2_nphy(pi, &phy_a4, CAL_GCTRL, core); - - wlc_phy_table_read_nphy(pi, - (core == - PHY_CORE_0 ? - NPHY_TBL_ID_EPSILONTBL0 : - NPHY_TBL_ID_EPSILONTBL1), 1, - 63, 32, &phy_a9); - - wlc_phy_papd_decode_epsilon(phy_a9, &phy_a7, &phy_a8); - - phy_a3 = ((phy_a7 == 4095) || (phy_a7 == -4096) || - (phy_a8 == 4095) || (phy_a8 == -4096)); - - if (!phy_a6 && (phy_a3 != phy_a5)) { - if (!phy_a3) - phy_a12 -= (u8) phy_a1; - - phy_a11 = true; - break; - } - - if (phy_a3) - phy_a12 += (u8) phy_a1; - else - phy_a12 -= (u8) phy_a1; - - if ((phy_a12 < 0) || (phy_a12 > 127)) { - if (phy_a12 < 0) - phy_a12 = 0; - else - phy_a12 = 127; - - phy_a11 = true; - break; - } - - phy_a6 = false; - phy_a5 = phy_a3; - } - - } - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - return (u8) phy_a15[phy_a12]; - else - return (u8) phy_a12; - -} - -static void wlc_phy_a4(struct brcms_phy *pi, bool full_cal) -{ - struct nphy_ipa_txcalgains phy_b1[2]; - struct nphy_papd_restore_state phy_b2; - bool phy_b3; - u8 phy_b4; - u8 phy_b5; - s16 phy_b6, phy_b7, phy_b8; - u16 phy_b9; - s16 phy_b10, phy_b11, phy_b12; - - phy_b11 = 0; - phy_b12 = 0; - phy_b7 = 0; - phy_b8 = 0; - phy_b6 = 0; - - if (pi->nphy_papd_skip == 1) - return; - - phy_b3 = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & - MCTL_EN_MAC)); - if (!phy_b3) - wlapi_suspend_mac_and_wait(pi->sh->physhim); - - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - pi->nphy_force_papd_cal = false; - - for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) - pi->nphy_papd_tx_gain_at_last_cal[phy_b5] = - wlc_phy_txpwr_idx_cur_get_nphy(pi, phy_b5); - - pi->nphy_papd_last_cal = pi->sh->now; - pi->nphy_papd_recal_counter++; - - phy_b4 = pi->nphy_txpwrctrl; - wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SCALARTBL0, 64, 0, 32, - nphy_papd_scaltbl); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SCALARTBL1, 64, 0, 32, - nphy_papd_scaltbl); - - phy_b9 = read_phy_reg(pi, 0x01); - mod_phy_reg(pi, 0x01, (0x1 << 15), 0); - - for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) { - s32 i, val = 0; - for (i = 0; i < 64; i++) - wlc_phy_table_write_nphy(pi, - ((phy_b5 == - PHY_CORE_0) ? - NPHY_TBL_ID_EPSILONTBL0 : - NPHY_TBL_ID_EPSILONTBL1), 1, - i, 32, &val); - } - - wlc_phy_ipa_restore_tx_digi_filts_nphy(pi); - - phy_b2.mm = wlc_phy_ipa_get_bbmult_nphy(pi); - for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) { - wlc_phy_papd_cal_setup_nphy(pi, &phy_b2, phy_b5); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if ((pi->pubpi.radiorev == 3) - || (pi->pubpi.radiorev == 4) - || (pi->pubpi.radiorev == 6)) { - pi->nphy_papd_cal_gain_index[phy_b5] = - 23; - } else if (pi->pubpi.radiorev == 5) { - pi->nphy_papd_cal_gain_index[phy_b5] = - 0; - pi->nphy_papd_cal_gain_index[phy_b5] = - wlc_phy_a3_nphy( - pi, - pi-> - nphy_papd_cal_gain_index - [phy_b5], - phy_b5); - - } else if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) { - - pi->nphy_papd_cal_gain_index[phy_b5] = - 0; - pi->nphy_papd_cal_gain_index[phy_b5] = - wlc_phy_a3_nphy( - pi, - pi-> - nphy_papd_cal_gain_index - [phy_b5], - phy_b5); - - } - - phy_b1[phy_b5].gains.pad[phy_b5] = - pi->nphy_papd_cal_gain_index[phy_b5]; - - } else { - pi->nphy_papd_cal_gain_index[phy_b5] = 0; - pi->nphy_papd_cal_gain_index[phy_b5] = - wlc_phy_a3_nphy( - pi, - pi-> - nphy_papd_cal_gain_index - [phy_b5], phy_b5); - phy_b1[phy_b5].gains.pga[phy_b5] = - pi->nphy_papd_cal_gain_index[phy_b5]; - } - } else { - phy_b1[phy_b5].useindex = true; - phy_b1[phy_b5].index = 16; - phy_b1[phy_b5].index = - wlc_phy_a3_nphy(pi, phy_b1[phy_b5].index, - phy_b5); - - pi->nphy_papd_cal_gain_index[phy_b5] = - 15 - ((phy_b1[phy_b5].index) >> 3); - } - - switch (pi->nphy_papd_cal_type) { - case 0: - wlc_phy_a2_nphy(pi, &phy_b1[phy_b5], CAL_FULL, phy_b5); - break; - case 1: - wlc_phy_a2_nphy(pi, &phy_b1[phy_b5], CAL_SOFT, phy_b5); - break; - } - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - wlc_phy_papd_cal_cleanup_nphy(pi, &phy_b2); - } - - if (NREV_LT(pi->pubpi.phy_rev, 7)) - wlc_phy_papd_cal_cleanup_nphy(pi, &phy_b2); - - for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) { - int eps_offset = 0; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if (CHSPEC_IS2G(pi->radio_chanspec)) { - if (pi->pubpi.radiorev == 3) - eps_offset = -2; - else if (pi->pubpi.radiorev == 5) - eps_offset = 3; - else - eps_offset = -1; - } else { - eps_offset = 2; - } - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - phy_b8 = phy_b1[phy_b5].gains.pad[phy_b5]; - phy_b10 = 0; - if ((pi->pubpi.radiorev == 3) || - (pi->pubpi.radiorev == 4) || - (pi->pubpi.radiorev == 6)) { - phy_b12 = -( - nphy_papd_padgain_dlt_2g_2057rev3n4 - [phy_b8] + 1) / 2; - phy_b10 = -1; - } else if (pi->pubpi.radiorev == 5) { - phy_b12 = -( - nphy_papd_padgain_dlt_2g_2057rev5 - [phy_b8] + 1) / 2; - } else if ((pi->pubpi.radiorev == 7) || - (pi->pubpi.radiorev == 8)) { - phy_b12 = -( - nphy_papd_padgain_dlt_2g_2057rev7 - [phy_b8] + 1) / 2; - } - } else { - phy_b7 = phy_b1[phy_b5].gains.pga[phy_b5]; - if ((pi->pubpi.radiorev == 3) || - (pi->pubpi.radiorev == 4) || - (pi->pubpi.radiorev == 6)) - phy_b11 = - -(nphy_papd_pgagain_dlt_5g_2057 - [phy_b7] - + 1) / 2; - else if ((pi->pubpi.radiorev == 7) - || (pi->pubpi.radiorev == 8)) - phy_b11 = -( - nphy_papd_pgagain_dlt_5g_2057rev7 - [phy_b7] + 1) / 2; - - phy_b10 = -9; - } - - if (CHSPEC_IS2G(pi->radio_chanspec)) - phy_b6 = - -60 + 27 + eps_offset + phy_b12 + - phy_b10; - else - phy_b6 = - -60 + 27 + eps_offset + phy_b11 + - phy_b10; - - mod_phy_reg(pi, (phy_b5 == PHY_CORE_0) ? 0x298 : - 0x29c, (0x1ff << 7), (phy_b6) << 7); - - pi->nphy_papd_epsilon_offset[phy_b5] = phy_b6; - } else { - if (NREV_LT(pi->pubpi.phy_rev, 5)) - eps_offset = 4; - else - eps_offset = 2; - - phy_b7 = 15 - ((phy_b1[phy_b5].index) >> 3); - - if (CHSPEC_IS2G(pi->radio_chanspec)) { - phy_b11 = - -(nphy_papd_pga_gain_delta_ipa_2g[ - phy_b7] + - 1) / 2; - phy_b10 = 0; - } else { - phy_b11 = - -(nphy_papd_pga_gain_delta_ipa_5g[ - phy_b7] + - 1) / 2; - phy_b10 = -9; - } - - phy_b6 = -60 + 27 + eps_offset + phy_b11 + phy_b10; - - mod_phy_reg(pi, (phy_b5 == PHY_CORE_0) ? 0x298 : - 0x29c, (0x1ff << 7), (phy_b6) << 7); - - pi->nphy_papd_epsilon_offset[phy_b5] = phy_b6; - } - } - - mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0); - - mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0); - - if (NREV_GE(pi->pubpi.phy_rev, 6)) { - mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 13), (0) << 13); - - mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 13), (0) << 13); - - } else { - mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 11), (0) << 11); - - mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x2a3 : - 0x2a4, (0x1 << 11), (0) << 11); - - } - pi->nphy_papdcomp = NPHY_PAPD_COMP_ON; - - write_phy_reg(pi, 0x01, phy_b9); - - wlc_phy_ipa_set_tx_digi_filts_nphy(pi); - - wlc_phy_txpwrctrl_enable_nphy(pi, phy_b4); - if (phy_b4 == PHY_TPC_HW_OFF) { - wlc_phy_txpwr_index_nphy(pi, (1 << 0), - (s8) (pi->nphy_txpwrindex[0]. - index_internal), false); - wlc_phy_txpwr_index_nphy(pi, (1 << 1), - (s8) (pi->nphy_txpwrindex[1]. - index_internal), false); - } - - wlc_phy_stay_in_carriersearch_nphy(pi, false); - - if (!phy_b3) - wlapi_enable_mac(pi->sh->physhim); -} - -void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype) -{ - struct nphy_txgains target_gain; - u8 tx_pwr_ctrl_state; - bool fullcal = true; - bool restore_tx_gain = false; - bool mphase; - - if (PHY_MUTED(pi)) - return; - - if (caltype == PHY_PERICAL_AUTO) - fullcal = (pi->radio_chanspec != pi->nphy_txiqlocal_chanspec); - else if (caltype == PHY_PERICAL_PARTIAL) - fullcal = false; - - if (pi->cal_type_override != PHY_PERICAL_AUTO) - fullcal = - (pi->cal_type_override == - PHY_PERICAL_FULL) ? true : false; - - if ((pi->mphase_cal_phase_id > MPHASE_CAL_STATE_INIT)) { - if (pi->nphy_txiqlocal_chanspec != pi->radio_chanspec) - wlc_phy_cal_perical_mphase_restart(pi); - } - - if ((pi->mphase_cal_phase_id == MPHASE_CAL_STATE_RXCAL)) - wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, 10000); - - wlapi_suspend_mac_and_wait(pi->sh->physhim); - - wlc_phyreg_enter((struct brcms_phy_pub *) pi); - - if ((pi->mphase_cal_phase_id == MPHASE_CAL_STATE_IDLE) || - (pi->mphase_cal_phase_id == MPHASE_CAL_STATE_INIT)) { - pi->nphy_cal_orig_pwr_idx[0] = - (u8) ((read_phy_reg(pi, 0x1ed) >> 8) & 0x7f); - pi->nphy_cal_orig_pwr_idx[1] = - (u8) ((read_phy_reg(pi, 0x1ee) >> 8) & 0x7f); - - if (pi->nphy_txpwrctrl != PHY_TPC_HW_OFF) { - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, - 0x110, 16, - pi->nphy_cal_orig_tx_gain); - } else { - pi->nphy_cal_orig_tx_gain[0] = 0; - pi->nphy_cal_orig_tx_gain[1] = 0; - } - } - target_gain = wlc_phy_get_tx_gain_nphy(pi); - tx_pwr_ctrl_state = pi->nphy_txpwrctrl; - wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); - - if (pi->antsel_type == ANTSEL_2x3) - wlc_phy_antsel_init((struct brcms_phy_pub *) pi, true); - - mphase = (pi->mphase_cal_phase_id != MPHASE_CAL_STATE_IDLE); - if (!mphase) { - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - wlc_phy_precal_txgain_nphy(pi); - pi->nphy_cal_target_gain = wlc_phy_get_tx_gain_nphy(pi); - restore_tx_gain = true; - - target_gain = pi->nphy_cal_target_gain; - } - if (0 == - wlc_phy_cal_txiqlo_nphy(pi, target_gain, fullcal, - mphase)) { - if (PHY_IPA(pi)) - wlc_phy_a4(pi, true); - - wlc_phyreg_exit((struct brcms_phy_pub *) pi); - wlapi_enable_mac(pi->sh->physhim); - wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, - 10000); - wlapi_suspend_mac_and_wait(pi->sh->physhim); - wlc_phyreg_enter((struct brcms_phy_pub *) pi); - - if (0 == wlc_phy_cal_rxiq_nphy(pi, target_gain, - (pi->first_cal_after_assoc || - (pi->cal_type_override == - PHY_PERICAL_FULL)) ? 2 : 0, false)) { - wlc_phy_savecal_nphy(pi); - - wlc_phy_txpwrctrl_coeff_setup_nphy(pi); - - pi->nphy_perical_last = pi->sh->now; - } - } - if (caltype != PHY_PERICAL_AUTO) - wlc_phy_rssi_cal_nphy(pi); - - if (pi->first_cal_after_assoc - || (pi->cal_type_override == PHY_PERICAL_FULL)) { - pi->first_cal_after_assoc = false; - wlc_phy_txpwrctrl_idle_tssi_nphy(pi); - wlc_phy_txpwrctrl_pwr_setup_nphy(pi); - } - - if (NREV_GE(pi->pubpi.phy_rev, 3)) - wlc_phy_radio205x_vcocal_nphy(pi); - } else { - switch (pi->mphase_cal_phase_id) { - case MPHASE_CAL_STATE_INIT: - pi->nphy_perical_last = pi->sh->now; - pi->nphy_txiqlocal_chanspec = pi->radio_chanspec; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) - wlc_phy_precal_txgain_nphy(pi); - - pi->nphy_cal_target_gain = wlc_phy_get_tx_gain_nphy(pi); - pi->mphase_cal_phase_id++; - break; - - case MPHASE_CAL_STATE_TXPHASE0: - case MPHASE_CAL_STATE_TXPHASE1: - case MPHASE_CAL_STATE_TXPHASE2: - case MPHASE_CAL_STATE_TXPHASE3: - case MPHASE_CAL_STATE_TXPHASE4: - case MPHASE_CAL_STATE_TXPHASE5: - if ((pi->radar_percal_mask & 0x10) != 0) - pi->nphy_rxcal_active = true; - - if (wlc_phy_cal_txiqlo_nphy - (pi, pi->nphy_cal_target_gain, fullcal, - true) != 0) { - - wlc_phy_cal_perical_mphase_reset(pi); - break; - } - - if (NREV_LE(pi->pubpi.phy_rev, 2) && - (pi->mphase_cal_phase_id == - MPHASE_CAL_STATE_TXPHASE4)) - pi->mphase_cal_phase_id += 2; - else - pi->mphase_cal_phase_id++; - break; - - case MPHASE_CAL_STATE_PAPDCAL: - if ((pi->radar_percal_mask & 0x2) != 0) - pi->nphy_rxcal_active = true; - - if (PHY_IPA(pi)) - wlc_phy_a4(pi, true); - - pi->mphase_cal_phase_id++; - break; - - case MPHASE_CAL_STATE_RXCAL: - if ((pi->radar_percal_mask & 0x1) != 0) - pi->nphy_rxcal_active = true; - if (wlc_phy_cal_rxiq_nphy(pi, target_gain, - (pi->first_cal_after_assoc || - (pi->cal_type_override == - PHY_PERICAL_FULL)) ? 2 : 0, - false) == 0) - wlc_phy_savecal_nphy(pi); - - pi->mphase_cal_phase_id++; - break; - - case MPHASE_CAL_STATE_RSSICAL: - if ((pi->radar_percal_mask & 0x4) != 0) - pi->nphy_rxcal_active = true; - wlc_phy_txpwrctrl_coeff_setup_nphy(pi); - wlc_phy_rssi_cal_nphy(pi); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) - wlc_phy_radio205x_vcocal_nphy(pi); - - restore_tx_gain = true; - - if (pi->first_cal_after_assoc) - pi->mphase_cal_phase_id++; - else - wlc_phy_cal_perical_mphase_reset(pi); - - break; - - case MPHASE_CAL_STATE_IDLETSSI: - if ((pi->radar_percal_mask & 0x8) != 0) - pi->nphy_rxcal_active = true; - - if (pi->first_cal_after_assoc) { - pi->first_cal_after_assoc = false; - wlc_phy_txpwrctrl_idle_tssi_nphy(pi); - wlc_phy_txpwrctrl_pwr_setup_nphy(pi); - } - - wlc_phy_cal_perical_mphase_reset(pi); - break; - - default: - wlc_phy_cal_perical_mphase_reset(pi); - break; - } - } - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - if (restore_tx_gain) { - if (tx_pwr_ctrl_state != PHY_TPC_HW_OFF) { - - wlc_phy_txpwr_index_nphy(pi, 1, - pi-> - nphy_cal_orig_pwr_idx - [0], false); - wlc_phy_txpwr_index_nphy(pi, 2, - pi-> - nphy_cal_orig_pwr_idx - [1], false); - - pi->nphy_txpwrindex[0].index = -1; - pi->nphy_txpwrindex[1].index = -1; - } else { - wlc_phy_txpwr_index_nphy(pi, (1 << 0), - (s8) (pi-> - nphy_txpwrindex - [0]. - index_internal), - false); - wlc_phy_txpwr_index_nphy(pi, (1 << 1), - (s8) (pi-> - nphy_txpwrindex - [1]. - index_internal), - false); - } - } - } - - wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); - wlc_phyreg_exit((struct brcms_phy_pub *) pi); - wlapi_enable_mac(pi->sh->physhim); -} - -int -wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, - bool fullcal, bool mphase) -{ - u16 val; - u16 tbl_buf[11]; - u8 cal_cnt; - u16 cal_cmd; - u8 num_cals, max_cal_cmds; - u16 core_no, cal_type; - u16 diq_start = 0; - u8 phy_bw; - u16 max_val; - u16 tone_freq; - u16 gain_save[2]; - u16 cal_gain[2]; - struct nphy_iqcal_params cal_params[2]; - u32 tbl_len; - void *tbl_ptr; - bool ladder_updated[2]; - u8 mphase_cal_lastphase = 0; - int bcmerror = 0; - bool phyhang_avoid_state = false; - - u16 tbl_tx_iqlo_cal_loft_ladder_20[] = { - 0x0300, 0x0500, 0x0700, 0x0900, 0x0d00, 0x1100, 0x1900, 0x1901, - 0x1902, - 0x1903, 0x1904, 0x1905, 0x1906, 0x1907, 0x2407, 0x3207, 0x4607, - 0x6407 - }; - - u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = { - 0x0200, 0x0300, 0x0600, 0x0900, 0x0d00, 0x1100, 0x1900, 0x2400, - 0x3200, - 0x4600, 0x6400, 0x6401, 0x6402, 0x6403, 0x6404, 0x6405, 0x6406, - 0x6407 - }; - - u16 tbl_tx_iqlo_cal_loft_ladder_40[] = { - 0x0200, 0x0300, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1201, - 0x1202, - 0x1203, 0x1204, 0x1205, 0x1206, 0x1207, 0x1907, 0x2307, 0x3207, - 0x4707 - }; - - u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = { - 0x0100, 0x0200, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1900, - 0x2300, - 0x3200, 0x4700, 0x4701, 0x4702, 0x4703, 0x4704, 0x4705, 0x4706, - 0x4707 - }; - - u16 tbl_tx_iqlo_cal_startcoefs[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000 - }; - - u16 tbl_tx_iqlo_cal_cmds_fullcal[] = { - 0x8123, 0x8264, 0x8086, 0x8245, 0x8056, - 0x9123, 0x9264, 0x9086, 0x9245, 0x9056 - }; - - u16 tbl_tx_iqlo_cal_cmds_recal[] = { - 0x8101, 0x8253, 0x8053, 0x8234, 0x8034, - 0x9101, 0x9253, 0x9053, 0x9234, 0x9034 - }; - - u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[] = { - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000 - }; - - u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = { - 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234, - 0x9434, 0x9334, 0x9084, 0x9267, 0x9056, 0x9234 - }; - - u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = { - 0x8423, 0x8323, 0x8073, 0x8256, 0x8045, 0x8223, - 0x9423, 0x9323, 0x9073, 0x9256, 0x9045, 0x9223 - }; - - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - if (NREV_GE(pi->pubpi.phy_rev, 4)) { - phyhang_avoid_state = pi->phyhang_avoid; - pi->phyhang_avoid = false; - } - - if (CHSPEC_IS40(pi->radio_chanspec)) - phy_bw = 40; - else - phy_bw = 20; - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); - - for (core_no = 0; core_no <= 1; core_no++) { - wlc_phy_iqcal_gainparams_nphy(pi, core_no, target_gain, - &cal_params[core_no]); - cal_gain[core_no] = cal_params[core_no].cal_gain; - } - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, cal_gain); - - wlc_phy_txcal_radio_setup_nphy(pi); - - wlc_phy_txcal_physetup_nphy(pi); - - ladder_updated[0] = ladder_updated[1] = false; - if (!(NREV_GE(pi->pubpi.phy_rev, 6) || - (NREV_IS(pi->pubpi.phy_rev, 5) && PHY_IPA(pi) - && (CHSPEC_IS2G(pi->radio_chanspec))))) { - - if (phy_bw == 40) { - tbl_ptr = tbl_tx_iqlo_cal_loft_ladder_40; - tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_loft_ladder_40); - } else { - tbl_ptr = tbl_tx_iqlo_cal_loft_ladder_20; - tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_loft_ladder_20); - } - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 0, - 16, tbl_ptr); - - if (phy_bw == 40) { - tbl_ptr = tbl_tx_iqlo_cal_iqimb_ladder_40; - tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_iqimb_ladder_40); - } else { - tbl_ptr = tbl_tx_iqlo_cal_iqimb_ladder_20; - tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_iqimb_ladder_20); - } - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 32, - 16, tbl_ptr); - } - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - write_phy_reg(pi, 0xc2, 0x8ad9); - else - write_phy_reg(pi, 0xc2, 0x8aa9); - - max_val = 250; - tone_freq = (phy_bw == 20) ? 2500 : 5000; - - if (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_TXPHASE0) { - wlc_phy_runsamples_nphy(pi, phy_bw * 8, 0xffff, 0, 1, 0, false); - bcmerror = 0; - } else { - bcmerror = - wlc_phy_tx_tone_nphy(pi, tone_freq, max_val, 1, 0, - false); - } - - if (bcmerror == 0) { - - if (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_TXPHASE0) { - tbl_ptr = pi->mphase_txcal_bestcoeffs; - tbl_len = ARRAY_SIZE(pi->mphase_txcal_bestcoeffs); - if (NREV_LT(pi->pubpi.phy_rev, 3)) - tbl_len -= 2; - } else { - if ((!fullcal) && (pi->nphy_txiqlocal_coeffsvalid)) { - - tbl_ptr = pi->nphy_txiqlocal_bestc; - tbl_len = ARRAY_SIZE(pi->nphy_txiqlocal_bestc); - if (NREV_LT(pi->pubpi.phy_rev, 3)) - tbl_len -= 2; - } else { - - fullcal = true; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - tbl_ptr = - tbl_tx_iqlo_cal_startcoefs_nphyrev3; - tbl_len = ARRAY_SIZE( - tbl_tx_iqlo_cal_startcoefs_nphyrev3); - } else { - tbl_ptr = tbl_tx_iqlo_cal_startcoefs; - tbl_len = ARRAY_SIZE( - tbl_tx_iqlo_cal_startcoefs); - } - } - } - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 64, - 16, tbl_ptr); - - if (fullcal) { - max_cal_cmds = (NREV_GE(pi->pubpi.phy_rev, 3)) ? - ARRAY_SIZE( - tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3) : - ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_fullcal); - } else { - max_cal_cmds = (NREV_GE(pi->pubpi.phy_rev, 3)) ? - ARRAY_SIZE( - tbl_tx_iqlo_cal_cmds_recal_nphyrev3) : - ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_recal); - } - - if (mphase) { - cal_cnt = pi->mphase_txcal_cmdidx; - if ((cal_cnt + pi->mphase_txcal_numcmds) < max_cal_cmds) - num_cals = cal_cnt + pi->mphase_txcal_numcmds; - else - num_cals = max_cal_cmds; - } else { - cal_cnt = 0; - num_cals = max_cal_cmds; - } - - for (; cal_cnt < num_cals; cal_cnt++) { - - if (fullcal) { - cal_cmd = (NREV_GE(pi->pubpi.phy_rev, 3)) ? - tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3 - [cal_cnt] : - tbl_tx_iqlo_cal_cmds_fullcal[cal_cnt]; - } else { - cal_cmd = (NREV_GE(pi->pubpi.phy_rev, 3)) ? - tbl_tx_iqlo_cal_cmds_recal_nphyrev3[ - cal_cnt] - : tbl_tx_iqlo_cal_cmds_recal[cal_cnt]; - } - - core_no = ((cal_cmd & 0x3000) >> 12); - cal_type = ((cal_cmd & 0x0F00) >> 8); - - if (NREV_GE(pi->pubpi.phy_rev, 6) || - (NREV_IS(pi->pubpi.phy_rev, 5) && - PHY_IPA(pi) - && (CHSPEC_IS2G(pi->radio_chanspec)))) { - if (!ladder_updated[core_no]) { - wlc_phy_update_txcal_ladder_nphy( - pi, - core_no); - ladder_updated[core_no] = true; - } - } - - val = - (cal_params[core_no]. - ncorr[cal_type] << 8) | NPHY_N_GCTL; - write_phy_reg(pi, 0xc1, val); - - if ((cal_type == 1) || (cal_type == 3) - || (cal_type == 4)) { - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, - 1, 69 + core_no, 16, - tbl_buf); - - diq_start = tbl_buf[0]; - - tbl_buf[0] = 0; - wlc_phy_table_write_nphy(pi, - NPHY_TBL_ID_IQLOCAL, 1, - 69 + core_no, 16, - tbl_buf); - } - - write_phy_reg(pi, 0xc0, cal_cmd); - - SPINWAIT(((read_phy_reg(pi, 0xc0) & 0xc000) != 0), - 20000); - if (WARN(read_phy_reg(pi, 0xc0) & 0xc000, - "HW error: txiq calib")) - return -EIO; - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, - tbl_len, 96, 16, tbl_buf); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, - tbl_len, 64, 16, tbl_buf); - - if ((cal_type == 1) || (cal_type == 3) - || (cal_type == 4)) { - - tbl_buf[0] = diq_start; - - } - - } - - if (mphase) { - pi->mphase_txcal_cmdidx = num_cals; - if (pi->mphase_txcal_cmdidx >= max_cal_cmds) - pi->mphase_txcal_cmdidx = 0; - } - - mphase_cal_lastphase = - (NREV_LE(pi->pubpi.phy_rev, 2)) ? - MPHASE_CAL_STATE_TXPHASE4 : MPHASE_CAL_STATE_TXPHASE5; - - if (!mphase - || (pi->mphase_cal_phase_id == mphase_cal_lastphase)) { - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 96, - 16, tbl_buf); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 80, - 16, tbl_buf); - - if (NREV_LT(pi->pubpi.phy_rev, 2)) { - - tbl_buf[0] = 0; - tbl_buf[1] = 0; - tbl_buf[2] = 0; - tbl_buf[3] = 0; - - } - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 88, - 16, tbl_buf); - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 101, - 16, tbl_buf); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 85, - 16, tbl_buf); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 93, - 16, tbl_buf); - - tbl_len = ARRAY_SIZE(pi->nphy_txiqlocal_bestc); - if (NREV_LT(pi->pubpi.phy_rev, 3)) - tbl_len -= 2; - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, - tbl_len, 96, 16, - pi->nphy_txiqlocal_bestc); - - pi->nphy_txiqlocal_coeffsvalid = true; - pi->nphy_txiqlocal_chanspec = pi->radio_chanspec; - } else { - tbl_len = ARRAY_SIZE(pi->mphase_txcal_bestcoeffs); - if (NREV_LT(pi->pubpi.phy_rev, 3)) - tbl_len -= 2; - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, - tbl_len, 96, 16, - pi->mphase_txcal_bestcoeffs); - } - - wlc_phy_stopplayback_nphy(pi); - - write_phy_reg(pi, 0xc2, 0x0000); - - } - - wlc_phy_txcal_phycleanup_nphy(pi); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, - gain_save); - - wlc_phy_txcal_radio_cleanup_nphy(pi); - - if (NREV_LT(pi->pubpi.phy_rev, 2)) { - if (!mphase - || (pi->mphase_cal_phase_id == mphase_cal_lastphase)) - wlc_phy_tx_iq_war_nphy(pi); - } - - if (NREV_GE(pi->pubpi.phy_rev, 4)) - pi->phyhang_avoid = phyhang_avoid_state; - - wlc_phy_stay_in_carriersearch_nphy(pi, false); - - return bcmerror; -} - -static void wlc_phy_reapply_txcal_coeffs_nphy(struct brcms_phy *pi) -{ - u16 tbl_buf[7]; - - if ((pi->nphy_txiqlocal_chanspec == pi->radio_chanspec) && - (pi->nphy_txiqlocal_coeffsvalid)) { - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, - ARRAY_SIZE(tbl_buf), 80, 16, tbl_buf); - - if ((pi->nphy_txiqlocal_bestc[0] != tbl_buf[0]) || - (pi->nphy_txiqlocal_bestc[1] != tbl_buf[1]) || - (pi->nphy_txiqlocal_bestc[2] != tbl_buf[2]) || - (pi->nphy_txiqlocal_bestc[3] != tbl_buf[3])) { - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 80, - 16, pi->nphy_txiqlocal_bestc); - - tbl_buf[0] = 0; - tbl_buf[1] = 0; - tbl_buf[2] = 0; - tbl_buf[3] = 0; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 88, - 16, tbl_buf); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 85, - 16, - &pi->nphy_txiqlocal_bestc[5]); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 93, - 16, - &pi->nphy_txiqlocal_bestc[5]); - } - } -} - -void -wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write, - struct nphy_iq_comp *pcomp) -{ - if (write) { - write_phy_reg(pi, 0x9a, pcomp->a0); - write_phy_reg(pi, 0x9b, pcomp->b0); - write_phy_reg(pi, 0x9c, pcomp->a1); - write_phy_reg(pi, 0x9d, pcomp->b1); - } else { - pcomp->a0 = read_phy_reg(pi, 0x9a); - pcomp->b0 = read_phy_reg(pi, 0x9b); - pcomp->a1 = read_phy_reg(pi, 0x9c); - pcomp->b1 = read_phy_reg(pi, 0x9d); - } -} - -void -wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est, - u16 num_samps, u8 wait_time, u8 wait_for_crs) -{ - u8 core; - - write_phy_reg(pi, 0x12b, num_samps); - mod_phy_reg(pi, 0x12a, (0xff << 0), (wait_time << 0)); - mod_phy_reg(pi, 0x129, NPHY_IqestCmd_iqMode, - (wait_for_crs) ? NPHY_IqestCmd_iqMode : 0); - - mod_phy_reg(pi, 0x129, NPHY_IqestCmd_iqstart, NPHY_IqestCmd_iqstart); - - SPINWAIT(((read_phy_reg(pi, 0x129) & NPHY_IqestCmd_iqstart) != 0), - 10000); - if (WARN(read_phy_reg(pi, 0x129) & NPHY_IqestCmd_iqstart, - "HW error: rxiq est")) - return; - - if ((read_phy_reg(pi, 0x129) & NPHY_IqestCmd_iqstart) == 0) { - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - est[core].i_pwr = - (read_phy_reg(pi, - NPHY_IqestipwrAccHi(core)) << 16) - | read_phy_reg(pi, NPHY_IqestipwrAccLo(core)); - est[core].q_pwr = - (read_phy_reg(pi, - NPHY_IqestqpwrAccHi(core)) << 16) - | read_phy_reg(pi, NPHY_IqestqpwrAccLo(core)); - est[core].iq_prod = - (read_phy_reg(pi, - NPHY_IqestIqAccHi(core)) << 16) | - read_phy_reg(pi, NPHY_IqestIqAccLo(core)); - } - } -} - -#define CAL_RETRY_CNT 2 -static void wlc_phy_calc_rx_iq_comp_nphy(struct brcms_phy *pi, u8 core_mask) -{ - u8 curr_core; - struct phy_iq_est est[PHY_CORE_MAX]; - struct nphy_iq_comp old_comp, new_comp; - s32 iq = 0; - u32 ii = 0, qq = 0; - s16 iq_nbits, qq_nbits, brsh, arsh; - s32 a, b, temp; - int bcmerror = 0; - uint cal_retry = 0; - - if (core_mask == 0x0) - return; - - wlc_phy_rx_iq_coeffs_nphy(pi, 0, &old_comp); - new_comp.a0 = new_comp.b0 = new_comp.a1 = new_comp.b1 = 0x0; - wlc_phy_rx_iq_coeffs_nphy(pi, 1, &new_comp); - -cal_try: - wlc_phy_rx_iq_est_nphy(pi, est, 0x4000, 32, 0); - - new_comp = old_comp; - - for (curr_core = 0; curr_core < pi->pubpi.phy_corenum; curr_core++) { - - if ((curr_core == PHY_CORE_0) && (core_mask & 0x1)) { - iq = est[curr_core].iq_prod; - ii = est[curr_core].i_pwr; - qq = est[curr_core].q_pwr; - } else if ((curr_core == PHY_CORE_1) && (core_mask & 0x2)) { - iq = est[curr_core].iq_prod; - ii = est[curr_core].i_pwr; - qq = est[curr_core].q_pwr; - } else { - continue; - } - - if ((ii + qq) < NPHY_MIN_RXIQ_PWR) { - bcmerror = -EBADE; - break; - } - - iq_nbits = wlc_phy_nbits(iq); - qq_nbits = wlc_phy_nbits(qq); - - arsh = 10 - (30 - iq_nbits); - if (arsh >= 0) { - a = (-(iq << (30 - iq_nbits)) + (ii >> (1 + arsh))); - temp = (s32) (ii >> arsh); - if (temp == 0) { - bcmerror = -EBADE; - break; - } - } else { - a = (-(iq << (30 - iq_nbits)) + (ii << (-1 - arsh))); - temp = (s32) (ii << -arsh); - if (temp == 0) { - bcmerror = -EBADE; - break; - } - } - - a /= temp; - - brsh = qq_nbits - 31 + 20; - if (brsh >= 0) { - b = (qq << (31 - qq_nbits)); - temp = (s32) (ii >> brsh); - if (temp == 0) { - bcmerror = -EBADE; - break; - } - } else { - b = (qq << (31 - qq_nbits)); - temp = (s32) (ii << -brsh); - if (temp == 0) { - bcmerror = -EBADE; - break; - } - } - b /= temp; - b -= a * a; - b = (s32) int_sqrt((unsigned long) b); - b -= (1 << 10); - - if ((curr_core == PHY_CORE_0) && (core_mask & 0x1)) { - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - new_comp.a0 = (s16) a & 0x3ff; - new_comp.b0 = (s16) b & 0x3ff; - } else { - - new_comp.a0 = (s16) b & 0x3ff; - new_comp.b0 = (s16) a & 0x3ff; - } - } - if ((curr_core == PHY_CORE_1) && (core_mask & 0x2)) { - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - new_comp.a1 = (s16) a & 0x3ff; - new_comp.b1 = (s16) b & 0x3ff; - } else { - - new_comp.a1 = (s16) b & 0x3ff; - new_comp.b1 = (s16) a & 0x3ff; - } - } - } - - if (bcmerror != 0) { - pr_debug("%s: Failed, cnt = %d\n", __func__, cal_retry); - - if (cal_retry < CAL_RETRY_CNT) { - cal_retry++; - goto cal_try; - } - - new_comp = old_comp; - } - - wlc_phy_rx_iq_coeffs_nphy(pi, 1, &new_comp); -} - -static void wlc_phy_rxcal_radio_setup_nphy(struct brcms_phy *pi, u8 rx_core) -{ - u16 offtune_val; - u16 bias_g = 0; - u16 bias_a = 0; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if (rx_core == PHY_CORE_0) { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - pi->tx_rx_cal_radio_saveregs[0] = - read_radio_reg(pi, - RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP); - pi->tx_rx_cal_radio_saveregs[1] = - read_radio_reg(pi, - RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN); - - write_radio_reg(pi, - RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP, - 0x3); - write_radio_reg(pi, - RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN, - 0xaf); - - } else { - pi->tx_rx_cal_radio_saveregs[0] = - read_radio_reg(pi, - RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP); - pi->tx_rx_cal_radio_saveregs[1] = - read_radio_reg(pi, - RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN); - - write_radio_reg( - pi, - RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP, - 0x3); - write_radio_reg( - pi, - RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN, - 0x7f); - } - - } else { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - pi->tx_rx_cal_radio_saveregs[0] = - read_radio_reg(pi, - RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP); - pi->tx_rx_cal_radio_saveregs[1] = - read_radio_reg(pi, - RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN); - - write_radio_reg( - pi, - RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP, - 0x3); - write_radio_reg( - pi, - RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN, - 0xaf); - - } else { - pi->tx_rx_cal_radio_saveregs[0] = - read_radio_reg(pi, - RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP); - pi->tx_rx_cal_radio_saveregs[1] = - read_radio_reg(pi, - RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN); - - write_radio_reg(pi, - RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP, - 0x3); - write_radio_reg(pi, - RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN, - 0x7f); - } - } - - } else { - if (rx_core == PHY_CORE_0) { - pi->tx_rx_cal_radio_saveregs[0] = - read_radio_reg(pi, - RADIO_2056_TX_RXIQCAL_TXMUX | - RADIO_2056_TX1); - pi->tx_rx_cal_radio_saveregs[1] = - read_radio_reg(pi, - RADIO_2056_RX_RXIQCAL_RXMUX | - RADIO_2056_RX0); - - if (pi->pubpi.radiorev >= 5) { - pi->tx_rx_cal_radio_saveregs[2] = - read_radio_reg(pi, - RADIO_2056_RX_RXSPARE2 | - RADIO_2056_RX0); - pi->tx_rx_cal_radio_saveregs[3] = - read_radio_reg(pi, - RADIO_2056_TX_TXSPARE2 | - RADIO_2056_TX1); - } - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - - if (pi->pubpi.radiorev >= 5) { - pi->tx_rx_cal_radio_saveregs[4] = - read_radio_reg(pi, - RADIO_2056_RX_LNAA_MASTER - | RADIO_2056_RX0); - - write_radio_reg( - pi, - RADIO_2056_RX_LNAA_MASTER - | RADIO_2056_RX0, 0x40); - - write_radio_reg(pi, - RADIO_2056_TX_TXSPARE2 | - RADIO_2056_TX1, bias_a); - - write_radio_reg(pi, - RADIO_2056_RX_RXSPARE2 | - RADIO_2056_RX0, bias_a); - } else { - pi->tx_rx_cal_radio_saveregs[4] = - read_radio_reg(pi, - RADIO_2056_RX_LNAA_TUNE - | RADIO_2056_RX0); - - offtune_val = - (pi->tx_rx_cal_radio_saveregs - [2] & 0xF0) >> 8; - offtune_val = - (offtune_val <= 0x7) ? 0xF : 0; - - mod_radio_reg(pi, - RADIO_2056_RX_LNAA_TUNE | - RADIO_2056_RX0, 0xF0, - (offtune_val << 8)); - } - - write_radio_reg(pi, - RADIO_2056_TX_RXIQCAL_TXMUX | - RADIO_2056_TX1, 0x9); - write_radio_reg(pi, - RADIO_2056_RX_RXIQCAL_RXMUX | - RADIO_2056_RX0, 0x9); - } else { - if (pi->pubpi.radiorev >= 5) { - pi->tx_rx_cal_radio_saveregs[4] = - read_radio_reg( - pi, - RADIO_2056_RX_LNAG_MASTER - | RADIO_2056_RX0); - - write_radio_reg( - pi, - RADIO_2056_RX_LNAG_MASTER - | RADIO_2056_RX0, 0x40); - - write_radio_reg( - pi, - RADIO_2056_TX_TXSPARE2 - | - RADIO_2056_TX1, bias_g); - - write_radio_reg( - pi, - RADIO_2056_RX_RXSPARE2 - | - RADIO_2056_RX0, bias_g); - - } else { - pi->tx_rx_cal_radio_saveregs[4] = - read_radio_reg( - pi, - RADIO_2056_RX_LNAG_TUNE - | RADIO_2056_RX0); - - offtune_val = - (pi-> - tx_rx_cal_radio_saveregs[2] & - 0xF0) >> 8; - offtune_val = - (offtune_val <= 0x7) ? 0xF : 0; - - mod_radio_reg(pi, - RADIO_2056_RX_LNAG_TUNE | - RADIO_2056_RX0, 0xF0, - (offtune_val << 8)); - } - - write_radio_reg(pi, - RADIO_2056_TX_RXIQCAL_TXMUX | - RADIO_2056_TX1, 0x6); - write_radio_reg(pi, - RADIO_2056_RX_RXIQCAL_RXMUX | - RADIO_2056_RX0, 0x6); - } - - } else { - pi->tx_rx_cal_radio_saveregs[0] = - read_radio_reg(pi, - RADIO_2056_TX_RXIQCAL_TXMUX | - RADIO_2056_TX0); - pi->tx_rx_cal_radio_saveregs[1] = - read_radio_reg(pi, - RADIO_2056_RX_RXIQCAL_RXMUX | - RADIO_2056_RX1); - - if (pi->pubpi.radiorev >= 5) { - pi->tx_rx_cal_radio_saveregs[2] = - read_radio_reg(pi, - RADIO_2056_RX_RXSPARE2 | - RADIO_2056_RX1); - pi->tx_rx_cal_radio_saveregs[3] = - read_radio_reg(pi, - RADIO_2056_TX_TXSPARE2 | - RADIO_2056_TX0); - } - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - - if (pi->pubpi.radiorev >= 5) { - pi->tx_rx_cal_radio_saveregs[4] = - read_radio_reg( - pi, - RADIO_2056_RX_LNAA_MASTER - | RADIO_2056_RX1); - - write_radio_reg( - pi, - RADIO_2056_RX_LNAA_MASTER | - RADIO_2056_RX1, 0x40); - - write_radio_reg( - pi, - RADIO_2056_TX_TXSPARE2 - | - RADIO_2056_TX0, bias_a); - - write_radio_reg( - pi, - RADIO_2056_RX_RXSPARE2 - | - RADIO_2056_RX1, bias_a); - } else { - pi->tx_rx_cal_radio_saveregs[4] = - read_radio_reg( - pi, - RADIO_2056_RX_LNAA_TUNE - | RADIO_2056_RX1); - - offtune_val = - (pi-> - tx_rx_cal_radio_saveregs[2] & - 0xF0) >> 8; - offtune_val = - (offtune_val <= 0x7) ? 0xF : 0; - - mod_radio_reg(pi, - RADIO_2056_RX_LNAA_TUNE | - RADIO_2056_RX1, 0xF0, - (offtune_val << 8)); - } - - write_radio_reg(pi, - RADIO_2056_TX_RXIQCAL_TXMUX | - RADIO_2056_TX0, 0x9); - write_radio_reg(pi, - RADIO_2056_RX_RXIQCAL_RXMUX | - RADIO_2056_RX1, 0x9); - } else { - if (pi->pubpi.radiorev >= 5) { - pi->tx_rx_cal_radio_saveregs[4] = - read_radio_reg( - pi, - RADIO_2056_RX_LNAG_MASTER - | RADIO_2056_RX1); - - write_radio_reg( - pi, - RADIO_2056_RX_LNAG_MASTER - | RADIO_2056_RX1, 0x40); - - write_radio_reg( - pi, - RADIO_2056_TX_TXSPARE2 - | - RADIO_2056_TX0, bias_g); - - write_radio_reg( - pi, - RADIO_2056_RX_RXSPARE2 - | - RADIO_2056_RX1, bias_g); - } else { - pi->tx_rx_cal_radio_saveregs[4] = - read_radio_reg( - pi, - RADIO_2056_RX_LNAG_TUNE - | RADIO_2056_RX1); - - offtune_val = - (pi-> - tx_rx_cal_radio_saveregs[2] & - 0xF0) >> 8; - offtune_val = - (offtune_val <= 0x7) ? 0xF : 0; - - mod_radio_reg(pi, - RADIO_2056_RX_LNAG_TUNE | - RADIO_2056_RX1, 0xF0, - (offtune_val << 8)); - } - - write_radio_reg(pi, - RADIO_2056_TX_RXIQCAL_TXMUX | - RADIO_2056_TX0, 0x6); - write_radio_reg(pi, - RADIO_2056_RX_RXIQCAL_RXMUX | - RADIO_2056_RX1, 0x6); - } - } - } -} - -static void wlc_phy_rxcal_radio_cleanup_nphy(struct brcms_phy *pi, u8 rx_core) -{ - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - if (rx_core == PHY_CORE_0) { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - write_radio_reg( - pi, - RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP, - pi-> - tx_rx_cal_radio_saveregs[0]); - write_radio_reg( - pi, - RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN, - pi-> - tx_rx_cal_radio_saveregs[1]); - - } else { - write_radio_reg( - pi, - RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP, - pi-> - tx_rx_cal_radio_saveregs[0]); - write_radio_reg( - pi, - RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN, - pi-> - tx_rx_cal_radio_saveregs[1]); - } - - } else { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - write_radio_reg( - pi, - RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP, - pi-> - tx_rx_cal_radio_saveregs[0]); - write_radio_reg( - pi, - RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN, - pi-> - tx_rx_cal_radio_saveregs[1]); - - } else { - write_radio_reg( - pi, - RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP, - pi-> - tx_rx_cal_radio_saveregs[0]); - write_radio_reg( - pi, - RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN, - pi-> - tx_rx_cal_radio_saveregs[1]); - } - } - - } else { - if (rx_core == PHY_CORE_0) { - write_radio_reg(pi, - RADIO_2056_TX_RXIQCAL_TXMUX | - RADIO_2056_TX1, - pi->tx_rx_cal_radio_saveregs[0]); - - write_radio_reg(pi, - RADIO_2056_RX_RXIQCAL_RXMUX | - RADIO_2056_RX0, - pi->tx_rx_cal_radio_saveregs[1]); - - if (pi->pubpi.radiorev >= 5) { - write_radio_reg(pi, - RADIO_2056_RX_RXSPARE2 | - RADIO_2056_RX0, - pi-> - tx_rx_cal_radio_saveregs[2]); - - write_radio_reg(pi, - RADIO_2056_TX_TXSPARE2 | - RADIO_2056_TX1, - pi-> - tx_rx_cal_radio_saveregs[3]); - } - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - if (pi->pubpi.radiorev >= 5) - write_radio_reg( - pi, - RADIO_2056_RX_LNAA_MASTER - | RADIO_2056_RX0, - pi-> - tx_rx_cal_radio_saveregs - [4]); - else - write_radio_reg( - pi, - RADIO_2056_RX_LNAA_TUNE - | RADIO_2056_RX0, - pi-> - tx_rx_cal_radio_saveregs - [4]); - } else { - if (pi->pubpi.radiorev >= 5) - write_radio_reg( - pi, - RADIO_2056_RX_LNAG_MASTER - | RADIO_2056_RX0, - pi-> - tx_rx_cal_radio_saveregs - [4]); - else - write_radio_reg( - pi, - RADIO_2056_RX_LNAG_TUNE - | RADIO_2056_RX0, - pi-> - tx_rx_cal_radio_saveregs - [4]); - } - - } else { - write_radio_reg(pi, - RADIO_2056_TX_RXIQCAL_TXMUX | - RADIO_2056_TX0, - pi->tx_rx_cal_radio_saveregs[0]); - - write_radio_reg(pi, - RADIO_2056_RX_RXIQCAL_RXMUX | - RADIO_2056_RX1, - pi->tx_rx_cal_radio_saveregs[1]); - - if (pi->pubpi.radiorev >= 5) { - write_radio_reg(pi, - RADIO_2056_RX_RXSPARE2 | - RADIO_2056_RX1, - pi-> - tx_rx_cal_radio_saveregs[2]); - - write_radio_reg(pi, - RADIO_2056_TX_TXSPARE2 | - RADIO_2056_TX0, - pi-> - tx_rx_cal_radio_saveregs[3]); - } - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - if (pi->pubpi.radiorev >= 5) - write_radio_reg( - pi, - RADIO_2056_RX_LNAA_MASTER - | RADIO_2056_RX1, - pi-> - tx_rx_cal_radio_saveregs - [4]); - else - write_radio_reg( - pi, - RADIO_2056_RX_LNAA_TUNE - | RADIO_2056_RX1, - pi-> - tx_rx_cal_radio_saveregs - [4]); - } else { - if (pi->pubpi.radiorev >= 5) - write_radio_reg( - pi, - RADIO_2056_RX_LNAG_MASTER - | RADIO_2056_RX1, - pi-> - tx_rx_cal_radio_saveregs - [4]); - else - write_radio_reg( - pi, - RADIO_2056_RX_LNAG_TUNE - | RADIO_2056_RX1, - pi-> - tx_rx_cal_radio_saveregs - [4]); - } - } - } -} - -static void wlc_phy_rxcal_physetup_nphy(struct brcms_phy *pi, u8 rx_core) -{ - u8 tx_core; - u16 rx_antval, tx_antval; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - tx_core = rx_core; - else - tx_core = (rx_core == PHY_CORE_0) ? 1 : 0; - - pi->tx_rx_cal_phy_saveregs[0] = read_phy_reg(pi, 0xa2); - pi->tx_rx_cal_phy_saveregs[1] = - read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : 0xa7); - pi->tx_rx_cal_phy_saveregs[2] = - read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5); - pi->tx_rx_cal_phy_saveregs[3] = read_phy_reg(pi, 0x91); - pi->tx_rx_cal_phy_saveregs[4] = read_phy_reg(pi, 0x92); - pi->tx_rx_cal_phy_saveregs[5] = read_phy_reg(pi, 0x7a); - pi->tx_rx_cal_phy_saveregs[6] = read_phy_reg(pi, 0x7d); - pi->tx_rx_cal_phy_saveregs[7] = read_phy_reg(pi, 0xe7); - pi->tx_rx_cal_phy_saveregs[8] = read_phy_reg(pi, 0xec); - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - pi->tx_rx_cal_phy_saveregs[11] = read_phy_reg(pi, 0x342); - pi->tx_rx_cal_phy_saveregs[12] = read_phy_reg(pi, 0x343); - pi->tx_rx_cal_phy_saveregs[13] = read_phy_reg(pi, 0x346); - pi->tx_rx_cal_phy_saveregs[14] = read_phy_reg(pi, 0x347); - } - - pi->tx_rx_cal_phy_saveregs[9] = read_phy_reg(pi, 0x297); - pi->tx_rx_cal_phy_saveregs[10] = read_phy_reg(pi, 0x29b); - mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (0) << 0); - - mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 0), (0) << 0); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - mod_phy_reg(pi, 0xa2, (0xf << 0), (1 << tx_core) << 0); - - mod_phy_reg(pi, 0xa2, (0xf << 12), (1 << (1 - rx_core)) << 12); - - } else { - - mod_phy_reg(pi, 0xa2, (0xf << 12), (1 << tx_core) << 12); - mod_phy_reg(pi, 0xa2, (0xf << 0), (1 << tx_core) << 0); - mod_phy_reg(pi, 0xa2, (0xf << 4), (1 << rx_core) << 4); - mod_phy_reg(pi, 0xa2, (0xf << 8), (1 << rx_core) << 8); - } - - mod_phy_reg(pi, ((rx_core == PHY_CORE_0) ? 0xa6 : 0xa7), (0x1 << 2), 0); - mod_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5, - (0x1 << 2), (0x1 << 2)); - if (NREV_LT(pi->pubpi.phy_rev, 7)) { - mod_phy_reg(pi, ((rx_core == PHY_CORE_0) ? 0xa6 : 0xa7), - (0x1 << 0) | (0x1 << 1), 0); - mod_phy_reg(pi, (rx_core == PHY_CORE_0) ? - 0x8f : 0xa5, - (0x1 << 0) | (0x1 << 1), (0x1 << 0) | (0x1 << 1)); - } - - wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_PA, 0, - RADIO_MIMO_CORESEL_CORE1 | - RADIO_MIMO_CORESEL_CORE2); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), - 0, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID0); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 0, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 1, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 1, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID2); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 0, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - if (CHSPEC_IS40(pi->radio_chanspec)) - wlc_phy_rfctrl_override_nphy_rev7( - pi, - (0x1 << 7), - 2, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - else - wlc_phy_rfctrl_override_nphy_rev7( - pi, - (0x1 << 7), - 0, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7), - 0, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), 0, 0, 0, - NPHY_REV7_RFCTRLOVERRIDE_ID1); - } else { - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 3, 0); - } - - wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - - wlc_phy_rfctrlintc_override_nphy(pi, - NPHY_RfctrlIntc_override_TRSW, - 0x1, rx_core + 1); - } else { - - if (rx_core == PHY_CORE_0) { - rx_antval = 0x1; - tx_antval = 0x8; - } else { - rx_antval = 0x4; - tx_antval = 0x2; - } - - wlc_phy_rfctrlintc_override_nphy(pi, - NPHY_RfctrlIntc_override_TRSW, - rx_antval, rx_core + 1); - wlc_phy_rfctrlintc_override_nphy(pi, - NPHY_RfctrlIntc_override_TRSW, - tx_antval, tx_core + 1); - } -} - -static void wlc_phy_rxcal_phycleanup_nphy(struct brcms_phy *pi, u8 rx_core) -{ - - write_phy_reg(pi, 0xa2, pi->tx_rx_cal_phy_saveregs[0]); - write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : 0xa7, - pi->tx_rx_cal_phy_saveregs[1]); - write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5, - pi->tx_rx_cal_phy_saveregs[2]); - write_phy_reg(pi, 0x91, pi->tx_rx_cal_phy_saveregs[3]); - write_phy_reg(pi, 0x92, pi->tx_rx_cal_phy_saveregs[4]); - - write_phy_reg(pi, 0x7a, pi->tx_rx_cal_phy_saveregs[5]); - write_phy_reg(pi, 0x7d, pi->tx_rx_cal_phy_saveregs[6]); - write_phy_reg(pi, 0xe7, pi->tx_rx_cal_phy_saveregs[7]); - write_phy_reg(pi, 0xec, pi->tx_rx_cal_phy_saveregs[8]); - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - write_phy_reg(pi, 0x342, pi->tx_rx_cal_phy_saveregs[11]); - write_phy_reg(pi, 0x343, pi->tx_rx_cal_phy_saveregs[12]); - write_phy_reg(pi, 0x346, pi->tx_rx_cal_phy_saveregs[13]); - write_phy_reg(pi, 0x347, pi->tx_rx_cal_phy_saveregs[14]); - } - - write_phy_reg(pi, 0x297, pi->tx_rx_cal_phy_saveregs[9]); - write_phy_reg(pi, 0x29b, pi->tx_rx_cal_phy_saveregs[10]); -} - -static void -wlc_phy_rxcal_gainctrl_nphy_rev5(struct brcms_phy *pi, u8 rx_core, - u16 *rxgain, u8 cal_type) -{ - - u16 num_samps; - struct phy_iq_est est[PHY_CORE_MAX]; - u8 tx_core; - struct nphy_iq_comp save_comp, zero_comp; - u32 i_pwr, q_pwr, curr_pwr, optim_pwr = 0, prev_pwr = 0, - thresh_pwr = 10000; - s16 desired_log2_pwr, actual_log2_pwr, delta_pwr; - bool gainctrl_done = false; - u8 mix_tia_gain = 3; - s8 optim_gaintbl_index = 0, prev_gaintbl_index = 0; - s8 curr_gaintbl_index = 3; - u8 gainctrl_dirn = NPHY_RXCAL_GAIN_INIT; - const struct nphy_ipa_txrxgain *nphy_rxcal_gaintbl; - u16 hpvga, lpf_biq1, lpf_biq0, lna2, lna1; - int fine_gain_idx; - s8 txpwrindex; - u16 nphy_rxcal_txgain[2]; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - tx_core = rx_core; - else - tx_core = 1 - rx_core; - - num_samps = 1024; - desired_log2_pwr = (cal_type == 0) ? 13 : 13; - - wlc_phy_rx_iq_coeffs_nphy(pi, 0, &save_comp); - zero_comp.a0 = zero_comp.b0 = zero_comp.a1 = zero_comp.b1 = 0x0; - wlc_phy_rx_iq_coeffs_nphy(pi, 1, &zero_comp); - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) - mix_tia_gain = 3; - else if (NREV_GE(pi->pubpi.phy_rev, 4)) - mix_tia_gain = 4; - else - mix_tia_gain = 6; - if (NREV_GE(pi->pubpi.phy_rev, 7)) - nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_5GHz_rev7; - else - nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_5GHz; - } else { - if (NREV_GE(pi->pubpi.phy_rev, 7)) - nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_2GHz_rev7; - else - nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_2GHz; - } - - do { - - hpvga = (NREV_GE(pi->pubpi.phy_rev, 7)) ? - 0 : nphy_rxcal_gaintbl[curr_gaintbl_index].hpvga; - lpf_biq1 = nphy_rxcal_gaintbl[curr_gaintbl_index].lpf_biq1; - lpf_biq0 = nphy_rxcal_gaintbl[curr_gaintbl_index].lpf_biq0; - lna2 = nphy_rxcal_gaintbl[curr_gaintbl_index].lna2; - lna1 = nphy_rxcal_gaintbl[curr_gaintbl_index].lna1; - txpwrindex = nphy_rxcal_gaintbl[curr_gaintbl_index].txpwrindex; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - wlc_phy_rfctrl_override_1tomany_nphy( - pi, - NPHY_REV7_RfctrlOverride_cmd_rxgain, - ((lpf_biq1 << 12) | - (lpf_biq0 << 8) | - (mix_tia_gain << 4) | (lna2 << 2) - | lna1), 0x3, 0); - else - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), - ((hpvga << 12) | - (lpf_biq1 << 10) | - (lpf_biq0 << 8) | - (mix_tia_gain << 4) | - (lna2 << 2) | lna1), 0x3, - 0); - - pi->nphy_rxcal_pwr_idx[tx_core] = txpwrindex; - - if (txpwrindex == -1) { - nphy_rxcal_txgain[0] = 0x8ff0 | pi->nphy_gmval; - nphy_rxcal_txgain[1] = 0x8ff0 | pi->nphy_gmval; - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, - 2, 0x110, 16, - nphy_rxcal_txgain); - } else { - wlc_phy_txpwr_index_nphy(pi, tx_core + 1, txpwrindex, - false); - } - - wlc_phy_tx_tone_nphy(pi, (CHSPEC_IS40(pi->radio_chanspec)) ? - NPHY_RXCAL_TONEFREQ_40MHz : - NPHY_RXCAL_TONEFREQ_20MHz, - NPHY_RXCAL_TONEAMP, 0, cal_type, false); - - wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0); - i_pwr = (est[rx_core].i_pwr + num_samps / 2) / num_samps; - q_pwr = (est[rx_core].q_pwr + num_samps / 2) / num_samps; - curr_pwr = i_pwr + q_pwr; - - switch (gainctrl_dirn) { - case NPHY_RXCAL_GAIN_INIT: - if (curr_pwr > thresh_pwr) { - gainctrl_dirn = NPHY_RXCAL_GAIN_DOWN; - prev_gaintbl_index = curr_gaintbl_index; - curr_gaintbl_index--; - } else { - gainctrl_dirn = NPHY_RXCAL_GAIN_UP; - prev_gaintbl_index = curr_gaintbl_index; - curr_gaintbl_index++; - } - break; - - case NPHY_RXCAL_GAIN_UP: - if (curr_pwr > thresh_pwr) { - gainctrl_done = true; - optim_pwr = prev_pwr; - optim_gaintbl_index = prev_gaintbl_index; - } else { - prev_gaintbl_index = curr_gaintbl_index; - curr_gaintbl_index++; - } - break; - - case NPHY_RXCAL_GAIN_DOWN: - if (curr_pwr > thresh_pwr) { - prev_gaintbl_index = curr_gaintbl_index; - curr_gaintbl_index--; - } else { - gainctrl_done = true; - optim_pwr = curr_pwr; - optim_gaintbl_index = curr_gaintbl_index; - } - break; - - default: - break; - } - - if ((curr_gaintbl_index < 0) || - (curr_gaintbl_index > NPHY_IPA_RXCAL_MAXGAININDEX)) { - gainctrl_done = true; - optim_pwr = curr_pwr; - optim_gaintbl_index = prev_gaintbl_index; - } else { - prev_pwr = curr_pwr; - } - - wlc_phy_stopplayback_nphy(pi); - } while (!gainctrl_done); - - hpvga = nphy_rxcal_gaintbl[optim_gaintbl_index].hpvga; - lpf_biq1 = nphy_rxcal_gaintbl[optim_gaintbl_index].lpf_biq1; - lpf_biq0 = nphy_rxcal_gaintbl[optim_gaintbl_index].lpf_biq0; - lna2 = nphy_rxcal_gaintbl[optim_gaintbl_index].lna2; - lna1 = nphy_rxcal_gaintbl[optim_gaintbl_index].lna1; - txpwrindex = nphy_rxcal_gaintbl[optim_gaintbl_index].txpwrindex; - - actual_log2_pwr = wlc_phy_nbits(optim_pwr); - delta_pwr = desired_log2_pwr - actual_log2_pwr; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - fine_gain_idx = (int)lpf_biq1 + delta_pwr; - - if (fine_gain_idx + (int)lpf_biq0 > 10) - lpf_biq1 = 10 - lpf_biq0; - else - lpf_biq1 = (u16) max(fine_gain_idx, 0); - - wlc_phy_rfctrl_override_1tomany_nphy( - pi, - NPHY_REV7_RfctrlOverride_cmd_rxgain, - ((lpf_biq1 << 12) | - (lpf_biq0 << 8) | - (mix_tia_gain << 4) | - (lna2 << 2) | lna1), 0x3, - 0); - } else { - hpvga = (u16) max(min(((int)hpvga) + delta_pwr, 10), 0); - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), - ((hpvga << 12) | - (lpf_biq1 << 10) | - (lpf_biq0 << 8) | - (mix_tia_gain << 4) | - (lna2 << 2) | - lna1), 0x3, 0); - } - - if (rxgain != NULL) { - *rxgain++ = lna1; - *rxgain++ = lna2; - *rxgain++ = mix_tia_gain; - *rxgain++ = lpf_biq0; - *rxgain++ = lpf_biq1; - *rxgain = hpvga; - } - - wlc_phy_rx_iq_coeffs_nphy(pi, 1, &save_comp); -} - -static void -wlc_phy_rxcal_gainctrl_nphy(struct brcms_phy *pi, u8 rx_core, u16 *rxgain, - u8 cal_type) -{ - wlc_phy_rxcal_gainctrl_nphy_rev5(pi, rx_core, rxgain, cal_type); -} - -static u8 -wlc_phy_rc_sweep_nphy(struct brcms_phy *pi, u8 core_idx, u8 loopback_type) -{ - u32 target_bws[2] = { 9500, 21000 }; - u32 ref_tones[2] = { 3000, 6000 }; - u32 target_bw, ref_tone; - - u32 target_pwr_ratios[2] = { 28606, 18468 }; - u32 target_pwr_ratio, pwr_ratio, last_pwr_ratio = 0; - - u16 start_rccal_ovr_val = 128; - u16 txlpf_rccal_lpc_ovr_val = 128; - u16 rxlpf_rccal_hpc_ovr_val = 159; - - u16 orig_txlpf_rccal_lpc_ovr_val; - u16 orig_rxlpf_rccal_hpc_ovr_val; - u16 radio_addr_offset_rx; - u16 radio_addr_offset_tx; - u16 orig_dcBypass; - u16 orig_RxStrnFilt40Num[6]; - u16 orig_RxStrnFilt40Den[4]; - u16 orig_rfctrloverride[2]; - u16 orig_rfctrlauxreg[2]; - u16 orig_rfctrlrssiothers; - u16 tx_lpf_bw = 4; - - u16 rx_lpf_bw, rx_lpf_bws[2] = { 2, 4 }; - u16 lpf_hpc = 7, hpvga_hpc = 7; - - s8 rccal_stepsize; - u16 rccal_val, last_rccal_val = 0, best_rccal_val = 0; - u32 ref_iq_vals = 0, target_iq_vals = 0; - u16 num_samps, log_num_samps = 10; - struct phy_iq_est est[PHY_CORE_MAX]; - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - return 0; - - num_samps = (1 << log_num_samps); - - if (CHSPEC_IS40(pi->radio_chanspec)) { - target_bw = target_bws[1]; - target_pwr_ratio = target_pwr_ratios[1]; - ref_tone = ref_tones[1]; - rx_lpf_bw = rx_lpf_bws[1]; - } else { - target_bw = target_bws[0]; - target_pwr_ratio = target_pwr_ratios[0]; - ref_tone = ref_tones[0]; - rx_lpf_bw = rx_lpf_bws[0]; - } - - if (core_idx == 0) { - radio_addr_offset_rx = RADIO_2056_RX0; - radio_addr_offset_tx = - (loopback_type == 0) ? RADIO_2056_TX0 : RADIO_2056_TX1; - } else { - radio_addr_offset_rx = RADIO_2056_RX1; - radio_addr_offset_tx = - (loopback_type == 0) ? RADIO_2056_TX1 : RADIO_2056_TX0; - } - - orig_txlpf_rccal_lpc_ovr_val = - read_radio_reg(pi, - (RADIO_2056_TX_TXLPF_RCCAL | - radio_addr_offset_tx)); - orig_rxlpf_rccal_hpc_ovr_val = - read_radio_reg(pi, - (RADIO_2056_RX_RXLPF_RCCAL_HPC | - radio_addr_offset_rx)); - - orig_dcBypass = ((read_phy_reg(pi, 0x48) >> 8) & 1); - - orig_RxStrnFilt40Num[0] = read_phy_reg(pi, 0x267); - orig_RxStrnFilt40Num[1] = read_phy_reg(pi, 0x268); - orig_RxStrnFilt40Num[2] = read_phy_reg(pi, 0x269); - orig_RxStrnFilt40Den[0] = read_phy_reg(pi, 0x26a); - orig_RxStrnFilt40Den[1] = read_phy_reg(pi, 0x26b); - orig_RxStrnFilt40Num[3] = read_phy_reg(pi, 0x26c); - orig_RxStrnFilt40Num[4] = read_phy_reg(pi, 0x26d); - orig_RxStrnFilt40Num[5] = read_phy_reg(pi, 0x26e); - orig_RxStrnFilt40Den[2] = read_phy_reg(pi, 0x26f); - orig_RxStrnFilt40Den[3] = read_phy_reg(pi, 0x270); - - orig_rfctrloverride[0] = read_phy_reg(pi, 0xe7); - orig_rfctrloverride[1] = read_phy_reg(pi, 0xec); - orig_rfctrlauxreg[0] = read_phy_reg(pi, 0xf8); - orig_rfctrlauxreg[1] = read_phy_reg(pi, 0xfa); - orig_rfctrlrssiothers = read_phy_reg(pi, (core_idx == 0) ? 0x7a : 0x7d); - - write_radio_reg(pi, (RADIO_2056_TX_TXLPF_RCCAL | radio_addr_offset_tx), - txlpf_rccal_lpc_ovr_val); - - write_radio_reg(pi, - (RADIO_2056_RX_RXLPF_RCCAL_HPC | radio_addr_offset_rx), - rxlpf_rccal_hpc_ovr_val); - - mod_phy_reg(pi, 0x48, (0x1 << 8), (0x1 << 8)); - - write_phy_reg(pi, 0x267, 0x02d4); - write_phy_reg(pi, 0x268, 0x0000); - write_phy_reg(pi, 0x269, 0x0000); - write_phy_reg(pi, 0x26a, 0x0000); - write_phy_reg(pi, 0x26b, 0x0000); - write_phy_reg(pi, 0x26c, 0x02d4); - write_phy_reg(pi, 0x26d, 0x0000); - write_phy_reg(pi, 0x26e, 0x0000); - write_phy_reg(pi, 0x26f, 0x0000); - write_phy_reg(pi, 0x270, 0x0000); - - or_phy_reg(pi, (core_idx == 0) ? 0xe7 : 0xec, (0x1 << 8)); - or_phy_reg(pi, (core_idx == 0) ? 0xec : 0xe7, (0x1 << 15)); - or_phy_reg(pi, (core_idx == 0) ? 0xe7 : 0xec, (0x1 << 9)); - or_phy_reg(pi, (core_idx == 0) ? 0xe7 : 0xec, (0x1 << 10)); - - mod_phy_reg(pi, (core_idx == 0) ? 0xfa : 0xf8, - (0x7 << 10), (tx_lpf_bw << 10)); - mod_phy_reg(pi, (core_idx == 0) ? 0xf8 : 0xfa, - (0x7 << 0), (hpvga_hpc << 0)); - mod_phy_reg(pi, (core_idx == 0) ? 0xf8 : 0xfa, - (0x7 << 4), (lpf_hpc << 4)); - mod_phy_reg(pi, (core_idx == 0) ? 0x7a : 0x7d, - (0x7 << 8), (rx_lpf_bw << 8)); - - rccal_stepsize = 16; - rccal_val = start_rccal_ovr_val + rccal_stepsize; - - while (rccal_stepsize >= 0) { - write_radio_reg(pi, - (RADIO_2056_RX_RXLPF_RCCAL_LPC | - radio_addr_offset_rx), rccal_val); - - if (rccal_stepsize == 16) { - - wlc_phy_tx_tone_nphy(pi, ref_tone, NPHY_RXCAL_TONEAMP, - 0, 1, false); - udelay(2); - - wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0); - - if (core_idx == 0) - ref_iq_vals = - max_t(u32, (est[0].i_pwr + - est[0].q_pwr) >> - (log_num_samps + 1), - 1); - else - ref_iq_vals = - max_t(u32, (est[1].i_pwr + - est[1].q_pwr) >> - (log_num_samps + 1), - 1); - - wlc_phy_tx_tone_nphy(pi, target_bw, NPHY_RXCAL_TONEAMP, - 0, 1, false); - udelay(2); - } - - wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0); - - if (core_idx == 0) - target_iq_vals = (est[0].i_pwr + est[0].q_pwr) >> - (log_num_samps + 1); - else - target_iq_vals = - (est[1].i_pwr + - est[1].q_pwr) >> (log_num_samps + 1); - - pwr_ratio = (uint) ((target_iq_vals << 16) / ref_iq_vals); - - if (rccal_stepsize == 0) - rccal_stepsize--; - else if (rccal_stepsize == 1) { - last_rccal_val = rccal_val; - rccal_val += (pwr_ratio > target_pwr_ratio) ? 1 : -1; - last_pwr_ratio = pwr_ratio; - rccal_stepsize--; - } else { - rccal_stepsize = (rccal_stepsize >> 1); - rccal_val += ((pwr_ratio > target_pwr_ratio) ? - rccal_stepsize : (-rccal_stepsize)); - } - - if (rccal_stepsize == -1) { - best_rccal_val = - (abs((int)last_pwr_ratio - - (int)target_pwr_ratio) < - abs((int)pwr_ratio - - (int)target_pwr_ratio)) ? last_rccal_val : - rccal_val; - - if (CHSPEC_IS40(pi->radio_chanspec)) { - if ((best_rccal_val > 140) - || (best_rccal_val < 135)) - best_rccal_val = 138; - } else { - if ((best_rccal_val > 142) - || (best_rccal_val < 137)) - best_rccal_val = 140; - } - - write_radio_reg(pi, - (RADIO_2056_RX_RXLPF_RCCAL_LPC | - radio_addr_offset_rx), best_rccal_val); - } - } - - wlc_phy_stopplayback_nphy(pi); - - write_radio_reg(pi, (RADIO_2056_TX_TXLPF_RCCAL | radio_addr_offset_tx), - orig_txlpf_rccal_lpc_ovr_val); - write_radio_reg(pi, - (RADIO_2056_RX_RXLPF_RCCAL_HPC | radio_addr_offset_rx), - orig_rxlpf_rccal_hpc_ovr_val); - - mod_phy_reg(pi, 0x48, (0x1 << 8), (orig_dcBypass << 8)); - - write_phy_reg(pi, 0x267, orig_RxStrnFilt40Num[0]); - write_phy_reg(pi, 0x268, orig_RxStrnFilt40Num[1]); - write_phy_reg(pi, 0x269, orig_RxStrnFilt40Num[2]); - write_phy_reg(pi, 0x26a, orig_RxStrnFilt40Den[0]); - write_phy_reg(pi, 0x26b, orig_RxStrnFilt40Den[1]); - write_phy_reg(pi, 0x26c, orig_RxStrnFilt40Num[3]); - write_phy_reg(pi, 0x26d, orig_RxStrnFilt40Num[4]); - write_phy_reg(pi, 0x26e, orig_RxStrnFilt40Num[5]); - write_phy_reg(pi, 0x26f, orig_RxStrnFilt40Den[2]); - write_phy_reg(pi, 0x270, orig_RxStrnFilt40Den[3]); - - write_phy_reg(pi, 0xe7, orig_rfctrloverride[0]); - write_phy_reg(pi, 0xec, orig_rfctrloverride[1]); - write_phy_reg(pi, 0xf8, orig_rfctrlauxreg[0]); - write_phy_reg(pi, 0xfa, orig_rfctrlauxreg[1]); - write_phy_reg(pi, (core_idx == 0) ? 0x7a : 0x7d, orig_rfctrlrssiothers); - - pi->nphy_anarxlpf_adjusted = false; - - return best_rccal_val - 0x80; -} - -#define WAIT_FOR_SCOPE 4000 -static int wlc_phy_cal_rxiq_nphy_rev3(struct brcms_phy *pi, - struct nphy_txgains target_gain, - u8 cal_type, bool debug) -{ - u16 orig_BBConfig; - u8 core_no, rx_core; - u8 best_rccal[2]; - u16 gain_save[2]; - u16 cal_gain[2]; - struct nphy_iqcal_params cal_params[2]; - u8 rxcore_state; - s8 rxlpf_rccal_hpc, txlpf_rccal_lpc; - s8 txlpf_idac; - bool phyhang_avoid_state = false; - bool skip_rxiqcal = false; - - orig_BBConfig = read_phy_reg(pi, 0x01); - mod_phy_reg(pi, 0x01, (0x1 << 15), 0); - - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - if (NREV_GE(pi->pubpi.phy_rev, 4)) { - phyhang_avoid_state = pi->phyhang_avoid; - pi->phyhang_avoid = false; - } - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); - - for (core_no = 0; core_no <= 1; core_no++) { - wlc_phy_iqcal_gainparams_nphy(pi, core_no, target_gain, - &cal_params[core_no]); - cal_gain[core_no] = cal_params[core_no].cal_gain; - } - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, cal_gain); - - rxcore_state = wlc_phy_rxcore_getstate_nphy( - (struct brcms_phy_pub *) pi); - - for (rx_core = 0; rx_core < pi->pubpi.phy_corenum; rx_core++) { - - skip_rxiqcal = - ((rxcore_state & (1 << rx_core)) == 0) ? true : false; - - wlc_phy_rxcal_physetup_nphy(pi, rx_core); - - wlc_phy_rxcal_radio_setup_nphy(pi, rx_core); - - if ((!skip_rxiqcal) && ((cal_type == 0) || (cal_type == 2))) { - - wlc_phy_rxcal_gainctrl_nphy(pi, rx_core, NULL, 0); - - wlc_phy_tx_tone_nphy(pi, - (CHSPEC_IS40( - pi->radio_chanspec)) ? - NPHY_RXCAL_TONEFREQ_40MHz : - NPHY_RXCAL_TONEFREQ_20MHz, - NPHY_RXCAL_TONEAMP, 0, cal_type, - false); - - if (debug) - mdelay(WAIT_FOR_SCOPE); - - wlc_phy_calc_rx_iq_comp_nphy(pi, rx_core + 1); - wlc_phy_stopplayback_nphy(pi); - } - - if (((cal_type == 1) || (cal_type == 2)) - && NREV_LT(pi->pubpi.phy_rev, 7)) { - - if (rx_core == PHY_CORE_1) { - - if (rxcore_state == 1) - wlc_phy_rxcore_setstate_nphy( - (struct brcms_phy_pub *) pi, 3); - - wlc_phy_rxcal_gainctrl_nphy(pi, rx_core, NULL, - 1); - - best_rccal[rx_core] = - wlc_phy_rc_sweep_nphy(pi, rx_core, 1); - pi->nphy_rccal_value = best_rccal[rx_core]; - - if (rxcore_state == 1) - wlc_phy_rxcore_setstate_nphy( - (struct brcms_phy_pub *) pi, - rxcore_state); - } - } - - wlc_phy_rxcal_radio_cleanup_nphy(pi, rx_core); - - wlc_phy_rxcal_phycleanup_nphy(pi, rx_core); - wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); - } - - if ((cal_type == 1) || (cal_type == 2)) { - - best_rccal[0] = best_rccal[1]; - write_radio_reg(pi, - (RADIO_2056_RX_RXLPF_RCCAL_LPC | - RADIO_2056_RX0), (best_rccal[0] | 0x80)); - - for (rx_core = 0; rx_core < pi->pubpi.phy_corenum; rx_core++) { - rxlpf_rccal_hpc = - (((int)best_rccal[rx_core] - 12) >> 1) + 10; - txlpf_rccal_lpc = ((int)best_rccal[rx_core] - 12) + 10; - - if (PHY_IPA(pi)) { - txlpf_rccal_lpc += - (pi->bw == WL_CHANSPEC_BW_40) ? 24 : 12; - txlpf_idac = (pi->bw == WL_CHANSPEC_BW_40) ? - 0x0e : 0x13; - WRITE_RADIO_REG2(pi, RADIO_2056, TX, rx_core, - TXLPF_IDAC_4, txlpf_idac); - } - - rxlpf_rccal_hpc = max(min_t(u8, rxlpf_rccal_hpc, 31), - 0); - txlpf_rccal_lpc = max(min_t(u8, txlpf_rccal_lpc, 31), - 0); - - write_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_HPC | - ((rx_core == - PHY_CORE_0) ? RADIO_2056_RX0 : - RADIO_2056_RX1)), - (rxlpf_rccal_hpc | 0x80)); - - write_radio_reg(pi, (RADIO_2056_TX_TXLPF_RCCAL | - ((rx_core == - PHY_CORE_0) ? RADIO_2056_TX0 : - RADIO_2056_TX1)), - (txlpf_rccal_lpc | 0x80)); - } - } - - write_phy_reg(pi, 0x01, orig_BBConfig); - - wlc_phy_resetcca_nphy(pi); - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - wlc_phy_rfctrl_override_1tomany_nphy( - pi, - NPHY_REV7_RfctrlOverride_cmd_rxgain, - 0, 0x3, 1); - else - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 1); - - wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, - gain_save); - - if (NREV_GE(pi->pubpi.phy_rev, 4)) - pi->phyhang_avoid = phyhang_avoid_state; - - wlc_phy_stay_in_carriersearch_nphy(pi, false); - - return 0; -} - -static int -wlc_phy_cal_rxiq_nphy_rev2(struct brcms_phy *pi, - struct nphy_txgains target_gain, bool debug) -{ - struct phy_iq_est est[PHY_CORE_MAX]; - u8 core_num, rx_core, tx_core; - u16 lna_vals[] = { 0x3, 0x3, 0x1 }; - u16 hpf1_vals[] = { 0x7, 0x2, 0x0 }; - u16 hpf2_vals[] = { 0x2, 0x0, 0x0 }; - s16 curr_hpf1, curr_hpf2, curr_hpf, curr_lna; - s16 desired_log2_pwr, actual_log2_pwr, hpf_change; - u16 orig_RfseqCoreActv, orig_AfectrlCore, orig_AfectrlOverride; - u16 orig_RfctrlIntcRx, orig_RfctrlIntcTx; - u16 num_samps; - u32 i_pwr, q_pwr, tot_pwr[3]; - u8 gain_pass, use_hpf_num; - u16 mask, val1, val2; - u16 core_no; - u16 gain_save[2]; - u16 cal_gain[2]; - struct nphy_iqcal_params cal_params[2]; - u8 phy_bw; - int bcmerror = 0; - bool first_playtone = true; - - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - if (NREV_LT(pi->pubpi.phy_rev, 2)) - wlc_phy_reapply_txcal_coeffs_nphy(pi); - - wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); - - for (core_no = 0; core_no <= 1; core_no++) { - wlc_phy_iqcal_gainparams_nphy(pi, core_no, target_gain, - &cal_params[core_no]); - cal_gain[core_no] = cal_params[core_no].cal_gain; - } - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, cal_gain); - - num_samps = 1024; - desired_log2_pwr = 13; - - for (core_num = 0; core_num < 2; core_num++) { - - rx_core = core_num; - tx_core = 1 - core_num; - - orig_RfseqCoreActv = read_phy_reg(pi, 0xa2); - orig_AfectrlCore = read_phy_reg(pi, (rx_core == PHY_CORE_0) ? - 0xa6 : 0xa7); - orig_AfectrlOverride = read_phy_reg(pi, 0xa5); - orig_RfctrlIntcRx = read_phy_reg(pi, (rx_core == PHY_CORE_0) ? - 0x91 : 0x92); - orig_RfctrlIntcTx = read_phy_reg(pi, (tx_core == PHY_CORE_0) ? - 0x91 : 0x92); - - mod_phy_reg(pi, 0xa2, (0xf << 12), (1 << tx_core) << 12); - mod_phy_reg(pi, 0xa2, (0xf << 0), (1 << tx_core) << 0); - - or_phy_reg(pi, ((rx_core == PHY_CORE_0) ? 0xa6 : 0xa7), - ((0x1 << 1) | (0x1 << 2))); - or_phy_reg(pi, 0xa5, ((0x1 << 1) | (0x1 << 2))); - - if (((pi->nphy_rxcalparams) & 0xff000000)) - write_phy_reg(pi, - (rx_core == PHY_CORE_0) ? 0x91 : 0x92, - (CHSPEC_IS5G(pi->radio_chanspec) ? - 0x140 : 0x110)); - else - write_phy_reg(pi, - (rx_core == PHY_CORE_0) ? 0x91 : 0x92, - (CHSPEC_IS5G(pi->radio_chanspec) ? - 0x180 : 0x120)); - - write_phy_reg(pi, (tx_core == PHY_CORE_0) ? 0x91 : 0x92, - (CHSPEC_IS5G(pi->radio_chanspec) ? 0x148 : - 0x114)); - - mask = RADIO_2055_COUPLE_RX_MASK | RADIO_2055_COUPLE_TX_MASK; - if (rx_core == PHY_CORE_0) { - val1 = RADIO_2055_COUPLE_RX_MASK; - val2 = RADIO_2055_COUPLE_TX_MASK; - } else { - val1 = RADIO_2055_COUPLE_TX_MASK; - val2 = RADIO_2055_COUPLE_RX_MASK; - } - - if ((pi->nphy_rxcalparams & 0x10000)) { - mod_radio_reg(pi, RADIO_2055_CORE1_GEN_SPARE2, mask, - val1); - mod_radio_reg(pi, RADIO_2055_CORE2_GEN_SPARE2, mask, - val2); - } - - for (gain_pass = 0; gain_pass < 4; gain_pass++) { - - if (debug) - mdelay(WAIT_FOR_SCOPE); - - if (gain_pass < 3) { - curr_lna = lna_vals[gain_pass]; - curr_hpf1 = hpf1_vals[gain_pass]; - curr_hpf2 = hpf2_vals[gain_pass]; - } else { - - if (tot_pwr[1] > 10000) { - curr_lna = lna_vals[2]; - curr_hpf1 = hpf1_vals[2]; - curr_hpf2 = hpf2_vals[2]; - use_hpf_num = 1; - curr_hpf = curr_hpf1; - actual_log2_pwr = - wlc_phy_nbits(tot_pwr[2]); - } else { - if (tot_pwr[0] > 10000) { - curr_lna = lna_vals[1]; - curr_hpf1 = hpf1_vals[1]; - curr_hpf2 = hpf2_vals[1]; - use_hpf_num = 1; - curr_hpf = curr_hpf1; - actual_log2_pwr = - wlc_phy_nbits( - tot_pwr[1]); - } else { - curr_lna = lna_vals[0]; - curr_hpf1 = hpf1_vals[0]; - curr_hpf2 = hpf2_vals[0]; - use_hpf_num = 2; - curr_hpf = curr_hpf2; - actual_log2_pwr = - wlc_phy_nbits( - tot_pwr[0]); - } - } - - hpf_change = desired_log2_pwr - actual_log2_pwr; - curr_hpf += hpf_change; - curr_hpf = max(min_t(u16, curr_hpf, 10), 0); - if (use_hpf_num == 1) - curr_hpf1 = curr_hpf; - else - curr_hpf2 = curr_hpf; - } - - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 10), - ((curr_hpf2 << 8) | - (curr_hpf1 << 4) | - (curr_lna << 2)), 0x3, 0); - wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); - - wlc_phy_stopplayback_nphy(pi); - - if (first_playtone) { - bcmerror = wlc_phy_tx_tone_nphy(pi, 4000, - (u16) (pi->nphy_rxcalparams & - 0xffff), 0, 0, true); - first_playtone = false; - } else { - phy_bw = (CHSPEC_IS40(pi->radio_chanspec)) ? - 40 : 20; - wlc_phy_runsamples_nphy(pi, phy_bw * 8, 0xffff, - 0, 0, 0, true); - } - - if (bcmerror == 0) { - if (gain_pass < 3) { - - wlc_phy_rx_iq_est_nphy(pi, est, - num_samps, 32, - 0); - i_pwr = (est[rx_core].i_pwr + - num_samps / 2) / num_samps; - q_pwr = (est[rx_core].q_pwr + - num_samps / 2) / num_samps; - tot_pwr[gain_pass] = i_pwr + q_pwr; - } else { - - wlc_phy_calc_rx_iq_comp_nphy(pi, - (1 << - rx_core)); - } - - wlc_phy_stopplayback_nphy(pi); - } - - if (bcmerror != 0) - break; - } - - and_radio_reg(pi, RADIO_2055_CORE1_GEN_SPARE2, ~mask); - and_radio_reg(pi, RADIO_2055_CORE2_GEN_SPARE2, ~mask); - - write_phy_reg(pi, (tx_core == PHY_CORE_0) ? 0x91 : - 0x92, orig_RfctrlIntcTx); - write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x91 : - 0x92, orig_RfctrlIntcRx); - write_phy_reg(pi, 0xa5, orig_AfectrlOverride); - write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : - 0xa7, orig_AfectrlCore); - write_phy_reg(pi, 0xa2, orig_RfseqCoreActv); - - if (bcmerror != 0) - break; - } - - wlc_phy_rfctrl_override_nphy(pi, (0x1 << 10), 0, 0x3, 1); - wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, - gain_save); - - wlc_phy_stay_in_carriersearch_nphy(pi, false); - - return bcmerror; -} - -int -wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, - u8 cal_type, bool debug) -{ - if (NREV_GE(pi->pubpi.phy_rev, 7)) - cal_type = 0; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) - return wlc_phy_cal_rxiq_nphy_rev3(pi, target_gain, cal_type, - debug); - else - return wlc_phy_cal_rxiq_nphy_rev2(pi, target_gain, debug); -} - -void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi) -{ - uint core; - u32 txgain; - u16 rad_gain, dac_gain, bbmult, m1m2; - u8 txpi[2], chan_freq_range; - s32 rfpwr_offset; - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - if (pi->sh->sromrev < 4) { - txpi[0] = txpi[1] = 72; - } else { - - chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); - switch (chan_freq_range) { - case WL_CHAN_FREQ_RANGE_2G: - case WL_CHAN_FREQ_RANGE_5GL: - case WL_CHAN_FREQ_RANGE_5GM: - case WL_CHAN_FREQ_RANGE_5GH: - txpi[0] = 0; - txpi[1] = 0; - break; - default: - txpi[0] = txpi[1] = 91; - break; - } - } - - if (NREV_GE(pi->pubpi.phy_rev, 7)) - txpi[0] = txpi[1] = 30; - else if (NREV_GE(pi->pubpi.phy_rev, 3)) - txpi[0] = txpi[1] = 40; - - if (NREV_LT(pi->pubpi.phy_rev, 7)) { - - if ((txpi[0] < 40) || (txpi[0] > 100) || - (txpi[1] < 40) || (txpi[1] > 100)) - txpi[0] = txpi[1] = 91; - } - - pi->nphy_txpwrindex[PHY_CORE_0].index_internal = txpi[0]; - pi->nphy_txpwrindex[PHY_CORE_1].index_internal = txpi[1]; - pi->nphy_txpwrindex[PHY_CORE_0].index_internal_save = txpi[0]; - pi->nphy_txpwrindex[PHY_CORE_1].index_internal_save = txpi[1]; - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - uint phyrev = pi->pubpi.phy_rev; - - if (NREV_GE(phyrev, 3)) { - if (PHY_IPA(pi)) { - u32 *tx_gaintbl = - wlc_phy_get_ipa_gaintbl_nphy(pi); - txgain = tx_gaintbl[txpi[core]]; - } else { - if (CHSPEC_IS5G(pi->radio_chanspec)) { - if (NREV_IS(phyrev, 3)) { - txgain = - nphy_tpc_5GHz_txgain_rev3 - [txpi[core]]; - } else if (NREV_IS(phyrev, 4)) { - txgain = ( - pi->srom_fem5g.extpagain == - 3) ? - nphy_tpc_5GHz_txgain_HiPwrEPA - [txpi[core]] : - nphy_tpc_5GHz_txgain_rev4 - [txpi[core]]; - } else { - txgain = - nphy_tpc_5GHz_txgain_rev5 - [txpi[core]]; - } - } else { - if (NREV_GE(phyrev, 5) && - (pi->srom_fem2g.extpagain == 3)) { - txgain = - nphy_tpc_txgain_HiPwrEPA - [txpi[core]]; - } else { - txgain = nphy_tpc_txgain_rev3 - [txpi[core]]; - } - } - } - } else { - txgain = nphy_tpc_txgain[txpi[core]]; - } - - if (NREV_GE(phyrev, 3)) - rad_gain = (txgain >> 16) & ((1 << (32 - 16 + 1)) - 1); - else - rad_gain = (txgain >> 16) & ((1 << (28 - 16 + 1)) - 1); - - if (NREV_GE(phyrev, 7)) - dac_gain = (txgain >> 8) & ((1 << (10 - 8 + 1)) - 1); - else - dac_gain = (txgain >> 8) & ((1 << (13 - 8 + 1)) - 1); - - bbmult = (txgain >> 0) & ((1 << (7 - 0 + 1)) - 1); - - if (NREV_GE(phyrev, 3)) - mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : - 0xa5), (0x1 << 8), (0x1 << 8)); - else - mod_phy_reg(pi, 0xa5, (0x1 << 14), (0x1 << 14)); - - write_phy_reg(pi, (core == PHY_CORE_0) ? 0xaa : 0xab, dac_gain); - - wlc_phy_table_write_nphy(pi, 7, 1, (0x110 + core), 16, - &rad_gain); - - wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m1m2); - m1m2 &= ((core == PHY_CORE_0) ? 0x00ff : 0xff00); - m1m2 |= ((core == PHY_CORE_0) ? (bbmult << 8) : (bbmult << 0)); - wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m1m2); - - if (PHY_IPA(pi)) { - wlc_phy_table_read_nphy(pi, - (core == - PHY_CORE_0 ? - NPHY_TBL_ID_CORE1TXPWRCTL : - NPHY_TBL_ID_CORE2TXPWRCTL), 1, - 576 + txpi[core], 32, - &rfpwr_offset); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1ff << 4), - ((s16) rfpwr_offset) << 4); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 2), (1) << 2); - - } - } - - and_phy_reg(pi, 0xbf, (u16) (~(0x1f << 0))); - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -static void -wlc_phy_txpwr_nphy_srom_convert(u8 *srom_max, u16 *pwr_offset, - u8 tmp_max_pwr, u8 rate_start, - u8 rate_end) -{ - u8 rate; - u8 word_num, nibble_num; - u8 tmp_nibble; - - for (rate = rate_start; rate <= rate_end; rate++) { - word_num = (rate - rate_start) >> 2; - nibble_num = (rate - rate_start) & 0x3; - tmp_nibble = (pwr_offset[word_num] >> 4 * nibble_num) & 0xf; - - srom_max[rate] = tmp_max_pwr - 2 * tmp_nibble; - } -} - -static void -wlc_phy_txpwr_nphy_po_apply(u8 *srom_max, u8 pwr_offset, - u8 rate_start, u8 rate_end) -{ - u8 rate; - - for (rate = rate_start; rate <= rate_end; rate++) - srom_max[rate] -= 2 * pwr_offset; -} - -void -wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start, - u8 rate_mcs_end, u8 rate_ofdm_start) -{ - u8 rate1, rate2; - - rate2 = rate_ofdm_start; - for (rate1 = rate_mcs_start; rate1 <= rate_mcs_end - 1; rate1++) { - power[rate1] = power[rate2]; - rate2 += (rate1 == rate_mcs_start) ? 2 : 1; - } - power[rate_mcs_end] = power[rate_mcs_end - 1]; -} - -void -wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, u8 rate_ofdm_start, - u8 rate_ofdm_end, u8 rate_mcs_start) -{ - u8 rate1, rate2; - - for (rate1 = rate_ofdm_start, rate2 = rate_mcs_start; - rate1 <= rate_ofdm_end; rate1++, rate2++) { - power[rate1] = power[rate2]; - if (rate1 == rate_ofdm_start) - power[++rate1] = power[rate2]; - } -} - -void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi) -{ - uint rate1, rate2, band_num; - u8 tmp_bw40po = 0, tmp_cddpo = 0, tmp_stbcpo = 0; - u8 tmp_max_pwr = 0; - u16 pwr_offsets1[2], *pwr_offsets2 = NULL; - u8 *tx_srom_max_rate = NULL; - - for (band_num = 0; band_num < (CH_2G_GROUP + CH_5G_GROUP); - band_num++) { - switch (band_num) { - case 0: - - tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_2g, - pi->nphy_pwrctrl_info[1].max_pwr_2g); - - pwr_offsets1[0] = pi->cck2gpo; - wlc_phy_txpwr_nphy_srom_convert(pi->tx_srom_max_rate_2g, - pwr_offsets1, - tmp_max_pwr, - TXP_FIRST_CCK, - TXP_LAST_CCK); - - pwr_offsets1[0] = (u16) (pi->ofdm2gpo & 0xffff); - pwr_offsets1[1] = - (u16) (pi->ofdm2gpo >> 16) & 0xffff; - - pwr_offsets2 = pi->mcs2gpo; - - tmp_cddpo = pi->cdd2gpo; - tmp_stbcpo = pi->stbc2gpo; - tmp_bw40po = pi->bw402gpo; - - tx_srom_max_rate = pi->tx_srom_max_rate_2g; - break; - case 1: - - tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_5gm, - pi->nphy_pwrctrl_info[1].max_pwr_5gm); - - pwr_offsets1[0] = (u16) (pi->ofdm5gpo & 0xffff); - pwr_offsets1[1] = - (u16) (pi->ofdm5gpo >> 16) & 0xffff; - - pwr_offsets2 = pi->mcs5gpo; - - tmp_cddpo = pi->cdd5gpo; - tmp_stbcpo = pi->stbc5gpo; - tmp_bw40po = pi->bw405gpo; - - tx_srom_max_rate = pi->tx_srom_max_rate_5g_mid; - break; - case 2: - - tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_5gl, - pi->nphy_pwrctrl_info[1].max_pwr_5gl); - - pwr_offsets1[0] = (u16) (pi->ofdm5glpo & 0xffff); - pwr_offsets1[1] = - (u16) (pi->ofdm5glpo >> 16) & 0xffff; - - pwr_offsets2 = pi->mcs5glpo; - - tmp_cddpo = pi->cdd5glpo; - tmp_stbcpo = pi->stbc5glpo; - tmp_bw40po = pi->bw405glpo; - - tx_srom_max_rate = pi->tx_srom_max_rate_5g_low; - break; - case 3: - - tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_5gh, - pi->nphy_pwrctrl_info[1].max_pwr_5gh); - - pwr_offsets1[0] = (u16) (pi->ofdm5ghpo & 0xffff); - pwr_offsets1[1] = - (u16) (pi->ofdm5ghpo >> 16) & 0xffff; - - pwr_offsets2 = pi->mcs5ghpo; - - tmp_cddpo = pi->cdd5ghpo; - tmp_stbcpo = pi->stbc5ghpo; - tmp_bw40po = pi->bw405ghpo; - - tx_srom_max_rate = pi->tx_srom_max_rate_5g_hi; - break; - } - - wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, pwr_offsets1, - tmp_max_pwr, TXP_FIRST_OFDM, - TXP_LAST_OFDM); - - wlc_phy_ofdm_to_mcs_powers_nphy(tx_srom_max_rate, - TXP_FIRST_MCS_20_SISO, - TXP_LAST_MCS_20_SISO, - TXP_FIRST_OFDM); - - wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, pwr_offsets2, - tmp_max_pwr, - TXP_FIRST_MCS_20_CDD, - TXP_LAST_MCS_20_CDD); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) - wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, tmp_cddpo, - TXP_FIRST_MCS_20_CDD, - TXP_LAST_MCS_20_CDD); - - wlc_phy_mcs_to_ofdm_powers_nphy(tx_srom_max_rate, - TXP_FIRST_OFDM_20_CDD, - TXP_LAST_OFDM_20_CDD, - TXP_FIRST_MCS_20_CDD); - - wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, pwr_offsets2, - tmp_max_pwr, - TXP_FIRST_MCS_20_STBC, - TXP_LAST_MCS_20_STBC); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) - wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, - tmp_stbcpo, - TXP_FIRST_MCS_20_STBC, - TXP_LAST_MCS_20_STBC); - - wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, - &pwr_offsets2[2], tmp_max_pwr, - TXP_FIRST_MCS_20_SDM, - TXP_LAST_MCS_20_SDM); - - if (NPHY_IS_SROM_REINTERPRET) { - - wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, - &pwr_offsets2[4], - tmp_max_pwr, - TXP_FIRST_MCS_40_SISO, - TXP_LAST_MCS_40_SISO); - - wlc_phy_mcs_to_ofdm_powers_nphy(tx_srom_max_rate, - TXP_FIRST_OFDM_40_SISO, - TXP_LAST_OFDM_40_SISO, - TXP_FIRST_MCS_40_SISO); - - wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, - &pwr_offsets2[4], - tmp_max_pwr, - TXP_FIRST_MCS_40_CDD, - TXP_LAST_MCS_40_CDD); - - wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, tmp_cddpo, - TXP_FIRST_MCS_40_CDD, - TXP_LAST_MCS_40_CDD); - - wlc_phy_mcs_to_ofdm_powers_nphy(tx_srom_max_rate, - TXP_FIRST_OFDM_40_CDD, - TXP_LAST_OFDM_40_CDD, - TXP_FIRST_MCS_40_CDD); - - wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, - &pwr_offsets2[4], - tmp_max_pwr, - TXP_FIRST_MCS_40_STBC, - TXP_LAST_MCS_40_STBC); - - wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, - tmp_stbcpo, - TXP_FIRST_MCS_40_STBC, - TXP_LAST_MCS_40_STBC); - - wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, - &pwr_offsets2[6], - tmp_max_pwr, - TXP_FIRST_MCS_40_SDM, - TXP_LAST_MCS_40_SDM); - } else { - - for (rate1 = TXP_FIRST_OFDM_40_SISO, rate2 = - TXP_FIRST_OFDM; - rate1 <= TXP_LAST_MCS_40_SDM; - rate1++, rate2++) - tx_srom_max_rate[rate1] = - tx_srom_max_rate[rate2]; - } - - if (NREV_GE(pi->pubpi.phy_rev, 3)) - wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, - tmp_bw40po, - TXP_FIRST_OFDM_40_SISO, - TXP_LAST_MCS_40_SDM); - - tx_srom_max_rate[TXP_MCS_32] = - tx_srom_max_rate[TXP_FIRST_MCS_40_CDD]; - } - - return; -} - -void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi) -{ - u8 tx_pwr_ctrl_state; - wlc_phy_txpwr_limit_to_tbl_nphy(pi); - wlc_phy_txpwrctrl_pwr_setup_nphy(pi); - - tx_pwr_ctrl_state = pi->nphy_txpwrctrl; - - if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) { - wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK); - (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); - udelay(1); - } - - wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); - - if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) - wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0); -} - -static bool wlc_phy_txpwr_ison_nphy(struct brcms_phy *pi) -{ - return read_phy_reg((pi), 0x1e7) & ((0x1 << 15) | - (0x1 << 14) | (0x1 << 13)); -} - -u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi) -{ - u16 tmp; - u16 pwr_idx[2]; - - if (wlc_phy_txpwr_ison_nphy(pi)) { - pwr_idx[0] = wlc_phy_txpwr_idx_cur_get_nphy(pi, PHY_CORE_0); - pwr_idx[1] = wlc_phy_txpwr_idx_cur_get_nphy(pi, PHY_CORE_1); - - tmp = (pwr_idx[0] << 8) | pwr_idx[1]; - } else { - tmp = ((pi->nphy_txpwrindex[PHY_CORE_0].index_internal & 0xff) - << 8) | - (pi->nphy_txpwrindex[PHY_CORE_1].index_internal & 0xff); - } - - return tmp; -} - -void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi) -{ - if (PHY_IPA(pi) - && (pi->nphy_force_papd_cal - || (wlc_phy_txpwr_ison_nphy(pi) - && - (((u32) - abs(wlc_phy_txpwr_idx_cur_get_nphy(pi, 0) - - pi->nphy_papd_tx_gain_at_last_cal[0]) >= 4) - || ((u32) - abs(wlc_phy_txpwr_idx_cur_get_nphy(pi, 1) - - pi->nphy_papd_tx_gain_at_last_cal[1]) >= 4))))) - wlc_phy_a4(pi, true); -} - -void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type) -{ - u16 mask = 0, val = 0, ishw = 0; - u8 ctr; - uint core; - u32 tbl_offset; - u32 tbl_len; - u16 regval[84]; - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - switch (ctrl_type) { - case PHY_TPC_HW_OFF: - case PHY_TPC_HW_ON: - pi->nphy_txpwrctrl = ctrl_type; - break; - default: - break; - } - - if (ctrl_type == PHY_TPC_HW_OFF) { - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - - if (wlc_phy_txpwr_ison_nphy(pi)) { - for (core = 0; core < pi->pubpi.phy_corenum; - core++) - pi->nphy_txpwr_idx[core] = - wlc_phy_txpwr_idx_cur_get_nphy( - pi, - (u8) core); - } - - } - - tbl_len = 84; - tbl_offset = 64; - for (ctr = 0; ctr < tbl_len; ctr++) - regval[ctr] = 0; - wlc_phy_table_write_nphy(pi, 26, tbl_len, tbl_offset, 16, - regval); - wlc_phy_table_write_nphy(pi, 27, tbl_len, tbl_offset, 16, - regval); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) - and_phy_reg(pi, 0x1e7, - (u16) (~((0x1 << 15) | - (0x1 << 14) | (0x1 << 13)))); - else - and_phy_reg(pi, 0x1e7, - (u16) (~((0x1 << 14) | (0x1 << 13)))); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - or_phy_reg(pi, 0x8f, (0x1 << 8)); - or_phy_reg(pi, 0xa5, (0x1 << 8)); - } else { - or_phy_reg(pi, 0xa5, (0x1 << 14)); - } - - if (NREV_IS(pi->pubpi.phy_rev, 2)) - mod_phy_reg(pi, 0xdc, 0x00ff, 0x53); - else if (NREV_LT(pi->pubpi.phy_rev, 2)) - mod_phy_reg(pi, 0xdc, 0x00ff, 0x5a); - - if (NREV_LT(pi->pubpi.phy_rev, 2) && - pi->bw == WL_CHANSPEC_BW_40) - wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_IQSWAP_WAR, - MHF1_IQSWAP_WAR, BRCM_BAND_ALL); - - } else { - - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 84, 64, - 8, pi->adj_pwr_tbl_nphy); - wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 84, 64, - 8, pi->adj_pwr_tbl_nphy); - - ishw = (ctrl_type == PHY_TPC_HW_ON) ? 0x1 : 0x0; - mask = (0x1 << 14) | (0x1 << 13); - val = (ishw << 14) | (ishw << 13); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - mask |= (0x1 << 15); - val |= (ishw << 15); - } - - mod_phy_reg(pi, 0x1e7, mask, val); - - if (CHSPEC_IS5G(pi->radio_chanspec)) { - if (NREV_GE(pi->pubpi.phy_rev, 7)) { - mod_phy_reg(pi, 0x1e7, (0x7f << 0), 0x32); - mod_phy_reg(pi, 0x222, (0xff << 0), 0x32); - } else { - mod_phy_reg(pi, 0x1e7, (0x7f << 0), 0x64); - if (NREV_GT(pi->pubpi.phy_rev, 1)) - mod_phy_reg(pi, 0x222, - (0xff << 0), 0x64); - } - } - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - if ((pi->nphy_txpwr_idx[0] != 128) - && (pi->nphy_txpwr_idx[1] != 128)) - wlc_phy_txpwr_idx_cur_set_nphy(pi, - pi-> - nphy_txpwr_idx - [0], - pi-> - nphy_txpwr_idx - [1]); - } - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - and_phy_reg(pi, 0x8f, ~(0x1 << 8)); - and_phy_reg(pi, 0xa5, ~(0x1 << 8)); - } else { - and_phy_reg(pi, 0xa5, ~(0x1 << 14)); - } - - if (NREV_IS(pi->pubpi.phy_rev, 2)) - mod_phy_reg(pi, 0xdc, 0x00ff, 0x3b); - else if (NREV_LT(pi->pubpi.phy_rev, 2)) - mod_phy_reg(pi, 0xdc, 0x00ff, 0x40); - - if (NREV_LT(pi->pubpi.phy_rev, 2) && - pi->bw == WL_CHANSPEC_BW_40) - wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_IQSWAP_WAR, - 0x0, BRCM_BAND_ALL); - - if (PHY_IPA(pi)) { - mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 2), (0) << 2); - - mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 2), (0) << 2); - - } - - } - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -void -wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask, s8 txpwrindex, - bool restore_cals) -{ - u8 core, txpwrctl_tbl; - u16 tx_ind0, iq_ind0, lo_ind0; - u16 m1m2; - u32 txgain; - u16 rad_gain, dac_gain; - u8 bbmult; - u32 iqcomp; - u16 iqcomp_a, iqcomp_b; - u32 locomp; - u16 tmpval; - u8 tx_pwr_ctrl_state; - s32 rfpwr_offset; - u16 regval[2]; - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - - tx_ind0 = 192; - iq_ind0 = 320; - lo_ind0 = 448; - - for (core = 0; core < pi->pubpi.phy_corenum; core++) { - - if ((core_mask & (1 << core)) == 0) - continue; - - txpwrctl_tbl = (core == PHY_CORE_0) ? 26 : 27; - - if (txpwrindex < 0) { - if (pi->nphy_txpwrindex[core].index < 0) - continue; - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - mod_phy_reg(pi, 0x8f, - (0x1 << 8), - pi->nphy_txpwrindex[core]. - AfectrlOverride); - mod_phy_reg(pi, 0xa5, (0x1 << 8), - pi->nphy_txpwrindex[core]. - AfectrlOverride); - } else { - mod_phy_reg(pi, 0xa5, - (0x1 << 14), - pi->nphy_txpwrindex[core]. - AfectrlOverride); - } - - write_phy_reg(pi, (core == PHY_CORE_0) ? - 0xaa : 0xab, - pi->nphy_txpwrindex[core].AfeCtrlDacGain); - - wlc_phy_table_write_nphy(pi, 7, 1, (0x110 + core), 16, - &pi->nphy_txpwrindex[core]. - rad_gain); - - wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m1m2); - m1m2 &= ((core == PHY_CORE_0) ? 0x00ff : 0xff00); - m1m2 |= ((core == PHY_CORE_0) ? - (pi->nphy_txpwrindex[core].bbmult << 8) : - (pi->nphy_txpwrindex[core].bbmult << 0)); - wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m1m2); - - if (restore_cals) { - wlc_phy_table_write_nphy( - pi, 15, 2, (80 + 2 * core), 16, - &pi->nphy_txpwrindex[core].iqcomp_a); - wlc_phy_table_write_nphy( - pi, 15, 1, (85 + core), 16, - &pi->nphy_txpwrindex[core].locomp); - wlc_phy_table_write_nphy( - pi, 15, 1, (93 + core), 16, - &pi->nphy_txpwrindex[core].locomp); - } - - wlc_phy_txpwrctrl_enable_nphy(pi, pi->nphy_txpwrctrl); - - pi->nphy_txpwrindex[core].index_internal = - pi->nphy_txpwrindex[core].index_internal_save; - } else { - - if (pi->nphy_txpwrindex[core].index < 0) { - - if (NREV_GE(pi->pubpi.phy_rev, 3)) { - mod_phy_reg(pi, 0x8f, - (0x1 << 8), - pi->nphy_txpwrindex[core]. - AfectrlOverride); - mod_phy_reg(pi, 0xa5, (0x1 << 8), - pi->nphy_txpwrindex[core]. - AfectrlOverride); - } else { - pi->nphy_txpwrindex[core]. - AfectrlOverride = - read_phy_reg(pi, 0xa5); - } - - pi->nphy_txpwrindex[core].AfeCtrlDacGain = - read_phy_reg(pi, (core == PHY_CORE_0) ? - 0xaa : 0xab); - - wlc_phy_table_read_nphy(pi, 7, 1, - (0x110 + core), 16, - &pi-> - nphy_txpwrindex[core]. - rad_gain); - - wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, - &tmpval); - tmpval >>= ((core == PHY_CORE_0) ? 8 : 0); - tmpval &= 0xff; - pi->nphy_txpwrindex[core].bbmult = (u8) tmpval; - - wlc_phy_table_read_nphy(pi, 15, 2, - (80 + 2 * core), 16, - &pi-> - nphy_txpwrindex[core]. - iqcomp_a); - - wlc_phy_table_read_nphy(pi, 15, 1, (85 + core), - 16, - &pi-> - nphy_txpwrindex[core]. - locomp); - - pi->nphy_txpwrindex[core].index_internal_save = - pi->nphy_txpwrindex[core]. - index_internal; - } - - tx_pwr_ctrl_state = pi->nphy_txpwrctrl; - wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); - - if (NREV_IS(pi->pubpi.phy_rev, 1)) - wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON); - - wlc_phy_table_read_nphy(pi, txpwrctl_tbl, 1, - (tx_ind0 + txpwrindex), 32, - &txgain); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) - rad_gain = (txgain >> 16) & - ((1 << (32 - 16 + 1)) - 1); - else - rad_gain = (txgain >> 16) & - ((1 << (28 - 16 + 1)) - 1); - - dac_gain = (txgain >> 8) & ((1 << (13 - 8 + 1)) - 1); - bbmult = (txgain >> 0) & ((1 << (7 - 0 + 1)) - 1); - - if (NREV_GE(pi->pubpi.phy_rev, 3)) - mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : - 0xa5), (0x1 << 8), (0x1 << 8)); - else - mod_phy_reg(pi, 0xa5, (0x1 << 14), (0x1 << 14)); - - write_phy_reg(pi, (core == PHY_CORE_0) ? - 0xaa : 0xab, dac_gain); - - wlc_phy_table_write_nphy(pi, 7, 1, (0x110 + core), 16, - &rad_gain); - - wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m1m2); - m1m2 &= ((core == PHY_CORE_0) ? 0x00ff : 0xff00); - m1m2 |= ((core == PHY_CORE_0) ? - (bbmult << 8) : (bbmult << 0)); - - wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m1m2); - - wlc_phy_table_read_nphy(pi, txpwrctl_tbl, 1, - (iq_ind0 + txpwrindex), 32, - &iqcomp); - iqcomp_a = (iqcomp >> 10) & ((1 << (19 - 10 + 1)) - 1); - iqcomp_b = (iqcomp >> 0) & ((1 << (9 - 0 + 1)) - 1); - - if (restore_cals) { - regval[0] = (u16) iqcomp_a; - regval[1] = (u16) iqcomp_b; - wlc_phy_table_write_nphy(pi, 15, 2, - (80 + 2 * core), 16, - regval); - } - - wlc_phy_table_read_nphy(pi, txpwrctl_tbl, 1, - (lo_ind0 + txpwrindex), 32, - &locomp); - if (restore_cals) - wlc_phy_table_write_nphy(pi, 15, 1, (85 + core), - 16, &locomp); - - if (NREV_IS(pi->pubpi.phy_rev, 1)) - wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF); - - if (PHY_IPA(pi)) { - wlc_phy_table_read_nphy(pi, - (core == PHY_CORE_0 ? - NPHY_TBL_ID_CORE1TXPWRCTL : - NPHY_TBL_ID_CORE2TXPWRCTL), - 1, 576 + txpwrindex, 32, - &rfpwr_offset); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1ff << 4), - ((s16) rfpwr_offset) << 4); - - mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : - 0x29b, (0x1 << 2), (1) << 2); - - } - - wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); - } - - pi->nphy_txpwrindex[core].index = txpwrindex; - } - - if (pi->phyhang_avoid) - wlc_phy_stay_in_carriersearch_nphy(pi, false); -} - -void -wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan, u8 *max_pwr, - u8 txp_rate_idx) -{ - u8 chan_freq_range; - - chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, chan); - switch (chan_freq_range) { - case WL_CHAN_FREQ_RANGE_2G: - *max_pwr = pi->tx_srom_max_rate_2g[txp_rate_idx]; - break; - case WL_CHAN_FREQ_RANGE_5GM: - *max_pwr = pi->tx_srom_max_rate_5g_mid[txp_rate_idx]; - break; - case WL_CHAN_FREQ_RANGE_5GL: - *max_pwr = pi->tx_srom_max_rate_5g_low[txp_rate_idx]; - break; - case WL_CHAN_FREQ_RANGE_5GH: - *max_pwr = pi->tx_srom_max_rate_5g_hi[txp_rate_idx]; - break; - default: - *max_pwr = pi->tx_srom_max_rate_2g[txp_rate_idx]; - break; - } - - return; -} - -void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, bool enable) -{ - u16 clip_off[] = { 0xffff, 0xffff }; - - if (enable) { - if (pi->nphy_deaf_count == 0) { - pi->classifier_state = - wlc_phy_classifier_nphy(pi, 0, 0); - wlc_phy_classifier_nphy(pi, (0x7 << 0), 4); - wlc_phy_clip_det_nphy(pi, 0, pi->clip_state); - wlc_phy_clip_det_nphy(pi, 1, clip_off); - } - - pi->nphy_deaf_count++; - - wlc_phy_resetcca_nphy(pi); - - } else { - pi->nphy_deaf_count--; - - if (pi->nphy_deaf_count == 0) { - wlc_phy_classifier_nphy(pi, (0x7 << 0), - pi->classifier_state); - wlc_phy_clip_det_nphy(pi, 1, pi->clip_state); - } - } -} - -void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode) -{ - wlapi_suspend_mac_and_wait(pi->sh->physhim); - - if (mode) { - if (pi->nphy_deaf_count == 0) - wlc_phy_stay_in_carriersearch_nphy(pi, true); - } else if (pi->nphy_deaf_count > 0) { - wlc_phy_stay_in_carriersearch_nphy(pi, false); - } - - wlapi_enable_mac(pi->sh->physhim); -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c deleted file mode 100644 index faf1ebe76068..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (c) 2010 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 "phy_qmath.h" - -/* - * Description: This function make 16 bit unsigned multiplication. - * To fit the output into 16 bits the 32 bit multiplication result is right - * shifted by 16 bits. - */ -u16 qm_mulu16(u16 op1, u16 op2) -{ - return (u16) (((u32) op1 * (u32) op2) >> 16); -} - -/* - * Description: This function make 16 bit multiplication and return the result - * in 16 bits. To fit the multiplication result into 16 bits the multiplication - * result is right shifted by 15 bits. Right shifting 15 bits instead of 16 bits - * is done to remove the extra sign bit formed due to the multiplication. - * When both the 16bit inputs are 0x8000 then the output is saturated to - * 0x7fffffff. - */ -s16 qm_muls16(s16 op1, s16 op2) -{ - s32 result; - if (op1 == (s16) 0x8000 && op2 == (s16) 0x8000) - result = 0x7fffffff; - else - result = ((s32) (op1) * (s32) (op2)); - - return (s16) (result >> 15); -} - -/* - * Description: This function add two 32 bit numbers and return the 32bit - * result. If the result overflow 32 bits, the output will be saturated to - * 32bits. - */ -s32 qm_add32(s32 op1, s32 op2) -{ - s32 result; - result = op1 + op2; - if (op1 < 0 && op2 < 0 && result > 0) - result = 0x80000000; - else if (op1 > 0 && op2 > 0 && result < 0) - result = 0x7fffffff; - - return result; -} - -/* - * Description: This function add two 16 bit numbers and return the 16bit - * result. If the result overflow 16 bits, the output will be saturated to - * 16bits. - */ -s16 qm_add16(s16 op1, s16 op2) -{ - s16 result; - s32 temp = (s32) op1 + (s32) op2; - if (temp > (s32) 0x7fff) - result = (s16) 0x7fff; - else if (temp < (s32) 0xffff8000) - result = (s16) 0xffff8000; - else - result = (s16) temp; - - return result; -} - -/* - * Description: This function make 16 bit subtraction and return the 16bit - * result. If the result overflow 16 bits, the output will be saturated to - * 16bits. - */ -s16 qm_sub16(s16 op1, s16 op2) -{ - s16 result; - s32 temp = (s32) op1 - (s32) op2; - if (temp > (s32) 0x7fff) - result = (s16) 0x7fff; - else if (temp < (s32) 0xffff8000) - result = (s16) 0xffff8000; - else - result = (s16) temp; - - return result; -} - -/* - * Description: This function make a 32 bit saturated left shift when the - * specified shift is +ve. This function will make a 32 bit right shift when - * the specified shift is -ve. This function return the result after shifting - * operation. - */ -s32 qm_shl32(s32 op, int shift) -{ - int i; - s32 result; - result = op; - if (shift > 31) - shift = 31; - else if (shift < -31) - shift = -31; - if (shift >= 0) { - for (i = 0; i < shift; i++) - result = qm_add32(result, result); - } else { - result = result >> (-shift); - } - - return result; -} - -/* - * Description: This function make a 16 bit saturated left shift when the - * specified shift is +ve. This function will make a 16 bit right shift when - * the specified shift is -ve. This function return the result after shifting - * operation. - */ -s16 qm_shl16(s16 op, int shift) -{ - int i; - s16 result; - result = op; - if (shift > 15) - shift = 15; - else if (shift < -15) - shift = -15; - if (shift > 0) { - for (i = 0; i < shift; i++) - result = qm_add16(result, result); - } else { - result = result >> (-shift); - } - - return result; -} - -/* - * Description: This function make a 16 bit right shift when shift is +ve. - * This function make a 16 bit saturated left shift when shift is -ve. This - * function return the result of the shift operation. - */ -s16 qm_shr16(s16 op, int shift) -{ - return qm_shl16(op, -shift); -} - -/* - * Description: This function return the number of redundant sign bits in a - * 32 bit number. Example: qm_norm32(0x00000080) = 23 - */ -s16 qm_norm32(s32 op) -{ - u16 u16extraSignBits; - if (op == 0) { - return 31; - } else { - u16extraSignBits = 0; - while ((op >> 31) == (op >> 30)) { - u16extraSignBits++; - op = op << 1; - } - } - return u16extraSignBits; -} - -/* This table is log2(1+(i/32)) where i=[0:1:31], in q.15 format */ -static const s16 log_table[] = { - 0, - 1455, - 2866, - 4236, - 5568, - 6863, - 8124, - 9352, - 10549, - 11716, - 12855, - 13968, - 15055, - 16117, - 17156, - 18173, - 19168, - 20143, - 21098, - 22034, - 22952, - 23852, - 24736, - 25604, - 26455, - 27292, - 28114, - 28922, - 29717, - 30498, - 31267, - 32024 -}; - -#define LOG_TABLE_SIZE 32 /* log_table size */ -#define LOG2_LOG_TABLE_SIZE 5 /* log2(log_table size) */ -#define Q_LOG_TABLE 15 /* qformat of log_table */ -#define LOG10_2 19728 /* log10(2) in q.16 */ - -/* - * Description: - * This routine takes the input number N and its q format qN and compute - * the log10(N). This routine first normalizes the input no N. Then N is in - * mag*(2^x) format. mag is any number in the range 2^30-(2^31 - 1). - * Then log2(mag * 2^x) = log2(mag) + x is computed. From that - * log10(mag * 2^x) = log2(mag * 2^x) * log10(2) is computed. - * This routine looks the log2 value in the table considering - * LOG2_LOG_TABLE_SIZE+1 MSBs. As the MSB is always 1, only next - * LOG2_OF_LOG_TABLE_SIZE MSBs are used for table lookup. Next 16 MSBs are used - * for interpolation. - * Inputs: - * N - number to which log10 has to be found. - * qN - q format of N - * log10N - address where log10(N) will be written. - * qLog10N - address where log10N qformat will be written. - * Note/Problem: - * For accurate results input should be in normalized or near normalized form. - */ -void qm_log10(s32 N, s16 qN, s16 *log10N, s16 *qLog10N) -{ - s16 s16norm, s16tableIndex, s16errorApproximation; - u16 u16offset; - s32 s32log; - - /* normalize the N. */ - s16norm = qm_norm32(N); - N = N << s16norm; - - /* The qformat of N after normalization. - * -30 is added to treat the no as between 1.0 to 2.0 - * i.e. after adding the -30 to the qformat the decimal point will be - * just rigtht of the MSB. (i.e. after sign bit and 1st MSB). i.e. - * at the right side of 30th bit. - */ - qN = qN + s16norm - 30; - - /* take the table index as the LOG2_OF_LOG_TABLE_SIZE bits right of the - * MSB */ - s16tableIndex = (s16) (N >> (32 - (2 + LOG2_LOG_TABLE_SIZE))); - - /* remove the MSB. the MSB is always 1 after normalization. */ - s16tableIndex = - s16tableIndex & (s16) ((1 << LOG2_LOG_TABLE_SIZE) - 1); - - /* remove the (1+LOG2_OF_LOG_TABLE_SIZE) MSBs in the N. */ - N = N & ((1 << (32 - (2 + LOG2_LOG_TABLE_SIZE))) - 1); - - /* take the offset as the 16 MSBS after table index. - */ - u16offset = (u16) (N >> (32 - (2 + LOG2_LOG_TABLE_SIZE + 16))); - - /* look the log value in the table. */ - s32log = log_table[s16tableIndex]; /* q.15 format */ - - /* interpolate using the offset. q.15 format. */ - s16errorApproximation = (s16) qm_mulu16(u16offset, - (u16) (log_table[s16tableIndex + 1] - - log_table[s16tableIndex])); - - /* q.15 format */ - s32log = qm_add16((s16) s32log, s16errorApproximation); - - /* adjust for the qformat of the N as - * log2(mag * 2^x) = log2(mag) + x - */ - s32log = qm_add32(s32log, ((s32) -qN) << 15); /* q.15 format */ - - /* normalize the result. */ - s16norm = qm_norm32(s32log); - - /* bring all the important bits into lower 16 bits */ - /* q.15+s16norm-16 format */ - s32log = qm_shl32(s32log, s16norm - 16); - - /* compute the log10(N) by multiplying log2(N) with log10(2). - * as log10(mag * 2^x) = log2(mag * 2^x) * log10(2) - * log10N in q.15+s16norm-16+1 (LOG10_2 is in q.16) - */ - *log10N = qm_muls16((s16) s32log, (s16) LOG10_2); - - /* write the q format of the result. */ - *qLog10N = 15 + s16norm - 16 + 1; - - return; -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h deleted file mode 100644 index 20e3783f921b..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_QMATH_H_ -#define _BRCM_QMATH_H_ - -#include - -u16 qm_mulu16(u16 op1, u16 op2); - -s16 qm_muls16(s16 op1, s16 op2); - -s32 qm_add32(s32 op1, s32 op2); - -s16 qm_add16(s16 op1, s16 op2); - -s16 qm_sub16(s16 op1, s16 op2); - -s32 qm_shl32(s32 op, int shift); - -s16 qm_shl16(s16 op, int shift); - -s16 qm_shr16(s16 op, int shift); - -s16 qm_norm32(s32 op); - -void qm_log10(s32 N, s16 qN, s16 *log10N, s16 *qLog10N); - -#endif /* #ifndef _BRCM_QMATH_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h deleted file mode 100644 index c3a675455ff5..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h +++ /dev/null @@ -1,1533 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_PHY_RADIO_H_ -#define _BRCM_PHY_RADIO_H_ - -#define RADIO_IDCODE 0x01 - -#define RADIO_DEFAULT_CORE 0 - -#define RXC0_RSSI_RST 0x80 -#define RXC0_MODE_RSSI 0x40 -#define RXC0_MODE_OFF 0x20 -#define RXC0_MODE_CM 0x10 -#define RXC0_LAN_LOAD 0x08 -#define RXC0_OFF_ADJ_MASK 0x07 - -#define TXC0_MODE_TXLPF 0x04 -#define TXC0_PA_TSSI_EN 0x02 -#define TXC0_TSSI_EN 0x01 - -#define TXC1_PA_GAIN_MASK 0x60 -#define TXC1_PA_GAIN_3DB 0x40 -#define TXC1_PA_GAIN_2DB 0x20 -#define TXC1_TX_MIX_GAIN 0x10 -#define TXC1_OFF_I_MASK 0x0c -#define TXC1_OFF_Q_MASK 0x03 - -#define RADIO_2055_READ_OFF 0x100 -#define RADIO_2057_READ_OFF 0x200 - -#define RADIO_2055_GEN_SPARE 0x00 -#define RADIO_2055_SP_PIN_PD 0x02 -#define RADIO_2055_SP_RSSI_CORE1 0x03 -#define RADIO_2055_SP_PD_MISC_CORE1 0x04 -#define RADIO_2055_SP_RSSI_CORE2 0x05 -#define RADIO_2055_SP_PD_MISC_CORE2 0x06 -#define RADIO_2055_SP_RX_GC1_CORE1 0x07 -#define RADIO_2055_SP_RX_GC2_CORE1 0x08 -#define RADIO_2055_SP_RX_GC1_CORE2 0x09 -#define RADIO_2055_SP_RX_GC2_CORE2 0x0a -#define RADIO_2055_SP_LPF_BW_SELECT_CORE1 0x0b -#define RADIO_2055_SP_LPF_BW_SELECT_CORE2 0x0c -#define RADIO_2055_SP_TX_GC1_CORE1 0x0d -#define RADIO_2055_SP_TX_GC2_CORE1 0x0e -#define RADIO_2055_SP_TX_GC1_CORE2 0x0f -#define RADIO_2055_SP_TX_GC2_CORE2 0x10 -#define RADIO_2055_MASTER_CNTRL1 0x11 -#define RADIO_2055_MASTER_CNTRL2 0x12 -#define RADIO_2055_PD_LGEN 0x13 -#define RADIO_2055_PD_PLL_TS 0x14 -#define RADIO_2055_PD_CORE1_LGBUF 0x15 -#define RADIO_2055_PD_CORE1_TX 0x16 -#define RADIO_2055_PD_CORE1_RXTX 0x17 -#define RADIO_2055_PD_CORE1_RSSI_MISC 0x18 -#define RADIO_2055_PD_CORE2_LGBUF 0x19 -#define RADIO_2055_PD_CORE2_TX 0x1a -#define RADIO_2055_PD_CORE2_RXTX 0x1b -#define RADIO_2055_PD_CORE2_RSSI_MISC 0x1c -#define RADIO_2055_PWRDET_LGEN 0x1d -#define RADIO_2055_PWRDET_LGBUF_CORE1 0x1e -#define RADIO_2055_PWRDET_RXTX_CORE1 0x1f -#define RADIO_2055_PWRDET_LGBUF_CORE2 0x20 -#define RADIO_2055_PWRDET_RXTX_CORE2 0x21 -#define RADIO_2055_RRCCAL_CNTRL_SPARE 0x22 -#define RADIO_2055_RRCCAL_N_OPT_SEL 0x23 -#define RADIO_2055_CAL_MISC 0x24 -#define RADIO_2055_CAL_COUNTER_OUT 0x25 -#define RADIO_2055_CAL_COUNTER_OUT2 0x26 -#define RADIO_2055_CAL_CVAR_CNTRL 0x27 -#define RADIO_2055_CAL_RVAR_CNTRL 0x28 -#define RADIO_2055_CAL_LPO_CNTRL 0x29 -#define RADIO_2055_CAL_TS 0x2a -#define RADIO_2055_CAL_RCCAL_READ_TS 0x2b -#define RADIO_2055_CAL_RCAL_READ_TS 0x2c -#define RADIO_2055_PAD_DRIVER 0x2d -#define RADIO_2055_XO_CNTRL1 0x2e -#define RADIO_2055_XO_CNTRL2 0x2f -#define RADIO_2055_XO_REGULATOR 0x30 -#define RADIO_2055_XO_MISC 0x31 -#define RADIO_2055_PLL_LF_C1 0x32 -#define RADIO_2055_PLL_CAL_VTH 0x33 -#define RADIO_2055_PLL_LF_C2 0x34 -#define RADIO_2055_PLL_REF 0x35 -#define RADIO_2055_PLL_LF_R1 0x36 -#define RADIO_2055_PLL_PFD_CP 0x37 -#define RADIO_2055_PLL_IDAC_CPOPAMP 0x38 -#define RADIO_2055_PLL_CP_REGULATOR 0x39 -#define RADIO_2055_PLL_RCAL 0x3a -#define RADIO_2055_RF_PLL_MOD0 0x3b -#define RADIO_2055_RF_PLL_MOD1 0x3c -#define RADIO_2055_RF_MMD_IDAC1 0x3d -#define RADIO_2055_RF_MMD_IDAC0 0x3e -#define RADIO_2055_RF_MMD_SPARE 0x3f -#define RADIO_2055_VCO_CAL1 0x40 -#define RADIO_2055_VCO_CAL2 0x41 -#define RADIO_2055_VCO_CAL3 0x42 -#define RADIO_2055_VCO_CAL4 0x43 -#define RADIO_2055_VCO_CAL5 0x44 -#define RADIO_2055_VCO_CAL6 0x45 -#define RADIO_2055_VCO_CAL7 0x46 -#define RADIO_2055_VCO_CAL8 0x47 -#define RADIO_2055_VCO_CAL9 0x48 -#define RADIO_2055_VCO_CAL10 0x49 -#define RADIO_2055_VCO_CAL11 0x4a -#define RADIO_2055_VCO_CAL12 0x4b -#define RADIO_2055_VCO_CAL13 0x4c -#define RADIO_2055_VCO_CAL14 0x4d -#define RADIO_2055_VCO_CAL15 0x4e -#define RADIO_2055_VCO_CAL16 0x4f -#define RADIO_2055_VCO_KVCO 0x50 -#define RADIO_2055_VCO_CAP_TAIL 0x51 -#define RADIO_2055_VCO_IDAC_VCO 0x52 -#define RADIO_2055_VCO_REGULATOR 0x53 -#define RADIO_2055_PLL_RF_VTH 0x54 -#define RADIO_2055_LGBUF_CEN_BUF 0x55 -#define RADIO_2055_LGEN_TUNE1 0x56 -#define RADIO_2055_LGEN_TUNE2 0x57 -#define RADIO_2055_LGEN_IDAC1 0x58 -#define RADIO_2055_LGEN_IDAC2 0x59 -#define RADIO_2055_LGEN_BIAS_CNT 0x5a -#define RADIO_2055_LGEN_BIAS_IDAC 0x5b -#define RADIO_2055_LGEN_RCAL 0x5c -#define RADIO_2055_LGEN_DIV 0x5d -#define RADIO_2055_LGEN_SPARE2 0x5e -#define RADIO_2055_CORE1_LGBUF_A_TUNE 0x5f -#define RADIO_2055_CORE1_LGBUF_G_TUNE 0x60 -#define RADIO_2055_CORE1_LGBUF_DIV 0x61 -#define RADIO_2055_CORE1_LGBUF_A_IDAC 0x62 -#define RADIO_2055_CORE1_LGBUF_G_IDAC 0x63 -#define RADIO_2055_CORE1_LGBUF_IDACFIL_OVR 0x64 -#define RADIO_2055_CORE1_LGBUF_SPARE 0x65 -#define RADIO_2055_CORE1_RXRF_SPC1 0x66 -#define RADIO_2055_CORE1_RXRF_REG1 0x67 -#define RADIO_2055_CORE1_RXRF_REG2 0x68 -#define RADIO_2055_CORE1_RXRF_RCAL 0x69 -#define RADIO_2055_CORE1_RXBB_BUFI_LPFCMP 0x6a -#define RADIO_2055_CORE1_RXBB_LPF 0x6b -#define RADIO_2055_CORE1_RXBB_MIDAC_HIPAS 0x6c -#define RADIO_2055_CORE1_RXBB_VGA1_IDAC 0x6d -#define RADIO_2055_CORE1_RXBB_VGA2_IDAC 0x6e -#define RADIO_2055_CORE1_RXBB_VGA3_IDAC 0x6f -#define RADIO_2055_CORE1_RXBB_BUFO_CTRL 0x70 -#define RADIO_2055_CORE1_RXBB_RCCAL_CTRL 0x71 -#define RADIO_2055_CORE1_RXBB_RSSI_CTRL1 0x72 -#define RADIO_2055_CORE1_RXBB_RSSI_CTRL2 0x73 -#define RADIO_2055_CORE1_RXBB_RSSI_CTRL3 0x74 -#define RADIO_2055_CORE1_RXBB_RSSI_CTRL4 0x75 -#define RADIO_2055_CORE1_RXBB_RSSI_CTRL5 0x76 -#define RADIO_2055_CORE1_RXBB_REGULATOR 0x77 -#define RADIO_2055_CORE1_RXBB_SPARE1 0x78 -#define RADIO_2055_CORE1_RXTXBB_RCAL 0x79 -#define RADIO_2055_CORE1_TXRF_SGM_PGA 0x7a -#define RADIO_2055_CORE1_TXRF_SGM_PAD 0x7b -#define RADIO_2055_CORE1_TXRF_CNTR_PGA1 0x7c -#define RADIO_2055_CORE1_TXRF_CNTR_PAD1 0x7d -#define RADIO_2055_CORE1_TX_RFPGA_IDAC 0x7e -#define RADIO_2055_CORE1_TX_PGA_PAD_TN 0x7f -#define RADIO_2055_CORE1_TX_PAD_IDAC1 0x80 -#define RADIO_2055_CORE1_TX_PAD_IDAC2 0x81 -#define RADIO_2055_CORE1_TX_MX_BGTRIM 0x82 -#define RADIO_2055_CORE1_TXRF_RCAL 0x83 -#define RADIO_2055_CORE1_TXRF_PAD_TSSI1 0x84 -#define RADIO_2055_CORE1_TXRF_PAD_TSSI2 0x85 -#define RADIO_2055_CORE1_TX_RF_SPARE 0x86 -#define RADIO_2055_CORE1_TXRF_IQCAL1 0x87 -#define RADIO_2055_CORE1_TXRF_IQCAL2 0x88 -#define RADIO_2055_CORE1_TXBB_RCCAL_CTRL 0x89 -#define RADIO_2055_CORE1_TXBB_LPF1 0x8a -#define RADIO_2055_CORE1_TX_VOS_CNCL 0x8b -#define RADIO_2055_CORE1_TX_LPF_MXGM_IDAC 0x8c -#define RADIO_2055_CORE1_TX_BB_MXGM 0x8d -#define RADIO_2055_CORE2_LGBUF_A_TUNE 0x8e -#define RADIO_2055_CORE2_LGBUF_G_TUNE 0x8f -#define RADIO_2055_CORE2_LGBUF_DIV 0x90 -#define RADIO_2055_CORE2_LGBUF_A_IDAC 0x91 -#define RADIO_2055_CORE2_LGBUF_G_IDAC 0x92 -#define RADIO_2055_CORE2_LGBUF_IDACFIL_OVR 0x93 -#define RADIO_2055_CORE2_LGBUF_SPARE 0x94 -#define RADIO_2055_CORE2_RXRF_SPC1 0x95 -#define RADIO_2055_CORE2_RXRF_REG1 0x96 -#define RADIO_2055_CORE2_RXRF_REG2 0x97 -#define RADIO_2055_CORE2_RXRF_RCAL 0x98 -#define RADIO_2055_CORE2_RXBB_BUFI_LPFCMP 0x99 -#define RADIO_2055_CORE2_RXBB_LPF 0x9a -#define RADIO_2055_CORE2_RXBB_MIDAC_HIPAS 0x9b -#define RADIO_2055_CORE2_RXBB_VGA1_IDAC 0x9c -#define RADIO_2055_CORE2_RXBB_VGA2_IDAC 0x9d -#define RADIO_2055_CORE2_RXBB_VGA3_IDAC 0x9e -#define RADIO_2055_CORE2_RXBB_BUFO_CTRL 0x9f -#define RADIO_2055_CORE2_RXBB_RCCAL_CTRL 0xa0 -#define RADIO_2055_CORE2_RXBB_RSSI_CTRL1 0xa1 -#define RADIO_2055_CORE2_RXBB_RSSI_CTRL2 0xa2 -#define RADIO_2055_CORE2_RXBB_RSSI_CTRL3 0xa3 -#define RADIO_2055_CORE2_RXBB_RSSI_CTRL4 0xa4 -#define RADIO_2055_CORE2_RXBB_RSSI_CTRL5 0xa5 -#define RADIO_2055_CORE2_RXBB_REGULATOR 0xa6 -#define RADIO_2055_CORE2_RXBB_SPARE1 0xa7 -#define RADIO_2055_CORE2_RXTXBB_RCAL 0xa8 -#define RADIO_2055_CORE2_TXRF_SGM_PGA 0xa9 -#define RADIO_2055_CORE2_TXRF_SGM_PAD 0xaa -#define RADIO_2055_CORE2_TXRF_CNTR_PGA1 0xab -#define RADIO_2055_CORE2_TXRF_CNTR_PAD1 0xac -#define RADIO_2055_CORE2_TX_RFPGA_IDAC 0xad -#define RADIO_2055_CORE2_TX_PGA_PAD_TN 0xae -#define RADIO_2055_CORE2_TX_PAD_IDAC1 0xaf -#define RADIO_2055_CORE2_TX_PAD_IDAC2 0xb0 -#define RADIO_2055_CORE2_TX_MX_BGTRIM 0xb1 -#define RADIO_2055_CORE2_TXRF_RCAL 0xb2 -#define RADIO_2055_CORE2_TXRF_PAD_TSSI1 0xb3 -#define RADIO_2055_CORE2_TXRF_PAD_TSSI2 0xb4 -#define RADIO_2055_CORE2_TX_RF_SPARE 0xb5 -#define RADIO_2055_CORE2_TXRF_IQCAL1 0xb6 -#define RADIO_2055_CORE2_TXRF_IQCAL2 0xb7 -#define RADIO_2055_CORE2_TXBB_RCCAL_CTRL 0xb8 -#define RADIO_2055_CORE2_TXBB_LPF1 0xb9 -#define RADIO_2055_CORE2_TX_VOS_CNCL 0xba -#define RADIO_2055_CORE2_TX_LPF_MXGM_IDAC 0xbb -#define RADIO_2055_CORE2_TX_BB_MXGM 0xbc -#define RADIO_2055_PRG_GC_HPVGA23_21 0xbd -#define RADIO_2055_PRG_GC_HPVGA23_22 0xbe -#define RADIO_2055_PRG_GC_HPVGA23_23 0xbf -#define RADIO_2055_PRG_GC_HPVGA23_24 0xc0 -#define RADIO_2055_PRG_GC_HPVGA23_25 0xc1 -#define RADIO_2055_PRG_GC_HPVGA23_26 0xc2 -#define RADIO_2055_PRG_GC_HPVGA23_27 0xc3 -#define RADIO_2055_PRG_GC_HPVGA23_28 0xc4 -#define RADIO_2055_PRG_GC_HPVGA23_29 0xc5 -#define RADIO_2055_PRG_GC_HPVGA23_30 0xc6 -#define RADIO_2055_CORE1_LNA_GAINBST 0xcd -#define RADIO_2055_CORE1_B0_NBRSSI_VCM 0xd2 -#define RADIO_2055_CORE1_GEN_SPARE2 0xd6 -#define RADIO_2055_CORE2_LNA_GAINBST 0xd9 -#define RADIO_2055_CORE2_B0_NBRSSI_VCM 0xde -#define RADIO_2055_CORE2_GEN_SPARE2 0xe2 - -#define RADIO_2055_GAINBST_GAIN_DB 6 -#define RADIO_2055_GAINBST_CODE 0x6 - -#define RADIO_2055_JTAGCTRL_MASK 0x04 -#define RADIO_2055_JTAGSYNC_MASK 0x08 -#define RADIO_2055_RRCAL_START 0x40 -#define RADIO_2055_RRCAL_RST_N 0x01 -#define RADIO_2055_CAL_LPO_ENABLE 0x80 -#define RADIO_2055_RCAL_DONE 0x80 -#define RADIO_2055_NBRSSI_VCM_I_MASK 0x03 -#define RADIO_2055_NBRSSI_VCM_I_SHIFT 0x00 -#define RADIO_2055_NBRSSI_VCM_Q_MASK 0x03 -#define RADIO_2055_NBRSSI_VCM_Q_SHIFT 0x00 -#define RADIO_2055_WBRSSI_VCM_IQ_MASK 0x0c -#define RADIO_2055_WBRSSI_VCM_IQ_SHIFT 0x02 -#define RADIO_2055_NBRSSI_PD 0x01 -#define RADIO_2055_WBRSSI_G1_PD 0x04 -#define RADIO_2055_WBRSSI_G2_PD 0x02 -#define RADIO_2055_NBRSSI_SEL 0x01 -#define RADIO_2055_WBRSSI_G1_SEL 0x04 -#define RADIO_2055_WBRSSI_G2_SEL 0x02 -#define RADIO_2055_COUPLE_RX_MASK 0x01 -#define RADIO_2055_COUPLE_TX_MASK 0x02 -#define RADIO_2055_GAINBST_DISABLE 0x02 -#define RADIO_2055_GAINBST_VAL_MASK 0x07 -#define RADIO_2055_RXMX_GC_MASK 0x0c - -#define RADIO_MIMO_CORESEL_OFF 0x0 -#define RADIO_MIMO_CORESEL_CORE1 0x1 -#define RADIO_MIMO_CORESEL_CORE2 0x2 -#define RADIO_MIMO_CORESEL_CORE3 0x3 -#define RADIO_MIMO_CORESEL_CORE4 0x4 -#define RADIO_MIMO_CORESEL_ALLRX 0x5 -#define RADIO_MIMO_CORESEL_ALLTX 0x6 -#define RADIO_MIMO_CORESEL_ALLRXTX 0x7 - -#define RADIO_2064_READ_OFF 0x200 - -#define RADIO_2064_REG000 0x0 -#define RADIO_2064_REG001 0x1 -#define RADIO_2064_REG002 0x2 -#define RADIO_2064_REG003 0x3 -#define RADIO_2064_REG004 0x4 -#define RADIO_2064_REG005 0x5 -#define RADIO_2064_REG006 0x6 -#define RADIO_2064_REG007 0x7 -#define RADIO_2064_REG008 0x8 -#define RADIO_2064_REG009 0x9 -#define RADIO_2064_REG00A 0xa -#define RADIO_2064_REG00B 0xb -#define RADIO_2064_REG00C 0xc -#define RADIO_2064_REG00D 0xd -#define RADIO_2064_REG00E 0xe -#define RADIO_2064_REG00F 0xf -#define RADIO_2064_REG010 0x10 -#define RADIO_2064_REG011 0x11 -#define RADIO_2064_REG012 0x12 -#define RADIO_2064_REG013 0x13 -#define RADIO_2064_REG014 0x14 -#define RADIO_2064_REG015 0x15 -#define RADIO_2064_REG016 0x16 -#define RADIO_2064_REG017 0x17 -#define RADIO_2064_REG018 0x18 -#define RADIO_2064_REG019 0x19 -#define RADIO_2064_REG01A 0x1a -#define RADIO_2064_REG01B 0x1b -#define RADIO_2064_REG01C 0x1c -#define RADIO_2064_REG01D 0x1d -#define RADIO_2064_REG01E 0x1e -#define RADIO_2064_REG01F 0x1f -#define RADIO_2064_REG020 0x20 -#define RADIO_2064_REG021 0x21 -#define RADIO_2064_REG022 0x22 -#define RADIO_2064_REG023 0x23 -#define RADIO_2064_REG024 0x24 -#define RADIO_2064_REG025 0x25 -#define RADIO_2064_REG026 0x26 -#define RADIO_2064_REG027 0x27 -#define RADIO_2064_REG028 0x28 -#define RADIO_2064_REG029 0x29 -#define RADIO_2064_REG02A 0x2a -#define RADIO_2064_REG02B 0x2b -#define RADIO_2064_REG02C 0x2c -#define RADIO_2064_REG02D 0x2d -#define RADIO_2064_REG02E 0x2e -#define RADIO_2064_REG02F 0x2f -#define RADIO_2064_REG030 0x30 -#define RADIO_2064_REG031 0x31 -#define RADIO_2064_REG032 0x32 -#define RADIO_2064_REG033 0x33 -#define RADIO_2064_REG034 0x34 -#define RADIO_2064_REG035 0x35 -#define RADIO_2064_REG036 0x36 -#define RADIO_2064_REG037 0x37 -#define RADIO_2064_REG038 0x38 -#define RADIO_2064_REG039 0x39 -#define RADIO_2064_REG03A 0x3a -#define RADIO_2064_REG03B 0x3b -#define RADIO_2064_REG03C 0x3c -#define RADIO_2064_REG03D 0x3d -#define RADIO_2064_REG03E 0x3e -#define RADIO_2064_REG03F 0x3f -#define RADIO_2064_REG040 0x40 -#define RADIO_2064_REG041 0x41 -#define RADIO_2064_REG042 0x42 -#define RADIO_2064_REG043 0x43 -#define RADIO_2064_REG044 0x44 -#define RADIO_2064_REG045 0x45 -#define RADIO_2064_REG046 0x46 -#define RADIO_2064_REG047 0x47 -#define RADIO_2064_REG048 0x48 -#define RADIO_2064_REG049 0x49 -#define RADIO_2064_REG04A 0x4a -#define RADIO_2064_REG04B 0x4b -#define RADIO_2064_REG04C 0x4c -#define RADIO_2064_REG04D 0x4d -#define RADIO_2064_REG04E 0x4e -#define RADIO_2064_REG04F 0x4f -#define RADIO_2064_REG050 0x50 -#define RADIO_2064_REG051 0x51 -#define RADIO_2064_REG052 0x52 -#define RADIO_2064_REG053 0x53 -#define RADIO_2064_REG054 0x54 -#define RADIO_2064_REG055 0x55 -#define RADIO_2064_REG056 0x56 -#define RADIO_2064_REG057 0x57 -#define RADIO_2064_REG058 0x58 -#define RADIO_2064_REG059 0x59 -#define RADIO_2064_REG05A 0x5a -#define RADIO_2064_REG05B 0x5b -#define RADIO_2064_REG05C 0x5c -#define RADIO_2064_REG05D 0x5d -#define RADIO_2064_REG05E 0x5e -#define RADIO_2064_REG05F 0x5f -#define RADIO_2064_REG060 0x60 -#define RADIO_2064_REG061 0x61 -#define RADIO_2064_REG062 0x62 -#define RADIO_2064_REG063 0x63 -#define RADIO_2064_REG064 0x64 -#define RADIO_2064_REG065 0x65 -#define RADIO_2064_REG066 0x66 -#define RADIO_2064_REG067 0x67 -#define RADIO_2064_REG068 0x68 -#define RADIO_2064_REG069 0x69 -#define RADIO_2064_REG06A 0x6a -#define RADIO_2064_REG06B 0x6b -#define RADIO_2064_REG06C 0x6c -#define RADIO_2064_REG06D 0x6d -#define RADIO_2064_REG06E 0x6e -#define RADIO_2064_REG06F 0x6f -#define RADIO_2064_REG070 0x70 -#define RADIO_2064_REG071 0x71 -#define RADIO_2064_REG072 0x72 -#define RADIO_2064_REG073 0x73 -#define RADIO_2064_REG074 0x74 -#define RADIO_2064_REG075 0x75 -#define RADIO_2064_REG076 0x76 -#define RADIO_2064_REG077 0x77 -#define RADIO_2064_REG078 0x78 -#define RADIO_2064_REG079 0x79 -#define RADIO_2064_REG07A 0x7a -#define RADIO_2064_REG07B 0x7b -#define RADIO_2064_REG07C 0x7c -#define RADIO_2064_REG07D 0x7d -#define RADIO_2064_REG07E 0x7e -#define RADIO_2064_REG07F 0x7f -#define RADIO_2064_REG080 0x80 -#define RADIO_2064_REG081 0x81 -#define RADIO_2064_REG082 0x82 -#define RADIO_2064_REG083 0x83 -#define RADIO_2064_REG084 0x84 -#define RADIO_2064_REG085 0x85 -#define RADIO_2064_REG086 0x86 -#define RADIO_2064_REG087 0x87 -#define RADIO_2064_REG088 0x88 -#define RADIO_2064_REG089 0x89 -#define RADIO_2064_REG08A 0x8a -#define RADIO_2064_REG08B 0x8b -#define RADIO_2064_REG08C 0x8c -#define RADIO_2064_REG08D 0x8d -#define RADIO_2064_REG08E 0x8e -#define RADIO_2064_REG08F 0x8f -#define RADIO_2064_REG090 0x90 -#define RADIO_2064_REG091 0x91 -#define RADIO_2064_REG092 0x92 -#define RADIO_2064_REG093 0x93 -#define RADIO_2064_REG094 0x94 -#define RADIO_2064_REG095 0x95 -#define RADIO_2064_REG096 0x96 -#define RADIO_2064_REG097 0x97 -#define RADIO_2064_REG098 0x98 -#define RADIO_2064_REG099 0x99 -#define RADIO_2064_REG09A 0x9a -#define RADIO_2064_REG09B 0x9b -#define RADIO_2064_REG09C 0x9c -#define RADIO_2064_REG09D 0x9d -#define RADIO_2064_REG09E 0x9e -#define RADIO_2064_REG09F 0x9f -#define RADIO_2064_REG0A0 0xa0 -#define RADIO_2064_REG0A1 0xa1 -#define RADIO_2064_REG0A2 0xa2 -#define RADIO_2064_REG0A3 0xa3 -#define RADIO_2064_REG0A4 0xa4 -#define RADIO_2064_REG0A5 0xa5 -#define RADIO_2064_REG0A6 0xa6 -#define RADIO_2064_REG0A7 0xa7 -#define RADIO_2064_REG0A8 0xa8 -#define RADIO_2064_REG0A9 0xa9 -#define RADIO_2064_REG0AA 0xaa -#define RADIO_2064_REG0AB 0xab -#define RADIO_2064_REG0AC 0xac -#define RADIO_2064_REG0AD 0xad -#define RADIO_2064_REG0AE 0xae -#define RADIO_2064_REG0AF 0xaf -#define RADIO_2064_REG0B0 0xb0 -#define RADIO_2064_REG0B1 0xb1 -#define RADIO_2064_REG0B2 0xb2 -#define RADIO_2064_REG0B3 0xb3 -#define RADIO_2064_REG0B4 0xb4 -#define RADIO_2064_REG0B5 0xb5 -#define RADIO_2064_REG0B6 0xb6 -#define RADIO_2064_REG0B7 0xb7 -#define RADIO_2064_REG0B8 0xb8 -#define RADIO_2064_REG0B9 0xb9 -#define RADIO_2064_REG0BA 0xba -#define RADIO_2064_REG0BB 0xbb -#define RADIO_2064_REG0BC 0xbc -#define RADIO_2064_REG0BD 0xbd -#define RADIO_2064_REG0BE 0xbe -#define RADIO_2064_REG0BF 0xbf -#define RADIO_2064_REG0C0 0xc0 -#define RADIO_2064_REG0C1 0xc1 -#define RADIO_2064_REG0C2 0xc2 -#define RADIO_2064_REG0C3 0xc3 -#define RADIO_2064_REG0C4 0xc4 -#define RADIO_2064_REG0C5 0xc5 -#define RADIO_2064_REG0C6 0xc6 -#define RADIO_2064_REG0C7 0xc7 -#define RADIO_2064_REG0C8 0xc8 -#define RADIO_2064_REG0C9 0xc9 -#define RADIO_2064_REG0CA 0xca -#define RADIO_2064_REG0CB 0xcb -#define RADIO_2064_REG0CC 0xcc -#define RADIO_2064_REG0CD 0xcd -#define RADIO_2064_REG0CE 0xce -#define RADIO_2064_REG0CF 0xcf -#define RADIO_2064_REG0D0 0xd0 -#define RADIO_2064_REG0D1 0xd1 -#define RADIO_2064_REG0D2 0xd2 -#define RADIO_2064_REG0D3 0xd3 -#define RADIO_2064_REG0D4 0xd4 -#define RADIO_2064_REG0D5 0xd5 -#define RADIO_2064_REG0D6 0xd6 -#define RADIO_2064_REG0D7 0xd7 -#define RADIO_2064_REG0D8 0xd8 -#define RADIO_2064_REG0D9 0xd9 -#define RADIO_2064_REG0DA 0xda -#define RADIO_2064_REG0DB 0xdb -#define RADIO_2064_REG0DC 0xdc -#define RADIO_2064_REG0DD 0xdd -#define RADIO_2064_REG0DE 0xde -#define RADIO_2064_REG0DF 0xdf -#define RADIO_2064_REG0E0 0xe0 -#define RADIO_2064_REG0E1 0xe1 -#define RADIO_2064_REG0E2 0xe2 -#define RADIO_2064_REG0E3 0xe3 -#define RADIO_2064_REG0E4 0xe4 -#define RADIO_2064_REG0E5 0xe5 -#define RADIO_2064_REG0E6 0xe6 -#define RADIO_2064_REG0E7 0xe7 -#define RADIO_2064_REG0E8 0xe8 -#define RADIO_2064_REG0E9 0xe9 -#define RADIO_2064_REG0EA 0xea -#define RADIO_2064_REG0EB 0xeb -#define RADIO_2064_REG0EC 0xec -#define RADIO_2064_REG0ED 0xed -#define RADIO_2064_REG0EE 0xee -#define RADIO_2064_REG0EF 0xef -#define RADIO_2064_REG0F0 0xf0 -#define RADIO_2064_REG0F1 0xf1 -#define RADIO_2064_REG0F2 0xf2 -#define RADIO_2064_REG0F3 0xf3 -#define RADIO_2064_REG0F4 0xf4 -#define RADIO_2064_REG0F5 0xf5 -#define RADIO_2064_REG0F6 0xf6 -#define RADIO_2064_REG0F7 0xf7 -#define RADIO_2064_REG0F8 0xf8 -#define RADIO_2064_REG0F9 0xf9 -#define RADIO_2064_REG0FA 0xfa -#define RADIO_2064_REG0FB 0xfb -#define RADIO_2064_REG0FC 0xfc -#define RADIO_2064_REG0FD 0xfd -#define RADIO_2064_REG0FE 0xfe -#define RADIO_2064_REG0FF 0xff -#define RADIO_2064_REG100 0x100 -#define RADIO_2064_REG101 0x101 -#define RADIO_2064_REG102 0x102 -#define RADIO_2064_REG103 0x103 -#define RADIO_2064_REG104 0x104 -#define RADIO_2064_REG105 0x105 -#define RADIO_2064_REG106 0x106 -#define RADIO_2064_REG107 0x107 -#define RADIO_2064_REG108 0x108 -#define RADIO_2064_REG109 0x109 -#define RADIO_2064_REG10A 0x10a -#define RADIO_2064_REG10B 0x10b -#define RADIO_2064_REG10C 0x10c -#define RADIO_2064_REG10D 0x10d -#define RADIO_2064_REG10E 0x10e -#define RADIO_2064_REG10F 0x10f -#define RADIO_2064_REG110 0x110 -#define RADIO_2064_REG111 0x111 -#define RADIO_2064_REG112 0x112 -#define RADIO_2064_REG113 0x113 -#define RADIO_2064_REG114 0x114 -#define RADIO_2064_REG115 0x115 -#define RADIO_2064_REG116 0x116 -#define RADIO_2064_REG117 0x117 -#define RADIO_2064_REG118 0x118 -#define RADIO_2064_REG119 0x119 -#define RADIO_2064_REG11A 0x11a -#define RADIO_2064_REG11B 0x11b -#define RADIO_2064_REG11C 0x11c -#define RADIO_2064_REG11D 0x11d -#define RADIO_2064_REG11E 0x11e -#define RADIO_2064_REG11F 0x11f -#define RADIO_2064_REG120 0x120 -#define RADIO_2064_REG121 0x121 -#define RADIO_2064_REG122 0x122 -#define RADIO_2064_REG123 0x123 -#define RADIO_2064_REG124 0x124 -#define RADIO_2064_REG125 0x125 -#define RADIO_2064_REG126 0x126 -#define RADIO_2064_REG127 0x127 -#define RADIO_2064_REG128 0x128 -#define RADIO_2064_REG129 0x129 -#define RADIO_2064_REG12A 0x12a -#define RADIO_2064_REG12B 0x12b -#define RADIO_2064_REG12C 0x12c -#define RADIO_2064_REG12D 0x12d -#define RADIO_2064_REG12E 0x12e -#define RADIO_2064_REG12F 0x12f -#define RADIO_2064_REG130 0x130 - -#define RADIO_2056_SYN (0x0 << 12) -#define RADIO_2056_TX0 (0x2 << 12) -#define RADIO_2056_TX1 (0x3 << 12) -#define RADIO_2056_RX0 (0x6 << 12) -#define RADIO_2056_RX1 (0x7 << 12) -#define RADIO_2056_ALLTX (0xe << 12) -#define RADIO_2056_ALLRX (0xf << 12) - -#define RADIO_2056_SYN_RESERVED_ADDR0 0x0 -#define RADIO_2056_SYN_IDCODE 0x1 -#define RADIO_2056_SYN_RESERVED_ADDR2 0x2 -#define RADIO_2056_SYN_RESERVED_ADDR3 0x3 -#define RADIO_2056_SYN_RESERVED_ADDR4 0x4 -#define RADIO_2056_SYN_RESERVED_ADDR5 0x5 -#define RADIO_2056_SYN_RESERVED_ADDR6 0x6 -#define RADIO_2056_SYN_RESERVED_ADDR7 0x7 -#define RADIO_2056_SYN_COM_CTRL 0x8 -#define RADIO_2056_SYN_COM_PU 0x9 -#define RADIO_2056_SYN_COM_OVR 0xa -#define RADIO_2056_SYN_COM_RESET 0xb -#define RADIO_2056_SYN_COM_RCAL 0xc -#define RADIO_2056_SYN_COM_RC_RXLPF 0xd -#define RADIO_2056_SYN_COM_RC_TXLPF 0xe -#define RADIO_2056_SYN_COM_RC_RXHPF 0xf -#define RADIO_2056_SYN_RESERVED_ADDR16 0x10 -#define RADIO_2056_SYN_RESERVED_ADDR17 0x11 -#define RADIO_2056_SYN_RESERVED_ADDR18 0x12 -#define RADIO_2056_SYN_RESERVED_ADDR19 0x13 -#define RADIO_2056_SYN_RESERVED_ADDR20 0x14 -#define RADIO_2056_SYN_RESERVED_ADDR21 0x15 -#define RADIO_2056_SYN_RESERVED_ADDR22 0x16 -#define RADIO_2056_SYN_RESERVED_ADDR23 0x17 -#define RADIO_2056_SYN_RESERVED_ADDR24 0x18 -#define RADIO_2056_SYN_RESERVED_ADDR25 0x19 -#define RADIO_2056_SYN_RESERVED_ADDR26 0x1a -#define RADIO_2056_SYN_RESERVED_ADDR27 0x1b -#define RADIO_2056_SYN_RESERVED_ADDR28 0x1c -#define RADIO_2056_SYN_RESERVED_ADDR29 0x1d -#define RADIO_2056_SYN_RESERVED_ADDR30 0x1e -#define RADIO_2056_SYN_RESERVED_ADDR31 0x1f -#define RADIO_2056_SYN_GPIO_MASTER1 0x20 -#define RADIO_2056_SYN_GPIO_MASTER2 0x21 -#define RADIO_2056_SYN_TOPBIAS_MASTER 0x22 -#define RADIO_2056_SYN_TOPBIAS_RCAL 0x23 -#define RADIO_2056_SYN_AFEREG 0x24 -#define RADIO_2056_SYN_TEMPPROCSENSE 0x25 -#define RADIO_2056_SYN_TEMPPROCSENSEIDAC 0x26 -#define RADIO_2056_SYN_TEMPPROCSENSERCAL 0x27 -#define RADIO_2056_SYN_LPO 0x28 -#define RADIO_2056_SYN_VDDCAL_MASTER 0x29 -#define RADIO_2056_SYN_VDDCAL_IDAC 0x2a -#define RADIO_2056_SYN_VDDCAL_STATUS 0x2b -#define RADIO_2056_SYN_RCAL_MASTER 0x2c -#define RADIO_2056_SYN_RCAL_CODE_OUT 0x2d -#define RADIO_2056_SYN_RCCAL_CTRL0 0x2e -#define RADIO_2056_SYN_RCCAL_CTRL1 0x2f -#define RADIO_2056_SYN_RCCAL_CTRL2 0x30 -#define RADIO_2056_SYN_RCCAL_CTRL3 0x31 -#define RADIO_2056_SYN_RCCAL_CTRL4 0x32 -#define RADIO_2056_SYN_RCCAL_CTRL5 0x33 -#define RADIO_2056_SYN_RCCAL_CTRL6 0x34 -#define RADIO_2056_SYN_RCCAL_CTRL7 0x35 -#define RADIO_2056_SYN_RCCAL_CTRL8 0x36 -#define RADIO_2056_SYN_RCCAL_CTRL9 0x37 -#define RADIO_2056_SYN_RCCAL_CTRL10 0x38 -#define RADIO_2056_SYN_RCCAL_CTRL11 0x39 -#define RADIO_2056_SYN_ZCAL_SPARE1 0x3a -#define RADIO_2056_SYN_ZCAL_SPARE2 0x3b -#define RADIO_2056_SYN_PLL_MAST1 0x3c -#define RADIO_2056_SYN_PLL_MAST2 0x3d -#define RADIO_2056_SYN_PLL_MAST3 0x3e -#define RADIO_2056_SYN_PLL_BIAS_RESET 0x3f -#define RADIO_2056_SYN_PLL_XTAL0 0x40 -#define RADIO_2056_SYN_PLL_XTAL1 0x41 -#define RADIO_2056_SYN_PLL_XTAL3 0x42 -#define RADIO_2056_SYN_PLL_XTAL4 0x43 -#define RADIO_2056_SYN_PLL_XTAL5 0x44 -#define RADIO_2056_SYN_PLL_XTAL6 0x45 -#define RADIO_2056_SYN_PLL_REFDIV 0x46 -#define RADIO_2056_SYN_PLL_PFD 0x47 -#define RADIO_2056_SYN_PLL_CP1 0x48 -#define RADIO_2056_SYN_PLL_CP2 0x49 -#define RADIO_2056_SYN_PLL_CP3 0x4a -#define RADIO_2056_SYN_PLL_LOOPFILTER1 0x4b -#define RADIO_2056_SYN_PLL_LOOPFILTER2 0x4c -#define RADIO_2056_SYN_PLL_LOOPFILTER3 0x4d -#define RADIO_2056_SYN_PLL_LOOPFILTER4 0x4e -#define RADIO_2056_SYN_PLL_LOOPFILTER5 0x4f -#define RADIO_2056_SYN_PLL_MMD1 0x50 -#define RADIO_2056_SYN_PLL_MMD2 0x51 -#define RADIO_2056_SYN_PLL_VCO1 0x52 -#define RADIO_2056_SYN_PLL_VCO2 0x53 -#define RADIO_2056_SYN_PLL_MONITOR1 0x54 -#define RADIO_2056_SYN_PLL_MONITOR2 0x55 -#define RADIO_2056_SYN_PLL_VCOCAL1 0x56 -#define RADIO_2056_SYN_PLL_VCOCAL2 0x57 -#define RADIO_2056_SYN_PLL_VCOCAL4 0x58 -#define RADIO_2056_SYN_PLL_VCOCAL5 0x59 -#define RADIO_2056_SYN_PLL_VCOCAL6 0x5a -#define RADIO_2056_SYN_PLL_VCOCAL7 0x5b -#define RADIO_2056_SYN_PLL_VCOCAL8 0x5c -#define RADIO_2056_SYN_PLL_VCOCAL9 0x5d -#define RADIO_2056_SYN_PLL_VCOCAL10 0x5e -#define RADIO_2056_SYN_PLL_VCOCAL11 0x5f -#define RADIO_2056_SYN_PLL_VCOCAL12 0x60 -#define RADIO_2056_SYN_PLL_VCOCAL13 0x61 -#define RADIO_2056_SYN_PLL_VREG 0x62 -#define RADIO_2056_SYN_PLL_STATUS1 0x63 -#define RADIO_2056_SYN_PLL_STATUS2 0x64 -#define RADIO_2056_SYN_PLL_STATUS3 0x65 -#define RADIO_2056_SYN_LOGEN_PU0 0x66 -#define RADIO_2056_SYN_LOGEN_PU1 0x67 -#define RADIO_2056_SYN_LOGEN_PU2 0x68 -#define RADIO_2056_SYN_LOGEN_PU3 0x69 -#define RADIO_2056_SYN_LOGEN_PU5 0x6a -#define RADIO_2056_SYN_LOGEN_PU6 0x6b -#define RADIO_2056_SYN_LOGEN_PU7 0x6c -#define RADIO_2056_SYN_LOGEN_PU8 0x6d -#define RADIO_2056_SYN_LOGEN_BIAS_RESET 0x6e -#define RADIO_2056_SYN_LOGEN_RCCR1 0x6f -#define RADIO_2056_SYN_LOGEN_VCOBUF1 0x70 -#define RADIO_2056_SYN_LOGEN_MIXER1 0x71 -#define RADIO_2056_SYN_LOGEN_MIXER2 0x72 -#define RADIO_2056_SYN_LOGEN_BUF1 0x73 -#define RADIO_2056_SYN_LOGENBUF2 0x74 -#define RADIO_2056_SYN_LOGEN_BUF3 0x75 -#define RADIO_2056_SYN_LOGEN_BUF4 0x76 -#define RADIO_2056_SYN_LOGEN_DIV1 0x77 -#define RADIO_2056_SYN_LOGEN_DIV2 0x78 -#define RADIO_2056_SYN_LOGEN_DIV3 0x79 -#define RADIO_2056_SYN_LOGEN_ACL1 0x7a -#define RADIO_2056_SYN_LOGEN_ACL2 0x7b -#define RADIO_2056_SYN_LOGEN_ACL3 0x7c -#define RADIO_2056_SYN_LOGEN_ACL4 0x7d -#define RADIO_2056_SYN_LOGEN_ACL5 0x7e -#define RADIO_2056_SYN_LOGEN_ACL6 0x7f -#define RADIO_2056_SYN_LOGEN_ACLOUT 0x80 -#define RADIO_2056_SYN_LOGEN_ACLCAL1 0x81 -#define RADIO_2056_SYN_LOGEN_ACLCAL2 0x82 -#define RADIO_2056_SYN_LOGEN_ACLCAL3 0x83 -#define RADIO_2056_SYN_CALEN 0x84 -#define RADIO_2056_SYN_LOGEN_PEAKDET1 0x85 -#define RADIO_2056_SYN_LOGEN_CORE_ACL_OVR 0x86 -#define RADIO_2056_SYN_LOGEN_RX_DIFF_ACL_OVR 0x87 -#define RADIO_2056_SYN_LOGEN_TX_DIFF_ACL_OVR 0x88 -#define RADIO_2056_SYN_LOGEN_RX_CMOS_ACL_OVR 0x89 -#define RADIO_2056_SYN_LOGEN_TX_CMOS_ACL_OVR 0x8a -#define RADIO_2056_SYN_LOGEN_VCOBUF2 0x8b -#define RADIO_2056_SYN_LOGEN_MIXER3 0x8c -#define RADIO_2056_SYN_LOGEN_BUF5 0x8d -#define RADIO_2056_SYN_LOGEN_BUF6 0x8e -#define RADIO_2056_SYN_LOGEN_CBUFRX1 0x8f -#define RADIO_2056_SYN_LOGEN_CBUFRX2 0x90 -#define RADIO_2056_SYN_LOGEN_CBUFRX3 0x91 -#define RADIO_2056_SYN_LOGEN_CBUFRX4 0x92 -#define RADIO_2056_SYN_LOGEN_CBUFTX1 0x93 -#define RADIO_2056_SYN_LOGEN_CBUFTX2 0x94 -#define RADIO_2056_SYN_LOGEN_CBUFTX3 0x95 -#define RADIO_2056_SYN_LOGEN_CBUFTX4 0x96 -#define RADIO_2056_SYN_LOGEN_CMOSRX1 0x97 -#define RADIO_2056_SYN_LOGEN_CMOSRX2 0x98 -#define RADIO_2056_SYN_LOGEN_CMOSRX3 0x99 -#define RADIO_2056_SYN_LOGEN_CMOSRX4 0x9a -#define RADIO_2056_SYN_LOGEN_CMOSTX1 0x9b -#define RADIO_2056_SYN_LOGEN_CMOSTX2 0x9c -#define RADIO_2056_SYN_LOGEN_CMOSTX3 0x9d -#define RADIO_2056_SYN_LOGEN_CMOSTX4 0x9e -#define RADIO_2056_SYN_LOGEN_VCOBUF2_OVRVAL 0x9f -#define RADIO_2056_SYN_LOGEN_MIXER3_OVRVAL 0xa0 -#define RADIO_2056_SYN_LOGEN_BUF5_OVRVAL 0xa1 -#define RADIO_2056_SYN_LOGEN_BUF6_OVRVAL 0xa2 -#define RADIO_2056_SYN_LOGEN_CBUFRX1_OVRVAL 0xa3 -#define RADIO_2056_SYN_LOGEN_CBUFRX2_OVRVAL 0xa4 -#define RADIO_2056_SYN_LOGEN_CBUFRX3_OVRVAL 0xa5 -#define RADIO_2056_SYN_LOGEN_CBUFRX4_OVRVAL 0xa6 -#define RADIO_2056_SYN_LOGEN_CBUFTX1_OVRVAL 0xa7 -#define RADIO_2056_SYN_LOGEN_CBUFTX2_OVRVAL 0xa8 -#define RADIO_2056_SYN_LOGEN_CBUFTX3_OVRVAL 0xa9 -#define RADIO_2056_SYN_LOGEN_CBUFTX4_OVRVAL 0xaa -#define RADIO_2056_SYN_LOGEN_CMOSRX1_OVRVAL 0xab -#define RADIO_2056_SYN_LOGEN_CMOSRX2_OVRVAL 0xac -#define RADIO_2056_SYN_LOGEN_CMOSRX3_OVRVAL 0xad -#define RADIO_2056_SYN_LOGEN_CMOSRX4_OVRVAL 0xae -#define RADIO_2056_SYN_LOGEN_CMOSTX1_OVRVAL 0xaf -#define RADIO_2056_SYN_LOGEN_CMOSTX2_OVRVAL 0xb0 -#define RADIO_2056_SYN_LOGEN_CMOSTX3_OVRVAL 0xb1 -#define RADIO_2056_SYN_LOGEN_CMOSTX4_OVRVAL 0xb2 -#define RADIO_2056_SYN_LOGEN_ACL_WAITCNT 0xb3 -#define RADIO_2056_SYN_LOGEN_CORE_CALVALID 0xb4 -#define RADIO_2056_SYN_LOGEN_RX_CMOS_CALVALID 0xb5 -#define RADIO_2056_SYN_LOGEN_TX_CMOS_VALID 0xb6 - -#define RADIO_2056_TX_RESERVED_ADDR0 0x0 -#define RADIO_2056_TX_IDCODE 0x1 -#define RADIO_2056_TX_RESERVED_ADDR2 0x2 -#define RADIO_2056_TX_RESERVED_ADDR3 0x3 -#define RADIO_2056_TX_RESERVED_ADDR4 0x4 -#define RADIO_2056_TX_RESERVED_ADDR5 0x5 -#define RADIO_2056_TX_RESERVED_ADDR6 0x6 -#define RADIO_2056_TX_RESERVED_ADDR7 0x7 -#define RADIO_2056_TX_COM_CTRL 0x8 -#define RADIO_2056_TX_COM_PU 0x9 -#define RADIO_2056_TX_COM_OVR 0xa -#define RADIO_2056_TX_COM_RESET 0xb -#define RADIO_2056_TX_COM_RCAL 0xc -#define RADIO_2056_TX_COM_RC_RXLPF 0xd -#define RADIO_2056_TX_COM_RC_TXLPF 0xe -#define RADIO_2056_TX_COM_RC_RXHPF 0xf -#define RADIO_2056_TX_RESERVED_ADDR16 0x10 -#define RADIO_2056_TX_RESERVED_ADDR17 0x11 -#define RADIO_2056_TX_RESERVED_ADDR18 0x12 -#define RADIO_2056_TX_RESERVED_ADDR19 0x13 -#define RADIO_2056_TX_RESERVED_ADDR20 0x14 -#define RADIO_2056_TX_RESERVED_ADDR21 0x15 -#define RADIO_2056_TX_RESERVED_ADDR22 0x16 -#define RADIO_2056_TX_RESERVED_ADDR23 0x17 -#define RADIO_2056_TX_RESERVED_ADDR24 0x18 -#define RADIO_2056_TX_RESERVED_ADDR25 0x19 -#define RADIO_2056_TX_RESERVED_ADDR26 0x1a -#define RADIO_2056_TX_RESERVED_ADDR27 0x1b -#define RADIO_2056_TX_RESERVED_ADDR28 0x1c -#define RADIO_2056_TX_RESERVED_ADDR29 0x1d -#define RADIO_2056_TX_RESERVED_ADDR30 0x1e -#define RADIO_2056_TX_RESERVED_ADDR31 0x1f -#define RADIO_2056_TX_IQCAL_GAIN_BW 0x20 -#define RADIO_2056_TX_LOFT_FINE_I 0x21 -#define RADIO_2056_TX_LOFT_FINE_Q 0x22 -#define RADIO_2056_TX_LOFT_COARSE_I 0x23 -#define RADIO_2056_TX_LOFT_COARSE_Q 0x24 -#define RADIO_2056_TX_TX_COM_MASTER1 0x25 -#define RADIO_2056_TX_TX_COM_MASTER2 0x26 -#define RADIO_2056_TX_RXIQCAL_TXMUX 0x27 -#define RADIO_2056_TX_TX_SSI_MASTER 0x28 -#define RADIO_2056_TX_IQCAL_VCM_HG 0x29 -#define RADIO_2056_TX_IQCAL_IDAC 0x2a -#define RADIO_2056_TX_TSSI_VCM 0x2b -#define RADIO_2056_TX_TX_AMP_DET 0x2c -#define RADIO_2056_TX_TX_SSI_MUX 0x2d -#define RADIO_2056_TX_TSSIA 0x2e -#define RADIO_2056_TX_TSSIG 0x2f -#define RADIO_2056_TX_TSSI_MISC1 0x30 -#define RADIO_2056_TX_TSSI_MISC2 0x31 -#define RADIO_2056_TX_TSSI_MISC3 0x32 -#define RADIO_2056_TX_PA_SPARE1 0x33 -#define RADIO_2056_TX_PA_SPARE2 0x34 -#define RADIO_2056_TX_INTPAA_MASTER 0x35 -#define RADIO_2056_TX_INTPAA_GAIN 0x36 -#define RADIO_2056_TX_INTPAA_BOOST_TUNE 0x37 -#define RADIO_2056_TX_INTPAA_IAUX_STAT 0x38 -#define RADIO_2056_TX_INTPAA_IAUX_DYN 0x39 -#define RADIO_2056_TX_INTPAA_IMAIN_STAT 0x3a -#define RADIO_2056_TX_INTPAA_IMAIN_DYN 0x3b -#define RADIO_2056_TX_INTPAA_CASCBIAS 0x3c -#define RADIO_2056_TX_INTPAA_PASLOPE 0x3d -#define RADIO_2056_TX_INTPAA_PA_MISC 0x3e -#define RADIO_2056_TX_INTPAG_MASTER 0x3f -#define RADIO_2056_TX_INTPAG_GAIN 0x40 -#define RADIO_2056_TX_INTPAG_BOOST_TUNE 0x41 -#define RADIO_2056_TX_INTPAG_IAUX_STAT 0x42 -#define RADIO_2056_TX_INTPAG_IAUX_DYN 0x43 -#define RADIO_2056_TX_INTPAG_IMAIN_STAT 0x44 -#define RADIO_2056_TX_INTPAG_IMAIN_DYN 0x45 -#define RADIO_2056_TX_INTPAG_CASCBIAS 0x46 -#define RADIO_2056_TX_INTPAG_PASLOPE 0x47 -#define RADIO_2056_TX_INTPAG_PA_MISC 0x48 -#define RADIO_2056_TX_PADA_MASTER 0x49 -#define RADIO_2056_TX_PADA_IDAC 0x4a -#define RADIO_2056_TX_PADA_CASCBIAS 0x4b -#define RADIO_2056_TX_PADA_GAIN 0x4c -#define RADIO_2056_TX_PADA_BOOST_TUNE 0x4d -#define RADIO_2056_TX_PADA_SLOPE 0x4e -#define RADIO_2056_TX_PADG_MASTER 0x4f -#define RADIO_2056_TX_PADG_IDAC 0x50 -#define RADIO_2056_TX_PADG_CASCBIAS 0x51 -#define RADIO_2056_TX_PADG_GAIN 0x52 -#define RADIO_2056_TX_PADG_BOOST_TUNE 0x53 -#define RADIO_2056_TX_PADG_SLOPE 0x54 -#define RADIO_2056_TX_PGAA_MASTER 0x55 -#define RADIO_2056_TX_PGAA_IDAC 0x56 -#define RADIO_2056_TX_PGAA_GAIN 0x57 -#define RADIO_2056_TX_PGAA_BOOST_TUNE 0x58 -#define RADIO_2056_TX_PGAA_SLOPE 0x59 -#define RADIO_2056_TX_PGAA_MISC 0x5a -#define RADIO_2056_TX_PGAG_MASTER 0x5b -#define RADIO_2056_TX_PGAG_IDAC 0x5c -#define RADIO_2056_TX_PGAG_GAIN 0x5d -#define RADIO_2056_TX_PGAG_BOOST_TUNE 0x5e -#define RADIO_2056_TX_PGAG_SLOPE 0x5f -#define RADIO_2056_TX_PGAG_MISC 0x60 -#define RADIO_2056_TX_MIXA_MASTER 0x61 -#define RADIO_2056_TX_MIXA_BOOST_TUNE 0x62 -#define RADIO_2056_TX_MIXG 0x63 -#define RADIO_2056_TX_MIXG_BOOST_TUNE 0x64 -#define RADIO_2056_TX_BB_GM_MASTER 0x65 -#define RADIO_2056_TX_GMBB_GM 0x66 -#define RADIO_2056_TX_GMBB_IDAC 0x67 -#define RADIO_2056_TX_TXLPF_MASTER 0x68 -#define RADIO_2056_TX_TXLPF_RCCAL 0x69 -#define RADIO_2056_TX_TXLPF_RCCAL_OFF0 0x6a -#define RADIO_2056_TX_TXLPF_RCCAL_OFF1 0x6b -#define RADIO_2056_TX_TXLPF_RCCAL_OFF2 0x6c -#define RADIO_2056_TX_TXLPF_RCCAL_OFF3 0x6d -#define RADIO_2056_TX_TXLPF_RCCAL_OFF4 0x6e -#define RADIO_2056_TX_TXLPF_RCCAL_OFF5 0x6f -#define RADIO_2056_TX_TXLPF_RCCAL_OFF6 0x70 -#define RADIO_2056_TX_TXLPF_BW 0x71 -#define RADIO_2056_TX_TXLPF_GAIN 0x72 -#define RADIO_2056_TX_TXLPF_IDAC 0x73 -#define RADIO_2056_TX_TXLPF_IDAC_0 0x74 -#define RADIO_2056_TX_TXLPF_IDAC_1 0x75 -#define RADIO_2056_TX_TXLPF_IDAC_2 0x76 -#define RADIO_2056_TX_TXLPF_IDAC_3 0x77 -#define RADIO_2056_TX_TXLPF_IDAC_4 0x78 -#define RADIO_2056_TX_TXLPF_IDAC_5 0x79 -#define RADIO_2056_TX_TXLPF_IDAC_6 0x7a -#define RADIO_2056_TX_TXLPF_OPAMP_IDAC 0x7b -#define RADIO_2056_TX_TXLPF_MISC 0x7c -#define RADIO_2056_TX_TXSPARE1 0x7d -#define RADIO_2056_TX_TXSPARE2 0x7e -#define RADIO_2056_TX_TXSPARE3 0x7f -#define RADIO_2056_TX_TXSPARE4 0x80 -#define RADIO_2056_TX_TXSPARE5 0x81 -#define RADIO_2056_TX_TXSPARE6 0x82 -#define RADIO_2056_TX_TXSPARE7 0x83 -#define RADIO_2056_TX_TXSPARE8 0x84 -#define RADIO_2056_TX_TXSPARE9 0x85 -#define RADIO_2056_TX_TXSPARE10 0x86 -#define RADIO_2056_TX_TXSPARE11 0x87 -#define RADIO_2056_TX_TXSPARE12 0x88 -#define RADIO_2056_TX_TXSPARE13 0x89 -#define RADIO_2056_TX_TXSPARE14 0x8a -#define RADIO_2056_TX_TXSPARE15 0x8b -#define RADIO_2056_TX_TXSPARE16 0x8c -#define RADIO_2056_TX_STATUS_INTPA_GAIN 0x8d -#define RADIO_2056_TX_STATUS_PAD_GAIN 0x8e -#define RADIO_2056_TX_STATUS_PGA_GAIN 0x8f -#define RADIO_2056_TX_STATUS_GM_TXLPF_GAIN 0x90 -#define RADIO_2056_TX_STATUS_TXLPF_BW 0x91 -#define RADIO_2056_TX_STATUS_TXLPF_RC 0x92 -#define RADIO_2056_TX_GMBB_IDAC0 0x93 -#define RADIO_2056_TX_GMBB_IDAC1 0x94 -#define RADIO_2056_TX_GMBB_IDAC2 0x95 -#define RADIO_2056_TX_GMBB_IDAC3 0x96 -#define RADIO_2056_TX_GMBB_IDAC4 0x97 -#define RADIO_2056_TX_GMBB_IDAC5 0x98 -#define RADIO_2056_TX_GMBB_IDAC6 0x99 -#define RADIO_2056_TX_GMBB_IDAC7 0x9a - -#define RADIO_2056_RX_RESERVED_ADDR0 0x0 -#define RADIO_2056_RX_IDCODE 0x1 -#define RADIO_2056_RX_RESERVED_ADDR2 0x2 -#define RADIO_2056_RX_RESERVED_ADDR3 0x3 -#define RADIO_2056_RX_RESERVED_ADDR4 0x4 -#define RADIO_2056_RX_RESERVED_ADDR5 0x5 -#define RADIO_2056_RX_RESERVED_ADDR6 0x6 -#define RADIO_2056_RX_RESERVED_ADDR7 0x7 -#define RADIO_2056_RX_COM_CTRL 0x8 -#define RADIO_2056_RX_COM_PU 0x9 -#define RADIO_2056_RX_COM_OVR 0xa -#define RADIO_2056_RX_COM_RESET 0xb -#define RADIO_2056_RX_COM_RCAL 0xc -#define RADIO_2056_RX_COM_RC_RXLPF 0xd -#define RADIO_2056_RX_COM_RC_TXLPF 0xe -#define RADIO_2056_RX_COM_RC_RXHPF 0xf -#define RADIO_2056_RX_RESERVED_ADDR16 0x10 -#define RADIO_2056_RX_RESERVED_ADDR17 0x11 -#define RADIO_2056_RX_RESERVED_ADDR18 0x12 -#define RADIO_2056_RX_RESERVED_ADDR19 0x13 -#define RADIO_2056_RX_RESERVED_ADDR20 0x14 -#define RADIO_2056_RX_RESERVED_ADDR21 0x15 -#define RADIO_2056_RX_RESERVED_ADDR22 0x16 -#define RADIO_2056_RX_RESERVED_ADDR23 0x17 -#define RADIO_2056_RX_RESERVED_ADDR24 0x18 -#define RADIO_2056_RX_RESERVED_ADDR25 0x19 -#define RADIO_2056_RX_RESERVED_ADDR26 0x1a -#define RADIO_2056_RX_RESERVED_ADDR27 0x1b -#define RADIO_2056_RX_RESERVED_ADDR28 0x1c -#define RADIO_2056_RX_RESERVED_ADDR29 0x1d -#define RADIO_2056_RX_RESERVED_ADDR30 0x1e -#define RADIO_2056_RX_RESERVED_ADDR31 0x1f -#define RADIO_2056_RX_RXIQCAL_RXMUX 0x20 -#define RADIO_2056_RX_RSSI_PU 0x21 -#define RADIO_2056_RX_RSSI_SEL 0x22 -#define RADIO_2056_RX_RSSI_GAIN 0x23 -#define RADIO_2056_RX_RSSI_NB_IDAC 0x24 -#define RADIO_2056_RX_RSSI_WB2I_IDAC_1 0x25 -#define RADIO_2056_RX_RSSI_WB2I_IDAC_2 0x26 -#define RADIO_2056_RX_RSSI_WB2Q_IDAC_1 0x27 -#define RADIO_2056_RX_RSSI_WB2Q_IDAC_2 0x28 -#define RADIO_2056_RX_RSSI_POLE 0x29 -#define RADIO_2056_RX_RSSI_WB1_IDAC 0x2a -#define RADIO_2056_RX_RSSI_MISC 0x2b -#define RADIO_2056_RX_LNAA_MASTER 0x2c -#define RADIO_2056_RX_LNAA_TUNE 0x2d -#define RADIO_2056_RX_LNAA_GAIN 0x2e -#define RADIO_2056_RX_LNA_A_SLOPE 0x2f -#define RADIO_2056_RX_BIASPOLE_LNAA1_IDAC 0x30 -#define RADIO_2056_RX_LNAA2_IDAC 0x31 -#define RADIO_2056_RX_LNA1A_MISC 0x32 -#define RADIO_2056_RX_LNAG_MASTER 0x33 -#define RADIO_2056_RX_LNAG_TUNE 0x34 -#define RADIO_2056_RX_LNAG_GAIN 0x35 -#define RADIO_2056_RX_LNA_G_SLOPE 0x36 -#define RADIO_2056_RX_BIASPOLE_LNAG1_IDAC 0x37 -#define RADIO_2056_RX_LNAG2_IDAC 0x38 -#define RADIO_2056_RX_LNA1G_MISC 0x39 -#define RADIO_2056_RX_MIXA_MASTER 0x3a -#define RADIO_2056_RX_MIXA_VCM 0x3b -#define RADIO_2056_RX_MIXA_CTRLPTAT 0x3c -#define RADIO_2056_RX_MIXA_LOB_BIAS 0x3d -#define RADIO_2056_RX_MIXA_CORE_IDAC 0x3e -#define RADIO_2056_RX_MIXA_CMFB_IDAC 0x3f -#define RADIO_2056_RX_MIXA_BIAS_AUX 0x40 -#define RADIO_2056_RX_MIXA_BIAS_MAIN 0x41 -#define RADIO_2056_RX_MIXA_BIAS_MISC 0x42 -#define RADIO_2056_RX_MIXA_MAST_BIAS 0x43 -#define RADIO_2056_RX_MIXG_MASTER 0x44 -#define RADIO_2056_RX_MIXG_VCM 0x45 -#define RADIO_2056_RX_MIXG_CTRLPTAT 0x46 -#define RADIO_2056_RX_MIXG_LOB_BIAS 0x47 -#define RADIO_2056_RX_MIXG_CORE_IDAC 0x48 -#define RADIO_2056_RX_MIXG_CMFB_IDAC 0x49 -#define RADIO_2056_RX_MIXG_BIAS_AUX 0x4a -#define RADIO_2056_RX_MIXG_BIAS_MAIN 0x4b -#define RADIO_2056_RX_MIXG_BIAS_MISC 0x4c -#define RADIO_2056_RX_MIXG_MAST_BIAS 0x4d -#define RADIO_2056_RX_TIA_MASTER 0x4e -#define RADIO_2056_RX_TIA_IOPAMP 0x4f -#define RADIO_2056_RX_TIA_QOPAMP 0x50 -#define RADIO_2056_RX_TIA_IMISC 0x51 -#define RADIO_2056_RX_TIA_QMISC 0x52 -#define RADIO_2056_RX_TIA_GAIN 0x53 -#define RADIO_2056_RX_TIA_SPARE1 0x54 -#define RADIO_2056_RX_TIA_SPARE2 0x55 -#define RADIO_2056_RX_BB_LPF_MASTER 0x56 -#define RADIO_2056_RX_AACI_MASTER 0x57 -#define RADIO_2056_RX_RXLPF_IDAC 0x58 -#define RADIO_2056_RX_RXLPF_OPAMPBIAS_LOWQ 0x59 -#define RADIO_2056_RX_RXLPF_OPAMPBIAS_HIGHQ 0x5a -#define RADIO_2056_RX_RXLPF_BIAS_DCCANCEL 0x5b -#define RADIO_2056_RX_RXLPF_OUTVCM 0x5c -#define RADIO_2056_RX_RXLPF_INVCM_BODY 0x5d -#define RADIO_2056_RX_RXLPF_CC_OP 0x5e -#define RADIO_2056_RX_RXLPF_GAIN 0x5f -#define RADIO_2056_RX_RXLPF_Q_BW 0x60 -#define RADIO_2056_RX_RXLPF_HP_CORNER_BW 0x61 -#define RADIO_2056_RX_RXLPF_RCCAL_HPC 0x62 -#define RADIO_2056_RX_RXHPF_OFF0 0x63 -#define RADIO_2056_RX_RXHPF_OFF1 0x64 -#define RADIO_2056_RX_RXHPF_OFF2 0x65 -#define RADIO_2056_RX_RXHPF_OFF3 0x66 -#define RADIO_2056_RX_RXHPF_OFF4 0x67 -#define RADIO_2056_RX_RXHPF_OFF5 0x68 -#define RADIO_2056_RX_RXHPF_OFF6 0x69 -#define RADIO_2056_RX_RXHPF_OFF7 0x6a -#define RADIO_2056_RX_RXLPF_RCCAL_LPC 0x6b -#define RADIO_2056_RX_RXLPF_OFF_0 0x6c -#define RADIO_2056_RX_RXLPF_OFF_1 0x6d -#define RADIO_2056_RX_RXLPF_OFF_2 0x6e -#define RADIO_2056_RX_RXLPF_OFF_3 0x6f -#define RADIO_2056_RX_RXLPF_OFF_4 0x70 -#define RADIO_2056_RX_UNUSED 0x71 -#define RADIO_2056_RX_VGA_MASTER 0x72 -#define RADIO_2056_RX_VGA_BIAS 0x73 -#define RADIO_2056_RX_VGA_BIAS_DCCANCEL 0x74 -#define RADIO_2056_RX_VGA_GAIN 0x75 -#define RADIO_2056_RX_VGA_HP_CORNER_BW 0x76 -#define RADIO_2056_RX_VGABUF_BIAS 0x77 -#define RADIO_2056_RX_VGABUF_GAIN_BW 0x78 -#define RADIO_2056_RX_TXFBMIX_A 0x79 -#define RADIO_2056_RX_TXFBMIX_G 0x7a -#define RADIO_2056_RX_RXSPARE1 0x7b -#define RADIO_2056_RX_RXSPARE2 0x7c -#define RADIO_2056_RX_RXSPARE3 0x7d -#define RADIO_2056_RX_RXSPARE4 0x7e -#define RADIO_2056_RX_RXSPARE5 0x7f -#define RADIO_2056_RX_RXSPARE6 0x80 -#define RADIO_2056_RX_RXSPARE7 0x81 -#define RADIO_2056_RX_RXSPARE8 0x82 -#define RADIO_2056_RX_RXSPARE9 0x83 -#define RADIO_2056_RX_RXSPARE10 0x84 -#define RADIO_2056_RX_RXSPARE11 0x85 -#define RADIO_2056_RX_RXSPARE12 0x86 -#define RADIO_2056_RX_RXSPARE13 0x87 -#define RADIO_2056_RX_RXSPARE14 0x88 -#define RADIO_2056_RX_RXSPARE15 0x89 -#define RADIO_2056_RX_RXSPARE16 0x8a -#define RADIO_2056_RX_STATUS_LNAA_GAIN 0x8b -#define RADIO_2056_RX_STATUS_LNAG_GAIN 0x8c -#define RADIO_2056_RX_STATUS_MIXTIA_GAIN 0x8d -#define RADIO_2056_RX_STATUS_RXLPF_GAIN 0x8e -#define RADIO_2056_RX_STATUS_VGA_BUF_GAIN 0x8f -#define RADIO_2056_RX_STATUS_RXLPF_Q 0x90 -#define RADIO_2056_RX_STATUS_RXLPF_BUF_BW 0x91 -#define RADIO_2056_RX_STATUS_RXLPF_VGA_HPC 0x92 -#define RADIO_2056_RX_STATUS_RXLPF_RC 0x93 -#define RADIO_2056_RX_STATUS_HPC_RC 0x94 - -#define RADIO_2056_LNA1_A_PU 0x01 -#define RADIO_2056_LNA2_A_PU 0x02 -#define RADIO_2056_LNA1_G_PU 0x01 -#define RADIO_2056_LNA2_G_PU 0x02 -#define RADIO_2056_MIXA_PU_I 0x01 -#define RADIO_2056_MIXA_PU_Q 0x02 -#define RADIO_2056_MIXA_PU_GM 0x10 -#define RADIO_2056_MIXG_PU_I 0x01 -#define RADIO_2056_MIXG_PU_Q 0x02 -#define RADIO_2056_MIXG_PU_GM 0x10 -#define RADIO_2056_TIA_PU 0x01 -#define RADIO_2056_BB_LPF_PU 0x20 -#define RADIO_2056_W1_PU 0x02 -#define RADIO_2056_W2_PU 0x04 -#define RADIO_2056_NB_PU 0x08 -#define RADIO_2056_RSSI_W1_SEL 0x02 -#define RADIO_2056_RSSI_W2_SEL 0x04 -#define RADIO_2056_RSSI_NB_SEL 0x08 -#define RADIO_2056_VCM_MASK 0x1c -#define RADIO_2056_RSSI_VCM_SHIFT 0x02 - -#define RADIO_2057_DACBUF_VINCM_CORE0 0x0 -#define RADIO_2057_IDCODE 0x1 -#define RADIO_2057_RCCAL_MASTER 0x2 -#define RADIO_2057_RCCAL_CAP_SIZE 0x3 -#define RADIO_2057_RCAL_CONFIG 0x4 -#define RADIO_2057_GPAIO_CONFIG 0x5 -#define RADIO_2057_GPAIO_SEL1 0x6 -#define RADIO_2057_GPAIO_SEL0 0x7 -#define RADIO_2057_CLPO_CONFIG 0x8 -#define RADIO_2057_BANDGAP_CONFIG 0x9 -#define RADIO_2057_BANDGAP_RCAL_TRIM 0xa -#define RADIO_2057_AFEREG_CONFIG 0xb -#define RADIO_2057_TEMPSENSE_CONFIG 0xc -#define RADIO_2057_XTAL_CONFIG1 0xd -#define RADIO_2057_XTAL_ICORE_SIZE 0xe -#define RADIO_2057_XTAL_BUF_SIZE 0xf -#define RADIO_2057_XTAL_PULLCAP_SIZE 0x10 -#define RADIO_2057_RFPLL_MASTER 0x11 -#define RADIO_2057_VCOMONITOR_VTH_L 0x12 -#define RADIO_2057_VCOMONITOR_VTH_H 0x13 -#define RADIO_2057_VCOCAL_BIASRESET_RFPLLREG_VOUT 0x14 -#define RADIO_2057_VCO_VARCSIZE_IDAC 0x15 -#define RADIO_2057_VCOCAL_COUNTVAL0 0x16 -#define RADIO_2057_VCOCAL_COUNTVAL1 0x17 -#define RADIO_2057_VCOCAL_INTCLK_COUNT 0x18 -#define RADIO_2057_VCOCAL_MASTER 0x19 -#define RADIO_2057_VCOCAL_NUMCAPCHANGE 0x1a -#define RADIO_2057_VCOCAL_WINSIZE 0x1b -#define RADIO_2057_VCOCAL_DELAY_AFTER_REFRESH 0x1c -#define RADIO_2057_VCOCAL_DELAY_AFTER_CLOSELOOP 0x1d -#define RADIO_2057_VCOCAL_DELAY_AFTER_OPENLOOP 0x1e -#define RADIO_2057_VCOCAL_DELAY_BEFORE_OPENLOOP 0x1f -#define RADIO_2057_VCO_FORCECAPEN_FORCECAP1 0x20 -#define RADIO_2057_VCO_FORCECAP0 0x21 -#define RADIO_2057_RFPLL_REFMASTER_SPAREXTALSIZE 0x22 -#define RADIO_2057_RFPLL_PFD_RESET_PW 0x23 -#define RADIO_2057_RFPLL_LOOPFILTER_R2 0x24 -#define RADIO_2057_RFPLL_LOOPFILTER_R1 0x25 -#define RADIO_2057_RFPLL_LOOPFILTER_C3 0x26 -#define RADIO_2057_RFPLL_LOOPFILTER_C2 0x27 -#define RADIO_2057_RFPLL_LOOPFILTER_C1 0x28 -#define RADIO_2057_CP_KPD_IDAC 0x29 -#define RADIO_2057_RFPLL_IDACS 0x2a -#define RADIO_2057_RFPLL_MISC_EN 0x2b -#define RADIO_2057_RFPLL_MMD0 0x2c -#define RADIO_2057_RFPLL_MMD1 0x2d -#define RADIO_2057_RFPLL_MISC_CAL_RESETN 0x2e -#define RADIO_2057_JTAGXTAL_SIZE_CPBIAS_FILTRES 0x2f -#define RADIO_2057_VCO_ALCREF_BBPLLXTAL_SIZE 0x30 -#define RADIO_2057_VCOCAL_READCAP0 0x31 -#define RADIO_2057_VCOCAL_READCAP1 0x32 -#define RADIO_2057_VCOCAL_STATUS 0x33 -#define RADIO_2057_LOGEN_PUS 0x34 -#define RADIO_2057_LOGEN_PTAT_RESETS 0x35 -#define RADIO_2057_VCOBUF_IDACS 0x36 -#define RADIO_2057_VCOBUF_TUNE 0x37 -#define RADIO_2057_CMOSBUF_TX2GQ_IDACS 0x38 -#define RADIO_2057_CMOSBUF_TX2GI_IDACS 0x39 -#define RADIO_2057_CMOSBUF_TX5GQ_IDACS 0x3a -#define RADIO_2057_CMOSBUF_TX5GI_IDACS 0x3b -#define RADIO_2057_CMOSBUF_RX2GQ_IDACS 0x3c -#define RADIO_2057_CMOSBUF_RX2GI_IDACS 0x3d -#define RADIO_2057_CMOSBUF_RX5GQ_IDACS 0x3e -#define RADIO_2057_CMOSBUF_RX5GI_IDACS 0x3f -#define RADIO_2057_LOGEN_MX2G_IDACS 0x40 -#define RADIO_2057_LOGEN_MX2G_TUNE 0x41 -#define RADIO_2057_LOGEN_MX5G_IDACS 0x42 -#define RADIO_2057_LOGEN_MX5G_TUNE 0x43 -#define RADIO_2057_LOGEN_MX5G_RCCR 0x44 -#define RADIO_2057_LOGEN_INDBUF2G_IDAC 0x45 -#define RADIO_2057_LOGEN_INDBUF2G_IBOOST 0x46 -#define RADIO_2057_LOGEN_INDBUF2G_TUNE 0x47 -#define RADIO_2057_LOGEN_INDBUF5G_IDAC 0x48 -#define RADIO_2057_LOGEN_INDBUF5G_IBOOST 0x49 -#define RADIO_2057_LOGEN_INDBUF5G_TUNE 0x4a -#define RADIO_2057_CMOSBUF_TX_RCCR 0x4b -#define RADIO_2057_CMOSBUF_RX_RCCR 0x4c -#define RADIO_2057_LOGEN_SEL_PKDET 0x4d -#define RADIO_2057_CMOSBUF_SHAREIQ_PTAT 0x4e -#define RADIO_2057_RXTXBIAS_CONFIG_CORE0 0x4f -#define RADIO_2057_TXGM_TXRF_PUS_CORE0 0x50 -#define RADIO_2057_TXGM_IDAC_BLEED_CORE0 0x51 -#define RADIO_2057_TXGM_GAIN_CORE0 0x56 -#define RADIO_2057_TXGM2G_PKDET_PUS_CORE0 0x57 -#define RADIO_2057_PAD2G_PTATS_CORE0 0x58 -#define RADIO_2057_PAD2G_IDACS_CORE0 0x59 -#define RADIO_2057_PAD2G_BOOST_PU_CORE0 0x5a -#define RADIO_2057_PAD2G_CASCV_GAIN_CORE0 0x5b -#define RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE0 0x5c -#define RADIO_2057_TXMIX2G_LODC_CORE0 0x5d -#define RADIO_2057_PAD2G_TUNE_PUS_CORE0 0x5e -#define RADIO_2057_IPA2G_GAIN_CORE0 0x5f -#define RADIO_2057_TSSI2G_SPARE1_CORE0 0x60 -#define RADIO_2057_TSSI2G_SPARE2_CORE0 0x61 -#define RADIO_2057_IPA2G_TUNEV_CASCV_PTAT_CORE0 0x62 -#define RADIO_2057_IPA2G_IMAIN_CORE0 0x63 -#define RADIO_2057_IPA2G_CASCONV_CORE0 0x64 -#define RADIO_2057_IPA2G_CASCOFFV_CORE0 0x65 -#define RADIO_2057_IPA2G_BIAS_FILTER_CORE0 0x66 -#define RADIO_2057_TX5G_PKDET_CORE0 0x69 -#define RADIO_2057_PGA_PTAT_TXGM5G_PU_CORE0 0x6a -#define RADIO_2057_PAD5G_PTATS1_CORE0 0x6b -#define RADIO_2057_PAD5G_CLASS_PTATS2_CORE0 0x6c -#define RADIO_2057_PGA_BOOSTPTAT_IMAIN_CORE0 0x6d -#define RADIO_2057_PAD5G_CASCV_IMAIN_CORE0 0x6e -#define RADIO_2057_TXMIX5G_IBOOST_PAD_IAUX_CORE0 0x6f -#define RADIO_2057_PGA_BOOST_TUNE_CORE0 0x70 -#define RADIO_2057_PGA_GAIN_CORE0 0x71 -#define RADIO_2057_PAD5G_CASCOFFV_GAIN_PUS_CORE0 0x72 -#define RADIO_2057_TXMIX5G_BOOST_TUNE_CORE0 0x73 -#define RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE0 0x74 -#define RADIO_2057_IPA5G_IAUX_CORE0 0x75 -#define RADIO_2057_IPA5G_GAIN_CORE0 0x76 -#define RADIO_2057_TSSI5G_SPARE1_CORE0 0x77 -#define RADIO_2057_TSSI5G_SPARE2_CORE0 0x78 -#define RADIO_2057_IPA5G_CASCOFFV_PU_CORE0 0x79 -#define RADIO_2057_IPA5G_PTAT_CORE0 0x7a -#define RADIO_2057_IPA5G_IMAIN_CORE0 0x7b -#define RADIO_2057_IPA5G_CASCONV_CORE0 0x7c -#define RADIO_2057_IPA5G_BIAS_FILTER_CORE0 0x7d -#define RADIO_2057_PAD_BIAS_FILTER_BWS_CORE0 0x80 -#define RADIO_2057_TR2G_CONFIG1_CORE0_NU 0x81 -#define RADIO_2057_TR2G_CONFIG2_CORE0_NU 0x82 -#define RADIO_2057_LNA5G_RFEN_CORE0 0x83 -#define RADIO_2057_TR5G_CONFIG2_CORE0_NU 0x84 -#define RADIO_2057_RXRFBIAS_IBOOST_PU_CORE0 0x85 -#define RADIO_2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE0 0x86 -#define RADIO_2057_RXGM_CMFBITAIL_AUXPTAT_CORE0 0x87 -#define RADIO_2057_RXMIX_ICORE_RXGM_IAUX_CORE0 0x88 -#define RADIO_2057_RXMIX_CMFBITAIL_PU_CORE0 0x89 -#define RADIO_2057_LNA2_IMAIN_PTAT_PU_CORE0 0x8a -#define RADIO_2057_LNA2_IAUX_PTAT_CORE0 0x8b -#define RADIO_2057_LNA1_IMAIN_PTAT_PU_CORE0 0x8c -#define RADIO_2057_LNA15G_INPUT_MATCH_TUNE_CORE0 0x8d -#define RADIO_2057_RXRFBIAS_BANDSEL_CORE0 0x8e -#define RADIO_2057_TIA_CONFIG_CORE0 0x8f -#define RADIO_2057_TIA_IQGAIN_CORE0 0x90 -#define RADIO_2057_TIA_IBIAS2_CORE0 0x91 -#define RADIO_2057_TIA_IBIAS1_CORE0 0x92 -#define RADIO_2057_TIA_SPARE_Q_CORE0 0x93 -#define RADIO_2057_TIA_SPARE_I_CORE0 0x94 -#define RADIO_2057_RXMIX2G_PUS_CORE0 0x95 -#define RADIO_2057_RXMIX2G_VCMREFS_CORE0 0x96 -#define RADIO_2057_RXMIX2G_LODC_QI_CORE0 0x97 -#define RADIO_2057_W12G_BW_LNA2G_PUS_CORE0 0x98 -#define RADIO_2057_LNA2G_GAIN_CORE0 0x99 -#define RADIO_2057_LNA2G_TUNE_CORE0 0x9a -#define RADIO_2057_RXMIX5G_PUS_CORE0 0x9b -#define RADIO_2057_RXMIX5G_VCMREFS_CORE0 0x9c -#define RADIO_2057_RXMIX5G_LODC_QI_CORE0 0x9d -#define RADIO_2057_W15G_BW_LNA5G_PUS_CORE0 0x9e -#define RADIO_2057_LNA5G_GAIN_CORE0 0x9f -#define RADIO_2057_LNA5G_TUNE_CORE0 0xa0 -#define RADIO_2057_LPFSEL_TXRX_RXBB_PUS_CORE0 0xa1 -#define RADIO_2057_RXBB_BIAS_MASTER_CORE0 0xa2 -#define RADIO_2057_RXBB_VGABUF_IDACS_CORE0 0xa3 -#define RADIO_2057_LPF_VCMREF_TXBUF_VCMREF_CORE0 0xa4 -#define RADIO_2057_TXBUF_VINCM_CORE0 0xa5 -#define RADIO_2057_TXBUF_IDACS_CORE0 0xa6 -#define RADIO_2057_LPF_RESP_RXBUF_BW_CORE0 0xa7 -#define RADIO_2057_RXBB_CC_CORE0 0xa8 -#define RADIO_2057_RXBB_SPARE3_CORE0 0xa9 -#define RADIO_2057_RXBB_RCCAL_HPC_CORE0 0xaa -#define RADIO_2057_LPF_IDACS_CORE0 0xab -#define RADIO_2057_LPFBYP_DCLOOP_BYP_IDAC_CORE0 0xac -#define RADIO_2057_TXBUF_GAIN_CORE0 0xad -#define RADIO_2057_AFELOOPBACK_AACI_RESP_CORE0 0xae -#define RADIO_2057_RXBUF_DEGEN_CORE0 0xaf -#define RADIO_2057_RXBB_SPARE2_CORE0 0xb0 -#define RADIO_2057_RXBB_SPARE1_CORE0 0xb1 -#define RADIO_2057_RSSI_MASTER_CORE0 0xb2 -#define RADIO_2057_W2_MASTER_CORE0 0xb3 -#define RADIO_2057_NB_MASTER_CORE0 0xb4 -#define RADIO_2057_W2_IDACS0_Q_CORE0 0xb5 -#define RADIO_2057_W2_IDACS1_Q_CORE0 0xb6 -#define RADIO_2057_W2_IDACS0_I_CORE0 0xb7 -#define RADIO_2057_W2_IDACS1_I_CORE0 0xb8 -#define RADIO_2057_RSSI_GPAIOSEL_W1_IDACS_CORE0 0xb9 -#define RADIO_2057_NB_IDACS_Q_CORE0 0xba -#define RADIO_2057_NB_IDACS_I_CORE0 0xbb -#define RADIO_2057_BACKUP4_CORE0 0xc1 -#define RADIO_2057_BACKUP3_CORE0 0xc2 -#define RADIO_2057_BACKUP2_CORE0 0xc3 -#define RADIO_2057_BACKUP1_CORE0 0xc4 -#define RADIO_2057_SPARE16_CORE0 0xc5 -#define RADIO_2057_SPARE15_CORE0 0xc6 -#define RADIO_2057_SPARE14_CORE0 0xc7 -#define RADIO_2057_SPARE13_CORE0 0xc8 -#define RADIO_2057_SPARE12_CORE0 0xc9 -#define RADIO_2057_SPARE11_CORE0 0xca -#define RADIO_2057_TX2G_BIAS_RESETS_CORE0 0xcb -#define RADIO_2057_TX5G_BIAS_RESETS_CORE0 0xcc -#define RADIO_2057_IQTEST_SEL_PU 0xcd -#define RADIO_2057_XTAL_CONFIG2 0xce -#define RADIO_2057_BUFS_MISC_LPFBW_CORE0 0xcf -#define RADIO_2057_TXLPF_RCCAL_CORE0 0xd0 -#define RADIO_2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE0 0xd1 -#define RADIO_2057_LPF_GAIN_CORE0 0xd2 -#define RADIO_2057_DACBUF_IDACS_BW_CORE0 0xd3 -#define RADIO_2057_RXTXBIAS_CONFIG_CORE1 0xd4 -#define RADIO_2057_TXGM_TXRF_PUS_CORE1 0xd5 -#define RADIO_2057_TXGM_IDAC_BLEED_CORE1 0xd6 -#define RADIO_2057_TXGM_GAIN_CORE1 0xdb -#define RADIO_2057_TXGM2G_PKDET_PUS_CORE1 0xdc -#define RADIO_2057_PAD2G_PTATS_CORE1 0xdd -#define RADIO_2057_PAD2G_IDACS_CORE1 0xde -#define RADIO_2057_PAD2G_BOOST_PU_CORE1 0xdf -#define RADIO_2057_PAD2G_CASCV_GAIN_CORE1 0xe0 -#define RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE1 0xe1 -#define RADIO_2057_TXMIX2G_LODC_CORE1 0xe2 -#define RADIO_2057_PAD2G_TUNE_PUS_CORE1 0xe3 -#define RADIO_2057_IPA2G_GAIN_CORE1 0xe4 -#define RADIO_2057_TSSI2G_SPARE1_CORE1 0xe5 -#define RADIO_2057_TSSI2G_SPARE2_CORE1 0xe6 -#define RADIO_2057_IPA2G_TUNEV_CASCV_PTAT_CORE1 0xe7 -#define RADIO_2057_IPA2G_IMAIN_CORE1 0xe8 -#define RADIO_2057_IPA2G_CASCONV_CORE1 0xe9 -#define RADIO_2057_IPA2G_CASCOFFV_CORE1 0xea -#define RADIO_2057_IPA2G_BIAS_FILTER_CORE1 0xeb -#define RADIO_2057_TX5G_PKDET_CORE1 0xee -#define RADIO_2057_PGA_PTAT_TXGM5G_PU_CORE1 0xef -#define RADIO_2057_PAD5G_PTATS1_CORE1 0xf0 -#define RADIO_2057_PAD5G_CLASS_PTATS2_CORE1 0xf1 -#define RADIO_2057_PGA_BOOSTPTAT_IMAIN_CORE1 0xf2 -#define RADIO_2057_PAD5G_CASCV_IMAIN_CORE1 0xf3 -#define RADIO_2057_TXMIX5G_IBOOST_PAD_IAUX_CORE1 0xf4 -#define RADIO_2057_PGA_BOOST_TUNE_CORE1 0xf5 -#define RADIO_2057_PGA_GAIN_CORE1 0xf6 -#define RADIO_2057_PAD5G_CASCOFFV_GAIN_PUS_CORE1 0xf7 -#define RADIO_2057_TXMIX5G_BOOST_TUNE_CORE1 0xf8 -#define RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE1 0xf9 -#define RADIO_2057_IPA5G_IAUX_CORE1 0xfa -#define RADIO_2057_IPA5G_GAIN_CORE1 0xfb -#define RADIO_2057_TSSI5G_SPARE1_CORE1 0xfc -#define RADIO_2057_TSSI5G_SPARE2_CORE1 0xfd -#define RADIO_2057_IPA5G_CASCOFFV_PU_CORE1 0xfe -#define RADIO_2057_IPA5G_PTAT_CORE1 0xff -#define RADIO_2057_IPA5G_IMAIN_CORE1 0x100 -#define RADIO_2057_IPA5G_CASCONV_CORE1 0x101 -#define RADIO_2057_IPA5G_BIAS_FILTER_CORE1 0x102 -#define RADIO_2057_PAD_BIAS_FILTER_BWS_CORE1 0x105 -#define RADIO_2057_TR2G_CONFIG1_CORE1_NU 0x106 -#define RADIO_2057_TR2G_CONFIG2_CORE1_NU 0x107 -#define RADIO_2057_LNA5G_RFEN_CORE1 0x108 -#define RADIO_2057_TR5G_CONFIG2_CORE1_NU 0x109 -#define RADIO_2057_RXRFBIAS_IBOOST_PU_CORE1 0x10a -#define RADIO_2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE1 0x10b -#define RADIO_2057_RXGM_CMFBITAIL_AUXPTAT_CORE1 0x10c -#define RADIO_2057_RXMIX_ICORE_RXGM_IAUX_CORE1 0x10d -#define RADIO_2057_RXMIX_CMFBITAIL_PU_CORE1 0x10e -#define RADIO_2057_LNA2_IMAIN_PTAT_PU_CORE1 0x10f -#define RADIO_2057_LNA2_IAUX_PTAT_CORE1 0x110 -#define RADIO_2057_LNA1_IMAIN_PTAT_PU_CORE1 0x111 -#define RADIO_2057_LNA15G_INPUT_MATCH_TUNE_CORE1 0x112 -#define RADIO_2057_RXRFBIAS_BANDSEL_CORE1 0x113 -#define RADIO_2057_TIA_CONFIG_CORE1 0x114 -#define RADIO_2057_TIA_IQGAIN_CORE1 0x115 -#define RADIO_2057_TIA_IBIAS2_CORE1 0x116 -#define RADIO_2057_TIA_IBIAS1_CORE1 0x117 -#define RADIO_2057_TIA_SPARE_Q_CORE1 0x118 -#define RADIO_2057_TIA_SPARE_I_CORE1 0x119 -#define RADIO_2057_RXMIX2G_PUS_CORE1 0x11a -#define RADIO_2057_RXMIX2G_VCMREFS_CORE1 0x11b -#define RADIO_2057_RXMIX2G_LODC_QI_CORE1 0x11c -#define RADIO_2057_W12G_BW_LNA2G_PUS_CORE1 0x11d -#define RADIO_2057_LNA2G_GAIN_CORE1 0x11e -#define RADIO_2057_LNA2G_TUNE_CORE1 0x11f -#define RADIO_2057_RXMIX5G_PUS_CORE1 0x120 -#define RADIO_2057_RXMIX5G_VCMREFS_CORE1 0x121 -#define RADIO_2057_RXMIX5G_LODC_QI_CORE1 0x122 -#define RADIO_2057_W15G_BW_LNA5G_PUS_CORE1 0x123 -#define RADIO_2057_LNA5G_GAIN_CORE1 0x124 -#define RADIO_2057_LNA5G_TUNE_CORE1 0x125 -#define RADIO_2057_LPFSEL_TXRX_RXBB_PUS_CORE1 0x126 -#define RADIO_2057_RXBB_BIAS_MASTER_CORE1 0x127 -#define RADIO_2057_RXBB_VGABUF_IDACS_CORE1 0x128 -#define RADIO_2057_LPF_VCMREF_TXBUF_VCMREF_CORE1 0x129 -#define RADIO_2057_TXBUF_VINCM_CORE1 0x12a -#define RADIO_2057_TXBUF_IDACS_CORE1 0x12b -#define RADIO_2057_LPF_RESP_RXBUF_BW_CORE1 0x12c -#define RADIO_2057_RXBB_CC_CORE1 0x12d -#define RADIO_2057_RXBB_SPARE3_CORE1 0x12e -#define RADIO_2057_RXBB_RCCAL_HPC_CORE1 0x12f -#define RADIO_2057_LPF_IDACS_CORE1 0x130 -#define RADIO_2057_LPFBYP_DCLOOP_BYP_IDAC_CORE1 0x131 -#define RADIO_2057_TXBUF_GAIN_CORE1 0x132 -#define RADIO_2057_AFELOOPBACK_AACI_RESP_CORE1 0x133 -#define RADIO_2057_RXBUF_DEGEN_CORE1 0x134 -#define RADIO_2057_RXBB_SPARE2_CORE1 0x135 -#define RADIO_2057_RXBB_SPARE1_CORE1 0x136 -#define RADIO_2057_RSSI_MASTER_CORE1 0x137 -#define RADIO_2057_W2_MASTER_CORE1 0x138 -#define RADIO_2057_NB_MASTER_CORE1 0x139 -#define RADIO_2057_W2_IDACS0_Q_CORE1 0x13a -#define RADIO_2057_W2_IDACS1_Q_CORE1 0x13b -#define RADIO_2057_W2_IDACS0_I_CORE1 0x13c -#define RADIO_2057_W2_IDACS1_I_CORE1 0x13d -#define RADIO_2057_RSSI_GPAIOSEL_W1_IDACS_CORE1 0x13e -#define RADIO_2057_NB_IDACS_Q_CORE1 0x13f -#define RADIO_2057_NB_IDACS_I_CORE1 0x140 -#define RADIO_2057_BACKUP4_CORE1 0x146 -#define RADIO_2057_BACKUP3_CORE1 0x147 -#define RADIO_2057_BACKUP2_CORE1 0x148 -#define RADIO_2057_BACKUP1_CORE1 0x149 -#define RADIO_2057_SPARE16_CORE1 0x14a -#define RADIO_2057_SPARE15_CORE1 0x14b -#define RADIO_2057_SPARE14_CORE1 0x14c -#define RADIO_2057_SPARE13_CORE1 0x14d -#define RADIO_2057_SPARE12_CORE1 0x14e -#define RADIO_2057_SPARE11_CORE1 0x14f -#define RADIO_2057_TX2G_BIAS_RESETS_CORE1 0x150 -#define RADIO_2057_TX5G_BIAS_RESETS_CORE1 0x151 -#define RADIO_2057_SPARE8_CORE1 0x152 -#define RADIO_2057_SPARE7_CORE1 0x153 -#define RADIO_2057_BUFS_MISC_LPFBW_CORE1 0x154 -#define RADIO_2057_TXLPF_RCCAL_CORE1 0x155 -#define RADIO_2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE1 0x156 -#define RADIO_2057_LPF_GAIN_CORE1 0x157 -#define RADIO_2057_DACBUF_IDACS_BW_CORE1 0x158 -#define RADIO_2057_DACBUF_VINCM_CORE1 0x159 -#define RADIO_2057_RCCAL_START_R1_Q1_P1 0x15a -#define RADIO_2057_RCCAL_X1 0x15b -#define RADIO_2057_RCCAL_TRC0 0x15c -#define RADIO_2057_RCCAL_TRC1 0x15d -#define RADIO_2057_RCCAL_DONE_OSCCAP 0x15e -#define RADIO_2057_RCCAL_N0_0 0x15f -#define RADIO_2057_RCCAL_N0_1 0x160 -#define RADIO_2057_RCCAL_N1_0 0x161 -#define RADIO_2057_RCCAL_N1_1 0x162 -#define RADIO_2057_RCAL_STATUS 0x163 -#define RADIO_2057_XTALPUOVR_PINCTRL 0x164 -#define RADIO_2057_OVR_REG0 0x165 -#define RADIO_2057_OVR_REG1 0x166 -#define RADIO_2057_OVR_REG2 0x167 -#define RADIO_2057_OVR_REG3 0x168 -#define RADIO_2057_OVR_REG4 0x169 -#define RADIO_2057_RCCAL_SCAP_VAL 0x16a -#define RADIO_2057_RCCAL_BCAP_VAL 0x16b -#define RADIO_2057_RCCAL_HPC_VAL 0x16c -#define RADIO_2057_RCCAL_OVERRIDES 0x16d -#define RADIO_2057_TX0_IQCAL_GAIN_BW 0x170 -#define RADIO_2057_TX0_LOFT_FINE_I 0x171 -#define RADIO_2057_TX0_LOFT_FINE_Q 0x172 -#define RADIO_2057_TX0_LOFT_COARSE_I 0x173 -#define RADIO_2057_TX0_LOFT_COARSE_Q 0x174 -#define RADIO_2057_TX0_TX_SSI_MASTER 0x175 -#define RADIO_2057_TX0_IQCAL_VCM_HG 0x176 -#define RADIO_2057_TX0_IQCAL_IDAC 0x177 -#define RADIO_2057_TX0_TSSI_VCM 0x178 -#define RADIO_2057_TX0_TX_SSI_MUX 0x179 -#define RADIO_2057_TX0_TSSIA 0x17a -#define RADIO_2057_TX0_TSSIG 0x17b -#define RADIO_2057_TX0_TSSI_MISC1 0x17c -#define RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN 0x17d -#define RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP 0x17e -#define RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN 0x17f -#define RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP 0x180 -#define RADIO_2057_TX1_IQCAL_GAIN_BW 0x190 -#define RADIO_2057_TX1_LOFT_FINE_I 0x191 -#define RADIO_2057_TX1_LOFT_FINE_Q 0x192 -#define RADIO_2057_TX1_LOFT_COARSE_I 0x193 -#define RADIO_2057_TX1_LOFT_COARSE_Q 0x194 -#define RADIO_2057_TX1_TX_SSI_MASTER 0x195 -#define RADIO_2057_TX1_IQCAL_VCM_HG 0x196 -#define RADIO_2057_TX1_IQCAL_IDAC 0x197 -#define RADIO_2057_TX1_TSSI_VCM 0x198 -#define RADIO_2057_TX1_TX_SSI_MUX 0x199 -#define RADIO_2057_TX1_TSSIA 0x19a -#define RADIO_2057_TX1_TSSIG 0x19b -#define RADIO_2057_TX1_TSSI_MISC1 0x19c -#define RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN 0x19d -#define RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP 0x19e -#define RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN 0x19f -#define RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP 0x1a0 -#define RADIO_2057_AFE_VCM_CAL_MASTER_CORE0 0x1a1 -#define RADIO_2057_AFE_SET_VCM_I_CORE0 0x1a2 -#define RADIO_2057_AFE_SET_VCM_Q_CORE0 0x1a3 -#define RADIO_2057_AFE_STATUS_VCM_IQADC_CORE0 0x1a4 -#define RADIO_2057_AFE_STATUS_VCM_I_CORE0 0x1a5 -#define RADIO_2057_AFE_STATUS_VCM_Q_CORE0 0x1a6 -#define RADIO_2057_AFE_VCM_CAL_MASTER_CORE1 0x1a7 -#define RADIO_2057_AFE_SET_VCM_I_CORE1 0x1a8 -#define RADIO_2057_AFE_SET_VCM_Q_CORE1 0x1a9 -#define RADIO_2057_AFE_STATUS_VCM_IQADC_CORE1 0x1aa -#define RADIO_2057_AFE_STATUS_VCM_I_CORE1 0x1ab -#define RADIO_2057_AFE_STATUS_VCM_Q_CORE1 0x1ac - -#define RADIO_2057v7_DACBUF_VINCM_CORE0 0x1ad -#define RADIO_2057v7_RCCAL_MASTER 0x1ae -#define RADIO_2057v7_TR2G_CONFIG3_CORE0_NU 0x1af -#define RADIO_2057v7_TR2G_CONFIG3_CORE1_NU 0x1b0 -#define RADIO_2057v7_LOGEN_PUS1 0x1b1 -#define RADIO_2057v7_OVR_REG5 0x1b2 -#define RADIO_2057v7_OVR_REG6 0x1b3 -#define RADIO_2057v7_OVR_REG7 0x1b4 -#define RADIO_2057v7_OVR_REG8 0x1b5 -#define RADIO_2057v7_OVR_REG9 0x1b6 -#define RADIO_2057v7_OVR_REG10 0x1b7 -#define RADIO_2057v7_OVR_REG11 0x1b8 -#define RADIO_2057v7_OVR_REG12 0x1b9 -#define RADIO_2057v7_OVR_REG13 0x1ba -#define RADIO_2057v7_OVR_REG14 0x1bb -#define RADIO_2057v7_OVR_REG15 0x1bc -#define RADIO_2057v7_OVR_REG16 0x1bd -#define RADIO_2057v7_OVR_REG1 0x1be -#define RADIO_2057v7_OVR_REG18 0x1bf -#define RADIO_2057v7_OVR_REG19 0x1c0 -#define RADIO_2057v7_OVR_REG20 0x1c1 -#define RADIO_2057v7_OVR_REG21 0x1c2 -#define RADIO_2057v7_OVR_REG2 0x1c3 -#define RADIO_2057v7_OVR_REG23 0x1c4 -#define RADIO_2057v7_OVR_REG24 0x1c5 -#define RADIO_2057v7_OVR_REG25 0x1c6 -#define RADIO_2057v7_OVR_REG26 0x1c7 -#define RADIO_2057v7_OVR_REG27 0x1c8 -#define RADIO_2057v7_OVR_REG28 0x1c9 -#define RADIO_2057v7_IQTEST_SEL_PU2 0x1ca - -#define RADIO_2057_VCM_MASK 0x7 - -#endif /* _BRCM_PHY_RADIO_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h deleted file mode 100644 index a97c3a799479..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2010 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. - */ - -#define NPHY_TBL_ID_GAIN1 0 -#define NPHY_TBL_ID_GAIN2 1 -#define NPHY_TBL_ID_GAINBITS1 2 -#define NPHY_TBL_ID_GAINBITS2 3 -#define NPHY_TBL_ID_GAINLIMIT 4 -#define NPHY_TBL_ID_WRSSIGainLimit 5 -#define NPHY_TBL_ID_RFSEQ 7 -#define NPHY_TBL_ID_AFECTRL 8 -#define NPHY_TBL_ID_ANTSWCTRLLUT 9 -#define NPHY_TBL_ID_IQLOCAL 15 -#define NPHY_TBL_ID_NOISEVAR 16 -#define NPHY_TBL_ID_SAMPLEPLAY 17 -#define NPHY_TBL_ID_CORE1TXPWRCTL 26 -#define NPHY_TBL_ID_CORE2TXPWRCTL 27 -#define NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL 30 - -#define NPHY_TBL_ID_EPSILONTBL0 31 -#define NPHY_TBL_ID_SCALARTBL0 32 -#define NPHY_TBL_ID_EPSILONTBL1 33 -#define NPHY_TBL_ID_SCALARTBL1 34 - -#define NPHY_TO_BPHY_OFF 0xc00 - -#define NPHY_BandControl_currentBand 0x0001 -#define RFCC_CHIP0_PU 0x0400 -#define RFCC_POR_FORCE 0x0040 -#define RFCC_OE_POR_FORCE 0x0080 -#define NPHY_RfctrlIntc_override_OFF 0 -#define NPHY_RfctrlIntc_override_TRSW 1 -#define NPHY_RfctrlIntc_override_PA 2 -#define NPHY_RfctrlIntc_override_EXT_LNA_PU 3 -#define NPHY_RfctrlIntc_override_EXT_LNA_GAIN 4 -#define RIFS_ENABLE 0x80 -#define BPHY_BAND_SEL_UP20 0x10 -#define NPHY_MLenable 0x02 - -#define NPHY_RfseqMode_CoreActv_override 0x0001 -#define NPHY_RfseqMode_Trigger_override 0x0002 -#define NPHY_RfseqCoreActv_TxRxChain0 (0x11) -#define NPHY_RfseqCoreActv_TxRxChain1 (0x22) - -#define NPHY_RfseqTrigger_rx2tx 0x0001 -#define NPHY_RfseqTrigger_tx2rx 0x0002 -#define NPHY_RfseqTrigger_updategainh 0x0004 -#define NPHY_RfseqTrigger_updategainl 0x0008 -#define NPHY_RfseqTrigger_updategainu 0x0010 -#define NPHY_RfseqTrigger_reset2rx 0x0020 -#define NPHY_RfseqStatus_rx2tx 0x0001 -#define NPHY_RfseqStatus_tx2rx 0x0002 -#define NPHY_RfseqStatus_updategainh 0x0004 -#define NPHY_RfseqStatus_updategainl 0x0008 -#define NPHY_RfseqStatus_updategainu 0x0010 -#define NPHY_RfseqStatus_reset2rx 0x0020 -#define NPHY_ClassifierCtrl_cck_en 0x1 -#define NPHY_ClassifierCtrl_ofdm_en 0x2 -#define NPHY_ClassifierCtrl_waited_en 0x4 -#define NPHY_IQFlip_ADC1 0x0001 -#define NPHY_IQFlip_ADC2 0x0010 -#define NPHY_sampleCmd_STOP 0x0002 - -#define RX_GF_OR_MM 0x0004 -#define RX_GF_MM_AUTO 0x0100 - -#define NPHY_iqloCalCmdGctl_IQLO_CAL_EN 0x8000 - -#define NPHY_IqestCmd_iqstart 0x1 -#define NPHY_IqestCmd_iqMode 0x2 - -#define NPHY_TxPwrCtrlCmd_pwrIndex_init 0x40 -#define NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 0x19 - -#define PRIM_SEL_UP20 0x8000 - -#define NPHY_RFSEQ_RX2TX 0x0 -#define NPHY_RFSEQ_TX2RX 0x1 -#define NPHY_RFSEQ_RESET2RX 0x2 -#define NPHY_RFSEQ_UPDATEGAINH 0x3 -#define NPHY_RFSEQ_UPDATEGAINL 0x4 -#define NPHY_RFSEQ_UPDATEGAINU 0x5 - -#define NPHY_RFSEQ_CMD_NOP 0x0 -#define NPHY_RFSEQ_CMD_RXG_FBW 0x1 -#define NPHY_RFSEQ_CMD_TR_SWITCH 0x2 -#define NPHY_RFSEQ_CMD_EXT_PA 0x3 -#define NPHY_RFSEQ_CMD_RXPD_TXPD 0x4 -#define NPHY_RFSEQ_CMD_TX_GAIN 0x5 -#define NPHY_RFSEQ_CMD_RX_GAIN 0x6 -#define NPHY_RFSEQ_CMD_SET_HPF_BW 0x7 -#define NPHY_RFSEQ_CMD_CLR_HIQ_DIS 0x8 -#define NPHY_RFSEQ_CMD_END 0xf - -#define NPHY_REV3_RFSEQ_CMD_NOP 0x0 -#define NPHY_REV3_RFSEQ_CMD_RXG_FBW 0x1 -#define NPHY_REV3_RFSEQ_CMD_TR_SWITCH 0x2 -#define NPHY_REV3_RFSEQ_CMD_INT_PA_PU 0x3 -#define NPHY_REV3_RFSEQ_CMD_EXT_PA 0x4 -#define NPHY_REV3_RFSEQ_CMD_RXPD_TXPD 0x5 -#define NPHY_REV3_RFSEQ_CMD_TX_GAIN 0x6 -#define NPHY_REV3_RFSEQ_CMD_RX_GAIN 0x7 -#define NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS 0x8 -#define NPHY_REV3_RFSEQ_CMD_SET_HPF_H_HPC 0x9 -#define NPHY_REV3_RFSEQ_CMD_SET_LPF_H_HPC 0xa -#define NPHY_REV3_RFSEQ_CMD_SET_HPF_M_HPC 0xb -#define NPHY_REV3_RFSEQ_CMD_SET_LPF_M_HPC 0xc -#define NPHY_REV3_RFSEQ_CMD_SET_HPF_L_HPC 0xd -#define NPHY_REV3_RFSEQ_CMD_SET_LPF_L_HPC 0xe -#define NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS 0xf -#define NPHY_REV3_RFSEQ_CMD_END 0x1f - -#define NPHY_RSSI_SEL_W1 0x0 -#define NPHY_RSSI_SEL_W2 0x1 -#define NPHY_RSSI_SEL_NB 0x2 -#define NPHY_RSSI_SEL_IQ 0x3 -#define NPHY_RSSI_SEL_TSSI_2G 0x4 -#define NPHY_RSSI_SEL_TSSI_5G 0x5 -#define NPHY_RSSI_SEL_TBD 0x6 - -#define NPHY_RAIL_I 0x0 -#define NPHY_RAIL_Q 0x1 - -#define NPHY_FORCESIG_DECODEGATEDCLKS 0x8 - -#define NPHY_REV7_RfctrlOverride_cmd_rxrf_pu 0x0 -#define NPHY_REV7_RfctrlOverride_cmd_rx_pu 0x1 -#define NPHY_REV7_RfctrlOverride_cmd_tx_pu 0x2 -#define NPHY_REV7_RfctrlOverride_cmd_rxgain 0x3 -#define NPHY_REV7_RfctrlOverride_cmd_txgain 0x4 - -#define NPHY_REV7_RXGAINCODE_RFMXGAIN_MASK 0x000ff -#define NPHY_REV7_RXGAINCODE_LPFGAIN_MASK 0x0ff00 -#define NPHY_REV7_RXGAINCODE_DVGAGAIN_MASK 0xf0000 - -#define NPHY_REV7_TXGAINCODE_TGAIN_MASK 0x7fff -#define NPHY_REV7_TXGAINCODE_LPFGAIN_MASK 0x8000 -#define NPHY_REV7_TXGAINCODE_BIQ0GAIN_SHIFT 14 - -#define NPHY_REV7_RFCTRLOVERRIDE_ID0 0x0 -#define NPHY_REV7_RFCTRLOVERRIDE_ID1 0x1 -#define NPHY_REV7_RFCTRLOVERRIDE_ID2 0x2 - -#define NPHY_IqestIqAccLo(core) ((core == 0) ? 0x12c : 0x134) - -#define NPHY_IqestIqAccHi(core) ((core == 0) ? 0x12d : 0x135) - -#define NPHY_IqestipwrAccLo(core) ((core == 0) ? 0x12e : 0x136) - -#define NPHY_IqestipwrAccHi(core) ((core == 0) ? 0x12f : 0x137) - -#define NPHY_IqestqpwrAccLo(core) ((core == 0) ? 0x130 : 0x138) - -#define NPHY_IqestqpwrAccHi(core) ((core == 0) ? 0x131 : 0x139) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c deleted file mode 100644 index d7fa312214f3..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c +++ /dev/null @@ -1,3293 +0,0 @@ -/* - * Copyright (c) 2010 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 "phytbl_lcn.h" - -static const u32 dot11lcn_gain_tbl_rev0[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000004, - 0x00000000, - 0x00000004, - 0x00000008, - 0x00000001, - 0x00000005, - 0x00000009, - 0x0000000d, - 0x0000004d, - 0x0000008d, - 0x0000000d, - 0x0000004d, - 0x0000008d, - 0x000000cd, - 0x0000004f, - 0x0000008f, - 0x000000cf, - 0x000000d3, - 0x00000113, - 0x00000513, - 0x00000913, - 0x00000953, - 0x00000d53, - 0x00001153, - 0x00001193, - 0x00005193, - 0x00009193, - 0x0000d193, - 0x00011193, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000004, - 0x00000000, - 0x00000004, - 0x00000008, - 0x00000001, - 0x00000005, - 0x00000009, - 0x0000000d, - 0x0000004d, - 0x0000008d, - 0x0000000d, - 0x0000004d, - 0x0000008d, - 0x000000cd, - 0x0000004f, - 0x0000008f, - 0x000000cf, - 0x000000d3, - 0x00000113, - 0x00000513, - 0x00000913, - 0x00000953, - 0x00000d53, - 0x00001153, - 0x00005153, - 0x00009153, - 0x0000d153, - 0x00011153, - 0x00015153, - 0x00019153, - 0x0001d153, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 dot11lcn_gain_tbl_rev1[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000008, - 0x00000004, - 0x00000008, - 0x00000001, - 0x00000005, - 0x00000009, - 0x0000000D, - 0x00000011, - 0x00000051, - 0x00000091, - 0x00000011, - 0x00000051, - 0x00000091, - 0x000000d1, - 0x00000053, - 0x00000093, - 0x000000d3, - 0x000000d7, - 0x00000117, - 0x00000517, - 0x00000917, - 0x00000957, - 0x00000d57, - 0x00001157, - 0x00001197, - 0x00005197, - 0x00009197, - 0x0000d197, - 0x00011197, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000008, - 0x00000004, - 0x00000008, - 0x00000001, - 0x00000005, - 0x00000009, - 0x0000000D, - 0x00000011, - 0x00000051, - 0x00000091, - 0x00000011, - 0x00000051, - 0x00000091, - 0x000000d1, - 0x00000053, - 0x00000093, - 0x000000d3, - 0x000000d7, - 0x00000117, - 0x00000517, - 0x00000917, - 0x00000957, - 0x00000d57, - 0x00001157, - 0x00005157, - 0x00009157, - 0x0000d157, - 0x00011157, - 0x00015157, - 0x00019157, - 0x0001d157, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u16 dot11lcn_aux_gain_idx_tbl_rev0[] = { - 0x0401, - 0x0402, - 0x0403, - 0x0404, - 0x0405, - 0x0406, - 0x0407, - 0x0408, - 0x0409, - 0x040a, - 0x058b, - 0x058c, - 0x058d, - 0x058e, - 0x058f, - 0x0090, - 0x0091, - 0x0092, - 0x0193, - 0x0194, - 0x0195, - 0x0196, - 0x0197, - 0x0198, - 0x0199, - 0x019a, - 0x019b, - 0x019c, - 0x019d, - 0x019e, - 0x019f, - 0x01a0, - 0x01a1, - 0x01a2, - 0x01a3, - 0x01a4, - 0x01a5, - 0x0000, -}; - -static const u32 dot11lcn_gain_idx_tbl_rev0[] = { - 0x00000000, - 0x00000000, - 0x10000000, - 0x00000000, - 0x20000000, - 0x00000000, - 0x30000000, - 0x00000000, - 0x40000000, - 0x00000000, - 0x50000000, - 0x00000000, - 0x60000000, - 0x00000000, - 0x70000000, - 0x00000000, - 0x80000000, - 0x00000000, - 0x90000000, - 0x00000008, - 0xa0000000, - 0x00000008, - 0xb0000000, - 0x00000008, - 0xc0000000, - 0x00000008, - 0xd0000000, - 0x00000008, - 0xe0000000, - 0x00000008, - 0xf0000000, - 0x00000008, - 0x00000000, - 0x00000009, - 0x10000000, - 0x00000009, - 0x20000000, - 0x00000019, - 0x30000000, - 0x00000019, - 0x40000000, - 0x00000019, - 0x50000000, - 0x00000019, - 0x60000000, - 0x00000019, - 0x70000000, - 0x00000019, - 0x80000000, - 0x00000019, - 0x90000000, - 0x00000019, - 0xa0000000, - 0x00000019, - 0xb0000000, - 0x00000019, - 0xc0000000, - 0x00000019, - 0xd0000000, - 0x00000019, - 0xe0000000, - 0x00000019, - 0xf0000000, - 0x00000019, - 0x00000000, - 0x0000001a, - 0x10000000, - 0x0000001a, - 0x20000000, - 0x0000001a, - 0x30000000, - 0x0000001a, - 0x40000000, - 0x0000001a, - 0x50000000, - 0x00000002, - 0x60000000, - 0x00000002, - 0x70000000, - 0x00000002, - 0x80000000, - 0x00000002, - 0x90000000, - 0x00000002, - 0xa0000000, - 0x00000002, - 0xb0000000, - 0x00000002, - 0xc0000000, - 0x0000000a, - 0xd0000000, - 0x0000000a, - 0xe0000000, - 0x0000000a, - 0xf0000000, - 0x0000000a, - 0x00000000, - 0x0000000b, - 0x10000000, - 0x0000000b, - 0x20000000, - 0x0000000b, - 0x30000000, - 0x0000000b, - 0x40000000, - 0x0000000b, - 0x50000000, - 0x0000001b, - 0x60000000, - 0x0000001b, - 0x70000000, - 0x0000001b, - 0x80000000, - 0x0000001b, - 0x90000000, - 0x0000001b, - 0xa0000000, - 0x0000001b, - 0xb0000000, - 0x0000001b, - 0xc0000000, - 0x0000001b, - 0xd0000000, - 0x0000001b, - 0xe0000000, - 0x0000001b, - 0xf0000000, - 0x0000001b, - 0x00000000, - 0x0000001c, - 0x10000000, - 0x0000001c, - 0x20000000, - 0x0000001c, - 0x30000000, - 0x0000001c, - 0x40000000, - 0x0000001c, - 0x50000000, - 0x0000001c, - 0x60000000, - 0x0000001c, - 0x70000000, - 0x0000001c, - 0x80000000, - 0x0000001c, - 0x90000000, - 0x0000001c, -}; - -static const u16 dot11lcn_aux_gain_idx_tbl_2G[] = { - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0001, - 0x0080, - 0x0081, - 0x0100, - 0x0101, - 0x0180, - 0x0181, - 0x0182, - 0x0183, - 0x0184, - 0x0185, - 0x0186, - 0x0187, - 0x0188, - 0x0285, - 0x0289, - 0x028a, - 0x028b, - 0x028c, - 0x028d, - 0x028e, - 0x028f, - 0x0290, - 0x0291, - 0x0292, - 0x0293, - 0x0294, - 0x0295, - 0x0296, - 0x0297, - 0x0298, - 0x0299, - 0x029a, - 0x0000 -}; - -static const u8 dot11lcn_gain_val_tbl_2G[] = { - 0xfc, - 0x02, - 0x08, - 0x0e, - 0x13, - 0x1b, - 0xfc, - 0x02, - 0x08, - 0x0e, - 0x13, - 0x1b, - 0xfc, - 0x00, - 0x0c, - 0x03, - 0xeb, - 0xfe, - 0x07, - 0x0b, - 0x0f, - 0xfb, - 0xfe, - 0x01, - 0x05, - 0x08, - 0x0b, - 0x0e, - 0x11, - 0x14, - 0x17, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x03, - 0x06, - 0x09, - 0x0c, - 0x0f, - 0x12, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x03, - 0x06, - 0x09, - 0x0c, - 0x0f, - 0x12, - 0x15, - 0x18, - 0x1b, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00 -}; - -static const u32 dot11lcn_gain_idx_tbl_2G[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x10000000, - 0x00000000, - 0x00000000, - 0x00000008, - 0x10000000, - 0x00000008, - 0x00000000, - 0x00000010, - 0x10000000, - 0x00000010, - 0x00000000, - 0x00000018, - 0x10000000, - 0x00000018, - 0x20000000, - 0x00000018, - 0x30000000, - 0x00000018, - 0x40000000, - 0x00000018, - 0x50000000, - 0x00000018, - 0x60000000, - 0x00000018, - 0x70000000, - 0x00000018, - 0x80000000, - 0x00000018, - 0x50000000, - 0x00000028, - 0x90000000, - 0x00000028, - 0xa0000000, - 0x00000028, - 0xb0000000, - 0x00000028, - 0xc0000000, - 0x00000028, - 0xd0000000, - 0x00000028, - 0xe0000000, - 0x00000028, - 0xf0000000, - 0x00000028, - 0x00000000, - 0x00000029, - 0x10000000, - 0x00000029, - 0x20000000, - 0x00000029, - 0x30000000, - 0x00000029, - 0x40000000, - 0x00000029, - 0x50000000, - 0x00000029, - 0x60000000, - 0x00000029, - 0x70000000, - 0x00000029, - 0x80000000, - 0x00000029, - 0x90000000, - 0x00000029, - 0xa0000000, - 0x00000029, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x10000000, - 0x00000000, - 0x00000000, - 0x00000008, - 0x10000000, - 0x00000008, - 0x00000000, - 0x00000010, - 0x10000000, - 0x00000010, - 0x00000000, - 0x00000018, - 0x10000000, - 0x00000018, - 0x20000000, - 0x00000018, - 0x30000000, - 0x00000018, - 0x40000000, - 0x00000018, - 0x50000000, - 0x00000018, - 0x60000000, - 0x00000018, - 0x70000000, - 0x00000018, - 0x80000000, - 0x00000018, - 0x50000000, - 0x00000028, - 0x90000000, - 0x00000028, - 0xa0000000, - 0x00000028, - 0xb0000000, - 0x00000028, - 0xc0000000, - 0x00000028, - 0xd0000000, - 0x00000028, - 0xe0000000, - 0x00000028, - 0xf0000000, - 0x00000028, - 0x00000000, - 0x00000029, - 0x10000000, - 0x00000029, - 0x20000000, - 0x00000029, - 0x30000000, - 0x00000029, - 0x40000000, - 0x00000029, - 0x50000000, - 0x00000029, - 0x60000000, - 0x00000029, - 0x70000000, - 0x00000029, - 0x80000000, - 0x00000029, - 0x90000000, - 0x00000029, - 0xa0000000, - 0x00000029, - 0xb0000000, - 0x00000029, - 0xc0000000, - 0x00000029, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000 -}; - -static const u32 dot11lcn_gain_tbl_2G[] = { - 0x00000000, - 0x00000004, - 0x00000008, - 0x00000001, - 0x00000005, - 0x00000009, - 0x0000000d, - 0x0000004d, - 0x0000008d, - 0x00000049, - 0x00000089, - 0x000000c9, - 0x0000004b, - 0x0000008b, - 0x000000cb, - 0x000000cf, - 0x0000010f, - 0x0000050f, - 0x0000090f, - 0x0000094f, - 0x00000d4f, - 0x0000114f, - 0x0000118f, - 0x0000518f, - 0x0000918f, - 0x0000d18f, - 0x0001118f, - 0x0001518f, - 0x0001918f, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000 -}; - -static const u32 dot11lcn_gain_tbl_extlna_2G[] = { - 0x00000000, - 0x00000004, - 0x00000008, - 0x00000001, - 0x00000005, - 0x00000009, - 0x0000000d, - 0x00000003, - 0x00000007, - 0x0000000b, - 0x0000000f, - 0x0000004f, - 0x0000008f, - 0x000000cf, - 0x0000010f, - 0x0000014f, - 0x0000018f, - 0x0000058f, - 0x0000098f, - 0x00000d8f, - 0x00008000, - 0x00008004, - 0x00008008, - 0x00008001, - 0x00008005, - 0x00008009, - 0x0000800d, - 0x00008003, - 0x00008007, - 0x0000800b, - 0x0000800f, - 0x0000804f, - 0x0000808f, - 0x000080cf, - 0x0000810f, - 0x0000814f, - 0x0000818f, - 0x0000858f, - 0x0000898f, - 0x00008d8f, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000 -}; - -static const u16 dot11lcn_aux_gain_idx_tbl_extlna_2G[] = { - 0x0400, - 0x0400, - 0x0400, - 0x0400, - 0x0400, - 0x0400, - 0x0400, - 0x0400, - 0x0400, - 0x0401, - 0x0402, - 0x0403, - 0x0404, - 0x0483, - 0x0484, - 0x0485, - 0x0486, - 0x0583, - 0x0584, - 0x0585, - 0x0587, - 0x0588, - 0x0589, - 0x058a, - 0x0687, - 0x0688, - 0x0689, - 0x068a, - 0x068b, - 0x068c, - 0x068d, - 0x068e, - 0x068f, - 0x0690, - 0x0691, - 0x0692, - 0x0693, - 0x0000 -}; - -static const u8 dot11lcn_gain_val_tbl_extlna_2G[] = { - 0xfc, - 0x02, - 0x08, - 0x0e, - 0x13, - 0x1b, - 0xfc, - 0x02, - 0x08, - 0x0e, - 0x13, - 0x1b, - 0xfc, - 0x00, - 0x0f, - 0x03, - 0xeb, - 0xfe, - 0x07, - 0x0b, - 0x0f, - 0xfb, - 0xfe, - 0x01, - 0x05, - 0x08, - 0x0b, - 0x0e, - 0x11, - 0x14, - 0x17, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x03, - 0x06, - 0x09, - 0x0c, - 0x0f, - 0x12, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x03, - 0x06, - 0x09, - 0x0c, - 0x0f, - 0x12, - 0x15, - 0x18, - 0x1b, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00 -}; - -static const u32 dot11lcn_gain_idx_tbl_extlna_2G[] = { - 0x00000000, - 0x00000040, - 0x00000000, - 0x00000040, - 0x00000000, - 0x00000040, - 0x00000000, - 0x00000040, - 0x00000000, - 0x00000040, - 0x00000000, - 0x00000040, - 0x00000000, - 0x00000040, - 0x00000000, - 0x00000040, - 0x00000000, - 0x00000040, - 0x10000000, - 0x00000040, - 0x20000000, - 0x00000040, - 0x30000000, - 0x00000040, - 0x40000000, - 0x00000040, - 0x30000000, - 0x00000048, - 0x40000000, - 0x00000048, - 0x50000000, - 0x00000048, - 0x60000000, - 0x00000048, - 0x30000000, - 0x00000058, - 0x40000000, - 0x00000058, - 0x50000000, - 0x00000058, - 0x70000000, - 0x00000058, - 0x80000000, - 0x00000058, - 0x90000000, - 0x00000058, - 0xa0000000, - 0x00000058, - 0x70000000, - 0x00000068, - 0x80000000, - 0x00000068, - 0x90000000, - 0x00000068, - 0xa0000000, - 0x00000068, - 0xb0000000, - 0x00000068, - 0xc0000000, - 0x00000068, - 0xd0000000, - 0x00000068, - 0xe0000000, - 0x00000068, - 0xf0000000, - 0x00000068, - 0x00000000, - 0x00000069, - 0x10000000, - 0x00000069, - 0x20000000, - 0x00000069, - 0x30000000, - 0x00000069, - 0x40000000, - 0x00000041, - 0x40000000, - 0x00000041, - 0x40000000, - 0x00000041, - 0x40000000, - 0x00000041, - 0x40000000, - 0x00000041, - 0x40000000, - 0x00000041, - 0x40000000, - 0x00000041, - 0x40000000, - 0x00000041, - 0x40000000, - 0x00000041, - 0x50000000, - 0x00000041, - 0x60000000, - 0x00000041, - 0x70000000, - 0x00000041, - 0x80000000, - 0x00000041, - 0x70000000, - 0x00000049, - 0x80000000, - 0x00000049, - 0x90000000, - 0x00000049, - 0xa0000000, - 0x00000049, - 0x70000000, - 0x00000059, - 0x80000000, - 0x00000059, - 0x90000000, - 0x00000059, - 0xb0000000, - 0x00000059, - 0xc0000000, - 0x00000059, - 0xd0000000, - 0x00000059, - 0xe0000000, - 0x00000059, - 0xb0000000, - 0x00000069, - 0xc0000000, - 0x00000069, - 0xd0000000, - 0x00000069, - 0xe0000000, - 0x00000069, - 0xf0000000, - 0x00000069, - 0x00000000, - 0x0000006a, - 0x10000000, - 0x0000006a, - 0x20000000, - 0x0000006a, - 0x30000000, - 0x0000006a, - 0x40000000, - 0x0000006a, - 0x50000000, - 0x0000006a, - 0x60000000, - 0x0000006a, - 0x70000000, - 0x0000006a, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000 -}; - -static const u32 dot11lcn_aux_gain_idx_tbl_5G[] = { - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0001, - 0x0002, - 0x0003, - 0x0004, - 0x0083, - 0x0084, - 0x0085, - 0x0086, - 0x0087, - 0x0186, - 0x0187, - 0x0188, - 0x0189, - 0x018a, - 0x018b, - 0x018c, - 0x018d, - 0x018e, - 0x018f, - 0x0190, - 0x0191, - 0x0192, - 0x0193, - 0x0194, - 0x0195, - 0x0196, - 0x0197, - 0x0198, - 0x0199, - 0x019a, - 0x019b, - 0x019c, - 0x019d, - 0x0000 -}; - -static const u32 dot11lcn_gain_val_tbl_5G[] = { - 0xf7, - 0xfd, - 0x00, - 0x04, - 0x04, - 0x04, - 0xf7, - 0xfd, - 0x00, - 0x04, - 0x04, - 0x04, - 0xf6, - 0x00, - 0x0c, - 0x03, - 0xeb, - 0xfe, - 0x06, - 0x0a, - 0x10, - 0x00, - 0x03, - 0x06, - 0x09, - 0x0c, - 0x0f, - 0x12, - 0x15, - 0x18, - 0x1b, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x03, - 0x06, - 0x09, - 0x0c, - 0x0f, - 0x12, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x03, - 0x06, - 0x09, - 0x0c, - 0x0f, - 0x12, - 0x15, - 0x18, - 0x1b, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00 -}; - -static const u32 dot11lcn_gain_idx_tbl_5G[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x10000000, - 0x00000000, - 0x20000000, - 0x00000000, - 0x30000000, - 0x00000000, - 0x40000000, - 0x00000000, - 0x30000000, - 0x00000008, - 0x40000000, - 0x00000008, - 0x50000000, - 0x00000008, - 0x60000000, - 0x00000008, - 0x70000000, - 0x00000008, - 0x60000000, - 0x00000018, - 0x70000000, - 0x00000018, - 0x80000000, - 0x00000018, - 0x90000000, - 0x00000018, - 0xa0000000, - 0x00000018, - 0xb0000000, - 0x00000018, - 0xc0000000, - 0x00000018, - 0xd0000000, - 0x00000018, - 0xe0000000, - 0x00000018, - 0xf0000000, - 0x00000018, - 0x00000000, - 0x00000019, - 0x10000000, - 0x00000019, - 0x20000000, - 0x00000019, - 0x30000000, - 0x00000019, - 0x40000000, - 0x00000019, - 0x50000000, - 0x00000019, - 0x60000000, - 0x00000019, - 0x70000000, - 0x00000019, - 0x80000000, - 0x00000019, - 0x90000000, - 0x00000019, - 0xa0000000, - 0x00000019, - 0xb0000000, - 0x00000019, - 0xc0000000, - 0x00000019, - 0xd0000000, - 0x00000019, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000 -}; - -static const u32 dot11lcn_gain_tbl_5G[] = { - 0x00000000, - 0x00000040, - 0x00000080, - 0x00000001, - 0x00000005, - 0x00000009, - 0x0000000d, - 0x00000011, - 0x00000015, - 0x00000055, - 0x00000095, - 0x00000017, - 0x0000001b, - 0x0000005b, - 0x0000009b, - 0x000000db, - 0x0000011b, - 0x0000015b, - 0x0000019b, - 0x0000059b, - 0x0000099b, - 0x00000d9b, - 0x0000119b, - 0x0000519b, - 0x0000919b, - 0x0000d19b, - 0x0001119b, - 0x0001519b, - 0x0001919b, - 0x0001d19b, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000 -}; - -const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[] = { - {&dot11lcn_gain_tbl_rev0, - ARRAY_SIZE(dot11lcn_gain_tbl_rev0), 18, - 0, 32} - , - {&dot11lcn_aux_gain_idx_tbl_rev0, - ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16} - , - {&dot11lcn_gain_idx_tbl_rev0, - ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32} - , -}; - -static const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev1[] = { - {&dot11lcn_gain_tbl_rev1, - ARRAY_SIZE(dot11lcn_gain_tbl_rev1), 18, - 0, 32} - , - {&dot11lcn_aux_gain_idx_tbl_rev0, - ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16} - , - {&dot11lcn_gain_idx_tbl_rev0, - ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32} - , -}; - -const struct phytbl_info dot11lcnphytbl_rx_gain_info_2G_rev2[] = { - {&dot11lcn_gain_tbl_2G, - ARRAY_SIZE(dot11lcn_gain_tbl_2G), 18, 0, - 32} - , - {&dot11lcn_aux_gain_idx_tbl_2G, - ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_2G), 14, 0, 16} - , - {&dot11lcn_gain_idx_tbl_2G, - ARRAY_SIZE(dot11lcn_gain_idx_tbl_2G), - 13, 0, 32} - , - {&dot11lcn_gain_val_tbl_2G, - ARRAY_SIZE(dot11lcn_gain_val_tbl_2G), - 17, 0, 8} -}; - -const struct phytbl_info dot11lcnphytbl_rx_gain_info_5G_rev2[] = { - {&dot11lcn_gain_tbl_5G, - ARRAY_SIZE(dot11lcn_gain_tbl_5G), 18, 0, - 32} - , - {&dot11lcn_aux_gain_idx_tbl_5G, - ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_5G), 14, 0, 16} - , - {&dot11lcn_gain_idx_tbl_5G, - ARRAY_SIZE(dot11lcn_gain_idx_tbl_5G), - 13, 0, 32} - , - {&dot11lcn_gain_val_tbl_5G, - ARRAY_SIZE(dot11lcn_gain_val_tbl_5G), - 17, 0, 8} -}; - -const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[] = { - {&dot11lcn_gain_tbl_extlna_2G, - ARRAY_SIZE(dot11lcn_gain_tbl_extlna_2G), 18, 0, 32} - , - {&dot11lcn_aux_gain_idx_tbl_extlna_2G, - ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_extlna_2G), 14, 0, 16} - , - {&dot11lcn_gain_idx_tbl_extlna_2G, - ARRAY_SIZE(dot11lcn_gain_idx_tbl_extlna_2G), 13, 0, 32} - , - {&dot11lcn_gain_val_tbl_extlna_2G, - ARRAY_SIZE(dot11lcn_gain_val_tbl_extlna_2G), 17, 0, 8} -}; - -const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[] = { - {&dot11lcn_gain_tbl_5G, - ARRAY_SIZE(dot11lcn_gain_tbl_5G), 18, 0, - 32} - , - {&dot11lcn_aux_gain_idx_tbl_5G, - ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_5G), 14, 0, 16} - , - {&dot11lcn_gain_idx_tbl_5G, - ARRAY_SIZE(dot11lcn_gain_idx_tbl_5G), - 13, 0, 32} - , - {&dot11lcn_gain_val_tbl_5G, - ARRAY_SIZE(dot11lcn_gain_val_tbl_5G), - 17, 0, 8} -}; - -const u32 dot11lcnphytbl_rx_gain_info_sz_rev0 = - ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_rev0); - -const u32 dot11lcnphytbl_rx_gain_info_2G_rev2_sz = - ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_2G_rev2); - -const u32 dot11lcnphytbl_rx_gain_info_5G_rev2_sz = - ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_5G_rev2); - -static const u16 dot11lcn_min_sig_sq_tbl_rev0[] = { - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, - 0x014d, -}; - -static const u16 dot11lcn_noise_scale_tbl_rev0[] = { - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, -}; - -static const u32 dot11lcn_fltr_ctrl_tbl_rev0[] = { - 0x000141f8, - 0x000021f8, - 0x000021fb, - 0x000041fb, - 0x0001fe4b, - 0x0000217b, - 0x00002133, - 0x000040eb, - 0x0001fea3, - 0x0000024b, -}; - -static const u32 dot11lcn_ps_ctrl_tbl_rev0[] = { - 0x00100001, - 0x00200010, - 0x00300001, - 0x00400010, - 0x00500022, - 0x00600122, - 0x00700222, - 0x00800322, - 0x00900422, - 0x00a00522, - 0x00b00622, - 0x00c00722, - 0x00d00822, - 0x00f00922, - 0x00100a22, - 0x00200b22, - 0x00300c22, - 0x00400d22, - 0x00500e22, - 0x00600f22, -}; - -static const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[] = { - 0x0007, - 0x0005, - 0x0006, - 0x0004, - 0x0007, - 0x0005, - 0x0006, - 0x0004, - 0x0007, - 0x0005, - 0x0006, - 0x0004, - 0x0007, - 0x0005, - 0x0006, - 0x0004, - 0x000b, - 0x000b, - 0x000a, - 0x000a, - 0x000b, - 0x000b, - 0x000a, - 0x000a, - 0x000b, - 0x000b, - 0x000a, - 0x000a, - 0x000b, - 0x000b, - 0x000a, - 0x000a, - 0x0007, - 0x0005, - 0x0006, - 0x0004, - 0x0007, - 0x0005, - 0x0006, - 0x0004, - 0x0007, - 0x0005, - 0x0006, - 0x0004, - 0x0007, - 0x0005, - 0x0006, - 0x0004, - 0x000b, - 0x000b, - 0x000a, - 0x000a, - 0x000b, - 0x000b, - 0x000a, - 0x000a, - 0x000b, - 0x000b, - 0x000a, - 0x000a, - 0x000b, - 0x000b, - 0x000a, - 0x000a, - -}; - -static const u16 dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[] = { - 0x0007, - 0x0005, - 0x0002, - 0x0000, - 0x0007, - 0x0005, - 0x0002, - 0x0000, - 0x0007, - 0x0005, - 0x0002, - 0x0000, - 0x0007, - 0x0005, - 0x0002, - 0x0000, - 0x0007, - 0x0007, - 0x0002, - 0x0002, - 0x0007, - 0x0007, - 0x0002, - 0x0002, - 0x0007, - 0x0007, - 0x0002, - 0x0002, - 0x0007, - 0x0007, - 0x0002, - 0x0002, - 0x0007, - 0x0005, - 0x0002, - 0x0000, - 0x0007, - 0x0005, - 0x0002, - 0x0000, - 0x0007, - 0x0005, - 0x0002, - 0x0000, - 0x0007, - 0x0005, - 0x0002, - 0x0000, - 0x0007, - 0x0007, - 0x0002, - 0x0002, - 0x0007, - 0x0007, - 0x0002, - 0x0002, - 0x0007, - 0x0007, - 0x0002, - 0x0002, - 0x0007, - 0x0007, - 0x0002, - 0x0002, -}; - -static const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0[] = { - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, - 0x0002, - 0x0008, - 0x0004, - 0x0001, -}; - -static const u16 dot11lcn_sw_ctrl_tbl_4313_rev0[] = { - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, - 0x000a, - 0x0009, - 0x0006, - 0x0005, -}; - -static const u16 dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo[] = { - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, - 0x0005, - 0x0006, - 0x0009, - 0x000a, -}; - -static const u16 dot11lcn_sw_ctrl_tbl_rev0[] = { - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, - 0x0004, - 0x0004, - 0x0002, - 0x0002, -}; - -static const u8 dot11lcn_nf_table_rev0[] = { - 0x5f, - 0x36, - 0x29, - 0x1f, - 0x5f, - 0x36, - 0x29, - 0x1f, - 0x5f, - 0x36, - 0x29, - 0x1f, - 0x5f, - 0x36, - 0x29, - 0x1f, -}; - -static const u8 dot11lcn_gain_val_tbl_rev0[] = { - 0x09, - 0x0f, - 0x14, - 0x18, - 0xfe, - 0x07, - 0x0b, - 0x0f, - 0xfb, - 0xfe, - 0x01, - 0x05, - 0x08, - 0x0b, - 0x0e, - 0x11, - 0x14, - 0x17, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x03, - 0x06, - 0x09, - 0x0c, - 0x0f, - 0x12, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x03, - 0x06, - 0x09, - 0x0c, - 0x0f, - 0x12, - 0x15, - 0x18, - 0x1b, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x03, - 0xeb, - 0x00, - 0x00, -}; - -static const u8 dot11lcn_spur_tbl_rev0[] = { - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x02, - 0x03, - 0x01, - 0x03, - 0x02, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x02, - 0x03, - 0x01, - 0x03, - 0x02, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, - 0x01, -}; - -static const u16 dot11lcn_unsup_mcs_tbl_rev0[] = { - 0x001a, - 0x0034, - 0x004e, - 0x0068, - 0x009c, - 0x00d0, - 0x00ea, - 0x0104, - 0x0034, - 0x0068, - 0x009c, - 0x00d0, - 0x0138, - 0x01a0, - 0x01d4, - 0x0208, - 0x004e, - 0x009c, - 0x00ea, - 0x0138, - 0x01d4, - 0x0270, - 0x02be, - 0x030c, - 0x0068, - 0x00d0, - 0x0138, - 0x01a0, - 0x0270, - 0x0340, - 0x03a8, - 0x0410, - 0x0018, - 0x009c, - 0x00d0, - 0x0104, - 0x00ea, - 0x0138, - 0x0186, - 0x00d0, - 0x0104, - 0x0104, - 0x0138, - 0x016c, - 0x016c, - 0x01a0, - 0x0138, - 0x0186, - 0x0186, - 0x01d4, - 0x0222, - 0x0222, - 0x0270, - 0x0104, - 0x0138, - 0x016c, - 0x0138, - 0x016c, - 0x01a0, - 0x01d4, - 0x01a0, - 0x01d4, - 0x0208, - 0x0208, - 0x023c, - 0x0186, - 0x01d4, - 0x0222, - 0x01d4, - 0x0222, - 0x0270, - 0x02be, - 0x0270, - 0x02be, - 0x030c, - 0x030c, - 0x035a, - 0x0036, - 0x006c, - 0x00a2, - 0x00d8, - 0x0144, - 0x01b0, - 0x01e6, - 0x021c, - 0x006c, - 0x00d8, - 0x0144, - 0x01b0, - 0x0288, - 0x0360, - 0x03cc, - 0x0438, - 0x00a2, - 0x0144, - 0x01e6, - 0x0288, - 0x03cc, - 0x0510, - 0x05b2, - 0x0654, - 0x00d8, - 0x01b0, - 0x0288, - 0x0360, - 0x0510, - 0x06c0, - 0x0798, - 0x0870, - 0x0018, - 0x0144, - 0x01b0, - 0x021c, - 0x01e6, - 0x0288, - 0x032a, - 0x01b0, - 0x021c, - 0x021c, - 0x0288, - 0x02f4, - 0x02f4, - 0x0360, - 0x0288, - 0x032a, - 0x032a, - 0x03cc, - 0x046e, - 0x046e, - 0x0510, - 0x021c, - 0x0288, - 0x02f4, - 0x0288, - 0x02f4, - 0x0360, - 0x03cc, - 0x0360, - 0x03cc, - 0x0438, - 0x0438, - 0x04a4, - 0x032a, - 0x03cc, - 0x046e, - 0x03cc, - 0x046e, - 0x0510, - 0x05b2, - 0x0510, - 0x05b2, - 0x0654, - 0x0654, - 0x06f6, -}; - -static const u16 dot11lcn_iq_local_tbl_rev0[] = { - 0x0200, - 0x0300, - 0x0400, - 0x0600, - 0x0800, - 0x0b00, - 0x1000, - 0x1001, - 0x1002, - 0x1003, - 0x1004, - 0x1005, - 0x1006, - 0x1007, - 0x1707, - 0x2007, - 0x2d07, - 0x4007, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0200, - 0x0300, - 0x0400, - 0x0600, - 0x0800, - 0x0b00, - 0x1000, - 0x1001, - 0x1002, - 0x1003, - 0x1004, - 0x1005, - 0x1006, - 0x1007, - 0x1707, - 0x2007, - 0x2d07, - 0x4007, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x4000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, -}; - -static const u32 dot11lcn_papd_compdelta_tbl_rev0[] = { - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, - 0x00080000, -}; - -const struct phytbl_info dot11lcnphytbl_info_rev0[] = { - {&dot11lcn_min_sig_sq_tbl_rev0, - ARRAY_SIZE(dot11lcn_min_sig_sq_tbl_rev0), 2, 0, 16} - , - {&dot11lcn_noise_scale_tbl_rev0, - ARRAY_SIZE(dot11lcn_noise_scale_tbl_rev0), 1, 0, 16} - , - {&dot11lcn_fltr_ctrl_tbl_rev0, - ARRAY_SIZE(dot11lcn_fltr_ctrl_tbl_rev0), 11, 0, 32} - , - {&dot11lcn_ps_ctrl_tbl_rev0, - ARRAY_SIZE(dot11lcn_ps_ctrl_tbl_rev0), 12, 0, 32} - , - {&dot11lcn_gain_idx_tbl_rev0, - ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32} - , - {&dot11lcn_aux_gain_idx_tbl_rev0, - ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16} - , - {&dot11lcn_sw_ctrl_tbl_rev0, - ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_rev0), 15, 0, 16} - , - {&dot11lcn_nf_table_rev0, - ARRAY_SIZE(dot11lcn_nf_table_rev0), 16, - 0, 8} - , - {&dot11lcn_gain_val_tbl_rev0, - ARRAY_SIZE(dot11lcn_gain_val_tbl_rev0), 17, 0, 8} - , - {&dot11lcn_gain_tbl_rev0, - ARRAY_SIZE(dot11lcn_gain_tbl_rev0), 18, - 0, 32} - , - {&dot11lcn_spur_tbl_rev0, - ARRAY_SIZE(dot11lcn_spur_tbl_rev0), 20, - 0, 8} - , - {&dot11lcn_unsup_mcs_tbl_rev0, - ARRAY_SIZE(dot11lcn_unsup_mcs_tbl_rev0), 23, 0, 16} - , - {&dot11lcn_iq_local_tbl_rev0, - ARRAY_SIZE(dot11lcn_iq_local_tbl_rev0), 0, 0, 16} - , - {&dot11lcn_papd_compdelta_tbl_rev0, - ARRAY_SIZE(dot11lcn_papd_compdelta_tbl_rev0), 24, 0, 32} - , -}; - -const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313 = { - &dot11lcn_sw_ctrl_tbl_4313_rev0, - ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_rev0), 15, 0, 16 -}; - -const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa = { - &dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo, - ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo), 15, 0, 16 -}; - -const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa = { - &dot11lcn_sw_ctrl_tbl_4313_epa_rev0, - ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_epa_rev0), 15, 0, 16 -}; - -const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa = { - &dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo, - ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo), 15, 0, 16 -}; - -const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250 = { - &dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0, - ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0), 15, 0, 16 -}; - -const u32 dot11lcnphytbl_info_sz_rev0 = - ARRAY_SIZE(dot11lcnphytbl_info_rev0); - -const struct lcnphy_tx_gain_tbl_entry -dot11lcnphy_2GHz_extPA_gaintable_rev0[128] = { - {3, 0, 31, 0, 72}, - {3, 0, 31, 0, 70}, - {3, 0, 31, 0, 68}, - {3, 0, 30, 0, 67}, - {3, 0, 29, 0, 68}, - {3, 0, 28, 0, 68}, - {3, 0, 27, 0, 69}, - {3, 0, 26, 0, 70}, - {3, 0, 25, 0, 70}, - {3, 0, 24, 0, 71}, - {3, 0, 23, 0, 72}, - {3, 0, 23, 0, 70}, - {3, 0, 22, 0, 71}, - {3, 0, 21, 0, 72}, - {3, 0, 21, 0, 70}, - {3, 0, 21, 0, 68}, - {3, 0, 21, 0, 66}, - {3, 0, 21, 0, 64}, - {3, 0, 21, 0, 63}, - {3, 0, 20, 0, 64}, - {3, 0, 19, 0, 65}, - {3, 0, 19, 0, 64}, - {3, 0, 18, 0, 65}, - {3, 0, 18, 0, 64}, - {3, 0, 17, 0, 65}, - {3, 0, 17, 0, 64}, - {3, 0, 16, 0, 65}, - {3, 0, 16, 0, 64}, - {3, 0, 16, 0, 62}, - {3, 0, 16, 0, 60}, - {3, 0, 16, 0, 58}, - {3, 0, 15, 0, 61}, - {3, 0, 15, 0, 59}, - {3, 0, 14, 0, 61}, - {3, 0, 14, 0, 60}, - {3, 0, 14, 0, 58}, - {3, 0, 13, 0, 60}, - {3, 0, 13, 0, 59}, - {3, 0, 12, 0, 62}, - {3, 0, 12, 0, 60}, - {3, 0, 12, 0, 58}, - {3, 0, 11, 0, 62}, - {3, 0, 11, 0, 60}, - {3, 0, 11, 0, 59}, - {3, 0, 11, 0, 57}, - {3, 0, 10, 0, 61}, - {3, 0, 10, 0, 59}, - {3, 0, 10, 0, 57}, - {3, 0, 9, 0, 62}, - {3, 0, 9, 0, 60}, - {3, 0, 9, 0, 58}, - {3, 0, 9, 0, 57}, - {3, 0, 8, 0, 62}, - {3, 0, 8, 0, 60}, - {3, 0, 8, 0, 58}, - {3, 0, 8, 0, 57}, - {3, 0, 8, 0, 55}, - {3, 0, 7, 0, 61}, - {3, 0, 7, 0, 60}, - {3, 0, 7, 0, 58}, - {3, 0, 7, 0, 56}, - {3, 0, 7, 0, 55}, - {3, 0, 6, 0, 62}, - {3, 0, 6, 0, 60}, - {3, 0, 6, 0, 58}, - {3, 0, 6, 0, 57}, - {3, 0, 6, 0, 55}, - {3, 0, 6, 0, 54}, - {3, 0, 6, 0, 52}, - {3, 0, 5, 0, 61}, - {3, 0, 5, 0, 59}, - {3, 0, 5, 0, 57}, - {3, 0, 5, 0, 56}, - {3, 0, 5, 0, 54}, - {3, 0, 5, 0, 53}, - {3, 0, 5, 0, 51}, - {3, 0, 4, 0, 62}, - {3, 0, 4, 0, 60}, - {3, 0, 4, 0, 58}, - {3, 0, 4, 0, 57}, - {3, 0, 4, 0, 55}, - {3, 0, 4, 0, 54}, - {3, 0, 4, 0, 52}, - {3, 0, 4, 0, 51}, - {3, 0, 4, 0, 49}, - {3, 0, 4, 0, 48}, - {3, 0, 4, 0, 46}, - {3, 0, 3, 0, 60}, - {3, 0, 3, 0, 58}, - {3, 0, 3, 0, 57}, - {3, 0, 3, 0, 55}, - {3, 0, 3, 0, 54}, - {3, 0, 3, 0, 52}, - {3, 0, 3, 0, 51}, - {3, 0, 3, 0, 49}, - {3, 0, 3, 0, 48}, - {3, 0, 3, 0, 46}, - {3, 0, 3, 0, 45}, - {3, 0, 3, 0, 44}, - {3, 0, 3, 0, 43}, - {3, 0, 3, 0, 41}, - {3, 0, 2, 0, 61}, - {3, 0, 2, 0, 59}, - {3, 0, 2, 0, 57}, - {3, 0, 2, 0, 56}, - {3, 0, 2, 0, 54}, - {3, 0, 2, 0, 53}, - {3, 0, 2, 0, 51}, - {3, 0, 2, 0, 50}, - {3, 0, 2, 0, 48}, - {3, 0, 2, 0, 47}, - {3, 0, 2, 0, 46}, - {3, 0, 2, 0, 44}, - {3, 0, 2, 0, 43}, - {3, 0, 2, 0, 42}, - {3, 0, 2, 0, 41}, - {3, 0, 2, 0, 39}, - {3, 0, 2, 0, 38}, - {3, 0, 2, 0, 37}, - {3, 0, 2, 0, 36}, - {3, 0, 2, 0, 35}, - {3, 0, 2, 0, 34}, - {3, 0, 2, 0, 33}, - {3, 0, 2, 0, 32}, - {3, 0, 1, 0, 63}, - {3, 0, 1, 0, 61}, - {3, 0, 1, 0, 59}, - {3, 0, 1, 0, 57}, -}; - -const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[128] = { - {15, 0, 31, 0, 72}, - {15, 0, 31, 0, 70}, - {15, 0, 31, 0, 68}, - {15, 0, 30, 0, 68}, - {15, 0, 29, 0, 69}, - {15, 0, 28, 0, 69}, - {15, 0, 27, 0, 70}, - {15, 0, 26, 0, 70}, - {15, 0, 25, 0, 71}, - {15, 0, 24, 0, 72}, - {15, 0, 23, 0, 73}, - {15, 0, 23, 0, 71}, - {15, 0, 22, 0, 72}, - {15, 0, 21, 0, 73}, - {15, 0, 21, 0, 71}, - {15, 0, 21, 0, 69}, - {15, 0, 21, 0, 67}, - {15, 0, 21, 0, 65}, - {15, 0, 21, 0, 63}, - {15, 0, 20, 0, 65}, - {15, 0, 19, 0, 66}, - {15, 0, 19, 0, 64}, - {15, 0, 18, 0, 66}, - {15, 0, 18, 0, 64}, - {15, 0, 17, 0, 66}, - {15, 0, 17, 0, 64}, - {15, 0, 16, 0, 66}, - {15, 0, 16, 0, 64}, - {15, 0, 16, 0, 62}, - {15, 0, 16, 0, 61}, - {15, 0, 16, 0, 59}, - {15, 0, 15, 0, 61}, - {15, 0, 15, 0, 59}, - {15, 0, 14, 0, 62}, - {15, 0, 14, 0, 60}, - {15, 0, 14, 0, 58}, - {15, 0, 13, 0, 61}, - {15, 0, 13, 0, 59}, - {15, 0, 12, 0, 62}, - {15, 0, 12, 0, 61}, - {15, 0, 12, 0, 59}, - {15, 0, 11, 0, 62}, - {15, 0, 11, 0, 61}, - {15, 0, 11, 0, 59}, - {15, 0, 11, 0, 57}, - {15, 0, 10, 0, 61}, - {15, 0, 10, 0, 59}, - {15, 0, 10, 0, 58}, - {15, 0, 9, 0, 62}, - {15, 0, 9, 0, 61}, - {15, 0, 9, 0, 59}, - {15, 0, 9, 0, 57}, - {15, 0, 8, 0, 62}, - {15, 0, 8, 0, 61}, - {15, 0, 8, 0, 59}, - {15, 0, 8, 0, 57}, - {15, 0, 8, 0, 56}, - {15, 0, 8, 0, 54}, - {15, 0, 8, 0, 53}, - {15, 0, 8, 0, 51}, - {15, 0, 8, 0, 50}, - {7, 0, 7, 0, 69}, - {7, 0, 7, 0, 67}, - {7, 0, 7, 0, 65}, - {7, 0, 7, 0, 64}, - {7, 0, 7, 0, 62}, - {7, 0, 7, 0, 60}, - {7, 0, 7, 0, 58}, - {7, 0, 7, 0, 57}, - {7, 0, 7, 0, 55}, - {7, 0, 6, 0, 62}, - {7, 0, 6, 0, 61}, - {7, 0, 6, 0, 59}, - {7, 0, 6, 0, 57}, - {7, 0, 6, 0, 56}, - {7, 0, 6, 0, 54}, - {7, 0, 6, 0, 53}, - {7, 0, 5, 0, 61}, - {7, 0, 5, 0, 60}, - {7, 0, 5, 0, 58}, - {7, 0, 5, 0, 56}, - {7, 0, 5, 0, 55}, - {7, 0, 5, 0, 53}, - {7, 0, 5, 0, 52}, - {7, 0, 5, 0, 50}, - {7, 0, 5, 0, 49}, - {7, 0, 5, 0, 47}, - {7, 0, 4, 0, 57}, - {7, 0, 4, 0, 56}, - {7, 0, 4, 0, 54}, - {7, 0, 4, 0, 53}, - {7, 0, 4, 0, 51}, - {7, 0, 4, 0, 50}, - {7, 0, 4, 0, 48}, - {7, 0, 4, 0, 47}, - {7, 0, 4, 0, 46}, - {7, 0, 4, 0, 44}, - {7, 0, 4, 0, 43}, - {7, 0, 4, 0, 42}, - {7, 0, 4, 0, 41}, - {7, 0, 4, 0, 40}, - {7, 0, 3, 0, 51}, - {7, 0, 3, 0, 50}, - {7, 0, 3, 0, 48}, - {7, 0, 3, 0, 47}, - {7, 0, 3, 0, 46}, - {7, 0, 3, 0, 44}, - {7, 0, 3, 0, 43}, - {7, 0, 3, 0, 42}, - {7, 0, 3, 0, 41}, - {3, 0, 3, 0, 56}, - {3, 0, 3, 0, 54}, - {3, 0, 3, 0, 53}, - {3, 0, 3, 0, 51}, - {3, 0, 3, 0, 50}, - {3, 0, 3, 0, 48}, - {3, 0, 3, 0, 47}, - {3, 0, 3, 0, 46}, - {3, 0, 3, 0, 44}, - {3, 0, 3, 0, 43}, - {3, 0, 3, 0, 42}, - {3, 0, 3, 0, 41}, - {3, 0, 3, 0, 39}, - {3, 0, 3, 0, 38}, - {3, 0, 3, 0, 37}, - {3, 0, 3, 0, 36}, - {3, 0, 3, 0, 35}, - {3, 0, 3, 0, 34}, -}; - -const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[128] = { - {255, 255, 0xf0, 0, 152}, - {255, 255, 0xf0, 0, 147}, - {255, 255, 0xf0, 0, 143}, - {255, 255, 0xf0, 0, 139}, - {255, 255, 0xf0, 0, 135}, - {255, 255, 0xf0, 0, 131}, - {255, 255, 0xf0, 0, 128}, - {255, 255, 0xf0, 0, 124}, - {255, 255, 0xf0, 0, 121}, - {255, 255, 0xf0, 0, 117}, - {255, 255, 0xf0, 0, 114}, - {255, 255, 0xf0, 0, 111}, - {255, 255, 0xf0, 0, 107}, - {255, 255, 0xf0, 0, 104}, - {255, 255, 0xf0, 0, 101}, - {255, 255, 0xf0, 0, 99}, - {255, 255, 0xf0, 0, 96}, - {255, 255, 0xf0, 0, 93}, - {255, 255, 0xf0, 0, 90}, - {255, 255, 0xf0, 0, 88}, - {255, 255, 0xf0, 0, 85}, - {255, 255, 0xf0, 0, 83}, - {255, 255, 0xf0, 0, 81}, - {255, 255, 0xf0, 0, 78}, - {255, 255, 0xf0, 0, 76}, - {255, 255, 0xf0, 0, 74}, - {255, 255, 0xf0, 0, 72}, - {255, 255, 0xf0, 0, 70}, - {255, 255, 0xf0, 0, 68}, - {255, 255, 0xf0, 0, 66}, - {255, 255, 0xf0, 0, 64}, - {255, 248, 0xf0, 0, 64}, - {255, 241, 0xf0, 0, 64}, - {255, 251, 0xe0, 0, 64}, - {255, 244, 0xe0, 0, 64}, - {255, 254, 0xd0, 0, 64}, - {255, 246, 0xd0, 0, 64}, - {255, 239, 0xd0, 0, 64}, - {255, 249, 0xc0, 0, 64}, - {255, 242, 0xc0, 0, 64}, - {255, 255, 0xb0, 0, 64}, - {255, 248, 0xb0, 0, 64}, - {255, 241, 0xb0, 0, 64}, - {255, 254, 0xa0, 0, 64}, - {255, 246, 0xa0, 0, 64}, - {255, 239, 0xa0, 0, 64}, - {255, 255, 0x90, 0, 64}, - {255, 248, 0x90, 0, 64}, - {255, 241, 0x90, 0, 64}, - {255, 234, 0x90, 0, 64}, - {255, 255, 0x80, 0, 64}, - {255, 248, 0x80, 0, 64}, - {255, 241, 0x80, 0, 64}, - {255, 234, 0x80, 0, 64}, - {255, 255, 0x70, 0, 64}, - {255, 248, 0x70, 0, 64}, - {255, 241, 0x70, 0, 64}, - {255, 234, 0x70, 0, 64}, - {255, 227, 0x70, 0, 64}, - {255, 221, 0x70, 0, 64}, - {255, 215, 0x70, 0, 64}, - {255, 208, 0x70, 0, 64}, - {255, 203, 0x70, 0, 64}, - {255, 197, 0x70, 0, 64}, - {255, 255, 0x60, 0, 64}, - {255, 248, 0x60, 0, 64}, - {255, 241, 0x60, 0, 64}, - {255, 234, 0x60, 0, 64}, - {255, 227, 0x60, 0, 64}, - {255, 221, 0x60, 0, 64}, - {255, 255, 0x50, 0, 64}, - {255, 248, 0x50, 0, 64}, - {255, 241, 0x50, 0, 64}, - {255, 234, 0x50, 0, 64}, - {255, 227, 0x50, 0, 64}, - {255, 221, 0x50, 0, 64}, - {255, 215, 0x50, 0, 64}, - {255, 208, 0x50, 0, 64}, - {255, 255, 0x40, 0, 64}, - {255, 248, 0x40, 0, 64}, - {255, 241, 0x40, 0, 64}, - {255, 234, 0x40, 0, 64}, - {255, 227, 0x40, 0, 64}, - {255, 221, 0x40, 0, 64}, - {255, 215, 0x40, 0, 64}, - {255, 208, 0x40, 0, 64}, - {255, 203, 0x40, 0, 64}, - {255, 197, 0x40, 0, 64}, - {255, 255, 0x30, 0, 64}, - {255, 248, 0x30, 0, 64}, - {255, 241, 0x30, 0, 64}, - {255, 234, 0x30, 0, 64}, - {255, 227, 0x30, 0, 64}, - {255, 221, 0x30, 0, 64}, - {255, 215, 0x30, 0, 64}, - {255, 208, 0x30, 0, 64}, - {255, 203, 0x30, 0, 64}, - {255, 197, 0x30, 0, 64}, - {255, 191, 0x30, 0, 64}, - {255, 186, 0x30, 0, 64}, - {255, 181, 0x30, 0, 64}, - {255, 175, 0x30, 0, 64}, - {255, 255, 0x20, 0, 64}, - {255, 248, 0x20, 0, 64}, - {255, 241, 0x20, 0, 64}, - {255, 234, 0x20, 0, 64}, - {255, 227, 0x20, 0, 64}, - {255, 221, 0x20, 0, 64}, - {255, 215, 0x20, 0, 64}, - {255, 208, 0x20, 0, 64}, - {255, 203, 0x20, 0, 64}, - {255, 197, 0x20, 0, 64}, - {255, 191, 0x20, 0, 64}, - {255, 186, 0x20, 0, 64}, - {255, 181, 0x20, 0, 64}, - {255, 175, 0x20, 0, 64}, - {255, 170, 0x20, 0, 64}, - {255, 166, 0x20, 0, 64}, - {255, 161, 0x20, 0, 64}, - {255, 156, 0x20, 0, 64}, - {255, 152, 0x20, 0, 64}, - {255, 148, 0x20, 0, 64}, - {255, 143, 0x20, 0, 64}, - {255, 139, 0x20, 0, 64}, - {255, 135, 0x20, 0, 64}, - {255, 132, 0x20, 0, 64}, - {255, 255, 0x10, 0, 64}, - {255, 248, 0x10, 0, 64}, -}; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h deleted file mode 100644 index 489422a36085..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2010 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 "phy_int.h" - -extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[]; -extern const u32 dot11lcnphytbl_rx_gain_info_sz_rev0; -extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313; -extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa; -extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa; -extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa_combo; -extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa; -extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250; - -extern const struct phytbl_info dot11lcnphytbl_info_rev0[]; -extern const u32 dot11lcnphytbl_info_sz_rev0; - -extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_2G_rev2[]; -extern const u32 dot11lcnphytbl_rx_gain_info_2G_rev2_sz; - -extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_5G_rev2[]; -extern const u32 dot11lcnphytbl_rx_gain_info_5G_rev2_sz; - -extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[]; - -extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[]; - -struct lcnphy_tx_gain_tbl_entry { - unsigned char gm; - unsigned char pga; - unsigned char pad; - unsigned char dac; - unsigned char bb_mult; -}; - -extern const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[]; - -extern const struct -lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_extPA_gaintable_rev0[]; - -extern const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[]; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c deleted file mode 100644 index dbf50ef6cd75..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c +++ /dev/null @@ -1,10630 +0,0 @@ -/* - * Copyright (c) 2010 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 "phytbl_n.h" - -static const u32 frame_struct_rev0[] = { - 0x08004a04, - 0x00100000, - 0x01000a05, - 0x00100020, - 0x09804506, - 0x00100030, - 0x09804507, - 0x00100030, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x08004a0c, - 0x00100004, - 0x01000a0d, - 0x00100024, - 0x0980450e, - 0x00100034, - 0x0980450f, - 0x00100034, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000a04, - 0x00100000, - 0x11008a05, - 0x00100020, - 0x1980c506, - 0x00100030, - 0x21810506, - 0x00100030, - 0x21810506, - 0x00100030, - 0x01800504, - 0x00100030, - 0x11808505, - 0x00100030, - 0x29814507, - 0x01100030, - 0x00000a04, - 0x00100000, - 0x11008a05, - 0x00100020, - 0x21810506, - 0x00100030, - 0x21810506, - 0x00100030, - 0x29814507, - 0x01100030, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000a0c, - 0x00100008, - 0x11008a0d, - 0x00100028, - 0x1980c50e, - 0x00100038, - 0x2181050e, - 0x00100038, - 0x2181050e, - 0x00100038, - 0x0180050c, - 0x00100038, - 0x1180850d, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000a0c, - 0x00100008, - 0x11008a0d, - 0x00100028, - 0x2181050e, - 0x00100038, - 0x2181050e, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x08004a04, - 0x00100000, - 0x01000a05, - 0x00100020, - 0x1980c506, - 0x00100030, - 0x1980c506, - 0x00100030, - 0x11808504, - 0x00100030, - 0x3981ca05, - 0x00100030, - 0x29814507, - 0x01100030, - 0x00000000, - 0x00000000, - 0x10008a04, - 0x00100000, - 0x3981ca05, - 0x00100030, - 0x1980c506, - 0x00100030, - 0x29814507, - 0x01100030, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x08004a0c, - 0x00100008, - 0x01000a0d, - 0x00100028, - 0x1980c50e, - 0x00100038, - 0x1980c50e, - 0x00100038, - 0x1180850c, - 0x00100038, - 0x3981ca0d, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000000, - 0x00000000, - 0x10008a0c, - 0x00100008, - 0x3981ca0d, - 0x00100038, - 0x1980c50e, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x40021404, - 0x00100000, - 0x02001405, - 0x00100040, - 0x0b004a06, - 0x01900060, - 0x13008a06, - 0x01900060, - 0x13008a06, - 0x01900060, - 0x43020a04, - 0x00100060, - 0x1b00ca05, - 0x00100060, - 0x23010a07, - 0x01500060, - 0x40021404, - 0x00100000, - 0x1a00d405, - 0x00100040, - 0x13008a06, - 0x01900060, - 0x13008a06, - 0x01900060, - 0x23010a07, - 0x01500060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x00100010, - 0x0200140d, - 0x00100050, - 0x0b004a0e, - 0x01900070, - 0x13008a0e, - 0x01900070, - 0x13008a0e, - 0x01900070, - 0x43020a0c, - 0x00100070, - 0x1b00ca0d, - 0x00100070, - 0x23010a0f, - 0x01500070, - 0x4002140c, - 0x00100010, - 0x1a00d40d, - 0x00100050, - 0x13008a0e, - 0x01900070, - 0x13008a0e, - 0x01900070, - 0x23010a0f, - 0x01500070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x50029404, - 0x00100000, - 0x32019405, - 0x00100040, - 0x0b004a06, - 0x01900060, - 0x0b004a06, - 0x01900060, - 0x5b02ca04, - 0x00100060, - 0x3b01d405, - 0x00100060, - 0x23010a07, - 0x01500060, - 0x00000000, - 0x00000000, - 0x5802d404, - 0x00100000, - 0x3b01d405, - 0x00100060, - 0x0b004a06, - 0x01900060, - 0x23010a07, - 0x01500060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x5002940c, - 0x00100010, - 0x3201940d, - 0x00100050, - 0x0b004a0e, - 0x01900070, - 0x0b004a0e, - 0x01900070, - 0x5b02ca0c, - 0x00100070, - 0x3b01d40d, - 0x00100070, - 0x23010a0f, - 0x01500070, - 0x00000000, - 0x00000000, - 0x5802d40c, - 0x00100010, - 0x3b01d40d, - 0x00100070, - 0x0b004a0e, - 0x01900070, - 0x23010a0f, - 0x01500070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x40021404, - 0x000f4800, - 0x62031405, - 0x00100040, - 0x53028a06, - 0x01900060, - 0x53028a07, - 0x01900060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x000f4808, - 0x6203140d, - 0x00100048, - 0x53028a0e, - 0x01900068, - 0x53028a0f, - 0x01900068, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000a0c, - 0x00100004, - 0x11008a0d, - 0x00100024, - 0x1980c50e, - 0x00100034, - 0x2181050e, - 0x00100034, - 0x2181050e, - 0x00100034, - 0x0180050c, - 0x00100038, - 0x1180850d, - 0x00100038, - 0x1181850d, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000a0c, - 0x00100008, - 0x11008a0d, - 0x00100028, - 0x2181050e, - 0x00100038, - 0x2181050e, - 0x00100038, - 0x1181850d, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x08004a04, - 0x00100000, - 0x01000a05, - 0x00100020, - 0x0180c506, - 0x00100030, - 0x0180c506, - 0x00100030, - 0x2180c50c, - 0x00100030, - 0x49820a0d, - 0x0016a130, - 0x41824a0d, - 0x0016a130, - 0x2981450f, - 0x01100030, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x2000ca0c, - 0x00100000, - 0x49820a0d, - 0x0016a130, - 0x1980c50e, - 0x00100030, - 0x41824a0d, - 0x0016a130, - 0x2981450f, - 0x01100030, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x00100008, - 0x0200140d, - 0x00100048, - 0x0b004a0e, - 0x01900068, - 0x13008a0e, - 0x01900068, - 0x13008a0e, - 0x01900068, - 0x43020a0c, - 0x00100070, - 0x1b00ca0d, - 0x00100070, - 0x1b014a0d, - 0x00100070, - 0x23010a0f, - 0x01500070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x00100010, - 0x1a00d40d, - 0x00100050, - 0x13008a0e, - 0x01900070, - 0x13008a0e, - 0x01900070, - 0x1b014a0d, - 0x00100070, - 0x23010a0f, - 0x01500070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x50029404, - 0x00100000, - 0x32019405, - 0x00100040, - 0x03004a06, - 0x01900060, - 0x03004a06, - 0x01900060, - 0x6b030a0c, - 0x00100060, - 0x4b02140d, - 0x0016a160, - 0x4302540d, - 0x0016a160, - 0x23010a0f, - 0x01500060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x6b03140c, - 0x00100060, - 0x4b02140d, - 0x0016a160, - 0x0b004a0e, - 0x01900060, - 0x4302540d, - 0x0016a160, - 0x23010a0f, - 0x01500060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x40021404, - 0x00100000, - 0x1a00d405, - 0x00100040, - 0x53028a06, - 0x01900060, - 0x5b02ca06, - 0x01900060, - 0x5b02ca06, - 0x01900060, - 0x43020a04, - 0x00100060, - 0x1b00ca05, - 0x00100060, - 0x53028a07, - 0x0190c060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x00100010, - 0x1a00d40d, - 0x00100050, - 0x53028a0e, - 0x01900070, - 0x5b02ca0e, - 0x01900070, - 0x5b02ca0e, - 0x01900070, - 0x43020a0c, - 0x00100070, - 0x1b00ca0d, - 0x00100070, - 0x53028a0f, - 0x0190c070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x40021404, - 0x00100000, - 0x1a00d405, - 0x00100040, - 0x5b02ca06, - 0x01900060, - 0x5b02ca06, - 0x01900060, - 0x53028a07, - 0x0190c060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x00100010, - 0x1a00d40d, - 0x00100050, - 0x5b02ca0e, - 0x01900070, - 0x5b02ca0e, - 0x01900070, - 0x53028a0f, - 0x0190c070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u8 frame_lut_rev0[] = { - 0x02, - 0x04, - 0x14, - 0x14, - 0x03, - 0x05, - 0x16, - 0x16, - 0x0a, - 0x0c, - 0x1c, - 0x1c, - 0x0b, - 0x0d, - 0x1e, - 0x1e, - 0x06, - 0x08, - 0x18, - 0x18, - 0x07, - 0x09, - 0x1a, - 0x1a, - 0x0e, - 0x10, - 0x20, - 0x28, - 0x0f, - 0x11, - 0x22, - 0x2a, -}; - -static const u32 tmap_tbl_rev0[] = { - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0xf1111110, - 0x11111111, - 0x11f11111, - 0x00000111, - 0x11000000, - 0x1111f111, - 0x11111111, - 0x111111f1, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x000aa888, - 0x88880000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa2222220, - 0x22222222, - 0x22c22222, - 0x00000222, - 0x22000000, - 0x2222a222, - 0x22222222, - 0x222222a2, - 0xf1111110, - 0x11111111, - 0x11f11111, - 0x00011111, - 0x11110000, - 0x1111f111, - 0x11111111, - 0x111111f1, - 0xa8aa88a0, - 0xa88888a8, - 0xa8a8a88a, - 0x00088aaa, - 0xaaaa0000, - 0xa8a8aa88, - 0xa88aaaaa, - 0xaaaa8a8a, - 0xaaa8aaa0, - 0x8aaa8aaa, - 0xaa8a8a8a, - 0x000aaa88, - 0x8aaa0000, - 0xaaa8a888, - 0x8aa88a8a, - 0x8a88a888, - 0x08080a00, - 0x0a08080a, - 0x080a0a08, - 0x00080808, - 0x080a0000, - 0x080a0808, - 0x080a0808, - 0x0a0a0a08, - 0xa0a0a0a0, - 0x80a0a080, - 0x8080a0a0, - 0x00008080, - 0x80a00000, - 0x80a080a0, - 0xa080a0a0, - 0x8080a0a0, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x99999000, - 0x9b9b99bb, - 0x9bb99999, - 0x9999b9b9, - 0x9b99bb90, - 0x9bbbbb9b, - 0x9b9b9bb9, - 0x00000999, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00aaa888, - 0x22000000, - 0x2222b222, - 0x22222222, - 0x222222b2, - 0xb2222220, - 0x22222222, - 0x22d22222, - 0x00000222, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x33000000, - 0x3333b333, - 0x33333333, - 0x333333b3, - 0xb3333330, - 0x33333333, - 0x33d33333, - 0x00000333, - 0x22000000, - 0x2222a222, - 0x22222222, - 0x222222a2, - 0xa2222220, - 0x22222222, - 0x22c22222, - 0x00000222, - 0x99b99b00, - 0x9b9b99bb, - 0x9bb99999, - 0x9999b9b9, - 0x9b99bb99, - 0x9bbbbb9b, - 0x9b9b9bb9, - 0x00000999, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa88, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x08aaa888, - 0x22222200, - 0x2222f222, - 0x22222222, - 0x222222f2, - 0x22222222, - 0x22222222, - 0x22f22222, - 0x00000222, - 0x11000000, - 0x1111f111, - 0x11111111, - 0x11111111, - 0xf1111111, - 0x11111111, - 0x11f11111, - 0x01111111, - 0xbb9bb900, - 0xb9b9bb99, - 0xb99bbbbb, - 0xbbbb9b9b, - 0xb9bb99bb, - 0xb99999b9, - 0xb9b9b99b, - 0x00000bbb, - 0xaa000000, - 0xa8a8aa88, - 0xa88aaaaa, - 0xaaaa8a8a, - 0xa8aa88aa, - 0xa88888a8, - 0xa8a8a88a, - 0x0a888aaa, - 0xaa000000, - 0xa8a8aa88, - 0xa88aaaaa, - 0xaaaa8a8a, - 0xa8aa88a0, - 0xa88888a8, - 0xa8a8a88a, - 0x00000aaa, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0xbbbbbb00, - 0x999bbbbb, - 0x9bb99b9b, - 0xb9b9b9bb, - 0xb9b99bbb, - 0xb9b9b9bb, - 0xb9bb9b99, - 0x00000999, - 0x8a000000, - 0xaa88a888, - 0xa88888aa, - 0xa88a8a88, - 0xa88aa88a, - 0x88a8aaaa, - 0xa8aa8aaa, - 0x0888a88a, - 0x0b0b0b00, - 0x090b0b0b, - 0x0b090b0b, - 0x0909090b, - 0x09090b0b, - 0x09090b0b, - 0x09090b09, - 0x00000909, - 0x0a000000, - 0x0a080808, - 0x080a080a, - 0x080a0a08, - 0x080a080a, - 0x0808080a, - 0x0a0a0a08, - 0x0808080a, - 0xb0b0b000, - 0x9090b0b0, - 0x90b09090, - 0xb0b0b090, - 0xb0b090b0, - 0x90b0b0b0, - 0xb0b09090, - 0x00000090, - 0x80000000, - 0xa080a080, - 0xa08080a0, - 0xa0808080, - 0xa080a080, - 0x80a0a0a0, - 0xa0a080a0, - 0x00a0a0a0, - 0x22000000, - 0x2222f222, - 0x22222222, - 0x222222f2, - 0xf2222220, - 0x22222222, - 0x22f22222, - 0x00000222, - 0x11000000, - 0x1111f111, - 0x11111111, - 0x111111f1, - 0xf1111110, - 0x11111111, - 0x11f11111, - 0x00000111, - 0x33000000, - 0x3333f333, - 0x33333333, - 0x333333f3, - 0xf3333330, - 0x33333333, - 0x33f33333, - 0x00000333, - 0x22000000, - 0x2222f222, - 0x22222222, - 0x222222f2, - 0xf2222220, - 0x22222222, - 0x22f22222, - 0x00000222, - 0x99000000, - 0x9b9b99bb, - 0x9bb99999, - 0x9999b9b9, - 0x9b99bb90, - 0x9bbbbb9b, - 0x9b9b9bb9, - 0x00000999, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88888000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00aaa888, - 0x88a88a00, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa88, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa88, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x08aaa888, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 tdtrn_tbl_rev0[] = { - 0x061c061c, - 0x0050ee68, - 0xf592fe36, - 0xfe5212f6, - 0x00000c38, - 0xfe5212f6, - 0xf592fe36, - 0x0050ee68, - 0x061c061c, - 0xee680050, - 0xfe36f592, - 0x12f6fe52, - 0x0c380000, - 0x12f6fe52, - 0xfe36f592, - 0xee680050, - 0x061c061c, - 0x0050ee68, - 0xf592fe36, - 0xfe5212f6, - 0x00000c38, - 0xfe5212f6, - 0xf592fe36, - 0x0050ee68, - 0x061c061c, - 0xee680050, - 0xfe36f592, - 0x12f6fe52, - 0x0c380000, - 0x12f6fe52, - 0xfe36f592, - 0xee680050, - 0x05e305e3, - 0x004def0c, - 0xf5f3fe47, - 0xfe611246, - 0x00000bc7, - 0xfe611246, - 0xf5f3fe47, - 0x004def0c, - 0x05e305e3, - 0xef0c004d, - 0xfe47f5f3, - 0x1246fe61, - 0x0bc70000, - 0x1246fe61, - 0xfe47f5f3, - 0xef0c004d, - 0x05e305e3, - 0x004def0c, - 0xf5f3fe47, - 0xfe611246, - 0x00000bc7, - 0xfe611246, - 0xf5f3fe47, - 0x004def0c, - 0x05e305e3, - 0xef0c004d, - 0xfe47f5f3, - 0x1246fe61, - 0x0bc70000, - 0x1246fe61, - 0xfe47f5f3, - 0xef0c004d, - 0xfa58fa58, - 0xf895043b, - 0xff4c09c0, - 0xfbc6ffa8, - 0xfb84f384, - 0x0798f6f9, - 0x05760122, - 0x058409f6, - 0x0b500000, - 0x05b7f542, - 0x08860432, - 0x06ddfee7, - 0xfb84f384, - 0xf9d90664, - 0xf7e8025c, - 0x00fff7bd, - 0x05a805a8, - 0xf7bd00ff, - 0x025cf7e8, - 0x0664f9d9, - 0xf384fb84, - 0xfee706dd, - 0x04320886, - 0xf54205b7, - 0x00000b50, - 0x09f60584, - 0x01220576, - 0xf6f90798, - 0xf384fb84, - 0xffa8fbc6, - 0x09c0ff4c, - 0x043bf895, - 0x02d402d4, - 0x07de0270, - 0xfc96079c, - 0xf90afe94, - 0xfe00ff2c, - 0x02d4065d, - 0x092a0096, - 0x0014fbb8, - 0xfd2cfd2c, - 0x076afb3c, - 0x0096f752, - 0xf991fd87, - 0xfb2c0200, - 0xfeb8f960, - 0x08e0fc96, - 0x049802a8, - 0xfd2cfd2c, - 0x02a80498, - 0xfc9608e0, - 0xf960feb8, - 0x0200fb2c, - 0xfd87f991, - 0xf7520096, - 0xfb3c076a, - 0xfd2cfd2c, - 0xfbb80014, - 0x0096092a, - 0x065d02d4, - 0xff2cfe00, - 0xfe94f90a, - 0x079cfc96, - 0x027007de, - 0x02d402d4, - 0x027007de, - 0x079cfc96, - 0xfe94f90a, - 0xff2cfe00, - 0x065d02d4, - 0x0096092a, - 0xfbb80014, - 0xfd2cfd2c, - 0xfb3c076a, - 0xf7520096, - 0xfd87f991, - 0x0200fb2c, - 0xf960feb8, - 0xfc9608e0, - 0x02a80498, - 0xfd2cfd2c, - 0x049802a8, - 0x08e0fc96, - 0xfeb8f960, - 0xfb2c0200, - 0xf991fd87, - 0x0096f752, - 0x076afb3c, - 0xfd2cfd2c, - 0x0014fbb8, - 0x092a0096, - 0x02d4065d, - 0xfe00ff2c, - 0xf90afe94, - 0xfc96079c, - 0x07de0270, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x062a0000, - 0xfefa0759, - 0x08b80908, - 0xf396fc2d, - 0xf9d6045c, - 0xfc4ef608, - 0xf748f596, - 0x07b207bf, - 0x062a062a, - 0xf84ef841, - 0xf748f596, - 0x03b209f8, - 0xf9d6045c, - 0x0c6a03d3, - 0x08b80908, - 0x0106f8a7, - 0x062a0000, - 0xfefaf8a7, - 0x08b8f6f8, - 0xf39603d3, - 0xf9d6fba4, - 0xfc4e09f8, - 0xf7480a6a, - 0x07b2f841, - 0x062af9d6, - 0xf84e07bf, - 0xf7480a6a, - 0x03b2f608, - 0xf9d6fba4, - 0x0c6afc2d, - 0x08b8f6f8, - 0x01060759, - 0x062a0000, - 0xfefa0759, - 0x08b80908, - 0xf396fc2d, - 0xf9d6045c, - 0xfc4ef608, - 0xf748f596, - 0x07b207bf, - 0x062a062a, - 0xf84ef841, - 0xf748f596, - 0x03b209f8, - 0xf9d6045c, - 0x0c6a03d3, - 0x08b80908, - 0x0106f8a7, - 0x062a0000, - 0xfefaf8a7, - 0x08b8f6f8, - 0xf39603d3, - 0xf9d6fba4, - 0xfc4e09f8, - 0xf7480a6a, - 0x07b2f841, - 0x062af9d6, - 0xf84e07bf, - 0xf7480a6a, - 0x03b2f608, - 0xf9d6fba4, - 0x0c6afc2d, - 0x08b8f6f8, - 0x01060759, - 0x061c061c, - 0xff30009d, - 0xffb21141, - 0xfd87fb54, - 0xf65dfe59, - 0x02eef99e, - 0x0166f03c, - 0xfff809b6, - 0x000008a4, - 0x000af42b, - 0x00eff577, - 0xfa840bf2, - 0xfc02ff51, - 0x08260f67, - 0xfff0036f, - 0x0842f9c3, - 0x00000000, - 0x063df7be, - 0xfc910010, - 0xf099f7da, - 0x00af03fe, - 0xf40e057c, - 0x0a89ff11, - 0x0bd5fff6, - 0xf75c0000, - 0xf64a0008, - 0x0fc4fe9a, - 0x0662fd12, - 0x01a709a3, - 0x04ac0279, - 0xeebf004e, - 0xff6300d0, - 0xf9e4f9e4, - 0x00d0ff63, - 0x004eeebf, - 0x027904ac, - 0x09a301a7, - 0xfd120662, - 0xfe9a0fc4, - 0x0008f64a, - 0x0000f75c, - 0xfff60bd5, - 0xff110a89, - 0x057cf40e, - 0x03fe00af, - 0xf7daf099, - 0x0010fc91, - 0xf7be063d, - 0x00000000, - 0xf9c30842, - 0x036ffff0, - 0x0f670826, - 0xff51fc02, - 0x0bf2fa84, - 0xf57700ef, - 0xf42b000a, - 0x08a40000, - 0x09b6fff8, - 0xf03c0166, - 0xf99e02ee, - 0xfe59f65d, - 0xfb54fd87, - 0x1141ffb2, - 0x009dff30, - 0x05e30000, - 0xff060705, - 0x085408a0, - 0xf425fc59, - 0xfa1d042a, - 0xfc78f67a, - 0xf7acf60e, - 0x075a0766, - 0x05e305e3, - 0xf8a6f89a, - 0xf7acf60e, - 0x03880986, - 0xfa1d042a, - 0x0bdb03a7, - 0x085408a0, - 0x00faf8fb, - 0x05e30000, - 0xff06f8fb, - 0x0854f760, - 0xf42503a7, - 0xfa1dfbd6, - 0xfc780986, - 0xf7ac09f2, - 0x075af89a, - 0x05e3fa1d, - 0xf8a60766, - 0xf7ac09f2, - 0x0388f67a, - 0xfa1dfbd6, - 0x0bdbfc59, - 0x0854f760, - 0x00fa0705, - 0x05e30000, - 0xff060705, - 0x085408a0, - 0xf425fc59, - 0xfa1d042a, - 0xfc78f67a, - 0xf7acf60e, - 0x075a0766, - 0x05e305e3, - 0xf8a6f89a, - 0xf7acf60e, - 0x03880986, - 0xfa1d042a, - 0x0bdb03a7, - 0x085408a0, - 0x00faf8fb, - 0x05e30000, - 0xff06f8fb, - 0x0854f760, - 0xf42503a7, - 0xfa1dfbd6, - 0xfc780986, - 0xf7ac09f2, - 0x075af89a, - 0x05e3fa1d, - 0xf8a60766, - 0xf7ac09f2, - 0x0388f67a, - 0xfa1dfbd6, - 0x0bdbfc59, - 0x0854f760, - 0x00fa0705, - 0xfa58fa58, - 0xf8f0fe00, - 0x0448073d, - 0xfdc9fe46, - 0xf9910258, - 0x089d0407, - 0xfd5cf71a, - 0x02affde0, - 0x083e0496, - 0xff5a0740, - 0xff7afd97, - 0x00fe01f1, - 0x0009082e, - 0xfa94ff75, - 0xfecdf8ea, - 0xffb0f693, - 0xfd2cfa58, - 0x0433ff16, - 0xfba405dd, - 0xfa610341, - 0x06a606cb, - 0x0039fd2d, - 0x0677fa97, - 0x01fa05e0, - 0xf896003e, - 0x075a068b, - 0x012cfc3e, - 0xfa23f98d, - 0xfc7cfd43, - 0xff90fc0d, - 0x01c10982, - 0x00c601d6, - 0xfd2cfd2c, - 0x01d600c6, - 0x098201c1, - 0xfc0dff90, - 0xfd43fc7c, - 0xf98dfa23, - 0xfc3e012c, - 0x068b075a, - 0x003ef896, - 0x05e001fa, - 0xfa970677, - 0xfd2d0039, - 0x06cb06a6, - 0x0341fa61, - 0x05ddfba4, - 0xff160433, - 0xfa58fd2c, - 0xf693ffb0, - 0xf8eafecd, - 0xff75fa94, - 0x082e0009, - 0x01f100fe, - 0xfd97ff7a, - 0x0740ff5a, - 0x0496083e, - 0xfde002af, - 0xf71afd5c, - 0x0407089d, - 0x0258f991, - 0xfe46fdc9, - 0x073d0448, - 0xfe00f8f0, - 0xfd2cfd2c, - 0xfce00500, - 0xfc09fddc, - 0xfe680157, - 0x04c70571, - 0xfc3aff21, - 0xfcd70228, - 0x056d0277, - 0x0200fe00, - 0x0022f927, - 0xfe3c032b, - 0xfc44ff3c, - 0x03e9fbdb, - 0x04570313, - 0x04c9ff5c, - 0x000d03b8, - 0xfa580000, - 0xfbe900d2, - 0xf9d0fe0b, - 0x0125fdf9, - 0x042501bf, - 0x0328fa2b, - 0xffa902f0, - 0xfa250157, - 0x0200fe00, - 0x03740438, - 0xff0405fd, - 0x030cfe52, - 0x0037fb39, - 0xff6904c5, - 0x04f8fd23, - 0xfd31fc1b, - 0xfd2cfd2c, - 0xfc1bfd31, - 0xfd2304f8, - 0x04c5ff69, - 0xfb390037, - 0xfe52030c, - 0x05fdff04, - 0x04380374, - 0xfe000200, - 0x0157fa25, - 0x02f0ffa9, - 0xfa2b0328, - 0x01bf0425, - 0xfdf90125, - 0xfe0bf9d0, - 0x00d2fbe9, - 0x0000fa58, - 0x03b8000d, - 0xff5c04c9, - 0x03130457, - 0xfbdb03e9, - 0xff3cfc44, - 0x032bfe3c, - 0xf9270022, - 0xfe000200, - 0x0277056d, - 0x0228fcd7, - 0xff21fc3a, - 0x057104c7, - 0x0157fe68, - 0xfddcfc09, - 0x0500fce0, - 0xfd2cfd2c, - 0x0500fce0, - 0xfddcfc09, - 0x0157fe68, - 0x057104c7, - 0xff21fc3a, - 0x0228fcd7, - 0x0277056d, - 0xfe000200, - 0xf9270022, - 0x032bfe3c, - 0xff3cfc44, - 0xfbdb03e9, - 0x03130457, - 0xff5c04c9, - 0x03b8000d, - 0x0000fa58, - 0x00d2fbe9, - 0xfe0bf9d0, - 0xfdf90125, - 0x01bf0425, - 0xfa2b0328, - 0x02f0ffa9, - 0x0157fa25, - 0xfe000200, - 0x04380374, - 0x05fdff04, - 0xfe52030c, - 0xfb390037, - 0x04c5ff69, - 0xfd2304f8, - 0xfc1bfd31, - 0xfd2cfd2c, - 0xfd31fc1b, - 0x04f8fd23, - 0xff6904c5, - 0x0037fb39, - 0x030cfe52, - 0xff0405fd, - 0x03740438, - 0x0200fe00, - 0xfa250157, - 0xffa902f0, - 0x0328fa2b, - 0x042501bf, - 0x0125fdf9, - 0xf9d0fe0b, - 0xfbe900d2, - 0xfa580000, - 0x000d03b8, - 0x04c9ff5c, - 0x04570313, - 0x03e9fbdb, - 0xfc44ff3c, - 0xfe3c032b, - 0x0022f927, - 0x0200fe00, - 0x056d0277, - 0xfcd70228, - 0xfc3aff21, - 0x04c70571, - 0xfe680157, - 0xfc09fddc, - 0xfce00500, - 0x05a80000, - 0xff1006be, - 0x0800084a, - 0xf49cfc7e, - 0xfa580400, - 0xfc9cf6da, - 0xf800f672, - 0x0710071c, - 0x05a805a8, - 0xf8f0f8e4, - 0xf800f672, - 0x03640926, - 0xfa580400, - 0x0b640382, - 0x0800084a, - 0x00f0f942, - 0x05a80000, - 0xff10f942, - 0x0800f7b6, - 0xf49c0382, - 0xfa58fc00, - 0xfc9c0926, - 0xf800098e, - 0x0710f8e4, - 0x05a8fa58, - 0xf8f0071c, - 0xf800098e, - 0x0364f6da, - 0xfa58fc00, - 0x0b64fc7e, - 0x0800f7b6, - 0x00f006be, - 0x05a80000, - 0xff1006be, - 0x0800084a, - 0xf49cfc7e, - 0xfa580400, - 0xfc9cf6da, - 0xf800f672, - 0x0710071c, - 0x05a805a8, - 0xf8f0f8e4, - 0xf800f672, - 0x03640926, - 0xfa580400, - 0x0b640382, - 0x0800084a, - 0x00f0f942, - 0x05a80000, - 0xff10f942, - 0x0800f7b6, - 0xf49c0382, - 0xfa58fc00, - 0xfc9c0926, - 0xf800098e, - 0x0710f8e4, - 0x05a8fa58, - 0xf8f0071c, - 0xf800098e, - 0x0364f6da, - 0xfa58fc00, - 0x0b64fc7e, - 0x0800f7b6, - 0x00f006be, -}; - -static const u32 intlv_tbl_rev0[] = { - 0x00802070, - 0x0671188d, - 0x0a60192c, - 0x0a300e46, - 0x00c1188d, - 0x080024d2, - 0x00000070, -}; - -static const u16 pilot_tbl_rev0[] = { - 0xff08, - 0xff08, - 0xff08, - 0xff08, - 0xff08, - 0xff08, - 0xff08, - 0xff08, - 0x80d5, - 0x80d5, - 0x80d5, - 0x80d5, - 0x80d5, - 0x80d5, - 0x80d5, - 0x80d5, - 0xff0a, - 0xff82, - 0xffa0, - 0xff28, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xff82, - 0xffa0, - 0xff28, - 0xff0a, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xf83f, - 0xfa1f, - 0xfa97, - 0xfab5, - 0xf2bd, - 0xf0bf, - 0xffff, - 0xffff, - 0xf017, - 0xf815, - 0xf215, - 0xf095, - 0xf035, - 0xf01d, - 0xffff, - 0xffff, - 0xff08, - 0xff02, - 0xff80, - 0xff20, - 0xff08, - 0xff02, - 0xff80, - 0xff20, - 0xf01f, - 0xf817, - 0xfa15, - 0xf295, - 0xf0b5, - 0xf03d, - 0xffff, - 0xffff, - 0xf82a, - 0xfa0a, - 0xfa82, - 0xfaa0, - 0xf2a8, - 0xf0aa, - 0xffff, - 0xffff, - 0xf002, - 0xf800, - 0xf200, - 0xf080, - 0xf020, - 0xf008, - 0xffff, - 0xffff, - 0xf00a, - 0xf802, - 0xfa00, - 0xf280, - 0xf0a0, - 0xf028, - 0xffff, - 0xffff, -}; - -static const u32 pltlut_tbl_rev0[] = { - 0x76540123, - 0x62407351, - 0x76543201, - 0x76540213, - 0x76540123, - 0x76430521, -}; - -static const u32 tdi_tbl20_ant0_rev0[] = { - 0x00091226, - 0x000a1429, - 0x000b56ad, - 0x000c58b0, - 0x000d5ab3, - 0x000e9cb6, - 0x000f9eba, - 0x0000c13d, - 0x00020301, - 0x00030504, - 0x00040708, - 0x0005090b, - 0x00064b8e, - 0x00095291, - 0x000a5494, - 0x000b9718, - 0x000c9927, - 0x000d9b2a, - 0x000edd2e, - 0x000fdf31, - 0x000101b4, - 0x000243b7, - 0x000345bb, - 0x000447be, - 0x00058982, - 0x00068c05, - 0x00099309, - 0x000a950c, - 0x000bd78f, - 0x000cd992, - 0x000ddb96, - 0x000f1d99, - 0x00005fa8, - 0x0001422c, - 0x0002842f, - 0x00038632, - 0x00048835, - 0x0005ca38, - 0x0006ccbc, - 0x0009d3bf, - 0x000b1603, - 0x000c1806, - 0x000d1a0a, - 0x000e1c0d, - 0x000f5e10, - 0x00008093, - 0x00018297, - 0x0002c49a, - 0x0003c680, - 0x0004c880, - 0x00060b00, - 0x00070d00, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 tdi_tbl20_ant1_rev0[] = { - 0x00014b26, - 0x00028d29, - 0x000393ad, - 0x00049630, - 0x0005d833, - 0x0006da36, - 0x00099c3a, - 0x000a9e3d, - 0x000bc081, - 0x000cc284, - 0x000dc488, - 0x000f068b, - 0x0000488e, - 0x00018b91, - 0x0002d214, - 0x0003d418, - 0x0004d6a7, - 0x000618aa, - 0x00071aae, - 0x0009dcb1, - 0x000b1eb4, - 0x000c0137, - 0x000d033b, - 0x000e053e, - 0x000f4702, - 0x00008905, - 0x00020c09, - 0x0003128c, - 0x0004148f, - 0x00051712, - 0x00065916, - 0x00091b19, - 0x000a1d28, - 0x000b5f2c, - 0x000c41af, - 0x000d43b2, - 0x000e85b5, - 0x000f87b8, - 0x0000c9bc, - 0x00024cbf, - 0x00035303, - 0x00045506, - 0x0005978a, - 0x0006998d, - 0x00095b90, - 0x000a5d93, - 0x000b9f97, - 0x000c821a, - 0x000d8400, - 0x000ec600, - 0x000fc800, - 0x00010a00, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 tdi_tbl40_ant0_rev0[] = { - 0x0011a346, - 0x00136ccf, - 0x0014f5d9, - 0x001641e2, - 0x0017cb6b, - 0x00195475, - 0x001b2383, - 0x001cad0c, - 0x001e7616, - 0x0000821f, - 0x00020ba8, - 0x0003d4b2, - 0x00056447, - 0x00072dd0, - 0x0008b6da, - 0x000a02e3, - 0x000b8c6c, - 0x000d15f6, - 0x0011e484, - 0x0013ae0d, - 0x00153717, - 0x00168320, - 0x00180ca9, - 0x00199633, - 0x001b6548, - 0x001ceed1, - 0x001eb7db, - 0x0000c3e4, - 0x00024d6d, - 0x000416f7, - 0x0005a585, - 0x00076f0f, - 0x0008f818, - 0x000a4421, - 0x000bcdab, - 0x000d9734, - 0x00122649, - 0x0013efd2, - 0x001578dc, - 0x0016c4e5, - 0x00184e6e, - 0x001a17f8, - 0x001ba686, - 0x001d3010, - 0x001ef999, - 0x00010522, - 0x00028eac, - 0x00045835, - 0x0005e74a, - 0x0007b0d3, - 0x00093a5d, - 0x000a85e6, - 0x000c0f6f, - 0x000dd8f9, - 0x00126787, - 0x00143111, - 0x0015ba9a, - 0x00170623, - 0x00188fad, - 0x001a5936, - 0x001be84b, - 0x001db1d4, - 0x001f3b5e, - 0x000146e7, - 0x00031070, - 0x000499fa, - 0x00062888, - 0x0007f212, - 0x00097b9b, - 0x000ac7a4, - 0x000c50ae, - 0x000e1a37, - 0x0012a94c, - 0x001472d5, - 0x0015fc5f, - 0x00174868, - 0x0018d171, - 0x001a9afb, - 0x001c2989, - 0x001df313, - 0x001f7c9c, - 0x000188a5, - 0x000351af, - 0x0004db38, - 0x0006aa4d, - 0x000833d7, - 0x0009bd60, - 0x000b0969, - 0x000c9273, - 0x000e5bfc, - 0x00132a8a, - 0x0014b414, - 0x00163d9d, - 0x001789a6, - 0x001912b0, - 0x001adc39, - 0x001c6bce, - 0x001e34d8, - 0x001fbe61, - 0x0001ca6a, - 0x00039374, - 0x00051cfd, - 0x0006ec0b, - 0x00087515, - 0x0009fe9e, - 0x000b4aa7, - 0x000cd3b1, - 0x000e9d3a, - 0x00000000, - 0x00000000, -}; - -static const u32 tdi_tbl40_ant1_rev0[] = { - 0x001edb36, - 0x000129ca, - 0x0002b353, - 0x00047cdd, - 0x0005c8e6, - 0x000791ef, - 0x00091bf9, - 0x000aaa07, - 0x000c3391, - 0x000dfd1a, - 0x00120923, - 0x0013d22d, - 0x00155c37, - 0x0016eacb, - 0x00187454, - 0x001a3dde, - 0x001b89e7, - 0x001d12f0, - 0x001f1cfa, - 0x00016b88, - 0x00033492, - 0x0004be1b, - 0x00060a24, - 0x0007d32e, - 0x00095d38, - 0x000aec4c, - 0x000c7555, - 0x000e3edf, - 0x00124ae8, - 0x001413f1, - 0x0015a37b, - 0x00172c89, - 0x0018b593, - 0x001a419c, - 0x001bcb25, - 0x001d942f, - 0x001f63b9, - 0x0001ad4d, - 0x00037657, - 0x0004c260, - 0x00068be9, - 0x000814f3, - 0x0009a47c, - 0x000b2d8a, - 0x000cb694, - 0x000e429d, - 0x00128c26, - 0x001455b0, - 0x0015e4ba, - 0x00176e4e, - 0x0018f758, - 0x001a8361, - 0x001c0cea, - 0x001dd674, - 0x001fa57d, - 0x0001ee8b, - 0x0003b795, - 0x0005039e, - 0x0006cd27, - 0x000856b1, - 0x0009e5c6, - 0x000b6f4f, - 0x000cf859, - 0x000e8462, - 0x00130deb, - 0x00149775, - 0x00162603, - 0x0017af8c, - 0x00193896, - 0x001ac49f, - 0x001c4e28, - 0x001e17b2, - 0x0000a6c7, - 0x00023050, - 0x0003f9da, - 0x00054563, - 0x00070eec, - 0x00089876, - 0x000a2704, - 0x000bb08d, - 0x000d3a17, - 0x001185a0, - 0x00134f29, - 0x0014d8b3, - 0x001667c8, - 0x0017f151, - 0x00197adb, - 0x001b0664, - 0x001c8fed, - 0x001e5977, - 0x0000e805, - 0x0002718f, - 0x00043b18, - 0x000586a1, - 0x0007502b, - 0x0008d9b4, - 0x000a68c9, - 0x000bf252, - 0x000dbbdc, - 0x0011c7e5, - 0x001390ee, - 0x00151a78, - 0x0016a906, - 0x00183290, - 0x0019bc19, - 0x001b4822, - 0x001cd12c, - 0x001e9ab5, - 0x00000000, - 0x00000000, -}; - -static const u16 bdi_tbl_rev0[] = { - 0x0070, - 0x0126, - 0x012c, - 0x0246, - 0x048d, - 0x04d2, -}; - -static const u32 chanest_tbl_rev0[] = { - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, -}; - -static const u8 mcs_tbl_rev0[] = { - 0x00, - 0x08, - 0x0a, - 0x10, - 0x12, - 0x19, - 0x1a, - 0x1c, - 0x40, - 0x48, - 0x4a, - 0x50, - 0x52, - 0x59, - 0x5a, - 0x5c, - 0x80, - 0x88, - 0x8a, - 0x90, - 0x92, - 0x99, - 0x9a, - 0x9c, - 0xc0, - 0xc8, - 0xca, - 0xd0, - 0xd2, - 0xd9, - 0xda, - 0xdc, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x01, - 0x02, - 0x04, - 0x08, - 0x09, - 0x0a, - 0x0c, - 0x10, - 0x11, - 0x12, - 0x14, - 0x18, - 0x19, - 0x1a, - 0x1c, - 0x20, - 0x21, - 0x22, - 0x24, - 0x40, - 0x41, - 0x42, - 0x44, - 0x48, - 0x49, - 0x4a, - 0x4c, - 0x50, - 0x51, - 0x52, - 0x54, - 0x58, - 0x59, - 0x5a, - 0x5c, - 0x60, - 0x61, - 0x62, - 0x64, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, -}; - -static const u32 noise_var_tbl0_rev0[] = { - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, -}; - -static const u32 noise_var_tbl1_rev0[] = { - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, -}; - -static const u8 est_pwr_lut_core0_rev0[] = { - 0x50, - 0x4f, - 0x4e, - 0x4d, - 0x4c, - 0x4b, - 0x4a, - 0x49, - 0x48, - 0x47, - 0x46, - 0x45, - 0x44, - 0x43, - 0x42, - 0x41, - 0x40, - 0x3f, - 0x3e, - 0x3d, - 0x3c, - 0x3b, - 0x3a, - 0x39, - 0x38, - 0x37, - 0x36, - 0x35, - 0x34, - 0x33, - 0x32, - 0x31, - 0x30, - 0x2f, - 0x2e, - 0x2d, - 0x2c, - 0x2b, - 0x2a, - 0x29, - 0x28, - 0x27, - 0x26, - 0x25, - 0x24, - 0x23, - 0x22, - 0x21, - 0x20, - 0x1f, - 0x1e, - 0x1d, - 0x1c, - 0x1b, - 0x1a, - 0x19, - 0x18, - 0x17, - 0x16, - 0x15, - 0x14, - 0x13, - 0x12, - 0x11, -}; - -static const u8 est_pwr_lut_core1_rev0[] = { - 0x50, - 0x4f, - 0x4e, - 0x4d, - 0x4c, - 0x4b, - 0x4a, - 0x49, - 0x48, - 0x47, - 0x46, - 0x45, - 0x44, - 0x43, - 0x42, - 0x41, - 0x40, - 0x3f, - 0x3e, - 0x3d, - 0x3c, - 0x3b, - 0x3a, - 0x39, - 0x38, - 0x37, - 0x36, - 0x35, - 0x34, - 0x33, - 0x32, - 0x31, - 0x30, - 0x2f, - 0x2e, - 0x2d, - 0x2c, - 0x2b, - 0x2a, - 0x29, - 0x28, - 0x27, - 0x26, - 0x25, - 0x24, - 0x23, - 0x22, - 0x21, - 0x20, - 0x1f, - 0x1e, - 0x1d, - 0x1c, - 0x1b, - 0x1a, - 0x19, - 0x18, - 0x17, - 0x16, - 0x15, - 0x14, - 0x13, - 0x12, - 0x11, -}; - -static const u8 adj_pwr_lut_core0_rev0[] = { - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, -}; - -static const u8 adj_pwr_lut_core1_rev0[] = { - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, -}; - -static const u32 gainctrl_lut_core0_rev0[] = { - 0x03cc2b44, - 0x03cc2b42, - 0x03cc2b40, - 0x03cc2b3e, - 0x03cc2b3d, - 0x03cc2b3b, - 0x03c82b44, - 0x03c82b42, - 0x03c82b40, - 0x03c82b3e, - 0x03c82b3d, - 0x03c82b3b, - 0x03c82b39, - 0x03c82b38, - 0x03c82b36, - 0x03c82b34, - 0x03c42b44, - 0x03c42b42, - 0x03c42b40, - 0x03c42b3e, - 0x03c42b3d, - 0x03c42b3b, - 0x03c42b39, - 0x03c42b38, - 0x03c42b36, - 0x03c42b34, - 0x03c42b33, - 0x03c42b32, - 0x03c42b30, - 0x03c42b2f, - 0x03c42b2d, - 0x03c02b44, - 0x03c02b42, - 0x03c02b40, - 0x03c02b3e, - 0x03c02b3d, - 0x03c02b3b, - 0x03c02b39, - 0x03c02b38, - 0x03c02b36, - 0x03c02b34, - 0x03b02b44, - 0x03b02b42, - 0x03b02b40, - 0x03b02b3e, - 0x03b02b3d, - 0x03b02b3b, - 0x03b02b39, - 0x03b02b38, - 0x03b02b36, - 0x03b02b34, - 0x03b02b33, - 0x03b02b32, - 0x03b02b30, - 0x03b02b2f, - 0x03b02b2d, - 0x03a02b44, - 0x03a02b42, - 0x03a02b40, - 0x03a02b3e, - 0x03a02b3d, - 0x03a02b3b, - 0x03a02b39, - 0x03a02b38, - 0x03a02b36, - 0x03a02b34, - 0x03902b44, - 0x03902b42, - 0x03902b40, - 0x03902b3e, - 0x03902b3d, - 0x03902b3b, - 0x03902b39, - 0x03902b38, - 0x03902b36, - 0x03902b34, - 0x03902b33, - 0x03902b32, - 0x03902b30, - 0x03802b44, - 0x03802b42, - 0x03802b40, - 0x03802b3e, - 0x03802b3d, - 0x03802b3b, - 0x03802b39, - 0x03802b38, - 0x03802b36, - 0x03802b34, - 0x03802b33, - 0x03802b32, - 0x03802b30, - 0x03802b2f, - 0x03802b2d, - 0x03802b2c, - 0x03802b2b, - 0x03802b2a, - 0x03802b29, - 0x03802b27, - 0x03802b26, - 0x03802b25, - 0x03802b24, - 0x03802b23, - 0x03802b22, - 0x03802b21, - 0x03802b20, - 0x03802b1f, - 0x03802b1e, - 0x03802b1e, - 0x03802b1d, - 0x03802b1c, - 0x03802b1b, - 0x03802b1a, - 0x03802b1a, - 0x03802b19, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x00002b00, -}; - -static const u32 gainctrl_lut_core1_rev0[] = { - 0x03cc2b44, - 0x03cc2b42, - 0x03cc2b40, - 0x03cc2b3e, - 0x03cc2b3d, - 0x03cc2b3b, - 0x03c82b44, - 0x03c82b42, - 0x03c82b40, - 0x03c82b3e, - 0x03c82b3d, - 0x03c82b3b, - 0x03c82b39, - 0x03c82b38, - 0x03c82b36, - 0x03c82b34, - 0x03c42b44, - 0x03c42b42, - 0x03c42b40, - 0x03c42b3e, - 0x03c42b3d, - 0x03c42b3b, - 0x03c42b39, - 0x03c42b38, - 0x03c42b36, - 0x03c42b34, - 0x03c42b33, - 0x03c42b32, - 0x03c42b30, - 0x03c42b2f, - 0x03c42b2d, - 0x03c02b44, - 0x03c02b42, - 0x03c02b40, - 0x03c02b3e, - 0x03c02b3d, - 0x03c02b3b, - 0x03c02b39, - 0x03c02b38, - 0x03c02b36, - 0x03c02b34, - 0x03b02b44, - 0x03b02b42, - 0x03b02b40, - 0x03b02b3e, - 0x03b02b3d, - 0x03b02b3b, - 0x03b02b39, - 0x03b02b38, - 0x03b02b36, - 0x03b02b34, - 0x03b02b33, - 0x03b02b32, - 0x03b02b30, - 0x03b02b2f, - 0x03b02b2d, - 0x03a02b44, - 0x03a02b42, - 0x03a02b40, - 0x03a02b3e, - 0x03a02b3d, - 0x03a02b3b, - 0x03a02b39, - 0x03a02b38, - 0x03a02b36, - 0x03a02b34, - 0x03902b44, - 0x03902b42, - 0x03902b40, - 0x03902b3e, - 0x03902b3d, - 0x03902b3b, - 0x03902b39, - 0x03902b38, - 0x03902b36, - 0x03902b34, - 0x03902b33, - 0x03902b32, - 0x03902b30, - 0x03802b44, - 0x03802b42, - 0x03802b40, - 0x03802b3e, - 0x03802b3d, - 0x03802b3b, - 0x03802b39, - 0x03802b38, - 0x03802b36, - 0x03802b34, - 0x03802b33, - 0x03802b32, - 0x03802b30, - 0x03802b2f, - 0x03802b2d, - 0x03802b2c, - 0x03802b2b, - 0x03802b2a, - 0x03802b29, - 0x03802b27, - 0x03802b26, - 0x03802b25, - 0x03802b24, - 0x03802b23, - 0x03802b22, - 0x03802b21, - 0x03802b20, - 0x03802b1f, - 0x03802b1e, - 0x03802b1e, - 0x03802b1d, - 0x03802b1c, - 0x03802b1b, - 0x03802b1a, - 0x03802b1a, - 0x03802b19, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x03802b18, - 0x00002b00, -}; - -static const u32 iq_lut_core0_rev0[] = { - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, -}; - -static const u32 iq_lut_core1_rev0[] = { - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, - 0x0000007f, -}; - -static const u16 loft_lut_core0_rev0[] = { - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, -}; - -static const u16 loft_lut_core1_rev0[] = { - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, - 0x0000, - 0x0101, - 0x0002, - 0x0103, -}; - -const struct phytbl_info mimophytbl_info_rev0_volatile[] = { - {&bdi_tbl_rev0, sizeof(bdi_tbl_rev0) / sizeof(bdi_tbl_rev0[0]), 21, 0, - 16} - , - {&pltlut_tbl_rev0, sizeof(pltlut_tbl_rev0) / sizeof(pltlut_tbl_rev0[0]), - 20, 0, 32} - , - {&gainctrl_lut_core0_rev0, - sizeof(gainctrl_lut_core0_rev0) / sizeof(gainctrl_lut_core0_rev0[0]), - 26, 192, 32} - , - {&gainctrl_lut_core1_rev0, - sizeof(gainctrl_lut_core1_rev0) / sizeof(gainctrl_lut_core1_rev0[0]), - 27, 192, 32} - , - - {&est_pwr_lut_core0_rev0, - sizeof(est_pwr_lut_core0_rev0) / sizeof(est_pwr_lut_core0_rev0[0]), 26, - 0, 8} - , - {&est_pwr_lut_core1_rev0, - sizeof(est_pwr_lut_core1_rev0) / sizeof(est_pwr_lut_core1_rev0[0]), 27, - 0, 8} - , - {&adj_pwr_lut_core0_rev0, - sizeof(adj_pwr_lut_core0_rev0) / sizeof(adj_pwr_lut_core0_rev0[0]), 26, - 64, 8} - , - {&adj_pwr_lut_core1_rev0, - sizeof(adj_pwr_lut_core1_rev0) / sizeof(adj_pwr_lut_core1_rev0[0]), 27, - 64, 8} - , - {&iq_lut_core0_rev0, - sizeof(iq_lut_core0_rev0) / sizeof(iq_lut_core0_rev0[0]), 26, 320, 32} - , - {&iq_lut_core1_rev0, - sizeof(iq_lut_core1_rev0) / sizeof(iq_lut_core1_rev0[0]), 27, 320, 32} - , - {&loft_lut_core0_rev0, - sizeof(loft_lut_core0_rev0) / sizeof(loft_lut_core0_rev0[0]), 26, 448, - 16} - , - {&loft_lut_core1_rev0, - sizeof(loft_lut_core1_rev0) / sizeof(loft_lut_core1_rev0[0]), 27, 448, - 16} - , -}; - -const struct phytbl_info mimophytbl_info_rev0[] = { - {&frame_struct_rev0, - sizeof(frame_struct_rev0) / sizeof(frame_struct_rev0[0]), 10, 0, 32} - , - {&frame_lut_rev0, sizeof(frame_lut_rev0) / sizeof(frame_lut_rev0[0]), - 24, 0, 8} - , - {&tmap_tbl_rev0, sizeof(tmap_tbl_rev0) / sizeof(tmap_tbl_rev0[0]), 12, - 0, 32} - , - {&tdtrn_tbl_rev0, sizeof(tdtrn_tbl_rev0) / sizeof(tdtrn_tbl_rev0[0]), - 14, 0, 32} - , - {&intlv_tbl_rev0, sizeof(intlv_tbl_rev0) / sizeof(intlv_tbl_rev0[0]), - 13, 0, 32} - , - {&pilot_tbl_rev0, sizeof(pilot_tbl_rev0) / sizeof(pilot_tbl_rev0[0]), - 11, 0, 16} - , - {&tdi_tbl20_ant0_rev0, - sizeof(tdi_tbl20_ant0_rev0) / sizeof(tdi_tbl20_ant0_rev0[0]), 19, 128, - 32} - , - {&tdi_tbl20_ant1_rev0, - sizeof(tdi_tbl20_ant1_rev0) / sizeof(tdi_tbl20_ant1_rev0[0]), 19, 256, - 32} - , - {&tdi_tbl40_ant0_rev0, - sizeof(tdi_tbl40_ant0_rev0) / sizeof(tdi_tbl40_ant0_rev0[0]), 19, 640, - 32} - , - {&tdi_tbl40_ant1_rev0, - sizeof(tdi_tbl40_ant1_rev0) / sizeof(tdi_tbl40_ant1_rev0[0]), 19, 768, - 32} - , - {&chanest_tbl_rev0, - sizeof(chanest_tbl_rev0) / sizeof(chanest_tbl_rev0[0]), 22, 0, 32} - , - {&mcs_tbl_rev0, sizeof(mcs_tbl_rev0) / sizeof(mcs_tbl_rev0[0]), 18, 0, - 8} - , - {&noise_var_tbl0_rev0, - sizeof(noise_var_tbl0_rev0) / sizeof(noise_var_tbl0_rev0[0]), 16, 0, - 32} - , - {&noise_var_tbl1_rev0, - sizeof(noise_var_tbl1_rev0) / sizeof(noise_var_tbl1_rev0[0]), 16, 128, - 32} - , -}; - -const u32 mimophytbl_info_sz_rev0 = - sizeof(mimophytbl_info_rev0) / sizeof(mimophytbl_info_rev0[0]); -const u32 mimophytbl_info_sz_rev0_volatile = - sizeof(mimophytbl_info_rev0_volatile) / - sizeof(mimophytbl_info_rev0_volatile[0]); - -static const u16 ant_swctrl_tbl_rev3[] = { - 0x0082, - 0x0082, - 0x0211, - 0x0222, - 0x0328, - 0x0000, - 0x0000, - 0x0000, - 0x0144, - 0x0000, - 0x0000, - 0x0000, - 0x0188, - 0x0000, - 0x0000, - 0x0000, - 0x0082, - 0x0082, - 0x0211, - 0x0222, - 0x0328, - 0x0000, - 0x0000, - 0x0000, - 0x0144, - 0x0000, - 0x0000, - 0x0000, - 0x0188, - 0x0000, - 0x0000, - 0x0000, -}; - -static const u16 ant_swctrl_tbl_rev3_1[] = { - 0x0022, - 0x0022, - 0x0011, - 0x0022, - 0x0022, - 0x0000, - 0x0000, - 0x0000, - 0x0011, - 0x0000, - 0x0000, - 0x0000, - 0x0022, - 0x0000, - 0x0000, - 0x0000, - 0x0022, - 0x0022, - 0x0011, - 0x0022, - 0x0022, - 0x0000, - 0x0000, - 0x0000, - 0x0011, - 0x0000, - 0x0000, - 0x0000, - 0x0022, - 0x0000, - 0x0000, - 0x0000, -}; - -static const u16 ant_swctrl_tbl_rev3_2[] = { - 0x0088, - 0x0088, - 0x0044, - 0x0088, - 0x0088, - 0x0000, - 0x0000, - 0x0000, - 0x0044, - 0x0000, - 0x0000, - 0x0000, - 0x0088, - 0x0000, - 0x0000, - 0x0000, - 0x0088, - 0x0088, - 0x0044, - 0x0088, - 0x0088, - 0x0000, - 0x0000, - 0x0000, - 0x0044, - 0x0000, - 0x0000, - 0x0000, - 0x0088, - 0x0000, - 0x0000, - 0x0000, -}; - -static const u16 ant_swctrl_tbl_rev3_3[] = { - 0x022, - 0x022, - 0x011, - 0x022, - 0x000, - 0x000, - 0x000, - 0x000, - 0x011, - 0x000, - 0x000, - 0x000, - 0x022, - 0x000, - 0x000, - 0x3cc, - 0x022, - 0x022, - 0x011, - 0x022, - 0x000, - 0x000, - 0x000, - 0x000, - 0x011, - 0x000, - 0x000, - 0x000, - 0x022, - 0x000, - 0x000, - 0x3cc -}; - -static const u32 frame_struct_rev3[] = { - 0x08004a04, - 0x00100000, - 0x01000a05, - 0x00100020, - 0x09804506, - 0x00100030, - 0x09804507, - 0x00100030, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x08004a0c, - 0x00100004, - 0x01000a0d, - 0x00100024, - 0x0980450e, - 0x00100034, - 0x0980450f, - 0x00100034, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000a04, - 0x00100000, - 0x11008a05, - 0x00100020, - 0x1980c506, - 0x00100030, - 0x21810506, - 0x00100030, - 0x21810506, - 0x00100030, - 0x01800504, - 0x00100030, - 0x11808505, - 0x00100030, - 0x29814507, - 0x01100030, - 0x00000a04, - 0x00100000, - 0x11008a05, - 0x00100020, - 0x21810506, - 0x00100030, - 0x21810506, - 0x00100030, - 0x29814507, - 0x01100030, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000a0c, - 0x00100008, - 0x11008a0d, - 0x00100028, - 0x1980c50e, - 0x00100038, - 0x2181050e, - 0x00100038, - 0x2181050e, - 0x00100038, - 0x0180050c, - 0x00100038, - 0x1180850d, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000a0c, - 0x00100008, - 0x11008a0d, - 0x00100028, - 0x2181050e, - 0x00100038, - 0x2181050e, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x08004a04, - 0x00100000, - 0x01000a05, - 0x00100020, - 0x1980c506, - 0x00100030, - 0x1980c506, - 0x00100030, - 0x11808504, - 0x00100030, - 0x3981ca05, - 0x00100030, - 0x29814507, - 0x01100030, - 0x00000000, - 0x00000000, - 0x10008a04, - 0x00100000, - 0x3981ca05, - 0x00100030, - 0x1980c506, - 0x00100030, - 0x29814507, - 0x01100030, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x08004a0c, - 0x00100008, - 0x01000a0d, - 0x00100028, - 0x1980c50e, - 0x00100038, - 0x1980c50e, - 0x00100038, - 0x1180850c, - 0x00100038, - 0x3981ca0d, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000000, - 0x00000000, - 0x10008a0c, - 0x00100008, - 0x3981ca0d, - 0x00100038, - 0x1980c50e, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x40021404, - 0x00100000, - 0x02001405, - 0x00100040, - 0x0b004a06, - 0x01900060, - 0x13008a06, - 0x01900060, - 0x13008a06, - 0x01900060, - 0x43020a04, - 0x00100060, - 0x1b00ca05, - 0x00100060, - 0x23010a07, - 0x01500060, - 0x40021404, - 0x00100000, - 0x1a00d405, - 0x00100040, - 0x13008a06, - 0x01900060, - 0x13008a06, - 0x01900060, - 0x23010a07, - 0x01500060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x00100010, - 0x0200140d, - 0x00100050, - 0x0b004a0e, - 0x01900070, - 0x13008a0e, - 0x01900070, - 0x13008a0e, - 0x01900070, - 0x43020a0c, - 0x00100070, - 0x1b00ca0d, - 0x00100070, - 0x23010a0f, - 0x01500070, - 0x4002140c, - 0x00100010, - 0x1a00d40d, - 0x00100050, - 0x13008a0e, - 0x01900070, - 0x13008a0e, - 0x01900070, - 0x23010a0f, - 0x01500070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x50029404, - 0x00100000, - 0x32019405, - 0x00100040, - 0x0b004a06, - 0x01900060, - 0x0b004a06, - 0x01900060, - 0x5b02ca04, - 0x00100060, - 0x3b01d405, - 0x00100060, - 0x23010a07, - 0x01500060, - 0x00000000, - 0x00000000, - 0x5802d404, - 0x00100000, - 0x3b01d405, - 0x00100060, - 0x0b004a06, - 0x01900060, - 0x23010a07, - 0x01500060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x5002940c, - 0x00100010, - 0x3201940d, - 0x00100050, - 0x0b004a0e, - 0x01900070, - 0x0b004a0e, - 0x01900070, - 0x5b02ca0c, - 0x00100070, - 0x3b01d40d, - 0x00100070, - 0x23010a0f, - 0x01500070, - 0x00000000, - 0x00000000, - 0x5802d40c, - 0x00100010, - 0x3b01d40d, - 0x00100070, - 0x0b004a0e, - 0x01900070, - 0x23010a0f, - 0x01500070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x40021404, - 0x000f4800, - 0x62031405, - 0x00100040, - 0x53028a06, - 0x01900060, - 0x53028a07, - 0x01900060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x000f4808, - 0x6203140d, - 0x00100048, - 0x53028a0e, - 0x01900068, - 0x53028a0f, - 0x01900068, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000a0c, - 0x00100004, - 0x11008a0d, - 0x00100024, - 0x1980c50e, - 0x00100034, - 0x2181050e, - 0x00100034, - 0x2181050e, - 0x00100034, - 0x0180050c, - 0x00100038, - 0x1180850d, - 0x00100038, - 0x1181850d, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000a0c, - 0x00100008, - 0x11008a0d, - 0x00100028, - 0x2181050e, - 0x00100038, - 0x2181050e, - 0x00100038, - 0x1181850d, - 0x00100038, - 0x2981450f, - 0x01100038, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x08004a04, - 0x00100000, - 0x01000a05, - 0x00100020, - 0x0180c506, - 0x00100030, - 0x0180c506, - 0x00100030, - 0x2180c50c, - 0x00100030, - 0x49820a0d, - 0x0016a130, - 0x41824a0d, - 0x0016a130, - 0x2981450f, - 0x01100030, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x2000ca0c, - 0x00100000, - 0x49820a0d, - 0x0016a130, - 0x1980c50e, - 0x00100030, - 0x41824a0d, - 0x0016a130, - 0x2981450f, - 0x01100030, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x00100008, - 0x0200140d, - 0x00100048, - 0x0b004a0e, - 0x01900068, - 0x13008a0e, - 0x01900068, - 0x13008a0e, - 0x01900068, - 0x43020a0c, - 0x00100070, - 0x1b00ca0d, - 0x00100070, - 0x1b014a0d, - 0x00100070, - 0x23010a0f, - 0x01500070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x00100010, - 0x1a00d40d, - 0x00100050, - 0x13008a0e, - 0x01900070, - 0x13008a0e, - 0x01900070, - 0x1b014a0d, - 0x00100070, - 0x23010a0f, - 0x01500070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x50029404, - 0x00100000, - 0x32019405, - 0x00100040, - 0x03004a06, - 0x01900060, - 0x03004a06, - 0x01900060, - 0x6b030a0c, - 0x00100060, - 0x4b02140d, - 0x0016a160, - 0x4302540d, - 0x0016a160, - 0x23010a0f, - 0x01500060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x6b03140c, - 0x00100060, - 0x4b02140d, - 0x0016a160, - 0x0b004a0e, - 0x01900060, - 0x4302540d, - 0x0016a160, - 0x23010a0f, - 0x01500060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x40021404, - 0x00100000, - 0x1a00d405, - 0x00100040, - 0x53028a06, - 0x01900060, - 0x5b02ca06, - 0x01900060, - 0x5b02ca06, - 0x01900060, - 0x43020a04, - 0x00100060, - 0x1b00ca05, - 0x00100060, - 0x53028a07, - 0x0190c060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x00100010, - 0x1a00d40d, - 0x00100050, - 0x53028a0e, - 0x01900070, - 0x5b02ca0e, - 0x01900070, - 0x5b02ca0e, - 0x01900070, - 0x43020a0c, - 0x00100070, - 0x1b00ca0d, - 0x00100070, - 0x53028a0f, - 0x0190c070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x40021404, - 0x00100000, - 0x1a00d405, - 0x00100040, - 0x5b02ca06, - 0x01900060, - 0x5b02ca06, - 0x01900060, - 0x53028a07, - 0x0190c060, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x4002140c, - 0x00100010, - 0x1a00d40d, - 0x00100050, - 0x5b02ca0e, - 0x01900070, - 0x5b02ca0e, - 0x01900070, - 0x53028a0f, - 0x0190c070, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u16 pilot_tbl_rev3[] = { - 0xff08, - 0xff08, - 0xff08, - 0xff08, - 0xff08, - 0xff08, - 0xff08, - 0xff08, - 0x80d5, - 0x80d5, - 0x80d5, - 0x80d5, - 0x80d5, - 0x80d5, - 0x80d5, - 0x80d5, - 0xff0a, - 0xff82, - 0xffa0, - 0xff28, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xff82, - 0xffa0, - 0xff28, - 0xff0a, - 0xffff, - 0xffff, - 0xffff, - 0xffff, - 0xf83f, - 0xfa1f, - 0xfa97, - 0xfab5, - 0xf2bd, - 0xf0bf, - 0xffff, - 0xffff, - 0xf017, - 0xf815, - 0xf215, - 0xf095, - 0xf035, - 0xf01d, - 0xffff, - 0xffff, - 0xff08, - 0xff02, - 0xff80, - 0xff20, - 0xff08, - 0xff02, - 0xff80, - 0xff20, - 0xf01f, - 0xf817, - 0xfa15, - 0xf295, - 0xf0b5, - 0xf03d, - 0xffff, - 0xffff, - 0xf82a, - 0xfa0a, - 0xfa82, - 0xfaa0, - 0xf2a8, - 0xf0aa, - 0xffff, - 0xffff, - 0xf002, - 0xf800, - 0xf200, - 0xf080, - 0xf020, - 0xf008, - 0xffff, - 0xffff, - 0xf00a, - 0xf802, - 0xfa00, - 0xf280, - 0xf0a0, - 0xf028, - 0xffff, - 0xffff, -}; - -static const u32 tmap_tbl_rev3[] = { - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0xf1111110, - 0x11111111, - 0x11f11111, - 0x00000111, - 0x11000000, - 0x1111f111, - 0x11111111, - 0x111111f1, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x000aa888, - 0x88880000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa2222220, - 0x22222222, - 0x22c22222, - 0x00000222, - 0x22000000, - 0x2222a222, - 0x22222222, - 0x222222a2, - 0xf1111110, - 0x11111111, - 0x11f11111, - 0x00011111, - 0x11110000, - 0x1111f111, - 0x11111111, - 0x111111f1, - 0xa8aa88a0, - 0xa88888a8, - 0xa8a8a88a, - 0x00088aaa, - 0xaaaa0000, - 0xa8a8aa88, - 0xa88aaaaa, - 0xaaaa8a8a, - 0xaaa8aaa0, - 0x8aaa8aaa, - 0xaa8a8a8a, - 0x000aaa88, - 0x8aaa0000, - 0xaaa8a888, - 0x8aa88a8a, - 0x8a88a888, - 0x08080a00, - 0x0a08080a, - 0x080a0a08, - 0x00080808, - 0x080a0000, - 0x080a0808, - 0x080a0808, - 0x0a0a0a08, - 0xa0a0a0a0, - 0x80a0a080, - 0x8080a0a0, - 0x00008080, - 0x80a00000, - 0x80a080a0, - 0xa080a0a0, - 0x8080a0a0, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x99999000, - 0x9b9b99bb, - 0x9bb99999, - 0x9999b9b9, - 0x9b99bb90, - 0x9bbbbb9b, - 0x9b9b9bb9, - 0x00000999, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00aaa888, - 0x22000000, - 0x2222b222, - 0x22222222, - 0x222222b2, - 0xb2222220, - 0x22222222, - 0x22d22222, - 0x00000222, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x33000000, - 0x3333b333, - 0x33333333, - 0x333333b3, - 0xb3333330, - 0x33333333, - 0x33d33333, - 0x00000333, - 0x22000000, - 0x2222a222, - 0x22222222, - 0x222222a2, - 0xa2222220, - 0x22222222, - 0x22c22222, - 0x00000222, - 0x99b99b00, - 0x9b9b99bb, - 0x9bb99999, - 0x9999b9b9, - 0x9b99bb99, - 0x9bbbbb9b, - 0x9b9b9bb9, - 0x00000999, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa88, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x08aaa888, - 0x22222200, - 0x2222f222, - 0x22222222, - 0x222222f2, - 0x22222222, - 0x22222222, - 0x22f22222, - 0x00000222, - 0x11000000, - 0x1111f111, - 0x11111111, - 0x11111111, - 0xf1111111, - 0x11111111, - 0x11f11111, - 0x01111111, - 0xbb9bb900, - 0xb9b9bb99, - 0xb99bbbbb, - 0xbbbb9b9b, - 0xb9bb99bb, - 0xb99999b9, - 0xb9b9b99b, - 0x00000bbb, - 0xaa000000, - 0xa8a8aa88, - 0xa88aaaaa, - 0xaaaa8a8a, - 0xa8aa88aa, - 0xa88888a8, - 0xa8a8a88a, - 0x0a888aaa, - 0xaa000000, - 0xa8a8aa88, - 0xa88aaaaa, - 0xaaaa8a8a, - 0xa8aa88a0, - 0xa88888a8, - 0xa8a8a88a, - 0x00000aaa, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0xbbbbbb00, - 0x999bbbbb, - 0x9bb99b9b, - 0xb9b9b9bb, - 0xb9b99bbb, - 0xb9b9b9bb, - 0xb9bb9b99, - 0x00000999, - 0x8a000000, - 0xaa88a888, - 0xa88888aa, - 0xa88a8a88, - 0xa88aa88a, - 0x88a8aaaa, - 0xa8aa8aaa, - 0x0888a88a, - 0x0b0b0b00, - 0x090b0b0b, - 0x0b090b0b, - 0x0909090b, - 0x09090b0b, - 0x09090b0b, - 0x09090b09, - 0x00000909, - 0x0a000000, - 0x0a080808, - 0x080a080a, - 0x080a0a08, - 0x080a080a, - 0x0808080a, - 0x0a0a0a08, - 0x0808080a, - 0xb0b0b000, - 0x9090b0b0, - 0x90b09090, - 0xb0b0b090, - 0xb0b090b0, - 0x90b0b0b0, - 0xb0b09090, - 0x00000090, - 0x80000000, - 0xa080a080, - 0xa08080a0, - 0xa0808080, - 0xa080a080, - 0x80a0a0a0, - 0xa0a080a0, - 0x00a0a0a0, - 0x22000000, - 0x2222f222, - 0x22222222, - 0x222222f2, - 0xf2222220, - 0x22222222, - 0x22f22222, - 0x00000222, - 0x11000000, - 0x1111f111, - 0x11111111, - 0x111111f1, - 0xf1111110, - 0x11111111, - 0x11f11111, - 0x00000111, - 0x33000000, - 0x3333f333, - 0x33333333, - 0x333333f3, - 0xf3333330, - 0x33333333, - 0x33f33333, - 0x00000333, - 0x22000000, - 0x2222f222, - 0x22222222, - 0x222222f2, - 0xf2222220, - 0x22222222, - 0x22f22222, - 0x00000222, - 0x99000000, - 0x9b9b99bb, - 0x9bb99999, - 0x9999b9b9, - 0x9b99bb90, - 0x9bbbbb9b, - 0x9b9b9bb9, - 0x00000999, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88888000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00aaa888, - 0x88a88a00, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa88, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa88, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x08aaa888, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 intlv_tbl_rev3[] = { - 0x00802070, - 0x0671188d, - 0x0a60192c, - 0x0a300e46, - 0x00c1188d, - 0x080024d2, - 0x00000070, -}; - -static const u32 tdtrn_tbl_rev3[] = { - 0x061c061c, - 0x0050ee68, - 0xf592fe36, - 0xfe5212f6, - 0x00000c38, - 0xfe5212f6, - 0xf592fe36, - 0x0050ee68, - 0x061c061c, - 0xee680050, - 0xfe36f592, - 0x12f6fe52, - 0x0c380000, - 0x12f6fe52, - 0xfe36f592, - 0xee680050, - 0x061c061c, - 0x0050ee68, - 0xf592fe36, - 0xfe5212f6, - 0x00000c38, - 0xfe5212f6, - 0xf592fe36, - 0x0050ee68, - 0x061c061c, - 0xee680050, - 0xfe36f592, - 0x12f6fe52, - 0x0c380000, - 0x12f6fe52, - 0xfe36f592, - 0xee680050, - 0x05e305e3, - 0x004def0c, - 0xf5f3fe47, - 0xfe611246, - 0x00000bc7, - 0xfe611246, - 0xf5f3fe47, - 0x004def0c, - 0x05e305e3, - 0xef0c004d, - 0xfe47f5f3, - 0x1246fe61, - 0x0bc70000, - 0x1246fe61, - 0xfe47f5f3, - 0xef0c004d, - 0x05e305e3, - 0x004def0c, - 0xf5f3fe47, - 0xfe611246, - 0x00000bc7, - 0xfe611246, - 0xf5f3fe47, - 0x004def0c, - 0x05e305e3, - 0xef0c004d, - 0xfe47f5f3, - 0x1246fe61, - 0x0bc70000, - 0x1246fe61, - 0xfe47f5f3, - 0xef0c004d, - 0xfa58fa58, - 0xf895043b, - 0xff4c09c0, - 0xfbc6ffa8, - 0xfb84f384, - 0x0798f6f9, - 0x05760122, - 0x058409f6, - 0x0b500000, - 0x05b7f542, - 0x08860432, - 0x06ddfee7, - 0xfb84f384, - 0xf9d90664, - 0xf7e8025c, - 0x00fff7bd, - 0x05a805a8, - 0xf7bd00ff, - 0x025cf7e8, - 0x0664f9d9, - 0xf384fb84, - 0xfee706dd, - 0x04320886, - 0xf54205b7, - 0x00000b50, - 0x09f60584, - 0x01220576, - 0xf6f90798, - 0xf384fb84, - 0xffa8fbc6, - 0x09c0ff4c, - 0x043bf895, - 0x02d402d4, - 0x07de0270, - 0xfc96079c, - 0xf90afe94, - 0xfe00ff2c, - 0x02d4065d, - 0x092a0096, - 0x0014fbb8, - 0xfd2cfd2c, - 0x076afb3c, - 0x0096f752, - 0xf991fd87, - 0xfb2c0200, - 0xfeb8f960, - 0x08e0fc96, - 0x049802a8, - 0xfd2cfd2c, - 0x02a80498, - 0xfc9608e0, - 0xf960feb8, - 0x0200fb2c, - 0xfd87f991, - 0xf7520096, - 0xfb3c076a, - 0xfd2cfd2c, - 0xfbb80014, - 0x0096092a, - 0x065d02d4, - 0xff2cfe00, - 0xfe94f90a, - 0x079cfc96, - 0x027007de, - 0x02d402d4, - 0x027007de, - 0x079cfc96, - 0xfe94f90a, - 0xff2cfe00, - 0x065d02d4, - 0x0096092a, - 0xfbb80014, - 0xfd2cfd2c, - 0xfb3c076a, - 0xf7520096, - 0xfd87f991, - 0x0200fb2c, - 0xf960feb8, - 0xfc9608e0, - 0x02a80498, - 0xfd2cfd2c, - 0x049802a8, - 0x08e0fc96, - 0xfeb8f960, - 0xfb2c0200, - 0xf991fd87, - 0x0096f752, - 0x076afb3c, - 0xfd2cfd2c, - 0x0014fbb8, - 0x092a0096, - 0x02d4065d, - 0xfe00ff2c, - 0xf90afe94, - 0xfc96079c, - 0x07de0270, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x062a0000, - 0xfefa0759, - 0x08b80908, - 0xf396fc2d, - 0xf9d6045c, - 0xfc4ef608, - 0xf748f596, - 0x07b207bf, - 0x062a062a, - 0xf84ef841, - 0xf748f596, - 0x03b209f8, - 0xf9d6045c, - 0x0c6a03d3, - 0x08b80908, - 0x0106f8a7, - 0x062a0000, - 0xfefaf8a7, - 0x08b8f6f8, - 0xf39603d3, - 0xf9d6fba4, - 0xfc4e09f8, - 0xf7480a6a, - 0x07b2f841, - 0x062af9d6, - 0xf84e07bf, - 0xf7480a6a, - 0x03b2f608, - 0xf9d6fba4, - 0x0c6afc2d, - 0x08b8f6f8, - 0x01060759, - 0x062a0000, - 0xfefa0759, - 0x08b80908, - 0xf396fc2d, - 0xf9d6045c, - 0xfc4ef608, - 0xf748f596, - 0x07b207bf, - 0x062a062a, - 0xf84ef841, - 0xf748f596, - 0x03b209f8, - 0xf9d6045c, - 0x0c6a03d3, - 0x08b80908, - 0x0106f8a7, - 0x062a0000, - 0xfefaf8a7, - 0x08b8f6f8, - 0xf39603d3, - 0xf9d6fba4, - 0xfc4e09f8, - 0xf7480a6a, - 0x07b2f841, - 0x062af9d6, - 0xf84e07bf, - 0xf7480a6a, - 0x03b2f608, - 0xf9d6fba4, - 0x0c6afc2d, - 0x08b8f6f8, - 0x01060759, - 0x061c061c, - 0xff30009d, - 0xffb21141, - 0xfd87fb54, - 0xf65dfe59, - 0x02eef99e, - 0x0166f03c, - 0xfff809b6, - 0x000008a4, - 0x000af42b, - 0x00eff577, - 0xfa840bf2, - 0xfc02ff51, - 0x08260f67, - 0xfff0036f, - 0x0842f9c3, - 0x00000000, - 0x063df7be, - 0xfc910010, - 0xf099f7da, - 0x00af03fe, - 0xf40e057c, - 0x0a89ff11, - 0x0bd5fff6, - 0xf75c0000, - 0xf64a0008, - 0x0fc4fe9a, - 0x0662fd12, - 0x01a709a3, - 0x04ac0279, - 0xeebf004e, - 0xff6300d0, - 0xf9e4f9e4, - 0x00d0ff63, - 0x004eeebf, - 0x027904ac, - 0x09a301a7, - 0xfd120662, - 0xfe9a0fc4, - 0x0008f64a, - 0x0000f75c, - 0xfff60bd5, - 0xff110a89, - 0x057cf40e, - 0x03fe00af, - 0xf7daf099, - 0x0010fc91, - 0xf7be063d, - 0x00000000, - 0xf9c30842, - 0x036ffff0, - 0x0f670826, - 0xff51fc02, - 0x0bf2fa84, - 0xf57700ef, - 0xf42b000a, - 0x08a40000, - 0x09b6fff8, - 0xf03c0166, - 0xf99e02ee, - 0xfe59f65d, - 0xfb54fd87, - 0x1141ffb2, - 0x009dff30, - 0x05e30000, - 0xff060705, - 0x085408a0, - 0xf425fc59, - 0xfa1d042a, - 0xfc78f67a, - 0xf7acf60e, - 0x075a0766, - 0x05e305e3, - 0xf8a6f89a, - 0xf7acf60e, - 0x03880986, - 0xfa1d042a, - 0x0bdb03a7, - 0x085408a0, - 0x00faf8fb, - 0x05e30000, - 0xff06f8fb, - 0x0854f760, - 0xf42503a7, - 0xfa1dfbd6, - 0xfc780986, - 0xf7ac09f2, - 0x075af89a, - 0x05e3fa1d, - 0xf8a60766, - 0xf7ac09f2, - 0x0388f67a, - 0xfa1dfbd6, - 0x0bdbfc59, - 0x0854f760, - 0x00fa0705, - 0x05e30000, - 0xff060705, - 0x085408a0, - 0xf425fc59, - 0xfa1d042a, - 0xfc78f67a, - 0xf7acf60e, - 0x075a0766, - 0x05e305e3, - 0xf8a6f89a, - 0xf7acf60e, - 0x03880986, - 0xfa1d042a, - 0x0bdb03a7, - 0x085408a0, - 0x00faf8fb, - 0x05e30000, - 0xff06f8fb, - 0x0854f760, - 0xf42503a7, - 0xfa1dfbd6, - 0xfc780986, - 0xf7ac09f2, - 0x075af89a, - 0x05e3fa1d, - 0xf8a60766, - 0xf7ac09f2, - 0x0388f67a, - 0xfa1dfbd6, - 0x0bdbfc59, - 0x0854f760, - 0x00fa0705, - 0xfa58fa58, - 0xf8f0fe00, - 0x0448073d, - 0xfdc9fe46, - 0xf9910258, - 0x089d0407, - 0xfd5cf71a, - 0x02affde0, - 0x083e0496, - 0xff5a0740, - 0xff7afd97, - 0x00fe01f1, - 0x0009082e, - 0xfa94ff75, - 0xfecdf8ea, - 0xffb0f693, - 0xfd2cfa58, - 0x0433ff16, - 0xfba405dd, - 0xfa610341, - 0x06a606cb, - 0x0039fd2d, - 0x0677fa97, - 0x01fa05e0, - 0xf896003e, - 0x075a068b, - 0x012cfc3e, - 0xfa23f98d, - 0xfc7cfd43, - 0xff90fc0d, - 0x01c10982, - 0x00c601d6, - 0xfd2cfd2c, - 0x01d600c6, - 0x098201c1, - 0xfc0dff90, - 0xfd43fc7c, - 0xf98dfa23, - 0xfc3e012c, - 0x068b075a, - 0x003ef896, - 0x05e001fa, - 0xfa970677, - 0xfd2d0039, - 0x06cb06a6, - 0x0341fa61, - 0x05ddfba4, - 0xff160433, - 0xfa58fd2c, - 0xf693ffb0, - 0xf8eafecd, - 0xff75fa94, - 0x082e0009, - 0x01f100fe, - 0xfd97ff7a, - 0x0740ff5a, - 0x0496083e, - 0xfde002af, - 0xf71afd5c, - 0x0407089d, - 0x0258f991, - 0xfe46fdc9, - 0x073d0448, - 0xfe00f8f0, - 0xfd2cfd2c, - 0xfce00500, - 0xfc09fddc, - 0xfe680157, - 0x04c70571, - 0xfc3aff21, - 0xfcd70228, - 0x056d0277, - 0x0200fe00, - 0x0022f927, - 0xfe3c032b, - 0xfc44ff3c, - 0x03e9fbdb, - 0x04570313, - 0x04c9ff5c, - 0x000d03b8, - 0xfa580000, - 0xfbe900d2, - 0xf9d0fe0b, - 0x0125fdf9, - 0x042501bf, - 0x0328fa2b, - 0xffa902f0, - 0xfa250157, - 0x0200fe00, - 0x03740438, - 0xff0405fd, - 0x030cfe52, - 0x0037fb39, - 0xff6904c5, - 0x04f8fd23, - 0xfd31fc1b, - 0xfd2cfd2c, - 0xfc1bfd31, - 0xfd2304f8, - 0x04c5ff69, - 0xfb390037, - 0xfe52030c, - 0x05fdff04, - 0x04380374, - 0xfe000200, - 0x0157fa25, - 0x02f0ffa9, - 0xfa2b0328, - 0x01bf0425, - 0xfdf90125, - 0xfe0bf9d0, - 0x00d2fbe9, - 0x0000fa58, - 0x03b8000d, - 0xff5c04c9, - 0x03130457, - 0xfbdb03e9, - 0xff3cfc44, - 0x032bfe3c, - 0xf9270022, - 0xfe000200, - 0x0277056d, - 0x0228fcd7, - 0xff21fc3a, - 0x057104c7, - 0x0157fe68, - 0xfddcfc09, - 0x0500fce0, - 0xfd2cfd2c, - 0x0500fce0, - 0xfddcfc09, - 0x0157fe68, - 0x057104c7, - 0xff21fc3a, - 0x0228fcd7, - 0x0277056d, - 0xfe000200, - 0xf9270022, - 0x032bfe3c, - 0xff3cfc44, - 0xfbdb03e9, - 0x03130457, - 0xff5c04c9, - 0x03b8000d, - 0x0000fa58, - 0x00d2fbe9, - 0xfe0bf9d0, - 0xfdf90125, - 0x01bf0425, - 0xfa2b0328, - 0x02f0ffa9, - 0x0157fa25, - 0xfe000200, - 0x04380374, - 0x05fdff04, - 0xfe52030c, - 0xfb390037, - 0x04c5ff69, - 0xfd2304f8, - 0xfc1bfd31, - 0xfd2cfd2c, - 0xfd31fc1b, - 0x04f8fd23, - 0xff6904c5, - 0x0037fb39, - 0x030cfe52, - 0xff0405fd, - 0x03740438, - 0x0200fe00, - 0xfa250157, - 0xffa902f0, - 0x0328fa2b, - 0x042501bf, - 0x0125fdf9, - 0xf9d0fe0b, - 0xfbe900d2, - 0xfa580000, - 0x000d03b8, - 0x04c9ff5c, - 0x04570313, - 0x03e9fbdb, - 0xfc44ff3c, - 0xfe3c032b, - 0x0022f927, - 0x0200fe00, - 0x056d0277, - 0xfcd70228, - 0xfc3aff21, - 0x04c70571, - 0xfe680157, - 0xfc09fddc, - 0xfce00500, - 0x05a80000, - 0xff1006be, - 0x0800084a, - 0xf49cfc7e, - 0xfa580400, - 0xfc9cf6da, - 0xf800f672, - 0x0710071c, - 0x05a805a8, - 0xf8f0f8e4, - 0xf800f672, - 0x03640926, - 0xfa580400, - 0x0b640382, - 0x0800084a, - 0x00f0f942, - 0x05a80000, - 0xff10f942, - 0x0800f7b6, - 0xf49c0382, - 0xfa58fc00, - 0xfc9c0926, - 0xf800098e, - 0x0710f8e4, - 0x05a8fa58, - 0xf8f0071c, - 0xf800098e, - 0x0364f6da, - 0xfa58fc00, - 0x0b64fc7e, - 0x0800f7b6, - 0x00f006be, - 0x05a80000, - 0xff1006be, - 0x0800084a, - 0xf49cfc7e, - 0xfa580400, - 0xfc9cf6da, - 0xf800f672, - 0x0710071c, - 0x05a805a8, - 0xf8f0f8e4, - 0xf800f672, - 0x03640926, - 0xfa580400, - 0x0b640382, - 0x0800084a, - 0x00f0f942, - 0x05a80000, - 0xff10f942, - 0x0800f7b6, - 0xf49c0382, - 0xfa58fc00, - 0xfc9c0926, - 0xf800098e, - 0x0710f8e4, - 0x05a8fa58, - 0xf8f0071c, - 0xf800098e, - 0x0364f6da, - 0xfa58fc00, - 0x0b64fc7e, - 0x0800f7b6, - 0x00f006be, -}; - -const u32 noise_var_tbl_rev3[] = { - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, - 0x02110211, - 0x0000014d, -}; - -static const u16 mcs_tbl_rev3[] = { - 0x0000, - 0x0008, - 0x000a, - 0x0010, - 0x0012, - 0x0019, - 0x001a, - 0x001c, - 0x0080, - 0x0088, - 0x008a, - 0x0090, - 0x0092, - 0x0099, - 0x009a, - 0x009c, - 0x0100, - 0x0108, - 0x010a, - 0x0110, - 0x0112, - 0x0119, - 0x011a, - 0x011c, - 0x0180, - 0x0188, - 0x018a, - 0x0190, - 0x0192, - 0x0199, - 0x019a, - 0x019c, - 0x0000, - 0x0098, - 0x00a0, - 0x00a8, - 0x009a, - 0x00a2, - 0x00aa, - 0x0120, - 0x0128, - 0x0128, - 0x0130, - 0x0138, - 0x0138, - 0x0140, - 0x0122, - 0x012a, - 0x012a, - 0x0132, - 0x013a, - 0x013a, - 0x0142, - 0x01a8, - 0x01b0, - 0x01b8, - 0x01b0, - 0x01b8, - 0x01c0, - 0x01c8, - 0x01c0, - 0x01c8, - 0x01d0, - 0x01d0, - 0x01d8, - 0x01aa, - 0x01b2, - 0x01ba, - 0x01b2, - 0x01ba, - 0x01c2, - 0x01ca, - 0x01c2, - 0x01ca, - 0x01d2, - 0x01d2, - 0x01da, - 0x0001, - 0x0002, - 0x0004, - 0x0009, - 0x000c, - 0x0011, - 0x0014, - 0x0018, - 0x0020, - 0x0021, - 0x0022, - 0x0024, - 0x0081, - 0x0082, - 0x0084, - 0x0089, - 0x008c, - 0x0091, - 0x0094, - 0x0098, - 0x00a0, - 0x00a1, - 0x00a2, - 0x00a4, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, - 0x0007, -}; - -static const u32 tdi_tbl20_ant0_rev3[] = { - 0x00091226, - 0x000a1429, - 0x000b56ad, - 0x000c58b0, - 0x000d5ab3, - 0x000e9cb6, - 0x000f9eba, - 0x0000c13d, - 0x00020301, - 0x00030504, - 0x00040708, - 0x0005090b, - 0x00064b8e, - 0x00095291, - 0x000a5494, - 0x000b9718, - 0x000c9927, - 0x000d9b2a, - 0x000edd2e, - 0x000fdf31, - 0x000101b4, - 0x000243b7, - 0x000345bb, - 0x000447be, - 0x00058982, - 0x00068c05, - 0x00099309, - 0x000a950c, - 0x000bd78f, - 0x000cd992, - 0x000ddb96, - 0x000f1d99, - 0x00005fa8, - 0x0001422c, - 0x0002842f, - 0x00038632, - 0x00048835, - 0x0005ca38, - 0x0006ccbc, - 0x0009d3bf, - 0x000b1603, - 0x000c1806, - 0x000d1a0a, - 0x000e1c0d, - 0x000f5e10, - 0x00008093, - 0x00018297, - 0x0002c49a, - 0x0003c680, - 0x0004c880, - 0x00060b00, - 0x00070d00, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 tdi_tbl20_ant1_rev3[] = { - 0x00014b26, - 0x00028d29, - 0x000393ad, - 0x00049630, - 0x0005d833, - 0x0006da36, - 0x00099c3a, - 0x000a9e3d, - 0x000bc081, - 0x000cc284, - 0x000dc488, - 0x000f068b, - 0x0000488e, - 0x00018b91, - 0x0002d214, - 0x0003d418, - 0x0004d6a7, - 0x000618aa, - 0x00071aae, - 0x0009dcb1, - 0x000b1eb4, - 0x000c0137, - 0x000d033b, - 0x000e053e, - 0x000f4702, - 0x00008905, - 0x00020c09, - 0x0003128c, - 0x0004148f, - 0x00051712, - 0x00065916, - 0x00091b19, - 0x000a1d28, - 0x000b5f2c, - 0x000c41af, - 0x000d43b2, - 0x000e85b5, - 0x000f87b8, - 0x0000c9bc, - 0x00024cbf, - 0x00035303, - 0x00045506, - 0x0005978a, - 0x0006998d, - 0x00095b90, - 0x000a5d93, - 0x000b9f97, - 0x000c821a, - 0x000d8400, - 0x000ec600, - 0x000fc800, - 0x00010a00, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 tdi_tbl40_ant0_rev3[] = { - 0x0011a346, - 0x00136ccf, - 0x0014f5d9, - 0x001641e2, - 0x0017cb6b, - 0x00195475, - 0x001b2383, - 0x001cad0c, - 0x001e7616, - 0x0000821f, - 0x00020ba8, - 0x0003d4b2, - 0x00056447, - 0x00072dd0, - 0x0008b6da, - 0x000a02e3, - 0x000b8c6c, - 0x000d15f6, - 0x0011e484, - 0x0013ae0d, - 0x00153717, - 0x00168320, - 0x00180ca9, - 0x00199633, - 0x001b6548, - 0x001ceed1, - 0x001eb7db, - 0x0000c3e4, - 0x00024d6d, - 0x000416f7, - 0x0005a585, - 0x00076f0f, - 0x0008f818, - 0x000a4421, - 0x000bcdab, - 0x000d9734, - 0x00122649, - 0x0013efd2, - 0x001578dc, - 0x0016c4e5, - 0x00184e6e, - 0x001a17f8, - 0x001ba686, - 0x001d3010, - 0x001ef999, - 0x00010522, - 0x00028eac, - 0x00045835, - 0x0005e74a, - 0x0007b0d3, - 0x00093a5d, - 0x000a85e6, - 0x000c0f6f, - 0x000dd8f9, - 0x00126787, - 0x00143111, - 0x0015ba9a, - 0x00170623, - 0x00188fad, - 0x001a5936, - 0x001be84b, - 0x001db1d4, - 0x001f3b5e, - 0x000146e7, - 0x00031070, - 0x000499fa, - 0x00062888, - 0x0007f212, - 0x00097b9b, - 0x000ac7a4, - 0x000c50ae, - 0x000e1a37, - 0x0012a94c, - 0x001472d5, - 0x0015fc5f, - 0x00174868, - 0x0018d171, - 0x001a9afb, - 0x001c2989, - 0x001df313, - 0x001f7c9c, - 0x000188a5, - 0x000351af, - 0x0004db38, - 0x0006aa4d, - 0x000833d7, - 0x0009bd60, - 0x000b0969, - 0x000c9273, - 0x000e5bfc, - 0x00132a8a, - 0x0014b414, - 0x00163d9d, - 0x001789a6, - 0x001912b0, - 0x001adc39, - 0x001c6bce, - 0x001e34d8, - 0x001fbe61, - 0x0001ca6a, - 0x00039374, - 0x00051cfd, - 0x0006ec0b, - 0x00087515, - 0x0009fe9e, - 0x000b4aa7, - 0x000cd3b1, - 0x000e9d3a, - 0x00000000, - 0x00000000, -}; - -static const u32 tdi_tbl40_ant1_rev3[] = { - 0x001edb36, - 0x000129ca, - 0x0002b353, - 0x00047cdd, - 0x0005c8e6, - 0x000791ef, - 0x00091bf9, - 0x000aaa07, - 0x000c3391, - 0x000dfd1a, - 0x00120923, - 0x0013d22d, - 0x00155c37, - 0x0016eacb, - 0x00187454, - 0x001a3dde, - 0x001b89e7, - 0x001d12f0, - 0x001f1cfa, - 0x00016b88, - 0x00033492, - 0x0004be1b, - 0x00060a24, - 0x0007d32e, - 0x00095d38, - 0x000aec4c, - 0x000c7555, - 0x000e3edf, - 0x00124ae8, - 0x001413f1, - 0x0015a37b, - 0x00172c89, - 0x0018b593, - 0x001a419c, - 0x001bcb25, - 0x001d942f, - 0x001f63b9, - 0x0001ad4d, - 0x00037657, - 0x0004c260, - 0x00068be9, - 0x000814f3, - 0x0009a47c, - 0x000b2d8a, - 0x000cb694, - 0x000e429d, - 0x00128c26, - 0x001455b0, - 0x0015e4ba, - 0x00176e4e, - 0x0018f758, - 0x001a8361, - 0x001c0cea, - 0x001dd674, - 0x001fa57d, - 0x0001ee8b, - 0x0003b795, - 0x0005039e, - 0x0006cd27, - 0x000856b1, - 0x0009e5c6, - 0x000b6f4f, - 0x000cf859, - 0x000e8462, - 0x00130deb, - 0x00149775, - 0x00162603, - 0x0017af8c, - 0x00193896, - 0x001ac49f, - 0x001c4e28, - 0x001e17b2, - 0x0000a6c7, - 0x00023050, - 0x0003f9da, - 0x00054563, - 0x00070eec, - 0x00089876, - 0x000a2704, - 0x000bb08d, - 0x000d3a17, - 0x001185a0, - 0x00134f29, - 0x0014d8b3, - 0x001667c8, - 0x0017f151, - 0x00197adb, - 0x001b0664, - 0x001c8fed, - 0x001e5977, - 0x0000e805, - 0x0002718f, - 0x00043b18, - 0x000586a1, - 0x0007502b, - 0x0008d9b4, - 0x000a68c9, - 0x000bf252, - 0x000dbbdc, - 0x0011c7e5, - 0x001390ee, - 0x00151a78, - 0x0016a906, - 0x00183290, - 0x0019bc19, - 0x001b4822, - 0x001cd12c, - 0x001e9ab5, - 0x00000000, - 0x00000000, -}; - -static const u32 pltlut_tbl_rev3[] = { - 0x76540213, - 0x62407351, - 0x76543210, - 0x76540213, - 0x76540213, - 0x76430521, -}; - -static const u32 chanest_tbl_rev3[] = { - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x44444444, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, - 0x10101010, -}; - -static const u8 frame_lut_rev3[] = { - 0x02, - 0x04, - 0x14, - 0x14, - 0x03, - 0x05, - 0x16, - 0x16, - 0x0a, - 0x0c, - 0x1c, - 0x1c, - 0x0b, - 0x0d, - 0x1e, - 0x1e, - 0x06, - 0x08, - 0x18, - 0x18, - 0x07, - 0x09, - 0x1a, - 0x1a, - 0x0e, - 0x10, - 0x20, - 0x28, - 0x0f, - 0x11, - 0x22, - 0x2a, -}; - -static const u8 est_pwr_lut_core0_rev3[] = { - 0x55, - 0x54, - 0x54, - 0x53, - 0x52, - 0x52, - 0x51, - 0x51, - 0x50, - 0x4f, - 0x4f, - 0x4e, - 0x4e, - 0x4d, - 0x4c, - 0x4c, - 0x4b, - 0x4a, - 0x49, - 0x49, - 0x48, - 0x47, - 0x46, - 0x46, - 0x45, - 0x44, - 0x43, - 0x42, - 0x41, - 0x40, - 0x40, - 0x3f, - 0x3e, - 0x3d, - 0x3c, - 0x3a, - 0x39, - 0x38, - 0x37, - 0x36, - 0x35, - 0x33, - 0x32, - 0x31, - 0x2f, - 0x2e, - 0x2c, - 0x2b, - 0x29, - 0x27, - 0x25, - 0x23, - 0x21, - 0x1f, - 0x1d, - 0x1a, - 0x18, - 0x15, - 0x12, - 0x0e, - 0x0b, - 0x07, - 0x02, - 0xfd, -}; - -static const u8 est_pwr_lut_core1_rev3[] = { - 0x55, - 0x54, - 0x54, - 0x53, - 0x52, - 0x52, - 0x51, - 0x51, - 0x50, - 0x4f, - 0x4f, - 0x4e, - 0x4e, - 0x4d, - 0x4c, - 0x4c, - 0x4b, - 0x4a, - 0x49, - 0x49, - 0x48, - 0x47, - 0x46, - 0x46, - 0x45, - 0x44, - 0x43, - 0x42, - 0x41, - 0x40, - 0x40, - 0x3f, - 0x3e, - 0x3d, - 0x3c, - 0x3a, - 0x39, - 0x38, - 0x37, - 0x36, - 0x35, - 0x33, - 0x32, - 0x31, - 0x2f, - 0x2e, - 0x2c, - 0x2b, - 0x29, - 0x27, - 0x25, - 0x23, - 0x21, - 0x1f, - 0x1d, - 0x1a, - 0x18, - 0x15, - 0x12, - 0x0e, - 0x0b, - 0x07, - 0x02, - 0xfd, -}; - -static const u8 adj_pwr_lut_core0_rev3[] = { - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, -}; - -static const u8 adj_pwr_lut_core1_rev3[] = { - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, -}; - -static const u32 gainctrl_lut_core0_rev3[] = { - 0x5bf70044, - 0x5bf70042, - 0x5bf70040, - 0x5bf7003e, - 0x5bf7003c, - 0x5bf7003b, - 0x5bf70039, - 0x5bf70037, - 0x5bf70036, - 0x5bf70034, - 0x5bf70033, - 0x5bf70031, - 0x5bf70030, - 0x5ba70044, - 0x5ba70042, - 0x5ba70040, - 0x5ba7003e, - 0x5ba7003c, - 0x5ba7003b, - 0x5ba70039, - 0x5ba70037, - 0x5ba70036, - 0x5ba70034, - 0x5ba70033, - 0x5b770044, - 0x5b770042, - 0x5b770040, - 0x5b77003e, - 0x5b77003c, - 0x5b77003b, - 0x5b770039, - 0x5b770037, - 0x5b770036, - 0x5b770034, - 0x5b770033, - 0x5b770031, - 0x5b770030, - 0x5b77002f, - 0x5b77002d, - 0x5b77002c, - 0x5b470044, - 0x5b470042, - 0x5b470040, - 0x5b47003e, - 0x5b47003c, - 0x5b47003b, - 0x5b470039, - 0x5b470037, - 0x5b470036, - 0x5b470034, - 0x5b470033, - 0x5b470031, - 0x5b470030, - 0x5b47002f, - 0x5b47002d, - 0x5b47002c, - 0x5b47002b, - 0x5b47002a, - 0x5b270044, - 0x5b270042, - 0x5b270040, - 0x5b27003e, - 0x5b27003c, - 0x5b27003b, - 0x5b270039, - 0x5b270037, - 0x5b270036, - 0x5b270034, - 0x5b270033, - 0x5b270031, - 0x5b270030, - 0x5b27002f, - 0x5b170044, - 0x5b170042, - 0x5b170040, - 0x5b17003e, - 0x5b17003c, - 0x5b17003b, - 0x5b170039, - 0x5b170037, - 0x5b170036, - 0x5b170034, - 0x5b170033, - 0x5b170031, - 0x5b170030, - 0x5b17002f, - 0x5b17002d, - 0x5b17002c, - 0x5b17002b, - 0x5b17002a, - 0x5b170028, - 0x5b170027, - 0x5b170026, - 0x5b170025, - 0x5b170024, - 0x5b170023, - 0x5b070044, - 0x5b070042, - 0x5b070040, - 0x5b07003e, - 0x5b07003c, - 0x5b07003b, - 0x5b070039, - 0x5b070037, - 0x5b070036, - 0x5b070034, - 0x5b070033, - 0x5b070031, - 0x5b070030, - 0x5b07002f, - 0x5b07002d, - 0x5b07002c, - 0x5b07002b, - 0x5b07002a, - 0x5b070028, - 0x5b070027, - 0x5b070026, - 0x5b070025, - 0x5b070024, - 0x5b070023, - 0x5b070022, - 0x5b070021, - 0x5b070020, - 0x5b07001f, - 0x5b07001e, - 0x5b07001d, - 0x5b07001d, - 0x5b07001c, -}; - -static const u32 gainctrl_lut_core1_rev3[] = { - 0x5bf70044, - 0x5bf70042, - 0x5bf70040, - 0x5bf7003e, - 0x5bf7003c, - 0x5bf7003b, - 0x5bf70039, - 0x5bf70037, - 0x5bf70036, - 0x5bf70034, - 0x5bf70033, - 0x5bf70031, - 0x5bf70030, - 0x5ba70044, - 0x5ba70042, - 0x5ba70040, - 0x5ba7003e, - 0x5ba7003c, - 0x5ba7003b, - 0x5ba70039, - 0x5ba70037, - 0x5ba70036, - 0x5ba70034, - 0x5ba70033, - 0x5b770044, - 0x5b770042, - 0x5b770040, - 0x5b77003e, - 0x5b77003c, - 0x5b77003b, - 0x5b770039, - 0x5b770037, - 0x5b770036, - 0x5b770034, - 0x5b770033, - 0x5b770031, - 0x5b770030, - 0x5b77002f, - 0x5b77002d, - 0x5b77002c, - 0x5b470044, - 0x5b470042, - 0x5b470040, - 0x5b47003e, - 0x5b47003c, - 0x5b47003b, - 0x5b470039, - 0x5b470037, - 0x5b470036, - 0x5b470034, - 0x5b470033, - 0x5b470031, - 0x5b470030, - 0x5b47002f, - 0x5b47002d, - 0x5b47002c, - 0x5b47002b, - 0x5b47002a, - 0x5b270044, - 0x5b270042, - 0x5b270040, - 0x5b27003e, - 0x5b27003c, - 0x5b27003b, - 0x5b270039, - 0x5b270037, - 0x5b270036, - 0x5b270034, - 0x5b270033, - 0x5b270031, - 0x5b270030, - 0x5b27002f, - 0x5b170044, - 0x5b170042, - 0x5b170040, - 0x5b17003e, - 0x5b17003c, - 0x5b17003b, - 0x5b170039, - 0x5b170037, - 0x5b170036, - 0x5b170034, - 0x5b170033, - 0x5b170031, - 0x5b170030, - 0x5b17002f, - 0x5b17002d, - 0x5b17002c, - 0x5b17002b, - 0x5b17002a, - 0x5b170028, - 0x5b170027, - 0x5b170026, - 0x5b170025, - 0x5b170024, - 0x5b170023, - 0x5b070044, - 0x5b070042, - 0x5b070040, - 0x5b07003e, - 0x5b07003c, - 0x5b07003b, - 0x5b070039, - 0x5b070037, - 0x5b070036, - 0x5b070034, - 0x5b070033, - 0x5b070031, - 0x5b070030, - 0x5b07002f, - 0x5b07002d, - 0x5b07002c, - 0x5b07002b, - 0x5b07002a, - 0x5b070028, - 0x5b070027, - 0x5b070026, - 0x5b070025, - 0x5b070024, - 0x5b070023, - 0x5b070022, - 0x5b070021, - 0x5b070020, - 0x5b07001f, - 0x5b07001e, - 0x5b07001d, - 0x5b07001d, - 0x5b07001c, -}; - -static const u32 iq_lut_core0_rev3[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u32 iq_lut_core1_rev3[] = { - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -static const u16 loft_lut_core0_rev3[] = { - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, -}; - -static const u16 loft_lut_core1_rev3[] = { - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, - 0x0000, -}; - -static const u16 papd_comp_rfpwr_tbl_core0_rev3[] = { - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, -}; - -static const u16 papd_comp_rfpwr_tbl_core1_rev3[] = { - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x0036, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x002a, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x001e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x000e, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01fc, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01ee, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, - 0x01d6, -}; - -static const u32 papd_comp_epsilon_tbl_core0_rev3[] = { - 0x00000000, - 0x00001fa0, - 0x00019f78, - 0x0001df7e, - 0x03fa9f86, - 0x03fd1f90, - 0x03fe5f8a, - 0x03fb1f94, - 0x03fd9fa0, - 0x00009f98, - 0x03fd1fac, - 0x03ff9fa2, - 0x03fe9fae, - 0x00001fae, - 0x03fddfb4, - 0x03ff1fb8, - 0x03ff9fbc, - 0x03ffdfbe, - 0x03fe9fc2, - 0x03fedfc6, - 0x03fedfc6, - 0x03ff9fc8, - 0x03ff5fc6, - 0x03fedfc2, - 0x03ff9fc0, - 0x03ff5fac, - 0x03ff5fac, - 0x03ff9fa2, - 0x03ff9fa6, - 0x03ff9faa, - 0x03ff5fb0, - 0x03ff5fb4, - 0x03ff1fca, - 0x03ff5fce, - 0x03fcdfdc, - 0x03fb4006, - 0x00000030, - 0x03ff808a, - 0x03ff80da, - 0x0000016c, - 0x03ff8318, - 0x03ff063a, - 0x03fd8bd6, - 0x00014ffe, - 0x00034ffe, - 0x00034ffe, - 0x0003cffe, - 0x00040ffe, - 0x00040ffe, - 0x0003cffe, - 0x0003cffe, - 0x00020ffe, - 0x03fe0ffe, - 0x03fdcffe, - 0x03f94ffe, - 0x03f54ffe, - 0x03f44ffe, - 0x03ef8ffe, - 0x03ee0ffe, - 0x03ebcffe, - 0x03e8cffe, - 0x03e74ffe, - 0x03e4cffe, - 0x03e38ffe, -}; - -static const u32 papd_cal_scalars_tbl_core0_rev3[] = { - 0x05af005a, - 0x0571005e, - 0x05040066, - 0x04bd006c, - 0x047d0072, - 0x04430078, - 0x03f70081, - 0x03cb0087, - 0x03870091, - 0x035e0098, - 0x032e00a1, - 0x030300aa, - 0x02d800b4, - 0x02ae00bf, - 0x028900ca, - 0x026400d6, - 0x024100e3, - 0x022200f0, - 0x020200ff, - 0x01e5010e, - 0x01ca011e, - 0x01b0012f, - 0x01990140, - 0x01830153, - 0x016c0168, - 0x0158017d, - 0x01450193, - 0x013301ab, - 0x012101c5, - 0x011101e0, - 0x010201fc, - 0x00f4021a, - 0x00e6011d, - 0x00d9012e, - 0x00cd0140, - 0x00c20153, - 0x00b70167, - 0x00ac017c, - 0x00a30193, - 0x009a01ab, - 0x009101c4, - 0x008901df, - 0x008101fb, - 0x007a0219, - 0x00730239, - 0x006d025b, - 0x0067027e, - 0x006102a4, - 0x005c02cc, - 0x005602f6, - 0x00520323, - 0x004d0353, - 0x00490385, - 0x004503bb, - 0x004103f3, - 0x003d042f, - 0x003a046f, - 0x003704b2, - 0x003404f9, - 0x00310545, - 0x002e0596, - 0x002b05f5, - 0x00290640, - 0x002606a4, -}; - -static const u32 papd_comp_epsilon_tbl_core1_rev3[] = { - 0x00000000, - 0x00001fa0, - 0x00019f78, - 0x0001df7e, - 0x03fa9f86, - 0x03fd1f90, - 0x03fe5f8a, - 0x03fb1f94, - 0x03fd9fa0, - 0x00009f98, - 0x03fd1fac, - 0x03ff9fa2, - 0x03fe9fae, - 0x00001fae, - 0x03fddfb4, - 0x03ff1fb8, - 0x03ff9fbc, - 0x03ffdfbe, - 0x03fe9fc2, - 0x03fedfc6, - 0x03fedfc6, - 0x03ff9fc8, - 0x03ff5fc6, - 0x03fedfc2, - 0x03ff9fc0, - 0x03ff5fac, - 0x03ff5fac, - 0x03ff9fa2, - 0x03ff9fa6, - 0x03ff9faa, - 0x03ff5fb0, - 0x03ff5fb4, - 0x03ff1fca, - 0x03ff5fce, - 0x03fcdfdc, - 0x03fb4006, - 0x00000030, - 0x03ff808a, - 0x03ff80da, - 0x0000016c, - 0x03ff8318, - 0x03ff063a, - 0x03fd8bd6, - 0x00014ffe, - 0x00034ffe, - 0x00034ffe, - 0x0003cffe, - 0x00040ffe, - 0x00040ffe, - 0x0003cffe, - 0x0003cffe, - 0x00020ffe, - 0x03fe0ffe, - 0x03fdcffe, - 0x03f94ffe, - 0x03f54ffe, - 0x03f44ffe, - 0x03ef8ffe, - 0x03ee0ffe, - 0x03ebcffe, - 0x03e8cffe, - 0x03e74ffe, - 0x03e4cffe, - 0x03e38ffe, -}; - -static const u32 papd_cal_scalars_tbl_core1_rev3[] = { - 0x05af005a, - 0x0571005e, - 0x05040066, - 0x04bd006c, - 0x047d0072, - 0x04430078, - 0x03f70081, - 0x03cb0087, - 0x03870091, - 0x035e0098, - 0x032e00a1, - 0x030300aa, - 0x02d800b4, - 0x02ae00bf, - 0x028900ca, - 0x026400d6, - 0x024100e3, - 0x022200f0, - 0x020200ff, - 0x01e5010e, - 0x01ca011e, - 0x01b0012f, - 0x01990140, - 0x01830153, - 0x016c0168, - 0x0158017d, - 0x01450193, - 0x013301ab, - 0x012101c5, - 0x011101e0, - 0x010201fc, - 0x00f4021a, - 0x00e6011d, - 0x00d9012e, - 0x00cd0140, - 0x00c20153, - 0x00b70167, - 0x00ac017c, - 0x00a30193, - 0x009a01ab, - 0x009101c4, - 0x008901df, - 0x008101fb, - 0x007a0219, - 0x00730239, - 0x006d025b, - 0x0067027e, - 0x006102a4, - 0x005c02cc, - 0x005602f6, - 0x00520323, - 0x004d0353, - 0x00490385, - 0x004503bb, - 0x004103f3, - 0x003d042f, - 0x003a046f, - 0x003704b2, - 0x003404f9, - 0x00310545, - 0x002e0596, - 0x002b05f5, - 0x00290640, - 0x002606a4, -}; - -const struct phytbl_info mimophytbl_info_rev3_volatile[] = { - {&ant_swctrl_tbl_rev3, - sizeof(ant_swctrl_tbl_rev3) / sizeof(ant_swctrl_tbl_rev3[0]), 9, 0, 16} - , -}; - -const struct phytbl_info mimophytbl_info_rev3_volatile1[] = { - {&ant_swctrl_tbl_rev3_1, - sizeof(ant_swctrl_tbl_rev3_1) / sizeof(ant_swctrl_tbl_rev3_1[0]), 9, 0, - 16} - , -}; - -const struct phytbl_info mimophytbl_info_rev3_volatile2[] = { - {&ant_swctrl_tbl_rev3_2, - sizeof(ant_swctrl_tbl_rev3_2) / sizeof(ant_swctrl_tbl_rev3_2[0]), 9, 0, - 16} - , -}; - -const struct phytbl_info mimophytbl_info_rev3_volatile3[] = { - {&ant_swctrl_tbl_rev3_3, - sizeof(ant_swctrl_tbl_rev3_3) / sizeof(ant_swctrl_tbl_rev3_3[0]), 9, 0, - 16} - , -}; - -const struct phytbl_info mimophytbl_info_rev3[] = { - {&frame_struct_rev3, - sizeof(frame_struct_rev3) / sizeof(frame_struct_rev3[0]), 10, 0, 32} - , - {&pilot_tbl_rev3, sizeof(pilot_tbl_rev3) / sizeof(pilot_tbl_rev3[0]), - 11, 0, 16} - , - {&tmap_tbl_rev3, sizeof(tmap_tbl_rev3) / sizeof(tmap_tbl_rev3[0]), 12, - 0, 32} - , - {&intlv_tbl_rev3, sizeof(intlv_tbl_rev3) / sizeof(intlv_tbl_rev3[0]), - 13, 0, 32} - , - {&tdtrn_tbl_rev3, sizeof(tdtrn_tbl_rev3) / sizeof(tdtrn_tbl_rev3[0]), - 14, 0, 32} - , - {&noise_var_tbl_rev3, - sizeof(noise_var_tbl_rev3) / sizeof(noise_var_tbl_rev3[0]), 16, 0, 32} - , - {&mcs_tbl_rev3, sizeof(mcs_tbl_rev3) / sizeof(mcs_tbl_rev3[0]), 18, 0, - 16} - , - {&tdi_tbl20_ant0_rev3, - sizeof(tdi_tbl20_ant0_rev3) / sizeof(tdi_tbl20_ant0_rev3[0]), 19, 128, - 32} - , - {&tdi_tbl20_ant1_rev3, - sizeof(tdi_tbl20_ant1_rev3) / sizeof(tdi_tbl20_ant1_rev3[0]), 19, 256, - 32} - , - {&tdi_tbl40_ant0_rev3, - sizeof(tdi_tbl40_ant0_rev3) / sizeof(tdi_tbl40_ant0_rev3[0]), 19, 640, - 32} - , - {&tdi_tbl40_ant1_rev3, - sizeof(tdi_tbl40_ant1_rev3) / sizeof(tdi_tbl40_ant1_rev3[0]), 19, 768, - 32} - , - {&pltlut_tbl_rev3, sizeof(pltlut_tbl_rev3) / sizeof(pltlut_tbl_rev3[0]), - 20, 0, 32} - , - {&chanest_tbl_rev3, - sizeof(chanest_tbl_rev3) / sizeof(chanest_tbl_rev3[0]), 22, 0, 32} - , - {&frame_lut_rev3, sizeof(frame_lut_rev3) / sizeof(frame_lut_rev3[0]), - 24, 0, 8} - , - {&est_pwr_lut_core0_rev3, - sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26, - 0, 8} - , - {&est_pwr_lut_core1_rev3, - sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27, - 0, 8} - , - {&adj_pwr_lut_core0_rev3, - sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26, - 64, 8} - , - {&adj_pwr_lut_core1_rev3, - sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27, - 64, 8} - , - {&gainctrl_lut_core0_rev3, - sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]), - 26, 192, 32} - , - {&gainctrl_lut_core1_rev3, - sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]), - 27, 192, 32} - , - {&iq_lut_core0_rev3, - sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32} - , - {&iq_lut_core1_rev3, - sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32} - , - {&loft_lut_core0_rev3, - sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448, - 16} - , - {&loft_lut_core1_rev3, - sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448, - 16} -}; - -const u32 mimophytbl_info_sz_rev3 = - sizeof(mimophytbl_info_rev3) / sizeof(mimophytbl_info_rev3[0]); -const u32 mimophytbl_info_sz_rev3_volatile = - sizeof(mimophytbl_info_rev3_volatile) / - sizeof(mimophytbl_info_rev3_volatile[0]); -const u32 mimophytbl_info_sz_rev3_volatile1 = - sizeof(mimophytbl_info_rev3_volatile1) / - sizeof(mimophytbl_info_rev3_volatile1[0]); -const u32 mimophytbl_info_sz_rev3_volatile2 = - sizeof(mimophytbl_info_rev3_volatile2) / - sizeof(mimophytbl_info_rev3_volatile2[0]); -const u32 mimophytbl_info_sz_rev3_volatile3 = - sizeof(mimophytbl_info_rev3_volatile3) / - sizeof(mimophytbl_info_rev3_volatile3[0]); - -static const u32 tmap_tbl_rev7[] = { - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0xf1111110, - 0x11111111, - 0x11f11111, - 0x00000111, - 0x11000000, - 0x1111f111, - 0x11111111, - 0x111111f1, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x000aa888, - 0x88880000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa2222220, - 0x22222222, - 0x22c22222, - 0x00000222, - 0x22000000, - 0x2222a222, - 0x22222222, - 0x222222a2, - 0xf1111110, - 0x11111111, - 0x11f11111, - 0x00011111, - 0x11110000, - 0x1111f111, - 0x11111111, - 0x111111f1, - 0xa8aa88a0, - 0xa88888a8, - 0xa8a8a88a, - 0x00088aaa, - 0xaaaa0000, - 0xa8a8aa88, - 0xa88aaaaa, - 0xaaaa8a8a, - 0xaaa8aaa0, - 0x8aaa8aaa, - 0xaa8a8a8a, - 0x000aaa88, - 0x8aaa0000, - 0xaaa8a888, - 0x8aa88a8a, - 0x8a88a888, - 0x08080a00, - 0x0a08080a, - 0x080a0a08, - 0x00080808, - 0x080a0000, - 0x080a0808, - 0x080a0808, - 0x0a0a0a08, - 0xa0a0a0a0, - 0x80a0a080, - 0x8080a0a0, - 0x00008080, - 0x80a00000, - 0x80a080a0, - 0xa080a0a0, - 0x8080a0a0, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x99999000, - 0x9b9b99bb, - 0x9bb99999, - 0x9999b9b9, - 0x9b99bb90, - 0x9bbbbb9b, - 0x9b9b9bb9, - 0x00000999, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00aaa888, - 0x22000000, - 0x2222b222, - 0x22222222, - 0x222222b2, - 0xb2222220, - 0x22222222, - 0x22d22222, - 0x00000222, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x33000000, - 0x3333b333, - 0x33333333, - 0x333333b3, - 0xb3333330, - 0x33333333, - 0x33d33333, - 0x00000333, - 0x22000000, - 0x2222a222, - 0x22222222, - 0x222222a2, - 0xa2222220, - 0x22222222, - 0x22c22222, - 0x00000222, - 0x99b99b00, - 0x9b9b99bb, - 0x9bb99999, - 0x9999b9b9, - 0x9b99bb99, - 0x9bbbbb9b, - 0x9b9b9bb9, - 0x00000999, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa88, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x08aaa888, - 0x22222200, - 0x2222f222, - 0x22222222, - 0x222222f2, - 0x22222222, - 0x22222222, - 0x22f22222, - 0x00000222, - 0x11000000, - 0x1111f111, - 0x11111111, - 0x11111111, - 0xf1111111, - 0x11111111, - 0x11f11111, - 0x01111111, - 0xbb9bb900, - 0xb9b9bb99, - 0xb99bbbbb, - 0xbbbb9b9b, - 0xb9bb99bb, - 0xb99999b9, - 0xb9b9b99b, - 0x00000bbb, - 0xaa000000, - 0xa8a8aa88, - 0xa88aaaaa, - 0xaaaa8a8a, - 0xa8aa88aa, - 0xa88888a8, - 0xa8a8a88a, - 0x0a888aaa, - 0xaa000000, - 0xa8a8aa88, - 0xa88aaaaa, - 0xaaaa8a8a, - 0xa8aa88a0, - 0xa88888a8, - 0xa8a8a88a, - 0x00000aaa, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0xbbbbbb00, - 0x999bbbbb, - 0x9bb99b9b, - 0xb9b9b9bb, - 0xb9b99bbb, - 0xb9b9b9bb, - 0xb9bb9b99, - 0x00000999, - 0x8a000000, - 0xaa88a888, - 0xa88888aa, - 0xa88a8a88, - 0xa88aa88a, - 0x88a8aaaa, - 0xa8aa8aaa, - 0x0888a88a, - 0x0b0b0b00, - 0x090b0b0b, - 0x0b090b0b, - 0x0909090b, - 0x09090b0b, - 0x09090b0b, - 0x09090b09, - 0x00000909, - 0x0a000000, - 0x0a080808, - 0x080a080a, - 0x080a0a08, - 0x080a080a, - 0x0808080a, - 0x0a0a0a08, - 0x0808080a, - 0xb0b0b000, - 0x9090b0b0, - 0x90b09090, - 0xb0b0b090, - 0xb0b090b0, - 0x90b0b0b0, - 0xb0b09090, - 0x00000090, - 0x80000000, - 0xa080a080, - 0xa08080a0, - 0xa0808080, - 0xa080a080, - 0x80a0a0a0, - 0xa0a080a0, - 0x00a0a0a0, - 0x22000000, - 0x2222f222, - 0x22222222, - 0x222222f2, - 0xf2222220, - 0x22222222, - 0x22f22222, - 0x00000222, - 0x11000000, - 0x1111f111, - 0x11111111, - 0x111111f1, - 0xf1111110, - 0x11111111, - 0x11f11111, - 0x00000111, - 0x33000000, - 0x3333f333, - 0x33333333, - 0x333333f3, - 0xf3333330, - 0x33333333, - 0x33f33333, - 0x00000333, - 0x22000000, - 0x2222f222, - 0x22222222, - 0x222222f2, - 0xf2222220, - 0x22222222, - 0x22f22222, - 0x00000222, - 0x99000000, - 0x9b9b99bb, - 0x9bb99999, - 0x9999b9b9, - 0x9b99bb90, - 0x9bbbbb9b, - 0x9b9b9bb9, - 0x00000999, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88888000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00aaa888, - 0x88a88a00, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa88, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x000aa888, - 0x88880000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa88, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x08aaa888, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x11000000, - 0x1111a111, - 0x11111111, - 0x111111a1, - 0xa1111110, - 0x11111111, - 0x11c11111, - 0x00000111, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x88000000, - 0x8a8a88aa, - 0x8aa88888, - 0x8888a8a8, - 0x8a88aa80, - 0x8aaaaa8a, - 0x8a8a8aa8, - 0x00000888, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -}; - -const u32 noise_var_tbl_rev7[] = { - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, - 0x020c020c, - 0x0000014d, -}; - -static const u32 papd_comp_epsilon_tbl_core0_rev7[] = { - 0x00000000, - 0x00000000, - 0x00016023, - 0x00006028, - 0x00034036, - 0x0003402e, - 0x0007203c, - 0x0006e037, - 0x00070030, - 0x0009401f, - 0x0009a00f, - 0x000b600d, - 0x000c8007, - 0x000ce007, - 0x00101fff, - 0x00121ff9, - 0x0012e004, - 0x0014dffc, - 0x0016dff6, - 0x0018dfe9, - 0x001b3fe5, - 0x001c5fd0, - 0x001ddfc2, - 0x001f1fb6, - 0x00207fa4, - 0x00219f8f, - 0x0022ff7d, - 0x00247f6c, - 0x0024df5b, - 0x00267f4b, - 0x0027df3b, - 0x0029bf3b, - 0x002b5f2f, - 0x002d3f2e, - 0x002f5f2a, - 0x002fff15, - 0x00315f0b, - 0x0032defa, - 0x0033beeb, - 0x0034fed9, - 0x00353ec5, - 0x00361eb0, - 0x00363e9b, - 0x0036be87, - 0x0036be70, - 0x0038fe67, - 0x0044beb2, - 0x00513ef3, - 0x00595f11, - 0x00669f3d, - 0x0078dfdf, - 0x00a143aa, - 0x01642fff, - 0x0162afff, - 0x01620fff, - 0x0160cfff, - 0x015f0fff, - 0x015dafff, - 0x015bcfff, - 0x015bcfff, - 0x015b4fff, - 0x015acfff, - 0x01590fff, - 0x0156cfff, -}; - -static const u32 papd_cal_scalars_tbl_core0_rev7[] = { - 0x0b5e002d, - 0x0ae2002f, - 0x0a3b0032, - 0x09a70035, - 0x09220038, - 0x08ab003b, - 0x081f003f, - 0x07a20043, - 0x07340047, - 0x06d2004b, - 0x067a004f, - 0x06170054, - 0x05bf0059, - 0x0571005e, - 0x051e0064, - 0x04d3006a, - 0x04910070, - 0x044c0077, - 0x040f007e, - 0x03d90085, - 0x03a1008d, - 0x036f0095, - 0x033d009e, - 0x030b00a8, - 0x02e000b2, - 0x02b900bc, - 0x029200c7, - 0x026d00d3, - 0x024900e0, - 0x022900ed, - 0x020a00fb, - 0x01ec010a, - 0x01d20119, - 0x01b7012a, - 0x019e013c, - 0x0188014e, - 0x01720162, - 0x015d0177, - 0x0149018e, - 0x013701a5, - 0x012601be, - 0x011501d8, - 0x010601f4, - 0x00f70212, - 0x00e90231, - 0x00dc0253, - 0x00d00276, - 0x00c4029b, - 0x00b902c3, - 0x00af02ed, - 0x00a50319, - 0x009c0348, - 0x0093037a, - 0x008b03af, - 0x008303e6, - 0x007c0422, - 0x00750460, - 0x006e04a3, - 0x006804e9, - 0x00620533, - 0x005d0582, - 0x005805d6, - 0x0053062e, - 0x004e068c, -}; - -static const u32 papd_comp_epsilon_tbl_core1_rev7[] = { - 0x00000000, - 0x00000000, - 0x00016023, - 0x00006028, - 0x00034036, - 0x0003402e, - 0x0007203c, - 0x0006e037, - 0x00070030, - 0x0009401f, - 0x0009a00f, - 0x000b600d, - 0x000c8007, - 0x000ce007, - 0x00101fff, - 0x00121ff9, - 0x0012e004, - 0x0014dffc, - 0x0016dff6, - 0x0018dfe9, - 0x001b3fe5, - 0x001c5fd0, - 0x001ddfc2, - 0x001f1fb6, - 0x00207fa4, - 0x00219f8f, - 0x0022ff7d, - 0x00247f6c, - 0x0024df5b, - 0x00267f4b, - 0x0027df3b, - 0x0029bf3b, - 0x002b5f2f, - 0x002d3f2e, - 0x002f5f2a, - 0x002fff15, - 0x00315f0b, - 0x0032defa, - 0x0033beeb, - 0x0034fed9, - 0x00353ec5, - 0x00361eb0, - 0x00363e9b, - 0x0036be87, - 0x0036be70, - 0x0038fe67, - 0x0044beb2, - 0x00513ef3, - 0x00595f11, - 0x00669f3d, - 0x0078dfdf, - 0x00a143aa, - 0x01642fff, - 0x0162afff, - 0x01620fff, - 0x0160cfff, - 0x015f0fff, - 0x015dafff, - 0x015bcfff, - 0x015bcfff, - 0x015b4fff, - 0x015acfff, - 0x01590fff, - 0x0156cfff, -}; - -static const u32 papd_cal_scalars_tbl_core1_rev7[] = { - 0x0b5e002d, - 0x0ae2002f, - 0x0a3b0032, - 0x09a70035, - 0x09220038, - 0x08ab003b, - 0x081f003f, - 0x07a20043, - 0x07340047, - 0x06d2004b, - 0x067a004f, - 0x06170054, - 0x05bf0059, - 0x0571005e, - 0x051e0064, - 0x04d3006a, - 0x04910070, - 0x044c0077, - 0x040f007e, - 0x03d90085, - 0x03a1008d, - 0x036f0095, - 0x033d009e, - 0x030b00a8, - 0x02e000b2, - 0x02b900bc, - 0x029200c7, - 0x026d00d3, - 0x024900e0, - 0x022900ed, - 0x020a00fb, - 0x01ec010a, - 0x01d20119, - 0x01b7012a, - 0x019e013c, - 0x0188014e, - 0x01720162, - 0x015d0177, - 0x0149018e, - 0x013701a5, - 0x012601be, - 0x011501d8, - 0x010601f4, - 0x00f70212, - 0x00e90231, - 0x00dc0253, - 0x00d00276, - 0x00c4029b, - 0x00b902c3, - 0x00af02ed, - 0x00a50319, - 0x009c0348, - 0x0093037a, - 0x008b03af, - 0x008303e6, - 0x007c0422, - 0x00750460, - 0x006e04a3, - 0x006804e9, - 0x00620533, - 0x005d0582, - 0x005805d6, - 0x0053062e, - 0x004e068c, -}; - -const struct phytbl_info mimophytbl_info_rev7[] = { - {&frame_struct_rev3, - sizeof(frame_struct_rev3) / sizeof(frame_struct_rev3[0]), 10, 0, 32} - , - {&pilot_tbl_rev3, sizeof(pilot_tbl_rev3) / sizeof(pilot_tbl_rev3[0]), - 11, 0, 16} - , - {&tmap_tbl_rev7, sizeof(tmap_tbl_rev7) / sizeof(tmap_tbl_rev7[0]), 12, - 0, 32} - , - {&intlv_tbl_rev3, sizeof(intlv_tbl_rev3) / sizeof(intlv_tbl_rev3[0]), - 13, 0, 32} - , - {&tdtrn_tbl_rev3, sizeof(tdtrn_tbl_rev3) / sizeof(tdtrn_tbl_rev3[0]), - 14, 0, 32} - , - {&noise_var_tbl_rev7, - sizeof(noise_var_tbl_rev7) / sizeof(noise_var_tbl_rev7[0]), 16, 0, 32} - , - {&mcs_tbl_rev3, sizeof(mcs_tbl_rev3) / sizeof(mcs_tbl_rev3[0]), 18, 0, - 16} - , - {&tdi_tbl20_ant0_rev3, - sizeof(tdi_tbl20_ant0_rev3) / sizeof(tdi_tbl20_ant0_rev3[0]), 19, 128, - 32} - , - {&tdi_tbl20_ant1_rev3, - sizeof(tdi_tbl20_ant1_rev3) / sizeof(tdi_tbl20_ant1_rev3[0]), 19, 256, - 32} - , - {&tdi_tbl40_ant0_rev3, - sizeof(tdi_tbl40_ant0_rev3) / sizeof(tdi_tbl40_ant0_rev3[0]), 19, 640, - 32} - , - {&tdi_tbl40_ant1_rev3, - sizeof(tdi_tbl40_ant1_rev3) / sizeof(tdi_tbl40_ant1_rev3[0]), 19, 768, - 32} - , - {&pltlut_tbl_rev3, sizeof(pltlut_tbl_rev3) / sizeof(pltlut_tbl_rev3[0]), - 20, 0, 32} - , - {&chanest_tbl_rev3, - sizeof(chanest_tbl_rev3) / sizeof(chanest_tbl_rev3[0]), 22, 0, 32} - , - {&frame_lut_rev3, sizeof(frame_lut_rev3) / sizeof(frame_lut_rev3[0]), - 24, 0, 8} - , - {&est_pwr_lut_core0_rev3, - sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26, - 0, 8} - , - {&est_pwr_lut_core1_rev3, - sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27, - 0, 8} - , - {&adj_pwr_lut_core0_rev3, - sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26, - 64, 8} - , - {&adj_pwr_lut_core1_rev3, - sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27, - 64, 8} - , - {&gainctrl_lut_core0_rev3, - sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]), - 26, 192, 32} - , - {&gainctrl_lut_core1_rev3, - sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]), - 27, 192, 32} - , - {&iq_lut_core0_rev3, - sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32} - , - {&iq_lut_core1_rev3, - sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32} - , - {&loft_lut_core0_rev3, - sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448, - 16} - , - {&loft_lut_core1_rev3, - sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448, - 16} - , - {&papd_comp_rfpwr_tbl_core0_rev3, - sizeof(papd_comp_rfpwr_tbl_core0_rev3) / - sizeof(papd_comp_rfpwr_tbl_core0_rev3[0]), 26, 576, 16} - , - {&papd_comp_rfpwr_tbl_core1_rev3, - sizeof(papd_comp_rfpwr_tbl_core1_rev3) / - sizeof(papd_comp_rfpwr_tbl_core1_rev3[0]), 27, 576, 16} - , - {&papd_comp_epsilon_tbl_core0_rev7, - sizeof(papd_comp_epsilon_tbl_core0_rev7) / - sizeof(papd_comp_epsilon_tbl_core0_rev7[0]), 31, 0, 32} - , - {&papd_cal_scalars_tbl_core0_rev7, - sizeof(papd_cal_scalars_tbl_core0_rev7) / - sizeof(papd_cal_scalars_tbl_core0_rev7[0]), 32, 0, 32} - , - {&papd_comp_epsilon_tbl_core1_rev7, - sizeof(papd_comp_epsilon_tbl_core1_rev7) / - sizeof(papd_comp_epsilon_tbl_core1_rev7[0]), 33, 0, 32} - , - {&papd_cal_scalars_tbl_core1_rev7, - sizeof(papd_cal_scalars_tbl_core1_rev7) / - sizeof(papd_cal_scalars_tbl_core1_rev7[0]), 34, 0, 32} - , -}; - -const u32 mimophytbl_info_sz_rev7 = - sizeof(mimophytbl_info_rev7) / sizeof(mimophytbl_info_rev7[0]); - -const struct phytbl_info mimophytbl_info_rev16[] = { - {&noise_var_tbl_rev7, - sizeof(noise_var_tbl_rev7) / sizeof(noise_var_tbl_rev7[0]), 16, 0, 32} - , - {&est_pwr_lut_core0_rev3, - sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26, - 0, 8} - , - {&est_pwr_lut_core1_rev3, - sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27, - 0, 8} - , - {&adj_pwr_lut_core0_rev3, - sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26, - 64, 8} - , - {&adj_pwr_lut_core1_rev3, - sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27, - 64, 8} - , - {&gainctrl_lut_core0_rev3, - sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]), - 26, 192, 32} - , - {&gainctrl_lut_core1_rev3, - sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]), - 27, 192, 32} - , - {&iq_lut_core0_rev3, - sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32} - , - {&iq_lut_core1_rev3, - sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32} - , - {&loft_lut_core0_rev3, - sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448, - 16} - , - {&loft_lut_core1_rev3, - sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448, - 16} - , -}; - -const u32 mimophytbl_info_sz_rev16 = - sizeof(mimophytbl_info_rev16) / sizeof(mimophytbl_info_rev16[0]); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h deleted file mode 100644 index dc8a84e85117..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2010 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. - */ - -#define ANT_SWCTRL_TBL_REV3_IDX (0) - -#include -#include "phy_int.h" - -extern const struct phytbl_info mimophytbl_info_rev0[], - mimophytbl_info_rev0_volatile[]; - -extern const u32 mimophytbl_info_sz_rev0, - mimophytbl_info_sz_rev0_volatile; - -extern const struct phytbl_info mimophytbl_info_rev3[], - mimophytbl_info_rev3_volatile[], - mimophytbl_info_rev3_volatile1[], - mimophytbl_info_rev3_volatile2[], - mimophytbl_info_rev3_volatile3[]; - -extern const u32 mimophytbl_info_sz_rev3, - mimophytbl_info_sz_rev3_volatile, - mimophytbl_info_sz_rev3_volatile1, - mimophytbl_info_sz_rev3_volatile2, - mimophytbl_info_sz_rev3_volatile3; - -extern const u32 noise_var_tbl_rev3[]; - -extern const struct phytbl_info mimophytbl_info_rev7[]; - -extern const u32 mimophytbl_info_sz_rev7; - -extern const u32 noise_var_tbl_rev7[]; - -extern const struct phytbl_info mimophytbl_info_rev16[]; - -extern const u32 mimophytbl_info_sz_rev16; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c b/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c deleted file mode 100644 index a0de5db0cd64..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2010 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. - */ - -/* - * This is "two-way" interface, acting as the SHIM layer between driver - * and PHY layer. The driver can optionally call this translation layer - * to do some preprocessing, then reach PHY. On the PHY->driver direction, - * all calls go through this layer since PHY doesn't have access to the - * driver's brcms_hardware pointer. - */ -#include -#include - -#include "main.h" -#include "mac80211_if.h" -#include "phy_shim.h" - -/* PHY SHIM module specific state */ -struct phy_shim_info { - struct brcms_hardware *wlc_hw; /* pointer to main wlc_hw structure */ - struct brcms_c_info *wlc; /* pointer to main wlc structure */ - struct brcms_info *wl; /* pointer to os-specific private state */ -}; - -struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw, - struct brcms_info *wl, - struct brcms_c_info *wlc) { - struct phy_shim_info *physhim = NULL; - - physhim = kzalloc(sizeof(struct phy_shim_info), GFP_ATOMIC); - if (!physhim) - return NULL; - - physhim->wlc_hw = wlc_hw; - physhim->wlc = wlc; - physhim->wl = wl; - - return physhim; -} - -void wlc_phy_shim_detach(struct phy_shim_info *physhim) -{ - kfree(physhim); -} - -struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, - void (*fn)(struct brcms_phy *pi), - void *arg, const char *name) -{ - return (struct wlapi_timer *) - brcms_init_timer(physhim->wl, (void (*)(void *))fn, - arg, name); -} - -void wlapi_free_timer(struct wlapi_timer *t) -{ - brcms_free_timer((struct brcms_timer *)t); -} - -void -wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic) -{ - brcms_add_timer((struct brcms_timer *)t, ms, periodic); -} - -bool wlapi_del_timer(struct wlapi_timer *t) -{ - return brcms_del_timer((struct brcms_timer *)t); -} - -void wlapi_intrson(struct phy_shim_info *physhim) -{ - brcms_intrson(physhim->wl); -} - -u32 wlapi_intrsoff(struct phy_shim_info *physhim) -{ - return brcms_intrsoff(physhim->wl); -} - -void wlapi_intrsrestore(struct phy_shim_info *physhim, u32 macintmask) -{ - brcms_intrsrestore(physhim->wl, macintmask); -} - -void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, u16 v) -{ - brcms_b_write_shm(physhim->wlc_hw, offset, v); -} - -u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset) -{ - return brcms_b_read_shm(physhim->wlc_hw, offset); -} - -void -wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, u16 mask, - u16 val, int bands) -{ - brcms_b_mhf(physhim->wlc_hw, idx, mask, val, bands); -} - -void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags) -{ - brcms_b_corereset(physhim->wlc_hw, flags); -} - -void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim) -{ - brcms_c_suspend_mac_and_wait(physhim->wlc); -} - -void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode) -{ - brcms_b_switch_macfreq(physhim->wlc_hw, spurmode); -} - -void wlapi_enable_mac(struct phy_shim_info *physhim) -{ - brcms_c_enable_mac(physhim->wlc); -} - -void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, u32 val) -{ - brcms_b_mctrl(physhim->wlc_hw, mask, val); -} - -void wlapi_bmac_phy_reset(struct phy_shim_info *physhim) -{ - brcms_b_phy_reset(physhim->wlc_hw); -} - -void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw) -{ - brcms_b_bw_set(physhim->wlc_hw, bw); -} - -u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim) -{ - return brcms_b_get_txant(physhim->wlc_hw); -} - -void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk) -{ - brcms_b_phyclk_fgc(physhim->wlc_hw, clk); -} - -void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk) -{ - brcms_b_macphyclk_set(physhim->wlc_hw, clk); -} - -void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on) -{ - brcms_b_core_phypll_ctl(physhim->wlc_hw, on); -} - -void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim) -{ - brcms_b_core_phypll_reset(physhim->wlc_hw); -} - -void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info *physhim) -{ - brcms_c_ucode_wake_override_set(physhim->wlc_hw, - BRCMS_WAKE_OVERRIDE_PHYREG); -} - -void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info *physhim) -{ - brcms_c_ucode_wake_override_clear(physhim->wlc_hw, - BRCMS_WAKE_OVERRIDE_PHYREG); -} - -void -wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int offset, - int len, void *buf) -{ - brcms_b_write_template_ram(physhim->wlc_hw, offset, len, buf); -} - -u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, u8 rate) -{ - return brcms_b_rate_shm_offset(physhim->wlc_hw, rate); -} - -void wlapi_ucode_sample_init(struct phy_shim_info *physhim) -{ -} - -void -wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint offset, void *buf, - int len, u32 sel) -{ - brcms_b_copyfrom_objmem(physhim->wlc_hw, offset, buf, len, sel); -} - -void -wlapi_copyto_objmem(struct phy_shim_info *physhim, uint offset, const void *buf, - int l, u32 sel) -{ - brcms_b_copyto_objmem(physhim->wlc_hw, offset, buf, l, sel); -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h b/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h deleted file mode 100644 index dd8774717ade..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2010 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. - */ - -/* - * phy_shim.h: stuff defined in phy_shim.c and included only by the phy - */ - -#ifndef _BRCM_PHY_SHIM_H_ -#define _BRCM_PHY_SHIM_H_ - -#include "types.h" - -#define RADAR_TYPE_NONE 0 /* Radar type None */ -#define RADAR_TYPE_ETSI_1 1 /* ETSI 1 Radar type */ -#define RADAR_TYPE_ETSI_2 2 /* ETSI 2 Radar type */ -#define RADAR_TYPE_ETSI_3 3 /* ETSI 3 Radar type */ -#define RADAR_TYPE_ITU_E 4 /* ITU E Radar type */ -#define RADAR_TYPE_ITU_K 5 /* ITU K Radar type */ -#define RADAR_TYPE_UNCLASSIFIED 6 /* Unclassified Radar type */ -#define RADAR_TYPE_BIN5 7 /* long pulse radar type */ -#define RADAR_TYPE_STG2 8 /* staggered-2 radar */ -#define RADAR_TYPE_STG3 9 /* staggered-3 radar */ -#define RADAR_TYPE_FRA 10 /* French radar */ - -/* French radar pulse widths */ -#define FRA_T1_20MHZ 52770 -#define FRA_T2_20MHZ 61538 -#define FRA_T3_20MHZ 66002 -#define FRA_T1_40MHZ 105541 -#define FRA_T2_40MHZ 123077 -#define FRA_T3_40MHZ 132004 -#define FRA_ERR_20MHZ 60 -#define FRA_ERR_40MHZ 120 - -#define ANTSEL_NA 0 /* No boardlevel selection available */ -#define ANTSEL_2x4 1 /* 2x4 boardlevel selection available */ -#define ANTSEL_2x3 2 /* 2x3 CB2 boardlevel selection available */ - -/* Rx Antenna diversity control values */ -#define ANT_RX_DIV_FORCE_0 0 /* Use antenna 0 */ -#define ANT_RX_DIV_FORCE_1 1 /* Use antenna 1 */ -#define ANT_RX_DIV_START_1 2 /* Choose starting with 1 */ -#define ANT_RX_DIV_START_0 3 /* Choose starting with 0 */ -#define ANT_RX_DIV_ENABLE 3 /* APHY bbConfig Enable RX Diversity */ -#define ANT_RX_DIV_DEF ANT_RX_DIV_START_0 /* default antdiv setting */ - -#define WL_ANT_RX_MAX 2 /* max 2 receive antennas */ -#define WL_ANT_HT_RX_MAX 3 /* max 3 receive antennas/cores */ -#define WL_ANT_IDX_1 0 /* antenna index 1 */ -#define WL_ANT_IDX_2 1 /* antenna index 2 */ - -/* values for n_preamble_type */ -#define BRCMS_N_PREAMBLE_MIXEDMODE 0 -#define BRCMS_N_PREAMBLE_GF 1 -#define BRCMS_N_PREAMBLE_GF_BRCM 2 - -#define WL_TX_POWER_RATES_LEGACY 45 -#define WL_TX_POWER_MCS20_FIRST 12 -#define WL_TX_POWER_MCS20_NUM 16 -#define WL_TX_POWER_MCS40_FIRST 28 -#define WL_TX_POWER_MCS40_NUM 17 - - -#define WL_TX_POWER_RATES 101 -#define WL_TX_POWER_CCK_FIRST 0 -#define WL_TX_POWER_CCK_NUM 4 -/* Index for first 20MHz OFDM SISO rate */ -#define WL_TX_POWER_OFDM_FIRST 4 -/* Index for first 20MHz OFDM CDD rate */ -#define WL_TX_POWER_OFDM20_CDD_FIRST 12 -/* Index for first 40MHz OFDM SISO rate */ -#define WL_TX_POWER_OFDM40_SISO_FIRST 52 -/* Index for first 40MHz OFDM CDD rate */ -#define WL_TX_POWER_OFDM40_CDD_FIRST 60 -#define WL_TX_POWER_OFDM_NUM 8 -/* Index for first 20MHz MCS SISO rate */ -#define WL_TX_POWER_MCS20_SISO_FIRST 20 -/* Index for first 20MHz MCS CDD rate */ -#define WL_TX_POWER_MCS20_CDD_FIRST 28 -/* Index for first 20MHz MCS STBC rate */ -#define WL_TX_POWER_MCS20_STBC_FIRST 36 -/* Index for first 20MHz MCS SDM rate */ -#define WL_TX_POWER_MCS20_SDM_FIRST 44 -/* Index for first 40MHz MCS SISO rate */ -#define WL_TX_POWER_MCS40_SISO_FIRST 68 -/* Index for first 40MHz MCS CDD rate */ -#define WL_TX_POWER_MCS40_CDD_FIRST 76 -/* Index for first 40MHz MCS STBC rate */ -#define WL_TX_POWER_MCS40_STBC_FIRST 84 -/* Index for first 40MHz MCS SDM rate */ -#define WL_TX_POWER_MCS40_SDM_FIRST 92 -#define WL_TX_POWER_MCS_1_STREAM_NUM 8 -#define WL_TX_POWER_MCS_2_STREAM_NUM 8 -/* Index for 40MHz rate MCS 32 */ -#define WL_TX_POWER_MCS_32 100 -#define WL_TX_POWER_MCS_32_NUM 1 - -/* sslpnphy specifics */ -/* Index for first 20MHz MCS SISO rate */ -#define WL_TX_POWER_MCS20_SISO_FIRST_SSN 12 - -/* struct tx_power::flags bits */ -#define WL_TX_POWER_F_ENABLED 1 -#define WL_TX_POWER_F_HW 2 -#define WL_TX_POWER_F_MIMO 4 -#define WL_TX_POWER_F_SISO 8 - -/* values to force tx/rx chain */ -#define BRCMS_N_TXRX_CHAIN0 0 -#define BRCMS_N_TXRX_CHAIN1 1 - -struct brcms_phy; - -struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw, - struct brcms_info *wl, - struct brcms_c_info *wlc); -void wlc_phy_shim_detach(struct phy_shim_info *physhim); - -/* PHY to WL utility functions */ -struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, - void (*fn)(struct brcms_phy *pi), - void *arg, const char *name); -void wlapi_free_timer(struct wlapi_timer *t); -void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic); -bool wlapi_del_timer(struct wlapi_timer *t); -void wlapi_intrson(struct phy_shim_info *physhim); -u32 wlapi_intrsoff(struct phy_shim_info *physhim); -void wlapi_intrsrestore(struct phy_shim_info *physhim, u32 macintmask); - -void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, u16 v); -u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset); -void wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, u16 mask, u16 val, - int bands); -void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags); -void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim); -void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode); -void wlapi_enable_mac(struct phy_shim_info *physhim); -void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, u32 val); -void wlapi_bmac_phy_reset(struct phy_shim_info *physhim); -void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw); -void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk); -void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk); -void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on); -void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim); -void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info *physhim); -void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info *physhim); -void wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int o, - int len, void *buf); -u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, u8 rate); -void wlapi_ucode_sample_init(struct phy_shim_info *physhim); -void wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint, void *buf, - int, u32 sel); -void wlapi_copyto_objmem(struct phy_shim_info *physhim, uint, const void *buf, - int, u32); - -void wlapi_high_update_phy_mode(struct phy_shim_info *physhim, u32 phy_mode); -u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim); - -#endif /* _BRCM_PHY_SHIM_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pmu.c b/drivers/net/wireless/brcm80211/brcmsmac/pmu.c deleted file mode 100644 index 71b80381f3ad..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/pmu.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2011 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 -#include -#include "pub.h" -#include "aiutils.h" -#include "pmu.h" -#include "soc.h" - -/* - * external LPO crystal frequency - */ -#define EXT_ILP_HZ 32768 - -/* - * Duration for ILP clock frequency measurment in milliseconds - * - * remark: 1000 must be an integer multiple of this duration - */ -#define ILP_CALC_DUR 10 - -/* Fields in pmucontrol */ -#define PCTL_ILP_DIV_MASK 0xffff0000 -#define PCTL_ILP_DIV_SHIFT 16 -#define PCTL_PLL_PLLCTL_UPD 0x00000400 /* rev 2 */ -#define PCTL_NOILP_ON_WAIT 0x00000200 /* rev 1 */ -#define PCTL_HT_REQ_EN 0x00000100 -#define PCTL_ALP_REQ_EN 0x00000080 -#define PCTL_XTALFREQ_MASK 0x0000007c -#define PCTL_XTALFREQ_SHIFT 2 -#define PCTL_ILP_DIV_EN 0x00000002 -#define PCTL_LPO_SEL 0x00000001 - -/* ILP clock */ -#define ILP_CLOCK 32000 - -/* ALP clock on pre-PMU chips */ -#define ALP_CLOCK 20000000 - -/* pmustatus */ -#define PST_EXTLPOAVAIL 0x0100 -#define PST_WDRESET 0x0080 -#define PST_INTPEND 0x0040 -#define PST_SBCLKST 0x0030 -#define PST_SBCLKST_ILP 0x0010 -#define PST_SBCLKST_ALP 0x0020 -#define PST_SBCLKST_HT 0x0030 -#define PST_ALPAVAIL 0x0008 -#define PST_HTAVAIL 0x0004 -#define PST_RESINIT 0x0003 - -/* PMU resource bit position */ -#define PMURES_BIT(bit) (1 << (bit)) - -/* PMU corerev and chip specific PLL controls. - * PMU_PLL_XX where is PMU corerev and is an arbitrary - * number to differentiate different PLLs controlled by the same PMU rev. - */ - -/* pmu XtalFreqRatio */ -#define PMU_XTALFREQ_REG_ILPCTR_MASK 0x00001FFF -#define PMU_XTALFREQ_REG_MEASURE_MASK 0x80000000 -#define PMU_XTALFREQ_REG_MEASURE_SHIFT 31 - -/* 4313 resources */ -#define RES4313_BB_PU_RSRC 0 -#define RES4313_ILP_REQ_RSRC 1 -#define RES4313_XTAL_PU_RSRC 2 -#define RES4313_ALP_AVAIL_RSRC 3 -#define RES4313_RADIO_PU_RSRC 4 -#define RES4313_BG_PU_RSRC 5 -#define RES4313_VREG1P4_PU_RSRC 6 -#define RES4313_AFE_PWRSW_RSRC 7 -#define RES4313_RX_PWRSW_RSRC 8 -#define RES4313_TX_PWRSW_RSRC 9 -#define RES4313_BB_PWRSW_RSRC 10 -#define RES4313_SYNTH_PWRSW_RSRC 11 -#define RES4313_MISC_PWRSW_RSRC 12 -#define RES4313_BB_PLL_PWRSW_RSRC 13 -#define RES4313_HT_AVAIL_RSRC 14 -#define RES4313_MACPHY_CLK_AVAIL_RSRC 15 - -u16 si_pmu_fast_pwrup_delay(struct si_pub *sih) -{ - uint delay = PMU_MAX_TRANSITION_DLY; - - switch (ai_get_chip_id(sih)) { - case BCMA_CHIP_ID_BCM43224: - case BCMA_CHIP_ID_BCM43225: - case BCMA_CHIP_ID_BCM4313: - delay = 3700; - break; - default: - break; - } - - return (u16) delay; -} - -u32 si_pmu_measure_alpclk(struct si_pub *sih) -{ - struct si_info *sii = container_of(sih, struct si_info, pub); - struct bcma_device *core; - u32 alp_khz; - - if (ai_get_pmurev(sih) < 10) - return 0; - - /* Remember original core before switch to chipc */ - core = sii->icbus->drv_cc.core; - - if (bcma_read32(core, CHIPCREGOFFS(pmustatus)) & PST_EXTLPOAVAIL) { - u32 ilp_ctr, alp_hz; - - /* - * Enable the reg to measure the freq, - * in case it was disabled before - */ - bcma_write32(core, CHIPCREGOFFS(pmu_xtalfreq), - 1U << PMU_XTALFREQ_REG_MEASURE_SHIFT); - - /* Delay for well over 4 ILP clocks */ - udelay(1000); - - /* Read the latched number of ALP ticks per 4 ILP ticks */ - ilp_ctr = bcma_read32(core, CHIPCREGOFFS(pmu_xtalfreq)) & - PMU_XTALFREQ_REG_ILPCTR_MASK; - - /* - * Turn off the PMU_XTALFREQ_REG_MEASURE_SHIFT - * bit to save power - */ - bcma_write32(core, CHIPCREGOFFS(pmu_xtalfreq), 0); - - /* Calculate ALP frequency */ - alp_hz = (ilp_ctr * EXT_ILP_HZ) / 4; - - /* - * Round to nearest 100KHz, and at - * the same time convert to KHz - */ - alp_khz = (alp_hz + 50000) / 100000 * 100; - } else - alp_khz = 0; - - return alp_khz; -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pmu.h b/drivers/net/wireless/brcm80211/brcmsmac/pmu.h deleted file mode 100644 index a014bbc4f935..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/pmu.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_PMU_H_ -#define _BRCM_PMU_H_ - -#include "types.h" - -u16 si_pmu_fast_pwrup_delay(struct si_pub *sih); -u32 si_pmu_measure_alpclk(struct si_pub *sih); - -#endif /* _BRCM_PMU_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pub.h b/drivers/net/wireless/brcm80211/brcmsmac/pub.h deleted file mode 100644 index 4da38cb4f318..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/pub.h +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_PUB_H_ -#define _BRCM_PUB_H_ - -#include -#include -#include "types.h" -#include "defs.h" - -#define BRCMS_NUMRATES 16 /* max # of rates in a rateset */ - -/* phy types */ -#define PHY_TYPE_A 0 /* Phy type A */ -#define PHY_TYPE_G 2 /* Phy type G */ -#define PHY_TYPE_N 4 /* Phy type N */ -#define PHY_TYPE_LP 5 /* Phy type Low Power A/B/G */ -#define PHY_TYPE_SSN 6 /* Phy type Single Stream N */ -#define PHY_TYPE_LCN 8 /* Phy type Single Stream N */ -#define PHY_TYPE_LCNXN 9 /* Phy type 2-stream N */ -#define PHY_TYPE_HT 7 /* Phy type 3-Stream N */ - -/* bw */ -#define BRCMS_10_MHZ 10 /* 10Mhz nphy channel bandwidth */ -#define BRCMS_20_MHZ 20 /* 20Mhz nphy channel bandwidth */ -#define BRCMS_40_MHZ 40 /* 40Mhz nphy channel bandwidth */ - -#define BRCMS_RSSI_MINVAL -200 /* Low value, e.g. for forcing roam */ -#define BRCMS_RSSI_NO_SIGNAL -91 /* NDIS RSSI link quality cutoffs */ -#define BRCMS_RSSI_VERY_LOW -80 /* Very low quality cutoffs */ -#define BRCMS_RSSI_LOW -70 /* Low quality cutoffs */ -#define BRCMS_RSSI_GOOD -68 /* Good quality cutoffs */ -#define BRCMS_RSSI_VERY_GOOD -58 /* Very good quality cutoffs */ -#define BRCMS_RSSI_EXCELLENT -57 /* Excellent quality cutoffs */ - -/* a large TX Power as an init value to factor out of min() calculations, - * keep low enough to fit in an s8, units are .25 dBm - */ -#define BRCMS_TXPWR_MAX (127) /* ~32 dBm = 1,500 mW */ - -/* rate related definitions */ -#define BRCMS_RATE_FLAG 0x80 /* Flag to indicate it is a basic rate */ -#define BRCMS_RATE_MASK 0x7f /* Rate value mask w/o basic rate flag */ - -/* legacy rx Antenna diversity for SISO rates */ -#define ANT_RX_DIV_FORCE_0 0 /* Use antenna 0 */ -#define ANT_RX_DIV_FORCE_1 1 /* Use antenna 1 */ -#define ANT_RX_DIV_START_1 2 /* Choose starting with 1 */ -#define ANT_RX_DIV_START_0 3 /* Choose starting with 0 */ -#define ANT_RX_DIV_ENABLE 3 /* APHY bbConfig Enable RX Diversity */ -/* default antdiv setting */ -#define ANT_RX_DIV_DEF ANT_RX_DIV_START_0 - -/* legacy rx Antenna diversity for SISO rates */ -/* Tx on antenna 0, "legacy term Main" */ -#define ANT_TX_FORCE_0 0 -/* Tx on antenna 1, "legacy term Aux" */ -#define ANT_TX_FORCE_1 1 -/* Tx on phy's last good Rx antenna */ -#define ANT_TX_LAST_RX 3 -/* driver's default tx antenna setting */ -#define ANT_TX_DEF 3 - -/* Tx Chain values */ -/* def bitmap of txchain */ -#define TXCHAIN_DEF 0x1 -/* default bitmap of tx chains for nphy */ -#define TXCHAIN_DEF_NPHY 0x3 -/* default bitmap of tx chains for nphy */ -#define TXCHAIN_DEF_HTPHY 0x7 -/* def bitmap of rxchain */ -#define RXCHAIN_DEF 0x1 -/* default bitmap of rx chains for nphy */ -#define RXCHAIN_DEF_NPHY 0x3 -/* default bitmap of rx chains for nphy */ -#define RXCHAIN_DEF_HTPHY 0x7 -/* no antenna switch */ -#define ANTSWITCH_NONE 0 -/* antenna switch on 4321CB2, 2of3 */ -#define ANTSWITCH_TYPE_1 1 -/* antenna switch on 4321MPCI, 2of3 */ -#define ANTSWITCH_TYPE_2 2 -/* antenna switch on 4322, 2of3 */ -#define ANTSWITCH_TYPE_3 3 - -#define RXBUFSZ PKTBUFSZ - -#define MAX_STREAMS_SUPPORTED 4 /* max number of streams supported */ - -struct brcm_rateset { - /* # rates in this set */ - u32 count; - /* rates in 500kbps units w/hi bit set if basic */ - u8 rates[WL_NUMRATES]; -}; - -struct brcms_c_rateset { - uint count; /* number of rates in rates[] */ - /* rates in 500kbps units w/hi bit set if basic */ - u8 rates[BRCMS_NUMRATES]; - u8 htphy_membership; /* HT PHY Membership */ - u8 mcs[MCSSET_LEN]; /* supported mcs index bit map */ -}; - -/* All the HT-specific default advertised capabilities (including AMPDU) - * should be grouped here at one place - */ -#define AMPDU_DEF_MPDU_DENSITY 6 /* default mpdu density (110 ==> 4us) */ - -/* wlc internal bss_info */ -struct brcms_bss_info { - u8 BSSID[ETH_ALEN]; /* network BSSID */ - u16 flags; /* flags for internal attributes */ - u8 SSID_len; /* the length of SSID */ - u8 SSID[32]; /* SSID string */ - s16 RSSI; /* receive signal strength (in dBm) */ - s16 SNR; /* receive signal SNR in dB */ - u16 beacon_period; /* units are Kusec */ - u16 chanspec; /* Channel num, bw, ctrl_sb and band */ - struct brcms_c_rateset rateset; /* supported rates */ -}; - -#define MAC80211_PROMISC_BCNS (1 << 0) -#define MAC80211_SCAN (1 << 1) - -/* - * Public portion of common driver state structure. - * The wlc handle points at this. - */ -struct brcms_pub { - struct brcms_c_info *wlc; - struct ieee80211_hw *ieee_hw; - struct scb_ampdu *global_ampdu; - uint mac80211_state; - uint unit; /* device instance number */ - uint corerev; /* core revision */ - struct si_pub *sih; /* SI handle (cookie for siutils calls) */ - bool up; /* interface up and running */ - bool hw_off; /* HW is off */ - bool hw_up; /* one time hw up/down */ - bool _piomode; /* true if pio mode */ - uint _nbands; /* # bands supported */ - uint now; /* # elapsed seconds */ - - bool delayed_down; /* down delayed */ - bool associated; /* true:part of [I]BSS, false: not */ - /* (union of stas_associated, aps_associated) */ - bool _ampdu; /* ampdu enabled or not */ - u8 _n_enab; /* bitmap of 11N + HT support */ - - u8 cur_etheraddr[ETH_ALEN]; /* our local ethernet address */ - - u32 radio_disabled; /* bit vector for radio disabled reasons */ - - u16 boardrev; /* version # of particular board */ - u8 sromrev; /* version # of the srom */ - char srom_ccode[BRCM_CNTRY_BUF_SZ]; /* Country Code in SROM */ - u32 boardflags; /* Board specific flags from srom */ - u32 boardflags2; /* More board flags if sromrev >= 4 */ - bool phy_11ncapable; /* the PHY/HW is capable of 802.11N */ - - struct wl_cnt *_cnt; /* low-level counters in driver */ - struct dentry *dbgfs_dir; -}; - -enum wlc_par_id { - IOV_MPC = 1, - IOV_RTSTHRESH, - IOV_QTXPOWER, - IOV_BCN_LI_BCN /* Beacon listen interval in # of beacons */ -}; - -/*********************************************** - * Feature-related macros to optimize out code * - * ********************************************* - */ - -#define ENAB_1x1 0x01 -#define ENAB_2x2 0x02 -#define ENAB_3x3 0x04 -#define ENAB_4x4 0x08 -#define SUPPORT_11N (ENAB_1x1|ENAB_2x2) -#define SUPPORT_HT (ENAB_1x1|ENAB_2x2|ENAB_3x3) - -/* WL11N Support */ -#define AMPDU_AGG_HOST 1 - -/* network protection config */ -#define BRCMS_PROT_G_SPEC 1 /* SPEC g protection */ -#define BRCMS_PROT_G_OVR 2 /* SPEC g prot override */ -#define BRCMS_PROT_G_USER 3 /* gmode specified by user */ -#define BRCMS_PROT_OVERLAP 4 /* overlap */ -#define BRCMS_PROT_N_USER 10 /* nmode specified by user */ -#define BRCMS_PROT_N_CFG 11 /* n protection */ -#define BRCMS_PROT_N_CFG_OVR 12 /* n protection override */ -#define BRCMS_PROT_N_NONGF 13 /* non-GF protection */ -#define BRCMS_PROT_N_NONGF_OVR 14 /* non-GF protection override */ -#define BRCMS_PROT_N_PAM_OVR 15 /* n preamble override */ -#define BRCMS_PROT_N_OBSS 16 /* non-HT OBSS present */ - -/* - * 54g modes (basic bits may still be overridden) - * - * GMODE_LEGACY_B - * Rateset: 1b, 2b, 5.5, 11 - * Preamble: Long - * Shortslot: Off - * GMODE_AUTO - * Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54 - * Extended Rateset: 6, 9, 12, 48 - * Preamble: Long - * Shortslot: Auto - * GMODE_ONLY - * Rateset: 1b, 2b, 5.5b, 11b, 18, 24b, 36, 54 - * Extended Rateset: 6b, 9, 12b, 48 - * Preamble: Short required - * Shortslot: Auto - * GMODE_B_DEFERRED - * Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54 - * Extended Rateset: 6, 9, 12, 48 - * Preamble: Long - * Shortslot: On - * GMODE_PERFORMANCE - * Rateset: 1b, 2b, 5.5b, 6b, 9, 11b, 12b, 18, 24b, 36, 48, 54 - * Preamble: Short required - * Shortslot: On and required - * GMODE_LRS - * Rateset: 1b, 2b, 5.5b, 11b - * Extended Rateset: 6, 9, 12, 18, 24, 36, 48, 54 - * Preamble: Long - * Shortslot: Auto - */ -#define GMODE_LEGACY_B 0 -#define GMODE_AUTO 1 -#define GMODE_ONLY 2 -#define GMODE_B_DEFERRED 3 -#define GMODE_PERFORMANCE 4 -#define GMODE_LRS 5 -#define GMODE_MAX 6 - -/* MCS values greater than this enable multiple streams */ -#define HIGHEST_SINGLE_STREAM_MCS 7 - -#define MAXBANDS 2 /* Maximum #of bands */ - -/* max number of antenna configurations */ -#define ANT_SELCFG_MAX 4 - -struct brcms_antselcfg { - u8 ant_config[ANT_SELCFG_MAX]; /* antenna configuration */ - u8 num_antcfg; /* number of available antenna configurations */ -}; - -/* common functions for every port */ -struct brcms_c_info *brcms_c_attach(struct brcms_info *wl, - struct bcma_device *core, uint unit, - bool piomode, uint *perr); -uint brcms_c_detach(struct brcms_c_info *wlc); -int brcms_c_up(struct brcms_c_info *wlc); -uint brcms_c_down(struct brcms_c_info *wlc); - -bool brcms_c_chipmatch(struct bcma_device *core); -void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx); -void brcms_c_reset(struct brcms_c_info *wlc); - -void brcms_c_intrson(struct brcms_c_info *wlc); -u32 brcms_c_intrsoff(struct brcms_c_info *wlc); -void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask); -bool brcms_c_intrsupd(struct brcms_c_info *wlc); -bool brcms_c_isr(struct brcms_c_info *wlc); -bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded); -bool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu, - struct ieee80211_hw *hw); -bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid); -void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val); -int brcms_c_get_header_len(void); -void brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset, - const u8 *addr); -void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, - const struct ieee80211_tx_queue_params *arg, - bool suspend); -struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc); -void brcms_c_ampdu_flush(struct brcms_c_info *wlc, struct ieee80211_sta *sta, - u16 tid); -void brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, - u8 ba_wsize, uint max_rx_ampdu_bytes); -int brcms_c_module_register(struct brcms_pub *pub, const char *name, - struct brcms_info *hdl, - int (*down_fn)(void *handle)); -int brcms_c_module_unregister(struct brcms_pub *pub, const char *name, - struct brcms_info *hdl); -void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc); -void brcms_c_enable_mac(struct brcms_c_info *wlc); -void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state); -void brcms_c_scan_start(struct brcms_c_info *wlc); -void brcms_c_scan_stop(struct brcms_c_info *wlc); -int brcms_c_get_curband(struct brcms_c_info *wlc); -int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel); -int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl); -void brcms_c_get_current_rateset(struct brcms_c_info *wlc, - struct brcm_rateset *currs); -int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs); -int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period); -u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx); -void brcms_c_set_shortslot_override(struct brcms_c_info *wlc, - s8 sslot_override); -void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval); -u64 brcms_c_tsf_get(struct brcms_c_info *wlc); -void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf); -int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr); -int brcms_c_get_tx_power(struct brcms_c_info *wlc); -bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc); -void brcms_c_mute(struct brcms_c_info *wlc, bool on); -bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc); -void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr); -void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, const u8 *bssid, - u8 *ssid, size_t ssid_len); -void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr); -void brcms_c_update_beacon(struct brcms_c_info *wlc); -void brcms_c_set_new_beacon(struct brcms_c_info *wlc, struct sk_buff *beacon, - u16 tim_offset, u16 dtim_period); -void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc, - struct sk_buff *probe_resp); -void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable); -void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, size_t ssid_len); - -#endif /* _BRCM_PUB_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/rate.c b/drivers/net/wireless/brcm80211/brcmsmac/rate.c deleted file mode 100644 index 0a0c0ad4f96f..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/rate.c +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Copyright (c) 2010 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 "d11.h" -#include "pub.h" -#include "rate.h" - -/* - * Rate info per rate: It tells whether a rate is ofdm or not and its phy_rate - * value - */ -const u8 rate_info[BRCM_MAXRATE + 1] = { - /* 0 1 2 3 4 5 6 7 8 9 */ -/* 0 */ 0x00, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 10 */ 0x00, 0x37, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x00, -/* 20 */ 0x00, 0x00, 0x6e, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, -/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, -/* 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 70 */ 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, -/* 100 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c -}; - -/* rates are in units of Kbps */ -const struct brcms_mcs_info mcs_table[MCS_TABLE_SIZE] = { - /* MCS 0: SS 1, MOD: BPSK, CR 1/2 */ - {6500, 13500, CEIL(6500 * 10, 9), CEIL(13500 * 10, 9), 0x00, - BRCM_RATE_6M}, - /* MCS 1: SS 1, MOD: QPSK, CR 1/2 */ - {13000, 27000, CEIL(13000 * 10, 9), CEIL(27000 * 10, 9), 0x08, - BRCM_RATE_12M}, - /* MCS 2: SS 1, MOD: QPSK, CR 3/4 */ - {19500, 40500, CEIL(19500 * 10, 9), CEIL(40500 * 10, 9), 0x0A, - BRCM_RATE_18M}, - /* MCS 3: SS 1, MOD: 16QAM, CR 1/2 */ - {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0x10, - BRCM_RATE_24M}, - /* MCS 4: SS 1, MOD: 16QAM, CR 3/4 */ - {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x12, - BRCM_RATE_36M}, - /* MCS 5: SS 1, MOD: 64QAM, CR 2/3 */ - {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0x19, - BRCM_RATE_48M}, - /* MCS 6: SS 1, MOD: 64QAM, CR 3/4 */ - {58500, 121500, CEIL(58500 * 10, 9), CEIL(121500 * 10, 9), 0x1A, - BRCM_RATE_54M}, - /* MCS 7: SS 1, MOD: 64QAM, CR 5/6 */ - {65000, 135000, CEIL(65000 * 10, 9), CEIL(135000 * 10, 9), 0x1C, - BRCM_RATE_54M}, - /* MCS 8: SS 2, MOD: BPSK, CR 1/2 */ - {13000, 27000, CEIL(13000 * 10, 9), CEIL(27000 * 10, 9), 0x40, - BRCM_RATE_6M}, - /* MCS 9: SS 2, MOD: QPSK, CR 1/2 */ - {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0x48, - BRCM_RATE_12M}, - /* MCS 10: SS 2, MOD: QPSK, CR 3/4 */ - {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x4A, - BRCM_RATE_18M}, - /* MCS 11: SS 2, MOD: 16QAM, CR 1/2 */ - {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0x50, - BRCM_RATE_24M}, - /* MCS 12: SS 2, MOD: 16QAM, CR 3/4 */ - {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0x52, - BRCM_RATE_36M}, - /* MCS 13: SS 2, MOD: 64QAM, CR 2/3 */ - {104000, 216000, CEIL(104000 * 10, 9), CEIL(216000 * 10, 9), 0x59, - BRCM_RATE_48M}, - /* MCS 14: SS 2, MOD: 64QAM, CR 3/4 */ - {117000, 243000, CEIL(117000 * 10, 9), CEIL(243000 * 10, 9), 0x5A, - BRCM_RATE_54M}, - /* MCS 15: SS 2, MOD: 64QAM, CR 5/6 */ - {130000, 270000, CEIL(130000 * 10, 9), CEIL(270000 * 10, 9), 0x5C, - BRCM_RATE_54M}, - /* MCS 16: SS 3, MOD: BPSK, CR 1/2 */ - {19500, 40500, CEIL(19500 * 10, 9), CEIL(40500 * 10, 9), 0x80, - BRCM_RATE_6M}, - /* MCS 17: SS 3, MOD: QPSK, CR 1/2 */ - {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x88, - BRCM_RATE_12M}, - /* MCS 18: SS 3, MOD: QPSK, CR 3/4 */ - {58500, 121500, CEIL(58500 * 10, 9), CEIL(121500 * 10, 9), 0x8A, - BRCM_RATE_18M}, - /* MCS 19: SS 3, MOD: 16QAM, CR 1/2 */ - {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0x90, - BRCM_RATE_24M}, - /* MCS 20: SS 3, MOD: 16QAM, CR 3/4 */ - {117000, 243000, CEIL(117000 * 10, 9), CEIL(243000 * 10, 9), 0x92, - BRCM_RATE_36M}, - /* MCS 21: SS 3, MOD: 64QAM, CR 2/3 */ - {156000, 324000, CEIL(156000 * 10, 9), CEIL(324000 * 10, 9), 0x99, - BRCM_RATE_48M}, - /* MCS 22: SS 3, MOD: 64QAM, CR 3/4 */ - {175500, 364500, CEIL(175500 * 10, 9), CEIL(364500 * 10, 9), 0x9A, - BRCM_RATE_54M}, - /* MCS 23: SS 3, MOD: 64QAM, CR 5/6 */ - {195000, 405000, CEIL(195000 * 10, 9), CEIL(405000 * 10, 9), 0x9B, - BRCM_RATE_54M}, - /* MCS 24: SS 4, MOD: BPSK, CR 1/2 */ - {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0xC0, - BRCM_RATE_6M}, - /* MCS 25: SS 4, MOD: QPSK, CR 1/2 */ - {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0xC8, - BRCM_RATE_12M}, - /* MCS 26: SS 4, MOD: QPSK, CR 3/4 */ - {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0xCA, - BRCM_RATE_18M}, - /* MCS 27: SS 4, MOD: 16QAM, CR 1/2 */ - {104000, 216000, CEIL(104000 * 10, 9), CEIL(216000 * 10, 9), 0xD0, - BRCM_RATE_24M}, - /* MCS 28: SS 4, MOD: 16QAM, CR 3/4 */ - {156000, 324000, CEIL(156000 * 10, 9), CEIL(324000 * 10, 9), 0xD2, - BRCM_RATE_36M}, - /* MCS 29: SS 4, MOD: 64QAM, CR 2/3 */ - {208000, 432000, CEIL(208000 * 10, 9), CEIL(432000 * 10, 9), 0xD9, - BRCM_RATE_48M}, - /* MCS 30: SS 4, MOD: 64QAM, CR 3/4 */ - {234000, 486000, CEIL(234000 * 10, 9), CEIL(486000 * 10, 9), 0xDA, - BRCM_RATE_54M}, - /* MCS 31: SS 4, MOD: 64QAM, CR 5/6 */ - {260000, 540000, CEIL(260000 * 10, 9), CEIL(540000 * 10, 9), 0xDB, - BRCM_RATE_54M}, - /* MCS 32: SS 1, MOD: BPSK, CR 1/2 */ - {0, 6000, 0, CEIL(6000 * 10, 9), 0x00, BRCM_RATE_6M}, -}; - -/* - * phycfg for legacy OFDM frames: code rate, modulation scheme, spatial streams - * Number of spatial streams: always 1 other fields: refer to table 78 of - * section 17.3.2.2 of the original .11a standard - */ -struct legacy_phycfg { - u32 rate_ofdm; /* ofdm mac rate */ - /* phy ctl byte 3, code rate, modulation type, # of streams */ - u8 tx_phy_ctl3; -}; - -/* Number of legacy_rate_cfg entries in the table */ -#define LEGACY_PHYCFG_TABLE_SIZE 12 - -/* - * In CCK mode LPPHY overloads OFDM Modulation bits with CCK Data Rate - * Eventually MIMOPHY would also be converted to this format - * 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps - */ -static const struct -legacy_phycfg legacy_phycfg_table[LEGACY_PHYCFG_TABLE_SIZE] = { - {BRCM_RATE_1M, 0x00}, /* CCK 1Mbps, data rate 0 */ - {BRCM_RATE_2M, 0x08}, /* CCK 2Mbps, data rate 1 */ - {BRCM_RATE_5M5, 0x10}, /* CCK 5.5Mbps, data rate 2 */ - {BRCM_RATE_11M, 0x18}, /* CCK 11Mbps, data rate 3 */ - /* OFDM 6Mbps, code rate 1/2, BPSK, 1 spatial stream */ - {BRCM_RATE_6M, 0x00}, - /* OFDM 9Mbps, code rate 3/4, BPSK, 1 spatial stream */ - {BRCM_RATE_9M, 0x02}, - /* OFDM 12Mbps, code rate 1/2, QPSK, 1 spatial stream */ - {BRCM_RATE_12M, 0x08}, - /* OFDM 18Mbps, code rate 3/4, QPSK, 1 spatial stream */ - {BRCM_RATE_18M, 0x0A}, - /* OFDM 24Mbps, code rate 1/2, 16-QAM, 1 spatial stream */ - {BRCM_RATE_24M, 0x10}, - /* OFDM 36Mbps, code rate 3/4, 16-QAM, 1 spatial stream */ - {BRCM_RATE_36M, 0x12}, - /* OFDM 48Mbps, code rate 2/3, 64-QAM, 1 spatial stream */ - {BRCM_RATE_48M, 0x19}, - /* OFDM 54Mbps, code rate 3/4, 64-QAM, 1 spatial stream */ - {BRCM_RATE_54M, 0x1A}, -}; - -/* Hardware rates (also encodes default basic rates) */ - -const struct brcms_c_rateset cck_ofdm_mimo_rates = { - 12, - /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48, */ - { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60, - /* 54 Mbps */ - 0x6c}, - 0x00, - { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00} -}; - -const struct brcms_c_rateset ofdm_mimo_rates = { - 8, - /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */ - { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c}, - 0x00, - { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00} -}; - -/* Default ratesets that include MCS32 for 40BW channels */ -static const struct brcms_c_rateset cck_ofdm_40bw_mimo_rates = { - 12, - /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48 */ - { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60, - /* 54 Mbps */ - 0x6c}, - 0x00, - { 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_c_rateset ofdm_40bw_mimo_rates = { - 8, - /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */ - { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c}, - 0x00, - { 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00} -}; - -const struct brcms_c_rateset cck_ofdm_rates = { - 12, - /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48,*/ - { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60, - /*54 Mbps */ - 0x6c}, - 0x00, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00} -}; - -const struct brcms_c_rateset gphy_legacy_rates = { - 4, - /* 1b, 2b, 5.5b, 11b Mbps */ - { 0x82, 0x84, 0x8b, 0x96}, - 0x00, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00} -}; - -const struct brcms_c_rateset ofdm_rates = { - 8, - /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */ - { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c}, - 0x00, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00} -}; - -const struct brcms_c_rateset cck_rates = { - 4, - /* 1b, 2b, 5.5, 11 Mbps */ - { 0x82, 0x84, 0x0b, 0x16}, - 0x00, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00} -}; - -/* check if rateset is valid. - * if check_brate is true, rateset without a basic rate is considered NOT valid. - */ -static bool brcms_c_rateset_valid(struct brcms_c_rateset *rs, bool check_brate) -{ - uint idx; - - if (!rs->count) - return false; - - if (!check_brate) - return true; - - /* error if no basic rates */ - for (idx = 0; idx < rs->count; idx++) { - if (rs->rates[idx] & BRCMS_RATE_FLAG) - return true; - } - return false; -} - -void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams) -{ - int i; - for (i = txstreams; i < MAX_STREAMS_SUPPORTED; i++) - rs->mcs[i] = 0; -} - -/* - * filter based on hardware rateset, and sort filtered rateset with basic - * bit(s) preserved, and check if resulting rateset is valid. -*/ -bool -brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs, - const struct brcms_c_rateset *hw_rs, - bool check_brate, u8 txstreams) -{ - u8 rateset[BRCM_MAXRATE + 1]; - u8 r; - uint count; - uint i; - - memset(rateset, 0, sizeof(rateset)); - count = rs->count; - - for (i = 0; i < count; i++) { - /* mask off "basic rate" bit, BRCMS_RATE_FLAG */ - r = (int)rs->rates[i] & BRCMS_RATE_MASK; - if ((r > BRCM_MAXRATE) || (rate_info[r] == 0)) - continue; - rateset[r] = rs->rates[i]; /* preserve basic bit! */ - } - - /* fill out the rates in order, looking at only supported rates */ - count = 0; - for (i = 0; i < hw_rs->count; i++) { - r = hw_rs->rates[i] & BRCMS_RATE_MASK; - if (rateset[r]) - rs->rates[count++] = rateset[r]; - } - - rs->count = count; - - /* only set the mcs rate bit if the equivalent hw mcs bit is set */ - for (i = 0; i < MCSSET_LEN; i++) - rs->mcs[i] = (rs->mcs[i] & hw_rs->mcs[i]); - - if (brcms_c_rateset_valid(rs, check_brate)) - return true; - else - return false; -} - -/* calculate the rate of a rx'd frame and return it as a ratespec */ -u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp) -{ - int phy_type; - u32 rspec = PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT; - - phy_type = - ((rxh->RxChan & RXS_CHAN_PHYTYPE_MASK) >> RXS_CHAN_PHYTYPE_SHIFT); - - if ((phy_type == PHY_TYPE_N) || (phy_type == PHY_TYPE_SSN) || - (phy_type == PHY_TYPE_LCN) || (phy_type == PHY_TYPE_HT)) { - switch (rxh->PhyRxStatus_0 & PRXS0_FT_MASK) { - case PRXS0_CCK: - rspec = - cck_phy2mac_rate( - ((struct cck_phy_hdr *) plcp)->signal); - break; - case PRXS0_OFDM: - rspec = - ofdm_phy2mac_rate( - ((struct ofdm_phy_hdr *) plcp)->rlpt[0]); - break; - case PRXS0_PREN: - rspec = (plcp[0] & MIMO_PLCP_MCS_MASK) | RSPEC_MIMORATE; - if (plcp[0] & MIMO_PLCP_40MHZ) { - /* indicate rspec is for 40 MHz mode */ - rspec &= ~RSPEC_BW_MASK; - rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); - } - break; - case PRXS0_STDN: - /* fallthru */ - default: - /* not supported, error condition */ - break; - } - if (plcp3_issgi(plcp[3])) - rspec |= RSPEC_SHORT_GI; - } else - if ((phy_type == PHY_TYPE_A) || (rxh->PhyRxStatus_0 & PRXS0_OFDM)) - rspec = ofdm_phy2mac_rate( - ((struct ofdm_phy_hdr *) plcp)->rlpt[0]); - else - rspec = cck_phy2mac_rate( - ((struct cck_phy_hdr *) plcp)->signal); - - return rspec; -} - -/* copy rateset src to dst as-is (no masking or sorting) */ -void brcms_c_rateset_copy(const struct brcms_c_rateset *src, - struct brcms_c_rateset *dst) -{ - memcpy(dst, src, sizeof(struct brcms_c_rateset)); -} - -/* - * Copy and selectively filter one rateset to another. - * 'basic_only' means only copy basic rates. - * 'rates' indicates cck (11b) and ofdm rates combinations. - * - 0: cck and ofdm - * - 1: cck only - * - 2: ofdm only - * 'xmask' is the copy mask (typically 0x7f or 0xff). - */ -void -brcms_c_rateset_filter(struct brcms_c_rateset *src, struct brcms_c_rateset *dst, - bool basic_only, u8 rates, uint xmask, bool mcsallow) -{ - uint i; - uint r; - uint count; - - count = 0; - for (i = 0; i < src->count; i++) { - r = src->rates[i]; - if (basic_only && !(r & BRCMS_RATE_FLAG)) - continue; - if (rates == BRCMS_RATES_CCK && - is_ofdm_rate((r & BRCMS_RATE_MASK))) - continue; - if (rates == BRCMS_RATES_OFDM && - is_cck_rate((r & BRCMS_RATE_MASK))) - continue; - dst->rates[count++] = r & xmask; - } - dst->count = count; - dst->htphy_membership = src->htphy_membership; - - if (mcsallow && rates != BRCMS_RATES_CCK) - memcpy(&dst->mcs[0], &src->mcs[0], MCSSET_LEN); - else - brcms_c_rateset_mcs_clear(dst); -} - -/* select rateset for a given phy_type and bandtype and filter it, sort it - * and fill rs_tgt with result - */ -void -brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt, - const struct brcms_c_rateset *rs_hw, - uint phy_type, int bandtype, bool cck_only, - uint rate_mask, bool mcsallow, u8 bw, u8 txstreams) -{ - const struct brcms_c_rateset *rs_dflt; - struct brcms_c_rateset rs_sel; - if ((PHYTYPE_IS(phy_type, PHY_TYPE_HT)) || - (PHYTYPE_IS(phy_type, PHY_TYPE_N)) || - (PHYTYPE_IS(phy_type, PHY_TYPE_LCN)) || - (PHYTYPE_IS(phy_type, PHY_TYPE_SSN))) { - if (bandtype == BRCM_BAND_5G) - rs_dflt = (bw == BRCMS_20_MHZ ? - &ofdm_mimo_rates : &ofdm_40bw_mimo_rates); - else - rs_dflt = (bw == BRCMS_20_MHZ ? - &cck_ofdm_mimo_rates : - &cck_ofdm_40bw_mimo_rates); - } else if (PHYTYPE_IS(phy_type, PHY_TYPE_LP)) { - rs_dflt = (bandtype == BRCM_BAND_5G) ? - &ofdm_rates : &cck_ofdm_rates; - } else if (PHYTYPE_IS(phy_type, PHY_TYPE_A)) { - rs_dflt = &ofdm_rates; - } else if (PHYTYPE_IS(phy_type, PHY_TYPE_G)) { - rs_dflt = &cck_ofdm_rates; - } else { - /* should not happen, error condition */ - rs_dflt = &cck_rates; /* force cck */ - } - - /* if hw rateset is not supplied, assign selected rateset to it */ - if (!rs_hw) - rs_hw = rs_dflt; - - brcms_c_rateset_copy(rs_dflt, &rs_sel); - brcms_c_rateset_mcs_upd(&rs_sel, txstreams); - brcms_c_rateset_filter(&rs_sel, rs_tgt, false, - cck_only ? BRCMS_RATES_CCK : BRCMS_RATES_CCK_OFDM, - rate_mask, mcsallow); - brcms_c_rate_hwrs_filter_sort_validate(rs_tgt, rs_hw, false, - mcsallow ? txstreams : 1); -} - -s16 brcms_c_rate_legacy_phyctl(uint rate) -{ - uint i; - for (i = 0; i < LEGACY_PHYCFG_TABLE_SIZE; i++) - if (rate == legacy_phycfg_table[i].rate_ofdm) - return legacy_phycfg_table[i].tx_phy_ctl3; - - return -1; -} - -void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset) -{ - uint i; - for (i = 0; i < MCSSET_LEN; i++) - rateset->mcs[i] = 0; -} - -void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, u8 txstreams) -{ - memcpy(&rateset->mcs[0], &cck_ofdm_mimo_rates.mcs[0], MCSSET_LEN); - brcms_c_rateset_mcs_upd(rateset, txstreams); -} - -/* Based on bandwidth passed, allow/disallow MCS 32 in the rateset */ -void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, u8 bw) -{ - if (bw == BRCMS_40_MHZ) - setbit(rateset->mcs, 32); - else - clrbit(rateset->mcs, 32); -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/rate.h b/drivers/net/wireless/brcm80211/brcmsmac/rate.h deleted file mode 100644 index 5bb88b78ed64..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/rate.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_RATE_H_ -#define _BRCM_RATE_H_ - -#include "types.h" -#include "d11.h" -#include "phy_hal.h" - -extern const u8 rate_info[]; -extern const struct brcms_c_rateset cck_ofdm_mimo_rates; -extern const struct brcms_c_rateset ofdm_mimo_rates; -extern const struct brcms_c_rateset cck_ofdm_rates; -extern const struct brcms_c_rateset ofdm_rates; -extern const struct brcms_c_rateset cck_rates; -extern const struct brcms_c_rateset gphy_legacy_rates; -extern const struct brcms_c_rateset rate_limit_1_2; - -struct brcms_mcs_info { - /* phy rate in kbps [20Mhz] */ - u32 phy_rate_20; - /* phy rate in kbps [40Mhz] */ - u32 phy_rate_40; - /* phy rate in kbps [20Mhz] with SGI */ - u32 phy_rate_20_sgi; - /* phy rate in kbps [40Mhz] with SGI */ - u32 phy_rate_40_sgi; - /* phy ctl byte 3, code rate, modulation type, # of streams */ - u8 tx_phy_ctl3; - /* matching legacy ofdm rate in 500bkps */ - u8 leg_ofdm; -}; - -#define BRCMS_MAXMCS 32 /* max valid mcs index */ -#define MCS_TABLE_SIZE 33 /* Number of mcs entries in the table */ -extern const struct brcms_mcs_info mcs_table[]; - -#define MCS_TXS_MASK 0xc0 /* num tx streams - 1 bit mask */ -#define MCS_TXS_SHIFT 6 /* num tx streams - 1 bit shift */ - -/* returns num tx streams - 1 */ -static inline u8 mcs_2_txstreams(u8 mcs) -{ - return (mcs_table[mcs].tx_phy_ctl3 & MCS_TXS_MASK) >> MCS_TXS_SHIFT; -} - -static inline uint mcs_2_rate(u8 mcs, bool is40, bool sgi) -{ - if (sgi) { - if (is40) - return mcs_table[mcs].phy_rate_40_sgi; - return mcs_table[mcs].phy_rate_20_sgi; - } - if (is40) - return mcs_table[mcs].phy_rate_40; - - return mcs_table[mcs].phy_rate_20; -} - -/* Macro to use the rate_info table */ -#define BRCMS_RATE_MASK_FULL 0xff /* Rate value mask with basic rate flag */ - -/* - * rate spec : holds rate and mode specific information required to generate a - * tx frame. Legacy CCK and OFDM information is held in the same manner as was - * done in the past (in the lower byte) the upper 3 bytes primarily hold MIMO - * specific information - */ - -/* rate spec bit fields */ - -/* Either 500Kbps units or MIMO MCS idx */ -#define RSPEC_RATE_MASK 0x0000007F -/* mimo MCS is stored in RSPEC_RATE_MASK */ -#define RSPEC_MIMORATE 0x08000000 -/* mimo bw mask */ -#define RSPEC_BW_MASK 0x00000700 -/* mimo bw shift */ -#define RSPEC_BW_SHIFT 8 -/* mimo Space/Time/Frequency mode mask */ -#define RSPEC_STF_MASK 0x00003800 -/* mimo Space/Time/Frequency mode shift */ -#define RSPEC_STF_SHIFT 11 -/* mimo coding type mask */ -#define RSPEC_CT_MASK 0x0000C000 -/* mimo coding type shift */ -#define RSPEC_CT_SHIFT 14 -/* mimo num STC streams per PLCP defn. */ -#define RSPEC_STC_MASK 0x00300000 -/* mimo num STC streams per PLCP defn. */ -#define RSPEC_STC_SHIFT 20 -/* mimo bit indicates adv coding in use */ -#define RSPEC_LDPC_CODING 0x00400000 -/* mimo bit indicates short GI in use */ -#define RSPEC_SHORT_GI 0x00800000 -/* bit indicates override both rate & mode */ -#define RSPEC_OVERRIDE 0x80000000 -/* bit indicates override rate only */ -#define RSPEC_OVERRIDE_MCS_ONLY 0x40000000 - -static inline bool rspec_active(u32 rspec) -{ - return rspec & (RSPEC_RATE_MASK | RSPEC_MIMORATE); -} - -static inline u8 rspec_phytxbyte2(u32 rspec) -{ - return (rspec & 0xff00) >> 8; -} - -static inline u32 rspec_get_bw(u32 rspec) -{ - return (rspec & RSPEC_BW_MASK) >> RSPEC_BW_SHIFT; -} - -static inline bool rspec_issgi(u32 rspec) -{ - return (rspec & RSPEC_SHORT_GI) == RSPEC_SHORT_GI; -} - -static inline bool rspec_is40mhz(u32 rspec) -{ - u32 bw = rspec_get_bw(rspec); - - return bw == PHY_TXC1_BW_40MHZ || bw == PHY_TXC1_BW_40MHZ_DUP; -} - -static inline uint rspec2rate(u32 rspec) -{ - if (rspec & RSPEC_MIMORATE) - return mcs_2_rate(rspec & RSPEC_RATE_MASK, rspec_is40mhz(rspec), - rspec_issgi(rspec)); - return rspec & RSPEC_RATE_MASK; -} - -static inline u8 rspec_mimoplcp3(u32 rspec) -{ - return (rspec & 0xf00000) >> 16; -} - -static inline bool plcp3_issgi(u8 plcp) -{ - return (plcp & (RSPEC_SHORT_GI >> 16)) != 0; -} - -static inline uint rspec_stc(u32 rspec) -{ - return (rspec & RSPEC_STC_MASK) >> RSPEC_STC_SHIFT; -} - -static inline uint rspec_stf(u32 rspec) -{ - return (rspec & RSPEC_STF_MASK) >> RSPEC_STF_SHIFT; -} - -static inline bool is_mcs_rate(u32 ratespec) -{ - return (ratespec & RSPEC_MIMORATE) != 0; -} - -static inline bool is_ofdm_rate(u32 ratespec) -{ - return !is_mcs_rate(ratespec) && - (rate_info[ratespec & RSPEC_RATE_MASK] & BRCMS_RATE_FLAG); -} - -static inline bool is_cck_rate(u32 ratespec) -{ - u32 rate = (ratespec & BRCMS_RATE_MASK); - - return !is_mcs_rate(ratespec) && ( - rate == BRCM_RATE_1M || rate == BRCM_RATE_2M || - rate == BRCM_RATE_5M5 || rate == BRCM_RATE_11M); -} - -static inline bool is_single_stream(u8 mcs) -{ - return mcs <= HIGHEST_SINGLE_STREAM_MCS || mcs == 32; -} - -static inline u8 cck_rspec(u8 cck) -{ - return cck & RSPEC_RATE_MASK; -} - -/* Convert encoded rate value in plcp header to numerical rates in 500 KHz - * increments */ -static inline u8 ofdm_phy2mac_rate(u8 rlpt) -{ - return wlc_phy_get_ofdm_rate_lookup()[rlpt & 0x7]; -} - -static inline u8 cck_phy2mac_rate(u8 signal) -{ - return signal/5; -} - -/* Rates specified in brcms_c_rateset_filter() */ -#define BRCMS_RATES_CCK_OFDM 0 -#define BRCMS_RATES_CCK 1 -#define BRCMS_RATES_OFDM 2 - -/* sanitize, and sort a rateset with the basic bit(s) preserved, validate - * rateset */ -bool brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs, - const struct brcms_c_rateset *hw_rs, - bool check_brate, u8 txstreams); -/* copy rateset src to dst as-is (no masking or sorting) */ -void brcms_c_rateset_copy(const struct brcms_c_rateset *src, - struct brcms_c_rateset *dst); - -/* would be nice to have these documented ... */ -u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp); - -void brcms_c_rateset_filter(struct brcms_c_rateset *src, - struct brcms_c_rateset *dst, bool basic_only, - u8 rates, uint xmask, bool mcsallow); - -void brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt, - const struct brcms_c_rateset *rs_hw, uint phy_type, - int bandtype, bool cck_only, uint rate_mask, - bool mcsallow, u8 bw, u8 txstreams); - -s16 brcms_c_rate_legacy_phyctl(uint rate); - -void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams); -void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset); -void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, u8 txstreams); -void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, u8 bw); - -#endif /* _BRCM_RATE_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/scb.h b/drivers/net/wireless/brcm80211/brcmsmac/scb.h deleted file mode 100644 index 3a3d73699f83..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/scb.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_SCB_H_ -#define _BRCM_SCB_H_ - -#include -#include -#include -#include "types.h" - -#define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */ - -#define AMPDU_MAX_SCB_TID NUMPRIO - -/* scb flags */ -#define SCB_WMECAP 0x0040 -#define SCB_HTCAP 0x10000 /* HT (MIMO) capable device */ -#define SCB_IS40 0x80000 /* 40MHz capable */ -#define SCB_STBCCAP 0x40000000 /* STBC Capable */ - -#define SCB_MAGIC 0xbeefcafe - -/* structure to store per-tid state for the ampdu initiator */ -struct scb_ampdu_tid_ini { - u8 tid; /* initiator tid for easy lookup */ - /* tx retry count; indexed by seq modulo */ - u8 txretry[AMPDU_TX_BA_MAX_WSIZE]; - struct scb *scb; /* backptr for easy lookup */ - u8 ba_wsize; /* negotiated ba window size (in pdu) */ -}; - -struct scb_ampdu { - struct scb *scb; /* back pointer for easy reference */ - u8 mpdu_density; /* mpdu density */ - u8 max_pdu; /* max pdus allowed in ampdu */ - u8 release; /* # of mpdus released at a time */ - u16 min_len; /* min mpdu len to support the density */ - u32 max_rx_ampdu_bytes; /* max ampdu rcv length; 8k, 16k, 32k, 64k */ - - /* - * This could easily be a ini[] pointer and we keep this info in wl - * itself instead of having mac80211 hold it for us. Also could be made - * dynamic per tid instead of static. - */ - /* initiator info - per tid (NUMPRIO): */ - struct scb_ampdu_tid_ini ini[AMPDU_MAX_SCB_TID]; -}; - -/* station control block - one per remote MAC address */ -struct scb { - u32 magic; - u32 flags; /* various bit flags as defined below */ - u32 flags2; /* various bit flags2 as defined below */ - u8 state; /* current state bitfield of auth/assoc process */ - u8 ea[ETH_ALEN]; /* station address */ - uint fragresid[NUMPRIO];/* #bytes unused in frag buffer per prio */ - - u16 seqctl[NUMPRIO]; /* seqctl of last received frame (for dups) */ - /* seqctl of last received frame (for dups) for non-QoS data and - * management */ - u16 seqctl_nonqos; - u16 seqnum[NUMPRIO];/* WME: driver maintained sw seqnum per priority */ - - struct scb_ampdu scb_ampdu; /* AMPDU state including per tid info */ -}; - -#endif /* _BRCM_SCB_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.c b/drivers/net/wireless/brcm80211/brcmsmac/stf.c deleted file mode 100644 index dd9162722495..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/stf.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * Copyright (c) 2010 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 "types.h" -#include "d11.h" -#include "rate.h" -#include "phy/phy_hal.h" -#include "channel.h" -#include "main.h" -#include "stf.h" -#include "debug.h" - -#define MIN_SPATIAL_EXPANSION 0 -#define MAX_SPATIAL_EXPANSION 1 - -#define BRCMS_STF_SS_STBC_RX(wlc) (BRCMS_ISNPHY(wlc->band) && \ - NREV_GT(wlc->band->phyrev, 3) && NREV_LE(wlc->band->phyrev, 6)) - -#define NSTS_1 1 -#define NSTS_2 2 -#define NSTS_3 3 -#define NSTS_4 4 - -static const u8 txcore_default[5] = { - (0), /* bitmap of the core enabled */ - (0x01), /* For Nsts = 1, enable core 1 */ - (0x03), /* For Nsts = 2, enable core 1 & 2 */ - (0x07), /* For Nsts = 3, enable core 1, 2 & 3 */ - (0x0f) /* For Nsts = 4, enable all cores */ -}; - -static void brcms_c_stf_stbc_rx_ht_update(struct brcms_c_info *wlc, int val) -{ - /* MIMOPHYs rev3-6 cannot receive STBC with only one rx core active */ - if (BRCMS_STF_SS_STBC_RX(wlc)) { - if ((wlc->stf->rxstreams == 1) && (val != HT_CAP_RX_STBC_NO)) - return; - } - - if (wlc->pub->up) { - brcms_c_update_beacon(wlc); - brcms_c_update_probe_resp(wlc, true); - } -} - -/* - * every WLC_TEMPSENSE_PERIOD seconds temperature check to decide whether to - * turn on/off txchain. - */ -void brcms_c_tempsense_upd(struct brcms_c_info *wlc) -{ - struct brcms_phy_pub *pi = wlc->band->pi; - uint active_chains, txchain; - - /* Check if the chip is too hot. Disable one Tx chain, if it is */ - /* high 4 bits are for Rx chain, low 4 bits are for Tx chain */ - active_chains = wlc_phy_stf_chain_active_get(pi); - txchain = active_chains & 0xf; - - if (wlc->stf->txchain == wlc->stf->hw_txchain) { - if (txchain && (txchain < wlc->stf->hw_txchain)) - /* turn off 1 tx chain */ - brcms_c_stf_txchain_set(wlc, txchain, true); - } else if (wlc->stf->txchain < wlc->stf->hw_txchain) { - if (txchain == wlc->stf->hw_txchain) - /* turn back on txchain */ - brcms_c_stf_txchain_set(wlc, txchain, true); - } -} - -void -brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, u16 *ss_algo_channel, - u16 chanspec) -{ - struct tx_power power; - u8 siso_mcs_id, cdd_mcs_id, stbc_mcs_id; - - /* Clear previous settings */ - *ss_algo_channel = 0; - - if (!wlc->pub->up) { - *ss_algo_channel = (u16) -1; - return; - } - - wlc_phy_txpower_get_current(wlc->band->pi, &power, - CHSPEC_CHANNEL(chanspec)); - - siso_mcs_id = (CHSPEC_IS40(chanspec)) ? - WL_TX_POWER_MCS40_SISO_FIRST : WL_TX_POWER_MCS20_SISO_FIRST; - cdd_mcs_id = (CHSPEC_IS40(chanspec)) ? - WL_TX_POWER_MCS40_CDD_FIRST : WL_TX_POWER_MCS20_CDD_FIRST; - stbc_mcs_id = (CHSPEC_IS40(chanspec)) ? - WL_TX_POWER_MCS40_STBC_FIRST : WL_TX_POWER_MCS20_STBC_FIRST; - - /* criteria to choose stf mode */ - - /* - * the "+3dbm (12 0.25db units)" is to account for the fact that with - * CDD, tx occurs on both chains - */ - if (power.target[siso_mcs_id] > (power.target[cdd_mcs_id] + 12)) - setbit(ss_algo_channel, PHY_TXC1_MODE_SISO); - else - setbit(ss_algo_channel, PHY_TXC1_MODE_CDD); - - /* - * STBC is ORed into to algo channel as STBC requires per-packet SCB - * capability check so cannot be default mode of operation. One of - * SISO, CDD have to be set - */ - if (power.target[siso_mcs_id] <= (power.target[stbc_mcs_id] + 12)) - setbit(ss_algo_channel, PHY_TXC1_MODE_STBC); -} - -static bool brcms_c_stf_stbc_tx_set(struct brcms_c_info *wlc, s32 int_val) -{ - if ((int_val != AUTO) && (int_val != OFF) && (int_val != ON)) - return false; - - if ((int_val == ON) && (wlc->stf->txstreams == 1)) - return false; - - wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = (s8) int_val; - wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = (s8) int_val; - - return true; -} - -bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val) -{ - if ((int_val != HT_CAP_RX_STBC_NO) - && (int_val != HT_CAP_RX_STBC_ONE_STREAM)) - return false; - - if (BRCMS_STF_SS_STBC_RX(wlc)) { - if ((int_val != HT_CAP_RX_STBC_NO) - && (wlc->stf->rxstreams == 1)) - return false; - } - - brcms_c_stf_stbc_rx_ht_update(wlc, int_val); - return true; -} - -static int brcms_c_stf_txcore_set(struct brcms_c_info *wlc, u8 Nsts, - u8 core_mask) -{ - brcms_dbg_ht(wlc->hw->d11core, "wl%d: Nsts %d core_mask %x\n", - wlc->pub->unit, Nsts, core_mask); - - if (hweight8(core_mask) > wlc->stf->txstreams) - core_mask = 0; - - if ((hweight8(core_mask) == wlc->stf->txstreams) && - ((core_mask & ~wlc->stf->txchain) - || !(core_mask & wlc->stf->txchain))) - core_mask = wlc->stf->txchain; - - wlc->stf->txcore[Nsts] = core_mask; - /* Nsts = 1..4, txcore index = 1..4 */ - if (Nsts == 1) { - /* Needs to update beacon and ucode generated response - * frames when 1 stream core map changed - */ - wlc->stf->phytxant = core_mask << PHY_TXC_ANT_SHIFT; - brcms_b_txant_set(wlc->hw, wlc->stf->phytxant); - if (wlc->clk) { - brcms_c_suspend_mac_and_wait(wlc); - brcms_c_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec); - brcms_c_enable_mac(wlc); - } - } - - return 0; -} - -static int brcms_c_stf_spatial_policy_set(struct brcms_c_info *wlc, int val) -{ - int i; - u8 core_mask = 0; - - brcms_dbg_ht(wlc->hw->d11core, "wl%d: val %x\n", wlc->pub->unit, - val); - - wlc->stf->spatial_policy = (s8) val; - for (i = 1; i <= MAX_STREAMS_SUPPORTED; i++) { - core_mask = (val == MAX_SPATIAL_EXPANSION) ? - wlc->stf->txchain : txcore_default[i]; - brcms_c_stf_txcore_set(wlc, (u8) i, core_mask); - } - return 0; -} - -/* - * Centralized txant update function. call it whenever wlc->stf->txant and/or - * wlc->stf->txchain change. - * - * Antennas are controlled by ucode indirectly, which drives PHY or GPIO to - * achieve various tx/rx antenna selection schemes - * - * legacy phy, bit 6 and bit 7 means antenna 0 and 1 respectively, bit6+bit7 - * means auto(last rx). - * for NREV<3, bit 6 and bit 7 means antenna 0 and 1 respectively, bit6+bit7 - * means last rx and do tx-antenna selection for SISO transmissions - * for NREV=3, bit 6 and bit _8_ means antenna 0 and 1 respectively, bit6+bit7 - * means last rx and do tx-antenna selection for SISO transmissions - * for NREV>=7, bit 6 and bit 7 mean antenna 0 and 1 respectively, nit6+bit7 - * means both cores active -*/ -static void _brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc) -{ - s8 txant; - - txant = (s8) wlc->stf->txant; - if (BRCMS_PHY_11N_CAP(wlc->band)) { - if (txant == ANT_TX_FORCE_0) { - wlc->stf->phytxant = PHY_TXC_ANT_0; - } else if (txant == ANT_TX_FORCE_1) { - wlc->stf->phytxant = PHY_TXC_ANT_1; - - if (BRCMS_ISNPHY(wlc->band) && - NREV_GE(wlc->band->phyrev, 3) - && NREV_LT(wlc->band->phyrev, 7)) - wlc->stf->phytxant = PHY_TXC_ANT_2; - } else { - if (BRCMS_ISLCNPHY(wlc->band) || - BRCMS_ISSSLPNPHY(wlc->band)) - wlc->stf->phytxant = PHY_TXC_LCNPHY_ANT_LAST; - else { - /* catch out of sync wlc->stf->txcore */ - WARN_ON(wlc->stf->txchain <= 0); - wlc->stf->phytxant = - wlc->stf->txchain << PHY_TXC_ANT_SHIFT; - } - } - } else { - if (txant == ANT_TX_FORCE_0) - wlc->stf->phytxant = PHY_TXC_OLD_ANT_0; - else if (txant == ANT_TX_FORCE_1) - wlc->stf->phytxant = PHY_TXC_OLD_ANT_1; - else - wlc->stf->phytxant = PHY_TXC_OLD_ANT_LAST; - } - - brcms_b_txant_set(wlc->hw, wlc->stf->phytxant); -} - -int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, bool force) -{ - u8 txchain = (u8) int_val; - u8 txstreams; - uint i; - - if (wlc->stf->txchain == txchain) - return 0; - - if ((txchain & ~wlc->stf->hw_txchain) - || !(txchain & wlc->stf->hw_txchain)) - return -EINVAL; - - /* - * if nrate override is configured to be non-SISO STF mode, reject - * reducing txchain to 1 - */ - txstreams = (u8) hweight8(txchain); - if (txstreams > MAX_STREAMS_SUPPORTED) - return -EINVAL; - - wlc->stf->txchain = txchain; - wlc->stf->txstreams = txstreams; - brcms_c_stf_stbc_tx_set(wlc, wlc->band->band_stf_stbc_tx); - brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); - brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); - wlc->stf->txant = - (wlc->stf->txstreams == 1) ? ANT_TX_FORCE_0 : ANT_TX_DEF; - _brcms_c_stf_phy_txant_upd(wlc); - - wlc_phy_stf_chain_set(wlc->band->pi, wlc->stf->txchain, - wlc->stf->rxchain); - - for (i = 1; i <= MAX_STREAMS_SUPPORTED; i++) - brcms_c_stf_txcore_set(wlc, (u8) i, txcore_default[i]); - - return 0; -} - -/* - * update wlc->stf->ss_opmode which represents the operational stf_ss mode - * we're using - */ -int brcms_c_stf_ss_update(struct brcms_c_info *wlc, struct brcms_band *band) -{ - int ret_code = 0; - u8 prev_stf_ss; - u8 upd_stf_ss; - - prev_stf_ss = wlc->stf->ss_opmode; - - /* - * NOTE: opmode can only be SISO or CDD as STBC is decided on a - * per-packet basis - */ - if (BRCMS_STBC_CAP_PHY(wlc) && - wlc->stf->ss_algosel_auto - && (wlc->stf->ss_algo_channel != (u16) -1)) { - upd_stf_ss = (wlc->stf->txstreams == 1 || - isset(&wlc->stf->ss_algo_channel, - PHY_TXC1_MODE_SISO)) ? - PHY_TXC1_MODE_SISO : PHY_TXC1_MODE_CDD; - } else { - if (wlc->band != band) - return ret_code; - upd_stf_ss = (wlc->stf->txstreams == 1) ? - PHY_TXC1_MODE_SISO : band->band_stf_ss_mode; - } - if (prev_stf_ss != upd_stf_ss) { - wlc->stf->ss_opmode = upd_stf_ss; - brcms_b_band_stf_ss_set(wlc->hw, upd_stf_ss); - } - - return ret_code; -} - -int brcms_c_stf_attach(struct brcms_c_info *wlc) -{ - wlc->bandstate[BAND_2G_INDEX]->band_stf_ss_mode = PHY_TXC1_MODE_SISO; - wlc->bandstate[BAND_5G_INDEX]->band_stf_ss_mode = PHY_TXC1_MODE_CDD; - - if (BRCMS_ISNPHY(wlc->band) && - (wlc_phy_txpower_hw_ctrl_get(wlc->band->pi) != PHY_TPC_HW_ON)) - wlc->bandstate[BAND_2G_INDEX]->band_stf_ss_mode = - PHY_TXC1_MODE_CDD; - brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); - brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); - - brcms_c_stf_stbc_rx_ht_update(wlc, HT_CAP_RX_STBC_NO); - wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = OFF; - wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = OFF; - - if (BRCMS_STBC_CAP_PHY(wlc)) { - wlc->stf->ss_algosel_auto = true; - /* Init the default value */ - wlc->stf->ss_algo_channel = (u16) -1; - } - return 0; -} - -void brcms_c_stf_detach(struct brcms_c_info *wlc) -{ -} - -void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc) -{ - _brcms_c_stf_phy_txant_upd(wlc); -} - -void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc) -{ - struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; - - /* get available rx/tx chains */ - wlc->stf->hw_txchain = sprom->txchain; - wlc->stf->hw_rxchain = sprom->rxchain; - - /* these parameter are intended to be used for all PHY types */ - if (wlc->stf->hw_txchain == 0 || wlc->stf->hw_txchain == 0xf) { - if (BRCMS_ISNPHY(wlc->band)) - wlc->stf->hw_txchain = TXCHAIN_DEF_NPHY; - else - wlc->stf->hw_txchain = TXCHAIN_DEF; - } - - wlc->stf->txchain = wlc->stf->hw_txchain; - wlc->stf->txstreams = (u8) hweight8(wlc->stf->hw_txchain); - - if (wlc->stf->hw_rxchain == 0 || wlc->stf->hw_rxchain == 0xf) { - if (BRCMS_ISNPHY(wlc->band)) - wlc->stf->hw_rxchain = RXCHAIN_DEF_NPHY; - else - wlc->stf->hw_rxchain = RXCHAIN_DEF; - } - - wlc->stf->rxchain = wlc->stf->hw_rxchain; - wlc->stf->rxstreams = (u8) hweight8(wlc->stf->hw_rxchain); - - /* initialize the txcore table */ - memcpy(wlc->stf->txcore, txcore_default, sizeof(wlc->stf->txcore)); - - /* default spatial_policy */ - wlc->stf->spatial_policy = MIN_SPATIAL_EXPANSION; - brcms_c_stf_spatial_policy_set(wlc, MIN_SPATIAL_EXPANSION); -} - -static u16 _brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, - u32 rspec) -{ - u16 phytxant = wlc->stf->phytxant; - - if (rspec_stf(rspec) != PHY_TXC1_MODE_SISO) - phytxant = wlc->stf->txchain << PHY_TXC_ANT_SHIFT; - else if (wlc->stf->txant == ANT_TX_DEF) - phytxant = wlc->stf->txchain << PHY_TXC_ANT_SHIFT; - phytxant &= PHY_TXC_ANT_MASK; - return phytxant; -} - -u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, u32 rspec) -{ - return _brcms_c_stf_phytxchain_sel(wlc, rspec); -} - -u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, u32 rspec) -{ - u16 phytxant = wlc->stf->phytxant; - u16 mask = PHY_TXC_ANT_MASK; - - /* for non-siso rates or default setting, use the available chains */ - if (BRCMS_ISNPHY(wlc->band)) { - phytxant = _brcms_c_stf_phytxchain_sel(wlc, rspec); - mask = PHY_TXC_HTANT_MASK; - } - phytxant |= phytxant & mask; - return phytxant; -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.h b/drivers/net/wireless/brcm80211/brcmsmac/stf.h deleted file mode 100644 index ba9493009a33..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/stf.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_STF_H_ -#define _BRCM_STF_H_ - -#include "types.h" - -int brcms_c_stf_attach(struct brcms_c_info *wlc); -void brcms_c_stf_detach(struct brcms_c_info *wlc); - -void brcms_c_tempsense_upd(struct brcms_c_info *wlc); -void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, - u16 *ss_algo_channel, u16 chanspec); -int brcms_c_stf_ss_update(struct brcms_c_info *wlc, struct brcms_band *band); -void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); -int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, bool force); -bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val); -void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); -void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc); -u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, u32 rspec); -u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, u32 rspec); - -#endif /* _BRCM_STF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/types.h b/drivers/net/wireless/brcm80211/brcmsmac/types.h deleted file mode 100644 index ae1f3ad40d45..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/types.h +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_TYPES_H_ -#define _BRCM_TYPES_H_ - -#include -#include - -#define WL_CHAN_FREQ_RANGE_2G 0 -#define WL_CHAN_FREQ_RANGE_5GL 1 -#define WL_CHAN_FREQ_RANGE_5GM 2 -#define WL_CHAN_FREQ_RANGE_5GH 3 - -/* boardflags */ - -/* Board has gpio 9 controlling the PA */ -#define BFL_PACTRL 0x00000002 -/* Not ok to power down the chip pll and oscillator */ -#define BFL_NOPLLDOWN 0x00000020 -/* Board supports the Front End Module */ -#define BFL_FEM 0x00000800 -/* Board has an external LNA in 2.4GHz band */ -#define BFL_EXTLNA 0x00001000 -/* Board has no PA */ -#define BFL_NOPA 0x00010000 -/* Power topology uses BUCKBOOST */ -#define BFL_BUCKBOOST 0x00200000 -/* Board has FEM and switch to share antenna w/ BT */ -#define BFL_FEM_BT 0x00400000 -/* Power topology doesn't use CBUCK */ -#define BFL_NOCBUCK 0x00800000 -/* Power topology uses PALDO */ -#define BFL_PALDO 0x02000000 -/* Board has an external LNA in 5GHz band */ -#define BFL_EXTLNA_5GHz 0x10000000 - -/* boardflags2 */ - -/* Board has an external rxbb regulator */ -#define BFL2_RXBB_INT_REG_DIS 0x00000001 -/* Flag to implement alternative A-band PLL settings */ -#define BFL2_APLL_WAR 0x00000002 -/* Board permits enabling TX Power Control */ -#define BFL2_TXPWRCTRL_EN 0x00000004 -/* Board supports the 2X4 diversity switch */ -#define BFL2_2X4_DIV 0x00000008 -/* Board supports 5G band power gain */ -#define BFL2_5G_PWRGAIN 0x00000010 -/* Board overrides ASPM and Clkreq settings */ -#define BFL2_PCIEWAR_OVR 0x00000020 -#define BFL2_LEGACY 0x00000080 -/* 4321mcm93 board uses Skyworks FEM */ -#define BFL2_SKWRKFEM_BRD 0x00000100 -/* Board has a WAR for clock-harmonic spurs */ -#define BFL2_SPUR_WAR 0x00000200 -/* Flag to narrow G-band PLL loop b/w */ -#define BFL2_GPLL_WAR 0x00000400 -/* Tx CCK pkts on Ant 0 only */ -#define BFL2_SINGLEANT_CCK 0x00001000 -/* WAR to reduce and avoid clock-harmonic spurs in 2G */ -#define BFL2_2G_SPUR_WAR 0x00002000 -/* Flag to widen G-band PLL loop b/w */ -#define BFL2_GPLL_WAR2 0x00010000 -#define BFL2_IPALVLSHIFT_3P3 0x00020000 -/* Use internal envelope detector for TX IQCAL */ -#define BFL2_INTERNDET_TXIQCAL 0x00040000 -/* Keep the buffered Xtal output from radio "ON". Most drivers will turn it - * off without this flag to save power. */ -#define BFL2_XTALBUFOUTEN 0x00080000 - -/* - * board specific GPIO assignment, gpio 0-3 are also customer-configurable - * led - */ - -/* bit 9 controls the PA on new 4306 boards */ -#define BOARD_GPIO_PACTRL 0x200 -#define BOARD_GPIO_12 0x1000 -#define BOARD_GPIO_13 0x2000 - -/* **** Core type/rev defaults **** */ -#define D11CONF 0x0fffffb0 /* Supported D11 revs: 4, 5, 7-27 - * also need to update wlc.h MAXCOREREV - */ - -#define NCONF 0x000001ff /* Supported nphy revs: - * 0 4321a0 - * 1 4321a1 - * 2 4321b0/b1/c0/c1 - * 3 4322a0 - * 4 4322a1 - * 5 4716a0 - * 6 43222a0, 43224a0 - * 7 43226a0 - * 8 5357a0, 43236a0 - */ - -#define LCNCONF 0x00000007 /* Supported lcnphy revs: - * 0 4313a0, 4336a0, 4330a0 - * 1 - * 2 4330a0 - */ - -#define SSLPNCONF 0x0000000f /* Supported sslpnphy revs: - * 0 4329a0/k0 - * 1 4329b0/4329C0 - * 2 4319a0 - * 3 5356a0 - */ - -/******************************************************************** - * Phy/Core Configuration. Defines macros to to check core phy/rev * - * compile-time configuration. Defines default core support. * - * ****************************************************************** - */ - -/* Basic macros to check a configuration bitmask */ - -#define CONF_HAS(config, val) ((config) & (1 << (val))) -#define CONF_MSK(config, mask) ((config) & (mask)) -#define MSK_RANGE(low, hi) ((1 << ((hi)+1)) - (1 << (low))) -#define CONF_RANGE(config, low, hi) (CONF_MSK(config, MSK_RANGE(low, high))) - -#define CONF_IS(config, val) ((config) == (1 << (val))) -#define CONF_GE(config, val) ((config) & (0-(1 << (val)))) -#define CONF_GT(config, val) ((config) & (0-2*(1 << (val)))) -#define CONF_LT(config, val) ((config) & ((1 << (val))-1)) -#define CONF_LE(config, val) ((config) & (2*(1 << (val))-1)) - -/* Wrappers for some of the above, specific to config constants */ - -#define NCONF_HAS(val) CONF_HAS(NCONF, val) -#define NCONF_MSK(mask) CONF_MSK(NCONF, mask) -#define NCONF_IS(val) CONF_IS(NCONF, val) -#define NCONF_GE(val) CONF_GE(NCONF, val) -#define NCONF_GT(val) CONF_GT(NCONF, val) -#define NCONF_LT(val) CONF_LT(NCONF, val) -#define NCONF_LE(val) CONF_LE(NCONF, val) - -#define LCNCONF_HAS(val) CONF_HAS(LCNCONF, val) -#define LCNCONF_MSK(mask) CONF_MSK(LCNCONF, mask) -#define LCNCONF_IS(val) CONF_IS(LCNCONF, val) -#define LCNCONF_GE(val) CONF_GE(LCNCONF, val) -#define LCNCONF_GT(val) CONF_GT(LCNCONF, val) -#define LCNCONF_LT(val) CONF_LT(LCNCONF, val) -#define LCNCONF_LE(val) CONF_LE(LCNCONF, val) - -#define D11CONF_HAS(val) CONF_HAS(D11CONF, val) -#define D11CONF_MSK(mask) CONF_MSK(D11CONF, mask) -#define D11CONF_IS(val) CONF_IS(D11CONF, val) -#define D11CONF_GE(val) CONF_GE(D11CONF, val) -#define D11CONF_GT(val) CONF_GT(D11CONF, val) -#define D11CONF_LT(val) CONF_LT(D11CONF, val) -#define D11CONF_LE(val) CONF_LE(D11CONF, val) - -#define PHYCONF_HAS(val) CONF_HAS(PHYTYPE, val) -#define PHYCONF_IS(val) CONF_IS(PHYTYPE, val) - -#define NREV_IS(var, val) \ - (NCONF_HAS(val) && (NCONF_IS(val) || ((var) == (val)))) - -#define NREV_GE(var, val) \ - (NCONF_GE(val) && (!NCONF_LT(val) || ((var) >= (val)))) - -#define NREV_GT(var, val) \ - (NCONF_GT(val) && (!NCONF_LE(val) || ((var) > (val)))) - -#define NREV_LT(var, val) \ - (NCONF_LT(val) && (!NCONF_GE(val) || ((var) < (val)))) - -#define NREV_LE(var, val) \ - (NCONF_LE(val) && (!NCONF_GT(val) || ((var) <= (val)))) - -#define LCNREV_IS(var, val) \ - (LCNCONF_HAS(val) && (LCNCONF_IS(val) || ((var) == (val)))) - -#define LCNREV_GE(var, val) \ - (LCNCONF_GE(val) && (!LCNCONF_LT(val) || ((var) >= (val)))) - -#define LCNREV_GT(var, val) \ - (LCNCONF_GT(val) && (!LCNCONF_LE(val) || ((var) > (val)))) - -#define LCNREV_LT(var, val) \ - (LCNCONF_LT(val) && (!LCNCONF_GE(val) || ((var) < (val)))) - -#define LCNREV_LE(var, val) \ - (LCNCONF_LE(val) && (!LCNCONF_GT(val) || ((var) <= (val)))) - -#define D11REV_IS(var, val) \ - (D11CONF_HAS(val) && (D11CONF_IS(val) || ((var) == (val)))) - -#define D11REV_GE(var, val) \ - (D11CONF_GE(val) && (!D11CONF_LT(val) || ((var) >= (val)))) - -#define D11REV_GT(var, val) \ - (D11CONF_GT(val) && (!D11CONF_LE(val) || ((var) > (val)))) - -#define D11REV_LT(var, val) \ - (D11CONF_LT(val) && (!D11CONF_GE(val) || ((var) < (val)))) - -#define D11REV_LE(var, val) \ - (D11CONF_LE(val) && (!D11CONF_GT(val) || ((var) <= (val)))) - -#define PHYTYPE_IS(var, val)\ - (PHYCONF_HAS(val) && (PHYCONF_IS(val) || ((var) == (val)))) - -/* Set up PHYTYPE automatically: (depends on PHY_TYPE_X, from d11.h) */ - -#define _PHYCONF_N (1 << PHY_TYPE_N) -#define _PHYCONF_LCN (1 << PHY_TYPE_LCN) -#define _PHYCONF_SSLPN (1 << PHY_TYPE_SSN) - -#define PHYTYPE (_PHYCONF_N | _PHYCONF_LCN | _PHYCONF_SSLPN) - -/* Utility macro to identify 802.11n (HT) capable PHYs */ -#define PHYTYPE_11N_CAP(phytype) \ - (PHYTYPE_IS(phytype, PHY_TYPE_N) || \ - PHYTYPE_IS(phytype, PHY_TYPE_LCN) || \ - PHYTYPE_IS(phytype, PHY_TYPE_SSN)) - -/* Last but not least: shorter wlc-specific var checks */ -#define BRCMS_ISNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_N) -#define BRCMS_ISLCNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_LCN) -#define BRCMS_ISSSLPNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_SSN) - -#define BRCMS_PHY_11N_CAP(band) PHYTYPE_11N_CAP((band)->phytype) - -/********************************************************************** - * ------------- End of Core phy/rev configuration. ----------------- * - * ******************************************************************** - */ - -#define BCMMSG(dev, fmt, args...) \ -do { \ - if (brcm_msg_level & BRCM_DL_INFO) \ - wiphy_err(dev, "%s: " fmt, __func__, ##args); \ -} while (0) - -#ifdef CONFIG_BCM47XX -/* - * bcm4716 (which includes 4717 & 4718), plus 4706 on PCIe can reorder - * transactions. As a fix, a read after write is performed on certain places - * in the code. Older chips and the newer 5357 family don't require this fix. - */ -#define bcma_wflush16(c, o, v) \ - ({ bcma_write16(c, o, v); (void)bcma_read16(c, o); }) -#else -#define bcma_wflush16(c, o, v) bcma_write16(c, o, v) -#endif /* CONFIG_BCM47XX */ - -/* multi-bool data type: set of bools, mbool is true if any is set */ - -/* set one bool */ -#define mboolset(mb, bit) ((mb) |= (bit)) -/* clear one bool */ -#define mboolclr(mb, bit) ((mb) &= ~(bit)) -/* true if one bool is set */ -#define mboolisset(mb, bit) (((mb) & (bit)) != 0) -#define mboolmaskset(mb, mask, val) ((mb) = (((mb) & ~(mask)) | (val))) - -#define CEIL(x, y) (((x) + ((y)-1)) / (y)) - -/* forward declarations */ -struct wiphy; -struct ieee80211_sta; -struct ieee80211_tx_queue_params; -struct brcms_info; -struct brcms_c_info; -struct brcms_hardware; -struct brcms_band; -struct dma_pub; -struct si_pub; -struct tx_status; -struct d11rxhdr; -struct txpwr_limits; - -/* iovar structure */ -struct brcmu_iovar { - const char *name; /* name for lookup and display */ - u16 varid; /* id for switch */ - u16 flags; /* driver-specific flag bits */ - u16 type; /* base type of argument */ - u16 minlen; /* min length for buffer vars */ -}; - -/* brcm_msg_level is a bit vector with defs in defs.h */ -extern u32 brcm_msg_level; - -#endif /* _BRCM_TYPES_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c b/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c deleted file mode 100644 index 80e3ccf865e3..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2010 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 "types.h" -#include - -enum { - D11UCODE_NAMETAG_START = 0, - D11LCN0BSINITVALS24, - D11LCN0INITVALS24, - D11LCN1BSINITVALS24, - D11LCN1INITVALS24, - D11LCN2BSINITVALS24, - D11LCN2INITVALS24, - D11N0ABSINITVALS16, - D11N0BSINITVALS16, - D11N0INITVALS16, - D11UCODE_OVERSIGHT16_MIMO, - D11UCODE_OVERSIGHT16_MIMOSZ, - D11UCODE_OVERSIGHT24_LCN, - D11UCODE_OVERSIGHT24_LCNSZ, - D11UCODE_OVERSIGHT_BOMMAJOR, - D11UCODE_OVERSIGHT_BOMMINOR -}; - -int brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode) -{ - int rc; - - rc = brcms_check_firmwares(wl); - - rc = rc < 0 ? rc : - brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn0bsinitvals24, - D11LCN0BSINITVALS24); - rc = rc < 0 ? - rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn0initvals24, - D11LCN0INITVALS24); - rc = rc < 0 ? - rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn1bsinitvals24, - D11LCN1BSINITVALS24); - rc = rc < 0 ? - rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn1initvals24, - D11LCN1INITVALS24); - rc = rc < 0 ? rc : - brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn2bsinitvals24, - D11LCN2BSINITVALS24); - rc = rc < 0 ? - rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn2initvals24, - D11LCN2INITVALS24); - rc = rc < 0 ? - rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0absinitvals16, - D11N0ABSINITVALS16); - rc = rc < 0 ? - rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0bsinitvals16, - D11N0BSINITVALS16); - rc = rc < 0 ? - rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0initvals16, - D11N0INITVALS16); - rc = rc < 0 ? - rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_16_mimo, - D11UCODE_OVERSIGHT16_MIMO); - rc = rc < 0 ? - rc : brcms_ucode_init_uint(wl, &ucode->bcm43xx_16_mimosz, - D11UCODE_OVERSIGHT16_MIMOSZ); - rc = rc < 0 ? - rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_24_lcn, - D11UCODE_OVERSIGHT24_LCN); - rc = rc < 0 ? - rc : brcms_ucode_init_uint(wl, &ucode->bcm43xx_24_lcnsz, - D11UCODE_OVERSIGHT24_LCNSZ); - rc = rc < 0 ? - rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_bommajor, - D11UCODE_OVERSIGHT_BOMMAJOR); - rc = rc < 0 ? - rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_bomminor, - D11UCODE_OVERSIGHT_BOMMINOR); - return rc; -} - -void brcms_ucode_data_free(struct brcms_ucode *ucode) -{ - brcms_ucode_free_buf((void *)ucode->d11lcn0bsinitvals24); - brcms_ucode_free_buf((void *)ucode->d11lcn0initvals24); - brcms_ucode_free_buf((void *)ucode->d11lcn1bsinitvals24); - brcms_ucode_free_buf((void *)ucode->d11lcn1initvals24); - brcms_ucode_free_buf((void *)ucode->d11lcn2bsinitvals24); - brcms_ucode_free_buf((void *)ucode->d11lcn2initvals24); - brcms_ucode_free_buf((void *)ucode->d11n0absinitvals16); - brcms_ucode_free_buf((void *)ucode->d11n0bsinitvals16); - brcms_ucode_free_buf((void *)ucode->d11n0initvals16); - brcms_ucode_free_buf((void *)ucode->bcm43xx_16_mimo); - brcms_ucode_free_buf((void *)ucode->bcm43xx_24_lcn); - brcms_ucode_free_buf((void *)ucode->bcm43xx_bommajor); - brcms_ucode_free_buf((void *)ucode->bcm43xx_bomminor); -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h b/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h deleted file mode 100644 index c87dd89bcb78..000000000000 --- a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_UCODE_H_ -#define _BRCM_UCODE_H_ - -#include "types.h" /* forward structure declarations */ - -#define MIN_FW_SIZE 40000 /* minimum firmware file size in bytes */ -#define MAX_FW_SIZE 150000 - -#define UCODE_LOADER_API_VER 0 - -struct d11init; - -struct brcms_ucode { - struct d11init *d11lcn0bsinitvals24; - struct d11init *d11lcn0initvals24; - struct d11init *d11lcn1bsinitvals24; - struct d11init *d11lcn1initvals24; - struct d11init *d11lcn2bsinitvals24; - struct d11init *d11lcn2initvals24; - struct d11init *d11n0absinitvals16; - struct d11init *d11n0bsinitvals16; - struct d11init *d11n0initvals16; - __le32 *bcm43xx_16_mimo; - size_t bcm43xx_16_mimosz; - __le32 *bcm43xx_24_lcn; - size_t bcm43xx_24_lcnsz; - u32 *bcm43xx_bommajor; - u32 *bcm43xx_bomminor; -}; - -int brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode); - -void brcms_ucode_data_free(struct brcms_ucode *ucode); - -int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, unsigned int idx); -int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, - unsigned int idx); -void brcms_ucode_free_buf(void *); -int brcms_check_firmwares(struct brcms_info *wl); - -#endif /* _BRCM_UCODE_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmutil/Makefile b/drivers/net/wireless/brcm80211/brcmutil/Makefile deleted file mode 100644 index 8a928184016a..000000000000 --- a/drivers/net/wireless/brcm80211/brcmutil/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# -# Makefile fragment for Broadcom 802.11n Networking Device Driver Utilities -# -# Copyright (c) 2011 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. - -ccflags-y := \ - -Idrivers/net/wireless/brcm80211/brcmutil \ - -Idrivers/net/wireless/brcm80211/include - -obj-$(CONFIG_BRCMUTIL) += brcmutil.o -brcmutil-objs = utils.o d11.o diff --git a/drivers/net/wireless/brcm80211/brcmutil/d11.c b/drivers/net/wireless/brcm80211/brcmutil/d11.c deleted file mode 100644 index 2b2522bdd8eb..000000000000 --- a/drivers/net/wireless/brcm80211/brcmutil/d11.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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. - */ -/*********************channel spec common functions*********************/ - -#include - -#include -#include -#include - -static u16 d11n_sb(enum brcmu_chan_sb sb) -{ - switch (sb) { - case BRCMU_CHAN_SB_NONE: - return BRCMU_CHSPEC_D11N_SB_N; - case BRCMU_CHAN_SB_L: - return BRCMU_CHSPEC_D11N_SB_L; - case BRCMU_CHAN_SB_U: - return BRCMU_CHSPEC_D11N_SB_U; - default: - WARN_ON(1); - } - return 0; -} - -static u16 d11n_bw(enum brcmu_chan_bw bw) -{ - switch (bw) { - case BRCMU_CHAN_BW_20: - return BRCMU_CHSPEC_D11N_BW_20; - case BRCMU_CHAN_BW_40: - return BRCMU_CHSPEC_D11N_BW_40; - default: - WARN_ON(1); - } - return 0; -} - -static void brcmu_d11n_encchspec(struct brcmu_chan *ch) -{ - if (ch->bw == BRCMU_CHAN_BW_20) - ch->sb = BRCMU_CHAN_SB_NONE; - - ch->chspec = 0; - brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK, - BRCMU_CHSPEC_CH_SHIFT, ch->chnum); - brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_SB_MASK, - 0, d11n_sb(ch->sb)); - brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_BW_MASK, - 0, d11n_bw(ch->bw)); - - if (ch->chnum <= CH_MAX_2G_CHANNEL) - ch->chspec |= BRCMU_CHSPEC_D11N_BND_2G; - else - ch->chspec |= BRCMU_CHSPEC_D11N_BND_5G; -} - -static u16 d11ac_bw(enum brcmu_chan_bw bw) -{ - switch (bw) { - case BRCMU_CHAN_BW_20: - return BRCMU_CHSPEC_D11AC_BW_20; - case BRCMU_CHAN_BW_40: - return BRCMU_CHSPEC_D11AC_BW_40; - case BRCMU_CHAN_BW_80: - return BRCMU_CHSPEC_D11AC_BW_80; - default: - WARN_ON(1); - } - return 0; -} - -static void brcmu_d11ac_encchspec(struct brcmu_chan *ch) -{ - if (ch->bw == BRCMU_CHAN_BW_20 || ch->sb == BRCMU_CHAN_SB_NONE) - ch->sb = BRCMU_CHAN_SB_L; - - brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK, - BRCMU_CHSPEC_CH_SHIFT, ch->chnum); - brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK, - BRCMU_CHSPEC_D11AC_SB_SHIFT, ch->sb); - brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_BW_MASK, - 0, d11ac_bw(ch->bw)); - - ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK; - if (ch->chnum <= CH_MAX_2G_CHANNEL) - ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G; - else - ch->chspec |= BRCMU_CHSPEC_D11AC_BND_5G; -} - -static void brcmu_d11n_decchspec(struct brcmu_chan *ch) -{ - u16 val; - - ch->chnum = (u8)(ch->chspec & BRCMU_CHSPEC_CH_MASK); - - switch (ch->chspec & BRCMU_CHSPEC_D11N_BW_MASK) { - case BRCMU_CHSPEC_D11N_BW_20: - ch->bw = BRCMU_CHAN_BW_20; - ch->sb = BRCMU_CHAN_SB_NONE; - break; - case BRCMU_CHSPEC_D11N_BW_40: - ch->bw = BRCMU_CHAN_BW_40; - val = ch->chspec & BRCMU_CHSPEC_D11N_SB_MASK; - if (val == BRCMU_CHSPEC_D11N_SB_L) { - ch->sb = BRCMU_CHAN_SB_L; - ch->chnum -= CH_10MHZ_APART; - } else { - ch->sb = BRCMU_CHAN_SB_U; - ch->chnum += CH_10MHZ_APART; - } - break; - default: - WARN_ON_ONCE(1); - break; - } - - switch (ch->chspec & BRCMU_CHSPEC_D11N_BND_MASK) { - case BRCMU_CHSPEC_D11N_BND_5G: - ch->band = BRCMU_CHAN_BAND_5G; - break; - case BRCMU_CHSPEC_D11N_BND_2G: - ch->band = BRCMU_CHAN_BAND_2G; - break; - default: - WARN_ON_ONCE(1); - break; - } -} - -static void brcmu_d11ac_decchspec(struct brcmu_chan *ch) -{ - u16 val; - - ch->chnum = (u8)(ch->chspec & BRCMU_CHSPEC_CH_MASK); - - switch (ch->chspec & BRCMU_CHSPEC_D11AC_BW_MASK) { - case BRCMU_CHSPEC_D11AC_BW_20: - ch->bw = BRCMU_CHAN_BW_20; - ch->sb = BRCMU_CHAN_SB_NONE; - break; - case BRCMU_CHSPEC_D11AC_BW_40: - ch->bw = BRCMU_CHAN_BW_40; - val = ch->chspec & BRCMU_CHSPEC_D11AC_SB_MASK; - if (val == BRCMU_CHSPEC_D11AC_SB_L) { - ch->sb = BRCMU_CHAN_SB_L; - ch->chnum -= CH_10MHZ_APART; - } else if (val == BRCMU_CHSPEC_D11AC_SB_U) { - ch->sb = BRCMU_CHAN_SB_U; - ch->chnum += CH_10MHZ_APART; - } else { - WARN_ON_ONCE(1); - } - break; - case BRCMU_CHSPEC_D11AC_BW_80: - ch->bw = BRCMU_CHAN_BW_80; - ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK, - BRCMU_CHSPEC_D11AC_SB_SHIFT); - switch (ch->sb) { - case BRCMU_CHAN_SB_LL: - ch->chnum -= CH_30MHZ_APART; - break; - case BRCMU_CHAN_SB_LU: - ch->chnum -= CH_10MHZ_APART; - break; - case BRCMU_CHAN_SB_UL: - ch->chnum += CH_10MHZ_APART; - break; - case BRCMU_CHAN_SB_UU: - ch->chnum += CH_30MHZ_APART; - break; - default: - WARN_ON_ONCE(1); - break; - } - break; - case BRCMU_CHSPEC_D11AC_BW_8080: - case BRCMU_CHSPEC_D11AC_BW_160: - default: - WARN_ON_ONCE(1); - break; - } - - switch (ch->chspec & BRCMU_CHSPEC_D11AC_BND_MASK) { - case BRCMU_CHSPEC_D11AC_BND_5G: - ch->band = BRCMU_CHAN_BAND_5G; - break; - case BRCMU_CHSPEC_D11AC_BND_2G: - ch->band = BRCMU_CHAN_BAND_2G; - break; - default: - WARN_ON_ONCE(1); - break; - } -} - -void brcmu_d11_attach(struct brcmu_d11inf *d11inf) -{ - if (d11inf->io_type == BRCMU_D11N_IOTYPE) { - d11inf->encchspec = brcmu_d11n_encchspec; - d11inf->decchspec = brcmu_d11n_decchspec; - } else { - d11inf->encchspec = brcmu_d11ac_encchspec; - d11inf->decchspec = brcmu_d11ac_decchspec; - } -} -EXPORT_SYMBOL(brcmu_d11_attach); diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/brcm80211/brcmutil/utils.c deleted file mode 100644 index 0543607002fd..000000000000 --- a/drivers/net/wireless/brcm80211/brcmutil/utils.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (c) 2010 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. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include - -#include - -MODULE_AUTHOR("Broadcom Corporation"); -MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities."); -MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards"); -MODULE_LICENSE("Dual BSD/GPL"); - -struct sk_buff *brcmu_pkt_buf_get_skb(uint len) -{ - struct sk_buff *skb; - - skb = dev_alloc_skb(len); - if (skb) { - skb_put(skb, len); - skb->priority = 0; - } - - return skb; -} -EXPORT_SYMBOL(brcmu_pkt_buf_get_skb); - -/* Free the driver packet. Free the tag if present */ -void brcmu_pkt_buf_free_skb(struct sk_buff *skb) -{ - if (!skb) - return; - - WARN_ON(skb->next); - dev_kfree_skb_any(skb); -} -EXPORT_SYMBOL(brcmu_pkt_buf_free_skb); - -/* - * osl multiple-precedence packet queue - * hi_prec is always >= the number of the highest non-empty precedence - */ -struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, - struct sk_buff *p) -{ - struct sk_buff_head *q; - - if (pktq_full(pq) || pktq_pfull(pq, prec)) - return NULL; - - q = &pq->q[prec].skblist; - skb_queue_tail(q, p); - pq->len++; - - if (pq->hi_prec < prec) - pq->hi_prec = (u8) prec; - - return p; -} -EXPORT_SYMBOL(brcmu_pktq_penq); - -struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, - struct sk_buff *p) -{ - struct sk_buff_head *q; - - if (pktq_full(pq) || pktq_pfull(pq, prec)) - return NULL; - - q = &pq->q[prec].skblist; - skb_queue_head(q, p); - pq->len++; - - if (pq->hi_prec < prec) - pq->hi_prec = (u8) prec; - - return p; -} -EXPORT_SYMBOL(brcmu_pktq_penq_head); - -struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec) -{ - struct sk_buff_head *q; - struct sk_buff *p; - - q = &pq->q[prec].skblist; - p = skb_dequeue(q); - if (p == NULL) - return NULL; - - pq->len--; - return p; -} -EXPORT_SYMBOL(brcmu_pktq_pdeq); - -/* - * precedence based dequeue with match function. Passing a NULL pointer - * for the match function parameter is considered to be a wildcard so - * any packet on the queue is returned. In that case it is no different - * from brcmu_pktq_pdeq() above. - */ -struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec, - bool (*match_fn)(struct sk_buff *skb, - void *arg), void *arg) -{ - struct sk_buff_head *q; - struct sk_buff *p, *next; - - q = &pq->q[prec].skblist; - skb_queue_walk_safe(q, p, next) { - if (match_fn == NULL || match_fn(p, arg)) { - skb_unlink(p, q); - pq->len--; - return p; - } - } - return NULL; -} -EXPORT_SYMBOL(brcmu_pktq_pdeq_match); - -struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec) -{ - struct sk_buff_head *q; - struct sk_buff *p; - - q = &pq->q[prec].skblist; - p = skb_dequeue_tail(q); - if (p == NULL) - return NULL; - - pq->len--; - return p; -} -EXPORT_SYMBOL(brcmu_pktq_pdeq_tail); - -void -brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir, - bool (*fn)(struct sk_buff *, void *), void *arg) -{ - struct sk_buff_head *q; - struct sk_buff *p, *next; - - q = &pq->q[prec].skblist; - skb_queue_walk_safe(q, p, next) { - if (fn == NULL || (*fn) (p, arg)) { - skb_unlink(p, q); - brcmu_pkt_buf_free_skb(p); - pq->len--; - } - } -} -EXPORT_SYMBOL(brcmu_pktq_pflush); - -void brcmu_pktq_flush(struct pktq *pq, bool dir, - bool (*fn)(struct sk_buff *, void *), void *arg) -{ - int prec; - for (prec = 0; prec < pq->num_prec; prec++) - brcmu_pktq_pflush(pq, prec, dir, fn, arg); -} -EXPORT_SYMBOL(brcmu_pktq_flush); - -void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len) -{ - int prec; - - /* pq is variable size; only zero out what's requested */ - memset(pq, 0, - offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec)); - - pq->num_prec = (u16) num_prec; - - pq->max = (u16) max_len; - - for (prec = 0; prec < num_prec; prec++) { - pq->q[prec].max = pq->max; - skb_queue_head_init(&pq->q[prec].skblist); - } -} -EXPORT_SYMBOL(brcmu_pktq_init); - -struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out) -{ - int prec; - - if (pq->len == 0) - return NULL; - - for (prec = 0; prec < pq->hi_prec; prec++) - if (!skb_queue_empty(&pq->q[prec].skblist)) - break; - - if (prec_out) - *prec_out = prec; - - return skb_peek_tail(&pq->q[prec].skblist); -} -EXPORT_SYMBOL(brcmu_pktq_peek_tail); - -/* Return sum of lengths of a specific set of precedences */ -int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp) -{ - int prec, len; - - len = 0; - - for (prec = 0; prec <= pq->hi_prec; prec++) - if (prec_bmp & (1 << prec)) - len += pq->q[prec].skblist.qlen; - - return len; -} -EXPORT_SYMBOL(brcmu_pktq_mlen); - -/* Priority dequeue from a specific set of precedences */ -struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, - int *prec_out) -{ - struct sk_buff_head *q; - struct sk_buff *p; - int prec; - - if (pq->len == 0) - return NULL; - - while ((prec = pq->hi_prec) > 0 && - skb_queue_empty(&pq->q[prec].skblist)) - pq->hi_prec--; - - while ((prec_bmp & (1 << prec)) == 0 || - skb_queue_empty(&pq->q[prec].skblist)) - if (prec-- == 0) - return NULL; - - q = &pq->q[prec].skblist; - p = skb_dequeue(q); - if (p == NULL) - return NULL; - - pq->len--; - - if (prec_out) - *prec_out = prec; - - return p; -} -EXPORT_SYMBOL(brcmu_pktq_mdeq); - -/* Produce a human-readable string for boardrev */ -char *brcmu_boardrev_str(u32 brev, char *buf) -{ - char c; - - if (brev < 0x100) { - snprintf(buf, BRCMU_BOARDREV_LEN, "%d.%d", - (brev & 0xf0) >> 4, brev & 0xf); - } else { - c = (brev & 0xf000) == 0x1000 ? 'P' : 'A'; - snprintf(buf, BRCMU_BOARDREV_LEN, "%c%03x", c, brev & 0xfff); - } - return buf; -} -EXPORT_SYMBOL(brcmu_boardrev_str); - -char *brcmu_dotrev_str(u32 dotrev, char *buf) -{ - u8 dotval[4]; - - if (!dotrev) { - snprintf(buf, BRCMU_DOTREV_LEN, "unknown"); - return buf; - } - dotval[0] = (dotrev >> 24) & 0xFF; - dotval[1] = (dotrev >> 16) & 0xFF; - dotval[2] = (dotrev >> 8) & 0xFF; - dotval[3] = dotrev & 0xFF; - - if (dotval[3]) - snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d.%d", dotval[0], - dotval[1], dotval[2], dotval[3]); - else if (dotval[2]) - snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d", dotval[0], - dotval[1], dotval[2]); - else - snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d", dotval[0], - dotval[1]); - - return buf; -} -EXPORT_SYMBOL(brcmu_dotrev_str); - -#if defined(DEBUG) -/* pretty hex print a pkt buffer chain */ -void brcmu_prpkt(const char *msg, struct sk_buff *p0) -{ - struct sk_buff *p; - - if (msg && (msg[0] != '\0')) - pr_debug("%s:\n", msg); - - for (p = p0; p; p = p->next) - print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len); -} -EXPORT_SYMBOL(brcmu_prpkt); - -void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - pr_debug("%pV", &vaf); - - va_end(args); - - print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size); -} -EXPORT_SYMBOL(brcmu_dbg_hex_dump); - -#endif /* defined(DEBUG) */ diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h deleted file mode 100644 index aa06ea231db3..000000000000 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_HW_IDS_H_ -#define _BRCM_HW_IDS_H_ - -#include -#include - -#define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c -#define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM - -/* Chipcommon Core Chip IDs */ -#define BRCM_CC_43143_CHIP_ID 43143 -#define BRCM_CC_43235_CHIP_ID 43235 -#define BRCM_CC_43236_CHIP_ID 43236 -#define BRCM_CC_43238_CHIP_ID 43238 -#define BRCM_CC_43241_CHIP_ID 0x4324 -#define BRCM_CC_43242_CHIP_ID 43242 -#define BRCM_CC_4329_CHIP_ID 0x4329 -#define BRCM_CC_4330_CHIP_ID 0x4330 -#define BRCM_CC_4334_CHIP_ID 0x4334 -#define BRCM_CC_43340_CHIP_ID 43340 -#define BRCM_CC_43362_CHIP_ID 43362 -#define BRCM_CC_4335_CHIP_ID 0x4335 -#define BRCM_CC_4339_CHIP_ID 0x4339 -#define BRCM_CC_43430_CHIP_ID 43430 -#define BRCM_CC_4345_CHIP_ID 0x4345 -#define BRCM_CC_4350_CHIP_ID 0x4350 -#define BRCM_CC_4354_CHIP_ID 0x4354 -#define BRCM_CC_4356_CHIP_ID 0x4356 -#define BRCM_CC_43566_CHIP_ID 43566 -#define BRCM_CC_43567_CHIP_ID 43567 -#define BRCM_CC_43569_CHIP_ID 43569 -#define BRCM_CC_43570_CHIP_ID 43570 -#define BRCM_CC_4358_CHIP_ID 0x4358 -#define BRCM_CC_43602_CHIP_ID 43602 -#define BRCM_CC_4365_CHIP_ID 0x4365 -#define BRCM_CC_4366_CHIP_ID 0x4366 -#define BRCM_CC_4371_CHIP_ID 0x4371 - -/* USB Device IDs */ -#define BRCM_USB_43143_DEVICE_ID 0xbd1e -#define BRCM_USB_43236_DEVICE_ID 0xbd17 -#define BRCM_USB_43242_DEVICE_ID 0xbd1f -#define BRCM_USB_43569_DEVICE_ID 0xbd27 -#define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc - -/* PCIE Device IDs */ -#define BRCM_PCIE_4350_DEVICE_ID 0x43a3 -#define BRCM_PCIE_4354_DEVICE_ID 0x43df -#define BRCM_PCIE_4356_DEVICE_ID 0x43ec -#define BRCM_PCIE_43567_DEVICE_ID 0x43d3 -#define BRCM_PCIE_43570_DEVICE_ID 0x43d9 -#define BRCM_PCIE_4358_DEVICE_ID 0x43e9 -#define BRCM_PCIE_43602_DEVICE_ID 0x43ba -#define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb -#define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc -#define BRCM_PCIE_43602_RAW_DEVICE_ID 43602 -#define BRCM_PCIE_4365_DEVICE_ID 0x43ca -#define BRCM_PCIE_4365_2G_DEVICE_ID 0x43cb -#define BRCM_PCIE_4365_5G_DEVICE_ID 0x43cc -#define BRCM_PCIE_4366_DEVICE_ID 0x43c3 -#define BRCM_PCIE_4366_2G_DEVICE_ID 0x43c4 -#define BRCM_PCIE_4366_5G_DEVICE_ID 0x43c5 -#define BRCM_PCIE_4371_DEVICE_ID 0x440d - - -/* brcmsmac IDs */ -#define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ -#define BCM43224_D11N_ID 0x4353 /* 43224 802.11n dualband device */ -#define BCM43224_D11N_ID_VEN1 0x0576 /* Vendor specific 43224 802.11n db */ -#define BCM43225_D11N2G_ID 0x4357 /* 43225 802.11n 2.4GHz device */ -#define BCM43236_D11N_ID 0x4346 /* 43236 802.11n dualband device */ -#define BCM43236_D11N2G_ID 0x4347 /* 43236 802.11n 2.4GHz device */ - -#define BCM4313_CHIP_ID 0x4313 -#define BCM43224_CHIP_ID 43224 - -#endif /* _BRCM_HW_IDS_H_ */ diff --git a/drivers/net/wireless/brcm80211/include/brcmu_d11.h b/drivers/net/wireless/brcm80211/include/brcmu_d11.h deleted file mode 100644 index f9745ea8b3e0..000000000000 --- a/drivers/net/wireless/brcm80211/include/brcmu_d11.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCMU_D11_H_ -#define _BRCMU_D11_H_ - -/* d11 io type */ -#define BRCMU_D11N_IOTYPE 1 -#define BRCMU_D11AC_IOTYPE 2 - -/* A chanspec (channel specification) holds the channel number, band, - * bandwidth and control sideband - */ - -/* chanspec binary format */ - -#define BRCMU_CHSPEC_INVALID 255 -/* bit 0~7 channel number - * for 80+80 channels: bit 0~3 low channel id, bit 4~7 high channel id - */ -#define BRCMU_CHSPEC_CH_MASK 0x00ff -#define BRCMU_CHSPEC_CH_SHIFT 0 -#define BRCMU_CHSPEC_CHL_MASK 0x000f -#define BRCMU_CHSPEC_CHL_SHIFT 0 -#define BRCMU_CHSPEC_CHH_MASK 0x00f0 -#define BRCMU_CHSPEC_CHH_SHIFT 4 - -/* bit 8~16 for dot 11n IO types - * bit 8~9 sideband - * bit 10~11 bandwidth - * bit 12~13 spectral band - * bit 14~15 not used - */ -#define BRCMU_CHSPEC_D11N_SB_MASK 0x0300 -#define BRCMU_CHSPEC_D11N_SB_SHIFT 8 -#define BRCMU_CHSPEC_D11N_SB_L 0x0100 /* control lower */ -#define BRCMU_CHSPEC_D11N_SB_U 0x0200 /* control upper */ -#define BRCMU_CHSPEC_D11N_SB_N 0x0300 /* none */ -#define BRCMU_CHSPEC_D11N_BW_MASK 0x0c00 -#define BRCMU_CHSPEC_D11N_BW_SHIFT 10 -#define BRCMU_CHSPEC_D11N_BW_10 0x0400 -#define BRCMU_CHSPEC_D11N_BW_20 0x0800 -#define BRCMU_CHSPEC_D11N_BW_40 0x0c00 -#define BRCMU_CHSPEC_D11N_BND_MASK 0x3000 -#define BRCMU_CHSPEC_D11N_BND_SHIFT 12 -#define BRCMU_CHSPEC_D11N_BND_5G 0x1000 -#define BRCMU_CHSPEC_D11N_BND_2G 0x2000 - -/* bit 8~16 for dot 11ac IO types - * bit 8~10 sideband - * bit 11~13 bandwidth - * bit 14~15 spectral band - */ -#define BRCMU_CHSPEC_D11AC_SB_MASK 0x0700 -#define BRCMU_CHSPEC_D11AC_SB_SHIFT 8 -#define BRCMU_CHSPEC_D11AC_SB_LLL 0x0000 -#define BRCMU_CHSPEC_D11AC_SB_LLU 0x0100 -#define BRCMU_CHSPEC_D11AC_SB_LUL 0x0200 -#define BRCMU_CHSPEC_D11AC_SB_LUU 0x0300 -#define BRCMU_CHSPEC_D11AC_SB_ULL 0x0400 -#define BRCMU_CHSPEC_D11AC_SB_ULU 0x0500 -#define BRCMU_CHSPEC_D11AC_SB_UUL 0x0600 -#define BRCMU_CHSPEC_D11AC_SB_UUU 0x0700 -#define BRCMU_CHSPEC_D11AC_SB_LL BRCMU_CHSPEC_D11AC_SB_LLL -#define BRCMU_CHSPEC_D11AC_SB_LU BRCMU_CHSPEC_D11AC_SB_LLU -#define BRCMU_CHSPEC_D11AC_SB_UL BRCMU_CHSPEC_D11AC_SB_LUL -#define BRCMU_CHSPEC_D11AC_SB_UU BRCMU_CHSPEC_D11AC_SB_LUU -#define BRCMU_CHSPEC_D11AC_SB_L BRCMU_CHSPEC_D11AC_SB_LLL -#define BRCMU_CHSPEC_D11AC_SB_U BRCMU_CHSPEC_D11AC_SB_LLU -#define BRCMU_CHSPEC_D11AC_BW_MASK 0x3800 -#define BRCMU_CHSPEC_D11AC_BW_SHIFT 11 -#define BRCMU_CHSPEC_D11AC_BW_5 0x0000 -#define BRCMU_CHSPEC_D11AC_BW_10 0x0800 -#define BRCMU_CHSPEC_D11AC_BW_20 0x1000 -#define BRCMU_CHSPEC_D11AC_BW_40 0x1800 -#define BRCMU_CHSPEC_D11AC_BW_80 0x2000 -#define BRCMU_CHSPEC_D11AC_BW_160 0x2800 -#define BRCMU_CHSPEC_D11AC_BW_8080 0x3000 -#define BRCMU_CHSPEC_D11AC_BND_MASK 0xc000 -#define BRCMU_CHSPEC_D11AC_BND_SHIFT 14 -#define BRCMU_CHSPEC_D11AC_BND_2G 0x0000 -#define BRCMU_CHSPEC_D11AC_BND_3G 0x4000 -#define BRCMU_CHSPEC_D11AC_BND_4G 0x8000 -#define BRCMU_CHSPEC_D11AC_BND_5G 0xc000 - -#define BRCMU_CHAN_BAND_2G 0 -#define BRCMU_CHAN_BAND_5G 1 - -enum brcmu_chan_bw { - BRCMU_CHAN_BW_20, - BRCMU_CHAN_BW_40, - BRCMU_CHAN_BW_80, - BRCMU_CHAN_BW_80P80, - BRCMU_CHAN_BW_160, -}; - -enum brcmu_chan_sb { - BRCMU_CHAN_SB_NONE = -1, - BRCMU_CHAN_SB_LLL, - BRCMU_CHAN_SB_LLU, - BRCMU_CHAN_SB_LUL, - BRCMU_CHAN_SB_LUU, - BRCMU_CHAN_SB_ULL, - BRCMU_CHAN_SB_ULU, - BRCMU_CHAN_SB_UUL, - BRCMU_CHAN_SB_UUU, - BRCMU_CHAN_SB_L = BRCMU_CHAN_SB_LLL, - BRCMU_CHAN_SB_U = BRCMU_CHAN_SB_LLU, - BRCMU_CHAN_SB_LL = BRCMU_CHAN_SB_LLL, - BRCMU_CHAN_SB_LU = BRCMU_CHAN_SB_LLU, - BRCMU_CHAN_SB_UL = BRCMU_CHAN_SB_LUL, - BRCMU_CHAN_SB_UU = BRCMU_CHAN_SB_LUU, -}; - -struct brcmu_chan { - u16 chspec; - u8 chnum; - u8 band; - enum brcmu_chan_bw bw; - enum brcmu_chan_sb sb; -}; - -struct brcmu_d11inf { - u8 io_type; - - void (*encchspec)(struct brcmu_chan *ch); - void (*decchspec)(struct brcmu_chan *ch); -}; - -void brcmu_d11_attach(struct brcmu_d11inf *d11inf); - -#endif /* _BRCMU_CHANNELS_H_ */ diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/brcm80211/include/brcmu_utils.h deleted file mode 100644 index 41969527b459..000000000000 --- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCMU_UTILS_H_ -#define _BRCMU_UTILS_H_ - -#include - -/* - * Spin at most 'us' microseconds while 'exp' is true. - * Caller should explicitly test 'exp' when this completes - * and take appropriate error action if 'exp' is still true. - */ -#define SPINWAIT(exp, us) { \ - uint countdown = (us) + 9; \ - while ((exp) && (countdown >= 10)) {\ - udelay(10); \ - countdown -= 10; \ - } \ -} - -/* osl multi-precedence packet queue */ -#define PKTQ_LEN_DEFAULT 128 /* Max 128 packets */ -#define PKTQ_MAX_PREC 16 /* Maximum precedence levels */ - -#define BCME_STRLEN 64 /* Max string length for BCM errors */ - -/* the largest reasonable packet buffer driver uses for ethernet MTU in bytes */ -#define PKTBUFSZ 2048 - -#ifndef setbit -#ifndef NBBY /* the BSD family defines NBBY */ -#define NBBY 8 /* 8 bits per byte */ -#endif /* #ifndef NBBY */ -#define setbit(a, i) (((u8 *)a)[(i)/NBBY] |= 1<<((i)%NBBY)) -#define clrbit(a, i) (((u8 *)a)[(i)/NBBY] &= ~(1<<((i)%NBBY))) -#define isset(a, i) (((const u8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) -#define isclr(a, i) ((((const u8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0) -#endif /* setbit */ - -#define NBITS(type) (sizeof(type) * 8) -#define NBITVAL(nbits) (1 << (nbits)) -#define MAXBITVAL(nbits) ((1 << (nbits)) - 1) -#define NBITMASK(nbits) MAXBITVAL(nbits) -#define MAXNBVAL(nbyte) MAXBITVAL((nbyte) * 8) - -/* crc defines */ -#define CRC16_INIT_VALUE 0xffff /* Initial CRC16 checksum value */ -#define CRC16_GOOD_VALUE 0xf0b8 /* Good final CRC16 checksum value */ - -/* 18-bytes of Ethernet address buffer length */ -#define ETHER_ADDR_STR_LEN 18 - -struct pktq_prec { - struct sk_buff_head skblist; - u16 max; /* maximum number of queued packets */ -}; - -/* multi-priority pkt queue */ -struct pktq { - u16 num_prec; /* number of precedences in use */ - u16 hi_prec; /* rapid dequeue hint (>= highest non-empty prec) */ - u16 max; /* total max packets */ - u16 len; /* total number of packets */ - /* - * q array must be last since # of elements can be either - * PKTQ_MAX_PREC or 1 - */ - struct pktq_prec q[PKTQ_MAX_PREC]; -}; - -/* operations on a specific precedence in packet queue */ - -static inline int pktq_plen(struct pktq *pq, int prec) -{ - return pq->q[prec].skblist.qlen; -} - -static inline int pktq_pavail(struct pktq *pq, int prec) -{ - return pq->q[prec].max - pq->q[prec].skblist.qlen; -} - -static inline bool pktq_pfull(struct pktq *pq, int prec) -{ - return pq->q[prec].skblist.qlen >= pq->q[prec].max; -} - -static inline bool pktq_pempty(struct pktq *pq, int prec) -{ - return skb_queue_empty(&pq->q[prec].skblist); -} - -static inline struct sk_buff *pktq_ppeek(struct pktq *pq, int prec) -{ - return skb_peek(&pq->q[prec].skblist); -} - -static inline struct sk_buff *pktq_ppeek_tail(struct pktq *pq, int prec) -{ - return skb_peek_tail(&pq->q[prec].skblist); -} - -struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, struct sk_buff *p); -struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, - struct sk_buff *p); -struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec); -struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec); -struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec, - bool (*match_fn)(struct sk_buff *p, - void *arg), - void *arg); - -/* packet primitives */ -struct sk_buff *brcmu_pkt_buf_get_skb(uint len); -void brcmu_pkt_buf_free_skb(struct sk_buff *skb); - -/* Empty the queue at particular precedence level */ -/* callback function fn(pkt, arg) returns true if pkt belongs to if */ -void brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir, - bool (*fn)(struct sk_buff *, void *), void *arg); - -/* operations on a set of precedences in packet queue */ - -int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp); -struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out); - -/* operations on packet queue as a whole */ - -static inline int pktq_len(struct pktq *pq) -{ - return (int)pq->len; -} - -static inline int pktq_max(struct pktq *pq) -{ - return (int)pq->max; -} - -static inline int pktq_avail(struct pktq *pq) -{ - return (int)(pq->max - pq->len); -} - -static inline bool pktq_full(struct pktq *pq) -{ - return pq->len >= pq->max; -} - -static inline bool pktq_empty(struct pktq *pq) -{ - return pq->len == 0; -} - -void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len); -/* prec_out may be NULL if caller is not interested in return value */ -struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out); -void brcmu_pktq_flush(struct pktq *pq, bool dir, - bool (*fn)(struct sk_buff *, void *), void *arg); - -/* externs */ -/* ip address */ -struct ipv4_addr; - -/* - * bitfield macros using masking and shift - * - * remark: the mask parameter should be a shifted mask. - */ -static inline void brcmu_maskset32(u32 *var, u32 mask, u8 shift, u32 value) -{ - value = (value << shift) & mask; - *var = (*var & ~mask) | value; -} -static inline u32 brcmu_maskget32(u32 var, u32 mask, u8 shift) -{ - return (var & mask) >> shift; -} -static inline void brcmu_maskset16(u16 *var, u16 mask, u8 shift, u16 value) -{ - value = (value << shift) & mask; - *var = (*var & ~mask) | value; -} -static inline u16 brcmu_maskget16(u16 var, u16 mask, u8 shift) -{ - return (var & mask) >> shift; -} - -/* externs */ -/* format/print */ -#ifdef DEBUG -void brcmu_prpkt(const char *msg, struct sk_buff *p0); -#else -#define brcmu_prpkt(a, b) -#endif /* DEBUG */ - -#ifdef DEBUG -__printf(3, 4) -void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...); -#else -__printf(3, 4) -static inline -void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) -{ -} -#endif - -#define BRCMU_BOARDREV_LEN 8 -#define BRCMU_DOTREV_LEN 16 - -char *brcmu_boardrev_str(u32 brev, char *buf); -char *brcmu_dotrev_str(u32 dotrev, char *buf); - -#endif /* _BRCMU_UTILS_H_ */ diff --git a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h deleted file mode 100644 index 76b5d3a86294..000000000000 --- a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCMU_WIFI_H_ -#define _BRCMU_WIFI_H_ - -#include /* for ETH_ALEN */ -#include /* for WLAN_PMKID_LEN */ - -/* - * A chanspec (u16) holds the channel number, band, bandwidth and control - * sideband - */ - -/* channel defines */ -#define CH_UPPER_SB 0x01 -#define CH_LOWER_SB 0x02 -#define CH_EWA_VALID 0x04 -#define CH_30MHZ_APART 6 -#define CH_20MHZ_APART 4 -#define CH_10MHZ_APART 2 -#define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */ -#define CH_MIN_2G_CHANNEL 1 -#define CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */ -#define CH_MIN_5G_CHANNEL 34 - -/* bandstate array indices */ -#define BAND_2G_INDEX 0 /* wlc->bandstate[x] index */ -#define BAND_5G_INDEX 1 /* wlc->bandstate[x] index */ - -/* - * max # supported channels. The max channel no is 216, this is that + 1 - * rounded up to a multiple of NBBY (8). DO NOT MAKE it > 255: channels are - * u8's all over -*/ -#define MAXCHANNEL 224 - -#define WL_CHANSPEC_CHAN_MASK 0x00ff -#define WL_CHANSPEC_CHAN_SHIFT 0 - -#define WL_CHANSPEC_CTL_SB_MASK 0x0300 -#define WL_CHANSPEC_CTL_SB_SHIFT 8 -#define WL_CHANSPEC_CTL_SB_LOWER 0x0100 -#define WL_CHANSPEC_CTL_SB_UPPER 0x0200 -#define WL_CHANSPEC_CTL_SB_NONE 0x0300 - -#define WL_CHANSPEC_BW_MASK 0x0C00 -#define WL_CHANSPEC_BW_SHIFT 10 -#define WL_CHANSPEC_BW_10 0x0400 -#define WL_CHANSPEC_BW_20 0x0800 -#define WL_CHANSPEC_BW_40 0x0C00 -#define WL_CHANSPEC_BW_80 0x2000 - -#define WL_CHANSPEC_BAND_MASK 0xf000 -#define WL_CHANSPEC_BAND_SHIFT 12 -#define WL_CHANSPEC_BAND_5G 0x1000 -#define WL_CHANSPEC_BAND_2G 0x2000 -#define INVCHANSPEC 255 - -#define WL_CHAN_VALID_HW (1 << 0) /* valid with current HW */ -#define WL_CHAN_VALID_SW (1 << 1) /* valid with country sett. */ -#define WL_CHAN_BAND_5G (1 << 2) /* 5GHz-band channel */ -#define WL_CHAN_RADAR (1 << 3) /* radar sensitive channel */ -#define WL_CHAN_INACTIVE (1 << 4) /* inactive due to radar */ -#define WL_CHAN_PASSIVE (1 << 5) /* channel in passive mode */ -#define WL_CHAN_RESTRICTED (1 << 6) /* restricted use channel */ - -/* values for band specific 40MHz capabilities */ -#define WLC_N_BW_20ALL 0 -#define WLC_N_BW_40ALL 1 -#define WLC_N_BW_20IN2G_40IN5G 2 - -#define WLC_BW_20MHZ_BIT BIT(0) -#define WLC_BW_40MHZ_BIT BIT(1) -#define WLC_BW_80MHZ_BIT BIT(2) -#define WLC_BW_160MHZ_BIT BIT(3) - -/* Bandwidth capabilities */ -#define WLC_BW_CAP_20MHZ (WLC_BW_20MHZ_BIT) -#define WLC_BW_CAP_40MHZ (WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT) -#define WLC_BW_CAP_80MHZ (WLC_BW_80MHZ_BIT|WLC_BW_40MHZ_BIT| \ - WLC_BW_20MHZ_BIT) -#define WLC_BW_CAP_160MHZ (WLC_BW_160MHZ_BIT|WLC_BW_80MHZ_BIT| \ - WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT) -#define WLC_BW_CAP_UNRESTRICTED 0xFF - -/* band types */ -#define WLC_BAND_AUTO 0 /* auto-select */ -#define WLC_BAND_5G 1 /* 5 Ghz */ -#define WLC_BAND_2G 2 /* 2.4 Ghz */ -#define WLC_BAND_ALL 3 /* all bands */ - -#define CHSPEC_CHANNEL(chspec) ((u8)((chspec) & WL_CHANSPEC_CHAN_MASK)) -#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK) - -#define CHSPEC_CTL_SB(chspec) ((chspec) & WL_CHANSPEC_CTL_SB_MASK) -#define CHSPEC_BW(chspec) ((chspec) & WL_CHANSPEC_BW_MASK) - -#define CHSPEC_IS10(chspec) \ - (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10) - -#define CHSPEC_IS20(chspec) \ - (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20) - -#define CHSPEC_IS40(chspec) \ - (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40) - -#define CHSPEC_IS80(chspec) \ - (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_80) - -#define CHSPEC_IS5G(chspec) \ - (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G) - -#define CHSPEC_IS2G(chspec) \ - (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G) - -#define CHSPEC_SB_NONE(chspec) \ - (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE) - -#define CHSPEC_SB_UPPER(chspec) \ - (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER) - -#define CHSPEC_SB_LOWER(chspec) \ - (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER) - -#define CHSPEC_CTL_CHAN(chspec) \ - ((CHSPEC_SB_LOWER(chspec)) ? \ - (lower_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \ - (upper_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK)))) - -#define CHSPEC2BAND(chspec) (CHSPEC_IS5G(chspec) ? BRCM_BAND_5G : BRCM_BAND_2G) - -#define CHANSPEC_STR_LEN 8 - -static inline int lower_20_sb(int channel) -{ - return channel > CH_10MHZ_APART ? (channel - CH_10MHZ_APART) : 0; -} - -static inline int upper_20_sb(int channel) -{ - return (channel < (MAXCHANNEL - CH_10MHZ_APART)) ? - channel + CH_10MHZ_APART : 0; -} - -static inline int chspec_bandunit(u16 chspec) -{ - return CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX; -} - -static inline u16 ch20mhz_chspec(int channel) -{ - u16 rc = channel <= CH_MAX_2G_CHANNEL ? - WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G; - - return (u16)((u16)channel | WL_CHANSPEC_BW_20 | - WL_CHANSPEC_CTL_SB_NONE | rc); -} - -static inline int next_20mhz_chan(int channel) -{ - return channel < (MAXCHANNEL - CH_20MHZ_APART) ? - channel + CH_20MHZ_APART : 0; -} - -/* defined rate in 500kbps */ -#define BRCM_MAXRATE 108 /* in 500kbps units */ -#define BRCM_RATE_1M 2 /* in 500kbps units */ -#define BRCM_RATE_2M 4 /* in 500kbps units */ -#define BRCM_RATE_5M5 11 /* in 500kbps units */ -#define BRCM_RATE_11M 22 /* in 500kbps units */ -#define BRCM_RATE_6M 12 /* in 500kbps units */ -#define BRCM_RATE_9M 18 /* in 500kbps units */ -#define BRCM_RATE_12M 24 /* in 500kbps units */ -#define BRCM_RATE_18M 36 /* in 500kbps units */ -#define BRCM_RATE_24M 48 /* in 500kbps units */ -#define BRCM_RATE_36M 72 /* in 500kbps units */ -#define BRCM_RATE_48M 96 /* in 500kbps units */ -#define BRCM_RATE_54M 108 /* in 500kbps units */ - -#define BRCM_2G_25MHZ_OFFSET 5 /* 2.4GHz band channel offset */ - -#define MCSSET_LEN 16 - -static inline bool ac_bitmap_tst(u8 bitmap, int prec) -{ - return (bitmap & (1 << (prec))) != 0; -} - -/* Enumerate crypto algorithms */ -#define CRYPTO_ALGO_OFF 0 -#define CRYPTO_ALGO_WEP1 1 -#define CRYPTO_ALGO_TKIP 2 -#define CRYPTO_ALGO_WEP128 3 -#define CRYPTO_ALGO_AES_CCM 4 -#define CRYPTO_ALGO_AES_RESERVED1 5 -#define CRYPTO_ALGO_AES_RESERVED2 6 -#define CRYPTO_ALGO_NALG 7 - -/* wireless security bitvec */ - -#define WEP_ENABLED 0x0001 -#define TKIP_ENABLED 0x0002 -#define AES_ENABLED 0x0004 -#define WSEC_SWFLAG 0x0008 -/* to go into transition mode without setting wep */ -#define SES_OW_ENABLED 0x0040 -/* MFP */ -#define MFP_CAPABLE 0x0200 -#define MFP_REQUIRED 0x0400 - -/* WPA authentication mode bitvec */ -#define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */ -#define WPA_AUTH_NONE 0x0001 /* none (IBSS) */ -#define WPA_AUTH_UNSPECIFIED 0x0002 /* over 802.1x */ -#define WPA_AUTH_PSK 0x0004 /* Pre-shared key */ -#define WPA_AUTH_RESERVED1 0x0008 -#define WPA_AUTH_RESERVED2 0x0010 - -#define WPA2_AUTH_RESERVED1 0x0020 -#define WPA2_AUTH_UNSPECIFIED 0x0040 /* over 802.1x */ -#define WPA2_AUTH_PSK 0x0080 /* Pre-shared key */ -#define WPA2_AUTH_RESERVED3 0x0200 -#define WPA2_AUTH_RESERVED4 0x0400 -#define WPA2_AUTH_RESERVED5 0x0800 - -/* pmkid */ -#define MAXPMKID 16 - -#define DOT11_DEFAULT_RTS_LEN 2347 -#define DOT11_DEFAULT_FRAG_LEN 2346 - -#define DOT11_ICV_AES_LEN 8 -#define DOT11_QOS_LEN 2 -#define DOT11_IV_MAX_LEN 8 -#define DOT11_A4_HDR_LEN 30 - -#define HT_CAP_RX_STBC_NO 0x0 -#define HT_CAP_RX_STBC_ONE_STREAM 0x1 - -struct pmkid { - u8 BSSID[ETH_ALEN]; - u8 PMKID[WLAN_PMKID_LEN]; -}; - -struct pmkid_list { - __le32 npmkid; - struct pmkid pmkid[1]; -}; - -struct pmkid_cand { - u8 BSSID[ETH_ALEN]; - u8 preauth; -}; - -struct pmkid_cand_list { - u32 npmkid_cand; - struct pmkid_cand pmkid_cand[1]; -}; - -#endif /* _BRCMU_WIFI_H_ */ diff --git a/drivers/net/wireless/brcm80211/include/chipcommon.h b/drivers/net/wireless/brcm80211/include/chipcommon.h deleted file mode 100644 index e1fd499930a0..000000000000 --- a/drivers/net/wireless/brcm80211/include/chipcommon.h +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (c) 2010 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 _SBCHIPC_H -#define _SBCHIPC_H - -#include "defs.h" /* for PAD macro */ - -#define CHIPCREGOFFS(field) offsetof(struct chipcregs, field) - -struct chipcregs { - u32 chipid; /* 0x0 */ - u32 capabilities; - u32 corecontrol; /* corerev >= 1 */ - u32 bist; - - /* OTP */ - u32 otpstatus; /* 0x10, corerev >= 10 */ - u32 otpcontrol; - u32 otpprog; - u32 otplayout; /* corerev >= 23 */ - - /* Interrupt control */ - u32 intstatus; /* 0x20 */ - u32 intmask; - - /* Chip specific regs */ - u32 chipcontrol; /* 0x28, rev >= 11 */ - u32 chipstatus; /* 0x2c, rev >= 11 */ - - /* Jtag Master */ - u32 jtagcmd; /* 0x30, rev >= 10 */ - u32 jtagir; - u32 jtagdr; - u32 jtagctrl; - - /* serial flash interface registers */ - u32 flashcontrol; /* 0x40 */ - u32 flashaddress; - u32 flashdata; - u32 PAD[1]; - - /* Silicon backplane configuration broadcast control */ - u32 broadcastaddress; /* 0x50 */ - u32 broadcastdata; - - /* gpio - cleared only by power-on-reset */ - u32 gpiopullup; /* 0x58, corerev >= 20 */ - u32 gpiopulldown; /* 0x5c, corerev >= 20 */ - u32 gpioin; /* 0x60 */ - u32 gpioout; /* 0x64 */ - u32 gpioouten; /* 0x68 */ - u32 gpiocontrol; /* 0x6C */ - u32 gpiointpolarity; /* 0x70 */ - u32 gpiointmask; /* 0x74 */ - - /* GPIO events corerev >= 11 */ - u32 gpioevent; - u32 gpioeventintmask; - - /* Watchdog timer */ - u32 watchdog; /* 0x80 */ - - /* GPIO events corerev >= 11 */ - u32 gpioeventintpolarity; - - /* GPIO based LED powersave registers corerev >= 16 */ - u32 gpiotimerval; /* 0x88 */ - u32 gpiotimeroutmask; - - /* clock control */ - u32 clockcontrol_n; /* 0x90 */ - u32 clockcontrol_sb; /* aka m0 */ - u32 clockcontrol_pci; /* aka m1 */ - u32 clockcontrol_m2; /* mii/uart/mipsref */ - u32 clockcontrol_m3; /* cpu */ - u32 clkdiv; /* corerev >= 3 */ - u32 gpiodebugsel; /* corerev >= 28 */ - u32 capabilities_ext; /* 0xac */ - - /* pll delay registers (corerev >= 4) */ - u32 pll_on_delay; /* 0xb0 */ - u32 fref_sel_delay; - u32 slow_clk_ctl; /* 5 < corerev < 10 */ - u32 PAD; - - /* Instaclock registers (corerev >= 10) */ - u32 system_clk_ctl; /* 0xc0 */ - u32 clkstatestretch; - u32 PAD[2]; - - /* Indirect backplane access (corerev >= 22) */ - u32 bp_addrlow; /* 0xd0 */ - u32 bp_addrhigh; - u32 bp_data; - u32 PAD; - u32 bp_indaccess; - u32 PAD[3]; - - /* More clock dividers (corerev >= 32) */ - u32 clkdiv2; - u32 PAD[2]; - - /* In AI chips, pointer to erom */ - u32 eromptr; /* 0xfc */ - - /* ExtBus control registers (corerev >= 3) */ - u32 pcmcia_config; /* 0x100 */ - u32 pcmcia_memwait; - u32 pcmcia_attrwait; - u32 pcmcia_iowait; - u32 ide_config; - u32 ide_memwait; - u32 ide_attrwait; - u32 ide_iowait; - u32 prog_config; - u32 prog_waitcount; - u32 flash_config; - u32 flash_waitcount; - u32 SECI_config; /* 0x130 SECI configuration */ - u32 PAD[3]; - - /* Enhanced Coexistence Interface (ECI) registers (corerev >= 21) */ - u32 eci_output; /* 0x140 */ - u32 eci_control; - u32 eci_inputlo; - u32 eci_inputmi; - u32 eci_inputhi; - u32 eci_inputintpolaritylo; - u32 eci_inputintpolaritymi; - u32 eci_inputintpolarityhi; - u32 eci_intmasklo; - u32 eci_intmaskmi; - u32 eci_intmaskhi; - u32 eci_eventlo; - u32 eci_eventmi; - u32 eci_eventhi; - u32 eci_eventmasklo; - u32 eci_eventmaskmi; - u32 eci_eventmaskhi; - u32 PAD[3]; - - /* SROM interface (corerev >= 32) */ - u32 sromcontrol; /* 0x190 */ - u32 sromaddress; - u32 sromdata; - u32 PAD[17]; - - /* Clock control and hardware workarounds (corerev >= 20) */ - u32 clk_ctl_st; /* 0x1e0 */ - u32 hw_war; - u32 PAD[70]; - - /* UARTs */ - u8 uart0data; /* 0x300 */ - u8 uart0imr; - u8 uart0fcr; - u8 uart0lcr; - u8 uart0mcr; - u8 uart0lsr; - u8 uart0msr; - u8 uart0scratch; - u8 PAD[248]; /* corerev >= 1 */ - - u8 uart1data; /* 0x400 */ - u8 uart1imr; - u8 uart1fcr; - u8 uart1lcr; - u8 uart1mcr; - u8 uart1lsr; - u8 uart1msr; - u8 uart1scratch; - u32 PAD[62]; - - /* save/restore, corerev >= 48 */ - u32 sr_capability; /* 0x500 */ - u32 sr_control0; /* 0x504 */ - u32 sr_control1; /* 0x508 */ - u32 gpio_control; /* 0x50C */ - u32 PAD[60]; - - /* PMU registers (corerev >= 20) */ - u32 pmucontrol; /* 0x600 */ - u32 pmucapabilities; - u32 pmustatus; - u32 res_state; - u32 res_pending; - u32 pmutimer; - u32 min_res_mask; - u32 max_res_mask; - u32 res_table_sel; - u32 res_dep_mask; - u32 res_updn_timer; - u32 res_timer; - u32 clkstretch; - u32 pmuwatchdog; - u32 gpiosel; /* 0x638, rev >= 1 */ - u32 gpioenable; /* 0x63c, rev >= 1 */ - u32 res_req_timer_sel; - u32 res_req_timer; - u32 res_req_mask; - u32 pmucapabilities_ext; /* 0x64c, pmurev >=15 */ - u32 chipcontrol_addr; /* 0x650 */ - u32 chipcontrol_data; /* 0x654 */ - u32 regcontrol_addr; - u32 regcontrol_data; - u32 pllcontrol_addr; - u32 pllcontrol_data; - u32 pmustrapopt; /* 0x668, corerev >= 28 */ - u32 pmu_xtalfreq; /* 0x66C, pmurev >= 10 */ - u32 retention_ctl; /* 0x670, pmurev >= 15 */ - u32 PAD[3]; - u32 retention_grpidx; /* 0x680 */ - u32 retention_grpctl; /* 0x684 */ - u32 PAD[94]; - u16 sromotp[768]; -}; - -/* chipid */ -#define CID_ID_MASK 0x0000ffff /* Chip Id mask */ -#define CID_REV_MASK 0x000f0000 /* Chip Revision mask */ -#define CID_REV_SHIFT 16 /* Chip Revision shift */ -#define CID_PKG_MASK 0x00f00000 /* Package Option mask */ -#define CID_PKG_SHIFT 20 /* Package Option shift */ -#define CID_CC_MASK 0x0f000000 /* CoreCount (corerev >= 4) */ -#define CID_CC_SHIFT 24 -#define CID_TYPE_MASK 0xf0000000 /* Chip Type */ -#define CID_TYPE_SHIFT 28 - -/* capabilities */ -#define CC_CAP_UARTS_MASK 0x00000003 /* Number of UARTs */ -#define CC_CAP_MIPSEB 0x00000004 /* MIPS is in big-endian mode */ -#define CC_CAP_UCLKSEL 0x00000018 /* UARTs clock select */ -/* UARTs are driven by internal divided clock */ -#define CC_CAP_UINTCLK 0x00000008 -#define CC_CAP_UARTGPIO 0x00000020 /* UARTs own GPIOs 15:12 */ -#define CC_CAP_EXTBUS_MASK 0x000000c0 /* External bus mask */ -#define CC_CAP_EXTBUS_NONE 0x00000000 /* No ExtBus present */ -#define CC_CAP_EXTBUS_FULL 0x00000040 /* ExtBus: PCMCIA, IDE & Prog */ -#define CC_CAP_EXTBUS_PROG 0x00000080 /* ExtBus: ProgIf only */ -#define CC_CAP_FLASH_MASK 0x00000700 /* Type of flash */ -#define CC_CAP_PLL_MASK 0x00038000 /* Type of PLL */ -#define CC_CAP_PWR_CTL 0x00040000 /* Power control */ -#define CC_CAP_OTPSIZE 0x00380000 /* OTP Size (0 = none) */ -#define CC_CAP_OTPSIZE_SHIFT 19 /* OTP Size shift */ -#define CC_CAP_OTPSIZE_BASE 5 /* OTP Size base */ -#define CC_CAP_JTAGP 0x00400000 /* JTAG Master Present */ -#define CC_CAP_ROM 0x00800000 /* Internal boot rom active */ -#define CC_CAP_BKPLN64 0x08000000 /* 64-bit backplane */ -#define CC_CAP_PMU 0x10000000 /* PMU Present, rev >= 20 */ -#define CC_CAP_SROM 0x40000000 /* Srom Present, rev >= 32 */ -/* Nand flash present, rev >= 35 */ -#define CC_CAP_NFLASH 0x80000000 - -#define CC_CAP2_SECI 0x00000001 /* SECI Present, rev >= 36 */ -/* GSIO (spi/i2c) present, rev >= 37 */ -#define CC_CAP2_GSIO 0x00000002 - -/* pmucapabilities */ -#define PCAP_REV_MASK 0x000000ff -#define PCAP_RC_MASK 0x00001f00 -#define PCAP_RC_SHIFT 8 -#define PCAP_TC_MASK 0x0001e000 -#define PCAP_TC_SHIFT 13 -#define PCAP_PC_MASK 0x001e0000 -#define PCAP_PC_SHIFT 17 -#define PCAP_VC_MASK 0x01e00000 -#define PCAP_VC_SHIFT 21 -#define PCAP_CC_MASK 0x1e000000 -#define PCAP_CC_SHIFT 25 -#define PCAP5_PC_MASK 0x003e0000 /* PMU corerev >= 5 */ -#define PCAP5_PC_SHIFT 17 -#define PCAP5_VC_MASK 0x07c00000 -#define PCAP5_VC_SHIFT 22 -#define PCAP5_CC_MASK 0xf8000000 -#define PCAP5_CC_SHIFT 27 -/* pmucapabilites_ext PMU rev >= 15 */ -#define PCAPEXT_SR_SUPPORTED_MASK (1 << 1) -/* retention_ctl PMU rev >= 15 */ -#define PMU_RCTL_MACPHY_DISABLE_MASK (1 << 26) -#define PMU_RCTL_LOGIC_DISABLE_MASK (1 << 27) - - -/* -* Maximum delay for the PMU state transition in us. -* This is an upper bound intended for spinwaits etc. -*/ -#define PMU_MAX_TRANSITION_DLY 15000 - -#endif /* _SBCHIPC_H */ diff --git a/drivers/net/wireless/brcm80211/include/defs.h b/drivers/net/wireless/brcm80211/include/defs.h deleted file mode 100644 index 8d1e85e0ed51..000000000000 --- a/drivers/net/wireless/brcm80211/include/defs.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_DEFS_H_ -#define _BRCM_DEFS_H_ - -#include - -#define SI_BUS 0 -#define PCI_BUS 1 -#define PCMCIA_BUS 2 -#define SDIO_BUS 3 -#define JTAG_BUS 4 -#define USB_BUS 5 -#define SPI_BUS 6 - -#define OFF 0 -#define ON 1 /* ON = 1 */ -#define AUTO (-1) /* Auto = -1 */ - -/* - * Priority definitions according 802.1D - */ -#define PRIO_8021D_NONE 2 -#define PRIO_8021D_BK 1 -#define PRIO_8021D_BE 0 -#define PRIO_8021D_EE 3 -#define PRIO_8021D_CL 4 -#define PRIO_8021D_VI 5 -#define PRIO_8021D_VO 6 -#define PRIO_8021D_NC 7 - -#define MAXPRIO 7 -#define NUMPRIO (MAXPRIO + 1) - -#define WL_NUMRATES 16 /* max # of rates in a rateset */ - -#define BRCM_CNTRY_BUF_SZ 4 /* Country string is 3 bytes + NUL */ - -#define BRCM_SET_CHANNEL 30 -#define BRCM_SET_SRL 32 -#define BRCM_SET_LRL 34 -#define BRCM_SET_BCNPRD 76 - -#define BRCM_GET_CURR_RATESET 114 /* current rateset */ -#define BRCM_GET_PHYLIST 180 - -/* Bit masks for radio disabled status - returned by WL_GET_RADIO */ - -#define WL_RADIO_SW_DISABLE (1<<0) -#define WL_RADIO_HW_DISABLE (1<<1) -/* some countries don't support any channel */ -#define WL_RADIO_COUNTRY_DISABLE (1<<3) - -/* Override bit for SET_TXPWR. if set, ignore other level limits */ -#define WL_TXPWR_OVERRIDE (1U<<31) - -/* band types */ -#define BRCM_BAND_AUTO 0 /* auto-select */ -#define BRCM_BAND_5G 1 /* 5 Ghz */ -#define BRCM_BAND_2G 2 /* 2.4 Ghz */ -#define BRCM_BAND_ALL 3 /* all bands */ - -/* Debug levels */ -#define BRCM_DL_INFO 0x00000001 -#define BRCM_DL_MAC80211 0x00000002 -#define BRCM_DL_RX 0x00000004 -#define BRCM_DL_TX 0x00000008 -#define BRCM_DL_INT 0x00000010 -#define BRCM_DL_DMA 0x00000020 -#define BRCM_DL_HT 0x00000040 - -/* Values for PM */ -#define PM_OFF 0 -#define PM_MAX 1 -#define PM_FAST 2 - -/* - * Sonics Configuration Space Registers. - */ - -/* core sbconfig regs are top 256bytes of regs */ -#define SBCONFIGOFF 0xf00 - -/* cpp contortions to concatenate w/arg prescan */ -#ifndef PAD -#define _PADLINE(line) pad ## line -#define _XSTR(line) _PADLINE(line) -#define PAD _XSTR(__LINE__) -#endif - -#endif /* _BRCM_DEFS_H_ */ diff --git a/drivers/net/wireless/brcm80211/include/soc.h b/drivers/net/wireless/brcm80211/include/soc.h deleted file mode 100644 index 123cfa854a0d..000000000000 --- a/drivers/net/wireless/brcm80211/include/soc.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2010 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 _BRCM_SOC_H -#define _BRCM_SOC_H - -#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */ - -/* Common core control flags */ -#define SICF_BIST_EN 0x8000 -#define SICF_PME_EN 0x4000 -#define SICF_CORE_BITS 0x3ffc -#define SICF_FGC 0x0002 -#define SICF_CLOCK_EN 0x0001 - -/* Common core status flags */ -#define SISF_BIST_DONE 0x8000 -#define SISF_BIST_ERROR 0x4000 -#define SISF_GATED_CLK 0x2000 -#define SISF_DMA64 0x1000 -#define SISF_CORE_BITS 0x0fff - -#endif /* _BRCM_SOC_H */ diff --git a/drivers/net/wireless/broadcom/Kconfig b/drivers/net/wireless/broadcom/Kconfig index 323e738dd96f..d3651ceb5046 100644 --- a/drivers/net/wireless/broadcom/Kconfig +++ b/drivers/net/wireless/broadcom/Kconfig @@ -13,5 +13,6 @@ if WLAN_VENDOR_BROADCOM source "drivers/net/wireless/broadcom/b43/Kconfig" source "drivers/net/wireless/broadcom/b43legacy/Kconfig" +source "drivers/net/wireless/broadcom/brcm80211/Kconfig" endif # WLAN_VENDOR_BROADCOM diff --git a/drivers/net/wireless/broadcom/Makefile b/drivers/net/wireless/broadcom/Makefile index 66d7cc461fcf..9d5ac95710c3 100644 --- a/drivers/net/wireless/broadcom/Makefile +++ b/drivers/net/wireless/broadcom/Makefile @@ -1,2 +1,5 @@ obj-$(CONFIG_B43) += b43/ obj-$(CONFIG_B43LEGACY) += b43legacy/ + +obj-$(CONFIG_BRCMFMAC) += brcm80211/ +obj-$(CONFIG_BRCMSMAC) += brcm80211/ diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig new file mode 100644 index 000000000000..ab42b1fea03c --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig @@ -0,0 +1,87 @@ +config BRCMUTIL + tristate + +config BRCMSMAC + tristate "Broadcom IEEE802.11n PCIe SoftMAC WLAN driver" + depends on MAC80211 + depends on BCMA_POSSIBLE + select BCMA + select NEW_LEDS if BCMA_DRIVER_GPIO + select LEDS_CLASS if BCMA_DRIVER_GPIO + select BRCMUTIL + select FW_LOADER + select CORDIC + ---help--- + This module adds support for PCIe wireless adapters based on Broadcom + IEEE802.11n SoftMAC chipsets. It also has WLAN led support, which will + be available if you select BCMA_DRIVER_GPIO. If you choose to build a + module, the driver will be called brcmsmac.ko. + +config BRCMFMAC + tristate "Broadcom IEEE802.11n embedded FullMAC WLAN driver" + depends on CFG80211 + select BRCMUTIL + ---help--- + This module adds support for embedded wireless adapters based on + Broadcom IEEE802.11n FullMAC chipsets. It has to work with at least + one of the bus interface support. If you choose to build a module, + it'll be called brcmfmac.ko. + +config BRCMFMAC_PROTO_BCDC + bool + +config BRCMFMAC_PROTO_MSGBUF + bool + +config BRCMFMAC_SDIO + bool "SDIO bus interface support for FullMAC driver" + depends on (MMC = y || MMC = BRCMFMAC) + depends on BRCMFMAC + select BRCMFMAC_PROTO_BCDC + select FW_LOADER + default y + ---help--- + This option enables the SDIO bus interface support for Broadcom + IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to + use the driver for a SDIO wireless card. + +config BRCMFMAC_USB + bool "USB bus interface support for FullMAC driver" + depends on (USB = y || USB = BRCMFMAC) + depends on BRCMFMAC + select BRCMFMAC_PROTO_BCDC + select FW_LOADER + ---help--- + This option enables the USB bus interface support for Broadcom + IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to + use the driver for an USB wireless card. + +config BRCMFMAC_PCIE + bool "PCIE bus interface support for FullMAC driver" + depends on BRCMFMAC + depends on PCI + depends on HAS_DMA + select BRCMFMAC_PROTO_MSGBUF + select FW_LOADER + ---help--- + This option enables the PCIE bus interface support for Broadcom + IEEE802.11ac embedded FullMAC WLAN driver. Say Y if you want to + use the driver for an PCIE wireless card. + +config BRCM_TRACING + bool "Broadcom device tracing" + depends on BRCMSMAC || BRCMFMAC + ---help--- + If you say Y here, the Broadcom wireless drivers will register + with ftrace to dump event information into the trace ringbuffer. + Tracing can be enabled at runtime to aid in debugging wireless + issues. This option adds a small amount of overhead when tracing + is disabled. If unsure, say Y to allow developers to better help + you when wireless problems occur. + +config BRCMDBG + bool "Broadcom driver debug functions" + depends on BRCMSMAC || BRCMFMAC + select WANT_DEV_COREDUMP + ---help--- + Selecting this enables additional code for debug purposes. diff --git a/drivers/net/wireless/broadcom/brcm80211/Makefile b/drivers/net/wireless/broadcom/brcm80211/Makefile new file mode 100644 index 000000000000..b987920e982e --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/Makefile @@ -0,0 +1,23 @@ +# +# Makefile fragment for Broadcom 802.11n Networking Device Driver +# +# Copyright (c) 2010 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. + +# common flags +subdir-ccflags-$(CONFIG_BRCMDBG) += -DDEBUG + +obj-$(CONFIG_BRCMUTIL) += brcmutil/ +obj-$(CONFIG_BRCMFMAC) += brcmfmac/ +obj-$(CONFIG_BRCMSMAC) += brcmsmac/ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile new file mode 100644 index 000000000000..9e4b505ca593 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile @@ -0,0 +1,57 @@ +# +# Makefile fragment for Broadcom 802.11n Networking Device Driver +# +# Copyright (c) 2010 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. + +ccflags-y += \ + -Idrivers/net/wireless/broadcom/brcm80211/brcmfmac \ + -Idrivers/net/wireless/broadcom/brcm80211/include + +ccflags-y += -D__CHECK_ENDIAN__ + +obj-$(CONFIG_BRCMFMAC) += brcmfmac.o +brcmfmac-objs += \ + cfg80211.o \ + chip.o \ + fwil.o \ + fweh.o \ + fwsignal.o \ + p2p.o \ + proto.o \ + common.o \ + core.o \ + firmware.o \ + feature.o \ + btcoex.o \ + vendor.o +brcmfmac-$(CONFIG_BRCMFMAC_PROTO_BCDC) += \ + bcdc.o +brcmfmac-$(CONFIG_BRCMFMAC_PROTO_MSGBUF) += \ + commonring.o \ + flowring.o \ + msgbuf.o +brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ + sdio.o \ + bcmsdh.o +brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ + usb.o +brcmfmac-$(CONFIG_BRCMFMAC_PCIE) += \ + pcie.o +brcmfmac-$(CONFIG_BRCMDBG) += \ + debug.o +brcmfmac-$(CONFIG_BRCM_TRACING) += \ + tracepoint.o +brcmfmac-$(CONFIG_OF) += \ + of.o diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c new file mode 100644 index 000000000000..288c84e7c56b --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2010 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. + */ + +/******************************************************************************* + * Communicates with the dongle by using dcmd codes. + * For certain dcmd codes, the dongle interprets string data from the host. + ******************************************************************************/ + +#include +#include + +#include +#include + +#include "core.h" +#include "bus.h" +#include "fwsignal.h" +#include "debug.h" +#include "tracepoint.h" +#include "proto.h" +#include "bcdc.h" + +struct brcmf_proto_bcdc_dcmd { + __le32 cmd; /* dongle command value */ + __le32 len; /* lower 16: output buflen; + * upper 16: input buflen (excludes header) */ + __le32 flags; /* flag defns given below */ + __le32 status; /* status code returned from the device */ +}; + +/* BCDC flag definitions */ +#define BCDC_DCMD_ERROR 0x01 /* 1=cmd failed */ +#define BCDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */ +#define BCDC_DCMD_IF_MASK 0xF000 /* I/F index */ +#define BCDC_DCMD_IF_SHIFT 12 +#define BCDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */ +#define BCDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */ +#define BCDC_DCMD_ID(flags) \ + (((flags) & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT) + +/* + * BCDC header - Broadcom specific extension of CDC. + * Used on data packets to convey priority across USB. + */ +#define BCDC_HEADER_LEN 4 +#define BCDC_PROTO_VER 2 /* Protocol version */ +#define BCDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */ +#define BCDC_FLAG_VER_SHIFT 4 /* Protocol version shift */ +#define BCDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */ +#define BCDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */ +#define BCDC_PRIORITY_MASK 0x7 +#define BCDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */ +#define BCDC_FLAG2_IF_SHIFT 0 + +#define BCDC_GET_IF_IDX(hdr) \ + ((int)((((hdr)->flags2) & BCDC_FLAG2_IF_MASK) >> BCDC_FLAG2_IF_SHIFT)) +#define BCDC_SET_IF_IDX(hdr, idx) \ + ((hdr)->flags2 = (((hdr)->flags2 & ~BCDC_FLAG2_IF_MASK) | \ + ((idx) << BCDC_FLAG2_IF_SHIFT))) + +/** + * struct brcmf_proto_bcdc_header - BCDC header format + * + * @flags: flags contain protocol and checksum info. + * @priority: 802.1d priority and USB flow control info (bit 4:7). + * @flags2: additional flags containing dongle interface index. + * @data_offset: start of packet data. header is following by firmware signals. + */ +struct brcmf_proto_bcdc_header { + u8 flags; + u8 priority; + u8 flags2; + u8 data_offset; +}; + +/* + * maximum length of firmware signal data between + * the BCDC header and packet data in the tx path. + */ +#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES 12 + +#define RETRIES 2 /* # of retries to retrieve matching dcmd response */ +#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE + * (amount of header tha might be added) + * plus any space that might be needed + * for bus alignment padding. + */ +struct brcmf_bcdc { + u16 reqid; + u8 bus_header[BUS_HEADER_LEN]; + struct brcmf_proto_bcdc_dcmd msg; + unsigned char buf[BRCMF_DCMD_MAXLEN]; +}; + + +static int +brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, + uint len, bool set) +{ + struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; + struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg; + u32 flags; + + brcmf_dbg(BCDC, "Enter\n"); + + memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd)); + + msg->cmd = cpu_to_le32(cmd); + msg->len = cpu_to_le32(len); + flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT); + if (set) + flags |= BCDC_DCMD_SET; + flags = (flags & ~BCDC_DCMD_IF_MASK) | + (ifidx << BCDC_DCMD_IF_SHIFT); + msg->flags = cpu_to_le32(flags); + + if (buf) + memcpy(bcdc->buf, buf, len); + + len += sizeof(*msg); + if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE) + len = BRCMF_TX_IOCTL_MAX_MSG_SIZE; + + /* Send request */ + return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len); +} + +static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len) +{ + int ret; + struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; + + brcmf_dbg(BCDC, "Enter\n"); + len += sizeof(struct brcmf_proto_bcdc_dcmd); + do { + ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg, + len); + if (ret < 0) + break; + } while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id); + + return ret; +} + +static int +brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, + void *buf, uint len) +{ + struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; + struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg; + void *info; + int ret = 0, retries = 0; + u32 id, flags; + + brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len); + + ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false); + if (ret < 0) { + brcmf_err("brcmf_proto_bcdc_msg failed w/status %d\n", + ret); + goto done; + } + +retry: + /* wait for interrupt and get first fragment */ + ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len); + if (ret < 0) + goto done; + + flags = le32_to_cpu(msg->flags); + id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT; + + if ((id < bcdc->reqid) && (++retries < RETRIES)) + goto retry; + if (id != bcdc->reqid) { + brcmf_err("%s: unexpected request id %d (expected %d)\n", + brcmf_ifname(drvr, ifidx), id, bcdc->reqid); + ret = -EINVAL; + goto done; + } + + /* Check info buffer */ + info = (void *)&msg[1]; + + /* Copy info buffer */ + if (buf) { + if (ret < (int)len) + len = ret; + memcpy(buf, info, len); + } + + /* Check the ERROR flag */ + if (flags & BCDC_DCMD_ERROR) + ret = le32_to_cpu(msg->status); + +done: + return ret; +} + +static int +brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, + void *buf, uint len) +{ + struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; + struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg; + int ret = 0; + u32 flags, id; + + brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len); + + ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true); + if (ret < 0) + goto done; + + ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len); + if (ret < 0) + goto done; + + flags = le32_to_cpu(msg->flags); + id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT; + + if (id != bcdc->reqid) { + brcmf_err("%s: unexpected request id %d (expected %d)\n", + brcmf_ifname(drvr, ifidx), id, bcdc->reqid); + ret = -EINVAL; + goto done; + } + + /* Check the ERROR flag */ + if (flags & BCDC_DCMD_ERROR) + ret = le32_to_cpu(msg->status); + +done: + return ret; +} + +static void +brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset, + struct sk_buff *pktbuf) +{ + struct brcmf_proto_bcdc_header *h; + + brcmf_dbg(BCDC, "Enter\n"); + + /* Push BDC header used to convey priority for buses that don't */ + skb_push(pktbuf, BCDC_HEADER_LEN); + + h = (struct brcmf_proto_bcdc_header *)(pktbuf->data); + + h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT); + if (pktbuf->ip_summed == CHECKSUM_PARTIAL) + h->flags |= BCDC_FLAG_SUM_NEEDED; + + h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK); + h->flags2 = 0; + h->data_offset = offset; + BCDC_SET_IF_IDX(h, ifidx); + trace_brcmf_bcdchdr(pktbuf->data); +} + +static int +brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, + struct sk_buff *pktbuf, struct brcmf_if **ifp) +{ + struct brcmf_proto_bcdc_header *h; + struct brcmf_if *tmp_if; + + brcmf_dbg(BCDC, "Enter\n"); + + /* Pop BCDC header used to convey priority for buses that don't */ + if (pktbuf->len <= BCDC_HEADER_LEN) { + brcmf_dbg(INFO, "rx data too short (%d <= %d)\n", + pktbuf->len, BCDC_HEADER_LEN); + return -EBADE; + } + + trace_brcmf_bcdchdr(pktbuf->data); + h = (struct brcmf_proto_bcdc_header *)(pktbuf->data); + + tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h)); + if (!tmp_if) { + brcmf_dbg(INFO, "no matching ifp found\n"); + return -EBADE; + } + if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) != + BCDC_PROTO_VER) { + brcmf_err("%s: non-BCDC packet received, flags 0x%x\n", + brcmf_ifname(drvr, tmp_if->ifidx), h->flags); + return -EBADE; + } + + if (h->flags & BCDC_FLAG_SUM_GOOD) { + brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n", + brcmf_ifname(drvr, tmp_if->ifidx), h->flags); + pktbuf->ip_summed = CHECKSUM_UNNECESSARY; + } + + pktbuf->priority = h->priority & BCDC_PRIORITY_MASK; + + skb_pull(pktbuf, BCDC_HEADER_LEN); + if (do_fws) + brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf); + else + skb_pull(pktbuf, h->data_offset << 2); + + if (pktbuf->len == 0) + return -ENODATA; + + *ifp = tmp_if; + return 0; +} + +static int +brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset, + struct sk_buff *pktbuf) +{ + brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf); + return brcmf_bus_txdata(drvr->bus_if, pktbuf); +} + +static void +brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode) +{ +} + +static void +brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]) +{ +} + +static void +brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]) +{ +} + +int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) +{ + struct brcmf_bcdc *bcdc; + + bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC); + if (!bcdc) + goto fail; + + /* ensure that the msg buf directly follows the cdc msg struct */ + if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) { + brcmf_err("struct brcmf_proto_bcdc is not correctly defined\n"); + goto fail; + } + + drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull; + drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd; + drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd; + drvr->proto->txdata = brcmf_proto_bcdc_txdata; + drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode; + drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer; + drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer; + drvr->proto->pd = bcdc; + + drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; + drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + + sizeof(struct brcmf_proto_bcdc_dcmd); + return 0; + +fail: + kfree(bcdc); + return -ENOMEM; +} + +void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) +{ + kfree(drvr->proto->pd); + drvr->proto->pd = NULL; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h new file mode 100644 index 000000000000..6003179c0ceb --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h @@ -0,0 +1,27 @@ +/* + * 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_BCDC_H +#define BRCMFMAC_BCDC_H + +#ifdef CONFIG_BRCMFMAC_PROTO_BCDC +int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr); +void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr); +#else +static inline int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { return 0; } +static inline void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) {} +#endif + +#endif /* BRCMFMAC_BCDC_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c new file mode 100644 index 000000000000..410a6645d316 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -0,0 +1,1380 @@ +/* + * Copyright (c) 2010 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. + */ +/* ****************** SDIO CARD Interface Functions **************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "chip.h" +#include "bus.h" +#include "debug.h" +#include "sdio.h" +#include "of.h" + +#define SDIOH_API_ACCESS_RETRY_LIMIT 2 + +#define DMA_ALIGN_MASK 0x03 + +#define SDIO_FUNC1_BLOCKSIZE 64 +#define SDIO_FUNC2_BLOCKSIZE 512 +/* Maximum milliseconds to wait for F2 to come up */ +#define SDIO_WAIT_F2RDY 3000 + +#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ +#define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */ + +struct brcmf_sdiod_freezer { + atomic_t freezing; + atomic_t thread_count; + u32 frozen_count; + wait_queue_head_t thread_freeze; + struct completion resumed; +}; + +static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; +module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); +MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); + +static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev_id); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + + brcmf_dbg(INTR, "OOB intr triggered\n"); + + /* out-of-band interrupt is level-triggered which won't + * be cleared until dpc + */ + if (sdiodev->irq_en) { + disable_irq_nosync(irq); + sdiodev->irq_en = false; + } + + brcmf_sdio_isr(sdiodev->bus); + + return IRQ_HANDLED; +} + +static void brcmf_sdiod_ib_irqhandler(struct sdio_func *func) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + + brcmf_dbg(INTR, "IB intr triggered\n"); + + brcmf_sdio_isr(sdiodev->bus); +} + +/* dummy handler for SDIO function 2 interrupt */ +static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func) +{ +} + +int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) +{ + int ret = 0; + u8 data; + u32 addr, gpiocontrol; + unsigned long flags; + + if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { + brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n", + sdiodev->pdata->oob_irq_nr); + ret = request_irq(sdiodev->pdata->oob_irq_nr, + brcmf_sdiod_oob_irqhandler, + sdiodev->pdata->oob_irq_flags, + "brcmf_oob_intr", + &sdiodev->func[1]->dev); + if (ret != 0) { + brcmf_err("request_irq failed %d\n", ret); + return ret; + } + sdiodev->oob_irq_requested = true; + spin_lock_init(&sdiodev->irq_en_lock); + spin_lock_irqsave(&sdiodev->irq_en_lock, flags); + sdiodev->irq_en = true; + spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags); + + ret = enable_irq_wake(sdiodev->pdata->oob_irq_nr); + if (ret != 0) { + brcmf_err("enable_irq_wake failed %d\n", ret); + return ret; + } + sdiodev->irq_wake = true; + + sdio_claim_host(sdiodev->func[1]); + + if (sdiodev->bus_if->chip == BRCM_CC_43362_CHIP_ID) { + /* assign GPIO to SDIO core */ + addr = CORE_CC_REG(SI_ENUM_BASE, gpiocontrol); + gpiocontrol = brcmf_sdiod_regrl(sdiodev, addr, &ret); + gpiocontrol |= 0x2; + brcmf_sdiod_regwl(sdiodev, addr, gpiocontrol, &ret); + + brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_SELECT, 0xf, + &ret); + brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_OUT, 0, &ret); + brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_EN, 0x2, &ret); + } + + /* must configure SDIO_CCCR_IENx to enable irq */ + data = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_IENx, &ret); + data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1; + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret); + + /* redirect, configure and enable io for interrupt signal */ + data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE; + if (sdiodev->pdata->oob_irq_flags & IRQF_TRIGGER_HIGH) + data |= SDIO_SEPINT_ACT_HI; + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret); + + sdio_release_host(sdiodev->func[1]); + } else { + brcmf_dbg(SDIO, "Entering\n"); + sdio_claim_host(sdiodev->func[1]); + sdio_claim_irq(sdiodev->func[1], brcmf_sdiod_ib_irqhandler); + sdio_claim_irq(sdiodev->func[2], brcmf_sdiod_dummy_irqhandler); + sdio_release_host(sdiodev->func[1]); + } + + return 0; +} + +int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev) +{ + brcmf_dbg(SDIO, "Entering\n"); + + if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { + sdio_claim_host(sdiodev->func[1]); + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL); + sdio_release_host(sdiodev->func[1]); + + if (sdiodev->oob_irq_requested) { + sdiodev->oob_irq_requested = false; + if (sdiodev->irq_wake) { + disable_irq_wake(sdiodev->pdata->oob_irq_nr); + sdiodev->irq_wake = false; + } + free_irq(sdiodev->pdata->oob_irq_nr, + &sdiodev->func[1]->dev); + sdiodev->irq_en = false; + } + } else { + sdio_claim_host(sdiodev->func[1]); + sdio_release_irq(sdiodev->func[2]); + sdio_release_irq(sdiodev->func[1]); + sdio_release_host(sdiodev->func[1]); + } + + return 0; +} + +void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev, + enum brcmf_sdiod_state state) +{ + if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM || + state == sdiodev->state) + return; + + brcmf_dbg(TRACE, "%d -> %d\n", sdiodev->state, state); + switch (sdiodev->state) { + case BRCMF_SDIOD_DATA: + /* any other state means bus interface is down */ + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN); + break; + case BRCMF_SDIOD_DOWN: + /* transition from DOWN to DATA means bus interface is up */ + if (state == BRCMF_SDIOD_DATA) + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_UP); + break; + default: + break; + } + sdiodev->state = state; +} + +static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func, + uint regaddr, u8 byte) +{ + int err_ret; + + /* + * Can only directly write to some F0 registers. + * Handle CCCR_IENx and CCCR_ABORT command + * as a special case. + */ + if ((regaddr == SDIO_CCCR_ABORT) || + (regaddr == SDIO_CCCR_IENx)) + sdio_writeb(func, byte, regaddr, &err_ret); + else + sdio_f0_writeb(func, byte, regaddr, &err_ret); + + return err_ret; +} + +static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, + u32 addr, u8 regsz, void *data, bool write) +{ + struct sdio_func *func; + int ret; + + brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", + write, fn, addr, regsz); + + /* only allow byte access on F0 */ + if (WARN_ON(regsz > 1 && !fn)) + return -EINVAL; + func = sdiodev->func[fn]; + + switch (regsz) { + case sizeof(u8): + if (write) { + if (fn) + sdio_writeb(func, *(u8 *)data, addr, &ret); + else + ret = brcmf_sdiod_f0_writeb(func, addr, + *(u8 *)data); + } else { + if (fn) + *(u8 *)data = sdio_readb(func, addr, &ret); + else + *(u8 *)data = sdio_f0_readb(func, addr, &ret); + } + break; + case sizeof(u16): + if (write) + sdio_writew(func, *(u16 *)data, addr, &ret); + else + *(u16 *)data = sdio_readw(func, addr, &ret); + break; + case sizeof(u32): + if (write) + sdio_writel(func, *(u32 *)data, addr, &ret); + else + *(u32 *)data = sdio_readl(func, addr, &ret); + break; + default: + brcmf_err("invalid size: %d\n", regsz); + break; + } + + if (ret) + brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", fn, addr, ret); + + return ret; +} + +static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, + u8 regsz, void *data, bool write) +{ + u8 func; + s32 retry = 0; + int ret; + + if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM) + return -ENOMEDIUM; + + /* + * figure out how to read the register based on address range + * 0x00 ~ 0x7FF: function 0 CCCR and FBR + * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers + * The rest: function 1 silicon backplane core registers + */ + if ((addr & ~REG_F0_REG_MASK) == 0) + func = SDIO_FUNC_0; + else + func = SDIO_FUNC_1; + + do { + if (!write) + memset(data, 0, regsz); + /* for retry wait for 1 ms till bus get settled down */ + if (retry) + usleep_range(1000, 2000); + ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz, + data, write); + } while (ret != 0 && ret != -ENOMEDIUM && + retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); + + if (ret == -ENOMEDIUM) + brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); + else if (ret != 0) { + /* + * SleepCSR register access can fail when + * waking up the device so reduce this noise + * in the logs. + */ + if (addr != SBSDIO_FUNC1_SLEEPCSR) + brcmf_err("failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", func, addr, ret); + else + brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", func, addr, ret); + } + return ret; +} + +static int +brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) +{ + int err = 0, i; + u8 addr[3]; + + if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM) + return -ENOMEDIUM; + + addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK; + addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK; + addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK; + + for (i = 0; i < 3; i++) { + err = brcmf_sdiod_regrw_helper(sdiodev, + SBSDIO_FUNC1_SBADDRLOW + i, + sizeof(u8), &addr[i], true); + if (err) { + brcmf_err("failed at addr: 0x%0x\n", + SBSDIO_FUNC1_SBADDRLOW + i); + break; + } + } + + return err; +} + +static int +brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr) +{ + uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK; + int err = 0; + + if (bar0 != sdiodev->sbwad) { + err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0); + if (err) + return err; + + sdiodev->sbwad = bar0; + } + + *addr &= SBSDIO_SB_OFT_ADDR_MASK; + + if (width == 4) + *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + return 0; +} + +u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) +{ + u8 data; + int retval; + + brcmf_dbg(SDIO, "addr:0x%08x\n", addr); + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, + false); + brcmf_dbg(SDIO, "data:0x%02x\n", data); + + if (ret) + *ret = retval; + + return data; +} + +u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) +{ + u32 data; + int retval; + + brcmf_dbg(SDIO, "addr:0x%08x\n", addr); + retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr); + if (retval) + goto done; + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, + false); + brcmf_dbg(SDIO, "data:0x%08x\n", data); + +done: + if (ret) + *ret = retval; + + return data; +} + +void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, + u8 data, int *ret) +{ + int retval; + + brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data); + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, + true); + if (ret) + *ret = retval; +} + +void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, + u32 data, int *ret) +{ + int retval; + + brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data); + retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr); + if (retval) + goto done; + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, + true); + +done: + if (ret) + *ret = retval; +} + +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; + + /* Single skb use the standard mmc interface */ + req_sz = pkt->len + 3; + req_sz &= (uint)~3; + + if (write) + err = sdio_memcpy_toio(sdiodev->func[fn], addr, + ((u8 *)(pkt->data)), req_sz); + else if (fn == 1) + err = sdio_memcpy_fromio(sdiodev->func[fn], ((u8 *)(pkt->data)), + addr, req_sz); + else + /* function 2 read is FIFO operation */ + err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr, + req_sz); + if (err == -ENOMEDIUM) + brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); + return err; +} + +/** + * brcmf_sdiod_sglist_rw - SDIO interface function for block data access + * @sdiodev: brcmfmac sdio device + * @fn: SDIO function number + * @write: direction flag + * @addr: dongle memory address as source/destination + * @pkt: skb pointer + * + * This function takes the respbonsibility as the interface function to MMC + * stack for block data access. It assumes that the skb passed down by the + * caller has already been padded and aligned. + */ +static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, + bool write, u32 addr, + struct sk_buff_head *pktlist) +{ + unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset; + unsigned int max_req_sz, orig_offset, dst_offset; + unsigned short max_seg_cnt, seg_sz; + unsigned char *pkt_data, *orig_data, *dst_data; + struct sk_buff *pkt_next = NULL, *local_pkt_next; + struct sk_buff_head local_list, *target_list; + struct mmc_request mmc_req; + struct mmc_command mmc_cmd; + struct mmc_data mmc_dat; + struct scatterlist *sgl; + int ret = 0; + + if (!pktlist->qlen) + return -EINVAL; + + target_list = pktlist; + /* for host with broken sg support, prepare a page aligned list */ + __skb_queue_head_init(&local_list); + if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) { + req_sz = 0; + skb_queue_walk(pktlist, pkt_next) + req_sz += pkt_next->len; + req_sz = ALIGN(req_sz, sdiodev->func[fn]->cur_blksize); + while (req_sz > PAGE_SIZE) { + pkt_next = brcmu_pkt_buf_get_skb(PAGE_SIZE); + if (pkt_next == NULL) { + ret = -ENOMEM; + goto exit; + } + __skb_queue_tail(&local_list, pkt_next); + req_sz -= PAGE_SIZE; + } + pkt_next = brcmu_pkt_buf_get_skb(req_sz); + if (pkt_next == NULL) { + ret = -ENOMEM; + goto exit; + } + __skb_queue_tail(&local_list, pkt_next); + target_list = &local_list; + } + + func_blk_sz = sdiodev->func[fn]->cur_blksize; + max_req_sz = sdiodev->max_request_size; + max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count, + target_list->qlen); + seg_sz = target_list->qlen; + pkt_offset = 0; + pkt_next = target_list->next; + + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + mmc_dat.sg = sdiodev->sgtable.sgl; + mmc_dat.blksz = func_blk_sz; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + mmc_cmd.opcode = SD_IO_RW_EXTENDED; + mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ + mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */ + mmc_cmd.arg |= 1<<27; /* block mode */ + /* for function 1 the addr will be incremented */ + mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + + while (seg_sz) { + req_sz = 0; + sg_cnt = 0; + sgl = sdiodev->sgtable.sgl; + /* prep sg table */ + while (pkt_next != (struct sk_buff *)target_list) { + pkt_data = pkt_next->data + pkt_offset; + sg_data_sz = pkt_next->len - pkt_offset; + if (sg_data_sz > sdiodev->max_segment_size) + sg_data_sz = sdiodev->max_segment_size; + if (sg_data_sz > max_req_sz - req_sz) + sg_data_sz = max_req_sz - req_sz; + + sg_set_buf(sgl, pkt_data, sg_data_sz); + + sg_cnt++; + sgl = sg_next(sgl); + req_sz += sg_data_sz; + pkt_offset += sg_data_sz; + if (pkt_offset == pkt_next->len) { + pkt_offset = 0; + pkt_next = pkt_next->next; + } + + if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt) + break; + } + seg_sz -= sg_cnt; + + if (req_sz % func_blk_sz != 0) { + brcmf_err("sg request length %u is not %u aligned\n", + req_sz, func_blk_sz); + ret = -ENOTBLK; + goto exit; + } + + mmc_dat.sg_len = sg_cnt; + mmc_dat.blocks = req_sz / func_blk_sz; + mmc_cmd.arg |= (addr & 0x1FFFF) << 9; /* address */ + mmc_cmd.arg |= mmc_dat.blocks & 0x1FF; /* block count */ + /* incrementing addr for function 1 */ + if (fn == 1) + addr += req_sz; + + mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card); + mmc_wait_for_req(sdiodev->func[fn]->card->host, &mmc_req); + + ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; + if (ret == -ENOMEDIUM) { + brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); + break; + } else if (ret != 0) { + brcmf_err("CMD53 sg block %s failed %d\n", + write ? "write" : "read", ret); + ret = -EIO; + break; + } + } + + if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) { + local_pkt_next = local_list.next; + orig_offset = 0; + skb_queue_walk(pktlist, pkt_next) { + dst_offset = 0; + do { + req_sz = local_pkt_next->len - orig_offset; + req_sz = min_t(uint, pkt_next->len - dst_offset, + req_sz); + orig_data = local_pkt_next->data + orig_offset; + dst_data = pkt_next->data + dst_offset; + memcpy(dst_data, orig_data, req_sz); + orig_offset += req_sz; + dst_offset += req_sz; + if (orig_offset == local_pkt_next->len) { + orig_offset = 0; + local_pkt_next = local_pkt_next->next; + } + if (dst_offset == pkt_next->len) + break; + } while (!skb_queue_empty(&local_list)); + } + } + +exit: + sg_init_table(sdiodev->sgtable.sgl, sdiodev->sgtable.orig_nents); + while ((pkt_next = __skb_dequeue(&local_list)) != NULL) + brcmu_pkt_buf_free_skb(pkt_next); + + return ret; +} + +int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes) +{ + struct sk_buff *mypkt; + int err; + + mypkt = brcmu_pkt_buf_get_skb(nbytes); + if (!mypkt) { + brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n", + nbytes); + return -EIO; + } + + err = brcmf_sdiod_recv_pkt(sdiodev, mypkt); + if (!err) + memcpy(buf, mypkt->data, nbytes); + + brcmu_pkt_buf_free_skb(mypkt); + return err; +} + +int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt) +{ + u32 addr = sdiodev->sbwad; + int err = 0; + + brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pkt->len); + + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); + if (err) + goto done; + + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, pkt); + +done: + return err; +} + +int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq, uint totlen) +{ + struct sk_buff *glom_skb; + struct sk_buff *skb; + u32 addr = sdiodev->sbwad; + int err = 0; + + brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", + addr, pktq->qlen); + + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); + if (err) + goto done; + + if (pktq->qlen == 1) + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, + pktq->next); + else if (!sdiodev->sg_support) { + glom_skb = brcmu_pkt_buf_get_skb(totlen); + if (!glom_skb) + return -ENOMEM; + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, + glom_skb); + if (err) + goto done; + + skb_queue_walk(pktq, skb) { + memcpy(skb->data, glom_skb->data, skb->len); + skb_pull(glom_skb, skb->len); + } + } else + err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, false, addr, + pktq); + +done: + return err; +} + +int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes) +{ + struct sk_buff *mypkt; + u32 addr = sdiodev->sbwad; + int err; + + mypkt = brcmu_pkt_buf_get_skb(nbytes); + if (!mypkt) { + brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n", + nbytes); + return -EIO; + } + + memcpy(mypkt->data, buf, nbytes); + + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); + + if (!err) + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, addr, + mypkt); + + brcmu_pkt_buf_free_skb(mypkt); + return err; + +} + +int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq) +{ + struct sk_buff *skb; + u32 addr = sdiodev->sbwad; + int err; + + brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pktq->qlen); + + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); + if (err) + return err; + + if (pktq->qlen == 1 || !sdiodev->sg_support) + skb_queue_walk(pktq, skb) { + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, + addr, skb); + if (err) + break; + } + else + err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, true, addr, + pktq); + + return err; +} + +int +brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, + u8 *data, uint size) +{ + int bcmerror = 0; + struct sk_buff *pkt; + u32 sdaddr; + uint dsize; + + dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); + pkt = dev_alloc_skb(dsize); + if (!pkt) { + brcmf_err("dev_alloc_skb failed: len %d\n", dsize); + return -EIO; + } + pkt->priority = 0; + + /* Determine initial transfer parameters */ + sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK; + if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK) + dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr); + else + dsize = size; + + sdio_claim_host(sdiodev->func[1]); + + /* Do the transfer(s) */ + while (size) { + /* Set the backplane window to include the start address */ + bcmerror = brcmf_sdiod_set_sbaddr_window(sdiodev, address); + if (bcmerror) + break; + + brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n", + write ? "write" : "read", dsize, + sdaddr, address & SBSDIO_SBWINDOW_MASK); + + sdaddr &= SBSDIO_SB_OFT_ADDR_MASK; + sdaddr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + skb_put(pkt, dsize); + if (write) + memcpy(pkt->data, data, dsize); + bcmerror = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_1, write, + sdaddr, pkt); + if (bcmerror) { + brcmf_err("membytes transfer failed\n"); + break; + } + if (!write) + memcpy(data, pkt->data, dsize); + skb_trim(pkt, 0); + + /* Adjust for next transfer (if any) */ + size -= dsize; + if (size) { + data += dsize; + address += dsize; + sdaddr = 0; + dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); + } + } + + dev_kfree_skb(pkt); + + /* Return the window to backplane enumeration space for core access */ + if (brcmf_sdiod_set_sbaddr_window(sdiodev, sdiodev->sbwad)) + brcmf_err("FAILED to set window back to 0x%x\n", + sdiodev->sbwad); + + sdio_release_host(sdiodev->func[1]); + + return bcmerror; +} + +int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn) +{ + char t_func = (char)fn; + brcmf_dbg(SDIO, "Enter\n"); + + /* issue abort cmd52 command through F0 */ + brcmf_sdiod_request_data(sdiodev, SDIO_FUNC_0, SDIO_CCCR_ABORT, + sizeof(t_func), &t_func, true); + + brcmf_dbg(SDIO, "Exit\n"); + return 0; +} + +static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) +{ + uint nents; + int err; + + if (!sdiodev->sg_support) + return; + + nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, brcmf_sdiod_txglomsz); + nents += (nents >> 4) + 1; + + WARN_ON(nents > sdiodev->max_segment_count); + + brcmf_dbg(TRACE, "nents=%d\n", nents); + err = sg_alloc_table(&sdiodev->sgtable, nents, GFP_KERNEL); + if (err < 0) { + brcmf_err("allocation failed: disable scatter-gather"); + sdiodev->sg_support = false; + } + + sdiodev->txglomsz = brcmf_sdiod_txglomsz; +} + +#ifdef CONFIG_PM_SLEEP +static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev) +{ + sdiodev->freezer = kzalloc(sizeof(*sdiodev->freezer), GFP_KERNEL); + if (!sdiodev->freezer) + return -ENOMEM; + atomic_set(&sdiodev->freezer->thread_count, 0); + atomic_set(&sdiodev->freezer->freezing, 0); + init_waitqueue_head(&sdiodev->freezer->thread_freeze); + init_completion(&sdiodev->freezer->resumed); + return 0; +} + +static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev) +{ + if (sdiodev->freezer) { + WARN_ON(atomic_read(&sdiodev->freezer->freezing)); + kfree(sdiodev->freezer); + } +} + +static int brcmf_sdiod_freezer_on(struct brcmf_sdio_dev *sdiodev) +{ + atomic_t *expect = &sdiodev->freezer->thread_count; + int res = 0; + + sdiodev->freezer->frozen_count = 0; + reinit_completion(&sdiodev->freezer->resumed); + atomic_set(&sdiodev->freezer->freezing, 1); + brcmf_sdio_trigger_dpc(sdiodev->bus); + wait_event(sdiodev->freezer->thread_freeze, + atomic_read(expect) == sdiodev->freezer->frozen_count); + sdio_claim_host(sdiodev->func[1]); + res = brcmf_sdio_sleep(sdiodev->bus, true); + sdio_release_host(sdiodev->func[1]); + return res; +} + +static void brcmf_sdiod_freezer_off(struct brcmf_sdio_dev *sdiodev) +{ + sdio_claim_host(sdiodev->func[1]); + brcmf_sdio_sleep(sdiodev->bus, false); + sdio_release_host(sdiodev->func[1]); + atomic_set(&sdiodev->freezer->freezing, 0); + complete_all(&sdiodev->freezer->resumed); +} + +bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev) +{ + return atomic_read(&sdiodev->freezer->freezing); +} + +void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev) +{ + if (!brcmf_sdiod_freezing(sdiodev)) + return; + sdiodev->freezer->frozen_count++; + wake_up(&sdiodev->freezer->thread_freeze); + wait_for_completion(&sdiodev->freezer->resumed); +} + +void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev) +{ + atomic_inc(&sdiodev->freezer->thread_count); +} + +void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev) +{ + atomic_dec(&sdiodev->freezer->thread_count); +} +#else +static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev) +{ + return 0; +} + +static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev) +{ +} +#endif /* CONFIG_PM_SLEEP */ + +static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) +{ + sdiodev->state = BRCMF_SDIOD_DOWN; + if (sdiodev->bus) { + brcmf_sdio_remove(sdiodev->bus); + sdiodev->bus = NULL; + } + + brcmf_sdiod_freezer_detach(sdiodev); + + /* Disable Function 2 */ + sdio_claim_host(sdiodev->func[2]); + sdio_disable_func(sdiodev->func[2]); + sdio_release_host(sdiodev->func[2]); + + /* Disable Function 1 */ + sdio_claim_host(sdiodev->func[1]); + sdio_disable_func(sdiodev->func[1]); + sdio_release_host(sdiodev->func[1]); + + sg_free_table(&sdiodev->sgtable); + sdiodev->sbwad = 0; + + pm_runtime_allow(sdiodev->func[1]->card->host->parent); + return 0; +} + +static void brcmf_sdiod_host_fixup(struct mmc_host *host) +{ + /* runtime-pm powers off the device */ + pm_runtime_forbid(host->parent); + /* avoid removal detection upon resume */ + host->caps |= MMC_CAP_NONREMOVABLE; +} + +static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) +{ + struct sdio_func *func; + struct mmc_host *host; + uint max_blocks; + int ret = 0; + + sdiodev->num_funcs = 2; + + sdio_claim_host(sdiodev->func[1]); + + ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE); + if (ret) { + brcmf_err("Failed to set F1 blocksize\n"); + sdio_release_host(sdiodev->func[1]); + goto out; + } + ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE); + if (ret) { + brcmf_err("Failed to set F2 blocksize\n"); + sdio_release_host(sdiodev->func[1]); + goto out; + } + + /* increase F2 timeout */ + sdiodev->func[2]->enable_timeout = SDIO_WAIT_F2RDY; + + /* Enable Function 1 */ + ret = sdio_enable_func(sdiodev->func[1]); + sdio_release_host(sdiodev->func[1]); + if (ret) { + brcmf_err("Failed to enable F1: err=%d\n", ret); + goto out; + } + + /* + * determine host related variables after brcmf_sdiod_probe() + * as func->cur_blksize is properly set and F2 init has been + * completed successfully. + */ + func = sdiodev->func[2]; + host = func->card->host; + sdiodev->sg_support = host->max_segs > 1; + max_blocks = min_t(uint, host->max_blk_count, 511u); + sdiodev->max_request_size = min_t(uint, host->max_req_size, + max_blocks * func->cur_blksize); + sdiodev->max_segment_count = min_t(uint, host->max_segs, + SG_MAX_SINGLE_ALLOC); + sdiodev->max_segment_size = host->max_seg_size; + + /* allocate scatter-gather table. sg support + * will be disabled upon allocation failure. + */ + brcmf_sdiod_sgtable_alloc(sdiodev); + + ret = brcmf_sdiod_freezer_attach(sdiodev); + if (ret) + goto out; + + /* try to attach to the target device */ + sdiodev->bus = brcmf_sdio_probe(sdiodev); + if (!sdiodev->bus) { + ret = -ENODEV; + goto out; + } + brcmf_sdiod_host_fixup(host); +out: + if (ret) + brcmf_sdiod_remove(sdiodev); + + return ret; +} + +#define BRCMF_SDIO_DEVICE(dev_id) \ + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)} + +/* devices we support, null terminated */ +static const struct sdio_device_id brcmf_sdmmc_ids[] = { + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), + { /* end: all zeroes */ } +}; +MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); + +static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; + + +static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev, + int val) +{ +#if IS_ENABLED(CONFIG_ACPI) + struct acpi_device *adev; + + adev = ACPI_COMPANION(dev); + if (adev) + adev->flags.power_manageable = 0; +#endif +} + +static int brcmf_ops_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + int err; + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bus *bus_if; + struct device *dev; + + brcmf_dbg(SDIO, "Enter\n"); + brcmf_dbg(SDIO, "Class=%x\n", func->class); + brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); + brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); + brcmf_dbg(SDIO, "Function#: %d\n", func->num); + + dev = &func->dev; + /* prohibit ACPI power management for this device */ + brcmf_sdiod_acpi_set_power_manageable(dev, 0); + + /* Consume func num 1 but dont do anything with it. */ + if (func->num == 1) + return 0; + + /* Ignore anything but func 2 */ + if (func->num != 2) + return -ENODEV; + + bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL); + if (!bus_if) + return -ENOMEM; + sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL); + if (!sdiodev) { + kfree(bus_if); + return -ENOMEM; + } + + /* store refs to functions used. mmc_card does + * not hold the F0 function pointer. + */ + sdiodev->func[0] = kmemdup(func, sizeof(*func), GFP_KERNEL); + sdiodev->func[0]->num = 0; + sdiodev->func[1] = func->card->sdio_func[0]; + sdiodev->func[2] = func; + + sdiodev->bus_if = bus_if; + bus_if->bus_priv.sdio = sdiodev; + bus_if->proto_type = BRCMF_PROTO_BCDC; + dev_set_drvdata(&func->dev, bus_if); + dev_set_drvdata(&sdiodev->func[1]->dev, bus_if); + sdiodev->dev = &sdiodev->func[1]->dev; + sdiodev->pdata = brcmfmac_sdio_pdata; + + if (!sdiodev->pdata) + brcmf_of_probe(sdiodev); + +#ifdef CONFIG_PM_SLEEP + /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ + * is true or when platform data OOB irq is true). + */ + if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) && + ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) || + (sdiodev->pdata && sdiodev->pdata->oob_irq_supported))) + bus_if->wowl_supported = true; +#endif + + brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN); + + brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n"); + err = brcmf_sdiod_probe(sdiodev); + if (err) { + brcmf_err("F2 error, probe failed %d...\n", err); + goto fail; + } + + brcmf_dbg(SDIO, "F2 init completed...\n"); + return 0; + +fail: + dev_set_drvdata(&func->dev, NULL); + dev_set_drvdata(&sdiodev->func[1]->dev, NULL); + kfree(sdiodev->func[0]); + kfree(sdiodev); + kfree(bus_if); + return err; +} + +static void brcmf_ops_sdio_remove(struct sdio_func *func) +{ + struct brcmf_bus *bus_if; + struct brcmf_sdio_dev *sdiodev; + + brcmf_dbg(SDIO, "Enter\n"); + brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); + brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); + brcmf_dbg(SDIO, "Function: %d\n", func->num); + + if (func->num != 1) + return; + + bus_if = dev_get_drvdata(&func->dev); + if (bus_if) { + sdiodev = bus_if->bus_priv.sdio; + brcmf_sdiod_remove(sdiodev); + + dev_set_drvdata(&sdiodev->func[1]->dev, NULL); + dev_set_drvdata(&sdiodev->func[2]->dev, NULL); + + kfree(bus_if); + kfree(sdiodev->func[0]); + kfree(sdiodev); + } + + brcmf_dbg(SDIO, "Exit\n"); +} + +void brcmf_sdio_wowl_config(struct device *dev, bool enabled) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + + brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled); + sdiodev->wowl_enabled = enabled; +} + +#ifdef CONFIG_PM_SLEEP +static int brcmf_ops_sdio_suspend(struct device *dev) +{ + struct sdio_func *func; + struct brcmf_bus *bus_if; + struct brcmf_sdio_dev *sdiodev; + mmc_pm_flag_t sdio_flags; + + func = container_of(dev, struct sdio_func, dev); + brcmf_dbg(SDIO, "Enter: F%d\n", func->num); + if (func->num != SDIO_FUNC_1) + return 0; + + + bus_if = dev_get_drvdata(dev); + sdiodev = bus_if->bus_priv.sdio; + + brcmf_sdiod_freezer_on(sdiodev); + brcmf_sdio_wd_timer(sdiodev->bus, 0); + + sdio_flags = MMC_PM_KEEP_POWER; + if (sdiodev->wowl_enabled) { + if (sdiodev->pdata->oob_irq_supported) + enable_irq_wake(sdiodev->pdata->oob_irq_nr); + else + sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; + } + if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) + brcmf_err("Failed to set pm_flags %x\n", sdio_flags); + return 0; +} + +static int brcmf_ops_sdio_resume(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct sdio_func *func = container_of(dev, struct sdio_func, dev); + + brcmf_dbg(SDIO, "Enter: F%d\n", func->num); + if (func->num != SDIO_FUNC_2) + return 0; + + brcmf_sdiod_freezer_off(sdiodev); + return 0; +} + +static const struct dev_pm_ops brcmf_sdio_pm_ops = { + .suspend = brcmf_ops_sdio_suspend, + .resume = brcmf_ops_sdio_resume, +}; +#endif /* CONFIG_PM_SLEEP */ + +static struct sdio_driver brcmf_sdmmc_driver = { + .probe = brcmf_ops_sdio_probe, + .remove = brcmf_ops_sdio_remove, + .name = BRCMFMAC_SDIO_PDATA_NAME, + .id_table = brcmf_sdmmc_ids, + .drv = { + .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP + .pm = &brcmf_sdio_pm_ops, +#endif /* CONFIG_PM_SLEEP */ + }, +}; + +static int __init brcmf_sdio_pd_probe(struct platform_device *pdev) +{ + brcmf_dbg(SDIO, "Enter\n"); + + brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev); + + if (brcmfmac_sdio_pdata->power_on) + brcmfmac_sdio_pdata->power_on(); + + return 0; +} + +static int brcmf_sdio_pd_remove(struct platform_device *pdev) +{ + brcmf_dbg(SDIO, "Enter\n"); + + if (brcmfmac_sdio_pdata->power_off) + brcmfmac_sdio_pdata->power_off(); + + sdio_unregister_driver(&brcmf_sdmmc_driver); + + return 0; +} + +static struct platform_driver brcmf_sdio_pd = { + .remove = brcmf_sdio_pd_remove, + .driver = { + .name = BRCMFMAC_SDIO_PDATA_NAME, + } +}; + +void brcmf_sdio_register(void) +{ + int ret; + + ret = sdio_register_driver(&brcmf_sdmmc_driver); + if (ret) + brcmf_err("sdio_register_driver failed: %d\n", ret); +} + +void brcmf_sdio_exit(void) +{ + brcmf_dbg(SDIO, "Enter\n"); + + if (brcmfmac_sdio_pdata) + platform_driver_unregister(&brcmf_sdio_pd); + else + sdio_unregister_driver(&brcmf_sdmmc_driver); +} + +void __init brcmf_sdio_init(void) +{ + int ret; + + brcmf_dbg(SDIO, "Enter\n"); + + ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe); + if (ret == -ENODEV) + brcmf_dbg(SDIO, "No platform data available.\n"); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c new file mode 100644 index 000000000000..4e33f96b3dd1 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c @@ -0,0 +1,497 @@ +/* + * 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 +#include +#include +#include "core.h" +#include "debug.h" +#include "fwil.h" +#include "fwil_types.h" +#include "btcoex.h" +#include "p2p.h" +#include "cfg80211.h" + +/* T1 start SCO/eSCO priority suppression */ +#define BRCMF_BTCOEX_OPPR_WIN_TIME 2000 + +/* BT registers values during DHCP */ +#define BRCMF_BT_DHCP_REG50 0x8022 +#define BRCMF_BT_DHCP_REG51 0 +#define BRCMF_BT_DHCP_REG64 0 +#define BRCMF_BT_DHCP_REG65 0 +#define BRCMF_BT_DHCP_REG71 0 +#define BRCMF_BT_DHCP_REG66 0x2710 +#define BRCMF_BT_DHCP_REG41 0x33 +#define BRCMF_BT_DHCP_REG68 0x190 + +/* number of samples for SCO detection */ +#define BRCMF_BT_SCO_SAMPLES 12 + +/** +* enum brcmf_btcoex_state - BT coex DHCP state machine states +* @BRCMF_BT_DHCP_IDLE: DCHP is idle +* @BRCMF_BT_DHCP_START: DHCP started, wait before +* boosting wifi priority +* @BRCMF_BT_DHCP_OPPR_WIN: graceful DHCP opportunity ended, +* boost wifi priority +* @BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: wifi priority boost end, +* restore defaults +*/ +enum brcmf_btcoex_state { + BRCMF_BT_DHCP_IDLE, + BRCMF_BT_DHCP_START, + BRCMF_BT_DHCP_OPPR_WIN, + BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT +}; + +/** + * struct brcmf_btcoex_info - BT coex related information + * @vif: interface for which request was done. + * @timer: timer for DHCP state machine + * @timeout: configured timeout. + * @timer_on: DHCP timer active + * @dhcp_done: DHCP finished before T1/T2 timer expiration + * @bt_state: DHCP state machine state + * @work: DHCP state machine work + * @cfg: driver private data for cfg80211 interface + * @reg66: saved value of btc_params 66 + * @reg41: saved value of btc_params 41 + * @reg68: saved value of btc_params 68 + * @saved_regs_part1: flag indicating regs 66,41,68 + * have been saved + * @reg51: saved value of btc_params 51 + * @reg64: saved value of btc_params 64 + * @reg65: saved value of btc_params 65 + * @reg71: saved value of btc_params 71 + * @saved_regs_part1: flag indicating regs 50,51,64,65,71 + * have been saved + */ +struct brcmf_btcoex_info { + struct brcmf_cfg80211_vif *vif; + struct timer_list timer; + u16 timeout; + bool timer_on; + bool dhcp_done; + enum brcmf_btcoex_state bt_state; + struct work_struct work; + struct brcmf_cfg80211_info *cfg; + u32 reg66; + u32 reg41; + u32 reg68; + bool saved_regs_part1; + u32 reg50; + u32 reg51; + u32 reg64; + u32 reg65; + u32 reg71; + bool saved_regs_part2; +}; + +/** + * brcmf_btcoex_params_write() - write btc_params firmware variable + * @ifp: interface + * @addr: btc_params register number + * @data: data to write + */ +static s32 brcmf_btcoex_params_write(struct brcmf_if *ifp, u32 addr, u32 data) +{ + struct { + __le32 addr; + __le32 data; + } reg_write; + + reg_write.addr = cpu_to_le32(addr); + reg_write.data = cpu_to_le32(data); + return brcmf_fil_iovar_data_set(ifp, "btc_params", + ®_write, sizeof(reg_write)); +} + +/** + * brcmf_btcoex_params_read() - read btc_params firmware variable + * @ifp: interface + * @addr: btc_params register number + * @data: read data + */ +static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data) +{ + *data = addr; + + return brcmf_fil_iovar_int_get(ifp, "btc_params", data); +} + +/** + * brcmf_btcoex_boost_wifi() - control BT SCO/eSCO parameters + * @btci: BT coex info + * @trump_sco: + * true - set SCO/eSCO parameters for compatibility + * during DHCP window + * false - restore saved parameter values + * + * Enhanced BT COEX settings for eSCO compatibility during DHCP window + */ +static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, + bool trump_sco) +{ + struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0); + + if (trump_sco && !btci->saved_regs_part2) { + /* this should reduce eSCO agressive + * retransmit w/o breaking it + */ + + /* save current */ + brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n"); + brcmf_btcoex_params_read(ifp, 50, &btci->reg50); + brcmf_btcoex_params_read(ifp, 51, &btci->reg51); + brcmf_btcoex_params_read(ifp, 64, &btci->reg64); + brcmf_btcoex_params_read(ifp, 65, &btci->reg65); + brcmf_btcoex_params_read(ifp, 71, &btci->reg71); + + btci->saved_regs_part2 = true; + brcmf_dbg(INFO, + "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", + btci->reg50, btci->reg51, btci->reg64, + btci->reg65, btci->reg71); + + /* pacify the eSco */ + brcmf_btcoex_params_write(ifp, 50, BRCMF_BT_DHCP_REG50); + brcmf_btcoex_params_write(ifp, 51, BRCMF_BT_DHCP_REG51); + brcmf_btcoex_params_write(ifp, 64, BRCMF_BT_DHCP_REG64); + brcmf_btcoex_params_write(ifp, 65, BRCMF_BT_DHCP_REG65); + brcmf_btcoex_params_write(ifp, 71, BRCMF_BT_DHCP_REG71); + + } else if (btci->saved_regs_part2) { + /* restore previously saved bt params */ + brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n"); + brcmf_btcoex_params_write(ifp, 50, btci->reg50); + brcmf_btcoex_params_write(ifp, 51, btci->reg51); + brcmf_btcoex_params_write(ifp, 64, btci->reg64); + brcmf_btcoex_params_write(ifp, 65, btci->reg65); + brcmf_btcoex_params_write(ifp, 71, btci->reg71); + + brcmf_dbg(INFO, + "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", + btci->reg50, btci->reg51, btci->reg64, + btci->reg65, btci->reg71); + + btci->saved_regs_part2 = false; + } else { + brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n"); + } +} + +/** + * brcmf_btcoex_is_sco_active() - check if SCO/eSCO is active + * @ifp: interface + * + * return: true if SCO/eSCO session is active + */ +static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp) +{ + int ioc_res = 0; + bool res = false; + int sco_id_cnt = 0; + u32 param27; + int i; + + for (i = 0; i < BRCMF_BT_SCO_SAMPLES; i++) { + ioc_res = brcmf_btcoex_params_read(ifp, 27, ¶m27); + + if (ioc_res < 0) { + brcmf_err("ioc read btc params error\n"); + break; + } + + brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27); + + if ((param27 & 0x6) == 2) { /* count both sco & esco */ + sco_id_cnt++; + } + + if (sco_id_cnt > 2) { + brcmf_dbg(INFO, + "sco/esco detected, pkt id_cnt:%d samples:%d\n", + sco_id_cnt, i); + res = true; + break; + } + } + brcmf_dbg(TRACE, "exit: result=%d\n", res); + return res; +} + +/** + * btcmf_btcoex_save_part1() - save first step parameters. + */ +static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci) +{ + struct brcmf_if *ifp = btci->vif->ifp; + + if (!btci->saved_regs_part1) { + /* Retrieve and save original reg value */ + brcmf_btcoex_params_read(ifp, 66, &btci->reg66); + brcmf_btcoex_params_read(ifp, 41, &btci->reg41); + brcmf_btcoex_params_read(ifp, 68, &btci->reg68); + btci->saved_regs_part1 = true; + brcmf_dbg(INFO, + "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n", + btci->reg66, btci->reg41, + btci->reg68); + } +} + +/** + * brcmf_btcoex_restore_part1() - restore first step parameters. + */ +static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci) +{ + struct brcmf_if *ifp; + + if (btci->saved_regs_part1) { + btci->saved_regs_part1 = false; + ifp = btci->vif->ifp; + brcmf_btcoex_params_write(ifp, 66, btci->reg66); + brcmf_btcoex_params_write(ifp, 41, btci->reg41); + brcmf_btcoex_params_write(ifp, 68, btci->reg68); + brcmf_dbg(INFO, + "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n", + btci->reg66, btci->reg41, + btci->reg68); + } +} + +/** + * brcmf_btcoex_timerfunc() - BT coex timer callback + */ +static void brcmf_btcoex_timerfunc(ulong data) +{ + struct brcmf_btcoex_info *bt_local = (struct brcmf_btcoex_info *)data; + brcmf_dbg(TRACE, "enter\n"); + + bt_local->timer_on = false; + schedule_work(&bt_local->work); +} + +/** + * brcmf_btcoex_handler() - BT coex state machine work handler + * @work: work + */ +static void brcmf_btcoex_handler(struct work_struct *work) +{ + struct brcmf_btcoex_info *btci; + btci = container_of(work, struct brcmf_btcoex_info, work); + if (btci->timer_on) { + btci->timer_on = false; + del_timer_sync(&btci->timer); + } + + switch (btci->bt_state) { + case BRCMF_BT_DHCP_START: + /* DHCP started provide OPPORTUNITY window + to get DHCP address + */ + brcmf_dbg(INFO, "DHCP started\n"); + btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN; + if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) { + mod_timer(&btci->timer, btci->timer.expires); + } else { + btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME; + mod_timer(&btci->timer, + jiffies + + msecs_to_jiffies(BRCMF_BTCOEX_OPPR_WIN_TIME)); + } + btci->timer_on = true; + break; + + case BRCMF_BT_DHCP_OPPR_WIN: + if (btci->dhcp_done) { + brcmf_dbg(INFO, "DHCP done before T1 expiration\n"); + goto idle; + } + + /* DHCP is not over yet, start lowering BT priority */ + brcmf_dbg(INFO, "DHCP T1:%d expired\n", + BRCMF_BTCOEX_OPPR_WIN_TIME); + brcmf_btcoex_boost_wifi(btci, true); + + btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT; + mod_timer(&btci->timer, + jiffies + msecs_to_jiffies(btci->timeout)); + btci->timer_on = true; + break; + + case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: + if (btci->dhcp_done) + brcmf_dbg(INFO, "DHCP done before T2 expiration\n"); + else + brcmf_dbg(INFO, "DHCP T2:%d expired\n", + BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT); + + goto idle; + + default: + brcmf_err("invalid state=%d !!!\n", btci->bt_state); + goto idle; + } + + return; + +idle: + btci->bt_state = BRCMF_BT_DHCP_IDLE; + btci->timer_on = false; + brcmf_btcoex_boost_wifi(btci, false); + cfg80211_crit_proto_stopped(&btci->vif->wdev, GFP_KERNEL); + brcmf_btcoex_restore_part1(btci); + btci->vif = NULL; +} + +/** + * brcmf_btcoex_attach() - initialize BT coex data + * @cfg: driver private cfg80211 data + * + * return: 0 on success + */ +int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_btcoex_info *btci = NULL; + brcmf_dbg(TRACE, "enter\n"); + + btci = kmalloc(sizeof(struct brcmf_btcoex_info), GFP_KERNEL); + if (!btci) + return -ENOMEM; + + btci->bt_state = BRCMF_BT_DHCP_IDLE; + + /* Set up timer for BT */ + btci->timer_on = false; + btci->timeout = BRCMF_BTCOEX_OPPR_WIN_TIME; + init_timer(&btci->timer); + btci->timer.data = (ulong)btci; + btci->timer.function = brcmf_btcoex_timerfunc; + btci->cfg = cfg; + btci->saved_regs_part1 = false; + btci->saved_regs_part2 = false; + + INIT_WORK(&btci->work, brcmf_btcoex_handler); + + cfg->btcoex = btci; + return 0; +} + +/** + * brcmf_btcoex_detach - clean BT coex data + * @cfg: driver private cfg80211 data + */ +void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg) +{ + brcmf_dbg(TRACE, "enter\n"); + + if (!cfg->btcoex) + return; + + if (cfg->btcoex->timer_on) { + cfg->btcoex->timer_on = false; + del_timer_sync(&cfg->btcoex->timer); + } + + cancel_work_sync(&cfg->btcoex->work); + + brcmf_btcoex_boost_wifi(cfg->btcoex, false); + brcmf_btcoex_restore_part1(cfg->btcoex); + + kfree(cfg->btcoex); + cfg->btcoex = NULL; +} + +static void brcmf_btcoex_dhcp_start(struct brcmf_btcoex_info *btci) +{ + struct brcmf_if *ifp = btci->vif->ifp; + + btcmf_btcoex_save_part1(btci); + /* set new regs values */ + brcmf_btcoex_params_write(ifp, 66, BRCMF_BT_DHCP_REG66); + brcmf_btcoex_params_write(ifp, 41, BRCMF_BT_DHCP_REG41); + brcmf_btcoex_params_write(ifp, 68, BRCMF_BT_DHCP_REG68); + btci->dhcp_done = false; + btci->bt_state = BRCMF_BT_DHCP_START; + schedule_work(&btci->work); + brcmf_dbg(TRACE, "enable BT DHCP Timer\n"); +} + +static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci) +{ + /* Stop any bt timer because DHCP session is done */ + btci->dhcp_done = true; + if (btci->timer_on) { + brcmf_dbg(INFO, "disable BT DHCP Timer\n"); + btci->timer_on = false; + del_timer_sync(&btci->timer); + + /* schedule worker if transition to IDLE is needed */ + if (btci->bt_state != BRCMF_BT_DHCP_IDLE) { + brcmf_dbg(INFO, "bt_state:%d\n", + btci->bt_state); + schedule_work(&btci->work); + } + } else { + /* Restore original values */ + brcmf_btcoex_restore_part1(btci); + } +} + +/** + * brcmf_btcoex_set_mode - set BT coex mode + * @cfg: driver private cfg80211 data + * @mode: Wifi-Bluetooth coexistence mode + * + * return: 0 on success + */ +int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, + enum brcmf_btcoex_mode mode, u16 duration) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy); + struct brcmf_btcoex_info *btci = cfg->btcoex; + struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0); + + switch (mode) { + case BRCMF_BTCOEX_DISABLED: + brcmf_dbg(INFO, "DHCP session starts\n"); + if (btci->bt_state != BRCMF_BT_DHCP_IDLE) + return -EBUSY; + /* Start BT timer only for SCO connection */ + if (brcmf_btcoex_is_sco_active(ifp)) { + btci->timeout = duration; + btci->vif = vif; + brcmf_btcoex_dhcp_start(btci); + } + break; + + case BRCMF_BTCOEX_ENABLED: + brcmf_dbg(INFO, "DHCP session ends\n"); + if (btci->bt_state != BRCMF_BT_DHCP_IDLE && + vif == btci->vif) { + brcmf_btcoex_dhcp_end(btci); + } + break; + default: + brcmf_dbg(INFO, "Unknown mode, ignored\n"); + } + return 0; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h new file mode 100644 index 000000000000..19647c68aa9e --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h @@ -0,0 +1,29 @@ +/* + * 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 WL_BTCOEX_H_ +#define WL_BTCOEX_H_ + +enum brcmf_btcoex_mode { + BRCMF_BTCOEX_DISABLED, + BRCMF_BTCOEX_ENABLED +}; + +int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg); +void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg); +int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, + enum brcmf_btcoex_mode mode, u16 duration); + +#endif /* WL_BTCOEX_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h new file mode 100644 index 000000000000..230cad788ace --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2010 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_BUS_H +#define BRCMFMAC_BUS_H + +#include "debug.h" + +/* IDs of the 6 default common rings of msgbuf protocol */ +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT 1 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE 2 +#define BRCMF_D2H_MSGRING_TX_COMPLETE 3 +#define BRCMF_D2H_MSGRING_RX_COMPLETE 4 + +#define BRCMF_NROF_H2D_COMMON_MSGRINGS 2 +#define BRCMF_NROF_D2H_COMMON_MSGRINGS 3 +#define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \ + BRCMF_NROF_D2H_COMMON_MSGRINGS) + +/* The level of bus communication with the dongle */ +enum brcmf_bus_state { + BRCMF_BUS_DOWN, /* Not ready for frame transfers */ + BRCMF_BUS_UP /* Ready for frame transfers */ +}; + +/* The level of bus communication with the dongle */ +enum brcmf_bus_protocol_type { + BRCMF_PROTO_BCDC, + BRCMF_PROTO_MSGBUF +}; + +struct brcmf_bus_dcmd { + char *name; + char *param; + int param_len; + struct list_head list; +}; + +/** + * struct brcmf_bus_ops - bus callback operations. + * + * @preinit: execute bus/device specific dongle init commands (optional). + * @init: prepare for communication with dongle. + * @stop: clear pending frames, disable data flow. + * @txdata: send a data frame to the dongle. When the data + * has been transferred, the common driver must be + * notified using brcmf_txcomplete(). The common + * driver calls this function with interrupts + * disabled. + * @txctl: transmit a control request message to dongle. + * @rxctl: receive a control response message from dongle. + * @gettxq: obtain a reference of bus transmit queue (optional). + * @wowl_config: specify if dongle is configured for wowl when going to suspend + * @get_ramsize: obtain size of device memory. + * @get_memdump: obtain device memory dump in provided buffer. + * + * This structure provides an abstract interface towards the + * bus specific driver. For control messages to common driver + * will assure there is only one active transaction. Unless + * indicated otherwise these callbacks are mandatory. + */ +struct brcmf_bus_ops { + int (*preinit)(struct device *dev); + void (*stop)(struct device *dev); + int (*txdata)(struct device *dev, struct sk_buff *skb); + int (*txctl)(struct device *dev, unsigned char *msg, uint len); + int (*rxctl)(struct device *dev, unsigned char *msg, uint len); + struct pktq * (*gettxq)(struct device *dev); + void (*wowl_config)(struct device *dev, bool enabled); + size_t (*get_ramsize)(struct device *dev); + int (*get_memdump)(struct device *dev, void *data, size_t len); +}; + + +/** + * struct brcmf_bus_msgbuf - bus ringbuf if in case of msgbuf. + * + * @commonrings: commonrings which are always there. + * @flowrings: commonrings which are dynamically created and destroyed for data. + * @rx_dataoffset: if set then all rx data has this this offset. + * @max_rxbufpost: maximum number of buffers to post for rx. + * @nrof_flowrings: number of flowrings. + */ +struct brcmf_bus_msgbuf { + struct brcmf_commonring *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; + struct brcmf_commonring **flowrings; + u32 rx_dataoffset; + u32 max_rxbufpost; + u32 nrof_flowrings; +}; + + +/** + * struct brcmf_bus - interface structure between common and bus layer + * + * @bus_priv: pointer to private bus device. + * @proto_type: protocol type, bcdc or msgbuf + * @dev: device pointer of bus device. + * @drvr: public driver information. + * @state: operational state of the bus interface. + * @maxctl: maximum size for rxctl request message. + * @tx_realloc: number of tx packets realloced for headroom. + * @dstats: dongle-based statistical data. + * @dcmd_list: bus/device specific dongle initialization commands. + * @chip: device identifier of the dongle chip. + * @wowl_supported: is wowl supported by bus driver. + * @chiprev: revision of the dongle chip. + */ +struct brcmf_bus { + union { + struct brcmf_sdio_dev *sdio; + struct brcmf_usbdev *usb; + struct brcmf_pciedev *pcie; + } bus_priv; + enum brcmf_bus_protocol_type proto_type; + struct device *dev; + struct brcmf_pub *drvr; + enum brcmf_bus_state state; + uint maxctl; + unsigned long tx_realloc; + u32 chip; + u32 chiprev; + bool always_use_fws_queue; + bool wowl_supported; + + struct brcmf_bus_ops *ops; + struct brcmf_bus_msgbuf *msgbuf; +}; + +/* + * callback wrappers + */ +static inline int brcmf_bus_preinit(struct brcmf_bus *bus) +{ + if (!bus->ops->preinit) + return 0; + return bus->ops->preinit(bus->dev); +} + +static inline void brcmf_bus_stop(struct brcmf_bus *bus) +{ + bus->ops->stop(bus->dev); +} + +static inline int brcmf_bus_txdata(struct brcmf_bus *bus, struct sk_buff *skb) +{ + return bus->ops->txdata(bus->dev, skb); +} + +static inline +int brcmf_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint len) +{ + return bus->ops->txctl(bus->dev, msg, len); +} + +static inline +int brcmf_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint len) +{ + return bus->ops->rxctl(bus->dev, msg, len); +} + +static inline +struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus) +{ + if (!bus->ops->gettxq) + return ERR_PTR(-ENOENT); + + return bus->ops->gettxq(bus->dev); +} + +static inline +void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled) +{ + if (bus->ops->wowl_config) + bus->ops->wowl_config(bus->dev, enabled); +} + +static inline size_t brcmf_bus_get_ramsize(struct brcmf_bus *bus) +{ + if (!bus->ops->get_ramsize) + return 0; + + return bus->ops->get_ramsize(bus->dev); +} + +static inline +int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len) +{ + if (!bus->ops->get_memdump) + return -EOPNOTSUPP; + + return bus->ops->get_memdump(bus->dev, data, len); +} + +/* + * interface functions from common layer + */ + +bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, + int prec); + +/* Receive frame for delivery to OS. Callee disposes of rxp. */ +void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp); + +/* Indication from bus module regarding presence/insertion of dongle. */ +int brcmf_attach(struct device *dev); +/* Indication from bus module regarding removal/absence of dongle */ +void brcmf_detach(struct device *dev); +/* Indication from bus module that dongle should be reset */ +void brcmf_dev_reset(struct device *dev); +/* Indication from bus module to change flow-control state */ +void brcmf_txflowblock(struct device *dev, bool state); + +/* Notify the bus has transferred the tx packet to firmware */ +void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); + +/* Configure the "global" bus state used by upper layers */ +void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state); + +int brcmf_bus_start(struct device *dev); +s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len); +void brcmf_bus_add_txhdrlen(struct device *dev, uint len); + +#ifdef CONFIG_BRCMFMAC_SDIO +void brcmf_sdio_exit(void); +void brcmf_sdio_init(void); +void brcmf_sdio_register(void); +#endif +#ifdef CONFIG_BRCMFMAC_USB +void brcmf_usb_exit(void); +void brcmf_usb_register(void); +#endif + +#endif /* BRCMFMAC_BUS_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c new file mode 100644 index 000000000000..deb5f78dcacc --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -0,0 +1,6357 @@ +/* + * Copyright (c) 2010 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. + */ + +/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "core.h" +#include "debug.h" +#include "tracepoint.h" +#include "fwil_types.h" +#include "p2p.h" +#include "btcoex.h" +#include "cfg80211.h" +#include "feature.h" +#include "fwil.h" +#include "proto.h" +#include "vendor.h" +#include "bus.h" +#include "common.h" + +#define BRCMF_SCAN_IE_LEN_MAX 2048 +#define BRCMF_PNO_VERSION 2 +#define BRCMF_PNO_TIME 30 +#define BRCMF_PNO_REPEAT 4 +#define BRCMF_PNO_FREQ_EXPO_MAX 3 +#define BRCMF_PNO_MAX_PFN_COUNT 16 +#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6 +#define BRCMF_PNO_HIDDEN_BIT 2 +#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF +#define BRCMF_PNO_SCAN_COMPLETE 1 +#define BRCMF_PNO_SCAN_INCOMPLETE 0 + +#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */ +#define WPA_OUI_TYPE 1 +#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */ +#define WME_OUI_TYPE 2 +#define WPS_OUI_TYPE 4 + +#define VS_IE_FIXED_HDR_LEN 6 +#define WPA_IE_VERSION_LEN 2 +#define WPA_IE_MIN_OUI_LEN 4 +#define WPA_IE_SUITE_COUNT_LEN 2 + +#define WPA_CIPHER_NONE 0 /* None */ +#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */ +#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */ +#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */ +#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */ + +#define RSN_AKM_NONE 0 /* None (IBSS) */ +#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */ +#define RSN_AKM_PSK 2 /* Pre-shared Key */ +#define RSN_CAP_LEN 2 /* Length of RSN capabilities */ +#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C + +#define VNDR_IE_CMD_LEN 4 /* length of the set command + * string :"add", "del" (+ NUL) + */ +#define VNDR_IE_COUNT_OFFSET 4 +#define VNDR_IE_PKTFLAG_OFFSET 8 +#define VNDR_IE_VSIE_OFFSET 12 +#define VNDR_IE_HDR_SIZE 12 +#define VNDR_IE_PARSE_LIMIT 5 + +#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */ +#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */ + +#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 +#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 +#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20 + +#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ + (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) + +static bool check_vif_up(struct brcmf_cfg80211_vif *vif) +{ + if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) { + brcmf_dbg(INFO, "device is not ready : status (%lu)\n", + vif->sme_state); + return false; + } + return true; +} + +#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) +#define RATETAB_ENT(_rateid, _flags) \ + { \ + .bitrate = RATE_TO_BASE100KBPS(_rateid), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate __wl_rates[] = { + RATETAB_ENT(BRCM_RATE_1M, 0), + RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(BRCM_RATE_6M, 0), + RATETAB_ENT(BRCM_RATE_9M, 0), + RATETAB_ENT(BRCM_RATE_12M, 0), + RATETAB_ENT(BRCM_RATE_18M, 0), + RATETAB_ENT(BRCM_RATE_24M, 0), + RATETAB_ENT(BRCM_RATE_36M, 0), + RATETAB_ENT(BRCM_RATE_48M, 0), + RATETAB_ENT(BRCM_RATE_54M, 0), +}; + +#define wl_g_rates (__wl_rates + 0) +#define wl_g_rates_size ARRAY_SIZE(__wl_rates) +#define wl_a_rates (__wl_rates + 4) +#define wl_a_rates_size (wl_g_rates_size - 4) + +#define CHAN2G(_channel, _freq) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = IEEE80211_CHAN_DISABLED, \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = IEEE80211_CHAN_DISABLED, \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel __wl_2ghz_channels[] = { + CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427), + CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447), + CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467), + CHAN2G(13, 2472), CHAN2G(14, 2484) +}; + +static struct ieee80211_channel __wl_5ghz_channels[] = { + CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42), + CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56), + CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108), + CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128), + CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149), + CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165) +}; + +/* Band templates duplicated per wiphy. The channel info + * above is added to the band during setup. + */ +static const struct ieee80211_supported_band __wl_band_2ghz = { + .band = IEEE80211_BAND_2GHZ, + .bitrates = wl_g_rates, + .n_bitrates = wl_g_rates_size, +}; + +static const struct ieee80211_supported_band __wl_band_5ghz = { + .band = IEEE80211_BAND_5GHZ, + .bitrates = wl_a_rates, + .n_bitrates = wl_a_rates_size, +}; + +/* This is to override regulatory domains defined in cfg80211 module (reg.c) + * By default world regulatory domain defined in reg.c puts the flags + * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165). + * With respect to these flags, wpa_supplicant doesn't * start p2p + * operations on 5GHz channels. All the changes in world regulatory + * domain are to be done here. + */ +static const struct ieee80211_regdomain brcmf_regdom = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), + /* If any */ + /* IEEE 802.11 channel 14 - Only JP enables + * this and for 802.11b only + */ + REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), + /* IEEE 802.11a, channel 36..64 */ + REG_RULE(5150-10, 5350+10, 80, 6, 20, 0), + /* IEEE 802.11a, channel 100..165 */ + REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), } +}; + +static const u32 __wl_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + +/* Vendor specific ie. id = 221, oui and type defines exact ie */ +struct brcmf_vs_tlv { + u8 id; + u8 len; + u8 oui[3]; + u8 oui_type; +}; + +struct parsed_vndr_ie_info { + u8 *ie_ptr; + u32 ie_len; /* total length including id & length field */ + struct brcmf_vs_tlv vndrie; +}; + +struct parsed_vndr_ies { + u32 count; + struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; +}; + +static int brcmf_roamoff; +module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); +MODULE_PARM_DESC(roamoff, "do not use internal roaming engine"); + + +static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, + struct cfg80211_chan_def *ch) +{ + struct brcmu_chan ch_inf; + s32 primary_offset; + + brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n", + ch->chan->center_freq, ch->center_freq1, ch->width); + ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1); + primary_offset = ch->center_freq1 - ch->chan->center_freq; + switch (ch->width) { + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_20_NOHT: + ch_inf.bw = BRCMU_CHAN_BW_20; + WARN_ON(primary_offset != 0); + break; + case NL80211_CHAN_WIDTH_40: + ch_inf.bw = BRCMU_CHAN_BW_40; + if (primary_offset < 0) + ch_inf.sb = BRCMU_CHAN_SB_U; + else + ch_inf.sb = BRCMU_CHAN_SB_L; + break; + case NL80211_CHAN_WIDTH_80: + ch_inf.bw = BRCMU_CHAN_BW_80; + if (primary_offset < 0) { + if (primary_offset < -CH_10MHZ_APART) + ch_inf.sb = BRCMU_CHAN_SB_UU; + else + ch_inf.sb = BRCMU_CHAN_SB_UL; + } else { + if (primary_offset > CH_10MHZ_APART) + ch_inf.sb = BRCMU_CHAN_SB_LL; + else + ch_inf.sb = BRCMU_CHAN_SB_LU; + } + break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + default: + WARN_ON_ONCE(1); + } + switch (ch->chan->band) { + case IEEE80211_BAND_2GHZ: + ch_inf.band = BRCMU_CHAN_BAND_2G; + break; + case IEEE80211_BAND_5GHZ: + ch_inf.band = BRCMU_CHAN_BAND_5G; + break; + case IEEE80211_BAND_60GHZ: + default: + WARN_ON_ONCE(1); + } + d11inf->encchspec(&ch_inf); + + return ch_inf.chspec; +} + +u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, + struct ieee80211_channel *ch) +{ + struct brcmu_chan ch_inf; + + ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq); + ch_inf.bw = BRCMU_CHAN_BW_20; + d11inf->encchspec(&ch_inf); + + return ch_inf.chspec; +} + +/* Traverse a string of 1-byte tag/1-byte length/variable-length value + * triples, returning a pointer to the substring whose first element + * matches tag + */ +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key) +{ + const struct brcmf_tlv *elt = buf; + int totlen = buflen; + + /* find tagged parameter */ + while (totlen >= TLV_HDR_LEN) { + int len = elt->len; + + /* validate remaining totlen */ + if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN))) + return elt; + + elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN)); + totlen -= (len + TLV_HDR_LEN); + } + + return NULL; +} + +/* Is any of the tlvs the expected entry? If + * not update the tlvs buffer pointer/length. + */ +static bool +brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, + const u8 *oui, u32 oui_len, u8 type) +{ + /* If the contents match the OUI and the type */ + if (ie[TLV_LEN_OFF] >= oui_len + 1 && + !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) && + type == ie[TLV_BODY_OFF + oui_len]) { + return true; + } + + if (tlvs == NULL) + return false; + /* point to the next ie */ + ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN; + /* calculate the length of the rest of the buffer */ + *tlvs_len -= (int)(ie - *tlvs); + /* update the pointer to the start of the buffer */ + *tlvs = ie; + + return false; +} + +static struct brcmf_vs_tlv * +brcmf_find_wpaie(const u8 *parse, u32 len) +{ + const struct brcmf_tlv *ie; + + while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { + if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len, + WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE)) + return (struct brcmf_vs_tlv *)ie; + } + return NULL; +} + +static struct brcmf_vs_tlv * +brcmf_find_wpsie(const u8 *parse, u32 len) +{ + const struct brcmf_tlv *ie; + + while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { + if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, + WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE)) + return (struct brcmf_vs_tlv *)ie; + } + return NULL; +} + +static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg, + struct brcmf_cfg80211_vif *vif, + enum nl80211_iftype new_type) +{ + int iftype_num[NUM_NL80211_IFTYPES]; + struct brcmf_cfg80211_vif *pos; + + memset(&iftype_num[0], 0, sizeof(iftype_num)); + list_for_each_entry(pos, &cfg->vif_list, list) + if (pos == vif) + iftype_num[new_type]++; + else + iftype_num[pos->wdev.iftype]++; + + return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); +} + +static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg, + enum nl80211_iftype new_type) +{ + int iftype_num[NUM_NL80211_IFTYPES]; + struct brcmf_cfg80211_vif *pos; + + memset(&iftype_num[0], 0, sizeof(iftype_num)); + list_for_each_entry(pos, &cfg->vif_list, list) + iftype_num[pos->wdev.iftype]++; + + iftype_num[new_type]++; + return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); +} + +static void convert_key_from_CPU(struct brcmf_wsec_key *key, + struct brcmf_wsec_key_le *key_le) +{ + key_le->index = cpu_to_le32(key->index); + key_le->len = cpu_to_le32(key->len); + key_le->algo = cpu_to_le32(key->algo); + key_le->flags = cpu_to_le32(key->flags); + key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi); + key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo); + key_le->iv_initialized = cpu_to_le32(key->iv_initialized); + memcpy(key_le->data, key->data, sizeof(key->data)); + memcpy(key_le->ea, key->ea, sizeof(key->ea)); +} + +static int +send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key) +{ + int err; + struct brcmf_wsec_key_le key_le; + + convert_key_from_CPU(key, &key_le); + + brcmf_netdev_wait_pend8021x(ifp); + + err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le, + sizeof(key_le)); + + if (err) + brcmf_err("wsec_key error (%d)\n", err); + return err; +} + +static s32 +brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) +{ + s32 err; + u32 mode; + + if (enable) + mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY; + else + mode = 0; + + /* Try to set and enable ARP offload feature, this may fail, then it */ + /* is simply not supported and err 0 will be returned */ + err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode); + if (err) { + brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n", + mode, err); + err = 0; + } else { + err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable); + if (err) { + brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n", + enable, err); + err = 0; + } else + brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n", + enable, mode); + } + + return err; +} + +static void +brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) +{ + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + ifp = vif->ifp; + + if ((wdev->iftype == NL80211_IFTYPE_ADHOC) || + (wdev->iftype == NL80211_IFTYPE_AP) || + (wdev->iftype == NL80211_IFTYPE_P2P_GO)) + brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, + ADDR_DIRECT); + else + brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, + ADDR_INDIRECT); +} + +static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) +{ + struct brcmf_mbss_ssid_le mbss_ssid_le; + int bsscfgidx; + int err; + + memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); + bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr); + if (bsscfgidx < 0) + return bsscfgidx; + + mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); + mbss_ssid_le.SSID_len = cpu_to_le32(5); + sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx); + + err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, + sizeof(mbss_ssid_le)); + if (err < 0) + brcmf_err("setting ssid failed %d\n", err); + + return err; +} + +/** + * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS + * + * @wiphy: wiphy device of new interface. + * @name: name of the new interface. + * @flags: not used. + * @params: contains mac address for AP device. + */ +static +struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, + u32 *flags, struct vif_params *params) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_cfg80211_vif *vif; + int err; + + if (brcmf_cfg80211_vif_event_armed(cfg)) + return ERR_PTR(-EBUSY); + + brcmf_dbg(INFO, "Adding vif \"%s\"\n", name); + + vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false); + if (IS_ERR(vif)) + return (struct wireless_dev *)vif; + + brcmf_cfg80211_arm_vif_event(cfg, vif); + + err = brcmf_cfg80211_request_ap_if(ifp); + if (err) { + brcmf_cfg80211_arm_vif_event(cfg, NULL); + goto fail; + } + + /* wait for firmware event */ + err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, + msecs_to_jiffies(1500)); + brcmf_cfg80211_arm_vif_event(cfg, NULL); + if (!err) { + brcmf_err("timeout occurred\n"); + err = -EIO; + goto fail; + } + + /* interface created in firmware */ + ifp = vif->ifp; + if (!ifp) { + brcmf_err("no if pointer provided\n"); + err = -ENOENT; + goto fail; + } + + strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); + err = brcmf_net_attach(ifp, true); + if (err) { + brcmf_err("Registering netdevice failed\n"); + goto fail; + } + + return &ifp->vif->wdev; + +fail: + brcmf_free_vif(vif); + return ERR_PTR(err); +} + +static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) +{ + enum nl80211_iftype iftype; + + iftype = vif->wdev.iftype; + return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO; +} + +static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) +{ + return vif->wdev.iftype == NL80211_IFTYPE_ADHOC; +} + +static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + struct wireless_dev *wdev; + int err; + + brcmf_dbg(TRACE, "enter: %s type %d\n", name, type); + err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type); + if (err) { + brcmf_err("iface validation failed: err=%d\n", err); + return ERR_PTR(err); + } + switch (type) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_MESH_POINT: + return ERR_PTR(-EOPNOTSUPP); + case NL80211_IFTYPE_AP: + wdev = brcmf_ap_add_vif(wiphy, name, flags, params); + if (!IS_ERR(wdev)) + brcmf_cfg80211_update_proto_addr_mode(wdev); + return wdev; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: + wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params); + if (!IS_ERR(wdev)) + brcmf_cfg80211_update_proto_addr_mode(wdev); + return wdev; + case NL80211_IFTYPE_UNSPECIFIED: + default: + return ERR_PTR(-EINVAL); + } +} + +static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc) +{ + if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC)) + brcmf_set_mpc(ifp, mpc); +} + +void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) +{ + s32 err = 0; + + if (check_vif_up(ifp->vif)) { + err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc); + if (err) { + brcmf_err("fail to set mpc\n"); + return; + } + brcmf_dbg(INFO, "MPC : %d\n", mpc); + } +} + +s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp, bool aborted, + bool fw_abort) +{ + struct brcmf_scan_params_le params_le; + struct cfg80211_scan_request *scan_request; + s32 err = 0; + + brcmf_dbg(SCAN, "Enter\n"); + + /* clear scan request, because the FW abort can cause a second call */ + /* to this functon and might cause a double cfg80211_scan_done */ + scan_request = cfg->scan_request; + cfg->scan_request = NULL; + + if (timer_pending(&cfg->escan_timeout)) + del_timer_sync(&cfg->escan_timeout); + + if (fw_abort) { + /* Do a scan abort to stop the driver's scan engine */ + brcmf_dbg(SCAN, "ABORT scan in firmware\n"); + memset(¶ms_le, 0, sizeof(params_le)); + eth_broadcast_addr(params_le.bssid); + params_le.bss_type = DOT11_BSSTYPE_ANY; + params_le.scan_type = 0; + params_le.channel_num = cpu_to_le32(1); + params_le.nprobes = cpu_to_le32(1); + params_le.active_time = cpu_to_le32(-1); + params_le.passive_time = cpu_to_le32(-1); + params_le.home_time = cpu_to_le32(-1); + /* Scan is aborted by setting channel_list[0] to -1 */ + params_le.channel_list[0] = cpu_to_le16(-1); + /* E-Scan (or anyother type) can be aborted by SCAN */ + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, + ¶ms_le, sizeof(params_le)); + if (err) + brcmf_err("Scan abort failed\n"); + } + + brcmf_scan_config_mpc(ifp, 1); + + /* + * e-scan can be initiated by scheduled scan + * which takes precedence. + */ + if (cfg->sched_escan) { + brcmf_dbg(SCAN, "scheduled scan completed\n"); + cfg->sched_escan = false; + if (!aborted) + cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); + } else if (scan_request) { + brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", + aborted ? "Aborted" : "Done"); + cfg80211_scan_done(scan_request, aborted); + } + if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) + brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n"); + + return err; +} + +static +int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct net_device *ndev = wdev->netdev; + + /* vif event pending in firmware */ + if (brcmf_cfg80211_vif_event_armed(cfg)) + return -EBUSY; + + if (ndev) { + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) && + cfg->escan_info.ifp == netdev_priv(ndev)) + brcmf_notify_escan_complete(cfg, netdev_priv(ndev), + true, true); + + brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1); + } + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_MESH_POINT: + return -EOPNOTSUPP; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: + return brcmf_p2p_del_vif(wiphy, wdev); + case NL80211_IFTYPE_UNSPECIFIED: + default: + return -EINVAL; + } + return -EOPNOTSUPP; +} + +static s32 +brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_vif *vif = ifp->vif; + s32 infra = 0; + s32 ap = 0; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type); + + /* WAR: There are a number of p2p interface related problems which + * need to be handled initially (before doing the validate). + * wpa_supplicant tends to do iface changes on p2p device/client/go + * which are not always possible/allowed. However we need to return + * OK otherwise the wpa_supplicant wont start. The situation differs + * on configuration and setup (p2pon=1 module param). The first check + * is to see if the request is a change to station for p2p iface. + */ + if ((type == NL80211_IFTYPE_STATION) && + ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) || + (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) || + (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) { + brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n"); + /* Now depending on whether module param p2pon=1 was used the + * response needs to be either 0 or EOPNOTSUPP. The reason is + * that if p2pon=1 is used, but a newer supplicant is used then + * we should return an error, as this combination wont work. + * In other situations 0 is returned and supplicant will start + * normally. It will give a trace in cfg80211, but it is the + * only way to get it working. Unfortunately this will result + * in situation where we wont support new supplicant in + * combination with module param p2pon=1, but that is the way + * it is. If the user tries this then unloading of driver might + * fail/lock. + */ + if (cfg->p2p.p2pdev_dynamically) + return -EOPNOTSUPP; + else + return 0; + } + err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type); + if (err) { + brcmf_err("iface validation failed: err=%d\n", err); + return err; + } + switch (type) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_WDS: + brcmf_err("type (%d) : currently we do not support this type\n", + type); + return -EOPNOTSUPP; + case NL80211_IFTYPE_ADHOC: + infra = 0; + break; + case NL80211_IFTYPE_STATION: + infra = 1; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + ap = 1; + break; + default: + err = -EINVAL; + goto done; + } + + if (ap) { + if (type == NL80211_IFTYPE_P2P_GO) { + brcmf_dbg(INFO, "IF Type = P2P GO\n"); + err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO); + } + if (!err) { + brcmf_dbg(INFO, "IF Type = AP\n"); + } + } else { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra); + if (err) { + brcmf_err("WLC_SET_INFRA error (%d)\n", err); + err = -EAGAIN; + goto done; + } + brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ? + "Adhoc" : "Infra"); + } + ndev->ieee80211_ptr->iftype = type; + + brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); + +done: + brcmf_dbg(TRACE, "Exit\n"); + + return err; +} + +static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg, + struct brcmf_scan_params_le *params_le, + struct cfg80211_scan_request *request) +{ + u32 n_ssids; + u32 n_channels; + s32 i; + s32 offset; + u16 chanspec; + char *ptr; + struct brcmf_ssid_le ssid_le; + + eth_broadcast_addr(params_le->bssid); + params_le->bss_type = DOT11_BSSTYPE_ANY; + params_le->scan_type = 0; + params_le->channel_num = 0; + params_le->nprobes = cpu_to_le32(-1); + params_le->active_time = cpu_to_le32(-1); + params_le->passive_time = cpu_to_le32(-1); + params_le->home_time = cpu_to_le32(-1); + memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le)); + + /* if request is null exit so it will be all channel broadcast scan */ + if (!request) + return; + + n_ssids = request->n_ssids; + n_channels = request->n_channels; + /* Copy channel array if applicable */ + brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n", + n_channels); + if (n_channels > 0) { + for (i = 0; i < n_channels; i++) { + chanspec = channel_to_chanspec(&cfg->d11inf, + request->channels[i]); + brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n", + request->channels[i]->hw_value, chanspec); + params_le->channel_list[i] = cpu_to_le16(chanspec); + } + } else { + brcmf_dbg(SCAN, "Scanning all channels\n"); + } + /* Copy ssid array if applicable */ + brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids); + if (n_ssids > 0) { + offset = offsetof(struct brcmf_scan_params_le, channel_list) + + n_channels * sizeof(u16); + offset = roundup(offset, sizeof(u32)); + ptr = (char *)params_le + offset; + for (i = 0; i < n_ssids; i++) { + memset(&ssid_le, 0, sizeof(ssid_le)); + ssid_le.SSID_len = + cpu_to_le32(request->ssids[i].ssid_len); + memcpy(ssid_le.SSID, request->ssids[i].ssid, + request->ssids[i].ssid_len); + if (!ssid_le.SSID_len) + brcmf_dbg(SCAN, "%d: Broadcast scan\n", i); + else + brcmf_dbg(SCAN, "%d: scan for %s size =%d\n", + i, ssid_le.SSID, ssid_le.SSID_len); + memcpy(ptr, &ssid_le, sizeof(ssid_le)); + ptr += sizeof(ssid_le); + } + } else { + brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids); + if ((request->ssids) && request->ssids->ssid_len) { + brcmf_dbg(SCAN, "SSID %s len=%d\n", + params_le->ssid_le.SSID, + request->ssids->ssid_len); + params_le->ssid_le.SSID_len = + cpu_to_le32(request->ssids->ssid_len); + memcpy(¶ms_le->ssid_le.SSID, request->ssids->ssid, + request->ssids->ssid_len); + } + } + /* Adding mask to channel numbers */ + params_le->channel_num = + cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) | + (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK)); +} + +static s32 +brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, + struct cfg80211_scan_request *request, u16 action) +{ + s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + + offsetof(struct brcmf_escan_params_le, params_le); + struct brcmf_escan_params_le *params; + s32 err = 0; + + brcmf_dbg(SCAN, "E-SCAN START\n"); + + if (request != NULL) { + /* Allocate space for populating ssids in struct */ + params_size += sizeof(u32) * ((request->n_channels + 1) / 2); + + /* Allocate space for populating ssids in struct */ + params_size += sizeof(struct brcmf_ssid) * request->n_ssids; + } + + params = kzalloc(params_size, GFP_KERNEL); + if (!params) { + err = -ENOMEM; + goto exit; + } + BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN); + brcmf_escan_prep(cfg, ¶ms->params_le, request); + params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); + params->action = cpu_to_le16(action); + params->sync_id = cpu_to_le16(0x1234); + + err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size); + if (err) { + if (err == -EBUSY) + brcmf_dbg(INFO, "system busy : escan canceled\n"); + else + brcmf_err("error (%d)\n", err); + } + + kfree(params); +exit: + return err; +} + +static s32 +brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, + struct brcmf_if *ifp, struct cfg80211_scan_request *request) +{ + s32 err; + u32 passive_scan; + struct brcmf_scan_results *results; + struct escan_info *escan = &cfg->escan_info; + + brcmf_dbg(SCAN, "Enter\n"); + escan->ifp = ifp; + escan->wiphy = wiphy; + escan->escan_state = WL_ESCAN_STATE_SCANNING; + passive_scan = cfg->active_scan ? 0 : 1; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, + passive_scan); + if (err) { + brcmf_err("error (%d)\n", err); + return err; + } + brcmf_scan_config_mpc(ifp, 0); + results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; + results->version = 0; + results->count = 0; + results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE; + + err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START); + if (err) + brcmf_scan_config_mpc(ifp, 1); + return err; +} + +static s32 +brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, + struct cfg80211_scan_request *request, + struct cfg80211_ssid *this_ssid) +{ + struct brcmf_if *ifp = vif->ifp; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct cfg80211_ssid *ssids; + struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int; + u32 passive_scan; + bool escan_req; + bool spec_scan; + s32 err; + u32 SSID_len; + + brcmf_dbg(SCAN, "START ESCAN\n"); + + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); + return -EAGAIN; + } + if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) { + brcmf_err("Scanning being aborted: status (%lu)\n", + cfg->scan_status); + return -EAGAIN; + } + if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { + brcmf_err("Scanning suppressed: status (%lu)\n", + cfg->scan_status); + return -EAGAIN; + } + if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) { + brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state); + return -EAGAIN; + } + + /* If scan req comes for p2p0, send it over primary I/F */ + if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) + vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; + + escan_req = false; + if (request) { + /* scan bss */ + ssids = request->ssids; + escan_req = true; + } else { + /* scan in ibss */ + /* we don't do escan in ibss */ + ssids = this_ssid; + } + + cfg->scan_request = request; + set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + if (escan_req) { + cfg->escan_info.run = brcmf_run_escan; + err = brcmf_p2p_scan_prep(wiphy, request, vif); + if (err) + goto scan_out; + + err = brcmf_do_escan(cfg, wiphy, vif->ifp, request); + if (err) + goto scan_out; + } else { + brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n", + ssids->ssid, ssids->ssid_len); + memset(&sr->ssid_le, 0, sizeof(sr->ssid_le)); + SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len); + sr->ssid_le.SSID_len = cpu_to_le32(0); + spec_scan = false; + if (SSID_len) { + memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len); + sr->ssid_le.SSID_len = cpu_to_le32(SSID_len); + spec_scan = true; + } else + brcmf_dbg(SCAN, "Broadcast scan\n"); + + passive_scan = cfg->active_scan ? 0 : 1; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, + passive_scan); + if (err) { + brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err); + goto scan_out; + } + brcmf_scan_config_mpc(ifp, 0); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, + &sr->ssid_le, sizeof(sr->ssid_le)); + if (err) { + if (err == -EBUSY) + brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n", + sr->ssid_le.SSID); + else + brcmf_err("WLC_SCAN error (%d)\n", err); + + brcmf_scan_config_mpc(ifp, 1); + goto scan_out; + } + } + + /* Arm scan timeout timer */ + mod_timer(&cfg->escan_timeout, jiffies + + WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000); + + return 0; + +scan_out: + clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + cfg->scan_request = NULL; + return err; +} + +static s32 +brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) +{ + struct brcmf_cfg80211_vif *vif; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev); + if (!check_vif_up(vif)) + return -EIO; + + err = brcmf_cfg80211_escan(wiphy, vif, request, NULL); + + if (err) + brcmf_err("scan error (%d)\n", err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold) +{ + s32 err = 0; + + err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh", + rts_threshold); + if (err) + brcmf_err("Error (%d)\n", err); + + return err; +} + +static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold) +{ + s32 err = 0; + + err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh", + frag_threshold); + if (err) + brcmf_err("Error (%d)\n", err); + + return err; +} + +static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l) +{ + s32 err = 0; + u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL); + + err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry); + if (err) { + brcmf_err("cmd (%d) , error (%d)\n", cmd, err); + return err; + } + return err; +} + +static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + if (changed & WIPHY_PARAM_RTS_THRESHOLD && + (cfg->conf->rts_threshold != wiphy->rts_threshold)) { + cfg->conf->rts_threshold = wiphy->rts_threshold; + err = brcmf_set_rts(ndev, cfg->conf->rts_threshold); + if (!err) + goto done; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD && + (cfg->conf->frag_threshold != wiphy->frag_threshold)) { + cfg->conf->frag_threshold = wiphy->frag_threshold; + err = brcmf_set_frag(ndev, cfg->conf->frag_threshold); + if (!err) + goto done; + } + if (changed & WIPHY_PARAM_RETRY_LONG + && (cfg->conf->retry_long != wiphy->retry_long)) { + cfg->conf->retry_long = wiphy->retry_long; + err = brcmf_set_retry(ndev, cfg->conf->retry_long, true); + if (!err) + goto done; + } + if (changed & WIPHY_PARAM_RETRY_SHORT + && (cfg->conf->retry_short != wiphy->retry_short)) { + cfg->conf->retry_short = wiphy->retry_short; + err = brcmf_set_retry(ndev, cfg->conf->retry_short, false); + if (!err) + goto done; + } + +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof) +{ + memset(prof, 0, sizeof(*prof)); +} + +static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e) +{ + u16 reason; + + switch (e->event_code) { + case BRCMF_E_DEAUTH: + case BRCMF_E_DEAUTH_IND: + case BRCMF_E_DISASSOC_IND: + reason = e->reason; + break; + case BRCMF_E_LINK: + default: + reason = 0; + break; + } + return reason; +} + +static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy); + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) { + brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n "); + err = brcmf_fil_cmd_data_set(vif->ifp, + BRCMF_C_DISASSOC, NULL, 0); + if (err) { + brcmf_err("WLC_DISASSOC failed (%d)\n", err); + } + clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state); + cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0, + true, GFP_KERNEL); + + } + clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); + clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); + brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); + brcmf_dbg(TRACE, "Exit\n"); +} + +static s32 +brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_ibss_params *params) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct brcmf_join_params join_params; + size_t join_params_size = 0; + s32 err = 0; + s32 wsec = 0; + s32 bcnprd; + u16 chanspec; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + if (params->ssid) + brcmf_dbg(CONN, "SSID: %s\n", params->ssid); + else { + brcmf_dbg(CONN, "SSID: NULL, Not supported\n"); + return -EOPNOTSUPP; + } + + set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + + if (params->bssid) + brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid); + else + brcmf_dbg(CONN, "No BSSID specified\n"); + + if (params->chandef.chan) + brcmf_dbg(CONN, "channel: %d\n", + params->chandef.chan->center_freq); + else + brcmf_dbg(CONN, "no channel specified\n"); + + if (params->channel_fixed) + brcmf_dbg(CONN, "fixed channel required\n"); + else + brcmf_dbg(CONN, "no fixed channel required\n"); + + if (params->ie && params->ie_len) + brcmf_dbg(CONN, "ie len: %d\n", params->ie_len); + else + brcmf_dbg(CONN, "no ie specified\n"); + + if (params->beacon_interval) + brcmf_dbg(CONN, "beacon interval: %d\n", + params->beacon_interval); + else + brcmf_dbg(CONN, "no beacon interval specified\n"); + + if (params->basic_rates) + brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates); + else + brcmf_dbg(CONN, "no basic rates specified\n"); + + if (params->privacy) + brcmf_dbg(CONN, "privacy required\n"); + else + brcmf_dbg(CONN, "no privacy required\n"); + + /* Configure Privacy for starter */ + if (params->privacy) + wsec |= WEP_ENABLED; + + err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec); + if (err) { + brcmf_err("wsec failed (%d)\n", err); + goto done; + } + + /* Configure Beacon Interval for starter */ + if (params->beacon_interval) + bcnprd = params->beacon_interval; + else + bcnprd = 100; + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd); + if (err) { + brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err); + goto done; + } + + /* Configure required join parameter */ + memset(&join_params, 0, sizeof(struct brcmf_join_params)); + + /* SSID */ + profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32); + memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len); + memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len); + join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); + join_params_size = sizeof(join_params.ssid_le); + + /* BSSID */ + if (params->bssid) { + memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN); + join_params_size = sizeof(join_params.ssid_le) + + BRCMF_ASSOC_PARAMS_FIXED_SIZE; + memcpy(profile->bssid, params->bssid, ETH_ALEN); + } else { + eth_broadcast_addr(join_params.params_le.bssid); + eth_zero_addr(profile->bssid); + } + + /* Channel */ + if (params->chandef.chan) { + u32 target_channel; + + cfg->channel = + ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); + if (params->channel_fixed) { + /* adding chanspec */ + chanspec = chandef_to_chanspec(&cfg->d11inf, + ¶ms->chandef); + join_params.params_le.chanspec_list[0] = + cpu_to_le16(chanspec); + join_params.params_le.chanspec_num = cpu_to_le32(1); + join_params_size += sizeof(join_params.params_le); + } + + /* set channel for starter */ + target_channel = cfg->channel; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL, + target_channel); + if (err) { + brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err); + goto done; + } + } else + cfg->channel = 0; + + cfg->ibss_starter = false; + + + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, + &join_params, join_params_size); + if (err) { + brcmf_err("WLC_SET_SSID failed (%d)\n", err); + goto done; + } + +done: + if (err) + clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING); + + brcmf_dbg(TRACE, "Exit\n"); + + return 0; +} + +static s32 brcmf_set_wpa_version(struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); + struct brcmf_cfg80211_security *sec; + s32 val = 0; + s32 err = 0; + + if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) + val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; + else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) + val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; + else + val = WPA_AUTH_DISABLED; + brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val); + if (err) { + brcmf_err("set wpa_auth failed (%d)\n", err); + return err; + } + sec = &profile->sec; + sec->wpa_versions = sme->crypto.wpa_versions; + return err; +} + +static s32 brcmf_set_auth_type(struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); + struct brcmf_cfg80211_security *sec; + s32 val = 0; + s32 err = 0; + + switch (sme->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + val = 0; + brcmf_dbg(CONN, "open system\n"); + break; + case NL80211_AUTHTYPE_SHARED_KEY: + val = 1; + brcmf_dbg(CONN, "shared key\n"); + break; + case NL80211_AUTHTYPE_AUTOMATIC: + val = 2; + brcmf_dbg(CONN, "automatic\n"); + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + brcmf_dbg(CONN, "network eap\n"); + default: + val = 2; + brcmf_err("invalid auth type (%d)\n", sme->auth_type); + break; + } + + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val); + if (err) { + brcmf_err("set auth failed (%d)\n", err); + return err; + } + sec = &profile->sec; + sec->auth_type = sme->auth_type; + return err; +} + +static s32 +brcmf_set_wsec_mode(struct net_device *ndev, + struct cfg80211_connect_params *sme, bool mfp) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); + struct brcmf_cfg80211_security *sec; + s32 pval = 0; + s32 gval = 0; + s32 wsec; + s32 err = 0; + + if (sme->crypto.n_ciphers_pairwise) { + switch (sme->crypto.ciphers_pairwise[0]) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + pval = WEP_ENABLED; + break; + case WLAN_CIPHER_SUITE_TKIP: + pval = TKIP_ENABLED; + break; + case WLAN_CIPHER_SUITE_CCMP: + pval = AES_ENABLED; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + pval = AES_ENABLED; + break; + default: + brcmf_err("invalid cipher pairwise (%d)\n", + sme->crypto.ciphers_pairwise[0]); + return -EINVAL; + } + } + if (sme->crypto.cipher_group) { + switch (sme->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + gval = WEP_ENABLED; + break; + case WLAN_CIPHER_SUITE_TKIP: + gval = TKIP_ENABLED; + break; + case WLAN_CIPHER_SUITE_CCMP: + gval = AES_ENABLED; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + gval = AES_ENABLED; + break; + default: + brcmf_err("invalid cipher group (%d)\n", + sme->crypto.cipher_group); + return -EINVAL; + } + } + + brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval); + /* In case of privacy, but no security and WPS then simulate */ + /* setting AES. WPS-2.0 allows no security */ + if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval && + sme->privacy) + pval = AES_ENABLED; + + if (mfp) + wsec = pval | gval | MFP_CAPABLE; + else + wsec = pval | gval; + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec); + if (err) { + brcmf_err("error (%d)\n", err); + return err; + } + + sec = &profile->sec; + sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; + sec->cipher_group = sme->crypto.cipher_group; + + return err; +} + +static s32 +brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); + struct brcmf_cfg80211_security *sec; + s32 val = 0; + s32 err = 0; + + if (sme->crypto.n_akm_suites) { + err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), + "wpa_auth", &val); + if (err) { + brcmf_err("could not get wpa_auth (%d)\n", err); + return err; + } + if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA_AUTH_PSK; + break; + default: + brcmf_err("invalid cipher group (%d)\n", + sme->crypto.cipher_group); + return -EINVAL; + } + } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA2_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA2_AUTH_PSK; + break; + default: + brcmf_err("invalid cipher group (%d)\n", + sme->crypto.cipher_group); + return -EINVAL; + } + } + + brcmf_dbg(CONN, "setting wpa_auth to %d\n", val); + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), + "wpa_auth", val); + if (err) { + brcmf_err("could not set wpa_auth (%d)\n", err); + return err; + } + } + sec = &profile->sec; + sec->wpa_auth = sme->crypto.akm_suites[0]; + + return err; +} + +static s32 +brcmf_set_sharedkey(struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); + struct brcmf_cfg80211_security *sec; + struct brcmf_wsec_key key; + s32 val; + s32 err = 0; + + brcmf_dbg(CONN, "key len (%d)\n", sme->key_len); + + if (sme->key_len == 0) + return 0; + + sec = &profile->sec; + brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n", + sec->wpa_versions, sec->cipher_pairwise); + + if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)) + return 0; + + if (!(sec->cipher_pairwise & + (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104))) + return 0; + + memset(&key, 0, sizeof(key)); + key.len = (u32) sme->key_len; + key.index = (u32) sme->key_idx; + if (key.len > sizeof(key.data)) { + brcmf_err("Too long key length (%u)\n", key.len); + return -EINVAL; + } + memcpy(key.data, sme->key, key.len); + key.flags = BRCMF_PRIMARY_KEY; + switch (sec->cipher_pairwise) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + break; + default: + brcmf_err("Invalid algorithm (%d)\n", + sme->crypto.ciphers_pairwise[0]); + return -EINVAL; + } + /* Set the new key/index */ + brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n", + key.len, key.index, key.algo); + brcmf_dbg(CONN, "key \"%s\"\n", key.data); + err = send_key_to_dongle(netdev_priv(ndev), &key); + if (err) + return err; + + if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { + brcmf_dbg(CONN, "set auth_type to shared key\n"); + val = WL_AUTH_SHARED_KEY; /* shared key */ + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val); + if (err) + brcmf_err("set auth failed (%d)\n", err); + } + return err; +} + +static +enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp, + enum nl80211_auth_type type) +{ + if (type == NL80211_AUTHTYPE_AUTOMATIC && + brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) { + brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n"); + type = NL80211_AUTHTYPE_OPEN_SYSTEM; + } + return type; +} + +static s32 +brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct ieee80211_channel *chan = sme->channel; + struct brcmf_join_params join_params; + size_t join_params_size; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; + const void *ie; + u32 ie_len; + struct brcmf_ext_join_params_le *ext_join_params; + u16 chanspec; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + if (!sme->ssid) { + brcmf_err("Invalid ssid\n"); + return -EOPNOTSUPP; + } + + if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) { + /* A normal (non P2P) connection request setup. */ + ie = NULL; + ie_len = 0; + /* find the WPA_IE */ + wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len); + if (wpa_ie) { + ie = wpa_ie; + ie_len = wpa_ie->len + TLV_HDR_LEN; + } else { + /* find the RSN_IE */ + rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, + sme->ie_len, + WLAN_EID_RSN); + if (rsn_ie) { + ie = rsn_ie; + ie_len = rsn_ie->len + TLV_HDR_LEN; + } + } + brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len); + } + + err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG, + sme->ie, sme->ie_len); + if (err) + brcmf_err("Set Assoc REQ IE Failed\n"); + else + brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n"); + + set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + + if (chan) { + cfg->channel = + ieee80211_frequency_to_channel(chan->center_freq); + chanspec = channel_to_chanspec(&cfg->d11inf, chan); + brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n", + cfg->channel, chan->center_freq, chanspec); + } else { + cfg->channel = 0; + chanspec = 0; + } + + brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len); + + err = brcmf_set_wpa_version(ndev, sme); + if (err) { + brcmf_err("wl_set_wpa_version failed (%d)\n", err); + goto done; + } + + sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type); + err = brcmf_set_auth_type(ndev, sme); + if (err) { + brcmf_err("wl_set_auth_type failed (%d)\n", err); + goto done; + } + + err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED); + if (err) { + brcmf_err("wl_set_set_cipher failed (%d)\n", err); + goto done; + } + + err = brcmf_set_key_mgmt(ndev, sme); + if (err) { + brcmf_err("wl_set_key_mgmt failed (%d)\n", err); + goto done; + } + + err = brcmf_set_sharedkey(ndev, sme); + if (err) { + brcmf_err("brcmf_set_sharedkey failed (%d)\n", err); + goto done; + } + + profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID), + (u32)sme->ssid_len); + memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len); + if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { + profile->ssid.SSID[profile->ssid.SSID_len] = 0; + brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID, + profile->ssid.SSID_len); + } + + /* Join with specific BSSID and cached SSID + * If SSID is zero join based on BSSID only + */ + join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) + + offsetof(struct brcmf_assoc_params_le, chanspec_list); + if (cfg->channel) + join_params_size += sizeof(u16); + ext_join_params = kzalloc(join_params_size, GFP_KERNEL); + if (ext_join_params == NULL) { + err = -ENOMEM; + goto done; + } + ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); + memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, + profile->ssid.SSID_len); + + /* Set up join scan parameters */ + ext_join_params->scan_le.scan_type = -1; + ext_join_params->scan_le.home_time = cpu_to_le32(-1); + + if (sme->bssid) + memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN); + else + eth_broadcast_addr(ext_join_params->assoc_le.bssid); + + if (cfg->channel) { + ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1); + + ext_join_params->assoc_le.chanspec_list[0] = + cpu_to_le16(chanspec); + /* Increase dwell time to receive probe response or detect + * beacon from target AP at a noisy air only during connect + * command. + */ + ext_join_params->scan_le.active_time = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); + ext_join_params->scan_le.passive_time = + cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); + /* To sync with presence period of VSDB GO send probe request + * more frequently. Probe request will be stopped when it gets + * probe response from target AP/GO. + */ + ext_join_params->scan_le.nprobes = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS / + BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS); + } else { + ext_join_params->scan_le.active_time = cpu_to_le32(-1); + ext_join_params->scan_le.passive_time = cpu_to_le32(-1); + ext_join_params->scan_le.nprobes = cpu_to_le32(-1); + } + + err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params, + join_params_size); + kfree(ext_join_params); + if (!err) + /* This is it. join command worked, we are done */ + goto done; + + /* join command failed, fallback to set ssid */ + memset(&join_params, 0, sizeof(join_params)); + join_params_size = sizeof(join_params.ssid_le); + + memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len); + join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); + + if (sme->bssid) + memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN); + else + eth_broadcast_addr(join_params.params_le.bssid); + + if (cfg->channel) { + join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec); + join_params.params_le.chanspec_num = cpu_to_le32(1); + join_params_size += sizeof(join_params.params_le); + } + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, + &join_params, join_params_size); + if (err) + brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err); + +done: + if (err) + clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, + u16 reason_code) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct brcmf_scb_val_le scbval; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code); + if (!check_vif_up(ifp->vif)) + return -EIO; + + clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL); + + memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); + scbval.val = cpu_to_le32(reason_code); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC, + &scbval, sizeof(scbval)); + if (err) + brcmf_err("error (%d)\n", err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, s32 mbm) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + s32 disable; + u32 qdbm = 127; + + brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm); + if (!check_vif_up(ifp->vif)) + return -EIO; + + switch (type) { + case NL80211_TX_POWER_AUTOMATIC: + break; + case NL80211_TX_POWER_LIMITED: + case NL80211_TX_POWER_FIXED: + if (mbm < 0) { + brcmf_err("TX_POWER_FIXED - dbm is negative\n"); + err = -EINVAL; + goto done; + } + qdbm = MBM_TO_DBM(4 * mbm); + if (qdbm > 127) + qdbm = 127; + qdbm |= WL_TXPWR_OVERRIDE; + break; + default: + brcmf_err("Unsupported type %d\n", type); + err = -EINVAL; + goto done; + } + /* Make sure radio is off or on as far as software is concerned */ + disable = WL_RADIO_SW_DISABLE << 16; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable); + if (err) + brcmf_err("WLC_SET_RADIO error (%d)\n", err); + + err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm); + if (err) + brcmf_err("qtxpower error (%d)\n", err); + +done: + brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE); + return err; +} + +static s32 +brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, + s32 *dbm) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 qdbm = 0; + s32 err; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm); + if (err) { + brcmf_err("error (%d)\n", err); + goto done; + } + *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4; + +done: + brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm); + return err; +} + +static s32 +brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_idx, bool unicast, bool multicast) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + u32 index; + u32 wsec; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(CONN, "key index (%d)\n", key_idx); + if (!check_vif_up(ifp->vif)) + return -EIO; + + err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); + if (err) { + brcmf_err("WLC_GET_WSEC error (%d)\n", err); + goto done; + } + + if (wsec & WEP_ENABLED) { + /* Just select a new current key */ + index = key_idx; + err = brcmf_fil_cmd_int_set(ifp, + BRCMF_C_SET_KEY_PRIMARY, index); + if (err) + brcmf_err("error (%d)\n", err); + } +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, + u8 key_idx, const u8 *mac_addr, struct key_params *params) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_wsec_key key; + s32 err = 0; + u8 keybuf[8]; + + memset(&key, 0, sizeof(key)); + key.index = (u32) key_idx; + /* Instead of bcast for ea address for default wep keys, + driver needs it to be Null */ + if (!is_multicast_ether_addr(mac_addr)) + memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN); + key.len = (u32) params->key_len; + /* check for key index change */ + if (key.len == 0) { + /* key delete */ + err = send_key_to_dongle(ifp, &key); + if (err) + brcmf_err("key delete error (%d)\n", err); + } else { + if (key.len > sizeof(key.data)) { + brcmf_err("Invalid key length (%d)\n", key.len); + return -EINVAL; + } + + brcmf_dbg(CONN, "Setting the key index %d\n", key.index); + memcpy(key.data, params->key, key.len); + + if (!brcmf_is_apmode(ifp->vif) && + (params->cipher == WLAN_CIPHER_SUITE_TKIP)) { + brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); + memcpy(keybuf, &key.data[24], sizeof(keybuf)); + memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); + memcpy(&key.data[16], keybuf, sizeof(keybuf)); + } + + /* if IW_ENCODE_EXT_RX_SEQ_VALID set */ + if (params->seq && params->seq_len == 6) { + /* rx iv */ + u8 *ivptr; + ivptr = (u8 *) params->seq; + key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | + (ivptr[3] << 8) | ivptr[2]; + key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; + key.iv_initialized = true; + } + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); + break; + case WLAN_CIPHER_SUITE_TKIP: + key.algo = CRYPTO_ALGO_TKIP; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + key.algo = CRYPTO_ALGO_AES_CCM; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); + break; + case WLAN_CIPHER_SUITE_CCMP: + key.algo = CRYPTO_ALGO_AES_CCM; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); + break; + default: + brcmf_err("Invalid cipher (0x%x)\n", params->cipher); + return -EINVAL; + } + err = send_key_to_dongle(ifp, &key); + if (err) + brcmf_err("wsec_key error (%d)\n", err); + } + return err; +} + +static s32 +brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_idx, bool pairwise, const u8 *mac_addr, + struct key_params *params) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_wsec_key *key; + s32 val; + s32 wsec; + s32 err = 0; + u8 keybuf[8]; + + brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(CONN, "key index (%d)\n", key_idx); + if (!check_vif_up(ifp->vif)) + return -EIO; + + if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { + /* we ignore this key index in this case */ + brcmf_err("invalid key index (%d)\n", key_idx); + return -EINVAL; + } + + if (mac_addr && + (params->cipher != WLAN_CIPHER_SUITE_WEP40) && + (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { + brcmf_dbg(TRACE, "Exit"); + return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params); + } + + key = &ifp->vif->profile.key[key_idx]; + memset(key, 0, sizeof(*key)); + + if (params->key_len > sizeof(key->data)) { + brcmf_err("Too long key length (%u)\n", params->key_len); + err = -EINVAL; + goto done; + } + key->len = params->key_len; + key->index = key_idx; + + memcpy(key->data, params->key, key->len); + + key->flags = BRCMF_PRIMARY_KEY; + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + key->algo = CRYPTO_ALGO_WEP1; + val = WEP_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); + break; + case WLAN_CIPHER_SUITE_WEP104: + key->algo = CRYPTO_ALGO_WEP128; + val = WEP_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); + break; + case WLAN_CIPHER_SUITE_TKIP: + if (!brcmf_is_apmode(ifp->vif)) { + brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); + memcpy(keybuf, &key->data[24], sizeof(keybuf)); + memcpy(&key->data[24], &key->data[16], sizeof(keybuf)); + memcpy(&key->data[16], keybuf, sizeof(keybuf)); + } + key->algo = CRYPTO_ALGO_TKIP; + val = TKIP_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + key->algo = CRYPTO_ALGO_AES_CCM; + val = AES_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); + break; + case WLAN_CIPHER_SUITE_CCMP: + key->algo = CRYPTO_ALGO_AES_CCM; + val = AES_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); + break; + default: + brcmf_err("Invalid cipher (0x%x)\n", params->cipher); + err = -EINVAL; + goto done; + } + + err = send_key_to_dongle(ifp, key); + if (err) + goto done; + + err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); + if (err) { + brcmf_err("get wsec error (%d)\n", err); + goto done; + } + wsec |= val; + err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); + if (err) { + brcmf_err("set wsec error (%d)\n", err); + goto done; + } + +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_idx, bool pairwise, const u8 *mac_addr) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_wsec_key key; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { + /* we ignore this key index in this case */ + return -EINVAL; + } + + memset(&key, 0, sizeof(key)); + + key.index = (u32) key_idx; + key.flags = BRCMF_PRIMARY_KEY; + key.algo = CRYPTO_ALGO_OFF; + + brcmf_dbg(CONN, "key index (%d)\n", key_idx); + + /* Set the new key/index */ + err = send_key_to_dongle(ifp, &key); + + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, + void (*callback) (void *cookie, struct key_params * params)) +{ + struct key_params params; + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct brcmf_cfg80211_security *sec; + s32 wsec; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(CONN, "key index (%d)\n", key_idx); + if (!check_vif_up(ifp->vif)) + return -EIO; + + memset(¶ms, 0, sizeof(params)); + + err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); + if (err) { + brcmf_err("WLC_GET_WSEC error (%d)\n", err); + /* Ignore this error, may happen during DISASSOC */ + err = -EAGAIN; + goto done; + } + if (wsec & WEP_ENABLED) { + sec = &profile->sec; + if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { + params.cipher = WLAN_CIPHER_SUITE_WEP40; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); + } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) { + params.cipher = WLAN_CIPHER_SUITE_WEP104; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); + } + } else if (wsec & TKIP_ENABLED) { + params.cipher = WLAN_CIPHER_SUITE_TKIP; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); + } else if (wsec & AES_ENABLED) { + params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); + } else { + brcmf_err("Invalid algo (0x%x)\n", wsec); + err = -EINVAL; + goto done; + } + callback(cookie, ¶ms); + +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, + struct net_device *ndev, u8 key_idx) +{ + brcmf_dbg(INFO, "Not supported\n"); + + return -EOPNOTSUPP; +} + +static void +brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp) +{ + s32 err; + u8 key_idx; + struct brcmf_wsec_key *key; + s32 wsec; + + for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) { + key = &ifp->vif->profile.key[key_idx]; + if ((key->algo == CRYPTO_ALGO_WEP1) || + (key->algo == CRYPTO_ALGO_WEP128)) + break; + } + if (key_idx == BRCMF_MAX_DEFAULT_KEYS) + return; + + err = send_key_to_dongle(ifp, key); + if (err) { + brcmf_err("Setting WEP key failed (%d)\n", err); + return; + } + err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); + if (err) { + brcmf_err("get wsec error (%d)\n", err); + return; + } + wsec |= WEP_ENABLED; + err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); + if (err) + brcmf_err("set wsec error (%d)\n", err); +} + +static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si) +{ + struct nl80211_sta_flag_update *sfu; + + brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags); + si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS); + sfu = &si->sta_flags; + sfu->mask = BIT(NL80211_STA_FLAG_WME) | + BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_ASSOCIATED) | + BIT(NL80211_STA_FLAG_AUTHORIZED); + if (fw_sta_flags & BRCMF_STA_WME) + sfu->set |= BIT(NL80211_STA_FLAG_WME); + if (fw_sta_flags & BRCMF_STA_AUTHE) + sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (fw_sta_flags & BRCMF_STA_ASSOC) + sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED); + if (fw_sta_flags & BRCMF_STA_AUTHO) + sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED); +} + +static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si) +{ + struct { + __le32 len; + struct brcmf_bss_info_le bss_le; + } *buf; + u16 capability; + int err; + + buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); + if (!buf) + return; + + buf->len = cpu_to_le32(WL_BSS_INFO_MAX); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf, + WL_BSS_INFO_MAX); + if (err) { + brcmf_err("Failed to get bss info (%d)\n", err); + return; + } + si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); + si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period); + si->bss_param.dtim_period = buf->bss_le.dtim_period; + capability = le16_to_cpu(buf->bss_le.capability); + if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT) + si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; + if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE) + si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) + si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; +} + +static s32 +brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, + const u8 *mac, struct station_info *sinfo) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err = 0; + struct brcmf_sta_info_le sta_info_le; + u32 sta_flags; + u32 is_tdls_peer; + s32 total_rssi; + s32 count_rssi; + u32 i; + + brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac); + if (!check_vif_up(ifp->vif)) + return -EIO; + + memset(&sta_info_le, 0, sizeof(sta_info_le)); + memcpy(&sta_info_le, mac, ETH_ALEN); + err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info", + &sta_info_le, + sizeof(sta_info_le)); + is_tdls_peer = !err; + if (err) { + err = brcmf_fil_iovar_data_get(ifp, "sta_info", + &sta_info_le, + sizeof(sta_info_le)); + if (err < 0) { + brcmf_err("GET STA INFO failed, %d\n", err); + goto done; + } + } + brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver)); + sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME); + sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000; + sta_flags = le32_to_cpu(sta_info_le.flags); + brcmf_convert_sta_flags(sta_flags, sinfo); + sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER); + if (is_tdls_peer) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); + else + sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); + if (sta_flags & BRCMF_STA_ASSOC) { + sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME); + sinfo->connected_time = le32_to_cpu(sta_info_le.in); + brcmf_fill_bss_param(ifp, sinfo); + } + if (sta_flags & BRCMF_STA_SCBSTATS) { + sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED); + sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures); + sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS); + sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts); + sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts); + sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS); + sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts); + sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts); + if (sinfo->tx_packets) { + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); + sinfo->txrate.legacy = + le32_to_cpu(sta_info_le.tx_rate) / 100; + } + if (sinfo->rx_packets) { + sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); + sinfo->rxrate.legacy = + le32_to_cpu(sta_info_le.rx_rate) / 100; + } + if (le16_to_cpu(sta_info_le.ver) >= 4) { + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES); + sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes); + sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES); + sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes); + } + total_rssi = 0; + count_rssi = 0; + for (i = 0; i < BRCMF_ANT_MAX; i++) { + if (sta_info_le.rssi[i]) { + sinfo->chain_signal_avg[count_rssi] = + sta_info_le.rssi[i]; + sinfo->chain_signal[count_rssi] = + sta_info_le.rssi[i]; + total_rssi += sta_info_le.rssi[i]; + count_rssi++; + } + } + if (count_rssi) { + sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL); + sinfo->chains = count_rssi; + + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); + total_rssi /= count_rssi; + sinfo->signal = total_rssi; + } + } +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static int +brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev, + int idx, u8 *mac, struct station_info *sinfo) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + + brcmf_dbg(TRACE, "Enter, idx %d\n", idx); + + if (idx == 0) { + cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST, + &cfg->assoclist, + sizeof(cfg->assoclist)); + if (err) { + brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n", + err); + cfg->assoclist.count = 0; + return -EOPNOTSUPP; + } + } + if (idx < le32_to_cpu(cfg->assoclist.count)) { + memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN); + return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo); + } + return -ENOENT; +} + +static s32 +brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, + bool enabled, s32 timeout) +{ + s32 pm; + s32 err = 0; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter\n"); + + /* + * Powersave enable/disable request is coming from the + * cfg80211 even before the interface is up. In that + * scenario, driver will be storing the power save + * preference in cfg struct to apply this to + * FW later while initializing the dongle + */ + cfg->pwr_save = enabled; + if (!check_vif_up(ifp->vif)) { + + brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n"); + goto done; + } + + pm = enabled ? PM_FAST : PM_OFF; + /* Do not enable the power save after assoc if it is a p2p interface */ + if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) { + brcmf_dbg(INFO, "Do not enable power save for P2P clients\n"); + pm = PM_OFF; + } + brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled")); + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm); + if (err) { + if (err == -ENODEV) + brcmf_err("net_device is not ready yet\n"); + else + brcmf_err("error (%d)\n", err); + } +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, + struct brcmf_bss_info_le *bi) +{ + struct wiphy *wiphy = cfg_to_wiphy(cfg); + struct ieee80211_channel *notify_channel; + struct cfg80211_bss *bss; + struct ieee80211_supported_band *band; + struct brcmu_chan ch; + u16 channel; + u32 freq; + u16 notify_capability; + u16 notify_interval; + u8 *notify_ie; + size_t notify_ielen; + s32 notify_signal; + + if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) { + brcmf_err("Bss info is larger than buffer. Discarding\n"); + return 0; + } + + if (!bi->ctl_ch) { + ch.chspec = le16_to_cpu(bi->chanspec); + cfg->d11inf.decchspec(&ch); + bi->ctl_ch = ch.chnum; + } + channel = bi->ctl_ch; + + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + + freq = ieee80211_channel_to_frequency(channel, band->band); + notify_channel = ieee80211_get_channel(wiphy, freq); + + notify_capability = le16_to_cpu(bi->capability); + notify_interval = le16_to_cpu(bi->beacon_period); + notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); + notify_ielen = le32_to_cpu(bi->ie_length); + notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; + + brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID); + brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq); + brcmf_dbg(CONN, "Capability: %X\n", notify_capability); + brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval); + brcmf_dbg(CONN, "Signal: %d\n", notify_signal); + + bss = cfg80211_inform_bss(wiphy, notify_channel, + CFG80211_BSS_FTYPE_UNKNOWN, + (const u8 *)bi->BSSID, + 0, notify_capability, + notify_interval, notify_ie, + notify_ielen, notify_signal, + GFP_KERNEL); + + if (!bss) + return -ENOMEM; + + cfg80211_put_bss(wiphy, bss); + + return 0; +} + +static struct brcmf_bss_info_le * +next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss) +{ + if (bss == NULL) + return list->bss_info_le; + return (struct brcmf_bss_info_le *)((unsigned long)bss + + le32_to_cpu(bss->length)); +} + +static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_scan_results *bss_list; + struct brcmf_bss_info_le *bi = NULL; /* must be initialized */ + s32 err = 0; + int i; + + bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; + if (bss_list->count != 0 && + bss_list->version != BRCMF_BSS_INFO_VERSION) { + brcmf_err("Version %d != WL_BSS_INFO_VERSION\n", + bss_list->version); + return -EOPNOTSUPP; + } + brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count); + for (i = 0; i < bss_list->count; i++) { + bi = next_bss_le(bss_list, bi); + err = brcmf_inform_single_bss(cfg, bi); + if (err) + break; + } + return err; +} + +static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg, + struct net_device *ndev, const u8 *bssid) +{ + struct wiphy *wiphy = cfg_to_wiphy(cfg); + struct ieee80211_channel *notify_channel; + struct brcmf_bss_info_le *bi = NULL; + struct ieee80211_supported_band *band; + struct cfg80211_bss *bss; + struct brcmu_chan ch; + u8 *buf = NULL; + s32 err = 0; + u32 freq; + u16 notify_capability; + u16 notify_interval; + u8 *notify_ie; + size_t notify_ielen; + s32 notify_signal; + + brcmf_dbg(TRACE, "Enter\n"); + + buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); + if (buf == NULL) { + err = -ENOMEM; + goto CleanUp; + } + + *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); + + err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO, + buf, WL_BSS_INFO_MAX); + if (err) { + brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err); + goto CleanUp; + } + + bi = (struct brcmf_bss_info_le *)(buf + 4); + + ch.chspec = le16_to_cpu(bi->chanspec); + cfg->d11inf.decchspec(&ch); + + if (ch.band == BRCMU_CHAN_BAND_2G) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + + freq = ieee80211_channel_to_frequency(ch.chnum, band->band); + notify_channel = ieee80211_get_channel(wiphy, freq); + + notify_capability = le16_to_cpu(bi->capability); + notify_interval = le16_to_cpu(bi->beacon_period); + notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); + notify_ielen = le32_to_cpu(bi->ie_length); + notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; + + brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq); + brcmf_dbg(CONN, "capability: %X\n", notify_capability); + brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval); + brcmf_dbg(CONN, "signal: %d\n", notify_signal); + + bss = cfg80211_inform_bss(wiphy, notify_channel, + CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0, + notify_capability, notify_interval, + notify_ie, notify_ielen, notify_signal, + GFP_KERNEL); + + if (!bss) { + err = -ENOMEM; + goto CleanUp; + } + + cfg80211_put_bss(wiphy, bss); + +CleanUp: + + kfree(buf); + + brcmf_dbg(TRACE, "Exit\n"); + + return err; +} + +static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev); + struct brcmf_bss_info_le *bi; + struct brcmf_ssid *ssid; + const struct brcmf_tlv *tim; + u16 beacon_interval; + u8 dtim_period; + size_t ie_len; + u8 *ie; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + if (brcmf_is_ibssmode(ifp->vif)) + return err; + + ssid = &profile->ssid; + + *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, + cfg->extra_buf, WL_EXTRA_BUF_MAX); + if (err) { + brcmf_err("Could not get bss info %d\n", err); + goto update_bss_info_out; + } + + bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4); + err = brcmf_inform_single_bss(cfg, bi); + if (err) + goto update_bss_info_out; + + ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset); + ie_len = le32_to_cpu(bi->ie_length); + beacon_interval = le16_to_cpu(bi->beacon_period); + + tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM); + if (tim) + dtim_period = tim->data[1]; + else { + /* + * active scan was done so we could not get dtim + * information out of probe response. + * so we speficially query dtim information to dongle. + */ + u32 var; + err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var); + if (err) { + brcmf_err("wl dtim_assoc failed (%d)\n", err); + goto update_bss_info_out; + } + dtim_period = (u8)var; + } + +update_bss_info_out: + brcmf_dbg(TRACE, "Exit"); + return err; +} + +void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg) +{ + struct escan_info *escan = &cfg->escan_info; + + set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); + if (cfg->scan_request) { + escan->escan_state = WL_ESCAN_STATE_IDLE; + brcmf_notify_escan_complete(cfg, escan->ifp, true, true); + } + clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); +} + +static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work) +{ + struct brcmf_cfg80211_info *cfg = + container_of(work, struct brcmf_cfg80211_info, + escan_timeout_work); + + brcmf_inform_bss(cfg); + brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true); +} + +static void brcmf_escan_timeout(unsigned long data) +{ + struct brcmf_cfg80211_info *cfg = + (struct brcmf_cfg80211_info *)data; + + if (cfg->scan_request) { + brcmf_err("timer expired\n"); + schedule_work(&cfg->escan_timeout_work); + } +} + +static s32 +brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg, + struct brcmf_bss_info_le *bss, + struct brcmf_bss_info_le *bss_info_le) +{ + struct brcmu_chan ch_bss, ch_bss_info_le; + + ch_bss.chspec = le16_to_cpu(bss->chanspec); + cfg->d11inf.decchspec(&ch_bss); + ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec); + cfg->d11inf.decchspec(&ch_bss_info_le); + + if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) && + ch_bss.band == ch_bss_info_le.band && + bss_info_le->SSID_len == bss->SSID_len && + !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) { + if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == + (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) { + s16 bss_rssi = le16_to_cpu(bss->RSSI); + s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI); + + /* preserve max RSSI if the measurements are + * both on-channel or both off-channel + */ + if (bss_info_rssi > bss_rssi) + bss->RSSI = bss_info_le->RSSI; + } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) && + (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) { + /* preserve the on-channel rssi measurement + * if the new measurement is off channel + */ + bss->RSSI = bss_info_le->RSSI; + bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL; + } + return 1; + } + return 0; +} + +static s32 +brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + s32 status; + struct brcmf_escan_result_le *escan_result_le; + struct brcmf_bss_info_le *bss_info_le; + struct brcmf_bss_info_le *bss = NULL; + u32 bi_length; + struct brcmf_scan_results *list; + u32 i; + bool aborted; + + status = e->status; + + if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx); + return -EPERM; + } + + if (status == BRCMF_E_STATUS_PARTIAL) { + brcmf_dbg(SCAN, "ESCAN Partial result\n"); + escan_result_le = (struct brcmf_escan_result_le *) data; + if (!escan_result_le) { + brcmf_err("Invalid escan result (NULL pointer)\n"); + goto exit; + } + if (le16_to_cpu(escan_result_le->bss_count) != 1) { + brcmf_err("Invalid bss_count %d: ignoring\n", + escan_result_le->bss_count); + goto exit; + } + bss_info_le = &escan_result_le->bss_info_le; + + if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le)) + goto exit; + + if (!cfg->scan_request) { + brcmf_dbg(SCAN, "result without cfg80211 request\n"); + goto exit; + } + + bi_length = le32_to_cpu(bss_info_le->length); + if (bi_length != (le32_to_cpu(escan_result_le->buflen) - + WL_ESCAN_RESULTS_FIXED_SIZE)) { + brcmf_err("Invalid bss_info length %d: ignoring\n", + bi_length); + goto exit; + } + + if (!(cfg_to_wiphy(cfg)->interface_modes & + BIT(NL80211_IFTYPE_ADHOC))) { + if (le16_to_cpu(bss_info_le->capability) & + WLAN_CAPABILITY_IBSS) { + brcmf_err("Ignoring IBSS result\n"); + goto exit; + } + } + + list = (struct brcmf_scan_results *) + cfg->escan_info.escan_buf; + if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) { + brcmf_err("Buffer is too small: ignoring\n"); + goto exit; + } + + for (i = 0; i < list->count; i++) { + bss = bss ? (struct brcmf_bss_info_le *) + ((unsigned char *)bss + + le32_to_cpu(bss->length)) : list->bss_info_le; + if (brcmf_compare_update_same_bss(cfg, bss, + bss_info_le)) + goto exit; + } + memcpy(&(cfg->escan_info.escan_buf[list->buflen]), + bss_info_le, bi_length); + list->version = le32_to_cpu(bss_info_le->version); + list->buflen += bi_length; + list->count++; + } else { + cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + if (brcmf_p2p_scan_finding_common_channel(cfg, NULL)) + goto exit; + if (cfg->scan_request) { + brcmf_inform_bss(cfg); + aborted = status != BRCMF_E_STATUS_SUCCESS; + brcmf_notify_escan_complete(cfg, ifp, aborted, false); + } else + brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n", + status); + } +exit: + return 0; +} + +static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) +{ + brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT, + brcmf_cfg80211_escan_handler); + cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + /* Init scan_timeout timer */ + init_timer(&cfg->escan_timeout); + cfg->escan_timeout.data = (unsigned long) cfg; + cfg->escan_timeout.function = brcmf_escan_timeout; + INIT_WORK(&cfg->escan_timeout_work, + brcmf_cfg80211_escan_timeout_worker); +} + +static __always_inline void brcmf_delay(u32 ms) +{ + if (ms < 1000 / HZ) { + cond_resched(); + mdelay(ms); + } else { + msleep(ms); + } +} + +static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], + u8 *pattern, u32 patternsize, u8 *mask, + u32 packet_offset) +{ + struct brcmf_fil_wowl_pattern_le *filter; + u32 masksize; + u32 patternoffset; + u8 *buf; + u32 bufsize; + s32 ret; + + masksize = (patternsize + 7) / 8; + patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize; + + bufsize = sizeof(*filter) + patternsize + masksize; + buf = kzalloc(bufsize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + filter = (struct brcmf_fil_wowl_pattern_le *)buf; + + memcpy(filter->cmd, cmd, 4); + filter->masksize = cpu_to_le32(masksize); + filter->offset = cpu_to_le32(packet_offset); + filter->patternoffset = cpu_to_le32(patternoffset); + filter->patternsize = cpu_to_le32(patternsize); + filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP); + + if ((mask) && (masksize)) + memcpy(buf + sizeof(*filter), mask, masksize); + if ((pattern) && (patternsize)) + memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize); + + ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize); + + kfree(buf); + return ret; +} + +static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter\n"); + + if (cfg->wowl_enabled) { + brcmf_configure_arp_offload(ifp, true); + brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, + cfg->pre_wowl_pmmode); + brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); + brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); + cfg->wowl_enabled = false; + } + return 0; +} + +static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp, + struct cfg80211_wowlan *wowl) +{ + u32 wowl_config; + u32 i; + + brcmf_dbg(TRACE, "Suspend, wowl config.\n"); + + brcmf_configure_arp_offload(ifp, false); + brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode); + brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); + + wowl_config = 0; + if (wowl->disconnect) + wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR; + if (wowl->magic_pkt) + wowl_config |= BRCMF_WOWL_MAGIC; + if ((wowl->patterns) && (wowl->n_patterns)) { + wowl_config |= BRCMF_WOWL_NET; + for (i = 0; i < wowl->n_patterns; i++) { + brcmf_config_wowl_pattern(ifp, "add", + (u8 *)wowl->patterns[i].pattern, + wowl->patterns[i].pattern_len, + (u8 *)wowl->patterns[i].mask, + wowl->patterns[i].pkt_offset); + } + } + brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); + brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); + brcmf_bus_wowl_config(cfg->pub->bus_if, true); + cfg->wowl_enabled = true; +} + +static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wowl) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_vif *vif; + + brcmf_dbg(TRACE, "Enter\n"); + + /* if the primary net_device is not READY there is nothing + * we can do but pray resume goes smoothly. + */ + if (!check_vif_up(ifp->vif)) + goto exit; + + /* end any scanning */ + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) + brcmf_abort_scanning(cfg); + + if (wowl == NULL) { + brcmf_bus_wowl_config(cfg->pub->bus_if, false); + list_for_each_entry(vif, &cfg->vif_list, list) { + if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) + continue; + /* While going to suspend if associated with AP + * disassociate from AP to save power while system is + * in suspended state + */ + brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED); + /* Make sure WPA_Supplicant receives all the event + * generated due to DISASSOC call to the fw to keep + * the state fw and WPA_Supplicant state consistent + */ + brcmf_delay(500); + } + /* Configure MPC */ + brcmf_set_mpc(ifp, 1); + + } else { + /* Configure WOWL paramaters */ + brcmf_configure_wowl(cfg, ifp, wowl); + } + +exit: + brcmf_dbg(TRACE, "Exit\n"); + /* clear any scanning activity */ + cfg->scan_status = 0; + return 0; +} + +static __used s32 +brcmf_update_pmklist(struct net_device *ndev, + struct brcmf_cfg80211_pmk_list *pmk_list, s32 err) +{ + int i, j; + u32 pmkid_len; + + pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid); + + brcmf_dbg(CONN, "No of elements %d\n", pmkid_len); + for (i = 0; i < pmkid_len; i++) { + brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i, + &pmk_list->pmkids.pmkid[i].BSSID); + for (j = 0; j < WLAN_PMKID_LEN; j++) + brcmf_dbg(CONN, "%02x\n", + pmk_list->pmkids.pmkid[i].PMKID[j]); + } + + if (!err) + brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info", + (char *)pmk_list, sizeof(*pmk_list)); + + return err; +} + +static s32 +brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_pmksa *pmksa) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct pmkid_list *pmkids = &cfg->pmk_list->pmkids; + s32 err = 0; + u32 pmkid_len, i; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + pmkid_len = le32_to_cpu(pmkids->npmkid); + for (i = 0; i < pmkid_len; i++) + if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN)) + break; + if (i < WL_NUM_PMKIDS_MAX) { + memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN); + memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); + if (i == pmkid_len) { + pmkid_len++; + pmkids->npmkid = cpu_to_le32(pmkid_len); + } + } else + err = -EINVAL; + + brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n", + pmkids->pmkid[pmkid_len].BSSID); + for (i = 0; i < WLAN_PMKID_LEN; i++) + brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]); + + err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_pmksa *pmksa) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct pmkid_list pmkid; + s32 err = 0; + u32 pmkid_len, i; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN); + memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); + + brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n", + &pmkid.pmkid[0].BSSID); + for (i = 0; i < WLAN_PMKID_LEN; i++) + brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]); + + pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid); + for (i = 0; i < pmkid_len; i++) + if (!memcmp + (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID, + ETH_ALEN)) + break; + + if ((pmkid_len > 0) + && (i < pmkid_len)) { + memset(&cfg->pmk_list->pmkids.pmkid[i], 0, + sizeof(struct pmkid)); + for (; i < (pmkid_len - 1); i++) { + memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID, + &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID, + ETH_ALEN); + memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID, + &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID, + WLAN_PMKID_LEN); + } + cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1); + } else + err = -EINVAL; + + err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; + +} + +static s32 +brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list)); + err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; + +} + +/* + * PFN result doesn't have all the info which are + * required by the supplicant + * (For e.g IEs) Do a target Escan so that sched scan results are reported + * via wl_inform_single_bss in the required format. Escan does require the + * scan request in the form of cfg80211_scan_request. For timebeing, create + * cfg80211_scan_request one out of the received PNO event. + */ +static s32 +brcmf_notify_sched_scan_results(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_pno_net_info_le *netinfo, *netinfo_start; + struct cfg80211_scan_request *request = NULL; + struct cfg80211_ssid *ssid = NULL; + struct ieee80211_channel *channel = NULL; + struct wiphy *wiphy = cfg_to_wiphy(cfg); + int err = 0; + int channel_req = 0; + int band = 0; + struct brcmf_pno_scanresults_le *pfn_result; + u32 result_count; + u32 status; + + brcmf_dbg(SCAN, "Enter\n"); + + if (e->event_code == BRCMF_E_PFN_NET_LOST) { + brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n"); + return 0; + } + + pfn_result = (struct brcmf_pno_scanresults_le *)data; + result_count = le32_to_cpu(pfn_result->count); + status = le32_to_cpu(pfn_result->status); + + /* + * PFN event is limited to fit 512 bytes so we may get + * multiple NET_FOUND events. For now place a warning here. + */ + WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); + brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count); + if (result_count > 0) { + int i; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL); + channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL); + if (!request || !ssid || !channel) { + err = -ENOMEM; + goto out_err; + } + + request->wiphy = wiphy; + data += sizeof(struct brcmf_pno_scanresults_le); + netinfo_start = (struct brcmf_pno_net_info_le *)data; + + for (i = 0; i < result_count; i++) { + netinfo = &netinfo_start[i]; + if (!netinfo) { + brcmf_err("Invalid netinfo ptr. index: %d\n", + i); + err = -EINVAL; + goto out_err; + } + + brcmf_dbg(SCAN, "SSID:%s Channel:%d\n", + netinfo->SSID, netinfo->channel); + memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); + ssid[i].ssid_len = netinfo->SSID_len; + request->n_ssids++; + + channel_req = netinfo->channel; + if (channel_req <= CH_MAX_2G_CHANNEL) + band = NL80211_BAND_2GHZ; + else + band = NL80211_BAND_5GHZ; + channel[i].center_freq = + ieee80211_channel_to_frequency(channel_req, + band); + channel[i].band = band; + channel[i].flags |= IEEE80211_CHAN_NO_HT40; + request->channels[i] = &channel[i]; + request->n_channels++; + } + + /* assign parsed ssid array */ + if (request->n_ssids) + request->ssids = &ssid[0]; + + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + /* Abort any on-going scan */ + brcmf_abort_scanning(cfg); + } + + 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); + goto out_err; + } + cfg->sched_escan = true; + cfg->scan_request = request; + } else { + brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); + goto out_err; + } + + kfree(ssid); + kfree(channel); + kfree(request); + return 0; + +out_err: + kfree(ssid); + kfree(channel); + kfree(request); + cfg80211_sched_scan_stopped(wiphy); + return err; +} + +static int brcmf_dev_pno_clean(struct net_device *ndev) +{ + int ret; + + /* Disable pfn */ + ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0); + if (ret == 0) { + /* clear pfn */ + ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear", + NULL, 0); + } + if (ret < 0) + brcmf_err("failed code %d\n", ret); + + return ret; +} + +static int brcmf_dev_pno_config(struct net_device *ndev) +{ + struct brcmf_pno_param_le pfn_param; + + memset(&pfn_param, 0, sizeof(pfn_param)); + pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); + + /* set extra pno params */ + pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat = BRCMF_PNO_REPEAT; + pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; + + /* set up pno scan fr */ + pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); + + return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set", + &pfn_param, sizeof(pfn_param)); +} + +static int +brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_sched_scan_request *request) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_pno_net_param_le pfn; + int i; + int ret = 0; + + brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", + request->n_match_sets, request->n_ssids); + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); + return -EAGAIN; + } + if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { + brcmf_err("Scanning suppressed: status (%lu)\n", + cfg->scan_status); + return -EAGAIN; + } + + if (!request->n_ssids || !request->n_match_sets) { + brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", + request->n_ssids); + return -EINVAL; + } + + if (request->n_ssids > 0) { + for (i = 0; i < request->n_ssids; i++) { + /* Active scan req for ssids */ + brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n", + request->ssids[i].ssid); + + /* + * match_set ssids is a supert set of n_ssid list, + * so we need not add these set seperately. + */ + } + } + + if (request->n_match_sets > 0) { + /* clean up everything */ + ret = brcmf_dev_pno_clean(ndev); + if (ret < 0) { + brcmf_err("failed error=%d\n", ret); + return ret; + } + + /* configure pno */ + ret = brcmf_dev_pno_config(ndev); + if (ret < 0) { + brcmf_err("PNO setup failed!! ret=%d\n", ret); + return -EINVAL; + } + + /* configure each match set */ + for (i = 0; i < request->n_match_sets; i++) { + struct cfg80211_ssid *ssid; + u32 ssid_len; + + ssid = &request->match_sets[i].ssid; + ssid_len = ssid->ssid_len; + + if (!ssid_len) { + brcmf_err("skip broadcast ssid\n"); + continue; + } + pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); + pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); + pfn.wsec = cpu_to_le32(0); + pfn.infra = cpu_to_le32(1); + pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); + pfn.ssid.SSID_len = cpu_to_le32(ssid_len); + memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len); + ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, + sizeof(pfn)); + brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", + ret == 0 ? "set" : "failed", ssid->ssid); + } + /* Enable the PNO */ + if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) { + brcmf_err("PNO enable failed!! ret=%d\n", ret); + return -EINVAL; + } + } else { + return -EINVAL; + } + + return 0; +} + +static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *ndev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + + brcmf_dbg(SCAN, "enter\n"); + brcmf_dev_pno_clean(ndev); + if (cfg->sched_escan) + brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true); + return 0; +} + +static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp) +{ + s32 err; + + /* set auth */ + err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0); + if (err < 0) { + brcmf_err("auth error %d\n", err); + return err; + } + /* set wsec */ + err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0); + if (err < 0) { + brcmf_err("wsec error %d\n", err); + return err; + } + /* set upper-layer auth */ + err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE); + if (err < 0) { + brcmf_err("wpa_auth error %d\n", err); + return err; + } + + return 0; +} + +static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) +{ + if (is_rsn_ie) + return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0); + + return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0); +} + +static s32 +brcmf_configure_wpaie(struct brcmf_if *ifp, + const struct brcmf_vs_tlv *wpa_ie, + bool is_rsn_ie) +{ + u32 auth = 0; /* d11 open authentication */ + u16 count; + s32 err = 0; + s32 len = 0; + u32 i; + u32 wsec; + u32 pval = 0; + u32 gval = 0; + u32 wpa_auth = 0; + u32 offset; + u8 *data; + u16 rsn_cap; + u32 wme_bss_disable; + + brcmf_dbg(TRACE, "Enter\n"); + if (wpa_ie == NULL) + goto exit; + + len = wpa_ie->len + TLV_HDR_LEN; + data = (u8 *)wpa_ie; + offset = TLV_HDR_LEN; + if (!is_rsn_ie) + offset += VS_IE_FIXED_HDR_LEN; + else + offset += WPA_IE_VERSION_LEN; + + /* check for multicast cipher suite */ + if (offset + WPA_IE_MIN_OUI_LEN > len) { + err = -EINVAL; + brcmf_err("no multicast cipher suite\n"); + goto exit; + } + + if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + err = -EINVAL; + brcmf_err("ivalid OUI\n"); + goto exit; + } + offset += TLV_OUI_LEN; + + /* pick up multicast cipher */ + switch (data[offset]) { + case WPA_CIPHER_NONE: + gval = 0; + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + gval = WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + gval = TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + gval = AES_ENABLED; + break; + default: + err = -EINVAL; + brcmf_err("Invalid multi cast cipher info\n"); + goto exit; + } + + offset++; + /* walk thru unicast cipher list and pick up what we recognize */ + count = data[offset] + (data[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN; + /* Check for unicast suite(s) */ + if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) { + err = -EINVAL; + brcmf_err("no unicast cipher suite\n"); + goto exit; + } + for (i = 0; i < count; i++) { + if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + err = -EINVAL; + brcmf_err("ivalid OUI\n"); + goto exit; + } + offset += TLV_OUI_LEN; + switch (data[offset]) { + case WPA_CIPHER_NONE: + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + pval |= WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + pval |= TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + pval |= AES_ENABLED; + break; + default: + brcmf_err("Ivalid unicast security info\n"); + } + offset++; + } + /* walk thru auth management suite list and pick up what we recognize */ + count = data[offset] + (data[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN; + /* Check for auth key management suite(s) */ + if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) { + err = -EINVAL; + brcmf_err("no auth key mgmt suite\n"); + goto exit; + } + for (i = 0; i < count; i++) { + if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + err = -EINVAL; + brcmf_err("ivalid OUI\n"); + goto exit; + } + offset += TLV_OUI_LEN; + switch (data[offset]) { + case RSN_AKM_NONE: + brcmf_dbg(TRACE, "RSN_AKM_NONE\n"); + wpa_auth |= WPA_AUTH_NONE; + break; + case RSN_AKM_UNSPECIFIED: + brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n"); + is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) : + (wpa_auth |= WPA_AUTH_UNSPECIFIED); + break; + case RSN_AKM_PSK: + brcmf_dbg(TRACE, "RSN_AKM_PSK\n"); + is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) : + (wpa_auth |= WPA_AUTH_PSK); + break; + default: + brcmf_err("Ivalid key mgmt info\n"); + } + offset++; + } + + if (is_rsn_ie) { + wme_bss_disable = 1; + if ((offset + RSN_CAP_LEN) <= len) { + rsn_cap = data[offset] + (data[offset + 1] << 8); + if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK) + wme_bss_disable = 0; + } + /* set wme_bss_disable to sync RSN Capabilities */ + err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable", + wme_bss_disable); + if (err < 0) { + brcmf_err("wme_bss_disable error %d\n", err); + goto exit; + } + } + /* FOR WPS , set SES_OW_ENABLED */ + wsec = (pval | gval | SES_OW_ENABLED); + + /* set auth */ + err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth); + if (err < 0) { + brcmf_err("auth error %d\n", err); + goto exit; + } + /* set wsec */ + err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); + if (err < 0) { + brcmf_err("wsec error %d\n", err); + goto exit; + } + /* set upper-layer auth */ + err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth); + if (err < 0) { + brcmf_err("wpa_auth error %d\n", err); + goto exit; + } + +exit: + return err; +} + +static s32 +brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len, + struct parsed_vndr_ies *vndr_ies) +{ + struct brcmf_vs_tlv *vndrie; + struct brcmf_tlv *ie; + struct parsed_vndr_ie_info *parsed_info; + s32 remaining_len; + + remaining_len = (s32)vndr_ie_len; + memset(vndr_ies, 0, sizeof(*vndr_ies)); + + ie = (struct brcmf_tlv *)vndr_ie_buf; + while (ie) { + if (ie->id != WLAN_EID_VENDOR_SPECIFIC) + goto next; + vndrie = (struct brcmf_vs_tlv *)ie; + /* len should be bigger than OUI length + one */ + if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) { + brcmf_err("invalid vndr ie. length is too small %d\n", + vndrie->len); + goto next; + } + /* if wpa or wme ie, do not add ie */ + if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) && + ((vndrie->oui_type == WPA_OUI_TYPE) || + (vndrie->oui_type == WME_OUI_TYPE))) { + brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n"); + goto next; + } + + parsed_info = &vndr_ies->ie_info[vndr_ies->count]; + + /* save vndr ie information */ + parsed_info->ie_ptr = (char *)vndrie; + parsed_info->ie_len = vndrie->len + TLV_HDR_LEN; + memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie)); + + vndr_ies->count++; + + brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n", + parsed_info->vndrie.oui[0], + parsed_info->vndrie.oui[1], + parsed_info->vndrie.oui[2], + parsed_info->vndrie.oui_type); + + if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT) + break; +next: + remaining_len -= (ie->len + TLV_HDR_LEN); + if (remaining_len <= TLV_HDR_LEN) + ie = NULL; + else + ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len + + TLV_HDR_LEN); + } + return 0; +} + +static u32 +brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd) +{ + + strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1); + iebuf[VNDR_IE_CMD_LEN - 1] = '\0'; + + put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]); + + put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]); + + memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len); + + return ie_len + VNDR_IE_HDR_SIZE; +} + +s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, + const u8 *vndr_ie_buf, u32 vndr_ie_len) +{ + struct brcmf_if *ifp; + struct vif_saved_ie *saved_ie; + s32 err = 0; + u8 *iovar_ie_buf; + u8 *curr_ie_buf; + u8 *mgmt_ie_buf = NULL; + int mgmt_ie_buf_len; + u32 *mgmt_ie_len; + u32 del_add_ie_buf_len = 0; + u32 total_ie_buf_len = 0; + u32 parsed_ie_buf_len = 0; + struct parsed_vndr_ies old_vndr_ies; + struct parsed_vndr_ies new_vndr_ies; + struct parsed_vndr_ie_info *vndrie_info; + s32 i; + u8 *ptr; + int remained_buf_len; + + if (!vif) + return -ENODEV; + ifp = vif->ifp; + saved_ie = &vif->saved_ie; + + brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag); + iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); + if (!iovar_ie_buf) + return -ENOMEM; + curr_ie_buf = iovar_ie_buf; + switch (pktflag) { + case BRCMF_VNDR_IE_PRBREQ_FLAG: + mgmt_ie_buf = saved_ie->probe_req_ie; + mgmt_ie_len = &saved_ie->probe_req_ie_len; + mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie); + break; + case BRCMF_VNDR_IE_PRBRSP_FLAG: + mgmt_ie_buf = saved_ie->probe_res_ie; + mgmt_ie_len = &saved_ie->probe_res_ie_len; + mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie); + break; + case BRCMF_VNDR_IE_BEACON_FLAG: + mgmt_ie_buf = saved_ie->beacon_ie; + mgmt_ie_len = &saved_ie->beacon_ie_len; + mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie); + break; + case BRCMF_VNDR_IE_ASSOCREQ_FLAG: + mgmt_ie_buf = saved_ie->assoc_req_ie; + mgmt_ie_len = &saved_ie->assoc_req_ie_len; + mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie); + break; + default: + err = -EPERM; + brcmf_err("not suitable type\n"); + goto exit; + } + + if (vndr_ie_len > mgmt_ie_buf_len) { + err = -ENOMEM; + brcmf_err("extra IE size too big\n"); + goto exit; + } + + /* parse and save new vndr_ie in curr_ie_buff before comparing it */ + if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) { + ptr = curr_ie_buf; + brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies); + for (i = 0; i < new_vndr_ies.count; i++) { + vndrie_info = &new_vndr_ies.ie_info[i]; + memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr, + vndrie_info->ie_len); + parsed_ie_buf_len += vndrie_info->ie_len; + } + } + + if (mgmt_ie_buf && *mgmt_ie_len) { + if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) && + (memcmp(mgmt_ie_buf, curr_ie_buf, + parsed_ie_buf_len) == 0)) { + brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n"); + goto exit; + } + + /* parse old vndr_ie */ + brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies); + + /* make a command to delete old ie */ + for (i = 0; i < old_vndr_ies.count; i++) { + vndrie_info = &old_vndr_ies.ie_info[i]; + + brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n", + vndrie_info->vndrie.id, + vndrie_info->vndrie.len, + vndrie_info->vndrie.oui[0], + vndrie_info->vndrie.oui[1], + vndrie_info->vndrie.oui[2]); + + del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag, + vndrie_info->ie_ptr, + vndrie_info->ie_len, + "del"); + curr_ie_buf += del_add_ie_buf_len; + total_ie_buf_len += del_add_ie_buf_len; + } + } + + *mgmt_ie_len = 0; + /* Add if there is any extra IE */ + if (mgmt_ie_buf && parsed_ie_buf_len) { + ptr = mgmt_ie_buf; + + remained_buf_len = mgmt_ie_buf_len; + + /* make a command to add new ie */ + for (i = 0; i < new_vndr_ies.count; i++) { + vndrie_info = &new_vndr_ies.ie_info[i]; + + /* verify remained buf size before copy data */ + if (remained_buf_len < (vndrie_info->vndrie.len + + VNDR_IE_VSIE_OFFSET)) { + brcmf_err("no space in mgmt_ie_buf: len left %d", + remained_buf_len); + break; + } + remained_buf_len -= (vndrie_info->ie_len + + VNDR_IE_VSIE_OFFSET); + + brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n", + vndrie_info->vndrie.id, + vndrie_info->vndrie.len, + vndrie_info->vndrie.oui[0], + vndrie_info->vndrie.oui[1], + vndrie_info->vndrie.oui[2]); + + del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag, + vndrie_info->ie_ptr, + vndrie_info->ie_len, + "add"); + + /* save the parsed IE in wl struct */ + memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr, + vndrie_info->ie_len); + *mgmt_ie_len += vndrie_info->ie_len; + + curr_ie_buf += del_add_ie_buf_len; + total_ie_buf_len += del_add_ie_buf_len; + } + } + if (total_ie_buf_len) { + err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf, + total_ie_buf_len); + if (err) + brcmf_err("vndr ie set error : %d\n", err); + } + +exit: + kfree(iovar_ie_buf); + return err; +} + +s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif) +{ + s32 pktflags[] = { + BRCMF_VNDR_IE_PRBREQ_FLAG, + BRCMF_VNDR_IE_PRBRSP_FLAG, + BRCMF_VNDR_IE_BEACON_FLAG + }; + int i; + + for (i = 0; i < ARRAY_SIZE(pktflags); i++) + brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0); + + memset(&vif->saved_ie, 0, sizeof(vif->saved_ie)); + return 0; +} + +static s32 +brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif, + struct cfg80211_beacon_data *beacon) +{ + s32 err; + + /* Set Beacon IEs to FW */ + err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG, + beacon->tail, beacon->tail_len); + if (err) { + brcmf_err("Set Beacon IE Failed\n"); + return err; + } + brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n"); + + /* Set Probe Response IEs to FW */ + err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG, + beacon->proberesp_ies, + beacon->proberesp_ies_len); + if (err) + brcmf_err("Set Probe Resp IE Failed\n"); + else + brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n"); + + return err; +} + +static s32 +brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_ap_settings *settings) +{ + s32 ie_offset; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + const struct brcmf_tlv *ssid_ie; + const struct brcmf_tlv *country_ie; + struct brcmf_ssid_le ssid_le; + s32 err = -EPERM; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; + struct brcmf_join_params join_params; + enum nl80211_iftype dev_role; + struct brcmf_fil_bss_enable_le bss_enable; + u16 chanspec; + bool mbss; + int is_11d; + + brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", + settings->chandef.chan->hw_value, + settings->chandef.center_freq1, settings->chandef.width, + settings->beacon_interval, settings->dtim_period); + brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", + settings->ssid, settings->ssid_len, settings->auth_type, + settings->inactivity_timeout); + dev_role = ifp->vif->wdev.iftype; + mbss = ifp->vif->mbss; + + /* store current 11d setting */ + brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d); + country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, + settings->beacon.tail_len, + WLAN_EID_COUNTRY); + is_11d = country_ie ? 1 : 0; + + memset(&ssid_le, 0, sizeof(ssid_le)); + if (settings->ssid == NULL || settings->ssid_len == 0) { + ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; + ssid_ie = brcmf_parse_tlvs( + (u8 *)&settings->beacon.head[ie_offset], + settings->beacon.head_len - ie_offset, + WLAN_EID_SSID); + if (!ssid_ie) + return -EINVAL; + + memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len); + ssid_le.SSID_len = cpu_to_le32(ssid_ie->len); + brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID); + } else { + memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len); + ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len); + } + + if (!mbss) { + brcmf_set_mpc(ifp, 0); + brcmf_configure_arp_offload(ifp, false); + } + + /* find the RSN_IE */ + rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, + settings->beacon.tail_len, WLAN_EID_RSN); + + /* find the WPA_IE */ + wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail, + settings->beacon.tail_len); + + if ((wpa_ie != NULL || rsn_ie != NULL)) { + brcmf_dbg(TRACE, "WPA(2) IE is found\n"); + if (wpa_ie != NULL) { + /* WPA IE */ + err = brcmf_configure_wpaie(ifp, wpa_ie, false); + if (err < 0) + goto exit; + } else { + struct brcmf_vs_tlv *tmp_ie; + + tmp_ie = (struct brcmf_vs_tlv *)rsn_ie; + + /* RSN IE */ + err = brcmf_configure_wpaie(ifp, tmp_ie, true); + if (err < 0) + goto exit; + } + } else { + brcmf_dbg(TRACE, "No WPA(2) IEs found\n"); + brcmf_configure_opensecurity(ifp); + } + + brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); + + if (!mbss) { + chanspec = chandef_to_chanspec(&cfg->d11inf, + &settings->chandef); + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); + if (err < 0) { + brcmf_err("Set Channel failed: chspec=%d, %d\n", + chanspec, err); + goto exit; + } + + if (is_11d != ifp->vif->is_11d) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, + is_11d); + if (err < 0) { + brcmf_err("Regulatory Set Error, %d\n", err); + goto exit; + } + } + if (settings->beacon_interval) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, + settings->beacon_interval); + if (err < 0) { + brcmf_err("Beacon Interval Set Error, %d\n", + err); + goto exit; + } + } + if (settings->dtim_period) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD, + settings->dtim_period); + if (err < 0) { + brcmf_err("DTIM Interval Set Error, %d\n", err); + goto exit; + } + } + + if (dev_role == NL80211_IFTYPE_AP) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + if (err < 0) { + brcmf_err("BRCMF_C_DOWN error %d\n", err); + goto exit; + } + brcmf_fil_iovar_int_set(ifp, "apsta", 0); + } + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1); + if (err < 0) { + brcmf_err("SET INFRA error %d\n", err); + goto exit; + } + } else if (WARN_ON(is_11d != ifp->vif->is_11d)) { + /* Multiple-BSS should use same 11d configuration */ + err = -EINVAL; + goto exit; + } + if (dev_role == NL80211_IFTYPE_AP) { + if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss)) + brcmf_fil_iovar_int_set(ifp, "mbss", 1); + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1); + if (err < 0) { + brcmf_err("setting AP mode failed %d\n", err); + goto exit; + } + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); + if (err < 0) { + brcmf_err("BRCMF_C_UP error (%d)\n", err); + goto exit; + } + /* On DOWN the firmware removes the WEP keys, reconfigure + * them if they were set. + */ + brcmf_cfg80211_reconfigure_wep(ifp); + + memset(&join_params, 0, sizeof(join_params)); + /* join parameters starts with ssid */ + memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le)); + /* create softap */ + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, + &join_params, sizeof(join_params)); + if (err < 0) { + brcmf_err("SET SSID error (%d)\n", err); + goto exit; + } + brcmf_dbg(TRACE, "AP mode configuration complete\n"); + } else { + err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le, + sizeof(ssid_le)); + if (err < 0) { + brcmf_err("setting ssid failed %d\n", err); + goto exit; + } + bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); + bss_enable.enable = cpu_to_le32(1); + err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, + sizeof(bss_enable)); + if (err < 0) { + brcmf_err("bss_enable config failed %d\n", err); + goto exit; + } + + brcmf_dbg(TRACE, "GO mode configuration complete\n"); + } + set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); + brcmf_net_setcarrier(ifp, true); + +exit: + if ((err) && (!mbss)) { + brcmf_set_mpc(ifp, 1); + brcmf_configure_arp_offload(ifp, true); + } + return err; +} + +static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + struct brcmf_fil_bss_enable_le bss_enable; + struct brcmf_join_params join_params; + + brcmf_dbg(TRACE, "Enter\n"); + + if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) { + /* Due to most likely deauths outstanding we sleep */ + /* first to make sure they get processed by fw. */ + msleep(400); + + if (ifp->vif->mbss) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + return err; + } + + memset(&join_params, 0, sizeof(join_params)); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, + &join_params, sizeof(join_params)); + if (err < 0) + brcmf_err("SET SSID error (%d)\n", err); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + if (err < 0) + brcmf_err("BRCMF_C_DOWN error %d\n", err); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); + if (err < 0) + brcmf_err("setting AP mode failed %d\n", err); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0); + if (err < 0) + brcmf_err("setting INFRA mode failed %d\n", err); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) + brcmf_fil_iovar_int_set(ifp, "mbss", 0); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, + ifp->vif->is_11d); + if (err < 0) + brcmf_err("restoring REGULATORY setting failed %d\n", + err); + /* Bring device back up so it can be used again */ + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); + if (err < 0) + brcmf_err("BRCMF_C_UP error %d\n", err); + } else { + bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); + bss_enable.enable = cpu_to_le32(0); + err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, + sizeof(bss_enable)); + if (err < 0) + brcmf_err("bss_enable config failed %d\n", err); + } + brcmf_set_mpc(ifp, 1); + brcmf_configure_arp_offload(ifp, true); + clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); + brcmf_net_setcarrier(ifp, false); + + return err; +} + +static s32 +brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_beacon_data *info) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + + brcmf_dbg(TRACE, "Enter\n"); + + err = brcmf_config_ap_mgmt_ie(ifp->vif, info); + + return err; +} + +static int +brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, + struct station_del_parameters *params) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_scb_val_le scbval; + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + + if (!params->mac) + return -EFAULT; + + brcmf_dbg(TRACE, "Enter %pM\n", params->mac); + + if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) + ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; + if (!check_vif_up(ifp->vif)) + return -EIO; + + memcpy(&scbval.ea, params->mac, ETH_ALEN); + scbval.val = cpu_to_le32(params->reason_code); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON, + &scbval, sizeof(scbval)); + if (err) + brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static int +brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev, + const u8 *mac, struct station_parameters *params) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + + brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac, + params->sta_flags_mask, params->sta_flags_set); + + /* Ignore all 00 MAC */ + if (is_zero_ether_addr(mac)) + return 0; + + if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) + return 0; + + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE, + (void *)mac, ETH_ALEN); + else + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE, + (void *)mac, ETH_ALEN); + if (err < 0) + brcmf_err("Setting SCB (de-)authorize failed, %d\n", err); + + return err; +} + +static void +brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, + u16 frame_type, bool reg) +{ + struct brcmf_cfg80211_vif *vif; + u16 mgmt_type; + + brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg); + + mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + if (reg) + vif->mgmt_rx_reg |= BIT(mgmt_type); + else + vif->mgmt_rx_reg &= ~BIT(mgmt_type); +} + + +static int +brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, u64 *cookie) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct ieee80211_channel *chan = params->chan; + const u8 *buf = params->buf; + size_t len = params->len; + const struct ieee80211_mgmt *mgmt; + struct brcmf_cfg80211_vif *vif; + s32 err = 0; + s32 ie_offset; + s32 ie_len; + struct brcmf_fil_action_frame_le *action_frame; + struct brcmf_fil_af_params_le *af_params; + bool ack; + s32 chan_nr; + u32 freq; + + brcmf_dbg(TRACE, "Enter\n"); + + *cookie = 0; + + mgmt = (const struct ieee80211_mgmt *)buf; + + if (!ieee80211_is_mgmt(mgmt->frame_control)) { + brcmf_err("Driver only allows MGMT packet type\n"); + return -EPERM; + } + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + + if (ieee80211_is_probe_resp(mgmt->frame_control)) { + /* Right now the only reason to get a probe response */ + /* is for p2p listen response or for p2p GO from */ + /* wpa_supplicant. Unfortunately the probe is send */ + /* on primary ndev, while dongle wants it on the p2p */ + /* vif. Since this is only reason for a probe */ + /* response to be sent, the vif is taken from cfg. */ + /* If ever desired to send proberesp for non p2p */ + /* response then data should be checked for */ + /* "DIRECT-". Note in future supplicant will take */ + /* dedicated p2p wdev to do this and then this 'hack'*/ + /* is not needed anymore. */ + ie_offset = DOT11_MGMT_HDR_LEN + + DOT11_BCN_PRB_FIXED_LEN; + ie_len = len - ie_offset; + if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) + vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + err = brcmf_vif_set_mgmt_ie(vif, + BRCMF_VNDR_IE_PRBRSP_FLAG, + &buf[ie_offset], + ie_len); + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, + GFP_KERNEL); + } else if (ieee80211_is_action(mgmt->frame_control)) { + af_params = kzalloc(sizeof(*af_params), GFP_KERNEL); + if (af_params == NULL) { + brcmf_err("unable to allocate frame\n"); + err = -ENOMEM; + goto exit; + } + action_frame = &af_params->action_frame; + /* Add the packet Id */ + action_frame->packet_id = cpu_to_le32(*cookie); + /* Add BSSID */ + memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN); + memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); + /* Add the length exepted for 802.11 header */ + action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN); + /* Add the channel. Use the one specified as parameter if any or + * the current one (got from the firmware) otherwise + */ + if (chan) + freq = chan->center_freq; + else + brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, + &freq); + chan_nr = ieee80211_frequency_to_channel(freq); + af_params->channel = cpu_to_le32(chan_nr); + + memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], + le16_to_cpu(action_frame->len)); + + brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n", + *cookie, le16_to_cpu(action_frame->len), freq); + + ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg), + af_params); + + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, + GFP_KERNEL); + kfree(af_params); + } else { + brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control); + brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len); + } + +exit: + return err; +} + + +static int +brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_cfg80211_vif *vif; + int err = 0; + + brcmf_dbg(TRACE, "Enter p2p listen cancel\n"); + + vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + if (vif == NULL) { + brcmf_err("No p2p device available for probe response\n"); + err = -ENODEV; + goto exit; + } + brcmf_p2p_cancel_remain_on_channel(vif->ifp); +exit: + return err; +} + +static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_crit_proto_id proto, + u16 duration) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_cfg80211_vif *vif; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + + /* only DHCP support for now */ + if (proto != NL80211_CRIT_PROTO_DHCP) + return -EINVAL; + + /* suppress and abort scanning */ + set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); + brcmf_abort_scanning(cfg); + + return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration); +} + +static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy, + struct wireless_dev *wdev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_cfg80211_vif *vif; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + + brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); + clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); +} + +static s32 +brcmf_notify_tdls_peer_event(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + switch (e->reason) { + case BRCMF_E_REASON_TDLS_PEER_DISCOVERED: + brcmf_dbg(TRACE, "TDLS Peer Discovered\n"); + break; + case BRCMF_E_REASON_TDLS_PEER_CONNECTED: + brcmf_dbg(TRACE, "TDLS Peer Connected\n"); + brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + break; + case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED: + brcmf_dbg(TRACE, "TDLS Peer Disconnected\n"); + brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + break; + } + + return 0; +} + +static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper) +{ + int ret; + + switch (oper) { + case NL80211_TDLS_DISCOVERY_REQ: + ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY; + break; + case NL80211_TDLS_SETUP: + ret = BRCMF_TDLS_MANUAL_EP_CREATE; + break; + case NL80211_TDLS_TEARDOWN: + ret = BRCMF_TDLS_MANUAL_EP_DELETE; + break; + default: + brcmf_err("unsupported operation: %d\n", oper); + ret = -EOPNOTSUPP; + } + return ret; +} + +static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy, + struct net_device *ndev, const u8 *peer, + enum nl80211_tdls_operation oper) +{ + struct brcmf_if *ifp; + struct brcmf_tdls_iovar_le info; + int ret = 0; + + ret = brcmf_convert_nl80211_tdls_oper(oper); + if (ret < 0) + return ret; + + ifp = netdev_priv(ndev); + memset(&info, 0, sizeof(info)); + info.mode = (u8)ret; + if (peer) + memcpy(info.ea, peer, ETH_ALEN); + + ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint", + &info, sizeof(info)); + if (ret < 0) + brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret); + + return ret; +} + +static struct cfg80211_ops wl_cfg80211_ops = { + .add_virtual_intf = brcmf_cfg80211_add_iface, + .del_virtual_intf = brcmf_cfg80211_del_iface, + .change_virtual_intf = brcmf_cfg80211_change_iface, + .scan = brcmf_cfg80211_scan, + .set_wiphy_params = brcmf_cfg80211_set_wiphy_params, + .join_ibss = brcmf_cfg80211_join_ibss, + .leave_ibss = brcmf_cfg80211_leave_ibss, + .get_station = brcmf_cfg80211_get_station, + .dump_station = brcmf_cfg80211_dump_station, + .set_tx_power = brcmf_cfg80211_set_tx_power, + .get_tx_power = brcmf_cfg80211_get_tx_power, + .add_key = brcmf_cfg80211_add_key, + .del_key = brcmf_cfg80211_del_key, + .get_key = brcmf_cfg80211_get_key, + .set_default_key = brcmf_cfg80211_config_default_key, + .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key, + .set_power_mgmt = brcmf_cfg80211_set_power_mgmt, + .connect = brcmf_cfg80211_connect, + .disconnect = brcmf_cfg80211_disconnect, + .suspend = brcmf_cfg80211_suspend, + .resume = brcmf_cfg80211_resume, + .set_pmksa = brcmf_cfg80211_set_pmksa, + .del_pmksa = brcmf_cfg80211_del_pmksa, + .flush_pmksa = brcmf_cfg80211_flush_pmksa, + .start_ap = brcmf_cfg80211_start_ap, + .stop_ap = brcmf_cfg80211_stop_ap, + .change_beacon = brcmf_cfg80211_change_beacon, + .del_station = brcmf_cfg80211_del_station, + .change_station = brcmf_cfg80211_change_station, + .sched_scan_start = brcmf_cfg80211_sched_scan_start, + .sched_scan_stop = brcmf_cfg80211_sched_scan_stop, + .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register, + .mgmt_tx = brcmf_cfg80211_mgmt_tx, + .remain_on_channel = brcmf_p2p_remain_on_channel, + .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel, + .start_p2p_device = brcmf_p2p_start_device, + .stop_p2p_device = brcmf_p2p_stop_device, + .crit_proto_start = brcmf_cfg80211_crit_proto_start, + .crit_proto_stop = brcmf_cfg80211_crit_proto_stop, + .tdls_oper = brcmf_cfg80211_tdls_oper, +}; + +struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, + enum nl80211_iftype type, + bool pm_block) +{ + struct brcmf_cfg80211_vif *vif_walk; + struct brcmf_cfg80211_vif *vif; + bool mbss; + + brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n", + sizeof(*vif)); + vif = kzalloc(sizeof(*vif), GFP_KERNEL); + if (!vif) + return ERR_PTR(-ENOMEM); + + vif->wdev.wiphy = cfg->wiphy; + vif->wdev.iftype = type; + + vif->pm_block = pm_block; + vif->roam_off = -1; + + brcmf_init_prof(&vif->profile); + + if (type == NL80211_IFTYPE_AP) { + mbss = false; + list_for_each_entry(vif_walk, &cfg->vif_list, list) { + if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) { + mbss = true; + break; + } + } + vif->mbss = mbss; + } + + list_add_tail(&vif->list, &cfg->vif_list); + return vif; +} + +void brcmf_free_vif(struct brcmf_cfg80211_vif *vif) +{ + list_del(&vif->list); + kfree(vif); +} + +void brcmf_cfg80211_free_netdev(struct net_device *ndev) +{ + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + + ifp = netdev_priv(ndev); + vif = ifp->vif; + + if (vif) + brcmf_free_vif(vif); + free_netdev(ndev); +} + +static bool brcmf_is_linkup(const struct brcmf_event_msg *e) +{ + u32 event = e->event_code; + u32 status = e->status; + + if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) { + brcmf_dbg(CONN, "Processing set ssid\n"); + return true; + } + + return false; +} + +static bool brcmf_is_linkdown(const struct brcmf_event_msg *e) +{ + u32 event = e->event_code; + u16 flags = e->flags; + + if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) || + (event == BRCMF_E_DISASSOC_IND) || + ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) { + brcmf_dbg(CONN, "Processing link down\n"); + return true; + } + return false; +} + +static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg, + const struct brcmf_event_msg *e) +{ + u32 event = e->event_code; + u32 status = e->status; + + if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) { + brcmf_dbg(CONN, "Processing Link %s & no network found\n", + e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down"); + return true; + } + + if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) { + brcmf_dbg(CONN, "Processing connecting & no network found\n"); + return true; + } + + return false; +} + +static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); + + kfree(conn_info->req_ie); + conn_info->req_ie = NULL; + conn_info->req_ie_len = 0; + kfree(conn_info->resp_ie); + conn_info->resp_ie = NULL; + conn_info->resp_ie_len = 0; +} + +static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp) +{ + struct brcmf_cfg80211_assoc_ielen_le *assoc_info; + struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); + u32 req_len; + u32 resp_len; + s32 err = 0; + + brcmf_clear_assoc_ies(cfg); + + err = brcmf_fil_iovar_data_get(ifp, "assoc_info", + cfg->extra_buf, WL_ASSOC_INFO_MAX); + if (err) { + brcmf_err("could not get assoc info (%d)\n", err); + return err; + } + assoc_info = + (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf; + req_len = le32_to_cpu(assoc_info->req_len); + resp_len = le32_to_cpu(assoc_info->resp_len); + if (req_len) { + err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies", + cfg->extra_buf, + WL_ASSOC_INFO_MAX); + if (err) { + brcmf_err("could not get assoc req (%d)\n", err); + return err; + } + conn_info->req_ie_len = req_len; + conn_info->req_ie = + kmemdup(cfg->extra_buf, conn_info->req_ie_len, + GFP_KERNEL); + } else { + conn_info->req_ie_len = 0; + conn_info->req_ie = NULL; + } + if (resp_len) { + err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies", + cfg->extra_buf, + WL_ASSOC_INFO_MAX); + if (err) { + brcmf_err("could not get assoc resp (%d)\n", err); + return err; + } + conn_info->resp_ie_len = resp_len; + conn_info->resp_ie = + kmemdup(cfg->extra_buf, conn_info->resp_ie_len, + GFP_KERNEL); + } else { + conn_info->resp_ie_len = 0; + conn_info->resp_ie = NULL; + } + brcmf_dbg(CONN, "req len (%d) resp len (%d)\n", + conn_info->req_ie_len, conn_info->resp_ie_len); + + return err; +} + +static s32 +brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, + struct net_device *ndev, + const struct brcmf_event_msg *e) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); + struct wiphy *wiphy = cfg_to_wiphy(cfg); + struct ieee80211_channel *notify_channel = NULL; + struct ieee80211_supported_band *band; + struct brcmf_bss_info_le *bi; + struct brcmu_chan ch; + u32 freq; + s32 err = 0; + u8 *buf; + + brcmf_dbg(TRACE, "Enter\n"); + + brcmf_get_assoc_ies(cfg, ifp); + memcpy(profile->bssid, e->addr, ETH_ALEN); + brcmf_update_bss_info(cfg, ifp); + + buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); + if (buf == NULL) { + err = -ENOMEM; + goto done; + } + + /* data sent to dongle has to be little endian */ + *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, + buf, WL_BSS_INFO_MAX); + + if (err) + goto done; + + bi = (struct brcmf_bss_info_le *)(buf + 4); + ch.chspec = le16_to_cpu(bi->chanspec); + cfg->d11inf.decchspec(&ch); + + if (ch.band == BRCMU_CHAN_BAND_2G) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + + freq = ieee80211_channel_to_frequency(ch.chnum, band->band); + notify_channel = ieee80211_get_channel(wiphy, freq); + +done: + kfree(buf); + cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid, + conn_info->req_ie, conn_info->req_ie_len, + conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); + brcmf_dbg(CONN, "Report roaming result\n"); + + set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, + struct net_device *ndev, const struct brcmf_event_msg *e, + bool completed) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); + + brcmf_dbg(TRACE, "Enter\n"); + + if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING, + &ifp->vif->sme_state)) { + if (completed) { + brcmf_get_assoc_ies(cfg, ifp); + memcpy(profile->bssid, e->addr, ETH_ALEN); + brcmf_update_bss_info(cfg, ifp); + set_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state); + } + cfg80211_connect_result(ndev, + (u8 *)profile->bssid, + conn_info->req_ie, + conn_info->req_ie_len, + conn_info->resp_ie, + conn_info->resp_ie_len, + completed ? WLAN_STATUS_SUCCESS : + WLAN_STATUS_AUTH_TIMEOUT, + GFP_KERNEL); + brcmf_dbg(CONN, "Report connect result - connection %s\n", + completed ? "succeeded" : "failed"); + } + brcmf_dbg(TRACE, "Exit\n"); + return 0; +} + +static s32 +brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, + struct net_device *ndev, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + static int generation; + u32 event = e->event_code; + u32 reason = e->reason; + struct station_info sinfo; + + brcmf_dbg(CONN, "event %d, reason %d\n", event, reason); + if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS && + ndev != cfg_to_ndev(cfg)) { + brcmf_dbg(CONN, "AP mode link down\n"); + complete(&cfg->vif_disabled); + if (ifp->vif->mbss) + brcmf_remove_interface(ifp); + return 0; + } + + if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) && + (reason == BRCMF_E_STATUS_SUCCESS)) { + memset(&sinfo, 0, sizeof(sinfo)); + if (!data) { + brcmf_err("No IEs present in ASSOC/REASSOC_IND"); + return -EINVAL; + } + sinfo.assoc_req_ies = data; + sinfo.assoc_req_ies_len = e->datalen; + generation++; + sinfo.generation = generation; + cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL); + } else if ((event == BRCMF_E_DISASSOC_IND) || + (event == BRCMF_E_DEAUTH_IND) || + (event == BRCMF_E_DEAUTH)) { + cfg80211_del_sta(ndev, e->addr, GFP_KERNEL); + } + return 0; +} + +static s32 +brcmf_notify_connect_status(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct net_device *ndev = ifp->ndev; + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct ieee80211_channel *chan; + s32 err = 0; + + if ((e->event_code == BRCMF_E_DEAUTH) || + (e->event_code == BRCMF_E_DEAUTH_IND) || + (e->event_code == BRCMF_E_DISASSOC_IND) || + ((e->event_code == BRCMF_E_LINK) && (!e->flags))) { + brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + } + + if (brcmf_is_apmode(ifp->vif)) { + err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); + } else if (brcmf_is_linkup(e)) { + brcmf_dbg(CONN, "Linkup\n"); + if (brcmf_is_ibssmode(ifp->vif)) { + chan = ieee80211_get_channel(cfg->wiphy, cfg->channel); + memcpy(profile->bssid, e->addr, ETH_ALEN); + wl_inform_ibss(cfg, ndev, e->addr); + cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL); + clear_bit(BRCMF_VIF_STATUS_CONNECTING, + &ifp->vif->sme_state); + set_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state); + } else + brcmf_bss_connect_done(cfg, ndev, e, true); + brcmf_net_setcarrier(ifp, true); + } else if (brcmf_is_linkdown(e)) { + brcmf_dbg(CONN, "Linkdown\n"); + if (!brcmf_is_ibssmode(ifp->vif)) { + brcmf_bss_connect_done(cfg, ndev, e, false); + } + brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e)); + brcmf_init_prof(ndev_to_prof(ndev)); + if (ndev != cfg_to_ndev(cfg)) + complete(&cfg->vif_disabled); + brcmf_net_setcarrier(ifp, false); + } else if (brcmf_is_nonetwork(cfg, e)) { + if (brcmf_is_ibssmode(ifp->vif)) + clear_bit(BRCMF_VIF_STATUS_CONNECTING, + &ifp->vif->sme_state); + else + brcmf_bss_connect_done(cfg, ndev, e, false); + } + + return err; +} + +static s32 +brcmf_notify_roaming_status(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + u32 event = e->event_code; + u32 status = e->status; + + if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) { + if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) + brcmf_bss_roaming_done(cfg, ifp->ndev, e); + else + brcmf_bss_connect_done(cfg, ifp->ndev, e, true); + } + + return 0; +} + +static s32 +brcmf_notify_mic_status(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + u16 flags = e->flags; + enum nl80211_key_type key_type; + + if (flags & BRCMF_EVENT_MSG_GROUP) + key_type = NL80211_KEYTYPE_GROUP; + else + key_type = NL80211_KEYTYPE_PAIRWISE; + + cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1, + NULL, GFP_KERNEL); + + return 0; +} + +static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data; + struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; + struct brcmf_cfg80211_vif *vif; + + brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n", + ifevent->action, ifevent->flags, ifevent->ifidx, + ifevent->bssidx); + + mutex_lock(&event->vif_event_lock); + event->action = ifevent->action; + vif = event->vif; + + switch (ifevent->action) { + case BRCMF_E_IF_ADD: + /* waiting process may have timed out */ + if (!cfg->vif_event.vif) { + mutex_unlock(&event->vif_event_lock); + return -EBADF; + } + + ifp->vif = vif; + vif->ifp = ifp; + if (ifp->ndev) { + vif->wdev.netdev = ifp->ndev; + ifp->ndev->ieee80211_ptr = &vif->wdev; + SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy)); + } + mutex_unlock(&event->vif_event_lock); + wake_up(&event->vif_wq); + return 0; + + case BRCMF_E_IF_DEL: + mutex_unlock(&event->vif_event_lock); + /* event may not be upon user request */ + if (brcmf_cfg80211_vif_event_armed(cfg)) + wake_up(&event->vif_wq); + return 0; + + case BRCMF_E_IF_CHANGE: + mutex_unlock(&event->vif_event_lock); + wake_up(&event->vif_wq); + return 0; + + default: + mutex_unlock(&event->vif_event_lock); + break; + } + return -EINVAL; +} + +static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) +{ + conf->frag_threshold = (u32)-1; + conf->rts_threshold = (u32)-1; + conf->retry_short = (u32)-1; + conf->retry_long = (u32)-1; + conf->tx_power = -1; +} + +static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg) +{ + brcmf_fweh_register(cfg->pub, BRCMF_E_LINK, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM, + brcmf_notify_roaming_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR, + brcmf_notify_mic_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, + brcmf_notify_sched_scan_results); + brcmf_fweh_register(cfg->pub, BRCMF_E_IF, + brcmf_notify_vif_event); + brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG, + brcmf_p2p_notify_rx_mgmt_p2p_probereq); + brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE, + brcmf_p2p_notify_listen_complete); + brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX, + brcmf_p2p_notify_action_frame_rx); + brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE, + brcmf_p2p_notify_action_tx_complete); + brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE, + brcmf_p2p_notify_action_tx_complete); +} + +static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) +{ + kfree(cfg->conf); + cfg->conf = NULL; + kfree(cfg->escan_ioctl_buf); + cfg->escan_ioctl_buf = NULL; + kfree(cfg->extra_buf); + cfg->extra_buf = NULL; + kfree(cfg->pmk_list); + cfg->pmk_list = NULL; +} + +static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) +{ + cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL); + if (!cfg->conf) + goto init_priv_mem_out; + cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); + if (!cfg->escan_ioctl_buf) + goto init_priv_mem_out; + cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); + if (!cfg->extra_buf) + goto init_priv_mem_out; + cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL); + if (!cfg->pmk_list) + goto init_priv_mem_out; + + return 0; + +init_priv_mem_out: + brcmf_deinit_priv_mem(cfg); + + return -ENOMEM; +} + +static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) +{ + s32 err = 0; + + cfg->scan_request = NULL; + cfg->pwr_save = true; + cfg->active_scan = true; /* we do active scan per default */ + cfg->dongle_up = false; /* dongle is not up yet */ + err = brcmf_init_priv_mem(cfg); + if (err) + return err; + brcmf_register_event_handlers(cfg); + mutex_init(&cfg->usr_sync); + brcmf_init_escan(cfg); + brcmf_init_conf(cfg->conf); + init_completion(&cfg->vif_disabled); + return err; +} + +static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg) +{ + cfg->dongle_up = false; /* dongle down */ + brcmf_abort_scanning(cfg); + brcmf_deinit_priv_mem(cfg); +} + +static void init_vif_event(struct brcmf_cfg80211_vif_event *event) +{ + init_waitqueue_head(&event->vif_wq); + mutex_init(&event->vif_event_lock); +} + +static s32 +brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) +{ + s32 err = 0; + __le32 roamtrigger[2]; + __le32 roam_delta[2]; + + /* + * Setup timeout if Beacons are lost and roam is + * off to report link down + */ + if (brcmf_roamoff) { + err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout); + if (err) { + brcmf_err("bcn_timeout error (%d)\n", err); + goto dongle_rom_out; + } + } + + /* + * Enable/Disable built-in roaming to allow supplicant + * to take care of roaming + */ + brcmf_dbg(INFO, "Internal Roaming = %s\n", + brcmf_roamoff ? "Off" : "On"); + err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff)); + if (err) { + brcmf_err("roam_off error (%d)\n", err); + goto dongle_rom_out; + } + + roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL); + roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER, + (void *)roamtrigger, sizeof(roamtrigger)); + if (err) { + brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err); + goto dongle_rom_out; + } + + roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA); + roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA, + (void *)roam_delta, sizeof(roam_delta)); + if (err) { + brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err); + goto dongle_rom_out; + } + +dongle_rom_out: + return err; +} + +static s32 +brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time, + s32 scan_unassoc_time, s32 scan_passive_time) +{ + s32 err = 0; + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, + scan_assoc_time); + if (err) { + if (err == -EOPNOTSUPP) + brcmf_dbg(INFO, "Scan assoc time is not supported\n"); + else + brcmf_err("Scan assoc time error (%d)\n", err); + goto dongle_scantime_out; + } + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, + scan_unassoc_time); + if (err) { + if (err == -EOPNOTSUPP) + brcmf_dbg(INFO, "Scan unassoc time is not supported\n"); + else + brcmf_err("Scan unassoc time error (%d)\n", err); + goto dongle_scantime_out; + } + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME, + scan_passive_time); + if (err) { + if (err == -EOPNOTSUPP) + brcmf_dbg(INFO, "Scan passive time is not supported\n"); + else + brcmf_err("Scan passive time error (%d)\n", err); + goto dongle_scantime_out; + } + +dongle_scantime_out: + return err; +} + +static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel, + struct brcmu_chan *ch) +{ + u32 ht40_flag; + + ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40; + if (ch->sb == BRCMU_CHAN_SB_U) { + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + channel->flags &= ~IEEE80211_CHAN_NO_HT40; + channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; + } else { + /* It should be one of + * IEEE80211_CHAN_NO_HT40 or + * IEEE80211_CHAN_NO_HT40PLUS + */ + channel->flags &= ~IEEE80211_CHAN_NO_HT40; + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; + } +} + +static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, + u32 bw_cap[]) +{ + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct ieee80211_supported_band *band; + struct ieee80211_channel *channel; + struct wiphy *wiphy; + struct brcmf_chanspec_list *list; + struct brcmu_chan ch; + int err; + u8 *pbuf; + u32 i, j; + u32 total; + u32 chaninfo; + u32 index; + + pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); + + if (pbuf == NULL) + return -ENOMEM; + + list = (struct brcmf_chanspec_list *)pbuf; + + err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, + BRCMF_DCMD_MEDLEN); + if (err) { + brcmf_err("get chanspecs error (%d)\n", err); + goto fail_pbuf; + } + + wiphy = cfg_to_wiphy(cfg); + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + if (band) + for (i = 0; i < band->n_channels; i++) + band->channels[i].flags = IEEE80211_CHAN_DISABLED; + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (band) + for (i = 0; i < band->n_channels; i++) + band->channels[i].flags = IEEE80211_CHAN_DISABLED; + + total = le32_to_cpu(list->count); + for (i = 0; i < total; i++) { + ch.chspec = (u16)le32_to_cpu(list->element[i]); + cfg->d11inf.decchspec(&ch); + + if (ch.band == BRCMU_CHAN_BAND_2G) { + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + } else if (ch.band == BRCMU_CHAN_BAND_5G) { + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + } else { + brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec); + continue; + } + if (!band) + continue; + if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) && + ch.bw == BRCMU_CHAN_BW_40) + continue; + if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) && + ch.bw == BRCMU_CHAN_BW_80) + continue; + + channel = band->channels; + index = band->n_channels; + for (j = 0; j < band->n_channels; j++) { + if (channel[j].hw_value == ch.chnum) { + index = j; + break; + } + } + channel[index].center_freq = + ieee80211_channel_to_frequency(ch.chnum, band->band); + channel[index].hw_value = ch.chnum; + + /* assuming the chanspecs order is HT20, + * HT40 upper, HT40 lower, and VHT80. + */ + if (ch.bw == BRCMU_CHAN_BW_80) { + channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ; + } else if (ch.bw == BRCMU_CHAN_BW_40) { + brcmf_update_bw40_channel_flag(&channel[index], &ch); + } else { + /* enable the channel and disable other bandwidths + * for now as mentioned order assure they are enabled + * for subsequent chanspecs. + */ + channel[index].flags = IEEE80211_CHAN_NO_HT40 | + IEEE80211_CHAN_NO_80MHZ; + ch.bw = BRCMU_CHAN_BW_20; + cfg->d11inf.encchspec(&ch); + chaninfo = ch.chspec; + err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info", + &chaninfo); + if (!err) { + if (chaninfo & WL_CHAN_RADAR) + channel[index].flags |= + (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR); + if (chaninfo & WL_CHAN_PASSIVE) + channel[index].flags |= + IEEE80211_CHAN_NO_IR; + } + } + } + +fail_pbuf: + kfree(pbuf); + return err; +} + +static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct ieee80211_supported_band *band; + struct brcmf_fil_bwcap_le band_bwcap; + struct brcmf_chanspec_list *list; + u8 *pbuf; + u32 val; + int err; + struct brcmu_chan ch; + u32 num_chan; + int i, j; + + /* verify support for bw_cap command */ + val = WLC_BAND_5G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); + + if (!err) { + /* only set 2G bandwidth using bw_cap command */ + band_bwcap.band = cpu_to_le32(WLC_BAND_2G); + band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ); + err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, + sizeof(band_bwcap)); + } else { + brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); + val = WLC_N_BW_40ALL; + err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); + } + + if (!err) { + /* update channel info in 2G band */ + pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); + + if (pbuf == NULL) + return -ENOMEM; + + ch.band = BRCMU_CHAN_BAND_2G; + ch.bw = BRCMU_CHAN_BW_40; + ch.sb = BRCMU_CHAN_SB_NONE; + ch.chnum = 0; + cfg->d11inf.encchspec(&ch); + + /* pass encoded chanspec in query */ + *(__le16 *)pbuf = cpu_to_le16(ch.chspec); + + err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, + BRCMF_DCMD_MEDLEN); + if (err) { + brcmf_err("get chanspecs error (%d)\n", err); + kfree(pbuf); + return err; + } + + band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ]; + list = (struct brcmf_chanspec_list *)pbuf; + num_chan = le32_to_cpu(list->count); + for (i = 0; i < num_chan; i++) { + ch.chspec = (u16)le32_to_cpu(list->element[i]); + cfg->d11inf.decchspec(&ch); + if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G)) + continue; + if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40)) + continue; + for (j = 0; j < band->n_channels; j++) { + if (band->channels[j].hw_value == ch.chnum) + break; + } + if (WARN_ON(j == band->n_channels)) + continue; + + brcmf_update_bw40_channel_flag(&band->channels[j], &ch); + } + kfree(pbuf); + } + return err; +} + +static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) +{ + u32 band, mimo_bwcap; + int err; + + band = WLC_BAND_2G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); + if (!err) { + bw_cap[IEEE80211_BAND_2GHZ] = band; + band = WLC_BAND_5G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); + if (!err) { + bw_cap[IEEE80211_BAND_5GHZ] = band; + return; + } + WARN_ON(1); + return; + } + brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n"); + mimo_bwcap = 0; + err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap); + if (err) + /* assume 20MHz if firmware does not give a clue */ + mimo_bwcap = WLC_N_BW_20ALL; + + switch (mimo_bwcap) { + case WLC_N_BW_40ALL: + bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT; + /* fall-thru */ + case WLC_N_BW_20IN2G_40IN5G: + bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT; + /* fall-thru */ + case WLC_N_BW_20ALL: + bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT; + bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT; + break; + default: + brcmf_err("invalid mimo_bw_cap value\n"); + } +} + +static void brcmf_update_ht_cap(struct ieee80211_supported_band *band, + u32 bw_cap[2], u32 nchain) +{ + band->ht_cap.ht_supported = true; + if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; + memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; +} + +static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp) +{ + u16 mcs_map; + int i; + + for (i = 0, mcs_map = 0xFFFF; i < nchain; i++) + mcs_map = (mcs_map << 2) | supp; + + return cpu_to_le16(mcs_map); +} + +static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, + u32 bw_cap[2], u32 nchain) +{ + __le16 mcs_map; + + /* not allowed in 2.4G band */ + if (band->band == IEEE80211_BAND_2GHZ) + return; + + band->vht_cap.vht_supported = true; + /* 80MHz is mandatory */ + band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80; + if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) { + band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160; + } + /* all support 256-QAM */ + mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9); + band->vht_cap.vht_mcs.rx_mcs_map = mcs_map; + band->vht_cap.vht_mcs.tx_mcs_map = mcs_map; +} + +static int brcmf_setup_wiphybands(struct wiphy *wiphy) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + u32 nmode = 0; + u32 vhtmode = 0; + u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT }; + u32 rxchain; + u32 nchain; + int err; + s32 i; + struct ieee80211_supported_band *band; + + (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); + err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); + if (err) { + brcmf_err("nmode error (%d)\n", err); + } else { + brcmf_get_bwcap(ifp, bw_cap); + } + brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n", + nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ], + bw_cap[IEEE80211_BAND_5GHZ]); + + err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); + if (err) { + brcmf_err("rxchain error (%d)\n", err); + nchain = 1; + } else { + for (nchain = 0; rxchain; nchain++) + rxchain = rxchain & (rxchain - 1); + } + brcmf_dbg(INFO, "nchain=%d\n", nchain); + + err = brcmf_construct_chaninfo(cfg, bw_cap); + if (err) { + brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err); + return err; + } + + wiphy = cfg_to_wiphy(cfg); + for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) { + band = wiphy->bands[i]; + if (band == NULL) + continue; + + if (nmode) + brcmf_update_ht_cap(band, bw_cap, nchain); + if (vhtmode) + brcmf_update_vht_cap(band, bw_cap, nchain); + } + + return 0; +} + +static const struct ieee80211_txrx_stypes +brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4) + }, + [NL80211_IFTYPE_P2P_DEVICE] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + } +}; + +/** + * brcmf_setup_ifmodes() - determine interface modes and combinations. + * + * @wiphy: wiphy object. + * @ifp: interface object needed for feat module api. + * + * The interface modes and combinations are determined dynamically here + * based on firmware functionality. + * + * no p2p and no mbss: + * + * #STA <= 1, #AP <= 1, channels = 1, 2 total + * + * no p2p and mbss: + * + * #STA <= 1, #AP <= 1, channels = 1, 2 total + * #AP <= 4, matching BI, channels = 1, 4 total + * + * p2p, no mchan, and mbss: + * + * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total + * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total + * #AP <= 4, matching BI, channels = 1, 4 total + * + * p2p, mchan, and mbss: + * + * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total + * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total + * #AP <= 4, matching BI, channels = 1, 4 total + */ +static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) +{ + struct ieee80211_iface_combination *combo = NULL; + struct ieee80211_iface_limit *c0_limits = NULL; + struct ieee80211_iface_limit *p2p_limits = NULL; + struct ieee80211_iface_limit *mbss_limits = NULL; + bool mbss, p2p; + int i, c, n_combos; + + mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS); + p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P); + + n_combos = 1 + !!p2p + !!mbss; + combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL); + if (!combo) + goto err; + + c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL); + if (!c0_limits) + goto err; + + if (p2p) { + p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL); + if (!p2p_limits) + goto err; + } + + if (mbss) { + mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL); + if (!mbss_limits) + goto err; + } + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP); + + c = 0; + i = 0; + combo[c].num_different_channels = 1; + c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); + if (p2p) { + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) + combo[c].num_different_channels = 2; + wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_DEVICE); + c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE); + c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + } else { + c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); + } + combo[c].max_interfaces = i; + combo[c].n_limits = i; + combo[c].limits = c0_limits; + + if (p2p) { + c++; + i = 0; + combo[c].num_different_channels = 1; + p2p_limits[i].max = 1; + p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION); + p2p_limits[i].max = 1; + p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP); + p2p_limits[i].max = 1; + p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT); + p2p_limits[i].max = 1; + p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE); + combo[c].max_interfaces = i; + combo[c].n_limits = i; + combo[c].limits = p2p_limits; + } + + if (mbss) { + c++; + combo[c].beacon_int_infra_match = true; + combo[c].num_different_channels = 1; + mbss_limits[0].max = 4; + mbss_limits[0].types = BIT(NL80211_IFTYPE_AP); + combo[c].max_interfaces = 4; + combo[c].n_limits = 1; + combo[c].limits = mbss_limits; + } + wiphy->n_iface_combinations = n_combos; + wiphy->iface_combinations = combo; + return 0; + +err: + kfree(c0_limits); + kfree(p2p_limits); + kfree(mbss_limits); + kfree(combo); + return -ENOMEM; +} + +static void brcmf_wiphy_pno_params(struct wiphy *wiphy) +{ + /* scheduled scan settings */ + wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; + wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; + wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; + wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +} + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support brcmf_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = BRCMF_WOWL_MAXPATTERNS, + .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE, + .pattern_min_len = 1, + .max_pkt_offset = 1500, +}; +#endif + +static void brcmf_wiphy_wowl_params(struct wiphy *wiphy) +{ +#ifdef CONFIG_PM + /* wowl settings */ + wiphy->wowlan = &brcmf_wowlan_support; +#endif +} + +static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) +{ + struct brcmf_pub *drvr = ifp->drvr; + const struct ieee80211_iface_combination *combo; + struct ieee80211_supported_band *band; + u16 max_interfaces = 0; + __le32 bandlist[3]; + u32 n_bands; + int err, i; + + wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; + wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; + wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; + + err = brcmf_setup_ifmodes(wiphy, ifp); + if (err) + return err; + + for (i = 0, combo = wiphy->iface_combinations; + i < wiphy->n_iface_combinations; i++, combo++) { + max_interfaces = max(max_interfaces, combo->max_interfaces); + } + + for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses); + i++) { + u8 *addr = drvr->addresses[i].addr; + + memcpy(addr, drvr->mac, ETH_ALEN); + if (i) { + addr[0] |= BIT(1); + addr[ETH_ALEN - 1] ^= i; + } + } + wiphy->addresses = drvr->addresses; + wiphy->n_addresses = i; + + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->cipher_suites = __wl_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); + wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | + WIPHY_FLAG_OFFCHAN_TX | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_SUPPORTS_TDLS; + if (!brcmf_roamoff) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; + wiphy->mgmt_stypes = brcmf_txrx_stypes; + wiphy->max_remain_on_channel_duration = 5000; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) + brcmf_wiphy_pno_params(wiphy); + + /* vendor commands/events support */ + wiphy->vendor_commands = brcmf_vendor_cmds; + wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; + + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) + brcmf_wiphy_wowl_params(wiphy); + + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, + sizeof(bandlist)); + if (err) { + brcmf_err("could not obtain band info: err=%d\n", err); + return err; + } + /* first entry in bandlist is number of bands */ + n_bands = le32_to_cpu(bandlist[0]); + for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) { + if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) { + band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), + GFP_KERNEL); + if (!band) + return -ENOMEM; + + band->channels = kmemdup(&__wl_2ghz_channels, + sizeof(__wl_2ghz_channels), + GFP_KERNEL); + if (!band->channels) { + kfree(band); + return -ENOMEM; + } + + band->n_channels = ARRAY_SIZE(__wl_2ghz_channels); + wiphy->bands[IEEE80211_BAND_2GHZ] = band; + } + if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) { + band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz), + GFP_KERNEL); + if (!band) + return -ENOMEM; + + band->channels = kmemdup(&__wl_5ghz_channels, + sizeof(__wl_5ghz_channels), + GFP_KERNEL); + if (!band->channels) { + kfree(band); + return -ENOMEM; + } + + band->n_channels = ARRAY_SIZE(__wl_5ghz_channels); + wiphy->bands[IEEE80211_BAND_5GHZ] = band; + } + } + err = brcmf_setup_wiphybands(wiphy); + return err; +} + +static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) +{ + struct net_device *ndev; + struct wireless_dev *wdev; + struct brcmf_if *ifp; + s32 power_mode; + s32 err = 0; + + if (cfg->dongle_up) + return err; + + ndev = cfg_to_ndev(cfg); + wdev = ndev->ieee80211_ptr; + ifp = netdev_priv(ndev); + + /* make sure RF is ready for work */ + brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); + + brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME, + WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME); + + power_mode = cfg->pwr_save ? PM_FAST : PM_OFF; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode); + if (err) + goto default_conf_out; + brcmf_dbg(INFO, "power save set to %s\n", + (power_mode ? "enabled" : "disabled")); + + err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT); + if (err) + goto default_conf_out; + err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, + NULL, NULL); + if (err) + goto default_conf_out; + + brcmf_configure_arp_offload(ifp, true); + + cfg->dongle_up = true; +default_conf_out: + + return err; + +} + +static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp) +{ + set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); + + return brcmf_config_dongle(ifp->drvr->config); +} + +static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + + /* + * While going down, if associated with AP disassociate + * from AP to save power + */ + if (check_vif_up(ifp->vif)) { + brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED); + + /* Make sure WPA_Supplicant receives all the event + generated due to DISASSOC call to the fw to keep + the state fw and WPA_Supplicant state consistent + */ + brcmf_delay(500); + } + + brcmf_abort_scanning(cfg); + clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); + + return 0; +} + +s32 brcmf_cfg80211_up(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + s32 err = 0; + + mutex_lock(&cfg->usr_sync); + err = __brcmf_cfg80211_up(ifp); + mutex_unlock(&cfg->usr_sync); + + return err; +} + +s32 brcmf_cfg80211_down(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + s32 err = 0; + + mutex_lock(&cfg->usr_sync); + err = __brcmf_cfg80211_down(ifp); + mutex_unlock(&cfg->usr_sync); + + return err; +} + +enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp) +{ + struct wireless_dev *wdev = &ifp->vif->wdev; + + return wdev->iftype; +} + +bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, + unsigned long state) +{ + struct brcmf_cfg80211_vif *vif; + + list_for_each_entry(vif, &cfg->vif_list, list) { + if (test_bit(state, &vif->sme_state)) + return true; + } + return false; +} + +static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event, + u8 action) +{ + u8 evt_action; + + mutex_lock(&event->vif_event_lock); + evt_action = event->action; + mutex_unlock(&event->vif_event_lock); + return evt_action == action; +} + +void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, + struct brcmf_cfg80211_vif *vif) +{ + struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; + + mutex_lock(&event->vif_event_lock); + event->vif = vif; + event->action = 0; + mutex_unlock(&event->vif_event_lock); +} + +bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; + bool armed; + + mutex_lock(&event->vif_event_lock); + armed = event->vif != NULL; + mutex_unlock(&event->vif_event_lock); + + return armed; +} +int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, + u8 action, ulong timeout) +{ + struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; + + return wait_event_timeout(event->vif_wq, + vif_event_equals(event, action), timeout); +} + +static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *req) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_fil_country_le ccreq; + int i; + + brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator, + req->alpha2[0], req->alpha2[1]); + + /* ignore non-ISO3166 country codes */ + for (i = 0; i < sizeof(req->alpha2); i++) + if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { + brcmf_err("not a ISO3166 code\n"); + return; + } + memset(&ccreq, 0, sizeof(ccreq)); + ccreq.rev = cpu_to_le32(-1); + memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2)); + if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) { + brcmf_err("firmware rejected country setting\n"); + return; + } + brcmf_setup_wiphybands(wiphy); +} + +static void brcmf_free_wiphy(struct wiphy *wiphy) +{ + int i; + + if (!wiphy) + return; + + if (wiphy->iface_combinations) { + for (i = 0; i < wiphy->n_iface_combinations; i++) + kfree(wiphy->iface_combinations[i].limits); + } + kfree(wiphy->iface_combinations); + if (wiphy->bands[IEEE80211_BAND_2GHZ]) { + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); + } + if (wiphy->bands[IEEE80211_BAND_5GHZ]) { + kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels); + kfree(wiphy->bands[IEEE80211_BAND_5GHZ]); + } + wiphy_free(wiphy); +} + +struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, + struct device *busdev, + bool p2pdev_forced) +{ + struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev; + struct brcmf_cfg80211_info *cfg; + struct wiphy *wiphy; + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + s32 err = 0; + s32 io_type; + u16 *cap = NULL; + + if (!ndev) { + brcmf_err("ndev is invalid\n"); + return NULL; + } + + ifp = netdev_priv(ndev); + wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); + if (!wiphy) { + brcmf_err("Could not allocate wiphy device\n"); + return NULL; + } + memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN); + set_wiphy_dev(wiphy, busdev); + + cfg = wiphy_priv(wiphy); + cfg->wiphy = wiphy; + cfg->pub = drvr; + init_vif_event(&cfg->vif_event); + INIT_LIST_HEAD(&cfg->vif_list); + + vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false); + if (IS_ERR(vif)) + goto wiphy_out; + + vif->ifp = ifp; + vif->wdev.netdev = ndev; + ndev->ieee80211_ptr = &vif->wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); + + err = wl_init_priv(cfg); + if (err) { + brcmf_err("Failed to init iwm_priv (%d)\n", err); + brcmf_free_vif(vif); + goto wiphy_out; + } + ifp->vif = vif; + + /* determine d11 io type before wiphy setup */ + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type); + if (err) { + brcmf_err("Failed to get D11 version (%d)\n", err); + goto priv_out; + } + cfg->d11inf.io_type = (u8)io_type; + brcmu_d11_attach(&cfg->d11inf); + + err = brcmf_setup_wiphy(wiphy, ifp); + if (err < 0) + goto priv_out; + + brcmf_dbg(INFO, "Registering custom regulatory\n"); + wiphy->reg_notifier = brcmf_cfg80211_reg_notifier; + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); + + /* firmware defaults to 40MHz disabled in 2G band. We signal + * cfg80211 here that we do and have it decide we can enable + * it. But first check if device does support 2G operation. + */ + if (wiphy->bands[IEEE80211_BAND_2GHZ]) { + cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap; + *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + err = wiphy_register(wiphy); + if (err < 0) { + brcmf_err("Could not register wiphy device (%d)\n", err); + goto priv_out; + } + + /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), + * setup 40MHz in 2GHz band and enable OBSS scanning. + */ + if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) { + err = brcmf_enable_bw40_2g(cfg); + if (!err) + err = brcmf_fil_iovar_int_set(ifp, "obss_coex", + BRCMF_OBSS_COEX_AUTO); + else + *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + /* p2p might require that "if-events" get processed by fweh. So + * activate the already registered event handlers now and activate + * the rest when initialization has completed. drvr->config needs to + * be assigned before activating events. + */ + drvr->config = cfg; + err = brcmf_fweh_activate_events(ifp); + if (err) { + brcmf_err("FWEH activation failed (%d)\n", err); + goto wiphy_unreg_out; + } + + err = brcmf_p2p_attach(cfg, p2pdev_forced); + if (err) { + brcmf_err("P2P initilisation failed (%d)\n", err); + goto wiphy_unreg_out; + } + err = brcmf_btcoex_attach(cfg); + if (err) { + brcmf_err("BT-coex initialisation failed (%d)\n", err); + brcmf_p2p_detach(&cfg->p2p); + goto wiphy_unreg_out; + } + + err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); + if (err) { + brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); + wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; + } else { + brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT, + brcmf_notify_tdls_peer_event); + } + + /* (re-) activate FWEH event handling */ + err = brcmf_fweh_activate_events(ifp); + if (err) { + brcmf_err("FWEH activation failed (%d)\n", err); + goto wiphy_unreg_out; + } + + return cfg; + +wiphy_unreg_out: + wiphy_unregister(cfg->wiphy); +priv_out: + wl_deinit_priv(cfg); + brcmf_free_vif(vif); + ifp->vif = NULL; +wiphy_out: + brcmf_free_wiphy(wiphy); + return NULL; +} + +void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) +{ + if (!cfg) + return; + + brcmf_btcoex_detach(cfg); + wiphy_unregister(cfg->wiphy); + wl_deinit_priv(cfg); + brcmf_free_wiphy(cfg->wiphy); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h new file mode 100644 index 000000000000..6a878c8f883f --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2010 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_CFG80211_H +#define BRCMFMAC_CFG80211_H + +/* for brcmu_d11inf */ +#include + +#define WL_NUM_SCAN_MAX 10 +#define WL_NUM_PMKIDS_MAX MAXPMKID +#define WL_TLV_INFO_MAX 1024 +#define WL_BSS_INFO_MAX 2048 +#define WL_ASSOC_INFO_MAX 512 /* assoc related fil max buf */ +#define WL_EXTRA_BUF_MAX 2048 +#define WL_ROAM_TRIGGER_LEVEL -75 +#define WL_ROAM_DELTA 20 +#define WL_BEACON_TIMEOUT 3 + +#define WL_SCAN_CHANNEL_TIME 40 +#define WL_SCAN_UNASSOC_TIME 40 +#define WL_SCAN_PASSIVE_TIME 120 + +#define WL_ESCAN_BUF_SIZE (1024 * 64) +#define WL_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */ + +#define WL_ESCAN_ACTION_START 1 +#define WL_ESCAN_ACTION_CONTINUE 2 +#define WL_ESCAN_ACTION_ABORT 3 + +#define WL_AUTH_SHARED_KEY 1 /* d11 shared authentication */ +#define IE_MAX_LEN 512 + +/* IE TLV processing */ +#define TLV_LEN_OFF 1 /* length offset */ +#define TLV_HDR_LEN 2 /* header length */ +#define TLV_BODY_OFF 2 /* body offset */ +#define TLV_OUI_LEN 3 /* oui id length */ + +/* 802.11 Mgmt Packet flags */ +#define BRCMF_VNDR_IE_BEACON_FLAG 0x1 +#define BRCMF_VNDR_IE_PRBRSP_FLAG 0x2 +#define BRCMF_VNDR_IE_ASSOCRSP_FLAG 0x4 +#define BRCMF_VNDR_IE_AUTHRSP_FLAG 0x8 +#define BRCMF_VNDR_IE_PRBREQ_FLAG 0x10 +#define BRCMF_VNDR_IE_ASSOCREQ_FLAG 0x20 +/* vendor IE in IW advertisement protocol ID field */ +#define BRCMF_VNDR_IE_IWAPID_FLAG 0x40 +/* allow custom IE id */ +#define BRCMF_VNDR_IE_CUSTOM_FLAG 0x100 + +/* P2P Action Frames flags (spec ordered) */ +#define BRCMF_VNDR_IE_GONREQ_FLAG 0x001000 +#define BRCMF_VNDR_IE_GONRSP_FLAG 0x002000 +#define BRCMF_VNDR_IE_GONCFM_FLAG 0x004000 +#define BRCMF_VNDR_IE_INVREQ_FLAG 0x008000 +#define BRCMF_VNDR_IE_INVRSP_FLAG 0x010000 +#define BRCMF_VNDR_IE_DISREQ_FLAG 0x020000 +#define BRCMF_VNDR_IE_DISRSP_FLAG 0x040000 +#define BRCMF_VNDR_IE_PRDREQ_FLAG 0x080000 +#define BRCMF_VNDR_IE_PRDRSP_FLAG 0x100000 + +#define BRCMF_VNDR_IE_P2PAF_SHIFT 12 + +#define BRCMF_MAX_DEFAULT_KEYS 4 + + +/** + * enum brcmf_scan_status - scan engine status + * + * @BRCMF_SCAN_STATUS_BUSY: scanning in progress on dongle. + * @BRCMF_SCAN_STATUS_ABORT: scan being aborted on dongle. + * @BRCMF_SCAN_STATUS_SUPPRESS: scanning is suppressed in driver. + */ +enum brcmf_scan_status { + BRCMF_SCAN_STATUS_BUSY, + BRCMF_SCAN_STATUS_ABORT, + BRCMF_SCAN_STATUS_SUPPRESS, +}; + +/* dongle configuration */ +struct brcmf_cfg80211_conf { + u32 frag_threshold; + u32 rts_threshold; + u32 retry_short; + u32 retry_long; + s32 tx_power; + struct ieee80211_channel channel; +}; + +/* basic structure of scan request */ +struct brcmf_cfg80211_scan_req { + struct brcmf_ssid_le ssid_le; +}; + +/* basic structure of information element */ +struct brcmf_cfg80211_ie { + u16 offset; + u8 buf[WL_TLV_INFO_MAX]; +}; + +/* security information with currently associated ap */ +struct brcmf_cfg80211_security { + u32 wpa_versions; + u32 auth_type; + u32 cipher_pairwise; + u32 cipher_group; + u32 wpa_auth; +}; + +/** + * struct brcmf_cfg80211_profile - profile information. + * + * @ssid: ssid of associated/associating ap. + * @bssid: bssid of joined/joining ibss. + * @sec: security information. + * @key: key information + */ +struct brcmf_cfg80211_profile { + struct brcmf_ssid ssid; + u8 bssid[ETH_ALEN]; + struct brcmf_cfg80211_security sec; + struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS]; +}; + +/** + * enum brcmf_vif_status - bit indices for vif status. + * + * @BRCMF_VIF_STATUS_READY: ready for operation. + * @BRCMF_VIF_STATUS_CONNECTING: connect/join in progress. + * @BRCMF_VIF_STATUS_CONNECTED: connected/joined succesfully. + * @BRCMF_VIF_STATUS_DISCONNECTING: disconnect/disable in progress. + * @BRCMF_VIF_STATUS_AP_CREATED: AP operation started. + */ +enum brcmf_vif_status { + BRCMF_VIF_STATUS_READY, + BRCMF_VIF_STATUS_CONNECTING, + BRCMF_VIF_STATUS_CONNECTED, + BRCMF_VIF_STATUS_DISCONNECTING, + BRCMF_VIF_STATUS_AP_CREATED +}; + +/** + * struct vif_saved_ie - holds saved IEs for a virtual interface. + * + * @probe_req_ie: IE info for probe request. + * @probe_res_ie: IE info for probe response. + * @beacon_ie: IE info for beacon frame. + * @probe_req_ie_len: IE info length for probe request. + * @probe_res_ie_len: IE info length for probe response. + * @beacon_ie_len: IE info length for beacon frame. + */ +struct vif_saved_ie { + u8 probe_req_ie[IE_MAX_LEN]; + u8 probe_res_ie[IE_MAX_LEN]; + u8 beacon_ie[IE_MAX_LEN]; + u8 assoc_req_ie[IE_MAX_LEN]; + u32 probe_req_ie_len; + u32 probe_res_ie_len; + u32 beacon_ie_len; + u32 assoc_req_ie_len; +}; + +/** + * struct brcmf_cfg80211_vif - virtual interface specific information. + * + * @ifp: lower layer interface pointer + * @wdev: wireless device. + * @profile: profile information. + * @roam_off: roaming state. + * @sme_state: SME state using enum brcmf_vif_status bits. + * @pm_block: power-management blocked. + * @list: linked list. + * @mgmt_rx_reg: registered rx mgmt frame types. + * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P). + */ +struct brcmf_cfg80211_vif { + struct brcmf_if *ifp; + struct wireless_dev wdev; + struct brcmf_cfg80211_profile profile; + s32 roam_off; + unsigned long sme_state; + bool pm_block; + struct vif_saved_ie saved_ie; + struct list_head list; + u16 mgmt_rx_reg; + bool mbss; + int is_11d; +}; + +/* association inform */ +struct brcmf_cfg80211_connect_info { + u8 *req_ie; + s32 req_ie_len; + u8 *resp_ie; + s32 resp_ie_len; +}; + +/* assoc ie length */ +struct brcmf_cfg80211_assoc_ielen_le { + __le32 req_len; + __le32 resp_len; +}; + +/* wpa2 pmk list */ +struct brcmf_cfg80211_pmk_list { + struct pmkid_list pmkids; + struct pmkid foo[MAXPMKID - 1]; +}; + +/* dongle escan state */ +enum wl_escan_state { + WL_ESCAN_STATE_IDLE, + WL_ESCAN_STATE_SCANNING +}; + +struct escan_info { + u32 escan_state; + u8 escan_buf[WL_ESCAN_BUF_SIZE]; + struct wiphy *wiphy; + struct brcmf_if *ifp; + s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, + struct cfg80211_scan_request *request, u16 action); +}; + +/** + * struct brcmf_pno_param_le - PNO scan configuration parameters + * + * @version: PNO parameters version. + * @scan_freq: scan frequency. + * @lost_network_timeout: #sec. to declare discovered network as lost. + * @flags: Bit field to control features of PFN such as sort criteria auto + * enable switch and background scan. + * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort + * criteria. + * @bestn: number of best networks in each scan. + * @mscan: number of scans recorded. + * @repeat: minimum number of scan intervals before scan frequency changes + * in adaptive scan. + * @exp: exponent of 2 for maximum scan interval. + * @slow_freq: slow scan period. + */ +struct brcmf_pno_param_le { + __le32 version; + __le32 scan_freq; + __le32 lost_network_timeout; + __le16 flags; + __le16 rssi_margin; + u8 bestn; + u8 mscan; + u8 repeat; + u8 exp; + __le32 slow_freq; +}; + +/** + * struct brcmf_pno_net_param_le - scan parameters per preferred network. + * + * @ssid: ssid name and its length. + * @flags: bit2: hidden. + * @infra: BSS vs IBSS. + * @auth: Open vs Closed. + * @wpa_auth: WPA type. + * @wsec: wsec value. + */ +struct brcmf_pno_net_param_le { + struct brcmf_ssid_le ssid; + __le32 flags; + __le32 infra; + __le32 auth; + __le32 wpa_auth; + __le32 wsec; +}; + +/** + * struct brcmf_pno_net_info_le - information per found network. + * + * @bssid: BSS network identifier. + * @channel: channel number only. + * @SSID_len: length of ssid. + * @SSID: ssid characters. + * @RSSI: receive signal strength (in dBm). + * @timestamp: age in seconds. + */ +struct brcmf_pno_net_info_le { + u8 bssid[ETH_ALEN]; + u8 channel; + u8 SSID_len; + u8 SSID[32]; + __le16 RSSI; + __le16 timestamp; +}; + +/** + * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event. + * + * @version: PNO version identifier. + * @status: indicates completion status of PNO scan. + * @count: amount of brcmf_pno_net_info_le entries appended. + */ +struct brcmf_pno_scanresults_le { + __le32 version; + __le32 status; + __le32 count; +}; + +/** + * struct brcmf_cfg80211_vif_event - virtual interface event information. + * + * @vif_wq: waitqueue awaiting interface event from firmware. + * @vif_event_lock: protects other members in this structure. + * @vif_complete: completion for net attach. + * @action: either add, change, or delete. + * @vif: virtual interface object related to the event. + */ +struct brcmf_cfg80211_vif_event { + wait_queue_head_t vif_wq; + struct mutex vif_event_lock; + u8 action; + struct brcmf_cfg80211_vif *vif; +}; + +/** + * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface + * + * @wiphy: wiphy object for cfg80211 interface. + * @conf: dongle configuration. + * @p2p: peer-to-peer specific information. + * @btcoex: Bluetooth coexistence information. + * @scan_request: cfg80211 scan request object. + * @usr_sync: mainly for dongle up/down synchronization. + * @bss_list: bss_list holding scanned ap information. + * @scan_req_int: internal scan request object. + * @bss_info: bss information for cfg80211 layer. + * @ie: information element object for internal purpose. + * @conn_info: association info. + * @pmk_list: wpa2 pmk list. + * @scan_status: scan activity on the dongle. + * @pub: common driver information. + * @channel: current channel. + * @active_scan: current scan mode. + * @sched_escan: e-scan for scheduled scan support running. + * @ibss_starter: indicates this sta is ibss starter. + * @pwr_save: indicate whether dongle to support power save mode. + * @dongle_up: indicate whether dongle up or not. + * @roam_on: on/off switch for dongle self-roaming. + * @scan_tried: indicates if first scan attempted. + * @dcmd_buf: dcmd buffer. + * @extra_buf: mainly to grab assoc information. + * @debugfsdir: debugfs folder for this device. + * @escan_info: escan information. + * @escan_timeout: Timer for catch scan timeout. + * @escan_timeout_work: scan timeout worker. + * @escan_ioctl_buf: dongle command buffer for escan commands. + * @vif_list: linked list of vif instances. + * @vif_cnt: number of vif instances. + * @vif_event: vif event signalling. + * @wowl_enabled; set during suspend, is wowl used. + * @pre_wowl_pmmode: intermediate storage of pm mode during wowl. + */ +struct brcmf_cfg80211_info { + struct wiphy *wiphy; + struct brcmf_cfg80211_conf *conf; + struct brcmf_p2p_info p2p; + struct brcmf_btcoex_info *btcoex; + struct cfg80211_scan_request *scan_request; + struct mutex usr_sync; + struct brcmf_cfg80211_scan_req scan_req_int; + struct wl_cfg80211_bss_info *bss_info; + struct brcmf_cfg80211_ie ie; + struct brcmf_cfg80211_connect_info conn_info; + struct brcmf_cfg80211_pmk_list *pmk_list; + unsigned long scan_status; + struct brcmf_pub *pub; + u32 channel; + bool active_scan; + bool sched_escan; + bool ibss_starter; + bool pwr_save; + bool dongle_up; + bool scan_tried; + u8 *dcmd_buf; + u8 *extra_buf; + struct dentry *debugfsdir; + struct escan_info escan_info; + struct timer_list escan_timeout; + struct work_struct escan_timeout_work; + u8 *escan_ioctl_buf; + struct list_head vif_list; + struct brcmf_cfg80211_vif_event vif_event; + struct completion vif_disabled; + struct brcmu_d11inf d11inf; + bool wowl_enabled; + u32 pre_wowl_pmmode; + struct brcmf_assoclist_le assoclist; +}; + +/** + * struct brcmf_tlv - tag_ID/length/value_buffer tuple. + * + * @id: tag identifier. + * @len: number of bytes in value buffer. + * @data: value buffer. + */ +struct brcmf_tlv { + u8 id; + u8 len; + u8 data[1]; +}; + +static inline struct wiphy *cfg_to_wiphy(struct brcmf_cfg80211_info *cfg) +{ + return cfg->wiphy; +} + +static inline struct brcmf_cfg80211_info *wiphy_to_cfg(struct wiphy *w) +{ + return (struct brcmf_cfg80211_info *)(wiphy_priv(w)); +} + +static inline struct brcmf_cfg80211_info *wdev_to_cfg(struct wireless_dev *wd) +{ + return (struct brcmf_cfg80211_info *)(wdev_priv(wd)); +} + +static inline +struct net_device *cfg_to_ndev(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_cfg80211_vif *vif; + vif = list_first_entry(&cfg->vif_list, struct brcmf_cfg80211_vif, list); + return vif->wdev.netdev; +} + +static inline struct brcmf_cfg80211_info *ndev_to_cfg(struct net_device *ndev) +{ + return wdev_to_cfg(ndev->ieee80211_ptr); +} + +static inline struct brcmf_cfg80211_profile *ndev_to_prof(struct net_device *nd) +{ + struct brcmf_if *ifp = netdev_priv(nd); + return &ifp->vif->profile; +} + +static inline struct brcmf_cfg80211_vif *ndev_to_vif(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + return ifp->vif; +} + +static inline struct +brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg) +{ + return &cfg->conn_info; +} + +struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, + struct device *busdev, + bool p2pdev_forced); +void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg); +s32 brcmf_cfg80211_up(struct net_device *ndev); +s32 brcmf_cfg80211_down(struct net_device *ndev); +enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp); + +struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, + enum nl80211_iftype type, + bool pm_block); +void brcmf_free_vif(struct brcmf_cfg80211_vif *vif); + +s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, + const u8 *vndr_ie_buf, u32 vndr_ie_len); +s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif); +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key); +u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, + struct ieee80211_channel *ch); +bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, + unsigned long state); +void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, + struct brcmf_cfg80211_vif *vif); +bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg); +int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, + u8 action, ulong timeout); +s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp, bool aborted, + bool fw_abort); +void brcmf_set_mpc(struct brcmf_if *ndev, int mpc); +void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg); +void brcmf_cfg80211_free_netdev(struct net_device *ndev); + +#endif /* BRCMFMAC_CFG80211_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c new file mode 100644 index 000000000000..f04833db2fd0 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -0,0 +1,1331 @@ +/* + * Copyright (c) 2014 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 +#include +#include + +#include +#include +#include +#include +#include +#include "debug.h" +#include "chip.h" + +/* SOC Interconnect types (aka chip types) */ +#define SOCI_SB 0 +#define SOCI_AI 1 + +/* PL-368 DMP definitions */ +#define DMP_DESC_TYPE_MSK 0x0000000F +#define DMP_DESC_EMPTY 0x00000000 +#define DMP_DESC_VALID 0x00000001 +#define DMP_DESC_COMPONENT 0x00000001 +#define DMP_DESC_MASTER_PORT 0x00000003 +#define DMP_DESC_ADDRESS 0x00000005 +#define DMP_DESC_ADDRSIZE_GT32 0x00000008 +#define DMP_DESC_EOT 0x0000000F + +#define DMP_COMP_DESIGNER 0xFFF00000 +#define DMP_COMP_DESIGNER_S 20 +#define DMP_COMP_PARTNUM 0x000FFF00 +#define DMP_COMP_PARTNUM_S 8 +#define DMP_COMP_CLASS 0x000000F0 +#define DMP_COMP_CLASS_S 4 +#define DMP_COMP_REVISION 0xFF000000 +#define DMP_COMP_REVISION_S 24 +#define DMP_COMP_NUM_SWRAP 0x00F80000 +#define DMP_COMP_NUM_SWRAP_S 19 +#define DMP_COMP_NUM_MWRAP 0x0007C000 +#define DMP_COMP_NUM_MWRAP_S 14 +#define DMP_COMP_NUM_SPORT 0x00003E00 +#define DMP_COMP_NUM_SPORT_S 9 +#define DMP_COMP_NUM_MPORT 0x000001F0 +#define DMP_COMP_NUM_MPORT_S 4 + +#define DMP_MASTER_PORT_UID 0x0000FF00 +#define DMP_MASTER_PORT_UID_S 8 +#define DMP_MASTER_PORT_NUM 0x000000F0 +#define DMP_MASTER_PORT_NUM_S 4 + +#define DMP_SLAVE_ADDR_BASE 0xFFFFF000 +#define DMP_SLAVE_ADDR_BASE_S 12 +#define DMP_SLAVE_PORT_NUM 0x00000F00 +#define DMP_SLAVE_PORT_NUM_S 8 +#define DMP_SLAVE_TYPE 0x000000C0 +#define DMP_SLAVE_TYPE_S 6 +#define DMP_SLAVE_TYPE_SLAVE 0 +#define DMP_SLAVE_TYPE_BRIDGE 1 +#define DMP_SLAVE_TYPE_SWRAP 2 +#define DMP_SLAVE_TYPE_MWRAP 3 +#define DMP_SLAVE_SIZE_TYPE 0x00000030 +#define DMP_SLAVE_SIZE_TYPE_S 4 +#define DMP_SLAVE_SIZE_4K 0 +#define DMP_SLAVE_SIZE_8K 1 +#define DMP_SLAVE_SIZE_16K 2 +#define DMP_SLAVE_SIZE_DESC 3 + +/* EROM CompIdentB */ +#define CIB_REV_MASK 0xff000000 +#define CIB_REV_SHIFT 24 + +/* 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 + +/* chip core base & ramsize */ +/* bcm4329 */ +/* SDIO device core, ID 0x829 */ +#define BCM4329_CORE_BUS_BASE 0x18011000 +/* internal memory core, ID 0x80e */ +#define BCM4329_CORE_SOCRAM_BASE 0x18003000 +/* ARM Cortex M3 core, ID 0x82a */ +#define BCM4329_CORE_ARM_BASE 0x18002000 + +/* Max possibly supported memory size (limited by IO mapped memory) */ +#define BRCMF_CHIP_MAX_MEMSIZE (4 * 1024 * 1024) + +#define CORE_SB(base, field) \ + (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) +#define SBCOREREV(sbidh) \ + ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ + ((sbidh) & SSB_IDHIGH_RCLO)) + +struct sbconfig { + u32 PAD[2]; + u32 sbipsflag; /* initiator port ocp slave flag */ + u32 PAD[3]; + u32 sbtpsflag; /* target port ocp slave flag */ + u32 PAD[11]; + u32 sbtmerrloga; /* (sonics >= 2.3) */ + u32 PAD; + u32 sbtmerrlog; /* (sonics >= 2.3) */ + u32 PAD[3]; + u32 sbadmatch3; /* address match3 */ + u32 PAD; + u32 sbadmatch2; /* address match2 */ + u32 PAD; + u32 sbadmatch1; /* address match1 */ + u32 PAD[7]; + u32 sbimstate; /* initiator agent state */ + u32 sbintvec; /* interrupt mask */ + u32 sbtmstatelow; /* target state */ + u32 sbtmstatehigh; /* target state */ + u32 sbbwa0; /* bandwidth allocation table0 */ + u32 PAD; + u32 sbimconfiglow; /* initiator configuration */ + u32 sbimconfighigh; /* initiator configuration */ + u32 sbadmatch0; /* address match0 */ + u32 PAD; + u32 sbtmconfiglow; /* target configuration */ + u32 sbtmconfighigh; /* target configuration */ + u32 sbbconfig; /* broadcast configuration */ + u32 PAD; + u32 sbbstate; /* broadcast state */ + u32 PAD[3]; + u32 sbactcnfg; /* activate configuration */ + u32 PAD[3]; + u32 sbflagst; /* current sbflags */ + u32 PAD[3]; + u32 sbidlow; /* identification */ + u32 sbidhigh; /* identification */ +}; + +/* bankidx and bankinfo reg defines corerev >= 8 */ +#define SOCRAM_BANKINFO_RETNTRAM_MASK 0x00010000 +#define SOCRAM_BANKINFO_SZMASK 0x0000007f +#define SOCRAM_BANKIDX_ROM_MASK 0x00000100 + +#define SOCRAM_BANKIDX_MEMTYPE_SHIFT 8 +/* socram bankinfo memtype */ +#define SOCRAM_MEMTYPE_RAM 0 +#define SOCRAM_MEMTYPE_R0M 1 +#define SOCRAM_MEMTYPE_DEVRAM 2 + +#define SOCRAM_BANKINFO_SZBASE 8192 +#define SRCI_LSS_MASK 0x00f00000 +#define SRCI_LSS_SHIFT 20 +#define SRCI_SRNB_MASK 0xf0 +#define SRCI_SRNB_SHIFT 4 +#define SRCI_SRBSZ_MASK 0xf +#define SRCI_SRBSZ_SHIFT 0 +#define SR_BSZ_BASE 14 + +struct sbsocramregs { + u32 coreinfo; + u32 bwalloc; + u32 extracoreinfo; + u32 biststat; + u32 bankidx; + u32 standbyctrl; + + u32 errlogstatus; /* rev 6 */ + u32 errlogaddr; /* rev 6 */ + /* used for patching rev 3 & 5 */ + u32 cambankidx; + u32 cambankstandbyctrl; + u32 cambankpatchctrl; + u32 cambankpatchtblbaseaddr; + u32 cambankcmdreg; + u32 cambankdatareg; + u32 cambankmaskreg; + u32 PAD[1]; + u32 bankinfo; /* corev 8 */ + u32 bankpda; + u32 PAD[14]; + u32 extmemconfig; + u32 extmemparitycsr; + u32 extmemparityerrdata; + u32 extmemparityerrcnt; + u32 extmemwrctrlandsize; + u32 PAD[84]; + u32 workaround; + u32 pwrctl; /* corerev >= 2 */ + u32 PAD[133]; + u32 sr_control; /* corerev >= 15 */ + u32 sr_status; /* corerev >= 15 */ + u32 sr_address; /* corerev >= 15 */ + u32 sr_data; /* corerev >= 15 */ +}; + +#define SOCRAMREGOFFS(_f) offsetof(struct sbsocramregs, _f) +#define SYSMEMREGOFFS(_f) offsetof(struct sbsocramregs, _f) + +#define ARMCR4_CAP (0x04) +#define ARMCR4_BANKIDX (0x40) +#define ARMCR4_BANKINFO (0x44) +#define ARMCR4_BANKPDA (0x4C) + +#define ARMCR4_TCBBNB_MASK 0xf0 +#define ARMCR4_TCBBNB_SHIFT 4 +#define ARMCR4_TCBANB_MASK 0xf +#define ARMCR4_TCBANB_SHIFT 0 + +#define ARMCR4_BSZ_MASK 0x3f +#define ARMCR4_BSZ_MULT 8192 + +struct brcmf_core_priv { + struct brcmf_core pub; + u32 wrapbase; + struct list_head list; + struct brcmf_chip_priv *chip; +}; + +struct brcmf_chip_priv { + struct brcmf_chip pub; + const struct brcmf_buscore_ops *ops; + void *ctx; + /* assured first core is chipcommon, second core is buscore */ + struct list_head cores; + u16 num_cores; + + bool (*iscoreup)(struct brcmf_core_priv *core); + void (*coredisable)(struct brcmf_core_priv *core, u32 prereset, + u32 reset); + void (*resetcore)(struct brcmf_core_priv *core, u32 prereset, u32 reset, + u32 postreset); +}; + +static void brcmf_chip_sb_corerev(struct brcmf_chip_priv *ci, + struct brcmf_core *core) +{ + u32 regdata; + + regdata = ci->ops->read32(ci->ctx, CORE_SB(core->base, sbidhigh)); + core->rev = SBCOREREV(regdata); +} + +static bool brcmf_chip_sb_iscoreup(struct brcmf_core_priv *core) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + u32 address; + + ci = core->chip; + address = CORE_SB(core->pub.base, sbtmstatelow); + regdata = ci->ops->read32(ci->ctx, address); + regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | + SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); + return SSB_TMSLOW_CLOCK == regdata; +} + +static bool brcmf_chip_ai_iscoreup(struct brcmf_core_priv *core) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + bool ret; + + ci = core->chip; + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); + ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; + + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL); + ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); + + return ret; +} + +static void brcmf_chip_sb_coredisable(struct brcmf_core_priv *core, + u32 prereset, u32 reset) +{ + struct brcmf_chip_priv *ci; + u32 val, base; + + ci = core->chip; + base = core->pub.base; + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + if (val & SSB_TMSLOW_RESET) + return; + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + if ((val & SSB_TMSLOW_CLOCK) != 0) { + /* + * set target reject and spin until busy is clear + * (preserve core-specific bits) + */ + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + val | SSB_TMSLOW_REJECT); + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); + SPINWAIT((ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)) + & SSB_TMSHIGH_BUSY), 100000); + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)); + if (val & SSB_TMSHIGH_BUSY) + brcmf_err("core state still busy\n"); + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow)); + if (val & SSB_IDLOW_INITIATOR) { + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); + val |= SSB_IMSTATE_REJECT; + ci->ops->write32(ci->ctx, + CORE_SB(base, sbimstate), val); + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); + udelay(1); + SPINWAIT((ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)) & + SSB_IMSTATE_BUSY), 100000); + } + + /* set reset and reject while enabling the clocks */ + val = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | + SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), val); + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(10); + + /* clear the initiator reject bit */ + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow)); + if (val & SSB_IDLOW_INITIATOR) { + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); + val &= ~SSB_IMSTATE_REJECT; + ci->ops->write32(ci->ctx, + CORE_SB(base, sbimstate), val); + } + } + + /* leave reset and reject asserted */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET)); + udelay(1); +} + +static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core, + u32 prereset, u32 reset) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + + ci = core->chip; + + /* if core is already in reset, skip reset */ + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL); + if ((regdata & BCMA_RESET_CTL_RESET) != 0) + goto in_reset_configure; + + /* configure reset */ + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + prereset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); + + /* put in reset */ + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, + BCMA_RESET_CTL_RESET); + usleep_range(10, 20); + + /* wait till reset is 1 */ + SPINWAIT(ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) != + BCMA_RESET_CTL_RESET, 300); + +in_reset_configure: + /* in-reset configure */ + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); +} + +static void brcmf_chip_sb_resetcore(struct brcmf_core_priv *core, u32 prereset, + u32 reset, u32 postreset) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + u32 base; + + ci = core->chip; + base = core->pub.base; + /* + * Must do the disable sequence first to work for + * arbitrary current core state. + */ + brcmf_chip_sb_coredisable(core, 0, 0); + + /* + * Now do the initialization sequence. + * set reset while enabling the clock and + * forcing them on throughout the core + */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | + SSB_TMSLOW_RESET); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); + + /* clear any serror */ + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)); + if (regdata & SSB_TMSHIGH_SERR) + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatehigh), 0); + + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbimstate)); + if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) { + regdata &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO); + ci->ops->write32(ci->ctx, CORE_SB(base, sbimstate), regdata); + } + + /* clear reset and allow it to propagate throughout the core */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); + + /* leave clock enabled */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_CLOCK); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); +} + +static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset, + u32 reset, u32 postreset) +{ + struct brcmf_chip_priv *ci; + int count; + + ci = core->chip; + + /* must disable first to work for arbitrary current core state */ + brcmf_chip_ai_coredisable(core, prereset, reset); + + count = 0; + while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) & + BCMA_RESET_CTL_RESET) { + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, 0); + count++; + if (count > 50) + break; + usleep_range(40, 60); + } + + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + postreset | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); +} + +static char *brcmf_chip_name(uint chipid, char *buf, uint len) +{ + const char *fmt; + + fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; + snprintf(buf, len, fmt, chipid); + return buf; +} + +static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci, + u16 coreid, u32 base, + u32 wrapbase) +{ + struct brcmf_core_priv *core; + + core = kzalloc(sizeof(*core), GFP_KERNEL); + if (!core) + return ERR_PTR(-ENOMEM); + + core->pub.id = coreid; + core->pub.base = base; + core->chip = ci; + core->wrapbase = wrapbase; + + list_add_tail(&core->list, &ci->cores); + return &core->pub; +} + +/* safety check for chipinfo */ +static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) +{ + struct brcmf_core_priv *core; + bool need_socram = false; + bool has_socram = false; + bool cpu_found = false; + int idx = 1; + + list_for_each_entry(core, &ci->cores, list) { + brcmf_dbg(INFO, " [%-2d] core 0x%x:%-2d base 0x%08x wrap 0x%08x\n", + idx++, core->pub.id, core->pub.rev, core->pub.base, + core->wrapbase); + + switch (core->pub.id) { + case BCMA_CORE_ARM_CM3: + cpu_found = true; + need_socram = true; + break; + case BCMA_CORE_INTERNAL_MEM: + has_socram = true; + break; + case BCMA_CORE_ARM_CR4: + cpu_found = true; + break; + case BCMA_CORE_ARM_CA7: + cpu_found = true; + break; + default: + break; + } + } + + if (!cpu_found) { + brcmf_err("CPU core not detected\n"); + return -ENXIO; + } + /* check RAM core presence for ARM CM3 core */ + if (need_socram && !has_socram) { + brcmf_err("RAM core not provided with ARM CM3 core\n"); + return -ENODEV; + } + return 0; +} + +static u32 brcmf_chip_core_read32(struct brcmf_core_priv *core, u16 reg) +{ + return core->chip->ops->read32(core->chip->ctx, core->pub.base + reg); +} + +static void brcmf_chip_core_write32(struct brcmf_core_priv *core, + u16 reg, u32 val) +{ + core->chip->ops->write32(core->chip->ctx, core->pub.base + reg, val); +} + +static bool brcmf_chip_socram_banksize(struct brcmf_core_priv *core, u8 idx, + u32 *banksize) +{ + u32 bankinfo; + u32 bankidx = (SOCRAM_MEMTYPE_RAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); + + bankidx |= idx; + brcmf_chip_core_write32(core, SOCRAMREGOFFS(bankidx), bankidx); + bankinfo = brcmf_chip_core_read32(core, SOCRAMREGOFFS(bankinfo)); + *banksize = (bankinfo & SOCRAM_BANKINFO_SZMASK) + 1; + *banksize *= SOCRAM_BANKINFO_SZBASE; + return !!(bankinfo & SOCRAM_BANKINFO_RETNTRAM_MASK); +} + +static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, + u32 *srsize) +{ + u32 coreinfo; + uint nb, banksize, lss; + bool retent; + int i; + + *ramsize = 0; + *srsize = 0; + + if (WARN_ON(sr->pub.rev < 4)) + return; + + if (!brcmf_chip_iscoreup(&sr->pub)) + brcmf_chip_resetcore(&sr->pub, 0, 0, 0); + + /* Get info for determining size */ + coreinfo = brcmf_chip_core_read32(sr, SOCRAMREGOFFS(coreinfo)); + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + + if ((sr->pub.rev <= 7) || (sr->pub.rev == 12)) { + banksize = (coreinfo & SRCI_SRBSZ_MASK); + lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT; + if (lss != 0) + nb--; + *ramsize = nb * (1 << (banksize + SR_BSZ_BASE)); + if (lss != 0) + *ramsize += (1 << ((lss - 1) + SR_BSZ_BASE)); + } else { + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + for (i = 0; i < nb; i++) { + retent = brcmf_chip_socram_banksize(sr, i, &banksize); + *ramsize += banksize; + if (retent) + *srsize += banksize; + } + } + + /* hardcoded save&restore memory sizes */ + switch (sr->chip->pub.chip) { + case BRCM_CC_4334_CHIP_ID: + if (sr->chip->pub.chiprev < 2) + *srsize = (32 * 1024); + break; + case BRCM_CC_43430_CHIP_ID: + /* assume sr for now as we can not check + * firmware sr capability at this point. + */ + *srsize = (64 * 1024); + break; + default: + break; + } +} + +/** Return the SYS MEM size */ +static u32 brcmf_chip_sysmem_ramsize(struct brcmf_core_priv *sysmem) +{ + u32 memsize = 0; + u32 coreinfo; + u32 idx; + u32 nb; + u32 banksize; + + if (!brcmf_chip_iscoreup(&sysmem->pub)) + brcmf_chip_resetcore(&sysmem->pub, 0, 0, 0); + + coreinfo = brcmf_chip_core_read32(sysmem, SYSMEMREGOFFS(coreinfo)); + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + + for (idx = 0; idx < nb; idx++) { + brcmf_chip_socram_banksize(sysmem, idx, &banksize); + memsize += banksize; + } + + return memsize; +} + +/** Return the TCM-RAM size of the ARMCR4 core. */ +static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4) +{ + u32 corecap; + u32 memsize = 0; + u32 nab; + u32 nbb; + u32 totb; + u32 bxinfo; + u32 idx; + + corecap = brcmf_chip_core_read32(cr4, ARMCR4_CAP); + + nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT; + nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT; + totb = nab + nbb; + + for (idx = 0; idx < totb; idx++) { + brcmf_chip_core_write32(cr4, ARMCR4_BANKIDX, idx); + bxinfo = brcmf_chip_core_read32(cr4, ARMCR4_BANKINFO); + memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT; + } + + return memsize; +} + +static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) +{ + switch (ci->pub.chip) { + case BRCM_CC_4345_CHIP_ID: + return 0x198000; + case BRCM_CC_4335_CHIP_ID: + case BRCM_CC_4339_CHIP_ID: + case BRCM_CC_4350_CHIP_ID: + case BRCM_CC_4354_CHIP_ID: + case BRCM_CC_4356_CHIP_ID: + case BRCM_CC_43567_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: + case BRCM_CC_43570_CHIP_ID: + case BRCM_CC_4358_CHIP_ID: + case BRCM_CC_43602_CHIP_ID: + case BRCM_CC_4371_CHIP_ID: + return 0x180000; + case BRCM_CC_4365_CHIP_ID: + case BRCM_CC_4366_CHIP_ID: + return 0x200000; + default: + brcmf_err("unknown chip: %s\n", ci->pub.name); + break; + } + return 0; +} + +static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) +{ + struct brcmf_core_priv *mem_core; + struct brcmf_core *mem; + + mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_ARM_CR4); + if (mem) { + mem_core = container_of(mem, struct brcmf_core_priv, pub); + ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core); + ci->pub.rambase = brcmf_chip_tcm_rambase(ci); + if (!ci->pub.rambase) { + brcmf_err("RAM base not provided with ARM CR4 core\n"); + return -EINVAL; + } + } else { + mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_SYS_MEM); + if (mem) { + mem_core = container_of(mem, struct brcmf_core_priv, + pub); + ci->pub.ramsize = brcmf_chip_sysmem_ramsize(mem_core); + ci->pub.rambase = brcmf_chip_tcm_rambase(ci); + if (!ci->pub.rambase) { + brcmf_err("RAM base not provided with ARM CA7 core\n"); + return -EINVAL; + } + } else { + mem = brcmf_chip_get_core(&ci->pub, + BCMA_CORE_INTERNAL_MEM); + if (!mem) { + brcmf_err("No memory cores found\n"); + return -ENOMEM; + } + mem_core = container_of(mem, struct brcmf_core_priv, + pub); + brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize, + &ci->pub.srsize); + } + } + brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n", + ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize, + ci->pub.srsize, ci->pub.srsize); + + if (!ci->pub.ramsize) { + brcmf_err("RAM size is undetermined\n"); + return -ENOMEM; + } + + if (ci->pub.ramsize > BRCMF_CHIP_MAX_MEMSIZE) { + brcmf_err("RAM size is incorrect\n"); + return -ENOMEM; + } + + return 0; +} + +static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr, + u8 *type) +{ + u32 val; + + /* read next descriptor */ + val = ci->ops->read32(ci->ctx, *eromaddr); + *eromaddr += 4; + + if (!type) + return val; + + /* determine descriptor type */ + *type = (val & DMP_DESC_TYPE_MSK); + if ((*type & ~DMP_DESC_ADDRSIZE_GT32) == DMP_DESC_ADDRESS) + *type = DMP_DESC_ADDRESS; + + return val; +} + +static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr, + u32 *regbase, u32 *wrapbase) +{ + u8 desc; + u32 val; + u8 mpnum = 0; + u8 stype, sztype, wraptype; + + *regbase = 0; + *wrapbase = 0; + + val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc); + if (desc == DMP_DESC_MASTER_PORT) { + mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S; + wraptype = DMP_SLAVE_TYPE_MWRAP; + } else if (desc == DMP_DESC_ADDRESS) { + /* revert erom address */ + *eromaddr -= 4; + wraptype = DMP_SLAVE_TYPE_SWRAP; + } else { + *eromaddr -= 4; + return -EILSEQ; + } + + do { + /* locate address descriptor */ + do { + val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc); + /* unexpected table end */ + if (desc == DMP_DESC_EOT) { + *eromaddr -= 4; + return -EFAULT; + } + } while (desc != DMP_DESC_ADDRESS); + + /* skip upper 32-bit address descriptor */ + if (val & DMP_DESC_ADDRSIZE_GT32) + brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + + sztype = (val & DMP_SLAVE_SIZE_TYPE) >> DMP_SLAVE_SIZE_TYPE_S; + + /* next size descriptor can be skipped */ + if (sztype == DMP_SLAVE_SIZE_DESC) { + val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + /* skip upper size descriptor if present */ + if (val & DMP_DESC_ADDRSIZE_GT32) + brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + } + + /* only look for 4K register regions */ + if (sztype != DMP_SLAVE_SIZE_4K) + continue; + + stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S; + + /* only regular slave and wrapper */ + if (*regbase == 0 && stype == DMP_SLAVE_TYPE_SLAVE) + *regbase = val & DMP_SLAVE_ADDR_BASE; + if (*wrapbase == 0 && stype == wraptype) + *wrapbase = val & DMP_SLAVE_ADDR_BASE; + } while (*regbase == 0 || *wrapbase == 0); + + return 0; +} + +static +int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci) +{ + struct brcmf_core *core; + u32 eromaddr; + u8 desc_type = 0; + u32 val; + u16 id; + u8 nmp, nsp, nmw, nsw, rev; + u32 base, wrap; + int err; + + eromaddr = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, eromptr)); + + while (desc_type != DMP_DESC_EOT) { + val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type); + if (!(val & DMP_DESC_VALID)) + continue; + + if (desc_type == DMP_DESC_EMPTY) + continue; + + /* need a component descriptor */ + if (desc_type != DMP_DESC_COMPONENT) + continue; + + id = (val & DMP_COMP_PARTNUM) >> DMP_COMP_PARTNUM_S; + + /* next descriptor must be component as well */ + val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type); + if (WARN_ON((val & DMP_DESC_TYPE_MSK) != DMP_DESC_COMPONENT)) + return -EFAULT; + + /* only look at cores with master port(s) */ + nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S; + nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S; + nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S; + nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S; + rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S; + + /* need core with ports */ + if (nmw + nsw == 0) + continue; + + /* try to obtain register address info */ + err = brcmf_chip_dmp_get_regaddr(ci, &eromaddr, &base, &wrap); + if (err) + continue; + + /* finally a core to be added */ + core = brcmf_chip_add_core(ci, id, base, wrap); + if (IS_ERR(core)) + return PTR_ERR(core); + + core->rev = rev; + } + + return 0; +} + +static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) +{ + struct brcmf_core *core; + u32 regdata; + u32 socitype; + int ret; + + /* Get CC core rev + * 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. + */ + regdata = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, chipid)); + ci->pub.chip = regdata & CID_ID_MASK; + ci->pub.chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; + socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; + + brcmf_chip_name(ci->pub.chip, ci->pub.name, sizeof(ci->pub.name)); + brcmf_dbg(INFO, "found %s chip: BCM%s, rev=%d\n", + socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name, + ci->pub.chiprev); + + if (socitype == SOCI_SB) { + if (ci->pub.chip != BRCM_CC_4329_CHIP_ID) { + brcmf_err("SB chip is not supported\n"); + return -ENODEV; + } + ci->iscoreup = brcmf_chip_sb_iscoreup; + ci->coredisable = brcmf_chip_sb_coredisable; + ci->resetcore = brcmf_chip_sb_resetcore; + + core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON, + SI_ENUM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, + BCM4329_CORE_BUS_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, + BCM4329_CORE_SOCRAM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, + BCM4329_CORE_ARM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + + core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0); + brcmf_chip_sb_corerev(ci, core); + } else if (socitype == SOCI_AI) { + ci->iscoreup = brcmf_chip_ai_iscoreup; + ci->coredisable = brcmf_chip_ai_coredisable; + ci->resetcore = brcmf_chip_ai_resetcore; + + brcmf_chip_dmp_erom_scan(ci); + } else { + brcmf_err("chip backplane type %u is not supported\n", + socitype); + return -ENODEV; + } + + ret = brcmf_chip_cores_check(ci); + if (ret) + return ret; + + /* assure chip is passive for core access */ + brcmf_chip_set_passive(&ci->pub); + + /* Call bus specific reset function now. Cores have been determined + * but further access may require a chip specific reset at this point. + */ + if (ci->ops->reset) { + ci->ops->reset(ci->ctx, &ci->pub); + brcmf_chip_set_passive(&ci->pub); + } + + return brcmf_chip_get_raminfo(ci); +} + +static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) +{ + struct brcmf_core *core; + struct brcmf_core_priv *cpu; + u32 val; + + + core = brcmf_chip_get_core(&chip->pub, id); + if (!core) + return; + + switch (id) { + case BCMA_CORE_ARM_CM3: + brcmf_chip_coredisable(core, 0, 0); + break; + case BCMA_CORE_ARM_CR4: + case BCMA_CORE_ARM_CA7: + cpu = container_of(core, struct brcmf_core_priv, pub); + + /* clear all IOCTL bits except HALT bit */ + val = chip->ops->read32(chip->ctx, cpu->wrapbase + BCMA_IOCTL); + val &= ARMCR4_BCMA_IOCTL_CPUHALT; + brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT, + ARMCR4_BCMA_IOCTL_CPUHALT); + break; + default: + brcmf_err("unknown id: %u\n", id); + break; + } +} + +static int brcmf_chip_setup(struct brcmf_chip_priv *chip) +{ + struct brcmf_chip *pub; + struct brcmf_core_priv *cc; + u32 base; + u32 val; + int ret = 0; + + pub = &chip->pub; + cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list); + base = cc->pub.base; + + /* get chipcommon capabilites */ + pub->cc_caps = chip->ops->read32(chip->ctx, + CORE_CC_REG(base, capabilities)); + + /* get pmu caps & rev */ + if (pub->cc_caps & CC_CAP_PMU) { + val = chip->ops->read32(chip->ctx, + CORE_CC_REG(base, pmucapabilities)); + pub->pmurev = val & PCAP_REV_MASK; + pub->pmucaps = val; + } + + brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, pmucaps=0x%x\n", + cc->pub.rev, pub->pmurev, pub->pmucaps); + + /* execute bus core specific setup */ + if (chip->ops->setup) + ret = chip->ops->setup(chip->ctx, pub); + + return ret; +} + +struct brcmf_chip *brcmf_chip_attach(void *ctx, + const struct brcmf_buscore_ops *ops) +{ + struct brcmf_chip_priv *chip; + int err = 0; + + if (WARN_ON(!ops->read32)) + err = -EINVAL; + if (WARN_ON(!ops->write32)) + err = -EINVAL; + if (WARN_ON(!ops->prepare)) + err = -EINVAL; + if (WARN_ON(!ops->activate)) + err = -EINVAL; + if (err < 0) + return ERR_PTR(-EINVAL); + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&chip->cores); + chip->num_cores = 0; + chip->ops = ops; + chip->ctx = ctx; + + err = ops->prepare(ctx); + if (err < 0) + goto fail; + + err = brcmf_chip_recognition(chip); + if (err < 0) + goto fail; + + err = brcmf_chip_setup(chip); + if (err < 0) + goto fail; + + return &chip->pub; + +fail: + brcmf_chip_detach(&chip->pub); + return ERR_PTR(err); +} + +void brcmf_chip_detach(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *core; + struct brcmf_core_priv *tmp; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + list_for_each_entry_safe(core, tmp, &chip->cores, list) { + list_del(&core->list); + kfree(core); + } + kfree(chip); +} + +struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *core; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + list_for_each_entry(core, &chip->cores, list) + if (core->pub.id == coreid) + return &core->pub; + + return NULL; +} + +struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *cc; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list); + if (WARN_ON(!cc || cc->pub.id != BCMA_CORE_CHIPCOMMON)) + return brcmf_chip_get_core(pub, BCMA_CORE_CHIPCOMMON); + return &cc->pub; +} + +bool brcmf_chip_iscoreup(struct brcmf_core *pub) +{ + struct brcmf_core_priv *core; + + core = container_of(pub, struct brcmf_core_priv, pub); + return core->chip->iscoreup(core); +} + +void brcmf_chip_coredisable(struct brcmf_core *pub, u32 prereset, u32 reset) +{ + struct brcmf_core_priv *core; + + core = container_of(pub, struct brcmf_core_priv, pub); + core->chip->coredisable(core, prereset, reset); +} + +void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset, + u32 postreset) +{ + struct brcmf_core_priv *core; + + core = container_of(pub, struct brcmf_core_priv, pub); + core->chip->resetcore(core, prereset, reset, postreset); +} + +static void +brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + struct brcmf_core_priv *sr; + + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); + brcmf_chip_resetcore(core, 0, 0, 0); + + /* disable bank #3 remap for this device */ + if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) { + sr = container_of(core, struct brcmf_core_priv, pub); + brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3); + brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0); + } +} + +static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); + if (!brcmf_chip_iscoreup(core)) { + brcmf_err("SOCRAM core is down after reset?\n"); + return false; + } + + chip->ops->activate(chip->ctx, &chip->pub, 0); + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3); + brcmf_chip_resetcore(core, 0, 0, 0); + + return true; +} + +static inline void +brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); +} + +static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec) +{ + struct brcmf_core *core; + + chip->ops->activate(chip->ctx, &chip->pub, rstvec); + + /* restore ARM */ + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4); + brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0); + + return true; +} + +static inline void +brcmf_chip_ca7_set_passive(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CA7); + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); +} + +static bool brcmf_chip_ca7_set_active(struct brcmf_chip_priv *chip, u32 rstvec) +{ + struct brcmf_core *core; + + chip->ops->activate(chip->ctx, &chip->pub, rstvec); + + /* restore ARM */ + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CA7); + brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0); + + return true; +} + +void brcmf_chip_set_passive(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core *arm; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); + if (arm) { + brcmf_chip_cr4_set_passive(chip); + return; + } + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7); + if (arm) { + brcmf_chip_ca7_set_passive(chip); + return; + } + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + if (arm) { + brcmf_chip_cm3_set_passive(chip); + return; + } +} + +bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core *arm; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); + if (arm) + return brcmf_chip_cr4_set_active(chip, rstvec); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7); + if (arm) + return brcmf_chip_ca7_set_active(chip, rstvec); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + if (arm) + return brcmf_chip_cm3_set_active(chip); + + return false; +} + +bool brcmf_chip_sr_capable(struct brcmf_chip *pub) +{ + u32 base, addr, reg, pmu_cc3_mask = ~0; + struct brcmf_chip_priv *chip; + + brcmf_dbg(TRACE, "Enter\n"); + + /* old chips with PMU version less than 17 don't support save restore */ + if (pub->pmurev < 17) + return false; + + base = brcmf_chip_get_chipcommon(pub)->base; + chip = container_of(pub, struct brcmf_chip_priv, pub); + + switch (pub->chip) { + case BRCM_CC_4354_CHIP_ID: + /* explicitly check SR engine enable bit */ + pmu_cc3_mask = BIT(2); + /* fall-through */ + case BRCM_CC_43241_CHIP_ID: + case BRCM_CC_4335_CHIP_ID: + case BRCM_CC_4339_CHIP_ID: + /* read PMU chipcontrol register 3 */ + addr = CORE_CC_REG(base, chipcontrol_addr); + chip->ops->write32(chip->ctx, addr, 3); + addr = CORE_CC_REG(base, chipcontrol_data); + reg = chip->ops->read32(chip->ctx, addr); + return (reg & pmu_cc3_mask) != 0; + case BRCM_CC_43430_CHIP_ID: + addr = CORE_CC_REG(base, sr_control1); + reg = chip->ops->read32(chip->ctx, addr); + return reg != 0; + default: + addr = CORE_CC_REG(base, pmucapabilities_ext); + reg = chip->ops->read32(chip->ctx, addr); + if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0) + return false; + + addr = CORE_CC_REG(base, retention_ctl); + reg = chip->ops->read32(chip->ctx, addr); + return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK | + PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; + } +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h new file mode 100644 index 000000000000..f6b5feea23d2 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014 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 BRCMF_CHIP_H +#define BRCMF_CHIP_H + +#include + +#define CORE_CC_REG(base, field) \ + (base + offsetof(struct chipcregs, field)) + +/** + * struct brcmf_chip - chip level information. + * + * @chip: chip identifier. + * @chiprev: chip revision. + * @cc_caps: chipcommon core capabilities. + * @pmucaps: PMU capabilities. + * @pmurev: PMU revision. + * @rambase: RAM base address (only applicable for ARM CR4 chips). + * @ramsize: amount of RAM on chip including retention. + * @srsize: amount of retention RAM on chip. + * @name: string representation of the chip identifier. + */ +struct brcmf_chip { + u32 chip; + u32 chiprev; + u32 cc_caps; + u32 pmucaps; + u32 pmurev; + u32 rambase; + u32 ramsize; + u32 srsize; + char name[8]; +}; + +/** + * struct brcmf_core - core related information. + * + * @id: core identifier. + * @rev: core revision. + * @base: base address of core register space. + */ +struct brcmf_core { + u16 id; + u16 rev; + u32 base; +}; + +/** + * struct brcmf_buscore_ops - buscore specific callbacks. + * + * @read32: read 32-bit value over bus. + * @write32: write 32-bit value over bus. + * @prepare: prepare bus for core configuration. + * @setup: bus-specific core setup. + * @active: chip becomes active. + * The callback should use the provided @rstvec when non-zero. + */ +struct brcmf_buscore_ops { + u32 (*read32)(void *ctx, u32 addr); + void (*write32)(void *ctx, u32 addr, u32 value); + int (*prepare)(void *ctx); + int (*reset)(void *ctx, struct brcmf_chip *chip); + int (*setup)(void *ctx, struct brcmf_chip *chip); + void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec); +}; + +struct brcmf_chip *brcmf_chip_attach(void *ctx, + const struct brcmf_buscore_ops *ops); +void brcmf_chip_detach(struct brcmf_chip *chip); +struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid); +struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip); +bool brcmf_chip_iscoreup(struct brcmf_core *core); +void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset); +void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset, + u32 postreset); +void brcmf_chip_set_passive(struct brcmf_chip *ci); +bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec); +bool brcmf_chip_sr_capable(struct brcmf_chip *pub); + +#endif /* BRCMF_AXIDMP_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c new file mode 100644 index 000000000000..fe54844c75e0 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2010 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 +#include +#include "core.h" +#include "bus.h" +#include "debug.h" +#include "fwil.h" +#include "fwil_types.h" +#include "tracepoint.h" +#include "common.h" + +const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +#define BRCMF_DEFAULT_BCN_TIMEOUT 3 +#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 +#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 + +/* boost value for RSSI_DELTA in preferred join selection */ +#define BRCMF_JOIN_PREF_RSSI_BOOST 8 + +int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) +{ + s8 eventmask[BRCMF_EVENTING_MASK_LEN]; + u8 buf[BRCMF_DCMD_SMLEN]; + struct brcmf_join_pref_params join_pref_params[2]; + struct brcmf_rev_info_le revinfo; + struct brcmf_rev_info *ri; + char *ptr; + s32 err; + + /* retreive mac address */ + err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, + sizeof(ifp->mac_addr)); + if (err < 0) { + brcmf_err("Retreiving cur_etheraddr failed, %d\n", err); + goto done; + } + memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac)); + + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, + &revinfo, sizeof(revinfo)); + ri = &ifp->drvr->revinfo; + if (err < 0) { + brcmf_err("retrieving revision info failed, %d\n", err); + } else { + ri->vendorid = le32_to_cpu(revinfo.vendorid); + ri->deviceid = le32_to_cpu(revinfo.deviceid); + ri->radiorev = le32_to_cpu(revinfo.radiorev); + ri->chiprev = le32_to_cpu(revinfo.chiprev); + ri->corerev = le32_to_cpu(revinfo.corerev); + ri->boardid = le32_to_cpu(revinfo.boardid); + ri->boardvendor = le32_to_cpu(revinfo.boardvendor); + ri->boardrev = le32_to_cpu(revinfo.boardrev); + ri->driverrev = le32_to_cpu(revinfo.driverrev); + ri->ucoderev = le32_to_cpu(revinfo.ucoderev); + ri->bus = le32_to_cpu(revinfo.bus); + ri->chipnum = le32_to_cpu(revinfo.chipnum); + ri->phytype = le32_to_cpu(revinfo.phytype); + ri->phyrev = le32_to_cpu(revinfo.phyrev); + ri->anarev = le32_to_cpu(revinfo.anarev); + ri->chippkg = le32_to_cpu(revinfo.chippkg); + ri->nvramrev = le32_to_cpu(revinfo.nvramrev); + } + ri->result = err; + + /* query for 'ver' to get version info from firmware */ + memset(buf, 0, sizeof(buf)); + strcpy(buf, "ver"); + err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf)); + if (err < 0) { + brcmf_err("Retreiving version information failed, %d\n", + err); + goto done; + } + ptr = (char *)buf; + strsep(&ptr, "\n"); + + /* Print fw version info */ + brcmf_err("Firmware version = %s\n", buf); + + /* locate firmware version number for ethtool */ + ptr = strrchr(buf, ' ') + 1; + strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver)); + + /* set mpc */ + err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); + if (err) { + brcmf_err("failed setting mpc\n"); + goto done; + } + + /* + * Setup timeout if Beacons are lost and roam is off to report + * link down + */ + err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", + BRCMF_DEFAULT_BCN_TIMEOUT); + if (err) { + brcmf_err("bcn_timeout error (%d)\n", err); + goto done; + } + + /* Enable/Disable build-in roaming to allowed ext supplicant to take + * of romaing + */ + err = brcmf_fil_iovar_int_set(ifp, "roam_off", 1); + if (err) { + brcmf_err("roam_off error (%d)\n", err); + goto done; + } + + /* Setup join_pref to select target by RSSI(with boost on 5GHz) */ + join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA; + join_pref_params[0].len = 2; + join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST; + join_pref_params[0].band = WLC_BAND_5G; + join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI; + join_pref_params[1].len = 2; + join_pref_params[1].rssi_gain = 0; + join_pref_params[1].band = 0; + err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params, + sizeof(join_pref_params)); + if (err) + brcmf_err("Set join_pref error (%d)\n", err); + + /* Setup event_msgs, enable E_IF */ + err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask, + BRCMF_EVENTING_MASK_LEN); + if (err) { + brcmf_err("Get event_msgs error (%d)\n", err); + goto done; + } + setbit(eventmask, BRCMF_E_IF); + err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask, + BRCMF_EVENTING_MASK_LEN); + if (err) { + brcmf_err("Set event_msgs error (%d)\n", err); + goto done; + } + + /* Setup default scan channel time */ + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, + BRCMF_DEFAULT_SCAN_CHANNEL_TIME); + if (err) { + brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n", + err); + goto done; + } + + /* Setup default scan unassoc time */ + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, + BRCMF_DEFAULT_SCAN_UNASSOC_TIME); + if (err) { + brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n", + err); + goto done; + } + + /* do bus specific preinit here */ + err = brcmf_bus_preinit(ifp->drvr->bus_if); +done: + return err; +} + +#if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG) +void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + if (brcmf_msg_level & level) + pr_debug("%s %pV", func, &vaf); + trace_brcmf_dbg(level, func, &vaf); + va_end(args); +} +#endif diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h new file mode 100644 index 000000000000..21c7488b4732 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2014 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_COMMON_H +#define BRCMFMAC_COMMON_H + +extern const u8 ALLFFMAC[ETH_ALEN]; + +/* Sets dongle media info (drv_version, mac address). */ +int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); + +#endif /* BRCMFMAC_COMMON_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c new file mode 100644 index 000000000000..7b0e52195a85 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c @@ -0,0 +1,252 @@ +/* Copyright (c) 2014 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 + +#include "core.h" +#include "commonring.h" + +void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, + int (*cr_ring_bell)(void *ctx), + int (*cr_update_rptr)(void *ctx), + int (*cr_update_wptr)(void *ctx), + int (*cr_write_rptr)(void *ctx), + int (*cr_write_wptr)(void *ctx), void *ctx) +{ + commonring->cr_ring_bell = cr_ring_bell; + commonring->cr_update_rptr = cr_update_rptr; + commonring->cr_update_wptr = cr_update_wptr; + commonring->cr_write_rptr = cr_write_rptr; + commonring->cr_write_wptr = cr_write_wptr; + commonring->cr_ctx = ctx; +} + + +void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth, + u16 item_len, void *buf_addr) +{ + commonring->depth = depth; + commonring->item_len = item_len; + commonring->buf_addr = buf_addr; + if (!commonring->inited) { + spin_lock_init(&commonring->lock); + commonring->inited = true; + } + commonring->r_ptr = 0; + if (commonring->cr_write_rptr) + commonring->cr_write_rptr(commonring->cr_ctx); + commonring->w_ptr = 0; + if (commonring->cr_write_wptr) + commonring->cr_write_wptr(commonring->cr_ctx); + commonring->f_ptr = 0; +} + + +void brcmf_commonring_lock(struct brcmf_commonring *commonring) + __acquires(&commonring->lock) +{ + unsigned long flags; + + spin_lock_irqsave(&commonring->lock, flags); + commonring->flags = flags; +} + + +void brcmf_commonring_unlock(struct brcmf_commonring *commonring) + __releases(&commonring->lock) +{ + spin_unlock_irqrestore(&commonring->lock, commonring->flags); +} + + +bool brcmf_commonring_write_available(struct brcmf_commonring *commonring) +{ + u16 available; + bool retry = true; + +again: + if (commonring->r_ptr <= commonring->w_ptr) + available = commonring->depth - commonring->w_ptr + + commonring->r_ptr; + else + available = commonring->r_ptr - commonring->w_ptr; + + if (available > 1) { + if (!commonring->was_full) + return true; + if (available > commonring->depth / 8) { + commonring->was_full = false; + return true; + } + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + return false; + } + + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + + commonring->was_full = true; + return false; +} + + +void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring) +{ + void *ret_ptr; + u16 available; + bool retry = true; + +again: + if (commonring->r_ptr <= commonring->w_ptr) + available = commonring->depth - commonring->w_ptr + + commonring->r_ptr; + else + available = commonring->r_ptr - commonring->w_ptr; + + if (available > 1) { + ret_ptr = commonring->buf_addr + + (commonring->w_ptr * commonring->item_len); + commonring->w_ptr++; + if (commonring->w_ptr == commonring->depth) + commonring->w_ptr = 0; + return ret_ptr; + } + + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + + commonring->was_full = true; + return NULL; +} + + +void * +brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring, + u16 n_items, u16 *alloced) +{ + void *ret_ptr; + u16 available; + bool retry = true; + +again: + if (commonring->r_ptr <= commonring->w_ptr) + available = commonring->depth - commonring->w_ptr + + commonring->r_ptr; + else + available = commonring->r_ptr - commonring->w_ptr; + + if (available > 1) { + ret_ptr = commonring->buf_addr + + (commonring->w_ptr * commonring->item_len); + *alloced = min_t(u16, n_items, available - 1); + if (*alloced + commonring->w_ptr > commonring->depth) + *alloced = commonring->depth - commonring->w_ptr; + commonring->w_ptr += *alloced; + if (commonring->w_ptr == commonring->depth) + commonring->w_ptr = 0; + return ret_ptr; + } + + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + + commonring->was_full = true; + return NULL; +} + + +int brcmf_commonring_write_complete(struct brcmf_commonring *commonring) +{ + void *address; + + address = commonring->buf_addr; + address += (commonring->f_ptr * commonring->item_len); + if (commonring->f_ptr > commonring->w_ptr) { + address = commonring->buf_addr; + commonring->f_ptr = 0; + } + + commonring->f_ptr = commonring->w_ptr; + + if (commonring->cr_write_wptr) + commonring->cr_write_wptr(commonring->cr_ctx); + if (commonring->cr_ring_bell) + return commonring->cr_ring_bell(commonring->cr_ctx); + + return -EIO; +} + + +void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring, + u16 n_items) +{ + if (commonring->w_ptr == 0) + commonring->w_ptr = commonring->depth - n_items; + else + commonring->w_ptr -= n_items; +} + + +void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, + u16 *n_items) +{ + if (commonring->cr_update_wptr) + commonring->cr_update_wptr(commonring->cr_ctx); + + *n_items = (commonring->w_ptr >= commonring->r_ptr) ? + (commonring->w_ptr - commonring->r_ptr) : + (commonring->depth - commonring->r_ptr); + + if (*n_items == 0) + return NULL; + + return commonring->buf_addr + + (commonring->r_ptr * commonring->item_len); +} + + +int brcmf_commonring_read_complete(struct brcmf_commonring *commonring, + u16 n_items) +{ + commonring->r_ptr += n_items; + if (commonring->r_ptr == commonring->depth) + commonring->r_ptr = 0; + + if (commonring->cr_write_rptr) + return commonring->cr_write_rptr(commonring->cr_ctx); + + return -EIO; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h new file mode 100644 index 000000000000..b85033611c8d --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2014 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_COMMONRING_H +#define BRCMFMAC_COMMONRING_H + + +struct brcmf_commonring { + u16 r_ptr; + u16 w_ptr; + u16 f_ptr; + u16 depth; + u16 item_len; + + void *buf_addr; + + int (*cr_ring_bell)(void *ctx); + int (*cr_update_rptr)(void *ctx); + int (*cr_update_wptr)(void *ctx); + int (*cr_write_rptr)(void *ctx); + int (*cr_write_wptr)(void *ctx); + + void *cr_ctx; + + spinlock_t lock; + unsigned long flags; + bool inited; + bool was_full; + + atomic_t outstanding_tx; +}; + + +void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, + int (*cr_ring_bell)(void *ctx), + int (*cr_update_rptr)(void *ctx), + int (*cr_update_wptr)(void *ctx), + int (*cr_write_rptr)(void *ctx), + int (*cr_write_wptr)(void *ctx), void *ctx); +void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth, + u16 item_len, void *buf_addr); +void brcmf_commonring_lock(struct brcmf_commonring *commonring); +void brcmf_commonring_unlock(struct brcmf_commonring *commonring); +bool brcmf_commonring_write_available(struct brcmf_commonring *commonring); +void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring); +void * +brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring, + u16 n_items, u16 *alloced); +int brcmf_commonring_write_complete(struct brcmf_commonring *commonring); +void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring, + u16 n_items); +void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, + u16 *n_items); +int brcmf_commonring_read_complete(struct brcmf_commonring *commonring, + u16 n_items); + +#define brcmf_commonring_n_items(commonring) (commonring->depth) +#define brcmf_commonring_len_item(commonring) (commonring->item_len) + + +#endif /* BRCMFMAC_COMMONRING_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c new file mode 100644 index 000000000000..b5ab98ee1445 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -0,0 +1,1260 @@ +/* + * Copyright (c) 2010 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 +#include +#include +#include + +#include "core.h" +#include "bus.h" +#include "debug.h" +#include "fwil_types.h" +#include "p2p.h" +#include "cfg80211.h" +#include "fwil.h" +#include "fwsignal.h" +#include "feature.h" +#include "proto.h" +#include "pcie.h" +#include "common.h" + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); +MODULE_LICENSE("Dual BSD/GPL"); + +#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */ + +/* AMPDU rx reordering definitions */ +#define BRCMF_RXREORDER_FLOWID_OFFSET 0 +#define BRCMF_RXREORDER_MAXIDX_OFFSET 2 +#define BRCMF_RXREORDER_FLAGS_OFFSET 4 +#define BRCMF_RXREORDER_CURIDX_OFFSET 6 +#define BRCMF_RXREORDER_EXPIDX_OFFSET 8 + +#define BRCMF_RXREORDER_DEL_FLOW 0x01 +#define BRCMF_RXREORDER_FLUSH_ALL 0x02 +#define BRCMF_RXREORDER_CURIDX_VALID 0x04 +#define BRCMF_RXREORDER_EXPIDX_VALID 0x08 +#define BRCMF_RXREORDER_NEW_HOLE 0x10 + +#define BRCMF_BSSIDX_INVALID -1 + +/* Error bits */ +int brcmf_msg_level; +module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(debug, "level of debug output"); + +/* P2P0 enable */ +static int brcmf_p2p_enable; +module_param_named(p2pon, brcmf_p2p_enable, int, 0); +MODULE_PARM_DESC(p2pon, "enable legacy p2p management functionality"); + +char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) +{ + if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { + brcmf_err("ifidx %d out of range\n", ifidx); + return ""; + } + + if (drvr->iflist[ifidx] == NULL) { + brcmf_err("null i/f %d\n", ifidx); + return ""; + } + + if (drvr->iflist[ifidx]->ndev) + return drvr->iflist[ifidx]->ndev->name; + + return ""; +} + +struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx) +{ + struct brcmf_if *ifp; + s32 bssidx; + + if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { + brcmf_err("ifidx %d out of range\n", ifidx); + return NULL; + } + + ifp = NULL; + bssidx = drvr->if2bss[ifidx]; + if (bssidx >= 0) + ifp = drvr->iflist[bssidx]; + + return ifp; +} + +static void _brcmf_set_multicast_list(struct work_struct *work) +{ + struct brcmf_if *ifp; + struct net_device *ndev; + struct netdev_hw_addr *ha; + u32 cmd_value, cnt; + __le32 cnt_le; + char *buf, *bufp; + u32 buflen; + s32 err; + + ifp = container_of(work, struct brcmf_if, multicast_work); + + brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + + ndev = ifp->ndev; + + /* Determine initial value of allmulti flag */ + cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false; + + /* Send down the multicast list first. */ + cnt = netdev_mc_count(ndev); + buflen = sizeof(cnt) + (cnt * ETH_ALEN); + buf = kmalloc(buflen, GFP_ATOMIC); + if (!buf) + return; + bufp = buf; + + cnt_le = cpu_to_le32(cnt); + memcpy(bufp, &cnt_le, sizeof(cnt_le)); + bufp += sizeof(cnt_le); + + netdev_for_each_mc_addr(ha, ndev) { + if (!cnt) + break; + memcpy(bufp, ha->addr, ETH_ALEN); + bufp += ETH_ALEN; + cnt--; + } + + err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen); + if (err < 0) { + brcmf_err("Setting mcast_list failed, %d\n", err); + cmd_value = cnt ? true : cmd_value; + } + + kfree(buf); + + /* + * Now send the allmulti setting. This is based on the setting in the + * net_device flags, but might be modified above to be turned on if we + * were trying to set some addresses and dongle rejected it... + */ + err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value); + if (err < 0) + brcmf_err("Setting allmulti failed, %d\n", err); + + /*Finally, pick up the PROMISC flag */ + cmd_value = (ndev->flags & IFF_PROMISC) ? true : false; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value); + if (err < 0) + brcmf_err("Setting BRCMF_C_SET_PROMISC failed, %d\n", + err); +} + +static void +_brcmf_set_mac_address(struct work_struct *work) +{ + struct brcmf_if *ifp; + s32 err; + + ifp = container_of(work, struct brcmf_if, setmacaddr_work); + + brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + + err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr, + ETH_ALEN); + if (err < 0) { + brcmf_err("Setting cur_etheraddr failed, %d\n", err); + } else { + brcmf_dbg(TRACE, "MAC address updated to %pM\n", + ifp->mac_addr); + memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN); + } +} + +static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct sockaddr *sa = (struct sockaddr *)addr; + + memcpy(&ifp->mac_addr, sa->sa_data, ETH_ALEN); + schedule_work(&ifp->setmacaddr_work); + return 0; +} + +static void brcmf_netdev_set_multicast_list(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + + schedule_work(&ifp->multicast_work); +} + +static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + int ret; + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + struct ethhdr *eh = (struct ethhdr *)(skb->data); + + brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); + + /* Can the device send data? */ + if (drvr->bus_if->state != BRCMF_BUS_UP) { + brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state); + netif_stop_queue(ndev); + dev_kfree_skb(skb); + ret = -ENODEV; + goto done; + } + + if (!drvr->iflist[ifp->bssidx]) { + brcmf_err("bad ifidx %d\n", ifp->bssidx); + netif_stop_queue(ndev); + dev_kfree_skb(skb); + ret = -ENODEV; + goto done; + } + + /* Make sure there's enough room for any header */ + if (skb_headroom(skb) < drvr->hdrlen) { + struct sk_buff *skb2; + + brcmf_dbg(INFO, "%s: insufficient headroom\n", + brcmf_ifname(drvr, ifp->bssidx)); + drvr->bus_if->tx_realloc++; + skb2 = skb_realloc_headroom(skb, drvr->hdrlen); + dev_kfree_skb(skb); + skb = skb2; + if (skb == NULL) { + brcmf_err("%s: skb_realloc_headroom failed\n", + brcmf_ifname(drvr, ifp->bssidx)); + ret = -ENOMEM; + goto done; + } + } + + /* validate length for ether packet */ + if (skb->len < sizeof(*eh)) { + ret = -EINVAL; + dev_kfree_skb(skb); + goto done; + } + + if (eh->h_proto == htons(ETH_P_PAE)) + atomic_inc(&ifp->pend_8021x_cnt); + + ret = brcmf_fws_process_skb(ifp, skb); + +done: + if (ret) { + ifp->stats.tx_dropped++; + } else { + ifp->stats.tx_packets++; + ifp->stats.tx_bytes += skb->len; + } + + /* Return ok: we always eat the packet */ + return NETDEV_TX_OK; +} + +void brcmf_txflowblock_if(struct brcmf_if *ifp, + enum brcmf_netif_stop_reason reason, bool state) +{ + unsigned long flags; + + if (!ifp || !ifp->ndev) + return; + + brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n", + ifp->bssidx, ifp->netif_stop, reason, state); + + spin_lock_irqsave(&ifp->netif_stop_lock, flags); + if (state) { + if (!ifp->netif_stop) + netif_stop_queue(ifp->ndev); + ifp->netif_stop |= reason; + } else { + ifp->netif_stop &= ~reason; + if (!ifp->netif_stop) + netif_wake_queue(ifp->ndev); + } + spin_unlock_irqrestore(&ifp->netif_stop_lock, flags); +} + +void brcmf_txflowblock(struct device *dev, bool state) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + brcmf_dbg(TRACE, "Enter\n"); + + brcmf_fws_bus_blocked(drvr, state); +} + +void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) +{ + skb->dev = ifp->ndev; + skb->protocol = eth_type_trans(skb, skb->dev); + + if (skb->pkt_type == PACKET_MULTICAST) + ifp->stats.multicast++; + + /* Process special event packets */ + brcmf_fweh_process_skb(ifp->drvr, skb); + + if (!(ifp->ndev->flags & IFF_UP)) { + brcmu_pkt_buf_free_skb(skb); + return; + } + + ifp->stats.rx_bytes += skb->len; + ifp->stats.rx_packets++; + + brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol)); + if (in_interrupt()) + netif_rx(skb); + else + /* If the receive is not processed inside an ISR, + * the softirqd must be woken explicitly to service + * the NET_RX_SOFTIRQ. This is handled by netif_rx_ni(). + */ + netif_rx_ni(skb); +} + +static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi, + u8 start, u8 end, + struct sk_buff_head *skb_list) +{ + /* initialize return list */ + __skb_queue_head_init(skb_list); + + if (rfi->pend_pkts == 0) { + brcmf_dbg(INFO, "no packets in reorder queue\n"); + return; + } + + do { + if (rfi->pktslots[start]) { + __skb_queue_tail(skb_list, rfi->pktslots[start]); + rfi->pktslots[start] = NULL; + } + start++; + if (start > rfi->max_idx) + start = 0; + } while (start != end); + rfi->pend_pkts -= skb_queue_len(skb_list); +} + +static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data, + struct sk_buff *pkt) +{ + u8 flow_id, max_idx, cur_idx, exp_idx, end_idx; + struct brcmf_ampdu_rx_reorder *rfi; + struct sk_buff_head reorder_list; + struct sk_buff *pnext; + u8 flags; + u32 buf_size; + + flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET]; + flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET]; + + /* validate flags and flow id */ + if (flags == 0xFF) { + brcmf_err("invalid flags...so ignore this packet\n"); + brcmf_netif_rx(ifp, pkt); + return; + } + + rfi = ifp->drvr->reorder_flows[flow_id]; + if (flags & BRCMF_RXREORDER_DEL_FLOW) { + brcmf_dbg(INFO, "flow-%d: delete\n", + flow_id); + + if (rfi == NULL) { + brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n", + flow_id); + brcmf_netif_rx(ifp, pkt); + return; + } + + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx, + &reorder_list); + /* add the last packet */ + __skb_queue_tail(&reorder_list, pkt); + kfree(rfi); + ifp->drvr->reorder_flows[flow_id] = NULL; + goto netif_rx; + } + /* from here on we need a flow reorder instance */ + if (rfi == NULL) { + buf_size = sizeof(*rfi); + max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET]; + + buf_size += (max_idx + 1) * sizeof(pkt); + + /* allocate space for flow reorder info */ + brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n", + flow_id, max_idx); + rfi = kzalloc(buf_size, GFP_ATOMIC); + if (rfi == NULL) { + brcmf_err("failed to alloc buffer\n"); + brcmf_netif_rx(ifp, pkt); + return; + } + + ifp->drvr->reorder_flows[flow_id] = rfi; + rfi->pktslots = (struct sk_buff **)(rfi+1); + rfi->max_idx = max_idx; + } + if (flags & BRCMF_RXREORDER_NEW_HOLE) { + if (rfi->pend_pkts) { + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, + rfi->exp_idx, + &reorder_list); + WARN_ON(rfi->pend_pkts); + } else { + __skb_queue_head_init(&reorder_list); + } + rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET]; + rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; + rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET]; + rfi->pktslots[rfi->cur_idx] = pkt; + rfi->pend_pkts++; + brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n", + flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts); + } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) { + cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET]; + exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; + + if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) { + /* still in the current hole */ + /* enqueue the current on the buffer chain */ + if (rfi->pktslots[cur_idx] != NULL) { + brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n"); + brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]); + rfi->pktslots[cur_idx] = NULL; + } + rfi->pktslots[cur_idx] = pkt; + rfi->pend_pkts++; + rfi->cur_idx = cur_idx; + brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n", + flow_id, cur_idx, exp_idx, rfi->pend_pkts); + + /* can return now as there is no reorder + * list to process. + */ + return; + } + if (rfi->exp_idx == cur_idx) { + if (rfi->pktslots[cur_idx] != NULL) { + brcmf_dbg(INFO, "error buffer pending..free it\n"); + brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]); + rfi->pktslots[cur_idx] = NULL; + } + rfi->pktslots[cur_idx] = pkt; + rfi->pend_pkts++; + + /* got the expected one. flush from current to expected + * and update expected + */ + brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n", + flow_id, cur_idx, exp_idx, rfi->pend_pkts); + + rfi->cur_idx = cur_idx; + rfi->exp_idx = exp_idx; + + brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx, + &reorder_list); + brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n", + flow_id, skb_queue_len(&reorder_list), + rfi->pend_pkts); + } else { + u8 end_idx; + + brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n", + flow_id, flags, rfi->cur_idx, rfi->exp_idx, + cur_idx, exp_idx); + if (flags & BRCMF_RXREORDER_FLUSH_ALL) + end_idx = rfi->exp_idx; + else + end_idx = exp_idx; + + /* flush pkts first */ + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx, + &reorder_list); + + if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) { + __skb_queue_tail(&reorder_list, pkt); + } else { + rfi->pktslots[cur_idx] = pkt; + rfi->pend_pkts++; + } + rfi->exp_idx = exp_idx; + rfi->cur_idx = cur_idx; + } + } else { + /* explicity window move updating the expected index */ + exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; + + brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n", + flow_id, flags, rfi->exp_idx, exp_idx); + if (flags & BRCMF_RXREORDER_FLUSH_ALL) + end_idx = rfi->exp_idx; + else + end_idx = exp_idx; + + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx, + &reorder_list); + __skb_queue_tail(&reorder_list, pkt); + /* set the new expected idx */ + rfi->exp_idx = exp_idx; + } +netif_rx: + skb_queue_walk_safe(&reorder_list, pkt, pnext) { + __skb_unlink(pkt, &reorder_list); + brcmf_netif_rx(ifp, pkt); + } +} + +void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) +{ + struct brcmf_if *ifp; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_skb_reorder_data *rd; + int ret; + + brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb); + + /* process and remove protocol-specific header */ + ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp); + + if (ret || !ifp || !ifp->ndev) { + if (ret != -ENODATA && ifp) + ifp->stats.rx_errors++; + brcmu_pkt_buf_free_skb(skb); + return; + } + + rd = (struct brcmf_skb_reorder_data *)skb->cb; + if (rd->reorder) + brcmf_rxreorder_process_info(ifp, rd->reorder, skb); + else + brcmf_netif_rx(ifp, skb); +} + +void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success) +{ + struct ethhdr *eh; + u16 type; + + eh = (struct ethhdr *)(txp->data); + type = ntohs(eh->h_proto); + + if (type == ETH_P_PAE) { + atomic_dec(&ifp->pend_8021x_cnt); + if (waitqueue_active(&ifp->pend_8021x_wait)) + wake_up(&ifp->pend_8021x_wait); + } + + if (!success) + ifp->stats.tx_errors++; + + brcmu_pkt_buf_free_skb(txp); +} + +void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_if *ifp; + + /* await txstatus signal for firmware if active */ + if (brcmf_fws_fc_active(drvr->fws)) { + if (!success) + brcmf_fws_bustxfail(drvr->fws, txp); + } else { + if (brcmf_proto_hdrpull(drvr, false, txp, &ifp)) + brcmu_pkt_buf_free_skb(txp); + else + brcmf_txfinalize(ifp, txp, success); + } +} + +static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + + return &ifp->stats; +} + +static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + char drev[BRCMU_DOTREV_LEN] = "n/a"; + + if (drvr->revinfo.result == 0) + brcmu_dotrev_str(drvr->revinfo.driverrev, drev); + strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strlcpy(info->version, drev, sizeof(info->version)); + strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version)); + strlcpy(info->bus_info, dev_name(drvr->bus_if->dev), + sizeof(info->bus_info)); +} + +static const struct ethtool_ops brcmf_ethtool_ops = { + .get_drvinfo = brcmf_ethtool_get_drvinfo, +}; + +static int brcmf_netdev_stop(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + + brcmf_cfg80211_down(ndev); + + brcmf_net_setcarrier(ifp, false); + + return 0; +} + +static int brcmf_netdev_open(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_bus *bus_if = drvr->bus_if; + u32 toe_ol; + + brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + + /* If bus is not ready, can't continue */ + if (bus_if->state != BRCMF_BUS_UP) { + brcmf_err("failed bus is not ready\n"); + return -EAGAIN; + } + + atomic_set(&ifp->pend_8021x_cnt, 0); + + /* Get current TOE mode from dongle */ + if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0 + && (toe_ol & TOE_TX_CSUM_OL) != 0) + ndev->features |= NETIF_F_IP_CSUM; + else + ndev->features &= ~NETIF_F_IP_CSUM; + + if (brcmf_cfg80211_up(ndev)) { + brcmf_err("failed to bring up cfg80211\n"); + return -EIO; + } + + /* Clear, carrier, set when connected or AP mode. */ + netif_carrier_off(ndev); + return 0; +} + +static const struct net_device_ops brcmf_netdev_ops_pri = { + .ndo_open = brcmf_netdev_open, + .ndo_stop = brcmf_netdev_stop, + .ndo_get_stats = brcmf_netdev_get_stats, + .ndo_start_xmit = brcmf_netdev_start_xmit, + .ndo_set_mac_address = brcmf_netdev_set_mac_address, + .ndo_set_rx_mode = brcmf_netdev_set_multicast_list +}; + +int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct net_device *ndev; + s32 err; + + brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx, + ifp->mac_addr); + ndev = ifp->ndev; + + /* set appropriate operations */ + ndev->netdev_ops = &brcmf_netdev_ops_pri; + + ndev->hard_header_len += drvr->hdrlen; + ndev->ethtool_ops = &brcmf_ethtool_ops; + + drvr->rxsz = ndev->mtu + ndev->hard_header_len + + drvr->hdrlen; + + /* set the mac address */ + memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN); + + INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address); + INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list); + + if (rtnl_locked) + err = register_netdevice(ndev); + else + err = register_netdev(ndev); + if (err != 0) { + brcmf_err("couldn't register the net device\n"); + goto fail; + } + + brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); + return 0; + +fail: + drvr->iflist[ifp->bssidx] = NULL; + ndev->netdev_ops = NULL; + free_netdev(ndev); + return -EBADE; +} + +static void brcmf_net_detach(struct net_device *ndev) +{ + if (ndev->reg_state == NETREG_REGISTERED) + unregister_netdev(ndev); + else + brcmf_cfg80211_free_netdev(ndev); +} + +void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on) +{ + struct net_device *ndev; + + brcmf_dbg(TRACE, "Enter, idx=%d carrier=%d\n", ifp->bssidx, on); + + ndev = ifp->ndev; + brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_DISCONNECTED, !on); + if (on) { + if (!netif_carrier_ok(ndev)) + netif_carrier_on(ndev); + + } else { + if (netif_carrier_ok(ndev)) + netif_carrier_off(ndev); + } +} + +static int brcmf_net_p2p_open(struct net_device *ndev) +{ + brcmf_dbg(TRACE, "Enter\n"); + + return brcmf_cfg80211_up(ndev); +} + +static int brcmf_net_p2p_stop(struct net_device *ndev) +{ + brcmf_dbg(TRACE, "Enter\n"); + + return brcmf_cfg80211_down(ndev); +} + +static netdev_tx_t brcmf_net_p2p_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + if (skb) + dev_kfree_skb_any(skb); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops brcmf_netdev_ops_p2p = { + .ndo_open = brcmf_net_p2p_open, + .ndo_stop = brcmf_net_p2p_stop, + .ndo_start_xmit = brcmf_net_p2p_start_xmit +}; + +static int brcmf_net_p2p_attach(struct brcmf_if *ifp) +{ + struct net_device *ndev; + + brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx, + ifp->mac_addr); + ndev = ifp->ndev; + + ndev->netdev_ops = &brcmf_netdev_ops_p2p; + + /* set the mac address */ + memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN); + + if (register_netdev(ndev) != 0) { + brcmf_err("couldn't register the p2p net device\n"); + goto fail; + } + + brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); + + return 0; + +fail: + ifp->drvr->iflist[ifp->bssidx] = NULL; + ndev->netdev_ops = NULL; + free_netdev(ndev); + return -EBADE; +} + +struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, + bool is_p2pdev, char *name, u8 *mac_addr) +{ + struct brcmf_if *ifp; + struct net_device *ndev; + + brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifidx); + + ifp = drvr->iflist[bssidx]; + /* + * Delete the existing interface before overwriting it + * in case we missed the BRCMF_E_IF_DEL event. + */ + if (ifp) { + brcmf_err("ERROR: netdev:%s already exists\n", + ifp->ndev->name); + if (ifidx) { + netif_stop_queue(ifp->ndev); + brcmf_net_detach(ifp->ndev); + drvr->iflist[bssidx] = NULL; + } else { + brcmf_err("ignore IF event\n"); + return ERR_PTR(-EINVAL); + } + } + + if (!brcmf_p2p_enable && is_p2pdev) { + /* this is P2P_DEVICE interface */ + brcmf_dbg(INFO, "allocate non-netdev interface\n"); + ifp = kzalloc(sizeof(*ifp), GFP_KERNEL); + if (!ifp) + return ERR_PTR(-ENOMEM); + } else { + brcmf_dbg(INFO, "allocate netdev interface\n"); + /* Allocate netdev, including space for private structure */ + ndev = alloc_netdev(sizeof(*ifp), is_p2pdev ? "p2p%d" : name, + NET_NAME_UNKNOWN, ether_setup); + if (!ndev) + return ERR_PTR(-ENOMEM); + + ndev->destructor = brcmf_cfg80211_free_netdev; + ifp = netdev_priv(ndev); + ifp->ndev = ndev; + /* store mapping ifidx to bssidx */ + if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID) + drvr->if2bss[ifidx] = bssidx; + } + + ifp->drvr = drvr; + drvr->iflist[bssidx] = ifp; + ifp->ifidx = ifidx; + ifp->bssidx = bssidx; + + init_waitqueue_head(&ifp->pend_8021x_wait); + spin_lock_init(&ifp->netif_stop_lock); + + if (mac_addr != NULL) + memcpy(ifp->mac_addr, mac_addr, ETH_ALEN); + + brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n", + current->pid, name, ifp->mac_addr); + + return ifp; +} + +static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) +{ + struct brcmf_if *ifp; + + ifp = drvr->iflist[bssidx]; + drvr->iflist[bssidx] = NULL; + if (!ifp) { + brcmf_err("Null interface, idx=%d\n", bssidx); + return; + } + brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx); + if (drvr->if2bss[ifp->ifidx] == bssidx) + drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID; + if (ifp->ndev) { + if (bssidx == 0) { + if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { + rtnl_lock(); + brcmf_netdev_stop(ifp->ndev); + rtnl_unlock(); + } + } else { + netif_stop_queue(ifp->ndev); + } + + if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { + cancel_work_sync(&ifp->setmacaddr_work); + cancel_work_sync(&ifp->multicast_work); + } + brcmf_net_detach(ifp->ndev); + } else { + /* Only p2p device interfaces which get dynamically created + * end up here. In this case the p2p module should be informed + * about the removal of the interface within the firmware. If + * not then p2p commands towards the firmware will cause some + * serious troublesome side effects. The p2p module will clean + * up the ifp if needed. + */ + brcmf_p2p_ifp_removed(ifp); + kfree(ifp); + } +} + +void brcmf_remove_interface(struct brcmf_if *ifp) +{ + if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp)) + return; + brcmf_dbg(TRACE, "Enter, bssidx=%d, ifidx=%d\n", ifp->bssidx, + ifp->ifidx); + brcmf_fws_del_interface(ifp); + brcmf_del_if(ifp->drvr, ifp->bssidx); +} + +int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr) +{ + int ifidx; + int bsscfgidx; + bool available; + int highest; + + available = false; + bsscfgidx = 2; + highest = 2; + for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) { + if (drvr->iflist[ifidx]) { + if (drvr->iflist[ifidx]->bssidx == bsscfgidx) + bsscfgidx = highest + 1; + else if (drvr->iflist[ifidx]->bssidx > highest) + highest = drvr->iflist[ifidx]->bssidx; + } else { + available = true; + } + } + + return available ? bsscfgidx : -ENOMEM; +} + +int brcmf_attach(struct device *dev) +{ + struct brcmf_pub *drvr = NULL; + int ret = 0; + int i; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Allocate primary brcmf_info */ + drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC); + if (!drvr) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++) + drvr->if2bss[i] = BRCMF_BSSIDX_INVALID; + + mutex_init(&drvr->proto_block); + + /* Link to bus module */ + drvr->hdrlen = 0; + drvr->bus_if = dev_get_drvdata(dev); + drvr->bus_if->drvr = drvr; + + /* attach debug facilities */ + brcmf_debug_attach(drvr); + + /* Attach and link in the protocol */ + ret = brcmf_proto_attach(drvr); + if (ret != 0) { + brcmf_err("brcmf_prot_attach failed\n"); + goto fail; + } + + /* attach firmware event handler */ + brcmf_fweh_attach(drvr); + + return ret; + +fail: + brcmf_detach(dev); + + return ret; +} + +static int brcmf_revinfo_read(struct seq_file *s, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(s->private); + struct brcmf_rev_info *ri = &bus_if->drvr->revinfo; + char drev[BRCMU_DOTREV_LEN]; + char brev[BRCMU_BOARDREV_LEN]; + + seq_printf(s, "vendorid: 0x%04x\n", ri->vendorid); + seq_printf(s, "deviceid: 0x%04x\n", ri->deviceid); + seq_printf(s, "radiorev: %s\n", brcmu_dotrev_str(ri->radiorev, drev)); + seq_printf(s, "chipnum: %u (%x)\n", ri->chipnum, ri->chipnum); + seq_printf(s, "chiprev: %u\n", ri->chiprev); + seq_printf(s, "chippkg: %u\n", ri->chippkg); + seq_printf(s, "corerev: %u\n", ri->corerev); + seq_printf(s, "boardid: 0x%04x\n", ri->boardid); + seq_printf(s, "boardvendor: 0x%04x\n", ri->boardvendor); + seq_printf(s, "boardrev: %s\n", brcmu_boardrev_str(ri->boardrev, brev)); + seq_printf(s, "driverrev: %s\n", brcmu_dotrev_str(ri->driverrev, drev)); + seq_printf(s, "ucoderev: %u\n", ri->ucoderev); + seq_printf(s, "bus: %u\n", ri->bus); + seq_printf(s, "phytype: %u\n", ri->phytype); + seq_printf(s, "phyrev: %u\n", ri->phyrev); + seq_printf(s, "anarev: %u\n", ri->anarev); + seq_printf(s, "nvramrev: %08x\n", ri->nvramrev); + + return 0; +} + +int brcmf_bus_start(struct device *dev) +{ + int ret = -1; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_if *ifp; + struct brcmf_if *p2p_ifp; + + brcmf_dbg(TRACE, "\n"); + + /* add primary networking interface */ + ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL); + if (IS_ERR(ifp)) + return PTR_ERR(ifp); + + p2p_ifp = NULL; + + /* signal bus ready */ + brcmf_bus_change_state(bus_if, BRCMF_BUS_UP); + + /* Bus is ready, do any initialization */ + ret = brcmf_c_preinit_dcmds(ifp); + if (ret < 0) + goto fail; + + brcmf_debugfs_add_entry(drvr, "revinfo", brcmf_revinfo_read); + + /* assure we have chipid before feature attach */ + if (!bus_if->chip) { + bus_if->chip = drvr->revinfo.chipnum; + bus_if->chiprev = drvr->revinfo.chiprev; + brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n", + bus_if->chip, bus_if->chip, bus_if->chiprev); + } + brcmf_feat_attach(drvr); + + ret = brcmf_fws_init(drvr); + if (ret < 0) + goto fail; + + brcmf_fws_add_interface(ifp); + + drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev, + brcmf_p2p_enable); + if (drvr->config == NULL) { + ret = -ENOMEM; + goto fail; + } + + ret = brcmf_net_attach(ifp, false); + + if ((!ret) && (brcmf_p2p_enable)) { + p2p_ifp = drvr->iflist[1]; + if (p2p_ifp) + ret = brcmf_net_p2p_attach(p2p_ifp); + } +fail: + if (ret < 0) { + brcmf_err("failed: %d\n", ret); + if (drvr->config) { + brcmf_cfg80211_detach(drvr->config); + drvr->config = NULL; + } + if (drvr->fws) { + brcmf_fws_del_interface(ifp); + brcmf_fws_deinit(drvr); + } + if (ifp) + brcmf_net_detach(ifp->ndev); + if (p2p_ifp) + brcmf_net_detach(p2p_ifp->ndev); + return ret; + } + return 0; +} + +void brcmf_bus_add_txhdrlen(struct device *dev, uint len) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + if (drvr) { + drvr->hdrlen += len; + } +} + +static void brcmf_bus_detach(struct brcmf_pub *drvr) +{ + brcmf_dbg(TRACE, "Enter\n"); + + if (drvr) { + /* Stop the bus module */ + brcmf_bus_stop(drvr->bus_if); + } +} + +void brcmf_dev_reset(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + if (drvr == NULL) + return; + + if (drvr->iflist[0]) + brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1); +} + +void brcmf_detach(struct device *dev) +{ + s32 i; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + brcmf_dbg(TRACE, "Enter\n"); + + if (drvr == NULL) + return; + + /* stop firmware event handling */ + brcmf_fweh_detach(drvr); + if (drvr->config) + brcmf_p2p_detach(&drvr->config->p2p); + + brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN); + + /* make sure primary interface removed last */ + for (i = BRCMF_MAX_IFS-1; i > -1; i--) + brcmf_remove_interface(drvr->iflist[i]); + + brcmf_cfg80211_detach(drvr->config); + + brcmf_fws_deinit(drvr); + + brcmf_bus_detach(drvr); + + brcmf_proto_detach(drvr); + + brcmf_debug_detach(drvr); + bus_if->drvr = NULL; + kfree(drvr); +} + +s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_if *ifp = bus_if->drvr->iflist[0]; + + return brcmf_fil_iovar_data_set(ifp, name, data, len); +} + +static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp) +{ + return atomic_read(&ifp->pend_8021x_cnt); +} + +int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp) +{ + int err; + + err = wait_event_timeout(ifp->pend_8021x_wait, + !brcmf_get_pend_8021x_cnt(ifp), + msecs_to_jiffies(MAX_WAIT_FOR_8021X_TX)); + + WARN_ON(!err); + + return !err; +} + +void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state) +{ + struct brcmf_pub *drvr = bus->drvr; + struct net_device *ndev; + int ifidx; + + brcmf_dbg(TRACE, "%d -> %d\n", bus->state, state); + bus->state = state; + + if (state == BRCMF_BUS_UP) { + for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) { + if ((drvr->iflist[ifidx]) && + (drvr->iflist[ifidx]->ndev)) { + ndev = drvr->iflist[ifidx]->ndev; + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + } + } + } +} + +static void brcmf_driver_register(struct work_struct *work) +{ +#ifdef CONFIG_BRCMFMAC_SDIO + brcmf_sdio_register(); +#endif +#ifdef CONFIG_BRCMFMAC_USB + brcmf_usb_register(); +#endif +#ifdef CONFIG_BRCMFMAC_PCIE + brcmf_pcie_register(); +#endif +} +static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register); + +static int __init brcmfmac_module_init(void) +{ + brcmf_debugfs_init(); +#ifdef CONFIG_BRCMFMAC_SDIO + brcmf_sdio_init(); +#endif + if (!schedule_work(&brcmf_driver_work)) + return -EBUSY; + + return 0; +} + +static void __exit brcmfmac_module_exit(void) +{ + cancel_work_sync(&brcmf_driver_work); + +#ifdef CONFIG_BRCMFMAC_SDIO + brcmf_sdio_exit(); +#endif +#ifdef CONFIG_BRCMFMAC_USB + brcmf_usb_exit(); +#endif +#ifdef CONFIG_BRCMFMAC_PCIE + brcmf_pcie_exit(); +#endif + brcmf_debugfs_exit(); +} + +module_init(brcmfmac_module_init); +module_exit(brcmfmac_module_exit); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h new file mode 100644 index 000000000000..2f9101b2ad34 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2010 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. + */ + +/**************** + * Common types * + */ + +#ifndef BRCMFMAC_CORE_H +#define BRCMFMAC_CORE_H + +#include +#include "fweh.h" + +#define TOE_TX_CSUM_OL 0x00000001 +#define TOE_RX_CSUM_OL 0x00000002 + +/* For supporting multiple interfaces */ +#define BRCMF_MAX_IFS 16 + +/* Small, medium and maximum buffer size for dcmd + */ +#define BRCMF_DCMD_SMLEN 256 +#define BRCMF_DCMD_MEDLEN 1536 +#define BRCMF_DCMD_MAXLEN 8192 + +/* IOCTL from host to device are limited in lenght. A device can only handle + * ethernet frame size. This limitation is to be applied by protocol layer. + */ +#define BRCMF_TX_IOCTL_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN) + +#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS 256 + +/* Length of firmware version string stored for + * ethtool driver info which uses 32 bytes as well. + */ +#define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32 + +/** + * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info + * + * @pktslots: dynamic allocated array for ordering AMPDU packets. + * @flow_id: AMPDU flow identifier. + * @cur_idx: last AMPDU index from firmware. + * @exp_idx: expected next AMPDU index. + * @max_idx: maximum amount of packets per AMPDU. + * @pend_pkts: number of packets currently in @pktslots. + */ +struct brcmf_ampdu_rx_reorder { + struct sk_buff **pktslots; + u8 flow_id; + u8 cur_idx; + u8 exp_idx; + u8 max_idx; + u8 pend_pkts; +}; + +/* Forward decls for struct brcmf_pub (see below) */ +struct brcmf_proto; /* device communication protocol info */ +struct brcmf_cfg80211_dev; /* cfg80211 device info */ +struct brcmf_fws_info; /* firmware signalling info */ + +/* + * struct brcmf_rev_info + * + * The result field stores the error code of the + * revision info request from firmware. For the + * other fields see struct brcmf_rev_info_le in + * fwil_types.h + */ +struct brcmf_rev_info { + int result; + u32 vendorid; + u32 deviceid; + u32 radiorev; + u32 chiprev; + u32 corerev; + u32 boardid; + u32 boardvendor; + u32 boardrev; + u32 driverrev; + u32 ucoderev; + u32 bus; + u32 chipnum; + u32 phytype; + u32 phyrev; + u32 anarev; + u32 chippkg; + u32 nvramrev; +}; + +/* Common structure for module and instance linkage */ +struct brcmf_pub { + /* Linkage ponters */ + struct brcmf_bus *bus_if; + struct brcmf_proto *proto; + struct brcmf_cfg80211_info *config; + + /* Internal brcmf items */ + uint hdrlen; /* Total BRCMF header length (proto + bus) */ + uint rxsz; /* Rx buffer size bus module should use */ + + /* Dongle media info */ + char fwver[BRCMF_DRIVER_FIRMWARE_VERSION_LEN]; + u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */ + + /* Multicast data packets sent to dongle */ + unsigned long tx_multicast; + + struct mac_address addresses[BRCMF_MAX_IFS]; + + struct brcmf_if *iflist[BRCMF_MAX_IFS]; + s32 if2bss[BRCMF_MAX_IFS]; + + struct mutex proto_block; + unsigned char proto_buf[BRCMF_DCMD_MAXLEN]; + + struct brcmf_fweh_info fweh; + + struct brcmf_fws_info *fws; + + struct brcmf_ampdu_rx_reorder + *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS]; + + u32 feat_flags; + u32 chip_quirks; + + struct brcmf_rev_info revinfo; +#ifdef DEBUG + struct dentry *dbgfs_dir; +#endif +}; + +/* forward declarations */ +struct brcmf_cfg80211_vif; +struct brcmf_fws_mac_descriptor; + +/** + * enum brcmf_netif_stop_reason - reason for stopping netif queue. + * + * @BRCMF_NETIF_STOP_REASON_FWS_FC: + * netif stopped due to firmware signalling flow control. + * @BRCMF_NETIF_STOP_REASON_FLOW: + * netif stopped due to flowring full. + * @BRCMF_NETIF_STOP_REASON_DISCONNECTED: + * netif stopped due to not being connected (STA mode). + */ +enum brcmf_netif_stop_reason { + BRCMF_NETIF_STOP_REASON_FWS_FC = BIT(0), + BRCMF_NETIF_STOP_REASON_FLOW = BIT(1), + BRCMF_NETIF_STOP_REASON_DISCONNECTED = BIT(2) +}; + +/** + * struct brcmf_if - interface control information. + * + * @drvr: points to device related information. + * @vif: points to cfg80211 specific interface information. + * @ndev: associated network device. + * @stats: interface specific network statistics. + * @setmacaddr_work: worker object for setting mac address. + * @multicast_work: worker object for multicast provisioning. + * @fws_desc: interface specific firmware-signalling descriptor. + * @ifidx: interface index in device firmware. + * @bssidx: index of bss associated with this interface. + * @mac_addr: assigned mac address. + * @netif_stop: bitmap indicates reason why netif queues are stopped. + * @netif_stop_lock: spinlock for update netif_stop from multiple sources. + * @pend_8021x_cnt: tracks outstanding number of 802.1x frames. + * @pend_8021x_wait: used for signalling change in count. + */ +struct brcmf_if { + struct brcmf_pub *drvr; + struct brcmf_cfg80211_vif *vif; + struct net_device *ndev; + struct net_device_stats stats; + struct work_struct setmacaddr_work; + struct work_struct multicast_work; + struct brcmf_fws_mac_descriptor *fws_desc; + int ifidx; + s32 bssidx; + u8 mac_addr[ETH_ALEN]; + u8 netif_stop; + spinlock_t netif_stop_lock; + atomic_t pend_8021x_cnt; + wait_queue_head_t pend_8021x_wait; +}; + +struct brcmf_skb_reorder_data { + u8 *reorder; +}; + +int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); + +/* Return pointer to interface name */ +char *brcmf_ifname(struct brcmf_pub *drvr, int idx); +struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx); +int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); +struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, + bool is_p2pdev, char *name, u8 *mac_addr); +void brcmf_remove_interface(struct brcmf_if *ifp); +int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr); +void brcmf_txflowblock_if(struct brcmf_if *ifp, + enum brcmf_netif_stop_reason reason, bool state); +void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success); +void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb); +void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on); + +#endif /* BRCMFMAC_CORE_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c new file mode 100644 index 000000000000..1299dccc78b4 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012 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 + +#include +#include +#include "core.h" +#include "bus.h" +#include "fweh.h" +#include "debug.h" + +static struct dentry *root_folder; + +static int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, + size_t len) +{ + void *dump; + size_t ramsize; + + ramsize = brcmf_bus_get_ramsize(bus); + if (ramsize) { + dump = vzalloc(len + ramsize); + if (!dump) + return -ENOMEM; + memcpy(dump, data, len); + brcmf_bus_get_memdump(bus, dump + len, ramsize); + dev_coredumpv(bus->dev, dump, len + ramsize, GFP_KERNEL); + } + return 0; +} + +static int brcmf_debug_psm_watchdog_notify(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data) +{ + brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); + + return brcmf_debug_create_memdump(ifp->drvr->bus_if, data, + evtmsg->datalen); +} + +void brcmf_debugfs_init(void) +{ + root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (IS_ERR(root_folder)) + root_folder = NULL; +} + +void brcmf_debugfs_exit(void) +{ + if (!root_folder) + return; + + debugfs_remove_recursive(root_folder); + root_folder = NULL; +} + +int brcmf_debug_attach(struct brcmf_pub *drvr) +{ + struct device *dev = drvr->bus_if->dev; + + if (!root_folder) + return -ENODEV; + + drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder); + if (IS_ERR(drvr->dbgfs_dir)) + return PTR_ERR(drvr->dbgfs_dir); + + + return brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG, + brcmf_debug_psm_watchdog_notify); +} + +void brcmf_debug_detach(struct brcmf_pub *drvr) +{ + brcmf_fweh_unregister(drvr, BRCMF_E_PSM_WATCHDOG); + + if (!IS_ERR_OR_NULL(drvr->dbgfs_dir)) + debugfs_remove_recursive(drvr->dbgfs_dir); +} + +struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) +{ + return drvr->dbgfs_dir; +} + +int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)) +{ + struct dentry *e; + + e = debugfs_create_devm_seqfile(drvr->bus_if->dev, fn, + drvr->dbgfs_dir, read_fn); + return PTR_ERR_OR_ZERO(e); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h new file mode 100644 index 000000000000..d0d9676f7f9d --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2010 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_DEBUG_H +#define BRCMFMAC_DEBUG_H + +/* message levels */ +#define BRCMF_TRACE_VAL 0x00000002 +#define BRCMF_INFO_VAL 0x00000004 +#define BRCMF_DATA_VAL 0x00000008 +#define BRCMF_CTL_VAL 0x00000010 +#define BRCMF_TIMER_VAL 0x00000020 +#define BRCMF_HDRS_VAL 0x00000040 +#define BRCMF_BYTES_VAL 0x00000080 +#define BRCMF_INTR_VAL 0x00000100 +#define BRCMF_GLOM_VAL 0x00000200 +#define BRCMF_EVENT_VAL 0x00000400 +#define BRCMF_BTA_VAL 0x00000800 +#define BRCMF_FIL_VAL 0x00001000 +#define BRCMF_USB_VAL 0x00002000 +#define BRCMF_SCAN_VAL 0x00004000 +#define BRCMF_CONN_VAL 0x00008000 +#define BRCMF_BCDC_VAL 0x00010000 +#define BRCMF_SDIO_VAL 0x00020000 +#define BRCMF_MSGBUF_VAL 0x00040000 +#define BRCMF_PCIE_VAL 0x00080000 +#define BRCMF_FWCON_VAL 0x00100000 + +/* set default print format */ +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* Macro for error messages. net_ratelimit() is used when driver + * debugging is not selected. When debugging the driver error + * messages are as important as other tracing or even more so. + */ +#ifndef CONFIG_BRCM_TRACING +#ifdef CONFIG_BRCMDBG +#define brcmf_err(fmt, ...) pr_err("%s: " fmt, __func__, ##__VA_ARGS__) +#else +#define brcmf_err(fmt, ...) \ + do { \ + if (net_ratelimit()) \ + pr_err("%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) +#endif +#else +__printf(2, 3) +void __brcmf_err(const char *func, const char *fmt, ...); +#define brcmf_err(fmt, ...) \ + __brcmf_err(__func__, fmt, ##__VA_ARGS__) +#endif + +#if defined(DEBUG) || defined(CONFIG_BRCM_TRACING) +__printf(3, 4) +void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...); +#define brcmf_dbg(level, fmt, ...) \ +do { \ + __brcmf_dbg(BRCMF_##level##_VAL, __func__, \ + fmt, ##__VA_ARGS__); \ +} while (0) +#define BRCMF_DATA_ON() (brcmf_msg_level & BRCMF_DATA_VAL) +#define BRCMF_CTL_ON() (brcmf_msg_level & BRCMF_CTL_VAL) +#define BRCMF_HDRS_ON() (brcmf_msg_level & BRCMF_HDRS_VAL) +#define BRCMF_BYTES_ON() (brcmf_msg_level & BRCMF_BYTES_VAL) +#define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL) +#define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL) +#define BRCMF_FIL_ON() (brcmf_msg_level & BRCMF_FIL_VAL) +#define BRCMF_FWCON_ON() (brcmf_msg_level & BRCMF_FWCON_VAL) + +#else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ + +#define brcmf_dbg(level, fmt, ...) no_printk(fmt, ##__VA_ARGS__) + +#define BRCMF_DATA_ON() 0 +#define BRCMF_CTL_ON() 0 +#define BRCMF_HDRS_ON() 0 +#define BRCMF_BYTES_ON() 0 +#define BRCMF_GLOM_ON() 0 +#define BRCMF_EVENT_ON() 0 +#define BRCMF_FIL_ON() 0 +#define BRCMF_FWCON_ON() 0 + +#endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ + +#define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \ +do { \ + trace_brcmf_hexdump((void *)data, len); \ + if (test) \ + brcmu_dbg_hex_dump(data, len, fmt, ##__VA_ARGS__); \ +} while (0) + +extern int brcmf_msg_level; + +struct brcmf_pub; +#ifdef DEBUG +void brcmf_debugfs_init(void); +void brcmf_debugfs_exit(void); +int brcmf_debug_attach(struct brcmf_pub *drvr); +void brcmf_debug_detach(struct brcmf_pub *drvr); +struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); +int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)); +#else +static inline void brcmf_debugfs_init(void) +{ +} +static inline void brcmf_debugfs_exit(void) +{ +} +static inline int brcmf_debug_attach(struct brcmf_pub *drvr) +{ + return 0; +} +static inline void brcmf_debug_detach(struct brcmf_pub *drvr) +{ +} +static inline +int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)) +{ + return 0; +} +#endif + +#endif /* BRCMFMAC_DEBUG_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c new file mode 100644 index 000000000000..44bb30636690 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2014 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 "core.h" +#include "bus.h" +#include "debug.h" +#include "fwil.h" +#include "feature.h" + + +/* Module param feature_disable (global for all devices) */ +static int brcmf_feature_disable; +module_param_named(feature_disable, brcmf_feature_disable, int, 0); +MODULE_PARM_DESC(feature_disable, "Disable features"); + +/* + * expand feature list to array of feature strings. + */ +#define BRCMF_FEAT_DEF(_f) \ + #_f, +static const char *brcmf_feat_names[] = { + BRCMF_FEAT_LIST +}; +#undef BRCMF_FEAT_DEF + +#ifdef DEBUG +/* + * expand quirk list to array of quirk strings. + */ +#define BRCMF_QUIRK_DEF(_q) \ + #_q, +static const char * const brcmf_quirk_names[] = { + BRCMF_QUIRK_LIST +}; +#undef BRCMF_QUIRK_DEF + +/** + * brcmf_feat_debugfs_read() - expose feature info to debugfs. + * + * @seq: sequence for debugfs entry. + * @data: raw data pointer. + */ +static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + u32 feats = bus_if->drvr->feat_flags; + u32 quirks = bus_if->drvr->chip_quirks; + int id; + + seq_printf(seq, "Features: %08x\n", feats); + for (id = 0; id < BRCMF_FEAT_LAST; id++) + if (feats & BIT(id)) + seq_printf(seq, "\t%s\n", brcmf_feat_names[id]); + seq_printf(seq, "\nQuirks: %08x\n", quirks); + for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++) + if (quirks & BIT(id)) + seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]); + return 0; +} +#else +static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data) +{ + return 0; +} +#endif /* DEBUG */ + +/** + * brcmf_feat_iovar_int_get() - determine feature through iovar query. + * + * @ifp: interface to query. + * @id: feature id. + * @name: iovar name. + */ +static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp, + enum brcmf_feat_id id, char *name) +{ + u32 data; + int err; + + err = brcmf_fil_iovar_int_get(ifp, name, &data); + if (err == 0) { + brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); + ifp->drvr->feat_flags |= BIT(id); + } else { + brcmf_dbg(TRACE, "%s feature check failed: %d\n", + brcmf_feat_names[id], err); + } +} + +/** + * brcmf_feat_iovar_int_set() - determine feature through iovar set. + * + * @ifp: interface to query. + * @id: feature id. + * @name: iovar name. + */ +static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp, + enum brcmf_feat_id id, char *name, u32 val) +{ + int err; + + err = brcmf_fil_iovar_int_set(ifp, name, val); + if (err == 0) { + brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); + ifp->drvr->feat_flags |= BIT(id); + } else { + brcmf_dbg(TRACE, "%s feature check failed: %d\n", + brcmf_feat_names[id], err); + } +} + +void brcmf_feat_attach(struct brcmf_pub *drvr) +{ + struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); + + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); + if (drvr->bus_if->wowl_supported) + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); + if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) + brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p"); + + if (brcmf_feature_disable) { + brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", + ifp->drvr->feat_flags, brcmf_feature_disable); + ifp->drvr->feat_flags &= ~brcmf_feature_disable; + } + + /* set chip related quirks */ + switch (drvr->bus_if->chip) { + case BRCM_CC_43236_CHIP_ID: + drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH); + break; + case BRCM_CC_4329_CHIP_ID: + drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC); + break; + default: + /* no quirks */ + break; + } + + brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read); +} + +bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id) +{ + return (ifp->drvr->feat_flags & BIT(id)); +} + +bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp, + enum brcmf_feat_quirk quirk) +{ + return (ifp->drvr->chip_quirks & BIT(quirk)); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h new file mode 100644 index 000000000000..6b381f799f22 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014 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 _BRCMF_FEATURE_H +#define _BRCMF_FEATURE_H + +/* + * Features: + * + * MBSS: multiple BSSID support (eg. guest network in AP mode). + * MCHAN: multi-channel for concurrent P2P. + * PNO: preferred network offload. + * WOWL: Wake-On-WLAN. + * P2P: peer-to-peer + */ +#define BRCMF_FEAT_LIST \ + BRCMF_FEAT_DEF(MBSS) \ + BRCMF_FEAT_DEF(MCHAN) \ + BRCMF_FEAT_DEF(PNO) \ + BRCMF_FEAT_DEF(WOWL) \ + BRCMF_FEAT_DEF(P2P) +/* + * Quirks: + * + * AUTO_AUTH: workaround needed for automatic authentication type. + * NEED_MPC: driver needs to disable MPC during scanning operation. + */ +#define BRCMF_QUIRK_LIST \ + BRCMF_QUIRK_DEF(AUTO_AUTH) \ + BRCMF_QUIRK_DEF(NEED_MPC) + +#define BRCMF_FEAT_DEF(_f) \ + BRCMF_FEAT_ ## _f, +/* + * expand feature list to enumeration. + */ +enum brcmf_feat_id { + BRCMF_FEAT_LIST + BRCMF_FEAT_LAST +}; +#undef BRCMF_FEAT_DEF + +#define BRCMF_QUIRK_DEF(_q) \ + BRCMF_FEAT_QUIRK_ ## _q, +/* + * expand quirk list to enumeration. + */ +enum brcmf_feat_quirk { + BRCMF_QUIRK_LIST + BRCMF_FEAT_QUIRK_LAST +}; +#undef BRCMF_QUIRK_DEF + +/** + * brcmf_feat_attach() - determine features and quirks. + * + * @drvr: driver instance. + */ +void brcmf_feat_attach(struct brcmf_pub *drvr); + +/** + * brcmf_feat_is_enabled() - query feature. + * + * @ifp: interface instance. + * @id: feature id to check. + * + * Return: true is feature is enabled; otherwise false. + */ +bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id); + +/** + * brcmf_feat_is_quirk_enabled() - query chip quirk. + * + * @ifp: interface instance. + * @quirk: quirk id to check. + * + * Return: true is quirk is enabled; otherwise false. + */ +bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp, + enum brcmf_feat_quirk quirk); + +#endif /* _BRCMF_FEATURE_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c new file mode 100644 index 000000000000..4248f3c80e78 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -0,0 +1,542 @@ +/* + * 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 +#include +#include + +#include "debug.h" +#include "firmware.h" + +#define BRCMF_FW_MAX_NVRAM_SIZE 64000 +#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */ +#define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */ + +char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; +module_param_string(alternative_fw_path, brcmf_firmware_path, + BRCMF_FW_PATH_LEN, 0440); + +enum nvram_parser_state { + IDLE, + KEY, + VALUE, + COMMENT, + END +}; + +/** + * struct nvram_parser - internal info for parser. + * + * @state: current parser state. + * @data: input buffer being parsed. + * @nvram: output buffer with parse result. + * @nvram_len: lenght of parse result. + * @line: current line. + * @column: current column in line. + * @pos: byte offset in input buffer. + * @entry: start position of key,value entry. + * @multi_dev_v1: detect pcie multi device v1 (compressed). + * @multi_dev_v2: detect pcie multi device v2. + */ +struct nvram_parser { + enum nvram_parser_state state; + const u8 *data; + u8 *nvram; + u32 nvram_len; + u32 line; + u32 column; + u32 pos; + u32 entry; + bool multi_dev_v1; + bool multi_dev_v2; +}; + +/** + * is_nvram_char() - check if char is a valid one for NVRAM entry + * + * It accepts all printable ASCII chars except for '#' which opens a comment. + * Please note that ' ' (space) while accepted is not a valid key name char. + */ +static bool is_nvram_char(char c) +{ + /* comment marker excluded */ + if (c == '#') + return false; + + /* key and value may have any other readable character */ + return (c >= 0x20 && c < 0x7f); +} + +static bool is_whitespace(char c) +{ + return (c == ' ' || c == '\r' || c == '\n' || c == '\t'); +} + +static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp) +{ + char c; + + c = nvp->data[nvp->pos]; + if (c == '\n') + return COMMENT; + if (is_whitespace(c)) + goto proceed; + if (c == '#') + return COMMENT; + if (is_nvram_char(c)) { + nvp->entry = nvp->pos; + return KEY; + } + brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n", + nvp->line, nvp->column); +proceed: + nvp->column++; + nvp->pos++; + return IDLE; +} + +static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp) +{ + enum nvram_parser_state st = nvp->state; + char c; + + c = nvp->data[nvp->pos]; + if (c == '=') { + /* ignore RAW1 by treating as comment */ + if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0) + st = COMMENT; + else + st = VALUE; + if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0) + nvp->multi_dev_v1 = true; + if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0) + nvp->multi_dev_v2 = true; + } else if (!is_nvram_char(c) || c == ' ') { + brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", + nvp->line, nvp->column); + return COMMENT; + } + + nvp->column++; + nvp->pos++; + return st; +} + +static enum nvram_parser_state +brcmf_nvram_handle_value(struct nvram_parser *nvp) +{ + char c; + char *skv; + char *ekv; + u32 cplen; + + c = nvp->data[nvp->pos]; + if (!is_nvram_char(c)) { + /* key,value pair complete */ + ekv = (u8 *)&nvp->data[nvp->pos]; + skv = (u8 *)&nvp->data[nvp->entry]; + cplen = ekv - skv; + if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE) + return END; + /* copy to output buffer */ + memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen); + nvp->nvram_len += cplen; + nvp->nvram[nvp->nvram_len] = '\0'; + nvp->nvram_len++; + return IDLE; + } + nvp->pos++; + nvp->column++; + return VALUE; +} + +static enum nvram_parser_state +brcmf_nvram_handle_comment(struct nvram_parser *nvp) +{ + char *eoc, *sol; + + sol = (char *)&nvp->data[nvp->pos]; + eoc = strchr(sol, '\n'); + if (!eoc) { + eoc = strchr(sol, '\0'); + if (!eoc) + return END; + } + + /* eat all moving to next line */ + nvp->line++; + nvp->column = 1; + nvp->pos += (eoc - sol) + 1; + return IDLE; +} + +static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp) +{ + /* final state */ + return END; +} + +static enum nvram_parser_state +(*nv_parser_states[])(struct nvram_parser *nvp) = { + brcmf_nvram_handle_idle, + brcmf_nvram_handle_key, + brcmf_nvram_handle_value, + brcmf_nvram_handle_comment, + brcmf_nvram_handle_end +}; + +static int brcmf_init_nvram_parser(struct nvram_parser *nvp, + const u8 *data, size_t data_len) +{ + size_t size; + + memset(nvp, 0, sizeof(*nvp)); + nvp->data = data; + /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */ + if (data_len > BRCMF_FW_MAX_NVRAM_SIZE) + size = BRCMF_FW_MAX_NVRAM_SIZE; + else + size = data_len; + /* Alloc for extra 0 byte + roundup by 4 + length field */ + size += 1 + 3 + sizeof(u32); + nvp->nvram = kzalloc(size, GFP_KERNEL); + if (!nvp->nvram) + return -ENOMEM; + + nvp->line = 1; + nvp->column = 1; + return 0; +} + +/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple + * devices. Strip it down for one device, use domain_nr/bus_nr to determine + * which data is to be returned. v1 is the version where nvram is stored + * compressed and "devpath" maps to index for valid entries. + */ +static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr, + u16 bus_nr) +{ + /* Device path with a leading '=' key-value separator */ + char pci_path[] = "=pci/?/?"; + size_t pci_len; + char pcie_path[] = "=pcie/?/?"; + size_t pcie_len; + + u32 i, j; + bool found; + u8 *nvram; + u8 id; + + nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); + if (!nvram) + goto fail; + + /* min length: devpath0=pcie/1/4/ + 0:x=y */ + if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6) + goto fail; + + /* First search for the devpathX and see if it is the configuration + * for domain_nr/bus_nr. Search complete nvp + */ + snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr, + bus_nr); + pci_len = strlen(pci_path); + snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr, + bus_nr); + pcie_len = strlen(pcie_path); + found = false; + i = 0; + while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) { + /* Format: devpathX=pcie/Y/Z/ + * Y = domain_nr, Z = bus_nr, X = virtual ID + */ + if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 && + (!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) || + !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) { + id = nvp->nvram[i + 7] - '0'; + found = true; + break; + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + if (!found) + goto fail; + + /* Now copy all valid entries, release old nvram and assign new one */ + i = 0; + j = 0; + while (i < nvp->nvram_len) { + if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) { + i += 2; + while (nvp->nvram[i] != 0) { + nvram[j] = nvp->nvram[i]; + i++; + j++; + } + nvram[j] = 0; + j++; + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + kfree(nvp->nvram); + nvp->nvram = nvram; + nvp->nvram_len = j; + return; + +fail: + kfree(nvram); + nvp->nvram_len = 0; +} + +/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple + * devices. Strip it down for one device, use domain_nr/bus_nr to determine + * which data is to be returned. v2 is the version where nvram is stored + * uncompressed, all relevant valid entries are identified by + * pcie/domain_nr/bus_nr: + */ +static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr, + u16 bus_nr) +{ + char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN]; + size_t len; + u32 i, j; + u8 *nvram; + + nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); + if (!nvram) + goto fail; + + /* Copy all valid entries, release old nvram and assign new one. + * Valid entries are of type pcie/X/Y/ where X = domain_nr and + * Y = bus_nr. + */ + snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr); + len = strlen(prefix); + i = 0; + j = 0; + while (i < nvp->nvram_len - len) { + if (strncmp(&nvp->nvram[i], prefix, len) == 0) { + i += len; + while (nvp->nvram[i] != 0) { + nvram[j] = nvp->nvram[i]; + i++; + j++; + } + nvram[j] = 0; + j++; + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + kfree(nvp->nvram); + nvp->nvram = nvram; + nvp->nvram_len = j; + return; +fail: + kfree(nvram); + nvp->nvram_len = 0; +} + +/* brcmf_nvram_strip :Takes a buffer of "=\n" lines read from a fil + * 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. + */ +static void *brcmf_fw_nvram_strip(const u8 *data, size_t data_len, + u32 *new_length, u16 domain_nr, u16 bus_nr) +{ + struct nvram_parser nvp; + u32 pad; + u32 token; + __le32 token_le; + + if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0) + return NULL; + + while (nvp.pos < data_len) { + nvp.state = nv_parser_states[nvp.state](&nvp); + if (nvp.state == END) + break; + } + if (nvp.multi_dev_v1) + brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr); + else if (nvp.multi_dev_v2) + brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr); + + if (nvp.nvram_len == 0) { + kfree(nvp.nvram); + return NULL; + } + + pad = nvp.nvram_len; + *new_length = roundup(nvp.nvram_len + 1, 4); + while (pad != *new_length) { + nvp.nvram[pad] = 0; + pad++; + } + + token = *new_length / 4; + token = (~token << 16) | (token & 0x0000FFFF); + token_le = cpu_to_le32(token); + + memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le)); + *new_length += sizeof(token_le); + + return nvp.nvram; +} + +void brcmf_fw_nvram_free(void *nvram) +{ + kfree(nvram); +} + +struct brcmf_fw { + struct device *dev; + u16 flags; + const struct firmware *code; + const char *nvram_name; + u16 domain_nr; + u16 bus_nr; + void (*done)(struct device *dev, const struct firmware *fw, + void *nvram_image, u32 nvram_len); +}; + +static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) +{ + struct brcmf_fw *fwctx = ctx; + u32 nvram_length = 0; + void *nvram = NULL; + u8 *data = NULL; + size_t data_len; + bool raw_nvram; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); + if (fw && fw->data) { + data = (u8 *)fw->data; + data_len = fw->size; + raw_nvram = false; + } else { + data = bcm47xx_nvram_get_contents(&data_len); + if (!data && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) + goto fail; + raw_nvram = true; + } + + if (data) + nvram = brcmf_fw_nvram_strip(data, data_len, &nvram_length, + fwctx->domain_nr, fwctx->bus_nr); + + if (raw_nvram) + bcm47xx_nvram_release_contents(data); + if (fw) + release_firmware(fw); + if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) + goto fail; + + fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length); + kfree(fwctx); + return; + +fail: + brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); + release_firmware(fwctx->code); + device_release_driver(fwctx->dev); + kfree(fwctx); +} + +static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx) +{ + struct brcmf_fw *fwctx = ctx; + int ret; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); + if (!fw) + goto fail; + + /* only requested code so done here */ + if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) { + fwctx->done(fwctx->dev, fw, NULL, 0); + kfree(fwctx); + return; + } + fwctx->code = fw; + ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name, + fwctx->dev, GFP_KERNEL, fwctx, + brcmf_fw_request_nvram_done); + + if (!ret) + return; + + brcmf_fw_request_nvram_done(NULL, fwctx); + return; + +fail: + brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); + device_release_driver(fwctx->dev); + kfree(fwctx); +} + +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len), + u16 domain_nr, u16 bus_nr) +{ + struct brcmf_fw *fwctx; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev)); + if (!fw_cb || !code) + return -EINVAL; + + if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram) + return -EINVAL; + + fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL); + if (!fwctx) + return -ENOMEM; + + fwctx->dev = dev; + fwctx->flags = flags; + fwctx->done = fw_cb; + if (flags & BRCMF_FW_REQUEST_NVRAM) + fwctx->nvram_name = nvram; + fwctx->domain_nr = domain_nr; + fwctx->bus_nr = bus_nr; + + return request_firmware_nowait(THIS_MODULE, true, code, dev, + GFP_KERNEL, fwctx, + brcmf_fw_request_code_done); +} + +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len)) +{ + return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0, + 0); +} + diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h new file mode 100644 index 000000000000..604dd48ab4e0 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h @@ -0,0 +1,47 @@ +/* + * 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_FIRMWARE_H +#define BRCMFMAC_FIRMWARE_H + +#define BRCMF_FW_REQUEST 0x000F +#define BRCMF_FW_REQUEST_NVRAM 0x0001 +#define BRCMF_FW_REQ_FLAGS 0x00F0 +#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010 + +#define BRCMF_FW_PATH_LEN 256 +#define BRCMF_FW_NAME_LEN 32 + +extern char brcmf_firmware_path[]; + +void brcmf_fw_nvram_free(void *nvram); +/* + * Request firmware(s) asynchronously. When the asynchronous request + * fails it will not use the callback, but call device_release_driver() + * instead which will call the driver .remove() callback. + */ +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len), + u16 domain_nr, u16 bus_nr); +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len)); + +#endif /* BRCMFMAC_FIRMWARE_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c new file mode 100644 index 000000000000..2ca783fa50cf --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c @@ -0,0 +1,504 @@ +/* Copyright (c) 2014 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 + +#include "core.h" +#include "debug.h" +#include "bus.h" +#include "proto.h" +#include "flowring.h" +#include "msgbuf.h" +#include "common.h" + + +#define BRCMF_FLOWRING_HIGH 1024 +#define BRCMF_FLOWRING_LOW (BRCMF_FLOWRING_HIGH - 256) +#define BRCMF_FLOWRING_INVALID_IFIDX 0xff + +#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16) +#define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16) + +static const u8 brcmf_flowring_prio2fifo[] = { + 1, + 0, + 0, + 1, + 2, + 2, + 3, + 3 +}; + + +static bool +brcmf_flowring_is_tdls_mac(struct brcmf_flowring *flow, u8 mac[ETH_ALEN]) +{ + struct brcmf_flowring_tdls_entry *search; + + search = flow->tdls_entry; + + while (search) { + if (memcmp(search->mac, mac, ETH_ALEN) == 0) + return true; + search = search->next; + } + + return false; +} + + +u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx) +{ + struct brcmf_flowring_hash *hash; + u8 hash_idx; + u32 i; + bool found; + bool sta; + u8 fifo; + u8 *mac; + + fifo = brcmf_flowring_prio2fifo[prio]; + sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); + mac = da; + if ((!sta) && (is_multicast_ether_addr(da))) { + mac = (u8 *)ALLFFMAC; + fifo = 0; + } + if ((sta) && (flow->tdls_active) && + (brcmf_flowring_is_tdls_mac(flow, da))) { + sta = false; + } + hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : + BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); + found = false; + hash = flow->hash; + for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { + if ((sta || (memcmp(hash[hash_idx].mac, mac, ETH_ALEN) == 0)) && + (hash[hash_idx].fifo == fifo) && + (hash[hash_idx].ifidx == ifidx)) { + found = true; + break; + } + hash_idx++; + } + if (found) + return hash[hash_idx].flowid; + + return BRCMF_FLOWRING_INVALID_ID; +} + + +u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx) +{ + struct brcmf_flowring_ring *ring; + struct brcmf_flowring_hash *hash; + u8 hash_idx; + u32 i; + bool found; + u8 fifo; + bool sta; + u8 *mac; + + fifo = brcmf_flowring_prio2fifo[prio]; + sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); + mac = da; + if ((!sta) && (is_multicast_ether_addr(da))) { + mac = (u8 *)ALLFFMAC; + fifo = 0; + } + if ((sta) && (flow->tdls_active) && + (brcmf_flowring_is_tdls_mac(flow, da))) { + sta = false; + } + hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : + BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); + found = false; + hash = flow->hash; + for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { + if ((hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX) && + (is_zero_ether_addr(hash[hash_idx].mac))) { + found = true; + break; + } + hash_idx++; + } + if (found) { + for (i = 0; i < flow->nrofrings; i++) { + if (flow->rings[i] == NULL) + break; + } + if (i == flow->nrofrings) + return -ENOMEM; + + ring = kzalloc(sizeof(*ring), GFP_ATOMIC); + if (!ring) + return -ENOMEM; + + memcpy(hash[hash_idx].mac, mac, ETH_ALEN); + hash[hash_idx].fifo = fifo; + hash[hash_idx].ifidx = ifidx; + hash[hash_idx].flowid = i; + + ring->hash_id = hash_idx; + ring->status = RING_CLOSED; + skb_queue_head_init(&ring->skblist); + flow->rings[i] = ring; + + return i; + } + return BRCMF_FLOWRING_INVALID_ID; +} + + +u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + + return flow->hash[ring->hash_id].fifo; +} + + +static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid, + bool blocked) +{ + struct brcmf_flowring_ring *ring; + struct brcmf_bus *bus_if; + struct brcmf_pub *drvr; + struct brcmf_if *ifp; + bool currently_blocked; + int i; + u8 ifidx; + unsigned long flags; + + spin_lock_irqsave(&flow->block_lock, flags); + + ring = flow->rings[flowid]; + if (ring->blocked == blocked) { + spin_unlock_irqrestore(&flow->block_lock, flags); + return; + } + ifidx = brcmf_flowring_ifidx_get(flow, flowid); + + currently_blocked = false; + for (i = 0; i < flow->nrofrings; i++) { + if ((flow->rings[i]) && (i != flowid)) { + ring = flow->rings[i]; + if ((ring->status == RING_OPEN) && + (brcmf_flowring_ifidx_get(flow, i) == ifidx)) { + if (ring->blocked) { + currently_blocked = true; + break; + } + } + } + } + flow->rings[flowid]->blocked = blocked; + if (currently_blocked) { + spin_unlock_irqrestore(&flow->block_lock, flags); + return; + } + + bus_if = dev_get_drvdata(flow->dev); + drvr = bus_if->drvr; + ifp = brcmf_get_ifp(drvr, ifidx); + brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked); + + spin_unlock_irqrestore(&flow->block_lock, flags); +} + + +void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + u8 hash_idx; + struct sk_buff *skb; + + ring = flow->rings[flowid]; + if (!ring) + return; + brcmf_flowring_block(flow, flowid, false); + hash_idx = ring->hash_id; + flow->hash[hash_idx].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; + eth_zero_addr(flow->hash[hash_idx].mac); + flow->rings[flowid] = NULL; + + skb = skb_dequeue(&ring->skblist); + while (skb) { + brcmu_pkt_buf_free_skb(skb); + skb = skb_dequeue(&ring->skblist); + } + + kfree(ring); +} + + +u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + + skb_queue_tail(&ring->skblist, skb); + + if (!ring->blocked && + (skb_queue_len(&ring->skblist) > BRCMF_FLOWRING_HIGH)) { + brcmf_flowring_block(flow, flowid, true); + brcmf_dbg(MSGBUF, "Flowcontrol: BLOCK for ring %d\n", flowid); + /* To prevent (work around) possible race condition, check + * queue len again. It is also possible to use locking to + * protect, but that is undesirable for every enqueue and + * dequeue. This simple check will solve a possible race + * condition if it occurs. + */ + if (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW) + brcmf_flowring_block(flow, flowid, false); + } + return skb_queue_len(&ring->skblist); +} + + +struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + struct sk_buff *skb; + + ring = flow->rings[flowid]; + if (ring->status != RING_OPEN) + return NULL; + + skb = skb_dequeue(&ring->skblist); + + if (ring->blocked && + (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)) { + brcmf_flowring_block(flow, flowid, false); + brcmf_dbg(MSGBUF, "Flowcontrol: OPEN for ring %d\n", flowid); + } + + return skb; +} + + +void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + + skb_queue_head(&ring->skblist, skb); +} + + +u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + if (!ring) + return 0; + + if (ring->status != RING_OPEN) + return 0; + + return skb_queue_len(&ring->skblist); +} + + +void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + if (!ring) { + brcmf_err("Ring NULL, for flowid %d\n", flowid); + return; + } + + ring->status = RING_OPEN; +} + + +u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + u8 hash_idx; + + ring = flow->rings[flowid]; + hash_idx = ring->hash_id; + + return flow->hash[hash_idx].ifidx; +} + + +struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) +{ + struct brcmf_flowring *flow; + u32 i; + + flow = kzalloc(sizeof(*flow), GFP_KERNEL); + if (flow) { + flow->dev = dev; + flow->nrofrings = nrofrings; + spin_lock_init(&flow->block_lock); + for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++) + flow->addr_mode[i] = ADDR_INDIRECT; + for (i = 0; i < ARRAY_SIZE(flow->hash); i++) + flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; + flow->rings = kcalloc(nrofrings, sizeof(*flow->rings), + GFP_KERNEL); + if (!flow->rings) { + kfree(flow); + flow = NULL; + } + } + + return flow; +} + + +void brcmf_flowring_detach(struct brcmf_flowring *flow) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_flowring_tdls_entry *search; + struct brcmf_flowring_tdls_entry *remove; + u8 flowid; + + for (flowid = 0; flowid < flow->nrofrings; flowid++) { + if (flow->rings[flowid]) + brcmf_msgbuf_delete_flowring(drvr, flowid); + } + + search = flow->tdls_entry; + while (search) { + remove = search; + search = search->next; + kfree(remove); + } + kfree(flow->rings); + kfree(flow); +} + + +void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, + enum proto_addr_mode addr_mode) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); + struct brcmf_pub *drvr = bus_if->drvr; + u32 i; + u8 flowid; + + if (flow->addr_mode[ifidx] != addr_mode) { + for (i = 0; i < ARRAY_SIZE(flow->hash); i++) { + if (flow->hash[i].ifidx == ifidx) { + flowid = flow->hash[i].flowid; + if (flow->rings[flowid]->status != RING_OPEN) + continue; + flow->rings[flowid]->status = RING_CLOSING; + brcmf_msgbuf_delete_flowring(drvr, flowid); + } + } + flow->addr_mode[ifidx] = addr_mode; + } +} + + +void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_flowring_hash *hash; + struct brcmf_flowring_tdls_entry *prev; + struct brcmf_flowring_tdls_entry *search; + u32 i; + u8 flowid; + bool sta; + + sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); + + search = flow->tdls_entry; + prev = NULL; + while (search) { + if (memcmp(search->mac, peer, ETH_ALEN) == 0) { + sta = false; + break; + } + prev = search; + search = search->next; + } + + hash = flow->hash; + for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { + if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) && + (hash[i].ifidx == ifidx)) { + flowid = flow->hash[i].flowid; + if (flow->rings[flowid]->status == RING_OPEN) { + flow->rings[flowid]->status = RING_CLOSING; + brcmf_msgbuf_delete_flowring(drvr, flowid); + } + } + } + + if (search) { + if (prev) + prev->next = search->next; + else + flow->tdls_entry = search->next; + kfree(search); + if (flow->tdls_entry == NULL) + flow->tdls_active = false; + } +} + + +void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]) +{ + struct brcmf_flowring_tdls_entry *tdls_entry; + struct brcmf_flowring_tdls_entry *search; + + tdls_entry = kzalloc(sizeof(*tdls_entry), GFP_ATOMIC); + if (tdls_entry == NULL) + return; + + memcpy(tdls_entry->mac, peer, ETH_ALEN); + tdls_entry->next = NULL; + if (flow->tdls_entry == NULL) { + flow->tdls_entry = tdls_entry; + } else { + search = flow->tdls_entry; + if (memcmp(search->mac, peer, ETH_ALEN) == 0) + return; + while (search->next) { + search = search->next; + if (memcmp(search->mac, peer, ETH_ALEN) == 0) + return; + } + search->next = tdls_entry; + } + + flow->tdls_active = true; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h new file mode 100644 index 000000000000..95fd1c9675d1 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2014 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_FLOWRING_H +#define BRCMFMAC_FLOWRING_H + + +#define BRCMF_FLOWRING_HASHSIZE 256 +#define BRCMF_FLOWRING_INVALID_ID 0xFFFFFFFF + + +struct brcmf_flowring_hash { + u8 mac[ETH_ALEN]; + u8 fifo; + u8 ifidx; + u8 flowid; +}; + +enum ring_status { + RING_CLOSED, + RING_CLOSING, + RING_OPEN +}; + +struct brcmf_flowring_ring { + u16 hash_id; + bool blocked; + enum ring_status status; + struct sk_buff_head skblist; +}; + +struct brcmf_flowring_tdls_entry { + u8 mac[ETH_ALEN]; + struct brcmf_flowring_tdls_entry *next; +}; + +struct brcmf_flowring { + struct device *dev; + struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE]; + struct brcmf_flowring_ring **rings; + spinlock_t block_lock; + enum proto_addr_mode addr_mode[BRCMF_MAX_IFS]; + u16 nrofrings; + bool tdls_active; + struct brcmf_flowring_tdls_entry *tdls_entry; +}; + + +u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx); +u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx); +void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid); +void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid); +u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid); +u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb); +struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid); +void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb); +u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid); +u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid); +struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings); +void brcmf_flowring_detach(struct brcmf_flowring *flow); +void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, + enum proto_addr_mode addr_mode); +void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]); +void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]); + + +#endif /* BRCMFMAC_FLOWRING_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c new file mode 100644 index 000000000000..3878b6f6cfce --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2012 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 "brcmu_wifi.h" +#include "brcmu_utils.h" + +#include "core.h" +#include "debug.h" +#include "tracepoint.h" +#include "fwsignal.h" +#include "fweh.h" +#include "fwil.h" + +/** + * struct brcm_ethhdr - broadcom specific ether header. + * + * @subtype: subtype for this packet. + * @length: TODO: length of appended data. + * @version: version indication. + * @oui: OUI of this packet. + * @usr_subtype: subtype for this OUI. + */ +struct brcm_ethhdr { + __be16 subtype; + __be16 length; + u8 version; + u8 oui[3]; + __be16 usr_subtype; +} __packed; + +struct brcmf_event_msg_be { + __be16 version; + __be16 flags; + __be32 event_type; + __be32 status; + __be32 reason; + __be32 auth_type; + __be32 datalen; + u8 addr[ETH_ALEN]; + char ifname[IFNAMSIZ]; + u8 ifidx; + u8 bsscfgidx; +} __packed; + +/** + * struct brcmf_event - contents of broadcom event packet. + * + * @eth: standard ether header. + * @hdr: broadcom specific ether header. + * @msg: common part of the actual event message. + */ +struct brcmf_event { + struct ethhdr eth; + struct brcm_ethhdr hdr; + struct brcmf_event_msg_be msg; +} __packed; + +/** + * struct brcmf_fweh_queue_item - event item on event queue. + * + * @q: list element for queuing. + * @code: event code. + * @ifidx: interface index related to this event. + * @ifaddr: ethernet address for interface. + * @emsg: common parameters of the firmware event message. + * @data: event specific data part of the firmware event. + */ +struct brcmf_fweh_queue_item { + struct list_head q; + enum brcmf_fweh_event_code code; + u8 ifidx; + u8 ifaddr[ETH_ALEN]; + struct brcmf_event_msg_be emsg; + u8 data[0]; +}; + +/** + * struct brcmf_fweh_event_name - code, name mapping entry. + */ +struct brcmf_fweh_event_name { + enum brcmf_fweh_event_code code; + const char *name; +}; + +#ifdef DEBUG +#define BRCMF_ENUM_DEF(id, val) \ + { val, #id }, + +/* array for mapping code to event name */ +static struct brcmf_fweh_event_name fweh_event_names[] = { + BRCMF_FWEH_EVENT_ENUM_DEFLIST +}; +#undef BRCMF_ENUM_DEF + +/** + * brcmf_fweh_event_name() - returns name for given event code. + * + * @code: code to lookup. + */ +static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) +{ + int i; + for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) { + if (fweh_event_names[i].code == code) + return fweh_event_names[i].name; + } + return "unknown"; +} +#else +static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) +{ + return "nodebug"; +} +#endif + +/** + * brcmf_fweh_queue_event() - create and queue event. + * + * @fweh: firmware event handling info. + * @event: event queue entry. + */ +static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh, + struct brcmf_fweh_queue_item *event) +{ + ulong flags; + + spin_lock_irqsave(&fweh->evt_q_lock, flags); + list_add_tail(&event->q, &fweh->event_q); + spin_unlock_irqrestore(&fweh->evt_q_lock, flags); + schedule_work(&fweh->event_work); +} + +static int brcmf_fweh_call_event_handler(struct brcmf_if *ifp, + enum brcmf_fweh_event_code code, + struct brcmf_event_msg *emsg, + void *data) +{ + struct brcmf_fweh_info *fweh; + int err = -EINVAL; + + if (ifp) { + fweh = &ifp->drvr->fweh; + + /* handle the event if valid interface and handler */ + if (fweh->evt_handler[code]) + err = fweh->evt_handler[code](ifp, emsg, data); + else + brcmf_err("unhandled event %d ignored\n", code); + } else { + brcmf_err("no interface object\n"); + } + return err; +} + +/** + * brcmf_fweh_handle_if_event() - handle IF event. + * + * @drvr: driver information object. + * @item: queue entry. + * @ifpp: interface object (may change upon ADD action). + */ +static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, + struct brcmf_event_msg *emsg, + void *data) +{ + struct brcmf_if_event *ifevent = data; + struct brcmf_if *ifp; + bool is_p2pdev; + int err = 0; + + brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n", + ifevent->action, ifevent->ifidx, ifevent->bssidx, + ifevent->flags, ifevent->role); + + /* The P2P Device interface event must not be ignored contrary to what + * firmware tells us. Older firmware uses p2p noif, with sta role. + * This should be accepted when p2pdev_setup is ongoing. TDLS setup will + * use the same ifevent and should be ignored. + */ + is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) && + (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT || + ((ifevent->role == BRCMF_E_IF_ROLE_STA) && + (drvr->fweh.p2pdev_setup_ongoing)))); + if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) { + brcmf_dbg(EVENT, "event can be ignored\n"); + return; + } + if (ifevent->ifidx >= BRCMF_MAX_IFS) { + brcmf_err("invalid interface index: %u\n", ifevent->ifidx); + return; + } + + ifp = drvr->iflist[ifevent->bssidx]; + + if (ifevent->action == BRCMF_E_IF_ADD) { + brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname, + emsg->addr); + ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx, + is_p2pdev, emsg->ifname, emsg->addr); + if (IS_ERR(ifp)) + return; + if (!is_p2pdev) + brcmf_fws_add_interface(ifp); + if (!drvr->fweh.evt_handler[BRCMF_E_IF]) + if (brcmf_net_attach(ifp, false) < 0) + return; + } + + if (ifp && ifevent->action == BRCMF_E_IF_CHANGE) + brcmf_fws_reset_interface(ifp); + + err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data); + + if (ifp && ifevent->action == BRCMF_E_IF_DEL) + brcmf_remove_interface(ifp); +} + +/** + * brcmf_fweh_dequeue_event() - get event from the queue. + * + * @fweh: firmware event handling info. + */ +static struct brcmf_fweh_queue_item * +brcmf_fweh_dequeue_event(struct brcmf_fweh_info *fweh) +{ + struct brcmf_fweh_queue_item *event = NULL; + ulong flags; + + spin_lock_irqsave(&fweh->evt_q_lock, flags); + if (!list_empty(&fweh->event_q)) { + event = list_first_entry(&fweh->event_q, + struct brcmf_fweh_queue_item, q); + list_del(&event->q); + } + spin_unlock_irqrestore(&fweh->evt_q_lock, flags); + + return event; +} + +/** + * brcmf_fweh_event_worker() - firmware event worker. + * + * @work: worker object. + */ +static void brcmf_fweh_event_worker(struct work_struct *work) +{ + struct brcmf_pub *drvr; + struct brcmf_if *ifp; + struct brcmf_fweh_info *fweh; + struct brcmf_fweh_queue_item *event; + int err = 0; + struct brcmf_event_msg_be *emsg_be; + struct brcmf_event_msg emsg; + + fweh = container_of(work, struct brcmf_fweh_info, event_work); + drvr = container_of(fweh, struct brcmf_pub, fweh); + + while ((event = brcmf_fweh_dequeue_event(fweh))) { + brcmf_dbg(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM\n", + brcmf_fweh_event_name(event->code), event->code, + event->emsg.ifidx, event->emsg.bsscfgidx, + event->emsg.addr); + + /* convert event message */ + emsg_be = &event->emsg; + emsg.version = be16_to_cpu(emsg_be->version); + emsg.flags = be16_to_cpu(emsg_be->flags); + emsg.event_code = event->code; + emsg.status = be32_to_cpu(emsg_be->status); + emsg.reason = be32_to_cpu(emsg_be->reason); + emsg.auth_type = be32_to_cpu(emsg_be->auth_type); + emsg.datalen = be32_to_cpu(emsg_be->datalen); + memcpy(emsg.addr, emsg_be->addr, ETH_ALEN); + memcpy(emsg.ifname, emsg_be->ifname, sizeof(emsg.ifname)); + emsg.ifidx = emsg_be->ifidx; + emsg.bsscfgidx = emsg_be->bsscfgidx; + + brcmf_dbg(EVENT, " version %u flags %u status %u reason %u\n", + emsg.version, emsg.flags, emsg.status, emsg.reason); + brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data, + min_t(u32, emsg.datalen, 64), + "event payload, len=%d\n", emsg.datalen); + + /* special handling of interface event */ + if (event->code == BRCMF_E_IF) { + brcmf_fweh_handle_if_event(drvr, &emsg, event->data); + goto event_free; + } + + if (event->code == BRCMF_E_TDLS_PEER_EVENT) + ifp = drvr->iflist[0]; + else + ifp = drvr->iflist[emsg.bsscfgidx]; + err = brcmf_fweh_call_event_handler(ifp, event->code, &emsg, + event->data); + if (err) { + brcmf_err("event handler failed (%d)\n", + event->code); + err = 0; + } +event_free: + kfree(event); + } +} + +/** + * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not). + * + * @ifp: ifp on which setup is taking place or finished. + * @ongoing: p2p device setup in progress (or not). + */ +void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing) +{ + ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing; +} + +/** + * brcmf_fweh_attach() - initialize firmware event handling. + * + * @drvr: driver information object. + */ +void brcmf_fweh_attach(struct brcmf_pub *drvr) +{ + struct brcmf_fweh_info *fweh = &drvr->fweh; + INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker); + spin_lock_init(&fweh->evt_q_lock); + INIT_LIST_HEAD(&fweh->event_q); +} + +/** + * brcmf_fweh_detach() - cleanup firmware event handling. + * + * @drvr: driver information object. + */ +void brcmf_fweh_detach(struct brcmf_pub *drvr) +{ + struct brcmf_fweh_info *fweh = &drvr->fweh; + struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); + s8 eventmask[BRCMF_EVENTING_MASK_LEN]; + + if (ifp) { + /* clear all events */ + memset(eventmask, 0, BRCMF_EVENTING_MASK_LEN); + (void)brcmf_fil_iovar_data_set(ifp, "event_msgs", + eventmask, + BRCMF_EVENTING_MASK_LEN); + } + /* cancel the worker */ + cancel_work_sync(&fweh->event_work); + WARN_ON(!list_empty(&fweh->event_q)); + memset(fweh->evt_handler, 0, sizeof(fweh->evt_handler)); +} + +/** + * brcmf_fweh_register() - register handler for given event code. + * + * @drvr: driver information object. + * @code: event code. + * @handler: handler for the given event code. + */ +int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code, + brcmf_fweh_handler_t handler) +{ + if (drvr->fweh.evt_handler[code]) { + brcmf_err("event code %d already registered\n", code); + return -ENOSPC; + } + drvr->fweh.evt_handler[code] = handler; + brcmf_dbg(TRACE, "event handler registered for %s\n", + brcmf_fweh_event_name(code)); + return 0; +} + +/** + * brcmf_fweh_unregister() - remove handler for given code. + * + * @drvr: driver information object. + * @code: event code. + */ +void brcmf_fweh_unregister(struct brcmf_pub *drvr, + enum brcmf_fweh_event_code code) +{ + brcmf_dbg(TRACE, "event handler cleared for %s\n", + brcmf_fweh_event_name(code)); + drvr->fweh.evt_handler[code] = NULL; +} + +/** + * brcmf_fweh_activate_events() - enables firmware events registered. + * + * @ifp: primary interface object. + */ +int brcmf_fweh_activate_events(struct brcmf_if *ifp) +{ + int i, err; + s8 eventmask[BRCMF_EVENTING_MASK_LEN]; + + for (i = 0; i < BRCMF_E_LAST; i++) { + if (ifp->drvr->fweh.evt_handler[i]) { + brcmf_dbg(EVENT, "enable event %s\n", + brcmf_fweh_event_name(i)); + setbit(eventmask, i); + } + } + + /* want to handle IF event as well */ + brcmf_dbg(EVENT, "enable event IF\n"); + setbit(eventmask, BRCMF_E_IF); + + err = brcmf_fil_iovar_data_set(ifp, "event_msgs", + eventmask, BRCMF_EVENTING_MASK_LEN); + if (err) + brcmf_err("Set event_msgs error (%d)\n", err); + + return err; +} + +/** + * brcmf_fweh_process_event() - process skb as firmware event. + * + * @drvr: driver information object. + * @event_packet: event packet to process. + * + * If the packet buffer contains a firmware event message it will + * dispatch the event to a registered handler (using worker). + */ +void brcmf_fweh_process_event(struct brcmf_pub *drvr, + struct brcmf_event *event_packet) +{ + enum brcmf_fweh_event_code code; + struct brcmf_fweh_info *fweh = &drvr->fweh; + struct brcmf_fweh_queue_item *event; + gfp_t alloc_flag = GFP_KERNEL; + void *data; + u32 datalen; + + /* get event info */ + code = get_unaligned_be32(&event_packet->msg.event_type); + datalen = get_unaligned_be32(&event_packet->msg.datalen); + data = &event_packet[1]; + + if (code >= BRCMF_E_LAST) + return; + + if (code != BRCMF_E_IF && !fweh->evt_handler[code]) + return; + + if (in_interrupt()) + alloc_flag = GFP_ATOMIC; + + event = kzalloc(sizeof(*event) + datalen, alloc_flag); + if (!event) + return; + + event->code = code; + event->ifidx = event_packet->msg.ifidx; + + /* use memcpy to get aligned event message */ + memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg)); + memcpy(event->data, data, datalen); + memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN); + + brcmf_fweh_queue_event(fweh, event); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h new file mode 100644 index 000000000000..d9a942842382 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2012 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 FWEH_H_ +#define FWEH_H_ + +#include +#include +#include +#include + +/* formward declarations */ +struct brcmf_pub; +struct brcmf_if; +struct brcmf_cfg80211_info; +struct brcmf_event; + +/* list of firmware events */ +#define BRCMF_FWEH_EVENT_ENUM_DEFLIST \ + BRCMF_ENUM_DEF(SET_SSID, 0) \ + BRCMF_ENUM_DEF(JOIN, 1) \ + BRCMF_ENUM_DEF(START, 2) \ + BRCMF_ENUM_DEF(AUTH, 3) \ + BRCMF_ENUM_DEF(AUTH_IND, 4) \ + BRCMF_ENUM_DEF(DEAUTH, 5) \ + BRCMF_ENUM_DEF(DEAUTH_IND, 6) \ + BRCMF_ENUM_DEF(ASSOC, 7) \ + BRCMF_ENUM_DEF(ASSOC_IND, 8) \ + BRCMF_ENUM_DEF(REASSOC, 9) \ + BRCMF_ENUM_DEF(REASSOC_IND, 10) \ + BRCMF_ENUM_DEF(DISASSOC, 11) \ + BRCMF_ENUM_DEF(DISASSOC_IND, 12) \ + BRCMF_ENUM_DEF(QUIET_START, 13) \ + BRCMF_ENUM_DEF(QUIET_END, 14) \ + BRCMF_ENUM_DEF(BEACON_RX, 15) \ + BRCMF_ENUM_DEF(LINK, 16) \ + BRCMF_ENUM_DEF(MIC_ERROR, 17) \ + BRCMF_ENUM_DEF(NDIS_LINK, 18) \ + BRCMF_ENUM_DEF(ROAM, 19) \ + BRCMF_ENUM_DEF(TXFAIL, 20) \ + BRCMF_ENUM_DEF(PMKID_CACHE, 21) \ + BRCMF_ENUM_DEF(RETROGRADE_TSF, 22) \ + BRCMF_ENUM_DEF(PRUNE, 23) \ + BRCMF_ENUM_DEF(AUTOAUTH, 24) \ + BRCMF_ENUM_DEF(EAPOL_MSG, 25) \ + BRCMF_ENUM_DEF(SCAN_COMPLETE, 26) \ + BRCMF_ENUM_DEF(ADDTS_IND, 27) \ + BRCMF_ENUM_DEF(DELTS_IND, 28) \ + BRCMF_ENUM_DEF(BCNSENT_IND, 29) \ + BRCMF_ENUM_DEF(BCNRX_MSG, 30) \ + BRCMF_ENUM_DEF(BCNLOST_MSG, 31) \ + BRCMF_ENUM_DEF(ROAM_PREP, 32) \ + BRCMF_ENUM_DEF(PFN_NET_FOUND, 33) \ + BRCMF_ENUM_DEF(PFN_NET_LOST, 34) \ + BRCMF_ENUM_DEF(RESET_COMPLETE, 35) \ + BRCMF_ENUM_DEF(JOIN_START, 36) \ + BRCMF_ENUM_DEF(ROAM_START, 37) \ + BRCMF_ENUM_DEF(ASSOC_START, 38) \ + BRCMF_ENUM_DEF(IBSS_ASSOC, 39) \ + BRCMF_ENUM_DEF(RADIO, 40) \ + BRCMF_ENUM_DEF(PSM_WATCHDOG, 41) \ + BRCMF_ENUM_DEF(PROBREQ_MSG, 44) \ + BRCMF_ENUM_DEF(SCAN_CONFIRM_IND, 45) \ + BRCMF_ENUM_DEF(PSK_SUP, 46) \ + BRCMF_ENUM_DEF(COUNTRY_CODE_CHANGED, 47) \ + BRCMF_ENUM_DEF(EXCEEDED_MEDIUM_TIME, 48) \ + BRCMF_ENUM_DEF(ICV_ERROR, 49) \ + BRCMF_ENUM_DEF(UNICAST_DECODE_ERROR, 50) \ + BRCMF_ENUM_DEF(MULTICAST_DECODE_ERROR, 51) \ + BRCMF_ENUM_DEF(TRACE, 52) \ + BRCMF_ENUM_DEF(IF, 54) \ + BRCMF_ENUM_DEF(P2P_DISC_LISTEN_COMPLETE, 55) \ + BRCMF_ENUM_DEF(RSSI, 56) \ + BRCMF_ENUM_DEF(EXTLOG_MSG, 58) \ + BRCMF_ENUM_DEF(ACTION_FRAME, 59) \ + BRCMF_ENUM_DEF(ACTION_FRAME_COMPLETE, 60) \ + BRCMF_ENUM_DEF(PRE_ASSOC_IND, 61) \ + BRCMF_ENUM_DEF(PRE_REASSOC_IND, 62) \ + BRCMF_ENUM_DEF(CHANNEL_ADOPTED, 63) \ + BRCMF_ENUM_DEF(AP_STARTED, 64) \ + BRCMF_ENUM_DEF(DFS_AP_STOP, 65) \ + BRCMF_ENUM_DEF(DFS_AP_RESUME, 66) \ + BRCMF_ENUM_DEF(ESCAN_RESULT, 69) \ + BRCMF_ENUM_DEF(ACTION_FRAME_OFF_CHAN_COMPLETE, 70) \ + BRCMF_ENUM_DEF(PROBERESP_MSG, 71) \ + BRCMF_ENUM_DEF(P2P_PROBEREQ_MSG, 72) \ + BRCMF_ENUM_DEF(DCS_REQUEST, 73) \ + BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \ + BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \ + BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \ + BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) + +#define BRCMF_ENUM_DEF(id, val) \ + BRCMF_E_##id = (val), + +/* firmware event codes sent by the dongle */ +enum brcmf_fweh_event_code { + BRCMF_FWEH_EVENT_ENUM_DEFLIST + /* this determines event mask length which must match + * minimum length check in device firmware so it is + * hard-coded here. + */ + BRCMF_E_LAST = 139 +}; +#undef BRCMF_ENUM_DEF + +#define BRCMF_EVENTING_MASK_LEN DIV_ROUND_UP(BRCMF_E_LAST, 8) + +/* flags field values in struct brcmf_event_msg */ +#define BRCMF_EVENT_MSG_LINK 0x01 +#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02 +#define BRCMF_EVENT_MSG_GROUP 0x04 + +/* status field values in struct brcmf_event_msg */ +#define BRCMF_E_STATUS_SUCCESS 0 +#define BRCMF_E_STATUS_FAIL 1 +#define BRCMF_E_STATUS_TIMEOUT 2 +#define BRCMF_E_STATUS_NO_NETWORKS 3 +#define BRCMF_E_STATUS_ABORT 4 +#define BRCMF_E_STATUS_NO_ACK 5 +#define BRCMF_E_STATUS_UNSOLICITED 6 +#define BRCMF_E_STATUS_ATTEMPT 7 +#define BRCMF_E_STATUS_PARTIAL 8 +#define BRCMF_E_STATUS_NEWSCAN 9 +#define BRCMF_E_STATUS_NEWASSOC 10 +#define BRCMF_E_STATUS_11HQUIET 11 +#define BRCMF_E_STATUS_SUPPRESS 12 +#define BRCMF_E_STATUS_NOCHANS 13 +#define BRCMF_E_STATUS_CS_ABORT 15 +#define BRCMF_E_STATUS_ERROR 16 + +/* reason field values in struct brcmf_event_msg */ +#define BRCMF_E_REASON_INITIAL_ASSOC 0 +#define BRCMF_E_REASON_LOW_RSSI 1 +#define BRCMF_E_REASON_DEAUTH 2 +#define BRCMF_E_REASON_DISASSOC 3 +#define BRCMF_E_REASON_BCNS_LOST 4 +#define BRCMF_E_REASON_MINTXRATE 9 +#define BRCMF_E_REASON_TXFAIL 10 + +#define BRCMF_E_REASON_LINK_BSSCFG_DIS 4 +#define BRCMF_E_REASON_FAST_ROAM_FAILED 5 +#define BRCMF_E_REASON_DIRECTED_ROAM 6 +#define BRCMF_E_REASON_TSPEC_REJECTED 7 +#define BRCMF_E_REASON_BETTER_AP 8 + +#define BRCMF_E_REASON_TDLS_PEER_DISCOVERED 0 +#define BRCMF_E_REASON_TDLS_PEER_CONNECTED 1 +#define BRCMF_E_REASON_TDLS_PEER_DISCONNECTED 2 + +/* action field values for brcmf_ifevent */ +#define BRCMF_E_IF_ADD 1 +#define BRCMF_E_IF_DEL 2 +#define BRCMF_E_IF_CHANGE 3 + +/* flag field values for brcmf_ifevent */ +#define BRCMF_E_IF_FLAG_NOIF 1 + +/* role field values for brcmf_ifevent */ +#define BRCMF_E_IF_ROLE_STA 0 +#define BRCMF_E_IF_ROLE_AP 1 +#define BRCMF_E_IF_ROLE_WDS 2 +#define BRCMF_E_IF_ROLE_P2P_GO 3 +#define BRCMF_E_IF_ROLE_P2P_CLIENT 4 + +/** + * definitions for event packet validation. + */ +#define BRCMF_EVENT_OUI_OFFSET 19 +#define BRCM_OUI "\x00\x10\x18" +#define DOT11_OUI_LEN 3 +#define BCMILCP_BCM_SUBTYPE_EVENT 1 + + +/** + * struct brcmf_event_msg - firmware event message. + * + * @version: version information. + * @flags: event flags. + * @event_code: firmware event code. + * @status: status information. + * @reason: reason code. + * @auth_type: authentication type. + * @datalen: lenght of event data buffer. + * @addr: ether address. + * @ifname: interface name. + * @ifidx: interface index. + * @bsscfgidx: bsscfg index. + */ +struct brcmf_event_msg { + u16 version; + u16 flags; + u32 event_code; + u32 status; + u32 reason; + s32 auth_type; + u32 datalen; + u8 addr[ETH_ALEN]; + char ifname[IFNAMSIZ]; + u8 ifidx; + u8 bsscfgidx; +}; + +struct brcmf_if_event { + u8 ifidx; + u8 action; + u8 flags; + u8 bssidx; + u8 role; +}; + +typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data); + +/** + * struct brcmf_fweh_info - firmware event handling information. + * + * @p2pdev_setup_ongoing: P2P device creation in progress. + * @event_work: event worker. + * @evt_q_lock: lock for event queue protection. + * @event_q: event queue. + * @evt_handler: registered event handlers. + */ +struct brcmf_fweh_info { + bool p2pdev_setup_ongoing; + struct work_struct event_work; + spinlock_t evt_q_lock; + struct list_head event_q; + int (*evt_handler[BRCMF_E_LAST])(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data); +}; + +void brcmf_fweh_attach(struct brcmf_pub *drvr); +void brcmf_fweh_detach(struct brcmf_pub *drvr); +int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code, + int (*handler)(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data)); +void brcmf_fweh_unregister(struct brcmf_pub *drvr, + enum brcmf_fweh_event_code code); +int brcmf_fweh_activate_events(struct brcmf_if *ifp); +void brcmf_fweh_process_event(struct brcmf_pub *drvr, + struct brcmf_event *event_packet); +void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing); + +static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, + struct sk_buff *skb) +{ + struct brcmf_event *event_packet; + u8 *data; + u16 usr_stype; + + /* only process events when protocol matches */ + if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL)) + return; + + /* check for BRCM oui match */ + event_packet = (struct brcmf_event *)skb_mac_header(skb); + data = (u8 *)event_packet; + data += BRCMF_EVENT_OUI_OFFSET; + if (memcmp(BRCM_OUI, data, DOT11_OUI_LEN)) + return; + + /* final match on usr_subtype */ + data += DOT11_OUI_LEN; + usr_stype = get_unaligned_be16(data); + if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT) + return; + + brcmf_fweh_process_event(drvr, event_packet); +} + +#endif /* FWEH_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c new file mode 100644 index 000000000000..dcfa0bb149ce --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2012 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. + */ + +/* FWIL is the Firmware Interface Layer. In this module the support functions + * are located to set and get variables to and from the firmware. + */ + +#include +#include +#include +#include +#include "core.h" +#include "bus.h" +#include "debug.h" +#include "tracepoint.h" +#include "fwil.h" +#include "proto.h" + + +#define MAX_HEX_DUMP_LEN 64 + +#ifdef DEBUG +static const char * const brcmf_fil_errstr[] = { + "BCME_OK", + "BCME_ERROR", + "BCME_BADARG", + "BCME_BADOPTION", + "BCME_NOTUP", + "BCME_NOTDOWN", + "BCME_NOTAP", + "BCME_NOTSTA", + "BCME_BADKEYIDX", + "BCME_RADIOOFF", + "BCME_NOTBANDLOCKED", + "BCME_NOCLK", + "BCME_BADRATESET", + "BCME_BADBAND", + "BCME_BUFTOOSHORT", + "BCME_BUFTOOLONG", + "BCME_BUSY", + "BCME_NOTASSOCIATED", + "BCME_BADSSIDLEN", + "BCME_OUTOFRANGECHAN", + "BCME_BADCHAN", + "BCME_BADADDR", + "BCME_NORESOURCE", + "BCME_UNSUPPORTED", + "BCME_BADLEN", + "BCME_NOTREADY", + "BCME_EPERM", + "BCME_NOMEM", + "BCME_ASSOCIATED", + "BCME_RANGE", + "BCME_NOTFOUND", + "BCME_WME_NOT_ENABLED", + "BCME_TSPEC_NOTFOUND", + "BCME_ACM_NOTSUPPORTED", + "BCME_NOT_WME_ASSOCIATION", + "BCME_SDIO_ERROR", + "BCME_DONGLE_DOWN", + "BCME_VERSION", + "BCME_TXFAIL", + "BCME_RXFAIL", + "BCME_NODEVICE", + "BCME_NMODE_DISABLED", + "BCME_NONRESIDENT", + "BCME_SCANREJECT", + "BCME_USAGE_ERROR", + "BCME_IOCTL_ERROR", + "BCME_SERIAL_PORT_ERR", + "BCME_DISABLED", + "BCME_DECERR", + "BCME_ENCERR", + "BCME_MICERR", + "BCME_REPLAY", + "BCME_IE_NOTFOUND", +}; + +static const char *brcmf_fil_get_errstr(u32 err) +{ + if (err >= ARRAY_SIZE(brcmf_fil_errstr)) + return "(unknown)"; + + return brcmf_fil_errstr[err]; +} +#else +static const char *brcmf_fil_get_errstr(u32 err) +{ + return ""; +} +#endif /* DEBUG */ + +static s32 +brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) +{ + struct brcmf_pub *drvr = ifp->drvr; + s32 err; + + if (drvr->bus_if->state != BRCMF_BUS_UP) { + brcmf_err("bus is down. we have nothing to do.\n"); + return -EIO; + } + + if (data != NULL) + len = min_t(uint, len, BRCMF_DCMD_MAXLEN); + if (set) + err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd, data, len); + else + err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len); + + if (err >= 0) + return 0; + + brcmf_dbg(FIL, "Failed: %s (%d)\n", + brcmf_fil_get_errstr((u32)(-err)), err); + return -EBADE; +} + +s32 +brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) +{ + s32 err; + + mutex_lock(&ifp->drvr->proto_block); + + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); + + err = brcmf_fil_cmd_data(ifp, cmd, data, len, true); + mutex_unlock(&ifp->drvr->proto_block); + + return err; +} + +s32 +brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) +{ + s32 err; + + mutex_lock(&ifp->drvr->proto_block); + err = brcmf_fil_cmd_data(ifp, cmd, data, len, false); + + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); + + mutex_unlock(&ifp->drvr->proto_block); + + return err; +} + + +s32 +brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data) +{ + s32 err; + __le32 data_le = cpu_to_le32(data); + + mutex_lock(&ifp->drvr->proto_block); + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data); + err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true); + mutex_unlock(&ifp->drvr->proto_block); + + return err; +} + +s32 +brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) +{ + s32 err; + __le32 data_le = cpu_to_le32(*data); + + mutex_lock(&ifp->drvr->proto_block); + err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false); + mutex_unlock(&ifp->drvr->proto_block); + *data = le32_to_cpu(data_le); + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data); + + return err; +} + +static u32 +brcmf_create_iovar(char *name, const char *data, u32 datalen, + char *buf, u32 buflen) +{ + u32 len; + + len = strlen(name) + 1; + + if ((len + datalen) > buflen) + return 0; + + memcpy(buf, name, len); + + /* append data onto the end of the name string */ + if (data && datalen) + memcpy(&buf[len], data, datalen); + + return len + datalen; +} + + +s32 +brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, + u32 len) +{ + struct brcmf_pub *drvr = ifp->drvr; + s32 err; + u32 buflen; + + mutex_lock(&drvr->proto_block); + + brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); + + buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, + sizeof(drvr->proto_buf)); + if (buflen) { + err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, + buflen, true); + } else { + err = -EPERM; + brcmf_err("Creating iovar failed\n"); + } + + mutex_unlock(&drvr->proto_block); + return err; +} + +s32 +brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, + u32 len) +{ + struct brcmf_pub *drvr = ifp->drvr; + s32 err; + u32 buflen; + + mutex_lock(&drvr->proto_block); + + buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, + sizeof(drvr->proto_buf)); + if (buflen) { + err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, + buflen, false); + if (err == 0) + memcpy(data, drvr->proto_buf, len); + } else { + err = -EPERM; + brcmf_err("Creating iovar failed\n"); + } + + brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); + + mutex_unlock(&drvr->proto_block); + return err; +} + +s32 +brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data) +{ + __le32 data_le = cpu_to_le32(data); + + return brcmf_fil_iovar_data_set(ifp, name, &data_le, sizeof(data_le)); +} + +s32 +brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data) +{ + __le32 data_le = cpu_to_le32(*data); + s32 err; + + err = brcmf_fil_iovar_data_get(ifp, name, &data_le, sizeof(data_le)); + if (err == 0) + *data = le32_to_cpu(data_le); + return err; +} + +static u32 +brcmf_create_bsscfg(s32 bssidx, char *name, char *data, u32 datalen, char *buf, + u32 buflen) +{ + const s8 *prefix = "bsscfg:"; + s8 *p; + u32 prefixlen; + u32 namelen; + u32 iolen; + __le32 bssidx_le; + + if (bssidx == 0) + return brcmf_create_iovar(name, data, datalen, buf, buflen); + + prefixlen = strlen(prefix); + namelen = strlen(name) + 1; /* lengh of iovar name + null */ + iolen = prefixlen + namelen + sizeof(bssidx_le) + datalen; + + if (buflen < iolen) { + brcmf_err("buffer is too short\n"); + return 0; + } + + p = buf; + + /* copy prefix, no null */ + memcpy(p, prefix, prefixlen); + p += prefixlen; + + /* copy iovar name including null */ + memcpy(p, name, namelen); + p += namelen; + + /* bss config index as first data */ + bssidx_le = cpu_to_le32(bssidx); + memcpy(p, &bssidx_le, sizeof(bssidx_le)); + p += sizeof(bssidx_le); + + /* parameter buffer follows */ + if (datalen) + memcpy(p, data, datalen); + + return iolen; +} + +s32 +brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, + void *data, u32 len) +{ + struct brcmf_pub *drvr = ifp->drvr; + s32 err; + u32 buflen; + + mutex_lock(&drvr->proto_block); + + brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, + ifp->bssidx, name, len); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); + + buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, + drvr->proto_buf, sizeof(drvr->proto_buf)); + if (buflen) { + err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, + buflen, true); + } else { + err = -EPERM; + brcmf_err("Creating bsscfg failed\n"); + } + + mutex_unlock(&drvr->proto_block); + return err; +} + +s32 +brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, + void *data, u32 len) +{ + struct brcmf_pub *drvr = ifp->drvr; + s32 err; + u32 buflen; + + mutex_lock(&drvr->proto_block); + + buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, + drvr->proto_buf, sizeof(drvr->proto_buf)); + if (buflen) { + err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, + buflen, false); + if (err == 0) + memcpy(data, drvr->proto_buf, len); + } else { + err = -EPERM; + brcmf_err("Creating bsscfg failed\n"); + } + brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, + ifp->bssidx, name, len); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); + + mutex_unlock(&drvr->proto_block); + return err; + +} + +s32 +brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data) +{ + __le32 data_le = cpu_to_le32(data); + + return brcmf_fil_bsscfg_data_set(ifp, name, &data_le, + sizeof(data_le)); +} + +s32 +brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data) +{ + __le32 data_le = cpu_to_le32(*data); + s32 err; + + err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le, + sizeof(data_le)); + if (err == 0) + *data = le32_to_cpu(data_le); + return err; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h new file mode 100644 index 000000000000..b20fc0f82a48 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2012 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 _fwil_h_ +#define _fwil_h_ + +/******************************************************************************* + * Dongle command codes that are interpreted by firmware + ******************************************************************************/ +#define BRCMF_C_GET_VERSION 1 +#define BRCMF_C_UP 2 +#define BRCMF_C_DOWN 3 +#define BRCMF_C_SET_PROMISC 10 +#define BRCMF_C_GET_RATE 12 +#define BRCMF_C_GET_INFRA 19 +#define BRCMF_C_SET_INFRA 20 +#define BRCMF_C_GET_AUTH 21 +#define BRCMF_C_SET_AUTH 22 +#define BRCMF_C_GET_BSSID 23 +#define BRCMF_C_GET_SSID 25 +#define BRCMF_C_SET_SSID 26 +#define BRCMF_C_TERMINATED 28 +#define BRCMF_C_GET_CHANNEL 29 +#define BRCMF_C_SET_CHANNEL 30 +#define BRCMF_C_GET_SRL 31 +#define BRCMF_C_SET_SRL 32 +#define BRCMF_C_GET_LRL 33 +#define BRCMF_C_SET_LRL 34 +#define BRCMF_C_GET_RADIO 37 +#define BRCMF_C_SET_RADIO 38 +#define BRCMF_C_GET_PHYTYPE 39 +#define BRCMF_C_SET_KEY 45 +#define BRCMF_C_GET_REGULATORY 46 +#define BRCMF_C_SET_REGULATORY 47 +#define BRCMF_C_SET_PASSIVE_SCAN 49 +#define BRCMF_C_SCAN 50 +#define BRCMF_C_SCAN_RESULTS 51 +#define BRCMF_C_DISASSOC 52 +#define BRCMF_C_REASSOC 53 +#define BRCMF_C_SET_ROAM_TRIGGER 55 +#define BRCMF_C_SET_ROAM_DELTA 57 +#define BRCMF_C_GET_BCNPRD 75 +#define BRCMF_C_SET_BCNPRD 76 +#define BRCMF_C_GET_DTIMPRD 77 +#define BRCMF_C_SET_DTIMPRD 78 +#define BRCMF_C_SET_COUNTRY 84 +#define BRCMF_C_GET_PM 85 +#define BRCMF_C_SET_PM 86 +#define BRCMF_C_GET_REVINFO 98 +#define BRCMF_C_GET_CURR_RATESET 114 +#define BRCMF_C_GET_AP 117 +#define BRCMF_C_SET_AP 118 +#define BRCMF_C_SET_SCB_AUTHORIZE 121 +#define BRCMF_C_SET_SCB_DEAUTHORIZE 122 +#define BRCMF_C_GET_RSSI 127 +#define BRCMF_C_GET_WSEC 133 +#define BRCMF_C_SET_WSEC 134 +#define BRCMF_C_GET_PHY_NOISE 135 +#define BRCMF_C_GET_BSS_INFO 136 +#define BRCMF_C_GET_BANDLIST 140 +#define BRCMF_C_SET_SCB_TIMEOUT 158 +#define BRCMF_C_GET_ASSOCLIST 159 +#define BRCMF_C_GET_PHYLIST 180 +#define BRCMF_C_SET_SCAN_CHANNEL_TIME 185 +#define BRCMF_C_SET_SCAN_UNASSOC_TIME 187 +#define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201 +#define BRCMF_C_GET_VALID_CHANNELS 217 +#define BRCMF_C_GET_KEY_PRIMARY 235 +#define BRCMF_C_SET_KEY_PRIMARY 236 +#define BRCMF_C_SET_SCAN_PASSIVE_TIME 258 +#define BRCMF_C_GET_VAR 262 +#define BRCMF_C_SET_VAR 263 + +s32 brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); +s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); +s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data); +s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data); + +s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, + u32 len); +s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, + u32 len); +s32 brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data); +s32 brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data); + +s32 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, void *data, + u32 len); +s32 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, void *data, + u32 len); +s32 brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data); +s32 brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data); + +#endif /* _fwil_h_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h new file mode 100644 index 000000000000..daa427b46712 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -0,0 +1,637 @@ +/* + * Copyright (c) 2012 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 FWIL_TYPES_H_ +#define FWIL_TYPES_H_ + +#include + + +#define BRCMF_FIL_ACTION_FRAME_SIZE 1800 + +/* ARP Offload feature flags for arp_ol iovar */ +#define BRCMF_ARP_OL_AGENT 0x00000001 +#define BRCMF_ARP_OL_SNOOP 0x00000002 +#define BRCMF_ARP_OL_HOST_AUTO_REPLY 0x00000004 +#define BRCMF_ARP_OL_PEER_AUTO_REPLY 0x00000008 + +#define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */ +#define BRCMF_BSS_RSSI_ON_CHANNEL 0x0002 + +#define BRCMF_STA_WME 0x00000002 /* WMM association */ +#define BRCMF_STA_AUTHE 0x00000008 /* Authenticated */ +#define BRCMF_STA_ASSOC 0x00000010 /* Associated */ +#define BRCMF_STA_AUTHO 0x00000020 /* Authorized */ +#define BRCMF_STA_SCBSTATS 0x00004000 /* Per STA debug stats */ + +/* size of brcmf_scan_params not including variable length array */ +#define BRCMF_SCAN_PARAMS_FIXED_SIZE 64 + +/* masks for channel and ssid count */ +#define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff +#define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16 + +/* primary (ie tx) key */ +#define BRCMF_PRIMARY_KEY (1 << 1) +#define DOT11_BSSTYPE_ANY 2 +#define BRCMF_ESCAN_REQ_VERSION 1 + +#define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */ + +/* OBSS Coex Auto/On/Off */ +#define BRCMF_OBSS_COEX_AUTO (-1) +#define BRCMF_OBSS_COEX_OFF 0 +#define BRCMF_OBSS_COEX_ON 1 + +/* WOWL bits */ +/* Wakeup on Magic packet: */ +#define BRCMF_WOWL_MAGIC (1 << 0) +/* Wakeup on Netpattern */ +#define BRCMF_WOWL_NET (1 << 1) +/* Wakeup on loss-of-link due to Disassoc/Deauth: */ +#define BRCMF_WOWL_DIS (1 << 2) +/* Wakeup on retrograde TSF: */ +#define BRCMF_WOWL_RETR (1 << 3) +/* Wakeup on loss of beacon: */ +#define BRCMF_WOWL_BCN (1 << 4) +/* Wakeup after test: */ +#define BRCMF_WOWL_TST (1 << 5) +/* Wakeup after PTK refresh: */ +#define BRCMF_WOWL_M1 (1 << 6) +/* Wakeup after receipt of EAP-Identity Req: */ +#define BRCMF_WOWL_EAPID (1 << 7) +/* Wakeind via PME(0) or GPIO(1): */ +#define BRCMF_WOWL_PME_GPIO (1 << 8) +/* need tkip phase 1 key to be updated by the driver: */ +#define BRCMF_WOWL_NEEDTKIP1 (1 << 9) +/* enable wakeup if GTK fails: */ +#define BRCMF_WOWL_GTK_FAILURE (1 << 10) +/* support extended magic packets: */ +#define BRCMF_WOWL_EXTMAGPAT (1 << 11) +/* support ARP/NS/keepalive offloading: */ +#define BRCMF_WOWL_ARPOFFLOAD (1 << 12) +/* read protocol version for EAPOL frames: */ +#define BRCMF_WOWL_WPA2 (1 << 13) +/* If the bit is set, use key rotaton: */ +#define BRCMF_WOWL_KEYROT (1 << 14) +/* If the bit is set, frm received was bcast frame: */ +#define BRCMF_WOWL_BCAST (1 << 15) +/* If the bit is set, scan offload is enabled: */ +#define BRCMF_WOWL_SCANOL (1 << 16) +/* Wakeup on tcpkeep alive timeout: */ +#define BRCMF_WOWL_TCPKEEP_TIME (1 << 17) +/* Wakeup on mDNS Conflict Resolution: */ +#define BRCMF_WOWL_MDNS_CONFLICT (1 << 18) +/* Wakeup on mDNS Service Connect: */ +#define BRCMF_WOWL_MDNS_SERVICE (1 << 19) +/* tcp keepalive got data: */ +#define BRCMF_WOWL_TCPKEEP_DATA (1 << 20) +/* Firmware died in wowl mode: */ +#define BRCMF_WOWL_FW_HALT (1 << 21) +/* Enable detection of radio button changes: */ +#define BRCMF_WOWL_ENAB_HWRADIO (1 << 22) +/* Offloads detected MIC failure(s): */ +#define BRCMF_WOWL_MIC_FAIL (1 << 23) +/* Wakeup in Unassociated state (Net/Magic Pattern): */ +#define BRCMF_WOWL_UNASSOC (1 << 24) +/* Wakeup if received matched secured pattern: */ +#define BRCMF_WOWL_SECURE (1 << 25) +/* Link Down indication in WoWL mode: */ +#define BRCMF_WOWL_LINKDOWN (1 << 31) + +#define BRCMF_WOWL_MAXPATTERNS 8 +#define BRCMF_WOWL_MAXPATTERNSIZE 128 + +#define BRCMF_COUNTRY_BUF_SZ 4 +#define BRCMF_ANT_MAX 4 + +#define BRCMF_MAX_ASSOCLIST 128 + +/* join preference types for join_pref iovar */ +enum brcmf_join_pref_types { + BRCMF_JOIN_PREF_RSSI = 1, + BRCMF_JOIN_PREF_WPA, + BRCMF_JOIN_PREF_BAND, + BRCMF_JOIN_PREF_RSSI_DELTA, +}; + +enum brcmf_fil_p2p_if_types { + BRCMF_FIL_P2P_IF_CLIENT, + BRCMF_FIL_P2P_IF_GO, + BRCMF_FIL_P2P_IF_DYNBCN_GO, + BRCMF_FIL_P2P_IF_DEV, +}; + +enum brcmf_wowl_pattern_type { + BRCMF_WOWL_PATTERN_TYPE_BITMAP = 0, + BRCMF_WOWL_PATTERN_TYPE_ARP, + BRCMF_WOWL_PATTERN_TYPE_NA +}; + +struct brcmf_fil_p2p_if_le { + u8 addr[ETH_ALEN]; + __le16 type; + __le16 chspec; +}; + +struct brcmf_fil_chan_info_le { + __le32 hw_channel; + __le32 target_channel; + __le32 scan_channel; +}; + +struct brcmf_fil_action_frame_le { + u8 da[ETH_ALEN]; + __le16 len; + __le32 packet_id; + u8 data[BRCMF_FIL_ACTION_FRAME_SIZE]; +}; + +struct brcmf_fil_af_params_le { + __le32 channel; + __le32 dwell_time; + u8 bssid[ETH_ALEN]; + u8 pad[2]; + struct brcmf_fil_action_frame_le action_frame; +}; + +struct brcmf_fil_bss_enable_le { + __le32 bsscfg_idx; + __le32 enable; +}; + +struct brcmf_fil_bwcap_le { + __le32 band; + __le32 bw_cap; +}; + +/** + * struct tdls_iovar - common structure for tdls iovars. + * + * @ea: ether address of peer station. + * @mode: mode value depending on specific tdls iovar. + * @chanspec: channel specification. + * @pad: unused (for future use). + */ +struct brcmf_tdls_iovar_le { + u8 ea[ETH_ALEN]; /* Station address */ + u8 mode; /* mode: depends on iovar */ + __le16 chanspec; + __le32 pad; /* future */ +}; + +enum brcmf_tdls_manual_ep_ops { + BRCMF_TDLS_MANUAL_EP_CREATE = 1, + BRCMF_TDLS_MANUAL_EP_DELETE = 3, + BRCMF_TDLS_MANUAL_EP_DISCOVERY = 6 +}; + +/* Pattern matching filter. Specifies an offset within received packets to + * start matching, the pattern to match, the size of the pattern, and a bitmask + * that indicates which bits within the pattern should be matched. + */ +struct brcmf_pkt_filter_pattern_le { + /* + * Offset within received packet to start pattern matching. + * Offset '0' is the first byte of the ethernet header. + */ + __le32 offset; + /* Size of the pattern. Bitmask must be the same size.*/ + __le32 size_bytes; + /* + * Variable length mask and pattern data. mask starts at offset 0. + * Pattern immediately follows mask. + */ + u8 mask_and_pattern[1]; +}; + +/* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */ +struct brcmf_pkt_filter_le { + __le32 id; /* Unique filter id, specified by app. */ + __le32 type; /* Filter type (WL_PKT_FILTER_TYPE_xxx). */ + __le32 negate_match; /* Negate the result of filter matches */ + union { /* Filter definitions */ + struct brcmf_pkt_filter_pattern_le pattern; /* Filter pattern */ + } u; +}; + +/* IOVAR "pkt_filter_enable" parameter. */ +struct brcmf_pkt_filter_enable_le { + __le32 id; /* Unique filter id */ + __le32 enable; /* Enable/disable bool */ +}; + +/* BSS info structure + * Applications MUST CHECK ie_offset field and length field to access IEs and + * next bss_info structure in a vector (in struct brcmf_scan_results) + */ +struct brcmf_bss_info_le { + __le32 version; /* version field */ + __le32 length; /* byte length of data in this record, + * starting at version and including IEs + */ + u8 BSSID[ETH_ALEN]; + __le16 beacon_period; /* units are Kusec */ + __le16 capability; /* Capability information */ + u8 SSID_len; + u8 SSID[32]; + struct { + __le32 count; /* # rates in this set */ + u8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */ + } rateset; /* supported rates */ + __le16 chanspec; /* chanspec for bss */ + __le16 atim_window; /* units are Kusec */ + u8 dtim_period; /* DTIM period */ + __le16 RSSI; /* receive signal strength (in dBm) */ + s8 phy_noise; /* noise (in dBm) */ + + u8 n_cap; /* BSS is 802.11N Capable */ + /* 802.11N BSS Capabilities (based on HT_CAP_*): */ + __le32 nbss_cap; + u8 ctl_ch; /* 802.11N BSS control channel number */ + __le32 reserved32[1]; /* Reserved for expansion of BSS properties */ + u8 flags; /* flags */ + u8 reserved[3]; /* Reserved for expansion of BSS properties */ + u8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */ + + __le16 ie_offset; /* offset at which IEs start, from beginning */ + __le32 ie_length; /* byte length of Information Elements */ + __le16 SNR; /* average SNR of during frame reception */ + /* Add new fields here */ + /* variable length Information Elements */ +}; + +struct brcm_rateset_le { + /* # rates in this set */ + __le32 count; + /* rates in 500kbps units w/hi bit set if basic */ + u8 rates[BRCMF_MAXRATES_IN_SET]; +}; + +struct brcmf_ssid { + u32 SSID_len; + unsigned char SSID[32]; +}; + +struct brcmf_ssid_le { + __le32 SSID_len; + unsigned char SSID[32]; +}; + +struct brcmf_scan_params_le { + struct brcmf_ssid_le ssid_le; /* default: {0, ""} */ + u8 bssid[ETH_ALEN]; /* default: bcast */ + s8 bss_type; /* default: any, + * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT + */ + u8 scan_type; /* flags, 0 use default */ + __le32 nprobes; /* -1 use default, number of probes per channel */ + __le32 active_time; /* -1 use default, dwell time per channel for + * active scanning + */ + __le32 passive_time; /* -1 use default, dwell time per channel + * for passive scanning + */ + __le32 home_time; /* -1 use default, dwell time for the + * home channel between channel scans + */ + __le32 channel_num; /* count of channels and ssids that follow + * + * low half is count of channels in + * channel_list, 0 means default (use all + * available channels) + * + * high half is entries in struct brcmf_ssid + * array that follows channel_list, aligned for + * s32 (4 bytes) meaning an odd channel count + * implies a 2-byte pad between end of + * channel_list and first ssid + * + * if ssid count is zero, single ssid in the + * fixed parameter portion is assumed, otherwise + * ssid in the fixed portion is ignored + */ + __le16 channel_list[1]; /* list of chanspecs */ +}; + +struct brcmf_scan_results { + u32 buflen; + u32 version; + u32 count; + struct brcmf_bss_info_le bss_info_le[]; +}; + +struct brcmf_escan_params_le { + __le32 version; + __le16 action; + __le16 sync_id; + struct brcmf_scan_params_le params_le; +}; + +struct brcmf_escan_result_le { + __le32 buflen; + __le32 version; + __le16 sync_id; + __le16 bss_count; + struct brcmf_bss_info_le bss_info_le; +}; + +#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(struct brcmf_escan_result_le) - \ + sizeof(struct brcmf_bss_info_le)) + +/* used for association with a specific BSSID and chanspec list */ +struct brcmf_assoc_params_le { + /* 00:00:00:00:00:00: broadcast scan */ + u8 bssid[ETH_ALEN]; + /* 0: all available channels, otherwise count of chanspecs in + * chanspec_list */ + __le32 chanspec_num; + /* list of chanspecs */ + __le16 chanspec_list[1]; +}; + +/** + * struct join_pref params - parameters for preferred join selection. + * + * @type: preference type (see enum brcmf_join_pref_types). + * @len: length of bytes following (currently always 2). + * @rssi_gain: signal gain for selection (only when @type is RSSI_DELTA). + * @band: band to which selection preference applies. + * This is used if @type is BAND or RSSI_DELTA. + */ +struct brcmf_join_pref_params { + u8 type; + u8 len; + u8 rssi_gain; + u8 band; +}; + +/* used for join with or without a specific bssid and channel list */ +struct brcmf_join_params { + struct brcmf_ssid_le ssid_le; + struct brcmf_assoc_params_le params_le; +}; + +/* scan params for extended join */ +struct brcmf_join_scan_params_le { + u8 scan_type; /* 0 use default, active or passive scan */ + __le32 nprobes; /* -1 use default, nr of probes per channel */ + __le32 active_time; /* -1 use default, dwell time per channel for + * active scanning + */ + __le32 passive_time; /* -1 use default, dwell time per channel + * for passive scanning + */ + __le32 home_time; /* -1 use default, dwell time for the home + * channel between channel scans + */ +}; + +/* extended join params */ +struct brcmf_ext_join_params_le { + struct brcmf_ssid_le ssid_le; /* {0, ""}: wildcard scan */ + struct brcmf_join_scan_params_le scan_le; + struct brcmf_assoc_params_le assoc_le; +}; + +struct brcmf_wsec_key { + u32 index; /* key index */ + u32 len; /* key length */ + u8 data[WLAN_MAX_KEY_LEN]; /* key data */ + u32 pad_1[18]; + u32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ + u32 flags; /* misc flags */ + u32 pad_2[3]; + u32 iv_initialized; /* has IV been initialized already? */ + u32 pad_3; + /* Rx IV */ + struct { + u32 hi; /* upper 32 bits of IV */ + u16 lo; /* lower 16 bits of IV */ + } rxiv; + u32 pad_4[2]; + u8 ea[ETH_ALEN]; /* per station */ +}; + +/* + * dongle requires same struct as above but with fields in little endian order + */ +struct brcmf_wsec_key_le { + __le32 index; /* key index */ + __le32 len; /* key length */ + u8 data[WLAN_MAX_KEY_LEN]; /* key data */ + __le32 pad_1[18]; + __le32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ + __le32 flags; /* misc flags */ + __le32 pad_2[3]; + __le32 iv_initialized; /* has IV been initialized already? */ + __le32 pad_3; + /* Rx IV */ + struct { + __le32 hi; /* upper 32 bits of IV */ + __le16 lo; /* lower 16 bits of IV */ + } rxiv; + __le32 pad_4[2]; + u8 ea[ETH_ALEN]; /* per station */ +}; + +/* Used to get specific STA parameters */ +struct brcmf_scb_val_le { + __le32 val; + u8 ea[ETH_ALEN]; +}; + +/* channel encoding */ +struct brcmf_channel_info_le { + __le32 hw_channel; + __le32 target_channel; + __le32 scan_channel; +}; + +struct brcmf_sta_info_le { + __le16 ver; /* version of this struct */ + __le16 len; /* length in bytes of this structure */ + __le16 cap; /* sta's advertised capabilities */ + __le32 flags; /* flags defined below */ + __le32 idle; /* time since data pkt rx'd from sta */ + u8 ea[ETH_ALEN]; /* Station address */ + __le32 count; /* # rates in this set */ + u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */ + /* w/hi bit set if basic */ + __le32 in; /* seconds elapsed since associated */ + __le32 listen_interval_inms; /* Min Listen interval in ms for STA */ + __le32 tx_pkts; /* # of packets transmitted */ + __le32 tx_failures; /* # of packets failed */ + __le32 rx_ucast_pkts; /* # of unicast packets received */ + __le32 rx_mcast_pkts; /* # of multicast packets received */ + __le32 tx_rate; /* Rate of last successful tx frame */ + __le32 rx_rate; /* Rate of last successful rx frame */ + __le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */ + __le32 rx_decrypt_failures; /* # of packet decrypted failed */ + __le32 tx_tot_pkts; /* # of tx pkts (ucast + mcast) */ + __le32 rx_tot_pkts; /* # of data packets recvd (uni + mcast) */ + __le32 tx_mcast_pkts; /* # of mcast pkts txed */ + __le64 tx_tot_bytes; /* data bytes txed (ucast + mcast) */ + __le64 rx_tot_bytes; /* data bytes recvd (ucast + mcast) */ + __le64 tx_ucast_bytes; /* data bytes txed (ucast) */ + __le64 tx_mcast_bytes; /* # data bytes txed (mcast) */ + __le64 rx_ucast_bytes; /* data bytes recvd (ucast) */ + __le64 rx_mcast_bytes; /* data bytes recvd (mcast) */ + s8 rssi[BRCMF_ANT_MAX]; /* per antenna rssi */ + s8 nf[BRCMF_ANT_MAX]; /* per antenna noise floor */ + __le16 aid; /* association ID */ + __le16 ht_capabilities; /* advertised ht caps */ + __le16 vht_flags; /* converted vht flags */ + __le32 tx_pkts_retry_cnt; /* # of frames where a retry was + * exhausted. + */ + __le32 tx_pkts_retry_exhausted; /* # of user frames where a retry + * was exhausted + */ + s8 rx_lastpkt_rssi[BRCMF_ANT_MAX]; /* Per antenna RSSI of last + * received data frame. + */ + /* TX WLAN retry/failure statistics: + * Separated for host requested frames and locally generated frames. + * Include unicast frame only where the retries/failures can be counted. + */ + __le32 tx_pkts_total; /* # user frames sent successfully */ + __le32 tx_pkts_retries; /* # user frames retries */ + __le32 tx_pkts_fw_total; /* # FW generated sent successfully */ + __le32 tx_pkts_fw_retries; /* # retries for FW generated frames */ + __le32 tx_pkts_fw_retry_exhausted; /* # FW generated where a retry + * was exhausted + */ + __le32 rx_pkts_retried; /* # rx with retry bit set */ + __le32 tx_rate_fallback; /* lowest fallback TX rate */ +}; + +struct brcmf_chanspec_list { + __le32 count; /* # of entries */ + __le32 element[1]; /* variable length uint32 list */ +}; + +/* + * WLC_E_PROBRESP_MSG + * WLC_E_P2P_PROBREQ_MSG + * WLC_E_ACTION_FRAME_RX + */ +struct brcmf_rx_mgmt_data { + __be16 version; + __be16 chanspec; + __be32 rssi; + __be32 mactime; + __be32 rate; +}; + +/** + * struct brcmf_fil_wowl_pattern_le - wowl pattern configuration struct. + * + * @cmd: "add", "del" or "clr". + * @masksize: Size of the mask in #of bytes + * @offset: Pattern byte offset in packet + * @patternoffset: Offset of start of pattern. Starting from field masksize. + * @patternsize: Size of the pattern itself in #of bytes + * @id: id + * @reasonsize: Size of the wakeup reason code + * @type: Type of pattern (enum brcmf_wowl_pattern_type) + */ +struct brcmf_fil_wowl_pattern_le { + u8 cmd[4]; + __le32 masksize; + __le32 offset; + __le32 patternoffset; + __le32 patternsize; + __le32 id; + __le32 reasonsize; + __le32 type; + /* u8 mask[] - Mask follows the structure above */ + /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */ +}; + +struct brcmf_mbss_ssid_le { + __le32 bsscfgidx; + __le32 SSID_len; + unsigned char SSID[32]; +}; + +/** + * struct brcmf_fil_country_le - country configuration structure. + * + * @country_abbrev: null-terminated country code used in the country IE. + * @rev: revision specifier for ccode. on set, -1 indicates unspecified. + * @ccode: null-terminated built-in country code. + */ +struct brcmf_fil_country_le { + char country_abbrev[BRCMF_COUNTRY_BUF_SZ]; + __le32 rev; + char ccode[BRCMF_COUNTRY_BUF_SZ]; +}; + +/** + * struct brcmf_rev_info_le - device revision info. + * + * @vendorid: PCI vendor id. + * @deviceid: device id of chip. + * @radiorev: radio revision. + * @chiprev: chip revision. + * @corerev: core revision. + * @boardid: board identifier (usu. PCI sub-device id). + * @boardvendor: board vendor (usu. PCI sub-vendor id). + * @boardrev: board revision. + * @driverrev: driver version. + * @ucoderev: microcode version. + * @bus: bus type. + * @chipnum: chip number. + * @phytype: phy type. + * @phyrev: phy revision. + * @anarev: anacore rev. + * @chippkg: chip package info. + * @nvramrev: nvram revision number. + */ +struct brcmf_rev_info_le { + __le32 vendorid; + __le32 deviceid; + __le32 radiorev; + __le32 chiprev; + __le32 corerev; + __le32 boardid; + __le32 boardvendor; + __le32 boardrev; + __le32 driverrev; + __le32 ucoderev; + __le32 bus; + __le32 chipnum; + __le32 phytype; + __le32 phyrev; + __le32 anarev; + __le32 chippkg; + __le32 nvramrev; +}; + +/** + * struct brcmf_assoclist_le - request assoc list. + * + * @count: indicates number of stations. + * @mac: MAC addresses of stations. + */ +struct brcmf_assoclist_le { + __le32 count; + u8 mac[BRCMF_MAX_ASSOCLIST][ETH_ALEN]; +}; + +#endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c new file mode 100644 index 000000000000..086cac3f86d6 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -0,0 +1,2271 @@ +/* + * Copyright (c) 2010 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include "core.h" +#include "debug.h" +#include "bus.h" +#include "fwil.h" +#include "fwil_types.h" +#include "fweh.h" +#include "fwsignal.h" +#include "p2p.h" +#include "cfg80211.h" +#include "proto.h" + +/** + * DOC: Firmware Signalling + * + * Firmware can send signals to host and vice versa, which are passed in the + * data packets using TLV based header. This signalling layer is on top of the + * BDC bus protocol layer. + */ + +/* + * single definition for firmware-driver flow control tlv's. + * + * each tlv is specified by BRCMF_FWS_TLV_DEF(name, ID, length). + * A length value 0 indicates variable length tlv. + */ +#define BRCMF_FWS_TLV_DEFLIST \ + BRCMF_FWS_TLV_DEF(MAC_OPEN, 1, 1) \ + BRCMF_FWS_TLV_DEF(MAC_CLOSE, 2, 1) \ + BRCMF_FWS_TLV_DEF(MAC_REQUEST_CREDIT, 3, 2) \ + BRCMF_FWS_TLV_DEF(TXSTATUS, 4, 4) \ + BRCMF_FWS_TLV_DEF(PKTTAG, 5, 4) \ + BRCMF_FWS_TLV_DEF(MACDESC_ADD, 6, 8) \ + BRCMF_FWS_TLV_DEF(MACDESC_DEL, 7, 8) \ + BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \ + BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \ + BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \ + BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 6) \ + BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \ + BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \ + BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \ + BRCMF_FWS_TLV_DEF(TRANS_ID, 18, 6) \ + BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \ + BRCMF_FWS_TLV_DEF(FILLER, 255, 0) + +/* + * enum brcmf_fws_tlv_type - definition of tlv identifiers. + */ +#define BRCMF_FWS_TLV_DEF(name, id, len) \ + BRCMF_FWS_TYPE_ ## name = id, +enum brcmf_fws_tlv_type { + BRCMF_FWS_TLV_DEFLIST + BRCMF_FWS_TYPE_INVALID +}; +#undef BRCMF_FWS_TLV_DEF + +/* + * enum brcmf_fws_tlv_len - definition of tlv lengths. + */ +#define BRCMF_FWS_TLV_DEF(name, id, len) \ + BRCMF_FWS_TYPE_ ## name ## _LEN = (len), +enum brcmf_fws_tlv_len { + BRCMF_FWS_TLV_DEFLIST +}; +#undef BRCMF_FWS_TLV_DEF + +#ifdef DEBUG +/* + * brcmf_fws_tlv_names - array of tlv names. + */ +#define BRCMF_FWS_TLV_DEF(name, id, len) \ + { id, #name }, +static struct { + enum brcmf_fws_tlv_type id; + const char *name; +} brcmf_fws_tlv_names[] = { + BRCMF_FWS_TLV_DEFLIST +}; +#undef BRCMF_FWS_TLV_DEF + + +static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(brcmf_fws_tlv_names); i++) + if (brcmf_fws_tlv_names[i].id == id) + return brcmf_fws_tlv_names[i].name; + + return "INVALID"; +} +#else +static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) +{ + return "NODEBUG"; +} +#endif /* DEBUG */ + +/* + * The PKTTAG tlv has additional bytes when firmware-signalling + * mode has REUSESEQ flag set. + */ +#define BRCMF_FWS_TYPE_SEQ_LEN 2 + +/* + * flags used to enable tlv signalling from firmware. + */ +#define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001 +#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002 +#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004 +#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008 +#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010 +#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 +#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040 + +#define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32 +#define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff + +#define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0 +#define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1 +#define BRCMF_FWS_FLOWCONTROL_HIWATER 128 +#define BRCMF_FWS_FLOWCONTROL_LOWATER 64 + +#define BRCMF_FWS_PSQ_PREC_COUNT ((BRCMF_FWS_FIFO_COUNT + 1) * 2) +#define BRCMF_FWS_PSQ_LEN 256 + +#define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01 +#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02 + +#define BRCMF_FWS_RET_OK_NOSCHEDULE 0 +#define BRCMF_FWS_RET_OK_SCHEDULE 1 + +#define BRCMF_FWS_MODE_REUSESEQ_SHIFT 3 /* seq reuse */ +#define BRCMF_FWS_MODE_SET_REUSESEQ(x, val) ((x) = \ + ((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \ + (((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) +#define BRCMF_FWS_MODE_GET_REUSESEQ(x) \ + (((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1) + +/** + * enum brcmf_fws_skb_state - indicates processing state of skb. + * + * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver. + * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue. + * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware. + * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info. + */ +enum brcmf_fws_skb_state { + BRCMF_FWS_SKBSTATE_NEW, + BRCMF_FWS_SKBSTATE_DELAYED, + BRCMF_FWS_SKBSTATE_SUPPRESSED, + BRCMF_FWS_SKBSTATE_TIM +}; + +/** + * struct brcmf_skbuff_cb - control buffer associated with skbuff. + * + * @bus_flags: 2 bytes reserved for bus specific parameters + * @if_flags: holds interface index and packet related flags. + * @htod: host to device packet identifier (used in PKTTAG tlv). + * @htod_seq: this 16-bit is original seq number for every suppress packet. + * @state: transmit state of the packet. + * @mac: descriptor related to destination for this packet. + * + * This information is stored in control buffer struct sk_buff::cb, which + * provides 48 bytes of storage so this structure should not exceed that. + */ +struct brcmf_skbuff_cb { + u16 bus_flags; + u16 if_flags; + u32 htod; + u16 htod_seq; + enum brcmf_fws_skb_state state; + struct brcmf_fws_mac_descriptor *mac; +}; + +/* + * macro casting skbuff control buffer to struct brcmf_skbuff_cb. + */ +#define brcmf_skbcb(skb) ((struct brcmf_skbuff_cb *)((skb)->cb)) + +/* + * sk_buff control if flags + * + * b[11] - packet sent upon firmware request. + * b[10] - packet only contains signalling data. + * b[9] - packet is a tx packet. + * b[8] - packet used requested credit + * b[7] - interface in AP mode. + * b[3:0] - interface index. + */ +#define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800 +#define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT 11 +#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK 0x0400 +#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT 10 +#define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK 0x0200 +#define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT 9 +#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK 0x0100 +#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT 8 +#define BRCMF_SKB_IF_FLAGS_IF_AP_MASK 0x0080 +#define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT 7 +#define BRCMF_SKB_IF_FLAGS_INDEX_MASK 0x000f +#define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT 0 + +#define brcmf_skb_if_flags_set_field(skb, field, value) \ + brcmu_maskset16(&(brcmf_skbcb(skb)->if_flags), \ + BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \ + BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT, (value)) +#define brcmf_skb_if_flags_get_field(skb, field) \ + brcmu_maskget16(brcmf_skbcb(skb)->if_flags, \ + BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \ + BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT) + +/* + * sk_buff control packet identifier + * + * 32-bit packet identifier used in PKTTAG tlv from host to dongle. + * + * - Generated at the host (e.g. dhd) + * - Seen as a generic sequence number by firmware except for the flags field. + * + * Generation : b[31] => generation number for this packet [host->fw] + * OR, current generation number [fw->host] + * Flags : b[30:27] => command, status flags + * FIFO-AC : b[26:24] => AC-FIFO id + * h-slot : b[23:8] => hanger-slot + * freerun : b[7:0] => A free running counter + */ +#define BRCMF_SKB_HTOD_TAG_GENERATION_MASK 0x80000000 +#define BRCMF_SKB_HTOD_TAG_GENERATION_SHIFT 31 +#define BRCMF_SKB_HTOD_TAG_FLAGS_MASK 0x78000000 +#define BRCMF_SKB_HTOD_TAG_FLAGS_SHIFT 27 +#define BRCMF_SKB_HTOD_TAG_FIFO_MASK 0x07000000 +#define BRCMF_SKB_HTOD_TAG_FIFO_SHIFT 24 +#define BRCMF_SKB_HTOD_TAG_HSLOT_MASK 0x00ffff00 +#define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT 8 +#define BRCMF_SKB_HTOD_TAG_FREERUN_MASK 0x000000ff +#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT 0 + +#define brcmf_skb_htod_tag_set_field(skb, field, value) \ + brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \ + BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \ + BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT, (value)) +#define brcmf_skb_htod_tag_get_field(skb, field) \ + brcmu_maskget32(brcmf_skbcb(skb)->htod, \ + BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \ + BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT) + +#define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK 0x2000 +#define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT 13 +#define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK 0x1000 +#define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT 12 +#define BRCMF_SKB_HTOD_SEQ_NR_MASK 0x0fff +#define BRCMF_SKB_HTOD_SEQ_NR_SHIFT 0 + +#define brcmf_skb_htod_seq_set_field(skb, field, value) \ + brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value)) +#define brcmf_skb_htod_seq_get_field(skb, field) \ + brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT) + +#define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000 +#define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31 +#define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000 +#define BRCMF_FWS_TXSTAT_FLAGS_SHIFT 27 +#define BRCMF_FWS_TXSTAT_FIFO_MASK 0x07000000 +#define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24 +#define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00 +#define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8 +#define BRCMF_FWS_TXSTAT_FREERUN_MASK 0x000000FF +#define BRCMF_FWS_TXSTAT_FREERUN_SHIFT 0 + +#define brcmf_txstatus_get_field(txs, field) \ + brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \ + BRCMF_FWS_TXSTAT_ ## field ## _SHIFT) + +/* How long to defer borrowing in jiffies */ +#define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10) + +/** + * enum brcmf_fws_fifo - fifo indices used by dongle firmware. + * + * @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background. + * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic. + * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic. + * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic. + * @BRCMF_FWS_FIFO_AC_VO: fifo for voice traffic. + * @BRCMF_FWS_FIFO_BCMC: fifo for broadcast/multicast (AP only). + * @BRCMF_FWS_FIFO_ATIM: fifo for ATIM (AP only). + * @BRCMF_FWS_FIFO_COUNT: number of fifos. + */ +enum brcmf_fws_fifo { + BRCMF_FWS_FIFO_FIRST, + BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST, + BRCMF_FWS_FIFO_AC_BE, + BRCMF_FWS_FIFO_AC_VI, + BRCMF_FWS_FIFO_AC_VO, + BRCMF_FWS_FIFO_BCMC, + BRCMF_FWS_FIFO_ATIM, + BRCMF_FWS_FIFO_COUNT +}; + +/** + * enum brcmf_fws_txstatus - txstatus flag values. + * + * @BRCMF_FWS_TXSTATUS_DISCARD: + * host is free to discard the packet. + * @BRCMF_FWS_TXSTATUS_CORE_SUPPRESS: + * 802.11 core suppressed the packet. + * @BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS: + * firmware suppress the packet as device is already in PS mode. + * @BRCMF_FWS_TXSTATUS_FW_TOSSED: + * firmware tossed the packet. + * @BRCMF_FWS_TXSTATUS_HOST_TOSSED: + * host tossed the packet. + */ +enum brcmf_fws_txstatus { + BRCMF_FWS_TXSTATUS_DISCARD, + BRCMF_FWS_TXSTATUS_CORE_SUPPRESS, + BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS, + BRCMF_FWS_TXSTATUS_FW_TOSSED, + BRCMF_FWS_TXSTATUS_HOST_TOSSED +}; + +enum brcmf_fws_fcmode { + BRCMF_FWS_FCMODE_NONE, + BRCMF_FWS_FCMODE_IMPLIED_CREDIT, + BRCMF_FWS_FCMODE_EXPLICIT_CREDIT +}; + +enum brcmf_fws_mac_desc_state { + BRCMF_FWS_STATE_OPEN = 1, + BRCMF_FWS_STATE_CLOSE +}; + +/** + * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface + * + * @occupied: slot is in use. + * @mac_handle: handle for mac entry determined by firmware. + * @interface_id: interface index. + * @state: current state. + * @suppressed: mac entry is suppressed. + * @generation: generation bit. + * @ac_bitmap: ac queue bitmap. + * @requested_credit: credits requested by firmware. + * @ea: ethernet address. + * @seq: per-node free-running sequence. + * @psq: power-save queue. + * @transit_count: packet in transit to firmware. + */ +struct brcmf_fws_mac_descriptor { + char name[16]; + u8 occupied; + u8 mac_handle; + u8 interface_id; + u8 state; + bool suppressed; + u8 generation; + u8 ac_bitmap; + u8 requested_credit; + u8 requested_packet; + u8 ea[ETH_ALEN]; + u8 seq[BRCMF_FWS_FIFO_COUNT]; + struct pktq psq; + int transit_count; + int suppr_transit_count; + bool send_tim_signal; + u8 traffic_pending_bmp; + u8 traffic_lastreported_bmp; +}; + +#define BRCMF_FWS_HANGER_MAXITEMS 1024 + +/** + * enum brcmf_fws_hanger_item_state - state of hanger item. + * + * @BRCMF_FWS_HANGER_ITEM_STATE_FREE: item is free for use. + * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE: item is in use. + * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed. + */ +enum brcmf_fws_hanger_item_state { + BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1, + BRCMF_FWS_HANGER_ITEM_STATE_INUSE, + BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED +}; + + +/** + * struct brcmf_fws_hanger_item - single entry for tx pending packet. + * + * @state: entry is either free or occupied. + * @pkt: packet itself. + */ +struct brcmf_fws_hanger_item { + enum brcmf_fws_hanger_item_state state; + struct sk_buff *pkt; +}; + +/** + * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus. + * + * @pushed: packets pushed to await txstatus. + * @popped: packets popped upon handling txstatus. + * @failed_to_push: packets that could not be pushed. + * @failed_to_pop: packets that could not be popped. + * @failed_slotfind: packets for which failed to find an entry. + * @slot_pos: last returned item index for a free entry. + * @items: array of hanger items. + */ +struct brcmf_fws_hanger { + u32 pushed; + u32 popped; + u32 failed_to_push; + u32 failed_to_pop; + u32 failed_slotfind; + u32 slot_pos; + struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS]; +}; + +struct brcmf_fws_macdesc_table { + struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE]; + struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS]; + struct brcmf_fws_mac_descriptor other; +}; + +struct brcmf_fws_stats { + u32 tlv_parse_failed; + u32 tlv_invalid_type; + u32 header_only_pkt; + u32 header_pulls; + u32 pkt2bus; + u32 send_pkts[5]; + u32 requested_sent[5]; + u32 generic_error; + u32 mac_update_failed; + u32 mac_ps_update_failed; + u32 if_update_failed; + u32 packet_request_failed; + u32 credit_request_failed; + u32 rollback_success; + u32 rollback_failed; + u32 delayq_full_error; + u32 supprq_full_error; + u32 txs_indicate; + u32 txs_discard; + u32 txs_supp_core; + u32 txs_supp_ps; + u32 txs_tossed; + u32 txs_host_tossed; + u32 bus_flow_block; + u32 fws_flow_block; +}; + +struct brcmf_fws_info { + struct brcmf_pub *drvr; + spinlock_t spinlock; + ulong flags; + struct brcmf_fws_stats stats; + struct brcmf_fws_hanger hanger; + enum brcmf_fws_fcmode fcmode; + bool fw_signals; + bool bcmc_credit_check; + struct brcmf_fws_macdesc_table desc; + struct workqueue_struct *fws_wq; + struct work_struct fws_dequeue_work; + u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT]; + int fifo_credit[BRCMF_FWS_FIFO_COUNT]; + int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1]; + int deq_node_pos[BRCMF_FWS_FIFO_COUNT]; + u32 fifo_credit_map; + u32 fifo_delay_map; + unsigned long borrow_defer_timestamp; + bool bus_flow_blocked; + bool creditmap_received; + u8 mode; + bool avoid_queueing; +}; + +/* + * brcmf_fws_prio2fifo - mapping from 802.1d priority to firmware fifo index. + */ +static const int brcmf_fws_prio2fifo[] = { + BRCMF_FWS_FIFO_AC_BE, + BRCMF_FWS_FIFO_AC_BK, + BRCMF_FWS_FIFO_AC_BK, + BRCMF_FWS_FIFO_AC_BE, + BRCMF_FWS_FIFO_AC_VI, + BRCMF_FWS_FIFO_AC_VI, + BRCMF_FWS_FIFO_AC_VO, + BRCMF_FWS_FIFO_AC_VO +}; + +static int fcmode; +module_param(fcmode, int, S_IRUSR); +MODULE_PARM_DESC(fcmode, "mode of firmware signalled flow control"); + +#define BRCMF_FWS_TLV_DEF(name, id, len) \ + case BRCMF_FWS_TYPE_ ## name: \ + return len; + +/** + * brcmf_fws_get_tlv_len() - returns defined length for given tlv id. + * + * @fws: firmware-signalling information. + * @id: identifier of the TLV. + * + * Return: the specified length for the given TLV; Otherwise -EINVAL. + */ +static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws, + enum brcmf_fws_tlv_type id) +{ + switch (id) { + BRCMF_FWS_TLV_DEFLIST + default: + fws->stats.tlv_invalid_type++; + break; + } + return -EINVAL; +} +#undef BRCMF_FWS_TLV_DEF + +static void brcmf_fws_lock(struct brcmf_fws_info *fws) + __acquires(&fws->spinlock) +{ + spin_lock_irqsave(&fws->spinlock, fws->flags); +} + +static void brcmf_fws_unlock(struct brcmf_fws_info *fws) + __releases(&fws->spinlock) +{ + spin_unlock_irqrestore(&fws->spinlock, fws->flags); +} + +static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg) +{ + u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); + return ifidx == *(int *)arg; +} + +static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, + int ifidx) +{ + bool (*matchfn)(struct sk_buff *, void *) = NULL; + struct sk_buff *skb; + int prec; + + if (ifidx != -1) + matchfn = brcmf_fws_ifidx_match; + for (prec = 0; prec < q->num_prec; prec++) { + skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); + while (skb) { + brcmu_pkt_buf_free_skb(skb); + skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); + } + } +} + +static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger) +{ + int i; + + memset(hanger, 0, sizeof(*hanger)); + for (i = 0; i < ARRAY_SIZE(hanger->items); i++) + hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; +} + +static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) +{ + u32 i; + + i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS; + + while (i != h->slot_pos) { + if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { + h->slot_pos = i; + goto done; + } + i++; + if (i == BRCMF_FWS_HANGER_MAXITEMS) + i = 0; + } + brcmf_err("all slots occupied\n"); + h->failed_slotfind++; + i = BRCMF_FWS_HANGER_MAXITEMS; +done: + return i; +} + +static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, + struct sk_buff *pkt, u32 slot_id) +{ + if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) + return -ENOENT; + + if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) { + brcmf_err("slot is not free\n"); + h->failed_to_push++; + return -EINVAL; + } + + h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE; + h->items[slot_id].pkt = pkt; + h->pushed++; + return 0; +} + +static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, + u32 slot_id, struct sk_buff **pktout, + bool remove_item) +{ + if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) + return -ENOENT; + + if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { + brcmf_err("entry not in use\n"); + h->failed_to_pop++; + return -EINVAL; + } + + *pktout = h->items[slot_id].pkt; + if (remove_item) { + h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; + h->items[slot_id].pkt = NULL; + h->popped++; + } + return 0; +} + +static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, + u32 slot_id) +{ + if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) + return -ENOENT; + + if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { + brcmf_err("entry not in use\n"); + return -EINVAL; + } + + h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED; + return 0; +} + +static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws, + bool (*fn)(struct sk_buff *, void *), + int ifidx) +{ + struct brcmf_fws_hanger *h = &fws->hanger; + struct sk_buff *skb; + int i; + enum brcmf_fws_hanger_item_state s; + + for (i = 0; i < ARRAY_SIZE(h->items); i++) { + s = h->items[i].state; + if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE || + s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) { + skb = h->items[i].pkt; + if (fn == NULL || fn(skb, &ifidx)) { + /* suppress packets freed from psq */ + if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE) + brcmu_pkt_buf_free_skb(skb); + h->items[i].state = + BRCMF_FWS_HANGER_ITEM_STATE_FREE; + } + } + } +} + +static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws, + struct brcmf_fws_mac_descriptor *desc) +{ + if (desc == &fws->desc.other) + strlcpy(desc->name, "MAC-OTHER", sizeof(desc->name)); + else if (desc->mac_handle) + scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d", + desc->mac_handle, desc->interface_id); + else + scnprintf(desc->name, sizeof(desc->name), "MACIF:%d", + desc->interface_id); +} + +static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc, + u8 *addr, u8 ifidx) +{ + brcmf_dbg(TRACE, + "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx); + desc->occupied = 1; + desc->state = BRCMF_FWS_STATE_OPEN; + desc->requested_credit = 0; + desc->requested_packet = 0; + /* depending on use may need ifp->bssidx instead */ + desc->interface_id = ifidx; + desc->ac_bitmap = 0xff; /* update this when handling APSD */ + if (addr) + memcpy(&desc->ea[0], addr, ETH_ALEN); +} + +static +void brcmf_fws_macdesc_deinit(struct brcmf_fws_mac_descriptor *desc) +{ + brcmf_dbg(TRACE, + "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id); + desc->occupied = 0; + desc->state = BRCMF_FWS_STATE_CLOSE; + desc->requested_credit = 0; + desc->requested_packet = 0; +} + +static struct brcmf_fws_mac_descriptor * +brcmf_fws_macdesc_lookup(struct brcmf_fws_info *fws, u8 *ea) +{ + struct brcmf_fws_mac_descriptor *entry; + int i; + + if (ea == NULL) + return ERR_PTR(-EINVAL); + + entry = &fws->desc.nodes[0]; + for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) { + if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN)) + return entry; + entry++; + } + + return ERR_PTR(-ENOENT); +} + +static struct brcmf_fws_mac_descriptor* +brcmf_fws_macdesc_find(struct brcmf_fws_info *fws, struct brcmf_if *ifp, u8 *da) +{ + struct brcmf_fws_mac_descriptor *entry = &fws->desc.other; + bool multicast; + + multicast = is_multicast_ether_addr(da); + + /* Multicast destination, STA and P2P clients get the interface entry. + * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations + * have their own entry. + */ + if (multicast && ifp->fws_desc) { + entry = ifp->fws_desc; + goto done; + } + + entry = brcmf_fws_macdesc_lookup(fws, da); + if (IS_ERR(entry)) + entry = ifp->fws_desc; + +done: + return entry; +} + +static bool brcmf_fws_macdesc_closed(struct brcmf_fws_info *fws, + struct brcmf_fws_mac_descriptor *entry, + int fifo) +{ + struct brcmf_fws_mac_descriptor *if_entry; + bool closed; + + /* for unique destination entries the related interface + * may be closed. + */ + if (entry->mac_handle) { + if_entry = &fws->desc.iface[entry->interface_id]; + if (if_entry->state == BRCMF_FWS_STATE_CLOSE) + return true; + } + /* an entry is closed when the state is closed and + * the firmware did not request anything. + */ + closed = entry->state == BRCMF_FWS_STATE_CLOSE && + !entry->requested_credit && !entry->requested_packet; + + /* Or firmware does not allow traffic for given fifo */ + return closed || !(entry->ac_bitmap & BIT(fifo)); +} + +static void brcmf_fws_macdesc_cleanup(struct brcmf_fws_info *fws, + struct brcmf_fws_mac_descriptor *entry, + int ifidx) +{ + if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) { + brcmf_fws_psq_flush(fws, &entry->psq, ifidx); + entry->occupied = !!(entry->psq.len); + } +} + +static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws, + bool (*fn)(struct sk_buff *, void *), + int ifidx) +{ + struct brcmf_fws_hanger_item *hi; + struct pktq *txq; + struct sk_buff *skb; + int prec; + u32 hslot; + + txq = brcmf_bus_gettxq(fws->drvr->bus_if); + if (IS_ERR(txq)) { + brcmf_dbg(TRACE, "no txq to clean up\n"); + return; + } + + for (prec = 0; prec < txq->num_prec; prec++) { + skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx); + while (skb) { + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + hi = &fws->hanger.items[hslot]; + WARN_ON(skb != hi->pkt); + hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; + brcmu_pkt_buf_free_skb(skb); + skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx); + } + } +} + +static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) +{ + int i; + struct brcmf_fws_mac_descriptor *table; + bool (*matchfn)(struct sk_buff *, void *) = NULL; + + if (fws == NULL) + return; + + if (ifidx != -1) + matchfn = brcmf_fws_ifidx_match; + + /* cleanup individual nodes */ + table = &fws->desc.nodes[0]; + for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) + brcmf_fws_macdesc_cleanup(fws, &table[i], ifidx); + + brcmf_fws_macdesc_cleanup(fws, &fws->desc.other, ifidx); + brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx); + brcmf_fws_hanger_cleanup(fws, matchfn, ifidx); +} + +static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) +{ + struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; + u8 *wlh; + u16 data_offset = 0; + u8 fillers; + __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); + __le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq); + + brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n", + entry->name, brcmf_skb_if_flags_get_field(skb, INDEX), + (le32_to_cpu(pkttag) >> 8) & 0xffff, + brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq); + if (entry->send_tim_signal) + data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; + if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) + data_offset += BRCMF_FWS_TYPE_SEQ_LEN; + /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ + data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; + fillers = round_up(data_offset, 4) - data_offset; + data_offset += fillers; + + skb_push(skb, data_offset); + wlh = skb->data; + + wlh[0] = BRCMF_FWS_TYPE_PKTTAG; + wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN; + memcpy(&wlh[2], &pkttag, sizeof(pkttag)); + if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) { + wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN; + memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq, + sizeof(pktseq)); + } + wlh += wlh[1] + 2; + + if (entry->send_tim_signal) { + entry->send_tim_signal = 0; + wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP; + wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; + wlh[2] = entry->mac_handle; + wlh[3] = entry->traffic_pending_bmp; + brcmf_dbg(TRACE, "adding TIM info: handle %d bmp 0x%X\n", + entry->mac_handle, entry->traffic_pending_bmp); + wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2; + entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; + } + if (fillers) + memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); + + return (u8)(data_offset >> 2); +} + +static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, + struct brcmf_fws_mac_descriptor *entry, + int fifo, bool send_immediately) +{ + struct sk_buff *skb; + struct brcmf_skbuff_cb *skcb; + s32 err; + u32 len; + u8 data_offset; + int ifidx; + + /* check delayedQ and suppressQ in one call using bitmap */ + if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0) + entry->traffic_pending_bmp &= ~NBITVAL(fifo); + else + entry->traffic_pending_bmp |= NBITVAL(fifo); + + entry->send_tim_signal = false; + if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) + entry->send_tim_signal = true; + if (send_immediately && entry->send_tim_signal && + entry->state == BRCMF_FWS_STATE_CLOSE) { + /* create a dummy packet and sent that. The traffic */ + /* bitmap info will automatically be attached to that packet */ + len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 + + BRCMF_FWS_TYPE_SEQ_LEN + + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 + + 4 + fws->drvr->hdrlen; + skb = brcmu_pkt_buf_get_skb(len); + if (skb == NULL) + return false; + skb_pull(skb, len); + skcb = brcmf_skbcb(skb); + skcb->mac = entry; + skcb->state = BRCMF_FWS_SKBSTATE_TIM; + skcb->htod = 0; + skcb->htod_seq = 0; + data_offset = brcmf_fws_hdrpush(fws, skb); + ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); + brcmf_fws_unlock(fws); + err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb); + brcmf_fws_lock(fws); + if (err) + brcmu_pkt_buf_free_skb(skb); + return true; + } + return false; +} + +static void +brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq, + u8 if_id) +{ + struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id); + + if (WARN_ON(!ifp)) + return; + + if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && + pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER) + brcmf_txflowblock_if(ifp, + BRCMF_NETIF_STOP_REASON_FWS_FC, false); + if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && + pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) { + fws->stats.fws_flow_block++; + brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true); + } + return; +} + +static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi) +{ + brcmf_dbg(CTL, "rssi %d\n", rssi); + return 0; +} + +static +int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) +{ + struct brcmf_fws_mac_descriptor *entry, *existing; + u8 mac_handle; + u8 ifidx; + u8 *addr; + + mac_handle = *data++; + ifidx = *data++; + addr = data; + + entry = &fws->desc.nodes[mac_handle & 0x1F]; + if (type == BRCMF_FWS_TYPE_MACDESC_DEL) { + if (entry->occupied) { + brcmf_dbg(TRACE, "deleting %s mac %pM\n", + entry->name, addr); + brcmf_fws_lock(fws); + brcmf_fws_macdesc_cleanup(fws, entry, -1); + brcmf_fws_macdesc_deinit(entry); + brcmf_fws_unlock(fws); + } else + fws->stats.mac_update_failed++; + return 0; + } + + existing = brcmf_fws_macdesc_lookup(fws, addr); + if (IS_ERR(existing)) { + if (!entry->occupied) { + brcmf_fws_lock(fws); + entry->mac_handle = mac_handle; + brcmf_fws_macdesc_init(entry, addr, ifidx); + brcmf_fws_macdesc_set_name(fws, entry); + brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, + BRCMF_FWS_PSQ_LEN); + brcmf_fws_unlock(fws); + brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr); + } else { + fws->stats.mac_update_failed++; + } + } else { + if (entry != existing) { + brcmf_dbg(TRACE, "copy mac %s\n", existing->name); + brcmf_fws_lock(fws); + memcpy(entry, existing, + offsetof(struct brcmf_fws_mac_descriptor, psq)); + entry->mac_handle = mac_handle; + brcmf_fws_macdesc_deinit(existing); + brcmf_fws_macdesc_set_name(fws, entry); + brcmf_fws_unlock(fws); + brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name, + addr); + } else { + brcmf_dbg(TRACE, "use existing\n"); + WARN_ON(entry->mac_handle != mac_handle); + /* TODO: what should we do here: continue, reinit, .. */ + } + } + return 0; +} + +static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws, + u8 type, u8 *data) +{ + struct brcmf_fws_mac_descriptor *entry; + u8 mac_handle; + int ret; + + mac_handle = data[0]; + entry = &fws->desc.nodes[mac_handle & 0x1F]; + if (!entry->occupied) { + fws->stats.mac_ps_update_failed++; + return -ESRCH; + } + brcmf_fws_lock(fws); + /* a state update should wipe old credits */ + entry->requested_credit = 0; + entry->requested_packet = 0; + if (type == BRCMF_FWS_TYPE_MAC_OPEN) { + entry->state = BRCMF_FWS_STATE_OPEN; + ret = BRCMF_FWS_RET_OK_SCHEDULE; + } else { + entry->state = BRCMF_FWS_STATE_CLOSE; + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false); + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false); + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false); + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true); + ret = BRCMF_FWS_RET_OK_NOSCHEDULE; + } + brcmf_fws_unlock(fws); + return ret; +} + +static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws, + u8 type, u8 *data) +{ + struct brcmf_fws_mac_descriptor *entry; + u8 ifidx; + int ret; + + ifidx = data[0]; + + if (ifidx >= BRCMF_MAX_IFS) { + ret = -ERANGE; + goto fail; + } + + entry = &fws->desc.iface[ifidx]; + if (!entry->occupied) { + ret = -ESRCH; + goto fail; + } + + brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type, + entry->name); + brcmf_fws_lock(fws); + switch (type) { + case BRCMF_FWS_TYPE_INTERFACE_OPEN: + entry->state = BRCMF_FWS_STATE_OPEN; + ret = BRCMF_FWS_RET_OK_SCHEDULE; + break; + case BRCMF_FWS_TYPE_INTERFACE_CLOSE: + entry->state = BRCMF_FWS_STATE_CLOSE; + ret = BRCMF_FWS_RET_OK_NOSCHEDULE; + break; + default: + ret = -EINVAL; + brcmf_fws_unlock(fws); + goto fail; + } + brcmf_fws_unlock(fws); + return ret; + +fail: + fws->stats.if_update_failed++; + return ret; +} + +static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, + u8 *data) +{ + struct brcmf_fws_mac_descriptor *entry; + + entry = &fws->desc.nodes[data[1] & 0x1F]; + if (!entry->occupied) { + if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT) + fws->stats.credit_request_failed++; + else + fws->stats.packet_request_failed++; + return -ESRCH; + } + + brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n", + brcmf_fws_get_tlv_name(type), type, entry->name, + data[0], data[2]); + brcmf_fws_lock(fws); + if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT) + entry->requested_credit = data[0]; + else + entry->requested_packet = data[0]; + + entry->ac_bitmap = data[2]; + brcmf_fws_unlock(fws); + return BRCMF_FWS_RET_OK_SCHEDULE; +} + +static void +brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry, + struct sk_buff *skb) +{ + if (entry->requested_credit > 0) { + entry->requested_credit--; + brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); + brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1); + if (entry->state != BRCMF_FWS_STATE_CLOSE) + brcmf_err("requested credit set while mac not closed!\n"); + } else if (entry->requested_packet > 0) { + entry->requested_packet--; + brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); + brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0); + if (entry->state != BRCMF_FWS_STATE_CLOSE) + brcmf_err("requested packet set while mac not closed!\n"); + } else { + brcmf_skb_if_flags_set_field(skb, REQUESTED, 0); + brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0); + } +} + +static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb) +{ + struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; + + if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) && + (entry->state == BRCMF_FWS_STATE_CLOSE)) + entry->requested_credit++; +} + +static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, + u8 fifo, u8 credits) +{ + int lender_ac; + int *borrowed; + int *fifo_credit; + + if (!credits) + return; + + fws->fifo_credit_map |= 1 << fifo; + + if ((fifo == BRCMF_FWS_FIFO_AC_BE) && + (fws->credits_borrowed[0])) { + for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0; + lender_ac--) { + borrowed = &fws->credits_borrowed[lender_ac]; + if (*borrowed) { + fws->fifo_credit_map |= (1 << lender_ac); + fifo_credit = &fws->fifo_credit[lender_ac]; + if (*borrowed >= credits) { + *borrowed -= credits; + *fifo_credit += credits; + return; + } else { + credits -= *borrowed; + *fifo_credit += *borrowed; + *borrowed = 0; + } + } + } + } + + fws->fifo_credit[fifo] += credits; +} + +static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws) +{ + /* only schedule dequeue when there are credits for delayed traffic */ + if ((fws->fifo_credit_map & fws->fifo_delay_map) || + (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map)) + queue_work(fws->fws_wq, &fws->fws_dequeue_work); +} + +static int brcmf_fws_enq(struct brcmf_fws_info *fws, + enum brcmf_fws_skb_state state, int fifo, + struct sk_buff *p) +{ + int prec = 2 * fifo; + u32 *qfull_stat = &fws->stats.delayq_full_error; + struct brcmf_fws_mac_descriptor *entry; + struct pktq *pq; + struct sk_buff_head *queue; + struct sk_buff *p_head; + struct sk_buff *p_tail; + u32 fr_new; + u32 fr_compare; + + entry = brcmf_skbcb(p)->mac; + if (entry == NULL) { + brcmf_err("no mac descriptor found for skb %p\n", p); + return -ENOENT; + } + + brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p); + if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { + prec += 1; + qfull_stat = &fws->stats.supprq_full_error; + + /* Fix out of order delivery of frames. Dont assume frame */ + /* can be inserted at the end, but look for correct position */ + pq = &entry->psq; + if (pktq_full(pq) || pktq_pfull(pq, prec)) { + *qfull_stat += 1; + return -ENFILE; + } + queue = &pq->q[prec].skblist; + + p_head = skb_peek(queue); + p_tail = skb_peek_tail(queue); + fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN); + + while (p_head != p_tail) { + fr_compare = brcmf_skb_htod_tag_get_field(p_tail, + FREERUN); + /* be sure to handle wrap of 256 */ + if (((fr_new > fr_compare) && + ((fr_new - fr_compare) < 128)) || + ((fr_new < fr_compare) && + ((fr_compare - fr_new) > 128))) + break; + p_tail = skb_queue_prev(queue, p_tail); + } + /* Position found. Determine what to do */ + if (p_tail == NULL) { + /* empty list */ + __skb_queue_tail(queue, p); + } else { + fr_compare = brcmf_skb_htod_tag_get_field(p_tail, + FREERUN); + if (((fr_new > fr_compare) && + ((fr_new - fr_compare) < 128)) || + ((fr_new < fr_compare) && + ((fr_compare - fr_new) > 128))) { + /* After tail */ + __skb_queue_after(queue, p_tail, p); + } else { + /* Before tail */ + __skb_insert(p, p_tail->prev, p_tail, queue); + } + } + + /* Complete the counters and statistics */ + pq->len++; + if (pq->hi_prec < prec) + pq->hi_prec = (u8) prec; + } else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) { + *qfull_stat += 1; + return -ENFILE; + } + + /* increment total enqueued packet count */ + fws->fifo_delay_map |= 1 << fifo; + fws->fifo_enqpkt[fifo]++; + + /* update the sk_buff state */ + brcmf_skbcb(p)->state = state; + + /* + * A packet has been pushed so update traffic + * availability bitmap, if applicable + */ + brcmf_fws_tim_update(fws, entry, fifo, true); + brcmf_fws_flow_control_check(fws, &entry->psq, + brcmf_skb_if_flags_get_field(p, INDEX)); + return 0; +} + +static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) +{ + struct brcmf_fws_mac_descriptor *table; + struct brcmf_fws_mac_descriptor *entry; + struct sk_buff *p; + int num_nodes; + int node_pos; + int prec_out; + int pmsk; + int i; + + table = (struct brcmf_fws_mac_descriptor *)&fws->desc; + num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor); + node_pos = fws->deq_node_pos[fifo]; + + for (i = 0; i < num_nodes; i++) { + entry = &table[(node_pos + i) % num_nodes]; + if (!entry->occupied || + brcmf_fws_macdesc_closed(fws, entry, fifo)) + continue; + + if (entry->suppressed) + pmsk = 2; + else + pmsk = 3; + p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out); + if (p == NULL) { + if (entry->suppressed) { + if (entry->suppr_transit_count) + continue; + entry->suppressed = false; + p = brcmu_pktq_mdeq(&entry->psq, + 1 << (fifo * 2), &prec_out); + } + } + if (p == NULL) + continue; + + brcmf_fws_macdesc_use_req_credit(entry, p); + + /* move dequeue position to ensure fair round-robin */ + fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes; + brcmf_fws_flow_control_check(fws, &entry->psq, + brcmf_skb_if_flags_get_field(p, + INDEX) + ); + /* + * A packet has been picked up, update traffic + * availability bitmap, if applicable + */ + brcmf_fws_tim_update(fws, entry, fifo, false); + + /* + * decrement total enqueued fifo packets and + * clear delay bitmap if done. + */ + fws->fifo_enqpkt[fifo]--; + if (fws->fifo_enqpkt[fifo] == 0) + fws->fifo_delay_map &= ~(1 << fifo); + goto done; + } + p = NULL; +done: + brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p); + return p; +} + +static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, + struct sk_buff *skb, + u32 genbit, u16 seq) +{ + struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; + u32 hslot; + int ret; + + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + + /* this packet was suppressed */ + if (!entry->suppressed) { + entry->suppressed = true; + entry->suppr_transit_count = entry->transit_count; + brcmf_dbg(DATA, "suppress %s: transit %d\n", + entry->name, entry->transit_count); + } + + entry->generation = genbit; + + brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit); + brcmf_skbcb(skb)->htod_seq = seq; + if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) { + brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1); + brcmf_skb_htod_seq_set_field(skb, FROMFW, 0); + } else { + brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0); + } + ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb); + + if (ret != 0) { + /* suppress q is full drop this packet */ + brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true); + } else { + /* Mark suppressed to avoid a double free during wlfc cleanup */ + brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot); + } + + return ret; +} + +static int +brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, + u32 genbit, u16 seq) +{ + u32 fifo; + int ret; + bool remove_from_hanger = true; + struct sk_buff *skb; + struct brcmf_skbuff_cb *skcb; + struct brcmf_fws_mac_descriptor *entry = NULL; + struct brcmf_if *ifp; + + brcmf_dbg(DATA, "flags %d\n", flags); + + if (flags == BRCMF_FWS_TXSTATUS_DISCARD) + fws->stats.txs_discard++; + else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) { + fws->stats.txs_supp_core++; + remove_from_hanger = false; + } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) { + fws->stats.txs_supp_ps++; + remove_from_hanger = false; + } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED) + fws->stats.txs_tossed++; + else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED) + fws->stats.txs_host_tossed++; + else + brcmf_err("unexpected txstatus\n"); + + ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, + remove_from_hanger); + if (ret != 0) { + brcmf_err("no packet in hanger slot: hslot=%d\n", hslot); + return ret; + } + + skcb = brcmf_skbcb(skb); + entry = skcb->mac; + if (WARN_ON(!entry)) { + brcmu_pkt_buf_free_skb(skb); + return -EINVAL; + } + entry->transit_count--; + if (entry->suppressed && entry->suppr_transit_count) + entry->suppr_transit_count--; + + brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name, flags, + skcb->htod, seq); + + /* pick up the implicit credit from this packet */ + fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); + if ((fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT) || + (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) || + (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) { + brcmf_fws_return_credits(fws, fifo, 1); + brcmf_fws_schedule_deq(fws); + } + brcmf_fws_macdesc_return_req_credit(skb); + + ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp); + if (ret) { + brcmu_pkt_buf_free_skb(skb); + return -EINVAL; + } + if (!remove_from_hanger) + ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, + genbit, seq); + if (remove_from_hanger || ret) + brcmf_txfinalize(ifp, skb, true); + + return 0; +} + +static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws, + u8 *data) +{ + int i; + + if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) { + brcmf_dbg(INFO, "ignored\n"); + return BRCMF_FWS_RET_OK_NOSCHEDULE; + } + + brcmf_dbg(DATA, "enter: data %pM\n", data); + brcmf_fws_lock(fws); + for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++) + brcmf_fws_return_credits(fws, i, data[i]); + + brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map, + fws->fifo_delay_map); + brcmf_fws_unlock(fws); + return BRCMF_FWS_RET_OK_SCHEDULE; +} + +static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) +{ + __le32 status_le; + __le16 seq_le; + u32 status; + u32 hslot; + u32 genbit; + u8 flags; + u16 seq; + + fws->stats.txs_indicate++; + memcpy(&status_le, data, sizeof(status_le)); + status = le32_to_cpu(status_le); + flags = brcmf_txstatus_get_field(status, FLAGS); + hslot = brcmf_txstatus_get_field(status, HSLOT); + genbit = brcmf_txstatus_get_field(status, GENERATION); + if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) { + memcpy(&seq_le, &data[BRCMF_FWS_TYPE_PKTTAG_LEN], + sizeof(seq_le)); + seq = le16_to_cpu(seq_le); + } else { + seq = 0; + } + + brcmf_fws_lock(fws); + brcmf_fws_txs_process(fws, flags, hslot, genbit, seq); + brcmf_fws_unlock(fws); + return BRCMF_FWS_RET_OK_NOSCHEDULE; +} + +static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) +{ + __le32 timestamp; + + memcpy(×tamp, &data[2], sizeof(timestamp)); + brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1], + le32_to_cpu(timestamp)); + return 0; +} + +static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data) +{ + struct brcmf_fws_info *fws = ifp->drvr->fws; + int i; + u8 *credits = data; + + if (e->datalen < BRCMF_FWS_FIFO_COUNT) { + brcmf_err("event payload too small (%d)\n", e->datalen); + return -EINVAL; + } + if (fws->creditmap_received) + return 0; + + fws->creditmap_received = true; + + brcmf_dbg(TRACE, "enter: credits %pM\n", credits); + brcmf_fws_lock(fws); + for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) { + if (*credits) + fws->fifo_credit_map |= 1 << i; + else + fws->fifo_credit_map &= ~(1 << i); + fws->fifo_credit[i] = *credits++; + } + brcmf_fws_schedule_deq(fws); + brcmf_fws_unlock(fws); + return 0; +} + +static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data) +{ + struct brcmf_fws_info *fws = ifp->drvr->fws; + + brcmf_fws_lock(fws); + if (fws) + fws->bcmc_credit_check = true; + brcmf_fws_unlock(fws); + return 0; +} + +void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb) +{ + struct brcmf_skb_reorder_data *rd; + struct brcmf_fws_info *fws = ifp->drvr->fws; + u8 *signal_data; + s16 data_len; + u8 type; + u8 len; + u8 *data; + s32 status; + s32 err; + + brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n", + ifp->ifidx, skb->len, siglen); + + WARN_ON(siglen > skb->len); + + if (!siglen) + return; + /* if flow control disabled, skip to packet data and leave */ + if ((!fws) || (!fws->fw_signals)) { + skb_pull(skb, siglen); + return; + } + + fws->stats.header_pulls++; + data_len = siglen; + signal_data = skb->data; + + status = BRCMF_FWS_RET_OK_NOSCHEDULE; + while (data_len > 0) { + /* extract tlv info */ + type = signal_data[0]; + + /* FILLER type is actually not a TLV, but + * a single byte that can be skipped. + */ + if (type == BRCMF_FWS_TYPE_FILLER) { + signal_data += 1; + data_len -= 1; + continue; + } + len = signal_data[1]; + data = signal_data + 2; + + brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n", + brcmf_fws_get_tlv_name(type), type, len, + brcmf_fws_get_tlv_len(fws, type)); + + /* abort parsing when length invalid */ + if (data_len < len + 2) + break; + + if (len < brcmf_fws_get_tlv_len(fws, type)) + break; + + err = BRCMF_FWS_RET_OK_NOSCHEDULE; + switch (type) { + case BRCMF_FWS_TYPE_COMP_TXSTATUS: + break; + case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS: + rd = (struct brcmf_skb_reorder_data *)skb->cb; + rd->reorder = data; + break; + case BRCMF_FWS_TYPE_MACDESC_ADD: + case BRCMF_FWS_TYPE_MACDESC_DEL: + brcmf_fws_macdesc_indicate(fws, type, data); + break; + case BRCMF_FWS_TYPE_MAC_OPEN: + case BRCMF_FWS_TYPE_MAC_CLOSE: + err = brcmf_fws_macdesc_state_indicate(fws, type, data); + break; + case BRCMF_FWS_TYPE_INTERFACE_OPEN: + case BRCMF_FWS_TYPE_INTERFACE_CLOSE: + err = brcmf_fws_interface_state_indicate(fws, type, + data); + break; + case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT: + case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET: + err = brcmf_fws_request_indicate(fws, type, data); + break; + case BRCMF_FWS_TYPE_TXSTATUS: + brcmf_fws_txstatus_indicate(fws, data); + break; + case BRCMF_FWS_TYPE_FIFO_CREDITBACK: + err = brcmf_fws_fifocreditback_indicate(fws, data); + break; + case BRCMF_FWS_TYPE_RSSI: + brcmf_fws_rssi_indicate(fws, *data); + break; + case BRCMF_FWS_TYPE_TRANS_ID: + brcmf_fws_dbg_seqnum_check(fws, data); + break; + case BRCMF_FWS_TYPE_PKTTAG: + case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP: + default: + fws->stats.tlv_invalid_type++; + break; + } + if (err == BRCMF_FWS_RET_OK_SCHEDULE) + status = BRCMF_FWS_RET_OK_SCHEDULE; + signal_data += len + 2; + data_len -= len + 2; + } + + if (data_len != 0) + fws->stats.tlv_parse_failed++; + + if (status == BRCMF_FWS_RET_OK_SCHEDULE) + brcmf_fws_schedule_deq(fws); + + /* signalling processing result does + * not affect the actual ethernet packet. + */ + skb_pull(skb, siglen); + + /* this may be a signal-only packet + */ + if (skb->len == 0) + fws->stats.header_only_pkt++; +} + +static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, + struct sk_buff *p) +{ + struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); + struct brcmf_fws_mac_descriptor *entry = skcb->mac; + u8 flags; + + if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED) + brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation); + flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST; + if (brcmf_skb_if_flags_get_field(p, REQUESTED)) { + /* + * Indicate that this packet is being sent in response to an + * explicit request from the firmware side. + */ + flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED; + } + brcmf_skb_htod_tag_set_field(p, FLAGS, flags); + return brcmf_fws_hdrpush(fws, p); +} + +static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, + struct sk_buff *skb, int fifo) +{ + struct brcmf_fws_mac_descriptor *entry; + struct sk_buff *pktout; + int qidx, hslot; + int rc = 0; + + entry = brcmf_skbcb(skb)->mac; + if (entry->occupied) { + qidx = 2 * fifo; + if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_SUPPRESSED) + qidx++; + + pktout = brcmu_pktq_penq_head(&entry->psq, qidx, skb); + if (pktout == NULL) { + brcmf_err("%s queue %d full\n", entry->name, qidx); + rc = -ENOSPC; + } + } else { + brcmf_err("%s entry removed\n", entry->name); + rc = -ENOENT; + } + + if (rc) { + fws->stats.rollback_failed++; + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, + hslot, 0, 0); + } else { + fws->stats.rollback_success++; + brcmf_fws_return_credits(fws, fifo, 1); + brcmf_fws_macdesc_return_req_credit(skb); + } +} + +static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws) +{ + int lender_ac; + + if (time_after(fws->borrow_defer_timestamp, jiffies)) { + fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE); + return -ENAVAIL; + } + + for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) { + if (fws->fifo_credit[lender_ac]) { + fws->credits_borrowed[lender_ac]++; + fws->fifo_credit[lender_ac]--; + if (fws->fifo_credit[lender_ac] == 0) + fws->fifo_credit_map &= ~(1 << lender_ac); + fws->fifo_credit_map |= (1 << BRCMF_FWS_FIFO_AC_BE); + brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac); + return 0; + } + } + fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE); + return -ENAVAIL; +} + +static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, + struct sk_buff *skb) +{ + struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); + struct brcmf_fws_mac_descriptor *entry; + int rc; + u8 ifidx; + u8 data_offset; + + entry = skcb->mac; + if (IS_ERR(entry)) + return PTR_ERR(entry); + + data_offset = brcmf_fws_precommit_skb(fws, fifo, skb); + entry->transit_count++; + if (entry->suppressed) + entry->suppr_transit_count++; + ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); + brcmf_fws_unlock(fws); + rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb); + brcmf_fws_lock(fws); + brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name, + skcb->if_flags, skcb->htod, rc); + if (rc < 0) { + entry->transit_count--; + if (entry->suppressed) + entry->suppr_transit_count--; + (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL); + goto rollback; + } + + fws->stats.pkt2bus++; + fws->stats.send_pkts[fifo]++; + if (brcmf_skb_if_flags_get_field(skb, REQUESTED)) + fws->stats.requested_sent[fifo]++; + + return rc; + +rollback: + brcmf_fws_rollback_toq(fws, skb, fifo); + return rc; +} + +static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p, + int fifo) +{ + struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); + int rc, hslot; + + skcb->htod = 0; + skcb->htod_seq = 0; + hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger); + brcmf_skb_htod_tag_set_field(p, HSLOT, hslot); + brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]); + brcmf_skb_htod_tag_set_field(p, FIFO, fifo); + rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot); + if (!rc) + skcb->mac->seq[fifo]++; + else + fws->stats.generic_error++; + return rc; +} + +int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_fws_info *fws = drvr->fws; + struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); + struct ethhdr *eh = (struct ethhdr *)(skb->data); + int fifo = BRCMF_FWS_FIFO_BCMC; + bool multicast = is_multicast_ether_addr(eh->h_dest); + int rc = 0; + + brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto)); + /* determine the priority */ + if (!skb->priority) + skb->priority = cfg80211_classify8021d(skb, NULL); + + drvr->tx_multicast += !!multicast; + + if (fws->avoid_queueing) { + rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb); + if (rc < 0) + brcmf_txfinalize(ifp, skb, false); + return rc; + } + + /* set control buffer information */ + skcb->if_flags = 0; + skcb->state = BRCMF_FWS_SKBSTATE_NEW; + brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx); + if (!multicast) + fifo = brcmf_fws_prio2fifo[skb->priority]; + + brcmf_fws_lock(fws); + if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC) + fws->borrow_defer_timestamp = jiffies + + BRCMF_FWS_BORROW_DEFER_PERIOD; + + skcb->mac = brcmf_fws_macdesc_find(fws, ifp, eh->h_dest); + brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name, + eh->h_dest, multicast, fifo); + if (!brcmf_fws_assign_htod(fws, skb, fifo)) { + brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb); + brcmf_fws_schedule_deq(fws); + } else { + brcmf_err("drop skb: no hanger slot\n"); + brcmf_txfinalize(ifp, skb, false); + rc = -ENOMEM; + } + brcmf_fws_unlock(fws); + + return rc; +} + +void brcmf_fws_reset_interface(struct brcmf_if *ifp) +{ + struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; + + brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); + if (!entry) + return; + + brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx); +} + +void brcmf_fws_add_interface(struct brcmf_if *ifp) +{ + struct brcmf_fws_info *fws = ifp->drvr->fws; + struct brcmf_fws_mac_descriptor *entry; + + if (!ifp->ndev) + return; + + entry = &fws->desc.iface[ifp->ifidx]; + ifp->fws_desc = entry; + brcmf_fws_macdesc_init(entry, ifp->mac_addr, ifp->ifidx); + brcmf_fws_macdesc_set_name(fws, entry); + brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, + BRCMF_FWS_PSQ_LEN); + brcmf_dbg(TRACE, "added %s\n", entry->name); +} + +void brcmf_fws_del_interface(struct brcmf_if *ifp) +{ + struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; + + if (!entry) + return; + + brcmf_fws_lock(ifp->drvr->fws); + ifp->fws_desc = NULL; + brcmf_dbg(TRACE, "deleting %s\n", entry->name); + brcmf_fws_macdesc_deinit(entry); + brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx); + brcmf_fws_unlock(ifp->drvr->fws); +} + +static void brcmf_fws_dequeue_worker(struct work_struct *worker) +{ + struct brcmf_fws_info *fws; + struct brcmf_pub *drvr; + struct sk_buff *skb; + int fifo; + u32 hslot; + u32 ifidx; + int ret; + + fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); + drvr = fws->drvr; + + brcmf_fws_lock(fws); + for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked; + fifo--) { + if (!brcmf_fws_fc_active(fws)) { + while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) { + hslot = brcmf_skb_htod_tag_get_field(skb, + HSLOT); + brcmf_fws_hanger_poppkt(&fws->hanger, hslot, + &skb, true); + ifidx = brcmf_skb_if_flags_get_field(skb, + INDEX); + /* Use proto layer to send data frame */ + brcmf_fws_unlock(fws); + ret = brcmf_proto_txdata(drvr, ifidx, 0, skb); + brcmf_fws_lock(fws); + if (ret < 0) + brcmf_txfinalize(brcmf_get_ifp(drvr, + ifidx), + skb, false); + if (fws->bus_flow_blocked) + break; + } + continue; + } + while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) && + (fifo == BRCMF_FWS_FIFO_BCMC))) { + skb = brcmf_fws_deq(fws, fifo); + if (!skb) + break; + fws->fifo_credit[fifo]--; + if (brcmf_fws_commit_skb(fws, fifo, skb)) + break; + if (fws->bus_flow_blocked) + break; + } + if ((fifo == BRCMF_FWS_FIFO_AC_BE) && + (fws->fifo_credit[fifo] == 0) && + (!fws->bus_flow_blocked)) { + while (brcmf_fws_borrow_credit(fws) == 0) { + skb = brcmf_fws_deq(fws, fifo); + if (!skb) { + brcmf_fws_return_credits(fws, fifo, 1); + break; + } + if (brcmf_fws_commit_skb(fws, fifo, skb)) + break; + if (fws->bus_flow_blocked) + break; + } + } + } + brcmf_fws_unlock(fws); +} + +#ifdef DEBUG +static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_fws_stats *fwstats = &bus_if->drvr->fws->stats; + + seq_printf(seq, + "header_pulls: %u\n" + "header_only_pkt: %u\n" + "tlv_parse_failed: %u\n" + "tlv_invalid_type: %u\n" + "mac_update_fails: %u\n" + "ps_update_fails: %u\n" + "if_update_fails: %u\n" + "pkt2bus: %u\n" + "generic_error: %u\n" + "rollback_success: %u\n" + "rollback_failed: %u\n" + "delayq_full: %u\n" + "supprq_full: %u\n" + "txs_indicate: %u\n" + "txs_discard: %u\n" + "txs_suppr_core: %u\n" + "txs_suppr_ps: %u\n" + "txs_tossed: %u\n" + "txs_host_tossed: %u\n" + "bus_flow_block: %u\n" + "fws_flow_block: %u\n" + "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" + "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", + fwstats->header_pulls, + fwstats->header_only_pkt, + fwstats->tlv_parse_failed, + fwstats->tlv_invalid_type, + fwstats->mac_update_failed, + fwstats->mac_ps_update_failed, + fwstats->if_update_failed, + fwstats->pkt2bus, + fwstats->generic_error, + fwstats->rollback_success, + fwstats->rollback_failed, + fwstats->delayq_full_error, + fwstats->supprq_full_error, + fwstats->txs_indicate, + fwstats->txs_discard, + fwstats->txs_supp_core, + fwstats->txs_supp_ps, + fwstats->txs_tossed, + fwstats->txs_host_tossed, + fwstats->bus_flow_block, + fwstats->fws_flow_block, + fwstats->send_pkts[0], fwstats->send_pkts[1], + fwstats->send_pkts[2], fwstats->send_pkts[3], + fwstats->send_pkts[4], + fwstats->requested_sent[0], + fwstats->requested_sent[1], + fwstats->requested_sent[2], + fwstats->requested_sent[3], + fwstats->requested_sent[4]); + + return 0; +} +#else +static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) +{ + return 0; +} +#endif + +int brcmf_fws_init(struct brcmf_pub *drvr) +{ + struct brcmf_fws_info *fws; + struct brcmf_if *ifp; + u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS; + int rc; + u32 mode; + + drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL); + if (!drvr->fws) { + rc = -ENOMEM; + goto fail; + } + + fws = drvr->fws; + + spin_lock_init(&fws->spinlock); + + /* set linkage back */ + fws->drvr = drvr; + fws->fcmode = fcmode; + + if ((drvr->bus_if->always_use_fws_queue == false) && + (fcmode == BRCMF_FWS_FCMODE_NONE)) { + fws->avoid_queueing = true; + brcmf_dbg(INFO, "FWS queueing will be avoided\n"); + return 0; + } + + fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq"); + if (fws->fws_wq == NULL) { + brcmf_err("workqueue creation failed\n"); + rc = -EBADF; + goto fail; + } + INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker); + + /* enable firmware signalling if fcmode active */ + if (fws->fcmode != BRCMF_FWS_FCMODE_NONE) + tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS | + BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS | + BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE | + BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE; + + rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP, + brcmf_fws_notify_credit_map); + if (rc < 0) { + brcmf_err("register credit map handler failed\n"); + goto fail; + } + rc = brcmf_fweh_register(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT, + brcmf_fws_notify_bcmc_credit_support); + if (rc < 0) { + brcmf_err("register bcmc credit handler failed\n"); + brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP); + goto fail; + } + + /* Setting the iovar may fail if feature is unsupported + * so leave the rc as is so driver initialization can + * continue. Set mode back to none indicating not enabled. + */ + fws->fw_signals = true; + ifp = brcmf_get_ifp(drvr, 0); + if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) { + brcmf_err("failed to set bdcv2 tlv signaling\n"); + fws->fcmode = BRCMF_FWS_FCMODE_NONE; + fws->fw_signals = false; + } + + if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1)) + brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n"); + + /* Enable seq number reuse, if supported */ + if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) { + if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) { + mode = 0; + BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1); + if (brcmf_fil_iovar_int_set(ifp, + "wlfc_mode", mode) == 0) { + BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1); + } + } + } + + brcmf_fws_hanger_init(&fws->hanger); + brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0); + brcmf_fws_macdesc_set_name(fws, &fws->desc.other); + brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT, + BRCMF_FWS_PSQ_LEN); + + /* create debugfs file for statistics */ + brcmf_debugfs_add_entry(drvr, "fws_stats", + brcmf_debugfs_fws_stats_read); + + brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n", + fws->fw_signals ? "enabled" : "disabled", tlv); + return 0; + +fail: + brcmf_fws_deinit(drvr); + return rc; +} + +void brcmf_fws_deinit(struct brcmf_pub *drvr) +{ + struct brcmf_fws_info *fws = drvr->fws; + + if (!fws) + return; + + if (drvr->fws->fws_wq) + destroy_workqueue(drvr->fws->fws_wq); + + /* cleanup */ + brcmf_fws_lock(fws); + brcmf_fws_cleanup(fws, -1); + drvr->fws = NULL; + brcmf_fws_unlock(fws); + + /* free top structure */ + kfree(fws); +} + +bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) +{ + if (!fws->creditmap_received) + return false; + + return fws->fcmode != BRCMF_FWS_FCMODE_NONE; +} + +void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) +{ + u32 hslot; + + if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) { + brcmu_pkt_buf_free_skb(skb); + return; + } + brcmf_fws_lock(fws); + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0); + brcmf_fws_unlock(fws); +} + +void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked) +{ + struct brcmf_fws_info *fws = drvr->fws; + + fws->bus_flow_blocked = flow_blocked; + if (!flow_blocked) + brcmf_fws_schedule_deq(fws); + else + fws->stats.bus_flow_block++; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h new file mode 100644 index 000000000000..a36bac17eafd --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2012 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 FWSIGNAL_H_ +#define FWSIGNAL_H_ + +int brcmf_fws_init(struct brcmf_pub *drvr); +void brcmf_fws_deinit(struct brcmf_pub *drvr); +bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); +void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb); +int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb); + +void brcmf_fws_reset_interface(struct brcmf_if *ifp); +void brcmf_fws_add_interface(struct brcmf_if *ifp); +void brcmf_fws_del_interface(struct brcmf_if *ifp); +void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb); +void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked); + +#endif /* FWSIGNAL_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c new file mode 100644 index 000000000000..44e618f9d890 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -0,0 +1,1561 @@ +/* Copyright (c) 2014 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. + */ + +/******************************************************************************* + * Communicates with the dongle by using dcmd codes. + * For certain dcmd codes, the dongle interprets string data from the host. + ******************************************************************************/ + +#include +#include + +#include +#include + +#include "core.h" +#include "debug.h" +#include "proto.h" +#include "msgbuf.h" +#include "commonring.h" +#include "flowring.h" +#include "bus.h" +#include "tracepoint.h" + + +#define MSGBUF_IOCTL_RESP_TIMEOUT 2000 + +#define MSGBUF_TYPE_GEN_STATUS 0x1 +#define MSGBUF_TYPE_RING_STATUS 0x2 +#define MSGBUF_TYPE_FLOW_RING_CREATE 0x3 +#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT 0x4 +#define MSGBUF_TYPE_FLOW_RING_DELETE 0x5 +#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT 0x6 +#define MSGBUF_TYPE_FLOW_RING_FLUSH 0x7 +#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT 0x8 +#define MSGBUF_TYPE_IOCTLPTR_REQ 0x9 +#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK 0xA +#define MSGBUF_TYPE_IOCTLRESP_BUF_POST 0xB +#define MSGBUF_TYPE_IOCTL_CMPLT 0xC +#define MSGBUF_TYPE_EVENT_BUF_POST 0xD +#define MSGBUF_TYPE_WL_EVENT 0xE +#define MSGBUF_TYPE_TX_POST 0xF +#define MSGBUF_TYPE_TX_STATUS 0x10 +#define MSGBUF_TYPE_RXBUF_POST 0x11 +#define MSGBUF_TYPE_RX_CMPLT 0x12 +#define MSGBUF_TYPE_LPBK_DMAXFER 0x13 +#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14 + +#define NR_TX_PKTIDS 2048 +#define NR_RX_PKTIDS 1024 + +#define BRCMF_IOCTL_REQ_PKTID 0xFFFE + +#define BRCMF_MSGBUF_MAX_PKT_SIZE 2048 +#define BRCMF_MSGBUF_RXBUFPOST_THRESHOLD 32 +#define BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST 8 +#define BRCMF_MSGBUF_MAX_EVENTBUF_POST 8 + +#define BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3 0x01 +#define BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT 5 + +#define BRCMF_MSGBUF_TX_FLUSH_CNT1 32 +#define BRCMF_MSGBUF_TX_FLUSH_CNT2 96 + +#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 96 +#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32 +#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48 + + +struct msgbuf_common_hdr { + u8 msgtype; + u8 ifidx; + u8 flags; + u8 rsvd0; + __le32 request_id; +}; + +struct msgbuf_buf_addr { + __le32 low_addr; + __le32 high_addr; +}; + +struct msgbuf_ioctl_req_hdr { + struct msgbuf_common_hdr msg; + __le32 cmd; + __le16 trans_id; + __le16 input_buf_len; + __le16 output_buf_len; + __le16 rsvd0[3]; + struct msgbuf_buf_addr req_buf_addr; + __le32 rsvd1[2]; +}; + +struct msgbuf_tx_msghdr { + struct msgbuf_common_hdr msg; + u8 txhdr[ETH_HLEN]; + u8 flags; + u8 seg_cnt; + struct msgbuf_buf_addr metadata_buf_addr; + struct msgbuf_buf_addr data_buf_addr; + __le16 metadata_buf_len; + __le16 data_len; + __le32 rsvd0; +}; + +struct msgbuf_rx_bufpost { + struct msgbuf_common_hdr msg; + __le16 metadata_buf_len; + __le16 data_buf_len; + __le32 rsvd0; + struct msgbuf_buf_addr metadata_buf_addr; + struct msgbuf_buf_addr data_buf_addr; +}; + +struct msgbuf_rx_ioctl_resp_or_event { + struct msgbuf_common_hdr msg; + __le16 host_buf_len; + __le16 rsvd0[3]; + struct msgbuf_buf_addr host_buf_addr; + __le32 rsvd1[4]; +}; + +struct msgbuf_completion_hdr { + __le16 status; + __le16 flow_ring_id; +}; + +struct msgbuf_rx_event { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 event_data_len; + __le16 seqnum; + __le16 rsvd0[4]; +}; + +struct msgbuf_ioctl_resp_hdr { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 resp_len; + __le16 trans_id; + __le32 cmd; + __le32 rsvd0; +}; + +struct msgbuf_tx_status { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 metadata_len; + __le16 tx_status; +}; + +struct msgbuf_rx_complete { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 metadata_len; + __le16 data_len; + __le16 data_offset; + __le16 flags; + __le32 rx_status_0; + __le32 rx_status_1; + __le32 rsvd0; +}; + +struct msgbuf_tx_flowring_create_req { + struct msgbuf_common_hdr msg; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + u8 tid; + u8 if_flags; + __le16 flow_ring_id; + u8 tc; + u8 priority; + __le16 int_vector; + __le16 max_items; + __le16 len_item; + struct msgbuf_buf_addr flow_ring_addr; +}; + +struct msgbuf_tx_flowring_delete_req { + struct msgbuf_common_hdr msg; + __le16 flow_ring_id; + __le16 reason; + __le32 rsvd0[7]; +}; + +struct msgbuf_flowring_create_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 rsvd0[3]; +}; + +struct msgbuf_flowring_delete_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 rsvd0[3]; +}; + +struct msgbuf_flowring_flush_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 rsvd0[3]; +}; + +struct brcmf_msgbuf_work_item { + struct list_head queue; + u32 flowid; + int ifidx; + u8 sa[ETH_ALEN]; + u8 da[ETH_ALEN]; +}; + +struct brcmf_msgbuf { + struct brcmf_pub *drvr; + + struct brcmf_commonring **commonrings; + struct brcmf_commonring **flowrings; + dma_addr_t *flowring_dma_handle; + u16 nrof_flowrings; + + u16 rx_dataoffset; + u32 max_rxbufpost; + u16 rx_metadata_offset; + u32 rxbufpost; + + u32 max_ioctlrespbuf; + u32 cur_ioctlrespbuf; + u32 max_eventbuf; + u32 cur_eventbuf; + + void *ioctbuf; + dma_addr_t ioctbuf_handle; + u32 ioctbuf_phys_hi; + u32 ioctbuf_phys_lo; + int ioctl_resp_status; + u32 ioctl_resp_ret_len; + u32 ioctl_resp_pktid; + + u16 data_seq_no; + u16 ioctl_seq_no; + u32 reqid; + wait_queue_head_t ioctl_resp_wait; + bool ctl_completed; + + struct brcmf_msgbuf_pktids *tx_pktids; + struct brcmf_msgbuf_pktids *rx_pktids; + struct brcmf_flowring *flow; + + struct workqueue_struct *txflow_wq; + struct work_struct txflow_work; + unsigned long *flow_map; + unsigned long *txstatus_done_map; + + struct work_struct flowring_work; + spinlock_t flowring_work_lock; + struct list_head work_queue; +}; + +struct brcmf_msgbuf_pktid { + atomic_t allocated; + u16 data_offset; + struct sk_buff *skb; + dma_addr_t physaddr; +}; + +struct brcmf_msgbuf_pktids { + u32 array_size; + u32 last_allocated_idx; + enum dma_data_direction direction; + struct brcmf_msgbuf_pktid *array; +}; + +static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf); + + +static struct brcmf_msgbuf_pktids * +brcmf_msgbuf_init_pktids(u32 nr_array_entries, + enum dma_data_direction direction) +{ + struct brcmf_msgbuf_pktid *array; + struct brcmf_msgbuf_pktids *pktids; + + array = kcalloc(nr_array_entries, sizeof(*array), GFP_KERNEL); + if (!array) + return NULL; + + pktids = kzalloc(sizeof(*pktids), GFP_KERNEL); + if (!pktids) { + kfree(array); + return NULL; + } + pktids->array = array; + pktids->array_size = nr_array_entries; + + return pktids; +} + + +static int +brcmf_msgbuf_alloc_pktid(struct device *dev, + struct brcmf_msgbuf_pktids *pktids, + struct sk_buff *skb, u16 data_offset, + dma_addr_t *physaddr, u32 *idx) +{ + struct brcmf_msgbuf_pktid *array; + u32 count; + + array = pktids->array; + + *physaddr = dma_map_single(dev, skb->data + data_offset, + skb->len - data_offset, pktids->direction); + + if (dma_mapping_error(dev, *physaddr)) { + brcmf_err("dma_map_single failed !!\n"); + return -ENOMEM; + } + + *idx = pktids->last_allocated_idx; + + count = 0; + do { + (*idx)++; + if (*idx == pktids->array_size) + *idx = 0; + if (array[*idx].allocated.counter == 0) + if (atomic_cmpxchg(&array[*idx].allocated, 0, 1) == 0) + break; + count++; + } while (count < pktids->array_size); + + if (count == pktids->array_size) + return -ENOMEM; + + array[*idx].data_offset = data_offset; + array[*idx].physaddr = *physaddr; + array[*idx].skb = skb; + + pktids->last_allocated_idx = *idx; + + return 0; +} + + +static struct sk_buff * +brcmf_msgbuf_get_pktid(struct device *dev, struct brcmf_msgbuf_pktids *pktids, + u32 idx) +{ + struct brcmf_msgbuf_pktid *pktid; + struct sk_buff *skb; + + if (idx >= pktids->array_size) { + brcmf_err("Invalid packet id %d (max %d)\n", idx, + pktids->array_size); + return NULL; + } + if (pktids->array[idx].allocated.counter) { + pktid = &pktids->array[idx]; + dma_unmap_single(dev, pktid->physaddr, + pktid->skb->len - pktid->data_offset, + pktids->direction); + skb = pktid->skb; + pktid->allocated.counter = 0; + return skb; + } else { + brcmf_err("Invalid packet id %d (not in use)\n", idx); + } + + return NULL; +} + + +static void +brcmf_msgbuf_release_array(struct device *dev, + struct brcmf_msgbuf_pktids *pktids) +{ + struct brcmf_msgbuf_pktid *array; + struct brcmf_msgbuf_pktid *pktid; + u32 count; + + array = pktids->array; + count = 0; + do { + if (array[count].allocated.counter) { + pktid = &array[count]; + dma_unmap_single(dev, pktid->physaddr, + pktid->skb->len - pktid->data_offset, + pktids->direction); + brcmu_pkt_buf_free_skb(pktid->skb); + } + count++; + } while (count < pktids->array_size); + + kfree(array); + kfree(pktids); +} + + +static void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf) +{ + if (msgbuf->rx_pktids) + brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids); + if (msgbuf->tx_pktids) + brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids); +} + + +static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_commonring *commonring; + struct msgbuf_ioctl_req_hdr *request; + u16 buf_len; + void *ret_ptr; + int err; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + return -ENOMEM; + } + + msgbuf->reqid++; + + request = (struct msgbuf_ioctl_req_hdr *)ret_ptr; + request->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ; + request->msg.ifidx = (u8)ifidx; + request->msg.flags = 0; + request->msg.request_id = cpu_to_le32(BRCMF_IOCTL_REQ_PKTID); + request->cmd = cpu_to_le32(cmd); + request->output_buf_len = cpu_to_le16(len); + request->trans_id = cpu_to_le16(msgbuf->reqid); + + buf_len = min_t(u16, len, BRCMF_TX_IOCTL_MAX_MSG_SIZE); + request->input_buf_len = cpu_to_le16(buf_len); + request->req_buf_addr.high_addr = cpu_to_le32(msgbuf->ioctbuf_phys_hi); + request->req_buf_addr.low_addr = cpu_to_le32(msgbuf->ioctbuf_phys_lo); + if (buf) + memcpy(msgbuf->ioctbuf, buf, buf_len); + else + memset(msgbuf->ioctbuf, 0, buf_len); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + + return err; +} + + +static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf) +{ + return wait_event_timeout(msgbuf->ioctl_resp_wait, + msgbuf->ctl_completed, + msecs_to_jiffies(MSGBUF_IOCTL_RESP_TIMEOUT)); +} + + +static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf) +{ + msgbuf->ctl_completed = true; + if (waitqueue_active(&msgbuf->ioctl_resp_wait)) + wake_up(&msgbuf->ioctl_resp_wait); +} + + +static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct sk_buff *skb = NULL; + int timeout; + int err; + + brcmf_dbg(MSGBUF, "ifidx=%d, cmd=%d, len=%d\n", ifidx, cmd, len); + msgbuf->ctl_completed = false; + err = brcmf_msgbuf_tx_ioctl(drvr, ifidx, cmd, buf, len); + if (err) + return err; + + timeout = brcmf_msgbuf_ioctl_resp_wait(msgbuf); + if (!timeout) { + brcmf_err("Timeout on response for query command\n"); + return -EIO; + } + + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, + msgbuf->ioctl_resp_pktid); + if (msgbuf->ioctl_resp_ret_len != 0) { + if (!skb) + return -EBADF; + + memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ? + len : msgbuf->ioctl_resp_ret_len); + } + brcmu_pkt_buf_free_skb(skb); + + return msgbuf->ioctl_resp_status; +} + + +static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + return brcmf_msgbuf_query_dcmd(drvr, ifidx, cmd, buf, len); +} + + +static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws, + struct sk_buff *skb, struct brcmf_if **ifp) +{ + return -ENODEV; +} + + +static void +brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid) +{ + u32 dma_sz; + void *dma_buf; + + brcmf_dbg(MSGBUF, "Removing flowring %d\n", flowid); + + dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE; + dma_buf = msgbuf->flowrings[flowid]->buf_addr; + dma_free_coherent(msgbuf->drvr->bus_if->dev, dma_sz, dma_buf, + msgbuf->flowring_dma_handle[flowid]); + + brcmf_flowring_delete(msgbuf->flow, flowid); +} + + +static struct brcmf_msgbuf_work_item * +brcmf_msgbuf_dequeue_work(struct brcmf_msgbuf *msgbuf) +{ + struct brcmf_msgbuf_work_item *work = NULL; + ulong flags; + + spin_lock_irqsave(&msgbuf->flowring_work_lock, flags); + if (!list_empty(&msgbuf->work_queue)) { + work = list_first_entry(&msgbuf->work_queue, + struct brcmf_msgbuf_work_item, queue); + list_del(&work->queue); + } + spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags); + + return work; +} + + +static u32 +brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf, + struct brcmf_msgbuf_work_item *work) +{ + struct msgbuf_tx_flowring_create_req *create; + struct brcmf_commonring *commonring; + void *ret_ptr; + u32 flowid; + void *dma_buf; + u32 dma_sz; + u64 address; + int err; + + flowid = work->flowid; + dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE; + dma_buf = dma_alloc_coherent(msgbuf->drvr->bus_if->dev, dma_sz, + &msgbuf->flowring_dma_handle[flowid], + GFP_KERNEL); + if (!dma_buf) { + brcmf_err("dma_alloc_coherent failed\n"); + brcmf_flowring_delete(msgbuf->flow, flowid); + return BRCMF_FLOWRING_INVALID_ID; + } + + brcmf_commonring_config(msgbuf->flowrings[flowid], + BRCMF_H2D_TXFLOWRING_MAX_ITEM, + BRCMF_H2D_TXFLOWRING_ITEMSIZE, dma_buf); + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return BRCMF_FLOWRING_INVALID_ID; + } + + create = (struct msgbuf_tx_flowring_create_req *)ret_ptr; + create->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE; + create->msg.ifidx = work->ifidx; + create->msg.request_id = 0; + create->tid = brcmf_flowring_tid(msgbuf->flow, flowid); + create->flow_ring_id = cpu_to_le16(flowid + + BRCMF_NROF_H2D_COMMON_MSGRINGS); + memcpy(create->sa, work->sa, ETH_ALEN); + memcpy(create->da, work->da, ETH_ALEN); + address = (u64)msgbuf->flowring_dma_handle[flowid]; + create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32); + create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff); + create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM); + create->len_item = cpu_to_le16(BRCMF_H2D_TXFLOWRING_ITEMSIZE); + + brcmf_dbg(MSGBUF, "Send Flow Create Req flow ID %d for peer %pM prio %d ifindex %d\n", + flowid, work->da, create->tid, work->ifidx); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + if (err) { + brcmf_err("Failed to write commonring\n"); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return BRCMF_FLOWRING_INVALID_ID; + } + + return flowid; +} + + +static void brcmf_msgbuf_flowring_worker(struct work_struct *work) +{ + struct brcmf_msgbuf *msgbuf; + struct brcmf_msgbuf_work_item *create; + + msgbuf = container_of(work, struct brcmf_msgbuf, flowring_work); + + while ((create = brcmf_msgbuf_dequeue_work(msgbuf))) { + brcmf_msgbuf_flowring_create_worker(msgbuf, create); + kfree(create); + } +} + + +static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, + struct sk_buff *skb) +{ + struct brcmf_msgbuf_work_item *create; + struct ethhdr *eh = (struct ethhdr *)(skb->data); + u32 flowid; + ulong flags; + + create = kzalloc(sizeof(*create), GFP_ATOMIC); + if (create == NULL) + return BRCMF_FLOWRING_INVALID_ID; + + flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest, + skb->priority, ifidx); + if (flowid == BRCMF_FLOWRING_INVALID_ID) { + kfree(create); + return flowid; + } + + create->flowid = flowid; + create->ifidx = ifidx; + memcpy(create->sa, eh->h_source, ETH_ALEN); + memcpy(create->da, eh->h_dest, ETH_ALEN); + + spin_lock_irqsave(&msgbuf->flowring_work_lock, flags); + list_add_tail(&create->queue, &msgbuf->work_queue); + spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags); + schedule_work(&msgbuf->flowring_work); + + return flowid; +} + + +static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) +{ + struct brcmf_flowring *flow = msgbuf->flow; + struct brcmf_commonring *commonring; + void *ret_ptr; + u32 count; + struct sk_buff *skb; + dma_addr_t physaddr; + u32 pktid; + struct msgbuf_tx_msghdr *tx_msghdr; + u64 address; + + commonring = msgbuf->flowrings[flowid]; + if (!brcmf_commonring_write_available(commonring)) + return; + + brcmf_commonring_lock(commonring); + + count = BRCMF_MSGBUF_TX_FLUSH_CNT2 - BRCMF_MSGBUF_TX_FLUSH_CNT1; + while (brcmf_flowring_qlen(flow, flowid)) { + skb = brcmf_flowring_dequeue(flow, flowid); + if (skb == NULL) { + brcmf_err("No SKB, but qlen %d\n", + brcmf_flowring_qlen(flow, flowid)); + break; + } + skb_orphan(skb); + if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids, skb, ETH_HLEN, + &physaddr, &pktid)) { + brcmf_flowring_reinsert(flow, flowid, skb); + brcmf_err("No PKTID available !!\n"); + break; + } + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids, pktid); + brcmf_flowring_reinsert(flow, flowid, skb); + break; + } + count++; + + tx_msghdr = (struct msgbuf_tx_msghdr *)ret_ptr; + + tx_msghdr->msg.msgtype = MSGBUF_TYPE_TX_POST; + tx_msghdr->msg.request_id = cpu_to_le32(pktid); + tx_msghdr->msg.ifidx = brcmf_flowring_ifidx_get(flow, flowid); + tx_msghdr->flags = BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3; + tx_msghdr->flags |= (skb->priority & 0x07) << + BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT; + tx_msghdr->seg_cnt = 1; + memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN); + tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN); + address = (u64)physaddr; + tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32); + tx_msghdr->data_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + tx_msghdr->metadata_buf_len = 0; + tx_msghdr->metadata_buf_addr.high_addr = 0; + tx_msghdr->metadata_buf_addr.low_addr = 0; + atomic_inc(&commonring->outstanding_tx); + if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) { + brcmf_commonring_write_complete(commonring); + count = 0; + } + } + if (count) + brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); +} + + +static void brcmf_msgbuf_txflow_worker(struct work_struct *worker) +{ + struct brcmf_msgbuf *msgbuf; + u32 flowid; + + msgbuf = container_of(worker, struct brcmf_msgbuf, txflow_work); + for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->nrof_flowrings) { + clear_bit(flowid, msgbuf->flow_map); + brcmf_msgbuf_txflow(msgbuf, flowid); + } +} + + +static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid, + bool force) +{ + struct brcmf_commonring *commonring; + + set_bit(flowid, msgbuf->flow_map); + commonring = msgbuf->flowrings[flowid]; + if ((force) || (atomic_read(&commonring->outstanding_tx) < + BRCMF_MSGBUF_DELAY_TXWORKER_THRS)) + queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work); + + return 0; +} + + +static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, + u8 offset, struct sk_buff *skb) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_flowring *flow = msgbuf->flow; + struct ethhdr *eh = (struct ethhdr *)(skb->data); + u32 flowid; + u32 queue_count; + bool force; + + flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx); + if (flowid == BRCMF_FLOWRING_INVALID_ID) { + flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb); + if (flowid == BRCMF_FLOWRING_INVALID_ID) + return -ENOMEM; + } + queue_count = brcmf_flowring_enqueue(flow, flowid, skb); + force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0); + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, force); + + return 0; +} + + +static void +brcmf_msgbuf_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + + brcmf_flowring_configure_addr_mode(msgbuf->flow, ifidx, addr_mode); +} + + +static void +brcmf_msgbuf_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + + brcmf_flowring_delete_peer(msgbuf->flow, ifidx, peer); +} + + +static void +brcmf_msgbuf_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + + brcmf_flowring_add_tdls_peer(msgbuf->flow, ifidx, peer); +} + + +static void +brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_ioctl_resp_hdr *ioctl_resp; + + ioctl_resp = (struct msgbuf_ioctl_resp_hdr *)buf; + + msgbuf->ioctl_resp_status = + (s16)le16_to_cpu(ioctl_resp->compl_hdr.status); + msgbuf->ioctl_resp_ret_len = le16_to_cpu(ioctl_resp->resp_len); + msgbuf->ioctl_resp_pktid = le32_to_cpu(ioctl_resp->msg.request_id); + + brcmf_msgbuf_ioctl_resp_wake(msgbuf); + + if (msgbuf->cur_ioctlrespbuf) + msgbuf->cur_ioctlrespbuf--; + brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf); +} + + +static void +brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct brcmf_commonring *commonring; + struct msgbuf_tx_status *tx_status; + u32 idx; + struct sk_buff *skb; + u16 flowid; + + tx_status = (struct msgbuf_tx_status *)buf; + idx = le32_to_cpu(tx_status->msg.request_id); + flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id); + flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids, idx); + if (!skb) + return; + + set_bit(flowid, msgbuf->txstatus_done_map); + commonring = msgbuf->flowrings[flowid]; + atomic_dec(&commonring->outstanding_tx); + + brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx), + skb, true); +} + + +static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) +{ + struct brcmf_commonring *commonring; + void *ret_ptr; + struct sk_buff *skb; + u16 alloced; + u32 pktlen; + dma_addr_t physaddr; + struct msgbuf_rx_bufpost *rx_bufpost; + u64 address; + u32 pktid; + u32 i; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT]; + ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring, + count, + &alloced); + if (!ret_ptr) { + brcmf_dbg(MSGBUF, "Failed to reserve space in commonring\n"); + return 0; + } + + for (i = 0; i < alloced; i++) { + rx_bufpost = (struct msgbuf_rx_bufpost *)ret_ptr; + memset(rx_bufpost, 0, sizeof(*rx_bufpost)); + + skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE); + + if (skb == NULL) { + brcmf_err("Failed to alloc SKB\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + + pktlen = skb->len; + if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, skb, 0, + &physaddr, &pktid)) { + dev_kfree_skb_any(skb); + brcmf_err("No PKTID available !!\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + + if (msgbuf->rx_metadata_offset) { + address = (u64)physaddr; + rx_bufpost->metadata_buf_len = + cpu_to_le16(msgbuf->rx_metadata_offset); + rx_bufpost->metadata_buf_addr.high_addr = + cpu_to_le32(address >> 32); + rx_bufpost->metadata_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + + skb_pull(skb, msgbuf->rx_metadata_offset); + pktlen = skb->len; + physaddr += msgbuf->rx_metadata_offset; + } + rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST; + rx_bufpost->msg.request_id = cpu_to_le32(pktid); + + address = (u64)physaddr; + rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen); + rx_bufpost->data_buf_addr.high_addr = + cpu_to_le32(address >> 32); + rx_bufpost->data_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + + ret_ptr += brcmf_commonring_len_item(commonring); + } + + if (i) + brcmf_commonring_write_complete(commonring); + + return i; +} + + +static void +brcmf_msgbuf_rxbuf_data_fill(struct brcmf_msgbuf *msgbuf) +{ + u32 fillbufs; + u32 retcount; + + fillbufs = msgbuf->max_rxbufpost - msgbuf->rxbufpost; + + while (fillbufs) { + retcount = brcmf_msgbuf_rxbuf_data_post(msgbuf, fillbufs); + if (!retcount) + break; + msgbuf->rxbufpost += retcount; + fillbufs -= retcount; + } +} + + +static void +brcmf_msgbuf_update_rxbufpost_count(struct brcmf_msgbuf *msgbuf, u16 rxcnt) +{ + msgbuf->rxbufpost -= rxcnt; + if (msgbuf->rxbufpost <= (msgbuf->max_rxbufpost - + BRCMF_MSGBUF_RXBUFPOST_THRESHOLD)) + brcmf_msgbuf_rxbuf_data_fill(msgbuf); +} + + +static u32 +brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf, + u32 count) +{ + struct brcmf_commonring *commonring; + void *ret_ptr; + struct sk_buff *skb; + u16 alloced; + u32 pktlen; + dma_addr_t physaddr; + struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost; + u64 address; + u32 pktid; + u32 i; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring, + count, + &alloced); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + return 0; + } + + for (i = 0; i < alloced; i++) { + rx_bufpost = (struct msgbuf_rx_ioctl_resp_or_event *)ret_ptr; + memset(rx_bufpost, 0, sizeof(*rx_bufpost)); + + skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE); + + if (skb == NULL) { + brcmf_err("Failed to alloc SKB\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + + pktlen = skb->len; + if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, skb, 0, + &physaddr, &pktid)) { + dev_kfree_skb_any(skb); + brcmf_err("No PKTID available !!\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + if (event_buf) + rx_bufpost->msg.msgtype = MSGBUF_TYPE_EVENT_BUF_POST; + else + rx_bufpost->msg.msgtype = + MSGBUF_TYPE_IOCTLRESP_BUF_POST; + rx_bufpost->msg.request_id = cpu_to_le32(pktid); + + address = (u64)physaddr; + rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen); + rx_bufpost->host_buf_addr.high_addr = + cpu_to_le32(address >> 32); + rx_bufpost->host_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + + ret_ptr += brcmf_commonring_len_item(commonring); + } + + if (i) + brcmf_commonring_write_complete(commonring); + + brcmf_commonring_unlock(commonring); + + return i; +} + + +static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf) +{ + u32 count; + + count = msgbuf->max_ioctlrespbuf - msgbuf->cur_ioctlrespbuf; + count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, false, count); + msgbuf->cur_ioctlrespbuf += count; +} + + +static void brcmf_msgbuf_rxbuf_event_post(struct brcmf_msgbuf *msgbuf) +{ + u32 count; + + count = msgbuf->max_eventbuf - msgbuf->cur_eventbuf; + count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, true, count); + msgbuf->cur_eventbuf += count; +} + + +static void +brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb, + u8 ifidx) +{ + struct brcmf_if *ifp; + + ifp = brcmf_get_ifp(msgbuf->drvr, ifidx); + if (!ifp || !ifp->ndev) { + brcmf_err("Received pkt for invalid ifidx %d\n", ifidx); + brcmu_pkt_buf_free_skb(skb); + return; + } + brcmf_netif_rx(ifp, skb); +} + + +static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_rx_event *event; + u32 idx; + u16 buflen; + struct sk_buff *skb; + + event = (struct msgbuf_rx_event *)buf; + idx = le32_to_cpu(event->msg.request_id); + buflen = le16_to_cpu(event->event_data_len); + + if (msgbuf->cur_eventbuf) + msgbuf->cur_eventbuf--; + brcmf_msgbuf_rxbuf_event_post(msgbuf); + + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, idx); + if (!skb) + return; + + if (msgbuf->rx_dataoffset) + skb_pull(skb, msgbuf->rx_dataoffset); + + skb_trim(skb, buflen); + + brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx); +} + + +static void +brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_rx_complete *rx_complete; + struct sk_buff *skb; + u16 data_offset; + u16 buflen; + u32 idx; + + brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1); + + rx_complete = (struct msgbuf_rx_complete *)buf; + data_offset = le16_to_cpu(rx_complete->data_offset); + buflen = le16_to_cpu(rx_complete->data_len); + idx = le32_to_cpu(rx_complete->msg.request_id); + + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, idx); + if (!skb) + return; + + if (data_offset) + skb_pull(skb, data_offset); + else if (msgbuf->rx_dataoffset) + skb_pull(skb, msgbuf->rx_dataoffset); + + skb_trim(skb, buflen); + + brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx); +} + + +static void +brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf, + void *buf) +{ + struct msgbuf_flowring_create_resp *flowring_create_resp; + u16 status; + u16 flowid; + + flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf; + + flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id); + flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + status = le16_to_cpu(flowring_create_resp->compl_hdr.status); + + if (status) { + brcmf_err("Flowring creation failed, code %d\n", status); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return; + } + brcmf_dbg(MSGBUF, "Flowring %d Create response status %d\n", flowid, + status); + + brcmf_flowring_open(msgbuf->flow, flowid); + + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true); +} + + +static void +brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf, + void *buf) +{ + struct msgbuf_flowring_delete_resp *flowring_delete_resp; + u16 status; + u16 flowid; + + flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf; + + flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id); + flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + status = le16_to_cpu(flowring_delete_resp->compl_hdr.status); + + if (status) { + brcmf_err("Flowring deletion failed, code %d\n", status); + brcmf_flowring_delete(msgbuf->flow, flowid); + return; + } + brcmf_dbg(MSGBUF, "Flowring %d Delete response status %d\n", flowid, + status); + + brcmf_msgbuf_remove_flowring(msgbuf, flowid); +} + + +static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_common_hdr *msg; + + msg = (struct msgbuf_common_hdr *)buf; + switch (msg->msgtype) { + case MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT\n"); + brcmf_msgbuf_process_flow_ring_create_response(msgbuf, buf); + break; + case MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT\n"); + brcmf_msgbuf_process_flow_ring_delete_response(msgbuf, buf); + break; + case MSGBUF_TYPE_IOCTLPTR_REQ_ACK: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTLPTR_REQ_ACK\n"); + break; + case MSGBUF_TYPE_IOCTL_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTL_CMPLT\n"); + brcmf_msgbuf_process_ioctl_complete(msgbuf, buf); + break; + case MSGBUF_TYPE_WL_EVENT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_WL_EVENT\n"); + brcmf_msgbuf_process_event(msgbuf, buf); + break; + case MSGBUF_TYPE_TX_STATUS: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_TX_STATUS\n"); + brcmf_msgbuf_process_txstatus(msgbuf, buf); + break; + case MSGBUF_TYPE_RX_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n"); + brcmf_msgbuf_process_rx_complete(msgbuf, buf); + break; + default: + brcmf_err("Unsupported msgtype %d\n", msg->msgtype); + break; + } +} + + +static void brcmf_msgbuf_process_rx(struct brcmf_msgbuf *msgbuf, + struct brcmf_commonring *commonring) +{ + void *buf; + u16 count; + u16 processed; + +again: + buf = brcmf_commonring_get_read_ptr(commonring, &count); + if (buf == NULL) + return; + + processed = 0; + while (count) { + brcmf_msgbuf_process_msgtype(msgbuf, + buf + msgbuf->rx_dataoffset); + buf += brcmf_commonring_len_item(commonring); + processed++; + if (processed == BRCMF_MSGBUF_UPDATE_RX_PTR_THRS) { + brcmf_commonring_read_complete(commonring, processed); + processed = 0; + } + count--; + } + if (processed) + brcmf_commonring_read_complete(commonring, processed); + + if (commonring->r_ptr == 0) + goto again; +} + + +int brcmf_proto_msgbuf_rx_trigger(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_commonring *commonring; + void *buf; + u32 flowid; + int qlen; + + buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE]; + brcmf_msgbuf_process_rx(msgbuf, buf); + buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE]; + brcmf_msgbuf_process_rx(msgbuf, buf); + buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE]; + brcmf_msgbuf_process_rx(msgbuf, buf); + + for_each_set_bit(flowid, msgbuf->txstatus_done_map, + msgbuf->nrof_flowrings) { + clear_bit(flowid, msgbuf->txstatus_done_map); + commonring = msgbuf->flowrings[flowid]; + qlen = brcmf_flowring_qlen(msgbuf->flow, flowid); + if ((qlen > BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) || + ((qlen) && (atomic_read(&commonring->outstanding_tx) < + BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS))) + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true); + } + + return 0; +} + + +void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct msgbuf_tx_flowring_delete_req *delete; + struct brcmf_commonring *commonring; + void *ret_ptr; + u8 ifidx; + int err; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("FW unaware, flowring will be removed !!\n"); + brcmf_commonring_unlock(commonring); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return; + } + + delete = (struct msgbuf_tx_flowring_delete_req *)ret_ptr; + + ifidx = brcmf_flowring_ifidx_get(msgbuf->flow, flowid); + + delete->msg.msgtype = MSGBUF_TYPE_FLOW_RING_DELETE; + delete->msg.ifidx = ifidx; + delete->msg.request_id = 0; + + delete->flow_ring_id = cpu_to_le16(flowid + + BRCMF_NROF_H2D_COMMON_MSGRINGS); + delete->reason = 0; + + brcmf_dbg(MSGBUF, "Send Flow Delete Req flow ID %d, ifindex %d\n", + flowid, ifidx); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + if (err) { + brcmf_err("Failed to submit RING_DELETE, flowring will be removed\n"); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + } +} + +#ifdef DEBUG +static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_commonring *commonring; + u16 i; + struct brcmf_flowring_ring *ring; + struct brcmf_flowring_hash *hash; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + seq_printf(seq, "h2d_ctl_submit: rp %4u, wp %4u, depth %4u\n", + commonring->r_ptr, commonring->w_ptr, commonring->depth); + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT]; + seq_printf(seq, "h2d_rx_submit: rp %4u, wp %4u, depth %4u\n", + commonring->r_ptr, commonring->w_ptr, commonring->depth); + commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE]; + seq_printf(seq, "d2h_ctl_cmplt: rp %4u, wp %4u, depth %4u\n", + commonring->r_ptr, commonring->w_ptr, commonring->depth); + commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE]; + seq_printf(seq, "d2h_tx_cmplt: rp %4u, wp %4u, depth %4u\n", + commonring->r_ptr, commonring->w_ptr, commonring->depth); + commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE]; + seq_printf(seq, "d2h_rx_cmplt: rp %4u, wp %4u, depth %4u\n", + commonring->r_ptr, commonring->w_ptr, commonring->depth); + + seq_printf(seq, "\nh2d_flowrings: depth %u\n", + BRCMF_H2D_TXFLOWRING_MAX_ITEM); + seq_puts(seq, "Active flowrings:\n"); + hash = msgbuf->flow->hash; + for (i = 0; i < msgbuf->flow->nrofrings; i++) { + if (!msgbuf->flow->rings[i]) + continue; + ring = msgbuf->flow->rings[i]; + if (ring->status != RING_OPEN) + continue; + commonring = msgbuf->flowrings[i]; + hash = &msgbuf->flow->hash[ring->hash_id]; + seq_printf(seq, "id %3u: rp %4u, wp %4u, qlen %4u, blocked %u\n" + " ifidx %u, fifo %u, da %pM\n", + i, commonring->r_ptr, commonring->w_ptr, + skb_queue_len(&ring->skblist), ring->blocked, + hash->ifidx, hash->fifo, hash->mac); + } + + return 0; +} +#else +static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data) +{ + return 0; +} +#endif + +int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) +{ + struct brcmf_bus_msgbuf *if_msgbuf; + struct brcmf_msgbuf *msgbuf; + u64 address; + u32 count; + + if_msgbuf = drvr->bus_if->msgbuf; + msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL); + if (!msgbuf) + goto fail; + + msgbuf->txflow_wq = create_singlethread_workqueue("msgbuf_txflow"); + if (msgbuf->txflow_wq == NULL) { + brcmf_err("workqueue creation failed\n"); + goto fail; + } + INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker); + count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings); + count = count * sizeof(unsigned long); + msgbuf->flow_map = kzalloc(count, GFP_KERNEL); + if (!msgbuf->flow_map) + goto fail; + + msgbuf->txstatus_done_map = kzalloc(count, GFP_KERNEL); + if (!msgbuf->txstatus_done_map) + goto fail; + + msgbuf->drvr = drvr; + msgbuf->ioctbuf = dma_alloc_coherent(drvr->bus_if->dev, + BRCMF_TX_IOCTL_MAX_MSG_SIZE, + &msgbuf->ioctbuf_handle, + GFP_KERNEL); + if (!msgbuf->ioctbuf) + goto fail; + address = (u64)msgbuf->ioctbuf_handle; + msgbuf->ioctbuf_phys_hi = address >> 32; + msgbuf->ioctbuf_phys_lo = address & 0xffffffff; + + drvr->proto->hdrpull = brcmf_msgbuf_hdrpull; + drvr->proto->query_dcmd = brcmf_msgbuf_query_dcmd; + drvr->proto->set_dcmd = brcmf_msgbuf_set_dcmd; + drvr->proto->txdata = brcmf_msgbuf_txdata; + drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode; + drvr->proto->delete_peer = brcmf_msgbuf_delete_peer; + drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer; + drvr->proto->pd = msgbuf; + + init_waitqueue_head(&msgbuf->ioctl_resp_wait); + + msgbuf->commonrings = + (struct brcmf_commonring **)if_msgbuf->commonrings; + msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings; + msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings; + msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings * + sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL); + if (!msgbuf->flowring_dma_handle) + goto fail; + + msgbuf->rx_dataoffset = if_msgbuf->rx_dataoffset; + msgbuf->max_rxbufpost = if_msgbuf->max_rxbufpost; + + msgbuf->max_ioctlrespbuf = BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST; + msgbuf->max_eventbuf = BRCMF_MSGBUF_MAX_EVENTBUF_POST; + + msgbuf->tx_pktids = brcmf_msgbuf_init_pktids(NR_TX_PKTIDS, + DMA_TO_DEVICE); + if (!msgbuf->tx_pktids) + goto fail; + msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS, + DMA_FROM_DEVICE); + if (!msgbuf->rx_pktids) + goto fail; + + msgbuf->flow = brcmf_flowring_attach(drvr->bus_if->dev, + if_msgbuf->nrof_flowrings); + if (!msgbuf->flow) + goto fail; + + + brcmf_dbg(MSGBUF, "Feeding buffers, rx data %d, rx event %d, rx ioctl resp %d\n", + msgbuf->max_rxbufpost, msgbuf->max_eventbuf, + msgbuf->max_ioctlrespbuf); + count = 0; + do { + brcmf_msgbuf_rxbuf_data_fill(msgbuf); + if (msgbuf->max_rxbufpost != msgbuf->rxbufpost) + msleep(10); + else + break; + count++; + } while (count < 10); + brcmf_msgbuf_rxbuf_event_post(msgbuf); + brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf); + + INIT_WORK(&msgbuf->flowring_work, brcmf_msgbuf_flowring_worker); + spin_lock_init(&msgbuf->flowring_work_lock); + INIT_LIST_HEAD(&msgbuf->work_queue); + + brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read); + + return 0; + +fail: + if (msgbuf) { + kfree(msgbuf->flow_map); + kfree(msgbuf->txstatus_done_map); + brcmf_msgbuf_release_pktids(msgbuf); + kfree(msgbuf->flowring_dma_handle); + if (msgbuf->ioctbuf) + dma_free_coherent(drvr->bus_if->dev, + BRCMF_TX_IOCTL_MAX_MSG_SIZE, + msgbuf->ioctbuf, + msgbuf->ioctbuf_handle); + kfree(msgbuf); + } + return -ENOMEM; +} + + +void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) +{ + struct brcmf_msgbuf *msgbuf; + struct brcmf_msgbuf_work_item *work; + + brcmf_dbg(TRACE, "Enter\n"); + if (drvr->proto->pd) { + msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + cancel_work_sync(&msgbuf->flowring_work); + while (!list_empty(&msgbuf->work_queue)) { + work = list_first_entry(&msgbuf->work_queue, + struct brcmf_msgbuf_work_item, + queue); + list_del(&work->queue); + kfree(work); + } + kfree(msgbuf->flow_map); + kfree(msgbuf->txstatus_done_map); + if (msgbuf->txflow_wq) + destroy_workqueue(msgbuf->txflow_wq); + + brcmf_flowring_detach(msgbuf->flow); + dma_free_coherent(drvr->bus_if->dev, + BRCMF_TX_IOCTL_MAX_MSG_SIZE, + msgbuf->ioctbuf, msgbuf->ioctbuf_handle); + brcmf_msgbuf_release_pktids(msgbuf); + kfree(msgbuf->flowring_dma_handle); + kfree(msgbuf); + drvr->proto->pd = NULL; + } +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h new file mode 100644 index 000000000000..3d513e407e3d --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2014 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_MSGBUF_H +#define BRCMFMAC_MSGBUF_H + +#ifdef CONFIG_BRCMFMAC_PROTO_MSGBUF + +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 64 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 512 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 64 +#define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM 1024 +#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 512 +#define BRCMF_H2D_TXFLOWRING_MAX_ITEM 512 + +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE 40 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE 32 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE 24 +#define BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE 16 +#define BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE 32 +#define BRCMF_H2D_TXFLOWRING_ITEMSIZE 48 + + +int brcmf_proto_msgbuf_rx_trigger(struct device *dev); +void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid); +int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr); +void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr); +#else +static inline int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) +{ + return 0; +} +static inline void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) {} +#endif + +#endif /* BRCMFMAC_MSGBUF_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c new file mode 100644 index 000000000000..03f35e0c52ca --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014 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 +#include +#include + +#include +#include "debug.h" +#include "sdio.h" + +void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) +{ + struct device *dev = sdiodev->dev; + struct device_node *np = dev->of_node; + int irq; + u32 irqf; + u32 val; + + if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac")) + return; + + sdiodev->pdata = devm_kzalloc(dev, sizeof(*sdiodev->pdata), GFP_KERNEL); + if (!sdiodev->pdata) + return; + + if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) + sdiodev->pdata->drive_strength = val; + + /* make sure there are interrupts defined in the node */ + if (!of_find_property(np, "interrupts", NULL)) + return; + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + brcmf_err("interrupt could not be mapped\n"); + return; + } + irqf = irqd_get_trigger_type(irq_get_irq_data(irq)); + + sdiodev->pdata->oob_irq_supported = true; + sdiodev->pdata->oob_irq_nr = irq; + sdiodev->pdata->oob_irq_flags = irqf; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h new file mode 100644 index 000000000000..5f7c3550deda --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2014 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. + */ +#ifdef CONFIG_OF +void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev); +#else +static void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) +{ +} +#endif /* CONFIG_OF */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c new file mode 100644 index 000000000000..d224b3dd72ed --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -0,0 +1,2401 @@ +/* + * Copyright (c) 2012 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 +#include + +#include +#include +#include +#include "core.h" +#include "debug.h" +#include "fwil.h" +#include "fwil_types.h" +#include "p2p.h" +#include "cfg80211.h" + +/* parameters used for p2p escan */ +#define P2PAPI_SCAN_NPROBES 1 +#define P2PAPI_SCAN_DWELL_TIME_MS 80 +#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40 +#define P2PAPI_SCAN_HOME_TIME_MS 60 +#define P2PAPI_SCAN_NPROBS_TIME_MS 30 +#define P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS 100 +#define WL_SCAN_CONNECT_DWELL_TIME_MS 200 +#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20 + +#define BRCMF_P2P_WILDCARD_SSID "DIRECT-" +#define BRCMF_P2P_WILDCARD_SSID_LEN (sizeof(BRCMF_P2P_WILDCARD_SSID) - 1) + +#define SOCIAL_CHAN_1 1 +#define SOCIAL_CHAN_2 6 +#define SOCIAL_CHAN_3 11 +#define IS_P2P_SOCIAL_CHANNEL(channel) ((channel == SOCIAL_CHAN_1) || \ + (channel == SOCIAL_CHAN_2) || \ + (channel == SOCIAL_CHAN_3)) +#define BRCMF_P2P_TEMP_CHAN SOCIAL_CHAN_3 +#define SOCIAL_CHAN_CNT 3 +#define AF_PEER_SEARCH_CNT 2 + +#define BRCMF_SCB_TIMEOUT_VALUE 20 + +#define P2P_VER 9 /* P2P version: 9=WiFi P2P v1.0 */ +#define P2P_PUB_AF_CATEGORY 0x04 +#define P2P_PUB_AF_ACTION 0x09 +#define P2P_AF_CATEGORY 0x7f +#define P2P_OUI "\x50\x6F\x9A" /* P2P OUI */ +#define P2P_OUI_LEN 3 /* P2P OUI length */ + +/* Action Frame Constants */ +#define DOT11_ACTION_HDR_LEN 2 /* action frame category + action */ +#define DOT11_ACTION_CAT_OFF 0 /* category offset */ +#define DOT11_ACTION_ACT_OFF 1 /* action offset */ + +#define P2P_AF_DWELL_TIME 200 +#define P2P_AF_MIN_DWELL_TIME 100 +#define P2P_AF_MED_DWELL_TIME 400 +#define P2P_AF_LONG_DWELL_TIME 1000 +#define P2P_AF_TX_MAX_RETRY 1 +#define P2P_AF_MAX_WAIT_TIME 2000 +#define P2P_INVALID_CHANNEL -1 +#define P2P_CHANNEL_SYNC_RETRY 5 +#define P2P_AF_FRM_SCAN_MAX_WAIT 1500 +#define P2P_DEFAULT_SLEEP_TIME_VSDB 200 + +/* WiFi P2P Public Action Frame OUI Subtypes */ +#define P2P_PAF_GON_REQ 0 /* Group Owner Negotiation Req */ +#define P2P_PAF_GON_RSP 1 /* Group Owner Negotiation Rsp */ +#define P2P_PAF_GON_CONF 2 /* Group Owner Negotiation Confirm */ +#define P2P_PAF_INVITE_REQ 3 /* P2P Invitation Request */ +#define P2P_PAF_INVITE_RSP 4 /* P2P Invitation Response */ +#define P2P_PAF_DEVDIS_REQ 5 /* Device Discoverability Request */ +#define P2P_PAF_DEVDIS_RSP 6 /* Device Discoverability Response */ +#define P2P_PAF_PROVDIS_REQ 7 /* Provision Discovery Request */ +#define P2P_PAF_PROVDIS_RSP 8 /* Provision Discovery Response */ +#define P2P_PAF_SUBTYPE_INVALID 255 /* Invalid Subtype */ + +/* WiFi P2P Action Frame OUI Subtypes */ +#define P2P_AF_NOTICE_OF_ABSENCE 0 /* Notice of Absence */ +#define P2P_AF_PRESENCE_REQ 1 /* P2P Presence Request */ +#define P2P_AF_PRESENCE_RSP 2 /* P2P Presence Response */ +#define P2P_AF_GO_DISC_REQ 3 /* GO Discoverability Request */ + +/* P2P Service Discovery related */ +#define P2PSD_ACTION_CATEGORY 0x04 /* Public action frame */ +#define P2PSD_ACTION_ID_GAS_IREQ 0x0a /* GAS Initial Request AF */ +#define P2PSD_ACTION_ID_GAS_IRESP 0x0b /* GAS Initial Response AF */ +#define P2PSD_ACTION_ID_GAS_CREQ 0x0c /* GAS Comback Request AF */ +#define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comback Response AF */ + +/** + * struct brcmf_p2p_disc_st_le - set discovery state in firmware. + * + * @state: requested discovery state (see enum brcmf_p2p_disc_state). + * @chspec: channel parameter for %WL_P2P_DISC_ST_LISTEN state. + * @dwell: dwell time in ms for %WL_P2P_DISC_ST_LISTEN state. + */ +struct brcmf_p2p_disc_st_le { + u8 state; + __le16 chspec; + __le16 dwell; +}; + +/** + * enum brcmf_p2p_disc_state - P2P discovery state values + * + * @WL_P2P_DISC_ST_SCAN: P2P discovery with wildcard SSID and P2P IE. + * @WL_P2P_DISC_ST_LISTEN: P2P discovery off-channel for specified time. + * @WL_P2P_DISC_ST_SEARCH: P2P discovery with P2P wildcard SSID and P2P IE. + */ +enum brcmf_p2p_disc_state { + WL_P2P_DISC_ST_SCAN, + WL_P2P_DISC_ST_LISTEN, + WL_P2P_DISC_ST_SEARCH +}; + +/** + * struct brcmf_p2p_scan_le - P2P specific scan request. + * + * @type: type of scan method requested (values: 'E' or 'S'). + * @reserved: reserved (ignored). + * @eparams: parameters used for type 'E'. + * @sparams: parameters used for type 'S'. + */ +struct brcmf_p2p_scan_le { + u8 type; + u8 reserved[3]; + union { + struct brcmf_escan_params_le eparams; + struct brcmf_scan_params_le sparams; + }; +}; + +/** + * struct brcmf_p2p_pub_act_frame - WiFi P2P Public Action Frame + * + * @category: P2P_PUB_AF_CATEGORY + * @action: P2P_PUB_AF_ACTION + * @oui[3]: P2P_OUI + * @oui_type: OUI type - P2P_VER + * @subtype: OUI subtype - P2P_TYPE_* + * @dialog_token: nonzero, identifies req/rsp transaction + * @elts[1]: Variable length information elements. + */ +struct brcmf_p2p_pub_act_frame { + u8 category; + u8 action; + u8 oui[3]; + u8 oui_type; + u8 subtype; + u8 dialog_token; + u8 elts[1]; +}; + +/** + * struct brcmf_p2p_action_frame - WiFi P2P Action Frame + * + * @category: P2P_AF_CATEGORY + * @OUI[3]: OUI - P2P_OUI + * @type: OUI Type - P2P_VER + * @subtype: OUI Subtype - P2P_AF_* + * @dialog_token: nonzero, identifies req/resp tranaction + * @elts[1]: Variable length information elements. + */ +struct brcmf_p2p_action_frame { + u8 category; + u8 oui[3]; + u8 type; + u8 subtype; + u8 dialog_token; + u8 elts[1]; +}; + +/** + * struct brcmf_p2psd_gas_pub_act_frame - Wi-Fi GAS Public Action Frame + * + * @category: 0x04 Public Action Frame + * @action: 0x6c Advertisement Protocol + * @dialog_token: nonzero, identifies req/rsp transaction + * @query_data[1]: Query Data. SD gas ireq SD gas iresp + */ +struct brcmf_p2psd_gas_pub_act_frame { + u8 category; + u8 action; + u8 dialog_token; + u8 query_data[1]; +}; + +/** + * struct brcmf_config_af_params - Action Frame Parameters for tx. + * + * @mpc_onoff: To make sure to send successfully action frame, we have to + * turn off mpc 0: off, 1: on, (-1): do nothing + * @search_channel: 1: search peer's channel to send af + * extra_listen: keep the dwell time to get af response frame. + */ +struct brcmf_config_af_params { + s32 mpc_onoff; + bool search_channel; + bool extra_listen; +}; + +/** + * brcmf_p2p_is_pub_action() - true if p2p public type frame. + * + * @frame: action frame data. + * @frame_len: length of action frame data. + * + * Determine if action frame is p2p public action type + */ +static bool brcmf_p2p_is_pub_action(void *frame, u32 frame_len) +{ + struct brcmf_p2p_pub_act_frame *pact_frm; + + if (frame == NULL) + return false; + + pact_frm = (struct brcmf_p2p_pub_act_frame *)frame; + if (frame_len < sizeof(struct brcmf_p2p_pub_act_frame) - 1) + return false; + + if (pact_frm->category == P2P_PUB_AF_CATEGORY && + pact_frm->action == P2P_PUB_AF_ACTION && + pact_frm->oui_type == P2P_VER && + memcmp(pact_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0) + return true; + + return false; +} + +/** + * brcmf_p2p_is_p2p_action() - true if p2p action type frame. + * + * @frame: action frame data. + * @frame_len: length of action frame data. + * + * Determine if action frame is p2p action type + */ +static bool brcmf_p2p_is_p2p_action(void *frame, u32 frame_len) +{ + struct brcmf_p2p_action_frame *act_frm; + + if (frame == NULL) + return false; + + act_frm = (struct brcmf_p2p_action_frame *)frame; + if (frame_len < sizeof(struct brcmf_p2p_action_frame) - 1) + return false; + + if (act_frm->category == P2P_AF_CATEGORY && + act_frm->type == P2P_VER && + memcmp(act_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0) + return true; + + return false; +} + +/** + * brcmf_p2p_is_gas_action() - true if p2p gas action type frame. + * + * @frame: action frame data. + * @frame_len: length of action frame data. + * + * Determine if action frame is p2p gas action type + */ +static bool brcmf_p2p_is_gas_action(void *frame, u32 frame_len) +{ + struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm; + + if (frame == NULL) + return false; + + sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame; + if (frame_len < sizeof(struct brcmf_p2psd_gas_pub_act_frame) - 1) + return false; + + if (sd_act_frm->category != P2PSD_ACTION_CATEGORY) + return false; + + if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP) + return true; + + return false; +} + +/** + * brcmf_p2p_print_actframe() - debug print routine. + * + * @tx: Received or to be transmitted + * @frame: action frame data. + * @frame_len: length of action frame data. + * + * Print information about the p2p action frame + */ + +#ifdef DEBUG + +static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len) +{ + struct brcmf_p2p_pub_act_frame *pact_frm; + struct brcmf_p2p_action_frame *act_frm; + struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm; + + if (!frame || frame_len <= 2) + return; + + if (brcmf_p2p_is_pub_action(frame, frame_len)) { + pact_frm = (struct brcmf_p2p_pub_act_frame *)frame; + switch (pact_frm->subtype) { + case P2P_PAF_GON_REQ: + brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Req Frame\n", + (tx) ? "TX" : "RX"); + break; + case P2P_PAF_GON_RSP: + brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Rsp Frame\n", + (tx) ? "TX" : "RX"); + break; + case P2P_PAF_GON_CONF: + brcmf_dbg(TRACE, "%s P2P Group Owner Negotiation Confirm Frame\n", + (tx) ? "TX" : "RX"); + break; + case P2P_PAF_INVITE_REQ: + brcmf_dbg(TRACE, "%s P2P Invitation Request Frame\n", + (tx) ? "TX" : "RX"); + break; + case P2P_PAF_INVITE_RSP: + brcmf_dbg(TRACE, "%s P2P Invitation Response Frame\n", + (tx) ? "TX" : "RX"); + break; + case P2P_PAF_DEVDIS_REQ: + brcmf_dbg(TRACE, "%s P2P Device Discoverability Request Frame\n", + (tx) ? "TX" : "RX"); + break; + case P2P_PAF_DEVDIS_RSP: + brcmf_dbg(TRACE, "%s P2P Device Discoverability Response Frame\n", + (tx) ? "TX" : "RX"); + break; + case P2P_PAF_PROVDIS_REQ: + brcmf_dbg(TRACE, "%s P2P Provision Discovery Request Frame\n", + (tx) ? "TX" : "RX"); + break; + case P2P_PAF_PROVDIS_RSP: + brcmf_dbg(TRACE, "%s P2P Provision Discovery Response Frame\n", + (tx) ? "TX" : "RX"); + break; + default: + brcmf_dbg(TRACE, "%s Unknown P2P Public Action Frame\n", + (tx) ? "TX" : "RX"); + break; + } + } else if (brcmf_p2p_is_p2p_action(frame, frame_len)) { + act_frm = (struct brcmf_p2p_action_frame *)frame; + switch (act_frm->subtype) { + case P2P_AF_NOTICE_OF_ABSENCE: + brcmf_dbg(TRACE, "%s P2P Notice of Absence Frame\n", + (tx) ? "TX" : "RX"); + break; + case P2P_AF_PRESENCE_REQ: + brcmf_dbg(TRACE, "%s P2P Presence Request Frame\n", + (tx) ? "TX" : "RX"); + break; + case P2P_AF_PRESENCE_RSP: + brcmf_dbg(TRACE, "%s P2P Presence Response Frame\n", + (tx) ? "TX" : "RX"); + break; + case P2P_AF_GO_DISC_REQ: + brcmf_dbg(TRACE, "%s P2P Discoverability Request Frame\n", + (tx) ? "TX" : "RX"); + break; + default: + brcmf_dbg(TRACE, "%s Unknown P2P Action Frame\n", + (tx) ? "TX" : "RX"); + } + + } else if (brcmf_p2p_is_gas_action(frame, frame_len)) { + sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame; + switch (sd_act_frm->action) { + case P2PSD_ACTION_ID_GAS_IREQ: + brcmf_dbg(TRACE, "%s P2P GAS Initial Request\n", + (tx) ? "TX" : "RX"); + break; + case P2PSD_ACTION_ID_GAS_IRESP: + brcmf_dbg(TRACE, "%s P2P GAS Initial Response\n", + (tx) ? "TX" : "RX"); + break; + case P2PSD_ACTION_ID_GAS_CREQ: + brcmf_dbg(TRACE, "%s P2P GAS Comback Request\n", + (tx) ? "TX" : "RX"); + break; + case P2PSD_ACTION_ID_GAS_CRESP: + brcmf_dbg(TRACE, "%s P2P GAS Comback Response\n", + (tx) ? "TX" : "RX"); + break; + default: + brcmf_dbg(TRACE, "%s Unknown P2P GAS Frame\n", + (tx) ? "TX" : "RX"); + break; + } + } +} + +#else + +static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len) +{ +} + +#endif + + +/** + * brcmf_p2p_set_firmware() - prepare firmware for peer-to-peer operation. + * + * @ifp: ifp to use for iovars (primary). + * @p2p_mac: mac address to configure for p2p_da_override + */ +static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac) +{ + s32 ret = 0; + + brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + brcmf_fil_iovar_int_set(ifp, "apsta", 1); + brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); + + /* In case of COB type, firmware has default mac address + * After Initializing firmware, we have to set current mac address to + * firmware for P2P device address. This must be done with discovery + * disabled. + */ + brcmf_fil_iovar_int_set(ifp, "p2p_disc", 0); + + ret = brcmf_fil_iovar_data_set(ifp, "p2p_da_override", p2p_mac, + ETH_ALEN); + if (ret) + brcmf_err("failed to update device address ret %d\n", ret); + + return ret; +} + +/** + * brcmf_p2p_generate_bss_mac() - derive mac addresses for P2P. + * + * @p2p: P2P specific data. + * @dev_addr: optional device address. + * + * P2P needs mac addresses for P2P device and interface. If no device + * address it specified, these are derived from the primary net device, ie. + * the permanent ethernet address of the device. + */ +static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr) +{ + struct brcmf_if *pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; + bool local_admin = false; + + if (!dev_addr || is_zero_ether_addr(dev_addr)) { + dev_addr = pri_ifp->mac_addr; + local_admin = true; + } + + /* Generate the P2P Device Address. This consists of the device's + * primary MAC address with the locally administered bit set. + */ + memcpy(p2p->dev_addr, dev_addr, ETH_ALEN); + if (local_admin) + p2p->dev_addr[0] |= 0x02; + + /* Generate the P2P Interface Address. If the discovery and connection + * BSSCFGs need to simultaneously co-exist, then this address must be + * different from the P2P Device Address, but also locally administered. + */ + memcpy(p2p->int_addr, p2p->dev_addr, ETH_ALEN); + p2p->int_addr[0] |= 0x02; + p2p->int_addr[4] ^= 0x80; +} + +/** + * brcmf_p2p_scan_is_p2p_request() - is cfg80211 scan request a P2P scan. + * + * @request: the scan request as received from cfg80211. + * + * returns true if one of the ssids in the request matches the + * P2P wildcard ssid; otherwise returns false. + */ +static bool brcmf_p2p_scan_is_p2p_request(struct cfg80211_scan_request *request) +{ + struct cfg80211_ssid *ssids = request->ssids; + int i; + + for (i = 0; i < request->n_ssids; i++) { + if (ssids[i].ssid_len != BRCMF_P2P_WILDCARD_SSID_LEN) + continue; + + brcmf_dbg(INFO, "comparing ssid \"%s\"", ssids[i].ssid); + if (!memcmp(BRCMF_P2P_WILDCARD_SSID, ssids[i].ssid, + BRCMF_P2P_WILDCARD_SSID_LEN)) + return true; + } + return false; +} + +/** + * brcmf_p2p_set_discover_state - set discover state in firmware. + * + * @ifp: low-level interface object. + * @state: discover state to set. + * @chanspec: channel parameters (for state @WL_P2P_DISC_ST_LISTEN only). + * @listen_ms: duration to listen (for state @WL_P2P_DISC_ST_LISTEN only). + */ +static s32 brcmf_p2p_set_discover_state(struct brcmf_if *ifp, u8 state, + u16 chanspec, u16 listen_ms) +{ + struct brcmf_p2p_disc_st_le discover_state; + s32 ret = 0; + brcmf_dbg(TRACE, "enter\n"); + + discover_state.state = state; + discover_state.chspec = cpu_to_le16(chanspec); + discover_state.dwell = cpu_to_le16(listen_ms); + ret = brcmf_fil_bsscfg_data_set(ifp, "p2p_state", &discover_state, + sizeof(discover_state)); + return ret; +} + +/** + * brcmf_p2p_deinit_discovery() - disable P2P device discovery. + * + * @p2p: P2P specific data. + * + * Resets the discovery state and disables it in firmware. + */ +static s32 brcmf_p2p_deinit_discovery(struct brcmf_p2p_info *p2p) +{ + struct brcmf_cfg80211_vif *vif; + + brcmf_dbg(TRACE, "enter\n"); + + /* Set the discovery state to SCAN */ + vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + (void)brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0); + + /* Disable P2P discovery in the firmware */ + vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; + (void)brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 0); + + return 0; +} + +/** + * brcmf_p2p_enable_discovery() - initialize and configure discovery. + * + * @p2p: P2P specific data. + * + * Initializes the discovery device and configure the virtual interface. + */ +static int brcmf_p2p_enable_discovery(struct brcmf_p2p_info *p2p) +{ + struct brcmf_cfg80211_vif *vif; + s32 ret = 0; + + brcmf_dbg(TRACE, "enter\n"); + vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + if (!vif) { + brcmf_err("P2P config device not available\n"); + ret = -EPERM; + goto exit; + } + + if (test_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status)) { + brcmf_dbg(INFO, "P2P config device already configured\n"); + goto exit; + } + + /* Re-initialize P2P Discovery in the firmware */ + vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; + ret = brcmf_fil_iovar_int_set(vif->ifp, "p2p_disc", 1); + if (ret < 0) { + brcmf_err("set p2p_disc error\n"); + goto exit; + } + vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + ret = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_SCAN, 0, 0); + if (ret < 0) { + brcmf_err("unable to set WL_P2P_DISC_ST_SCAN\n"); + goto exit; + } + + /* + * Set wsec to any non-zero value in the discovery bsscfg + * to ensure our P2P probe responses have the privacy bit + * set in the 802.11 WPA IE. Some peer devices may not + * initiate WPS with us if this bit is not set. + */ + ret = brcmf_fil_bsscfg_int_set(vif->ifp, "wsec", AES_ENABLED); + if (ret < 0) { + brcmf_err("wsec error %d\n", ret); + goto exit; + } + + set_bit(BRCMF_P2P_STATUS_ENABLED, &p2p->status); +exit: + return ret; +} + +/** + * brcmf_p2p_escan() - initiate a P2P scan. + * + * @p2p: P2P specific data. + * @num_chans: number of channels to scan. + * @chanspecs: channel parameters for @num_chans channels. + * @search_state: P2P discover state to use. + * @action: scan action to pass to firmware. + * @bss_type: type of P2P bss. + */ +static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans, + u16 chanspecs[], s32 search_state, u16 action, + enum p2p_bss_type bss_type) +{ + s32 ret = 0; + s32 memsize = offsetof(struct brcmf_p2p_scan_le, + eparams.params_le.channel_list); + s32 nprobes; + s32 active; + u32 i; + u8 *memblk; + struct brcmf_cfg80211_vif *vif; + struct brcmf_p2p_scan_le *p2p_params; + struct brcmf_scan_params_le *sparams; + struct brcmf_ssid ssid; + + memsize += num_chans * sizeof(__le16); + memblk = kzalloc(memsize, GFP_KERNEL); + if (!memblk) + return -ENOMEM; + + vif = p2p->bss_idx[bss_type].vif; + if (vif == NULL) { + brcmf_err("no vif for bss type %d\n", bss_type); + ret = -EINVAL; + goto exit; + } + + switch (search_state) { + case WL_P2P_DISC_ST_SEARCH: + /* + * If we in SEARCH STATE, we don't need to set SSID explictly + * because dongle use P2P WILDCARD internally by default + */ + /* use null ssid */ + ssid.SSID_len = 0; + memset(ssid.SSID, 0, sizeof(ssid.SSID)); + break; + case WL_P2P_DISC_ST_SCAN: + /* + * wpa_supplicant has p2p_find command with type social or + * progressive. For progressive, we need to set the ssid to + * P2P WILDCARD because we just do broadcast scan unless + * setting SSID. + */ + ssid.SSID_len = BRCMF_P2P_WILDCARD_SSID_LEN; + memcpy(ssid.SSID, BRCMF_P2P_WILDCARD_SSID, ssid.SSID_len); + break; + default: + brcmf_err(" invalid search state %d\n", search_state); + ret = -EINVAL; + goto exit; + } + + brcmf_p2p_set_discover_state(vif->ifp, search_state, 0, 0); + + /* + * set p2p scan parameters. + */ + p2p_params = (struct brcmf_p2p_scan_le *)memblk; + p2p_params->type = 'E'; + + /* determine the scan engine parameters */ + sparams = &p2p_params->eparams.params_le; + sparams->bss_type = DOT11_BSSTYPE_ANY; + if (p2p->cfg->active_scan) + sparams->scan_type = 0; + else + sparams->scan_type = 1; + + eth_broadcast_addr(sparams->bssid); + if (ssid.SSID_len) + memcpy(sparams->ssid_le.SSID, ssid.SSID, ssid.SSID_len); + sparams->ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len); + sparams->home_time = cpu_to_le32(P2PAPI_SCAN_HOME_TIME_MS); + + /* + * SOCIAL_CHAN_CNT + 1 takes care of the Progressive scan + * supported by the supplicant. + */ + if (num_chans == SOCIAL_CHAN_CNT || num_chans == (SOCIAL_CHAN_CNT + 1)) + active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS; + else if (num_chans == AF_PEER_SEARCH_CNT) + active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS; + else if (brcmf_get_vif_state_any(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED)) + active = -1; + else + active = P2PAPI_SCAN_DWELL_TIME_MS; + + /* Override scan params to find a peer for a connection */ + if (num_chans == 1) { + active = WL_SCAN_CONNECT_DWELL_TIME_MS; + /* WAR to sync with presence period of VSDB GO. + * send probe request more frequently + */ + nprobes = active / WL_SCAN_JOIN_PROBE_INTERVAL_MS; + } else { + nprobes = active / P2PAPI_SCAN_NPROBS_TIME_MS; + } + + if (nprobes <= 0) + nprobes = 1; + + brcmf_dbg(INFO, "nprobes # %d, active_time %d\n", nprobes, active); + sparams->active_time = cpu_to_le32(active); + sparams->nprobes = cpu_to_le32(nprobes); + sparams->passive_time = cpu_to_le32(-1); + sparams->channel_num = cpu_to_le32(num_chans & + BRCMF_SCAN_PARAMS_COUNT_MASK); + for (i = 0; i < num_chans; i++) + sparams->channel_list[i] = cpu_to_le16(chanspecs[i]); + + /* set the escan specific parameters */ + p2p_params->eparams.version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); + p2p_params->eparams.action = cpu_to_le16(action); + p2p_params->eparams.sync_id = cpu_to_le16(0x1234); + /* perform p2p scan on primary device */ + ret = brcmf_fil_bsscfg_data_set(vif->ifp, "p2p_scan", memblk, memsize); + if (!ret) + set_bit(BRCMF_SCAN_STATUS_BUSY, &p2p->cfg->scan_status); +exit: + kfree(memblk); + return ret; +} + +/** + * brcmf_p2p_run_escan() - escan callback for peer-to-peer. + * + * @cfg: driver private data for cfg80211 interface. + * @ndev: net device for which scan is requested. + * @request: scan request from cfg80211. + * @action: scan action. + * + * Determines the P2P discovery state based to scan request parameters and + * validates the channels in the request. + */ +static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp, + struct cfg80211_scan_request *request, + u16 action) +{ + struct brcmf_p2p_info *p2p = &cfg->p2p; + s32 err = 0; + s32 search_state = WL_P2P_DISC_ST_SCAN; + struct brcmf_cfg80211_vif *vif; + struct net_device *dev = NULL; + int i, num_nodfs = 0; + u16 *chanspecs; + + brcmf_dbg(TRACE, "enter\n"); + + if (!request) { + err = -EINVAL; + goto exit; + } + + if (request->n_channels) { + chanspecs = kcalloc(request->n_channels, sizeof(*chanspecs), + GFP_KERNEL); + if (!chanspecs) { + err = -ENOMEM; + goto exit; + } + vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif; + if (vif) + dev = vif->wdev.netdev; + if (request->n_channels == 3 && + request->channels[0]->hw_value == SOCIAL_CHAN_1 && + request->channels[1]->hw_value == SOCIAL_CHAN_2 && + request->channels[2]->hw_value == SOCIAL_CHAN_3) { + /* SOCIAL CHANNELS 1, 6, 11 */ + search_state = WL_P2P_DISC_ST_SEARCH; + brcmf_dbg(INFO, "P2P SEARCH PHASE START\n"); + } else if (dev != NULL && + vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) { + /* If you are already a GO, then do SEARCH only */ + brcmf_dbg(INFO, "Already a GO. Do SEARCH Only\n"); + search_state = WL_P2P_DISC_ST_SEARCH; + } else { + brcmf_dbg(INFO, "P2P SCAN STATE START\n"); + } + + /* + * no P2P scanning on passive or DFS channels. + */ + for (i = 0; i < request->n_channels; i++) { + struct ieee80211_channel *chan = request->channels[i]; + + if (chan->flags & (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR)) + continue; + + chanspecs[i] = channel_to_chanspec(&p2p->cfg->d11inf, + chan); + brcmf_dbg(INFO, "%d: chan=%d, channel spec=%x\n", + num_nodfs, chan->hw_value, chanspecs[i]); + num_nodfs++; + } + err = brcmf_p2p_escan(p2p, num_nodfs, chanspecs, search_state, + action, P2PAPI_BSSCFG_DEVICE); + kfree(chanspecs); + } +exit: + if (err) + brcmf_err("error (%d)\n", err); + return err; +} + + +/** + * brcmf_p2p_find_listen_channel() - find listen channel in ie string. + * + * @ie: string of information elements. + * @ie_len: length of string. + * + * Scan ie for p2p ie and look for attribute 6 channel. If available determine + * channel and return it. + */ +static s32 brcmf_p2p_find_listen_channel(const u8 *ie, u32 ie_len) +{ + u8 channel_ie[5]; + s32 listen_channel; + s32 err; + + err = cfg80211_get_p2p_attr(ie, ie_len, + IEEE80211_P2P_ATTR_LISTEN_CHANNEL, + channel_ie, sizeof(channel_ie)); + if (err < 0) + return err; + + /* listen channel subel length format: */ + /* 3(country) + 1(op. class) + 1(chan num) */ + listen_channel = (s32)channel_ie[3 + 1]; + + if (listen_channel == SOCIAL_CHAN_1 || + listen_channel == SOCIAL_CHAN_2 || + listen_channel == SOCIAL_CHAN_3) { + brcmf_dbg(INFO, "Found my Listen Channel %d\n", listen_channel); + return listen_channel; + } + + return -EPERM; +} + + +/** + * brcmf_p2p_scan_prep() - prepare scan based on request. + * + * @wiphy: wiphy device. + * @request: scan request from cfg80211. + * @vif: vif on which scan request is to be executed. + * + * Prepare the scan appropriately for type of scan requested. Overrides the + * escan .run() callback for peer-to-peer scanning. + */ +int brcmf_p2p_scan_prep(struct wiphy *wiphy, + struct cfg80211_scan_request *request, + struct brcmf_cfg80211_vif *vif) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_p2p_info *p2p = &cfg->p2p; + int err = 0; + + if (brcmf_p2p_scan_is_p2p_request(request)) { + /* find my listen channel */ + err = brcmf_p2p_find_listen_channel(request->ie, + request->ie_len); + if (err < 0) + return err; + + p2p->afx_hdl.my_listen_chan = err; + + clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); + brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n"); + + err = brcmf_p2p_enable_discovery(p2p); + if (err) + return err; + + vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + + /* override .run_escan() callback. */ + cfg->escan_info.run = brcmf_p2p_run_escan; + } + err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG, + request->ie, request->ie_len); + return err; +} + + +/** + * brcmf_p2p_discover_listen() - set firmware to discover listen state. + * + * @p2p: p2p device. + * @channel: channel nr for discover listen. + * @duration: time in ms to stay on channel. + * + */ +static s32 +brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration) +{ + struct brcmf_cfg80211_vif *vif; + struct brcmu_chan ch; + s32 err = 0; + + vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + if (!vif) { + brcmf_err("Discovery is not set, so we have nothing to do\n"); + err = -EPERM; + goto exit; + } + + if (test_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status)) { + brcmf_err("Previous LISTEN is not completed yet\n"); + /* WAR: prevent cookie mismatch in wpa_supplicant return OK */ + goto exit; + } + + ch.chnum = channel; + ch.bw = BRCMU_CHAN_BW_20; + p2p->cfg->d11inf.encchspec(&ch); + err = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_LISTEN, + ch.chspec, (u16)duration); + if (!err) { + set_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status); + p2p->remain_on_channel_cookie++; + } +exit: + return err; +} + + +/** + * brcmf_p2p_remain_on_channel() - put device on channel and stay there. + * + * @wiphy: wiphy device. + * @channel: channel to stay on. + * @duration: time in ms to remain on channel. + * + */ +int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, + struct ieee80211_channel *channel, + unsigned int duration, u64 *cookie) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_p2p_info *p2p = &cfg->p2p; + s32 err; + u16 channel_nr; + + channel_nr = ieee80211_frequency_to_channel(channel->center_freq); + brcmf_dbg(TRACE, "Enter, channel: %d, duration ms (%d)\n", channel_nr, + duration); + + err = brcmf_p2p_enable_discovery(p2p); + if (err) + goto exit; + err = brcmf_p2p_discover_listen(p2p, channel_nr, duration); + if (err) + goto exit; + + memcpy(&p2p->remain_on_channel, channel, sizeof(*channel)); + *cookie = p2p->remain_on_channel_cookie; + cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL); + +exit: + return err; +} + + +/** + * brcmf_p2p_notify_listen_complete() - p2p listen has completed. + * + * @ifp: interfac control. + * @e: event message. Not used, to make it usable for fweh event dispatcher. + * @data: payload of message. Not used. + * + */ +int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_p2p_info *p2p = &cfg->p2p; + + brcmf_dbg(TRACE, "Enter\n"); + if (test_and_clear_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, + &p2p->status)) { + if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN, + &p2p->status)) { + clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, + &p2p->status); + brcmf_dbg(INFO, "Listen DONE, wake up wait_next_af\n"); + complete(&p2p->wait_next_af); + } + + cfg80211_remain_on_channel_expired(&ifp->vif->wdev, + p2p->remain_on_channel_cookie, + &p2p->remain_on_channel, + GFP_KERNEL); + } + return 0; +} + + +/** + * brcmf_p2p_cancel_remain_on_channel() - cancel p2p listen state. + * + * @ifp: interfac control. + * + */ +void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp) +{ + if (!ifp) + return; + brcmf_p2p_set_discover_state(ifp, WL_P2P_DISC_ST_SCAN, 0, 0); + brcmf_p2p_notify_listen_complete(ifp, NULL, NULL); +} + + +/** + * brcmf_p2p_act_frm_search() - search function for action frame. + * + * @p2p: p2p device. + * channel: channel on which action frame is to be trasmitted. + * + * search function to reach at common channel to send action frame. When + * channel is 0 then all social channels will be used to send af + */ +static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel) +{ + s32 err; + u32 channel_cnt; + u16 *default_chan_list; + u32 i; + struct brcmu_chan ch; + + brcmf_dbg(TRACE, "Enter\n"); + + if (channel) + channel_cnt = AF_PEER_SEARCH_CNT; + else + channel_cnt = SOCIAL_CHAN_CNT; + default_chan_list = kzalloc(channel_cnt * sizeof(*default_chan_list), + GFP_KERNEL); + if (default_chan_list == NULL) { + brcmf_err("channel list allocation failed\n"); + err = -ENOMEM; + goto exit; + } + ch.bw = BRCMU_CHAN_BW_20; + if (channel) { + ch.chnum = channel; + p2p->cfg->d11inf.encchspec(&ch); + /* insert same channel to the chan_list */ + for (i = 0; i < channel_cnt; i++) + default_chan_list[i] = ch.chspec; + } else { + ch.chnum = SOCIAL_CHAN_1; + p2p->cfg->d11inf.encchspec(&ch); + default_chan_list[0] = ch.chspec; + ch.chnum = SOCIAL_CHAN_2; + p2p->cfg->d11inf.encchspec(&ch); + default_chan_list[1] = ch.chspec; + ch.chnum = SOCIAL_CHAN_3; + p2p->cfg->d11inf.encchspec(&ch); + default_chan_list[2] = ch.chspec; + } + err = brcmf_p2p_escan(p2p, channel_cnt, default_chan_list, + WL_P2P_DISC_ST_SEARCH, WL_ESCAN_ACTION_START, + P2PAPI_BSSCFG_DEVICE); + kfree(default_chan_list); +exit: + return err; +} + + +/** + * brcmf_p2p_afx_handler() - afx worker thread. + * + * @work: + * + */ +static void brcmf_p2p_afx_handler(struct work_struct *work) +{ + struct afx_hdl *afx_hdl = container_of(work, struct afx_hdl, afx_work); + struct brcmf_p2p_info *p2p = container_of(afx_hdl, + struct brcmf_p2p_info, + afx_hdl); + s32 err; + + if (!afx_hdl->is_active) + return; + + if (afx_hdl->is_listen && afx_hdl->my_listen_chan) + /* 100ms ~ 300ms */ + err = brcmf_p2p_discover_listen(p2p, afx_hdl->my_listen_chan, + 100 * (1 + prandom_u32() % 3)); + else + err = brcmf_p2p_act_frm_search(p2p, afx_hdl->peer_listen_chan); + + if (err) { + brcmf_err("ERROR occurred! value is (%d)\n", err); + if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, + &p2p->status)) + complete(&afx_hdl->act_frm_scan); + } +} + + +/** + * brcmf_p2p_af_searching_channel() - search channel. + * + * @p2p: p2p device info struct. + * + */ +static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) +{ + struct afx_hdl *afx_hdl = &p2p->afx_hdl; + struct brcmf_cfg80211_vif *pri_vif; + unsigned long duration; + s32 retry; + + brcmf_dbg(TRACE, "Enter\n"); + + pri_vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; + + reinit_completion(&afx_hdl->act_frm_scan); + set_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status); + afx_hdl->is_active = true; + afx_hdl->peer_chan = P2P_INVALID_CHANNEL; + + /* Loop to wait until we find a peer's channel or the + * pending action frame tx is cancelled. + */ + retry = 0; + duration = msecs_to_jiffies(P2P_AF_FRM_SCAN_MAX_WAIT); + while ((retry < P2P_CHANNEL_SYNC_RETRY) && + (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)) { + afx_hdl->is_listen = false; + brcmf_dbg(TRACE, "Scheduling action frame for sending.. (%d)\n", + retry); + /* search peer on peer's listen channel */ + schedule_work(&afx_hdl->afx_work); + wait_for_completion_timeout(&afx_hdl->act_frm_scan, duration); + if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) || + (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, + &p2p->status))) + break; + + if (afx_hdl->my_listen_chan) { + brcmf_dbg(TRACE, "Scheduling listen peer, channel=%d\n", + afx_hdl->my_listen_chan); + /* listen on my listen channel */ + afx_hdl->is_listen = true; + schedule_work(&afx_hdl->afx_work); + wait_for_completion_timeout(&afx_hdl->act_frm_scan, + duration); + } + if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) || + (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, + &p2p->status))) + break; + retry++; + + /* if sta is connected or connecting, sleep for a while before + * retry af tx or finding a peer + */ + if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &pri_vif->sme_state) || + test_bit(BRCMF_VIF_STATUS_CONNECTING, &pri_vif->sme_state)) + msleep(P2P_DEFAULT_SLEEP_TIME_VSDB); + } + + brcmf_dbg(TRACE, "Completed search/listen peer_chan=%d\n", + afx_hdl->peer_chan); + afx_hdl->is_active = false; + + clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status); + + return afx_hdl->peer_chan; +} + + +/** + * brcmf_p2p_scan_finding_common_channel() - was escan used for finding channel + * + * @cfg: common configuration struct. + * @bi: bss info struct, result from scan. + * + */ +bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, + struct brcmf_bss_info_le *bi) + +{ + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct afx_hdl *afx_hdl = &p2p->afx_hdl; + struct brcmu_chan ch; + u8 *ie; + s32 err; + u8 p2p_dev_addr[ETH_ALEN]; + + if (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status)) + return false; + + if (bi == NULL) { + brcmf_dbg(TRACE, "ACTION FRAME SCAN Done\n"); + if (afx_hdl->peer_chan == P2P_INVALID_CHANNEL) + complete(&afx_hdl->act_frm_scan); + return true; + } + + ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset); + memset(p2p_dev_addr, 0, sizeof(p2p_dev_addr)); + err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length), + IEEE80211_P2P_ATTR_DEVICE_INFO, + p2p_dev_addr, sizeof(p2p_dev_addr)); + if (err < 0) + err = cfg80211_get_p2p_attr(ie, le32_to_cpu(bi->ie_length), + IEEE80211_P2P_ATTR_DEVICE_ID, + p2p_dev_addr, sizeof(p2p_dev_addr)); + if ((err >= 0) && + (ether_addr_equal(p2p_dev_addr, afx_hdl->tx_dst_addr))) { + if (!bi->ctl_ch) { + ch.chspec = le16_to_cpu(bi->chanspec); + cfg->d11inf.decchspec(&ch); + bi->ctl_ch = ch.chnum; + } + afx_hdl->peer_chan = bi->ctl_ch; + brcmf_dbg(TRACE, "ACTION FRAME SCAN : Peer %pM found, channel : %d\n", + afx_hdl->tx_dst_addr, afx_hdl->peer_chan); + complete(&afx_hdl->act_frm_scan); + } + return true; +} + +/** + * brcmf_p2p_stop_wait_next_action_frame() - finish scan if af tx complete. + * + * @cfg: common configuration struct. + * + */ +static void +brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct brcmf_if *ifp = cfg->escan_info.ifp; + + if (test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status) && + (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status) || + test_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status))) { + brcmf_dbg(TRACE, "*** Wake UP ** abort actframe iovar\n"); + /* if channel is not zero, "actfame" uses off channel scan. + * So abort scan for off channel completion. + */ + if (p2p->af_sent_channel) + brcmf_notify_escan_complete(cfg, ifp, true, true); + } else if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN, + &p2p->status)) { + brcmf_dbg(TRACE, "*** Wake UP ** abort listen for next af frame\n"); + /* So abort scan to cancel listen */ + brcmf_notify_escan_complete(cfg, ifp, true, true); + } +} + + +/** + * brcmf_p2p_gon_req_collision() - Check if go negotiaton collission + * + * @p2p: p2p device info struct. + * + * return true if recevied action frame is to be dropped. + */ +static bool +brcmf_p2p_gon_req_collision(struct brcmf_p2p_info *p2p, u8 *mac) +{ + struct brcmf_cfg80211_info *cfg = p2p->cfg; + struct brcmf_if *ifp; + + brcmf_dbg(TRACE, "Enter\n"); + + if (!test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) || + !p2p->gon_req_action) + return false; + + brcmf_dbg(TRACE, "GO Negotiation Request COLLISION !!!\n"); + /* if sa(peer) addr is less than da(my) addr, then this device + * process peer's gon request and block to send gon req. + * if not (sa addr > da addr), + * this device will process gon request and drop gon req of peer. + */ + ifp = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->ifp; + if (memcmp(mac, ifp->mac_addr, ETH_ALEN) < 0) { + brcmf_dbg(INFO, "Block transmit gon req !!!\n"); + p2p->block_gon_req_tx = true; + /* if we are finding a common channel for sending af, + * do not scan more to block to send current gon req + */ + if (test_and_clear_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, + &p2p->status)) + complete(&p2p->afx_hdl.act_frm_scan); + if (test_and_clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, + &p2p->status)) + brcmf_p2p_stop_wait_next_action_frame(cfg); + return false; + } + + /* drop gon request of peer to process gon request by this device. */ + brcmf_dbg(INFO, "Drop received gon req !!!\n"); + + return true; +} + + +/** + * brcmf_p2p_notify_action_frame_rx() - received action frame. + * + * @ifp: interfac control. + * @e: event message. Not used, to make it usable for fweh event dispatcher. + * @data: payload of message, containing action frame data. + * + */ +int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct afx_hdl *afx_hdl = &p2p->afx_hdl; + struct wireless_dev *wdev; + u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data); + struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data; + u8 *frame = (u8 *)(rxframe + 1); + struct brcmf_p2p_pub_act_frame *act_frm; + struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm; + struct brcmu_chan ch; + struct ieee80211_mgmt *mgmt_frame; + s32 freq; + u16 mgmt_type; + u8 action; + + ch.chspec = be16_to_cpu(rxframe->chanspec); + cfg->d11inf.decchspec(&ch); + /* Check if wpa_supplicant has registered for this frame */ + brcmf_dbg(INFO, "ifp->vif->mgmt_rx_reg %04x\n", ifp->vif->mgmt_rx_reg); + mgmt_type = (IEEE80211_STYPE_ACTION & IEEE80211_FCTL_STYPE) >> 4; + if ((ifp->vif->mgmt_rx_reg & BIT(mgmt_type)) == 0) + return 0; + + brcmf_p2p_print_actframe(false, frame, mgmt_frame_len); + + action = P2P_PAF_SUBTYPE_INVALID; + if (brcmf_p2p_is_pub_action(frame, mgmt_frame_len)) { + act_frm = (struct brcmf_p2p_pub_act_frame *)frame; + action = act_frm->subtype; + if ((action == P2P_PAF_GON_REQ) && + (brcmf_p2p_gon_req_collision(p2p, (u8 *)e->addr))) { + if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, + &p2p->status) && + (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) { + afx_hdl->peer_chan = ch.chnum; + brcmf_dbg(INFO, "GON request: Peer found, channel=%d\n", + afx_hdl->peer_chan); + complete(&afx_hdl->act_frm_scan); + } + return 0; + } + /* After complete GO Negotiation, roll back to mpc mode */ + if ((action == P2P_PAF_GON_CONF) || + (action == P2P_PAF_PROVDIS_RSP)) + brcmf_set_mpc(ifp, 1); + if (action == P2P_PAF_GON_CONF) { + brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n"); + clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); + } + } else if (brcmf_p2p_is_gas_action(frame, mgmt_frame_len)) { + sd_act_frm = (struct brcmf_p2psd_gas_pub_act_frame *)frame; + action = sd_act_frm->action; + } + + if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) && + (p2p->next_af_subtype == action)) { + brcmf_dbg(TRACE, "We got a right next frame! (%d)\n", action); + clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, + &p2p->status); + /* Stop waiting for next AF. */ + brcmf_p2p_stop_wait_next_action_frame(cfg); + } + + mgmt_frame = kzalloc(offsetof(struct ieee80211_mgmt, u) + + mgmt_frame_len, GFP_KERNEL); + if (!mgmt_frame) { + brcmf_err("No memory available for action frame\n"); + return -ENOMEM; + } + memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN); + brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid, + ETH_ALEN); + memcpy(mgmt_frame->sa, e->addr, ETH_ALEN); + mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_ACTION); + memcpy(&mgmt_frame->u, frame, mgmt_frame_len); + mgmt_frame_len += offsetof(struct ieee80211_mgmt, u); + + freq = ieee80211_channel_to_frequency(ch.chnum, + ch.band == BRCMU_CHAN_BAND_2G ? + IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ); + + wdev = &ifp->vif->wdev; + cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0); + + kfree(mgmt_frame); + return 0; +} + + +/** + * brcmf_p2p_notify_action_tx_complete() - transmit action frame complete + * + * @ifp: interfac control. + * @e: event message. Not used, to make it usable for fweh event dispatcher. + * @data: not used. + * + */ +int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_p2p_info *p2p = &cfg->p2p; + + brcmf_dbg(INFO, "Enter: event %s, status=%d\n", + e->event_code == BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE ? + "ACTION_FRAME_OFF_CHAN_COMPLETE" : "ACTION_FRAME_COMPLETE", + e->status); + + if (!test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status)) + return 0; + + if (e->event_code == BRCMF_E_ACTION_FRAME_COMPLETE) { + if (e->status == BRCMF_E_STATUS_SUCCESS) + set_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, + &p2p->status); + else { + set_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status); + /* If there is no ack, we don't need to wait for + * WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE event + */ + brcmf_p2p_stop_wait_next_action_frame(cfg); + } + + } else { + complete(&p2p->send_af_done); + } + return 0; +} + + +/** + * brcmf_p2p_tx_action_frame() - send action frame over fil. + * + * @p2p: p2p info struct for vif. + * @af_params: action frame data/info. + * + * Send an action frame immediately without doing channel synchronization. + * + * This function waits for a completion event before returning. + * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action + * frame is transmitted. + */ +static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, + struct brcmf_fil_af_params_le *af_params) +{ + struct brcmf_cfg80211_vif *vif; + s32 err = 0; + s32 timeout = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + reinit_completion(&p2p->send_af_done); + clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status); + clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status); + + vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params, + sizeof(*af_params)); + if (err) { + brcmf_err(" sending action frame has failed\n"); + goto exit; + } + + p2p->af_sent_channel = le32_to_cpu(af_params->channel); + p2p->af_tx_sent_jiffies = jiffies; + + timeout = wait_for_completion_timeout(&p2p->send_af_done, + msecs_to_jiffies(P2P_AF_MAX_WAIT_TIME)); + + if (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status)) { + brcmf_dbg(TRACE, "TX action frame operation is success\n"); + } else { + err = -EIO; + brcmf_dbg(TRACE, "TX action frame operation has failed\n"); + } + /* clear status bit for action tx */ + clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status); + clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status); + +exit: + return err; +} + + +/** + * brcmf_p2p_pub_af_tx() - public action frame tx routine. + * + * @cfg: driver private data for cfg80211 interface. + * @af_params: action frame data/info. + * @config_af_params: configuration data for action frame. + * + * routine which transmits ation frame public type. + */ +static s32 brcmf_p2p_pub_af_tx(struct brcmf_cfg80211_info *cfg, + struct brcmf_fil_af_params_le *af_params, + struct brcmf_config_af_params *config_af_params) +{ + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct brcmf_fil_action_frame_le *action_frame; + struct brcmf_p2p_pub_act_frame *act_frm; + s32 err = 0; + u16 ie_len; + + action_frame = &af_params->action_frame; + act_frm = (struct brcmf_p2p_pub_act_frame *)(action_frame->data); + + config_af_params->extra_listen = true; + + switch (act_frm->subtype) { + case P2P_PAF_GON_REQ: + brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status set\n"); + set_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); + config_af_params->mpc_onoff = 0; + config_af_params->search_channel = true; + p2p->next_af_subtype = act_frm->subtype + 1; + p2p->gon_req_action = true; + /* increase dwell time to wait for RESP frame */ + af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME); + break; + case P2P_PAF_GON_RSP: + p2p->next_af_subtype = act_frm->subtype + 1; + /* increase dwell time to wait for CONF frame */ + af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME); + break; + case P2P_PAF_GON_CONF: + /* If we reached till GO Neg confirmation reset the filter */ + brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n"); + clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); + /* turn on mpc again if go nego is done */ + config_af_params->mpc_onoff = 1; + /* minimize dwell time */ + af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME); + config_af_params->extra_listen = false; + break; + case P2P_PAF_INVITE_REQ: + config_af_params->search_channel = true; + p2p->next_af_subtype = act_frm->subtype + 1; + /* increase dwell time */ + af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME); + break; + case P2P_PAF_INVITE_RSP: + /* minimize dwell time */ + af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME); + config_af_params->extra_listen = false; + break; + case P2P_PAF_DEVDIS_REQ: + config_af_params->search_channel = true; + p2p->next_af_subtype = act_frm->subtype + 1; + /* maximize dwell time to wait for RESP frame */ + af_params->dwell_time = cpu_to_le32(P2P_AF_LONG_DWELL_TIME); + break; + case P2P_PAF_DEVDIS_RSP: + /* minimize dwell time */ + af_params->dwell_time = cpu_to_le32(P2P_AF_MIN_DWELL_TIME); + config_af_params->extra_listen = false; + break; + case P2P_PAF_PROVDIS_REQ: + ie_len = le16_to_cpu(action_frame->len) - + offsetof(struct brcmf_p2p_pub_act_frame, elts); + if (cfg80211_get_p2p_attr(&act_frm->elts[0], ie_len, + IEEE80211_P2P_ATTR_GROUP_ID, + NULL, 0) < 0) + config_af_params->search_channel = true; + config_af_params->mpc_onoff = 0; + p2p->next_af_subtype = act_frm->subtype + 1; + /* increase dwell time to wait for RESP frame */ + af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME); + break; + case P2P_PAF_PROVDIS_RSP: + /* wpa_supplicant send go nego req right after prov disc */ + p2p->next_af_subtype = P2P_PAF_GON_REQ; + /* increase dwell time to MED level */ + af_params->dwell_time = cpu_to_le32(P2P_AF_MED_DWELL_TIME); + config_af_params->extra_listen = false; + break; + default: + brcmf_err("Unknown p2p pub act frame subtype: %d\n", + act_frm->subtype); + err = -EINVAL; + } + return err; +} + +/** + * brcmf_p2p_send_action_frame() - send action frame . + * + * @cfg: driver private data for cfg80211 interface. + * @ndev: net device to transmit on. + * @af_params: configuration data for action frame. + */ +bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, + struct net_device *ndev, + struct brcmf_fil_af_params_le *af_params) +{ + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_fil_action_frame_le *action_frame; + struct brcmf_config_af_params config_af_params; + struct afx_hdl *afx_hdl = &p2p->afx_hdl; + u16 action_frame_len; + bool ack = false; + u8 category; + u8 action; + s32 tx_retry; + s32 extra_listen_time; + uint delta_ms; + + action_frame = &af_params->action_frame; + action_frame_len = le16_to_cpu(action_frame->len); + + brcmf_p2p_print_actframe(true, action_frame->data, action_frame_len); + + /* Add the default dwell time. Dwell time to stay off-channel */ + /* to wait for a response action frame after transmitting an */ + /* GO Negotiation action frame */ + af_params->dwell_time = cpu_to_le32(P2P_AF_DWELL_TIME); + + category = action_frame->data[DOT11_ACTION_CAT_OFF]; + action = action_frame->data[DOT11_ACTION_ACT_OFF]; + + /* initialize variables */ + p2p->next_af_subtype = P2P_PAF_SUBTYPE_INVALID; + p2p->gon_req_action = false; + + /* config parameters */ + config_af_params.mpc_onoff = -1; + config_af_params.search_channel = false; + config_af_params.extra_listen = false; + + if (brcmf_p2p_is_pub_action(action_frame->data, action_frame_len)) { + /* p2p public action frame process */ + if (brcmf_p2p_pub_af_tx(cfg, af_params, &config_af_params)) { + /* Just send unknown subtype frame with */ + /* default parameters. */ + brcmf_err("P2P Public action frame, unknown subtype.\n"); + } + } else if (brcmf_p2p_is_gas_action(action_frame->data, + action_frame_len)) { + /* service discovery process */ + if (action == P2PSD_ACTION_ID_GAS_IREQ || + action == P2PSD_ACTION_ID_GAS_CREQ) { + /* configure service discovery query frame */ + config_af_params.search_channel = true; + + /* save next af suptype to cancel */ + /* remaining dwell time */ + p2p->next_af_subtype = action + 1; + + af_params->dwell_time = + cpu_to_le32(P2P_AF_MED_DWELL_TIME); + } else if (action == P2PSD_ACTION_ID_GAS_IRESP || + action == P2PSD_ACTION_ID_GAS_CRESP) { + /* configure service discovery response frame */ + af_params->dwell_time = + cpu_to_le32(P2P_AF_MIN_DWELL_TIME); + } else { + brcmf_err("Unknown action type: %d\n", action); + goto exit; + } + } else if (brcmf_p2p_is_p2p_action(action_frame->data, + action_frame_len)) { + /* do not configure anything. it will be */ + /* sent with a default configuration */ + } else { + brcmf_err("Unknown Frame: category 0x%x, action 0x%x\n", + category, action); + return false; + } + + /* if connecting on primary iface, sleep for a while before sending + * af tx for VSDB + */ + if (test_bit(BRCMF_VIF_STATUS_CONNECTING, + &p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->sme_state)) + msleep(50); + + /* if scan is ongoing, abort current scan. */ + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) + brcmf_abort_scanning(cfg); + + memcpy(afx_hdl->tx_dst_addr, action_frame->da, ETH_ALEN); + + /* To make sure to send successfully action frame, turn off mpc */ + if (config_af_params.mpc_onoff == 0) + brcmf_set_mpc(ifp, 0); + + /* set status and destination address before sending af */ + if (p2p->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) { + /* set status to cancel the remained dwell time in rx process */ + set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status); + } + + p2p->af_sent_channel = 0; + set_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status); + /* validate channel and p2p ies */ + if (config_af_params.search_channel && + IS_P2P_SOCIAL_CHANNEL(le32_to_cpu(af_params->channel)) && + p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->saved_ie.probe_req_ie_len) { + afx_hdl = &p2p->afx_hdl; + afx_hdl->peer_listen_chan = le32_to_cpu(af_params->channel); + + if (brcmf_p2p_af_searching_channel(p2p) == + P2P_INVALID_CHANNEL) { + brcmf_err("Couldn't find peer's channel.\n"); + goto exit; + } + + /* Abort scan even for VSDB scenarios. Scan gets aborted in + * firmware but after the check of piggyback algorithm. To take + * care of current piggback algo, lets abort the scan here + * itself. + */ + brcmf_notify_escan_complete(cfg, ifp, true, true); + + /* update channel */ + af_params->channel = cpu_to_le32(afx_hdl->peer_chan); + } + + tx_retry = 0; + while (!p2p->block_gon_req_tx && + (ack == false) && (tx_retry < P2P_AF_TX_MAX_RETRY)) { + ack = !brcmf_p2p_tx_action_frame(p2p, af_params); + tx_retry++; + } + if (ack == false) { + brcmf_err("Failed to send Action Frame(retry %d)\n", tx_retry); + clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); + } + +exit: + clear_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status); + + /* WAR: sometimes dongle does not keep the dwell time of 'actframe'. + * if we coundn't get the next action response frame and dongle does + * not keep the dwell time, go to listen state again to get next action + * response frame. + */ + if (ack && config_af_params.extra_listen && !p2p->block_gon_req_tx && + test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) && + p2p->af_sent_channel == afx_hdl->my_listen_chan) { + delta_ms = jiffies_to_msecs(jiffies - p2p->af_tx_sent_jiffies); + if (le32_to_cpu(af_params->dwell_time) > delta_ms) + extra_listen_time = le32_to_cpu(af_params->dwell_time) - + delta_ms; + else + extra_listen_time = 0; + if (extra_listen_time > 50) { + set_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN, + &p2p->status); + brcmf_dbg(INFO, "Wait more time! actual af time:%d, calculated extra listen:%d\n", + le32_to_cpu(af_params->dwell_time), + extra_listen_time); + extra_listen_time += 100; + if (!brcmf_p2p_discover_listen(p2p, + p2p->af_sent_channel, + extra_listen_time)) { + unsigned long duration; + + extra_listen_time += 100; + duration = msecs_to_jiffies(extra_listen_time); + wait_for_completion_timeout(&p2p->wait_next_af, + duration); + } + clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN, + &p2p->status); + } + } + + if (p2p->block_gon_req_tx) { + /* if ack is true, supplicant will wait more time(100ms). + * so we will return it as a success to get more time . + */ + p2p->block_gon_req_tx = false; + ack = true; + } + + clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status); + /* if all done, turn mpc on again */ + if (config_af_params.mpc_onoff == 1) + brcmf_set_mpc(ifp, 1); + + return ack; +} + +/** + * brcmf_p2p_notify_rx_mgmt_p2p_probereq() - Event handler for p2p probe req. + * + * @ifp: interface pointer for which event was received. + * @e: even message. + * @data: payload of event message (probe request). + */ +s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct afx_hdl *afx_hdl = &p2p->afx_hdl; + struct brcmf_cfg80211_vif *vif = ifp->vif; + struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data; + u16 chanspec = be16_to_cpu(rxframe->chanspec); + struct brcmu_chan ch; + u8 *mgmt_frame; + u32 mgmt_frame_len; + s32 freq; + u16 mgmt_type; + + brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code, + e->reason); + + ch.chspec = be16_to_cpu(rxframe->chanspec); + cfg->d11inf.decchspec(&ch); + + if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) && + (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) { + afx_hdl->peer_chan = ch.chnum; + brcmf_dbg(INFO, "PROBE REQUEST: Peer found, channel=%d\n", + afx_hdl->peer_chan); + complete(&afx_hdl->act_frm_scan); + } + + /* Firmware sends us two proberesponses for each idx one. At the */ + /* moment anything but bsscfgidx 0 is passed up to supplicant */ + if (e->bsscfgidx == 0) + return 0; + + /* Filter any P2P probe reqs arriving during the GO-NEG Phase */ + if (test_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status)) { + brcmf_dbg(INFO, "Filtering P2P probe_req in GO-NEG phase\n"); + return 0; + } + + /* Check if wpa_supplicant has registered for this frame */ + brcmf_dbg(INFO, "vif->mgmt_rx_reg %04x\n", vif->mgmt_rx_reg); + mgmt_type = (IEEE80211_STYPE_PROBE_REQ & IEEE80211_FCTL_STYPE) >> 4; + if ((vif->mgmt_rx_reg & BIT(mgmt_type)) == 0) + return 0; + + mgmt_frame = (u8 *)(rxframe + 1); + mgmt_frame_len = e->datalen - sizeof(*rxframe); + freq = ieee80211_channel_to_frequency(ch.chnum, + ch.band == BRCMU_CHAN_BAND_2G ? + IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ); + + cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0); + + brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n", + mgmt_frame_len, e->datalen, chanspec, freq); + + return 0; +} + + +/** + * brcmf_p2p_get_current_chanspec() - Get current operation channel. + * + * @p2p: P2P specific data. + * @chanspec: chanspec to be returned. + */ +static void brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info *p2p, + u16 *chanspec) +{ + struct brcmf_if *ifp; + u8 mac_addr[ETH_ALEN]; + struct brcmu_chan ch; + struct brcmf_bss_info_le *bi; + u8 *buf; + + ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; + + if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mac_addr, + ETH_ALEN) == 0) { + buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); + if (buf != NULL) { + *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); + if (brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, + buf, WL_BSS_INFO_MAX) == 0) { + bi = (struct brcmf_bss_info_le *)(buf + 4); + *chanspec = le16_to_cpu(bi->chanspec); + kfree(buf); + return; + } + kfree(buf); + } + } + /* Use default channel for P2P */ + ch.chnum = BRCMF_P2P_TEMP_CHAN; + ch.bw = BRCMU_CHAN_BW_20; + p2p->cfg->d11inf.encchspec(&ch); + *chanspec = ch.chspec; +} + +/** + * Change a P2P Role. + * Parameters: + * @mac: MAC address of the BSS to change a role + * Returns 0 if success. + */ +int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, + enum brcmf_fil_p2p_if_types if_type) +{ + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct brcmf_cfg80211_vif *vif; + struct brcmf_fil_p2p_if_le if_request; + s32 err; + u16 chanspec; + + brcmf_dbg(TRACE, "Enter\n"); + + vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; + if (!vif) { + brcmf_err("vif for P2PAPI_BSSCFG_PRIMARY does not exist\n"); + return -EPERM; + } + brcmf_notify_escan_complete(cfg, vif->ifp, true, true); + vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif; + if (!vif) { + brcmf_err("vif for P2PAPI_BSSCFG_CONNECTION does not exist\n"); + return -EPERM; + } + brcmf_set_mpc(vif->ifp, 0); + + /* In concurrency case, STA may be already associated in a particular */ + /* channel. so retrieve the current channel of primary interface and */ + /* then start the virtual interface on that. */ + brcmf_p2p_get_current_chanspec(p2p, &chanspec); + + if_request.type = cpu_to_le16((u16)if_type); + if_request.chspec = cpu_to_le16(chanspec); + memcpy(if_request.addr, p2p->int_addr, sizeof(if_request.addr)); + + brcmf_cfg80211_arm_vif_event(cfg, vif); + err = brcmf_fil_iovar_data_set(vif->ifp, "p2p_ifupd", &if_request, + sizeof(if_request)); + if (err) { + brcmf_err("p2p_ifupd FAILED, err=%d\n", err); + brcmf_cfg80211_arm_vif_event(cfg, NULL); + return err; + } + err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_CHANGE, + msecs_to_jiffies(1500)); + brcmf_cfg80211_arm_vif_event(cfg, NULL); + if (!err) { + brcmf_err("No BRCMF_E_IF_CHANGE event received\n"); + return -EIO; + } + + err = brcmf_fil_cmd_int_set(vif->ifp, BRCMF_C_SET_SCB_TIMEOUT, + BRCMF_SCB_TIMEOUT_VALUE); + + return err; +} + +static int brcmf_p2p_request_p2p_if(struct brcmf_p2p_info *p2p, + struct brcmf_if *ifp, u8 ea[ETH_ALEN], + enum brcmf_fil_p2p_if_types iftype) +{ + struct brcmf_fil_p2p_if_le if_request; + int err; + u16 chanspec; + + /* we need a default channel */ + brcmf_p2p_get_current_chanspec(p2p, &chanspec); + + /* fill the firmware request */ + memcpy(if_request.addr, ea, ETH_ALEN); + if_request.type = cpu_to_le16((u16)iftype); + if_request.chspec = cpu_to_le16(chanspec); + + err = brcmf_fil_iovar_data_set(ifp, "p2p_ifadd", &if_request, + sizeof(if_request)); + if (err) + return err; + + return err; +} + +static int brcmf_p2p_disable_p2p_if(struct brcmf_cfg80211_vif *vif) +{ + struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev); + struct net_device *pri_ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(pri_ndev); + u8 *addr = vif->wdev.netdev->dev_addr; + + return brcmf_fil_iovar_data_set(ifp, "p2p_ifdis", addr, ETH_ALEN); +} + +static int brcmf_p2p_release_p2p_if(struct brcmf_cfg80211_vif *vif) +{ + struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev); + struct net_device *pri_ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(pri_ndev); + u8 *addr = vif->wdev.netdev->dev_addr; + + return brcmf_fil_iovar_data_set(ifp, "p2p_ifdel", addr, ETH_ALEN); +} + +/** + * brcmf_p2p_create_p2pdev() - create a P2P_DEVICE virtual interface. + * + * @p2p: P2P specific data. + * @wiphy: wiphy device of new interface. + * @addr: mac address for this new interface. + */ +static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, + struct wiphy *wiphy, + u8 *addr) +{ + struct brcmf_cfg80211_vif *p2p_vif; + struct brcmf_if *p2p_ifp; + struct brcmf_if *pri_ifp; + int err; + u32 bssidx; + + if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif) + return ERR_PTR(-ENOSPC); + + p2p_vif = brcmf_alloc_vif(p2p->cfg, NL80211_IFTYPE_P2P_DEVICE, + false); + if (IS_ERR(p2p_vif)) { + brcmf_err("could not create discovery vif\n"); + return (struct wireless_dev *)p2p_vif; + } + + pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; + brcmf_p2p_generate_bss_mac(p2p, addr); + brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr); + + brcmf_cfg80211_arm_vif_event(p2p->cfg, p2p_vif); + brcmf_fweh_p2pdev_setup(pri_ifp, true); + + /* Initialize P2P Discovery in the firmware */ + err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1); + if (err < 0) { + brcmf_err("set p2p_disc error\n"); + brcmf_fweh_p2pdev_setup(pri_ifp, false); + brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); + goto fail; + } + + /* wait for firmware event */ + err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD, + msecs_to_jiffies(1500)); + brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); + brcmf_fweh_p2pdev_setup(pri_ifp, false); + if (!err) { + brcmf_err("timeout occurred\n"); + err = -EIO; + goto fail; + } + + /* discovery interface created */ + p2p_ifp = p2p_vif->ifp; + p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif; + memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN); + memcpy(&p2p_vif->wdev.address, p2p->dev_addr, sizeof(p2p->dev_addr)); + + /* verify bsscfg index for P2P discovery */ + err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx); + if (err < 0) { + brcmf_err("retrieving discover bsscfg index failed\n"); + goto fail; + } + + WARN_ON(p2p_ifp->bssidx != bssidx); + + init_completion(&p2p->send_af_done); + INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler); + init_completion(&p2p->afx_hdl.act_frm_scan); + init_completion(&p2p->wait_next_af); + + return &p2p_vif->wdev; + +fail: + brcmf_free_vif(p2p_vif); + return ERR_PTR(err); +} + +/** + * brcmf_p2p_add_vif() - create a new P2P virtual interface. + * + * @wiphy: wiphy device of new interface. + * @name: name of the new interface. + * @name_assign_type: origin of the interface name + * @type: nl80211 interface type. + * @flags: not used. + * @params: contains mac address for P2P device. + */ +struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_cfg80211_vif *vif; + enum brcmf_fil_p2p_if_types iftype; + int err; + + if (brcmf_cfg80211_vif_event_armed(cfg)) + return ERR_PTR(-EBUSY); + + brcmf_dbg(INFO, "adding vif \"%s\" (type=%d)\n", name, type); + + switch (type) { + case NL80211_IFTYPE_P2P_CLIENT: + iftype = BRCMF_FIL_P2P_IF_CLIENT; + break; + case NL80211_IFTYPE_P2P_GO: + iftype = BRCMF_FIL_P2P_IF_GO; + break; + case NL80211_IFTYPE_P2P_DEVICE: + return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy, + params->macaddr); + default: + return ERR_PTR(-EOPNOTSUPP); + } + + vif = brcmf_alloc_vif(cfg, type, false); + if (IS_ERR(vif)) + return (struct wireless_dev *)vif; + brcmf_cfg80211_arm_vif_event(cfg, vif); + + err = brcmf_p2p_request_p2p_if(&cfg->p2p, ifp, cfg->p2p.int_addr, + iftype); + if (err) { + brcmf_cfg80211_arm_vif_event(cfg, NULL); + goto fail; + } + + /* wait for firmware event */ + err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, + msecs_to_jiffies(1500)); + brcmf_cfg80211_arm_vif_event(cfg, NULL); + if (!err) { + brcmf_err("timeout occurred\n"); + err = -EIO; + goto fail; + } + + /* interface created in firmware */ + ifp = vif->ifp; + if (!ifp) { + brcmf_err("no if pointer provided\n"); + err = -ENOENT; + goto fail; + } + + strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); + ifp->ndev->name_assign_type = name_assign_type; + err = brcmf_net_attach(ifp, true); + if (err) { + brcmf_err("Registering netdevice failed\n"); + goto fail; + } + + cfg->p2p.bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = vif; + /* Disable firmware roaming for P2P interface */ + brcmf_fil_iovar_int_set(ifp, "roam_off", 1); + if (iftype == BRCMF_FIL_P2P_IF_GO) { + /* set station timeout for p2p */ + brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCB_TIMEOUT, + BRCMF_SCB_TIMEOUT_VALUE); + } + return &ifp->vif->wdev; + +fail: + brcmf_free_vif(vif); + return ERR_PTR(err); +} + +/** + * brcmf_p2p_del_vif() - delete a P2P virtual interface. + * + * @wiphy: wiphy device of interface. + * @wdev: wireless device of interface. + */ +int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct brcmf_cfg80211_vif *vif; + unsigned long jiffie_timeout = msecs_to_jiffies(1500); + bool wait_for_disable = false; + int err; + + brcmf_dbg(TRACE, "delete P2P vif\n"); + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + + brcmf_cfg80211_arm_vif_event(cfg, vif); + switch (vif->wdev.iftype) { + case NL80211_IFTYPE_P2P_CLIENT: + if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state)) + wait_for_disable = true; + break; + + case NL80211_IFTYPE_P2P_GO: + if (!brcmf_p2p_disable_p2p_if(vif)) + wait_for_disable = true; + break; + + case NL80211_IFTYPE_P2P_DEVICE: + if (!p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif) + return 0; + brcmf_p2p_cancel_remain_on_channel(vif->ifp); + brcmf_p2p_deinit_discovery(p2p); + default: + return -ENOTSUPP; + } + + clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); + brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n"); + + if (wait_for_disable) + wait_for_completion_timeout(&cfg->vif_disabled, + msecs_to_jiffies(500)); + + err = 0; + if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) { + brcmf_vif_clear_mgmt_ies(vif); + err = brcmf_p2p_release_p2p_if(vif); + } + if (!err) { + /* wait for firmware event */ + err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL, + jiffie_timeout); + if (!err) + err = -EIO; + else + err = 0; + } + if (err) + brcmf_remove_interface(vif->ifp); + + brcmf_cfg80211_arm_vif_event(cfg, NULL); + if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + + return err; +} + +void brcmf_p2p_ifp_removed(struct brcmf_if *ifp) +{ + struct brcmf_cfg80211_info *cfg; + struct brcmf_cfg80211_vif *vif; + + brcmf_dbg(INFO, "P2P: device interface removed\n"); + vif = ifp->vif; + cfg = wdev_to_cfg(&vif->wdev); + cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; + rtnl_lock(); + cfg80211_unregister_wdev(&vif->wdev); + rtnl_unlock(); + brcmf_free_vif(vif); +} + +int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct brcmf_cfg80211_vif *vif; + int err; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + mutex_lock(&cfg->usr_sync); + err = brcmf_p2p_enable_discovery(p2p); + if (!err) + set_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); + mutex_unlock(&cfg->usr_sync); + return err; +} + +void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct brcmf_cfg80211_vif *vif; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + /* This call can be result of the unregister_wdev call. In that case + * we dont want to do anything anymore. Just return. The config vif + * will have been cleared at this point. + */ + if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) { + mutex_lock(&cfg->usr_sync); + /* Set the discovery state to SCAN */ + (void)brcmf_p2p_set_discover_state(vif->ifp, + WL_P2P_DISC_ST_SCAN, 0, 0); + brcmf_abort_scanning(cfg); + clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); + mutex_unlock(&cfg->usr_sync); + } +} + +/** + * brcmf_p2p_attach() - attach for P2P. + * + * @cfg: driver private data for cfg80211 interface. + * @p2pdev_forced: create p2p device interface at attach. + */ +s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced) +{ + struct brcmf_p2p_info *p2p; + struct brcmf_if *pri_ifp; + s32 err = 0; + void *err_ptr; + + p2p = &cfg->p2p; + p2p->cfg = cfg; + + pri_ifp = brcmf_get_ifp(cfg->pub, 0); + p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif; + + if (p2pdev_forced) { + err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL); + if (IS_ERR(err_ptr)) { + brcmf_err("P2P device creation failed.\n"); + err = PTR_ERR(err_ptr); + } + } else { + p2p->p2pdev_dynamically = true; + } + return err; +} + +/** + * brcmf_p2p_detach() - detach P2P. + * + * @p2p: P2P specific data. + */ +void brcmf_p2p_detach(struct brcmf_p2p_info *p2p) +{ + struct brcmf_cfg80211_vif *vif; + + vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + if (vif != NULL) { + brcmf_p2p_cancel_remain_on_channel(vif->ifp); + brcmf_p2p_deinit_discovery(p2p); + brcmf_remove_interface(vif->ifp); + } + /* just set it all to zero */ + memset(p2p, 0, sizeof(*p2p)); +} + diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h new file mode 100644 index 000000000000..5d49059021a9 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2012 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 WL_CFGP2P_H_ +#define WL_CFGP2P_H_ + +#include + +struct brcmf_cfg80211_info; + +/** + * enum p2p_bss_type - different type of BSS configurations. + * + * @P2PAPI_BSSCFG_PRIMARY: maps to driver's primary bsscfg. + * @P2PAPI_BSSCFG_DEVICE: maps to driver's P2P device discovery bsscfg. + * @P2PAPI_BSSCFG_CONNECTION: maps to driver's P2P connection bsscfg. + * @P2PAPI_BSSCFG_MAX: used for range checking. + */ +enum p2p_bss_type { + P2PAPI_BSSCFG_PRIMARY, /* maps to driver's primary bsscfg */ + P2PAPI_BSSCFG_DEVICE, /* maps to driver's P2P device discovery bsscfg */ + P2PAPI_BSSCFG_CONNECTION, /* maps to driver's P2P connection bsscfg */ + P2PAPI_BSSCFG_MAX +}; + +/** + * struct p2p_bss - peer-to-peer bss related information. + * + * @vif: virtual interface of this P2P bss. + * @private_data: TBD + */ +struct p2p_bss { + struct brcmf_cfg80211_vif *vif; + void *private_data; +}; + +/** + * enum brcmf_p2p_status - P2P specific dongle status. + * + * @BRCMF_P2P_STATUS_IF_ADD: peer-to-peer vif add sent to dongle. + * @BRCMF_P2P_STATUS_IF_DEL: NOT-USED? + * @BRCMF_P2P_STATUS_IF_DELETING: peer-to-peer vif delete sent to dongle. + * @BRCMF_P2P_STATUS_IF_CHANGING: peer-to-peer vif change sent to dongle. + * @BRCMF_P2P_STATUS_IF_CHANGED: peer-to-peer vif change completed on dongle. + * @BRCMF_P2P_STATUS_ACTION_TX_COMPLETED: action frame tx completed. + * @BRCMF_P2P_STATUS_ACTION_TX_NOACK: action frame tx not acked. + * @BRCMF_P2P_STATUS_GO_NEG_PHASE: P2P GO negotiation ongoing. + * @BRCMF_P2P_STATUS_DISCOVER_LISTEN: P2P listen, remaining on channel. + * @BRCMF_P2P_STATUS_SENDING_ACT_FRAME: In the process of sending action frame. + * @BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN: extra listen time for af tx. + * @BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME: waiting for action frame response. + * @BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL: search channel for AF active. + */ +enum brcmf_p2p_status { + BRCMF_P2P_STATUS_ENABLED, + BRCMF_P2P_STATUS_IF_ADD, + BRCMF_P2P_STATUS_IF_DEL, + BRCMF_P2P_STATUS_IF_DELETING, + BRCMF_P2P_STATUS_IF_CHANGING, + BRCMF_P2P_STATUS_IF_CHANGED, + BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, + BRCMF_P2P_STATUS_ACTION_TX_NOACK, + BRCMF_P2P_STATUS_GO_NEG_PHASE, + BRCMF_P2P_STATUS_DISCOVER_LISTEN, + BRCMF_P2P_STATUS_SENDING_ACT_FRAME, + BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN, + BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, + BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL +}; + +/** + * struct afx_hdl - action frame off channel storage. + * + * @afx_work: worker thread for searching channel + * @act_frm_scan: thread synchronizing struct. + * @is_active: channel searching active. + * @peer_chan: current channel. + * @is_listen: sets mode for afx worker. + * @my_listen_chan: this peers listen channel. + * @peer_listen_chan: remote peers listen channel. + * @tx_dst_addr: mac address where tx af should be sent to. + */ +struct afx_hdl { + struct work_struct afx_work; + struct completion act_frm_scan; + bool is_active; + s32 peer_chan; + bool is_listen; + u16 my_listen_chan; + u16 peer_listen_chan; + u8 tx_dst_addr[ETH_ALEN]; +}; + +/** + * struct brcmf_p2p_info - p2p specific driver information. + * + * @cfg: driver private data for cfg80211 interface. + * @status: status of P2P (see enum brcmf_p2p_status). + * @dev_addr: P2P device address. + * @int_addr: P2P interface address. + * @bss_idx: informate for P2P bss types. + * @listen_timer: timer for @WL_P2P_DISC_ST_LISTEN discover state. + * @ssid: ssid for P2P GO. + * @listen_channel: channel for @WL_P2P_DISC_ST_LISTEN discover state. + * @remain_on_channel: contains copy of struct used by cfg80211. + * @remain_on_channel_cookie: cookie counter for remain on channel cmd + * @next_af_subtype: expected action frame subtype. + * @send_af_done: indication that action frame tx is complete. + * @afx_hdl: action frame search handler info. + * @af_sent_channel: channel action frame is sent. + * @af_tx_sent_jiffies: jiffies time when af tx was transmitted. + * @wait_next_af: thread synchronizing struct. + * @gon_req_action: about to send go negotiation requets frame. + * @block_gon_req_tx: drop tx go negotiation requets frame. + * @p2pdev_dynamically: is p2p device if created by module param or supplicant. + */ +struct brcmf_p2p_info { + struct brcmf_cfg80211_info *cfg; + unsigned long status; + u8 dev_addr[ETH_ALEN]; + u8 int_addr[ETH_ALEN]; + struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX]; + struct timer_list listen_timer; + struct brcmf_ssid ssid; + u8 listen_channel; + struct ieee80211_channel remain_on_channel; + u32 remain_on_channel_cookie; + u8 next_af_subtype; + struct completion send_af_done; + struct afx_hdl afx_hdl; + u32 af_sent_channel; + unsigned long af_tx_sent_jiffies; + struct completion wait_next_af; + bool gon_req_action; + bool block_gon_req_tx; + bool p2pdev_dynamically; +}; + +s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced); +void brcmf_p2p_detach(struct brcmf_p2p_info *p2p); +struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params); +int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev); +int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, + enum brcmf_fil_p2p_if_types if_type); +void brcmf_p2p_ifp_removed(struct brcmf_if *ifp); +int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev); +void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev); +int brcmf_p2p_scan_prep(struct wiphy *wiphy, + struct cfg80211_scan_request *request, + struct brcmf_cfg80211_vif *vif); +int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, + struct ieee80211_channel *channel, + unsigned int duration, u64 *cookie); +int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data); +void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp); +int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data); +int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data); +bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, + struct net_device *ndev, + struct brcmf_fil_af_params_le *af_params); +bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, + struct brcmf_bss_info_le *bi); +s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data); +#endif /* WL_CFGP2P_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c new file mode 100644 index 000000000000..83d804221715 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -0,0 +1,2107 @@ +/* Copyright (c) 2014 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "debug.h" +#include "bus.h" +#include "commonring.h" +#include "msgbuf.h" +#include "pcie.h" +#include "firmware.h" +#include "chip.h" + + +enum brcmf_pcie_state { + BRCMFMAC_PCIE_STATE_DOWN, + BRCMFMAC_PCIE_STATE_UP +}; + + +#define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin" +#define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt" +#define BRCMF_PCIE_4350_FW_NAME "brcm/brcmfmac4350-pcie.bin" +#define BRCMF_PCIE_4350_NVRAM_NAME "brcm/brcmfmac4350-pcie.txt" +#define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin" +#define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt" +#define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin" +#define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt" +#define BRCMF_PCIE_4358_FW_NAME "brcm/brcmfmac4358-pcie.bin" +#define BRCMF_PCIE_4358_NVRAM_NAME "brcm/brcmfmac4358-pcie.txt" +#define BRCMF_PCIE_4365_FW_NAME "brcm/brcmfmac4365b-pcie.bin" +#define BRCMF_PCIE_4365_NVRAM_NAME "brcm/brcmfmac4365b-pcie.txt" +#define BRCMF_PCIE_4366_FW_NAME "brcm/brcmfmac4366b-pcie.bin" +#define BRCMF_PCIE_4366_NVRAM_NAME "brcm/brcmfmac4366b-pcie.txt" +#define BRCMF_PCIE_4371_FW_NAME "brcm/brcmfmac4371-pcie.bin" +#define BRCMF_PCIE_4371_NVRAM_NAME "brcm/brcmfmac4371-pcie.txt" + +#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ + +#define BRCMF_PCIE_TCM_MAP_SIZE (4096 * 1024) +#define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024) + +/* backplane addres space accessed by BAR0 */ +#define BRCMF_PCIE_BAR0_WINDOW 0x80 +#define BRCMF_PCIE_BAR0_REG_SIZE 0x1000 +#define BRCMF_PCIE_BAR0_WRAPPERBASE 0x70 + +#define BRCMF_PCIE_BAR0_WRAPBASE_DMP_OFFSET 0x1000 +#define BRCMF_PCIE_BARO_PCIE_ENUM_OFFSET 0x2000 + +#define BRCMF_PCIE_ARMCR4REG_BANKIDX 0x40 +#define BRCMF_PCIE_ARMCR4REG_BANKPDA 0x4C + +#define BRCMF_PCIE_REG_INTSTATUS 0x90 +#define BRCMF_PCIE_REG_INTMASK 0x94 +#define BRCMF_PCIE_REG_SBMBX 0x98 + +#define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC + +#define BRCMF_PCIE_PCIE2REG_INTMASK 0x24 +#define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48 +#define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C +#define BRCMF_PCIE_PCIE2REG_CONFIGADDR 0x120 +#define BRCMF_PCIE_PCIE2REG_CONFIGDATA 0x124 +#define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX 0x140 + +#define BRCMF_PCIE_GENREV1 1 +#define BRCMF_PCIE_GENREV2 2 + +#define BRCMF_PCIE2_INTA 0x01 +#define BRCMF_PCIE2_INTB 0x02 + +#define BRCMF_PCIE_INT_0 0x01 +#define BRCMF_PCIE_INT_1 0x02 +#define BRCMF_PCIE_INT_DEF (BRCMF_PCIE_INT_0 | \ + BRCMF_PCIE_INT_1) + +#define BRCMF_PCIE_MB_INT_FN0_0 0x0100 +#define BRCMF_PCIE_MB_INT_FN0_1 0x0200 +#define BRCMF_PCIE_MB_INT_D2H0_DB0 0x10000 +#define BRCMF_PCIE_MB_INT_D2H0_DB1 0x20000 +#define BRCMF_PCIE_MB_INT_D2H1_DB0 0x40000 +#define BRCMF_PCIE_MB_INT_D2H1_DB1 0x80000 +#define BRCMF_PCIE_MB_INT_D2H2_DB0 0x100000 +#define BRCMF_PCIE_MB_INT_D2H2_DB1 0x200000 +#define BRCMF_PCIE_MB_INT_D2H3_DB0 0x400000 +#define BRCMF_PCIE_MB_INT_D2H3_DB1 0x800000 + +#define BRCMF_PCIE_MB_INT_D2H_DB (BRCMF_PCIE_MB_INT_D2H0_DB0 | \ + BRCMF_PCIE_MB_INT_D2H0_DB1 | \ + BRCMF_PCIE_MB_INT_D2H1_DB0 | \ + BRCMF_PCIE_MB_INT_D2H1_DB1 | \ + BRCMF_PCIE_MB_INT_D2H2_DB0 | \ + BRCMF_PCIE_MB_INT_D2H2_DB1 | \ + BRCMF_PCIE_MB_INT_D2H3_DB0 | \ + BRCMF_PCIE_MB_INT_D2H3_DB1) + +#define BRCMF_PCIE_MIN_SHARED_VERSION 5 +#define BRCMF_PCIE_MAX_SHARED_VERSION 5 +#define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF +#define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000 +#define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000 + +#define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000 +#define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000 + +#define BRCMF_SHARED_MAX_RXBUFPOST_OFFSET 34 +#define BRCMF_SHARED_RING_BASE_OFFSET 52 +#define BRCMF_SHARED_RX_DATAOFFSET_OFFSET 36 +#define BRCMF_SHARED_CONSOLE_ADDR_OFFSET 20 +#define BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET 40 +#define BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET 44 +#define BRCMF_SHARED_RING_INFO_ADDR_OFFSET 48 +#define BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET 52 +#define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET 56 +#define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET 64 +#define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET 68 + +#define BRCMF_RING_H2D_RING_COUNT_OFFSET 0 +#define BRCMF_RING_D2H_RING_COUNT_OFFSET 1 +#define BRCMF_RING_H2D_RING_MEM_OFFSET 4 +#define BRCMF_RING_H2D_RING_STATE_OFFSET 8 + +#define BRCMF_RING_MEM_BASE_ADDR_OFFSET 8 +#define BRCMF_RING_MAX_ITEM_OFFSET 4 +#define BRCMF_RING_LEN_ITEMS_OFFSET 6 +#define BRCMF_RING_MEM_SZ 16 +#define BRCMF_RING_STATE_SZ 8 + +#define BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET 4 +#define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET 8 +#define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET 12 +#define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET 16 +#define BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET 20 +#define BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET 28 +#define BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET 36 +#define BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET 44 +#define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET 0 +#define BRCMF_SHARED_RING_MAX_SUB_QUEUES 52 + +#define BRCMF_DEF_MAX_RXBUFPOST 255 + +#define BRCMF_CONSOLE_BUFADDR_OFFSET 8 +#define BRCMF_CONSOLE_BUFSIZE_OFFSET 12 +#define BRCMF_CONSOLE_WRITEIDX_OFFSET 16 + +#define BRCMF_DMA_D2H_SCRATCH_BUF_LEN 8 +#define BRCMF_DMA_D2H_RINGUPD_BUF_LEN 1024 + +#define BRCMF_D2H_DEV_D3_ACK 0x00000001 +#define BRCMF_D2H_DEV_DS_ENTER_REQ 0x00000002 +#define BRCMF_D2H_DEV_DS_EXIT_NOTE 0x00000004 + +#define BRCMF_H2D_HOST_D3_INFORM 0x00000001 +#define BRCMF_H2D_HOST_DS_ACK 0x00000002 +#define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008 +#define BRCMF_H2D_HOST_D0_INFORM 0x00000010 + +#define BRCMF_PCIE_MBDATA_TIMEOUT 2000 + +#define BRCMF_PCIE_CFGREG_STATUS_CMD 0x4 +#define BRCMF_PCIE_CFGREG_PM_CSR 0x4C +#define BRCMF_PCIE_CFGREG_MSI_CAP 0x58 +#define BRCMF_PCIE_CFGREG_MSI_ADDR_L 0x5C +#define BRCMF_PCIE_CFGREG_MSI_ADDR_H 0x60 +#define BRCMF_PCIE_CFGREG_MSI_DATA 0x64 +#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL 0xBC +#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2 0xDC +#define BRCMF_PCIE_CFGREG_RBAR_CTRL 0x228 +#define BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1 0x248 +#define BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG 0x4E0 +#define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4 +#define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3 + + +MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4350_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4350_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4365_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4365_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4366_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4366_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4371_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4371_NVRAM_NAME); + + +struct brcmf_pcie_console { + u32 base_addr; + u32 buf_addr; + u32 bufsize; + u32 read_idx; + u8 log_str[256]; + u8 log_idx; +}; + +struct brcmf_pcie_shared_info { + u32 tcm_base_address; + u32 flags; + struct brcmf_pcie_ringbuf *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; + struct brcmf_pcie_ringbuf *flowrings; + u16 max_rxbufpost; + u32 nrof_flowrings; + u32 rx_dataoffset; + u32 htod_mb_data_addr; + u32 dtoh_mb_data_addr; + u32 ring_info_addr; + struct brcmf_pcie_console console; + void *scratch; + dma_addr_t scratch_dmahandle; + void *ringupd; + dma_addr_t ringupd_dmahandle; +}; + +struct brcmf_pcie_core_info { + u32 base; + u32 wrapbase; +}; + +struct brcmf_pciedev_info { + enum brcmf_pcie_state state; + bool in_irq; + bool irq_requested; + struct pci_dev *pdev; + char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + void __iomem *regs; + void __iomem *tcm; + u32 tcm_size; + u32 ram_base; + u32 ram_size; + struct brcmf_chip *ci; + u32 coreid; + u32 generic_corerev; + struct brcmf_pcie_shared_info shared; + void (*ringbell)(struct brcmf_pciedev_info *devinfo); + wait_queue_head_t mbdata_resp_wait; + bool mbdata_completed; + bool irq_allocated; + bool wowl_enabled; + u8 dma_idx_sz; + void *idxbuf; + u32 idxbuf_sz; + dma_addr_t idxbuf_dmahandle; + u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset); + void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u16 value); +}; + +struct brcmf_pcie_ringbuf { + struct brcmf_commonring commonring; + dma_addr_t dma_handle; + u32 w_idx_addr; + u32 r_idx_addr; + struct brcmf_pciedev_info *devinfo; + u8 id; +}; + + +static const u32 brcmf_ring_max_item[BRCMF_NROF_COMMON_MSGRINGS] = { + BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM, + BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM, + BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM, + BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM, + BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM +}; + +static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = { + BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE, + BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE, + BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE, + BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE, + BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE +}; + + +static u32 +brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) +{ + void __iomem *address = devinfo->regs + reg_offset; + + return (ioread32(address)); +} + + +static void +brcmf_pcie_write_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset, + u32 value) +{ + void __iomem *address = devinfo->regs + reg_offset; + + iowrite32(value, address); +} + + +static u8 +brcmf_pcie_read_tcm8(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + return (ioread8(address)); +} + + +static u16 +brcmf_pcie_read_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + return (ioread16(address)); +} + + +static void +brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u16 value) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + iowrite16(value, address); +} + + +static u16 +brcmf_pcie_read_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + u16 *address = devinfo->idxbuf + mem_offset; + + return (*(address)); +} + + +static void +brcmf_pcie_write_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u16 value) +{ + u16 *address = devinfo->idxbuf + mem_offset; + + *(address) = value; +} + + +static u32 +brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + return (ioread32(address)); +} + + +static void +brcmf_pcie_write_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u32 value) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + iowrite32(value, address); +} + + +static u32 +brcmf_pcie_read_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; + + return (ioread32(addr)); +} + + +static void +brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u32 value) +{ + void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; + + iowrite32(value, addr); +} + + +static void +brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + void *srcaddr, u32 len) +{ + void __iomem *address = devinfo->tcm + mem_offset; + __le32 *src32; + __le16 *src16; + u8 *src8; + + if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) { + if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) { + src8 = (u8 *)srcaddr; + while (len) { + iowrite8(*src8, address); + address++; + src8++; + len--; + } + } else { + len = len / 2; + src16 = (__le16 *)srcaddr; + while (len) { + iowrite16(le16_to_cpu(*src16), address); + address += 2; + src16++; + len--; + } + } + } else { + len = len / 4; + src32 = (__le32 *)srcaddr; + while (len) { + iowrite32(le32_to_cpu(*src32), address); + address += 4; + src32++; + len--; + } + } +} + + +static void +brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + void *dstaddr, u32 len) +{ + void __iomem *address = devinfo->tcm + mem_offset; + __le32 *dst32; + __le16 *dst16; + u8 *dst8; + + if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) { + if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) { + dst8 = (u8 *)dstaddr; + while (len) { + *dst8 = ioread8(address); + address++; + dst8++; + len--; + } + } else { + len = len / 2; + dst16 = (__le16 *)dstaddr; + while (len) { + *dst16 = cpu_to_le16(ioread16(address)); + address += 2; + dst16++; + len--; + } + } + } else { + len = len / 4; + dst32 = (__le32 *)dstaddr; + while (len) { + *dst32 = cpu_to_le32(ioread32(address)); + address += 4; + dst32++; + len--; + } + } +} + + +#define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \ + CHIPCREGOFFS(reg), value) + + +static void +brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid) +{ + const struct pci_dev *pdev = devinfo->pdev; + struct brcmf_core *core; + u32 bar0_win; + + core = brcmf_chip_get_core(devinfo->ci, coreid); + if (core) { + bar0_win = core->base; + pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, bar0_win); + if (pci_read_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, + &bar0_win) == 0) { + if (bar0_win != core->base) { + bar0_win = core->base; + pci_write_config_dword(pdev, + BRCMF_PCIE_BAR0_WINDOW, + bar0_win); + } + } + } else { + brcmf_err("Unsupported core selected %x\n", coreid); + } +} + + +static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_core *core; + u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD, + BRCMF_PCIE_CFGREG_PM_CSR, + BRCMF_PCIE_CFGREG_MSI_CAP, + BRCMF_PCIE_CFGREG_MSI_ADDR_L, + BRCMF_PCIE_CFGREG_MSI_ADDR_H, + BRCMF_PCIE_CFGREG_MSI_DATA, + BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2, + BRCMF_PCIE_CFGREG_RBAR_CTRL, + BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1, + BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG, + BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG }; + u32 i; + u32 val; + u32 lsc; + + if (!devinfo->ci) + return; + + /* Disable ASPM */ + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + &lsc); + val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + val); + + /* Watchdog reset */ + brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON); + WRITECC32(devinfo, watchdog, 4); + msleep(100); + + /* Restore ASPM */ + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + lsc); + + core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2); + if (core->rev <= 13) { + for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) { + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGADDR, + cfg_offset[i]); + val = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGDATA); + brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n", + cfg_offset[i], val); + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGDATA, + val); + } + } +} + + +static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) +{ + u32 config; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + /* BAR1 window may not be sized properly */ + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0); + config = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, config); + + device_wakeup_enable(&devinfo->pdev->dev); +} + + +static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { + brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, + 5); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA, + 0); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, + 7); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA, + 0); + } + return 0; +} + + +static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, + u32 resetintr) +{ + struct brcmf_core *core; + + if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { + core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_INTERNAL_MEM); + brcmf_chip_resetcore(core, 0, 0, 0); + } + + return !brcmf_chip_set_active(devinfo->ci, resetintr); +} + + +static int +brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data) +{ + struct brcmf_pcie_shared_info *shared; + u32 addr; + u32 cur_htod_mb_data; + u32 i; + + shared = &devinfo->shared; + addr = shared->htod_mb_data_addr; + cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + + if (cur_htod_mb_data != 0) + brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n", + cur_htod_mb_data); + + i = 0; + while (cur_htod_mb_data != 0) { + msleep(10); + i++; + if (i > 100) + return -EIO; + cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + } + + brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + + return 0; +} + + +static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_shared_info *shared; + u32 addr; + u32 dtoh_mb_data; + + shared = &devinfo->shared; + addr = shared->dtoh_mb_data_addr; + dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + + if (!dtoh_mb_data) + return; + + brcmf_pcie_write_tcm32(devinfo, addr, 0); + + brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data); + if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) { + brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n"); + brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_DS_ACK); + brcmf_dbg(PCIE, "D2H_MB_DATA: sent DEEP SLEEP ACK\n"); + } + if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) + brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); + if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) { + brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n"); + if (waitqueue_active(&devinfo->mbdata_resp_wait)) { + devinfo->mbdata_completed = true; + wake_up(&devinfo->mbdata_resp_wait); + } + } +} + + +static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_shared_info *shared; + struct brcmf_pcie_console *console; + u32 addr; + + shared = &devinfo->shared; + console = &shared->console; + addr = shared->tcm_base_address + BRCMF_SHARED_CONSOLE_ADDR_OFFSET; + console->base_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = console->base_addr + BRCMF_CONSOLE_BUFADDR_OFFSET; + console->buf_addr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET; + console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr); + + brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n", + console->base_addr, console->buf_addr, console->bufsize); +} + + +static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_console *console; + u32 addr; + u8 ch; + u32 newidx; + + if (!BRCMF_FWCON_ON()) + return; + + console = &devinfo->shared.console; + addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET; + newidx = brcmf_pcie_read_tcm32(devinfo, addr); + while (newidx != console->read_idx) { + addr = console->buf_addr + console->read_idx; + ch = brcmf_pcie_read_tcm8(devinfo, addr); + console->read_idx++; + if (console->read_idx == console->bufsize) + console->read_idx = 0; + if (ch == '\r') + continue; + console->log_str[console->log_idx] = ch; + console->log_idx++; + if ((ch != '\n') && + (console->log_idx == (sizeof(console->log_str) - 2))) { + ch = '\n'; + console->log_str[console->log_idx] = ch; + console->log_idx++; + } + if (ch == '\n') { + console->log_str[console->log_idx] = 0; + pr_debug("CONSOLE: %s", console->log_str); + console->log_idx = 0; + } + } +} + + +static __used void brcmf_pcie_ringbell_v1(struct brcmf_pciedev_info *devinfo) +{ + u32 reg_value; + + brcmf_dbg(PCIE, "RING !\n"); + reg_value = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT); + reg_value |= BRCMF_PCIE2_INTB; + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + reg_value); +} + + +static void brcmf_pcie_ringbell_v2(struct brcmf_pciedev_info *devinfo) +{ + brcmf_dbg(PCIE, "RING !\n"); + /* Any arbitrary value will do, lets use 1 */ + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1); +} + + +static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK, + 0); + else + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, + 0); +} + + +static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK, + BRCMF_PCIE_INT_DEF); + else + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, + BRCMF_PCIE_MB_INT_D2H_DB | + BRCMF_PCIE_MB_INT_FN0_0 | + BRCMF_PCIE_MB_INT_FN0_1); +} + + +static irqreturn_t brcmf_pcie_quick_check_isr_v1(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + u32 status; + + status = 0; + pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTSTATUS, &status); + if (status) { + brcmf_pcie_intr_disable(devinfo); + brcmf_dbg(PCIE, "Enter\n"); + return IRQ_WAKE_THREAD; + } + return IRQ_NONE; +} + + +static irqreturn_t brcmf_pcie_quick_check_isr_v2(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + + if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT)) { + brcmf_pcie_intr_disable(devinfo); + brcmf_dbg(PCIE, "Enter\n"); + return IRQ_WAKE_THREAD; + } + return IRQ_NONE; +} + + +static irqreturn_t brcmf_pcie_isr_thread_v1(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + const struct pci_dev *pdev = devinfo->pdev; + u32 status; + + devinfo->in_irq = true; + status = 0; + pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status); + brcmf_dbg(PCIE, "Enter %x\n", status); + if (status) { + pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status); + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_proto_msgbuf_rx_trigger(&devinfo->pdev->dev); + } + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_pcie_intr_enable(devinfo); + devinfo->in_irq = false; + return IRQ_HANDLED; +} + + +static irqreturn_t brcmf_pcie_isr_thread_v2(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + u32 status; + + devinfo->in_irq = true; + status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + brcmf_dbg(PCIE, "Enter %x\n", status); + if (status) { + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + status); + if (status & (BRCMF_PCIE_MB_INT_FN0_0 | + BRCMF_PCIE_MB_INT_FN0_1)) + brcmf_pcie_handle_mb_data(devinfo); + if (status & BRCMF_PCIE_MB_INT_D2H_DB) { + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_proto_msgbuf_rx_trigger( + &devinfo->pdev->dev); + } + } + brcmf_pcie_bus_console_read(devinfo); + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_pcie_intr_enable(devinfo); + devinfo->in_irq = false; + return IRQ_HANDLED; +} + + +static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo) +{ + struct pci_dev *pdev; + + pdev = devinfo->pdev; + + brcmf_pcie_intr_disable(devinfo); + + brcmf_dbg(PCIE, "Enter\n"); + /* is it a v1 or v2 implementation */ + devinfo->irq_requested = false; + pci_enable_msi(pdev); + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { + if (request_threaded_irq(pdev->irq, + brcmf_pcie_quick_check_isr_v1, + brcmf_pcie_isr_thread_v1, + IRQF_SHARED, "brcmf_pcie_intr", + devinfo)) { + pci_disable_msi(pdev); + brcmf_err("Failed to request IRQ %d\n", pdev->irq); + return -EIO; + } + } else { + if (request_threaded_irq(pdev->irq, + brcmf_pcie_quick_check_isr_v2, + brcmf_pcie_isr_thread_v2, + IRQF_SHARED, "brcmf_pcie_intr", + devinfo)) { + pci_disable_msi(pdev); + brcmf_err("Failed to request IRQ %d\n", pdev->irq); + return -EIO; + } + } + devinfo->irq_requested = true; + devinfo->irq_allocated = true; + return 0; +} + + +static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo) +{ + struct pci_dev *pdev; + u32 status; + u32 count; + + if (!devinfo->irq_allocated) + return; + + pdev = devinfo->pdev; + + brcmf_pcie_intr_disable(devinfo); + if (!devinfo->irq_requested) + return; + devinfo->irq_requested = false; + free_irq(pdev->irq, devinfo); + pci_disable_msi(pdev); + + msleep(50); + count = 0; + while ((devinfo->in_irq) && (count < 20)) { + msleep(50); + count++; + } + if (devinfo->in_irq) + brcmf_err("Still in IRQ (processing) !!!\n"); + + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { + status = 0; + pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status); + pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status); + } else { + status = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + status); + } + devinfo->irq_allocated = false; +} + + +static int brcmf_pcie_ring_mb_write_rptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + brcmf_dbg(PCIE, "W r_ptr %d (%d), ring %d\n", commonring->r_ptr, + commonring->w_ptr, ring->id); + + devinfo->write_ptr(devinfo, ring->r_idx_addr, commonring->r_ptr); + + return 0; +} + + +static int brcmf_pcie_ring_mb_write_wptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + brcmf_dbg(PCIE, "W w_ptr %d (%d), ring %d\n", commonring->w_ptr, + commonring->r_ptr, ring->id); + + devinfo->write_ptr(devinfo, ring->w_idx_addr, commonring->w_ptr); + + return 0; +} + + +static int brcmf_pcie_ring_mb_ring_bell(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + devinfo->ringbell(devinfo); + + return 0; +} + + +static int brcmf_pcie_ring_mb_update_rptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + commonring->r_ptr = devinfo->read_ptr(devinfo, ring->r_idx_addr); + + brcmf_dbg(PCIE, "R r_ptr %d (%d), ring %d\n", commonring->r_ptr, + commonring->w_ptr, ring->id); + + return 0; +} + + +static int brcmf_pcie_ring_mb_update_wptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + commonring->w_ptr = devinfo->read_ptr(devinfo, ring->w_idx_addr); + + brcmf_dbg(PCIE, "R w_ptr %d (%d), ring %d\n", commonring->w_ptr, + commonring->r_ptr, ring->id); + + return 0; +} + + +static void * +brcmf_pcie_init_dmabuffer_for_device(struct brcmf_pciedev_info *devinfo, + u32 size, u32 tcm_dma_phys_addr, + dma_addr_t *dma_handle) +{ + void *ring; + u64 address; + + ring = dma_alloc_coherent(&devinfo->pdev->dev, size, dma_handle, + GFP_KERNEL); + if (!ring) + return NULL; + + address = (u64)*dma_handle; + brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr, + address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr + 4, address >> 32); + + memset(ring, 0, size); + + return (ring); +} + + +static struct brcmf_pcie_ringbuf * +brcmf_pcie_alloc_dma_and_ring(struct brcmf_pciedev_info *devinfo, u32 ring_id, + u32 tcm_ring_phys_addr) +{ + void *dma_buf; + dma_addr_t dma_handle; + struct brcmf_pcie_ringbuf *ring; + u32 size; + u32 addr; + + size = brcmf_ring_max_item[ring_id] * brcmf_ring_itemsize[ring_id]; + dma_buf = brcmf_pcie_init_dmabuffer_for_device(devinfo, size, + tcm_ring_phys_addr + BRCMF_RING_MEM_BASE_ADDR_OFFSET, + &dma_handle); + if (!dma_buf) + return NULL; + + addr = tcm_ring_phys_addr + BRCMF_RING_MAX_ITEM_OFFSET; + brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_max_item[ring_id]); + addr = tcm_ring_phys_addr + BRCMF_RING_LEN_ITEMS_OFFSET; + brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_itemsize[ring_id]); + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) { + dma_free_coherent(&devinfo->pdev->dev, size, dma_buf, + dma_handle); + return NULL; + } + brcmf_commonring_config(&ring->commonring, brcmf_ring_max_item[ring_id], + brcmf_ring_itemsize[ring_id], dma_buf); + ring->dma_handle = dma_handle; + ring->devinfo = devinfo; + brcmf_commonring_register_cb(&ring->commonring, + brcmf_pcie_ring_mb_ring_bell, + brcmf_pcie_ring_mb_update_rptr, + brcmf_pcie_ring_mb_update_wptr, + brcmf_pcie_ring_mb_write_rptr, + brcmf_pcie_ring_mb_write_wptr, ring); + + return (ring); +} + + +static void brcmf_pcie_release_ringbuffer(struct device *dev, + struct brcmf_pcie_ringbuf *ring) +{ + void *dma_buf; + u32 size; + + if (!ring) + return; + + dma_buf = ring->commonring.buf_addr; + if (dma_buf) { + size = ring->commonring.depth * ring->commonring.item_len; + dma_free_coherent(dev, size, dma_buf, ring->dma_handle); + } + kfree(ring); +} + + +static void brcmf_pcie_release_ringbuffers(struct brcmf_pciedev_info *devinfo) +{ + u32 i; + + for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++) { + brcmf_pcie_release_ringbuffer(&devinfo->pdev->dev, + devinfo->shared.commonrings[i]); + devinfo->shared.commonrings[i] = NULL; + } + kfree(devinfo->shared.flowrings); + devinfo->shared.flowrings = NULL; + if (devinfo->idxbuf) { + dma_free_coherent(&devinfo->pdev->dev, + devinfo->idxbuf_sz, + devinfo->idxbuf, + devinfo->idxbuf_dmahandle); + devinfo->idxbuf = NULL; + } +} + + +static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_ringbuf *ring; + struct brcmf_pcie_ringbuf *rings; + u32 ring_addr; + u32 d2h_w_idx_ptr; + u32 d2h_r_idx_ptr; + u32 h2d_w_idx_ptr; + u32 h2d_r_idx_ptr; + u32 addr; + u32 ring_mem_ptr; + u32 i; + u64 address; + u32 bufsz; + u16 max_sub_queues; + u8 idx_offset; + + ring_addr = devinfo->shared.ring_info_addr; + brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr); + addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; + max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); + + if (devinfo->dma_idx_sz != 0) { + bufsz = (BRCMF_NROF_D2H_COMMON_MSGRINGS + max_sub_queues) * + devinfo->dma_idx_sz * 2; + devinfo->idxbuf = dma_alloc_coherent(&devinfo->pdev->dev, bufsz, + &devinfo->idxbuf_dmahandle, + GFP_KERNEL); + if (!devinfo->idxbuf) + devinfo->dma_idx_sz = 0; + } + + if (devinfo->dma_idx_sz == 0) { + addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; + d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; + d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; + h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; + h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + idx_offset = sizeof(u32); + devinfo->write_ptr = brcmf_pcie_write_tcm16; + devinfo->read_ptr = brcmf_pcie_read_tcm16; + brcmf_dbg(PCIE, "Using TCM indices\n"); + } else { + memset(devinfo->idxbuf, 0, bufsz); + devinfo->idxbuf_sz = bufsz; + idx_offset = devinfo->dma_idx_sz; + devinfo->write_ptr = brcmf_pcie_write_idx; + devinfo->read_ptr = brcmf_pcie_read_idx; + + h2d_w_idx_ptr = 0; + addr = ring_addr + BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET; + address = (u64)devinfo->idxbuf_dmahandle; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + h2d_r_idx_ptr = h2d_w_idx_ptr + max_sub_queues * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET; + address += max_sub_queues * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + d2h_w_idx_ptr = h2d_r_idx_ptr + max_sub_queues * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET; + address += max_sub_queues * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + d2h_r_idx_ptr = d2h_w_idx_ptr + + BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET; + address += BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + brcmf_dbg(PCIE, "Using host memory indices\n"); + } + + addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET; + ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + + for (i = 0; i < BRCMF_NROF_H2D_COMMON_MSGRINGS; i++) { + ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr); + if (!ring) + goto fail; + ring->w_idx_addr = h2d_w_idx_ptr; + ring->r_idx_addr = h2d_r_idx_ptr; + ring->id = i; + devinfo->shared.commonrings[i] = ring; + + h2d_w_idx_ptr += idx_offset; + h2d_r_idx_ptr += idx_offset; + ring_mem_ptr += BRCMF_RING_MEM_SZ; + } + + for (i = BRCMF_NROF_H2D_COMMON_MSGRINGS; + i < BRCMF_NROF_COMMON_MSGRINGS; i++) { + ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr); + if (!ring) + goto fail; + ring->w_idx_addr = d2h_w_idx_ptr; + ring->r_idx_addr = d2h_r_idx_ptr; + ring->id = i; + devinfo->shared.commonrings[i] = ring; + + d2h_w_idx_ptr += idx_offset; + d2h_r_idx_ptr += idx_offset; + ring_mem_ptr += BRCMF_RING_MEM_SZ; + } + + devinfo->shared.nrof_flowrings = + max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS; + rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring), + GFP_KERNEL); + if (!rings) + goto fail; + + brcmf_dbg(PCIE, "Nr of flowrings is %d\n", + devinfo->shared.nrof_flowrings); + + for (i = 0; i < devinfo->shared.nrof_flowrings; i++) { + ring = &rings[i]; + ring->devinfo = devinfo; + ring->id = i + BRCMF_NROF_COMMON_MSGRINGS; + brcmf_commonring_register_cb(&ring->commonring, + brcmf_pcie_ring_mb_ring_bell, + brcmf_pcie_ring_mb_update_rptr, + brcmf_pcie_ring_mb_update_wptr, + brcmf_pcie_ring_mb_write_rptr, + brcmf_pcie_ring_mb_write_wptr, + ring); + ring->w_idx_addr = h2d_w_idx_ptr; + ring->r_idx_addr = h2d_r_idx_ptr; + h2d_w_idx_ptr += idx_offset; + h2d_r_idx_ptr += idx_offset; + } + devinfo->shared.flowrings = rings; + + return 0; + +fail: + brcmf_err("Allocating ring buffers failed\n"); + brcmf_pcie_release_ringbuffers(devinfo); + return -ENOMEM; +} + + +static void +brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->shared.scratch) + dma_free_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_SCRATCH_BUF_LEN, + devinfo->shared.scratch, + devinfo->shared.scratch_dmahandle); + if (devinfo->shared.ringupd) + dma_free_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_RINGUPD_BUF_LEN, + devinfo->shared.ringupd, + devinfo->shared.ringupd_dmahandle); +} + +static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) +{ + u64 address; + u32 addr; + + devinfo->shared.scratch = dma_alloc_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_SCRATCH_BUF_LEN, + &devinfo->shared.scratch_dmahandle, GFP_KERNEL); + if (!devinfo->shared.scratch) + goto fail; + + memset(devinfo->shared.scratch, 0, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); + + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET; + address = (u64)devinfo->shared.scratch_dmahandle; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET; + brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); + + devinfo->shared.ringupd = dma_alloc_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_RINGUPD_BUF_LEN, + &devinfo->shared.ringupd_dmahandle, GFP_KERNEL); + if (!devinfo->shared.ringupd) + goto fail; + + memset(devinfo->shared.ringupd, 0, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); + + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET; + address = (u64)devinfo->shared.ringupd_dmahandle; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET; + brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); + return 0; + +fail: + brcmf_err("Allocating scratch buffers failed\n"); + brcmf_pcie_release_scratchbuffers(devinfo); + return -ENOMEM; +} + + +static void brcmf_pcie_down(struct device *dev) +{ +} + + +static int brcmf_pcie_tx(struct device *dev, struct sk_buff *skb) +{ + return 0; +} + + +static int brcmf_pcie_tx_ctlpkt(struct device *dev, unsigned char *msg, + uint len) +{ + return 0; +} + + +static int brcmf_pcie_rx_ctlpkt(struct device *dev, unsigned char *msg, + uint len) +{ + return 0; +} + + +static void brcmf_pcie_wowl_config(struct device *dev, bool enabled) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = buspub->devinfo; + + brcmf_dbg(PCIE, "Configuring WOWL, enabled=%d\n", enabled); + devinfo->wowl_enabled = enabled; + if (enabled) + device_set_wakeup_enable(&devinfo->pdev->dev, true); + else + device_set_wakeup_enable(&devinfo->pdev->dev, false); +} + + +static size_t brcmf_pcie_get_ramsize(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = buspub->devinfo; + + return devinfo->ci->ramsize - devinfo->ci->srsize; +} + + +static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = buspub->devinfo; + + brcmf_dbg(PCIE, "dump at 0x%08X: len=%zu\n", devinfo->ci->rambase, len); + brcmf_pcie_copy_dev_tomem(devinfo, devinfo->ci->rambase, data, len); + return 0; +} + + +static struct brcmf_bus_ops brcmf_pcie_bus_ops = { + .txdata = brcmf_pcie_tx, + .stop = brcmf_pcie_down, + .txctl = brcmf_pcie_tx_ctlpkt, + .rxctl = brcmf_pcie_rx_ctlpkt, + .wowl_config = brcmf_pcie_wowl_config, + .get_ramsize = brcmf_pcie_get_ramsize, + .get_memdump = brcmf_pcie_get_memdump, +}; + + +static int +brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, + u32 sharedram_addr) +{ + struct brcmf_pcie_shared_info *shared; + u32 addr; + u32 version; + + shared = &devinfo->shared; + shared->tcm_base_address = sharedram_addr; + + shared->flags = brcmf_pcie_read_tcm32(devinfo, sharedram_addr); + version = shared->flags & BRCMF_PCIE_SHARED_VERSION_MASK; + brcmf_dbg(PCIE, "PCIe protocol version %d\n", version); + if ((version > BRCMF_PCIE_MAX_SHARED_VERSION) || + (version < BRCMF_PCIE_MIN_SHARED_VERSION)) { + brcmf_err("Unsupported PCIE version %d\n", version); + return -EINVAL; + } + + /* check firmware support dma indicies */ + if (shared->flags & BRCMF_PCIE_SHARED_DMA_INDEX) { + if (shared->flags & BRCMF_PCIE_SHARED_DMA_2B_IDX) + devinfo->dma_idx_sz = sizeof(u16); + else + devinfo->dma_idx_sz = sizeof(u32); + } + + addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET; + shared->max_rxbufpost = brcmf_pcie_read_tcm16(devinfo, addr); + if (shared->max_rxbufpost == 0) + shared->max_rxbufpost = BRCMF_DEF_MAX_RXBUFPOST; + + addr = sharedram_addr + BRCMF_SHARED_RX_DATAOFFSET_OFFSET; + shared->rx_dataoffset = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = sharedram_addr + BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET; + shared->htod_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = sharedram_addr + BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET; + shared->dtoh_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET; + shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n", + shared->max_rxbufpost, shared->rx_dataoffset); + + brcmf_pcie_bus_console_init(devinfo); + + return 0; +} + + +static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) +{ + char *fw_name; + char *nvram_name; + uint fw_len, nv_len; + char end; + + brcmf_dbg(PCIE, "Enter, chip 0x%04x chiprev %d\n", devinfo->ci->chip, + devinfo->ci->chiprev); + + switch (devinfo->ci->chip) { + case BRCM_CC_43602_CHIP_ID: + fw_name = BRCMF_PCIE_43602_FW_NAME; + nvram_name = BRCMF_PCIE_43602_NVRAM_NAME; + break; + case BRCM_CC_4350_CHIP_ID: + fw_name = BRCMF_PCIE_4350_FW_NAME; + nvram_name = BRCMF_PCIE_4350_NVRAM_NAME; + break; + case BRCM_CC_4356_CHIP_ID: + fw_name = BRCMF_PCIE_4356_FW_NAME; + nvram_name = BRCMF_PCIE_4356_NVRAM_NAME; + break; + case BRCM_CC_43567_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: + case BRCM_CC_43570_CHIP_ID: + fw_name = BRCMF_PCIE_43570_FW_NAME; + nvram_name = BRCMF_PCIE_43570_NVRAM_NAME; + break; + case BRCM_CC_4358_CHIP_ID: + fw_name = BRCMF_PCIE_4358_FW_NAME; + nvram_name = BRCMF_PCIE_4358_NVRAM_NAME; + break; + case BRCM_CC_4365_CHIP_ID: + fw_name = BRCMF_PCIE_4365_FW_NAME; + nvram_name = BRCMF_PCIE_4365_NVRAM_NAME; + break; + case BRCM_CC_4366_CHIP_ID: + fw_name = BRCMF_PCIE_4366_FW_NAME; + nvram_name = BRCMF_PCIE_4366_NVRAM_NAME; + break; + case BRCM_CC_4371_CHIP_ID: + fw_name = BRCMF_PCIE_4371_FW_NAME; + nvram_name = BRCMF_PCIE_4371_NVRAM_NAME; + break; + default: + brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip); + return -ENODEV; + } + + fw_len = sizeof(devinfo->fw_name) - 1; + nv_len = sizeof(devinfo->nvram_name) - 1; + /* check if firmware path is provided by module parameter */ + if (brcmf_firmware_path[0] != '\0') { + strncpy(devinfo->fw_name, brcmf_firmware_path, fw_len); + strncpy(devinfo->nvram_name, brcmf_firmware_path, nv_len); + fw_len -= strlen(devinfo->fw_name); + nv_len -= strlen(devinfo->nvram_name); + + end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; + if (end != '/') { + strncat(devinfo->fw_name, "/", fw_len); + strncat(devinfo->nvram_name, "/", nv_len); + fw_len--; + nv_len--; + } + } + strncat(devinfo->fw_name, fw_name, fw_len); + strncat(devinfo->nvram_name, nvram_name, nv_len); + + return 0; +} + + +static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, + const struct firmware *fw, void *nvram, + u32 nvram_len) +{ + u32 sharedram_addr; + u32 sharedram_addr_written; + u32 loop_counter; + int err; + u32 address; + u32 resetintr; + + devinfo->ringbell = brcmf_pcie_ringbell_v2; + devinfo->generic_corerev = BRCMF_PCIE_GENREV2; + + brcmf_dbg(PCIE, "Halt ARM.\n"); + err = brcmf_pcie_enter_download_state(devinfo); + if (err) + return err; + + brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name); + brcmf_pcie_copy_mem_todev(devinfo, devinfo->ci->rambase, + (void *)fw->data, fw->size); + + resetintr = get_unaligned_le32(fw->data); + release_firmware(fw); + + /* reset last 4 bytes of RAM address. to be used for shared + * area. This identifies when FW is running + */ + brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, 0); + + if (nvram) { + brcmf_dbg(PCIE, "Download NVRAM %s\n", devinfo->nvram_name); + address = devinfo->ci->rambase + devinfo->ci->ramsize - + nvram_len; + brcmf_pcie_copy_mem_todev(devinfo, address, nvram, nvram_len); + brcmf_fw_nvram_free(nvram); + } else { + brcmf_dbg(PCIE, "No matching NVRAM file found %s\n", + devinfo->nvram_name); + } + + sharedram_addr_written = brcmf_pcie_read_ram32(devinfo, + devinfo->ci->ramsize - + 4); + brcmf_dbg(PCIE, "Bring ARM in running state\n"); + err = brcmf_pcie_exit_download_state(devinfo, resetintr); + if (err) + return err; + + brcmf_dbg(PCIE, "Wait for FW init\n"); + sharedram_addr = sharedram_addr_written; + loop_counter = BRCMF_PCIE_FW_UP_TIMEOUT / 50; + while ((sharedram_addr == sharedram_addr_written) && (loop_counter)) { + msleep(50); + sharedram_addr = brcmf_pcie_read_ram32(devinfo, + devinfo->ci->ramsize - + 4); + loop_counter--; + } + if (sharedram_addr == sharedram_addr_written) { + brcmf_err("FW failed to initialize\n"); + return -ENODEV; + } + brcmf_dbg(PCIE, "Shared RAM addr: 0x%08x\n", sharedram_addr); + + return (brcmf_pcie_init_share_ram_info(devinfo, sharedram_addr)); +} + + +static int brcmf_pcie_get_resource(struct brcmf_pciedev_info *devinfo) +{ + struct pci_dev *pdev; + int err; + phys_addr_t bar0_addr, bar1_addr; + ulong bar1_size; + + pdev = devinfo->pdev; + + err = pci_enable_device(pdev); + if (err) { + brcmf_err("pci_enable_device failed err=%d\n", err); + return err; + } + + pci_set_master(pdev); + + /* Bar-0 mapped address */ + bar0_addr = pci_resource_start(pdev, 0); + /* Bar-1 mapped address */ + bar1_addr = pci_resource_start(pdev, 2); + /* read Bar-1 mapped memory range */ + bar1_size = pci_resource_len(pdev, 2); + if ((bar1_size == 0) || (bar1_addr == 0)) { + brcmf_err("BAR1 Not enabled, device size=%ld, addr=%#016llx\n", + bar1_size, (unsigned long long)bar1_addr); + return -EINVAL; + } + + devinfo->regs = ioremap_nocache(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE); + devinfo->tcm = ioremap_nocache(bar1_addr, BRCMF_PCIE_TCM_MAP_SIZE); + devinfo->tcm_size = BRCMF_PCIE_TCM_MAP_SIZE; + + if (!devinfo->regs || !devinfo->tcm) { + brcmf_err("ioremap() failed (%p,%p)\n", devinfo->regs, + devinfo->tcm); + return -EINVAL; + } + brcmf_dbg(PCIE, "Phys addr : reg space = %p base addr %#016llx\n", + devinfo->regs, (unsigned long long)bar0_addr); + brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx\n", + devinfo->tcm, (unsigned long long)bar1_addr); + + return 0; +} + + +static void brcmf_pcie_release_resource(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->tcm) + iounmap(devinfo->tcm); + if (devinfo->regs) + iounmap(devinfo->regs); + + pci_disable_device(devinfo->pdev); +} + + +static int brcmf_pcie_attach_bus(struct device *dev) +{ + int ret; + + /* Attach to the common driver interface */ + ret = brcmf_attach(dev); + if (ret) { + brcmf_err("brcmf_attach failed\n"); + } else { + ret = brcmf_bus_start(dev); + if (ret) + brcmf_err("dongle is not responding\n"); + } + + return ret; +} + + +static u32 brcmf_pcie_buscore_prep_addr(const struct pci_dev *pdev, u32 addr) +{ + u32 ret_addr; + + ret_addr = addr & (BRCMF_PCIE_BAR0_REG_SIZE - 1); + addr &= ~(BRCMF_PCIE_BAR0_REG_SIZE - 1); + pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, addr); + + return ret_addr; +} + + +static u32 brcmf_pcie_buscore_read32(void *ctx, u32 addr) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr); + return brcmf_pcie_read_reg32(devinfo, addr); +} + + +static void brcmf_pcie_buscore_write32(void *ctx, u32 addr, u32 value) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr); + brcmf_pcie_write_reg32(devinfo, addr, value); +} + + +static int brcmf_pcie_buscoreprep(void *ctx) +{ + return brcmf_pcie_get_resource(ctx); +} + + +static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + u32 val; + + devinfo->ci = chip; + brcmf_pcie_reset_device(devinfo); + + val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + if (val != 0xffffffff) + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + val); + + return 0; +} + + +static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, + u32 rstvec) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + brcmf_pcie_write_tcm32(devinfo, 0, rstvec); +} + + +static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { + .prepare = brcmf_pcie_buscoreprep, + .reset = brcmf_pcie_buscore_reset, + .activate = brcmf_pcie_buscore_activate, + .read32 = brcmf_pcie_buscore_read32, + .write32 = brcmf_pcie_buscore_write32, +}; + +static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw, + void *nvram, u32 nvram_len) +{ + struct brcmf_bus *bus = dev_get_drvdata(dev); + struct brcmf_pciedev *pcie_bus_dev = bus->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; + struct brcmf_commonring **flowrings; + int ret; + u32 i; + + brcmf_pcie_attach(devinfo); + + ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len); + if (ret) + goto fail; + + devinfo->state = BRCMFMAC_PCIE_STATE_UP; + + ret = brcmf_pcie_init_ringbuffers(devinfo); + if (ret) + goto fail; + + ret = brcmf_pcie_init_scratchbuffers(devinfo); + if (ret) + goto fail; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + ret = brcmf_pcie_request_irq(devinfo); + if (ret) + goto fail; + + /* hook the commonrings in the bus structure. */ + for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++) + bus->msgbuf->commonrings[i] = + &devinfo->shared.commonrings[i]->commonring; + + flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*flowrings), + GFP_KERNEL); + if (!flowrings) + goto fail; + + for (i = 0; i < devinfo->shared.nrof_flowrings; i++) + flowrings[i] = &devinfo->shared.flowrings[i].commonring; + bus->msgbuf->flowrings = flowrings; + + bus->msgbuf->rx_dataoffset = devinfo->shared.rx_dataoffset; + bus->msgbuf->max_rxbufpost = devinfo->shared.max_rxbufpost; + bus->msgbuf->nrof_flowrings = devinfo->shared.nrof_flowrings; + + init_waitqueue_head(&devinfo->mbdata_resp_wait); + + brcmf_pcie_intr_enable(devinfo); + if (brcmf_pcie_attach_bus(bus->dev) == 0) + return; + + brcmf_pcie_bus_console_read(devinfo); + +fail: + device_release_driver(dev); +} + +static int +brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int ret; + struct brcmf_pciedev_info *devinfo; + struct brcmf_pciedev *pcie_bus_dev; + struct brcmf_bus *bus; + u16 domain_nr; + u16 bus_nr; + + domain_nr = pci_domain_nr(pdev->bus) + 1; + bus_nr = pdev->bus->number; + brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device, + domain_nr, bus_nr); + + ret = -ENOMEM; + devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); + if (devinfo == NULL) + return ret; + + devinfo->pdev = pdev; + pcie_bus_dev = NULL; + devinfo->ci = brcmf_chip_attach(devinfo, &brcmf_pcie_buscore_ops); + if (IS_ERR(devinfo->ci)) { + ret = PTR_ERR(devinfo->ci); + devinfo->ci = NULL; + goto fail; + } + + pcie_bus_dev = kzalloc(sizeof(*pcie_bus_dev), GFP_KERNEL); + if (pcie_bus_dev == NULL) { + ret = -ENOMEM; + goto fail; + } + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) { + ret = -ENOMEM; + goto fail; + } + bus->msgbuf = kzalloc(sizeof(*bus->msgbuf), GFP_KERNEL); + if (!bus->msgbuf) { + ret = -ENOMEM; + kfree(bus); + goto fail; + } + + /* hook it all together. */ + pcie_bus_dev->devinfo = devinfo; + pcie_bus_dev->bus = bus; + bus->dev = &pdev->dev; + bus->bus_priv.pcie = pcie_bus_dev; + bus->ops = &brcmf_pcie_bus_ops; + bus->proto_type = BRCMF_PROTO_MSGBUF; + bus->chip = devinfo->coreid; + bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot); + dev_set_drvdata(&pdev->dev, bus); + + ret = brcmf_pcie_get_fwnames(devinfo); + if (ret) + goto fail_bus; + + ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM | + BRCMF_FW_REQ_NV_OPTIONAL, + devinfo->fw_name, devinfo->nvram_name, + brcmf_pcie_setup, domain_nr, bus_nr); + if (ret == 0) + return 0; +fail_bus: + kfree(bus->msgbuf); + kfree(bus); +fail: + brcmf_err("failed %x:%x\n", pdev->vendor, pdev->device); + brcmf_pcie_release_resource(devinfo); + if (devinfo->ci) + brcmf_chip_detach(devinfo->ci); + kfree(pcie_bus_dev); + kfree(devinfo); + return ret; +} + + +static void +brcmf_pcie_remove(struct pci_dev *pdev) +{ + struct brcmf_pciedev_info *devinfo; + struct brcmf_bus *bus; + + brcmf_dbg(PCIE, "Enter\n"); + + bus = dev_get_drvdata(&pdev->dev); + if (bus == NULL) + return; + + devinfo = bus->bus_priv.pcie->devinfo; + + devinfo->state = BRCMFMAC_PCIE_STATE_DOWN; + if (devinfo->ci) + brcmf_pcie_intr_disable(devinfo); + + brcmf_detach(&pdev->dev); + + kfree(bus->bus_priv.pcie); + kfree(bus->msgbuf->flowrings); + kfree(bus->msgbuf); + kfree(bus); + + brcmf_pcie_release_irq(devinfo); + brcmf_pcie_release_scratchbuffers(devinfo); + brcmf_pcie_release_ringbuffers(devinfo); + brcmf_pcie_reset_device(devinfo); + brcmf_pcie_release_resource(devinfo); + + if (devinfo->ci) + brcmf_chip_detach(devinfo->ci); + + kfree(devinfo); + dev_set_drvdata(&pdev->dev, NULL); +} + + +#ifdef CONFIG_PM + + +static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct brcmf_pciedev_info *devinfo; + struct brcmf_bus *bus; + int err; + + brcmf_dbg(PCIE, "Enter, state=%d, pdev=%p\n", state.event, pdev); + + bus = dev_get_drvdata(&pdev->dev); + devinfo = bus->bus_priv.pcie->devinfo; + + brcmf_bus_change_state(bus, BRCMF_BUS_DOWN); + + devinfo->mbdata_completed = false; + brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D3_INFORM); + + wait_event_timeout(devinfo->mbdata_resp_wait, + devinfo->mbdata_completed, + msecs_to_jiffies(BRCMF_PCIE_MBDATA_TIMEOUT)); + if (!devinfo->mbdata_completed) { + brcmf_err("Timeout on response for entering D3 substate\n"); + return -EIO; + } + brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM_IN_USE); + + err = pci_save_state(pdev); + if (err) + brcmf_err("pci_save_state failed, err=%d\n", err); + if ((err) || (!devinfo->wowl_enabled)) { + brcmf_chip_detach(devinfo->ci); + devinfo->ci = NULL; + brcmf_pcie_remove(pdev); + return 0; + } + + return pci_prepare_to_sleep(pdev); +} + +static int brcmf_pcie_resume(struct pci_dev *pdev) +{ + struct brcmf_pciedev_info *devinfo; + struct brcmf_bus *bus; + int err; + + bus = dev_get_drvdata(&pdev->dev); + brcmf_dbg(PCIE, "Enter, pdev=%p, bus=%p\n", pdev, bus); + + err = pci_set_power_state(pdev, PCI_D0); + if (err) { + brcmf_err("pci_set_power_state failed, err=%d\n", err); + goto cleanup; + } + pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D3hot, false); + pci_enable_wake(pdev, PCI_D3cold, false); + + /* Check if device is still up and running, if so we are ready */ + if (bus) { + devinfo = bus->bus_priv.pcie->devinfo; + if (brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_INTMASK) != 0) { + if (brcmf_pcie_send_mb_data(devinfo, + BRCMF_H2D_HOST_D0_INFORM)) + goto cleanup; + brcmf_dbg(PCIE, "Hot resume, continue....\n"); + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_bus_change_state(bus, BRCMF_BUS_UP); + brcmf_pcie_intr_enable(devinfo); + return 0; + } + } + +cleanup: + if (bus) { + devinfo = bus->bus_priv.pcie->devinfo; + brcmf_chip_detach(devinfo->ci); + devinfo->ci = NULL; + brcmf_pcie_remove(pdev); + } + err = brcmf_pcie_probe(pdev, NULL); + if (err) + brcmf_err("probe after resume failed, err=%d\n", err); + + return err; +} + + +#endif /* CONFIG_PM */ + + +#define BRCMF_PCIE_DEVICE(dev_id) { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\ + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } + +static struct pci_device_id brcmf_pcie_devid_table[] = { + BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID), + { /* end: all zeroes */ } +}; + + +MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table); + + +static struct pci_driver brcmf_pciedrvr = { + .node = {}, + .name = KBUILD_MODNAME, + .id_table = brcmf_pcie_devid_table, + .probe = brcmf_pcie_probe, + .remove = brcmf_pcie_remove, +#ifdef CONFIG_PM + .suspend = brcmf_pcie_suspend, + .resume = brcmf_pcie_resume +#endif /* CONFIG_PM */ +}; + + +void brcmf_pcie_register(void) +{ + int err; + + brcmf_dbg(PCIE, "Enter\n"); + err = pci_register_driver(&brcmf_pciedrvr); + if (err) + brcmf_err("PCIE driver registration failed, err=%d\n", err); +} + + +void brcmf_pcie_exit(void) +{ + brcmf_dbg(PCIE, "Enter\n"); + pci_unregister_driver(&brcmf_pciedrvr); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h new file mode 100644 index 000000000000..6edaaf8ef5ce --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2014 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_PCIE_H +#define BRCMFMAC_PCIE_H + + +struct brcmf_pciedev { + struct brcmf_bus *bus; + struct brcmf_pciedev_info *devinfo; +}; + + +void brcmf_pcie_exit(void); +void brcmf_pcie_register(void); + + +#endif /* BRCMFMAC_PCIE_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c new file mode 100644 index 000000000000..26b68c367f57 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c @@ -0,0 +1,81 @@ +/* + * 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 +#include "core.h" +#include "bus.h" +#include "debug.h" +#include "proto.h" +#include "bcdc.h" +#include "msgbuf.h" + + +int brcmf_proto_attach(struct brcmf_pub *drvr) +{ + struct brcmf_proto *proto; + + brcmf_dbg(TRACE, "Enter\n"); + + proto = kzalloc(sizeof(*proto), GFP_ATOMIC); + if (!proto) + goto fail; + + drvr->proto = proto; + + if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC) { + if (brcmf_proto_bcdc_attach(drvr)) + goto fail; + } else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF) { + if (brcmf_proto_msgbuf_attach(drvr)) + goto fail; + } else { + brcmf_err("Unsupported proto type %d\n", + drvr->bus_if->proto_type); + goto fail; + } + if ((proto->txdata == NULL) || (proto->hdrpull == NULL) || + (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL) || + (proto->configure_addr_mode == NULL) || + (proto->delete_peer == NULL) || (proto->add_tdls_peer == NULL)) { + brcmf_err("Not all proto handlers have been installed\n"); + goto fail; + } + return 0; + +fail: + kfree(proto); + drvr->proto = NULL; + return -ENOMEM; +} + +void brcmf_proto_detach(struct brcmf_pub *drvr) +{ + brcmf_dbg(TRACE, "Enter\n"); + + if (drvr->proto) { + if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC) + brcmf_proto_bcdc_detach(drvr); + else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF) + brcmf_proto_msgbuf_detach(drvr); + kfree(drvr->proto); + drvr->proto = NULL; + } +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h new file mode 100644 index 000000000000..d55119d36755 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h @@ -0,0 +1,96 @@ +/* + * 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_PROTO_H +#define BRCMFMAC_PROTO_H + + +enum proto_addr_mode { + ADDR_INDIRECT = 0, + ADDR_DIRECT +}; + + +struct brcmf_proto { + int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, + struct sk_buff *skb, struct brcmf_if **ifp); + int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, + void *buf, uint len); + int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, + uint len); + int (*txdata)(struct brcmf_pub *drvr, int ifidx, u8 offset, + struct sk_buff *skb); + void (*configure_addr_mode)(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode); + void (*delete_peer)(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]); + void (*add_tdls_peer)(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]); + void *pd; +}; + + +int brcmf_proto_attach(struct brcmf_pub *drvr); +void brcmf_proto_detach(struct brcmf_pub *drvr); + +static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, + struct sk_buff *skb, + struct brcmf_if **ifp) +{ + struct brcmf_if *tmp = NULL; + + /* assure protocol is always called with + * non-null initialized pointer. + */ + if (ifp) + *ifp = NULL; + else + ifp = &tmp; + return drvr->proto->hdrpull(drvr, do_fws, skb, ifp); +} +static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + return drvr->proto->query_dcmd(drvr, ifidx, cmd, buf, len); +} +static inline int brcmf_proto_set_dcmd(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len); +} +static inline int brcmf_proto_txdata(struct brcmf_pub *drvr, int ifidx, + u8 offset, struct sk_buff *skb) +{ + return drvr->proto->txdata(drvr, ifidx, offset, skb); +} +static inline void +brcmf_proto_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode) +{ + drvr->proto->configure_addr_mode(drvr, ifidx, addr_mode); +} +static inline void +brcmf_proto_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + drvr->proto->delete_peer(drvr, ifidx, peer); +} +static inline void +brcmf_proto_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + drvr->proto->add_tdls_peer(drvr, ifidx, peer); +} + + +#endif /* BRCMFMAC_PROTO_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c new file mode 100644 index 000000000000..7e74ac3ad815 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -0,0 +1,4376 @@ +/* + * Copyright (c) 2010 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdio.h" +#include "chip.h" +#include "firmware.h" + +#define DCMD_RESP_TIMEOUT 2000 /* In milli second */ +#define CTL_DONE_TIMEOUT 2000 /* In milli second */ + +#ifdef DEBUG + +#define BRCMF_TRAP_INFO_SIZE 80 + +#define CBUF_LEN (128) + +/* Device console log buffer state */ +#define CONSOLE_BUFFER_MAX 2024 + +struct rte_log_le { + __le32 buf; /* Can't be pointer on (64-bit) hosts */ + __le32 buf_size; + __le32 idx; + char *_buf_compat; /* Redundant pointer for backward compat. */ +}; + +struct rte_console { + /* Virtual UART + * When there is no UART (e.g. Quickturn), + * the host should write a complete + * input line directly into cbuf and then write + * the length into vcons_in. + * This may also be used when there is a real UART + * (at risk of conflicting with + * the real UART). vcons_out is currently unused. + */ + uint vcons_in; + uint vcons_out; + + /* Output (logging) buffer + * Console output is written to a ring buffer log_buf at index log_idx. + * The host may read the output when it sees log_idx advance. + * Output will be lost if the output wraps around faster than the host + * polls. + */ + struct rte_log_le log_le; + + /* Console input line buffer + * Characters are read one at a time into cbuf + * until is received, then + * the buffer is processed as a command line. + * Also used for virtual UART. + */ + uint cbuf_idx; + char cbuf[CBUF_LEN]; +}; + +#endif /* DEBUG */ +#include + +#include "bus.h" +#include "debug.h" +#include "tracepoint.h" + +#define TXQLEN 2048 /* bulk tx queue length */ +#define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */ +#define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */ +#define PRIOMASK 7 + +#define TXRETRIES 2 /* # of retries for tx frames */ + +#define BRCMF_RXBOUND 50 /* Default for max rx frames in + one scheduling */ + +#define BRCMF_TXBOUND 20 /* Default for max tx frames in + one scheduling */ + +#define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */ + +#define MEMBLOCK 2048 /* Block size used for downloading + of dongle image */ +#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold + biggest possible glom */ + +#define BRCMF_FIRSTREAD (1 << 6) + +#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */ + +/* SBSDIO_DEVICE_CTL */ + +/* 1: device will assert busy signal when receiving CMD53 */ +#define SBSDIO_DEVCTL_SETBUSY 0x01 +/* 1: assertion of sdio interrupt is synchronous to the sdio clock */ +#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02 +/* 1: mask all interrupts to host except the chipActive (rev 8) */ +#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04 +/* 1: isolate internal sdio signals, put external pads in tri-state; requires + * sdio bus power cycle to clear (rev 9) */ +#define SBSDIO_DEVCTL_PADS_ISO 0x08 +/* Force SD->SB reset mapping (rev 11) */ +#define SBSDIO_DEVCTL_SB_RST_CTL 0x30 +/* Determined by CoreControl bit */ +#define SBSDIO_DEVCTL_RST_CORECTL 0x00 +/* Force backplane reset */ +#define SBSDIO_DEVCTL_RST_BPRESET 0x10 +/* Force no backplane reset */ +#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20 + +/* direct(mapped) cis space */ + +/* MAPPED common CIS address */ +#define SBSDIO_CIS_BASE_COMMON 0x1000 +/* maximum bytes in one CIS */ +#define SBSDIO_CIS_SIZE_LIMIT 0x200 +/* cis offset addr is < 17 bits */ +#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF + +/* manfid tuple length, include tuple, link bytes */ +#define SBSDIO_CIS_MANFID_TUPLE_LEN 6 + +#define CORE_BUS_REG(base, field) \ + (base + offsetof(struct sdpcmd_regs, field)) + +/* SDIO function 1 register CHIPCLKCSR */ +/* Force ALP request to backplane */ +#define SBSDIO_FORCE_ALP 0x01 +/* Force HT request to backplane */ +#define SBSDIO_FORCE_HT 0x02 +/* Force ILP request to backplane */ +#define SBSDIO_FORCE_ILP 0x04 +/* Make ALP ready (power up xtal) */ +#define SBSDIO_ALP_AVAIL_REQ 0x08 +/* Make HT ready (power up PLL) */ +#define SBSDIO_HT_AVAIL_REQ 0x10 +/* Squelch clock requests from HW */ +#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 +/* Status: ALP is ready */ +#define SBSDIO_ALP_AVAIL 0x40 +/* Status: HT is ready */ +#define SBSDIO_HT_AVAIL 0x80 +#define SBSDIO_CSR_MASK 0x1F +#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) +#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) +#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) +#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) +#define SBSDIO_CLKAV(regval, alponly) \ + (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) + +/* intstatus */ +#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ +#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ +#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */ +#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */ +#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */ +#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */ +#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */ +#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */ +#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */ +#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */ +#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */ +#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */ +#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */ +#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */ +#define I_PC (1 << 10) /* descriptor error */ +#define I_PD (1 << 11) /* data error */ +#define I_DE (1 << 12) /* Descriptor protocol Error */ +#define I_RU (1 << 13) /* Receive descriptor Underflow */ +#define I_RO (1 << 14) /* Receive fifo Overflow */ +#define I_XU (1 << 15) /* Transmit fifo Underflow */ +#define I_RI (1 << 16) /* Receive Interrupt */ +#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */ +#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */ +#define I_XI (1 << 24) /* Transmit Interrupt */ +#define I_RF_TERM (1 << 25) /* Read Frame Terminate */ +#define I_WF_TERM (1 << 26) /* Write Frame Terminate */ +#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */ +#define I_SBINT (1 << 28) /* sbintstatus Interrupt */ +#define I_CHIPACTIVE (1 << 29) /* chip from doze to active state */ +#define I_SRESET (1 << 30) /* CCCR RES interrupt */ +#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */ +#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU) +#define I_DMA (I_RI | I_XI | I_ERRORS) + +/* corecontrol */ +#define CC_CISRDY (1 << 0) /* CIS Ready */ +#define CC_BPRESEN (1 << 1) /* CCCR RES signal */ +#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */ +#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation */ +#define CC_XMTDATAAVAIL_MODE (1 << 4) +#define CC_XMTDATAAVAIL_CTRL (1 << 5) + +/* SDA_FRAMECTRL */ +#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */ +#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */ +#define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */ +#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */ + +/* + * Software allocation of To SB Mailbox resources + */ + +/* tosbmailbox bits corresponding to intstatus bits */ +#define SMB_NAK (1 << 0) /* Frame NAK */ +#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */ +#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */ +#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */ + +/* tosbmailboxdata */ +#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */ + +/* + * Software allocation of To Host Mailbox resources + */ + +/* intstatus bits */ +#define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */ +#define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */ +#define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */ +#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */ + +/* tohostmailboxdata */ +#define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */ +#define HMB_DATA_DEVREADY 2 /* talk to host after enable */ +#define HMB_DATA_FC 4 /* per prio flowcontrol update flag */ +#define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */ + +#define HMB_DATA_FCDATA_MASK 0xff000000 +#define HMB_DATA_FCDATA_SHIFT 24 + +#define HMB_DATA_VERSION_MASK 0x00ff0000 +#define HMB_DATA_VERSION_SHIFT 16 + +/* + * Software-defined protocol header + */ + +/* Current protocol version */ +#define SDPCM_PROT_VERSION 4 + +/* + * Shared structure between dongle and the host. + * The structure contains pointers to trap or assert information. + */ +#define SDPCM_SHARED_VERSION 0x0003 +#define SDPCM_SHARED_VERSION_MASK 0x00FF +#define SDPCM_SHARED_ASSERT_BUILT 0x0100 +#define SDPCM_SHARED_ASSERT 0x0200 +#define SDPCM_SHARED_TRAP 0x0400 + +/* Space for header read, limit for data packets */ +#define MAX_HDR_READ (1 << 6) +#define MAX_RX_DATASZ 2048 + +/* Bump up limit on waiting for HT to account for first startup; + * if the image is doing a CRC calculation before programming the PMU + * for HT availability, it could take a couple hundred ms more, so + * max out at a 1 second (1000000us). + */ +#undef PMU_MAX_TRANSITION_DLY +#define PMU_MAX_TRANSITION_DLY 1000000 + +/* Value for ChipClockCSR during initial setup */ +#define BRCMF_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \ + SBSDIO_ALP_AVAIL_REQ) + +/* Flags for SDH calls */ +#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) + +#define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change + * when idle + */ +#define BRCMF_IDLE_INTERVAL 1 + +#define KSO_WAIT_US 50 +#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US) + +/* + * Conversion of 802.1D priority to precedence level + */ +static uint prio2prec(u32 prio) +{ + return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ? + (prio^2) : prio; +} + +#ifdef DEBUG +/* Device console log buffer state */ +struct brcmf_console { + uint count; /* Poll interval msec counter */ + uint log_addr; /* Log struct address (fixed) */ + struct rte_log_le log_le; /* Log struct (host copy) */ + uint bufsize; /* Size of log buffer */ + u8 *buf; /* Log buffer (host copy) */ + uint last; /* Last buffer read index */ +}; + +struct brcmf_trap_info { + __le32 type; + __le32 epc; + __le32 cpsr; + __le32 spsr; + __le32 r0; /* a1 */ + __le32 r1; /* a2 */ + __le32 r2; /* a3 */ + __le32 r3; /* a4 */ + __le32 r4; /* v1 */ + __le32 r5; /* v2 */ + __le32 r6; /* v3 */ + __le32 r7; /* v4 */ + __le32 r8; /* v5 */ + __le32 r9; /* sb/v6 */ + __le32 r10; /* sl/v7 */ + __le32 r11; /* fp/v8 */ + __le32 r12; /* ip */ + __le32 r13; /* sp */ + __le32 r14; /* lr */ + __le32 pc; /* r15 */ +}; +#endif /* DEBUG */ + +struct sdpcm_shared { + u32 flags; + u32 trap_addr; + u32 assert_exp_addr; + u32 assert_file_addr; + u32 assert_line; + u32 console_addr; /* Address of struct rte_console */ + u32 msgtrace_addr; + u8 tag[32]; + u32 brpt_addr; +}; + +struct sdpcm_shared_le { + __le32 flags; + __le32 trap_addr; + __le32 assert_exp_addr; + __le32 assert_file_addr; + __le32 assert_line; + __le32 console_addr; /* Address of struct rte_console */ + __le32 msgtrace_addr; + u8 tag[32]; + __le32 brpt_addr; +}; + +/* dongle SDIO bus specific header info */ +struct brcmf_sdio_hdrinfo { + u8 seq_num; + u8 channel; + u16 len; + u16 len_left; + u16 len_nxtfrm; + u8 dat_offset; + bool lastfrm; + u16 tail_pad; +}; + +/* + * hold counter variables + */ +struct brcmf_sdio_count { + uint intrcount; /* Count of device interrupt callbacks */ + uint lastintrs; /* Count as of last watchdog timer */ + uint pollcnt; /* Count of active polls */ + uint regfails; /* Count of R_REG failures */ + uint tx_sderrs; /* Count of tx attempts with sd errors */ + uint fcqueued; /* Tx packets that got queued */ + uint rxrtx; /* Count of rtx requests (NAK to dongle) */ + uint rx_toolong; /* Receive frames too long to receive */ + uint rxc_errors; /* SDIO errors when reading control frames */ + uint rx_hdrfail; /* SDIO errors on header reads */ + uint rx_badhdr; /* Bad received headers (roosync?) */ + uint rx_badseq; /* Mismatched rx sequence number */ + uint fc_rcvd; /* Number of flow-control events received */ + uint fc_xoff; /* Number which turned on flow-control */ + uint fc_xon; /* Number which turned off flow-control */ + uint rxglomfail; /* Failed deglom attempts */ + uint rxglomframes; /* Number of glom frames (superframes) */ + uint rxglompkts; /* Number of packets from glom frames */ + uint f2rxhdrs; /* Number of header reads */ + uint f2rxdata; /* Number of frame data reads */ + uint f2txdata; /* Number of f2 frame writes */ + uint f1regdata; /* Number of f1 register accesses */ + uint tickcnt; /* Number of watchdog been schedule */ + ulong tx_ctlerrs; /* Err of sending ctrl frames */ + ulong tx_ctlpkts; /* Ctrl frames sent to dongle */ + ulong rx_ctlerrs; /* Err of processing rx ctrl frames */ + ulong rx_ctlpkts; /* Ctrl frames processed from dongle */ + ulong rx_readahead_cnt; /* packets where header read-ahead was used */ +}; + +/* misc chip info needed by some of the routines */ +/* Private data for SDIO bus interaction */ +struct brcmf_sdio { + struct brcmf_sdio_dev *sdiodev; /* sdio device handler */ + struct brcmf_chip *ci; /* Chip info struct */ + + u32 hostintmask; /* Copy of Host Interrupt Mask */ + atomic_t intstatus; /* Intstatus bits (events) pending */ + atomic_t fcstate; /* State of dongle flow-control */ + + uint blocksize; /* Block size of SDIO transfers */ + uint roundup; /* Max roundup limit */ + + struct pktq txq; /* Queue length used for flow-control */ + u8 flowcontrol; /* per prio flow control bitmask */ + u8 tx_seq; /* Transmit sequence number (next) */ + u8 tx_max; /* Maximum transmit sequence allowed */ + + u8 *hdrbuf; /* buffer for handling rx frame */ + u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */ + u8 rx_seq; /* Receive sequence number (expected) */ + struct brcmf_sdio_hdrinfo cur_read; + /* info of current read frame */ + bool rxskip; /* Skip receive (awaiting NAK ACK) */ + bool rxpending; /* Data frame pending in dongle */ + + uint rxbound; /* Rx frames to read before resched */ + uint txbound; /* Tx frames to send before resched */ + uint txminmax; + + struct sk_buff *glomd; /* Packet containing glomming descriptor */ + struct sk_buff_head glom; /* Packet list for glommed superframe */ + uint glomerr; /* Glom packet read errors */ + + u8 *rxbuf; /* Buffer for receiving control packets */ + uint rxblen; /* Allocated length of rxbuf */ + u8 *rxctl; /* Aligned pointer into rxbuf */ + u8 *rxctl_orig; /* pointer for freeing rxctl */ + uint rxlen; /* Length of valid data in buffer */ + spinlock_t rxctl_lock; /* protection lock for ctrl frame resources */ + + u8 sdpcm_ver; /* Bus protocol reported by dongle */ + + bool intr; /* Use interrupts */ + bool poll; /* Use polling */ + atomic_t ipend; /* Device interrupt is pending */ + uint spurious; /* Count of spurious interrupts */ + uint pollrate; /* Ticks between device polls */ + uint polltick; /* Tick counter */ + +#ifdef DEBUG + uint console_interval; + struct brcmf_console console; /* Console output polling support */ + uint console_addr; /* Console address from shared struct */ +#endif /* DEBUG */ + + uint clkstate; /* State of sd and backplane clock(s) */ + s32 idletime; /* Control for activity timeout */ + s32 idlecount; /* Activity timeout counter */ + s32 idleclock; /* How to set bus driver when idle */ + bool rxflow_mode; /* Rx flow control mode */ + bool rxflow; /* Is rx flow control on */ + bool alp_only; /* Don't use HT clock (ALP only) */ + + u8 *ctrl_frame_buf; + u16 ctrl_frame_len; + bool ctrl_frame_stat; + int ctrl_frame_err; + + spinlock_t txq_lock; /* protect bus->txq */ + wait_queue_head_t ctrl_wait; + wait_queue_head_t dcmd_resp_wait; + + struct timer_list timer; + struct completion watchdog_wait; + struct task_struct *watchdog_tsk; + bool wd_timer_valid; + uint save_ms; + + struct workqueue_struct *brcmf_wq; + struct work_struct datawork; + bool dpc_triggered; + bool dpc_running; + + bool txoff; /* Transmit flow-controlled */ + struct brcmf_sdio_count sdcnt; + bool sr_enabled; /* SaveRestore enabled */ + bool sleeping; + + u8 tx_hdrlen; /* sdio bus header length for tx packet */ + bool txglom; /* host tx glomming enable flag */ + u16 head_align; /* buffer pointer alignment */ + u16 sgentry_align; /* scatter-gather buffer alignment */ +}; + +/* clkstate */ +#define CLK_NONE 0 +#define CLK_SDONLY 1 +#define CLK_PENDING 2 +#define CLK_AVAIL 3 + +#ifdef DEBUG +static int qcount[NUMPRIO]; +#endif /* DEBUG */ + +#define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */ + +#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL) + +/* Retry count for register access failures */ +static const uint retry_limit = 2; + +/* Limit on rounding up frames */ +static const uint max_roundup = 512; + +#define ALIGNMENT 4 + +enum brcmf_sdio_frmtype { + BRCMF_SDIO_FT_NORMAL, + BRCMF_SDIO_FT_SUPER, + BRCMF_SDIO_FT_SUB, +}; + +#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) + +/* SDIO Pad drive strength to select value mappings */ +struct sdiod_drive_str { + u8 strength; /* Pad Drive Strength in mA */ + u8 sel; /* Chip-specific select value */ +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ +static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { + {32, 0x6}, + {26, 0x7}, + {22, 0x4}, + {16, 0x5}, + {12, 0x2}, + {8, 0x3}, + {4, 0x0}, + {0, 0x1} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ +static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = { + {6, 0x7}, + {5, 0x6}, + {4, 0x5}, + {3, 0x4}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ +static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = { + {3, 0x3}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */ +static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { + {16, 0x7}, + {12, 0x5}, + {8, 0x3}, + {4, 0x1} +}; + +#define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin" +#define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt" +#define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin" +#define BCM43241B0_NVRAM_NAME "brcm/brcmfmac43241b0-sdio.txt" +#define BCM43241B4_FIRMWARE_NAME "brcm/brcmfmac43241b4-sdio.bin" +#define BCM43241B4_NVRAM_NAME "brcm/brcmfmac43241b4-sdio.txt" +#define BCM43241B5_FIRMWARE_NAME "brcm/brcmfmac43241b5-sdio.bin" +#define BCM43241B5_NVRAM_NAME "brcm/brcmfmac43241b5-sdio.txt" +#define BCM4329_FIRMWARE_NAME "brcm/brcmfmac4329-sdio.bin" +#define BCM4329_NVRAM_NAME "brcm/brcmfmac4329-sdio.txt" +#define BCM4330_FIRMWARE_NAME "brcm/brcmfmac4330-sdio.bin" +#define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt" +#define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin" +#define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt" +#define BCM43340_FIRMWARE_NAME "brcm/brcmfmac43340-sdio.bin" +#define BCM43340_NVRAM_NAME "brcm/brcmfmac43340-sdio.txt" +#define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin" +#define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt" +#define BCM43362_FIRMWARE_NAME "brcm/brcmfmac43362-sdio.bin" +#define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" +#define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" +#define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" +#define BCM43430_FIRMWARE_NAME "brcm/brcmfmac43430-sdio.bin" +#define BCM43430_NVRAM_NAME "brcm/brcmfmac43430-sdio.txt" +#define BCM43455_FIRMWARE_NAME "brcm/brcmfmac43455-sdio.bin" +#define BCM43455_NVRAM_NAME "brcm/brcmfmac43455-sdio.txt" +#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" +#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" + +MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43143_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B5_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B5_NVRAM_NAME); +MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4329_NVRAM_NAME); +MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4330_NVRAM_NAME); +MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4334_NVRAM_NAME); +MODULE_FIRMWARE(BCM43340_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43340_NVRAM_NAME); +MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4335_NVRAM_NAME); +MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43362_NVRAM_NAME); +MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43430_NVRAM_NAME); +MODULE_FIRMWARE(BCM43455_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43455_NVRAM_NAME); +MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4354_NVRAM_NAME); + +struct brcmf_firmware_names { + u32 chipid; + u32 revmsk; + const char *bin; + const char *nv; +}; + +enum brcmf_firmware_type { + BRCMF_FIRMWARE_BIN, + BRCMF_FIRMWARE_NVRAM +}; + +#define BRCMF_FIRMWARE_NVRAM(name) \ + name ## _FIRMWARE_NAME, name ## _NVRAM_NAME + +static const struct brcmf_firmware_names brcmf_fwname_data[] = { + { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, + { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, + { BRCM_CC_43241_CHIP_ID, 0x00000020, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, + { BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43241B5) }, + { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, + { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, + { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, + { BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43340) }, + { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, + { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, + { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) }, + { BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43455) }, + { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } +}; + +static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, + struct brcmf_sdio_dev *sdiodev) +{ + int i; + char end; + + for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { + if (brcmf_fwname_data[i].chipid == ci->chip && + brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) + break; + } + + if (i == ARRAY_SIZE(brcmf_fwname_data)) { + brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev); + return -ENODEV; + } + + /* check if firmware path is provided by module parameter */ + if (brcmf_firmware_path[0] != '\0') { + strlcpy(sdiodev->fw_name, brcmf_firmware_path, + sizeof(sdiodev->fw_name)); + strlcpy(sdiodev->nvram_name, brcmf_firmware_path, + sizeof(sdiodev->nvram_name)); + + end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; + if (end != '/') { + strlcat(sdiodev->fw_name, "/", + sizeof(sdiodev->fw_name)); + strlcat(sdiodev->nvram_name, "/", + sizeof(sdiodev->nvram_name)); + } + } + strlcat(sdiodev->fw_name, brcmf_fwname_data[i].bin, + sizeof(sdiodev->fw_name)); + strlcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, + sizeof(sdiodev->nvram_name)); + + return 0; +} + +static void pkt_align(struct sk_buff *p, int len, int align) +{ + uint datalign; + datalign = (unsigned long)(p->data); + datalign = roundup(datalign, (align)) - datalign; + if (datalign) + skb_pull(p, datalign); + __skb_trim(p, len); +} + +/* To check if there's window offered */ +static bool data_ok(struct brcmf_sdio *bus) +{ + return (u8)(bus->tx_max - bus->tx_seq) != 0 && + ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0; +} + +/* + * Reads a register in the SDIO hardware block. This block occupies a series of + * adresses on the 32 bit backplane bus. + */ +static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) +{ + struct brcmf_core *core; + int ret; + + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret); + + return ret; +} + +static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) +{ + struct brcmf_core *core; + int ret; + + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret); + + return ret; +} + +static int +brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) +{ + u8 wr_val = 0, rd_val, cmp_val, bmask; + int err = 0; + int try_cnt = 0; + + brcmf_dbg(TRACE, "Enter: on=%d\n", on); + + wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); + /* 1st KSO write goes to AOS wake up core if device is asleep */ + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + wr_val, &err); + + if (on) { + /* device WAKEUP through KSO: + * write bit 0 & read back until + * both bits 0 (kso bit) & 1 (dev on status) are set + */ + cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | + SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK; + bmask = cmp_val; + usleep_range(2000, 3000); + } else { + /* Put device to sleep, turn off KSO */ + cmp_val = 0; + /* only check for bit0, bit1(dev on status) may not + * get cleared right away + */ + bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK; + } + + do { + /* reliable KSO bit set/clr: + * the sdiod sleep write access is synced to PMU 32khz clk + * just one write attempt may fail, + * read it back until it matches written value + */ + rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + &err); + if (((rd_val & bmask) == cmp_val) && !err) + break; + + udelay(KSO_WAIT_US); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + wr_val, &err); + } while (try_cnt++ < MAX_KSO_ATTEMPTS); + + if (try_cnt > 2) + brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt, + rd_val, err); + + if (try_cnt > MAX_KSO_ATTEMPTS) + brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err); + + return err; +} + +#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) + +/* Turn backplane clock on or off */ +static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok) +{ + int err; + u8 clkctl, clkreq, devctl; + unsigned long timeout; + + brcmf_dbg(SDIO, "Enter\n"); + + clkctl = 0; + + if (bus->sr_enabled) { + bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY); + return 0; + } + + if (on) { + /* Request HT Avail */ + clkreq = + bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; + + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + clkreq, &err); + if (err) { + brcmf_err("HT Avail request error: %d\n", err); + return -EBADE; + } + + /* Check current status */ + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (err) { + brcmf_err("HT Avail read error: %d\n", err); + return -EBADE; + } + + /* Go to pending and await interrupt if appropriate */ + if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) { + /* Allow only clock-available interrupt */ + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); + if (err) { + brcmf_err("Devctl error setting CA: %d\n", + err); + return -EBADE; + } + + devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); + brcmf_dbg(SDIO, "CLKCTL: set PENDING\n"); + bus->clkstate = CLK_PENDING; + + return 0; + } else if (bus->clkstate == CLK_PENDING) { + /* Cancel CA-only interrupt filter */ + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); + } + + /* Otherwise, wait here (polling) for HT Avail */ + timeout = jiffies + + msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000); + while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + &err); + if (time_after(jiffies, timeout)) + break; + else + usleep_range(5000, 10000); + } + if (err) { + brcmf_err("HT Avail request error: %d\n", err); + return -EBADE; + } + if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { + brcmf_err("HT Avail timeout (%d): clkctl 0x%02x\n", + PMU_MAX_TRANSITION_DLY, clkctl); + return -EBADE; + } + + /* Mark clock available */ + bus->clkstate = CLK_AVAIL; + brcmf_dbg(SDIO, "CLKCTL: turned ON\n"); + +#if defined(DEBUG) + if (!bus->alp_only) { + if (SBSDIO_ALPONLY(clkctl)) + brcmf_err("HT Clock should be on\n"); + } +#endif /* defined (DEBUG) */ + + } else { + clkreq = 0; + + if (bus->clkstate == CLK_PENDING) { + /* Cancel CA-only interrupt filter */ + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); + } + + bus->clkstate = CLK_SDONLY; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + clkreq, &err); + brcmf_dbg(SDIO, "CLKCTL: turned OFF\n"); + if (err) { + brcmf_err("Failed access turning clock off: %d\n", + err); + return -EBADE; + } + } + return 0; +} + +/* Change idle/active SD state */ +static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on) +{ + brcmf_dbg(SDIO, "Enter\n"); + + if (on) + bus->clkstate = CLK_SDONLY; + else + bus->clkstate = CLK_NONE; + + return 0; +} + +/* Transition SD and backplane clock readiness */ +static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) +{ +#ifdef DEBUG + uint oldstate = bus->clkstate; +#endif /* DEBUG */ + + brcmf_dbg(SDIO, "Enter\n"); + + /* Early exit if we're already there */ + if (bus->clkstate == target) + return 0; + + switch (target) { + case CLK_AVAIL: + /* Make sure SD clock is available */ + if (bus->clkstate == CLK_NONE) + brcmf_sdio_sdclk(bus, true); + /* Now request HT Avail on the backplane */ + brcmf_sdio_htclk(bus, true, pendok); + break; + + case CLK_SDONLY: + /* Remove HT request, or bring up SD clock */ + if (bus->clkstate == CLK_NONE) + brcmf_sdio_sdclk(bus, true); + else if (bus->clkstate == CLK_AVAIL) + brcmf_sdio_htclk(bus, false, false); + else + brcmf_err("request for %d -> %d\n", + bus->clkstate, target); + break; + + case CLK_NONE: + /* Make sure to remove HT request */ + if (bus->clkstate == CLK_AVAIL) + brcmf_sdio_htclk(bus, false, false); + /* Now remove the SD clock */ + brcmf_sdio_sdclk(bus, false); + break; + } +#ifdef DEBUG + brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate); +#endif /* DEBUG */ + + return 0; +} + +static int +brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) +{ + int err = 0; + u8 clkcsr; + + brcmf_dbg(SDIO, "Enter: request %s currently %s\n", + (sleep ? "SLEEP" : "WAKE"), + (bus->sleeping ? "SLEEP" : "WAKE")); + + /* If SR is enabled control bus state with KSO */ + if (bus->sr_enabled) { + /* Done if we're already in the requested state */ + if (sleep == bus->sleeping) + goto end; + + /* Going to sleep */ + if (sleep) { + clkcsr = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + &err); + if ((clkcsr & SBSDIO_CSR_MASK) == 0) { + brcmf_dbg(SDIO, "no clock, set ALP\n"); + brcmf_sdiod_regwb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_ALP_AVAIL_REQ, &err); + } + err = brcmf_sdio_kso_control(bus, false); + } else { + err = brcmf_sdio_kso_control(bus, true); + } + if (err) { + brcmf_err("error while changing bus sleep state %d\n", + err); + goto done; + } + } + +end: + /* control clocks */ + if (sleep) { + if (!bus->sr_enabled) + brcmf_sdio_clkctl(bus, CLK_NONE, pendok); + } else { + brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + } + bus->sleeping = sleep; + brcmf_dbg(SDIO, "new state %s\n", + (sleep ? "SLEEP" : "WAKE")); +done: + brcmf_dbg(SDIO, "Exit: err=%d\n", err); + return err; + +} + +#ifdef DEBUG +static inline bool brcmf_sdio_valid_shared_address(u32 addr) +{ + return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); +} + +static int brcmf_sdio_readshared(struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + u32 addr = 0; + int rv; + u32 shaddr = 0; + struct sdpcm_shared_le sh_le; + __le32 addr_le; + + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, false, false); + + /* + * Read last word in socram to determine + * address of sdpcm_shared structure + */ + shaddr = bus->ci->rambase + bus->ci->ramsize - 4; + if (!bus->ci->rambase && brcmf_chip_sr_capable(bus->ci)) + shaddr -= bus->ci->srsize; + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, + (u8 *)&addr_le, 4); + if (rv < 0) + goto fail; + + /* + * Check if addr is valid. + * NVRAM length at the end of memory should have been overwritten. + */ + addr = le32_to_cpu(addr_le); + if (!brcmf_sdio_valid_shared_address(addr)) { + brcmf_err("invalid sdpcm_shared address 0x%08X\n", addr); + rv = -EINVAL; + goto fail; + } + + brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr); + + /* Read hndrte_shared structure */ + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, + sizeof(struct sdpcm_shared_le)); + if (rv < 0) + goto fail; + + sdio_release_host(bus->sdiodev->func[1]); + + /* Endianness */ + sh->flags = le32_to_cpu(sh_le.flags); + sh->trap_addr = le32_to_cpu(sh_le.trap_addr); + sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); + sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); + sh->assert_line = le32_to_cpu(sh_le.assert_line); + sh->console_addr = le32_to_cpu(sh_le.console_addr); + sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); + + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { + brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", + SDPCM_SHARED_VERSION, + sh->flags & SDPCM_SHARED_VERSION_MASK); + return -EPROTO; + } + return 0; + +fail: + brcmf_err("unable to obtain sdpcm_shared info: rv=%d (addr=0x%x)\n", + rv, addr); + sdio_release_host(bus->sdiodev->func[1]); + return rv; +} + +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ + struct sdpcm_shared sh; + + if (brcmf_sdio_readshared(bus, &sh) == 0) + bus->console_addr = sh.console_addr; +} +#else +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ +} +#endif /* DEBUG */ + +static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) +{ + u32 intstatus = 0; + u32 hmb_data; + u8 fcbits; + int ret; + + brcmf_dbg(SDIO, "Enter\n"); + + /* Read mailbox data and ack that we did so */ + ret = r_sdreg32(bus, &hmb_data, + offsetof(struct sdpcmd_regs, tohostmailboxdata)); + + if (ret == 0) + w_sdreg32(bus, SMB_INT_ACK, + offsetof(struct sdpcmd_regs, tosbmailbox)); + bus->sdcnt.f1regdata += 2; + + /* Dongle recomposed rx frames, accept them again */ + if (hmb_data & HMB_DATA_NAKHANDLED) { + brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n", + bus->rx_seq); + if (!bus->rxskip) + brcmf_err("unexpected NAKHANDLED!\n"); + + bus->rxskip = false; + intstatus |= I_HMB_FRAME_IND; + } + + /* + * DEVREADY does not occur with gSPI. + */ + if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) { + bus->sdpcm_ver = + (hmb_data & HMB_DATA_VERSION_MASK) >> + HMB_DATA_VERSION_SHIFT; + if (bus->sdpcm_ver != SDPCM_PROT_VERSION) + brcmf_err("Version mismatch, dongle reports %d, " + "expecting %d\n", + bus->sdpcm_ver, SDPCM_PROT_VERSION); + else + brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n", + bus->sdpcm_ver); + + /* + * Retrieve console state address now that firmware should have + * updated it. + */ + brcmf_sdio_get_console_addr(bus); + } + + /* + * Flow Control has been moved into the RX headers and this out of band + * method isn't used any more. + * remaining backward compatible with older dongles. + */ + if (hmb_data & HMB_DATA_FC) { + fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >> + HMB_DATA_FCDATA_SHIFT; + + if (fcbits & ~bus->flowcontrol) + bus->sdcnt.fc_xoff++; + + if (bus->flowcontrol & ~fcbits) + bus->sdcnt.fc_xon++; + + bus->sdcnt.fc_rcvd++; + bus->flowcontrol = fcbits; + } + + /* Shouldn't be any others */ + if (hmb_data & ~(HMB_DATA_DEVREADY | + HMB_DATA_NAKHANDLED | + HMB_DATA_FC | + HMB_DATA_FWREADY | + HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) + brcmf_err("Unknown mailbox data content: 0x%02x\n", + hmb_data); + + return intstatus; +} + +static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) +{ + uint retries = 0; + u16 lastrbc; + u8 hi, lo; + int err; + + brcmf_err("%sterminate frame%s\n", + abort ? "abort command, " : "", + rtx ? ", send NAK" : ""); + + if (abort) + brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); + + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, + SFC_RF_TERM, &err); + bus->sdcnt.f1regdata++; + + /* Wait until the packet has been flushed (device/FIFO stable) */ + for (lastrbc = retries = 0xffff; retries > 0; retries--) { + hi = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_RFRAMEBCHI, &err); + lo = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_RFRAMEBCLO, &err); + bus->sdcnt.f1regdata += 2; + + if ((hi == 0) && (lo == 0)) + break; + + if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) { + brcmf_err("count growing: last 0x%04x now 0x%04x\n", + lastrbc, (hi << 8) + lo); + } + lastrbc = (hi << 8) + lo; + } + + if (!retries) + brcmf_err("count never zeroed: last 0x%04x\n", lastrbc); + else + brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries); + + if (rtx) { + bus->sdcnt.rxrtx++; + err = w_sdreg32(bus, SMB_NAK, + offsetof(struct sdpcmd_regs, tosbmailbox)); + + bus->sdcnt.f1regdata++; + if (err == 0) + bus->rxskip = true; + } + + /* Clear partial in any case */ + bus->cur_read.len = 0; +} + +static void brcmf_sdio_txfail(struct brcmf_sdio *bus) +{ + struct brcmf_sdio_dev *sdiodev = bus->sdiodev; + u8 i, hi, lo; + + /* On failure, abort the command and terminate the frame */ + brcmf_err("sdio error, abort command and terminate frame\n"); + bus->sdcnt.tx_sderrs++; + + brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2); + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); + bus->sdcnt.f1regdata++; + + for (i = 0; i < 3; i++) { + hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); + bus->sdcnt.f1regdata += 2; + if ((hi == 0) && (lo == 0)) + break; + } +} + +/* return total length of buffer chain */ +static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus) +{ + struct sk_buff *p; + uint total; + + total = 0; + skb_queue_walk(&bus->glom, p) + total += p->len; + return total; +} + +static void brcmf_sdio_free_glom(struct brcmf_sdio *bus) +{ + struct sk_buff *cur, *next; + + skb_queue_walk_safe(&bus->glom, cur, next) { + skb_unlink(cur, &bus->glom); + brcmu_pkt_buf_free_skb(cur); + } +} + +/** + * brcmfmac sdio bus specific header + * This is the lowest layer header wrapped on the packets transmitted between + * host and WiFi dongle which contains information needed for SDIO core and + * firmware + * + * It consists of 3 parts: hardware header, hardware extension header and + * software header + * hardware header (frame tag) - 4 bytes + * Byte 0~1: Frame length + * Byte 2~3: Checksum, bit-wise inverse of frame length + * hardware extension header - 8 bytes + * Tx glom mode only, N/A for Rx or normal Tx + * Byte 0~1: Packet length excluding hw frame tag + * Byte 2: Reserved + * Byte 3: Frame flags, bit 0: last frame indication + * Byte 4~5: Reserved + * Byte 6~7: Tail padding length + * software header - 8 bytes + * Byte 0: Rx/Tx sequence number + * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag + * Byte 2: Length of next data frame, reserved for Tx + * Byte 3: Data offset + * Byte 4: Flow control bits, reserved for Tx + * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet + * Byte 6~7: Reserved + */ +#define SDPCM_HWHDR_LEN 4 +#define SDPCM_HWEXT_LEN 8 +#define SDPCM_SWHDR_LEN 8 +#define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN) +/* software header */ +#define SDPCM_SEQ_MASK 0x000000ff +#define SDPCM_SEQ_WRAP 256 +#define SDPCM_CHANNEL_MASK 0x00000f00 +#define SDPCM_CHANNEL_SHIFT 8 +#define SDPCM_CONTROL_CHANNEL 0 /* Control */ +#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication */ +#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv */ +#define SDPCM_GLOM_CHANNEL 3 /* Coalesced packets */ +#define SDPCM_TEST_CHANNEL 15 /* Test/debug packets */ +#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80) +#define SDPCM_NEXTLEN_MASK 0x00ff0000 +#define SDPCM_NEXTLEN_SHIFT 16 +#define SDPCM_DOFFSET_MASK 0xff000000 +#define SDPCM_DOFFSET_SHIFT 24 +#define SDPCM_FCMASK_MASK 0x000000ff +#define SDPCM_WINDOW_MASK 0x0000ff00 +#define SDPCM_WINDOW_SHIFT 8 + +static inline u8 brcmf_sdio_getdatoffset(u8 *swheader) +{ + u32 hdrvalue; + hdrvalue = *(u32 *)swheader; + return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT); +} + +static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, + struct brcmf_sdio_hdrinfo *rd, + enum brcmf_sdio_frmtype type) +{ + u16 len, checksum; + u8 rx_seq, fc, tx_seq_max; + u32 swheader; + + trace_brcmf_sdpcm_hdr(SDPCM_RX, header); + + /* hw header */ + len = get_unaligned_le16(header); + checksum = get_unaligned_le16(header + sizeof(u16)); + /* All zero means no more to read */ + if (!(len | checksum)) { + bus->rxpending = false; + return -ENODATA; + } + if ((u16)(~(len ^ checksum))) { + brcmf_err("HW header checksum error\n"); + bus->sdcnt.rx_badhdr++; + brcmf_sdio_rxfail(bus, false, false); + return -EIO; + } + if (len < SDPCM_HDRLEN) { + brcmf_err("HW header length error\n"); + return -EPROTO; + } + if (type == BRCMF_SDIO_FT_SUPER && + (roundup(len, bus->blocksize) != rd->len)) { + brcmf_err("HW superframe header length error\n"); + return -EPROTO; + } + if (type == BRCMF_SDIO_FT_SUB && len > rd->len) { + brcmf_err("HW subframe header length error\n"); + return -EPROTO; + } + rd->len = len; + + /* software header */ + header += SDPCM_HWHDR_LEN; + swheader = le32_to_cpu(*(__le32 *)header); + if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) { + brcmf_err("Glom descriptor found in superframe head\n"); + rd->len = 0; + return -EINVAL; + } + rx_seq = (u8)(swheader & SDPCM_SEQ_MASK); + rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT; + if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL && + type != BRCMF_SDIO_FT_SUPER) { + brcmf_err("HW header length too long\n"); + bus->sdcnt.rx_toolong++; + brcmf_sdio_rxfail(bus, false, false); + rd->len = 0; + return -EPROTO; + } + if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) { + brcmf_err("Wrong channel for superframe\n"); + rd->len = 0; + return -EINVAL; + } + if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL && + rd->channel != SDPCM_EVENT_CHANNEL) { + brcmf_err("Wrong channel for subframe\n"); + rd->len = 0; + return -EINVAL; + } + rd->dat_offset = brcmf_sdio_getdatoffset(header); + if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { + brcmf_err("seq %d: bad data offset\n", rx_seq); + bus->sdcnt.rx_badhdr++; + brcmf_sdio_rxfail(bus, false, false); + rd->len = 0; + return -ENXIO; + } + if (rd->seq_num != rx_seq) { + brcmf_err("seq %d: sequence number error, expect %d\n", + rx_seq, rd->seq_num); + bus->sdcnt.rx_badseq++; + rd->seq_num = rx_seq; + } + /* no need to check the reset for subframe */ + if (type == BRCMF_SDIO_FT_SUB) + return 0; + rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT; + if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) { + /* only warm for NON glom packet */ + if (rd->channel != SDPCM_GLOM_CHANNEL) + brcmf_err("seq %d: next length error\n", rx_seq); + rd->len_nxtfrm = 0; + } + swheader = le32_to_cpu(*(__le32 *)(header + 4)); + fc = swheader & SDPCM_FCMASK_MASK; + if (bus->flowcontrol != fc) { + if (~bus->flowcontrol & fc) + bus->sdcnt.fc_xoff++; + if (bus->flowcontrol & ~fc) + bus->sdcnt.fc_xon++; + bus->sdcnt.fc_rcvd++; + bus->flowcontrol = fc; + } + tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT; + if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) { + brcmf_err("seq %d: max tx seq number error\n", rx_seq); + tx_seq_max = bus->tx_seq + 2; + } + bus->tx_max = tx_seq_max; + + return 0; +} + +static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length) +{ + *(__le16 *)header = cpu_to_le16(frm_length); + *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length); +} + +static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header, + struct brcmf_sdio_hdrinfo *hd_info) +{ + u32 hdrval; + u8 hdr_offset; + + brcmf_sdio_update_hwhdr(header, hd_info->len); + hdr_offset = SDPCM_HWHDR_LEN; + + if (bus->txglom) { + hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24); + *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); + hdrval = (u16)hd_info->tail_pad << 16; + *(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval); + hdr_offset += SDPCM_HWEXT_LEN; + } + + hdrval = hd_info->seq_num; + hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) & + SDPCM_CHANNEL_MASK; + hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) & + SDPCM_DOFFSET_MASK; + *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); + *(((__le32 *)(header + hdr_offset)) + 1) = 0; + trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header); +} + +static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) +{ + u16 dlen, totlen; + u8 *dptr, num = 0; + u16 sublen; + struct sk_buff *pfirst, *pnext; + + int errcode; + u8 doff, sfdoff; + + struct brcmf_sdio_hdrinfo rd_new; + + /* If packets, issue read(s) and send up packet chain */ + /* Return sequence numbers consumed? */ + + brcmf_dbg(SDIO, "start: glomd %p glom %p\n", + bus->glomd, skb_peek(&bus->glom)); + + /* If there's a descriptor, generate the packet chain */ + if (bus->glomd) { + pfirst = pnext = NULL; + dlen = (u16) (bus->glomd->len); + dptr = bus->glomd->data; + if (!dlen || (dlen & 1)) { + brcmf_err("bad glomd len(%d), ignore descriptor\n", + dlen); + dlen = 0; + } + + for (totlen = num = 0; dlen; num++) { + /* Get (and move past) next length */ + sublen = get_unaligned_le16(dptr); + dlen -= sizeof(u16); + dptr += sizeof(u16); + if ((sublen < SDPCM_HDRLEN) || + ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) { + brcmf_err("descriptor len %d bad: %d\n", + num, sublen); + pnext = NULL; + break; + } + if (sublen % bus->sgentry_align) { + brcmf_err("sublen %d not multiple of %d\n", + sublen, bus->sgentry_align); + } + totlen += sublen; + + /* For last frame, adjust read len so total + is a block multiple */ + if (!dlen) { + sublen += + (roundup(totlen, bus->blocksize) - totlen); + totlen = roundup(totlen, bus->blocksize); + } + + /* Allocate/chain packet for next subframe */ + pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align); + if (pnext == NULL) { + brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n", + num, sublen); + break; + } + skb_queue_tail(&bus->glom, pnext); + + /* Adhere to start alignment requirements */ + pkt_align(pnext, sublen, bus->sgentry_align); + } + + /* If all allocations succeeded, save packet chain + in bus structure */ + if (pnext) { + brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n", + totlen, num); + if (BRCMF_GLOM_ON() && bus->cur_read.len && + totlen != bus->cur_read.len) { + brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n", + bus->cur_read.len, totlen, rxseq); + } + pfirst = pnext = NULL; + } else { + brcmf_sdio_free_glom(bus); + num = 0; + } + + /* Done with descriptor packet */ + brcmu_pkt_buf_free_skb(bus->glomd); + bus->glomd = NULL; + bus->cur_read.len = 0; + } + + /* Ok -- either we just generated a packet chain, + or had one from before */ + if (!skb_queue_empty(&bus->glom)) { + if (BRCMF_GLOM_ON()) { + brcmf_dbg(GLOM, "try superframe read, packet chain:\n"); + skb_queue_walk(&bus->glom, pnext) { + brcmf_dbg(GLOM, " %p: %p len 0x%04x (%d)\n", + pnext, (u8 *) (pnext->data), + pnext->len, pnext->len); + } + } + + pfirst = skb_peek(&bus->glom); + dlen = (u16) brcmf_sdio_glom_len(bus); + + /* Do an SDIO read for the superframe. Configurable iovar to + * read directly into the chained packet, or allocate a large + * packet and and copy into the chain. + */ + sdio_claim_host(bus->sdiodev->func[1]); + errcode = brcmf_sdiod_recv_chain(bus->sdiodev, + &bus->glom, dlen); + sdio_release_host(bus->sdiodev->func[1]); + bus->sdcnt.f2rxdata++; + + /* On failure, kill the superframe, allow a couple retries */ + if (errcode < 0) { + brcmf_err("glom read of %d bytes failed: %d\n", + dlen, errcode); + + sdio_claim_host(bus->sdiodev->func[1]); + if (bus->glomerr++ < 3) { + brcmf_sdio_rxfail(bus, true, true); + } else { + bus->glomerr = 0; + brcmf_sdio_rxfail(bus, true, false); + bus->sdcnt.rxglomfail++; + brcmf_sdio_free_glom(bus); + } + sdio_release_host(bus->sdiodev->func[1]); + return 0; + } + + brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), + pfirst->data, min_t(int, pfirst->len, 48), + "SUPERFRAME:\n"); + + rd_new.seq_num = rxseq; + rd_new.len = dlen; + sdio_claim_host(bus->sdiodev->func[1]); + errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new, + BRCMF_SDIO_FT_SUPER); + sdio_release_host(bus->sdiodev->func[1]); + bus->cur_read.len = rd_new.len_nxtfrm << 4; + + /* Remove superframe header, remember offset */ + skb_pull(pfirst, rd_new.dat_offset); + sfdoff = rd_new.dat_offset; + num = 0; + + /* Validate all the subframe headers */ + skb_queue_walk(&bus->glom, pnext) { + /* leave when invalid subframe is found */ + if (errcode) + break; + + rd_new.len = pnext->len; + rd_new.seq_num = rxseq++; + sdio_claim_host(bus->sdiodev->func[1]); + errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new, + BRCMF_SDIO_FT_SUB); + sdio_release_host(bus->sdiodev->func[1]); + brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), + pnext->data, 32, "subframe:\n"); + + num++; + } + + if (errcode) { + /* Terminate frame on error, request + a couple retries */ + sdio_claim_host(bus->sdiodev->func[1]); + if (bus->glomerr++ < 3) { + /* Restore superframe header space */ + skb_push(pfirst, sfdoff); + brcmf_sdio_rxfail(bus, true, true); + } else { + bus->glomerr = 0; + brcmf_sdio_rxfail(bus, true, false); + bus->sdcnt.rxglomfail++; + brcmf_sdio_free_glom(bus); + } + sdio_release_host(bus->sdiodev->func[1]); + bus->cur_read.len = 0; + return 0; + } + + /* Basic SD framing looks ok - process each packet (header) */ + + skb_queue_walk_safe(&bus->glom, pfirst, pnext) { + dptr = (u8 *) (pfirst->data); + sublen = get_unaligned_le16(dptr); + doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]); + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), + dptr, pfirst->len, + "Rx Subframe Data:\n"); + + __skb_trim(pfirst, sublen); + skb_pull(pfirst, doff); + + if (pfirst->len == 0) { + skb_unlink(pfirst, &bus->glom); + brcmu_pkt_buf_free_skb(pfirst); + continue; + } + + brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), + pfirst->data, + min_t(int, pfirst->len, 32), + "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n", + bus->glom.qlen, pfirst, pfirst->data, + pfirst->len, pfirst->next, + pfirst->prev); + skb_unlink(pfirst, &bus->glom); + brcmf_rx_frame(bus->sdiodev->dev, pfirst); + bus->sdcnt.rxglompkts++; + } + + bus->sdcnt.rxglomframes++; + } + return num; +} + +static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, + bool *pending) +{ + DECLARE_WAITQUEUE(wait, current); + int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT); + + /* Wait until control frame is available */ + add_wait_queue(&bus->dcmd_resp_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + while (!(*condition) && (!signal_pending(current) && timeout)) + timeout = schedule_timeout(timeout); + + if (signal_pending(current)) + *pending = true; + + set_current_state(TASK_RUNNING); + remove_wait_queue(&bus->dcmd_resp_wait, &wait); + + return timeout; +} + +static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus) +{ + if (waitqueue_active(&bus->dcmd_resp_wait)) + wake_up_interruptible(&bus->dcmd_resp_wait); + + return 0; +} +static void +brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) +{ + uint rdlen, pad; + u8 *buf = NULL, *rbuf; + int sdret; + + brcmf_dbg(TRACE, "Enter\n"); + + if (bus->rxblen) + buf = vzalloc(bus->rxblen); + if (!buf) + goto done; + + rbuf = bus->rxbuf; + pad = ((unsigned long)rbuf % bus->head_align); + if (pad) + rbuf += (bus->head_align - pad); + + /* Copy the already-read portion over */ + memcpy(buf, hdr, BRCMF_FIRSTREAD); + if (len <= BRCMF_FIRSTREAD) + goto gotpkt; + + /* Raise rdlen to next SDIO block to avoid tail command */ + rdlen = len - BRCMF_FIRSTREAD; + if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { + pad = bus->blocksize - (rdlen % bus->blocksize); + if ((pad <= bus->roundup) && (pad < bus->blocksize) && + ((len + pad) < bus->sdiodev->bus_if->maxctl)) + rdlen += pad; + } else if (rdlen % bus->head_align) { + rdlen += bus->head_align - (rdlen % bus->head_align); + } + + /* Drop if the read is too big or it exceeds our maximum */ + if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) { + brcmf_err("%d-byte control read exceeds %d-byte buffer\n", + rdlen, bus->sdiodev->bus_if->maxctl); + brcmf_sdio_rxfail(bus, false, false); + goto done; + } + + if ((len - doff) > bus->sdiodev->bus_if->maxctl) { + brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", + len, len - doff, bus->sdiodev->bus_if->maxctl); + bus->sdcnt.rx_toolong++; + brcmf_sdio_rxfail(bus, false, false); + goto done; + } + + /* Read remain of frame body */ + sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen); + bus->sdcnt.f2rxdata++; + + /* Control frame failures need retransmission */ + if (sdret < 0) { + brcmf_err("read %d control bytes failed: %d\n", + rdlen, sdret); + bus->sdcnt.rxc_errors++; + brcmf_sdio_rxfail(bus, true, true); + goto done; + } else + memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen); + +gotpkt: + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), + buf, len, "RxCtrl:\n"); + + /* Point to valid data and indicate its length */ + spin_lock_bh(&bus->rxctl_lock); + if (bus->rxctl) { + brcmf_err("last control frame is being processed.\n"); + spin_unlock_bh(&bus->rxctl_lock); + vfree(buf); + goto done; + } + bus->rxctl = buf + doff; + bus->rxctl_orig = buf; + bus->rxlen = len - doff; + spin_unlock_bh(&bus->rxctl_lock); + +done: + /* Awake any waiters */ + brcmf_sdio_dcmd_resp_wake(bus); +} + +/* Pad read to blocksize for efficiency */ +static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) +{ + if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) { + *pad = bus->blocksize - (*rdlen % bus->blocksize); + if (*pad <= bus->roundup && *pad < bus->blocksize && + *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ) + *rdlen += *pad; + } else if (*rdlen % bus->head_align) { + *rdlen += bus->head_align - (*rdlen % bus->head_align); + } +} + +static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) +{ + struct sk_buff *pkt; /* Packet for event or data frames */ + u16 pad; /* Number of pad bytes to read */ + uint rxleft = 0; /* Remaining number of frames allowed */ + int ret; /* Return code from calls */ + uint rxcount = 0; /* Total frames read */ + struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new; + u8 head_read = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Not finished unless we encounter no more frames indication */ + bus->rxpending = true; + + for (rd->seq_num = bus->rx_seq, rxleft = maxframes; + !bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_SDIOD_DATA; + rd->seq_num++, rxleft--) { + + /* Handle glomming separately */ + if (bus->glomd || !skb_queue_empty(&bus->glom)) { + u8 cnt; + brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n", + bus->glomd, skb_peek(&bus->glom)); + cnt = brcmf_sdio_rxglom(bus, rd->seq_num); + brcmf_dbg(GLOM, "rxglom returned %d\n", cnt); + rd->seq_num += cnt - 1; + rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; + continue; + } + + rd->len_left = rd->len; + /* read header first for unknow frame length */ + sdio_claim_host(bus->sdiodev->func[1]); + if (!rd->len) { + ret = brcmf_sdiod_recv_buf(bus->sdiodev, + bus->rxhdr, BRCMF_FIRSTREAD); + bus->sdcnt.f2rxhdrs++; + if (ret < 0) { + brcmf_err("RXHEADER FAILED: %d\n", + ret); + bus->sdcnt.rx_hdrfail++; + brcmf_sdio_rxfail(bus, true, true); + sdio_release_host(bus->sdiodev->func[1]); + continue; + } + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(), + bus->rxhdr, SDPCM_HDRLEN, + "RxHdr:\n"); + + if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd, + BRCMF_SDIO_FT_NORMAL)) { + sdio_release_host(bus->sdiodev->func[1]); + if (!bus->rxpending) + break; + else + continue; + } + + if (rd->channel == SDPCM_CONTROL_CHANNEL) { + brcmf_sdio_read_control(bus, bus->rxhdr, + rd->len, + rd->dat_offset); + /* prepare the descriptor for the next read */ + rd->len = rd->len_nxtfrm << 4; + rd->len_nxtfrm = 0; + /* treat all packet as event if we don't know */ + rd->channel = SDPCM_EVENT_CHANNEL; + sdio_release_host(bus->sdiodev->func[1]); + continue; + } + rd->len_left = rd->len > BRCMF_FIRSTREAD ? + rd->len - BRCMF_FIRSTREAD : 0; + head_read = BRCMF_FIRSTREAD; + } + + brcmf_sdio_pad(bus, &pad, &rd->len_left); + + pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read + + bus->head_align); + if (!pkt) { + /* Give up on data, request rtx of events */ + brcmf_err("brcmu_pkt_buf_get_skb failed\n"); + brcmf_sdio_rxfail(bus, false, + RETRYCHAN(rd->channel)); + sdio_release_host(bus->sdiodev->func[1]); + continue; + } + skb_pull(pkt, head_read); + pkt_align(pkt, rd->len_left, bus->head_align); + + ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt); + bus->sdcnt.f2rxdata++; + sdio_release_host(bus->sdiodev->func[1]); + + if (ret < 0) { + brcmf_err("read %d bytes from channel %d failed: %d\n", + rd->len, rd->channel, ret); + brcmu_pkt_buf_free_skb(pkt); + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_rxfail(bus, true, + RETRYCHAN(rd->channel)); + sdio_release_host(bus->sdiodev->func[1]); + continue; + } + + if (head_read) { + skb_push(pkt, head_read); + memcpy(pkt->data, bus->rxhdr, head_read); + head_read = 0; + } else { + memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN); + rd_new.seq_num = rd->seq_num; + sdio_claim_host(bus->sdiodev->func[1]); + if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new, + BRCMF_SDIO_FT_NORMAL)) { + rd->len = 0; + brcmu_pkt_buf_free_skb(pkt); + } + bus->sdcnt.rx_readahead_cnt++; + if (rd->len != roundup(rd_new.len, 16)) { + brcmf_err("frame length mismatch:read %d, should be %d\n", + rd->len, + roundup(rd_new.len, 16) >> 4); + rd->len = 0; + brcmf_sdio_rxfail(bus, true, true); + sdio_release_host(bus->sdiodev->func[1]); + brcmu_pkt_buf_free_skb(pkt); + continue; + } + sdio_release_host(bus->sdiodev->func[1]); + rd->len_nxtfrm = rd_new.len_nxtfrm; + rd->channel = rd_new.channel; + rd->dat_offset = rd_new.dat_offset; + + brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && + BRCMF_DATA_ON()) && + BRCMF_HDRS_ON(), + bus->rxhdr, SDPCM_HDRLEN, + "RxHdr:\n"); + + if (rd_new.channel == SDPCM_CONTROL_CHANNEL) { + brcmf_err("readahead on control packet %d?\n", + rd_new.seq_num); + /* Force retry w/normal header read */ + rd->len = 0; + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_rxfail(bus, false, true); + sdio_release_host(bus->sdiodev->func[1]); + brcmu_pkt_buf_free_skb(pkt); + continue; + } + } + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), + pkt->data, rd->len, "Rx Data:\n"); + + /* Save superframe descriptor and allocate packet frame */ + if (rd->channel == SDPCM_GLOM_CHANNEL) { + if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) { + brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n", + rd->len); + brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), + pkt->data, rd->len, + "Glom Data:\n"); + __skb_trim(pkt, rd->len); + skb_pull(pkt, SDPCM_HDRLEN); + bus->glomd = pkt; + } else { + brcmf_err("%s: glom superframe w/o " + "descriptor!\n", __func__); + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_rxfail(bus, false, false); + sdio_release_host(bus->sdiodev->func[1]); + } + /* prepare the descriptor for the next read */ + rd->len = rd->len_nxtfrm << 4; + rd->len_nxtfrm = 0; + /* treat all packet as event if we don't know */ + rd->channel = SDPCM_EVENT_CHANNEL; + continue; + } + + /* Fill in packet len and prio, deliver upward */ + __skb_trim(pkt, rd->len); + skb_pull(pkt, rd->dat_offset); + + /* prepare the descriptor for the next read */ + rd->len = rd->len_nxtfrm << 4; + rd->len_nxtfrm = 0; + /* treat all packet as event if we don't know */ + rd->channel = SDPCM_EVENT_CHANNEL; + + if (pkt->len == 0) { + brcmu_pkt_buf_free_skb(pkt); + continue; + } + + brcmf_rx_frame(bus->sdiodev->dev, pkt); + } + + rxcount = maxframes - rxleft; + /* Message if we hit the limit */ + if (!rxleft) + brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes); + else + brcmf_dbg(DATA, "processed %d frames\n", rxcount); + /* Back off rxseq if awaiting rtx, update rx_seq */ + if (bus->rxskip) + rd->seq_num--; + bus->rx_seq = rd->seq_num; + + return rxcount; +} + +static void +brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus) +{ + if (waitqueue_active(&bus->ctrl_wait)) + wake_up_interruptible(&bus->ctrl_wait); + return; +} + +static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt) +{ + u16 head_pad; + u8 *dat_buf; + + dat_buf = (u8 *)(pkt->data); + + /* Check head padding */ + head_pad = ((unsigned long)dat_buf % bus->head_align); + if (head_pad) { + if (skb_headroom(pkt) < head_pad) { + bus->sdiodev->bus_if->tx_realloc++; + head_pad = 0; + if (skb_cow(pkt, head_pad)) + return -ENOMEM; + } + skb_push(pkt, head_pad); + dat_buf = (u8 *)(pkt->data); + memset(dat_buf, 0, head_pad + bus->tx_hdrlen); + } + return head_pad; +} + +/** + * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for + * bus layer usage. + */ +/* flag marking a dummy skb added for DMA alignment requirement */ +#define ALIGN_SKB_FLAG 0x8000 +/* bit mask of data length chopped from the previous packet */ +#define ALIGN_SKB_CHOP_LEN_MASK 0x7fff + +static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, + struct sk_buff_head *pktq, + struct sk_buff *pkt, u16 total_len) +{ + struct brcmf_sdio_dev *sdiodev; + struct sk_buff *pkt_pad; + u16 tail_pad, tail_chop, chain_pad; + unsigned int blksize; + bool lastfrm; + int ntail, ret; + + sdiodev = bus->sdiodev; + blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize; + /* sg entry alignment should be a divisor of block size */ + WARN_ON(blksize % bus->sgentry_align); + + /* Check tail padding */ + lastfrm = skb_queue_is_last(pktq, pkt); + tail_pad = 0; + tail_chop = pkt->len % bus->sgentry_align; + if (tail_chop) + tail_pad = bus->sgentry_align - tail_chop; + chain_pad = (total_len + tail_pad) % blksize; + if (lastfrm && chain_pad) + tail_pad += blksize - chain_pad; + if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) { + pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop + + bus->head_align); + if (pkt_pad == NULL) + return -ENOMEM; + ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad); + if (unlikely(ret < 0)) { + kfree_skb(pkt_pad); + return ret; + } + memcpy(pkt_pad->data, + pkt->data + pkt->len - tail_chop, + tail_chop); + *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; + skb_trim(pkt, pkt->len - tail_chop); + skb_trim(pkt_pad, tail_pad + tail_chop); + __skb_queue_after(pktq, pkt, pkt_pad); + } else { + ntail = pkt->data_len + tail_pad - + (pkt->end - pkt->tail); + if (skb_cloned(pkt) || ntail > 0) + if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC)) + return -ENOMEM; + if (skb_linearize(pkt)) + return -ENOMEM; + __skb_put(pkt, tail_pad); + } + + return tail_pad; +} + +/** + * brcmf_sdio_txpkt_prep - packet preparation for transmit + * @bus: brcmf_sdio structure pointer + * @pktq: packet list pointer + * @chan: virtual channel to transmit the packet + * + * Processes to be applied to the packet + * - Align data buffer pointer + * - Align data buffer length + * - Prepare header + * Return: negative value if there is error + */ +static int +brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, + uint chan) +{ + u16 head_pad, total_len; + struct sk_buff *pkt_next; + u8 txseq; + int ret; + struct brcmf_sdio_hdrinfo hd_info = {0}; + + txseq = bus->tx_seq; + total_len = 0; + skb_queue_walk(pktq, pkt_next) { + /* alignment packet inserted in previous + * loop cycle can be skipped as it is + * already properly aligned and does not + * need an sdpcm header. + */ + if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG) + continue; + + /* align packet data pointer */ + ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next); + if (ret < 0) + return ret; + head_pad = (u16)ret; + if (head_pad) + memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad); + + total_len += pkt_next->len; + + hd_info.len = pkt_next->len; + hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next); + if (bus->txglom && pktq->qlen > 1) { + ret = brcmf_sdio_txpkt_prep_sg(bus, pktq, + pkt_next, total_len); + if (ret < 0) + return ret; + hd_info.tail_pad = (u16)ret; + total_len += (u16)ret; + } + + hd_info.channel = chan; + hd_info.dat_offset = head_pad + bus->tx_hdrlen; + hd_info.seq_num = txseq++; + + /* Now fill the header */ + brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info); + + if (BRCMF_BYTES_ON() && + ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || + (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) + brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len, + "Tx Frame:\n"); + else if (BRCMF_HDRS_ON()) + brcmf_dbg_hex_dump(true, pkt_next->data, + head_pad + bus->tx_hdrlen, + "Tx Header:\n"); + } + /* Hardware length tag of the first packet should be total + * length of the chain (including padding) + */ + if (bus->txglom) + brcmf_sdio_update_hwhdr(pktq->next->data, total_len); + return 0; +} + +/** + * brcmf_sdio_txpkt_postp - packet post processing for transmit + * @bus: brcmf_sdio structure pointer + * @pktq: packet list pointer + * + * Processes to be applied to the packet + * - Remove head padding + * - Remove tail padding + */ +static void +brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) +{ + u8 *hdr; + u32 dat_offset; + u16 tail_pad; + u16 dummy_flags, chop_len; + struct sk_buff *pkt_next, *tmp, *pkt_prev; + + skb_queue_walk_safe(pktq, pkt_next, tmp) { + dummy_flags = *(u16 *)(pkt_next->cb); + if (dummy_flags & ALIGN_SKB_FLAG) { + chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK; + if (chop_len) { + pkt_prev = pkt_next->prev; + skb_put(pkt_prev, chop_len); + } + __skb_unlink(pkt_next, pktq); + brcmu_pkt_buf_free_skb(pkt_next); + } else { + hdr = pkt_next->data + bus->tx_hdrlen - SDPCM_SWHDR_LEN; + dat_offset = le32_to_cpu(*(__le32 *)hdr); + dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >> + SDPCM_DOFFSET_SHIFT; + skb_pull(pkt_next, dat_offset); + if (bus->txglom) { + tail_pad = le16_to_cpu(*(__le16 *)(hdr - 2)); + skb_trim(pkt_next, pkt_next->len - tail_pad); + } + } + } +} + +/* Writes a HW/SW header into the packet and sends it. */ +/* Assumes: (a) header space already there, (b) caller holds lock */ +static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, + uint chan) +{ + int ret; + struct sk_buff *pkt_next, *tmp; + + brcmf_dbg(TRACE, "Enter\n"); + + ret = brcmf_sdio_txpkt_prep(bus, pktq, chan); + if (ret) + goto done; + + sdio_claim_host(bus->sdiodev->func[1]); + ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq); + bus->sdcnt.f2txdata++; + + if (ret < 0) + brcmf_sdio_txfail(bus); + + sdio_release_host(bus->sdiodev->func[1]); + +done: + brcmf_sdio_txpkt_postp(bus, pktq); + if (ret == 0) + bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP; + skb_queue_walk_safe(pktq, pkt_next, tmp) { + __skb_unlink(pkt_next, pktq); + brcmf_txcomplete(bus->sdiodev->dev, pkt_next, ret == 0); + } + return ret; +} + +static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) +{ + struct sk_buff *pkt; + struct sk_buff_head pktq; + u32 intstatus = 0; + int ret = 0, prec_out, i; + uint cnt = 0; + u8 tx_prec_map, pkt_num; + + brcmf_dbg(TRACE, "Enter\n"); + + tx_prec_map = ~bus->flowcontrol; + + /* Send frames until the limit or some other event */ + for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { + pkt_num = 1; + if (bus->txglom) + pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, + bus->sdiodev->txglomsz); + pkt_num = min_t(u32, pkt_num, + brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)); + __skb_queue_head_init(&pktq); + spin_lock_bh(&bus->txq_lock); + for (i = 0; i < pkt_num; i++) { + pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, + &prec_out); + if (pkt == NULL) + break; + __skb_queue_tail(&pktq, pkt); + } + spin_unlock_bh(&bus->txq_lock); + if (i == 0) + break; + + ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); + + cnt += i; + + /* In poll mode, need to check for other events */ + if (!bus->intr) { + /* Check device status, signal pending interrupt */ + sdio_claim_host(bus->sdiodev->func[1]); + ret = r_sdreg32(bus, &intstatus, + offsetof(struct sdpcmd_regs, + intstatus)); + sdio_release_host(bus->sdiodev->func[1]); + bus->sdcnt.f2txdata++; + if (ret != 0) + break; + if (intstatus & bus->hostintmask) + atomic_set(&bus->ipend, 1); + } + } + + /* Deflow-control stack if needed */ + if ((bus->sdiodev->state == BRCMF_SDIOD_DATA) && + bus->txoff && (pktq_len(&bus->txq) < TXLOW)) { + bus->txoff = false; + brcmf_txflowblock(bus->sdiodev->dev, false); + } + + return cnt; +} + +static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len) +{ + u8 doff; + u16 pad; + uint retries = 0; + struct brcmf_sdio_hdrinfo hd_info = {0}; + int ret; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Back the pointer to make room for bus header */ + frame -= bus->tx_hdrlen; + len += bus->tx_hdrlen; + + /* Add alignment padding (optional for ctl frames) */ + doff = ((unsigned long)frame % bus->head_align); + if (doff) { + frame -= doff; + len += doff; + memset(frame + bus->tx_hdrlen, 0, doff); + } + + /* Round send length to next SDIO block */ + pad = 0; + if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { + pad = bus->blocksize - (len % bus->blocksize); + if ((pad > bus->roundup) || (pad >= bus->blocksize)) + pad = 0; + } else if (len % bus->head_align) { + pad = bus->head_align - (len % bus->head_align); + } + len += pad; + + hd_info.len = len - pad; + hd_info.channel = SDPCM_CONTROL_CHANNEL; + hd_info.dat_offset = doff + bus->tx_hdrlen; + hd_info.seq_num = bus->tx_seq; + hd_info.lastfrm = true; + hd_info.tail_pad = pad; + brcmf_sdio_hdpack(bus, frame, &hd_info); + + if (bus->txglom) + brcmf_sdio_update_hwhdr(frame, len); + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), + frame, len, "Tx Frame:\n"); + brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && + BRCMF_HDRS_ON(), + frame, min_t(u16, len, 16), "TxHdr:\n"); + + do { + ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); + + if (ret < 0) + brcmf_sdio_txfail(bus); + else + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + } while (ret < 0 && retries++ < TXRETRIES); + + return ret; +} + +static void brcmf_sdio_bus_stop(struct device *dev) +{ + u32 local_hostintmask; + u8 saveclk; + int err; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + + brcmf_dbg(TRACE, "Enter\n"); + + if (bus->watchdog_tsk) { + send_sig(SIGTERM, bus->watchdog_tsk, 1); + kthread_stop(bus->watchdog_tsk); + bus->watchdog_tsk = NULL; + } + + if (sdiodev->state != BRCMF_SDIOD_NOMEDIUM) { + 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); + + /* Turn off the bus (F2), free any pending packets */ + brcmf_dbg(INTR, "disable SDIO interrupts\n"); + sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); + + /* Clear any pending interrupts now that F2 is disabled */ + w_sdreg32(bus, local_hostintmask, + offsetof(struct sdpcmd_regs, intstatus)); + + sdio_release_host(sdiodev->func[1]); + } + /* Clear the data packet queues */ + brcmu_pktq_flush(&bus->txq, true, NULL, NULL); + + /* Clear any held glomming stuff */ + brcmu_pkt_buf_free_skb(bus->glomd); + brcmf_sdio_free_glom(bus); + + /* Clear rx control and wake any waiters */ + spin_lock_bh(&bus->rxctl_lock); + bus->rxlen = 0; + spin_unlock_bh(&bus->rxctl_lock); + brcmf_sdio_dcmd_resp_wake(bus); + + /* Reset some F2 state stuff */ + bus->rxskip = false; + bus->tx_seq = bus->rx_seq = 0; +} + +static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) +{ + unsigned long flags; + + if (bus->sdiodev->oob_irq_requested) { + spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags); + if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) { + enable_irq(bus->sdiodev->pdata->oob_irq_nr); + bus->sdiodev->irq_en = true; + } + spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags); + } +} + +static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) +{ + struct brcmf_core *buscore; + u32 addr; + unsigned long val; + int ret; + + buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); + + val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); + bus->sdcnt.f1regdata++; + if (ret != 0) + return ret; + + val &= bus->hostintmask; + atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); + + /* Clear interrupts */ + if (val) { + brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); + bus->sdcnt.f1regdata++; + atomic_or(val, &bus->intstatus); + } + + return ret; +} + +static void brcmf_sdio_dpc(struct brcmf_sdio *bus) +{ + u32 newstatus = 0; + unsigned long intstatus; + uint txlimit = bus->txbound; /* Tx frames to send before resched */ + uint framecnt; /* Temporary counter of tx/rx frames */ + int err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + sdio_claim_host(bus->sdiodev->func[1]); + + /* If waiting for HTAVAIL, check status */ + if (!bus->sr_enabled && bus->clkstate == CLK_PENDING) { + u8 clkctl, devctl = 0; + +#ifdef DEBUG + /* Check for inconsistent device control */ + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); +#endif /* DEBUG */ + + /* Read CSR, if clock on switch to AVAIL, else ignore */ + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); + + brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", + devctl, clkctl); + + if (SBSDIO_HTAV(clkctl)) { + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); + bus->clkstate = CLK_AVAIL; + } + } + + /* Make sure backplane clock is on */ + brcmf_sdio_bus_sleep(bus, false, true); + + /* Pending interrupt indicates new device status */ + if (atomic_read(&bus->ipend) > 0) { + atomic_set(&bus->ipend, 0); + err = brcmf_sdio_intr_rstatus(bus); + } + + /* Start with leftover status bits */ + intstatus = atomic_xchg(&bus->intstatus, 0); + + /* Handle flow-control change: read new state in case our ack + * crossed another change interrupt. If change still set, assume + * FC ON for safety, let next loop through do the debounce. + */ + if (intstatus & I_HMB_FC_CHANGE) { + intstatus &= ~I_HMB_FC_CHANGE; + err = w_sdreg32(bus, I_HMB_FC_CHANGE, + offsetof(struct sdpcmd_regs, intstatus)); + + err = r_sdreg32(bus, &newstatus, + offsetof(struct sdpcmd_regs, intstatus)); + bus->sdcnt.f1regdata += 2; + atomic_set(&bus->fcstate, + !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE))); + intstatus |= (newstatus & bus->hostintmask); + } + + /* Handle host mailbox indication */ + if (intstatus & I_HMB_HOST_INT) { + intstatus &= ~I_HMB_HOST_INT; + intstatus |= brcmf_sdio_hostmail(bus); + } + + sdio_release_host(bus->sdiodev->func[1]); + + /* Generally don't ask for these, can get CRC errors... */ + if (intstatus & I_WR_OOSYNC) { + brcmf_err("Dongle reports WR_OOSYNC\n"); + intstatus &= ~I_WR_OOSYNC; + } + + if (intstatus & I_RD_OOSYNC) { + brcmf_err("Dongle reports RD_OOSYNC\n"); + intstatus &= ~I_RD_OOSYNC; + } + + if (intstatus & I_SBINT) { + brcmf_err("Dongle reports SBINT\n"); + intstatus &= ~I_SBINT; + } + + /* Would be active due to wake-wlan in gSPI */ + if (intstatus & I_CHIPACTIVE) { + brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n"); + intstatus &= ~I_CHIPACTIVE; + } + + /* Ignore frame indications if rxskip is set */ + if (bus->rxskip) + intstatus &= ~I_HMB_FRAME_IND; + + /* On frame indication, read available frames */ + if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) { + brcmf_sdio_readframes(bus, bus->rxbound); + if (!bus->rxpending) + intstatus &= ~I_HMB_FRAME_IND; + } + + /* Keep still-pending events for next scheduling */ + if (intstatus) + atomic_or(intstatus, &bus->intstatus); + + brcmf_sdio_clrintr(bus); + + if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && + data_ok(bus)) { + sdio_claim_host(bus->sdiodev->func[1]); + if (bus->ctrl_frame_stat) { + err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, + bus->ctrl_frame_len); + bus->ctrl_frame_err = err; + wmb(); + bus->ctrl_frame_stat = false; + } + sdio_release_host(bus->sdiodev->func[1]); + brcmf_sdio_wait_event_wakeup(bus); + } + /* Send queued frames (limit 1 if rx may still be pending) */ + if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && + brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && + data_ok(bus)) { + framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : + txlimit; + brcmf_sdio_sendfromq(bus, framecnt); + } + + if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) { + brcmf_err("failed backplane access over SDIO, halting operation\n"); + atomic_set(&bus->intstatus, 0); + if (bus->ctrl_frame_stat) { + sdio_claim_host(bus->sdiodev->func[1]); + if (bus->ctrl_frame_stat) { + bus->ctrl_frame_err = -ENODEV; + wmb(); + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); + } + sdio_release_host(bus->sdiodev->func[1]); + } + } else if (atomic_read(&bus->intstatus) || + atomic_read(&bus->ipend) > 0 || + (!atomic_read(&bus->fcstate) && + brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && + data_ok(bus))) { + bus->dpc_triggered = true; + } +} + +static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + + return &bus->txq; +} + +static bool brcmf_sdio_prec_enq(struct pktq *q, struct sk_buff *pkt, int prec) +{ + struct sk_buff *p; + int eprec = -1; /* precedence to evict from */ + + /* Fast case, precedence queue is not full and we are also not + * exceeding total queue length + */ + if (!pktq_pfull(q, prec) && !pktq_full(q)) { + brcmu_pktq_penq(q, prec, pkt); + return true; + } + + /* Determine precedence from which to evict packet, if any */ + if (pktq_pfull(q, prec)) { + eprec = prec; + } else if (pktq_full(q)) { + p = brcmu_pktq_peek_tail(q, &eprec); + if (eprec > prec) + return false; + } + + /* Evict if needed */ + if (eprec >= 0) { + /* Detect queueing to unconfigured precedence */ + if (eprec == prec) + return false; /* refuse newer (incoming) packet */ + /* Evict packet according to discard policy */ + p = brcmu_pktq_pdeq_tail(q, eprec); + if (p == NULL) + brcmf_err("brcmu_pktq_pdeq_tail() failed\n"); + brcmu_pkt_buf_free_skb(p); + } + + /* Enqueue */ + p = brcmu_pktq_penq(q, prec, pkt); + if (p == NULL) + brcmf_err("brcmu_pktq_penq() failed\n"); + + return p != NULL; +} + +static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) +{ + int ret = -EBADE; + uint prec; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + + brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); + if (sdiodev->state != BRCMF_SDIOD_DATA) + return -EIO; + + /* Add space for the header */ + skb_push(pkt, bus->tx_hdrlen); + /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */ + + prec = prio2prec((pkt->priority & PRIOMASK)); + + /* Check for existing queue, current flow-control, + pending event, or pending clock */ + brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq)); + bus->sdcnt.fcqueued++; + + /* Priority based enq */ + spin_lock_bh(&bus->txq_lock); + /* reset bus_flags in packet cb */ + *(u16 *)(pkt->cb) = 0; + if (!brcmf_sdio_prec_enq(&bus->txq, pkt, prec)) { + skb_pull(pkt, bus->tx_hdrlen); + brcmf_err("out of bus->txq !!!\n"); + ret = -ENOSR; + } else { + ret = 0; + } + + if (pktq_len(&bus->txq) >= TXHI) { + bus->txoff = true; + brcmf_txflowblock(dev, true); + } + spin_unlock_bh(&bus->txq_lock); + +#ifdef DEBUG + if (pktq_plen(&bus->txq, prec) > qcount[prec]) + qcount[prec] = pktq_plen(&bus->txq, prec); +#endif + + brcmf_sdio_trigger_dpc(bus); + return ret; +} + +#ifdef DEBUG +#define CONSOLE_LINE_MAX 192 + +static int brcmf_sdio_readconsole(struct brcmf_sdio *bus) +{ + struct brcmf_console *c = &bus->console; + u8 line[CONSOLE_LINE_MAX], ch; + u32 n, idx, addr; + int rv; + + /* Don't do anything until FWREADY updates console address */ + if (bus->console_addr == 0) + return 0; + + /* Read console log struct */ + addr = bus->console_addr + offsetof(struct rte_console, log_le); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le, + sizeof(c->log_le)); + if (rv < 0) + return rv; + + /* Allocate console buffer (one time only) */ + if (c->buf == NULL) { + c->bufsize = le32_to_cpu(c->log_le.buf_size); + c->buf = kmalloc(c->bufsize, GFP_ATOMIC); + if (c->buf == NULL) + return -ENOMEM; + } + + idx = le32_to_cpu(c->log_le.idx); + + /* Protect against corrupt value */ + if (idx > c->bufsize) + return -EBADE; + + /* Skip reading the console buffer if the index pointer + has not moved */ + if (idx == c->last) + return 0; + + /* Read the console buffer */ + addr = le32_to_cpu(c->log_le.buf); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize); + if (rv < 0) + return rv; + + while (c->last != idx) { + for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { + if (c->last == idx) { + /* This would output a partial line. + * Instead, back up + * the buffer pointer and output this + * line next time around. + */ + if (c->last >= n) + c->last -= n; + else + c->last = c->bufsize - n; + goto break2; + } + ch = c->buf[c->last]; + c->last = (c->last + 1) % c->bufsize; + if (ch == '\n') + break; + line[n] = ch; + } + + if (n > 0) { + if (line[n - 1] == '\r') + n--; + line[n] = 0; + pr_debug("CONSOLE: %s\n", line); + } + } +break2: + + return 0; +} +#endif /* DEBUG */ + +static int +brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + int ret; + + brcmf_dbg(TRACE, "Enter\n"); + if (sdiodev->state != BRCMF_SDIOD_DATA) + return -EIO; + + /* Send from dpc */ + bus->ctrl_frame_buf = msg; + bus->ctrl_frame_len = msglen; + wmb(); + bus->ctrl_frame_stat = true; + + brcmf_sdio_trigger_dpc(bus); + wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, + msecs_to_jiffies(CTL_DONE_TIMEOUT)); + ret = 0; + if (bus->ctrl_frame_stat) { + sdio_claim_host(bus->sdiodev->func[1]); + if (bus->ctrl_frame_stat) { + brcmf_dbg(SDIO, "ctrl_frame timeout\n"); + bus->ctrl_frame_stat = false; + ret = -ETIMEDOUT; + } + sdio_release_host(bus->sdiodev->func[1]); + } + if (!ret) { + brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n", + bus->ctrl_frame_err); + rmb(); + ret = bus->ctrl_frame_err; + } + + if (ret) + bus->sdcnt.tx_ctlerrs++; + else + bus->sdcnt.tx_ctlpkts++; + + return ret; +} + +#ifdef DEBUG +static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + u32 addr, console_ptr, console_size, console_index; + char *conbuf = NULL; + __le32 sh_val; + int rv; + + /* obtain console information from device memory */ + addr = sh->console_addr + offsetof(struct rte_console, log_le); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); + if (rv < 0) + return rv; + console_ptr = le32_to_cpu(sh_val); + + addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); + if (rv < 0) + return rv; + console_size = le32_to_cpu(sh_val); + + addr = sh->console_addr + offsetof(struct rte_console, log_le.idx); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); + if (rv < 0) + return rv; + console_index = le32_to_cpu(sh_val); + + /* allocate buffer for console data */ + if (console_size <= CONSOLE_BUFFER_MAX) + conbuf = vzalloc(console_size+1); + + if (!conbuf) + return -ENOMEM; + + /* obtain the console data from device */ + conbuf[console_size] = '\0'; + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf, + console_size); + if (rv < 0) + goto done; + + rv = seq_write(seq, conbuf + console_index, + console_size - console_index); + if (rv < 0) + goto done; + + if (console_index > 0) + rv = seq_write(seq, conbuf, console_index - 1); + +done: + vfree(conbuf); + return rv; +} + +static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + int error; + struct brcmf_trap_info tr; + + if ((sh->flags & SDPCM_SHARED_TRAP) == 0) { + brcmf_dbg(INFO, "no trap in firmware\n"); + return 0; + } + + error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr, + sizeof(struct brcmf_trap_info)); + if (error < 0) + return error; + + seq_printf(seq, + "dongle trap info: type 0x%x @ epc 0x%08x\n" + " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" + " lr 0x%08x pc 0x%08x offset 0x%x\n" + " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" + " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", + le32_to_cpu(tr.type), le32_to_cpu(tr.epc), + le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), + le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), + le32_to_cpu(tr.pc), sh->trap_addr, + le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), + le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), + le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), + le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); + + return 0; +} + +static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + int error = 0; + char file[80] = "?"; + char expr[80] = ""; + + if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { + brcmf_dbg(INFO, "firmware not built with -assert\n"); + return 0; + } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) { + brcmf_dbg(INFO, "no assert in dongle\n"); + return 0; + } + + sdio_claim_host(bus->sdiodev->func[1]); + if (sh->assert_file_addr != 0) { + error = brcmf_sdiod_ramrw(bus->sdiodev, false, + sh->assert_file_addr, (u8 *)file, 80); + if (error < 0) + return error; + } + if (sh->assert_exp_addr != 0) { + error = brcmf_sdiod_ramrw(bus->sdiodev, false, + sh->assert_exp_addr, (u8 *)expr, 80); + if (error < 0) + return error; + } + sdio_release_host(bus->sdiodev->func[1]); + + seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n", + file, sh->assert_line, expr); + return 0; +} + +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) +{ + int error; + struct sdpcm_shared sh; + + error = brcmf_sdio_readshared(bus, &sh); + + if (error < 0) + return error; + + if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) + brcmf_dbg(INFO, "firmware not built with -assert\n"); + else if (sh.flags & SDPCM_SHARED_ASSERT) + brcmf_err("assertion in dongle\n"); + + if (sh.flags & SDPCM_SHARED_TRAP) + brcmf_err("firmware trap in dongle\n"); + + return 0; +} + +static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus) +{ + int error = 0; + struct sdpcm_shared sh; + + error = brcmf_sdio_readshared(bus, &sh); + if (error < 0) + goto done; + + error = brcmf_sdio_assert_info(seq, bus, &sh); + if (error < 0) + goto done; + + error = brcmf_sdio_trap_info(seq, bus, &sh); + if (error < 0) + goto done; + + error = brcmf_sdio_dump_console(seq, bus, &sh); + +done: + return error; +} + +static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus; + + return brcmf_sdio_died_dump(seq, bus); +} + +static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt; + + seq_printf(seq, + "intrcount: %u\nlastintrs: %u\n" + "pollcnt: %u\nregfails: %u\n" + "tx_sderrs: %u\nfcqueued: %u\n" + "rxrtx: %u\nrx_toolong: %u\n" + "rxc_errors: %u\nrx_hdrfail: %u\n" + "rx_badhdr: %u\nrx_badseq: %u\n" + "fc_rcvd: %u\nfc_xoff: %u\n" + "fc_xon: %u\nrxglomfail: %u\n" + "rxglomframes: %u\nrxglompkts: %u\n" + "f2rxhdrs: %u\nf2rxdata: %u\n" + "f2txdata: %u\nf1regdata: %u\n" + "tickcnt: %u\ntx_ctlerrs: %lu\n" + "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" + "rx_ctlpkts: %lu\nrx_readahead: %lu\n", + sdcnt->intrcount, sdcnt->lastintrs, + sdcnt->pollcnt, sdcnt->regfails, + sdcnt->tx_sderrs, sdcnt->fcqueued, + sdcnt->rxrtx, sdcnt->rx_toolong, + sdcnt->rxc_errors, sdcnt->rx_hdrfail, + sdcnt->rx_badhdr, sdcnt->rx_badseq, + sdcnt->fc_rcvd, sdcnt->fc_xoff, + sdcnt->fc_xon, sdcnt->rxglomfail, + sdcnt->rxglomframes, sdcnt->rxglompkts, + sdcnt->f2rxhdrs, sdcnt->f2rxdata, + sdcnt->f2txdata, sdcnt->f1regdata, + sdcnt->tickcnt, sdcnt->tx_ctlerrs, + sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, + sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); + + return 0; +} + +static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) +{ + struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr; + struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); + + if (IS_ERR_OR_NULL(dentry)) + return; + + bus->console_interval = BRCMF_CONSOLE; + + brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read); + brcmf_debugfs_add_entry(drvr, "counters", + brcmf_debugfs_sdio_count_read); + debugfs_create_u32("console_interval", 0644, dentry, + &bus->console_interval); +} +#else +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) +{ + return 0; +} + +static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) +{ +} +#endif /* DEBUG */ + +static int +brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) +{ + int timeleft; + uint rxlen = 0; + bool pending; + u8 *buf; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + + brcmf_dbg(TRACE, "Enter\n"); + if (sdiodev->state != BRCMF_SDIOD_DATA) + return -EIO; + + /* Wait until control frame is available */ + timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending); + + spin_lock_bh(&bus->rxctl_lock); + rxlen = bus->rxlen; + memcpy(msg, bus->rxctl, min(msglen, rxlen)); + bus->rxctl = NULL; + buf = bus->rxctl_orig; + bus->rxctl_orig = NULL; + bus->rxlen = 0; + spin_unlock_bh(&bus->rxctl_lock); + vfree(buf); + + if (rxlen) { + brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n", + rxlen, msglen); + } else if (timeleft == 0) { + brcmf_err("resumed on timeout\n"); + brcmf_sdio_checkdied(bus); + } else if (pending) { + brcmf_dbg(CTL, "cancelled\n"); + return -ERESTARTSYS; + } else { + brcmf_dbg(CTL, "resumed for unknown reason?\n"); + brcmf_sdio_checkdied(bus); + } + + if (rxlen) + bus->sdcnt.rx_ctlpkts++; + else + bus->sdcnt.rx_ctlerrs++; + + return rxlen ? (int)rxlen : -ETIMEDOUT; +} + +#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) +{ + int err; + + brcmf_dbg(TRACE, "Enter\n"); + + err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase, + (u8 *)fw->data, fw->size); + if (err) + brcmf_err("error %d on writing %d membytes at 0x%08x\n", + err, (int)fw->size, bus->ci->rambase); + else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, + (u8 *)fw->data, fw->size)) + err = -EIO; + + return err; +} + +static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, + void *vars, u32 varsz) +{ + int address; + int err; + + brcmf_dbg(TRACE, "Enter\n"); + + 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; + + return err; +} + +static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, + const struct firmware *fw, + void *nvram, u32 nvlen) +{ + int bcmerror = -EFAULT; + u32 rstvec; + + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + + 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"); + brcmf_fw_nvram_free(nvram); + goto err; + } + + bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen); + brcmf_fw_nvram_free(nvram); + if (bcmerror) { + brcmf_err("dongle nvram file download failed\n"); + goto err; + } + + /* Take arm out of reset */ + if (!brcmf_chip_set_active(bus->ci, rstvec)) { + brcmf_err("error getting out of ARM core reset\n"); + goto err; + } + + /* Allow full data communication using DPC from now on. */ + brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA); + bcmerror = 0; + +err: + brcmf_sdio_clkctl(bus, CLK_SDONLY, false); + sdio_release_host(bus->sdiodev->func[1]); + return bcmerror; +} + +static void brcmf_sdio_sr_init(struct brcmf_sdio *bus) +{ + int err = 0; + u8 val; + + brcmf_dbg(TRACE, "Enter\n"); + + val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err); + if (err) { + brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n"); + return; + } + + val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err); + if (err) { + brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n"); + return; + } + + /* Add CMD14 Support */ + brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP, + (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | + SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT), + &err); + if (err) { + brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n"); + return; + } + + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_FORCE_HT, &err); + if (err) { + brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n"); + return; + } + + /* set flag */ + bus->sr_enabled = true; + brcmf_dbg(INFO, "SR enabled\n"); +} + +/* enable KSO bit */ +static int brcmf_sdio_kso_init(struct brcmf_sdio *bus) +{ + u8 val; + int err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + /* KSO bit added in SDIO core rev 12 */ + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) + return 0; + + val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err); + if (err) { + brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n"); + return err; + } + + if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) { + val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN << + SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + val, &err); + if (err) { + brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n"); + return err; + } + } + + return 0; +} + + +static int brcmf_sdio_bus_preinit(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + uint pad_size; + u32 value; + int err; + + /* the commands below use the terms tx and rx from + * a device perspective, ie. bus:txglom affects the + * bus transfers from device to host. + */ + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) { + /* for sdio core rev < 12, disable txgloming */ + value = 0; + err = brcmf_iovar_data_set(dev, "bus:txglom", &value, + sizeof(u32)); + } else { + /* otherwise, set txglomalign */ + value = 4; + if (sdiodev->pdata) + value = sdiodev->pdata->sd_sgentry_align; + /* SDIO ADMA requires at least 32 bit alignment */ + value = max_t(u32, value, 4); + err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value, + sizeof(u32)); + } + + if (err < 0) + goto done; + + bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; + if (sdiodev->sg_support) { + bus->txglom = false; + value = 1; + pad_size = bus->sdiodev->func[2]->cur_blksize << 1; + err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom", + &value, sizeof(u32)); + if (err < 0) { + /* bus:rxglom is allowed to fail */ + err = 0; + } else { + bus->txglom = true; + bus->tx_hdrlen += SDPCM_HWEXT_LEN; + } + } + brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen); + +done: + return err; +} + +static size_t brcmf_sdio_bus_get_ramsize(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + + return bus->ci->ramsize - bus->ci->srsize; +} + +static int brcmf_sdio_bus_get_memdump(struct device *dev, void *data, + size_t mem_size) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + int err; + int address; + int offset; + int len; + + brcmf_dbg(INFO, "dump at 0x%08x: size=%zu\n", bus->ci->rambase, + mem_size); + + address = bus->ci->rambase; + offset = err = 0; + sdio_claim_host(sdiodev->func[1]); + while (offset < mem_size) { + len = ((offset + MEMBLOCK) < mem_size) ? MEMBLOCK : + mem_size - offset; + err = brcmf_sdiod_ramrw(sdiodev, false, address, data, len); + if (err) { + brcmf_err("error %d on reading %d membytes at 0x%08x\n", + err, len, address); + goto done; + } + data += len; + offset += len; + address += len; + } + +done: + sdio_release_host(sdiodev->func[1]); + return err; +} + +void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus) +{ + if (!bus->dpc_triggered) { + bus->dpc_triggered = true; + queue_work(bus->brcmf_wq, &bus->datawork); + } +} + +void brcmf_sdio_isr(struct brcmf_sdio *bus) +{ + brcmf_dbg(TRACE, "Enter\n"); + + if (!bus) { + brcmf_err("bus is null pointer, exiting\n"); + return; + } + + /* Count the interrupt call */ + bus->sdcnt.intrcount++; + if (in_interrupt()) + atomic_set(&bus->ipend, 1); + else + if (brcmf_sdio_intr_rstatus(bus)) { + brcmf_err("failed backplane access\n"); + } + + /* Disable additional interrupts (is this needed now)? */ + if (!bus->intr) + brcmf_err("isr w/o interrupt configured!\n"); + + bus->dpc_triggered = true; + queue_work(bus->brcmf_wq, &bus->datawork); +} + +static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) +{ + brcmf_dbg(TIMER, "Enter\n"); + + /* Poll period: check device if appropriate. */ + if (!bus->sr_enabled && + bus->poll && (++bus->polltick >= bus->pollrate)) { + u32 intstatus = 0; + + /* Reset poll tick */ + bus->polltick = 0; + + /* Check device if no interrupts */ + if (!bus->intr || + (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { + + if (!bus->dpc_triggered) { + u8 devpend; + + sdio_claim_host(bus->sdiodev->func[1]); + devpend = brcmf_sdiod_regrb(bus->sdiodev, + SDIO_CCCR_INTx, + NULL); + sdio_release_host(bus->sdiodev->func[1]); + intstatus = devpend & (INTR_STATUS_FUNC1 | + INTR_STATUS_FUNC2); + } + + /* If there is something, make like the ISR and + schedule the DPC */ + if (intstatus) { + bus->sdcnt.pollcnt++; + atomic_set(&bus->ipend, 1); + + bus->dpc_triggered = true; + queue_work(bus->brcmf_wq, &bus->datawork); + } + } + + /* Update interrupt tracking */ + bus->sdcnt.lastintrs = bus->sdcnt.intrcount; + } +#ifdef DEBUG + /* Poll for console output periodically */ + if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() && + bus->console_interval != 0) { + bus->console.count += BRCMF_WD_POLL_MS; + if (bus->console.count >= bus->console_interval) { + bus->console.count -= bus->console_interval; + sdio_claim_host(bus->sdiodev->func[1]); + /* Make sure backplane clock is on */ + brcmf_sdio_bus_sleep(bus, false, false); + if (brcmf_sdio_readconsole(bus) < 0) + /* stop on error */ + bus->console_interval = 0; + sdio_release_host(bus->sdiodev->func[1]); + } + } +#endif /* DEBUG */ + + /* On idle timeout clear activity flag and/or turn off clock */ + if (!bus->dpc_triggered) { + rmb(); + if ((!bus->dpc_running) && (bus->idletime > 0) && + (bus->clkstate == CLK_AVAIL)) { + bus->idlecount++; + if (bus->idlecount > bus->idletime) { + brcmf_dbg(SDIO, "idle\n"); + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_wd_timer(bus, 0); + bus->idlecount = 0; + brcmf_sdio_bus_sleep(bus, true, false); + sdio_release_host(bus->sdiodev->func[1]); + } + } else { + bus->idlecount = 0; + } + } else { + bus->idlecount = 0; + } +} + +static void brcmf_sdio_dataworker(struct work_struct *work) +{ + struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio, + datawork); + + bus->dpc_running = true; + wmb(); + while (ACCESS_ONCE(bus->dpc_triggered)) { + bus->dpc_triggered = false; + brcmf_sdio_dpc(bus); + bus->idlecount = 0; + } + bus->dpc_running = false; + if (brcmf_sdiod_freezing(bus->sdiodev)) { + brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN); + brcmf_sdiod_try_freeze(bus->sdiodev); + brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA); + } +} + +static void +brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u32 drivestrength) +{ + const struct sdiod_drive_str *str_tab = NULL; + u32 str_mask; + u32 str_shift; + u32 base; + u32 i; + u32 drivestrength_sel = 0; + u32 cc_data_temp; + u32 addr; + + if (!(ci->cc_caps & CC_CAP_PMU)) + return; + + switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { + case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12): + str_tab = sdiod_drvstr_tab1_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17): + str_tab = sdiod_drvstr_tab6_1v8; + str_mask = 0x00001800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17): + /* note: 43143 does not support tristate */ + i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; + if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { + str_tab = sdiod_drvstr_tab2_3v3; + str_mask = 0x00000007; + str_shift = 0; + } else + brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", + ci->name, drivestrength); + break; + case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13): + str_tab = sdiod_drive_strength_tab5_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + default: + brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", + ci->name, ci->chiprev, ci->pmurev); + break; + } + + if (str_tab != NULL) { + for (i = 0; str_tab[i].strength != 0; i++) { + if (drivestrength >= str_tab[i].strength) { + drivestrength_sel = str_tab[i].sel; + break; + } + } + base = brcmf_chip_get_chipcommon(ci)->base; + addr = CORE_CC_REG(base, chipcontrol_addr); + brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); + cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); + cc_data_temp &= ~str_mask; + drivestrength_sel <<= str_shift; + cc_data_temp |= drivestrength_sel; + brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); + + brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", + str_tab[i].strength, drivestrength, cc_data_temp); + } +} + +static int brcmf_sdio_buscoreprep(void *ctx) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + int err = 0; + u8 clkval, clkset; + + /* Try forcing SDIO core to do ALPAvail request only */ + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + if (err) { + brcmf_err("error writing for HT off\n"); + return err; + } + + /* If register supported, wait for ALPAvail and then force ALP */ + /* This may take up to 15 milliseconds */ + clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL); + + if ((clkval & ~SBSDIO_AVBITS) != clkset) { + brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", + clkset, clkval); + return -EACCES; + } + + SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), + !SBSDIO_ALPAV(clkval)), + PMU_MAX_TRANSITION_DLY); + if (!SBSDIO_ALPAV(clkval)) { + brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", + clkval); + return -EBUSY; + } + + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + udelay(65); + + /* Also, disable the extra SDIO pull-ups */ + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); + + return 0; +} + +static void brcmf_sdio_buscore_activate(void *ctx, struct brcmf_chip *chip, + u32 rstvec) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + struct brcmf_core *core; + u32 reg_addr; + + /* clear all interrupts */ + core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV); + reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus); + brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + + if (rstvec) + /* Write reset vector to address 0 */ + brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, + sizeof(rstvec)); +} + +static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + u32 val, rev; + + val = brcmf_sdiod_regrl(sdiodev, addr, NULL); + if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && + addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { + rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; + if (rev >= 2) { + val &= ~CID_ID_MASK; + val |= BRCM_CC_4339_CHIP_ID; + } + } + return val; +} + +static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + + brcmf_sdiod_regwl(sdiodev, addr, val, NULL); +} + +static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { + .prepare = brcmf_sdio_buscoreprep, + .activate = brcmf_sdio_buscore_activate, + .read32 = brcmf_sdio_buscore_read32, + .write32 = brcmf_sdio_buscore_write32, +}; + +static bool +brcmf_sdio_probe_attach(struct brcmf_sdio *bus) +{ + u8 clkctl = 0; + int err = 0; + int reg_addr; + u32 reg_val; + u32 drivestrength; + + sdio_claim_host(bus->sdiodev->func[1]); + + pr_debug("F1 signature read @0x18000000=0x%4x\n", + brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); + + /* + * Force PLL off until brcmf_chip_attach() + * programs PLL control regs + */ + + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + BRCMF_INIT_CLKCTL1, &err); + if (!err) + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); + + if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) { + brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", + err, BRCMF_INIT_CLKCTL1, clkctl); + goto fail; + } + + bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); + if (IS_ERR(bus->ci)) { + brcmf_err("brcmf_chip_attach failed!\n"); + bus->ci = NULL; + goto fail; + } + + if (brcmf_sdio_kso_init(bus)) { + brcmf_err("error enabling KSO\n"); + goto fail; + } + + if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength)) + drivestrength = bus->sdiodev->pdata->drive_strength; + else + drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; + brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); + + /* Set card control so an SDIO card reset does a WLAN backplane reset */ + reg_val = brcmf_sdiod_regrb(bus->sdiodev, + SDIO_CCCR_BRCM_CARDCTRL, &err); + if (err) + goto fail; + + reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET; + + brcmf_sdiod_regwb(bus->sdiodev, + SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); + if (err) + goto fail; + + /* set PMUControl so a backplane reset does PMU state reload */ + reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base, + pmucontrol); + reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err); + if (err) + goto fail; + + reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); + + brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err); + if (err) + goto fail; + + sdio_release_host(bus->sdiodev->func[1]); + + brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); + + /* allocate header buffer */ + bus->hdrbuf = kzalloc(MAX_HDR_READ + bus->head_align, GFP_KERNEL); + if (!bus->hdrbuf) + return false; + /* Locate an appropriately-aligned portion of hdrbuf */ + bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0], + bus->head_align); + + /* Set the poll and/or interrupt flags */ + bus->intr = true; + bus->poll = false; + if (bus->poll) + bus->pollrate = 1; + + return true; + +fail: + sdio_release_host(bus->sdiodev->func[1]); + return false; +} + +static int +brcmf_sdio_watchdog_thread(void *data) +{ + struct brcmf_sdio *bus = (struct brcmf_sdio *)data; + int wait; + + allow_signal(SIGTERM); + /* Run until signal received */ + brcmf_sdiod_freezer_count(bus->sdiodev); + while (1) { + if (kthread_should_stop()) + break; + brcmf_sdiod_freezer_uncount(bus->sdiodev); + wait = wait_for_completion_interruptible(&bus->watchdog_wait); + brcmf_sdiod_freezer_count(bus->sdiodev); + brcmf_sdiod_try_freeze(bus->sdiodev); + if (!wait) { + brcmf_sdio_bus_watchdog(bus); + /* Count the tick for reference */ + bus->sdcnt.tickcnt++; + reinit_completion(&bus->watchdog_wait); + } else + break; + } + return 0; +} + +static void +brcmf_sdio_watchdog(unsigned long data) +{ + struct brcmf_sdio *bus = (struct brcmf_sdio *)data; + + if (bus->watchdog_tsk) { + complete(&bus->watchdog_wait); + /* Reschedule the watchdog */ + if (bus->wd_timer_valid) + mod_timer(&bus->timer, + jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); + } +} + +static struct brcmf_bus_ops brcmf_sdio_bus_ops = { + .stop = brcmf_sdio_bus_stop, + .preinit = brcmf_sdio_bus_preinit, + .txdata = brcmf_sdio_bus_txdata, + .txctl = brcmf_sdio_bus_txctl, + .rxctl = brcmf_sdio_bus_rxctl, + .gettxq = brcmf_sdio_bus_gettxq, + .wowl_config = brcmf_sdio_wowl_config, + .get_ramsize = brcmf_sdio_bus_get_ramsize, + .get_memdump = brcmf_sdio_bus_get_memdump, +}; + +static void brcmf_sdio_firmware_callback(struct device *dev, + const struct firmware *code, + void *nvram, u32 nvram_len) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + int err = 0; + u8 saveclk; + + brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev)); + + if (!bus_if->drvr) + return; + + /* try to download image and nvram to the dongle */ + bus->alp_only = true; + err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); + if (err) + goto fail; + bus->alp_only = false; + + /* Start the watchdog timer */ + bus->sdcnt.tickcnt = 0; + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + + sdio_claim_host(sdiodev->func[1]); + + /* Make sure backplane clock is on, needed to generate F2 interrupt */ + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + if (bus->clkstate != CLK_AVAIL) + goto release; + + /* Force clocks on backplane to be sure 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); + goto release; + } + + /* Enable function 2 (frame transfers) */ + w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, + offsetof(struct sdpcmd_regs, tosbmailboxdata)); + err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]); + + + brcmf_dbg(INFO, "enable F2: err=%d\n", err); + + /* If F2 successfully enabled, set core and enable interrupts */ + if (!err) { + /* Set up the interrupt mask and enable interrupts */ + bus->hostintmask = HOSTINTMASK; + w_sdreg32(bus, bus->hostintmask, + offsetof(struct sdpcmd_regs, hostintmask)); + + brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err); + } else { + /* Disable F2 again */ + sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); + goto release; + } + + if (brcmf_chip_sr_capable(bus->ci)) { + brcmf_sdio_sr_init(bus); + } else { + /* Restore previous clock setting */ + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + saveclk, &err); + } + + if (err == 0) { + err = brcmf_sdiod_intr_register(sdiodev); + if (err != 0) + brcmf_err("intr register failed:%d\n", err); + } + + /* If we didn't come up, turn off backplane clock */ + if (err != 0) + brcmf_sdio_clkctl(bus, CLK_NONE, false); + + sdio_release_host(sdiodev->func[1]); + + err = brcmf_bus_start(dev); + if (err != 0) { + brcmf_err("dongle is not responding\n"); + goto fail; + } + return; + +release: + sdio_release_host(sdiodev->func[1]); +fail: + brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err); + device_release_driver(dev); +} + +struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) +{ + int ret; + struct brcmf_sdio *bus; + struct workqueue_struct *wq; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Allocate private bus interface state */ + bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC); + if (!bus) + goto fail; + + bus->sdiodev = sdiodev; + sdiodev->bus = bus; + skb_queue_head_init(&bus->glom); + bus->txbound = BRCMF_TXBOUND; + bus->rxbound = BRCMF_RXBOUND; + bus->txminmax = BRCMF_TXMINMAX; + bus->tx_seq = SDPCM_SEQ_WRAP - 1; + + /* platform specific configuration: + * alignments must be at least 4 bytes for ADMA + */ + bus->head_align = ALIGNMENT; + bus->sgentry_align = ALIGNMENT; + if (sdiodev->pdata) { + if (sdiodev->pdata->sd_head_align > ALIGNMENT) + bus->head_align = sdiodev->pdata->sd_head_align; + if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT) + bus->sgentry_align = sdiodev->pdata->sd_sgentry_align; + } + + /* single-threaded workqueue */ + wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM, + dev_name(&sdiodev->func[1]->dev)); + if (!wq) { + brcmf_err("insufficient memory to create txworkqueue\n"); + goto fail; + } + brcmf_sdiod_freezer_count(sdiodev); + INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); + bus->brcmf_wq = wq; + + /* attempt to attach to the dongle */ + if (!(brcmf_sdio_probe_attach(bus))) { + brcmf_err("brcmf_sdio_probe_attach failed\n"); + goto fail; + } + + spin_lock_init(&bus->rxctl_lock); + spin_lock_init(&bus->txq_lock); + init_waitqueue_head(&bus->ctrl_wait); + init_waitqueue_head(&bus->dcmd_resp_wait); + + /* Set up the watchdog timer */ + init_timer(&bus->timer); + bus->timer.data = (unsigned long)bus; + bus->timer.function = brcmf_sdio_watchdog; + + /* Initialize watchdog thread */ + init_completion(&bus->watchdog_wait); + bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread, + bus, "brcmf_wdog/%s", + dev_name(&sdiodev->func[1]->dev)); + if (IS_ERR(bus->watchdog_tsk)) { + pr_warn("brcmf_watchdog thread failed to start\n"); + bus->watchdog_tsk = NULL; + } + /* Initialize DPC thread */ + bus->dpc_triggered = false; + bus->dpc_running = false; + + /* Assign bus interface call back */ + bus->sdiodev->bus_if->dev = bus->sdiodev->dev; + bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops; + bus->sdiodev->bus_if->chip = bus->ci->chip; + bus->sdiodev->bus_if->chiprev = bus->ci->chiprev; + + /* default sdio bus header length for tx packet */ + bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; + + /* Attach to the common layer, reserve hdr space */ + ret = brcmf_attach(bus->sdiodev->dev); + if (ret != 0) { + brcmf_err("brcmf_attach failed\n"); + goto fail; + } + + /* Query the F2 block size, set roundup accordingly */ + bus->blocksize = bus->sdiodev->func[2]->cur_blksize; + bus->roundup = min(max_roundup, bus->blocksize); + + /* Allocate buffers */ + if (bus->sdiodev->bus_if->maxctl) { + bus->sdiodev->bus_if->maxctl += bus->roundup; + bus->rxblen = + roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), + ALIGNMENT) + bus->head_align; + bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC); + if (!(bus->rxbuf)) { + brcmf_err("rxbuf allocation failed\n"); + goto fail; + } + } + + sdio_claim_host(bus->sdiodev->func[1]); + + /* Disable F2 to clear any intermediate frame state on the dongle */ + sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); + + bus->rxflow = false; + + /* Done with backplane-dependent accesses, can drop clock... */ + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); + + sdio_release_host(bus->sdiodev->func[1]); + + /* ...and initialize clock/power states */ + bus->clkstate = CLK_SDONLY; + bus->idletime = BRCMF_IDLE_INTERVAL; + bus->idleclock = BRCMF_IDLE_ACTIVE; + + /* SR state */ + bus->sr_enabled = false; + + brcmf_sdio_debugfs_create(bus); + brcmf_dbg(INFO, "completed!!\n"); + + ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev); + if (ret) + goto fail; + + ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, + sdiodev->fw_name, sdiodev->nvram_name, + brcmf_sdio_firmware_callback); + if (ret != 0) { + brcmf_err("async firmware request failed: %d\n", ret); + goto fail; + } + + return bus; + +fail: + brcmf_sdio_remove(bus); + return NULL; +} + +/* Detach and free everything */ +void brcmf_sdio_remove(struct brcmf_sdio *bus) +{ + brcmf_dbg(TRACE, "Enter\n"); + + if (bus) { + /* De-register interrupt handler */ + brcmf_sdiod_intr_unregister(bus->sdiodev); + + brcmf_detach(bus->sdiodev->dev); + + cancel_work_sync(&bus->datawork); + if (bus->brcmf_wq) + destroy_workqueue(bus->brcmf_wq); + + if (bus->ci) { + if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) { + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_wd_timer(bus, 0); + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + /* Leave the device in state where it is + * 'passive'. This is done by resetting all + * necessary cores. + */ + msleep(20); + brcmf_chip_set_passive(bus->ci); + brcmf_sdio_clkctl(bus, CLK_NONE, false); + sdio_release_host(bus->sdiodev->func[1]); + } + brcmf_chip_detach(bus->ci); + } + + kfree(bus->rxbuf); + kfree(bus->hdrbuf); + kfree(bus); + } + + brcmf_dbg(TRACE, "Disconnected\n"); +} + +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) +{ + /* Totally stop the timer */ + if (!wdtick && bus->wd_timer_valid) { + del_timer_sync(&bus->timer); + bus->wd_timer_valid = false; + bus->save_ms = wdtick; + return; + } + + /* don't start the wd until fw is loaded */ + if (bus->sdiodev->state != BRCMF_SDIOD_DATA) + return; + + if (wdtick) { + if (bus->save_ms != BRCMF_WD_POLL_MS) { + if (bus->wd_timer_valid) + /* Stop timer and restart at new value */ + del_timer_sync(&bus->timer); + + /* Create timer again when watchdog period is + dynamically changed or in the first instance + */ + bus->timer.expires = + jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS); + add_timer(&bus->timer); + + } else { + /* Re arm the timer, at last watchdog period */ + mod_timer(&bus->timer, + jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); + } + + bus->wd_timer_valid = true; + bus->save_ms = wdtick; + } +} + +int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep) +{ + int ret; + + sdio_claim_host(bus->sdiodev->func[1]); + ret = brcmf_sdio_bus_sleep(bus, sleep, false); + sdio_release_host(bus->sdiodev->func[1]); + + return ret; +} + diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h new file mode 100644 index 000000000000..7328478b2d7b --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2010 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_SDIO_H +#define BRCMFMAC_SDIO_H + +#include +#include +#include "firmware.h" + +#define SDIO_FUNC_0 0 +#define SDIO_FUNC_1 1 +#define SDIO_FUNC_2 2 + +#define SDIOD_FBR_SIZE 0x100 + +/* io_en */ +#define SDIO_FUNC_ENABLE_1 0x02 +#define SDIO_FUNC_ENABLE_2 0x04 + +/* io_rdys */ +#define SDIO_FUNC_READY_1 0x02 +#define SDIO_FUNC_READY_2 0x04 + +/* intr_status */ +#define INTR_STATUS_FUNC1 0x2 +#define INTR_STATUS_FUNC2 0x4 + +/* Maximum number of I/O funcs */ +#define SDIOD_MAX_IOFUNCS 7 + +/* mask of register map */ +#define REG_F0_REG_MASK 0x7FF +#define REG_F1_MISC_MASK 0x1FFFF + +/* as of sdiod rev 0, supports 3 functions */ +#define SBSDIO_NUM_FUNCTION 3 + +/* function 0 vendor specific CCCR registers */ +#define SDIO_CCCR_BRCM_CARDCAP 0xf0 +#define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02 +#define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04 +#define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08 +#define SDIO_CCCR_BRCM_CARDCTRL 0xf1 +#define SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02 +#define SDIO_CCCR_BRCM_SEPINT 0xf2 + +#define SDIO_SEPINT_MASK 0x01 +#define SDIO_SEPINT_OE 0x02 +#define SDIO_SEPINT_ACT_HI 0x04 + +/* function 1 miscellaneous registers */ + +/* sprom command and status */ +#define SBSDIO_SPROM_CS 0x10000 +/* sprom info register */ +#define SBSDIO_SPROM_INFO 0x10001 +/* sprom indirect access data byte 0 */ +#define SBSDIO_SPROM_DATA_LOW 0x10002 +/* sprom indirect access data byte 1 */ +#define SBSDIO_SPROM_DATA_HIGH 0x10003 +/* sprom indirect access addr byte 0 */ +#define SBSDIO_SPROM_ADDR_LOW 0x10004 +/* gpio select */ +#define SBSDIO_GPIO_SELECT 0x10005 +/* gpio output */ +#define SBSDIO_GPIO_OUT 0x10006 +/* gpio enable */ +#define SBSDIO_GPIO_EN 0x10007 +/* rev < 7, watermark for sdio device */ +#define SBSDIO_WATERMARK 0x10008 +/* control busy signal generation */ +#define SBSDIO_DEVICE_CTL 0x10009 + +/* SB Address Window Low (b15) */ +#define SBSDIO_FUNC1_SBADDRLOW 0x1000A +/* SB Address Window Mid (b23:b16) */ +#define SBSDIO_FUNC1_SBADDRMID 0x1000B +/* SB Address Window High (b31:b24) */ +#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C +/* Frame Control (frame term/abort) */ +#define SBSDIO_FUNC1_FRAMECTRL 0x1000D +/* ChipClockCSR (ALP/HT ctl/status) */ +#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E +/* SdioPullUp (on cmd, d0-d2) */ +#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F +/* Write Frame Byte Count Low */ +#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019 +/* Write Frame Byte Count High */ +#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A +/* Read Frame Byte Count Low */ +#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B +/* Read Frame Byte Count High */ +#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C +/* MesBusyCtl (rev 11) */ +#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D +/* Sdio Core Rev 12 */ +#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E +#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1 +#define SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT 0 +#define SBSDIO_FUNC1_WCTRL_HTWAIT_MASK 0x2 +#define SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT 1 +#define SBSDIO_FUNC1_SLEEPCSR 0x1001F +#define SBSDIO_FUNC1_SLEEPCSR_KSO_MASK 0x1 +#define SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT 0 +#define SBSDIO_FUNC1_SLEEPCSR_KSO_EN 1 +#define SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK 0x2 +#define SBSDIO_FUNC1_SLEEPCSR_DEVON_SHIFT 1 + +#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */ +#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001F /* f1 misc register end */ + +/* function 1 OCP space */ + +/* sb offset addr is <= 15 bits, 32k */ +#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF +#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000 +/* with b15, maps to 32-bit SB access */ +#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000 + +/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */ + +#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */ +#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */ +#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */ +/* Address bits from SBADDR regs */ +#define SBSDIO_SBWINDOW_MASK 0xffff8000 + +#define SDIOH_READ 0 /* Read request */ +#define SDIOH_WRITE 1 /* Write request */ + +#define SDIOH_DATA_FIX 0 /* Fixed addressing */ +#define SDIOH_DATA_INC 1 /* Incremental addressing */ + +/* internal return code */ +#define SUCCESS 0 +#define ERROR 1 + +/* Packet alignment for most efficient SDIO (can change based on platform) */ +#define BRCMF_SDALIGN (1 << 6) + +/* watchdog polling interval in ms */ +#define BRCMF_WD_POLL_MS 10 + +/** + * enum brcmf_sdiod_state - the state of the bus. + * + * @BRCMF_SDIOD_DOWN: Device can be accessed, no DPC. + * @BRCMF_SDIOD_DATA: Ready for data transfers, DPC enabled. + * @BRCMF_SDIOD_NOMEDIUM: No medium access to dongle possible. + */ +enum brcmf_sdiod_state { + BRCMF_SDIOD_DOWN, + BRCMF_SDIOD_DATA, + BRCMF_SDIOD_NOMEDIUM +}; + +struct brcmf_sdreg { + int func; + int offset; + int value; +}; + +struct brcmf_sdio; +struct brcmf_sdiod_freezer; + +struct brcmf_sdio_dev { + struct sdio_func *func[SDIO_MAX_FUNCS]; + u8 num_funcs; /* Supported funcs on client */ + u32 sbwad; /* Save backplane window address */ + struct brcmf_sdio *bus; + struct device *dev; + struct brcmf_bus *bus_if; + struct brcmfmac_sdio_platform_data *pdata; + bool oob_irq_requested; + bool irq_en; /* irq enable flags */ + spinlock_t irq_en_lock; + bool irq_wake; /* irq wake enable flags */ + bool sg_support; + uint max_request_size; + ushort max_segment_count; + uint max_segment_size; + uint txglomsz; + struct sg_table sgtable; + char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + bool wowl_enabled; + enum brcmf_sdiod_state state; + struct brcmf_sdiod_freezer *freezer; +}; + +/* sdio core registers */ +struct sdpcmd_regs { + u32 corecontrol; /* 0x00, rev8 */ + u32 corestatus; /* rev8 */ + u32 PAD[1]; + u32 biststatus; /* rev8 */ + + /* PCMCIA access */ + u16 pcmciamesportaladdr; /* 0x010, rev8 */ + u16 PAD[1]; + u16 pcmciamesportalmask; /* rev8 */ + u16 PAD[1]; + u16 pcmciawrframebc; /* rev8 */ + u16 PAD[1]; + u16 pcmciaunderflowtimer; /* rev8 */ + u16 PAD[1]; + + /* interrupt */ + u32 intstatus; /* 0x020, rev8 */ + u32 hostintmask; /* rev8 */ + u32 intmask; /* rev8 */ + u32 sbintstatus; /* rev8 */ + u32 sbintmask; /* rev8 */ + u32 funcintmask; /* rev4 */ + u32 PAD[2]; + u32 tosbmailbox; /* 0x040, rev8 */ + u32 tohostmailbox; /* rev8 */ + u32 tosbmailboxdata; /* rev8 */ + u32 tohostmailboxdata; /* rev8 */ + + /* synchronized access to registers in SDIO clock domain */ + u32 sdioaccess; /* 0x050, rev8 */ + u32 PAD[3]; + + /* PCMCIA frame control */ + u8 pcmciaframectrl; /* 0x060, rev8 */ + u8 PAD[3]; + u8 pcmciawatermark; /* rev8 */ + u8 PAD[155]; + + /* interrupt batching control */ + u32 intrcvlazy; /* 0x100, rev8 */ + u32 PAD[3]; + + /* counters */ + u32 cmd52rd; /* 0x110, rev8 */ + u32 cmd52wr; /* rev8 */ + u32 cmd53rd; /* rev8 */ + u32 cmd53wr; /* rev8 */ + u32 abort; /* rev8 */ + u32 datacrcerror; /* rev8 */ + u32 rdoutofsync; /* rev8 */ + u32 wroutofsync; /* rev8 */ + u32 writebusy; /* rev8 */ + u32 readwait; /* rev8 */ + u32 readterm; /* rev8 */ + u32 writeterm; /* rev8 */ + u32 PAD[40]; + u32 clockctlstatus; /* rev8 */ + u32 PAD[7]; + + u32 PAD[128]; /* DMA engines */ + + /* SDIO/PCMCIA CIS region */ + char cis[512]; /* 0x400-0x5ff, rev6 */ + + /* PCMCIA function control registers */ + char pcmciafcr[256]; /* 0x600-6ff, rev6 */ + u16 PAD[55]; + + /* PCMCIA backplane access */ + u16 backplanecsr; /* 0x76E, rev6 */ + u16 backplaneaddr0; /* rev6 */ + u16 backplaneaddr1; /* rev6 */ + u16 backplaneaddr2; /* rev6 */ + u16 backplaneaddr3; /* rev6 */ + u16 backplanedata0; /* rev6 */ + u16 backplanedata1; /* rev6 */ + u16 backplanedata2; /* rev6 */ + u16 backplanedata3; /* rev6 */ + u16 PAD[31]; + + /* sprom "size" & "blank" info */ + u16 spromstatus; /* 0x7BE, rev2 */ + u32 PAD[464]; + + u16 PAD[0x80]; +}; + +/* Register/deregister interrupt handler. */ +int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev); +int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev); + +/* sdio device register access interface */ +u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); +u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); +void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, + int *ret); +void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, + int *ret); + +/* Buffer transfer to/from device (client) core via cmd53. + * fn: function number + * flags: backplane width, address increment, sync/async + * buf: pointer to memory data buffer + * nbytes: number of bytes to transfer to/from buf + * pkt: pointer to packet associated with buf (if any) + * complete: callback function for command completion (async only) + * handle: handle for completion callback (first arg in callback) + * Returns 0 or error code. + * NOTE: Async operation is not currently supported. + */ +int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq); +int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); + +int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt); +int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); +int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq, uint totlen); + +/* Flags bits */ + +/* Four-byte target (backplane) width (vs. two-byte) */ +#define SDIO_REQ_4BYTE 0x1 +/* Fixed address (FIFO) (vs. incrementing address) */ +#define SDIO_REQ_FIXED 0x2 + +/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only). + * rw: read or write (0/1) + * addr: direct SDIO address + * buf: pointer to memory data buffer + * nbytes: number of bytes to transfer to/from buf + * Returns 0 or error code. + */ +int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, + u8 *data, uint size); + +/* Issue an abort to the specified function */ +int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn); +void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev, + enum brcmf_sdiod_state state); +#ifdef CONFIG_PM_SLEEP +bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev); +void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev); +void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev); +void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev); +#else +static inline bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev) +{ + return false; +} +static inline void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev) +{ +} +static inline void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev) +{ +} +static inline void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev) +{ +} +#endif /* CONFIG_PM_SLEEP */ + +struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); +void brcmf_sdio_remove(struct brcmf_sdio *bus); +void brcmf_sdio_isr(struct brcmf_sdio *bus); + +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); +void brcmf_sdio_wowl_config(struct device *dev, bool enabled); +int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep); +void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus); + +#endif /* BRCMFMAC_SDIO_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c new file mode 100644 index 000000000000..a10f35c5eb3d --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012 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 /* bug in tracepoint.h, it should include this */ + +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "tracepoint.h" + +void __brcmf_err(const char *func, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + pr_err("%s: %pV", func, &vaf); + trace_brcmf_err(func, &vaf); + va_end(args); +} + +#endif diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h new file mode 100644 index 000000000000..4d7d51f95716 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h @@ -0,0 +1,152 @@ +/* + * 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. + */ +#if !defined(BRCMF_TRACEPOINT_H_) || defined(TRACE_HEADER_MULTI_READ) +#define BRCMF_TRACEPOINT_H_ + +#include +#include + +#ifndef CONFIG_BRCM_TRACING + +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} + +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(...) + +#undef DEFINE_EVENT +#define DEFINE_EVENT(evt_class, name, proto, ...) \ +static inline void trace_ ## name(proto) {} + +#endif /* CONFIG_BRCM_TRACING */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM brcmfmac + +#define MAX_MSG_LEN 100 + +TRACE_EVENT(brcmf_err, + TP_PROTO(const char *func, struct va_format *vaf), + TP_ARGS(func, vaf), + TP_STRUCT__entry( + __string(func, func) + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + __assign_str(func, func); + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s: %s", __get_str(func), __get_str(msg)) +); + +TRACE_EVENT(brcmf_dbg, + TP_PROTO(u32 level, const char *func, struct va_format *vaf), + TP_ARGS(level, func, vaf), + TP_STRUCT__entry( + __field(u32, level) + __string(func, func) + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + __entry->level = level; + __assign_str(func, func); + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s: %s", __get_str(func), __get_str(msg)) +); + +TRACE_EVENT(brcmf_hexdump, + TP_PROTO(void *data, size_t len), + TP_ARGS(data, len), + TP_STRUCT__entry( + __field(unsigned long, len) + __field(unsigned long, addr) + __dynamic_array(u8, hdata, len) + ), + TP_fast_assign( + __entry->len = len; + __entry->addr = (unsigned long)data; + memcpy(__get_dynamic_array(hdata), data, len); + ), + TP_printk("hexdump [addr=%lx, length=%lu]", __entry->addr, __entry->len) +); + +TRACE_EVENT(brcmf_bcdchdr, + TP_PROTO(void *data), + TP_ARGS(data), + TP_STRUCT__entry( + __field(u8, flags) + __field(u8, prio) + __field(u8, flags2) + __field(u32, siglen) + __dynamic_array(u8, signal, *((u8 *)data + 3) * 4) + ), + TP_fast_assign( + __entry->flags = *(u8 *)data; + __entry->prio = *((u8 *)data + 1); + __entry->flags2 = *((u8 *)data + 2); + __entry->siglen = *((u8 *)data + 3) * 4; + memcpy(__get_dynamic_array(signal), + (u8 *)data + 4, __entry->siglen); + ), + TP_printk("bcdc: prio=%d siglen=%d", __entry->prio, __entry->siglen) +); + +#ifndef SDPCM_RX +#define SDPCM_RX 0 +#endif +#ifndef SDPCM_TX +#define SDPCM_TX 1 +#endif +#ifndef SDPCM_GLOM +#define SDPCM_GLOM 2 +#endif + +TRACE_EVENT(brcmf_sdpcm_hdr, + TP_PROTO(u8 dir, void *data), + TP_ARGS(dir, data), + TP_STRUCT__entry( + __field(u8, dir) + __field(u16, len) + __dynamic_array(u8, hdr, dir == SDPCM_GLOM ? 20 : 12) + ), + TP_fast_assign( + memcpy(__get_dynamic_array(hdr), data, dir == SDPCM_GLOM ? 20 : 12); + __entry->len = *(u8 *)data | (*((u8 *)data + 1) << 8); + __entry->dir = dir; + ), + TP_printk("sdpcm: %s len %u, seq %d", + __entry->dir == SDPCM_RX ? "RX" : "TX", + __entry->len, ((u8 *)__get_dynamic_array(hdr))[4]) +); + +#ifdef CONFIG_BRCM_TRACING + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE tracepoint + +#include + +#endif /* CONFIG_BRCM_TRACING */ + +#endif /* BRCMF_TRACEPOINT_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c new file mode 100644 index 000000000000..689e64d004bc --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -0,0 +1,1535 @@ +/* + * Copyright (c) 2011 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 +#include + +#include +#include +#include +#include "bus.h" +#include "debug.h" +#include "firmware.h" +#include "usb.h" + + +#define IOCTL_RESP_TIMEOUT 2000 + +#define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */ +#define BRCMF_USB_RESET_GETVER_LOOP_CNT 10 + +#define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle + has boot up */ +#define BRCMF_USB_NRXQ 50 +#define BRCMF_USB_NTXQ 50 + +#define BRCMF_USB_CBCTL_WRITE 0 +#define BRCMF_USB_CBCTL_READ 1 +#define BRCMF_USB_MAX_PKT_SIZE 1600 + +#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin" +#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" +#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" +#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin" + +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_MAX_OFFSET 3 /* Max number of file offsets */ +#define TRX_UNCOMP_IMAGE 0x20 /* Trx holds uncompressed img */ +#define TRX_RDL_CHUNK 1500 /* size of each dl transfer */ +#define TRX_OFFSETS_DLFWLEN_IDX 0 + +/* Control messages: bRequest values */ +#define DL_GETSTATE 0 /* returns the rdl_state_t struct */ +#define DL_CHECK_CRC 1 /* currently unused */ +#define DL_GO 2 /* execute downloaded image */ +#define DL_START 3 /* initialize dl state */ +#define DL_REBOOT 4 /* reboot the device in 2 seconds */ +#define DL_GETVER 5 /* returns the bootrom_id_t struct */ +#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset + * event to occur in 2 seconds. It is the + * responsibility of the downloaded code to + * clear this event + */ +#define DL_EXEC 7 /* jump to a supplied address */ +#define DL_RESETCFG 8 /* To support single enum on dongle + * - Not used by bootloader + */ +#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup + * if resp unavailable + */ + +/* states */ +#define DL_WAITING 0 /* waiting to rx first pkt */ +#define DL_READY 1 /* hdr was good, waiting for more of the + * compressed image + */ +#define DL_BAD_HDR 2 /* hdr was corrupted */ +#define DL_BAD_CRC 3 /* compressed image was corrupted */ +#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */ +#define DL_START_FAIL 5 /* failed to initialize correctly */ +#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM + * value + */ +#define DL_IMAGE_TOOBIG 7 /* firmware image too big */ + + +struct trx_header_le { + __le32 magic; /* "HDR0" */ + __le32 len; /* Length of file including header */ + __le32 crc32; /* CRC from flag_version to end of file */ + __le32 flag_version; /* 0:15 flags, 16:31 version */ + __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of + * header + */ +}; + +struct rdl_state_le { + __le32 state; + __le32 bytes; +}; + +struct bootrom_id_le { + __le32 chip; /* Chip id */ + __le32 chiprev; /* Chip rev */ + __le32 ramsize; /* Size of RAM */ + __le32 remapbase; /* Current remap base address */ + __le32 boardtype; /* Type of board */ + __le32 boardrev; /* Board revision */ +}; + +struct brcmf_usb_image { + struct list_head list; + s8 *fwname; + u8 *image; + int image_len; +}; + +struct brcmf_usbdev_info { + struct brcmf_usbdev bus_pub; /* MUST BE FIRST */ + spinlock_t qlock; + struct list_head rx_freeq; + struct list_head rx_postq; + struct list_head tx_freeq; + struct list_head tx_postq; + uint rx_pipe, tx_pipe; + + int rx_low_watermark; + int tx_low_watermark; + int tx_high_watermark; + int tx_freecount; + bool tx_flowblock; + spinlock_t tx_flowblock_lock; + + struct brcmf_usbreq *tx_reqs; + struct brcmf_usbreq *rx_reqs; + + const u8 *image; /* buffer for combine fw and nvram */ + int image_len; + + struct usb_device *usbdev; + struct device *dev; + struct mutex dev_init_lock; + + int ctl_in_pipe, ctl_out_pipe; + struct urb *ctl_urb; /* URB for control endpoint */ + struct usb_ctrlrequest ctl_write; + struct usb_ctrlrequest ctl_read; + u32 ctl_urb_actual_length; + int ctl_urb_status; + int ctl_completed; + wait_queue_head_t ioctl_resp_wait; + ulong ctl_op; + u8 ifnum; + + struct urb *bulk_urb; /* used for FW download */ + + bool wowl_enabled; +}; + +static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, + struct brcmf_usbreq *req); + +static struct brcmf_usbdev *brcmf_usb_get_buspub(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + return bus_if->bus_priv.usb; +} + +static struct brcmf_usbdev_info *brcmf_usb_get_businfo(struct device *dev) +{ + return brcmf_usb_get_buspub(dev)->devinfo; +} + +static int brcmf_usb_ioctl_resp_wait(struct brcmf_usbdev_info *devinfo) +{ + return wait_event_timeout(devinfo->ioctl_resp_wait, + devinfo->ctl_completed, + msecs_to_jiffies(IOCTL_RESP_TIMEOUT)); +} + +static void brcmf_usb_ioctl_resp_wake(struct brcmf_usbdev_info *devinfo) +{ + if (waitqueue_active(&devinfo->ioctl_resp_wait)) + wake_up(&devinfo->ioctl_resp_wait); +} + +static void +brcmf_usb_ctl_complete(struct brcmf_usbdev_info *devinfo, int type, int status) +{ + brcmf_dbg(USB, "Enter, status=%d\n", status); + + if (unlikely(devinfo == NULL)) + return; + + if (type == BRCMF_USB_CBCTL_READ) { + if (status == 0) + devinfo->bus_pub.stats.rx_ctlpkts++; + else + devinfo->bus_pub.stats.rx_ctlerrs++; + } else if (type == BRCMF_USB_CBCTL_WRITE) { + if (status == 0) + devinfo->bus_pub.stats.tx_ctlpkts++; + else + devinfo->bus_pub.stats.tx_ctlerrs++; + } + + devinfo->ctl_urb_status = status; + devinfo->ctl_completed = true; + brcmf_usb_ioctl_resp_wake(devinfo); +} + +static void +brcmf_usb_ctlread_complete(struct urb *urb) +{ + struct brcmf_usbdev_info *devinfo = + (struct brcmf_usbdev_info *)urb->context; + + brcmf_dbg(USB, "Enter\n"); + devinfo->ctl_urb_actual_length = urb->actual_length; + brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_READ, + urb->status); +} + +static void +brcmf_usb_ctlwrite_complete(struct urb *urb) +{ + struct brcmf_usbdev_info *devinfo = + (struct brcmf_usbdev_info *)urb->context; + + brcmf_dbg(USB, "Enter\n"); + brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_WRITE, + urb->status); +} + +static int +brcmf_usb_send_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len) +{ + int ret; + u16 size; + + brcmf_dbg(USB, "Enter\n"); + if (devinfo == NULL || buf == NULL || + len == 0 || devinfo->ctl_urb == NULL) + return -EINVAL; + + size = len; + devinfo->ctl_write.wLength = cpu_to_le16p(&size); + devinfo->ctl_urb->transfer_buffer_length = size; + devinfo->ctl_urb_status = 0; + devinfo->ctl_urb_actual_length = 0; + + usb_fill_control_urb(devinfo->ctl_urb, + devinfo->usbdev, + devinfo->ctl_out_pipe, + (unsigned char *) &devinfo->ctl_write, + buf, size, + (usb_complete_t)brcmf_usb_ctlwrite_complete, + devinfo); + + ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); + if (ret < 0) + brcmf_err("usb_submit_urb failed %d\n", ret); + + return ret; +} + +static int +brcmf_usb_recv_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len) +{ + int ret; + u16 size; + + brcmf_dbg(USB, "Enter\n"); + if ((devinfo == NULL) || (buf == NULL) || (len == 0) + || (devinfo->ctl_urb == NULL)) + return -EINVAL; + + size = len; + devinfo->ctl_read.wLength = cpu_to_le16p(&size); + devinfo->ctl_urb->transfer_buffer_length = size; + + devinfo->ctl_read.bRequestType = USB_DIR_IN + | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + devinfo->ctl_read.bRequest = 1; + + usb_fill_control_urb(devinfo->ctl_urb, + devinfo->usbdev, + devinfo->ctl_in_pipe, + (unsigned char *) &devinfo->ctl_read, + buf, size, + (usb_complete_t)brcmf_usb_ctlread_complete, + devinfo); + + ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); + if (ret < 0) + brcmf_err("usb_submit_urb failed %d\n", ret); + + return ret; +} + +static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len) +{ + int err = 0; + int timeout = 0; + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); + + brcmf_dbg(USB, "Enter\n"); + if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) + return -EIO; + + if (test_and_set_bit(0, &devinfo->ctl_op)) + return -EIO; + + devinfo->ctl_completed = false; + err = brcmf_usb_send_ctl(devinfo, buf, len); + if (err) { + brcmf_err("fail %d bytes: %d\n", err, len); + clear_bit(0, &devinfo->ctl_op); + return err; + } + timeout = brcmf_usb_ioctl_resp_wait(devinfo); + clear_bit(0, &devinfo->ctl_op); + if (!timeout) { + brcmf_err("Txctl wait timed out\n"); + err = -EIO; + } + return err; +} + +static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len) +{ + int err = 0; + int timeout = 0; + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); + + brcmf_dbg(USB, "Enter\n"); + if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) + return -EIO; + + if (test_and_set_bit(0, &devinfo->ctl_op)) + return -EIO; + + devinfo->ctl_completed = false; + err = brcmf_usb_recv_ctl(devinfo, buf, len); + if (err) { + brcmf_err("fail %d bytes: %d\n", err, len); + clear_bit(0, &devinfo->ctl_op); + return err; + } + timeout = brcmf_usb_ioctl_resp_wait(devinfo); + err = devinfo->ctl_urb_status; + clear_bit(0, &devinfo->ctl_op); + if (!timeout) { + brcmf_err("rxctl wait timed out\n"); + err = -EIO; + } + if (!err) + return devinfo->ctl_urb_actual_length; + else + return err; +} + +static struct brcmf_usbreq *brcmf_usb_deq(struct brcmf_usbdev_info *devinfo, + struct list_head *q, int *counter) +{ + unsigned long flags; + struct brcmf_usbreq *req; + spin_lock_irqsave(&devinfo->qlock, flags); + if (list_empty(q)) { + spin_unlock_irqrestore(&devinfo->qlock, flags); + return NULL; + } + req = list_entry(q->next, struct brcmf_usbreq, list); + list_del_init(q->next); + if (counter) + (*counter)--; + spin_unlock_irqrestore(&devinfo->qlock, flags); + return req; + +} + +static void brcmf_usb_enq(struct brcmf_usbdev_info *devinfo, + struct list_head *q, struct brcmf_usbreq *req, + int *counter) +{ + unsigned long flags; + spin_lock_irqsave(&devinfo->qlock, flags); + list_add_tail(&req->list, q); + if (counter) + (*counter)++; + spin_unlock_irqrestore(&devinfo->qlock, flags); +} + +static struct brcmf_usbreq * +brcmf_usbdev_qinit(struct list_head *q, int qsize) +{ + int i; + struct brcmf_usbreq *req, *reqs; + + reqs = kcalloc(qsize, sizeof(struct brcmf_usbreq), GFP_ATOMIC); + if (reqs == NULL) + return NULL; + + req = reqs; + + for (i = 0; i < qsize; i++) { + req->urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!req->urb) + goto fail; + + INIT_LIST_HEAD(&req->list); + list_add_tail(&req->list, q); + req++; + } + return reqs; +fail: + brcmf_err("fail!\n"); + while (!list_empty(q)) { + req = list_entry(q->next, struct brcmf_usbreq, list); + if (req) + usb_free_urb(req->urb); + list_del(q->next); + } + return NULL; + +} + +static void brcmf_usb_free_q(struct list_head *q, bool pending) +{ + struct brcmf_usbreq *req, *next; + int i = 0; + list_for_each_entry_safe(req, next, q, list) { + if (!req->urb) { + brcmf_err("bad req\n"); + break; + } + i++; + if (pending) { + usb_kill_urb(req->urb); + } else { + usb_free_urb(req->urb); + list_del_init(&req->list); + } + } +} + +static void brcmf_usb_del_fromq(struct brcmf_usbdev_info *devinfo, + struct brcmf_usbreq *req) +{ + unsigned long flags; + + spin_lock_irqsave(&devinfo->qlock, flags); + list_del_init(&req->list); + spin_unlock_irqrestore(&devinfo->qlock, flags); +} + + +static void brcmf_usb_tx_complete(struct urb *urb) +{ + struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; + struct brcmf_usbdev_info *devinfo = req->devinfo; + unsigned long flags; + + brcmf_dbg(USB, "Enter, urb->status=%d, skb=%p\n", urb->status, + req->skb); + brcmf_usb_del_fromq(devinfo, req); + + brcmf_txcomplete(devinfo->dev, req->skb, urb->status == 0); + req->skb = NULL; + brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount); + spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags); + if (devinfo->tx_freecount > devinfo->tx_high_watermark && + devinfo->tx_flowblock) { + brcmf_txflowblock(devinfo->dev, false); + devinfo->tx_flowblock = false; + } + spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags); +} + +static void brcmf_usb_rx_complete(struct urb *urb) +{ + struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; + struct brcmf_usbdev_info *devinfo = req->devinfo; + struct sk_buff *skb; + + brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); + brcmf_usb_del_fromq(devinfo, req); + skb = req->skb; + req->skb = NULL; + + /* zero lenght packets indicate usb "failure". Do not refill */ + if (urb->status != 0 || !urb->actual_length) { + brcmu_pkt_buf_free_skb(skb); + brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); + return; + } + + if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { + skb_put(skb, urb->actual_length); + brcmf_rx_frame(devinfo->dev, skb); + brcmf_usb_rx_refill(devinfo, req); + } else { + brcmu_pkt_buf_free_skb(skb); + brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); + } + return; + +} + +static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, + struct brcmf_usbreq *req) +{ + struct sk_buff *skb; + int ret; + + if (!req || !devinfo) + return; + + skb = dev_alloc_skb(devinfo->bus_pub.bus_mtu); + if (!skb) { + brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); + return; + } + req->skb = skb; + + usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->rx_pipe, + skb->data, skb_tailroom(skb), brcmf_usb_rx_complete, + req); + req->devinfo = devinfo; + brcmf_usb_enq(devinfo, &devinfo->rx_postq, req, NULL); + + ret = usb_submit_urb(req->urb, GFP_ATOMIC); + if (ret) { + brcmf_usb_del_fromq(devinfo, req); + brcmu_pkt_buf_free_skb(req->skb); + req->skb = NULL; + brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); + } + return; +} + +static void brcmf_usb_rx_fill_all(struct brcmf_usbdev_info *devinfo) +{ + struct brcmf_usbreq *req; + + if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) { + brcmf_err("bus is not up=%d\n", devinfo->bus_pub.state); + return; + } + while ((req = brcmf_usb_deq(devinfo, &devinfo->rx_freeq, NULL)) != NULL) + brcmf_usb_rx_refill(devinfo, req); +} + +static void +brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state) +{ + struct brcmf_bus *bcmf_bus = devinfo->bus_pub.bus; + int old_state; + + brcmf_dbg(USB, "Enter, current state=%d, new state=%d\n", + devinfo->bus_pub.state, state); + + if (devinfo->bus_pub.state == state) + return; + + old_state = devinfo->bus_pub.state; + devinfo->bus_pub.state = state; + + /* update state of upper layer */ + if (state == BRCMFMAC_USB_STATE_DOWN) { + brcmf_dbg(USB, "DBUS is down\n"); + brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DOWN); + } else if (state == BRCMFMAC_USB_STATE_UP) { + brcmf_dbg(USB, "DBUS is up\n"); + brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_UP); + } else { + brcmf_dbg(USB, "DBUS current state=%d\n", state); + } +} + +static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) +{ + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); + struct brcmf_usbreq *req; + int ret; + unsigned long flags; + + brcmf_dbg(USB, "Enter, skb=%p\n", skb); + if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) { + ret = -EIO; + goto fail; + } + + req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq, + &devinfo->tx_freecount); + if (!req) { + brcmf_err("no req to send\n"); + ret = -ENOMEM; + goto fail; + } + + req->skb = skb; + req->devinfo = devinfo; + usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe, + skb->data, skb->len, brcmf_usb_tx_complete, req); + req->urb->transfer_flags |= URB_ZERO_PACKET; + brcmf_usb_enq(devinfo, &devinfo->tx_postq, req, NULL); + ret = usb_submit_urb(req->urb, GFP_ATOMIC); + if (ret) { + brcmf_err("brcmf_usb_tx usb_submit_urb FAILED\n"); + brcmf_usb_del_fromq(devinfo, req); + req->skb = NULL; + brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, + &devinfo->tx_freecount); + goto fail; + } + + spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags); + if (devinfo->tx_freecount < devinfo->tx_low_watermark && + !devinfo->tx_flowblock) { + brcmf_txflowblock(dev, true); + devinfo->tx_flowblock = true; + } + spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags); + return 0; + +fail: + return ret; +} + + +static int brcmf_usb_up(struct device *dev) +{ + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); + + brcmf_dbg(USB, "Enter\n"); + if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) + return 0; + + /* Success, indicate devinfo is fully up */ + brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP); + + if (devinfo->ctl_urb) { + devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0); + devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0); + + /* CTL Write */ + devinfo->ctl_write.bRequestType = + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + devinfo->ctl_write.bRequest = 0; + devinfo->ctl_write.wValue = cpu_to_le16(0); + devinfo->ctl_write.wIndex = cpu_to_le16(devinfo->ifnum); + + /* CTL Read */ + devinfo->ctl_read.bRequestType = + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + devinfo->ctl_read.bRequest = 1; + devinfo->ctl_read.wValue = cpu_to_le16(0); + devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum); + } + brcmf_usb_rx_fill_all(devinfo); + return 0; +} + +static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo) +{ + if (devinfo->ctl_urb) + usb_kill_urb(devinfo->ctl_urb); + if (devinfo->bulk_urb) + usb_kill_urb(devinfo->bulk_urb); + brcmf_usb_free_q(&devinfo->tx_postq, true); + brcmf_usb_free_q(&devinfo->rx_postq, true); +} + +static void brcmf_usb_down(struct device *dev) +{ + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); + + brcmf_dbg(USB, "Enter\n"); + if (devinfo == NULL) + return; + + if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN) + return; + + brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN); + + brcmf_cancel_all_urbs(devinfo); +} + +static void +brcmf_usb_sync_complete(struct urb *urb) +{ + struct brcmf_usbdev_info *devinfo = + (struct brcmf_usbdev_info *)urb->context; + + devinfo->ctl_completed = true; + brcmf_usb_ioctl_resp_wake(devinfo); +} + +static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, + void *buffer, int buflen) +{ + int ret; + char *tmpbuf; + u16 size; + + if ((!devinfo) || (devinfo->ctl_urb == NULL)) + return -EINVAL; + + tmpbuf = kmalloc(buflen, GFP_ATOMIC); + if (!tmpbuf) + return -ENOMEM; + + size = buflen; + devinfo->ctl_urb->transfer_buffer_length = size; + + devinfo->ctl_read.wLength = cpu_to_le16p(&size); + devinfo->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_INTERFACE; + devinfo->ctl_read.bRequest = cmd; + + usb_fill_control_urb(devinfo->ctl_urb, + devinfo->usbdev, + usb_rcvctrlpipe(devinfo->usbdev, 0), + (unsigned char *) &devinfo->ctl_read, + (void *) tmpbuf, size, + (usb_complete_t)brcmf_usb_sync_complete, devinfo); + + devinfo->ctl_completed = false; + ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); + if (ret < 0) { + brcmf_err("usb_submit_urb failed %d\n", ret); + goto finalize; + } + + if (!brcmf_usb_ioctl_resp_wait(devinfo)) { + usb_kill_urb(devinfo->ctl_urb); + ret = -ETIMEDOUT; + } else { + memcpy(buffer, tmpbuf, buflen); + } + +finalize: + kfree(tmpbuf); + return ret; +} + +static bool +brcmf_usb_dlneeded(struct brcmf_usbdev_info *devinfo) +{ + struct bootrom_id_le id; + u32 chipid, chiprev; + + brcmf_dbg(USB, "Enter\n"); + + if (devinfo == NULL) + return false; + + /* Check if firmware downloaded already by querying runtime ID */ + id.chip = cpu_to_le32(0xDEAD); + brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id)); + + chipid = le32_to_cpu(id.chip); + chiprev = le32_to_cpu(id.chiprev); + + if ((chipid & 0x4300) == 0x4300) + brcmf_dbg(USB, "chip %x rev 0x%x\n", chipid, chiprev); + else + brcmf_dbg(USB, "chip %d rev 0x%x\n", chipid, chiprev); + if (chipid == BRCMF_POSTBOOT_ID) { + brcmf_dbg(USB, "firmware already downloaded\n"); + brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id)); + return false; + } else { + devinfo->bus_pub.devid = chipid; + devinfo->bus_pub.chiprev = chiprev; + } + return true; +} + +static int +brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo) +{ + struct bootrom_id_le id; + u32 loop_cnt; + int err; + + brcmf_dbg(USB, "Enter\n"); + + loop_cnt = 0; + do { + mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT); + loop_cnt++; + id.chip = cpu_to_le32(0xDEAD); /* Get the ID */ + err = brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id)); + if ((err) && (err != -ETIMEDOUT)) + return err; + if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) + break; + } while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT); + + if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) { + brcmf_dbg(USB, "postboot chip 0x%x/rev 0x%x\n", + le32_to_cpu(id.chip), le32_to_cpu(id.chiprev)); + + brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id)); + return 0; + } else { + brcmf_err("Cannot talk to Dongle. Firmware is not UP, %d ms\n", + BRCMF_USB_RESET_GETVER_SPINWAIT * loop_cnt); + return -EINVAL; + } +} + + +static int +brcmf_usb_dl_send_bulk(struct brcmf_usbdev_info *devinfo, void *buffer, int len) +{ + int ret; + + if ((devinfo == NULL) || (devinfo->bulk_urb == NULL)) + return -EINVAL; + + /* Prepare the URB */ + usb_fill_bulk_urb(devinfo->bulk_urb, devinfo->usbdev, + devinfo->tx_pipe, buffer, len, + (usb_complete_t)brcmf_usb_sync_complete, devinfo); + + devinfo->bulk_urb->transfer_flags |= URB_ZERO_PACKET; + + devinfo->ctl_completed = false; + ret = usb_submit_urb(devinfo->bulk_urb, GFP_ATOMIC); + if (ret) { + brcmf_err("usb_submit_urb failed %d\n", ret); + return ret; + } + ret = brcmf_usb_ioctl_resp_wait(devinfo); + return (ret == 0); +} + +static int +brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) +{ + unsigned int sendlen, sent, dllen; + char *bulkchunk = NULL, *dlpos; + struct rdl_state_le state; + u32 rdlstate, rdlbytes; + int err = 0; + + brcmf_dbg(USB, "Enter, fw %p, len %d\n", fw, fwlen); + + bulkchunk = kmalloc(TRX_RDL_CHUNK, GFP_ATOMIC); + if (bulkchunk == NULL) { + err = -ENOMEM; + goto fail; + } + + /* 1) Prepare USB boot loader for runtime image */ + brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state)); + + rdlstate = le32_to_cpu(state.state); + rdlbytes = le32_to_cpu(state.bytes); + + /* 2) Check we are in the Waiting state */ + if (rdlstate != DL_WAITING) { + brcmf_err("Failed to DL_START\n"); + err = -EINVAL; + goto fail; + } + sent = 0; + dlpos = fw; + dllen = fwlen; + + /* Get chip id and rev */ + while (rdlbytes != dllen) { + /* Wait until the usb device reports it received all + * the bytes we sent */ + if ((rdlbytes == sent) && (rdlbytes != dllen)) { + if ((dllen-sent) < TRX_RDL_CHUNK) + sendlen = dllen-sent; + else + sendlen = TRX_RDL_CHUNK; + + /* simply avoid having to send a ZLP by ensuring we + * never have an even + * multiple of 64 + */ + if (!(sendlen % 64)) + sendlen -= 4; + + /* send data */ + memcpy(bulkchunk, dlpos, sendlen); + if (brcmf_usb_dl_send_bulk(devinfo, bulkchunk, + sendlen)) { + brcmf_err("send_bulk failed\n"); + err = -EINVAL; + goto fail; + } + + dlpos += sendlen; + sent += sendlen; + } + err = brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, + sizeof(state)); + if (err) { + brcmf_err("DL_GETSTATE Failed\n"); + goto fail; + } + + rdlstate = le32_to_cpu(state.state); + rdlbytes = le32_to_cpu(state.bytes); + + /* restart if an error is reported */ + if (rdlstate == DL_BAD_HDR || rdlstate == DL_BAD_CRC) { + brcmf_err("Bad Hdr or Bad CRC state %d\n", + rdlstate); + err = -EINVAL; + goto fail; + } + } + +fail: + kfree(bulkchunk); + brcmf_dbg(USB, "Exit, err=%d\n", err); + return err; +} + +static int brcmf_usb_dlstart(struct brcmf_usbdev_info *devinfo, u8 *fw, int len) +{ + int err; + + brcmf_dbg(USB, "Enter\n"); + + if (devinfo == NULL) + return -EINVAL; + + if (devinfo->bus_pub.devid == 0xDEAD) + return -EINVAL; + + err = brcmf_usb_dl_writeimage(devinfo, fw, len); + if (err == 0) + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_DONE; + else + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_FAIL; + brcmf_dbg(USB, "Exit, err=%d\n", err); + + return err; +} + +static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo) +{ + struct rdl_state_le state; + + brcmf_dbg(USB, "Enter\n"); + if (!devinfo) + return -EINVAL; + + if (devinfo->bus_pub.devid == 0xDEAD) + return -EINVAL; + + /* Check we are runnable */ + state.state = 0; + brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(state)); + + /* Start the image */ + if (state.state == cpu_to_le32(DL_RUNNABLE)) { + if (brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(state))) + return -ENODEV; + if (brcmf_usb_resetcfg(devinfo)) + return -ENODEV; + /* The Dongle may go for re-enumeration. */ + } else { + brcmf_err("Dongle not runnable\n"); + return -EINVAL; + } + brcmf_dbg(USB, "Exit\n"); + return 0; +} + +static bool brcmf_usb_chip_support(int chipid, int chiprev) +{ + switch(chipid) { + case BRCM_CC_43143_CHIP_ID: + return true; + case BRCM_CC_43235_CHIP_ID: + case BRCM_CC_43236_CHIP_ID: + case BRCM_CC_43238_CHIP_ID: + return (chiprev == 3); + case BRCM_CC_43242_CHIP_ID: + return true; + case BRCM_CC_43566_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: + return true; + default: + break; + } + return false; +} + +static int +brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo) +{ + int devid, chiprev; + int err; + + brcmf_dbg(USB, "Enter\n"); + if (devinfo == NULL) + return -ENODEV; + + devid = devinfo->bus_pub.devid; + chiprev = devinfo->bus_pub.chiprev; + + if (!brcmf_usb_chip_support(devid, chiprev)) { + brcmf_err("unsupported chip %d rev %d\n", + devid, chiprev); + return -EINVAL; + } + + if (!devinfo->image) { + brcmf_err("No firmware!\n"); + return -ENOENT; + } + + err = brcmf_usb_dlstart(devinfo, + (u8 *)devinfo->image, devinfo->image_len); + if (err == 0) + err = brcmf_usb_dlrun(devinfo); + return err; +} + + +static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo) +{ + brcmf_dbg(USB, "Enter, devinfo %p\n", devinfo); + + /* free the URBS */ + brcmf_usb_free_q(&devinfo->rx_freeq, false); + brcmf_usb_free_q(&devinfo->tx_freeq, false); + + usb_free_urb(devinfo->ctl_urb); + usb_free_urb(devinfo->bulk_urb); + + kfree(devinfo->tx_reqs); + kfree(devinfo->rx_reqs); +} + + +static int check_file(const u8 *headers) +{ + struct trx_header_le *trx; + int actual_len = -1; + + brcmf_dbg(USB, "Enter\n"); + /* Extract trx header */ + trx = (struct trx_header_le *) headers; + if (trx->magic != cpu_to_le32(TRX_MAGIC)) + return -1; + + headers += sizeof(struct trx_header_le); + + if (le32_to_cpu(trx->flag_version) & TRX_UNCOMP_IMAGE) { + actual_len = le32_to_cpu(trx->offsets[TRX_OFFSETS_DLFWLEN_IDX]); + return actual_len + sizeof(struct trx_header_le); + } + return -1; +} + +static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) +{ + switch (devinfo->bus_pub.devid) { + case BRCM_CC_43143_CHIP_ID: + return BRCMF_USB_43143_FW_NAME; + case BRCM_CC_43235_CHIP_ID: + case BRCM_CC_43236_CHIP_ID: + case BRCM_CC_43238_CHIP_ID: + return BRCMF_USB_43236_FW_NAME; + case BRCM_CC_43242_CHIP_ID: + return BRCMF_USB_43242_FW_NAME; + case BRCM_CC_43566_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: + return BRCMF_USB_43569_FW_NAME; + default: + return NULL; + } +} + + +static +struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, + int nrxq, int ntxq) +{ + brcmf_dbg(USB, "Enter\n"); + + devinfo->bus_pub.nrxq = nrxq; + devinfo->rx_low_watermark = nrxq / 2; + devinfo->bus_pub.devinfo = devinfo; + devinfo->bus_pub.ntxq = ntxq; + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DOWN; + + /* flow control when too many tx urbs posted */ + devinfo->tx_low_watermark = ntxq / 4; + devinfo->tx_high_watermark = devinfo->tx_low_watermark * 3; + devinfo->bus_pub.bus_mtu = BRCMF_USB_MAX_PKT_SIZE; + + /* Initialize other structure content */ + init_waitqueue_head(&devinfo->ioctl_resp_wait); + + /* Initialize the spinlocks */ + spin_lock_init(&devinfo->qlock); + spin_lock_init(&devinfo->tx_flowblock_lock); + + INIT_LIST_HEAD(&devinfo->rx_freeq); + INIT_LIST_HEAD(&devinfo->rx_postq); + + INIT_LIST_HEAD(&devinfo->tx_freeq); + INIT_LIST_HEAD(&devinfo->tx_postq); + + devinfo->tx_flowblock = false; + + devinfo->rx_reqs = brcmf_usbdev_qinit(&devinfo->rx_freeq, nrxq); + if (!devinfo->rx_reqs) + goto error; + + devinfo->tx_reqs = brcmf_usbdev_qinit(&devinfo->tx_freeq, ntxq); + if (!devinfo->tx_reqs) + goto error; + devinfo->tx_freecount = ntxq; + + devinfo->ctl_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!devinfo->ctl_urb) { + brcmf_err("usb_alloc_urb (ctl) failed\n"); + goto error; + } + devinfo->bulk_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!devinfo->bulk_urb) { + brcmf_err("usb_alloc_urb (bulk) failed\n"); + goto error; + } + + return &devinfo->bus_pub; + +error: + brcmf_err("failed!\n"); + brcmf_usb_detach(devinfo); + return NULL; +} + +static void brcmf_usb_wowl_config(struct device *dev, bool enabled) +{ + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); + + brcmf_dbg(USB, "Configuring WOWL, enabled=%d\n", enabled); + devinfo->wowl_enabled = enabled; + if (enabled) + device_set_wakeup_enable(devinfo->dev, true); + else + device_set_wakeup_enable(devinfo->dev, false); +} + +static struct brcmf_bus_ops brcmf_usb_bus_ops = { + .txdata = brcmf_usb_tx, + .stop = brcmf_usb_down, + .txctl = brcmf_usb_tx_ctlpkt, + .rxctl = brcmf_usb_rx_ctlpkt, + .wowl_config = brcmf_usb_wowl_config, +}; + +static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo) +{ + int ret; + + /* Attach to the common driver interface */ + ret = brcmf_attach(devinfo->dev); + if (ret) { + brcmf_err("brcmf_attach failed\n"); + return ret; + } + + ret = brcmf_usb_up(devinfo->dev); + if (ret) + goto fail; + + ret = brcmf_bus_start(devinfo->dev); + if (ret) + goto fail; + + return 0; +fail: + brcmf_detach(devinfo->dev); + return ret; +} + +static void brcmf_usb_probe_phase2(struct device *dev, + const struct firmware *fw, + void *nvram, u32 nvlen) +{ + struct brcmf_bus *bus = dev_get_drvdata(dev); + struct brcmf_usbdev_info *devinfo; + int ret; + + brcmf_dbg(USB, "Start fw downloading\n"); + + devinfo = bus->bus_priv.usb->devinfo; + ret = check_file(fw->data); + if (ret < 0) { + brcmf_err("invalid firmware\n"); + release_firmware(fw); + goto error; + } + + devinfo->image = fw->data; + devinfo->image_len = fw->size; + + ret = brcmf_usb_fw_download(devinfo); + release_firmware(fw); + if (ret) + goto error; + + ret = brcmf_usb_bus_setup(devinfo); + if (ret) + goto error; + + mutex_unlock(&devinfo->dev_init_lock); + return; +error: + brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret); + mutex_unlock(&devinfo->dev_init_lock); + device_release_driver(dev); +} + +static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) +{ + struct brcmf_bus *bus = NULL; + struct brcmf_usbdev *bus_pub = NULL; + struct device *dev = devinfo->dev; + int ret; + + brcmf_dbg(USB, "Enter\n"); + bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ); + if (!bus_pub) + return -ENODEV; + + bus = kzalloc(sizeof(struct brcmf_bus), GFP_ATOMIC); + if (!bus) { + ret = -ENOMEM; + goto fail; + } + + bus->dev = dev; + bus_pub->bus = bus; + bus->bus_priv.usb = bus_pub; + dev_set_drvdata(dev, bus); + bus->ops = &brcmf_usb_bus_ops; + bus->proto_type = BRCMF_PROTO_BCDC; + bus->always_use_fws_queue = true; +#ifdef CONFIG_PM + bus->wowl_supported = true; +#endif + + if (!brcmf_usb_dlneeded(devinfo)) { + ret = brcmf_usb_bus_setup(devinfo); + if (ret) + goto fail; + /* we are done */ + mutex_unlock(&devinfo->dev_init_lock); + return 0; + } + bus->chip = bus_pub->devid; + bus->chiprev = bus_pub->chiprev; + + /* request firmware here */ + ret = brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), + NULL, brcmf_usb_probe_phase2); + if (ret) { + brcmf_err("firmware request failed: %d\n", ret); + goto fail; + } + + return 0; + +fail: + /* Release resources in reverse order */ + kfree(bus); + brcmf_usb_detach(devinfo); + return ret; +} + +static void +brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo) +{ + if (!devinfo) + return; + brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo); + + brcmf_detach(devinfo->dev); + kfree(devinfo->bus_pub.bus); + brcmf_usb_detach(devinfo); +} + +static int +brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *usb = interface_to_usbdev(intf); + struct brcmf_usbdev_info *devinfo; + struct usb_interface_descriptor *desc; + struct usb_endpoint_descriptor *endpoint; + int ret = 0; + u32 num_of_eps; + u8 endpoint_num, ep; + + brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct); + + devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC); + if (devinfo == NULL) + return -ENOMEM; + + devinfo->usbdev = usb; + devinfo->dev = &usb->dev; + /* Take an init lock, to protect for disconnect while still loading. + * Necessary because of the asynchronous firmware load construction + */ + mutex_init(&devinfo->dev_init_lock); + mutex_lock(&devinfo->dev_init_lock); + + usb_set_intfdata(intf, devinfo); + + /* Check that the device supports only one configuration */ + if (usb->descriptor.bNumConfigurations != 1) { + brcmf_err("Number of configurations: %d not supported\n", + usb->descriptor.bNumConfigurations); + ret = -ENODEV; + goto fail; + } + + if ((usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) && + (usb->descriptor.bDeviceClass != USB_CLASS_MISC) && + (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS_CONTROLLER)) { + brcmf_err("Device class: 0x%x not supported\n", + usb->descriptor.bDeviceClass); + ret = -ENODEV; + goto fail; + } + + desc = &intf->altsetting[0].desc; + if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || + (desc->bInterfaceSubClass != 2) || + (desc->bInterfaceProtocol != 0xff)) { + brcmf_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n", + desc->bInterfaceNumber, desc->bInterfaceClass, + desc->bInterfaceSubClass, desc->bInterfaceProtocol); + ret = -ENODEV; + goto fail; + } + + num_of_eps = desc->bNumEndpoints; + for (ep = 0; ep < num_of_eps; ep++) { + endpoint = &intf->altsetting[0].endpoint[ep].desc; + endpoint_num = usb_endpoint_num(endpoint); + if (!usb_endpoint_xfer_bulk(endpoint)) + continue; + if (usb_endpoint_dir_in(endpoint)) { + if (!devinfo->rx_pipe) + devinfo->rx_pipe = + usb_rcvbulkpipe(usb, endpoint_num); + } else { + if (!devinfo->tx_pipe) + devinfo->tx_pipe = + usb_sndbulkpipe(usb, endpoint_num); + } + } + if (devinfo->rx_pipe == 0) { + brcmf_err("No RX (in) Bulk EP found\n"); + ret = -ENODEV; + goto fail; + } + if (devinfo->tx_pipe == 0) { + brcmf_err("No TX (out) Bulk EP found\n"); + ret = -ENODEV; + goto fail; + } + + devinfo->ifnum = desc->bInterfaceNumber; + + if (usb->speed == USB_SPEED_SUPER) + brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n"); + else if (usb->speed == USB_SPEED_HIGH) + brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n"); + else + brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n"); + + ret = brcmf_usb_probe_cb(devinfo); + if (ret) + goto fail; + + /* Success */ + return 0; + +fail: + mutex_unlock(&devinfo->dev_init_lock); + kfree(devinfo); + usb_set_intfdata(intf, NULL); + return ret; +} + +static void +brcmf_usb_disconnect(struct usb_interface *intf) +{ + struct brcmf_usbdev_info *devinfo; + + brcmf_dbg(USB, "Enter\n"); + devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf); + + if (devinfo) { + mutex_lock(&devinfo->dev_init_lock); + /* Make sure that devinfo still exists. Firmware probe routines + * may have released the device and cleared the intfdata. + */ + if (!usb_get_intfdata(intf)) + goto done; + + brcmf_usb_disconnect_cb(devinfo); + kfree(devinfo); + } +done: + brcmf_dbg(USB, "Exit\n"); +} + +/* + * only need to signal the bus being down and update the state. + */ +static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state) +{ + struct usb_device *usb = interface_to_usbdev(intf); + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); + + brcmf_dbg(USB, "Enter\n"); + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP; + if (devinfo->wowl_enabled) + brcmf_cancel_all_urbs(devinfo); + else + brcmf_detach(&usb->dev); + return 0; +} + +/* + * (re-) start the bus. + */ +static int brcmf_usb_resume(struct usb_interface *intf) +{ + struct usb_device *usb = interface_to_usbdev(intf); + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); + + brcmf_dbg(USB, "Enter\n"); + if (!devinfo->wowl_enabled) + return brcmf_usb_bus_setup(devinfo); + + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP; + brcmf_usb_rx_fill_all(devinfo); + return 0; +} + +static int brcmf_usb_reset_resume(struct usb_interface *intf) +{ + struct usb_device *usb = interface_to_usbdev(intf); + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); + + brcmf_dbg(USB, "Enter\n"); + + return brcmf_fw_get_firmwares(&usb->dev, 0, + brcmf_usb_get_fwname(devinfo), NULL, + brcmf_usb_probe_phase2); +} + +#define BRCMF_USB_DEVICE(dev_id) \ + { USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id) } + +static struct usb_device_id brcmf_usb_devid_table[] = { + BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID), + BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID), + BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID), + BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID), + /* special entry for device with firmware loaded and running */ + BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID), + { /* end: all zeroes */ } +}; + +MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table); +MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME); +MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME); +MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME); +MODULE_FIRMWARE(BRCMF_USB_43569_FW_NAME); + +static struct usb_driver brcmf_usbdrvr = { + .name = KBUILD_MODNAME, + .probe = brcmf_usb_probe, + .disconnect = brcmf_usb_disconnect, + .id_table = brcmf_usb_devid_table, + .suspend = brcmf_usb_suspend, + .resume = brcmf_usb_resume, + .reset_resume = brcmf_usb_reset_resume, + .supports_autosuspend = 1, + .disable_hub_initiated_lpm = 1, +}; + +static int brcmf_usb_reset_device(struct device *dev, void *notused) +{ + /* device past is the usb interface so we + * need to use parent here. + */ + brcmf_dev_reset(dev->parent); + return 0; +} + +void brcmf_usb_exit(void) +{ + struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver; + int ret; + + brcmf_dbg(USB, "Enter\n"); + ret = driver_for_each_device(drv, NULL, NULL, + brcmf_usb_reset_device); + usb_deregister(&brcmf_usbdrvr); +} + +void brcmf_usb_register(void) +{ + brcmf_dbg(USB, "Enter\n"); + usb_register(&brcmf_usbdrvr); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h new file mode 100644 index 000000000000..f483a8c9945b --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 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_USB_H +#define BRCMFMAC_USB_H + +enum brcmf_usb_state { + BRCMFMAC_USB_STATE_DOWN, + BRCMFMAC_USB_STATE_DL_FAIL, + BRCMFMAC_USB_STATE_DL_DONE, + BRCMFMAC_USB_STATE_UP, + BRCMFMAC_USB_STATE_SLEEP +}; + +struct brcmf_stats { + u32 tx_ctlpkts; + u32 tx_ctlerrs; + u32 rx_ctlpkts; + u32 rx_ctlerrs; +}; + +struct brcmf_usbdev { + struct brcmf_bus *bus; + struct brcmf_usbdev_info *devinfo; + enum brcmf_usb_state state; + struct brcmf_stats stats; + int ntxq, nrxq, rxsize; + u32 bus_mtu; + int devid; + int chiprev; /* chip revsion number */ +}; + +/* IO Request Block (IRB) */ +struct brcmf_usbreq { + struct list_head list; + struct brcmf_usbdev_info *devinfo; + struct urb *urb; + struct sk_buff *skb; +}; + +#endif /* BRCMFMAC_USB_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c new file mode 100644 index 000000000000..8eff2753abad --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2014 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 +#include "fwil_types.h" +#include "core.h" +#include "p2p.h" +#include "debug.h" +#include "cfg80211.h" +#include "vendor.h" +#include "fwil.h" + +static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + const struct brcmf_vndr_dcmd_hdr *cmdhdr = data; + struct sk_buff *reply; + int ret, payload, ret_len; + void *dcmd_buf = NULL, *wr_pointer; + u16 msglen, maxmsglen = PAGE_SIZE - 0x100; + + if (len < sizeof(*cmdhdr)) { + brcmf_err("vendor command too short: %d\n", len); + return -EINVAL; + } + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + ifp = vif->ifp; + + brcmf_dbg(TRACE, "ifidx=%d, cmd=%d\n", ifp->ifidx, cmdhdr->cmd); + + if (cmdhdr->offset > len) { + brcmf_err("bad buffer offset %d > %d\n", cmdhdr->offset, len); + return -EINVAL; + } + + len -= cmdhdr->offset; + ret_len = cmdhdr->len; + if (ret_len > 0 || len > 0) { + if (len > BRCMF_DCMD_MAXLEN) { + brcmf_err("oversize input buffer %d\n", len); + len = BRCMF_DCMD_MAXLEN; + } + if (ret_len > BRCMF_DCMD_MAXLEN) { + brcmf_err("oversize return buffer %d\n", ret_len); + ret_len = BRCMF_DCMD_MAXLEN; + } + payload = max(ret_len, len) + 1; + dcmd_buf = vzalloc(payload); + if (NULL == dcmd_buf) + return -ENOMEM; + + memcpy(dcmd_buf, (void *)cmdhdr + cmdhdr->offset, len); + *(char *)(dcmd_buf + len) = '\0'; + } + + if (cmdhdr->set) + ret = brcmf_fil_cmd_data_set(ifp, cmdhdr->cmd, dcmd_buf, + ret_len); + else + ret = brcmf_fil_cmd_data_get(ifp, cmdhdr->cmd, dcmd_buf, + ret_len); + if (ret != 0) + goto exit; + + wr_pointer = dcmd_buf; + while (ret_len > 0) { + msglen = ret_len > maxmsglen ? maxmsglen : ret_len; + ret_len -= msglen; + payload = msglen + sizeof(msglen); + reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload); + if (NULL == reply) { + ret = -ENOMEM; + break; + } + + if (nla_put(reply, BRCMF_NLATTR_DATA, msglen, wr_pointer) || + nla_put_u16(reply, BRCMF_NLATTR_LEN, msglen)) { + kfree_skb(reply); + ret = -ENOBUFS; + break; + } + + ret = cfg80211_vendor_cmd_reply(reply); + if (ret) + break; + + wr_pointer += msglen; + } + +exit: + vfree(dcmd_buf); + + return ret; +} + +const struct wiphy_vendor_command brcmf_vendor_cmds[] = { + { + { + .vendor_id = BROADCOM_OUI, + .subcmd = BRCMF_VNDR_CMDS_DCMD + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = brcmf_cfg80211_vndr_cmds_dcmd_handler + }, +}; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h new file mode 100644 index 000000000000..061b7bfa2e1c --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 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 _vendor_h_ +#define _vendor_h_ + +#define BROADCOM_OUI 0x001018 + +enum brcmf_vndr_cmds { + BRCMF_VNDR_CMDS_UNSPEC, + BRCMF_VNDR_CMDS_DCMD, + BRCMF_VNDR_CMDS_LAST +}; + +/** + * enum brcmf_nlattrs - nl80211 message attributes + * + * @BRCMF_NLATTR_LEN: message body length + * @BRCMF_NLATTR_DATA: message body + */ +enum brcmf_nlattrs { + BRCMF_NLATTR_UNSPEC, + + BRCMF_NLATTR_LEN, + BRCMF_NLATTR_DATA, + + __BRCMF_NLATTR_AFTER_LAST, + BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1 +}; + +/** + * struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd + * support + * + * @cmd: common dongle cmd definition + * @len: length of expecting return buffer + * @offset: offset of data buffer + * @set: get or set request(optional) + * @magic: magic number for verification + */ +struct brcmf_vndr_dcmd_hdr { + uint cmd; + int len; + uint offset; + uint set; + uint magic; +}; + +extern const struct wiphy_vendor_command brcmf_vendor_cmds[]; + +#endif /* _vendor_h_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile new file mode 100644 index 000000000000..960e6b86bbcb --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile @@ -0,0 +1,48 @@ +# +# Makefile fragment for Broadcom 802.11n Networking Device Driver +# +# Copyright (c) 2010 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. + +ccflags-y := \ + -D__CHECK_ENDIAN__ \ + -Idrivers/net/wireless/broadcom/brcm80211/brcmsmac \ + -Idrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy \ + -Idrivers/net/wireless/broadcom/brcm80211/include + +brcmsmac-y := \ + mac80211_if.o \ + ucode_loader.o \ + ampdu.o \ + antsel.o \ + channel.o \ + main.o \ + phy_shim.o \ + pmu.o \ + rate.o \ + stf.o \ + aiutils.o \ + phy/phy_cmn.o \ + phy/phy_lcn.o \ + phy/phy_n.o \ + phy/phytbl_lcn.o \ + phy/phytbl_n.o \ + phy/phy_qmath.o \ + dma.o \ + brcms_trace_events.o \ + debug.o + +brcmsmac-$(CONFIG_BCMA_DRIVER_GPIO) += led.o + +obj-$(CONFIG_BRCMSMAC) += brcmsmac.o diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c new file mode 100644 index 000000000000..53365977bfd6 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c @@ -0,0 +1,710 @@ +/* + * Copyright (c) 2010 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. + * + * File contents: support functions for PCI/PCIe + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include + +#include +#include +#include +#include +#include +#include "types.h" +#include "pub.h" +#include "pmu.h" +#include "aiutils.h" + +/* slow_clk_ctl */ + /* slow clock source mask */ +#define SCC_SS_MASK 0x00000007 + /* source of slow clock is LPO */ +#define SCC_SS_LPO 0x00000000 + /* source of slow clock is crystal */ +#define SCC_SS_XTAL 0x00000001 + /* source of slow clock is PCI */ +#define SCC_SS_PCI 0x00000002 + /* LPOFreqSel, 1: 160Khz, 0: 32KHz */ +#define SCC_LF 0x00000200 + /* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */ +#define SCC_LP 0x00000400 + /* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */ +#define SCC_FS 0x00000800 + /* IgnorePllOffReq, 1/0: + * power logic ignores/honors PLL clock disable requests from core + */ +#define SCC_IP 0x00001000 + /* XtalControlEn, 1/0: + * power logic does/doesn't disable crystal when appropriate + */ +#define SCC_XC 0x00002000 + /* XtalPU (RO), 1/0: crystal running/disabled */ +#define SCC_XP 0x00004000 + /* ClockDivider (SlowClk = 1/(4+divisor)) */ +#define SCC_CD_MASK 0xffff0000 +#define SCC_CD_SHIFT 16 + +/* system_clk_ctl */ + /* ILPen: Enable Idle Low Power */ +#define SYCC_IE 0x00000001 + /* ALPen: Enable Active Low Power */ +#define SYCC_AE 0x00000002 + /* ForcePLLOn */ +#define SYCC_FP 0x00000004 + /* Force ALP (or HT if ALPen is not set */ +#define SYCC_AR 0x00000008 + /* Force HT */ +#define SYCC_HR 0x00000010 + /* ClkDiv (ILP = 1/(4 * (divisor + 1)) */ +#define SYCC_CD_MASK 0xffff0000 +#define SYCC_CD_SHIFT 16 + +#define CST4329_SPROM_OTP_SEL_MASK 0x00000003 + /* OTP is powered up, use def. CIS, no SPROM */ +#define CST4329_DEFCIS_SEL 0 + /* OTP is powered up, SPROM is present */ +#define CST4329_SPROM_SEL 1 + /* OTP is powered up, no SPROM */ +#define CST4329_OTP_SEL 2 + /* OTP is powered down, SPROM is present */ +#define CST4329_OTP_PWRDN 3 + +#define CST4329_SPI_SDIO_MODE_MASK 0x00000004 +#define CST4329_SPI_SDIO_MODE_SHIFT 2 + +/* 43224 chip-specific ChipControl register bits */ +#define CCTRL43224_GPIO_TOGGLE 0x8000 + /* 12 mA drive strength */ +#define CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 + /* 12 mA drive strength for later 43224s */ +#define CCTRL_43224B0_12MA_LED_DRIVE 0xF0 + +/* 43236 Chip specific ChipStatus register bits */ +#define CST43236_SFLASH_MASK 0x00000040 +#define CST43236_OTP_MASK 0x00000080 +#define CST43236_HSIC_MASK 0x00000100 /* USB/HSIC */ +#define CST43236_BP_CLK 0x00000200 /* 120/96Mbps */ +#define CST43236_BOOT_MASK 0x00001800 +#define CST43236_BOOT_SHIFT 11 +#define CST43236_BOOT_FROM_SRAM 0 /* boot from SRAM, ARM in reset */ +#define CST43236_BOOT_FROM_ROM 1 /* boot from ROM */ +#define CST43236_BOOT_FROM_FLASH 2 /* boot from FLASH */ +#define CST43236_BOOT_FROM_INVALID 3 + +/* 4331 chip-specific ChipControl register bits */ + /* 0 disable */ +#define CCTRL4331_BT_COEXIST (1<<0) + /* 0 SECI is disabled (JTAG functional) */ +#define CCTRL4331_SECI (1<<1) + /* 0 disable */ +#define CCTRL4331_EXT_LNA (1<<2) + /* sprom/gpio13-15 mux */ +#define CCTRL4331_SPROM_GPIO13_15 (1<<3) + /* 0 ext pa disable, 1 ext pa enabled */ +#define CCTRL4331_EXTPA_EN (1<<4) + /* set drive out GPIO_CLK on sprom_cs pin */ +#define CCTRL4331_GPIOCLK_ON_SPROMCS (1<<5) + /* use sprom_cs pin as PCIE mdio interface */ +#define CCTRL4331_PCIE_MDIO_ON_SPROMCS (1<<6) + /* aband extpa will be at gpio2/5 and sprom_dout */ +#define CCTRL4331_EXTPA_ON_GPIO2_5 (1<<7) + /* override core control on pipe_AuxClkEnable */ +#define CCTRL4331_OVR_PIPEAUXCLKEN (1<<8) + /* override core control on pipe_AuxPowerDown */ +#define CCTRL4331_OVR_PIPEAUXPWRDOWN (1<<9) + /* pcie_auxclkenable */ +#define CCTRL4331_PCIE_AUXCLKEN (1<<10) + /* pcie_pipe_pllpowerdown */ +#define CCTRL4331_PCIE_PIPE_PLLDOWN (1<<11) + /* enable bt_shd0 at gpio4 */ +#define CCTRL4331_BT_SHD0_ON_GPIO4 (1<<16) + /* enable bt_shd1 at gpio5 */ +#define CCTRL4331_BT_SHD1_ON_GPIO5 (1<<17) + +/* 4331 Chip specific ChipStatus register bits */ + /* crystal frequency 20/40Mhz */ +#define CST4331_XTAL_FREQ 0x00000001 +#define CST4331_SPROM_PRESENT 0x00000002 +#define CST4331_OTP_PRESENT 0x00000004 +#define CST4331_LDO_RF 0x00000008 +#define CST4331_LDO_PAR 0x00000010 + +/* 4319 chip-specific ChipStatus register bits */ +#define CST4319_SPI_CPULESSUSB 0x00000001 +#define CST4319_SPI_CLK_POL 0x00000002 +#define CST4319_SPI_CLK_PH 0x00000008 + /* gpio [7:6], SDIO CIS selection */ +#define CST4319_SPROM_OTP_SEL_MASK 0x000000c0 +#define CST4319_SPROM_OTP_SEL_SHIFT 6 + /* use default CIS, OTP is powered up */ +#define CST4319_DEFCIS_SEL 0x00000000 + /* use SPROM, OTP is powered up */ +#define CST4319_SPROM_SEL 0x00000040 + /* use OTP, OTP is powered up */ +#define CST4319_OTP_SEL 0x00000080 + /* use SPROM, OTP is powered down */ +#define CST4319_OTP_PWRDN 0x000000c0 + /* gpio [8], sdio/usb mode */ +#define CST4319_SDIO_USB_MODE 0x00000100 +#define CST4319_REMAP_SEL_MASK 0x00000600 +#define CST4319_ILPDIV_EN 0x00000800 +#define CST4319_XTAL_PD_POL 0x00001000 +#define CST4319_LPO_SEL 0x00002000 +#define CST4319_RES_INIT_MODE 0x0000c000 + /* PALDO is configured with external PNP */ +#define CST4319_PALDO_EXTPNP 0x00010000 +#define CST4319_CBUCK_MODE_MASK 0x00060000 +#define CST4319_CBUCK_MODE_BURST 0x00020000 +#define CST4319_CBUCK_MODE_LPBURST 0x00060000 +#define CST4319_RCAL_VALID 0x01000000 +#define CST4319_RCAL_VALUE_MASK 0x3e000000 +#define CST4319_RCAL_VALUE_SHIFT 25 + +/* 4336 chip-specific ChipStatus register bits */ +#define CST4336_SPI_MODE_MASK 0x00000001 +#define CST4336_SPROM_PRESENT 0x00000002 +#define CST4336_OTP_PRESENT 0x00000004 +#define CST4336_ARMREMAP_0 0x00000008 +#define CST4336_ILPDIV_EN_MASK 0x00000010 +#define CST4336_ILPDIV_EN_SHIFT 4 +#define CST4336_XTAL_PD_POL_MASK 0x00000020 +#define CST4336_XTAL_PD_POL_SHIFT 5 +#define CST4336_LPO_SEL_MASK 0x00000040 +#define CST4336_LPO_SEL_SHIFT 6 +#define CST4336_RES_INIT_MODE_MASK 0x00000180 +#define CST4336_RES_INIT_MODE_SHIFT 7 +#define CST4336_CBUCK_MODE_MASK 0x00000600 +#define CST4336_CBUCK_MODE_SHIFT 9 + +/* 4313 chip-specific ChipStatus register bits */ +#define CST4313_SPROM_PRESENT 1 +#define CST4313_OTP_PRESENT 2 +#define CST4313_SPROM_OTP_SEL_MASK 0x00000002 +#define CST4313_SPROM_OTP_SEL_SHIFT 0 + +/* 4313 Chip specific ChipControl register bits */ + /* 12 mA drive strengh for later 4313 */ +#define CCTRL_4313_12MA_LED_DRIVE 0x00000007 + +/* Manufacturer Ids */ +#define MFGID_ARM 0x43b +#define MFGID_BRCM 0x4bf +#define MFGID_MIPS 0x4a7 + +/* Enumeration ROM registers */ +#define ER_EROMENTRY 0x000 +#define ER_REMAPCONTROL 0xe00 +#define ER_REMAPSELECT 0xe04 +#define ER_MASTERSELECT 0xe10 +#define ER_ITCR 0xf00 +#define ER_ITIP 0xf04 + +/* Erom entries */ +#define ER_TAG 0xe +#define ER_TAG1 0x6 +#define ER_VALID 1 +#define ER_CI 0 +#define ER_MP 2 +#define ER_ADD 4 +#define ER_END 0xe +#define ER_BAD 0xffffffff + +/* EROM CompIdentA */ +#define CIA_MFG_MASK 0xfff00000 +#define CIA_MFG_SHIFT 20 +#define CIA_CID_MASK 0x000fff00 +#define CIA_CID_SHIFT 8 +#define CIA_CCL_MASK 0x000000f0 +#define CIA_CCL_SHIFT 4 + +/* EROM CompIdentB */ +#define CIB_REV_MASK 0xff000000 +#define CIB_REV_SHIFT 24 +#define CIB_NSW_MASK 0x00f80000 +#define CIB_NSW_SHIFT 19 +#define CIB_NMW_MASK 0x0007c000 +#define CIB_NMW_SHIFT 14 +#define CIB_NSP_MASK 0x00003e00 +#define CIB_NSP_SHIFT 9 +#define CIB_NMP_MASK 0x000001f0 +#define CIB_NMP_SHIFT 4 + +/* EROM AddrDesc */ +#define AD_ADDR_MASK 0xfffff000 +#define AD_SP_MASK 0x00000f00 +#define AD_SP_SHIFT 8 +#define AD_ST_MASK 0x000000c0 +#define AD_ST_SHIFT 6 +#define AD_ST_SLAVE 0x00000000 +#define AD_ST_BRIDGE 0x00000040 +#define AD_ST_SWRAP 0x00000080 +#define AD_ST_MWRAP 0x000000c0 +#define AD_SZ_MASK 0x00000030 +#define AD_SZ_SHIFT 4 +#define AD_SZ_4K 0x00000000 +#define AD_SZ_8K 0x00000010 +#define AD_SZ_16K 0x00000020 +#define AD_SZ_SZD 0x00000030 +#define AD_AG32 0x00000008 +#define AD_ADDR_ALIGN 0x00000fff +#define AD_SZ_BASE 0x00001000 /* 4KB */ + +/* EROM SizeDesc */ +#define SD_SZ_MASK 0xfffff000 +#define SD_SG32 0x00000008 +#define SD_SZ_ALIGN 0x00000fff + +/* PCI config space bit 4 for 4306c0 slow clock source */ +#define PCI_CFG_GPIO_SCS 0x10 +/* PCI config space GPIO 14 for Xtal power-up */ +#define PCI_CFG_GPIO_XTAL 0x40 +/* PCI config space GPIO 15 for PLL power-down */ +#define PCI_CFG_GPIO_PLL 0x80 + +/* power control defines */ +#define PLL_DELAY 150 /* us pll on delay */ +#define FREF_DELAY 200 /* us fref change delay */ +#define XTAL_ON_DELAY 1000 /* us crystal power-on delay */ + +/* resetctrl */ +#define AIRC_RESET 1 + +#define NOREV -1 /* Invalid rev */ + +/* GPIO Based LED powersave defines */ +#define DEFAULT_GPIO_ONTIME 10 /* Default: 10% on */ +#define DEFAULT_GPIO_OFFTIME 90 /* Default: 10% on */ + +/* When Srom support present, fields in sromcontrol */ +#define SRC_START 0x80000000 +#define SRC_BUSY 0x80000000 +#define SRC_OPCODE 0x60000000 +#define SRC_OP_READ 0x00000000 +#define SRC_OP_WRITE 0x20000000 +#define SRC_OP_WRDIS 0x40000000 +#define SRC_OP_WREN 0x60000000 +#define SRC_OTPSEL 0x00000010 +#define SRC_LOCK 0x00000008 +#define SRC_SIZE_MASK 0x00000006 +#define SRC_SIZE_1K 0x00000000 +#define SRC_SIZE_4K 0x00000002 +#define SRC_SIZE_16K 0x00000004 +#define SRC_SIZE_SHIFT 1 +#define SRC_PRESENT 0x00000001 + +/* External PA enable mask */ +#define GPIO_CTRL_EPA_EN_MASK 0x40 + +#define DEFAULT_GPIOTIMERVAL \ + ((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME) + +#define BADIDX (SI_MAXCORES + 1) + +#define IS_SIM(chippkg) \ + ((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID)) + +#define GOODCOREADDR(x, b) \ + (((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \ + IS_ALIGNED((x), SI_CORE_SIZE)) + +struct aidmp { + u32 oobselina30; /* 0x000 */ + u32 oobselina74; /* 0x004 */ + u32 PAD[6]; + u32 oobselinb30; /* 0x020 */ + u32 oobselinb74; /* 0x024 */ + u32 PAD[6]; + u32 oobselinc30; /* 0x040 */ + u32 oobselinc74; /* 0x044 */ + u32 PAD[6]; + u32 oobselind30; /* 0x060 */ + u32 oobselind74; /* 0x064 */ + u32 PAD[38]; + u32 oobselouta30; /* 0x100 */ + u32 oobselouta74; /* 0x104 */ + u32 PAD[6]; + u32 oobseloutb30; /* 0x120 */ + u32 oobseloutb74; /* 0x124 */ + u32 PAD[6]; + u32 oobseloutc30; /* 0x140 */ + u32 oobseloutc74; /* 0x144 */ + u32 PAD[6]; + u32 oobseloutd30; /* 0x160 */ + u32 oobseloutd74; /* 0x164 */ + u32 PAD[38]; + u32 oobsynca; /* 0x200 */ + u32 oobseloutaen; /* 0x204 */ + u32 PAD[6]; + u32 oobsyncb; /* 0x220 */ + u32 oobseloutben; /* 0x224 */ + u32 PAD[6]; + u32 oobsyncc; /* 0x240 */ + u32 oobseloutcen; /* 0x244 */ + u32 PAD[6]; + u32 oobsyncd; /* 0x260 */ + u32 oobseloutden; /* 0x264 */ + u32 PAD[38]; + u32 oobaextwidth; /* 0x300 */ + u32 oobainwidth; /* 0x304 */ + u32 oobaoutwidth; /* 0x308 */ + u32 PAD[5]; + u32 oobbextwidth; /* 0x320 */ + u32 oobbinwidth; /* 0x324 */ + u32 oobboutwidth; /* 0x328 */ + u32 PAD[5]; + u32 oobcextwidth; /* 0x340 */ + u32 oobcinwidth; /* 0x344 */ + u32 oobcoutwidth; /* 0x348 */ + u32 PAD[5]; + u32 oobdextwidth; /* 0x360 */ + u32 oobdinwidth; /* 0x364 */ + u32 oobdoutwidth; /* 0x368 */ + u32 PAD[37]; + u32 ioctrlset; /* 0x400 */ + u32 ioctrlclear; /* 0x404 */ + u32 ioctrl; /* 0x408 */ + u32 PAD[61]; + u32 iostatus; /* 0x500 */ + u32 PAD[127]; + u32 ioctrlwidth; /* 0x700 */ + u32 iostatuswidth; /* 0x704 */ + u32 PAD[62]; + u32 resetctrl; /* 0x800 */ + u32 resetstatus; /* 0x804 */ + u32 resetreadid; /* 0x808 */ + u32 resetwriteid; /* 0x80c */ + u32 PAD[60]; + u32 errlogctrl; /* 0x900 */ + u32 errlogdone; /* 0x904 */ + u32 errlogstatus; /* 0x908 */ + u32 errlogaddrlo; /* 0x90c */ + u32 errlogaddrhi; /* 0x910 */ + u32 errlogid; /* 0x914 */ + u32 errloguser; /* 0x918 */ + u32 errlogflags; /* 0x91c */ + u32 PAD[56]; + u32 intstatus; /* 0xa00 */ + u32 PAD[127]; + u32 config; /* 0xe00 */ + u32 PAD[63]; + u32 itcr; /* 0xf00 */ + u32 PAD[3]; + u32 itipooba; /* 0xf10 */ + u32 itipoobb; /* 0xf14 */ + u32 itipoobc; /* 0xf18 */ + u32 itipoobd; /* 0xf1c */ + u32 PAD[4]; + u32 itipoobaout; /* 0xf30 */ + u32 itipoobbout; /* 0xf34 */ + u32 itipoobcout; /* 0xf38 */ + u32 itipoobdout; /* 0xf3c */ + u32 PAD[4]; + u32 itopooba; /* 0xf50 */ + u32 itopoobb; /* 0xf54 */ + u32 itopoobc; /* 0xf58 */ + u32 itopoobd; /* 0xf5c */ + u32 PAD[4]; + u32 itopoobain; /* 0xf70 */ + u32 itopoobbin; /* 0xf74 */ + u32 itopoobcin; /* 0xf78 */ + u32 itopoobdin; /* 0xf7c */ + u32 PAD[4]; + u32 itopreset; /* 0xf90 */ + u32 PAD[15]; + u32 peripherialid4; /* 0xfd0 */ + u32 peripherialid5; /* 0xfd4 */ + u32 peripherialid6; /* 0xfd8 */ + u32 peripherialid7; /* 0xfdc */ + u32 peripherialid0; /* 0xfe0 */ + u32 peripherialid1; /* 0xfe4 */ + u32 peripherialid2; /* 0xfe8 */ + u32 peripherialid3; /* 0xfec */ + u32 componentid0; /* 0xff0 */ + u32 componentid1; /* 0xff4 */ + u32 componentid2; /* 0xff8 */ + u32 componentid3; /* 0xffc */ +}; + +static bool +ai_buscore_setup(struct si_info *sii, struct bcma_device *cc) +{ + /* no cores found, bail out */ + if (cc->bus->nr_cores == 0) + return false; + + /* get chipcommon rev */ + sii->pub.ccrev = cc->id.rev; + + /* get chipcommon chipstatus */ + sii->chipst = bcma_read32(cc, CHIPCREGOFFS(chipstatus)); + + /* get chipcommon capabilites */ + sii->pub.cccaps = bcma_read32(cc, CHIPCREGOFFS(capabilities)); + + /* get pmu rev and caps */ + if (ai_get_cccaps(&sii->pub) & CC_CAP_PMU) { + sii->pub.pmucaps = bcma_read32(cc, + CHIPCREGOFFS(pmucapabilities)); + sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK; + } + + return true; +} + +static struct si_info *ai_doattach(struct si_info *sii, + struct bcma_bus *pbus) +{ + struct si_pub *sih = &sii->pub; + struct bcma_device *cc; + + sii->icbus = pbus; + sii->pcibus = pbus->host_pci; + + /* switch to Chipcommon core */ + cc = pbus->drv_cc.core; + + sih->chip = pbus->chipinfo.id; + sih->chiprev = pbus->chipinfo.rev; + sih->chippkg = pbus->chipinfo.pkg; + sih->boardvendor = pbus->boardinfo.vendor; + sih->boardtype = pbus->boardinfo.type; + + if (!ai_buscore_setup(sii, cc)) + goto exit; + + /* === NVRAM, clock is ready === */ + bcma_write32(cc, CHIPCREGOFFS(gpiopullup), 0); + bcma_write32(cc, CHIPCREGOFFS(gpiopulldown), 0); + + /* PMU specific initializations */ + if (ai_get_cccaps(sih) & CC_CAP_PMU) { + (void)si_pmu_measure_alpclk(sih); + } + + return sii; + + exit: + + return NULL; +} + +/* + * Allocate a si handle and do the attach. + */ +struct si_pub * +ai_attach(struct bcma_bus *pbus) +{ + struct si_info *sii; + + /* alloc struct si_info */ + sii = kzalloc(sizeof(struct si_info), GFP_ATOMIC); + if (sii == NULL) + return NULL; + + if (ai_doattach(sii, pbus) == NULL) { + kfree(sii); + return NULL; + } + + return (struct si_pub *) sii; +} + +/* may be called with core in reset */ +void ai_detach(struct si_pub *sih) +{ + struct si_info *sii; + + sii = container_of(sih, struct si_info, pub); + + if (sii == NULL) + return; + + kfree(sii); +} + +/* + * read/modify chipcommon core register. + */ +uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val) +{ + struct bcma_device *cc; + u32 w; + struct si_info *sii; + + sii = container_of(sih, struct si_info, pub); + cc = sii->icbus->drv_cc.core; + + /* mask and set */ + if (mask || val) + bcma_maskset32(cc, regoff, ~mask, val); + + /* readback */ + w = bcma_read32(cc, regoff); + + return w; +} + +/* return the slow clock source - LPO, XTAL, or PCI */ +static uint ai_slowclk_src(struct si_pub *sih, struct bcma_device *cc) +{ + return SCC_SS_XTAL; +} + +/* +* return the ILP (slowclock) min or max frequency +* precondition: we've established the chip has dynamic clk control +*/ +static uint ai_slowclk_freq(struct si_pub *sih, bool max_freq, + struct bcma_device *cc) +{ + uint div; + + /* Chipc rev 10 is InstaClock */ + div = bcma_read32(cc, CHIPCREGOFFS(system_clk_ctl)); + div = 4 * ((div >> SYCC_CD_SHIFT) + 1); + return max_freq ? XTALMAXFREQ : (XTALMINFREQ / div); +} + +static void +ai_clkctl_setdelay(struct si_pub *sih, struct bcma_device *cc) +{ + uint slowmaxfreq, pll_delay, slowclk; + uint pll_on_delay, fref_sel_delay; + + pll_delay = PLL_DELAY; + + /* + * If the slow clock is not sourced by the xtal then + * add the xtal_on_delay since the xtal will also be + * powered down by dynamic clk control logic. + */ + + slowclk = ai_slowclk_src(sih, cc); + if (slowclk != SCC_SS_XTAL) + pll_delay += XTAL_ON_DELAY; + + /* Starting with 4318 it is ILP that is used for the delays */ + slowmaxfreq = + ai_slowclk_freq(sih, false, cc); + + pll_on_delay = ((slowmaxfreq * pll_delay) + 999999) / 1000000; + fref_sel_delay = ((slowmaxfreq * FREF_DELAY) + 999999) / 1000000; + + bcma_write32(cc, CHIPCREGOFFS(pll_on_delay), pll_on_delay); + bcma_write32(cc, CHIPCREGOFFS(fref_sel_delay), fref_sel_delay); +} + +/* initialize power control delay registers */ +void ai_clkctl_init(struct si_pub *sih) +{ + struct si_info *sii = container_of(sih, struct si_info, pub); + struct bcma_device *cc; + + if (!(ai_get_cccaps(sih) & CC_CAP_PWR_CTL)) + return; + + cc = sii->icbus->drv_cc.core; + if (cc == NULL) + return; + + /* set all Instaclk chip ILP to 1 MHz */ + bcma_maskset32(cc, CHIPCREGOFFS(system_clk_ctl), SYCC_CD_MASK, + (ILP_DIV_1MHZ << SYCC_CD_SHIFT)); + + ai_clkctl_setdelay(sih, cc); +} + +/* + * return the value suitable for writing to the + * dot11 core FAST_PWRUP_DELAY register + */ +u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih) +{ + struct si_info *sii; + struct bcma_device *cc; + uint slowminfreq; + u16 fpdelay; + + sii = container_of(sih, struct si_info, pub); + if (ai_get_cccaps(sih) & CC_CAP_PMU) { + fpdelay = si_pmu_fast_pwrup_delay(sih); + return fpdelay; + } + + if (!(ai_get_cccaps(sih) & CC_CAP_PWR_CTL)) + return 0; + + fpdelay = 0; + cc = sii->icbus->drv_cc.core; + if (cc) { + slowminfreq = ai_slowclk_freq(sih, false, cc); + fpdelay = (((bcma_read32(cc, CHIPCREGOFFS(pll_on_delay)) + 2) + * 1000000) + (slowminfreq - 1)) / slowminfreq; + } + return fpdelay; +} + +/* + * clock control policy function throught chipcommon + * + * set dynamic clk control mode (forceslow, forcefast, dynamic) + * returns true if we are forcing fast clock + * this is a wrapper over the next internal function + * to allow flexible policy settings for outside caller + */ +bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode) +{ + struct si_info *sii; + struct bcma_device *cc; + + sii = container_of(sih, struct si_info, pub); + + cc = sii->icbus->drv_cc.core; + bcma_core_set_clockmode(cc, mode); + return mode == BCMA_CLKMODE_FAST; +} + +/* Enable BT-COEX & Ex-PA for 4313 */ +void ai_epa_4313war(struct si_pub *sih) +{ + struct si_info *sii = container_of(sih, struct si_info, pub); + struct bcma_device *cc; + + cc = sii->icbus->drv_cc.core; + + /* EPA Fix */ + bcma_set32(cc, CHIPCREGOFFS(gpiocontrol), GPIO_CTRL_EPA_EN_MASK); +} + +/* check if the device is removed */ +bool ai_deviceremoved(struct si_pub *sih) +{ + u32 w = 0; + struct si_info *sii; + + sii = container_of(sih, struct si_info, pub); + + if (sii->icbus->hosttype != BCMA_HOSTTYPE_PCI) + return false; + + pci_read_config_dword(sii->pcibus, PCI_VENDOR_ID, &w); + if ((w & 0xFFFF) != PCI_VENDOR_ID_BROADCOM) + return true; + + return false; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h new file mode 100644 index 000000000000..2d08c155c23b --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2011 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 _BRCM_AIUTILS_H_ +#define _BRCM_AIUTILS_H_ + +#include + +#include "types.h" + +/* + * SOC Interconnect Address Map. + * All regions may not exist on all chips. + */ +/* each core gets 4Kbytes for registers */ +#define SI_CORE_SIZE 0x1000 +/* + * Max cores (this is arbitrary, for software + * convenience and could be changed if we + * make any larger chips + */ +#define SI_MAXCORES 16 + +/* Client Mode sb2pcitranslation2 size in bytes */ +#define SI_PCI_DMA_SZ 0x40000000 + +/* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), high 32 bits */ +#define SI_PCIE_DMA_H32 0x80000000 + +/* chipcommon being the first core: */ +#define SI_CC_IDX 0 + +/* SOC Interconnect types (aka chip types) */ +#define SOCI_AI 1 + +/* A register that is common to all cores to + * communicate w/PMU regarding clock control. + */ +#define SI_CLK_CTL_ST 0x1e0 /* clock control and status */ + +/* clk_ctl_st register */ +#define CCS_FORCEALP 0x00000001 /* force ALP request */ +#define CCS_FORCEHT 0x00000002 /* force HT request */ +#define CCS_FORCEILP 0x00000004 /* force ILP request */ +#define CCS_ALPAREQ 0x00000008 /* ALP Avail Request */ +#define CCS_HTAREQ 0x00000010 /* HT Avail Request */ +#define CCS_FORCEHWREQOFF 0x00000020 /* Force HW Clock Request Off */ +#define CCS_ERSRC_REQ_MASK 0x00000700 /* external resource requests */ +#define CCS_ERSRC_REQ_SHIFT 8 +#define CCS_ALPAVAIL 0x00010000 /* ALP is available */ +#define CCS_HTAVAIL 0x00020000 /* HT is available */ +#define CCS_BP_ON_APL 0x00040000 /* RO: running on ALP clock */ +#define CCS_BP_ON_HT 0x00080000 /* RO: running on HT clock */ +#define CCS_ERSRC_STS_MASK 0x07000000 /* external resource status */ +#define CCS_ERSRC_STS_SHIFT 24 + +/* HT avail in chipc and pcmcia on 4328a0 */ +#define CCS0_HTAVAIL 0x00010000 +/* ALP avail in chipc and pcmcia on 4328a0 */ +#define CCS0_ALPAVAIL 0x00020000 + +/* Not really related to SOC Interconnect, but a couple of software + * conventions for the use the flash space: + */ + +/* Minumum amount of flash we support */ +#define FLASH_MIN 0x00020000 /* Minimum flash size */ + +#define CC_SROM_OTP 0x800 /* SROM/OTP address space */ + +/* gpiotimerval */ +#define GPIO_ONTIME_SHIFT 16 + +/* Fields in clkdiv */ +#define CLKD_OTP 0x000f0000 +#define CLKD_OTP_SHIFT 16 + +/* dynamic clock control defines */ +#define LPOMINFREQ 25000 /* low power oscillator min */ +#define LPOMAXFREQ 43000 /* low power oscillator max */ +#define XTALMINFREQ 19800000 /* 20 MHz - 1% */ +#define XTALMAXFREQ 20200000 /* 20 MHz + 1% */ +#define PCIMINFREQ 25000000 /* 25 MHz */ +#define PCIMAXFREQ 34000000 /* 33 MHz + fudge */ + +#define ILP_DIV_5MHZ 0 /* ILP = 5 MHz */ +#define ILP_DIV_1MHZ 4 /* ILP = 1 MHz */ + +/* clkctl xtal what flags */ +#define XTAL 0x1 /* primary crystal oscillator (2050) */ +#define PLL 0x2 /* main chip pll */ + +/* GPIO usage priorities */ +#define GPIO_DRV_PRIORITY 0 /* Driver */ +#define GPIO_APP_PRIORITY 1 /* Application */ +#define GPIO_HI_PRIORITY 2 /* Highest priority. Ignore GPIO + * reservation + */ + +/* GPIO pull up/down */ +#define GPIO_PULLUP 0 +#define GPIO_PULLDN 1 + +/* GPIO event regtype */ +#define GPIO_REGEVT 0 /* GPIO register event */ +#define GPIO_REGEVT_INTMSK 1 /* GPIO register event int mask */ +#define GPIO_REGEVT_INTPOL 2 /* GPIO register event int polarity */ + +/* device path */ +#define SI_DEVPATH_BUFSZ 16 /* min buffer size in bytes */ + +/* SI routine enumeration: to be used by update function with multiple hooks */ +#define SI_DOATTACH 1 +#define SI_PCIDOWN 2 +#define SI_PCIUP 3 + +/* + * Data structure to export all chip specific common variables + * public (read-only) portion of aiutils handle returned by si_attach() + */ +struct si_pub { + int ccrev; /* chip common core rev */ + u32 cccaps; /* chip common capabilities */ + int pmurev; /* pmu core rev */ + u32 pmucaps; /* pmu capabilities */ + uint boardtype; /* board type */ + uint boardvendor; /* board vendor */ + uint chip; /* chip number */ + uint chiprev; /* chip revision */ + uint chippkg; /* chip package option */ +}; + +struct pci_dev; + +struct gpioh_item { + void *arg; + bool level; + void (*handler) (u32 stat, void *arg); + u32 event; + struct gpioh_item *next; +}; + +/* misc si info needed by some of the routines */ +struct si_info { + struct si_pub pub; /* back plane public state (must be first) */ + struct bcma_bus *icbus; /* handle to soc interconnect bus */ + struct pci_dev *pcibus; /* handle to pci bus */ + + u32 chipst; /* chip status */ +}; + +/* + * Many of the routines below take an 'sih' handle as their first arg. + * Allocate this by calling si_attach(). Free it by calling si_detach(). + * At any one time, the sih is logically focused on one particular si core + * (the "current core"). + * Use si_setcore() or si_setcoreidx() to change the association to another core + */ + + +/* AMBA Interconnect exported externs */ +u32 ai_core_cflags(struct bcma_device *core, u32 mask, u32 val); + +/* === exported functions === */ +struct si_pub *ai_attach(struct bcma_bus *pbus); +void ai_detach(struct si_pub *sih); +uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val); +void ai_clkctl_init(struct si_pub *sih); +u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih); +bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode); +bool ai_deviceremoved(struct si_pub *sih); + +/* Enable Ex-PA for 4313 */ +void ai_epa_4313war(struct si_pub *sih); + +static inline u32 ai_get_cccaps(struct si_pub *sih) +{ + return sih->cccaps; +} + +static inline int ai_get_pmurev(struct si_pub *sih) +{ + return sih->pmurev; +} + +static inline u32 ai_get_pmucaps(struct si_pub *sih) +{ + return sih->pmucaps; +} + +static inline uint ai_get_boardtype(struct si_pub *sih) +{ + return sih->boardtype; +} + +static inline uint ai_get_boardvendor(struct si_pub *sih) +{ + return sih->boardvendor; +} + +static inline uint ai_get_chip_id(struct si_pub *sih) +{ + return sih->chip; +} + +static inline uint ai_get_chiprev(struct si_pub *sih) +{ + return sih->chiprev; +} + +static inline uint ai_get_chippkg(struct si_pub *sih) +{ + return sih->chippkg; +} + +#endif /* _BRCM_AIUTILS_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c new file mode 100644 index 000000000000..fa391e4eb098 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c @@ -0,0 +1,1144 @@ +/* + * Copyright (c) 2010 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 "rate.h" +#include "scb.h" +#include "phy/phy_hal.h" +#include "antsel.h" +#include "main.h" +#include "ampdu.h" +#include "debug.h" +#include "brcms_trace_events.h" + +/* max number of mpdus in an ampdu */ +#define AMPDU_MAX_MPDU 32 +/* max number of mpdus in an ampdu to a legacy */ +#define AMPDU_NUM_MPDU_LEGACY 16 +/* max Tx ba window size (in pdu) */ +#define AMPDU_TX_BA_MAX_WSIZE 64 +/* default Tx ba window size (in pdu) */ +#define AMPDU_TX_BA_DEF_WSIZE 64 +/* default Rx ba window size (in pdu) */ +#define AMPDU_RX_BA_DEF_WSIZE 64 +/* max Rx ba window size (in pdu) */ +#define AMPDU_RX_BA_MAX_WSIZE 64 +/* max dur of tx ampdu (in msec) */ +#define AMPDU_MAX_DUR 5 +/* default tx retry limit */ +#define AMPDU_DEF_RETRY_LIMIT 5 +/* default tx retry limit at reg rate */ +#define AMPDU_DEF_RR_RETRY_LIMIT 2 +/* default ffpld reserved bytes */ +#define AMPDU_DEF_FFPLD_RSVD 2048 +/* # of inis to be freed on detach */ +#define AMPDU_INI_FREE 10 +/* max # of mpdus released at a time */ +#define AMPDU_SCB_MAX_RELEASE 20 + +#define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */ +#define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu + * without underflows + */ +#define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */ +#define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */ +#define FFPLD_PLD_INCR 1000 /* increments in bytes */ +#define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we + * accumulate between resets. + */ + +#define AMPDU_DELIMITER_LEN 4 + +/* max allowed number of mpdus in an ampdu (2 streams) */ +#define AMPDU_NUM_MPDU 16 + +#define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE) + +/* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */ +#define AMPDU_MAX_MPDU_OVERHEAD (FCS_LEN + DOT11_ICV_AES_LEN +\ + AMPDU_DELIMITER_LEN + 3\ + + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN) + +/* modulo add/sub, bound = 2^k */ +#define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1)) +#define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1)) + +/* structure to hold tx fifo information and pre-loading state + * counters specific to tx underflows of ampdus + * some counters might be redundant with the ones in wlc or ampdu structures. + * This allows to maintain a specific state independently of + * how often and/or when the wlc counters are updated. + * + * ampdu_pld_size: number of bytes to be pre-loaded + * mcs2ampdu_table: per-mcs max # of mpdus in an ampdu + * prev_txfunfl: num of underflows last read from the HW macstats counter + * accum_txfunfl: num of underflows since we modified pld params + * accum_txampdu: num of tx ampdu since we modified pld params + * prev_txampdu: previous reading of tx ampdu + * dmaxferrate: estimated dma avg xfer rate in kbits/sec + */ +struct brcms_fifo_info { + u16 ampdu_pld_size; + u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; + u16 prev_txfunfl; + u32 accum_txfunfl; + u32 accum_txampdu; + u32 prev_txampdu; + u32 dmaxferrate; +}; + +/* AMPDU module specific state + * + * wlc: pointer to main wlc structure + * scb_handle: scb cubby handle to retrieve data from scb + * ini_enable: per-tid initiator enable/disable of ampdu + * ba_tx_wsize: Tx ba window size (in pdu) + * ba_rx_wsize: Rx ba window size (in pdu) + * retry_limit: mpdu transmit retry limit + * rr_retry_limit: mpdu transmit retry limit at regular rate + * retry_limit_tid: per-tid mpdu transmit retry limit + * rr_retry_limit_tid: per-tid mpdu transmit retry limit at regular rate + * mpdu_density: min mpdu spacing (0-7) ==> 2^(x-1)/8 usec + * max_pdu: max pdus allowed in ampdu + * dur: max duration of an ampdu (in msec) + * rx_factor: maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes + * ffpld_rsvd: number of bytes to reserve for preload + * max_txlen: max size of ampdu per mcs, bw and sgi + * mfbr: enable multiple fallback rate + * tx_max_funl: underflows should be kept such that + * (tx_max_funfl*underflows) < tx frames + * fifo_tb: table of fifo infos + */ +struct ampdu_info { + struct brcms_c_info *wlc; + int scb_handle; + u8 ini_enable[AMPDU_MAX_SCB_TID]; + u8 ba_tx_wsize; + u8 ba_rx_wsize; + u8 retry_limit; + u8 rr_retry_limit; + u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; + u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID]; + u8 mpdu_density; + s8 max_pdu; + u8 dur; + u8 rx_factor; + u32 ffpld_rsvd; + u32 max_txlen[MCS_TABLE_SIZE][2][2]; + bool mfbr; + u32 tx_max_funl; + struct brcms_fifo_info fifo_tb[NUM_FFPLD_FIFO]; +}; + +/* used for flushing ampdu packets */ +struct cb_del_ampdu_pars { + struct ieee80211_sta *sta; + u16 tid; +}; + +static void brcms_c_scb_ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur) +{ + u32 rate, mcs; + + for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) { + /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */ + /* 20MHz, No SGI */ + rate = mcs_2_rate(mcs, false, false); + ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3; + /* 40 MHz, No SGI */ + rate = mcs_2_rate(mcs, true, false); + ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3; + /* 20MHz, SGI */ + rate = mcs_2_rate(mcs, false, true); + ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3; + /* 40 MHz, SGI */ + rate = mcs_2_rate(mcs, true, true); + ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3; + } +} + +static bool brcms_c_ampdu_cap(struct ampdu_info *ampdu) +{ + if (BRCMS_PHY_11N_CAP(ampdu->wlc->band)) + return true; + else + return false; +} + +static int brcms_c_ampdu_set(struct ampdu_info *ampdu, bool on) +{ + struct brcms_c_info *wlc = ampdu->wlc; + struct bcma_device *core = wlc->hw->d11core; + + wlc->pub->_ampdu = false; + + if (on) { + if (!(wlc->pub->_n_enab & SUPPORT_11N)) { + brcms_err(core, "wl%d: driver not nmode enabled\n", + wlc->pub->unit); + return -ENOTSUPP; + } + if (!brcms_c_ampdu_cap(ampdu)) { + brcms_err(core, "wl%d: device not ampdu capable\n", + wlc->pub->unit); + return -ENOTSUPP; + } + wlc->pub->_ampdu = on; + } + + return 0; +} + +static void brcms_c_ffpld_init(struct ampdu_info *ampdu) +{ + int i, j; + struct brcms_fifo_info *fifo; + + for (j = 0; j < NUM_FFPLD_FIFO; j++) { + fifo = (ampdu->fifo_tb + j); + fifo->ampdu_pld_size = 0; + for (i = 0; i <= FFPLD_MAX_MCS; i++) + fifo->mcs2ampdu_table[i] = 255; + fifo->dmaxferrate = 0; + fifo->accum_txampdu = 0; + fifo->prev_txfunfl = 0; + fifo->accum_txfunfl = 0; + + } +} + +struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc) +{ + struct ampdu_info *ampdu; + int i; + + ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC); + if (!ampdu) + return NULL; + + ampdu->wlc = wlc; + + for (i = 0; i < AMPDU_MAX_SCB_TID; i++) + ampdu->ini_enable[i] = true; + /* Disable ampdu for VO by default */ + ampdu->ini_enable[PRIO_8021D_VO] = false; + ampdu->ini_enable[PRIO_8021D_NC] = false; + + /* Disable ampdu for BK by default since not enough fifo space */ + ampdu->ini_enable[PRIO_8021D_NONE] = false; + ampdu->ini_enable[PRIO_8021D_BK] = false; + + ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE; + ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE; + ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY; + ampdu->max_pdu = AUTO; + ampdu->dur = AMPDU_MAX_DUR; + + ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD; + /* + * bump max ampdu rcv size to 64k for all 11n + * devices except 4321A0 and 4321A1 + */ + if (BRCMS_ISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2)) + ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_32K; + else + ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_64K; + ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT; + ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT; + + for (i = 0; i < AMPDU_MAX_SCB_TID; i++) { + ampdu->retry_limit_tid[i] = ampdu->retry_limit; + ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit; + } + + brcms_c_scb_ampdu_update_max_txlen(ampdu, ampdu->dur); + ampdu->mfbr = false; + /* try to set ampdu to the default value */ + brcms_c_ampdu_set(ampdu, wlc->pub->_ampdu); + + ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL; + brcms_c_ffpld_init(ampdu); + + return ampdu; +} + +void brcms_c_ampdu_detach(struct ampdu_info *ampdu) +{ + kfree(ampdu); +} + +static void brcms_c_scb_ampdu_update_config(struct ampdu_info *ampdu, + struct scb *scb) +{ + struct scb_ampdu *scb_ampdu = &scb->scb_ampdu; + int i; + + scb_ampdu->max_pdu = AMPDU_NUM_MPDU; + + /* go back to legacy size if some preloading is occurring */ + for (i = 0; i < NUM_FFPLD_FIFO; i++) { + if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR) + scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY; + } + + /* apply user override */ + if (ampdu->max_pdu != AUTO) + scb_ampdu->max_pdu = (u8) ampdu->max_pdu; + + scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, + AMPDU_SCB_MAX_RELEASE); + + if (scb_ampdu->max_rx_ampdu_bytes) + scb_ampdu->release = min_t(u8, scb_ampdu->release, + scb_ampdu->max_rx_ampdu_bytes / 1600); + + scb_ampdu->release = min(scb_ampdu->release, + ampdu->fifo_tb[TX_AC_BE_FIFO]. + mcs2ampdu_table[FFPLD_MAX_MCS]); +} + +static void brcms_c_scb_ampdu_update_config_all(struct ampdu_info *ampdu) +{ + brcms_c_scb_ampdu_update_config(ampdu, &du->wlc->pri_scb); +} + +static void brcms_c_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f) +{ + int i; + u32 phy_rate, dma_rate, tmp; + u8 max_mpdu; + struct brcms_fifo_info *fifo = (ampdu->fifo_tb + f); + + /* recompute the dma rate */ + /* note : we divide/multiply by 100 to avoid integer overflows */ + max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], + AMPDU_NUM_MPDU_LEGACY); + phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false); + dma_rate = + (((phy_rate / 100) * + (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size)) + / (max_mpdu * FFPLD_MPDU_SIZE)) * 100; + fifo->dmaxferrate = dma_rate; + + /* fill up the mcs2ampdu table; do not recalc the last mcs */ + dma_rate = dma_rate >> 7; + for (i = 0; i < FFPLD_MAX_MCS; i++) { + /* shifting to keep it within integer range */ + phy_rate = mcs_2_rate(i, true, false) >> 7; + if (phy_rate > dma_rate) { + tmp = ((fifo->ampdu_pld_size * phy_rate) / + ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1; + tmp = min_t(u32, tmp, 255); + fifo->mcs2ampdu_table[i] = (u8) tmp; + } + } +} + +/* evaluate the dma transfer rate using the tx underflows as feedback. + * If necessary, increase tx fifo preloading. If not enough, + * decrease maximum ampdu size for each mcs till underflows stop + * Return 1 if pre-loading not active, -1 if not an underflow event, + * 0 if pre-loading module took care of the event. + */ +static int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid) +{ + struct ampdu_info *ampdu = wlc->ampdu; + u32 phy_rate = mcs_2_rate(FFPLD_MAX_MCS, true, false); + u32 txunfl_ratio; + u8 max_mpdu; + u32 current_ampdu_cnt = 0; + u16 max_pld_size; + u32 new_txunfl; + struct brcms_fifo_info *fifo = (ampdu->fifo_tb + fid); + uint xmtfifo_sz; + u16 cur_txunfl; + + /* return if we got here for a different reason than underflows */ + cur_txunfl = brcms_b_read_shm(wlc->hw, + M_UCODE_MACSTAT + + offsetof(struct macstat, txfunfl[fid])); + new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl); + if (new_txunfl == 0) { + brcms_dbg_ht(wlc->hw->d11core, + "TX status FRAG set but no tx underflows\n"); + return -1; + } + fifo->prev_txfunfl = cur_txunfl; + + if (!ampdu->tx_max_funl) + return 1; + + /* check if fifo is big enough */ + if (brcms_b_xmtfifo_sz_get(wlc->hw, fid, &xmtfifo_sz)) + return -1; + + if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd) + return 1; + + max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd; + fifo->accum_txfunfl += new_txunfl; + + /* we need to wait for at least 10 underflows */ + if (fifo->accum_txfunfl < 10) + return 0; + + brcms_dbg_ht(wlc->hw->d11core, "ampdu_count %d tx_underflows %d\n", + current_ampdu_cnt, fifo->accum_txfunfl); + + /* + compute the current ratio of tx unfl per ampdu. + When the current ampdu count becomes too + big while the ratio remains small, we reset + the current count in order to not + introduce too big of a latency in detecting a + large amount of tx underflows later. + */ + + txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl; + + if (txunfl_ratio > ampdu->tx_max_funl) { + if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) + fifo->accum_txfunfl = 0; + + return 0; + } + max_mpdu = min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], + AMPDU_NUM_MPDU_LEGACY); + + /* In case max value max_pdu is already lower than + the fifo depth, there is nothing more we can do. + */ + + if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) { + fifo->accum_txfunfl = 0; + return 0; + } + + if (fifo->ampdu_pld_size < max_pld_size) { + + /* increment by TX_FIFO_PLD_INC bytes */ + fifo->ampdu_pld_size += FFPLD_PLD_INCR; + if (fifo->ampdu_pld_size > max_pld_size) + fifo->ampdu_pld_size = max_pld_size; + + /* update scb release size */ + brcms_c_scb_ampdu_update_config_all(ampdu); + + /* + * compute a new dma xfer rate for max_mpdu @ max mcs. + * This is the minimum dma rate that can achieve no + * underflow condition for the current mpdu size. + * + * note : we divide/multiply by 100 to avoid integer overflows + */ + fifo->dmaxferrate = + (((phy_rate / 100) * + (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size)) + / (max_mpdu * FFPLD_MPDU_SIZE)) * 100; + + brcms_dbg_ht(wlc->hw->d11core, + "DMA estimated transfer rate %d; " + "pre-load size %d\n", + fifo->dmaxferrate, fifo->ampdu_pld_size); + } else { + + /* decrease ampdu size */ + if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) { + if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255) + fifo->mcs2ampdu_table[FFPLD_MAX_MCS] = + AMPDU_NUM_MPDU_LEGACY - 1; + else + fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1; + + /* recompute the table */ + brcms_c_ffpld_calc_mcs2ampdu_table(ampdu, fid); + + /* update scb release size */ + brcms_c_scb_ampdu_update_config_all(ampdu); + } + } + fifo->accum_txfunfl = 0; + return 0; +} + +void +brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, + u8 ba_wsize, /* negotiated ba window size (in pdu) */ + uint max_rx_ampdu_bytes) /* from ht_cap in beacon */ +{ + struct scb_ampdu *scb_ampdu; + struct scb_ampdu_tid_ini *ini; + struct ampdu_info *ampdu = wlc->ampdu; + struct scb *scb = &wlc->pri_scb; + scb_ampdu = &scb->scb_ampdu; + + if (!ampdu->ini_enable[tid]) { + brcms_err(wlc->hw->d11core, "%s: Rejecting tid %d\n", + __func__, tid); + return; + } + + ini = &scb_ampdu->ini[tid]; + ini->tid = tid; + ini->scb = scb_ampdu->scb; + ini->ba_wsize = ba_wsize; + scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes; +} + +void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, + struct brcms_c_info *wlc) +{ + session->wlc = wlc; + skb_queue_head_init(&session->skb_list); + session->max_ampdu_len = 0; /* determined from first MPDU */ + session->max_ampdu_frames = 0; /* determined from first MPDU */ + session->ampdu_len = 0; + session->dma_len = 0; +} + +/* + * Preps the given packet for AMPDU based on the session data. If the + * frame cannot be accomodated in the current session, -ENOSPC is + * returned. + */ +int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, + struct sk_buff *p) +{ + struct brcms_c_info *wlc = session->wlc; + struct ampdu_info *ampdu = wlc->ampdu; + struct scb *scb = &wlc->pri_scb; + struct scb_ampdu *scb_ampdu = &scb->scb_ampdu; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p); + struct ieee80211_tx_rate *txrate = tx_info->status.rates; + struct d11txh *txh = (struct d11txh *)p->data; + unsigned ampdu_frames; + u8 ndelim, tid; + u8 *plcp; + uint len; + u16 mcl; + bool fbr_iscck; + bool rr; + + ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; + plcp = (u8 *)(txh + 1); + fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03); + len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : + BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); + len = roundup(len, 4) + (ndelim + 1) * AMPDU_DELIMITER_LEN; + + ampdu_frames = skb_queue_len(&session->skb_list); + if (ampdu_frames != 0) { + struct sk_buff *first; + + if (ampdu_frames + 1 > session->max_ampdu_frames || + session->ampdu_len + len > session->max_ampdu_len) + return -ENOSPC; + + /* + * We aren't really out of space if the new frame is of + * a different priority, but we want the same behaviour + * so return -ENOSPC anyway. + * + * XXX: The old AMPDU code did this, but is it really + * necessary? + */ + first = skb_peek(&session->skb_list); + if (p->priority != first->priority) + return -ENOSPC; + } + + /* + * Now that we're sure this frame can be accomodated, update the + * session information. + */ + session->ampdu_len += len; + session->dma_len += p->len; + + tid = (u8)p->priority; + + /* Handle retry limits */ + if (txrate[0].count <= ampdu->rr_retry_limit_tid[tid]) { + txrate[0].count++; + rr = true; + } else { + txrate[1].count++; + rr = false; + } + + if (ampdu_frames == 0) { + u8 plcp0, plcp3, is40, sgi, mcs; + uint fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; + struct brcms_fifo_info *f = &du->fifo_tb[fifo]; + + if (rr) { + plcp0 = plcp[0]; + plcp3 = plcp[3]; + } else { + plcp0 = txh->FragPLCPFallback[0]; + plcp3 = txh->FragPLCPFallback[3]; + + } + + /* Limit AMPDU size based on MCS */ + is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; + sgi = plcp3_issgi(plcp3) ? 1 : 0; + mcs = plcp0 & ~MIMO_PLCP_40MHZ; + session->max_ampdu_len = min(scb_ampdu->max_rx_ampdu_bytes, + ampdu->max_txlen[mcs][is40][sgi]); + + session->max_ampdu_frames = scb_ampdu->max_pdu; + if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { + session->max_ampdu_frames = + min_t(u16, f->mcs2ampdu_table[mcs], + session->max_ampdu_frames); + } + } + + /* + * Treat all frames as "middle" frames of AMPDU here. First and + * last frames must be fixed up after all MPDUs have been prepped. + */ + mcl = le16_to_cpu(txh->MacTxControlLow); + mcl &= ~TXC_AMPDU_MASK; + mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT); + mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS); + txh->MacTxControlLow = cpu_to_le16(mcl); + txh->PreloadSize = 0; /* always default to 0 */ + + skb_queue_tail(&session->skb_list, p); + + return 0; +} + +void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session) +{ + struct brcms_c_info *wlc = session->wlc; + struct ampdu_info *ampdu = wlc->ampdu; + struct sk_buff *first, *last; + struct d11txh *txh; + struct ieee80211_tx_info *tx_info; + struct ieee80211_tx_rate *txrate; + u8 ndelim; + u8 *plcp; + uint len; + uint fifo; + struct brcms_fifo_info *f; + u16 mcl; + bool fbr; + bool fbr_iscck; + struct ieee80211_rts *rts; + bool use_rts = false, use_cts = false; + u16 dma_len = session->dma_len; + u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; + u32 rspec = 0, rspec_fallback = 0; + u32 rts_rspec = 0, rts_rspec_fallback = 0; + u8 plcp0, plcp3, is40, sgi, mcs; + u16 mch; + u8 preamble_type = BRCMS_GF_PREAMBLE; + u8 fbr_preamble_type = BRCMS_GF_PREAMBLE; + u8 rts_preamble_type = BRCMS_LONG_PREAMBLE; + u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE; + + if (skb_queue_empty(&session->skb_list)) + return; + + first = skb_peek(&session->skb_list); + last = skb_peek_tail(&session->skb_list); + + /* Need to fix up last MPDU first to adjust AMPDU length */ + txh = (struct d11txh *)last->data; + fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; + f = &du->fifo_tb[fifo]; + + mcl = le16_to_cpu(txh->MacTxControlLow); + mcl &= ~TXC_AMPDU_MASK; + mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT); + txh->MacTxControlLow = cpu_to_le16(mcl); + + /* remove the null delimiter after last mpdu */ + ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; + txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0; + session->ampdu_len -= ndelim * AMPDU_DELIMITER_LEN; + + /* remove the pad len from last mpdu */ + fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0); + len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : + BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); + session->ampdu_len -= roundup(len, 4) - len; + + /* Now fix up the first MPDU */ + tx_info = IEEE80211_SKB_CB(first); + txrate = tx_info->status.rates; + txh = (struct d11txh *)first->data; + plcp = (u8 *)(txh + 1); + rts = (struct ieee80211_rts *)&txh->rts_frame; + + mcl = le16_to_cpu(txh->MacTxControlLow); + /* If only one MPDU leave it marked as last */ + if (first != last) { + mcl &= ~TXC_AMPDU_MASK; + mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT); + } + mcl |= TXC_STARTMSDU; + if (ieee80211_is_rts(rts->frame_control)) { + mcl |= TXC_SENDRTS; + use_rts = true; + } + if (ieee80211_is_cts(rts->frame_control)) { + mcl |= TXC_SENDCTS; + use_cts = true; + } + txh->MacTxControlLow = cpu_to_le16(mcl); + + fbr = txrate[1].count > 0; + if (!fbr) { + plcp0 = plcp[0]; + plcp3 = plcp[3]; + } else { + plcp0 = txh->FragPLCPFallback[0]; + plcp3 = txh->FragPLCPFallback[3]; + } + is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; + sgi = plcp3_issgi(plcp3) ? 1 : 0; + mcs = plcp0 & ~MIMO_PLCP_40MHZ; + + if (is40) { + if (CHSPEC_SB_UPPER(wlc_phy_chanspec_get(wlc->band->pi))) + mimo_ctlchbw = PHY_TXC1_BW_20MHZ_UP; + else + mimo_ctlchbw = PHY_TXC1_BW_20MHZ; + } + + /* rebuild the rspec and rspec_fallback */ + rspec = RSPEC_MIMORATE; + rspec |= plcp[0] & ~MIMO_PLCP_40MHZ; + if (plcp[0] & MIMO_PLCP_40MHZ) + rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); + + fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03); + if (fbr_iscck) { + rspec_fallback = + cck_rspec(cck_phy2mac_rate(txh->FragPLCPFallback[0])); + } else { + rspec_fallback = RSPEC_MIMORATE; + rspec_fallback |= txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ; + if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ) + rspec_fallback |= PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT; + } + + if (use_rts || use_cts) { + rts_rspec = + brcms_c_rspec_to_rts_rspec(wlc, rspec, + false, mimo_ctlchbw); + rts_rspec_fallback = + brcms_c_rspec_to_rts_rspec(wlc, rspec_fallback, + false, mimo_ctlchbw); + } + + BRCMS_SET_MIMO_PLCP_LEN(plcp, session->ampdu_len); + /* mark plcp to indicate ampdu */ + BRCMS_SET_MIMO_PLCP_AMPDU(plcp); + + /* reset the mixed mode header durations */ + if (txh->MModeLen) { + u16 mmodelen = brcms_c_calc_lsig_len(wlc, rspec, + session->ampdu_len); + txh->MModeLen = cpu_to_le16(mmodelen); + preamble_type = BRCMS_MM_PREAMBLE; + } + if (txh->MModeFbrLen) { + u16 mmfbrlen = brcms_c_calc_lsig_len(wlc, rspec_fallback, + session->ampdu_len); + txh->MModeFbrLen = cpu_to_le16(mmfbrlen); + fbr_preamble_type = BRCMS_MM_PREAMBLE; + } + + /* set the preload length */ + if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { + dma_len = min(dma_len, f->ampdu_pld_size); + txh->PreloadSize = cpu_to_le16(dma_len); + } else { + txh->PreloadSize = 0; + } + + mch = le16_to_cpu(txh->MacTxControlHigh); + + /* update RTS dur fields */ + if (use_rts || use_cts) { + u16 durid; + if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) == + TXC_PREAMBLE_RTS_MAIN_SHORT) + rts_preamble_type = BRCMS_SHORT_PREAMBLE; + + if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) == + TXC_PREAMBLE_RTS_FB_SHORT) + rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE; + + durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec, + rspec, rts_preamble_type, + preamble_type, + session->ampdu_len, true); + rts->duration = cpu_to_le16(durid); + durid = brcms_c_compute_rtscts_dur(wlc, use_cts, + rts_rspec_fallback, + rspec_fallback, + rts_fbr_preamble_type, + fbr_preamble_type, + session->ampdu_len, true); + txh->RTSDurFallback = cpu_to_le16(durid); + /* set TxFesTimeNormal */ + txh->TxFesTimeNormal = rts->duration; + /* set fallback rate version of TxFesTimeNormal */ + txh->TxFesTimeFallback = txh->RTSDurFallback; + } + + /* set flag and plcp for fallback rate */ + if (fbr) { + mch |= TXC_AMPDU_FBR; + txh->MacTxControlHigh = cpu_to_le16(mch); + BRCMS_SET_MIMO_PLCP_AMPDU(plcp); + BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback); + } + + brcms_dbg_ht(wlc->hw->d11core, "wl%d: count %d ampdu_len %d\n", + wlc->pub->unit, skb_queue_len(&session->skb_list), + session->ampdu_len); +} + +static void +brcms_c_ampdu_rate_status(struct brcms_c_info *wlc, + struct ieee80211_tx_info *tx_info, + struct tx_status *txs, u8 mcs) +{ + struct ieee80211_tx_rate *txrate = tx_info->status.rates; + int i; + + /* clear the rest of the rates */ + for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) { + txrate[i].idx = -1; + txrate[i].count = 0; + } +} + +static void +brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, + struct sk_buff *p, struct tx_status *txs, + u32 s1, u32 s2) +{ + struct scb_ampdu *scb_ampdu; + struct brcms_c_info *wlc = ampdu->wlc; + struct scb_ampdu_tid_ini *ini; + u8 bitmap[8], queue, tid; + struct d11txh *txh; + u8 *plcp; + struct ieee80211_hdr *h; + u16 seq, start_seq = 0, bindex, index, mcl; + u8 mcs = 0; + bool ba_recd = false, ack_recd = false; + u8 suc_mpdu = 0, tot_mpdu = 0; + uint supr_status; + bool update_rate = true, retry = true, tx_error = false; + u16 mimoantsel = 0; + u8 antselid = 0; + u8 retry_limit, rr_retry_limit; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p); + +#ifdef DEBUG + u8 hole[AMPDU_MAX_MPDU]; + memset(hole, 0, sizeof(hole)); +#endif + + scb_ampdu = &scb->scb_ampdu; + tid = (u8) (p->priority); + + ini = &scb_ampdu->ini[tid]; + retry_limit = ampdu->retry_limit_tid[tid]; + rr_retry_limit = ampdu->rr_retry_limit_tid[tid]; + memset(bitmap, 0, sizeof(bitmap)); + queue = txs->frameid & TXFID_QUEUE_MASK; + supr_status = txs->status & TX_STATUS_SUPR_MASK; + + if (txs->status & TX_STATUS_ACK_RCV) { + if (TX_STATUS_SUPR_UF == supr_status) + update_rate = false; + + WARN_ON(!(txs->status & TX_STATUS_INTERMEDIATE)); + start_seq = txs->sequence >> SEQNUM_SHIFT; + bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >> + TX_STATUS_BA_BMAP03_SHIFT; + + WARN_ON(s1 & TX_STATUS_INTERMEDIATE); + WARN_ON(!(s1 & TX_STATUS_AMPDU)); + + bitmap[0] |= + (s1 & TX_STATUS_BA_BMAP47_MASK) << + TX_STATUS_BA_BMAP47_SHIFT; + bitmap[1] = (s1 >> 8) & 0xff; + bitmap[2] = (s1 >> 16) & 0xff; + bitmap[3] = (s1 >> 24) & 0xff; + + bitmap[4] = s2 & 0xff; + bitmap[5] = (s2 >> 8) & 0xff; + bitmap[6] = (s2 >> 16) & 0xff; + bitmap[7] = (s2 >> 24) & 0xff; + + ba_recd = true; + } else { + if (supr_status) { + update_rate = false; + if (supr_status == TX_STATUS_SUPR_BADCH) { + brcms_dbg_ht(wlc->hw->d11core, + "%s: Pkt tx suppressed, illegal channel possibly %d\n", + __func__, CHSPEC_CHANNEL( + wlc->default_bss->chanspec)); + } else { + if (supr_status != TX_STATUS_SUPR_FRAG) + brcms_err(wlc->hw->d11core, + "%s: supr_status 0x%x\n", + __func__, supr_status); + } + /* no need to retry for badch; will fail again */ + if (supr_status == TX_STATUS_SUPR_BADCH || + supr_status == TX_STATUS_SUPR_EXPTIME) { + retry = false; + } else if (supr_status == TX_STATUS_SUPR_EXPTIME) { + /* TX underflow: + * try tuning pre-loading or ampdu size + */ + } else if (supr_status == TX_STATUS_SUPR_FRAG) { + /* + * if there were underflows, but pre-loading + * is not active, notify rate adaptation. + */ + if (brcms_c_ffpld_check_txfunfl(wlc, queue) > 0) + tx_error = true; + } + } else if (txs->phyerr) { + update_rate = false; + brcms_dbg_ht(wlc->hw->d11core, + "%s: ampdu tx phy error (0x%x)\n", + __func__, txs->phyerr); + } + } + + /* loop through all pkts and retry if not acked */ + while (p) { + tx_info = IEEE80211_SKB_CB(p); + txh = (struct d11txh *) p->data; + mcl = le16_to_cpu(txh->MacTxControlLow); + plcp = (u8 *) (txh + 1); + h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN); + seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT; + + trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh)); + + if (tot_mpdu == 0) { + mcs = plcp[0] & MIMO_PLCP_MCS_MASK; + mimoantsel = le16_to_cpu(txh->ABI_MimoAntSel); + } + + index = TX_SEQ_TO_INDEX(seq); + ack_recd = false; + if (ba_recd) { + bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX); + brcms_dbg_ht(wlc->hw->d11core, + "tid %d seq %d, start_seq %d, bindex %d set %d, index %d\n", + tid, seq, start_seq, bindex, + isset(bitmap, bindex), index); + /* if acked then clear bit and free packet */ + if ((bindex < AMPDU_TX_BA_MAX_WSIZE) + && isset(bitmap, bindex)) { + ini->txretry[index] = 0; + + /* + * ampdu_ack_len: + * number of acked aggregated frames + */ + /* ampdu_len: number of aggregated frames */ + brcms_c_ampdu_rate_status(wlc, tx_info, txs, + mcs); + tx_info->flags |= IEEE80211_TX_STAT_ACK; + tx_info->flags |= IEEE80211_TX_STAT_AMPDU; + tx_info->status.ampdu_ack_len = + tx_info->status.ampdu_len = 1; + + skb_pull(p, D11_PHY_HDR_LEN); + skb_pull(p, D11_TXH_LEN); + + ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, + p); + ack_recd = true; + suc_mpdu++; + } + } + /* either retransmit or send bar if ack not recd */ + if (!ack_recd) { + if (retry && (ini->txretry[index] < (int)retry_limit)) { + int ret; + ini->txretry[index]++; + ret = brcms_c_txfifo(wlc, queue, p); + /* + * We shouldn't be out of space in the DMA + * ring here since we're reinserting a frame + * that was just pulled out. + */ + WARN_ONCE(ret, "queue %d out of txds\n", queue); + } else { + /* Retry timeout */ + ieee80211_tx_info_clear_status(tx_info); + tx_info->status.ampdu_ack_len = 0; + tx_info->status.ampdu_len = 1; + tx_info->flags |= + IEEE80211_TX_STAT_AMPDU_NO_BACK; + skb_pull(p, D11_PHY_HDR_LEN); + skb_pull(p, D11_TXH_LEN); + brcms_dbg_ht(wlc->hw->d11core, + "BA Timeout, seq %d\n", + seq); + ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, + p); + } + } + tot_mpdu++; + + /* break out if last packet of ampdu */ + if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) == + TXC_AMPDU_LAST) + break; + + p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); + } + + /* update rate state */ + antselid = brcms_c_antsel_antsel2id(wlc->asi, mimoantsel); +} + +void +brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, + struct sk_buff *p, struct tx_status *txs) +{ + struct scb_ampdu *scb_ampdu; + struct brcms_c_info *wlc = ampdu->wlc; + struct scb_ampdu_tid_ini *ini; + u32 s1 = 0, s2 = 0; + struct ieee80211_tx_info *tx_info; + + tx_info = IEEE80211_SKB_CB(p); + + /* BMAC_NOTE: For the split driver, second level txstatus comes later + * So if the ACK was received then wait for the second level else just + * call the first one + */ + if (txs->status & TX_STATUS_ACK_RCV) { + u8 status_delay = 0; + + /* wait till the next 8 bytes of txstatus is available */ + s1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus)); + while ((s1 & TXS_V) == 0) { + udelay(1); + status_delay++; + if (status_delay > 10) + return; /* error condition */ + s1 = bcma_read32(wlc->hw->d11core, + D11REGOFFS(frmtxstatus)); + } + + s2 = bcma_read32(wlc->hw->d11core, D11REGOFFS(frmtxstatus2)); + } + + if (scb) { + scb_ampdu = &scb->scb_ampdu; + ini = &scb_ampdu->ini[p->priority]; + brcms_c_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2); + } else { + /* loop through all pkts and free */ + u8 queue = txs->frameid & TXFID_QUEUE_MASK; + struct d11txh *txh; + u16 mcl; + while (p) { + tx_info = IEEE80211_SKB_CB(p); + txh = (struct d11txh *) p->data; + trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, + sizeof(*txh)); + mcl = le16_to_cpu(txh->MacTxControlLow); + brcmu_pkt_buf_free_skb(p); + /* break out if last packet of ampdu */ + if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) == + TXC_AMPDU_LAST) + break; + p = dma_getnexttxp(wlc->hw->di[queue], + DMA_RANGE_TRANSMITTED); + } + } +} + +void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc) +{ + char template[T_RAM_ACCESS_SZ * 2]; + + /* driver needs to write the ta in the template; ta is at offset 16 */ + memset(template, 0, sizeof(template)); + memcpy(template, wlc->pub->cur_etheraddr, ETH_ALEN); + brcms_b_write_template_ram(wlc->hw, (T_BA_TPL_BASE + 16), + (T_RAM_ACCESS_SZ * 2), + template); +} + +bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid) +{ + return wlc->ampdu->ini_enable[tid]; +} + +void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu) +{ + struct brcms_c_info *wlc = ampdu->wlc; + + /* + * Extend ucode internal watchdog timer to + * match larger received frames + */ + if ((ampdu->rx_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) == + IEEE80211_HT_MAX_AMPDU_64K) { + brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX); + brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX); + } else { + brcms_b_write_shm(wlc->hw, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF); + brcms_b_write_shm(wlc->hw, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF); + } +} + +/* + * callback function that helps invalidating ampdu packets in a DMA queue + */ +static void dma_cb_fn_ampdu(void *txi, void *arg_a) +{ + struct ieee80211_sta *sta = arg_a; + struct ieee80211_tx_info *tx_info = (struct ieee80211_tx_info *)txi; + + if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && + (tx_info->rate_driver_data[0] == sta || sta == NULL)) + tx_info->rate_driver_data[0] = NULL; +} + +/* + * When a remote party is no longer available for ampdu communication, any + * pending tx ampdu packets in the driver have to be flushed. + */ +void brcms_c_ampdu_flush(struct brcms_c_info *wlc, + struct ieee80211_sta *sta, u16 tid) +{ + brcms_c_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.h new file mode 100644 index 000000000000..03bdcf29bd50 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010 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 _BRCM_AMPDU_H_ +#define _BRCM_AMPDU_H_ + +/* + * Data structure representing an in-progress session for accumulating + * frames for AMPDU. + * + * wlc: pointer to common driver data + * skb_list: queue of skb's for AMPDU + * max_ampdu_len: maximum length for this AMPDU + * max_ampdu_frames: maximum number of frames for this AMPDU + * ampdu_len: total number of bytes accumulated for this AMPDU + * dma_len: DMA length of this AMPDU + */ +struct brcms_ampdu_session { + struct brcms_c_info *wlc; + struct sk_buff_head skb_list; + unsigned max_ampdu_len; + u16 max_ampdu_frames; + u16 ampdu_len; + u16 dma_len; +}; + +void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, + struct brcms_c_info *wlc); +int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, + struct sk_buff *p); +void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session); + +struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc); +void brcms_c_ampdu_detach(struct ampdu_info *ampdu); +void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, + struct sk_buff *p, struct tx_status *txs); +void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc); +void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu); + +#endif /* _BRCM_AMPDU_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c new file mode 100644 index 000000000000..54c616919590 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2010 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 "types.h" +#include "main.h" +#include "phy_shim.h" +#include "antsel.h" +#include "debug.h" + +#define ANT_SELCFG_AUTO 0x80 /* bit indicates antenna sel AUTO */ +#define ANT_SELCFG_MASK 0x33 /* antenna configuration mask */ +#define ANT_SELCFG_TX_UNICAST 0 /* unicast tx antenna configuration */ +#define ANT_SELCFG_RX_UNICAST 1 /* unicast rx antenna configuration */ +#define ANT_SELCFG_TX_DEF 2 /* default tx antenna configuration */ +#define ANT_SELCFG_RX_DEF 3 /* default rx antenna configuration */ + +/* useful macros */ +#define BRCMS_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf) +#define BRCMS_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf) +#define BRCMS_ANTIDX_11N(ant) (((BRCMS_ANTSEL_11N_0(ant)) << 2) +\ + (BRCMS_ANTSEL_11N_1(ant))) +#define BRCMS_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) +#define BRCMS_ANTSEL_11N(ant) ((ant) & ANT_SELCFG_MASK) + +/* antenna switch */ +/* defines for no boardlevel antenna diversity */ +#define ANT_SELCFG_DEF_2x2 0x01 /* default antenna configuration */ + +/* 2x3 antdiv defines and tables for GPIO communication */ +#define ANT_SELCFG_NUM_2x3 3 +#define ANT_SELCFG_DEF_2x3 0x01 /* default antenna configuration */ + +/* 2x4 antdiv rev4 defines and tables for GPIO communication */ +#define ANT_SELCFG_NUM_2x4 4 +#define ANT_SELCFG_DEF_2x4 0x02 /* default antenna configuration */ + +static const u16 mimo_2x4_div_antselpat_tbl[] = { + 0, 0, 0x9, 0xa, /* ant0: 0 ant1: 2,3 */ + 0, 0, 0x5, 0x6, /* ant0: 1 ant1: 2,3 */ + 0, 0, 0, 0, /* n.a. */ + 0, 0, 0, 0 /* n.a. */ +}; + +static const u8 mimo_2x4_div_antselid_tbl[16] = { + 0, 0, 0, 0, 0, 2, 3, 0, + 0, 0, 1, 0, 0, 0, 0, 0 /* pat to antselid */ +}; + +static const u16 mimo_2x3_div_antselpat_tbl[] = { + 16, 0, 1, 16, /* ant0: 0 ant1: 1,2 */ + 16, 16, 16, 16, /* n.a. */ + 16, 2, 16, 16, /* ant0: 2 ant1: 1 */ + 16, 16, 16, 16 /* n.a. */ +}; + +static const u8 mimo_2x3_div_antselid_tbl[16] = { + 0, 1, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 /* pat to antselid */ +}; + +/* boardlevel antenna selection: init antenna selection structure */ +static void +brcms_c_antsel_init_cfg(struct antsel_info *asi, struct brcms_antselcfg *antsel, + bool auto_sel) +{ + if (asi->antsel_type == ANTSEL_2x3) { + u8 antcfg_def = ANT_SELCFG_DEF_2x3 | + ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0); + antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def; + antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def; + antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def; + antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def; + antsel->num_antcfg = ANT_SELCFG_NUM_2x3; + + } else if (asi->antsel_type == ANTSEL_2x4) { + + antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4; + antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4; + antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4; + antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4; + antsel->num_antcfg = ANT_SELCFG_NUM_2x4; + + } else { /* no antenna selection available */ + + antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2; + antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2; + antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2; + antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2; + antsel->num_antcfg = 0; + } +} + +struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc) +{ + struct antsel_info *asi; + struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; + + asi = kzalloc(sizeof(struct antsel_info), GFP_ATOMIC); + if (!asi) + return NULL; + + asi->wlc = wlc; + asi->pub = wlc->pub; + asi->antsel_type = ANTSEL_NA; + asi->antsel_avail = false; + asi->antsel_antswitch = sprom->antswitch; + + if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) { + switch (asi->antsel_antswitch) { + case ANTSWITCH_TYPE_1: + case ANTSWITCH_TYPE_2: + case ANTSWITCH_TYPE_3: + /* 4321/2 board with 2x3 switch logic */ + asi->antsel_type = ANTSEL_2x3; + /* Antenna selection availability */ + if ((sprom->ant_available_bg == 7) || + (sprom->ant_available_a == 7)) { + asi->antsel_avail = true; + } else if ( + sprom->ant_available_bg == 3 || + sprom->ant_available_a == 3) { + asi->antsel_avail = false; + } else { + asi->antsel_avail = false; + brcms_err(wlc->hw->d11core, + "antsel_attach: 2o3 " + "board cfg invalid\n"); + } + + break; + default: + break; + } + } else if ((asi->pub->sromrev == 4) && + (sprom->ant_available_bg == 7) && + (sprom->ant_available_a == 0)) { + /* hack to match old 4321CB2 cards with 2of3 antenna switch */ + asi->antsel_type = ANTSEL_2x3; + asi->antsel_avail = true; + } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) { + asi->antsel_type = ANTSEL_2x4; + asi->antsel_avail = true; + } + + /* Set the antenna selection type for the low driver */ + brcms_b_antsel_type_set(wlc->hw, asi->antsel_type); + + /* Init (auto/manual) antenna selection */ + brcms_c_antsel_init_cfg(asi, &asi->antcfg_11n, true); + brcms_c_antsel_init_cfg(asi, &asi->antcfg_cur, true); + + return asi; +} + +void brcms_c_antsel_detach(struct antsel_info *asi) +{ + kfree(asi); +} + +/* + * boardlevel antenna selection: + * convert ant_cfg to mimo_antsel (ucode interface) + */ +static u16 brcms_c_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg) +{ + u8 idx = BRCMS_ANTIDX_11N(BRCMS_ANTSEL_11N(ant_cfg)); + u16 mimo_antsel = 0; + + if (asi->antsel_type == ANTSEL_2x4) { + /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ + mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf); + return mimo_antsel; + + } else if (asi->antsel_type == ANTSEL_2x3) { + /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ + mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf); + return mimo_antsel; + } + + return mimo_antsel; +} + +/* boardlevel antenna selection: ucode interface control */ +static int brcms_c_antsel_cfgupd(struct antsel_info *asi, + struct brcms_antselcfg *antsel) +{ + struct brcms_c_info *wlc = asi->wlc; + u8 ant_cfg; + u16 mimo_antsel; + + /* 1) Update TX antconfig for all frames that are not unicast data + * (aka default TX) + */ + ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF]; + mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg); + brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_TXDFLT, mimo_antsel); + /* + * Update driver stats for currently selected + * default tx/rx antenna config + */ + asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg; + + /* 2) Update RX antconfig for all frames that are not unicast data + * (aka default RX) + */ + ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF]; + mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg); + brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_RXDFLT, mimo_antsel); + /* + * Update driver stats for currently selected + * default tx/rx antenna config + */ + asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg; + + return 0; +} + +void brcms_c_antsel_init(struct antsel_info *asi) +{ + if ((asi->antsel_type == ANTSEL_2x3) || + (asi->antsel_type == ANTSEL_2x4)) + brcms_c_antsel_cfgupd(asi, &asi->antcfg_11n); +} + +/* boardlevel antenna selection: convert id to ant_cfg */ +static u8 brcms_c_antsel_id2antcfg(struct antsel_info *asi, u8 id) +{ + u8 antcfg = ANT_SELCFG_DEF_2x2; + + if (asi->antsel_type == ANTSEL_2x4) { + /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ + antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2)); + return antcfg; + + } else if (asi->antsel_type == ANTSEL_2x3) { + /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ + antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1)); + return antcfg; + } + + return antcfg; +} + +void +brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel, + u8 antselid, u8 fbantselid, u8 *antcfg, + u8 *fbantcfg) +{ + u8 ant; + + /* if use default, assign it and return */ + if (usedef) { + *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF]; + *fbantcfg = *antcfg; + return; + } + + if (!sel) { + *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; + *fbantcfg = *antcfg; + + } else { + ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; + if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) { + *antcfg = brcms_c_antsel_id2antcfg(asi, antselid); + *fbantcfg = brcms_c_antsel_id2antcfg(asi, fbantselid); + } else { + *antcfg = + asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; + *fbantcfg = *antcfg; + } + } + return; +} + +/* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */ +u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel) +{ + u8 antselid = 0; + + if (asi->antsel_type == ANTSEL_2x4) { + /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ + antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)]; + return antselid; + + } else if (asi->antsel_type == ANTSEL_2x3) { + /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ + antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)]; + return antselid; + } + + return antselid; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.h new file mode 100644 index 000000000000..a3d487ab1964 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010 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 _BRCM_ANTSEL_H_ +#define _BRCM_ANTSEL_H_ + +struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc); +void brcms_c_antsel_detach(struct antsel_info *asi); +void brcms_c_antsel_init(struct antsel_info *asi); +void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel, + u8 id, u8 fbid, u8 *antcfg, u8 *fbantcfg); +u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel); + +#endif /* _BRCM_ANTSEL_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac.h new file mode 100644 index 000000000000..a0da3248b942 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2011 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. + */ + +#if !defined(__TRACE_BRCMSMAC_H) || defined(TRACE_HEADER_MULTI_READ) +#define __TRACE_BRCMSMAC_H + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM brcmsmac + +/* + * We define a tracepoint, its arguments, its printk format and its + * 'fast binary record' layout. + */ +TRACE_EVENT(brcms_timer, + /* TPPROTO is the prototype of the function called by this tracepoint */ + TP_PROTO(struct brcms_timer *t), + /* + * TPARGS(firstarg, p) are the parameters names, same as found in the + * prototype. + */ + TP_ARGS(t), + /* + * Fast binary tracing: define the trace record via TP_STRUCT__entry(). + * You can think about it like a regular C structure local variable + * definition. + */ + TP_STRUCT__entry( + __field(uint, ms) + __field(uint, set) + __field(uint, periodic) + ), + TP_fast_assign( + __entry->ms = t->ms; + __entry->set = t->set; + __entry->periodic = t->periodic; + ), + TP_printk( + "ms=%u set=%u periodic=%u", + __entry->ms, __entry->set, __entry->periodic + ) +); + +TRACE_EVENT(brcms_dpc, + TP_PROTO(unsigned long data), + TP_ARGS(data), + TP_STRUCT__entry( + __field(unsigned long, data) + ), + TP_fast_assign( + __entry->data = data; + ), + TP_printk( + "data=%p", + (void *)__entry->data + ) +); + +TRACE_EVENT(brcms_macintstatus, + TP_PROTO(const struct device *dev, int in_isr, u32 macintstatus, + u32 mask), + TP_ARGS(dev, in_isr, macintstatus, mask), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(int, in_isr) + __field(u32, macintstatus) + __field(u32, mask) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + __entry->in_isr = in_isr; + __entry->macintstatus = macintstatus; + __entry->mask = mask; + ), + TP_printk("[%s] in_isr=%d macintstatus=%#x mask=%#x", __get_str(dev), + __entry->in_isr, __entry->macintstatus, __entry->mask) +); +#endif /* __TRACE_BRCMSMAC_H */ + +#ifdef CONFIG_BRCM_TRACING + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac +#include + +#endif /* CONFIG_BRCM_TRACING */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h new file mode 100644 index 000000000000..0e8a69ab909f --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011 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. + */ + +#if !defined(__TRACE_BRCMSMAC_MSG_H) || defined(TRACE_HEADER_MULTI_READ) +#define __TRACE_BRCMSMAC_MSG_H + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM brcmsmac_msg + +#define MAX_MSG_LEN 100 + +DECLARE_EVENT_CLASS(brcms_msg_event, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry( + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(brcms_msg_event, brcms_info, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(brcms_msg_event, brcms_warn, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(brcms_msg_event, brcms_err, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(brcms_msg_event, brcms_crit, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +TRACE_EVENT(brcms_dbg, + TP_PROTO(u32 level, const char *func, struct va_format *vaf), + TP_ARGS(level, func, vaf), + TP_STRUCT__entry( + __field(u32, level) + __string(func, func) + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + __entry->level = level; + __assign_str(func, func); + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s: %s", __get_str(func), __get_str(msg)) +); +#endif /* __TRACE_BRCMSMAC_MSG_H */ + +#ifdef CONFIG_BRCM_TRACING + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac_msg +#include + +#endif /* CONFIG_BRCM_TRACING */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h new file mode 100644 index 000000000000..cf2cc070f1e5 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2011 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. + */ + +#if !defined(__TRACE_BRCMSMAC_TX_H) || defined(TRACE_HEADER_MULTI_READ) +#define __TRACE_BRCMSMAC_TX_H + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM brcmsmac_tx + +TRACE_EVENT(brcms_txdesc, + TP_PROTO(const struct device *dev, + void *txh, size_t txh_len), + TP_ARGS(dev, txh, txh_len), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __dynamic_array(u8, txh, txh_len) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + memcpy(__get_dynamic_array(txh), txh, txh_len); + ), + TP_printk("[%s] txdesc", __get_str(dev)) +); + +TRACE_EVENT(brcms_txstatus, + TP_PROTO(const struct device *dev, u16 framelen, u16 frameid, + u16 status, u16 lasttxtime, u16 sequence, u16 phyerr, + u16 ackphyrxsh), + TP_ARGS(dev, framelen, frameid, status, lasttxtime, sequence, phyerr, + ackphyrxsh), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(u16, framelen) + __field(u16, frameid) + __field(u16, status) + __field(u16, lasttxtime) + __field(u16, sequence) + __field(u16, phyerr) + __field(u16, ackphyrxsh) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + __entry->framelen = framelen; + __entry->frameid = frameid; + __entry->status = status; + __entry->lasttxtime = lasttxtime; + __entry->sequence = sequence; + __entry->phyerr = phyerr; + __entry->ackphyrxsh = ackphyrxsh; + ), + TP_printk("[%s] FrameId %#04x TxStatus %#04x LastTxTime %#04x " + "Seq %#04x PHYTxStatus %#04x RxAck %#04x", + __get_str(dev), __entry->frameid, __entry->status, + __entry->lasttxtime, __entry->sequence, __entry->phyerr, + __entry->ackphyrxsh) +); + +TRACE_EVENT(brcms_ampdu_session, + TP_PROTO(const struct device *dev, unsigned max_ampdu_len, + u16 max_ampdu_frames, u16 ampdu_len, u16 ampdu_frames, + u16 dma_len), + TP_ARGS(dev, max_ampdu_len, max_ampdu_frames, ampdu_len, ampdu_frames, + dma_len), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(unsigned, max_ampdu_len) + __field(u16, max_ampdu_frames) + __field(u16, ampdu_len) + __field(u16, ampdu_frames) + __field(u16, dma_len) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + __entry->max_ampdu_len = max_ampdu_len; + __entry->max_ampdu_frames = max_ampdu_frames; + __entry->ampdu_len = ampdu_len; + __entry->ampdu_frames = ampdu_frames; + __entry->dma_len = dma_len; + ), + TP_printk("[%s] ampdu session max_len=%u max_frames=%u len=%u frames=%u dma_len=%u", + __get_str(dev), __entry->max_ampdu_len, + __entry->max_ampdu_frames, __entry->ampdu_len, + __entry->ampdu_frames, __entry->dma_len) +); +#endif /* __TRACE_BRCMSMAC_TX_H */ + +#ifdef CONFIG_BRCM_TRACING + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac_tx +#include + +#endif /* CONFIG_BRCM_TRACING */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.c new file mode 100644 index 000000000000..52fc9eeb5fa5 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 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 /* bug in tracepoint.h, it should include this */ + +#ifndef __CHECKER__ +#include "mac80211_if.h" +#define CREATE_TRACE_POINTS +#include "brcms_trace_events.h" +#endif diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.h new file mode 100644 index 000000000000..cbf2f06436fc --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 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 __BRCMS_TRACE_EVENTS_H +#define __BRCMS_TRACE_EVENTS_H + +#include +#include +#include +#include "mac80211_if.h" + +#ifndef CONFIG_BRCM_TRACING +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(...) +#undef DEFINE_EVENT +#define DEFINE_EVENT(evt_class, name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#endif + +#include "brcms_trace_brcmsmac.h" +#include "brcms_trace_brcmsmac_tx.h" +#include "brcms_trace_brcmsmac_msg.h" + +#endif /* __TRACE_BRCMSMAC_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c new file mode 100644 index 000000000000..635ae034c7e5 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c @@ -0,0 +1,774 @@ +/* + * Copyright (c) 2010 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 + +#include +#include "pub.h" +#include "phy/phy_hal.h" +#include "main.h" +#include "stf.h" +#include "channel.h" +#include "mac80211_if.h" +#include "debug.h" + +/* QDB() macro takes a dB value and converts to a quarter dB value */ +#define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR) + +#define LOCALE_MIMO_IDX_bn 0 +#define LOCALE_MIMO_IDX_11n 0 + +/* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */ +#define BRCMS_MAXPWR_MIMO_TBL_SIZE 14 + +/* maxpwr mapping to 5GHz band channels: + * maxpwr[0] - channels [34-48] + * maxpwr[1] - channels [52-60] + * maxpwr[2] - channels [62-64] + * maxpwr[3] - channels [100-140] + * maxpwr[4] - channels [149-165] + */ +#define BAND_5G_PWR_LVLS 5 /* 5 power levels for 5G */ + +#define LC(id) LOCALE_MIMO_IDX_ ## id + +#define LOCALES(mimo2, mimo5) \ + {LC(mimo2), LC(mimo5)} + +/* macro to get 5 GHz channel group index for tx power */ +#define CHANNEL_POWER_IDX_5G(c) (((c) < 52) ? 0 : \ + (((c) < 62) ? 1 : \ + (((c) < 100) ? 2 : \ + (((c) < 149) ? 3 : 4)))) + +#define BRCM_2GHZ_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 19, 0) +#define BRCM_2GHZ_2467_2472 REG_RULE(2467-10, 2472+10, 20, 0, 19, \ + NL80211_RRF_NO_IR) + +#define BRCM_5GHZ_5180_5240 REG_RULE(5180-10, 5240+10, 40, 0, 21, \ + NL80211_RRF_NO_IR) +#define BRCM_5GHZ_5260_5320 REG_RULE(5260-10, 5320+10, 40, 0, 21, \ + NL80211_RRF_DFS | \ + NL80211_RRF_NO_IR) +#define BRCM_5GHZ_5500_5700 REG_RULE(5500-10, 5700+10, 40, 0, 21, \ + NL80211_RRF_DFS | \ + NL80211_RRF_NO_IR) +#define BRCM_5GHZ_5745_5825 REG_RULE(5745-10, 5825+10, 40, 0, 21, \ + NL80211_RRF_NO_IR) + +static const struct ieee80211_regdomain brcms_regdom_x2 = { + .n_reg_rules = 6, + .alpha2 = "X2", + .reg_rules = { + BRCM_2GHZ_2412_2462, + BRCM_2GHZ_2467_2472, + BRCM_5GHZ_5180_5240, + BRCM_5GHZ_5260_5320, + BRCM_5GHZ_5500_5700, + BRCM_5GHZ_5745_5825, + } +}; + + /* locale per-channel tx power limits for MIMO frames + * maxpwr arrays are index by channel for 2.4 GHz limits, and + * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel) + */ +struct locale_mimo_info { + /* tx 20 MHz power limits, qdBm units */ + s8 maxpwr20[BRCMS_MAXPWR_MIMO_TBL_SIZE]; + /* tx 40 MHz power limits, qdBm units */ + s8 maxpwr40[BRCMS_MAXPWR_MIMO_TBL_SIZE]; +}; + +/* Country names and abbreviations with locale defined from ISO 3166 */ +struct country_info { + const u8 locale_mimo_2G; /* 2.4G mimo info */ + const u8 locale_mimo_5G; /* 5G mimo info */ +}; + +struct brcms_regd { + struct country_info country; + const struct ieee80211_regdomain *regdomain; +}; + +struct brcms_cm_info { + struct brcms_pub *pub; + struct brcms_c_info *wlc; + const struct brcms_regd *world_regd; +}; + +/* + * MIMO Locale Definitions - 2.4 GHz + */ +static const struct locale_mimo_info locale_bn = { + {QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), + QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), + QDB(13), QDB(13), QDB(13)}, + {0, 0, QDB(13), QDB(13), QDB(13), + QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), + QDB(13), 0, 0}, +}; + +static const struct locale_mimo_info *g_mimo_2g_table[] = { + &locale_bn +}; + +/* + * MIMO Locale Definitions - 5 GHz + */ +static const struct locale_mimo_info locale_11n = { + { /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)}, + {QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)}, +}; + +static const struct locale_mimo_info *g_mimo_5g_table[] = { + &locale_11n +}; + +static const struct brcms_regd cntry_locales[] = { + /* Worldwide RoW 2, must always be at index 0 */ + { + .country = LOCALES(bn, 11n), + .regdomain = &brcms_regdom_x2, + }, +}; + +static const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx) +{ + if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table)) + return NULL; + + return g_mimo_2g_table[locale_idx]; +} + +static const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx) +{ + if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table)) + return NULL; + + return g_mimo_5g_table[locale_idx]; +} + +/* + * Indicates whether the country provided is valid to pass + * to cfg80211 or not. + * + * returns true if valid; false if not. + */ +static bool brcms_c_country_valid(const char *ccode) +{ + /* + * only allow ascii alpha uppercase for the first 2 + * chars. + */ + if (!((0x80 & ccode[0]) == 0 && ccode[0] >= 0x41 && ccode[0] <= 0x5A && + (0x80 & ccode[1]) == 0 && ccode[1] >= 0x41 && ccode[1] <= 0x5A)) + return false; + + /* + * do not match ISO 3166-1 user assigned country codes + * that may be in the driver table + */ + if (!strcmp("AA", ccode) || /* AA */ + !strcmp("ZZ", ccode) || /* ZZ */ + ccode[0] == 'X' || /* XA - XZ */ + (ccode[0] == 'Q' && /* QM - QZ */ + (ccode[1] >= 'M' && ccode[1] <= 'Z'))) + return false; + + if (!strcmp("NA", ccode)) + return false; + + return true; +} + +static const struct brcms_regd *brcms_world_regd(const char *regdom, int len) +{ + const struct brcms_regd *regd = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(cntry_locales); i++) { + if (!strncmp(regdom, cntry_locales[i].regdomain->alpha2, len)) { + regd = &cntry_locales[i]; + break; + } + } + + return regd; +} + +static const struct brcms_regd *brcms_default_world_regd(void) +{ + return &cntry_locales[0]; +} + +/* JP, J1 - J10 are Japan ccodes */ +static bool brcms_c_japan_ccode(const char *ccode) +{ + return (ccode[0] == 'J' && + (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9'))); +} + +static void +brcms_c_channel_min_txpower_limits_with_local_constraint( + struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr, + u8 local_constraint_qdbm) +{ + int j; + + /* CCK Rates */ + for (j = 0; j < WL_TX_POWER_CCK_NUM; j++) + txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm); + + /* 20 MHz Legacy OFDM SISO */ + for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++) + txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm); + + /* 20 MHz Legacy OFDM CDD */ + for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) + txpwr->ofdm_cdd[j] = + min(txpwr->ofdm_cdd[j], local_constraint_qdbm); + + /* 40 MHz Legacy OFDM SISO */ + for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) + txpwr->ofdm_40_siso[j] = + min(txpwr->ofdm_40_siso[j], local_constraint_qdbm); + + /* 40 MHz Legacy OFDM CDD */ + for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) + txpwr->ofdm_40_cdd[j] = + min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm); + + /* 20MHz MCS 0-7 SISO */ + for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) + txpwr->mcs_20_siso[j] = + min(txpwr->mcs_20_siso[j], local_constraint_qdbm); + + /* 20MHz MCS 0-7 CDD */ + for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) + txpwr->mcs_20_cdd[j] = + min(txpwr->mcs_20_cdd[j], local_constraint_qdbm); + + /* 20MHz MCS 0-7 STBC */ + for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) + txpwr->mcs_20_stbc[j] = + min(txpwr->mcs_20_stbc[j], local_constraint_qdbm); + + /* 20MHz MCS 8-15 MIMO */ + for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++) + txpwr->mcs_20_mimo[j] = + min(txpwr->mcs_20_mimo[j], local_constraint_qdbm); + + /* 40MHz MCS 0-7 SISO */ + for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) + txpwr->mcs_40_siso[j] = + min(txpwr->mcs_40_siso[j], local_constraint_qdbm); + + /* 40MHz MCS 0-7 CDD */ + for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) + txpwr->mcs_40_cdd[j] = + min(txpwr->mcs_40_cdd[j], local_constraint_qdbm); + + /* 40MHz MCS 0-7 STBC */ + for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) + txpwr->mcs_40_stbc[j] = + min(txpwr->mcs_40_stbc[j], local_constraint_qdbm); + + /* 40MHz MCS 8-15 MIMO */ + for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++) + txpwr->mcs_40_mimo[j] = + min(txpwr->mcs_40_mimo[j], local_constraint_qdbm); + + /* 40MHz MCS 32 */ + txpwr->mcs32 = min(txpwr->mcs32, local_constraint_qdbm); + +} + +/* + * set the driver's current country and regulatory information + * using a country code as the source. Look up built in country + * information found with the country code. + */ +static void +brcms_c_set_country(struct brcms_cm_info *wlc_cm, + const struct brcms_regd *regd) +{ + struct brcms_c_info *wlc = wlc_cm->wlc; + + if ((wlc->pub->_n_enab & SUPPORT_11N) != + wlc->protection->nmode_user) + brcms_c_set_nmode(wlc); + + brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); + brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); + + brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); + + return; +} + +struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) +{ + struct brcms_cm_info *wlc_cm; + struct brcms_pub *pub = wlc->pub; + struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; + const char *ccode = sprom->alpha2; + int ccode_len = sizeof(sprom->alpha2); + + wlc_cm = kzalloc(sizeof(struct brcms_cm_info), GFP_ATOMIC); + if (wlc_cm == NULL) + return NULL; + wlc_cm->pub = pub; + wlc_cm->wlc = wlc; + wlc->cmi = wlc_cm; + + /* store the country code for passing up as a regulatory hint */ + wlc_cm->world_regd = brcms_world_regd(ccode, ccode_len); + if (brcms_c_country_valid(ccode)) + strncpy(wlc->pub->srom_ccode, ccode, ccode_len); + + /* + * If no custom world domain is found in the SROM, use the + * default "X2" domain. + */ + if (!wlc_cm->world_regd) { + wlc_cm->world_regd = brcms_default_world_regd(); + ccode = wlc_cm->world_regd->regdomain->alpha2; + ccode_len = BRCM_CNTRY_BUF_SZ - 1; + } + + /* save default country for exiting 11d regulatory mode */ + strncpy(wlc->country_default, ccode, ccode_len); + + /* initialize autocountry_default to driver default */ + strncpy(wlc->autocountry_default, ccode, ccode_len); + + brcms_c_set_country(wlc_cm, wlc_cm->world_regd); + + return wlc_cm; +} + +void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm) +{ + kfree(wlc_cm); +} + +void +brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, + u8 local_constraint_qdbm) +{ + struct brcms_c_info *wlc = wlc_cm->wlc; + struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan; + struct txpwr_limits txpwr; + + brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr); + + brcms_c_channel_min_txpower_limits_with_local_constraint( + wlc_cm, &txpwr, local_constraint_qdbm + ); + + /* set or restore gmode as required by regulatory */ + if (ch->flags & IEEE80211_CHAN_NO_OFDM) + brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false); + else + brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); + + brcms_b_set_chanspec(wlc->hw, chanspec, + !!(ch->flags & IEEE80211_CHAN_NO_IR), + &txpwr); +} + +void +brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, + struct txpwr_limits *txpwr) +{ + struct brcms_c_info *wlc = wlc_cm->wlc; + struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan; + uint i; + uint chan; + int maxpwr; + int delta; + const struct country_info *country; + struct brcms_band *band; + int conducted_max = BRCMS_TXPWR_MAX; + const struct locale_mimo_info *li_mimo; + int maxpwr20, maxpwr40; + int maxpwr_idx; + uint j; + + memset(txpwr, 0, sizeof(struct txpwr_limits)); + + if (WARN_ON(!ch)) + return; + + country = &wlc_cm->world_regd->country; + + chan = CHSPEC_CHANNEL(chanspec); + band = wlc->bandstate[chspec_bandunit(chanspec)]; + li_mimo = (band->bandtype == BRCM_BAND_5G) ? + brcms_c_get_mimo_5g(country->locale_mimo_5G) : + brcms_c_get_mimo_2g(country->locale_mimo_2G); + + delta = band->antgain; + + if (band->bandtype == BRCM_BAND_2G) + conducted_max = QDB(22); + + maxpwr = QDB(ch->max_power) - delta; + maxpwr = max(maxpwr, 0); + maxpwr = min(maxpwr, conducted_max); + + /* CCK txpwr limits for 2.4G band */ + if (band->bandtype == BRCM_BAND_2G) { + for (i = 0; i < BRCMS_NUM_RATES_CCK; i++) + txpwr->cck[i] = (u8) maxpwr; + } + + for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) { + txpwr->ofdm[i] = (u8) maxpwr; + + /* + * OFDM 40 MHz SISO has the same power as the corresponding + * MCS0-7 rate unless overriden by the locale specific code. + * We set this value to 0 as a flag (presumably 0 dBm isn't + * a possibility) and then copy the MCS0-7 value to the 40 MHz + * value if it wasn't explicitly set. + */ + txpwr->ofdm_40_siso[i] = 0; + + txpwr->ofdm_cdd[i] = (u8) maxpwr; + + txpwr->ofdm_40_cdd[i] = 0; + } + + delta = 0; + if (band->antgain > QDB(6)) + delta = band->antgain - QDB(6); /* Excess over 6 dB */ + + if (band->bandtype == BRCM_BAND_2G) + maxpwr_idx = (chan - 1); + else + maxpwr_idx = CHANNEL_POWER_IDX_5G(chan); + + maxpwr20 = li_mimo->maxpwr20[maxpwr_idx]; + maxpwr40 = li_mimo->maxpwr40[maxpwr_idx]; + + maxpwr20 = maxpwr20 - delta; + maxpwr20 = max(maxpwr20, 0); + maxpwr40 = maxpwr40 - delta; + maxpwr40 = max(maxpwr40, 0); + + /* Fill in the MCS 0-7 (SISO) rates */ + for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { + + /* + * 20 MHz has the same power as the corresponding OFDM rate + * unless overriden by the locale specific code. + */ + txpwr->mcs_20_siso[i] = txpwr->ofdm[i]; + txpwr->mcs_40_siso[i] = 0; + } + + /* Fill in the MCS 0-7 CDD rates */ + for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { + txpwr->mcs_20_cdd[i] = (u8) maxpwr20; + txpwr->mcs_40_cdd[i] = (u8) maxpwr40; + } + + /* + * These locales have SISO expressed in the + * table and override CDD later + */ + if (li_mimo == &locale_bn) { + if (li_mimo == &locale_bn) { + maxpwr20 = QDB(16); + maxpwr40 = 0; + + if (chan >= 3 && chan <= 11) + maxpwr40 = QDB(16); + } + + for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { + txpwr->mcs_20_siso[i] = (u8) maxpwr20; + txpwr->mcs_40_siso[i] = (u8) maxpwr40; + } + } + + /* Fill in the MCS 0-7 STBC rates */ + for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { + txpwr->mcs_20_stbc[i] = 0; + txpwr->mcs_40_stbc[i] = 0; + } + + /* Fill in the MCS 8-15 SDM rates */ + for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) { + txpwr->mcs_20_mimo[i] = (u8) maxpwr20; + txpwr->mcs_40_mimo[i] = (u8) maxpwr40; + } + + /* Fill in MCS32 */ + txpwr->mcs32 = (u8) maxpwr40; + + for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) { + if (txpwr->ofdm_40_cdd[i] == 0) + txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j]; + if (i == 0) { + i = i + 1; + if (txpwr->ofdm_40_cdd[i] == 0) + txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j]; + } + } + + /* + * Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO + * value if it wasn't provided explicitly. + */ + for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { + if (txpwr->mcs_40_siso[i] == 0) + txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i]; + } + + for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) { + if (txpwr->ofdm_40_siso[i] == 0) + txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j]; + if (i == 0) { + i = i + 1; + if (txpwr->ofdm_40_siso[i] == 0) + txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j]; + } + } + + /* + * Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding + * STBC values if they weren't provided explicitly. + */ + for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) { + if (txpwr->mcs_20_stbc[i] == 0) + txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i]; + + if (txpwr->mcs_40_stbc[i] == 0) + txpwr->mcs_40_stbc[i] = txpwr->mcs_40_cdd[i]; + } + + return; +} + +/* + * Verify the chanspec is using a legal set of parameters, i.e. that the + * chanspec specified a band, bw, ctl_sb and channel and that the + * combination could be legal given any set of circumstances. + * RETURNS: true is the chanspec is malformed, false if it looks good. + */ +static bool brcms_c_chspec_malformed(u16 chanspec) +{ + /* must be 2G or 5G band */ + if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec)) + return true; + /* must be 20 or 40 bandwidth */ + if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec)) + return true; + + /* 20MHZ b/w must have no ctl sb, 40 must have a ctl sb */ + if (CHSPEC_IS20(chanspec)) { + if (!CHSPEC_SB_NONE(chanspec)) + return true; + } else if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) { + return true; + } + + return false; +} + +/* + * Validate the chanspec for this locale, for 40MHZ we need to also + * check that the sidebands are valid 20MZH channels in this locale + * and they are also a legal HT combination + */ +static bool +brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec) +{ + struct brcms_c_info *wlc = wlc_cm->wlc; + u8 channel = CHSPEC_CHANNEL(chspec); + + /* check the chanspec */ + if (brcms_c_chspec_malformed(chspec)) { + brcms_err(wlc->hw->d11core, "wl%d: malformed chanspec 0x%x\n", + wlc->pub->unit, chspec); + return false; + } + + if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) != + chspec_bandunit(chspec)) + return false; + + return true; +} + +bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec) +{ + return brcms_c_valid_chanspec_ext(wlc_cm, chspec); +} + +static bool brcms_is_radar_freq(u16 center_freq) +{ + return center_freq >= 5260 && center_freq <= 5700; +} + +static void brcms_reg_apply_radar_flags(struct wiphy *wiphy) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + int i; + + sband = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!sband) + return; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (!brcms_is_radar_freq(ch->center_freq)) + continue; + + /* + * All channels in this range should be passive and have + * DFS enabled. + */ + if (!(ch->flags & IEEE80211_CHAN_DISABLED)) + ch->flags |= IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | + IEEE80211_CHAN_NO_IR; + } +} + +static void +brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + const struct ieee80211_reg_rule *rule; + int band, i; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (ch->flags & + (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR)) + continue; + + if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { + rule = freq_reg_info(wiphy, + MHZ_TO_KHZ(ch->center_freq)); + if (IS_ERR(rule)) + continue; + + if (!(rule->flags & NL80211_RRF_NO_IR)) + ch->flags &= ~IEEE80211_CHAN_NO_IR; + } else if (ch->beacon_found) { + ch->flags &= ~IEEE80211_CHAN_NO_IR; + } + } + } +} + +static void brcms_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct brcms_info *wl = hw->priv; + struct brcms_c_info *wlc = wl->wlc; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + int band, i; + bool ch_found = false; + + brcms_reg_apply_radar_flags(wiphy); + + if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) + brcms_reg_apply_beaconing_flags(wiphy, request->initiator); + + /* Disable radio if all channels disallowed by regulatory */ + for (band = 0; !ch_found && band < IEEE80211_NUM_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + + for (i = 0; !ch_found && i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (!(ch->flags & IEEE80211_CHAN_DISABLED)) + ch_found = true; + } + } + + if (ch_found) { + mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); + } else { + mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); + brcms_err(wlc->hw->d11core, + "wl%d: %s: no valid channel for \"%s\"\n", + wlc->pub->unit, __func__, request->alpha2); + } + + if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G) + wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi, + brcms_c_japan_ccode(request->alpha2)); +} + +void brcms_c_regd_init(struct brcms_c_info *wlc) +{ + struct wiphy *wiphy = wlc->wiphy; + const struct brcms_regd *regd = wlc->cmi->world_regd; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct brcms_chanvec sup_chan; + struct brcms_band *band; + int band_idx, i; + + /* Disable any channels not supported by the phy */ + for (band_idx = 0; band_idx < wlc->pub->_nbands; band_idx++) { + band = wlc->bandstate[band_idx]; + + wlc_phy_chanspec_band_validch(band->pi, band->bandtype, + &sup_chan); + + if (band_idx == BAND_2G_INDEX) + sband = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + sband = wiphy->bands[IEEE80211_BAND_5GHZ]; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + if (!isset(sup_chan.vec, ch->hw_value)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } + } + + wlc->wiphy->reg_notifier = brcms_reg_notifier; + wlc->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_STRICT_REG; + wiphy_apply_custom_regulatory(wlc->wiphy, regd->regdomain); + brcms_reg_apply_beaconing_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.h new file mode 100644 index 000000000000..39dd3a5b2979 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010 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 _BRCM_CHANNEL_H_ +#define _BRCM_CHANNEL_H_ + +/* conversion for phy txpwr calculations that use .25 dB units */ +#define BRCMS_TXPWR_DB_FACTOR 4 + +/* bits for locale_info flags */ +#define BRCMS_PEAK_CONDUCTED 0x00 /* Peak for locals */ +#define BRCMS_EIRP 0x01 /* Flag for EIRP */ +#define BRCMS_DFS_TPC 0x02 /* Flag for DFS TPC */ +#define BRCMS_NO_OFDM 0x04 /* Flag for No OFDM */ +#define BRCMS_NO_40MHZ 0x08 /* Flag for No MIMO 40MHz */ +#define BRCMS_NO_MIMO 0x10 /* Flag for No MIMO, 20 or 40 MHz */ +#define BRCMS_RADAR_TYPE_EU 0x20 /* Flag for EU */ +#define BRCMS_DFS_FCC BRCMS_DFS_TPC /* Flag for DFS FCC */ + +#define BRCMS_DFS_EU (BRCMS_DFS_TPC | BRCMS_RADAR_TYPE_EU) /* Flag for DFS EU */ + +struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc); + +void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm); + +bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec); + +void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, + struct txpwr_limits *txpwr); +void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, + u8 local_constraint_qdbm); +void brcms_c_regd_init(struct brcms_c_info *wlc); + +#endif /* _WLC_CHANNEL_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/d11.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/d11.h new file mode 100644 index 000000000000..9035cc4d6ff3 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/d11.h @@ -0,0 +1,1902 @@ +/* + * Copyright (c) 2010 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 _BRCM_D11_H_ +#define _BRCM_D11_H_ + +#include + +#include +#include "pub.h" +#include "dma.h" + +/* RX FIFO numbers */ +#define RX_FIFO 0 /* data and ctl frames */ +#define RX_TXSTATUS_FIFO 3 /* RX fifo for tx status packages */ + +/* TX FIFO numbers using WME Access Category */ +#define TX_AC_BK_FIFO 0 /* Background TX FIFO */ +#define TX_AC_BE_FIFO 1 /* Best-Effort TX FIFO */ +#define TX_AC_VI_FIFO 2 /* Video TX FIFO */ +#define TX_AC_VO_FIFO 3 /* Voice TX FIFO */ +#define TX_BCMC_FIFO 4 /* Broadcast/Multicast TX FIFO */ +#define TX_ATIM_FIFO 5 /* TX fifo for ATIM window info */ + +/* Addr is byte address used by SW; offset is word offset used by uCode */ + +/* Per AC TX limit settings */ +#define M_AC_TXLMT_BASE_ADDR (0x180 * 2) +#define M_AC_TXLMT_ADDR(_ac) (M_AC_TXLMT_BASE_ADDR + (2 * (_ac))) + +/* Legacy TX FIFO numbers */ +#define TX_DATA_FIFO TX_AC_BE_FIFO +#define TX_CTL_FIFO TX_AC_VO_FIFO + +#define WL_RSSI_ANT_MAX 4 /* max possible rx antennas */ + +struct intctrlregs { + u32 intstatus; + u32 intmask; +}; + +/* PIO structure, + * support two PIO format: 2 bytes access and 4 bytes access + * basic FIFO register set is per channel(transmit or receive) + * a pair of channels is defined for convenience + */ +/* 2byte-wide pio register set per channel(xmt or rcv) */ +struct pio2regs { + u16 fifocontrol; + u16 fifodata; + u16 fifofree; /* only valid in xmt channel, not in rcv channel */ + u16 PAD; +}; + +/* a pair of pio channels(tx and rx) */ +struct pio2regp { + struct pio2regs tx; + struct pio2regs rx; +}; + +/* 4byte-wide pio register set per channel(xmt or rcv) */ +struct pio4regs { + u32 fifocontrol; + u32 fifodata; +}; + +/* a pair of pio channels(tx and rx) */ +struct pio4regp { + struct pio4regs tx; + struct pio4regs rx; +}; + +/* read: 32-bit register that can be read as 32-bit or as 2 16-bit + * write: only low 16b-it half can be written + */ +union pmqreg { + u32 pmqhostdata; /* read only! */ + struct { + u16 pmqctrlstatus; /* read/write */ + u16 PAD; + } w; +}; + +struct fifo64 { + struct dma64regs dmaxmt; /* dma tx */ + struct pio4regs piotx; /* pio tx */ + struct dma64regs dmarcv; /* dma rx */ + struct pio4regs piorx; /* pio rx */ +}; + +/* + * Host Interface Registers + */ +struct d11regs { + /* Device Control ("semi-standard host registers") */ + u32 PAD[3]; /* 0x0 - 0x8 */ + u32 biststatus; /* 0xC */ + u32 biststatus2; /* 0x10 */ + u32 PAD; /* 0x14 */ + u32 gptimer; /* 0x18 */ + u32 usectimer; /* 0x1c *//* for corerev >= 26 */ + + /* Interrupt Control *//* 0x20 */ + struct intctrlregs intctrlregs[8]; + + u32 PAD[40]; /* 0x60 - 0xFC */ + + u32 intrcvlazy[4]; /* 0x100 - 0x10C */ + + u32 PAD[4]; /* 0x110 - 0x11c */ + + u32 maccontrol; /* 0x120 */ + u32 maccommand; /* 0x124 */ + u32 macintstatus; /* 0x128 */ + u32 macintmask; /* 0x12C */ + + /* Transmit Template Access */ + u32 tplatewrptr; /* 0x130 */ + u32 tplatewrdata; /* 0x134 */ + u32 PAD[2]; /* 0x138 - 0x13C */ + + /* PMQ registers */ + union pmqreg pmqreg; /* 0x140 */ + u32 pmqpatl; /* 0x144 */ + u32 pmqpath; /* 0x148 */ + u32 PAD; /* 0x14C */ + + u32 chnstatus; /* 0x150 */ + u32 psmdebug; /* 0x154 */ + u32 phydebug; /* 0x158 */ + u32 machwcap; /* 0x15C */ + + /* Extended Internal Objects */ + u32 objaddr; /* 0x160 */ + u32 objdata; /* 0x164 */ + u32 PAD[2]; /* 0x168 - 0x16c */ + + u32 frmtxstatus; /* 0x170 */ + u32 frmtxstatus2; /* 0x174 */ + u32 PAD[2]; /* 0x178 - 0x17c */ + + /* TSF host access */ + u32 tsf_timerlow; /* 0x180 */ + u32 tsf_timerhigh; /* 0x184 */ + u32 tsf_cfprep; /* 0x188 */ + u32 tsf_cfpstart; /* 0x18c */ + u32 tsf_cfpmaxdur32; /* 0x190 */ + u32 PAD[3]; /* 0x194 - 0x19c */ + + u32 maccontrol1; /* 0x1a0 */ + u32 machwcap1; /* 0x1a4 */ + u32 PAD[14]; /* 0x1a8 - 0x1dc */ + + /* Clock control and hardware workarounds*/ + u32 clk_ctl_st; /* 0x1e0 */ + u32 hw_war; + u32 d11_phypllctl; /* the phypll request/avail bits are + * moved to clk_ctl_st + */ + u32 PAD[5]; /* 0x1ec - 0x1fc */ + + /* 0x200-0x37F dma/pio registers */ + struct fifo64 fifo64regs[6]; + + /* FIFO diagnostic port access */ + struct dma32diag dmafifo; /* 0x380 - 0x38C */ + + u32 aggfifocnt; /* 0x390 */ + u32 aggfifodata; /* 0x394 */ + u32 PAD[16]; /* 0x398 - 0x3d4 */ + u16 radioregaddr; /* 0x3d8 */ + u16 radioregdata; /* 0x3da */ + + /* + * time delay between the change on rf disable input and + * radio shutdown + */ + u32 rfdisabledly; /* 0x3DC */ + + /* PHY register access */ + u16 phyversion; /* 0x3e0 - 0x0 */ + u16 phybbconfig; /* 0x3e2 - 0x1 */ + u16 phyadcbias; /* 0x3e4 - 0x2 Bphy only */ + u16 phyanacore; /* 0x3e6 - 0x3 pwwrdwn on aphy */ + u16 phyrxstatus0; /* 0x3e8 - 0x4 */ + u16 phyrxstatus1; /* 0x3ea - 0x5 */ + u16 phycrsth; /* 0x3ec - 0x6 */ + u16 phytxerror; /* 0x3ee - 0x7 */ + u16 phychannel; /* 0x3f0 - 0x8 */ + u16 PAD[1]; /* 0x3f2 - 0x9 */ + u16 phytest; /* 0x3f4 - 0xa */ + u16 phy4waddr; /* 0x3f6 - 0xb */ + u16 phy4wdatahi; /* 0x3f8 - 0xc */ + u16 phy4wdatalo; /* 0x3fa - 0xd */ + u16 phyregaddr; /* 0x3fc - 0xe */ + u16 phyregdata; /* 0x3fe - 0xf */ + + /* IHR *//* 0x400 - 0x7FE */ + + /* RXE Block */ + u16 PAD[3]; /* 0x400 - 0x406 */ + u16 rcv_fifo_ctl; /* 0x406 */ + u16 PAD; /* 0x408 - 0x40a */ + u16 rcv_frm_cnt; /* 0x40a */ + u16 PAD[4]; /* 0x40a - 0x414 */ + u16 rssi; /* 0x414 */ + u16 PAD[5]; /* 0x414 - 0x420 */ + u16 rcm_ctl; /* 0x420 */ + u16 rcm_mat_data; /* 0x422 */ + u16 rcm_mat_mask; /* 0x424 */ + u16 rcm_mat_dly; /* 0x426 */ + u16 rcm_cond_mask_l; /* 0x428 */ + u16 rcm_cond_mask_h; /* 0x42A */ + u16 rcm_cond_dly; /* 0x42C */ + u16 PAD[1]; /* 0x42E */ + u16 ext_ihr_addr; /* 0x430 */ + u16 ext_ihr_data; /* 0x432 */ + u16 rxe_phyrs_2; /* 0x434 */ + u16 rxe_phyrs_3; /* 0x436 */ + u16 phy_mode; /* 0x438 */ + u16 rcmta_ctl; /* 0x43a */ + u16 rcmta_size; /* 0x43c */ + u16 rcmta_addr0; /* 0x43e */ + u16 rcmta_addr1; /* 0x440 */ + u16 rcmta_addr2; /* 0x442 */ + u16 PAD[30]; /* 0x444 - 0x480 */ + + /* PSM Block *//* 0x480 - 0x500 */ + + u16 PAD; /* 0x480 */ + u16 psm_maccontrol_h; /* 0x482 */ + u16 psm_macintstatus_l; /* 0x484 */ + u16 psm_macintstatus_h; /* 0x486 */ + u16 psm_macintmask_l; /* 0x488 */ + u16 psm_macintmask_h; /* 0x48A */ + u16 PAD; /* 0x48C */ + u16 psm_maccommand; /* 0x48E */ + u16 psm_brc; /* 0x490 */ + u16 psm_phy_hdr_param; /* 0x492 */ + u16 psm_postcard; /* 0x494 */ + u16 psm_pcard_loc_l; /* 0x496 */ + u16 psm_pcard_loc_h; /* 0x498 */ + u16 psm_gpio_in; /* 0x49A */ + u16 psm_gpio_out; /* 0x49C */ + u16 psm_gpio_oe; /* 0x49E */ + + u16 psm_bred_0; /* 0x4A0 */ + u16 psm_bred_1; /* 0x4A2 */ + u16 psm_bred_2; /* 0x4A4 */ + u16 psm_bred_3; /* 0x4A6 */ + u16 psm_brcl_0; /* 0x4A8 */ + u16 psm_brcl_1; /* 0x4AA */ + u16 psm_brcl_2; /* 0x4AC */ + u16 psm_brcl_3; /* 0x4AE */ + u16 psm_brpo_0; /* 0x4B0 */ + u16 psm_brpo_1; /* 0x4B2 */ + u16 psm_brpo_2; /* 0x4B4 */ + u16 psm_brpo_3; /* 0x4B6 */ + u16 psm_brwk_0; /* 0x4B8 */ + u16 psm_brwk_1; /* 0x4BA */ + u16 psm_brwk_2; /* 0x4BC */ + u16 psm_brwk_3; /* 0x4BE */ + + u16 psm_base_0; /* 0x4C0 */ + u16 psm_base_1; /* 0x4C2 */ + u16 psm_base_2; /* 0x4C4 */ + u16 psm_base_3; /* 0x4C6 */ + u16 psm_base_4; /* 0x4C8 */ + u16 psm_base_5; /* 0x4CA */ + u16 psm_base_6; /* 0x4CC */ + u16 psm_pc_reg_0; /* 0x4CE */ + u16 psm_pc_reg_1; /* 0x4D0 */ + u16 psm_pc_reg_2; /* 0x4D2 */ + u16 psm_pc_reg_3; /* 0x4D4 */ + u16 PAD[0xD]; /* 0x4D6 - 0x4DE */ + u16 psm_corectlsts; /* 0x4f0 *//* Corerev >= 13 */ + u16 PAD[0x7]; /* 0x4f2 - 0x4fE */ + + /* TXE0 Block *//* 0x500 - 0x580 */ + u16 txe_ctl; /* 0x500 */ + u16 txe_aux; /* 0x502 */ + u16 txe_ts_loc; /* 0x504 */ + u16 txe_time_out; /* 0x506 */ + u16 txe_wm_0; /* 0x508 */ + u16 txe_wm_1; /* 0x50A */ + u16 txe_phyctl; /* 0x50C */ + u16 txe_status; /* 0x50E */ + u16 txe_mmplcp0; /* 0x510 */ + u16 txe_mmplcp1; /* 0x512 */ + u16 txe_phyctl1; /* 0x514 */ + + u16 PAD[0x05]; /* 0x510 - 0x51E */ + + /* Transmit control */ + u16 xmtfifodef; /* 0x520 */ + u16 xmtfifo_frame_cnt; /* 0x522 *//* Corerev >= 16 */ + u16 xmtfifo_byte_cnt; /* 0x524 *//* Corerev >= 16 */ + u16 xmtfifo_head; /* 0x526 *//* Corerev >= 16 */ + u16 xmtfifo_rd_ptr; /* 0x528 *//* Corerev >= 16 */ + u16 xmtfifo_wr_ptr; /* 0x52A *//* Corerev >= 16 */ + u16 xmtfifodef1; /* 0x52C *//* Corerev >= 16 */ + + u16 PAD[0x09]; /* 0x52E - 0x53E */ + + u16 xmtfifocmd; /* 0x540 */ + u16 xmtfifoflush; /* 0x542 */ + u16 xmtfifothresh; /* 0x544 */ + u16 xmtfifordy; /* 0x546 */ + u16 xmtfifoprirdy; /* 0x548 */ + u16 xmtfiforqpri; /* 0x54A */ + u16 xmttplatetxptr; /* 0x54C */ + u16 PAD; /* 0x54E */ + u16 xmttplateptr; /* 0x550 */ + u16 smpl_clct_strptr; /* 0x552 *//* Corerev >= 22 */ + u16 smpl_clct_stpptr; /* 0x554 *//* Corerev >= 22 */ + u16 smpl_clct_curptr; /* 0x556 *//* Corerev >= 22 */ + u16 PAD[0x04]; /* 0x558 - 0x55E */ + u16 xmttplatedatalo; /* 0x560 */ + u16 xmttplatedatahi; /* 0x562 */ + + u16 PAD[2]; /* 0x564 - 0x566 */ + + u16 xmtsel; /* 0x568 */ + u16 xmttxcnt; /* 0x56A */ + u16 xmttxshmaddr; /* 0x56C */ + + u16 PAD[0x09]; /* 0x56E - 0x57E */ + + /* TXE1 Block */ + u16 PAD[0x40]; /* 0x580 - 0x5FE */ + + /* TSF Block */ + u16 PAD[0X02]; /* 0x600 - 0x602 */ + u16 tsf_cfpstrt_l; /* 0x604 */ + u16 tsf_cfpstrt_h; /* 0x606 */ + u16 PAD[0X05]; /* 0x608 - 0x610 */ + u16 tsf_cfppretbtt; /* 0x612 */ + u16 PAD[0XD]; /* 0x614 - 0x62C */ + u16 tsf_clk_frac_l; /* 0x62E */ + u16 tsf_clk_frac_h; /* 0x630 */ + u16 PAD[0X14]; /* 0x632 - 0x658 */ + u16 tsf_random; /* 0x65A */ + u16 PAD[0x05]; /* 0x65C - 0x664 */ + /* GPTimer 2 registers */ + u16 tsf_gpt2_stat; /* 0x666 */ + u16 tsf_gpt2_ctr_l; /* 0x668 */ + u16 tsf_gpt2_ctr_h; /* 0x66A */ + u16 tsf_gpt2_val_l; /* 0x66C */ + u16 tsf_gpt2_val_h; /* 0x66E */ + u16 tsf_gptall_stat; /* 0x670 */ + u16 PAD[0x07]; /* 0x672 - 0x67E */ + + /* IFS Block */ + u16 ifs_sifs_rx_tx_tx; /* 0x680 */ + u16 ifs_sifs_nav_tx; /* 0x682 */ + u16 ifs_slot; /* 0x684 */ + u16 PAD; /* 0x686 */ + u16 ifs_ctl; /* 0x688 */ + u16 PAD[0x3]; /* 0x68a - 0x68F */ + u16 ifsstat; /* 0x690 */ + u16 ifsmedbusyctl; /* 0x692 */ + u16 iftxdur; /* 0x694 */ + u16 PAD[0x3]; /* 0x696 - 0x69b */ + /* EDCF support in dot11macs */ + u16 ifs_aifsn; /* 0x69c */ + u16 ifs_ctl1; /* 0x69e */ + + /* slow clock registers */ + u16 scc_ctl; /* 0x6a0 */ + u16 scc_timer_l; /* 0x6a2 */ + u16 scc_timer_h; /* 0x6a4 */ + u16 scc_frac; /* 0x6a6 */ + u16 scc_fastpwrup_dly; /* 0x6a8 */ + u16 scc_per; /* 0x6aa */ + u16 scc_per_frac; /* 0x6ac */ + u16 scc_cal_timer_l; /* 0x6ae */ + u16 scc_cal_timer_h; /* 0x6b0 */ + u16 PAD; /* 0x6b2 */ + + u16 PAD[0x26]; + + /* NAV Block */ + u16 nav_ctl; /* 0x700 */ + u16 navstat; /* 0x702 */ + u16 PAD[0x3e]; /* 0x702 - 0x77E */ + + /* WEP/PMQ Block *//* 0x780 - 0x7FE */ + u16 PAD[0x20]; /* 0x780 - 0x7BE */ + + u16 wepctl; /* 0x7C0 */ + u16 wepivloc; /* 0x7C2 */ + u16 wepivkey; /* 0x7C4 */ + u16 wepwkey; /* 0x7C6 */ + + u16 PAD[4]; /* 0x7C8 - 0x7CE */ + u16 pcmctl; /* 0X7D0 */ + u16 pcmstat; /* 0X7D2 */ + u16 PAD[6]; /* 0x7D4 - 0x7DE */ + + u16 pmqctl; /* 0x7E0 */ + u16 pmqstatus; /* 0x7E2 */ + u16 pmqpat0; /* 0x7E4 */ + u16 pmqpat1; /* 0x7E6 */ + u16 pmqpat2; /* 0x7E8 */ + + u16 pmqdat; /* 0x7EA */ + u16 pmqdator; /* 0x7EC */ + u16 pmqhst; /* 0x7EE */ + u16 pmqpath0; /* 0x7F0 */ + u16 pmqpath1; /* 0x7F2 */ + u16 pmqpath2; /* 0x7F4 */ + u16 pmqdath; /* 0x7F6 */ + + u16 PAD[0x04]; /* 0x7F8 - 0x7FE */ + + /* SHM *//* 0x800 - 0xEFE */ + u16 PAD[0x380]; /* 0x800 - 0xEFE */ +}; + +/* d11 register field offset */ +#define D11REGOFFS(field) offsetof(struct d11regs, field) + +#define PIHR_BASE 0x0400 /* byte address of packed IHR region */ + +/* biststatus */ +#define BT_DONE (1U << 31) /* bist done */ +#define BT_B2S (1 << 30) /* bist2 ram summary bit */ + +/* intstatus and intmask */ +#define I_PC (1 << 10) /* pci descriptor error */ +#define I_PD (1 << 11) /* pci data error */ +#define I_DE (1 << 12) /* descriptor protocol error */ +#define I_RU (1 << 13) /* receive descriptor underflow */ +#define I_RO (1 << 14) /* receive fifo overflow */ +#define I_XU (1 << 15) /* transmit fifo underflow */ +#define I_RI (1 << 16) /* receive interrupt */ +#define I_XI (1 << 24) /* transmit interrupt */ + +/* interrupt receive lazy */ +#define IRL_TO_MASK 0x00ffffff /* timeout */ +#define IRL_FC_MASK 0xff000000 /* frame count */ +#define IRL_FC_SHIFT 24 /* frame count */ + +/*== maccontrol register ==*/ +#define MCTL_GMODE (1U << 31) +#define MCTL_DISCARD_PMQ (1 << 30) +#define MCTL_TBTTHOLD (1 << 28) +#define MCTL_WAKE (1 << 26) +#define MCTL_HPS (1 << 25) +#define MCTL_PROMISC (1 << 24) +#define MCTL_KEEPBADFCS (1 << 23) +#define MCTL_KEEPCONTROL (1 << 22) +#define MCTL_PHYLOCK (1 << 21) +#define MCTL_BCNS_PROMISC (1 << 20) +#define MCTL_LOCK_RADIO (1 << 19) +#define MCTL_AP (1 << 18) +#define MCTL_INFRA (1 << 17) +#define MCTL_BIGEND (1 << 16) +#define MCTL_GPOUT_SEL_MASK (3 << 14) +#define MCTL_GPOUT_SEL_SHIFT 14 +#define MCTL_EN_PSMDBG (1 << 13) +#define MCTL_IHR_EN (1 << 10) +#define MCTL_SHM_UPPER (1 << 9) +#define MCTL_SHM_EN (1 << 8) +#define MCTL_PSM_JMP_0 (1 << 2) +#define MCTL_PSM_RUN (1 << 1) +#define MCTL_EN_MAC (1 << 0) + +/*== maccommand register ==*/ +#define MCMD_BCN0VLD (1 << 0) +#define MCMD_BCN1VLD (1 << 1) +#define MCMD_DIRFRMQVAL (1 << 2) +#define MCMD_CCA (1 << 3) +#define MCMD_BG_NOISE (1 << 4) +#define MCMD_SKIP_SHMINIT (1 << 5) /* only used for simulation */ +#define MCMD_SAMPLECOLL MCMD_SKIP_SHMINIT /* reuse for sample collect */ + +/*== macintstatus/macintmask ==*/ +/* gracefully suspended */ +#define MI_MACSSPNDD (1 << 0) +/* beacon template available */ +#define MI_BCNTPL (1 << 1) +/* TBTT indication */ +#define MI_TBTT (1 << 2) +/* beacon successfully tx'd */ +#define MI_BCNSUCCESS (1 << 3) +/* beacon canceled (IBSS) */ +#define MI_BCNCANCLD (1 << 4) +/* end of ATIM-window (IBSS) */ +#define MI_ATIMWINEND (1 << 5) +/* PMQ entries available */ +#define MI_PMQ (1 << 6) +/* non-specific gen-stat bits that are set by PSM */ +#define MI_NSPECGEN_0 (1 << 7) +/* non-specific gen-stat bits that are set by PSM */ +#define MI_NSPECGEN_1 (1 << 8) +/* MAC level Tx error */ +#define MI_MACTXERR (1 << 9) +/* non-specific gen-stat bits that are set by PSM */ +#define MI_NSPECGEN_3 (1 << 10) +/* PHY Tx error */ +#define MI_PHYTXERR (1 << 11) +/* Power Management Event */ +#define MI_PME (1 << 12) +/* General-purpose timer0 */ +#define MI_GP0 (1 << 13) +/* General-purpose timer1 */ +#define MI_GP1 (1 << 14) +/* (ORed) DMA-interrupts */ +#define MI_DMAINT (1 << 15) +/* MAC has completed a TX FIFO Suspend/Flush */ +#define MI_TXSTOP (1 << 16) +/* MAC has completed a CCA measurement */ +#define MI_CCA (1 << 17) +/* MAC has collected background noise samples */ +#define MI_BG_NOISE (1 << 18) +/* MBSS DTIM TBTT indication */ +#define MI_DTIM_TBTT (1 << 19) +/* Probe response queue needs attention */ +#define MI_PRQ (1 << 20) +/* Radio/PHY has been powered back up. */ +#define MI_PWRUP (1 << 21) +#define MI_RESERVED3 (1 << 22) +#define MI_RESERVED2 (1 << 23) +#define MI_RESERVED1 (1 << 25) +/* MAC detected change on RF Disable input*/ +#define MI_RFDISABLE (1 << 28) +/* MAC has completed a TX */ +#define MI_TFS (1 << 29) +/* A phy status change wrt G mode */ +#define MI_PHYCHANGED (1 << 30) +/* general purpose timeout */ +#define MI_TO (1U << 31) + +/* Mac capabilities registers */ +/*== machwcap ==*/ +#define MCAP_TKIPMIC 0x80000000 /* TKIP MIC hardware present */ + +/*== pmqhost data ==*/ +/* data entry of head pmq entry */ +#define PMQH_DATA_MASK 0xffff0000 +/* PM entry for BSS config */ +#define PMQH_BSSCFG 0x00100000 +/* PM Mode OFF: power save off */ +#define PMQH_PMOFF 0x00010000 +/* PM Mode ON: power save on */ +#define PMQH_PMON 0x00020000 +/* Dis-associated or De-authenticated */ +#define PMQH_DASAT 0x00040000 +/* ATIM not acknowledged */ +#define PMQH_ATIMFAIL 0x00080000 +/* delete head entry */ +#define PMQH_DEL_ENTRY 0x00000001 +/* delete head entry to cur read pointer -1 */ +#define PMQH_DEL_MULT 0x00000002 +/* pmq overflow indication */ +#define PMQH_OFLO 0x00000004 +/* entries are present in pmq */ +#define PMQH_NOT_EMPTY 0x00000008 + +/*== phydebug ==*/ +/* phy is asserting carrier sense */ +#define PDBG_CRS (1 << 0) +/* phy is taking xmit byte from mac this cycle */ +#define PDBG_TXA (1 << 1) +/* mac is instructing the phy to transmit a frame */ +#define PDBG_TXF (1 << 2) +/* phy is signalling a transmit Error to the mac */ +#define PDBG_TXE (1 << 3) +/* phy detected the end of a valid frame preamble */ +#define PDBG_RXF (1 << 4) +/* phy detected the end of a valid PLCP header */ +#define PDBG_RXS (1 << 5) +/* rx start not asserted */ +#define PDBG_RXFRG (1 << 6) +/* mac is taking receive byte from phy this cycle */ +#define PDBG_RXV (1 << 7) +/* RF portion of the radio is disabled */ +#define PDBG_RFD (1 << 16) + +/*== objaddr register ==*/ +#define OBJADDR_SEL_MASK 0x000F0000 +#define OBJADDR_UCM_SEL 0x00000000 +#define OBJADDR_SHM_SEL 0x00010000 +#define OBJADDR_SCR_SEL 0x00020000 +#define OBJADDR_IHR_SEL 0x00030000 +#define OBJADDR_RCMTA_SEL 0x00040000 +#define OBJADDR_SRCHM_SEL 0x00060000 +#define OBJADDR_WINC 0x01000000 +#define OBJADDR_RINC 0x02000000 +#define OBJADDR_AUTO_INC 0x03000000 + +#define WEP_PCMADDR 0x07d4 +#define WEP_PCMDATA 0x07d6 + +/*== frmtxstatus ==*/ +#define TXS_V (1 << 0) /* valid bit */ +#define TXS_STATUS_MASK 0xffff +#define TXS_FID_MASK 0xffff0000 +#define TXS_FID_SHIFT 16 + +/*== frmtxstatus2 ==*/ +#define TXS_SEQ_MASK 0xffff +#define TXS_PTX_MASK 0xff0000 +#define TXS_PTX_SHIFT 16 +#define TXS_MU_MASK 0x01000000 +#define TXS_MU_SHIFT 24 + +/*== clk_ctl_st ==*/ +#define CCS_ERSRC_REQ_D11PLL 0x00000100 /* d11 core pll request */ +#define CCS_ERSRC_REQ_PHYPLL 0x00000200 /* PHY pll request */ +#define CCS_ERSRC_AVAIL_D11PLL 0x01000000 /* d11 core pll available */ +#define CCS_ERSRC_AVAIL_PHYPLL 0x02000000 /* PHY pll available */ + +/* HT Cloclk Ctrl and Clock Avail for 4313 */ +#define CCS_ERSRC_REQ_HT 0x00000010 /* HT avail request */ +#define CCS_ERSRC_AVAIL_HT 0x00020000 /* HT clock available */ + +/* tsf_cfprep register */ +#define CFPREP_CBI_MASK 0xffffffc0 +#define CFPREP_CBI_SHIFT 6 +#define CFPREP_CFPP 0x00000001 + +/* tx fifo sizes values are in terms of 256 byte blocks */ +#define TXFIFOCMD_RESET_MASK (1 << 15) /* reset */ +#define TXFIFOCMD_FIFOSEL_SHIFT 8 /* fifo */ +#define TXFIFO_FIFOTOP_SHIFT 8 /* fifo start */ + +#define TXFIFO_START_BLK16 65 /* Base address + 32 * 512 B/P */ +#define TXFIFO_START_BLK 6 /* Base address + 6 * 256 B */ +#define TXFIFO_SIZE_UNIT 256 /* one unit corresponds to 256 bytes */ +#define MBSS16_TEMPLMEM_MINBLKS 65 /* one unit corresponds to 256 bytes */ + +/*== phy versions (PhyVersion:Revision field) ==*/ +/* analog block version */ +#define PV_AV_MASK 0xf000 +/* analog block version bitfield offset */ +#define PV_AV_SHIFT 12 +/* phy type */ +#define PV_PT_MASK 0x0f00 +/* phy type bitfield offset */ +#define PV_PT_SHIFT 8 +/* phy version */ +#define PV_PV_MASK 0x000f +#define PHY_TYPE(v) ((v & PV_PT_MASK) >> PV_PT_SHIFT) + +/*== phy types (PhyVersion:PhyType field) ==*/ +#define PHY_TYPE_N 4 /* N-Phy value */ +#define PHY_TYPE_SSN 6 /* SSLPN-Phy value */ +#define PHY_TYPE_LCN 8 /* LCN-Phy value */ +#define PHY_TYPE_LCNXN 9 /* LCNXN-Phy value */ +#define PHY_TYPE_NULL 0xf /* Invalid Phy value */ + +/*== analog types (PhyVersion:AnalogType field) ==*/ +#define ANA_11N_013 5 + +/* 802.11a PLCP header def */ +struct ofdm_phy_hdr { + u8 rlpt[3]; /* rate, length, parity, tail */ + u16 service; + u8 pad; +} __packed; + +#define D11A_PHY_HDR_GRATE(phdr) ((phdr)->rlpt[0] & 0x0f) +#define D11A_PHY_HDR_GRES(phdr) (((phdr)->rlpt[0] >> 4) & 0x01) +#define D11A_PHY_HDR_GLENGTH(phdr) (((u32 *)((phdr)->rlpt) >> 5) & 0x0fff) +#define D11A_PHY_HDR_GPARITY(phdr) (((phdr)->rlpt[3] >> 1) & 0x01) +#define D11A_PHY_HDR_GTAIL(phdr) (((phdr)->rlpt[3] >> 2) & 0x3f) + +/* rate encoded per 802.11a-1999 sec 17.3.4.1 */ +#define D11A_PHY_HDR_SRATE(phdr, rate) \ + ((phdr)->rlpt[0] = ((phdr)->rlpt[0] & 0xf0) | ((rate) & 0xf)) +/* set reserved field to zero */ +#define D11A_PHY_HDR_SRES(phdr) ((phdr)->rlpt[0] &= 0xef) +/* length is number of octets in PSDU */ +#define D11A_PHY_HDR_SLENGTH(phdr, length) \ + (*(u32 *)((phdr)->rlpt) = *(u32 *)((phdr)->rlpt) | \ + (((length) & 0x0fff) << 5)) +/* set the tail to all zeros */ +#define D11A_PHY_HDR_STAIL(phdr) ((phdr)->rlpt[3] &= 0x03) + +#define D11A_PHY_HDR_LEN_L 3 /* low-rate part of PLCP header */ +#define D11A_PHY_HDR_LEN_R 2 /* high-rate part of PLCP header */ + +#define D11A_PHY_TX_DELAY (2) /* 2.1 usec */ + +#define D11A_PHY_HDR_TIME (4) /* low-rate part of PLCP header */ +#define D11A_PHY_PRE_TIME (16) +#define D11A_PHY_PREHDR_TIME (D11A_PHY_PRE_TIME + D11A_PHY_HDR_TIME) + +/* 802.11b PLCP header def */ +struct cck_phy_hdr { + u8 signal; + u8 service; + u16 length; + u16 crc; +} __packed; + +#define D11B_PHY_HDR_LEN 6 + +#define D11B_PHY_TX_DELAY (3) /* 3.4 usec */ + +#define D11B_PHY_LHDR_TIME (D11B_PHY_HDR_LEN << 3) +#define D11B_PHY_LPRE_TIME (144) +#define D11B_PHY_LPREHDR_TIME (D11B_PHY_LPRE_TIME + D11B_PHY_LHDR_TIME) + +#define D11B_PHY_SHDR_TIME (D11B_PHY_LHDR_TIME >> 1) +#define D11B_PHY_SPRE_TIME (D11B_PHY_LPRE_TIME >> 1) +#define D11B_PHY_SPREHDR_TIME (D11B_PHY_SPRE_TIME + D11B_PHY_SHDR_TIME) + +#define D11B_PLCP_SIGNAL_LOCKED (1 << 2) +#define D11B_PLCP_SIGNAL_LE (1 << 7) + +#define MIMO_PLCP_MCS_MASK 0x7f /* mcs index */ +#define MIMO_PLCP_40MHZ 0x80 /* 40 Hz frame */ +#define MIMO_PLCP_AMPDU 0x08 /* ampdu */ + +#define BRCMS_GET_CCK_PLCP_LEN(plcp) (plcp[4] + (plcp[5] << 8)) +#define BRCMS_GET_MIMO_PLCP_LEN(plcp) (plcp[1] + (plcp[2] << 8)) +#define BRCMS_SET_MIMO_PLCP_LEN(plcp, len) \ + do { \ + plcp[1] = len & 0xff; \ + plcp[2] = ((len >> 8) & 0xff); \ + } while (0) + +#define BRCMS_SET_MIMO_PLCP_AMPDU(plcp) (plcp[3] |= MIMO_PLCP_AMPDU) +#define BRCMS_CLR_MIMO_PLCP_AMPDU(plcp) (plcp[3] &= ~MIMO_PLCP_AMPDU) +#define BRCMS_IS_MIMO_PLCP_AMPDU(plcp) (plcp[3] & MIMO_PLCP_AMPDU) + +/* + * The dot11a PLCP header is 5 bytes. To simplify the software (so that we + * don't need e.g. different tx DMA headers for 11a and 11b), the PLCP header + * has padding added in the ucode. + */ +#define D11_PHY_HDR_LEN 6 + +/* TX DMA buffer header */ +struct d11txh { + __le16 MacTxControlLow; /* 0x0 */ + __le16 MacTxControlHigh; /* 0x1 */ + __le16 MacFrameControl; /* 0x2 */ + __le16 TxFesTimeNormal; /* 0x3 */ + __le16 PhyTxControlWord; /* 0x4 */ + __le16 PhyTxControlWord_1; /* 0x5 */ + __le16 PhyTxControlWord_1_Fbr; /* 0x6 */ + __le16 PhyTxControlWord_1_Rts; /* 0x7 */ + __le16 PhyTxControlWord_1_FbrRts; /* 0x8 */ + __le16 MainRates; /* 0x9 */ + __le16 XtraFrameTypes; /* 0xa */ + u8 IV[16]; /* 0x0b - 0x12 */ + u8 TxFrameRA[6]; /* 0x13 - 0x15 */ + __le16 TxFesTimeFallback; /* 0x16 */ + u8 RTSPLCPFallback[6]; /* 0x17 - 0x19 */ + __le16 RTSDurFallback; /* 0x1a */ + u8 FragPLCPFallback[6]; /* 0x1b - 1d */ + __le16 FragDurFallback; /* 0x1e */ + __le16 MModeLen; /* 0x1f */ + __le16 MModeFbrLen; /* 0x20 */ + __le16 TstampLow; /* 0x21 */ + __le16 TstampHigh; /* 0x22 */ + __le16 ABI_MimoAntSel; /* 0x23 */ + __le16 PreloadSize; /* 0x24 */ + __le16 AmpduSeqCtl; /* 0x25 */ + __le16 TxFrameID; /* 0x26 */ + __le16 TxStatus; /* 0x27 */ + __le16 MaxNMpdus; /* 0x28 */ + __le16 MaxABytes_MRT; /* 0x29 */ + __le16 MaxABytes_FBR; /* 0x2a */ + __le16 MinMBytes; /* 0x2b */ + u8 RTSPhyHeader[D11_PHY_HDR_LEN]; /* 0x2c - 0x2e */ + struct ieee80211_rts rts_frame; /* 0x2f - 0x36 */ + u16 PAD; /* 0x37 */ +} __packed; + +#define D11_TXH_LEN 112 /* bytes */ + +/* Frame Types */ +#define FT_CCK 0 +#define FT_OFDM 1 +#define FT_HT 2 +#define FT_N 3 + +/* + * Position of MPDU inside A-MPDU; indicated with bits 10:9 + * of MacTxControlLow + */ +#define TXC_AMPDU_SHIFT 9 /* shift for ampdu settings */ +#define TXC_AMPDU_NONE 0 /* Regular MPDU, not an A-MPDU */ +#define TXC_AMPDU_FIRST 1 /* first MPDU of an A-MPDU */ +#define TXC_AMPDU_MIDDLE 2 /* intermediate MPDU of an A-MPDU */ +#define TXC_AMPDU_LAST 3 /* last (or single) MPDU of an A-MPDU */ + +/*== MacTxControlLow ==*/ +#define TXC_AMIC 0x8000 +#define TXC_SENDCTS 0x0800 +#define TXC_AMPDU_MASK 0x0600 +#define TXC_BW_40 0x0100 +#define TXC_FREQBAND_5G 0x0080 +#define TXC_DFCS 0x0040 +#define TXC_IGNOREPMQ 0x0020 +#define TXC_HWSEQ 0x0010 +#define TXC_STARTMSDU 0x0008 +#define TXC_SENDRTS 0x0004 +#define TXC_LONGFRAME 0x0002 +#define TXC_IMMEDACK 0x0001 + +/*== MacTxControlHigh ==*/ +/* RTS fallback preamble type 1 = SHORT 0 = LONG */ +#define TXC_PREAMBLE_RTS_FB_SHORT 0x8000 +/* RTS main rate preamble type 1 = SHORT 0 = LONG */ +#define TXC_PREAMBLE_RTS_MAIN_SHORT 0x4000 +/* + * Main fallback rate preamble type + * 1 = SHORT for OFDM/GF for MIMO + * 0 = LONG for CCK/MM for MIMO + */ +#define TXC_PREAMBLE_DATA_FB_SHORT 0x2000 + +/* TXC_PREAMBLE_DATA_MAIN is in PhyTxControl bit 5 */ +/* use fallback rate for this AMPDU */ +#define TXC_AMPDU_FBR 0x1000 +#define TXC_SECKEY_MASK 0x0FF0 +#define TXC_SECKEY_SHIFT 4 +/* Use alternate txpwr defined at loc. M_ALT_TXPWR_IDX */ +#define TXC_ALT_TXPWR 0x0008 +#define TXC_SECTYPE_MASK 0x0007 +#define TXC_SECTYPE_SHIFT 0 + +/* Null delimiter for Fallback rate */ +#define AMPDU_FBR_NULL_DELIM 5 /* Location of Null delimiter count for AMPDU */ + +/* PhyTxControl for Mimophy */ +#define PHY_TXC_PWR_MASK 0xFC00 +#define PHY_TXC_PWR_SHIFT 10 +#define PHY_TXC_ANT_MASK 0x03C0 /* bit 6, 7, 8, 9 */ +#define PHY_TXC_ANT_SHIFT 6 +#define PHY_TXC_ANT_0_1 0x00C0 /* auto, last rx */ +#define PHY_TXC_LCNPHY_ANT_LAST 0x0000 +#define PHY_TXC_ANT_3 0x0200 /* virtual antenna 3 */ +#define PHY_TXC_ANT_2 0x0100 /* virtual antenna 2 */ +#define PHY_TXC_ANT_1 0x0080 /* virtual antenna 1 */ +#define PHY_TXC_ANT_0 0x0040 /* virtual antenna 0 */ +#define PHY_TXC_SHORT_HDR 0x0010 + +#define PHY_TXC_OLD_ANT_0 0x0000 +#define PHY_TXC_OLD_ANT_1 0x0100 +#define PHY_TXC_OLD_ANT_LAST 0x0300 + +/* PhyTxControl_1 for Mimophy */ +#define PHY_TXC1_BW_MASK 0x0007 +#define PHY_TXC1_BW_10MHZ 0 +#define PHY_TXC1_BW_10MHZ_UP 1 +#define PHY_TXC1_BW_20MHZ 2 +#define PHY_TXC1_BW_20MHZ_UP 3 +#define PHY_TXC1_BW_40MHZ 4 +#define PHY_TXC1_BW_40MHZ_DUP 5 +#define PHY_TXC1_MODE_SHIFT 3 +#define PHY_TXC1_MODE_MASK 0x0038 +#define PHY_TXC1_MODE_SISO 0 +#define PHY_TXC1_MODE_CDD 1 +#define PHY_TXC1_MODE_STBC 2 +#define PHY_TXC1_MODE_SDM 3 + +/* PhyTxControl for HTphy that are different from Mimophy */ +#define PHY_TXC_HTANT_MASK 0x3fC0 /* bits 6-13 */ + +/* XtraFrameTypes */ +#define XFTS_RTS_FT_SHIFT 2 +#define XFTS_FBRRTS_FT_SHIFT 4 +#define XFTS_CHANNEL_SHIFT 8 + +/* Antenna diversity bit in ant_wr_settle */ +#define PHY_AWS_ANTDIV 0x2000 + +/* IFS ctl */ +#define IFS_USEEDCF (1 << 2) + +/* IFS ctl1 */ +#define IFS_CTL1_EDCRS (1 << 3) +#define IFS_CTL1_EDCRS_20L (1 << 4) +#define IFS_CTL1_EDCRS_40 (1 << 5) + +/* ABI_MimoAntSel */ +#define ABI_MAS_ADDR_BMP_IDX_MASK 0x0f00 +#define ABI_MAS_ADDR_BMP_IDX_SHIFT 8 +#define ABI_MAS_FBR_ANT_PTN_MASK 0x00f0 +#define ABI_MAS_FBR_ANT_PTN_SHIFT 4 +#define ABI_MAS_MRT_ANT_PTN_MASK 0x000f + +/* tx status packet */ +struct tx_status { + u16 framelen; + u16 PAD; + u16 frameid; + u16 status; + u16 lasttxtime; + u16 sequence; + u16 phyerr; + u16 ackphyrxsh; +} __packed; + +#define TXSTATUS_LEN 16 + +/* status field bit definitions */ +#define TX_STATUS_FRM_RTX_MASK 0xF000 +#define TX_STATUS_FRM_RTX_SHIFT 12 +#define TX_STATUS_RTS_RTX_MASK 0x0F00 +#define TX_STATUS_RTS_RTX_SHIFT 8 +#define TX_STATUS_MASK 0x00FE +#define TX_STATUS_PMINDCTD (1 << 7) /* PM mode indicated to AP */ +#define TX_STATUS_INTERMEDIATE (1 << 6) /* intermediate or 1st ampdu pkg */ +#define TX_STATUS_AMPDU (1 << 5) /* AMPDU status */ +#define TX_STATUS_SUPR_MASK 0x1C /* suppress status bits (4:2) */ +#define TX_STATUS_SUPR_SHIFT 2 +#define TX_STATUS_ACK_RCV (1 << 1) /* ACK received */ +#define TX_STATUS_VALID (1 << 0) /* Tx status valid */ +#define TX_STATUS_NO_ACK 0 + +/* suppress status reason codes */ +#define TX_STATUS_SUPR_PMQ (1 << 2) /* PMQ entry */ +#define TX_STATUS_SUPR_FLUSH (2 << 2) /* flush request */ +#define TX_STATUS_SUPR_FRAG (3 << 2) /* previous frag failure */ +#define TX_STATUS_SUPR_TBTT (3 << 2) /* SHARED: Probe resp supr for TBTT */ +#define TX_STATUS_SUPR_BADCH (4 << 2) /* channel mismatch */ +#define TX_STATUS_SUPR_EXPTIME (5 << 2) /* lifetime expiry */ +#define TX_STATUS_SUPR_UF (6 << 2) /* underflow */ + +/* Unexpected tx status for rate update */ +#define TX_STATUS_UNEXP(status) \ + ((((status) & TX_STATUS_INTERMEDIATE) != 0) && \ + TX_STATUS_UNEXP_AMPDU(status)) + +/* Unexpected tx status for A-MPDU rate update */ +#define TX_STATUS_UNEXP_AMPDU(status) \ + ((((status) & TX_STATUS_SUPR_MASK) != 0) && \ + (((status) & TX_STATUS_SUPR_MASK) != TX_STATUS_SUPR_EXPTIME)) + +#define TX_STATUS_BA_BMAP03_MASK 0xF000 /* ba bitmap 0:3 in 1st pkg */ +#define TX_STATUS_BA_BMAP03_SHIFT 12 /* ba bitmap 0:3 in 1st pkg */ +#define TX_STATUS_BA_BMAP47_MASK 0x001E /* ba bitmap 4:7 in 2nd pkg */ +#define TX_STATUS_BA_BMAP47_SHIFT 3 /* ba bitmap 4:7 in 2nd pkg */ + +/* RXE (Receive Engine) */ + +/* RCM_CTL */ +#define RCM_INC_MASK_H 0x0080 +#define RCM_INC_MASK_L 0x0040 +#define RCM_INC_DATA 0x0020 +#define RCM_INDEX_MASK 0x001F +#define RCM_SIZE 15 + +#define RCM_MAC_OFFSET 0 /* current MAC address */ +#define RCM_BSSID_OFFSET 3 /* current BSSID address */ +#define RCM_F_BSSID_0_OFFSET 6 /* foreign BSS CFP tracking */ +#define RCM_F_BSSID_1_OFFSET 9 /* foreign BSS CFP tracking */ +#define RCM_F_BSSID_2_OFFSET 12 /* foreign BSS CFP tracking */ + +#define RCM_WEP_TA0_OFFSET 16 +#define RCM_WEP_TA1_OFFSET 19 +#define RCM_WEP_TA2_OFFSET 22 +#define RCM_WEP_TA3_OFFSET 25 + +/* PSM Block */ + +/* psm_phy_hdr_param bits */ +#define MAC_PHY_RESET 1 +#define MAC_PHY_CLOCK_EN 2 +#define MAC_PHY_FORCE_CLK 4 + +/* WEP Block */ + +/* WEP_WKEY */ +#define WKEY_START (1 << 8) +#define WKEY_SEL_MASK 0x1F + +/* WEP data formats */ + +/* the number of RCMTA entries */ +#define RCMTA_SIZE 50 + +#define M_ADDR_BMP_BLK (0x37e * 2) +#define M_ADDR_BMP_BLK_SZ 12 + +#define ADDR_BMP_RA (1 << 0) /* Receiver Address (RA) */ +#define ADDR_BMP_TA (1 << 1) /* Transmitter Address (TA) */ +#define ADDR_BMP_BSSID (1 << 2) /* BSSID */ +#define ADDR_BMP_AP (1 << 3) /* Infra-BSS Access Point */ +#define ADDR_BMP_STA (1 << 4) /* Infra-BSS Station */ +#define ADDR_BMP_RESERVED1 (1 << 5) +#define ADDR_BMP_RESERVED2 (1 << 6) +#define ADDR_BMP_RESERVED3 (1 << 7) +#define ADDR_BMP_BSS_IDX_MASK (3 << 8) /* BSS control block index */ +#define ADDR_BMP_BSS_IDX_SHIFT 8 + +#define WSEC_MAX_RCMTA_KEYS 54 + +/* max keys in M_TKMICKEYS_BLK */ +#define WSEC_MAX_TKMIC_ENGINE_KEYS 12 /* 8 + 4 default */ + +/* max RXE match registers */ +#define WSEC_MAX_RXE_KEYS 4 + +/* SECKINDXALGO (Security Key Index & Algorithm Block) word format */ +/* SKL (Security Key Lookup) */ +#define SKL_ALGO_MASK 0x0007 +#define SKL_ALGO_SHIFT 0 +#define SKL_KEYID_MASK 0x0008 +#define SKL_KEYID_SHIFT 3 +#define SKL_INDEX_MASK 0x03F0 +#define SKL_INDEX_SHIFT 4 +#define SKL_GRP_ALGO_MASK 0x1c00 +#define SKL_GRP_ALGO_SHIFT 10 + +/* additional bits defined for IBSS group key support */ +#define SKL_IBSS_INDEX_MASK 0x01F0 +#define SKL_IBSS_INDEX_SHIFT 4 +#define SKL_IBSS_KEYID1_MASK 0x0600 +#define SKL_IBSS_KEYID1_SHIFT 9 +#define SKL_IBSS_KEYID2_MASK 0x1800 +#define SKL_IBSS_KEYID2_SHIFT 11 +#define SKL_IBSS_KEYALGO_MASK 0xE000 +#define SKL_IBSS_KEYALGO_SHIFT 13 + +#define WSEC_MODE_OFF 0 +#define WSEC_MODE_HW 1 +#define WSEC_MODE_SW 2 + +#define WSEC_ALGO_OFF 0 +#define WSEC_ALGO_WEP1 1 +#define WSEC_ALGO_TKIP 2 +#define WSEC_ALGO_AES 3 +#define WSEC_ALGO_WEP128 4 +#define WSEC_ALGO_AES_LEGACY 5 +#define WSEC_ALGO_NALG 6 + +#define AES_MODE_NONE 0 +#define AES_MODE_CCM 1 + +/* WEP_CTL (Rev 0) */ +#define WECR0_KEYREG_SHIFT 0 +#define WECR0_KEYREG_MASK 0x7 +#define WECR0_DECRYPT (1 << 3) +#define WECR0_IVINLINE (1 << 4) +#define WECR0_WEPALG_SHIFT 5 +#define WECR0_WEPALG_MASK (0x7 << 5) +#define WECR0_WKEYSEL_SHIFT 8 +#define WECR0_WKEYSEL_MASK (0x7 << 8) +#define WECR0_WKEYSTART (1 << 11) +#define WECR0_WEPINIT (1 << 14) +#define WECR0_ICVERR (1 << 15) + +/* Frame template map byte offsets */ +#define T_ACTS_TPL_BASE (0) +#define T_NULL_TPL_BASE (0xc * 2) +#define T_QNULL_TPL_BASE (0x1c * 2) +#define T_RR_TPL_BASE (0x2c * 2) +#define T_BCN0_TPL_BASE (0x34 * 2) +#define T_PRS_TPL_BASE (0x134 * 2) +#define T_BCN1_TPL_BASE (0x234 * 2) +#define T_TX_FIFO_TXRAM_BASE (T_ACTS_TPL_BASE + \ + (TXFIFO_START_BLK * TXFIFO_SIZE_UNIT)) + +#define T_BA_TPL_BASE T_QNULL_TPL_BASE /* template area for BA */ + +#define T_RAM_ACCESS_SZ 4 /* template ram is 4 byte access only */ + +/* Shared Mem byte offsets */ + +/* Location where the ucode expects the corerev */ +#define M_MACHW_VER (0x00b * 2) + +/* Location where the ucode expects the MAC capabilities */ +#define M_MACHW_CAP_L (0x060 * 2) +#define M_MACHW_CAP_H (0x061 * 2) + +/* WME shared memory */ +#define M_EDCF_STATUS_OFF (0x007 * 2) +#define M_TXF_CUR_INDEX (0x018 * 2) +#define M_EDCF_QINFO (0x120 * 2) + +/* PS-mode related parameters */ +#define M_DOT11_SLOT (0x008 * 2) +#define M_DOT11_DTIMPERIOD (0x009 * 2) +#define M_NOSLPZNATDTIM (0x026 * 2) + +/* Beacon-related parameters */ +#define M_BCN0_FRM_BYTESZ (0x00c * 2) /* Bcn 0 template length */ +#define M_BCN1_FRM_BYTESZ (0x00d * 2) /* Bcn 1 template length */ +#define M_BCN_TXTSF_OFFSET (0x00e * 2) +#define M_TIMBPOS_INBEACON (0x00f * 2) +#define M_SFRMTXCNTFBRTHSD (0x022 * 2) +#define M_LFRMTXCNTFBRTHSD (0x023 * 2) +#define M_BCN_PCTLWD (0x02a * 2) +#define M_BCN_LI (0x05b * 2) /* beacon listen interval */ + +/* MAX Rx Frame len */ +#define M_MAXRXFRM_LEN (0x010 * 2) + +/* ACK/CTS related params */ +#define M_RSP_PCTLWD (0x011 * 2) + +/* Hardware Power Control */ +#define M_TXPWR_N (0x012 * 2) +#define M_TXPWR_TARGET (0x013 * 2) +#define M_TXPWR_MAX (0x014 * 2) +#define M_TXPWR_CUR (0x019 * 2) + +/* Rx-related parameters */ +#define M_RX_PAD_DATA_OFFSET (0x01a * 2) + +/* WEP Shared mem data */ +#define M_SEC_DEFIVLOC (0x01e * 2) +#define M_SEC_VALNUMSOFTMCHTA (0x01f * 2) +#define M_PHYVER (0x028 * 2) +#define M_PHYTYPE (0x029 * 2) +#define M_SECRXKEYS_PTR (0x02b * 2) +#define M_TKMICKEYS_PTR (0x059 * 2) +#define M_SECKINDXALGO_BLK (0x2ea * 2) +#define M_SECKINDXALGO_BLK_SZ 54 +#define M_SECPSMRXTAMCH_BLK (0x2fa * 2) +#define M_TKIP_TSC_TTAK (0x18c * 2) +#define D11_MAX_KEY_SIZE 16 + +#define M_MAX_ANTCNT (0x02e * 2) /* antenna swap threshold */ + +/* Probe response related parameters */ +#define M_SSIDLEN (0x024 * 2) +#define M_PRB_RESP_FRM_LEN (0x025 * 2) +#define M_PRS_MAXTIME (0x03a * 2) +#define M_SSID (0xb0 * 2) +#define M_CTXPRS_BLK (0xc0 * 2) +#define C_CTX_PCTLWD_POS (0x4 * 2) + +/* Delta between OFDM and CCK power in CCK power boost mode */ +#define M_OFDM_OFFSET (0x027 * 2) + +/* TSSI for last 4 11b/g CCK packets transmitted */ +#define M_B_TSSI_0 (0x02c * 2) +#define M_B_TSSI_1 (0x02d * 2) + +/* Host flags to turn on ucode options */ +#define M_HOST_FLAGS1 (0x02f * 2) +#define M_HOST_FLAGS2 (0x030 * 2) +#define M_HOST_FLAGS3 (0x031 * 2) +#define M_HOST_FLAGS4 (0x03c * 2) +#define M_HOST_FLAGS5 (0x06a * 2) +#define M_HOST_FLAGS_SZ 16 + +#define M_RADAR_REG (0x033 * 2) + +/* TSSI for last 4 11a OFDM packets transmitted */ +#define M_A_TSSI_0 (0x034 * 2) +#define M_A_TSSI_1 (0x035 * 2) + +/* noise interference measurement */ +#define M_NOISE_IF_COUNT (0x034 * 2) +#define M_NOISE_IF_TIMEOUT (0x035 * 2) + +#define M_RF_RX_SP_REG1 (0x036 * 2) + +/* TSSI for last 4 11g OFDM packets transmitted */ +#define M_G_TSSI_0 (0x038 * 2) +#define M_G_TSSI_1 (0x039 * 2) + +/* Background noise measure */ +#define M_JSSI_0 (0x44 * 2) +#define M_JSSI_1 (0x45 * 2) +#define M_JSSI_AUX (0x46 * 2) + +#define M_CUR_2050_RADIOCODE (0x47 * 2) + +/* TX fifo sizes */ +#define M_FIFOSIZE0 (0x4c * 2) +#define M_FIFOSIZE1 (0x4d * 2) +#define M_FIFOSIZE2 (0x4e * 2) +#define M_FIFOSIZE3 (0x4f * 2) +#define D11_MAX_TX_FRMS 32 /* max frames allowed in tx fifo */ + +/* Current channel number plus upper bits */ +#define M_CURCHANNEL (0x50 * 2) +#define D11_CURCHANNEL_5G 0x0100; +#define D11_CURCHANNEL_40 0x0200; +#define D11_CURCHANNEL_MAX 0x00FF; + +/* last posted frameid on the bcmc fifo */ +#define M_BCMC_FID (0x54 * 2) +#define INVALIDFID 0xffff + +/* extended beacon phyctl bytes for 11N */ +#define M_BCN_PCTL1WD (0x058 * 2) + +/* idle busy ratio to duty_cycle requirement */ +#define M_TX_IDLE_BUSY_RATIO_X_16_CCK (0x52 * 2) +#define M_TX_IDLE_BUSY_RATIO_X_16_OFDM (0x5A * 2) + +/* CW RSSI for LCNPHY */ +#define M_LCN_RSSI_0 0x1332 +#define M_LCN_RSSI_1 0x1338 +#define M_LCN_RSSI_2 0x133e +#define M_LCN_RSSI_3 0x1344 + +/* SNR for LCNPHY */ +#define M_LCN_SNR_A_0 0x1334 +#define M_LCN_SNR_B_0 0x1336 + +#define M_LCN_SNR_A_1 0x133a +#define M_LCN_SNR_B_1 0x133c + +#define M_LCN_SNR_A_2 0x1340 +#define M_LCN_SNR_B_2 0x1342 + +#define M_LCN_SNR_A_3 0x1346 +#define M_LCN_SNR_B_3 0x1348 + +#define M_LCN_LAST_RESET (81*2) +#define M_LCN_LAST_LOC (63*2) +#define M_LCNPHY_RESET_STATUS (4902) +#define M_LCNPHY_DSC_TIME (0x98d*2) +#define M_LCNPHY_RESET_CNT_DSC (0x98b*2) +#define M_LCNPHY_RESET_CNT (0x98c*2) + +/* Rate table offsets */ +#define M_RT_DIRMAP_A (0xe0 * 2) +#define M_RT_BBRSMAP_A (0xf0 * 2) +#define M_RT_DIRMAP_B (0x100 * 2) +#define M_RT_BBRSMAP_B (0x110 * 2) + +/* Rate table entry offsets */ +#define M_RT_PRS_PLCP_POS 10 +#define M_RT_PRS_DUR_POS 16 +#define M_RT_OFDM_PCTL1_POS 18 + +#define M_20IN40_IQ (0x380 * 2) + +/* SHM locations where ucode stores the current power index */ +#define M_CURR_IDX1 (0x384 * 2) +#define M_CURR_IDX2 (0x387 * 2) + +#define M_BSCALE_ANT0 (0x5e * 2) +#define M_BSCALE_ANT1 (0x5f * 2) + +/* Antenna Diversity Testing */ +#define M_MIMO_ANTSEL_RXDFLT (0x63 * 2) +#define M_ANTSEL_CLKDIV (0x61 * 2) +#define M_MIMO_ANTSEL_TXDFLT (0x64 * 2) + +#define M_MIMO_MAXSYM (0x5d * 2) +#define MIMO_MAXSYM_DEF 0x8000 /* 32k */ +#define MIMO_MAXSYM_MAX 0xffff /* 64k */ + +#define M_WATCHDOG_8TU (0x1e * 2) +#define WATCHDOG_8TU_DEF 5 +#define WATCHDOG_8TU_MAX 10 + +/* Manufacturing Test Variables */ +/* PER test mode */ +#define M_PKTENG_CTRL (0x6c * 2) +/* IFS for TX mode */ +#define M_PKTENG_IFS (0x6d * 2) +/* Lower word of tx frmcnt/rx lostcnt */ +#define M_PKTENG_FRMCNT_LO (0x6e * 2) +/* Upper word of tx frmcnt/rx lostcnt */ +#define M_PKTENG_FRMCNT_HI (0x6f * 2) + +/* Index variation in vbat ripple */ +#define M_LCN_PWR_IDX_MAX (0x67 * 2) /* highest index read by ucode */ +#define M_LCN_PWR_IDX_MIN (0x66 * 2) /* lowest index read by ucode */ + +/* M_PKTENG_CTRL bit definitions */ +#define M_PKTENG_MODE_TX 0x0001 +#define M_PKTENG_MODE_TX_RIFS 0x0004 +#define M_PKTENG_MODE_TX_CTS 0x0008 +#define M_PKTENG_MODE_RX 0x0002 +#define M_PKTENG_MODE_RX_WITH_ACK 0x0402 +#define M_PKTENG_MODE_MASK 0x0003 +/* TX frames indicated in the frmcnt reg */ +#define M_PKTENG_FRMCNT_VLD 0x0100 + +/* Sample Collect parameters (bitmap and type) */ +/* Trigger bitmap for sample collect */ +#define M_SMPL_COL_BMP (0x37d * 2) +/* Sample collect type */ +#define M_SMPL_COL_CTL (0x3b2 * 2) + +#define ANTSEL_CLKDIV_4MHZ 6 +#define MIMO_ANTSEL_BUSY 0x4000 /* bit 14 (busy) */ +#define MIMO_ANTSEL_SEL 0x8000 /* bit 15 write the value */ +#define MIMO_ANTSEL_WAIT 50 /* 50us wait */ +#define MIMO_ANTSEL_OVERRIDE 0x8000 /* flag */ + +struct shm_acparams { + u16 txop; + u16 cwmin; + u16 cwmax; + u16 cwcur; + u16 aifs; + u16 bslots; + u16 reggap; + u16 status; + u16 rsvd[8]; +} __packed; +#define M_EDCF_QLEN (16 * 2) + +#define WME_STATUS_NEWAC (1 << 8) + +/* M_HOST_FLAGS */ +#define MHFMAX 5 /* Number of valid hostflag half-word (u16) */ +#define MHF1 0 /* Hostflag 1 index */ +#define MHF2 1 /* Hostflag 2 index */ +#define MHF3 2 /* Hostflag 3 index */ +#define MHF4 3 /* Hostflag 4 index */ +#define MHF5 4 /* Hostflag 5 index */ + +/* Flags in M_HOST_FLAGS */ +/* Enable ucode antenna diversity help */ +#define MHF1_ANTDIV 0x0001 +/* Enable EDCF access control */ +#define MHF1_EDCF 0x0100 +#define MHF1_IQSWAP_WAR 0x0200 +/* Disable Slow clock request, for corerev < 11 */ +#define MHF1_FORCEFASTCLK 0x0400 + +/* Flags in M_HOST_FLAGS2 */ + +/* Flush BCMC FIFO immediately */ +#define MHF2_TXBCMC_NOW 0x0040 +/* Enable ucode/hw power control */ +#define MHF2_HWPWRCTL 0x0080 +#define MHF2_NPHY40MHZ_WAR 0x0800 + +/* Flags in M_HOST_FLAGS3 */ +/* enabled mimo antenna selection */ +#define MHF3_ANTSEL_EN 0x0001 +/* antenna selection mode: 0: 2x3, 1: 2x4 */ +#define MHF3_ANTSEL_MODE 0x0002 +#define MHF3_RESERVED1 0x0004 +#define MHF3_RESERVED2 0x0008 +#define MHF3_NPHY_MLADV_WAR 0x0010 + +/* Flags in M_HOST_FLAGS4 */ +/* force bphy Tx on core 0 (board level WAR) */ +#define MHF4_BPHY_TXCORE0 0x0080 +/* for 4313A0 FEM boards */ +#define MHF4_EXTPA_ENABLE 0x4000 + +/* Flags in M_HOST_FLAGS5 */ +#define MHF5_4313_GPIOCTRL 0x0001 +#define MHF5_RESERVED1 0x0002 +#define MHF5_RESERVED2 0x0004 +/* Radio power setting for ucode */ +#define M_RADIO_PWR (0x32 * 2) + +/* phy noise recorded by ucode right after tx */ +#define M_PHY_NOISE (0x037 * 2) +#define PHY_NOISE_MASK 0x00ff + +/* + * Receive Frame Data Header for 802.11b DCF-only frames + * + * RxFrameSize: Actual byte length of the frame data received + * PAD: padding (not used) + * PhyRxStatus_0: PhyRxStatus 15:0 + * PhyRxStatus_1: PhyRxStatus 31:16 + * PhyRxStatus_2: PhyRxStatus 47:32 + * PhyRxStatus_3: PhyRxStatus 63:48 + * PhyRxStatus_4: PhyRxStatus 79:64 + * PhyRxStatus_5: PhyRxStatus 95:80 + * RxStatus1: MAC Rx Status + * RxStatus2: extended MAC Rx status + * RxTSFTime: RxTSFTime time of first MAC symbol + M_PHY_PLCPRX_DLY + * RxChan: gain code, channel radio code, and phy type + */ +struct d11rxhdr_le { + __le16 RxFrameSize; + u16 PAD; + __le16 PhyRxStatus_0; + __le16 PhyRxStatus_1; + __le16 PhyRxStatus_2; + __le16 PhyRxStatus_3; + __le16 PhyRxStatus_4; + __le16 PhyRxStatus_5; + __le16 RxStatus1; + __le16 RxStatus2; + __le16 RxTSFTime; + __le16 RxChan; +} __packed; + +struct d11rxhdr { + u16 RxFrameSize; + u16 PAD; + u16 PhyRxStatus_0; + u16 PhyRxStatus_1; + u16 PhyRxStatus_2; + u16 PhyRxStatus_3; + u16 PhyRxStatus_4; + u16 PhyRxStatus_5; + u16 RxStatus1; + u16 RxStatus2; + u16 RxTSFTime; + u16 RxChan; +} __packed; + +/* PhyRxStatus_0: */ +/* NPHY only: CCK, OFDM, preN, N */ +#define PRXS0_FT_MASK 0x0003 +/* NPHY only: clip count adjustment steps by AGC */ +#define PRXS0_CLIP_MASK 0x000C +#define PRXS0_CLIP_SHIFT 2 +/* PHY received a frame with unsupported rate */ +#define PRXS0_UNSRATE 0x0010 +/* GPHY: rx ant, NPHY: upper sideband */ +#define PRXS0_RXANT_UPSUBBAND 0x0020 +/* CCK frame only: lost crs during cck frame reception */ +#define PRXS0_LCRS 0x0040 +/* Short Preamble */ +#define PRXS0_SHORTH 0x0080 +/* PLCP violation */ +#define PRXS0_PLCPFV 0x0100 +/* PLCP header integrity check failed */ +#define PRXS0_PLCPHCF 0x0200 +/* legacy PHY gain control */ +#define PRXS0_GAIN_CTL 0x4000 +/* NPHY: Antennas used for received frame, bitmask */ +#define PRXS0_ANTSEL_MASK 0xF000 +#define PRXS0_ANTSEL_SHIFT 0x12 + +/* subfield PRXS0_FT_MASK */ +#define PRXS0_CCK 0x0000 +/* valid only for G phy, use rxh->RxChan for A phy */ +#define PRXS0_OFDM 0x0001 +#define PRXS0_PREN 0x0002 +#define PRXS0_STDN 0x0003 + +/* subfield PRXS0_ANTSEL_MASK */ +#define PRXS0_ANTSEL_0 0x0 /* antenna 0 is used */ +#define PRXS0_ANTSEL_1 0x2 /* antenna 1 is used */ +#define PRXS0_ANTSEL_2 0x4 /* antenna 2 is used */ +#define PRXS0_ANTSEL_3 0x8 /* antenna 3 is used */ + +/* PhyRxStatus_1: */ +#define PRXS1_JSSI_MASK 0x00FF +#define PRXS1_JSSI_SHIFT 0 +#define PRXS1_SQ_MASK 0xFF00 +#define PRXS1_SQ_SHIFT 8 + +/* nphy PhyRxStatus_1: */ +#define PRXS1_nphy_PWR0_MASK 0x00FF +#define PRXS1_nphy_PWR1_MASK 0xFF00 + +/* HTPHY Rx Status defines */ +/* htphy PhyRxStatus_0: those bit are overlapped with PhyRxStatus_0 */ +#define PRXS0_BAND 0x0400 /* 0 = 2.4G, 1 = 5G */ +#define PRXS0_RSVD 0x0800 /* reserved; set to 0 */ +#define PRXS0_UNUSED 0xF000 /* unused and not defined; set to 0 */ + +/* htphy PhyRxStatus_1: */ +/* core enables for {3..0}, 0=disabled, 1=enabled */ +#define PRXS1_HTPHY_CORE_MASK 0x000F +/* antenna configation */ +#define PRXS1_HTPHY_ANTCFG_MASK 0x00F0 +/* Mixmode PLCP Length low byte mask */ +#define PRXS1_HTPHY_MMPLCPLenL_MASK 0xFF00 + +/* htphy PhyRxStatus_2: */ +/* Mixmode PLCP Length high byte maskw */ +#define PRXS2_HTPHY_MMPLCPLenH_MASK 0x000F +/* Mixmode PLCP rate mask */ +#define PRXS2_HTPHY_MMPLCH_RATE_MASK 0x00F0 +/* Rx power on core 0 */ +#define PRXS2_HTPHY_RXPWR_ANT0 0xFF00 + +/* htphy PhyRxStatus_3: */ +/* Rx power on core 1 */ +#define PRXS3_HTPHY_RXPWR_ANT1 0x00FF +/* Rx power on core 2 */ +#define PRXS3_HTPHY_RXPWR_ANT2 0xFF00 + +/* htphy PhyRxStatus_4: */ +/* Rx power on core 3 */ +#define PRXS4_HTPHY_RXPWR_ANT3 0x00FF +/* Coarse frequency offset */ +#define PRXS4_HTPHY_CFO 0xFF00 + +/* htphy PhyRxStatus_5: */ +/* Fine frequency offset */ +#define PRXS5_HTPHY_FFO 0x00FF +/* Advance Retard */ +#define PRXS5_HTPHY_AR 0xFF00 + +#define HTPHY_MMPLCPLen(rxs) \ + ((((rxs)->PhyRxStatus_1 & PRXS1_HTPHY_MMPLCPLenL_MASK) >> 8) | \ + (((rxs)->PhyRxStatus_2 & PRXS2_HTPHY_MMPLCPLenH_MASK) << 8)) +/* Get Rx power on core 0 */ +#define HTPHY_RXPWR_ANT0(rxs) \ + ((((rxs)->PhyRxStatus_2) & PRXS2_HTPHY_RXPWR_ANT0) >> 8) +/* Get Rx power on core 1 */ +#define HTPHY_RXPWR_ANT1(rxs) \ + (((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT1) +/* Get Rx power on core 2 */ +#define HTPHY_RXPWR_ANT2(rxs) \ + ((((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT2) >> 8) + +/* ucode RxStatus1: */ +#define RXS_BCNSENT 0x8000 +#define RXS_SECKINDX_MASK 0x07e0 +#define RXS_SECKINDX_SHIFT 5 +#define RXS_DECERR (1 << 4) +#define RXS_DECATMPT (1 << 3) +/* PAD bytes to make IP data 4 bytes aligned */ +#define RXS_PBPRES (1 << 2) +#define RXS_RESPFRAMETX (1 << 1) +#define RXS_FCSERR (1 << 0) + +/* ucode RxStatus2: */ +#define RXS_AMSDU_MASK 1 +#define RXS_AGGTYPE_MASK 0x6 +#define RXS_AGGTYPE_SHIFT 1 +#define RXS_PHYRXST_VALID (1 << 8) +#define RXS_RXANT_MASK 0x3 +#define RXS_RXANT_SHIFT 12 + +/* RxChan */ +#define RXS_CHAN_40 0x1000 +#define RXS_CHAN_5G 0x0800 +#define RXS_CHAN_ID_MASK 0x07f8 +#define RXS_CHAN_ID_SHIFT 3 +#define RXS_CHAN_PHYTYPE_MASK 0x0007 +#define RXS_CHAN_PHYTYPE_SHIFT 0 + +/* Index of attenuations used during ucode power control. */ +#define M_PWRIND_BLKS (0x184 * 2) +#define M_PWRIND_MAP0 (M_PWRIND_BLKS + 0x0) +#define M_PWRIND_MAP1 (M_PWRIND_BLKS + 0x2) +#define M_PWRIND_MAP2 (M_PWRIND_BLKS + 0x4) +#define M_PWRIND_MAP3 (M_PWRIND_BLKS + 0x6) +/* M_PWRIND_MAP(core) macro */ +#define M_PWRIND_MAP(core) (M_PWRIND_BLKS + ((core)<<1)) + +/* PSM SHM variable offsets */ +#define M_PSM_SOFT_REGS 0x0 +#define M_BOM_REV_MAJOR (M_PSM_SOFT_REGS + 0x0) +#define M_BOM_REV_MINOR (M_PSM_SOFT_REGS + 0x2) +#define M_UCODE_DBGST (M_PSM_SOFT_REGS + 0x40) /* ucode debug status code */ +#define M_UCODE_MACSTAT (M_PSM_SOFT_REGS + 0xE0) /* macstat counters */ + +#define M_AGING_THRSH (0x3e * 2) /* max time waiting for medium before tx */ +#define M_MBURST_SIZE (0x40 * 2) /* max frames in a frameburst */ +#define M_MBURST_TXOP (0x41 * 2) /* max frameburst TXOP in unit of us */ +#define M_SYNTHPU_DLY (0x4a * 2) /* pre-wakeup for synthpu, default: 500 */ +#define M_PRETBTT (0x4b * 2) + +/* offset to the target txpwr */ +#define M_ALT_TXPWR_IDX (M_PSM_SOFT_REGS + (0x3b * 2)) +#define M_PHY_TX_FLT_PTR (M_PSM_SOFT_REGS + (0x3d * 2)) +#define M_CTS_DURATION (M_PSM_SOFT_REGS + (0x5c * 2)) +#define M_LP_RCCAL_OVR (M_PSM_SOFT_REGS + (0x6b * 2)) + +/* PKTENG Rx Stats Block */ +#define M_RXSTATS_BLK_PTR (M_PSM_SOFT_REGS + (0x65 * 2)) + +/* ucode debug status codes */ +/* not valid really */ +#define DBGST_INACTIVE 0 +/* after zeroing SHM, before suspending at init */ +#define DBGST_INIT 1 +/* "normal" state */ +#define DBGST_ACTIVE 2 +/* suspended */ +#define DBGST_SUSPENDED 3 +/* asleep (PS mode) */ +#define DBGST_ASLEEP 4 + +/* Scratch Reg defs */ +enum _ePsmScratchPadRegDefinitions { + S_RSV0 = 0, + S_RSV1, + S_RSV2, + + /* offset 0x03: scratch registers for Dot11-contants */ + S_DOT11_CWMIN, /* CW-minimum */ + S_DOT11_CWMAX, /* CW-maximum */ + S_DOT11_CWCUR, /* CW-current */ + S_DOT11_SRC_LMT, /* short retry count limit */ + S_DOT11_LRC_LMT, /* long retry count limit */ + S_DOT11_DTIMCOUNT, /* DTIM-count */ + + /* offset 0x09: Tx-side scratch registers */ + S_SEQ_NUM, /* hardware sequence number reg */ + S_SEQ_NUM_FRAG, /* seq num for frags (at the start of MSDU) */ + S_FRMRETX_CNT, /* frame retx count */ + S_SSRC, /* Station short retry count */ + S_SLRC, /* Station long retry count */ + S_EXP_RSP, /* Expected response frame */ + S_OLD_BREM, /* Remaining backoff ctr */ + S_OLD_CWWIN, /* saved-off CW-cur */ + S_TXECTL, /* TXE-Ctl word constructed in scr-pad */ + S_CTXTST, /* frm type-subtype as read from Tx-descr */ + + /* offset 0x13: Rx-side scratch registers */ + S_RXTST, /* Type and subtype in Rxframe */ + + /* Global state register */ + S_STREG, /* state storage actual bit maps below */ + + S_TXPWR_SUM, /* Tx power control: accumulator */ + S_TXPWR_ITER, /* Tx power control: iteration */ + S_RX_FRMTYPE, /* Rate and PHY type for frames */ + S_THIS_AGG, /* Size of this AGG (A-MSDU) */ + + S_KEYINDX, + S_RXFRMLEN, /* Receive MPDU length in bytes */ + + /* offset 0x1B: Receive TSF time stored in SCR */ + S_RXTSFTMRVAL_WD3, /* TSF value at the start of rx */ + S_RXTSFTMRVAL_WD2, /* TSF value at the start of rx */ + S_RXTSFTMRVAL_WD1, /* TSF value at the start of rx */ + S_RXTSFTMRVAL_WD0, /* TSF value at the start of rx */ + S_RXSSN, /* Received start seq number for A-MPDU BA */ + S_RXQOSFLD, /* Rx-QoS field (if present) */ + + /* offset 0x21: Scratch pad regs used in microcode as temp storage */ + S_TMP0, /* stmp0 */ + S_TMP1, /* stmp1 */ + S_TMP2, /* stmp2 */ + S_TMP3, /* stmp3 */ + S_TMP4, /* stmp4 */ + S_TMP5, /* stmp5 */ + S_PRQPENALTY_CTR, /* Probe response queue penalty counter */ + S_ANTCNT, /* unsuccessful attempts on current ant. */ + S_SYMBOL, /* flag for possible symbol ctl frames */ + S_RXTP, /* rx frame type */ + S_STREG2, /* extra state storage */ + S_STREG3, /* even more extra state storage */ + S_STREG4, /* ... */ + S_STREG5, /* remember to initialize it to zero */ + + S_ADJPWR_IDX, + S_CUR_PTR, /* Temp pointer for A-MPDU re-Tx SHM table */ + S_REVID4, /* 0x33 */ + S_INDX, /* 0x34 */ + S_ADDR0, /* 0x35 */ + S_ADDR1, /* 0x36 */ + S_ADDR2, /* 0x37 */ + S_ADDR3, /* 0x38 */ + S_ADDR4, /* 0x39 */ + S_ADDR5, /* 0x3A */ + S_TMP6, /* 0x3B */ + S_KEYINDX_BU, /* Backup for Key index */ + S_MFGTEST_TMP0, /* Temp regs used for RX test calculations */ + S_RXESN, /* Received end sequence number for A-MPDU BA */ + S_STREG6, /* 0x3F */ +}; + +#define S_BEACON_INDX S_OLD_BREM +#define S_PRS_INDX S_OLD_CWWIN +#define S_PHYTYPE S_SSRC +#define S_PHYVER S_SLRC + +/* IHR SLOW_CTRL values */ +#define SLOW_CTRL_PDE (1 << 0) +#define SLOW_CTRL_FD (1 << 8) + +/* ucode mac statistic counters in shared memory */ +struct macstat { + u16 txallfrm; /* 0x80 */ + u16 txrtsfrm; /* 0x82 */ + u16 txctsfrm; /* 0x84 */ + u16 txackfrm; /* 0x86 */ + u16 txdnlfrm; /* 0x88 */ + u16 txbcnfrm; /* 0x8a */ + u16 txfunfl[8]; /* 0x8c - 0x9b */ + u16 txtplunfl; /* 0x9c */ + u16 txphyerr; /* 0x9e */ + u16 pktengrxducast; /* 0xa0 */ + u16 pktengrxdmcast; /* 0xa2 */ + u16 rxfrmtoolong; /* 0xa4 */ + u16 rxfrmtooshrt; /* 0xa6 */ + u16 rxinvmachdr; /* 0xa8 */ + u16 rxbadfcs; /* 0xaa */ + u16 rxbadplcp; /* 0xac */ + u16 rxcrsglitch; /* 0xae */ + u16 rxstrt; /* 0xb0 */ + u16 rxdfrmucastmbss; /* 0xb2 */ + u16 rxmfrmucastmbss; /* 0xb4 */ + u16 rxcfrmucast; /* 0xb6 */ + u16 rxrtsucast; /* 0xb8 */ + u16 rxctsucast; /* 0xba */ + u16 rxackucast; /* 0xbc */ + u16 rxdfrmocast; /* 0xbe */ + u16 rxmfrmocast; /* 0xc0 */ + u16 rxcfrmocast; /* 0xc2 */ + u16 rxrtsocast; /* 0xc4 */ + u16 rxctsocast; /* 0xc6 */ + u16 rxdfrmmcast; /* 0xc8 */ + u16 rxmfrmmcast; /* 0xca */ + u16 rxcfrmmcast; /* 0xcc */ + u16 rxbeaconmbss; /* 0xce */ + u16 rxdfrmucastobss; /* 0xd0 */ + u16 rxbeaconobss; /* 0xd2 */ + u16 rxrsptmout; /* 0xd4 */ + u16 bcntxcancl; /* 0xd6 */ + u16 PAD; + u16 rxf0ovfl; /* 0xda */ + u16 rxf1ovfl; /* 0xdc */ + u16 rxf2ovfl; /* 0xde */ + u16 txsfovfl; /* 0xe0 */ + u16 pmqovfl; /* 0xe2 */ + u16 rxcgprqfrm; /* 0xe4 */ + u16 rxcgprsqovfl; /* 0xe6 */ + u16 txcgprsfail; /* 0xe8 */ + u16 txcgprssuc; /* 0xea */ + u16 prs_timeout; /* 0xec */ + u16 rxnack; + u16 frmscons; + u16 txnack; + u16 txglitch_nack; + u16 txburst; /* 0xf6 # tx bursts */ + u16 bphy_rxcrsglitch; /* bphy rx crs glitch */ + u16 phywatchdog; /* 0xfa # of phy watchdog events */ + u16 PAD; + u16 bphy_badplcp; /* bphy bad plcp */ +}; + +/* dot11 core-specific control flags */ +#define SICF_PCLKE 0x0004 /* PHY clock enable */ +#define SICF_PRST 0x0008 /* PHY reset */ +#define SICF_MPCLKE 0x0010 /* MAC PHY clockcontrol enable */ +#define SICF_FREF 0x0020 /* PLL FreqRefSelect */ +/* NOTE: the following bw bits only apply when the core is attached + * to a NPHY + */ +#define SICF_BWMASK 0x00c0 /* phy clock mask (b6 & b7) */ +#define SICF_BW40 0x0080 /* 40MHz BW (160MHz phyclk) */ +#define SICF_BW20 0x0040 /* 20MHz BW (80MHz phyclk) */ +#define SICF_BW10 0x0000 /* 10MHz BW (40MHz phyclk) */ +#define SICF_GMODE 0x2000 /* gmode enable */ + +/* dot11 core-specific status flags */ +#define SISF_2G_PHY 0x0001 /* 2.4G capable phy */ +#define SISF_5G_PHY 0x0002 /* 5G capable phy */ +#define SISF_FCLKA 0x0004 /* FastClkAvailable */ +#define SISF_DB_PHY 0x0008 /* Dualband phy */ + +/* === End of MAC reg, Beginning of PHY(b/a/g/n) reg === */ +/* radio and LPPHY regs are separated */ + +#define BPHY_REG_OFT_BASE 0x0 +/* offsets for indirect access to bphy registers */ +#define BPHY_BB_CONFIG 0x01 +#define BPHY_ADCBIAS 0x02 +#define BPHY_ANACORE 0x03 +#define BPHY_PHYCRSTH 0x06 +#define BPHY_TEST 0x0a +#define BPHY_PA_TX_TO 0x10 +#define BPHY_SYNTH_DC_TO 0x11 +#define BPHY_PA_TX_TIME_UP 0x12 +#define BPHY_RX_FLTR_TIME_UP 0x13 +#define BPHY_TX_POWER_OVERRIDE 0x14 +#define BPHY_RF_OVERRIDE 0x15 +#define BPHY_RF_TR_LOOKUP1 0x16 +#define BPHY_RF_TR_LOOKUP2 0x17 +#define BPHY_COEFFS 0x18 +#define BPHY_PLL_OUT 0x19 +#define BPHY_REFRESH_MAIN 0x1a +#define BPHY_REFRESH_TO0 0x1b +#define BPHY_REFRESH_TO1 0x1c +#define BPHY_RSSI_TRESH 0x20 +#define BPHY_IQ_TRESH_HH 0x21 +#define BPHY_IQ_TRESH_H 0x22 +#define BPHY_IQ_TRESH_L 0x23 +#define BPHY_IQ_TRESH_LL 0x24 +#define BPHY_GAIN 0x25 +#define BPHY_LNA_GAIN_RANGE 0x26 +#define BPHY_JSSI 0x27 +#define BPHY_TSSI_CTL 0x28 +#define BPHY_TSSI 0x29 +#define BPHY_TR_LOSS_CTL 0x2a +#define BPHY_LO_LEAKAGE 0x2b +#define BPHY_LO_RSSI_ACC 0x2c +#define BPHY_LO_IQMAG_ACC 0x2d +#define BPHY_TX_DC_OFF1 0x2e +#define BPHY_TX_DC_OFF2 0x2f +#define BPHY_PEAK_CNT_THRESH 0x30 +#define BPHY_FREQ_OFFSET 0x31 +#define BPHY_DIVERSITY_CTL 0x32 +#define BPHY_PEAK_ENERGY_LO 0x33 +#define BPHY_PEAK_ENERGY_HI 0x34 +#define BPHY_SYNC_CTL 0x35 +#define BPHY_TX_PWR_CTRL 0x36 +#define BPHY_TX_EST_PWR 0x37 +#define BPHY_STEP 0x38 +#define BPHY_WARMUP 0x39 +#define BPHY_LMS_CFF_READ 0x3a +#define BPHY_LMS_COEFF_I 0x3b +#define BPHY_LMS_COEFF_Q 0x3c +#define BPHY_SIG_POW 0x3d +#define BPHY_RFDC_CANCEL_CTL 0x3e +#define BPHY_HDR_TYPE 0x40 +#define BPHY_SFD_TO 0x41 +#define BPHY_SFD_CTL 0x42 +#define BPHY_DEBUG 0x43 +#define BPHY_RX_DELAY_COMP 0x44 +#define BPHY_CRS_DROP_TO 0x45 +#define BPHY_SHORT_SFD_NZEROS 0x46 +#define BPHY_DSSS_COEFF1 0x48 +#define BPHY_DSSS_COEFF2 0x49 +#define BPHY_CCK_COEFF1 0x4a +#define BPHY_CCK_COEFF2 0x4b +#define BPHY_TR_CORR 0x4c +#define BPHY_ANGLE_SCALE 0x4d +#define BPHY_TX_PWR_BASE_IDX 0x4e +#define BPHY_OPTIONAL_MODES2 0x4f +#define BPHY_CCK_LMS_STEP 0x50 +#define BPHY_BYPASS 0x51 +#define BPHY_CCK_DELAY_LONG 0x52 +#define BPHY_CCK_DELAY_SHORT 0x53 +#define BPHY_PPROC_CHAN_DELAY 0x54 +#define BPHY_DDFS_ENABLE 0x58 +#define BPHY_PHASE_SCALE 0x59 +#define BPHY_FREQ_CONTROL 0x5a +#define BPHY_LNA_GAIN_RANGE_10 0x5b +#define BPHY_LNA_GAIN_RANGE_32 0x5c +#define BPHY_OPTIONAL_MODES 0x5d +#define BPHY_RX_STATUS2 0x5e +#define BPHY_RX_STATUS3 0x5f +#define BPHY_DAC_CONTROL 0x60 +#define BPHY_ANA11G_FILT_CTRL 0x62 +#define BPHY_REFRESH_CTRL 0x64 +#define BPHY_RF_OVERRIDE2 0x65 +#define BPHY_SPUR_CANCEL_CTRL 0x66 +#define BPHY_FINE_DIGIGAIN_CTRL 0x67 +#define BPHY_RSSI_LUT 0x88 +#define BPHY_RSSI_LUT_END 0xa7 +#define BPHY_TSSI_LUT 0xa8 +#define BPHY_TSSI_LUT_END 0xc7 +#define BPHY_TSSI2PWR_LUT 0x380 +#define BPHY_TSSI2PWR_LUT_END 0x39f +#define BPHY_LOCOMP_LUT 0x3a0 +#define BPHY_LOCOMP_LUT_END 0x3bf +#define BPHY_TXGAIN_LUT 0x3c0 +#define BPHY_TXGAIN_LUT_END 0x3ff + +/* Bits in BB_CONFIG: */ +#define PHY_BBC_ANT_MASK 0x0180 +#define PHY_BBC_ANT_SHIFT 7 +#define BB_DARWIN 0x1000 +#define BBCFG_RESETCCA 0x4000 +#define BBCFG_RESETRX 0x8000 + +/* Bits in phytest(0x0a): */ +#define TST_DDFS 0x2000 +#define TST_TXFILT1 0x0800 +#define TST_UNSCRAM 0x0400 +#define TST_CARR_SUPP 0x0200 +#define TST_DC_COMP_LOOP 0x0100 +#define TST_LOOPBACK 0x0080 +#define TST_TXFILT0 0x0040 +#define TST_TXTEST_ENABLE 0x0020 +#define TST_TXTEST_RATE 0x0018 +#define TST_TXTEST_PHASE 0x0007 + +/* phytest txTestRate values */ +#define TST_TXTEST_RATE_1MBPS 0 +#define TST_TXTEST_RATE_2MBPS 1 +#define TST_TXTEST_RATE_5_5MBPS 2 +#define TST_TXTEST_RATE_11MBPS 3 +#define TST_TXTEST_RATE_SHIFT 3 + +#define SHM_BYT_CNT 0x2 /* IHR location */ +#define MAX_BYT_CNT 0x600 /* Maximum frame len */ + +struct d11cnt { + u32 txfrag; + u32 txmulti; + u32 txfail; + u32 txretry; + u32 txretrie; + u32 rxdup; + u32 txrts; + u32 txnocts; + u32 txnoack; + u32 rxfrag; + u32 rxmulti; + u32 rxcrc; + u32 txfrmsnt; + u32 rxundec; +}; + +#endif /* _BRCM_D11_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c new file mode 100644 index 000000000000..7a1fbb2e3a71 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2012 Broadcom Corporation + * Copyright (c) 2012 Canonical Ltd. + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include "types.h" +#include "main.h" +#include "debug.h" +#include "brcms_trace_events.h" +#include "phy/phy_int.h" + +static struct dentry *root_folder; + +void brcms_debugfs_init(void) +{ + root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (IS_ERR(root_folder)) + root_folder = NULL; +} + +void brcms_debugfs_exit(void) +{ + if (!root_folder) + return; + + debugfs_remove_recursive(root_folder); + root_folder = NULL; +} + +int brcms_debugfs_attach(struct brcms_pub *drvr) +{ + if (!root_folder) + return -ENODEV; + + drvr->dbgfs_dir = debugfs_create_dir( + dev_name(&drvr->wlc->hw->d11core->dev), root_folder); + return PTR_ERR_OR_ZERO(drvr->dbgfs_dir); +} + +void brcms_debugfs_detach(struct brcms_pub *drvr) +{ + if (!IS_ERR_OR_NULL(drvr->dbgfs_dir)) + debugfs_remove_recursive(drvr->dbgfs_dir); +} + +struct dentry *brcms_debugfs_get_devdir(struct brcms_pub *drvr) +{ + return drvr->dbgfs_dir; +} + +static +int brcms_debugfs_hardware_read(struct seq_file *s, void *data) +{ + struct brcms_pub *drvr = s->private; + struct brcms_hardware *hw = drvr->wlc->hw; + struct bcma_device *core = hw->d11core; + struct bcma_bus *bus = core->bus; + char boardrev[BRCMU_BOARDREV_LEN]; + + seq_printf(s, "chipnum 0x%x\n" + "chiprev 0x%x\n" + "chippackage 0x%x\n" + "corerev 0x%x\n" + "boardid 0x%x\n" + "boardvendor 0x%x\n" + "boardrev %s\n" + "boardflags 0x%x\n" + "boardflags2 0x%x\n" + "ucoderev 0x%x\n" + "radiorev 0x%x\n" + "phytype 0x%x\n" + "phyrev 0x%x\n" + "anarev 0x%x\n" + "nvramrev %d\n", + bus->chipinfo.id, bus->chipinfo.rev, bus->chipinfo.pkg, + core->id.rev, bus->boardinfo.type, bus->boardinfo.vendor, + brcmu_boardrev_str(hw->boardrev, boardrev), + drvr->wlc->hw->boardflags, drvr->wlc->hw->boardflags2, + drvr->wlc->ucode_rev, hw->band->radiorev, + hw->band->phytype, hw->band->phyrev, hw->band->pi->ana_rev, + hw->sromrev); + return 0; +} + +static int brcms_debugfs_macstat_read(struct seq_file *s, void *data) +{ + struct brcms_pub *drvr = s->private; + struct brcms_info *wl = drvr->ieee_hw->priv; + struct macstat stats; + int i; + + spin_lock_bh(&wl->lock); + stats = *(drvr->wlc->core->macstat_snapshot); + spin_unlock_bh(&wl->lock); + + seq_printf(s, "txallfrm: %d\n", stats.txallfrm); + seq_printf(s, "txrtsfrm: %d\n", stats.txrtsfrm); + seq_printf(s, "txctsfrm: %d\n", stats.txctsfrm); + seq_printf(s, "txackfrm: %d\n", stats.txackfrm); + seq_printf(s, "txdnlfrm: %d\n", stats.txdnlfrm); + seq_printf(s, "txbcnfrm: %d\n", stats.txbcnfrm); + seq_printf(s, "txfunfl[8]:"); + for (i = 0; i < ARRAY_SIZE(stats.txfunfl); i++) + seq_printf(s, " %d", stats.txfunfl[i]); + seq_printf(s, "\ntxtplunfl: %d\n", stats.txtplunfl); + seq_printf(s, "txphyerr: %d\n", stats.txphyerr); + seq_printf(s, "pktengrxducast: %d\n", stats.pktengrxducast); + seq_printf(s, "pktengrxdmcast: %d\n", stats.pktengrxdmcast); + seq_printf(s, "rxfrmtoolong: %d\n", stats.rxfrmtoolong); + seq_printf(s, "rxfrmtooshrt: %d\n", stats.rxfrmtooshrt); + seq_printf(s, "rxinvmachdr: %d\n", stats.rxinvmachdr); + seq_printf(s, "rxbadfcs: %d\n", stats.rxbadfcs); + seq_printf(s, "rxbadplcp: %d\n", stats.rxbadplcp); + seq_printf(s, "rxcrsglitch: %d\n", stats.rxcrsglitch); + seq_printf(s, "rxstrt: %d\n", stats.rxstrt); + seq_printf(s, "rxdfrmucastmbss: %d\n", stats.rxdfrmucastmbss); + seq_printf(s, "rxmfrmucastmbss: %d\n", stats.rxmfrmucastmbss); + seq_printf(s, "rxcfrmucast: %d\n", stats.rxcfrmucast); + seq_printf(s, "rxrtsucast: %d\n", stats.rxrtsucast); + seq_printf(s, "rxctsucast: %d\n", stats.rxctsucast); + seq_printf(s, "rxackucast: %d\n", stats.rxackucast); + seq_printf(s, "rxdfrmocast: %d\n", stats.rxdfrmocast); + seq_printf(s, "rxmfrmocast: %d\n", stats.rxmfrmocast); + seq_printf(s, "rxcfrmocast: %d\n", stats.rxcfrmocast); + seq_printf(s, "rxrtsocast: %d\n", stats.rxrtsocast); + seq_printf(s, "rxctsocast: %d\n", stats.rxctsocast); + seq_printf(s, "rxdfrmmcast: %d\n", stats.rxdfrmmcast); + seq_printf(s, "rxmfrmmcast: %d\n", stats.rxmfrmmcast); + seq_printf(s, "rxcfrmmcast: %d\n", stats.rxcfrmmcast); + seq_printf(s, "rxbeaconmbss: %d\n", stats.rxbeaconmbss); + seq_printf(s, "rxdfrmucastobss: %d\n", stats.rxdfrmucastobss); + seq_printf(s, "rxbeaconobss: %d\n", stats.rxbeaconobss); + seq_printf(s, "rxrsptmout: %d\n", stats.rxrsptmout); + seq_printf(s, "bcntxcancl: %d\n", stats.bcntxcancl); + seq_printf(s, "rxf0ovfl: %d\n", stats.rxf0ovfl); + seq_printf(s, "rxf1ovfl: %d\n", stats.rxf1ovfl); + seq_printf(s, "rxf2ovfl: %d\n", stats.rxf2ovfl); + seq_printf(s, "txsfovfl: %d\n", stats.txsfovfl); + seq_printf(s, "pmqovfl: %d\n", stats.pmqovfl); + seq_printf(s, "rxcgprqfrm: %d\n", stats.rxcgprqfrm); + seq_printf(s, "rxcgprsqovfl: %d\n", stats.rxcgprsqovfl); + seq_printf(s, "txcgprsfail: %d\n", stats.txcgprsfail); + seq_printf(s, "txcgprssuc: %d\n", stats.txcgprssuc); + seq_printf(s, "prs_timeout: %d\n", stats.prs_timeout); + seq_printf(s, "rxnack: %d\n", stats.rxnack); + seq_printf(s, "frmscons: %d\n", stats.frmscons); + seq_printf(s, "txnack: %d\n", stats.txnack); + seq_printf(s, "txglitch_nack: %d\n", stats.txglitch_nack); + seq_printf(s, "txburst: %d\n", stats.txburst); + seq_printf(s, "bphy_rxcrsglitch: %d\n", stats.bphy_rxcrsglitch); + seq_printf(s, "phywatchdog: %d\n", stats.phywatchdog); + seq_printf(s, "bphy_badplcp: %d\n", stats.bphy_badplcp); + return 0; +} + +struct brcms_debugfs_entry { + int (*read)(struct seq_file *seq, void *data); + struct brcms_pub *drvr; +}; + +static int brcms_debugfs_entry_open(struct inode *inode, struct file *f) +{ + struct brcms_debugfs_entry *entry = inode->i_private; + + return single_open(f, entry->read, entry->drvr); +} + +static const struct file_operations brcms_debugfs_def_ops = { + .owner = THIS_MODULE, + .open = brcms_debugfs_entry_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek +}; + +static int +brcms_debugfs_add_entry(struct brcms_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)) +{ + struct device *dev = &drvr->wlc->hw->d11core->dev; + struct dentry *dentry = drvr->dbgfs_dir; + struct brcms_debugfs_entry *entry; + + if (IS_ERR_OR_NULL(dentry)) + return -ENOENT; + + entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->read = read_fn; + entry->drvr = drvr; + + dentry = debugfs_create_file(fn, S_IRUGO, dentry, entry, + &brcms_debugfs_def_ops); + + return PTR_ERR_OR_ZERO(dentry); +} + +void brcms_debugfs_create_files(struct brcms_pub *drvr) +{ + if (IS_ERR_OR_NULL(drvr->dbgfs_dir)) + return; + + brcms_debugfs_add_entry(drvr, "hardware", brcms_debugfs_hardware_read); + brcms_debugfs_add_entry(drvr, "macstat", brcms_debugfs_macstat_read); +} + +#define __brcms_fn(fn) \ +void __brcms_ ##fn(struct device *dev, const char *fmt, ...) \ +{ \ + struct va_format vaf = { \ + .fmt = fmt, \ + }; \ + va_list args; \ + \ + va_start(args, fmt); \ + vaf.va = &args; \ + dev_ ##fn(dev, "%pV", &vaf); \ + trace_brcms_ ##fn(&vaf); \ + va_end(args); \ +} + +__brcms_fn(info) +__brcms_fn(warn) +__brcms_fn(err) +__brcms_fn(crit) + +#if defined(CONFIG_BRCMDBG) || defined(CONFIG_BRCM_TRACING) +void __brcms_dbg(struct device *dev, u32 level, const char *func, + const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; +#ifdef CONFIG_BRCMDBG + if ((brcm_msg_level & level) && net_ratelimit()) + dev_err(dev, "%s %pV", func, &vaf); +#endif + trace_brcms_dbg(level, func, &vaf); + va_end(args); +} +#endif diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h new file mode 100644 index 000000000000..822781cf15d4 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2012 Broadcom Corporation + * Copyright (c) 2012 Canonical Ltd. + * + * 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 _BRCMS_DEBUG_H_ +#define _BRCMS_DEBUG_H_ + +#include +#include +#include +#include +#include "main.h" +#include "mac80211_if.h" + +__printf(2, 3) +void __brcms_info(struct device *dev, const char *fmt, ...); +__printf(2, 3) +void __brcms_warn(struct device *dev, const char *fmt, ...); +__printf(2, 3) +void __brcms_err(struct device *dev, const char *fmt, ...); +__printf(2, 3) +void __brcms_crit(struct device *dev, const char *fmt, ...); + +#if defined(CONFIG_BRCMDBG) || defined(CONFIG_BRCM_TRACING) +__printf(4, 5) +void __brcms_dbg(struct device *dev, u32 level, const char *func, + const char *fmt, ...); +#else +static inline __printf(4, 5) +void __brcms_dbg(struct device *dev, u32 level, const char *func, + const char *fmt, ...) +{ +} +#endif + +/* + * Debug macros cannot be used when wlc is uninitialized. Generally + * this means any code that could run before brcms_c_attach() has + * returned successfully probably shouldn't use the following macros. + */ + +#define brcms_dbg(core, l, f, a...) __brcms_dbg(&(core)->dev, l, __func__, f, ##a) +#define brcms_info(core, f, a...) __brcms_info(&(core)->dev, f, ##a) +#define brcms_warn(core, f, a...) __brcms_warn(&(core)->dev, f, ##a) +#define brcms_err(core, f, a...) __brcms_err(&(core)->dev, f, ##a) +#define brcms_crit(core, f, a...) __brcms_crit(&(core)->dev, f, ##a) + +#define brcms_dbg_info(core, f, a...) brcms_dbg(core, BRCM_DL_INFO, f, ##a) +#define brcms_dbg_mac80211(core, f, a...) brcms_dbg(core, BRCM_DL_MAC80211, f, ##a) +#define brcms_dbg_rx(core, f, a...) brcms_dbg(core, BRCM_DL_RX, f, ##a) +#define brcms_dbg_tx(core, f, a...) brcms_dbg(core, BRCM_DL_TX, f, ##a) +#define brcms_dbg_int(core, f, a...) brcms_dbg(core, BRCM_DL_INT, f, ##a) +#define brcms_dbg_dma(core, f, a...) brcms_dbg(core, BRCM_DL_DMA, f, ##a) +#define brcms_dbg_ht(core, f, a...) brcms_dbg(core, BRCM_DL_HT, f, ##a) + +struct brcms_pub; +void brcms_debugfs_init(void); +void brcms_debugfs_exit(void); +int brcms_debugfs_attach(struct brcms_pub *drvr); +void brcms_debugfs_detach(struct brcms_pub *drvr); +struct dentry *brcms_debugfs_get_devdir(struct brcms_pub *drvr); +void brcms_debugfs_create_files(struct brcms_pub *drvr); + +#endif /* _BRCMS_DEBUG_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c new file mode 100644 index 000000000000..796f5f9d5d5a --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c @@ -0,0 +1,1564 @@ +/* + * Copyright (c) 2010 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 +#include + +#include +#include +#include "types.h" +#include "main.h" +#include "dma.h" +#include "soc.h" +#include "scb.h" +#include "ampdu.h" +#include "debug.h" +#include "brcms_trace_events.h" + +/* + * dma register field offset calculation + */ +#define DMA64REGOFFS(field) offsetof(struct dma64regs, field) +#define DMA64TXREGOFFS(di, field) (di->d64txregbase + DMA64REGOFFS(field)) +#define DMA64RXREGOFFS(di, field) (di->d64rxregbase + DMA64REGOFFS(field)) + +/* + * DMA hardware requires each descriptor ring to be 8kB aligned, and fit within + * a contiguous 8kB physical address. + */ +#define D64RINGALIGN_BITS 13 +#define D64MAXRINGSZ (1 << D64RINGALIGN_BITS) +#define D64RINGALIGN (1 << D64RINGALIGN_BITS) + +#define D64MAXDD (D64MAXRINGSZ / sizeof(struct dma64desc)) + +/* transmit channel control */ +#define D64_XC_XE 0x00000001 /* transmit enable */ +#define D64_XC_SE 0x00000002 /* transmit suspend request */ +#define D64_XC_LE 0x00000004 /* loopback enable */ +#define D64_XC_FL 0x00000010 /* flush request */ +#define D64_XC_PD 0x00000800 /* parity check disable */ +#define D64_XC_AE 0x00030000 /* address extension bits */ +#define D64_XC_AE_SHIFT 16 + +/* transmit descriptor table pointer */ +#define D64_XP_LD_MASK 0x00000fff /* last valid descriptor */ + +/* transmit channel status */ +#define D64_XS0_CD_MASK 0x00001fff /* current descriptor pointer */ +#define D64_XS0_XS_MASK 0xf0000000 /* transmit state */ +#define D64_XS0_XS_SHIFT 28 +#define D64_XS0_XS_DISABLED 0x00000000 /* disabled */ +#define D64_XS0_XS_ACTIVE 0x10000000 /* active */ +#define D64_XS0_XS_IDLE 0x20000000 /* idle wait */ +#define D64_XS0_XS_STOPPED 0x30000000 /* stopped */ +#define D64_XS0_XS_SUSP 0x40000000 /* suspend pending */ + +#define D64_XS1_AD_MASK 0x00001fff /* active descriptor */ +#define D64_XS1_XE_MASK 0xf0000000 /* transmit errors */ +#define D64_XS1_XE_SHIFT 28 +#define D64_XS1_XE_NOERR 0x00000000 /* no error */ +#define D64_XS1_XE_DPE 0x10000000 /* descriptor protocol error */ +#define D64_XS1_XE_DFU 0x20000000 /* data fifo underrun */ +#define D64_XS1_XE_DTE 0x30000000 /* data transfer error */ +#define D64_XS1_XE_DESRE 0x40000000 /* descriptor read error */ +#define D64_XS1_XE_COREE 0x50000000 /* core error */ + +/* receive channel control */ +/* receive enable */ +#define D64_RC_RE 0x00000001 +/* receive frame offset */ +#define D64_RC_RO_MASK 0x000000fe +#define D64_RC_RO_SHIFT 1 +/* direct fifo receive (pio) mode */ +#define D64_RC_FM 0x00000100 +/* separate rx header descriptor enable */ +#define D64_RC_SH 0x00000200 +/* overflow continue */ +#define D64_RC_OC 0x00000400 +/* parity check disable */ +#define D64_RC_PD 0x00000800 +/* address extension bits */ +#define D64_RC_AE 0x00030000 +#define D64_RC_AE_SHIFT 16 + +/* flags for dma controller */ +/* partity enable */ +#define DMA_CTRL_PEN (1 << 0) +/* rx overflow continue */ +#define DMA_CTRL_ROC (1 << 1) +/* allow rx scatter to multiple descriptors */ +#define DMA_CTRL_RXMULTI (1 << 2) +/* Unframed Rx/Tx data */ +#define DMA_CTRL_UNFRAMED (1 << 3) + +/* receive descriptor table pointer */ +#define D64_RP_LD_MASK 0x00000fff /* last valid descriptor */ + +/* receive channel status */ +#define D64_RS0_CD_MASK 0x00001fff /* current descriptor pointer */ +#define D64_RS0_RS_MASK 0xf0000000 /* receive state */ +#define D64_RS0_RS_SHIFT 28 +#define D64_RS0_RS_DISABLED 0x00000000 /* disabled */ +#define D64_RS0_RS_ACTIVE 0x10000000 /* active */ +#define D64_RS0_RS_IDLE 0x20000000 /* idle wait */ +#define D64_RS0_RS_STOPPED 0x30000000 /* stopped */ +#define D64_RS0_RS_SUSP 0x40000000 /* suspend pending */ + +#define D64_RS1_AD_MASK 0x0001ffff /* active descriptor */ +#define D64_RS1_RE_MASK 0xf0000000 /* receive errors */ +#define D64_RS1_RE_SHIFT 28 +#define D64_RS1_RE_NOERR 0x00000000 /* no error */ +#define D64_RS1_RE_DPO 0x10000000 /* descriptor protocol error */ +#define D64_RS1_RE_DFU 0x20000000 /* data fifo overflow */ +#define D64_RS1_RE_DTE 0x30000000 /* data transfer error */ +#define D64_RS1_RE_DESRE 0x40000000 /* descriptor read error */ +#define D64_RS1_RE_COREE 0x50000000 /* core error */ + +/* fifoaddr */ +#define D64_FA_OFF_MASK 0xffff /* offset */ +#define D64_FA_SEL_MASK 0xf0000 /* select */ +#define D64_FA_SEL_SHIFT 16 +#define D64_FA_SEL_XDD 0x00000 /* transmit dma data */ +#define D64_FA_SEL_XDP 0x10000 /* transmit dma pointers */ +#define D64_FA_SEL_RDD 0x40000 /* receive dma data */ +#define D64_FA_SEL_RDP 0x50000 /* receive dma pointers */ +#define D64_FA_SEL_XFD 0x80000 /* transmit fifo data */ +#define D64_FA_SEL_XFP 0x90000 /* transmit fifo pointers */ +#define D64_FA_SEL_RFD 0xc0000 /* receive fifo data */ +#define D64_FA_SEL_RFP 0xd0000 /* receive fifo pointers */ +#define D64_FA_SEL_RSD 0xe0000 /* receive frame status data */ +#define D64_FA_SEL_RSP 0xf0000 /* receive frame status pointers */ + +/* descriptor control flags 1 */ +#define D64_CTRL_COREFLAGS 0x0ff00000 /* core specific flags */ +#define D64_CTRL1_EOT ((u32)1 << 28) /* end of descriptor table */ +#define D64_CTRL1_IOC ((u32)1 << 29) /* interrupt on completion */ +#define D64_CTRL1_EOF ((u32)1 << 30) /* end of frame */ +#define D64_CTRL1_SOF ((u32)1 << 31) /* start of frame */ + +/* descriptor control flags 2 */ +/* buffer byte count. real data len must <= 16KB */ +#define D64_CTRL2_BC_MASK 0x00007fff +/* address extension bits */ +#define D64_CTRL2_AE 0x00030000 +#define D64_CTRL2_AE_SHIFT 16 +/* parity bit */ +#define D64_CTRL2_PARITY 0x00040000 + +/* control flags in the range [27:20] are core-specific and not defined here */ +#define D64_CTRL_CORE_MASK 0x0ff00000 + +#define D64_RX_FRM_STS_LEN 0x0000ffff /* frame length mask */ +#define D64_RX_FRM_STS_OVFL 0x00800000 /* RxOverFlow */ +#define D64_RX_FRM_STS_DSCRCNT 0x0f000000 /* no. of descriptors used - 1 */ +#define D64_RX_FRM_STS_DATATYPE 0xf0000000 /* core-dependent data type */ + +/* + * packet headroom necessary to accommodate the largest header + * in the system, (i.e TXOFF). By doing, we avoid the need to + * allocate an extra buffer for the header when bridging to WL. + * There is a compile time check in wlc.c which ensure that this + * value is at least as big as TXOFF. This value is used in + * dma_rxfill(). + */ + +#define BCMEXTRAHDROOM 172 + +#define MAXNAMEL 8 /* 8 char names */ + +/* macros to convert between byte offsets and indexes */ +#define B2I(bytes, type) ((bytes) / sizeof(type)) +#define I2B(index, type) ((index) * sizeof(type)) + +#define PCI32ADDR_HIGH 0xc0000000 /* address[31:30] */ +#define PCI32ADDR_HIGH_SHIFT 30 /* address[31:30] */ + +#define PCI64ADDR_HIGH 0x80000000 /* address[63] */ +#define PCI64ADDR_HIGH_SHIFT 31 /* address[63] */ + +/* + * DMA Descriptor + * Descriptors are only read by the hardware, never written back. + */ +struct dma64desc { + __le32 ctrl1; /* misc control bits & bufcount */ + __le32 ctrl2; /* buffer count and address extension */ + __le32 addrlow; /* memory address of the date buffer, bits 31:0 */ + __le32 addrhigh; /* memory address of the date buffer, bits 63:32 */ +}; + +/* dma engine software state */ +struct dma_info { + struct dma_pub dma; /* exported structure */ + char name[MAXNAMEL]; /* callers name for diag msgs */ + + struct bcma_device *core; + struct device *dmadev; + + /* session information for AMPDU */ + struct brcms_ampdu_session ampdu_session; + + bool dma64; /* this dma engine is operating in 64-bit mode */ + bool addrext; /* this dma engine supports DmaExtendedAddrChanges */ + + /* 64-bit dma tx engine registers */ + uint d64txregbase; + /* 64-bit dma rx engine registers */ + uint d64rxregbase; + /* pointer to dma64 tx descriptor ring */ + struct dma64desc *txd64; + /* pointer to dma64 rx descriptor ring */ + struct dma64desc *rxd64; + + u16 dmadesc_align; /* alignment requirement for dma descriptors */ + + u16 ntxd; /* # tx descriptors tunable */ + u16 txin; /* index of next descriptor to reclaim */ + u16 txout; /* index of next descriptor to post */ + /* pointer to parallel array of pointers to packets */ + struct sk_buff **txp; + /* Aligned physical address of descriptor ring */ + dma_addr_t txdpa; + /* Original physical address of descriptor ring */ + dma_addr_t txdpaorig; + u16 txdalign; /* #bytes added to alloc'd mem to align txd */ + u32 txdalloc; /* #bytes allocated for the ring */ + u32 xmtptrbase; /* When using unaligned descriptors, the ptr register + * is not just an index, it needs all 13 bits to be + * an offset from the addr register. + */ + + u16 nrxd; /* # rx descriptors tunable */ + u16 rxin; /* index of next descriptor to reclaim */ + u16 rxout; /* index of next descriptor to post */ + /* pointer to parallel array of pointers to packets */ + struct sk_buff **rxp; + /* Aligned physical address of descriptor ring */ + dma_addr_t rxdpa; + /* Original physical address of descriptor ring */ + dma_addr_t rxdpaorig; + u16 rxdalign; /* #bytes added to alloc'd mem to align rxd */ + u32 rxdalloc; /* #bytes allocated for the ring */ + u32 rcvptrbase; /* Base for ptr reg when using unaligned descriptors */ + + /* tunables */ + unsigned int rxbufsize; /* rx buffer size in bytes, not including + * the extra headroom + */ + uint rxextrahdrroom; /* extra rx headroom, reverseved to assist upper + * stack, e.g. some rx pkt buffers will be + * bridged to tx side without byte copying. + * The extra headroom needs to be large enough + * to fit txheader needs. Some dongle driver may + * not need it. + */ + uint nrxpost; /* # rx buffers to keep posted */ + unsigned int rxoffset; /* rxcontrol offset */ + /* add to get dma address of descriptor ring, low 32 bits */ + uint ddoffsetlow; + /* high 32 bits */ + uint ddoffsethigh; + /* add to get dma address of data buffer, low 32 bits */ + uint dataoffsetlow; + /* high 32 bits */ + uint dataoffsethigh; + /* descriptor base need to be aligned or not */ + bool aligndesc_4k; +}; + +/* Check for odd number of 1's */ +static u32 parity32(__le32 data) +{ + /* no swap needed for counting 1's */ + u32 par_data = *(u32 *)&data; + + par_data ^= par_data >> 16; + par_data ^= par_data >> 8; + par_data ^= par_data >> 4; + par_data ^= par_data >> 2; + par_data ^= par_data >> 1; + + return par_data & 1; +} + +static bool dma64_dd_parity(struct dma64desc *dd) +{ + return parity32(dd->addrlow ^ dd->addrhigh ^ dd->ctrl1 ^ dd->ctrl2); +} + +/* descriptor bumping functions */ + +static uint xxd(uint x, uint n) +{ + return x & (n - 1); /* faster than %, but n must be power of 2 */ +} + +static uint txd(struct dma_info *di, uint x) +{ + return xxd(x, di->ntxd); +} + +static uint rxd(struct dma_info *di, uint x) +{ + return xxd(x, di->nrxd); +} + +static uint nexttxd(struct dma_info *di, uint i) +{ + return txd(di, i + 1); +} + +static uint prevtxd(struct dma_info *di, uint i) +{ + return txd(di, i - 1); +} + +static uint nextrxd(struct dma_info *di, uint i) +{ + return rxd(di, i + 1); +} + +static uint ntxdactive(struct dma_info *di, uint h, uint t) +{ + return txd(di, t-h); +} + +static uint nrxdactive(struct dma_info *di, uint h, uint t) +{ + return rxd(di, t-h); +} + +static uint _dma_ctrlflags(struct dma_info *di, uint mask, uint flags) +{ + uint dmactrlflags; + + if (di == NULL) + return 0; + + dmactrlflags = di->dma.dmactrlflags; + dmactrlflags &= ~mask; + dmactrlflags |= flags; + + /* If trying to enable parity, check if parity is actually supported */ + if (dmactrlflags & DMA_CTRL_PEN) { + u32 control; + + control = bcma_read32(di->core, DMA64TXREGOFFS(di, control)); + bcma_write32(di->core, DMA64TXREGOFFS(di, control), + control | D64_XC_PD); + if (bcma_read32(di->core, DMA64TXREGOFFS(di, control)) & + D64_XC_PD) + /* We *can* disable it so it is supported, + * restore control register + */ + bcma_write32(di->core, DMA64TXREGOFFS(di, control), + control); + else + /* Not supported, don't allow it to be enabled */ + dmactrlflags &= ~DMA_CTRL_PEN; + } + + di->dma.dmactrlflags = dmactrlflags; + + return dmactrlflags; +} + +static bool _dma64_addrext(struct dma_info *di, uint ctrl_offset) +{ + u32 w; + bcma_set32(di->core, ctrl_offset, D64_XC_AE); + w = bcma_read32(di->core, ctrl_offset); + bcma_mask32(di->core, ctrl_offset, ~D64_XC_AE); + return (w & D64_XC_AE) == D64_XC_AE; +} + +/* + * return true if this dma engine supports DmaExtendedAddrChanges, + * otherwise false + */ +static bool _dma_isaddrext(struct dma_info *di) +{ + /* DMA64 supports full 32- or 64-bit operation. AE is always valid */ + + /* not all tx or rx channel are available */ + if (di->d64txregbase != 0) { + if (!_dma64_addrext(di, DMA64TXREGOFFS(di, control))) + brcms_dbg_dma(di->core, + "%s: DMA64 tx doesn't have AE set\n", + di->name); + return true; + } else if (di->d64rxregbase != 0) { + if (!_dma64_addrext(di, DMA64RXREGOFFS(di, control))) + brcms_dbg_dma(di->core, + "%s: DMA64 rx doesn't have AE set\n", + di->name); + return true; + } + + return false; +} + +static bool _dma_descriptor_align(struct dma_info *di) +{ + u32 addrl; + + /* Check to see if the descriptors need to be aligned on 4K/8K or not */ + if (di->d64txregbase != 0) { + bcma_write32(di->core, DMA64TXREGOFFS(di, addrlow), 0xff0); + addrl = bcma_read32(di->core, DMA64TXREGOFFS(di, addrlow)); + if (addrl != 0) + return false; + } else if (di->d64rxregbase != 0) { + bcma_write32(di->core, DMA64RXREGOFFS(di, addrlow), 0xff0); + addrl = bcma_read32(di->core, DMA64RXREGOFFS(di, addrlow)); + if (addrl != 0) + return false; + } + return true; +} + +/* + * Descriptor table must start at the DMA hardware dictated alignment, so + * allocated memory must be large enough to support this requirement. + */ +static void *dma_alloc_consistent(struct dma_info *di, uint size, + u16 align_bits, uint *alloced, + dma_addr_t *pap) +{ + if (align_bits) { + u16 align = (1 << align_bits); + if (!IS_ALIGNED(PAGE_SIZE, align)) + size += align; + *alloced = size; + } + return dma_alloc_coherent(di->dmadev, size, pap, GFP_ATOMIC); +} + +static +u8 dma_align_sizetobits(uint size) +{ + u8 bitpos = 0; + while (size >>= 1) + bitpos++; + return bitpos; +} + +/* This function ensures that the DMA descriptor ring will not get allocated + * across Page boundary. If the allocation is done across the page boundary + * at the first time, then it is freed and the allocation is done at + * descriptor ring size aligned location. This will ensure that the ring will + * not cross page boundary + */ +static void *dma_ringalloc(struct dma_info *di, u32 boundary, uint size, + u16 *alignbits, uint *alloced, + dma_addr_t *descpa) +{ + void *va; + u32 desc_strtaddr; + u32 alignbytes = 1 << *alignbits; + + va = dma_alloc_consistent(di, size, *alignbits, alloced, descpa); + + if (NULL == va) + return NULL; + + desc_strtaddr = (u32) roundup((unsigned long)va, alignbytes); + if (((desc_strtaddr + size - 1) & boundary) != (desc_strtaddr + & boundary)) { + *alignbits = dma_align_sizetobits(size); + dma_free_coherent(di->dmadev, size, va, *descpa); + va = dma_alloc_consistent(di, size, *alignbits, + alloced, descpa); + } + return va; +} + +static bool dma64_alloc(struct dma_info *di, uint direction) +{ + u16 size; + uint ddlen; + void *va; + uint alloced = 0; + u16 align; + u16 align_bits; + + ddlen = sizeof(struct dma64desc); + + size = (direction == DMA_TX) ? (di->ntxd * ddlen) : (di->nrxd * ddlen); + align_bits = di->dmadesc_align; + align = (1 << align_bits); + + if (direction == DMA_TX) { + va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits, + &alloced, &di->txdpaorig); + if (va == NULL) { + brcms_dbg_dma(di->core, + "%s: DMA_ALLOC_CONSISTENT(ntxd) failed\n", + di->name); + return false; + } + align = (1 << align_bits); + di->txd64 = (struct dma64desc *) + roundup((unsigned long)va, align); + di->txdalign = (uint) ((s8 *)di->txd64 - (s8 *) va); + di->txdpa = di->txdpaorig + di->txdalign; + di->txdalloc = alloced; + } else { + va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits, + &alloced, &di->rxdpaorig); + if (va == NULL) { + brcms_dbg_dma(di->core, + "%s: DMA_ALLOC_CONSISTENT(nrxd) failed\n", + di->name); + return false; + } + align = (1 << align_bits); + di->rxd64 = (struct dma64desc *) + roundup((unsigned long)va, align); + di->rxdalign = (uint) ((s8 *)di->rxd64 - (s8 *) va); + di->rxdpa = di->rxdpaorig + di->rxdalign; + di->rxdalloc = alloced; + } + + return true; +} + +static bool _dma_alloc(struct dma_info *di, uint direction) +{ + return dma64_alloc(di, direction); +} + +struct dma_pub *dma_attach(char *name, struct brcms_c_info *wlc, + uint txregbase, uint rxregbase, uint ntxd, uint nrxd, + uint rxbufsize, int rxextheadroom, + uint nrxpost, uint rxoffset) +{ + struct si_pub *sih = wlc->hw->sih; + struct bcma_device *core = wlc->hw->d11core; + struct dma_info *di; + u8 rev = core->id.rev; + uint size; + struct si_info *sii = container_of(sih, struct si_info, pub); + + /* allocate private info structure */ + di = kzalloc(sizeof(struct dma_info), GFP_ATOMIC); + if (di == NULL) + return NULL; + + di->dma64 = + ((bcma_aread32(core, BCMA_IOST) & SISF_DMA64) == SISF_DMA64); + + /* init dma reg info */ + di->core = core; + di->d64txregbase = txregbase; + di->d64rxregbase = rxregbase; + + /* + * Default flags (which can be changed by the driver calling + * dma_ctrlflags before enable): For backwards compatibility + * both Rx Overflow Continue and Parity are DISABLED. + */ + _dma_ctrlflags(di, DMA_CTRL_ROC | DMA_CTRL_PEN, 0); + + brcms_dbg_dma(di->core, "%s: %s flags 0x%x ntxd %d nrxd %d " + "rxbufsize %d rxextheadroom %d nrxpost %d rxoffset %d " + "txregbase %u rxregbase %u\n", name, "DMA64", + di->dma.dmactrlflags, ntxd, nrxd, rxbufsize, + rxextheadroom, nrxpost, rxoffset, txregbase, rxregbase); + + /* make a private copy of our callers name */ + strncpy(di->name, name, MAXNAMEL); + di->name[MAXNAMEL - 1] = '\0'; + + di->dmadev = core->dma_dev; + + /* save tunables */ + di->ntxd = (u16) ntxd; + di->nrxd = (u16) nrxd; + + /* the actual dma size doesn't include the extra headroom */ + di->rxextrahdrroom = + (rxextheadroom == -1) ? BCMEXTRAHDROOM : rxextheadroom; + if (rxbufsize > BCMEXTRAHDROOM) + di->rxbufsize = (u16) (rxbufsize - di->rxextrahdrroom); + else + di->rxbufsize = (u16) rxbufsize; + + di->nrxpost = (u16) nrxpost; + di->rxoffset = (u8) rxoffset; + + /* + * figure out the DMA physical address offset for dd and data + * PCI/PCIE: they map silicon backplace address to zero + * based memory, need offset + * Other bus: use zero SI_BUS BIGENDIAN kludge: use sdram + * swapped region for data buffer, not descriptor + */ + di->ddoffsetlow = 0; + di->dataoffsetlow = 0; + /* for pci bus, add offset */ + if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI) { + /* add offset for pcie with DMA64 bus */ + di->ddoffsetlow = 0; + di->ddoffsethigh = SI_PCIE_DMA_H32; + } + di->dataoffsetlow = di->ddoffsetlow; + di->dataoffsethigh = di->ddoffsethigh; + + /* WAR64450 : DMACtl.Addr ext fields are not supported in SDIOD core. */ + if ((core->id.id == BCMA_CORE_SDIO_DEV) + && ((rev > 0) && (rev <= 2))) + di->addrext = false; + else if ((core->id.id == BCMA_CORE_I2S) && + ((rev == 0) || (rev == 1))) + di->addrext = false; + else + di->addrext = _dma_isaddrext(di); + + /* does the descriptor need to be aligned and if yes, on 4K/8K or not */ + di->aligndesc_4k = _dma_descriptor_align(di); + if (di->aligndesc_4k) { + di->dmadesc_align = D64RINGALIGN_BITS; + if ((ntxd < D64MAXDD / 2) && (nrxd < D64MAXDD / 2)) + /* for smaller dd table, HW relax alignment reqmnt */ + di->dmadesc_align = D64RINGALIGN_BITS - 1; + } else { + di->dmadesc_align = 4; /* 16 byte alignment */ + } + + brcms_dbg_dma(di->core, "DMA descriptor align_needed %d, align %d\n", + di->aligndesc_4k, di->dmadesc_align); + + /* allocate tx packet pointer vector */ + if (ntxd) { + size = ntxd * sizeof(void *); + di->txp = kzalloc(size, GFP_ATOMIC); + if (di->txp == NULL) + goto fail; + } + + /* allocate rx packet pointer vector */ + if (nrxd) { + size = nrxd * sizeof(void *); + di->rxp = kzalloc(size, GFP_ATOMIC); + if (di->rxp == NULL) + goto fail; + } + + /* + * allocate transmit descriptor ring, only need ntxd descriptors + * but it must be aligned + */ + if (ntxd) { + if (!_dma_alloc(di, DMA_TX)) + goto fail; + } + + /* + * allocate receive descriptor ring, only need nrxd descriptors + * but it must be aligned + */ + if (nrxd) { + if (!_dma_alloc(di, DMA_RX)) + goto fail; + } + + if ((di->ddoffsetlow != 0) && !di->addrext) { + if (di->txdpa > SI_PCI_DMA_SZ) { + brcms_dbg_dma(di->core, + "%s: txdpa 0x%x: addrext not supported\n", + di->name, (u32)di->txdpa); + goto fail; + } + if (di->rxdpa > SI_PCI_DMA_SZ) { + brcms_dbg_dma(di->core, + "%s: rxdpa 0x%x: addrext not supported\n", + di->name, (u32)di->rxdpa); + goto fail; + } + } + + /* Initialize AMPDU session */ + brcms_c_ampdu_reset_session(&di->ampdu_session, wlc); + + brcms_dbg_dma(di->core, + "ddoffsetlow 0x%x ddoffsethigh 0x%x dataoffsetlow 0x%x dataoffsethigh 0x%x addrext %d\n", + di->ddoffsetlow, di->ddoffsethigh, + di->dataoffsetlow, di->dataoffsethigh, + di->addrext); + + return (struct dma_pub *) di; + + fail: + dma_detach((struct dma_pub *)di); + return NULL; +} + +static inline void +dma64_dd_upd(struct dma_info *di, struct dma64desc *ddring, + dma_addr_t pa, uint outidx, u32 *flags, u32 bufcount) +{ + u32 ctrl2 = bufcount & D64_CTRL2_BC_MASK; + + /* PCI bus with big(>1G) physical address, use address extension */ + if ((di->dataoffsetlow == 0) || !(pa & PCI32ADDR_HIGH)) { + ddring[outidx].addrlow = cpu_to_le32(pa + di->dataoffsetlow); + ddring[outidx].addrhigh = cpu_to_le32(di->dataoffsethigh); + ddring[outidx].ctrl1 = cpu_to_le32(*flags); + ddring[outidx].ctrl2 = cpu_to_le32(ctrl2); + } else { + /* address extension for 32-bit PCI */ + u32 ae; + + ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT; + pa &= ~PCI32ADDR_HIGH; + + ctrl2 |= (ae << D64_CTRL2_AE_SHIFT) & D64_CTRL2_AE; + ddring[outidx].addrlow = cpu_to_le32(pa + di->dataoffsetlow); + ddring[outidx].addrhigh = cpu_to_le32(di->dataoffsethigh); + ddring[outidx].ctrl1 = cpu_to_le32(*flags); + ddring[outidx].ctrl2 = cpu_to_le32(ctrl2); + } + if (di->dma.dmactrlflags & DMA_CTRL_PEN) { + if (dma64_dd_parity(&ddring[outidx])) + ddring[outidx].ctrl2 = + cpu_to_le32(ctrl2 | D64_CTRL2_PARITY); + } +} + +/* !! may be called with core in reset */ +void dma_detach(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + + brcms_dbg_dma(di->core, "%s:\n", di->name); + + /* free dma descriptor rings */ + if (di->txd64) + dma_free_coherent(di->dmadev, di->txdalloc, + ((s8 *)di->txd64 - di->txdalign), + (di->txdpaorig)); + if (di->rxd64) + dma_free_coherent(di->dmadev, di->rxdalloc, + ((s8 *)di->rxd64 - di->rxdalign), + (di->rxdpaorig)); + + /* free packet pointer vectors */ + kfree(di->txp); + kfree(di->rxp); + + /* free our private info structure */ + kfree(di); + +} + +/* initialize descriptor table base address */ +static void +_dma_ddtable_init(struct dma_info *di, uint direction, dma_addr_t pa) +{ + if (!di->aligndesc_4k) { + if (direction == DMA_TX) + di->xmtptrbase = pa; + else + di->rcvptrbase = pa; + } + + if ((di->ddoffsetlow == 0) + || !(pa & PCI32ADDR_HIGH)) { + if (direction == DMA_TX) { + bcma_write32(di->core, DMA64TXREGOFFS(di, addrlow), + pa + di->ddoffsetlow); + bcma_write32(di->core, DMA64TXREGOFFS(di, addrhigh), + di->ddoffsethigh); + } else { + bcma_write32(di->core, DMA64RXREGOFFS(di, addrlow), + pa + di->ddoffsetlow); + bcma_write32(di->core, DMA64RXREGOFFS(di, addrhigh), + di->ddoffsethigh); + } + } else { + /* DMA64 32bits address extension */ + u32 ae; + + /* shift the high bit(s) from pa to ae */ + ae = (pa & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT; + pa &= ~PCI32ADDR_HIGH; + + if (direction == DMA_TX) { + bcma_write32(di->core, DMA64TXREGOFFS(di, addrlow), + pa + di->ddoffsetlow); + bcma_write32(di->core, DMA64TXREGOFFS(di, addrhigh), + di->ddoffsethigh); + bcma_maskset32(di->core, DMA64TXREGOFFS(di, control), + D64_XC_AE, (ae << D64_XC_AE_SHIFT)); + } else { + bcma_write32(di->core, DMA64RXREGOFFS(di, addrlow), + pa + di->ddoffsetlow); + bcma_write32(di->core, DMA64RXREGOFFS(di, addrhigh), + di->ddoffsethigh); + bcma_maskset32(di->core, DMA64RXREGOFFS(di, control), + D64_RC_AE, (ae << D64_RC_AE_SHIFT)); + } + } +} + +static void _dma_rxenable(struct dma_info *di) +{ + uint dmactrlflags = di->dma.dmactrlflags; + u32 control; + + brcms_dbg_dma(di->core, "%s:\n", di->name); + + control = D64_RC_RE | (bcma_read32(di->core, + DMA64RXREGOFFS(di, control)) & + D64_RC_AE); + + if ((dmactrlflags & DMA_CTRL_PEN) == 0) + control |= D64_RC_PD; + + if (dmactrlflags & DMA_CTRL_ROC) + control |= D64_RC_OC; + + bcma_write32(di->core, DMA64RXREGOFFS(di, control), + ((di->rxoffset << D64_RC_RO_SHIFT) | control)); +} + +void dma_rxinit(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + + brcms_dbg_dma(di->core, "%s:\n", di->name); + + if (di->nrxd == 0) + return; + + di->rxin = di->rxout = 0; + + /* clear rx descriptor ring */ + memset(di->rxd64, '\0', di->nrxd * sizeof(struct dma64desc)); + + /* DMA engine with out alignment requirement requires table to be inited + * before enabling the engine + */ + if (!di->aligndesc_4k) + _dma_ddtable_init(di, DMA_RX, di->rxdpa); + + _dma_rxenable(di); + + if (di->aligndesc_4k) + _dma_ddtable_init(di, DMA_RX, di->rxdpa); +} + +static struct sk_buff *dma64_getnextrxp(struct dma_info *di, bool forceall) +{ + uint i, curr; + struct sk_buff *rxp; + dma_addr_t pa; + + i = di->rxin; + + /* return if no packets posted */ + if (i == di->rxout) + return NULL; + + curr = + B2I(((bcma_read32(di->core, + DMA64RXREGOFFS(di, status0)) & D64_RS0_CD_MASK) - + di->rcvptrbase) & D64_RS0_CD_MASK, struct dma64desc); + + /* ignore curr if forceall */ + if (!forceall && (i == curr)) + return NULL; + + /* get the packet pointer that corresponds to the rx descriptor */ + rxp = di->rxp[i]; + di->rxp[i] = NULL; + + pa = le32_to_cpu(di->rxd64[i].addrlow) - di->dataoffsetlow; + + /* clear this packet from the descriptor ring */ + dma_unmap_single(di->dmadev, pa, di->rxbufsize, DMA_FROM_DEVICE); + + di->rxd64[i].addrlow = cpu_to_le32(0xdeadbeef); + di->rxd64[i].addrhigh = cpu_to_le32(0xdeadbeef); + + di->rxin = nextrxd(di, i); + + return rxp; +} + +static struct sk_buff *_dma_getnextrxp(struct dma_info *di, bool forceall) +{ + if (di->nrxd == 0) + return NULL; + + return dma64_getnextrxp(di, forceall); +} + +/* + * !! rx entry routine + * returns the number packages in the next frame, or 0 if there are no more + * if DMA_CTRL_RXMULTI is defined, DMA scattering(multiple buffers) is + * supported with pkts chain + * otherwise, it's treated as giant pkt and will be tossed. + * The DMA scattering starts with normal DMA header, followed by first + * buffer data. After it reaches the max size of buffer, the data continues + * in next DMA descriptor buffer WITHOUT DMA header + */ +int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + struct sk_buff_head dma_frames; + struct sk_buff *p, *next; + uint len; + uint pkt_len; + int resid = 0; + int pktcnt = 1; + + skb_queue_head_init(&dma_frames); + next_frame: + p = _dma_getnextrxp(di, false); + if (p == NULL) + return 0; + + len = le16_to_cpu(*(__le16 *) (p->data)); + brcms_dbg_dma(di->core, "%s: dma_rx len %d\n", di->name, len); + dma_spin_for_len(len, p); + + /* set actual length */ + pkt_len = min((di->rxoffset + len), di->rxbufsize); + __skb_trim(p, pkt_len); + skb_queue_tail(&dma_frames, p); + resid = len - (di->rxbufsize - di->rxoffset); + + /* check for single or multi-buffer rx */ + if (resid > 0) { + while ((resid > 0) && (p = _dma_getnextrxp(di, false))) { + pkt_len = min_t(uint, resid, di->rxbufsize); + __skb_trim(p, pkt_len); + skb_queue_tail(&dma_frames, p); + resid -= di->rxbufsize; + pktcnt++; + } + +#ifdef DEBUG + if (resid > 0) { + uint cur; + cur = + B2I(((bcma_read32(di->core, + DMA64RXREGOFFS(di, status0)) & + D64_RS0_CD_MASK) - di->rcvptrbase) & + D64_RS0_CD_MASK, struct dma64desc); + brcms_dbg_dma(di->core, + "rxin %d rxout %d, hw_curr %d\n", + di->rxin, di->rxout, cur); + } +#endif /* DEBUG */ + + if ((di->dma.dmactrlflags & DMA_CTRL_RXMULTI) == 0) { + brcms_dbg_dma(di->core, "%s: bad frame length (%d)\n", + di->name, len); + skb_queue_walk_safe(&dma_frames, p, next) { + skb_unlink(p, &dma_frames); + brcmu_pkt_buf_free_skb(p); + } + di->dma.rxgiants++; + pktcnt = 1; + goto next_frame; + } + } + + skb_queue_splice_tail(&dma_frames, skb_list); + return pktcnt; +} + +static bool dma64_rxidle(struct dma_info *di) +{ + brcms_dbg_dma(di->core, "%s:\n", di->name); + + if (di->nrxd == 0) + return true; + + return ((bcma_read32(di->core, + DMA64RXREGOFFS(di, status0)) & D64_RS0_CD_MASK) == + (bcma_read32(di->core, DMA64RXREGOFFS(di, ptr)) & + D64_RS0_CD_MASK)); +} + +static bool dma64_txidle(struct dma_info *di) +{ + if (di->ntxd == 0) + return true; + + return ((bcma_read32(di->core, + DMA64TXREGOFFS(di, status0)) & D64_XS0_CD_MASK) == + (bcma_read32(di->core, DMA64TXREGOFFS(di, ptr)) & + D64_XS0_CD_MASK)); +} + +/* + * post receive buffers + * Return false if refill failed completely or dma mapping failed. The ring + * is empty, which will stall the rx dma and user might want to call rxfill + * again asap. This is unlikely to happen on a memory-rich NIC, but often on + * memory-constrained dongle. + */ +bool dma_rxfill(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + struct sk_buff *p; + u16 rxin, rxout; + u32 flags = 0; + uint n; + uint i; + dma_addr_t pa; + uint extra_offset = 0; + bool ring_empty; + + ring_empty = false; + + /* + * Determine how many receive buffers we're lacking + * from the full complement, allocate, initialize, + * and post them, then update the chip rx lastdscr. + */ + + rxin = di->rxin; + rxout = di->rxout; + + n = di->nrxpost - nrxdactive(di, rxin, rxout); + + brcms_dbg_dma(di->core, "%s: post %d\n", di->name, n); + + if (di->rxbufsize > BCMEXTRAHDROOM) + extra_offset = di->rxextrahdrroom; + + for (i = 0; i < n; i++) { + /* + * the di->rxbufsize doesn't include the extra headroom, + * we need to add it to the size to be allocated + */ + p = brcmu_pkt_buf_get_skb(di->rxbufsize + extra_offset); + + if (p == NULL) { + brcms_dbg_dma(di->core, "%s: out of rxbufs\n", + di->name); + if (i == 0 && dma64_rxidle(di)) { + brcms_dbg_dma(di->core, "%s: ring is empty !\n", + di->name); + ring_empty = true; + } + di->dma.rxnobuf++; + break; + } + /* reserve an extra headroom, if applicable */ + if (extra_offset) + skb_pull(p, extra_offset); + + /* Do a cached write instead of uncached write since DMA_MAP + * will flush the cache. + */ + *(u32 *) (p->data) = 0; + + pa = dma_map_single(di->dmadev, p->data, di->rxbufsize, + DMA_FROM_DEVICE); + if (dma_mapping_error(di->dmadev, pa)) + return false; + + /* save the free packet pointer */ + di->rxp[rxout] = p; + + /* reset flags for each descriptor */ + flags = 0; + if (rxout == (di->nrxd - 1)) + flags = D64_CTRL1_EOT; + + dma64_dd_upd(di, di->rxd64, pa, rxout, &flags, + di->rxbufsize); + rxout = nextrxd(di, rxout); + } + + di->rxout = rxout; + + /* update the chip lastdscr pointer */ + bcma_write32(di->core, DMA64RXREGOFFS(di, ptr), + di->rcvptrbase + I2B(rxout, struct dma64desc)); + + return ring_empty; +} + +void dma_rxreclaim(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + struct sk_buff *p; + + brcms_dbg_dma(di->core, "%s:\n", di->name); + + while ((p = _dma_getnextrxp(di, true))) + brcmu_pkt_buf_free_skb(p); +} + +void dma_counterreset(struct dma_pub *pub) +{ + /* reset all software counters */ + pub->rxgiants = 0; + pub->rxnobuf = 0; + pub->txnobuf = 0; +} + +/* get the address of the var in order to change later */ +unsigned long dma_getvar(struct dma_pub *pub, const char *name) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + + if (!strcmp(name, "&txavail")) + return (unsigned long)&(di->dma.txavail); + return 0; +} + +/* 64-bit DMA functions */ + +void dma_txinit(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + u32 control = D64_XC_XE; + + brcms_dbg_dma(di->core, "%s:\n", di->name); + + if (di->ntxd == 0) + return; + + di->txin = di->txout = 0; + di->dma.txavail = di->ntxd - 1; + + /* clear tx descriptor ring */ + memset(di->txd64, '\0', (di->ntxd * sizeof(struct dma64desc))); + + /* DMA engine with out alignment requirement requires table to be inited + * before enabling the engine + */ + if (!di->aligndesc_4k) + _dma_ddtable_init(di, DMA_TX, di->txdpa); + + if ((di->dma.dmactrlflags & DMA_CTRL_PEN) == 0) + control |= D64_XC_PD; + bcma_set32(di->core, DMA64TXREGOFFS(di, control), control); + + /* DMA engine with alignment requirement requires table to be inited + * before enabling the engine + */ + if (di->aligndesc_4k) + _dma_ddtable_init(di, DMA_TX, di->txdpa); +} + +void dma_txsuspend(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + + brcms_dbg_dma(di->core, "%s:\n", di->name); + + if (di->ntxd == 0) + return; + + bcma_set32(di->core, DMA64TXREGOFFS(di, control), D64_XC_SE); +} + +void dma_txresume(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + + brcms_dbg_dma(di->core, "%s:\n", di->name); + + if (di->ntxd == 0) + return; + + bcma_mask32(di->core, DMA64TXREGOFFS(di, control), ~D64_XC_SE); +} + +bool dma_txsuspended(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + + return (di->ntxd == 0) || + ((bcma_read32(di->core, + DMA64TXREGOFFS(di, control)) & D64_XC_SE) == + D64_XC_SE); +} + +void dma_txreclaim(struct dma_pub *pub, enum txd_range range) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + struct sk_buff *p; + + brcms_dbg_dma(di->core, "%s: %s\n", + di->name, + range == DMA_RANGE_ALL ? "all" : + range == DMA_RANGE_TRANSMITTED ? "transmitted" : + "transferred"); + + if (di->txin == di->txout) + return; + + while ((p = dma_getnexttxp(pub, range))) { + /* For unframed data, we don't have any packets to free */ + if (!(di->dma.dmactrlflags & DMA_CTRL_UNFRAMED)) + brcmu_pkt_buf_free_skb(p); + } +} + +bool dma_txreset(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + u32 status; + + if (di->ntxd == 0) + return true; + + /* suspend tx DMA first */ + bcma_write32(di->core, DMA64TXREGOFFS(di, control), D64_XC_SE); + SPINWAIT(((status = + (bcma_read32(di->core, DMA64TXREGOFFS(di, status0)) & + D64_XS0_XS_MASK)) != D64_XS0_XS_DISABLED) && + (status != D64_XS0_XS_IDLE) && (status != D64_XS0_XS_STOPPED), + 10000); + + bcma_write32(di->core, DMA64TXREGOFFS(di, control), 0); + SPINWAIT(((status = + (bcma_read32(di->core, DMA64TXREGOFFS(di, status0)) & + D64_XS0_XS_MASK)) != D64_XS0_XS_DISABLED), 10000); + + /* wait for the last transaction to complete */ + udelay(300); + + return status == D64_XS0_XS_DISABLED; +} + +bool dma_rxreset(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + u32 status; + + if (di->nrxd == 0) + return true; + + bcma_write32(di->core, DMA64RXREGOFFS(di, control), 0); + SPINWAIT(((status = + (bcma_read32(di->core, DMA64RXREGOFFS(di, status0)) & + D64_RS0_RS_MASK)) != D64_RS0_RS_DISABLED), 10000); + + return status == D64_RS0_RS_DISABLED; +} + +static void dma_txenq(struct dma_info *di, struct sk_buff *p) +{ + unsigned char *data; + uint len; + u16 txout; + u32 flags = 0; + dma_addr_t pa; + + txout = di->txout; + + if (WARN_ON(nexttxd(di, txout) == di->txin)) + return; + + /* + * obtain and initialize transmit descriptor entry. + */ + data = p->data; + len = p->len; + + /* get physical address of buffer start */ + pa = dma_map_single(di->dmadev, data, len, DMA_TO_DEVICE); + /* if mapping failed, free skb */ + if (dma_mapping_error(di->dmadev, pa)) { + brcmu_pkt_buf_free_skb(p); + return; + } + /* With a DMA segment list, Descriptor table is filled + * using the segment list instead of looping over + * buffers in multi-chain DMA. Therefore, EOF for SGLIST + * is when end of segment list is reached. + */ + flags = D64_CTRL1_SOF | D64_CTRL1_IOC | D64_CTRL1_EOF; + if (txout == (di->ntxd - 1)) + flags |= D64_CTRL1_EOT; + + dma64_dd_upd(di, di->txd64, pa, txout, &flags, len); + + txout = nexttxd(di, txout); + + /* save the packet */ + di->txp[prevtxd(di, txout)] = p; + + /* bump the tx descriptor index */ + di->txout = txout; +} + +static void ampdu_finalize(struct dma_info *di) +{ + struct brcms_ampdu_session *session = &di->ampdu_session; + struct sk_buff *p; + + trace_brcms_ampdu_session(&session->wlc->hw->d11core->dev, + session->max_ampdu_len, + session->max_ampdu_frames, + session->ampdu_len, + skb_queue_len(&session->skb_list), + session->dma_len); + + if (WARN_ON(skb_queue_empty(&session->skb_list))) + return; + + brcms_c_ampdu_finalize(session); + + while (!skb_queue_empty(&session->skb_list)) { + p = skb_dequeue(&session->skb_list); + dma_txenq(di, p); + } + + bcma_write32(di->core, DMA64TXREGOFFS(di, ptr), + di->xmtptrbase + I2B(di->txout, struct dma64desc)); + brcms_c_ampdu_reset_session(session, session->wlc); +} + +static void prep_ampdu_frame(struct dma_info *di, struct sk_buff *p) +{ + struct brcms_ampdu_session *session = &di->ampdu_session; + int ret; + + ret = brcms_c_ampdu_add_frame(session, p); + if (ret == -ENOSPC) { + /* + * AMPDU cannot accomodate this frame. Close out the in- + * progress AMPDU session and start a new one. + */ + ampdu_finalize(di); + ret = brcms_c_ampdu_add_frame(session, p); + } + + WARN_ON(ret); +} + +/* Update count of available tx descriptors based on current DMA state */ +static void dma_update_txavail(struct dma_info *di) +{ + /* + * Available space is number of descriptors less the number of + * active descriptors and the number of queued AMPDU frames. + */ + di->dma.txavail = di->ntxd - ntxdactive(di, di->txin, di->txout) - + skb_queue_len(&di->ampdu_session.skb_list) - 1; +} + +/* + * !! tx entry routine + * WARNING: call must check the return value for error. + * the error(toss frames) could be fatal and cause many subsequent hard + * to debug problems + */ +int dma_txfast(struct brcms_c_info *wlc, struct dma_pub *pub, + struct sk_buff *p) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + struct brcms_ampdu_session *session = &di->ampdu_session; + struct ieee80211_tx_info *tx_info; + bool is_ampdu; + + /* no use to transmit a zero length packet */ + if (p->len == 0) + return 0; + + /* return nonzero if out of tx descriptors */ + if (di->dma.txavail == 0 || nexttxd(di, di->txout) == di->txin) + goto outoftxd; + + tx_info = IEEE80211_SKB_CB(p); + is_ampdu = tx_info->flags & IEEE80211_TX_CTL_AMPDU; + if (is_ampdu) + prep_ampdu_frame(di, p); + else + dma_txenq(di, p); + + /* tx flow control */ + dma_update_txavail(di); + + /* kick the chip */ + if (is_ampdu) { + /* + * Start sending data if we've got a full AMPDU, there's + * no more space in the DMA ring, or the ring isn't + * currently transmitting. + */ + if (skb_queue_len(&session->skb_list) == session->max_ampdu_frames || + di->dma.txavail == 0 || dma64_txidle(di)) + ampdu_finalize(di); + } else { + bcma_write32(di->core, DMA64TXREGOFFS(di, ptr), + di->xmtptrbase + I2B(di->txout, struct dma64desc)); + } + + return 0; + + outoftxd: + brcms_dbg_dma(di->core, "%s: out of txds !!!\n", di->name); + brcmu_pkt_buf_free_skb(p); + di->dma.txavail = 0; + di->dma.txnobuf++; + return -ENOSPC; +} + +void dma_txflush(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + struct brcms_ampdu_session *session = &di->ampdu_session; + + if (!skb_queue_empty(&session->skb_list)) + ampdu_finalize(di); +} + +int dma_txpending(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + return ntxdactive(di, di->txin, di->txout); +} + +/* + * If we have an active AMPDU session and are not transmitting, + * this function will force tx to start. + */ +void dma_kick_tx(struct dma_pub *pub) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + struct brcms_ampdu_session *session = &di->ampdu_session; + + if (!skb_queue_empty(&session->skb_list) && dma64_txidle(di)) + ampdu_finalize(di); +} + +/* + * Reclaim next completed txd (txds if using chained buffers) in the range + * specified and return associated packet. + * If range is DMA_RANGE_TRANSMITTED, reclaim descriptors that have be + * transmitted as noted by the hardware "CurrDescr" pointer. + * If range is DMA_RANGE_TRANSFERED, reclaim descriptors that have be + * transferred by the DMA as noted by the hardware "ActiveDescr" pointer. + * If range is DMA_RANGE_ALL, reclaim all txd(s) posted to the ring and + * return associated packet regardless of the value of hardware pointers. + */ +struct sk_buff *dma_getnexttxp(struct dma_pub *pub, enum txd_range range) +{ + struct dma_info *di = container_of(pub, struct dma_info, dma); + u16 start, end, i; + u16 active_desc; + struct sk_buff *txp; + + brcms_dbg_dma(di->core, "%s: %s\n", + di->name, + range == DMA_RANGE_ALL ? "all" : + range == DMA_RANGE_TRANSMITTED ? "transmitted" : + "transferred"); + + if (di->ntxd == 0) + return NULL; + + txp = NULL; + + start = di->txin; + if (range == DMA_RANGE_ALL) + end = di->txout; + else { + end = (u16) (B2I(((bcma_read32(di->core, + DMA64TXREGOFFS(di, status0)) & + D64_XS0_CD_MASK) - di->xmtptrbase) & + D64_XS0_CD_MASK, struct dma64desc)); + + if (range == DMA_RANGE_TRANSFERED) { + active_desc = + (u16)(bcma_read32(di->core, + DMA64TXREGOFFS(di, status1)) & + D64_XS1_AD_MASK); + active_desc = + (active_desc - di->xmtptrbase) & D64_XS0_CD_MASK; + active_desc = B2I(active_desc, struct dma64desc); + if (end != active_desc) + end = prevtxd(di, active_desc); + } + } + + if ((start == 0) && (end > di->txout)) + goto bogus; + + for (i = start; i != end && !txp; i = nexttxd(di, i)) { + dma_addr_t pa; + uint size; + + pa = le32_to_cpu(di->txd64[i].addrlow) - di->dataoffsetlow; + + size = + (le32_to_cpu(di->txd64[i].ctrl2) & + D64_CTRL2_BC_MASK); + + di->txd64[i].addrlow = cpu_to_le32(0xdeadbeef); + di->txd64[i].addrhigh = cpu_to_le32(0xdeadbeef); + + txp = di->txp[i]; + di->txp[i] = NULL; + + dma_unmap_single(di->dmadev, pa, size, DMA_TO_DEVICE); + } + + di->txin = i; + + /* tx flow control */ + dma_update_txavail(di); + + return txp; + + bogus: + brcms_dbg_dma(di->core, "bogus curr: start %d end %d txout %d\n", + start, end, di->txout); + return NULL; +} + +/* + * Mac80211 initiated actions sometimes require packets in the DMA queue to be + * modified. The modified portion of the packet is not under control of the DMA + * engine. This function calls a caller-supplied function for each packet in + * the caller specified dma chain. + */ +void dma_walk_packets(struct dma_pub *dmah, void (*callback_fnc) + (void *pkt, void *arg_a), void *arg_a) +{ + struct dma_info *di = container_of(dmah, struct dma_info, dma); + uint i = di->txin; + uint end = di->txout; + struct sk_buff *skb; + struct ieee80211_tx_info *tx_info; + + while (i != end) { + skb = di->txp[i]; + if (skb != NULL) { + tx_info = (struct ieee80211_tx_info *)skb->cb; + (callback_fnc)(tx_info, arg_a); + } + i = nexttxd(di, i); + } +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.h new file mode 100644 index 000000000000..ff5b80b09046 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2010 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 _BRCM_DMA_H_ +#define _BRCM_DMA_H_ + +#include +#include +#include "types.h" /* forward structure declarations */ + +/* map/unmap direction */ +#define DMA_TX 1 /* TX direction for DMA */ +#define DMA_RX 2 /* RX direction for DMA */ + +/* DMA structure: + * support two DMA engines: 32 bits address or 64 bit addressing + * basic DMA register set is per channel(transmit or receive) + * a pair of channels is defined for convenience + */ + +/* 32 bits addressing */ + +struct dma32diag { /* diag access */ + u32 fifoaddr; /* diag address */ + u32 fifodatalow; /* low 32bits of data */ + u32 fifodatahigh; /* high 32bits of data */ + u32 pad; /* reserved */ +}; + +/* 64 bits addressing */ + +/* dma registers per channel(xmt or rcv) */ +struct dma64regs { + u32 control; /* enable, et al */ + u32 ptr; /* last descriptor posted to chip */ + u32 addrlow; /* desc ring base address low 32-bits (8K aligned) */ + u32 addrhigh; /* desc ring base address bits 63:32 (8K aligned) */ + u32 status0; /* current descriptor, xmt state */ + u32 status1; /* active descriptor, xmt error */ +}; + +/* range param for dma_getnexttxp() and dma_txreclaim */ +enum txd_range { + DMA_RANGE_ALL = 1, + DMA_RANGE_TRANSMITTED, + DMA_RANGE_TRANSFERED +}; + +/* + * Exported data structure (read-only) + */ +/* export structure */ +struct dma_pub { + uint txavail; /* # free tx descriptors */ + uint dmactrlflags; /* dma control flags */ + + /* rx error counters */ + uint rxgiants; /* rx giant frames */ + uint rxnobuf; /* rx out of dma descriptors */ + /* tx error counters */ + uint txnobuf; /* tx out of dma descriptors */ +}; + +extern struct dma_pub *dma_attach(char *name, struct brcms_c_info *wlc, + uint txregbase, uint rxregbase, + uint ntxd, uint nrxd, + uint rxbufsize, int rxextheadroom, + uint nrxpost, uint rxoffset); + +void dma_rxinit(struct dma_pub *pub); +int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list); +bool dma_rxfill(struct dma_pub *pub); +bool dma_rxreset(struct dma_pub *pub); +bool dma_txreset(struct dma_pub *pub); +void dma_txinit(struct dma_pub *pub); +int dma_txfast(struct brcms_c_info *wlc, struct dma_pub *pub, + struct sk_buff *p0); +void dma_txflush(struct dma_pub *pub); +int dma_txpending(struct dma_pub *pub); +void dma_kick_tx(struct dma_pub *pub); +void dma_txsuspend(struct dma_pub *pub); +bool dma_txsuspended(struct dma_pub *pub); +void dma_txresume(struct dma_pub *pub); +void dma_txreclaim(struct dma_pub *pub, enum txd_range range); +void dma_rxreclaim(struct dma_pub *pub); +void dma_detach(struct dma_pub *pub); +unsigned long dma_getvar(struct dma_pub *pub, const char *name); +struct sk_buff *dma_getnexttxp(struct dma_pub *pub, enum txd_range range); +void dma_counterreset(struct dma_pub *pub); + +void dma_walk_packets(struct dma_pub *dmah, void (*callback_fnc) + (void *pkt, void *arg_a), void *arg_a); + +/* + * DMA(Bug) on bcm47xx chips seems to declare that the packet is ready, but + * the packet length is not updated yet (by DMA) on the expected time. + * Workaround is to hold processor till DMA updates the length, and stay off + * the bus to allow DMA update the length in buffer + */ +static inline void dma_spin_for_len(uint len, struct sk_buff *head) +{ +#if defined(CONFIG_BCM47XX) + if (!len) { + while (!(len = *(u16 *) KSEG1ADDR(head->data))) + udelay(1); + + *(u16 *) (head->data) = cpu_to_le16((u16) len); + } +#endif /* defined(CONFIG_BCM47XX) */ +} + +#endif /* _BRCM_DMA_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c new file mode 100644 index 000000000000..74b17cecb189 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c @@ -0,0 +1,126 @@ +#include +#include +#include + +#include "mac80211_if.h" +#include "pub.h" +#include "main.h" +#include "led.h" + + /* number of leds */ +#define BRCMS_LED_NO 4 + /* behavior mask */ +#define BRCMS_LED_BEH_MASK 0x7f + /* activelow (polarity) bit */ +#define BRCMS_LED_AL_MASK 0x80 + /* radio enabled */ +#define BRCMS_LED_RADIO 3 + +static void brcms_radio_led_ctrl(struct brcms_info *wl, bool state) +{ + if (wl->radio_led.gpio == -1) + return; + + if (wl->radio_led.active_low) + state = !state; + + if (state) + gpio_set_value(wl->radio_led.gpio, 1); + else + gpio_set_value(wl->radio_led.gpio, 0); +} + + +/* Callback from the LED subsystem. */ +static void brcms_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct brcms_info *wl = container_of(led_dev, + struct brcms_info, led_dev); + brcms_radio_led_ctrl(wl, brightness); +} + +void brcms_led_unregister(struct brcms_info *wl) +{ + if (wl->led_dev.dev) + led_classdev_unregister(&wl->led_dev); + if (wl->radio_led.gpio != -1) + gpio_free(wl->radio_led.gpio); +} + +int brcms_led_register(struct brcms_info *wl) +{ + int i, err; + struct brcms_led *radio_led = &wl->radio_led; + /* get CC core */ + struct bcma_drv_cc *cc_drv = &wl->wlc->hw->d11core->bus->drv_cc; + struct gpio_chip *bcma_gpio = &cc_drv->gpio; + struct ssb_sprom *sprom = &wl->wlc->hw->d11core->bus->sprom; + u8 *leds[] = { &sprom->gpio0, + &sprom->gpio1, + &sprom->gpio2, + &sprom->gpio3 }; + unsigned gpio = -1; + bool active_low = false; + + /* none by default */ + radio_led->gpio = -1; + radio_led->active_low = false; + + if (!bcma_gpio || !gpio_is_valid(bcma_gpio->base)) + return -ENODEV; + + /* find radio enabled LED */ + for (i = 0; i < BRCMS_LED_NO; i++) { + u8 led = *leds[i]; + if ((led & BRCMS_LED_BEH_MASK) == BRCMS_LED_RADIO) { + gpio = bcma_gpio->base + i; + if (led & BRCMS_LED_AL_MASK) + active_low = true; + break; + } + } + + if (gpio == -1 || !gpio_is_valid(gpio)) + return -ENODEV; + + /* request and configure LED gpio */ + err = gpio_request_one(gpio, + active_low ? GPIOF_OUT_INIT_HIGH + : GPIOF_OUT_INIT_LOW, + "radio on"); + if (err) { + wiphy_err(wl->wiphy, "requesting led gpio %d failed (err: %d)\n", + gpio, err); + return err; + } + err = gpio_direction_output(gpio, 1); + if (err) { + wiphy_err(wl->wiphy, "cannot set led gpio %d to output (err: %d)\n", + gpio, err); + return err; + } + + snprintf(wl->radio_led.name, sizeof(wl->radio_led.name), + "brcmsmac-%s:radio", wiphy_name(wl->wiphy)); + + wl->led_dev.name = wl->radio_led.name; + wl->led_dev.default_trigger = + ieee80211_get_radio_led_name(wl->pub->ieee_hw); + wl->led_dev.brightness_set = brcms_led_brightness_set; + err = led_classdev_register(wiphy_dev(wl->wiphy), &wl->led_dev); + + if (err) { + wiphy_err(wl->wiphy, "cannot register led device: %s (err: %d)\n", + wl->radio_led.name, err); + return err; + } + + wiphy_info(wl->wiphy, "registered radio enabled led device: %s gpio: %d\n", + wl->radio_led.name, + gpio); + radio_led->gpio = gpio; + radio_led->active_low = active_low; + + return 0; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h new file mode 100644 index 000000000000..17a0b1f5dbcf --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2012 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 _BRCM_LED_H_ +#define _BRCM_LED_H_ +struct brcms_led { + char name[32]; + unsigned gpio; + bool active_low; +}; + +#ifdef CONFIG_BCMA_DRIVER_GPIO +void brcms_led_unregister(struct brcms_info *wl); +int brcms_led_register(struct brcms_info *wl); +#else +static inline void brcms_led_unregister(struct brcms_info *wl) {}; +static inline int brcms_led_register(struct brcms_info *wl) +{ + return -ENOTSUPP; +}; +#endif + +#endif /* _BRCM_LED_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c new file mode 100644 index 000000000000..bec2dc1ca2e4 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c @@ -0,0 +1,1700 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * Copyright (c) 2013 Hauke Mehrtens + * + * 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. + */ + +#define __UNDEF_NO_VERSION__ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "phy/phy_int.h" +#include "d11.h" +#include "channel.h" +#include "scb.h" +#include "pub.h" +#include "ucode_loader.h" +#include "mac80211_if.h" +#include "main.h" +#include "debug.h" +#include "led.h" + +#define N_TX_QUEUES 4 /* #tx queues on mac80211<->driver interface */ +#define BRCMS_FLUSH_TIMEOUT 500 /* msec */ + +/* Flags we support */ +#define MAC_FILTERS (FIF_ALLMULTI | \ + FIF_FCSFAIL | \ + FIF_CONTROL | \ + FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_PSPOLL) + +#define CHAN2GHZ(channel, freqency, chflags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (freqency), \ + .hw_value = (channel), \ + .flags = chflags, \ + .max_antenna_gain = 0, \ + .max_power = 19, \ +} + +#define CHAN5GHZ(channel, chflags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + 5*(channel), \ + .hw_value = (channel), \ + .flags = chflags, \ + .max_antenna_gain = 0, \ + .max_power = 21, \ +} + +#define RATE(rate100m, _flags) { \ + .bitrate = (rate100m), \ + .flags = (_flags), \ + .hw_value = (rate100m / 5), \ +} + +struct firmware_hdr { + __le32 offset; + __le32 len; + __le32 idx; +}; + +static const char * const brcms_firmwares[MAX_FW_IMAGES] = { + "brcm/bcm43xx", + NULL +}; + +static int n_adapters_found; + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards"); +MODULE_LICENSE("Dual BSD/GPL"); +/* This needs to be adjusted when brcms_firmwares changes */ +MODULE_FIRMWARE("brcm/bcm43xx-0.fw"); +MODULE_FIRMWARE("brcm/bcm43xx_hdr-0.fw"); + +/* recognized BCMA Core IDs */ +static struct bcma_device_id brcms_coreid_table[] = { + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 17, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 23, BCMA_ANY_CLASS), + BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 24, BCMA_ANY_CLASS), + {}, +}; +MODULE_DEVICE_TABLE(bcma, brcms_coreid_table); + +#if defined(CONFIG_BRCMDBG) +/* + * Module parameter for setting the debug message level. Available + * flags are specified by the BRCM_DL_* macros in + * drivers/net/wireless/brcm80211/include/defs.h. + */ +module_param_named(debug, brcm_msg_level, uint, S_IRUGO | S_IWUSR); +#endif + +static struct ieee80211_channel brcms_2ghz_chantable[] = { + CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS), + CHAN2GHZ(2, 2417, IEEE80211_CHAN_NO_HT40MINUS), + CHAN2GHZ(3, 2422, IEEE80211_CHAN_NO_HT40MINUS), + CHAN2GHZ(4, 2427, IEEE80211_CHAN_NO_HT40MINUS), + CHAN2GHZ(5, 2432, 0), + CHAN2GHZ(6, 2437, 0), + CHAN2GHZ(7, 2442, 0), + CHAN2GHZ(8, 2447, IEEE80211_CHAN_NO_HT40PLUS), + CHAN2GHZ(9, 2452, IEEE80211_CHAN_NO_HT40PLUS), + CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS), + CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS), + CHAN2GHZ(12, 2467, + IEEE80211_CHAN_NO_IR | + IEEE80211_CHAN_NO_HT40PLUS), + CHAN2GHZ(13, 2472, + IEEE80211_CHAN_NO_IR | + IEEE80211_CHAN_NO_HT40PLUS), + CHAN2GHZ(14, 2484, + IEEE80211_CHAN_NO_IR | + IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS | + IEEE80211_CHAN_NO_OFDM) +}; + +static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = { + /* UNII-1 */ + CHAN5GHZ(36, IEEE80211_CHAN_NO_HT40MINUS), + CHAN5GHZ(40, IEEE80211_CHAN_NO_HT40PLUS), + CHAN5GHZ(44, IEEE80211_CHAN_NO_HT40MINUS), + CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS), + /* UNII-2 */ + CHAN5GHZ(52, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), + CHAN5GHZ(56, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), + CHAN5GHZ(60, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), + CHAN5GHZ(64, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), + /* MID */ + CHAN5GHZ(100, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), + CHAN5GHZ(104, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), + CHAN5GHZ(108, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), + CHAN5GHZ(112, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), + CHAN5GHZ(116, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), + CHAN5GHZ(120, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), + CHAN5GHZ(124, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), + CHAN5GHZ(128, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), + CHAN5GHZ(132, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), + CHAN5GHZ(136, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), + CHAN5GHZ(140, + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS | + IEEE80211_CHAN_NO_HT40MINUS), + /* UNII-3 */ + CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS), + CHAN5GHZ(153, IEEE80211_CHAN_NO_HT40PLUS), + CHAN5GHZ(157, IEEE80211_CHAN_NO_HT40MINUS), + CHAN5GHZ(161, IEEE80211_CHAN_NO_HT40PLUS), + CHAN5GHZ(165, IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS) +}; + +/* + * The rate table is used for both 2.4G and 5G rates. The + * latter being a subset as it does not support CCK rates. + */ +static struct ieee80211_rate legacy_ratetable[] = { + RATE(10, 0), + RATE(20, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(55, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(110, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(60, 0), + RATE(90, 0), + RATE(120, 0), + RATE(180, 0), + RATE(240, 0), + RATE(360, 0), + RATE(480, 0), + RATE(540, 0), +}; + +static const struct ieee80211_supported_band brcms_band_2GHz_nphy_template = { + .band = IEEE80211_BAND_2GHZ, + .channels = brcms_2ghz_chantable, + .n_channels = ARRAY_SIZE(brcms_2ghz_chantable), + .bitrates = legacy_ratetable, + .n_bitrates = ARRAY_SIZE(legacy_ratetable), + .ht_cap = { + /* from include/linux/ieee80211.h */ + .cap = IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, + .ampdu_density = AMPDU_DEF_MPDU_DENSITY, + .mcs = { + /* placeholders for now */ + .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}, + .rx_highest = cpu_to_le16(500), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED} + } +}; + +static const struct ieee80211_supported_band brcms_band_5GHz_nphy_template = { + .band = IEEE80211_BAND_5GHZ, + .channels = brcms_5ghz_nphy_chantable, + .n_channels = ARRAY_SIZE(brcms_5ghz_nphy_chantable), + .bitrates = legacy_ratetable + BRCMS_LEGACY_5G_RATE_OFFSET, + .n_bitrates = ARRAY_SIZE(legacy_ratetable) - + BRCMS_LEGACY_5G_RATE_OFFSET, + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, + .ampdu_density = AMPDU_DEF_MPDU_DENSITY, + .mcs = { + /* placeholders for now */ + .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}, + .rx_highest = cpu_to_le16(500), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED} + } +}; + +/* flags the given rate in rateset as requested */ +static void brcms_set_basic_rate(struct brcm_rateset *rs, u16 rate, bool is_br) +{ + u32 i; + + for (i = 0; i < rs->count; i++) { + if (rate != (rs->rates[i] & 0x7f)) + continue; + + if (is_br) + rs->rates[i] |= BRCMS_RATE_FLAG; + else + rs->rates[i] &= BRCMS_RATE_MASK; + return; + } +} + +/** + * This function frees the WL per-device resources. + * + * This function frees resources owned by the WL device pointed to + * by the wl parameter. + * + * precondition: can both be called locked and unlocked + * + */ +static void brcms_free(struct brcms_info *wl) +{ + struct brcms_timer *t, *next; + + /* free ucode data */ + if (wl->fw.fw_cnt) + brcms_ucode_data_free(&wl->ucode); + if (wl->irq) + free_irq(wl->irq, wl); + + /* kill dpc */ + tasklet_kill(&wl->tasklet); + + if (wl->pub) { + brcms_debugfs_detach(wl->pub); + brcms_c_module_unregister(wl->pub, "linux", wl); + } + + /* free common resources */ + if (wl->wlc) { + brcms_c_detach(wl->wlc); + wl->wlc = NULL; + wl->pub = NULL; + } + + /* virtual interface deletion is deferred so we cannot spinwait */ + + /* wait for all pending callbacks to complete */ + while (atomic_read(&wl->callbacks) > 0) + schedule(); + + /* free timers */ + for (t = wl->timers; t; t = next) { + next = t->next; +#ifdef DEBUG + kfree(t->name); +#endif + kfree(t); + } +} + +/* +* called from both kernel as from this kernel module (error flow on attach) +* precondition: perimeter lock is not acquired. +*/ +static void brcms_remove(struct bcma_device *pdev) +{ + struct ieee80211_hw *hw = bcma_get_drvdata(pdev); + struct brcms_info *wl = hw->priv; + + if (wl->wlc) { + brcms_led_unregister(wl); + wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false); + wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); + ieee80211_unregister_hw(hw); + } + + brcms_free(wl); + + bcma_set_drvdata(pdev, NULL); + ieee80211_free_hw(hw); +} + +/* + * Precondition: Since this function is called in brcms_pci_probe() context, + * no locking is required. + */ +static void brcms_release_fw(struct brcms_info *wl) +{ + int i; + for (i = 0; i < MAX_FW_IMAGES; i++) { + release_firmware(wl->fw.fw_bin[i]); + release_firmware(wl->fw.fw_hdr[i]); + } +} + +/* + * Precondition: Since this function is called in brcms_pci_probe() context, + * no locking is required. + */ +static int brcms_request_fw(struct brcms_info *wl, struct bcma_device *pdev) +{ + int status; + struct device *device = &pdev->dev; + char fw_name[100]; + int i; + + memset(&wl->fw, 0, sizeof(struct brcms_firmware)); + for (i = 0; i < MAX_FW_IMAGES; i++) { + if (brcms_firmwares[i] == NULL) + break; + sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i], + UCODE_LOADER_API_VER); + status = request_firmware(&wl->fw.fw_bin[i], fw_name, device); + if (status) { + wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n", + KBUILD_MODNAME, fw_name); + return status; + } + sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i], + UCODE_LOADER_API_VER); + status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device); + if (status) { + wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n", + KBUILD_MODNAME, fw_name); + return status; + } + wl->fw.hdr_num_entries[i] = + wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr)); + } + wl->fw.fw_cnt = i; + status = brcms_ucode_data_init(wl, &wl->ucode); + brcms_release_fw(wl); + return status; +} + +static void brcms_ops_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct brcms_info *wl = hw->priv; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + + spin_lock_bh(&wl->lock); + if (!wl->pub->up) { + brcms_err(wl->wlc->hw->d11core, "ops->tx called while down\n"); + kfree_skb(skb); + goto done; + } + if (brcms_c_sendpkt_mac80211(wl->wlc, skb, hw)) + tx_info->rate_driver_data[0] = control->sta; + done: + spin_unlock_bh(&wl->lock); +} + +static int brcms_ops_start(struct ieee80211_hw *hw) +{ + struct brcms_info *wl = hw->priv; + bool blocked; + int err; + + if (!wl->ucode.bcm43xx_bomminor) { + err = brcms_request_fw(wl, wl->wlc->hw->d11core); + if (err) + return -ENOENT; + } + + ieee80211_wake_queues(hw); + spin_lock_bh(&wl->lock); + blocked = brcms_rfkill_set_hw_state(wl); + spin_unlock_bh(&wl->lock); + if (!blocked) + wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); + + spin_lock_bh(&wl->lock); + /* avoid acknowledging frames before a non-monitor device is added */ + wl->mute_tx = true; + + if (!wl->pub->up) + if (!blocked) + err = brcms_up(wl); + else + err = -ERFKILL; + else + err = -ENODEV; + spin_unlock_bh(&wl->lock); + + if (err != 0) + brcms_err(wl->wlc->hw->d11core, "%s: brcms_up() returned %d\n", + __func__, err); + + bcma_core_pci_power_save(wl->wlc->hw->d11core->bus, true); + return err; +} + +static void brcms_ops_stop(struct ieee80211_hw *hw) +{ + struct brcms_info *wl = hw->priv; + int status; + + ieee80211_stop_queues(hw); + + if (wl->wlc == NULL) + return; + + spin_lock_bh(&wl->lock); + status = brcms_c_chipmatch(wl->wlc->hw->d11core); + spin_unlock_bh(&wl->lock); + if (!status) { + brcms_err(wl->wlc->hw->d11core, + "wl: brcms_ops_stop: chipmatch failed\n"); + return; + } + + bcma_core_pci_power_save(wl->wlc->hw->d11core->bus, false); + + /* put driver in down state */ + spin_lock_bh(&wl->lock); + brcms_down(wl); + spin_unlock_bh(&wl->lock); +} + +static int +brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct brcms_info *wl = hw->priv; + + /* Just STA, AP and ADHOC for now */ + if (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC) { + brcms_err(wl->wlc->hw->d11core, + "%s: Attempt to add type %d, only STA, AP and AdHoc for now\n", + __func__, vif->type); + return -EOPNOTSUPP; + } + + spin_lock_bh(&wl->lock); + wl->mute_tx = false; + brcms_c_mute(wl->wlc, false); + if (vif->type == NL80211_IFTYPE_STATION) + brcms_c_start_station(wl->wlc, vif->addr); + else if (vif->type == NL80211_IFTYPE_AP) + brcms_c_start_ap(wl->wlc, vif->addr, vif->bss_conf.bssid, + vif->bss_conf.ssid, vif->bss_conf.ssid_len); + else if (vif->type == NL80211_IFTYPE_ADHOC) + brcms_c_start_adhoc(wl->wlc, vif->addr); + spin_unlock_bh(&wl->lock); + + return 0; +} + +static void +brcms_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ +} + +static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ieee80211_conf *conf = &hw->conf; + struct brcms_info *wl = hw->priv; + struct bcma_device *core = wl->wlc->hw->d11core; + int err = 0; + int new_int; + + spin_lock_bh(&wl->lock); + if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { + brcms_c_set_beacon_listen_interval(wl->wlc, + conf->listen_interval); + } + if (changed & IEEE80211_CONF_CHANGE_MONITOR) + brcms_dbg_info(core, "%s: change monitor mode: %s\n", + __func__, conf->flags & IEEE80211_CONF_MONITOR ? + "true" : "false"); + if (changed & IEEE80211_CONF_CHANGE_PS) + brcms_err(core, "%s: change power-save mode: %s (implement)\n", + __func__, conf->flags & IEEE80211_CONF_PS ? + "true" : "false"); + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + err = brcms_c_set_tx_power(wl->wlc, conf->power_level); + if (err < 0) { + brcms_err(core, "%s: Error setting power_level\n", + __func__); + goto config_out; + } + new_int = brcms_c_get_tx_power(wl->wlc); + if (new_int != conf->power_level) + brcms_err(core, + "%s: Power level req != actual, %d %d\n", + __func__, conf->power_level, + new_int); + } + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + if (conf->chandef.width == NL80211_CHAN_WIDTH_20 || + conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) + err = brcms_c_set_channel(wl->wlc, + conf->chandef.chan->hw_value); + else + err = -ENOTSUPP; + } + if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + err = brcms_c_set_rate_limit(wl->wlc, + conf->short_frame_max_tx_count, + conf->long_frame_max_tx_count); + + config_out: + spin_unlock_bh(&wl->lock); + return err; +} + +static void +brcms_ops_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + struct brcms_info *wl = hw->priv; + struct bcma_device *core = wl->wlc->hw->d11core; + + if (changed & BSS_CHANGED_ASSOC) { + /* association status changed (associated/disassociated) + * also implies a change in the AID. + */ + brcms_err(core, "%s: %s: %sassociated\n", KBUILD_MODNAME, + __func__, info->assoc ? "" : "dis"); + spin_lock_bh(&wl->lock); + brcms_c_associate_upd(wl->wlc, info->assoc); + spin_unlock_bh(&wl->lock); + } + if (changed & BSS_CHANGED_ERP_SLOT) { + s8 val; + + /* slot timing changed */ + if (info->use_short_slot) + val = 1; + else + val = 0; + spin_lock_bh(&wl->lock); + brcms_c_set_shortslot_override(wl->wlc, val); + spin_unlock_bh(&wl->lock); + } + + if (changed & BSS_CHANGED_HT) { + /* 802.11n parameters changed */ + u16 mode = info->ht_operation_mode; + + spin_lock_bh(&wl->lock); + brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_CFG, + mode & IEEE80211_HT_OP_MODE_PROTECTION); + brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_NONGF, + mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_OBSS, + mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT); + spin_unlock_bh(&wl->lock); + } + if (changed & BSS_CHANGED_BASIC_RATES) { + struct ieee80211_supported_band *bi; + u32 br_mask, i; + u16 rate; + struct brcm_rateset rs; + int error; + + /* retrieve the current rates */ + spin_lock_bh(&wl->lock); + brcms_c_get_current_rateset(wl->wlc, &rs); + spin_unlock_bh(&wl->lock); + + br_mask = info->basic_rates; + bi = hw->wiphy->bands[brcms_c_get_curband(wl->wlc)]; + for (i = 0; i < bi->n_bitrates; i++) { + /* convert to internal rate value */ + rate = (bi->bitrates[i].bitrate << 1) / 10; + + /* set/clear basic rate flag */ + brcms_set_basic_rate(&rs, rate, br_mask & 1); + br_mask >>= 1; + } + + /* update the rate set */ + spin_lock_bh(&wl->lock); + error = brcms_c_set_rateset(wl->wlc, &rs); + spin_unlock_bh(&wl->lock); + if (error) + brcms_err(core, "changing basic rates failed: %d\n", + error); + } + if (changed & BSS_CHANGED_BEACON_INT) { + /* Beacon interval changed */ + spin_lock_bh(&wl->lock); + brcms_c_set_beacon_period(wl->wlc, info->beacon_int); + spin_unlock_bh(&wl->lock); + } + if (changed & BSS_CHANGED_BSSID) { + /* BSSID changed, for whatever reason (IBSS and managed mode) */ + spin_lock_bh(&wl->lock); + brcms_c_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET, info->bssid); + spin_unlock_bh(&wl->lock); + } + if (changed & BSS_CHANGED_SSID) { + /* BSSID changed, for whatever reason (IBSS and managed mode) */ + spin_lock_bh(&wl->lock); + brcms_c_set_ssid(wl->wlc, info->ssid, info->ssid_len); + spin_unlock_bh(&wl->lock); + } + if (changed & BSS_CHANGED_BEACON) { + /* Beacon data changed, retrieve new beacon (beaconing modes) */ + struct sk_buff *beacon; + u16 tim_offset = 0; + + spin_lock_bh(&wl->lock); + beacon = ieee80211_beacon_get_tim(hw, vif, &tim_offset, NULL); + brcms_c_set_new_beacon(wl->wlc, beacon, tim_offset, + info->dtim_period); + spin_unlock_bh(&wl->lock); + } + + if (changed & BSS_CHANGED_AP_PROBE_RESP) { + struct sk_buff *probe_resp; + + spin_lock_bh(&wl->lock); + probe_resp = ieee80211_proberesp_get(hw, vif); + brcms_c_set_new_probe_resp(wl->wlc, probe_resp); + spin_unlock_bh(&wl->lock); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + /* Beaconing should be enabled/disabled (beaconing modes) */ + brcms_err(core, "%s: Beacon enabled: %s\n", __func__, + info->enable_beacon ? "true" : "false"); + if (info->enable_beacon && + hw->wiphy->flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) { + brcms_c_enable_probe_resp(wl->wlc, true); + } else { + brcms_c_enable_probe_resp(wl->wlc, false); + } + } + + if (changed & BSS_CHANGED_CQM) { + /* Connection quality monitor config changed */ + brcms_err(core, "%s: cqm change: threshold %d, hys %d " + " (implement)\n", __func__, info->cqm_rssi_thold, + info->cqm_rssi_hyst); + } + + if (changed & BSS_CHANGED_IBSS) { + /* IBSS join status changed */ + brcms_err(core, "%s: IBSS joined: %s (implement)\n", + __func__, info->ibss_joined ? "true" : "false"); + } + + if (changed & BSS_CHANGED_ARP_FILTER) { + /* Hardware ARP filter address list or state changed */ + brcms_err(core, "%s: arp filtering: %d addresses" + " (implement)\n", __func__, info->arp_addr_cnt); + } + + if (changed & BSS_CHANGED_QOS) { + /* + * QoS for this association was enabled/disabled. + * Note that it is only ever disabled for station mode. + */ + brcms_err(core, "%s: qos enabled: %s (implement)\n", + __func__, info->qos ? "true" : "false"); + } + return; +} + +static void +brcms_ops_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct brcms_info *wl = hw->priv; + struct bcma_device *core = wl->wlc->hw->d11core; + + changed_flags &= MAC_FILTERS; + *total_flags &= MAC_FILTERS; + + if (changed_flags & FIF_ALLMULTI) + brcms_dbg_info(core, "FIF_ALLMULTI\n"); + if (changed_flags & FIF_FCSFAIL) + brcms_dbg_info(core, "FIF_FCSFAIL\n"); + if (changed_flags & FIF_CONTROL) + brcms_dbg_info(core, "FIF_CONTROL\n"); + if (changed_flags & FIF_OTHER_BSS) + brcms_dbg_info(core, "FIF_OTHER_BSS\n"); + if (changed_flags & FIF_PSPOLL) + brcms_dbg_info(core, "FIF_PSPOLL\n"); + if (changed_flags & FIF_BCN_PRBRESP_PROMISC) + brcms_dbg_info(core, "FIF_BCN_PRBRESP_PROMISC\n"); + + spin_lock_bh(&wl->lock); + brcms_c_mac_promisc(wl->wlc, *total_flags); + spin_unlock_bh(&wl->lock); + return; +} + +static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) +{ + struct brcms_info *wl = hw->priv; + spin_lock_bh(&wl->lock); + brcms_c_scan_start(wl->wlc); + spin_unlock_bh(&wl->lock); + return; +} + +static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct brcms_info *wl = hw->priv; + spin_lock_bh(&wl->lock); + brcms_c_scan_stop(wl->wlc); + spin_unlock_bh(&wl->lock); + return; +} + +static int +brcms_ops_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct brcms_info *wl = hw->priv; + + spin_lock_bh(&wl->lock); + brcms_c_wme_setparams(wl->wlc, queue, params, true); + spin_unlock_bh(&wl->lock); + + return 0; +} + +static int +brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct brcms_info *wl = hw->priv; + struct scb *scb = &wl->wlc->pri_scb; + + brcms_c_init_scb(scb); + + wl->pub->global_ampdu = &(scb->scb_ampdu); + wl->pub->global_ampdu->scb = scb; + wl->pub->global_ampdu->max_pdu = 16; + + /* + * minstrel_ht initiates addBA on our behalf by calling + * ieee80211_start_tx_ba_session() + */ + return 0; +} + +static int +brcms_ops_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size, bool amsdu) +{ + struct brcms_info *wl = hw->priv; + struct scb *scb = &wl->wlc->pri_scb; + int status; + + if (WARN_ON(scb->magic != SCB_MAGIC)) + return -EIDRM; + switch (action) { + case IEEE80211_AMPDU_RX_START: + break; + case IEEE80211_AMPDU_RX_STOP: + break; + case IEEE80211_AMPDU_TX_START: + spin_lock_bh(&wl->lock); + status = brcms_c_aggregatable(wl->wlc, tid); + spin_unlock_bh(&wl->lock); + if (!status) { + brcms_err(wl->wlc->hw->d11core, + "START: tid %d is not agg\'able\n", tid); + return -EINVAL; + } + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + spin_lock_bh(&wl->lock); + brcms_c_ampdu_flush(wl->wlc, sta, tid); + spin_unlock_bh(&wl->lock); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + /* + * BA window size from ADDBA response ('buf_size') defines how + * many outstanding MPDUs are allowed for the BA stream by + * recipient and traffic class. 'ampdu_factor' gives maximum + * AMPDU size. + */ + spin_lock_bh(&wl->lock); + brcms_c_ampdu_tx_operational(wl->wlc, tid, buf_size, + (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + + sta->ht_cap.ampdu_factor)) - 1); + spin_unlock_bh(&wl->lock); + /* Power save wakeup */ + break; + default: + brcms_err(wl->wlc->hw->d11core, + "%s: Invalid command, ignoring\n", __func__); + } + + return 0; +} + +static void brcms_ops_rfkill_poll(struct ieee80211_hw *hw) +{ + struct brcms_info *wl = hw->priv; + bool blocked; + + spin_lock_bh(&wl->lock); + blocked = brcms_c_check_radio_disabled(wl->wlc); + spin_unlock_bh(&wl->lock); + + wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked); +} + +static bool brcms_tx_flush_completed(struct brcms_info *wl) +{ + bool result; + + spin_lock_bh(&wl->lock); + result = brcms_c_tx_flush_completed(wl->wlc); + spin_unlock_bh(&wl->lock); + return result; +} + +static void brcms_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct brcms_info *wl = hw->priv; + int ret; + + no_printk("%s: drop = %s\n", __func__, drop ? "true" : "false"); + + ret = wait_event_timeout(wl->tx_flush_wq, + brcms_tx_flush_completed(wl), + msecs_to_jiffies(BRCMS_FLUSH_TIMEOUT)); + + brcms_dbg_mac80211(wl->wlc->hw->d11core, + "ret=%d\n", jiffies_to_msecs(ret)); +} + +static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct brcms_info *wl = hw->priv; + u64 tsf; + + spin_lock_bh(&wl->lock); + tsf = brcms_c_tsf_get(wl->wlc); + spin_unlock_bh(&wl->lock); + + return tsf; +} + +static void brcms_ops_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u64 tsf) +{ + struct brcms_info *wl = hw->priv; + + spin_lock_bh(&wl->lock); + brcms_c_tsf_set(wl->wlc, tsf); + spin_unlock_bh(&wl->lock); +} + +static const struct ieee80211_ops brcms_ops = { + .tx = brcms_ops_tx, + .start = brcms_ops_start, + .stop = brcms_ops_stop, + .add_interface = brcms_ops_add_interface, + .remove_interface = brcms_ops_remove_interface, + .config = brcms_ops_config, + .bss_info_changed = brcms_ops_bss_info_changed, + .configure_filter = brcms_ops_configure_filter, + .sw_scan_start = brcms_ops_sw_scan_start, + .sw_scan_complete = brcms_ops_sw_scan_complete, + .conf_tx = brcms_ops_conf_tx, + .sta_add = brcms_ops_sta_add, + .ampdu_action = brcms_ops_ampdu_action, + .rfkill_poll = brcms_ops_rfkill_poll, + .flush = brcms_ops_flush, + .get_tsf = brcms_ops_get_tsf, + .set_tsf = brcms_ops_set_tsf, +}; + +void brcms_dpc(unsigned long data) +{ + struct brcms_info *wl; + + wl = (struct brcms_info *) data; + + spin_lock_bh(&wl->lock); + + /* call the common second level interrupt handler */ + if (wl->pub->up) { + if (wl->resched) { + unsigned long flags; + + spin_lock_irqsave(&wl->isr_lock, flags); + brcms_c_intrsupd(wl->wlc); + spin_unlock_irqrestore(&wl->isr_lock, flags); + } + + wl->resched = brcms_c_dpc(wl->wlc, true); + } + + /* brcms_c_dpc() may bring the driver down */ + if (!wl->pub->up) + goto done; + + /* re-schedule dpc */ + if (wl->resched) + tasklet_schedule(&wl->tasklet); + else + /* re-enable interrupts */ + brcms_intrson(wl); + + done: + spin_unlock_bh(&wl->lock); + wake_up(&wl->tx_flush_wq); +} + +static irqreturn_t brcms_isr(int irq, void *dev_id) +{ + struct brcms_info *wl; + irqreturn_t ret = IRQ_NONE; + + wl = (struct brcms_info *) dev_id; + + spin_lock(&wl->isr_lock); + + /* call common first level interrupt handler */ + if (brcms_c_isr(wl->wlc)) { + /* schedule second level handler */ + tasklet_schedule(&wl->tasklet); + ret = IRQ_HANDLED; + } + + spin_unlock(&wl->isr_lock); + + return ret; +} + +/* + * is called in brcms_pci_probe() context, therefore no locking required. + */ +static int ieee_hw_rate_init(struct ieee80211_hw *hw) +{ + struct brcms_info *wl = hw->priv; + struct brcms_c_info *wlc = wl->wlc; + struct ieee80211_supported_band *band; + int has_5g = 0; + u16 phy_type; + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; + + phy_type = brcms_c_get_phy_type(wl->wlc, 0); + if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) { + band = &wlc->bandstate[BAND_2G_INDEX]->band; + *band = brcms_band_2GHz_nphy_template; + if (phy_type == PHY_TYPE_LCN) { + /* Single stream */ + band->ht_cap.mcs.rx_mask[1] = 0; + band->ht_cap.mcs.rx_highest = cpu_to_le16(72); + } + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band; + } else { + return -EPERM; + } + + /* Assume all bands use the same phy. True for 11n devices. */ + if (wl->pub->_nbands > 1) { + has_5g++; + if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) { + band = &wlc->bandstate[BAND_5G_INDEX]->band; + *band = brcms_band_5GHz_nphy_template; + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band; + } else { + return -EPERM; + } + } + return 0; +} + +/* + * is called in brcms_pci_probe() context, therefore no locking required. + */ +static int ieee_hw_init(struct ieee80211_hw *hw) +{ + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + + hw->extra_tx_headroom = brcms_c_get_header_len(); + hw->queues = N_TX_QUEUES; + hw->max_rates = 2; /* Primary rate and 1 fallback rate */ + + /* channel change time is dependent on chip and band */ + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_ADHOC); + + /* + * deactivate sending probe responses by ucude, because this will + * cause problems when WPS is used. + * + * hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + */ + + hw->rate_control_algorithm = "minstrel_ht"; + + hw->sta_data_size = 0; + return ieee_hw_rate_init(hw); +} + +/** + * attach to the WL device. + * + * Attach to the WL device identified by vendor and device parameters. + * regs is a host accessible memory address pointing to WL device registers. + * + * is called in brcms_bcma_probe() context, therefore no locking required. + */ +static struct brcms_info *brcms_attach(struct bcma_device *pdev) +{ + struct brcms_info *wl = NULL; + int unit, err; + struct ieee80211_hw *hw; + u8 perm[ETH_ALEN]; + + unit = n_adapters_found; + err = 0; + + if (unit < 0) + return NULL; + + /* allocate private info */ + hw = bcma_get_drvdata(pdev); + if (hw != NULL) + wl = hw->priv; + if (WARN_ON(hw == NULL) || WARN_ON(wl == NULL)) + return NULL; + wl->wiphy = hw->wiphy; + + atomic_set(&wl->callbacks, 0); + + init_waitqueue_head(&wl->tx_flush_wq); + + /* setup the bottom half handler */ + tasklet_init(&wl->tasklet, brcms_dpc, (unsigned long) wl); + + spin_lock_init(&wl->lock); + spin_lock_init(&wl->isr_lock); + + /* common load-time initialization */ + wl->wlc = brcms_c_attach((void *)wl, pdev, unit, false, &err); + if (!wl->wlc) { + wiphy_err(wl->wiphy, "%s: attach() failed with code %d\n", + KBUILD_MODNAME, err); + goto fail; + } + wl->pub = brcms_c_pub(wl->wlc); + + wl->pub->ieee_hw = hw; + + /* register our interrupt handler */ + if (request_irq(pdev->irq, brcms_isr, + IRQF_SHARED, KBUILD_MODNAME, wl)) { + wiphy_err(wl->wiphy, "wl%d: request_irq() failed\n", unit); + goto fail; + } + wl->irq = pdev->irq; + + /* register module */ + brcms_c_module_register(wl->pub, "linux", wl, NULL); + + if (ieee_hw_init(hw)) { + wiphy_err(wl->wiphy, "wl%d: %s: ieee_hw_init failed!\n", unit, + __func__); + goto fail; + } + + brcms_c_regd_init(wl->wlc); + + memcpy(perm, &wl->pub->cur_etheraddr, ETH_ALEN); + if (WARN_ON(!is_valid_ether_addr(perm))) + goto fail; + SET_IEEE80211_PERM_ADDR(hw, perm); + + err = ieee80211_register_hw(hw); + if (err) + wiphy_err(wl->wiphy, "%s: ieee80211_register_hw failed, status" + "%d\n", __func__, err); + + if (wl->pub->srom_ccode[0] && + regulatory_hint(wl->wiphy, wl->pub->srom_ccode)) + wiphy_err(wl->wiphy, "%s: regulatory hint failed\n", __func__); + + brcms_debugfs_attach(wl->pub); + brcms_debugfs_create_files(wl->pub); + n_adapters_found++; + return wl; + +fail: + brcms_free(wl); + return NULL; +} + + + +/** + * determines if a device is a WL device, and if so, attaches it. + * + * This function determines if a device pointed to by pdev is a WL device, + * and if so, performs a brcms_attach() on it. + * + * Perimeter lock is initialized in the course of this function. + */ +static int brcms_bcma_probe(struct bcma_device *pdev) +{ + struct brcms_info *wl; + struct ieee80211_hw *hw; + + dev_info(&pdev->dev, "mfg %x core %x rev %d class %d irq %d\n", + pdev->id.manuf, pdev->id.id, pdev->id.rev, pdev->id.class, + pdev->irq); + + if ((pdev->id.manuf != BCMA_MANUF_BCM) || + (pdev->id.id != BCMA_CORE_80211)) + return -ENODEV; + + hw = ieee80211_alloc_hw(sizeof(struct brcms_info), &brcms_ops); + if (!hw) { + pr_err("%s: ieee80211_alloc_hw failed\n", __func__); + return -ENOMEM; + } + + SET_IEEE80211_DEV(hw, &pdev->dev); + + bcma_set_drvdata(pdev, hw); + + memset(hw->priv, 0, sizeof(*wl)); + + wl = brcms_attach(pdev); + if (!wl) { + pr_err("%s: brcms_attach failed!\n", __func__); + return -ENODEV; + } + brcms_led_register(wl); + + return 0; +} + +static int brcms_suspend(struct bcma_device *pdev) +{ + struct brcms_info *wl; + struct ieee80211_hw *hw; + + hw = bcma_get_drvdata(pdev); + wl = hw->priv; + if (!wl) { + pr_err("%s: %s: no driver private struct!\n", KBUILD_MODNAME, + __func__); + return -ENODEV; + } + + /* only need to flag hw is down for proper resume */ + spin_lock_bh(&wl->lock); + wl->pub->hw_up = false; + spin_unlock_bh(&wl->lock); + + brcms_dbg_info(wl->wlc->hw->d11core, "brcms_suspend ok\n"); + + return 0; +} + +static int brcms_resume(struct bcma_device *pdev) +{ + return 0; +} + +static struct bcma_driver brcms_bcma_driver = { + .name = KBUILD_MODNAME, + .probe = brcms_bcma_probe, + .suspend = brcms_suspend, + .resume = brcms_resume, + .remove = brcms_remove, + .id_table = brcms_coreid_table, +}; + +/** + * This is the main entry point for the brcmsmac driver. + * + * This function is scheduled upon module initialization and + * does the driver registration, which result in brcms_bcma_probe() + * call resulting in the driver bringup. + */ +static void brcms_driver_init(struct work_struct *work) +{ + int error; + + error = bcma_driver_register(&brcms_bcma_driver); + if (error) + pr_err("%s: register returned %d\n", __func__, error); +} + +static DECLARE_WORK(brcms_driver_work, brcms_driver_init); + +static int __init brcms_module_init(void) +{ + brcms_debugfs_init(); + if (!schedule_work(&brcms_driver_work)) + return -EBUSY; + + return 0; +} + +/** + * This function unloads the brcmsmac driver from the system. + * + * This function unconditionally unloads the brcmsmac driver module from the + * system. + * + */ +static void __exit brcms_module_exit(void) +{ + cancel_work_sync(&brcms_driver_work); + bcma_driver_unregister(&brcms_bcma_driver); + brcms_debugfs_exit(); +} + +module_init(brcms_module_init); +module_exit(brcms_module_exit); + +/* + * precondition: perimeter lock has been acquired + */ +void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, + bool state, int prio) +{ + brcms_err(wl->wlc->hw->d11core, "Shouldn't be here %s\n", __func__); +} + +/* + * precondition: perimeter lock has been acquired + */ +void brcms_init(struct brcms_info *wl) +{ + brcms_dbg_info(wl->wlc->hw->d11core, "Initializing wl%d\n", + wl->pub->unit); + brcms_reset(wl); + brcms_c_init(wl->wlc, wl->mute_tx); +} + +/* + * precondition: perimeter lock has been acquired + */ +uint brcms_reset(struct brcms_info *wl) +{ + brcms_dbg_info(wl->wlc->hw->d11core, "Resetting wl%d\n", wl->pub->unit); + brcms_c_reset(wl->wlc); + + /* dpc will not be rescheduled */ + wl->resched = false; + + /* inform publicly that interface is down */ + wl->pub->up = false; + + return 0; +} + +void brcms_fatal_error(struct brcms_info *wl) +{ + brcms_err(wl->wlc->hw->d11core, "wl%d: fatal error, reinitializing\n", + wl->wlc->pub->unit); + brcms_reset(wl); + ieee80211_restart_hw(wl->pub->ieee_hw); +} + +/* + * These are interrupt on/off entry points. Disable interrupts + * during interrupt state transition. + */ +void brcms_intrson(struct brcms_info *wl) +{ + unsigned long flags; + + spin_lock_irqsave(&wl->isr_lock, flags); + brcms_c_intrson(wl->wlc); + spin_unlock_irqrestore(&wl->isr_lock, flags); +} + +u32 brcms_intrsoff(struct brcms_info *wl) +{ + unsigned long flags; + u32 status; + + spin_lock_irqsave(&wl->isr_lock, flags); + status = brcms_c_intrsoff(wl->wlc); + spin_unlock_irqrestore(&wl->isr_lock, flags); + return status; +} + +void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask) +{ + unsigned long flags; + + spin_lock_irqsave(&wl->isr_lock, flags); + brcms_c_intrsrestore(wl->wlc, macintmask); + spin_unlock_irqrestore(&wl->isr_lock, flags); +} + +/* + * precondition: perimeter lock has been acquired + */ +int brcms_up(struct brcms_info *wl) +{ + int error = 0; + + if (wl->pub->up) + return 0; + + error = brcms_c_up(wl->wlc); + + return error; +} + +/* + * precondition: perimeter lock has been acquired + */ +void brcms_down(struct brcms_info *wl) +{ + uint callbacks, ret_val = 0; + + /* call common down function */ + ret_val = brcms_c_down(wl->wlc); + callbacks = atomic_read(&wl->callbacks) - ret_val; + + /* wait for down callbacks to complete */ + spin_unlock_bh(&wl->lock); + + /* For HIGH_only driver, it's important to actually schedule other work, + * not just spin wait since everything runs at schedule level + */ + SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000); + + spin_lock_bh(&wl->lock); +} + +/* +* precondition: perimeter lock is not acquired + */ +static void _brcms_timer(struct work_struct *work) +{ + struct brcms_timer *t = container_of(work, struct brcms_timer, + dly_wrk.work); + + spin_lock_bh(&t->wl->lock); + + if (t->set) { + if (t->periodic) { + atomic_inc(&t->wl->callbacks); + ieee80211_queue_delayed_work(t->wl->pub->ieee_hw, + &t->dly_wrk, + msecs_to_jiffies(t->ms)); + } else { + t->set = false; + } + + t->fn(t->arg); + } + + atomic_dec(&t->wl->callbacks); + + spin_unlock_bh(&t->wl->lock); +} + +/* + * Adds a timer to the list. Caller supplies a timer function. + * Is called from wlc. + * + * precondition: perimeter lock has been acquired + */ +struct brcms_timer *brcms_init_timer(struct brcms_info *wl, + void (*fn) (void *arg), + void *arg, const char *name) +{ + struct brcms_timer *t; + + t = kzalloc(sizeof(struct brcms_timer), GFP_ATOMIC); + if (!t) + return NULL; + + INIT_DELAYED_WORK(&t->dly_wrk, _brcms_timer); + t->wl = wl; + t->fn = fn; + t->arg = arg; + t->next = wl->timers; + wl->timers = t; + +#ifdef DEBUG + t->name = kstrdup(name, GFP_ATOMIC); +#endif + + return t; +} + +/* + * adds only the kernel timer since it's going to be more accurate + * as well as it's easier to make it periodic + * + * precondition: perimeter lock has been acquired + */ +void brcms_add_timer(struct brcms_timer *t, uint ms, int periodic) +{ + struct ieee80211_hw *hw = t->wl->pub->ieee_hw; + +#ifdef DEBUG + if (t->set) + brcms_dbg_info(t->wl->wlc->hw->d11core, + "%s: Already set. Name: %s, per %d\n", + __func__, t->name, periodic); +#endif + t->ms = ms; + t->periodic = (bool) periodic; + if (!t->set) { + t->set = true; + atomic_inc(&t->wl->callbacks); + } + + ieee80211_queue_delayed_work(hw, &t->dly_wrk, msecs_to_jiffies(ms)); +} + +/* + * return true if timer successfully deleted, false if still pending + * + * precondition: perimeter lock has been acquired + */ +bool brcms_del_timer(struct brcms_timer *t) +{ + if (t->set) { + t->set = false; + if (!cancel_delayed_work(&t->dly_wrk)) + return false; + + atomic_dec(&t->wl->callbacks); + } + + return true; +} + +/* + * precondition: perimeter lock has been acquired + */ +void brcms_free_timer(struct brcms_timer *t) +{ + struct brcms_info *wl = t->wl; + struct brcms_timer *tmp; + + /* delete the timer in case it is active */ + brcms_del_timer(t); + + if (wl->timers == t) { + wl->timers = wl->timers->next; +#ifdef DEBUG + kfree(t->name); +#endif + kfree(t); + return; + + } + + tmp = wl->timers; + while (tmp) { + if (tmp->next == t) { + tmp->next = t->next; +#ifdef DEBUG + kfree(t->name); +#endif + kfree(t); + return; + } + tmp = tmp->next; + } + +} + +/* + * precondition: perimeter lock has been acquired + */ +int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx) +{ + int i, entry; + const u8 *pdata; + struct firmware_hdr *hdr; + for (i = 0; i < wl->fw.fw_cnt; i++) { + hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data; + for (entry = 0; entry < wl->fw.hdr_num_entries[i]; + entry++, hdr++) { + u32 len = le32_to_cpu(hdr->len); + if (le32_to_cpu(hdr->idx) == idx) { + pdata = wl->fw.fw_bin[i]->data + + le32_to_cpu(hdr->offset); + *pbuf = kmemdup(pdata, len, GFP_ATOMIC); + if (*pbuf == NULL) + goto fail; + + return 0; + } + } + } + brcms_err(wl->wlc->hw->d11core, + "ERROR: ucode buf tag:%d can not be found!\n", idx); + *pbuf = NULL; +fail: + return -ENODATA; +} + +/* + * Precondition: Since this function is called in brcms_bcma_probe() context, + * no locking is required. + */ +int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, u32 idx) +{ + int i, entry; + const u8 *pdata; + struct firmware_hdr *hdr; + for (i = 0; i < wl->fw.fw_cnt; i++) { + hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data; + for (entry = 0; entry < wl->fw.hdr_num_entries[i]; + entry++, hdr++) { + if (le32_to_cpu(hdr->idx) == idx) { + pdata = wl->fw.fw_bin[i]->data + + le32_to_cpu(hdr->offset); + if (le32_to_cpu(hdr->len) != 4) { + brcms_err(wl->wlc->hw->d11core, + "ERROR: fw hdr len\n"); + return -ENOMSG; + } + *n_bytes = le32_to_cpu(*((__le32 *) pdata)); + return 0; + } + } + } + brcms_err(wl->wlc->hw->d11core, + "ERROR: ucode tag:%d can not be found!\n", idx); + return -ENOMSG; +} + +/* + * precondition: can both be called locked and unlocked + */ +void brcms_ucode_free_buf(void *p) +{ + kfree(p); +} + +/* + * checks validity of all firmware images loaded from user space + * + * Precondition: Since this function is called in brcms_bcma_probe() context, + * no locking is required. + */ +int brcms_check_firmwares(struct brcms_info *wl) +{ + int i; + int entry; + int rc = 0; + const struct firmware *fw; + const struct firmware *fw_hdr; + struct firmware_hdr *ucode_hdr; + for (i = 0; i < MAX_FW_IMAGES && rc == 0; i++) { + fw = wl->fw.fw_bin[i]; + fw_hdr = wl->fw.fw_hdr[i]; + if (fw == NULL && fw_hdr == NULL) { + break; + } else if (fw == NULL || fw_hdr == NULL) { + wiphy_err(wl->wiphy, "%s: invalid bin/hdr fw\n", + __func__); + rc = -EBADF; + } else if (fw_hdr->size % sizeof(struct firmware_hdr)) { + wiphy_err(wl->wiphy, "%s: non integral fw hdr file " + "size %zu/%zu\n", __func__, fw_hdr->size, + sizeof(struct firmware_hdr)); + rc = -EBADF; + } else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) { + wiphy_err(wl->wiphy, "%s: out of bounds fw file size %zu\n", + __func__, fw->size); + rc = -EBADF; + } else { + /* check if ucode section overruns firmware image */ + ucode_hdr = (struct firmware_hdr *)fw_hdr->data; + for (entry = 0; entry < wl->fw.hdr_num_entries[i] && + !rc; entry++, ucode_hdr++) { + if (le32_to_cpu(ucode_hdr->offset) + + le32_to_cpu(ucode_hdr->len) > + fw->size) { + wiphy_err(wl->wiphy, + "%s: conflicting bin/hdr\n", + __func__); + rc = -EBADF; + } + } + } + } + if (rc == 0 && wl->fw.fw_cnt != i) { + wiphy_err(wl->wiphy, "%s: invalid fw_cnt=%d\n", __func__, + wl->fw.fw_cnt); + rc = -EBADF; + } + return rc; +} + +/* + * precondition: perimeter lock has been acquired + */ +bool brcms_rfkill_set_hw_state(struct brcms_info *wl) +{ + bool blocked = brcms_c_check_radio_disabled(wl->wlc); + + spin_unlock_bh(&wl->lock); + wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked); + if (blocked) + wiphy_rfkill_start_polling(wl->pub->ieee_hw->wiphy); + spin_lock_bh(&wl->lock); + return blocked; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.h new file mode 100644 index 000000000000..198053dfc310 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010 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 _BRCM_MAC80211_IF_H_ +#define _BRCM_MAC80211_IF_H_ + +#include +#include +#include +#include + +#include "ucode_loader.h" +#include "led.h" +/* + * Starting index for 5G rates in the + * legacy rate table. + */ +#define BRCMS_LEGACY_5G_RATE_OFFSET 4 + +/* softmac ioctl definitions */ +#define BRCMS_SET_SHORTSLOT_OVERRIDE 146 + +struct brcms_timer { + struct delayed_work dly_wrk; + struct brcms_info *wl; + void (*fn) (void *); /* function called upon expiration */ + void *arg; /* fixed argument provided to called function */ + uint ms; + bool periodic; + bool set; /* indicates if timer is active */ + struct brcms_timer *next; /* for freeing on unload */ +#ifdef DEBUG + char *name; /* Description of the timer */ +#endif +}; + +struct brcms_if { + uint subunit; /* WDS/BSS unit */ + struct pci_dev *pci_dev; +}; + +#define MAX_FW_IMAGES 4 +struct brcms_firmware { + u32 fw_cnt; + const struct firmware *fw_bin[MAX_FW_IMAGES]; + const struct firmware *fw_hdr[MAX_FW_IMAGES]; + u32 hdr_num_entries[MAX_FW_IMAGES]; +}; + +struct brcms_info { + struct brcms_pub *pub; /* pointer to public wlc state */ + struct brcms_c_info *wlc; /* pointer to private common data */ + u32 magic; + + int irq; + + spinlock_t lock; /* per-device perimeter lock */ + spinlock_t isr_lock; /* per-device ISR synchronization lock */ + + /* tx flush */ + wait_queue_head_t tx_flush_wq; + + /* timer related fields */ + atomic_t callbacks; /* # outstanding callback functions */ + struct brcms_timer *timers; /* timer cleanup queue */ + + struct tasklet_struct tasklet; /* dpc tasklet */ + bool resched; /* dpc needs to be and is rescheduled */ + struct brcms_firmware fw; + struct wiphy *wiphy; + struct brcms_ucode ucode; + bool mute_tx; + struct brcms_led radio_led; + struct led_classdev led_dev; +}; + +/* misc callbacks */ +void brcms_init(struct brcms_info *wl); +uint brcms_reset(struct brcms_info *wl); +void brcms_intrson(struct brcms_info *wl); +u32 brcms_intrsoff(struct brcms_info *wl); +void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask); +int brcms_up(struct brcms_info *wl); +void brcms_down(struct brcms_info *wl); +void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, + bool state, int prio); +bool brcms_rfkill_set_hw_state(struct brcms_info *wl); + +/* timer functions */ +struct brcms_timer *brcms_init_timer(struct brcms_info *wl, + void (*fn) (void *arg), void *arg, + const char *name); +void brcms_free_timer(struct brcms_timer *timer); +void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic); +bool brcms_del_timer(struct brcms_timer *timer); +void brcms_dpc(unsigned long data); +void brcms_timer(struct brcms_timer *t); +void brcms_fatal_error(struct brcms_info *wl); + +#endif /* _BRCM_MAC80211_IF_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c new file mode 100644 index 000000000000..218cbc8bf3a7 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c @@ -0,0 +1,8139 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * Copyright (c) 2013 Hauke Mehrtens + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include "rate.h" +#include "scb.h" +#include "phy/phy_hal.h" +#include "channel.h" +#include "antsel.h" +#include "stf.h" +#include "ampdu.h" +#include "mac80211_if.h" +#include "ucode_loader.h" +#include "main.h" +#include "soc.h" +#include "dma.h" +#include "debug.h" +#include "brcms_trace_events.h" + +/* watchdog timer, in unit of ms */ +#define TIMER_INTERVAL_WATCHDOG 1000 +/* radio monitor timer, in unit of ms */ +#define TIMER_INTERVAL_RADIOCHK 800 + +/* beacon interval, in unit of 1024TU */ +#define BEACON_INTERVAL_DEFAULT 100 + +/* n-mode support capability */ +/* 2x2 includes both 1x1 & 2x2 devices + * reserved #define 2 for future when we want to separate 1x1 & 2x2 and + * control it independently + */ +#define WL_11N_2x2 1 +#define WL_11N_3x3 3 +#define WL_11N_4x4 4 + +#define EDCF_ACI_MASK 0x60 +#define EDCF_ACI_SHIFT 5 +#define EDCF_ECWMIN_MASK 0x0f +#define EDCF_ECWMAX_SHIFT 4 +#define EDCF_AIFSN_MASK 0x0f +#define EDCF_AIFSN_MAX 15 +#define EDCF_ECWMAX_MASK 0xf0 + +#define EDCF_AC_BE_TXOP_STA 0x0000 +#define EDCF_AC_BK_TXOP_STA 0x0000 +#define EDCF_AC_VO_ACI_STA 0x62 +#define EDCF_AC_VO_ECW_STA 0x32 +#define EDCF_AC_VI_ACI_STA 0x42 +#define EDCF_AC_VI_ECW_STA 0x43 +#define EDCF_AC_BK_ECW_STA 0xA4 +#define EDCF_AC_VI_TXOP_STA 0x005e +#define EDCF_AC_VO_TXOP_STA 0x002f +#define EDCF_AC_BE_ACI_STA 0x03 +#define EDCF_AC_BE_ECW_STA 0xA4 +#define EDCF_AC_BK_ACI_STA 0x27 +#define EDCF_AC_VO_TXOP_AP 0x002f + +#define EDCF_TXOP2USEC(txop) ((txop) << 5) +#define EDCF_ECW2CW(exp) ((1 << (exp)) - 1) + +#define APHY_SYMBOL_TIME 4 +#define APHY_PREAMBLE_TIME 16 +#define APHY_SIGNAL_TIME 4 +#define APHY_SIFS_TIME 16 +#define APHY_SERVICE_NBITS 16 +#define APHY_TAIL_NBITS 6 +#define BPHY_SIFS_TIME 10 +#define BPHY_PLCP_SHORT_TIME 96 + +#define PREN_PREAMBLE 24 +#define PREN_MM_EXT 12 +#define PREN_PREAMBLE_EXT 4 + +#define DOT11_MAC_HDR_LEN 24 +#define DOT11_ACK_LEN 10 +#define DOT11_BA_LEN 4 +#define DOT11_OFDM_SIGNAL_EXTENSION 6 +#define DOT11_MIN_FRAG_LEN 256 +#define DOT11_RTS_LEN 16 +#define DOT11_CTS_LEN 10 +#define DOT11_BA_BITMAP_LEN 128 +#define DOT11_MAXNUMFRAGS 16 +#define DOT11_MAX_FRAG_LEN 2346 + +#define BPHY_PLCP_TIME 192 +#define RIFS_11N_TIME 2 + +/* length of the BCN template area */ +#define BCN_TMPL_LEN 512 + +/* brcms_bss_info flag bit values */ +#define BRCMS_BSS_HT 0x0020 /* BSS is HT (MIMO) capable */ + +/* chip rx buffer offset */ +#define BRCMS_HWRXOFF 38 + +/* rfdisable delay timer 500 ms, runs of ALP clock */ +#define RFDISABLE_DEFAULT 10000000 + +#define BRCMS_TEMPSENSE_PERIOD 10 /* 10 second timeout */ + +/* synthpu_dly times in us */ +#define SYNTHPU_DLY_APHY_US 3700 +#define SYNTHPU_DLY_BPHY_US 1050 +#define SYNTHPU_DLY_NPHY_US 2048 +#define SYNTHPU_DLY_LPPHY_US 300 + +#define ANTCNT 10 /* vanilla M_MAX_ANTCNT val */ + +/* Per-AC retry limit register definitions; uses defs.h bitfield macros */ +#define EDCF_SHORT_S 0 +#define EDCF_SFB_S 4 +#define EDCF_LONG_S 8 +#define EDCF_LFB_S 12 +#define EDCF_SHORT_M BITFIELD_MASK(4) +#define EDCF_SFB_M BITFIELD_MASK(4) +#define EDCF_LONG_M BITFIELD_MASK(4) +#define EDCF_LFB_M BITFIELD_MASK(4) + +#define RETRY_SHORT_DEF 7 /* Default Short retry Limit */ +#define RETRY_SHORT_MAX 255 /* Maximum Short retry Limit */ +#define RETRY_LONG_DEF 4 /* Default Long retry count */ +#define RETRY_SHORT_FB 3 /* Short count for fb rate */ +#define RETRY_LONG_FB 2 /* Long count for fb rate */ + +#define APHY_CWMIN 15 +#define PHY_CWMAX 1023 + +#define EDCF_AIFSN_MIN 1 + +#define FRAGNUM_MASK 0xF + +#define APHY_SLOT_TIME 9 +#define BPHY_SLOT_TIME 20 + +#define WL_SPURAVOID_OFF 0 +#define WL_SPURAVOID_ON1 1 +#define WL_SPURAVOID_ON2 2 + +/* invalid core flags, use the saved coreflags */ +#define BRCMS_USE_COREFLAGS 0xffffffff + +/* values for PLCPHdr_override */ +#define BRCMS_PLCP_AUTO -1 +#define BRCMS_PLCP_SHORT 0 +#define BRCMS_PLCP_LONG 1 + +/* values for g_protection_override and n_protection_override */ +#define BRCMS_PROTECTION_AUTO -1 +#define BRCMS_PROTECTION_OFF 0 +#define BRCMS_PROTECTION_ON 1 +#define BRCMS_PROTECTION_MMHDR_ONLY 2 +#define BRCMS_PROTECTION_CTS_ONLY 3 + +/* values for g_protection_control and n_protection_control */ +#define BRCMS_PROTECTION_CTL_OFF 0 +#define BRCMS_PROTECTION_CTL_LOCAL 1 +#define BRCMS_PROTECTION_CTL_OVERLAP 2 + +/* values for n_protection */ +#define BRCMS_N_PROTECTION_OFF 0 +#define BRCMS_N_PROTECTION_OPTIONAL 1 +#define BRCMS_N_PROTECTION_20IN40 2 +#define BRCMS_N_PROTECTION_MIXEDMODE 3 + +/* values for band specific 40MHz capabilities */ +#define BRCMS_N_BW_20ALL 0 +#define BRCMS_N_BW_40ALL 1 +#define BRCMS_N_BW_20IN2G_40IN5G 2 + +/* bitflags for SGI support (sgi_rx iovar) */ +#define BRCMS_N_SGI_20 0x01 +#define BRCMS_N_SGI_40 0x02 + +/* defines used by the nrate iovar */ +/* MSC in use,indicates b0-6 holds an mcs */ +#define NRATE_MCS_INUSE 0x00000080 +/* rate/mcs value */ +#define NRATE_RATE_MASK 0x0000007f +/* stf mode mask: siso, cdd, stbc, sdm */ +#define NRATE_STF_MASK 0x0000ff00 +/* stf mode shift */ +#define NRATE_STF_SHIFT 8 +/* bit indicate to override mcs only */ +#define NRATE_OVERRIDE_MCS_ONLY 0x40000000 +#define NRATE_SGI_MASK 0x00800000 /* sgi mode */ +#define NRATE_SGI_SHIFT 23 /* sgi mode */ +#define NRATE_LDPC_CODING 0x00400000 /* adv coding in use */ +#define NRATE_LDPC_SHIFT 22 /* ldpc shift */ + +#define NRATE_STF_SISO 0 /* stf mode SISO */ +#define NRATE_STF_CDD 1 /* stf mode CDD */ +#define NRATE_STF_STBC 2 /* stf mode STBC */ +#define NRATE_STF_SDM 3 /* stf mode SDM */ + +#define MAX_DMA_SEGS 4 + +/* # of entries in Tx FIFO */ +#define NTXD 64 +/* Max # of entries in Rx FIFO based on 4kb page size */ +#define NRXD 256 + +/* Amount of headroom to leave in Tx FIFO */ +#define TX_HEADROOM 4 + +/* try to keep this # rbufs posted to the chip */ +#define NRXBUFPOST 32 + +/* max # frames to process in brcms_c_recv() */ +#define RXBND 8 +/* max # tx status to process in wlc_txstatus() */ +#define TXSBND 8 + +/* brcmu_format_flags() bit description structure */ +struct brcms_c_bit_desc { + u32 bit; + const char *name; +}; + +/* + * The following table lists the buffer memory allocated to xmt fifos in HW. + * the size is in units of 256bytes(one block), total size is HW dependent + * ucode has default fifo partition, sw can overwrite if necessary + * + * This is documented in twiki under the topic UcodeTxFifo. Please ensure + * the twiki is updated before making changes. + */ + +/* Starting corerev for the fifo size table */ +#define XMTFIFOTBL_STARTREV 17 + +struct d11init { + __le16 addr; + __le16 size; + __le32 value; +}; + +struct edcf_acparam { + u8 ACI; + u8 ECW; + u16 TXOP; +} __packed; + +/* debug/trace */ +uint brcm_msg_level; + +/* TX FIFO number to WME/802.1E Access Category */ +static const u8 wme_fifo2ac[] = { + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_BE, + IEEE80211_AC_BE +}; + +/* ieee80211 Access Category to TX FIFO number */ +static const u8 wme_ac2fifo[] = { + TX_AC_VO_FIFO, + TX_AC_VI_FIFO, + TX_AC_BE_FIFO, + TX_AC_BK_FIFO +}; + +static const u16 xmtfifo_sz[][NFIFO] = { + /* corerev 17: 5120, 49152, 49152, 5376, 4352, 1280 */ + {20, 192, 192, 21, 17, 5}, + /* corerev 18: */ + {0, 0, 0, 0, 0, 0}, + /* corerev 19: */ + {0, 0, 0, 0, 0, 0}, + /* corerev 20: 5120, 49152, 49152, 5376, 4352, 1280 */ + {20, 192, 192, 21, 17, 5}, + /* corerev 21: 2304, 14848, 5632, 3584, 3584, 1280 */ + {9, 58, 22, 14, 14, 5}, + /* corerev 22: 5120, 49152, 49152, 5376, 4352, 1280 */ + {20, 192, 192, 21, 17, 5}, + /* corerev 23: 5120, 49152, 49152, 5376, 4352, 1280 */ + {20, 192, 192, 21, 17, 5}, + /* corerev 24: 2304, 14848, 5632, 3584, 3584, 1280 */ + {9, 58, 22, 14, 14, 5}, + /* corerev 25: */ + {0, 0, 0, 0, 0, 0}, + /* corerev 26: */ + {0, 0, 0, 0, 0, 0}, + /* corerev 27: */ + {0, 0, 0, 0, 0, 0}, + /* corerev 28: 2304, 14848, 5632, 3584, 3584, 1280 */ + {9, 58, 22, 14, 14, 5}, +}; + +#ifdef DEBUG +static const char * const fifo_names[] = { + "AC_BK", "AC_BE", "AC_VI", "AC_VO", "BCMC", "ATIM" }; +#else +static const char fifo_names[6][1]; +#endif + +#ifdef DEBUG +/* pointer to most recently allocated wl/wlc */ +static struct brcms_c_info *wlc_info_dbg = (struct brcms_c_info *) (NULL); +#endif + +/* Mapping of ieee80211 AC numbers to tx fifos */ +static const u8 ac_to_fifo_mapping[IEEE80211_NUM_ACS] = { + [IEEE80211_AC_VO] = TX_AC_VO_FIFO, + [IEEE80211_AC_VI] = TX_AC_VI_FIFO, + [IEEE80211_AC_BE] = TX_AC_BE_FIFO, + [IEEE80211_AC_BK] = TX_AC_BK_FIFO, +}; + +/* Mapping of tx fifos to ieee80211 AC numbers */ +static const u8 fifo_to_ac_mapping[IEEE80211_NUM_ACS] = { + [TX_AC_BK_FIFO] = IEEE80211_AC_BK, + [TX_AC_BE_FIFO] = IEEE80211_AC_BE, + [TX_AC_VI_FIFO] = IEEE80211_AC_VI, + [TX_AC_VO_FIFO] = IEEE80211_AC_VO, +}; + +static u8 brcms_ac_to_fifo(u8 ac) +{ + if (ac >= ARRAY_SIZE(ac_to_fifo_mapping)) + return TX_AC_BE_FIFO; + return ac_to_fifo_mapping[ac]; +} + +static u8 brcms_fifo_to_ac(u8 fifo) +{ + if (fifo >= ARRAY_SIZE(fifo_to_ac_mapping)) + return IEEE80211_AC_BE; + return fifo_to_ac_mapping[fifo]; +} + +/* Find basic rate for a given rate */ +static u8 brcms_basic_rate(struct brcms_c_info *wlc, u32 rspec) +{ + if (is_mcs_rate(rspec)) + return wlc->band->basic_rate[mcs_table[rspec & RSPEC_RATE_MASK] + .leg_ofdm]; + return wlc->band->basic_rate[rspec & RSPEC_RATE_MASK]; +} + +static u16 frametype(u32 rspec, u8 mimoframe) +{ + if (is_mcs_rate(rspec)) + return mimoframe; + return is_cck_rate(rspec) ? FT_CCK : FT_OFDM; +} + +/* currently the best mechanism for determining SIFS is the band in use */ +static u16 get_sifs(struct brcms_band *band) +{ + return band->bandtype == BRCM_BAND_5G ? APHY_SIFS_TIME : + BPHY_SIFS_TIME; +} + +/* + * Detect Card removed. + * Even checking an sbconfig register read will not false trigger when the core + * is in reset it breaks CF address mechanism. Accessing gphy phyversion will + * cause SB error if aphy is in reset on 4306B0-DB. Need a simple accessible + * reg with fixed 0/1 pattern (some platforms return all 0). + * If clocks are present, call the sb routine which will figure out if the + * device is removed. + */ +static bool brcms_deviceremoved(struct brcms_c_info *wlc) +{ + u32 macctrl; + + if (!wlc->hw->clk) + return ai_deviceremoved(wlc->hw->sih); + macctrl = bcma_read32(wlc->hw->d11core, + D11REGOFFS(maccontrol)); + return (macctrl & (MCTL_PSM_JMP_0 | MCTL_IHR_EN)) != MCTL_IHR_EN; +} + +/* sum the individual fifo tx pending packet counts */ +static int brcms_txpktpendtot(struct brcms_c_info *wlc) +{ + int i; + int pending = 0; + + for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++) + if (wlc->hw->di[i]) + pending += dma_txpending(wlc->hw->di[i]); + return pending; +} + +static bool brcms_is_mband_unlocked(struct brcms_c_info *wlc) +{ + return wlc->pub->_nbands > 1 && !wlc->bandlocked; +} + +static int brcms_chspec_bw(u16 chanspec) +{ + if (CHSPEC_IS40(chanspec)) + return BRCMS_40_MHZ; + if (CHSPEC_IS20(chanspec)) + return BRCMS_20_MHZ; + + return BRCMS_10_MHZ; +} + +static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg) +{ + if (cfg == NULL) + return; + + kfree(cfg->current_bss); + kfree(cfg); +} + +static void brcms_c_detach_mfree(struct brcms_c_info *wlc) +{ + if (wlc == NULL) + return; + + brcms_c_bsscfg_mfree(wlc->bsscfg); + kfree(wlc->pub); + kfree(wlc->modulecb); + kfree(wlc->default_bss); + kfree(wlc->protection); + kfree(wlc->stf); + kfree(wlc->bandstate[0]); + if (wlc->corestate) + kfree(wlc->corestate->macstat_snapshot); + kfree(wlc->corestate); + if (wlc->hw) + kfree(wlc->hw->bandstate[0]); + kfree(wlc->hw); + if (wlc->beacon) + dev_kfree_skb_any(wlc->beacon); + if (wlc->probe_resp) + dev_kfree_skb_any(wlc->probe_resp); + + kfree(wlc); +} + +static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit) +{ + struct brcms_bss_cfg *cfg; + + cfg = kzalloc(sizeof(struct brcms_bss_cfg), GFP_ATOMIC); + if (cfg == NULL) + goto fail; + + cfg->current_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC); + if (cfg->current_bss == NULL) + goto fail; + + return cfg; + + fail: + brcms_c_bsscfg_mfree(cfg); + return NULL; +} + +static struct brcms_c_info * +brcms_c_attach_malloc(uint unit, uint *err, uint devid) +{ + struct brcms_c_info *wlc; + + wlc = kzalloc(sizeof(struct brcms_c_info), GFP_ATOMIC); + if (wlc == NULL) { + *err = 1002; + goto fail; + } + + /* allocate struct brcms_c_pub state structure */ + wlc->pub = kzalloc(sizeof(struct brcms_pub), GFP_ATOMIC); + if (wlc->pub == NULL) { + *err = 1003; + goto fail; + } + wlc->pub->wlc = wlc; + + /* allocate struct brcms_hardware state structure */ + + wlc->hw = kzalloc(sizeof(struct brcms_hardware), GFP_ATOMIC); + if (wlc->hw == NULL) { + *err = 1005; + goto fail; + } + wlc->hw->wlc = wlc; + + wlc->hw->bandstate[0] = + kzalloc(sizeof(struct brcms_hw_band) * MAXBANDS, GFP_ATOMIC); + if (wlc->hw->bandstate[0] == NULL) { + *err = 1006; + goto fail; + } else { + int i; + + for (i = 1; i < MAXBANDS; i++) + wlc->hw->bandstate[i] = (struct brcms_hw_band *) + ((unsigned long)wlc->hw->bandstate[0] + + (sizeof(struct brcms_hw_band) * i)); + } + + wlc->modulecb = + kzalloc(sizeof(struct modulecb) * BRCMS_MAXMODULES, GFP_ATOMIC); + if (wlc->modulecb == NULL) { + *err = 1009; + goto fail; + } + + wlc->default_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC); + if (wlc->default_bss == NULL) { + *err = 1010; + goto fail; + } + + wlc->bsscfg = brcms_c_bsscfg_malloc(unit); + if (wlc->bsscfg == NULL) { + *err = 1011; + goto fail; + } + + wlc->protection = kzalloc(sizeof(struct brcms_protection), + GFP_ATOMIC); + if (wlc->protection == NULL) { + *err = 1016; + goto fail; + } + + wlc->stf = kzalloc(sizeof(struct brcms_stf), GFP_ATOMIC); + if (wlc->stf == NULL) { + *err = 1017; + goto fail; + } + + wlc->bandstate[0] = + kzalloc(sizeof(struct brcms_band)*MAXBANDS, GFP_ATOMIC); + if (wlc->bandstate[0] == NULL) { + *err = 1025; + goto fail; + } else { + int i; + + for (i = 1; i < MAXBANDS; i++) + wlc->bandstate[i] = (struct brcms_band *) + ((unsigned long)wlc->bandstate[0] + + (sizeof(struct brcms_band)*i)); + } + + wlc->corestate = kzalloc(sizeof(struct brcms_core), GFP_ATOMIC); + if (wlc->corestate == NULL) { + *err = 1026; + goto fail; + } + + wlc->corestate->macstat_snapshot = + kzalloc(sizeof(struct macstat), GFP_ATOMIC); + if (wlc->corestate->macstat_snapshot == NULL) { + *err = 1027; + goto fail; + } + + return wlc; + + fail: + brcms_c_detach_mfree(wlc); + return NULL; +} + +/* + * Update the slot timing for standard 11b/g (20us slots) + * or shortslot 11g (9us slots) + * The PSM needs to be suspended for this call. + */ +static void brcms_b_update_slot_timing(struct brcms_hardware *wlc_hw, + bool shortslot) +{ + struct bcma_device *core = wlc_hw->d11core; + + if (shortslot) { + /* 11g short slot: 11a timing */ + bcma_write16(core, D11REGOFFS(ifs_slot), 0x0207); + brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, APHY_SLOT_TIME); + } else { + /* 11g long slot: 11b timing */ + bcma_write16(core, D11REGOFFS(ifs_slot), 0x0212); + brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, BPHY_SLOT_TIME); + } +} + +/* + * calculate frame duration of a given rate and length, return + * time in usec unit + */ +static uint brcms_c_calc_frame_time(struct brcms_c_info *wlc, u32 ratespec, + u8 preamble_type, uint mac_len) +{ + uint nsyms, dur = 0, Ndps, kNdps; + uint rate = rspec2rate(ratespec); + + if (rate == 0) { + brcms_err(wlc->hw->d11core, "wl%d: WAR: using rate of 1 mbps\n", + wlc->pub->unit); + rate = BRCM_RATE_1M; + } + + if (is_mcs_rate(ratespec)) { + uint mcs = ratespec & RSPEC_RATE_MASK; + int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); + + dur = PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT); + if (preamble_type == BRCMS_MM_PREAMBLE) + dur += PREN_MM_EXT; + /* 1000Ndbps = kbps * 4 */ + kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), + rspec_issgi(ratespec)) * 4; + + if (rspec_stc(ratespec) == 0) + nsyms = + CEIL((APHY_SERVICE_NBITS + 8 * mac_len + + APHY_TAIL_NBITS) * 1000, kNdps); + else + /* STBC needs to have even number of symbols */ + nsyms = + 2 * + CEIL((APHY_SERVICE_NBITS + 8 * mac_len + + APHY_TAIL_NBITS) * 1000, 2 * kNdps); + + dur += APHY_SYMBOL_TIME * nsyms; + if (wlc->band->bandtype == BRCM_BAND_2G) + dur += DOT11_OFDM_SIGNAL_EXTENSION; + } else if (is_ofdm_rate(rate)) { + dur = APHY_PREAMBLE_TIME; + dur += APHY_SIGNAL_TIME; + /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */ + Ndps = rate * 2; + /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */ + nsyms = + CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS), + Ndps); + dur += APHY_SYMBOL_TIME * nsyms; + if (wlc->band->bandtype == BRCM_BAND_2G) + dur += DOT11_OFDM_SIGNAL_EXTENSION; + } else { + /* + * calc # bits * 2 so factor of 2 in rate (1/2 mbps) + * will divide out + */ + mac_len = mac_len * 8 * 2; + /* calc ceiling of bits/rate = microseconds of air time */ + dur = (mac_len + rate - 1) / rate; + if (preamble_type & BRCMS_SHORT_PREAMBLE) + dur += BPHY_PLCP_SHORT_TIME; + else + dur += BPHY_PLCP_TIME; + } + return dur; +} + +static void brcms_c_write_inits(struct brcms_hardware *wlc_hw, + const struct d11init *inits) +{ + struct bcma_device *core = wlc_hw->d11core; + int i; + uint offset; + u16 size; + u32 value; + + brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); + + for (i = 0; inits[i].addr != cpu_to_le16(0xffff); i++) { + size = le16_to_cpu(inits[i].size); + offset = le16_to_cpu(inits[i].addr); + value = le32_to_cpu(inits[i].value); + if (size == 2) + bcma_write16(core, offset, value); + else if (size == 4) + bcma_write32(core, offset, value); + else + break; + } +} + +static void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs) +{ + u8 idx; + u16 addr[] = { + M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4, + M_HOST_FLAGS5 + }; + + for (idx = 0; idx < MHFMAX; idx++) + brcms_b_write_shm(wlc_hw, addr[idx], mhfs[idx]); +} + +static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw) +{ + struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; + + /* init microcode host flags */ + brcms_c_write_mhf(wlc_hw, wlc_hw->band->mhfs); + + /* do band-specific ucode IHR, SHM, and SCR inits */ + if (D11REV_IS(wlc_hw->corerev, 17) || D11REV_IS(wlc_hw->corerev, 23)) { + if (BRCMS_ISNPHY(wlc_hw->band)) + brcms_c_write_inits(wlc_hw, ucode->d11n0bsinitvals16); + else + brcms_err(wlc_hw->d11core, + "%s: wl%d: unsupported phy in corerev %d\n", + __func__, wlc_hw->unit, + wlc_hw->corerev); + } else { + if (D11REV_IS(wlc_hw->corerev, 24)) { + if (BRCMS_ISLCNPHY(wlc_hw->band)) + brcms_c_write_inits(wlc_hw, + ucode->d11lcn0bsinitvals24); + else + brcms_err(wlc_hw->d11core, + "%s: wl%d: unsupported phy in core rev %d\n", + __func__, wlc_hw->unit, + wlc_hw->corerev); + } else { + brcms_err(wlc_hw->d11core, + "%s: wl%d: unsupported corerev %d\n", + __func__, wlc_hw->unit, wlc_hw->corerev); + } + } +} + +static void brcms_b_core_ioctl(struct brcms_hardware *wlc_hw, u32 m, u32 v) +{ + struct bcma_device *core = wlc_hw->d11core; + u32 ioctl = bcma_aread32(core, BCMA_IOCTL) & ~m; + + bcma_awrite32(core, BCMA_IOCTL, ioctl | v); +} + +static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk) +{ + brcms_dbg_info(wlc_hw->d11core, "wl%d: clk %d\n", wlc_hw->unit, clk); + + wlc_hw->phyclk = clk; + + if (OFF == clk) { /* clear gmode bit, put phy into reset */ + + brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC | SICF_GMODE), + (SICF_PRST | SICF_FGC)); + udelay(1); + brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC), SICF_PRST); + udelay(1); + + } else { /* take phy out of reset */ + + brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC), SICF_FGC); + udelay(1); + brcms_b_core_ioctl(wlc_hw, SICF_FGC, 0); + udelay(1); + + } +} + +/* low-level band switch utility routine */ +static void brcms_c_setxband(struct brcms_hardware *wlc_hw, uint bandunit) +{ + brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: bandunit %d\n", wlc_hw->unit, + bandunit); + + wlc_hw->band = wlc_hw->bandstate[bandunit]; + + /* + * BMAC_NOTE: + * until we eliminate need for wlc->band refs in low level code + */ + wlc_hw->wlc->band = wlc_hw->wlc->bandstate[bandunit]; + + /* set gmode core flag */ + if (wlc_hw->sbclk && !wlc_hw->noreset) { + u32 gmode = 0; + + if (bandunit == 0) + gmode = SICF_GMODE; + + brcms_b_core_ioctl(wlc_hw, SICF_GMODE, gmode); + } +} + +/* switch to new band but leave it inactive */ +static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + u32 macintmask; + u32 macctrl; + + brcms_dbg_mac80211(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); + macctrl = bcma_read32(wlc_hw->d11core, + D11REGOFFS(maccontrol)); + WARN_ON((macctrl & MCTL_EN_MAC) != 0); + + /* disable interrupts */ + macintmask = brcms_intrsoff(wlc->wl); + + /* radio off */ + wlc_phy_switch_radio(wlc_hw->band->pi, OFF); + + brcms_b_core_phy_clk(wlc_hw, OFF); + + brcms_c_setxband(wlc_hw, bandunit); + + return macintmask; +} + +/* process an individual struct tx_status */ +static bool +brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) +{ + struct sk_buff *p = NULL; + uint queue = NFIFO; + struct dma_pub *dma = NULL; + struct d11txh *txh = NULL; + struct scb *scb = NULL; + bool free_pdu; + int tx_rts, tx_frame_count, tx_rts_count; + uint totlen, supr_status; + bool lastframe; + struct ieee80211_hdr *h; + u16 mcl; + struct ieee80211_tx_info *tx_info; + struct ieee80211_tx_rate *txrate; + int i; + bool fatal = true; + + trace_brcms_txstatus(&wlc->hw->d11core->dev, txs->framelen, + txs->frameid, txs->status, txs->lasttxtime, + txs->sequence, txs->phyerr, txs->ackphyrxsh); + + /* discard intermediate indications for ucode with one legitimate case: + * e.g. if "useRTS" is set. ucode did a successful rts/cts exchange, + * but the subsequent tx of DATA failed. so it will start rts/cts + * from the beginning (resetting the rts transmission count) + */ + if (!(txs->status & TX_STATUS_AMPDU) + && (txs->status & TX_STATUS_INTERMEDIATE)) { + brcms_dbg_tx(wlc->hw->d11core, "INTERMEDIATE but not AMPDU\n"); + fatal = false; + goto out; + } + + queue = txs->frameid & TXFID_QUEUE_MASK; + if (queue >= NFIFO) { + brcms_err(wlc->hw->d11core, "queue %u >= NFIFO\n", queue); + goto out; + } + + dma = wlc->hw->di[queue]; + + p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); + if (p == NULL) { + brcms_err(wlc->hw->d11core, "dma_getnexttxp returned null!\n"); + goto out; + } + + txh = (struct d11txh *) (p->data); + mcl = le16_to_cpu(txh->MacTxControlLow); + + if (txs->phyerr) + brcms_dbg_tx(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n", + txs->phyerr, txh->MainRates); + + if (txs->frameid != le16_to_cpu(txh->TxFrameID)) { + brcms_err(wlc->hw->d11core, "frameid != txh->TxFrameID\n"); + goto out; + } + tx_info = IEEE80211_SKB_CB(p); + h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN); + + if (tx_info->rate_driver_data[0]) + scb = &wlc->pri_scb; + + if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { + brcms_c_ampdu_dotxstatus(wlc->ampdu, scb, p, txs); + fatal = false; + goto out; + } + + /* + * brcms_c_ampdu_dotxstatus() will trace tx descriptors for AMPDU + * frames; this traces them for the rest. + */ + trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh)); + + supr_status = txs->status & TX_STATUS_SUPR_MASK; + if (supr_status == TX_STATUS_SUPR_BADCH) { + unsigned xfts = le16_to_cpu(txh->XtraFrameTypes); + brcms_dbg_tx(wlc->hw->d11core, + "Pkt tx suppressed, dest chan %u, current %d\n", + (xfts >> XFTS_CHANNEL_SHIFT) & 0xff, + CHSPEC_CHANNEL(wlc->default_bss->chanspec)); + } + + tx_rts = le16_to_cpu(txh->MacTxControlLow) & TXC_SENDRTS; + tx_frame_count = + (txs->status & TX_STATUS_FRM_RTX_MASK) >> TX_STATUS_FRM_RTX_SHIFT; + tx_rts_count = + (txs->status & TX_STATUS_RTS_RTX_MASK) >> TX_STATUS_RTS_RTX_SHIFT; + + lastframe = !ieee80211_has_morefrags(h->frame_control); + + if (!lastframe) { + brcms_err(wlc->hw->d11core, "Not last frame!\n"); + } else { + /* + * Set information to be consumed by Minstrel ht. + * + * The "fallback limit" is the number of tx attempts a given + * MPDU is sent at the "primary" rate. Tx attempts beyond that + * limit are sent at the "secondary" rate. + * A 'short frame' does not exceed RTS treshold. + */ + u16 sfbl, /* Short Frame Rate Fallback Limit */ + lfbl, /* Long Frame Rate Fallback Limit */ + fbl; + + if (queue < IEEE80211_NUM_ACS) { + sfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]], + EDCF_SFB); + lfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]], + EDCF_LFB); + } else { + sfbl = wlc->SFBL; + lfbl = wlc->LFBL; + } + + txrate = tx_info->status.rates; + if (txrate[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) + fbl = lfbl; + else + fbl = sfbl; + + ieee80211_tx_info_clear_status(tx_info); + + if ((tx_frame_count > fbl) && (txrate[1].idx >= 0)) { + /* + * rate selection requested a fallback rate + * and we used it + */ + txrate[0].count = fbl; + txrate[1].count = tx_frame_count - fbl; + } else { + /* + * rate selection did not request fallback rate, or + * we didn't need it + */ + txrate[0].count = tx_frame_count; + /* + * rc80211_minstrel.c:minstrel_tx_status() expects + * unused rates to be marked with idx = -1 + */ + txrate[1].idx = -1; + txrate[1].count = 0; + } + + /* clear the rest of the rates */ + for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) { + txrate[i].idx = -1; + txrate[i].count = 0; + } + + if (txs->status & TX_STATUS_ACK_RCV) + tx_info->flags |= IEEE80211_TX_STAT_ACK; + } + + totlen = p->len; + free_pdu = true; + + if (lastframe) { + /* remove PLCP & Broadcom tx descriptor header */ + skb_pull(p, D11_PHY_HDR_LEN); + skb_pull(p, D11_TXH_LEN); + ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p); + } else { + brcms_err(wlc->hw->d11core, + "%s: Not last frame => not calling tx_status\n", + __func__); + } + + fatal = false; + + out: + if (fatal) { + if (txh) + trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, + sizeof(*txh)); + brcmu_pkt_buf_free_skb(p); + } + + if (dma && queue < NFIFO) { + u16 ac_queue = brcms_fifo_to_ac(queue); + if (dma->txavail > TX_HEADROOM && queue < TX_BCMC_FIFO && + ieee80211_queue_stopped(wlc->pub->ieee_hw, ac_queue)) + ieee80211_wake_queue(wlc->pub->ieee_hw, ac_queue); + dma_kick_tx(dma); + } + + return fatal; +} + +/* process tx completion events in BMAC + * Return true if more tx status need to be processed. false otherwise. + */ +static bool +brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) +{ + struct bcma_device *core; + struct tx_status txstatus, *txs; + u32 s1, s2; + uint n = 0; + /* + * Param 'max_tx_num' indicates max. # tx status to process before + * break out. + */ + uint max_tx_num = bound ? TXSBND : -1; + + txs = &txstatus; + core = wlc_hw->d11core; + *fatal = false; + + while (n < max_tx_num) { + s1 = bcma_read32(core, D11REGOFFS(frmtxstatus)); + if (s1 == 0xffffffff) { + brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, + __func__); + *fatal = true; + return false; + } + /* only process when valid */ + if (!(s1 & TXS_V)) + break; + + s2 = bcma_read32(core, D11REGOFFS(frmtxstatus2)); + txs->status = s1 & TXS_STATUS_MASK; + txs->frameid = (s1 & TXS_FID_MASK) >> TXS_FID_SHIFT; + txs->sequence = s2 & TXS_SEQ_MASK; + txs->phyerr = (s2 & TXS_PTX_MASK) >> TXS_PTX_SHIFT; + txs->lasttxtime = 0; + + *fatal = brcms_c_dotxstatus(wlc_hw->wlc, txs); + if (*fatal == true) + return false; + n++; + } + + return n >= max_tx_num; +} + +static void brcms_c_tbtt(struct brcms_c_info *wlc) +{ + if (wlc->bsscfg->type == BRCMS_TYPE_ADHOC) + /* + * DirFrmQ is now valid...defer setting until end + * of ATIM window + */ + wlc->qvalid |= MCMD_DIRFRMQVAL; +} + +/* set initial host flags value */ +static void +brcms_c_mhfdef(struct brcms_c_info *wlc, u16 *mhfs, u16 mhf2_init) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + + memset(mhfs, 0, MHFMAX * sizeof(u16)); + + mhfs[MHF2] |= mhf2_init; + + /* prohibit use of slowclock on multifunction boards */ + if (wlc_hw->boardflags & BFL_NOPLLDOWN) + mhfs[MHF1] |= MHF1_FORCEFASTCLK; + + if (BRCMS_ISNPHY(wlc_hw->band) && NREV_LT(wlc_hw->band->phyrev, 2)) { + mhfs[MHF2] |= MHF2_NPHY40MHZ_WAR; + mhfs[MHF1] |= MHF1_IQSWAP_WAR; + } +} + +static uint +dmareg(uint direction, uint fifonum) +{ + if (direction == DMA_TX) + return offsetof(struct d11regs, fifo64regs[fifonum].dmaxmt); + return offsetof(struct d11regs, fifo64regs[fifonum].dmarcv); +} + +static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme) +{ + uint i; + char name[8]; + /* + * ucode host flag 2 needed for pio mode, independent of band and fifo + */ + u16 pio_mhf2 = 0; + struct brcms_hardware *wlc_hw = wlc->hw; + uint unit = wlc_hw->unit; + + /* name and offsets for dma_attach */ + snprintf(name, sizeof(name), "wl%d", unit); + + if (wlc_hw->di[0] == NULL) { /* Init FIFOs */ + int dma_attach_err = 0; + + /* + * FIFO 0 + * TX: TX_AC_BK_FIFO (TX AC Background data packets) + * RX: RX_FIFO (RX data packets) + */ + wlc_hw->di[0] = dma_attach(name, wlc, + (wme ? dmareg(DMA_TX, 0) : 0), + dmareg(DMA_RX, 0), + (wme ? NTXD : 0), NRXD, + RXBUFSZ, -1, NRXBUFPOST, + BRCMS_HWRXOFF); + dma_attach_err |= (NULL == wlc_hw->di[0]); + + /* + * FIFO 1 + * TX: TX_AC_BE_FIFO (TX AC Best-Effort data packets) + * (legacy) TX_DATA_FIFO (TX data packets) + * RX: UNUSED + */ + wlc_hw->di[1] = dma_attach(name, wlc, + dmareg(DMA_TX, 1), 0, + NTXD, 0, 0, -1, 0, 0); + dma_attach_err |= (NULL == wlc_hw->di[1]); + + /* + * FIFO 2 + * TX: TX_AC_VI_FIFO (TX AC Video data packets) + * RX: UNUSED + */ + wlc_hw->di[2] = dma_attach(name, wlc, + dmareg(DMA_TX, 2), 0, + NTXD, 0, 0, -1, 0, 0); + dma_attach_err |= (NULL == wlc_hw->di[2]); + /* + * FIFO 3 + * TX: TX_AC_VO_FIFO (TX AC Voice data packets) + * (legacy) TX_CTL_FIFO (TX control & mgmt packets) + */ + wlc_hw->di[3] = dma_attach(name, wlc, + dmareg(DMA_TX, 3), + 0, NTXD, 0, 0, -1, + 0, 0); + dma_attach_err |= (NULL == wlc_hw->di[3]); +/* Cleaner to leave this as if with AP defined */ + + if (dma_attach_err) { + brcms_err(wlc_hw->d11core, + "wl%d: wlc_attach: dma_attach failed\n", + unit); + return false; + } + + /* get pointer to dma engine tx flow control variable */ + for (i = 0; i < NFIFO; i++) + if (wlc_hw->di[i]) + wlc_hw->txavail[i] = + (uint *) dma_getvar(wlc_hw->di[i], + "&txavail"); + } + + /* initial ucode host flags */ + brcms_c_mhfdef(wlc, wlc_hw->band->mhfs, pio_mhf2); + + return true; +} + +static void brcms_b_detach_dmapio(struct brcms_hardware *wlc_hw) +{ + uint j; + + for (j = 0; j < NFIFO; j++) { + if (wlc_hw->di[j]) { + dma_detach(wlc_hw->di[j]); + wlc_hw->di[j] = NULL; + } + } +} + +/* + * Initialize brcms_c_info default values ... + * may get overrides later in this function + * BMAC_NOTES, move low out and resolve the dangling ones + */ +static void brcms_b_info_init(struct brcms_hardware *wlc_hw) +{ + struct brcms_c_info *wlc = wlc_hw->wlc; + + /* set default sw macintmask value */ + wlc->defmacintmask = DEF_MACINTMASK; + + /* various 802.11g modes */ + wlc_hw->shortslot = false; + + wlc_hw->SFBL = RETRY_SHORT_FB; + wlc_hw->LFBL = RETRY_LONG_FB; + + /* default mac retry limits */ + wlc_hw->SRL = RETRY_SHORT_DEF; + wlc_hw->LRL = RETRY_LONG_DEF; + wlc_hw->chanspec = ch20mhz_chspec(1); +} + +static void brcms_b_wait_for_wake(struct brcms_hardware *wlc_hw) +{ + /* delay before first read of ucode state */ + udelay(40); + + /* wait until ucode is no longer asleep */ + SPINWAIT((brcms_b_read_shm(wlc_hw, M_UCODE_DBGST) == + DBGST_ASLEEP), wlc_hw->wlc->fastpwrup_dly); +} + +/* control chip clock to save power, enable dynamic clock or force fast clock */ +static void brcms_b_clkctl_clk(struct brcms_hardware *wlc_hw, enum bcma_clkmode mode) +{ + if (ai_get_cccaps(wlc_hw->sih) & CC_CAP_PMU) { + /* new chips with PMU, CCS_FORCEHT will distribute the HT clock + * on backplane, but mac core will still run on ALP(not HT) when + * it enters powersave mode, which means the FCA bit may not be + * set. Should wakeup mac if driver wants it to run on HT. + */ + + if (wlc_hw->clk) { + if (mode == BCMA_CLKMODE_FAST) { + bcma_set32(wlc_hw->d11core, + D11REGOFFS(clk_ctl_st), + CCS_FORCEHT); + + udelay(64); + + SPINWAIT( + ((bcma_read32(wlc_hw->d11core, + D11REGOFFS(clk_ctl_st)) & + CCS_HTAVAIL) == 0), + PMU_MAX_TRANSITION_DLY); + WARN_ON(!(bcma_read32(wlc_hw->d11core, + D11REGOFFS(clk_ctl_st)) & + CCS_HTAVAIL)); + } else { + if ((ai_get_pmurev(wlc_hw->sih) == 0) && + (bcma_read32(wlc_hw->d11core, + D11REGOFFS(clk_ctl_st)) & + (CCS_FORCEHT | CCS_HTAREQ))) + SPINWAIT( + ((bcma_read32(wlc_hw->d11core, + offsetof(struct d11regs, + clk_ctl_st)) & + CCS_HTAVAIL) == 0), + PMU_MAX_TRANSITION_DLY); + bcma_mask32(wlc_hw->d11core, + D11REGOFFS(clk_ctl_st), + ~CCS_FORCEHT); + } + } + wlc_hw->forcefastclk = (mode == BCMA_CLKMODE_FAST); + } else { + + /* old chips w/o PMU, force HT through cc, + * then use FCA to verify mac is running fast clock + */ + + wlc_hw->forcefastclk = ai_clkctl_cc(wlc_hw->sih, mode); + + /* check fast clock is available (if core is not in reset) */ + if (wlc_hw->forcefastclk && wlc_hw->clk) + WARN_ON(!(bcma_aread32(wlc_hw->d11core, BCMA_IOST) & + SISF_FCLKA)); + + /* + * keep the ucode wake bit on if forcefastclk is on since we + * do not want ucode to put us back to slow clock when it dozes + * for PM mode. Code below matches the wake override bit with + * current forcefastclk state. Only setting bit in wake_override + * instead of waking ucode immediately since old code had this + * behavior. Older code set wlc->forcefastclk but only had the + * wake happen if the wakup_ucode work (protected by an up + * check) was executed just below. + */ + if (wlc_hw->forcefastclk) + mboolset(wlc_hw->wake_override, + BRCMS_WAKE_OVERRIDE_FORCEFAST); + else + mboolclr(wlc_hw->wake_override, + BRCMS_WAKE_OVERRIDE_FORCEFAST); + } +} + +/* set or clear ucode host flag bits + * it has an optimization for no-change write + * it only writes through shared memory when the core has clock; + * pre-CLK changes should use wlc_write_mhf to get around the optimization + * + * + * bands values are: BRCM_BAND_AUTO <--- Current band only + * BRCM_BAND_5G <--- 5G band only + * BRCM_BAND_2G <--- 2G band only + * BRCM_BAND_ALL <--- All bands + */ +void +brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val, + int bands) +{ + u16 save; + u16 addr[MHFMAX] = { + M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4, + M_HOST_FLAGS5 + }; + struct brcms_hw_band *band; + + if ((val & ~mask) || idx >= MHFMAX) + return; /* error condition */ + + switch (bands) { + /* Current band only or all bands, + * then set the band to current band + */ + case BRCM_BAND_AUTO: + case BRCM_BAND_ALL: + band = wlc_hw->band; + break; + case BRCM_BAND_5G: + band = wlc_hw->bandstate[BAND_5G_INDEX]; + break; + case BRCM_BAND_2G: + band = wlc_hw->bandstate[BAND_2G_INDEX]; + break; + default: + band = NULL; /* error condition */ + } + + if (band) { + save = band->mhfs[idx]; + band->mhfs[idx] = (band->mhfs[idx] & ~mask) | val; + + /* optimization: only write through if changed, and + * changed band is the current band + */ + if (wlc_hw->clk && (band->mhfs[idx] != save) + && (band == wlc_hw->band)) + brcms_b_write_shm(wlc_hw, addr[idx], + (u16) band->mhfs[idx]); + } + + if (bands == BRCM_BAND_ALL) { + wlc_hw->bandstate[0]->mhfs[idx] = + (wlc_hw->bandstate[0]->mhfs[idx] & ~mask) | val; + wlc_hw->bandstate[1]->mhfs[idx] = + (wlc_hw->bandstate[1]->mhfs[idx] & ~mask) | val; + } +} + +/* set the maccontrol register to desired reset state and + * initialize the sw cache of the register + */ +static void brcms_c_mctrl_reset(struct brcms_hardware *wlc_hw) +{ + /* IHR accesses are always enabled, PSM disabled, HPS off and WAKE on */ + wlc_hw->maccontrol = 0; + wlc_hw->suspended_fifos = 0; + wlc_hw->wake_override = 0; + wlc_hw->mute_override = 0; + brcms_b_mctrl(wlc_hw, ~0, MCTL_IHR_EN | MCTL_WAKE); +} + +/* + * write the software state of maccontrol and + * overrides to the maccontrol register + */ +static void brcms_c_mctrl_write(struct brcms_hardware *wlc_hw) +{ + u32 maccontrol = wlc_hw->maccontrol; + + /* OR in the wake bit if overridden */ + if (wlc_hw->wake_override) + maccontrol |= MCTL_WAKE; + + /* set AP and INFRA bits for mute if needed */ + if (wlc_hw->mute_override) { + maccontrol &= ~(MCTL_AP); + maccontrol |= MCTL_INFRA; + } + + bcma_write32(wlc_hw->d11core, D11REGOFFS(maccontrol), + maccontrol); +} + +/* set or clear maccontrol bits */ +void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val) +{ + u32 maccontrol; + u32 new_maccontrol; + + if (val & ~mask) + return; /* error condition */ + maccontrol = wlc_hw->maccontrol; + new_maccontrol = (maccontrol & ~mask) | val; + + /* if the new maccontrol value is the same as the old, nothing to do */ + if (new_maccontrol == maccontrol) + return; + + /* something changed, cache the new value */ + wlc_hw->maccontrol = new_maccontrol; + + /* write the new values with overrides applied */ + brcms_c_mctrl_write(wlc_hw); +} + +void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw, + u32 override_bit) +{ + if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) { + mboolset(wlc_hw->wake_override, override_bit); + return; + } + + mboolset(wlc_hw->wake_override, override_bit); + + brcms_c_mctrl_write(wlc_hw); + brcms_b_wait_for_wake(wlc_hw); +} + +void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw, + u32 override_bit) +{ + mboolclr(wlc_hw->wake_override, override_bit); + + if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) + return; + + brcms_c_mctrl_write(wlc_hw); +} + +/* When driver needs ucode to stop beaconing, it has to make sure that + * MCTL_AP is clear and MCTL_INFRA is set + * Mode MCTL_AP MCTL_INFRA + * AP 1 1 + * STA 0 1 <--- This will ensure no beacons + * IBSS 0 0 + */ +static void brcms_c_ucode_mute_override_set(struct brcms_hardware *wlc_hw) +{ + wlc_hw->mute_override = 1; + + /* if maccontrol already has AP == 0 and INFRA == 1 without this + * override, then there is no change to write + */ + if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA) + return; + + brcms_c_mctrl_write(wlc_hw); +} + +/* Clear the override on AP and INFRA bits */ +static void brcms_c_ucode_mute_override_clear(struct brcms_hardware *wlc_hw) +{ + if (wlc_hw->mute_override == 0) + return; + + wlc_hw->mute_override = 0; + + /* if maccontrol already has AP == 0 and INFRA == 1 without this + * override, then there is no change to write + */ + if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA) + return; + + brcms_c_mctrl_write(wlc_hw); +} + +/* + * Write a MAC address to the given match reg offset in the RXE match engine. + */ +static void +brcms_b_set_addrmatch(struct brcms_hardware *wlc_hw, int match_reg_offset, + const u8 *addr) +{ + struct bcma_device *core = wlc_hw->d11core; + u16 mac_l; + u16 mac_m; + u16 mac_h; + + brcms_dbg_rx(core, "wl%d: brcms_b_set_addrmatch\n", wlc_hw->unit); + + mac_l = addr[0] | (addr[1] << 8); + mac_m = addr[2] | (addr[3] << 8); + mac_h = addr[4] | (addr[5] << 8); + + /* enter the MAC addr into the RXE match registers */ + bcma_write16(core, D11REGOFFS(rcm_ctl), + RCM_INC_DATA | match_reg_offset); + bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_l); + bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_m); + bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_h); +} + +void +brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, int len, + void *buf) +{ + struct bcma_device *core = wlc_hw->d11core; + u32 word; + __le32 word_le; + __be32 word_be; + bool be_bit; + brcms_dbg_info(core, "wl%d\n", wlc_hw->unit); + + bcma_write32(core, D11REGOFFS(tplatewrptr), offset); + + /* if MCTL_BIGEND bit set in mac control register, + * the chip swaps data in fifo, as well as data in + * template ram + */ + be_bit = (bcma_read32(core, D11REGOFFS(maccontrol)) & MCTL_BIGEND) != 0; + + while (len > 0) { + memcpy(&word, buf, sizeof(u32)); + + if (be_bit) { + word_be = cpu_to_be32(word); + word = *(u32 *)&word_be; + } else { + word_le = cpu_to_le32(word); + word = *(u32 *)&word_le; + } + + bcma_write32(core, D11REGOFFS(tplatewrdata), word); + + buf = (u8 *) buf + sizeof(u32); + len -= sizeof(u32); + } +} + +static void brcms_b_set_cwmin(struct brcms_hardware *wlc_hw, u16 newmin) +{ + wlc_hw->band->CWmin = newmin; + + bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), + OBJADDR_SCR_SEL | S_DOT11_CWMIN); + (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); + bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), newmin); +} + +static void brcms_b_set_cwmax(struct brcms_hardware *wlc_hw, u16 newmax) +{ + wlc_hw->band->CWmax = newmax; + + bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), + OBJADDR_SCR_SEL | S_DOT11_CWMAX); + (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); + bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), newmax); +} + +void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw) +{ + bool fastclk; + + /* request FAST clock if not on */ + fastclk = wlc_hw->forcefastclk; + if (!fastclk) + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); + + wlc_phy_bw_state_set(wlc_hw->band->pi, bw); + + brcms_b_phy_reset(wlc_hw); + wlc_phy_init(wlc_hw->band->pi, wlc_phy_chanspec_get(wlc_hw->band->pi)); + + /* restore the clk */ + if (!fastclk) + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); +} + +static void brcms_b_upd_synthpu(struct brcms_hardware *wlc_hw) +{ + u16 v; + struct brcms_c_info *wlc = wlc_hw->wlc; + /* update SYNTHPU_DLY */ + + if (BRCMS_ISLCNPHY(wlc->band)) + v = SYNTHPU_DLY_LPPHY_US; + else if (BRCMS_ISNPHY(wlc->band) && (NREV_GE(wlc->band->phyrev, 3))) + v = SYNTHPU_DLY_NPHY_US; + else + v = SYNTHPU_DLY_BPHY_US; + + brcms_b_write_shm(wlc_hw, M_SYNTHPU_DLY, v); +} + +static void brcms_c_ucode_txant_set(struct brcms_hardware *wlc_hw) +{ + u16 phyctl; + u16 phytxant = wlc_hw->bmac_phytxant; + u16 mask = PHY_TXC_ANT_MASK; + + /* set the Probe Response frame phy control word */ + phyctl = brcms_b_read_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS); + phyctl = (phyctl & ~mask) | phytxant; + brcms_b_write_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS, phyctl); + + /* set the Response (ACK/CTS) frame phy control word */ + phyctl = brcms_b_read_shm(wlc_hw, M_RSP_PCTLWD); + phyctl = (phyctl & ~mask) | phytxant; + brcms_b_write_shm(wlc_hw, M_RSP_PCTLWD, phyctl); +} + +static u16 brcms_b_ofdm_ratetable_offset(struct brcms_hardware *wlc_hw, + u8 rate) +{ + uint i; + u8 plcp_rate = 0; + struct plcp_signal_rate_lookup { + u8 rate; + u8 signal_rate; + }; + /* OFDM RATE sub-field of PLCP SIGNAL field, per 802.11 sec 17.3.4.1 */ + const struct plcp_signal_rate_lookup rate_lookup[] = { + {BRCM_RATE_6M, 0xB}, + {BRCM_RATE_9M, 0xF}, + {BRCM_RATE_12M, 0xA}, + {BRCM_RATE_18M, 0xE}, + {BRCM_RATE_24M, 0x9}, + {BRCM_RATE_36M, 0xD}, + {BRCM_RATE_48M, 0x8}, + {BRCM_RATE_54M, 0xC} + }; + + for (i = 0; i < ARRAY_SIZE(rate_lookup); i++) { + if (rate == rate_lookup[i].rate) { + plcp_rate = rate_lookup[i].signal_rate; + break; + } + } + + /* Find the SHM pointer to the rate table entry by looking in the + * Direct-map Table + */ + return 2 * brcms_b_read_shm(wlc_hw, M_RT_DIRMAP_A + (plcp_rate * 2)); +} + +static void brcms_upd_ofdm_pctl1_table(struct brcms_hardware *wlc_hw) +{ + u8 rate; + u8 rates[8] = { + BRCM_RATE_6M, BRCM_RATE_9M, BRCM_RATE_12M, BRCM_RATE_18M, + BRCM_RATE_24M, BRCM_RATE_36M, BRCM_RATE_48M, BRCM_RATE_54M + }; + u16 entry_ptr; + u16 pctl1; + uint i; + + if (!BRCMS_PHY_11N_CAP(wlc_hw->band)) + return; + + /* walk the phy rate table and update the entries */ + for (i = 0; i < ARRAY_SIZE(rates); i++) { + rate = rates[i]; + + entry_ptr = brcms_b_ofdm_ratetable_offset(wlc_hw, rate); + + /* read the SHM Rate Table entry OFDM PCTL1 values */ + pctl1 = + brcms_b_read_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS); + + /* modify the value */ + pctl1 &= ~PHY_TXC1_MODE_MASK; + pctl1 |= (wlc_hw->hw_stf_ss_opmode << PHY_TXC1_MODE_SHIFT); + + /* Update the SHM Rate Table entry OFDM PCTL1 values */ + brcms_b_write_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS, + pctl1); + } +} + +/* band-specific init */ +static void brcms_b_bsinit(struct brcms_c_info *wlc, u16 chanspec) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + + brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: bandunit %d\n", wlc_hw->unit, + wlc_hw->band->bandunit); + + brcms_c_ucode_bsinit(wlc_hw); + + wlc_phy_init(wlc_hw->band->pi, chanspec); + + brcms_c_ucode_txant_set(wlc_hw); + + /* + * cwmin is band-specific, update hardware + * with value for current band + */ + brcms_b_set_cwmin(wlc_hw, wlc_hw->band->CWmin); + brcms_b_set_cwmax(wlc_hw, wlc_hw->band->CWmax); + + brcms_b_update_slot_timing(wlc_hw, + wlc_hw->band->bandtype == BRCM_BAND_5G ? + true : wlc_hw->shortslot); + + /* write phytype and phyvers */ + brcms_b_write_shm(wlc_hw, M_PHYTYPE, (u16) wlc_hw->band->phytype); + brcms_b_write_shm(wlc_hw, M_PHYVER, (u16) wlc_hw->band->phyrev); + + /* + * initialize the txphyctl1 rate table since + * shmem is shared between bands + */ + brcms_upd_ofdm_pctl1_table(wlc_hw); + + brcms_b_upd_synthpu(wlc_hw); +} + +/* Perform a soft reset of the PHY PLL */ +void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw) +{ + ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_addr), + ~0, 0); + udelay(1); + ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), + 0x4, 0); + udelay(1); + ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), + 0x4, 4); + udelay(1); + ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), + 0x4, 0); + udelay(1); +} + +/* light way to turn on phy clock without reset for NPHY only + * refer to brcms_b_core_phy_clk for full version + */ +void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk) +{ + /* support(necessary for NPHY and HYPHY) only */ + if (!BRCMS_ISNPHY(wlc_hw->band)) + return; + + if (ON == clk) + brcms_b_core_ioctl(wlc_hw, SICF_FGC, SICF_FGC); + else + brcms_b_core_ioctl(wlc_hw, SICF_FGC, 0); + +} + +void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk) +{ + if (ON == clk) + brcms_b_core_ioctl(wlc_hw, SICF_MPCLKE, SICF_MPCLKE); + else + brcms_b_core_ioctl(wlc_hw, SICF_MPCLKE, 0); +} + +void brcms_b_phy_reset(struct brcms_hardware *wlc_hw) +{ + struct brcms_phy_pub *pih = wlc_hw->band->pi; + u32 phy_bw_clkbits; + bool phy_in_reset = false; + + brcms_dbg_info(wlc_hw->d11core, "wl%d: reset phy\n", wlc_hw->unit); + + if (pih == NULL) + return; + + phy_bw_clkbits = wlc_phy_clk_bwbits(wlc_hw->band->pi); + + /* Specific reset sequence required for NPHY rev 3 and 4 */ + if (BRCMS_ISNPHY(wlc_hw->band) && NREV_GE(wlc_hw->band->phyrev, 3) && + NREV_LE(wlc_hw->band->phyrev, 4)) { + /* Set the PHY bandwidth */ + brcms_b_core_ioctl(wlc_hw, SICF_BWMASK, phy_bw_clkbits); + + udelay(1); + + /* Perform a soft reset of the PHY PLL */ + brcms_b_core_phypll_reset(wlc_hw); + + /* reset the PHY */ + brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_PCLKE), + (SICF_PRST | SICF_PCLKE)); + phy_in_reset = true; + } else { + brcms_b_core_ioctl(wlc_hw, + (SICF_PRST | SICF_PCLKE | SICF_BWMASK), + (SICF_PRST | SICF_PCLKE | phy_bw_clkbits)); + } + + udelay(2); + brcms_b_core_phy_clk(wlc_hw, ON); + + if (pih) + wlc_phy_anacore(pih, ON); +} + +/* switch to and initialize new band */ +static void brcms_b_setband(struct brcms_hardware *wlc_hw, uint bandunit, + u16 chanspec) { + struct brcms_c_info *wlc = wlc_hw->wlc; + u32 macintmask; + + /* Enable the d11 core before accessing it */ + if (!bcma_core_is_enabled(wlc_hw->d11core)) { + bcma_core_enable(wlc_hw->d11core, 0); + brcms_c_mctrl_reset(wlc_hw); + } + + macintmask = brcms_c_setband_inact(wlc, bandunit); + + if (!wlc_hw->up) + return; + + brcms_b_core_phy_clk(wlc_hw, ON); + + /* band-specific initializations */ + brcms_b_bsinit(wlc, chanspec); + + /* + * If there are any pending software interrupt bits, + * then replace these with a harmless nonzero value + * so brcms_c_dpc() will re-enable interrupts when done. + */ + if (wlc->macintstatus) + wlc->macintstatus = MI_DMAINT; + + /* restore macintmask */ + brcms_intrsrestore(wlc->wl, macintmask); + + /* ucode should still be suspended.. */ + WARN_ON((bcma_read32(wlc_hw->d11core, D11REGOFFS(maccontrol)) & + MCTL_EN_MAC) != 0); +} + +static bool brcms_c_isgoodchip(struct brcms_hardware *wlc_hw) +{ + + /* reject unsupported corerev */ + if (!CONF_HAS(D11CONF, wlc_hw->corerev)) { + wiphy_err(wlc_hw->wlc->wiphy, "unsupported core rev %d\n", + wlc_hw->corerev); + return false; + } + + return true; +} + +/* Validate some board info parameters */ +static bool brcms_c_validboardtype(struct brcms_hardware *wlc_hw) +{ + uint boardrev = wlc_hw->boardrev; + + /* 4 bits each for board type, major, minor, and tiny version */ + uint brt = (boardrev & 0xf000) >> 12; + uint b0 = (boardrev & 0xf00) >> 8; + uint b1 = (boardrev & 0xf0) >> 4; + uint b2 = boardrev & 0xf; + + /* voards from other vendors are always considered valid */ + if (ai_get_boardvendor(wlc_hw->sih) != PCI_VENDOR_ID_BROADCOM) + return true; + + /* do some boardrev sanity checks when boardvendor is Broadcom */ + if (boardrev == 0) + return false; + + if (boardrev <= 0xff) + return true; + + if ((brt > 2) || (brt == 0) || (b0 > 9) || (b0 == 0) || (b1 > 9) + || (b2 > 9)) + return false; + + return true; +} + +static void brcms_c_get_macaddr(struct brcms_hardware *wlc_hw, u8 etheraddr[ETH_ALEN]) +{ + struct ssb_sprom *sprom = &wlc_hw->d11core->bus->sprom; + + /* If macaddr exists, use it (Sromrev4, CIS, ...). */ + if (!is_zero_ether_addr(sprom->il0mac)) { + memcpy(etheraddr, sprom->il0mac, ETH_ALEN); + return; + } + + if (wlc_hw->_nbands > 1) + memcpy(etheraddr, sprom->et1mac, ETH_ALEN); + else + memcpy(etheraddr, sprom->il0mac, ETH_ALEN); +} + +/* power both the pll and external oscillator on/off */ +static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want) +{ + brcms_dbg_info(wlc_hw->d11core, "wl%d: want %d\n", wlc_hw->unit, want); + + /* + * dont power down if plldown is false or + * we must poll hw radio disable + */ + if (!want && wlc_hw->pllreq) + return; + + wlc_hw->sbclk = want; + if (!wlc_hw->sbclk) { + wlc_hw->clk = false; + if (wlc_hw->band && wlc_hw->band->pi) + wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); + } +} + +/* + * Return true if radio is disabled, otherwise false. + * hw radio disable signal is an external pin, users activate it asynchronously + * this function could be called when driver is down and w/o clock + * it operates on different registers depending on corerev and boardflag. + */ +static bool brcms_b_radio_read_hwdisabled(struct brcms_hardware *wlc_hw) +{ + bool v, clk, xtal; + u32 flags = 0; + + xtal = wlc_hw->sbclk; + if (!xtal) + brcms_b_xtal(wlc_hw, ON); + + /* may need to take core out of reset first */ + clk = wlc_hw->clk; + if (!clk) { + /* + * mac no longer enables phyclk automatically when driver + * accesses phyreg throughput mac. This can be skipped since + * only mac reg is accessed below + */ + if (D11REV_GE(wlc_hw->corerev, 18)) + flags |= SICF_PCLKE; + + /* + * TODO: test suspend/resume + * + * AI chip doesn't restore bar0win2 on + * hibernation/resume, need sw fixup + */ + + bcma_core_enable(wlc_hw->d11core, flags); + brcms_c_mctrl_reset(wlc_hw); + } + + v = ((bcma_read32(wlc_hw->d11core, + D11REGOFFS(phydebug)) & PDBG_RFD) != 0); + + /* put core back into reset */ + if (!clk) + bcma_core_disable(wlc_hw->d11core, 0); + + if (!xtal) + brcms_b_xtal(wlc_hw, OFF); + + return v; +} + +static bool wlc_dma_rxreset(struct brcms_hardware *wlc_hw, uint fifo) +{ + struct dma_pub *di = wlc_hw->di[fifo]; + return dma_rxreset(di); +} + +/* d11 core reset + * ensure fask clock during reset + * reset dma + * reset d11(out of reset) + * reset phy(out of reset) + * clear software macintstatus for fresh new start + * one testing hack wlc_hw->noreset will bypass the d11/phy reset + */ +void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags) +{ + uint i; + bool fastclk; + + if (flags == BRCMS_USE_COREFLAGS) + flags = (wlc_hw->band->pi ? wlc_hw->band->core_flags : 0); + + brcms_dbg_info(wlc_hw->d11core, "wl%d: core reset\n", wlc_hw->unit); + + /* request FAST clock if not on */ + fastclk = wlc_hw->forcefastclk; + if (!fastclk) + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); + + /* reset the dma engines except first time thru */ + if (bcma_core_is_enabled(wlc_hw->d11core)) { + for (i = 0; i < NFIFO; i++) + if ((wlc_hw->di[i]) && (!dma_txreset(wlc_hw->di[i]))) + brcms_err(wlc_hw->d11core, "wl%d: %s: " + "dma_txreset[%d]: cannot stop dma\n", + wlc_hw->unit, __func__, i); + + if ((wlc_hw->di[RX_FIFO]) + && (!wlc_dma_rxreset(wlc_hw, RX_FIFO))) + brcms_err(wlc_hw->d11core, "wl%d: %s: dma_rxreset" + "[%d]: cannot stop dma\n", + wlc_hw->unit, __func__, RX_FIFO); + } + /* if noreset, just stop the psm and return */ + if (wlc_hw->noreset) { + wlc_hw->wlc->macintstatus = 0; /* skip wl_dpc after down */ + brcms_b_mctrl(wlc_hw, MCTL_PSM_RUN | MCTL_EN_MAC, 0); + return; + } + + /* + * mac no longer enables phyclk automatically when driver accesses + * phyreg throughput mac, AND phy_reset is skipped at early stage when + * band->pi is invalid. need to enable PHY CLK + */ + if (D11REV_GE(wlc_hw->corerev, 18)) + flags |= SICF_PCLKE; + + /* + * reset the core + * In chips with PMU, the fastclk request goes through d11 core + * reg 0x1e0, which is cleared by the core_reset. have to re-request it. + * + * This adds some delay and we can optimize it by also requesting + * fastclk through chipcommon during this period if necessary. But + * that has to work coordinate with other driver like mips/arm since + * they may touch chipcommon as well. + */ + wlc_hw->clk = false; + bcma_core_enable(wlc_hw->d11core, flags); + wlc_hw->clk = true; + if (wlc_hw->band && wlc_hw->band->pi) + wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, true); + + brcms_c_mctrl_reset(wlc_hw); + + if (ai_get_cccaps(wlc_hw->sih) & CC_CAP_PMU) + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); + + brcms_b_phy_reset(wlc_hw); + + /* turn on PHY_PLL */ + brcms_b_core_phypll_ctl(wlc_hw, true); + + /* clear sw intstatus */ + wlc_hw->wlc->macintstatus = 0; + + /* restore the clk setting */ + if (!fastclk) + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); +} + +/* txfifo sizes needs to be modified(increased) since the newer cores + * have more memory. + */ +static void brcms_b_corerev_fifofixup(struct brcms_hardware *wlc_hw) +{ + struct bcma_device *core = wlc_hw->d11core; + u16 fifo_nu; + u16 txfifo_startblk = TXFIFO_START_BLK, txfifo_endblk; + u16 txfifo_def, txfifo_def1; + u16 txfifo_cmd; + + /* tx fifos start at TXFIFO_START_BLK from the Base address */ + txfifo_startblk = TXFIFO_START_BLK; + + /* sequence of operations: reset fifo, set fifo size, reset fifo */ + for (fifo_nu = 0; fifo_nu < NFIFO; fifo_nu++) { + + txfifo_endblk = txfifo_startblk + wlc_hw->xmtfifo_sz[fifo_nu]; + txfifo_def = (txfifo_startblk & 0xff) | + (((txfifo_endblk - 1) & 0xff) << TXFIFO_FIFOTOP_SHIFT); + txfifo_def1 = ((txfifo_startblk >> 8) & 0x1) | + ((((txfifo_endblk - + 1) >> 8) & 0x1) << TXFIFO_FIFOTOP_SHIFT); + txfifo_cmd = + TXFIFOCMD_RESET_MASK | (fifo_nu << TXFIFOCMD_FIFOSEL_SHIFT); + + bcma_write16(core, D11REGOFFS(xmtfifocmd), txfifo_cmd); + bcma_write16(core, D11REGOFFS(xmtfifodef), txfifo_def); + bcma_write16(core, D11REGOFFS(xmtfifodef1), txfifo_def1); + + bcma_write16(core, D11REGOFFS(xmtfifocmd), txfifo_cmd); + + txfifo_startblk += wlc_hw->xmtfifo_sz[fifo_nu]; + } + /* + * need to propagate to shm location to be in sync since ucode/hw won't + * do this + */ + brcms_b_write_shm(wlc_hw, M_FIFOSIZE0, + wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]); + brcms_b_write_shm(wlc_hw, M_FIFOSIZE1, + wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]); + brcms_b_write_shm(wlc_hw, M_FIFOSIZE2, + ((wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO] << 8) | wlc_hw-> + xmtfifo_sz[TX_AC_BK_FIFO])); + brcms_b_write_shm(wlc_hw, M_FIFOSIZE3, + ((wlc_hw->xmtfifo_sz[TX_ATIM_FIFO] << 8) | wlc_hw-> + xmtfifo_sz[TX_BCMC_FIFO])); +} + +/* This function is used for changing the tsf frac register + * If spur avoidance mode is off, the mac freq will be 80/120/160Mhz + * If spur avoidance mode is on1, the mac freq will be 82/123/164Mhz + * If spur avoidance mode is on2, the mac freq will be 84/126/168Mhz + * HTPHY Formula is 2^26/freq(MHz) e.g. + * For spuron2 - 126MHz -> 2^26/126 = 532610.0 + * - 532610 = 0x82082 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x2082 + * For spuron: 123MHz -> 2^26/123 = 545600.5 + * - 545601 = 0x85341 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x5341 + * For spur off: 120MHz -> 2^26/120 = 559240.5 + * - 559241 = 0x88889 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x8889 + */ + +void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode) +{ + struct bcma_device *core = wlc_hw->d11core; + + if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43224) || + (ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43225)) { + if (spurmode == WL_SPURAVOID_ON2) { /* 126Mhz */ + bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x2082); + bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); + } else if (spurmode == WL_SPURAVOID_ON1) { /* 123Mhz */ + bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x5341); + bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); + } else { /* 120Mhz */ + bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x8889); + bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); + } + } else if (BRCMS_ISLCNPHY(wlc_hw->band)) { + if (spurmode == WL_SPURAVOID_ON1) { /* 82Mhz */ + bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x7CE0); + bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0xC); + } else { /* 80Mhz */ + bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0xCCCD); + bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0xC); + } + } +} + +void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr) +{ + memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr)); + wlc->bsscfg->type = BRCMS_TYPE_STATION; +} + +void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, const u8 *bssid, + u8 *ssid, size_t ssid_len) +{ + brcms_c_set_ssid(wlc, ssid, ssid_len); + + memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr)); + memcpy(wlc->bsscfg->BSSID, bssid, sizeof(wlc->bsscfg->BSSID)); + wlc->bsscfg->type = BRCMS_TYPE_AP; + + brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, MCTL_AP | MCTL_INFRA); +} + +void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr) +{ + memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr)); + wlc->bsscfg->type = BRCMS_TYPE_ADHOC; + + brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, 0); +} + +/* Initialize GPIOs that are controlled by D11 core */ +static void brcms_c_gpio_init(struct brcms_c_info *wlc) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + u32 gc, gm; + + /* use GPIO select 0 to get all gpio signals from the gpio out reg */ + brcms_b_mctrl(wlc_hw, MCTL_GPOUT_SEL_MASK, 0); + + /* + * Common GPIO setup: + * G0 = LED 0 = WLAN Activity + * G1 = LED 1 = WLAN 2.4 GHz Radio State + * G2 = LED 2 = WLAN 5 GHz Radio State + * G4 = radio disable input (HI enabled, LO disabled) + */ + + gc = gm = 0; + + /* Allocate GPIOs for mimo antenna diversity feature */ + if (wlc_hw->antsel_type == ANTSEL_2x3) { + /* Enable antenna diversity, use 2x3 mode */ + brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN, + MHF3_ANTSEL_EN, BRCM_BAND_ALL); + brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, + MHF3_ANTSEL_MODE, BRCM_BAND_ALL); + + /* init superswitch control */ + wlc_phy_antsel_init(wlc_hw->band->pi, false); + + } else if (wlc_hw->antsel_type == ANTSEL_2x4) { + gm |= gc |= (BOARD_GPIO_12 | BOARD_GPIO_13); + /* + * The board itself is powered by these GPIOs + * (when not sending pattern) so set them high + */ + bcma_set16(wlc_hw->d11core, D11REGOFFS(psm_gpio_oe), + (BOARD_GPIO_12 | BOARD_GPIO_13)); + bcma_set16(wlc_hw->d11core, D11REGOFFS(psm_gpio_out), + (BOARD_GPIO_12 | BOARD_GPIO_13)); + + /* Enable antenna diversity, use 2x4 mode */ + brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN, + MHF3_ANTSEL_EN, BRCM_BAND_ALL); + brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, 0, + BRCM_BAND_ALL); + + /* Configure the desired clock to be 4Mhz */ + brcms_b_write_shm(wlc_hw, M_ANTSEL_CLKDIV, + ANTSEL_CLKDIV_4MHZ); + } + + /* + * gpio 9 controls the PA. ucode is responsible + * for wiggling out and oe + */ + if (wlc_hw->boardflags & BFL_PACTRL) + gm |= gc |= BOARD_GPIO_PACTRL; + + /* apply to gpiocontrol register */ + bcma_chipco_gpio_control(&wlc_hw->d11core->bus->drv_cc, gm, gc); +} + +static void brcms_ucode_write(struct brcms_hardware *wlc_hw, + const __le32 ucode[], const size_t nbytes) +{ + struct bcma_device *core = wlc_hw->d11core; + uint i; + uint count; + + brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); + + count = (nbytes / sizeof(u32)); + + bcma_write32(core, D11REGOFFS(objaddr), + OBJADDR_AUTO_INC | OBJADDR_UCM_SEL); + (void)bcma_read32(core, D11REGOFFS(objaddr)); + for (i = 0; i < count; i++) + bcma_write32(core, D11REGOFFS(objdata), le32_to_cpu(ucode[i])); + +} + +static void brcms_ucode_download(struct brcms_hardware *wlc_hw) +{ + struct brcms_c_info *wlc; + struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; + + wlc = wlc_hw->wlc; + + if (wlc_hw->ucode_loaded) + return; + + if (D11REV_IS(wlc_hw->corerev, 17) || D11REV_IS(wlc_hw->corerev, 23)) { + if (BRCMS_ISNPHY(wlc_hw->band)) { + brcms_ucode_write(wlc_hw, ucode->bcm43xx_16_mimo, + ucode->bcm43xx_16_mimosz); + wlc_hw->ucode_loaded = true; + } else + brcms_err(wlc_hw->d11core, + "%s: wl%d: unsupported phy in corerev %d\n", + __func__, wlc_hw->unit, wlc_hw->corerev); + } else if (D11REV_IS(wlc_hw->corerev, 24)) { + if (BRCMS_ISLCNPHY(wlc_hw->band)) { + brcms_ucode_write(wlc_hw, ucode->bcm43xx_24_lcn, + ucode->bcm43xx_24_lcnsz); + wlc_hw->ucode_loaded = true; + } else { + brcms_err(wlc_hw->d11core, + "%s: wl%d: unsupported phy in corerev %d\n", + __func__, wlc_hw->unit, wlc_hw->corerev); + } + } +} + +void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant) +{ + /* update sw state */ + wlc_hw->bmac_phytxant = phytxant; + + /* push to ucode if up */ + if (!wlc_hw->up) + return; + brcms_c_ucode_txant_set(wlc_hw); + +} + +u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw) +{ + return (u16) wlc_hw->wlc->stf->txant; +} + +void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type) +{ + wlc_hw->antsel_type = antsel_type; + + /* Update the antsel type for phy module to use */ + wlc_phy_antsel_type_set(wlc_hw->band->pi, antsel_type); +} + +static void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw) +{ + bool fatal = false; + uint unit; + uint intstatus, idx; + struct bcma_device *core = wlc_hw->d11core; + + unit = wlc_hw->unit; + + for (idx = 0; idx < NFIFO; idx++) { + /* read intstatus register and ignore any non-error bits */ + intstatus = + bcma_read32(core, + D11REGOFFS(intctrlregs[idx].intstatus)) & + I_ERRORS; + if (!intstatus) + continue; + + brcms_dbg_int(core, "wl%d: intstatus%d 0x%x\n", + unit, idx, intstatus); + + if (intstatus & I_RO) { + brcms_err(core, "wl%d: fifo %d: receive fifo " + "overflow\n", unit, idx); + fatal = true; + } + + if (intstatus & I_PC) { + brcms_err(core, "wl%d: fifo %d: descriptor error\n", + unit, idx); + fatal = true; + } + + if (intstatus & I_PD) { + brcms_err(core, "wl%d: fifo %d: data error\n", unit, + idx); + fatal = true; + } + + if (intstatus & I_DE) { + brcms_err(core, "wl%d: fifo %d: descriptor protocol " + "error\n", unit, idx); + fatal = true; + } + + if (intstatus & I_RU) + brcms_err(core, "wl%d: fifo %d: receive descriptor " + "underflow\n", idx, unit); + + if (intstatus & I_XU) { + brcms_err(core, "wl%d: fifo %d: transmit fifo " + "underflow\n", idx, unit); + fatal = true; + } + + if (fatal) { + brcms_fatal_error(wlc_hw->wlc->wl); /* big hammer */ + break; + } else + bcma_write32(core, + D11REGOFFS(intctrlregs[idx].intstatus), + intstatus); + } +} + +void brcms_c_intrson(struct brcms_c_info *wlc) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + wlc->macintmask = wlc->defmacintmask; + bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), wlc->macintmask); +} + +u32 brcms_c_intrsoff(struct brcms_c_info *wlc) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + u32 macintmask; + + if (!wlc_hw->clk) + return 0; + + macintmask = wlc->macintmask; /* isr can still happen */ + + bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), 0); + (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(macintmask)); + udelay(1); /* ensure int line is no longer driven */ + wlc->macintmask = 0; + + /* return previous macintmask; resolve race between us and our isr */ + return wlc->macintstatus ? 0 : macintmask; +} + +void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + if (!wlc_hw->clk) + return; + + wlc->macintmask = macintmask; + bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), wlc->macintmask); +} + +/* assumes that the d11 MAC is enabled */ +static void brcms_b_tx_fifo_suspend(struct brcms_hardware *wlc_hw, + uint tx_fifo) +{ + u8 fifo = 1 << tx_fifo; + + /* Two clients of this code, 11h Quiet period and scanning. */ + + /* only suspend if not already suspended */ + if ((wlc_hw->suspended_fifos & fifo) == fifo) + return; + + /* force the core awake only if not already */ + if (wlc_hw->suspended_fifos == 0) + brcms_c_ucode_wake_override_set(wlc_hw, + BRCMS_WAKE_OVERRIDE_TXFIFO); + + wlc_hw->suspended_fifos |= fifo; + + if (wlc_hw->di[tx_fifo]) { + /* + * Suspending AMPDU transmissions in the middle can cause + * underflow which may result in mismatch between ucode and + * driver so suspend the mac before suspending the FIFO + */ + if (BRCMS_PHY_11N_CAP(wlc_hw->band)) + brcms_c_suspend_mac_and_wait(wlc_hw->wlc); + + dma_txsuspend(wlc_hw->di[tx_fifo]); + + if (BRCMS_PHY_11N_CAP(wlc_hw->band)) + brcms_c_enable_mac(wlc_hw->wlc); + } +} + +static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw, + uint tx_fifo) +{ + /* BMAC_NOTE: BRCMS_TX_FIFO_ENAB is done in brcms_c_dpc() for DMA case + * but need to be done here for PIO otherwise the watchdog will catch + * the inconsistency and fire + */ + /* Two clients of this code, 11h Quiet period and scanning. */ + if (wlc_hw->di[tx_fifo]) + dma_txresume(wlc_hw->di[tx_fifo]); + + /* allow core to sleep again */ + if (wlc_hw->suspended_fifos == 0) + return; + else { + wlc_hw->suspended_fifos &= ~(1 << tx_fifo); + if (wlc_hw->suspended_fifos == 0) + brcms_c_ucode_wake_override_clear(wlc_hw, + BRCMS_WAKE_OVERRIDE_TXFIFO); + } +} + +/* precondition: requires the mac core to be enabled */ +static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx) +{ + static const u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; + u8 *ethaddr = wlc_hw->wlc->pub->cur_etheraddr; + + if (mute_tx) { + /* suspend tx fifos */ + brcms_b_tx_fifo_suspend(wlc_hw, TX_DATA_FIFO); + brcms_b_tx_fifo_suspend(wlc_hw, TX_CTL_FIFO); + brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_BK_FIFO); + brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_VI_FIFO); + + /* zero the address match register so we do not send ACKs */ + brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, null_ether_addr); + } else { + /* resume tx fifos */ + brcms_b_tx_fifo_resume(wlc_hw, TX_DATA_FIFO); + brcms_b_tx_fifo_resume(wlc_hw, TX_CTL_FIFO); + brcms_b_tx_fifo_resume(wlc_hw, TX_AC_BK_FIFO); + brcms_b_tx_fifo_resume(wlc_hw, TX_AC_VI_FIFO); + + /* Restore address */ + brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, ethaddr); + } + + wlc_phy_mute_upd(wlc_hw->band->pi, mute_tx, 0); + + if (mute_tx) + brcms_c_ucode_mute_override_set(wlc_hw); + else + brcms_c_ucode_mute_override_clear(wlc_hw); +} + +void +brcms_c_mute(struct brcms_c_info *wlc, bool mute_tx) +{ + brcms_b_mute(wlc->hw, mute_tx); +} + +/* + * Read and clear macintmask and macintstatus and intstatus registers. + * This routine should be called with interrupts off + * Return: + * -1 if brcms_deviceremoved(wlc) evaluates to true; + * 0 if the interrupt is not for us, or we are in some special cases; + * device interrupt status bits otherwise. + */ +static inline u32 wlc_intstatus(struct brcms_c_info *wlc, bool in_isr) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + struct bcma_device *core = wlc_hw->d11core; + u32 macintstatus, mask; + + /* macintstatus includes a DMA interrupt summary bit */ + macintstatus = bcma_read32(core, D11REGOFFS(macintstatus)); + mask = in_isr ? wlc->macintmask : wlc->defmacintmask; + + trace_brcms_macintstatus(&core->dev, in_isr, macintstatus, mask); + + /* detect cardbus removed, in power down(suspend) and in reset */ + if (brcms_deviceremoved(wlc)) + return -1; + + /* brcms_deviceremoved() succeeds even when the core is still resetting, + * handle that case here. + */ + if (macintstatus == 0xffffffff) + return 0; + + /* defer unsolicited interrupts */ + macintstatus &= mask; + + /* if not for us */ + if (macintstatus == 0) + return 0; + + /* turn off the interrupts */ + bcma_write32(core, D11REGOFFS(macintmask), 0); + (void)bcma_read32(core, D11REGOFFS(macintmask)); + wlc->macintmask = 0; + + /* clear device interrupts */ + bcma_write32(core, D11REGOFFS(macintstatus), macintstatus); + + /* MI_DMAINT is indication of non-zero intstatus */ + if (macintstatus & MI_DMAINT) + /* + * only fifo interrupt enabled is I_RI in + * RX_FIFO. If MI_DMAINT is set, assume it + * is set and clear the interrupt. + */ + bcma_write32(core, D11REGOFFS(intctrlregs[RX_FIFO].intstatus), + DEF_RXINTMASK); + + return macintstatus; +} + +/* Update wlc->macintstatus and wlc->intstatus[]. */ +/* Return true if they are updated successfully. false otherwise */ +bool brcms_c_intrsupd(struct brcms_c_info *wlc) +{ + u32 macintstatus; + + /* read and clear macintstatus and intstatus registers */ + macintstatus = wlc_intstatus(wlc, false); + + /* device is removed */ + if (macintstatus == 0xffffffff) + return false; + + /* update interrupt status in software */ + wlc->macintstatus |= macintstatus; + + return true; +} + +/* + * First-level interrupt processing. + * Return true if this was our interrupt + * and if further brcms_c_dpc() processing is required, + * false otherwise. + */ +bool brcms_c_isr(struct brcms_c_info *wlc) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + u32 macintstatus; + + if (!wlc_hw->up || !wlc->macintmask) + return false; + + /* read and clear macintstatus and intstatus registers */ + macintstatus = wlc_intstatus(wlc, true); + + if (macintstatus == 0xffffffff) { + brcms_err(wlc_hw->d11core, + "DEVICEREMOVED detected in the ISR code path\n"); + return false; + } + + /* it is not for us */ + if (macintstatus == 0) + return false; + + /* save interrupt status bits */ + wlc->macintstatus = macintstatus; + + return true; + +} + +void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + struct bcma_device *core = wlc_hw->d11core; + u32 mc, mi; + + brcms_dbg_mac80211(core, "wl%d: bandunit %d\n", wlc_hw->unit, + wlc_hw->band->bandunit); + + /* + * Track overlapping suspend requests + */ + wlc_hw->mac_suspend_depth++; + if (wlc_hw->mac_suspend_depth > 1) + return; + + /* force the core awake */ + brcms_c_ucode_wake_override_set(wlc_hw, BRCMS_WAKE_OVERRIDE_MACSUSPEND); + + mc = bcma_read32(core, D11REGOFFS(maccontrol)); + + if (mc == 0xffffffff) { + brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, + __func__); + brcms_down(wlc->wl); + return; + } + WARN_ON(mc & MCTL_PSM_JMP_0); + WARN_ON(!(mc & MCTL_PSM_RUN)); + WARN_ON(!(mc & MCTL_EN_MAC)); + + mi = bcma_read32(core, D11REGOFFS(macintstatus)); + if (mi == 0xffffffff) { + brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, + __func__); + brcms_down(wlc->wl); + return; + } + WARN_ON(mi & MI_MACSSPNDD); + + brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, 0); + + SPINWAIT(!(bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD), + BRCMS_MAX_MAC_SUSPEND); + + if (!(bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD)) { + brcms_err(core, "wl%d: wlc_suspend_mac_and_wait: waited %d uS" + " and MI_MACSSPNDD is still not on.\n", + wlc_hw->unit, BRCMS_MAX_MAC_SUSPEND); + brcms_err(core, "wl%d: psmdebug 0x%08x, phydebug 0x%08x, " + "psm_brc 0x%04x\n", wlc_hw->unit, + bcma_read32(core, D11REGOFFS(psmdebug)), + bcma_read32(core, D11REGOFFS(phydebug)), + bcma_read16(core, D11REGOFFS(psm_brc))); + } + + mc = bcma_read32(core, D11REGOFFS(maccontrol)); + if (mc == 0xffffffff) { + brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, + __func__); + brcms_down(wlc->wl); + return; + } + WARN_ON(mc & MCTL_PSM_JMP_0); + WARN_ON(!(mc & MCTL_PSM_RUN)); + WARN_ON(mc & MCTL_EN_MAC); +} + +void brcms_c_enable_mac(struct brcms_c_info *wlc) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + struct bcma_device *core = wlc_hw->d11core; + u32 mc, mi; + + brcms_dbg_mac80211(core, "wl%d: bandunit %d\n", wlc_hw->unit, + wlc->band->bandunit); + + /* + * Track overlapping suspend requests + */ + wlc_hw->mac_suspend_depth--; + if (wlc_hw->mac_suspend_depth > 0) + return; + + mc = bcma_read32(core, D11REGOFFS(maccontrol)); + WARN_ON(mc & MCTL_PSM_JMP_0); + WARN_ON(mc & MCTL_EN_MAC); + WARN_ON(!(mc & MCTL_PSM_RUN)); + + brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, MCTL_EN_MAC); + bcma_write32(core, D11REGOFFS(macintstatus), MI_MACSSPNDD); + + mc = bcma_read32(core, D11REGOFFS(maccontrol)); + WARN_ON(mc & MCTL_PSM_JMP_0); + WARN_ON(!(mc & MCTL_EN_MAC)); + WARN_ON(!(mc & MCTL_PSM_RUN)); + + mi = bcma_read32(core, D11REGOFFS(macintstatus)); + WARN_ON(mi & MI_MACSSPNDD); + + brcms_c_ucode_wake_override_clear(wlc_hw, + BRCMS_WAKE_OVERRIDE_MACSUSPEND); +} + +void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode) +{ + wlc_hw->hw_stf_ss_opmode = stf_mode; + + if (wlc_hw->clk) + brcms_upd_ofdm_pctl1_table(wlc_hw); +} + +static bool brcms_b_validate_chip_access(struct brcms_hardware *wlc_hw) +{ + struct bcma_device *core = wlc_hw->d11core; + u32 w, val; + struct wiphy *wiphy = wlc_hw->wlc->wiphy; + + /* Validate dchip register access */ + + bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); + (void)bcma_read32(core, D11REGOFFS(objaddr)); + w = bcma_read32(core, D11REGOFFS(objdata)); + + /* Can we write and read back a 32bit register? */ + bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); + (void)bcma_read32(core, D11REGOFFS(objaddr)); + bcma_write32(core, D11REGOFFS(objdata), (u32) 0xaa5555aa); + + bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); + (void)bcma_read32(core, D11REGOFFS(objaddr)); + val = bcma_read32(core, D11REGOFFS(objdata)); + if (val != (u32) 0xaa5555aa) { + wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, " + "expected 0xaa5555aa\n", wlc_hw->unit, val); + return false; + } + + bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); + (void)bcma_read32(core, D11REGOFFS(objaddr)); + bcma_write32(core, D11REGOFFS(objdata), (u32) 0x55aaaa55); + + bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); + (void)bcma_read32(core, D11REGOFFS(objaddr)); + val = bcma_read32(core, D11REGOFFS(objdata)); + if (val != (u32) 0x55aaaa55) { + wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, " + "expected 0x55aaaa55\n", wlc_hw->unit, val); + return false; + } + + bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); + (void)bcma_read32(core, D11REGOFFS(objaddr)); + bcma_write32(core, D11REGOFFS(objdata), w); + + /* clear CFPStart */ + bcma_write32(core, D11REGOFFS(tsf_cfpstart), 0); + + w = bcma_read32(core, D11REGOFFS(maccontrol)); + if ((w != (MCTL_IHR_EN | MCTL_WAKE)) && + (w != (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE))) { + wiphy_err(wiphy, "wl%d: validate_chip_access: maccontrol = " + "0x%x, expected 0x%x or 0x%x\n", wlc_hw->unit, w, + (MCTL_IHR_EN | MCTL_WAKE), + (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE)); + return false; + } + + return true; +} + +#define PHYPLL_WAIT_US 100000 + +void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on) +{ + struct bcma_device *core = wlc_hw->d11core; + u32 tmp; + + brcms_dbg_info(core, "wl%d\n", wlc_hw->unit); + + tmp = 0; + + if (on) { + if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM4313)) { + bcma_set32(core, D11REGOFFS(clk_ctl_st), + CCS_ERSRC_REQ_HT | + CCS_ERSRC_REQ_D11PLL | + CCS_ERSRC_REQ_PHYPLL); + SPINWAIT((bcma_read32(core, D11REGOFFS(clk_ctl_st)) & + CCS_ERSRC_AVAIL_HT) != CCS_ERSRC_AVAIL_HT, + PHYPLL_WAIT_US); + + tmp = bcma_read32(core, D11REGOFFS(clk_ctl_st)); + if ((tmp & CCS_ERSRC_AVAIL_HT) != CCS_ERSRC_AVAIL_HT) + brcms_err(core, "%s: turn on PHY PLL failed\n", + __func__); + } else { + bcma_set32(core, D11REGOFFS(clk_ctl_st), + tmp | CCS_ERSRC_REQ_D11PLL | + CCS_ERSRC_REQ_PHYPLL); + SPINWAIT((bcma_read32(core, D11REGOFFS(clk_ctl_st)) & + (CCS_ERSRC_AVAIL_D11PLL | + CCS_ERSRC_AVAIL_PHYPLL)) != + (CCS_ERSRC_AVAIL_D11PLL | + CCS_ERSRC_AVAIL_PHYPLL), PHYPLL_WAIT_US); + + tmp = bcma_read32(core, D11REGOFFS(clk_ctl_st)); + if ((tmp & + (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) + != + (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) + brcms_err(core, "%s: turn on PHY PLL failed\n", + __func__); + } + } else { + /* + * Since the PLL may be shared, other cores can still + * be requesting it; so we'll deassert the request but + * not wait for status to comply. + */ + bcma_mask32(core, D11REGOFFS(clk_ctl_st), + ~CCS_ERSRC_REQ_PHYPLL); + (void)bcma_read32(core, D11REGOFFS(clk_ctl_st)); + } +} + +static void brcms_c_coredisable(struct brcms_hardware *wlc_hw) +{ + bool dev_gone; + + brcms_dbg_info(wlc_hw->d11core, "wl%d: disable core\n", wlc_hw->unit); + + dev_gone = brcms_deviceremoved(wlc_hw->wlc); + + if (dev_gone) + return; + + if (wlc_hw->noreset) + return; + + /* radio off */ + wlc_phy_switch_radio(wlc_hw->band->pi, OFF); + + /* turn off analog core */ + wlc_phy_anacore(wlc_hw->band->pi, OFF); + + /* turn off PHYPLL to save power */ + brcms_b_core_phypll_ctl(wlc_hw, false); + + wlc_hw->clk = false; + bcma_core_disable(wlc_hw->d11core, 0); + wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); +} + +static void brcms_c_flushqueues(struct brcms_c_info *wlc) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + uint i; + + /* free any posted tx packets */ + for (i = 0; i < NFIFO; i++) { + if (wlc_hw->di[i]) { + dma_txreclaim(wlc_hw->di[i], DMA_RANGE_ALL); + if (i < TX_BCMC_FIFO) + ieee80211_wake_queue(wlc->pub->ieee_hw, + brcms_fifo_to_ac(i)); + } + } + + /* free any posted rx packets */ + dma_rxreclaim(wlc_hw->di[RX_FIFO]); +} + +static u16 +brcms_b_read_objmem(struct brcms_hardware *wlc_hw, uint offset, u32 sel) +{ + struct bcma_device *core = wlc_hw->d11core; + u16 objoff = D11REGOFFS(objdata); + + bcma_write32(core, D11REGOFFS(objaddr), sel | (offset >> 2)); + (void)bcma_read32(core, D11REGOFFS(objaddr)); + if (offset & 2) + objoff += 2; + + return bcma_read16(core, objoff); +} + +static void +brcms_b_write_objmem(struct brcms_hardware *wlc_hw, uint offset, u16 v, + u32 sel) +{ + struct bcma_device *core = wlc_hw->d11core; + u16 objoff = D11REGOFFS(objdata); + + bcma_write32(core, D11REGOFFS(objaddr), sel | (offset >> 2)); + (void)bcma_read32(core, D11REGOFFS(objaddr)); + if (offset & 2) + objoff += 2; + + bcma_wflush16(core, objoff, v); +} + +/* + * Read a single u16 from shared memory. + * SHM 'offset' needs to be an even address + */ +u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset) +{ + return brcms_b_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL); +} + +/* + * Write a single u16 to shared memory. + * SHM 'offset' needs to be an even address + */ +void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v) +{ + brcms_b_write_objmem(wlc_hw, offset, v, OBJADDR_SHM_SEL); +} + +/* + * Copy a buffer to shared memory of specified type . + * SHM 'offset' needs to be an even address and + * Buffer length 'len' must be an even number of bytes + * 'sel' selects the type of memory + */ +void +brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset, + const void *buf, int len, u32 sel) +{ + u16 v; + const u8 *p = (const u8 *)buf; + int i; + + if (len <= 0 || (offset & 1) || (len & 1)) + return; + + for (i = 0; i < len; i += 2) { + v = p[i] | (p[i + 1] << 8); + brcms_b_write_objmem(wlc_hw, offset + i, v, sel); + } +} + +/* + * Copy a piece of shared memory of specified type to a buffer . + * SHM 'offset' needs to be an even address and + * Buffer length 'len' must be an even number of bytes + * 'sel' selects the type of memory + */ +void +brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, void *buf, + int len, u32 sel) +{ + u16 v; + u8 *p = (u8 *) buf; + int i; + + if (len <= 0 || (offset & 1) || (len & 1)) + return; + + for (i = 0; i < len; i += 2) { + v = brcms_b_read_objmem(wlc_hw, offset + i, sel); + p[i] = v & 0xFF; + p[i + 1] = (v >> 8) & 0xFF; + } +} + +/* Copy a buffer to shared memory. + * SHM 'offset' needs to be an even address and + * Buffer length 'len' must be an even number of bytes + */ +static void brcms_c_copyto_shm(struct brcms_c_info *wlc, uint offset, + const void *buf, int len) +{ + brcms_b_copyto_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL); +} + +static void brcms_b_retrylimit_upd(struct brcms_hardware *wlc_hw, + u16 SRL, u16 LRL) +{ + wlc_hw->SRL = SRL; + wlc_hw->LRL = LRL; + + /* write retry limit to SCR, shouldn't need to suspend */ + if (wlc_hw->up) { + bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), + OBJADDR_SCR_SEL | S_DOT11_SRC_LMT); + (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); + bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), wlc_hw->SRL); + bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), + OBJADDR_SCR_SEL | S_DOT11_LRC_LMT); + (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); + bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), wlc_hw->LRL); + } +} + +static void brcms_b_pllreq(struct brcms_hardware *wlc_hw, bool set, u32 req_bit) +{ + if (set) { + if (mboolisset(wlc_hw->pllreq, req_bit)) + return; + + mboolset(wlc_hw->pllreq, req_bit); + + if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) { + if (!wlc_hw->sbclk) + brcms_b_xtal(wlc_hw, ON); + } + } else { + if (!mboolisset(wlc_hw->pllreq, req_bit)) + return; + + mboolclr(wlc_hw->pllreq, req_bit); + + if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) { + if (wlc_hw->sbclk) + brcms_b_xtal(wlc_hw, OFF); + } + } +} + +static void brcms_b_antsel_set(struct brcms_hardware *wlc_hw, u32 antsel_avail) +{ + wlc_hw->antsel_avail = antsel_avail; +} + +/* + * conditions under which the PM bit should be set in outgoing frames + * and STAY_AWAKE is meaningful + */ +static bool brcms_c_ps_allowed(struct brcms_c_info *wlc) +{ + /* not supporting PS so always return false for now */ + return false; +} + +static void brcms_c_statsupd(struct brcms_c_info *wlc) +{ + int i; + struct macstat *macstats; +#ifdef DEBUG + u16 delta; + u16 rxf0ovfl; + u16 txfunfl[NFIFO]; +#endif /* DEBUG */ + + /* if driver down, make no sense to update stats */ + if (!wlc->pub->up) + return; + + macstats = wlc->core->macstat_snapshot; + +#ifdef DEBUG + /* save last rx fifo 0 overflow count */ + rxf0ovfl = macstats->rxf0ovfl; + + /* save last tx fifo underflow count */ + for (i = 0; i < NFIFO; i++) + txfunfl[i] = macstats->txfunfl[i]; +#endif /* DEBUG */ + + /* Read mac stats from contiguous shared memory */ + brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, macstats, + sizeof(*macstats), OBJADDR_SHM_SEL); + +#ifdef DEBUG + /* check for rx fifo 0 overflow */ + delta = (u16)(macstats->rxf0ovfl - rxf0ovfl); + if (delta) + brcms_err(wlc->hw->d11core, "wl%d: %u rx fifo 0 overflows!\n", + wlc->pub->unit, delta); + + /* check for tx fifo underflows */ + for (i = 0; i < NFIFO; i++) { + delta = macstats->txfunfl[i] - txfunfl[i]; + if (delta) + brcms_err(wlc->hw->d11core, + "wl%d: %u tx fifo %d underflows!\n", + wlc->pub->unit, delta, i); + } +#endif /* DEBUG */ + + /* merge counters from dma module */ + for (i = 0; i < NFIFO; i++) { + if (wlc->hw->di[i]) + dma_counterreset(wlc->hw->di[i]); + } +} + +static void brcms_b_reset(struct brcms_hardware *wlc_hw) +{ + /* reset the core */ + if (!brcms_deviceremoved(wlc_hw->wlc)) + brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); + + /* purge the dma rings */ + brcms_c_flushqueues(wlc_hw->wlc); +} + +void brcms_c_reset(struct brcms_c_info *wlc) +{ + brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); + + /* slurp up hw mac counters before core reset */ + brcms_c_statsupd(wlc); + + /* reset our snapshot of macstat counters */ + memset(wlc->core->macstat_snapshot, 0, sizeof(struct macstat)); + + brcms_b_reset(wlc->hw); +} + +void brcms_c_init_scb(struct scb *scb) +{ + int i; + + memset(scb, 0, sizeof(struct scb)); + scb->flags = SCB_WMECAP | SCB_HTCAP; + for (i = 0; i < NUMPRIO; i++) { + scb->seqnum[i] = 0; + scb->seqctl[i] = 0xFFFF; + } + + scb->seqctl_nonqos = 0xFFFF; + scb->magic = SCB_MAGIC; +} + +/* d11 core init + * reset PSM + * download ucode/PCM + * let ucode run to suspended + * download ucode inits + * config other core registers + * init dma + */ +static void brcms_b_coreinit(struct brcms_c_info *wlc) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + struct bcma_device *core = wlc_hw->d11core; + u32 sflags; + u32 bcnint_us; + uint i = 0; + bool fifosz_fixup = false; + int err = 0; + u16 buf[NFIFO]; + struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; + + brcms_dbg_info(core, "wl%d: core init\n", wlc_hw->unit); + + /* reset PSM */ + brcms_b_mctrl(wlc_hw, ~0, (MCTL_IHR_EN | MCTL_PSM_JMP_0 | MCTL_WAKE)); + + brcms_ucode_download(wlc_hw); + /* + * FIFOSZ fixup. driver wants to controls the fifo allocation. + */ + fifosz_fixup = true; + + /* let the PSM run to the suspended state, set mode to BSS STA */ + bcma_write32(core, D11REGOFFS(macintstatus), -1); + brcms_b_mctrl(wlc_hw, ~0, + (MCTL_IHR_EN | MCTL_INFRA | MCTL_PSM_RUN | MCTL_WAKE)); + + /* wait for ucode to self-suspend after auto-init */ + SPINWAIT(((bcma_read32(core, D11REGOFFS(macintstatus)) & + MI_MACSSPNDD) == 0), 1000 * 1000); + if ((bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD) == 0) + brcms_err(core, "wl%d: wlc_coreinit: ucode did not self-" + "suspend!\n", wlc_hw->unit); + + brcms_c_gpio_init(wlc); + + sflags = bcma_aread32(core, BCMA_IOST); + + if (D11REV_IS(wlc_hw->corerev, 17) || D11REV_IS(wlc_hw->corerev, 23)) { + if (BRCMS_ISNPHY(wlc_hw->band)) + brcms_c_write_inits(wlc_hw, ucode->d11n0initvals16); + else + brcms_err(core, "%s: wl%d: unsupported phy in corerev" + " %d\n", __func__, wlc_hw->unit, + wlc_hw->corerev); + } else if (D11REV_IS(wlc_hw->corerev, 24)) { + if (BRCMS_ISLCNPHY(wlc_hw->band)) + brcms_c_write_inits(wlc_hw, ucode->d11lcn0initvals24); + else + brcms_err(core, "%s: wl%d: unsupported phy in corerev" + " %d\n", __func__, wlc_hw->unit, + wlc_hw->corerev); + } else { + brcms_err(core, "%s: wl%d: unsupported corerev %d\n", + __func__, wlc_hw->unit, wlc_hw->corerev); + } + + /* For old ucode, txfifo sizes needs to be modified(increased) */ + if (fifosz_fixup) + brcms_b_corerev_fifofixup(wlc_hw); + + /* check txfifo allocations match between ucode and driver */ + buf[TX_AC_BE_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE0); + if (buf[TX_AC_BE_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]) { + i = TX_AC_BE_FIFO; + err = -1; + } + buf[TX_AC_VI_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE1); + if (buf[TX_AC_VI_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]) { + i = TX_AC_VI_FIFO; + err = -1; + } + buf[TX_AC_BK_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE2); + buf[TX_AC_VO_FIFO] = (buf[TX_AC_BK_FIFO] >> 8) & 0xff; + buf[TX_AC_BK_FIFO] &= 0xff; + if (buf[TX_AC_BK_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BK_FIFO]) { + i = TX_AC_BK_FIFO; + err = -1; + } + if (buf[TX_AC_VO_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO]) { + i = TX_AC_VO_FIFO; + err = -1; + } + buf[TX_BCMC_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE3); + buf[TX_ATIM_FIFO] = (buf[TX_BCMC_FIFO] >> 8) & 0xff; + buf[TX_BCMC_FIFO] &= 0xff; + if (buf[TX_BCMC_FIFO] != wlc_hw->xmtfifo_sz[TX_BCMC_FIFO]) { + i = TX_BCMC_FIFO; + err = -1; + } + if (buf[TX_ATIM_FIFO] != wlc_hw->xmtfifo_sz[TX_ATIM_FIFO]) { + i = TX_ATIM_FIFO; + err = -1; + } + if (err != 0) + brcms_err(core, "wlc_coreinit: txfifo mismatch: ucode size %d" + " driver size %d index %d\n", buf[i], + wlc_hw->xmtfifo_sz[i], i); + + /* make sure we can still talk to the mac */ + WARN_ON(bcma_read32(core, D11REGOFFS(maccontrol)) == 0xffffffff); + + /* band-specific inits done by wlc_bsinit() */ + + /* Set up frame burst size and antenna swap threshold init values */ + brcms_b_write_shm(wlc_hw, M_MBURST_SIZE, MAXTXFRAMEBURST); + brcms_b_write_shm(wlc_hw, M_MAX_ANTCNT, ANTCNT); + + /* enable one rx interrupt per received frame */ + bcma_write32(core, D11REGOFFS(intrcvlazy[0]), (1 << IRL_FC_SHIFT)); + + /* set the station mode (BSS STA) */ + brcms_b_mctrl(wlc_hw, + (MCTL_INFRA | MCTL_DISCARD_PMQ | MCTL_AP), + (MCTL_INFRA | MCTL_DISCARD_PMQ)); + + /* set up Beacon interval */ + bcnint_us = 0x8000 << 10; + bcma_write32(core, D11REGOFFS(tsf_cfprep), + (bcnint_us << CFPREP_CBI_SHIFT)); + bcma_write32(core, D11REGOFFS(tsf_cfpstart), bcnint_us); + bcma_write32(core, D11REGOFFS(macintstatus), MI_GP1); + + /* write interrupt mask */ + bcma_write32(core, D11REGOFFS(intctrlregs[RX_FIFO].intmask), + DEF_RXINTMASK); + + /* allow the MAC to control the PHY clock (dynamic on/off) */ + brcms_b_macphyclk_set(wlc_hw, ON); + + /* program dynamic clock control fast powerup delay register */ + wlc->fastpwrup_dly = ai_clkctl_fast_pwrup_delay(wlc_hw->sih); + bcma_write16(core, D11REGOFFS(scc_fastpwrup_dly), wlc->fastpwrup_dly); + + /* tell the ucode the corerev */ + brcms_b_write_shm(wlc_hw, M_MACHW_VER, (u16) wlc_hw->corerev); + + /* tell the ucode MAC capabilities */ + brcms_b_write_shm(wlc_hw, M_MACHW_CAP_L, + (u16) (wlc_hw->machwcap & 0xffff)); + brcms_b_write_shm(wlc_hw, M_MACHW_CAP_H, + (u16) ((wlc_hw-> + machwcap >> 16) & 0xffff)); + + /* write retry limits to SCR, this done after PSM init */ + bcma_write32(core, D11REGOFFS(objaddr), + OBJADDR_SCR_SEL | S_DOT11_SRC_LMT); + (void)bcma_read32(core, D11REGOFFS(objaddr)); + bcma_write32(core, D11REGOFFS(objdata), wlc_hw->SRL); + bcma_write32(core, D11REGOFFS(objaddr), + OBJADDR_SCR_SEL | S_DOT11_LRC_LMT); + (void)bcma_read32(core, D11REGOFFS(objaddr)); + bcma_write32(core, D11REGOFFS(objdata), wlc_hw->LRL); + + /* write rate fallback retry limits */ + brcms_b_write_shm(wlc_hw, M_SFRMTXCNTFBRTHSD, wlc_hw->SFBL); + brcms_b_write_shm(wlc_hw, M_LFRMTXCNTFBRTHSD, wlc_hw->LFBL); + + bcma_mask16(core, D11REGOFFS(ifs_ctl), 0x0FFF); + bcma_write16(core, D11REGOFFS(ifs_aifsn), EDCF_AIFSN_MIN); + + /* init the tx dma engines */ + for (i = 0; i < NFIFO; i++) { + if (wlc_hw->di[i]) + dma_txinit(wlc_hw->di[i]); + } + + /* init the rx dma engine(s) and post receive buffers */ + dma_rxinit(wlc_hw->di[RX_FIFO]); + dma_rxfill(wlc_hw->di[RX_FIFO]); +} + +void +static brcms_b_init(struct brcms_hardware *wlc_hw, u16 chanspec) { + u32 macintmask; + bool fastclk; + struct brcms_c_info *wlc = wlc_hw->wlc; + + /* request FAST clock if not on */ + fastclk = wlc_hw->forcefastclk; + if (!fastclk) + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); + + /* disable interrupts */ + macintmask = brcms_intrsoff(wlc->wl); + + /* set up the specified band and chanspec */ + brcms_c_setxband(wlc_hw, chspec_bandunit(chanspec)); + wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec); + + /* do one-time phy inits and calibration */ + wlc_phy_cal_init(wlc_hw->band->pi); + + /* core-specific initialization */ + brcms_b_coreinit(wlc); + + /* band-specific inits */ + brcms_b_bsinit(wlc, chanspec); + + /* restore macintmask */ + brcms_intrsrestore(wlc->wl, macintmask); + + /* seed wake_override with BRCMS_WAKE_OVERRIDE_MACSUSPEND since the mac + * is suspended and brcms_c_enable_mac() will clear this override bit. + */ + mboolset(wlc_hw->wake_override, BRCMS_WAKE_OVERRIDE_MACSUSPEND); + + /* + * initialize mac_suspend_depth to 1 to match ucode + * initial suspended state + */ + wlc_hw->mac_suspend_depth = 1; + + /* restore the clk */ + if (!fastclk) + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); +} + +static void brcms_c_set_phy_chanspec(struct brcms_c_info *wlc, + u16 chanspec) +{ + /* Save our copy of the chanspec */ + wlc->chanspec = chanspec; + + /* Set the chanspec and power limits for this locale */ + brcms_c_channel_set_chanspec(wlc->cmi, chanspec, BRCMS_TXPWR_MAX); + + if (wlc->stf->ss_algosel_auto) + brcms_c_stf_ss_algo_channel_get(wlc, &wlc->stf->ss_algo_channel, + chanspec); + + brcms_c_stf_ss_update(wlc, wlc->band); +} + +static void +brcms_default_rateset(struct brcms_c_info *wlc, struct brcms_c_rateset *rs) +{ + brcms_c_rateset_default(rs, NULL, wlc->band->phytype, + wlc->band->bandtype, false, BRCMS_RATE_MASK_FULL, + (bool) (wlc->pub->_n_enab & SUPPORT_11N), + brcms_chspec_bw(wlc->default_bss->chanspec), + wlc->stf->txstreams); +} + +/* derive wlc->band->basic_rate[] table from 'rateset' */ +static void brcms_c_rate_lookup_init(struct brcms_c_info *wlc, + struct brcms_c_rateset *rateset) +{ + u8 rate; + u8 mandatory; + u8 cck_basic = 0; + u8 ofdm_basic = 0; + u8 *br = wlc->band->basic_rate; + uint i; + + /* incoming rates are in 500kbps units as in 802.11 Supported Rates */ + memset(br, 0, BRCM_MAXRATE + 1); + + /* For each basic rate in the rates list, make an entry in the + * best basic lookup. + */ + for (i = 0; i < rateset->count; i++) { + /* only make an entry for a basic rate */ + if (!(rateset->rates[i] & BRCMS_RATE_FLAG)) + continue; + + /* mask off basic bit */ + rate = (rateset->rates[i] & BRCMS_RATE_MASK); + + if (rate > BRCM_MAXRATE) { + brcms_err(wlc->hw->d11core, "brcms_c_rate_lookup_init: " + "invalid rate 0x%X in rate set\n", + rateset->rates[i]); + continue; + } + + br[rate] = rate; + } + + /* The rate lookup table now has non-zero entries for each + * basic rate, equal to the basic rate: br[basicN] = basicN + * + * To look up the best basic rate corresponding to any + * particular rate, code can use the basic_rate table + * like this + * + * basic_rate = wlc->band->basic_rate[tx_rate] + * + * Make sure there is a best basic rate entry for + * every rate by walking up the table from low rates + * to high, filling in holes in the lookup table + */ + + for (i = 0; i < wlc->band->hw_rateset.count; i++) { + rate = wlc->band->hw_rateset.rates[i]; + + if (br[rate] != 0) { + /* This rate is a basic rate. + * Keep track of the best basic rate so far by + * modulation type. + */ + if (is_ofdm_rate(rate)) + ofdm_basic = rate; + else + cck_basic = rate; + + continue; + } + + /* This rate is not a basic rate so figure out the + * best basic rate less than this rate and fill in + * the hole in the table + */ + + br[rate] = is_ofdm_rate(rate) ? ofdm_basic : cck_basic; + + if (br[rate] != 0) + continue; + + if (is_ofdm_rate(rate)) { + /* + * In 11g and 11a, the OFDM mandatory rates + * are 6, 12, and 24 Mbps + */ + if (rate >= BRCM_RATE_24M) + mandatory = BRCM_RATE_24M; + else if (rate >= BRCM_RATE_12M) + mandatory = BRCM_RATE_12M; + else + mandatory = BRCM_RATE_6M; + } else { + /* In 11b, all CCK rates are mandatory 1 - 11 Mbps */ + mandatory = rate; + } + + br[rate] = mandatory; + } +} + +static void brcms_c_bandinit_ordered(struct brcms_c_info *wlc, + u16 chanspec) +{ + struct brcms_c_rateset default_rateset; + uint parkband; + uint i, band_order[2]; + + /* + * We might have been bandlocked during down and the chip + * power-cycled (hibernate). Figure out the right band to park on + */ + if (wlc->bandlocked || wlc->pub->_nbands == 1) { + /* updated in brcms_c_bandlock() */ + parkband = wlc->band->bandunit; + band_order[0] = band_order[1] = parkband; + } else { + /* park on the band of the specified chanspec */ + parkband = chspec_bandunit(chanspec); + + /* order so that parkband initialize last */ + band_order[0] = parkband ^ 1; + band_order[1] = parkband; + } + + /* make each band operational, software state init */ + for (i = 0; i < wlc->pub->_nbands; i++) { + uint j = band_order[i]; + + wlc->band = wlc->bandstate[j]; + + brcms_default_rateset(wlc, &default_rateset); + + /* fill in hw_rate */ + brcms_c_rateset_filter(&default_rateset, &wlc->band->hw_rateset, + false, BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK, + (bool) (wlc->pub->_n_enab & SUPPORT_11N)); + + /* init basic rate lookup */ + brcms_c_rate_lookup_init(wlc, &default_rateset); + } + + /* sync up phy/radio chanspec */ + brcms_c_set_phy_chanspec(wlc, chanspec); +} + +/* + * Set or clear filtering related maccontrol bits based on + * specified filter flags + */ +void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags) +{ + u32 promisc_bits = 0; + + wlc->filter_flags = filter_flags; + + if (filter_flags & FIF_OTHER_BSS) + promisc_bits |= MCTL_PROMISC; + + if (filter_flags & FIF_BCN_PRBRESP_PROMISC) + promisc_bits |= MCTL_BCNS_PROMISC; + + if (filter_flags & FIF_FCSFAIL) + promisc_bits |= MCTL_KEEPBADFCS; + + if (filter_flags & (FIF_CONTROL | FIF_PSPOLL)) + promisc_bits |= MCTL_KEEPCONTROL; + + brcms_b_mctrl(wlc->hw, + MCTL_PROMISC | MCTL_BCNS_PROMISC | + MCTL_KEEPCONTROL | MCTL_KEEPBADFCS, + promisc_bits); +} + +/* + * ucode, hwmac update + * Channel dependent updates for ucode and hw + */ +static void brcms_c_ucode_mac_upd(struct brcms_c_info *wlc) +{ + /* enable or disable any active IBSSs depending on whether or not + * we are on the home channel + */ + if (wlc->home_chanspec == wlc_phy_chanspec_get(wlc->band->pi)) { + if (wlc->pub->associated) { + /* + * BMAC_NOTE: This is something that should be fixed + * in ucode inits. I think that the ucode inits set + * up the bcn templates and shm values with a bogus + * beacon. This should not be done in the inits. If + * ucode needs to set up a beacon for testing, the + * test routines should write it down, not expect the + * inits to populate a bogus beacon. + */ + if (BRCMS_PHY_11N_CAP(wlc->band)) + brcms_b_write_shm(wlc->hw, + M_BCN_TXTSF_OFFSET, 0); + } + } else { + /* disable an active IBSS if we are not on the home channel */ + } +} + +static void brcms_c_write_rate_shm(struct brcms_c_info *wlc, u8 rate, + u8 basic_rate) +{ + u8 phy_rate, index; + u8 basic_phy_rate, basic_index; + u16 dir_table, basic_table; + u16 basic_ptr; + + /* Shared memory address for the table we are reading */ + dir_table = is_ofdm_rate(basic_rate) ? M_RT_DIRMAP_A : M_RT_DIRMAP_B; + + /* Shared memory address for the table we are writing */ + basic_table = is_ofdm_rate(rate) ? M_RT_BBRSMAP_A : M_RT_BBRSMAP_B; + + /* + * for a given rate, the LS-nibble of the PLCP SIGNAL field is + * the index into the rate table. + */ + phy_rate = rate_info[rate] & BRCMS_RATE_MASK; + basic_phy_rate = rate_info[basic_rate] & BRCMS_RATE_MASK; + index = phy_rate & 0xf; + basic_index = basic_phy_rate & 0xf; + + /* Find the SHM pointer to the ACK rate entry by looking in the + * Direct-map Table + */ + basic_ptr = brcms_b_read_shm(wlc->hw, (dir_table + basic_index * 2)); + + /* Update the SHM BSS-basic-rate-set mapping table with the pointer + * to the correct basic rate for the given incoming rate + */ + brcms_b_write_shm(wlc->hw, (basic_table + index * 2), basic_ptr); +} + +static const struct brcms_c_rateset * +brcms_c_rateset_get_hwrs(struct brcms_c_info *wlc) +{ + const struct brcms_c_rateset *rs_dflt; + + if (BRCMS_PHY_11N_CAP(wlc->band)) { + if (wlc->band->bandtype == BRCM_BAND_5G) + rs_dflt = &ofdm_mimo_rates; + else + rs_dflt = &cck_ofdm_mimo_rates; + } else if (wlc->band->gmode) + rs_dflt = &cck_ofdm_rates; + else + rs_dflt = &cck_rates; + + return rs_dflt; +} + +static void brcms_c_set_ratetable(struct brcms_c_info *wlc) +{ + const struct brcms_c_rateset *rs_dflt; + struct brcms_c_rateset rs; + u8 rate, basic_rate; + uint i; + + rs_dflt = brcms_c_rateset_get_hwrs(wlc); + + brcms_c_rateset_copy(rs_dflt, &rs); + brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); + + /* walk the phy rate table and update SHM basic rate lookup table */ + for (i = 0; i < rs.count; i++) { + rate = rs.rates[i] & BRCMS_RATE_MASK; + + /* for a given rate brcms_basic_rate returns the rate at + * which a response ACK/CTS should be sent. + */ + basic_rate = brcms_basic_rate(wlc, rate); + if (basic_rate == 0) + /* This should only happen if we are using a + * restricted rateset. + */ + basic_rate = rs.rates[0] & BRCMS_RATE_MASK; + + brcms_c_write_rate_shm(wlc, rate, basic_rate); + } +} + +/* band-specific init */ +static void brcms_c_bsinit(struct brcms_c_info *wlc) +{ + brcms_dbg_info(wlc->hw->d11core, "wl%d: bandunit %d\n", + wlc->pub->unit, wlc->band->bandunit); + + /* write ucode ACK/CTS rate table */ + brcms_c_set_ratetable(wlc); + + /* update some band specific mac configuration */ + brcms_c_ucode_mac_upd(wlc); + + /* init antenna selection */ + brcms_c_antsel_init(wlc->asi); + +} + +/* formula: IDLE_BUSY_RATIO_X_16 = (100-duty_cycle)/duty_cycle*16 */ +static int +brcms_c_duty_cycle_set(struct brcms_c_info *wlc, int duty_cycle, bool isOFDM, + bool writeToShm) +{ + int idle_busy_ratio_x_16 = 0; + uint offset = + isOFDM ? M_TX_IDLE_BUSY_RATIO_X_16_OFDM : + M_TX_IDLE_BUSY_RATIO_X_16_CCK; + if (duty_cycle > 100 || duty_cycle < 0) { + brcms_err(wlc->hw->d11core, + "wl%d: duty cycle value off limit\n", + wlc->pub->unit); + return -EINVAL; + } + if (duty_cycle) + idle_busy_ratio_x_16 = (100 - duty_cycle) * 16 / duty_cycle; + /* Only write to shared memory when wl is up */ + if (writeToShm) + brcms_b_write_shm(wlc->hw, offset, (u16) idle_busy_ratio_x_16); + + if (isOFDM) + wlc->tx_duty_cycle_ofdm = (u16) duty_cycle; + else + wlc->tx_duty_cycle_cck = (u16) duty_cycle; + + return 0; +} + +/* push sw hps and wake state through hardware */ +static void brcms_c_set_ps_ctrl(struct brcms_c_info *wlc) +{ + u32 v1, v2; + bool hps; + bool awake_before; + + hps = brcms_c_ps_allowed(wlc); + + brcms_dbg_mac80211(wlc->hw->d11core, "wl%d: hps %d\n", wlc->pub->unit, + hps); + + v1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); + v2 = MCTL_WAKE; + if (hps) + v2 |= MCTL_HPS; + + brcms_b_mctrl(wlc->hw, MCTL_WAKE | MCTL_HPS, v2); + + awake_before = ((v1 & MCTL_WAKE) || ((v1 & MCTL_HPS) == 0)); + + if (!awake_before) + brcms_b_wait_for_wake(wlc->hw); +} + +/* + * Write this BSS config's MAC address to core. + * Updates RXE match engine. + */ +static int brcms_c_set_mac(struct brcms_bss_cfg *bsscfg) +{ + int err = 0; + struct brcms_c_info *wlc = bsscfg->wlc; + + /* enter the MAC addr into the RXE match registers */ + brcms_c_set_addrmatch(wlc, RCM_MAC_OFFSET, wlc->pub->cur_etheraddr); + + brcms_c_ampdu_macaddr_upd(wlc); + + return err; +} + +/* Write the BSS config's BSSID address to core (set_bssid in d11procs.tcl). + * Updates RXE match engine. + */ +static void brcms_c_set_bssid(struct brcms_bss_cfg *bsscfg) +{ + /* we need to update BSSID in RXE match registers */ + brcms_c_set_addrmatch(bsscfg->wlc, RCM_BSSID_OFFSET, bsscfg->BSSID); +} + +void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, size_t ssid_len) +{ + u8 len = min_t(u8, sizeof(wlc->bsscfg->SSID), ssid_len); + memset(wlc->bsscfg->SSID, 0, sizeof(wlc->bsscfg->SSID)); + + memcpy(wlc->bsscfg->SSID, ssid, len); + wlc->bsscfg->SSID_len = len; +} + +static void brcms_b_set_shortslot(struct brcms_hardware *wlc_hw, bool shortslot) +{ + wlc_hw->shortslot = shortslot; + + if (wlc_hw->band->bandtype == BRCM_BAND_2G && wlc_hw->up) { + brcms_c_suspend_mac_and_wait(wlc_hw->wlc); + brcms_b_update_slot_timing(wlc_hw, shortslot); + brcms_c_enable_mac(wlc_hw->wlc); + } +} + +/* + * Suspend the the MAC and update the slot timing + * for standard 11b/g (20us slots) or shortslot 11g (9us slots). + */ +static void brcms_c_switch_shortslot(struct brcms_c_info *wlc, bool shortslot) +{ + /* use the override if it is set */ + if (wlc->shortslot_override != BRCMS_SHORTSLOT_AUTO) + shortslot = (wlc->shortslot_override == BRCMS_SHORTSLOT_ON); + + if (wlc->shortslot == shortslot) + return; + + wlc->shortslot = shortslot; + + brcms_b_set_shortslot(wlc->hw, shortslot); +} + +static void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec) +{ + if (wlc->home_chanspec != chanspec) { + wlc->home_chanspec = chanspec; + + if (wlc->pub->associated) + wlc->bsscfg->current_bss->chanspec = chanspec; + } +} + +void +brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, + bool mute_tx, struct txpwr_limits *txpwr) +{ + uint bandunit; + + brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: 0x%x\n", wlc_hw->unit, + chanspec); + + wlc_hw->chanspec = chanspec; + + /* Switch bands if necessary */ + if (wlc_hw->_nbands > 1) { + bandunit = chspec_bandunit(chanspec); + if (wlc_hw->band->bandunit != bandunit) { + /* brcms_b_setband disables other bandunit, + * use light band switch if not up yet + */ + if (wlc_hw->up) { + wlc_phy_chanspec_radio_set(wlc_hw-> + bandstate[bandunit]-> + pi, chanspec); + brcms_b_setband(wlc_hw, bandunit, chanspec); + } else { + brcms_c_setxband(wlc_hw, bandunit); + } + } + } + + wlc_phy_initcal_enable(wlc_hw->band->pi, !mute_tx); + + if (!wlc_hw->up) { + if (wlc_hw->clk) + wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, + chanspec); + wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec); + } else { + wlc_phy_chanspec_set(wlc_hw->band->pi, chanspec); + wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, chanspec); + + /* Update muting of the channel */ + brcms_b_mute(wlc_hw, mute_tx); + } +} + +/* switch to and initialize new band */ +static void brcms_c_setband(struct brcms_c_info *wlc, + uint bandunit) +{ + wlc->band = wlc->bandstate[bandunit]; + + if (!wlc->pub->up) + return; + + /* wait for at least one beacon before entering sleeping state */ + brcms_c_set_ps_ctrl(wlc); + + /* band-specific initializations */ + brcms_c_bsinit(wlc); +} + +static void brcms_c_set_chanspec(struct brcms_c_info *wlc, u16 chanspec) +{ + uint bandunit; + bool switchband = false; + u16 old_chanspec = wlc->chanspec; + + if (!brcms_c_valid_chanspec_db(wlc->cmi, chanspec)) { + brcms_err(wlc->hw->d11core, "wl%d: %s: Bad channel %d\n", + wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec)); + return; + } + + /* Switch bands if necessary */ + if (wlc->pub->_nbands > 1) { + bandunit = chspec_bandunit(chanspec); + if (wlc->band->bandunit != bandunit || wlc->bandinit_pending) { + switchband = true; + if (wlc->bandlocked) { + brcms_err(wlc->hw->d11core, + "wl%d: %s: chspec %d band is locked!\n", + wlc->pub->unit, __func__, + CHSPEC_CHANNEL(chanspec)); + return; + } + /* + * should the setband call come after the + * brcms_b_chanspec() ? if the setband updates + * (brcms_c_bsinit) use low level calls to inspect and + * set state, the state inspected may be from the wrong + * band, or the following brcms_b_set_chanspec() may + * undo the work. + */ + brcms_c_setband(wlc, bandunit); + } + } + + /* sync up phy/radio chanspec */ + brcms_c_set_phy_chanspec(wlc, chanspec); + + /* init antenna selection */ + if (brcms_chspec_bw(old_chanspec) != brcms_chspec_bw(chanspec)) { + brcms_c_antsel_init(wlc->asi); + + /* Fix the hardware rateset based on bw. + * Mainly add MCS32 for 40Mhz, remove MCS 32 for 20Mhz + */ + brcms_c_rateset_bw_mcs_filter(&wlc->band->hw_rateset, + wlc->band->mimo_cap_40 ? brcms_chspec_bw(chanspec) : 0); + } + + /* update some mac configuration since chanspec changed */ + brcms_c_ucode_mac_upd(wlc); +} + +/* + * This function changes the phytxctl for beacon based on current + * beacon ratespec AND txant setting as per this table: + * ratespec CCK ant = wlc->stf->txant + * OFDM ant = 3 + */ +void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, + u32 bcn_rspec) +{ + u16 phyctl; + u16 phytxant = wlc->stf->phytxant; + u16 mask = PHY_TXC_ANT_MASK; + + /* for non-siso rates or default setting, use the available chains */ + if (BRCMS_PHY_11N_CAP(wlc->band)) + phytxant = brcms_c_stf_phytxchain_sel(wlc, bcn_rspec); + + phyctl = brcms_b_read_shm(wlc->hw, M_BCN_PCTLWD); + phyctl = (phyctl & ~mask) | phytxant; + brcms_b_write_shm(wlc->hw, M_BCN_PCTLWD, phyctl); +} + +/* + * centralized protection config change function to simplify debugging, no + * consistency checking this should be called only on changes to avoid overhead + * in periodic function + */ +void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val) +{ + /* + * Cannot use brcms_dbg_* here because this function is called + * before wlc is sufficiently initialized. + */ + BCMMSG(wlc->wiphy, "idx %d, val %d\n", idx, val); + + switch (idx) { + case BRCMS_PROT_G_SPEC: + wlc->protection->_g = (bool) val; + break; + case BRCMS_PROT_G_OVR: + wlc->protection->g_override = (s8) val; + break; + case BRCMS_PROT_G_USER: + wlc->protection->gmode_user = (u8) val; + break; + case BRCMS_PROT_OVERLAP: + wlc->protection->overlap = (s8) val; + break; + case BRCMS_PROT_N_USER: + wlc->protection->nmode_user = (s8) val; + break; + case BRCMS_PROT_N_CFG: + wlc->protection->n_cfg = (s8) val; + break; + case BRCMS_PROT_N_CFG_OVR: + wlc->protection->n_cfg_override = (s8) val; + break; + case BRCMS_PROT_N_NONGF: + wlc->protection->nongf = (bool) val; + break; + case BRCMS_PROT_N_NONGF_OVR: + wlc->protection->nongf_override = (s8) val; + break; + case BRCMS_PROT_N_PAM_OVR: + wlc->protection->n_pam_override = (s8) val; + break; + case BRCMS_PROT_N_OBSS: + wlc->protection->n_obss = (bool) val; + break; + + default: + break; + } + +} + +static void brcms_c_ht_update_sgi_rx(struct brcms_c_info *wlc, int val) +{ + if (wlc->pub->up) { + brcms_c_update_beacon(wlc); + brcms_c_update_probe_resp(wlc, true); + } +} + +static void brcms_c_ht_update_ldpc(struct brcms_c_info *wlc, s8 val) +{ + wlc->stf->ldpc = val; + + if (wlc->pub->up) { + brcms_c_update_beacon(wlc); + brcms_c_update_probe_resp(wlc, true); + wlc_phy_ldpc_override_set(wlc->band->pi, (val ? true : false)); + } +} + +void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, + const struct ieee80211_tx_queue_params *params, + bool suspend) +{ + int i; + struct shm_acparams acp_shm; + u16 *shm_entry; + + /* Only apply params if the core is out of reset and has clocks */ + if (!wlc->clk) { + brcms_err(wlc->hw->d11core, "wl%d: %s : no-clock\n", + wlc->pub->unit, __func__); + return; + } + + memset(&acp_shm, 0, sizeof(struct shm_acparams)); + /* fill in shm ac params struct */ + acp_shm.txop = params->txop; + /* convert from units of 32us to us for ucode */ + wlc->edcf_txop[aci & 0x3] = acp_shm.txop = + EDCF_TXOP2USEC(acp_shm.txop); + acp_shm.aifs = (params->aifs & EDCF_AIFSN_MASK); + + if (aci == IEEE80211_AC_VI && acp_shm.txop == 0 + && acp_shm.aifs < EDCF_AIFSN_MAX) + acp_shm.aifs++; + + if (acp_shm.aifs < EDCF_AIFSN_MIN + || acp_shm.aifs > EDCF_AIFSN_MAX) { + brcms_err(wlc->hw->d11core, "wl%d: edcf_setparams: bad " + "aifs %d\n", wlc->pub->unit, acp_shm.aifs); + } else { + acp_shm.cwmin = params->cw_min; + acp_shm.cwmax = params->cw_max; + acp_shm.cwcur = acp_shm.cwmin; + acp_shm.bslots = + bcma_read16(wlc->hw->d11core, D11REGOFFS(tsf_random)) & + acp_shm.cwcur; + acp_shm.reggap = acp_shm.bslots + acp_shm.aifs; + /* Indicate the new params to the ucode */ + acp_shm.status = brcms_b_read_shm(wlc->hw, (M_EDCF_QINFO + + wme_ac2fifo[aci] * + M_EDCF_QLEN + + M_EDCF_STATUS_OFF)); + acp_shm.status |= WME_STATUS_NEWAC; + + /* Fill in shm acparam table */ + shm_entry = (u16 *) &acp_shm; + for (i = 0; i < (int)sizeof(struct shm_acparams); i += 2) + brcms_b_write_shm(wlc->hw, + M_EDCF_QINFO + + wme_ac2fifo[aci] * M_EDCF_QLEN + i, + *shm_entry++); + } + + if (suspend) + brcms_c_suspend_mac_and_wait(wlc); + + brcms_c_update_beacon(wlc); + brcms_c_update_probe_resp(wlc, false); + + if (suspend) + brcms_c_enable_mac(wlc); +} + +static void brcms_c_edcf_setparams(struct brcms_c_info *wlc, bool suspend) +{ + u16 aci; + int i_ac; + struct ieee80211_tx_queue_params txq_pars; + static const struct edcf_acparam default_edcf_acparams[] = { + {EDCF_AC_BE_ACI_STA, EDCF_AC_BE_ECW_STA, EDCF_AC_BE_TXOP_STA}, + {EDCF_AC_BK_ACI_STA, EDCF_AC_BK_ECW_STA, EDCF_AC_BK_TXOP_STA}, + {EDCF_AC_VI_ACI_STA, EDCF_AC_VI_ECW_STA, EDCF_AC_VI_TXOP_STA}, + {EDCF_AC_VO_ACI_STA, EDCF_AC_VO_ECW_STA, EDCF_AC_VO_TXOP_STA} + }; /* ucode needs these parameters during its initialization */ + const struct edcf_acparam *edcf_acp = &default_edcf_acparams[0]; + + for (i_ac = 0; i_ac < IEEE80211_NUM_ACS; i_ac++, edcf_acp++) { + /* find out which ac this set of params applies to */ + aci = (edcf_acp->ACI & EDCF_ACI_MASK) >> EDCF_ACI_SHIFT; + + /* fill in shm ac params struct */ + txq_pars.txop = edcf_acp->TXOP; + txq_pars.aifs = edcf_acp->ACI; + + /* CWmin = 2^(ECWmin) - 1 */ + txq_pars.cw_min = EDCF_ECW2CW(edcf_acp->ECW & EDCF_ECWMIN_MASK); + /* CWmax = 2^(ECWmax) - 1 */ + txq_pars.cw_max = EDCF_ECW2CW((edcf_acp->ECW & EDCF_ECWMAX_MASK) + >> EDCF_ECWMAX_SHIFT); + brcms_c_wme_setparams(wlc, aci, &txq_pars, suspend); + } + + if (suspend) { + brcms_c_suspend_mac_and_wait(wlc); + brcms_c_enable_mac(wlc); + } +} + +static void brcms_c_radio_monitor_start(struct brcms_c_info *wlc) +{ + /* Don't start the timer if HWRADIO feature is disabled */ + if (wlc->radio_monitor) + return; + + wlc->radio_monitor = true; + brcms_b_pllreq(wlc->hw, true, BRCMS_PLLREQ_RADIO_MON); + brcms_add_timer(wlc->radio_timer, TIMER_INTERVAL_RADIOCHK, true); +} + +static bool brcms_c_radio_monitor_stop(struct brcms_c_info *wlc) +{ + if (!wlc->radio_monitor) + return true; + + wlc->radio_monitor = false; + brcms_b_pllreq(wlc->hw, false, BRCMS_PLLREQ_RADIO_MON); + return brcms_del_timer(wlc->radio_timer); +} + +/* read hwdisable state and propagate to wlc flag */ +static void brcms_c_radio_hwdisable_upd(struct brcms_c_info *wlc) +{ + if (wlc->pub->hw_off) + return; + + if (brcms_b_radio_read_hwdisabled(wlc->hw)) + mboolset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE); + else + mboolclr(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE); +} + +/* update hwradio status and return it */ +bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc) +{ + brcms_c_radio_hwdisable_upd(wlc); + + return mboolisset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE) ? + true : false; +} + +/* periodical query hw radio button while driver is "down" */ +static void brcms_c_radio_timer(void *arg) +{ + struct brcms_c_info *wlc = (struct brcms_c_info *) arg; + + if (brcms_deviceremoved(wlc)) { + brcms_err(wlc->hw->d11core, "wl%d: %s: dead chip\n", + wlc->pub->unit, __func__); + brcms_down(wlc->wl); + return; + } + + brcms_c_radio_hwdisable_upd(wlc); +} + +/* common low-level watchdog code */ +static void brcms_b_watchdog(struct brcms_c_info *wlc) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + + if (!wlc_hw->up) + return; + + /* increment second count */ + wlc_hw->now++; + + /* Check for FIFO error interrupts */ + brcms_b_fifoerrors(wlc_hw); + + /* make sure RX dma has buffers */ + dma_rxfill(wlc->hw->di[RX_FIFO]); + + wlc_phy_watchdog(wlc_hw->band->pi); +} + +/* common watchdog code */ +static void brcms_c_watchdog(struct brcms_c_info *wlc) +{ + brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); + + if (!wlc->pub->up) + return; + + if (brcms_deviceremoved(wlc)) { + brcms_err(wlc->hw->d11core, "wl%d: %s: dead chip\n", + wlc->pub->unit, __func__); + brcms_down(wlc->wl); + return; + } + + /* increment second count */ + wlc->pub->now++; + + brcms_c_radio_hwdisable_upd(wlc); + /* if radio is disable, driver may be down, quit here */ + if (wlc->pub->radio_disabled) + return; + + brcms_b_watchdog(wlc); + + /* + * occasionally sample mac stat counters to + * detect 16-bit counter wrap + */ + if ((wlc->pub->now % SW_TIMER_MAC_STAT_UPD) == 0) + brcms_c_statsupd(wlc); + + if (BRCMS_ISNPHY(wlc->band) && + ((wlc->pub->now - wlc->tempsense_lasttime) >= + BRCMS_TEMPSENSE_PERIOD)) { + wlc->tempsense_lasttime = wlc->pub->now; + brcms_c_tempsense_upd(wlc); + } +} + +static void brcms_c_watchdog_by_timer(void *arg) +{ + struct brcms_c_info *wlc = (struct brcms_c_info *) arg; + + brcms_c_watchdog(wlc); +} + +static bool brcms_c_timers_init(struct brcms_c_info *wlc, int unit) +{ + wlc->wdtimer = brcms_init_timer(wlc->wl, brcms_c_watchdog_by_timer, + wlc, "watchdog"); + if (!wlc->wdtimer) { + wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for wdtimer " + "failed\n", unit); + goto fail; + } + + wlc->radio_timer = brcms_init_timer(wlc->wl, brcms_c_radio_timer, + wlc, "radio"); + if (!wlc->radio_timer) { + wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for radio_timer " + "failed\n", unit); + goto fail; + } + + return true; + + fail: + return false; +} + +/* + * Initialize brcms_c_info default values ... + * may get overrides later in this function + */ +static void brcms_c_info_init(struct brcms_c_info *wlc, int unit) +{ + int i; + + /* Save our copy of the chanspec */ + wlc->chanspec = ch20mhz_chspec(1); + + /* various 802.11g modes */ + wlc->shortslot = false; + wlc->shortslot_override = BRCMS_SHORTSLOT_AUTO; + + brcms_c_protection_upd(wlc, BRCMS_PROT_G_OVR, BRCMS_PROTECTION_AUTO); + brcms_c_protection_upd(wlc, BRCMS_PROT_G_SPEC, false); + + brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG_OVR, + BRCMS_PROTECTION_AUTO); + brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG, BRCMS_N_PROTECTION_OFF); + brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF_OVR, + BRCMS_PROTECTION_AUTO); + brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF, false); + brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, AUTO); + + brcms_c_protection_upd(wlc, BRCMS_PROT_OVERLAP, + BRCMS_PROTECTION_CTL_OVERLAP); + + /* 802.11g draft 4.0 NonERP elt advertisement */ + wlc->include_legacy_erp = true; + + wlc->stf->ant_rx_ovr = ANT_RX_DIV_DEF; + wlc->stf->txant = ANT_TX_DEF; + + wlc->prb_resp_timeout = BRCMS_PRB_RESP_TIMEOUT; + + wlc->usr_fragthresh = DOT11_DEFAULT_FRAG_LEN; + for (i = 0; i < NFIFO; i++) + wlc->fragthresh[i] = DOT11_DEFAULT_FRAG_LEN; + wlc->RTSThresh = DOT11_DEFAULT_RTS_LEN; + + /* default rate fallback retry limits */ + wlc->SFBL = RETRY_SHORT_FB; + wlc->LFBL = RETRY_LONG_FB; + + /* default mac retry limits */ + wlc->SRL = RETRY_SHORT_DEF; + wlc->LRL = RETRY_LONG_DEF; + + /* WME QoS mode is Auto by default */ + wlc->pub->_ampdu = AMPDU_AGG_HOST; +} + +static uint brcms_c_attach_module(struct brcms_c_info *wlc) +{ + uint err = 0; + uint unit; + unit = wlc->pub->unit; + + wlc->asi = brcms_c_antsel_attach(wlc); + if (wlc->asi == NULL) { + wiphy_err(wlc->wiphy, "wl%d: attach: antsel_attach " + "failed\n", unit); + err = 44; + goto fail; + } + + wlc->ampdu = brcms_c_ampdu_attach(wlc); + if (wlc->ampdu == NULL) { + wiphy_err(wlc->wiphy, "wl%d: attach: ampdu_attach " + "failed\n", unit); + err = 50; + goto fail; + } + + if ((brcms_c_stf_attach(wlc) != 0)) { + wiphy_err(wlc->wiphy, "wl%d: attach: stf_attach " + "failed\n", unit); + err = 68; + goto fail; + } + fail: + return err; +} + +struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc) +{ + return wlc->pub; +} + +/* low level attach + * run backplane attach, init nvram + * run phy attach + * initialize software state for each core and band + * put the whole chip in reset(driver down state), no clock + */ +static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core, + uint unit, bool piomode) +{ + struct brcms_hardware *wlc_hw; + uint err = 0; + uint j; + bool wme = false; + struct shared_phy_params sha_params; + struct wiphy *wiphy = wlc->wiphy; + struct pci_dev *pcidev = core->bus->host_pci; + struct ssb_sprom *sprom = &core->bus->sprom; + + if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) + brcms_dbg_info(core, "wl%d: vendor 0x%x device 0x%x\n", unit, + pcidev->vendor, + pcidev->device); + else + brcms_dbg_info(core, "wl%d: vendor 0x%x device 0x%x\n", unit, + core->bus->boardinfo.vendor, + core->bus->boardinfo.type); + + wme = true; + + wlc_hw = wlc->hw; + wlc_hw->wlc = wlc; + wlc_hw->unit = unit; + wlc_hw->band = wlc_hw->bandstate[0]; + wlc_hw->_piomode = piomode; + + /* populate struct brcms_hardware with default values */ + brcms_b_info_init(wlc_hw); + + /* + * Do the hardware portion of the attach. Also initialize software + * state that depends on the particular hardware we are running. + */ + wlc_hw->sih = ai_attach(core->bus); + if (wlc_hw->sih == NULL) { + wiphy_err(wiphy, "wl%d: brcms_b_attach: si_attach failed\n", + unit); + err = 11; + goto fail; + } + + /* verify again the device is supported */ + if (!brcms_c_chipmatch(core)) { + wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported device\n", + unit); + err = 12; + goto fail; + } + + if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) { + wlc_hw->vendorid = pcidev->vendor; + wlc_hw->deviceid = pcidev->device; + } else { + wlc_hw->vendorid = core->bus->boardinfo.vendor; + wlc_hw->deviceid = core->bus->boardinfo.type; + } + + wlc_hw->d11core = core; + wlc_hw->corerev = core->id.rev; + + /* validate chip, chiprev and corerev */ + if (!brcms_c_isgoodchip(wlc_hw)) { + err = 13; + goto fail; + } + + /* initialize power control registers */ + ai_clkctl_init(wlc_hw->sih); + + /* request fastclock and force fastclock for the rest of attach + * bring the d11 core out of reset. + * For PMU chips, the first wlc_clkctl_clk is no-op since core-clk + * is still false; But it will be called again inside wlc_corereset, + * after d11 is out of reset. + */ + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); + brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); + + if (!brcms_b_validate_chip_access(wlc_hw)) { + wiphy_err(wiphy, "wl%d: brcms_b_attach: validate_chip_access " + "failed\n", unit); + err = 14; + goto fail; + } + + /* get the board rev, used just below */ + j = sprom->board_rev; + /* promote srom boardrev of 0xFF to 1 */ + if (j == BOARDREV_PROMOTABLE) + j = BOARDREV_PROMOTED; + wlc_hw->boardrev = (u16) j; + if (!brcms_c_validboardtype(wlc_hw)) { + wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported Broadcom " + "board type (0x%x)" " or revision level (0x%x)\n", + unit, ai_get_boardtype(wlc_hw->sih), + wlc_hw->boardrev); + err = 15; + goto fail; + } + wlc_hw->sromrev = sprom->revision; + wlc_hw->boardflags = sprom->boardflags_lo + (sprom->boardflags_hi << 16); + wlc_hw->boardflags2 = sprom->boardflags2_lo + (sprom->boardflags2_hi << 16); + + if (wlc_hw->boardflags & BFL_NOPLLDOWN) + brcms_b_pllreq(wlc_hw, true, BRCMS_PLLREQ_SHARED); + + /* check device id(srom, nvram etc.) to set bands */ + if (wlc_hw->deviceid == BCM43224_D11N_ID || + wlc_hw->deviceid == BCM43224_D11N_ID_VEN1 || + wlc_hw->deviceid == BCM43224_CHIP_ID) + /* Dualband boards */ + wlc_hw->_nbands = 2; + else + wlc_hw->_nbands = 1; + + if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43225)) + wlc_hw->_nbands = 1; + + /* BMAC_NOTE: remove init of pub values when brcms_c_attach() + * unconditionally does the init of these values + */ + wlc->vendorid = wlc_hw->vendorid; + wlc->deviceid = wlc_hw->deviceid; + wlc->pub->sih = wlc_hw->sih; + wlc->pub->corerev = wlc_hw->corerev; + wlc->pub->sromrev = wlc_hw->sromrev; + wlc->pub->boardrev = wlc_hw->boardrev; + wlc->pub->boardflags = wlc_hw->boardflags; + wlc->pub->boardflags2 = wlc_hw->boardflags2; + wlc->pub->_nbands = wlc_hw->_nbands; + + wlc_hw->physhim = wlc_phy_shim_attach(wlc_hw, wlc->wl, wlc); + + if (wlc_hw->physhim == NULL) { + wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_shim_attach " + "failed\n", unit); + err = 25; + goto fail; + } + + /* pass all the parameters to wlc_phy_shared_attach in one struct */ + sha_params.sih = wlc_hw->sih; + sha_params.physhim = wlc_hw->physhim; + sha_params.unit = unit; + sha_params.corerev = wlc_hw->corerev; + sha_params.vid = wlc_hw->vendorid; + sha_params.did = wlc_hw->deviceid; + sha_params.chip = ai_get_chip_id(wlc_hw->sih); + sha_params.chiprev = ai_get_chiprev(wlc_hw->sih); + sha_params.chippkg = ai_get_chippkg(wlc_hw->sih); + sha_params.sromrev = wlc_hw->sromrev; + sha_params.boardtype = ai_get_boardtype(wlc_hw->sih); + sha_params.boardrev = wlc_hw->boardrev; + sha_params.boardflags = wlc_hw->boardflags; + sha_params.boardflags2 = wlc_hw->boardflags2; + + /* alloc and save pointer to shared phy state area */ + wlc_hw->phy_sh = wlc_phy_shared_attach(&sha_params); + if (!wlc_hw->phy_sh) { + err = 16; + goto fail; + } + + /* initialize software state for each core and band */ + for (j = 0; j < wlc_hw->_nbands; j++) { + /* + * band0 is always 2.4Ghz + * band1, if present, is 5Ghz + */ + + brcms_c_setxband(wlc_hw, j); + + wlc_hw->band->bandunit = j; + wlc_hw->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G; + wlc->band->bandunit = j; + wlc->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G; + wlc->core->coreidx = core->core_index; + + wlc_hw->machwcap = bcma_read32(core, D11REGOFFS(machwcap)); + wlc_hw->machwcap_backup = wlc_hw->machwcap; + + /* init tx fifo size */ + WARN_ON(wlc_hw->corerev < XMTFIFOTBL_STARTREV || + (wlc_hw->corerev - XMTFIFOTBL_STARTREV) > + ARRAY_SIZE(xmtfifo_sz)); + wlc_hw->xmtfifo_sz = + xmtfifo_sz[(wlc_hw->corerev - XMTFIFOTBL_STARTREV)]; + WARN_ON(!wlc_hw->xmtfifo_sz[0]); + + /* Get a phy for this band */ + wlc_hw->band->pi = + wlc_phy_attach(wlc_hw->phy_sh, core, + wlc_hw->band->bandtype, + wlc->wiphy); + if (wlc_hw->band->pi == NULL) { + wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_" + "attach failed\n", unit); + err = 17; + goto fail; + } + + wlc_phy_machwcap_set(wlc_hw->band->pi, wlc_hw->machwcap); + + wlc_phy_get_phyversion(wlc_hw->band->pi, &wlc_hw->band->phytype, + &wlc_hw->band->phyrev, + &wlc_hw->band->radioid, + &wlc_hw->band->radiorev); + wlc_hw->band->abgphy_encore = + wlc_phy_get_encore(wlc_hw->band->pi); + wlc->band->abgphy_encore = wlc_phy_get_encore(wlc_hw->band->pi); + wlc_hw->band->core_flags = + wlc_phy_get_coreflags(wlc_hw->band->pi); + + /* verify good phy_type & supported phy revision */ + if (BRCMS_ISNPHY(wlc_hw->band)) { + if (NCONF_HAS(wlc_hw->band->phyrev)) + goto good_phy; + else + goto bad_phy; + } else if (BRCMS_ISLCNPHY(wlc_hw->band)) { + if (LCNCONF_HAS(wlc_hw->band->phyrev)) + goto good_phy; + else + goto bad_phy; + } else { + bad_phy: + wiphy_err(wiphy, "wl%d: brcms_b_attach: unsupported " + "phy type/rev (%d/%d)\n", unit, + wlc_hw->band->phytype, wlc_hw->band->phyrev); + err = 18; + goto fail; + } + + good_phy: + /* + * BMAC_NOTE: wlc->band->pi should not be set below and should + * be done in the high level attach. However we can not make + * that change until all low level access is changed to + * wlc_hw->band->pi. Instead do the wlc->band->pi init below, + * keeping wlc_hw->band->pi as well for incremental update of + * low level fns, and cut over low only init when all fns + * updated. + */ + wlc->band->pi = wlc_hw->band->pi; + wlc->band->phytype = wlc_hw->band->phytype; + wlc->band->phyrev = wlc_hw->band->phyrev; + wlc->band->radioid = wlc_hw->band->radioid; + wlc->band->radiorev = wlc_hw->band->radiorev; + brcms_dbg_info(core, "wl%d: phy %u/%u radio %x/%u\n", unit, + wlc->band->phytype, wlc->band->phyrev, + wlc->band->radioid, wlc->band->radiorev); + /* default contention windows size limits */ + wlc_hw->band->CWmin = APHY_CWMIN; + wlc_hw->band->CWmax = PHY_CWMAX; + + if (!brcms_b_attach_dmapio(wlc, j, wme)) { + err = 19; + goto fail; + } + } + + /* disable core to match driver "down" state */ + brcms_c_coredisable(wlc_hw); + + /* Match driver "down" state */ + bcma_host_pci_down(wlc_hw->d11core->bus); + + /* turn off pll and xtal to match driver "down" state */ + brcms_b_xtal(wlc_hw, OFF); + + /* ******************************************************************* + * The hardware is in the DOWN state at this point. D11 core + * or cores are in reset with clocks off, and the board PLLs + * are off if possible. + * + * Beyond this point, wlc->sbclk == false and chip registers + * should not be touched. + ********************************************************************* + */ + + /* init etheraddr state variables */ + brcms_c_get_macaddr(wlc_hw, wlc_hw->etheraddr); + + if (is_broadcast_ether_addr(wlc_hw->etheraddr) || + is_zero_ether_addr(wlc_hw->etheraddr)) { + wiphy_err(wiphy, "wl%d: brcms_b_attach: bad macaddr\n", + unit); + err = 22; + goto fail; + } + + brcms_dbg_info(wlc_hw->d11core, "deviceid 0x%x nbands %d board 0x%x\n", + wlc_hw->deviceid, wlc_hw->_nbands, + ai_get_boardtype(wlc_hw->sih)); + + return err; + + fail: + wiphy_err(wiphy, "wl%d: brcms_b_attach: failed with err %d\n", unit, + err); + return err; +} + +static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc) +{ + int aa; + uint unit; + int bandtype; + struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; + + unit = wlc->pub->unit; + bandtype = wlc->band->bandtype; + + /* get antennas available */ + if (bandtype == BRCM_BAND_5G) + aa = sprom->ant_available_a; + else + aa = sprom->ant_available_bg; + + if ((aa < 1) || (aa > 15)) { + wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in" + " srom (0x%x), using 3\n", unit, __func__, aa); + aa = 3; + } + + /* reset the defaults if we have a single antenna */ + if (aa == 1) { + wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_0; + wlc->stf->txant = ANT_TX_FORCE_0; + } else if (aa == 2) { + wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_1; + wlc->stf->txant = ANT_TX_FORCE_1; + } else { + } + + /* Compute Antenna Gain */ + if (bandtype == BRCM_BAND_5G) + wlc->band->antgain = sprom->antenna_gain.a1; + else + wlc->band->antgain = sprom->antenna_gain.a0; + + return true; +} + +static void brcms_c_bss_default_init(struct brcms_c_info *wlc) +{ + u16 chanspec; + struct brcms_band *band; + struct brcms_bss_info *bi = wlc->default_bss; + + /* init default and target BSS with some sane initial values */ + memset(bi, 0, sizeof(*bi)); + bi->beacon_period = BEACON_INTERVAL_DEFAULT; + + /* fill the default channel as the first valid channel + * starting from the 2G channels + */ + chanspec = ch20mhz_chspec(1); + wlc->home_chanspec = bi->chanspec = chanspec; + + /* find the band of our default channel */ + band = wlc->band; + if (wlc->pub->_nbands > 1 && + band->bandunit != chspec_bandunit(chanspec)) + band = wlc->bandstate[OTHERBANDUNIT(wlc)]; + + /* init bss rates to the band specific default rate set */ + brcms_c_rateset_default(&bi->rateset, NULL, band->phytype, + band->bandtype, false, BRCMS_RATE_MASK_FULL, + (bool) (wlc->pub->_n_enab & SUPPORT_11N), + brcms_chspec_bw(chanspec), wlc->stf->txstreams); + + if (wlc->pub->_n_enab & SUPPORT_11N) + bi->flags |= BRCMS_BSS_HT; +} + +static void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap) +{ + uint i; + struct brcms_band *band; + + for (i = 0; i < wlc->pub->_nbands; i++) { + band = wlc->bandstate[i]; + if (band->bandtype == BRCM_BAND_5G) { + if ((bwcap == BRCMS_N_BW_40ALL) + || (bwcap == BRCMS_N_BW_20IN2G_40IN5G)) + band->mimo_cap_40 = true; + else + band->mimo_cap_40 = false; + } else { + if (bwcap == BRCMS_N_BW_40ALL) + band->mimo_cap_40 = true; + else + band->mimo_cap_40 = false; + } + } +} + +static void brcms_c_timers_deinit(struct brcms_c_info *wlc) +{ + /* free timer state */ + if (wlc->wdtimer) { + brcms_free_timer(wlc->wdtimer); + wlc->wdtimer = NULL; + } + if (wlc->radio_timer) { + brcms_free_timer(wlc->radio_timer); + wlc->radio_timer = NULL; + } +} + +static void brcms_c_detach_module(struct brcms_c_info *wlc) +{ + if (wlc->asi) { + brcms_c_antsel_detach(wlc->asi); + wlc->asi = NULL; + } + + if (wlc->ampdu) { + brcms_c_ampdu_detach(wlc->ampdu); + wlc->ampdu = NULL; + } + + brcms_c_stf_detach(wlc); +} + +/* + * low level detach + */ +static void brcms_b_detach(struct brcms_c_info *wlc) +{ + uint i; + struct brcms_hw_band *band; + struct brcms_hardware *wlc_hw = wlc->hw; + + brcms_b_detach_dmapio(wlc_hw); + + band = wlc_hw->band; + for (i = 0; i < wlc_hw->_nbands; i++) { + if (band->pi) { + /* Detach this band's phy */ + wlc_phy_detach(band->pi); + band->pi = NULL; + } + band = wlc_hw->bandstate[OTHERBANDUNIT(wlc)]; + } + + /* Free shared phy state */ + kfree(wlc_hw->phy_sh); + + wlc_phy_shim_detach(wlc_hw->physhim); + + if (wlc_hw->sih) { + ai_detach(wlc_hw->sih); + wlc_hw->sih = NULL; + } +} + +/* + * Return a count of the number of driver callbacks still pending. + * + * General policy is that brcms_c_detach can only dealloc/free software states. + * It can NOT touch hardware registers since the d11core may be in reset and + * clock may not be available. + * One exception is sb register access, which is possible if crystal is turned + * on after "down" state, driver should avoid software timer with the exception + * of radio_monitor. + */ +uint brcms_c_detach(struct brcms_c_info *wlc) +{ + uint callbacks; + + if (wlc == NULL) + return 0; + + brcms_b_detach(wlc); + + /* delete software timers */ + callbacks = 0; + if (!brcms_c_radio_monitor_stop(wlc)) + callbacks++; + + brcms_c_channel_mgr_detach(wlc->cmi); + + brcms_c_timers_deinit(wlc); + + brcms_c_detach_module(wlc); + + brcms_c_detach_mfree(wlc); + return callbacks; +} + +/* update state that depends on the current value of "ap" */ +static void brcms_c_ap_upd(struct brcms_c_info *wlc) +{ + /* STA-BSS; short capable */ + wlc->PLCPHdr_override = BRCMS_PLCP_SHORT; +} + +/* Initialize just the hardware when coming out of POR or S3/S5 system states */ +static void brcms_b_hw_up(struct brcms_hardware *wlc_hw) +{ + if (wlc_hw->wlc->pub->hw_up) + return; + + brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); + + /* + * Enable pll and xtal, initialize the power control registers, + * and force fastclock for the remainder of brcms_c_up(). + */ + brcms_b_xtal(wlc_hw, ON); + ai_clkctl_init(wlc_hw->sih); + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); + + /* + * TODO: test suspend/resume + * + * AI chip doesn't restore bar0win2 on + * hibernation/resume, need sw fixup + */ + + /* + * Inform phy that a POR reset has occurred so + * it does a complete phy init + */ + wlc_phy_por_inform(wlc_hw->band->pi); + + wlc_hw->ucode_loaded = false; + wlc_hw->wlc->pub->hw_up = true; + + if ((wlc_hw->boardflags & BFL_FEM) + && (ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM4313)) { + if (! + (wlc_hw->boardrev >= 0x1250 + && (wlc_hw->boardflags & BFL_FEM_BT))) + ai_epa_4313war(wlc_hw->sih); + } +} + +static int brcms_b_up_prep(struct brcms_hardware *wlc_hw) +{ + brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); + + /* + * Enable pll and xtal, initialize the power control registers, + * and force fastclock for the remainder of brcms_c_up(). + */ + brcms_b_xtal(wlc_hw, ON); + ai_clkctl_init(wlc_hw->sih); + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); + + /* + * Configure pci/pcmcia here instead of in brcms_c_attach() + * to allow mfg hotswap: down, hotswap (chip power cycle), up. + */ + bcma_host_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core, + true); + + /* + * Need to read the hwradio status here to cover the case where the + * system is loaded with the hw radio disabled. We do not want to + * bring the driver up in this case. + */ + if (brcms_b_radio_read_hwdisabled(wlc_hw)) { + /* put SB PCI in down state again */ + bcma_host_pci_down(wlc_hw->d11core->bus); + brcms_b_xtal(wlc_hw, OFF); + return -ENOMEDIUM; + } + + bcma_host_pci_up(wlc_hw->d11core->bus); + + /* reset the d11 core */ + brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); + + return 0; +} + +static int brcms_b_up_finish(struct brcms_hardware *wlc_hw) +{ + wlc_hw->up = true; + wlc_phy_hw_state_upd(wlc_hw->band->pi, true); + + /* FULLY enable dynamic power control and d11 core interrupt */ + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); + brcms_intrson(wlc_hw->wlc->wl); + return 0; +} + +/* + * Write WME tunable parameters for retransmit/max rate + * from wlc struct to ucode + */ +static void brcms_c_wme_retries_write(struct brcms_c_info *wlc) +{ + int ac; + + /* Need clock to do this */ + if (!wlc->clk) + return; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + brcms_b_write_shm(wlc->hw, M_AC_TXLMT_ADDR(ac), + wlc->wme_retries[ac]); +} + +/* make interface operational */ +int brcms_c_up(struct brcms_c_info *wlc) +{ + struct ieee80211_channel *ch; + + brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); + + /* HW is turned off so don't try to access it */ + if (wlc->pub->hw_off || brcms_deviceremoved(wlc)) + return -ENOMEDIUM; + + if (!wlc->pub->hw_up) { + brcms_b_hw_up(wlc->hw); + wlc->pub->hw_up = true; + } + + if ((wlc->pub->boardflags & BFL_FEM) + && (ai_get_chip_id(wlc->hw->sih) == BCMA_CHIP_ID_BCM4313)) { + if (wlc->pub->boardrev >= 0x1250 + && (wlc->pub->boardflags & BFL_FEM_BT)) + brcms_b_mhf(wlc->hw, MHF5, MHF5_4313_GPIOCTRL, + MHF5_4313_GPIOCTRL, BRCM_BAND_ALL); + else + brcms_b_mhf(wlc->hw, MHF4, MHF4_EXTPA_ENABLE, + MHF4_EXTPA_ENABLE, BRCM_BAND_ALL); + } + + /* + * Need to read the hwradio status here to cover the case where the + * system is loaded with the hw radio disabled. We do not want to bring + * the driver up in this case. If radio is disabled, abort up, lower + * power, start radio timer and return 0(for NDIS) don't call + * radio_update to avoid looping brcms_c_up. + * + * brcms_b_up_prep() returns either 0 or -BCME_RADIOOFF only + */ + if (!wlc->pub->radio_disabled) { + int status = brcms_b_up_prep(wlc->hw); + if (status == -ENOMEDIUM) { + if (!mboolisset + (wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE)) { + struct brcms_bss_cfg *bsscfg = wlc->bsscfg; + mboolset(wlc->pub->radio_disabled, + WL_RADIO_HW_DISABLE); + if (bsscfg->type == BRCMS_TYPE_STATION || + bsscfg->type == BRCMS_TYPE_ADHOC) + brcms_err(wlc->hw->d11core, + "wl%d: up: rfdisable -> " + "bsscfg_disable()\n", + wlc->pub->unit); + } + } + } + + if (wlc->pub->radio_disabled) { + brcms_c_radio_monitor_start(wlc); + return 0; + } + + /* brcms_b_up_prep has done brcms_c_corereset(). so clk is on, set it */ + wlc->clk = true; + + brcms_c_radio_monitor_stop(wlc); + + /* Set EDCF hostflags */ + brcms_b_mhf(wlc->hw, MHF1, MHF1_EDCF, MHF1_EDCF, BRCM_BAND_ALL); + + brcms_init(wlc->wl); + wlc->pub->up = true; + + if (wlc->bandinit_pending) { + ch = wlc->pub->ieee_hw->conf.chandef.chan; + brcms_c_suspend_mac_and_wait(wlc); + brcms_c_set_chanspec(wlc, ch20mhz_chspec(ch->hw_value)); + wlc->bandinit_pending = false; + brcms_c_enable_mac(wlc); + } + + brcms_b_up_finish(wlc->hw); + + /* Program the TX wme params with the current settings */ + brcms_c_wme_retries_write(wlc); + + /* start one second watchdog timer */ + brcms_add_timer(wlc->wdtimer, TIMER_INTERVAL_WATCHDOG, true); + wlc->WDarmed = true; + + /* ensure antenna config is up to date */ + brcms_c_stf_phy_txant_upd(wlc); + /* ensure LDPC config is in sync */ + brcms_c_ht_update_ldpc(wlc, wlc->stf->ldpc); + + return 0; +} + +static uint brcms_c_down_del_timer(struct brcms_c_info *wlc) +{ + uint callbacks = 0; + + return callbacks; +} + +static int brcms_b_bmac_down_prep(struct brcms_hardware *wlc_hw) +{ + bool dev_gone; + uint callbacks = 0; + + if (!wlc_hw->up) + return callbacks; + + dev_gone = brcms_deviceremoved(wlc_hw->wlc); + + /* disable interrupts */ + if (dev_gone) + wlc_hw->wlc->macintmask = 0; + else { + /* now disable interrupts */ + brcms_intrsoff(wlc_hw->wlc->wl); + + /* ensure we're running on the pll clock again */ + brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); + } + /* down phy at the last of this stage */ + callbacks += wlc_phy_down(wlc_hw->band->pi); + + return callbacks; +} + +static int brcms_b_down_finish(struct brcms_hardware *wlc_hw) +{ + uint callbacks = 0; + bool dev_gone; + + if (!wlc_hw->up) + return callbacks; + + wlc_hw->up = false; + wlc_phy_hw_state_upd(wlc_hw->band->pi, false); + + dev_gone = brcms_deviceremoved(wlc_hw->wlc); + + if (dev_gone) { + wlc_hw->sbclk = false; + wlc_hw->clk = false; + wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); + + /* reclaim any posted packets */ + brcms_c_flushqueues(wlc_hw->wlc); + } else { + + /* Reset and disable the core */ + if (bcma_core_is_enabled(wlc_hw->d11core)) { + if (bcma_read32(wlc_hw->d11core, + D11REGOFFS(maccontrol)) & MCTL_EN_MAC) + brcms_c_suspend_mac_and_wait(wlc_hw->wlc); + callbacks += brcms_reset(wlc_hw->wlc->wl); + brcms_c_coredisable(wlc_hw); + } + + /* turn off primary xtal and pll */ + if (!wlc_hw->noreset) { + bcma_host_pci_down(wlc_hw->d11core->bus); + brcms_b_xtal(wlc_hw, OFF); + } + } + + return callbacks; +} + +/* + * Mark the interface nonoperational, stop the software mechanisms, + * disable the hardware, free any transient buffer state. + * Return a count of the number of driver callbacks still pending. + */ +uint brcms_c_down(struct brcms_c_info *wlc) +{ + + uint callbacks = 0; + int i; + bool dev_gone = false; + + brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); + + /* check if we are already in the going down path */ + if (wlc->going_down) { + brcms_err(wlc->hw->d11core, + "wl%d: %s: Driver going down so return\n", + wlc->pub->unit, __func__); + return 0; + } + if (!wlc->pub->up) + return callbacks; + + wlc->going_down = true; + + callbacks += brcms_b_bmac_down_prep(wlc->hw); + + dev_gone = brcms_deviceremoved(wlc); + + /* Call any registered down handlers */ + for (i = 0; i < BRCMS_MAXMODULES; i++) { + if (wlc->modulecb[i].down_fn) + callbacks += + wlc->modulecb[i].down_fn(wlc->modulecb[i].hdl); + } + + /* cancel the watchdog timer */ + if (wlc->WDarmed) { + if (!brcms_del_timer(wlc->wdtimer)) + callbacks++; + wlc->WDarmed = false; + } + /* cancel all other timers */ + callbacks += brcms_c_down_del_timer(wlc); + + wlc->pub->up = false; + + wlc_phy_mute_upd(wlc->band->pi, false, PHY_MUTE_ALL); + + callbacks += brcms_b_down_finish(wlc->hw); + + /* brcms_b_down_finish has done brcms_c_coredisable(). so clk is off */ + wlc->clk = false; + + wlc->going_down = false; + return callbacks; +} + +/* Set the current gmode configuration */ +int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config) +{ + int ret = 0; + uint i; + struct brcms_c_rateset rs; + /* Default to 54g Auto */ + /* Advertise and use shortslot (-1/0/1 Auto/Off/On) */ + s8 shortslot = BRCMS_SHORTSLOT_AUTO; + bool shortslot_restrict = false; /* Restrict association to stations + * that support shortslot + */ + bool ofdm_basic = false; /* Make 6, 12, and 24 basic rates */ + /* Advertise and use short preambles (-1/0/1 Auto/Off/On) */ + int preamble = BRCMS_PLCP_LONG; + bool preamble_restrict = false; /* Restrict association to stations + * that support short preambles + */ + struct brcms_band *band; + + /* if N-support is enabled, allow Gmode set as long as requested + * Gmode is not GMODE_LEGACY_B + */ + if ((wlc->pub->_n_enab & SUPPORT_11N) && gmode == GMODE_LEGACY_B) + return -ENOTSUPP; + + /* verify that we are dealing with 2G band and grab the band pointer */ + if (wlc->band->bandtype == BRCM_BAND_2G) + band = wlc->band; + else if ((wlc->pub->_nbands > 1) && + (wlc->bandstate[OTHERBANDUNIT(wlc)]->bandtype == BRCM_BAND_2G)) + band = wlc->bandstate[OTHERBANDUNIT(wlc)]; + else + return -EINVAL; + + /* update configuration value */ + if (config) + brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, gmode); + + /* Clear rateset override */ + memset(&rs, 0, sizeof(rs)); + + switch (gmode) { + case GMODE_LEGACY_B: + shortslot = BRCMS_SHORTSLOT_OFF; + brcms_c_rateset_copy(&gphy_legacy_rates, &rs); + + break; + + case GMODE_LRS: + break; + + case GMODE_AUTO: + /* Accept defaults */ + break; + + case GMODE_ONLY: + ofdm_basic = true; + preamble = BRCMS_PLCP_SHORT; + preamble_restrict = true; + break; + + case GMODE_PERFORMANCE: + shortslot = BRCMS_SHORTSLOT_ON; + shortslot_restrict = true; + ofdm_basic = true; + preamble = BRCMS_PLCP_SHORT; + preamble_restrict = true; + break; + + default: + /* Error */ + brcms_err(wlc->hw->d11core, "wl%d: %s: invalid gmode %d\n", + wlc->pub->unit, __func__, gmode); + return -ENOTSUPP; + } + + band->gmode = gmode; + + wlc->shortslot_override = shortslot; + + /* Use the default 11g rateset */ + if (!rs.count) + brcms_c_rateset_copy(&cck_ofdm_rates, &rs); + + if (ofdm_basic) { + for (i = 0; i < rs.count; i++) { + if (rs.rates[i] == BRCM_RATE_6M + || rs.rates[i] == BRCM_RATE_12M + || rs.rates[i] == BRCM_RATE_24M) + rs.rates[i] |= BRCMS_RATE_FLAG; + } + } + + /* Set default bss rateset */ + wlc->default_bss->rateset.count = rs.count; + memcpy(wlc->default_bss->rateset.rates, rs.rates, + sizeof(wlc->default_bss->rateset.rates)); + + return ret; +} + +int brcms_c_set_nmode(struct brcms_c_info *wlc) +{ + uint i; + s32 nmode = AUTO; + + if (wlc->stf->txstreams == WL_11N_3x3) + nmode = WL_11N_3x3; + else + nmode = WL_11N_2x2; + + /* force GMODE_AUTO if NMODE is ON */ + brcms_c_set_gmode(wlc, GMODE_AUTO, true); + if (nmode == WL_11N_3x3) + wlc->pub->_n_enab = SUPPORT_HT; + else + wlc->pub->_n_enab = SUPPORT_11N; + wlc->default_bss->flags |= BRCMS_BSS_HT; + /* add the mcs rates to the default and hw ratesets */ + brcms_c_rateset_mcs_build(&wlc->default_bss->rateset, + wlc->stf->txstreams); + for (i = 0; i < wlc->pub->_nbands; i++) + memcpy(wlc->bandstate[i]->hw_rateset.mcs, + wlc->default_bss->rateset.mcs, MCSSET_LEN); + + return 0; +} + +static int +brcms_c_set_internal_rateset(struct brcms_c_info *wlc, + struct brcms_c_rateset *rs_arg) +{ + struct brcms_c_rateset rs, new; + uint bandunit; + + memcpy(&rs, rs_arg, sizeof(struct brcms_c_rateset)); + + /* check for bad count value */ + if ((rs.count == 0) || (rs.count > BRCMS_NUMRATES)) + return -EINVAL; + + /* try the current band */ + bandunit = wlc->band->bandunit; + memcpy(&new, &rs, sizeof(struct brcms_c_rateset)); + if (brcms_c_rate_hwrs_filter_sort_validate + (&new, &wlc->bandstate[bandunit]->hw_rateset, true, + wlc->stf->txstreams)) + goto good; + + /* try the other band */ + if (brcms_is_mband_unlocked(wlc)) { + bandunit = OTHERBANDUNIT(wlc); + memcpy(&new, &rs, sizeof(struct brcms_c_rateset)); + if (brcms_c_rate_hwrs_filter_sort_validate(&new, + &wlc-> + bandstate[bandunit]-> + hw_rateset, true, + wlc->stf->txstreams)) + goto good; + } + + return -EBADE; + + good: + /* apply new rateset */ + memcpy(&wlc->default_bss->rateset, &new, + sizeof(struct brcms_c_rateset)); + memcpy(&wlc->bandstate[bandunit]->defrateset, &new, + sizeof(struct brcms_c_rateset)); + return 0; +} + +static void brcms_c_ofdm_rateset_war(struct brcms_c_info *wlc) +{ + u8 r; + bool war = false; + + if (wlc->pub->associated) + r = wlc->bsscfg->current_bss->rateset.rates[0]; + else + r = wlc->default_bss->rateset.rates[0]; + + wlc_phy_ofdm_rateset_war(wlc->band->pi, war); +} + +int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel) +{ + u16 chspec = ch20mhz_chspec(channel); + + if (channel < 0 || channel > MAXCHANNEL) + return -EINVAL; + + if (!brcms_c_valid_chanspec_db(wlc->cmi, chspec)) + return -EINVAL; + + + if (!wlc->pub->up && brcms_is_mband_unlocked(wlc)) { + if (wlc->band->bandunit != chspec_bandunit(chspec)) + wlc->bandinit_pending = true; + else + wlc->bandinit_pending = false; + } + + wlc->default_bss->chanspec = chspec; + /* brcms_c_BSSinit() will sanitize the rateset before + * using it.. */ + if (wlc->pub->up && (wlc_phy_chanspec_get(wlc->band->pi) != chspec)) { + brcms_c_set_home_chanspec(wlc, chspec); + brcms_c_suspend_mac_and_wait(wlc); + brcms_c_set_chanspec(wlc, chspec); + brcms_c_enable_mac(wlc); + } + return 0; +} + +int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl) +{ + int ac; + + if (srl < 1 || srl > RETRY_SHORT_MAX || + lrl < 1 || lrl > RETRY_SHORT_MAX) + return -EINVAL; + + wlc->SRL = srl; + wlc->LRL = lrl; + + brcms_b_retrylimit_upd(wlc->hw, wlc->SRL, wlc->LRL); + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], + EDCF_SHORT, wlc->SRL); + wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], + EDCF_LONG, wlc->LRL); + } + brcms_c_wme_retries_write(wlc); + + return 0; +} + +void brcms_c_get_current_rateset(struct brcms_c_info *wlc, + struct brcm_rateset *currs) +{ + struct brcms_c_rateset *rs; + + if (wlc->pub->associated) + rs = &wlc->bsscfg->current_bss->rateset; + else + rs = &wlc->default_bss->rateset; + + /* Copy only legacy rateset section */ + currs->count = rs->count; + memcpy(&currs->rates, &rs->rates, rs->count); +} + +int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs) +{ + struct brcms_c_rateset internal_rs; + int bcmerror; + + if (rs->count > BRCMS_NUMRATES) + return -ENOBUFS; + + memset(&internal_rs, 0, sizeof(internal_rs)); + + /* Copy only legacy rateset section */ + internal_rs.count = rs->count; + memcpy(&internal_rs.rates, &rs->rates, internal_rs.count); + + /* merge rateset coming in with the current mcsset */ + if (wlc->pub->_n_enab & SUPPORT_11N) { + struct brcms_bss_info *mcsset_bss; + if (wlc->pub->associated) + mcsset_bss = wlc->bsscfg->current_bss; + else + mcsset_bss = wlc->default_bss; + memcpy(internal_rs.mcs, &mcsset_bss->rateset.mcs[0], + MCSSET_LEN); + } + + bcmerror = brcms_c_set_internal_rateset(wlc, &internal_rs); + if (!bcmerror) + brcms_c_ofdm_rateset_war(wlc); + + return bcmerror; +} + +static void brcms_c_time_lock(struct brcms_c_info *wlc) +{ + bcma_set32(wlc->hw->d11core, D11REGOFFS(maccontrol), MCTL_TBTTHOLD); + /* Commit the write */ + bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); +} + +static void brcms_c_time_unlock(struct brcms_c_info *wlc) +{ + bcma_mask32(wlc->hw->d11core, D11REGOFFS(maccontrol), ~MCTL_TBTTHOLD); + /* Commit the write */ + bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); +} + +int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period) +{ + u32 bcnint_us; + + if (period == 0) + return -EINVAL; + + wlc->default_bss->beacon_period = period; + + bcnint_us = period << 10; + brcms_c_time_lock(wlc); + bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_cfprep), + (bcnint_us << CFPREP_CBI_SHIFT)); + bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_cfpstart), bcnint_us); + brcms_c_time_unlock(wlc); + + return 0; +} + +u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx) +{ + return wlc->band->phytype; +} + +void brcms_c_set_shortslot_override(struct brcms_c_info *wlc, s8 sslot_override) +{ + wlc->shortslot_override = sslot_override; + + /* + * shortslot is an 11g feature, so no more work if we are + * currently on the 5G band + */ + if (wlc->band->bandtype == BRCM_BAND_5G) + return; + + if (wlc->pub->up && wlc->pub->associated) { + /* let watchdog or beacon processing update shortslot */ + } else if (wlc->pub->up) { + /* unassociated shortslot is off */ + brcms_c_switch_shortslot(wlc, false); + } else { + /* driver is down, so just update the brcms_c_info + * value */ + if (wlc->shortslot_override == BRCMS_SHORTSLOT_AUTO) + wlc->shortslot = false; + else + wlc->shortslot = + (wlc->shortslot_override == + BRCMS_SHORTSLOT_ON); + } +} + +/* + * register watchdog and down handlers. + */ +int brcms_c_module_register(struct brcms_pub *pub, + const char *name, struct brcms_info *hdl, + int (*d_fn)(void *handle)) +{ + struct brcms_c_info *wlc = (struct brcms_c_info *) pub->wlc; + int i; + + /* find an empty entry and just add, no duplication check! */ + for (i = 0; i < BRCMS_MAXMODULES; i++) { + if (wlc->modulecb[i].name[0] == '\0') { + strncpy(wlc->modulecb[i].name, name, + sizeof(wlc->modulecb[i].name) - 1); + wlc->modulecb[i].hdl = hdl; + wlc->modulecb[i].down_fn = d_fn; + return 0; + } + } + + return -ENOSR; +} + +/* unregister module callbacks */ +int brcms_c_module_unregister(struct brcms_pub *pub, const char *name, + struct brcms_info *hdl) +{ + struct brcms_c_info *wlc = (struct brcms_c_info *) pub->wlc; + int i; + + if (wlc == NULL) + return -ENODATA; + + for (i = 0; i < BRCMS_MAXMODULES; i++) { + if (!strcmp(wlc->modulecb[i].name, name) && + (wlc->modulecb[i].hdl == hdl)) { + memset(&wlc->modulecb[i], 0, sizeof(wlc->modulecb[i])); + return 0; + } + } + + /* table not found! */ + return -ENODATA; +} + +static bool brcms_c_chipmatch_pci(struct bcma_device *core) +{ + struct pci_dev *pcidev = core->bus->host_pci; + u16 vendor = pcidev->vendor; + u16 device = pcidev->device; + + if (vendor != PCI_VENDOR_ID_BROADCOM) { + pr_err("unknown vendor id %04x\n", vendor); + return false; + } + + if (device == BCM43224_D11N_ID_VEN1 || device == BCM43224_CHIP_ID) + return true; + if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID)) + return true; + if (device == BCM4313_D11N2G_ID || device == BCM4313_CHIP_ID) + return true; + if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID)) + return true; + + pr_err("unknown device id %04x\n", device); + return false; +} + +static bool brcms_c_chipmatch_soc(struct bcma_device *core) +{ + struct bcma_chipinfo *chipinfo = &core->bus->chipinfo; + + if (chipinfo->id == BCMA_CHIP_ID_BCM4716) + return true; + + pr_err("unknown chip id %04x\n", chipinfo->id); + return false; +} + +bool brcms_c_chipmatch(struct bcma_device *core) +{ + switch (core->bus->hosttype) { + case BCMA_HOSTTYPE_PCI: + return brcms_c_chipmatch_pci(core); + case BCMA_HOSTTYPE_SOC: + return brcms_c_chipmatch_soc(core); + default: + pr_err("unknown host type: %i\n", core->bus->hosttype); + return false; + } +} + +u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate) +{ + u16 table_ptr; + u8 phy_rate, index; + + /* get the phy specific rate encoding for the PLCP SIGNAL field */ + if (is_ofdm_rate(rate)) + table_ptr = M_RT_DIRMAP_A; + else + table_ptr = M_RT_DIRMAP_B; + + /* for a given rate, the LS-nibble of the PLCP SIGNAL field is + * the index into the rate table. + */ + phy_rate = rate_info[rate] & BRCMS_RATE_MASK; + index = phy_rate & 0xf; + + /* Find the SHM pointer to the rate table entry by looking in the + * Direct-map Table + */ + return 2 * brcms_b_read_shm(wlc_hw, table_ptr + (index * 2)); +} + +/* + * bcmc_fid_generate: + * Generate frame ID for a BCMC packet. The frag field is not used + * for MC frames so is used as part of the sequence number. + */ +static inline u16 +bcmc_fid_generate(struct brcms_c_info *wlc, struct brcms_bss_cfg *bsscfg, + struct d11txh *txh) +{ + u16 frameid; + + frameid = le16_to_cpu(txh->TxFrameID) & ~(TXFID_SEQ_MASK | + TXFID_QUEUE_MASK); + frameid |= + (((wlc-> + mc_fid_counter++) << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) | + TX_BCMC_FIFO; + + return frameid; +} + +static uint +brcms_c_calc_ack_time(struct brcms_c_info *wlc, u32 rspec, + u8 preamble_type) +{ + uint dur = 0; + + /* + * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that + * is less than or equal to the rate of the immediately previous + * frame in the FES + */ + rspec = brcms_basic_rate(wlc, rspec); + /* ACK frame len == 14 == 2(fc) + 2(dur) + 6(ra) + 4(fcs) */ + dur = + brcms_c_calc_frame_time(wlc, rspec, preamble_type, + (DOT11_ACK_LEN + FCS_LEN)); + return dur; +} + +static uint +brcms_c_calc_cts_time(struct brcms_c_info *wlc, u32 rspec, + u8 preamble_type) +{ + return brcms_c_calc_ack_time(wlc, rspec, preamble_type); +} + +static uint +brcms_c_calc_ba_time(struct brcms_c_info *wlc, u32 rspec, + u8 preamble_type) +{ + /* + * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that + * is less than or equal to the rate of the immediately previous + * frame in the FES + */ + rspec = brcms_basic_rate(wlc, rspec); + /* BA len == 32 == 16(ctl hdr) + 4(ba len) + 8(bitmap) + 4(fcs) */ + return brcms_c_calc_frame_time(wlc, rspec, preamble_type, + (DOT11_BA_LEN + DOT11_BA_BITMAP_LEN + + FCS_LEN)); +} + +/* brcms_c_compute_frame_dur() + * + * Calculate the 802.11 MAC header DUR field for MPDU + * DUR for a single frame = 1 SIFS + 1 ACK + * DUR for a frame with following frags = 3 SIFS + 2 ACK + next frag time + * + * rate MPDU rate in unit of 500kbps + * next_frag_len next MPDU length in bytes + * preamble_type use short/GF or long/MM PLCP header + */ +static u16 +brcms_c_compute_frame_dur(struct brcms_c_info *wlc, u32 rate, + u8 preamble_type, uint next_frag_len) +{ + u16 dur, sifs; + + sifs = get_sifs(wlc->band); + + dur = sifs; + dur += (u16) brcms_c_calc_ack_time(wlc, rate, preamble_type); + + if (next_frag_len) { + /* Double the current DUR to get 2 SIFS + 2 ACKs */ + dur *= 2; + /* add another SIFS and the frag time */ + dur += sifs; + dur += + (u16) brcms_c_calc_frame_time(wlc, rate, preamble_type, + next_frag_len); + } + return dur; +} + +/* The opposite of brcms_c_calc_frame_time */ +static uint +brcms_c_calc_frame_len(struct brcms_c_info *wlc, u32 ratespec, + u8 preamble_type, uint dur) +{ + uint nsyms, mac_len, Ndps, kNdps; + uint rate = rspec2rate(ratespec); + + if (is_mcs_rate(ratespec)) { + uint mcs = ratespec & RSPEC_RATE_MASK; + int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); + dur -= PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT); + /* payload calculation matches that of regular ofdm */ + if (wlc->band->bandtype == BRCM_BAND_2G) + dur -= DOT11_OFDM_SIGNAL_EXTENSION; + /* kNdbps = kbps * 4 */ + kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), + rspec_issgi(ratespec)) * 4; + nsyms = dur / APHY_SYMBOL_TIME; + mac_len = + ((nsyms * kNdps) - + ((APHY_SERVICE_NBITS + APHY_TAIL_NBITS) * 1000)) / 8000; + } else if (is_ofdm_rate(ratespec)) { + dur -= APHY_PREAMBLE_TIME; + dur -= APHY_SIGNAL_TIME; + /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */ + Ndps = rate * 2; + nsyms = dur / APHY_SYMBOL_TIME; + mac_len = + ((nsyms * Ndps) - + (APHY_SERVICE_NBITS + APHY_TAIL_NBITS)) / 8; + } else { + if (preamble_type & BRCMS_SHORT_PREAMBLE) + dur -= BPHY_PLCP_SHORT_TIME; + else + dur -= BPHY_PLCP_TIME; + mac_len = dur * rate; + /* divide out factor of 2 in rate (1/2 mbps) */ + mac_len = mac_len / 8 / 2; + } + return mac_len; +} + +/* + * Return true if the specified rate is supported by the specified band. + * BRCM_BAND_AUTO indicates the current band. + */ +static bool brcms_c_valid_rate(struct brcms_c_info *wlc, u32 rspec, int band, + bool verbose) +{ + struct brcms_c_rateset *hw_rateset; + uint i; + + if ((band == BRCM_BAND_AUTO) || (band == wlc->band->bandtype)) + hw_rateset = &wlc->band->hw_rateset; + else if (wlc->pub->_nbands > 1) + hw_rateset = &wlc->bandstate[OTHERBANDUNIT(wlc)]->hw_rateset; + else + /* other band specified and we are a single band device */ + return false; + + /* check if this is a mimo rate */ + if (is_mcs_rate(rspec)) { + if ((rspec & RSPEC_RATE_MASK) >= MCS_TABLE_SIZE) + goto error; + + return isset(hw_rateset->mcs, (rspec & RSPEC_RATE_MASK)); + } + + for (i = 0; i < hw_rateset->count; i++) + if (hw_rateset->rates[i] == rspec2rate(rspec)) + return true; + error: + if (verbose) + brcms_err(wlc->hw->d11core, "wl%d: valid_rate: rate spec 0x%x " + "not in hw_rateset\n", wlc->pub->unit, rspec); + + return false; +} + +static u32 +mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band, + u32 int_val) +{ + struct bcma_device *core = wlc->hw->d11core; + u8 stf = (int_val & NRATE_STF_MASK) >> NRATE_STF_SHIFT; + u8 rate = int_val & NRATE_RATE_MASK; + u32 rspec; + bool ismcs = ((int_val & NRATE_MCS_INUSE) == NRATE_MCS_INUSE); + bool issgi = ((int_val & NRATE_SGI_MASK) >> NRATE_SGI_SHIFT); + bool override_mcs_only = ((int_val & NRATE_OVERRIDE_MCS_ONLY) + == NRATE_OVERRIDE_MCS_ONLY); + int bcmerror = 0; + + if (!ismcs) + return (u32) rate; + + /* validate the combination of rate/mcs/stf is allowed */ + if ((wlc->pub->_n_enab & SUPPORT_11N) && ismcs) { + /* mcs only allowed when nmode */ + if (stf > PHY_TXC1_MODE_SDM) { + brcms_err(core, "wl%d: %s: Invalid stf\n", + wlc->pub->unit, __func__); + bcmerror = -EINVAL; + goto done; + } + + /* mcs 32 is a special case, DUP mode 40 only */ + if (rate == 32) { + if (!CHSPEC_IS40(wlc->home_chanspec) || + ((stf != PHY_TXC1_MODE_SISO) + && (stf != PHY_TXC1_MODE_CDD))) { + brcms_err(core, "wl%d: %s: Invalid mcs 32\n", + wlc->pub->unit, __func__); + bcmerror = -EINVAL; + goto done; + } + /* mcs > 7 must use stf SDM */ + } else if (rate > HIGHEST_SINGLE_STREAM_MCS) { + /* mcs > 7 must use stf SDM */ + if (stf != PHY_TXC1_MODE_SDM) { + brcms_dbg_mac80211(core, "wl%d: enabling " + "SDM mode for mcs %d\n", + wlc->pub->unit, rate); + stf = PHY_TXC1_MODE_SDM; + } + } else { + /* + * MCS 0-7 may use SISO, CDD, and for + * phy_rev >= 3 STBC + */ + if ((stf > PHY_TXC1_MODE_STBC) || + (!BRCMS_STBC_CAP_PHY(wlc) + && (stf == PHY_TXC1_MODE_STBC))) { + brcms_err(core, "wl%d: %s: Invalid STBC\n", + wlc->pub->unit, __func__); + bcmerror = -EINVAL; + goto done; + } + } + } else if (is_ofdm_rate(rate)) { + if ((stf != PHY_TXC1_MODE_CDD) && (stf != PHY_TXC1_MODE_SISO)) { + brcms_err(core, "wl%d: %s: Invalid OFDM\n", + wlc->pub->unit, __func__); + bcmerror = -EINVAL; + goto done; + } + } else if (is_cck_rate(rate)) { + if ((cur_band->bandtype != BRCM_BAND_2G) + || (stf != PHY_TXC1_MODE_SISO)) { + brcms_err(core, "wl%d: %s: Invalid CCK\n", + wlc->pub->unit, __func__); + bcmerror = -EINVAL; + goto done; + } + } else { + brcms_err(core, "wl%d: %s: Unknown rate type\n", + wlc->pub->unit, __func__); + bcmerror = -EINVAL; + goto done; + } + /* make sure multiple antennae are available for non-siso rates */ + if ((stf != PHY_TXC1_MODE_SISO) && (wlc->stf->txstreams == 1)) { + brcms_err(core, "wl%d: %s: SISO antenna but !SISO " + "request\n", wlc->pub->unit, __func__); + bcmerror = -EINVAL; + goto done; + } + + rspec = rate; + if (ismcs) { + rspec |= RSPEC_MIMORATE; + /* For STBC populate the STC field of the ratespec */ + if (stf == PHY_TXC1_MODE_STBC) { + u8 stc; + stc = 1; /* Nss for single stream is always 1 */ + rspec |= (stc << RSPEC_STC_SHIFT); + } + } + + rspec |= (stf << RSPEC_STF_SHIFT); + + if (override_mcs_only) + rspec |= RSPEC_OVERRIDE_MCS_ONLY; + + if (issgi) + rspec |= RSPEC_SHORT_GI; + + if ((rate != 0) + && !brcms_c_valid_rate(wlc, rspec, cur_band->bandtype, true)) + return rate; + + return rspec; +done: + return rate; +} + +/* + * Compute PLCP, but only requires actual rate and length of pkt. + * Rate is given in the driver standard multiple of 500 kbps. + * le is set for 11 Mbps rate if necessary. + * Broken out for PRQ. + */ + +static void brcms_c_cck_plcp_set(struct brcms_c_info *wlc, int rate_500, + uint length, u8 *plcp) +{ + u16 usec = 0; + u8 le = 0; + + switch (rate_500) { + case BRCM_RATE_1M: + usec = length << 3; + break; + case BRCM_RATE_2M: + usec = length << 2; + break; + case BRCM_RATE_5M5: + usec = (length << 4) / 11; + if ((length << 4) - (usec * 11) > 0) + usec++; + break; + case BRCM_RATE_11M: + usec = (length << 3) / 11; + if ((length << 3) - (usec * 11) > 0) { + usec++; + if ((usec * 11) - (length << 3) >= 8) + le = D11B_PLCP_SIGNAL_LE; + } + break; + + default: + brcms_err(wlc->hw->d11core, + "brcms_c_cck_plcp_set: unsupported rate %d\n", + rate_500); + rate_500 = BRCM_RATE_1M; + usec = length << 3; + break; + } + /* PLCP signal byte */ + plcp[0] = rate_500 * 5; /* r (500kbps) * 5 == r (100kbps) */ + /* PLCP service byte */ + plcp[1] = (u8) (le | D11B_PLCP_SIGNAL_LOCKED); + /* PLCP length u16, little endian */ + plcp[2] = usec & 0xff; + plcp[3] = (usec >> 8) & 0xff; + /* PLCP CRC16 */ + plcp[4] = 0; + plcp[5] = 0; +} + +/* Rate: 802.11 rate code, length: PSDU length in octets */ +static void brcms_c_compute_mimo_plcp(u32 rspec, uint length, u8 *plcp) +{ + u8 mcs = (u8) (rspec & RSPEC_RATE_MASK); + plcp[0] = mcs; + if (rspec_is40mhz(rspec) || (mcs == 32)) + plcp[0] |= MIMO_PLCP_40MHZ; + BRCMS_SET_MIMO_PLCP_LEN(plcp, length); + plcp[3] = rspec_mimoplcp3(rspec); /* rspec already holds this byte */ + plcp[3] |= 0x7; /* set smoothing, not sounding ppdu & reserved */ + plcp[4] = 0; /* number of extension spatial streams bit 0 & 1 */ + plcp[5] = 0; +} + +/* Rate: 802.11 rate code, length: PSDU length in octets */ +static void +brcms_c_compute_ofdm_plcp(u32 rspec, u32 length, u8 *plcp) +{ + u8 rate_signal; + u32 tmp = 0; + int rate = rspec2rate(rspec); + + /* + * encode rate per 802.11a-1999 sec 17.3.4.1, with lsb + * transmitted first + */ + rate_signal = rate_info[rate] & BRCMS_RATE_MASK; + memset(plcp, 0, D11_PHY_HDR_LEN); + D11A_PHY_HDR_SRATE((struct ofdm_phy_hdr *) plcp, rate_signal); + + tmp = (length & 0xfff) << 5; + plcp[2] |= (tmp >> 16) & 0xff; + plcp[1] |= (tmp >> 8) & 0xff; + plcp[0] |= tmp & 0xff; +} + +/* Rate: 802.11 rate code, length: PSDU length in octets */ +static void brcms_c_compute_cck_plcp(struct brcms_c_info *wlc, u32 rspec, + uint length, u8 *plcp) +{ + int rate = rspec2rate(rspec); + + brcms_c_cck_plcp_set(wlc, rate, length, plcp); +} + +static void +brcms_c_compute_plcp(struct brcms_c_info *wlc, u32 rspec, + uint length, u8 *plcp) +{ + if (is_mcs_rate(rspec)) + brcms_c_compute_mimo_plcp(rspec, length, plcp); + else if (is_ofdm_rate(rspec)) + brcms_c_compute_ofdm_plcp(rspec, length, plcp); + else + brcms_c_compute_cck_plcp(wlc, rspec, length, plcp); +} + +/* brcms_c_compute_rtscts_dur() + * + * Calculate the 802.11 MAC header DUR field for an RTS or CTS frame + * DUR for normal RTS/CTS w/ frame = 3 SIFS + 1 CTS + next frame time + 1 ACK + * DUR for CTS-TO-SELF w/ frame = 2 SIFS + next frame time + 1 ACK + * + * cts cts-to-self or rts/cts + * rts_rate rts or cts rate in unit of 500kbps + * rate next MPDU rate in unit of 500kbps + * frame_len next MPDU frame length in bytes + */ +u16 +brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only, + u32 rts_rate, + u32 frame_rate, u8 rts_preamble_type, + u8 frame_preamble_type, uint frame_len, bool ba) +{ + u16 dur, sifs; + + sifs = get_sifs(wlc->band); + + if (!cts_only) { + /* RTS/CTS */ + dur = 3 * sifs; + dur += + (u16) brcms_c_calc_cts_time(wlc, rts_rate, + rts_preamble_type); + } else { + /* CTS-TO-SELF */ + dur = 2 * sifs; + } + + dur += + (u16) brcms_c_calc_frame_time(wlc, frame_rate, frame_preamble_type, + frame_len); + if (ba) + dur += + (u16) brcms_c_calc_ba_time(wlc, frame_rate, + BRCMS_SHORT_PREAMBLE); + else + dur += + (u16) brcms_c_calc_ack_time(wlc, frame_rate, + frame_preamble_type); + return dur; +} + +static u16 brcms_c_phytxctl1_calc(struct brcms_c_info *wlc, u32 rspec) +{ + u16 phyctl1 = 0; + u16 bw; + + if (BRCMS_ISLCNPHY(wlc->band)) { + bw = PHY_TXC1_BW_20MHZ; + } else { + bw = rspec_get_bw(rspec); + /* 10Mhz is not supported yet */ + if (bw < PHY_TXC1_BW_20MHZ) { + brcms_err(wlc->hw->d11core, "phytxctl1_calc: bw %d is " + "not supported yet, set to 20L\n", bw); + bw = PHY_TXC1_BW_20MHZ; + } + } + + if (is_mcs_rate(rspec)) { + uint mcs = rspec & RSPEC_RATE_MASK; + + /* bw, stf, coding-type is part of rspec_phytxbyte2 returns */ + phyctl1 = rspec_phytxbyte2(rspec); + /* set the upper byte of phyctl1 */ + phyctl1 |= (mcs_table[mcs].tx_phy_ctl3 << 8); + } else if (is_cck_rate(rspec) && !BRCMS_ISLCNPHY(wlc->band) + && !BRCMS_ISSSLPNPHY(wlc->band)) { + /* + * In CCK mode LPPHY overloads OFDM Modulation bits with CCK + * Data Rate. Eventually MIMOPHY would also be converted to + * this format + */ + /* 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */ + phyctl1 = (bw | (rspec_stf(rspec) << PHY_TXC1_MODE_SHIFT)); + } else { /* legacy OFDM/CCK */ + s16 phycfg; + /* get the phyctl byte from rate phycfg table */ + phycfg = brcms_c_rate_legacy_phyctl(rspec2rate(rspec)); + if (phycfg == -1) { + brcms_err(wlc->hw->d11core, "phytxctl1_calc: wrong " + "legacy OFDM/CCK rate\n"); + phycfg = 0; + } + /* set the upper byte of phyctl1 */ + phyctl1 = + (bw | (phycfg << 8) | + (rspec_stf(rspec) << PHY_TXC1_MODE_SHIFT)); + } + return phyctl1; +} + +/* + * Add struct d11txh, struct cck_phy_hdr. + * + * 'p' data must start with 802.11 MAC header + * 'p' must allow enough bytes of local headers to be "pushed" onto the packet + * + * headroom == D11_PHY_HDR_LEN + D11_TXH_LEN (D11_TXH_LEN is now 104 bytes) + * + */ +static u16 +brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw, + struct sk_buff *p, struct scb *scb, uint frag, + uint nfrags, uint queue, uint next_frag_len) +{ + struct ieee80211_hdr *h; + struct d11txh *txh; + u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN]; + int len, phylen, rts_phylen; + u16 mch, phyctl, xfts, mainrates; + u16 seq = 0, mcl = 0, status = 0, frameid = 0; + u32 rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; + u32 rts_rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; + bool use_rts = false; + bool use_cts = false; + bool use_rifs = false; + bool short_preamble[2] = { false, false }; + u8 preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; + u8 rts_preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; + u8 *rts_plcp, rts_plcp_fallback[D11_PHY_HDR_LEN]; + struct ieee80211_rts *rts = NULL; + bool qos; + uint ac; + bool hwtkmic = false; + u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; +#define ANTCFG_NONE 0xFF + u8 antcfg = ANTCFG_NONE; + u8 fbantcfg = ANTCFG_NONE; + uint phyctl1_stf = 0; + u16 durid = 0; + struct ieee80211_tx_rate *txrate[2]; + int k; + struct ieee80211_tx_info *tx_info; + bool is_mcs; + u16 mimo_txbw; + u8 mimo_preamble_type; + + /* locate 802.11 MAC header */ + h = (struct ieee80211_hdr *)(p->data); + qos = ieee80211_is_data_qos(h->frame_control); + + /* compute length of frame in bytes for use in PLCP computations */ + len = p->len; + phylen = len + FCS_LEN; + + /* Get tx_info */ + tx_info = IEEE80211_SKB_CB(p); + + /* add PLCP */ + plcp = skb_push(p, D11_PHY_HDR_LEN); + + /* add Broadcom tx descriptor header */ + txh = (struct d11txh *) skb_push(p, D11_TXH_LEN); + memset(txh, 0, D11_TXH_LEN); + + /* setup frameid */ + if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + /* non-AP STA should never use BCMC queue */ + if (queue == TX_BCMC_FIFO) { + brcms_err(wlc->hw->d11core, + "wl%d: %s: ASSERT queue == TX_BCMC!\n", + wlc->pub->unit, __func__); + frameid = bcmc_fid_generate(wlc, NULL, txh); + } else { + /* Increment the counter for first fragment */ + if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + scb->seqnum[p->priority]++; + + /* extract fragment number from frame first */ + seq = le16_to_cpu(h->seq_ctrl) & FRAGNUM_MASK; + seq |= (scb->seqnum[p->priority] << SEQNUM_SHIFT); + h->seq_ctrl = cpu_to_le16(seq); + + frameid = ((seq << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) | + (queue & TXFID_QUEUE_MASK); + } + } + frameid |= queue & TXFID_QUEUE_MASK; + + /* set the ignpmq bit for all pkts tx'd in PS mode and for beacons */ + if (ieee80211_is_beacon(h->frame_control)) + mcl |= TXC_IGNOREPMQ; + + txrate[0] = tx_info->control.rates; + txrate[1] = txrate[0] + 1; + + /* + * if rate control algorithm didn't give us a fallback + * rate, use the primary rate + */ + if (txrate[1]->idx < 0) + txrate[1] = txrate[0]; + + for (k = 0; k < hw->max_rates; k++) { + is_mcs = txrate[k]->flags & IEEE80211_TX_RC_MCS ? true : false; + if (!is_mcs) { + if ((txrate[k]->idx >= 0) + && (txrate[k]->idx < + hw->wiphy->bands[tx_info->band]->n_bitrates)) { + rspec[k] = + hw->wiphy->bands[tx_info->band]-> + bitrates[txrate[k]->idx].hw_value; + short_preamble[k] = + txrate[k]-> + flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ? + true : false; + } else { + rspec[k] = BRCM_RATE_1M; + } + } else { + rspec[k] = mac80211_wlc_set_nrate(wlc, wlc->band, + NRATE_MCS_INUSE | txrate[k]->idx); + } + + /* + * Currently only support same setting for primay and + * fallback rates. Unify flags for each rate into a + * single value for the frame + */ + use_rts |= + txrate[k]-> + flags & IEEE80211_TX_RC_USE_RTS_CTS ? true : false; + use_cts |= + txrate[k]-> + flags & IEEE80211_TX_RC_USE_CTS_PROTECT ? true : false; + + + /* + * (1) RATE: + * determine and validate primary rate + * and fallback rates + */ + if (!rspec_active(rspec[k])) { + rspec[k] = BRCM_RATE_1M; + } else { + if (!is_multicast_ether_addr(h->addr1)) { + /* set tx antenna config */ + brcms_c_antsel_antcfg_get(wlc->asi, false, + false, 0, 0, &antcfg, &fbantcfg); + } + } + } + + phyctl1_stf = wlc->stf->ss_opmode; + + if (wlc->pub->_n_enab & SUPPORT_11N) { + for (k = 0; k < hw->max_rates; k++) { + /* + * apply siso/cdd to single stream mcs's or ofdm + * if rspec is auto selected + */ + if (((is_mcs_rate(rspec[k]) && + is_single_stream(rspec[k] & RSPEC_RATE_MASK)) || + is_ofdm_rate(rspec[k])) + && ((rspec[k] & RSPEC_OVERRIDE_MCS_ONLY) + || !(rspec[k] & RSPEC_OVERRIDE))) { + rspec[k] &= ~(RSPEC_STF_MASK | RSPEC_STC_MASK); + + /* For SISO MCS use STBC if possible */ + if (is_mcs_rate(rspec[k]) + && BRCMS_STF_SS_STBC_TX(wlc, scb)) { + u8 stc; + + /* Nss for single stream is always 1 */ + stc = 1; + rspec[k] |= (PHY_TXC1_MODE_STBC << + RSPEC_STF_SHIFT) | + (stc << RSPEC_STC_SHIFT); + } else + rspec[k] |= + (phyctl1_stf << RSPEC_STF_SHIFT); + } + + /* + * Is the phy configured to use 40MHZ frames? If + * so then pick the desired txbw + */ + if (brcms_chspec_bw(wlc->chanspec) == BRCMS_40_MHZ) { + /* default txbw is 20in40 SB */ + mimo_ctlchbw = mimo_txbw = + CHSPEC_SB_UPPER(wlc_phy_chanspec_get( + wlc->band->pi)) + ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ; + + if (is_mcs_rate(rspec[k])) { + /* mcs 32 must be 40b/w DUP */ + if ((rspec[k] & RSPEC_RATE_MASK) + == 32) { + mimo_txbw = + PHY_TXC1_BW_40MHZ_DUP; + /* use override */ + } else if (wlc->mimo_40txbw != AUTO) + mimo_txbw = wlc->mimo_40txbw; + /* else check if dst is using 40 Mhz */ + else if (scb->flags & SCB_IS40) + mimo_txbw = PHY_TXC1_BW_40MHZ; + } else if (is_ofdm_rate(rspec[k])) { + if (wlc->ofdm_40txbw != AUTO) + mimo_txbw = wlc->ofdm_40txbw; + } else if (wlc->cck_40txbw != AUTO) { + mimo_txbw = wlc->cck_40txbw; + } + } else { + /* + * mcs32 is 40 b/w only. + * This is possible for probe packets on + * a STA during SCAN + */ + if ((rspec[k] & RSPEC_RATE_MASK) == 32) + /* mcs 0 */ + rspec[k] = RSPEC_MIMORATE; + + mimo_txbw = PHY_TXC1_BW_20MHZ; + } + + /* Set channel width */ + rspec[k] &= ~RSPEC_BW_MASK; + if ((k == 0) || ((k > 0) && is_mcs_rate(rspec[k]))) + rspec[k] |= (mimo_txbw << RSPEC_BW_SHIFT); + else + rspec[k] |= (mimo_ctlchbw << RSPEC_BW_SHIFT); + + /* Disable short GI, not supported yet */ + rspec[k] &= ~RSPEC_SHORT_GI; + + mimo_preamble_type = BRCMS_MM_PREAMBLE; + if (txrate[k]->flags & IEEE80211_TX_RC_GREEN_FIELD) + mimo_preamble_type = BRCMS_GF_PREAMBLE; + + if ((txrate[k]->flags & IEEE80211_TX_RC_MCS) + && (!is_mcs_rate(rspec[k]))) { + brcms_warn(wlc->hw->d11core, + "wl%d: %s: IEEE80211_TX_RC_MCS != is_mcs_rate(rspec)\n", + wlc->pub->unit, __func__); + } + + if (is_mcs_rate(rspec[k])) { + preamble_type[k] = mimo_preamble_type; + + /* + * if SGI is selected, then forced mm + * for single stream + */ + if ((rspec[k] & RSPEC_SHORT_GI) + && is_single_stream(rspec[k] & + RSPEC_RATE_MASK)) + preamble_type[k] = BRCMS_MM_PREAMBLE; + } + + /* should be better conditionalized */ + if (!is_mcs_rate(rspec[0]) + && (tx_info->control.rates[0]. + flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)) + preamble_type[k] = BRCMS_SHORT_PREAMBLE; + } + } else { + for (k = 0; k < hw->max_rates; k++) { + /* Set ctrlchbw as 20Mhz */ + rspec[k] &= ~RSPEC_BW_MASK; + rspec[k] |= (PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT); + + /* for nphy, stf of ofdm frames must follow policies */ + if (BRCMS_ISNPHY(wlc->band) && is_ofdm_rate(rspec[k])) { + rspec[k] &= ~RSPEC_STF_MASK; + rspec[k] |= phyctl1_stf << RSPEC_STF_SHIFT; + } + } + } + + /* Reset these for use with AMPDU's */ + txrate[0]->count = 0; + txrate[1]->count = 0; + + /* (2) PROTECTION, may change rspec */ + if ((ieee80211_is_data(h->frame_control) || + ieee80211_is_mgmt(h->frame_control)) && + (phylen > wlc->RTSThresh) && !is_multicast_ether_addr(h->addr1)) + use_rts = true; + + /* (3) PLCP: determine PLCP header and MAC duration, + * fill struct d11txh */ + brcms_c_compute_plcp(wlc, rspec[0], phylen, plcp); + brcms_c_compute_plcp(wlc, rspec[1], phylen, plcp_fallback); + memcpy(&txh->FragPLCPFallback, + plcp_fallback, sizeof(txh->FragPLCPFallback)); + + /* Length field now put in CCK FBR CRC field */ + if (is_cck_rate(rspec[1])) { + txh->FragPLCPFallback[4] = phylen & 0xff; + txh->FragPLCPFallback[5] = (phylen & 0xff00) >> 8; + } + + /* MIMO-RATE: need validation ?? */ + mainrates = is_ofdm_rate(rspec[0]) ? + D11A_PHY_HDR_GRATE((struct ofdm_phy_hdr *) plcp) : + plcp[0]; + + /* DUR field for main rate */ + if (!ieee80211_is_pspoll(h->frame_control) && + !is_multicast_ether_addr(h->addr1) && !use_rifs) { + durid = + brcms_c_compute_frame_dur(wlc, rspec[0], preamble_type[0], + next_frag_len); + h->duration_id = cpu_to_le16(durid); + } else if (use_rifs) { + /* NAV protect to end of next max packet size */ + durid = + (u16) brcms_c_calc_frame_time(wlc, rspec[0], + preamble_type[0], + DOT11_MAX_FRAG_LEN); + durid += RIFS_11N_TIME; + h->duration_id = cpu_to_le16(durid); + } + + /* DUR field for fallback rate */ + if (ieee80211_is_pspoll(h->frame_control)) + txh->FragDurFallback = h->duration_id; + else if (is_multicast_ether_addr(h->addr1) || use_rifs) + txh->FragDurFallback = 0; + else { + durid = brcms_c_compute_frame_dur(wlc, rspec[1], + preamble_type[1], next_frag_len); + txh->FragDurFallback = cpu_to_le16(durid); + } + + /* (4) MAC-HDR: MacTxControlLow */ + if (frag == 0) + mcl |= TXC_STARTMSDU; + + if (!is_multicast_ether_addr(h->addr1)) + mcl |= TXC_IMMEDACK; + + if (wlc->band->bandtype == BRCM_BAND_5G) + mcl |= TXC_FREQBAND_5G; + + if (CHSPEC_IS40(wlc_phy_chanspec_get(wlc->band->pi))) + mcl |= TXC_BW_40; + + /* set AMIC bit if using hardware TKIP MIC */ + if (hwtkmic) + mcl |= TXC_AMIC; + + txh->MacTxControlLow = cpu_to_le16(mcl); + + /* MacTxControlHigh */ + mch = 0; + + /* Set fallback rate preamble type */ + if ((preamble_type[1] == BRCMS_SHORT_PREAMBLE) || + (preamble_type[1] == BRCMS_GF_PREAMBLE)) { + if (rspec2rate(rspec[1]) != BRCM_RATE_1M) + mch |= TXC_PREAMBLE_DATA_FB_SHORT; + } + + /* MacFrameControl */ + memcpy(&txh->MacFrameControl, &h->frame_control, sizeof(u16)); + txh->TxFesTimeNormal = cpu_to_le16(0); + + txh->TxFesTimeFallback = cpu_to_le16(0); + + /* TxFrameRA */ + memcpy(&txh->TxFrameRA, &h->addr1, ETH_ALEN); + + /* TxFrameID */ + txh->TxFrameID = cpu_to_le16(frameid); + + /* + * TxStatus, Note the case of recreating the first frag of a suppressed + * frame then we may need to reset the retry cnt's via the status reg + */ + txh->TxStatus = cpu_to_le16(status); + + /* + * extra fields for ucode AMPDU aggregation, the new fields are added to + * the END of previous structure so that it's compatible in driver. + */ + txh->MaxNMpdus = cpu_to_le16(0); + txh->MaxABytes_MRT = cpu_to_le16(0); + txh->MaxABytes_FBR = cpu_to_le16(0); + txh->MinMBytes = cpu_to_le16(0); + + /* (5) RTS/CTS: determine RTS/CTS PLCP header and MAC duration, + * furnish struct d11txh */ + /* RTS PLCP header and RTS frame */ + if (use_rts || use_cts) { + if (use_rts && use_cts) + use_cts = false; + + for (k = 0; k < 2; k++) { + rts_rspec[k] = brcms_c_rspec_to_rts_rspec(wlc, rspec[k], + false, + mimo_ctlchbw); + } + + if (!is_ofdm_rate(rts_rspec[0]) && + !((rspec2rate(rts_rspec[0]) == BRCM_RATE_1M) || + (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) { + rts_preamble_type[0] = BRCMS_SHORT_PREAMBLE; + mch |= TXC_PREAMBLE_RTS_MAIN_SHORT; + } + + if (!is_ofdm_rate(rts_rspec[1]) && + !((rspec2rate(rts_rspec[1]) == BRCM_RATE_1M) || + (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) { + rts_preamble_type[1] = BRCMS_SHORT_PREAMBLE; + mch |= TXC_PREAMBLE_RTS_FB_SHORT; + } + + /* RTS/CTS additions to MacTxControlLow */ + if (use_cts) { + txh->MacTxControlLow |= cpu_to_le16(TXC_SENDCTS); + } else { + txh->MacTxControlLow |= cpu_to_le16(TXC_SENDRTS); + txh->MacTxControlLow |= cpu_to_le16(TXC_LONGFRAME); + } + + /* RTS PLCP header */ + rts_plcp = txh->RTSPhyHeader; + if (use_cts) + rts_phylen = DOT11_CTS_LEN + FCS_LEN; + else + rts_phylen = DOT11_RTS_LEN + FCS_LEN; + + brcms_c_compute_plcp(wlc, rts_rspec[0], rts_phylen, rts_plcp); + + /* fallback rate version of RTS PLCP header */ + brcms_c_compute_plcp(wlc, rts_rspec[1], rts_phylen, + rts_plcp_fallback); + memcpy(&txh->RTSPLCPFallback, rts_plcp_fallback, + sizeof(txh->RTSPLCPFallback)); + + /* RTS frame fields... */ + rts = (struct ieee80211_rts *)&txh->rts_frame; + + durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec[0], + rspec[0], rts_preamble_type[0], + preamble_type[0], phylen, false); + rts->duration = cpu_to_le16(durid); + /* fallback rate version of RTS DUR field */ + durid = brcms_c_compute_rtscts_dur(wlc, use_cts, + rts_rspec[1], rspec[1], + rts_preamble_type[1], + preamble_type[1], phylen, false); + txh->RTSDurFallback = cpu_to_le16(durid); + + if (use_cts) { + rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | + IEEE80211_STYPE_CTS); + + memcpy(&rts->ra, &h->addr2, ETH_ALEN); + } else { + rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | + IEEE80211_STYPE_RTS); + + memcpy(&rts->ra, &h->addr1, 2 * ETH_ALEN); + } + + /* mainrate + * low 8 bits: main frag rate/mcs, + * high 8 bits: rts/cts rate/mcs + */ + mainrates |= (is_ofdm_rate(rts_rspec[0]) ? + D11A_PHY_HDR_GRATE( + (struct ofdm_phy_hdr *) rts_plcp) : + rts_plcp[0]) << 8; + } else { + memset(txh->RTSPhyHeader, 0, D11_PHY_HDR_LEN); + memset(&txh->rts_frame, 0, sizeof(struct ieee80211_rts)); + memset(txh->RTSPLCPFallback, 0, sizeof(txh->RTSPLCPFallback)); + txh->RTSDurFallback = 0; + } + +#ifdef SUPPORT_40MHZ + /* add null delimiter count */ + if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && is_mcs_rate(rspec)) + txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = + brcm_c_ampdu_null_delim_cnt(wlc->ampdu, scb, rspec, phylen); + +#endif + + /* + * Now that RTS/RTS FB preamble types are updated, write + * the final value + */ + txh->MacTxControlHigh = cpu_to_le16(mch); + + /* + * MainRates (both the rts and frag plcp rates have + * been calculated now) + */ + txh->MainRates = cpu_to_le16(mainrates); + + /* XtraFrameTypes */ + xfts = frametype(rspec[1], wlc->mimoft); + xfts |= (frametype(rts_rspec[0], wlc->mimoft) << XFTS_RTS_FT_SHIFT); + xfts |= (frametype(rts_rspec[1], wlc->mimoft) << XFTS_FBRRTS_FT_SHIFT); + xfts |= CHSPEC_CHANNEL(wlc_phy_chanspec_get(wlc->band->pi)) << + XFTS_CHANNEL_SHIFT; + txh->XtraFrameTypes = cpu_to_le16(xfts); + + /* PhyTxControlWord */ + phyctl = frametype(rspec[0], wlc->mimoft); + if ((preamble_type[0] == BRCMS_SHORT_PREAMBLE) || + (preamble_type[0] == BRCMS_GF_PREAMBLE)) { + if (rspec2rate(rspec[0]) != BRCM_RATE_1M) + phyctl |= PHY_TXC_SHORT_HDR; + } + + /* phytxant is properly bit shifted */ + phyctl |= brcms_c_stf_d11hdrs_phyctl_txant(wlc, rspec[0]); + txh->PhyTxControlWord = cpu_to_le16(phyctl); + + /* PhyTxControlWord_1 */ + if (BRCMS_PHY_11N_CAP(wlc->band)) { + u16 phyctl1 = 0; + + phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[0]); + txh->PhyTxControlWord_1 = cpu_to_le16(phyctl1); + phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[1]); + txh->PhyTxControlWord_1_Fbr = cpu_to_le16(phyctl1); + + if (use_rts || use_cts) { + phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[0]); + txh->PhyTxControlWord_1_Rts = cpu_to_le16(phyctl1); + phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[1]); + txh->PhyTxControlWord_1_FbrRts = cpu_to_le16(phyctl1); + } + + /* + * For mcs frames, if mixedmode(overloaded with long preamble) + * is going to be set, fill in non-zero MModeLen and/or + * MModeFbrLen it will be unnecessary if they are separated + */ + if (is_mcs_rate(rspec[0]) && + (preamble_type[0] == BRCMS_MM_PREAMBLE)) { + u16 mmodelen = + brcms_c_calc_lsig_len(wlc, rspec[0], phylen); + txh->MModeLen = cpu_to_le16(mmodelen); + } + + if (is_mcs_rate(rspec[1]) && + (preamble_type[1] == BRCMS_MM_PREAMBLE)) { + u16 mmodefbrlen = + brcms_c_calc_lsig_len(wlc, rspec[1], phylen); + txh->MModeFbrLen = cpu_to_le16(mmodefbrlen); + } + } + + ac = skb_get_queue_mapping(p); + if ((scb->flags & SCB_WMECAP) && qos && wlc->edcf_txop[ac]) { + uint frag_dur, dur, dur_fallback; + + /* WME: Update TXOP threshold */ + if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU) && frag == 0) { + frag_dur = + brcms_c_calc_frame_time(wlc, rspec[0], + preamble_type[0], phylen); + + if (rts) { + /* 1 RTS or CTS-to-self frame */ + dur = + brcms_c_calc_cts_time(wlc, rts_rspec[0], + rts_preamble_type[0]); + dur_fallback = + brcms_c_calc_cts_time(wlc, rts_rspec[1], + rts_preamble_type[1]); + /* (SIFS + CTS) + SIFS + frame + SIFS + ACK */ + dur += le16_to_cpu(rts->duration); + dur_fallback += + le16_to_cpu(txh->RTSDurFallback); + } else if (use_rifs) { + dur = frag_dur; + dur_fallback = 0; + } else { + /* frame + SIFS + ACK */ + dur = frag_dur; + dur += + brcms_c_compute_frame_dur(wlc, rspec[0], + preamble_type[0], 0); + + dur_fallback = + brcms_c_calc_frame_time(wlc, rspec[1], + preamble_type[1], + phylen); + dur_fallback += + brcms_c_compute_frame_dur(wlc, rspec[1], + preamble_type[1], 0); + } + /* NEED to set TxFesTimeNormal (hard) */ + txh->TxFesTimeNormal = cpu_to_le16((u16) dur); + /* + * NEED to set fallback rate version of + * TxFesTimeNormal (hard) + */ + txh->TxFesTimeFallback = + cpu_to_le16((u16) dur_fallback); + + /* + * update txop byte threshold (txop minus intraframe + * overhead) + */ + if (wlc->edcf_txop[ac] >= (dur - frag_dur)) { + uint newfragthresh; + + newfragthresh = + brcms_c_calc_frame_len(wlc, + rspec[0], preamble_type[0], + (wlc->edcf_txop[ac] - + (dur - frag_dur))); + /* range bound the fragthreshold */ + if (newfragthresh < DOT11_MIN_FRAG_LEN) + newfragthresh = + DOT11_MIN_FRAG_LEN; + else if (newfragthresh > + wlc->usr_fragthresh) + newfragthresh = + wlc->usr_fragthresh; + /* update the fragthresh and do txc update */ + if (wlc->fragthresh[queue] != + (u16) newfragthresh) + wlc->fragthresh[queue] = + (u16) newfragthresh; + } else { + brcms_warn(wlc->hw->d11core, + "wl%d: %s txop invalid for rate %d\n", + wlc->pub->unit, fifo_names[queue], + rspec2rate(rspec[0])); + } + + if (dur > wlc->edcf_txop[ac]) + brcms_warn(wlc->hw->d11core, + "wl%d: %s: %s txop exceeded phylen %d/%d dur %d/%d\n", + wlc->pub->unit, __func__, + fifo_names[queue], + phylen, wlc->fragthresh[queue], + dur, wlc->edcf_txop[ac]); + } + } + + return 0; +} + +static int brcms_c_tx(struct brcms_c_info *wlc, struct sk_buff *skb) +{ + struct dma_pub *dma; + int fifo, ret = -ENOSPC; + struct d11txh *txh; + u16 frameid = INVALIDFID; + + fifo = brcms_ac_to_fifo(skb_get_queue_mapping(skb)); + dma = wlc->hw->di[fifo]; + txh = (struct d11txh *)(skb->data); + + if (dma->txavail == 0) { + /* + * We sometimes get a frame from mac80211 after stopping + * the queues. This only ever seems to be a single frame + * and is seems likely to be a race. TX_HEADROOM should + * ensure that we have enough space to handle these stray + * packets, so warn if there isn't. If we're out of space + * in the tx ring and the tx queue isn't stopped then + * we've really got a bug; warn loudly if that happens. + */ + brcms_warn(wlc->hw->d11core, + "Received frame for tx with no space in DMA ring\n"); + WARN_ON(!ieee80211_queue_stopped(wlc->pub->ieee_hw, + skb_get_queue_mapping(skb))); + return -ENOSPC; + } + + /* When a BC/MC frame is being committed to the BCMC fifo + * via DMA (NOT PIO), update ucode or BSS info as appropriate. + */ + if (fifo == TX_BCMC_FIFO) + frameid = le16_to_cpu(txh->TxFrameID); + + /* Commit BCMC sequence number in the SHM frame ID location */ + if (frameid != INVALIDFID) { + /* + * To inform the ucode of the last mcast frame posted + * so that it can clear moredata bit + */ + brcms_b_write_shm(wlc->hw, M_BCMC_FID, frameid); + } + + ret = brcms_c_txfifo(wlc, fifo, skb); + /* + * The only reason for brcms_c_txfifo to fail is because + * there weren't any DMA descriptors, but we've already + * checked for that. So if it does fail yell loudly. + */ + WARN_ON_ONCE(ret); + + return ret; +} + +bool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu, + struct ieee80211_hw *hw) +{ + uint fifo; + struct scb *scb = &wlc->pri_scb; + + fifo = brcms_ac_to_fifo(skb_get_queue_mapping(sdu)); + brcms_c_d11hdrs_mac80211(wlc, hw, sdu, scb, 0, 1, fifo, 0); + if (!brcms_c_tx(wlc, sdu)) + return true; + + /* packet discarded */ + dev_kfree_skb_any(sdu); + return false; +} + +int +brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p) +{ + struct dma_pub *dma = wlc->hw->di[fifo]; + int ret; + u16 queue; + + ret = dma_txfast(wlc, dma, p); + if (ret < 0) + wiphy_err(wlc->wiphy, "txfifo: fatal, toss frames !!!\n"); + + /* + * Stop queue if DMA ring is full. Reserve some free descriptors, + * as we sometimes receive a frame from mac80211 after the queues + * are stopped. + */ + queue = skb_get_queue_mapping(p); + if (dma->txavail <= TX_HEADROOM && fifo < TX_BCMC_FIFO && + !ieee80211_queue_stopped(wlc->pub->ieee_hw, queue)) + ieee80211_stop_queue(wlc->pub->ieee_hw, queue); + + return ret; +} + +u32 +brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, + bool use_rspec, u16 mimo_ctlchbw) +{ + u32 rts_rspec = 0; + + if (use_rspec) + /* use frame rate as rts rate */ + rts_rspec = rspec; + else if (wlc->band->gmode && wlc->protection->_g && !is_cck_rate(rspec)) + /* Use 11Mbps as the g protection RTS target rate and fallback. + * Use the brcms_basic_rate() lookup to find the best basic rate + * under the target in case 11 Mbps is not Basic. + * 6 and 9 Mbps are not usually selected by rate selection, but + * even if the OFDM rate we are protecting is 6 or 9 Mbps, 11 + * is more robust. + */ + rts_rspec = brcms_basic_rate(wlc, BRCM_RATE_11M); + else + /* calculate RTS rate and fallback rate based on the frame rate + * RTS must be sent at a basic rate since it is a + * control frame, sec 9.6 of 802.11 spec + */ + rts_rspec = brcms_basic_rate(wlc, rspec); + + if (BRCMS_PHY_11N_CAP(wlc->band)) { + /* set rts txbw to correct side band */ + rts_rspec &= ~RSPEC_BW_MASK; + + /* + * if rspec/rspec_fallback is 40MHz, then send RTS on both + * 20MHz channel (DUP), otherwise send RTS on control channel + */ + if (rspec_is40mhz(rspec) && !is_cck_rate(rts_rspec)) + rts_rspec |= (PHY_TXC1_BW_40MHZ_DUP << RSPEC_BW_SHIFT); + else + rts_rspec |= (mimo_ctlchbw << RSPEC_BW_SHIFT); + + /* pick siso/cdd as default for ofdm */ + if (is_ofdm_rate(rts_rspec)) { + rts_rspec &= ~RSPEC_STF_MASK; + rts_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT); + } + } + return rts_rspec; +} + +/* Update beacon listen interval in shared memory */ +static void brcms_c_bcn_li_upd(struct brcms_c_info *wlc) +{ + /* wake up every DTIM is the default */ + if (wlc->bcn_li_dtim == 1) + brcms_b_write_shm(wlc->hw, M_BCN_LI, 0); + else + brcms_b_write_shm(wlc->hw, M_BCN_LI, + (wlc->bcn_li_dtim << 8) | wlc->bcn_li_bcn); +} + +static void +brcms_b_read_tsf(struct brcms_hardware *wlc_hw, u32 *tsf_l_ptr, + u32 *tsf_h_ptr) +{ + struct bcma_device *core = wlc_hw->d11core; + + /* read the tsf timer low, then high to get an atomic read */ + *tsf_l_ptr = bcma_read32(core, D11REGOFFS(tsf_timerlow)); + *tsf_h_ptr = bcma_read32(core, D11REGOFFS(tsf_timerhigh)); +} + +/* + * recover 64bit TSF value from the 16bit TSF value in the rx header + * given the assumption that the TSF passed in header is within 65ms + * of the current tsf. + * + * 6 5 4 4 3 2 1 + * 3.......6.......8.......0.......2.......4.......6.......8......0 + * |<---------- tsf_h ----------->||<--- tsf_l -->||<-RxTSFTime ->| + * + * The RxTSFTime are the lowest 16 bits and provided by the ucode. The + * tsf_l is filled in by brcms_b_recv, which is done earlier in the + * receive call sequence after rx interrupt. Only the higher 16 bits + * are used. Finally, the tsf_h is read from the tsf register. + */ +static u64 brcms_c_recover_tsf64(struct brcms_c_info *wlc, + struct d11rxhdr *rxh) +{ + u32 tsf_h, tsf_l; + u16 rx_tsf_0_15, rx_tsf_16_31; + + brcms_b_read_tsf(wlc->hw, &tsf_l, &tsf_h); + + rx_tsf_16_31 = (u16)(tsf_l >> 16); + rx_tsf_0_15 = rxh->RxTSFTime; + + /* + * a greater tsf time indicates the low 16 bits of + * tsf_l wrapped, so decrement the high 16 bits. + */ + if ((u16)tsf_l < rx_tsf_0_15) { + rx_tsf_16_31 -= 1; + if (rx_tsf_16_31 == 0xffff) + tsf_h -= 1; + } + + return ((u64)tsf_h << 32) | (((u32)rx_tsf_16_31 << 16) + rx_tsf_0_15); +} + +static void +prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, + struct sk_buff *p, + struct ieee80211_rx_status *rx_status) +{ + int channel; + u32 rspec; + unsigned char *plcp; + + /* fill in TSF and flag its presence */ + rx_status->mactime = brcms_c_recover_tsf64(wlc, rxh); + rx_status->flag |= RX_FLAG_MACTIME_START; + + channel = BRCMS_CHAN_CHANNEL(rxh->RxChan); + + rx_status->band = + channel > 14 ? IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + rx_status->freq = + ieee80211_channel_to_frequency(channel, rx_status->band); + + rx_status->signal = wlc_phy_rssi_compute(wlc->hw->band->pi, rxh); + + /* noise */ + /* qual */ + rx_status->antenna = + (rxh->PhyRxStatus_0 & PRXS0_RXANT_UPSUBBAND) ? 1 : 0; + + plcp = p->data; + + rspec = brcms_c_compute_rspec(rxh, plcp); + if (is_mcs_rate(rspec)) { + rx_status->rate_idx = rspec & RSPEC_RATE_MASK; + rx_status->flag |= RX_FLAG_HT; + if (rspec_is40mhz(rspec)) + rx_status->flag |= RX_FLAG_40MHZ; + } else { + switch (rspec2rate(rspec)) { + case BRCM_RATE_1M: + rx_status->rate_idx = 0; + break; + case BRCM_RATE_2M: + rx_status->rate_idx = 1; + break; + case BRCM_RATE_5M5: + rx_status->rate_idx = 2; + break; + case BRCM_RATE_11M: + rx_status->rate_idx = 3; + break; + case BRCM_RATE_6M: + rx_status->rate_idx = 4; + break; + case BRCM_RATE_9M: + rx_status->rate_idx = 5; + break; + case BRCM_RATE_12M: + rx_status->rate_idx = 6; + break; + case BRCM_RATE_18M: + rx_status->rate_idx = 7; + break; + case BRCM_RATE_24M: + rx_status->rate_idx = 8; + break; + case BRCM_RATE_36M: + rx_status->rate_idx = 9; + break; + case BRCM_RATE_48M: + rx_status->rate_idx = 10; + break; + case BRCM_RATE_54M: + rx_status->rate_idx = 11; + break; + default: + brcms_err(wlc->hw->d11core, + "%s: Unknown rate\n", __func__); + } + + /* + * For 5GHz, we should decrease the index as it is + * a subset of the 2.4G rates. See bitrates field + * of brcms_band_5GHz_nphy (in mac80211_if.c). + */ + if (rx_status->band == IEEE80211_BAND_5GHZ) + rx_status->rate_idx -= BRCMS_LEGACY_5G_RATE_OFFSET; + + /* Determine short preamble and rate_idx */ + if (is_cck_rate(rspec)) { + if (rxh->PhyRxStatus_0 & PRXS0_SHORTH) + rx_status->flag |= RX_FLAG_SHORTPRE; + } else if (is_ofdm_rate(rspec)) { + rx_status->flag |= RX_FLAG_SHORTPRE; + } else { + brcms_err(wlc->hw->d11core, "%s: Unknown modulation\n", + __func__); + } + } + + if (plcp3_issgi(plcp[3])) + rx_status->flag |= RX_FLAG_SHORT_GI; + + if (rxh->RxStatus1 & RXS_DECERR) { + rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC; + brcms_err(wlc->hw->d11core, "%s: RX_FLAG_FAILED_PLCP_CRC\n", + __func__); + } + if (rxh->RxStatus1 & RXS_FCSERR) { + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + brcms_err(wlc->hw->d11core, "%s: RX_FLAG_FAILED_FCS_CRC\n", + __func__); + } +} + +static void +brcms_c_recvctl(struct brcms_c_info *wlc, struct d11rxhdr *rxh, + struct sk_buff *p) +{ + int len_mpdu; + struct ieee80211_rx_status rx_status; + struct ieee80211_hdr *hdr; + + memset(&rx_status, 0, sizeof(rx_status)); + prep_mac80211_status(wlc, rxh, p, &rx_status); + + /* mac header+body length, exclude CRC and plcp header */ + len_mpdu = p->len - D11_PHY_HDR_LEN - FCS_LEN; + skb_pull(p, D11_PHY_HDR_LEN); + __skb_trim(p, len_mpdu); + + /* unmute transmit */ + if (wlc->hw->suspended_fifos) { + hdr = (struct ieee80211_hdr *)p->data; + if (ieee80211_is_beacon(hdr->frame_control)) + brcms_b_mute(wlc->hw, false); + } + + memcpy(IEEE80211_SKB_RXCB(p), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(wlc->pub->ieee_hw, p); +} + +/* calculate frame duration for Mixed-mode L-SIG spoofing, return + * number of bytes goes in the length field + * + * Formula given by HT PHY Spec v 1.13 + * len = 3(nsyms + nstream + 3) - 3 + */ +u16 +brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, + uint mac_len) +{ + uint nsyms, len = 0, kNdps; + + if (is_mcs_rate(ratespec)) { + uint mcs = ratespec & RSPEC_RATE_MASK; + int tot_streams = (mcs_2_txstreams(mcs) + 1) + + rspec_stc(ratespec); + + /* + * the payload duration calculation matches that + * of regular ofdm + */ + /* 1000Ndbps = kbps * 4 */ + kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), + rspec_issgi(ratespec)) * 4; + + if (rspec_stc(ratespec) == 0) + nsyms = + CEIL((APHY_SERVICE_NBITS + 8 * mac_len + + APHY_TAIL_NBITS) * 1000, kNdps); + else + /* STBC needs to have even number of symbols */ + nsyms = + 2 * + CEIL((APHY_SERVICE_NBITS + 8 * mac_len + + APHY_TAIL_NBITS) * 1000, 2 * kNdps); + + /* (+3) account for HT-SIG(2) and HT-STF(1) */ + nsyms += (tot_streams + 3); + /* + * 3 bytes/symbol @ legacy 6Mbps rate + * (-3) excluding service bits and tail bits + */ + len = (3 * nsyms) - 3; + } + + return (u16) len; +} + +static void +brcms_c_mod_prb_rsp_rate_table(struct brcms_c_info *wlc, uint frame_len) +{ + const struct brcms_c_rateset *rs_dflt; + struct brcms_c_rateset rs; + u8 rate; + u16 entry_ptr; + u8 plcp[D11_PHY_HDR_LEN]; + u16 dur, sifs; + uint i; + + sifs = get_sifs(wlc->band); + + rs_dflt = brcms_c_rateset_get_hwrs(wlc); + + brcms_c_rateset_copy(rs_dflt, &rs); + brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); + + /* + * walk the phy rate table and update MAC core SHM + * basic rate table entries + */ + for (i = 0; i < rs.count; i++) { + rate = rs.rates[i] & BRCMS_RATE_MASK; + + entry_ptr = brcms_b_rate_shm_offset(wlc->hw, rate); + + /* Calculate the Probe Response PLCP for the given rate */ + brcms_c_compute_plcp(wlc, rate, frame_len, plcp); + + /* + * Calculate the duration of the Probe Response + * frame plus SIFS for the MAC + */ + dur = (u16) brcms_c_calc_frame_time(wlc, rate, + BRCMS_LONG_PREAMBLE, frame_len); + dur += sifs; + + /* Update the SHM Rate Table entry Probe Response values */ + brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS, + (u16) (plcp[0] + (plcp[1] << 8))); + brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS + 2, + (u16) (plcp[2] + (plcp[3] << 8))); + brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_DUR_POS, dur); + } +} + +int brcms_c_get_header_len(void) +{ + return TXOFF; +} + +static void brcms_c_beacon_write(struct brcms_c_info *wlc, + struct sk_buff *beacon, u16 tim_offset, + u16 dtim_period, bool bcn0, bool bcn1) +{ + size_t len; + struct ieee80211_tx_info *tx_info; + struct brcms_hardware *wlc_hw = wlc->hw; + struct ieee80211_hw *ieee_hw = brcms_c_pub(wlc)->ieee_hw; + + /* Get tx_info */ + tx_info = IEEE80211_SKB_CB(beacon); + + len = min_t(size_t, beacon->len, BCN_TMPL_LEN); + wlc->bcn_rspec = ieee80211_get_tx_rate(ieee_hw, tx_info)->hw_value; + + brcms_c_compute_plcp(wlc, wlc->bcn_rspec, + len + FCS_LEN - D11_PHY_HDR_LEN, beacon->data); + + /* "Regular" and 16 MBSS but not for 4 MBSS */ + /* Update the phytxctl for the beacon based on the rspec */ + brcms_c_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec); + + if (bcn0) { + /* write the probe response into the template region */ + brcms_b_write_template_ram(wlc_hw, T_BCN0_TPL_BASE, + (len + 3) & ~3, beacon->data); + + /* write beacon length to SCR */ + brcms_b_write_shm(wlc_hw, M_BCN0_FRM_BYTESZ, (u16) len); + } + if (bcn1) { + /* write the probe response into the template region */ + brcms_b_write_template_ram(wlc_hw, T_BCN1_TPL_BASE, + (len + 3) & ~3, beacon->data); + + /* write beacon length to SCR */ + brcms_b_write_shm(wlc_hw, M_BCN1_FRM_BYTESZ, (u16) len); + } + + if (tim_offset != 0) { + brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON, + tim_offset + D11B_PHY_HDR_LEN); + brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, dtim_period); + } else { + brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON, + len + D11B_PHY_HDR_LEN); + brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, 0); + } +} + +static void brcms_c_update_beacon_hw(struct brcms_c_info *wlc, + struct sk_buff *beacon, u16 tim_offset, + u16 dtim_period) +{ + struct brcms_hardware *wlc_hw = wlc->hw; + struct bcma_device *core = wlc_hw->d11core; + + /* Hardware beaconing for this config */ + u32 both_valid = MCMD_BCN0VLD | MCMD_BCN1VLD; + + /* Check if both templates are in use, if so sched. an interrupt + * that will call back into this routine + */ + if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid) + /* clear any previous status */ + bcma_write32(core, D11REGOFFS(macintstatus), MI_BCNTPL); + + if (wlc->beacon_template_virgin) { + wlc->beacon_template_virgin = false; + brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true, + true); + /* mark beacon0 valid */ + bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD); + return; + } + + /* Check that after scheduling the interrupt both of the + * templates are still busy. if not clear the int. & remask + */ + if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid) { + wlc->defmacintmask |= MI_BCNTPL; + return; + } + + if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN0VLD)) { + brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true, + false); + /* mark beacon0 valid */ + bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD); + return; + } + if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN1VLD)) { + brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, + false, true); + /* mark beacon0 valid */ + bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN1VLD); + return; + } + return; +} + +/* + * Update all beacons for the system. + */ +void brcms_c_update_beacon(struct brcms_c_info *wlc) +{ + struct brcms_bss_cfg *bsscfg = wlc->bsscfg; + + if (wlc->pub->up && (bsscfg->type == BRCMS_TYPE_AP || + bsscfg->type == BRCMS_TYPE_ADHOC)) { + /* Clear the soft intmask */ + wlc->defmacintmask &= ~MI_BCNTPL; + if (!wlc->beacon) + return; + brcms_c_update_beacon_hw(wlc, wlc->beacon, + wlc->beacon_tim_offset, + wlc->beacon_dtim_period); + } +} + +void brcms_c_set_new_beacon(struct brcms_c_info *wlc, struct sk_buff *beacon, + u16 tim_offset, u16 dtim_period) +{ + if (!beacon) + return; + if (wlc->beacon) + dev_kfree_skb_any(wlc->beacon); + wlc->beacon = beacon; + + /* add PLCP */ + skb_push(wlc->beacon, D11_PHY_HDR_LEN); + wlc->beacon_tim_offset = tim_offset; + wlc->beacon_dtim_period = dtim_period; + brcms_c_update_beacon(wlc); +} + +void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc, + struct sk_buff *probe_resp) +{ + if (!probe_resp) + return; + if (wlc->probe_resp) + dev_kfree_skb_any(wlc->probe_resp); + wlc->probe_resp = probe_resp; + + /* add PLCP */ + skb_push(wlc->probe_resp, D11_PHY_HDR_LEN); + brcms_c_update_probe_resp(wlc, false); +} + +void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable) +{ + /* + * prevent ucode from sending probe responses by setting the timeout + * to 1, it can not send it in that time frame. + */ + wlc->prb_resp_timeout = enable ? BRCMS_PRB_RESP_TIMEOUT : 1; + brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout); + /* TODO: if (enable) => also deactivate receiving of probe request */ +} + +/* Write ssid into shared memory */ +static void +brcms_c_shm_ssid_upd(struct brcms_c_info *wlc, struct brcms_bss_cfg *cfg) +{ + u8 *ssidptr = cfg->SSID; + u16 base = M_SSID; + u8 ssidbuf[IEEE80211_MAX_SSID_LEN]; + + /* padding the ssid with zero and copy it into shm */ + memset(ssidbuf, 0, IEEE80211_MAX_SSID_LEN); + memcpy(ssidbuf, ssidptr, cfg->SSID_len); + + brcms_c_copyto_shm(wlc, base, ssidbuf, IEEE80211_MAX_SSID_LEN); + brcms_b_write_shm(wlc->hw, M_SSIDLEN, (u16) cfg->SSID_len); +} + +static void +brcms_c_bss_update_probe_resp(struct brcms_c_info *wlc, + struct brcms_bss_cfg *cfg, + struct sk_buff *probe_resp, + bool suspend) +{ + int len; + + len = min_t(size_t, probe_resp->len, BCN_TMPL_LEN); + + if (suspend) + brcms_c_suspend_mac_and_wait(wlc); + + /* write the probe response into the template region */ + brcms_b_write_template_ram(wlc->hw, T_PRS_TPL_BASE, + (len + 3) & ~3, probe_resp->data); + + /* write the length of the probe response frame (+PLCP/-FCS) */ + brcms_b_write_shm(wlc->hw, M_PRB_RESP_FRM_LEN, (u16) len); + + /* write the SSID and SSID length */ + brcms_c_shm_ssid_upd(wlc, cfg); + + /* + * Write PLCP headers and durations for probe response frames + * at all rates. Use the actual frame length covered by the + * PLCP header for the call to brcms_c_mod_prb_rsp_rate_table() + * by subtracting the PLCP len and adding the FCS. + */ + brcms_c_mod_prb_rsp_rate_table(wlc, + (u16)len + FCS_LEN - D11_PHY_HDR_LEN); + + if (suspend) + brcms_c_enable_mac(wlc); +} + +void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend) +{ + struct brcms_bss_cfg *bsscfg = wlc->bsscfg; + + /* update AP or IBSS probe responses */ + if (wlc->pub->up && (bsscfg->type == BRCMS_TYPE_AP || + bsscfg->type == BRCMS_TYPE_ADHOC)) { + if (!wlc->probe_resp) + return; + brcms_c_bss_update_probe_resp(wlc, bsscfg, wlc->probe_resp, + suspend); + } +} + +int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, + uint *blocks) +{ + if (fifo >= NFIFO) + return -EINVAL; + + *blocks = wlc_hw->xmtfifo_sz[fifo]; + + return 0; +} + +void +brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset, + const u8 *addr) +{ + brcms_b_set_addrmatch(wlc->hw, match_reg_offset, addr); + if (match_reg_offset == RCM_BSSID_OFFSET) + memcpy(wlc->bsscfg->BSSID, addr, ETH_ALEN); +} + +/* + * Flag 'scan in progress' to withhold dynamic phy calibration + */ +void brcms_c_scan_start(struct brcms_c_info *wlc) +{ + wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, true); +} + +void brcms_c_scan_stop(struct brcms_c_info *wlc) +{ + wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, false); +} + +void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state) +{ + wlc->pub->associated = state; +} + +/* + * When a remote STA/AP is removed by Mac80211, or when it can no longer accept + * AMPDU traffic, packets pending in hardware have to be invalidated so that + * when later on hardware releases them, they can be handled appropriately. + */ +void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, + struct ieee80211_sta *sta, + void (*dma_callback_fn)) +{ + struct dma_pub *dmah; + int i; + for (i = 0; i < NFIFO; i++) { + dmah = hw->di[i]; + if (dmah != NULL) + dma_walk_packets(dmah, dma_callback_fn, sta); + } +} + +int brcms_c_get_curband(struct brcms_c_info *wlc) +{ + return wlc->band->bandunit; +} + +bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc) +{ + int i; + + /* Kick DMA to send any pending AMPDU */ + for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++) + if (wlc->hw->di[i]) + dma_kick_tx(wlc->hw->di[i]); + + return !brcms_txpktpendtot(wlc); +} + +void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval) +{ + wlc->bcn_li_bcn = interval; + if (wlc->pub->up) + brcms_c_bcn_li_upd(wlc); +} + +u64 brcms_c_tsf_get(struct brcms_c_info *wlc) +{ + u32 tsf_h, tsf_l; + u64 tsf; + + brcms_b_read_tsf(wlc->hw, &tsf_l, &tsf_h); + + tsf = tsf_h; + tsf <<= 32; + tsf |= tsf_l; + + return tsf; +} + +void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf) +{ + u32 tsf_h, tsf_l; + + brcms_c_time_lock(wlc); + + tsf_l = tsf; + tsf_h = (tsf >> 32); + + /* read the tsf timer low, then high to get an atomic read */ + bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_timerlow), tsf_l); + bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_timerhigh), tsf_h); + + brcms_c_time_unlock(wlc); +} + +int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr) +{ + uint qdbm; + + /* Remove override bit and clip to max qdbm value */ + qdbm = min_t(uint, txpwr * BRCMS_TXPWR_DB_FACTOR, 0xff); + return wlc_phy_txpower_set(wlc->band->pi, qdbm, false); +} + +int brcms_c_get_tx_power(struct brcms_c_info *wlc) +{ + uint qdbm; + bool override; + + wlc_phy_txpower_get(wlc->band->pi, &qdbm, &override); + + /* Return qdbm units */ + return (int)(qdbm / BRCMS_TXPWR_DB_FACTOR); +} + +/* Process received frames */ +/* + * Return true if more frames need to be processed. false otherwise. + * Param 'bound' indicates max. # frames to process before break out. + */ +static void brcms_c_recv(struct brcms_c_info *wlc, struct sk_buff *p) +{ + struct d11rxhdr *rxh; + struct ieee80211_hdr *h; + uint len; + bool is_amsdu; + + /* frame starts with rxhdr */ + rxh = (struct d11rxhdr *) (p->data); + + /* strip off rxhdr */ + skb_pull(p, BRCMS_HWRXOFF); + + /* MAC inserts 2 pad bytes for a4 headers or QoS or A-MSDU subframes */ + if (rxh->RxStatus1 & RXS_PBPRES) { + if (p->len < 2) { + brcms_err(wlc->hw->d11core, + "wl%d: recv: rcvd runt of len %d\n", + wlc->pub->unit, p->len); + goto toss; + } + skb_pull(p, 2); + } + + h = (struct ieee80211_hdr *)(p->data + D11_PHY_HDR_LEN); + len = p->len; + + if (rxh->RxStatus1 & RXS_FCSERR) { + if (!(wlc->filter_flags & FIF_FCSFAIL)) + goto toss; + } + + /* check received pkt has at least frame control field */ + if (len < D11_PHY_HDR_LEN + sizeof(h->frame_control)) + goto toss; + + /* not supporting A-MSDU */ + is_amsdu = rxh->RxStatus2 & RXS_AMSDU_MASK; + if (is_amsdu) + goto toss; + + brcms_c_recvctl(wlc, rxh, p); + return; + + toss: + brcmu_pkt_buf_free_skb(p); +} + +/* Process received frames */ +/* + * Return true if more frames need to be processed. false otherwise. + * Param 'bound' indicates max. # frames to process before break out. + */ +static bool +brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound) +{ + struct sk_buff *p; + struct sk_buff *next = NULL; + struct sk_buff_head recv_frames; + + uint n = 0; + uint bound_limit = bound ? RXBND : -1; + bool morepending = false; + + skb_queue_head_init(&recv_frames); + + /* gather received frames */ + do { + /* !give others some time to run! */ + if (n >= bound_limit) + break; + + morepending = dma_rx(wlc_hw->di[fifo], &recv_frames); + n++; + } while (morepending); + + /* post more rbufs */ + dma_rxfill(wlc_hw->di[fifo]); + + /* process each frame */ + skb_queue_walk_safe(&recv_frames, p, next) { + struct d11rxhdr_le *rxh_le; + struct d11rxhdr *rxh; + + skb_unlink(p, &recv_frames); + rxh_le = (struct d11rxhdr_le *)p->data; + rxh = (struct d11rxhdr *)p->data; + + /* fixup rx header endianness */ + rxh->RxFrameSize = le16_to_cpu(rxh_le->RxFrameSize); + rxh->PhyRxStatus_0 = le16_to_cpu(rxh_le->PhyRxStatus_0); + rxh->PhyRxStatus_1 = le16_to_cpu(rxh_le->PhyRxStatus_1); + rxh->PhyRxStatus_2 = le16_to_cpu(rxh_le->PhyRxStatus_2); + rxh->PhyRxStatus_3 = le16_to_cpu(rxh_le->PhyRxStatus_3); + rxh->PhyRxStatus_4 = le16_to_cpu(rxh_le->PhyRxStatus_4); + rxh->PhyRxStatus_5 = le16_to_cpu(rxh_le->PhyRxStatus_5); + rxh->RxStatus1 = le16_to_cpu(rxh_le->RxStatus1); + rxh->RxStatus2 = le16_to_cpu(rxh_le->RxStatus2); + rxh->RxTSFTime = le16_to_cpu(rxh_le->RxTSFTime); + rxh->RxChan = le16_to_cpu(rxh_le->RxChan); + + brcms_c_recv(wlc_hw->wlc, p); + } + + return morepending; +} + +/* second-level interrupt processing + * Return true if another dpc needs to be re-scheduled. false otherwise. + * Param 'bounded' indicates if applicable loops should be bounded. + */ +bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) +{ + u32 macintstatus; + struct brcms_hardware *wlc_hw = wlc->hw; + struct bcma_device *core = wlc_hw->d11core; + + if (brcms_deviceremoved(wlc)) { + brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, + __func__); + brcms_down(wlc->wl); + return false; + } + + /* grab and clear the saved software intstatus bits */ + macintstatus = wlc->macintstatus; + wlc->macintstatus = 0; + + brcms_dbg_int(core, "wl%d: macintstatus 0x%x\n", + wlc_hw->unit, macintstatus); + + WARN_ON(macintstatus & MI_PRQ); /* PRQ Interrupt in non-MBSS */ + + /* tx status */ + if (macintstatus & MI_TFS) { + bool fatal; + if (brcms_b_txstatus(wlc->hw, bounded, &fatal)) + wlc->macintstatus |= MI_TFS; + if (fatal) { + brcms_err(core, "MI_TFS: fatal\n"); + goto fatal; + } + } + + if (macintstatus & (MI_TBTT | MI_DTIM_TBTT)) + brcms_c_tbtt(wlc); + + /* ATIM window end */ + if (macintstatus & MI_ATIMWINEND) { + brcms_dbg_info(core, "end of ATIM window\n"); + bcma_set32(core, D11REGOFFS(maccommand), wlc->qvalid); + wlc->qvalid = 0; + } + + /* + * received data or control frame, MI_DMAINT is + * indication of RX_FIFO interrupt + */ + if (macintstatus & MI_DMAINT) + if (brcms_b_recv(wlc_hw, RX_FIFO, bounded)) + wlc->macintstatus |= MI_DMAINT; + + /* noise sample collected */ + if (macintstatus & MI_BG_NOISE) + wlc_phy_noise_sample_intr(wlc_hw->band->pi); + + if (macintstatus & MI_GP0) { + brcms_err(core, "wl%d: PSM microcode watchdog fired at %d " + "(seconds). Resetting.\n", wlc_hw->unit, wlc_hw->now); + + printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n", + __func__, ai_get_chip_id(wlc_hw->sih), + ai_get_chiprev(wlc_hw->sih)); + brcms_fatal_error(wlc_hw->wlc->wl); + } + + /* gptimer timeout */ + if (macintstatus & MI_TO) + bcma_write32(core, D11REGOFFS(gptimer), 0); + + if (macintstatus & MI_RFDISABLE) { + brcms_dbg_info(core, "wl%d: BMAC Detected a change on the" + " RF Disable Input\n", wlc_hw->unit); + brcms_rfkill_set_hw_state(wlc->wl); + } + + /* BCN template is available */ + if (macintstatus & MI_BCNTPL) + brcms_c_update_beacon(wlc); + + /* it isn't done and needs to be resched if macintstatus is non-zero */ + return wlc->macintstatus != 0; + + fatal: + brcms_fatal_error(wlc_hw->wlc->wl); + return wlc->macintstatus != 0; +} + +void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx) +{ + struct bcma_device *core = wlc->hw->d11core; + struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan; + u16 chanspec; + + brcms_dbg_info(core, "wl%d\n", wlc->pub->unit); + + chanspec = ch20mhz_chspec(ch->hw_value); + + brcms_b_init(wlc->hw, chanspec); + + /* update beacon listen interval */ + brcms_c_bcn_li_upd(wlc); + + /* write ethernet address to core */ + brcms_c_set_mac(wlc->bsscfg); + brcms_c_set_bssid(wlc->bsscfg); + + /* Update tsf_cfprep if associated and up */ + if (wlc->pub->associated && wlc->pub->up) { + u32 bi; + + /* get beacon period and convert to uS */ + bi = wlc->bsscfg->current_bss->beacon_period << 10; + /* + * update since init path would reset + * to default value + */ + bcma_write32(core, D11REGOFFS(tsf_cfprep), + bi << CFPREP_CBI_SHIFT); + + /* Update maccontrol PM related bits */ + brcms_c_set_ps_ctrl(wlc); + } + + brcms_c_bandinit_ordered(wlc, chanspec); + + /* init probe response timeout */ + brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout); + + /* init max burst txop (framebursting) */ + brcms_b_write_shm(wlc->hw, M_MBURST_TXOP, + (wlc-> + _rifs ? (EDCF_AC_VO_TXOP_AP << 5) : MAXFRAMEBURST_TXOP)); + + /* initialize maximum allowed duty cycle */ + brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_ofdm, true, true); + brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_cck, false, true); + + /* + * Update some shared memory locations related to + * max AMPDU size allowed to received + */ + brcms_c_ampdu_shm_upd(wlc->ampdu); + + /* band-specific inits */ + brcms_c_bsinit(wlc); + + /* Enable EDCF mode (while the MAC is suspended) */ + bcma_set16(core, D11REGOFFS(ifs_ctl), IFS_USEEDCF); + brcms_c_edcf_setparams(wlc, false); + + /* read the ucode version if we have not yet done so */ + if (wlc->ucode_rev == 0) { + u16 rev; + u16 patch; + + rev = brcms_b_read_shm(wlc->hw, M_BOM_REV_MAJOR); + patch = brcms_b_read_shm(wlc->hw, M_BOM_REV_MINOR); + wlc->ucode_rev = (rev << NBITS(u16)) | patch; + snprintf(wlc->wiphy->fw_version, + sizeof(wlc->wiphy->fw_version), "%u.%u", rev, patch); + } + + /* ..now really unleash hell (allow the MAC out of suspend) */ + brcms_c_enable_mac(wlc); + + /* suspend the tx fifos and mute the phy for preism cac time */ + if (mute_tx) + brcms_b_mute(wlc->hw, true); + + /* enable the RF Disable Delay timer */ + bcma_write32(core, D11REGOFFS(rfdisabledly), RFDISABLE_DEFAULT); + + /* + * Initialize WME parameters; if they haven't been set by some other + * mechanism (IOVar, etc) then read them from the hardware. + */ + if (GFIELD(wlc->wme_retries[0], EDCF_SHORT) == 0) { + /* Uninitialized; read from HW */ + int ac; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + wlc->wme_retries[ac] = + brcms_b_read_shm(wlc->hw, M_AC_TXLMT_ADDR(ac)); + } +} + +/* + * The common driver entry routine. Error codes should be unique + */ +struct brcms_c_info * +brcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit, + bool piomode, uint *perr) +{ + struct brcms_c_info *wlc; + uint err = 0; + uint i, j; + struct brcms_pub *pub; + + /* allocate struct brcms_c_info state and its substructures */ + wlc = brcms_c_attach_malloc(unit, &err, 0); + if (wlc == NULL) + goto fail; + wlc->wiphy = wl->wiphy; + pub = wlc->pub; + +#if defined(DEBUG) + wlc_info_dbg = wlc; +#endif + + wlc->band = wlc->bandstate[0]; + wlc->core = wlc->corestate; + wlc->wl = wl; + pub->unit = unit; + pub->_piomode = piomode; + wlc->bandinit_pending = false; + wlc->beacon_template_virgin = true; + + /* populate struct brcms_c_info with default values */ + brcms_c_info_init(wlc, unit); + + /* update sta/ap related parameters */ + brcms_c_ap_upd(wlc); + + /* + * low level attach steps(all hw accesses go + * inside, no more in rest of the attach) + */ + err = brcms_b_attach(wlc, core, unit, piomode); + if (err) + goto fail; + + brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, OFF); + + pub->phy_11ncapable = BRCMS_PHY_11N_CAP(wlc->band); + + /* disable allowed duty cycle */ + wlc->tx_duty_cycle_ofdm = 0; + wlc->tx_duty_cycle_cck = 0; + + brcms_c_stf_phy_chain_calc(wlc); + + /* txchain 1: txant 0, txchain 2: txant 1 */ + if (BRCMS_ISNPHY(wlc->band) && (wlc->stf->txstreams == 1)) + wlc->stf->txant = wlc->stf->hw_txchain - 1; + + /* push to BMAC driver */ + wlc_phy_stf_chain_init(wlc->band->pi, wlc->stf->hw_txchain, + wlc->stf->hw_rxchain); + + /* pull up some info resulting from the low attach */ + for (i = 0; i < NFIFO; i++) + wlc->core->txavail[i] = wlc->hw->txavail[i]; + + memcpy(&wlc->perm_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); + memcpy(&pub->cur_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); + + for (j = 0; j < wlc->pub->_nbands; j++) { + wlc->band = wlc->bandstate[j]; + + if (!brcms_c_attach_stf_ant_init(wlc)) { + err = 24; + goto fail; + } + + /* default contention windows size limits */ + wlc->band->CWmin = APHY_CWMIN; + wlc->band->CWmax = PHY_CWMAX; + + /* init gmode value */ + if (wlc->band->bandtype == BRCM_BAND_2G) { + wlc->band->gmode = GMODE_AUTO; + brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, + wlc->band->gmode); + } + + /* init _n_enab supported mode */ + if (BRCMS_PHY_11N_CAP(wlc->band)) { + pub->_n_enab = SUPPORT_11N; + brcms_c_protection_upd(wlc, BRCMS_PROT_N_USER, + ((pub->_n_enab == + SUPPORT_11N) ? WL_11N_2x2 : + WL_11N_3x3)); + } + + /* init per-band default rateset, depend on band->gmode */ + brcms_default_rateset(wlc, &wlc->band->defrateset); + + /* fill in hw_rateset */ + brcms_c_rateset_filter(&wlc->band->defrateset, + &wlc->band->hw_rateset, false, + BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK, + (bool) (wlc->pub->_n_enab & SUPPORT_11N)); + } + + /* + * update antenna config due to + * wlc->stf->txant/txchain/ant_rx_ovr change + */ + brcms_c_stf_phy_txant_upd(wlc); + + /* attach each modules */ + err = brcms_c_attach_module(wlc); + if (err != 0) + goto fail; + + if (!brcms_c_timers_init(wlc, unit)) { + wiphy_err(wl->wiphy, "wl%d: %s: init_timer failed\n", unit, + __func__); + err = 32; + goto fail; + } + + /* depend on rateset, gmode */ + wlc->cmi = brcms_c_channel_mgr_attach(wlc); + if (!wlc->cmi) { + wiphy_err(wl->wiphy, "wl%d: %s: channel_mgr_attach failed" + "\n", unit, __func__); + err = 33; + goto fail; + } + + /* init default when all parameters are ready, i.e. ->rateset */ + brcms_c_bss_default_init(wlc); + + /* + * Complete the wlc default state initializations.. + */ + + wlc->bsscfg->wlc = wlc; + + wlc->mimoft = FT_HT; + wlc->mimo_40txbw = AUTO; + wlc->ofdm_40txbw = AUTO; + wlc->cck_40txbw = AUTO; + brcms_c_update_mimo_band_bwcap(wlc, BRCMS_N_BW_20IN2G_40IN5G); + + /* Set default values of SGI */ + if (BRCMS_SGI_CAP_PHY(wlc)) { + brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | + BRCMS_N_SGI_40)); + } else if (BRCMS_ISSSLPNPHY(wlc->band)) { + brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | + BRCMS_N_SGI_40)); + } else { + brcms_c_ht_update_sgi_rx(wlc, 0); + } + + brcms_b_antsel_set(wlc->hw, wlc->asi->antsel_avail); + + if (perr) + *perr = 0; + + return wlc; + + fail: + wiphy_err(wl->wiphy, "wl%d: %s: failed with err %d\n", + unit, __func__, err); + if (wlc) + brcms_c_detach(wlc); + + if (perr) + *perr = err; + return NULL; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h new file mode 100644 index 000000000000..c4d135cff04a --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h @@ -0,0 +1,669 @@ +/* + * Copyright (c) 2010 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 _BRCM_MAIN_H_ +#define _BRCM_MAIN_H_ + +#include + +#include +#include "types.h" +#include "d11.h" +#include "scb.h" + +#define INVCHANNEL 255 /* invalid channel */ + +/* max # brcms_c_module_register() calls */ +#define BRCMS_MAXMODULES 22 + +#define SEQNUM_SHIFT 4 +#define SEQNUM_MAX 0x1000 + +#define NTXRATE 64 /* # tx MPDUs rate is reported for */ + +/* Maximum wait time for a MAC suspend */ +/* uS: 83mS is max packet time (64KB ampdu @ 6Mbps) */ +#define BRCMS_MAX_MAC_SUSPEND 83000 + +/* responses for probe requests older that this are tossed, zero to disable */ +#define BRCMS_PRB_RESP_TIMEOUT 0 /* Disable probe response timeout */ + +/* transmit buffer max headroom for protocol headers */ +#define TXOFF (D11_TXH_LEN + D11_PHY_HDR_LEN) + +/* Macros for doing definition and get/set of bitfields + * Usage example, e.g. a three-bit field (bits 4-6): + * #define _M BITFIELD_MASK(3) + * #define _S 4 + * ... + * regval = R_REG(osh, ®s->regfoo); + * field = GFIELD(regval, ); + * regval = SFIELD(regval, , 1); + * W_REG(osh, ®s->regfoo, regval); + */ +#define BITFIELD_MASK(width) \ + (((unsigned)1 << (width)) - 1) +#define GFIELD(val, field) \ + (((val) >> field ## _S) & field ## _M) +#define SFIELD(val, field, bits) \ + (((val) & (~(field ## _M << field ## _S))) | \ + ((unsigned)(bits) << field ## _S)) + +#define SW_TIMER_MAC_STAT_UPD 30 /* periodic MAC stats update */ + +/* max # supported core revisions (0 .. MAXCOREREV - 1) */ +#define MAXCOREREV 28 + +/* Double check that unsupported cores are not enabled */ +#if CONF_MSK(D11CONF, 0x4f) || CONF_GE(D11CONF, MAXCOREREV) +#error "Configuration for D11CONF includes unsupported versions." +#endif /* Bad versions */ + +/* values for shortslot_override */ +#define BRCMS_SHORTSLOT_AUTO -1 /* Driver will manage Shortslot setting */ +#define BRCMS_SHORTSLOT_OFF 0 /* Turn off short slot */ +#define BRCMS_SHORTSLOT_ON 1 /* Turn on short slot */ + +/* value for short/long and mixmode/greenfield preamble */ +#define BRCMS_LONG_PREAMBLE (0) +#define BRCMS_SHORT_PREAMBLE (1 << 0) +#define BRCMS_GF_PREAMBLE (1 << 1) +#define BRCMS_MM_PREAMBLE (1 << 2) +#define BRCMS_IS_MIMO_PREAMBLE(_pre) (((_pre) == BRCMS_GF_PREAMBLE) || \ + ((_pre) == BRCMS_MM_PREAMBLE)) + +/* TxFrameID */ +/* seq and frag bits: SEQNUM_SHIFT, FRAGNUM_MASK (802.11.h) */ +/* rate epoch bits: TXFID_RATE_SHIFT, TXFID_RATE_MASK ((wlc_rate.c) */ +#define TXFID_QUEUE_MASK 0x0007 /* Bits 0-2 */ +#define TXFID_SEQ_MASK 0x7FE0 /* Bits 5-15 */ +#define TXFID_SEQ_SHIFT 5 /* Number of bit shifts */ +#define TXFID_RATE_PROBE_MASK 0x8000 /* Bit 15 for rate probe */ +#define TXFID_RATE_MASK 0x0018 /* Mask for bits 3 and 4 */ +#define TXFID_RATE_SHIFT 3 /* Shift 3 bits for rate mask */ + +/* promote boardrev */ +#define BOARDREV_PROMOTABLE 0xFF /* from */ +#define BOARDREV_PROMOTED 1 /* to */ + +#define DATA_BLOCK_TX_SUPR (1 << 4) + +/* Ucode MCTL_WAKE override bits */ +#define BRCMS_WAKE_OVERRIDE_CLKCTL 0x01 +#define BRCMS_WAKE_OVERRIDE_PHYREG 0x02 +#define BRCMS_WAKE_OVERRIDE_MACSUSPEND 0x04 +#define BRCMS_WAKE_OVERRIDE_TXFIFO 0x08 +#define BRCMS_WAKE_OVERRIDE_FORCEFAST 0x10 + +/* stuff pulled in from wlc.c */ + +/* Interrupt bit error summary. Don't include I_RU: we refill DMA at other + * times; and if we run out, constant I_RU interrupts may cause lockup. We + * will still get error counts from rx0ovfl. + */ +#define I_ERRORS (I_PC | I_PD | I_DE | I_RO | I_XU) +/* default software intmasks */ +#define DEF_RXINTMASK (I_RI) /* enable rx int on rxfifo only */ +#define DEF_MACINTMASK (MI_TXSTOP | MI_TBTT | MI_ATIMWINEND | MI_PMQ | \ + MI_PHYTXERR | MI_DMAINT | MI_TFS | MI_BG_NOISE | \ + MI_CCA | MI_TO | MI_GP0 | MI_RFDISABLE | MI_PWRUP) + +#define MAXTXPKTS 6 /* max # pkts pending */ + +/* frameburst */ +#define MAXTXFRAMEBURST 8 /* vanilla xpress mode: max frames/burst */ +#define MAXFRAMEBURST_TXOP 10000 /* Frameburst TXOP in usec */ + +#define NFIFO 6 /* # tx/rx fifopairs */ + +/* PLL requests */ + +/* pll is shared on old chips */ +#define BRCMS_PLLREQ_SHARED 0x1 +/* hold pll for radio monitor register checking */ +#define BRCMS_PLLREQ_RADIO_MON 0x2 +/* hold/release pll for some short operation */ +#define BRCMS_PLLREQ_FLIP 0x4 + +#define CHANNEL_BANDUNIT(wlc, ch) \ + (((ch) <= CH_MAX_2G_CHANNEL) ? BAND_2G_INDEX : BAND_5G_INDEX) + +#define OTHERBANDUNIT(wlc) \ + ((uint)((wlc)->band->bandunit ? BAND_2G_INDEX : BAND_5G_INDEX)) + +/* + * 802.11 protection information + * + * _g: use g spec protection, driver internal. + * g_override: override for use of g spec protection. + * gmode_user: user config gmode, operating band->gmode is different. + * overlap: Overlap BSS/IBSS protection for both 11g and 11n. + * nmode_user: user config nmode, operating pub->nmode is different. + * n_cfg: use OFDM protection on MIMO frames. + * n_cfg_override: override for use of N protection. + * nongf: non-GF present protection. + * nongf_override: override for use of GF protection. + * n_pam_override: override for preamble: MM or GF. + * n_obss: indicated OBSS Non-HT STA present. +*/ +struct brcms_protection { + bool _g; + s8 g_override; + u8 gmode_user; + s8 overlap; + s8 nmode_user; + s8 n_cfg; + s8 n_cfg_override; + bool nongf; + s8 nongf_override; + s8 n_pam_override; + bool n_obss; +}; + +/* + * anything affecting the single/dual streams/antenna operation + * + * hw_txchain: HW txchain bitmap cfg. + * txchain: txchain bitmap being used. + * txstreams: number of txchains being used. + * hw_rxchain: HW rxchain bitmap cfg. + * rxchain: rxchain bitmap being used. + * rxstreams: number of rxchains being used. + * ant_rx_ovr: rx antenna override. + * txant: userTx antenna setting. + * phytxant: phyTx antenna setting in txheader. + * ss_opmode: singlestream Operational mode, 0:siso; 1:cdd. + * ss_algosel_auto: if true, use wlc->stf->ss_algo_channel; + * else use wlc->band->stf->ss_mode_band. + * ss_algo_channel: ss based on per-channel algo: 0: SISO, 1: CDD 2: STBC. + * rxchain_restore_delay: delay time to restore default rxchain. + * ldpc: AUTO/ON/OFF ldpc cap supported. + * txcore[MAX_STREAMS_SUPPORTED + 1]: bitmap of selected core for each Nsts. + * spatial_policy: + */ +struct brcms_stf { + u8 hw_txchain; + u8 txchain; + u8 txstreams; + u8 hw_rxchain; + u8 rxchain; + u8 rxstreams; + u8 ant_rx_ovr; + s8 txant; + u16 phytxant; + u8 ss_opmode; + bool ss_algosel_auto; + u16 ss_algo_channel; + u8 rxchain_restore_delay; + s8 ldpc; + u8 txcore[MAX_STREAMS_SUPPORTED + 1]; + s8 spatial_policy; +}; + +#define BRCMS_STF_SS_STBC_TX(wlc, scb) \ + (((wlc)->stf->txstreams > 1) && (((wlc)->band->band_stf_stbc_tx == ON) \ + || (((scb)->flags & SCB_STBCCAP) && \ + (wlc)->band->band_stf_stbc_tx == AUTO && \ + isset(&((wlc)->stf->ss_algo_channel), PHY_TXC1_MODE_STBC)))) + +#define BRCMS_STBC_CAP_PHY(wlc) (BRCMS_ISNPHY(wlc->band) && \ + NREV_GE(wlc->band->phyrev, 3)) + +#define BRCMS_SGI_CAP_PHY(wlc) ((BRCMS_ISNPHY(wlc->band) && \ + NREV_GE(wlc->band->phyrev, 3)) || \ + BRCMS_ISLCNPHY(wlc->band)) + +#define BRCMS_CHAN_PHYTYPE(x) (((x) & RXS_CHAN_PHYTYPE_MASK) \ + >> RXS_CHAN_PHYTYPE_SHIFT) +#define BRCMS_CHAN_CHANNEL(x) (((x) & RXS_CHAN_ID_MASK) \ + >> RXS_CHAN_ID_SHIFT) + +/* + * core state (mac) + */ +struct brcms_core { + uint coreidx; /* # sb enumerated core */ + + /* fifo */ + uint *txavail[NFIFO]; /* # tx descriptors available */ + + struct macstat *macstat_snapshot; /* mac hw prev read values */ +}; + +/* + * band state (phy+ana+radio) + */ +struct brcms_band { + int bandtype; /* BRCM_BAND_2G, BRCM_BAND_5G */ + uint bandunit; /* bandstate[] index */ + + u16 phytype; /* phytype */ + u16 phyrev; + u16 radioid; + u16 radiorev; + struct brcms_phy_pub *pi; /* pointer to phy specific information */ + bool abgphy_encore; + + u8 gmode; /* currently active gmode */ + + struct scb *hwrs_scb; /* permanent scb for hw rateset */ + + /* band-specific copy of default_bss.rateset */ + struct brcms_c_rateset defrateset; + + u8 band_stf_ss_mode; /* Configured STF type, 0:siso; 1:cdd */ + s8 band_stf_stbc_tx; /* STBC TX 0:off; 1:force on; -1:auto */ + /* rates supported by chip (phy-specific) */ + struct brcms_c_rateset hw_rateset; + u8 basic_rate[BRCM_MAXRATE + 1]; /* basic rates indexed by rate */ + bool mimo_cap_40; /* 40 MHz cap enabled on this band */ + s8 antgain; /* antenna gain from srom */ + + u16 CWmin; /* minimum size of contention window, in unit of aSlotTime */ + u16 CWmax; /* maximum size of contention window, in unit of aSlotTime */ + struct ieee80211_supported_band band; +}; + +/* module control blocks */ +struct modulecb { + /* module name : NULL indicates empty array member */ + char name[32]; + /* handle passed when handler 'doiovar' is called */ + struct brcms_info *hdl; + + int (*down_fn)(void *handle); /* down handler. Note: the int returned + * by the down function is a count of the + * number of timers that could not be + * freed. + */ + +}; + +struct brcms_hw_band { + int bandtype; /* BRCM_BAND_2G, BRCM_BAND_5G */ + uint bandunit; /* bandstate[] index */ + u16 mhfs[MHFMAX]; /* MHF array shadow */ + u8 bandhw_stf_ss_mode; /* HW configured STF type, 0:siso; 1:cdd */ + u16 CWmin; + u16 CWmax; + u32 core_flags; + + u16 phytype; /* phytype */ + u16 phyrev; + u16 radioid; + u16 radiorev; + struct brcms_phy_pub *pi; /* pointer to phy specific information */ + bool abgphy_encore; +}; + +struct brcms_hardware { + bool _piomode; /* true if pio mode */ + struct brcms_c_info *wlc; + + /* fifo */ + struct dma_pub *di[NFIFO]; /* dma handles, per fifo */ + + uint unit; /* device instance number */ + + /* version info */ + u16 vendorid; /* PCI vendor id */ + u16 deviceid; /* PCI device id */ + uint corerev; /* core revision */ + u8 sromrev; /* version # of the srom */ + u16 boardrev; /* version # of particular board */ + u32 boardflags; /* Board specific flags from srom */ + u32 boardflags2; /* More board flags if sromrev >= 4 */ + u32 machwcap; /* MAC capabilities */ + u32 machwcap_backup; /* backup of machwcap */ + + struct si_pub *sih; /* SI handle (cookie for siutils calls) */ + struct bcma_device *d11core; /* pointer to 802.11 core */ + struct phy_shim_info *physhim; /* phy shim layer handler */ + struct shared_phy *phy_sh; /* pointer to shared phy state */ + struct brcms_hw_band *band;/* pointer to active per-band state */ + /* band state per phy/radio */ + struct brcms_hw_band *bandstate[MAXBANDS]; + u16 bmac_phytxant; /* cache of high phytxant state */ + bool shortslot; /* currently using 11g ShortSlot timing */ + u16 SRL; /* 802.11 dot11ShortRetryLimit */ + u16 LRL; /* 802.11 dot11LongRetryLimit */ + u16 SFBL; /* Short Frame Rate Fallback Limit */ + u16 LFBL; /* Long Frame Rate Fallback Limit */ + + bool up; /* d11 hardware up and running */ + uint now; /* # elapsed seconds */ + uint _nbands; /* # bands supported */ + u16 chanspec; /* bmac chanspec shadow */ + + uint *txavail[NFIFO]; /* # tx descriptors available */ + const u16 *xmtfifo_sz; /* fifo size in 256B for each xmt fifo */ + + u32 pllreq; /* pll requests to keep PLL on */ + + u8 suspended_fifos; /* Which TX fifo to remain awake for */ + u32 maccontrol; /* Cached value of maccontrol */ + uint mac_suspend_depth; /* current depth of mac_suspend levels */ + u32 wake_override; /* bit flags to force MAC to WAKE mode */ + u32 mute_override; /* Prevent ucode from sending beacons */ + u8 etheraddr[ETH_ALEN]; /* currently configured ethernet address */ + bool noreset; /* true= do not reset hw, used by WLC_OUT */ + bool forcefastclk; /* true if h/w is forcing to use fast clk */ + bool clk; /* core is out of reset and has clock */ + bool sbclk; /* sb has clock */ + bool phyclk; /* phy is out of reset and has clock */ + + bool ucode_loaded; /* true after ucode downloaded */ + + + u8 hw_stf_ss_opmode; /* STF single stream operation mode */ + u8 antsel_type; /* Type of boardlevel mimo antenna switch-logic + * 0 = N/A, 1 = 2x4 board, 2 = 2x3 CB2 board + */ + u32 antsel_avail; /* + * put struct antsel_info here if more info is + * needed + */ +}; + +/* + * Principal common driver data structure. + * + * pub: pointer to driver public state. + * wl: pointer to specific private state. + * hw: HW related state. + * clkreq_override: setting for clkreq for PCIE : Auto, 0, 1. + * fastpwrup_dly: time in us needed to bring up d11 fast clock. + * macintstatus: bit channel between isr and dpc. + * macintmask: sw runtime master macintmask value. + * defmacintmask: default "on" macintmask value. + * clk: core is out of reset and has clock. + * core: pointer to active io core. + * band: pointer to active per-band state. + * corestate: per-core state (one per hw core). + * bandstate: per-band state (one per phy/radio). + * qvalid: DirFrmQValid and BcMcFrmQValid. + * ampdu: ampdu module handler. + * asi: antsel module handler. + * cmi: channel manager module handler. + * vendorid: PCI vendor id. + * deviceid: PCI device id. + * ucode_rev: microcode revision. + * machwcap: MAC capabilities, BMAC shadow. + * perm_etheraddr: original sprom local ethernet address. + * bandlocked: disable auto multi-band switching. + * bandinit_pending: track band init in auto band. + * radio_monitor: radio timer is running. + * going_down: down path intermediate variable. + * wdtimer: timer for watchdog routine. + * radio_timer: timer for hw radio button monitor routine. + * monitor: monitor (MPDU sniffing) mode. + * bcnmisc_monitor: bcns promisc mode override for monitor. + * _rifs: enable per-packet rifs. + * bcn_li_bcn: beacon listen interval in # beacons. + * bcn_li_dtim: beacon listen interval in # dtims. + * WDarmed: watchdog timer is armed. + * WDlast: last time wlc_watchdog() was called. + * edcf_txop[IEEE80211_NUM_ACS]: current txop for each ac. + * wme_retries: per-AC retry limits. + * bsscfg: set of BSS configurations, idx 0 is default and always valid. + * cfg: the primary bsscfg (can be AP or STA). + * modulecb: + * mimoft: SIGN or 11N. + * cck_40txbw: 11N, cck tx b/w override when in 40MHZ mode. + * ofdm_40txbw: 11N, ofdm tx b/w override when in 40MHZ mode. + * mimo_40txbw: 11N, mimo tx b/w override when in 40MHZ mode. + * default_bss: configured BSS parameters. + * mc_fid_counter: BC/MC FIFO frame ID counter. + * country_default: saved country for leaving 802.11d auto-country mode. + * autocountry_default: initial country for 802.11d auto-country mode. + * prb_resp_timeout: do not send prb resp if request older + * than this, 0 = disable. + * home_chanspec: shared home chanspec. + * chanspec: target operational channel. + * usr_fragthresh: user configured fragmentation threshold. + * fragthresh[NFIFO]: per-fifo fragmentation thresholds. + * RTSThresh: 802.11 dot11RTSThreshold. + * SRL: 802.11 dot11ShortRetryLimit. + * LRL: 802.11 dot11LongRetryLimit. + * SFBL: Short Frame Rate Fallback Limit. + * LFBL: Long Frame Rate Fallback Limit. + * shortslot: currently using 11g ShortSlot timing. + * shortslot_override: 11g ShortSlot override. + * include_legacy_erp: include Legacy ERP info elt ID 47 as well as g ID 42. + * PLCPHdr_override: 802.11b Preamble Type override. + * stf: + * bcn_rspec: save bcn ratespec purpose. + * tempsense_lasttime; + * tx_duty_cycle_ofdm: maximum allowed duty cycle for OFDM. + * tx_duty_cycle_cck: maximum allowed duty cycle for CCK. + * wiphy: + * pri_scb: primary Station Control Block + */ +struct brcms_c_info { + struct brcms_pub *pub; + struct brcms_info *wl; + struct brcms_hardware *hw; + + /* clock */ + u16 fastpwrup_dly; + + /* interrupt */ + u32 macintstatus; + u32 macintmask; + u32 defmacintmask; + + bool clk; + + /* multiband */ + struct brcms_core *core; + struct brcms_band *band; + struct brcms_core *corestate; + struct brcms_band *bandstate[MAXBANDS]; + + /* packet queue */ + uint qvalid; + + struct ampdu_info *ampdu; + struct antsel_info *asi; + struct brcms_cm_info *cmi; + + u16 vendorid; + u16 deviceid; + uint ucode_rev; + + u8 perm_etheraddr[ETH_ALEN]; + + bool bandlocked; + bool bandinit_pending; + + bool radio_monitor; + bool going_down; + + bool beacon_template_virgin; + + struct brcms_timer *wdtimer; + struct brcms_timer *radio_timer; + + /* promiscuous */ + uint filter_flags; + + /* driver feature */ + bool _rifs; + + /* AP-STA synchronization, power save */ + u8 bcn_li_bcn; + u8 bcn_li_dtim; + + bool WDarmed; + u32 WDlast; + + /* WME */ + u16 edcf_txop[IEEE80211_NUM_ACS]; + + u16 wme_retries[IEEE80211_NUM_ACS]; + + struct brcms_bss_cfg *bsscfg; + + struct modulecb *modulecb; + + u8 mimoft; + s8 cck_40txbw; + s8 ofdm_40txbw; + s8 mimo_40txbw; + + struct brcms_bss_info *default_bss; + + u16 mc_fid_counter; + + char country_default[BRCM_CNTRY_BUF_SZ]; + char autocountry_default[BRCM_CNTRY_BUF_SZ]; + u16 prb_resp_timeout; + + u16 home_chanspec; + + /* PHY parameters */ + u16 chanspec; + u16 usr_fragthresh; + u16 fragthresh[NFIFO]; + u16 RTSThresh; + u16 SRL; + u16 LRL; + u16 SFBL; + u16 LFBL; + + /* network config */ + bool shortslot; + s8 shortslot_override; + bool include_legacy_erp; + + struct brcms_protection *protection; + s8 PLCPHdr_override; + + struct brcms_stf *stf; + + u32 bcn_rspec; + + uint tempsense_lasttime; + + u16 tx_duty_cycle_ofdm; + u16 tx_duty_cycle_cck; + + struct wiphy *wiphy; + struct scb pri_scb; + + struct sk_buff *beacon; + u16 beacon_tim_offset; + u16 beacon_dtim_period; + struct sk_buff *probe_resp; +}; + +/* antsel module specific state */ +struct antsel_info { + struct brcms_c_info *wlc; /* pointer to main wlc structure */ + struct brcms_pub *pub; /* pointer to public fn */ + u8 antsel_type; /* Type of boardlevel mimo antenna switch-logic + * 0 = N/A, 1 = 2x4 board, 2 = 2x3 CB2 board + */ + u8 antsel_antswitch; /* board level antenna switch type */ + bool antsel_avail; /* Ant selection availability (SROM based) */ + struct brcms_antselcfg antcfg_11n; /* antenna configuration */ + struct brcms_antselcfg antcfg_cur; /* current antenna config (auto) */ +}; + +enum brcms_bss_type { + BRCMS_TYPE_STATION, + BRCMS_TYPE_AP, + BRCMS_TYPE_ADHOC, +}; + +/* + * BSS configuration state + * + * wlc: wlc to which this bsscfg belongs to. + * type: interface type + * SSID_len: the length of SSID + * SSID: SSID string + * + * + * BSSID: BSSID (associated) + * cur_etheraddr: h/w address + * flags: BSSCFG flags; see below + * + * current_bss: BSS parms in ASSOCIATED state + * + * + * ID: 'unique' ID of this bsscfg, assigned at bsscfg allocation + */ +struct brcms_bss_cfg { + struct brcms_c_info *wlc; + enum brcms_bss_type type; + u8 SSID_len; + u8 SSID[IEEE80211_MAX_SSID_LEN]; + u8 BSSID[ETH_ALEN]; + struct brcms_bss_info *current_bss; +}; + +int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p); +int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, + uint *blocks); + +int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config); +void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags); +u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, uint mac_len); +u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, + bool use_rspec, u16 mimo_ctlchbw); +u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only, + u32 rts_rate, u32 frame_rate, + u8 rts_preamble_type, u8 frame_preamble_type, + uint frame_len, bool ba); +void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, + struct ieee80211_sta *sta, void (*dma_callback_fn)); +void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend); +int brcms_c_set_nmode(struct brcms_c_info *wlc); +void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, u32 bcn_rate); +void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type); +void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, + bool mute, struct txpwr_limits *txpwr); +void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v); +u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset); +void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val, + int bands); +void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags); +void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val); +void brcms_b_phy_reset(struct brcms_hardware *wlc_hw); +void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw); +void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw); +void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw, + u32 override_bit); +void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw, + u32 override_bit); +void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, + int len, void *buf); +u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate); +void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset, + const void *buf, int len, u32 sel); +void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, + void *buf, int len, u32 sel); +void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode); +u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw); +void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk); +void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk); +void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on); +void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant); +void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode); +void brcms_c_init_scb(struct scb *scb); + +#endif /* _BRCM_MAIN_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c new file mode 100644 index 000000000000..1c4e9dd57960 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c @@ -0,0 +1,2953 @@ +/* + * Copyright (c) 2010 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 +#include +#include +#include +#include +#include "phy_hal.h" +#include "phy_int.h" +#include "phy_radio.h" +#include "phy_lcn.h" +#include "phyreg_n.h" + +#define VALID_N_RADIO(radioid) ((radioid == BCM2055_ID) || \ + (radioid == BCM2056_ID) || \ + (radioid == BCM2057_ID)) + +#define VALID_LCN_RADIO(radioid) (radioid == BCM2064_ID) + +#define VALID_RADIO(pi, radioid) ( \ + (ISNPHY(pi) ? VALID_N_RADIO(radioid) : false) || \ + (ISLCNPHY(pi) ? VALID_LCN_RADIO(radioid) : false)) + +/* basic mux operation - can be optimized on several architectures */ +#define MUX(pred, true, false) ((pred) ? (true) : (false)) + +/* modulo inc/dec - assumes x E [0, bound - 1] */ +#define MODINC(x, bound) MUX((x) == (bound) - 1, 0, (x) + 1) + +/* modulo inc/dec, bound = 2^k */ +#define MODDEC_POW2(x, bound) (((x) - 1) & ((bound) - 1)) +#define MODINC_POW2(x, bound) (((x) + 1) & ((bound) - 1)) + +struct chan_info_basic { + u16 chan; + u16 freq; +}; + +static const struct chan_info_basic chan_info_all[] = { + {1, 2412}, + {2, 2417}, + {3, 2422}, + {4, 2427}, + {5, 2432}, + {6, 2437}, + {7, 2442}, + {8, 2447}, + {9, 2452}, + {10, 2457}, + {11, 2462}, + {12, 2467}, + {13, 2472}, + {14, 2484}, + + {34, 5170}, + {38, 5190}, + {42, 5210}, + {46, 5230}, + + {36, 5180}, + {40, 5200}, + {44, 5220}, + {48, 5240}, + {52, 5260}, + {56, 5280}, + {60, 5300}, + {64, 5320}, + + {100, 5500}, + {104, 5520}, + {108, 5540}, + {112, 5560}, + {116, 5580}, + {120, 5600}, + {124, 5620}, + {128, 5640}, + {132, 5660}, + {136, 5680}, + {140, 5700}, + + {149, 5745}, + {153, 5765}, + {157, 5785}, + {161, 5805}, + {165, 5825}, + + {184, 4920}, + {188, 4940}, + {192, 4960}, + {196, 4980}, + {200, 5000}, + {204, 5020}, + {208, 5040}, + {212, 5060}, + {216, 5080} +}; + +static const u8 ofdm_rate_lookup[] = { + + BRCM_RATE_48M, + BRCM_RATE_24M, + BRCM_RATE_12M, + BRCM_RATE_6M, + BRCM_RATE_54M, + BRCM_RATE_36M, + BRCM_RATE_18M, + BRCM_RATE_9M +}; + +#define PHY_WREG_LIMIT 24 + +void wlc_phyreg_enter(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + wlapi_bmac_ucode_wake_override_phyreg_set(pi->sh->physhim); +} + +void wlc_phyreg_exit(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + wlapi_bmac_ucode_wake_override_phyreg_clear(pi->sh->physhim); +} + +void wlc_radioreg_enter(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + wlapi_bmac_mctrl(pi->sh->physhim, MCTL_LOCK_RADIO, MCTL_LOCK_RADIO); + + udelay(10); +} + +void wlc_radioreg_exit(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + (void)bcma_read16(pi->d11core, D11REGOFFS(phyversion)); + pi->phy_wreg = 0; + wlapi_bmac_mctrl(pi->sh->physhim, MCTL_LOCK_RADIO, 0); +} + +u16 read_radio_reg(struct brcms_phy *pi, u16 addr) +{ + u16 data; + + if ((addr == RADIO_IDCODE)) + return 0xffff; + + switch (pi->pubpi.phy_type) { + case PHY_TYPE_N: + if (!CONF_HAS(PHYTYPE, PHY_TYPE_N)) + break; + if (NREV_GE(pi->pubpi.phy_rev, 7)) + addr |= RADIO_2057_READ_OFF; + else + addr |= RADIO_2055_READ_OFF; + break; + + case PHY_TYPE_LCN: + if (!CONF_HAS(PHYTYPE, PHY_TYPE_LCN)) + break; + addr |= RADIO_2064_READ_OFF; + break; + + default: + break; + } + + if ((D11REV_GE(pi->sh->corerev, 24)) || + (D11REV_IS(pi->sh->corerev, 22) + && (pi->pubpi.phy_type != PHY_TYPE_SSN))) { + bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), addr); + data = bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); + } else { + bcma_wflush16(pi->d11core, D11REGOFFS(phy4waddr), addr); + data = bcma_read16(pi->d11core, D11REGOFFS(phy4wdatalo)); + } + pi->phy_wreg = 0; + + return data; +} + +void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val) +{ + if ((D11REV_GE(pi->sh->corerev, 24)) || + (D11REV_IS(pi->sh->corerev, 22) + && (pi->pubpi.phy_type != PHY_TYPE_SSN))) { + + bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), addr); + bcma_write16(pi->d11core, D11REGOFFS(radioregdata), val); + } else { + bcma_wflush16(pi->d11core, D11REGOFFS(phy4waddr), addr); + bcma_write16(pi->d11core, D11REGOFFS(phy4wdatalo), val); + } + + if ((pi->d11core->bus->hosttype == BCMA_HOSTTYPE_PCI) && + (++pi->phy_wreg >= pi->phy_wreg_limit)) { + (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); + pi->phy_wreg = 0; + } +} + +static u32 read_radio_id(struct brcms_phy *pi) +{ + u32 id; + + if (D11REV_GE(pi->sh->corerev, 24)) { + u32 b0, b1, b2; + + bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), 0); + b0 = (u32) bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); + bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), 1); + b1 = (u32) bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); + bcma_wflush16(pi->d11core, D11REGOFFS(radioregaddr), 2); + b2 = (u32) bcma_read16(pi->d11core, D11REGOFFS(radioregdata)); + + id = ((b0 & 0xf) << 28) | (((b2 << 8) | b1) << 12) | ((b0 >> 4) + & 0xf); + } else { + bcma_wflush16(pi->d11core, D11REGOFFS(phy4waddr), RADIO_IDCODE); + id = (u32) bcma_read16(pi->d11core, D11REGOFFS(phy4wdatalo)); + id |= (u32) bcma_read16(pi->d11core, + D11REGOFFS(phy4wdatahi)) << 16; + } + pi->phy_wreg = 0; + return id; +} + +void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val) +{ + u16 rval; + + rval = read_radio_reg(pi, addr); + write_radio_reg(pi, addr, (rval & val)); +} + +void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val) +{ + u16 rval; + + rval = read_radio_reg(pi, addr); + write_radio_reg(pi, addr, (rval | val)); +} + +void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask) +{ + u16 rval; + + rval = read_radio_reg(pi, addr); + write_radio_reg(pi, addr, (rval ^ mask)); +} + +void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val) +{ + u16 rval; + + rval = read_radio_reg(pi, addr); + write_radio_reg(pi, addr, (rval & ~mask) | (val & mask)); +} + +void write_phy_channel_reg(struct brcms_phy *pi, uint val) +{ + bcma_write16(pi->d11core, D11REGOFFS(phychannel), val); +} + +u16 read_phy_reg(struct brcms_phy *pi, u16 addr) +{ + bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); + + pi->phy_wreg = 0; + return bcma_read16(pi->d11core, D11REGOFFS(phyregdata)); +} + +void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val) +{ +#ifdef CONFIG_BCM47XX + bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); + bcma_write16(pi->d11core, D11REGOFFS(phyregdata), val); + if (addr == 0x72) + (void)bcma_read16(pi->d11core, D11REGOFFS(phyregdata)); +#else + bcma_write32(pi->d11core, D11REGOFFS(phyregaddr), addr | (val << 16)); + if ((pi->d11core->bus->hosttype == BCMA_HOSTTYPE_PCI) && + (++pi->phy_wreg >= pi->phy_wreg_limit)) { + pi->phy_wreg = 0; + (void)bcma_read16(pi->d11core, D11REGOFFS(phyversion)); + } +#endif +} + +void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val) +{ + bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); + bcma_mask16(pi->d11core, D11REGOFFS(phyregdata), val); + pi->phy_wreg = 0; +} + +void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val) +{ + bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); + bcma_set16(pi->d11core, D11REGOFFS(phyregdata), val); + pi->phy_wreg = 0; +} + +void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val) +{ + val &= mask; + bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), addr); + bcma_maskset16(pi->d11core, D11REGOFFS(phyregdata), ~mask, val); + pi->phy_wreg = 0; +} + +static void wlc_set_phy_uninitted(struct brcms_phy *pi) +{ + int i, j; + + pi->initialized = false; + + pi->tx_vos = 0xffff; + pi->nrssi_table_delta = 0x7fffffff; + pi->rc_cal = 0xffff; + pi->mintxbias = 0xffff; + pi->txpwridx = -1; + if (ISNPHY(pi)) { + pi->phy_spuravoid = SPURAVOID_DISABLE; + + if (NREV_GE(pi->pubpi.phy_rev, 3) + && NREV_LT(pi->pubpi.phy_rev, 7)) + pi->phy_spuravoid = SPURAVOID_AUTO; + + pi->nphy_papd_skip = 0; + pi->nphy_papd_epsilon_offset[0] = 0xf588; + pi->nphy_papd_epsilon_offset[1] = 0xf588; + pi->nphy_txpwr_idx[0] = 128; + pi->nphy_txpwr_idx[1] = 128; + pi->nphy_txpwrindex[0].index_internal = 40; + pi->nphy_txpwrindex[1].index_internal = 40; + pi->phy_pabias = 0; + } else { + pi->phy_spuravoid = SPURAVOID_AUTO; + } + pi->radiopwr = 0xffff; + for (i = 0; i < STATIC_NUM_RF; i++) { + for (j = 0; j < STATIC_NUM_BB; j++) + pi->stats_11b_txpower[i][j] = -1; + } +} + +struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp) +{ + struct shared_phy *sh; + + sh = kzalloc(sizeof(struct shared_phy), GFP_ATOMIC); + if (sh == NULL) + return NULL; + + sh->physhim = shp->physhim; + sh->unit = shp->unit; + sh->corerev = shp->corerev; + + sh->vid = shp->vid; + sh->did = shp->did; + sh->chip = shp->chip; + sh->chiprev = shp->chiprev; + sh->chippkg = shp->chippkg; + sh->sromrev = shp->sromrev; + sh->boardtype = shp->boardtype; + sh->boardrev = shp->boardrev; + sh->boardflags = shp->boardflags; + sh->boardflags2 = shp->boardflags2; + + sh->fast_timer = PHY_SW_TIMER_FAST; + sh->slow_timer = PHY_SW_TIMER_SLOW; + sh->glacial_timer = PHY_SW_TIMER_GLACIAL; + + sh->rssi_mode = RSSI_ANT_MERGE_MAX; + + return sh; +} + +static void wlc_phy_timercb_phycal(struct brcms_phy *pi) +{ + uint delay = 5; + + if (PHY_PERICAL_MPHASE_PENDING(pi)) { + if (!pi->sh->up) { + wlc_phy_cal_perical_mphase_reset(pi); + return; + } + + if (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi)) { + + delay = 1000; + wlc_phy_cal_perical_mphase_restart(pi); + } else + wlc_phy_cal_perical_nphy_run(pi, PHY_PERICAL_AUTO); + wlapi_add_timer(pi->phycal_timer, delay, 0); + return; + } + +} + +static u32 wlc_phy_get_radio_ver(struct brcms_phy *pi) +{ + u32 ver; + + ver = read_radio_id(pi); + + return ver; +} + +struct brcms_phy_pub * +wlc_phy_attach(struct shared_phy *sh, struct bcma_device *d11core, + int bandtype, struct wiphy *wiphy) +{ + struct brcms_phy *pi; + u32 sflags = 0; + uint phyversion; + u32 idcode; + int i; + + if (D11REV_IS(sh->corerev, 4)) + sflags = SISF_2G_PHY | SISF_5G_PHY; + else + sflags = bcma_aread32(d11core, BCMA_IOST); + + if (bandtype == BRCM_BAND_5G) { + if ((sflags & (SISF_5G_PHY | SISF_DB_PHY)) == 0) + return NULL; + } + + pi = sh->phy_head; + if ((sflags & SISF_DB_PHY) && pi) { + wlapi_bmac_corereset(pi->sh->physhim, pi->pubpi.coreflags); + pi->refcnt++; + return &pi->pubpi_ro; + } + + pi = kzalloc(sizeof(struct brcms_phy), GFP_ATOMIC); + if (pi == NULL) + return NULL; + pi->wiphy = wiphy; + pi->d11core = d11core; + pi->sh = sh; + pi->phy_init_por = true; + pi->phy_wreg_limit = PHY_WREG_LIMIT; + + pi->txpwr_percent = 100; + + pi->do_initcal = true; + + pi->phycal_tempdelta = 0; + + if (bandtype == BRCM_BAND_2G && (sflags & SISF_2G_PHY)) + pi->pubpi.coreflags = SICF_GMODE; + + wlapi_bmac_corereset(pi->sh->physhim, pi->pubpi.coreflags); + phyversion = bcma_read16(pi->d11core, D11REGOFFS(phyversion)); + + pi->pubpi.phy_type = PHY_TYPE(phyversion); + pi->pubpi.phy_rev = phyversion & PV_PV_MASK; + + if (pi->pubpi.phy_type == PHY_TYPE_LCNXN) { + pi->pubpi.phy_type = PHY_TYPE_N; + pi->pubpi.phy_rev += LCNXN_BASEREV; + } + pi->pubpi.phy_corenum = PHY_CORE_NUM_2; + pi->pubpi.ana_rev = (phyversion & PV_AV_MASK) >> PV_AV_SHIFT; + + if (pi->pubpi.phy_type != PHY_TYPE_N && + pi->pubpi.phy_type != PHY_TYPE_LCN) + goto err; + + if (bandtype == BRCM_BAND_5G) { + if (!ISNPHY(pi)) + goto err; + } else if (!ISNPHY(pi) && !ISLCNPHY(pi)) { + goto err; + } + + wlc_phy_anacore((struct brcms_phy_pub *) pi, ON); + + idcode = wlc_phy_get_radio_ver(pi); + pi->pubpi.radioid = + (idcode & IDCODE_ID_MASK) >> IDCODE_ID_SHIFT; + pi->pubpi.radiorev = + (idcode & IDCODE_REV_MASK) >> IDCODE_REV_SHIFT; + pi->pubpi.radiover = + (idcode & IDCODE_VER_MASK) >> IDCODE_VER_SHIFT; + if (!VALID_RADIO(pi, pi->pubpi.radioid)) + goto err; + + wlc_phy_switch_radio((struct brcms_phy_pub *) pi, OFF); + + wlc_set_phy_uninitted(pi); + + pi->bw = WL_CHANSPEC_BW_20; + pi->radio_chanspec = (bandtype == BRCM_BAND_2G) ? + ch20mhz_chspec(1) : ch20mhz_chspec(36); + + pi->rxiq_samps = PHY_NOISE_SAMPLE_LOG_NUM_NPHY; + pi->rxiq_antsel = ANT_RX_DIV_DEF; + + pi->watchdog_override = true; + + pi->cal_type_override = PHY_PERICAL_AUTO; + + pi->nphy_saved_noisevars.bufcount = 0; + + if (ISNPHY(pi)) + pi->min_txpower = PHY_TXPWR_MIN_NPHY; + else + pi->min_txpower = PHY_TXPWR_MIN; + + pi->sh->phyrxchain = 0x3; + + pi->rx2tx_biasentry = -1; + + pi->phy_txcore_disable_temp = PHY_CHAIN_TX_DISABLE_TEMP; + pi->phy_txcore_enable_temp = + PHY_CHAIN_TX_DISABLE_TEMP - PHY_HYSTERESIS_DELTATEMP; + pi->phy_tempsense_offset = 0; + pi->phy_txcore_heatedup = false; + + pi->nphy_lastcal_temp = -50; + + pi->phynoise_polling = true; + if (ISNPHY(pi) || ISLCNPHY(pi)) + pi->phynoise_polling = false; + + for (i = 0; i < TXP_NUM_RATES; i++) { + pi->txpwr_limit[i] = BRCMS_TXPWR_MAX; + pi->txpwr_env_limit[i] = BRCMS_TXPWR_MAX; + pi->tx_user_target[i] = BRCMS_TXPWR_MAX; + } + + pi->radiopwr_override = RADIOPWR_OVERRIDE_DEF; + + pi->user_txpwr_at_rfport = false; + + if (ISNPHY(pi)) { + + pi->phycal_timer = wlapi_init_timer(pi->sh->physhim, + wlc_phy_timercb_phycal, + pi, "phycal"); + if (!pi->phycal_timer) + goto err; + + if (!wlc_phy_attach_nphy(pi)) + goto err; + + } else if (ISLCNPHY(pi)) { + if (!wlc_phy_attach_lcnphy(pi)) + goto err; + + } + + pi->refcnt++; + pi->next = pi->sh->phy_head; + sh->phy_head = pi; + + memcpy(&pi->pubpi_ro, &pi->pubpi, sizeof(struct brcms_phy_pub)); + + return &pi->pubpi_ro; + +err: + kfree(pi); + return NULL; +} + +void wlc_phy_detach(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + if (pih) { + if (--pi->refcnt) + return; + + if (pi->phycal_timer) { + wlapi_free_timer(pi->phycal_timer); + pi->phycal_timer = NULL; + } + + if (pi->sh->phy_head == pi) + pi->sh->phy_head = pi->next; + else if (pi->sh->phy_head->next == pi) + pi->sh->phy_head->next = NULL; + + if (pi->pi_fptr.detach) + (pi->pi_fptr.detach)(pi); + + kfree(pi); + } +} + +bool +wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype, u16 *phyrev, + u16 *radioid, u16 *radiover) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + *phytype = (u16) pi->pubpi.phy_type; + *phyrev = (u16) pi->pubpi.phy_rev; + *radioid = pi->pubpi.radioid; + *radiover = pi->pubpi.radiorev; + + return true; +} + +bool wlc_phy_get_encore(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + return pi->pubpi.abgphy_encore; +} + +u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + return pi->pubpi.coreflags; +} + +void wlc_phy_anacore(struct brcms_phy_pub *pih, bool on) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + if (ISNPHY(pi)) { + if (on) { + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + write_phy_reg(pi, 0xa6, 0x0d); + write_phy_reg(pi, 0x8f, 0x0); + write_phy_reg(pi, 0xa7, 0x0d); + write_phy_reg(pi, 0xa5, 0x0); + } else { + write_phy_reg(pi, 0xa5, 0x0); + } + } else { + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + write_phy_reg(pi, 0x8f, 0x07ff); + write_phy_reg(pi, 0xa6, 0x0fd); + write_phy_reg(pi, 0xa5, 0x07ff); + write_phy_reg(pi, 0xa7, 0x0fd); + } else { + write_phy_reg(pi, 0xa5, 0x7fff); + } + } + } else if (ISLCNPHY(pi)) { + if (on) { + and_phy_reg(pi, 0x43b, + ~((0x1 << 0) | (0x1 << 1) | (0x1 << 2))); + } else { + or_phy_reg(pi, 0x43c, + (0x1 << 0) | (0x1 << 1) | (0x1 << 2)); + or_phy_reg(pi, 0x43b, + (0x1 << 0) | (0x1 << 1) | (0x1 << 2)); + } + } +} + +u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + u32 phy_bw_clkbits = 0; + + if (pi && (ISNPHY(pi) || ISLCNPHY(pi))) { + switch (pi->bw) { + case WL_CHANSPEC_BW_10: + phy_bw_clkbits = SICF_BW10; + break; + case WL_CHANSPEC_BW_20: + phy_bw_clkbits = SICF_BW20; + break; + case WL_CHANSPEC_BW_40: + phy_bw_clkbits = SICF_BW40; + break; + default: + break; + } + } + + return phy_bw_clkbits; +} + +void wlc_phy_por_inform(struct brcms_phy_pub *ppi) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + pi->phy_init_por = true; +} + +void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + pi->edcrs_threshold_lock = lock; + + write_phy_reg(pi, 0x22c, 0x46b); + write_phy_reg(pi, 0x22d, 0x46b); + write_phy_reg(pi, 0x22e, 0x3c0); + write_phy_reg(pi, 0x22f, 0x3c0); +} + +void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + pi->do_initcal = initcal; +} + +void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *pih, bool newstate) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + if (!pi || !pi->sh) + return; + + pi->sh->clk = newstate; +} + +void wlc_phy_hw_state_upd(struct brcms_phy_pub *pih, bool newstate) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + if (!pi || !pi->sh) + return; + + pi->sh->up = newstate; +} + +void wlc_phy_init(struct brcms_phy_pub *pih, u16 chanspec) +{ + u32 mc; + void (*phy_init)(struct brcms_phy *) = NULL; + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + if (pi->init_in_progress) + return; + + pi->init_in_progress = true; + + pi->radio_chanspec = chanspec; + + mc = bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); + if (WARN(mc & MCTL_EN_MAC, "HW error MAC running on init")) + return; + + if (!(pi->measure_hold & PHY_HOLD_FOR_SCAN)) + pi->measure_hold |= PHY_HOLD_FOR_NOT_ASSOC; + + if (WARN(!(bcma_aread32(pi->d11core, BCMA_IOST) & SISF_FCLKA), + "HW error SISF_FCLKA\n")) + return; + + phy_init = pi->pi_fptr.init; + + if (phy_init == NULL) + return; + + wlc_phy_anacore(pih, ON); + + if (CHSPEC_BW(pi->radio_chanspec) != pi->bw) + wlapi_bmac_bw_set(pi->sh->physhim, + CHSPEC_BW(pi->radio_chanspec)); + + pi->nphy_gain_boost = true; + + wlc_phy_switch_radio((struct brcms_phy_pub *) pi, ON); + + (*phy_init)(pi); + + pi->phy_init_por = false; + + if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) + wlc_phy_do_dummy_tx(pi, true, OFF); + + if (!(ISNPHY(pi))) + wlc_phy_txpower_update_shm(pi); + + wlc_phy_ant_rxdiv_set((struct brcms_phy_pub *) pi, pi->sh->rx_antdiv); + + pi->init_in_progress = false; +} + +void wlc_phy_cal_init(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + void (*cal_init)(struct brcms_phy *) = NULL; + + if (WARN((bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & + MCTL_EN_MAC) != 0, "HW error: MAC enabled during phy cal\n")) + return; + + if (!pi->initialized) { + cal_init = pi->pi_fptr.calinit; + if (cal_init) + (*cal_init)(pi); + + pi->initialized = true; + } +} + +int wlc_phy_down(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + int callbacks = 0; + + if (pi->phycal_timer + && !wlapi_del_timer(pi->phycal_timer)) + callbacks++; + + pi->nphy_iqcal_chanspec_2G = 0; + pi->nphy_iqcal_chanspec_5G = 0; + + return callbacks; +} + +void +wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, uint tbl_offset, + u16 tblAddr, u16 tblDataHi, u16 tblDataLo) +{ + write_phy_reg(pi, tblAddr, (tbl_id << 10) | tbl_offset); + + pi->tbl_data_hi = tblDataHi; + pi->tbl_data_lo = tblDataLo; + + if (pi->sh->chip == BCMA_CHIP_ID_BCM43224 && + pi->sh->chiprev == 1) { + pi->tbl_addr = tblAddr; + pi->tbl_save_id = tbl_id; + pi->tbl_save_offset = tbl_offset; + } +} + +void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val) +{ + if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) && + (pi->sh->chiprev == 1) && + (pi->tbl_save_id == NPHY_TBL_ID_ANTSWCTRLLUT)) { + read_phy_reg(pi, pi->tbl_data_lo); + + write_phy_reg(pi, pi->tbl_addr, + (pi->tbl_save_id << 10) | pi->tbl_save_offset); + pi->tbl_save_offset++; + } + + if (width == 32) { + write_phy_reg(pi, pi->tbl_data_hi, (u16) (val >> 16)); + write_phy_reg(pi, pi->tbl_data_lo, (u16) val); + } else { + write_phy_reg(pi, pi->tbl_data_lo, (u16) val); + } +} + +void +wlc_phy_write_table(struct brcms_phy *pi, const struct phytbl_info *ptbl_info, + u16 tblAddr, u16 tblDataHi, u16 tblDataLo) +{ + uint idx; + uint tbl_id = ptbl_info->tbl_id; + uint tbl_offset = ptbl_info->tbl_offset; + uint tbl_width = ptbl_info->tbl_width; + const u8 *ptbl_8b = (const u8 *)ptbl_info->tbl_ptr; + const u16 *ptbl_16b = (const u16 *)ptbl_info->tbl_ptr; + const u32 *ptbl_32b = (const u32 *)ptbl_info->tbl_ptr; + + write_phy_reg(pi, tblAddr, (tbl_id << 10) | tbl_offset); + + for (idx = 0; idx < ptbl_info->tbl_len; idx++) { + + if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) && + (pi->sh->chiprev == 1) && + (tbl_id == NPHY_TBL_ID_ANTSWCTRLLUT)) { + read_phy_reg(pi, tblDataLo); + + write_phy_reg(pi, tblAddr, + (tbl_id << 10) | (tbl_offset + idx)); + } + + if (tbl_width == 32) { + write_phy_reg(pi, tblDataHi, + (u16) (ptbl_32b[idx] >> 16)); + write_phy_reg(pi, tblDataLo, (u16) ptbl_32b[idx]); + } else if (tbl_width == 16) { + write_phy_reg(pi, tblDataLo, ptbl_16b[idx]); + } else { + write_phy_reg(pi, tblDataLo, ptbl_8b[idx]); + } + } +} + +void +wlc_phy_read_table(struct brcms_phy *pi, const struct phytbl_info *ptbl_info, + u16 tblAddr, u16 tblDataHi, u16 tblDataLo) +{ + uint idx; + uint tbl_id = ptbl_info->tbl_id; + uint tbl_offset = ptbl_info->tbl_offset; + uint tbl_width = ptbl_info->tbl_width; + u8 *ptbl_8b = (u8 *)ptbl_info->tbl_ptr; + u16 *ptbl_16b = (u16 *)ptbl_info->tbl_ptr; + u32 *ptbl_32b = (u32 *)ptbl_info->tbl_ptr; + + write_phy_reg(pi, tblAddr, (tbl_id << 10) | tbl_offset); + + for (idx = 0; idx < ptbl_info->tbl_len; idx++) { + + if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) && + (pi->sh->chiprev == 1)) { + (void)read_phy_reg(pi, tblDataLo); + + write_phy_reg(pi, tblAddr, + (tbl_id << 10) | (tbl_offset + idx)); + } + + if (tbl_width == 32) { + ptbl_32b[idx] = read_phy_reg(pi, tblDataLo); + ptbl_32b[idx] |= (read_phy_reg(pi, tblDataHi) << 16); + } else if (tbl_width == 16) { + ptbl_16b[idx] = read_phy_reg(pi, tblDataLo); + } else { + ptbl_8b[idx] = (u8) read_phy_reg(pi, tblDataLo); + } + } +} + +uint +wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi, + struct radio_20xx_regs *radioregs) +{ + uint i = 0; + + do { + if (radioregs[i].do_init) + write_radio_reg(pi, radioregs[i].address, + (u16) radioregs[i].init); + + i++; + } while (radioregs[i].address != 0xffff); + + return i; +} + +uint +wlc_phy_init_radio_regs(struct brcms_phy *pi, + const struct radio_regs *radioregs, + u16 core_offset) +{ + uint i = 0; + uint count = 0; + + do { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + if (radioregs[i].do_init_a) { + write_radio_reg(pi, + radioregs[i]. + address | core_offset, + (u16) radioregs[i].init_a); + if (ISNPHY(pi) && (++count % 4 == 0)) + BRCMS_PHY_WAR_PR51571(pi); + } + } else { + if (radioregs[i].do_init_g) { + write_radio_reg(pi, + radioregs[i]. + address | core_offset, + (u16) radioregs[i].init_g); + if (ISNPHY(pi) && (++count % 4 == 0)) + BRCMS_PHY_WAR_PR51571(pi); + } + } + + i++; + } while (radioregs[i].address != 0xffff); + + return i; +} + +void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on) +{ +#define DUMMY_PKT_LEN 20 + struct bcma_device *core = pi->d11core; + int i, count; + u8 ofdmpkt[DUMMY_PKT_LEN] = { + 0xcc, 0x01, 0x02, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + u8 cckpkt[DUMMY_PKT_LEN] = { + 0x6e, 0x84, 0x0b, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + u32 *dummypkt; + + dummypkt = (u32 *) (ofdm ? ofdmpkt : cckpkt); + wlapi_bmac_write_template_ram(pi->sh->physhim, 0, DUMMY_PKT_LEN, + dummypkt); + + bcma_write16(core, D11REGOFFS(xmtsel), 0); + + if (D11REV_GE(pi->sh->corerev, 11)) + bcma_write16(core, D11REGOFFS(wepctl), 0x100); + else + bcma_write16(core, D11REGOFFS(wepctl), 0); + + bcma_write16(core, D11REGOFFS(txe_phyctl), + (ofdm ? 1 : 0) | PHY_TXC_ANT_0); + if (ISNPHY(pi) || ISLCNPHY(pi)) + bcma_write16(core, D11REGOFFS(txe_phyctl1), 0x1A02); + + bcma_write16(core, D11REGOFFS(txe_wm_0), 0); + bcma_write16(core, D11REGOFFS(txe_wm_1), 0); + + bcma_write16(core, D11REGOFFS(xmttplatetxptr), 0); + bcma_write16(core, D11REGOFFS(xmttxcnt), DUMMY_PKT_LEN); + + bcma_write16(core, D11REGOFFS(xmtsel), + ((8 << 8) | (1 << 5) | (1 << 2) | 2)); + + bcma_write16(core, D11REGOFFS(txe_ctl), 0); + + if (!pa_on) { + if (ISNPHY(pi)) + wlc_phy_pa_override_nphy(pi, OFF); + } + + if (ISNPHY(pi) || ISLCNPHY(pi)) + bcma_write16(core, D11REGOFFS(txe_aux), 0xD0); + else + bcma_write16(core, D11REGOFFS(txe_aux), ((1 << 5) | (1 << 4))); + + (void)bcma_read16(core, D11REGOFFS(txe_aux)); + + i = 0; + count = ofdm ? 30 : 250; + while ((i++ < count) + && (bcma_read16(core, D11REGOFFS(txe_status)) & (1 << 7))) + udelay(10); + + i = 0; + + while ((i++ < 10) && + ((bcma_read16(core, D11REGOFFS(txe_status)) & (1 << 10)) == 0)) + udelay(10); + + i = 0; + + while ((i++ < 10) && + ((bcma_read16(core, D11REGOFFS(ifsstat)) & (1 << 8)))) + udelay(10); + + if (!pa_on) { + if (ISNPHY(pi)) + wlc_phy_pa_override_nphy(pi, ON); + } +} + +void wlc_phy_hold_upd(struct brcms_phy_pub *pih, u32 id, bool set) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + if (set) + mboolset(pi->measure_hold, id); + else + mboolclr(pi->measure_hold, id); + + return; +} + +void wlc_phy_mute_upd(struct brcms_phy_pub *pih, bool mute, u32 flags) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + if (mute) + mboolset(pi->measure_hold, PHY_HOLD_FOR_MUTE); + else + mboolclr(pi->measure_hold, PHY_HOLD_FOR_MUTE); + + if (!mute && (flags & PHY_MUTE_FOR_PREISM)) + pi->nphy_perical_last = pi->sh->now - pi->sh->glacial_timer; + return; +} + +void wlc_phy_clear_tssi(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + if (ISNPHY(pi)) { + return; + } else { + wlapi_bmac_write_shm(pi->sh->physhim, M_B_TSSI_0, NULL_TSSI_W); + wlapi_bmac_write_shm(pi->sh->physhim, M_B_TSSI_1, NULL_TSSI_W); + wlapi_bmac_write_shm(pi->sh->physhim, M_G_TSSI_0, NULL_TSSI_W); + wlapi_bmac_write_shm(pi->sh->physhim, M_G_TSSI_1, NULL_TSSI_W); + } +} + +static bool wlc_phy_cal_txpower_recalc_sw(struct brcms_phy *pi) +{ + return false; +} + +void wlc_phy_switch_radio(struct brcms_phy_pub *pih, bool on) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); + + if (ISNPHY(pi)) { + wlc_phy_switch_radio_nphy(pi, on); + } else if (ISLCNPHY(pi)) { + if (on) { + and_phy_reg(pi, 0x44c, + ~((0x1 << 8) | + (0x1 << 9) | + (0x1 << 10) | (0x1 << 11) | (0x1 << 12))); + and_phy_reg(pi, 0x4b0, ~((0x1 << 3) | (0x1 << 11))); + and_phy_reg(pi, 0x4f9, ~(0x1 << 3)); + } else { + and_phy_reg(pi, 0x44d, + ~((0x1 << 10) | + (0x1 << 11) | + (0x1 << 12) | (0x1 << 13) | (0x1 << 14))); + or_phy_reg(pi, 0x44c, + (0x1 << 8) | + (0x1 << 9) | + (0x1 << 10) | (0x1 << 11) | (0x1 << 12)); + + and_phy_reg(pi, 0x4b7, ~((0x7f << 8))); + and_phy_reg(pi, 0x4b1, ~((0x1 << 13))); + or_phy_reg(pi, 0x4b0, (0x1 << 3) | (0x1 << 11)); + and_phy_reg(pi, 0x4fa, ~((0x1 << 3))); + or_phy_reg(pi, 0x4f9, (0x1 << 3)); + } + } +} + +u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + return pi->bw; +} + +void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + pi->bw = bw; +} + +void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, u16 newch) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + pi->radio_chanspec = newch; + +} + +u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + return pi->radio_chanspec; +} + +void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, u16 chanspec) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + u16 m_cur_channel; + void (*chanspec_set)(struct brcms_phy *, u16) = NULL; + m_cur_channel = CHSPEC_CHANNEL(chanspec); + if (CHSPEC_IS5G(chanspec)) + m_cur_channel |= D11_CURCHANNEL_5G; + if (CHSPEC_IS40(chanspec)) + m_cur_channel |= D11_CURCHANNEL_40; + wlapi_bmac_write_shm(pi->sh->physhim, M_CURCHANNEL, m_cur_channel); + + chanspec_set = pi->pi_fptr.chanset; + if (chanspec_set) + (*chanspec_set)(pi, chanspec); + +} + +int wlc_phy_chanspec_freq2bandrange_lpssn(uint freq) +{ + int range = -1; + + if (freq < 2500) + range = WL_CHAN_FREQ_RANGE_2G; + else if (freq <= 5320) + range = WL_CHAN_FREQ_RANGE_5GL; + else if (freq <= 5700) + range = WL_CHAN_FREQ_RANGE_5GM; + else + range = WL_CHAN_FREQ_RANGE_5GH; + + return range; +} + +int wlc_phy_chanspec_bandrange_get(struct brcms_phy *pi, u16 chanspec) +{ + int range = -1; + uint channel = CHSPEC_CHANNEL(chanspec); + uint freq = wlc_phy_channel2freq(channel); + + if (ISNPHY(pi)) + range = wlc_phy_get_chan_freq_range_nphy(pi, channel); + else if (ISLCNPHY(pi)) + range = wlc_phy_chanspec_freq2bandrange_lpssn(freq); + + return range; +} + +void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi, + bool wide_filter) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + pi->channel_14_wide_filter = wide_filter; + +} + +int wlc_phy_channel2freq(uint channel) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) + if (chan_info_all[i].chan == channel) + return chan_info_all[i].freq; + return 0; +} + +void +wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band, + struct brcms_chanvec *channels) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + uint i; + uint channel; + + memset(channels, 0, sizeof(struct brcms_chanvec)); + + for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) { + channel = chan_info_all[i].chan; + + if ((pi->a_band_high_disable) && (channel >= FIRST_REF5_CHANNUM) + && (channel <= LAST_REF5_CHANNUM)) + continue; + + if ((band == BRCM_BAND_2G && channel <= CH_MAX_2G_CHANNEL) || + (band == BRCM_BAND_5G && channel > CH_MAX_2G_CHANNEL)) + setbit(channels->vec, channel); + } +} + +u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, uint band) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + uint i; + uint channel; + u16 chspec; + + for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) { + channel = chan_info_all[i].chan; + + if (ISNPHY(pi) && pi->bw == WL_CHANSPEC_BW_40) { + uint j; + + for (j = 0; j < ARRAY_SIZE(chan_info_all); j++) { + if (chan_info_all[j].chan == + channel + CH_10MHZ_APART) + break; + } + + if (j == ARRAY_SIZE(chan_info_all)) + continue; + + channel = upper_20_sb(channel); + chspec = channel | WL_CHANSPEC_BW_40 | + WL_CHANSPEC_CTL_SB_LOWER; + if (band == BRCM_BAND_2G) + chspec |= WL_CHANSPEC_BAND_2G; + else + chspec |= WL_CHANSPEC_BAND_5G; + } else + chspec = ch20mhz_chspec(channel); + + if ((pi->a_band_high_disable) && (channel >= FIRST_REF5_CHANNUM) + && (channel <= LAST_REF5_CHANNUM)) + continue; + + if ((band == BRCM_BAND_2G && channel <= CH_MAX_2G_CHANNEL) || + (band == BRCM_BAND_5G && channel > CH_MAX_2G_CHANNEL)) + return chspec; + } + + return (u16) INVCHANSPEC; +} + +int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, bool *override) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + *qdbm = pi->tx_user_target[0]; + if (override != NULL) + *override = pi->txpwroverride; + return 0; +} + +void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi, + struct txpwr_limits *txpwr) +{ + bool mac_enabled = false; + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + memcpy(&pi->tx_user_target[TXP_FIRST_CCK], + &txpwr->cck[0], BRCMS_NUM_RATES_CCK); + + memcpy(&pi->tx_user_target[TXP_FIRST_OFDM], + &txpwr->ofdm[0], BRCMS_NUM_RATES_OFDM); + memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_20_CDD], + &txpwr->ofdm_cdd[0], BRCMS_NUM_RATES_OFDM); + + memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_40_SISO], + &txpwr->ofdm_40_siso[0], BRCMS_NUM_RATES_OFDM); + memcpy(&pi->tx_user_target[TXP_FIRST_OFDM_40_CDD], + &txpwr->ofdm_40_cdd[0], BRCMS_NUM_RATES_OFDM); + + memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_SISO], + &txpwr->mcs_20_siso[0], BRCMS_NUM_RATES_MCS_1_STREAM); + memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_CDD], + &txpwr->mcs_20_cdd[0], BRCMS_NUM_RATES_MCS_1_STREAM); + memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_STBC], + &txpwr->mcs_20_stbc[0], BRCMS_NUM_RATES_MCS_1_STREAM); + memcpy(&pi->tx_user_target[TXP_FIRST_MCS_20_SDM], + &txpwr->mcs_20_mimo[0], BRCMS_NUM_RATES_MCS_2_STREAM); + + memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_SISO], + &txpwr->mcs_40_siso[0], BRCMS_NUM_RATES_MCS_1_STREAM); + memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_CDD], + &txpwr->mcs_40_cdd[0], BRCMS_NUM_RATES_MCS_1_STREAM); + memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_STBC], + &txpwr->mcs_40_stbc[0], BRCMS_NUM_RATES_MCS_1_STREAM); + memcpy(&pi->tx_user_target[TXP_FIRST_MCS_40_SDM], + &txpwr->mcs_40_mimo[0], BRCMS_NUM_RATES_MCS_2_STREAM); + + if (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & MCTL_EN_MAC) + mac_enabled = true; + + if (mac_enabled) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + + wlc_phy_txpower_recalc_target(pi); + wlc_phy_cal_txpower_recalc_sw(pi); + + if (mac_enabled) + wlapi_enable_mac(pi->sh->physhim); +} + +int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, bool override) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + int i; + + if (qdbm > 127) + return -EINVAL; + + for (i = 0; i < TXP_NUM_RATES; i++) + pi->tx_user_target[i] = (u8) qdbm; + + pi->txpwroverride = false; + + if (pi->sh->up) { + if (!SCAN_INPROG_PHY(pi)) { + bool suspend; + + suspend = (0 == (bcma_read32(pi->d11core, + D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + + if (!suspend) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + + wlc_phy_txpower_recalc_target(pi); + wlc_phy_cal_txpower_recalc_sw(pi); + + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); + } + } + return 0; +} + +void +wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint channel, u8 *min_pwr, + u8 *max_pwr, int txp_rate_idx) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + uint i; + + *min_pwr = pi->min_txpower * BRCMS_TXPWR_DB_FACTOR; + + if (ISNPHY(pi)) { + if (txp_rate_idx < 0) + txp_rate_idx = TXP_FIRST_CCK; + wlc_phy_txpower_sromlimit_get_nphy(pi, channel, max_pwr, + (u8) txp_rate_idx); + + } else if ((channel <= CH_MAX_2G_CHANNEL)) { + if (txp_rate_idx < 0) + txp_rate_idx = TXP_FIRST_CCK; + *max_pwr = pi->tx_srom_max_rate_2g[txp_rate_idx]; + } else { + + *max_pwr = BRCMS_TXPWR_MAX; + + if (txp_rate_idx < 0) + txp_rate_idx = TXP_FIRST_OFDM; + + for (i = 0; i < ARRAY_SIZE(chan_info_all); i++) { + if (channel == chan_info_all[i].chan) + break; + } + + if (pi->hwtxpwr) { + *max_pwr = pi->hwtxpwr[i]; + } else { + + if ((i >= FIRST_MID_5G_CHAN) && (i <= LAST_MID_5G_CHAN)) + *max_pwr = + pi->tx_srom_max_rate_5g_mid[txp_rate_idx]; + if ((i >= FIRST_HIGH_5G_CHAN) + && (i <= LAST_HIGH_5G_CHAN)) + *max_pwr = + pi->tx_srom_max_rate_5g_hi[txp_rate_idx]; + if ((i >= FIRST_LOW_5G_CHAN) && (i <= LAST_LOW_5G_CHAN)) + *max_pwr = + pi->tx_srom_max_rate_5g_low[txp_rate_idx]; + } + } +} + +void +wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, uint chan, + u8 *max_txpwr, u8 *min_txpwr) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + u8 tx_pwr_max = 0; + u8 tx_pwr_min = 255; + u8 max_num_rate; + u8 maxtxpwr, mintxpwr, rate, pactrl; + + pactrl = 0; + + max_num_rate = ISNPHY(pi) ? TXP_NUM_RATES : + ISLCNPHY(pi) ? (TXP_LAST_SISO_MCS_20 + + 1) : (TXP_LAST_OFDM + 1); + + for (rate = 0; rate < max_num_rate; rate++) { + + wlc_phy_txpower_sromlimit(ppi, chan, &mintxpwr, &maxtxpwr, + rate); + + maxtxpwr = (maxtxpwr > pactrl) ? (maxtxpwr - pactrl) : 0; + + maxtxpwr = (maxtxpwr > 6) ? (maxtxpwr - 6) : 0; + + tx_pwr_max = max(tx_pwr_max, maxtxpwr); + tx_pwr_min = min(tx_pwr_min, maxtxpwr); + } + *max_txpwr = tx_pwr_max; + *min_txpwr = tx_pwr_min; +} + +void +wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, uint bandunit, + s32 *max_pwr, s32 *min_pwr, u32 *step_pwr) +{ + return; +} + +u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + return pi->tx_power_min; +} + +u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + return pi->tx_power_max; +} + +static s8 wlc_phy_env_measure_vbat(struct brcms_phy *pi) +{ + if (ISLCNPHY(pi)) + return wlc_lcnphy_vbatsense(pi, 0); + else + return 0; +} + +static s8 wlc_phy_env_measure_temperature(struct brcms_phy *pi) +{ + if (ISLCNPHY(pi)) + return wlc_lcnphy_tempsense_degree(pi, 0); + else + return 0; +} + +static void wlc_phy_upd_env_txpwr_rate_limits(struct brcms_phy *pi, u32 band) +{ + u8 i; + s8 temp, vbat; + + for (i = 0; i < TXP_NUM_RATES; i++) + pi->txpwr_env_limit[i] = BRCMS_TXPWR_MAX; + + vbat = wlc_phy_env_measure_vbat(pi); + temp = wlc_phy_env_measure_temperature(pi); + +} + +static s8 +wlc_user_txpwr_antport_to_rfport(struct brcms_phy *pi, uint chan, u32 band, + u8 rate) +{ + return 0; +} + +void wlc_phy_txpower_recalc_target(struct brcms_phy *pi) +{ + u8 maxtxpwr, mintxpwr, rate, pactrl; + uint target_chan; + u8 tx_pwr_target[TXP_NUM_RATES]; + u8 tx_pwr_max = 0; + u8 tx_pwr_min = 255; + u8 tx_pwr_max_rate_ind = 0; + u8 max_num_rate; + u8 start_rate = 0; + u16 chspec; + u32 band = CHSPEC2BAND(pi->radio_chanspec); + void (*txpwr_recalc_fn)(struct brcms_phy *) = NULL; + + chspec = pi->radio_chanspec; + if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) + target_chan = CHSPEC_CHANNEL(chspec); + else if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) + target_chan = upper_20_sb(CHSPEC_CHANNEL(chspec)); + else + target_chan = lower_20_sb(CHSPEC_CHANNEL(chspec)); + + pactrl = 0; + if (ISLCNPHY(pi)) { + u32 offset_mcs, i; + + if (CHSPEC_IS40(pi->radio_chanspec)) { + offset_mcs = pi->mcs40_po; + for (i = TXP_FIRST_SISO_MCS_20; + i <= TXP_LAST_SISO_MCS_20; i++) { + pi->tx_srom_max_rate_2g[i - 8] = + pi->tx_srom_max_2g - + ((offset_mcs & 0xf) * 2); + offset_mcs >>= 4; + } + } else { + offset_mcs = pi->mcs20_po; + for (i = TXP_FIRST_SISO_MCS_20; + i <= TXP_LAST_SISO_MCS_20; i++) { + pi->tx_srom_max_rate_2g[i - 8] = + pi->tx_srom_max_2g - + ((offset_mcs & 0xf) * 2); + offset_mcs >>= 4; + } + } + } + + max_num_rate = ((ISNPHY(pi)) ? (TXP_NUM_RATES) : + ((ISLCNPHY(pi)) ? + (TXP_LAST_SISO_MCS_20 + 1) : (TXP_LAST_OFDM + 1))); + + wlc_phy_upd_env_txpwr_rate_limits(pi, band); + + for (rate = start_rate; rate < max_num_rate; rate++) { + + tx_pwr_target[rate] = pi->tx_user_target[rate]; + + if (pi->user_txpwr_at_rfport) + tx_pwr_target[rate] += + wlc_user_txpwr_antport_to_rfport(pi, + target_chan, + band, + rate); + + wlc_phy_txpower_sromlimit((struct brcms_phy_pub *) pi, + target_chan, + &mintxpwr, &maxtxpwr, rate); + + maxtxpwr = min(maxtxpwr, pi->txpwr_limit[rate]); + + maxtxpwr = (maxtxpwr > pactrl) ? (maxtxpwr - pactrl) : 0; + + maxtxpwr = (maxtxpwr > 6) ? (maxtxpwr - 6) : 0; + + maxtxpwr = min(maxtxpwr, tx_pwr_target[rate]); + + if (pi->txpwr_percent <= 100) + maxtxpwr = (maxtxpwr * pi->txpwr_percent) / 100; + + tx_pwr_target[rate] = max(maxtxpwr, mintxpwr); + + tx_pwr_target[rate] = + min(tx_pwr_target[rate], pi->txpwr_env_limit[rate]); + + if (tx_pwr_target[rate] > tx_pwr_max) + tx_pwr_max_rate_ind = rate; + + tx_pwr_max = max(tx_pwr_max, tx_pwr_target[rate]); + tx_pwr_min = min(tx_pwr_min, tx_pwr_target[rate]); + } + + memset(pi->tx_power_offset, 0, sizeof(pi->tx_power_offset)); + pi->tx_power_max = tx_pwr_max; + pi->tx_power_min = tx_pwr_min; + pi->tx_power_max_rate_ind = tx_pwr_max_rate_ind; + for (rate = 0; rate < max_num_rate; rate++) { + + pi->tx_power_target[rate] = tx_pwr_target[rate]; + + if (!pi->hwpwrctrl || ISNPHY(pi)) + pi->tx_power_offset[rate] = + pi->tx_power_max - pi->tx_power_target[rate]; + else + pi->tx_power_offset[rate] = + pi->tx_power_target[rate] - pi->tx_power_min; + } + + txpwr_recalc_fn = pi->pi_fptr.txpwrrecalc; + if (txpwr_recalc_fn) + (*txpwr_recalc_fn)(pi); +} + +static void +wlc_phy_txpower_reg_limit_calc(struct brcms_phy *pi, struct txpwr_limits *txpwr, + u16 chanspec) +{ + u8 tmp_txpwr_limit[2 * BRCMS_NUM_RATES_OFDM]; + u8 *txpwr_ptr1 = NULL, *txpwr_ptr2 = NULL; + int rate_start_index = 0, rate1, rate2, k; + + for (rate1 = WL_TX_POWER_CCK_FIRST, rate2 = 0; + rate2 < WL_TX_POWER_CCK_NUM; rate1++, rate2++) + pi->txpwr_limit[rate1] = txpwr->cck[rate2]; + + for (rate1 = WL_TX_POWER_OFDM_FIRST, rate2 = 0; + rate2 < WL_TX_POWER_OFDM_NUM; rate1++, rate2++) + pi->txpwr_limit[rate1] = txpwr->ofdm[rate2]; + + if (ISNPHY(pi)) { + + for (k = 0; k < 4; k++) { + switch (k) { + case 0: + + txpwr_ptr1 = txpwr->mcs_20_siso; + txpwr_ptr2 = txpwr->ofdm; + rate_start_index = WL_TX_POWER_OFDM_FIRST; + break; + case 1: + + txpwr_ptr1 = txpwr->mcs_20_cdd; + txpwr_ptr2 = txpwr->ofdm_cdd; + rate_start_index = WL_TX_POWER_OFDM20_CDD_FIRST; + break; + case 2: + + txpwr_ptr1 = txpwr->mcs_40_siso; + txpwr_ptr2 = txpwr->ofdm_40_siso; + rate_start_index = + WL_TX_POWER_OFDM40_SISO_FIRST; + break; + case 3: + + txpwr_ptr1 = txpwr->mcs_40_cdd; + txpwr_ptr2 = txpwr->ofdm_40_cdd; + rate_start_index = WL_TX_POWER_OFDM40_CDD_FIRST; + break; + } + + for (rate2 = 0; rate2 < BRCMS_NUM_RATES_OFDM; + rate2++) { + tmp_txpwr_limit[rate2] = 0; + tmp_txpwr_limit[BRCMS_NUM_RATES_OFDM + rate2] = + txpwr_ptr1[rate2]; + } + wlc_phy_mcs_to_ofdm_powers_nphy( + tmp_txpwr_limit, 0, + BRCMS_NUM_RATES_OFDM - + 1, BRCMS_NUM_RATES_OFDM); + for (rate1 = rate_start_index, rate2 = 0; + rate2 < BRCMS_NUM_RATES_OFDM; rate1++, rate2++) + pi->txpwr_limit[rate1] = + min(txpwr_ptr2[rate2], + tmp_txpwr_limit[rate2]); + } + + for (k = 0; k < 4; k++) { + switch (k) { + case 0: + + txpwr_ptr1 = txpwr->ofdm; + txpwr_ptr2 = txpwr->mcs_20_siso; + rate_start_index = WL_TX_POWER_MCS20_SISO_FIRST; + break; + case 1: + + txpwr_ptr1 = txpwr->ofdm_cdd; + txpwr_ptr2 = txpwr->mcs_20_cdd; + rate_start_index = WL_TX_POWER_MCS20_CDD_FIRST; + break; + case 2: + + txpwr_ptr1 = txpwr->ofdm_40_siso; + txpwr_ptr2 = txpwr->mcs_40_siso; + rate_start_index = WL_TX_POWER_MCS40_SISO_FIRST; + break; + case 3: + + txpwr_ptr1 = txpwr->ofdm_40_cdd; + txpwr_ptr2 = txpwr->mcs_40_cdd; + rate_start_index = WL_TX_POWER_MCS40_CDD_FIRST; + break; + } + for (rate2 = 0; rate2 < BRCMS_NUM_RATES_OFDM; + rate2++) { + tmp_txpwr_limit[rate2] = 0; + tmp_txpwr_limit[BRCMS_NUM_RATES_OFDM + rate2] = + txpwr_ptr1[rate2]; + } + wlc_phy_ofdm_to_mcs_powers_nphy( + tmp_txpwr_limit, 0, + BRCMS_NUM_RATES_OFDM - + 1, BRCMS_NUM_RATES_OFDM); + for (rate1 = rate_start_index, rate2 = 0; + rate2 < BRCMS_NUM_RATES_MCS_1_STREAM; + rate1++, rate2++) + pi->txpwr_limit[rate1] = + min(txpwr_ptr2[rate2], + tmp_txpwr_limit[rate2]); + } + + for (k = 0; k < 2; k++) { + switch (k) { + case 0: + + rate_start_index = WL_TX_POWER_MCS20_STBC_FIRST; + txpwr_ptr1 = txpwr->mcs_20_stbc; + break; + case 1: + + rate_start_index = WL_TX_POWER_MCS40_STBC_FIRST; + txpwr_ptr1 = txpwr->mcs_40_stbc; + break; + } + for (rate1 = rate_start_index, rate2 = 0; + rate2 < BRCMS_NUM_RATES_MCS_1_STREAM; + rate1++, rate2++) + pi->txpwr_limit[rate1] = txpwr_ptr1[rate2]; + } + + for (k = 0; k < 2; k++) { + switch (k) { + case 0: + + rate_start_index = WL_TX_POWER_MCS20_SDM_FIRST; + txpwr_ptr1 = txpwr->mcs_20_mimo; + break; + case 1: + + rate_start_index = WL_TX_POWER_MCS40_SDM_FIRST; + txpwr_ptr1 = txpwr->mcs_40_mimo; + break; + } + for (rate1 = rate_start_index, rate2 = 0; + rate2 < BRCMS_NUM_RATES_MCS_2_STREAM; + rate1++, rate2++) + pi->txpwr_limit[rate1] = txpwr_ptr1[rate2]; + } + + pi->txpwr_limit[WL_TX_POWER_MCS_32] = txpwr->mcs32; + + pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST] = + min(pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST], + pi->txpwr_limit[WL_TX_POWER_MCS_32]); + pi->txpwr_limit[WL_TX_POWER_MCS_32] = + pi->txpwr_limit[WL_TX_POWER_MCS40_CDD_FIRST]; + } +} + +void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, u8 txpwr_percent) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + pi->txpwr_percent = txpwr_percent; +} + +void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + pi->sh->machwcap = machwcap; +} + +void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + u16 rxc; + rxc = 0; + + if (start_end == ON) { + if (!ISNPHY(pi)) + return; + + if (NREV_IS(pi->pubpi.phy_rev, 3) + || NREV_IS(pi->pubpi.phy_rev, 4)) { + bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), + 0xa0); + bcma_set16(pi->d11core, D11REGOFFS(phyregdata), + 0x1 << 15); + } + } else { + if (NREV_IS(pi->pubpi.phy_rev, 3) + || NREV_IS(pi->pubpi.phy_rev, 4)) { + bcma_wflush16(pi->d11core, D11REGOFFS(phyregaddr), + 0xa0); + bcma_write16(pi->d11core, D11REGOFFS(phyregdata), rxc); + } + + wlc_phy_por_inform(ppi); + } +} + +void +wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, struct txpwr_limits *txpwr, + u16 chanspec) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + wlc_phy_txpower_reg_limit_calc(pi, txpwr, chanspec); + + if (ISLCNPHY(pi)) { + int i, j; + for (i = TXP_FIRST_OFDM_20_CDD, j = 0; + j < BRCMS_NUM_RATES_MCS_1_STREAM; i++, j++) { + if (txpwr->mcs_20_siso[j]) + pi->txpwr_limit[i] = txpwr->mcs_20_siso[j]; + else + pi->txpwr_limit[i] = txpwr->ofdm[j]; + } + } + + wlapi_suspend_mac_and_wait(pi->sh->physhim); + + wlc_phy_txpower_recalc_target(pi); + wlc_phy_cal_txpower_recalc_sw(pi); + wlapi_enable_mac(pi->sh->physhim); +} + +void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + pi->ofdm_rateset_war = war; +} + +void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, bool bf_preempt) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + pi->bf_preempt_4306 = bf_preempt; +} + +void wlc_phy_txpower_update_shm(struct brcms_phy *pi) +{ + int j; + if (ISNPHY(pi)) + return; + + if (!pi->sh->clk) + return; + + if (pi->hwpwrctrl) { + u16 offset; + + wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_MAX, 63); + wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_N, + 1 << NUM_TSSI_FRAMES); + + wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_TARGET, + pi->tx_power_min << NUM_TSSI_FRAMES); + + wlapi_bmac_write_shm(pi->sh->physhim, M_TXPWR_CUR, + pi->hwpwr_txcur); + + for (j = TXP_FIRST_OFDM; j <= TXP_LAST_OFDM; j++) { + const u8 ucode_ofdm_rates[] = { + 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c + }; + offset = wlapi_bmac_rate_shm_offset( + pi->sh->physhim, + ucode_ofdm_rates[j - TXP_FIRST_OFDM]); + wlapi_bmac_write_shm(pi->sh->physhim, offset + 6, + pi->tx_power_offset[j]); + wlapi_bmac_write_shm(pi->sh->physhim, offset + 14, + -(pi->tx_power_offset[j] / 2)); + } + + wlapi_bmac_mhf(pi->sh->physhim, MHF2, MHF2_HWPWRCTL, + MHF2_HWPWRCTL, BRCM_BAND_ALL); + } else { + int i; + + for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++) + pi->tx_power_offset[i] = + (u8) roundup(pi->tx_power_offset[i], 8); + wlapi_bmac_write_shm(pi->sh->physhim, M_OFDM_OFFSET, + (u16) + ((pi->tx_power_offset[TXP_FIRST_OFDM] + + 7) >> 3)); + } +} + +bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + if (ISNPHY(pi)) + return pi->nphy_txpwrctrl; + else + return pi->hwpwrctrl; +} + +void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, bool hwpwrctrl) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + bool suspend; + + if (!pi->hwpwrctrl_capable) + return; + + pi->hwpwrctrl = hwpwrctrl; + pi->nphy_txpwrctrl = hwpwrctrl; + pi->txpwrctrl = hwpwrctrl; + + if (ISNPHY(pi)) { + suspend = (0 == (bcma_read32(pi->d11core, + D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!suspend) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + + wlc_phy_txpwrctrl_enable_nphy(pi, pi->nphy_txpwrctrl); + if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) + wlc_phy_txpwr_fixpower_nphy(pi); + else + mod_phy_reg(pi, 0x1e7, (0x7f << 0), + pi->saved_txpwr_idx); + + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); + } +} + +void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi) +{ + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + pi->ipa2g_on = (pi->srom_fem2g.extpagain == 2); + pi->ipa5g_on = (pi->srom_fem5g.extpagain == 2); + } else { + pi->ipa2g_on = false; + pi->ipa5g_on = false; + } +} + +static u32 wlc_phy_txpower_est_power_nphy(struct brcms_phy *pi) +{ + s16 tx0_status, tx1_status; + u16 estPower1, estPower2; + u8 pwr0, pwr1, adj_pwr0, adj_pwr1; + u32 est_pwr; + + estPower1 = read_phy_reg(pi, 0x118); + estPower2 = read_phy_reg(pi, 0x119); + + if ((estPower1 & (0x1 << 8)) == (0x1 << 8)) + pwr0 = (u8) (estPower1 & (0xff << 0)) >> 0; + else + pwr0 = 0x80; + + if ((estPower2 & (0x1 << 8)) == (0x1 << 8)) + pwr1 = (u8) (estPower2 & (0xff << 0)) >> 0; + else + pwr1 = 0x80; + + tx0_status = read_phy_reg(pi, 0x1ed); + tx1_status = read_phy_reg(pi, 0x1ee); + + if ((tx0_status & (0x1 << 15)) == (0x1 << 15)) + adj_pwr0 = (u8) (tx0_status & (0xff << 0)) >> 0; + else + adj_pwr0 = 0x80; + if ((tx1_status & (0x1 << 15)) == (0x1 << 15)) + adj_pwr1 = (u8) (tx1_status & (0xff << 0)) >> 0; + else + adj_pwr1 = 0x80; + + est_pwr = (u32) ((pwr0 << 24) | (pwr1 << 16) | (adj_pwr0 << 8) | + adj_pwr1); + + return est_pwr; +} + +void +wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi, struct tx_power *power, + uint channel) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + uint rate, num_rates; + u8 min_pwr, max_pwr; + +#if WL_TX_POWER_RATES != TXP_NUM_RATES +#error "struct tx_power out of sync with this fn" +#endif + + if (ISNPHY(pi)) { + power->rf_cores = 2; + power->flags |= (WL_TX_POWER_F_MIMO); + if (pi->nphy_txpwrctrl == PHY_TPC_HW_ON) + power->flags |= + (WL_TX_POWER_F_ENABLED | WL_TX_POWER_F_HW); + } else if (ISLCNPHY(pi)) { + power->rf_cores = 1; + power->flags |= (WL_TX_POWER_F_SISO); + if (pi->radiopwr_override == RADIOPWR_OVERRIDE_DEF) + power->flags |= WL_TX_POWER_F_ENABLED; + if (pi->hwpwrctrl) + power->flags |= WL_TX_POWER_F_HW; + } + + num_rates = ((ISNPHY(pi)) ? (TXP_NUM_RATES) : + ((ISLCNPHY(pi)) ? + (TXP_LAST_OFDM_20_CDD + 1) : (TXP_LAST_OFDM + 1))); + + for (rate = 0; rate < num_rates; rate++) { + power->user_limit[rate] = pi->tx_user_target[rate]; + wlc_phy_txpower_sromlimit(ppi, channel, &min_pwr, &max_pwr, + rate); + power->board_limit[rate] = (u8) max_pwr; + power->target[rate] = pi->tx_power_target[rate]; + } + + if (ISNPHY(pi)) { + u32 est_pout; + + wlapi_suspend_mac_and_wait(pi->sh->physhim); + wlc_phyreg_enter((struct brcms_phy_pub *) pi); + est_pout = wlc_phy_txpower_est_power_nphy(pi); + wlc_phyreg_exit((struct brcms_phy_pub *) pi); + wlapi_enable_mac(pi->sh->physhim); + + power->est_Pout[0] = (est_pout >> 8) & 0xff; + power->est_Pout[1] = est_pout & 0xff; + + power->est_Pout_act[0] = est_pout >> 24; + power->est_Pout_act[1] = (est_pout >> 16) & 0xff; + + if (power->est_Pout[0] == 0x80) + power->est_Pout[0] = 0; + if (power->est_Pout[1] == 0x80) + power->est_Pout[1] = 0; + + if (power->est_Pout_act[0] == 0x80) + power->est_Pout_act[0] = 0; + if (power->est_Pout_act[1] == 0x80) + power->est_Pout_act[1] = 0; + + power->est_Pout_cck = 0; + + power->tx_power_max[0] = pi->tx_power_max; + power->tx_power_max[1] = pi->tx_power_max; + + power->tx_power_max_rate_ind[0] = pi->tx_power_max_rate_ind; + power->tx_power_max_rate_ind[1] = pi->tx_power_max_rate_ind; + } else if (pi->hwpwrctrl && pi->sh->up) { + + wlc_phyreg_enter(ppi); + if (ISLCNPHY(pi)) { + + power->tx_power_max[0] = pi->tx_power_max; + power->tx_power_max[1] = pi->tx_power_max; + + power->tx_power_max_rate_ind[0] = + pi->tx_power_max_rate_ind; + power->tx_power_max_rate_ind[1] = + pi->tx_power_max_rate_ind; + + if (wlc_phy_tpc_isenabled_lcnphy(pi)) + power->flags |= + (WL_TX_POWER_F_HW | + WL_TX_POWER_F_ENABLED); + else + power->flags &= + ~(WL_TX_POWER_F_HW | + WL_TX_POWER_F_ENABLED); + + wlc_lcnphy_get_tssi(pi, (s8 *) &power->est_Pout[0], + (s8 *) &power->est_Pout_cck); + } + wlc_phyreg_exit(ppi); + } +} + +void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + pi->antsel_type = antsel_type; +} + +bool wlc_phy_test_ison(struct brcms_phy_pub *ppi) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + return pi->phytest_on; +} + +void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + bool suspend; + + pi->sh->rx_antdiv = val; + + if (!(ISNPHY(pi) && D11REV_IS(pi->sh->corerev, 16))) { + if (val > ANT_RX_DIV_FORCE_1) + wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_ANTDIV, + MHF1_ANTDIV, BRCM_BAND_ALL); + else + wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_ANTDIV, 0, + BRCM_BAND_ALL); + } + + if (ISNPHY(pi)) + return; + + if (!pi->sh->clk) + return; + + suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!suspend) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + + if (ISLCNPHY(pi)) { + if (val > ANT_RX_DIV_FORCE_1) { + mod_phy_reg(pi, 0x410, (0x1 << 1), 0x01 << 1); + mod_phy_reg(pi, 0x410, + (0x1 << 0), + ((ANT_RX_DIV_START_1 == val) ? 1 : 0) << 0); + } else { + mod_phy_reg(pi, 0x410, (0x1 << 1), 0x00 << 1); + mod_phy_reg(pi, 0x410, (0x1 << 0), (u16) val << 0); + } + } + + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); + + return; +} + +static bool +wlc_phy_noise_calc_phy(struct brcms_phy *pi, u32 *cmplx_pwr, s8 *pwr_ant) +{ + s8 cmplx_pwr_dbm[PHY_CORE_MAX]; + u8 i; + + memset((u8 *) cmplx_pwr_dbm, 0, sizeof(cmplx_pwr_dbm)); + wlc_phy_compute_dB(cmplx_pwr, cmplx_pwr_dbm, pi->pubpi.phy_corenum); + + for (i = 0; i < pi->pubpi.phy_corenum; i++) { + if (NREV_GE(pi->pubpi.phy_rev, 3)) + cmplx_pwr_dbm[i] += (s8) PHY_NOISE_OFFSETFACT_4322; + else + + cmplx_pwr_dbm[i] += (s8) (16 - (15) * 3 - 70); + } + + for (i = 0; i < pi->pubpi.phy_corenum; i++) { + pi->nphy_noise_win[i][pi->nphy_noise_index] = cmplx_pwr_dbm[i]; + pwr_ant[i] = cmplx_pwr_dbm[i]; + } + pi->nphy_noise_index = + MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ); + return true; +} + +static void wlc_phy_noise_cb(struct brcms_phy *pi, u8 channel, s8 noise_dbm) +{ + if (!pi->phynoise_state) + return; + + if (pi->phynoise_state & PHY_NOISE_STATE_MON) { + if (pi->phynoise_chan_watchdog == channel) { + pi->sh->phy_noise_window[pi->sh->phy_noise_index] = + noise_dbm; + pi->sh->phy_noise_index = + MODINC(pi->sh->phy_noise_index, MA_WINDOW_SZ); + } + pi->phynoise_state &= ~PHY_NOISE_STATE_MON; + } + + if (pi->phynoise_state & PHY_NOISE_STATE_EXTERNAL) + pi->phynoise_state &= ~PHY_NOISE_STATE_EXTERNAL; + +} + +static s8 wlc_phy_noise_read_shmem(struct brcms_phy *pi) +{ + u32 cmplx_pwr[PHY_CORE_MAX]; + s8 noise_dbm_ant[PHY_CORE_MAX]; + u16 lo, hi; + u32 cmplx_pwr_tot = 0; + s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; + u8 idx, core; + + memset((u8 *) cmplx_pwr, 0, sizeof(cmplx_pwr)); + memset((u8 *) noise_dbm_ant, 0, sizeof(noise_dbm_ant)); + + for (idx = 0, core = 0; core < pi->pubpi.phy_corenum; idx += 2, + core++) { + lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP(idx)); + hi = wlapi_bmac_read_shm(pi->sh->physhim, + M_PWRIND_MAP(idx + 1)); + cmplx_pwr[core] = (hi << 16) + lo; + cmplx_pwr_tot += cmplx_pwr[core]; + if (cmplx_pwr[core] == 0) + noise_dbm_ant[core] = PHY_NOISE_FIXED_VAL_NPHY; + else + cmplx_pwr[core] >>= PHY_NOISE_SAMPLE_LOG_NUM_UCODE; + } + + if (cmplx_pwr_tot != 0) + wlc_phy_noise_calc_phy(pi, cmplx_pwr, noise_dbm_ant); + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + pi->nphy_noise_win[core][pi->nphy_noise_index] = + noise_dbm_ant[core]; + + if (noise_dbm_ant[core] > noise_dbm) + noise_dbm = noise_dbm_ant[core]; + } + pi->nphy_noise_index = + MODINC_POW2(pi->nphy_noise_index, PHY_NOISE_WINDOW_SZ); + + return noise_dbm; + +} + +void wlc_phy_noise_sample_intr(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + u16 jssi_aux; + u8 channel = 0; + s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; + + if (ISLCNPHY(pi)) { + u32 cmplx_pwr, cmplx_pwr0, cmplx_pwr1; + u16 lo, hi; + s32 pwr_offset_dB, gain_dB; + u16 status_0, status_1; + + jssi_aux = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_AUX); + channel = jssi_aux & D11_CURCHANNEL_MAX; + + lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP0); + hi = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP1); + cmplx_pwr0 = (hi << 16) + lo; + + lo = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP2); + hi = wlapi_bmac_read_shm(pi->sh->physhim, M_PWRIND_MAP3); + cmplx_pwr1 = (hi << 16) + lo; + cmplx_pwr = (cmplx_pwr0 + cmplx_pwr1) >> 6; + + status_0 = 0x44; + status_1 = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_0); + if ((cmplx_pwr > 0 && cmplx_pwr < 500) + && ((status_1 & 0xc000) == 0x4000)) { + + wlc_phy_compute_dB(&cmplx_pwr, &noise_dbm, + pi->pubpi.phy_corenum); + pwr_offset_dB = (read_phy_reg(pi, 0x434) & 0xFF); + if (pwr_offset_dB > 127) + pwr_offset_dB -= 256; + + noise_dbm += (s8) (pwr_offset_dB - 30); + + gain_dB = (status_0 & 0x1ff); + noise_dbm -= (s8) (gain_dB); + } else { + noise_dbm = PHY_NOISE_FIXED_VAL_LCNPHY; + } + } else if (ISNPHY(pi)) { + + jssi_aux = wlapi_bmac_read_shm(pi->sh->physhim, M_JSSI_AUX); + channel = jssi_aux & D11_CURCHANNEL_MAX; + + noise_dbm = wlc_phy_noise_read_shmem(pi); + } + + wlc_phy_noise_cb(pi, channel, noise_dbm); + +} + +static void +wlc_phy_noise_sample_request(struct brcms_phy_pub *pih, u8 reason, u8 ch) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + s8 noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; + bool sampling_in_progress = (pi->phynoise_state != 0); + bool wait_for_intr = true; + + switch (reason) { + case PHY_NOISE_SAMPLE_MON: + pi->phynoise_chan_watchdog = ch; + pi->phynoise_state |= PHY_NOISE_STATE_MON; + break; + + case PHY_NOISE_SAMPLE_EXTERNAL: + pi->phynoise_state |= PHY_NOISE_STATE_EXTERNAL; + break; + + default: + break; + } + + if (sampling_in_progress) + return; + + pi->phynoise_now = pi->sh->now; + + if (pi->phy_fixed_noise) { + if (ISNPHY(pi)) { + pi->nphy_noise_win[WL_ANT_IDX_1][pi->nphy_noise_index] = + PHY_NOISE_FIXED_VAL_NPHY; + pi->nphy_noise_win[WL_ANT_IDX_2][pi->nphy_noise_index] = + PHY_NOISE_FIXED_VAL_NPHY; + pi->nphy_noise_index = MODINC_POW2(pi->nphy_noise_index, + PHY_NOISE_WINDOW_SZ); + noise_dbm = PHY_NOISE_FIXED_VAL_NPHY; + } else { + noise_dbm = PHY_NOISE_FIXED_VAL; + } + + wait_for_intr = false; + goto done; + } + + if (ISLCNPHY(pi)) { + if (!pi->phynoise_polling + || (reason == PHY_NOISE_SAMPLE_EXTERNAL)) { + wlapi_bmac_write_shm(pi->sh->physhim, M_JSSI_0, 0); + wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP0, 0); + wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP1, 0); + wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP2, 0); + wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP3, 0); + + bcma_set32(pi->d11core, D11REGOFFS(maccommand), + MCMD_BG_NOISE); + } else { + wlapi_suspend_mac_and_wait(pi->sh->physhim); + wlc_lcnphy_deaf_mode(pi, (bool) 0); + noise_dbm = (s8) wlc_lcnphy_rx_signal_power(pi, 20); + wlc_lcnphy_deaf_mode(pi, (bool) 1); + wlapi_enable_mac(pi->sh->physhim); + wait_for_intr = false; + } + } else if (ISNPHY(pi)) { + if (!pi->phynoise_polling + || (reason == PHY_NOISE_SAMPLE_EXTERNAL)) { + + wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP0, 0); + wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP1, 0); + wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP2, 0); + wlapi_bmac_write_shm(pi->sh->physhim, M_PWRIND_MAP3, 0); + + bcma_set32(pi->d11core, D11REGOFFS(maccommand), + MCMD_BG_NOISE); + } else { + struct phy_iq_est est[PHY_CORE_MAX]; + u32 cmplx_pwr[PHY_CORE_MAX]; + s8 noise_dbm_ant[PHY_CORE_MAX]; + u16 log_num_samps, num_samps, classif_state = 0; + u8 wait_time = 32; + u8 wait_crs = 0; + u8 i; + + memset((u8 *) est, 0, sizeof(est)); + memset((u8 *) cmplx_pwr, 0, sizeof(cmplx_pwr)); + memset((u8 *) noise_dbm_ant, 0, sizeof(noise_dbm_ant)); + + log_num_samps = PHY_NOISE_SAMPLE_LOG_NUM_NPHY; + num_samps = 1 << log_num_samps; + + wlapi_suspend_mac_and_wait(pi->sh->physhim); + classif_state = wlc_phy_classifier_nphy(pi, 0, 0); + wlc_phy_classifier_nphy(pi, 3, 0); + wlc_phy_rx_iq_est_nphy(pi, est, num_samps, wait_time, + wait_crs); + wlc_phy_classifier_nphy(pi, (0x7 << 0), classif_state); + wlapi_enable_mac(pi->sh->physhim); + + for (i = 0; i < pi->pubpi.phy_corenum; i++) + cmplx_pwr[i] = (est[i].i_pwr + est[i].q_pwr) >> + log_num_samps; + + wlc_phy_noise_calc_phy(pi, cmplx_pwr, noise_dbm_ant); + + for (i = 0; i < pi->pubpi.phy_corenum; i++) { + pi->nphy_noise_win[i][pi->nphy_noise_index] = + noise_dbm_ant[i]; + + if (noise_dbm_ant[i] > noise_dbm) + noise_dbm = noise_dbm_ant[i]; + } + pi->nphy_noise_index = MODINC_POW2(pi->nphy_noise_index, + PHY_NOISE_WINDOW_SZ); + + wait_for_intr = false; + } + } + +done: + + if (!wait_for_intr) + wlc_phy_noise_cb(pi, ch, noise_dbm); + +} + +void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *pih) +{ + u8 channel; + + channel = CHSPEC_CHANNEL(wlc_phy_chanspec_get(pih)); + + wlc_phy_noise_sample_request(pih, PHY_NOISE_SAMPLE_EXTERNAL, channel); +} + +static const s8 lcnphy_gain_index_offset_for_pkt_rssi[] = { + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 9, + 10, + 8, + 8, + 7, + 7, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 0, + 0, + 0, + 0 +}; + +void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_cmplx_pwr_dB, u8 core) +{ + u8 msb, secondmsb, i; + u32 tmp; + + for (i = 0; i < core; i++) { + secondmsb = 0; + tmp = cmplx_pwr[i]; + msb = fls(tmp); + if (msb) + secondmsb = (u8) ((tmp >> (--msb - 1)) & 1); + p_cmplx_pwr_dB[i] = (s8) (3 * msb + 2 * secondmsb); + } +} + +int wlc_phy_rssi_compute(struct brcms_phy_pub *pih, + struct d11rxhdr *rxh) +{ + int rssi = rxh->PhyRxStatus_1 & PRXS1_JSSI_MASK; + uint radioid = pih->radioid; + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + if ((pi->sh->corerev >= 11) + && !(rxh->RxStatus2 & RXS_PHYRXST_VALID)) { + rssi = BRCMS_RSSI_INVALID; + goto end; + } + + if (ISLCNPHY(pi)) { + u8 gidx = (rxh->PhyRxStatus_2 & 0xFC00) >> 10; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + if (rssi > 127) + rssi -= 256; + + rssi = rssi + lcnphy_gain_index_offset_for_pkt_rssi[gidx]; + if ((rssi > -46) && (gidx > 18)) + rssi = rssi + 7; + + rssi = rssi + pi_lcn->lcnphy_pkteng_rssi_slope; + + rssi = rssi + 2; + + } + + if (ISLCNPHY(pi)) { + if (rssi > 127) + rssi -= 256; + } else if (radioid == BCM2055_ID || radioid == BCM2056_ID + || radioid == BCM2057_ID) { + rssi = wlc_phy_rssi_compute_nphy(pi, rxh); + } + +end: + return rssi; +} + +void wlc_phy_freqtrack_start(struct brcms_phy_pub *pih) +{ + return; +} + +void wlc_phy_freqtrack_end(struct brcms_phy_pub *pih) +{ + return; +} + +void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag) +{ + struct brcms_phy *pi; + pi = (struct brcms_phy *) ppi; + + if (ISLCNPHY(pi)) + wlc_lcnphy_deaf_mode(pi, true); + else if (ISNPHY(pi)) + wlc_nphy_deaf_mode(pi, true); +} + +void wlc_phy_watchdog(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + bool delay_phy_cal = false; + pi->sh->now++; + + if (!pi->watchdog_override) + return; + + if (!(SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi))) + wlc_phy_noise_sample_request((struct brcms_phy_pub *) pi, + PHY_NOISE_SAMPLE_MON, + CHSPEC_CHANNEL(pi-> + radio_chanspec)); + + if (pi->phynoise_state && (pi->sh->now - pi->phynoise_now) > 5) + pi->phynoise_state = 0; + + if ((!pi->phycal_txpower) || + ((pi->sh->now - pi->phycal_txpower) >= pi->sh->fast_timer)) { + + if (!SCAN_INPROG_PHY(pi) && wlc_phy_cal_txpower_recalc_sw(pi)) + pi->phycal_txpower = pi->sh->now; + } + + if ((SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi) + || ASSOC_INPROG_PHY(pi))) + return; + + if (ISNPHY(pi) && !pi->disable_percal && !delay_phy_cal) { + + if ((pi->nphy_perical != PHY_PERICAL_DISABLE) && + (pi->nphy_perical != PHY_PERICAL_MANUAL) && + ((pi->sh->now - pi->nphy_perical_last) >= + pi->sh->glacial_timer)) + wlc_phy_cal_perical((struct brcms_phy_pub *) pi, + PHY_PERICAL_WATCHDOG); + + wlc_phy_txpwr_papd_cal_nphy(pi); + } + + if (ISLCNPHY(pi)) { + if (pi->phy_forcecal || + ((pi->sh->now - pi->phy_lastcal) >= + pi->sh->glacial_timer)) { + if (!(SCAN_RM_IN_PROGRESS(pi) || ASSOC_INPROG_PHY(pi))) + wlc_lcnphy_calib_modes( + pi, + LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL); + if (! + (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi) + || ASSOC_INPROG_PHY(pi) + || pi->carrier_suppr_disable + || pi->disable_percal)) + wlc_lcnphy_calib_modes(pi, + PHY_PERICAL_WATCHDOG); + } + } +} + +void wlc_phy_BSSinit(struct brcms_phy_pub *pih, bool bonlyap, int rssi) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + uint i; + uint k; + + for (i = 0; i < MA_WINDOW_SZ; i++) + pi->sh->phy_noise_window[i] = (s8) (rssi & 0xff); + if (ISLCNPHY(pi)) { + for (i = 0; i < MA_WINDOW_SZ; i++) + pi->sh->phy_noise_window[i] = + PHY_NOISE_FIXED_VAL_LCNPHY; + } + pi->sh->phy_noise_index = 0; + + for (i = 0; i < PHY_NOISE_WINDOW_SZ; i++) { + for (k = WL_ANT_IDX_1; k < WL_ANT_RX_MAX; k++) + pi->nphy_noise_win[k][i] = PHY_NOISE_FIXED_VAL_NPHY; + } + pi->nphy_noise_index = 0; +} + +void +wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, s32 *eps_imag) +{ + *eps_imag = (epsilon >> 13); + if (*eps_imag > 0xfff) + *eps_imag -= 0x2000; + + *eps_real = (epsilon & 0x1fff); + if (*eps_real > 0xfff) + *eps_real -= 0x2000; +} + +void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi) +{ + wlapi_del_timer(pi->phycal_timer); + + pi->cal_type_override = PHY_PERICAL_AUTO; + pi->mphase_cal_phase_id = MPHASE_CAL_STATE_IDLE; + pi->mphase_txcal_cmdidx = 0; +} + +static void +wlc_phy_cal_perical_mphase_schedule(struct brcms_phy *pi, uint delay) +{ + + if ((pi->nphy_perical != PHY_PERICAL_MPHASE) && + (pi->nphy_perical != PHY_PERICAL_MANUAL)) + return; + + wlapi_del_timer(pi->phycal_timer); + + pi->mphase_cal_phase_id = MPHASE_CAL_STATE_INIT; + wlapi_add_timer(pi->phycal_timer, delay, 0); +} + +void wlc_phy_cal_perical(struct brcms_phy_pub *pih, u8 reason) +{ + s16 nphy_currtemp = 0; + s16 delta_temp = 0; + bool do_periodic_cal = true; + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + if (!ISNPHY(pi)) + return; + + if ((pi->nphy_perical == PHY_PERICAL_DISABLE) || + (pi->nphy_perical == PHY_PERICAL_MANUAL)) + return; + + switch (reason) { + case PHY_PERICAL_DRIVERUP: + break; + + case PHY_PERICAL_PHYINIT: + if (pi->nphy_perical == PHY_PERICAL_MPHASE) { + if (PHY_PERICAL_MPHASE_PENDING(pi)) + wlc_phy_cal_perical_mphase_reset(pi); + + wlc_phy_cal_perical_mphase_schedule( + pi, + PHY_PERICAL_INIT_DELAY); + } + break; + + case PHY_PERICAL_JOIN_BSS: + case PHY_PERICAL_START_IBSS: + case PHY_PERICAL_UP_BSS: + if ((pi->nphy_perical == PHY_PERICAL_MPHASE) && + PHY_PERICAL_MPHASE_PENDING(pi)) + wlc_phy_cal_perical_mphase_reset(pi); + + pi->first_cal_after_assoc = true; + + pi->cal_type_override = PHY_PERICAL_FULL; + + if (pi->phycal_tempdelta) + pi->nphy_lastcal_temp = wlc_phy_tempsense_nphy(pi); + + wlc_phy_cal_perical_nphy_run(pi, PHY_PERICAL_FULL); + break; + + case PHY_PERICAL_WATCHDOG: + if (pi->phycal_tempdelta) { + nphy_currtemp = wlc_phy_tempsense_nphy(pi); + delta_temp = + (nphy_currtemp > pi->nphy_lastcal_temp) ? + nphy_currtemp - pi->nphy_lastcal_temp : + pi->nphy_lastcal_temp - nphy_currtemp; + + if ((delta_temp < (s16) pi->phycal_tempdelta) && + (pi->nphy_txiqlocal_chanspec == + pi->radio_chanspec)) + do_periodic_cal = false; + else + pi->nphy_lastcal_temp = nphy_currtemp; + } + + if (do_periodic_cal) { + if (pi->nphy_perical == PHY_PERICAL_MPHASE) { + if (!PHY_PERICAL_MPHASE_PENDING(pi)) + wlc_phy_cal_perical_mphase_schedule( + pi, + PHY_PERICAL_WDOG_DELAY); + } else if (pi->nphy_perical == PHY_PERICAL_SPHASE) + wlc_phy_cal_perical_nphy_run(pi, + PHY_PERICAL_AUTO); + } + break; + default: + break; + } +} + +void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi) +{ + pi->mphase_cal_phase_id = MPHASE_CAL_STATE_INIT; + pi->mphase_txcal_cmdidx = 0; +} + +u8 wlc_phy_nbits(s32 value) +{ + s32 abs_val; + u8 nbits = 0; + + abs_val = abs(value); + while ((abs_val >> nbits) > 0) + nbits++; + + return nbits; +} + +void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + pi->sh->hw_phytxchain = txchain; + pi->sh->hw_phyrxchain = rxchain; + pi->sh->phytxchain = txchain; + pi->sh->phyrxchain = rxchain; + pi->pubpi.phy_corenum = (u8)hweight8(pi->sh->phyrxchain); +} + +void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + pi->sh->phytxchain = txchain; + + if (ISNPHY(pi)) + wlc_phy_rxcore_setstate_nphy(pih, rxchain); + + pi->pubpi.phy_corenum = (u8)hweight8(pi->sh->phyrxchain); +} + +void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, u8 *rxchain) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + *txchain = pi->sh->phytxchain; + *rxchain = pi->sh->phyrxchain; +} + +u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih) +{ + s16 nphy_currtemp; + u8 active_bitmap; + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + active_bitmap = (pi->phy_txcore_heatedup) ? 0x31 : 0x33; + + if (!pi->watchdog_override) + return active_bitmap; + + if (NREV_GE(pi->pubpi.phy_rev, 6)) { + wlapi_suspend_mac_and_wait(pi->sh->physhim); + nphy_currtemp = wlc_phy_tempsense_nphy(pi); + wlapi_enable_mac(pi->sh->physhim); + + if (!pi->phy_txcore_heatedup) { + if (nphy_currtemp >= pi->phy_txcore_disable_temp) { + active_bitmap &= 0xFD; + pi->phy_txcore_heatedup = true; + } + } else { + if (nphy_currtemp <= pi->phy_txcore_enable_temp) { + active_bitmap |= 0x2; + pi->phy_txcore_heatedup = false; + } + } + } + + return active_bitmap; +} + +s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, u16 chanspec) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + u8 siso_mcs_id, cdd_mcs_id; + + siso_mcs_id = + (CHSPEC_IS40(chanspec)) ? TXP_FIRST_MCS_40_SISO : + TXP_FIRST_MCS_20_SISO; + cdd_mcs_id = + (CHSPEC_IS40(chanspec)) ? TXP_FIRST_MCS_40_CDD : + TXP_FIRST_MCS_20_CDD; + + if (pi->tx_power_target[siso_mcs_id] > + (pi->tx_power_target[cdd_mcs_id] + 12)) + return PHY_TXC1_MODE_SISO; + else + return PHY_TXC1_MODE_CDD; +} + +const u8 *wlc_phy_get_ofdm_rate_lookup(void) +{ + return ofdm_rate_lookup; +} + +void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode) +{ + if ((pi->sh->chip == BCMA_CHIP_ID_BCM4313) && + (pi->sh->boardflags & BFL_FEM)) { + if (mode) { + u16 txant = 0; + txant = wlapi_bmac_get_txant(pi->sh->physhim); + if (txant == 1) { + mod_phy_reg(pi, 0x44d, (0x1 << 2), (1) << 2); + + mod_phy_reg(pi, 0x44c, (0x1 << 2), (1) << 2); + + } + + bcma_chipco_gpio_control(&pi->d11core->bus->drv_cc, + 0x0, 0x0); + bcma_chipco_gpio_out(&pi->d11core->bus->drv_cc, + ~0x40, 0x40); + bcma_chipco_gpio_outen(&pi->d11core->bus->drv_cc, + ~0x40, 0x40); + } else { + mod_phy_reg(pi, 0x44c, (0x1 << 2), (0) << 2); + + mod_phy_reg(pi, 0x44d, (0x1 << 2), (0) << 2); + + bcma_chipco_gpio_out(&pi->d11core->bus->drv_cc, + ~0x40, 0x00); + bcma_chipco_gpio_outen(&pi->d11core->bus->drv_cc, + ~0x40, 0x00); + bcma_chipco_gpio_control(&pi->d11core->bus->drv_cc, + 0x0, 0x40); + } + } +} + +void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool ldpc) +{ + return; +} + +void +wlc_phy_get_pwrdet_offsets(struct brcms_phy *pi, s8 *cckoffset, s8 *ofdmoffset) +{ + *cckoffset = 0; + *ofdmoffset = 0; +} + +s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, u16 chanspec) +{ + + return rssi; +} + +bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *ppi) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + if (ISNPHY(pi)) + return wlc_phy_n_txpower_ipa_ison(pi); + else + return false; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h new file mode 100644 index 000000000000..4d3734f48d9c --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2010 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. + */ + +/* + * phy_hal.h: functionality exported from the phy to higher layers + */ + +#ifndef _BRCM_PHY_HAL_H_ +#define _BRCM_PHY_HAL_H_ + +#include +#include +#include + +#define IDCODE_VER_MASK 0x0000000f +#define IDCODE_VER_SHIFT 0 +#define IDCODE_MFG_MASK 0x00000fff +#define IDCODE_MFG_SHIFT 0 +#define IDCODE_ID_MASK 0x0ffff000 +#define IDCODE_ID_SHIFT 12 +#define IDCODE_REV_MASK 0xf0000000 +#define IDCODE_REV_SHIFT 28 + +#define NORADIO_ID 0xe4f5 +#define NORADIO_IDCODE 0x4e4f5246 + +#define BCM2055_ID 0x2055 +#define BCM2055_IDCODE 0x02055000 +#define BCM2055A0_IDCODE 0x1205517f + +#define BCM2056_ID 0x2056 +#define BCM2056_IDCODE 0x02056000 +#define BCM2056A0_IDCODE 0x1205617f + +#define BCM2057_ID 0x2057 +#define BCM2057_IDCODE 0x02057000 +#define BCM2057A0_IDCODE 0x1205717f + +#define BCM2064_ID 0x2064 +#define BCM2064_IDCODE 0x02064000 +#define BCM2064A0_IDCODE 0x0206417f + +#define PHY_TPC_HW_OFF false +#define PHY_TPC_HW_ON true + +#define PHY_PERICAL_DRIVERUP 1 +#define PHY_PERICAL_WATCHDOG 2 +#define PHY_PERICAL_PHYINIT 3 +#define PHY_PERICAL_JOIN_BSS 4 +#define PHY_PERICAL_START_IBSS 5 +#define PHY_PERICAL_UP_BSS 6 +#define PHY_PERICAL_CHAN 7 +#define PHY_FULLCAL 8 + +#define PHY_PERICAL_DISABLE 0 +#define PHY_PERICAL_SPHASE 1 +#define PHY_PERICAL_MPHASE 2 +#define PHY_PERICAL_MANUAL 3 + +#define PHY_HOLD_FOR_ASSOC 1 +#define PHY_HOLD_FOR_SCAN 2 +#define PHY_HOLD_FOR_RM 4 +#define PHY_HOLD_FOR_PLT 8 +#define PHY_HOLD_FOR_MUTE 16 +#define PHY_HOLD_FOR_NOT_ASSOC 0x20 + +#define PHY_MUTE_FOR_PREISM 1 +#define PHY_MUTE_ALL 0xffffffff + +#define PHY_NOISE_FIXED_VAL (-95) +#define PHY_NOISE_FIXED_VAL_NPHY (-92) +#define PHY_NOISE_FIXED_VAL_LCNPHY (-92) + +#define PHY_MODE_CAL 0x0002 +#define PHY_MODE_NOISEM 0x0004 + +#define BRCMS_TXPWR_DB_FACTOR 4 + +/* a large TX Power as an init value to factor out of min() calculations, + * keep low enough to fit in an s8, units are .25 dBm + */ +#define BRCMS_TXPWR_MAX (127) /* ~32 dBm = 1,500 mW */ + +#define BRCMS_NUM_RATES_CCK 4 +#define BRCMS_NUM_RATES_OFDM 8 +#define BRCMS_NUM_RATES_MCS_1_STREAM 8 +#define BRCMS_NUM_RATES_MCS_2_STREAM 8 +#define BRCMS_NUM_RATES_MCS_3_STREAM 8 +#define BRCMS_NUM_RATES_MCS_4_STREAM 8 + +#define BRCMS_RSSI_INVALID 0 /* invalid RSSI value */ + +struct d11regs; +struct phy_shim_info; + +struct txpwr_limits { + u8 cck[BRCMS_NUM_RATES_CCK]; + u8 ofdm[BRCMS_NUM_RATES_OFDM]; + + u8 ofdm_cdd[BRCMS_NUM_RATES_OFDM]; + + u8 ofdm_40_siso[BRCMS_NUM_RATES_OFDM]; + u8 ofdm_40_cdd[BRCMS_NUM_RATES_OFDM]; + + u8 mcs_20_siso[BRCMS_NUM_RATES_MCS_1_STREAM]; + u8 mcs_20_cdd[BRCMS_NUM_RATES_MCS_1_STREAM]; + u8 mcs_20_stbc[BRCMS_NUM_RATES_MCS_1_STREAM]; + u8 mcs_20_mimo[BRCMS_NUM_RATES_MCS_2_STREAM]; + + u8 mcs_40_siso[BRCMS_NUM_RATES_MCS_1_STREAM]; + u8 mcs_40_cdd[BRCMS_NUM_RATES_MCS_1_STREAM]; + u8 mcs_40_stbc[BRCMS_NUM_RATES_MCS_1_STREAM]; + u8 mcs_40_mimo[BRCMS_NUM_RATES_MCS_2_STREAM]; + u8 mcs32; +}; + +struct tx_power { + u32 flags; + u16 chanspec; /* txpwr report for this channel */ + u16 local_chanspec; /* channel on which we are associated */ + u8 local_max; /* local max according to the AP */ + u8 local_constraint; /* local constraint according to the AP */ + s8 antgain[2]; /* Ant gain for each band - from SROM */ + u8 rf_cores; /* count of RF Cores being reported */ + u8 est_Pout[4]; /* Latest tx power out estimate per RF chain */ + u8 est_Pout_act[4]; /* Latest tx power out estimate per RF chain + * without adjustment */ + u8 est_Pout_cck; /* Latest CCK tx power out estimate */ + u8 tx_power_max[4]; /* Maximum target power among all rates */ + /* Index of the rate with the max target power */ + u8 tx_power_max_rate_ind[4]; + /* User limit */ + u8 user_limit[WL_TX_POWER_RATES]; + /* Regulatory power limit */ + u8 reg_limit[WL_TX_POWER_RATES]; + /* Max power board can support (SROM) */ + u8 board_limit[WL_TX_POWER_RATES]; + /* Latest target power */ + u8 target[WL_TX_POWER_RATES]; +}; + +struct tx_inst_power { + u8 txpwr_est_Pout[2]; /* Latest estimate for 2.4 and 5 Ghz */ + u8 txpwr_est_Pout_gofdm; /* Pwr estimate for 2.4 OFDM */ +}; + +struct brcms_chanvec { + u8 vec[MAXCHANNEL / NBBY]; +}; + +struct shared_phy_params { + struct si_pub *sih; + struct phy_shim_info *physhim; + uint unit; + uint corerev; + u16 vid; + u16 did; + uint chip; + uint chiprev; + uint chippkg; + uint sromrev; + uint boardtype; + uint boardrev; + u32 boardflags; + u32 boardflags2; +}; + + +struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp); +struct brcms_phy_pub *wlc_phy_attach(struct shared_phy *sh, + struct bcma_device *d11core, int bandtype, + struct wiphy *wiphy); +void wlc_phy_detach(struct brcms_phy_pub *ppi); + +bool wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype, + u16 *phyrev, u16 *radioid, u16 *radiover); +bool wlc_phy_get_encore(struct brcms_phy_pub *pih); +u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih); + +void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *ppi, bool newstate); +void wlc_phy_hw_state_upd(struct brcms_phy_pub *ppi, bool newstate); +void wlc_phy_init(struct brcms_phy_pub *ppi, u16 chanspec); +void wlc_phy_watchdog(struct brcms_phy_pub *ppi); +int wlc_phy_down(struct brcms_phy_pub *ppi); +u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih); +void wlc_phy_cal_init(struct brcms_phy_pub *ppi); +void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init); + +void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, u16 chanspec); +u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi); +void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, u16 newch); +u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi); +void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw); + +int wlc_phy_rssi_compute(struct brcms_phy_pub *pih, struct d11rxhdr *rxh); +void wlc_phy_por_inform(struct brcms_phy_pub *ppi); +void wlc_phy_noise_sample_intr(struct brcms_phy_pub *ppi); +bool wlc_phy_bist_check_phy(struct brcms_phy_pub *ppi); + +void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag); + +void wlc_phy_switch_radio(struct brcms_phy_pub *ppi, bool on); +void wlc_phy_anacore(struct brcms_phy_pub *ppi, bool on); + + +void wlc_phy_BSSinit(struct brcms_phy_pub *ppi, bool bonlyap, int rssi); + +void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi, + bool wide_filter); +void wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band, + struct brcms_chanvec *channels); +u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, uint band); + +void wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint chan, u8 *_min_, + u8 *_max_, int rate); +void wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, uint chan, + u8 *_max_, u8 *_min_); +void wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, uint band, + s32 *, s32 *, u32 *); +void wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, struct txpwr_limits *, + u16 chanspec); +int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, bool *override); +int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, bool override); +void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi, + struct txpwr_limits *); +bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi); +void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, bool hwpwrctrl); +u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi); +u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi); +bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *pih); + +void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain); +void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain); +void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, u8 *rxchain); +u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih); +s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, u16 chanspec); +void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool val); + +void wlc_phy_cal_perical(struct brcms_phy_pub *ppi, u8 reason); +void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *ppi); +void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock); +void wlc_phy_cal_papd_recal(struct brcms_phy_pub *ppi); + +void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val); +void wlc_phy_clear_tssi(struct brcms_phy_pub *ppi); +void wlc_phy_hold_upd(struct brcms_phy_pub *ppi, u32 id, bool val); +void wlc_phy_mute_upd(struct brcms_phy_pub *ppi, bool val, u32 flags); + +void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type); + +void wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi, + struct tx_power *power, uint channel); + +void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal); +bool wlc_phy_test_ison(struct brcms_phy_pub *ppi); +void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, u8 txpwr_percent); +void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war); +void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, bool bf_preempt); +void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap); + +void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end); + +void wlc_phy_freqtrack_start(struct brcms_phy_pub *ppi); +void wlc_phy_freqtrack_end(struct brcms_phy_pub *ppi); + +const u8 *wlc_phy_get_ofdm_rate_lookup(void); + +s8 wlc_phy_get_tx_power_offset_by_mcs(struct brcms_phy_pub *ppi, + u8 mcs_offset); +s8 wlc_phy_get_tx_power_offset(struct brcms_phy_pub *ppi, u8 tbl_offset); +#endif /* _BRCM_PHY_HAL_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h new file mode 100644 index 000000000000..4960f7d26804 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h @@ -0,0 +1,1142 @@ +/* + * Copyright (c) 2010 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 _BRCM_PHY_INT_H_ +#define _BRCM_PHY_INT_H_ + +#include +#include +#include + +#define PHY_VERSION { 1, 82, 8, 0 } + +#define LCNXN_BASEREV 16 + +struct phy_shim_info; + +struct brcms_phy_srom_fem { + /* TSSI positive slope, 1: positive, 0: negative */ + u8 tssipos; + /* Ext PA gain-type: full-gain: 0, pa-lite: 1, no_pa: 2 */ + u8 extpagain; + /* support 32 combinations of different Pdet dynamic ranges */ + u8 pdetrange; + /* TR switch isolation */ + u8 triso; + /* antswctrl lookup table configuration: 32 possible choices */ + u8 antswctrllut; +}; + +#define ISNPHY(pi) PHYTYPE_IS((pi)->pubpi.phy_type, PHY_TYPE_N) +#define ISLCNPHY(pi) PHYTYPE_IS((pi)->pubpi.phy_type, PHY_TYPE_LCN) + +#define PHY_GET_RFATTN(rfgain) ((rfgain) & 0x0f) +#define PHY_GET_PADMIX(rfgain) (((rfgain) & 0x10) >> 4) +#define PHY_GET_RFGAINID(rfattn, padmix, width) ((rfattn) + ((padmix)*(width))) +#define PHY_SAT(x, n) ((x) > ((1<<((n)-1))-1) ? ((1<<((n)-1))-1) : \ + ((x) < -(1<<((n)-1)) ? -(1<<((n)-1)) : (x))) +#define PHY_SHIFT_ROUND(x, n) ((x) >= 0 ? ((x)+(1<<((n)-1)))>>(n) : (x)>>(n)) +#define PHY_HW_ROUND(x, s) ((x >> s) + ((x >> (s-1)) & (s != 0))) + +#define CH_5G_GROUP 3 +#define A_LOW_CHANS 0 +#define A_MID_CHANS 1 +#define A_HIGH_CHANS 2 +#define CH_2G_GROUP 1 +#define G_ALL_CHANS 0 + +#define FIRST_REF5_CHANNUM 149 +#define LAST_REF5_CHANNUM 165 +#define FIRST_5G_CHAN 14 +#define LAST_5G_CHAN 50 +#define FIRST_MID_5G_CHAN 14 +#define LAST_MID_5G_CHAN 35 +#define FIRST_HIGH_5G_CHAN 36 +#define LAST_HIGH_5G_CHAN 41 +#define FIRST_LOW_5G_CHAN 42 +#define LAST_LOW_5G_CHAN 50 + +#define BASE_LOW_5G_CHAN 4900 +#define BASE_MID_5G_CHAN 5100 +#define BASE_HIGH_5G_CHAN 5500 + +#define CHAN5G_FREQ(chan) (5000 + chan*5) +#define CHAN2G_FREQ(chan) (2407 + chan*5) + +#define TXP_FIRST_CCK 0 +#define TXP_LAST_CCK 3 +#define TXP_FIRST_OFDM 4 +#define TXP_LAST_OFDM 11 +#define TXP_FIRST_OFDM_20_CDD 12 +#define TXP_LAST_OFDM_20_CDD 19 +#define TXP_FIRST_MCS_20_SISO 20 +#define TXP_LAST_MCS_20_SISO 27 +#define TXP_FIRST_MCS_20_CDD 28 +#define TXP_LAST_MCS_20_CDD 35 +#define TXP_FIRST_MCS_20_STBC 36 +#define TXP_LAST_MCS_20_STBC 43 +#define TXP_FIRST_MCS_20_SDM 44 +#define TXP_LAST_MCS_20_SDM 51 +#define TXP_FIRST_OFDM_40_SISO 52 +#define TXP_LAST_OFDM_40_SISO 59 +#define TXP_FIRST_OFDM_40_CDD 60 +#define TXP_LAST_OFDM_40_CDD 67 +#define TXP_FIRST_MCS_40_SISO 68 +#define TXP_LAST_MCS_40_SISO 75 +#define TXP_FIRST_MCS_40_CDD 76 +#define TXP_LAST_MCS_40_CDD 83 +#define TXP_FIRST_MCS_40_STBC 84 +#define TXP_LAST_MCS_40_STBC 91 +#define TXP_FIRST_MCS_40_SDM 92 +#define TXP_LAST_MCS_40_SDM 99 +#define TXP_MCS_32 100 +#define TXP_NUM_RATES 101 +#define ADJ_PWR_TBL_LEN 84 + +#define TXP_FIRST_SISO_MCS_20 20 +#define TXP_LAST_SISO_MCS_20 27 + +#define PHY_CORE_NUM_1 1 +#define PHY_CORE_NUM_2 2 +#define PHY_CORE_NUM_3 3 +#define PHY_CORE_NUM_4 4 +#define PHY_CORE_MAX PHY_CORE_NUM_4 +#define PHY_CORE_0 0 +#define PHY_CORE_1 1 +#define PHY_CORE_2 2 +#define PHY_CORE_3 3 + +#define MA_WINDOW_SZ 8 + +#define PHY_NOISE_SAMPLE_MON 1 +#define PHY_NOISE_SAMPLE_EXTERNAL 2 +#define PHY_NOISE_WINDOW_SZ 16 +#define PHY_NOISE_GLITCH_INIT_MA 10 +#define PHY_NOISE_GLITCH_INIT_MA_BADPlCP 10 +#define PHY_NOISE_STATE_MON 0x1 +#define PHY_NOISE_STATE_EXTERNAL 0x2 +#define PHY_NOISE_SAMPLE_LOG_NUM_NPHY 10 +#define PHY_NOISE_SAMPLE_LOG_NUM_UCODE 9 + +#define PHY_NOISE_OFFSETFACT_4322 (-103) +#define PHY_NOISE_MA_WINDOW_SZ 2 + +#define PHY_RSSI_TABLE_SIZE 64 +#define RSSI_ANT_MERGE_MAX 0 +#define RSSI_ANT_MERGE_MIN 1 +#define RSSI_ANT_MERGE_AVG 2 + +#define PHY_TSSI_TABLE_SIZE 64 +#define APHY_TSSI_TABLE_SIZE 256 +#define TX_GAIN_TABLE_LENGTH 64 +#define DEFAULT_11A_TXP_IDX 24 +#define NUM_TSSI_FRAMES 4 +#define NULL_TSSI 0x7f +#define NULL_TSSI_W 0x7f7f + +#define PHY_PAPD_EPS_TBL_SIZE_LCNPHY 64 + +#define LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL 9 + +#define PHY_TXPWR_MIN 10 +#define PHY_TXPWR_MIN_NPHY 8 +#define RADIOPWR_OVERRIDE_DEF (-1) + +#define PWRTBL_NUM_COEFF 3 + +#define SPURAVOID_DISABLE 0 +#define SPURAVOID_AUTO 1 +#define SPURAVOID_FORCEON 2 +#define SPURAVOID_FORCEON2 3 + +#define PHY_SW_TIMER_FAST 15 +#define PHY_SW_TIMER_SLOW 60 +#define PHY_SW_TIMER_GLACIAL 120 + +#define PHY_PERICAL_AUTO 0 +#define PHY_PERICAL_FULL 1 +#define PHY_PERICAL_PARTIAL 2 + +#define PHY_PERICAL_NODELAY 0 +#define PHY_PERICAL_INIT_DELAY 5 +#define PHY_PERICAL_ASSOC_DELAY 5 +#define PHY_PERICAL_WDOG_DELAY 5 + +#define MPHASE_TXCAL_NUMCMDS 2 + +#define PHY_PERICAL_MPHASE_PENDING(pi) \ + (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_IDLE) + +enum { + MPHASE_CAL_STATE_IDLE = 0, + MPHASE_CAL_STATE_INIT = 1, + MPHASE_CAL_STATE_TXPHASE0, + MPHASE_CAL_STATE_TXPHASE1, + MPHASE_CAL_STATE_TXPHASE2, + MPHASE_CAL_STATE_TXPHASE3, + MPHASE_CAL_STATE_TXPHASE4, + MPHASE_CAL_STATE_TXPHASE5, + MPHASE_CAL_STATE_PAPDCAL, + MPHASE_CAL_STATE_RXCAL, + MPHASE_CAL_STATE_RSSICAL, + MPHASE_CAL_STATE_IDLETSSI +}; + +enum phy_cal_mode { + CAL_FULL, + CAL_RECAL, + CAL_CURRECAL, + CAL_DIGCAL, + CAL_GCTRL, + CAL_SOFT, + CAL_DIGLO +}; + +#define RDR_NTIERS 1 +#define RDR_TIER_SIZE 64 +#define RDR_LIST_SIZE (512/3) +#define RDR_EPOCH_SIZE 40 +#define RDR_NANTENNAS 2 +#define RDR_NTIER_SIZE RDR_LIST_SIZE +#define RDR_LP_BUFFER_SIZE 64 +#define LP_LEN_HIS_SIZE 10 + +#define STATIC_NUM_RF 32 +#define STATIC_NUM_BB 9 + +#define BB_MULT_MASK 0x0000ffff +#define BB_MULT_VALID_MASK 0x80000000 + +#define CORDIC_AG 39797 +#define CORDIC_NI 18 +#define FIXED(X) ((s32)((X) << 16)) + +#define FLOAT(X) \ + (((X) >= 0) ? ((((X) >> 15) + 1) >> 1) : -((((-(X)) >> 15) + 1) >> 1)) + +#define PHY_CHAIN_TX_DISABLE_TEMP 115 +#define PHY_HYSTERESIS_DELTATEMP 5 + +#define SCAN_INPROG_PHY(pi) \ + (mboolisset(pi->measure_hold, PHY_HOLD_FOR_SCAN)) + +#define PLT_INPROG_PHY(pi) (mboolisset(pi->measure_hold, PHY_HOLD_FOR_PLT)) + +#define ASSOC_INPROG_PHY(pi) \ + (mboolisset(pi->measure_hold, PHY_HOLD_FOR_ASSOC)) + +#define SCAN_RM_IN_PROGRESS(pi) \ + (mboolisset(pi->measure_hold, PHY_HOLD_FOR_SCAN | PHY_HOLD_FOR_RM)) + +#define PHY_MUTED(pi) \ + (mboolisset(pi->measure_hold, PHY_HOLD_FOR_MUTE)) + +#define PUB_NOT_ASSOC(pi) \ + (mboolisset(pi->measure_hold, PHY_HOLD_FOR_NOT_ASSOC)) + +struct phy_table_info { + uint table; + int q; + uint max; +}; + +struct phytbl_info { + const void *tbl_ptr; + u32 tbl_len; + u32 tbl_id; + u32 tbl_offset; + u32 tbl_width; +}; + +struct interference_info { + u8 curr_home_channel; + u16 crsminpwrthld_40_stored; + u16 crsminpwrthld_20L_stored; + u16 crsminpwrthld_20U_stored; + u16 init_gain_code_core1_stored; + u16 init_gain_code_core2_stored; + u16 init_gain_codeb_core1_stored; + u16 init_gain_codeb_core2_stored; + u16 init_gain_table_stored[4]; + + u16 clip1_hi_gain_code_core1_stored; + u16 clip1_hi_gain_code_core2_stored; + u16 clip1_hi_gain_codeb_core1_stored; + u16 clip1_hi_gain_codeb_core2_stored; + u16 nb_clip_thresh_core1_stored; + u16 nb_clip_thresh_core2_stored; + u16 init_ofdmlna2gainchange_stored[4]; + u16 init_ccklna2gainchange_stored[4]; + u16 clip1_lo_gain_code_core1_stored; + u16 clip1_lo_gain_code_core2_stored; + u16 clip1_lo_gain_codeb_core1_stored; + u16 clip1_lo_gain_codeb_core2_stored; + u16 w1_clip_thresh_core1_stored; + u16 w1_clip_thresh_core2_stored; + u16 radio_2056_core1_rssi_gain_stored; + u16 radio_2056_core2_rssi_gain_stored; + u16 energy_drop_timeout_len_stored; + + u16 ed_crs40_assertthld0_stored; + u16 ed_crs40_assertthld1_stored; + u16 ed_crs40_deassertthld0_stored; + u16 ed_crs40_deassertthld1_stored; + u16 ed_crs20L_assertthld0_stored; + u16 ed_crs20L_assertthld1_stored; + u16 ed_crs20L_deassertthld0_stored; + u16 ed_crs20L_deassertthld1_stored; + u16 ed_crs20U_assertthld0_stored; + u16 ed_crs20U_assertthld1_stored; + u16 ed_crs20U_deassertthld0_stored; + u16 ed_crs20U_deassertthld1_stored; + + u16 badplcp_ma; + u16 badplcp_ma_previous; + u16 badplcp_ma_total; + u16 badplcp_ma_list[MA_WINDOW_SZ]; + int badplcp_ma_index; + s16 pre_badplcp_cnt; + s16 bphy_pre_badplcp_cnt; + + u16 init_gain_core1; + u16 init_gain_core2; + u16 init_gainb_core1; + u16 init_gainb_core2; + u16 init_gain_rfseq[4]; + + u16 crsminpwr0; + u16 crsminpwrl0; + u16 crsminpwru0; + + s16 crsminpwr_index; + + u16 radio_2057_core1_rssi_wb1a_gc_stored; + u16 radio_2057_core2_rssi_wb1a_gc_stored; + u16 radio_2057_core1_rssi_wb1g_gc_stored; + u16 radio_2057_core2_rssi_wb1g_gc_stored; + u16 radio_2057_core1_rssi_wb2_gc_stored; + u16 radio_2057_core2_rssi_wb2_gc_stored; + u16 radio_2057_core1_rssi_nb_gc_stored; + u16 radio_2057_core2_rssi_nb_gc_stored; +}; + +struct aci_save_gphy { + u16 rc_cal_ovr; + u16 phycrsth1; + u16 phycrsth2; + u16 init_n1p1_gain; + u16 p1_p2_gain; + u16 n1_n2_gain; + u16 n1_p1_gain; + u16 div_search_gain; + u16 div_p1_p2_gain; + u16 div_search_gn_change; + u16 table_7_2; + u16 table_7_3; + u16 cckshbits_gnref; + u16 clip_thresh; + u16 clip2_thresh; + u16 clip3_thresh; + u16 clip_p2_thresh; + u16 clip_pwdn_thresh; + u16 clip_n1p1_thresh; + u16 clip_n1_pwdn_thresh; + u16 bbconfig; + u16 cthr_sthr_shdin; + u16 energy; + u16 clip_p1_p2_thresh; + u16 threshold; + u16 reg15; + u16 reg16; + u16 reg17; + u16 div_srch_idx; + u16 div_srch_p1_p2; + u16 div_srch_gn_back; + u16 ant_dwell; + u16 ant_wr_settle; +}; + +struct lo_complex_abgphy_info { + s8 i; + s8 q; +}; + +struct nphy_iq_comp { + s16 a0; + s16 b0; + s16 a1; + s16 b1; +}; + +struct nphy_txpwrindex { + s8 index; + s8 index_internal; + s8 index_internal_save; + u16 AfectrlOverride; + u16 AfeCtrlDacGain; + u16 rad_gain; + u8 bbmult; + u16 iqcomp_a; + u16 iqcomp_b; + u16 locomp; +}; + +struct txiqcal_cache { + + u16 txcal_coeffs_2G[8]; + u16 txcal_radio_regs_2G[8]; + struct nphy_iq_comp rxcal_coeffs_2G; + + u16 txcal_coeffs_5G[8]; + u16 txcal_radio_regs_5G[8]; + struct nphy_iq_comp rxcal_coeffs_5G; +}; + +struct nphy_pwrctrl { + s8 max_pwr_2g; + s8 idle_targ_2g; + s16 pwrdet_2g_a1; + s16 pwrdet_2g_b0; + s16 pwrdet_2g_b1; + s8 max_pwr_5gm; + s8 idle_targ_5gm; + s8 max_pwr_5gh; + s8 max_pwr_5gl; + s16 pwrdet_5gm_a1; + s16 pwrdet_5gm_b0; + s16 pwrdet_5gm_b1; + s16 pwrdet_5gl_a1; + s16 pwrdet_5gl_b0; + s16 pwrdet_5gl_b1; + s16 pwrdet_5gh_a1; + s16 pwrdet_5gh_b0; + s16 pwrdet_5gh_b1; + s8 idle_targ_5gl; + s8 idle_targ_5gh; + s8 idle_tssi_2g; + s8 idle_tssi_5g; + s8 idle_tssi; + s16 a1; + s16 b0; + s16 b1; +}; + +struct nphy_txgains { + u16 txlpf[2]; + u16 txgm[2]; + u16 pga[2]; + u16 pad[2]; + u16 ipa[2]; +}; + +#define PHY_NOISEVAR_BUFSIZE 10 + +struct nphy_noisevar_buf { + int bufcount; + int tone_id[PHY_NOISEVAR_BUFSIZE]; + u32 noise_vars[PHY_NOISEVAR_BUFSIZE]; + u32 min_noise_vars[PHY_NOISEVAR_BUFSIZE]; +}; + +struct rssical_cache { + u16 rssical_radio_regs_2G[2]; + u16 rssical_phyregs_2G[12]; + + u16 rssical_radio_regs_5G[2]; + u16 rssical_phyregs_5G[12]; +}; + +struct lcnphy_cal_results { + + u16 txiqlocal_a; + u16 txiqlocal_b; + u16 txiqlocal_didq; + u8 txiqlocal_ei0; + u8 txiqlocal_eq0; + u8 txiqlocal_fi0; + u8 txiqlocal_fq0; + + u16 txiqlocal_bestcoeffs[11]; + u16 txiqlocal_bestcoeffs_valid; + + u32 papd_eps_tbl[PHY_PAPD_EPS_TBL_SIZE_LCNPHY]; + u16 analog_gain_ref; + u16 lut_begin; + u16 lut_end; + u16 lut_step; + u16 rxcompdbm; + u16 papdctrl; + u16 sslpnCalibClkEnCtrl; + + u16 rxiqcal_coeff_a0; + u16 rxiqcal_coeff_b0; +}; + +struct shared_phy { + struct brcms_phy *phy_head; + uint unit; + struct phy_shim_info *physhim; + uint corerev; + u32 machwcap; + bool up; + bool clk; + uint now; + u16 vid; + u16 did; + uint chip; + uint chiprev; + uint chippkg; + uint sromrev; + uint boardtype; + uint boardrev; + u32 boardflags; + u32 boardflags2; + uint fast_timer; + uint slow_timer; + uint glacial_timer; + u8 rx_antdiv; + s8 phy_noise_window[MA_WINDOW_SZ]; + uint phy_noise_index; + u8 hw_phytxchain; + u8 hw_phyrxchain; + u8 phytxchain; + u8 phyrxchain; + u8 rssi_mode; + bool _rifs_phy; +}; + +struct brcms_phy_pub { + uint phy_type; + uint phy_rev; + u8 phy_corenum; + u16 radioid; + u8 radiorev; + u8 radiover; + + uint coreflags; + uint ana_rev; + bool abgphy_encore; +}; + +struct phy_func_ptr { + void (*init)(struct brcms_phy *); + void (*calinit)(struct brcms_phy *); + void (*chanset)(struct brcms_phy *, u16 chanspec); + void (*txpwrrecalc)(struct brcms_phy *); + int (*longtrn)(struct brcms_phy *, int); + void (*txiqccget)(struct brcms_phy *, u16 *, u16 *); + void (*txiqccset)(struct brcms_phy *, u16, u16); + u16 (*txloccget)(struct brcms_phy *); + void (*radioloftget)(struct brcms_phy *, u8 *, u8 *, u8 *, u8 *); + void (*carrsuppr)(struct brcms_phy *); + s32 (*rxsigpwr)(struct brcms_phy *, s32); + void (*detach)(struct brcms_phy *); +}; + +struct brcms_phy { + struct brcms_phy_pub pubpi_ro; + struct shared_phy *sh; + struct phy_func_ptr pi_fptr; + + union { + struct brcms_phy_lcnphy *pi_lcnphy; + } u; + bool user_txpwr_at_rfport; + + struct bcma_device *d11core; + struct brcms_phy *next; + struct brcms_phy_pub pubpi; + + bool do_initcal; + bool phytest_on; + bool ofdm_rateset_war; + bool bf_preempt_4306; + u16 radio_chanspec; + u8 antsel_type; + u16 bw; + u8 txpwr_percent; + bool phy_init_por; + + bool init_in_progress; + bool initialized; + bool sbtml_gm; + uint refcnt; + bool watchdog_override; + u8 phynoise_state; + uint phynoise_now; + int phynoise_chan_watchdog; + bool phynoise_polling; + bool disable_percal; + u32 measure_hold; + + s16 txpa_2g[PWRTBL_NUM_COEFF]; + s16 txpa_2g_low_temp[PWRTBL_NUM_COEFF]; + s16 txpa_2g_high_temp[PWRTBL_NUM_COEFF]; + s16 txpa_5g_low[PWRTBL_NUM_COEFF]; + s16 txpa_5g_mid[PWRTBL_NUM_COEFF]; + s16 txpa_5g_hi[PWRTBL_NUM_COEFF]; + + u8 tx_srom_max_2g; + u8 tx_srom_max_5g_low; + u8 tx_srom_max_5g_mid; + u8 tx_srom_max_5g_hi; + u8 tx_srom_max_rate_2g[TXP_NUM_RATES]; + u8 tx_srom_max_rate_5g_low[TXP_NUM_RATES]; + u8 tx_srom_max_rate_5g_mid[TXP_NUM_RATES]; + u8 tx_srom_max_rate_5g_hi[TXP_NUM_RATES]; + u8 tx_user_target[TXP_NUM_RATES]; + s8 tx_power_offset[TXP_NUM_RATES]; + u8 tx_power_target[TXP_NUM_RATES]; + + struct brcms_phy_srom_fem srom_fem2g; + struct brcms_phy_srom_fem srom_fem5g; + + u8 tx_power_max; + u8 tx_power_max_rate_ind; + bool hwpwrctrl; + u8 nphy_txpwrctrl; + s8 nphy_txrx_chain; + bool phy_5g_pwrgain; + + u16 phy_wreg; + u16 phy_wreg_limit; + + s8 n_preamble_override; + u8 antswitch; + u8 aa2g, aa5g; + + s8 idle_tssi[CH_5G_GROUP]; + s8 target_idle_tssi; + s8 txpwr_est_Pout; + u8 tx_power_min; + u8 txpwr_limit[TXP_NUM_RATES]; + u8 txpwr_env_limit[TXP_NUM_RATES]; + u8 adj_pwr_tbl_nphy[ADJ_PWR_TBL_LEN]; + + bool channel_14_wide_filter; + + bool txpwroverride; + bool txpwridx_override_aphy; + s16 radiopwr_override; + u16 hwpwr_txcur; + u8 saved_txpwr_idx; + + bool edcrs_threshold_lock; + + u32 tr_R_gain_val; + u32 tr_T_gain_val; + + s16 ofdm_analog_filt_bw_override; + s16 cck_analog_filt_bw_override; + s16 ofdm_rccal_override; + s16 cck_rccal_override; + u16 extlna_type; + + uint interference_mode_crs_time; + u16 crsglitch_prev; + bool interference_mode_crs; + + u32 phy_tx_tone_freq; + uint phy_lastcal; + bool phy_forcecal; + bool phy_fixed_noise; + u32 xtalfreq; + u8 pdiv; + s8 carrier_suppr_disable; + + bool phy_bphy_evm; + bool phy_bphy_rfcs; + s8 phy_scraminit; + u8 phy_gpiosel; + + s16 phy_txcore_disable_temp; + s16 phy_txcore_enable_temp; + s8 phy_tempsense_offset; + bool phy_txcore_heatedup; + + u16 radiopwr; + u16 bb_atten; + u16 txctl1; + + u16 mintxbias; + u16 mintxmag; + struct lo_complex_abgphy_info gphy_locomp_iq + [STATIC_NUM_RF][STATIC_NUM_BB]; + s8 stats_11b_txpower[STATIC_NUM_RF][STATIC_NUM_BB]; + u16 gain_table[TX_GAIN_TABLE_LENGTH]; + bool loopback_gain; + s16 max_lpback_gain_hdB; + s16 trsw_rx_gain_hdB; + u8 power_vec[8]; + + u16 rc_cal; + int nrssi_table_delta; + int nrssi_slope_scale; + int nrssi_slope_offset; + int min_rssi; + int max_rssi; + + s8 txpwridx; + u8 min_txpower; + + u8 a_band_high_disable; + + u16 tx_vos; + u16 global_tx_bb_dc_bias_loft; + + int rf_max; + int bb_max; + int rf_list_size; + int bb_list_size; + u16 *rf_attn_list; + u16 *bb_attn_list; + u16 padmix_mask; + u16 padmix_reg; + u16 *txmag_list; + uint txmag_len; + bool txmag_enable; + + s8 *a_tssi_to_dbm; + s8 *m_tssi_to_dbm; + s8 *l_tssi_to_dbm; + s8 *h_tssi_to_dbm; + u8 *hwtxpwr; + + u16 freqtrack_saved_regs[2]; + int cur_interference_mode; + bool hwpwrctrl_capable; + bool temppwrctrl_capable; + + uint phycal_nslope; + uint phycal_noffset; + uint phycal_mlo; + uint phycal_txpower; + + u8 phy_aa2g; + + bool nphy_tableloaded; + s8 nphy_rssisel; + u32 nphy_bb_mult_save; + u16 nphy_txiqlocal_bestc[11]; + bool nphy_txiqlocal_coeffsvalid; + struct nphy_txpwrindex nphy_txpwrindex[PHY_CORE_NUM_2]; + struct nphy_pwrctrl nphy_pwrctrl_info[PHY_CORE_NUM_2]; + u16 cck2gpo; + u32 ofdm2gpo; + u32 ofdm5gpo; + u32 ofdm5glpo; + u32 ofdm5ghpo; + u8 bw402gpo; + u8 bw405gpo; + u8 bw405glpo; + u8 bw405ghpo; + u8 cdd2gpo; + u8 cdd5gpo; + u8 cdd5glpo; + u8 cdd5ghpo; + u8 stbc2gpo; + u8 stbc5gpo; + u8 stbc5glpo; + u8 stbc5ghpo; + u8 bwdup2gpo; + u8 bwdup5gpo; + u8 bwdup5glpo; + u8 bwdup5ghpo; + u16 mcs2gpo[8]; + u16 mcs5gpo[8]; + u16 mcs5glpo[8]; + u16 mcs5ghpo[8]; + u32 nphy_rxcalparams; + + u8 phy_spuravoid; + bool phy_isspuravoid; + + u8 phy_pabias; + u8 nphy_papd_skip; + u8 nphy_tssi_slope; + + s16 nphy_noise_win[PHY_CORE_MAX][PHY_NOISE_WINDOW_SZ]; + u8 nphy_noise_index; + + bool nphy_gain_boost; + bool nphy_elna_gain_config; + u16 old_bphy_test; + u16 old_bphy_testcontrol; + + bool phyhang_avoid; + + bool rssical_nphy; + u8 nphy_perical; + uint nphy_perical_last; + u8 cal_type_override; + u8 mphase_cal_phase_id; + u8 mphase_txcal_cmdidx; + u8 mphase_txcal_numcmds; + u16 mphase_txcal_bestcoeffs[11]; + u16 nphy_txiqlocal_chanspec; + u16 nphy_iqcal_chanspec_2G; + u16 nphy_iqcal_chanspec_5G; + u16 nphy_rssical_chanspec_2G; + u16 nphy_rssical_chanspec_5G; + struct wlapi_timer *phycal_timer; + bool use_int_tx_iqlo_cal_nphy; + bool internal_tx_iqlo_cal_tapoff_intpa_nphy; + s16 nphy_lastcal_temp; + + struct txiqcal_cache calibration_cache; + struct rssical_cache rssical_cache; + + u8 nphy_txpwr_idx[2]; + u8 nphy_papd_cal_type; + uint nphy_papd_last_cal; + u16 nphy_papd_tx_gain_at_last_cal[2]; + u8 nphy_papd_cal_gain_index[2]; + s16 nphy_papd_epsilon_offset[2]; + bool nphy_papd_recal_enable; + u32 nphy_papd_recal_counter; + bool nphy_force_papd_cal; + bool nphy_papdcomp; + bool ipa2g_on; + bool ipa5g_on; + + u16 classifier_state; + u16 clip_state[2]; + uint nphy_deaf_count; + u8 rxiq_samps; + u8 rxiq_antsel; + + u16 rfctrlIntc1_save; + u16 rfctrlIntc2_save; + bool first_cal_after_assoc; + u16 tx_rx_cal_radio_saveregs[22]; + u16 tx_rx_cal_phy_saveregs[15]; + + u8 nphy_cal_orig_pwr_idx[2]; + u8 nphy_txcal_pwr_idx[2]; + u8 nphy_rxcal_pwr_idx[2]; + u16 nphy_cal_orig_tx_gain[2]; + struct nphy_txgains nphy_cal_target_gain; + u16 nphy_txcal_bbmult; + u16 nphy_gmval; + + u16 nphy_saved_bbconf; + + bool nphy_gband_spurwar_en; + bool nphy_gband_spurwar2_en; + bool nphy_aband_spurwar_en; + u16 nphy_rccal_value; + u16 nphy_crsminpwr[3]; + struct nphy_noisevar_buf nphy_saved_noisevars; + bool nphy_anarxlpf_adjusted; + bool nphy_crsminpwr_adjusted; + bool nphy_noisevars_adjusted; + + bool nphy_rxcal_active; + u16 radar_percal_mask; + bool dfs_lp_buffer_nphy; + + u16 nphy_fineclockgatecontrol; + + s8 rx2tx_biasentry; + + u16 crsminpwr0; + u16 crsminpwrl0; + u16 crsminpwru0; + s16 noise_crsminpwr_index; + u16 init_gain_core1; + u16 init_gain_core2; + u16 init_gainb_core1; + u16 init_gainb_core2; + u8 aci_noise_curr_channel; + u16 init_gain_rfseq[4]; + + bool radio_is_on; + + bool nphy_sample_play_lpf_bw_ctl_ovr; + + u16 tbl_data_hi; + u16 tbl_data_lo; + u16 tbl_addr; + + uint tbl_save_id; + uint tbl_save_offset; + + u8 txpwrctrl; + s8 txpwrindex[PHY_CORE_MAX]; + + u8 phycal_tempdelta; + u32 mcs20_po; + u32 mcs40_po; + struct wiphy *wiphy; +}; + +struct cs32 { + s32 q; + s32 i; +}; + +struct radio_regs { + u16 address; + u32 init_a; + u32 init_g; + u8 do_init_a; + u8 do_init_g; +}; + +struct radio_20xx_regs { + u16 address; + u8 init; + u8 do_init; +}; + +struct lcnphy_radio_regs { + u16 address; + u8 init_a; + u8 init_g; + u8 do_init_a; + u8 do_init_g; +}; + +u16 read_phy_reg(struct brcms_phy *pi, u16 addr); +void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); +void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); +void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); +void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); + +u16 read_radio_reg(struct brcms_phy *pi, u16 addr); +void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); +void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); +void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); +void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask); + +void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); + +void wlc_phyreg_enter(struct brcms_phy_pub *pih); +void wlc_phyreg_exit(struct brcms_phy_pub *pih); +void wlc_radioreg_enter(struct brcms_phy_pub *pih); +void wlc_radioreg_exit(struct brcms_phy_pub *pih); + +void wlc_phy_read_table(struct brcms_phy *pi, + const struct phytbl_info *ptbl_info, + u16 tblAddr, u16 tblDataHi, u16 tblDatalo); +void wlc_phy_write_table(struct brcms_phy *pi, + const struct phytbl_info *ptbl_info, + u16 tblAddr, u16 tblDataHi, u16 tblDatalo); +void wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, uint tbl_offset, + u16 tblAddr, u16 tblDataHi, u16 tblDataLo); +void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val); + +void write_phy_channel_reg(struct brcms_phy *pi, uint val); +void wlc_phy_txpower_update_shm(struct brcms_phy *pi); + +u8 wlc_phy_nbits(s32 value); +void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_dB, u8 core); + +uint wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi, + struct radio_20xx_regs *radioregs); +uint wlc_phy_init_radio_regs(struct brcms_phy *pi, + const struct radio_regs *radioregs, + u16 core_offset); + +void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi); + +void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on); +void wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, s32 *eps_imag); + +void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi); +void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi); + +bool wlc_phy_attach_nphy(struct brcms_phy *pi); +bool wlc_phy_attach_lcnphy(struct brcms_phy *pi); + +void wlc_phy_detach_lcnphy(struct brcms_phy *pi); + +void wlc_phy_init_nphy(struct brcms_phy *pi); +void wlc_phy_init_lcnphy(struct brcms_phy *pi); + +void wlc_phy_cal_init_nphy(struct brcms_phy *pi); +void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi); + +void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, u16 chanspec); +void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec); +void wlc_phy_chanspec_set_fixup_lcnphy(struct brcms_phy *pi, u16 chanspec); +int wlc_phy_channel2freq(uint channel); +int wlc_phy_chanspec_freq2bandrange_lpssn(uint); +int wlc_phy_chanspec_bandrange_get(struct brcms_phy *, u16 chanspec); + +void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode); +s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi); + +void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi); +void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi); +void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi); + +void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index); +void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable); +void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi); +void wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val, + bool iqcalmode); + +void wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan, + u8 *max_pwr, u8 rate_id); +void wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start, + u8 rate_mcs_end, u8 rate_ofdm_start); +void wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, u8 rate_ofdm_start, + u8 rate_ofdm_end, u8 rate_mcs_start); + +u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode); +s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode); +s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode); +s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode); +void wlc_phy_carrier_suppress_lcnphy(struct brcms_phy *pi); +void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel); +void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode); +void wlc_2064_vco_cal(struct brcms_phy *pi); + +void wlc_phy_txpower_recalc_target(struct brcms_phy *pi); + +#define LCNPHY_TBL_ID_PAPDCOMPDELTATBL 0x18 +#define LCNPHY_TX_POWER_TABLE_SIZE 128 +#define LCNPHY_MAX_TX_POWER_INDEX (LCNPHY_TX_POWER_TABLE_SIZE - 1) +#define LCNPHY_TBL_ID_TXPWRCTL 0x07 +#define LCNPHY_TX_PWR_CTRL_OFF 0 +#define LCNPHY_TX_PWR_CTRL_SW (0x1 << 15) +#define LCNPHY_TX_PWR_CTRL_HW ((0x1 << 15) | \ + (0x1 << 14) | \ + (0x1 << 13)) + +#define LCNPHY_TX_PWR_CTRL_TEMPBASED 0xE001 + +void wlc_lcnphy_write_table(struct brcms_phy *pi, + const struct phytbl_info *pti); +void wlc_lcnphy_read_table(struct brcms_phy *pi, struct phytbl_info *pti); +void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b); +void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq); +void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b); +u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi); +void wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, u8 *ei0, u8 *eq0, u8 *fi0, + u8 *fq0); +void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode); +void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode); +bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi); +void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi); +s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1); +void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, s8 *cck_pwr); +void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi); + +s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index); + +#define NPHY_MAX_HPVGA1_INDEX 10 +#define NPHY_DEF_HPVGA1_INDEXLIMIT 7 + +struct phy_iq_est { + s32 iq_prod; + u32 i_pwr; + u32 q_pwr; +}; + +void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, bool enable); +void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode); + +#define wlc_phy_write_table_nphy(pi, pti) \ + wlc_phy_write_table(pi, pti, 0x72, 0x74, 0x73) + +#define wlc_phy_read_table_nphy(pi, pti) \ + wlc_phy_read_table(pi, pti, 0x72, 0x74, 0x73) + +#define wlc_nphy_table_addr(pi, id, off) \ + wlc_phy_table_addr((pi), (id), (off), 0x72, 0x74, 0x73) + +#define wlc_nphy_table_data_write(pi, w, v) \ + wlc_phy_table_data_write((pi), (w), (v)) + +void wlc_phy_table_read_nphy(struct brcms_phy *pi, u32, u32 l, u32 o, u32 w, + void *d); +void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32, u32, + const void *); + +#define PHY_IPA(pi) \ + ((pi->ipa2g_on && CHSPEC_IS2G(pi->radio_chanspec)) || \ + (pi->ipa5g_on && CHSPEC_IS5G(pi->radio_chanspec))) + +#define BRCMS_PHY_WAR_PR51571(pi) \ + if (NREV_LT((pi)->pubpi.phy_rev, 3)) \ + (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) + +void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype); +void wlc_phy_aci_reset_nphy(struct brcms_phy *pi); +void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en); + +u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint chan); +void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on); + +void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi); + +void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd); +s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi); + +u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val); + +void wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est, + u16 num_samps, u8 wait_time, u8 wait_for_crs); + +void wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write, + struct nphy_iq_comp *comp); +void wlc_phy_aci_and_noise_reduction_nphy(struct brcms_phy *pi); + +void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, u8 rxcore_bitmask); +u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih); + +void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type); +void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi); +void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi); +void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi); +u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi); + +struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi); +int wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, + struct nphy_txgains target_gain, bool full, bool m); +int wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, + u8 type, bool d); +void wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask, + s8 txpwrindex, bool res); +void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core, u8 rssi_type); +int wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type, + s32 *rssi_buf, u8 nsamps); +void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi); +int wlc_phy_aci_scan_nphy(struct brcms_phy *pi); +void wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, s32 dBm_targetpower, + bool debug); +int wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, u8 mode, + u8, bool); +void wlc_phy_stopplayback_nphy(struct brcms_phy *pi); +void wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf, + u8 num_samps); +void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi); + +int wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, struct d11rxhdr *rxh); + +#define NPHY_TESTPATTERN_BPHY_EVM 0 +#define NPHY_TESTPATTERN_BPHY_RFCS 1 + +void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs); + +void wlc_phy_get_pwrdet_offsets(struct brcms_phy *pi, s8 *cckoffset, + s8 *ofdmoffset); +s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, u16 chanspec); + +bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pih); +#endif /* _BRCM_PHY_INT_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c new file mode 100644 index 000000000000..93d4cde0eb31 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c @@ -0,0 +1,5247 @@ +/* + * Copyright (c) 2010 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 +#include +#include +#include "phy_qmath.h" +#include "phy_hal.h" +#include "phy_radio.h" +#include "phytbl_lcn.h" +#include "phy_lcn.h" + +#define PLL_2064_NDIV 90 +#define PLL_2064_LOW_END_VCO 3000 +#define PLL_2064_LOW_END_KVCO 27 +#define PLL_2064_HIGH_END_VCO 4200 +#define PLL_2064_HIGH_END_KVCO 68 +#define PLL_2064_LOOP_BW_DOUBLER 200 +#define PLL_2064_D30_DOUBLER 10500 +#define PLL_2064_LOOP_BW 260 +#define PLL_2064_D30 8000 +#define PLL_2064_CAL_REF_TO 8 +#define PLL_2064_MHZ 1000000 +#define PLL_2064_OPEN_LOOP_DELAY 5 + +#define TEMPSENSE 1 +#define VBATSENSE 2 + +#define NOISE_IF_UPD_CHK_INTERVAL 1 +#define NOISE_IF_UPD_RST_INTERVAL 60 +#define NOISE_IF_UPD_THRESHOLD_CNT 1 +#define NOISE_IF_UPD_TRHRESHOLD 50 +#define NOISE_IF_UPD_TIMEOUT 1000 +#define NOISE_IF_OFF 0 +#define NOISE_IF_CHK 1 +#define NOISE_IF_ON 2 + +#define PAPD_BLANKING_PROFILE 3 +#define PAPD2LUT 0 +#define PAPD_CORR_NORM 0 +#define PAPD_BLANKING_THRESHOLD 0 +#define PAPD_STOP_AFTER_LAST_UPDATE 0 + +#define LCN_TARGET_PWR 60 + +#define LCN_VBAT_OFFSET_433X 34649679 +#define LCN_VBAT_SLOPE_433X 8258032 + +#define LCN_VBAT_SCALE_NOM 53 +#define LCN_VBAT_SCALE_DEN 432 + +#define LCN_TEMPSENSE_OFFSET 80812 +#define LCN_TEMPSENSE_DEN 2647 + +#define LCN_BW_LMT 200 +#define LCN_CUR_LMT 1250 +#define LCN_MULT 1 +#define LCN_VCO_DIV 30 +#define LCN_OFFSET 680 +#define LCN_FACT 490 +#define LCN_CUR_DIV 2640 + +#define LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT \ + (0 + 8) +#define LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK \ + (0x7f << LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT) + +#define LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_SHIFT \ + (0 + 8) +#define LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_MASK \ + (0x7f << LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_SHIFT) + +#define wlc_lcnphy_enable_tx_gain_override(pi) \ + wlc_lcnphy_set_tx_gain_override(pi, true) +#define wlc_lcnphy_disable_tx_gain_override(pi) \ + wlc_lcnphy_set_tx_gain_override(pi, false) + +#define wlc_lcnphy_iqcal_active(pi) \ + (read_phy_reg((pi), 0x451) & \ + ((0x1 << 15) | (0x1 << 14))) + +#define txpwrctrl_off(pi) (0x7 != ((read_phy_reg(pi, 0x4a4) & 0xE000) >> 13)) +#define wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) \ + (pi->temppwrctrl_capable) +#define wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi) \ + (pi->hwpwrctrl_capable) + +#define SWCTRL_BT_TX 0x18 +#define SWCTRL_OVR_DISABLE 0x40 + +#define AFE_CLK_INIT_MODE_TXRX2X 1 +#define AFE_CLK_INIT_MODE_PAPD 0 + +#define LCNPHY_TBL_ID_IQLOCAL 0x00 + +#define LCNPHY_TBL_ID_RFSEQ 0x08 +#define LCNPHY_TBL_ID_GAIN_IDX 0x0d +#define LCNPHY_TBL_ID_SW_CTRL 0x0f +#define LCNPHY_TBL_ID_GAIN_TBL 0x12 +#define LCNPHY_TBL_ID_SPUR 0x14 +#define LCNPHY_TBL_ID_SAMPLEPLAY 0x15 +#define LCNPHY_TBL_ID_SAMPLEPLAY1 0x16 + +#define LCNPHY_TX_PWR_CTRL_RATE_OFFSET 832 +#define LCNPHY_TX_PWR_CTRL_MAC_OFFSET 128 +#define LCNPHY_TX_PWR_CTRL_GAIN_OFFSET 192 +#define LCNPHY_TX_PWR_CTRL_IQ_OFFSET 320 +#define LCNPHY_TX_PWR_CTRL_LO_OFFSET 448 +#define LCNPHY_TX_PWR_CTRL_PWR_OFFSET 576 + +#define LCNPHY_TX_PWR_CTRL_START_INDEX_2G_4313 140 + +#define LCNPHY_TX_PWR_CTRL_START_NPT 1 +#define LCNPHY_TX_PWR_CTRL_MAX_NPT 7 + +#define LCNPHY_NOISE_SAMPLES_DEFAULT 5000 + +#define LCNPHY_ACI_DETECT_START 1 +#define LCNPHY_ACI_DETECT_PROGRESS 2 +#define LCNPHY_ACI_DETECT_STOP 3 + +#define LCNPHY_ACI_CRSHIFRMLO_TRSH 100 +#define LCNPHY_ACI_GLITCH_TRSH 2000 +#define LCNPHY_ACI_TMOUT 250 +#define LCNPHY_ACI_DETECT_TIMEOUT 2 +#define LCNPHY_ACI_START_DELAY 0 + +#define wlc_lcnphy_tx_gain_override_enabled(pi) \ + (0 != (read_phy_reg((pi), 0x43b) & (0x1 << 6))) + +#define wlc_lcnphy_total_tx_frames(pi) \ + wlapi_bmac_read_shm((pi)->sh->physhim, M_UCODE_MACSTAT + \ + offsetof(struct macstat, txallfrm)) + +struct lcnphy_txgains { + u16 gm_gain; + u16 pga_gain; + u16 pad_gain; + u16 dac_gain; +}; + +enum lcnphy_cal_mode { + LCNPHY_CAL_FULL, + LCNPHY_CAL_RECAL, + LCNPHY_CAL_CURRECAL, + LCNPHY_CAL_DIGCAL, + LCNPHY_CAL_GCTRL +}; + +struct lcnphy_rx_iqcomp { + u8 chan; + s16 a; + s16 b; +}; + +struct lcnphy_spb_tone { + s16 re; + s16 im; +}; + +struct lcnphy_unsign16_struct { + u16 re; + u16 im; +}; + +struct lcnphy_iq_est { + u32 iq_prod; + u32 i_pwr; + u32 q_pwr; +}; + +struct lcnphy_sfo_cfg { + u16 ptcentreTs20; + u16 ptcentreFactor; +}; + +enum lcnphy_papd_cal_type { + LCNPHY_PAPD_CAL_CW, + LCNPHY_PAPD_CAL_OFDM +}; + +typedef u16 iqcal_gain_params_lcnphy[9]; + +static const iqcal_gain_params_lcnphy tbl_iqcal_gainparams_lcnphy_2G[] = { + {0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +static const iqcal_gain_params_lcnphy *tbl_iqcal_gainparams_lcnphy[1] = { + tbl_iqcal_gainparams_lcnphy_2G, +}; + +static const u16 iqcal_gainparams_numgains_lcnphy[1] = { + ARRAY_SIZE(tbl_iqcal_gainparams_lcnphy_2G), +}; + +static const struct lcnphy_sfo_cfg lcnphy_sfo_cfg[] = { + {965, 1087}, + {967, 1085}, + {969, 1082}, + {971, 1080}, + {973, 1078}, + {975, 1076}, + {977, 1073}, + {979, 1071}, + {981, 1069}, + {983, 1067}, + {985, 1065}, + {987, 1063}, + {989, 1060}, + {994, 1055} +}; + +static const +u16 lcnphy_iqcal_loft_gainladder[] = { + ((2 << 8) | 0), + ((3 << 8) | 0), + ((4 << 8) | 0), + ((6 << 8) | 0), + ((8 << 8) | 0), + ((11 << 8) | 0), + ((16 << 8) | 0), + ((16 << 8) | 1), + ((16 << 8) | 2), + ((16 << 8) | 3), + ((16 << 8) | 4), + ((16 << 8) | 5), + ((16 << 8) | 6), + ((16 << 8) | 7), + ((23 << 8) | 7), + ((32 << 8) | 7), + ((45 << 8) | 7), + ((64 << 8) | 7), + ((91 << 8) | 7), + ((128 << 8) | 7) +}; + +static const +u16 lcnphy_iqcal_ir_gainladder[] = { + ((1 << 8) | 0), + ((2 << 8) | 0), + ((4 << 8) | 0), + ((6 << 8) | 0), + ((8 << 8) | 0), + ((11 << 8) | 0), + ((16 << 8) | 0), + ((23 << 8) | 0), + ((32 << 8) | 0), + ((45 << 8) | 0), + ((64 << 8) | 0), + ((64 << 8) | 1), + ((64 << 8) | 2), + ((64 << 8) | 3), + ((64 << 8) | 4), + ((64 << 8) | 5), + ((64 << 8) | 6), + ((64 << 8) | 7), + ((91 << 8) | 7), + ((128 << 8) | 7) +}; + +static const +struct lcnphy_spb_tone lcnphy_spb_tone_3750[] = { + {88, 0}, + {73, 49}, + {34, 81}, + {-17, 86}, + {-62, 62}, + {-86, 17}, + {-81, -34}, + {-49, -73}, + {0, -88}, + {49, -73}, + {81, -34}, + {86, 17}, + {62, 62}, + {17, 86}, + {-34, 81}, + {-73, 49}, + {-88, 0}, + {-73, -49}, + {-34, -81}, + {17, -86}, + {62, -62}, + {86, -17}, + {81, 34}, + {49, 73}, + {0, 88}, + {-49, 73}, + {-81, 34}, + {-86, -17}, + {-62, -62}, + {-17, -86}, + {34, -81}, + {73, -49}, +}; + +static const +u16 iqlo_loopback_rf_regs[20] = { + RADIO_2064_REG036, + RADIO_2064_REG11A, + RADIO_2064_REG03A, + RADIO_2064_REG025, + RADIO_2064_REG028, + RADIO_2064_REG005, + RADIO_2064_REG112, + RADIO_2064_REG0FF, + RADIO_2064_REG11F, + RADIO_2064_REG00B, + RADIO_2064_REG113, + RADIO_2064_REG007, + RADIO_2064_REG0FC, + RADIO_2064_REG0FD, + RADIO_2064_REG012, + RADIO_2064_REG057, + RADIO_2064_REG059, + RADIO_2064_REG05C, + RADIO_2064_REG078, + RADIO_2064_REG092, +}; + +static const +u16 tempsense_phy_regs[14] = { + 0x503, + 0x4a4, + 0x4d0, + 0x4d9, + 0x4da, + 0x4a6, + 0x938, + 0x939, + 0x4d8, + 0x4d0, + 0x4d7, + 0x4a5, + 0x40d, + 0x4a2, +}; + +static const +u16 rxiq_cal_rf_reg[11] = { + RADIO_2064_REG098, + RADIO_2064_REG116, + RADIO_2064_REG12C, + RADIO_2064_REG06A, + RADIO_2064_REG00B, + RADIO_2064_REG01B, + RADIO_2064_REG113, + RADIO_2064_REG01D, + RADIO_2064_REG114, + RADIO_2064_REG02E, + RADIO_2064_REG12A, +}; + +static const +struct lcnphy_rx_iqcomp lcnphy_rx_iqcomp_table_rev0[] = { + {1, 0, 0}, + {2, 0, 0}, + {3, 0, 0}, + {4, 0, 0}, + {5, 0, 0}, + {6, 0, 0}, + {7, 0, 0}, + {8, 0, 0}, + {9, 0, 0}, + {10, 0, 0}, + {11, 0, 0}, + {12, 0, 0}, + {13, 0, 0}, + {14, 0, 0}, + {34, 0, 0}, + {38, 0, 0}, + {42, 0, 0}, + {46, 0, 0}, + {36, 0, 0}, + {40, 0, 0}, + {44, 0, 0}, + {48, 0, 0}, + {52, 0, 0}, + {56, 0, 0}, + {60, 0, 0}, + {64, 0, 0}, + {100, 0, 0}, + {104, 0, 0}, + {108, 0, 0}, + {112, 0, 0}, + {116, 0, 0}, + {120, 0, 0}, + {124, 0, 0}, + {128, 0, 0}, + {132, 0, 0}, + {136, 0, 0}, + {140, 0, 0}, + {149, 0, 0}, + {153, 0, 0}, + {157, 0, 0}, + {161, 0, 0}, + {165, 0, 0}, + {184, 0, 0}, + {188, 0, 0}, + {192, 0, 0}, + {196, 0, 0}, + {200, 0, 0}, + {204, 0, 0}, + {208, 0, 0}, + {212, 0, 0}, + {216, 0, 0}, +}; + +static const u32 lcnphy_23bitgaincode_table[] = { + 0x200100, + 0x200200, + 0x200004, + 0x200014, + 0x200024, + 0x200034, + 0x200134, + 0x200234, + 0x200334, + 0x200434, + 0x200037, + 0x200137, + 0x200237, + 0x200337, + 0x200437, + 0x000035, + 0x000135, + 0x000235, + 0x000037, + 0x000137, + 0x000237, + 0x000337, + 0x00013f, + 0x00023f, + 0x00033f, + 0x00034f, + 0x00044f, + 0x00144f, + 0x00244f, + 0x00254f, + 0x00354f, + 0x00454f, + 0x00464f, + 0x01464f, + 0x02464f, + 0x03464f, + 0x04464f, +}; + +static const s8 lcnphy_gain_table[] = { + -16, + -13, + 10, + 7, + 4, + 0, + 3, + 6, + 9, + 12, + 15, + 18, + 21, + 24, + 27, + 30, + 33, + 36, + 39, + 42, + 45, + 48, + 50, + 53, + 56, + 59, + 62, + 65, + 68, + 71, + 74, + 77, + 80, + 83, + 86, + 89, + 92, +}; + +static const s8 lcnphy_gain_index_offset_for_rssi[] = { + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 8, + 7, + 7, + 6, + 7, + 7, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + -1, + -2, + -2, + -2 +}; + +struct chan_info_2064_lcnphy { + uint chan; + uint freq; + u8 logen_buftune; + u8 logen_rccr_tx; + u8 txrf_mix_tune_ctrl; + u8 pa_input_tune_g; + u8 logen_rccr_rx; + u8 pa_rxrf_lna1_freq_tune; + u8 pa_rxrf_lna2_freq_tune; + u8 rxrf_rxrf_spare1; +}; + +static const struct chan_info_2064_lcnphy chan_info_2064_lcnphy[] = { + {1, 2412, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {2, 2417, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {3, 2422, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {4, 2427, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {5, 2432, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {6, 2437, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {7, 2442, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {8, 2447, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {9, 2452, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {10, 2457, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {11, 2462, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {12, 2467, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {13, 2472, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, + {14, 2484, 0x0B, 0x0A, 0x00, 0x07, 0x0A, 0x88, 0x88, 0x80}, +}; + +static const struct lcnphy_radio_regs lcnphy_radio_regs_2064[] = { + {0x00, 0, 0, 0, 0}, + {0x01, 0x64, 0x64, 0, 0}, + {0x02, 0x20, 0x20, 0, 0}, + {0x03, 0x66, 0x66, 0, 0}, + {0x04, 0xf8, 0xf8, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0x10, 0x10, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0x37, 0x37, 0, 0}, + {0x0B, 0x6, 0x6, 0, 0}, + {0x0C, 0x55, 0x55, 0, 0}, + {0x0D, 0x8b, 0x8b, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0x5, 0x5, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0xe, 0xe, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0xb, 0xb, 0, 0}, + {0x14, 0x2, 0x2, 0, 0}, + {0x15, 0x12, 0x12, 0, 0}, + {0x16, 0x12, 0x12, 0, 0}, + {0x17, 0xc, 0xc, 0, 0}, + {0x18, 0xc, 0xc, 0, 0}, + {0x19, 0xc, 0xc, 0, 0}, + {0x1A, 0x8, 0x8, 0, 0}, + {0x1B, 0x2, 0x2, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0x1, 0x1, 0, 0}, + {0x1E, 0x12, 0x12, 0, 0}, + {0x1F, 0x6e, 0x6e, 0, 0}, + {0x20, 0x2, 0x2, 0, 0}, + {0x21, 0x23, 0x23, 0, 0}, + {0x22, 0x8, 0x8, 0, 0}, + {0x23, 0, 0, 0, 0}, + {0x24, 0, 0, 0, 0}, + {0x25, 0xc, 0xc, 0, 0}, + {0x26, 0x33, 0x33, 0, 0}, + {0x27, 0x55, 0x55, 0, 0}, + {0x28, 0, 0, 0, 0}, + {0x29, 0x30, 0x30, 0, 0}, + {0x2A, 0xb, 0xb, 0, 0}, + {0x2B, 0x1b, 0x1b, 0, 0}, + {0x2C, 0x3, 0x3, 0, 0}, + {0x2D, 0x1b, 0x1b, 0, 0}, + {0x2E, 0, 0, 0, 0}, + {0x2F, 0x20, 0x20, 0, 0}, + {0x30, 0xa, 0xa, 0, 0}, + {0x31, 0, 0, 0, 0}, + {0x32, 0x62, 0x62, 0, 0}, + {0x33, 0x19, 0x19, 0, 0}, + {0x34, 0x33, 0x33, 0, 0}, + {0x35, 0x77, 0x77, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0x70, 0x70, 0, 0}, + {0x38, 0x3, 0x3, 0, 0}, + {0x39, 0xf, 0xf, 0, 0}, + {0x3A, 0x6, 0x6, 0, 0}, + {0x3B, 0xcf, 0xcf, 0, 0}, + {0x3C, 0x1a, 0x1a, 0, 0}, + {0x3D, 0x6, 0x6, 0, 0}, + {0x3E, 0x42, 0x42, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0xfb, 0xfb, 0, 0}, + {0x41, 0x9a, 0x9a, 0, 0}, + {0x42, 0x7a, 0x7a, 0, 0}, + {0x43, 0x29, 0x29, 0, 0}, + {0x44, 0, 0, 0, 0}, + {0x45, 0x8, 0x8, 0, 0}, + {0x46, 0xce, 0xce, 0, 0}, + {0x47, 0x27, 0x27, 0, 0}, + {0x48, 0x62, 0x62, 0, 0}, + {0x49, 0x6, 0x6, 0, 0}, + {0x4A, 0x58, 0x58, 0, 0}, + {0x4B, 0xf7, 0xf7, 0, 0}, + {0x4C, 0, 0, 0, 0}, + {0x4D, 0xb3, 0xb3, 0, 0}, + {0x4E, 0, 0, 0, 0}, + {0x4F, 0x2, 0x2, 0, 0}, + {0x50, 0, 0, 0, 0}, + {0x51, 0x9, 0x9, 0, 0}, + {0x52, 0x5, 0x5, 0, 0}, + {0x53, 0x17, 0x17, 0, 0}, + {0x54, 0x38, 0x38, 0, 0}, + {0x55, 0, 0, 0, 0}, + {0x56, 0, 0, 0, 0}, + {0x57, 0xb, 0xb, 0, 0}, + {0x58, 0, 0, 0, 0}, + {0x59, 0, 0, 0, 0}, + {0x5A, 0, 0, 0, 0}, + {0x5B, 0, 0, 0, 0}, + {0x5C, 0, 0, 0, 0}, + {0x5D, 0, 0, 0, 0}, + {0x5E, 0x88, 0x88, 0, 0}, + {0x5F, 0xcc, 0xcc, 0, 0}, + {0x60, 0x74, 0x74, 0, 0}, + {0x61, 0x74, 0x74, 0, 0}, + {0x62, 0x74, 0x74, 0, 0}, + {0x63, 0x44, 0x44, 0, 0}, + {0x64, 0x77, 0x77, 0, 0}, + {0x65, 0x44, 0x44, 0, 0}, + {0x66, 0x77, 0x77, 0, 0}, + {0x67, 0x55, 0x55, 0, 0}, + {0x68, 0x77, 0x77, 0, 0}, + {0x69, 0x77, 0x77, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0x7f, 0x7f, 0, 0}, + {0x6C, 0x8, 0x8, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0x88, 0x88, 0, 0}, + {0x6F, 0x66, 0x66, 0, 0}, + {0x70, 0x66, 0x66, 0, 0}, + {0x71, 0x28, 0x28, 0, 0}, + {0x72, 0x55, 0x55, 0, 0}, + {0x73, 0x4, 0x4, 0, 0}, + {0x74, 0, 0, 0, 0}, + {0x75, 0, 0, 0, 0}, + {0x76, 0, 0, 0, 0}, + {0x77, 0x1, 0x1, 0, 0}, + {0x78, 0xd6, 0xd6, 0, 0}, + {0x79, 0, 0, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0xb4, 0xb4, 0, 0}, + {0x84, 0x1, 0x1, 0, 0}, + {0x85, 0x20, 0x20, 0, 0}, + {0x86, 0x5, 0x5, 0, 0}, + {0x87, 0xff, 0xff, 0, 0}, + {0x88, 0x7, 0x7, 0, 0}, + {0x89, 0x77, 0x77, 0, 0}, + {0x8A, 0x77, 0x77, 0, 0}, + {0x8B, 0x77, 0x77, 0, 0}, + {0x8C, 0x77, 0x77, 0, 0}, + {0x8D, 0x8, 0x8, 0, 0}, + {0x8E, 0xa, 0xa, 0, 0}, + {0x8F, 0x8, 0x8, 0, 0}, + {0x90, 0x18, 0x18, 0, 0}, + {0x91, 0x5, 0x5, 0, 0}, + {0x92, 0x1f, 0x1f, 0, 0}, + {0x93, 0x10, 0x10, 0, 0}, + {0x94, 0x3, 0x3, 0, 0}, + {0x95, 0, 0, 0, 0}, + {0x96, 0, 0, 0, 0}, + {0x97, 0xaa, 0xaa, 0, 0}, + {0x98, 0, 0, 0, 0}, + {0x99, 0x23, 0x23, 0, 0}, + {0x9A, 0x7, 0x7, 0, 0}, + {0x9B, 0xf, 0xf, 0, 0}, + {0x9C, 0x10, 0x10, 0, 0}, + {0x9D, 0x3, 0x3, 0, 0}, + {0x9E, 0x4, 0x4, 0, 0}, + {0x9F, 0x20, 0x20, 0, 0}, + {0xA0, 0, 0, 0, 0}, + {0xA1, 0, 0, 0, 0}, + {0xA2, 0, 0, 0, 0}, + {0xA3, 0, 0, 0, 0}, + {0xA4, 0x1, 0x1, 0, 0}, + {0xA5, 0x77, 0x77, 0, 0}, + {0xA6, 0x77, 0x77, 0, 0}, + {0xA7, 0x77, 0x77, 0, 0}, + {0xA8, 0x77, 0x77, 0, 0}, + {0xA9, 0x8c, 0x8c, 0, 0}, + {0xAA, 0x88, 0x88, 0, 0}, + {0xAB, 0x78, 0x78, 0, 0}, + {0xAC, 0x57, 0x57, 0, 0}, + {0xAD, 0x88, 0x88, 0, 0}, + {0xAE, 0, 0, 0, 0}, + {0xAF, 0x8, 0x8, 0, 0}, + {0xB0, 0x88, 0x88, 0, 0}, + {0xB1, 0, 0, 0, 0}, + {0xB2, 0x1b, 0x1b, 0, 0}, + {0xB3, 0x3, 0x3, 0, 0}, + {0xB4, 0x24, 0x24, 0, 0}, + {0xB5, 0x3, 0x3, 0, 0}, + {0xB6, 0x1b, 0x1b, 0, 0}, + {0xB7, 0x24, 0x24, 0, 0}, + {0xB8, 0x3, 0x3, 0, 0}, + {0xB9, 0, 0, 0, 0}, + {0xBA, 0xaa, 0xaa, 0, 0}, + {0xBB, 0, 0, 0, 0}, + {0xBC, 0x4, 0x4, 0, 0}, + {0xBD, 0, 0, 0, 0}, + {0xBE, 0x8, 0x8, 0, 0}, + {0xBF, 0x11, 0x11, 0, 0}, + {0xC0, 0, 0, 0, 0}, + {0xC1, 0, 0, 0, 0}, + {0xC2, 0x62, 0x62, 0, 0}, + {0xC3, 0x1e, 0x1e, 0, 0}, + {0xC4, 0x33, 0x33, 0, 0}, + {0xC5, 0x37, 0x37, 0, 0}, + {0xC6, 0, 0, 0, 0}, + {0xC7, 0x70, 0x70, 0, 0}, + {0xC8, 0x1e, 0x1e, 0, 0}, + {0xC9, 0x6, 0x6, 0, 0}, + {0xCA, 0x4, 0x4, 0, 0}, + {0xCB, 0x2f, 0x2f, 0, 0}, + {0xCC, 0xf, 0xf, 0, 0}, + {0xCD, 0, 0, 0, 0}, + {0xCE, 0xff, 0xff, 0, 0}, + {0xCF, 0x8, 0x8, 0, 0}, + {0xD0, 0x3f, 0x3f, 0, 0}, + {0xD1, 0x3f, 0x3f, 0, 0}, + {0xD2, 0x3f, 0x3f, 0, 0}, + {0xD3, 0, 0, 0, 0}, + {0xD4, 0, 0, 0, 0}, + {0xD5, 0, 0, 0, 0}, + {0xD6, 0xcc, 0xcc, 0, 0}, + {0xD7, 0, 0, 0, 0}, + {0xD8, 0x8, 0x8, 0, 0}, + {0xD9, 0x8, 0x8, 0, 0}, + {0xDA, 0x8, 0x8, 0, 0}, + {0xDB, 0x11, 0x11, 0, 0}, + {0xDC, 0, 0, 0, 0}, + {0xDD, 0x87, 0x87, 0, 0}, + {0xDE, 0x88, 0x88, 0, 0}, + {0xDF, 0x8, 0x8, 0, 0}, + {0xE0, 0x8, 0x8, 0, 0}, + {0xE1, 0x8, 0x8, 0, 0}, + {0xE2, 0, 0, 0, 0}, + {0xE3, 0, 0, 0, 0}, + {0xE4, 0, 0, 0, 0}, + {0xE5, 0xf5, 0xf5, 0, 0}, + {0xE6, 0x30, 0x30, 0, 0}, + {0xE7, 0x1, 0x1, 0, 0}, + {0xE8, 0, 0, 0, 0}, + {0xE9, 0xff, 0xff, 0, 0}, + {0xEA, 0, 0, 0, 0}, + {0xEB, 0, 0, 0, 0}, + {0xEC, 0x22, 0x22, 0, 0}, + {0xED, 0, 0, 0, 0}, + {0xEE, 0, 0, 0, 0}, + {0xEF, 0, 0, 0, 0}, + {0xF0, 0x3, 0x3, 0, 0}, + {0xF1, 0x1, 0x1, 0, 0}, + {0xF2, 0, 0, 0, 0}, + {0xF3, 0, 0, 0, 0}, + {0xF4, 0, 0, 0, 0}, + {0xF5, 0, 0, 0, 0}, + {0xF6, 0, 0, 0, 0}, + {0xF7, 0x6, 0x6, 0, 0}, + {0xF8, 0, 0, 0, 0}, + {0xF9, 0, 0, 0, 0}, + {0xFA, 0x40, 0x40, 0, 0}, + {0xFB, 0, 0, 0, 0}, + {0xFC, 0x1, 0x1, 0, 0}, + {0xFD, 0x80, 0x80, 0, 0}, + {0xFE, 0x2, 0x2, 0, 0}, + {0xFF, 0x10, 0x10, 0, 0}, + {0x100, 0x2, 0x2, 0, 0}, + {0x101, 0x1e, 0x1e, 0, 0}, + {0x102, 0x1e, 0x1e, 0, 0}, + {0x103, 0, 0, 0, 0}, + {0x104, 0x1f, 0x1f, 0, 0}, + {0x105, 0, 0x8, 0, 1}, + {0x106, 0x2a, 0x2a, 0, 0}, + {0x107, 0xf, 0xf, 0, 0}, + {0x108, 0, 0, 0, 0}, + {0x109, 0, 0, 0, 0}, + {0x10A, 0, 0, 0, 0}, + {0x10B, 0, 0, 0, 0}, + {0x10C, 0, 0, 0, 0}, + {0x10D, 0, 0, 0, 0}, + {0x10E, 0, 0, 0, 0}, + {0x10F, 0, 0, 0, 0}, + {0x110, 0, 0, 0, 0}, + {0x111, 0, 0, 0, 0}, + {0x112, 0, 0, 0, 0}, + {0x113, 0, 0, 0, 0}, + {0x114, 0, 0, 0, 0}, + {0x115, 0, 0, 0, 0}, + {0x116, 0, 0, 0, 0}, + {0x117, 0, 0, 0, 0}, + {0x118, 0, 0, 0, 0}, + {0x119, 0, 0, 0, 0}, + {0x11A, 0, 0, 0, 0}, + {0x11B, 0, 0, 0, 0}, + {0x11C, 0x1, 0x1, 0, 0}, + {0x11D, 0, 0, 0, 0}, + {0x11E, 0, 0, 0, 0}, + {0x11F, 0, 0, 0, 0}, + {0x120, 0, 0, 0, 0}, + {0x121, 0, 0, 0, 0}, + {0x122, 0x80, 0x80, 0, 0}, + {0x123, 0, 0, 0, 0}, + {0x124, 0xf8, 0xf8, 0, 0}, + {0x125, 0, 0, 0, 0}, + {0x126, 0, 0, 0, 0}, + {0x127, 0, 0, 0, 0}, + {0x128, 0, 0, 0, 0}, + {0x129, 0, 0, 0, 0}, + {0x12A, 0, 0, 0, 0}, + {0x12B, 0, 0, 0, 0}, + {0x12C, 0, 0, 0, 0}, + {0x12D, 0, 0, 0, 0}, + {0x12E, 0, 0, 0, 0}, + {0x12F, 0, 0, 0, 0}, + {0x130, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +#define LCNPHY_NUM_DIG_FILT_COEFFS 16 +#define LCNPHY_NUM_TX_DIG_FILTERS_CCK 13 + +static const u16 LCNPHY_txdigfiltcoeffs_cck[LCNPHY_NUM_TX_DIG_FILTERS_CCK] + [LCNPHY_NUM_DIG_FILT_COEFFS + 1] = { + {0, 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778, 1582, 64, + 128, 64,}, + {1, 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608, 1863, 93, + 167, 93,}, + {2, 1, 415, 1874, 64, 128, 64, 792, 1656, 192, 384, 192, 778, 1582, 64, + 128, 64,}, + {3, 1, 302, 1841, 129, 258, 129, 658, 1720, 205, 410, 205, 754, 1760, + 170, 340, 170,}, + {20, 1, 360, 1884, 242, 1734, 242, 752, 1720, 205, 1845, 205, 767, 1760, + 256, 185, 256,}, + {21, 1, 360, 1884, 149, 1874, 149, 752, 1720, 205, 1883, 205, 767, 1760, + 256, 273, 256,}, + {22, 1, 360, 1884, 98, 1948, 98, 752, 1720, 205, 1924, 205, 767, 1760, + 256, 352, 256,}, + {23, 1, 350, 1884, 116, 1966, 116, 752, 1720, 205, 2008, 205, 767, 1760, + 128, 233, 128,}, + {24, 1, 325, 1884, 32, 40, 32, 756, 1720, 256, 471, 256, 766, 1760, 256, + 1881, 256,}, + {25, 1, 299, 1884, 51, 64, 51, 736, 1720, 256, 471, 256, 765, 1760, 256, + 1881, 256,}, + {26, 1, 277, 1943, 39, 117, 88, 637, 1838, 64, 192, 144, 614, 1864, 128, + 384, 288,}, + {27, 1, 245, 1943, 49, 147, 110, 626, 1838, 256, 768, 576, 613, 1864, + 128, 384, 288,}, + {30, 1, 302, 1841, 61, 122, 61, 658, 1720, 205, 410, 205, 754, 1760, + 170, 340, 170,}, +}; + +#define LCNPHY_NUM_TX_DIG_FILTERS_OFDM 3 +static const u16 LCNPHY_txdigfiltcoeffs_ofdm[LCNPHY_NUM_TX_DIG_FILTERS_OFDM] + [LCNPHY_NUM_DIG_FILT_COEFFS + 1] = { + {0, 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, + 0x278, 0xfea0, 0x80, 0x100, 0x80,}, + {1, 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50, + 750, 0xFE2B, 212, 0xFFCE, 212,}, + {2, 0, 375, 0xFF16, 37, 76, 37, 799, 0xFE74, 32, 20, 32, 748, + 0xFEF2, 128, 0xFFE2, 128} +}; + +#define wlc_lcnphy_set_start_tx_pwr_idx(pi, idx) \ + mod_phy_reg(pi, 0x4a4, \ + (0x1ff << 0), \ + (u16)(idx) << 0) + +#define wlc_lcnphy_set_tx_pwr_npt(pi, npt) \ + mod_phy_reg(pi, 0x4a5, \ + (0x7 << 8), \ + (u16)(npt) << 8) + +#define wlc_lcnphy_get_tx_pwr_ctrl(pi) \ + (read_phy_reg((pi), 0x4a4) & \ + ((0x1 << 15) | \ + (0x1 << 14) | \ + (0x1 << 13))) + +#define wlc_lcnphy_get_tx_pwr_npt(pi) \ + ((read_phy_reg(pi, 0x4a5) & \ + (0x7 << 8)) >> \ + 8) + +#define wlc_lcnphy_get_current_tx_pwr_idx_if_pwrctrl_on(pi) \ + (read_phy_reg(pi, 0x473) & 0x1ff) + +#define wlc_lcnphy_get_target_tx_pwr(pi) \ + ((read_phy_reg(pi, 0x4a7) & \ + (0xff << 0)) >> \ + 0) + +#define wlc_lcnphy_set_target_tx_pwr(pi, target) \ + mod_phy_reg(pi, 0x4a7, \ + (0xff << 0), \ + (u16)(target) << 0) + +#define wlc_radio_2064_rcal_done(pi) \ + (0 != (read_radio_reg(pi, RADIO_2064_REG05C) & 0x20)) + +#define tempsense_done(pi) \ + (0x8000 == (read_phy_reg(pi, 0x476) & 0x8000)) + +#define LCNPHY_IQLOCC_READ(val) \ + ((u8)(-(s8)(((val) & 0xf0) >> 4) + (s8)((val) & 0x0f))) + +#define FIXED_TXPWR 78 +#define LCNPHY_TEMPSENSE(val) ((s16)((val > 255) ? (val - 512) : val)) + +void wlc_lcnphy_write_table(struct brcms_phy *pi, const struct phytbl_info *pti) +{ + wlc_phy_write_table(pi, pti, 0x455, 0x457, 0x456); +} + +void wlc_lcnphy_read_table(struct brcms_phy *pi, struct phytbl_info *pti) +{ + wlc_phy_read_table(pi, pti, 0x455, 0x457, 0x456); +} + +static void +wlc_lcnphy_common_read_table(struct brcms_phy *pi, u32 tbl_id, + const u16 *tbl_ptr, u32 tbl_len, + u32 tbl_width, u32 tbl_offset) +{ + struct phytbl_info tab; + tab.tbl_id = tbl_id; + tab.tbl_ptr = tbl_ptr; + tab.tbl_len = tbl_len; + tab.tbl_width = tbl_width; + tab.tbl_offset = tbl_offset; + wlc_lcnphy_read_table(pi, &tab); +} + +static void +wlc_lcnphy_common_write_table(struct brcms_phy *pi, u32 tbl_id, + const u16 *tbl_ptr, u32 tbl_len, + u32 tbl_width, u32 tbl_offset) +{ + + struct phytbl_info tab; + tab.tbl_id = tbl_id; + tab.tbl_ptr = tbl_ptr; + tab.tbl_len = tbl_len; + tab.tbl_width = tbl_width; + tab.tbl_offset = tbl_offset; + wlc_lcnphy_write_table(pi, &tab); +} + +static u32 +wlc_lcnphy_qdiv_roundup(u32 dividend, u32 divisor, u8 precision) +{ + u32 quotient, remainder, roundup, rbit; + + quotient = dividend / divisor; + remainder = dividend % divisor; + rbit = divisor & 1; + roundup = (divisor >> 1) + rbit; + + while (precision--) { + quotient <<= 1; + if (remainder >= roundup) { + quotient++; + remainder = ((remainder - roundup) << 1) + rbit; + } else { + remainder <<= 1; + } + } + + if (remainder >= roundup) + quotient++; + + return quotient; +} + +static int wlc_lcnphy_calc_floor(s16 coeff_x, int type) +{ + int k; + k = 0; + if (type == 0) { + if (coeff_x < 0) + k = (coeff_x - 1) / 2; + else + k = coeff_x / 2; + } + + if (type == 1) { + if ((coeff_x + 1) < 0) + k = (coeff_x) / 2; + else + k = (coeff_x + 1) / 2; + } + return k; +} + +static void +wlc_lcnphy_get_tx_gain(struct brcms_phy *pi, struct lcnphy_txgains *gains) +{ + u16 dac_gain, rfgain0, rfgain1; + + dac_gain = read_phy_reg(pi, 0x439) >> 0; + gains->dac_gain = (dac_gain & 0x380) >> 7; + + rfgain0 = (read_phy_reg(pi, 0x4b5) & (0xffff << 0)) >> 0; + rfgain1 = (read_phy_reg(pi, 0x4fb) & (0x7fff << 0)) >> 0; + + gains->gm_gain = rfgain0 & 0xff; + gains->pga_gain = (rfgain0 >> 8) & 0xff; + gains->pad_gain = rfgain1 & 0xff; +} + + +static void wlc_lcnphy_set_dac_gain(struct brcms_phy *pi, u16 dac_gain) +{ + u16 dac_ctrl; + + dac_ctrl = (read_phy_reg(pi, 0x439) >> 0); + dac_ctrl = dac_ctrl & 0xc7f; + dac_ctrl = dac_ctrl | (dac_gain << 7); + mod_phy_reg(pi, 0x439, (0xfff << 0), (dac_ctrl) << 0); + +} + +static void wlc_lcnphy_set_tx_gain_override(struct brcms_phy *pi, bool bEnable) +{ + u16 bit = bEnable ? 1 : 0; + + mod_phy_reg(pi, 0x4b0, (0x1 << 7), bit << 7); + + mod_phy_reg(pi, 0x4b0, (0x1 << 14), bit << 14); + + mod_phy_reg(pi, 0x43b, (0x1 << 6), bit << 6); +} + +static void +wlc_lcnphy_rx_gain_override_enable(struct brcms_phy *pi, bool enable) +{ + u16 ebit = enable ? 1 : 0; + + mod_phy_reg(pi, 0x4b0, (0x1 << 8), ebit << 8); + + mod_phy_reg(pi, 0x44c, (0x1 << 0), ebit << 0); + + if (LCNREV_LT(pi->pubpi.phy_rev, 2)) { + mod_phy_reg(pi, 0x44c, (0x1 << 4), ebit << 4); + mod_phy_reg(pi, 0x44c, (0x1 << 6), ebit << 6); + mod_phy_reg(pi, 0x4b0, (0x1 << 5), ebit << 5); + mod_phy_reg(pi, 0x4b0, (0x1 << 6), ebit << 6); + } else { + mod_phy_reg(pi, 0x4b0, (0x1 << 12), ebit << 12); + mod_phy_reg(pi, 0x4b0, (0x1 << 13), ebit << 13); + mod_phy_reg(pi, 0x4b0, (0x1 << 5), ebit << 5); + } + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + mod_phy_reg(pi, 0x4b0, (0x1 << 10), ebit << 10); + mod_phy_reg(pi, 0x4e5, (0x1 << 3), ebit << 3); + } +} + +static void +wlc_lcnphy_set_rx_gain_by_distribution(struct brcms_phy *pi, + u16 trsw, + u16 ext_lna, + u16 biq2, + u16 biq1, + u16 tia, u16 lna2, u16 lna1) +{ + u16 gain0_15, gain16_19; + + gain16_19 = biq2 & 0xf; + gain0_15 = ((biq1 & 0xf) << 12) | + ((tia & 0xf) << 8) | + ((lna2 & 0x3) << 6) | + ((lna2 & 0x3) << 4) | + ((lna1 & 0x3) << 2) | + ((lna1 & 0x3) << 0); + + mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0); + mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0); + mod_phy_reg(pi, 0x4b1, (0x3 << 11), lna1 << 11); + + if (LCNREV_LT(pi->pubpi.phy_rev, 2)) { + mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9); + mod_phy_reg(pi, 0x4b1, (0x1 << 10), ext_lna << 10); + } else { + mod_phy_reg(pi, 0x4b1, (0x1 << 10), 0 << 10); + + mod_phy_reg(pi, 0x4b1, (0x1 << 15), 0 << 15); + + mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9); + } + + mod_phy_reg(pi, 0x44d, (0x1 << 0), (!trsw) << 0); + +} + +static void wlc_lcnphy_set_trsw_override(struct brcms_phy *pi, bool tx, bool rx) +{ + + mod_phy_reg(pi, 0x44d, + (0x1 << 1) | + (0x1 << 0), (tx ? (0x1 << 1) : 0) | (rx ? (0x1 << 0) : 0)); + + or_phy_reg(pi, 0x44c, (0x1 << 1) | (0x1 << 0)); +} + +static void wlc_lcnphy_clear_trsw_override(struct brcms_phy *pi) +{ + + and_phy_reg(pi, 0x44c, (u16) ~((0x1 << 1) | (0x1 << 0))); +} + +static void wlc_lcnphy_set_rx_iq_comp(struct brcms_phy *pi, u16 a, u16 b) +{ + mod_phy_reg(pi, 0x645, (0x3ff << 0), (a) << 0); + + mod_phy_reg(pi, 0x646, (0x3ff << 0), (b) << 0); + + mod_phy_reg(pi, 0x647, (0x3ff << 0), (a) << 0); + + mod_phy_reg(pi, 0x648, (0x3ff << 0), (b) << 0); + + mod_phy_reg(pi, 0x649, (0x3ff << 0), (a) << 0); + + mod_phy_reg(pi, 0x64a, (0x3ff << 0), (b) << 0); + +} + +static bool +wlc_lcnphy_rx_iq_est(struct brcms_phy *pi, + u16 num_samps, + u8 wait_time, struct lcnphy_iq_est *iq_est) +{ + int wait_count = 0; + bool result = true; + u8 phybw40; + phybw40 = CHSPEC_IS40(pi->radio_chanspec); + + mod_phy_reg(pi, 0x6da, (0x1 << 5), (1) << 5); + + mod_phy_reg(pi, 0x410, (0x1 << 3), (0) << 3); + + mod_phy_reg(pi, 0x482, (0xffff << 0), (num_samps) << 0); + + mod_phy_reg(pi, 0x481, (0xff << 0), ((u16) wait_time) << 0); + + mod_phy_reg(pi, 0x481, (0x1 << 8), (0) << 8); + + mod_phy_reg(pi, 0x481, (0x1 << 9), (1) << 9); + + while (read_phy_reg(pi, 0x481) & (0x1 << 9)) { + + if (wait_count > (10 * 500)) { + result = false; + goto cleanup; + } + udelay(100); + wait_count++; + } + + iq_est->iq_prod = ((u32) read_phy_reg(pi, 0x483) << 16) | + (u32) read_phy_reg(pi, 0x484); + iq_est->i_pwr = ((u32) read_phy_reg(pi, 0x485) << 16) | + (u32) read_phy_reg(pi, 0x486); + iq_est->q_pwr = ((u32) read_phy_reg(pi, 0x487) << 16) | + (u32) read_phy_reg(pi, 0x488); + +cleanup: + mod_phy_reg(pi, 0x410, (0x1 << 3), (1) << 3); + + mod_phy_reg(pi, 0x6da, (0x1 << 5), (0) << 5); + + return result; +} + +static bool wlc_lcnphy_calc_rx_iq_comp(struct brcms_phy *pi, u16 num_samps) +{ +#define LCNPHY_MIN_RXIQ_PWR 2 + bool result; + u16 a0_new, b0_new; + struct lcnphy_iq_est iq_est = { 0, 0, 0 }; + s32 a, b, temp; + s16 iq_nbits, qq_nbits, arsh, brsh; + s32 iq; + u32 ii, qq; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + a0_new = ((read_phy_reg(pi, 0x645) & (0x3ff << 0)) >> 0); + b0_new = ((read_phy_reg(pi, 0x646) & (0x3ff << 0)) >> 0); + mod_phy_reg(pi, 0x6d1, (0x1 << 2), (0) << 2); + + mod_phy_reg(pi, 0x64b, (0x1 << 6), (1) << 6); + + wlc_lcnphy_set_rx_iq_comp(pi, 0, 0); + + result = wlc_lcnphy_rx_iq_est(pi, num_samps, 32, &iq_est); + if (!result) + goto cleanup; + + iq = (s32) iq_est.iq_prod; + ii = iq_est.i_pwr; + qq = iq_est.q_pwr; + + if ((ii + qq) < LCNPHY_MIN_RXIQ_PWR) { + result = false; + goto cleanup; + } + + iq_nbits = wlc_phy_nbits(iq); + qq_nbits = wlc_phy_nbits(qq); + + arsh = 10 - (30 - iq_nbits); + if (arsh >= 0) { + a = (-(iq << (30 - iq_nbits)) + (ii >> (1 + arsh))); + temp = (s32) (ii >> arsh); + if (temp == 0) + return false; + } else { + a = (-(iq << (30 - iq_nbits)) + (ii << (-1 - arsh))); + temp = (s32) (ii << -arsh); + if (temp == 0) + return false; + } + a /= temp; + brsh = qq_nbits - 31 + 20; + if (brsh >= 0) { + b = (qq << (31 - qq_nbits)); + temp = (s32) (ii >> brsh); + if (temp == 0) + return false; + } else { + b = (qq << (31 - qq_nbits)); + temp = (s32) (ii << -brsh); + if (temp == 0) + return false; + } + b /= temp; + b -= a * a; + b = (s32) int_sqrt((unsigned long) b); + b -= (1 << 10); + a0_new = (u16) (a & 0x3ff); + b0_new = (u16) (b & 0x3ff); +cleanup: + + wlc_lcnphy_set_rx_iq_comp(pi, a0_new, b0_new); + + mod_phy_reg(pi, 0x64b, (0x1 << 0), (1) << 0); + + mod_phy_reg(pi, 0x64b, (0x1 << 3), (1) << 3); + + pi_lcn->lcnphy_cal_results.rxiqcal_coeff_a0 = a0_new; + pi_lcn->lcnphy_cal_results.rxiqcal_coeff_b0 = b0_new; + + return result; +} + +static u32 wlc_lcnphy_measure_digital_power(struct brcms_phy *pi, u16 nsamples) +{ + struct lcnphy_iq_est iq_est = { 0, 0, 0 }; + + if (!wlc_lcnphy_rx_iq_est(pi, nsamples, 32, &iq_est)) + return 0; + return (iq_est.i_pwr + iq_est.q_pwr) / nsamples; +} + +static bool wlc_lcnphy_rx_iq_cal_gain(struct brcms_phy *pi, u16 biq1_gain, + u16 tia_gain, u16 lna2_gain) +{ + u32 i_thresh_l, q_thresh_l; + u32 i_thresh_h, q_thresh_h; + struct lcnphy_iq_est iq_est_h, iq_est_l; + + wlc_lcnphy_set_rx_gain_by_distribution(pi, 0, 0, 0, biq1_gain, tia_gain, + lna2_gain, 0); + + wlc_lcnphy_rx_gain_override_enable(pi, true); + wlc_lcnphy_start_tx_tone(pi, 2000, (40 >> 1), 0); + udelay(500); + write_radio_reg(pi, RADIO_2064_REG112, 0); + if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_l)) + return false; + + wlc_lcnphy_start_tx_tone(pi, 2000, 40, 0); + udelay(500); + write_radio_reg(pi, RADIO_2064_REG112, 0); + if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_h)) + return false; + + i_thresh_l = (iq_est_l.i_pwr << 1); + i_thresh_h = (iq_est_l.i_pwr << 2) + iq_est_l.i_pwr; + + q_thresh_l = (iq_est_l.q_pwr << 1); + q_thresh_h = (iq_est_l.q_pwr << 2) + iq_est_l.q_pwr; + if ((iq_est_h.i_pwr > i_thresh_l) && + (iq_est_h.i_pwr < i_thresh_h) && + (iq_est_h.q_pwr > q_thresh_l) && + (iq_est_h.q_pwr < q_thresh_h)) + return true; + + return false; +} + +static bool +wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi, + const struct lcnphy_rx_iqcomp *iqcomp, + int iqcomp_sz, bool tx_switch, bool rx_switch, int module, + int tx_gain_idx) +{ + struct lcnphy_txgains old_gains; + u16 tx_pwr_ctrl; + u8 tx_gain_index_old = 0; + bool result = false, tx_gain_override_old = false; + u16 i, Core1TxControl_old, RFOverride0_old, + RFOverrideVal0_old, rfoverride2_old, rfoverride2val_old, + rfoverride3_old, rfoverride3val_old, rfoverride4_old, + rfoverride4val_old, afectrlovr_old, afectrlovrval_old; + int tia_gain, lna2_gain, biq1_gain; + bool set_gain; + u16 old_sslpnCalibClkEnCtrl, old_sslpnRxFeClkEnCtrl; + u16 values_to_save[11]; + s16 *ptr; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + ptr = kmalloc(sizeof(s16) * 131, GFP_ATOMIC); + if (NULL == ptr) + return false; + if (module == 2) { + while (iqcomp_sz--) { + if (iqcomp[iqcomp_sz].chan == + CHSPEC_CHANNEL(pi->radio_chanspec)) { + wlc_lcnphy_set_rx_iq_comp(pi, + (u16) + iqcomp[iqcomp_sz].a, + (u16) + iqcomp[iqcomp_sz].b); + result = true; + break; + } + } + goto cal_done; + } + + WARN_ON(module != 1); + tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); + + for (i = 0; i < 11; i++) + values_to_save[i] = + read_radio_reg(pi, rxiq_cal_rf_reg[i]); + Core1TxControl_old = read_phy_reg(pi, 0x631); + + or_phy_reg(pi, 0x631, 0x0015); + + RFOverride0_old = read_phy_reg(pi, 0x44c); + RFOverrideVal0_old = read_phy_reg(pi, 0x44d); + rfoverride2_old = read_phy_reg(pi, 0x4b0); + rfoverride2val_old = read_phy_reg(pi, 0x4b1); + rfoverride3_old = read_phy_reg(pi, 0x4f9); + rfoverride3val_old = read_phy_reg(pi, 0x4fa); + rfoverride4_old = read_phy_reg(pi, 0x938); + rfoverride4val_old = read_phy_reg(pi, 0x939); + afectrlovr_old = read_phy_reg(pi, 0x43b); + afectrlovrval_old = read_phy_reg(pi, 0x43c); + old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); + old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db); + + tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi); + if (tx_gain_override_old) { + wlc_lcnphy_get_tx_gain(pi, &old_gains); + tx_gain_index_old = pi_lcn->lcnphy_current_index; + } + + wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx); + + mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0); + + mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1); + mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1); + + write_radio_reg(pi, RADIO_2064_REG116, 0x06); + write_radio_reg(pi, RADIO_2064_REG12C, 0x07); + write_radio_reg(pi, RADIO_2064_REG06A, 0xd3); + write_radio_reg(pi, RADIO_2064_REG098, 0x03); + write_radio_reg(pi, RADIO_2064_REG00B, 0x7); + mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4); + write_radio_reg(pi, RADIO_2064_REG01D, 0x01); + write_radio_reg(pi, RADIO_2064_REG114, 0x01); + write_radio_reg(pi, RADIO_2064_REG02E, 0x10); + write_radio_reg(pi, RADIO_2064_REG12A, 0x08); + + mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0); + mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1); + mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1); + mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2); + mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2); + mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3); + mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3); + mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5); + mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5); + + mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0); + + write_phy_reg(pi, 0x6da, 0xffff); + or_phy_reg(pi, 0x6db, 0x3); + + wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch); + for (lna2_gain = 3; lna2_gain >= 0; lna2_gain--) { + for (tia_gain = 4; tia_gain >= 0; tia_gain--) { + for (biq1_gain = 6; biq1_gain >= 0; biq1_gain--) { + set_gain = wlc_lcnphy_rx_iq_cal_gain(pi, + (u16) + biq1_gain, + (u16) + tia_gain, + (u16) + lna2_gain); + if (!set_gain) + continue; + + result = wlc_lcnphy_calc_rx_iq_comp(pi, 1024); + goto stop_tone; + } + } + } + +stop_tone: + wlc_lcnphy_stop_tx_tone(pi); + + write_phy_reg(pi, 0x631, Core1TxControl_old); + + write_phy_reg(pi, 0x44c, RFOverrideVal0_old); + write_phy_reg(pi, 0x44d, RFOverrideVal0_old); + write_phy_reg(pi, 0x4b0, rfoverride2_old); + write_phy_reg(pi, 0x4b1, rfoverride2val_old); + write_phy_reg(pi, 0x4f9, rfoverride3_old); + write_phy_reg(pi, 0x4fa, rfoverride3val_old); + write_phy_reg(pi, 0x938, rfoverride4_old); + write_phy_reg(pi, 0x939, rfoverride4val_old); + write_phy_reg(pi, 0x43b, afectrlovr_old); + write_phy_reg(pi, 0x43c, afectrlovrval_old); + write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl); + write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl); + + wlc_lcnphy_clear_trsw_override(pi); + + mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2); + + for (i = 0; i < 11; i++) + write_radio_reg(pi, rxiq_cal_rf_reg[i], + values_to_save[i]); + + if (tx_gain_override_old) + wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old); + else + wlc_lcnphy_disable_tx_gain_override(pi); + + wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl); + wlc_lcnphy_rx_gain_override_enable(pi, false); + +cal_done: + kfree(ptr); + return result; +} + +s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi) +{ + s8 index; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + if (txpwrctrl_off(pi)) + index = pi_lcn->lcnphy_current_index; + else if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) + index = (s8) (wlc_lcnphy_get_current_tx_pwr_idx_if_pwrctrl_on( + pi) / 2); + else + index = pi_lcn->lcnphy_current_index; + return index; +} + +void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel) +{ + u16 afectrlovr, afectrlovrval; + afectrlovr = read_phy_reg(pi, 0x43b); + afectrlovrval = read_phy_reg(pi, 0x43c); + if (channel != 0) { + mod_phy_reg(pi, 0x43b, (0x1 << 1), (1) << 1); + + mod_phy_reg(pi, 0x43c, (0x1 << 1), (0) << 1); + + mod_phy_reg(pi, 0x43b, (0x1 << 4), (1) << 4); + + mod_phy_reg(pi, 0x43c, (0x1 << 6), (0) << 6); + + write_phy_reg(pi, 0x44b, 0xffff); + wlc_lcnphy_tx_pu(pi, 1); + + mod_phy_reg(pi, 0x634, (0xff << 8), (0) << 8); + + or_phy_reg(pi, 0x6da, 0x0080); + + or_phy_reg(pi, 0x00a, 0x228); + } else { + and_phy_reg(pi, 0x00a, ~(0x228)); + + and_phy_reg(pi, 0x6da, 0xFF7F); + write_phy_reg(pi, 0x43b, afectrlovr); + write_phy_reg(pi, 0x43c, afectrlovrval); + } +} + +static void wlc_lcnphy_toggle_afe_pwdn(struct brcms_phy *pi) +{ + u16 save_AfeCtrlOvrVal, save_AfeCtrlOvr; + + save_AfeCtrlOvrVal = read_phy_reg(pi, 0x43c); + save_AfeCtrlOvr = read_phy_reg(pi, 0x43b); + + write_phy_reg(pi, 0x43c, save_AfeCtrlOvrVal | 0x1); + write_phy_reg(pi, 0x43b, save_AfeCtrlOvr | 0x1); + + write_phy_reg(pi, 0x43c, save_AfeCtrlOvrVal & 0xfffe); + write_phy_reg(pi, 0x43b, save_AfeCtrlOvr & 0xfffe); + + write_phy_reg(pi, 0x43c, save_AfeCtrlOvrVal); + write_phy_reg(pi, 0x43b, save_AfeCtrlOvr); +} + +static void +wlc_lcnphy_txrx_spur_avoidance_mode(struct brcms_phy *pi, bool enable) +{ + if (enable) { + write_phy_reg(pi, 0x942, 0x7); + write_phy_reg(pi, 0x93b, ((1 << 13) + 23)); + write_phy_reg(pi, 0x93c, ((1 << 13) + 1989)); + + write_phy_reg(pi, 0x44a, 0x084); + write_phy_reg(pi, 0x44a, 0x080); + write_phy_reg(pi, 0x6d3, 0x2222); + write_phy_reg(pi, 0x6d3, 0x2220); + } else { + write_phy_reg(pi, 0x942, 0x0); + write_phy_reg(pi, 0x93b, ((0 << 13) + 23)); + write_phy_reg(pi, 0x93c, ((0 << 13) + 1989)); + } + wlapi_switch_macfreq(pi->sh->physhim, enable); +} + +static void +wlc_lcnphy_set_chanspec_tweaks(struct brcms_phy *pi, u16 chanspec) +{ + u8 channel = CHSPEC_CHANNEL(chanspec); + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + if (channel == 14) + mod_phy_reg(pi, 0x448, (0x3 << 8), (2) << 8); + else + mod_phy_reg(pi, 0x448, (0x3 << 8), (1) << 8); + + pi_lcn->lcnphy_bandedge_corr = 2; + if (channel == 1) + pi_lcn->lcnphy_bandedge_corr = 4; + + if (channel == 1 || channel == 2 || channel == 3 || + channel == 4 || channel == 9 || + channel == 10 || channel == 11 || channel == 12) { + bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x2, + 0x03000c04); + bcma_chipco_pll_maskset(&pi->d11core->bus->drv_cc, 0x3, + ~0x00ffffff, 0x0); + bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x4, + 0x200005c0); + + bcma_cc_set32(&pi->d11core->bus->drv_cc, BCMA_CC_PMU_CTL, + BCMA_CC_PMU_CTL_PLL_UPD); + write_phy_reg(pi, 0x942, 0); + wlc_lcnphy_txrx_spur_avoidance_mode(pi, false); + pi_lcn->lcnphy_spurmod = false; + mod_phy_reg(pi, 0x424, (0xff << 8), (0x1b) << 8); + + write_phy_reg(pi, 0x425, 0x5907); + } else { + bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x2, + 0x03140c04); + bcma_chipco_pll_maskset(&pi->d11core->bus->drv_cc, 0x3, + ~0x00ffffff, 0x333333); + bcma_chipco_pll_write(&pi->d11core->bus->drv_cc, 0x4, + 0x202c2820); + + bcma_cc_set32(&pi->d11core->bus->drv_cc, BCMA_CC_PMU_CTL, + BCMA_CC_PMU_CTL_PLL_UPD); + write_phy_reg(pi, 0x942, 0); + wlc_lcnphy_txrx_spur_avoidance_mode(pi, true); + + pi_lcn->lcnphy_spurmod = false; + mod_phy_reg(pi, 0x424, (0xff << 8), (0x1f) << 8); + + write_phy_reg(pi, 0x425, 0x590a); + } + + or_phy_reg(pi, 0x44a, 0x44); + write_phy_reg(pi, 0x44a, 0x80); +} + +static void +wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel) +{ + uint i; + const struct chan_info_2064_lcnphy *ci; + u8 rfpll_doubler = 0; + u8 pll_pwrup, pll_pwrup_ovr; + s32 qFxtal, qFref, qFvco, qFcal; + u8 d15, d16, f16, e44, e45; + u32 div_int, div_frac, fvco3, fpfd, fref3, fcal_div; + u16 loop_bw, d30, setCount; + + u8 h29, h28_ten, e30, h30_ten, cp_current; + u16 g30, d28; + + ci = &chan_info_2064_lcnphy[0]; + rfpll_doubler = 1; + + mod_radio_reg(pi, RADIO_2064_REG09D, 0x4, 0x1 << 2); + + write_radio_reg(pi, RADIO_2064_REG09E, 0xf); + if (!rfpll_doubler) { + loop_bw = PLL_2064_LOOP_BW; + d30 = PLL_2064_D30; + } else { + loop_bw = PLL_2064_LOOP_BW_DOUBLER; + d30 = PLL_2064_D30_DOUBLER; + } + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + for (i = 0; i < ARRAY_SIZE(chan_info_2064_lcnphy); i++) + if (chan_info_2064_lcnphy[i].chan == channel) + break; + + if (i >= ARRAY_SIZE(chan_info_2064_lcnphy)) + return; + + ci = &chan_info_2064_lcnphy[i]; + } + + write_radio_reg(pi, RADIO_2064_REG02A, ci->logen_buftune); + + mod_radio_reg(pi, RADIO_2064_REG030, 0x3, ci->logen_rccr_tx); + + mod_radio_reg(pi, RADIO_2064_REG091, 0x3, ci->txrf_mix_tune_ctrl); + + mod_radio_reg(pi, RADIO_2064_REG038, 0xf, ci->pa_input_tune_g); + + mod_radio_reg(pi, RADIO_2064_REG030, 0x3 << 2, + (ci->logen_rccr_rx) << 2); + + mod_radio_reg(pi, RADIO_2064_REG05E, 0xf, ci->pa_rxrf_lna1_freq_tune); + + mod_radio_reg(pi, RADIO_2064_REG05E, (0xf) << 4, + (ci->pa_rxrf_lna2_freq_tune) << 4); + + write_radio_reg(pi, RADIO_2064_REG06C, ci->rxrf_rxrf_spare1); + + pll_pwrup = (u8) read_radio_reg(pi, RADIO_2064_REG044); + pll_pwrup_ovr = (u8) read_radio_reg(pi, RADIO_2064_REG12B); + + or_radio_reg(pi, RADIO_2064_REG044, 0x07); + + or_radio_reg(pi, RADIO_2064_REG12B, (0x07) << 1); + e44 = 0; + e45 = 0; + + fpfd = rfpll_doubler ? (pi->xtalfreq << 1) : (pi->xtalfreq); + if (pi->xtalfreq > 26000000) + e44 = 1; + if (pi->xtalfreq > 52000000) + e45 = 1; + if (e44 == 0) + fcal_div = 1; + else if (e45 == 0) + fcal_div = 2; + else + fcal_div = 4; + fvco3 = (ci->freq * 3); + fref3 = 2 * fpfd; + + qFxtal = wlc_lcnphy_qdiv_roundup(pi->xtalfreq, PLL_2064_MHZ, 16); + qFref = wlc_lcnphy_qdiv_roundup(fpfd, PLL_2064_MHZ, 16); + qFcal = pi->xtalfreq * fcal_div / PLL_2064_MHZ; + qFvco = wlc_lcnphy_qdiv_roundup(fvco3, 2, 16); + + write_radio_reg(pi, RADIO_2064_REG04F, 0x02); + + d15 = (pi->xtalfreq * fcal_div * 4 / 5) / PLL_2064_MHZ - 1; + write_radio_reg(pi, RADIO_2064_REG052, (0x07 & (d15 >> 2))); + write_radio_reg(pi, RADIO_2064_REG053, (d15 & 0x3) << 5); + + d16 = (qFcal * 8 / (d15 + 1)) - 1; + write_radio_reg(pi, RADIO_2064_REG051, d16); + + f16 = ((d16 + 1) * (d15 + 1)) / qFcal; + setCount = f16 * 3 * (ci->freq) / 32 - 1; + mod_radio_reg(pi, RADIO_2064_REG053, (0x0f << 0), + (u8) (setCount >> 8)); + + or_radio_reg(pi, RADIO_2064_REG053, 0x10); + write_radio_reg(pi, RADIO_2064_REG054, (u8) (setCount & 0xff)); + + div_int = ((fvco3 * (PLL_2064_MHZ >> 4)) / fref3) << 4; + + div_frac = ((fvco3 * (PLL_2064_MHZ >> 4)) % fref3) << 4; + while (div_frac >= fref3) { + div_int++; + div_frac -= fref3; + } + div_frac = wlc_lcnphy_qdiv_roundup(div_frac, fref3, 20); + + mod_radio_reg(pi, RADIO_2064_REG045, (0x1f << 0), + (u8) (div_int >> 4)); + mod_radio_reg(pi, RADIO_2064_REG046, (0x1f << 4), + (u8) (div_int << 4)); + mod_radio_reg(pi, RADIO_2064_REG046, (0x0f << 0), + (u8) (div_frac >> 16)); + write_radio_reg(pi, RADIO_2064_REG047, (u8) (div_frac >> 8) & 0xff); + write_radio_reg(pi, RADIO_2064_REG048, (u8) div_frac & 0xff); + + write_radio_reg(pi, RADIO_2064_REG040, 0xfb); + + write_radio_reg(pi, RADIO_2064_REG041, 0x9A); + write_radio_reg(pi, RADIO_2064_REG042, 0xA3); + write_radio_reg(pi, RADIO_2064_REG043, 0x0C); + + h29 = LCN_BW_LMT / loop_bw; + d28 = (((PLL_2064_HIGH_END_KVCO - PLL_2064_LOW_END_KVCO) * + (fvco3 / 2 - PLL_2064_LOW_END_VCO)) / + (PLL_2064_HIGH_END_VCO - PLL_2064_LOW_END_VCO)) + + PLL_2064_LOW_END_KVCO; + h28_ten = (d28 * 10) / LCN_VCO_DIV; + e30 = (d30 - LCN_OFFSET) / LCN_FACT; + g30 = LCN_OFFSET + (e30 * LCN_FACT); + h30_ten = (g30 * 10) / LCN_CUR_DIV; + cp_current = ((LCN_CUR_LMT * h29 * LCN_MULT * 100) / h28_ten) / h30_ten; + mod_radio_reg(pi, RADIO_2064_REG03C, 0x3f, cp_current); + + if (channel >= 1 && channel <= 5) + write_radio_reg(pi, RADIO_2064_REG03C, 0x8); + else + write_radio_reg(pi, RADIO_2064_REG03C, 0x7); + write_radio_reg(pi, RADIO_2064_REG03D, 0x3); + + mod_radio_reg(pi, RADIO_2064_REG044, 0x0c, 0x0c); + udelay(1); + + wlc_2064_vco_cal(pi); + + write_radio_reg(pi, RADIO_2064_REG044, pll_pwrup); + write_radio_reg(pi, RADIO_2064_REG12B, pll_pwrup_ovr); + if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { + write_radio_reg(pi, RADIO_2064_REG038, 3); + write_radio_reg(pi, RADIO_2064_REG091, 7); + } + + if (!(pi->sh->boardflags & BFL_FEM)) { + static const u8 reg038[14] = { + 0xd, 0xe, 0xd, 0xd, 0xd, 0xc, 0xa, + 0xb, 0xb, 0x3, 0x3, 0x2, 0x0, 0x0 + }; + + write_radio_reg(pi, RADIO_2064_REG02A, 0xf); + write_radio_reg(pi, RADIO_2064_REG091, 0x3); + write_radio_reg(pi, RADIO_2064_REG038, 0x3); + + write_radio_reg(pi, RADIO_2064_REG038, reg038[channel - 1]); + } +} + +static int +wlc_lcnphy_load_tx_iir_filter(struct brcms_phy *pi, bool is_ofdm, s16 filt_type) +{ + s16 filt_index = -1; + int j; + + u16 addr[] = { + 0x910, + 0x91e, + 0x91f, + 0x924, + 0x925, + 0x926, + 0x920, + 0x921, + 0x927, + 0x928, + 0x929, + 0x922, + 0x923, + 0x930, + 0x931, + 0x932 + }; + + u16 addr_ofdm[] = { + 0x90f, + 0x900, + 0x901, + 0x906, + 0x907, + 0x908, + 0x902, + 0x903, + 0x909, + 0x90a, + 0x90b, + 0x904, + 0x905, + 0x90c, + 0x90d, + 0x90e + }; + + if (!is_ofdm) { + for (j = 0; j < LCNPHY_NUM_TX_DIG_FILTERS_CCK; j++) { + if (filt_type == LCNPHY_txdigfiltcoeffs_cck[j][0]) { + filt_index = (s16) j; + break; + } + } + + if (filt_index != -1) { + for (j = 0; j < LCNPHY_NUM_DIG_FILT_COEFFS; j++) + write_phy_reg(pi, addr[j], + LCNPHY_txdigfiltcoeffs_cck + [filt_index][j + 1]); + } + } else { + for (j = 0; j < LCNPHY_NUM_TX_DIG_FILTERS_OFDM; j++) { + if (filt_type == LCNPHY_txdigfiltcoeffs_ofdm[j][0]) { + filt_index = (s16) j; + break; + } + } + + if (filt_index != -1) { + for (j = 0; j < LCNPHY_NUM_DIG_FILT_COEFFS; j++) + write_phy_reg(pi, addr_ofdm[j], + LCNPHY_txdigfiltcoeffs_ofdm + [filt_index][j + 1]); + } + } + + return (filt_index != -1) ? 0 : -1; +} + +static u16 wlc_lcnphy_get_pa_gain(struct brcms_phy *pi) +{ + u16 pa_gain; + + pa_gain = (read_phy_reg(pi, 0x4fb) & + LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK) >> + LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT; + + return pa_gain; +} + +static void wlc_lcnphy_set_tx_gain(struct brcms_phy *pi, + struct lcnphy_txgains *target_gains) +{ + u16 pa_gain = wlc_lcnphy_get_pa_gain(pi); + + mod_phy_reg( + pi, 0x4b5, + (0xffff << 0), + ((target_gains->gm_gain) | + (target_gains->pga_gain << 8)) << + 0); + mod_phy_reg(pi, 0x4fb, + (0x7fff << 0), + ((target_gains->pad_gain) | (pa_gain << 8)) << 0); + + mod_phy_reg( + pi, 0x4fc, + (0xffff << 0), + ((target_gains->gm_gain) | + (target_gains->pga_gain << 8)) << + 0); + mod_phy_reg(pi, 0x4fd, + (0x7fff << 0), + ((target_gains->pad_gain) | (pa_gain << 8)) << 0); + + wlc_lcnphy_set_dac_gain(pi, target_gains->dac_gain); + + wlc_lcnphy_enable_tx_gain_override(pi); +} + +static u8 wlc_lcnphy_get_bbmult(struct brcms_phy *pi) +{ + u16 m0m1; + struct phytbl_info tab; + + tab.tbl_ptr = &m0m1; + tab.tbl_len = 1; + tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; + tab.tbl_offset = 87; + tab.tbl_width = 16; + wlc_lcnphy_read_table(pi, &tab); + + return (u8) ((m0m1 & 0xff00) >> 8); +} + +static void wlc_lcnphy_set_bbmult(struct brcms_phy *pi, u8 m0) +{ + u16 m0m1 = (u16) m0 << 8; + struct phytbl_info tab; + + tab.tbl_ptr = &m0m1; + tab.tbl_len = 1; + tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; + tab.tbl_offset = 87; + tab.tbl_width = 16; + wlc_lcnphy_write_table(pi, &tab); +} + +static void wlc_lcnphy_clear_tx_power_offsets(struct brcms_phy *pi) +{ + u32 data_buf[64]; + struct phytbl_info tab; + + memset(data_buf, 0, sizeof(data_buf)); + + tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; + tab.tbl_width = 32; + tab.tbl_ptr = data_buf; + + if (!wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { + + tab.tbl_len = 30; + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; + wlc_lcnphy_write_table(pi, &tab); + } + + tab.tbl_len = 64; + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_MAC_OFFSET; + wlc_lcnphy_write_table(pi, &tab); +} + +enum lcnphy_tssi_mode { + LCNPHY_TSSI_PRE_PA, + LCNPHY_TSSI_POST_PA, + LCNPHY_TSSI_EXT +}; + +static void +wlc_lcnphy_set_tssi_mux(struct brcms_phy *pi, enum lcnphy_tssi_mode pos) +{ + mod_phy_reg(pi, 0x4d7, (0x1 << 0), (0x1) << 0); + + mod_phy_reg(pi, 0x4d7, (0x1 << 6), (1) << 6); + + if (LCNPHY_TSSI_POST_PA == pos) { + mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0) << 2); + + mod_phy_reg(pi, 0x4d9, (0x1 << 3), (1) << 3); + + if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { + mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4); + } else { + mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0x1); + mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8); + mod_radio_reg(pi, RADIO_2064_REG028, 0x1, 0x0); + mod_radio_reg(pi, RADIO_2064_REG11A, 0x4, 1<<2); + mod_radio_reg(pi, RADIO_2064_REG036, 0x10, 0x0); + mod_radio_reg(pi, RADIO_2064_REG11A, 0x10, 1<<4); + mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0); + mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x77); + mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0xe<<1); + mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 1<<7); + mod_radio_reg(pi, RADIO_2064_REG005, 0x7, 1<<1); + mod_radio_reg(pi, RADIO_2064_REG029, 0xf0, 0<<4); + } + } else { + mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0x1) << 2); + + mod_phy_reg(pi, 0x4d9, (0x1 << 3), (0) << 3); + + if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { + mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4); + } else { + mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0); + mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8); + } + } + mod_phy_reg(pi, 0x637, (0x3 << 14), (0) << 14); + + if (LCNPHY_TSSI_EXT == pos) { + write_radio_reg(pi, RADIO_2064_REG07F, 1); + mod_radio_reg(pi, RADIO_2064_REG005, 0x7, 0x2); + mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 0x1 << 7); + mod_radio_reg(pi, RADIO_2064_REG028, 0x1f, 0x3); + } +} + +static u16 wlc_lcnphy_rfseq_tbl_adc_pwrup(struct brcms_phy *pi) +{ + u16 N1, N2, N3, N4, N5, N6, N; + N1 = ((read_phy_reg(pi, 0x4a5) & (0xff << 0)) + >> 0); + N2 = 1 << ((read_phy_reg(pi, 0x4a5) & (0x7 << 12)) + >> 12); + N3 = ((read_phy_reg(pi, 0x40d) & (0xff << 0)) + >> 0); + N4 = 1 << ((read_phy_reg(pi, 0x40d) & (0x7 << 8)) + >> 8); + N5 = ((read_phy_reg(pi, 0x4a2) & (0xff << 0)) + >> 0); + N6 = 1 << ((read_phy_reg(pi, 0x4a2) & (0x7 << 8)) + >> 8); + N = 2 * (N1 + N2 + N3 + N4 + 2 * (N5 + N6)) + 80; + if (N < 1600) + N = 1600; + return N; +} + +static void wlc_lcnphy_pwrctrl_rssiparams(struct brcms_phy *pi) +{ + u16 auxpga_vmid, auxpga_vmid_temp, auxpga_gain_temp; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + auxpga_vmid = (2 << 8) | + (pi_lcn->lcnphy_rssi_vc << 4) | pi_lcn->lcnphy_rssi_vf; + auxpga_vmid_temp = (2 << 8) | (8 << 4) | 4; + auxpga_gain_temp = 2; + + mod_phy_reg(pi, 0x4d8, (0x1 << 0), (0) << 0); + + mod_phy_reg(pi, 0x4d8, (0x1 << 1), (0) << 1); + + mod_phy_reg(pi, 0x4d7, (0x1 << 3), (0) << 3); + + mod_phy_reg(pi, 0x4db, + (0x3ff << 0) | + (0x7 << 12), + (auxpga_vmid << 0) | (pi_lcn->lcnphy_rssi_gs << 12)); + + mod_phy_reg(pi, 0x4dc, + (0x3ff << 0) | + (0x7 << 12), + (auxpga_vmid << 0) | (pi_lcn->lcnphy_rssi_gs << 12)); + + mod_phy_reg(pi, 0x40a, + (0x3ff << 0) | + (0x7 << 12), + (auxpga_vmid << 0) | (pi_lcn->lcnphy_rssi_gs << 12)); + + mod_phy_reg(pi, 0x40b, + (0x3ff << 0) | + (0x7 << 12), + (auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12)); + + mod_phy_reg(pi, 0x40c, + (0x3ff << 0) | + (0x7 << 12), + (auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12)); + + mod_radio_reg(pi, RADIO_2064_REG082, (1 << 5), (1 << 5)); + mod_radio_reg(pi, RADIO_2064_REG07C, (1 << 0), (1 << 0)); +} + +static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi) +{ + struct phytbl_info tab; + u32 rfseq, ind; + enum lcnphy_tssi_mode mode; + u8 tssi_sel; + + if (pi->sh->boardflags & BFL_FEM) { + tssi_sel = 0x1; + mode = LCNPHY_TSSI_EXT; + } else { + tssi_sel = 0xe; + mode = LCNPHY_TSSI_POST_PA; + } + tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; + tab.tbl_width = 32; + tab.tbl_ptr = &ind; + tab.tbl_len = 1; + tab.tbl_offset = 0; + for (ind = 0; ind < 128; ind++) { + wlc_lcnphy_write_table(pi, &tab); + tab.tbl_offset++; + } + tab.tbl_offset = 704; + for (ind = 0; ind < 128; ind++) { + wlc_lcnphy_write_table(pi, &tab); + tab.tbl_offset++; + } + mod_phy_reg(pi, 0x503, (0x1 << 0), (0) << 0); + + mod_phy_reg(pi, 0x503, (0x1 << 2), (0) << 2); + + mod_phy_reg(pi, 0x503, (0x1 << 4), (1) << 4); + + wlc_lcnphy_set_tssi_mux(pi, mode); + mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14); + + mod_phy_reg(pi, 0x4a4, (0x1 << 15), (1) << 15); + + mod_phy_reg(pi, 0x4d0, (0x1 << 5), (0) << 5); + + mod_phy_reg(pi, 0x4a4, (0x1ff << 0), (0) << 0); + + mod_phy_reg(pi, 0x4a5, (0xff << 0), (255) << 0); + + mod_phy_reg(pi, 0x4a5, (0x7 << 12), (5) << 12); + + mod_phy_reg(pi, 0x4a5, (0x7 << 8), (0) << 8); + + mod_phy_reg(pi, 0x40d, (0xff << 0), (64) << 0); + + mod_phy_reg(pi, 0x40d, (0x7 << 8), (4) << 8); + + mod_phy_reg(pi, 0x4a2, (0xff << 0), (64) << 0); + + mod_phy_reg(pi, 0x4a2, (0x7 << 8), (4) << 8); + + mod_phy_reg(pi, 0x4d0, (0x1ff << 6), (0) << 6); + + mod_phy_reg(pi, 0x4a8, (0xff << 0), (0x1) << 0); + + wlc_lcnphy_clear_tx_power_offsets(pi); + + mod_phy_reg(pi, 0x4a6, (0x1 << 15), (1) << 15); + + mod_phy_reg(pi, 0x4a6, (0x1ff << 0), (0xff) << 0); + + mod_phy_reg(pi, 0x49a, (0x1ff << 0), (0xff) << 0); + + if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { + mod_radio_reg(pi, RADIO_2064_REG028, 0xf, tssi_sel); + mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4); + } else { + mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, tssi_sel << 1); + mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1); + mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 1 << 3); + } + + write_radio_reg(pi, RADIO_2064_REG025, 0xc); + + if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { + mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1); + } else { + if (CHSPEC_IS2G(pi->radio_chanspec)) + mod_radio_reg(pi, RADIO_2064_REG03A, 0x2, 1 << 1); + else + mod_radio_reg(pi, RADIO_2064_REG03A, 0x2, 0 << 1); + } + + if (LCNREV_IS(pi->pubpi.phy_rev, 2)) + mod_radio_reg(pi, RADIO_2064_REG03A, 0x2, 1 << 1); + else + mod_radio_reg(pi, RADIO_2064_REG03A, 0x4, 1 << 2); + + mod_radio_reg(pi, RADIO_2064_REG11A, 0x1, 1 << 0); + + mod_radio_reg(pi, RADIO_2064_REG005, 0x8, 1 << 3); + + if (!wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) + mod_phy_reg(pi, 0x4d7, + (0x1 << 3) | (0x7 << 12), 0 << 3 | 2 << 12); + + rfseq = wlc_lcnphy_rfseq_tbl_adc_pwrup(pi); + tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; + tab.tbl_width = 16; + tab.tbl_ptr = &rfseq; + tab.tbl_len = 1; + tab.tbl_offset = 6; + wlc_lcnphy_write_table(pi, &tab); + + mod_phy_reg(pi, 0x938, (0x1 << 2), (1) << 2); + + mod_phy_reg(pi, 0x939, (0x1 << 2), (1) << 2); + + mod_phy_reg(pi, 0x4a4, (0x1 << 12), (1) << 12); + + mod_phy_reg(pi, 0x4d7, (0x1 << 2), (1) << 2); + + mod_phy_reg(pi, 0x4d7, (0xf << 8), (0) << 8); + + mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x0); + mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0); + mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8); + + wlc_lcnphy_pwrctrl_rssiparams(pi); +} + +void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi) +{ + u16 tx_cnt, tx_total, npt; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + tx_total = wlc_lcnphy_total_tx_frames(pi); + tx_cnt = tx_total - pi_lcn->lcnphy_tssi_tx_cnt; + npt = wlc_lcnphy_get_tx_pwr_npt(pi); + + if (tx_cnt > (1 << npt)) { + + pi_lcn->lcnphy_tssi_tx_cnt = tx_total; + + pi_lcn->lcnphy_tssi_idx = wlc_lcnphy_get_current_tx_pwr_idx(pi); + pi_lcn->lcnphy_tssi_npt = npt; + + } +} + +s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1) +{ + s32 a, b, p; + + a = 32768 + (a1 * tssi); + b = (1024 * b0) + (64 * b1 * tssi); + p = ((2 * b) + a) / (2 * a); + + return p; +} + +static void wlc_lcnphy_txpower_reset_npt(struct brcms_phy *pi) +{ + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) + return; + + pi_lcn->lcnphy_tssi_idx = LCNPHY_TX_PWR_CTRL_START_INDEX_2G_4313; + pi_lcn->lcnphy_tssi_npt = LCNPHY_TX_PWR_CTRL_START_NPT; +} + +void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi) +{ + struct phytbl_info tab; + u32 rate_table[BRCMS_NUM_RATES_CCK + BRCMS_NUM_RATES_OFDM + + BRCMS_NUM_RATES_MCS_1_STREAM]; + uint i, j; + if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) + return; + + for (i = 0, j = 0; i < ARRAY_SIZE(rate_table); i++, j++) { + + if (i == BRCMS_NUM_RATES_CCK + BRCMS_NUM_RATES_OFDM) + j = TXP_FIRST_MCS_20_SISO; + + rate_table[i] = (u32) ((s32) (-pi->tx_power_offset[j])); + } + + tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; + tab.tbl_width = 32; + tab.tbl_len = ARRAY_SIZE(rate_table); + tab.tbl_ptr = rate_table; + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; + wlc_lcnphy_write_table(pi, &tab); + + if (wlc_lcnphy_get_target_tx_pwr(pi) != pi->tx_power_min) { + wlc_lcnphy_set_target_tx_pwr(pi, pi->tx_power_min); + + wlc_lcnphy_txpower_reset_npt(pi); + } +} + +static void wlc_lcnphy_set_tx_pwr_soft_ctrl(struct brcms_phy *pi, s8 index) +{ + u32 cck_offset[4] = { 22, 22, 22, 22 }; + u32 ofdm_offset, reg_offset_cck; + int i; + u16 index2; + struct phytbl_info tab; + + if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) + return; + + mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0x1) << 14); + + mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0x0) << 14); + + or_phy_reg(pi, 0x6da, 0x0040); + + reg_offset_cck = 0; + for (i = 0; i < 4; i++) + cck_offset[i] -= reg_offset_cck; + tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; + tab.tbl_width = 32; + tab.tbl_len = 4; + tab.tbl_ptr = cck_offset; + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; + wlc_lcnphy_write_table(pi, &tab); + ofdm_offset = 0; + tab.tbl_len = 1; + tab.tbl_ptr = &ofdm_offset; + for (i = 836; i < 862; i++) { + tab.tbl_offset = i; + wlc_lcnphy_write_table(pi, &tab); + } + + mod_phy_reg(pi, 0x4a4, (0x1 << 15), (0x1) << 15); + + mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0x1) << 14); + + mod_phy_reg(pi, 0x4a4, (0x1 << 13), (0x1) << 13); + + mod_phy_reg(pi, 0x4b0, (0x1 << 7), (0) << 7); + + mod_phy_reg(pi, 0x43b, (0x1 << 6), (0) << 6); + + mod_phy_reg(pi, 0x4a9, (0x1 << 15), (1) << 15); + + index2 = (u16) (index * 2); + mod_phy_reg(pi, 0x4a9, (0x1ff << 0), (index2) << 0); + + mod_phy_reg(pi, 0x6a3, (0x1 << 4), (0) << 4); + +} + +static s8 wlc_lcnphy_tempcompensated_txpwrctrl(struct brcms_phy *pi) +{ + s8 index, delta_brd, delta_temp, new_index, tempcorrx; + s16 manp, meas_temp, temp_diff; + bool neg = false; + u16 temp; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) + return pi_lcn->lcnphy_current_index; + + index = FIXED_TXPWR; + + if (pi_lcn->lcnphy_tempsense_slope == 0) + return index; + + temp = (u16) wlc_lcnphy_tempsense(pi, 0); + meas_temp = LCNPHY_TEMPSENSE(temp); + + if (pi->tx_power_min != 0) + delta_brd = (pi_lcn->lcnphy_measPower - pi->tx_power_min); + else + delta_brd = 0; + + manp = LCNPHY_TEMPSENSE(pi_lcn->lcnphy_rawtempsense); + temp_diff = manp - meas_temp; + if (temp_diff < 0) { + neg = true; + temp_diff = -temp_diff; + } + + delta_temp = (s8) wlc_lcnphy_qdiv_roundup((u32) (temp_diff * 192), + (u32) (pi_lcn-> + lcnphy_tempsense_slope + * 10), 0); + if (neg) + delta_temp = -delta_temp; + + if (pi_lcn->lcnphy_tempsense_option == 3 + && LCNREV_IS(pi->pubpi.phy_rev, 0)) + delta_temp = 0; + if (pi_lcn->lcnphy_tempcorrx > 31) + tempcorrx = (s8) (pi_lcn->lcnphy_tempcorrx - 64); + else + tempcorrx = (s8) pi_lcn->lcnphy_tempcorrx; + if (LCNREV_IS(pi->pubpi.phy_rev, 1)) + tempcorrx = 4; + new_index = + index + delta_brd + delta_temp - pi_lcn->lcnphy_bandedge_corr; + new_index += tempcorrx; + + if (LCNREV_IS(pi->pubpi.phy_rev, 1)) + index = 127; + + if (new_index < 0 || new_index > 126) + return index; + + return new_index; +} + +static u16 wlc_lcnphy_set_tx_pwr_ctrl_mode(struct brcms_phy *pi, u16 mode) +{ + + u16 current_mode = mode; + if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) && + mode == LCNPHY_TX_PWR_CTRL_HW) + current_mode = LCNPHY_TX_PWR_CTRL_TEMPBASED; + if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi) && + mode == LCNPHY_TX_PWR_CTRL_TEMPBASED) + current_mode = LCNPHY_TX_PWR_CTRL_HW; + return current_mode; +} + +void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode) +{ + u16 old_mode = wlc_lcnphy_get_tx_pwr_ctrl(pi); + s8 index; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + mode = wlc_lcnphy_set_tx_pwr_ctrl_mode(pi, mode); + old_mode = wlc_lcnphy_set_tx_pwr_ctrl_mode(pi, old_mode); + + mod_phy_reg(pi, 0x6da, (0x1 << 6), + ((LCNPHY_TX_PWR_CTRL_HW == mode) ? 1 : 0) << 6); + + mod_phy_reg(pi, 0x6a3, (0x1 << 4), + ((LCNPHY_TX_PWR_CTRL_HW == mode) ? 0 : 1) << 4); + + if (old_mode != mode) { + if (LCNPHY_TX_PWR_CTRL_HW == old_mode) { + + wlc_lcnphy_tx_pwr_update_npt(pi); + + wlc_lcnphy_clear_tx_power_offsets(pi); + } + if (LCNPHY_TX_PWR_CTRL_HW == mode) { + + wlc_lcnphy_txpower_recalc_target(pi); + + wlc_lcnphy_set_start_tx_pwr_idx(pi, + pi_lcn-> + lcnphy_tssi_idx); + wlc_lcnphy_set_tx_pwr_npt(pi, pi_lcn->lcnphy_tssi_npt); + mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 0); + + pi_lcn->lcnphy_tssi_tx_cnt = + wlc_lcnphy_total_tx_frames(pi); + + wlc_lcnphy_disable_tx_gain_override(pi); + pi_lcn->lcnphy_tx_power_idx_override = -1; + } else + wlc_lcnphy_enable_tx_gain_override(pi); + + mod_phy_reg(pi, 0x4a4, + ((0x1 << 15) | (0x1 << 14) | (0x1 << 13)), mode); + if (mode == LCNPHY_TX_PWR_CTRL_TEMPBASED) { + index = wlc_lcnphy_tempcompensated_txpwrctrl(pi); + wlc_lcnphy_set_tx_pwr_soft_ctrl(pi, index); + pi_lcn->lcnphy_current_index = (s8) + ((read_phy_reg(pi, + 0x4a9) & + 0xFF) / 2); + } + } +} + +static void +wlc_lcnphy_tx_iqlo_loopback(struct brcms_phy *pi, u16 *values_to_save) +{ + u16 vmid; + int i; + for (i = 0; i < 20; i++) + values_to_save[i] = + read_radio_reg(pi, iqlo_loopback_rf_regs[i]); + + mod_phy_reg(pi, 0x44c, (0x1 << 12), 1 << 12); + mod_phy_reg(pi, 0x44d, (0x1 << 14), 1 << 14); + + mod_phy_reg(pi, 0x44c, (0x1 << 11), 1 << 11); + mod_phy_reg(pi, 0x44d, (0x1 << 13), 0 << 13); + + mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1); + mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1); + + mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0); + + if (LCNREV_IS(pi->pubpi.phy_rev, 2)) + and_radio_reg(pi, RADIO_2064_REG03A, 0xFD); + else + and_radio_reg(pi, RADIO_2064_REG03A, 0xF9); + or_radio_reg(pi, RADIO_2064_REG11A, 0x1); + + or_radio_reg(pi, RADIO_2064_REG036, 0x01); + or_radio_reg(pi, RADIO_2064_REG11A, 0x18); + udelay(20); + + if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { + if (CHSPEC_IS5G(pi->radio_chanspec)) + mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0); + else + or_radio_reg(pi, RADIO_2064_REG03A, 1); + } else { + if (CHSPEC_IS5G(pi->radio_chanspec)) + mod_radio_reg(pi, RADIO_2064_REG03A, 3, 1); + else + or_radio_reg(pi, RADIO_2064_REG03A, 0x3); + } + + udelay(20); + + write_radio_reg(pi, RADIO_2064_REG025, 0xF); + if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { + if (CHSPEC_IS5G(pi->radio_chanspec)) + mod_radio_reg(pi, RADIO_2064_REG028, 0xF, 0x4); + else + mod_radio_reg(pi, RADIO_2064_REG028, 0xF, 0x6); + } else { + if (CHSPEC_IS5G(pi->radio_chanspec)) + mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0x4 << 1); + else + mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0x6 << 1); + } + + udelay(20); + + write_radio_reg(pi, RADIO_2064_REG005, 0x8); + or_radio_reg(pi, RADIO_2064_REG112, 0x80); + udelay(20); + + or_radio_reg(pi, RADIO_2064_REG0FF, 0x10); + or_radio_reg(pi, RADIO_2064_REG11F, 0x44); + udelay(20); + + or_radio_reg(pi, RADIO_2064_REG00B, 0x7); + or_radio_reg(pi, RADIO_2064_REG113, 0x10); + udelay(20); + + write_radio_reg(pi, RADIO_2064_REG007, 0x1); + udelay(20); + + vmid = 0x2A6; + mod_radio_reg(pi, RADIO_2064_REG0FC, 0x3 << 0, (vmid >> 8) & 0x3); + write_radio_reg(pi, RADIO_2064_REG0FD, (vmid & 0xff)); + or_radio_reg(pi, RADIO_2064_REG11F, 0x44); + udelay(20); + + or_radio_reg(pi, RADIO_2064_REG0FF, 0x10); + udelay(20); + write_radio_reg(pi, RADIO_2064_REG012, 0x02); + or_radio_reg(pi, RADIO_2064_REG112, 0x06); + write_radio_reg(pi, RADIO_2064_REG036, 0x11); + write_radio_reg(pi, RADIO_2064_REG059, 0xcc); + write_radio_reg(pi, RADIO_2064_REG05C, 0x2e); + write_radio_reg(pi, RADIO_2064_REG078, 0xd7); + write_radio_reg(pi, RADIO_2064_REG092, 0x15); +} + +static bool wlc_lcnphy_iqcal_wait(struct brcms_phy *pi) +{ + uint delay_count = 0; + + while (wlc_lcnphy_iqcal_active(pi)) { + udelay(100); + delay_count++; + + if (delay_count > (10 * 500)) + break; + } + + return (0 == wlc_lcnphy_iqcal_active(pi)); +} + +static void +wlc_lcnphy_tx_iqlo_loopback_cleanup(struct brcms_phy *pi, u16 *values_to_save) +{ + int i; + + and_phy_reg(pi, 0x44c, 0x0 >> 11); + + and_phy_reg(pi, 0x43b, 0xC); + + for (i = 0; i < 20; i++) + write_radio_reg(pi, iqlo_loopback_rf_regs[i], + values_to_save[i]); +} + +static void +wlc_lcnphy_tx_iqlo_cal(struct brcms_phy *pi, + struct lcnphy_txgains *target_gains, + enum lcnphy_cal_mode cal_mode, bool keep_tone) +{ + + struct lcnphy_txgains cal_gains, temp_gains; + u16 hash; + u8 band_idx; + int j; + u16 ncorr_override[5]; + u16 syst_coeffs[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; + + u16 commands_fullcal[] = { + 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234 + }; + + u16 commands_recal[] = { + 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234 + }; + + u16 command_nums_fullcal[] = { + 0x7a97, 0x7a97, 0x7a97, 0x7a87, 0x7a87, 0x7b97 + }; + + u16 command_nums_recal[] = { + 0x7a97, 0x7a97, 0x7a97, 0x7a87, 0x7a87, 0x7b97 + }; + u16 *command_nums = command_nums_fullcal; + + u16 *start_coeffs = NULL, *cal_cmds = NULL, cal_type, diq_start; + u16 tx_pwr_ctrl_old, save_txpwrctrlrfctrl2; + u16 save_sslpnCalibClkEnCtrl, save_sslpnRxFeClkEnCtrl; + bool tx_gain_override_old; + struct lcnphy_txgains old_gains; + uint i, n_cal_cmds = 0, n_cal_start = 0; + u16 *values_to_save; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + values_to_save = kmalloc(sizeof(u16) * 20, GFP_ATOMIC); + if (NULL == values_to_save) + return; + + save_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db); + save_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); + + or_phy_reg(pi, 0x6da, 0x40); + or_phy_reg(pi, 0x6db, 0x3); + + switch (cal_mode) { + case LCNPHY_CAL_FULL: + start_coeffs = syst_coeffs; + cal_cmds = commands_fullcal; + n_cal_cmds = ARRAY_SIZE(commands_fullcal); + break; + + case LCNPHY_CAL_RECAL: + start_coeffs = syst_coeffs; + cal_cmds = commands_recal; + n_cal_cmds = ARRAY_SIZE(commands_recal); + command_nums = command_nums_recal; + break; + + default: + break; + } + + wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, + start_coeffs, 11, 16, 64); + + write_phy_reg(pi, 0x6da, 0xffff); + mod_phy_reg(pi, 0x503, (0x1 << 3), (1) << 3); + + tx_pwr_ctrl_old = wlc_lcnphy_get_tx_pwr_ctrl(pi); + + mod_phy_reg(pi, 0x4a4, (0x1 << 12), (1) << 12); + + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); + + save_txpwrctrlrfctrl2 = read_phy_reg(pi, 0x4db); + + mod_phy_reg(pi, 0x4db, (0x3ff << 0), (0x2a6) << 0); + + mod_phy_reg(pi, 0x4db, (0x7 << 12), (2) << 12); + + wlc_lcnphy_tx_iqlo_loopback(pi, values_to_save); + + tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi); + if (tx_gain_override_old) + wlc_lcnphy_get_tx_gain(pi, &old_gains); + + if (!target_gains) { + if (!tx_gain_override_old) + wlc_lcnphy_set_tx_pwr_by_index(pi, + pi_lcn->lcnphy_tssi_idx); + wlc_lcnphy_get_tx_gain(pi, &temp_gains); + target_gains = &temp_gains; + } + + hash = (target_gains->gm_gain << 8) | + (target_gains->pga_gain << 4) | (target_gains->pad_gain); + + band_idx = (CHSPEC_IS5G(pi->radio_chanspec) ? 1 : 0); + + cal_gains = *target_gains; + memset(ncorr_override, 0, sizeof(ncorr_override)); + for (j = 0; j < iqcal_gainparams_numgains_lcnphy[band_idx]; j++) { + if (hash == tbl_iqcal_gainparams_lcnphy[band_idx][j][0]) { + cal_gains.gm_gain = + tbl_iqcal_gainparams_lcnphy[band_idx][j][1]; + cal_gains.pga_gain = + tbl_iqcal_gainparams_lcnphy[band_idx][j][2]; + cal_gains.pad_gain = + tbl_iqcal_gainparams_lcnphy[band_idx][j][3]; + memcpy(ncorr_override, + &tbl_iqcal_gainparams_lcnphy[band_idx][j][3], + sizeof(ncorr_override)); + break; + } + } + + wlc_lcnphy_set_tx_gain(pi, &cal_gains); + + write_phy_reg(pi, 0x453, 0xaa9); + write_phy_reg(pi, 0x93d, 0xc0); + + wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, + lcnphy_iqcal_loft_gainladder, + ARRAY_SIZE(lcnphy_iqcal_loft_gainladder), + 16, 0); + + wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, + lcnphy_iqcal_ir_gainladder, + ARRAY_SIZE( + lcnphy_iqcal_ir_gainladder), 16, + 32); + + if (pi->phy_tx_tone_freq) { + + wlc_lcnphy_stop_tx_tone(pi); + udelay(5); + wlc_lcnphy_start_tx_tone(pi, 3750, 88, 1); + } else { + wlc_lcnphy_start_tx_tone(pi, 3750, 88, 1); + } + + write_phy_reg(pi, 0x6da, 0xffff); + + for (i = n_cal_start; i < n_cal_cmds; i++) { + u16 zero_diq = 0; + u16 best_coeffs[11]; + u16 command_num; + + cal_type = (cal_cmds[i] & 0x0f00) >> 8; + + command_num = command_nums[i]; + if (ncorr_override[cal_type]) + command_num = + ncorr_override[cal_type] << 8 | (command_num & + 0xff); + + write_phy_reg(pi, 0x452, command_num); + + if ((cal_type == 3) || (cal_type == 4)) { + wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, + &diq_start, 1, 16, 69); + + wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, + &zero_diq, 1, 16, 69); + } + + write_phy_reg(pi, 0x451, cal_cmds[i]); + + if (!wlc_lcnphy_iqcal_wait(pi)) + goto cleanup; + + wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, + best_coeffs, + ARRAY_SIZE(best_coeffs), 16, 96); + wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, + best_coeffs, + ARRAY_SIZE(best_coeffs), 16, 64); + + if ((cal_type == 3) || (cal_type == 4)) + wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, + &diq_start, 1, 16, 69); + wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, + pi_lcn->lcnphy_cal_results. + txiqlocal_bestcoeffs, + ARRAY_SIZE(pi_lcn-> + lcnphy_cal_results. + txiqlocal_bestcoeffs), + 16, 96); + } + + wlc_lcnphy_common_read_table(pi, LCNPHY_TBL_ID_IQLOCAL, + pi_lcn->lcnphy_cal_results. + txiqlocal_bestcoeffs, + ARRAY_SIZE(pi_lcn->lcnphy_cal_results. + txiqlocal_bestcoeffs), 16, 96); + pi_lcn->lcnphy_cal_results.txiqlocal_bestcoeffs_valid = true; + + wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, + &pi_lcn->lcnphy_cal_results. + txiqlocal_bestcoeffs[0], 4, 16, 80); + + wlc_lcnphy_common_write_table(pi, LCNPHY_TBL_ID_IQLOCAL, + &pi_lcn->lcnphy_cal_results. + txiqlocal_bestcoeffs[5], 2, 16, 85); + +cleanup: + wlc_lcnphy_tx_iqlo_loopback_cleanup(pi, values_to_save); + kfree(values_to_save); + + if (!keep_tone) + wlc_lcnphy_stop_tx_tone(pi); + + write_phy_reg(pi, 0x4db, save_txpwrctrlrfctrl2); + + write_phy_reg(pi, 0x453, 0); + + if (tx_gain_override_old) + wlc_lcnphy_set_tx_gain(pi, &old_gains); + wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl_old); + + write_phy_reg(pi, 0x6da, save_sslpnCalibClkEnCtrl); + write_phy_reg(pi, 0x6db, save_sslpnRxFeClkEnCtrl); + +} + +static void wlc_lcnphy_idle_tssi_est(struct brcms_phy_pub *ppi) +{ + bool suspend, tx_gain_override_old; + struct lcnphy_txgains old_gains; + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + u16 idleTssi, idleTssi0_2C, idleTssi0_OB, idleTssi0_regvalue_OB, + idleTssi0_regvalue_2C; + u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); + u16 SAVE_lpfgain = read_radio_reg(pi, RADIO_2064_REG112); + u16 SAVE_jtag_bb_afe_switch = + read_radio_reg(pi, RADIO_2064_REG007) & 1; + u16 SAVE_jtag_auxpga = read_radio_reg(pi, RADIO_2064_REG0FF) & 0x10; + u16 SAVE_iqadc_aux_en = read_radio_reg(pi, RADIO_2064_REG11F) & 4; + u8 SAVE_bbmult = wlc_lcnphy_get_bbmult(pi); + + idleTssi = read_phy_reg(pi, 0x4ab); + suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!suspend) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); + + tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi); + wlc_lcnphy_get_tx_gain(pi, &old_gains); + + wlc_lcnphy_enable_tx_gain_override(pi); + wlc_lcnphy_set_tx_pwr_by_index(pi, 127); + write_radio_reg(pi, RADIO_2064_REG112, 0x6); + mod_radio_reg(pi, RADIO_2064_REG007, 0x1, 1); + mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 1 << 4); + mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 1 << 2); + wlc_lcnphy_tssi_setup(pi); + + mod_phy_reg(pi, 0x4d7, (0x1 << 0), (1 << 0)); + mod_phy_reg(pi, 0x4d7, (0x1 << 6), (1 << 6)); + + wlc_lcnphy_set_bbmult(pi, 0x0); + + wlc_phy_do_dummy_tx(pi, true, OFF); + idleTssi = ((read_phy_reg(pi, 0x4ab) & (0x1ff << 0)) + >> 0); + + idleTssi0_2C = ((read_phy_reg(pi, 0x63e) & (0x1ff << 0)) + >> 0); + + if (idleTssi0_2C >= 256) + idleTssi0_OB = idleTssi0_2C - 256; + else + idleTssi0_OB = idleTssi0_2C + 256; + + idleTssi0_regvalue_OB = idleTssi0_OB; + if (idleTssi0_regvalue_OB >= 256) + idleTssi0_regvalue_2C = idleTssi0_regvalue_OB - 256; + else + idleTssi0_regvalue_2C = idleTssi0_regvalue_OB + 256; + mod_phy_reg(pi, 0x4a6, (0x1ff << 0), (idleTssi0_regvalue_2C) << 0); + + mod_phy_reg(pi, 0x44c, (0x1 << 12), (0) << 12); + + wlc_lcnphy_set_bbmult(pi, SAVE_bbmult); + wlc_lcnphy_set_tx_gain_override(pi, tx_gain_override_old); + wlc_lcnphy_set_tx_gain(pi, &old_gains); + wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl); + + write_radio_reg(pi, RADIO_2064_REG112, SAVE_lpfgain); + mod_radio_reg(pi, RADIO_2064_REG007, 0x1, SAVE_jtag_bb_afe_switch); + mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, SAVE_jtag_auxpga); + mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, SAVE_iqadc_aux_en); + mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 1 << 7); + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); +} + +static void wlc_lcnphy_vbat_temp_sense_setup(struct brcms_phy *pi, u8 mode) +{ + bool suspend; + u16 save_txpwrCtrlEn; + u8 auxpga_vmidcourse, auxpga_vmidfine, auxpga_gain; + u16 auxpga_vmid; + struct phytbl_info tab; + u32 val; + u8 save_reg007, save_reg0FF, save_reg11F, save_reg005, save_reg025, + save_reg112; + u16 values_to_save[14]; + s8 index; + int i; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + udelay(999); + + save_reg007 = (u8) read_radio_reg(pi, RADIO_2064_REG007); + save_reg0FF = (u8) read_radio_reg(pi, RADIO_2064_REG0FF); + save_reg11F = (u8) read_radio_reg(pi, RADIO_2064_REG11F); + save_reg005 = (u8) read_radio_reg(pi, RADIO_2064_REG005); + save_reg025 = (u8) read_radio_reg(pi, RADIO_2064_REG025); + save_reg112 = (u8) read_radio_reg(pi, RADIO_2064_REG112); + + for (i = 0; i < 14; i++) + values_to_save[i] = read_phy_reg(pi, tempsense_phy_regs[i]); + suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!suspend) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + save_txpwrCtrlEn = read_radio_reg(pi, 0x4a4); + + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); + index = pi_lcn->lcnphy_current_index; + wlc_lcnphy_set_tx_pwr_by_index(pi, 127); + mod_radio_reg(pi, RADIO_2064_REG007, 0x1, 0x1); + mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 0x1 << 4); + mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 0x1 << 2); + mod_phy_reg(pi, 0x503, (0x1 << 0), (0) << 0); + + mod_phy_reg(pi, 0x503, (0x1 << 2), (0) << 2); + + mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14); + + mod_phy_reg(pi, 0x4a4, (0x1 << 15), (0) << 15); + + mod_phy_reg(pi, 0x4d0, (0x1 << 5), (0) << 5); + + mod_phy_reg(pi, 0x4a5, (0xff << 0), (255) << 0); + + mod_phy_reg(pi, 0x4a5, (0x7 << 12), (5) << 12); + + mod_phy_reg(pi, 0x4a5, (0x7 << 8), (0) << 8); + + mod_phy_reg(pi, 0x40d, (0xff << 0), (64) << 0); + + mod_phy_reg(pi, 0x40d, (0x7 << 8), (6) << 8); + + mod_phy_reg(pi, 0x4a2, (0xff << 0), (64) << 0); + + mod_phy_reg(pi, 0x4a2, (0x7 << 8), (6) << 8); + + mod_phy_reg(pi, 0x4d9, (0x7 << 4), (2) << 4); + + mod_phy_reg(pi, 0x4d9, (0x7 << 8), (3) << 8); + + mod_phy_reg(pi, 0x4d9, (0x7 << 12), (1) << 12); + + mod_phy_reg(pi, 0x4da, (0x1 << 12), (0) << 12); + + mod_phy_reg(pi, 0x4da, (0x1 << 13), (1) << 13); + + mod_phy_reg(pi, 0x4a6, (0x1 << 15), (1) << 15); + + write_radio_reg(pi, RADIO_2064_REG025, 0xC); + + mod_radio_reg(pi, RADIO_2064_REG005, 0x8, 0x1 << 3); + + mod_phy_reg(pi, 0x938, (0x1 << 2), (1) << 2); + + mod_phy_reg(pi, 0x939, (0x1 << 2), (1) << 2); + + mod_phy_reg(pi, 0x4a4, (0x1 << 12), (1) << 12); + + val = wlc_lcnphy_rfseq_tbl_adc_pwrup(pi); + tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; + tab.tbl_width = 16; + tab.tbl_len = 1; + tab.tbl_ptr = &val; + tab.tbl_offset = 6; + wlc_lcnphy_write_table(pi, &tab); + if (mode == TEMPSENSE) { + mod_phy_reg(pi, 0x4d7, (0x1 << 3), (1) << 3); + + mod_phy_reg(pi, 0x4d7, (0x7 << 12), (1) << 12); + + auxpga_vmidcourse = 8; + auxpga_vmidfine = 0x4; + auxpga_gain = 2; + mod_radio_reg(pi, RADIO_2064_REG082, 0x20, 1 << 5); + } else { + mod_phy_reg(pi, 0x4d7, (0x1 << 3), (1) << 3); + + mod_phy_reg(pi, 0x4d7, (0x7 << 12), (3) << 12); + + auxpga_vmidcourse = 7; + auxpga_vmidfine = 0xa; + auxpga_gain = 2; + } + auxpga_vmid = + (u16) ((2 << 8) | (auxpga_vmidcourse << 4) | auxpga_vmidfine); + mod_phy_reg(pi, 0x4d8, (0x1 << 0), (1) << 0); + + mod_phy_reg(pi, 0x4d8, (0x3ff << 2), (auxpga_vmid) << 2); + + mod_phy_reg(pi, 0x4d8, (0x1 << 1), (1) << 1); + + mod_phy_reg(pi, 0x4d8, (0x7 << 12), (auxpga_gain) << 12); + + mod_phy_reg(pi, 0x4d0, (0x1 << 5), (1) << 5); + + write_radio_reg(pi, RADIO_2064_REG112, 0x6); + + wlc_phy_do_dummy_tx(pi, true, OFF); + if (!tempsense_done(pi)) + udelay(10); + + write_radio_reg(pi, RADIO_2064_REG007, (u16) save_reg007); + write_radio_reg(pi, RADIO_2064_REG0FF, (u16) save_reg0FF); + write_radio_reg(pi, RADIO_2064_REG11F, (u16) save_reg11F); + write_radio_reg(pi, RADIO_2064_REG005, (u16) save_reg005); + write_radio_reg(pi, RADIO_2064_REG025, (u16) save_reg025); + write_radio_reg(pi, RADIO_2064_REG112, (u16) save_reg112); + for (i = 0; i < 14; i++) + write_phy_reg(pi, tempsense_phy_regs[i], values_to_save[i]); + wlc_lcnphy_set_tx_pwr_by_index(pi, (int)index); + + write_radio_reg(pi, 0x4a4, save_txpwrCtrlEn); + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); + udelay(999); +} + +static void wlc_lcnphy_tx_pwr_ctrl_init(struct brcms_phy_pub *ppi) +{ + struct lcnphy_txgains tx_gains; + u8 bbmult; + struct phytbl_info tab; + s32 a1, b0, b1; + s32 tssi, pwr, maxtargetpwr, mintargetpwr; + bool suspend; + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + + suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!suspend) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + + if (!pi->hwpwrctrl_capable) { + if (CHSPEC_IS2G(pi->radio_chanspec)) { + tx_gains.gm_gain = 4; + tx_gains.pga_gain = 12; + tx_gains.pad_gain = 12; + tx_gains.dac_gain = 0; + + bbmult = 150; + } else { + tx_gains.gm_gain = 7; + tx_gains.pga_gain = 15; + tx_gains.pad_gain = 14; + tx_gains.dac_gain = 0; + + bbmult = 150; + } + wlc_lcnphy_set_tx_gain(pi, &tx_gains); + wlc_lcnphy_set_bbmult(pi, bbmult); + wlc_lcnphy_vbat_temp_sense_setup(pi, TEMPSENSE); + } else { + + wlc_lcnphy_idle_tssi_est(ppi); + + wlc_lcnphy_clear_tx_power_offsets(pi); + + b0 = pi->txpa_2g[0]; + b1 = pi->txpa_2g[1]; + a1 = pi->txpa_2g[2]; + maxtargetpwr = wlc_lcnphy_tssi2dbm(10, a1, b0, b1); + mintargetpwr = wlc_lcnphy_tssi2dbm(125, a1, b0, b1); + + tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; + tab.tbl_width = 32; + tab.tbl_ptr = &pwr; + tab.tbl_len = 1; + tab.tbl_offset = 0; + for (tssi = 0; tssi < 128; tssi++) { + pwr = wlc_lcnphy_tssi2dbm(tssi, a1, b0, b1); + + pwr = (pwr < mintargetpwr) ? mintargetpwr : pwr; + wlc_lcnphy_write_table(pi, &tab); + tab.tbl_offset++; + } + mod_phy_reg(pi, 0x4d0, (0x1 << 0), (0) << 0); + mod_phy_reg(pi, 0x4d3, (0xff << 0), (0) << 0); + mod_phy_reg(pi, 0x4d3, (0xff << 8), (0) << 8); + mod_phy_reg(pi, 0x4d0, (0x1 << 4), (0) << 4); + mod_phy_reg(pi, 0x4d0, (0x1 << 2), (0) << 2); + + mod_phy_reg(pi, 0x410, (0x1 << 7), (0) << 7); + + write_phy_reg(pi, 0x4a8, 10); + + wlc_lcnphy_set_target_tx_pwr(pi, LCN_TARGET_PWR); + + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_HW); + } + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); +} + +static void wlc_lcnphy_set_pa_gain(struct brcms_phy *pi, u16 gain) +{ + mod_phy_reg(pi, 0x4fb, + LCNPHY_txgainctrlovrval1_pagain_ovr_val1_MASK, + gain << LCNPHY_txgainctrlovrval1_pagain_ovr_val1_SHIFT); + mod_phy_reg(pi, 0x4fd, + LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_MASK, + gain << LCNPHY_stxtxgainctrlovrval1_pagain_ovr_val1_SHIFT); +} + +void +wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, + u8 *ei0, u8 *eq0, u8 *fi0, u8 *fq0) +{ + *ei0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG089)); + *eq0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG08A)); + *fi0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG08B)); + *fq0 = LCNPHY_IQLOCC_READ(read_radio_reg(pi, RADIO_2064_REG08C)); +} + +void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b) +{ + struct phytbl_info tab; + u16 iqcc[2]; + + iqcc[0] = a; + iqcc[1] = b; + + tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; + tab.tbl_width = 16; + tab.tbl_ptr = iqcc; + tab.tbl_len = 2; + tab.tbl_offset = 80; + wlc_lcnphy_write_table(pi, &tab); +} + +void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq) +{ + struct phytbl_info tab; + + tab.tbl_id = LCNPHY_TBL_ID_IQLOCAL; + tab.tbl_width = 16; + tab.tbl_ptr = &didq; + tab.tbl_len = 1; + tab.tbl_offset = 85; + wlc_lcnphy_write_table(pi, &tab); +} + +void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index) +{ + struct phytbl_info tab; + u16 a, b; + u8 bb_mult; + u32 bbmultiqcomp, txgain, locoeffs, rfpower; + struct lcnphy_txgains gains; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + pi_lcn->lcnphy_tx_power_idx_override = (s8) index; + pi_lcn->lcnphy_current_index = (u8) index; + + tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; + tab.tbl_width = 32; + tab.tbl_len = 1; + + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); + + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + index; + tab.tbl_ptr = &bbmultiqcomp; + wlc_lcnphy_read_table(pi, &tab); + + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + index; + tab.tbl_width = 32; + tab.tbl_ptr = &txgain; + wlc_lcnphy_read_table(pi, &tab); + + gains.gm_gain = (u16) (txgain & 0xff); + gains.pga_gain = (u16) (txgain >> 8) & 0xff; + gains.pad_gain = (u16) (txgain >> 16) & 0xff; + gains.dac_gain = (u16) (bbmultiqcomp >> 28) & 0x07; + wlc_lcnphy_set_tx_gain(pi, &gains); + wlc_lcnphy_set_pa_gain(pi, (u16) (txgain >> 24) & 0x7f); + + bb_mult = (u8) ((bbmultiqcomp >> 20) & 0xff); + wlc_lcnphy_set_bbmult(pi, bb_mult); + + wlc_lcnphy_enable_tx_gain_override(pi); + + if (!wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { + + a = (u16) ((bbmultiqcomp >> 10) & 0x3ff); + b = (u16) (bbmultiqcomp & 0x3ff); + wlc_lcnphy_set_tx_iqcc(pi, a, b); + + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_LO_OFFSET + index; + tab.tbl_ptr = &locoeffs; + wlc_lcnphy_read_table(pi, &tab); + + wlc_lcnphy_set_tx_locc(pi, (u16) locoeffs); + + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_PWR_OFFSET + index; + tab.tbl_ptr = &rfpower; + wlc_lcnphy_read_table(pi, &tab); + mod_phy_reg(pi, 0x6a6, (0x1fff << 0), (rfpower * 8) << 0); + + } +} + +static void wlc_lcnphy_clear_papd_comptable(struct brcms_phy *pi) +{ + u32 j; + struct phytbl_info tab; + u32 temp_offset[128]; + tab.tbl_ptr = temp_offset; + tab.tbl_len = 128; + tab.tbl_id = LCNPHY_TBL_ID_PAPDCOMPDELTATBL; + tab.tbl_width = 32; + tab.tbl_offset = 0; + + memset(temp_offset, 0, sizeof(temp_offset)); + for (j = 1; j < 128; j += 2) + temp_offset[j] = 0x80000; + + wlc_lcnphy_write_table(pi, &tab); + return; +} + +void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable) +{ + if (!bEnable) { + + and_phy_reg(pi, 0x43b, ~(u16) ((0x1 << 1) | (0x1 << 4))); + + mod_phy_reg(pi, 0x43c, (0x1 << 1), 1 << 1); + + and_phy_reg(pi, 0x44c, + ~(u16) ((0x1 << 3) | + (0x1 << 5) | + (0x1 << 12) | + (0x1 << 0) | (0x1 << 1) | (0x1 << 2))); + + and_phy_reg(pi, 0x44d, + ~(u16) ((0x1 << 3) | (0x1 << 5) | (0x1 << 14))); + mod_phy_reg(pi, 0x44d, (0x1 << 2), 1 << 2); + + mod_phy_reg(pi, 0x44d, (0x1 << 1) | (0x1 << 0), (0x1 << 0)); + + and_phy_reg(pi, 0x4f9, + ~(u16) ((0x1 << 0) | (0x1 << 1) | (0x1 << 2))); + + and_phy_reg(pi, 0x4fa, + ~(u16) ((0x1 << 0) | (0x1 << 1) | (0x1 << 2))); + } else { + + mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1); + mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1); + + mod_phy_reg(pi, 0x43b, (0x1 << 4), 1 << 4); + mod_phy_reg(pi, 0x43c, (0x1 << 6), 0 << 6); + + mod_phy_reg(pi, 0x44c, (0x1 << 12), 1 << 12); + mod_phy_reg(pi, 0x44d, (0x1 << 14), 1 << 14); + + wlc_lcnphy_set_trsw_override(pi, true, false); + + mod_phy_reg(pi, 0x44d, (0x1 << 2), 0 << 2); + mod_phy_reg(pi, 0x44c, (0x1 << 2), 1 << 2); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + + mod_phy_reg(pi, 0x44c, (0x1 << 3), 1 << 3); + mod_phy_reg(pi, 0x44d, (0x1 << 3), 1 << 3); + + mod_phy_reg(pi, 0x44c, (0x1 << 5), 1 << 5); + mod_phy_reg(pi, 0x44d, (0x1 << 5), 0 << 5); + + mod_phy_reg(pi, 0x4f9, (0x1 << 1), 1 << 1); + mod_phy_reg(pi, 0x4fa, (0x1 << 1), 1 << 1); + + mod_phy_reg(pi, 0x4f9, (0x1 << 2), 1 << 2); + mod_phy_reg(pi, 0x4fa, (0x1 << 2), 1 << 2); + + mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0x4fa, (0x1 << 0), 1 << 0); + } else { + + mod_phy_reg(pi, 0x44c, (0x1 << 3), 1 << 3); + mod_phy_reg(pi, 0x44d, (0x1 << 3), 0 << 3); + + mod_phy_reg(pi, 0x44c, (0x1 << 5), 1 << 5); + mod_phy_reg(pi, 0x44d, (0x1 << 5), 1 << 5); + + mod_phy_reg(pi, 0x4f9, (0x1 << 1), 1 << 1); + mod_phy_reg(pi, 0x4fa, (0x1 << 1), 0 << 1); + + mod_phy_reg(pi, 0x4f9, (0x1 << 2), 1 << 2); + mod_phy_reg(pi, 0x4fa, (0x1 << 2), 0 << 2); + + mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0); + } + } +} + +static void +wlc_lcnphy_run_samples(struct brcms_phy *pi, + u16 num_samps, + u16 num_loops, u16 wait, bool iqcalmode) +{ + + or_phy_reg(pi, 0x6da, 0x8080); + + mod_phy_reg(pi, 0x642, (0x7f << 0), (num_samps - 1) << 0); + if (num_loops != 0xffff) + num_loops--; + mod_phy_reg(pi, 0x640, (0xffff << 0), num_loops << 0); + + mod_phy_reg(pi, 0x641, (0xffff << 0), wait << 0); + + if (iqcalmode) { + + and_phy_reg(pi, 0x453, (u16) ~(0x1 << 15)); + or_phy_reg(pi, 0x453, (0x1 << 15)); + } else { + write_phy_reg(pi, 0x63f, 1); + wlc_lcnphy_tx_pu(pi, 1); + } + + or_radio_reg(pi, RADIO_2064_REG112, 0x6); +} + +void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode) +{ + + u8 phybw40; + phybw40 = CHSPEC_IS40(pi->radio_chanspec); + + if (LCNREV_LT(pi->pubpi.phy_rev, 2)) { + mod_phy_reg(pi, 0x4b0, (0x1 << 5), (mode) << 5); + mod_phy_reg(pi, 0x4b1, (0x1 << 9), 0 << 9); + } else { + mod_phy_reg(pi, 0x4b0, (0x1 << 5), (mode) << 5); + mod_phy_reg(pi, 0x4b1, (0x1 << 9), 0 << 9); + } + + if (phybw40 == 0) { + mod_phy_reg((pi), 0x410, + (0x1 << 6) | + (0x1 << 5), + ((CHSPEC_IS2G( + pi->radio_chanspec)) ? (!mode) : 0) << + 6 | (!mode) << 5); + mod_phy_reg(pi, 0x410, (0x1 << 7), (mode) << 7); + } +} + +void +wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val, + bool iqcalmode) +{ + u8 phy_bw; + u16 num_samps, t, k; + u32 bw; + s32 theta = 0, rot = 0; + struct cordic_iq tone_samp; + u32 data_buf[64]; + u16 i_samp, q_samp; + struct phytbl_info tab; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + pi->phy_tx_tone_freq = f_kHz; + + wlc_lcnphy_deaf_mode(pi, true); + + phy_bw = 40; + if (pi_lcn->lcnphy_spurmod) { + write_phy_reg(pi, 0x942, 0x2); + write_phy_reg(pi, 0x93b, 0x0); + write_phy_reg(pi, 0x93c, 0x0); + wlc_lcnphy_txrx_spur_avoidance_mode(pi, false); + } + + if (f_kHz) { + k = 1; + do { + bw = phy_bw * 1000 * k; + num_samps = bw / abs(f_kHz); + k++; + } while ((num_samps * (u32) (abs(f_kHz))) != bw); + } else + num_samps = 2; + + rot = ((f_kHz * 36) / phy_bw) / 100; + theta = 0; + + for (t = 0; t < num_samps; t++) { + + tone_samp = cordic_calc_iq(theta); + + theta += rot; + + i_samp = (u16) (FLOAT(tone_samp.i * max_val) & 0x3ff); + q_samp = (u16) (FLOAT(tone_samp.q * max_val) & 0x3ff); + data_buf[t] = (i_samp << 10) | q_samp; + } + + mod_phy_reg(pi, 0x6d6, (0x3 << 0), 0 << 0); + + mod_phy_reg(pi, 0x6da, (0x1 << 3), 1 << 3); + + tab.tbl_ptr = data_buf; + tab.tbl_len = num_samps; + tab.tbl_id = LCNPHY_TBL_ID_SAMPLEPLAY; + tab.tbl_offset = 0; + tab.tbl_width = 32; + wlc_lcnphy_write_table(pi, &tab); + + wlc_lcnphy_run_samples(pi, num_samps, 0xffff, 0, iqcalmode); +} + +void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi) +{ + s16 playback_status; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + pi->phy_tx_tone_freq = 0; + if (pi_lcn->lcnphy_spurmod) { + write_phy_reg(pi, 0x942, 0x7); + write_phy_reg(pi, 0x93b, 0x2017); + write_phy_reg(pi, 0x93c, 0x27c5); + wlc_lcnphy_txrx_spur_avoidance_mode(pi, true); + } + + playback_status = read_phy_reg(pi, 0x644); + if (playback_status & (0x1 << 0)) { + wlc_lcnphy_tx_pu(pi, 0); + mod_phy_reg(pi, 0x63f, (0x1 << 1), 1 << 1); + } else if (playback_status & (0x1 << 1)) + mod_phy_reg(pi, 0x453, (0x1 << 15), 0 << 15); + + mod_phy_reg(pi, 0x6d6, (0x3 << 0), 1 << 0); + + mod_phy_reg(pi, 0x6da, (0x1 << 3), 0 << 3); + + mod_phy_reg(pi, 0x6da, (0x1 << 7), 0 << 7); + + and_radio_reg(pi, RADIO_2064_REG112, 0xFFF9); + + wlc_lcnphy_deaf_mode(pi, false); +} + +static void +wlc_lcnphy_set_cc(struct brcms_phy *pi, int cal_type, s16 coeff_x, s16 coeff_y) +{ + u16 di0dq0; + u16 x, y, data_rf; + int k; + switch (cal_type) { + case 0: + wlc_lcnphy_set_tx_iqcc(pi, coeff_x, coeff_y); + break; + case 2: + di0dq0 = (coeff_x & 0xff) << 8 | (coeff_y & 0xff); + wlc_lcnphy_set_tx_locc(pi, di0dq0); + break; + case 3: + k = wlc_lcnphy_calc_floor(coeff_x, 0); + y = 8 + k; + k = wlc_lcnphy_calc_floor(coeff_x, 1); + x = 8 - k; + data_rf = (x * 16 + y); + write_radio_reg(pi, RADIO_2064_REG089, data_rf); + k = wlc_lcnphy_calc_floor(coeff_y, 0); + y = 8 + k; + k = wlc_lcnphy_calc_floor(coeff_y, 1); + x = 8 - k; + data_rf = (x * 16 + y); + write_radio_reg(pi, RADIO_2064_REG08A, data_rf); + break; + case 4: + k = wlc_lcnphy_calc_floor(coeff_x, 0); + y = 8 + k; + k = wlc_lcnphy_calc_floor(coeff_x, 1); + x = 8 - k; + data_rf = (x * 16 + y); + write_radio_reg(pi, RADIO_2064_REG08B, data_rf); + k = wlc_lcnphy_calc_floor(coeff_y, 0); + y = 8 + k; + k = wlc_lcnphy_calc_floor(coeff_y, 1); + x = 8 - k; + data_rf = (x * 16 + y); + write_radio_reg(pi, RADIO_2064_REG08C, data_rf); + break; + } +} + +static struct lcnphy_unsign16_struct +wlc_lcnphy_get_cc(struct brcms_phy *pi, int cal_type) +{ + u16 a, b, didq; + u8 di0, dq0, ei, eq, fi, fq; + struct lcnphy_unsign16_struct cc; + cc.re = 0; + cc.im = 0; + switch (cal_type) { + case 0: + wlc_lcnphy_get_tx_iqcc(pi, &a, &b); + cc.re = a; + cc.im = b; + break; + case 2: + didq = wlc_lcnphy_get_tx_locc(pi); + di0 = (((didq & 0xff00) << 16) >> 24); + dq0 = (((didq & 0x00ff) << 24) >> 24); + cc.re = (u16) di0; + cc.im = (u16) dq0; + break; + case 3: + wlc_lcnphy_get_radio_loft(pi, &ei, &eq, &fi, &fq); + cc.re = (u16) ei; + cc.im = (u16) eq; + break; + case 4: + wlc_lcnphy_get_radio_loft(pi, &ei, &eq, &fi, &fq); + cc.re = (u16) fi; + cc.im = (u16) fq; + break; + } + return cc; +} + +static void +wlc_lcnphy_samp_cap(struct brcms_phy *pi, int clip_detect_algo, u16 thresh, + s16 *ptr, int mode) +{ + u32 curval1, curval2, stpptr, curptr, strptr, val; + u16 sslpnCalibClkEnCtrl, timer; + u16 old_sslpnCalibClkEnCtrl; + s16 imag, real; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + timer = 0; + old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); + + curval1 = bcma_read16(pi->d11core, D11REGOFFS(psm_corectlsts)); + ptr[130] = 0; + bcma_write16(pi->d11core, D11REGOFFS(psm_corectlsts), + ((1 << 6) | curval1)); + + bcma_write16(pi->d11core, D11REGOFFS(smpl_clct_strptr), 0x7E00); + bcma_write16(pi->d11core, D11REGOFFS(smpl_clct_stpptr), 0x8000); + udelay(20); + curval2 = bcma_read16(pi->d11core, D11REGOFFS(psm_phy_hdr_param)); + bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), + curval2 | 0x30); + + write_phy_reg(pi, 0x555, 0x0); + write_phy_reg(pi, 0x5a6, 0x5); + + write_phy_reg(pi, 0x5a2, (u16) (mode | mode << 6)); + write_phy_reg(pi, 0x5cf, 3); + write_phy_reg(pi, 0x5a5, 0x3); + write_phy_reg(pi, 0x583, 0x0); + write_phy_reg(pi, 0x584, 0x0); + write_phy_reg(pi, 0x585, 0x0fff); + write_phy_reg(pi, 0x586, 0x0000); + + write_phy_reg(pi, 0x580, 0x4501); + + sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da); + write_phy_reg(pi, 0x6da, (u32) (sslpnCalibClkEnCtrl | 0x2008)); + stpptr = bcma_read16(pi->d11core, D11REGOFFS(smpl_clct_stpptr)); + curptr = bcma_read16(pi->d11core, D11REGOFFS(smpl_clct_curptr)); + do { + udelay(10); + curptr = bcma_read16(pi->d11core, D11REGOFFS(smpl_clct_curptr)); + timer++; + } while ((curptr != stpptr) && (timer < 500)); + + bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), 0x2); + strptr = 0x7E00; + bcma_write32(pi->d11core, D11REGOFFS(tplatewrptr), strptr); + while (strptr < 0x8000) { + val = bcma_read32(pi->d11core, D11REGOFFS(tplatewrdata)); + imag = ((val >> 16) & 0x3ff); + real = ((val) & 0x3ff); + if (imag > 511) + imag -= 1024; + + if (real > 511) + real -= 1024; + + if (pi_lcn->lcnphy_iqcal_swp_dis) + ptr[(strptr - 0x7E00) / 4] = real; + else + ptr[(strptr - 0x7E00) / 4] = imag; + + if (clip_detect_algo) { + if (imag > thresh || imag < -thresh) { + strptr = 0x8000; + ptr[130] = 1; + } + } + + strptr += 4; + } + + write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl); + bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), curval2); + bcma_write16(pi->d11core, D11REGOFFS(psm_corectlsts), curval1); +} + +static void +wlc_lcnphy_a1(struct brcms_phy *pi, int cal_type, int num_levels, + int step_size_lg2) +{ + const struct lcnphy_spb_tone *phy_c1; + struct lcnphy_spb_tone phy_c2; + struct lcnphy_unsign16_struct phy_c3; + int phy_c4, phy_c5, k, l, j, phy_c6; + u16 phy_c7, phy_c8, phy_c9; + s16 phy_c10, phy_c11, phy_c12, phy_c13, phy_c14, phy_c15, phy_c16; + s16 *ptr, phy_c17; + s32 phy_c18, phy_c19; + u32 phy_c20, phy_c21; + bool phy_c22, phy_c23, phy_c24, phy_c25; + u16 phy_c26, phy_c27; + u16 phy_c28, phy_c29, phy_c30; + u16 phy_c31; + u16 *phy_c32; + phy_c21 = 0; + phy_c10 = phy_c13 = phy_c14 = phy_c8 = 0; + ptr = kmalloc(sizeof(s16) * 131, GFP_ATOMIC); + if (NULL == ptr) + return; + + phy_c32 = kmalloc(sizeof(u16) * 20, GFP_ATOMIC); + if (NULL == phy_c32) { + kfree(ptr); + return; + } + phy_c26 = read_phy_reg(pi, 0x6da); + phy_c27 = read_phy_reg(pi, 0x6db); + phy_c31 = read_radio_reg(pi, RADIO_2064_REG026); + write_phy_reg(pi, 0x93d, 0xC0); + + wlc_lcnphy_start_tx_tone(pi, 3750, 88, 0); + write_phy_reg(pi, 0x6da, 0xffff); + or_phy_reg(pi, 0x6db, 0x3); + + wlc_lcnphy_tx_iqlo_loopback(pi, phy_c32); + udelay(500); + phy_c28 = read_phy_reg(pi, 0x938); + phy_c29 = read_phy_reg(pi, 0x4d7); + phy_c30 = read_phy_reg(pi, 0x4d8); + or_phy_reg(pi, 0x938, 0x1 << 2); + or_phy_reg(pi, 0x4d7, 0x1 << 2); + or_phy_reg(pi, 0x4d7, 0x1 << 3); + mod_phy_reg(pi, 0x4d7, (0x7 << 12), 0x2 << 12); + or_phy_reg(pi, 0x4d8, 1 << 0); + or_phy_reg(pi, 0x4d8, 1 << 1); + mod_phy_reg(pi, 0x4d8, (0x3ff << 2), 0x23A << 2); + mod_phy_reg(pi, 0x4d8, (0x7 << 12), 0x7 << 12); + phy_c1 = &lcnphy_spb_tone_3750[0]; + phy_c4 = 32; + + if (num_levels == 0) { + if (cal_type != 0) + num_levels = 4; + else + num_levels = 9; + } + if (step_size_lg2 == 0) { + if (cal_type != 0) + step_size_lg2 = 3; + else + step_size_lg2 = 8; + } + + phy_c7 = (1 << step_size_lg2); + phy_c3 = wlc_lcnphy_get_cc(pi, cal_type); + phy_c15 = (s16) phy_c3.re; + phy_c16 = (s16) phy_c3.im; + if (cal_type == 2) { + if (phy_c3.re > 127) + phy_c15 = phy_c3.re - 256; + if (phy_c3.im > 127) + phy_c16 = phy_c3.im - 256; + } + wlc_lcnphy_set_cc(pi, cal_type, phy_c15, phy_c16); + udelay(20); + for (phy_c8 = 0; phy_c7 != 0 && phy_c8 < num_levels; phy_c8++) { + phy_c23 = true; + phy_c22 = false; + switch (cal_type) { + case 0: + phy_c10 = 511; + break; + case 2: + phy_c10 = 127; + break; + case 3: + phy_c10 = 15; + break; + case 4: + phy_c10 = 15; + break; + } + + phy_c9 = read_phy_reg(pi, 0x93d); + phy_c9 = 2 * phy_c9; + phy_c24 = false; + phy_c5 = 7; + phy_c25 = true; + while (1) { + write_radio_reg(pi, RADIO_2064_REG026, + (phy_c5 & 0x7) | ((phy_c5 & 0x7) << 4)); + udelay(50); + phy_c22 = false; + ptr[130] = 0; + wlc_lcnphy_samp_cap(pi, 1, phy_c9, &ptr[0], 2); + if (ptr[130] == 1) + phy_c22 = true; + if (phy_c22) + phy_c5 -= 1; + if ((phy_c22 != phy_c24) && (!phy_c25)) + break; + if (!phy_c22) + phy_c5 += 1; + if (phy_c5 <= 0 || phy_c5 >= 7) + break; + phy_c24 = phy_c22; + phy_c25 = false; + } + + if (phy_c5 < 0) + phy_c5 = 0; + else if (phy_c5 > 7) + phy_c5 = 7; + + for (k = -phy_c7; k <= phy_c7; k += phy_c7) { + for (l = -phy_c7; l <= phy_c7; l += phy_c7) { + phy_c11 = phy_c15 + k; + phy_c12 = phy_c16 + l; + + if (phy_c11 < -phy_c10) + phy_c11 = -phy_c10; + else if (phy_c11 > phy_c10) + phy_c11 = phy_c10; + if (phy_c12 < -phy_c10) + phy_c12 = -phy_c10; + else if (phy_c12 > phy_c10) + phy_c12 = phy_c10; + wlc_lcnphy_set_cc(pi, cal_type, phy_c11, + phy_c12); + udelay(20); + wlc_lcnphy_samp_cap(pi, 0, 0, ptr, 2); + + phy_c18 = 0; + phy_c19 = 0; + for (j = 0; j < 128; j++) { + if (cal_type != 0) + phy_c6 = j % phy_c4; + else + phy_c6 = (2 * j) % phy_c4; + + phy_c2.re = phy_c1[phy_c6].re; + phy_c2.im = phy_c1[phy_c6].im; + phy_c17 = ptr[j]; + phy_c18 = phy_c18 + phy_c17 * phy_c2.re; + phy_c19 = phy_c19 + phy_c17 * phy_c2.im; + } + + phy_c18 = phy_c18 >> 10; + phy_c19 = phy_c19 >> 10; + phy_c20 = ((phy_c18 * phy_c18) + + (phy_c19 * phy_c19)); + + if (phy_c23 || phy_c20 < phy_c21) { + phy_c21 = phy_c20; + phy_c13 = phy_c11; + phy_c14 = phy_c12; + } + phy_c23 = false; + } + } + phy_c23 = true; + phy_c15 = phy_c13; + phy_c16 = phy_c14; + phy_c7 = phy_c7 >> 1; + wlc_lcnphy_set_cc(pi, cal_type, phy_c15, phy_c16); + udelay(20); + } + goto cleanup; +cleanup: + wlc_lcnphy_tx_iqlo_loopback_cleanup(pi, phy_c32); + wlc_lcnphy_stop_tx_tone(pi); + write_phy_reg(pi, 0x6da, phy_c26); + write_phy_reg(pi, 0x6db, phy_c27); + write_phy_reg(pi, 0x938, phy_c28); + write_phy_reg(pi, 0x4d7, phy_c29); + write_phy_reg(pi, 0x4d8, phy_c30); + write_radio_reg(pi, RADIO_2064_REG026, phy_c31); + + kfree(phy_c32); + kfree(ptr); +} + +void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b) +{ + u16 iqcc[2]; + struct phytbl_info tab; + + tab.tbl_ptr = iqcc; + tab.tbl_len = 2; + tab.tbl_id = 0; + tab.tbl_offset = 80; + tab.tbl_width = 16; + wlc_lcnphy_read_table(pi, &tab); + + *a = iqcc[0]; + *b = iqcc[1]; +} + +static void wlc_lcnphy_tx_iqlo_soft_cal_full(struct brcms_phy *pi) +{ + struct lcnphy_unsign16_struct iqcc0, locc2, locc3, locc4; + + wlc_lcnphy_set_cc(pi, 0, 0, 0); + wlc_lcnphy_set_cc(pi, 2, 0, 0); + wlc_lcnphy_set_cc(pi, 3, 0, 0); + wlc_lcnphy_set_cc(pi, 4, 0, 0); + + wlc_lcnphy_a1(pi, 4, 0, 0); + wlc_lcnphy_a1(pi, 3, 0, 0); + wlc_lcnphy_a1(pi, 2, 3, 2); + wlc_lcnphy_a1(pi, 0, 5, 8); + wlc_lcnphy_a1(pi, 2, 2, 1); + wlc_lcnphy_a1(pi, 0, 4, 3); + + iqcc0 = wlc_lcnphy_get_cc(pi, 0); + locc2 = wlc_lcnphy_get_cc(pi, 2); + locc3 = wlc_lcnphy_get_cc(pi, 3); + locc4 = wlc_lcnphy_get_cc(pi, 4); +} + +u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi) +{ + struct phytbl_info tab; + u16 didq; + + tab.tbl_id = 0; + tab.tbl_width = 16; + tab.tbl_ptr = &didq; + tab.tbl_len = 1; + tab.tbl_offset = 85; + wlc_lcnphy_read_table(pi, &tab); + + return didq; +} + +static void wlc_lcnphy_txpwrtbl_iqlo_cal(struct brcms_phy *pi) +{ + + struct lcnphy_txgains target_gains, old_gains; + u8 save_bb_mult; + u16 a, b, didq, save_pa_gain = 0; + uint idx, SAVE_txpwrindex = 0xFF; + u32 val; + u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); + struct phytbl_info tab; + u8 ei0, eq0, fi0, fq0; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + wlc_lcnphy_get_tx_gain(pi, &old_gains); + save_pa_gain = wlc_lcnphy_get_pa_gain(pi); + + save_bb_mult = wlc_lcnphy_get_bbmult(pi); + + if (SAVE_txpwrctrl == LCNPHY_TX_PWR_CTRL_OFF) + SAVE_txpwrindex = wlc_lcnphy_get_current_tx_pwr_idx(pi); + + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); + + target_gains.gm_gain = 7; + target_gains.pga_gain = 0; + target_gains.pad_gain = 21; + target_gains.dac_gain = 0; + wlc_lcnphy_set_tx_gain(pi, &target_gains); + + if (LCNREV_IS(pi->pubpi.phy_rev, 1) || pi_lcn->lcnphy_hw_iqcal_en) { + + wlc_lcnphy_set_tx_pwr_by_index(pi, 30); + + wlc_lcnphy_tx_iqlo_cal(pi, &target_gains, + (pi_lcn-> + lcnphy_recal ? LCNPHY_CAL_RECAL : + LCNPHY_CAL_FULL), false); + } else { + wlc_lcnphy_set_tx_pwr_by_index(pi, 16); + wlc_lcnphy_tx_iqlo_soft_cal_full(pi); + } + + wlc_lcnphy_get_radio_loft(pi, &ei0, &eq0, &fi0, &fq0); + if ((abs((s8) fi0) == 15) && (abs((s8) fq0) == 15)) { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + target_gains.gm_gain = 255; + target_gains.pga_gain = 255; + target_gains.pad_gain = 0xf0; + target_gains.dac_gain = 0; + } else { + target_gains.gm_gain = 7; + target_gains.pga_gain = 45; + target_gains.pad_gain = 186; + target_gains.dac_gain = 0; + } + + if (LCNREV_IS(pi->pubpi.phy_rev, 1) + || pi_lcn->lcnphy_hw_iqcal_en) { + + target_gains.pga_gain = 0; + target_gains.pad_gain = 30; + wlc_lcnphy_set_tx_pwr_by_index(pi, 16); + wlc_lcnphy_tx_iqlo_cal(pi, &target_gains, + LCNPHY_CAL_FULL, false); + } else { + wlc_lcnphy_tx_iqlo_soft_cal_full(pi); + } + } + + wlc_lcnphy_get_tx_iqcc(pi, &a, &b); + + didq = wlc_lcnphy_get_tx_locc(pi); + + tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; + tab.tbl_width = 32; + tab.tbl_ptr = &val; + + tab.tbl_len = 1; + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_RATE_OFFSET; + + for (idx = 0; idx < 128; idx++) { + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + idx; + + wlc_lcnphy_read_table(pi, &tab); + val = (val & 0xfff00000) | + ((u32) (a & 0x3FF) << 10) | (b & 0x3ff); + wlc_lcnphy_write_table(pi, &tab); + + val = didq; + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_LO_OFFSET + idx; + wlc_lcnphy_write_table(pi, &tab); + } + + pi_lcn->lcnphy_cal_results.txiqlocal_a = a; + pi_lcn->lcnphy_cal_results.txiqlocal_b = b; + pi_lcn->lcnphy_cal_results.txiqlocal_didq = didq; + pi_lcn->lcnphy_cal_results.txiqlocal_ei0 = ei0; + pi_lcn->lcnphy_cal_results.txiqlocal_eq0 = eq0; + pi_lcn->lcnphy_cal_results.txiqlocal_fi0 = fi0; + pi_lcn->lcnphy_cal_results.txiqlocal_fq0 = fq0; + + wlc_lcnphy_set_bbmult(pi, save_bb_mult); + wlc_lcnphy_set_pa_gain(pi, save_pa_gain); + wlc_lcnphy_set_tx_gain(pi, &old_gains); + + if (SAVE_txpwrctrl != LCNPHY_TX_PWR_CTRL_OFF) + wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl); + else + wlc_lcnphy_set_tx_pwr_by_index(pi, SAVE_txpwrindex); +} + +s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode) +{ + u16 tempsenseval1, tempsenseval2; + s16 avg = 0; + bool suspend = false; + + if (mode == 1) { + suspend = (0 == (bcma_read32(pi->d11core, + D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!suspend) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + wlc_lcnphy_vbat_temp_sense_setup(pi, TEMPSENSE); + } + tempsenseval1 = read_phy_reg(pi, 0x476) & 0x1FF; + tempsenseval2 = read_phy_reg(pi, 0x477) & 0x1FF; + + if (tempsenseval1 > 255) + avg = (s16) (tempsenseval1 - 512); + else + avg = (s16) tempsenseval1; + + if (tempsenseval2 > 255) + avg += (s16) (tempsenseval2 - 512); + else + avg += (s16) tempsenseval2; + + avg /= 2; + + if (mode == 1) { + + mod_phy_reg(pi, 0x448, (0x1 << 14), (1) << 14); + + udelay(100); + mod_phy_reg(pi, 0x448, (0x1 << 14), (0) << 14); + + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); + } + return avg; +} + +u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode) +{ + u16 tempsenseval1, tempsenseval2; + s32 avg = 0; + bool suspend = false; + u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + if (mode == 1) { + suspend = (0 == (bcma_read32(pi->d11core, + D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!suspend) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + wlc_lcnphy_vbat_temp_sense_setup(pi, TEMPSENSE); + } + tempsenseval1 = read_phy_reg(pi, 0x476) & 0x1FF; + tempsenseval2 = read_phy_reg(pi, 0x477) & 0x1FF; + + if (tempsenseval1 > 255) + avg = (int)(tempsenseval1 - 512); + else + avg = (int)tempsenseval1; + + if (pi_lcn->lcnphy_tempsense_option == 1 || pi->hwpwrctrl_capable) { + if (tempsenseval2 > 255) + avg = (int)(avg - tempsenseval2 + 512); + else + avg = (int)(avg - tempsenseval2); + } else { + if (tempsenseval2 > 255) + avg = (int)(avg + tempsenseval2 - 512); + else + avg = (int)(avg + tempsenseval2); + avg = avg / 2; + } + if (avg < 0) + avg = avg + 512; + + if (pi_lcn->lcnphy_tempsense_option == 2) + avg = tempsenseval1; + + if (mode) + wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl); + + if (mode == 1) { + + mod_phy_reg(pi, 0x448, (0x1 << 14), (1) << 14); + + udelay(100); + mod_phy_reg(pi, 0x448, (0x1 << 14), (0) << 14); + + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); + } + return (u16) avg; +} + +s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode) +{ + s32 degree = wlc_lcnphy_tempsense_new(pi, mode); + degree = + ((degree << + 10) + LCN_TEMPSENSE_OFFSET + (LCN_TEMPSENSE_DEN >> 1)) + / LCN_TEMPSENSE_DEN; + return (s8) degree; +} + +s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode) +{ + u16 vbatsenseval; + s32 avg = 0; + bool suspend = false; + + if (mode == 1) { + suspend = (0 == (bcma_read32(pi->d11core, + D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!suspend) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + wlc_lcnphy_vbat_temp_sense_setup(pi, VBATSENSE); + } + + vbatsenseval = read_phy_reg(pi, 0x475) & 0x1FF; + + if (vbatsenseval > 255) + avg = (s32) (vbatsenseval - 512); + else + avg = (s32) vbatsenseval; + + avg = (avg * LCN_VBAT_SCALE_NOM + + (LCN_VBAT_SCALE_DEN >> 1)) / LCN_VBAT_SCALE_DEN; + + if (mode == 1) { + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); + } + return (s8) avg; +} + +static void wlc_lcnphy_afe_clk_init(struct brcms_phy *pi, u8 mode) +{ + u8 phybw40; + phybw40 = CHSPEC_IS40(pi->radio_chanspec); + + mod_phy_reg(pi, 0x6d1, (0x1 << 7), (1) << 7); + + if (((mode == AFE_CLK_INIT_MODE_PAPD) && (phybw40 == 0)) || + (mode == AFE_CLK_INIT_MODE_TXRX2X)) + write_phy_reg(pi, 0x6d0, 0x7); + + wlc_lcnphy_toggle_afe_pwdn(pi); +} + +static void wlc_lcnphy_temp_adj(struct brcms_phy *pi) +{ +} + +static void wlc_lcnphy_glacial_timer_based_cal(struct brcms_phy *pi) +{ + bool suspend; + s8 index; + u16 SAVE_pwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!suspend) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + wlc_lcnphy_deaf_mode(pi, true); + pi->phy_lastcal = pi->sh->now; + pi->phy_forcecal = false; + index = pi_lcn->lcnphy_current_index; + + wlc_lcnphy_txpwrtbl_iqlo_cal(pi); + + wlc_lcnphy_set_tx_pwr_by_index(pi, index); + wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_pwrctrl); + wlc_lcnphy_deaf_mode(pi, false); + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); + +} + +static void wlc_lcnphy_periodic_cal(struct brcms_phy *pi) +{ + bool suspend, full_cal; + const struct lcnphy_rx_iqcomp *rx_iqcomp; + int rx_iqcomp_sz; + u16 SAVE_pwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); + s8 index; + struct phytbl_info tab; + s32 a1, b0, b1; + s32 tssi, pwr, maxtargetpwr, mintargetpwr; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + pi->phy_lastcal = pi->sh->now; + pi->phy_forcecal = false; + full_cal = + (pi_lcn->lcnphy_full_cal_channel != + CHSPEC_CHANNEL(pi->radio_chanspec)); + pi_lcn->lcnphy_full_cal_channel = CHSPEC_CHANNEL(pi->radio_chanspec); + index = pi_lcn->lcnphy_current_index; + + suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!suspend) { + wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, 10000); + wlapi_suspend_mac_and_wait(pi->sh->physhim); + } + + wlc_lcnphy_deaf_mode(pi, true); + + wlc_lcnphy_txpwrtbl_iqlo_cal(pi); + + rx_iqcomp = lcnphy_rx_iqcomp_table_rev0; + rx_iqcomp_sz = ARRAY_SIZE(lcnphy_rx_iqcomp_table_rev0); + + if (LCNREV_IS(pi->pubpi.phy_rev, 1)) + wlc_lcnphy_rx_iq_cal(pi, NULL, 0, true, false, 1, 40); + else + wlc_lcnphy_rx_iq_cal(pi, NULL, 0, true, false, 1, 127); + + if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) { + + wlc_lcnphy_idle_tssi_est((struct brcms_phy_pub *) pi); + + b0 = pi->txpa_2g[0]; + b1 = pi->txpa_2g[1]; + a1 = pi->txpa_2g[2]; + maxtargetpwr = wlc_lcnphy_tssi2dbm(10, a1, b0, b1); + mintargetpwr = wlc_lcnphy_tssi2dbm(125, a1, b0, b1); + + tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; + tab.tbl_width = 32; + tab.tbl_ptr = &pwr; + tab.tbl_len = 1; + tab.tbl_offset = 0; + for (tssi = 0; tssi < 128; tssi++) { + pwr = wlc_lcnphy_tssi2dbm(tssi, a1, b0, b1); + pwr = (pwr < mintargetpwr) ? mintargetpwr : pwr; + wlc_lcnphy_write_table(pi, &tab); + tab.tbl_offset++; + } + } + + wlc_lcnphy_set_tx_pwr_by_index(pi, index); + wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_pwrctrl); + wlc_lcnphy_deaf_mode(pi, false); + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); +} + +void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode) +{ + u16 temp_new; + int temp1, temp2, temp_diff; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + switch (mode) { + case PHY_PERICAL_CHAN: + break; + case PHY_FULLCAL: + wlc_lcnphy_periodic_cal(pi); + break; + case PHY_PERICAL_PHYINIT: + wlc_lcnphy_periodic_cal(pi); + break; + case PHY_PERICAL_WATCHDOG: + if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { + temp_new = wlc_lcnphy_tempsense(pi, 0); + temp1 = LCNPHY_TEMPSENSE(temp_new); + temp2 = LCNPHY_TEMPSENSE(pi_lcn->lcnphy_cal_temper); + temp_diff = temp1 - temp2; + if ((pi_lcn->lcnphy_cal_counter > 90) || + (temp_diff > 60) || (temp_diff < -60)) { + wlc_lcnphy_glacial_timer_based_cal(pi); + wlc_2064_vco_cal(pi); + pi_lcn->lcnphy_cal_temper = temp_new; + pi_lcn->lcnphy_cal_counter = 0; + } else + pi_lcn->lcnphy_cal_counter++; + } + break; + case LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL: + if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) + wlc_lcnphy_tx_power_adjustment( + (struct brcms_phy_pub *) pi); + break; + } +} + +void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, s8 *cck_pwr) +{ + s8 cck_offset; + u16 status; + status = (read_phy_reg(pi, 0x4ab)); + if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi) && + (status & (0x1 << 15))) { + *ofdm_pwr = (s8) (((read_phy_reg(pi, 0x4ab) & (0x1ff << 0)) + >> 0) >> 1); + + if (wlc_phy_tpc_isenabled_lcnphy(pi)) + cck_offset = pi->tx_power_offset[TXP_FIRST_CCK]; + else + cck_offset = 0; + + *cck_pwr = *ofdm_pwr + cck_offset; + } else { + *cck_pwr = 0; + *ofdm_pwr = 0; + } +} + +void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi) +{ + return; + +} + +void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi) +{ + s8 index; + u16 index2; + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + u16 SAVE_txpwrctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); + if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi) && + SAVE_txpwrctrl) { + index = wlc_lcnphy_tempcompensated_txpwrctrl(pi); + index2 = (u16) (index * 2); + mod_phy_reg(pi, 0x4a9, (0x1ff << 0), (index2) << 0); + + pi_lcn->lcnphy_current_index = + (s8)((read_phy_reg(pi, 0x4a9) & 0xFF) / 2); + } +} + +static void +wlc_lcnphy_load_tx_gain_table(struct brcms_phy *pi, + const struct lcnphy_tx_gain_tbl_entry *gain_table) +{ + u32 j; + struct phytbl_info tab; + u32 val; + u16 pa_gain; + u16 gm_gain; + + if (pi->sh->boardflags & BFL_FEM) + pa_gain = 0x10; + else + pa_gain = 0x60; + tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; + tab.tbl_width = 32; + tab.tbl_len = 1; + tab.tbl_ptr = &val; + + /* fixed gm_gain value for iPA */ + gm_gain = 15; + for (j = 0; j < 128; j++) { + if (pi->sh->boardflags & BFL_FEM) + gm_gain = gain_table[j].gm; + val = (((u32) pa_gain << 24) | + (gain_table[j].pad << 16) | + (gain_table[j].pga << 8) | gm_gain); + + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + j; + wlc_lcnphy_write_table(pi, &tab); + + val = (gain_table[j].dac << 28) | (gain_table[j].bb_mult << 20); + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + j; + wlc_lcnphy_write_table(pi, &tab); + } +} + +static void wlc_lcnphy_load_rfpower(struct brcms_phy *pi) +{ + struct phytbl_info tab; + u32 val, bbmult, rfgain; + u8 index; + u8 scale_factor = 1; + s16 temp, temp1, temp2, qQ, qQ1, qQ2, shift; + + tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL; + tab.tbl_width = 32; + tab.tbl_len = 1; + + for (index = 0; index < 128; index++) { + tab.tbl_ptr = &bbmult; + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_IQ_OFFSET + index; + wlc_lcnphy_read_table(pi, &tab); + bbmult = bbmult >> 20; + + tab.tbl_ptr = &rfgain; + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_GAIN_OFFSET + index; + wlc_lcnphy_read_table(pi, &tab); + + qm_log10((s32) (bbmult), 0, &temp1, &qQ1); + qm_log10((s32) (1 << 6), 0, &temp2, &qQ2); + + if (qQ1 < qQ2) { + temp2 = qm_shr16(temp2, qQ2 - qQ1); + qQ = qQ1; + } else { + temp1 = qm_shr16(temp1, qQ1 - qQ2); + qQ = qQ2; + } + temp = qm_sub16(temp1, temp2); + + if (qQ >= 4) + shift = qQ - 4; + else + shift = 4 - qQ; + + val = (((index << shift) + (5 * temp) + + (1 << (scale_factor + shift - 3))) >> (scale_factor + + shift - 2)); + + tab.tbl_ptr = &val; + tab.tbl_offset = LCNPHY_TX_PWR_CTRL_PWR_OFFSET + index; + wlc_lcnphy_write_table(pi, &tab); + } +} + +static void wlc_lcnphy_bu_tweaks(struct brcms_phy *pi) +{ + or_phy_reg(pi, 0x805, 0x1); + + mod_phy_reg(pi, 0x42f, (0x7 << 0), (0x3) << 0); + + mod_phy_reg(pi, 0x030, (0x7 << 0), (0x3) << 0); + + write_phy_reg(pi, 0x414, 0x1e10); + write_phy_reg(pi, 0x415, 0x0640); + + mod_phy_reg(pi, 0x4df, (0xff << 8), -9 << 8); + + or_phy_reg(pi, 0x44a, 0x44); + write_phy_reg(pi, 0x44a, 0x80); + mod_phy_reg(pi, 0x434, (0xff << 0), (0xFD) << 0); + + mod_phy_reg(pi, 0x420, (0xff << 0), (16) << 0); + + if (!(pi->sh->boardrev < 0x1204)) + mod_radio_reg(pi, RADIO_2064_REG09B, 0xF0, 0xF0); + + write_phy_reg(pi, 0x7d6, 0x0902); + mod_phy_reg(pi, 0x429, (0xf << 0), (0x9) << 0); + + mod_phy_reg(pi, 0x429, (0x3f << 4), (0xe) << 4); + + if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { + mod_phy_reg(pi, 0x423, (0xff << 0), (0x46) << 0); + + mod_phy_reg(pi, 0x411, (0xff << 0), (1) << 0); + + mod_phy_reg(pi, 0x434, (0xff << 0), (0xFF) << 0); + + mod_phy_reg(pi, 0x656, (0xf << 0), (2) << 0); + + mod_phy_reg(pi, 0x44d, (0x1 << 2), (1) << 2); + + mod_radio_reg(pi, RADIO_2064_REG0F7, 0x4, 0x4); + mod_radio_reg(pi, RADIO_2064_REG0F1, 0x3, 0); + mod_radio_reg(pi, RADIO_2064_REG0F2, 0xF8, 0x90); + mod_radio_reg(pi, RADIO_2064_REG0F3, 0x3, 0x2); + mod_radio_reg(pi, RADIO_2064_REG0F3, 0xf0, 0xa0); + + mod_radio_reg(pi, RADIO_2064_REG11F, 0x2, 0x2); + + wlc_lcnphy_clear_tx_power_offsets(pi); + mod_phy_reg(pi, 0x4d0, (0x1ff << 6), (10) << 6); + + } +} + +static void wlc_lcnphy_rcal(struct brcms_phy *pi) +{ + u8 rcal_value; + + and_radio_reg(pi, RADIO_2064_REG05B, 0xfD); + + or_radio_reg(pi, RADIO_2064_REG004, 0x40); + or_radio_reg(pi, RADIO_2064_REG120, 0x10); + + or_radio_reg(pi, RADIO_2064_REG078, 0x80); + or_radio_reg(pi, RADIO_2064_REG129, 0x02); + + or_radio_reg(pi, RADIO_2064_REG057, 0x01); + + or_radio_reg(pi, RADIO_2064_REG05B, 0x02); + mdelay(5); + SPINWAIT(!wlc_radio_2064_rcal_done(pi), 10 * 1000 * 1000); + + if (wlc_radio_2064_rcal_done(pi)) { + rcal_value = (u8) read_radio_reg(pi, RADIO_2064_REG05C); + rcal_value = rcal_value & 0x1f; + } + + and_radio_reg(pi, RADIO_2064_REG05B, 0xfD); + + and_radio_reg(pi, RADIO_2064_REG057, 0xFE); +} + +static void wlc_lcnphy_rc_cal(struct brcms_phy *pi) +{ + u8 dflt_rc_cal_val; + u16 flt_val; + + dflt_rc_cal_val = 7; + if (LCNREV_IS(pi->pubpi.phy_rev, 1)) + dflt_rc_cal_val = 11; + flt_val = + (dflt_rc_cal_val << 10) | (dflt_rc_cal_val << 5) | + (dflt_rc_cal_val); + write_phy_reg(pi, 0x933, flt_val); + write_phy_reg(pi, 0x934, flt_val); + write_phy_reg(pi, 0x935, flt_val); + write_phy_reg(pi, 0x936, flt_val); + write_phy_reg(pi, 0x937, (flt_val & 0x1FF)); + + return; +} + +static void wlc_radio_2064_init(struct brcms_phy *pi) +{ + u32 i; + const struct lcnphy_radio_regs *lcnphyregs = NULL; + + lcnphyregs = lcnphy_radio_regs_2064; + + for (i = 0; lcnphyregs[i].address != 0xffff; i++) + if (CHSPEC_IS5G(pi->radio_chanspec) && lcnphyregs[i].do_init_a) + write_radio_reg(pi, + ((lcnphyregs[i].address & 0x3fff) | + RADIO_DEFAULT_CORE), + (u16) lcnphyregs[i].init_a); + else if (lcnphyregs[i].do_init_g) + write_radio_reg(pi, + ((lcnphyregs[i].address & 0x3fff) | + RADIO_DEFAULT_CORE), + (u16) lcnphyregs[i].init_g); + + write_radio_reg(pi, RADIO_2064_REG032, 0x62); + write_radio_reg(pi, RADIO_2064_REG033, 0x19); + + write_radio_reg(pi, RADIO_2064_REG090, 0x10); + + write_radio_reg(pi, RADIO_2064_REG010, 0x00); + + if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { + + write_radio_reg(pi, RADIO_2064_REG060, 0x7f); + write_radio_reg(pi, RADIO_2064_REG061, 0x72); + write_radio_reg(pi, RADIO_2064_REG062, 0x7f); + } + + write_radio_reg(pi, RADIO_2064_REG01D, 0x02); + write_radio_reg(pi, RADIO_2064_REG01E, 0x06); + + mod_phy_reg(pi, 0x4ea, (0x7 << 0), 0 << 0); + + mod_phy_reg(pi, 0x4ea, (0x7 << 3), 1 << 3); + + mod_phy_reg(pi, 0x4ea, (0x7 << 6), 2 << 6); + + mod_phy_reg(pi, 0x4ea, (0x7 << 9), 3 << 9); + + mod_phy_reg(pi, 0x4ea, (0x7 << 12), 4 << 12); + + write_phy_reg(pi, 0x4ea, 0x4688); + + if (pi->sh->boardflags & BFL_FEM) + mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0); + else + mod_phy_reg(pi, 0x4eb, (0x7 << 0), 3 << 0); + + mod_phy_reg(pi, 0x4eb, (0x7 << 6), 0 << 6); + + mod_phy_reg(pi, 0x46a, (0xffff << 0), 25 << 0); + + wlc_lcnphy_set_tx_locc(pi, 0); + + wlc_lcnphy_rcal(pi); + + wlc_lcnphy_rc_cal(pi); + + if (!(pi->sh->boardflags & BFL_FEM)) { + write_radio_reg(pi, RADIO_2064_REG032, 0x6f); + write_radio_reg(pi, RADIO_2064_REG033, 0x19); + write_radio_reg(pi, RADIO_2064_REG039, 0xe); + } + +} + +static void wlc_lcnphy_radio_init(struct brcms_phy *pi) +{ + wlc_radio_2064_init(pi); +} + +static void wlc_lcnphy_tbl_init(struct brcms_phy *pi) +{ + uint idx; + u8 phybw40; + struct phytbl_info tab; + const struct phytbl_info *tb; + u32 val; + + phybw40 = CHSPEC_IS40(pi->radio_chanspec); + + for (idx = 0; idx < dot11lcnphytbl_info_sz_rev0; idx++) + wlc_lcnphy_write_table(pi, &dot11lcnphytbl_info_rev0[idx]); + + if (pi->sh->boardflags & BFL_FEM_BT) { + tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; + tab.tbl_width = 16; + tab.tbl_ptr = &val; + tab.tbl_len = 1; + val = 100; + tab.tbl_offset = 4; + wlc_lcnphy_write_table(pi, &tab); + } + + if (!(pi->sh->boardflags & BFL_FEM)) { + tab.tbl_id = LCNPHY_TBL_ID_RFSEQ; + tab.tbl_width = 16; + tab.tbl_ptr = &val; + tab.tbl_len = 1; + + val = 150; + tab.tbl_offset = 0; + wlc_lcnphy_write_table(pi, &tab); + + val = 220; + tab.tbl_offset = 1; + wlc_lcnphy_write_table(pi, &tab); + } + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if (pi->sh->boardflags & BFL_FEM) + wlc_lcnphy_load_tx_gain_table( + pi, + dot11lcnphy_2GHz_extPA_gaintable_rev0); + else + wlc_lcnphy_load_tx_gain_table( + pi, + dot11lcnphy_2GHz_gaintable_rev0); + } + + if (LCNREV_IS(pi->pubpi.phy_rev, 2)) { + int l; + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + l = dot11lcnphytbl_rx_gain_info_2G_rev2_sz; + if (pi->sh->boardflags & BFL_EXTLNA) + tb = dot11lcnphytbl_rx_gain_info_extlna_2G_rev2; + else + tb = dot11lcnphytbl_rx_gain_info_2G_rev2; + } else { + l = dot11lcnphytbl_rx_gain_info_5G_rev2_sz; + if (pi->sh->boardflags & BFL_EXTLNA_5GHz) + tb = dot11lcnphytbl_rx_gain_info_extlna_5G_rev2; + else + tb = dot11lcnphytbl_rx_gain_info_5G_rev2; + } + + for (idx = 0; idx < l; idx++) + wlc_lcnphy_write_table(pi, &tb[idx]); + } + + if (pi->sh->boardflags & BFL_FEM) { + if (pi->sh->boardflags & BFL_FEM_BT) { + if (pi->sh->boardrev < 0x1250) + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa; + else + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250; + } else { + tb = &dot11lcn_sw_ctrl_tbl_info_4313_epa; + } + } else { + if (pi->sh->boardflags & BFL_FEM_BT) + tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa; + else + tb = &dot11lcn_sw_ctrl_tbl_info_4313; + } + wlc_lcnphy_write_table(pi, tb); + wlc_lcnphy_load_rfpower(pi); + + wlc_lcnphy_clear_papd_comptable(pi); +} + +static void wlc_lcnphy_rev0_baseband_init(struct brcms_phy *pi) +{ + u16 afectrl1; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + write_radio_reg(pi, RADIO_2064_REG11C, 0x0); + + write_phy_reg(pi, 0x43b, 0x0); + write_phy_reg(pi, 0x43c, 0x0); + write_phy_reg(pi, 0x44c, 0x0); + write_phy_reg(pi, 0x4e6, 0x0); + write_phy_reg(pi, 0x4f9, 0x0); + write_phy_reg(pi, 0x4b0, 0x0); + write_phy_reg(pi, 0x938, 0x0); + write_phy_reg(pi, 0x4b0, 0x0); + write_phy_reg(pi, 0x44e, 0); + + or_phy_reg(pi, 0x567, 0x03); + + or_phy_reg(pi, 0x44a, 0x44); + write_phy_reg(pi, 0x44a, 0x80); + + if (!(pi->sh->boardflags & BFL_FEM)) + wlc_lcnphy_set_tx_pwr_by_index(pi, 52); + + if (0) { + afectrl1 = 0; + afectrl1 = (u16) ((pi_lcn->lcnphy_rssi_vf) | + (pi_lcn->lcnphy_rssi_vc << 4) | + (pi_lcn->lcnphy_rssi_gs << 10)); + write_phy_reg(pi, 0x43e, afectrl1); + } + + mod_phy_reg(pi, 0x634, (0xff << 0), 0xC << 0); + if (pi->sh->boardflags & BFL_FEM) { + mod_phy_reg(pi, 0x634, (0xff << 0), 0xA << 0); + + write_phy_reg(pi, 0x910, 0x1); + } + + mod_phy_reg(pi, 0x448, (0x3 << 8), 1 << 8); + mod_phy_reg(pi, 0x608, (0xff << 0), 0x17 << 0); + mod_phy_reg(pi, 0x604, (0x7ff << 0), 0x3EA << 0); + +} + +static void wlc_lcnphy_rev2_baseband_init(struct brcms_phy *pi) +{ + if (CHSPEC_IS5G(pi->radio_chanspec)) { + mod_phy_reg(pi, 0x416, (0xff << 0), 80 << 0); + mod_phy_reg(pi, 0x416, (0xff << 8), 80 << 8); + } +} + +static void wlc_lcnphy_agc_temp_init(struct brcms_phy *pi) +{ + s16 temp; + struct phytbl_info tab; + u32 tableBuffer[2]; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + temp = (s16) read_phy_reg(pi, 0x4df); + pi_lcn->lcnphy_ofdmgainidxtableoffset = (temp & (0xff << 0)) >> 0; + + if (pi_lcn->lcnphy_ofdmgainidxtableoffset > 127) + pi_lcn->lcnphy_ofdmgainidxtableoffset -= 256; + + pi_lcn->lcnphy_dsssgainidxtableoffset = (temp & (0xff << 8)) >> 8; + + if (pi_lcn->lcnphy_dsssgainidxtableoffset > 127) + pi_lcn->lcnphy_dsssgainidxtableoffset -= 256; + + tab.tbl_ptr = tableBuffer; + tab.tbl_len = 2; + tab.tbl_id = 17; + tab.tbl_offset = 59; + tab.tbl_width = 32; + wlc_lcnphy_read_table(pi, &tab); + + if (tableBuffer[0] > 63) + tableBuffer[0] -= 128; + pi_lcn->lcnphy_tr_R_gain_val = tableBuffer[0]; + + if (tableBuffer[1] > 63) + tableBuffer[1] -= 128; + pi_lcn->lcnphy_tr_T_gain_val = tableBuffer[1]; + + temp = (s16) (read_phy_reg(pi, 0x434) & (0xff << 0)); + if (temp > 127) + temp -= 256; + pi_lcn->lcnphy_input_pwr_offset_db = (s8) temp; + + pi_lcn->lcnphy_Med_Low_Gain_db = + (read_phy_reg(pi, 0x424) & (0xff << 8)) >> 8; + pi_lcn->lcnphy_Very_Low_Gain_db = + (read_phy_reg(pi, 0x425) & (0xff << 0)) >> 0; + + tab.tbl_ptr = tableBuffer; + tab.tbl_len = 2; + tab.tbl_id = LCNPHY_TBL_ID_GAIN_IDX; + tab.tbl_offset = 28; + tab.tbl_width = 32; + wlc_lcnphy_read_table(pi, &tab); + + pi_lcn->lcnphy_gain_idx_14_lowword = tableBuffer[0]; + pi_lcn->lcnphy_gain_idx_14_hiword = tableBuffer[1]; + +} + +static void wlc_lcnphy_baseband_init(struct brcms_phy *pi) +{ + + wlc_lcnphy_tbl_init(pi); + wlc_lcnphy_rev0_baseband_init(pi); + if (LCNREV_IS(pi->pubpi.phy_rev, 2)) + wlc_lcnphy_rev2_baseband_init(pi); + wlc_lcnphy_bu_tweaks(pi); +} + +void wlc_phy_init_lcnphy(struct brcms_phy *pi) +{ + u8 phybw40; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + phybw40 = CHSPEC_IS40(pi->radio_chanspec); + + pi_lcn->lcnphy_cal_counter = 0; + pi_lcn->lcnphy_cal_temper = pi_lcn->lcnphy_rawtempsense; + + or_phy_reg(pi, 0x44a, 0x80); + and_phy_reg(pi, 0x44a, 0x7f); + + wlc_lcnphy_afe_clk_init(pi, AFE_CLK_INIT_MODE_TXRX2X); + + write_phy_reg(pi, 0x60a, 160); + + write_phy_reg(pi, 0x46a, 25); + + wlc_lcnphy_baseband_init(pi); + + wlc_lcnphy_radio_init(pi); + + if (CHSPEC_IS2G(pi->radio_chanspec)) + wlc_lcnphy_tx_pwr_ctrl_init((struct brcms_phy_pub *) pi); + + wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, pi->radio_chanspec); + + bcma_chipco_regctl_maskset(&pi->d11core->bus->drv_cc, 0, ~0xf, 0x9); + + bcma_chipco_chipctl_maskset(&pi->d11core->bus->drv_cc, 0, 0x0, + 0x03CDDDDD); + + if ((pi->sh->boardflags & BFL_FEM) + && wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) + wlc_lcnphy_set_tx_pwr_by_index(pi, FIXED_TXPWR); + + wlc_lcnphy_agc_temp_init(pi); + + wlc_lcnphy_temp_adj(pi); + + mod_phy_reg(pi, 0x448, (0x1 << 14), (1) << 14); + + udelay(100); + mod_phy_reg(pi, 0x448, (0x1 << 14), (0) << 14); + + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_HW); + pi_lcn->lcnphy_noise_samples = LCNPHY_NOISE_SAMPLES_DEFAULT; + wlc_lcnphy_calib_modes(pi, PHY_PERICAL_PHYINIT); +} + +static bool wlc_phy_txpwr_srom_read_lcnphy(struct brcms_phy *pi) +{ + s8 txpwr = 0; + int i; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + struct ssb_sprom *sprom = &pi->d11core->bus->sprom; + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + u16 cckpo = 0; + u32 offset_ofdm, offset_mcs; + + pi_lcn->lcnphy_tr_isolation_mid = sprom->fem.ghz2.tr_iso; + + pi_lcn->lcnphy_rx_power_offset = sprom->rxpo2g; + + pi->txpa_2g[0] = sprom->pa0b0; + pi->txpa_2g[1] = sprom->pa0b1; + pi->txpa_2g[2] = sprom->pa0b2; + + pi_lcn->lcnphy_rssi_vf = sprom->rssismf2g; + pi_lcn->lcnphy_rssi_vc = sprom->rssismc2g; + pi_lcn->lcnphy_rssi_gs = sprom->rssisav2g; + + pi_lcn->lcnphy_rssi_vf_lowtemp = pi_lcn->lcnphy_rssi_vf; + pi_lcn->lcnphy_rssi_vc_lowtemp = pi_lcn->lcnphy_rssi_vc; + pi_lcn->lcnphy_rssi_gs_lowtemp = pi_lcn->lcnphy_rssi_gs; + + pi_lcn->lcnphy_rssi_vf_hightemp = pi_lcn->lcnphy_rssi_vf; + pi_lcn->lcnphy_rssi_vc_hightemp = pi_lcn->lcnphy_rssi_vc; + pi_lcn->lcnphy_rssi_gs_hightemp = pi_lcn->lcnphy_rssi_gs; + + txpwr = sprom->core_pwr_info[0].maxpwr_2g; + pi->tx_srom_max_2g = txpwr; + + for (i = 0; i < PWRTBL_NUM_COEFF; i++) { + pi->txpa_2g_low_temp[i] = pi->txpa_2g[i]; + pi->txpa_2g_high_temp[i] = pi->txpa_2g[i]; + } + + cckpo = sprom->cck2gpo; + offset_ofdm = sprom->ofdm2gpo; + if (cckpo) { + uint max_pwr_chan = txpwr; + + for (i = TXP_FIRST_CCK; i <= TXP_LAST_CCK; i++) { + pi->tx_srom_max_rate_2g[i] = + max_pwr_chan - ((cckpo & 0xf) * 2); + cckpo >>= 4; + } + + for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++) { + pi->tx_srom_max_rate_2g[i] = + max_pwr_chan - + ((offset_ofdm & 0xf) * 2); + offset_ofdm >>= 4; + } + } else { + u8 opo = 0; + + opo = sprom->opo; + + for (i = TXP_FIRST_CCK; i <= TXP_LAST_CCK; i++) + pi->tx_srom_max_rate_2g[i] = txpwr; + + for (i = TXP_FIRST_OFDM; i <= TXP_LAST_OFDM; i++) { + pi->tx_srom_max_rate_2g[i] = txpwr - + ((offset_ofdm & 0xf) * 2); + offset_ofdm >>= 4; + } + offset_mcs = sprom->mcs2gpo[1] << 16; + offset_mcs |= sprom->mcs2gpo[0]; + pi_lcn->lcnphy_mcs20_po = offset_mcs; + for (i = TXP_FIRST_SISO_MCS_20; + i <= TXP_LAST_SISO_MCS_20; i++) { + pi->tx_srom_max_rate_2g[i] = + txpwr - ((offset_mcs & 0xf) * 2); + offset_mcs >>= 4; + } + } + + pi_lcn->lcnphy_rawtempsense = sprom->rawtempsense; + pi_lcn->lcnphy_measPower = sprom->measpower; + pi_lcn->lcnphy_tempsense_slope = sprom->tempsense_slope; + pi_lcn->lcnphy_hw_iqcal_en = sprom->hw_iqcal_en; + pi_lcn->lcnphy_iqcal_swp_dis = sprom->iqcal_swp_dis; + pi_lcn->lcnphy_tempcorrx = sprom->tempcorrx; + pi_lcn->lcnphy_tempsense_option = sprom->tempsense_option; + pi_lcn->lcnphy_freqoffset_corr = sprom->freqoffset_corr; + if (sprom->ant_available_bg > 1) + wlc_phy_ant_rxdiv_set((struct brcms_phy_pub *) pi, + sprom->ant_available_bg); + } + pi_lcn->lcnphy_cck_dig_filt_type = -1; + + return true; +} + +void wlc_2064_vco_cal(struct brcms_phy *pi) +{ + u8 calnrst; + + mod_radio_reg(pi, RADIO_2064_REG057, 1 << 3, 1 << 3); + calnrst = (u8) read_radio_reg(pi, RADIO_2064_REG056) & 0xf8; + write_radio_reg(pi, RADIO_2064_REG056, calnrst); + udelay(1); + write_radio_reg(pi, RADIO_2064_REG056, calnrst | 0x03); + udelay(1); + write_radio_reg(pi, RADIO_2064_REG056, calnrst | 0x07); + udelay(300); + mod_radio_reg(pi, RADIO_2064_REG057, 1 << 3, 0); +} + +bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi) +{ + if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) + return false; + else + return (LCNPHY_TX_PWR_CTRL_HW == + wlc_lcnphy_get_tx_pwr_ctrl((pi))); +} + +void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi) +{ + u16 pwr_ctrl; + if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi)) { + wlc_lcnphy_calib_modes(pi, LCNPHY_PERICAL_TEMPBASED_TXPWRCTRL); + } else if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) { + pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi); + wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); + wlc_lcnphy_txpower_recalc_target(pi); + wlc_lcnphy_set_tx_pwr_ctrl(pi, pwr_ctrl); + } +} + +void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec) +{ + u8 channel = CHSPEC_CHANNEL(chanspec); + + wlc_phy_chanspec_radio_set((struct brcms_phy_pub *)pi, chanspec); + + wlc_lcnphy_set_chanspec_tweaks(pi, pi->radio_chanspec); + + or_phy_reg(pi, 0x44a, 0x44); + write_phy_reg(pi, 0x44a, 0x80); + + wlc_lcnphy_radio_2064_channel_tune_4313(pi, channel); + udelay(1000); + + wlc_lcnphy_toggle_afe_pwdn(pi); + + write_phy_reg(pi, 0x657, lcnphy_sfo_cfg[channel - 1].ptcentreTs20); + write_phy_reg(pi, 0x658, lcnphy_sfo_cfg[channel - 1].ptcentreFactor); + + if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) { + mod_phy_reg(pi, 0x448, (0x3 << 8), (2) << 8); + + wlc_lcnphy_load_tx_iir_filter(pi, false, 3); + } else { + mod_phy_reg(pi, 0x448, (0x3 << 8), (1) << 8); + + wlc_lcnphy_load_tx_iir_filter(pi, false, 2); + } + + if (pi->sh->boardflags & BFL_FEM) + wlc_lcnphy_load_tx_iir_filter(pi, true, 0); + else + wlc_lcnphy_load_tx_iir_filter(pi, true, 3); + + mod_phy_reg(pi, 0x4eb, (0x7 << 3), (1) << 3); + if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi)) + wlc_lcnphy_tssi_setup(pi); +} + +void wlc_phy_detach_lcnphy(struct brcms_phy *pi) +{ + kfree(pi->u.pi_lcnphy); +} + +bool wlc_phy_attach_lcnphy(struct brcms_phy *pi) +{ + struct brcms_phy_lcnphy *pi_lcn; + + pi->u.pi_lcnphy = kzalloc(sizeof(struct brcms_phy_lcnphy), GFP_ATOMIC); + if (pi->u.pi_lcnphy == NULL) + return false; + + pi_lcn = pi->u.pi_lcnphy; + + if (0 == (pi->sh->boardflags & BFL_NOPA)) { + pi->hwpwrctrl = true; + pi->hwpwrctrl_capable = true; + } + + pi->xtalfreq = bcma_chipco_get_alp_clock(&pi->d11core->bus->drv_cc); + pi_lcn->lcnphy_papd_rxGnCtrl_init = 0; + + pi->pi_fptr.init = wlc_phy_init_lcnphy; + pi->pi_fptr.calinit = wlc_phy_cal_init_lcnphy; + pi->pi_fptr.chanset = wlc_phy_chanspec_set_lcnphy; + pi->pi_fptr.txpwrrecalc = wlc_phy_txpower_recalc_target_lcnphy; + pi->pi_fptr.txiqccget = wlc_lcnphy_get_tx_iqcc; + pi->pi_fptr.txiqccset = wlc_lcnphy_set_tx_iqcc; + pi->pi_fptr.txloccget = wlc_lcnphy_get_tx_locc; + pi->pi_fptr.radioloftget = wlc_lcnphy_get_radio_loft; + pi->pi_fptr.detach = wlc_phy_detach_lcnphy; + + if (!wlc_phy_txpwr_srom_read_lcnphy(pi)) + return false; + + if (LCNREV_IS(pi->pubpi.phy_rev, 1)) { + if (pi_lcn->lcnphy_tempsense_option == 3) { + pi->hwpwrctrl = true; + pi->hwpwrctrl_capable = true; + pi->temppwrctrl_capable = false; + } else { + pi->hwpwrctrl = false; + pi->hwpwrctrl_capable = false; + pi->temppwrctrl_capable = true; + } + } + + return true; +} + +static void wlc_lcnphy_set_rx_gain(struct brcms_phy *pi, u32 gain) +{ + u16 trsw, ext_lna, lna1, lna2, tia, biq0, biq1, gain0_15, gain16_19; + + trsw = (gain & ((u32) 1 << 28)) ? 0 : 1; + ext_lna = (u16) (gain >> 29) & 0x01; + lna1 = (u16) (gain >> 0) & 0x0f; + lna2 = (u16) (gain >> 4) & 0x0f; + tia = (u16) (gain >> 8) & 0xf; + biq0 = (u16) (gain >> 12) & 0xf; + biq1 = (u16) (gain >> 16) & 0xf; + + gain0_15 = (u16) ((lna1 & 0x3) | ((lna1 & 0x3) << 2) | + ((lna2 & 0x3) << 4) | ((lna2 & 0x3) << 6) | + ((tia & 0xf) << 8) | ((biq0 & 0xf) << 12)); + gain16_19 = biq1; + + mod_phy_reg(pi, 0x44d, (0x1 << 0), trsw << 0); + mod_phy_reg(pi, 0x4b1, (0x1 << 9), ext_lna << 9); + mod_phy_reg(pi, 0x4b1, (0x1 << 10), ext_lna << 10); + mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0); + mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + mod_phy_reg(pi, 0x4b1, (0x3 << 11), lna1 << 11); + mod_phy_reg(pi, 0x4e6, (0x3 << 3), lna1 << 3); + } + wlc_lcnphy_rx_gain_override_enable(pi, true); +} + +static u32 wlc_lcnphy_get_receive_power(struct brcms_phy *pi, s32 *gain_index) +{ + u32 received_power = 0; + s32 max_index = 0; + u32 gain_code = 0; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + max_index = 36; + if (*gain_index >= 0) + gain_code = lcnphy_23bitgaincode_table[*gain_index]; + + if (-1 == *gain_index) { + *gain_index = 0; + while ((*gain_index <= (s32) max_index) + && (received_power < 700)) { + wlc_lcnphy_set_rx_gain(pi, + lcnphy_23bitgaincode_table + [*gain_index]); + received_power = + wlc_lcnphy_measure_digital_power( + pi, + pi_lcn-> + lcnphy_noise_samples); + (*gain_index)++; + } + (*gain_index)--; + } else { + wlc_lcnphy_set_rx_gain(pi, gain_code); + received_power = + wlc_lcnphy_measure_digital_power(pi, + pi_lcn-> + lcnphy_noise_samples); + } + + return received_power; +} + +s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index) +{ + s32 gain = 0; + s32 nominal_power_db; + s32 log_val, gain_mismatch, desired_gain, input_power_offset_db, + input_power_db; + s32 received_power, temperature; + u32 power; + u32 msb1, msb2, val1, val2, diff1, diff2; + uint freq; + struct brcms_phy_lcnphy *pi_lcn = pi->u.pi_lcnphy; + + received_power = wlc_lcnphy_get_receive_power(pi, &gain_index); + + gain = lcnphy_gain_table[gain_index]; + + nominal_power_db = read_phy_reg(pi, 0x425) >> 8; + + power = (received_power * 16); + msb1 = ffs(power) - 1; + msb2 = msb1 + 1; + val1 = 1 << msb1; + val2 = 1 << msb2; + diff1 = (power - val1); + diff2 = (val2 - power); + if (diff1 < diff2) + log_val = msb1; + else + log_val = msb2; + + log_val = log_val * 3; + + gain_mismatch = (nominal_power_db / 2) - (log_val); + + desired_gain = gain + gain_mismatch; + + input_power_offset_db = read_phy_reg(pi, 0x434) & 0xFF; + + if (input_power_offset_db > 127) + input_power_offset_db -= 256; + + input_power_db = input_power_offset_db - desired_gain; + + input_power_db = + input_power_db + lcnphy_gain_index_offset_for_rssi[gain_index]; + + freq = wlc_phy_channel2freq(CHSPEC_CHANNEL(pi->radio_chanspec)); + if ((freq > 2427) && (freq <= 2467)) + input_power_db = input_power_db - 1; + + temperature = pi_lcn->lcnphy_lastsensed_temperature; + + if ((temperature - 15) < -30) + input_power_db = + input_power_db + + (((temperature - 10 - 25) * 286) >> 12) - + 7; + else if ((temperature - 15) < 4) + input_power_db = + input_power_db + + (((temperature - 10 - 25) * 286) >> 12) - + 3; + else + input_power_db = input_power_db + + (((temperature - 10 - 25) * 286) >> 12); + + wlc_lcnphy_rx_gain_override_enable(pi, 0); + + return input_power_db; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.h new file mode 100644 index 000000000000..f4a8ab09da43 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2010 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 _BRCM_PHY_LCN_H_ +#define _BRCM_PHY_LCN_H_ + +#include + +struct brcms_phy_lcnphy { + int lcnphy_txrf_sp_9_override; + u8 lcnphy_full_cal_channel; + u8 lcnphy_cal_counter; + u16 lcnphy_cal_temper; + bool lcnphy_recal; + + u8 lcnphy_rc_cap; + u32 lcnphy_mcs20_po; + + u8 lcnphy_tr_isolation_mid; + u8 lcnphy_tr_isolation_low; + u8 lcnphy_tr_isolation_hi; + + u8 lcnphy_bx_arch; + u8 lcnphy_rx_power_offset; + u8 lcnphy_rssi_vf; + u8 lcnphy_rssi_vc; + u8 lcnphy_rssi_gs; + u8 lcnphy_tssi_val; + u8 lcnphy_rssi_vf_lowtemp; + u8 lcnphy_rssi_vc_lowtemp; + u8 lcnphy_rssi_gs_lowtemp; + + u8 lcnphy_rssi_vf_hightemp; + u8 lcnphy_rssi_vc_hightemp; + u8 lcnphy_rssi_gs_hightemp; + + s16 lcnphy_pa0b0; + s16 lcnphy_pa0b1; + s16 lcnphy_pa0b2; + + u16 lcnphy_rawtempsense; + u8 lcnphy_measPower; + u8 lcnphy_tempsense_slope; + u8 lcnphy_freqoffset_corr; + u8 lcnphy_tempsense_option; + u8 lcnphy_tempcorrx; + bool lcnphy_iqcal_swp_dis; + bool lcnphy_hw_iqcal_en; + uint lcnphy_bandedge_corr; + bool lcnphy_spurmod; + u16 lcnphy_tssi_tx_cnt; + u16 lcnphy_tssi_idx; + u16 lcnphy_tssi_npt; + + u16 lcnphy_target_tx_freq; + s8 lcnphy_tx_power_idx_override; + u16 lcnphy_noise_samples; + + u32 lcnphy_papdRxGnIdx; + u32 lcnphy_papd_rxGnCtrl_init; + + u32 lcnphy_gain_idx_14_lowword; + u32 lcnphy_gain_idx_14_hiword; + u32 lcnphy_gain_idx_27_lowword; + u32 lcnphy_gain_idx_27_hiword; + s16 lcnphy_ofdmgainidxtableoffset; + s16 lcnphy_dsssgainidxtableoffset; + u32 lcnphy_tr_R_gain_val; + u32 lcnphy_tr_T_gain_val; + s8 lcnphy_input_pwr_offset_db; + u16 lcnphy_Med_Low_Gain_db; + u16 lcnphy_Very_Low_Gain_db; + s8 lcnphy_lastsensed_temperature; + s8 lcnphy_pkteng_rssi_slope; + u8 lcnphy_saved_tx_user_target[TXP_NUM_RATES]; + u8 lcnphy_volt_winner; + u8 lcnphy_volt_low; + u8 lcnphy_54_48_36_24mbps_backoff; + u8 lcnphy_11n_backoff; + u8 lcnphy_lowerofdm; + u8 lcnphy_cck; + u8 lcnphy_psat_2pt3_detected; + s32 lcnphy_lowest_Re_div_Im; + s8 lcnphy_final_papd_cal_idx; + u16 lcnphy_extstxctrl4; + u16 lcnphy_extstxctrl0; + u16 lcnphy_extstxctrl1; + s16 lcnphy_cck_dig_filt_type; + s16 lcnphy_ofdm_dig_filt_type; + struct lcnphy_cal_results lcnphy_cal_results; + + u8 lcnphy_psat_pwr; + u8 lcnphy_psat_indx; + s32 lcnphy_min_phase; + u8 lcnphy_final_idx; + u8 lcnphy_start_idx; + u8 lcnphy_current_index; + u16 lcnphy_logen_buf_1; + u16 lcnphy_local_ovr_2; + u16 lcnphy_local_oval_6; + u16 lcnphy_local_oval_5; + u16 lcnphy_logen_mixer_1; + + u8 lcnphy_aci_stat; + uint lcnphy_aci_start_time; + s8 lcnphy_tx_power_offset[TXP_NUM_RATES]; +}; +#endif /* _BRCM_PHY_LCN_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c new file mode 100644 index 000000000000..99dac9b8a082 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c @@ -0,0 +1,28721 @@ +/* + * Copyright (c) 2010 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "phy_int.h" +#include "phy_hal.h" +#include "phy_radio.h" +#include "phyreg_n.h" +#include "phytbl_n.h" +#include "soc.h" + +#define READ_RADIO_REG2(pi, radio_type, jspace, core, reg_name) \ + read_radio_reg(pi, radio_type##_##jspace##_##reg_name | \ + ((core == PHY_CORE_0) ? \ + radio_type##_##jspace##0 : \ + radio_type##_##jspace##1)) + +#define WRITE_RADIO_REG2(pi, radio_type, jspace, core, reg_name, value) \ + write_radio_reg(pi, radio_type##_##jspace##_##reg_name | \ + ((core == PHY_CORE_0) ? \ + radio_type##_##jspace##0 : \ + radio_type##_##jspace##1), value) + +#define WRITE_RADIO_SYN(pi, radio_type, reg_name, value) \ + write_radio_reg(pi, radio_type##_##SYN##_##reg_name, value) + +#define READ_RADIO_REG3(pi, radio_type, jspace, core, reg_name) \ + read_radio_reg(pi, ((core == PHY_CORE_0) ? \ + radio_type##_##jspace##0##_##reg_name : \ + radio_type##_##jspace##1##_##reg_name)) + +#define WRITE_RADIO_REG3(pi, radio_type, jspace, core, reg_name, value) \ + write_radio_reg(pi, ((core == PHY_CORE_0) ? \ + radio_type##_##jspace##0##_##reg_name : \ + radio_type##_##jspace##1##_##reg_name), \ + value) + +#define READ_RADIO_REG4(pi, radio_type, jspace, core, reg_name) \ + read_radio_reg(pi, ((core == PHY_CORE_0) ? \ + radio_type##_##reg_name##_##jspace##0 : \ + radio_type##_##reg_name##_##jspace##1)) + +#define WRITE_RADIO_REG4(pi, radio_type, jspace, core, reg_name, value) \ + write_radio_reg(pi, ((core == PHY_CORE_0) ? \ + radio_type##_##reg_name##_##jspace##0 : \ + radio_type##_##reg_name##_##jspace##1), \ + value) + +#define NPHY_ACI_MAX_UNDETECT_WINDOW_SZ 40 +#define NPHY_ACI_CHANNEL_DELTA 5 +#define NPHY_ACI_CHANNEL_SKIP 4 +#define NPHY_ACI_40MHZ_CHANNEL_DELTA 6 +#define NPHY_ACI_40MHZ_CHANNEL_SKIP 5 +#define NPHY_ACI_40MHZ_CHANNEL_DELTA_GE_REV3 6 +#define NPHY_ACI_40MHZ_CHANNEL_SKIP_GE_REV3 5 +#define NPHY_ACI_CHANNEL_DELTA_GE_REV3 4 +#define NPHY_ACI_CHANNEL_SKIP_GE_REV3 3 + +#define NPHY_NOISE_NOASSOC_GLITCH_TH_UP 2 + +#define NPHY_NOISE_NOASSOC_GLITCH_TH_DN 8 + +#define NPHY_NOISE_ASSOC_GLITCH_TH_UP 2 + +#define NPHY_NOISE_ASSOC_GLITCH_TH_DN 8 + +#define NPHY_NOISE_ASSOC_ACI_GLITCH_TH_UP 2 + +#define NPHY_NOISE_ASSOC_ACI_GLITCH_TH_DN 8 + +#define NPHY_NOISE_NOASSOC_ENTER_TH 400 + +#define NPHY_NOISE_ASSOC_ENTER_TH 400 + +#define NPHY_NOISE_ASSOC_RX_GLITCH_BADPLCP_ENTER_TH 400 + +#define NPHY_NOISE_CRSMINPWR_ARRAY_MAX_INDEX 44 +#define NPHY_NOISE_CRSMINPWR_ARRAY_MAX_INDEX_REV_7 56 + +#define NPHY_NOISE_NOASSOC_CRSIDX_INCR 16 + +#define NPHY_NOISE_ASSOC_CRSIDX_INCR 8 + +#define NPHY_IS_SROM_REINTERPRET NREV_GE(pi->pubpi.phy_rev, 5) + +#define NPHY_RSSICAL_MAXREAD 31 + +#define NPHY_RSSICAL_NPOLL 8 +#define NPHY_RSSICAL_MAXD (1<<20) +#define NPHY_MIN_RXIQ_PWR 2 + +#define NPHY_RSSICAL_W1_TARGET 25 +#define NPHY_RSSICAL_W2_TARGET NPHY_RSSICAL_W1_TARGET +#define NPHY_RSSICAL_NB_TARGET 0 + +#define NPHY_RSSICAL_W1_TARGET_REV3 29 +#define NPHY_RSSICAL_W2_TARGET_REV3 NPHY_RSSICAL_W1_TARGET_REV3 + +#define NPHY_CALSANITY_RSSI_NB_MAX_POS 9 +#define NPHY_CALSANITY_RSSI_NB_MAX_NEG -9 +#define NPHY_CALSANITY_RSSI_W1_MAX_POS 12 +#define NPHY_CALSANITY_RSSI_W1_MAX_NEG (NPHY_RSSICAL_W1_TARGET - \ + NPHY_RSSICAL_MAXREAD) +#define NPHY_CALSANITY_RSSI_W2_MAX_POS NPHY_CALSANITY_RSSI_W1_MAX_POS +#define NPHY_CALSANITY_RSSI_W2_MAX_NEG (NPHY_RSSICAL_W2_TARGET - \ + NPHY_RSSICAL_MAXREAD) +#define NPHY_RSSI_SXT(x) ((s8) (-((x) & 0x20) + ((x) & 0x1f))) +#define NPHY_RSSI_NB_VIOL(x) (((x) > NPHY_CALSANITY_RSSI_NB_MAX_POS) || \ + ((x) < NPHY_CALSANITY_RSSI_NB_MAX_NEG)) +#define NPHY_RSSI_W1_VIOL(x) (((x) > NPHY_CALSANITY_RSSI_W1_MAX_POS) || \ + ((x) < NPHY_CALSANITY_RSSI_W1_MAX_NEG)) +#define NPHY_RSSI_W2_VIOL(x) (((x) > NPHY_CALSANITY_RSSI_W2_MAX_POS) || \ + ((x) < NPHY_CALSANITY_RSSI_W2_MAX_NEG)) + +#define NPHY_IQCAL_NUMGAINS 9 +#define NPHY_N_GCTL 0x66 + +#define NPHY_PAPD_EPS_TBL_SIZE 64 +#define NPHY_PAPD_SCL_TBL_SIZE 64 +#define NPHY_NUM_DIG_FILT_COEFFS 15 + +#define NPHY_PAPD_COMP_OFF 0 +#define NPHY_PAPD_COMP_ON 1 + +#define NPHY_SROM_TEMPSHIFT 32 +#define NPHY_SROM_MAXTEMPOFFSET 16 +#define NPHY_SROM_MINTEMPOFFSET -16 + +#define NPHY_CAL_MAXTEMPDELTA 64 + +#define NPHY_NOISEVAR_TBLLEN40 256 +#define NPHY_NOISEVAR_TBLLEN20 128 + +#define NPHY_ANARXLPFBW_REDUCTIONFACT 7 + +#define NPHY_ADJUSTED_MINCRSPOWER 0x1e + +/* 5357 Chip specific ChipControl register bits */ +#define CCTRL5357_EXTPA (1<<14) /* extPA in ChipControl 1, bit 14 */ +#define CCTRL5357_ANT_MUX_2o3 (1<<15) /* 2o3 in ChipControl 1, bit 15 */ + +#define NPHY_CAL_TSSISAMPS 64 +#define NPHY_TEST_TONE_FREQ_40MHz 4000 +#define NPHY_TEST_TONE_FREQ_20MHz 2500 + +#define MAX_205x_RCAL_WAITLOOPS 10000 + +#define NPHY_RXCAL_TONEAMP 181 +#define NPHY_RXCAL_TONEFREQ_40MHz 4000 +#define NPHY_RXCAL_TONEFREQ_20MHz 2000 + +#define TXFILT_SHAPING_OFDM20 0 +#define TXFILT_SHAPING_OFDM40 1 +#define TXFILT_SHAPING_CCK 2 +#define TXFILT_DEFAULT_OFDM20 3 +#define TXFILT_DEFAULT_OFDM40 4 + +struct nphy_iqcal_params { + u16 txlpf; + u16 txgm; + u16 pga; + u16 pad; + u16 ipa; + u16 cal_gain; + u16 ncorr[5]; +}; + +struct nphy_txiqcal_ladder { + u8 percent; + u8 g_env; +}; + +struct nphy_ipa_txcalgains { + struct nphy_txgains gains; + bool useindex; + u8 index; +}; + +struct nphy_papd_restore_state { + u16 fbmix[2]; + u16 vga_master[2]; + u16 intpa_master[2]; + u16 afectrl[2]; + u16 afeoverride[2]; + u16 pwrup[2]; + u16 atten[2]; + u16 mm; +}; + +struct nphy_ipa_txrxgain { + u16 hpvga; + u16 lpf_biq1; + u16 lpf_biq0; + u16 lna2; + u16 lna1; + s8 txpwrindex; +}; + +#define NPHY_IPA_RXCAL_MAXGAININDEX (6 - 1) + +static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_5GHz[] = { + {0, 0, 0, 0, 0, 100}, + {0, 0, 0, 0, 0, 50}, + {0, 0, 0, 0, 0, -1}, + {0, 0, 0, 3, 0, -1}, + {0, 0, 3, 3, 0, -1}, + {0, 2, 3, 3, 0, -1} +}; + +static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_2GHz[] = { + {0, 0, 0, 0, 0, 128}, + {0, 0, 0, 0, 0, 70}, + {0, 0, 0, 0, 0, 20}, + {0, 0, 0, 3, 0, 20}, + {0, 0, 3, 3, 0, 20}, + {0, 2, 3, 3, 0, 20} +}; + +static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_5GHz_rev7[] = { + {0, 0, 0, 0, 0, 100}, + {0, 0, 0, 0, 0, 50}, + {0, 0, 0, 0, 0, -1}, + {0, 0, 0, 3, 0, -1}, + {0, 0, 3, 3, 0, -1}, + {0, 0, 5, 3, 0, -1} +}; + +static const struct nphy_ipa_txrxgain nphy_ipa_rxcal_gaintbl_2GHz_rev7[] = { + {0, 0, 0, 0, 0, 10}, + {0, 0, 0, 1, 0, 10}, + {0, 0, 1, 2, 0, 10}, + {0, 0, 1, 3, 0, 10}, + {0, 0, 4, 3, 0, 10}, + {0, 0, 6, 3, 0, 10} +}; + +enum { + NPHY_RXCAL_GAIN_INIT = 0, + NPHY_RXCAL_GAIN_UP, + NPHY_RXCAL_GAIN_DOWN +}; + +#define wlc_phy_get_papd_nphy(pi) \ + (read_phy_reg((pi), 0x1e7) & \ + ((0x1 << 15) | \ + (0x1 << 14) | \ + (0x1 << 13))) + +static const u16 NPHY_IPA_REV4_txdigi_filtcoeffs[][NPHY_NUM_DIG_FILT_COEFFS] = { + {-377, 137, -407, 208, -1527, 956, 93, 186, 93, + 230, -44, 230, 201, -191, 201}, + {-77, 20, -98, 49, -93, 60, 56, 111, 56, 26, -5, + 26, 34, -32, 34}, + {-360, 164, -376, 164, -1533, 576, 308, -314, 308, + 121, -73, 121, 91, 124, 91}, + {-295, 200, -363, 142, -1391, 826, 151, 301, 151, + 151, 301, 151, 602, -752, 602}, + {-92, 58, -96, 49, -104, 44, 17, 35, 17, + 12, 25, 12, 13, 27, 13}, + {-375, 136, -399, 209, -1479, 949, 130, 260, 130, + 230, -44, 230, 201, -191, 201}, + {0xed9, 0xc8, 0xe95, 0x8e, 0xa91, 0x33a, 0x97, 0x12d, 0x97, + 0x97, 0x12d, 0x97, 0x25a, 0xd10, 0x25a} +}; + +struct chan_info_nphy_2055 { + u16 chan; + u16 freq; + uint unknown; + u8 RF_pll_ref; + u8 RF_rf_pll_mod1; + u8 RF_rf_pll_mod0; + u8 RF_vco_cap_tail; + u8 RF_vco_cal1; + u8 RF_vco_cal2; + u8 RF_pll_lf_c1; + u8 RF_pll_lf_r1; + u8 RF_pll_lf_c2; + u8 RF_lgbuf_cen_buf; + u8 RF_lgen_tune1; + u8 RF_lgen_tune2; + u8 RF_core1_lgbuf_a_tune; + u8 RF_core1_lgbuf_g_tune; + u8 RF_core1_rxrf_reg1; + u8 RF_core1_tx_pga_pad_tn; + u8 RF_core1_tx_mx_bgtrim; + u8 RF_core2_lgbuf_a_tune; + u8 RF_core2_lgbuf_g_tune; + u8 RF_core2_rxrf_reg1; + u8 RF_core2_tx_pga_pad_tn; + u8 RF_core2_tx_mx_bgtrim; + u16 PHY_BW1a; + u16 PHY_BW2; + u16 PHY_BW3; + u16 PHY_BW4; + u16 PHY_BW5; + u16 PHY_BW6; +}; + +struct chan_info_nphy_radio205x { + u16 chan; + u16 freq; + u8 RF_SYN_pll_vcocal1; + u8 RF_SYN_pll_vcocal2; + u8 RF_SYN_pll_refdiv; + u8 RF_SYN_pll_mmd2; + u8 RF_SYN_pll_mmd1; + u8 RF_SYN_pll_loopfilter1; + u8 RF_SYN_pll_loopfilter2; + u8 RF_SYN_pll_loopfilter3; + u8 RF_SYN_pll_loopfilter4; + u8 RF_SYN_pll_loopfilter5; + u8 RF_SYN_reserved_addr27; + u8 RF_SYN_reserved_addr28; + u8 RF_SYN_reserved_addr29; + u8 RF_SYN_logen_VCOBUF1; + u8 RF_SYN_logen_MIXER2; + u8 RF_SYN_logen_BUF3; + u8 RF_SYN_logen_BUF4; + u8 RF_RX0_lnaa_tune; + u8 RF_RX0_lnag_tune; + u8 RF_TX0_intpaa_boost_tune; + u8 RF_TX0_intpag_boost_tune; + u8 RF_TX0_pada_boost_tune; + u8 RF_TX0_padg_boost_tune; + u8 RF_TX0_pgaa_boost_tune; + u8 RF_TX0_pgag_boost_tune; + u8 RF_TX0_mixa_boost_tune; + u8 RF_TX0_mixg_boost_tune; + u8 RF_RX1_lnaa_tune; + u8 RF_RX1_lnag_tune; + u8 RF_TX1_intpaa_boost_tune; + u8 RF_TX1_intpag_boost_tune; + u8 RF_TX1_pada_boost_tune; + u8 RF_TX1_padg_boost_tune; + u8 RF_TX1_pgaa_boost_tune; + u8 RF_TX1_pgag_boost_tune; + u8 RF_TX1_mixa_boost_tune; + u8 RF_TX1_mixg_boost_tune; + u16 PHY_BW1a; + u16 PHY_BW2; + u16 PHY_BW3; + u16 PHY_BW4; + u16 PHY_BW5; + u16 PHY_BW6; +}; + +struct chan_info_nphy_radio2057 { + u16 chan; + u16 freq; + u8 RF_vcocal_countval0; + u8 RF_vcocal_countval1; + u8 RF_rfpll_refmaster_sparextalsize; + u8 RF_rfpll_loopfilter_r1; + u8 RF_rfpll_loopfilter_c2; + u8 RF_rfpll_loopfilter_c1; + u8 RF_cp_kpd_idac; + u8 RF_rfpll_mmd0; + u8 RF_rfpll_mmd1; + u8 RF_vcobuf_tune; + u8 RF_logen_mx2g_tune; + u8 RF_logen_mx5g_tune; + u8 RF_logen_indbuf2g_tune; + u8 RF_logen_indbuf5g_tune; + u8 RF_txmix2g_tune_boost_pu_core0; + u8 RF_pad2g_tune_pus_core0; + u8 RF_pga_boost_tune_core0; + u8 RF_txmix5g_boost_tune_core0; + u8 RF_pad5g_tune_misc_pus_core0; + u8 RF_lna2g_tune_core0; + u8 RF_lna5g_tune_core0; + u8 RF_txmix2g_tune_boost_pu_core1; + u8 RF_pad2g_tune_pus_core1; + u8 RF_pga_boost_tune_core1; + u8 RF_txmix5g_boost_tune_core1; + u8 RF_pad5g_tune_misc_pus_core1; + u8 RF_lna2g_tune_core1; + u8 RF_lna5g_tune_core1; + u16 PHY_BW1a; + u16 PHY_BW2; + u16 PHY_BW3; + u16 PHY_BW4; + u16 PHY_BW5; + u16 PHY_BW6; +}; + +struct chan_info_nphy_radio2057_rev5 { + u16 chan; + u16 freq; + u8 RF_vcocal_countval0; + u8 RF_vcocal_countval1; + u8 RF_rfpll_refmaster_sparextalsize; + u8 RF_rfpll_loopfilter_r1; + u8 RF_rfpll_loopfilter_c2; + u8 RF_rfpll_loopfilter_c1; + u8 RF_cp_kpd_idac; + u8 RF_rfpll_mmd0; + u8 RF_rfpll_mmd1; + u8 RF_vcobuf_tune; + u8 RF_logen_mx2g_tune; + u8 RF_logen_indbuf2g_tune; + u8 RF_txmix2g_tune_boost_pu_core0; + u8 RF_pad2g_tune_pus_core0; + u8 RF_lna2g_tune_core0; + u8 RF_txmix2g_tune_boost_pu_core1; + u8 RF_pad2g_tune_pus_core1; + u8 RF_lna2g_tune_core1; + u16 PHY_BW1a; + u16 PHY_BW2; + u16 PHY_BW3; + u16 PHY_BW4; + u16 PHY_BW5; + u16 PHY_BW6; +}; + +struct nphy_sfo_cfg { + u16 PHY_BW1a; + u16 PHY_BW2; + u16 PHY_BW3; + u16 PHY_BW4; + u16 PHY_BW5; + u16 PHY_BW6; +}; + +static const struct chan_info_nphy_2055 chan_info_nphy_2055[] = { + { + 184, 4920, 3280, 0x71, 0x01, 0xEC, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7B4, 0x7B0, 0x7AC, 0x214, 0x215, 0x216}, + { + 186, 4930, 3287, 0x71, 0x01, 0xED, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7B8, 0x7B4, 0x7B0, 0x213, 0x214, 0x215}, + { + 188, 4940, 3293, 0x71, 0x01, 0xEE, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7BC, 0x7B8, 0x7B4, 0x212, 0x213, 0x214}, + { + 190, 4950, 3300, 0x71, 0x01, 0xEF, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7C0, 0x7BC, 0x7B8, 0x211, 0x212, 0x213}, + { + 192, 4960, 3307, 0x71, 0x01, 0xF0, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7C4, 0x7C0, 0x7BC, 0x20F, 0x211, 0x212}, + { + 194, 4970, 3313, 0x71, 0x01, 0xF1, 0x0F, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xEE, 0xEE, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7C8, 0x7C4, 0x7C0, 0x20E, 0x20F, 0x211}, + { + 196, 4980, 3320, 0x71, 0x01, 0xF2, 0x0E, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7CC, 0x7C8, 0x7C4, 0x20D, 0x20E, 0x20F}, + { + 198, 4990, 3327, 0x71, 0x01, 0xF3, 0x0E, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7D0, 0x7CC, 0x7C8, 0x20C, 0x20D, 0x20E}, + { + 200, 5000, 3333, 0x71, 0x01, 0xF4, 0x0E, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7D4, 0x7D0, 0x7CC, 0x20B, 0x20C, 0x20D}, + { + 202, 5010, 3340, 0x71, 0x01, 0xF5, 0x0E, 0xFF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xDD, 0xDD, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7D8, 0x7D4, 0x7D0, 0x20A, 0x20B, 0x20C}, + { + 204, 5020, 3347, 0x71, 0x01, 0xF6, 0x0E, 0xF7, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7DC, 0x7D8, 0x7D4, 0x209, 0x20A, 0x20B}, + { + 206, 5030, 3353, 0x71, 0x01, 0xF7, 0x0E, 0xF7, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7E0, 0x7DC, 0x7D8, 0x208, 0x209, 0x20A}, + { + 208, 5040, 3360, 0x71, 0x01, 0xF8, 0x0D, 0xEF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7E4, 0x7E0, 0x7DC, 0x207, 0x208, 0x209}, + { + 210, 5050, 3367, 0x71, 0x01, 0xF9, 0x0D, 0xEF, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xCC, 0xCC, 0xFF, 0x00, 0x0F, 0x0F, 0x8F, 0xFF, 0x00, 0x0F, + 0x0F, 0x8F, 0x7E8, 0x7E4, 0x7E0, 0x206, 0x207, 0x208}, + { + 212, 5060, 3373, 0x71, 0x01, 0xFA, 0x0D, 0xE6, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, 0x8E, 0xFF, 0x00, 0x0E, + 0x0F, 0x8E, 0x7EC, 0x7E8, 0x7E4, 0x205, 0x206, 0x207}, + { + 214, 5070, 3380, 0x71, 0x01, 0xFB, 0x0D, 0xE6, 0x01, 0x04, 0x0A, + 0x00, 0x8F, 0xBB, 0xBB, 0xFF, 0x00, 0x0E, 0x0F, 0x8E, 0xFF, 0x00, 0x0E, + 0x0F, 0x8E, 0x7F0, 0x7EC, 0x7E8, 0x204, 0x205, 0x206}, + { + 216, 5080, 3387, 0x71, 0x01, 0xFC, 0x0D, 0xDE, 0x01, 0x04, 0x0A, + 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, 0x8D, 0xEE, 0x00, 0x0E, + 0x0F, 0x8D, 0x7F4, 0x7F0, 0x7EC, 0x203, 0x204, 0x205}, + { + 218, 5090, 3393, 0x71, 0x01, 0xFD, 0x0D, 0xDE, 0x01, 0x04, 0x0A, + 0x00, 0x8E, 0xBB, 0xBB, 0xEE, 0x00, 0x0E, 0x0F, 0x8D, 0xEE, 0x00, 0x0E, + 0x0F, 0x8D, 0x7F8, 0x7F4, 0x7F0, 0x202, 0x203, 0x204}, + { + 220, 5100, 3400, 0x71, 0x01, 0xFE, 0x0C, 0xD6, 0x01, 0x04, 0x0A, + 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, 0x8D, 0xEE, 0x00, 0x0D, + 0x0F, 0x8D, 0x7FC, 0x7F8, 0x7F4, 0x201, 0x202, 0x203}, + { + 222, 5110, 3407, 0x71, 0x01, 0xFF, 0x0C, 0xD6, 0x01, 0x04, 0x0A, + 0x00, 0x8E, 0xAA, 0xAA, 0xEE, 0x00, 0x0D, 0x0F, 0x8D, 0xEE, 0x00, 0x0D, + 0x0F, 0x8D, 0x800, 0x7FC, 0x7F8, 0x200, 0x201, 0x202}, + { + 224, 5120, 3413, 0x71, 0x02, 0x00, 0x0C, 0xCE, 0x01, 0x04, 0x0A, + 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, 0x8C, 0xDD, 0x00, 0x0D, + 0x0F, 0x8C, 0x804, 0x800, 0x7FC, 0x1FF, 0x200, 0x201}, + { + 226, 5130, 3420, 0x71, 0x02, 0x01, 0x0C, 0xCE, 0x01, 0x04, 0x0A, + 0x00, 0x8D, 0xAA, 0xAA, 0xDD, 0x00, 0x0D, 0x0F, 0x8C, 0xDD, 0x00, 0x0D, + 0x0F, 0x8C, 0x808, 0x804, 0x800, 0x1FE, 0x1FF, 0x200}, + { + 228, 5140, 3427, 0x71, 0x02, 0x02, 0x0C, 0xC6, 0x01, 0x04, 0x0A, + 0x00, 0x8D, 0x99, 0x99, 0xDD, 0x00, 0x0C, 0x0E, 0x8B, 0xDD, 0x00, 0x0C, + 0x0E, 0x8B, 0x80C, 0x808, 0x804, 0x1FD, 0x1FE, 0x1FF}, + { + 32, 5160, 3440, 0x71, 0x02, 0x04, 0x0B, 0xBE, 0x01, 0x04, 0x0A, + 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, 0x8A, 0xCC, 0x00, 0x0B, + 0x0D, 0x8A, 0x814, 0x810, 0x80C, 0x1FB, 0x1FC, 0x1FD}, + { + 34, 5170, 3447, 0x71, 0x02, 0x05, 0x0B, 0xBE, 0x01, 0x04, 0x0A, + 0x00, 0x8C, 0x99, 0x99, 0xCC, 0x00, 0x0B, 0x0D, 0x8A, 0xCC, 0x00, 0x0B, + 0x0D, 0x8A, 0x818, 0x814, 0x810, 0x1FA, 0x1FB, 0x1FC}, + { + 36, 5180, 3453, 0x71, 0x02, 0x06, 0x0B, 0xB6, 0x01, 0x04, 0x0A, + 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, 0x89, 0xCC, 0x00, 0x0B, + 0x0C, 0x89, 0x81C, 0x818, 0x814, 0x1F9, 0x1FA, 0x1FB}, + { + 38, 5190, 3460, 0x71, 0x02, 0x07, 0x0B, 0xB6, 0x01, 0x04, 0x0A, + 0x00, 0x8C, 0x88, 0x88, 0xCC, 0x00, 0x0B, 0x0C, 0x89, 0xCC, 0x00, 0x0B, + 0x0C, 0x89, 0x820, 0x81C, 0x818, 0x1F8, 0x1F9, 0x1FA}, + { + 40, 5200, 3467, 0x71, 0x02, 0x08, 0x0B, 0xAF, 0x01, 0x04, 0x0A, + 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, 0x89, 0xBB, 0x00, 0x0A, + 0x0B, 0x89, 0x824, 0x820, 0x81C, 0x1F7, 0x1F8, 0x1F9}, + { + 42, 5210, 3473, 0x71, 0x02, 0x09, 0x0B, 0xAF, 0x01, 0x04, 0x0A, + 0x00, 0x8B, 0x88, 0x88, 0xBB, 0x00, 0x0A, 0x0B, 0x89, 0xBB, 0x00, 0x0A, + 0x0B, 0x89, 0x828, 0x824, 0x820, 0x1F6, 0x1F7, 0x1F8}, + { + 44, 5220, 3480, 0x71, 0x02, 0x0A, 0x0A, 0xA7, 0x01, 0x04, 0x0A, + 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, 0x88, 0xBB, 0x00, 0x09, + 0x0A, 0x88, 0x82C, 0x828, 0x824, 0x1F5, 0x1F6, 0x1F7}, + { + 46, 5230, 3487, 0x71, 0x02, 0x0B, 0x0A, 0xA7, 0x01, 0x04, 0x0A, + 0x00, 0x8B, 0x77, 0x77, 0xBB, 0x00, 0x09, 0x0A, 0x88, 0xBB, 0x00, 0x09, + 0x0A, 0x88, 0x830, 0x82C, 0x828, 0x1F4, 0x1F5, 0x1F6}, + { + 48, 5240, 3493, 0x71, 0x02, 0x0C, 0x0A, 0xA0, 0x01, 0x04, 0x0A, + 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, 0x87, 0xAA, 0x00, 0x09, + 0x0A, 0x87, 0x834, 0x830, 0x82C, 0x1F3, 0x1F4, 0x1F5}, + { + 50, 5250, 3500, 0x71, 0x02, 0x0D, 0x0A, 0xA0, 0x01, 0x04, 0x0A, + 0x00, 0x8A, 0x77, 0x77, 0xAA, 0x00, 0x09, 0x0A, 0x87, 0xAA, 0x00, 0x09, + 0x0A, 0x87, 0x838, 0x834, 0x830, 0x1F2, 0x1F3, 0x1F4}, + { + 52, 5260, 3507, 0x71, 0x02, 0x0E, 0x0A, 0x98, 0x01, 0x04, 0x0A, + 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, 0x87, 0xAA, 0x00, 0x08, + 0x09, 0x87, 0x83C, 0x838, 0x834, 0x1F1, 0x1F2, 0x1F3}, + { + 54, 5270, 3513, 0x71, 0x02, 0x0F, 0x0A, 0x98, 0x01, 0x04, 0x0A, + 0x00, 0x8A, 0x66, 0x66, 0xAA, 0x00, 0x08, 0x09, 0x87, 0xAA, 0x00, 0x08, + 0x09, 0x87, 0x840, 0x83C, 0x838, 0x1F0, 0x1F1, 0x1F2}, + { + 56, 5280, 3520, 0x71, 0x02, 0x10, 0x09, 0x91, 0x01, 0x04, 0x0A, + 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, 0x86, 0x99, 0x00, 0x08, + 0x08, 0x86, 0x844, 0x840, 0x83C, 0x1F0, 0x1F0, 0x1F1}, + { + 58, 5290, 3527, 0x71, 0x02, 0x11, 0x09, 0x91, 0x01, 0x04, 0x0A, + 0x00, 0x89, 0x66, 0x66, 0x99, 0x00, 0x08, 0x08, 0x86, 0x99, 0x00, 0x08, + 0x08, 0x86, 0x848, 0x844, 0x840, 0x1EF, 0x1F0, 0x1F0}, + { + 60, 5300, 3533, 0x71, 0x02, 0x12, 0x09, 0x8A, 0x01, 0x04, 0x0A, + 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, 0x85, 0x99, 0x00, 0x08, + 0x07, 0x85, 0x84C, 0x848, 0x844, 0x1EE, 0x1EF, 0x1F0}, + { + 62, 5310, 3540, 0x71, 0x02, 0x13, 0x09, 0x8A, 0x01, 0x04, 0x0A, + 0x00, 0x89, 0x55, 0x55, 0x99, 0x00, 0x08, 0x07, 0x85, 0x99, 0x00, 0x08, + 0x07, 0x85, 0x850, 0x84C, 0x848, 0x1ED, 0x1EE, 0x1EF}, + { + 64, 5320, 3547, 0x71, 0x02, 0x14, 0x09, 0x83, 0x01, 0x04, 0x0A, + 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, 0x84, 0x88, 0x00, 0x07, + 0x07, 0x84, 0x854, 0x850, 0x84C, 0x1EC, 0x1ED, 0x1EE}, + { + 66, 5330, 3553, 0x71, 0x02, 0x15, 0x09, 0x83, 0x01, 0x04, 0x0A, + 0x00, 0x88, 0x55, 0x55, 0x88, 0x00, 0x07, 0x07, 0x84, 0x88, 0x00, 0x07, + 0x07, 0x84, 0x858, 0x854, 0x850, 0x1EB, 0x1EC, 0x1ED}, + { + 68, 5340, 3560, 0x71, 0x02, 0x16, 0x08, 0x7C, 0x01, 0x04, 0x0A, + 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, 0x84, 0x88, 0x00, 0x07, + 0x06, 0x84, 0x85C, 0x858, 0x854, 0x1EA, 0x1EB, 0x1EC}, + { + 70, 5350, 3567, 0x71, 0x02, 0x17, 0x08, 0x7C, 0x01, 0x04, 0x0A, + 0x00, 0x88, 0x44, 0x44, 0x88, 0x00, 0x07, 0x06, 0x84, 0x88, 0x00, 0x07, + 0x06, 0x84, 0x860, 0x85C, 0x858, 0x1E9, 0x1EA, 0x1EB}, + { + 72, 5360, 3573, 0x71, 0x02, 0x18, 0x08, 0x75, 0x01, 0x04, 0x0A, + 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, 0x83, 0x77, 0x00, 0x06, + 0x05, 0x83, 0x864, 0x860, 0x85C, 0x1E8, 0x1E9, 0x1EA}, + { + 74, 5370, 3580, 0x71, 0x02, 0x19, 0x08, 0x75, 0x01, 0x04, 0x0A, + 0x00, 0x87, 0x44, 0x44, 0x77, 0x00, 0x06, 0x05, 0x83, 0x77, 0x00, 0x06, + 0x05, 0x83, 0x868, 0x864, 0x860, 0x1E7, 0x1E8, 0x1E9}, + { + 76, 5380, 3587, 0x71, 0x02, 0x1A, 0x08, 0x6E, 0x01, 0x04, 0x0A, + 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, 0x82, 0x77, 0x00, 0x06, + 0x04, 0x82, 0x86C, 0x868, 0x864, 0x1E6, 0x1E7, 0x1E8}, + { + 78, 5390, 3593, 0x71, 0x02, 0x1B, 0x08, 0x6E, 0x01, 0x04, 0x0A, + 0x00, 0x87, 0x33, 0x33, 0x77, 0x00, 0x06, 0x04, 0x82, 0x77, 0x00, 0x06, + 0x04, 0x82, 0x870, 0x86C, 0x868, 0x1E5, 0x1E6, 0x1E7}, + { + 80, 5400, 3600, 0x71, 0x02, 0x1C, 0x07, 0x67, 0x01, 0x04, 0x0A, + 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, 0x81, 0x66, 0x00, 0x05, + 0x04, 0x81, 0x874, 0x870, 0x86C, 0x1E5, 0x1E5, 0x1E6}, + { + 82, 5410, 3607, 0x71, 0x02, 0x1D, 0x07, 0x67, 0x01, 0x04, 0x0A, + 0x00, 0x86, 0x33, 0x33, 0x66, 0x00, 0x05, 0x04, 0x81, 0x66, 0x00, 0x05, + 0x04, 0x81, 0x878, 0x874, 0x870, 0x1E4, 0x1E5, 0x1E5}, + { + 84, 5420, 3613, 0x71, 0x02, 0x1E, 0x07, 0x61, 0x01, 0x04, 0x0A, + 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, 0x80, 0x66, 0x00, 0x05, + 0x03, 0x80, 0x87C, 0x878, 0x874, 0x1E3, 0x1E4, 0x1E5}, + { + 86, 5430, 3620, 0x71, 0x02, 0x1F, 0x07, 0x61, 0x01, 0x04, 0x0A, + 0x00, 0x86, 0x22, 0x22, 0x66, 0x00, 0x05, 0x03, 0x80, 0x66, 0x00, 0x05, + 0x03, 0x80, 0x880, 0x87C, 0x878, 0x1E2, 0x1E3, 0x1E4}, + { + 88, 5440, 3627, 0x71, 0x02, 0x20, 0x07, 0x5A, 0x01, 0x04, 0x0A, + 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, 0x80, 0x55, 0x00, 0x04, + 0x02, 0x80, 0x884, 0x880, 0x87C, 0x1E1, 0x1E2, 0x1E3}, + { + 90, 5450, 3633, 0x71, 0x02, 0x21, 0x07, 0x5A, 0x01, 0x04, 0x0A, + 0x00, 0x85, 0x22, 0x22, 0x55, 0x00, 0x04, 0x02, 0x80, 0x55, 0x00, 0x04, + 0x02, 0x80, 0x888, 0x884, 0x880, 0x1E0, 0x1E1, 0x1E2}, + { + 92, 5460, 3640, 0x71, 0x02, 0x22, 0x06, 0x53, 0x01, 0x04, 0x0A, + 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, 0x80, 0x55, 0x00, 0x04, + 0x01, 0x80, 0x88C, 0x888, 0x884, 0x1DF, 0x1E0, 0x1E1}, + { + 94, 5470, 3647, 0x71, 0x02, 0x23, 0x06, 0x53, 0x01, 0x04, 0x0A, + 0x00, 0x85, 0x11, 0x11, 0x55, 0x00, 0x04, 0x01, 0x80, 0x55, 0x00, 0x04, + 0x01, 0x80, 0x890, 0x88C, 0x888, 0x1DE, 0x1DF, 0x1E0}, + { + 96, 5480, 3653, 0x71, 0x02, 0x24, 0x06, 0x4D, 0x01, 0x04, 0x0A, + 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, + 0x00, 0x80, 0x894, 0x890, 0x88C, 0x1DD, 0x1DE, 0x1DF}, + { + 98, 5490, 3660, 0x71, 0x02, 0x25, 0x06, 0x4D, 0x01, 0x04, 0x0A, + 0x00, 0x84, 0x11, 0x11, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, + 0x00, 0x80, 0x898, 0x894, 0x890, 0x1DD, 0x1DD, 0x1DE}, + { + 100, 5500, 3667, 0x71, 0x02, 0x26, 0x06, 0x47, 0x01, 0x04, 0x0A, + 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, + 0x00, 0x80, 0x89C, 0x898, 0x894, 0x1DC, 0x1DD, 0x1DD}, + { + 102, 5510, 3673, 0x71, 0x02, 0x27, 0x06, 0x47, 0x01, 0x04, 0x0A, + 0x00, 0x84, 0x00, 0x00, 0x44, 0x00, 0x03, 0x00, 0x80, 0x44, 0x00, 0x03, + 0x00, 0x80, 0x8A0, 0x89C, 0x898, 0x1DB, 0x1DC, 0x1DD}, + { + 104, 5520, 3680, 0x71, 0x02, 0x28, 0x05, 0x40, 0x01, 0x04, 0x0A, + 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, + 0x00, 0x80, 0x8A4, 0x8A0, 0x89C, 0x1DA, 0x1DB, 0x1DC}, + { + 106, 5530, 3687, 0x71, 0x02, 0x29, 0x05, 0x40, 0x01, 0x04, 0x0A, + 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, + 0x00, 0x80, 0x8A8, 0x8A4, 0x8A0, 0x1D9, 0x1DA, 0x1DB}, + { + 108, 5540, 3693, 0x71, 0x02, 0x2A, 0x05, 0x3A, 0x01, 0x04, 0x0A, + 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, + 0x00, 0x80, 0x8AC, 0x8A8, 0x8A4, 0x1D8, 0x1D9, 0x1DA}, + { + 110, 5550, 3700, 0x71, 0x02, 0x2B, 0x05, 0x3A, 0x01, 0x04, 0x0A, + 0x00, 0x83, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x80, 0x33, 0x00, 0x02, + 0x00, 0x80, 0x8B0, 0x8AC, 0x8A8, 0x1D7, 0x1D8, 0x1D9}, + { + 112, 5560, 3707, 0x71, 0x02, 0x2C, 0x05, 0x34, 0x01, 0x04, 0x0A, + 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, + 0x00, 0x80, 0x8B4, 0x8B0, 0x8AC, 0x1D7, 0x1D7, 0x1D8}, + { + 114, 5570, 3713, 0x71, 0x02, 0x2D, 0x05, 0x34, 0x01, 0x04, 0x0A, + 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, + 0x00, 0x80, 0x8B8, 0x8B4, 0x8B0, 0x1D6, 0x1D7, 0x1D7}, + { + 116, 5580, 3720, 0x71, 0x02, 0x2E, 0x04, 0x2E, 0x01, 0x04, 0x0A, + 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, + 0x00, 0x80, 0x8BC, 0x8B8, 0x8B4, 0x1D5, 0x1D6, 0x1D7}, + { + 118, 5590, 3727, 0x71, 0x02, 0x2F, 0x04, 0x2E, 0x01, 0x04, 0x0A, + 0x00, 0x82, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x80, 0x22, 0x00, 0x01, + 0x00, 0x80, 0x8C0, 0x8BC, 0x8B8, 0x1D4, 0x1D5, 0x1D6}, + { + 120, 5600, 3733, 0x71, 0x02, 0x30, 0x04, 0x28, 0x01, 0x04, 0x0A, + 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, 0x80, 0x11, 0x00, 0x01, + 0x00, 0x80, 0x8C4, 0x8C0, 0x8BC, 0x1D3, 0x1D4, 0x1D5}, + { + 122, 5610, 3740, 0x71, 0x02, 0x31, 0x04, 0x28, 0x01, 0x04, 0x0A, + 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, 0x80, 0x11, 0x00, 0x01, + 0x00, 0x80, 0x8C8, 0x8C4, 0x8C0, 0x1D2, 0x1D3, 0x1D4}, + { + 124, 5620, 3747, 0x71, 0x02, 0x32, 0x04, 0x21, 0x01, 0x04, 0x0A, + 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, + 0x00, 0x80, 0x8CC, 0x8C8, 0x8C4, 0x1D2, 0x1D2, 0x1D3}, + { + 126, 5630, 3753, 0x71, 0x02, 0x33, 0x04, 0x21, 0x01, 0x04, 0x0A, + 0x00, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, + 0x00, 0x80, 0x8D0, 0x8CC, 0x8C8, 0x1D1, 0x1D2, 0x1D2}, + { + 128, 5640, 3760, 0x71, 0x02, 0x34, 0x03, 0x1C, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8D4, 0x8D0, 0x8CC, 0x1D0, 0x1D1, 0x1D2}, + { + 130, 5650, 3767, 0x71, 0x02, 0x35, 0x03, 0x1C, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8D8, 0x8D4, 0x8D0, 0x1CF, 0x1D0, 0x1D1}, + { + 132, 5660, 3773, 0x71, 0x02, 0x36, 0x03, 0x16, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8DC, 0x8D8, 0x8D4, 0x1CE, 0x1CF, 0x1D0}, + { + 134, 5670, 3780, 0x71, 0x02, 0x37, 0x03, 0x16, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8E0, 0x8DC, 0x8D8, 0x1CE, 0x1CE, 0x1CF}, + { + 136, 5680, 3787, 0x71, 0x02, 0x38, 0x03, 0x10, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8E4, 0x8E0, 0x8DC, 0x1CD, 0x1CE, 0x1CE}, + { + 138, 5690, 3793, 0x71, 0x02, 0x39, 0x03, 0x10, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8E8, 0x8E4, 0x8E0, 0x1CC, 0x1CD, 0x1CE}, + { + 140, 5700, 3800, 0x71, 0x02, 0x3A, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8EC, 0x8E8, 0x8E4, 0x1CB, 0x1CC, 0x1CD}, + { + 142, 5710, 3807, 0x71, 0x02, 0x3B, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8F0, 0x8EC, 0x8E8, 0x1CA, 0x1CB, 0x1CC}, + { + 144, 5720, 3813, 0x71, 0x02, 0x3C, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8F4, 0x8F0, 0x8EC, 0x1C9, 0x1CA, 0x1CB}, + { + 145, 5725, 3817, 0x72, 0x04, 0x79, 0x02, 0x03, 0x01, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8F6, 0x8F2, 0x8EE, 0x1C9, 0x1CA, 0x1CB}, + { + 146, 5730, 3820, 0x71, 0x02, 0x3D, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8F8, 0x8F4, 0x8F0, 0x1C9, 0x1C9, 0x1CA}, + { + 147, 5735, 3823, 0x72, 0x04, 0x7B, 0x02, 0x03, 0x01, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8FA, 0x8F6, 0x8F2, 0x1C8, 0x1C9, 0x1CA}, + { + 148, 5740, 3827, 0x71, 0x02, 0x3E, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8FC, 0x8F8, 0x8F4, 0x1C8, 0x1C9, 0x1C9}, + { + 149, 5745, 3830, 0x72, 0x04, 0x7D, 0x02, 0xFE, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x8FE, 0x8FA, 0x8F6, 0x1C8, 0x1C8, 0x1C9}, + { + 150, 5750, 3833, 0x71, 0x02, 0x3F, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x900, 0x8FC, 0x8F8, 0x1C7, 0x1C8, 0x1C9}, + { + 151, 5755, 3837, 0x72, 0x04, 0x7F, 0x02, 0xFE, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x902, 0x8FE, 0x8FA, 0x1C7, 0x1C8, 0x1C8}, + { + 152, 5760, 3840, 0x71, 0x02, 0x40, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x904, 0x900, 0x8FC, 0x1C6, 0x1C7, 0x1C8}, + { + 153, 5765, 3843, 0x72, 0x04, 0x81, 0x02, 0xF8, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x906, 0x902, 0x8FE, 0x1C6, 0x1C7, 0x1C8}, + { + 154, 5770, 3847, 0x71, 0x02, 0x41, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x908, 0x904, 0x900, 0x1C6, 0x1C6, 0x1C7}, + { + 155, 5775, 3850, 0x72, 0x04, 0x83, 0x02, 0xF8, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x90A, 0x906, 0x902, 0x1C5, 0x1C6, 0x1C7}, + { + 156, 5780, 3853, 0x71, 0x02, 0x42, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x90C, 0x908, 0x904, 0x1C5, 0x1C6, 0x1C6}, + { + 157, 5785, 3857, 0x72, 0x04, 0x85, 0x02, 0xF2, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x90E, 0x90A, 0x906, 0x1C4, 0x1C5, 0x1C6}, + { + 158, 5790, 3860, 0x71, 0x02, 0x43, 0x02, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x910, 0x90C, 0x908, 0x1C4, 0x1C5, 0x1C6}, + { + 159, 5795, 3863, 0x72, 0x04, 0x87, 0x02, 0xF2, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x912, 0x90E, 0x90A, 0x1C4, 0x1C4, 0x1C5}, + { + 160, 5800, 3867, 0x71, 0x02, 0x44, 0x01, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x914, 0x910, 0x90C, 0x1C3, 0x1C4, 0x1C5}, + { + 161, 5805, 3870, 0x72, 0x04, 0x89, 0x01, 0xED, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x916, 0x912, 0x90E, 0x1C3, 0x1C4, 0x1C4}, + { + 162, 5810, 3873, 0x71, 0x02, 0x45, 0x01, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x918, 0x914, 0x910, 0x1C2, 0x1C3, 0x1C4}, + { + 163, 5815, 3877, 0x72, 0x04, 0x8B, 0x01, 0xED, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x91A, 0x916, 0x912, 0x1C2, 0x1C3, 0x1C4}, + { + 164, 5820, 3880, 0x71, 0x02, 0x46, 0x01, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x91C, 0x918, 0x914, 0x1C2, 0x1C2, 0x1C3}, + { + 165, 5825, 3883, 0x72, 0x04, 0x8D, 0x01, 0xED, 0x00, 0x03, 0x14, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x91E, 0x91A, 0x916, 0x1C1, 0x1C2, 0x1C3}, + { + 166, 5830, 3887, 0x71, 0x02, 0x47, 0x01, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x920, 0x91C, 0x918, 0x1C1, 0x1C2, 0x1C2}, + { + 168, 5840, 3893, 0x71, 0x02, 0x48, 0x01, 0x0A, 0x01, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x924, 0x920, 0x91C, 0x1C0, 0x1C1, 0x1C2}, + { + 170, 5850, 3900, 0x71, 0x02, 0x49, 0x01, 0xE0, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x928, 0x924, 0x920, 0x1BF, 0x1C0, 0x1C1}, + { + 172, 5860, 3907, 0x71, 0x02, 0x4A, 0x01, 0xDE, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x92C, 0x928, 0x924, 0x1BF, 0x1BF, 0x1C0}, + { + 174, 5870, 3913, 0x71, 0x02, 0x4B, 0x00, 0xDB, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x930, 0x92C, 0x928, 0x1BE, 0x1BF, 0x1BF}, + { + 176, 5880, 3920, 0x71, 0x02, 0x4C, 0x00, 0xD8, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x934, 0x930, 0x92C, 0x1BD, 0x1BE, 0x1BF}, + { + 178, 5890, 3927, 0x71, 0x02, 0x4D, 0x00, 0xD6, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x938, 0x934, 0x930, 0x1BC, 0x1BD, 0x1BE}, + { + 180, 5900, 3933, 0x71, 0x02, 0x4E, 0x00, 0xD3, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x93C, 0x938, 0x934, 0x1BC, 0x1BC, 0x1BD}, + { + 182, 5910, 3940, 0x71, 0x02, 0x4F, 0x00, 0xD6, 0x00, 0x04, 0x0A, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x940, 0x93C, 0x938, 0x1BB, 0x1BC, 0x1BC}, + { + 1, 2412, 3216, 0x73, 0x09, 0x6C, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0D, 0x0C, 0x80, 0xFF, 0x88, 0x0D, + 0x0C, 0x80, 0x3C9, 0x3C5, 0x3C1, 0x43A, 0x43F, 0x443}, + { + 2, 2417, 3223, 0x73, 0x09, 0x71, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0B, 0x80, 0xFF, 0x88, 0x0C, + 0x0B, 0x80, 0x3CB, 0x3C7, 0x3C3, 0x438, 0x43D, 0x441}, + { + 3, 2422, 3229, 0x73, 0x09, 0x76, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, 0x80, 0xFF, 0x88, 0x0C, + 0x0A, 0x80, 0x3CD, 0x3C9, 0x3C5, 0x436, 0x43A, 0x43F}, + { + 4, 2427, 3236, 0x73, 0x09, 0x7B, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x0A, 0x80, 0xFF, 0x88, 0x0C, + 0x0A, 0x80, 0x3CF, 0x3CB, 0x3C7, 0x434, 0x438, 0x43D}, + { + 5, 2432, 3243, 0x73, 0x09, 0x80, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0C, 0x09, 0x80, 0xFF, 0x88, 0x0C, + 0x09, 0x80, 0x3D1, 0x3CD, 0x3C9, 0x431, 0x436, 0x43A}, + { + 6, 2437, 3249, 0x73, 0x09, 0x85, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0B, 0x08, 0x80, 0xFF, 0x88, 0x0B, + 0x08, 0x80, 0x3D3, 0x3CF, 0x3CB, 0x42F, 0x434, 0x438}, + { + 7, 2442, 3256, 0x73, 0x09, 0x8A, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x07, 0x80, 0xFF, 0x88, 0x0A, + 0x07, 0x80, 0x3D5, 0x3D1, 0x3CD, 0x42D, 0x431, 0x436}, + { + 8, 2447, 3263, 0x73, 0x09, 0x8F, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x0A, 0x06, 0x80, 0xFF, 0x88, 0x0A, + 0x06, 0x80, 0x3D7, 0x3D3, 0x3CF, 0x42B, 0x42F, 0x434}, + { + 9, 2452, 3269, 0x73, 0x09, 0x94, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x09, 0x06, 0x80, 0xFF, 0x88, 0x09, + 0x06, 0x80, 0x3D9, 0x3D5, 0x3D1, 0x429, 0x42D, 0x431}, + { + 10, 2457, 3276, 0x73, 0x09, 0x99, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x05, 0x80, 0xFF, 0x88, 0x08, + 0x05, 0x80, 0x3DB, 0x3D7, 0x3D3, 0x427, 0x42B, 0x42F}, + { + 11, 2462, 3283, 0x73, 0x09, 0x9E, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x04, 0x80, 0xFF, 0x88, 0x08, + 0x04, 0x80, 0x3DD, 0x3D9, 0x3D5, 0x424, 0x429, 0x42D}, + { + 12, 2467, 3289, 0x73, 0x09, 0xA3, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x08, 0x03, 0x80, 0xFF, 0x88, 0x08, + 0x03, 0x80, 0x3DF, 0x3DB, 0x3D7, 0x422, 0x427, 0x42B}, + { + 13, 2472, 3296, 0x73, 0x09, 0xA8, 0x0F, 0x00, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x03, 0x80, 0xFF, 0x88, 0x07, + 0x03, 0x80, 0x3E1, 0x3DD, 0x3D9, 0x420, 0x424, 0x429}, + { + 14, 2484, 3312, 0x73, 0x09, 0xB4, 0x0F, 0xFF, 0x01, 0x07, 0x15, + 0x01, 0x8F, 0xFF, 0xFF, 0xFF, 0x88, 0x07, 0x01, 0x80, 0xFF, 0x88, 0x07, + 0x01, 0x80, 0x3E6, 0x3E2, 0x3DE, 0x41B, 0x41F, 0x424} +}; + +static const struct chan_info_nphy_radio205x chan_info_nphyrev3_2056[] = { + { + 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, + { + 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, + { + 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, + { + 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, + { + 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, + { + 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, + { + 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, + { + 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, + { + 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, + { + 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, + { + 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, + { + 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, + { + 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, + { + 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, + { + 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, + { + 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, + { + 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, + { + 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, + { + 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xff, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xff, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, + { + 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, + { + 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, + { + 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, + { + 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, + { + 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, + { + 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xff, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, + { + 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, + { + 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xfc, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, + { + 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, + { + 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, + { + 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, + { + 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, + { + 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, + { + 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, + { + 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, + { + 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, + { + 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, + { + 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xfc, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, + { + 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfc, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, + { + 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, + { + 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, + { + 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, + { + 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, + { + 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, + { + 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, + { + 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, + { + 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, + { + 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x8f, 0x00, 0x05, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x05, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xfa, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, + { + 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, + { + 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, + { + 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, + { + 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00, 0x8e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, + { + 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7e, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00, 0x7e, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, + { + 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7d, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xfa, 0x00, 0x7d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xfa, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, + { + 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xf8, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, + { + 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xf8, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, + { + 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5d, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xf8, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, + { + 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5c, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x08, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x08, + 0x00, 0xf8, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, + { + 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x5c, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, + { + 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, + { + 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x4c, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, + { + 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, + { + 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, + { + 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x3b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, + { + 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2b, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x2b, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, + { + 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2a, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x2a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, + { + 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, + { + 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, + { + 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x1a, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, + { + 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, + { + 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x19, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, + { + 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, + { + 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x09, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, + { + 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf8, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf8, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, + { + 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf6, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, + { + 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf6, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, + { + 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf6, 0x00, 0x08, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf6, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, + { + 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x07, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf6, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, + { + 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf6, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf6, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, + { + 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, + { + 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, + { + 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, + { + 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, + { + 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, + { + 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, + { + 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, + { + 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x06, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, + { + 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, + { + 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, + { + 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, + { + 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, + { + 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, + { + 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, + { + 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, + { + 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, + { + 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, + { + 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, + { + 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, + { + 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, + { + 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, + { + 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, + { + 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, + { + 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, + { + 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, + { + 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf4, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf4, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, + { + 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf2, 0x00, 0x03, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf2, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, + { + 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf2, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, + { + 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf2, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, + { + 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x06, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x06, + 0x00, 0xf2, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, + { + 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x05, 0x00, 0xf2, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, + 0x00, 0xf2, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, + { + 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x05, 0x00, 0xf2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x05, + 0x00, 0xf2, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, + { + 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, + { + 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, + { + 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xff, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, + { + 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfd, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, + { + 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfb, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, + { + 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, + { + 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, + { + 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf7, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, + { + 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0xf6, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0f, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, + { + 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf5, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0d, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, + { + 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf4, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0d, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, + { + 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf3, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0d, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, + { + 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0d, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, + { + 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x05, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0xf0, 0x00, 0x05, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0d, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} +}; + +static const struct chan_info_nphy_radio205x chan_info_nphyrev4_2056_A1[] = { + { + 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, + { + 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, + { + 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, + { + 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, + { + 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, + { + 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, + { + 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, + { + 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0e, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0e, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, + { + 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, + { + 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, + { + 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, + { + 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, + { + 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, + { + 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, + { + 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, + { + 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, + { + 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, + { + 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0d, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xff, 0x00, 0x0d, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xff, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, + { + 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, + { + 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, + { + 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, + { + 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, + { + 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, + { + 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, + { + 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, + { + 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, + { + 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0c, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfe, 0x00, 0xef, 0x00, 0x0c, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfe, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, + { + 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xef, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xef, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, + { + 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, + { + 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, + { + 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xdf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xdf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, + { + 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, + { + 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, + { + 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, + { + 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xcf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xcf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, + { + 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, + { + 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xbf, 0x00, 0x0a, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfc, 0x00, 0xbf, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfc, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, + { + 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, + { + 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, + { + 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xbf, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xbf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, + { + 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, + { + 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xaf, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00, 0xaf, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, + { + 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, + { + 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, + { + 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, + { + 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x9f, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x9f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, + { + 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x8f, 0x00, 0x08, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xfa, 0x00, 0x8f, 0x00, 0x08, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xfa, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, + { + 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, + { + 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8f, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8f, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, + { + 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, + { + 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x8e, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x8e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, + { + 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7e, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x7e, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, + { + 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x7d, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x7d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, + { + 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, + { + 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x6d, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x6d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, + { + 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5d, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x5d, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, + { + 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x5c, 0x00, 0x07, 0x00, 0x7f, + 0x00, 0x0f, 0x00, 0xf8, 0x00, 0x5c, 0x00, 0x07, 0x00, 0x7f, 0x00, 0x0f, + 0x00, 0xf8, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, + { + 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x5c, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x5c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, + { + 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, + { + 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x4c, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x4c, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, + { + 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, + { + 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, + { + 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x3b, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x3b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, + { + 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2b, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x2b, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, + { + 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x2a, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x2a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, + { + 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, + { + 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x1a, 0x00, 0x06, 0x00, 0x7f, + 0x00, 0x0d, 0x00, 0xf6, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x7f, 0x00, 0x0d, + 0x00, 0xf6, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, + { + 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x1a, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x1a, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, + { + 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, + { + 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x19, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x19, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, + { + 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, + { + 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x09, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x09, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, + { + 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, + { + 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, + { + 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, + { + 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x08, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x08, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, + { + 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x07, 0x00, 0x04, 0x00, 0x7f, + 0x00, 0x0b, 0x00, 0xf4, 0x00, 0x07, 0x00, 0x04, 0x00, 0x7f, 0x00, 0x0b, + 0x00, 0xf4, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, + { + 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, + { + 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, + { + 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x07, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x07, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, + { + 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, + { + 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, + { + 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, + { + 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, + { + 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, + { + 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x06, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x06, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, + { + 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, + { + 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, + { + 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, + { + 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, + { + 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, + { + 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x05, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x05, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, + { + 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, + { + 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, + { + 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x04, 0x00, 0x03, 0x00, 0x7f, + 0x00, 0x0a, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x0a, + 0x00, 0xf2, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, + { + 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, + { + 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, + { + 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, + { + 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x04, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, + { + 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, + { + 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, + { + 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, + { + 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, + { + 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, + { + 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x03, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x03, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, + { + 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, + { + 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, + { + 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x02, 0x00, 0x02, 0x00, 0x7f, + 0x00, 0x09, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7f, 0x00, 0x09, + 0x00, 0xf0, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, + { + 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf0, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, + { + 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x07, 0x00, 0xf0, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x07, + 0x00, 0xf0, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, + { + 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, + { + 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, + { + 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xff, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, + { + 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfd, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, + { + 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfb, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, + { + 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, + { + 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf8, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, + { + 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf7, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, + { + 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf6, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, + { + 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf5, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, + { + 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf4, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, + { + 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf3, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, + { + 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf2, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, + { + 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x04, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0e, 0x00, 0xf0, 0x00, 0x04, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0e, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} +}; + +static const struct chan_info_nphy_radio205x chan_info_nphyrev5_2056v5[] = { + { + 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0f, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, + { + 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, + { + 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, + { + 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, + { + 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, + { + 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, + { + 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, + { + 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, + { + 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, + { + 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, + { + 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, + { + 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, + { + 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, + { + 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, + { + 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, + { + 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, + { + 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, + { + 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, + { + 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, + { + 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, + { + 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, + { + 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, + { + 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, + 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, + { + 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, + { + 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, + { + 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, + { + 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, + { + 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, + { + 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, + { + 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, + { + 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xea, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x6e, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, + { + 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x6d, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, + { + 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xe9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x6d, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, + { + 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xd9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x6d, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, + { + 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xd8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, + { + 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, + { + 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0f, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, + { + 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, + { + 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, + { + 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, + { + 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb7, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6b, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, + { + 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xb7, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6b, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, + { + 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa7, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x6b, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, + { + 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x6b, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, + { + 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0xa6, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x5b, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, + { + 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x96, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x5a, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, + { + 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8f, 0x0e, 0x00, 0xff, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x5a, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, + { + 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x5a, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, + { + 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x5a, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, + { + 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x5a, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, + { + 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xc8, 0x85, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x59, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, + { + 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x59, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, + { + 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x59, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, + { + 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, + { + 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x74, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, + { + 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, + { + 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0d, 0x00, 0xc8, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, + { + 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x78, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, + { + 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x78, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, + { + 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x78, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, + { + 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x63, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x78, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, + { + 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x77, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, + { + 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x77, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, + { + 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x77, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, + { + 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x76, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, + { + 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x52, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x76, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, + { + 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8d, 0x0b, 0x00, 0x84, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x76, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, + { + 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x76, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, + { + 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x76, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, + { + 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x76, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, + { + 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x76, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, + { + 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x75, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, + { + 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x75, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, + { + 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x50, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x75, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, + { + 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x74, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, + { + 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x74, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, + { + 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8b, 0x09, 0x00, 0x70, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x74, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, + { + 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x74, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, + { + 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x74, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, + { + 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x74, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, + { + 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x74, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, + { + 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x84, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, + { + 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x83, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, + { + 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x83, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, + { + 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x83, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, + { + 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x83, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, + { + 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x83, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, + { + 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x83, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, + { + 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, + { + 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, + { + 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, + { + 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, + { + 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, + { + 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, + { + 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x8a, 0x06, 0x00, 0x40, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, + { + 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, + { + 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, + { + 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, + { + 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, + { + 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, + { + 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x82, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, + { + 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x72, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, + { + 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x72, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, + { + 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x72, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, + { + 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x72, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, + { + 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x71, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, + { + 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x71, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, + { + 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x88, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x71, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, + { + 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x71, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, + { + 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x87, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x71, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, + { + 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0b, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, + { + 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x1f, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, + { + 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, + { + 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, + { + 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, + { + 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, + { + 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x09, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, + { + 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x08, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, + { + 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x07, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, + { + 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, + { + 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x05, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, + { + 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, + { + 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, + { + 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} +}; + +static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v6[] = { + { + 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, + { + 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, + { + 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, + { + 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, + { + 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, + { + 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, + { + 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, + { + 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, + { + 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, + { + 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, + { + 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, + { + 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, + { + 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, + { + 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, + { + 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, + { + 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, + { + 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, + { + 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, + { + 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, + { + 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, + { + 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, + { + 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, + { + 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, + { + 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, + { + 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, + { + 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, + { + 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, + { + 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, + { + 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, + { + 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, + { + 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, + { + 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, + { + 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, + { + 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, + { + 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, + { + 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, + { + 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, + { + 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, + { + 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, + { + 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, + { + 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, + { + 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, + { + 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, + { + 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, + { + 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, + { + 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, + { + 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, + { + 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, + { + 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, + { + 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, + { + 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, + { + 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, + { + 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, + { + 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, + { + 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, + { + 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, + { + 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, + { + 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, + { + 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, + { + 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, + { + 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, + { + 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, + { + 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, + { + 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, + { + 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, + { + 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, + { + 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, + { + 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, + { + 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, + { + 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, + { + 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, + { + 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, + { + 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, + { + 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, + { + 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, + { + 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, + { + 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, + { + 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, + { + 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, + { + 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, + { + 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, + { + 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, + { + 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, + { + 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, + { + 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, + { + 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6d, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, + { + 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, + { + 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, + { + 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, + { + 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, + { + 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, + { + 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, + { + 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, + { + 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, + { + 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, + { + 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, + { + 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, + { + 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, + { + 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, + { + 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, + { + 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, + { + 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, + { + 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, + { + 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, + { + 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, + { + 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, + { + 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, + { + 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, + { + 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, + { + 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, + { + 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, + { + 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, + { + 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, + { + 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, + { + 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, + { + 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, + { + 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, + { + 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, + { + 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, + { + 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, + { + 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, + { + 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, + { + 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, + { + 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} +}; + +static const struct chan_info_nphy_radio205x chan_info_nphyrev5n6_2056v7[] = { + { + 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0f, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, + { + 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, + { + 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, + { + 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0b, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0b, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, + { + 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0e, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, + { + 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, + { + 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, + { + 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, + { + 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, + { + 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x0a, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, + { + 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0d, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, + { + 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xff, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, + { + 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, + { + 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, + { + 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x70, + 0x00, 0x0c, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x70, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, + { + 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, + { + 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, + { + 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, + { + 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, + { + 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, + { + 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0b, 0x00, 0x9f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, + { + 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x70, + 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x70, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, + { + 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, + 0x00, 0x0a, 0x00, 0x9f, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, + { + 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x07, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x07, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, + { + 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfb, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, + { + 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, + { + 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, + { + 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, + { + 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, + { + 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xfa, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x09, 0x00, 0x9e, 0x00, 0xfa, 0x00, 0x06, 0x00, 0x70, 0x00, 0x09, + 0x00, 0x6e, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, + { + 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xea, 0x00, 0x06, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x9e, 0x00, 0xea, 0x00, 0x06, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x6e, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, + { + 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xe9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x6d, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, + { + 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xe9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x9d, 0x00, 0xe9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x6d, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, + { + 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xd9, 0x00, 0x05, 0x00, 0x70, + 0x00, 0x08, 0x00, 0x9d, 0x00, 0xd9, 0x00, 0x05, 0x00, 0x70, 0x00, 0x08, + 0x00, 0x6d, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, + { + 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xd8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xd8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, + { + 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, + { + 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, + { + 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, + { + 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xc8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xc8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, + { + 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb8, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9c, 0x00, 0xb8, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6c, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, + { + 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xb7, 0x00, 0x04, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x04, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6b, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, + { + 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xb7, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x07, 0x00, 0x9b, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x07, + 0x00, 0x6b, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, + { + 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa7, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa7, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x6b, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, + { + 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x6b, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, + { + 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0xa6, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x7b, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, + { + 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x96, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x7a, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, + { + 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x7a, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, + { + 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x06, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x06, + 0x00, 0x7a, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, + { + 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x7a, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, + { + 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x95, 0x00, 0x03, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x03, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x7a, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, + { + 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x85, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x99, 0x00, 0x85, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x79, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, + { + 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x79, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, + { + 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x05, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x05, + 0x00, 0x79, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, + { + 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x02, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x99, 0x00, 0x84, 0x00, 0x02, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x79, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, + { + 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x74, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x99, 0x00, 0x74, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x79, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, + { + 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x78, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, + { + 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x78, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, + { + 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x78, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, + { + 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x78, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, + { + 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x04, 0x00, 0x98, 0x00, 0x73, 0x00, 0x01, 0x00, 0x70, 0x00, 0x04, + 0x00, 0x78, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, + { + 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x63, 0x00, 0x01, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x98, 0x00, 0x63, 0x00, 0x01, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x78, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, + { + 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x77, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, + { + 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x77, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, + { + 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x03, 0x00, 0x97, 0x00, 0x62, 0x00, 0x00, 0x00, 0x70, 0x00, 0x03, + 0x00, 0x77, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, + { + 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x52, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x76, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, + { + 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x52, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x86, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, + { + 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x86, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, + { + 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x86, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, + { + 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x86, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, + { + 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x86, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, + { + 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x96, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x86, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, + { + 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x51, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x02, 0x00, 0x95, 0x00, 0x51, 0x00, 0x00, 0x00, 0x70, 0x00, 0x02, + 0x00, 0x85, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, + { + 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x85, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, + { + 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x95, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x85, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, + { + 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x84, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, + { + 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x84, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, + { + 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, + { + 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, + { + 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, + { + 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, + { + 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x40, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, + { + 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x01, 0x00, 0x94, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x01, + 0x00, 0x94, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, + { + 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, + { + 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, + { + 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, + { + 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, + { + 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x30, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, + { + 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x93, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x93, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, + { + 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, + { + 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, + { + 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x20, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, + { + 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, + { + 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, + { + 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, + { + 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, + { + 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, + { + 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, + { + 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, + { + 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, + { + 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, + { + 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, + { + 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, + { + 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, + { + 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, + { + 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x92, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, + { + 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, + { + 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, + { + 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, + { + 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, + { + 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x00, 0x91, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, + { + 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0b, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, + { + 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, + { + 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0f, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, + { + 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, + { + 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, + { + 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x76, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, + { + 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x66, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, + { + 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x55, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, + { + 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0e, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0e, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, + { + 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, + { + 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, + { + 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x22, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, + { + 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, + { + 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0d, 0x00, 0x08, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} +}; + +static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v8[] = { + { + 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, + { + 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, + { + 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, + { + 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, + { + 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, + { + 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, + { + 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, + { + 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, + { + 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, + { + 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, + { + 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, + { + 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, + { + 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, + { + 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, + { + 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, + { + 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, + { + 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, + { + 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, + { + 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, + { + 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, + { + 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, + { + 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, + { + 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, + { + 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, + { + 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, + { + 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, + { + 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, + { + 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, + { + 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, + { + 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, + { + 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, + { + 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, + { + 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, + { + 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, + { + 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, + { + 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, + { + 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, + { + 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, + { + 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, + { + 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, + { + 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, + { + 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, + { + 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, + { + 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, + { + 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, + { + 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, + { + 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, + { + 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, + { + 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, + { + 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, + { + 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, + { + 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, + { + 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, + { + 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, + { + 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, + { + 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, + { + 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, + { + 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, + { + 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, + { + 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, + { + 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, + { + 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, + { + 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, + { + 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, + { + 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, + { + 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, + { + 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, + { + 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, + { + 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, + { + 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, + { + 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, + { + 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, + { + 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, + { + 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, + { + 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, + { + 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, + { + 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, + { + 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, + { + 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, + { + 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, + { + 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, + { + 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, + { + 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, + { + 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, + { + 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, + { + 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6d, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, + { + 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, + { + 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, + { + 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, + { + 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, + { + 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, + { + 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, + { + 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, + { + 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, + { + 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, + { + 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, + { + 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, + { + 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, + { + 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, + { + 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, + { + 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x07, 0x07, 0x04, 0x10, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, + { + 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, + { + 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, + { + 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, + { + 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, + { + 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, + { + 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, + { + 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, + { + 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, + { + 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x04, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, + { + 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, + { + 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, + { + 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, + { + 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, + { + 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, + { + 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, + { + 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, + { + 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, + { + 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, + { + 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, + { + 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, + { + 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, + { + 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, + { + 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x08, 0x08, 0x04, 0x16, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} +}; + +static const struct chan_info_nphy_radio205x chan_info_nphyrev6_2056v11[] = { + { + 184, 4920, 0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216}, + { + 186, 4930, 0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215}, + { + 188, 4940, 0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214}, + { + 190, 4950, 0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213}, + { + 192, 4960, 0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212}, + { + 194, 4970, 0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211}, + { + 196, 4980, 0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f}, + { + 198, 4990, 0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e}, + { + 200, 5000, 0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d}, + { + 202, 5010, 0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c}, + { + 204, 5020, 0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b}, + { + 206, 5030, 0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a}, + { + 208, 5040, 0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209}, + { + 210, 5050, 0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208}, + { + 212, 5060, 0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207}, + { + 214, 5070, 0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206}, + { + 216, 5080, 0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205}, + { + 218, 5090, 0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204}, + { + 220, 5100, 0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203}, + { + 222, 5110, 0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202}, + { + 224, 5120, 0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201}, + { + 226, 5130, 0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200}, + { + 228, 5140, 0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff}, + { + 32, 5160, 0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd}, + { + 34, 5170, 0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc}, + { + 36, 5180, 0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb}, + { + 38, 5190, 0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa}, + { + 40, 5200, 0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9}, + { + 42, 5210, 0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8}, + { + 44, 5220, 0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7}, + { + 46, 5230, 0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6}, + { + 48, 5240, 0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5}, + { + 50, 5250, 0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4}, + { + 52, 5260, 0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3}, + { + 54, 5270, 0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2}, + { + 56, 5280, 0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1}, + { + 58, 5290, 0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0}, + { + 60, 5300, 0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0}, + { + 62, 5310, 0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef}, + { + 64, 5320, 0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee}, + { + 66, 5330, 0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed}, + { + 68, 5340, 0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec}, + { + 70, 5350, 0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb}, + { + 72, 5360, 0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea}, + { + 74, 5370, 0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9}, + { + 76, 5380, 0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8}, + { + 78, 5390, 0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7}, + { + 80, 5400, 0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6}, + { + 82, 5410, 0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5}, + { + 84, 5420, 0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5}, + { + 86, 5430, 0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4}, + { + 88, 5440, 0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3}, + { + 90, 5450, 0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2}, + { + 92, 5460, 0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1}, + { + 94, 5470, 0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0}, + { + 96, 5480, 0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df}, + { + 98, 5490, 0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de}, + { + 100, 5500, 0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd}, + { + 102, 5510, 0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd}, + { + 104, 5520, 0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc}, + { + 106, 5530, 0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db}, + { + 108, 5540, 0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da}, + { + 110, 5550, 0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9}, + { + 112, 5560, 0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8}, + { + 114, 5570, 0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7}, + { + 116, 5580, 0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7}, + { + 118, 5590, 0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6}, + { + 120, 5600, 0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5}, + { + 122, 5610, 0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4}, + { + 124, 5620, 0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3}, + { + 126, 5630, 0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2}, + { + 128, 5640, 0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2}, + { + 130, 5650, 0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1}, + { + 132, 5660, 0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0}, + { + 134, 5670, 0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf}, + { + 136, 5680, 0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce}, + { + 138, 5690, 0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce}, + { + 140, 5700, 0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd}, + { + 142, 5710, 0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc}, + { + 144, 5720, 0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb}, + { + 145, 5725, 0x03, 0x01, 0x02, 0x04, 0x79, 0x05, 0x05, 0x02, 0x15, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb}, + { + 146, 5730, 0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca}, + { + 147, 5735, 0x03, 0x01, 0x02, 0x04, 0x7b, 0x05, 0x05, 0x02, 0x15, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca}, + { + 148, 5740, 0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9}, + { + 149, 5745, 0xfe, 0x00, 0x02, 0x04, 0x7d, 0x05, 0x05, 0x02, 0x15, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9}, + { + 150, 5750, 0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6d, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9}, + { + 151, 5755, 0xfe, 0x00, 0x02, 0x04, 0x7f, 0x05, 0x05, 0x02, 0x15, 0x01, + 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8}, + { + 152, 5760, 0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8}, + { + 153, 5765, 0xf8, 0x00, 0x02, 0x04, 0x81, 0x05, 0x05, 0x02, 0x15, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8}, + { + 154, 5770, 0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7}, + { + 155, 5775, 0xf8, 0x00, 0x02, 0x04, 0x83, 0x05, 0x05, 0x02, 0x15, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7}, + { + 156, 5780, 0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6}, + { + 157, 5785, 0xf2, 0x00, 0x02, 0x04, 0x85, 0x05, 0x05, 0x02, 0x15, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6}, + { + 158, 5790, 0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6}, + { + 159, 5795, 0xf2, 0x00, 0x02, 0x04, 0x87, 0x05, 0x05, 0x02, 0x15, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5}, + { + 160, 5800, 0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5}, + { + 161, 5805, 0xed, 0x00, 0x02, 0x04, 0x89, 0x05, 0x05, 0x02, 0x15, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4}, + { + 162, 5810, 0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4}, + { + 163, 5815, 0xed, 0x00, 0x02, 0x04, 0x8b, 0x05, 0x05, 0x02, 0x15, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4}, + { + 164, 5820, 0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3}, + { + 165, 5825, 0xed, 0x00, 0x02, 0x04, 0x8d, 0x05, 0x05, 0x02, 0x15, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3}, + { + 166, 5830, 0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2}, + { + 168, 5840, 0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2}, + { + 170, 5850, 0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1}, + { + 172, 5860, 0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0}, + { + 174, 5870, 0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf}, + { + 176, 5880, 0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf}, + { + 178, 5890, 0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be}, + { + 180, 5900, 0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd}, + { + 182, 5910, 0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x02, 0x0c, 0x01, + 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc}, + { + 1, 2412, 0x00, 0x01, 0x03, 0x09, 0x6c, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443}, + { + 2, 2417, 0x00, 0x01, 0x03, 0x09, 0x71, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441}, + { + 3, 2422, 0x00, 0x01, 0x03, 0x09, 0x76, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f}, + { + 4, 2427, 0x00, 0x01, 0x03, 0x09, 0x7b, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d}, + { + 5, 2432, 0x00, 0x01, 0x03, 0x09, 0x80, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a}, + { + 6, 2437, 0x00, 0x01, 0x03, 0x09, 0x85, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438}, + { + 7, 2442, 0x00, 0x01, 0x03, 0x09, 0x8a, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436}, + { + 8, 2447, 0x00, 0x01, 0x03, 0x09, 0x8f, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434}, + { + 9, 2452, 0x00, 0x01, 0x03, 0x09, 0x94, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431}, + { + 10, 2457, 0x00, 0x01, 0x03, 0x09, 0x99, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f}, + { + 11, 2462, 0x00, 0x01, 0x03, 0x09, 0x9e, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d}, + { + 12, 2467, 0x00, 0x01, 0x03, 0x09, 0xa3, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b}, + { + 13, 2472, 0x00, 0x01, 0x03, 0x09, 0xa8, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429}, + { + 14, 2484, 0xff, 0x01, 0x03, 0x09, 0xb4, 0x06, 0x06, 0x04, 0x2b, 0x01, + 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424} +}; + +static const struct chan_info_nphy_radio2057 chan_info_nphyrev7_2057_rev4[] = { + { + 184, 4920, 0x68, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xec, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07b4, 0x07b0, 0x07ac, 0x0214, + 0x0215, + 0x0216, + }, + { + 186, 4930, 0x6b, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xed, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07b8, 0x07b4, 0x07b0, 0x0213, + 0x0214, + 0x0215, + }, + { + 188, 4940, 0x6e, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xee, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07bc, 0x07b8, 0x07b4, 0x0212, + 0x0213, + 0x0214, + }, + { + 190, 4950, 0x72, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xef, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07c0, 0x07bc, 0x07b8, 0x0211, + 0x0212, + 0x0213, + }, + { + 192, 4960, 0x75, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf0, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07c4, 0x07c0, 0x07bc, 0x020f, + 0x0211, + 0x0212, + }, + { + 194, 4970, 0x78, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf1, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07c8, 0x07c4, 0x07c0, 0x020e, + 0x020f, + 0x0211, + }, + { + 196, 4980, 0x7c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf2, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07cc, 0x07c8, 0x07c4, 0x020d, + 0x020e, + 0x020f, + }, + { + 198, 4990, 0x7f, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf3, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07d0, 0x07cc, 0x07c8, 0x020c, + 0x020d, + 0x020e, + }, + { + 200, 5000, 0x82, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf4, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07d4, 0x07d0, 0x07cc, 0x020b, + 0x020c, + 0x020d, + }, + { + 202, 5010, 0x86, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf5, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07d8, 0x07d4, 0x07d0, 0x020a, + 0x020b, + 0x020c, + }, + { + 204, 5020, 0x89, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf6, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07dc, 0x07d8, 0x07d4, 0x0209, + 0x020a, + 0x020b, + }, + { + 206, 5030, 0x8c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf7, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07e0, 0x07dc, 0x07d8, 0x0208, + 0x0209, + 0x020a, + }, + { + 208, 5040, 0x90, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf8, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07e4, 0x07e0, 0x07dc, 0x0207, + 0x0208, + 0x0209, + }, + { + 210, 5050, 0x93, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf9, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xf3, 0x00, 0xef, 0x07e8, 0x07e4, 0x07e0, 0x0206, + 0x0207, + 0x0208, + }, + { + 212, 5060, 0x96, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfa, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xe3, 0x00, 0xef, 0x00, + 0x00, 0x0f, 0x0f, 0xe3, 0x00, 0xef, 0x07ec, 0x07e8, 0x07e4, 0x0205, + 0x0206, + 0x0207, + }, + { + 214, 5070, 0x9a, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfb, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x00, + 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x07f0, 0x07ec, 0x07e8, 0x0204, + 0x0205, + 0x0206, + }, + { + 216, 5080, 0x9d, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfc, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x00, + 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xef, 0x07f4, 0x07f0, 0x07ec, 0x0203, + 0x0204, + 0x0205, + }, + { + 218, 5090, 0xa0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfd, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, + 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x07f8, 0x07f4, 0x07f0, 0x0202, + 0x0203, + 0x0204, + }, + { + 220, 5100, 0xa4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfe, 0x01, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, + 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x07fc, 0x07f8, 0x07f4, 0x0201, + 0x0202, + 0x0203, + }, + { + 222, 5110, 0xa7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xff, 0x01, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, + 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x0800, 0x07fc, 0x07f8, 0x0200, + 0x0201, + 0x0202, + }, + { + 224, 5120, 0xaa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x00, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, + 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x0804, 0x0800, 0x07fc, 0x01ff, + 0x0200, + 0x0201, + }, + { + 226, 5130, 0xae, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x01, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x00, + 0x00, 0x0e, 0x0f, 0xe3, 0x00, 0xd6, 0x0808, 0x0804, 0x0800, 0x01fe, + 0x01ff, + 0x0200, + }, + { + 228, 5140, 0xb1, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x02, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0e, 0x0e, 0xe3, 0x00, 0xd6, 0x00, + 0x00, 0x0e, 0x0e, 0xe3, 0x00, 0xd6, 0x080c, 0x0808, 0x0804, 0x01fd, + 0x01fe, + 0x01ff, + }, + { + 32, 5160, 0xb8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x04, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x00, + 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x0814, 0x0810, 0x080c, 0x01fb, + 0x01fc, + 0x01fd, + }, + { + 34, 5170, 0xbb, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x05, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x00, + 0x00, 0x0d, 0x0e, 0xe3, 0x00, 0xd6, 0x0818, 0x0814, 0x0810, 0x01fa, + 0x01fb, + 0x01fc, + }, + { + 36, 5180, 0xbe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x06, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, + 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x081c, 0x0818, 0x0814, 0x01f9, + 0x01fa, + 0x01fb, + }, + { + 38, 5190, 0xc2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x07, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, + 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x0820, 0x081c, 0x0818, 0x01f8, + 0x01f9, + 0x01fa, + }, + { + 40, 5200, 0xc5, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x08, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, + 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x0824, 0x0820, 0x081c, 0x01f7, + 0x01f8, + 0x01f9, + }, + { + 42, 5210, 0xc8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x09, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x00, + 0x00, 0x0d, 0x0e, 0xd3, 0x00, 0xd6, 0x0828, 0x0824, 0x0820, 0x01f6, + 0x01f7, + 0x01f8, + }, + { + 44, 5220, 0xcc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0a, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, + 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x082c, 0x0828, 0x0824, 0x01f5, + 0x01f6, + 0x01f7, + }, + { + 46, 5230, 0xcf, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0b, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, + 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x0830, 0x082c, 0x0828, 0x01f4, + 0x01f5, + 0x01f6, + }, + { + 48, 5240, 0xd2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0c, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, + 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x0834, 0x0830, 0x082c, 0x01f3, + 0x01f4, + 0x01f5, + }, + { + 50, 5250, 0xd6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0d, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x00, + 0x00, 0x0c, 0x0e, 0xd3, 0x00, 0xd6, 0x0838, 0x0834, 0x0830, 0x01f2, + 0x01f3, + 0x01f4, + }, + { + 52, 5260, 0xd9, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0e, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x00, + 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x083c, 0x0838, 0x0834, 0x01f1, + 0x01f2, + 0x01f3, + }, + { + 54, 5270, 0xdc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0f, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x00, + 0x00, 0x0c, 0x0d, 0xd3, 0x00, 0xd6, 0x0840, 0x083c, 0x0838, 0x01f0, + 0x01f1, + 0x01f2, + }, + { + 56, 5280, 0xe0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x10, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x00, + 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x0844, 0x0840, 0x083c, 0x01f0, + 0x01f0, + 0x01f1, + }, + { + 58, 5290, 0xe3, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x11, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x00, + 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x0848, 0x0844, 0x0840, 0x01ef, + 0x01f0, + 0x01f0, + }, + { + 60, 5300, 0xe6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x12, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x00, + 0x00, 0x0c, 0x0c, 0xc3, 0x00, 0xd4, 0x084c, 0x0848, 0x0844, 0x01ee, + 0x01ef, + 0x01f0, + }, + { + 62, 5310, 0xea, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x13, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x00, + 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x0850, 0x084c, 0x0848, 0x01ed, + 0x01ee, + 0x01ef, + }, + { + 64, 5320, 0xed, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x14, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x00, + 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x0854, 0x0850, 0x084c, 0x01ec, + 0x01ed, + 0x01ee, + }, + { + 66, 5330, 0xf0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x15, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x00, + 0x00, 0x0b, 0x0c, 0xc3, 0x00, 0xd4, 0x0858, 0x0854, 0x0850, 0x01eb, + 0x01ec, + 0x01ed, + }, + { + 68, 5340, 0xf4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x16, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0c, 0xc3, 0x00, 0xa1, 0x00, + 0x00, 0x0a, 0x0c, 0xc3, 0x00, 0xa1, 0x085c, 0x0858, 0x0854, 0x01ea, + 0x01eb, + 0x01ec, + }, + { + 70, 5350, 0xf7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x17, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, + 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x0860, 0x085c, 0x0858, 0x01e9, + 0x01ea, + 0x01eb, + }, + { + 72, 5360, 0xfa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x18, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, + 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x0864, 0x0860, 0x085c, 0x01e8, + 0x01e9, + 0x01ea, + }, + { + 74, 5370, 0xfe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x19, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, + 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x0868, 0x0864, 0x0860, 0x01e7, + 0x01e8, + 0x01e9, + }, + { + 76, 5380, 0x01, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1a, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x00, + 0x00, 0x0a, 0x0b, 0xb3, 0x00, 0xa1, 0x086c, 0x0868, 0x0864, 0x01e6, + 0x01e7, + 0x01e8, + }, + { + 78, 5390, 0x04, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1b, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0a, 0x0a, 0xa3, 0x00, 0xa1, 0x00, + 0x00, 0x0a, 0x0a, 0xa3, 0x00, 0xa1, 0x0870, 0x086c, 0x0868, 0x01e5, + 0x01e6, + 0x01e7, + }, + { + 80, 5400, 0x08, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1c, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x00, + 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x0874, 0x0870, 0x086c, 0x01e5, + 0x01e5, + 0x01e6, + }, + { + 82, 5410, 0x0b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1d, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x00, + 0x00, 0x09, 0x0a, 0xa3, 0x00, 0x90, 0x0878, 0x0874, 0x0870, 0x01e4, + 0x01e5, + 0x01e5, + }, + { + 84, 5420, 0x0e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1e, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0xa3, 0x00, 0x90, 0x00, + 0x00, 0x09, 0x09, 0xa3, 0x00, 0x90, 0x087c, 0x0878, 0x0874, 0x01e3, + 0x01e4, + 0x01e5, + }, + { + 86, 5430, 0x12, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1f, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x00, + 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x0880, 0x087c, 0x0878, 0x01e2, + 0x01e3, + 0x01e4, + }, + { + 88, 5440, 0x15, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x20, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x00, + 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x0884, 0x0880, 0x087c, 0x01e1, + 0x01e2, + 0x01e3, + }, + { + 90, 5450, 0x18, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x21, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x00, + 0x00, 0x09, 0x09, 0x93, 0x00, 0x90, 0x0888, 0x0884, 0x0880, 0x01e0, + 0x01e1, + 0x01e2, + }, + { + 92, 5460, 0x1c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x22, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x08, 0x93, 0x00, 0x90, 0x00, + 0x00, 0x08, 0x08, 0x93, 0x00, 0x90, 0x088c, 0x0888, 0x0884, 0x01df, + 0x01e0, + 0x01e1, + }, + { + 94, 5470, 0x1f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x23, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x08, 0x93, 0x00, 0x60, 0x00, + 0x00, 0x08, 0x08, 0x93, 0x00, 0x60, 0x0890, 0x088c, 0x0888, 0x01de, + 0x01df, + 0x01e0, + }, + { + 96, 5480, 0x22, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x24, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, + 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x0894, 0x0890, 0x088c, 0x01dd, + 0x01de, + 0x01df, + }, + { + 98, 5490, 0x26, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x25, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, + 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x0898, 0x0894, 0x0890, 0x01dd, + 0x01dd, + 0x01de, + }, + { + 100, 5500, 0x29, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x26, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, + 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x089c, 0x0898, 0x0894, 0x01dc, + 0x01dd, + 0x01dd, + }, + { + 102, 5510, 0x2c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x27, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x00, + 0x00, 0x08, 0x07, 0x93, 0x00, 0x60, 0x08a0, 0x089c, 0x0898, 0x01db, + 0x01dc, + 0x01dd, + }, + { + 104, 5520, 0x30, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x28, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x00, + 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x08a4, 0x08a0, 0x089c, 0x01da, + 0x01db, + 0x01dc, + }, + { + 106, 5530, 0x33, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x29, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x00, + 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x08a8, 0x08a4, 0x08a0, 0x01d9, + 0x01da, + 0x01db, + }, + { + 108, 5540, 0x36, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2a, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x00, + 0x00, 0x08, 0x06, 0x93, 0x00, 0x60, 0x08ac, 0x08a8, 0x08a4, 0x01d8, + 0x01d9, + 0x01da, + }, + { + 110, 5550, 0x3a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2b, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x00, + 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x08b0, 0x08ac, 0x08a8, 0x01d7, + 0x01d8, + 0x01d9, + }, + { + 112, 5560, 0x3d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2c, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x00, + 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x08b4, 0x08b0, 0x08ac, 0x01d7, + 0x01d7, + 0x01d8, + }, + { + 114, 5570, 0x40, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2d, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x00, + 0x00, 0x08, 0x05, 0x83, 0x00, 0x60, 0x08b8, 0x08b4, 0x08b0, 0x01d6, + 0x01d7, + 0x01d7, + }, + { + 116, 5580, 0x44, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2e, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x07, 0x05, 0x83, 0x00, 0x60, 0x00, + 0x00, 0x07, 0x05, 0x83, 0x00, 0x60, 0x08bc, 0x08b8, 0x08b4, 0x01d5, + 0x01d6, + 0x01d7, + }, + { + 118, 5590, 0x47, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2f, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x07, 0x04, 0x83, 0x00, 0x60, 0x00, + 0x00, 0x07, 0x04, 0x83, 0x00, 0x60, 0x08c0, 0x08bc, 0x08b8, 0x01d4, + 0x01d5, + 0x01d6, + }, + { + 120, 5600, 0x4a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x30, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x07, 0x04, 0x73, 0x00, 0x30, 0x00, + 0x00, 0x07, 0x04, 0x73, 0x00, 0x30, 0x08c4, 0x08c0, 0x08bc, 0x01d3, + 0x01d4, + 0x01d5, + }, + { + 122, 5610, 0x4e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x31, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, + 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08c8, 0x08c4, 0x08c0, 0x01d2, + 0x01d3, + 0x01d4, + }, + { + 124, 5620, 0x51, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x32, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, + 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08cc, 0x08c8, 0x08c4, 0x01d2, + 0x01d2, + 0x01d3, + }, + { + 126, 5630, 0x54, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x33, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, + 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08d0, 0x08cc, 0x08c8, 0x01d1, + 0x01d2, + 0x01d2, + }, + { + 128, 5640, 0x58, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x34, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x00, + 0x00, 0x06, 0x04, 0x73, 0x00, 0x30, 0x08d4, 0x08d0, 0x08cc, 0x01d0, + 0x01d1, + 0x01d2, + }, + { + 130, 5650, 0x5b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x35, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x00, + 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x08d8, 0x08d4, 0x08d0, 0x01cf, + 0x01d0, + 0x01d1, + }, + { + 132, 5660, 0x5e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x36, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x00, + 0x00, 0x06, 0x03, 0x63, 0x00, 0x30, 0x08dc, 0x08d8, 0x08d4, 0x01ce, + 0x01cf, + 0x01d0, + }, + { + 134, 5670, 0x62, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x37, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x03, 0x63, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x03, 0x63, 0x00, 0x00, 0x08e0, 0x08dc, 0x08d8, 0x01ce, + 0x01ce, + 0x01cf, + }, + { + 136, 5680, 0x65, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x38, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08e4, 0x08e0, 0x08dc, 0x01cd, + 0x01ce, + 0x01ce, + }, + { + 138, 5690, 0x68, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x39, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08e8, 0x08e4, 0x08e0, 0x01cc, + 0x01cd, + 0x01ce, + }, + { + 140, 5700, 0x6c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3a, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08ec, 0x08e8, 0x08e4, 0x01cb, + 0x01cc, + 0x01cd, + }, + { + 142, 5710, 0x6f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3b, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08f0, 0x08ec, 0x08e8, 0x01ca, + 0x01cb, + 0x01cc, + }, + { + 144, 5720, 0x72, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3c, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x02, 0x53, 0x00, 0x00, 0x08f4, 0x08f0, 0x08ec, 0x01c9, + 0x01ca, + 0x01cb, + }, + { + 145, 5725, 0x74, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x79, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x05, 0x01, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x01, 0x53, 0x00, 0x00, 0x08f6, 0x08f2, 0x08ee, 0x01c9, + 0x01ca, + 0x01cb, + }, + { + 146, 5730, 0x76, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3d, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08f8, 0x08f4, 0x08f0, 0x01c9, + 0x01c9, + 0x01ca, + }, + { + 147, 5735, 0x77, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7b, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08fa, 0x08f6, 0x08f2, 0x01c8, + 0x01c9, + 0x01ca, + }, + { + 148, 5740, 0x79, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3e, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08fc, 0x08f8, 0x08f4, 0x01c8, + 0x01c9, + 0x01c9, + }, + { + 149, 5745, 0x7b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7d, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x08fe, 0x08fa, 0x08f6, 0x01c8, + 0x01c8, + 0x01c9, + }, + { + 150, 5750, 0x7c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3f, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, + 0x01c8, + 0x01c9, + }, + { + 151, 5755, 0x7e, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7f, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x53, 0x00, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, + 0x01c8, + 0x01c8, + }, + { + 152, 5760, 0x80, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x40, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, + 0x01c7, + 0x01c8, + }, + { + 153, 5765, 0x81, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x81, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, + 0x01c7, + 0x01c8, + }, + { + 154, 5770, 0x83, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x41, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, + 0x01c6, + 0x01c7, + }, + { + 155, 5775, 0x85, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x83, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x01, 0x43, 0x00, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, + 0x01c6, + 0x01c7, + }, + { + 156, 5780, 0x86, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x42, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x03, 0x01, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x01, 0x43, 0x00, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, + 0x01c6, + 0x01c6, + }, + { + 157, 5785, 0x88, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x85, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, + 0x01c5, + 0x01c6, + }, + { + 158, 5790, 0x8a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x43, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, + 0x01c5, + 0x01c6, + }, + { + 159, 5795, 0x8b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x87, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, + 0x01c4, + 0x01c5, + }, + { + 160, 5800, 0x8d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x44, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, + 0x01c4, + 0x01c5, + }, + { + 161, 5805, 0x8f, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x89, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, + 0x01c4, + 0x01c4, + }, + { + 162, 5810, 0x90, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x45, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, + 0x01c3, + 0x01c4, + }, + { + 163, 5815, 0x92, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8b, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, + 0x01c3, + 0x01c4, + }, + { + 164, 5820, 0x94, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x46, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, + 0x01c2, + 0x01c3, + }, + { + 165, 5825, 0x95, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8d, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, + 0x01c2, + 0x01c3, + }, + { + 166, 5830, 0x97, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x47, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, + 0x01c2, + 0x01c2, + }, + { + 168, 5840, 0x9a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x48, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, + 0x01c1, + 0x01c2, + }, + { + 170, 5850, 0x9e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x49, 0x02, 0x04, + 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, + 0x01c0, + 0x01c1, + }, + { + 172, 5860, 0xa1, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4a, 0x02, 0x04, + 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, + 0x01bf, + 0x01c0, + }, + { + 174, 5870, 0xa4, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4b, 0x02, 0x04, + 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, + 0x01bf, + 0x01bf, + }, + { + 176, 5880, 0xa8, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4c, 0x02, 0x03, + 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, + 0x01be, + 0x01bf, + }, + { + 178, 5890, 0xab, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4d, 0x02, 0x03, + 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, + 0x01bd, + 0x01be, + }, + { + 180, 5900, 0xae, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4e, 0x02, 0x03, + 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x43, 0x00, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, + 0x01bc, + 0x01bd, + }, + { + 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0f, + 0x0a, 0x00, 0x0a, 0x00, 0x71, 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, + 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03c9, 0x03c5, 0x03c1, 0x043a, + 0x043f, + 0x0443, + }, + { + 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0f, + 0x0a, 0x00, 0x0a, 0x00, 0x71, 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, + 0xa3, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cb, 0x03c7, 0x03c3, 0x0438, + 0x043d, + 0x0441, + }, + { + 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0f, + 0x09, 0x00, 0x09, 0x00, 0x71, 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, + 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cd, 0x03c9, 0x03c5, 0x0436, + 0x043a, + 0x043f, + }, + { + 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0f, + 0x09, 0x00, 0x09, 0x00, 0x71, 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x71, + 0x93, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cf, 0x03cb, 0x03c7, 0x0434, + 0x0438, + 0x043d, + }, + { + 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0f, + 0x08, 0x00, 0x08, 0x00, 0x51, 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x51, + 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d1, 0x03cd, 0x03c9, 0x0431, + 0x0436, + 0x043a, + }, + { + 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0f, + 0x08, 0x00, 0x08, 0x00, 0x51, 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x51, + 0x83, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d3, 0x03cf, 0x03cb, 0x042f, + 0x0434, + 0x0438, + }, + { + 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0f, + 0x07, 0x00, 0x07, 0x00, 0x51, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x51, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d5, 0x03d1, 0x03cd, 0x042d, + 0x0431, + 0x0436, + }, + { + 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0f, + 0x07, 0x00, 0x07, 0x00, 0x31, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d7, 0x03d3, 0x03cf, 0x042b, + 0x042f, + 0x0434, + }, + { + 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0f, + 0x07, 0x00, 0x07, 0x00, 0x31, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d9, 0x03d5, 0x03d1, 0x0429, + 0x042d, + 0x0431, + }, + { + 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0f, + 0x06, 0x00, 0x06, 0x00, 0x31, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, + 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03db, 0x03d7, 0x03d3, 0x0427, + 0x042b, + 0x042f, + }, + { + 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0f, + 0x06, 0x00, 0x06, 0x00, 0x31, 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x31, + 0x63, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03dd, 0x03d9, 0x03d5, 0x0424, + 0x0429, + 0x042d, + }, + { + 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0f, + 0x05, 0x00, 0x05, 0x00, 0x11, 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x11, + 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03df, 0x03db, 0x03d7, 0x0422, + 0x0427, + 0x042b, + }, + { + 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0f, + 0x05, 0x00, 0x05, 0x00, 0x11, 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x11, + 0x53, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03e1, 0x03dd, 0x03d9, 0x0420, + 0x0424, + 0x0429, + }, + { + 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0f, + 0x04, 0x00, 0x04, 0x00, 0x11, 0x43, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x11, + 0x43, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03e6, 0x03e2, 0x03de, 0x041b, + 0x041f, + 0x0424} +}; + +static const struct chan_info_nphy_radio2057_rev5 +chan_info_nphyrev8_2057_rev5[] = { + { + 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0d, + 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03c9, 0x03c5, 0x03c1, + 0x043a, 0x043f, 0x0443}, + { + 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0d, + 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03cb, 0x03c7, 0x03c3, + 0x0438, 0x043d, 0x0441}, + { + 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0d, + 0x08, 0x0e, 0x61, 0x03, 0xef, 0x61, 0x03, 0xef, 0x03cd, 0x03c9, 0x03c5, + 0x0436, 0x043a, 0x043f}, + { + 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0c, + 0x08, 0x0e, 0x61, 0x03, 0xdf, 0x61, 0x03, 0xdf, 0x03cf, 0x03cb, 0x03c7, + 0x0434, 0x0438, 0x043d}, + { + 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0c, + 0x07, 0x0d, 0x61, 0x03, 0xcf, 0x61, 0x03, 0xcf, 0x03d1, 0x03cd, 0x03c9, + 0x0431, 0x0436, 0x043a}, + { + 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0c, + 0x07, 0x0d, 0x61, 0x03, 0xbf, 0x61, 0x03, 0xbf, 0x03d3, 0x03cf, 0x03cb, + 0x042f, 0x0434, 0x0438}, + { + 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0b, + 0x07, 0x0d, 0x61, 0x03, 0xaf, 0x61, 0x03, 0xaf, 0x03d5, 0x03d1, 0x03cd, + 0x042d, 0x0431, 0x0436}, + { + 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0b, + 0x07, 0x0d, 0x61, 0x03, 0x9f, 0x61, 0x03, 0x9f, 0x03d7, 0x03d3, 0x03cf, + 0x042b, 0x042f, 0x0434}, + { + 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0b, + 0x07, 0x0d, 0x61, 0x03, 0x8f, 0x61, 0x03, 0x8f, 0x03d9, 0x03d5, 0x03d1, + 0x0429, 0x042d, 0x0431}, + { + 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0b, + 0x07, 0x0c, 0x61, 0x03, 0x7f, 0x61, 0x03, 0x7f, 0x03db, 0x03d7, 0x03d3, + 0x0427, 0x042b, 0x042f}, + { + 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0b, + 0x07, 0x0c, 0x61, 0x03, 0x6f, 0x61, 0x03, 0x6f, 0x03dd, 0x03d9, 0x03d5, + 0x0424, 0x0429, 0x042d}, + { + 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0b, + 0x06, 0x0c, 0x61, 0x03, 0x5f, 0x61, 0x03, 0x5f, 0x03df, 0x03db, 0x03d7, + 0x0422, 0x0427, 0x042b}, + { + 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0a, + 0x06, 0x0b, 0x61, 0x03, 0x4f, 0x61, 0x03, 0x4f, 0x03e1, 0x03dd, 0x03d9, + 0x0420, 0x0424, 0x0429}, + { + 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0a, + 0x06, 0x0b, 0x61, 0x03, 0x3f, 0x61, 0x03, 0x3f, 0x03e6, 0x03e2, 0x03de, + 0x041b, 0x041f, 0x0424} +}; + +static const struct chan_info_nphy_radio2057_rev5 +chan_info_nphyrev9_2057_rev5v1[] = { + { + 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0d, + 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03c9, 0x03c5, 0x03c1, + 0x043a, 0x043f, 0x0443}, + { + 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0d, + 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, 0x03, 0xff, 0x03cb, 0x03c7, 0x03c3, + 0x0438, 0x043d, 0x0441}, + { + 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0d, + 0x08, 0x0e, 0x61, 0x03, 0xef, 0x61, 0x03, 0xef, 0x03cd, 0x03c9, 0x03c5, + 0x0436, 0x043a, 0x043f}, + { + 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0c, + 0x08, 0x0e, 0x61, 0x03, 0xdf, 0x61, 0x03, 0xdf, 0x03cf, 0x03cb, 0x03c7, + 0x0434, 0x0438, 0x043d}, + { + 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0c, + 0x07, 0x0d, 0x61, 0x03, 0xcf, 0x61, 0x03, 0xcf, 0x03d1, 0x03cd, 0x03c9, + 0x0431, 0x0436, 0x043a}, + { + 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0c, + 0x07, 0x0d, 0x61, 0x03, 0xbf, 0x61, 0x03, 0xbf, 0x03d3, 0x03cf, 0x03cb, + 0x042f, 0x0434, 0x0438}, + { + 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0b, + 0x07, 0x0d, 0x61, 0x03, 0xaf, 0x61, 0x03, 0xaf, 0x03d5, 0x03d1, 0x03cd, + 0x042d, 0x0431, 0x0436}, + { + 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0b, + 0x07, 0x0d, 0x61, 0x03, 0x9f, 0x61, 0x03, 0x9f, 0x03d7, 0x03d3, 0x03cf, + 0x042b, 0x042f, 0x0434}, + { + 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0b, + 0x07, 0x0d, 0x61, 0x03, 0x8f, 0x61, 0x03, 0x8f, 0x03d9, 0x03d5, 0x03d1, + 0x0429, 0x042d, 0x0431}, + { + 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0b, + 0x07, 0x0c, 0x61, 0x03, 0x7f, 0x61, 0x03, 0x7f, 0x03db, 0x03d7, 0x03d3, + 0x0427, 0x042b, 0x042f}, + { + 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0b, + 0x07, 0x0c, 0x61, 0x03, 0x6f, 0x61, 0x03, 0x6f, 0x03dd, 0x03d9, 0x03d5, + 0x0424, 0x0429, 0x042d}, + { + 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0b, + 0x06, 0x0c, 0x61, 0x03, 0x5f, 0x61, 0x03, 0x5f, 0x03df, 0x03db, 0x03d7, + 0x0422, 0x0427, 0x042b}, + { + 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0a, + 0x06, 0x0b, 0x61, 0x03, 0x4f, 0x61, 0x03, 0x4f, 0x03e1, 0x03dd, 0x03d9, + 0x0420, 0x0424, 0x0429}, + { + 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0a, + 0x06, 0x0b, 0x61, 0x03, 0x3f, 0x61, 0x03, 0x3f, 0x03e6, 0x03e2, 0x03de, + 0x041b, 0x041f, 0x0424} +}; + +static const struct chan_info_nphy_radio2057 chan_info_nphyrev8_2057_rev7[] = { + { + 184, 4920, 0x68, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xec, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07b4, 0x07b0, 0x07ac, 0x0214, + 0x0215, + 0x0216}, + { + 186, 4930, 0x6b, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xed, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07b8, 0x07b4, 0x07b0, 0x0213, + 0x0214, + 0x0215}, + { + 188, 4940, 0x6e, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xee, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07bc, 0x07b8, 0x07b4, 0x0212, + 0x0213, + 0x0214}, + { + 190, 4950, 0x72, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xef, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c0, 0x07bc, 0x07b8, 0x0211, + 0x0212, + 0x0213}, + { + 192, 4960, 0x75, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf0, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c4, 0x07c0, 0x07bc, 0x020f, + 0x0211, + 0x0212}, + { + 194, 4970, 0x78, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf1, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c8, 0x07c4, 0x07c0, 0x020e, + 0x020f, + 0x0211}, + { + 196, 4980, 0x7c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf2, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07cc, 0x07c8, 0x07c4, 0x020d, + 0x020e, + 0x020f}, + { + 198, 4990, 0x7f, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf3, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07d0, 0x07cc, 0x07c8, 0x020c, + 0x020d, + 0x020e}, + { + 200, 5000, 0x82, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf4, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d4, 0x07d0, 0x07cc, 0x020b, + 0x020c, + 0x020d}, + { + 202, 5010, 0x86, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf5, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d8, 0x07d4, 0x07d0, 0x020a, + 0x020b, + 0x020c}, + { + 204, 5020, 0x89, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf6, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07dc, 0x07d8, 0x07d4, 0x0209, + 0x020a, + 0x020b}, + { + 206, 5030, 0x8c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf7, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e0, 0x07dc, 0x07d8, 0x0208, + 0x0209, + 0x020a}, + { + 208, 5040, 0x90, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf8, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e4, 0x07e0, 0x07dc, 0x0207, + 0x0208, + 0x0209}, + { + 210, 5050, 0x93, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf9, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e8, 0x07e4, 0x07e0, 0x0206, + 0x0207, + 0x0208}, + { + 212, 5060, 0x96, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfa, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07ec, 0x07e8, 0x07e4, 0x0205, + 0x0206, + 0x0207}, + { + 214, 5070, 0x9a, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfb, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f0, 0x07ec, 0x07e8, 0x0204, + 0x0205, + 0x0206}, + { + 216, 5080, 0x9d, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfc, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f4, 0x07f0, 0x07ec, 0x0203, + 0x0204, + 0x0205}, + { + 218, 5090, 0xa0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfd, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f8, 0x07f4, 0x07f0, 0x0202, + 0x0203, + 0x0204}, + { + 220, 5100, 0xa4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfe, 0x01, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x07fc, 0x07f8, 0x07f4, 0x0201, + 0x0202, + 0x0203}, + { + 222, 5110, 0xa7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xff, 0x01, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0800, 0x07fc, 0x07f8, 0x0200, + 0x0201, + 0x0202}, + { + 224, 5120, 0xaa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x00, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0804, 0x0800, 0x07fc, 0x01ff, + 0x0200, + 0x0201}, + { + 226, 5130, 0xae, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x01, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0808, 0x0804, 0x0800, 0x01fe, + 0x01ff, + 0x0200}, + { + 228, 5140, 0xb1, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x02, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x080c, 0x0808, 0x0804, 0x01fd, + 0x01fe, + 0x01ff}, + { + 32, 5160, 0xb8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x04, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0814, 0x0810, 0x080c, 0x01fb, + 0x01fc, + 0x01fd}, + { + 34, 5170, 0xbb, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x05, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0818, 0x0814, 0x0810, 0x01fa, + 0x01fb, + 0x01fc}, + { + 36, 5180, 0xbe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x06, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x081c, 0x0818, 0x0814, 0x01f9, + 0x01fa, + 0x01fb}, + { + 38, 5190, 0xc2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x07, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0820, 0x081c, 0x0818, 0x01f8, + 0x01f9, + 0x01fa}, + { + 40, 5200, 0xc5, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x08, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0824, 0x0820, 0x081c, 0x01f7, + 0x01f8, + 0x01f9}, + { + 42, 5210, 0xc8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x09, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0828, 0x0824, 0x0820, 0x01f6, + 0x01f7, + 0x01f8}, + { + 44, 5220, 0xcc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0a, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x082c, 0x0828, 0x0824, 0x01f5, + 0x01f6, + 0x01f7}, + { + 46, 5230, 0xcf, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0b, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0830, 0x082c, 0x0828, 0x01f4, + 0x01f5, + 0x01f6}, + { + 48, 5240, 0xd2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0c, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0834, 0x0830, 0x082c, 0x01f3, + 0x01f4, + 0x01f5}, + { + 50, 5250, 0xd6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0d, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0838, 0x0834, 0x0830, 0x01f2, + 0x01f3, + 0x01f4}, + { + 52, 5260, 0xd9, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0e, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x083c, 0x0838, 0x0834, 0x01f1, + 0x01f2, + 0x01f3}, + { + 54, 5270, 0xdc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0f, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0840, 0x083c, 0x0838, 0x01f0, + 0x01f1, + 0x01f2}, + { + 56, 5280, 0xe0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x10, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0844, 0x0840, 0x083c, 0x01f0, + 0x01f0, + 0x01f1}, + { + 58, 5290, 0xe3, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x11, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0848, 0x0844, 0x0840, 0x01ef, + 0x01f0, + 0x01f0}, + { + 60, 5300, 0xe6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x12, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x084c, 0x0848, 0x0844, 0x01ee, + 0x01ef, + 0x01f0}, + { + 62, 5310, 0xea, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x13, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0850, 0x084c, 0x0848, 0x01ed, + 0x01ee, + 0x01ef}, + { + 64, 5320, 0xed, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x14, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0854, 0x0850, 0x084c, 0x01ec, + 0x01ed, + 0x01ee}, + { + 66, 5330, 0xf0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x15, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0858, 0x0854, 0x0850, 0x01eb, + 0x01ec, + 0x01ed}, + { + 68, 5340, 0xf4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x16, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x085c, 0x0858, 0x0854, 0x01ea, + 0x01eb, + 0x01ec}, + { + 70, 5350, 0xf7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x17, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0860, 0x085c, 0x0858, 0x01e9, + 0x01ea, + 0x01eb}, + { + 72, 5360, 0xfa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x18, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0864, 0x0860, 0x085c, 0x01e8, + 0x01e9, + 0x01ea}, + { + 74, 5370, 0xfe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x19, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0868, 0x0864, 0x0860, 0x01e7, + 0x01e8, + 0x01e9}, + { + 76, 5380, 0x01, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1a, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x086c, 0x0868, 0x0864, 0x01e6, + 0x01e7, + 0x01e8}, + { + 78, 5390, 0x04, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1b, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0870, 0x086c, 0x0868, 0x01e5, + 0x01e6, + 0x01e7}, + { + 80, 5400, 0x08, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1c, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0874, 0x0870, 0x086c, 0x01e5, + 0x01e5, + 0x01e6}, + { + 82, 5410, 0x0b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1d, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0878, 0x0874, 0x0870, 0x01e4, + 0x01e5, + 0x01e5}, + { + 84, 5420, 0x0e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1e, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x087c, 0x0878, 0x0874, 0x01e3, + 0x01e4, + 0x01e5}, + { + 86, 5430, 0x12, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1f, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0880, 0x087c, 0x0878, 0x01e2, + 0x01e3, + 0x01e4}, + { + 88, 5440, 0x15, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x20, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0884, 0x0880, 0x087c, 0x01e1, + 0x01e2, + 0x01e3}, + { + 90, 5450, 0x18, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x21, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0888, 0x0884, 0x0880, 0x01e0, + 0x01e1, + 0x01e2}, + { + 92, 5460, 0x1c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x22, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x088c, 0x0888, 0x0884, 0x01df, + 0x01e0, + 0x01e1}, + { + 94, 5470, 0x1f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x23, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0890, 0x088c, 0x0888, 0x01de, + 0x01df, + 0x01e0}, + { + 96, 5480, 0x22, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x24, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0894, 0x0890, 0x088c, 0x01dd, + 0x01de, + 0x01df}, + { + 98, 5490, 0x26, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x25, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0898, 0x0894, 0x0890, 0x01dd, + 0x01dd, + 0x01de}, + { + 100, 5500, 0x29, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x26, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x089c, 0x0898, 0x0894, 0x01dc, + 0x01dd, + 0x01dd}, + { + 102, 5510, 0x2c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x27, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a0, 0x089c, 0x0898, 0x01db, + 0x01dc, + 0x01dd}, + { + 104, 5520, 0x30, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x28, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a4, 0x08a0, 0x089c, 0x01da, + 0x01db, + 0x01dc}, + { + 106, 5530, 0x33, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x29, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a8, 0x08a4, 0x08a0, 0x01d9, + 0x01da, + 0x01db}, + { + 108, 5540, 0x36, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2a, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08ac, 0x08a8, 0x08a4, 0x01d8, + 0x01d9, + 0x01da}, + { + 110, 5550, 0x3a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2b, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b0, 0x08ac, 0x08a8, 0x01d7, + 0x01d8, + 0x01d9}, + { + 112, 5560, 0x3d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2c, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b4, 0x08b0, 0x08ac, 0x01d7, + 0x01d7, + 0x01d8}, + { + 114, 5570, 0x40, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2d, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b8, 0x08b4, 0x08b0, 0x01d6, + 0x01d7, + 0x01d7}, + { + 116, 5580, 0x44, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2e, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08bc, 0x08b8, 0x08b4, 0x01d5, + 0x01d6, + 0x01d7}, + { + 118, 5590, 0x47, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2f, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08c0, 0x08bc, 0x08b8, 0x01d4, + 0x01d5, + 0x01d6}, + { + 120, 5600, 0x4a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x30, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c4, 0x08c0, 0x08bc, 0x01d3, + 0x01d4, + 0x01d5}, + { + 122, 5610, 0x4e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x31, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c8, 0x08c4, 0x08c0, 0x01d2, + 0x01d3, + 0x01d4}, + { + 124, 5620, 0x51, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x32, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08cc, 0x08c8, 0x08c4, 0x01d2, + 0x01d2, + 0x01d3}, + { + 126, 5630, 0x54, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x33, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d0, 0x08cc, 0x08c8, 0x01d1, + 0x01d2, + 0x01d2}, + { + 128, 5640, 0x58, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x34, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d4, 0x08d0, 0x08cc, 0x01d0, + 0x01d1, + 0x01d2}, + { + 130, 5650, 0x5b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x35, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08d8, 0x08d4, 0x08d0, 0x01cf, + 0x01d0, + 0x01d1}, + { + 132, 5660, 0x5e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x36, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08dc, 0x08d8, 0x08d4, 0x01ce, + 0x01cf, + 0x01d0}, + { + 134, 5670, 0x62, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x37, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08e0, 0x08dc, 0x08d8, 0x01ce, + 0x01ce, + 0x01cf}, + { + 136, 5680, 0x65, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x38, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e4, 0x08e0, 0x08dc, 0x01cd, + 0x01ce, + 0x01ce}, + { + 138, 5690, 0x68, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x39, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e8, 0x08e4, 0x08e0, 0x01cc, + 0x01cd, + 0x01ce}, + { + 140, 5700, 0x6c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3a, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08ec, 0x08e8, 0x08e4, 0x01cb, + 0x01cc, + 0x01cd}, + { + 142, 5710, 0x6f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3b, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f0, 0x08ec, 0x08e8, 0x01ca, + 0x01cb, + 0x01cc}, + { + 144, 5720, 0x72, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3c, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f4, 0x08f0, 0x08ec, 0x01c9, + 0x01ca, + 0x01cb}, + { + 145, 5725, 0x74, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x79, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f6, 0x08f2, 0x08ee, 0x01c9, + 0x01ca, + 0x01cb}, + { + 146, 5730, 0x76, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3d, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f8, 0x08f4, 0x08f0, 0x01c9, + 0x01c9, + 0x01ca}, + { + 147, 5735, 0x77, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7b, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fa, 0x08f6, 0x08f2, 0x01c8, + 0x01c9, + 0x01ca}, + { + 148, 5740, 0x79, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3e, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fc, 0x08f8, 0x08f4, 0x01c8, + 0x01c9, + 0x01c9}, + { + 149, 5745, 0x7b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7d, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fe, 0x08fa, 0x08f6, 0x01c8, + 0x01c8, + 0x01c9}, + { + 150, 5750, 0x7c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3f, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, + 0x01c8, + 0x01c9}, + { + 151, 5755, 0x7e, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7f, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, + 0x01c8, + 0x01c8}, + { + 152, 5760, 0x80, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x40, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, + 0x01c7, + 0x01c8}, + { + 153, 5765, 0x81, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x81, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, + 0x01c7, + 0x01c8}, + { + 154, 5770, 0x83, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x41, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, + 0x01c6, + 0x01c7}, + { + 155, 5775, 0x85, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x83, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, + 0x01c6, + 0x01c7}, + { + 156, 5780, 0x86, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x42, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, + 0x01c6, + 0x01c6}, + { + 157, 5785, 0x88, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x85, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, + 0x01c5, + 0x01c6}, + { + 158, 5790, 0x8a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x43, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, + 0x01c5, + 0x01c6}, + { + 159, 5795, 0x8b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x87, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, + 0x01c4, + 0x01c5}, + { + 160, 5800, 0x8d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x44, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, + 0x01c4, + 0x01c5}, + { + 161, 5805, 0x8f, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x89, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, + 0x01c4, + 0x01c4}, + { + 162, 5810, 0x90, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x45, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, + 0x01c3, + 0x01c4}, + { + 163, 5815, 0x92, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8b, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, + 0x01c3, + 0x01c4}, + { + 164, 5820, 0x94, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x46, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, + 0x01c2, + 0x01c3}, + { + 165, 5825, 0x95, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8d, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, + 0x01c2, + 0x01c3}, + { + 166, 5830, 0x97, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x47, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, + 0x01c2, + 0x01c2}, + { + 168, 5840, 0x9a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x48, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, + 0x01c1, + 0x01c2}, + { + 170, 5850, 0x9e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x49, 0x02, 0x04, + 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, + 0x01c0, + 0x01c1}, + { + 172, 5860, 0xa1, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4a, 0x02, 0x04, + 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, + 0x01bf, + 0x01c0}, + { + 174, 5870, 0xa4, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4b, 0x02, 0x04, + 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, + 0x01bf, + 0x01bf}, + { + 176, 5880, 0xa8, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4c, 0x02, 0x03, + 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, + 0x01be, + 0x01bf}, + { + 178, 5890, 0xab, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4d, 0x02, 0x03, + 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, + 0x01bd, + 0x01be}, + { + 180, 5900, 0xae, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4e, 0x02, 0x03, + 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, + 0x01bc, + 0x01bd}, + { + 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0f, + 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03c9, 0x03c5, 0x03c1, 0x043a, + 0x043f, + 0x0443}, + { + 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0f, + 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cb, 0x03c7, 0x03c3, 0x0438, + 0x043d, + 0x0441}, + { + 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0f, + 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cd, 0x03c9, 0x03c5, 0x0436, + 0x043a, + 0x043f}, + { + 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0f, + 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cf, 0x03cb, 0x03c7, 0x0434, + 0x0438, + 0x043d}, + { + 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0f, + 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d1, 0x03cd, 0x03c9, 0x0431, + 0x0436, + 0x043a}, + { + 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0f, + 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d3, 0x03cf, 0x03cb, 0x042f, + 0x0434, + 0x0438}, + { + 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0f, + 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d5, 0x03d1, 0x03cd, 0x042d, + 0x0431, + 0x0436}, + { + 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0f, + 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d7, 0x03d3, 0x03cf, 0x042b, + 0x042f, + 0x0434}, + { + 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0f, + 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d9, 0x03d5, 0x03d1, 0x0429, + 0x042d, + 0x0431}, + { + 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0f, + 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03db, 0x03d7, 0x03d3, 0x0427, + 0x042b, + 0x042f}, + { + 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0f, + 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03dd, 0x03d9, 0x03d5, 0x0424, + 0x0429, + 0x042d}, + { + 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0f, + 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03df, 0x03db, 0x03d7, 0x0422, + 0x0427, + 0x042b}, + { + 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0f, + 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03e1, 0x03dd, 0x03d9, 0x0420, + 0x0424, + 0x0429}, + { + 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0f, + 0x04, 0x00, 0x04, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03e6, 0x03e2, 0x03de, 0x041b, + 0x041f, + 0x0424} +}; + +static const struct chan_info_nphy_radio2057 chan_info_nphyrev8_2057_rev8[] = { + { + 186, 4930, 0x6b, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xed, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07b8, 0x07b4, 0x07b0, 0x0213, + 0x0214, + 0x0215}, + { + 188, 4940, 0x6e, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xee, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0xd3, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07bc, 0x07b8, 0x07b4, 0x0212, + 0x0213, + 0x0214}, + { + 190, 4950, 0x72, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xef, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c0, 0x07bc, 0x07b8, 0x0211, + 0x0212, + 0x0213}, + { + 192, 4960, 0x75, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf0, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c4, 0x07c0, 0x07bc, 0x020f, + 0x0211, + 0x0212}, + { + 194, 4970, 0x78, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf1, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07c8, 0x07c4, 0x07c0, 0x020e, + 0x020f, + 0x0211}, + { + 196, 4980, 0x7c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf2, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07cc, 0x07c8, 0x07c4, 0x020d, + 0x020e, + 0x020f}, + { + 198, 4990, 0x7f, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf3, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xd3, 0x00, 0xff, 0x07d0, 0x07cc, 0x07c8, 0x020c, + 0x020d, + 0x020e}, + { + 200, 5000, 0x82, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf4, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d4, 0x07d0, 0x07cc, 0x020b, + 0x020c, + 0x020d}, + { + 202, 5010, 0x86, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf5, 0x01, 0x0f, + 0x00, 0x0f, 0x00, 0xff, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07d8, 0x07d4, 0x07d0, 0x020a, + 0x020b, + 0x020c}, + { + 204, 5020, 0x89, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf6, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07dc, 0x07d8, 0x07d4, 0x0209, + 0x020a, + 0x020b}, + { + 206, 5030, 0x8c, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf7, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e0, 0x07dc, 0x07d8, 0x0208, + 0x0209, + 0x020a}, + { + 208, 5040, 0x90, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf8, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e4, 0x07e0, 0x07dc, 0x0207, + 0x0208, + 0x0209}, + { + 210, 5050, 0x93, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xf9, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07e8, 0x07e4, 0x07e0, 0x0206, + 0x0207, + 0x0208}, + { + 212, 5060, 0x96, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfa, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07ec, 0x07e8, 0x07e4, 0x0205, + 0x0206, + 0x0207}, + { + 214, 5070, 0x9a, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfb, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f0, 0x07ec, 0x07e8, 0x0204, + 0x0205, + 0x0206}, + { + 216, 5080, 0x9d, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfc, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f4, 0x07f0, 0x07ec, 0x0203, + 0x0204, + 0x0205}, + { + 218, 5090, 0xa0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfd, 0x01, 0x0e, + 0x00, 0x0e, 0x00, 0xee, 0x00, 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x00, + 0x00, 0x0f, 0x0f, 0xb3, 0x00, 0xff, 0x07f8, 0x07f4, 0x07f0, 0x0202, + 0x0203, + 0x0204}, + { + 220, 5100, 0xa4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xfe, 0x01, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x07fc, 0x07f8, 0x07f4, 0x0201, + 0x0202, + 0x0203}, + { + 222, 5110, 0xa7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0xff, 0x01, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0800, 0x07fc, 0x07f8, 0x0200, + 0x0201, + 0x0202}, + { + 224, 5120, 0xaa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x00, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0804, 0x0800, 0x07fc, 0x01ff, + 0x0200, + 0x0201}, + { + 226, 5130, 0xae, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x01, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0808, 0x0804, 0x0800, 0x01fe, + 0x01ff, + 0x0200}, + { + 228, 5140, 0xb1, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x02, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x080c, 0x0808, 0x0804, 0x01fd, + 0x01fe, + 0x01ff}, + { + 32, 5160, 0xb8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x04, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0814, 0x0810, 0x080c, 0x01fb, + 0x01fc, + 0x01fd}, + { + 34, 5170, 0xbb, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x05, 0x02, 0x0d, + 0x00, 0x0d, 0x00, 0xdd, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0818, 0x0814, 0x0810, 0x01fa, + 0x01fb, + 0x01fc}, + { + 36, 5180, 0xbe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x06, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x081c, 0x0818, 0x0814, 0x01f9, + 0x01fa, + 0x01fb}, + { + 38, 5190, 0xc2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x07, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x00, + 0x00, 0x0f, 0x0f, 0xa3, 0x00, 0xfc, 0x0820, 0x081c, 0x0818, 0x01f8, + 0x01f9, + 0x01fa}, + { + 40, 5200, 0xc5, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x08, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0824, 0x0820, 0x081c, 0x01f7, + 0x01f8, + 0x01f9}, + { + 42, 5210, 0xc8, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x09, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0828, 0x0824, 0x0820, 0x01f6, + 0x01f7, + 0x01f8}, + { + 44, 5220, 0xcc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0a, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x082c, 0x0828, 0x0824, 0x01f5, + 0x01f6, + 0x01f7}, + { + 46, 5230, 0xcf, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0b, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0830, 0x082c, 0x0828, 0x01f4, + 0x01f5, + 0x01f6}, + { + 48, 5240, 0xd2, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0c, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0834, 0x0830, 0x082c, 0x01f3, + 0x01f4, + 0x01f5}, + { + 50, 5250, 0xd6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0d, 0x02, 0x0c, + 0x00, 0x0c, 0x00, 0xcc, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0838, 0x0834, 0x0830, 0x01f2, + 0x01f3, + 0x01f4}, + { + 52, 5260, 0xd9, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0e, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x083c, 0x0838, 0x0834, 0x01f1, + 0x01f2, + 0x01f3}, + { + 54, 5270, 0xdc, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x0f, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0840, 0x083c, 0x0838, 0x01f0, + 0x01f1, + 0x01f2}, + { + 56, 5280, 0xe0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x10, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0844, 0x0840, 0x083c, 0x01f0, + 0x01f0, + 0x01f1}, + { + 58, 5290, 0xe3, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x11, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x00, + 0x00, 0x0f, 0x0f, 0x93, 0x00, 0xf8, 0x0848, 0x0844, 0x0840, 0x01ef, + 0x01f0, + 0x01f0}, + { + 60, 5300, 0xe6, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x12, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x084c, 0x0848, 0x0844, 0x01ee, + 0x01ef, + 0x01f0}, + { + 62, 5310, 0xea, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x13, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0850, 0x084c, 0x0848, 0x01ed, + 0x01ee, + 0x01ef}, + { + 64, 5320, 0xed, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x14, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0854, 0x0850, 0x084c, 0x01ec, + 0x01ed, + 0x01ee}, + { + 66, 5330, 0xf0, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x15, 0x02, 0x0b, + 0x00, 0x0b, 0x00, 0xbb, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0858, 0x0854, 0x0850, 0x01eb, + 0x01ec, + 0x01ed}, + { + 68, 5340, 0xf4, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x16, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x085c, 0x0858, 0x0854, 0x01ea, + 0x01eb, + 0x01ec}, + { + 70, 5350, 0xf7, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x17, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0860, 0x085c, 0x0858, 0x01e9, + 0x01ea, + 0x01eb}, + { + 72, 5360, 0xfa, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x18, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0864, 0x0860, 0x085c, 0x01e8, + 0x01e9, + 0x01ea}, + { + 74, 5370, 0xfe, 0x16, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x19, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0868, 0x0864, 0x0860, 0x01e7, + 0x01e8, + 0x01e9}, + { + 76, 5380, 0x01, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1a, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x086c, 0x0868, 0x0864, 0x01e6, + 0x01e7, + 0x01e8}, + { + 78, 5390, 0x04, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1b, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x00, + 0x00, 0x0f, 0x0c, 0x83, 0x00, 0xf5, 0x0870, 0x086c, 0x0868, 0x01e5, + 0x01e6, + 0x01e7}, + { + 80, 5400, 0x08, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1c, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0874, 0x0870, 0x086c, 0x01e5, + 0x01e5, + 0x01e6}, + { + 82, 5410, 0x0b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1d, 0x02, 0x0a, + 0x00, 0x0a, 0x00, 0xaa, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0878, 0x0874, 0x0870, 0x01e4, + 0x01e5, + 0x01e5}, + { + 84, 5420, 0x0e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1e, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x087c, 0x0878, 0x0874, 0x01e3, + 0x01e4, + 0x01e5}, + { + 86, 5430, 0x12, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x1f, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0880, 0x087c, 0x0878, 0x01e2, + 0x01e3, + 0x01e4}, + { + 88, 5440, 0x15, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x20, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0884, 0x0880, 0x087c, 0x01e1, + 0x01e2, + 0x01e3}, + { + 90, 5450, 0x18, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x21, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0888, 0x0884, 0x0880, 0x01e0, + 0x01e1, + 0x01e2}, + { + 92, 5460, 0x1c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x22, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x088c, 0x0888, 0x0884, 0x01df, + 0x01e0, + 0x01e1}, + { + 94, 5470, 0x1f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x23, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0890, 0x088c, 0x0888, 0x01de, + 0x01df, + 0x01e0}, + { + 96, 5480, 0x22, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x24, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0894, 0x0890, 0x088c, 0x01dd, + 0x01de, + 0x01df}, + { + 98, 5490, 0x26, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x25, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x00, + 0x00, 0x0d, 0x09, 0x53, 0x00, 0xb1, 0x0898, 0x0894, 0x0890, 0x01dd, + 0x01dd, + 0x01de}, + { + 100, 5500, 0x29, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x26, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x089c, 0x0898, 0x0894, 0x01dc, + 0x01dd, + 0x01dd}, + { + 102, 5510, 0x2c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x27, 0x02, 0x09, + 0x00, 0x09, 0x00, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a0, 0x089c, 0x0898, 0x01db, + 0x01dc, + 0x01dd}, + { + 104, 5520, 0x30, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x28, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a4, 0x08a0, 0x089c, 0x01da, + 0x01db, + 0x01dc}, + { + 106, 5530, 0x33, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x29, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08a8, 0x08a4, 0x08a0, 0x01d9, + 0x01da, + 0x01db}, + { + 108, 5540, 0x36, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2a, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08ac, 0x08a8, 0x08a4, 0x01d8, + 0x01d9, + 0x01da}, + { + 110, 5550, 0x3a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2b, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b0, 0x08ac, 0x08a8, 0x01d7, + 0x01d8, + 0x01d9}, + { + 112, 5560, 0x3d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2c, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b4, 0x08b0, 0x08ac, 0x01d7, + 0x01d7, + 0x01d8}, + { + 114, 5570, 0x40, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2d, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08b8, 0x08b4, 0x08b0, 0x01d6, + 0x01d7, + 0x01d7}, + { + 116, 5580, 0x44, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2e, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08bc, 0x08b8, 0x08b4, 0x01d5, + 0x01d6, + 0x01d7}, + { + 118, 5590, 0x47, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x2f, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x00, + 0x00, 0x0a, 0x06, 0x43, 0x00, 0x80, 0x08c0, 0x08bc, 0x08b8, 0x01d4, + 0x01d5, + 0x01d6}, + { + 120, 5600, 0x4a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x30, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c4, 0x08c0, 0x08bc, 0x01d3, + 0x01d4, + 0x01d5}, + { + 122, 5610, 0x4e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x31, 0x02, 0x08, + 0x00, 0x08, 0x00, 0x88, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08c8, 0x08c4, 0x08c0, 0x01d2, + 0x01d3, + 0x01d4}, + { + 124, 5620, 0x51, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x32, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08cc, 0x08c8, 0x08c4, 0x01d2, + 0x01d2, + 0x01d3}, + { + 126, 5630, 0x54, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x33, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d0, 0x08cc, 0x08c8, 0x01d1, + 0x01d2, + 0x01d2}, + { + 128, 5640, 0x58, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x34, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x04, 0x23, 0x00, 0x60, 0x08d4, 0x08d0, 0x08cc, 0x01d0, + 0x01d1, + 0x01d2}, + { + 130, 5650, 0x5b, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x35, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08d8, 0x08d4, 0x08d0, 0x01cf, + 0x01d0, + 0x01d1}, + { + 132, 5660, 0x5e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x36, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08dc, 0x08d8, 0x08d4, 0x01ce, + 0x01cf, + 0x01d0}, + { + 134, 5670, 0x62, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x37, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x03, 0x23, 0x00, 0x60, 0x08e0, 0x08dc, 0x08d8, 0x01ce, + 0x01ce, + 0x01cf}, + { + 136, 5680, 0x65, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x38, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e4, 0x08e0, 0x08dc, 0x01cd, + 0x01ce, + 0x01ce}, + { + 138, 5690, 0x68, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x39, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x00, + 0x00, 0x09, 0x02, 0x23, 0x00, 0x60, 0x08e8, 0x08e4, 0x08e0, 0x01cc, + 0x01cd, + 0x01ce}, + { + 140, 5700, 0x6c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3a, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08ec, 0x08e8, 0x08e4, 0x01cb, + 0x01cc, + 0x01cd}, + { + 142, 5710, 0x6f, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3b, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f0, 0x08ec, 0x08e8, 0x01ca, + 0x01cb, + 0x01cc}, + { + 144, 5720, 0x72, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3c, 0x02, 0x07, + 0x00, 0x07, 0x00, 0x77, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f4, 0x08f0, 0x08ec, 0x01c9, + 0x01ca, + 0x01cb}, + { + 145, 5725, 0x74, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x79, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f6, 0x08f2, 0x08ee, 0x01c9, + 0x01ca, + 0x01cb}, + { + 146, 5730, 0x76, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3d, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08f8, 0x08f4, 0x08f0, 0x01c9, + 0x01c9, + 0x01ca}, + { + 147, 5735, 0x77, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7b, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fa, 0x08f6, 0x08f2, 0x01c8, + 0x01c9, + 0x01ca}, + { + 148, 5740, 0x79, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3e, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fc, 0x08f8, 0x08f4, 0x01c8, + 0x01c9, + 0x01c9}, + { + 149, 5745, 0x7b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7d, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x30, 0x08fe, 0x08fa, 0x08f6, 0x01c8, + 0x01c8, + 0x01c9}, + { + 150, 5750, 0x7c, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x3f, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0900, 0x08fc, 0x08f8, 0x01c7, + 0x01c8, + 0x01c9}, + { + 151, 5755, 0x7e, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x7f, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0902, 0x08fe, 0x08fa, 0x01c7, + 0x01c8, + 0x01c8}, + { + 152, 5760, 0x80, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x40, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0904, 0x0900, 0x08fc, 0x01c6, + 0x01c7, + 0x01c8}, + { + 153, 5765, 0x81, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x81, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0906, 0x0902, 0x08fe, 0x01c6, + 0x01c7, + 0x01c8}, + { + 154, 5770, 0x83, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x41, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0908, 0x0904, 0x0900, 0x01c6, + 0x01c6, + 0x01c7}, + { + 155, 5775, 0x85, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x83, 0x04, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090a, 0x0906, 0x0902, 0x01c5, + 0x01c6, + 0x01c7}, + { + 156, 5780, 0x86, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x42, 0x02, 0x06, + 0x00, 0x06, 0x00, 0x66, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090c, 0x0908, 0x0904, 0x01c5, + 0x01c6, + 0x01c6}, + { + 157, 5785, 0x88, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x85, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x090e, 0x090a, 0x0906, 0x01c4, + 0x01c5, + 0x01c6}, + { + 158, 5790, 0x8a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x43, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0910, 0x090c, 0x0908, 0x01c4, + 0x01c5, + 0x01c6}, + { + 159, 5795, 0x8b, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x87, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x02, 0x13, 0x00, 0x00, 0x0912, 0x090e, 0x090a, 0x01c4, + 0x01c4, + 0x01c5}, + { + 160, 5800, 0x8d, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x44, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x01, 0x03, 0x00, 0x00, 0x0914, 0x0910, 0x090c, 0x01c3, + 0x01c4, + 0x01c5}, + { + 161, 5805, 0x8f, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x89, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0916, 0x0912, 0x090e, 0x01c3, + 0x01c4, + 0x01c4}, + { + 162, 5810, 0x90, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x45, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0918, 0x0914, 0x0910, 0x01c2, + 0x01c3, + 0x01c4}, + { + 163, 5815, 0x92, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8b, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091a, 0x0916, 0x0912, 0x01c2, + 0x01c3, + 0x01c4}, + { + 164, 5820, 0x94, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x46, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091c, 0x0918, 0x0914, 0x01c2, + 0x01c2, + 0x01c3}, + { + 165, 5825, 0x95, 0x17, 0x20, 0x14, 0x08, 0x08, 0x30, 0x8d, 0x04, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x091e, 0x091a, 0x0916, 0x01c1, + 0x01c2, + 0x01c3}, + { + 166, 5830, 0x97, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x47, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0920, 0x091c, 0x0918, 0x01c1, + 0x01c2, + 0x01c2}, + { + 168, 5840, 0x9a, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x48, 0x02, 0x05, + 0x00, 0x05, 0x00, 0x55, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0924, 0x0920, 0x091c, 0x01c0, + 0x01c1, + 0x01c2}, + { + 170, 5850, 0x9e, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x49, 0x02, 0x04, + 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0928, 0x0924, 0x0920, 0x01bf, + 0x01c0, + 0x01c1}, + { + 172, 5860, 0xa1, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4a, 0x02, 0x04, + 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x092c, 0x0928, 0x0924, 0x01bf, + 0x01bf, + 0x01c0}, + { + 174, 5870, 0xa4, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4b, 0x02, 0x04, + 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0930, 0x092c, 0x0928, 0x01be, + 0x01bf, + 0x01bf}, + { + 176, 5880, 0xa8, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4c, 0x02, 0x03, + 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0934, 0x0930, 0x092c, 0x01bd, + 0x01be, + 0x01bf}, + { + 178, 5890, 0xab, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4d, 0x02, 0x03, + 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x0938, 0x0934, 0x0930, 0x01bc, + 0x01bd, + 0x01be}, + { + 180, 5900, 0xae, 0x17, 0x10, 0x0c, 0x0c, 0x0c, 0x30, 0x4e, 0x02, 0x03, + 0x00, 0x03, 0x00, 0x33, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x093c, 0x0938, 0x0934, 0x01bc, + 0x01bc, + 0x01bd}, + { + 1, 2412, 0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, 0x09, 0x0f, + 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03c9, 0x03c5, 0x03c1, 0x043a, + 0x043f, + 0x0443}, + { + 2, 2417, 0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, 0x09, 0x0f, + 0x0a, 0x00, 0x0a, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cb, 0x03c7, 0x03c3, 0x0438, + 0x043d, + 0x0441}, + { + 3, 2422, 0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, 0x09, 0x0f, + 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cd, 0x03c9, 0x03c5, 0x0436, + 0x043a, + 0x043f}, + { + 4, 2427, 0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, 0x09, 0x0f, + 0x09, 0x00, 0x09, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03cf, 0x03cb, 0x03c7, 0x0434, + 0x0438, + 0x043d}, + { + 5, 2432, 0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, 0x09, 0x0f, + 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d1, 0x03cd, 0x03c9, 0x0431, + 0x0436, + 0x043a}, + { + 6, 2437, 0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, 0x09, 0x0f, + 0x08, 0x00, 0x08, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d3, 0x03cf, 0x03cb, 0x042f, + 0x0434, + 0x0438}, + { + 7, 2442, 0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, 0x09, 0x0f, + 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d5, 0x03d1, 0x03cd, 0x042d, + 0x0431, + 0x0436}, + { + 8, 2447, 0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, 0x09, 0x0f, + 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d7, 0x03d3, 0x03cf, 0x042b, + 0x042f, + 0x0434}, + { + 9, 2452, 0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, 0x09, 0x0f, + 0x07, 0x00, 0x07, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03d9, 0x03d5, 0x03d1, 0x0429, + 0x042d, + 0x0431}, + { + 10, 2457, 0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, 0x09, 0x0f, + 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03db, 0x03d7, 0x03d3, 0x0427, + 0x042b, + 0x042f}, + { + 11, 2462, 0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, 0x09, 0x0f, + 0x06, 0x00, 0x06, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03dd, 0x03d9, 0x03d5, 0x0424, + 0x0429, + 0x042d}, + { + 12, 2467, 0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, 0x09, 0x0f, + 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03df, 0x03db, 0x03d7, 0x0422, + 0x0427, + 0x042b}, + { + 13, 2472, 0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, 0x09, 0x0f, + 0x05, 0x00, 0x05, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x03e1, 0x03dd, 0x03d9, 0x0420, + 0x0424, + 0x0429}, + { + 14, 2484, 0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, 0x09, 0x0f, + 0x04, 0x00, 0x04, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x61, + 0x73, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03e6, 0x03e2, 0x03de, 0x041b, + 0x041f, + 0x0424} +}; + +static struct radio_regs regs_2055[] = { + {0x02, 0x80, 0x80, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0x27, 0x27, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0x27, 0x27, 0, 0}, + {0x07, 0x7f, 0x7f, 1, 1}, + {0x08, 0x7, 0x7, 1, 1}, + {0x09, 0x7f, 0x7f, 1, 1}, + {0x0A, 0x7, 0x7, 1, 1}, + {0x0B, 0x15, 0x15, 0, 0}, + {0x0C, 0x15, 0x15, 0, 0}, + {0x0D, 0x4f, 0x4f, 1, 1}, + {0x0E, 0x5, 0x5, 1, 1}, + {0x0F, 0x4f, 0x4f, 1, 1}, + {0x10, 0x5, 0x5, 1, 1}, + {0x11, 0xd0, 0xd0, 0, 0}, + {0x12, 0x2, 0x2, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0x40, 0x40, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0xc0, 0xc0, 0, 0}, + {0x1E, 0xff, 0xff, 0, 0}, + {0x1F, 0xc0, 0xc0, 0, 0}, + {0x20, 0xff, 0xff, 0, 0}, + {0x21, 0xc0, 0xc0, 0, 0}, + {0x22, 0, 0, 0, 0}, + {0x23, 0x2c, 0x2c, 0, 0}, + {0x24, 0, 0, 0, 0}, + {0x25, 0, 0, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0, 0, 0, 0}, + {0x28, 0, 0, 0, 0}, + {0x29, 0, 0, 0, 0}, + {0x2A, 0, 0, 0, 0}, + {0x2B, 0, 0, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0xa4, 0xa4, 0, 0}, + {0x2E, 0x38, 0x38, 0, 0}, + {0x2F, 0, 0, 0, 0}, + {0x30, 0x4, 0x4, 1, 1}, + {0x31, 0, 0, 0, 0}, + {0x32, 0xa, 0xa, 0, 0}, + {0x33, 0x87, 0x87, 0, 0}, + {0x34, 0x9, 0x9, 0, 0}, + {0x35, 0x70, 0x70, 0, 0}, + {0x36, 0x11, 0x11, 0, 0}, + {0x37, 0x18, 0x18, 1, 1}, + {0x38, 0x6, 0x6, 0, 0}, + {0x39, 0x4, 0x4, 1, 1}, + {0x3A, 0x6, 0x6, 0, 0}, + {0x3B, 0x9e, 0x9e, 0, 0}, + {0x3C, 0x9, 0x9, 0, 0}, + {0x3D, 0xc8, 0xc8, 1, 1}, + {0x3E, 0x88, 0x88, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0, 0, 0, 0}, + {0x42, 0x1, 0x1, 0, 0}, + {0x43, 0x2, 0x2, 0, 0}, + {0x44, 0x96, 0x96, 0, 0}, + {0x45, 0x3e, 0x3e, 0, 0}, + {0x46, 0x3e, 0x3e, 0, 0}, + {0x47, 0x13, 0x13, 0, 0}, + {0x48, 0x2, 0x2, 0, 0}, + {0x49, 0x15, 0x15, 0, 0}, + {0x4A, 0x7, 0x7, 0, 0}, + {0x4B, 0, 0, 0, 0}, + {0x4C, 0, 0, 0, 0}, + {0x4D, 0, 0, 0, 0}, + {0x4E, 0, 0, 0, 0}, + {0x4F, 0, 0, 0, 0}, + {0x50, 0x8, 0x8, 0, 0}, + {0x51, 0x8, 0x8, 0, 0}, + {0x52, 0x6, 0x6, 0, 0}, + {0x53, 0x84, 0x84, 1, 1}, + {0x54, 0xc3, 0xc3, 0, 0}, + {0x55, 0x8f, 0x8f, 0, 0}, + {0x56, 0xff, 0xff, 0, 0}, + {0x57, 0xff, 0xff, 0, 0}, + {0x58, 0x88, 0x88, 0, 0}, + {0x59, 0x88, 0x88, 0, 0}, + {0x5A, 0, 0, 0, 0}, + {0x5B, 0xcc, 0xcc, 0, 0}, + {0x5C, 0x6, 0x6, 0, 0}, + {0x5D, 0x80, 0x80, 0, 0}, + {0x5E, 0x80, 0x80, 0, 0}, + {0x5F, 0xf8, 0xf8, 0, 0}, + {0x60, 0x88, 0x88, 0, 0}, + {0x61, 0x88, 0x88, 0, 0}, + {0x62, 0x88, 0x8, 1, 1}, + {0x63, 0x88, 0x88, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0x1, 0x1, 1, 1}, + {0x66, 0x8a, 0x8a, 0, 0}, + {0x67, 0x8, 0x8, 0, 0}, + {0x68, 0x83, 0x83, 0, 0}, + {0x69, 0x6, 0x6, 0, 0}, + {0x6A, 0xa0, 0xa0, 0, 0}, + {0x6B, 0xa, 0xa, 0, 0}, + {0x6C, 0x87, 0x87, 1, 1}, + {0x6D, 0x2a, 0x2a, 0, 0}, + {0x6E, 0x2a, 0x2a, 0, 0}, + {0x6F, 0x2a, 0x2a, 0, 0}, + {0x70, 0x2a, 0x2a, 0, 0}, + {0x71, 0x18, 0x18, 0, 0}, + {0x72, 0x6a, 0x6a, 1, 1}, + {0x73, 0xab, 0xab, 1, 1}, + {0x74, 0x13, 0x13, 1, 1}, + {0x75, 0xc1, 0xc1, 1, 1}, + {0x76, 0xaa, 0xaa, 1, 1}, + {0x77, 0x87, 0x87, 1, 1}, + {0x78, 0, 0, 0, 0}, + {0x79, 0x6, 0x6, 0, 0}, + {0x7A, 0x7, 0x7, 0, 0}, + {0x7B, 0x7, 0x7, 0, 0}, + {0x7C, 0x15, 0x15, 0, 0}, + {0x7D, 0x55, 0x55, 0, 0}, + {0x7E, 0x97, 0x97, 1, 1}, + {0x7F, 0x8, 0x8, 0, 0}, + {0x80, 0x14, 0x14, 1, 1}, + {0x81, 0x33, 0x33, 0, 0}, + {0x82, 0x88, 0x88, 0, 0}, + {0x83, 0x6, 0x6, 0, 0}, + {0x84, 0x3, 0x3, 1, 1}, + {0x85, 0xa, 0xa, 0, 0}, + {0x86, 0x3, 0x3, 1, 1}, + {0x87, 0x2a, 0x2a, 0, 0}, + {0x88, 0xa4, 0xa4, 0, 0}, + {0x89, 0x18, 0x18, 0, 0}, + {0x8A, 0x28, 0x28, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0x4a, 0x4a, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0xf8, 0xf8, 0, 0}, + {0x8F, 0x88, 0x88, 0, 0}, + {0x90, 0x88, 0x88, 0, 0}, + {0x91, 0x88, 0x8, 1, 1}, + {0x92, 0x88, 0x88, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0x1, 0x1, 1, 1}, + {0x95, 0x8a, 0x8a, 0, 0}, + {0x96, 0x8, 0x8, 0, 0}, + {0x97, 0x83, 0x83, 0, 0}, + {0x98, 0x6, 0x6, 0, 0}, + {0x99, 0xa0, 0xa0, 0, 0}, + {0x9A, 0xa, 0xa, 0, 0}, + {0x9B, 0x87, 0x87, 1, 1}, + {0x9C, 0x2a, 0x2a, 0, 0}, + {0x9D, 0x2a, 0x2a, 0, 0}, + {0x9E, 0x2a, 0x2a, 0, 0}, + {0x9F, 0x2a, 0x2a, 0, 0}, + {0xA0, 0x18, 0x18, 0, 0}, + {0xA1, 0x6a, 0x6a, 1, 1}, + {0xA2, 0xab, 0xab, 1, 1}, + {0xA3, 0x13, 0x13, 1, 1}, + {0xA4, 0xc1, 0xc1, 1, 1}, + {0xA5, 0xaa, 0xaa, 1, 1}, + {0xA6, 0x87, 0x87, 1, 1}, + {0xA7, 0, 0, 0, 0}, + {0xA8, 0x6, 0x6, 0, 0}, + {0xA9, 0x7, 0x7, 0, 0}, + {0xAA, 0x7, 0x7, 0, 0}, + {0xAB, 0x15, 0x15, 0, 0}, + {0xAC, 0x55, 0x55, 0, 0}, + {0xAD, 0x97, 0x97, 1, 1}, + {0xAE, 0x8, 0x8, 0, 0}, + {0xAF, 0x14, 0x14, 1, 1}, + {0xB0, 0x33, 0x33, 0, 0}, + {0xB1, 0x88, 0x88, 0, 0}, + {0xB2, 0x6, 0x6, 0, 0}, + {0xB3, 0x3, 0x3, 1, 1}, + {0xB4, 0xa, 0xa, 0, 0}, + {0xB5, 0x3, 0x3, 1, 1}, + {0xB6, 0x2a, 0x2a, 0, 0}, + {0xB7, 0xa4, 0xa4, 0, 0}, + {0xB8, 0x18, 0x18, 0, 0}, + {0xB9, 0x28, 0x28, 0, 0}, + {0xBA, 0, 0, 0, 0}, + {0xBB, 0x4a, 0x4a, 0, 0}, + {0xBC, 0, 0, 0, 0}, + {0xBD, 0x71, 0x71, 0, 0}, + {0xBE, 0x72, 0x72, 0, 0}, + {0xBF, 0x73, 0x73, 0, 0}, + {0xC0, 0x74, 0x74, 0, 0}, + {0xC1, 0x75, 0x75, 0, 0}, + {0xC2, 0x76, 0x76, 0, 0}, + {0xC3, 0x77, 0x77, 0, 0}, + {0xC4, 0x78, 0x78, 0, 0}, + {0xC5, 0x79, 0x79, 0, 0}, + {0xC6, 0x7a, 0x7a, 0, 0}, + {0xC7, 0, 0, 0, 0}, + {0xC8, 0, 0, 0, 0}, + {0xC9, 0, 0, 0, 0}, + {0xCA, 0, 0, 0, 0}, + {0xCB, 0, 0, 0, 0}, + {0xCC, 0, 0, 0, 0}, + {0xCD, 0, 0, 0, 0}, + {0xCE, 0x6, 0x6, 0, 0}, + {0xCF, 0, 0, 0, 0}, + {0xD0, 0, 0, 0, 0}, + {0xD1, 0x18, 0x18, 0, 0}, + {0xD2, 0x88, 0x88, 0, 0}, + {0xD3, 0, 0, 0, 0}, + {0xD4, 0, 0, 0, 0}, + {0xD5, 0, 0, 0, 0}, + {0xD6, 0, 0, 0, 0}, + {0xD7, 0, 0, 0, 0}, + {0xD8, 0, 0, 0, 0}, + {0xD9, 0, 0, 0, 0}, + {0xDA, 0x6, 0x6, 0, 0}, + {0xDB, 0, 0, 0, 0}, + {0xDC, 0, 0, 0, 0}, + {0xDD, 0x18, 0x18, 0, 0}, + {0xDE, 0x88, 0x88, 0, 0}, + {0xDF, 0, 0, 0, 0}, + {0xE0, 0, 0, 0, 0}, + {0xE1, 0, 0, 0, 0}, + {0xE2, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0}, +}; + +static struct radio_regs regs_SYN_2056[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0x1, 0x1, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0x60, 0x60, 0, 0}, + {0x23, 0x6, 0x6, 0, 0}, + {0x24, 0xc, 0xc, 0, 0}, + {0x25, 0, 0, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0, 0, 0, 0}, + {0x28, 0x1, 0x1, 0, 0}, + {0x29, 0, 0, 0, 0}, + {0x2A, 0, 0, 0, 0}, + {0x2B, 0, 0, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0xd, 0xd, 0, 0}, + {0x2F, 0x1f, 0x1f, 0, 0}, + {0x30, 0x15, 0x15, 0, 0}, + {0x31, 0xf, 0xf, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0, 0, 0, 0}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0, 0, 0, 0}, + {0x38, 0, 0, 0, 0}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0, 0, 0, 0}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x13, 0x13, 0, 0}, + {0x3D, 0xf, 0xf, 0, 0}, + {0x3E, 0x18, 0x18, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x20, 0x20, 0, 0}, + {0x42, 0x20, 0x20, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x77, 0x77, 0, 0}, + {0x45, 0x7, 0x7, 0, 0}, + {0x46, 0x1, 0x1, 0, 0}, + {0x47, 0x4, 0x4, 0, 0}, + {0x48, 0xf, 0xf, 0, 0}, + {0x49, 0x30, 0x30, 0, 0}, + {0x4A, 0x32, 0x32, 0, 0}, + {0x4B, 0xd, 0xd, 0, 0}, + {0x4C, 0xd, 0xd, 0, 0}, + {0x4D, 0x4, 0x4, 0, 0}, + {0x4E, 0x6, 0x6, 0, 0}, + {0x4F, 0x1, 0x1, 0, 0}, + {0x50, 0x1c, 0x1c, 0, 0}, + {0x51, 0x2, 0x2, 0, 0}, + {0x52, 0x2, 0x2, 0, 0}, + {0x53, 0xf7, 0xf7, 1, 1}, + {0x54, 0xb4, 0xb4, 0, 0}, + {0x55, 0xd2, 0xd2, 0, 0}, + {0x56, 0, 0, 0, 0}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x4, 0x4, 0, 0}, + {0x59, 0x96, 0x96, 0, 0}, + {0x5A, 0x3e, 0x3e, 0, 0}, + {0x5B, 0x3e, 0x3e, 0, 0}, + {0x5C, 0x13, 0x13, 0, 0}, + {0x5D, 0x2, 0x2, 0, 0}, + {0x5E, 0, 0, 0, 0}, + {0x5F, 0x7, 0x7, 0, 0}, + {0x60, 0x7, 0x7, 1, 1}, + {0x61, 0x8, 0x8, 0, 0}, + {0x62, 0x3, 0x3, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0x40, 0x40, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0x1, 0x1, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0x60, 0x60, 0, 0}, + {0x71, 0x66, 0x66, 0, 0}, + {0x72, 0xc, 0xc, 0, 0}, + {0x73, 0x66, 0x66, 0, 0}, + {0x74, 0x8f, 0x8f, 1, 1}, + {0x75, 0, 0, 0, 0}, + {0x76, 0xcc, 0xcc, 0, 0}, + {0x77, 0x1, 0x1, 0, 0}, + {0x78, 0x66, 0x66, 0, 0}, + {0x79, 0x66, 0x66, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0xff, 0xff, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0x95, 0, 0, 0, 0}, + {0x96, 0, 0, 0, 0}, + {0x97, 0, 0, 0, 0}, + {0x98, 0, 0, 0, 0}, + {0x99, 0, 0, 0, 0}, + {0x9A, 0, 0, 0, 0}, + {0x9B, 0, 0, 0, 0}, + {0x9C, 0, 0, 0, 0}, + {0x9D, 0, 0, 0, 0}, + {0x9E, 0, 0, 0, 0}, + {0x9F, 0x6, 0x6, 0, 0}, + {0xA0, 0x66, 0x66, 0, 0}, + {0xA1, 0x66, 0x66, 0, 0}, + {0xA2, 0x66, 0x66, 0, 0}, + {0xA3, 0x66, 0x66, 0, 0}, + {0xA4, 0x66, 0x66, 0, 0}, + {0xA5, 0x66, 0x66, 0, 0}, + {0xA6, 0x66, 0x66, 0, 0}, + {0xA7, 0x66, 0x66, 0, 0}, + {0xA8, 0x66, 0x66, 0, 0}, + {0xA9, 0x66, 0x66, 0, 0}, + {0xAA, 0x66, 0x66, 0, 0}, + {0xAB, 0x66, 0x66, 0, 0}, + {0xAC, 0x66, 0x66, 0, 0}, + {0xAD, 0x66, 0x66, 0, 0}, + {0xAE, 0x66, 0x66, 0, 0}, + {0xAF, 0x66, 0x66, 0, 0}, + {0xB0, 0x66, 0x66, 0, 0}, + {0xB1, 0x66, 0x66, 0, 0}, + {0xB2, 0x66, 0x66, 0, 0}, + {0xB3, 0xa, 0xa, 0, 0}, + {0xB4, 0, 0, 0, 0}, + {0xB5, 0, 0, 0, 0}, + {0xB6, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_TX_2056[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0x88, 0x88, 0, 0}, + {0x22, 0x88, 0x88, 0, 0}, + {0x23, 0x88, 0x88, 0, 0}, + {0x24, 0x88, 0x88, 0, 0}, + {0x25, 0xc, 0xc, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0x3, 0x3, 0, 0}, + {0x28, 0, 0, 0, 0}, + {0x29, 0x3, 0x3, 0, 0}, + {0x2A, 0x37, 0x37, 0, 0}, + {0x2B, 0x3, 0x3, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0x1, 0x1, 0, 0}, + {0x2F, 0x1, 0x1, 0, 0}, + {0x30, 0, 0, 0, 0}, + {0x31, 0, 0, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0x11, 0x11, 0, 0}, + {0x34, 0x11, 0x11, 0, 0}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0x3, 0x3, 0, 0}, + {0x38, 0xf, 0xf, 0, 0}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0x2d, 0x2d, 0, 0}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x6e, 0x6e, 0, 0}, + {0x3D, 0xf0, 0xf0, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x3, 0x3, 0, 0}, + {0x42, 0x3, 0x3, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x1e, 0x1e, 0, 0}, + {0x45, 0, 0, 0, 0}, + {0x46, 0x6e, 0x6e, 0, 0}, + {0x47, 0xf0, 0xf0, 1, 1}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x2, 0x2, 0, 0}, + {0x4A, 0xff, 0xff, 1, 1}, + {0x4B, 0xc, 0xc, 0, 0}, + {0x4C, 0, 0, 0, 0}, + {0x4D, 0x38, 0x38, 0, 0}, + {0x4E, 0x70, 0x70, 1, 1}, + {0x4F, 0x2, 0x2, 0, 0}, + {0x50, 0x88, 0x88, 0, 0}, + {0x51, 0xc, 0xc, 0, 0}, + {0x52, 0, 0, 0, 0}, + {0x53, 0x8, 0x8, 0, 0}, + {0x54, 0x70, 0x70, 1, 1}, + {0x55, 0x2, 0x2, 0, 0}, + {0x56, 0xff, 0xff, 1, 1}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x83, 0x83, 0, 0}, + {0x59, 0x77, 0x77, 1, 1}, + {0x5A, 0, 0, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x88, 0x88, 0, 0}, + {0x5D, 0, 0, 0, 0}, + {0x5E, 0x8, 0x8, 0, 0}, + {0x5F, 0x77, 0x77, 1, 1}, + {0x60, 0x1, 0x1, 0, 0}, + {0x61, 0, 0, 0, 0}, + {0x62, 0x7, 0x7, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0x7, 0x7, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0x74, 0x74, 1, 1}, + {0x68, 0, 0, 0, 0}, + {0x69, 0xa, 0xa, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0x2, 0x2, 0, 0}, + {0x72, 0, 0, 0, 0}, + {0x73, 0, 0, 0, 0}, + {0x74, 0xe, 0xe, 0, 0}, + {0x75, 0xe, 0xe, 0, 0}, + {0x76, 0xe, 0xe, 0, 0}, + {0x77, 0x13, 0x13, 0, 0}, + {0x78, 0x13, 0x13, 0, 0}, + {0x79, 0x1b, 0x1b, 0, 0}, + {0x7A, 0x1b, 0x1b, 0, 0}, + {0x7B, 0x55, 0x55, 0, 0}, + {0x7C, 0x5b, 0x5b, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_RX_2056[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0x3, 0x3, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0, 0, 0, 0}, + {0x23, 0x90, 0x90, 0, 0}, + {0x24, 0x55, 0x55, 0, 0}, + {0x25, 0x15, 0x15, 0, 0}, + {0x26, 0x5, 0x5, 0, 0}, + {0x27, 0x15, 0x15, 0, 0}, + {0x28, 0x5, 0x5, 0, 0}, + {0x29, 0x20, 0x20, 0, 0}, + {0x2A, 0x11, 0x11, 0, 0}, + {0x2B, 0x90, 0x90, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0x88, 0x88, 0, 0}, + {0x2E, 0x32, 0x32, 0, 0}, + {0x2F, 0x77, 0x77, 0, 0}, + {0x30, 0x17, 0x17, 1, 1}, + {0x31, 0xff, 0xff, 1, 1}, + {0x32, 0x20, 0x20, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0x88, 0x88, 0, 0}, + {0x35, 0x32, 0x32, 0, 0}, + {0x36, 0x77, 0x77, 0, 0}, + {0x37, 0x17, 0x17, 1, 1}, + {0x38, 0xf0, 0xf0, 1, 1}, + {0x39, 0x20, 0x20, 0, 0}, + {0x3A, 0x8, 0x8, 0, 0}, + {0x3B, 0x99, 0x99, 0, 0}, + {0x3C, 0, 0, 0, 0}, + {0x3D, 0x44, 0x44, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0x44, 0x44, 0, 0}, + {0x40, 0xf, 0xf, 1, 1}, + {0x41, 0x6, 0x6, 0, 0}, + {0x42, 0x4, 0x4, 0, 0}, + {0x43, 0x50, 0x50, 1, 1}, + {0x44, 0x8, 0x8, 0, 0}, + {0x45, 0x99, 0x99, 0, 0}, + {0x46, 0, 0, 0, 0}, + {0x47, 0x11, 0x11, 0, 0}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x44, 0x44, 0, 0}, + {0x4A, 0x7, 0x7, 0, 0}, + {0x4B, 0x6, 0x6, 0, 0}, + {0x4C, 0x4, 0x4, 0, 0}, + {0x4D, 0, 0, 0, 0}, + {0x4E, 0, 0, 0, 0}, + {0x4F, 0x66, 0x66, 0, 0}, + {0x50, 0x66, 0x66, 0, 0}, + {0x51, 0x57, 0x57, 0, 0}, + {0x52, 0x57, 0x57, 0, 0}, + {0x53, 0x44, 0x44, 0, 0}, + {0x54, 0, 0, 0, 0}, + {0x55, 0, 0, 0, 0}, + {0x56, 0x8, 0x8, 0, 0}, + {0x57, 0x8, 0x8, 0, 0}, + {0x58, 0x7, 0x7, 0, 0}, + {0x59, 0x22, 0x22, 0, 0}, + {0x5A, 0x22, 0x22, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x23, 0x23, 0, 0}, + {0x5D, 0x7, 0x7, 0, 0}, + {0x5E, 0x55, 0x55, 0, 0}, + {0x5F, 0x23, 0x23, 0, 0}, + {0x60, 0x41, 0x41, 0, 0}, + {0x61, 0x1, 0x1, 0, 0}, + {0x62, 0xa, 0xa, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0, 0, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0xc, 0xc, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0, 0, 0, 0}, + {0x72, 0x22, 0x22, 0, 0}, + {0x73, 0x22, 0x22, 0, 0}, + {0x74, 0x2, 0x2, 0, 0}, + {0x75, 0xa, 0xa, 0, 0}, + {0x76, 0x1, 0x1, 0, 0}, + {0x77, 0x22, 0x22, 0, 0}, + {0x78, 0x30, 0x30, 0, 0}, + {0x79, 0, 0, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_SYN_2056_A1[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0x1, 0x1, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0x60, 0x60, 0, 0}, + {0x23, 0x6, 0x6, 0, 0}, + {0x24, 0xc, 0xc, 0, 0}, + {0x25, 0, 0, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0, 0, 0, 0}, + {0x28, 0x1, 0x1, 0, 0}, + {0x29, 0, 0, 0, 0}, + {0x2A, 0, 0, 0, 0}, + {0x2B, 0, 0, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0xd, 0xd, 0, 0}, + {0x2F, 0x1f, 0x1f, 0, 0}, + {0x30, 0x15, 0x15, 0, 0}, + {0x31, 0xf, 0xf, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0, 0, 0, 0}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0, 0, 0, 0}, + {0x38, 0, 0, 0, 0}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0, 0, 0, 0}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x13, 0x13, 0, 0}, + {0x3D, 0xf, 0xf, 0, 0}, + {0x3E, 0x18, 0x18, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x20, 0x20, 0, 0}, + {0x42, 0x20, 0x20, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x77, 0x77, 0, 0}, + {0x45, 0x7, 0x7, 0, 0}, + {0x46, 0x1, 0x1, 0, 0}, + {0x47, 0x4, 0x4, 0, 0}, + {0x48, 0xf, 0xf, 0, 0}, + {0x49, 0x30, 0x30, 0, 0}, + {0x4A, 0x32, 0x32, 0, 0}, + {0x4B, 0xd, 0xd, 0, 0}, + {0x4C, 0xd, 0xd, 0, 0}, + {0x4D, 0x4, 0x4, 0, 0}, + {0x4E, 0x6, 0x6, 0, 0}, + {0x4F, 0x1, 0x1, 0, 0}, + {0x50, 0x1c, 0x1c, 0, 0}, + {0x51, 0x2, 0x2, 0, 0}, + {0x52, 0x2, 0x2, 0, 0}, + {0x53, 0xf7, 0xf7, 1, 1}, + {0x54, 0xb4, 0xb4, 0, 0}, + {0x55, 0xd2, 0xd2, 0, 0}, + {0x56, 0, 0, 0, 0}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x4, 0x4, 0, 0}, + {0x59, 0x96, 0x96, 0, 0}, + {0x5A, 0x3e, 0x3e, 0, 0}, + {0x5B, 0x3e, 0x3e, 0, 0}, + {0x5C, 0x13, 0x13, 0, 0}, + {0x5D, 0x2, 0x2, 0, 0}, + {0x5E, 0, 0, 0, 0}, + {0x5F, 0x7, 0x7, 0, 0}, + {0x60, 0x7, 0x7, 1, 1}, + {0x61, 0x8, 0x8, 0, 0}, + {0x62, 0x3, 0x3, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0x40, 0x40, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0x1, 0x1, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0x60, 0x60, 0, 0}, + {0x71, 0x66, 0x66, 0, 0}, + {0x72, 0xc, 0xc, 0, 0}, + {0x73, 0x66, 0x66, 0, 0}, + {0x74, 0x8f, 0x8f, 1, 1}, + {0x75, 0, 0, 0, 0}, + {0x76, 0xcc, 0xcc, 0, 0}, + {0x77, 0x1, 0x1, 0, 0}, + {0x78, 0x66, 0x66, 0, 0}, + {0x79, 0x66, 0x66, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0xff, 0xff, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0x95, 0, 0, 0, 0}, + {0x96, 0, 0, 0, 0}, + {0x97, 0, 0, 0, 0}, + {0x98, 0, 0, 0, 0}, + {0x99, 0, 0, 0, 0}, + {0x9A, 0, 0, 0, 0}, + {0x9B, 0, 0, 0, 0}, + {0x9C, 0, 0, 0, 0}, + {0x9D, 0, 0, 0, 0}, + {0x9E, 0, 0, 0, 0}, + {0x9F, 0x6, 0x6, 0, 0}, + {0xA0, 0x66, 0x66, 0, 0}, + {0xA1, 0x66, 0x66, 0, 0}, + {0xA2, 0x66, 0x66, 0, 0}, + {0xA3, 0x66, 0x66, 0, 0}, + {0xA4, 0x66, 0x66, 0, 0}, + {0xA5, 0x66, 0x66, 0, 0}, + {0xA6, 0x66, 0x66, 0, 0}, + {0xA7, 0x66, 0x66, 0, 0}, + {0xA8, 0x66, 0x66, 0, 0}, + {0xA9, 0x66, 0x66, 0, 0}, + {0xAA, 0x66, 0x66, 0, 0}, + {0xAB, 0x66, 0x66, 0, 0}, + {0xAC, 0x66, 0x66, 0, 0}, + {0xAD, 0x66, 0x66, 0, 0}, + {0xAE, 0x66, 0x66, 0, 0}, + {0xAF, 0x66, 0x66, 0, 0}, + {0xB0, 0x66, 0x66, 0, 0}, + {0xB1, 0x66, 0x66, 0, 0}, + {0xB2, 0x66, 0x66, 0, 0}, + {0xB3, 0xa, 0xa, 0, 0}, + {0xB4, 0, 0, 0, 0}, + {0xB5, 0, 0, 0, 0}, + {0xB6, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_TX_2056_A1[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0x88, 0x88, 0, 0}, + {0x22, 0x88, 0x88, 0, 0}, + {0x23, 0x88, 0x88, 0, 0}, + {0x24, 0x88, 0x88, 0, 0}, + {0x25, 0xc, 0xc, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0x3, 0x3, 0, 0}, + {0x28, 0, 0, 0, 0}, + {0x29, 0x3, 0x3, 0, 0}, + {0x2A, 0x37, 0x37, 0, 0}, + {0x2B, 0x3, 0x3, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0x1, 0x1, 0, 0}, + {0x2F, 0x1, 0x1, 0, 0}, + {0x30, 0, 0, 0, 0}, + {0x31, 0, 0, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0x11, 0x11, 0, 0}, + {0x34, 0x11, 0x11, 0, 0}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0x3, 0x3, 0, 0}, + {0x38, 0xf, 0xf, 0, 0}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0x2d, 0x2d, 0, 0}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x6e, 0x6e, 0, 0}, + {0x3D, 0xf0, 0xf0, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x3, 0x3, 0, 0}, + {0x42, 0x3, 0x3, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x1e, 0x1e, 0, 0}, + {0x45, 0, 0, 0, 0}, + {0x46, 0x6e, 0x6e, 0, 0}, + {0x47, 0xf0, 0xf0, 1, 1}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x2, 0x2, 0, 0}, + {0x4A, 0xff, 0xff, 1, 1}, + {0x4B, 0xc, 0xc, 0, 0}, + {0x4C, 0, 0, 0, 0}, + {0x4D, 0x38, 0x38, 0, 0}, + {0x4E, 0x70, 0x70, 1, 1}, + {0x4F, 0x2, 0x2, 0, 0}, + {0x50, 0x88, 0x88, 0, 0}, + {0x51, 0xc, 0xc, 0, 0}, + {0x52, 0, 0, 0, 0}, + {0x53, 0x8, 0x8, 0, 0}, + {0x54, 0x70, 0x70, 1, 1}, + {0x55, 0x2, 0x2, 0, 0}, + {0x56, 0xff, 0xff, 1, 1}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x83, 0x83, 0, 0}, + {0x59, 0x77, 0x77, 1, 1}, + {0x5A, 0, 0, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x88, 0x88, 0, 0}, + {0x5D, 0, 0, 0, 0}, + {0x5E, 0x8, 0x8, 0, 0}, + {0x5F, 0x77, 0x77, 1, 1}, + {0x60, 0x1, 0x1, 0, 0}, + {0x61, 0, 0, 0, 0}, + {0x62, 0x7, 0x7, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0x7, 0x7, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0x72, 0x72, 1, 1}, + {0x68, 0, 0, 0, 0}, + {0x69, 0xa, 0xa, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0x2, 0x2, 0, 0}, + {0x72, 0, 0, 0, 0}, + {0x73, 0, 0, 0, 0}, + {0x74, 0xe, 0xe, 0, 0}, + {0x75, 0xe, 0xe, 0, 0}, + {0x76, 0xe, 0xe, 0, 0}, + {0x77, 0x13, 0x13, 0, 0}, + {0x78, 0x13, 0x13, 0, 0}, + {0x79, 0x1b, 0x1b, 0, 0}, + {0x7A, 0x1b, 0x1b, 0, 0}, + {0x7B, 0x55, 0x55, 0, 0}, + {0x7C, 0x5b, 0x5b, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_RX_2056_A1[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0x3, 0x3, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0, 0, 0, 0}, + {0x23, 0x90, 0x90, 0, 0}, + {0x24, 0x55, 0x55, 0, 0}, + {0x25, 0x15, 0x15, 0, 0}, + {0x26, 0x5, 0x5, 0, 0}, + {0x27, 0x15, 0x15, 0, 0}, + {0x28, 0x5, 0x5, 0, 0}, + {0x29, 0x20, 0x20, 0, 0}, + {0x2A, 0x11, 0x11, 0, 0}, + {0x2B, 0x90, 0x90, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0x88, 0x88, 0, 0}, + {0x2E, 0x32, 0x32, 0, 0}, + {0x2F, 0x77, 0x77, 0, 0}, + {0x30, 0x17, 0x17, 1, 1}, + {0x31, 0xff, 0xff, 1, 1}, + {0x32, 0x20, 0x20, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0x88, 0x88, 0, 0}, + {0x35, 0x32, 0x32, 0, 0}, + {0x36, 0x77, 0x77, 0, 0}, + {0x37, 0x17, 0x17, 1, 1}, + {0x38, 0xf0, 0xf0, 1, 1}, + {0x39, 0x20, 0x20, 0, 0}, + {0x3A, 0x8, 0x8, 0, 0}, + {0x3B, 0x55, 0x55, 1, 1}, + {0x3C, 0, 0, 0, 0}, + {0x3D, 0x44, 0x44, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0x44, 0x44, 0, 0}, + {0x40, 0xf, 0xf, 1, 1}, + {0x41, 0x6, 0x6, 0, 0}, + {0x42, 0x4, 0x4, 0, 0}, + {0x43, 0x50, 0x50, 1, 1}, + {0x44, 0x8, 0x8, 0, 0}, + {0x45, 0x55, 0x55, 1, 1}, + {0x46, 0, 0, 0, 0}, + {0x47, 0x11, 0x11, 0, 0}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x44, 0x44, 0, 0}, + {0x4A, 0x7, 0x7, 0, 0}, + {0x4B, 0x6, 0x6, 0, 0}, + {0x4C, 0x4, 0x4, 0, 0}, + {0x4D, 0, 0, 0, 0}, + {0x4E, 0, 0, 0, 0}, + {0x4F, 0x26, 0x26, 1, 1}, + {0x50, 0x26, 0x26, 1, 1}, + {0x51, 0xf, 0xf, 1, 1}, + {0x52, 0xf, 0xf, 1, 1}, + {0x53, 0x44, 0x44, 0, 0}, + {0x54, 0, 0, 0, 0}, + {0x55, 0, 0, 0, 0}, + {0x56, 0x8, 0x8, 0, 0}, + {0x57, 0x8, 0x8, 0, 0}, + {0x58, 0x7, 0x7, 0, 0}, + {0x59, 0x22, 0x22, 0, 0}, + {0x5A, 0x22, 0x22, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x2f, 0x2f, 1, 1}, + {0x5D, 0x7, 0x7, 0, 0}, + {0x5E, 0x55, 0x55, 0, 0}, + {0x5F, 0x23, 0x23, 0, 0}, + {0x60, 0x41, 0x41, 0, 0}, + {0x61, 0x1, 0x1, 0, 0}, + {0x62, 0xa, 0xa, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0, 0, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0xc, 0xc, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0, 0, 0, 0}, + {0x72, 0x22, 0x22, 0, 0}, + {0x73, 0x22, 0x22, 0, 0}, + {0x74, 0, 0, 1, 1}, + {0x75, 0xa, 0xa, 0, 0}, + {0x76, 0x1, 0x1, 0, 0}, + {0x77, 0x22, 0x22, 0, 0}, + {0x78, 0x30, 0x30, 0, 0}, + {0x79, 0, 0, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_SYN_2056_rev5[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0x1, 0x1, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0x60, 0x60, 0, 0}, + {0x23, 0x6, 0x6, 0, 0}, + {0x24, 0xc, 0xc, 0, 0}, + {0x25, 0, 0, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0, 0, 0, 0}, + {0x28, 0x1, 0x1, 0, 0}, + {0x29, 0, 0, 0, 0}, + {0x2A, 0, 0, 0, 0}, + {0x2B, 0, 0, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0, 0, 0, 0}, + {0x2F, 0x1f, 0x1f, 0, 0}, + {0x30, 0x15, 0x15, 0, 0}, + {0x31, 0xf, 0xf, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0, 0, 0, 0}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0, 0, 0, 0}, + {0x38, 0, 0, 0, 0}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0, 0, 0, 0}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x13, 0x13, 0, 0}, + {0x3D, 0xf, 0xf, 0, 0}, + {0x3E, 0x18, 0x18, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x20, 0x20, 0, 0}, + {0x42, 0x20, 0x20, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x77, 0x77, 0, 0}, + {0x45, 0x7, 0x7, 0, 0}, + {0x46, 0x1, 0x1, 0, 0}, + {0x47, 0x4, 0x4, 0, 0}, + {0x48, 0xf, 0xf, 0, 0}, + {0x49, 0x30, 0x30, 0, 0}, + {0x4A, 0x32, 0x32, 0, 0}, + {0x4B, 0xd, 0xd, 0, 0}, + {0x4C, 0xd, 0xd, 0, 0}, + {0x4D, 0x4, 0x4, 0, 0}, + {0x4E, 0x6, 0x6, 0, 0}, + {0x4F, 0x1, 0x1, 0, 0}, + {0x50, 0x1c, 0x1c, 0, 0}, + {0x51, 0x2, 0x2, 0, 0}, + {0x52, 0x2, 0x2, 0, 0}, + {0x53, 0xf7, 0xf7, 1, 1}, + {0x54, 0xb4, 0xb4, 0, 0}, + {0x55, 0xd2, 0xd2, 0, 0}, + {0x56, 0, 0, 0, 0}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x4, 0x4, 0, 0}, + {0x59, 0x96, 0x96, 0, 0}, + {0x5A, 0x3e, 0x3e, 0, 0}, + {0x5B, 0x3e, 0x3e, 0, 0}, + {0x5C, 0x13, 0x13, 0, 0}, + {0x5D, 0x2, 0x2, 0, 0}, + {0x5E, 0, 0, 0, 0}, + {0x5F, 0x7, 0x7, 0, 0}, + {0x60, 0x7, 0x7, 1, 1}, + {0x61, 0x8, 0x8, 0, 0}, + {0x62, 0x3, 0x3, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0x40, 0x40, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0x1, 0x1, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0x60, 0x60, 0, 0}, + {0x71, 0x66, 0x66, 0, 0}, + {0x72, 0xc, 0xc, 0, 0}, + {0x73, 0x66, 0x66, 0, 0}, + {0x74, 0x8f, 0x8f, 1, 1}, + {0x75, 0, 0, 0, 0}, + {0x76, 0xcc, 0xcc, 0, 0}, + {0x77, 0x1, 0x1, 0, 0}, + {0x78, 0x66, 0x66, 0, 0}, + {0x79, 0x66, 0x66, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0xff, 0xff, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0x95, 0, 0, 0, 0}, + {0x96, 0, 0, 0, 0}, + {0x97, 0, 0, 0, 0}, + {0x98, 0, 0, 0, 0}, + {0x99, 0, 0, 0, 0}, + {0x9A, 0, 0, 0, 0}, + {0x9B, 0, 0, 0, 0}, + {0x9C, 0, 0, 0, 0}, + {0x9D, 0, 0, 0, 0}, + {0x9E, 0, 0, 0, 0}, + {0x9F, 0x6, 0x6, 0, 0}, + {0xA0, 0x66, 0x66, 0, 0}, + {0xA1, 0x66, 0x66, 0, 0}, + {0xA2, 0x66, 0x66, 0, 0}, + {0xA3, 0x66, 0x66, 0, 0}, + {0xA4, 0x66, 0x66, 0, 0}, + {0xA5, 0x66, 0x66, 0, 0}, + {0xA6, 0x66, 0x66, 0, 0}, + {0xA7, 0x66, 0x66, 0, 0}, + {0xA8, 0x66, 0x66, 0, 0}, + {0xA9, 0x66, 0x66, 0, 0}, + {0xAA, 0x66, 0x66, 0, 0}, + {0xAB, 0x66, 0x66, 0, 0}, + {0xAC, 0x66, 0x66, 0, 0}, + {0xAD, 0x66, 0x66, 0, 0}, + {0xAE, 0x66, 0x66, 0, 0}, + {0xAF, 0x66, 0x66, 0, 0}, + {0xB0, 0x66, 0x66, 0, 0}, + {0xB1, 0x66, 0x66, 0, 0}, + {0xB2, 0x66, 0x66, 0, 0}, + {0xB3, 0xa, 0xa, 0, 0}, + {0xB4, 0, 0, 0, 0}, + {0xB5, 0, 0, 0, 0}, + {0xB6, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_TX_2056_rev5[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0x88, 0x88, 0, 0}, + {0x22, 0x88, 0x88, 0, 0}, + {0x23, 0x88, 0x88, 0, 0}, + {0x24, 0x88, 0x88, 0, 0}, + {0x25, 0xc, 0xc, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0x3, 0x3, 0, 0}, + {0x28, 0, 0, 0, 0}, + {0x29, 0x3, 0x3, 0, 0}, + {0x2A, 0x37, 0x37, 0, 0}, + {0x2B, 0x3, 0x3, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0x1, 0x1, 0, 0}, + {0x2F, 0x1, 0x1, 0, 0}, + {0x30, 0, 0, 0, 0}, + {0x31, 0, 0, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0x11, 0x11, 0, 0}, + {0x34, 0x11, 0x11, 0, 0}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0x3, 0x3, 0, 0}, + {0x38, 0xf, 0xf, 0, 0}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0x2d, 0x2d, 0, 0}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x6e, 0x6e, 0, 0}, + {0x3D, 0xf0, 0xf0, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x3, 0x3, 0, 0}, + {0x42, 0x3, 0x3, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x1e, 0x1e, 0, 0}, + {0x45, 0, 0, 0, 0}, + {0x46, 0x6e, 0x6e, 0, 0}, + {0x47, 0xf0, 0xf0, 1, 1}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x2, 0x2, 0, 0}, + {0x4A, 0xff, 0xff, 1, 1}, + {0x4B, 0xc, 0xc, 0, 0}, + {0x4C, 0, 0, 0, 0}, + {0x4D, 0x38, 0x38, 0, 0}, + {0x4E, 0x70, 0x70, 1, 1}, + {0x4F, 0x2, 0x2, 0, 0}, + {0x50, 0x88, 0x88, 0, 0}, + {0x51, 0xc, 0xc, 0, 0}, + {0x52, 0, 0, 0, 0}, + {0x53, 0x8, 0x8, 0, 0}, + {0x54, 0x70, 0x70, 1, 1}, + {0x55, 0x2, 0x2, 0, 0}, + {0x56, 0xff, 0xff, 1, 1}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x83, 0x83, 0, 0}, + {0x59, 0x77, 0x77, 1, 1}, + {0x5A, 0, 0, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x88, 0x88, 0, 0}, + {0x5D, 0, 0, 0, 0}, + {0x5E, 0x8, 0x8, 0, 0}, + {0x5F, 0x77, 0x77, 1, 1}, + {0x60, 0x1, 0x1, 0, 0}, + {0x61, 0, 0, 0, 0}, + {0x62, 0x7, 0x7, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0x7, 0x7, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 1, 1}, + {0x68, 0, 0, 0, 0}, + {0x69, 0xa, 0xa, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0x2, 0x2, 0, 0}, + {0x72, 0, 0, 0, 0}, + {0x73, 0, 0, 0, 0}, + {0x74, 0xe, 0xe, 0, 0}, + {0x75, 0xe, 0xe, 0, 0}, + {0x76, 0xe, 0xe, 0, 0}, + {0x77, 0x13, 0x13, 0, 0}, + {0x78, 0x13, 0x13, 0, 0}, + {0x79, 0x1b, 0x1b, 0, 0}, + {0x7A, 0x1b, 0x1b, 0, 0}, + {0x7B, 0x55, 0x55, 0, 0}, + {0x7C, 0x5b, 0x5b, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0x70, 0x70, 0, 0}, + {0x94, 0x70, 0x70, 0, 0}, + {0x95, 0x71, 0x71, 1, 1}, + {0x96, 0x71, 0x71, 1, 1}, + {0x97, 0x72, 0x72, 1, 1}, + {0x98, 0x73, 0x73, 1, 1}, + {0x99, 0x74, 0x74, 1, 1}, + {0x9A, 0x75, 0x75, 1, 1}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_RX_2056_rev5[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0x3, 0x3, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0, 0, 0, 0}, + {0x23, 0x90, 0x90, 0, 0}, + {0x24, 0x55, 0x55, 0, 0}, + {0x25, 0x15, 0x15, 0, 0}, + {0x26, 0x5, 0x5, 0, 0}, + {0x27, 0x15, 0x15, 0, 0}, + {0x28, 0x5, 0x5, 0, 0}, + {0x29, 0x20, 0x20, 0, 0}, + {0x2A, 0x11, 0x11, 0, 0}, + {0x2B, 0x90, 0x90, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0x88, 0x88, 0, 0}, + {0x2E, 0x32, 0x32, 0, 0}, + {0x2F, 0x77, 0x77, 0, 0}, + {0x30, 0x17, 0x17, 1, 1}, + {0x31, 0xff, 0xff, 1, 1}, + {0x32, 0x20, 0x20, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0x88, 0x88, 0, 0}, + {0x35, 0x32, 0x32, 0, 0}, + {0x36, 0x77, 0x77, 0, 0}, + {0x37, 0x17, 0x17, 1, 1}, + {0x38, 0xf0, 0xf0, 1, 1}, + {0x39, 0x20, 0x20, 0, 0}, + {0x3A, 0x8, 0x8, 0, 0}, + {0x3B, 0x55, 0x55, 1, 1}, + {0x3C, 0, 0, 0, 0}, + {0x3D, 0x88, 0x88, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0, 0, 1, 1}, + {0x40, 0x7, 0x7, 1, 1}, + {0x41, 0x6, 0x6, 0, 0}, + {0x42, 0x4, 0x4, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x8, 0x8, 0, 0}, + {0x45, 0x55, 0x55, 1, 1}, + {0x46, 0, 0, 0, 0}, + {0x47, 0x11, 0x11, 0, 0}, + {0x48, 0, 0, 0, 0}, + {0x49, 0, 0, 1, 1}, + {0x4A, 0x7, 0x7, 0, 0}, + {0x4B, 0x6, 0x6, 0, 0}, + {0x4C, 0x4, 0x4, 0, 0}, + {0x4D, 0, 0, 0, 0}, + {0x4E, 0, 0, 0, 0}, + {0x4F, 0x26, 0x26, 1, 1}, + {0x50, 0x26, 0x26, 1, 1}, + {0x51, 0xf, 0xf, 1, 1}, + {0x52, 0xf, 0xf, 1, 1}, + {0x53, 0x44, 0x44, 0, 0}, + {0x54, 0, 0, 0, 0}, + {0x55, 0, 0, 0, 0}, + {0x56, 0x8, 0x8, 0, 0}, + {0x57, 0x8, 0x8, 0, 0}, + {0x58, 0x7, 0x7, 0, 0}, + {0x59, 0x22, 0x22, 0, 0}, + {0x5A, 0x22, 0x22, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x4, 0x4, 1, 1}, + {0x5D, 0x7, 0x7, 0, 0}, + {0x5E, 0x55, 0x55, 0, 0}, + {0x5F, 0x23, 0x23, 0, 0}, + {0x60, 0x41, 0x41, 0, 0}, + {0x61, 0x1, 0x1, 0, 0}, + {0x62, 0xa, 0xa, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0, 0, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0xc, 0xc, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0, 0, 0, 0}, + {0x72, 0x22, 0x22, 0, 0}, + {0x73, 0x22, 0x22, 0, 0}, + {0x74, 0, 0, 1, 1}, + {0x75, 0xa, 0xa, 0, 0}, + {0x76, 0x1, 0x1, 0, 0}, + {0x77, 0x22, 0x22, 0, 0}, + {0x78, 0x30, 0x30, 0, 0}, + {0x79, 0, 0, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_SYN_2056_rev6[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0x1, 0x1, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0x60, 0x60, 0, 0}, + {0x23, 0x6, 0x6, 0, 0}, + {0x24, 0xc, 0xc, 0, 0}, + {0x25, 0, 0, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0, 0, 0, 0}, + {0x28, 0x1, 0x1, 0, 0}, + {0x29, 0, 0, 0, 0}, + {0x2A, 0, 0, 0, 0}, + {0x2B, 0, 0, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0, 0, 0, 0}, + {0x2F, 0x1f, 0x1f, 0, 0}, + {0x30, 0x15, 0x15, 0, 0}, + {0x31, 0xf, 0xf, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0, 0, 0, 0}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0, 0, 0, 0}, + {0x38, 0, 0, 0, 0}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0, 0, 0, 0}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x13, 0x13, 0, 0}, + {0x3D, 0xf, 0xf, 0, 0}, + {0x3E, 0x18, 0x18, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x20, 0x20, 0, 0}, + {0x42, 0x20, 0x20, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x77, 0x77, 0, 0}, + {0x45, 0x7, 0x7, 0, 0}, + {0x46, 0x1, 0x1, 0, 0}, + {0x47, 0x4, 0x4, 0, 0}, + {0x48, 0xf, 0xf, 0, 0}, + {0x49, 0x30, 0x30, 0, 0}, + {0x4A, 0x32, 0x32, 0, 0}, + {0x4B, 0xd, 0xd, 0, 0}, + {0x4C, 0xd, 0xd, 0, 0}, + {0x4D, 0x4, 0x4, 0, 0}, + {0x4E, 0x6, 0x6, 0, 0}, + {0x4F, 0x1, 0x1, 0, 0}, + {0x50, 0x1c, 0x1c, 0, 0}, + {0x51, 0x2, 0x2, 0, 0}, + {0x52, 0x2, 0x2, 0, 0}, + {0x53, 0xf7, 0xf7, 1, 1}, + {0x54, 0xb4, 0xb4, 0, 0}, + {0x55, 0xd2, 0xd2, 0, 0}, + {0x56, 0, 0, 0, 0}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x4, 0x4, 0, 0}, + {0x59, 0x96, 0x96, 0, 0}, + {0x5A, 0x3e, 0x3e, 0, 0}, + {0x5B, 0x3e, 0x3e, 0, 0}, + {0x5C, 0x13, 0x13, 0, 0}, + {0x5D, 0x2, 0x2, 0, 0}, + {0x5E, 0, 0, 0, 0}, + {0x5F, 0x7, 0x7, 0, 0}, + {0x60, 0x7, 0x7, 1, 1}, + {0x61, 0x8, 0x8, 0, 0}, + {0x62, 0x3, 0x3, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0x40, 0x40, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0x1, 0x1, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0x60, 0x60, 0, 0}, + {0x71, 0x66, 0x66, 0, 0}, + {0x72, 0xc, 0xc, 0, 0}, + {0x73, 0x66, 0x66, 0, 0}, + {0x74, 0x8f, 0x8f, 1, 1}, + {0x75, 0, 0, 0, 0}, + {0x76, 0xcc, 0xcc, 0, 0}, + {0x77, 0x1, 0x1, 0, 0}, + {0x78, 0x66, 0x66, 0, 0}, + {0x79, 0x66, 0x66, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0xff, 0xff, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0x95, 0, 0, 0, 0}, + {0x96, 0, 0, 0, 0}, + {0x97, 0, 0, 0, 0}, + {0x98, 0, 0, 0, 0}, + {0x99, 0, 0, 0, 0}, + {0x9A, 0, 0, 0, 0}, + {0x9B, 0, 0, 0, 0}, + {0x9C, 0, 0, 0, 0}, + {0x9D, 0, 0, 0, 0}, + {0x9E, 0, 0, 0, 0}, + {0x9F, 0x6, 0x6, 0, 0}, + {0xA0, 0x66, 0x66, 0, 0}, + {0xA1, 0x66, 0x66, 0, 0}, + {0xA2, 0x66, 0x66, 0, 0}, + {0xA3, 0x66, 0x66, 0, 0}, + {0xA4, 0x66, 0x66, 0, 0}, + {0xA5, 0x66, 0x66, 0, 0}, + {0xA6, 0x66, 0x66, 0, 0}, + {0xA7, 0x66, 0x66, 0, 0}, + {0xA8, 0x66, 0x66, 0, 0}, + {0xA9, 0x66, 0x66, 0, 0}, + {0xAA, 0x66, 0x66, 0, 0}, + {0xAB, 0x66, 0x66, 0, 0}, + {0xAC, 0x66, 0x66, 0, 0}, + {0xAD, 0x66, 0x66, 0, 0}, + {0xAE, 0x66, 0x66, 0, 0}, + {0xAF, 0x66, 0x66, 0, 0}, + {0xB0, 0x66, 0x66, 0, 0}, + {0xB1, 0x66, 0x66, 0, 0}, + {0xB2, 0x66, 0x66, 0, 0}, + {0xB3, 0xa, 0xa, 0, 0}, + {0xB4, 0, 0, 0, 0}, + {0xB5, 0, 0, 0, 0}, + {0xB6, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_TX_2056_rev6[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0x88, 0x88, 0, 0}, + {0x22, 0x88, 0x88, 0, 0}, + {0x23, 0x88, 0x88, 0, 0}, + {0x24, 0x88, 0x88, 0, 0}, + {0x25, 0xc, 0xc, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0x3, 0x3, 0, 0}, + {0x28, 0, 0, 0, 0}, + {0x29, 0x3, 0x3, 0, 0}, + {0x2A, 0x37, 0x37, 0, 0}, + {0x2B, 0x3, 0x3, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0x1, 0x1, 0, 0}, + {0x2F, 0x1, 0x1, 0, 0}, + {0x30, 0, 0, 0, 0}, + {0x31, 0, 0, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0x11, 0x11, 0, 0}, + {0x34, 0xee, 0xee, 1, 1}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0x3, 0x3, 0, 0}, + {0x38, 0x50, 0x50, 1, 1}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0x50, 0x50, 1, 1}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x6e, 0x6e, 0, 0}, + {0x3D, 0xf0, 0xf0, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x3, 0x3, 0, 0}, + {0x42, 0x3, 0x3, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x1e, 0x1e, 0, 0}, + {0x45, 0, 0, 0, 0}, + {0x46, 0x6e, 0x6e, 0, 0}, + {0x47, 0xf0, 0xf0, 1, 1}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x2, 0x2, 0, 0}, + {0x4A, 0xff, 0xff, 1, 1}, + {0x4B, 0xc, 0xc, 0, 0}, + {0x4C, 0, 0, 0, 0}, + {0x4D, 0x38, 0x38, 0, 0}, + {0x4E, 0x70, 0x70, 1, 1}, + {0x4F, 0x2, 0x2, 0, 0}, + {0x50, 0x88, 0x88, 0, 0}, + {0x51, 0xc, 0xc, 0, 0}, + {0x52, 0, 0, 0, 0}, + {0x53, 0x8, 0x8, 0, 0}, + {0x54, 0x70, 0x70, 1, 1}, + {0x55, 0x2, 0x2, 0, 0}, + {0x56, 0xff, 0xff, 1, 1}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x83, 0x83, 0, 0}, + {0x59, 0x77, 0x77, 1, 1}, + {0x5A, 0, 0, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x88, 0x88, 0, 0}, + {0x5D, 0, 0, 0, 0}, + {0x5E, 0x8, 0x8, 0, 0}, + {0x5F, 0x77, 0x77, 1, 1}, + {0x60, 0x1, 0x1, 0, 0}, + {0x61, 0, 0, 0, 0}, + {0x62, 0x7, 0x7, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0x7, 0x7, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 1, 1}, + {0x68, 0, 0, 0, 0}, + {0x69, 0xa, 0xa, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0x2, 0x2, 0, 0}, + {0x72, 0, 0, 0, 0}, + {0x73, 0, 0, 0, 0}, + {0x74, 0xe, 0xe, 0, 0}, + {0x75, 0xe, 0xe, 0, 0}, + {0x76, 0xe, 0xe, 0, 0}, + {0x77, 0x13, 0x13, 0, 0}, + {0x78, 0x13, 0x13, 0, 0}, + {0x79, 0x1b, 0x1b, 0, 0}, + {0x7A, 0x1b, 0x1b, 0, 0}, + {0x7B, 0x55, 0x55, 0, 0}, + {0x7C, 0x5b, 0x5b, 0, 0}, + {0x7D, 0x30, 0x30, 1, 1}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0x70, 0x70, 0, 0}, + {0x94, 0x70, 0x70, 0, 0}, + {0x95, 0x70, 0x70, 0, 0}, + {0x96, 0x70, 0x70, 0, 0}, + {0x97, 0x70, 0x70, 0, 0}, + {0x98, 0x70, 0x70, 0, 0}, + {0x99, 0x70, 0x70, 0, 0}, + {0x9A, 0x70, 0x70, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_RX_2056_rev6[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0x3, 0x3, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0, 0, 0, 0}, + {0x23, 0x90, 0x90, 0, 0}, + {0x24, 0x55, 0x55, 0, 0}, + {0x25, 0x15, 0x15, 0, 0}, + {0x26, 0x5, 0x5, 0, 0}, + {0x27, 0x15, 0x15, 0, 0}, + {0x28, 0x5, 0x5, 0, 0}, + {0x29, 0x20, 0x20, 0, 0}, + {0x2A, 0x11, 0x11, 0, 0}, + {0x2B, 0x90, 0x90, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0x88, 0x88, 0, 0}, + {0x2E, 0x32, 0x32, 0, 0}, + {0x2F, 0x77, 0x77, 0, 0}, + {0x30, 0x17, 0x17, 1, 1}, + {0x31, 0xff, 0xff, 1, 1}, + {0x32, 0x20, 0x20, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0x88, 0x88, 0, 0}, + {0x35, 0x32, 0x32, 0, 0}, + {0x36, 0x77, 0x77, 0, 0}, + {0x37, 0x17, 0x17, 1, 1}, + {0x38, 0xf0, 0xf0, 1, 1}, + {0x39, 0x20, 0x20, 0, 0}, + {0x3A, 0x8, 0x8, 0, 0}, + {0x3B, 0x55, 0x55, 1, 1}, + {0x3C, 0, 0, 0, 0}, + {0x3D, 0x88, 0x88, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0x44, 0x44, 0, 0}, + {0x40, 0x7, 0x7, 1, 1}, + {0x41, 0x6, 0x6, 0, 0}, + {0x42, 0x4, 0x4, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x8, 0x8, 0, 0}, + {0x45, 0x55, 0x55, 1, 1}, + {0x46, 0, 0, 0, 0}, + {0x47, 0x11, 0x11, 0, 0}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x44, 0x44, 0, 0}, + {0x4A, 0x7, 0x7, 0, 0}, + {0x4B, 0x6, 0x6, 0, 0}, + {0x4C, 0x4, 0x4, 0, 0}, + {0x4D, 0, 0, 0, 0}, + {0x4E, 0, 0, 0, 0}, + {0x4F, 0x26, 0x26, 1, 1}, + {0x50, 0x26, 0x26, 1, 1}, + {0x51, 0xf, 0xf, 1, 1}, + {0x52, 0xf, 0xf, 1, 1}, + {0x53, 0x44, 0x44, 0, 0}, + {0x54, 0, 0, 0, 0}, + {0x55, 0, 0, 0, 0}, + {0x56, 0x8, 0x8, 0, 0}, + {0x57, 0x8, 0x8, 0, 0}, + {0x58, 0x7, 0x7, 0, 0}, + {0x59, 0x22, 0x22, 0, 0}, + {0x5A, 0x22, 0x22, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x4, 0x4, 1, 1}, + {0x5D, 0x7, 0x7, 0, 0}, + {0x5E, 0x55, 0x55, 0, 0}, + {0x5F, 0x23, 0x23, 0, 0}, + {0x60, 0x41, 0x41, 0, 0}, + {0x61, 0x1, 0x1, 0, 0}, + {0x62, 0xa, 0xa, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0, 0, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0xc, 0xc, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0, 0, 0, 0}, + {0x72, 0x22, 0x22, 0, 0}, + {0x73, 0x22, 0x22, 0, 0}, + {0x74, 0, 0, 1, 1}, + {0x75, 0xa, 0xa, 0, 0}, + {0x76, 0x1, 0x1, 0, 0}, + {0x77, 0x22, 0x22, 0, 0}, + {0x78, 0x30, 0x30, 0, 0}, + {0x79, 0, 0, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0x5, 0x5, 1, 1}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0} +}; + +static struct radio_regs regs_SYN_2056_rev7[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0x1, 0x1, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0x60, 0x60, 0, 0}, + {0x23, 0x6, 0x6, 0, 0}, + {0x24, 0xc, 0xc, 0, 0}, + {0x25, 0, 0, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0, 0, 0, 0}, + {0x28, 0x1, 0x1, 0, 0}, + {0x29, 0, 0, 0, 0}, + {0x2A, 0, 0, 0, 0}, + {0x2B, 0, 0, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0, 0, 0, 0}, + {0x2F, 0x1f, 0x1f, 0, 0}, + {0x30, 0x15, 0x15, 0, 0}, + {0x31, 0xf, 0xf, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0, 0, 0, 0}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0, 0, 0, 0}, + {0x38, 0, 0, 0, 0}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0, 0, 0, 0}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x13, 0x13, 0, 0}, + {0x3D, 0xf, 0xf, 0, 0}, + {0x3E, 0x18, 0x18, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x20, 0x20, 0, 0}, + {0x42, 0x20, 0x20, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x77, 0x77, 0, 0}, + {0x45, 0x7, 0x7, 0, 0}, + {0x46, 0x1, 0x1, 0, 0}, + {0x47, 0x4, 0x4, 0, 0}, + {0x48, 0xf, 0xf, 0, 0}, + {0x49, 0x30, 0x30, 0, 0}, + {0x4A, 0x32, 0x32, 0, 0}, + {0x4B, 0xd, 0xd, 0, 0}, + {0x4C, 0xd, 0xd, 0, 0}, + {0x4D, 0x4, 0x4, 0, 0}, + {0x4E, 0x6, 0x6, 0, 0}, + {0x4F, 0x1, 0x1, 0, 0}, + {0x50, 0x1c, 0x1c, 0, 0}, + {0x51, 0x2, 0x2, 0, 0}, + {0x52, 0x2, 0x2, 0, 0}, + {0x53, 0xf7, 0xf7, 1, 1}, + {0x54, 0xb4, 0xb4, 0, 0}, + {0x55, 0xd2, 0xd2, 0, 0}, + {0x56, 0, 0, 0, 0}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x4, 0x4, 0, 0}, + {0x59, 0x96, 0x96, 0, 0}, + {0x5A, 0x3e, 0x3e, 0, 0}, + {0x5B, 0x3e, 0x3e, 0, 0}, + {0x5C, 0x13, 0x13, 0, 0}, + {0x5D, 0x2, 0x2, 0, 0}, + {0x5E, 0, 0, 0, 0}, + {0x5F, 0x7, 0x7, 0, 0}, + {0x60, 0x7, 0x7, 1, 1}, + {0x61, 0x8, 0x8, 0, 0}, + {0x62, 0x3, 0x3, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0x40, 0x40, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0x1, 0x1, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0x60, 0x60, 0, 0}, + {0x71, 0x66, 0x66, 0, 0}, + {0x72, 0xc, 0xc, 0, 0}, + {0x73, 0x66, 0x66, 0, 0}, + {0x74, 0x8f, 0x8f, 1, 1}, + {0x75, 0, 0, 0, 0}, + {0x76, 0xcc, 0xcc, 0, 0}, + {0x77, 0x1, 0x1, 0, 0}, + {0x78, 0x66, 0x66, 0, 0}, + {0x79, 0x66, 0x66, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0xff, 0xff, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0x95, 0, 0, 0, 0}, + {0x96, 0, 0, 0, 0}, + {0x97, 0, 0, 0, 0}, + {0x98, 0, 0, 0, 0}, + {0x99, 0, 0, 0, 0}, + {0x9A, 0, 0, 0, 0}, + {0x9B, 0, 0, 0, 0}, + {0x9C, 0, 0, 0, 0}, + {0x9D, 0, 0, 0, 0}, + {0x9E, 0, 0, 0, 0}, + {0x9F, 0x6, 0x6, 0, 0}, + {0xA0, 0x66, 0x66, 0, 0}, + {0xA1, 0x66, 0x66, 0, 0}, + {0xA2, 0x66, 0x66, 0, 0}, + {0xA3, 0x66, 0x66, 0, 0}, + {0xA4, 0x66, 0x66, 0, 0}, + {0xA5, 0x66, 0x66, 0, 0}, + {0xA6, 0x66, 0x66, 0, 0}, + {0xA7, 0x66, 0x66, 0, 0}, + {0xA8, 0x66, 0x66, 0, 0}, + {0xA9, 0x66, 0x66, 0, 0}, + {0xAA, 0x66, 0x66, 0, 0}, + {0xAB, 0x66, 0x66, 0, 0}, + {0xAC, 0x66, 0x66, 0, 0}, + {0xAD, 0x66, 0x66, 0, 0}, + {0xAE, 0x66, 0x66, 0, 0}, + {0xAF, 0x66, 0x66, 0, 0}, + {0xB0, 0x66, 0x66, 0, 0}, + {0xB1, 0x66, 0x66, 0, 0}, + {0xB2, 0x66, 0x66, 0, 0}, + {0xB3, 0xa, 0xa, 0, 0}, + {0xB4, 0, 0, 0, 0}, + {0xB5, 0, 0, 0, 0}, + {0xB6, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0}, +}; + +static struct radio_regs regs_TX_2056_rev7[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0x88, 0x88, 0, 0}, + {0x22, 0x88, 0x88, 0, 0}, + {0x23, 0x88, 0x88, 0, 0}, + {0x24, 0x88, 0x88, 0, 0}, + {0x25, 0xc, 0xc, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0x3, 0x3, 0, 0}, + {0x28, 0, 0, 0, 0}, + {0x29, 0x3, 0x3, 0, 0}, + {0x2A, 0x37, 0x37, 0, 0}, + {0x2B, 0x3, 0x3, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0x1, 0x1, 0, 0}, + {0x2F, 0x1, 0x1, 0, 0}, + {0x30, 0, 0, 0, 0}, + {0x31, 0, 0, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0x11, 0x11, 0, 0}, + {0x34, 0xee, 0xee, 1, 1}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0x3, 0x3, 0, 0}, + {0x38, 0x50, 0x50, 1, 1}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0x50, 0x50, 1, 1}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x6e, 0x6e, 0, 0}, + {0x3D, 0xf0, 0xf0, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x3, 0x3, 0, 0}, + {0x42, 0x3, 0x3, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x1e, 0x1e, 0, 0}, + {0x45, 0, 0, 0, 0}, + {0x46, 0x6e, 0x6e, 0, 0}, + {0x47, 0xf0, 0xf0, 1, 1}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x2, 0x2, 0, 0}, + {0x4A, 0xff, 0xff, 1, 1}, + {0x4B, 0xc, 0xc, 0, 0}, + {0x4C, 0, 0, 0, 0}, + {0x4D, 0x38, 0x38, 0, 0}, + {0x4E, 0x70, 0x70, 1, 1}, + {0x4F, 0x2, 0x2, 0, 0}, + {0x50, 0x88, 0x88, 0, 0}, + {0x51, 0xc, 0xc, 0, 0}, + {0x52, 0, 0, 0, 0}, + {0x53, 0x8, 0x8, 0, 0}, + {0x54, 0x70, 0x70, 1, 1}, + {0x55, 0x2, 0x2, 0, 0}, + {0x56, 0xff, 0xff, 1, 1}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x83, 0x83, 0, 0}, + {0x59, 0x77, 0x77, 1, 1}, + {0x5A, 0, 0, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x88, 0x88, 0, 0}, + {0x5D, 0, 0, 0, 0}, + {0x5E, 0x8, 0x8, 0, 0}, + {0x5F, 0x77, 0x77, 1, 1}, + {0x60, 0x1, 0x1, 0, 0}, + {0x61, 0, 0, 0, 0}, + {0x62, 0x7, 0x7, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0x7, 0x7, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 1, 1}, + {0x68, 0, 0, 0, 0}, + {0x69, 0xa, 0xa, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0x2, 0x2, 0, 0}, + {0x72, 0, 0, 0, 0}, + {0x73, 0, 0, 0, 0}, + {0x74, 0xe, 0xe, 0, 0}, + {0x75, 0xe, 0xe, 0, 0}, + {0x76, 0xe, 0xe, 0, 0}, + {0x77, 0x13, 0x13, 0, 0}, + {0x78, 0x13, 0x13, 0, 0}, + {0x79, 0x1b, 0x1b, 0, 0}, + {0x7A, 0x1b, 0x1b, 0, 0}, + {0x7B, 0x55, 0x55, 0, 0}, + {0x7C, 0x5b, 0x5b, 0, 0}, + {0x7D, 0x30, 0x30, 1, 1}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0x70, 0x70, 0, 0}, + {0x94, 0x70, 0x70, 0, 0}, + {0x95, 0x71, 0x71, 1, 1}, + {0x96, 0x71, 0x71, 1, 1}, + {0x97, 0x72, 0x72, 1, 1}, + {0x98, 0x73, 0x73, 1, 1}, + {0x99, 0x74, 0x74, 1, 1}, + {0x9A, 0x75, 0x75, 1, 1}, + {0xFFFF, 0, 0, 0, 0}, +}; + +static struct radio_regs regs_RX_2056_rev7[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0x3, 0x3, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0, 0, 0, 0}, + {0x23, 0x90, 0x90, 0, 0}, + {0x24, 0x55, 0x55, 0, 0}, + {0x25, 0x15, 0x15, 0, 0}, + {0x26, 0x5, 0x5, 0, 0}, + {0x27, 0x15, 0x15, 0, 0}, + {0x28, 0x5, 0x5, 0, 0}, + {0x29, 0x20, 0x20, 0, 0}, + {0x2A, 0x11, 0x11, 0, 0}, + {0x2B, 0x90, 0x90, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0x88, 0x88, 0, 0}, + {0x2E, 0x32, 0x32, 0, 0}, + {0x2F, 0x77, 0x77, 0, 0}, + {0x30, 0x17, 0x17, 1, 1}, + {0x31, 0xff, 0xff, 1, 1}, + {0x32, 0x20, 0x20, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0x88, 0x88, 0, 0}, + {0x35, 0x32, 0x32, 0, 0}, + {0x36, 0x77, 0x77, 0, 0}, + {0x37, 0x17, 0x17, 1, 1}, + {0x38, 0xf0, 0xf0, 1, 1}, + {0x39, 0x20, 0x20, 0, 0}, + {0x3A, 0x8, 0x8, 0, 0}, + {0x3B, 0x55, 0x55, 1, 1}, + {0x3C, 0, 0, 0, 0}, + {0x3D, 0x88, 0x88, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0, 0, 1, 1}, + {0x40, 0x7, 0x7, 1, 1}, + {0x41, 0x6, 0x6, 0, 0}, + {0x42, 0x4, 0x4, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x8, 0x8, 0, 0}, + {0x45, 0x55, 0x55, 1, 1}, + {0x46, 0, 0, 0, 0}, + {0x47, 0x11, 0x11, 0, 0}, + {0x48, 0, 0, 0, 0}, + {0x49, 0, 0, 1, 1}, + {0x4A, 0x7, 0x7, 0, 0}, + {0x4B, 0x6, 0x6, 0, 0}, + {0x4C, 0x4, 0x4, 0, 0}, + {0x4D, 0, 0, 0, 0}, + {0x4E, 0, 0, 0, 0}, + {0x4F, 0x26, 0x26, 1, 1}, + {0x50, 0x26, 0x26, 1, 1}, + {0x51, 0xf, 0xf, 1, 1}, + {0x52, 0xf, 0xf, 1, 1}, + {0x53, 0x44, 0x44, 0, 0}, + {0x54, 0, 0, 0, 0}, + {0x55, 0, 0, 0, 0}, + {0x56, 0x8, 0x8, 0, 0}, + {0x57, 0x8, 0x8, 0, 0}, + {0x58, 0x7, 0x7, 0, 0}, + {0x59, 0x22, 0x22, 0, 0}, + {0x5A, 0x22, 0x22, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x4, 0x4, 1, 1}, + {0x5D, 0x7, 0x7, 0, 0}, + {0x5E, 0x55, 0x55, 0, 0}, + {0x5F, 0x23, 0x23, 0, 0}, + {0x60, 0x41, 0x41, 0, 0}, + {0x61, 0x1, 0x1, 0, 0}, + {0x62, 0xa, 0xa, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0, 0, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0xc, 0xc, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0, 0, 0, 0}, + {0x72, 0x22, 0x22, 0, 0}, + {0x73, 0x22, 0x22, 0, 0}, + {0x74, 0, 0, 1, 1}, + {0x75, 0xa, 0xa, 0, 0}, + {0x76, 0x1, 0x1, 0, 0}, + {0x77, 0x22, 0x22, 0, 0}, + {0x78, 0x30, 0x30, 0, 0}, + {0x79, 0, 0, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0}, +}; + +static struct radio_regs regs_SYN_2056_rev8[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0x1, 0x1, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0x60, 0x60, 0, 0}, + {0x23, 0x6, 0x6, 0, 0}, + {0x24, 0xc, 0xc, 0, 0}, + {0x25, 0, 0, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0, 0, 0, 0}, + {0x28, 0x1, 0x1, 0, 0}, + {0x29, 0, 0, 0, 0}, + {0x2A, 0, 0, 0, 0}, + {0x2B, 0, 0, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0, 0, 0, 0}, + {0x2F, 0x1f, 0x1f, 0, 0}, + {0x30, 0x15, 0x15, 0, 0}, + {0x31, 0xf, 0xf, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0, 0, 0, 0}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0, 0, 0, 0}, + {0x38, 0, 0, 0, 0}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0, 0, 0, 0}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x13, 0x13, 0, 0}, + {0x3D, 0xf, 0xf, 0, 0}, + {0x3E, 0x18, 0x18, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x20, 0x20, 0, 0}, + {0x42, 0x20, 0x20, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x77, 0x77, 0, 0}, + {0x45, 0x7, 0x7, 0, 0}, + {0x46, 0x1, 0x1, 0, 0}, + {0x47, 0x4, 0x4, 0, 0}, + {0x48, 0xf, 0xf, 0, 0}, + {0x49, 0x30, 0x30, 0, 0}, + {0x4A, 0x32, 0x32, 0, 0}, + {0x4B, 0xd, 0xd, 0, 0}, + {0x4C, 0xd, 0xd, 0, 0}, + {0x4D, 0x4, 0x4, 0, 0}, + {0x4E, 0x6, 0x6, 0, 0}, + {0x4F, 0x1, 0x1, 0, 0}, + {0x50, 0x1c, 0x1c, 0, 0}, + {0x51, 0x2, 0x2, 0, 0}, + {0x52, 0x2, 0x2, 0, 0}, + {0x53, 0xf7, 0xf7, 1, 1}, + {0x54, 0xb4, 0xb4, 0, 0}, + {0x55, 0xd2, 0xd2, 0, 0}, + {0x56, 0, 0, 0, 0}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x4, 0x4, 0, 0}, + {0x59, 0x96, 0x96, 0, 0}, + {0x5A, 0x3e, 0x3e, 0, 0}, + {0x5B, 0x3e, 0x3e, 0, 0}, + {0x5C, 0x13, 0x13, 0, 0}, + {0x5D, 0x2, 0x2, 0, 0}, + {0x5E, 0, 0, 0, 0}, + {0x5F, 0x7, 0x7, 0, 0}, + {0x60, 0x7, 0x7, 1, 1}, + {0x61, 0x8, 0x8, 0, 0}, + {0x62, 0x3, 0x3, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0x40, 0x40, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0x1, 0x1, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0x60, 0x60, 0, 0}, + {0x71, 0x66, 0x66, 0, 0}, + {0x72, 0xc, 0xc, 0, 0}, + {0x73, 0x66, 0x66, 0, 0}, + {0x74, 0x8f, 0x8f, 1, 1}, + {0x75, 0, 0, 0, 0}, + {0x76, 0xcc, 0xcc, 0, 0}, + {0x77, 0x1, 0x1, 0, 0}, + {0x78, 0x66, 0x66, 0, 0}, + {0x79, 0x66, 0x66, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0xff, 0xff, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0x95, 0, 0, 0, 0}, + {0x96, 0, 0, 0, 0}, + {0x97, 0, 0, 0, 0}, + {0x98, 0, 0, 0, 0}, + {0x99, 0, 0, 0, 0}, + {0x9A, 0, 0, 0, 0}, + {0x9B, 0, 0, 0, 0}, + {0x9C, 0, 0, 0, 0}, + {0x9D, 0, 0, 0, 0}, + {0x9E, 0, 0, 0, 0}, + {0x9F, 0x6, 0x6, 0, 0}, + {0xA0, 0x66, 0x66, 0, 0}, + {0xA1, 0x66, 0x66, 0, 0}, + {0xA2, 0x66, 0x66, 0, 0}, + {0xA3, 0x66, 0x66, 0, 0}, + {0xA4, 0x66, 0x66, 0, 0}, + {0xA5, 0x66, 0x66, 0, 0}, + {0xA6, 0x66, 0x66, 0, 0}, + {0xA7, 0x66, 0x66, 0, 0}, + {0xA8, 0x66, 0x66, 0, 0}, + {0xA9, 0x66, 0x66, 0, 0}, + {0xAA, 0x66, 0x66, 0, 0}, + {0xAB, 0x66, 0x66, 0, 0}, + {0xAC, 0x66, 0x66, 0, 0}, + {0xAD, 0x66, 0x66, 0, 0}, + {0xAE, 0x66, 0x66, 0, 0}, + {0xAF, 0x66, 0x66, 0, 0}, + {0xB0, 0x66, 0x66, 0, 0}, + {0xB1, 0x66, 0x66, 0, 0}, + {0xB2, 0x66, 0x66, 0, 0}, + {0xB3, 0xa, 0xa, 0, 0}, + {0xB4, 0, 0, 0, 0}, + {0xB5, 0, 0, 0, 0}, + {0xB6, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0}, +}; + +static struct radio_regs regs_TX_2056_rev8[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0x88, 0x88, 0, 0}, + {0x22, 0x88, 0x88, 0, 0}, + {0x23, 0x88, 0x88, 0, 0}, + {0x24, 0x88, 0x88, 0, 0}, + {0x25, 0xc, 0xc, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0x3, 0x3, 0, 0}, + {0x28, 0, 0, 0, 0}, + {0x29, 0x3, 0x3, 0, 0}, + {0x2A, 0x37, 0x37, 0, 0}, + {0x2B, 0x3, 0x3, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0x1, 0x1, 0, 0}, + {0x2F, 0x1, 0x1, 0, 0}, + {0x30, 0, 0, 0, 0}, + {0x31, 0, 0, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0x11, 0x11, 0, 0}, + {0x34, 0xee, 0xee, 1, 1}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0x3, 0x3, 0, 0}, + {0x38, 0x50, 0x50, 1, 1}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0x50, 0x50, 1, 1}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x6e, 0x6e, 0, 0}, + {0x3D, 0xf0, 0xf0, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x3, 0x3, 0, 0}, + {0x42, 0x3, 0x3, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x1e, 0x1e, 0, 0}, + {0x45, 0, 0, 0, 0}, + {0x46, 0x6e, 0x6e, 0, 0}, + {0x47, 0xf0, 0xf0, 1, 1}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x2, 0x2, 0, 0}, + {0x4A, 0xff, 0xff, 1, 1}, + {0x4B, 0xc, 0xc, 0, 0}, + {0x4C, 0, 0, 0, 0}, + {0x4D, 0x38, 0x38, 0, 0}, + {0x4E, 0x70, 0x70, 1, 1}, + {0x4F, 0x2, 0x2, 0, 0}, + {0x50, 0x88, 0x88, 0, 0}, + {0x51, 0xc, 0xc, 0, 0}, + {0x52, 0, 0, 0, 0}, + {0x53, 0x8, 0x8, 0, 0}, + {0x54, 0x70, 0x70, 1, 1}, + {0x55, 0x2, 0x2, 0, 0}, + {0x56, 0xff, 0xff, 1, 1}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x83, 0x83, 0, 0}, + {0x59, 0x77, 0x77, 1, 1}, + {0x5A, 0, 0, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x88, 0x88, 0, 0}, + {0x5D, 0, 0, 0, 0}, + {0x5E, 0x8, 0x8, 0, 0}, + {0x5F, 0x77, 0x77, 1, 1}, + {0x60, 0x1, 0x1, 0, 0}, + {0x61, 0, 0, 0, 0}, + {0x62, 0x7, 0x7, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0x7, 0x7, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 1, 1}, + {0x68, 0, 0, 0, 0}, + {0x69, 0xa, 0xa, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0x2, 0x2, 0, 0}, + {0x72, 0, 0, 0, 0}, + {0x73, 0, 0, 0, 0}, + {0x74, 0xe, 0xe, 0, 0}, + {0x75, 0xe, 0xe, 0, 0}, + {0x76, 0xe, 0xe, 0, 0}, + {0x77, 0x13, 0x13, 0, 0}, + {0x78, 0x13, 0x13, 0, 0}, + {0x79, 0x1b, 0x1b, 0, 0}, + {0x7A, 0x1b, 0x1b, 0, 0}, + {0x7B, 0x55, 0x55, 0, 0}, + {0x7C, 0x5b, 0x5b, 0, 0}, + {0x7D, 0x30, 0x30, 1, 1}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0x70, 0x70, 0, 0}, + {0x94, 0x70, 0x70, 0, 0}, + {0x95, 0x70, 0x70, 0, 0}, + {0x96, 0x70, 0x70, 0, 0}, + {0x97, 0x70, 0x70, 0, 0}, + {0x98, 0x70, 0x70, 0, 0}, + {0x99, 0x70, 0x70, 0, 0}, + {0x9A, 0x70, 0x70, 0, 0}, + {0xFFFF, 0, 0, 0, 0}, +}; + +static struct radio_regs regs_RX_2056_rev8[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0x3, 0x3, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0, 0, 0, 0}, + {0x23, 0x90, 0x90, 0, 0}, + {0x24, 0x55, 0x55, 0, 0}, + {0x25, 0x15, 0x15, 0, 0}, + {0x26, 0x5, 0x5, 0, 0}, + {0x27, 0x15, 0x15, 0, 0}, + {0x28, 0x5, 0x5, 0, 0}, + {0x29, 0x20, 0x20, 0, 0}, + {0x2A, 0x11, 0x11, 0, 0}, + {0x2B, 0x90, 0x90, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0x88, 0x88, 0, 0}, + {0x2E, 0x32, 0x32, 0, 0}, + {0x2F, 0x77, 0x77, 0, 0}, + {0x30, 0x17, 0x17, 1, 1}, + {0x31, 0xff, 0xff, 1, 1}, + {0x32, 0x20, 0x20, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0x88, 0x88, 0, 0}, + {0x35, 0x32, 0x32, 0, 0}, + {0x36, 0x77, 0x77, 0, 0}, + {0x37, 0x17, 0x17, 1, 1}, + {0x38, 0xf0, 0xf0, 1, 1}, + {0x39, 0x20, 0x20, 0, 0}, + {0x3A, 0x8, 0x8, 0, 0}, + {0x3B, 0x55, 0x55, 1, 1}, + {0x3C, 0, 0, 0, 0}, + {0x3D, 0x88, 0x88, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0x44, 0x44, 0, 0}, + {0x40, 0x7, 0x7, 1, 1}, + {0x41, 0x6, 0x6, 0, 0}, + {0x42, 0x4, 0x4, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x8, 0x8, 0, 0}, + {0x45, 0x55, 0x55, 1, 1}, + {0x46, 0, 0, 0, 0}, + {0x47, 0x11, 0x11, 0, 0}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x44, 0x44, 0, 0}, + {0x4A, 0x7, 0x7, 0, 0}, + {0x4B, 0x6, 0x6, 0, 0}, + {0x4C, 0x4, 0x4, 0, 0}, + {0x4D, 0, 0, 0, 0}, + {0x4E, 0, 0, 0, 0}, + {0x4F, 0x26, 0x26, 1, 1}, + {0x50, 0x26, 0x26, 1, 1}, + {0x51, 0xf, 0xf, 1, 1}, + {0x52, 0xf, 0xf, 1, 1}, + {0x53, 0x44, 0x44, 0, 0}, + {0x54, 0, 0, 0, 0}, + {0x55, 0, 0, 0, 0}, + {0x56, 0x8, 0x8, 0, 0}, + {0x57, 0x8, 0x8, 0, 0}, + {0x58, 0x7, 0x7, 0, 0}, + {0x59, 0x22, 0x22, 0, 0}, + {0x5A, 0x22, 0x22, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x4, 0x4, 1, 1}, + {0x5D, 0x7, 0x7, 0, 0}, + {0x5E, 0x55, 0x55, 0, 0}, + {0x5F, 0x23, 0x23, 0, 0}, + {0x60, 0x41, 0x41, 0, 0}, + {0x61, 0x1, 0x1, 0, 0}, + {0x62, 0xa, 0xa, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0, 0, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0xc, 0xc, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0, 0, 0, 0}, + {0x72, 0x22, 0x22, 0, 0}, + {0x73, 0x22, 0x22, 0, 0}, + {0x74, 0, 0, 1, 1}, + {0x75, 0xa, 0xa, 0, 0}, + {0x76, 0x1, 0x1, 0, 0}, + {0x77, 0x22, 0x22, 0, 0}, + {0x78, 0x30, 0x30, 0, 0}, + {0x79, 0, 0, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0x5, 0x5, 1, 1}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0}, +}; + +static const struct radio_regs regs_SYN_2056_rev11[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0x1, 0x1, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0x60, 0x60, 0, 0}, + {0x23, 0x6, 0x6, 0, 0}, + {0x24, 0xc, 0xc, 0, 0}, + {0x25, 0, 0, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0, 0, 0, 0}, + {0x28, 0x1, 0x1, 0, 0}, + {0x29, 0, 0, 0, 0}, + {0x2A, 0, 0, 0, 0}, + {0x2B, 0, 0, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0, 0, 0, 0}, + {0x2F, 0x1f, 0x1f, 0, 0}, + {0x30, 0x15, 0x15, 0, 0}, + {0x31, 0xf, 0xf, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0, 0, 0, 0}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0, 0, 0, 0}, + {0x38, 0, 0, 0, 0}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0, 0, 0, 0}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x13, 0x13, 0, 0}, + {0x3D, 0xf, 0xf, 0, 0}, + {0x3E, 0x18, 0x18, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x20, 0x20, 0, 0}, + {0x42, 0x20, 0x20, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x77, 0x77, 0, 0}, + {0x45, 0x7, 0x7, 0, 0}, + {0x46, 0x1, 0x1, 0, 0}, + {0x47, 0x6, 0x6, 1, 1}, + {0x48, 0xf, 0xf, 0, 0}, + {0x49, 0x3f, 0x3f, 1, 1}, + {0x4A, 0x32, 0x32, 0, 0}, + {0x4B, 0x6, 0x6, 1, 1}, + {0x4C, 0x6, 0x6, 1, 1}, + {0x4D, 0x4, 0x4, 0, 0}, + {0x4E, 0x2b, 0x2b, 1, 1}, + {0x4F, 0x1, 0x1, 0, 0}, + {0x50, 0x1c, 0x1c, 0, 0}, + {0x51, 0x2, 0x2, 0, 0}, + {0x52, 0x2, 0x2, 0, 0}, + {0x53, 0xf7, 0xf7, 1, 1}, + {0x54, 0xb4, 0xb4, 0, 0}, + {0x55, 0xd2, 0xd2, 0, 0}, + {0x56, 0, 0, 0, 0}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x4, 0x4, 0, 0}, + {0x59, 0x96, 0x96, 0, 0}, + {0x5A, 0x3e, 0x3e, 0, 0}, + {0x5B, 0x3e, 0x3e, 0, 0}, + {0x5C, 0x13, 0x13, 0, 0}, + {0x5D, 0x2, 0x2, 0, 0}, + {0x5E, 0, 0, 0, 0}, + {0x5F, 0x7, 0x7, 0, 0}, + {0x60, 0x7, 0x7, 1, 1}, + {0x61, 0x8, 0x8, 0, 0}, + {0x62, 0x3, 0x3, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0x40, 0x40, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0x1, 0x1, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0x60, 0x60, 0, 0}, + {0x71, 0x66, 0x66, 0, 0}, + {0x72, 0xc, 0xc, 0, 0}, + {0x73, 0x66, 0x66, 0, 0}, + {0x74, 0x8f, 0x8f, 1, 1}, + {0x75, 0, 0, 0, 0}, + {0x76, 0xcc, 0xcc, 0, 0}, + {0x77, 0x1, 0x1, 0, 0}, + {0x78, 0x66, 0x66, 0, 0}, + {0x79, 0x66, 0x66, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0, 0, 0, 0}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0xff, 0xff, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0x95, 0, 0, 0, 0}, + {0x96, 0, 0, 0, 0}, + {0x97, 0, 0, 0, 0}, + {0x98, 0, 0, 0, 0}, + {0x99, 0, 0, 0, 0}, + {0x9A, 0, 0, 0, 0}, + {0x9B, 0, 0, 0, 0}, + {0x9C, 0, 0, 0, 0}, + {0x9D, 0, 0, 0, 0}, + {0x9E, 0, 0, 0, 0}, + {0x9F, 0x6, 0x6, 0, 0}, + {0xA0, 0x66, 0x66, 0, 0}, + {0xA1, 0x66, 0x66, 0, 0}, + {0xA2, 0x66, 0x66, 0, 0}, + {0xA3, 0x66, 0x66, 0, 0}, + {0xA4, 0x66, 0x66, 0, 0}, + {0xA5, 0x66, 0x66, 0, 0}, + {0xA6, 0x66, 0x66, 0, 0}, + {0xA7, 0x66, 0x66, 0, 0}, + {0xA8, 0x66, 0x66, 0, 0}, + {0xA9, 0x66, 0x66, 0, 0}, + {0xAA, 0x66, 0x66, 0, 0}, + {0xAB, 0x66, 0x66, 0, 0}, + {0xAC, 0x66, 0x66, 0, 0}, + {0xAD, 0x66, 0x66, 0, 0}, + {0xAE, 0x66, 0x66, 0, 0}, + {0xAF, 0x66, 0x66, 0, 0}, + {0xB0, 0x66, 0x66, 0, 0}, + {0xB1, 0x66, 0x66, 0, 0}, + {0xB2, 0x66, 0x66, 0, 0}, + {0xB3, 0xa, 0xa, 0, 0}, + {0xB4, 0, 0, 0, 0}, + {0xB5, 0, 0, 0, 0}, + {0xB6, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0}, +}; + +static const struct radio_regs regs_TX_2056_rev11[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0, 0, 0, 0}, + {0x21, 0x88, 0x88, 0, 0}, + {0x22, 0x88, 0x88, 0, 0}, + {0x23, 0x88, 0x88, 0, 0}, + {0x24, 0x88, 0x88, 0, 0}, + {0x25, 0xc, 0xc, 0, 0}, + {0x26, 0, 0, 0, 0}, + {0x27, 0x3, 0x3, 0, 0}, + {0x28, 0, 0, 0, 0}, + {0x29, 0x3, 0x3, 0, 0}, + {0x2A, 0x37, 0x37, 0, 0}, + {0x2B, 0x3, 0x3, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0, 0, 0, 0}, + {0x2E, 0x1, 0x1, 0, 0}, + {0x2F, 0x1, 0x1, 0, 0}, + {0x30, 0, 0, 0, 0}, + {0x31, 0, 0, 0, 0}, + {0x32, 0, 0, 0, 0}, + {0x33, 0x11, 0x11, 0, 0}, + {0x34, 0xee, 0xee, 1, 1}, + {0x35, 0, 0, 0, 0}, + {0x36, 0, 0, 0, 0}, + {0x37, 0x3, 0x3, 0, 0}, + {0x38, 0x50, 0x50, 1, 1}, + {0x39, 0, 0, 0, 0}, + {0x3A, 0x50, 0x50, 1, 1}, + {0x3B, 0, 0, 0, 0}, + {0x3C, 0x6e, 0x6e, 0, 0}, + {0x3D, 0xf0, 0xf0, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0, 0, 0, 0}, + {0x40, 0, 0, 0, 0}, + {0x41, 0x3, 0x3, 0, 0}, + {0x42, 0x3, 0x3, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x1e, 0x1e, 0, 0}, + {0x45, 0, 0, 0, 0}, + {0x46, 0x6e, 0x6e, 0, 0}, + {0x47, 0xf0, 0xf0, 1, 1}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x2, 0x2, 0, 0}, + {0x4A, 0xff, 0xff, 1, 1}, + {0x4B, 0xc, 0xc, 0, 0}, + {0x4C, 0, 0, 0, 0}, + {0x4D, 0x38, 0x38, 0, 0}, + {0x4E, 0x70, 0x70, 1, 1}, + {0x4F, 0x2, 0x2, 0, 0}, + {0x50, 0x88, 0x88, 0, 0}, + {0x51, 0xc, 0xc, 0, 0}, + {0x52, 0, 0, 0, 0}, + {0x53, 0x8, 0x8, 0, 0}, + {0x54, 0x70, 0x70, 1, 1}, + {0x55, 0x2, 0x2, 0, 0}, + {0x56, 0xff, 0xff, 1, 1}, + {0x57, 0, 0, 0, 0}, + {0x58, 0x83, 0x83, 0, 0}, + {0x59, 0x77, 0x77, 1, 1}, + {0x5A, 0, 0, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x88, 0x88, 0, 0}, + {0x5D, 0, 0, 0, 0}, + {0x5E, 0x8, 0x8, 0, 0}, + {0x5F, 0x77, 0x77, 1, 1}, + {0x60, 0x1, 0x1, 0, 0}, + {0x61, 0, 0, 0, 0}, + {0x62, 0x7, 0x7, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0x7, 0x7, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 1, 1}, + {0x68, 0, 0, 0, 0}, + {0x69, 0xa, 0xa, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0, 0, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0x2, 0x2, 0, 0}, + {0x72, 0, 0, 0, 0}, + {0x73, 0, 0, 0, 0}, + {0x74, 0xe, 0xe, 0, 0}, + {0x75, 0xe, 0xe, 0, 0}, + {0x76, 0xe, 0xe, 0, 0}, + {0x77, 0x13, 0x13, 0, 0}, + {0x78, 0x13, 0x13, 0, 0}, + {0x79, 0x1b, 0x1b, 0, 0}, + {0x7A, 0x1b, 0x1b, 0, 0}, + {0x7B, 0x55, 0x55, 0, 0}, + {0x7C, 0x5b, 0x5b, 0, 0}, + {0x7D, 0x30, 0x30, 1, 1}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0x70, 0x70, 0, 0}, + {0x94, 0x70, 0x70, 0, 0}, + {0x95, 0x70, 0x70, 0, 0}, + {0x96, 0x70, 0x70, 0, 0}, + {0x97, 0x70, 0x70, 0, 0}, + {0x98, 0x70, 0x70, 0, 0}, + {0x99, 0x70, 0x70, 0, 0}, + {0x9A, 0x70, 0x70, 0, 0}, + {0xFFFF, 0, 0, 0, 0}, +}; + +static const struct radio_regs regs_RX_2056_rev11[] = { + {0x02, 0, 0, 0, 0}, + {0x03, 0, 0, 0, 0}, + {0x04, 0, 0, 0, 0}, + {0x05, 0, 0, 0, 0}, + {0x06, 0, 0, 0, 0}, + {0x07, 0, 0, 0, 0}, + {0x08, 0, 0, 0, 0}, + {0x09, 0, 0, 0, 0}, + {0x0A, 0, 0, 0, 0}, + {0x0B, 0, 0, 0, 0}, + {0x0C, 0, 0, 0, 0}, + {0x0D, 0, 0, 0, 0}, + {0x0E, 0, 0, 0, 0}, + {0x0F, 0, 0, 0, 0}, + {0x10, 0, 0, 0, 0}, + {0x11, 0, 0, 0, 0}, + {0x12, 0, 0, 0, 0}, + {0x13, 0, 0, 0, 0}, + {0x14, 0, 0, 0, 0}, + {0x15, 0, 0, 0, 0}, + {0x16, 0, 0, 0, 0}, + {0x17, 0, 0, 0, 0}, + {0x18, 0, 0, 0, 0}, + {0x19, 0, 0, 0, 0}, + {0x1A, 0, 0, 0, 0}, + {0x1B, 0, 0, 0, 0}, + {0x1C, 0, 0, 0, 0}, + {0x1D, 0, 0, 0, 0}, + {0x1E, 0, 0, 0, 0}, + {0x1F, 0, 0, 0, 0}, + {0x20, 0x3, 0x3, 0, 0}, + {0x21, 0, 0, 0, 0}, + {0x22, 0, 0, 0, 0}, + {0x23, 0x90, 0x90, 0, 0}, + {0x24, 0x55, 0x55, 0, 0}, + {0x25, 0x15, 0x15, 0, 0}, + {0x26, 0x5, 0x5, 0, 0}, + {0x27, 0x15, 0x15, 0, 0}, + {0x28, 0x5, 0x5, 0, 0}, + {0x29, 0x20, 0x20, 0, 0}, + {0x2A, 0x11, 0x11, 0, 0}, + {0x2B, 0x90, 0x90, 0, 0}, + {0x2C, 0, 0, 0, 0}, + {0x2D, 0x88, 0x88, 0, 0}, + {0x2E, 0x32, 0x32, 0, 0}, + {0x2F, 0x77, 0x77, 0, 0}, + {0x30, 0x17, 0x17, 1, 1}, + {0x31, 0xff, 0xff, 1, 1}, + {0x32, 0x20, 0x20, 0, 0}, + {0x33, 0, 0, 0, 0}, + {0x34, 0x88, 0x88, 0, 0}, + {0x35, 0x32, 0x32, 0, 0}, + {0x36, 0x77, 0x77, 0, 0}, + {0x37, 0x17, 0x17, 1, 1}, + {0x38, 0xf0, 0xf0, 1, 1}, + {0x39, 0x20, 0x20, 0, 0}, + {0x3A, 0x8, 0x8, 0, 0}, + {0x3B, 0x55, 0x55, 1, 1}, + {0x3C, 0, 0, 0, 0}, + {0x3D, 0x88, 0x88, 1, 1}, + {0x3E, 0, 0, 0, 0}, + {0x3F, 0x44, 0x44, 0, 0}, + {0x40, 0x7, 0x7, 1, 1}, + {0x41, 0x6, 0x6, 0, 0}, + {0x42, 0x4, 0x4, 0, 0}, + {0x43, 0, 0, 0, 0}, + {0x44, 0x8, 0x8, 0, 0}, + {0x45, 0x55, 0x55, 1, 1}, + {0x46, 0, 0, 0, 0}, + {0x47, 0x11, 0x11, 0, 0}, + {0x48, 0, 0, 0, 0}, + {0x49, 0x44, 0x44, 0, 0}, + {0x4A, 0x7, 0x7, 0, 0}, + {0x4B, 0x6, 0x6, 0, 0}, + {0x4C, 0x4, 0x4, 0, 0}, + {0x4D, 0, 0, 0, 0}, + {0x4E, 0, 0, 0, 0}, + {0x4F, 0x26, 0x26, 1, 1}, + {0x50, 0x26, 0x26, 1, 1}, + {0x51, 0xf, 0xf, 1, 1}, + {0x52, 0xf, 0xf, 1, 1}, + {0x53, 0x44, 0x44, 0, 0}, + {0x54, 0, 0, 0, 0}, + {0x55, 0, 0, 0, 0}, + {0x56, 0x8, 0x8, 0, 0}, + {0x57, 0x8, 0x8, 0, 0}, + {0x58, 0x7, 0x7, 0, 0}, + {0x59, 0x22, 0x22, 0, 0}, + {0x5A, 0x22, 0x22, 0, 0}, + {0x5B, 0x2, 0x2, 0, 0}, + {0x5C, 0x4, 0x4, 1, 1}, + {0x5D, 0x7, 0x7, 0, 0}, + {0x5E, 0x55, 0x55, 0, 0}, + {0x5F, 0x23, 0x23, 0, 0}, + {0x60, 0x41, 0x41, 0, 0}, + {0x61, 0x1, 0x1, 0, 0}, + {0x62, 0xa, 0xa, 0, 0}, + {0x63, 0, 0, 0, 0}, + {0x64, 0, 0, 0, 0}, + {0x65, 0, 0, 0, 0}, + {0x66, 0, 0, 0, 0}, + {0x67, 0, 0, 0, 0}, + {0x68, 0, 0, 0, 0}, + {0x69, 0, 0, 0, 0}, + {0x6A, 0, 0, 0, 0}, + {0x6B, 0xc, 0xc, 0, 0}, + {0x6C, 0, 0, 0, 0}, + {0x6D, 0, 0, 0, 0}, + {0x6E, 0, 0, 0, 0}, + {0x6F, 0, 0, 0, 0}, + {0x70, 0, 0, 0, 0}, + {0x71, 0, 0, 0, 0}, + {0x72, 0x22, 0x22, 0, 0}, + {0x73, 0x22, 0x22, 0, 0}, + {0x74, 0, 0, 1, 1}, + {0x75, 0xa, 0xa, 0, 0}, + {0x76, 0x1, 0x1, 0, 0}, + {0x77, 0x22, 0x22, 0, 0}, + {0x78, 0x30, 0x30, 0, 0}, + {0x79, 0, 0, 0, 0}, + {0x7A, 0, 0, 0, 0}, + {0x7B, 0, 0, 0, 0}, + {0x7C, 0, 0, 0, 0}, + {0x7D, 0x5, 0x5, 1, 1}, + {0x7E, 0, 0, 0, 0}, + {0x7F, 0, 0, 0, 0}, + {0x80, 0, 0, 0, 0}, + {0x81, 0, 0, 0, 0}, + {0x82, 0, 0, 0, 0}, + {0x83, 0, 0, 0, 0}, + {0x84, 0, 0, 0, 0}, + {0x85, 0, 0, 0, 0}, + {0x86, 0, 0, 0, 0}, + {0x87, 0, 0, 0, 0}, + {0x88, 0, 0, 0, 0}, + {0x89, 0, 0, 0, 0}, + {0x8A, 0, 0, 0, 0}, + {0x8B, 0, 0, 0, 0}, + {0x8C, 0, 0, 0, 0}, + {0x8D, 0, 0, 0, 0}, + {0x8E, 0, 0, 0, 0}, + {0x8F, 0, 0, 0, 0}, + {0x90, 0, 0, 0, 0}, + {0x91, 0, 0, 0, 0}, + {0x92, 0, 0, 0, 0}, + {0x93, 0, 0, 0, 0}, + {0x94, 0, 0, 0, 0}, + {0xFFFF, 0, 0, 0, 0}, +}; + +static struct radio_20xx_regs regs_2057_rev4[] = { + {0x00, 0x84, 0}, + {0x01, 0, 0}, + {0x02, 0x60, 0}, + {0x03, 0x1f, 0}, + {0x04, 0x4, 0}, + {0x05, 0x2, 0}, + {0x06, 0x1, 0}, + {0x07, 0x1, 0}, + {0x08, 0x1, 0}, + {0x09, 0x69, 0}, + {0x0A, 0x66, 0}, + {0x0B, 0x6, 0}, + {0x0C, 0x18, 0}, + {0x0D, 0x3, 0}, + {0x0E, 0x20, 1}, + {0x0F, 0x20, 0}, + {0x10, 0, 0}, + {0x11, 0x7c, 0}, + {0x12, 0x42, 0}, + {0x13, 0xbd, 0}, + {0x14, 0x7, 0}, + {0x15, 0xf7, 0}, + {0x16, 0x8, 0}, + {0x17, 0x17, 0}, + {0x18, 0x7, 0}, + {0x19, 0, 0}, + {0x1A, 0x2, 0}, + {0x1B, 0x13, 0}, + {0x1C, 0x3e, 0}, + {0x1D, 0x3e, 0}, + {0x1E, 0x96, 0}, + {0x1F, 0x4, 0}, + {0x20, 0, 0}, + {0x21, 0, 0}, + {0x22, 0x17, 0}, + {0x23, 0x4, 0}, + {0x24, 0x1, 0}, + {0x25, 0x6, 0}, + {0x26, 0x4, 0}, + {0x27, 0xd, 0}, + {0x28, 0xd, 0}, + {0x29, 0x30, 0}, + {0x2A, 0x32, 0}, + {0x2B, 0x8, 0}, + {0x2C, 0x1c, 0}, + {0x2D, 0x2, 0}, + {0x2E, 0x4, 0}, + {0x2F, 0x7f, 0}, + {0x30, 0x27, 0}, + {0x31, 0, 1}, + {0x32, 0, 1}, + {0x33, 0, 1}, + {0x34, 0, 0}, + {0x35, 0x26, 1}, + {0x36, 0x18, 0}, + {0x37, 0x7, 0}, + {0x38, 0x66, 0}, + {0x39, 0x66, 0}, + {0x3A, 0x66, 0}, + {0x3B, 0x66, 0}, + {0x3C, 0xff, 1}, + {0x3D, 0xff, 1}, + {0x3E, 0xff, 1}, + {0x3F, 0xff, 1}, + {0x40, 0x16, 0}, + {0x41, 0x7, 0}, + {0x42, 0x19, 0}, + {0x43, 0x7, 0}, + {0x44, 0x6, 0}, + {0x45, 0x3, 0}, + {0x46, 0x1, 0}, + {0x47, 0x7, 0}, + {0x48, 0x33, 0}, + {0x49, 0x5, 0}, + {0x4A, 0x77, 0}, + {0x4B, 0x66, 0}, + {0x4C, 0x66, 0}, + {0x4D, 0, 0}, + {0x4E, 0x4, 0}, + {0x4F, 0xc, 0}, + {0x50, 0, 0}, + {0x51, 0x75, 0}, + {0x56, 0x7, 0}, + {0x57, 0, 0}, + {0x58, 0, 0}, + {0x59, 0xa8, 0}, + {0x5A, 0, 0}, + {0x5B, 0x1f, 0}, + {0x5C, 0x30, 0}, + {0x5D, 0x1, 0}, + {0x5E, 0x30, 0}, + {0x5F, 0x70, 0}, + {0x60, 0, 0}, + {0x61, 0, 0}, + {0x62, 0x33, 1}, + {0x63, 0x19, 0}, + {0x64, 0x62, 0}, + {0x65, 0, 0}, + {0x66, 0x11, 0}, + {0x69, 0, 0}, + {0x6A, 0x7e, 0}, + {0x6B, 0x3f, 0}, + {0x6C, 0x7f, 0}, + {0x6D, 0x78, 0}, + {0x6E, 0xc8, 0}, + {0x6F, 0x88, 0}, + {0x70, 0x8, 0}, + {0x71, 0xf, 0}, + {0x72, 0xbc, 0}, + {0x73, 0x8, 0}, + {0x74, 0x60, 0}, + {0x75, 0x1e, 0}, + {0x76, 0x70, 0}, + {0x77, 0, 0}, + {0x78, 0, 0}, + {0x79, 0, 0}, + {0x7A, 0x33, 0}, + {0x7B, 0x1e, 0}, + {0x7C, 0x62, 0}, + {0x7D, 0x11, 0}, + {0x80, 0x3c, 0}, + {0x81, 0x9c, 0}, + {0x82, 0xa, 0}, + {0x83, 0x9d, 0}, + {0x84, 0xa, 0}, + {0x85, 0, 0}, + {0x86, 0x40, 0}, + {0x87, 0x40, 0}, + {0x88, 0x88, 0}, + {0x89, 0x10, 0}, + {0x8A, 0xf0, 1}, + {0x8B, 0x10, 1}, + {0x8C, 0xf0, 1}, + {0x8D, 0, 0}, + {0x8E, 0, 0}, + {0x8F, 0x10, 0}, + {0x90, 0x55, 0}, + {0x91, 0x3f, 1}, + {0x92, 0x36, 1}, + {0x93, 0, 0}, + {0x94, 0, 0}, + {0x95, 0, 0}, + {0x96, 0x87, 0}, + {0x97, 0x11, 0}, + {0x98, 0, 0}, + {0x99, 0x33, 0}, + {0x9A, 0x88, 0}, + {0x9B, 0, 0}, + {0x9C, 0x87, 0}, + {0x9D, 0x11, 0}, + {0x9E, 0, 0}, + {0x9F, 0x33, 0}, + {0xA0, 0x88, 0}, + {0xA1, 0xe1, 0}, + {0xA2, 0x3f, 0}, + {0xA3, 0x44, 0}, + {0xA4, 0x8c, 1}, + {0xA5, 0x6d, 0}, + {0xA6, 0x22, 0}, + {0xA7, 0xbe, 0}, + {0xA8, 0x55, 1}, + {0xA9, 0xc, 0}, + {0xAA, 0xc, 0}, + {0xAB, 0xaa, 0}, + {0xAC, 0x2, 0}, + {0xAD, 0, 0}, + {0xAE, 0x10, 0}, + {0xAF, 0x1, 1}, + {0xB0, 0, 0}, + {0xB1, 0, 0}, + {0xB2, 0x80, 0}, + {0xB3, 0x60, 0}, + {0xB4, 0x44, 0}, + {0xB5, 0x55, 0}, + {0xB6, 0x1, 0}, + {0xB7, 0x55, 0}, + {0xB8, 0x1, 0}, + {0xB9, 0x5, 0}, + {0xBA, 0x55, 0}, + {0xBB, 0x55, 0}, + {0xC1, 0, 0}, + {0xC2, 0, 0}, + {0xC3, 0, 0}, + {0xC4, 0, 0}, + {0xC5, 0, 0}, + {0xC6, 0, 0}, + {0xC7, 0, 0}, + {0xC8, 0, 0}, + {0xC9, 0, 0}, + {0xCA, 0, 0}, + {0xCB, 0, 0}, + {0xCC, 0, 0}, + {0xCD, 0, 0}, + {0xCE, 0x5e, 0}, + {0xCF, 0xc, 0}, + {0xD0, 0xc, 0}, + {0xD1, 0xc, 0}, + {0xD2, 0, 0}, + {0xD3, 0x2b, 0}, + {0xD4, 0xc, 0}, + {0xD5, 0, 0}, + {0xD6, 0x75, 0}, + {0xDB, 0x7, 0}, + {0xDC, 0, 0}, + {0xDD, 0, 0}, + {0xDE, 0xa8, 0}, + {0xDF, 0, 0}, + {0xE0, 0x1f, 0}, + {0xE1, 0x30, 0}, + {0xE2, 0x1, 0}, + {0xE3, 0x30, 0}, + {0xE4, 0x70, 0}, + {0xE5, 0, 0}, + {0xE6, 0, 0}, + {0xE7, 0x33, 0}, + {0xE8, 0x19, 0}, + {0xE9, 0x62, 0}, + {0xEA, 0, 0}, + {0xEB, 0x11, 0}, + {0xEE, 0, 0}, + {0xEF, 0x7e, 0}, + {0xF0, 0x3f, 0}, + {0xF1, 0x7f, 0}, + {0xF2, 0x78, 0}, + {0xF3, 0xc8, 0}, + {0xF4, 0x88, 0}, + {0xF5, 0x8, 0}, + {0xF6, 0xf, 0}, + {0xF7, 0xbc, 0}, + {0xF8, 0x8, 0}, + {0xF9, 0x60, 0}, + {0xFA, 0x1e, 0}, + {0xFB, 0x70, 0}, + {0xFC, 0, 0}, + {0xFD, 0, 0}, + {0xFE, 0, 0}, + {0xFF, 0x33, 0}, + {0x100, 0x1e, 0}, + {0x101, 0x62, 0}, + {0x102, 0x11, 0}, + {0x105, 0x3c, 0}, + {0x106, 0x9c, 0}, + {0x107, 0xa, 0}, + {0x108, 0x9d, 0}, + {0x109, 0xa, 0}, + {0x10A, 0, 0}, + {0x10B, 0x40, 0}, + {0x10C, 0x40, 0}, + {0x10D, 0x88, 0}, + {0x10E, 0x10, 0}, + {0x10F, 0xf0, 1}, + {0x110, 0x10, 1}, + {0x111, 0xf0, 1}, + {0x112, 0, 0}, + {0x113, 0, 0}, + {0x114, 0x10, 0}, + {0x115, 0x55, 0}, + {0x116, 0x3f, 1}, + {0x117, 0x36, 1}, + {0x118, 0, 0}, + {0x119, 0, 0}, + {0x11A, 0, 0}, + {0x11B, 0x87, 0}, + {0x11C, 0x11, 0}, + {0x11D, 0, 0}, + {0x11E, 0x33, 0}, + {0x11F, 0x88, 0}, + {0x120, 0, 0}, + {0x121, 0x87, 0}, + {0x122, 0x11, 0}, + {0x123, 0, 0}, + {0x124, 0x33, 0}, + {0x125, 0x88, 0}, + {0x126, 0xe1, 0}, + {0x127, 0x3f, 0}, + {0x128, 0x44, 0}, + {0x129, 0x8c, 1}, + {0x12A, 0x6d, 0}, + {0x12B, 0x22, 0}, + {0x12C, 0xbe, 0}, + {0x12D, 0x55, 1}, + {0x12E, 0xc, 0}, + {0x12F, 0xc, 0}, + {0x130, 0xaa, 0}, + {0x131, 0x2, 0}, + {0x132, 0, 0}, + {0x133, 0x10, 0}, + {0x134, 0x1, 1}, + {0x135, 0, 0}, + {0x136, 0, 0}, + {0x137, 0x80, 0}, + {0x138, 0x60, 0}, + {0x139, 0x44, 0}, + {0x13A, 0x55, 0}, + {0x13B, 0x1, 0}, + {0x13C, 0x55, 0}, + {0x13D, 0x1, 0}, + {0x13E, 0x5, 0}, + {0x13F, 0x55, 0}, + {0x140, 0x55, 0}, + {0x146, 0, 0}, + {0x147, 0, 0}, + {0x148, 0, 0}, + {0x149, 0, 0}, + {0x14A, 0, 0}, + {0x14B, 0, 0}, + {0x14C, 0, 0}, + {0x14D, 0, 0}, + {0x14E, 0, 0}, + {0x14F, 0, 0}, + {0x150, 0, 0}, + {0x151, 0, 0}, + {0x152, 0, 0}, + {0x153, 0, 0}, + {0x154, 0xc, 0}, + {0x155, 0xc, 0}, + {0x156, 0xc, 0}, + {0x157, 0, 0}, + {0x158, 0x2b, 0}, + {0x159, 0x84, 0}, + {0x15A, 0x15, 0}, + {0x15B, 0xf, 0}, + {0x15C, 0, 0}, + {0x15D, 0, 0}, + {0x15E, 0, 1}, + {0x15F, 0, 1}, + {0x160, 0, 1}, + {0x161, 0, 1}, + {0x162, 0, 1}, + {0x163, 0, 1}, + {0x164, 0, 0}, + {0x165, 0, 0}, + {0x166, 0, 0}, + {0x167, 0, 0}, + {0x168, 0, 0}, + {0x169, 0x2, 1}, + {0x16A, 0, 1}, + {0x16B, 0, 1}, + {0x16C, 0, 1}, + {0x16D, 0, 0}, + {0x170, 0, 0}, + {0x171, 0x77, 0}, + {0x172, 0x77, 0}, + {0x173, 0x77, 0}, + {0x174, 0x77, 0}, + {0x175, 0, 0}, + {0x176, 0x3, 0}, + {0x177, 0x37, 0}, + {0x178, 0x3, 0}, + {0x179, 0, 0}, + {0x17A, 0x21, 0}, + {0x17B, 0x21, 0}, + {0x17C, 0, 0}, + {0x17D, 0xaa, 0}, + {0x17E, 0, 0}, + {0x17F, 0xaa, 0}, + {0x180, 0, 0}, + {0x190, 0, 0}, + {0x191, 0x77, 0}, + {0x192, 0x77, 0}, + {0x193, 0x77, 0}, + {0x194, 0x77, 0}, + {0x195, 0, 0}, + {0x196, 0x3, 0}, + {0x197, 0x37, 0}, + {0x198, 0x3, 0}, + {0x199, 0, 0}, + {0x19A, 0x21, 0}, + {0x19B, 0x21, 0}, + {0x19C, 0, 0}, + {0x19D, 0xaa, 0}, + {0x19E, 0, 0}, + {0x19F, 0xaa, 0}, + {0x1A0, 0, 0}, + {0x1A1, 0x2, 0}, + {0x1A2, 0xf, 0}, + {0x1A3, 0xf, 0}, + {0x1A4, 0, 1}, + {0x1A5, 0, 1}, + {0x1A6, 0, 1}, + {0x1A7, 0x2, 0}, + {0x1A8, 0xf, 0}, + {0x1A9, 0xf, 0}, + {0x1AA, 0, 1}, + {0x1AB, 0, 1}, + {0x1AC, 0, 1}, + {0xFFFF, 0, 0}, +}; + +static struct radio_20xx_regs regs_2057_rev5[] = { + {0x00, 0, 1}, + {0x01, 0x57, 1}, + {0x02, 0x20, 1}, + {0x03, 0x1f, 0}, + {0x04, 0x4, 0}, + {0x05, 0x2, 0}, + {0x06, 0x1, 0}, + {0x07, 0x1, 0}, + {0x08, 0x1, 0}, + {0x09, 0x69, 0}, + {0x0A, 0x66, 0}, + {0x0B, 0x6, 0}, + {0x0C, 0x18, 0}, + {0x0D, 0x3, 0}, + {0x0E, 0x20, 0}, + {0x0F, 0x20, 0}, + {0x10, 0, 0}, + {0x11, 0x7c, 0}, + {0x12, 0x42, 0}, + {0x13, 0xbd, 0}, + {0x14, 0x7, 0}, + {0x15, 0x87, 0}, + {0x16, 0x8, 0}, + {0x17, 0x17, 0}, + {0x18, 0x7, 0}, + {0x19, 0, 0}, + {0x1A, 0x2, 0}, + {0x1B, 0x13, 0}, + {0x1C, 0x3e, 0}, + {0x1D, 0x3e, 0}, + {0x1E, 0x96, 0}, + {0x1F, 0x4, 0}, + {0x20, 0, 0}, + {0x21, 0, 0}, + {0x22, 0x17, 0}, + {0x23, 0x6, 1}, + {0x24, 0x1, 0}, + {0x25, 0x6, 0}, + {0x26, 0x4, 0}, + {0x27, 0xd, 0}, + {0x28, 0xd, 0}, + {0x29, 0x30, 0}, + {0x2A, 0x32, 0}, + {0x2B, 0x8, 0}, + {0x2C, 0x1c, 0}, + {0x2D, 0x2, 0}, + {0x2E, 0x4, 0}, + {0x2F, 0x7f, 0}, + {0x30, 0x27, 0}, + {0x31, 0, 1}, + {0x32, 0, 1}, + {0x33, 0, 1}, + {0x34, 0, 0}, + {0x35, 0x20, 0}, + {0x36, 0x18, 0}, + {0x37, 0x7, 0}, + {0x38, 0x66, 0}, + {0x39, 0x66, 0}, + {0x3C, 0xff, 0}, + {0x3D, 0xff, 0}, + {0x40, 0x16, 0}, + {0x41, 0x7, 0}, + {0x45, 0x3, 0}, + {0x46, 0x1, 0}, + {0x47, 0x7, 0}, + {0x4B, 0x66, 0}, + {0x4C, 0x66, 0}, + {0x4D, 0, 0}, + {0x4E, 0x4, 0}, + {0x4F, 0xc, 0}, + {0x50, 0, 0}, + {0x51, 0x70, 1}, + {0x56, 0x7, 0}, + {0x57, 0, 0}, + {0x58, 0, 0}, + {0x59, 0x88, 1}, + {0x5A, 0, 0}, + {0x5B, 0x1f, 0}, + {0x5C, 0x20, 1}, + {0x5D, 0x1, 0}, + {0x5E, 0x30, 0}, + {0x5F, 0x70, 0}, + {0x60, 0, 0}, + {0x61, 0, 0}, + {0x62, 0x33, 1}, + {0x63, 0xf, 1}, + {0x64, 0xf, 1}, + {0x65, 0, 0}, + {0x66, 0x11, 0}, + {0x80, 0x3c, 0}, + {0x81, 0x1, 1}, + {0x82, 0xa, 0}, + {0x85, 0, 0}, + {0x86, 0x40, 0}, + {0x87, 0x40, 0}, + {0x88, 0x88, 0}, + {0x89, 0x10, 0}, + {0x8A, 0xf0, 0}, + {0x8B, 0x10, 0}, + {0x8C, 0xf0, 0}, + {0x8F, 0x10, 0}, + {0x90, 0x55, 0}, + {0x91, 0x3f, 1}, + {0x92, 0x36, 1}, + {0x93, 0, 0}, + {0x94, 0, 0}, + {0x95, 0, 0}, + {0x96, 0x87, 0}, + {0x97, 0x11, 0}, + {0x98, 0, 0}, + {0x99, 0x33, 0}, + {0x9A, 0x88, 0}, + {0xA1, 0x20, 1}, + {0xA2, 0x3f, 0}, + {0xA3, 0x44, 0}, + {0xA4, 0x8c, 0}, + {0xA5, 0x6c, 0}, + {0xA6, 0x22, 0}, + {0xA7, 0xbe, 0}, + {0xA8, 0x55, 0}, + {0xAA, 0xc, 0}, + {0xAB, 0xaa, 0}, + {0xAC, 0x2, 0}, + {0xAD, 0, 0}, + {0xAE, 0x10, 0}, + {0xAF, 0x1, 0}, + {0xB0, 0, 0}, + {0xB1, 0, 0}, + {0xB2, 0x80, 0}, + {0xB3, 0x60, 0}, + {0xB4, 0x44, 0}, + {0xB5, 0x55, 0}, + {0xB6, 0x1, 0}, + {0xB7, 0x55, 0}, + {0xB8, 0x1, 0}, + {0xB9, 0x5, 0}, + {0xBA, 0x55, 0}, + {0xBB, 0x55, 0}, + {0xC3, 0, 0}, + {0xC4, 0, 0}, + {0xC5, 0, 0}, + {0xC6, 0, 0}, + {0xC7, 0, 0}, + {0xC8, 0, 0}, + {0xC9, 0, 0}, + {0xCA, 0, 0}, + {0xCB, 0, 0}, + {0xCD, 0, 0}, + {0xCE, 0x5e, 0}, + {0xCF, 0xc, 0}, + {0xD0, 0xc, 0}, + {0xD1, 0xc, 0}, + {0xD2, 0, 0}, + {0xD3, 0x2b, 0}, + {0xD4, 0xc, 0}, + {0xD5, 0, 0}, + {0xD6, 0x70, 1}, + {0xDB, 0x7, 0}, + {0xDC, 0, 0}, + {0xDD, 0, 0}, + {0xDE, 0x88, 1}, + {0xDF, 0, 0}, + {0xE0, 0x1f, 0}, + {0xE1, 0x20, 1}, + {0xE2, 0x1, 0}, + {0xE3, 0x30, 0}, + {0xE4, 0x70, 0}, + {0xE5, 0, 0}, + {0xE6, 0, 0}, + {0xE7, 0x33, 0}, + {0xE8, 0xf, 1}, + {0xE9, 0xf, 1}, + {0xEA, 0, 0}, + {0xEB, 0x11, 0}, + {0x105, 0x3c, 0}, + {0x106, 0x1, 1}, + {0x107, 0xa, 0}, + {0x10A, 0, 0}, + {0x10B, 0x40, 0}, + {0x10C, 0x40, 0}, + {0x10D, 0x88, 0}, + {0x10E, 0x10, 0}, + {0x10F, 0xf0, 0}, + {0x110, 0x10, 0}, + {0x111, 0xf0, 0}, + {0x114, 0x10, 0}, + {0x115, 0x55, 0}, + {0x116, 0x3f, 1}, + {0x117, 0x36, 1}, + {0x118, 0, 0}, + {0x119, 0, 0}, + {0x11A, 0, 0}, + {0x11B, 0x87, 0}, + {0x11C, 0x11, 0}, + {0x11D, 0, 0}, + {0x11E, 0x33, 0}, + {0x11F, 0x88, 0}, + {0x126, 0x20, 1}, + {0x127, 0x3f, 0}, + {0x128, 0x44, 0}, + {0x129, 0x8c, 0}, + {0x12A, 0x6c, 0}, + {0x12B, 0x22, 0}, + {0x12C, 0xbe, 0}, + {0x12D, 0x55, 0}, + {0x12F, 0xc, 0}, + {0x130, 0xaa, 0}, + {0x131, 0x2, 0}, + {0x132, 0, 0}, + {0x133, 0x10, 0}, + {0x134, 0x1, 0}, + {0x135, 0, 0}, + {0x136, 0, 0}, + {0x137, 0x80, 0}, + {0x138, 0x60, 0}, + {0x139, 0x44, 0}, + {0x13A, 0x55, 0}, + {0x13B, 0x1, 0}, + {0x13C, 0x55, 0}, + {0x13D, 0x1, 0}, + {0x13E, 0x5, 0}, + {0x13F, 0x55, 0}, + {0x140, 0x55, 0}, + {0x148, 0, 0}, + {0x149, 0, 0}, + {0x14A, 0, 0}, + {0x14B, 0, 0}, + {0x14C, 0, 0}, + {0x14D, 0, 0}, + {0x14E, 0, 0}, + {0x14F, 0, 0}, + {0x150, 0, 0}, + {0x154, 0xc, 0}, + {0x155, 0xc, 0}, + {0x156, 0xc, 0}, + {0x157, 0, 0}, + {0x158, 0x2b, 0}, + {0x159, 0x84, 0}, + {0x15A, 0x15, 0}, + {0x15B, 0xf, 0}, + {0x15C, 0, 0}, + {0x15D, 0, 0}, + {0x15E, 0, 1}, + {0x15F, 0, 1}, + {0x160, 0, 1}, + {0x161, 0, 1}, + {0x162, 0, 1}, + {0x163, 0, 1}, + {0x164, 0, 0}, + {0x165, 0, 0}, + {0x166, 0, 0}, + {0x167, 0, 0}, + {0x168, 0, 0}, + {0x169, 0, 0}, + {0x16A, 0, 1}, + {0x16B, 0, 1}, + {0x16C, 0, 1}, + {0x16D, 0, 0}, + {0x170, 0, 0}, + {0x171, 0x77, 0}, + {0x172, 0x77, 0}, + {0x173, 0x77, 0}, + {0x174, 0x77, 0}, + {0x175, 0, 0}, + {0x176, 0x3, 0}, + {0x177, 0x37, 0}, + {0x178, 0x3, 0}, + {0x179, 0, 0}, + {0x17B, 0x21, 0}, + {0x17C, 0, 0}, + {0x17D, 0xaa, 0}, + {0x17E, 0, 0}, + {0x190, 0, 0}, + {0x191, 0x77, 0}, + {0x192, 0x77, 0}, + {0x193, 0x77, 0}, + {0x194, 0x77, 0}, + {0x195, 0, 0}, + {0x196, 0x3, 0}, + {0x197, 0x37, 0}, + {0x198, 0x3, 0}, + {0x199, 0, 0}, + {0x19B, 0x21, 0}, + {0x19C, 0, 0}, + {0x19D, 0xaa, 0}, + {0x19E, 0, 0}, + {0x1A1, 0x2, 0}, + {0x1A2, 0xf, 0}, + {0x1A3, 0xf, 0}, + {0x1A4, 0, 1}, + {0x1A5, 0, 1}, + {0x1A6, 0, 1}, + {0x1A7, 0x2, 0}, + {0x1A8, 0xf, 0}, + {0x1A9, 0xf, 0}, + {0x1AA, 0, 1}, + {0x1AB, 0, 1}, + {0x1AC, 0, 1}, + {0x1AD, 0x84, 0}, + {0x1AE, 0x60, 0}, + {0x1AF, 0x47, 0}, + {0x1B0, 0x47, 0}, + {0x1B1, 0, 0}, + {0x1B2, 0, 0}, + {0x1B3, 0, 0}, + {0x1B4, 0, 0}, + {0x1B5, 0, 0}, + {0x1B6, 0, 0}, + {0x1B7, 0xc, 1}, + {0x1B8, 0, 0}, + {0x1B9, 0, 0}, + {0x1BA, 0, 0}, + {0x1BB, 0, 0}, + {0x1BC, 0, 0}, + {0x1BD, 0, 0}, + {0x1BE, 0, 0}, + {0x1BF, 0, 0}, + {0x1C0, 0, 0}, + {0x1C1, 0x1, 1}, + {0x1C2, 0x80, 1}, + {0x1C3, 0, 0}, + {0x1C4, 0, 0}, + {0x1C5, 0, 0}, + {0x1C6, 0, 0}, + {0x1C7, 0, 0}, + {0x1C8, 0, 0}, + {0x1C9, 0, 0}, + {0x1CA, 0, 0}, + {0xFFFF, 0, 0} +}; + +static struct radio_20xx_regs regs_2057_rev5v1[] = { + {0x00, 0x15, 1}, + {0x01, 0x57, 1}, + {0x02, 0x20, 1}, + {0x03, 0x1f, 0}, + {0x04, 0x4, 0}, + {0x05, 0x2, 0}, + {0x06, 0x1, 0}, + {0x07, 0x1, 0}, + {0x08, 0x1, 0}, + {0x09, 0x69, 0}, + {0x0A, 0x66, 0}, + {0x0B, 0x6, 0}, + {0x0C, 0x18, 0}, + {0x0D, 0x3, 0}, + {0x0E, 0x20, 0}, + {0x0F, 0x20, 0}, + {0x10, 0, 0}, + {0x11, 0x7c, 0}, + {0x12, 0x42, 0}, + {0x13, 0xbd, 0}, + {0x14, 0x7, 0}, + {0x15, 0x87, 0}, + {0x16, 0x8, 0}, + {0x17, 0x17, 0}, + {0x18, 0x7, 0}, + {0x19, 0, 0}, + {0x1A, 0x2, 0}, + {0x1B, 0x13, 0}, + {0x1C, 0x3e, 0}, + {0x1D, 0x3e, 0}, + {0x1E, 0x96, 0}, + {0x1F, 0x4, 0}, + {0x20, 0, 0}, + {0x21, 0, 0}, + {0x22, 0x17, 0}, + {0x23, 0x6, 1}, + {0x24, 0x1, 0}, + {0x25, 0x6, 0}, + {0x26, 0x4, 0}, + {0x27, 0xd, 0}, + {0x28, 0xd, 0}, + {0x29, 0x30, 0}, + {0x2A, 0x32, 0}, + {0x2B, 0x8, 0}, + {0x2C, 0x1c, 0}, + {0x2D, 0x2, 0}, + {0x2E, 0x4, 0}, + {0x2F, 0x7f, 0}, + {0x30, 0x27, 0}, + {0x31, 0, 1}, + {0x32, 0, 1}, + {0x33, 0, 1}, + {0x34, 0, 0}, + {0x35, 0x20, 0}, + {0x36, 0x18, 0}, + {0x37, 0x7, 0}, + {0x38, 0x66, 0}, + {0x39, 0x66, 0}, + {0x3C, 0xff, 0}, + {0x3D, 0xff, 0}, + {0x40, 0x16, 0}, + {0x41, 0x7, 0}, + {0x45, 0x3, 0}, + {0x46, 0x1, 0}, + {0x47, 0x7, 0}, + {0x4B, 0x66, 0}, + {0x4C, 0x66, 0}, + {0x4D, 0, 0}, + {0x4E, 0x4, 0}, + {0x4F, 0xc, 0}, + {0x50, 0, 0}, + {0x51, 0x70, 1}, + {0x56, 0x7, 0}, + {0x57, 0, 0}, + {0x58, 0, 0}, + {0x59, 0x88, 1}, + {0x5A, 0, 0}, + {0x5B, 0x1f, 0}, + {0x5C, 0x20, 1}, + {0x5D, 0x1, 0}, + {0x5E, 0x30, 0}, + {0x5F, 0x70, 0}, + {0x60, 0, 0}, + {0x61, 0, 0}, + {0x62, 0x33, 1}, + {0x63, 0xf, 1}, + {0x64, 0xf, 1}, + {0x65, 0, 0}, + {0x66, 0x11, 0}, + {0x80, 0x3c, 0}, + {0x81, 0x1, 1}, + {0x82, 0xa, 0}, + {0x85, 0, 0}, + {0x86, 0x40, 0}, + {0x87, 0x40, 0}, + {0x88, 0x88, 0}, + {0x89, 0x10, 0}, + {0x8A, 0xf0, 0}, + {0x8B, 0x10, 0}, + {0x8C, 0xf0, 0}, + {0x8F, 0x10, 0}, + {0x90, 0x55, 0}, + {0x91, 0x3f, 1}, + {0x92, 0x36, 1}, + {0x93, 0, 0}, + {0x94, 0, 0}, + {0x95, 0, 0}, + {0x96, 0x87, 0}, + {0x97, 0x11, 0}, + {0x98, 0, 0}, + {0x99, 0x33, 0}, + {0x9A, 0x88, 0}, + {0xA1, 0x20, 1}, + {0xA2, 0x3f, 0}, + {0xA3, 0x44, 0}, + {0xA4, 0x8c, 0}, + {0xA5, 0x6c, 0}, + {0xA6, 0x22, 0}, + {0xA7, 0xbe, 0}, + {0xA8, 0x55, 0}, + {0xAA, 0xc, 0}, + {0xAB, 0xaa, 0}, + {0xAC, 0x2, 0}, + {0xAD, 0, 0}, + {0xAE, 0x10, 0}, + {0xAF, 0x1, 0}, + {0xB0, 0, 0}, + {0xB1, 0, 0}, + {0xB2, 0x80, 0}, + {0xB3, 0x60, 0}, + {0xB4, 0x44, 0}, + {0xB5, 0x55, 0}, + {0xB6, 0x1, 0}, + {0xB7, 0x55, 0}, + {0xB8, 0x1, 0}, + {0xB9, 0x5, 0}, + {0xBA, 0x55, 0}, + {0xBB, 0x55, 0}, + {0xC3, 0, 0}, + {0xC4, 0, 0}, + {0xC5, 0, 0}, + {0xC6, 0, 0}, + {0xC7, 0, 0}, + {0xC8, 0, 0}, + {0xC9, 0x1, 1}, + {0xCA, 0, 0}, + {0xCB, 0, 0}, + {0xCD, 0, 0}, + {0xCE, 0x5e, 0}, + {0xCF, 0xc, 0}, + {0xD0, 0xc, 0}, + {0xD1, 0xc, 0}, + {0xD2, 0, 0}, + {0xD3, 0x2b, 0}, + {0xD4, 0xc, 0}, + {0xD5, 0, 0}, + {0xD6, 0x70, 1}, + {0xDB, 0x7, 0}, + {0xDC, 0, 0}, + {0xDD, 0, 0}, + {0xDE, 0x88, 1}, + {0xDF, 0, 0}, + {0xE0, 0x1f, 0}, + {0xE1, 0x20, 1}, + {0xE2, 0x1, 0}, + {0xE3, 0x30, 0}, + {0xE4, 0x70, 0}, + {0xE5, 0, 0}, + {0xE6, 0, 0}, + {0xE7, 0x33, 0}, + {0xE8, 0xf, 1}, + {0xE9, 0xf, 1}, + {0xEA, 0, 0}, + {0xEB, 0x11, 0}, + {0x105, 0x3c, 0}, + {0x106, 0x1, 1}, + {0x107, 0xa, 0}, + {0x10A, 0, 0}, + {0x10B, 0x40, 0}, + {0x10C, 0x40, 0}, + {0x10D, 0x88, 0}, + {0x10E, 0x10, 0}, + {0x10F, 0xf0, 0}, + {0x110, 0x10, 0}, + {0x111, 0xf0, 0}, + {0x114, 0x10, 0}, + {0x115, 0x55, 0}, + {0x116, 0x3f, 1}, + {0x117, 0x36, 1}, + {0x118, 0, 0}, + {0x119, 0, 0}, + {0x11A, 0, 0}, + {0x11B, 0x87, 0}, + {0x11C, 0x11, 0}, + {0x11D, 0, 0}, + {0x11E, 0x33, 0}, + {0x11F, 0x88, 0}, + {0x126, 0x20, 1}, + {0x127, 0x3f, 0}, + {0x128, 0x44, 0}, + {0x129, 0x8c, 0}, + {0x12A, 0x6c, 0}, + {0x12B, 0x22, 0}, + {0x12C, 0xbe, 0}, + {0x12D, 0x55, 0}, + {0x12F, 0xc, 0}, + {0x130, 0xaa, 0}, + {0x131, 0x2, 0}, + {0x132, 0, 0}, + {0x133, 0x10, 0}, + {0x134, 0x1, 0}, + {0x135, 0, 0}, + {0x136, 0, 0}, + {0x137, 0x80, 0}, + {0x138, 0x60, 0}, + {0x139, 0x44, 0}, + {0x13A, 0x55, 0}, + {0x13B, 0x1, 0}, + {0x13C, 0x55, 0}, + {0x13D, 0x1, 0}, + {0x13E, 0x5, 0}, + {0x13F, 0x55, 0}, + {0x140, 0x55, 0}, + {0x148, 0, 0}, + {0x149, 0, 0}, + {0x14A, 0, 0}, + {0x14B, 0, 0}, + {0x14C, 0, 0}, + {0x14D, 0, 0}, + {0x14E, 0x1, 1}, + {0x14F, 0, 0}, + {0x150, 0, 0}, + {0x154, 0xc, 0}, + {0x155, 0xc, 0}, + {0x156, 0xc, 0}, + {0x157, 0, 0}, + {0x158, 0x2b, 0}, + {0x159, 0x84, 0}, + {0x15A, 0x15, 0}, + {0x15B, 0xf, 0}, + {0x15C, 0, 0}, + {0x15D, 0, 0}, + {0x15E, 0, 1}, + {0x15F, 0, 1}, + {0x160, 0, 1}, + {0x161, 0, 1}, + {0x162, 0, 1}, + {0x163, 0, 1}, + {0x164, 0, 0}, + {0x165, 0, 0}, + {0x166, 0, 0}, + {0x167, 0, 0}, + {0x168, 0, 0}, + {0x169, 0, 0}, + {0x16A, 0, 1}, + {0x16B, 0, 1}, + {0x16C, 0, 1}, + {0x16D, 0, 0}, + {0x170, 0, 0}, + {0x171, 0x77, 0}, + {0x172, 0x77, 0}, + {0x173, 0x77, 0}, + {0x174, 0x77, 0}, + {0x175, 0, 0}, + {0x176, 0x3, 0}, + {0x177, 0x37, 0}, + {0x178, 0x3, 0}, + {0x179, 0, 0}, + {0x17B, 0x21, 0}, + {0x17C, 0, 0}, + {0x17D, 0xaa, 0}, + {0x17E, 0, 0}, + {0x190, 0, 0}, + {0x191, 0x77, 0}, + {0x192, 0x77, 0}, + {0x193, 0x77, 0}, + {0x194, 0x77, 0}, + {0x195, 0, 0}, + {0x196, 0x3, 0}, + {0x197, 0x37, 0}, + {0x198, 0x3, 0}, + {0x199, 0, 0}, + {0x19B, 0x21, 0}, + {0x19C, 0, 0}, + {0x19D, 0xaa, 0}, + {0x19E, 0, 0}, + {0x1A1, 0x2, 0}, + {0x1A2, 0xf, 0}, + {0x1A3, 0xf, 0}, + {0x1A4, 0, 1}, + {0x1A5, 0, 1}, + {0x1A6, 0, 1}, + {0x1A7, 0x2, 0}, + {0x1A8, 0xf, 0}, + {0x1A9, 0xf, 0}, + {0x1AA, 0, 1}, + {0x1AB, 0, 1}, + {0x1AC, 0, 1}, + {0x1AD, 0x84, 0}, + {0x1AE, 0x60, 0}, + {0x1AF, 0x47, 0}, + {0x1B0, 0x47, 0}, + {0x1B1, 0, 0}, + {0x1B2, 0, 0}, + {0x1B3, 0, 0}, + {0x1B4, 0, 0}, + {0x1B5, 0, 0}, + {0x1B6, 0, 0}, + {0x1B7, 0xc, 1}, + {0x1B8, 0, 0}, + {0x1B9, 0, 0}, + {0x1BA, 0, 0}, + {0x1BB, 0, 0}, + {0x1BC, 0, 0}, + {0x1BD, 0, 0}, + {0x1BE, 0, 0}, + {0x1BF, 0, 0}, + {0x1C0, 0, 0}, + {0x1C1, 0x1, 1}, + {0x1C2, 0x80, 1}, + {0x1C3, 0, 0}, + {0x1C4, 0, 0}, + {0x1C5, 0, 0}, + {0x1C6, 0, 0}, + {0x1C7, 0, 0}, + {0x1C8, 0, 0}, + {0x1C9, 0, 0}, + {0x1CA, 0, 0}, + {0xFFFF, 0, 0} +}; + +static struct radio_20xx_regs regs_2057_rev7[] = { + {0x00, 0, 1}, + {0x01, 0x57, 1}, + {0x02, 0x20, 1}, + {0x03, 0x1f, 0}, + {0x04, 0x4, 0}, + {0x05, 0x2, 0}, + {0x06, 0x1, 0}, + {0x07, 0x1, 0}, + {0x08, 0x1, 0}, + {0x09, 0x69, 0}, + {0x0A, 0x66, 0}, + {0x0B, 0x6, 0}, + {0x0C, 0x18, 0}, + {0x0D, 0x3, 0}, + {0x0E, 0x20, 0}, + {0x0F, 0x20, 0}, + {0x10, 0, 0}, + {0x11, 0x7c, 0}, + {0x12, 0x42, 0}, + {0x13, 0xbd, 0}, + {0x14, 0x7, 0}, + {0x15, 0x87, 0}, + {0x16, 0x8, 0}, + {0x17, 0x17, 0}, + {0x18, 0x7, 0}, + {0x19, 0, 0}, + {0x1A, 0x2, 0}, + {0x1B, 0x13, 0}, + {0x1C, 0x3e, 0}, + {0x1D, 0x3e, 0}, + {0x1E, 0x96, 0}, + {0x1F, 0x4, 0}, + {0x20, 0, 0}, + {0x21, 0, 0}, + {0x22, 0x17, 0}, + {0x23, 0x6, 0}, + {0x24, 0x1, 0}, + {0x25, 0x6, 0}, + {0x26, 0x4, 0}, + {0x27, 0xd, 0}, + {0x28, 0xd, 0}, + {0x29, 0x30, 0}, + {0x2A, 0x32, 0}, + {0x2B, 0x8, 0}, + {0x2C, 0x1c, 0}, + {0x2D, 0x2, 0}, + {0x2E, 0x4, 0}, + {0x2F, 0x7f, 0}, + {0x30, 0x27, 0}, + {0x31, 0, 1}, + {0x32, 0, 1}, + {0x33, 0, 1}, + {0x34, 0, 0}, + {0x35, 0x20, 0}, + {0x36, 0x18, 0}, + {0x37, 0x7, 0}, + {0x38, 0x66, 0}, + {0x39, 0x66, 0}, + {0x3A, 0x66, 0}, + {0x3B, 0x66, 0}, + {0x3C, 0xff, 0}, + {0x3D, 0xff, 0}, + {0x3E, 0xff, 0}, + {0x3F, 0xff, 0}, + {0x40, 0x16, 0}, + {0x41, 0x7, 0}, + {0x42, 0x19, 0}, + {0x43, 0x7, 0}, + {0x44, 0x6, 0}, + {0x45, 0x3, 0}, + {0x46, 0x1, 0}, + {0x47, 0x7, 0}, + {0x48, 0x33, 0}, + {0x49, 0x5, 0}, + {0x4A, 0x77, 0}, + {0x4B, 0x66, 0}, + {0x4C, 0x66, 0}, + {0x4D, 0, 0}, + {0x4E, 0x4, 0}, + {0x4F, 0xc, 0}, + {0x50, 0, 0}, + {0x51, 0x70, 1}, + {0x56, 0x7, 0}, + {0x57, 0, 0}, + {0x58, 0, 0}, + {0x59, 0x88, 1}, + {0x5A, 0, 0}, + {0x5B, 0x1f, 0}, + {0x5C, 0x20, 1}, + {0x5D, 0x1, 0}, + {0x5E, 0x30, 0}, + {0x5F, 0x70, 0}, + {0x60, 0, 0}, + {0x61, 0, 0}, + {0x62, 0x33, 1}, + {0x63, 0xf, 1}, + {0x64, 0x13, 1}, + {0x65, 0, 0}, + {0x66, 0xee, 1}, + {0x69, 0, 0}, + {0x6A, 0x7e, 0}, + {0x6B, 0x3f, 0}, + {0x6C, 0x7f, 0}, + {0x6D, 0x78, 0}, + {0x6E, 0x58, 1}, + {0x6F, 0x88, 0}, + {0x70, 0x8, 0}, + {0x71, 0xf, 0}, + {0x72, 0xbc, 0}, + {0x73, 0x8, 0}, + {0x74, 0x60, 0}, + {0x75, 0x13, 1}, + {0x76, 0x70, 0}, + {0x77, 0, 0}, + {0x78, 0, 0}, + {0x79, 0, 0}, + {0x7A, 0x33, 0}, + {0x7B, 0x13, 1}, + {0x7C, 0x14, 1}, + {0x7D, 0xee, 1}, + {0x80, 0x3c, 0}, + {0x81, 0x1, 1}, + {0x82, 0xa, 0}, + {0x83, 0x9d, 0}, + {0x84, 0xa, 0}, + {0x85, 0, 0}, + {0x86, 0x40, 0}, + {0x87, 0x40, 0}, + {0x88, 0x88, 0}, + {0x89, 0x10, 0}, + {0x8A, 0xf0, 0}, + {0x8B, 0x10, 0}, + {0x8C, 0xf0, 0}, + {0x8D, 0, 0}, + {0x8E, 0, 0}, + {0x8F, 0x10, 0}, + {0x90, 0x55, 0}, + {0x91, 0x3f, 1}, + {0x92, 0x36, 1}, + {0x93, 0, 0}, + {0x94, 0, 0}, + {0x95, 0, 0}, + {0x96, 0x87, 0}, + {0x97, 0x11, 0}, + {0x98, 0, 0}, + {0x99, 0x33, 0}, + {0x9A, 0x88, 0}, + {0x9B, 0, 0}, + {0x9C, 0x87, 0}, + {0x9D, 0x11, 0}, + {0x9E, 0, 0}, + {0x9F, 0x33, 0}, + {0xA0, 0x88, 0}, + {0xA1, 0x20, 1}, + {0xA2, 0x3f, 0}, + {0xA3, 0x44, 0}, + {0xA4, 0x8c, 0}, + {0xA5, 0x6c, 0}, + {0xA6, 0x22, 0}, + {0xA7, 0xbe, 0}, + {0xA8, 0x55, 0}, + {0xAA, 0xc, 0}, + {0xAB, 0xaa, 0}, + {0xAC, 0x2, 0}, + {0xAD, 0, 0}, + {0xAE, 0x10, 0}, + {0xAF, 0x1, 0}, + {0xB0, 0, 0}, + {0xB1, 0, 0}, + {0xB2, 0x80, 0}, + {0xB3, 0x60, 0}, + {0xB4, 0x44, 0}, + {0xB5, 0x55, 0}, + {0xB6, 0x1, 0}, + {0xB7, 0x55, 0}, + {0xB8, 0x1, 0}, + {0xB9, 0x5, 0}, + {0xBA, 0x55, 0}, + {0xBB, 0x55, 0}, + {0xC1, 0, 0}, + {0xC2, 0, 0}, + {0xC3, 0, 0}, + {0xC4, 0, 0}, + {0xC5, 0, 0}, + {0xC6, 0, 0}, + {0xC7, 0, 0}, + {0xC8, 0, 0}, + {0xC9, 0, 0}, + {0xCA, 0, 0}, + {0xCB, 0, 0}, + {0xCC, 0, 0}, + {0xCD, 0, 0}, + {0xCE, 0x5e, 0}, + {0xCF, 0xc, 0}, + {0xD0, 0xc, 0}, + {0xD1, 0xc, 0}, + {0xD2, 0, 0}, + {0xD3, 0x2b, 0}, + {0xD4, 0xc, 0}, + {0xD5, 0, 0}, + {0xD6, 0x70, 1}, + {0xDB, 0x7, 0}, + {0xDC, 0, 0}, + {0xDD, 0, 0}, + {0xDE, 0x88, 1}, + {0xDF, 0, 0}, + {0xE0, 0x1f, 0}, + {0xE1, 0x20, 1}, + {0xE2, 0x1, 0}, + {0xE3, 0x30, 0}, + {0xE4, 0x70, 0}, + {0xE5, 0, 0}, + {0xE6, 0, 0}, + {0xE7, 0x33, 0}, + {0xE8, 0xf, 1}, + {0xE9, 0x13, 1}, + {0xEA, 0, 0}, + {0xEB, 0xee, 1}, + {0xEE, 0, 0}, + {0xEF, 0x7e, 0}, + {0xF0, 0x3f, 0}, + {0xF1, 0x7f, 0}, + {0xF2, 0x78, 0}, + {0xF3, 0x58, 1}, + {0xF4, 0x88, 0}, + {0xF5, 0x8, 0}, + {0xF6, 0xf, 0}, + {0xF7, 0xbc, 0}, + {0xF8, 0x8, 0}, + {0xF9, 0x60, 0}, + {0xFA, 0x13, 1}, + {0xFB, 0x70, 0}, + {0xFC, 0, 0}, + {0xFD, 0, 0}, + {0xFE, 0, 0}, + {0xFF, 0x33, 0}, + {0x100, 0x13, 1}, + {0x101, 0x14, 1}, + {0x102, 0xee, 1}, + {0x105, 0x3c, 0}, + {0x106, 0x1, 1}, + {0x107, 0xa, 0}, + {0x108, 0x9d, 0}, + {0x109, 0xa, 0}, + {0x10A, 0, 0}, + {0x10B, 0x40, 0}, + {0x10C, 0x40, 0}, + {0x10D, 0x88, 0}, + {0x10E, 0x10, 0}, + {0x10F, 0xf0, 0}, + {0x110, 0x10, 0}, + {0x111, 0xf0, 0}, + {0x112, 0, 0}, + {0x113, 0, 0}, + {0x114, 0x10, 0}, + {0x115, 0x55, 0}, + {0x116, 0x3f, 1}, + {0x117, 0x36, 1}, + {0x118, 0, 0}, + {0x119, 0, 0}, + {0x11A, 0, 0}, + {0x11B, 0x87, 0}, + {0x11C, 0x11, 0}, + {0x11D, 0, 0}, + {0x11E, 0x33, 0}, + {0x11F, 0x88, 0}, + {0x120, 0, 0}, + {0x121, 0x87, 0}, + {0x122, 0x11, 0}, + {0x123, 0, 0}, + {0x124, 0x33, 0}, + {0x125, 0x88, 0}, + {0x126, 0x20, 1}, + {0x127, 0x3f, 0}, + {0x128, 0x44, 0}, + {0x129, 0x8c, 0}, + {0x12A, 0x6c, 0}, + {0x12B, 0x22, 0}, + {0x12C, 0xbe, 0}, + {0x12D, 0x55, 0}, + {0x12F, 0xc, 0}, + {0x130, 0xaa, 0}, + {0x131, 0x2, 0}, + {0x132, 0, 0}, + {0x133, 0x10, 0}, + {0x134, 0x1, 0}, + {0x135, 0, 0}, + {0x136, 0, 0}, + {0x137, 0x80, 0}, + {0x138, 0x60, 0}, + {0x139, 0x44, 0}, + {0x13A, 0x55, 0}, + {0x13B, 0x1, 0}, + {0x13C, 0x55, 0}, + {0x13D, 0x1, 0}, + {0x13E, 0x5, 0}, + {0x13F, 0x55, 0}, + {0x140, 0x55, 0}, + {0x146, 0, 0}, + {0x147, 0, 0}, + {0x148, 0, 0}, + {0x149, 0, 0}, + {0x14A, 0, 0}, + {0x14B, 0, 0}, + {0x14C, 0, 0}, + {0x14D, 0, 0}, + {0x14E, 0, 0}, + {0x14F, 0, 0}, + {0x150, 0, 0}, + {0x151, 0, 0}, + {0x154, 0xc, 0}, + {0x155, 0xc, 0}, + {0x156, 0xc, 0}, + {0x157, 0, 0}, + {0x158, 0x2b, 0}, + {0x159, 0x84, 0}, + {0x15A, 0x15, 0}, + {0x15B, 0xf, 0}, + {0x15C, 0, 0}, + {0x15D, 0, 0}, + {0x15E, 0, 1}, + {0x15F, 0, 1}, + {0x160, 0, 1}, + {0x161, 0, 1}, + {0x162, 0, 1}, + {0x163, 0, 1}, + {0x164, 0, 0}, + {0x165, 0, 0}, + {0x166, 0, 0}, + {0x167, 0, 0}, + {0x168, 0, 0}, + {0x169, 0, 0}, + {0x16A, 0, 1}, + {0x16B, 0, 1}, + {0x16C, 0, 1}, + {0x16D, 0, 0}, + {0x170, 0, 0}, + {0x171, 0x77, 0}, + {0x172, 0x77, 0}, + {0x173, 0x77, 0}, + {0x174, 0x77, 0}, + {0x175, 0, 0}, + {0x176, 0x3, 0}, + {0x177, 0x37, 0}, + {0x178, 0x3, 0}, + {0x179, 0, 0}, + {0x17A, 0x21, 0}, + {0x17B, 0x21, 0}, + {0x17C, 0, 0}, + {0x17D, 0xaa, 0}, + {0x17E, 0, 0}, + {0x17F, 0xaa, 0}, + {0x180, 0, 0}, + {0x190, 0, 0}, + {0x191, 0x77, 0}, + {0x192, 0x77, 0}, + {0x193, 0x77, 0}, + {0x194, 0x77, 0}, + {0x195, 0, 0}, + {0x196, 0x3, 0}, + {0x197, 0x37, 0}, + {0x198, 0x3, 0}, + {0x199, 0, 0}, + {0x19A, 0x21, 0}, + {0x19B, 0x21, 0}, + {0x19C, 0, 0}, + {0x19D, 0xaa, 0}, + {0x19E, 0, 0}, + {0x19F, 0xaa, 0}, + {0x1A0, 0, 0}, + {0x1A1, 0x2, 0}, + {0x1A2, 0xf, 0}, + {0x1A3, 0xf, 0}, + {0x1A4, 0, 1}, + {0x1A5, 0, 1}, + {0x1A6, 0, 1}, + {0x1A7, 0x2, 0}, + {0x1A8, 0xf, 0}, + {0x1A9, 0xf, 0}, + {0x1AA, 0, 1}, + {0x1AB, 0, 1}, + {0x1AC, 0, 1}, + {0x1AD, 0x84, 0}, + {0x1AE, 0x60, 0}, + {0x1AF, 0x47, 0}, + {0x1B0, 0x47, 0}, + {0x1B1, 0, 0}, + {0x1B2, 0, 0}, + {0x1B3, 0, 0}, + {0x1B4, 0, 0}, + {0x1B5, 0, 0}, + {0x1B6, 0, 0}, + {0x1B7, 0x5, 1}, + {0x1B8, 0, 0}, + {0x1B9, 0, 0}, + {0x1BA, 0, 0}, + {0x1BB, 0, 0}, + {0x1BC, 0, 0}, + {0x1BD, 0, 0}, + {0x1BE, 0, 0}, + {0x1BF, 0, 0}, + {0x1C0, 0, 0}, + {0x1C1, 0, 0}, + {0x1C2, 0xa0, 1}, + {0x1C3, 0, 0}, + {0x1C4, 0, 0}, + {0x1C5, 0, 0}, + {0x1C6, 0, 0}, + {0x1C7, 0, 0}, + {0x1C8, 0, 0}, + {0x1C9, 0, 0}, + {0x1CA, 0, 0}, + {0xFFFF, 0, 0} +}; + +static struct radio_20xx_regs regs_2057_rev8[] = { + {0x00, 0x8, 1}, + {0x01, 0x57, 1}, + {0x02, 0x20, 1}, + {0x03, 0x1f, 0}, + {0x04, 0x4, 0}, + {0x05, 0x2, 0}, + {0x06, 0x1, 0}, + {0x07, 0x1, 0}, + {0x08, 0x1, 0}, + {0x09, 0x69, 0}, + {0x0A, 0x66, 0}, + {0x0B, 0x6, 0}, + {0x0C, 0x18, 0}, + {0x0D, 0x3, 0}, + {0x0E, 0x20, 0}, + {0x0F, 0x20, 0}, + {0x10, 0, 0}, + {0x11, 0x7c, 0}, + {0x12, 0x42, 0}, + {0x13, 0xbd, 0}, + {0x14, 0x7, 0}, + {0x15, 0x87, 0}, + {0x16, 0x8, 0}, + {0x17, 0x17, 0}, + {0x18, 0x7, 0}, + {0x19, 0, 0}, + {0x1A, 0x2, 0}, + {0x1B, 0x13, 0}, + {0x1C, 0x3e, 0}, + {0x1D, 0x3e, 0}, + {0x1E, 0x96, 0}, + {0x1F, 0x4, 0}, + {0x20, 0, 0}, + {0x21, 0, 0}, + {0x22, 0x17, 0}, + {0x23, 0x6, 0}, + {0x24, 0x1, 0}, + {0x25, 0x6, 0}, + {0x26, 0x4, 0}, + {0x27, 0xd, 0}, + {0x28, 0xd, 0}, + {0x29, 0x30, 0}, + {0x2A, 0x32, 0}, + {0x2B, 0x8, 0}, + {0x2C, 0x1c, 0}, + {0x2D, 0x2, 0}, + {0x2E, 0x4, 0}, + {0x2F, 0x7f, 0}, + {0x30, 0x27, 0}, + {0x31, 0, 1}, + {0x32, 0, 1}, + {0x33, 0, 1}, + {0x34, 0, 0}, + {0x35, 0x20, 0}, + {0x36, 0x18, 0}, + {0x37, 0x7, 0}, + {0x38, 0x66, 0}, + {0x39, 0x66, 0}, + {0x3A, 0x66, 0}, + {0x3B, 0x66, 0}, + {0x3C, 0xff, 0}, + {0x3D, 0xff, 0}, + {0x3E, 0xff, 0}, + {0x3F, 0xff, 0}, + {0x40, 0x16, 0}, + {0x41, 0x7, 0}, + {0x42, 0x19, 0}, + {0x43, 0x7, 0}, + {0x44, 0x6, 0}, + {0x45, 0x3, 0}, + {0x46, 0x1, 0}, + {0x47, 0x7, 0}, + {0x48, 0x33, 0}, + {0x49, 0x5, 0}, + {0x4A, 0x77, 0}, + {0x4B, 0x66, 0}, + {0x4C, 0x66, 0}, + {0x4D, 0, 0}, + {0x4E, 0x4, 0}, + {0x4F, 0xc, 0}, + {0x50, 0, 0}, + {0x51, 0x70, 1}, + {0x56, 0x7, 0}, + {0x57, 0, 0}, + {0x58, 0, 0}, + {0x59, 0x88, 1}, + {0x5A, 0, 0}, + {0x5B, 0x1f, 0}, + {0x5C, 0x20, 1}, + {0x5D, 0x1, 0}, + {0x5E, 0x30, 0}, + {0x5F, 0x70, 0}, + {0x60, 0, 0}, + {0x61, 0, 0}, + {0x62, 0x33, 1}, + {0x63, 0xf, 1}, + {0x64, 0xf, 1}, + {0x65, 0, 0}, + {0x66, 0x11, 0}, + {0x69, 0, 0}, + {0x6A, 0x7e, 0}, + {0x6B, 0x3f, 0}, + {0x6C, 0x7f, 0}, + {0x6D, 0x78, 0}, + {0x6E, 0x58, 1}, + {0x6F, 0x88, 0}, + {0x70, 0x8, 0}, + {0x71, 0xf, 0}, + {0x72, 0xbc, 0}, + {0x73, 0x8, 0}, + {0x74, 0x60, 0}, + {0x75, 0x13, 1}, + {0x76, 0x70, 0}, + {0x77, 0, 0}, + {0x78, 0, 0}, + {0x79, 0, 0}, + {0x7A, 0x33, 0}, + {0x7B, 0x13, 1}, + {0x7C, 0xf, 1}, + {0x7D, 0xee, 1}, + {0x80, 0x3c, 0}, + {0x81, 0x1, 1}, + {0x82, 0xa, 0}, + {0x83, 0x9d, 0}, + {0x84, 0xa, 0}, + {0x85, 0, 0}, + {0x86, 0x40, 0}, + {0x87, 0x40, 0}, + {0x88, 0x88, 0}, + {0x89, 0x10, 0}, + {0x8A, 0xf0, 0}, + {0x8B, 0x10, 0}, + {0x8C, 0xf0, 0}, + {0x8D, 0, 0}, + {0x8E, 0, 0}, + {0x8F, 0x10, 0}, + {0x90, 0x55, 0}, + {0x91, 0x3f, 1}, + {0x92, 0x36, 1}, + {0x93, 0, 0}, + {0x94, 0, 0}, + {0x95, 0, 0}, + {0x96, 0x87, 0}, + {0x97, 0x11, 0}, + {0x98, 0, 0}, + {0x99, 0x33, 0}, + {0x9A, 0x88, 0}, + {0x9B, 0, 0}, + {0x9C, 0x87, 0}, + {0x9D, 0x11, 0}, + {0x9E, 0, 0}, + {0x9F, 0x33, 0}, + {0xA0, 0x88, 0}, + {0xA1, 0x20, 1}, + {0xA2, 0x3f, 0}, + {0xA3, 0x44, 0}, + {0xA4, 0x8c, 0}, + {0xA5, 0x6c, 0}, + {0xA6, 0x22, 0}, + {0xA7, 0xbe, 0}, + {0xA8, 0x55, 0}, + {0xAA, 0xc, 0}, + {0xAB, 0xaa, 0}, + {0xAC, 0x2, 0}, + {0xAD, 0, 0}, + {0xAE, 0x10, 0}, + {0xAF, 0x1, 0}, + {0xB0, 0, 0}, + {0xB1, 0, 0}, + {0xB2, 0x80, 0}, + {0xB3, 0x60, 0}, + {0xB4, 0x44, 0}, + {0xB5, 0x55, 0}, + {0xB6, 0x1, 0}, + {0xB7, 0x55, 0}, + {0xB8, 0x1, 0}, + {0xB9, 0x5, 0}, + {0xBA, 0x55, 0}, + {0xBB, 0x55, 0}, + {0xC1, 0, 0}, + {0xC2, 0, 0}, + {0xC3, 0, 0}, + {0xC4, 0, 0}, + {0xC5, 0, 0}, + {0xC6, 0, 0}, + {0xC7, 0, 0}, + {0xC8, 0, 0}, + {0xC9, 0x1, 1}, + {0xCA, 0, 0}, + {0xCB, 0, 0}, + {0xCC, 0, 0}, + {0xCD, 0, 0}, + {0xCE, 0x5e, 0}, + {0xCF, 0xc, 0}, + {0xD0, 0xc, 0}, + {0xD1, 0xc, 0}, + {0xD2, 0, 0}, + {0xD3, 0x2b, 0}, + {0xD4, 0xc, 0}, + {0xD5, 0, 0}, + {0xD6, 0x70, 1}, + {0xDB, 0x7, 0}, + {0xDC, 0, 0}, + {0xDD, 0, 0}, + {0xDE, 0x88, 1}, + {0xDF, 0, 0}, + {0xE0, 0x1f, 0}, + {0xE1, 0x20, 1}, + {0xE2, 0x1, 0}, + {0xE3, 0x30, 0}, + {0xE4, 0x70, 0}, + {0xE5, 0, 0}, + {0xE6, 0, 0}, + {0xE7, 0x33, 0}, + {0xE8, 0xf, 1}, + {0xE9, 0xf, 1}, + {0xEA, 0, 0}, + {0xEB, 0x11, 0}, + {0xEE, 0, 0}, + {0xEF, 0x7e, 0}, + {0xF0, 0x3f, 0}, + {0xF1, 0x7f, 0}, + {0xF2, 0x78, 0}, + {0xF3, 0x58, 1}, + {0xF4, 0x88, 0}, + {0xF5, 0x8, 0}, + {0xF6, 0xf, 0}, + {0xF7, 0xbc, 0}, + {0xF8, 0x8, 0}, + {0xF9, 0x60, 0}, + {0xFA, 0x13, 1}, + {0xFB, 0x70, 0}, + {0xFC, 0, 0}, + {0xFD, 0, 0}, + {0xFE, 0, 0}, + {0xFF, 0x33, 0}, + {0x100, 0x13, 1}, + {0x101, 0xf, 1}, + {0x102, 0xee, 1}, + {0x105, 0x3c, 0}, + {0x106, 0x1, 1}, + {0x107, 0xa, 0}, + {0x108, 0x9d, 0}, + {0x109, 0xa, 0}, + {0x10A, 0, 0}, + {0x10B, 0x40, 0}, + {0x10C, 0x40, 0}, + {0x10D, 0x88, 0}, + {0x10E, 0x10, 0}, + {0x10F, 0xf0, 0}, + {0x110, 0x10, 0}, + {0x111, 0xf0, 0}, + {0x112, 0, 0}, + {0x113, 0, 0}, + {0x114, 0x10, 0}, + {0x115, 0x55, 0}, + {0x116, 0x3f, 1}, + {0x117, 0x36, 1}, + {0x118, 0, 0}, + {0x119, 0, 0}, + {0x11A, 0, 0}, + {0x11B, 0x87, 0}, + {0x11C, 0x11, 0}, + {0x11D, 0, 0}, + {0x11E, 0x33, 0}, + {0x11F, 0x88, 0}, + {0x120, 0, 0}, + {0x121, 0x87, 0}, + {0x122, 0x11, 0}, + {0x123, 0, 0}, + {0x124, 0x33, 0}, + {0x125, 0x88, 0}, + {0x126, 0x20, 1}, + {0x127, 0x3f, 0}, + {0x128, 0x44, 0}, + {0x129, 0x8c, 0}, + {0x12A, 0x6c, 0}, + {0x12B, 0x22, 0}, + {0x12C, 0xbe, 0}, + {0x12D, 0x55, 0}, + {0x12F, 0xc, 0}, + {0x130, 0xaa, 0}, + {0x131, 0x2, 0}, + {0x132, 0, 0}, + {0x133, 0x10, 0}, + {0x134, 0x1, 0}, + {0x135, 0, 0}, + {0x136, 0, 0}, + {0x137, 0x80, 0}, + {0x138, 0x60, 0}, + {0x139, 0x44, 0}, + {0x13A, 0x55, 0}, + {0x13B, 0x1, 0}, + {0x13C, 0x55, 0}, + {0x13D, 0x1, 0}, + {0x13E, 0x5, 0}, + {0x13F, 0x55, 0}, + {0x140, 0x55, 0}, + {0x146, 0, 0}, + {0x147, 0, 0}, + {0x148, 0, 0}, + {0x149, 0, 0}, + {0x14A, 0, 0}, + {0x14B, 0, 0}, + {0x14C, 0, 0}, + {0x14D, 0, 0}, + {0x14E, 0x1, 1}, + {0x14F, 0, 0}, + {0x150, 0, 0}, + {0x151, 0, 0}, + {0x154, 0xc, 0}, + {0x155, 0xc, 0}, + {0x156, 0xc, 0}, + {0x157, 0, 0}, + {0x158, 0x2b, 0}, + {0x159, 0x84, 0}, + {0x15A, 0x15, 0}, + {0x15B, 0xf, 0}, + {0x15C, 0, 0}, + {0x15D, 0, 0}, + {0x15E, 0, 1}, + {0x15F, 0, 1}, + {0x160, 0, 1}, + {0x161, 0, 1}, + {0x162, 0, 1}, + {0x163, 0, 1}, + {0x164, 0, 0}, + {0x165, 0, 0}, + {0x166, 0, 0}, + {0x167, 0, 0}, + {0x168, 0, 0}, + {0x169, 0, 0}, + {0x16A, 0, 1}, + {0x16B, 0, 1}, + {0x16C, 0, 1}, + {0x16D, 0, 0}, + {0x170, 0, 0}, + {0x171, 0x77, 0}, + {0x172, 0x77, 0}, + {0x173, 0x77, 0}, + {0x174, 0x77, 0}, + {0x175, 0, 0}, + {0x176, 0x3, 0}, + {0x177, 0x37, 0}, + {0x178, 0x3, 0}, + {0x179, 0, 0}, + {0x17A, 0x21, 0}, + {0x17B, 0x21, 0}, + {0x17C, 0, 0}, + {0x17D, 0xaa, 0}, + {0x17E, 0, 0}, + {0x17F, 0xaa, 0}, + {0x180, 0, 0}, + {0x190, 0, 0}, + {0x191, 0x77, 0}, + {0x192, 0x77, 0}, + {0x193, 0x77, 0}, + {0x194, 0x77, 0}, + {0x195, 0, 0}, + {0x196, 0x3, 0}, + {0x197, 0x37, 0}, + {0x198, 0x3, 0}, + {0x199, 0, 0}, + {0x19A, 0x21, 0}, + {0x19B, 0x21, 0}, + {0x19C, 0, 0}, + {0x19D, 0xaa, 0}, + {0x19E, 0, 0}, + {0x19F, 0xaa, 0}, + {0x1A0, 0, 0}, + {0x1A1, 0x2, 0}, + {0x1A2, 0xf, 0}, + {0x1A3, 0xf, 0}, + {0x1A4, 0, 1}, + {0x1A5, 0, 1}, + {0x1A6, 0, 1}, + {0x1A7, 0x2, 0}, + {0x1A8, 0xf, 0}, + {0x1A9, 0xf, 0}, + {0x1AA, 0, 1}, + {0x1AB, 0, 1}, + {0x1AC, 0, 1}, + {0x1AD, 0x84, 0}, + {0x1AE, 0x60, 0}, + {0x1AF, 0x47, 0}, + {0x1B0, 0x47, 0}, + {0x1B1, 0, 0}, + {0x1B2, 0, 0}, + {0x1B3, 0, 0}, + {0x1B4, 0, 0}, + {0x1B5, 0, 0}, + {0x1B6, 0, 0}, + {0x1B7, 0x5, 1}, + {0x1B8, 0, 0}, + {0x1B9, 0, 0}, + {0x1BA, 0, 0}, + {0x1BB, 0, 0}, + {0x1BC, 0, 0}, + {0x1BD, 0, 0}, + {0x1BE, 0, 0}, + {0x1BF, 0, 0}, + {0x1C0, 0, 0}, + {0x1C1, 0, 0}, + {0x1C2, 0xa0, 1}, + {0x1C3, 0, 0}, + {0x1C4, 0, 0}, + {0x1C5, 0, 0}, + {0x1C6, 0, 0}, + {0x1C7, 0, 0}, + {0x1C8, 0, 0}, + {0x1C9, 0, 0}, + {0x1CA, 0, 0}, + {0xFFFF, 0, 0} +}; + +static s16 nphy_def_lnagains[] = { -2, 10, 19, 25 }; + +static s32 nphy_lnagain_est0[] = { -315, 40370 }; +static s32 nphy_lnagain_est1[] = { -224, 23242 }; + +static const u16 tbl_iqcal_gainparams_nphy[2][NPHY_IQCAL_NUMGAINS][8] = { + { + {0x000, 0, 0, 2, 0x69, 0x69, 0x69, 0x69}, + {0x700, 7, 0, 0, 0x69, 0x69, 0x69, 0x69}, + {0x710, 7, 1, 0, 0x68, 0x68, 0x68, 0x68}, + {0x720, 7, 2, 0, 0x67, 0x67, 0x67, 0x67}, + {0x730, 7, 3, 0, 0x66, 0x66, 0x66, 0x66}, + {0x740, 7, 4, 0, 0x65, 0x65, 0x65, 0x65}, + {0x741, 7, 4, 1, 0x65, 0x65, 0x65, 0x65}, + {0x742, 7, 4, 2, 0x65, 0x65, 0x65, 0x65}, + {0x743, 7, 4, 3, 0x65, 0x65, 0x65, 0x65} + }, + { + {0x000, 7, 0, 0, 0x79, 0x79, 0x79, 0x79}, + {0x700, 7, 0, 0, 0x79, 0x79, 0x79, 0x79}, + {0x710, 7, 1, 0, 0x79, 0x79, 0x79, 0x79}, + {0x720, 7, 2, 0, 0x78, 0x78, 0x78, 0x78}, + {0x730, 7, 3, 0, 0x78, 0x78, 0x78, 0x78}, + {0x740, 7, 4, 0, 0x78, 0x78, 0x78, 0x78}, + {0x741, 7, 4, 1, 0x78, 0x78, 0x78, 0x78}, + {0x742, 7, 4, 2, 0x78, 0x78, 0x78, 0x78}, + {0x743, 7, 4, 3, 0x78, 0x78, 0x78, 0x78} + } +}; + +static const u32 nphy_tpc_txgain[] = { + 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, + 0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44, + 0x03c82a42, 0x03c82944, 0x03c82942, 0x03c82844, + 0x03c82842, 0x03c42b44, 0x03c42b42, 0x03c42a44, + 0x03c42a42, 0x03c42944, 0x03c42942, 0x03c42844, + 0x03c42842, 0x03c42744, 0x03c42742, 0x03c42644, + 0x03c42642, 0x03c42544, 0x03c42542, 0x03c42444, + 0x03c42442, 0x03c02b44, 0x03c02b42, 0x03c02a44, + 0x03c02a42, 0x03c02944, 0x03c02942, 0x03c02844, + 0x03c02842, 0x03c02744, 0x03c02742, 0x03b02b44, + 0x03b02b42, 0x03b02a44, 0x03b02a42, 0x03b02944, + 0x03b02942, 0x03b02844, 0x03b02842, 0x03b02744, + 0x03b02742, 0x03b02644, 0x03b02642, 0x03b02544, + 0x03b02542, 0x03a02b44, 0x03a02b42, 0x03a02a44, + 0x03a02a42, 0x03a02944, 0x03a02942, 0x03a02844, + 0x03a02842, 0x03a02744, 0x03a02742, 0x03902b44, + 0x03902b42, 0x03902a44, 0x03902a42, 0x03902944, + 0x03902942, 0x03902844, 0x03902842, 0x03902744, + 0x03902742, 0x03902644, 0x03902642, 0x03902544, + 0x03902542, 0x03802b44, 0x03802b42, 0x03802a44, + 0x03802a42, 0x03802944, 0x03802942, 0x03802844, + 0x03802842, 0x03802744, 0x03802742, 0x03802644, + 0x03802642, 0x03802544, 0x03802542, 0x03802444, + 0x03802442, 0x03802344, 0x03802342, 0x03802244, + 0x03802242, 0x03802144, 0x03802142, 0x03802044, + 0x03802042, 0x03801f44, 0x03801f42, 0x03801e44, + 0x03801e42, 0x03801d44, 0x03801d42, 0x03801c44, + 0x03801c42, 0x03801b44, 0x03801b42, 0x03801a44, + 0x03801a42, 0x03801944, 0x03801942, 0x03801844, + 0x03801842, 0x03801744, 0x03801742, 0x03801644, + 0x03801642, 0x03801544, 0x03801542, 0x03801444, + 0x03801442, 0x03801344, 0x03801342, 0x00002b00 +}; + +static const u16 nphy_tpc_loscale[] = { + 256, 256, 271, 271, 287, 256, 256, 271, + 271, 287, 287, 304, 304, 256, 256, 271, + 271, 287, 287, 304, 304, 322, 322, 341, + 341, 362, 362, 383, 383, 256, 256, 271, + 271, 287, 287, 304, 304, 322, 322, 256, + 256, 271, 271, 287, 287, 304, 304, 322, + 322, 341, 341, 362, 362, 256, 256, 271, + 271, 287, 287, 304, 304, 322, 322, 256, + 256, 271, 271, 287, 287, 304, 304, 322, + 322, 341, 341, 362, 362, 256, 256, 271, + 271, 287, 287, 304, 304, 322, 322, 341, + 341, 362, 362, 383, 383, 406, 406, 430, + 430, 455, 455, 482, 482, 511, 511, 541, + 541, 573, 573, 607, 607, 643, 643, 681, + 681, 722, 722, 764, 764, 810, 810, 858, + 858, 908, 908, 962, 962, 1019, 1019, 256 +}; + +static u32 nphy_tpc_txgain_ipa[] = { + 0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029, + 0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025, + 0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029, + 0x5ef70028, 0x5ef70027, 0x5ef70026, 0x5ef70025, + 0x5df7002d, 0x5df7002b, 0x5df7002a, 0x5df70029, + 0x5df70028, 0x5df70027, 0x5df70026, 0x5df70025, + 0x5cf7002d, 0x5cf7002b, 0x5cf7002a, 0x5cf70029, + 0x5cf70028, 0x5cf70027, 0x5cf70026, 0x5cf70025, + 0x5bf7002d, 0x5bf7002b, 0x5bf7002a, 0x5bf70029, + 0x5bf70028, 0x5bf70027, 0x5bf70026, 0x5bf70025, + 0x5af7002d, 0x5af7002b, 0x5af7002a, 0x5af70029, + 0x5af70028, 0x5af70027, 0x5af70026, 0x5af70025, + 0x59f7002d, 0x59f7002b, 0x59f7002a, 0x59f70029, + 0x59f70028, 0x59f70027, 0x59f70026, 0x59f70025, + 0x58f7002d, 0x58f7002b, 0x58f7002a, 0x58f70029, + 0x58f70028, 0x58f70027, 0x58f70026, 0x58f70025, + 0x57f7002d, 0x57f7002b, 0x57f7002a, 0x57f70029, + 0x57f70028, 0x57f70027, 0x57f70026, 0x57f70025, + 0x56f7002d, 0x56f7002b, 0x56f7002a, 0x56f70029, + 0x56f70028, 0x56f70027, 0x56f70026, 0x56f70025, + 0x55f7002d, 0x55f7002b, 0x55f7002a, 0x55f70029, + 0x55f70028, 0x55f70027, 0x55f70026, 0x55f70025, + 0x54f7002d, 0x54f7002b, 0x54f7002a, 0x54f70029, + 0x54f70028, 0x54f70027, 0x54f70026, 0x54f70025, + 0x53f7002d, 0x53f7002b, 0x53f7002a, 0x53f70029, + 0x53f70028, 0x53f70027, 0x53f70026, 0x53f70025, + 0x52f7002d, 0x52f7002b, 0x52f7002a, 0x52f70029, + 0x52f70028, 0x52f70027, 0x52f70026, 0x52f70025, + 0x51f7002d, 0x51f7002b, 0x51f7002a, 0x51f70029, + 0x51f70028, 0x51f70027, 0x51f70026, 0x51f70025, + 0x50f7002d, 0x50f7002b, 0x50f7002a, 0x50f70029, + 0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025 +}; + +static u32 nphy_tpc_txgain_ipa_rev5[] = { + 0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029, + 0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025, + 0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029, + 0x1ef70028, 0x1ef70027, 0x1ef70026, 0x1ef70025, + 0x1df7002d, 0x1df7002b, 0x1df7002a, 0x1df70029, + 0x1df70028, 0x1df70027, 0x1df70026, 0x1df70025, + 0x1cf7002d, 0x1cf7002b, 0x1cf7002a, 0x1cf70029, + 0x1cf70028, 0x1cf70027, 0x1cf70026, 0x1cf70025, + 0x1bf7002d, 0x1bf7002b, 0x1bf7002a, 0x1bf70029, + 0x1bf70028, 0x1bf70027, 0x1bf70026, 0x1bf70025, + 0x1af7002d, 0x1af7002b, 0x1af7002a, 0x1af70029, + 0x1af70028, 0x1af70027, 0x1af70026, 0x1af70025, + 0x19f7002d, 0x19f7002b, 0x19f7002a, 0x19f70029, + 0x19f70028, 0x19f70027, 0x19f70026, 0x19f70025, + 0x18f7002d, 0x18f7002b, 0x18f7002a, 0x18f70029, + 0x18f70028, 0x18f70027, 0x18f70026, 0x18f70025, + 0x17f7002d, 0x17f7002b, 0x17f7002a, 0x17f70029, + 0x17f70028, 0x17f70027, 0x17f70026, 0x17f70025, + 0x16f7002d, 0x16f7002b, 0x16f7002a, 0x16f70029, + 0x16f70028, 0x16f70027, 0x16f70026, 0x16f70025, + 0x15f7002d, 0x15f7002b, 0x15f7002a, 0x15f70029, + 0x15f70028, 0x15f70027, 0x15f70026, 0x15f70025, + 0x14f7002d, 0x14f7002b, 0x14f7002a, 0x14f70029, + 0x14f70028, 0x14f70027, 0x14f70026, 0x14f70025, + 0x13f7002d, 0x13f7002b, 0x13f7002a, 0x13f70029, + 0x13f70028, 0x13f70027, 0x13f70026, 0x13f70025, + 0x12f7002d, 0x12f7002b, 0x12f7002a, 0x12f70029, + 0x12f70028, 0x12f70027, 0x12f70026, 0x12f70025, + 0x11f7002d, 0x11f7002b, 0x11f7002a, 0x11f70029, + 0x11f70028, 0x11f70027, 0x11f70026, 0x11f70025, + 0x10f7002d, 0x10f7002b, 0x10f7002a, 0x10f70029, + 0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025 +}; + +static u32 nphy_tpc_txgain_ipa_rev6[] = { + 0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029, + 0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025, + 0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029, + 0x0ef70028, 0x0ef70027, 0x0ef70026, 0x0ef70025, + 0x0df7002d, 0x0df7002b, 0x0df7002a, 0x0df70029, + 0x0df70028, 0x0df70027, 0x0df70026, 0x0df70025, + 0x0cf7002d, 0x0cf7002b, 0x0cf7002a, 0x0cf70029, + 0x0cf70028, 0x0cf70027, 0x0cf70026, 0x0cf70025, + 0x0bf7002d, 0x0bf7002b, 0x0bf7002a, 0x0bf70029, + 0x0bf70028, 0x0bf70027, 0x0bf70026, 0x0bf70025, + 0x0af7002d, 0x0af7002b, 0x0af7002a, 0x0af70029, + 0x0af70028, 0x0af70027, 0x0af70026, 0x0af70025, + 0x09f7002d, 0x09f7002b, 0x09f7002a, 0x09f70029, + 0x09f70028, 0x09f70027, 0x09f70026, 0x09f70025, + 0x08f7002d, 0x08f7002b, 0x08f7002a, 0x08f70029, + 0x08f70028, 0x08f70027, 0x08f70026, 0x08f70025, + 0x07f7002d, 0x07f7002b, 0x07f7002a, 0x07f70029, + 0x07f70028, 0x07f70027, 0x07f70026, 0x07f70025, + 0x06f7002d, 0x06f7002b, 0x06f7002a, 0x06f70029, + 0x06f70028, 0x06f70027, 0x06f70026, 0x06f70025, + 0x05f7002d, 0x05f7002b, 0x05f7002a, 0x05f70029, + 0x05f70028, 0x05f70027, 0x05f70026, 0x05f70025, + 0x04f7002d, 0x04f7002b, 0x04f7002a, 0x04f70029, + 0x04f70028, 0x04f70027, 0x04f70026, 0x04f70025, + 0x03f7002d, 0x03f7002b, 0x03f7002a, 0x03f70029, + 0x03f70028, 0x03f70027, 0x03f70026, 0x03f70025, + 0x02f7002d, 0x02f7002b, 0x02f7002a, 0x02f70029, + 0x02f70028, 0x02f70027, 0x02f70026, 0x02f70025, + 0x01f7002d, 0x01f7002b, 0x01f7002a, 0x01f70029, + 0x01f70028, 0x01f70027, 0x01f70026, 0x01f70025, + 0x00f7002d, 0x00f7002b, 0x00f7002a, 0x00f70029, + 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025 +}; + +static u32 nphy_tpc_txgain_ipa_2g_2057rev3[] = { + 0x70ff0040, 0x70f7003e, 0x70ef003b, 0x70e70039, + 0x70df0037, 0x70d70036, 0x70cf0033, 0x70c70032, + 0x70bf0031, 0x70b7002f, 0x70af002e, 0x70a7002d, + 0x709f002d, 0x7097002c, 0x708f002c, 0x7087002c, + 0x707f002b, 0x7077002c, 0x706f002c, 0x7067002d, + 0x705f002e, 0x705f002b, 0x705f0029, 0x7057002a, + 0x70570028, 0x704f002a, 0x7047002c, 0x7047002a, + 0x70470028, 0x70470026, 0x70470024, 0x70470022, + 0x7047001f, 0x70370027, 0x70370024, 0x70370022, + 0x70370020, 0x7037001f, 0x7037001d, 0x7037001b, + 0x7037001a, 0x70370018, 0x70370017, 0x7027001e, + 0x7027001d, 0x7027001a, 0x701f0024, 0x701f0022, + 0x701f0020, 0x701f001f, 0x701f001d, 0x701f001b, + 0x701f001a, 0x701f0018, 0x701f0017, 0x701f0015, + 0x701f0014, 0x701f0013, 0x701f0012, 0x701f0011, + 0x70170019, 0x70170018, 0x70170016, 0x70170015, + 0x70170014, 0x70170013, 0x70170012, 0x70170010, + 0x70170010, 0x7017000f, 0x700f001d, 0x700f001b, + 0x700f001a, 0x700f0018, 0x700f0017, 0x700f0015, + 0x700f0015, 0x700f0013, 0x700f0013, 0x700f0011, + 0x700f0010, 0x700f0010, 0x700f000f, 0x700f000e, + 0x700f000d, 0x700f000c, 0x700f000b, 0x700f000b, + 0x700f000b, 0x700f000a, 0x700f0009, 0x700f0009, + 0x700f0009, 0x700f0008, 0x700f0007, 0x700f0007, + 0x700f0006, 0x700f0006, 0x700f0006, 0x700f0006, + 0x700f0005, 0x700f0005, 0x700f0005, 0x700f0004, + 0x700f0004, 0x700f0004, 0x700f0004, 0x700f0004, + 0x700f0004, 0x700f0003, 0x700f0003, 0x700f0003, + 0x700f0003, 0x700f0002, 0x700f0002, 0x700f0002, + 0x700f0002, 0x700f0002, 0x700f0002, 0x700f0001, + 0x700f0001, 0x700f0001, 0x700f0001, 0x700f0001, + 0x700f0001, 0x700f0001, 0x700f0001, 0x700f0001 +}; + +static u32 nphy_tpc_txgain_ipa_2g_2057rev4n6[] = { + 0xf0ff0040, 0xf0f7003e, 0xf0ef003b, 0xf0e70039, + 0xf0df0037, 0xf0d70036, 0xf0cf0033, 0xf0c70032, + 0xf0bf0031, 0xf0b7002f, 0xf0af002e, 0xf0a7002d, + 0xf09f002d, 0xf097002c, 0xf08f002c, 0xf087002c, + 0xf07f002b, 0xf077002c, 0xf06f002c, 0xf067002d, + 0xf05f002e, 0xf05f002b, 0xf05f0029, 0xf057002a, + 0xf0570028, 0xf04f002a, 0xf047002c, 0xf047002a, + 0xf0470028, 0xf0470026, 0xf0470024, 0xf0470022, + 0xf047001f, 0xf0370027, 0xf0370024, 0xf0370022, + 0xf0370020, 0xf037001f, 0xf037001d, 0xf037001b, + 0xf037001a, 0xf0370018, 0xf0370017, 0xf027001e, + 0xf027001d, 0xf027001a, 0xf01f0024, 0xf01f0022, + 0xf01f0020, 0xf01f001f, 0xf01f001d, 0xf01f001b, + 0xf01f001a, 0xf01f0018, 0xf01f0017, 0xf01f0015, + 0xf01f0014, 0xf01f0013, 0xf01f0012, 0xf01f0011, + 0xf0170019, 0xf0170018, 0xf0170016, 0xf0170015, + 0xf0170014, 0xf0170013, 0xf0170012, 0xf0170010, + 0xf0170010, 0xf017000f, 0xf00f001d, 0xf00f001b, + 0xf00f001a, 0xf00f0018, 0xf00f0017, 0xf00f0015, + 0xf00f0015, 0xf00f0013, 0xf00f0013, 0xf00f0011, + 0xf00f0010, 0xf00f0010, 0xf00f000f, 0xf00f000e, + 0xf00f000d, 0xf00f000c, 0xf00f000b, 0xf00f000b, + 0xf00f000b, 0xf00f000a, 0xf00f0009, 0xf00f0009, + 0xf00f0009, 0xf00f0008, 0xf00f0007, 0xf00f0007, + 0xf00f0006, 0xf00f0006, 0xf00f0006, 0xf00f0006, + 0xf00f0005, 0xf00f0005, 0xf00f0005, 0xf00f0004, + 0xf00f0004, 0xf00f0004, 0xf00f0004, 0xf00f0004, + 0xf00f0004, 0xf00f0003, 0xf00f0003, 0xf00f0003, + 0xf00f0003, 0xf00f0002, 0xf00f0002, 0xf00f0002, + 0xf00f0002, 0xf00f0002, 0xf00f0002, 0xf00f0001, + 0xf00f0001, 0xf00f0001, 0xf00f0001, 0xf00f0001, + 0xf00f0001, 0xf00f0001, 0xf00f0001, 0xf00f0001 +}; + +static u32 nphy_tpc_txgain_ipa_2g_2057rev5[] = { + 0x30ff0031, 0x30e70031, 0x30e7002e, 0x30cf002e, + 0x30bf002e, 0x30af002e, 0x309f002f, 0x307f0033, + 0x307f0031, 0x307f002e, 0x3077002e, 0x306f002e, + 0x3067002e, 0x305f002f, 0x30570030, 0x3057002d, + 0x304f002e, 0x30470031, 0x3047002e, 0x3047002c, + 0x30470029, 0x303f002c, 0x303f0029, 0x3037002d, + 0x3037002a, 0x30370028, 0x302f002c, 0x302f002a, + 0x302f0028, 0x302f0026, 0x3027002c, 0x30270029, + 0x30270027, 0x30270025, 0x30270023, 0x301f002c, + 0x301f002a, 0x301f0028, 0x301f0025, 0x301f0024, + 0x301f0022, 0x301f001f, 0x3017002d, 0x3017002b, + 0x30170028, 0x30170026, 0x30170024, 0x30170022, + 0x30170020, 0x3017001e, 0x3017001d, 0x3017001b, + 0x3017001a, 0x30170018, 0x30170017, 0x30170015, + 0x300f002c, 0x300f0029, 0x300f0027, 0x300f0024, + 0x300f0022, 0x300f0021, 0x300f001f, 0x300f001d, + 0x300f001b, 0x300f001a, 0x300f0018, 0x300f0017, + 0x300f0016, 0x300f0015, 0x300f0115, 0x300f0215, + 0x300f0315, 0x300f0415, 0x300f0515, 0x300f0615, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715 +}; + +static u32 nphy_tpc_txgain_ipa_2g_2057rev7[] = { + 0x30ff0031, 0x30e70031, 0x30e7002e, 0x30cf002e, + 0x30bf002e, 0x30af002e, 0x309f002f, 0x307f0033, + 0x307f0031, 0x307f002e, 0x3077002e, 0x306f002e, + 0x3067002e, 0x305f002f, 0x30570030, 0x3057002d, + 0x304f002e, 0x30470031, 0x3047002e, 0x3047002c, + 0x30470029, 0x303f002c, 0x303f0029, 0x3037002d, + 0x3037002a, 0x30370028, 0x302f002c, 0x302f002a, + 0x302f0028, 0x302f0026, 0x3027002c, 0x30270029, + 0x30270027, 0x30270025, 0x30270023, 0x301f002c, + 0x301f002a, 0x301f0028, 0x301f0025, 0x301f0024, + 0x301f0022, 0x301f001f, 0x3017002d, 0x3017002b, + 0x30170028, 0x30170026, 0x30170024, 0x30170022, + 0x30170020, 0x3017001e, 0x3017001d, 0x3017001b, + 0x3017001a, 0x30170018, 0x30170017, 0x30170015, + 0x300f002c, 0x300f0029, 0x300f0027, 0x300f0024, + 0x300f0022, 0x300f0021, 0x300f001f, 0x300f001d, + 0x300f001b, 0x300f001a, 0x300f0018, 0x300f0017, + 0x300f0016, 0x300f0015, 0x300f0115, 0x300f0215, + 0x300f0315, 0x300f0415, 0x300f0515, 0x300f0615, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715 +}; + +static u32 nphy_tpc_txgain_ipa_5g[] = { + 0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031, + 0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b, + 0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027, + 0x7ff70026, 0x7ff70024, 0x7ff70023, 0x7ff70022, + 0x7ef70028, 0x7ef70027, 0x7ef70026, 0x7ef70025, + 0x7ef70024, 0x7ef70023, 0x7df70028, 0x7df70027, + 0x7df70026, 0x7df70025, 0x7df70024, 0x7df70023, + 0x7df70022, 0x7cf70029, 0x7cf70028, 0x7cf70027, + 0x7cf70026, 0x7cf70025, 0x7cf70023, 0x7cf70022, + 0x7bf70029, 0x7bf70028, 0x7bf70026, 0x7bf70025, + 0x7bf70024, 0x7bf70023, 0x7bf70022, 0x7bf70021, + 0x7af70029, 0x7af70028, 0x7af70027, 0x7af70026, + 0x7af70025, 0x7af70024, 0x7af70023, 0x7af70022, + 0x79f70029, 0x79f70028, 0x79f70027, 0x79f70026, + 0x79f70025, 0x79f70024, 0x79f70023, 0x79f70022, + 0x78f70029, 0x78f70028, 0x78f70027, 0x78f70026, + 0x78f70025, 0x78f70024, 0x78f70023, 0x78f70022, + 0x77f70029, 0x77f70028, 0x77f70027, 0x77f70026, + 0x77f70025, 0x77f70024, 0x77f70023, 0x77f70022, + 0x76f70029, 0x76f70028, 0x76f70027, 0x76f70026, + 0x76f70024, 0x76f70023, 0x76f70022, 0x76f70021, + 0x75f70029, 0x75f70028, 0x75f70027, 0x75f70026, + 0x75f70025, 0x75f70024, 0x75f70023, 0x74f70029, + 0x74f70028, 0x74f70026, 0x74f70025, 0x74f70024, + 0x74f70023, 0x74f70022, 0x73f70029, 0x73f70027, + 0x73f70026, 0x73f70025, 0x73f70024, 0x73f70023, + 0x73f70022, 0x72f70028, 0x72f70027, 0x72f70026, + 0x72f70025, 0x72f70024, 0x72f70023, 0x72f70022, + 0x71f70028, 0x71f70027, 0x71f70026, 0x71f70025, + 0x71f70024, 0x71f70023, 0x70f70028, 0x70f70027, + 0x70f70026, 0x70f70024, 0x70f70023, 0x70f70022, + 0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f +}; + +static u32 nphy_tpc_txgain_ipa_5g_2057[] = { + 0x7f7f0044, 0x7f7f0040, 0x7f7f003c, 0x7f7f0039, + 0x7f7f0036, 0x7e7f003c, 0x7e7f0038, 0x7e7f0035, + 0x7d7f003c, 0x7d7f0039, 0x7d7f0036, 0x7d7f0033, + 0x7c7f003b, 0x7c7f0037, 0x7c7f0034, 0x7b7f003a, + 0x7b7f0036, 0x7b7f0033, 0x7a7f003c, 0x7a7f0039, + 0x7a7f0036, 0x7a7f0033, 0x797f003b, 0x797f0038, + 0x797f0035, 0x797f0032, 0x787f003b, 0x787f0038, + 0x787f0035, 0x787f0032, 0x777f003a, 0x777f0037, + 0x777f0034, 0x777f0031, 0x767f003a, 0x767f0036, + 0x767f0033, 0x767f0031, 0x757f003a, 0x757f0037, + 0x757f0034, 0x747f003c, 0x747f0039, 0x747f0036, + 0x747f0033, 0x737f003b, 0x737f0038, 0x737f0035, + 0x737f0032, 0x727f0039, 0x727f0036, 0x727f0033, + 0x727f0030, 0x717f003a, 0x717f0037, 0x717f0034, + 0x707f003b, 0x707f0038, 0x707f0035, 0x707f0032, + 0x707f002f, 0x707f002d, 0x707f002a, 0x707f0028, + 0x707f0025, 0x707f0023, 0x707f0021, 0x707f0020, + 0x707f001e, 0x707f001c, 0x707f001b, 0x707f0019, + 0x707f0018, 0x707f0016, 0x707f0015, 0x707f0014, + 0x707f0013, 0x707f0012, 0x707f0011, 0x707f0010, + 0x707f000f, 0x707f000e, 0x707f000d, 0x707f000d, + 0x707f000c, 0x707f000b, 0x707f000b, 0x707f000a, + 0x707f0009, 0x707f0009, 0x707f0008, 0x707f0008, + 0x707f0007, 0x707f0007, 0x707f0007, 0x707f0006, + 0x707f0006, 0x707f0006, 0x707f0005, 0x707f0005, + 0x707f0005, 0x707f0004, 0x707f0004, 0x707f0004, + 0x707f0004, 0x707f0004, 0x707f0003, 0x707f0003, + 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, + 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, + 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, + 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001, + 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001 +}; + +static u32 nphy_tpc_txgain_ipa_5g_2057rev7[] = { + 0x6f7f0031, 0x6f7f002e, 0x6f7f002c, 0x6f7f002a, + 0x6f7f0027, 0x6e7f002e, 0x6e7f002c, 0x6e7f002a, + 0x6d7f0030, 0x6d7f002d, 0x6d7f002a, 0x6d7f0028, + 0x6c7f0030, 0x6c7f002d, 0x6c7f002b, 0x6b7f002e, + 0x6b7f002c, 0x6b7f002a, 0x6b7f0027, 0x6a7f002e, + 0x6a7f002c, 0x6a7f002a, 0x697f0030, 0x697f002e, + 0x697f002b, 0x697f0029, 0x687f002f, 0x687f002d, + 0x687f002a, 0x687f0027, 0x677f002f, 0x677f002d, + 0x677f002a, 0x667f0031, 0x667f002e, 0x667f002c, + 0x667f002a, 0x657f0030, 0x657f002e, 0x657f002b, + 0x657f0029, 0x647f0030, 0x647f002d, 0x647f002b, + 0x647f0029, 0x637f002f, 0x637f002d, 0x637f002a, + 0x627f0030, 0x627f002d, 0x627f002b, 0x627f0029, + 0x617f0030, 0x617f002e, 0x617f002b, 0x617f0029, + 0x607f002f, 0x607f002d, 0x607f002a, 0x607f0027, + 0x607f0026, 0x607f0023, 0x607f0021, 0x607f0020, + 0x607f001e, 0x607f001c, 0x607f001a, 0x607f0019, + 0x607f0018, 0x607f0016, 0x607f0015, 0x607f0014, + 0x607f0012, 0x607f0012, 0x607f0011, 0x607f000f, + 0x607f000f, 0x607f000e, 0x607f000d, 0x607f000c, + 0x607f000c, 0x607f000b, 0x607f000b, 0x607f000a, + 0x607f0009, 0x607f0009, 0x607f0008, 0x607f0008, + 0x607f0008, 0x607f0007, 0x607f0007, 0x607f0006, + 0x607f0006, 0x607f0005, 0x607f0005, 0x607f0005, + 0x607f0005, 0x607f0005, 0x607f0004, 0x607f0004, + 0x607f0004, 0x607f0004, 0x607f0003, 0x607f0003, + 0x607f0003, 0x607f0003, 0x607f0002, 0x607f0002, + 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, + 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, + 0x607f0002, 0x607f0002, 0x607f0002, 0x607f0002, + 0x607f0002, 0x607f0001, 0x607f0001, 0x607f0001, + 0x607f0001, 0x607f0001, 0x607f0001, 0x607f0001 +}; + +static s8 nphy_papd_pga_gain_delta_ipa_2g[] = { + -114, -108, -98, -91, -84, -78, -70, -62, + -54, -46, -39, -31, -23, -15, -8, 0 +}; + +static s8 nphy_papd_pga_gain_delta_ipa_5g[] = { + -100, -95, -89, -83, -77, -70, -63, -56, + -48, -41, -33, -25, -19, -12, -6, 0 +}; + +static s16 nphy_papd_padgain_dlt_2g_2057rev3n4[] = { + -159, -113, -86, -72, -62, -54, -48, -43, + -39, -35, -31, -28, -25, -23, -20, -18, + -17, -15, -13, -11, -10, -8, -7, -6, + -5, -4, -3, -3, -2, -1, -1, 0 +}; + +static s16 nphy_papd_padgain_dlt_2g_2057rev5[] = { + -109, -109, -82, -68, -58, -50, -44, -39, + -35, -31, -28, -26, -23, -21, -19, -17, + -16, -14, -13, -11, -10, -9, -8, -7, + -5, -5, -4, -3, -2, -1, -1, 0 +}; + +static s16 nphy_papd_padgain_dlt_2g_2057rev7[] = { + -122, -122, -95, -80, -69, -61, -54, -49, + -43, -39, -35, -32, -28, -26, -23, -21, + -18, -16, -15, -13, -11, -10, -8, -7, + -6, -5, -4, -3, -2, -1, -1, 0 +}; + +static s8 nphy_papd_pgagain_dlt_5g_2057[] = { + -107, -101, -92, -85, -78, -71, -62, -55, + -47, -39, -32, -24, -19, -12, -6, 0 +}; + +static s8 nphy_papd_pgagain_dlt_5g_2057rev7[] = { + -110, -104, -95, -88, -81, -74, -66, -58, + -50, -44, -36, -28, -23, -15, -8, 0 +}; + +static u8 pad_gain_codes_used_2057rev5[] = { + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 +}; + +static u8 pad_gain_codes_used_2057rev7[] = { + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, + 5, 4, 3, 2, 1 +}; + +static u8 pad_all_gain_codes_2057[] = { + 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, + 1, 0 +}; + +static u8 pga_all_gain_codes_2057[] = { + 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +}; + +static u32 nphy_papd_scaltbl[] = { + 0x0ae2002f, 0x0a3b0032, 0x09a70035, 0x09220038, + 0x0887003c, 0x081f003f, 0x07a20043, 0x07340047, + 0x06d2004b, 0x067a004f, 0x06170054, 0x05bf0059, + 0x0571005e, 0x051e0064, 0x04d3006a, 0x04910070, + 0x044c0077, 0x040f007e, 0x03d90085, 0x03a1008d, + 0x036f0095, 0x033d009e, 0x030b00a8, 0x02e000b2, + 0x02b900bc, 0x029200c7, 0x026d00d3, 0x024900e0, + 0x022900ed, 0x020a00fb, 0x01ec010a, 0x01d0011a, + 0x01b7012a, 0x019e013c, 0x0187014f, 0x01720162, + 0x015d0177, 0x0149018e, 0x013701a5, 0x012601be, + 0x011501d9, 0x010501f5, 0x00f70212, 0x00e90232, + 0x00dc0253, 0x00d00276, 0x00c4029c, 0x00b902c3, + 0x00af02ed, 0x00a5031a, 0x009c0349, 0x0093037a, + 0x008b03af, 0x008303e7, 0x007c0422, 0x00750461, + 0x006e04a3, 0x006804ea, 0x00620534, 0x005d0583, + 0x005805d7, 0x0053062f, 0x004e068d, 0x004a06f1 +}; + +static u32 nphy_tpc_txgain_rev3[] = { + 0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e, + 0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037, + 0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e, + 0x1e41003c, 0x1e41003b, 0x1e410039, 0x1e410037, + 0x1d410044, 0x1d410042, 0x1d410040, 0x1d41003e, + 0x1d41003c, 0x1d41003b, 0x1d410039, 0x1d410037, + 0x1c410044, 0x1c410042, 0x1c410040, 0x1c41003e, + 0x1c41003c, 0x1c41003b, 0x1c410039, 0x1c410037, + 0x1b410044, 0x1b410042, 0x1b410040, 0x1b41003e, + 0x1b41003c, 0x1b41003b, 0x1b410039, 0x1b410037, + 0x1a410044, 0x1a410042, 0x1a410040, 0x1a41003e, + 0x1a41003c, 0x1a41003b, 0x1a410039, 0x1a410037, + 0x19410044, 0x19410042, 0x19410040, 0x1941003e, + 0x1941003c, 0x1941003b, 0x19410039, 0x19410037, + 0x18410044, 0x18410042, 0x18410040, 0x1841003e, + 0x1841003c, 0x1841003b, 0x18410039, 0x18410037, + 0x17410044, 0x17410042, 0x17410040, 0x1741003e, + 0x1741003c, 0x1741003b, 0x17410039, 0x17410037, + 0x16410044, 0x16410042, 0x16410040, 0x1641003e, + 0x1641003c, 0x1641003b, 0x16410039, 0x16410037, + 0x15410044, 0x15410042, 0x15410040, 0x1541003e, + 0x1541003c, 0x1541003b, 0x15410039, 0x15410037, + 0x14410044, 0x14410042, 0x14410040, 0x1441003e, + 0x1441003c, 0x1441003b, 0x14410039, 0x14410037, + 0x13410044, 0x13410042, 0x13410040, 0x1341003e, + 0x1341003c, 0x1341003b, 0x13410039, 0x13410037, + 0x12410044, 0x12410042, 0x12410040, 0x1241003e, + 0x1241003c, 0x1241003b, 0x12410039, 0x12410037, + 0x11410044, 0x11410042, 0x11410040, 0x1141003e, + 0x1141003c, 0x1141003b, 0x11410039, 0x11410037, + 0x10410044, 0x10410042, 0x10410040, 0x1041003e, + 0x1041003c, 0x1041003b, 0x10410039, 0x10410037 +}; + +static u32 nphy_tpc_txgain_HiPwrEPA[] = { + 0x0f410044, 0x0f410042, 0x0f410040, 0x0f41003e, + 0x0f41003c, 0x0f41003b, 0x0f410039, 0x0f410037, + 0x0e410044, 0x0e410042, 0x0e410040, 0x0e41003e, + 0x0e41003c, 0x0e41003b, 0x0e410039, 0x0e410037, + 0x0d410044, 0x0d410042, 0x0d410040, 0x0d41003e, + 0x0d41003c, 0x0d41003b, 0x0d410039, 0x0d410037, + 0x0c410044, 0x0c410042, 0x0c410040, 0x0c41003e, + 0x0c41003c, 0x0c41003b, 0x0c410039, 0x0c410037, + 0x0b410044, 0x0b410042, 0x0b410040, 0x0b41003e, + 0x0b41003c, 0x0b41003b, 0x0b410039, 0x0b410037, + 0x0a410044, 0x0a410042, 0x0a410040, 0x0a41003e, + 0x0a41003c, 0x0a41003b, 0x0a410039, 0x0a410037, + 0x09410044, 0x09410042, 0x09410040, 0x0941003e, + 0x0941003c, 0x0941003b, 0x09410039, 0x09410037, + 0x08410044, 0x08410042, 0x08410040, 0x0841003e, + 0x0841003c, 0x0841003b, 0x08410039, 0x08410037, + 0x07410044, 0x07410042, 0x07410040, 0x0741003e, + 0x0741003c, 0x0741003b, 0x07410039, 0x07410037, + 0x06410044, 0x06410042, 0x06410040, 0x0641003e, + 0x0641003c, 0x0641003b, 0x06410039, 0x06410037, + 0x05410044, 0x05410042, 0x05410040, 0x0541003e, + 0x0541003c, 0x0541003b, 0x05410039, 0x05410037, + 0x04410044, 0x04410042, 0x04410040, 0x0441003e, + 0x0441003c, 0x0441003b, 0x04410039, 0x04410037, + 0x03410044, 0x03410042, 0x03410040, 0x0341003e, + 0x0341003c, 0x0341003b, 0x03410039, 0x03410037, + 0x02410044, 0x02410042, 0x02410040, 0x0241003e, + 0x0241003c, 0x0241003b, 0x02410039, 0x02410037, + 0x01410044, 0x01410042, 0x01410040, 0x0141003e, + 0x0141003c, 0x0141003b, 0x01410039, 0x01410037, + 0x00410044, 0x00410042, 0x00410040, 0x0041003e, + 0x0041003c, 0x0041003b, 0x00410039, 0x00410037 +}; + +static u32 nphy_tpc_txgain_epa_2057rev3[] = { + 0x80f90040, 0x80e10040, 0x80e1003c, 0x80c9003d, + 0x80b9003c, 0x80a9003d, 0x80a1003c, 0x8099003b, + 0x8091003b, 0x8089003a, 0x8081003a, 0x80790039, + 0x80710039, 0x8069003a, 0x8061003b, 0x8059003d, + 0x8051003f, 0x80490042, 0x8049003e, 0x8049003b, + 0x8041003e, 0x8041003b, 0x8039003e, 0x8039003b, + 0x80390038, 0x80390035, 0x8031003a, 0x80310036, + 0x80310033, 0x8029003a, 0x80290037, 0x80290034, + 0x80290031, 0x80210039, 0x80210036, 0x80210033, + 0x80210030, 0x8019003c, 0x80190039, 0x80190036, + 0x80190033, 0x80190030, 0x8019002d, 0x8019002b, + 0x80190028, 0x8011003a, 0x80110036, 0x80110033, + 0x80110030, 0x8011002e, 0x8011002b, 0x80110029, + 0x80110027, 0x80110024, 0x80110022, 0x80110020, + 0x8011001f, 0x8011001d, 0x8009003a, 0x80090037, + 0x80090034, 0x80090031, 0x8009002e, 0x8009002c, + 0x80090029, 0x80090027, 0x80090025, 0x80090023, + 0x80090021, 0x8009001f, 0x8009001d, 0x8009011d, + 0x8009021d, 0x8009031d, 0x8009041d, 0x8009051d, + 0x8009061d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d, + 0x8009071d, 0x8009071d, 0x8009071d, 0x8009071d +}; + +static u32 nphy_tpc_txgain_epa_2057rev5[] = { + 0x10f90040, 0x10e10040, 0x10e1003c, 0x10c9003d, + 0x10b9003c, 0x10a9003d, 0x10a1003c, 0x1099003b, + 0x1091003b, 0x1089003a, 0x1081003a, 0x10790039, + 0x10710039, 0x1069003a, 0x1061003b, 0x1059003d, + 0x1051003f, 0x10490042, 0x1049003e, 0x1049003b, + 0x1041003e, 0x1041003b, 0x1039003e, 0x1039003b, + 0x10390038, 0x10390035, 0x1031003a, 0x10310036, + 0x10310033, 0x1029003a, 0x10290037, 0x10290034, + 0x10290031, 0x10210039, 0x10210036, 0x10210033, + 0x10210030, 0x1019003c, 0x10190039, 0x10190036, + 0x10190033, 0x10190030, 0x1019002d, 0x1019002b, + 0x10190028, 0x1011003a, 0x10110036, 0x10110033, + 0x10110030, 0x1011002e, 0x1011002b, 0x10110029, + 0x10110027, 0x10110024, 0x10110022, 0x10110020, + 0x1011001f, 0x1011001d, 0x1009003a, 0x10090037, + 0x10090034, 0x10090031, 0x1009002e, 0x1009002c, + 0x10090029, 0x10090027, 0x10090025, 0x10090023, + 0x10090021, 0x1009001f, 0x1009001d, 0x1009001b, + 0x1009001a, 0x10090018, 0x10090017, 0x10090016, + 0x10090015, 0x10090013, 0x10090012, 0x10090011, + 0x10090010, 0x1009000f, 0x1009000f, 0x1009000e, + 0x1009000d, 0x1009000c, 0x1009000c, 0x1009000b, + 0x1009000a, 0x1009000a, 0x10090009, 0x10090009, + 0x10090008, 0x10090008, 0x10090007, 0x10090007, + 0x10090007, 0x10090006, 0x10090006, 0x10090005, + 0x10090005, 0x10090005, 0x10090005, 0x10090004, + 0x10090004, 0x10090004, 0x10090004, 0x10090003, + 0x10090003, 0x10090003, 0x10090003, 0x10090003, + 0x10090003, 0x10090002, 0x10090002, 0x10090002, + 0x10090002, 0x10090002, 0x10090002, 0x10090002, + 0x10090002, 0x10090002, 0x10090001, 0x10090001, + 0x10090001, 0x10090001, 0x10090001, 0x10090001 +}; + +static u32 nphy_tpc_5GHz_txgain_rev3[] = { + 0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e, + 0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037, + 0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e, + 0xcef7003c, 0xcef7003b, 0xcef70039, 0xcef70037, + 0xcdf70044, 0xcdf70042, 0xcdf70040, 0xcdf7003e, + 0xcdf7003c, 0xcdf7003b, 0xcdf70039, 0xcdf70037, + 0xccf70044, 0xccf70042, 0xccf70040, 0xccf7003e, + 0xccf7003c, 0xccf7003b, 0xccf70039, 0xccf70037, + 0xcbf70044, 0xcbf70042, 0xcbf70040, 0xcbf7003e, + 0xcbf7003c, 0xcbf7003b, 0xcbf70039, 0xcbf70037, + 0xcaf70044, 0xcaf70042, 0xcaf70040, 0xcaf7003e, + 0xcaf7003c, 0xcaf7003b, 0xcaf70039, 0xcaf70037, + 0xc9f70044, 0xc9f70042, 0xc9f70040, 0xc9f7003e, + 0xc9f7003c, 0xc9f7003b, 0xc9f70039, 0xc9f70037, + 0xc8f70044, 0xc8f70042, 0xc8f70040, 0xc8f7003e, + 0xc8f7003c, 0xc8f7003b, 0xc8f70039, 0xc8f70037, + 0xc7f70044, 0xc7f70042, 0xc7f70040, 0xc7f7003e, + 0xc7f7003c, 0xc7f7003b, 0xc7f70039, 0xc7f70037, + 0xc6f70044, 0xc6f70042, 0xc6f70040, 0xc6f7003e, + 0xc6f7003c, 0xc6f7003b, 0xc6f70039, 0xc6f70037, + 0xc5f70044, 0xc5f70042, 0xc5f70040, 0xc5f7003e, + 0xc5f7003c, 0xc5f7003b, 0xc5f70039, 0xc5f70037, + 0xc4f70044, 0xc4f70042, 0xc4f70040, 0xc4f7003e, + 0xc4f7003c, 0xc4f7003b, 0xc4f70039, 0xc4f70037, + 0xc3f70044, 0xc3f70042, 0xc3f70040, 0xc3f7003e, + 0xc3f7003c, 0xc3f7003b, 0xc3f70039, 0xc3f70037, + 0xc2f70044, 0xc2f70042, 0xc2f70040, 0xc2f7003e, + 0xc2f7003c, 0xc2f7003b, 0xc2f70039, 0xc2f70037, + 0xc1f70044, 0xc1f70042, 0xc1f70040, 0xc1f7003e, + 0xc1f7003c, 0xc1f7003b, 0xc1f70039, 0xc1f70037, + 0xc0f70044, 0xc0f70042, 0xc0f70040, 0xc0f7003e, + 0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037 +}; + +static u32 nphy_tpc_5GHz_txgain_rev4[] = { + 0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e, + 0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037, + 0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e, + 0x2ef2003c, 0x2ef2003b, 0x2ef20039, 0x2ef20037, + 0x2df20044, 0x2df20042, 0x2df20040, 0x2df2003e, + 0x2df2003c, 0x2df2003b, 0x2df20039, 0x2df20037, + 0x2cf20044, 0x2cf20042, 0x2cf20040, 0x2cf2003e, + 0x2cf2003c, 0x2cf2003b, 0x2cf20039, 0x2cf20037, + 0x2bf20044, 0x2bf20042, 0x2bf20040, 0x2bf2003e, + 0x2bf2003c, 0x2bf2003b, 0x2bf20039, 0x2bf20037, + 0x2af20044, 0x2af20042, 0x2af20040, 0x2af2003e, + 0x2af2003c, 0x2af2003b, 0x2af20039, 0x2af20037, + 0x29f20044, 0x29f20042, 0x29f20040, 0x29f2003e, + 0x29f2003c, 0x29f2003b, 0x29f20039, 0x29f20037, + 0x28f20044, 0x28f20042, 0x28f20040, 0x28f2003e, + 0x28f2003c, 0x28f2003b, 0x28f20039, 0x28f20037, + 0x27f20044, 0x27f20042, 0x27f20040, 0x27f2003e, + 0x27f2003c, 0x27f2003b, 0x27f20039, 0x27f20037, + 0x26f20044, 0x26f20042, 0x26f20040, 0x26f2003e, + 0x26f2003c, 0x26f2003b, 0x26f20039, 0x26f20037, + 0x25f20044, 0x25f20042, 0x25f20040, 0x25f2003e, + 0x25f2003c, 0x25f2003b, 0x25f20039, 0x25f20037, + 0x24f20044, 0x24f20042, 0x24f20040, 0x24f2003e, + 0x24f2003c, 0x24f2003b, 0x24f20039, 0x24f20038, + 0x23f20041, 0x23f20040, 0x23f2003f, 0x23f2003e, + 0x23f2003c, 0x23f2003b, 0x23f20039, 0x23f20037, + 0x22f20044, 0x22f20042, 0x22f20040, 0x22f2003e, + 0x22f2003c, 0x22f2003b, 0x22f20039, 0x22f20037, + 0x21f20044, 0x21f20042, 0x21f20040, 0x21f2003e, + 0x21f2003c, 0x21f2003b, 0x21f20039, 0x21f20037, + 0x20d20043, 0x20d20041, 0x20d2003e, 0x20d2003c, + 0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034 +}; + +static u32 nphy_tpc_5GHz_txgain_rev5[] = { + 0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044, + 0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c, + 0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e, + 0x0e62003c, 0x0e62003d, 0x0e62003b, 0x0e62003a, + 0x0d620043, 0x0d620041, 0x0d620040, 0x0d62003e, + 0x0d62003d, 0x0d62003c, 0x0d62003b, 0x0d62003a, + 0x0c620041, 0x0c620040, 0x0c62003f, 0x0c62003e, + 0x0c62003c, 0x0c62003b, 0x0c620039, 0x0c620037, + 0x0b620046, 0x0b620044, 0x0b620042, 0x0b620040, + 0x0b62003e, 0x0b62003c, 0x0b62003b, 0x0b62003a, + 0x0a620041, 0x0a620040, 0x0a62003e, 0x0a62003c, + 0x0a62003b, 0x0a62003a, 0x0a620039, 0x0a620038, + 0x0962003e, 0x0962003d, 0x0962003c, 0x0962003b, + 0x09620039, 0x09620037, 0x09620035, 0x09620033, + 0x08620044, 0x08620042, 0x08620040, 0x0862003e, + 0x0862003c, 0x0862003b, 0x0862003a, 0x08620039, + 0x07620043, 0x07620042, 0x07620040, 0x0762003f, + 0x0762003d, 0x0762003b, 0x0762003a, 0x07620039, + 0x0662003e, 0x0662003d, 0x0662003c, 0x0662003b, + 0x06620039, 0x06620037, 0x06620035, 0x06620033, + 0x05620046, 0x05620044, 0x05620042, 0x05620040, + 0x0562003e, 0x0562003c, 0x0562003b, 0x05620039, + 0x04620044, 0x04620042, 0x04620040, 0x0462003e, + 0x0462003c, 0x0462003b, 0x04620039, 0x04620038, + 0x0362003c, 0x0362003b, 0x0362003a, 0x03620039, + 0x03620038, 0x03620037, 0x03620035, 0x03620033, + 0x0262004c, 0x0262004a, 0x02620048, 0x02620047, + 0x02620046, 0x02620044, 0x02620043, 0x02620042, + 0x0162004a, 0x01620048, 0x01620046, 0x01620044, + 0x01620043, 0x01620042, 0x01620041, 0x01620040, + 0x00620042, 0x00620040, 0x0062003e, 0x0062003c, + 0x0062003b, 0x00620039, 0x00620037, 0x00620035 +}; + +static u32 nphy_tpc_5GHz_txgain_HiPwrEPA[] = { + 0x2ff10044, 0x2ff10042, 0x2ff10040, 0x2ff1003e, + 0x2ff1003c, 0x2ff1003b, 0x2ff10039, 0x2ff10037, + 0x2ef10044, 0x2ef10042, 0x2ef10040, 0x2ef1003e, + 0x2ef1003c, 0x2ef1003b, 0x2ef10039, 0x2ef10037, + 0x2df10044, 0x2df10042, 0x2df10040, 0x2df1003e, + 0x2df1003c, 0x2df1003b, 0x2df10039, 0x2df10037, + 0x2cf10044, 0x2cf10042, 0x2cf10040, 0x2cf1003e, + 0x2cf1003c, 0x2cf1003b, 0x2cf10039, 0x2cf10037, + 0x2bf10044, 0x2bf10042, 0x2bf10040, 0x2bf1003e, + 0x2bf1003c, 0x2bf1003b, 0x2bf10039, 0x2bf10037, + 0x2af10044, 0x2af10042, 0x2af10040, 0x2af1003e, + 0x2af1003c, 0x2af1003b, 0x2af10039, 0x2af10037, + 0x29f10044, 0x29f10042, 0x29f10040, 0x29f1003e, + 0x29f1003c, 0x29f1003b, 0x29f10039, 0x29f10037, + 0x28f10044, 0x28f10042, 0x28f10040, 0x28f1003e, + 0x28f1003c, 0x28f1003b, 0x28f10039, 0x28f10037, + 0x27f10044, 0x27f10042, 0x27f10040, 0x27f1003e, + 0x27f1003c, 0x27f1003b, 0x27f10039, 0x27f10037, + 0x26f10044, 0x26f10042, 0x26f10040, 0x26f1003e, + 0x26f1003c, 0x26f1003b, 0x26f10039, 0x26f10037, + 0x25f10044, 0x25f10042, 0x25f10040, 0x25f1003e, + 0x25f1003c, 0x25f1003b, 0x25f10039, 0x25f10037, + 0x24f10044, 0x24f10042, 0x24f10040, 0x24f1003e, + 0x24f1003c, 0x24f1003b, 0x24f10039, 0x24f10038, + 0x23f10041, 0x23f10040, 0x23f1003f, 0x23f1003e, + 0x23f1003c, 0x23f1003b, 0x23f10039, 0x23f10037, + 0x22f10044, 0x22f10042, 0x22f10040, 0x22f1003e, + 0x22f1003c, 0x22f1003b, 0x22f10039, 0x22f10037, + 0x21f10044, 0x21f10042, 0x21f10040, 0x21f1003e, + 0x21f1003c, 0x21f1003b, 0x21f10039, 0x21f10037, + 0x20d10043, 0x20d10041, 0x20d1003e, 0x20d1003c, + 0x20d1003a, 0x20d10038, 0x20d10036, 0x20d10034 +}; + +static u8 ant_sw_ctrl_tbl_rev8_2o3[] = { 0x14, 0x18 }; +static u8 ant_sw_ctrl_tbl_rev8[] = { 0x4, 0x8, 0x4, 0x8, 0x11, 0x12 }; +static u8 ant_sw_ctrl_tbl_rev8_2057v7_core0[] = { + 0x09, 0x0a, 0x15, 0x16, 0x09, 0x0a +}; +static u8 ant_sw_ctrl_tbl_rev8_2057v7_core1[] = { + 0x09, 0x0a, 0x09, 0x0a, 0x15, 0x16 +}; + +bool wlc_phy_bist_check_phy(struct brcms_phy_pub *pih) +{ + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + u32 phybist0, phybist1, phybist2, phybist3, phybist4; + + if (NREV_GE(pi->pubpi.phy_rev, 16)) + return true; + + phybist0 = read_phy_reg(pi, 0x0e); + phybist1 = read_phy_reg(pi, 0x0f); + phybist2 = read_phy_reg(pi, 0xea); + phybist3 = read_phy_reg(pi, 0xeb); + phybist4 = read_phy_reg(pi, 0x156); + + if ((phybist0 == 0) && (phybist1 == 0x4000) && (phybist2 == 0x1fe0) && + (phybist3 == 0) && (phybist4 == 0)) + return true; + + return false; +} + +static void wlc_phy_bphy_init_nphy(struct brcms_phy *pi) +{ + u16 addr, val; + + val = 0x1e1f; + for (addr = (NPHY_TO_BPHY_OFF + BPHY_RSSI_LUT); + addr <= (NPHY_TO_BPHY_OFF + BPHY_RSSI_LUT_END); addr++) { + write_phy_reg(pi, addr, val); + if (addr == (NPHY_TO_BPHY_OFF + 0x97)) + val = 0x3e3f; + else + val -= 0x0202; + } + + write_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_STEP, 0x668); +} + +void +wlc_phy_table_write_nphy(struct brcms_phy *pi, u32 id, u32 len, u32 offset, + u32 width, const void *data) +{ + struct phytbl_info tbl; + + tbl.tbl_id = id; + tbl.tbl_len = len; + tbl.tbl_offset = offset; + tbl.tbl_width = width; + tbl.tbl_ptr = data; + wlc_phy_write_table_nphy(pi, &tbl); +} + +void +wlc_phy_table_read_nphy(struct brcms_phy *pi, u32 id, u32 len, u32 offset, + u32 width, void *data) +{ + struct phytbl_info tbl; + + tbl.tbl_id = id; + tbl.tbl_len = len; + tbl.tbl_offset = offset; + tbl.tbl_width = width; + tbl.tbl_ptr = data; + wlc_phy_read_table_nphy(pi, &tbl); +} + +static void +wlc_phy_static_table_download_nphy(struct brcms_phy *pi) +{ + uint idx; + + if (NREV_GE(pi->pubpi.phy_rev, 16)) { + for (idx = 0; idx < mimophytbl_info_sz_rev16; idx++) + wlc_phy_write_table_nphy(pi, + &mimophytbl_info_rev16[idx]); + } else if (NREV_GE(pi->pubpi.phy_rev, 7)) { + for (idx = 0; idx < mimophytbl_info_sz_rev7; idx++) + wlc_phy_write_table_nphy(pi, + &mimophytbl_info_rev7[idx]); + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + for (idx = 0; idx < mimophytbl_info_sz_rev3; idx++) + wlc_phy_write_table_nphy(pi, + &mimophytbl_info_rev3[idx]); + } else { + for (idx = 0; idx < mimophytbl_info_sz_rev0; idx++) + wlc_phy_write_table_nphy(pi, + &mimophytbl_info_rev0[idx]); + } +} + +static void wlc_phy_tbl_init_nphy(struct brcms_phy *pi) +{ + uint idx = 0; + u8 antswctrllut; + + if (pi->phy_init_por) + wlc_phy_static_table_download_nphy(pi); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + antswctrllut = CHSPEC_IS2G(pi->radio_chanspec) ? + pi->srom_fem2g.antswctrllut : pi->srom_fem5g. + antswctrllut; + + switch (antswctrllut) { + case 0: + + break; + + case 1: + + if (pi->aa2g == 7) + wlc_phy_table_write_nphy( + pi, + NPHY_TBL_ID_ANTSWCTRLLUT, + 2, 0x21, 8, + &ant_sw_ctrl_tbl_rev8_2o3[0]); + else + wlc_phy_table_write_nphy( + pi, + NPHY_TBL_ID_ANTSWCTRLLUT, + 2, 0x21, 8, + &ant_sw_ctrl_tbl_rev8 + [0]); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 2, 0x25, 8, + &ant_sw_ctrl_tbl_rev8[2]); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 2, 0x29, 8, + &ant_sw_ctrl_tbl_rev8[4]); + break; + + case 2: + + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 2, 0x1, 8, + &ant_sw_ctrl_tbl_rev8_2057v7_core0[0]); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 2, 0x5, 8, + &ant_sw_ctrl_tbl_rev8_2057v7_core0[2]); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 2, 0x9, 8, + &ant_sw_ctrl_tbl_rev8_2057v7_core0[4]); + + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 2, 0x21, 8, + &ant_sw_ctrl_tbl_rev8_2057v7_core1[0]); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 2, 0x25, 8, + &ant_sw_ctrl_tbl_rev8_2057v7_core1[2]); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 2, 0x29, 8, + &ant_sw_ctrl_tbl_rev8_2057v7_core1[4]); + break; + + default: + break; + } + + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + for (idx = 0; idx < mimophytbl_info_sz_rev3_volatile; idx++) { + + if (idx == ANT_SWCTRL_TBL_REV3_IDX) { + antswctrllut = + CHSPEC_IS2G(pi->radio_chanspec) ? + pi->srom_fem2g.antswctrllut : + pi->srom_fem5g.antswctrllut; + switch (antswctrllut) { + case 0: + wlc_phy_write_table_nphy( + pi, + &mimophytbl_info_rev3_volatile + [idx]); + break; + case 1: + wlc_phy_write_table_nphy( + pi, + &mimophytbl_info_rev3_volatile1 + [idx]); + break; + case 2: + wlc_phy_write_table_nphy( + pi, + &mimophytbl_info_rev3_volatile2 + [idx]); + break; + case 3: + wlc_phy_write_table_nphy( + pi, + &mimophytbl_info_rev3_volatile3 + [idx]); + break; + default: + break; + } + } else { + wlc_phy_write_table_nphy( + pi, + &mimophytbl_info_rev3_volatile[idx]); + } + } + } else { + for (idx = 0; idx < mimophytbl_info_sz_rev0_volatile; idx++) + wlc_phy_write_table_nphy(pi, + &mimophytbl_info_rev0_volatile + [idx]); + } +} + +static void +wlc_phy_write_txmacreg_nphy(struct brcms_phy *pi, u16 holdoff, u16 delay) +{ + write_phy_reg(pi, 0x77, holdoff); + write_phy_reg(pi, 0xb4, delay); +} + +void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs) +{ + u16 holdoff, delay; + + if (rifs) { + + holdoff = 0x10; + delay = 0x258; + } else { + + holdoff = 0x15; + delay = 0x320; + } + + wlc_phy_write_txmacreg_nphy(pi, holdoff, delay); + + if (pi->sh && (pi->sh->_rifs_phy != rifs)) + pi->sh->_rifs_phy = rifs; +} + +static void wlc_phy_txpwrctrl_config_nphy(struct brcms_phy *pi) +{ + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + pi->nphy_txpwrctrl = PHY_TPC_HW_ON; + pi->phy_5g_pwrgain = true; + return; + } + + pi->nphy_txpwrctrl = PHY_TPC_HW_OFF; + pi->phy_5g_pwrgain = false; + + if ((pi->sh->boardflags2 & BFL2_TXPWRCTRL_EN) && + NREV_GE(pi->pubpi.phy_rev, 2) && (pi->sh->sromrev >= 4)) + pi->nphy_txpwrctrl = PHY_TPC_HW_ON; + else if ((pi->sh->sromrev >= 4) + && (pi->sh->boardflags2 & BFL2_5G_PWRGAIN)) + pi->phy_5g_pwrgain = true; +} + +static void wlc_phy_txpwr_srom_read_ppr_nphy(struct brcms_phy *pi) +{ + u16 bw40po, cddpo, stbcpo, bwduppo; + uint band_num; + struct ssb_sprom *sprom = &pi->d11core->bus->sprom; + + if (pi->sh->sromrev >= 9) + return; + + bw40po = sprom->bw40po; + pi->bw402gpo = bw40po & 0xf; + pi->bw405gpo = (bw40po & 0xf0) >> 4; + pi->bw405glpo = (bw40po & 0xf00) >> 8; + pi->bw405ghpo = (bw40po & 0xf000) >> 12; + + cddpo = sprom->cddpo; + pi->cdd2gpo = cddpo & 0xf; + pi->cdd5gpo = (cddpo & 0xf0) >> 4; + pi->cdd5glpo = (cddpo & 0xf00) >> 8; + pi->cdd5ghpo = (cddpo & 0xf000) >> 12; + + stbcpo = sprom->stbcpo; + pi->stbc2gpo = stbcpo & 0xf; + pi->stbc5gpo = (stbcpo & 0xf0) >> 4; + pi->stbc5glpo = (stbcpo & 0xf00) >> 8; + pi->stbc5ghpo = (stbcpo & 0xf000) >> 12; + + bwduppo = sprom->bwduppo; + pi->bwdup2gpo = bwduppo & 0xf; + pi->bwdup5gpo = (bwduppo & 0xf0) >> 4; + pi->bwdup5glpo = (bwduppo & 0xf00) >> 8; + pi->bwdup5ghpo = (bwduppo & 0xf000) >> 12; + + for (band_num = 0; band_num < (CH_2G_GROUP + CH_5G_GROUP); + band_num++) { + switch (band_num) { + case 0: + pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_2g = + sprom->core_pwr_info[0].maxpwr_2g; + pi->nphy_pwrctrl_info[PHY_CORE_1].max_pwr_2g = + sprom->core_pwr_info[1].maxpwr_2g; + pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_a1 = + sprom->core_pwr_info[0].pa_2g[0]; + pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_a1 = + sprom->core_pwr_info[1].pa_2g[0]; + pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_b0 = + sprom->core_pwr_info[0].pa_2g[1]; + pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_b0 = + sprom->core_pwr_info[1].pa_2g[1]; + pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_2g_b1 = + sprom->core_pwr_info[0].pa_2g[2]; + pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_2g_b1 = + sprom->core_pwr_info[1].pa_2g[2]; + pi->nphy_pwrctrl_info[PHY_CORE_0].idle_targ_2g = + sprom->core_pwr_info[0].itssi_2g; + pi->nphy_pwrctrl_info[PHY_CORE_1].idle_targ_2g = + sprom->core_pwr_info[1].itssi_2g; + + pi->cck2gpo = sprom->cck2gpo; + + pi->ofdm2gpo = sprom->ofdm2gpo; + + pi->mcs2gpo[0] = sprom->mcs2gpo[0]; + pi->mcs2gpo[1] = sprom->mcs2gpo[1]; + pi->mcs2gpo[2] = sprom->mcs2gpo[2]; + pi->mcs2gpo[3] = sprom->mcs2gpo[3]; + pi->mcs2gpo[4] = sprom->mcs2gpo[4]; + pi->mcs2gpo[5] = sprom->mcs2gpo[5]; + pi->mcs2gpo[6] = sprom->mcs2gpo[6]; + pi->mcs2gpo[7] = sprom->mcs2gpo[7]; + break; + case 1: + + pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_5gm = + sprom->core_pwr_info[0].maxpwr_5g; + pi->nphy_pwrctrl_info[PHY_CORE_1].max_pwr_5gm = + sprom->core_pwr_info[1].maxpwr_5g; + pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_a1 = + sprom->core_pwr_info[0].pa_5g[0]; + pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_a1 = + sprom->core_pwr_info[1].pa_5g[0]; + pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_b0 = + sprom->core_pwr_info[0].pa_5g[1]; + pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_b0 = + sprom->core_pwr_info[1].pa_5g[1]; + pi->nphy_pwrctrl_info[PHY_CORE_0].pwrdet_5gm_b1 = + sprom->core_pwr_info[0].pa_5g[2]; + pi->nphy_pwrctrl_info[PHY_CORE_1].pwrdet_5gm_b1 = + sprom->core_pwr_info[1].pa_5g[2]; + pi->nphy_pwrctrl_info[PHY_CORE_0].idle_targ_5gm = + sprom->core_pwr_info[0].itssi_5g; + pi->nphy_pwrctrl_info[PHY_CORE_1].idle_targ_5gm = + sprom->core_pwr_info[1].itssi_5g; + + pi->ofdm5gpo = sprom->ofdm5gpo; + + pi->mcs5gpo[0] = sprom->mcs5gpo[0]; + pi->mcs5gpo[1] = sprom->mcs5gpo[1]; + pi->mcs5gpo[2] = sprom->mcs5gpo[2]; + pi->mcs5gpo[3] = sprom->mcs5gpo[3]; + pi->mcs5gpo[4] = sprom->mcs5gpo[4]; + pi->mcs5gpo[5] = sprom->mcs5gpo[5]; + pi->mcs5gpo[6] = sprom->mcs5gpo[6]; + pi->mcs5gpo[7] = sprom->mcs5gpo[7]; + break; + case 2: + + pi->nphy_pwrctrl_info[0].max_pwr_5gl = + sprom->core_pwr_info[0].maxpwr_5gl; + pi->nphy_pwrctrl_info[1].max_pwr_5gl = + sprom->core_pwr_info[1].maxpwr_5gl; + pi->nphy_pwrctrl_info[0].pwrdet_5gl_a1 = + sprom->core_pwr_info[0].pa_5gl[0]; + pi->nphy_pwrctrl_info[1].pwrdet_5gl_a1 = + sprom->core_pwr_info[1].pa_5gl[0]; + pi->nphy_pwrctrl_info[0].pwrdet_5gl_b0 = + sprom->core_pwr_info[0].pa_5gl[1]; + pi->nphy_pwrctrl_info[1].pwrdet_5gl_b0 = + sprom->core_pwr_info[1].pa_5gl[1]; + pi->nphy_pwrctrl_info[0].pwrdet_5gl_b1 = + sprom->core_pwr_info[0].pa_5gl[2]; + pi->nphy_pwrctrl_info[1].pwrdet_5gl_b1 = + sprom->core_pwr_info[1].pa_5gl[2]; + pi->nphy_pwrctrl_info[0].idle_targ_5gl = 0; + pi->nphy_pwrctrl_info[1].idle_targ_5gl = 0; + + pi->ofdm5glpo = sprom->ofdm5glpo; + + pi->mcs5glpo[0] = sprom->mcs5glpo[0]; + pi->mcs5glpo[1] = sprom->mcs5glpo[1]; + pi->mcs5glpo[2] = sprom->mcs5glpo[2]; + pi->mcs5glpo[3] = sprom->mcs5glpo[3]; + pi->mcs5glpo[4] = sprom->mcs5glpo[4]; + pi->mcs5glpo[5] = sprom->mcs5glpo[5]; + pi->mcs5glpo[6] = sprom->mcs5glpo[6]; + pi->mcs5glpo[7] = sprom->mcs5glpo[7]; + break; + case 3: + + pi->nphy_pwrctrl_info[0].max_pwr_5gh = + sprom->core_pwr_info[0].maxpwr_5gh; + pi->nphy_pwrctrl_info[1].max_pwr_5gh = + sprom->core_pwr_info[1].maxpwr_5gh; + pi->nphy_pwrctrl_info[0].pwrdet_5gh_a1 = + sprom->core_pwr_info[0].pa_5gh[0]; + pi->nphy_pwrctrl_info[1].pwrdet_5gh_a1 = + sprom->core_pwr_info[1].pa_5gh[0]; + pi->nphy_pwrctrl_info[0].pwrdet_5gh_b0 = + sprom->core_pwr_info[0].pa_5gh[1]; + pi->nphy_pwrctrl_info[1].pwrdet_5gh_b0 = + sprom->core_pwr_info[1].pa_5gh[1]; + pi->nphy_pwrctrl_info[0].pwrdet_5gh_b1 = + sprom->core_pwr_info[0].pa_5gh[2]; + pi->nphy_pwrctrl_info[1].pwrdet_5gh_b1 = + sprom->core_pwr_info[1].pa_5gh[2]; + pi->nphy_pwrctrl_info[0].idle_targ_5gh = 0; + pi->nphy_pwrctrl_info[1].idle_targ_5gh = 0; + + pi->ofdm5ghpo = sprom->ofdm5ghpo; + + pi->mcs5ghpo[0] = sprom->mcs5ghpo[0]; + pi->mcs5ghpo[1] = sprom->mcs5ghpo[1]; + pi->mcs5ghpo[2] = sprom->mcs5ghpo[2]; + pi->mcs5ghpo[3] = sprom->mcs5ghpo[3]; + pi->mcs5ghpo[4] = sprom->mcs5ghpo[4]; + pi->mcs5ghpo[5] = sprom->mcs5ghpo[5]; + pi->mcs5ghpo[6] = sprom->mcs5ghpo[6]; + pi->mcs5ghpo[7] = sprom->mcs5ghpo[7]; + break; + } + } + + wlc_phy_txpwr_apply_nphy(pi); +} + +static bool wlc_phy_txpwr_srom_read_nphy(struct brcms_phy *pi) +{ + struct ssb_sprom *sprom = &pi->d11core->bus->sprom; + + pi->antswitch = sprom->antswitch; + pi->aa2g = sprom->ant_available_bg; + pi->aa5g = sprom->ant_available_a; + + pi->srom_fem2g.tssipos = sprom->fem.ghz2.tssipos; + pi->srom_fem2g.extpagain = sprom->fem.ghz2.extpa_gain; + pi->srom_fem2g.pdetrange = sprom->fem.ghz2.pdet_range; + pi->srom_fem2g.triso = sprom->fem.ghz2.tr_iso; + pi->srom_fem2g.antswctrllut = sprom->fem.ghz2.antswlut; + + pi->srom_fem5g.tssipos = sprom->fem.ghz5.tssipos; + pi->srom_fem5g.extpagain = sprom->fem.ghz5.extpa_gain; + pi->srom_fem5g.pdetrange = sprom->fem.ghz5.pdet_range; + pi->srom_fem5g.triso = sprom->fem.ghz5.tr_iso; + if (sprom->fem.ghz5.antswlut) + pi->srom_fem5g.antswctrllut = sprom->fem.ghz5.antswlut; + else + pi->srom_fem5g.antswctrllut = sprom->fem.ghz2.antswlut; + + wlc_phy_txpower_ipa_upd(pi); + + pi->phy_txcore_disable_temp = sprom->tempthresh; + if (pi->phy_txcore_disable_temp == 0) + pi->phy_txcore_disable_temp = PHY_CHAIN_TX_DISABLE_TEMP; + + pi->phy_tempsense_offset = sprom->tempoffset; + if (pi->phy_tempsense_offset != 0) { + if (pi->phy_tempsense_offset > + (NPHY_SROM_TEMPSHIFT + NPHY_SROM_MAXTEMPOFFSET)) + pi->phy_tempsense_offset = NPHY_SROM_MAXTEMPOFFSET; + else if (pi->phy_tempsense_offset < (NPHY_SROM_TEMPSHIFT + + NPHY_SROM_MINTEMPOFFSET)) + pi->phy_tempsense_offset = NPHY_SROM_MINTEMPOFFSET; + else + pi->phy_tempsense_offset -= NPHY_SROM_TEMPSHIFT; + } + + pi->phy_txcore_enable_temp = + pi->phy_txcore_disable_temp - PHY_HYSTERESIS_DELTATEMP; + + pi->phycal_tempdelta = sprom->phycal_tempdelta; + if (pi->phycal_tempdelta > NPHY_CAL_MAXTEMPDELTA) + pi->phycal_tempdelta = 0; + + wlc_phy_txpwr_srom_read_ppr_nphy(pi); + + return true; +} + +bool wlc_phy_attach_nphy(struct brcms_phy *pi) +{ + uint i; + + if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 6)) + pi->phyhang_avoid = true; + + if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { + pi->nphy_gband_spurwar_en = true; + if (pi->sh->boardflags2 & BFL2_SPUR_WAR) + pi->nphy_aband_spurwar_en = true; + } + if (NREV_GE(pi->pubpi.phy_rev, 6) && NREV_LT(pi->pubpi.phy_rev, 7)) { + if (pi->sh->boardflags2 & BFL2_2G_SPUR_WAR) + pi->nphy_gband_spurwar2_en = true; + } + + pi->n_preamble_override = AUTO; + if (NREV_IS(pi->pubpi.phy_rev, 3) || NREV_IS(pi->pubpi.phy_rev, 4)) + pi->n_preamble_override = BRCMS_N_PREAMBLE_MIXEDMODE; + + pi->nphy_txrx_chain = AUTO; + pi->phy_scraminit = AUTO; + + pi->nphy_rxcalparams = 0x010100B5; + + pi->nphy_perical = PHY_PERICAL_MPHASE; + pi->mphase_cal_phase_id = MPHASE_CAL_STATE_IDLE; + pi->mphase_txcal_numcmds = MPHASE_TXCAL_NUMCMDS; + + pi->nphy_gain_boost = true; + pi->nphy_elna_gain_config = false; + pi->radio_is_on = false; + + for (i = 0; i < pi->pubpi.phy_corenum; i++) + pi->nphy_txpwrindex[i].index = AUTO; + + wlc_phy_txpwrctrl_config_nphy(pi); + if (pi->nphy_txpwrctrl == PHY_TPC_HW_ON) + pi->hwpwrctrl_capable = true; + + pi->pi_fptr.init = wlc_phy_init_nphy; + pi->pi_fptr.calinit = wlc_phy_cal_init_nphy; + pi->pi_fptr.chanset = wlc_phy_chanspec_set_nphy; + pi->pi_fptr.txpwrrecalc = wlc_phy_txpower_recalc_target_nphy; + + if (!wlc_phy_txpwr_srom_read_nphy(pi)) + return false; + + return true; +} + +static s32 get_rf_pwr_offset(struct brcms_phy *pi, s16 pga_gn, s16 pad_gn) +{ + s32 rfpwr_offset = 0; + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if ((pi->pubpi.radiorev == 3) || + (pi->pubpi.radiorev == 4) || + (pi->pubpi.radiorev == 6)) + rfpwr_offset = (s16) + nphy_papd_padgain_dlt_2g_2057rev3n4 + [pad_gn]; + else if (pi->pubpi.radiorev == 5) + rfpwr_offset = (s16) + nphy_papd_padgain_dlt_2g_2057rev5 + [pad_gn]; + else if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == + 8)) + rfpwr_offset = (s16) + nphy_papd_padgain_dlt_2g_2057rev7 + [pad_gn]; + } else { + if ((pi->pubpi.radiorev == 3) || + (pi->pubpi.radiorev == 4) || + (pi->pubpi.radiorev == 6)) + rfpwr_offset = (s16) + nphy_papd_pgagain_dlt_5g_2057 + [pga_gn]; + else if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == + 8)) + rfpwr_offset = (s16) + nphy_papd_pgagain_dlt_5g_2057rev7 + [pga_gn]; + } + return rfpwr_offset; +} + +static void wlc_phy_update_mimoconfig_nphy(struct brcms_phy *pi, s32 preamble) +{ + bool gf_preamble = false; + u16 val; + + if (preamble == BRCMS_N_PREAMBLE_GF) + gf_preamble = true; + + val = read_phy_reg(pi, 0xed); + + val |= RX_GF_MM_AUTO; + val &= ~RX_GF_OR_MM; + if (gf_preamble) + val |= RX_GF_OR_MM; + + write_phy_reg(pi, 0xed, val); +} + +static void wlc_phy_ipa_set_tx_digi_filts_nphy(struct brcms_phy *pi) +{ + int j, type; + u16 addr_offset[] = { 0x186, 0x195, 0x2c5}; + + for (type = 0; type < 3; type++) { + for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) + write_phy_reg(pi, addr_offset[type] + j, + NPHY_IPA_REV4_txdigi_filtcoeffs[type][j]); + } + + if (pi->bw == WL_CHANSPEC_BW_40) { + for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) + write_phy_reg(pi, 0x186 + j, + NPHY_IPA_REV4_txdigi_filtcoeffs[3][j]); + } else { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) + write_phy_reg(pi, 0x186 + j, + NPHY_IPA_REV4_txdigi_filtcoeffs[5][j]); + } + + if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) { + for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) + write_phy_reg(pi, 0x2c5 + j, + NPHY_IPA_REV4_txdigi_filtcoeffs[6][j]); + } + } +} + +static void wlc_phy_ipa_restore_tx_digi_filts_nphy(struct brcms_phy *pi) +{ + int j; + + if (pi->bw == WL_CHANSPEC_BW_40) { + for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) + write_phy_reg(pi, 0x195 + j, + NPHY_IPA_REV4_txdigi_filtcoeffs[4][j]); + } else { + for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) + write_phy_reg(pi, 0x186 + j, + NPHY_IPA_REV4_txdigi_filtcoeffs[3][j]); + } +} + +static void +wlc_phy_set_rfseq_nphy(struct brcms_phy *pi, u8 cmd, u8 *events, u8 *dlys, + u8 len) +{ + u32 t1_offset, t2_offset; + u8 ctr; + u8 end_event = + NREV_GE(pi->pubpi.phy_rev, + 3) ? NPHY_REV3_RFSEQ_CMD_END : NPHY_RFSEQ_CMD_END; + u8 end_dly = 1; + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + t1_offset = cmd << 4; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, len, t1_offset, 8, + events); + t2_offset = t1_offset + 0x080; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, len, t2_offset, 8, + dlys); + + for (ctr = len; ctr < 16; ctr++) { + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, + t1_offset + ctr, 8, &end_event); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, + t2_offset + ctr, 8, &end_dly); + } + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +static u16 wlc_phy_read_lpf_bw_ctl_nphy(struct brcms_phy *pi, u16 offset) +{ + u16 lpf_bw_ctl_val = 0; + u16 rx2tx_lpf_rc_lut_offset = 0; + + if (offset == 0) { + if (CHSPEC_IS40(pi->radio_chanspec)) + rx2tx_lpf_rc_lut_offset = 0x159; + else + rx2tx_lpf_rc_lut_offset = 0x154; + } else { + rx2tx_lpf_rc_lut_offset = offset; + } + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, + (u32) rx2tx_lpf_rc_lut_offset, 16, + &lpf_bw_ctl_val); + + lpf_bw_ctl_val = lpf_bw_ctl_val & 0x7; + + return lpf_bw_ctl_val; +} + +static void +wlc_phy_rfctrl_override_nphy_rev7(struct brcms_phy *pi, u16 field, u16 value, + u8 core_mask, u8 off, u8 override_id) +{ + u8 core_num; + u16 addr = 0, en_addr = 0, val_addr = 0, en_mask = 0, val_mask = 0; + u8 val_shift = 0; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + en_mask = field; + for (core_num = 0; core_num < 2; core_num++) { + if (override_id == NPHY_REV7_RFCTRLOVERRIDE_ID0) { + + switch (field) { + case (0x1 << 2): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : + 0x7d; + val_mask = (0x1 << 1); + val_shift = 1; + break; + case (0x1 << 3): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : + 0x7d; + val_mask = (0x1 << 2); + val_shift = 2; + break; + case (0x1 << 4): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : + 0x7d; + val_mask = (0x1 << 4); + val_shift = 4; + break; + case (0x1 << 5): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : + 0x7d; + val_mask = (0x1 << 5); + val_shift = 5; + break; + case (0x1 << 6): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : + 0x7d; + val_mask = (0x1 << 6); + val_shift = 6; + break; + case (0x1 << 7): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : + 0x7d; + val_mask = (0x1 << 7); + val_shift = 7; + break; + case (0x1 << 10): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0xf8 : + 0xfa; + val_mask = (0x7 << 4); + val_shift = 4; + break; + case (0x1 << 11): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7b : + 0x7e; + val_mask = (0xffff << 0); + val_shift = 0; + break; + case (0x1 << 12): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7c : + 0x7f; + val_mask = (0xffff << 0); + val_shift = 0; + break; + case (0x3 << 13): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x348 : + 0x349; + val_mask = (0xff << 0); + val_shift = 0; + break; + case (0x1 << 13): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x348 : + 0x349; + val_mask = (0xf << 0); + val_shift = 0; + break; + default: + addr = 0xffff; + break; + } + } else if (override_id == + NPHY_REV7_RFCTRLOVERRIDE_ID1) { + + switch (field) { + case (0x1 << 1): + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x1 << 1); + val_shift = 1; + break; + case (0x1 << 3): + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x1 << 3); + val_shift = 3; + break; + case (0x1 << 5): + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x1 << 5); + val_shift = 5; + break; + case (0x1 << 4): + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x1 << 4); + val_shift = 4; + break; + case (0x1 << 2): + + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x1 << 2); + val_shift = 2; + break; + case (0x1 << 7): + + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x7 << 8); + val_shift = 8; + break; + case (0x1 << 11): + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x1 << 14); + val_shift = 14; + break; + case (0x1 << 10): + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x1 << 13); + val_shift = 13; + break; + case (0x1 << 9): + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x1 << 12); + val_shift = 12; + break; + case (0x1 << 8): + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x1 << 11); + val_shift = 11; + break; + case (0x1 << 6): + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x1 << 6); + val_shift = 6; + break; + case (0x1 << 0): + en_addr = (core_num == 0) ? 0x342 : + 0x343; + val_addr = (core_num == 0) ? 0x340 : + 0x341; + val_mask = (0x1 << 0); + val_shift = 0; + break; + default: + addr = 0xffff; + break; + } + } else if (override_id == + NPHY_REV7_RFCTRLOVERRIDE_ID2) { + + switch (field) { + case (0x1 << 3): + en_addr = (core_num == 0) ? 0x346 : + 0x347; + val_addr = (core_num == 0) ? 0x344 : + 0x345; + val_mask = (0x1 << 3); + val_shift = 3; + break; + case (0x1 << 1): + en_addr = (core_num == 0) ? 0x346 : + 0x347; + val_addr = (core_num == 0) ? 0x344 : + 0x345; + val_mask = (0x1 << 1); + val_shift = 1; + break; + case (0x1 << 0): + en_addr = (core_num == 0) ? 0x346 : + 0x347; + val_addr = (core_num == 0) ? 0x344 : + 0x345; + val_mask = (0x1 << 0); + val_shift = 0; + break; + case (0x1 << 2): + en_addr = (core_num == 0) ? 0x346 : + 0x347; + val_addr = (core_num == 0) ? 0x344 : + 0x345; + val_mask = (0x1 << 2); + val_shift = 2; + break; + case (0x1 << 4): + en_addr = (core_num == 0) ? 0x346 : + 0x347; + val_addr = (core_num == 0) ? 0x344 : + 0x345; + val_mask = (0x1 << 4); + val_shift = 4; + break; + default: + addr = 0xffff; + break; + } + } + + if (off) { + and_phy_reg(pi, en_addr, ~en_mask); + and_phy_reg(pi, val_addr, ~val_mask); + } else { + + if ((core_mask == 0) + || (core_mask & (1 << core_num))) { + or_phy_reg(pi, en_addr, en_mask); + + if (addr != 0xffff) + mod_phy_reg(pi, val_addr, + val_mask, + (value << + val_shift)); + } + } + } + } +} + +static void wlc_phy_adjust_lnagaintbl_nphy(struct brcms_phy *pi) +{ + uint core; + int ctr; + s16 gain_delta[2]; + u8 curr_channel; + u16 minmax_gain[2]; + u16 regval[4]; + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + if (pi->nphy_gain_boost) { + if ((CHSPEC_IS2G(pi->radio_chanspec))) { + + gain_delta[0] = 6; + gain_delta[1] = 6; + } else { + + curr_channel = CHSPEC_CHANNEL(pi->radio_chanspec); + gain_delta[0] = + (s16) + PHY_HW_ROUND(((nphy_lnagain_est0[0] * + curr_channel) + + nphy_lnagain_est0[1]), 13); + gain_delta[1] = + (s16) + PHY_HW_ROUND(((nphy_lnagain_est1[0] * + curr_channel) + + nphy_lnagain_est1[1]), 13); + } + } else { + + gain_delta[0] = 0; + gain_delta[1] = 0; + } + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + if (pi->nphy_elna_gain_config) { + + regval[0] = nphy_def_lnagains[2] + gain_delta[core]; + regval[1] = nphy_def_lnagains[3] + gain_delta[core]; + regval[2] = nphy_def_lnagains[3] + gain_delta[core]; + regval[3] = nphy_def_lnagains[3] + gain_delta[core]; + } else { + for (ctr = 0; ctr < 4; ctr++) + regval[ctr] = + nphy_def_lnagains[ctr] + + gain_delta[core]; + } + wlc_phy_table_write_nphy(pi, core, 4, 8, 16, regval); + + minmax_gain[core] = + (u16) (nphy_def_lnagains[2] + gain_delta[core] + 4); + } + + mod_phy_reg(pi, 0x1e, (0xff << 0), (minmax_gain[0] << 0)); + mod_phy_reg(pi, 0x34, (0xff << 0), (minmax_gain[1] << 0)); + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +static void +wlc_phy_war_force_trsw_to_R_cliplo_nphy(struct brcms_phy *pi, u8 core) +{ + if (core == PHY_CORE_0) { + write_phy_reg(pi, 0x38, 0x4); + if (CHSPEC_IS2G(pi->radio_chanspec)) + write_phy_reg(pi, 0x37, 0x0060); + else + write_phy_reg(pi, 0x37, 0x1080); + } else if (core == PHY_CORE_1) { + write_phy_reg(pi, 0x2ae, 0x4); + if (CHSPEC_IS2G(pi->radio_chanspec)) + write_phy_reg(pi, 0x2ad, 0x0060); + else + write_phy_reg(pi, 0x2ad, 0x1080); + } +} + +static void wlc_phy_war_txchain_upd_nphy(struct brcms_phy *pi, u8 txchain) +{ + u8 txchain0, txchain1; + + txchain0 = txchain & 0x1; + txchain1 = (txchain & 0x2) >> 1; + if (!txchain0) + wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_0); + + if (!txchain1) + wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_1); +} + +static void wlc_phy_workarounds_nphy_gainctrl_2057_rev5(struct brcms_phy *pi) +{ + s8 lna1_gain_db[] = { 8, 13, 17, 22 }; + s8 lna2_gain_db[] = { -2, 7, 11, 15 }; + s8 tia_gain_db[] = { -4, -1, 2, 5, 5, 5, 5, 5, 5, 5 }; + s8 tia_gainbits[] = { + 0x0, 0x01, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }; + + mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); + mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); + + mod_phy_reg(pi, 0x289, (0xff << 0), (0x46 << 0)); + + mod_phy_reg(pi, 0x283, (0xff << 0), (0x3c << 0)); + mod_phy_reg(pi, 0x280, (0xff << 0), (0x3c << 0)); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x8, 8, + lna1_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x8, 8, + lna1_gain_db); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10, 8, + lna2_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10, 8, + lna2_gain_db); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, 8, + tia_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, 8, + tia_gain_db); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, 8, + tia_gainbits); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, 8, + tia_gainbits); + + write_phy_reg(pi, 0x37, 0x74); + write_phy_reg(pi, 0x2ad, 0x74); + write_phy_reg(pi, 0x38, 0x18); + write_phy_reg(pi, 0x2ae, 0x18); + + write_phy_reg(pi, 0x2b, 0xe8); + write_phy_reg(pi, 0x41, 0xe8); + + if (CHSPEC_IS20(pi->radio_chanspec)) { + + mod_phy_reg(pi, 0x300, (0x3f << 0), (0x12 << 0)); + mod_phy_reg(pi, 0x301, (0x3f << 0), (0x12 << 0)); + } else { + + mod_phy_reg(pi, 0x300, (0x3f << 0), (0x10 << 0)); + mod_phy_reg(pi, 0x301, (0x3f << 0), (0x10 << 0)); + } +} + +static void wlc_phy_workarounds_nphy_gainctrl_2057_rev6(struct brcms_phy *pi) +{ + u16 currband; + s8 lna1G_gain_db_rev7[] = { 9, 14, 19, 24 }; + s8 *lna1_gain_db = NULL; + s8 *lna1_gain_db_2 = NULL; + s8 *lna2_gain_db = NULL; + s8 tiaA_gain_db_rev7[] = { -9, -6, -3, 0, 3, 3, 3, 3, 3, 3 }; + s8 *tia_gain_db; + s8 tiaA_gainbits_rev7[] = { 0, 1, 2, 3, 4, 4, 4, 4, 4, 4 }; + s8 *tia_gainbits; + u16 rfseqA_init_gain_rev7[] = { 0x624f, 0x624f }; + u16 *rfseq_init_gain; + u16 init_gaincode; + u16 clip1hi_gaincode; + u16 clip1md_gaincode = 0; + u16 clip1md_gaincode_B; + u16 clip1lo_gaincode; + u16 clip1lo_gaincode_B; + u8 crsminl_th = 0; + u8 crsminu_th; + u16 nbclip_th = 0; + u8 w1clip_th; + u16 freq; + s8 nvar_baseline_offset0 = 0, nvar_baseline_offset1 = 0; + u8 chg_nbclip_th = 0; + + mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); + mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); + + currband = read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand; + if (currband == 0) { + + lna1_gain_db = lna1G_gain_db_rev7; + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, 8, + lna1_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, 8, + lna1_gain_db); + + mod_phy_reg(pi, 0x283, (0xff << 0), (0x40 << 0)); + + if (CHSPEC_IS40(pi->radio_chanspec)) { + mod_phy_reg(pi, 0x280, (0xff << 0), (0x3e << 0)); + mod_phy_reg(pi, 0x283, (0xff << 0), (0x3e << 0)); + } + + mod_phy_reg(pi, 0x289, (0xff << 0), (0x46 << 0)); + + if (CHSPEC_IS20(pi->radio_chanspec)) { + mod_phy_reg(pi, 0x300, (0x3f << 0), (13 << 0)); + mod_phy_reg(pi, 0x301, (0x3f << 0), (13 << 0)); + } + } else { + + init_gaincode = 0x9e; + clip1hi_gaincode = 0x9e; + clip1md_gaincode_B = 0x24; + clip1lo_gaincode = 0x8a; + clip1lo_gaincode_B = 8; + rfseq_init_gain = rfseqA_init_gain_rev7; + + tia_gain_db = tiaA_gain_db_rev7; + tia_gainbits = tiaA_gainbits_rev7; + + freq = CHAN5G_FREQ(CHSPEC_CHANNEL(pi->radio_chanspec)); + if (CHSPEC_IS20(pi->radio_chanspec)) { + + w1clip_th = 25; + clip1md_gaincode = 0x82; + + if ((freq <= 5080) || (freq == 5825)) { + + s8 lna1A_gain_db_rev7[] = { 11, 16, 20, 24 }; + s8 lna1A_gain_db_2_rev7[] = { + 11, 17, 22, 25}; + s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 }; + + crsminu_th = 0x3e; + lna1_gain_db = lna1A_gain_db_rev7; + lna1_gain_db_2 = lna1A_gain_db_2_rev7; + lna2_gain_db = lna2A_gain_db_rev7; + } else if ((freq >= 5500) && (freq <= 5700)) { + + s8 lna1A_gain_db_rev7[] = { 11, 17, 21, 25 }; + s8 lna1A_gain_db_2_rev7[] = { + 12, 18, 22, 26}; + s8 lna2A_gain_db_rev7[] = { 1, 8, 12, 16 }; + + crsminu_th = 0x45; + clip1md_gaincode_B = 0x14; + nbclip_th = 0xff; + chg_nbclip_th = 1; + lna1_gain_db = lna1A_gain_db_rev7; + lna1_gain_db_2 = lna1A_gain_db_2_rev7; + lna2_gain_db = lna2A_gain_db_rev7; + } else { + + s8 lna1A_gain_db_rev7[] = { 12, 18, 22, 26 }; + s8 lna1A_gain_db_2_rev7[] = { + 12, 18, 22, 26}; + s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 }; + + crsminu_th = 0x41; + lna1_gain_db = lna1A_gain_db_rev7; + lna1_gain_db_2 = lna1A_gain_db_2_rev7; + lna2_gain_db = lna2A_gain_db_rev7; + } + + if (freq <= 4920) { + nvar_baseline_offset0 = 5; + nvar_baseline_offset1 = 5; + } else if ((freq > 4920) && (freq <= 5320)) { + nvar_baseline_offset0 = 3; + nvar_baseline_offset1 = 5; + } else if ((freq > 5320) && (freq <= 5700)) { + nvar_baseline_offset0 = 3; + nvar_baseline_offset1 = 2; + } else { + nvar_baseline_offset0 = 4; + nvar_baseline_offset1 = 0; + } + } else { + + crsminu_th = 0x3a; + crsminl_th = 0x3a; + w1clip_th = 20; + + if ((freq >= 4920) && (freq <= 5320)) { + nvar_baseline_offset0 = 4; + nvar_baseline_offset1 = 5; + } else if ((freq > 5320) && (freq <= 5550)) { + nvar_baseline_offset0 = 4; + nvar_baseline_offset1 = 2; + } else { + nvar_baseline_offset0 = 5; + nvar_baseline_offset1 = 3; + } + } + + write_phy_reg(pi, 0x20, init_gaincode); + write_phy_reg(pi, 0x2a7, init_gaincode); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, + pi->pubpi.phy_corenum, 0x106, 16, + rfseq_init_gain); + + write_phy_reg(pi, 0x22, clip1hi_gaincode); + write_phy_reg(pi, 0x2a9, clip1hi_gaincode); + + write_phy_reg(pi, 0x36, clip1md_gaincode_B); + write_phy_reg(pi, 0x2ac, clip1md_gaincode_B); + + write_phy_reg(pi, 0x37, clip1lo_gaincode); + write_phy_reg(pi, 0x2ad, clip1lo_gaincode); + write_phy_reg(pi, 0x38, clip1lo_gaincode_B); + write_phy_reg(pi, 0x2ae, clip1lo_gaincode_B); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, 8, + tia_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, 8, + tia_gain_db); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, 8, + tia_gainbits); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, 8, + tia_gainbits); + + mod_phy_reg(pi, 0x283, (0xff << 0), (crsminu_th << 0)); + + if (chg_nbclip_th == 1) { + write_phy_reg(pi, 0x2b, nbclip_th); + write_phy_reg(pi, 0x41, nbclip_th); + } + + mod_phy_reg(pi, 0x300, (0x3f << 0), (w1clip_th << 0)); + mod_phy_reg(pi, 0x301, (0x3f << 0), (w1clip_th << 0)); + + mod_phy_reg(pi, 0x2e4, + (0x3f << 0), (nvar_baseline_offset0 << 0)); + + mod_phy_reg(pi, 0x2e4, + (0x3f << 6), (nvar_baseline_offset1 << 6)); + + if (CHSPEC_IS20(pi->radio_chanspec)) { + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, 8, + lna1_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, 8, + lna1_gain_db_2); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10, + 8, lna2_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10, + 8, lna2_gain_db); + + write_phy_reg(pi, 0x24, clip1md_gaincode); + write_phy_reg(pi, 0x2ab, clip1md_gaincode); + } else { + mod_phy_reg(pi, 0x280, (0xff << 0), (crsminl_th << 0)); + } + } +} + +static void wlc_phy_workarounds_nphy_gainctrl(struct brcms_phy *pi) +{ + u16 w1th, hpf_code, currband; + int ctr; + u8 rfseq_updategainu_events[] = { + NPHY_RFSEQ_CMD_RX_GAIN, + NPHY_RFSEQ_CMD_CLR_HIQ_DIS, + NPHY_RFSEQ_CMD_SET_HPF_BW + }; + u8 rfseq_updategainu_dlys[] = { 10, 30, 1 }; + s8 lna1G_gain_db[] = { 7, 11, 16, 23 }; + s8 lna1G_gain_db_rev4[] = { 8, 12, 17, 25 }; + s8 lna1G_gain_db_rev5[] = { 9, 13, 18, 26 }; + s8 lna1G_gain_db_rev6[] = { 8, 13, 18, 25 }; + s8 lna1G_gain_db_rev6_224B0[] = { 10, 14, 19, 27 }; + s8 lna1A_gain_db[] = { 7, 11, 17, 23 }; + s8 lna1A_gain_db_rev4[] = { 8, 12, 18, 23 }; + s8 lna1A_gain_db_rev5[] = { 6, 10, 16, 21 }; + s8 lna1A_gain_db_rev6[] = { 6, 10, 16, 21 }; + s8 *lna1_gain_db = NULL; + s8 lna2G_gain_db[] = { -5, 6, 10, 14 }; + s8 lna2G_gain_db_rev5[] = { -3, 7, 11, 16 }; + s8 lna2G_gain_db_rev6[] = { -5, 6, 10, 14 }; + s8 lna2G_gain_db_rev6_224B0[] = { -5, 6, 10, 15 }; + s8 lna2A_gain_db[] = { -6, 2, 6, 10 }; + s8 lna2A_gain_db_rev4[] = { -5, 2, 6, 10 }; + s8 lna2A_gain_db_rev5[] = { -7, 0, 4, 8 }; + s8 lna2A_gain_db_rev6[] = { -7, 0, 4, 8 }; + s8 *lna2_gain_db = NULL; + s8 tiaG_gain_db[] = { + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A }; + s8 tiaA_gain_db[] = { + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13 }; + s8 tiaA_gain_db_rev4[] = { + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; + s8 tiaA_gain_db_rev5[] = { + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; + s8 tiaA_gain_db_rev6[] = { + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d }; + s8 *tia_gain_db; + s8 tiaG_gainbits[] = { + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 }; + s8 tiaA_gainbits[] = { + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 }; + s8 tiaA_gainbits_rev4[] = { + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; + s8 tiaA_gainbits_rev5[] = { + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; + s8 tiaA_gainbits_rev6[] = { + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; + s8 *tia_gainbits; + s8 lpf_gain_db[] = { 0x00, 0x06, 0x0c, 0x12, 0x12, 0x12 }; + s8 lpf_gainbits[] = { 0x00, 0x01, 0x02, 0x03, 0x03, 0x03 }; + u16 rfseqG_init_gain[] = { 0x613f, 0x613f, 0x613f, 0x613f }; + u16 rfseqG_init_gain_rev4[] = { 0x513f, 0x513f, 0x513f, 0x513f }; + u16 rfseqG_init_gain_rev5[] = { 0x413f, 0x413f, 0x413f, 0x413f }; + u16 rfseqG_init_gain_rev5_elna[] = { + 0x013f, 0x013f, 0x013f, 0x013f }; + u16 rfseqG_init_gain_rev6[] = { 0x513f, 0x513f }; + u16 rfseqG_init_gain_rev6_224B0[] = { 0x413f, 0x413f }; + u16 rfseqG_init_gain_rev6_elna[] = { 0x113f, 0x113f }; + u16 rfseqA_init_gain[] = { 0x516f, 0x516f, 0x516f, 0x516f }; + u16 rfseqA_init_gain_rev4[] = { 0x614f, 0x614f, 0x614f, 0x614f }; + u16 rfseqA_init_gain_rev4_elna[] = { + 0x314f, 0x314f, 0x314f, 0x314f }; + u16 rfseqA_init_gain_rev5[] = { 0x714f, 0x714f, 0x714f, 0x714f }; + u16 rfseqA_init_gain_rev6[] = { 0x714f, 0x714f }; + u16 *rfseq_init_gain; + u16 initG_gaincode = 0x627e; + u16 initG_gaincode_rev4 = 0x527e; + u16 initG_gaincode_rev5 = 0x427e; + u16 initG_gaincode_rev5_elna = 0x027e; + u16 initG_gaincode_rev6 = 0x527e; + u16 initG_gaincode_rev6_224B0 = 0x427e; + u16 initG_gaincode_rev6_elna = 0x127e; + u16 initA_gaincode = 0x52de; + u16 initA_gaincode_rev4 = 0x629e; + u16 initA_gaincode_rev4_elna = 0x329e; + u16 initA_gaincode_rev5 = 0x729e; + u16 initA_gaincode_rev6 = 0x729e; + u16 init_gaincode; + u16 clip1hiG_gaincode = 0x107e; + u16 clip1hiG_gaincode_rev4 = 0x007e; + u16 clip1hiG_gaincode_rev5 = 0x1076; + u16 clip1hiG_gaincode_rev6 = 0x007e; + u16 clip1hiA_gaincode = 0x00de; + u16 clip1hiA_gaincode_rev4 = 0x029e; + u16 clip1hiA_gaincode_rev5 = 0x029e; + u16 clip1hiA_gaincode_rev6 = 0x029e; + u16 clip1hi_gaincode; + u16 clip1mdG_gaincode = 0x0066; + u16 clip1mdA_gaincode = 0x00ca; + u16 clip1mdA_gaincode_rev4 = 0x1084; + u16 clip1mdA_gaincode_rev5 = 0x2084; + u16 clip1mdA_gaincode_rev6 = 0x2084; + u16 clip1md_gaincode = 0; + u16 clip1loG_gaincode = 0x0074; + u16 clip1loG_gaincode_rev5[] = { + 0x0062, 0x0064, 0x006a, 0x106a, 0x106c, 0x1074, 0x107c, 0x207c + }; + u16 clip1loG_gaincode_rev6[] = { + 0x106a, 0x106c, 0x1074, 0x107c, 0x007e, 0x107e, 0x207e, 0x307e + }; + u16 clip1loG_gaincode_rev6_224B0 = 0x1074; + u16 clip1loA_gaincode = 0x00cc; + u16 clip1loA_gaincode_rev4 = 0x0086; + u16 clip1loA_gaincode_rev5 = 0x2086; + u16 clip1loA_gaincode_rev6 = 0x2086; + u16 clip1lo_gaincode; + u8 crsminG_th = 0x18; + u8 crsminG_th_rev5 = 0x18; + u8 crsminG_th_rev6 = 0x18; + u8 crsminA_th = 0x1e; + u8 crsminA_th_rev4 = 0x24; + u8 crsminA_th_rev5 = 0x24; + u8 crsminA_th_rev6 = 0x24; + u8 crsmin_th; + u8 crsminlG_th = 0x18; + u8 crsminlG_th_rev5 = 0x18; + u8 crsminlG_th_rev6 = 0x18; + u8 crsminlA_th = 0x1e; + u8 crsminlA_th_rev4 = 0x24; + u8 crsminlA_th_rev5 = 0x24; + u8 crsminlA_th_rev6 = 0x24; + u8 crsminl_th = 0; + u8 crsminuG_th = 0x18; + u8 crsminuG_th_rev5 = 0x18; + u8 crsminuG_th_rev6 = 0x18; + u8 crsminuA_th = 0x1e; + u8 crsminuA_th_rev4 = 0x24; + u8 crsminuA_th_rev5 = 0x24; + u8 crsminuA_th_rev6 = 0x24; + u8 crsminuA_th_rev6_224B0 = 0x2d; + u8 crsminu_th; + u16 nbclipG_th = 0x20d; + u16 nbclipG_th_rev4 = 0x1a1; + u16 nbclipG_th_rev5 = 0x1d0; + u16 nbclipG_th_rev6 = 0x1d0; + u16 nbclipA_th = 0x1a1; + u16 nbclipA_th_rev4 = 0x107; + u16 nbclipA_th_rev5 = 0x0a9; + u16 nbclipA_th_rev6 = 0x0f0; + u16 nbclip_th = 0; + u8 w1clipG_th = 5; + u8 w1clipG_th_rev5 = 9; + u8 w1clipG_th_rev6 = 5; + u8 w1clipA_th = 25, w1clip_th; + u8 rssi_gain_default = 0x50; + u8 rssiG_gain_rev6_224B0 = 0x50; + u8 rssiA_gain_rev5 = 0x90; + u8 rssiA_gain_rev6 = 0x90; + u8 rssi_gain; + u16 regval[21]; + u8 triso; + + triso = (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g.triso : + pi->srom_fem2g.triso; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if (pi->pubpi.radiorev == 5) { + wlc_phy_workarounds_nphy_gainctrl_2057_rev5(pi); + } else if (pi->pubpi.radiorev == 7) { + wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi); + + mod_phy_reg(pi, 0x283, (0xff << 0), (0x44 << 0)); + mod_phy_reg(pi, 0x280, (0xff << 0), (0x44 << 0)); + + } else if ((pi->pubpi.radiorev == 3) + || (pi->pubpi.radiorev == 8)) { + wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi); + + if (pi->pubpi.radiorev == 8) { + mod_phy_reg(pi, 0x283, + (0xff << 0), (0x44 << 0)); + mod_phy_reg(pi, 0x280, + (0xff << 0), (0x44 << 0)); + } + } else { + wlc_phy_workarounds_nphy_gainctrl_2057_rev6(pi); + } + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + + mod_phy_reg(pi, 0xa0, (0x1 << 6), (1 << 6)); + + mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); + mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); + + currband = + read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand; + if (currband == 0) { + if (NREV_GE(pi->pubpi.phy_rev, 6)) { + if (pi->pubpi.radiorev == 11) { + lna1_gain_db = lna1G_gain_db_rev6_224B0; + lna2_gain_db = lna2G_gain_db_rev6_224B0; + rfseq_init_gain = + rfseqG_init_gain_rev6_224B0; + init_gaincode = + initG_gaincode_rev6_224B0; + clip1hi_gaincode = + clip1hiG_gaincode_rev6; + clip1lo_gaincode = + clip1loG_gaincode_rev6_224B0; + nbclip_th = nbclipG_th_rev6; + w1clip_th = w1clipG_th_rev6; + crsmin_th = crsminG_th_rev6; + crsminl_th = crsminlG_th_rev6; + crsminu_th = crsminuG_th_rev6; + rssi_gain = rssiG_gain_rev6_224B0; + } else { + lna1_gain_db = lna1G_gain_db_rev6; + lna2_gain_db = lna2G_gain_db_rev6; + if (pi->sh->boardflags & BFL_EXTLNA) { + + rfseq_init_gain = + rfseqG_init_gain_rev6_elna; + init_gaincode = + initG_gaincode_rev6_elna; + } else { + rfseq_init_gain = + rfseqG_init_gain_rev6; + init_gaincode = + initG_gaincode_rev6; + } + clip1hi_gaincode = + clip1hiG_gaincode_rev6; + switch (triso) { + case 0: + clip1lo_gaincode = + clip1loG_gaincode_rev6 + [0]; + break; + case 1: + clip1lo_gaincode = + clip1loG_gaincode_rev6 + [1]; + break; + case 2: + clip1lo_gaincode = + clip1loG_gaincode_rev6 + [2]; + break; + case 3: + default: + + clip1lo_gaincode = + clip1loG_gaincode_rev6 + [3]; + break; + case 4: + clip1lo_gaincode = + clip1loG_gaincode_rev6 + [4]; + break; + case 5: + clip1lo_gaincode = + clip1loG_gaincode_rev6 + [5]; + break; + case 6: + clip1lo_gaincode = + clip1loG_gaincode_rev6 + [6]; + break; + case 7: + clip1lo_gaincode = + clip1loG_gaincode_rev6 + [7]; + break; + } + nbclip_th = nbclipG_th_rev6; + w1clip_th = w1clipG_th_rev6; + crsmin_th = crsminG_th_rev6; + crsminl_th = crsminlG_th_rev6; + crsminu_th = crsminuG_th_rev6; + rssi_gain = rssi_gain_default; + } + } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { + lna1_gain_db = lna1G_gain_db_rev5; + lna2_gain_db = lna2G_gain_db_rev5; + if (pi->sh->boardflags & BFL_EXTLNA) { + + rfseq_init_gain = + rfseqG_init_gain_rev5_elna; + init_gaincode = + initG_gaincode_rev5_elna; + } else { + rfseq_init_gain = rfseqG_init_gain_rev5; + init_gaincode = initG_gaincode_rev5; + } + clip1hi_gaincode = clip1hiG_gaincode_rev5; + switch (triso) { + case 0: + clip1lo_gaincode = + clip1loG_gaincode_rev5[0]; + break; + case 1: + clip1lo_gaincode = + clip1loG_gaincode_rev5[1]; + break; + case 2: + clip1lo_gaincode = + clip1loG_gaincode_rev5[2]; + break; + case 3: + + clip1lo_gaincode = + clip1loG_gaincode_rev5[3]; + break; + case 4: + clip1lo_gaincode = + clip1loG_gaincode_rev5[4]; + break; + case 5: + clip1lo_gaincode = + clip1loG_gaincode_rev5[5]; + break; + case 6: + clip1lo_gaincode = + clip1loG_gaincode_rev5[6]; + break; + case 7: + clip1lo_gaincode = + clip1loG_gaincode_rev5[7]; + break; + default: + clip1lo_gaincode = + clip1loG_gaincode_rev5[3]; + break; + } + nbclip_th = nbclipG_th_rev5; + w1clip_th = w1clipG_th_rev5; + crsmin_th = crsminG_th_rev5; + crsminl_th = crsminlG_th_rev5; + crsminu_th = crsminuG_th_rev5; + rssi_gain = rssi_gain_default; + } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { + lna1_gain_db = lna1G_gain_db_rev4; + lna2_gain_db = lna2G_gain_db; + rfseq_init_gain = rfseqG_init_gain_rev4; + init_gaincode = initG_gaincode_rev4; + clip1hi_gaincode = clip1hiG_gaincode_rev4; + clip1lo_gaincode = clip1loG_gaincode; + nbclip_th = nbclipG_th_rev4; + w1clip_th = w1clipG_th; + crsmin_th = crsminG_th; + crsminl_th = crsminlG_th; + crsminu_th = crsminuG_th; + rssi_gain = rssi_gain_default; + } else { + lna1_gain_db = lna1G_gain_db; + lna2_gain_db = lna2G_gain_db; + rfseq_init_gain = rfseqG_init_gain; + init_gaincode = initG_gaincode; + clip1hi_gaincode = clip1hiG_gaincode; + clip1lo_gaincode = clip1loG_gaincode; + nbclip_th = nbclipG_th; + w1clip_th = w1clipG_th; + crsmin_th = crsminG_th; + crsminl_th = crsminlG_th; + crsminu_th = crsminuG_th; + rssi_gain = rssi_gain_default; + } + tia_gain_db = tiaG_gain_db; + tia_gainbits = tiaG_gainbits; + clip1md_gaincode = clip1mdG_gaincode; + } else { + if (NREV_GE(pi->pubpi.phy_rev, 6)) { + lna1_gain_db = lna1A_gain_db_rev6; + lna2_gain_db = lna2A_gain_db_rev6; + tia_gain_db = tiaA_gain_db_rev6; + tia_gainbits = tiaA_gainbits_rev6; + rfseq_init_gain = rfseqA_init_gain_rev6; + init_gaincode = initA_gaincode_rev6; + clip1hi_gaincode = clip1hiA_gaincode_rev6; + clip1md_gaincode = clip1mdA_gaincode_rev6; + clip1lo_gaincode = clip1loA_gaincode_rev6; + crsmin_th = crsminA_th_rev6; + crsminl_th = crsminlA_th_rev6; + if ((pi->pubpi.radiorev == 11) && + (CHSPEC_IS40(pi->radio_chanspec) == 0)) + crsminu_th = crsminuA_th_rev6_224B0; + else + crsminu_th = crsminuA_th_rev6; + + nbclip_th = nbclipA_th_rev6; + rssi_gain = rssiA_gain_rev6; + } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { + lna1_gain_db = lna1A_gain_db_rev5; + lna2_gain_db = lna2A_gain_db_rev5; + tia_gain_db = tiaA_gain_db_rev5; + tia_gainbits = tiaA_gainbits_rev5; + rfseq_init_gain = rfseqA_init_gain_rev5; + init_gaincode = initA_gaincode_rev5; + clip1hi_gaincode = clip1hiA_gaincode_rev5; + clip1md_gaincode = clip1mdA_gaincode_rev5; + clip1lo_gaincode = clip1loA_gaincode_rev5; + crsmin_th = crsminA_th_rev5; + crsminl_th = crsminlA_th_rev5; + crsminu_th = crsminuA_th_rev5; + nbclip_th = nbclipA_th_rev5; + rssi_gain = rssiA_gain_rev5; + } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { + lna1_gain_db = lna1A_gain_db_rev4; + lna2_gain_db = lna2A_gain_db_rev4; + tia_gain_db = tiaA_gain_db_rev4; + tia_gainbits = tiaA_gainbits_rev4; + if (pi->sh->boardflags & BFL_EXTLNA_5GHz) { + + rfseq_init_gain = + rfseqA_init_gain_rev4_elna; + init_gaincode = + initA_gaincode_rev4_elna; + } else { + rfseq_init_gain = rfseqA_init_gain_rev4; + init_gaincode = initA_gaincode_rev4; + } + clip1hi_gaincode = clip1hiA_gaincode_rev4; + clip1md_gaincode = clip1mdA_gaincode_rev4; + clip1lo_gaincode = clip1loA_gaincode_rev4; + crsmin_th = crsminA_th_rev4; + crsminl_th = crsminlA_th_rev4; + crsminu_th = crsminuA_th_rev4; + nbclip_th = nbclipA_th_rev4; + rssi_gain = rssi_gain_default; + } else { + lna1_gain_db = lna1A_gain_db; + lna2_gain_db = lna2A_gain_db; + tia_gain_db = tiaA_gain_db; + tia_gainbits = tiaA_gainbits; + rfseq_init_gain = rfseqA_init_gain; + init_gaincode = initA_gaincode; + clip1hi_gaincode = clip1hiA_gaincode; + clip1md_gaincode = clip1mdA_gaincode; + clip1lo_gaincode = clip1loA_gaincode; + crsmin_th = crsminA_th; + crsminl_th = crsminlA_th; + crsminu_th = crsminuA_th; + nbclip_th = nbclipA_th; + rssi_gain = rssi_gain_default; + } + w1clip_th = w1clipA_th; + } + + write_radio_reg(pi, + (RADIO_2056_RX_BIASPOLE_LNAG1_IDAC | + RADIO_2056_RX0), 0x17); + write_radio_reg(pi, + (RADIO_2056_RX_BIASPOLE_LNAG1_IDAC | + RADIO_2056_RX1), 0x17); + + write_radio_reg(pi, (RADIO_2056_RX_LNAG2_IDAC | RADIO_2056_RX0), + 0xf0); + write_radio_reg(pi, (RADIO_2056_RX_LNAG2_IDAC | RADIO_2056_RX1), + 0xf0); + + write_radio_reg(pi, (RADIO_2056_RX_RSSI_POLE | RADIO_2056_RX0), + 0x0); + write_radio_reg(pi, (RADIO_2056_RX_RSSI_POLE | RADIO_2056_RX1), + 0x0); + + write_radio_reg(pi, (RADIO_2056_RX_RSSI_GAIN | RADIO_2056_RX0), + rssi_gain); + write_radio_reg(pi, (RADIO_2056_RX_RSSI_GAIN | RADIO_2056_RX1), + rssi_gain); + + write_radio_reg(pi, + (RADIO_2056_RX_BIASPOLE_LNAA1_IDAC | + RADIO_2056_RX0), 0x17); + write_radio_reg(pi, + (RADIO_2056_RX_BIASPOLE_LNAA1_IDAC | + RADIO_2056_RX1), 0x17); + + write_radio_reg(pi, (RADIO_2056_RX_LNAA2_IDAC | RADIO_2056_RX0), + 0xFF); + write_radio_reg(pi, (RADIO_2056_RX_LNAA2_IDAC | RADIO_2056_RX1), + 0xFF); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 8, + 8, lna1_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 8, + 8, lna1_gain_db); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 4, 0x10, + 8, lna2_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 4, 0x10, + 8, lna2_gain_db); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 10, 0x20, + 8, tia_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 10, 0x20, + 8, tia_gain_db); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 10, 0x20, + 8, tia_gainbits); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 10, 0x20, + 8, tia_gainbits); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN1, 6, 0x40, + 8, &lpf_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAIN2, 6, 0x40, + 8, &lpf_gain_db); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS1, 6, 0x40, + 8, &lpf_gainbits); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_GAINBITS2, 6, 0x40, + 8, &lpf_gainbits); + + write_phy_reg(pi, 0x20, init_gaincode); + write_phy_reg(pi, 0x2a7, init_gaincode); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, + pi->pubpi.phy_corenum, 0x106, 16, + rfseq_init_gain); + + write_phy_reg(pi, 0x22, clip1hi_gaincode); + write_phy_reg(pi, 0x2a9, clip1hi_gaincode); + + write_phy_reg(pi, 0x24, clip1md_gaincode); + write_phy_reg(pi, 0x2ab, clip1md_gaincode); + + write_phy_reg(pi, 0x37, clip1lo_gaincode); + write_phy_reg(pi, 0x2ad, clip1lo_gaincode); + + mod_phy_reg(pi, 0x27d, (0xff << 0), (crsmin_th << 0)); + mod_phy_reg(pi, 0x280, (0xff << 0), (crsminl_th << 0)); + mod_phy_reg(pi, 0x283, (0xff << 0), (crsminu_th << 0)); + + write_phy_reg(pi, 0x2b, nbclip_th); + write_phy_reg(pi, 0x41, nbclip_th); + + mod_phy_reg(pi, 0x27, (0x3f << 0), (w1clip_th << 0)); + mod_phy_reg(pi, 0x3d, (0x3f << 0), (w1clip_th << 0)); + + write_phy_reg(pi, 0x150, 0x809c); + + } else { + + mod_phy_reg(pi, 0x1c, (0x1 << 13), (1 << 13)); + mod_phy_reg(pi, 0x32, (0x1 << 13), (1 << 13)); + + write_phy_reg(pi, 0x2b, 0x84); + write_phy_reg(pi, 0x41, 0x84); + + if (CHSPEC_IS20(pi->radio_chanspec)) { + write_phy_reg(pi, 0x6b, 0x2b); + write_phy_reg(pi, 0x6c, 0x2b); + write_phy_reg(pi, 0x6d, 0x9); + write_phy_reg(pi, 0x6e, 0x9); + } + + w1th = NPHY_RSSICAL_W1_TARGET - 4; + mod_phy_reg(pi, 0x27, (0x3f << 0), (w1th << 0)); + mod_phy_reg(pi, 0x3d, (0x3f << 0), (w1th << 0)); + + if (CHSPEC_IS20(pi->radio_chanspec)) { + mod_phy_reg(pi, 0x1c, (0x1f << 0), (0x1 << 0)); + mod_phy_reg(pi, 0x32, (0x1f << 0), (0x1 << 0)); + + mod_phy_reg(pi, 0x1d, (0x1f << 0), (0x1 << 0)); + mod_phy_reg(pi, 0x33, (0x1f << 0), (0x1 << 0)); + } + + write_phy_reg(pi, 0x150, 0x809c); + + if (pi->nphy_gain_boost) + if ((CHSPEC_IS2G(pi->radio_chanspec)) && + (CHSPEC_IS40(pi->radio_chanspec))) + hpf_code = 4; + else + hpf_code = 5; + else if (CHSPEC_IS40(pi->radio_chanspec)) + hpf_code = 6; + else + hpf_code = 7; + + mod_phy_reg(pi, 0x20, (0x1f << 7), (hpf_code << 7)); + mod_phy_reg(pi, 0x36, (0x1f << 7), (hpf_code << 7)); + + for (ctr = 0; ctr < 4; ctr++) + regval[ctr] = (hpf_code << 8) | 0x7c; + wlc_phy_table_write_nphy(pi, 7, 4, 0x106, 16, regval); + + wlc_phy_adjust_lnagaintbl_nphy(pi); + + if (pi->nphy_elna_gain_config) { + regval[0] = 0; + regval[1] = 1; + regval[2] = 1; + regval[3] = 1; + wlc_phy_table_write_nphy(pi, 2, 4, 8, 16, regval); + wlc_phy_table_write_nphy(pi, 3, 4, 8, 16, regval); + + for (ctr = 0; ctr < 4; ctr++) + regval[ctr] = (hpf_code << 8) | 0x74; + wlc_phy_table_write_nphy(pi, 7, 4, 0x106, 16, regval); + } + + if (NREV_IS(pi->pubpi.phy_rev, 2)) { + for (ctr = 0; ctr < 21; ctr++) + regval[ctr] = 3 * ctr; + wlc_phy_table_write_nphy(pi, 0, 21, 32, 16, regval); + wlc_phy_table_write_nphy(pi, 1, 21, 32, 16, regval); + + for (ctr = 0; ctr < 21; ctr++) + regval[ctr] = (u16) ctr; + wlc_phy_table_write_nphy(pi, 2, 21, 32, 16, regval); + wlc_phy_table_write_nphy(pi, 3, 21, 32, 16, regval); + } + + wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_UPDATEGAINU, + rfseq_updategainu_events, + rfseq_updategainu_dlys, + sizeof(rfseq_updategainu_events) / + sizeof(rfseq_updategainu_events[0])); + + mod_phy_reg(pi, 0x153, (0xff << 8), (90 << 8)); + + if (CHSPEC_IS2G(pi->radio_chanspec)) + mod_phy_reg(pi, + (NPHY_TO_BPHY_OFF + BPHY_OPTIONAL_MODES), + 0x7f, 0x4); + } +} + +static void wlc_phy_workarounds_nphy(struct brcms_phy *pi) +{ + u8 rfseq_rx2tx_events[] = { + NPHY_RFSEQ_CMD_NOP, + NPHY_RFSEQ_CMD_RXG_FBW, + NPHY_RFSEQ_CMD_TR_SWITCH, + NPHY_RFSEQ_CMD_CLR_HIQ_DIS, + NPHY_RFSEQ_CMD_RXPD_TXPD, + NPHY_RFSEQ_CMD_TX_GAIN, + NPHY_RFSEQ_CMD_EXT_PA + }; + u8 rfseq_rx2tx_dlys[] = { 8, 6, 6, 2, 4, 60, 1 }; + u8 rfseq_tx2rx_events[] = { + NPHY_RFSEQ_CMD_NOP, + NPHY_RFSEQ_CMD_EXT_PA, + NPHY_RFSEQ_CMD_TX_GAIN, + NPHY_RFSEQ_CMD_RXPD_TXPD, + NPHY_RFSEQ_CMD_TR_SWITCH, + NPHY_RFSEQ_CMD_RXG_FBW, + NPHY_RFSEQ_CMD_CLR_HIQ_DIS + }; + u8 rfseq_tx2rx_dlys[] = { 8, 6, 2, 4, 4, 6, 1 }; + u8 rfseq_tx2rx_events_rev3[] = { + NPHY_REV3_RFSEQ_CMD_EXT_PA, + NPHY_REV3_RFSEQ_CMD_INT_PA_PU, + NPHY_REV3_RFSEQ_CMD_TX_GAIN, + NPHY_REV3_RFSEQ_CMD_RXPD_TXPD, + NPHY_REV3_RFSEQ_CMD_TR_SWITCH, + NPHY_REV3_RFSEQ_CMD_RXG_FBW, + NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS, + NPHY_REV3_RFSEQ_CMD_END + }; + u8 rfseq_tx2rx_dlys_rev3[] = { 8, 4, 2, 2, 4, 4, 6, 1 }; + u8 rfseq_rx2tx_events_rev3[] = { + NPHY_REV3_RFSEQ_CMD_NOP, + NPHY_REV3_RFSEQ_CMD_RXG_FBW, + NPHY_REV3_RFSEQ_CMD_TR_SWITCH, + NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS, + NPHY_REV3_RFSEQ_CMD_RXPD_TXPD, + NPHY_REV3_RFSEQ_CMD_TX_GAIN, + NPHY_REV3_RFSEQ_CMD_INT_PA_PU, + NPHY_REV3_RFSEQ_CMD_EXT_PA, + NPHY_REV3_RFSEQ_CMD_END + }; + u8 rfseq_rx2tx_dlys_rev3[] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 }; + + u8 rfseq_rx2tx_events_rev3_ipa[] = { + NPHY_REV3_RFSEQ_CMD_NOP, + NPHY_REV3_RFSEQ_CMD_RXG_FBW, + NPHY_REV3_RFSEQ_CMD_TR_SWITCH, + NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS, + NPHY_REV3_RFSEQ_CMD_RXPD_TXPD, + NPHY_REV3_RFSEQ_CMD_TX_GAIN, + NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS, + NPHY_REV3_RFSEQ_CMD_INT_PA_PU, + NPHY_REV3_RFSEQ_CMD_END + }; + u8 rfseq_rx2tx_dlys_rev3_ipa[] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 }; + u16 rfseq_rx2tx_dacbufpu_rev7[] = { 0x10f, 0x10f }; + + s16 alpha0, alpha1, alpha2; + s16 beta0, beta1, beta2; + u32 leg_data_weights, ht_data_weights, nss1_data_weights, + stbc_data_weights; + u8 chan_freq_range = 0; + u16 dac_control = 0x0002; + u16 aux_adc_vmid_rev7_core0[] = { 0x8e, 0x96, 0x96, 0x96 }; + u16 aux_adc_vmid_rev7_core1[] = { 0x8f, 0x9f, 0x9f, 0x96 }; + u16 aux_adc_vmid_rev4[] = { 0xa2, 0xb4, 0xb4, 0x89 }; + u16 aux_adc_vmid_rev3[] = { 0xa2, 0xb4, 0xb4, 0x89 }; + u16 *aux_adc_vmid; + u16 aux_adc_gain_rev7[] = { 0x02, 0x02, 0x02, 0x02 }; + u16 aux_adc_gain_rev4[] = { 0x02, 0x02, 0x02, 0x00 }; + u16 aux_adc_gain_rev3[] = { 0x02, 0x02, 0x02, 0x00 }; + u16 *aux_adc_gain; + u16 sk_adc_vmid[] = { 0xb4, 0xb4, 0xb4, 0x24 }; + u16 sk_adc_gain[] = { 0x02, 0x02, 0x02, 0x02 }; + s32 min_nvar_val = 0x18d; + s32 min_nvar_offset_6mbps = 20; + u8 pdetrange; + u8 triso; + u16 regval; + u16 afectrl_adc_ctrl1_rev7 = 0x20; + u16 afectrl_adc_ctrl2_rev7 = 0x0; + u16 rfseq_rx2tx_lpf_h_hpc_rev7 = 0x77; + u16 rfseq_tx2rx_lpf_h_hpc_rev7 = 0x77; + u16 rfseq_pktgn_lpf_h_hpc_rev7 = 0x77; + u16 rfseq_htpktgn_lpf_hpc_rev7[] = { 0x77, 0x11, 0x11 }; + u16 rfseq_pktgn_lpf_hpc_rev7[] = { 0x11, 0x11 }; + u16 rfseq_cckpktgn_lpf_hpc_rev7[] = { 0x11, 0x11 }; + u16 ipalvlshift_3p3_war_en = 0; + u16 rccal_bcap_val, rccal_scap_val; + u16 rccal_tx20_11b_bcap = 0; + u16 rccal_tx20_11b_scap = 0; + u16 rccal_tx20_11n_bcap = 0; + u16 rccal_tx20_11n_scap = 0; + u16 rccal_tx40_11n_bcap = 0; + u16 rccal_tx40_11n_scap = 0; + u16 rx2tx_lpf_rc_lut_tx20_11b = 0; + u16 rx2tx_lpf_rc_lut_tx20_11n = 0; + u16 rx2tx_lpf_rc_lut_tx40_11n = 0; + u16 tx_lpf_bw_ofdm_20mhz = 0; + u16 tx_lpf_bw_ofdm_40mhz = 0; + u16 tx_lpf_bw_11b = 0; + u16 ipa2g_mainbias, ipa2g_casconv, ipa2g_biasfilt; + u16 txgm_idac_bleed = 0; + bool rccal_ovrd = false; + u16 freq; + int coreNum; + + if (CHSPEC_IS5G(pi->radio_chanspec)) + wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_cck_en, 0); + else + wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_cck_en, 1); + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + or_phy_reg(pi, 0xb1, NPHY_IQFlip_ADC1 | NPHY_IQFlip_ADC2); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + if (NREV_IS(pi->pubpi.phy_rev, 7)) { + mod_phy_reg(pi, 0x221, (0x1 << 4), (1 << 4)); + + mod_phy_reg(pi, 0x160, (0x7f << 0), (32 << 0)); + mod_phy_reg(pi, 0x160, (0x7f << 8), (39 << 8)); + mod_phy_reg(pi, 0x161, (0x7f << 0), (46 << 0)); + mod_phy_reg(pi, 0x161, (0x7f << 8), (51 << 8)); + mod_phy_reg(pi, 0x162, (0x7f << 0), (55 << 0)); + mod_phy_reg(pi, 0x162, (0x7f << 8), (58 << 8)); + mod_phy_reg(pi, 0x163, (0x7f << 0), (60 << 0)); + mod_phy_reg(pi, 0x163, (0x7f << 8), (62 << 8)); + mod_phy_reg(pi, 0x164, (0x7f << 0), (62 << 0)); + mod_phy_reg(pi, 0x164, (0x7f << 8), (63 << 8)); + mod_phy_reg(pi, 0x165, (0x7f << 0), (63 << 0)); + mod_phy_reg(pi, 0x165, (0x7f << 8), (64 << 8)); + mod_phy_reg(pi, 0x166, (0x7f << 0), (64 << 0)); + mod_phy_reg(pi, 0x166, (0x7f << 8), (64 << 8)); + mod_phy_reg(pi, 0x167, (0x7f << 0), (64 << 0)); + mod_phy_reg(pi, 0x167, (0x7f << 8), (64 << 8)); + } + + if (NREV_LE(pi->pubpi.phy_rev, 8)) { + write_phy_reg(pi, 0x23f, 0x1b0); + write_phy_reg(pi, 0x240, 0x1b0); + } + + if (NREV_GE(pi->pubpi.phy_rev, 8)) + mod_phy_reg(pi, 0xbd, (0xff << 0), (114 << 0)); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x00, 16, + &dac_control); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x10, 16, + &dac_control); + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, + 1, 0, 32, &leg_data_weights); + leg_data_weights = leg_data_weights & 0xffffff; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, + 1, 0, 32, &leg_data_weights); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, + 2, 0x15e, 16, + rfseq_rx2tx_dacbufpu_rev7); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x16e, 16, + rfseq_rx2tx_dacbufpu_rev7); + + if (PHY_IPA(pi)) + wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX, + rfseq_rx2tx_events_rev3_ipa, + rfseq_rx2tx_dlys_rev3_ipa, + ARRAY_SIZE(rfseq_rx2tx_events_rev3_ipa)); + + mod_phy_reg(pi, 0x299, (0x3 << 14), (0x1 << 14)); + mod_phy_reg(pi, 0x29d, (0x3 << 14), (0x1 << 14)); + + tx_lpf_bw_ofdm_20mhz = wlc_phy_read_lpf_bw_ctl_nphy(pi, 0x154); + tx_lpf_bw_ofdm_40mhz = wlc_phy_read_lpf_bw_ctl_nphy(pi, 0x159); + tx_lpf_bw_11b = wlc_phy_read_lpf_bw_ctl_nphy(pi, 0x152); + + if (PHY_IPA(pi)) { + + if (((pi->pubpi.radiorev == 5) + && (CHSPEC_IS40(pi->radio_chanspec) == 1)) + || (pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) { + + rccal_bcap_val = + read_radio_reg( + pi, + RADIO_2057_RCCAL_BCAP_VAL); + rccal_scap_val = + read_radio_reg( + pi, + RADIO_2057_RCCAL_SCAP_VAL); + + rccal_tx20_11b_bcap = rccal_bcap_val; + rccal_tx20_11b_scap = rccal_scap_val; + + if ((pi->pubpi.radiorev == 5) && + (CHSPEC_IS40(pi->radio_chanspec) == 1)) { + + rccal_tx20_11n_bcap = rccal_bcap_val; + rccal_tx20_11n_scap = rccal_scap_val; + rccal_tx40_11n_bcap = 0xc; + rccal_tx40_11n_scap = 0xc; + + rccal_ovrd = true; + + } else if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) { + + tx_lpf_bw_ofdm_20mhz = 4; + tx_lpf_bw_11b = 1; + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + rccal_tx20_11n_bcap = 0xc; + rccal_tx20_11n_scap = 0xc; + rccal_tx40_11n_bcap = 0xa; + rccal_tx40_11n_scap = 0xa; + } else { + rccal_tx20_11n_bcap = 0x14; + rccal_tx20_11n_scap = 0x14; + rccal_tx40_11n_bcap = 0xf; + rccal_tx40_11n_scap = 0xf; + } + + rccal_ovrd = true; + } + } + + } else { + + if (pi->pubpi.radiorev == 5) { + + tx_lpf_bw_ofdm_20mhz = 1; + tx_lpf_bw_ofdm_40mhz = 3; + + rccal_bcap_val = + read_radio_reg( + pi, + RADIO_2057_RCCAL_BCAP_VAL); + rccal_scap_val = + read_radio_reg( + pi, + RADIO_2057_RCCAL_SCAP_VAL); + + rccal_tx20_11b_bcap = rccal_bcap_val; + rccal_tx20_11b_scap = rccal_scap_val; + + rccal_tx20_11n_bcap = 0x13; + rccal_tx20_11n_scap = 0x11; + rccal_tx40_11n_bcap = 0x13; + rccal_tx40_11n_scap = 0x11; + + rccal_ovrd = true; + } + } + + if (rccal_ovrd) { + + rx2tx_lpf_rc_lut_tx20_11b = + (rccal_tx20_11b_bcap << 8) | + (rccal_tx20_11b_scap << 3) | + tx_lpf_bw_11b; + rx2tx_lpf_rc_lut_tx20_11n = + (rccal_tx20_11n_bcap << 8) | + (rccal_tx20_11n_scap << 3) | + tx_lpf_bw_ofdm_20mhz; + rx2tx_lpf_rc_lut_tx40_11n = + (rccal_tx40_11n_bcap << 8) | + (rccal_tx40_11n_scap << 3) | + tx_lpf_bw_ofdm_40mhz; + + for (coreNum = 0; coreNum <= 1; coreNum++) { + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_RFSEQ, + 1, + 0x152 + coreNum * 0x10, + 16, + &rx2tx_lpf_rc_lut_tx20_11b); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_RFSEQ, + 1, + 0x153 + coreNum * 0x10, + 16, + &rx2tx_lpf_rc_lut_tx20_11n); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_RFSEQ, + 1, + 0x154 + coreNum * 0x10, + 16, + &rx2tx_lpf_rc_lut_tx20_11n); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_RFSEQ, + 1, + 0x155 + coreNum * 0x10, + 16, + &rx2tx_lpf_rc_lut_tx40_11n); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_RFSEQ, + 1, + 0x156 + coreNum * 0x10, + 16, + &rx2tx_lpf_rc_lut_tx40_11n); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_RFSEQ, + 1, + 0x157 + coreNum * 0x10, + 16, + &rx2tx_lpf_rc_lut_tx40_11n); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_RFSEQ, + 1, + 0x158 + coreNum * 0x10, + 16, + &rx2tx_lpf_rc_lut_tx40_11n); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_RFSEQ, + 1, + 0x159 + coreNum * 0x10, + 16, + &rx2tx_lpf_rc_lut_tx40_11n); + } + + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 4), + 1, 0x3, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID2); + } + + write_phy_reg(pi, 0x32f, 0x3); + + if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 2), + 1, 0x3, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + + if ((pi->pubpi.radiorev == 3) || (pi->pubpi.radiorev == 4) || + (pi->pubpi.radiorev == 6)) { + if ((pi->sh->sromrev >= 8) + && (pi->sh->boardflags2 & BFL2_IPALVLSHIFT_3P3)) + ipalvlshift_3p3_war_en = 1; + + if (ipalvlshift_3p3_war_en) { + write_radio_reg(pi, RADIO_2057_GPAIO_CONFIG, + 0x5); + write_radio_reg(pi, RADIO_2057_GPAIO_SEL1, + 0x30); + write_radio_reg(pi, RADIO_2057_GPAIO_SEL0, 0x0); + or_radio_reg(pi, + RADIO_2057_RXTXBIAS_CONFIG_CORE0, + 0x1); + or_radio_reg(pi, + RADIO_2057_RXTXBIAS_CONFIG_CORE1, + 0x1); + + ipa2g_mainbias = 0x1f; + + ipa2g_casconv = 0x6f; + + ipa2g_biasfilt = 0xaa; + } else { + + ipa2g_mainbias = 0x2b; + + ipa2g_casconv = 0x7f; + + ipa2g_biasfilt = 0xee; + } + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + for (coreNum = 0; coreNum <= 1; coreNum++) { + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, + coreNum, IPA2G_IMAIN, + ipa2g_mainbias); + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, + coreNum, IPA2G_CASCONV, + ipa2g_casconv); + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, + coreNum, + IPA2G_BIAS_FILTER, + ipa2g_biasfilt); + } + } + } + + if (PHY_IPA(pi)) { + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if ((pi->pubpi.radiorev == 3) + || (pi->pubpi.radiorev == 4) + || (pi->pubpi.radiorev == 6)) + txgm_idac_bleed = 0x7f; + + for (coreNum = 0; coreNum <= 1; coreNum++) { + if (txgm_idac_bleed != 0) + WRITE_RADIO_REG4( + pi, RADIO_2057, + CORE, coreNum, + TXGM_IDAC_BLEED, + txgm_idac_bleed); + } + + if (pi->pubpi.radiorev == 5) { + + for (coreNum = 0; coreNum <= 1; + coreNum++) { + WRITE_RADIO_REG4(pi, RADIO_2057, + CORE, coreNum, + IPA2G_CASCONV, + 0x13); + WRITE_RADIO_REG4(pi, RADIO_2057, + CORE, coreNum, + IPA2G_IMAIN, + 0x1f); + WRITE_RADIO_REG4( + pi, RADIO_2057, + CORE, coreNum, + IPA2G_BIAS_FILTER, + 0xee); + WRITE_RADIO_REG4(pi, RADIO_2057, + CORE, coreNum, + PAD2G_IDACS, + 0x8a); + WRITE_RADIO_REG4( + pi, RADIO_2057, + CORE, coreNum, + PAD_BIAS_FILTER_BWS, + 0x3e); + } + + } else if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) { + + if (CHSPEC_IS40(pi->radio_chanspec) == + 0) { + WRITE_RADIO_REG4(pi, RADIO_2057, + CORE, 0, + IPA2G_IMAIN, + 0x14); + WRITE_RADIO_REG4(pi, RADIO_2057, + CORE, 1, + IPA2G_IMAIN, + 0x12); + } else { + WRITE_RADIO_REG4(pi, RADIO_2057, + CORE, 0, + IPA2G_IMAIN, + 0x16); + WRITE_RADIO_REG4(pi, RADIO_2057, + CORE, 1, + IPA2G_IMAIN, + 0x16); + } + } + + } else { + freq = CHAN5G_FREQ(CHSPEC_CHANNEL( + pi->radio_chanspec)); + if (((freq >= 5180) && (freq <= 5230)) + || ((freq >= 5745) && (freq <= 5805))) { + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, + 0, IPA5G_BIAS_FILTER, + 0xff); + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, + 1, IPA5G_BIAS_FILTER, + 0xff); + } + } + } else { + + if (pi->pubpi.radiorev != 5) { + for (coreNum = 0; coreNum <= 1; coreNum++) { + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, + coreNum, + TXMIX2G_TUNE_BOOST_PU, + 0x61); + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, + coreNum, + TXGM_IDAC_BLEED, 0x70); + } + } + } + + if (pi->pubpi.radiorev == 4) { + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, + 0x05, 16, + &afectrl_adc_ctrl1_rev7); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, + 0x15, 16, + &afectrl_adc_ctrl1_rev7); + + for (coreNum = 0; coreNum <= 1; coreNum++) { + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, + AFE_VCM_CAL_MASTER, 0x0); + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, + AFE_SET_VCM_I, 0x3f); + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, + AFE_SET_VCM_Q, 0x3f); + } + } else { + mod_phy_reg(pi, 0xa6, (0x1 << 2), (0x1 << 2)); + mod_phy_reg(pi, 0x8f, (0x1 << 2), (0x1 << 2)); + mod_phy_reg(pi, 0xa7, (0x1 << 2), (0x1 << 2)); + mod_phy_reg(pi, 0xa5, (0x1 << 2), (0x1 << 2)); + + mod_phy_reg(pi, 0xa6, (0x1 << 0), 0); + mod_phy_reg(pi, 0x8f, (0x1 << 0), (0x1 << 0)); + mod_phy_reg(pi, 0xa7, (0x1 << 0), 0); + mod_phy_reg(pi, 0xa5, (0x1 << 0), (0x1 << 0)); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, + 0x05, 16, + &afectrl_adc_ctrl2_rev7); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, + 0x15, 16, + &afectrl_adc_ctrl2_rev7); + + mod_phy_reg(pi, 0xa6, (0x1 << 2), 0); + mod_phy_reg(pi, 0x8f, (0x1 << 2), 0); + mod_phy_reg(pi, 0xa7, (0x1 << 2), 0); + mod_phy_reg(pi, 0xa5, (0x1 << 2), 0); + } + + write_phy_reg(pi, 0x6a, 0x2); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 256, 32, + &min_nvar_offset_6mbps); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x138, 16, + &rfseq_pktgn_lpf_hpc_rev7); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, 0x141, 16, + &rfseq_pktgn_lpf_h_hpc_rev7); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 3, 0x133, 16, + &rfseq_htpktgn_lpf_hpc_rev7); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x146, 16, + &rfseq_cckpktgn_lpf_hpc_rev7); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, 0x123, 16, + &rfseq_tx2rx_lpf_h_hpc_rev7); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 1, 0x12A, 16, + &rfseq_rx2tx_lpf_h_hpc_rev7); + + if (CHSPEC_IS40(pi->radio_chanspec) == 0) { + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, + 32, &min_nvar_val); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, + 127, 32, &min_nvar_val); + } else { + min_nvar_val = noise_var_tbl_rev7[3]; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, + 32, &min_nvar_val); + + min_nvar_val = noise_var_tbl_rev7[127]; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, + 127, 32, &min_nvar_val); + } + + wlc_phy_workarounds_nphy_gainctrl(pi); + + pdetrange = + (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g. + pdetrange : pi->srom_fem2g.pdetrange; + + if (pdetrange == 0) { + chan_freq_range = + wlc_phy_get_chan_freq_range_nphy(pi, 0); + if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { + aux_adc_vmid_rev7_core0[3] = 0x70; + aux_adc_vmid_rev7_core1[3] = 0x70; + aux_adc_gain_rev7[3] = 2; + } else { + aux_adc_vmid_rev7_core0[3] = 0x80; + aux_adc_vmid_rev7_core1[3] = 0x80; + aux_adc_gain_rev7[3] = 3; + } + } else if (pdetrange == 1) { + if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { + aux_adc_vmid_rev7_core0[3] = 0x7c; + aux_adc_vmid_rev7_core1[3] = 0x7c; + aux_adc_gain_rev7[3] = 2; + } else { + aux_adc_vmid_rev7_core0[3] = 0x8c; + aux_adc_vmid_rev7_core1[3] = 0x8c; + aux_adc_gain_rev7[3] = 1; + } + } else if (pdetrange == 2) { + if (pi->pubpi.radioid == BCM2057_ID) { + if ((pi->pubpi.radiorev == 5) + || (pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) { + if (chan_freq_range == + WL_CHAN_FREQ_RANGE_2G) { + aux_adc_vmid_rev7_core0[3] = + 0x8c; + aux_adc_vmid_rev7_core1[3] = + 0x8c; + aux_adc_gain_rev7[3] = 0; + } else { + aux_adc_vmid_rev7_core0[3] = + 0x96; + aux_adc_vmid_rev7_core1[3] = + 0x96; + aux_adc_gain_rev7[3] = 0; + } + } + } + + } else if (pdetrange == 3) { + if (chan_freq_range == WL_CHAN_FREQ_RANGE_2G) { + aux_adc_vmid_rev7_core0[3] = 0x89; + aux_adc_vmid_rev7_core1[3] = 0x89; + aux_adc_gain_rev7[3] = 0; + } + + } else if (pdetrange == 5) { + + if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { + aux_adc_vmid_rev7_core0[3] = 0x80; + aux_adc_vmid_rev7_core1[3] = 0x80; + aux_adc_gain_rev7[3] = 3; + } else { + aux_adc_vmid_rev7_core0[3] = 0x70; + aux_adc_vmid_rev7_core1[3] = 0x70; + aux_adc_gain_rev7[3] = 2; + } + } + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x08, 16, + &aux_adc_vmid_rev7_core0); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x18, 16, + &aux_adc_vmid_rev7_core1); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x0c, 16, + &aux_adc_gain_rev7); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, 0x1c, 16, + &aux_adc_gain_rev7); + + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + + write_phy_reg(pi, 0x23f, 0x1f8); + write_phy_reg(pi, 0x240, 0x1f8); + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, + 1, 0, 32, &leg_data_weights); + leg_data_weights = leg_data_weights & 0xffffff; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, + 1, 0, 32, &leg_data_weights); + + alpha0 = 293; + alpha1 = 435; + alpha2 = 261; + beta0 = 366; + beta1 = 205; + beta2 = 32; + write_phy_reg(pi, 0x145, alpha0); + write_phy_reg(pi, 0x146, alpha1); + write_phy_reg(pi, 0x147, alpha2); + write_phy_reg(pi, 0x148, beta0); + write_phy_reg(pi, 0x149, beta1); + write_phy_reg(pi, 0x14a, beta2); + + write_phy_reg(pi, 0x38, 0xC); + write_phy_reg(pi, 0x2ae, 0xC); + + wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_TX2RX, + rfseq_tx2rx_events_rev3, + rfseq_tx2rx_dlys_rev3, + ARRAY_SIZE(rfseq_tx2rx_events_rev3)); + + if (PHY_IPA(pi)) + wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX, + rfseq_rx2tx_events_rev3_ipa, + rfseq_rx2tx_dlys_rev3_ipa, + ARRAY_SIZE(rfseq_rx2tx_events_rev3_ipa)); + + if ((pi->sh->hw_phyrxchain != 0x3) && + (pi->sh->hw_phyrxchain != pi->sh->hw_phytxchain)) { + + if (PHY_IPA(pi)) { + rfseq_rx2tx_dlys_rev3[5] = 59; + rfseq_rx2tx_dlys_rev3[6] = 1; + rfseq_rx2tx_events_rev3[7] = + NPHY_REV3_RFSEQ_CMD_END; + } + + wlc_phy_set_rfseq_nphy( + pi, NPHY_RFSEQ_RX2TX, + rfseq_rx2tx_events_rev3, + rfseq_rx2tx_dlys_rev3, + ARRAY_SIZE(rfseq_rx2tx_events_rev3)); + } + + if (CHSPEC_IS2G(pi->radio_chanspec)) + write_phy_reg(pi, 0x6a, 0x2); + else + write_phy_reg(pi, 0x6a, 0x9c40); + + mod_phy_reg(pi, 0x294, (0xf << 8), (7 << 8)); + + if (CHSPEC_IS40(pi->radio_chanspec) == 0) { + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, + 32, &min_nvar_val); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, + 127, 32, &min_nvar_val); + } else { + min_nvar_val = noise_var_tbl_rev3[3]; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, 3, + 32, &min_nvar_val); + + min_nvar_val = noise_var_tbl_rev3[127]; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, + 127, 32, &min_nvar_val); + } + + wlc_phy_workarounds_nphy_gainctrl(pi); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x00, 16, + &dac_control); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x10, 16, + &dac_control); + + pdetrange = + (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g. + pdetrange : pi->srom_fem2g.pdetrange; + + if (pdetrange == 0) { + if (NREV_GE(pi->pubpi.phy_rev, 4)) { + aux_adc_vmid = aux_adc_vmid_rev4; + aux_adc_gain = aux_adc_gain_rev4; + } else { + aux_adc_vmid = aux_adc_vmid_rev3; + aux_adc_gain = aux_adc_gain_rev3; + } + chan_freq_range = + wlc_phy_get_chan_freq_range_nphy(pi, 0); + if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { + switch (chan_freq_range) { + case WL_CHAN_FREQ_RANGE_5GL: + aux_adc_vmid[3] = 0x89; + aux_adc_gain[3] = 0; + break; + case WL_CHAN_FREQ_RANGE_5GM: + aux_adc_vmid[3] = 0x89; + aux_adc_gain[3] = 0; + break; + case WL_CHAN_FREQ_RANGE_5GH: + aux_adc_vmid[3] = 0x89; + aux_adc_gain[3] = 0; + break; + default: + break; + } + } + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x08, 16, aux_adc_vmid); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x18, 16, aux_adc_vmid); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x0c, 16, aux_adc_gain); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x1c, 16, aux_adc_gain); + } else if (pdetrange == 1) { + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x08, 16, sk_adc_vmid); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x18, 16, sk_adc_vmid); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x0c, 16, sk_adc_gain); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x1c, 16, sk_adc_gain); + } else if (pdetrange == 2) { + + u16 bcm_adc_vmid[] = { 0xa2, 0xb4, 0xb4, 0x74 }; + u16 bcm_adc_gain[] = { 0x02, 0x02, 0x02, 0x04 }; + + if (NREV_GE(pi->pubpi.phy_rev, 6)) { + chan_freq_range = + wlc_phy_get_chan_freq_range_nphy(pi, 0); + if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { + bcm_adc_vmid[3] = 0x8e; + bcm_adc_gain[3] = 0x03; + } else { + bcm_adc_vmid[3] = 0x94; + bcm_adc_gain[3] = 0x03; + } + } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { + bcm_adc_vmid[3] = 0x84; + bcm_adc_gain[3] = 0x02; + } + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x08, 16, bcm_adc_vmid); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x18, 16, bcm_adc_vmid); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x0c, 16, bcm_adc_gain); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x1c, 16, bcm_adc_gain); + } else if (pdetrange == 3) { + chan_freq_range = + wlc_phy_get_chan_freq_range_nphy(pi, 0); + if ((NREV_GE(pi->pubpi.phy_rev, 4)) + && (chan_freq_range == WL_CHAN_FREQ_RANGE_2G)) { + + u16 auxadc_vmid[] = { + 0xa2, 0xb4, 0xb4, 0x270 + }; + u16 auxadc_gain[] = { + 0x02, 0x02, 0x02, 0x00 + }; + + wlc_phy_table_write_nphy(pi, + NPHY_TBL_ID_AFECTRL, 4, + 0x08, 16, auxadc_vmid); + wlc_phy_table_write_nphy(pi, + NPHY_TBL_ID_AFECTRL, 4, + 0x18, 16, auxadc_vmid); + wlc_phy_table_write_nphy(pi, + NPHY_TBL_ID_AFECTRL, 4, + 0x0c, 16, auxadc_gain); + wlc_phy_table_write_nphy(pi, + NPHY_TBL_ID_AFECTRL, 4, + 0x1c, 16, auxadc_gain); + } + } else if ((pdetrange == 4) || (pdetrange == 5)) { + u16 bcm_adc_vmid[] = { 0xa2, 0xb4, 0xb4, 0x0 }; + u16 bcm_adc_gain[] = { 0x02, 0x02, 0x02, 0x0 }; + u16 Vmid[2], Av[2]; + + chan_freq_range = + wlc_phy_get_chan_freq_range_nphy(pi, 0); + if (chan_freq_range != WL_CHAN_FREQ_RANGE_2G) { + Vmid[0] = (pdetrange == 4) ? 0x8e : 0x89; + Vmid[1] = (pdetrange == 4) ? 0x96 : 0x89; + Av[0] = (pdetrange == 4) ? 2 : 0; + Av[1] = (pdetrange == 4) ? 2 : 0; + } else { + Vmid[0] = (pdetrange == 4) ? 0x89 : 0x74; + Vmid[1] = (pdetrange == 4) ? 0x8b : 0x70; + Av[0] = (pdetrange == 4) ? 2 : 0; + Av[1] = (pdetrange == 4) ? 2 : 0; + } + + bcm_adc_vmid[3] = Vmid[0]; + bcm_adc_gain[3] = Av[0]; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x08, 16, bcm_adc_vmid); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x0c, 16, bcm_adc_gain); + + bcm_adc_vmid[3] = Vmid[1]; + bcm_adc_gain[3] = Av[1]; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x18, 16, bcm_adc_vmid); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 4, + 0x1c, 16, bcm_adc_gain); + } + + write_radio_reg(pi, + (RADIO_2056_RX_MIXA_MAST_BIAS | RADIO_2056_RX0), + 0x0); + write_radio_reg(pi, + (RADIO_2056_RX_MIXA_MAST_BIAS | RADIO_2056_RX1), + 0x0); + + write_radio_reg(pi, + (RADIO_2056_RX_MIXA_BIAS_MAIN | RADIO_2056_RX0), + 0x6); + write_radio_reg(pi, + (RADIO_2056_RX_MIXA_BIAS_MAIN | RADIO_2056_RX1), + 0x6); + + write_radio_reg(pi, + (RADIO_2056_RX_MIXA_BIAS_AUX | RADIO_2056_RX0), + 0x7); + write_radio_reg(pi, + (RADIO_2056_RX_MIXA_BIAS_AUX | RADIO_2056_RX1), + 0x7); + + write_radio_reg(pi, + (RADIO_2056_RX_MIXA_LOB_BIAS | RADIO_2056_RX0), + 0x88); + write_radio_reg(pi, + (RADIO_2056_RX_MIXA_LOB_BIAS | RADIO_2056_RX1), + 0x88); + + write_radio_reg(pi, + (RADIO_2056_RX_MIXA_CMFB_IDAC | RADIO_2056_RX0), + 0x0); + write_radio_reg(pi, + (RADIO_2056_RX_MIXA_CMFB_IDAC | RADIO_2056_RX1), + 0x0); + + write_radio_reg(pi, + (RADIO_2056_RX_MIXG_CMFB_IDAC | RADIO_2056_RX0), + 0x0); + write_radio_reg(pi, + (RADIO_2056_RX_MIXG_CMFB_IDAC | RADIO_2056_RX1), + 0x0); + + triso = + (CHSPEC_IS5G(pi->radio_chanspec)) ? pi->srom_fem5g. + triso : pi->srom_fem2g.triso; + if (triso == 7) { + wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_0); + wlc_phy_war_force_trsw_to_R_cliplo_nphy(pi, PHY_CORE_1); + } + + wlc_phy_war_txchain_upd_nphy(pi, pi->sh->hw_phytxchain); + + if (((pi->sh->boardflags2 & BFL2_APLL_WAR) && + (CHSPEC_IS5G(pi->radio_chanspec))) || + (((pi->sh->boardflags2 & BFL2_GPLL_WAR) || + (pi->sh->boardflags2 & BFL2_GPLL_WAR2)) && + (CHSPEC_IS2G(pi->radio_chanspec)))) { + nss1_data_weights = 0x00088888; + ht_data_weights = 0x00088888; + stbc_data_weights = 0x00088888; + } else { + nss1_data_weights = 0x88888888; + ht_data_weights = 0x88888888; + stbc_data_weights = 0x88888888; + } + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, + 1, 1, 32, &nss1_data_weights); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, + 1, 2, 32, &ht_data_weights); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL, + 1, 3, 32, &stbc_data_weights); + + if (NREV_IS(pi->pubpi.phy_rev, 4)) { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + write_radio_reg(pi, + RADIO_2056_TX_GMBB_IDAC | + RADIO_2056_TX0, 0x70); + write_radio_reg(pi, + RADIO_2056_TX_GMBB_IDAC | + RADIO_2056_TX1, 0x70); + } + } + + if (!pi->edcrs_threshold_lock) { + write_phy_reg(pi, 0x224, 0x3eb); + write_phy_reg(pi, 0x225, 0x3eb); + write_phy_reg(pi, 0x226, 0x341); + write_phy_reg(pi, 0x227, 0x341); + write_phy_reg(pi, 0x228, 0x42b); + write_phy_reg(pi, 0x229, 0x42b); + write_phy_reg(pi, 0x22a, 0x381); + write_phy_reg(pi, 0x22b, 0x381); + write_phy_reg(pi, 0x22c, 0x42b); + write_phy_reg(pi, 0x22d, 0x42b); + write_phy_reg(pi, 0x22e, 0x381); + write_phy_reg(pi, 0x22f, 0x381); + } + + if (NREV_GE(pi->pubpi.phy_rev, 6)) { + + if (pi->sh->boardflags2 & BFL2_SINGLEANT_CCK) + wlapi_bmac_mhf(pi->sh->physhim, MHF4, + MHF4_BPHY_TXCORE0, + MHF4_BPHY_TXCORE0, BRCM_BAND_ALL); + } + } else { + + if (pi->sh->boardflags2 & BFL2_SKWRKFEM_BRD || + (pi->sh->boardtype == 0x8b)) { + uint i; + u8 war_dlys[] = { 1, 6, 6, 2, 4, 20, 1 }; + for (i = 0; i < ARRAY_SIZE(rfseq_rx2tx_dlys); i++) + rfseq_rx2tx_dlys[i] = war_dlys[i]; + } + + if (CHSPEC_IS5G(pi->radio_chanspec) && pi->phy_5g_pwrgain) { + and_radio_reg(pi, RADIO_2055_CORE1_TX_RF_SPARE, 0xf7); + and_radio_reg(pi, RADIO_2055_CORE2_TX_RF_SPARE, 0xf7); + } else { + or_radio_reg(pi, RADIO_2055_CORE1_TX_RF_SPARE, 0x8); + or_radio_reg(pi, RADIO_2055_CORE2_TX_RF_SPARE, 0x8); + } + + regval = 0x000a; + wlc_phy_table_write_nphy(pi, 8, 1, 0, 16, ®val); + wlc_phy_table_write_nphy(pi, 8, 1, 0x10, 16, ®val); + + if (NREV_LT(pi->pubpi.phy_rev, 3)) { + regval = 0xcdaa; + wlc_phy_table_write_nphy(pi, 8, 1, 0x02, 16, ®val); + wlc_phy_table_write_nphy(pi, 8, 1, 0x12, 16, ®val); + } + + if (NREV_LT(pi->pubpi.phy_rev, 2)) { + regval = 0x0000; + wlc_phy_table_write_nphy(pi, 8, 1, 0x08, 16, ®val); + wlc_phy_table_write_nphy(pi, 8, 1, 0x18, 16, ®val); + + regval = 0x7aab; + wlc_phy_table_write_nphy(pi, 8, 1, 0x07, 16, ®val); + wlc_phy_table_write_nphy(pi, 8, 1, 0x17, 16, ®val); + + regval = 0x0800; + wlc_phy_table_write_nphy(pi, 8, 1, 0x06, 16, ®val); + wlc_phy_table_write_nphy(pi, 8, 1, 0x16, 16, ®val); + } + + write_phy_reg(pi, 0xf8, 0x02d8); + write_phy_reg(pi, 0xf9, 0x0301); + write_phy_reg(pi, 0xfa, 0x02d8); + write_phy_reg(pi, 0xfb, 0x0301); + + wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX, rfseq_rx2tx_events, + rfseq_rx2tx_dlys, + ARRAY_SIZE(rfseq_rx2tx_events)); + + wlc_phy_set_rfseq_nphy(pi, NPHY_RFSEQ_TX2RX, rfseq_tx2rx_events, + rfseq_tx2rx_dlys, + ARRAY_SIZE(rfseq_tx2rx_events)); + + wlc_phy_workarounds_nphy_gainctrl(pi); + + if (NREV_LT(pi->pubpi.phy_rev, 2)) { + + if (read_phy_reg(pi, 0xa0) & NPHY_MLenable) + wlapi_bmac_mhf(pi->sh->physhim, MHF3, + MHF3_NPHY_MLADV_WAR, + MHF3_NPHY_MLADV_WAR, + BRCM_BAND_ALL); + + } else if (NREV_IS(pi->pubpi.phy_rev, 2)) { + write_phy_reg(pi, 0x1e3, 0x0); + write_phy_reg(pi, 0x1e4, 0x0); + } + + if (NREV_LT(pi->pubpi.phy_rev, 2)) + mod_phy_reg(pi, 0x90, (0x1 << 7), 0); + + alpha0 = 293; + alpha1 = 435; + alpha2 = 261; + beta0 = 366; + beta1 = 205; + beta2 = 32; + write_phy_reg(pi, 0x145, alpha0); + write_phy_reg(pi, 0x146, alpha1); + write_phy_reg(pi, 0x147, alpha2); + write_phy_reg(pi, 0x148, beta0); + write_phy_reg(pi, 0x149, beta1); + write_phy_reg(pi, 0x14a, beta2); + + if (NREV_LT(pi->pubpi.phy_rev, 3)) { + mod_phy_reg(pi, 0x142, (0xf << 12), 0); + + write_phy_reg(pi, 0x192, 0xb5); + write_phy_reg(pi, 0x193, 0xa4); + write_phy_reg(pi, 0x194, 0x0); + } + + if (NREV_IS(pi->pubpi.phy_rev, 2)) + mod_phy_reg(pi, 0x221, + NPHY_FORCESIG_DECODEGATEDCLKS, + NPHY_FORCESIG_DECODEGATEDCLKS); + } + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +static void wlc_phy_extpa_set_tx_digi_filts_nphy(struct brcms_phy *pi) +{ + int j, type = 2; + u16 addr_offset = 0x2c5; + + for (j = 0; j < NPHY_NUM_DIG_FILT_COEFFS; j++) + write_phy_reg(pi, addr_offset + j, + NPHY_IPA_REV4_txdigi_filtcoeffs[type][j]); +} + +static void wlc_phy_clip_det_nphy(struct brcms_phy *pi, u8 write, u16 *vals) +{ + + if (write == 0) { + vals[0] = read_phy_reg(pi, 0x2c); + vals[1] = read_phy_reg(pi, 0x42); + } else { + write_phy_reg(pi, 0x2c, vals[0]); + write_phy_reg(pi, 0x42, vals[1]); + } +} + +static void wlc_phy_ipa_internal_tssi_setup_nphy(struct brcms_phy *pi) +{ + u8 core; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + if (CHSPEC_IS2G(pi->radio_chanspec)) { + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TX_SSI_MASTER, 0x5); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TX_SSI_MUX, 0xe); + + if (pi->pubpi.radiorev != 5) + WRITE_RADIO_REG3(pi, RADIO_2057, TX, + core, TSSIA, 0); + + if (!NREV_IS(pi->pubpi.phy_rev, 7)) + WRITE_RADIO_REG3(pi, RADIO_2057, TX, + core, TSSIG, 0x1); + else + WRITE_RADIO_REG3(pi, RADIO_2057, TX, + core, TSSIG, 0x31); + } else { + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TX_SSI_MASTER, 0x9); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TX_SSI_MUX, 0xc); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TSSIG, 0); + + if (pi->pubpi.radiorev != 5) { + if (!NREV_IS(pi->pubpi.phy_rev, 7)) + WRITE_RADIO_REG3(pi, RADIO_2057, + TX, core, + TSSIA, 0x1); + else + WRITE_RADIO_REG3(pi, RADIO_2057, + TX, core, + TSSIA, 0x31); + } + } + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_VCM_HG, + 0); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_IDAC, + 0); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM, + 0x3); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_MISC1, + 0x0); + } + } else { + WRITE_RADIO_SYN(pi, RADIO_2056, RESERVED_ADDR31, + (CHSPEC_IS2G(pi->radio_chanspec)) ? 0x128 : + 0x80); + WRITE_RADIO_SYN(pi, RADIO_2056, RESERVED_ADDR30, 0x0); + WRITE_RADIO_SYN(pi, RADIO_2056, GPIO_MASTER1, 0x29); + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, IQCAL_VCM_HG, + 0x0); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, IQCAL_IDAC, + 0x0); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_VCM, + 0x3); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TX_AMP_DET, + 0x0); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC1, + 0x8); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC2, + 0x0); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, TSSI_MISC3, + 0x0); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + TX_SSI_MASTER, 0x5); + + if (pi->pubpi.radiorev != 5) + WRITE_RADIO_REG2(pi, RADIO_2056, TX, + core, TSSIA, 0x0); + if (NREV_GE(pi->pubpi.phy_rev, 5)) + WRITE_RADIO_REG2(pi, RADIO_2056, TX, + core, TSSIG, 0x31); + else + WRITE_RADIO_REG2(pi, RADIO_2056, TX, + core, TSSIG, 0x11); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + TX_SSI_MUX, 0xe); + } else { + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + TX_SSI_MASTER, 0x9); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + TSSIA, 0x31); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + TSSIG, 0x0); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + TX_SSI_MUX, 0xc); + } + } + } +} + +static void +wlc_phy_rfctrl_override_nphy(struct brcms_phy *pi, u16 field, u16 value, + u8 core_mask, u8 off) +{ + u8 core_num; + u16 addr = 0, mask = 0, en_addr = 0, val_addr = 0, en_mask = + 0, val_mask = 0; + u8 shift = 0, val_shift = 0; + + if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { + + en_mask = field; + for (core_num = 0; core_num < 2; core_num++) { + + switch (field) { + case (0x1 << 1): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : 0x7d; + val_mask = (0x1 << 0); + val_shift = 0; + break; + case (0x1 << 2): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : 0x7d; + val_mask = (0x1 << 1); + val_shift = 1; + break; + case (0x1 << 3): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : 0x7d; + val_mask = (0x1 << 2); + val_shift = 2; + break; + case (0x1 << 4): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : 0x7d; + val_mask = (0x1 << 4); + val_shift = 4; + break; + case (0x1 << 5): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : 0x7d; + val_mask = (0x1 << 5); + val_shift = 5; + break; + case (0x1 << 6): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : 0x7d; + val_mask = (0x1 << 6); + val_shift = 6; + break; + case (0x1 << 7): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : 0x7d; + val_mask = (0x1 << 7); + val_shift = 7; + break; + case (0x1 << 8): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : 0x7d; + val_mask = (0x7 << 8); + val_shift = 8; + break; + case (0x1 << 11): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7a : 0x7d; + val_mask = (0x7 << 13); + val_shift = 13; + break; + + case (0x1 << 9): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0xf8 : 0xfa; + val_mask = (0x7 << 0); + val_shift = 0; + break; + + case (0x1 << 10): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0xf8 : 0xfa; + val_mask = (0x7 << 4); + val_shift = 4; + break; + + case (0x1 << 12): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7b : 0x7e; + val_mask = (0xffff << 0); + val_shift = 0; + break; + case (0x1 << 13): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0x7c : 0x7f; + val_mask = (0xffff << 0); + val_shift = 0; + break; + case (0x1 << 14): + en_addr = (core_num == 0) ? 0xe7 : 0xec; + val_addr = (core_num == 0) ? 0xf9 : 0xfb; + val_mask = (0x3 << 6); + val_shift = 6; + break; + case (0x1 << 0): + en_addr = (core_num == 0) ? 0xe5 : 0xe6; + val_addr = (core_num == 0) ? 0xf9 : 0xfb; + val_mask = (0x1 << 15); + val_shift = 15; + break; + default: + addr = 0xffff; + break; + } + + if (off) { + and_phy_reg(pi, en_addr, ~en_mask); + and_phy_reg(pi, val_addr, ~val_mask); + } else { + + if ((core_mask == 0) + || (core_mask & (1 << core_num))) { + or_phy_reg(pi, en_addr, en_mask); + + if (addr != 0xffff) + mod_phy_reg(pi, val_addr, + val_mask, + (value << + val_shift)); + } + } + } + } else { + + if (off) { + and_phy_reg(pi, 0xec, ~field); + value = 0x0; + } else { + or_phy_reg(pi, 0xec, field); + } + + for (core_num = 0; core_num < 2; core_num++) { + + switch (field) { + case (0x1 << 1): + case (0x1 << 9): + case (0x1 << 12): + case (0x1 << 13): + case (0x1 << 14): + addr = 0x78; + + core_mask = 0x1; + break; + case (0x1 << 2): + case (0x1 << 3): + case (0x1 << 4): + case (0x1 << 5): + case (0x1 << 6): + case (0x1 << 7): + case (0x1 << 8): + addr = (core_num == 0) ? 0x7a : 0x7d; + break; + case (0x1 << 10): + addr = (core_num == 0) ? 0x7b : 0x7e; + break; + case (0x1 << 11): + addr = (core_num == 0) ? 0x7c : 0x7f; + break; + default: + addr = 0xffff; + } + + switch (field) { + case (0x1 << 1): + mask = (0x7 << 3); + shift = 3; + break; + case (0x1 << 9): + mask = (0x1 << 2); + shift = 2; + break; + case (0x1 << 12): + mask = (0x1 << 8); + shift = 8; + break; + case (0x1 << 13): + mask = (0x1 << 9); + shift = 9; + break; + case (0x1 << 14): + mask = (0xf << 12); + shift = 12; + break; + case (0x1 << 2): + mask = (0x1 << 0); + shift = 0; + break; + case (0x1 << 3): + mask = (0x1 << 1); + shift = 1; + break; + case (0x1 << 4): + mask = (0x1 << 2); + shift = 2; + break; + case (0x1 << 5): + mask = (0x3 << 4); + shift = 4; + break; + case (0x1 << 6): + mask = (0x3 << 6); + shift = 6; + break; + case (0x1 << 7): + mask = (0x1 << 8); + shift = 8; + break; + case (0x1 << 8): + mask = (0x1 << 9); + shift = 9; + break; + case (0x1 << 10): + mask = 0x1fff; + shift = 0x0; + break; + case (0x1 << 11): + mask = 0x1fff; + shift = 0x0; + break; + default: + mask = 0x0; + shift = 0x0; + break; + } + + if ((addr != 0xffff) && (core_mask & (1 << core_num))) + mod_phy_reg(pi, addr, mask, (value << shift)); + } + + or_phy_reg(pi, 0xec, (0x1 << 0)); + or_phy_reg(pi, 0x78, (0x1 << 0)); + udelay(1); + and_phy_reg(pi, 0xec, ~(0x1 << 0)); + } +} + +static void wlc_phy_txpwrctrl_idle_tssi_nphy(struct brcms_phy *pi) +{ + s32 rssi_buf[4]; + s32 int_val; + + if (SCAN_RM_IN_PROGRESS(pi) || PLT_INPROG_PHY(pi) || PHY_MUTED(pi)) + + return; + + if (PHY_IPA(pi)) + wlc_phy_ipa_internal_tssi_setup_nphy(pi); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12), + 0, 0x3, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + else if (NREV_GE(pi->pubpi.phy_rev, 3)) + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 3, 0); + + wlc_phy_stopplayback_nphy(pi); + + wlc_phy_tx_tone_nphy(pi, 4000, 0, 0, 0, false); + + udelay(20); + int_val = + wlc_phy_poll_rssi_nphy(pi, (u8) NPHY_RSSI_SEL_TSSI_2G, rssi_buf, + 1); + wlc_phy_stopplayback_nphy(pi); + wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_OFF, 0); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12), + 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + else if (NREV_GE(pi->pubpi.phy_rev, 3)) + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 3, 1); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + + pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_2g = + (u8) ((int_val >> 24) & 0xff); + pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_5g = + (u8) ((int_val >> 24) & 0xff); + + pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_2g = + (u8) ((int_val >> 8) & 0xff); + pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_5g = + (u8) ((int_val >> 8) & 0xff); + } else { + pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_2g = + (u8) ((int_val >> 24) & 0xff); + + pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_2g = + (u8) ((int_val >> 8) & 0xff); + + pi->nphy_pwrctrl_info[PHY_CORE_0].idle_tssi_5g = + (u8) ((int_val >> 16) & 0xff); + pi->nphy_pwrctrl_info[PHY_CORE_1].idle_tssi_5g = + (u8) ((int_val) & 0xff); + } + +} + +static void wlc_phy_txpwr_limit_to_tbl_nphy(struct brcms_phy *pi) +{ + u8 idx, idx2, i, delta_ind; + + for (idx = TXP_FIRST_CCK; idx <= TXP_LAST_CCK; idx++) + pi->adj_pwr_tbl_nphy[idx] = pi->tx_power_offset[idx]; + + for (i = 0; i < 4; i++) { + idx2 = 0; + + delta_ind = 0; + + switch (i) { + case 0: + + if (CHSPEC_IS40(pi->radio_chanspec) + && NPHY_IS_SROM_REINTERPRET) { + idx = TXP_FIRST_MCS_40_SISO; + } else { + idx = (CHSPEC_IS40(pi->radio_chanspec)) ? + TXP_FIRST_OFDM_40_SISO : TXP_FIRST_OFDM; + delta_ind = 1; + } + break; + + case 1: + + idx = (CHSPEC_IS40(pi->radio_chanspec)) ? + TXP_FIRST_MCS_40_CDD : TXP_FIRST_MCS_20_CDD; + break; + + case 2: + + idx = (CHSPEC_IS40(pi->radio_chanspec)) ? + TXP_FIRST_MCS_40_STBC : TXP_FIRST_MCS_20_STBC; + break; + + case 3: + + idx = (CHSPEC_IS40(pi->radio_chanspec)) ? + TXP_FIRST_MCS_40_SDM : TXP_FIRST_MCS_20_SDM; + break; + } + + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + idx = idx + delta_ind; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx++]; + + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx++]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx++]; + + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx++]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx++]; + + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx++]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + idx = idx + 1 - delta_ind; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + pi->adj_pwr_tbl_nphy[4 + 4 * (idx2++) + i] = + pi->tx_power_offset[idx]; + } +} + +static void wlc_phy_txpwrctrl_pwr_setup_nphy(struct brcms_phy *pi) +{ + u32 idx; + s16 a1[2], b0[2], b1[2]; + s8 target_pwr_qtrdbm[2]; + s32 num, den, pwr_est; + u8 chan_freq_range; + u8 idle_tssi[2]; + u32 tbl_id, tbl_len, tbl_offset; + u32 regval[64]; + u8 core; + + if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) { + wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK); + (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); + udelay(1); + } + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + or_phy_reg(pi, 0x122, (0x1 << 0)); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) + and_phy_reg(pi, 0x1e7, (u16) (~(0x1 << 15))); + else + or_phy_reg(pi, 0x1e7, (0x1 << 15)); + + if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) + wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0); + + if (pi->sh->sromrev < 4) { + idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g; + idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g; + a1[0] = -424; + a1[1] = -424; + b0[0] = 5612; + b0[1] = 5612; + b1[1] = -1393; + b1[0] = -1393; + } else { + + chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); + switch (chan_freq_range) { + case WL_CHAN_FREQ_RANGE_2G: + idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g; + idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g; + a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_a1; + a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_a1; + b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_b0; + b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_b0; + b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_2g_b1; + b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_2g_b1; + break; + case WL_CHAN_FREQ_RANGE_5GL: + idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g; + idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g; + a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_a1; + a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_a1; + b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_b0; + b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_b0; + b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gl_b1; + b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gl_b1; + break; + case WL_CHAN_FREQ_RANGE_5GM: + idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g; + idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g; + a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_a1; + a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_a1; + b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_b0; + b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_b0; + b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gm_b1; + b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gm_b1; + break; + case WL_CHAN_FREQ_RANGE_5GH: + idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_5g; + idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_5g; + a1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_a1; + a1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_a1; + b0[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_b0; + b0[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_b0; + b1[0] = pi->nphy_pwrctrl_info[0].pwrdet_5gh_b1; + b1[1] = pi->nphy_pwrctrl_info[1].pwrdet_5gh_b1; + break; + default: + idle_tssi[0] = pi->nphy_pwrctrl_info[0].idle_tssi_2g; + idle_tssi[1] = pi->nphy_pwrctrl_info[1].idle_tssi_2g; + a1[0] = -424; + a1[1] = -424; + b0[0] = 5612; + b0[1] = 5612; + b1[1] = -1393; + b1[0] = -1393; + break; + } + } + + /* use the provided transmit power */ + target_pwr_qtrdbm[0] = (s8) pi->tx_power_max; + target_pwr_qtrdbm[1] = (s8) pi->tx_power_max; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + if (pi->srom_fem2g.tssipos) + or_phy_reg(pi, 0x1e9, (0x1 << 14)); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + for (core = 0; core <= 1; core++) { + if (PHY_IPA(pi)) { + if (CHSPEC_IS2G(pi->radio_chanspec)) + WRITE_RADIO_REG3(pi, RADIO_2057, + TX, core, + TX_SSI_MUX, + 0xe); + else + WRITE_RADIO_REG3(pi, RADIO_2057, + TX, core, + TX_SSI_MUX, + 0xc); + } + } + } else { + if (PHY_IPA(pi)) { + + write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | + RADIO_2056_TX0, + (CHSPEC_IS5G + (pi->radio_chanspec)) ? + 0xc : 0xe); + write_radio_reg(pi, + RADIO_2056_TX_TX_SSI_MUX | + RADIO_2056_TX1, + (CHSPEC_IS5G + (pi->radio_chanspec)) ? + 0xc : 0xe); + } else { + + write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | + RADIO_2056_TX0, 0x11); + write_radio_reg(pi, RADIO_2056_TX_TX_SSI_MUX | + RADIO_2056_TX1, 0x11); + } + } + } + + if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) { + wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK); + (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); + udelay(1); + } + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + mod_phy_reg(pi, 0x1e7, (0x7f << 0), + (NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 << 0)); + else + mod_phy_reg(pi, 0x1e7, (0x7f << 0), + (NPHY_TxPwrCtrlCmd_pwrIndex_init << 0)); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + mod_phy_reg(pi, 0x222, (0xff << 0), + (NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 << 0)); + else if (NREV_GT(pi->pubpi.phy_rev, 1)) + mod_phy_reg(pi, 0x222, (0xff << 0), + (NPHY_TxPwrCtrlCmd_pwrIndex_init << 0)); + + if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) + wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0); + + write_phy_reg(pi, 0x1e8, (0x3 << 8) | (240 << 0)); + + write_phy_reg(pi, 0x1e9, + (1 << 15) | (idle_tssi[0] << 0) | (idle_tssi[1] << 8)); + + write_phy_reg(pi, 0x1ea, + (target_pwr_qtrdbm[0] << 0) | + (target_pwr_qtrdbm[1] << 8)); + + tbl_len = 64; + tbl_offset = 0; + for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL; + tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) { + + for (idx = 0; idx < tbl_len; idx++) { + num = 8 * + (16 * b0[tbl_id - 26] + b1[tbl_id - 26] * idx); + den = 32768 + a1[tbl_id - 26] * idx; + pwr_est = max(((4 * num + den / 2) / den), -8); + if (NREV_LT(pi->pubpi.phy_rev, 3)) { + if (idx <= + (uint) (31 - idle_tssi[tbl_id - 26] + 1)) + pwr_est = + max(pwr_est, + target_pwr_qtrdbm + [tbl_id - 26] + 1); + } + regval[idx] = (u32) pwr_est; + } + wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32, + regval); + } + + wlc_phy_txpwr_limit_to_tbl_nphy(pi); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 84, 64, 8, + pi->adj_pwr_tbl_nphy); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 84, 64, 8, + pi->adj_pwr_tbl_nphy); + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +static u32 *wlc_phy_get_ipa_gaintbl_nphy(struct brcms_phy *pi) +{ + u32 *tx_pwrctrl_tbl = NULL; + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if ((pi->pubpi.radiorev == 4) + || (pi->pubpi.radiorev == 6)) + tx_pwrctrl_tbl = + nphy_tpc_txgain_ipa_2g_2057rev4n6; + else if (pi->pubpi.radiorev == 3) + tx_pwrctrl_tbl = + nphy_tpc_txgain_ipa_2g_2057rev3; + else if (pi->pubpi.radiorev == 5) + tx_pwrctrl_tbl = + nphy_tpc_txgain_ipa_2g_2057rev5; + else if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) + tx_pwrctrl_tbl = + nphy_tpc_txgain_ipa_2g_2057rev7; + } else if (NREV_IS(pi->pubpi.phy_rev, 6)) { + tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev6; + if (pi->sh->chip == BCMA_CHIP_ID_BCM47162) + tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev5; + } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { + tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_rev5; + } else { + tx_pwrctrl_tbl = nphy_tpc_txgain_ipa; + } + } else { + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if ((pi->pubpi.radiorev == 3) || + (pi->pubpi.radiorev == 4) || + (pi->pubpi.radiorev == 6)) + tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_5g_2057; + else if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) + tx_pwrctrl_tbl = + nphy_tpc_txgain_ipa_5g_2057rev7; + } else { + tx_pwrctrl_tbl = nphy_tpc_txgain_ipa_5g; + } + } + + return tx_pwrctrl_tbl; +} + +static void wlc_phy_restore_rssical_nphy(struct brcms_phy *pi) +{ + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if (pi->nphy_rssical_chanspec_2G == 0) + return; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0, + RADIO_2057_VCM_MASK, + pi->rssical_cache. + rssical_radio_regs_2G[0]); + mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1, + RADIO_2057_VCM_MASK, + pi->rssical_cache. + rssical_radio_regs_2G[1]); + } else { + mod_radio_reg(pi, + RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0, + RADIO_2056_VCM_MASK, + pi->rssical_cache. + rssical_radio_regs_2G[0]); + mod_radio_reg(pi, + RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1, + RADIO_2056_VCM_MASK, + pi->rssical_cache. + rssical_radio_regs_2G[1]); + } + + write_phy_reg(pi, 0x1a6, + pi->rssical_cache.rssical_phyregs_2G[0]); + write_phy_reg(pi, 0x1ac, + pi->rssical_cache.rssical_phyregs_2G[1]); + write_phy_reg(pi, 0x1b2, + pi->rssical_cache.rssical_phyregs_2G[2]); + write_phy_reg(pi, 0x1b8, + pi->rssical_cache.rssical_phyregs_2G[3]); + write_phy_reg(pi, 0x1a4, + pi->rssical_cache.rssical_phyregs_2G[4]); + write_phy_reg(pi, 0x1aa, + pi->rssical_cache.rssical_phyregs_2G[5]); + write_phy_reg(pi, 0x1b0, + pi->rssical_cache.rssical_phyregs_2G[6]); + write_phy_reg(pi, 0x1b6, + pi->rssical_cache.rssical_phyregs_2G[7]); + write_phy_reg(pi, 0x1a5, + pi->rssical_cache.rssical_phyregs_2G[8]); + write_phy_reg(pi, 0x1ab, + pi->rssical_cache.rssical_phyregs_2G[9]); + write_phy_reg(pi, 0x1b1, + pi->rssical_cache.rssical_phyregs_2G[10]); + write_phy_reg(pi, 0x1b7, + pi->rssical_cache.rssical_phyregs_2G[11]); + + } else { + if (pi->nphy_rssical_chanspec_5G == 0) + return; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0, + RADIO_2057_VCM_MASK, + pi->rssical_cache. + rssical_radio_regs_5G[0]); + mod_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1, + RADIO_2057_VCM_MASK, + pi->rssical_cache. + rssical_radio_regs_5G[1]); + } else { + mod_radio_reg(pi, + RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX0, + RADIO_2056_VCM_MASK, + pi->rssical_cache. + rssical_radio_regs_5G[0]); + mod_radio_reg(pi, + RADIO_2056_RX_RSSI_MISC | RADIO_2056_RX1, + RADIO_2056_VCM_MASK, + pi->rssical_cache. + rssical_radio_regs_5G[1]); + } + + write_phy_reg(pi, 0x1a6, + pi->rssical_cache.rssical_phyregs_5G[0]); + write_phy_reg(pi, 0x1ac, + pi->rssical_cache.rssical_phyregs_5G[1]); + write_phy_reg(pi, 0x1b2, + pi->rssical_cache.rssical_phyregs_5G[2]); + write_phy_reg(pi, 0x1b8, + pi->rssical_cache.rssical_phyregs_5G[3]); + write_phy_reg(pi, 0x1a4, + pi->rssical_cache.rssical_phyregs_5G[4]); + write_phy_reg(pi, 0x1aa, + pi->rssical_cache.rssical_phyregs_5G[5]); + write_phy_reg(pi, 0x1b0, + pi->rssical_cache.rssical_phyregs_5G[6]); + write_phy_reg(pi, 0x1b6, + pi->rssical_cache.rssical_phyregs_5G[7]); + write_phy_reg(pi, 0x1a5, + pi->rssical_cache.rssical_phyregs_5G[8]); + write_phy_reg(pi, 0x1ab, + pi->rssical_cache.rssical_phyregs_5G[9]); + write_phy_reg(pi, 0x1b1, + pi->rssical_cache.rssical_phyregs_5G[10]); + write_phy_reg(pi, 0x1b7, + pi->rssical_cache.rssical_phyregs_5G[11]); + } +} + +static void wlc_phy_internal_cal_txgain_nphy(struct brcms_phy *pi) +{ + u16 txcal_gain[2]; + + pi->nphy_txcal_pwr_idx[0] = pi->nphy_cal_orig_pwr_idx[0]; + pi->nphy_txcal_pwr_idx[1] = pi->nphy_cal_orig_pwr_idx[0]; + wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_cal_orig_pwr_idx[0], true); + wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_cal_orig_pwr_idx[1], true); + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, + txcal_gain); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + txcal_gain[0] = (txcal_gain[0] & 0xF000) | 0x0F40; + txcal_gain[1] = (txcal_gain[1] & 0xF000) | 0x0F40; + } else { + txcal_gain[0] = (txcal_gain[0] & 0xF000) | 0x0F60; + txcal_gain[1] = (txcal_gain[1] & 0xF000) | 0x0F60; + } + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, + txcal_gain); +} + +static void wlc_phy_precal_txgain_nphy(struct brcms_phy *pi) +{ + bool save_bbmult = false; + u8 txcal_index_2057_rev5n7 = 0; + u8 txcal_index_2057_rev3n4n6 = 10; + + if (pi->use_int_tx_iqlo_cal_nphy) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if ((pi->pubpi.radiorev == 3) || + (pi->pubpi.radiorev == 4) || + (pi->pubpi.radiorev == 6)) { + + pi->nphy_txcal_pwr_idx[0] = + txcal_index_2057_rev3n4n6; + pi->nphy_txcal_pwr_idx[1] = + txcal_index_2057_rev3n4n6; + wlc_phy_txpwr_index_nphy( + pi, 3, + txcal_index_2057_rev3n4n6, + false); + } else { + + pi->nphy_txcal_pwr_idx[0] = + txcal_index_2057_rev5n7; + pi->nphy_txcal_pwr_idx[1] = + txcal_index_2057_rev5n7; + wlc_phy_txpwr_index_nphy( + pi, 3, + txcal_index_2057_rev5n7, + false); + } + save_bbmult = true; + + } else if (NREV_LT(pi->pubpi.phy_rev, 5)) { + wlc_phy_cal_txgainctrl_nphy(pi, 11, false); + if (pi->sh->hw_phytxchain != 3) { + pi->nphy_txcal_pwr_idx[1] = + pi->nphy_txcal_pwr_idx[0]; + wlc_phy_txpwr_index_nphy(pi, 3, + pi-> + nphy_txcal_pwr_idx[0], + true); + save_bbmult = true; + } + + } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { + if (PHY_IPA(pi)) { + if (CHSPEC_IS2G(pi->radio_chanspec)) { + wlc_phy_cal_txgainctrl_nphy(pi, 12, + false); + } else { + pi->nphy_txcal_pwr_idx[0] = 80; + pi->nphy_txcal_pwr_idx[1] = 80; + wlc_phy_txpwr_index_nphy(pi, 3, 80, + false); + save_bbmult = true; + } + } else { + wlc_phy_internal_cal_txgain_nphy(pi); + save_bbmult = true; + } + + } else if (NREV_IS(pi->pubpi.phy_rev, 6)) { + if (PHY_IPA(pi)) { + if (CHSPEC_IS2G(pi->radio_chanspec)) + wlc_phy_cal_txgainctrl_nphy(pi, 12, + false); + else + wlc_phy_cal_txgainctrl_nphy(pi, 14, + false); + } else { + wlc_phy_internal_cal_txgain_nphy(pi); + save_bbmult = true; + } + } + + } else { + wlc_phy_cal_txgainctrl_nphy(pi, 10, false); + } + + if (save_bbmult) + wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, + &pi->nphy_txcal_bbmult); +} + +static void +wlc_phy_rfctrlintc_override_nphy(struct brcms_phy *pi, u8 field, u16 value, + u8 core_code) +{ + u16 mask; + u16 val; + u8 core; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + if (core_code == RADIO_MIMO_CORESEL_CORE1 + && core == PHY_CORE_1) + continue; + else if (core_code == RADIO_MIMO_CORESEL_CORE2 + && core == PHY_CORE_0) + continue; + + if (NREV_LT(pi->pubpi.phy_rev, 7)) { + + mask = (0x1 << 10); + val = 1 << 10; + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : + 0x92, mask, val); + } + + if (field == NPHY_RfctrlIntc_override_OFF) { + + write_phy_reg(pi, (core == PHY_CORE_0) ? 0x91 : + 0x92, 0); + + wlc_phy_force_rfseq_nphy(pi, + NPHY_RFSEQ_RESET2RX); + } else if (field == NPHY_RfctrlIntc_override_TRSW) { + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + mask = (0x1 << 6) | (0x1 << 7); + + val = value << 6; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 : 0x92, + mask, val); + + or_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 : 0x92, + (0x1 << 10)); + + and_phy_reg(pi, 0x2ff, (u16) + ~(0x3 << 14)); + or_phy_reg(pi, 0x2ff, (0x1 << 13)); + or_phy_reg(pi, 0x2ff, (0x1 << 0)); + } else { + + mask = (0x1 << 6) | + (0x1 << 7) | + (0x1 << 8) | (0x1 << 9); + val = value << 6; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 : 0x92, + mask, val); + + mask = (0x1 << 0); + val = 1 << 0; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0xe7 : 0xec, + mask, val); + + mask = (core == PHY_CORE_0) ? + (0x1 << 0) : (0x1 << 1); + val = 1 << ((core == PHY_CORE_0) ? + 0 : 1); + mod_phy_reg(pi, 0x78, mask, val); + + SPINWAIT(((read_phy_reg(pi, 0x78) & val) + != 0), 10000); + if (WARN(read_phy_reg(pi, 0x78) & val, + "HW error: override failed")) + return; + + mask = (0x1 << 0); + val = 0 << 0; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0xe7 : 0xec, + mask, val); + } + } else if (field == NPHY_RfctrlIntc_override_PA) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + mask = (0x1 << 4) | (0x1 << 5); + + if (CHSPEC_IS5G(pi->radio_chanspec)) + val = value << 5; + else + val = value << 4; + + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 : 0x92, + mask, val); + + or_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 : 0x92, + (0x1 << 12)); + } else { + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + mask = (0x1 << 5); + val = value << 5; + } else { + mask = (0x1 << 4); + val = value << 4; + } + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 : 0x92, + mask, val); + } + } else if (field == + NPHY_RfctrlIntc_override_EXT_LNA_PU) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + + mask = (0x1 << 0); + val = value << 0; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 + : 0x92, mask, val); + + mask = (0x1 << 2); + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 + : 0x92, mask, 0); + } else { + + mask = (0x1 << 2); + val = value << 2; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 + : 0x92, mask, val); + + mask = (0x1 << 0); + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 + : 0x92, mask, 0); + } + + mask = (0x1 << 11); + val = 1 << 11; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 : 0x92, + mask, val); + } else { + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + mask = (0x1 << 0); + val = value << 0; + } else { + mask = (0x1 << 2); + val = value << 2; + } + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 : 0x92, + mask, val); + } + } else if (field == + NPHY_RfctrlIntc_override_EXT_LNA_GAIN) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + + mask = (0x1 << 1); + val = value << 1; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 + : 0x92, mask, val); + + mask = (0x1 << 3); + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 + : 0x92, mask, 0); + } else { + + mask = (0x1 << 3); + val = value << 3; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 + : 0x92, mask, val); + + mask = (0x1 << 1); + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 + : 0x92, mask, 0); + } + + mask = (0x1 << 11); + val = 1 << 11; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 : 0x92, + mask, val); + } else { + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + mask = (0x1 << 1); + val = value << 1; + } else { + mask = (0x1 << 3); + val = value << 3; + } + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x91 : 0x92, + mask, val); + } + } + } + } +} + +void +wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, s32 dBm_targetpower, + bool debug) +{ + int gainctrl_loopidx; + uint core; + u16 m0m1, curr_m0m1; + s32 delta_power; + s32 txpwrindex; + s32 qdBm_power[2]; + u16 orig_BBConfig; + u16 phy_saveregs[4]; + u32 freq_test; + u16 ampl_test = 250; + uint stepsize; + bool phyhang_avoid_state = false; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + stepsize = 2; + else + stepsize = 1; + + if (CHSPEC_IS40(pi->radio_chanspec)) + freq_test = 5000; + else + freq_test = 2500; + + wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_cal_orig_pwr_idx[0], true); + wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_cal_orig_pwr_idx[1], true); + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + phyhang_avoid_state = pi->phyhang_avoid; + pi->phyhang_avoid = false; + + phy_saveregs[0] = read_phy_reg(pi, 0x91); + phy_saveregs[1] = read_phy_reg(pi, 0x92); + phy_saveregs[2] = read_phy_reg(pi, 0xe7); + phy_saveregs[3] = read_phy_reg(pi, 0xec); + wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_PA, 1, + RADIO_MIMO_CORESEL_CORE1 | + RADIO_MIMO_CORESEL_CORE2); + + if (!debug) { + wlc_phy_rfctrlintc_override_nphy(pi, + NPHY_RfctrlIntc_override_TRSW, + 0x2, RADIO_MIMO_CORESEL_CORE1); + wlc_phy_rfctrlintc_override_nphy(pi, + NPHY_RfctrlIntc_override_TRSW, + 0x8, RADIO_MIMO_CORESEL_CORE2); + } else { + wlc_phy_rfctrlintc_override_nphy(pi, + NPHY_RfctrlIntc_override_TRSW, + 0x1, RADIO_MIMO_CORESEL_CORE1); + wlc_phy_rfctrlintc_override_nphy(pi, + NPHY_RfctrlIntc_override_TRSW, + 0x7, RADIO_MIMO_CORESEL_CORE2); + } + + orig_BBConfig = read_phy_reg(pi, 0x01); + mod_phy_reg(pi, 0x01, (0x1 << 15), 0); + + wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m0m1); + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + txpwrindex = (s32) pi->nphy_cal_orig_pwr_idx[core]; + + for (gainctrl_loopidx = 0; gainctrl_loopidx < 2; + gainctrl_loopidx++) { + wlc_phy_tx_tone_nphy(pi, freq_test, ampl_test, 0, 0, + false); + + if (core == PHY_CORE_0) + curr_m0m1 = m0m1 & 0xff00; + else + curr_m0m1 = m0m1 & 0x00ff; + + wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &curr_m0m1); + wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &curr_m0m1); + + udelay(50); + + wlc_phy_est_tonepwr_nphy(pi, qdBm_power, + NPHY_CAL_TSSISAMPS); + + pi->nphy_bb_mult_save = 0; + wlc_phy_stopplayback_nphy(pi); + + delta_power = (dBm_targetpower * 4) - qdBm_power[core]; + + txpwrindex -= stepsize * delta_power; + if (txpwrindex < 0) + txpwrindex = 0; + else if (txpwrindex > 127) + txpwrindex = 127; + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + if (NREV_IS(pi->pubpi.phy_rev, 4) && + (pi->srom_fem5g.extpagain == 3)) { + if (txpwrindex < 30) + txpwrindex = 30; + } + } else { + if (NREV_GE(pi->pubpi.phy_rev, 5) && + (pi->srom_fem2g.extpagain == 3)) { + if (txpwrindex < 50) + txpwrindex = 50; + } + } + + wlc_phy_txpwr_index_nphy(pi, (1 << core), + (u8) txpwrindex, true); + } + + pi->nphy_txcal_pwr_idx[core] = (u8) txpwrindex; + + if (debug) { + u16 radio_gain; + u16 dbg_m0m1; + + wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &dbg_m0m1); + + wlc_phy_tx_tone_nphy(pi, freq_test, ampl_test, 0, 0, + false); + + wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &dbg_m0m1); + wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &dbg_m0m1); + + udelay(100); + + wlc_phy_est_tonepwr_nphy(pi, qdBm_power, + NPHY_CAL_TSSISAMPS); + + wlc_phy_table_read_nphy(pi, 7, 1, (0x110 + core), 16, + &radio_gain); + + mdelay(4000); + pi->nphy_bb_mult_save = 0; + wlc_phy_stopplayback_nphy(pi); + } + } + + wlc_phy_txpwr_index_nphy(pi, 1, pi->nphy_txcal_pwr_idx[0], true); + wlc_phy_txpwr_index_nphy(pi, 2, pi->nphy_txcal_pwr_idx[1], true); + + wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &pi->nphy_txcal_bbmult); + + write_phy_reg(pi, 0x01, orig_BBConfig); + + write_phy_reg(pi, 0x91, phy_saveregs[0]); + write_phy_reg(pi, 0x92, phy_saveregs[1]); + write_phy_reg(pi, 0xe7, phy_saveregs[2]); + write_phy_reg(pi, 0xec, phy_saveregs[3]); + + pi->phyhang_avoid = phyhang_avoid_state; + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +static void wlc_phy_savecal_nphy(struct brcms_phy *pi) +{ + void *tbl_ptr; + int coreNum; + u16 *txcal_radio_regs = NULL; + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + + wlc_phy_rx_iq_coeffs_nphy(pi, 0, + &pi->calibration_cache. + rxcal_coeffs_2G); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + txcal_radio_regs = + pi->calibration_cache.txcal_radio_regs_2G; + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + + pi->calibration_cache.txcal_radio_regs_2G[0] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_I | + RADIO_2056_TX0); + pi->calibration_cache.txcal_radio_regs_2G[1] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_Q | + RADIO_2056_TX0); + pi->calibration_cache.txcal_radio_regs_2G[2] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_I | + RADIO_2056_TX1); + pi->calibration_cache.txcal_radio_regs_2G[3] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_Q | + RADIO_2056_TX1); + + pi->calibration_cache.txcal_radio_regs_2G[4] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_I | + RADIO_2056_TX0); + pi->calibration_cache.txcal_radio_regs_2G[5] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_Q | + RADIO_2056_TX0); + pi->calibration_cache.txcal_radio_regs_2G[6] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_I | + RADIO_2056_TX1); + pi->calibration_cache.txcal_radio_regs_2G[7] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_Q | + RADIO_2056_TX1); + } else { + pi->calibration_cache.txcal_radio_regs_2G[0] = + read_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL); + pi->calibration_cache.txcal_radio_regs_2G[1] = + read_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL); + pi->calibration_cache.txcal_radio_regs_2G[2] = + read_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM); + pi->calibration_cache.txcal_radio_regs_2G[3] = + read_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM); + } + + pi->nphy_iqcal_chanspec_2G = pi->radio_chanspec; + tbl_ptr = pi->calibration_cache.txcal_coeffs_2G; + } else { + + wlc_phy_rx_iq_coeffs_nphy(pi, 0, + &pi->calibration_cache. + rxcal_coeffs_5G); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + txcal_radio_regs = + pi->calibration_cache.txcal_radio_regs_5G; + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + + pi->calibration_cache.txcal_radio_regs_5G[0] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_I | + RADIO_2056_TX0); + pi->calibration_cache.txcal_radio_regs_5G[1] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_Q | + RADIO_2056_TX0); + pi->calibration_cache.txcal_radio_regs_5G[2] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_I | + RADIO_2056_TX1); + pi->calibration_cache.txcal_radio_regs_5G[3] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_Q | + RADIO_2056_TX1); + + pi->calibration_cache.txcal_radio_regs_5G[4] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_I | + RADIO_2056_TX0); + pi->calibration_cache.txcal_radio_regs_5G[5] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_Q | + RADIO_2056_TX0); + pi->calibration_cache.txcal_radio_regs_5G[6] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_I | + RADIO_2056_TX1); + pi->calibration_cache.txcal_radio_regs_5G[7] = + read_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_Q | + RADIO_2056_TX1); + } else { + pi->calibration_cache.txcal_radio_regs_5G[0] = + read_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL); + pi->calibration_cache.txcal_radio_regs_5G[1] = + read_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL); + pi->calibration_cache.txcal_radio_regs_5G[2] = + read_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM); + pi->calibration_cache.txcal_radio_regs_5G[3] = + read_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM); + } + + pi->nphy_iqcal_chanspec_5G = pi->radio_chanspec; + tbl_ptr = pi->calibration_cache.txcal_coeffs_5G; + } + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + for (coreNum = 0; coreNum <= 1; coreNum++) { + + txcal_radio_regs[2 * coreNum] = + READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, + LOFT_FINE_I); + txcal_radio_regs[2 * coreNum + 1] = + READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, + LOFT_FINE_Q); + + txcal_radio_regs[2 * coreNum + 4] = + READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, + LOFT_COARSE_I); + txcal_radio_regs[2 * coreNum + 5] = + READ_RADIO_REG3(pi, RADIO_2057, TX, coreNum, + LOFT_COARSE_Q); + } + } + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 8, 80, 16, tbl_ptr); + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +static void wlc_phy_tx_iq_war_nphy(struct brcms_phy *pi) +{ + struct nphy_iq_comp tx_comp; + + wlc_phy_table_read_nphy(pi, 15, 4, 0x50, 16, &tx_comp); + + wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ, tx_comp.a0); + wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 2, tx_comp.b0); + wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 4, tx_comp.a1); + wlapi_bmac_write_shm(pi->sh->physhim, M_20IN40_IQ + 6, tx_comp.b1); +} + +static void wlc_phy_restorecal_nphy(struct brcms_phy *pi) +{ + u16 *loft_comp; + u16 txcal_coeffs_bphy[4]; + u16 *tbl_ptr; + int coreNum; + u16 *txcal_radio_regs = NULL; + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if (pi->nphy_iqcal_chanspec_2G == 0) + return; + + tbl_ptr = pi->calibration_cache.txcal_coeffs_2G; + loft_comp = &pi->calibration_cache.txcal_coeffs_2G[5]; + } else { + if (pi->nphy_iqcal_chanspec_5G == 0) + return; + + tbl_ptr = pi->calibration_cache.txcal_coeffs_5G; + loft_comp = &pi->calibration_cache.txcal_coeffs_5G[5]; + } + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 80, 16, tbl_ptr); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + txcal_coeffs_bphy[0] = tbl_ptr[0]; + txcal_coeffs_bphy[1] = tbl_ptr[1]; + txcal_coeffs_bphy[2] = tbl_ptr[2]; + txcal_coeffs_bphy[3] = tbl_ptr[3]; + } else { + txcal_coeffs_bphy[0] = 0; + txcal_coeffs_bphy[1] = 0; + txcal_coeffs_bphy[2] = 0; + txcal_coeffs_bphy[3] = 0; + } + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 88, 16, + txcal_coeffs_bphy); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 85, 16, loft_comp); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 93, 16, loft_comp); + + if (NREV_LT(pi->pubpi.phy_rev, 2)) + wlc_phy_tx_iq_war_nphy(pi); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + txcal_radio_regs = + pi->calibration_cache.txcal_radio_regs_2G; + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + + write_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_I | + RADIO_2056_TX0, + pi->calibration_cache. + txcal_radio_regs_2G[0]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_Q | + RADIO_2056_TX0, + pi->calibration_cache. + txcal_radio_regs_2G[1]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_I | + RADIO_2056_TX1, + pi->calibration_cache. + txcal_radio_regs_2G[2]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_Q | + RADIO_2056_TX1, + pi->calibration_cache. + txcal_radio_regs_2G[3]); + + write_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_I | + RADIO_2056_TX0, + pi->calibration_cache. + txcal_radio_regs_2G[4]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_Q | + RADIO_2056_TX0, + pi->calibration_cache. + txcal_radio_regs_2G[5]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_I | + RADIO_2056_TX1, + pi->calibration_cache. + txcal_radio_regs_2G[6]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_Q | + RADIO_2056_TX1, + pi->calibration_cache. + txcal_radio_regs_2G[7]); + } else { + write_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL, + pi->calibration_cache. + txcal_radio_regs_2G[0]); + write_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL, + pi->calibration_cache. + txcal_radio_regs_2G[1]); + write_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, + pi->calibration_cache. + txcal_radio_regs_2G[2]); + write_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, + pi->calibration_cache. + txcal_radio_regs_2G[3]); + } + + wlc_phy_rx_iq_coeffs_nphy(pi, 1, + &pi->calibration_cache. + rxcal_coeffs_2G); + } else { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + txcal_radio_regs = + pi->calibration_cache.txcal_radio_regs_5G; + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + + write_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_I | + RADIO_2056_TX0, + pi->calibration_cache. + txcal_radio_regs_5G[0]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_Q | + RADIO_2056_TX0, + pi->calibration_cache. + txcal_radio_regs_5G[1]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_I | + RADIO_2056_TX1, + pi->calibration_cache. + txcal_radio_regs_5G[2]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_FINE_Q | + RADIO_2056_TX1, + pi->calibration_cache. + txcal_radio_regs_5G[3]); + + write_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_I | + RADIO_2056_TX0, + pi->calibration_cache. + txcal_radio_regs_5G[4]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_Q | + RADIO_2056_TX0, + pi->calibration_cache. + txcal_radio_regs_5G[5]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_I | + RADIO_2056_TX1, + pi->calibration_cache. + txcal_radio_regs_5G[6]); + write_radio_reg(pi, + RADIO_2056_TX_LOFT_COARSE_Q | + RADIO_2056_TX1, + pi->calibration_cache. + txcal_radio_regs_5G[7]); + } else { + write_radio_reg(pi, RADIO_2055_CORE1_TX_VOS_CNCL, + pi->calibration_cache. + txcal_radio_regs_5G[0]); + write_radio_reg(pi, RADIO_2055_CORE2_TX_VOS_CNCL, + pi->calibration_cache. + txcal_radio_regs_5G[1]); + write_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, + pi->calibration_cache. + txcal_radio_regs_5G[2]); + write_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, + pi->calibration_cache. + txcal_radio_regs_5G[3]); + } + + wlc_phy_rx_iq_coeffs_nphy(pi, 1, + &pi->calibration_cache. + rxcal_coeffs_5G); + } + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + for (coreNum = 0; coreNum <= 1; coreNum++) { + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, + LOFT_FINE_I, + txcal_radio_regs[2 * coreNum]); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, + LOFT_FINE_Q, + txcal_radio_regs[2 * coreNum + 1]); + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, + LOFT_COARSE_I, + txcal_radio_regs[2 * coreNum + 4]); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, coreNum, + LOFT_COARSE_Q, + txcal_radio_regs[2 * coreNum + 5]); + } + } +} + +static void wlc_phy_txpwrctrl_coeff_setup_nphy(struct brcms_phy *pi) +{ + u32 idx; + u16 iqloCalbuf[7]; + u32 iqcomp, locomp, curr_locomp; + s8 locomp_i, locomp_q; + s8 curr_locomp_i, curr_locomp_q; + u32 tbl_id, tbl_len, tbl_offset; + u32 regval[128]; + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + wlc_phy_table_read_nphy(pi, 15, 7, 80, 16, iqloCalbuf); + + tbl_len = 128; + tbl_offset = 320; + for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL; + tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) { + iqcomp = + (tbl_id == + 26) ? (((u32) (iqloCalbuf[0] & 0x3ff)) << 10) | + (iqloCalbuf[1] & 0x3ff) + : (((u32) (iqloCalbuf[2] & 0x3ff)) << 10) | + (iqloCalbuf[3] & 0x3ff); + + for (idx = 0; idx < tbl_len; idx++) + regval[idx] = iqcomp; + wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32, + regval); + } + + tbl_offset = 448; + for (tbl_id = NPHY_TBL_ID_CORE1TXPWRCTL; + tbl_id <= NPHY_TBL_ID_CORE2TXPWRCTL; tbl_id++) { + + locomp = + (u32) ((tbl_id == 26) ? iqloCalbuf[5] : iqloCalbuf[6]); + locomp_i = (s8) ((locomp >> 8) & 0xff); + locomp_q = (s8) ((locomp) & 0xff); + for (idx = 0; idx < tbl_len; idx++) { + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + curr_locomp_i = locomp_i; + curr_locomp_q = locomp_q; + } else { + curr_locomp_i = (s8) ((locomp_i * + nphy_tpc_loscale[idx] + + 128) >> 8); + curr_locomp_q = + (s8) ((locomp_q * + nphy_tpc_loscale[idx] + + 128) >> 8); + } + curr_locomp = (u32) ((curr_locomp_i & 0xff) << 8); + curr_locomp |= (u32) (curr_locomp_q & 0xff); + regval[idx] = curr_locomp; + } + wlc_phy_table_write_nphy(pi, tbl_id, tbl_len, tbl_offset, 32, + regval); + } + + if (NREV_LT(pi->pubpi.phy_rev, 2)) { + + wlapi_bmac_write_shm(pi->sh->physhim, M_CURR_IDX1, 0xFFFF); + wlapi_bmac_write_shm(pi->sh->physhim, M_CURR_IDX2, 0xFFFF); + } + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +static void wlc_phy_txlpfbw_nphy(struct brcms_phy *pi) +{ + u8 tx_lpf_bw = 0; + + if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { + if (CHSPEC_IS40(pi->radio_chanspec)) + tx_lpf_bw = 3; + else + tx_lpf_bw = 1; + + if (PHY_IPA(pi)) { + if (CHSPEC_IS40(pi->radio_chanspec)) + tx_lpf_bw = 5; + else + tx_lpf_bw = 4; + } + + write_phy_reg(pi, 0xe8, + (tx_lpf_bw << 0) | + (tx_lpf_bw << 3) | + (tx_lpf_bw << 6) | (tx_lpf_bw << 9)); + + if (PHY_IPA(pi)) { + + if (CHSPEC_IS40(pi->radio_chanspec)) + tx_lpf_bw = 4; + else + tx_lpf_bw = 1; + + write_phy_reg(pi, 0xe9, + (tx_lpf_bw << 0) | + (tx_lpf_bw << 3) | + (tx_lpf_bw << 6) | (tx_lpf_bw << 9)); + } + } +} + +static void +wlc_phy_adjust_rx_analpfbw_nphy(struct brcms_phy *pi, u16 reduction_factr) +{ + if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LT(pi->pubpi.phy_rev, 7)) { + if ((CHSPEC_CHANNEL(pi->radio_chanspec) == 11) && + CHSPEC_IS40(pi->radio_chanspec)) { + if (!pi->nphy_anarxlpf_adjusted) { + write_radio_reg(pi, + (RADIO_2056_RX_RXLPF_RCCAL_LPC | + RADIO_2056_RX0), + ((pi->nphy_rccal_value + + reduction_factr) | 0x80)); + + pi->nphy_anarxlpf_adjusted = true; + } + } else { + if (pi->nphy_anarxlpf_adjusted) { + write_radio_reg(pi, + (RADIO_2056_RX_RXLPF_RCCAL_LPC | + RADIO_2056_RX0), + (pi->nphy_rccal_value | 0x80)); + + pi->nphy_anarxlpf_adjusted = false; + } + } + } +} + +static void +wlc_phy_adjust_min_noisevar_nphy(struct brcms_phy *pi, int ntones, + int *tone_id_buf, u32 *noise_var_buf) +{ + int i; + u32 offset; + int tone_id; + int tbllen = + CHSPEC_IS40(pi->radio_chanspec) ? + NPHY_NOISEVAR_TBLLEN40 : NPHY_NOISEVAR_TBLLEN20; + + if (pi->nphy_noisevars_adjusted) { + for (i = 0; i < pi->nphy_saved_noisevars.bufcount; i++) { + tone_id = pi->nphy_saved_noisevars.tone_id[i]; + offset = (tone_id >= 0) ? + ((tone_id * + 2) + 1) : (tbllen + (tone_id * 2) + 1); + wlc_phy_table_write_nphy( + pi, NPHY_TBL_ID_NOISEVAR, 1, + offset, 32, + &pi->nphy_saved_noisevars.min_noise_vars[i]); + } + + pi->nphy_saved_noisevars.bufcount = 0; + pi->nphy_noisevars_adjusted = false; + } + + if ((noise_var_buf != NULL) && (tone_id_buf != NULL)) { + pi->nphy_saved_noisevars.bufcount = 0; + + for (i = 0; i < ntones; i++) { + tone_id = tone_id_buf[i]; + offset = (tone_id >= 0) ? + ((tone_id * 2) + 1) : + (tbllen + (tone_id * 2) + 1); + pi->nphy_saved_noisevars.tone_id[i] = tone_id; + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, + offset, 32, + &pi->nphy_saved_noisevars. + min_noise_vars[i]); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_NOISEVAR, 1, + offset, 32, &noise_var_buf[i]); + pi->nphy_saved_noisevars.bufcount++; + } + + pi->nphy_noisevars_adjusted = true; + } +} + +static void wlc_phy_adjust_crsminpwr_nphy(struct brcms_phy *pi, u8 minpwr) +{ + u16 regval; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + if ((CHSPEC_CHANNEL(pi->radio_chanspec) == 11) && + CHSPEC_IS40(pi->radio_chanspec)) { + if (!pi->nphy_crsminpwr_adjusted) { + regval = read_phy_reg(pi, 0x27d); + pi->nphy_crsminpwr[0] = regval & 0xff; + regval &= 0xff00; + regval |= (u16) minpwr; + write_phy_reg(pi, 0x27d, regval); + + regval = read_phy_reg(pi, 0x280); + pi->nphy_crsminpwr[1] = regval & 0xff; + regval &= 0xff00; + regval |= (u16) minpwr; + write_phy_reg(pi, 0x280, regval); + + regval = read_phy_reg(pi, 0x283); + pi->nphy_crsminpwr[2] = regval & 0xff; + regval &= 0xff00; + regval |= (u16) minpwr; + write_phy_reg(pi, 0x283, regval); + + pi->nphy_crsminpwr_adjusted = true; + } + } else { + if (pi->nphy_crsminpwr_adjusted) { + regval = read_phy_reg(pi, 0x27d); + regval &= 0xff00; + regval |= pi->nphy_crsminpwr[0]; + write_phy_reg(pi, 0x27d, regval); + + regval = read_phy_reg(pi, 0x280); + regval &= 0xff00; + regval |= pi->nphy_crsminpwr[1]; + write_phy_reg(pi, 0x280, regval); + + regval = read_phy_reg(pi, 0x283); + regval &= 0xff00; + regval |= pi->nphy_crsminpwr[2]; + write_phy_reg(pi, 0x283, regval); + + pi->nphy_crsminpwr_adjusted = false; + } + } + } +} + +static void wlc_phy_spurwar_nphy(struct brcms_phy *pi) +{ + u16 cur_channel = 0; + int nphy_adj_tone_id_buf[] = { 57, 58 }; + u32 nphy_adj_noise_var_buf[] = { 0x3ff, 0x3ff }; + bool isAdjustNoiseVar = false; + uint numTonesAdjust = 0; + u32 tempval = 0; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + cur_channel = CHSPEC_CHANNEL(pi->radio_chanspec); + + if (pi->nphy_gband_spurwar_en) { + + wlc_phy_adjust_rx_analpfbw_nphy( + pi, + NPHY_ANARXLPFBW_REDUCTIONFACT); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if ((cur_channel == 11) + && CHSPEC_IS40(pi->radio_chanspec)) + wlc_phy_adjust_min_noisevar_nphy( + pi, 2, + nphy_adj_tone_id_buf, + nphy_adj_noise_var_buf); + else + wlc_phy_adjust_min_noisevar_nphy(pi, 0, + NULL, + NULL); + } + + wlc_phy_adjust_crsminpwr_nphy(pi, + NPHY_ADJUSTED_MINCRSPOWER); + } + + if ((pi->nphy_gband_spurwar2_en) + && CHSPEC_IS2G(pi->radio_chanspec)) { + + if (CHSPEC_IS40(pi->radio_chanspec)) { + switch (cur_channel) { + case 3: + nphy_adj_tone_id_buf[0] = 57; + nphy_adj_tone_id_buf[1] = 58; + nphy_adj_noise_var_buf[0] = 0x22f; + nphy_adj_noise_var_buf[1] = 0x25f; + isAdjustNoiseVar = true; + break; + case 4: + nphy_adj_tone_id_buf[0] = 41; + nphy_adj_tone_id_buf[1] = 42; + nphy_adj_noise_var_buf[0] = 0x22f; + nphy_adj_noise_var_buf[1] = 0x25f; + isAdjustNoiseVar = true; + break; + case 5: + nphy_adj_tone_id_buf[0] = 25; + nphy_adj_tone_id_buf[1] = 26; + nphy_adj_noise_var_buf[0] = 0x24f; + nphy_adj_noise_var_buf[1] = 0x25f; + isAdjustNoiseVar = true; + break; + case 6: + nphy_adj_tone_id_buf[0] = 9; + nphy_adj_tone_id_buf[1] = 10; + nphy_adj_noise_var_buf[0] = 0x22f; + nphy_adj_noise_var_buf[1] = 0x24f; + isAdjustNoiseVar = true; + break; + case 7: + nphy_adj_tone_id_buf[0] = 121; + nphy_adj_tone_id_buf[1] = 122; + nphy_adj_noise_var_buf[0] = 0x18f; + nphy_adj_noise_var_buf[1] = 0x24f; + isAdjustNoiseVar = true; + break; + case 8: + nphy_adj_tone_id_buf[0] = 105; + nphy_adj_tone_id_buf[1] = 106; + nphy_adj_noise_var_buf[0] = 0x22f; + nphy_adj_noise_var_buf[1] = 0x25f; + isAdjustNoiseVar = true; + break; + case 9: + nphy_adj_tone_id_buf[0] = 89; + nphy_adj_tone_id_buf[1] = 90; + nphy_adj_noise_var_buf[0] = 0x22f; + nphy_adj_noise_var_buf[1] = 0x24f; + isAdjustNoiseVar = true; + break; + case 10: + nphy_adj_tone_id_buf[0] = 73; + nphy_adj_tone_id_buf[1] = 74; + nphy_adj_noise_var_buf[0] = 0x22f; + nphy_adj_noise_var_buf[1] = 0x24f; + isAdjustNoiseVar = true; + break; + default: + isAdjustNoiseVar = false; + break; + } + } + + if (isAdjustNoiseVar) { + numTonesAdjust = ARRAY_SIZE(nphy_adj_tone_id_buf); + + wlc_phy_adjust_min_noisevar_nphy( + pi, + numTonesAdjust, + nphy_adj_tone_id_buf, + nphy_adj_noise_var_buf); + + tempval = 0; + + } else { + wlc_phy_adjust_min_noisevar_nphy(pi, 0, NULL, + NULL); + } + } + + if ((pi->nphy_aband_spurwar_en) && + (CHSPEC_IS5G(pi->radio_chanspec))) { + switch (cur_channel) { + case 54: + nphy_adj_tone_id_buf[0] = 32; + nphy_adj_noise_var_buf[0] = 0x25f; + break; + case 38: + case 102: + case 118: + if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) && + (pi->sh->chippkg == BCMA_PKG_ID_BCM4717)) { + nphy_adj_tone_id_buf[0] = 32; + nphy_adj_noise_var_buf[0] = 0x21f; + } else { + nphy_adj_tone_id_buf[0] = 0; + nphy_adj_noise_var_buf[0] = 0x0; + } + break; + case 134: + nphy_adj_tone_id_buf[0] = 32; + nphy_adj_noise_var_buf[0] = 0x21f; + break; + case 151: + nphy_adj_tone_id_buf[0] = 16; + nphy_adj_noise_var_buf[0] = 0x23f; + break; + case 153: + case 161: + nphy_adj_tone_id_buf[0] = 48; + nphy_adj_noise_var_buf[0] = 0x23f; + break; + default: + nphy_adj_tone_id_buf[0] = 0; + nphy_adj_noise_var_buf[0] = 0x0; + break; + } + + if (nphy_adj_tone_id_buf[0] + && nphy_adj_noise_var_buf[0]) + wlc_phy_adjust_min_noisevar_nphy( + pi, 1, + nphy_adj_tone_id_buf, + nphy_adj_noise_var_buf); + else + wlc_phy_adjust_min_noisevar_nphy(pi, 0, NULL, + NULL); + } + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); + } +} + +void wlc_phy_init_nphy(struct brcms_phy *pi) +{ + u16 val; + u16 clip1_ths[2]; + struct nphy_txgains target_gain; + u8 tx_pwr_ctrl_state; + bool do_nphy_cal = false; + uint core; + u32 d11_clk_ctl_st; + bool do_rssi_cal = false; + + core = 0; + + if (!(pi->measure_hold & PHY_HOLD_FOR_SCAN)) + pi->measure_hold |= PHY_HOLD_FOR_NOT_ASSOC; + + if ((ISNPHY(pi)) && (NREV_GE(pi->pubpi.phy_rev, 5)) && + ((pi->sh->chippkg == BCMA_PKG_ID_BCM4717) || + (pi->sh->chippkg == BCMA_PKG_ID_BCM4718))) { + if ((pi->sh->boardflags & BFL_EXTLNA) && + (CHSPEC_IS2G(pi->radio_chanspec))) + bcma_cc_set32(&pi->d11core->bus->drv_cc, + BCMA_CC_CHIPCTL, 0x40); + } + + if ((!PHY_IPA(pi)) && (pi->sh->chip == BCMA_CHIP_ID_BCM5357)) + bcma_chipco_chipctl_maskset(&pi->d11core->bus->drv_cc, 1, + ~CCTRL5357_EXTPA, CCTRL5357_EXTPA); + + if ((pi->nphy_gband_spurwar2_en) && CHSPEC_IS2G(pi->radio_chanspec) && + CHSPEC_IS40(pi->radio_chanspec)) { + + d11_clk_ctl_st = bcma_read32(pi->d11core, + D11REGOFFS(clk_ctl_st)); + bcma_mask32(pi->d11core, D11REGOFFS(clk_ctl_st), + ~(CCS_FORCEHT | CCS_HTAREQ)); + + bcma_write32(pi->d11core, D11REGOFFS(clk_ctl_st), + d11_clk_ctl_st); + } + + pi->use_int_tx_iqlo_cal_nphy = + (PHY_IPA(pi) || + (NREV_GE(pi->pubpi.phy_rev, 7) || + (NREV_GE(pi->pubpi.phy_rev, 5) + && pi->sh->boardflags2 & BFL2_INTERNDET_TXIQCAL))); + + pi->internal_tx_iqlo_cal_tapoff_intpa_nphy = false; + + pi->nphy_deaf_count = 0; + + wlc_phy_tbl_init_nphy(pi); + + pi->nphy_crsminpwr_adjusted = false; + pi->nphy_noisevars_adjusted = false; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + write_phy_reg(pi, 0xe7, 0); + write_phy_reg(pi, 0xec, 0); + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + write_phy_reg(pi, 0x342, 0); + write_phy_reg(pi, 0x343, 0); + write_phy_reg(pi, 0x346, 0); + write_phy_reg(pi, 0x347, 0); + } + write_phy_reg(pi, 0xe5, 0); + write_phy_reg(pi, 0xe6, 0); + } else { + write_phy_reg(pi, 0xec, 0); + } + + write_phy_reg(pi, 0x91, 0); + write_phy_reg(pi, 0x92, 0); + if (NREV_LT(pi->pubpi.phy_rev, 6)) { + write_phy_reg(pi, 0x93, 0); + write_phy_reg(pi, 0x94, 0); + } + + and_phy_reg(pi, 0xa1, ~3); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + write_phy_reg(pi, 0x8f, 0); + write_phy_reg(pi, 0xa5, 0); + } else { + write_phy_reg(pi, 0xa5, 0); + } + + if (NREV_IS(pi->pubpi.phy_rev, 2)) + mod_phy_reg(pi, 0xdc, 0x00ff, 0x3b); + else if (NREV_LT(pi->pubpi.phy_rev, 2)) + mod_phy_reg(pi, 0xdc, 0x00ff, 0x40); + + write_phy_reg(pi, 0x203, 32); + write_phy_reg(pi, 0x201, 32); + + if (pi->sh->boardflags2 & BFL2_SKWRKFEM_BRD) + write_phy_reg(pi, 0x20d, 160); + else + write_phy_reg(pi, 0x20d, 184); + + write_phy_reg(pi, 0x13a, 200); + + write_phy_reg(pi, 0x70, 80); + + write_phy_reg(pi, 0x1ff, 48); + + if (NREV_LT(pi->pubpi.phy_rev, 8)) + wlc_phy_update_mimoconfig_nphy(pi, pi->n_preamble_override); + + wlc_phy_stf_chain_upd_nphy(pi); + + if (NREV_LT(pi->pubpi.phy_rev, 2)) { + write_phy_reg(pi, 0x180, 0xaa8); + write_phy_reg(pi, 0x181, 0x9a4); + } + + if (PHY_IPA(pi)) { + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (1) << 0); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x298 : + 0x29c, (0x1ff << 7), + (pi->nphy_papd_epsilon_offset[core]) << 7); + + } + + wlc_phy_ipa_set_tx_digi_filts_nphy(pi); + } else if (NREV_GE(pi->pubpi.phy_rev, 5)) { + wlc_phy_extpa_set_tx_digi_filts_nphy(pi); + } + + wlc_phy_workarounds_nphy(pi); + + wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON); + + val = read_phy_reg(pi, 0x01); + write_phy_reg(pi, 0x01, val | BBCFG_RESETCCA); + write_phy_reg(pi, 0x01, val & (~BBCFG_RESETCCA)); + wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF); + + wlapi_bmac_macphyclk_set(pi->sh->physhim, ON); + + wlc_phy_pa_override_nphy(pi, OFF); + wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX); + wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); + wlc_phy_pa_override_nphy(pi, ON); + + wlc_phy_classifier_nphy(pi, 0, 0); + wlc_phy_clip_det_nphy(pi, 0, clip1_ths); + + if (CHSPEC_IS2G(pi->radio_chanspec)) + wlc_phy_bphy_init_nphy(pi); + + tx_pwr_ctrl_state = pi->nphy_txpwrctrl; + wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); + + wlc_phy_txpwr_fixpower_nphy(pi); + + wlc_phy_txpwrctrl_idle_tssi_nphy(pi); + + wlc_phy_txpwrctrl_pwr_setup_nphy(pi); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + u32 *tx_pwrctrl_tbl = NULL; + u16 idx; + s16 pga_gn = 0; + s16 pad_gn = 0; + s32 rfpwr_offset; + + if (PHY_IPA(pi)) { + tx_pwrctrl_tbl = wlc_phy_get_ipa_gaintbl_nphy(pi); + } else { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + if (NREV_IS(pi->pubpi.phy_rev, 3)) + tx_pwrctrl_tbl = + nphy_tpc_5GHz_txgain_rev3; + else if (NREV_IS(pi->pubpi.phy_rev, 4)) + tx_pwrctrl_tbl = + (pi->srom_fem5g.extpagain == + 3) ? + nphy_tpc_5GHz_txgain_HiPwrEPA : + nphy_tpc_5GHz_txgain_rev4; + else + tx_pwrctrl_tbl = + nphy_tpc_5GHz_txgain_rev5; + } else { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if (pi->pubpi.radiorev == 5) + tx_pwrctrl_tbl = + nphy_tpc_txgain_epa_2057rev5; + else if (pi->pubpi.radiorev == 3) + tx_pwrctrl_tbl = + nphy_tpc_txgain_epa_2057rev3; + } else { + if (NREV_GE(pi->pubpi.phy_rev, 5) && + (pi->srom_fem2g.extpagain == 3)) + tx_pwrctrl_tbl = + nphy_tpc_txgain_HiPwrEPA; + else + tx_pwrctrl_tbl = + nphy_tpc_txgain_rev3; + } + } + } + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 128, + 192, 32, tx_pwrctrl_tbl); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 128, + 192, 32, tx_pwrctrl_tbl); + + pi->nphy_gmval = (u16) ((*tx_pwrctrl_tbl >> 16) & 0x7000); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + for (idx = 0; idx < 128; idx++) { + pga_gn = (tx_pwrctrl_tbl[idx] >> 24) & 0xf; + pad_gn = (tx_pwrctrl_tbl[idx] >> 19) & 0x1f; + rfpwr_offset = get_rf_pwr_offset(pi, pga_gn, + pad_gn); + wlc_phy_table_write_nphy( + pi, + NPHY_TBL_ID_CORE1TXPWRCTL, + 1, 576 + idx, 32, + &rfpwr_offset); + wlc_phy_table_write_nphy( + pi, + NPHY_TBL_ID_CORE2TXPWRCTL, + 1, 576 + idx, 32, + &rfpwr_offset); + } + } else { + + for (idx = 0; idx < 128; idx++) { + pga_gn = (tx_pwrctrl_tbl[idx] >> 24) & 0xf; + if (CHSPEC_IS2G(pi->radio_chanspec)) + rfpwr_offset = (s16) + nphy_papd_pga_gain_delta_ipa_2g + [pga_gn]; + else + rfpwr_offset = (s16) + nphy_papd_pga_gain_delta_ipa_5g + [pga_gn]; + + wlc_phy_table_write_nphy( + pi, + NPHY_TBL_ID_CORE1TXPWRCTL, + 1, 576 + idx, 32, + &rfpwr_offset); + wlc_phy_table_write_nphy( + pi, + NPHY_TBL_ID_CORE2TXPWRCTL, + 1, 576 + idx, 32, + &rfpwr_offset); + } + + } + } else { + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 128, + 192, 32, nphy_tpc_txgain); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 128, + 192, 32, nphy_tpc_txgain); + } + + if (pi->sh->phyrxchain != 0x3) + wlc_phy_rxcore_setstate_nphy((struct brcms_phy_pub *) pi, + pi->sh->phyrxchain); + + if (PHY_PERICAL_MPHASE_PENDING(pi)) + wlc_phy_cal_perical_mphase_restart(pi); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + do_rssi_cal = (CHSPEC_IS2G(pi->radio_chanspec)) ? + (pi->nphy_rssical_chanspec_2G == 0) : + (pi->nphy_rssical_chanspec_5G == 0); + + if (do_rssi_cal) + wlc_phy_rssi_cal_nphy(pi); + else + wlc_phy_restore_rssical_nphy(pi); + } else { + wlc_phy_rssi_cal_nphy(pi); + } + + if (!SCAN_RM_IN_PROGRESS(pi)) + do_nphy_cal = (CHSPEC_IS2G(pi->radio_chanspec)) ? + (pi->nphy_iqcal_chanspec_2G == 0) : + (pi->nphy_iqcal_chanspec_5G == 0); + + if (!pi->do_initcal) + do_nphy_cal = false; + + if (do_nphy_cal) { + + target_gain = wlc_phy_get_tx_gain_nphy(pi); + + if (pi->antsel_type == ANTSEL_2x3) + wlc_phy_antsel_init((struct brcms_phy_pub *) pi, + true); + + if (pi->nphy_perical != PHY_PERICAL_MPHASE) { + wlc_phy_rssi_cal_nphy(pi); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + pi->nphy_cal_orig_pwr_idx[0] = + pi->nphy_txpwrindex[PHY_CORE_0] + . + index_internal; + pi->nphy_cal_orig_pwr_idx[1] = + pi->nphy_txpwrindex[PHY_CORE_1] + . + index_internal; + + wlc_phy_precal_txgain_nphy(pi); + target_gain = + wlc_phy_get_tx_gain_nphy(pi); + } + + if (wlc_phy_cal_txiqlo_nphy + (pi, target_gain, true, + false) == 0) { + if (wlc_phy_cal_rxiq_nphy + (pi, target_gain, 2, + false) == 0) + wlc_phy_savecal_nphy(pi); + + } + } else if (pi->mphase_cal_phase_id == + MPHASE_CAL_STATE_IDLE) { + wlc_phy_cal_perical((struct brcms_phy_pub *) pi, + PHY_PERICAL_PHYINIT); + } + } else { + wlc_phy_restorecal_nphy(pi); + } + + wlc_phy_txpwrctrl_coeff_setup_nphy(pi); + + wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); + + wlc_phy_nphy_tkip_rifs_war(pi, pi->sh->_rifs_phy); + + if (NREV_GE(pi->pubpi.phy_rev, 3) && NREV_LE(pi->pubpi.phy_rev, 6)) + + write_phy_reg(pi, 0x70, 50); + + wlc_phy_txlpfbw_nphy(pi); + + wlc_phy_spurwar_nphy(pi); + +} + +static void wlc_phy_resetcca_nphy(struct brcms_phy *pi) +{ + u16 val; + + wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON); + + val = read_phy_reg(pi, 0x01); + write_phy_reg(pi, 0x01, val | BBCFG_RESETCCA); + udelay(1); + write_phy_reg(pi, 0x01, val & (~BBCFG_RESETCCA)); + + wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF); + + wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); +} + +void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en) +{ + u16 rfctrlintc_override_val; + + if (!en) { + + pi->rfctrlIntc1_save = read_phy_reg(pi, 0x91); + pi->rfctrlIntc2_save = read_phy_reg(pi, 0x92); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + rfctrlintc_override_val = 0x1480; + else if (NREV_GE(pi->pubpi.phy_rev, 3)) + rfctrlintc_override_val = + CHSPEC_IS5G(pi->radio_chanspec) ? 0x600 : 0x480; + else + rfctrlintc_override_val = + CHSPEC_IS5G(pi->radio_chanspec) ? 0x180 : 0x120; + + write_phy_reg(pi, 0x91, rfctrlintc_override_val); + write_phy_reg(pi, 0x92, rfctrlintc_override_val); + } else { + write_phy_reg(pi, 0x91, pi->rfctrlIntc1_save); + write_phy_reg(pi, 0x92, pi->rfctrlIntc2_save); + } + +} + +void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi) +{ + + u16 txrx_chain = + (NPHY_RfseqCoreActv_TxRxChain0 | NPHY_RfseqCoreActv_TxRxChain1); + bool CoreActv_override = false; + + if (pi->nphy_txrx_chain == BRCMS_N_TXRX_CHAIN0) { + txrx_chain = NPHY_RfseqCoreActv_TxRxChain0; + CoreActv_override = true; + + if (NREV_LE(pi->pubpi.phy_rev, 2)) + and_phy_reg(pi, 0xa0, ~0x20); + } else if (pi->nphy_txrx_chain == BRCMS_N_TXRX_CHAIN1) { + txrx_chain = NPHY_RfseqCoreActv_TxRxChain1; + CoreActv_override = true; + + if (NREV_LE(pi->pubpi.phy_rev, 2)) + or_phy_reg(pi, 0xa0, 0x20); + } + + mod_phy_reg(pi, 0xa2, ((0xf << 0) | (0xf << 4)), txrx_chain); + + if (CoreActv_override) { + pi->nphy_perical = PHY_PERICAL_DISABLE; + or_phy_reg(pi, 0xa1, NPHY_RfseqMode_CoreActv_override); + } else { + pi->nphy_perical = PHY_PERICAL_MPHASE; + and_phy_reg(pi, 0xa1, ~NPHY_RfseqMode_CoreActv_override); + } +} + +void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, u8 rxcore_bitmask) +{ + u16 regval; + u16 tbl_buf[16]; + uint i; + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + u16 tbl_opcode; + bool suspend; + + pi->sh->phyrxchain = rxcore_bitmask; + + if (!pi->sh->clk) + return; + + suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!suspend) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + regval = read_phy_reg(pi, 0xa2); + regval &= ~(0xf << 4); + regval |= ((u16) (rxcore_bitmask & 0x3)) << 4; + write_phy_reg(pi, 0xa2, regval); + + if ((rxcore_bitmask & 0x3) != 0x3) { + + write_phy_reg(pi, 0x20e, 1); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + if (pi->rx2tx_biasentry == -1) { + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, + ARRAY_SIZE(tbl_buf), 80, + 16, tbl_buf); + + for (i = 0; i < ARRAY_SIZE(tbl_buf); i++) { + if (tbl_buf[i] == + NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS) { + pi->rx2tx_biasentry = (u8) i; + tbl_opcode = + NPHY_REV3_RFSEQ_CMD_NOP; + wlc_phy_table_write_nphy( + pi, + NPHY_TBL_ID_RFSEQ, + 1, i, + 16, + &tbl_opcode); + break; + } else if (tbl_buf[i] == + NPHY_REV3_RFSEQ_CMD_END) + break; + } + } + } + } else { + + write_phy_reg(pi, 0x20e, 30); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + if (pi->rx2tx_biasentry != -1) { + tbl_opcode = NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, + 1, pi->rx2tx_biasentry, + 16, &tbl_opcode); + pi->rx2tx_biasentry = -1; + } + } + } + + wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); + + if (!suspend) + wlapi_enable_mac(pi->sh->physhim); +} + +u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih) +{ + u16 regval, rxen_bits; + struct brcms_phy *pi = container_of(pih, struct brcms_phy, pubpi_ro); + + regval = read_phy_reg(pi, 0xa2); + rxen_bits = (regval >> 4) & 0xf; + + return (u8) rxen_bits; +} + +bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pi) +{ + return PHY_IPA(pi); +} + +void wlc_phy_cal_init_nphy(struct brcms_phy *pi) +{ +} + +static void wlc_phy_radio_preinit_205x(struct brcms_phy *pi) +{ + + and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU); + and_phy_reg(pi, 0x78, RFCC_OE_POR_FORCE); + + or_phy_reg(pi, 0x78, ~RFCC_OE_POR_FORCE); + or_phy_reg(pi, 0x78, RFCC_CHIP0_PU); + +} + +static void wlc_phy_radio_init_2057(struct brcms_phy *pi) +{ + struct radio_20xx_regs *regs_2057_ptr = NULL; + + if (NREV_IS(pi->pubpi.phy_rev, 7)) { + regs_2057_ptr = regs_2057_rev4; + } else if (NREV_IS(pi->pubpi.phy_rev, 8) + || NREV_IS(pi->pubpi.phy_rev, 9)) { + switch (pi->pubpi.radiorev) { + case 5: + + if (NREV_IS(pi->pubpi.phy_rev, 8)) + regs_2057_ptr = regs_2057_rev5; + else if (NREV_IS(pi->pubpi.phy_rev, 9)) + regs_2057_ptr = regs_2057_rev5v1; + break; + + case 7: + + regs_2057_ptr = regs_2057_rev7; + break; + + case 8: + + regs_2057_ptr = regs_2057_rev8; + break; + + default: + break; + } + } + + wlc_phy_init_radio_regs_allbands(pi, regs_2057_ptr); +} + +static u16 wlc_phy_radio205x_rcal(struct brcms_phy *pi) +{ + u16 rcal_reg = 0; + int i; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + if (pi->pubpi.radiorev == 5) { + + and_phy_reg(pi, 0x342, ~(0x1 << 1)); + + udelay(10); + + mod_radio_reg(pi, RADIO_2057_IQTEST_SEL_PU, 0x1, 0x1); + mod_radio_reg(pi, RADIO_2057v7_IQTEST_SEL_PU2, 0x2, + 0x1); + } + mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x1, 0x1); + + udelay(10); + + mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x3, 0x3); + + for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { + rcal_reg = read_radio_reg(pi, RADIO_2057_RCAL_STATUS); + if (rcal_reg & 0x1) + break; + + udelay(100); + } + + if (WARN(i == MAX_205x_RCAL_WAITLOOPS, + "HW error: radio calib2")) + return 0; + + mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x2, 0x0); + + rcal_reg = read_radio_reg(pi, RADIO_2057_RCAL_STATUS) & 0x3e; + + mod_radio_reg(pi, RADIO_2057_RCAL_CONFIG, 0x1, 0x0); + if (pi->pubpi.radiorev == 5) { + + mod_radio_reg(pi, RADIO_2057_IQTEST_SEL_PU, 0x1, 0x0); + mod_radio_reg(pi, RADIO_2057v7_IQTEST_SEL_PU2, 0x2, + 0x0); + } + + if ((pi->pubpi.radiorev <= 4) || (pi->pubpi.radiorev == 6)) { + + mod_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, 0x3c, + rcal_reg); + mod_radio_reg(pi, RADIO_2057_BANDGAP_RCAL_TRIM, 0xf0, + rcal_reg << 2); + } + + } else if (NREV_IS(pi->pubpi.phy_rev, 3)) { + u16 savereg; + + savereg = + read_radio_reg( + pi, + RADIO_2056_SYN_PLL_MAST2 | + RADIO_2056_SYN); + write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN, + savereg | 0x7); + udelay(10); + + write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, + 0x1); + udelay(10); + + write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, + 0x9); + + for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { + rcal_reg = read_radio_reg( + pi, + RADIO_2056_SYN_RCAL_CODE_OUT | + RADIO_2056_SYN); + if (rcal_reg & 0x80) + break; + + udelay(100); + } + + if (WARN(i == MAX_205x_RCAL_WAITLOOPS, + "HW error: radio calib3")) + return 0; + + write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, + 0x1); + + rcal_reg = + read_radio_reg(pi, + RADIO_2056_SYN_RCAL_CODE_OUT | + RADIO_2056_SYN); + + write_radio_reg(pi, RADIO_2056_SYN_RCAL_MASTER | RADIO_2056_SYN, + 0x0); + + write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2 | RADIO_2056_SYN, + savereg); + + return rcal_reg & 0x1f; + } + return rcal_reg & 0x3e; +} + +static u16 wlc_phy_radio2057_rccal(struct brcms_phy *pi) +{ + u16 rccal_valid; + int i; + bool chip43226_6362A0; + + chip43226_6362A0 = ((pi->pubpi.radiorev == 3) + || (pi->pubpi.radiorev == 4) + || (pi->pubpi.radiorev == 6)); + + rccal_valid = 0; + if (chip43226_6362A0) { + write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x61); + write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xc0); + } else { + write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x61); + + write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xe9); + } + write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e); + write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55); + + for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { + rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP); + if (rccal_valid & 0x2) + break; + + udelay(500); + } + + write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15); + + rccal_valid = 0; + if (chip43226_6362A0) { + write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x69); + write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xb0); + } else { + write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x69); + + write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xd5); + } + write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e); + write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55); + + for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { + rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP); + if (rccal_valid & 0x2) + break; + + udelay(500); + } + + write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15); + + rccal_valid = 0; + if (chip43226_6362A0) { + write_radio_reg(pi, RADIO_2057_RCCAL_MASTER, 0x73); + + write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x28); + write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0xb0); + } else { + write_radio_reg(pi, RADIO_2057v7_RCCAL_MASTER, 0x73); + write_radio_reg(pi, RADIO_2057_RCCAL_X1, 0x6e); + write_radio_reg(pi, RADIO_2057_RCCAL_TRC0, 0x99); + } + write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x55); + + for (i = 0; i < MAX_205x_RCAL_WAITLOOPS; i++) { + rccal_valid = read_radio_reg(pi, RADIO_2057_RCCAL_DONE_OSCCAP); + if (rccal_valid & 0x2) + break; + + udelay(500); + } + + if (WARN(!(rccal_valid & 0x2), "HW error: radio calib4")) + return 0; + + write_radio_reg(pi, RADIO_2057_RCCAL_START_R1_Q1_P1, 0x15); + + return rccal_valid; +} + +static void wlc_phy_radio_postinit_2057(struct brcms_phy *pi) +{ + + mod_radio_reg(pi, RADIO_2057_XTALPUOVR_PINCTRL, 0x1, 0x1); + + mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x78, 0x78); + mod_radio_reg(pi, RADIO_2057_XTAL_CONFIG2, 0x80, 0x80); + mdelay(2); + mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x78, 0x0); + mod_radio_reg(pi, RADIO_2057_XTAL_CONFIG2, 0x80, 0x0); + + if (pi->phy_init_por) { + wlc_phy_radio205x_rcal(pi); + wlc_phy_radio2057_rccal(pi); + } + + mod_radio_reg(pi, RADIO_2057_RFPLL_MASTER, 0x8, 0x0); +} + +static void wlc_phy_radio_init_2056(struct brcms_phy *pi) +{ + const struct radio_regs *regs_SYN_2056_ptr = NULL; + const struct radio_regs *regs_TX_2056_ptr = NULL; + const struct radio_regs *regs_RX_2056_ptr = NULL; + + if (NREV_IS(pi->pubpi.phy_rev, 3)) { + regs_SYN_2056_ptr = regs_SYN_2056; + regs_TX_2056_ptr = regs_TX_2056; + regs_RX_2056_ptr = regs_RX_2056; + } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { + regs_SYN_2056_ptr = regs_SYN_2056_A1; + regs_TX_2056_ptr = regs_TX_2056_A1; + regs_RX_2056_ptr = regs_RX_2056_A1; + } else { + switch (pi->pubpi.radiorev) { + case 5: + regs_SYN_2056_ptr = regs_SYN_2056_rev5; + regs_TX_2056_ptr = regs_TX_2056_rev5; + regs_RX_2056_ptr = regs_RX_2056_rev5; + break; + + case 6: + regs_SYN_2056_ptr = regs_SYN_2056_rev6; + regs_TX_2056_ptr = regs_TX_2056_rev6; + regs_RX_2056_ptr = regs_RX_2056_rev6; + break; + + case 7: + case 9: + regs_SYN_2056_ptr = regs_SYN_2056_rev7; + regs_TX_2056_ptr = regs_TX_2056_rev7; + regs_RX_2056_ptr = regs_RX_2056_rev7; + break; + + case 8: + regs_SYN_2056_ptr = regs_SYN_2056_rev8; + regs_TX_2056_ptr = regs_TX_2056_rev8; + regs_RX_2056_ptr = regs_RX_2056_rev8; + break; + + case 11: + regs_SYN_2056_ptr = regs_SYN_2056_rev11; + regs_TX_2056_ptr = regs_TX_2056_rev11; + regs_RX_2056_ptr = regs_RX_2056_rev11; + break; + + default: + break; + } + } + + wlc_phy_init_radio_regs(pi, regs_SYN_2056_ptr, (u16) RADIO_2056_SYN); + + wlc_phy_init_radio_regs(pi, regs_TX_2056_ptr, (u16) RADIO_2056_TX0); + + wlc_phy_init_radio_regs(pi, regs_TX_2056_ptr, (u16) RADIO_2056_TX1); + + wlc_phy_init_radio_regs(pi, regs_RX_2056_ptr, (u16) RADIO_2056_RX0); + + wlc_phy_init_radio_regs(pi, regs_RX_2056_ptr, (u16) RADIO_2056_RX1); +} + +static void wlc_phy_radio_postinit_2056(struct brcms_phy *pi) +{ + mod_radio_reg(pi, RADIO_2056_SYN_COM_CTRL, 0xb, 0xb); + + mod_radio_reg(pi, RADIO_2056_SYN_COM_PU, 0x2, 0x2); + mod_radio_reg(pi, RADIO_2056_SYN_COM_RESET, 0x2, 0x2); + udelay(1000); + mod_radio_reg(pi, RADIO_2056_SYN_COM_RESET, 0x2, 0x0); + + if ((pi->sh->boardflags2 & BFL2_LEGACY) + || (pi->sh->boardflags2 & BFL2_XTALBUFOUTEN)) + mod_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2, 0xf4, 0x0); + else + mod_radio_reg(pi, RADIO_2056_SYN_PLL_MAST2, 0xfc, 0x0); + + mod_radio_reg(pi, RADIO_2056_SYN_RCCAL_CTRL0, 0x1, 0x0); + + if (pi->phy_init_por) + wlc_phy_radio205x_rcal(pi); +} + +static void wlc_phy_radio_preinit_2055(struct brcms_phy *pi) +{ + + and_phy_reg(pi, 0x78, ~RFCC_POR_FORCE); + or_phy_reg(pi, 0x78, RFCC_CHIP0_PU | RFCC_OE_POR_FORCE); + + or_phy_reg(pi, 0x78, RFCC_POR_FORCE); +} + +static void wlc_phy_radio_init_2055(struct brcms_phy *pi) +{ + wlc_phy_init_radio_regs(pi, regs_2055, RADIO_DEFAULT_CORE); +} + +static void wlc_phy_radio_postinit_2055(struct brcms_phy *pi) +{ + + and_radio_reg(pi, RADIO_2055_MASTER_CNTRL1, + ~(RADIO_2055_JTAGCTRL_MASK | RADIO_2055_JTAGSYNC_MASK)); + + if (((pi->sh->sromrev >= 4) + && !(pi->sh->boardflags2 & BFL2_RXBB_INT_REG_DIS)) + || ((pi->sh->sromrev < 4))) { + and_radio_reg(pi, RADIO_2055_CORE1_RXBB_REGULATOR, 0x7F); + and_radio_reg(pi, RADIO_2055_CORE2_RXBB_REGULATOR, 0x7F); + } + + mod_radio_reg(pi, RADIO_2055_RRCCAL_N_OPT_SEL, 0x3F, 0x2C); + write_radio_reg(pi, RADIO_2055_CAL_MISC, 0x3C); + + and_radio_reg(pi, RADIO_2055_CAL_MISC, + ~(RADIO_2055_RRCAL_START | RADIO_2055_RRCAL_RST_N)); + + or_radio_reg(pi, RADIO_2055_CAL_LPO_CNTRL, RADIO_2055_CAL_LPO_ENABLE); + + or_radio_reg(pi, RADIO_2055_CAL_MISC, RADIO_2055_RRCAL_RST_N); + + udelay(1000); + + or_radio_reg(pi, RADIO_2055_CAL_MISC, RADIO_2055_RRCAL_START); + + SPINWAIT(((read_radio_reg(pi, RADIO_2055_CAL_COUNTER_OUT2) & + RADIO_2055_RCAL_DONE) != RADIO_2055_RCAL_DONE), 2000); + + if (WARN((read_radio_reg(pi, RADIO_2055_CAL_COUNTER_OUT2) & + RADIO_2055_RCAL_DONE) != RADIO_2055_RCAL_DONE, + "HW error: radio calibration1\n")) + return; + + and_radio_reg(pi, RADIO_2055_CAL_LPO_CNTRL, + ~(RADIO_2055_CAL_LPO_ENABLE)); + + wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, pi->radio_chanspec); + + write_radio_reg(pi, RADIO_2055_CORE1_RXBB_LPF, 9); + write_radio_reg(pi, RADIO_2055_CORE2_RXBB_LPF, 9); + + write_radio_reg(pi, RADIO_2055_CORE1_RXBB_MIDAC_HIPAS, 0x83); + write_radio_reg(pi, RADIO_2055_CORE2_RXBB_MIDAC_HIPAS, 0x83); + + mod_radio_reg(pi, RADIO_2055_CORE1_LNA_GAINBST, + RADIO_2055_GAINBST_VAL_MASK, RADIO_2055_GAINBST_CODE); + mod_radio_reg(pi, RADIO_2055_CORE2_LNA_GAINBST, + RADIO_2055_GAINBST_VAL_MASK, RADIO_2055_GAINBST_CODE); + if (pi->nphy_gain_boost) { + and_radio_reg(pi, RADIO_2055_CORE1_RXRF_SPC1, + ~(RADIO_2055_GAINBST_DISABLE)); + and_radio_reg(pi, RADIO_2055_CORE2_RXRF_SPC1, + ~(RADIO_2055_GAINBST_DISABLE)); + } else { + or_radio_reg(pi, RADIO_2055_CORE1_RXRF_SPC1, + RADIO_2055_GAINBST_DISABLE); + or_radio_reg(pi, RADIO_2055_CORE2_RXRF_SPC1, + RADIO_2055_GAINBST_DISABLE); + } + + udelay(2); +} + +void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on) +{ + if (on) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if (!pi->radio_is_on) { + wlc_phy_radio_preinit_205x(pi); + wlc_phy_radio_init_2057(pi); + wlc_phy_radio_postinit_2057(pi); + } + + wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, + pi->radio_chanspec); + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + wlc_phy_radio_preinit_205x(pi); + wlc_phy_radio_init_2056(pi); + wlc_phy_radio_postinit_2056(pi); + + wlc_phy_chanspec_set((struct brcms_phy_pub *) pi, + pi->radio_chanspec); + } else { + wlc_phy_radio_preinit_2055(pi); + wlc_phy_radio_init_2055(pi); + wlc_phy_radio_postinit_2055(pi); + } + + pi->radio_is_on = true; + + } else { + + if (NREV_GE(pi->pubpi.phy_rev, 3) + && NREV_LT(pi->pubpi.phy_rev, 7)) { + and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU); + mod_radio_reg(pi, RADIO_2056_SYN_COM_PU, 0x2, 0x0); + + write_radio_reg(pi, + RADIO_2056_TX_PADA_BOOST_TUNE | + RADIO_2056_TX0, 0); + write_radio_reg(pi, + RADIO_2056_TX_PADG_BOOST_TUNE | + RADIO_2056_TX0, 0); + write_radio_reg(pi, + RADIO_2056_TX_PGAA_BOOST_TUNE | + RADIO_2056_TX0, 0); + write_radio_reg(pi, + RADIO_2056_TX_PGAG_BOOST_TUNE | + RADIO_2056_TX0, 0); + mod_radio_reg(pi, + RADIO_2056_TX_MIXA_BOOST_TUNE | + RADIO_2056_TX0, 0xf0, 0); + write_radio_reg(pi, + RADIO_2056_TX_MIXG_BOOST_TUNE | + RADIO_2056_TX0, 0); + + write_radio_reg(pi, + RADIO_2056_TX_PADA_BOOST_TUNE | + RADIO_2056_TX1, 0); + write_radio_reg(pi, + RADIO_2056_TX_PADG_BOOST_TUNE | + RADIO_2056_TX1, 0); + write_radio_reg(pi, + RADIO_2056_TX_PGAA_BOOST_TUNE | + RADIO_2056_TX1, 0); + write_radio_reg(pi, + RADIO_2056_TX_PGAG_BOOST_TUNE | + RADIO_2056_TX1, 0); + mod_radio_reg(pi, + RADIO_2056_TX_MIXA_BOOST_TUNE | + RADIO_2056_TX1, 0xf0, 0); + write_radio_reg(pi, + RADIO_2056_TX_MIXG_BOOST_TUNE | + RADIO_2056_TX1, 0); + + pi->radio_is_on = false; + } + + if (NREV_GE(pi->pubpi.phy_rev, 8)) { + and_phy_reg(pi, 0x78, ~RFCC_CHIP0_PU); + pi->radio_is_on = false; + } + + } +} + +static bool +wlc_phy_chan2freq_nphy(struct brcms_phy *pi, uint channel, int *f, + const struct chan_info_nphy_radio2057 **t0, + const struct chan_info_nphy_radio205x **t1, + const struct chan_info_nphy_radio2057_rev5 **t2, + const struct chan_info_nphy_2055 **t3) +{ + uint i; + const struct chan_info_nphy_radio2057 *chan_info_tbl_p_0 = NULL; + const struct chan_info_nphy_radio205x *chan_info_tbl_p_1 = NULL; + const struct chan_info_nphy_radio2057_rev5 *chan_info_tbl_p_2 = NULL; + u32 tbl_len = 0; + + int freq = 0; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + if (NREV_IS(pi->pubpi.phy_rev, 7)) { + + chan_info_tbl_p_0 = chan_info_nphyrev7_2057_rev4; + tbl_len = ARRAY_SIZE(chan_info_nphyrev7_2057_rev4); + + } else if (NREV_IS(pi->pubpi.phy_rev, 8) + || NREV_IS(pi->pubpi.phy_rev, 9)) { + switch (pi->pubpi.radiorev) { + + case 5: + + if (pi->pubpi.radiover == 0x0) { + + chan_info_tbl_p_2 = + chan_info_nphyrev8_2057_rev5; + tbl_len = ARRAY_SIZE( + chan_info_nphyrev8_2057_rev5); + + } else if (pi->pubpi.radiover == 0x1) { + + chan_info_tbl_p_2 = + chan_info_nphyrev9_2057_rev5v1; + tbl_len = ARRAY_SIZE( + chan_info_nphyrev9_2057_rev5v1); + + } + break; + + case 7: + chan_info_tbl_p_0 = + chan_info_nphyrev8_2057_rev7; + tbl_len = ARRAY_SIZE( + chan_info_nphyrev8_2057_rev7); + break; + + case 8: + chan_info_tbl_p_0 = + chan_info_nphyrev8_2057_rev8; + tbl_len = ARRAY_SIZE( + chan_info_nphyrev8_2057_rev8); + break; + + default: + break; + } + } else if (NREV_IS(pi->pubpi.phy_rev, 16)) { + + chan_info_tbl_p_0 = chan_info_nphyrev8_2057_rev8; + tbl_len = ARRAY_SIZE(chan_info_nphyrev8_2057_rev8); + } else { + goto fail; + } + + for (i = 0; i < tbl_len; i++) { + if (pi->pubpi.radiorev == 5) { + + if (chan_info_tbl_p_2[i].chan == channel) + break; + } else { + + if (chan_info_tbl_p_0[i].chan == channel) + break; + } + } + + if (i >= tbl_len) + goto fail; + + if (pi->pubpi.radiorev == 5) { + *t2 = &chan_info_tbl_p_2[i]; + freq = chan_info_tbl_p_2[i].freq; + } else { + *t0 = &chan_info_tbl_p_0[i]; + freq = chan_info_tbl_p_0[i].freq; + } + + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + if (NREV_IS(pi->pubpi.phy_rev, 3)) { + chan_info_tbl_p_1 = chan_info_nphyrev3_2056; + tbl_len = ARRAY_SIZE(chan_info_nphyrev3_2056); + } else if (NREV_IS(pi->pubpi.phy_rev, 4)) { + chan_info_tbl_p_1 = chan_info_nphyrev4_2056_A1; + tbl_len = ARRAY_SIZE(chan_info_nphyrev4_2056_A1); + } else if (NREV_IS(pi->pubpi.phy_rev, 5) + || NREV_IS(pi->pubpi.phy_rev, 6)) { + switch (pi->pubpi.radiorev) { + case 5: + chan_info_tbl_p_1 = chan_info_nphyrev5_2056v5; + tbl_len = ARRAY_SIZE(chan_info_nphyrev5_2056v5); + break; + case 6: + chan_info_tbl_p_1 = chan_info_nphyrev6_2056v6; + tbl_len = ARRAY_SIZE(chan_info_nphyrev6_2056v6); + break; + case 7: + case 9: + chan_info_tbl_p_1 = chan_info_nphyrev5n6_2056v7; + tbl_len = + ARRAY_SIZE(chan_info_nphyrev5n6_2056v7); + break; + case 8: + chan_info_tbl_p_1 = chan_info_nphyrev6_2056v8; + tbl_len = ARRAY_SIZE(chan_info_nphyrev6_2056v8); + break; + case 11: + chan_info_tbl_p_1 = chan_info_nphyrev6_2056v11; + tbl_len = ARRAY_SIZE( + chan_info_nphyrev6_2056v11); + break; + default: + break; + } + } + + for (i = 0; i < tbl_len; i++) { + if (chan_info_tbl_p_1[i].chan == channel) + break; + } + + if (i >= tbl_len) + goto fail; + + *t1 = &chan_info_tbl_p_1[i]; + freq = chan_info_tbl_p_1[i].freq; + + } else { + for (i = 0; i < ARRAY_SIZE(chan_info_nphy_2055); i++) + if (chan_info_nphy_2055[i].chan == channel) + break; + + if (i >= ARRAY_SIZE(chan_info_nphy_2055)) + goto fail; + + *t3 = &chan_info_nphy_2055[i]; + freq = chan_info_nphy_2055[i].freq; + } + + *f = freq; + return true; + +fail: + *f = WL_CHAN_FREQ_RANGE_2G; + return false; +} + +u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint channel) +{ + int freq; + const struct chan_info_nphy_radio2057 *t0 = NULL; + const struct chan_info_nphy_radio205x *t1 = NULL; + const struct chan_info_nphy_radio2057_rev5 *t2 = NULL; + const struct chan_info_nphy_2055 *t3 = NULL; + + if (channel == 0) + channel = CHSPEC_CHANNEL(pi->radio_chanspec); + + wlc_phy_chan2freq_nphy(pi, channel, &freq, &t0, &t1, &t2, &t3); + + if (CHSPEC_IS2G(pi->radio_chanspec)) + return WL_CHAN_FREQ_RANGE_2G; + + if ((freq >= BASE_LOW_5G_CHAN) && (freq < BASE_MID_5G_CHAN)) + return WL_CHAN_FREQ_RANGE_5GL; + else if ((freq >= BASE_MID_5G_CHAN) && (freq < BASE_HIGH_5G_CHAN)) + return WL_CHAN_FREQ_RANGE_5GM; + else + return WL_CHAN_FREQ_RANGE_5GH; +} + +static void +wlc_phy_chanspec_radio2055_setup(struct brcms_phy *pi, + const struct chan_info_nphy_2055 *ci) +{ + + write_radio_reg(pi, RADIO_2055_PLL_REF, ci->RF_pll_ref); + write_radio_reg(pi, RADIO_2055_RF_PLL_MOD0, ci->RF_rf_pll_mod0); + write_radio_reg(pi, RADIO_2055_RF_PLL_MOD1, ci->RF_rf_pll_mod1); + write_radio_reg(pi, RADIO_2055_VCO_CAP_TAIL, ci->RF_vco_cap_tail); + + BRCMS_PHY_WAR_PR51571(pi); + + write_radio_reg(pi, RADIO_2055_VCO_CAL1, ci->RF_vco_cal1); + write_radio_reg(pi, RADIO_2055_VCO_CAL2, ci->RF_vco_cal2); + write_radio_reg(pi, RADIO_2055_PLL_LF_C1, ci->RF_pll_lf_c1); + write_radio_reg(pi, RADIO_2055_PLL_LF_R1, ci->RF_pll_lf_r1); + + BRCMS_PHY_WAR_PR51571(pi); + + write_radio_reg(pi, RADIO_2055_PLL_LF_C2, ci->RF_pll_lf_c2); + write_radio_reg(pi, RADIO_2055_LGBUF_CEN_BUF, ci->RF_lgbuf_cen_buf); + write_radio_reg(pi, RADIO_2055_LGEN_TUNE1, ci->RF_lgen_tune1); + write_radio_reg(pi, RADIO_2055_LGEN_TUNE2, ci->RF_lgen_tune2); + + BRCMS_PHY_WAR_PR51571(pi); + + write_radio_reg(pi, RADIO_2055_CORE1_LGBUF_A_TUNE, + ci->RF_core1_lgbuf_a_tune); + write_radio_reg(pi, RADIO_2055_CORE1_LGBUF_G_TUNE, + ci->RF_core1_lgbuf_g_tune); + write_radio_reg(pi, RADIO_2055_CORE1_RXRF_REG1, ci->RF_core1_rxrf_reg1); + write_radio_reg(pi, RADIO_2055_CORE1_TX_PGA_PAD_TN, + ci->RF_core1_tx_pga_pad_tn); + + BRCMS_PHY_WAR_PR51571(pi); + + write_radio_reg(pi, RADIO_2055_CORE1_TX_MX_BGTRIM, + ci->RF_core1_tx_mx_bgtrim); + write_radio_reg(pi, RADIO_2055_CORE2_LGBUF_A_TUNE, + ci->RF_core2_lgbuf_a_tune); + write_radio_reg(pi, RADIO_2055_CORE2_LGBUF_G_TUNE, + ci->RF_core2_lgbuf_g_tune); + write_radio_reg(pi, RADIO_2055_CORE2_RXRF_REG1, ci->RF_core2_rxrf_reg1); + + BRCMS_PHY_WAR_PR51571(pi); + + write_radio_reg(pi, RADIO_2055_CORE2_TX_PGA_PAD_TN, + ci->RF_core2_tx_pga_pad_tn); + write_radio_reg(pi, RADIO_2055_CORE2_TX_MX_BGTRIM, + ci->RF_core2_tx_mx_bgtrim); + + udelay(50); + + write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x05); + write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x45); + + BRCMS_PHY_WAR_PR51571(pi); + + write_radio_reg(pi, RADIO_2055_VCO_CAL10, 0x65); + + udelay(300); +} + +static void +wlc_phy_chanspec_radio2056_setup(struct brcms_phy *pi, + const struct chan_info_nphy_radio205x *ci) +{ + const struct radio_regs *regs_SYN_2056_ptr = NULL; + + write_radio_reg(pi, + RADIO_2056_SYN_PLL_VCOCAL1 | RADIO_2056_SYN, + ci->RF_SYN_pll_vcocal1); + write_radio_reg(pi, RADIO_2056_SYN_PLL_VCOCAL2 | RADIO_2056_SYN, + ci->RF_SYN_pll_vcocal2); + write_radio_reg(pi, RADIO_2056_SYN_PLL_REFDIV | RADIO_2056_SYN, + ci->RF_SYN_pll_refdiv); + write_radio_reg(pi, RADIO_2056_SYN_PLL_MMD2 | RADIO_2056_SYN, + ci->RF_SYN_pll_mmd2); + write_radio_reg(pi, RADIO_2056_SYN_PLL_MMD1 | RADIO_2056_SYN, + ci->RF_SYN_pll_mmd1); + write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER1 | RADIO_2056_SYN, + ci->RF_SYN_pll_loopfilter1); + write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER2 | RADIO_2056_SYN, + ci->RF_SYN_pll_loopfilter2); + write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER3 | RADIO_2056_SYN, + ci->RF_SYN_pll_loopfilter3); + write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER4 | RADIO_2056_SYN, + ci->RF_SYN_pll_loopfilter4); + write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER5 | RADIO_2056_SYN, + ci->RF_SYN_pll_loopfilter5); + write_radio_reg(pi, RADIO_2056_SYN_RESERVED_ADDR27 | RADIO_2056_SYN, + ci->RF_SYN_reserved_addr27); + write_radio_reg(pi, RADIO_2056_SYN_RESERVED_ADDR28 | RADIO_2056_SYN, + ci->RF_SYN_reserved_addr28); + write_radio_reg(pi, RADIO_2056_SYN_RESERVED_ADDR29 | RADIO_2056_SYN, + ci->RF_SYN_reserved_addr29); + write_radio_reg(pi, RADIO_2056_SYN_LOGEN_VCOBUF1 | RADIO_2056_SYN, + ci->RF_SYN_logen_VCOBUF1); + write_radio_reg(pi, RADIO_2056_SYN_LOGEN_MIXER2 | RADIO_2056_SYN, + ci->RF_SYN_logen_MIXER2); + write_radio_reg(pi, RADIO_2056_SYN_LOGEN_BUF3 | RADIO_2056_SYN, + ci->RF_SYN_logen_BUF3); + write_radio_reg(pi, RADIO_2056_SYN_LOGEN_BUF4 | RADIO_2056_SYN, + ci->RF_SYN_logen_BUF4); + + write_radio_reg(pi, + RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX0, + ci->RF_RX0_lnaa_tune); + write_radio_reg(pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX0, + ci->RF_RX0_lnag_tune); + write_radio_reg(pi, RADIO_2056_TX_INTPAA_BOOST_TUNE | RADIO_2056_TX0, + ci->RF_TX0_intpaa_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_INTPAG_BOOST_TUNE | RADIO_2056_TX0, + ci->RF_TX0_intpag_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_PADA_BOOST_TUNE | RADIO_2056_TX0, + ci->RF_TX0_pada_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_PADG_BOOST_TUNE | RADIO_2056_TX0, + ci->RF_TX0_padg_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_PGAA_BOOST_TUNE | RADIO_2056_TX0, + ci->RF_TX0_pgaa_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_PGAG_BOOST_TUNE | RADIO_2056_TX0, + ci->RF_TX0_pgag_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_MIXA_BOOST_TUNE | RADIO_2056_TX0, + ci->RF_TX0_mixa_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_MIXG_BOOST_TUNE | RADIO_2056_TX0, + ci->RF_TX0_mixg_boost_tune); + + write_radio_reg(pi, + RADIO_2056_RX_LNAA_TUNE | RADIO_2056_RX1, + ci->RF_RX1_lnaa_tune); + write_radio_reg(pi, RADIO_2056_RX_LNAG_TUNE | RADIO_2056_RX1, + ci->RF_RX1_lnag_tune); + write_radio_reg(pi, RADIO_2056_TX_INTPAA_BOOST_TUNE | RADIO_2056_TX1, + ci->RF_TX1_intpaa_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_INTPAG_BOOST_TUNE | RADIO_2056_TX1, + ci->RF_TX1_intpag_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_PADA_BOOST_TUNE | RADIO_2056_TX1, + ci->RF_TX1_pada_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_PADG_BOOST_TUNE | RADIO_2056_TX1, + ci->RF_TX1_padg_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_PGAA_BOOST_TUNE | RADIO_2056_TX1, + ci->RF_TX1_pgaa_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_PGAG_BOOST_TUNE | RADIO_2056_TX1, + ci->RF_TX1_pgag_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_MIXA_BOOST_TUNE | RADIO_2056_TX1, + ci->RF_TX1_mixa_boost_tune); + write_radio_reg(pi, RADIO_2056_TX_MIXG_BOOST_TUNE | RADIO_2056_TX1, + ci->RF_TX1_mixg_boost_tune); + + if (NREV_IS(pi->pubpi.phy_rev, 3)) + regs_SYN_2056_ptr = regs_SYN_2056; + else if (NREV_IS(pi->pubpi.phy_rev, 4)) + regs_SYN_2056_ptr = regs_SYN_2056_A1; + else { + switch (pi->pubpi.radiorev) { + case 5: + regs_SYN_2056_ptr = regs_SYN_2056_rev5; + break; + case 6: + regs_SYN_2056_ptr = regs_SYN_2056_rev6; + break; + case 7: + case 9: + regs_SYN_2056_ptr = regs_SYN_2056_rev7; + break; + case 8: + regs_SYN_2056_ptr = regs_SYN_2056_rev8; + break; + case 11: + regs_SYN_2056_ptr = regs_SYN_2056_rev11; + break; + } + } + if (CHSPEC_IS2G(pi->radio_chanspec)) + write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | + RADIO_2056_SYN, + (u16) regs_SYN_2056_ptr[0x49 - 2].init_g); + else + write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | + RADIO_2056_SYN, + (u16) regs_SYN_2056_ptr[0x49 - 2].init_a); + + if (pi->sh->boardflags2 & BFL2_GPLL_WAR) { + if (CHSPEC_IS2G(pi->radio_chanspec)) { + write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER1 | + RADIO_2056_SYN, 0x1f); + write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER2 | + RADIO_2056_SYN, 0x1f); + + if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || + (pi->sh->chip == BCMA_CHIP_ID_BCM47162)) { + write_radio_reg(pi, + RADIO_2056_SYN_PLL_LOOPFILTER4 | + RADIO_2056_SYN, 0x14); + write_radio_reg(pi, + RADIO_2056_SYN_PLL_CP2 | + RADIO_2056_SYN, 0x00); + } else { + write_radio_reg(pi, + RADIO_2056_SYN_PLL_LOOPFILTER4 | + RADIO_2056_SYN, 0xb); + write_radio_reg(pi, + RADIO_2056_SYN_PLL_CP2 | + RADIO_2056_SYN, 0x14); + } + } + } + + if ((pi->sh->boardflags2 & BFL2_GPLL_WAR2) && + (CHSPEC_IS2G(pi->radio_chanspec))) { + write_radio_reg(pi, + RADIO_2056_SYN_PLL_LOOPFILTER1 | RADIO_2056_SYN, + 0x1f); + write_radio_reg(pi, + RADIO_2056_SYN_PLL_LOOPFILTER2 | RADIO_2056_SYN, + 0x1f); + write_radio_reg(pi, + RADIO_2056_SYN_PLL_LOOPFILTER4 | RADIO_2056_SYN, + 0xb); + write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | RADIO_2056_SYN, + 0x20); + } + + if (pi->sh->boardflags2 & BFL2_APLL_WAR) { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER1 | + RADIO_2056_SYN, 0x1f); + write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER2 | + RADIO_2056_SYN, 0x1f); + write_radio_reg(pi, RADIO_2056_SYN_PLL_LOOPFILTER4 | + RADIO_2056_SYN, 0x5); + write_radio_reg(pi, RADIO_2056_SYN_PLL_CP2 | + RADIO_2056_SYN, 0xc); + } + } + + if (PHY_IPA(pi) && CHSPEC_IS2G(pi->radio_chanspec)) { + u16 pag_boost_tune; + u16 padg_boost_tune; + u16 pgag_boost_tune; + u16 mixg_boost_tune; + u16 bias, cascbias; + uint core; + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + + if (NREV_GE(pi->pubpi.phy_rev, 5)) { + + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + PADG_IDAC, 0xcc); + + if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || + (pi->sh->chip == BCMA_CHIP_ID_BCM47162)) { + bias = 0x40; + cascbias = 0x45; + pag_boost_tune = 0x5; + pgag_boost_tune = 0x33; + padg_boost_tune = 0x77; + mixg_boost_tune = 0x55; + } else { + bias = 0x25; + cascbias = 0x20; + + if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224 || + pi->sh->chip == BCMA_CHIP_ID_BCM43225) && + pi->sh->chippkg == BCMA_PKG_ID_BCM43224_FAB_SMIC) { + bias = 0x2a; + cascbias = 0x38; + } + + pag_boost_tune = 0x4; + pgag_boost_tune = 0x03; + padg_boost_tune = 0x77; + mixg_boost_tune = 0x65; + } + + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAG_IMAIN_STAT, bias); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAG_IAUX_STAT, bias); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAG_CASCBIAS, cascbias); + + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAG_BOOST_TUNE, + pag_boost_tune); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + PGAG_BOOST_TUNE, + pgag_boost_tune); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + PADG_BOOST_TUNE, + padg_boost_tune); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + MIXG_BOOST_TUNE, + mixg_boost_tune); + } else { + + bias = (pi->bw == WL_CHANSPEC_BW_40) ? + 0x40 : 0x20; + + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAG_IMAIN_STAT, bias); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAG_IAUX_STAT, bias); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAG_CASCBIAS, 0x30); + } + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, PA_SPARE1, + 0xee); + } + } + + if (PHY_IPA(pi) && NREV_IS(pi->pubpi.phy_rev, 6) + && CHSPEC_IS5G(pi->radio_chanspec)) { + u16 paa_boost_tune; + u16 pada_boost_tune; + u16 pgaa_boost_tune; + u16 mixa_boost_tune; + u16 freq, pabias, cascbias; + uint core; + + freq = CHAN5G_FREQ(CHSPEC_CHANNEL(pi->radio_chanspec)); + + if (freq < 5150) { + + paa_boost_tune = 0xa; + pada_boost_tune = 0x77; + pgaa_boost_tune = 0xf; + mixa_boost_tune = 0xf; + } else if (freq < 5340) { + + paa_boost_tune = 0x8; + pada_boost_tune = 0x77; + pgaa_boost_tune = 0xfb; + mixa_boost_tune = 0xf; + } else if (freq < 5650) { + + paa_boost_tune = 0x0; + pada_boost_tune = 0x77; + pgaa_boost_tune = 0xb; + mixa_boost_tune = 0xf; + } else { + + paa_boost_tune = 0x0; + pada_boost_tune = 0x77; + if (freq != 5825) + pgaa_boost_tune = -(int)(freq - 18) / 36 + 168; + else + pgaa_boost_tune = 6; + + mixa_boost_tune = 0xf; + } + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAA_BOOST_TUNE, paa_boost_tune); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + PADA_BOOST_TUNE, pada_boost_tune); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + PGAA_BOOST_TUNE, pgaa_boost_tune); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + MIXA_BOOST_TUNE, mixa_boost_tune); + + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + TXSPARE1, 0x30); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + PA_SPARE2, 0xee); + + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + PADA_CASCBIAS, 0x3); + + cascbias = 0x30; + + if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224 || + pi->sh->chip == BCMA_CHIP_ID_BCM43225) && + pi->sh->chippkg == BCMA_PKG_ID_BCM43224_FAB_SMIC) + cascbias = 0x35; + + pabias = (pi->phy_pabias == 0) ? 0x30 : pi->phy_pabias; + + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAA_IAUX_STAT, pabias); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAA_IMAIN_STAT, pabias); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAA_CASCBIAS, cascbias); + } + } + + udelay(50); + + wlc_phy_radio205x_vcocal_nphy(pi); +} + +void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi) +{ + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_EN, 0x01, 0x0); + mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x04, 0x0); + mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_CAL_RESETN, 0x04, + (1 << 2)); + mod_radio_reg(pi, RADIO_2057_RFPLL_MISC_EN, 0x01, 0x01); + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + write_radio_reg(pi, RADIO_2056_SYN_PLL_VCOCAL12, 0x0); + write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x38); + write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x18); + write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x38); + write_radio_reg(pi, RADIO_2056_SYN_PLL_MAST3, 0x39); + } + + udelay(300); +} + +static void +wlc_phy_chanspec_radio2057_setup( + struct brcms_phy *pi, + const struct chan_info_nphy_radio2057 *ci, + const struct chan_info_nphy_radio2057_rev5 * + ci2) +{ + int coreNum; + u16 txmix2g_tune_boost_pu = 0; + u16 pad2g_tune_pus = 0; + + if (pi->pubpi.radiorev == 5) { + + write_radio_reg(pi, + RADIO_2057_VCOCAL_COUNTVAL0, + ci2->RF_vcocal_countval0); + write_radio_reg(pi, RADIO_2057_VCOCAL_COUNTVAL1, + ci2->RF_vcocal_countval1); + write_radio_reg(pi, RADIO_2057_RFPLL_REFMASTER_SPAREXTALSIZE, + ci2->RF_rfpll_refmaster_sparextalsize); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, + ci2->RF_rfpll_loopfilter_r1); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, + ci2->RF_rfpll_loopfilter_c2); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, + ci2->RF_rfpll_loopfilter_c1); + write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, + ci2->RF_cp_kpd_idac); + write_radio_reg(pi, RADIO_2057_RFPLL_MMD0, ci2->RF_rfpll_mmd0); + write_radio_reg(pi, RADIO_2057_RFPLL_MMD1, ci2->RF_rfpll_mmd1); + write_radio_reg(pi, + RADIO_2057_VCOBUF_TUNE, ci2->RF_vcobuf_tune); + write_radio_reg(pi, + RADIO_2057_LOGEN_MX2G_TUNE, + ci2->RF_logen_mx2g_tune); + write_radio_reg(pi, RADIO_2057_LOGEN_INDBUF2G_TUNE, + ci2->RF_logen_indbuf2g_tune); + + write_radio_reg(pi, + RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE0, + ci2->RF_txmix2g_tune_boost_pu_core0); + write_radio_reg(pi, + RADIO_2057_PAD2G_TUNE_PUS_CORE0, + ci2->RF_pad2g_tune_pus_core0); + write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE0, + ci2->RF_lna2g_tune_core0); + + write_radio_reg(pi, + RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE1, + ci2->RF_txmix2g_tune_boost_pu_core1); + write_radio_reg(pi, + RADIO_2057_PAD2G_TUNE_PUS_CORE1, + ci2->RF_pad2g_tune_pus_core1); + write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE1, + ci2->RF_lna2g_tune_core1); + + } else { + + write_radio_reg(pi, + RADIO_2057_VCOCAL_COUNTVAL0, + ci->RF_vcocal_countval0); + write_radio_reg(pi, RADIO_2057_VCOCAL_COUNTVAL1, + ci->RF_vcocal_countval1); + write_radio_reg(pi, RADIO_2057_RFPLL_REFMASTER_SPAREXTALSIZE, + ci->RF_rfpll_refmaster_sparextalsize); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, + ci->RF_rfpll_loopfilter_r1); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, + ci->RF_rfpll_loopfilter_c2); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, + ci->RF_rfpll_loopfilter_c1); + write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, ci->RF_cp_kpd_idac); + write_radio_reg(pi, RADIO_2057_RFPLL_MMD0, ci->RF_rfpll_mmd0); + write_radio_reg(pi, RADIO_2057_RFPLL_MMD1, ci->RF_rfpll_mmd1); + write_radio_reg(pi, RADIO_2057_VCOBUF_TUNE, ci->RF_vcobuf_tune); + write_radio_reg(pi, + RADIO_2057_LOGEN_MX2G_TUNE, + ci->RF_logen_mx2g_tune); + write_radio_reg(pi, RADIO_2057_LOGEN_MX5G_TUNE, + ci->RF_logen_mx5g_tune); + write_radio_reg(pi, RADIO_2057_LOGEN_INDBUF2G_TUNE, + ci->RF_logen_indbuf2g_tune); + write_radio_reg(pi, RADIO_2057_LOGEN_INDBUF5G_TUNE, + ci->RF_logen_indbuf5g_tune); + + write_radio_reg(pi, + RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE0, + ci->RF_txmix2g_tune_boost_pu_core0); + write_radio_reg(pi, + RADIO_2057_PAD2G_TUNE_PUS_CORE0, + ci->RF_pad2g_tune_pus_core0); + write_radio_reg(pi, RADIO_2057_PGA_BOOST_TUNE_CORE0, + ci->RF_pga_boost_tune_core0); + write_radio_reg(pi, RADIO_2057_TXMIX5G_BOOST_TUNE_CORE0, + ci->RF_txmix5g_boost_tune_core0); + write_radio_reg(pi, RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE0, + ci->RF_pad5g_tune_misc_pus_core0); + write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE0, + ci->RF_lna2g_tune_core0); + write_radio_reg(pi, RADIO_2057_LNA5G_TUNE_CORE0, + ci->RF_lna5g_tune_core0); + + write_radio_reg(pi, + RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE1, + ci->RF_txmix2g_tune_boost_pu_core1); + write_radio_reg(pi, + RADIO_2057_PAD2G_TUNE_PUS_CORE1, + ci->RF_pad2g_tune_pus_core1); + write_radio_reg(pi, RADIO_2057_PGA_BOOST_TUNE_CORE1, + ci->RF_pga_boost_tune_core1); + write_radio_reg(pi, RADIO_2057_TXMIX5G_BOOST_TUNE_CORE1, + ci->RF_txmix5g_boost_tune_core1); + write_radio_reg(pi, RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE1, + ci->RF_pad5g_tune_misc_pus_core1); + write_radio_reg(pi, RADIO_2057_LNA2G_TUNE_CORE1, + ci->RF_lna2g_tune_core1); + write_radio_reg(pi, RADIO_2057_LNA5G_TUNE_CORE1, + ci->RF_lna5g_tune_core1); + } + + if ((pi->pubpi.radiorev <= 4) || (pi->pubpi.radiorev == 6)) { + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, + 0x3f); + write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x3f); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, + 0x8); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, + 0x8); + } else { + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, + 0x1f); + write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x3f); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, + 0x8); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, + 0x8); + } + } else if ((pi->pubpi.radiorev == 5) || (pi->pubpi.radiorev == 7) || + (pi->pubpi.radiorev == 8)) { + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, + 0x1b); + write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x30); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, + 0xa); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, + 0xa); + } else { + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_R1, + 0x1f); + write_radio_reg(pi, RADIO_2057_CP_KPD_IDAC, 0x3f); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C1, + 0x8); + write_radio_reg(pi, RADIO_2057_RFPLL_LOOPFILTER_C2, + 0x8); + } + + } + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if (PHY_IPA(pi)) { + if (pi->pubpi.radiorev == 3) + txmix2g_tune_boost_pu = 0x6b; + + if (pi->pubpi.radiorev == 5) + pad2g_tune_pus = 0x73; + + } else { + if (pi->pubpi.radiorev != 5) { + pad2g_tune_pus = 0x3; + + txmix2g_tune_boost_pu = 0x61; + } + } + + for (coreNum = 0; coreNum <= 1; coreNum++) { + + if (txmix2g_tune_boost_pu != 0) + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, + TXMIX2G_TUNE_BOOST_PU, + txmix2g_tune_boost_pu); + + if (pad2g_tune_pus != 0) + WRITE_RADIO_REG4(pi, RADIO_2057, CORE, coreNum, + PAD2G_TUNE_PUS, + pad2g_tune_pus); + } + } + + udelay(50); + + wlc_phy_radio205x_vcocal_nphy(pi); +} + +static void +wlc_phy_chanspec_nphy_setup(struct brcms_phy *pi, u16 chanspec, + const struct nphy_sfo_cfg *ci) +{ + u16 val; + + val = read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand; + if (CHSPEC_IS5G(chanspec) && !val) { + + val = bcma_read16(pi->d11core, D11REGOFFS(psm_phy_hdr_param)); + bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), + (val | MAC_PHY_FORCE_CLK)); + + or_phy_reg(pi, (NPHY_TO_BPHY_OFF + BPHY_BB_CONFIG), + (BBCFG_RESETCCA | BBCFG_RESETRX)); + + bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), val); + + or_phy_reg(pi, 0x09, NPHY_BandControl_currentBand); + } else if (!CHSPEC_IS5G(chanspec) && val) { + + and_phy_reg(pi, 0x09, ~NPHY_BandControl_currentBand); + + val = bcma_read16(pi->d11core, D11REGOFFS(psm_phy_hdr_param)); + bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), + (val | MAC_PHY_FORCE_CLK)); + + and_phy_reg(pi, (NPHY_TO_BPHY_OFF + BPHY_BB_CONFIG), + (u16) (~(BBCFG_RESETCCA | BBCFG_RESETRX))); + + bcma_write16(pi->d11core, D11REGOFFS(psm_phy_hdr_param), val); + } + + write_phy_reg(pi, 0x1ce, ci->PHY_BW1a); + write_phy_reg(pi, 0x1cf, ci->PHY_BW2); + write_phy_reg(pi, 0x1d0, ci->PHY_BW3); + + write_phy_reg(pi, 0x1d1, ci->PHY_BW4); + write_phy_reg(pi, 0x1d2, ci->PHY_BW5); + write_phy_reg(pi, 0x1d3, ci->PHY_BW6); + + if (CHSPEC_CHANNEL(pi->radio_chanspec) == 14) { + wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_ofdm_en, 0); + + or_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_TEST, 0x800); + } else { + wlc_phy_classifier_nphy(pi, NPHY_ClassifierCtrl_ofdm_en, + NPHY_ClassifierCtrl_ofdm_en); + + if (CHSPEC_IS2G(chanspec)) + and_phy_reg(pi, NPHY_TO_BPHY_OFF + BPHY_TEST, ~0x840); + } + + if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) + wlc_phy_txpwr_fixpower_nphy(pi); + + if (NREV_LT(pi->pubpi.phy_rev, 3)) + wlc_phy_adjust_lnagaintbl_nphy(pi); + + wlc_phy_txlpfbw_nphy(pi); + + if (NREV_GE(pi->pubpi.phy_rev, 3) + && (pi->phy_spuravoid != SPURAVOID_DISABLE)) { + u8 spuravoid = 0; + + val = CHSPEC_CHANNEL(chanspec); + if (!CHSPEC_IS40(pi->radio_chanspec)) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if ((val == 13) || (val == 14) || (val == 153)) + spuravoid = 1; + } else if (((val >= 5) && (val <= 8)) || (val == 13) + || (val == 14)) { + spuravoid = 1; + } + } else if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if (val == 54) + spuravoid = 1; + } else if (pi->nphy_aband_spurwar_en && + ((val == 38) || (val == 102) || (val == 118))) { + if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) + && (pi->sh->chippkg == BCMA_PKG_ID_BCM4717)) { + spuravoid = 0; + } else { + spuravoid = 1; + } + } + + if (pi->phy_spuravoid == SPURAVOID_FORCEON) + spuravoid = 1; + + if ((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || + (pi->sh->chip == BCMA_CHIP_ID_BCM43225)) { + bcma_pmu_spuravoid_pllupdate(&pi->d11core->bus->drv_cc, + spuravoid); + } else { + wlapi_bmac_core_phypll_ctl(pi->sh->physhim, false); + bcma_pmu_spuravoid_pllupdate(&pi->d11core->bus->drv_cc, + spuravoid); + wlapi_bmac_core_phypll_ctl(pi->sh->physhim, true); + } + + if ((pi->sh->chip == BCMA_CHIP_ID_BCM43224) || + (pi->sh->chip == BCMA_CHIP_ID_BCM43225)) { + if (spuravoid == 1) { + bcma_write16(pi->d11core, + D11REGOFFS(tsf_clk_frac_l), + 0x5341); + bcma_write16(pi->d11core, + D11REGOFFS(tsf_clk_frac_h), 0x8); + } else { + bcma_write16(pi->d11core, + D11REGOFFS(tsf_clk_frac_l), + 0x8889); + bcma_write16(pi->d11core, + D11REGOFFS(tsf_clk_frac_h), 0x8); + } + } + + if (!((pi->sh->chip == BCMA_CHIP_ID_BCM4716) || + (pi->sh->chip == BCMA_CHIP_ID_BCM47162))) + wlapi_bmac_core_phypll_reset(pi->sh->physhim); + + mod_phy_reg(pi, 0x01, (0x1 << 15), + ((spuravoid > 0) ? (0x1 << 15) : 0)); + + wlc_phy_resetcca_nphy(pi); + + pi->phy_isspuravoid = (spuravoid > 0); + } + + if (NREV_LT(pi->pubpi.phy_rev, 7)) + write_phy_reg(pi, 0x17e, 0x3830); + + wlc_phy_spurwar_nphy(pi); +} + +void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, u16 chanspec) +{ + int freq; + const struct chan_info_nphy_radio2057 *t0 = NULL; + const struct chan_info_nphy_radio205x *t1 = NULL; + const struct chan_info_nphy_radio2057_rev5 *t2 = NULL; + const struct chan_info_nphy_2055 *t3 = NULL; + + if (!wlc_phy_chan2freq_nphy + (pi, CHSPEC_CHANNEL(chanspec), &freq, &t0, &t1, &t2, &t3)) + return; + + wlc_phy_chanspec_radio_set((struct brcms_phy_pub *) pi, chanspec); + + if (CHSPEC_BW(chanspec) != pi->bw) + wlapi_bmac_bw_set(pi->sh->physhim, CHSPEC_BW(chanspec)); + + if (CHSPEC_IS40(chanspec)) { + if (CHSPEC_SB_UPPER(chanspec)) { + or_phy_reg(pi, 0xa0, BPHY_BAND_SEL_UP20); + if (NREV_GE(pi->pubpi.phy_rev, 7)) + or_phy_reg(pi, 0x310, PRIM_SEL_UP20); + } else { + and_phy_reg(pi, 0xa0, ~BPHY_BAND_SEL_UP20); + if (NREV_GE(pi->pubpi.phy_rev, 7)) + and_phy_reg(pi, 0x310, + (~PRIM_SEL_UP20 & 0xffff)); + } + } + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + if ((pi->pubpi.radiorev <= 4) + || (pi->pubpi.radiorev == 6)) { + mod_radio_reg(pi, RADIO_2057_TIA_CONFIG_CORE0, + 0x2, + (CHSPEC_IS5G(chanspec) ? (1 << 1) + : 0)); + mod_radio_reg(pi, RADIO_2057_TIA_CONFIG_CORE1, + 0x2, + (CHSPEC_IS5G(chanspec) ? (1 << 1) + : 0)); + } + + wlc_phy_chanspec_radio2057_setup(pi, t0, t2); + wlc_phy_chanspec_nphy_setup(pi, chanspec, + (pi->pubpi.radiorev == 5) ? + (const struct nphy_sfo_cfg *)&(t2->PHY_BW1a) : + (const struct nphy_sfo_cfg *)&(t0->PHY_BW1a)); + + } else { + + mod_radio_reg(pi, + RADIO_2056_SYN_COM_CTRL | RADIO_2056_SYN, + 0x4, + (CHSPEC_IS5G(chanspec) ? (0x1 << 2) : 0)); + wlc_phy_chanspec_radio2056_setup(pi, t1); + + wlc_phy_chanspec_nphy_setup(pi, chanspec, + (const struct nphy_sfo_cfg *) &(t1->PHY_BW1a)); + } + + } else { + + mod_radio_reg(pi, RADIO_2055_MASTER_CNTRL1, 0x70, + (CHSPEC_IS5G(chanspec) ? (0x02 << 4) + : (0x05 << 4))); + + wlc_phy_chanspec_radio2055_setup(pi, t3); + wlc_phy_chanspec_nphy_setup(pi, chanspec, + (const struct nphy_sfo_cfg *) + &(t3->PHY_BW1a)); + } + +} + +void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init) +{ + struct brcms_phy *pi = container_of(ppi, struct brcms_phy, pubpi_ro); + u16 mask = 0xfc00; + u32 mc = 0; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + return; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + u16 v0 = 0x211, v1 = 0x222, v2 = 0x144, v3 = 0x188; + + if (!lut_init) + return; + + if (pi->srom_fem2g.antswctrllut == 0) { + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 1, 0x02, 16, &v0); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 1, 0x03, 16, &v1); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 1, 0x08, 16, &v2); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 1, 0x0C, 16, &v3); + } + + if (pi->srom_fem5g.antswctrllut == 0) { + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 1, 0x12, 16, &v0); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 1, 0x13, 16, &v1); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 1, 0x18, 16, &v2); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_ANTSWCTRLLUT, + 1, 0x1C, 16, &v3); + } + } else { + + write_phy_reg(pi, 0xc8, 0x0); + write_phy_reg(pi, 0xc9, 0x0); + + bcma_chipco_gpio_control(&pi->d11core->bus->drv_cc, mask, mask); + + mc = bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); + mc &= ~MCTL_GPOUT_SEL_MASK; + bcma_write32(pi->d11core, D11REGOFFS(maccontrol), mc); + + bcma_set16(pi->d11core, D11REGOFFS(psm_gpio_oe), mask); + + bcma_mask16(pi->d11core, D11REGOFFS(psm_gpio_out), ~mask); + + if (lut_init) { + write_phy_reg(pi, 0xf8, 0x02d8); + write_phy_reg(pi, 0xf9, 0x0301); + write_phy_reg(pi, 0xfa, 0x02d8); + write_phy_reg(pi, 0xfb, 0x0301); + } + } +} + +u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val) +{ + u16 curr_ctl, new_ctl; + bool suspended = false; + + if (D11REV_IS(pi->sh->corerev, 16)) { + suspended = (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & + MCTL_EN_MAC) ? false : true; + if (!suspended) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + } + + curr_ctl = read_phy_reg(pi, 0xb0) & (0x7 << 0); + + new_ctl = (curr_ctl & (~mask)) | (val & mask); + + mod_phy_reg(pi, 0xb0, (0x7 << 0), new_ctl); + + if (D11REV_IS(pi->sh->corerev, 16) && !suspended) + wlapi_enable_mac(pi->sh->physhim); + + return new_ctl; +} + +void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd) +{ + u16 trigger_mask, status_mask; + u16 orig_RfseqCoreActv; + + switch (cmd) { + case NPHY_RFSEQ_RX2TX: + trigger_mask = NPHY_RfseqTrigger_rx2tx; + status_mask = NPHY_RfseqStatus_rx2tx; + break; + case NPHY_RFSEQ_TX2RX: + trigger_mask = NPHY_RfseqTrigger_tx2rx; + status_mask = NPHY_RfseqStatus_tx2rx; + break; + case NPHY_RFSEQ_RESET2RX: + trigger_mask = NPHY_RfseqTrigger_reset2rx; + status_mask = NPHY_RfseqStatus_reset2rx; + break; + case NPHY_RFSEQ_UPDATEGAINH: + trigger_mask = NPHY_RfseqTrigger_updategainh; + status_mask = NPHY_RfseqStatus_updategainh; + break; + case NPHY_RFSEQ_UPDATEGAINL: + trigger_mask = NPHY_RfseqTrigger_updategainl; + status_mask = NPHY_RfseqStatus_updategainl; + break; + case NPHY_RFSEQ_UPDATEGAINU: + trigger_mask = NPHY_RfseqTrigger_updategainu; + status_mask = NPHY_RfseqStatus_updategainu; + break; + default: + return; + } + + orig_RfseqCoreActv = read_phy_reg(pi, 0xa1); + or_phy_reg(pi, 0xa1, + (NPHY_RfseqMode_CoreActv_override | + NPHY_RfseqMode_Trigger_override)); + or_phy_reg(pi, 0xa3, trigger_mask); + SPINWAIT((read_phy_reg(pi, 0xa4) & status_mask), 200000); + write_phy_reg(pi, 0xa1, orig_RfseqCoreActv); + WARN(read_phy_reg(pi, 0xa4) & status_mask, "HW error in rf"); +} + +static void +wlc_phy_rfctrl_override_1tomany_nphy(struct brcms_phy *pi, u16 cmd, u16 value, + u8 core_mask, u8 off) +{ + u16 rfmxgain = 0, lpfgain = 0; + u16 tgain = 0; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + switch (cmd) { + case NPHY_REV7_RfctrlOverride_cmd_rxrf_pu: + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 5), + value, core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 4), value, + core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 3), value, + core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + break; + case NPHY_REV7_RfctrlOverride_cmd_rx_pu: + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 2), + value, core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 1), value, + core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 0), value, + core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 1), value, + core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID2); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 11), 0, + core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + break; + case NPHY_REV7_RfctrlOverride_cmd_tx_pu: + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 2), + value, core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 1), value, + core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 0), value, + core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID2); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 2), value, + core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID2); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 11), 1, + core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + break; + case NPHY_REV7_RfctrlOverride_cmd_rxgain: + rfmxgain = value & 0x000ff; + lpfgain = value & 0x0ff00; + lpfgain = lpfgain >> 8; + + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 11), + rfmxgain, core_mask, + off, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x3 << 13), + lpfgain, core_mask, + off, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + break; + case NPHY_REV7_RfctrlOverride_cmd_txgain: + tgain = value & 0x7fff; + lpfgain = value & 0x8000; + lpfgain = lpfgain >> 14; + + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 12), + tgain, core_mask, off, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 13), + lpfgain, core_mask, + off, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + break; + } + } +} + +static void +wlc_phy_scale_offset_rssi_nphy(struct brcms_phy *pi, u16 scale, s8 offset, + u8 coresel, u8 rail, u8 rssi_type) +{ + u16 valuetostuff; + + offset = (offset > NPHY_RSSICAL_MAXREAD) ? + NPHY_RSSICAL_MAXREAD : offset; + offset = (offset < (-NPHY_RSSICAL_MAXREAD - 1)) ? + -NPHY_RSSICAL_MAXREAD - 1 : offset; + + valuetostuff = ((scale & 0x3f) << 8) | (offset & 0x3f); + + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_NB)) + write_phy_reg(pi, 0x1a6, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_NB)) + write_phy_reg(pi, 0x1ac, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_NB)) + write_phy_reg(pi, 0x1b2, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_NB)) + write_phy_reg(pi, 0x1b8, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W1)) + write_phy_reg(pi, 0x1a4, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W1)) + write_phy_reg(pi, 0x1aa, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W1)) + write_phy_reg(pi, 0x1b0, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W1)) + write_phy_reg(pi, 0x1b6, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W2)) + write_phy_reg(pi, 0x1a5, valuetostuff); + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W2)) + write_phy_reg(pi, 0x1ab, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_W2)) + write_phy_reg(pi, 0x1b1, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_W2)) + write_phy_reg(pi, 0x1b7, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_TBD)) + write_phy_reg(pi, 0x1a7, valuetostuff); + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_TBD)) + write_phy_reg(pi, 0x1ad, valuetostuff); + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_TBD)) + write_phy_reg(pi, 0x1b3, valuetostuff); + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_TBD)) + write_phy_reg(pi, 0x1b9, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_IQ)) + write_phy_reg(pi, 0x1a8, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_IQ)) + write_phy_reg(pi, 0x1ae, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_I) && (rssi_type == NPHY_RSSI_SEL_IQ)) + write_phy_reg(pi, 0x1b4, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rail == NPHY_RAIL_Q) && (rssi_type == NPHY_RSSI_SEL_IQ)) + write_phy_reg(pi, 0x1ba, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rssi_type == NPHY_RSSI_SEL_TSSI_2G)) + write_phy_reg(pi, 0x1a9, valuetostuff); + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rssi_type == NPHY_RSSI_SEL_TSSI_2G)) + write_phy_reg(pi, 0x1b5, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE1) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rssi_type == NPHY_RSSI_SEL_TSSI_5G)) + write_phy_reg(pi, 0x1af, valuetostuff); + + if (((coresel == RADIO_MIMO_CORESEL_CORE2) || + (coresel == RADIO_MIMO_CORESEL_ALLRX)) && + (rssi_type == NPHY_RSSI_SEL_TSSI_5G)) + write_phy_reg(pi, 0x1bb, valuetostuff); +} + +static void brcms_phy_wr_tx_mux(struct brcms_phy *pi, u8 core) +{ + if (PHY_IPA(pi)) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) + write_radio_reg(pi, + ((core == PHY_CORE_0) ? + RADIO_2057_TX0_TX_SSI_MUX : + RADIO_2057_TX1_TX_SSI_MUX), + (CHSPEC_IS5G(pi->radio_chanspec) ? + 0xc : 0xe)); + else + write_radio_reg(pi, + RADIO_2056_TX_TX_SSI_MUX | + ((core == PHY_CORE_0) ? + RADIO_2056_TX0 : RADIO_2056_TX1), + (CHSPEC_IS5G(pi->radio_chanspec) ? + 0xc : 0xe)); + } else { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + write_radio_reg(pi, + ((core == PHY_CORE_0) ? + RADIO_2057_TX0_TX_SSI_MUX : + RADIO_2057_TX1_TX_SSI_MUX), + 0x11); + + if (pi->pubpi.radioid == BCM2057_ID) + write_radio_reg(pi, + RADIO_2057_IQTEST_SEL_PU, 0x1); + + } else { + write_radio_reg(pi, + RADIO_2056_TX_TX_SSI_MUX | + ((core == PHY_CORE_0) ? + RADIO_2056_TX0 : RADIO_2056_TX1), + 0x11); + } + } +} + +void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core_code, u8 rssi_type) +{ + u16 mask, val; + u16 afectrlovr_rssi_val, rfctrlcmd_rxen_val, rfctrlcmd_coresel_val, + startseq; + u16 rfctrlovr_rssi_val, rfctrlovr_rxen_val, rfctrlovr_coresel_val, + rfctrlovr_trigger_val; + u16 afectrlovr_rssi_mask, rfctrlcmd_mask, rfctrlovr_mask; + u16 rfctrlcmd_val, rfctrlovr_val; + u8 core; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + if (core_code == RADIO_MIMO_CORESEL_OFF) { + mod_phy_reg(pi, 0x8f, (0x1 << 9), 0); + mod_phy_reg(pi, 0xa5, (0x1 << 9), 0); + + mod_phy_reg(pi, 0xa6, (0x3 << 8), 0); + mod_phy_reg(pi, 0xa7, (0x3 << 8), 0); + + mod_phy_reg(pi, 0xe5, (0x1 << 5), 0); + mod_phy_reg(pi, 0xe6, (0x1 << 5), 0); + + mask = (0x1 << 2) | + (0x1 << 3) | (0x1 << 4) | (0x1 << 5); + mod_phy_reg(pi, 0xf9, mask, 0); + mod_phy_reg(pi, 0xfb, mask, 0); + + } else { + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + if (core_code == RADIO_MIMO_CORESEL_CORE1 + && core == PHY_CORE_1) + continue; + else if (core_code == RADIO_MIMO_CORESEL_CORE2 + && core == PHY_CORE_0) + continue; + + mod_phy_reg(pi, (core == PHY_CORE_0) ? + 0x8f : 0xa5, (0x1 << 9), 1 << 9); + + if (rssi_type == NPHY_RSSI_SEL_W1 || + rssi_type == NPHY_RSSI_SEL_W2 || + rssi_type == NPHY_RSSI_SEL_NB) { + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0xa6 : 0xa7, + (0x3 << 8), 0); + + mask = (0x1 << 2) | + (0x1 << 3) | + (0x1 << 4) | (0x1 << 5); + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0xf9 : 0xfb, + mask, 0); + + if (rssi_type == NPHY_RSSI_SEL_W1) { + if (CHSPEC_IS5G( + pi->radio_chanspec)) { + mask = (0x1 << 2); + val = 1 << 2; + } else { + mask = (0x1 << 3); + val = 1 << 3; + } + } else if (rssi_type == + NPHY_RSSI_SEL_W2) { + mask = (0x1 << 4); + val = 1 << 4; + } else { + mask = (0x1 << 5); + val = 1 << 5; + } + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0xf9 : 0xfb, + mask, val); + + mask = (0x1 << 5); + val = 1 << 5; + mod_phy_reg(pi, (core == PHY_CORE_0) ? + 0xe5 : 0xe6, mask, val); + } else { + if (rssi_type == NPHY_RSSI_SEL_TBD) { + mask = (0x3 << 8); + val = 1 << 8; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0xa6 + : 0xa7, mask, val); + mask = (0x3 << 10); + val = 1 << 10; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0xa6 + : 0xa7, mask, val); + } else if (rssi_type == + NPHY_RSSI_SEL_IQ) { + mask = (0x3 << 8); + val = 2 << 8; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0xa6 + : 0xa7, mask, val); + mask = (0x3 << 10); + val = 2 << 10; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0xa6 + : 0xa7, mask, val); + } else { + mask = (0x3 << 8); + val = 3 << 8; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0xa6 + : 0xa7, mask, val); + mask = (0x3 << 10); + val = 3 << 10; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0xa6 + : 0xa7, mask, val); + brcms_phy_wr_tx_mux(pi, core); + afectrlovr_rssi_val = 1 << 9; + mod_phy_reg(pi, + (core == + PHY_CORE_0) ? 0x8f + : 0xa5, (0x1 << 9), + afectrlovr_rssi_val); + } + } + } + } + } else { + + if ((rssi_type == NPHY_RSSI_SEL_W1) || + (rssi_type == NPHY_RSSI_SEL_W2) || + (rssi_type == NPHY_RSSI_SEL_NB)) + val = 0x0; + else if (rssi_type == NPHY_RSSI_SEL_TBD) + val = 0x1; + else if (rssi_type == NPHY_RSSI_SEL_IQ) + val = 0x2; + else + val = 0x3; + + mask = ((0x3 << 12) | (0x3 << 14)); + val = (val << 12) | (val << 14); + mod_phy_reg(pi, 0xa6, mask, val); + mod_phy_reg(pi, 0xa7, mask, val); + + if ((rssi_type == NPHY_RSSI_SEL_W1) || + (rssi_type == NPHY_RSSI_SEL_W2) || + (rssi_type == NPHY_RSSI_SEL_NB)) { + if (rssi_type == NPHY_RSSI_SEL_W1) + val = 0x1; + if (rssi_type == NPHY_RSSI_SEL_W2) + val = 0x2; + if (rssi_type == NPHY_RSSI_SEL_NB) + val = 0x3; + + mask = (0x3 << 4); + val = (val << 4); + mod_phy_reg(pi, 0x7a, mask, val); + mod_phy_reg(pi, 0x7d, mask, val); + } + + if (core_code == RADIO_MIMO_CORESEL_OFF) { + afectrlovr_rssi_val = 0; + rfctrlcmd_rxen_val = 0; + rfctrlcmd_coresel_val = 0; + rfctrlovr_rssi_val = 0; + rfctrlovr_rxen_val = 0; + rfctrlovr_coresel_val = 0; + rfctrlovr_trigger_val = 0; + startseq = 0; + } else { + afectrlovr_rssi_val = 1; + rfctrlcmd_rxen_val = 1; + rfctrlcmd_coresel_val = core_code; + rfctrlovr_rssi_val = 1; + rfctrlovr_rxen_val = 1; + rfctrlovr_coresel_val = 1; + rfctrlovr_trigger_val = 1; + startseq = 1; + } + + afectrlovr_rssi_mask = ((0x1 << 12) | (0x1 << 13)); + afectrlovr_rssi_val = (afectrlovr_rssi_val << + 12) | (afectrlovr_rssi_val << 13); + mod_phy_reg(pi, 0xa5, afectrlovr_rssi_mask, + afectrlovr_rssi_val); + + if ((rssi_type == NPHY_RSSI_SEL_W1) || + (rssi_type == NPHY_RSSI_SEL_W2) || + (rssi_type == NPHY_RSSI_SEL_NB)) { + rfctrlcmd_mask = ((0x1 << 8) | (0x7 << 3)); + rfctrlcmd_val = (rfctrlcmd_rxen_val << 8) | + (rfctrlcmd_coresel_val << 3); + + rfctrlovr_mask = ((0x1 << 5) | + (0x1 << 12) | + (0x1 << 1) | (0x1 << 0)); + rfctrlovr_val = (rfctrlovr_rssi_val << + 5) | + (rfctrlovr_rxen_val << 12) | + (rfctrlovr_coresel_val << 1) | + (rfctrlovr_trigger_val << 0); + + mod_phy_reg(pi, 0x78, rfctrlcmd_mask, rfctrlcmd_val); + mod_phy_reg(pi, 0xec, rfctrlovr_mask, rfctrlovr_val); + + mod_phy_reg(pi, 0x78, (0x1 << 0), (startseq << 0)); + udelay(20); + + mod_phy_reg(pi, 0xec, (0x1 << 0), 0); + } + } +} + +int +wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type, s32 *rssi_buf, + u8 nsamps) +{ + s16 rssi0, rssi1; + u16 afectrlCore1_save = 0; + u16 afectrlCore2_save = 0; + u16 afectrlOverride1_save = 0; + u16 afectrlOverride2_save = 0; + u16 rfctrlOverrideAux0_save = 0; + u16 rfctrlOverrideAux1_save = 0; + u16 rfctrlMiscReg1_save = 0; + u16 rfctrlMiscReg2_save = 0; + u16 rfctrlcmd_save = 0; + u16 rfctrloverride_save = 0; + u16 rfctrlrssiothers1_save = 0; + u16 rfctrlrssiothers2_save = 0; + s8 tmp_buf[4]; + u8 ctr = 0, samp = 0; + s32 rssi_out_val; + u16 gpiosel_orig; + + afectrlCore1_save = read_phy_reg(pi, 0xa6); + afectrlCore2_save = read_phy_reg(pi, 0xa7); + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + rfctrlMiscReg1_save = read_phy_reg(pi, 0xf9); + rfctrlMiscReg2_save = read_phy_reg(pi, 0xfb); + afectrlOverride1_save = read_phy_reg(pi, 0x8f); + afectrlOverride2_save = read_phy_reg(pi, 0xa5); + rfctrlOverrideAux0_save = read_phy_reg(pi, 0xe5); + rfctrlOverrideAux1_save = read_phy_reg(pi, 0xe6); + } else { + afectrlOverride1_save = read_phy_reg(pi, 0xa5); + rfctrlcmd_save = read_phy_reg(pi, 0x78); + rfctrloverride_save = read_phy_reg(pi, 0xec); + rfctrlrssiothers1_save = read_phy_reg(pi, 0x7a); + rfctrlrssiothers2_save = read_phy_reg(pi, 0x7d); + } + + wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_ALLRX, rssi_type); + + gpiosel_orig = read_phy_reg(pi, 0xca); + if (NREV_LT(pi->pubpi.phy_rev, 2)) + write_phy_reg(pi, 0xca, 5); + + for (ctr = 0; ctr < 4; ctr++) + rssi_buf[ctr] = 0; + + for (samp = 0; samp < nsamps; samp++) { + if (NREV_LT(pi->pubpi.phy_rev, 2)) { + rssi0 = read_phy_reg(pi, 0x1c9); + rssi1 = read_phy_reg(pi, 0x1ca); + } else { + rssi0 = read_phy_reg(pi, 0x219); + rssi1 = read_phy_reg(pi, 0x21a); + } + + ctr = 0; + tmp_buf[ctr++] = ((s8) ((rssi0 & 0x3f) << 2)) >> 2; + tmp_buf[ctr++] = ((s8) (((rssi0 >> 8) & 0x3f) << 2)) >> 2; + tmp_buf[ctr++] = ((s8) ((rssi1 & 0x3f) << 2)) >> 2; + tmp_buf[ctr++] = ((s8) (((rssi1 >> 8) & 0x3f) << 2)) >> 2; + + for (ctr = 0; ctr < 4; ctr++) + rssi_buf[ctr] += tmp_buf[ctr]; + + } + + rssi_out_val = rssi_buf[3] & 0xff; + rssi_out_val |= (rssi_buf[2] & 0xff) << 8; + rssi_out_val |= (rssi_buf[1] & 0xff) << 16; + rssi_out_val |= (rssi_buf[0] & 0xff) << 24; + + if (NREV_LT(pi->pubpi.phy_rev, 2)) + write_phy_reg(pi, 0xca, gpiosel_orig); + + write_phy_reg(pi, 0xa6, afectrlCore1_save); + write_phy_reg(pi, 0xa7, afectrlCore2_save); + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + write_phy_reg(pi, 0xf9, rfctrlMiscReg1_save); + write_phy_reg(pi, 0xfb, rfctrlMiscReg2_save); + write_phy_reg(pi, 0x8f, afectrlOverride1_save); + write_phy_reg(pi, 0xa5, afectrlOverride2_save); + write_phy_reg(pi, 0xe5, rfctrlOverrideAux0_save); + write_phy_reg(pi, 0xe6, rfctrlOverrideAux1_save); + } else { + write_phy_reg(pi, 0xa5, afectrlOverride1_save); + write_phy_reg(pi, 0x78, rfctrlcmd_save); + write_phy_reg(pi, 0xec, rfctrloverride_save); + write_phy_reg(pi, 0x7a, rfctrlrssiothers1_save); + write_phy_reg(pi, 0x7d, rfctrlrssiothers2_save); + } + + return rssi_out_val; +} + +s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi) +{ + u16 core1_txrf_iqcal1_save, core1_txrf_iqcal2_save; + u16 core2_txrf_iqcal1_save, core2_txrf_iqcal2_save; + u16 pwrdet_rxtx_core1_save; + u16 pwrdet_rxtx_core2_save; + u16 afectrlCore1_save; + u16 afectrlCore2_save; + u16 afectrlOverride_save; + u16 afectrlOverride2_save; + u16 pd_pll_ts_save; + u16 gpioSel_save; + s32 radio_temp[4]; + s32 radio_temp2[4]; + u16 syn_tempprocsense_save; + s16 offset = 0; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + u16 auxADC_Vmid, auxADC_Av, auxADC_Vmid_save, auxADC_Av_save; + u16 auxADC_rssi_ctrlL_save, auxADC_rssi_ctrlH_save; + u16 auxADC_rssi_ctrlL, auxADC_rssi_ctrlH; + s32 auxADC_Vl; + u16 RfctrlOverride5_save, RfctrlOverride6_save; + u16 RfctrlMiscReg5_save, RfctrlMiscReg6_save; + u16 RSSIMultCoef0QPowerDet_save; + u16 tempsense_Rcal; + + syn_tempprocsense_save = + read_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG); + + afectrlCore1_save = read_phy_reg(pi, 0xa6); + afectrlCore2_save = read_phy_reg(pi, 0xa7); + afectrlOverride_save = read_phy_reg(pi, 0x8f); + afectrlOverride2_save = read_phy_reg(pi, 0xa5); + RSSIMultCoef0QPowerDet_save = read_phy_reg(pi, 0x1ae); + RfctrlOverride5_save = read_phy_reg(pi, 0x346); + RfctrlOverride6_save = read_phy_reg(pi, 0x347); + RfctrlMiscReg5_save = read_phy_reg(pi, 0x344); + RfctrlMiscReg6_save = read_phy_reg(pi, 0x345); + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, + &auxADC_Vmid_save); + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, + &auxADC_Av_save); + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x02, 16, + &auxADC_rssi_ctrlL_save); + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x03, 16, + &auxADC_rssi_ctrlH_save); + + write_phy_reg(pi, 0x1ae, 0x0); + + auxADC_rssi_ctrlL = 0x0; + auxADC_rssi_ctrlH = 0x20; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x02, 16, + &auxADC_rssi_ctrlL); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x03, 16, + &auxADC_rssi_ctrlH); + + tempsense_Rcal = syn_tempprocsense_save & 0x1c; + + write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, + tempsense_Rcal | 0x01); + + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), + 1, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID2); + mod_phy_reg(pi, 0xa6, (0x1 << 7), 0); + mod_phy_reg(pi, 0xa7, (0x1 << 7), 0); + mod_phy_reg(pi, 0x8f, (0x1 << 7), (0x1 << 7)); + mod_phy_reg(pi, 0xa5, (0x1 << 7), (0x1 << 7)); + + mod_phy_reg(pi, 0xa6, (0x1 << 2), (0x1 << 2)); + mod_phy_reg(pi, 0xa7, (0x1 << 2), (0x1 << 2)); + mod_phy_reg(pi, 0x8f, (0x1 << 2), (0x1 << 2)); + mod_phy_reg(pi, 0xa5, (0x1 << 2), (0x1 << 2)); + udelay(5); + mod_phy_reg(pi, 0xa6, (0x1 << 2), 0); + mod_phy_reg(pi, 0xa7, (0x1 << 2), 0); + mod_phy_reg(pi, 0xa6, (0x1 << 3), 0); + mod_phy_reg(pi, 0xa7, (0x1 << 3), 0); + mod_phy_reg(pi, 0x8f, (0x1 << 3), (0x1 << 3)); + mod_phy_reg(pi, 0xa5, (0x1 << 3), (0x1 << 3)); + mod_phy_reg(pi, 0xa6, (0x1 << 6), 0); + mod_phy_reg(pi, 0xa7, (0x1 << 6), 0); + mod_phy_reg(pi, 0x8f, (0x1 << 6), (0x1 << 6)); + mod_phy_reg(pi, 0xa5, (0x1 << 6), (0x1 << 6)); + + auxADC_Vmid = 0xA3; + auxADC_Av = 0x0; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, + &auxADC_Vmid); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, + &auxADC_Av); + + udelay(3); + + wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); + write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, + tempsense_Rcal | 0x03); + + udelay(5); + wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); + + auxADC_Av = 0x7; + if (radio_temp[1] + radio_temp2[1] < -30) { + auxADC_Vmid = 0x45; + auxADC_Vl = 263; + } else if (radio_temp[1] + radio_temp2[1] < -9) { + auxADC_Vmid = 0x200; + auxADC_Vl = 467; + } else if (radio_temp[1] + radio_temp2[1] < 11) { + auxADC_Vmid = 0x266; + auxADC_Vl = 634; + } else { + auxADC_Vmid = 0x2D5; + auxADC_Vl = 816; + } + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, + &auxADC_Vmid); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, + &auxADC_Av); + + udelay(3); + + wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); + write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, + tempsense_Rcal | 0x01); + + udelay(5); + wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); + + write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, + syn_tempprocsense_save); + + write_phy_reg(pi, 0xa6, afectrlCore1_save); + write_phy_reg(pi, 0xa7, afectrlCore2_save); + write_phy_reg(pi, 0x8f, afectrlOverride_save); + write_phy_reg(pi, 0xa5, afectrlOverride2_save); + write_phy_reg(pi, 0x1ae, RSSIMultCoef0QPowerDet_save); + write_phy_reg(pi, 0x346, RfctrlOverride5_save); + write_phy_reg(pi, 0x347, RfctrlOverride6_save); + write_phy_reg(pi, 0x344, RfctrlMiscReg5_save); + write_phy_reg(pi, 0x345, RfctrlMiscReg5_save); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0A, 16, + &auxADC_Vmid_save); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x0E, 16, + &auxADC_Av_save); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x02, 16, + &auxADC_rssi_ctrlL_save); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 0x03, 16, + &auxADC_rssi_ctrlH_save); + + if (pi->sh->chip == BCMA_CHIP_ID_BCM5357) { + radio_temp[0] = (193 * (radio_temp[1] + radio_temp2[1]) + + 88 * (auxADC_Vl) - 27111 + + 128) / 256; + } else { + radio_temp[0] = (179 * (radio_temp[1] + radio_temp2[1]) + + 82 * (auxADC_Vl) - 28861 + + 128) / 256; + } + + offset = (s16) pi->phy_tempsense_offset; + + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + syn_tempprocsense_save = + read_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE); + + afectrlCore1_save = read_phy_reg(pi, 0xa6); + afectrlCore2_save = read_phy_reg(pi, 0xa7); + afectrlOverride_save = read_phy_reg(pi, 0x8f); + afectrlOverride2_save = read_phy_reg(pi, 0xa5); + gpioSel_save = read_phy_reg(pi, 0xca); + + write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x01); + + wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); + if (NREV_LT(pi->pubpi.phy_rev, 7)) + write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x05); + + wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); + if (NREV_GE(pi->pubpi.phy_rev, 7)) + write_radio_reg(pi, RADIO_2057_TEMPSENSE_CONFIG, 0x01); + else + write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, 0x01); + + radio_temp[0] = + (126 * (radio_temp[1] + radio_temp2[1]) + 3987) / 64; + + write_radio_reg(pi, RADIO_2056_SYN_TEMPPROCSENSE, + syn_tempprocsense_save); + + write_phy_reg(pi, 0xca, gpioSel_save); + write_phy_reg(pi, 0xa6, afectrlCore1_save); + write_phy_reg(pi, 0xa7, afectrlCore2_save); + write_phy_reg(pi, 0x8f, afectrlOverride_save); + write_phy_reg(pi, 0xa5, afectrlOverride2_save); + + offset = (s16) pi->phy_tempsense_offset; + } else { + + pwrdet_rxtx_core1_save = + read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1); + pwrdet_rxtx_core2_save = + read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2); + core1_txrf_iqcal1_save = + read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1); + core1_txrf_iqcal2_save = + read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2); + core2_txrf_iqcal1_save = + read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1); + core2_txrf_iqcal2_save = + read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2); + pd_pll_ts_save = read_radio_reg(pi, RADIO_2055_PD_PLL_TS); + + afectrlCore1_save = read_phy_reg(pi, 0xa6); + afectrlCore2_save = read_phy_reg(pi, 0xa7); + afectrlOverride_save = read_phy_reg(pi, 0xa5); + gpioSel_save = read_phy_reg(pi, 0xca); + + write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, 0x01); + write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, 0x01); + write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, 0x08); + write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, 0x08); + write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, 0x04); + write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, 0x04); + write_radio_reg(pi, RADIO_2055_PD_PLL_TS, 0x00); + + wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); + xor_radio_reg(pi, RADIO_2055_CAL_TS, 0x80); + + wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp, 1); + xor_radio_reg(pi, RADIO_2055_CAL_TS, 0x80); + + wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_IQ, radio_temp2, 1); + xor_radio_reg(pi, RADIO_2055_CAL_TS, 0x80); + + radio_temp[0] = (radio_temp[0] + radio_temp2[0]); + radio_temp[1] = (radio_temp[1] + radio_temp2[1]); + radio_temp[2] = (radio_temp[2] + radio_temp2[2]); + radio_temp[3] = (radio_temp[3] + radio_temp2[3]); + + radio_temp[0] = + (radio_temp[0] + radio_temp[1] + radio_temp[2] + + radio_temp[3]); + + radio_temp[0] = + (radio_temp[0] + + (8 * 32)) * (950 - 350) / 63 + (350 * 8); + + radio_temp[0] = (radio_temp[0] - (8 * 420)) / 38; + + write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, + pwrdet_rxtx_core1_save); + write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, + pwrdet_rxtx_core2_save); + write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, + core1_txrf_iqcal1_save); + write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, + core2_txrf_iqcal1_save); + write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, + core1_txrf_iqcal2_save); + write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, + core2_txrf_iqcal2_save); + write_radio_reg(pi, RADIO_2055_PD_PLL_TS, pd_pll_ts_save); + + write_phy_reg(pi, 0xca, gpioSel_save); + write_phy_reg(pi, 0xa6, afectrlCore1_save); + write_phy_reg(pi, 0xa7, afectrlCore2_save); + write_phy_reg(pi, 0xa5, afectrlOverride_save); + } + + return (s16) radio_temp[0] + offset; +} + +static void +wlc_phy_set_rssi_2055_vcm(struct brcms_phy *pi, u8 rssi_type, u8 *vcm_buf) +{ + u8 core; + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + if (rssi_type == NPHY_RSSI_SEL_NB) { + if (core == PHY_CORE_0) { + mod_radio_reg(pi, + RADIO_2055_CORE1_B0_NBRSSI_VCM, + RADIO_2055_NBRSSI_VCM_I_MASK, + vcm_buf[2 * + core] << + RADIO_2055_NBRSSI_VCM_I_SHIFT); + mod_radio_reg(pi, + RADIO_2055_CORE1_RXBB_RSSI_CTRL5, + RADIO_2055_NBRSSI_VCM_Q_MASK, + vcm_buf[2 * core + + 1] << + RADIO_2055_NBRSSI_VCM_Q_SHIFT); + } else { + mod_radio_reg(pi, + RADIO_2055_CORE2_B0_NBRSSI_VCM, + RADIO_2055_NBRSSI_VCM_I_MASK, + vcm_buf[2 * + core] << + RADIO_2055_NBRSSI_VCM_I_SHIFT); + mod_radio_reg(pi, + RADIO_2055_CORE2_RXBB_RSSI_CTRL5, + RADIO_2055_NBRSSI_VCM_Q_MASK, + vcm_buf[2 * core + + 1] << + RADIO_2055_NBRSSI_VCM_Q_SHIFT); + } + } else { + if (core == PHY_CORE_0) + mod_radio_reg(pi, + RADIO_2055_CORE1_RXBB_RSSI_CTRL5, + RADIO_2055_WBRSSI_VCM_IQ_MASK, + vcm_buf[2 * + core] << + RADIO_2055_WBRSSI_VCM_IQ_SHIFT); + else + mod_radio_reg(pi, + RADIO_2055_CORE2_RXBB_RSSI_CTRL5, + RADIO_2055_WBRSSI_VCM_IQ_MASK, + vcm_buf[2 * + core] << + RADIO_2055_WBRSSI_VCM_IQ_SHIFT); + } + } +} + +static void wlc_phy_rssi_cal_nphy_rev3(struct brcms_phy *pi) +{ + u16 classif_state; + u16 clip_state[2]; + u16 clip_off[] = { 0xffff, 0xffff }; + s32 target_code; + u8 vcm, min_vcm; + u8 vcm_final = 0; + u8 result_idx; + s32 poll_results[8][4] = { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }; + s32 poll_result_core[4] = { 0, 0, 0, 0 }; + s32 min_d = NPHY_RSSICAL_MAXD, curr_d; + s32 fine_digital_offset[4]; + s32 poll_results_min[4] = { 0, 0, 0, 0 }; + s32 min_poll; + u8 vcm_level_max; + u8 core; + u8 wb_cnt; + u8 rssi_type; + u16 NPHY_Rfctrlintc1_save, NPHY_Rfctrlintc2_save; + u16 NPHY_AfectrlOverride1_save, NPHY_AfectrlOverride2_save; + u16 NPHY_AfectrlCore1_save, NPHY_AfectrlCore2_save; + u16 NPHY_RfctrlOverride0_save, NPHY_RfctrlOverride1_save; + u16 NPHY_RfctrlOverrideAux0_save, NPHY_RfctrlOverrideAux1_save; + u16 NPHY_RfctrlCmd_save; + u16 NPHY_RfctrlMiscReg1_save, NPHY_RfctrlMiscReg2_save; + u16 NPHY_RfctrlRSSIOTHERS1_save, NPHY_RfctrlRSSIOTHERS2_save; + u8 rxcore_state; + u16 NPHY_REV7_RfctrlOverride3_save, NPHY_REV7_RfctrlOverride4_save; + u16 NPHY_REV7_RfctrlOverride5_save, NPHY_REV7_RfctrlOverride6_save; + u16 NPHY_REV7_RfctrlMiscReg3_save, NPHY_REV7_RfctrlMiscReg4_save; + u16 NPHY_REV7_RfctrlMiscReg5_save, NPHY_REV7_RfctrlMiscReg6_save; + + NPHY_REV7_RfctrlOverride3_save = + NPHY_REV7_RfctrlOverride4_save = + NPHY_REV7_RfctrlOverride5_save = + NPHY_REV7_RfctrlOverride6_save = + NPHY_REV7_RfctrlMiscReg3_save = + NPHY_REV7_RfctrlMiscReg4_save = + NPHY_REV7_RfctrlMiscReg5_save = + NPHY_REV7_RfctrlMiscReg6_save = 0; + + classif_state = wlc_phy_classifier_nphy(pi, 0, 0); + wlc_phy_classifier_nphy(pi, (0x7 << 0), 4); + wlc_phy_clip_det_nphy(pi, 0, clip_state); + wlc_phy_clip_det_nphy(pi, 1, clip_off); + + NPHY_Rfctrlintc1_save = read_phy_reg(pi, 0x91); + NPHY_Rfctrlintc2_save = read_phy_reg(pi, 0x92); + NPHY_AfectrlOverride1_save = read_phy_reg(pi, 0x8f); + NPHY_AfectrlOverride2_save = read_phy_reg(pi, 0xa5); + NPHY_AfectrlCore1_save = read_phy_reg(pi, 0xa6); + NPHY_AfectrlCore2_save = read_phy_reg(pi, 0xa7); + NPHY_RfctrlOverride0_save = read_phy_reg(pi, 0xe7); + NPHY_RfctrlOverride1_save = read_phy_reg(pi, 0xec); + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + NPHY_REV7_RfctrlOverride3_save = read_phy_reg(pi, 0x342); + NPHY_REV7_RfctrlOverride4_save = read_phy_reg(pi, 0x343); + NPHY_REV7_RfctrlOverride5_save = read_phy_reg(pi, 0x346); + NPHY_REV7_RfctrlOverride6_save = read_phy_reg(pi, 0x347); + } + NPHY_RfctrlOverrideAux0_save = read_phy_reg(pi, 0xe5); + NPHY_RfctrlOverrideAux1_save = read_phy_reg(pi, 0xe6); + NPHY_RfctrlCmd_save = read_phy_reg(pi, 0x78); + NPHY_RfctrlMiscReg1_save = read_phy_reg(pi, 0xf9); + NPHY_RfctrlMiscReg2_save = read_phy_reg(pi, 0xfb); + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + NPHY_REV7_RfctrlMiscReg3_save = read_phy_reg(pi, 0x340); + NPHY_REV7_RfctrlMiscReg4_save = read_phy_reg(pi, 0x341); + NPHY_REV7_RfctrlMiscReg5_save = read_phy_reg(pi, 0x344); + NPHY_REV7_RfctrlMiscReg6_save = read_phy_reg(pi, 0x345); + } + NPHY_RfctrlRSSIOTHERS1_save = read_phy_reg(pi, 0x7a); + NPHY_RfctrlRSSIOTHERS2_save = read_phy_reg(pi, 0x7d); + + wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_OFF, 0, + RADIO_MIMO_CORESEL_ALLRXTX); + wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_TRSW, 1, + RADIO_MIMO_CORESEL_ALLRXTX); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + wlc_phy_rfctrl_override_1tomany_nphy( + pi, + NPHY_REV7_RfctrlOverride_cmd_rxrf_pu, + 0, 0, 0); + else + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0, 0); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + wlc_phy_rfctrl_override_1tomany_nphy( + pi, + NPHY_REV7_RfctrlOverride_cmd_rx_pu, + 1, 0, 0); + else + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 1, 0, 0); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7), + 1, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 6), 1, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + } else { + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 7), 1, 0, 0); + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 6), 1, 0, 0); + } + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 5), + 0, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 4), 1, 0, + 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + } else { + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 5), 0, 0, 0); + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 4), 1, 0, 0); + } + + } else { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 4), + 0, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 5), 1, 0, + 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + } else { + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 4), 0, 0, 0); + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 5), 1, 0, 0); + } + } + + rxcore_state = wlc_phy_rxcore_getstate_nphy( + (struct brcms_phy_pub *) pi); + + vcm_level_max = 8; + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + + if ((rxcore_state & (1 << core)) == 0) + continue; + + wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, + core == + PHY_CORE_0 ? + RADIO_MIMO_CORESEL_CORE1 : + RADIO_MIMO_CORESEL_CORE2, + NPHY_RAIL_I, NPHY_RSSI_SEL_NB); + wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, + core == + PHY_CORE_0 ? + RADIO_MIMO_CORESEL_CORE1 : + RADIO_MIMO_CORESEL_CORE2, + NPHY_RAIL_Q, NPHY_RSSI_SEL_NB); + + for (vcm = 0; vcm < vcm_level_max; vcm++) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) + mod_radio_reg(pi, (core == PHY_CORE_0) ? + RADIO_2057_NB_MASTER_CORE0 : + RADIO_2057_NB_MASTER_CORE1, + RADIO_2057_VCM_MASK, vcm); + else + mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | + ((core == + PHY_CORE_0) ? RADIO_2056_RX0 : + RADIO_2056_RX1), + RADIO_2056_VCM_MASK, + vcm << RADIO_2056_RSSI_VCM_SHIFT); + + wlc_phy_poll_rssi_nphy(pi, NPHY_RSSI_SEL_NB, + &poll_results[vcm][0], + NPHY_RSSICAL_NPOLL); + } + + for (result_idx = 0; result_idx < 4; result_idx++) { + if ((core == result_idx / 2) && + (result_idx % 2 == 0)) { + + min_d = NPHY_RSSICAL_MAXD; + min_vcm = 0; + min_poll = + NPHY_RSSICAL_MAXREAD * + NPHY_RSSICAL_NPOLL + 1; + for (vcm = 0; vcm < vcm_level_max; vcm++) { + curr_d = + poll_results[vcm][result_idx] * + poll_results[vcm][result_idx] + + poll_results[vcm][result_idx + + 1] * + poll_results[vcm][result_idx + + 1]; + if (curr_d < min_d) { + min_d = curr_d; + min_vcm = vcm; + } + if (poll_results[vcm][result_idx] < + min_poll) + min_poll = + poll_results[vcm] + [result_idx]; + } + vcm_final = min_vcm; + poll_results_min[result_idx] = min_poll; + } + } + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + mod_radio_reg(pi, (core == PHY_CORE_0) ? + RADIO_2057_NB_MASTER_CORE0 : + RADIO_2057_NB_MASTER_CORE1, + RADIO_2057_VCM_MASK, vcm_final); + else + mod_radio_reg(pi, RADIO_2056_RX_RSSI_MISC | + ((core == + PHY_CORE_0) ? RADIO_2056_RX0 : + RADIO_2056_RX1), RADIO_2056_VCM_MASK, + vcm_final << RADIO_2056_RSSI_VCM_SHIFT); + + for (result_idx = 0; result_idx < 4; result_idx++) { + if (core == result_idx / 2) { + fine_digital_offset[result_idx] = + (NPHY_RSSICAL_NB_TARGET * + NPHY_RSSICAL_NPOLL) - + poll_results[vcm_final][result_idx]; + if (fine_digital_offset[result_idx] < 0) { + fine_digital_offset[result_idx] = + abs(fine_digital_offset + [result_idx]); + fine_digital_offset[result_idx] += + (NPHY_RSSICAL_NPOLL / 2); + fine_digital_offset[result_idx] /= + NPHY_RSSICAL_NPOLL; + fine_digital_offset[result_idx] = + -fine_digital_offset[ + result_idx]; + } else { + fine_digital_offset[result_idx] += + (NPHY_RSSICAL_NPOLL / 2); + fine_digital_offset[result_idx] /= + NPHY_RSSICAL_NPOLL; + } + + if (poll_results_min[result_idx] == + NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL) + fine_digital_offset[result_idx] = + (NPHY_RSSICAL_NB_TARGET - + NPHY_RSSICAL_MAXREAD - 1); + + wlc_phy_scale_offset_rssi_nphy( + pi, 0x0, + (s8) + fine_digital_offset + [result_idx], + (result_idx / 2 == 0) ? + RADIO_MIMO_CORESEL_CORE1 : + RADIO_MIMO_CORESEL_CORE2, + (result_idx % 2 == 0) ? + NPHY_RAIL_I : NPHY_RAIL_Q, + NPHY_RSSI_SEL_NB); + } + } + + } + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + + if ((rxcore_state & (1 << core)) == 0) + continue; + + for (wb_cnt = 0; wb_cnt < 2; wb_cnt++) { + if (wb_cnt == 0) { + rssi_type = NPHY_RSSI_SEL_W1; + target_code = NPHY_RSSICAL_W1_TARGET_REV3; + } else { + rssi_type = NPHY_RSSI_SEL_W2; + target_code = NPHY_RSSICAL_W2_TARGET_REV3; + } + + wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, + core == + PHY_CORE_0 ? + RADIO_MIMO_CORESEL_CORE1 + : + RADIO_MIMO_CORESEL_CORE2, + NPHY_RAIL_I, rssi_type); + wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, + core == + PHY_CORE_0 ? + RADIO_MIMO_CORESEL_CORE1 + : + RADIO_MIMO_CORESEL_CORE2, + NPHY_RAIL_Q, rssi_type); + + wlc_phy_poll_rssi_nphy(pi, rssi_type, poll_result_core, + NPHY_RSSICAL_NPOLL); + + for (result_idx = 0; result_idx < 4; result_idx++) { + if (core == result_idx / 2) { + fine_digital_offset[result_idx] = + (target_code * + NPHY_RSSICAL_NPOLL) - + poll_result_core[result_idx]; + if (fine_digital_offset[result_idx] < + 0) { + fine_digital_offset[result_idx] + = abs( + fine_digital_offset + [result_idx]); + fine_digital_offset[result_idx] + += (NPHY_RSSICAL_NPOLL + / 2); + fine_digital_offset[result_idx] + /= NPHY_RSSICAL_NPOLL; + fine_digital_offset[result_idx] + = -fine_digital_offset + [result_idx]; + } else { + fine_digital_offset[result_idx] + += (NPHY_RSSICAL_NPOLL + / 2); + fine_digital_offset[result_idx] + /= NPHY_RSSICAL_NPOLL; + } + + wlc_phy_scale_offset_rssi_nphy( + pi, 0x0, + (s8) + fine_digital_offset + [core * + 2], + (core == PHY_CORE_0) ? + RADIO_MIMO_CORESEL_CORE1 : + RADIO_MIMO_CORESEL_CORE2, + (result_idx % 2 == 0) ? + NPHY_RAIL_I : + NPHY_RAIL_Q, + rssi_type); + } + } + + } + } + + write_phy_reg(pi, 0x91, NPHY_Rfctrlintc1_save); + write_phy_reg(pi, 0x92, NPHY_Rfctrlintc2_save); + + wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); + + mod_phy_reg(pi, 0xe7, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0x78, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0xe7, (0x1 << 0), 0); + + mod_phy_reg(pi, 0xec, (0x1 << 0), 1 << 0); + mod_phy_reg(pi, 0x78, (0x1 << 1), 1 << 1); + mod_phy_reg(pi, 0xec, (0x1 << 0), 0); + + write_phy_reg(pi, 0x8f, NPHY_AfectrlOverride1_save); + write_phy_reg(pi, 0xa5, NPHY_AfectrlOverride2_save); + write_phy_reg(pi, 0xa6, NPHY_AfectrlCore1_save); + write_phy_reg(pi, 0xa7, NPHY_AfectrlCore2_save); + write_phy_reg(pi, 0xe7, NPHY_RfctrlOverride0_save); + write_phy_reg(pi, 0xec, NPHY_RfctrlOverride1_save); + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + write_phy_reg(pi, 0x342, NPHY_REV7_RfctrlOverride3_save); + write_phy_reg(pi, 0x343, NPHY_REV7_RfctrlOverride4_save); + write_phy_reg(pi, 0x346, NPHY_REV7_RfctrlOverride5_save); + write_phy_reg(pi, 0x347, NPHY_REV7_RfctrlOverride6_save); + } + write_phy_reg(pi, 0xe5, NPHY_RfctrlOverrideAux0_save); + write_phy_reg(pi, 0xe6, NPHY_RfctrlOverrideAux1_save); + write_phy_reg(pi, 0x78, NPHY_RfctrlCmd_save); + write_phy_reg(pi, 0xf9, NPHY_RfctrlMiscReg1_save); + write_phy_reg(pi, 0xfb, NPHY_RfctrlMiscReg2_save); + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + write_phy_reg(pi, 0x340, NPHY_REV7_RfctrlMiscReg3_save); + write_phy_reg(pi, 0x341, NPHY_REV7_RfctrlMiscReg4_save); + write_phy_reg(pi, 0x344, NPHY_REV7_RfctrlMiscReg5_save); + write_phy_reg(pi, 0x345, NPHY_REV7_RfctrlMiscReg6_save); + } + write_phy_reg(pi, 0x7a, NPHY_RfctrlRSSIOTHERS1_save); + write_phy_reg(pi, 0x7d, NPHY_RfctrlRSSIOTHERS2_save); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + pi->rssical_cache.rssical_radio_regs_2G[0] = + read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0); + pi->rssical_cache.rssical_radio_regs_2G[1] = + read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1); + } else { + pi->rssical_cache.rssical_radio_regs_2G[0] = + read_radio_reg(pi, + RADIO_2056_RX_RSSI_MISC | + RADIO_2056_RX0); + pi->rssical_cache.rssical_radio_regs_2G[1] = + read_radio_reg(pi, + RADIO_2056_RX_RSSI_MISC | + RADIO_2056_RX1); + } + + pi->rssical_cache.rssical_phyregs_2G[0] = + read_phy_reg(pi, 0x1a6); + pi->rssical_cache.rssical_phyregs_2G[1] = + read_phy_reg(pi, 0x1ac); + pi->rssical_cache.rssical_phyregs_2G[2] = + read_phy_reg(pi, 0x1b2); + pi->rssical_cache.rssical_phyregs_2G[3] = + read_phy_reg(pi, 0x1b8); + pi->rssical_cache.rssical_phyregs_2G[4] = + read_phy_reg(pi, 0x1a4); + pi->rssical_cache.rssical_phyregs_2G[5] = + read_phy_reg(pi, 0x1aa); + pi->rssical_cache.rssical_phyregs_2G[6] = + read_phy_reg(pi, 0x1b0); + pi->rssical_cache.rssical_phyregs_2G[7] = + read_phy_reg(pi, 0x1b6); + pi->rssical_cache.rssical_phyregs_2G[8] = + read_phy_reg(pi, 0x1a5); + pi->rssical_cache.rssical_phyregs_2G[9] = + read_phy_reg(pi, 0x1ab); + pi->rssical_cache.rssical_phyregs_2G[10] = + read_phy_reg(pi, 0x1b1); + pi->rssical_cache.rssical_phyregs_2G[11] = + read_phy_reg(pi, 0x1b7); + + pi->nphy_rssical_chanspec_2G = pi->radio_chanspec; + } else { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + pi->rssical_cache.rssical_radio_regs_5G[0] = + read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE0); + pi->rssical_cache.rssical_radio_regs_5G[1] = + read_radio_reg(pi, RADIO_2057_NB_MASTER_CORE1); + } else { + pi->rssical_cache.rssical_radio_regs_5G[0] = + read_radio_reg(pi, + RADIO_2056_RX_RSSI_MISC | + RADIO_2056_RX0); + pi->rssical_cache.rssical_radio_regs_5G[1] = + read_radio_reg(pi, + RADIO_2056_RX_RSSI_MISC | + RADIO_2056_RX1); + } + + pi->rssical_cache.rssical_phyregs_5G[0] = + read_phy_reg(pi, 0x1a6); + pi->rssical_cache.rssical_phyregs_5G[1] = + read_phy_reg(pi, 0x1ac); + pi->rssical_cache.rssical_phyregs_5G[2] = + read_phy_reg(pi, 0x1b2); + pi->rssical_cache.rssical_phyregs_5G[3] = + read_phy_reg(pi, 0x1b8); + pi->rssical_cache.rssical_phyregs_5G[4] = + read_phy_reg(pi, 0x1a4); + pi->rssical_cache.rssical_phyregs_5G[5] = + read_phy_reg(pi, 0x1aa); + pi->rssical_cache.rssical_phyregs_5G[6] = + read_phy_reg(pi, 0x1b0); + pi->rssical_cache.rssical_phyregs_5G[7] = + read_phy_reg(pi, 0x1b6); + pi->rssical_cache.rssical_phyregs_5G[8] = + read_phy_reg(pi, 0x1a5); + pi->rssical_cache.rssical_phyregs_5G[9] = + read_phy_reg(pi, 0x1ab); + pi->rssical_cache.rssical_phyregs_5G[10] = + read_phy_reg(pi, 0x1b1); + pi->rssical_cache.rssical_phyregs_5G[11] = + read_phy_reg(pi, 0x1b7); + + pi->nphy_rssical_chanspec_5G = pi->radio_chanspec; + } + + wlc_phy_classifier_nphy(pi, (0x7 << 0), classif_state); + wlc_phy_clip_det_nphy(pi, 1, clip_state); +} + +static void wlc_phy_rssi_cal_nphy_rev2(struct brcms_phy *pi, u8 rssi_type) +{ + s32 target_code; + u16 classif_state; + u16 clip_state[2]; + u16 rssi_ctrl_state[2], pd_state[2]; + u16 rfctrlintc_state[2], rfpdcorerxtx_state[2]; + u16 rfctrlintc_override_val; + u16 clip_off[] = { 0xffff, 0xffff }; + u16 rf_pd_val, pd_mask, rssi_ctrl_mask; + u8 vcm, min_vcm, vcm_tmp[4]; + u8 vcm_final[4] = { 0, 0, 0, 0 }; + u8 result_idx, ctr; + s32 poll_results[4][4] = { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + }; + s32 poll_miniq[4][2] = { + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} + }; + s32 min_d, curr_d; + s32 fine_digital_offset[4]; + s32 poll_results_min[4] = { 0, 0, 0, 0 }; + s32 min_poll; + + switch (rssi_type) { + case NPHY_RSSI_SEL_NB: + target_code = NPHY_RSSICAL_NB_TARGET; + break; + case NPHY_RSSI_SEL_W1: + target_code = NPHY_RSSICAL_W1_TARGET; + break; + case NPHY_RSSI_SEL_W2: + target_code = NPHY_RSSICAL_W2_TARGET; + break; + default: + return; + } + + classif_state = wlc_phy_classifier_nphy(pi, 0, 0); + wlc_phy_classifier_nphy(pi, (0x7 << 0), 4); + wlc_phy_clip_det_nphy(pi, 0, clip_state); + wlc_phy_clip_det_nphy(pi, 1, clip_off); + + rf_pd_val = (rssi_type == NPHY_RSSI_SEL_NB) ? 0x6 : 0x4; + rfctrlintc_override_val = + CHSPEC_IS5G(pi->radio_chanspec) ? 0x140 : 0x110; + + rfctrlintc_state[0] = read_phy_reg(pi, 0x91); + rfpdcorerxtx_state[0] = read_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX); + write_phy_reg(pi, 0x91, rfctrlintc_override_val); + write_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX, rf_pd_val); + + rfctrlintc_state[1] = read_phy_reg(pi, 0x92); + rfpdcorerxtx_state[1] = read_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX); + write_phy_reg(pi, 0x92, rfctrlintc_override_val); + write_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX, rf_pd_val); + + pd_mask = RADIO_2055_NBRSSI_PD | RADIO_2055_WBRSSI_G1_PD | + RADIO_2055_WBRSSI_G2_PD; + pd_state[0] = + read_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC) & pd_mask; + pd_state[1] = + read_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC) & pd_mask; + mod_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC, pd_mask, 0); + mod_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC, pd_mask, 0); + rssi_ctrl_mask = RADIO_2055_NBRSSI_SEL | RADIO_2055_WBRSSI_G1_SEL | + RADIO_2055_WBRSSI_G2_SEL; + rssi_ctrl_state[0] = + read_radio_reg(pi, RADIO_2055_SP_RSSI_CORE1) & rssi_ctrl_mask; + rssi_ctrl_state[1] = + read_radio_reg(pi, RADIO_2055_SP_RSSI_CORE2) & rssi_ctrl_mask; + wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_ALLRX, rssi_type); + + wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, RADIO_MIMO_CORESEL_ALLRX, + NPHY_RAIL_I, rssi_type); + wlc_phy_scale_offset_rssi_nphy(pi, 0x0, 0x0, RADIO_MIMO_CORESEL_ALLRX, + NPHY_RAIL_Q, rssi_type); + + for (vcm = 0; vcm < 4; vcm++) { + + vcm_tmp[0] = vcm_tmp[1] = vcm_tmp[2] = vcm_tmp[3] = vcm; + if (rssi_type != NPHY_RSSI_SEL_W2) + wlc_phy_set_rssi_2055_vcm(pi, rssi_type, vcm_tmp); + + wlc_phy_poll_rssi_nphy(pi, rssi_type, &poll_results[vcm][0], + NPHY_RSSICAL_NPOLL); + + if ((rssi_type == NPHY_RSSI_SEL_W1) + || (rssi_type == NPHY_RSSI_SEL_W2)) { + for (ctr = 0; ctr < 2; ctr++) + poll_miniq[vcm][ctr] = + min(poll_results[vcm][ctr * 2 + 0], + poll_results[vcm][ctr * 2 + 1]); + } + } + + for (result_idx = 0; result_idx < 4; result_idx++) { + min_d = NPHY_RSSICAL_MAXD; + min_vcm = 0; + min_poll = NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL + 1; + for (vcm = 0; vcm < 4; vcm++) { + curr_d = abs(((rssi_type == NPHY_RSSI_SEL_NB) ? + poll_results[vcm][result_idx] : + poll_miniq[vcm][result_idx / 2]) - + (target_code * NPHY_RSSICAL_NPOLL)); + if (curr_d < min_d) { + min_d = curr_d; + min_vcm = vcm; + } + if (poll_results[vcm][result_idx] < min_poll) + min_poll = poll_results[vcm][result_idx]; + } + vcm_final[result_idx] = min_vcm; + poll_results_min[result_idx] = min_poll; + } + + if (rssi_type != NPHY_RSSI_SEL_W2) + wlc_phy_set_rssi_2055_vcm(pi, rssi_type, vcm_final); + + for (result_idx = 0; result_idx < 4; result_idx++) { + fine_digital_offset[result_idx] = + (target_code * NPHY_RSSICAL_NPOLL) - + poll_results[vcm_final[result_idx]][result_idx]; + if (fine_digital_offset[result_idx] < 0) { + fine_digital_offset[result_idx] = + abs(fine_digital_offset[result_idx]); + fine_digital_offset[result_idx] += + (NPHY_RSSICAL_NPOLL / 2); + fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL; + fine_digital_offset[result_idx] = + -fine_digital_offset[result_idx]; + } else { + fine_digital_offset[result_idx] += + (NPHY_RSSICAL_NPOLL / 2); + fine_digital_offset[result_idx] /= NPHY_RSSICAL_NPOLL; + } + + if (poll_results_min[result_idx] == + NPHY_RSSICAL_MAXREAD * NPHY_RSSICAL_NPOLL) + fine_digital_offset[result_idx] = + (target_code - NPHY_RSSICAL_MAXREAD - 1); + + wlc_phy_scale_offset_rssi_nphy(pi, 0x0, + (s8) + fine_digital_offset[result_idx], + (result_idx / 2 == + 0) ? RADIO_MIMO_CORESEL_CORE1 : + RADIO_MIMO_CORESEL_CORE2, + (result_idx % 2 == + 0) ? NPHY_RAIL_I : NPHY_RAIL_Q, + rssi_type); + } + + mod_radio_reg(pi, RADIO_2055_PD_CORE1_RSSI_MISC, pd_mask, pd_state[0]); + mod_radio_reg(pi, RADIO_2055_PD_CORE2_RSSI_MISC, pd_mask, pd_state[1]); + if (rssi_ctrl_state[0] == RADIO_2055_NBRSSI_SEL) + wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1, + NPHY_RSSI_SEL_NB); + else if (rssi_ctrl_state[0] == RADIO_2055_WBRSSI_G1_SEL) + wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1, + NPHY_RSSI_SEL_W1); + else /* RADIO_2055_WBRSSI_G2_SEL */ + wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE1, + NPHY_RSSI_SEL_W2); + if (rssi_ctrl_state[1] == RADIO_2055_NBRSSI_SEL) + wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2, + NPHY_RSSI_SEL_NB); + else if (rssi_ctrl_state[1] == RADIO_2055_WBRSSI_G1_SEL) + wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2, + NPHY_RSSI_SEL_W1); + else /* RADIO_2055_WBRSSI_G1_SEL */ + wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_CORE2, + NPHY_RSSI_SEL_W2); + wlc_phy_rssisel_nphy(pi, RADIO_MIMO_CORESEL_OFF, rssi_type); + + write_phy_reg(pi, 0x91, rfctrlintc_state[0]); + write_radio_reg(pi, RADIO_2055_PD_CORE1_RXTX, rfpdcorerxtx_state[0]); + write_phy_reg(pi, 0x92, rfctrlintc_state[1]); + write_radio_reg(pi, RADIO_2055_PD_CORE2_RXTX, rfpdcorerxtx_state[1]); + + wlc_phy_classifier_nphy(pi, (0x7 << 0), classif_state); + wlc_phy_clip_det_nphy(pi, 1, clip_state); + + wlc_phy_resetcca_nphy(pi); +} + +void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi) +{ + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + wlc_phy_rssi_cal_nphy_rev3(pi); + } else { + wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_NB); + wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_W1); + wlc_phy_rssi_cal_nphy_rev2(pi, NPHY_RSSI_SEL_W2); + } +} + +int +wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, struct d11rxhdr *rxh) +{ + s16 rxpwr, rxpwr0, rxpwr1; + s16 phyRx0_l, phyRx2_l; + + rxpwr = 0; + rxpwr0 = rxh->PhyRxStatus_1 & PRXS1_nphy_PWR0_MASK; + rxpwr1 = (rxh->PhyRxStatus_1 & PRXS1_nphy_PWR1_MASK) >> 8; + + if (rxpwr0 > 127) + rxpwr0 -= 256; + if (rxpwr1 > 127) + rxpwr1 -= 256; + + phyRx0_l = rxh->PhyRxStatus_0 & 0x00ff; + phyRx2_l = rxh->PhyRxStatus_2 & 0x00ff; + if (phyRx2_l > 127) + phyRx2_l -= 256; + + if (((rxpwr0 == 16) || (rxpwr0 == 32))) { + rxpwr0 = rxpwr1; + rxpwr1 = phyRx2_l; + } + + if (pi->sh->rssi_mode == RSSI_ANT_MERGE_MAX) + rxpwr = (rxpwr0 > rxpwr1) ? rxpwr0 : rxpwr1; + else if (pi->sh->rssi_mode == RSSI_ANT_MERGE_MIN) + rxpwr = (rxpwr0 < rxpwr1) ? rxpwr0 : rxpwr1; + else if (pi->sh->rssi_mode == RSSI_ANT_MERGE_AVG) + rxpwr = (rxpwr0 + rxpwr1) >> 1; + + return rxpwr; +} + +static void +wlc_phy_loadsampletable_nphy(struct brcms_phy *pi, struct cordic_iq *tone_buf, + u16 num_samps) +{ + u16 t; + u32 *data_buf = NULL; + + data_buf = kmalloc(sizeof(u32) * num_samps, GFP_ATOMIC); + if (data_buf == NULL) + return; + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + for (t = 0; t < num_samps; t++) + data_buf[t] = ((((unsigned int)tone_buf[t].i) & 0x3ff) << 10) | + (((unsigned int)tone_buf[t].q) & 0x3ff); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SAMPLEPLAY, num_samps, 0, 32, + data_buf); + + kfree(data_buf); + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +static u16 +wlc_phy_gen_load_samples_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, + u8 dac_test_mode) +{ + u8 phy_bw, is_phybw40; + u16 num_samps, t, spur; + s32 theta = 0, rot = 0; + u32 tbl_len; + struct cordic_iq *tone_buf = NULL; + + is_phybw40 = CHSPEC_IS40(pi->radio_chanspec); + phy_bw = (is_phybw40 == 1) ? 40 : 20; + tbl_len = (phy_bw << 3); + + if (dac_test_mode == 1) { + spur = read_phy_reg(pi, 0x01); + spur = (spur >> 15) & 1; + phy_bw = (spur == 1) ? 82 : 80; + phy_bw = (is_phybw40 == 1) ? (phy_bw << 1) : phy_bw; + + tbl_len = (phy_bw << 1); + } + + tone_buf = kmalloc(sizeof(struct cordic_iq) * tbl_len, GFP_ATOMIC); + if (tone_buf == NULL) + return 0; + + num_samps = (u16) tbl_len; + rot = ((f_kHz * 36) / phy_bw) / 100; + theta = 0; + + for (t = 0; t < num_samps; t++) { + + tone_buf[t] = cordic_calc_iq(theta); + + theta += rot; + + tone_buf[t].q = (s32) FLOAT(tone_buf[t].q * max_val); + tone_buf[t].i = (s32) FLOAT(tone_buf[t].i * max_val); + } + + wlc_phy_loadsampletable_nphy(pi, tone_buf, num_samps); + + kfree(tone_buf); + + return num_samps; +} + +static void +wlc_phy_runsamples_nphy(struct brcms_phy *pi, u16 num_samps, u16 loops, + u16 wait, u8 iqmode, u8 dac_test_mode, + bool modify_bbmult) +{ + u16 bb_mult; + u8 phy_bw, sample_cmd; + u16 orig_RfseqCoreActv; + u16 lpf_bw_ctl_override3, lpf_bw_ctl_override4, lpf_bw_ctl_miscreg3, + lpf_bw_ctl_miscreg4; + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + phy_bw = 20; + if (CHSPEC_IS40(pi->radio_chanspec)) + phy_bw = 40; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + lpf_bw_ctl_override3 = read_phy_reg(pi, 0x342) & (0x1 << 7); + lpf_bw_ctl_override4 = read_phy_reg(pi, 0x343) & (0x1 << 7); + if (lpf_bw_ctl_override3 | lpf_bw_ctl_override4) { + lpf_bw_ctl_miscreg3 = read_phy_reg(pi, 0x340) & + (0x7 << 8); + lpf_bw_ctl_miscreg4 = read_phy_reg(pi, 0x341) & + (0x7 << 8); + } else { + wlc_phy_rfctrl_override_nphy_rev7( + pi, + (0x1 << 7), + wlc_phy_read_lpf_bw_ctl_nphy + (pi, + 0), 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + + pi->nphy_sample_play_lpf_bw_ctl_ovr = true; + + lpf_bw_ctl_miscreg3 = read_phy_reg(pi, 0x340) & + (0x7 << 8); + lpf_bw_ctl_miscreg4 = read_phy_reg(pi, 0x341) & + (0x7 << 8); + } + } + + if ((pi->nphy_bb_mult_save & BB_MULT_VALID_MASK) == 0) { + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 87, 16, + &bb_mult); + pi->nphy_bb_mult_save = + BB_MULT_VALID_MASK | (bb_mult & BB_MULT_MASK); + } + + if (modify_bbmult) { + bb_mult = (phy_bw == 20) ? 100 : 71; + bb_mult = (bb_mult << 8) + bb_mult; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 87, 16, + &bb_mult); + } + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); + + write_phy_reg(pi, 0xc6, num_samps - 1); + + if (loops != 0xffff) + write_phy_reg(pi, 0xc4, loops - 1); + else + write_phy_reg(pi, 0xc4, loops); + + write_phy_reg(pi, 0xc5, wait); + + orig_RfseqCoreActv = read_phy_reg(pi, 0xa1); + or_phy_reg(pi, 0xa1, NPHY_RfseqMode_CoreActv_override); + if (iqmode) { + + and_phy_reg(pi, 0xc2, 0x7FFF); + + or_phy_reg(pi, 0xc2, 0x8000); + } else { + + sample_cmd = (dac_test_mode == 1) ? 0x5 : 0x1; + write_phy_reg(pi, 0xc3, sample_cmd); + } + + SPINWAIT(((read_phy_reg(pi, 0xa4) & 0x1) == 1), 1000); + + write_phy_reg(pi, 0xa1, orig_RfseqCoreActv); +} + +int +wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, + u8 iqmode, u8 dac_test_mode, bool modify_bbmult) +{ + u16 num_samps; + u16 loops = 0xffff; + u16 wait = 0; + + num_samps = wlc_phy_gen_load_samples_nphy(pi, f_kHz, max_val, + dac_test_mode); + if (num_samps == 0) + return -EBADE; + + wlc_phy_runsamples_nphy(pi, num_samps, loops, wait, iqmode, + dac_test_mode, modify_bbmult); + + return 0; +} + +void wlc_phy_stopplayback_nphy(struct brcms_phy *pi) +{ + u16 playback_status; + u16 bb_mult; + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + playback_status = read_phy_reg(pi, 0xc7); + if (playback_status & 0x1) + or_phy_reg(pi, 0xc3, NPHY_sampleCmd_STOP); + else if (playback_status & 0x2) + and_phy_reg(pi, 0xc2, + (u16) ~NPHY_iqloCalCmdGctl_IQLO_CAL_EN); + + and_phy_reg(pi, 0xc3, (u16) ~(0x1 << 2)); + + if ((pi->nphy_bb_mult_save & BB_MULT_VALID_MASK) != 0) { + + bb_mult = pi->nphy_bb_mult_save & BB_MULT_MASK; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, 87, 16, + &bb_mult); + + pi->nphy_bb_mult_save = 0; + } + + if (NREV_IS(pi->pubpi.phy_rev, 7) || NREV_GE(pi->pubpi.phy_rev, 8)) { + if (pi->nphy_sample_play_lpf_bw_ctl_ovr) { + wlc_phy_rfctrl_override_nphy_rev7( + pi, + (0x1 << 7), + 0, 0, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + pi->nphy_sample_play_lpf_bw_ctl_ovr = false; + } + } + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +static u32 *brcms_phy_get_tx_pwrctrl_tbl(struct brcms_phy *pi) +{ + u32 *tx_pwrctrl_tbl = NULL; + uint phyrev = pi->pubpi.phy_rev; + + if (PHY_IPA(pi)) { + tx_pwrctrl_tbl = + wlc_phy_get_ipa_gaintbl_nphy(pi); + } else { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + if (NREV_IS(phyrev, 3)) + tx_pwrctrl_tbl = nphy_tpc_5GHz_txgain_rev3; + else if (NREV_IS(phyrev, 4)) + tx_pwrctrl_tbl = + (pi->srom_fem5g.extpagain == 3) ? + nphy_tpc_5GHz_txgain_HiPwrEPA : + nphy_tpc_5GHz_txgain_rev4; + else + tx_pwrctrl_tbl = nphy_tpc_5GHz_txgain_rev5; + } else { + if (NREV_GE(phyrev, 7)) { + if (pi->pubpi.radiorev == 3) + tx_pwrctrl_tbl = + nphy_tpc_txgain_epa_2057rev3; + else if (pi->pubpi.radiorev == 5) + tx_pwrctrl_tbl = + nphy_tpc_txgain_epa_2057rev5; + } else { + if (NREV_GE(phyrev, 5) && + (pi->srom_fem2g.extpagain == 3)) + tx_pwrctrl_tbl = + nphy_tpc_txgain_HiPwrEPA; + else + tx_pwrctrl_tbl = + nphy_tpc_txgain_rev3; + } + } + } + return tx_pwrctrl_tbl; +} + +struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi) +{ + u16 base_idx[2], curr_gain[2]; + u8 core_no; + struct nphy_txgains target_gain; + u32 *tx_pwrctrl_tbl = NULL; + + if (pi->nphy_txpwrctrl == PHY_TPC_HW_OFF) { + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, + curr_gain); + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); + + for (core_no = 0; core_no < 2; core_no++) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + target_gain.ipa[core_no] = + curr_gain[core_no] & 0x0007; + target_gain.pad[core_no] = + ((curr_gain[core_no] & 0x00F8) >> 3); + target_gain.pga[core_no] = + ((curr_gain[core_no] & 0x0F00) >> 8); + target_gain.txgm[core_no] = + ((curr_gain[core_no] & 0x7000) >> 12); + target_gain.txlpf[core_no] = + ((curr_gain[core_no] & 0x8000) >> 15); + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + target_gain.ipa[core_no] = + curr_gain[core_no] & 0x000F; + target_gain.pad[core_no] = + ((curr_gain[core_no] & 0x00F0) >> 4); + target_gain.pga[core_no] = + ((curr_gain[core_no] & 0x0F00) >> 8); + target_gain.txgm[core_no] = + ((curr_gain[core_no] & 0x7000) >> 12); + } else { + target_gain.ipa[core_no] = + curr_gain[core_no] & 0x0003; + target_gain.pad[core_no] = + ((curr_gain[core_no] & 0x000C) >> 2); + target_gain.pga[core_no] = + ((curr_gain[core_no] & 0x0070) >> 4); + target_gain.txgm[core_no] = + ((curr_gain[core_no] & 0x0380) >> 7); + } + } + } else { + uint phyrev = pi->pubpi.phy_rev; + + base_idx[0] = (read_phy_reg(pi, 0x1ed) >> 8) & 0x7f; + base_idx[1] = (read_phy_reg(pi, 0x1ee) >> 8) & 0x7f; + for (core_no = 0; core_no < 2; core_no++) { + if (NREV_GE(phyrev, 3)) { + tx_pwrctrl_tbl = + brcms_phy_get_tx_pwrctrl_tbl(pi); + if (NREV_GE(phyrev, 7)) { + target_gain.ipa[core_no] = + (tx_pwrctrl_tbl + [base_idx[core_no]] + >> 16) & 0x7; + target_gain.pad[core_no] = + (tx_pwrctrl_tbl + [base_idx[core_no]] + >> 19) & 0x1f; + target_gain.pga[core_no] = + (tx_pwrctrl_tbl + [base_idx[core_no]] + >> 24) & 0xf; + target_gain.txgm[core_no] = + (tx_pwrctrl_tbl + [base_idx[core_no]] + >> 28) & 0x7; + target_gain.txlpf[core_no] = + (tx_pwrctrl_tbl + [base_idx[core_no]] + >> 31) & 0x1; + } else { + target_gain.ipa[core_no] = + (tx_pwrctrl_tbl + [base_idx[core_no]] + >> 16) & 0xf; + target_gain.pad[core_no] = + (tx_pwrctrl_tbl + [base_idx[core_no]] + >> 20) & 0xf; + target_gain.pga[core_no] = + (tx_pwrctrl_tbl + [base_idx[core_no]] + >> 24) & 0xf; + target_gain.txgm[core_no] = + (tx_pwrctrl_tbl + [base_idx[core_no]] + >> 28) & 0x7; + } + } else { + target_gain.ipa[core_no] = + (nphy_tpc_txgain[base_idx[core_no]] >> + 16) & 0x3; + target_gain.pad[core_no] = + (nphy_tpc_txgain[base_idx[core_no]] >> + 18) & 0x3; + target_gain.pga[core_no] = + (nphy_tpc_txgain[base_idx[core_no]] >> + 20) & 0x7; + target_gain.txgm[core_no] = + (nphy_tpc_txgain[base_idx[core_no]] >> + 23) & 0x7; + } + } + } + + return target_gain; +} + +static void +wlc_phy_iqcal_gainparams_nphy(struct brcms_phy *pi, u16 core_no, + struct nphy_txgains target_gain, + struct nphy_iqcal_params *params) +{ + u8 k; + int idx; + u16 gain_index; + u8 band_idx = (CHSPEC_IS5G(pi->radio_chanspec) ? 1 : 0); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) + params->txlpf = target_gain.txlpf[core_no]; + + params->txgm = target_gain.txgm[core_no]; + params->pga = target_gain.pga[core_no]; + params->pad = target_gain.pad[core_no]; + params->ipa = target_gain.ipa[core_no]; + if (NREV_GE(pi->pubpi.phy_rev, 7)) + params->cal_gain = + ((params->txlpf << 15) | (params->txgm << 12) | + (params->pga << 8) | + (params->pad << 3) | (params->ipa)); + else + params->cal_gain = + ((params->txgm << 12) | (params->pga << 8) | + (params->pad << 4) | (params->ipa)); + + params->ncorr[0] = 0x79; + params->ncorr[1] = 0x79; + params->ncorr[2] = 0x79; + params->ncorr[3] = 0x79; + params->ncorr[4] = 0x79; + } else { + + gain_index = ((target_gain.pad[core_no] << 0) | + (target_gain.pga[core_no] << 4) | + (target_gain.txgm[core_no] << 8)); + + idx = -1; + for (k = 0; k < NPHY_IQCAL_NUMGAINS; k++) { + if (tbl_iqcal_gainparams_nphy[band_idx][k][0] == + gain_index) { + idx = k; + break; + } + } + + params->txgm = tbl_iqcal_gainparams_nphy[band_idx][k][1]; + params->pga = tbl_iqcal_gainparams_nphy[band_idx][k][2]; + params->pad = tbl_iqcal_gainparams_nphy[band_idx][k][3]; + params->cal_gain = ((params->txgm << 7) | (params->pga << 4) | + (params->pad << 2)); + params->ncorr[0] = tbl_iqcal_gainparams_nphy[band_idx][k][4]; + params->ncorr[1] = tbl_iqcal_gainparams_nphy[band_idx][k][5]; + params->ncorr[2] = tbl_iqcal_gainparams_nphy[band_idx][k][6]; + params->ncorr[3] = tbl_iqcal_gainparams_nphy[band_idx][k][7]; + } +} + +static void wlc_phy_txcal_radio_setup_nphy(struct brcms_phy *pi) +{ + u16 jtag_core, core; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + for (core = 0; core <= 1; core++) { + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 0] = + READ_RADIO_REG3(pi, RADIO_2057, TX, core, + TX_SSI_MASTER); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 1] = + READ_RADIO_REG3(pi, RADIO_2057, TX, core, + IQCAL_VCM_HG); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 2] = + READ_RADIO_REG3(pi, RADIO_2057, TX, core, + IQCAL_IDAC); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 3] = + READ_RADIO_REG3(pi, RADIO_2057, TX, core, + TSSI_VCM); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 4] = 0; + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 5] = + READ_RADIO_REG3(pi, RADIO_2057, TX, core, + TX_SSI_MUX); + + if (pi->pubpi.radiorev != 5) + pi->tx_rx_cal_radio_saveregs[(core * 11) + 6] = + READ_RADIO_REG3(pi, RADIO_2057, TX, + core, + TSSIA); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 7] = + READ_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 8] = + READ_RADIO_REG3(pi, RADIO_2057, TX, core, + TSSI_MISC1); + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TX_SSI_MASTER, 0x0a); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + IQCAL_VCM_HG, 0x43); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + IQCAL_IDAC, 0x55); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TSSI_VCM, 0x00); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TSSIG, 0x00); + if (pi->use_int_tx_iqlo_cal_nphy) { + WRITE_RADIO_REG3(pi, RADIO_2057, TX, + core, TX_SSI_MUX, 0x4); + if (!(pi-> + internal_tx_iqlo_cal_tapoff_intpa_nphy)) + WRITE_RADIO_REG3(pi, RADIO_2057, + TX, core, + TSSIA, 0x31); + else + WRITE_RADIO_REG3(pi, RADIO_2057, + TX, core, + TSSIA, 0x21); + } + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TSSI_MISC1, 0x00); + } else { + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TX_SSI_MASTER, 0x06); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + IQCAL_VCM_HG, 0x43); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + IQCAL_IDAC, 0x55); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TSSI_VCM, 0x00); + + if (pi->pubpi.radiorev != 5) + WRITE_RADIO_REG3(pi, RADIO_2057, TX, + core, TSSIA, 0x00); + if (pi->use_int_tx_iqlo_cal_nphy) { + WRITE_RADIO_REG3(pi, RADIO_2057, TX, + core, TX_SSI_MUX, + 0x06); + if (!(pi-> + internal_tx_iqlo_cal_tapoff_intpa_nphy)) + WRITE_RADIO_REG3(pi, RADIO_2057, + TX, core, + TSSIG, 0x31); + else + WRITE_RADIO_REG3(pi, RADIO_2057, + TX, core, + TSSIG, 0x21); + } + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TSSI_MISC1, 0x00); + } + } + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + + for (core = 0; core <= 1; core++) { + jtag_core = + (core == + PHY_CORE_0) ? RADIO_2056_TX0 : RADIO_2056_TX1; + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 0] = + read_radio_reg(pi, + RADIO_2056_TX_TX_SSI_MASTER | + jtag_core); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 1] = + read_radio_reg(pi, + RADIO_2056_TX_IQCAL_VCM_HG | + jtag_core); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 2] = + read_radio_reg(pi, + RADIO_2056_TX_IQCAL_IDAC | + jtag_core); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 3] = + read_radio_reg( + pi, + RADIO_2056_TX_TSSI_VCM | + jtag_core); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 4] = + read_radio_reg(pi, + RADIO_2056_TX_TX_AMP_DET | + jtag_core); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 5] = + read_radio_reg(pi, + RADIO_2056_TX_TX_SSI_MUX | + jtag_core); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 6] = + read_radio_reg(pi, + RADIO_2056_TX_TSSIA | jtag_core); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 7] = + read_radio_reg(pi, + RADIO_2056_TX_TSSIG | jtag_core); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 8] = + read_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC1 | + jtag_core); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 9] = + read_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC2 | + jtag_core); + + pi->tx_rx_cal_radio_saveregs[(core * 11) + 10] = + read_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC3 | + jtag_core); + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + write_radio_reg(pi, + RADIO_2056_TX_TX_SSI_MASTER | + jtag_core, 0x0a); + write_radio_reg(pi, + RADIO_2056_TX_IQCAL_VCM_HG | + jtag_core, 0x40); + write_radio_reg(pi, + RADIO_2056_TX_IQCAL_IDAC | + jtag_core, 0x55); + write_radio_reg(pi, + RADIO_2056_TX_TSSI_VCM | + jtag_core, 0x00); + write_radio_reg(pi, + RADIO_2056_TX_TX_AMP_DET | + jtag_core, 0x00); + + if (PHY_IPA(pi)) { + write_radio_reg( + pi, + RADIO_2056_TX_TX_SSI_MUX + | jtag_core, 0x4); + write_radio_reg(pi, + RADIO_2056_TX_TSSIA | + jtag_core, 0x1); + } else { + write_radio_reg( + pi, + RADIO_2056_TX_TX_SSI_MUX + | jtag_core, 0x00); + write_radio_reg(pi, + RADIO_2056_TX_TSSIA | + jtag_core, 0x2f); + } + write_radio_reg(pi, + RADIO_2056_TX_TSSIG | jtag_core, + 0x00); + write_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC1 | + jtag_core, 0x00); + + write_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC2 | + jtag_core, 0x00); + write_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC3 | + jtag_core, 0x00); + } else { + write_radio_reg(pi, + RADIO_2056_TX_TX_SSI_MASTER | + jtag_core, 0x06); + write_radio_reg(pi, + RADIO_2056_TX_IQCAL_VCM_HG | + jtag_core, 0x40); + write_radio_reg(pi, + RADIO_2056_TX_IQCAL_IDAC | + jtag_core, 0x55); + write_radio_reg(pi, + RADIO_2056_TX_TSSI_VCM | + jtag_core, 0x00); + write_radio_reg(pi, + RADIO_2056_TX_TX_AMP_DET | + jtag_core, 0x00); + write_radio_reg(pi, + RADIO_2056_TX_TSSIA | jtag_core, + 0x00); + + if (PHY_IPA(pi)) { + + write_radio_reg( + pi, + RADIO_2056_TX_TX_SSI_MUX + | jtag_core, 0x06); + if (NREV_LT(pi->pubpi.phy_rev, 5)) + write_radio_reg( + pi, + RADIO_2056_TX_TSSIG + | jtag_core, + 0x11); + else + write_radio_reg( + pi, + RADIO_2056_TX_TSSIG + | jtag_core, + 0x1); + } else { + write_radio_reg( + pi, + RADIO_2056_TX_TX_SSI_MUX + | jtag_core, 0x00); + write_radio_reg(pi, + RADIO_2056_TX_TSSIG | + jtag_core, 0x20); + } + + write_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC1 | + jtag_core, 0x00); + write_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC2 | + jtag_core, 0x00); + write_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC3 | + jtag_core, 0x00); + } + } + } else { + + pi->tx_rx_cal_radio_saveregs[0] = + read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1); + write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, 0x29); + pi->tx_rx_cal_radio_saveregs[1] = + read_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2); + write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, 0x54); + + pi->tx_rx_cal_radio_saveregs[2] = + read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1); + write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, 0x29); + pi->tx_rx_cal_radio_saveregs[3] = + read_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2); + write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, 0x54); + + pi->tx_rx_cal_radio_saveregs[4] = + read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1); + pi->tx_rx_cal_radio_saveregs[5] = + read_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2); + + if ((read_phy_reg(pi, 0x09) & NPHY_BandControl_currentBand) == + 0) { + + write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, 0x04); + write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, 0x04); + } else { + + write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, 0x20); + write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, 0x20); + } + + if (NREV_LT(pi->pubpi.phy_rev, 2)) { + + or_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, 0x20); + or_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, 0x20); + } else { + + and_radio_reg(pi, RADIO_2055_CORE1_TX_BB_MXGM, 0xdf); + and_radio_reg(pi, RADIO_2055_CORE2_TX_BB_MXGM, 0xdf); + } + } +} + +static void wlc_phy_txcal_radio_cleanup_nphy(struct brcms_phy *pi) +{ + u16 jtag_core, core; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + for (core = 0; core <= 1; core++) { + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TX_SSI_MASTER, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 0]); + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_VCM_HG, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 1]); + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, IQCAL_IDAC, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 2]); + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_VCM, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 3]); + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TX_SSI_MUX, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 5]); + + if (pi->pubpi.radiorev != 5) + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TSSIA, + pi->tx_rx_cal_radio_saveregs + [(core * 11) + 6]); + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSIG, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 7]); + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, TSSI_MISC1, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 8]); + } + } else if (NREV_GE(pi->pubpi.phy_rev, 3)) { + for (core = 0; core <= 1; core++) { + jtag_core = (core == PHY_CORE_0) ? + RADIO_2056_TX0 : RADIO_2056_TX1; + + write_radio_reg(pi, + RADIO_2056_TX_TX_SSI_MASTER | jtag_core, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 0]); + + write_radio_reg(pi, + RADIO_2056_TX_IQCAL_VCM_HG | jtag_core, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 1]); + + write_radio_reg(pi, + RADIO_2056_TX_IQCAL_IDAC | jtag_core, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 2]); + + write_radio_reg(pi, RADIO_2056_TX_TSSI_VCM | jtag_core, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 3]); + + write_radio_reg(pi, + RADIO_2056_TX_TX_AMP_DET | jtag_core, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 4]); + + write_radio_reg(pi, + RADIO_2056_TX_TX_SSI_MUX | jtag_core, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 5]); + + write_radio_reg(pi, RADIO_2056_TX_TSSIA | jtag_core, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 6]); + + write_radio_reg(pi, RADIO_2056_TX_TSSIG | jtag_core, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 7]); + + write_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC1 | jtag_core, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 8]); + + write_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC2 | jtag_core, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 9]); + + write_radio_reg(pi, + RADIO_2056_TX_TSSI_MISC3 | jtag_core, + pi-> + tx_rx_cal_radio_saveregs[(core * 11) + + 10]); + } + } else { + + write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL1, + pi->tx_rx_cal_radio_saveregs[0]); + write_radio_reg(pi, RADIO_2055_CORE1_TXRF_IQCAL2, + pi->tx_rx_cal_radio_saveregs[1]); + write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL1, + pi->tx_rx_cal_radio_saveregs[2]); + write_radio_reg(pi, RADIO_2055_CORE2_TXRF_IQCAL2, + pi->tx_rx_cal_radio_saveregs[3]); + write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE1, + pi->tx_rx_cal_radio_saveregs[4]); + write_radio_reg(pi, RADIO_2055_PWRDET_RXTX_CORE2, + pi->tx_rx_cal_radio_saveregs[5]); + } +} + +static void wlc_phy_txcal_physetup_nphy(struct brcms_phy *pi) +{ + u16 val, mask; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + pi->tx_rx_cal_phy_saveregs[0] = read_phy_reg(pi, 0xa6); + pi->tx_rx_cal_phy_saveregs[1] = read_phy_reg(pi, 0xa7); + + mask = ((0x3 << 8) | (0x3 << 10)); + val = (0x2 << 8); + val |= (0x2 << 10); + mod_phy_reg(pi, 0xa6, mask, val); + mod_phy_reg(pi, 0xa7, mask, val); + + val = read_phy_reg(pi, 0x8f); + pi->tx_rx_cal_phy_saveregs[2] = val; + val |= ((0x1 << 9) | (0x1 << 10)); + write_phy_reg(pi, 0x8f, val); + + val = read_phy_reg(pi, 0xa5); + pi->tx_rx_cal_phy_saveregs[3] = val; + val |= ((0x1 << 9) | (0x1 << 10)); + write_phy_reg(pi, 0xa5, val); + + pi->tx_rx_cal_phy_saveregs[4] = read_phy_reg(pi, 0x01); + mod_phy_reg(pi, 0x01, (0x1 << 15), 0); + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 3, 16, + &val); + pi->tx_rx_cal_phy_saveregs[5] = val; + val = 0; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 3, 16, + &val); + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 19, 16, + &val); + pi->tx_rx_cal_phy_saveregs[6] = val; + val = 0; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 19, 16, + &val); + + pi->tx_rx_cal_phy_saveregs[7] = read_phy_reg(pi, 0x91); + pi->tx_rx_cal_phy_saveregs[8] = read_phy_reg(pi, 0x92); + + if (!(pi->use_int_tx_iqlo_cal_nphy)) + wlc_phy_rfctrlintc_override_nphy( + pi, + NPHY_RfctrlIntc_override_PA, + 1, + RADIO_MIMO_CORESEL_CORE1 + | + RADIO_MIMO_CORESEL_CORE2); + else + wlc_phy_rfctrlintc_override_nphy( + pi, + NPHY_RfctrlIntc_override_PA, + 0, + RADIO_MIMO_CORESEL_CORE1 + | + RADIO_MIMO_CORESEL_CORE2); + + wlc_phy_rfctrlintc_override_nphy(pi, + NPHY_RfctrlIntc_override_TRSW, + 0x2, RADIO_MIMO_CORESEL_CORE1); + wlc_phy_rfctrlintc_override_nphy(pi, + NPHY_RfctrlIntc_override_TRSW, + 0x8, RADIO_MIMO_CORESEL_CORE2); + + pi->tx_rx_cal_phy_saveregs[9] = read_phy_reg(pi, 0x297); + pi->tx_rx_cal_phy_saveregs[10] = read_phy_reg(pi, 0x29b); + mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (0) << 0); + + mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (0) << 0); + + if (NREV_IS(pi->pubpi.phy_rev, 7) + || NREV_GE(pi->pubpi.phy_rev, 8)) + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 7), + wlc_phy_read_lpf_bw_ctl_nphy + (pi, + 0), 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + + if (pi->use_int_tx_iqlo_cal_nphy + && !(pi->internal_tx_iqlo_cal_tapoff_intpa_nphy)) { + + if (NREV_IS(pi->pubpi.phy_rev, 7)) { + + mod_radio_reg(pi, RADIO_2057_OVR_REG0, 1 << 4, + 1 << 4); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + mod_radio_reg( + pi, + RADIO_2057_PAD2G_TUNE_PUS_CORE0, + 1, 0); + mod_radio_reg( + pi, + RADIO_2057_PAD2G_TUNE_PUS_CORE1, + 1, 0); + } else { + mod_radio_reg( + pi, + RADIO_2057_IPA5G_CASCOFFV_PU_CORE0, + 1, 0); + mod_radio_reg( + pi, + RADIO_2057_IPA5G_CASCOFFV_PU_CORE1, + 1, 0); + } + } else if (NREV_GE(pi->pubpi.phy_rev, 8)) { + wlc_phy_rfctrl_override_nphy_rev7( + pi, + (0x1 << 3), 0, + 0x3, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + } + } + } else { + pi->tx_rx_cal_phy_saveregs[0] = read_phy_reg(pi, 0xa6); + pi->tx_rx_cal_phy_saveregs[1] = read_phy_reg(pi, 0xa7); + + mask = ((0x3 << 12) | (0x3 << 14)); + val = (0x2 << 12); + val |= (0x2 << 14); + mod_phy_reg(pi, 0xa6, mask, val); + mod_phy_reg(pi, 0xa7, mask, val); + + val = read_phy_reg(pi, 0xa5); + pi->tx_rx_cal_phy_saveregs[2] = val; + val |= ((0x1 << 12) | (0x1 << 13)); + write_phy_reg(pi, 0xa5, val); + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 2, 16, + &val); + pi->tx_rx_cal_phy_saveregs[3] = val; + val |= 0x2000; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 2, 16, + &val); + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 18, 16, + &val); + pi->tx_rx_cal_phy_saveregs[4] = val; + val |= 0x2000; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 18, 16, + &val); + + pi->tx_rx_cal_phy_saveregs[5] = read_phy_reg(pi, 0x91); + pi->tx_rx_cal_phy_saveregs[6] = read_phy_reg(pi, 0x92); + val = CHSPEC_IS5G(pi->radio_chanspec) ? 0x180 : 0x120; + write_phy_reg(pi, 0x91, val); + write_phy_reg(pi, 0x92, val); + } +} + +static void wlc_phy_txcal_phycleanup_nphy(struct brcms_phy *pi) +{ + u16 mask; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + write_phy_reg(pi, 0xa6, pi->tx_rx_cal_phy_saveregs[0]); + write_phy_reg(pi, 0xa7, pi->tx_rx_cal_phy_saveregs[1]); + write_phy_reg(pi, 0x8f, pi->tx_rx_cal_phy_saveregs[2]); + write_phy_reg(pi, 0xa5, pi->tx_rx_cal_phy_saveregs[3]); + write_phy_reg(pi, 0x01, pi->tx_rx_cal_phy_saveregs[4]); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 3, 16, + &pi->tx_rx_cal_phy_saveregs[5]); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 19, 16, + &pi->tx_rx_cal_phy_saveregs[6]); + + write_phy_reg(pi, 0x91, pi->tx_rx_cal_phy_saveregs[7]); + write_phy_reg(pi, 0x92, pi->tx_rx_cal_phy_saveregs[8]); + + write_phy_reg(pi, 0x297, pi->tx_rx_cal_phy_saveregs[9]); + write_phy_reg(pi, 0x29b, pi->tx_rx_cal_phy_saveregs[10]); + + if (NREV_IS(pi->pubpi.phy_rev, 7) + || NREV_GE(pi->pubpi.phy_rev, 8)) + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 7), 0, 0, + 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + + wlc_phy_resetcca_nphy(pi); + + if (pi->use_int_tx_iqlo_cal_nphy + && !(pi->internal_tx_iqlo_cal_tapoff_intpa_nphy)) { + + if (NREV_IS(pi->pubpi.phy_rev, 7)) { + if (CHSPEC_IS2G(pi->radio_chanspec)) { + mod_radio_reg( + pi, + RADIO_2057_PAD2G_TUNE_PUS_CORE0, + 1, 1); + mod_radio_reg( + pi, + RADIO_2057_PAD2G_TUNE_PUS_CORE1, + 1, 1); + } else { + mod_radio_reg( + pi, + RADIO_2057_IPA5G_CASCOFFV_PU_CORE0, + 1, 1); + mod_radio_reg( + pi, + RADIO_2057_IPA5G_CASCOFFV_PU_CORE1, + 1, 1); + } + + mod_radio_reg(pi, RADIO_2057_OVR_REG0, 1 << 4, + 0); + } else if (NREV_GE(pi->pubpi.phy_rev, 8)) { + wlc_phy_rfctrl_override_nphy_rev7( + pi, + (0x1 << 3), 0, + 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + } + } + } else { + mask = ((0x3 << 12) | (0x3 << 14)); + mod_phy_reg(pi, 0xa6, mask, pi->tx_rx_cal_phy_saveregs[0]); + mod_phy_reg(pi, 0xa7, mask, pi->tx_rx_cal_phy_saveregs[1]); + write_phy_reg(pi, 0xa5, pi->tx_rx_cal_phy_saveregs[2]); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 2, 16, + &pi->tx_rx_cal_phy_saveregs[3]); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_AFECTRL, 1, 18, 16, + &pi->tx_rx_cal_phy_saveregs[4]); + + write_phy_reg(pi, 0x91, pi->tx_rx_cal_phy_saveregs[5]); + write_phy_reg(pi, 0x92, pi->tx_rx_cal_phy_saveregs[6]); + } +} + +void +wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf, u8 num_samps) +{ + u16 tssi_reg; + s32 temp, pwrindex[2]; + s32 idle_tssi[2]; + s32 rssi_buf[4]; + s32 tssival[2]; + u8 tssi_type; + + tssi_reg = read_phy_reg(pi, 0x1e9); + + temp = (s32) (tssi_reg & 0x3f); + idle_tssi[0] = (temp <= 31) ? temp : (temp - 64); + + temp = (s32) ((tssi_reg >> 8) & 0x3f); + idle_tssi[1] = (temp <= 31) ? temp : (temp - 64); + + tssi_type = + CHSPEC_IS5G(pi->radio_chanspec) ? + (u8)NPHY_RSSI_SEL_TSSI_5G : (u8)NPHY_RSSI_SEL_TSSI_2G; + + wlc_phy_poll_rssi_nphy(pi, tssi_type, rssi_buf, num_samps); + + tssival[0] = rssi_buf[0] / ((s32) num_samps); + tssival[1] = rssi_buf[2] / ((s32) num_samps); + + pwrindex[0] = idle_tssi[0] - tssival[0] + 64; + pwrindex[1] = idle_tssi[1] - tssival[1] + 64; + + if (pwrindex[0] < 0) + pwrindex[0] = 0; + else if (pwrindex[0] > 63) + pwrindex[0] = 63; + + if (pwrindex[1] < 0) + pwrindex[1] = 0; + else if (pwrindex[1] > 63) + pwrindex[1] = 63; + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 1, + (u32) pwrindex[0], 32, &qdBm_pwrbuf[0]); + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 1, + (u32) pwrindex[1], 32, &qdBm_pwrbuf[1]); +} + +static void wlc_phy_update_txcal_ladder_nphy(struct brcms_phy *pi, u16 core) +{ + int index; + u32 bbmult_scale; + u16 bbmult; + u16 tblentry; + + struct nphy_txiqcal_ladder ladder_lo[] = { + {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0}, + {25, 0}, {25, 1}, {25, 2}, {25, 3}, {25, 4}, {25, 5}, + {25, 6}, {25, 7}, {35, 7}, {50, 7}, {71, 7}, {100, 7} + }; + + struct nphy_txiqcal_ladder ladder_iq[] = { + {3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0}, + {25, 0}, {35, 0}, {50, 0}, {71, 0}, {100, 0}, {100, 1}, + {100, 2}, {100, 3}, {100, 4}, {100, 5}, {100, 6}, {100, 7} + }; + + bbmult = (core == PHY_CORE_0) ? + ((pi->nphy_txcal_bbmult >> 8) & 0xff) : + (pi->nphy_txcal_bbmult & 0xff); + + for (index = 0; index < 18; index++) { + bbmult_scale = ladder_lo[index].percent * bbmult; + bbmult_scale /= 100; + + tblentry = + ((bbmult_scale & 0xff) << 8) | ladder_lo[index].g_env; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, index, 16, + &tblentry); + + bbmult_scale = ladder_iq[index].percent * bbmult; + bbmult_scale /= 100; + + tblentry = + ((bbmult_scale & 0xff) << 8) | ladder_iq[index].g_env; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 1, index + 32, + 16, &tblentry); + } +} + +static u8 wlc_phy_txpwr_idx_cur_get_nphy(struct brcms_phy *pi, u8 core) +{ + u16 tmp; + tmp = read_phy_reg(pi, ((core == PHY_CORE_0) ? 0x1ed : 0x1ee)); + + tmp = (tmp & (0x7f << 8)) >> 8; + return (u8) tmp; +} + +static void +wlc_phy_txpwr_idx_cur_set_nphy(struct brcms_phy *pi, u8 idx0, u8 idx1) +{ + mod_phy_reg(pi, 0x1e7, (0x7f << 0), idx0); + + if (NREV_GT(pi->pubpi.phy_rev, 1)) + mod_phy_reg(pi, 0x222, (0xff << 0), idx1); +} + +static u16 wlc_phy_ipa_get_bbmult_nphy(struct brcms_phy *pi) +{ + u16 m0m1; + + wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m0m1); + + return m0m1; +} + +static void wlc_phy_ipa_set_bbmult_nphy(struct brcms_phy *pi, u8 m0, u8 m1) +{ + u16 m0m1 = (u16) ((m0 << 8) | m1); + + wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m0m1); + wlc_phy_table_write_nphy(pi, 15, 1, 95, 16, &m0m1); +} + +static void +wlc_phy_papd_cal_setup_nphy(struct brcms_phy *pi, + struct nphy_papd_restore_state *state, u8 core) +{ + s32 tone_freq; + u8 off_core; + u16 mixgain = 0; + + off_core = core ^ 0x1; + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + if (NREV_IS(pi->pubpi.phy_rev, 7) + || NREV_GE(pi->pubpi.phy_rev, 8)) + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 7), + wlc_phy_read_lpf_bw_ctl_nphy + (pi, + 0), 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if (pi->pubpi.radiorev == 5) + mixgain = (core == 0) ? 0x20 : 0x00; + else if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) + mixgain = 0x00; + else if ((pi->pubpi.radiorev <= 4) + || (pi->pubpi.radiorev == 6)) + mixgain = 0x00; + } else { + if ((pi->pubpi.radiorev == 4) || + (pi->pubpi.radiorev == 6)) + mixgain = 0x50; + else if ((pi->pubpi.radiorev == 3) + || (pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) + mixgain = 0x0; + } + + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), + mixgain, (1 << core), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + + wlc_phy_rfctrl_override_1tomany_nphy( + pi, + NPHY_REV7_RfctrlOverride_cmd_tx_pu, + 1, (1 << core), 0); + wlc_phy_rfctrl_override_1tomany_nphy( + pi, + NPHY_REV7_RfctrlOverride_cmd_tx_pu, + 0, (1 << off_core), 0); + + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), + 0, 0x3, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 1, + (1 << core), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, + (1 << core), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, + (1 << core), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID2); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 8), 0, + (1 << core), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 1, + (1 << core), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 0, + (1 << core), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 1, + (1 << core), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), + 0, (1 << core), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), 0, + (1 << core), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + + state->afectrl[core] = read_phy_reg(pi, (core == PHY_CORE_0) ? + 0xa6 : 0xa7); + state->afeoverride[core] = + read_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5); + state->afectrl[off_core] = + read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa7 : 0xa6); + state->afeoverride[off_core] = + read_phy_reg(pi, (core == PHY_CORE_0) ? 0xa5 : 0x8f); + + mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa6 : 0xa7), + (0x1 << 2), 0); + mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : + 0xa5), (0x1 << 2), (0x1 << 2)); + + mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa7 : 0xa6), + (0x1 << 2), (0x1 << 2)); + mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa5 : + 0x8f), (0x1 << 2), (0x1 << 2)); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + state->pwrup[core] = + READ_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_2G_PWRUP); + state->atten[core] = + READ_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_2G_ATTEN); + state->pwrup[off_core] = + READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, + TXRXCOUPLE_2G_PWRUP); + state->atten[off_core] = + READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, + TXRXCOUPLE_2G_ATTEN); + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_2G_PWRUP, 0xc); + + if ((pi->pubpi.radiorev == 3) || + (pi->pubpi.radiorev == 4) || + (pi->pubpi.radiorev == 6)) + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_2G_ATTEN, 0xf0); + else if (pi->pubpi.radiorev == 5) + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_2G_ATTEN, + (core == 0) ? 0xf7 : 0xf2); + else if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_2G_ATTEN, 0xf0); + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, + TXRXCOUPLE_2G_PWRUP, 0x0); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, + TXRXCOUPLE_2G_ATTEN, 0xff); + } else { + state->pwrup[core] = + READ_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_5G_PWRUP); + state->atten[core] = + READ_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_5G_ATTEN); + state->pwrup[off_core] = + READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, + TXRXCOUPLE_5G_PWRUP); + state->atten[off_core] = + READ_RADIO_REG3(pi, RADIO_2057, TX, off_core, + TXRXCOUPLE_5G_ATTEN); + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_5G_PWRUP, 0xc); + + if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_5G_ATTEN, 0xf4); + + else + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_5G_ATTEN, 0xf0); + + WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, + TXRXCOUPLE_5G_PWRUP, 0x0); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, off_core, + TXRXCOUPLE_5G_ATTEN, 0xff); + } + + tone_freq = 4000; + + wlc_phy_tx_tone_nphy(pi, tone_freq, 181, 0, 0, false); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 13), (1) << 13); + + mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_OFF) << 0); + + mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 13), (0) << 13); + + } else { + + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 0); + + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 1, 0, 0); + + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0x3, 0); + + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 2), 1, 0x3, 0); + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 1, 0x3, 0); + + state->afectrl[core] = read_phy_reg(pi, (core == PHY_CORE_0) ? + 0xa6 : 0xa7); + state->afeoverride[core] = + read_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : 0xa5); + + mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0xa6 : 0xa7), + (0x1 << 0) | (0x1 << 1) | (0x1 << 2), 0); + mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : + 0xa5), + (0x1 << 0) | + (0x1 << 1) | + (0x1 << 2), (0x1 << 0) | (0x1 << 1) | (0x1 << 2)); + + state->vga_master[core] = + READ_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER); + WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER, 0x2b); + if (CHSPEC_IS2G(pi->radio_chanspec)) { + state->fbmix[core] = + READ_RADIO_REG2(pi, RADIO_2056, RX, core, + TXFBMIX_G); + state->intpa_master[core] = + READ_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAG_MASTER); + + WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_G, + 0x03); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAG_MASTER, 0x04); + } else { + state->fbmix[core] = + READ_RADIO_REG2(pi, RADIO_2056, RX, core, + TXFBMIX_A); + state->intpa_master[core] = + READ_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAA_MASTER); + + WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, TXFBMIX_A, + 0x03); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAA_MASTER, 0x04); + + } + + tone_freq = 4000; + + wlc_phy_tx_tone_nphy(pi, tone_freq, 181, 0, 0, false); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (1) << 0); + + mod_phy_reg(pi, (off_core == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (0) << 0); + + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 0); + } +} + +static void +wlc_phy_papd_cal_cleanup_nphy(struct brcms_phy *pi, + struct nphy_papd_restore_state *state) +{ + u8 core; + + wlc_phy_stopplayback_nphy(pi); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_2G_PWRUP, 0); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_2G_ATTEN, + state->atten[core]); + } else { + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_5G_PWRUP, 0); + WRITE_RADIO_REG3(pi, RADIO_2057, TX, core, + TXRXCOUPLE_5G_ATTEN, + state->atten[core]); + } + } + + if ((pi->pubpi.radiorev == 4) || (pi->pubpi.radiorev == 6)) + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 2), + 1, 0x3, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + else + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 2), + 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), + 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID2); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID2); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 1, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 12), 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 2), 1, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID2); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 8), 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 1, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), 1, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 4), 0, 0x3, 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + + write_phy_reg(pi, (core == PHY_CORE_0) ? + 0xa6 : 0xa7, state->afectrl[core]); + write_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : + 0xa5, state->afeoverride[core]); + } + + wlc_phy_ipa_set_bbmult_nphy(pi, (state->mm >> 8) & 0xff, + (state->mm & 0xff)); + + if (NREV_IS(pi->pubpi.phy_rev, 7) + || NREV_GE(pi->pubpi.phy_rev, 8)) + wlc_phy_rfctrl_override_nphy_rev7( + pi, (0x1 << 7), 0, 0, + 1, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + } else { + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 1); + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 13), 0, 0x3, 1); + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 0), 0, 0x3, 1); + + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 2), 0, 0x3, 1); + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 1), 0, 0x3, 1); + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + + WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, VGA_MASTER, + state->vga_master[core]); + if (CHSPEC_IS2G(pi->radio_chanspec)) { + WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, + TXFBMIX_G, state->fbmix[core]); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAG_MASTER, + state->intpa_master[core]); + } else { + WRITE_RADIO_REG2(pi, RADIO_2056, RX, core, + TXFBMIX_A, state->fbmix[core]); + WRITE_RADIO_REG2(pi, RADIO_2056, TX, core, + INTPAA_MASTER, + state->intpa_master[core]); + } + + write_phy_reg(pi, (core == PHY_CORE_0) ? + 0xa6 : 0xa7, state->afectrl[core]); + write_phy_reg(pi, (core == PHY_CORE_0) ? 0x8f : + 0xa5, state->afeoverride[core]); + } + + wlc_phy_ipa_set_bbmult_nphy(pi, (state->mm >> 8) & 0xff, + (state->mm & 0xff)); + + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 1); + } +} + +static void +wlc_phy_a1_nphy(struct brcms_phy *pi, u8 core, u32 winsz, u32 start, + u32 end) +{ + u32 *buf, *src, *dst, sz; + + sz = end - start + 1; + + buf = kmalloc(2 * sizeof(u32) * NPHY_PAPD_EPS_TBL_SIZE, GFP_ATOMIC); + if (NULL == buf) + return; + + src = buf; + dst = buf + NPHY_PAPD_EPS_TBL_SIZE; + + wlc_phy_table_read_nphy(pi, + (core == + PHY_CORE_0 ? NPHY_TBL_ID_EPSILONTBL0 : + NPHY_TBL_ID_EPSILONTBL1), + NPHY_PAPD_EPS_TBL_SIZE, 0, 32, src); + + do { + u32 phy_a1, phy_a2; + s32 phy_a3, phy_a4, phy_a5, phy_a6, phy_a7; + + phy_a1 = end - min(end, (winsz >> 1)); + phy_a2 = min_t(u32, NPHY_PAPD_EPS_TBL_SIZE - 1, + end + (winsz >> 1)); + phy_a3 = phy_a2 - phy_a1 + 1; + phy_a6 = 0; + phy_a7 = 0; + + do { + wlc_phy_papd_decode_epsilon(src[phy_a2], &phy_a4, + &phy_a5); + phy_a6 += phy_a4; + phy_a7 += phy_a5; + } while (phy_a2-- != phy_a1); + + phy_a6 /= phy_a3; + phy_a7 /= phy_a3; + dst[end] = ((u32) phy_a7 << 13) | ((u32) phy_a6 & 0x1fff); + } while (end-- != start); + + wlc_phy_table_write_nphy(pi, + (core == + PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 : + NPHY_TBL_ID_EPSILONTBL1, sz, start, 32, dst); + + kfree(buf); +} + +static void +wlc_phy_a2_nphy(struct brcms_phy *pi, struct nphy_ipa_txcalgains *txgains, + enum phy_cal_mode cal_mode, u8 core) +{ + u16 phy_a1, phy_a2, phy_a3; + u16 phy_a4, phy_a5; + bool phy_a6; + u8 phy_a7, m[2]; + u32 phy_a8 = 0; + struct nphy_txgains phy_a9; + + if (NREV_LT(pi->pubpi.phy_rev, 3)) + return; + + phy_a7 = (core == PHY_CORE_0) ? 1 : 0; + + phy_a6 = ((cal_mode == CAL_GCTRL) + || (cal_mode == CAL_SOFT)) ? true : false; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + phy_a9 = wlc_phy_get_tx_gain_nphy(pi); + + if (CHSPEC_IS2G(pi->radio_chanspec)) + phy_a5 = ((phy_a9.txlpf[core] << 15) | + (phy_a9.txgm[core] << 12) | + (phy_a9.pga[core] << 8) | + (txgains->gains.pad[core] << 3) | + (phy_a9.ipa[core])); + else + phy_a5 = ((phy_a9.txlpf[core] << 15) | + (phy_a9.txgm[core] << 12) | + (txgains->gains.pga[core] << 8) | + (phy_a9.pad[core] << 3) | (phy_a9.ipa[core])); + + wlc_phy_rfctrl_override_1tomany_nphy( + pi, + NPHY_REV7_RfctrlOverride_cmd_txgain, + phy_a5, (1 << core), 0); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if ((pi->pubpi.radiorev <= 4) + || (pi->pubpi.radiorev == 6)) + m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? + 60 : 79; + else + m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? + 45 : 64; + } else { + m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 75 : 107; + } + + m[phy_a7] = 0; + wlc_phy_ipa_set_bbmult_nphy(pi, m[0], m[1]); + + phy_a2 = 63; + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if ((pi->pubpi.radiorev == 4) + || (pi->pubpi.radiorev == 6)) { + phy_a1 = 30; + phy_a3 = 30; + } else { + phy_a1 = 25; + phy_a3 = 25; + } + } else { + if ((pi->pubpi.radiorev == 5) + || (pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) { + phy_a1 = 25; + phy_a3 = 25; + } else { + phy_a1 = 35; + phy_a3 = 35; + } + } + + if (cal_mode == CAL_GCTRL) { + if ((pi->pubpi.radiorev == 5) + && (CHSPEC_IS2G(pi->radio_chanspec))) + phy_a1 = 55; + else if (((pi->pubpi.radiorev == 7) && + (CHSPEC_IS2G(pi->radio_chanspec))) || + ((pi->pubpi.radiorev == 8) && + (CHSPEC_IS2G(pi->radio_chanspec)))) + phy_a1 = 60; + else + phy_a1 = 63; + + } else if ((cal_mode != CAL_FULL) && (cal_mode != CAL_SOFT)) { + + phy_a1 = 35; + phy_a3 = 35; + } + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (1) << 0); + + mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (0) << 0); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 13), (1) << 13); + + mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 13), (0) << 13); + + write_phy_reg(pi, 0x2a1, 0x80); + write_phy_reg(pi, 0x2a2, 0x100); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x7 << 4), (11) << 4); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x7 << 8), (11) << 8); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x7 << 0), (0x3) << 0); + + write_phy_reg(pi, 0x2e5, 0x20); + + mod_phy_reg(pi, 0x2a0, (0x3f << 0), (phy_a3) << 0); + + mod_phy_reg(pi, 0x29f, (0x3f << 0), (phy_a1) << 0); + + mod_phy_reg(pi, 0x29f, (0x3f << 8), (phy_a2) << 8); + + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), + 1, ((core == 0) ? 1 : 2), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), + 0, ((core == 0) ? 2 : 1), 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + + write_phy_reg(pi, 0x2be, 1); + SPINWAIT(read_phy_reg(pi, 0x2be), 10 * 1000 * 1000); + + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), + 0, 0x3, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + + wlc_phy_table_write_nphy(pi, + (core == + PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 + : NPHY_TBL_ID_EPSILONTBL1, 1, phy_a3, + 32, &phy_a8); + + if (cal_mode != CAL_GCTRL) { + if (CHSPEC_IS5G(pi->radio_chanspec)) + wlc_phy_a1_nphy(pi, core, 5, 0, 35); + } + + wlc_phy_rfctrl_override_1tomany_nphy( + pi, + NPHY_REV7_RfctrlOverride_cmd_txgain, + phy_a5, (1 << core), 1); + + } else { + + if (txgains) { + if (txgains->useindex) { + phy_a4 = 15 - ((txgains->index) >> 3); + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if (NREV_GE(pi->pubpi.phy_rev, 6) && + pi->sh->chip == BCMA_CHIP_ID_BCM47162) { + phy_a5 = 0x10f7 | (phy_a4 << 8); + } else if (NREV_GE(pi->pubpi.phy_rev, 6)) { + phy_a5 = 0x00f7 | (phy_a4 << 8); + } else if (NREV_IS(pi->pubpi.phy_rev, 5)) { + phy_a5 = 0x10f7 | (phy_a4 << 8); + } else { + phy_a5 = 0x50f7 | (phy_a4 << 8); + } + } else { + phy_a5 = 0x70f7 | (phy_a4 << 8); + } + wlc_phy_rfctrl_override_nphy(pi, + (0x1 << 13), + phy_a5, + (1 << core), 0); + } else { + wlc_phy_rfctrl_override_nphy(pi, + (0x1 << 13), + 0x5bf7, + (1 << core), 0); + } + } + + if (CHSPEC_IS2G(pi->radio_chanspec)) + m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 45 : 64; + else + m[core] = (pi->bw == WL_CHANSPEC_BW_40) ? 75 : 107; + + m[phy_a7] = 0; + wlc_phy_ipa_set_bbmult_nphy(pi, m[0], m[1]); + + phy_a2 = 63; + + if (cal_mode == CAL_FULL) { + phy_a1 = 25; + phy_a3 = 25; + } else if (cal_mode == CAL_SOFT) { + phy_a1 = 25; + phy_a3 = 25; + } else if (cal_mode == CAL_GCTRL) { + phy_a1 = 63; + phy_a3 = 25; + } else { + + phy_a1 = 25; + phy_a3 = 25; + } + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (1) << 0); + + mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (0) << 0); + + if (NREV_GE(pi->pubpi.phy_rev, 6)) { + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 13), (1) << 13); + + mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 13), (0) << 13); + + write_phy_reg(pi, 0x2a1, 0x20); + write_phy_reg(pi, 0x2a2, 0x60); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0xf << 4), (9) << 4); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0xf << 8), (9) << 8); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0xf << 0), (0x2) << 0); + + write_phy_reg(pi, 0x2e5, 0x20); + } else { + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 11), (1) << 11); + + mod_phy_reg(pi, (phy_a7 == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 11), (0) << 11); + + write_phy_reg(pi, 0x2a1, 0x80); + write_phy_reg(pi, 0x2a2, 0x600); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x7 << 4), (0) << 4); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x7 << 8), (0) << 8); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x7 << 0), (0x3) << 0); + + mod_phy_reg(pi, 0x2a0, (0x3f << 8), (0x20) << 8); + + } + + mod_phy_reg(pi, 0x2a0, (0x3f << 0), (phy_a3) << 0); + + mod_phy_reg(pi, 0x29f, (0x3f << 0), (phy_a1) << 0); + + mod_phy_reg(pi, 0x29f, (0x3f << 8), (phy_a2) << 8); + + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 1, 0x3, 0); + + write_phy_reg(pi, 0x2be, 1); + SPINWAIT(read_phy_reg(pi, 0x2be), 10 * 1000 * 1000); + + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 0x3, 0); + + wlc_phy_table_write_nphy(pi, + (core == + PHY_CORE_0) ? NPHY_TBL_ID_EPSILONTBL0 + : NPHY_TBL_ID_EPSILONTBL1, 1, phy_a3, + 32, &phy_a8); + + if (cal_mode != CAL_GCTRL) + wlc_phy_a1_nphy(pi, core, 5, 0, 40); + } +} + +static u8 wlc_phy_a3_nphy(struct brcms_phy *pi, u8 start_gain, u8 core) +{ + int phy_a1; + int phy_a2; + bool phy_a3; + struct nphy_ipa_txcalgains phy_a4; + bool phy_a5 = false; + bool phy_a6 = true; + s32 phy_a7, phy_a8; + u32 phy_a9; + int phy_a10; + bool phy_a11 = false; + int phy_a12; + u8 phy_a13 = 0; + u8 phy_a14; + u8 *phy_a15 = NULL; + + phy_a4.useindex = true; + phy_a12 = start_gain; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + phy_a2 = 20; + phy_a1 = 1; + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if (pi->pubpi.radiorev == 5) { + + phy_a15 = pad_gain_codes_used_2057rev5; + phy_a13 = + ARRAY_SIZE(pad_gain_codes_used_2057rev5) - 1; + + } else if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) { + + phy_a15 = pad_gain_codes_used_2057rev7; + phy_a13 = + ARRAY_SIZE(pad_gain_codes_used_2057rev7) - 1; + + } else { + + phy_a15 = pad_all_gain_codes_2057; + phy_a13 = ARRAY_SIZE(pad_all_gain_codes_2057) - + 1; + } + + } else { + + phy_a15 = pga_all_gain_codes_2057; + phy_a13 = ARRAY_SIZE(pga_all_gain_codes_2057) - 1; + } + + phy_a14 = 0; + + for (phy_a10 = 0; phy_a10 < phy_a2; phy_a10++) { + if (CHSPEC_IS2G(pi->radio_chanspec)) + phy_a4.gains.pad[core] = + (u16) phy_a15[phy_a12]; + else + phy_a4.gains.pga[core] = + (u16) phy_a15[phy_a12]; + + wlc_phy_a2_nphy(pi, &phy_a4, CAL_GCTRL, core); + + wlc_phy_table_read_nphy(pi, + (core == + PHY_CORE_0 ? + NPHY_TBL_ID_EPSILONTBL0 : + NPHY_TBL_ID_EPSILONTBL1), 1, + 63, 32, &phy_a9); + + wlc_phy_papd_decode_epsilon(phy_a9, &phy_a7, &phy_a8); + + phy_a3 = ((phy_a7 == 4095) || (phy_a7 == -4096) || + (phy_a8 == 4095) || (phy_a8 == -4096)); + + if (!phy_a6 && (phy_a3 != phy_a5)) { + if (!phy_a3) + phy_a12 -= (u8) phy_a1; + + phy_a11 = true; + break; + } + + if (phy_a3) + phy_a12 += (u8) phy_a1; + else + phy_a12 -= (u8) phy_a1; + + if ((phy_a12 < phy_a14) || (phy_a12 > phy_a13)) { + if (phy_a12 < phy_a14) + phy_a12 = phy_a14; + else + phy_a12 = phy_a13; + + phy_a11 = true; + break; + } + + phy_a6 = false; + phy_a5 = phy_a3; + } + + } else { + phy_a2 = 10; + phy_a1 = 8; + for (phy_a10 = 0; phy_a10 < phy_a2; phy_a10++) { + phy_a4.index = (u8) phy_a12; + wlc_phy_a2_nphy(pi, &phy_a4, CAL_GCTRL, core); + + wlc_phy_table_read_nphy(pi, + (core == + PHY_CORE_0 ? + NPHY_TBL_ID_EPSILONTBL0 : + NPHY_TBL_ID_EPSILONTBL1), 1, + 63, 32, &phy_a9); + + wlc_phy_papd_decode_epsilon(phy_a9, &phy_a7, &phy_a8); + + phy_a3 = ((phy_a7 == 4095) || (phy_a7 == -4096) || + (phy_a8 == 4095) || (phy_a8 == -4096)); + + if (!phy_a6 && (phy_a3 != phy_a5)) { + if (!phy_a3) + phy_a12 -= (u8) phy_a1; + + phy_a11 = true; + break; + } + + if (phy_a3) + phy_a12 += (u8) phy_a1; + else + phy_a12 -= (u8) phy_a1; + + if ((phy_a12 < 0) || (phy_a12 > 127)) { + if (phy_a12 < 0) + phy_a12 = 0; + else + phy_a12 = 127; + + phy_a11 = true; + break; + } + + phy_a6 = false; + phy_a5 = phy_a3; + } + + } + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + return (u8) phy_a15[phy_a12]; + else + return (u8) phy_a12; + +} + +static void wlc_phy_a4(struct brcms_phy *pi, bool full_cal) +{ + struct nphy_ipa_txcalgains phy_b1[2]; + struct nphy_papd_restore_state phy_b2; + bool phy_b3; + u8 phy_b4; + u8 phy_b5; + s16 phy_b6, phy_b7, phy_b8; + u16 phy_b9; + s16 phy_b10, phy_b11, phy_b12; + + phy_b11 = 0; + phy_b12 = 0; + phy_b7 = 0; + phy_b8 = 0; + phy_b6 = 0; + + if (pi->nphy_papd_skip == 1) + return; + + phy_b3 = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) & + MCTL_EN_MAC)); + if (!phy_b3) + wlapi_suspend_mac_and_wait(pi->sh->physhim); + + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + pi->nphy_force_papd_cal = false; + + for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) + pi->nphy_papd_tx_gain_at_last_cal[phy_b5] = + wlc_phy_txpwr_idx_cur_get_nphy(pi, phy_b5); + + pi->nphy_papd_last_cal = pi->sh->now; + pi->nphy_papd_recal_counter++; + + phy_b4 = pi->nphy_txpwrctrl; + wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SCALARTBL0, 64, 0, 32, + nphy_papd_scaltbl); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_SCALARTBL1, 64, 0, 32, + nphy_papd_scaltbl); + + phy_b9 = read_phy_reg(pi, 0x01); + mod_phy_reg(pi, 0x01, (0x1 << 15), 0); + + for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) { + s32 i, val = 0; + for (i = 0; i < 64; i++) + wlc_phy_table_write_nphy(pi, + ((phy_b5 == + PHY_CORE_0) ? + NPHY_TBL_ID_EPSILONTBL0 : + NPHY_TBL_ID_EPSILONTBL1), 1, + i, 32, &val); + } + + wlc_phy_ipa_restore_tx_digi_filts_nphy(pi); + + phy_b2.mm = wlc_phy_ipa_get_bbmult_nphy(pi); + for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) { + wlc_phy_papd_cal_setup_nphy(pi, &phy_b2, phy_b5); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if ((pi->pubpi.radiorev == 3) + || (pi->pubpi.radiorev == 4) + || (pi->pubpi.radiorev == 6)) { + pi->nphy_papd_cal_gain_index[phy_b5] = + 23; + } else if (pi->pubpi.radiorev == 5) { + pi->nphy_papd_cal_gain_index[phy_b5] = + 0; + pi->nphy_papd_cal_gain_index[phy_b5] = + wlc_phy_a3_nphy( + pi, + pi-> + nphy_papd_cal_gain_index + [phy_b5], + phy_b5); + + } else if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) { + + pi->nphy_papd_cal_gain_index[phy_b5] = + 0; + pi->nphy_papd_cal_gain_index[phy_b5] = + wlc_phy_a3_nphy( + pi, + pi-> + nphy_papd_cal_gain_index + [phy_b5], + phy_b5); + + } + + phy_b1[phy_b5].gains.pad[phy_b5] = + pi->nphy_papd_cal_gain_index[phy_b5]; + + } else { + pi->nphy_papd_cal_gain_index[phy_b5] = 0; + pi->nphy_papd_cal_gain_index[phy_b5] = + wlc_phy_a3_nphy( + pi, + pi-> + nphy_papd_cal_gain_index + [phy_b5], phy_b5); + phy_b1[phy_b5].gains.pga[phy_b5] = + pi->nphy_papd_cal_gain_index[phy_b5]; + } + } else { + phy_b1[phy_b5].useindex = true; + phy_b1[phy_b5].index = 16; + phy_b1[phy_b5].index = + wlc_phy_a3_nphy(pi, phy_b1[phy_b5].index, + phy_b5); + + pi->nphy_papd_cal_gain_index[phy_b5] = + 15 - ((phy_b1[phy_b5].index) >> 3); + } + + switch (pi->nphy_papd_cal_type) { + case 0: + wlc_phy_a2_nphy(pi, &phy_b1[phy_b5], CAL_FULL, phy_b5); + break; + case 1: + wlc_phy_a2_nphy(pi, &phy_b1[phy_b5], CAL_SOFT, phy_b5); + break; + } + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + wlc_phy_papd_cal_cleanup_nphy(pi, &phy_b2); + } + + if (NREV_LT(pi->pubpi.phy_rev, 7)) + wlc_phy_papd_cal_cleanup_nphy(pi, &phy_b2); + + for (phy_b5 = 0; phy_b5 < pi->pubpi.phy_corenum; phy_b5++) { + int eps_offset = 0; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if (CHSPEC_IS2G(pi->radio_chanspec)) { + if (pi->pubpi.radiorev == 3) + eps_offset = -2; + else if (pi->pubpi.radiorev == 5) + eps_offset = 3; + else + eps_offset = -1; + } else { + eps_offset = 2; + } + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + phy_b8 = phy_b1[phy_b5].gains.pad[phy_b5]; + phy_b10 = 0; + if ((pi->pubpi.radiorev == 3) || + (pi->pubpi.radiorev == 4) || + (pi->pubpi.radiorev == 6)) { + phy_b12 = -( + nphy_papd_padgain_dlt_2g_2057rev3n4 + [phy_b8] + 1) / 2; + phy_b10 = -1; + } else if (pi->pubpi.radiorev == 5) { + phy_b12 = -( + nphy_papd_padgain_dlt_2g_2057rev5 + [phy_b8] + 1) / 2; + } else if ((pi->pubpi.radiorev == 7) || + (pi->pubpi.radiorev == 8)) { + phy_b12 = -( + nphy_papd_padgain_dlt_2g_2057rev7 + [phy_b8] + 1) / 2; + } + } else { + phy_b7 = phy_b1[phy_b5].gains.pga[phy_b5]; + if ((pi->pubpi.radiorev == 3) || + (pi->pubpi.radiorev == 4) || + (pi->pubpi.radiorev == 6)) + phy_b11 = + -(nphy_papd_pgagain_dlt_5g_2057 + [phy_b7] + + 1) / 2; + else if ((pi->pubpi.radiorev == 7) + || (pi->pubpi.radiorev == 8)) + phy_b11 = -( + nphy_papd_pgagain_dlt_5g_2057rev7 + [phy_b7] + 1) / 2; + + phy_b10 = -9; + } + + if (CHSPEC_IS2G(pi->radio_chanspec)) + phy_b6 = + -60 + 27 + eps_offset + phy_b12 + + phy_b10; + else + phy_b6 = + -60 + 27 + eps_offset + phy_b11 + + phy_b10; + + mod_phy_reg(pi, (phy_b5 == PHY_CORE_0) ? 0x298 : + 0x29c, (0x1ff << 7), (phy_b6) << 7); + + pi->nphy_papd_epsilon_offset[phy_b5] = phy_b6; + } else { + if (NREV_LT(pi->pubpi.phy_rev, 5)) + eps_offset = 4; + else + eps_offset = 2; + + phy_b7 = 15 - ((phy_b1[phy_b5].index) >> 3); + + if (CHSPEC_IS2G(pi->radio_chanspec)) { + phy_b11 = + -(nphy_papd_pga_gain_delta_ipa_2g[ + phy_b7] + + 1) / 2; + phy_b10 = 0; + } else { + phy_b11 = + -(nphy_papd_pga_gain_delta_ipa_5g[ + phy_b7] + + 1) / 2; + phy_b10 = -9; + } + + phy_b6 = -60 + 27 + eps_offset + phy_b11 + phy_b10; + + mod_phy_reg(pi, (phy_b5 == PHY_CORE_0) ? 0x298 : + 0x29c, (0x1ff << 7), (phy_b6) << 7); + + pi->nphy_papd_epsilon_offset[phy_b5] = phy_b6; + } + } + + mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0); + + mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (NPHY_PAPD_COMP_ON) << 0); + + if (NREV_GE(pi->pubpi.phy_rev, 6)) { + mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 13), (0) << 13); + + mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 13), (0) << 13); + + } else { + mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 11), (0) << 11); + + mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x2a3 : + 0x2a4, (0x1 << 11), (0) << 11); + + } + pi->nphy_papdcomp = NPHY_PAPD_COMP_ON; + + write_phy_reg(pi, 0x01, phy_b9); + + wlc_phy_ipa_set_tx_digi_filts_nphy(pi); + + wlc_phy_txpwrctrl_enable_nphy(pi, phy_b4); + if (phy_b4 == PHY_TPC_HW_OFF) { + wlc_phy_txpwr_index_nphy(pi, (1 << 0), + (s8) (pi->nphy_txpwrindex[0]. + index_internal), false); + wlc_phy_txpwr_index_nphy(pi, (1 << 1), + (s8) (pi->nphy_txpwrindex[1]. + index_internal), false); + } + + wlc_phy_stay_in_carriersearch_nphy(pi, false); + + if (!phy_b3) + wlapi_enable_mac(pi->sh->physhim); +} + +void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype) +{ + struct nphy_txgains target_gain; + u8 tx_pwr_ctrl_state; + bool fullcal = true; + bool restore_tx_gain = false; + bool mphase; + + if (PHY_MUTED(pi)) + return; + + if (caltype == PHY_PERICAL_AUTO) + fullcal = (pi->radio_chanspec != pi->nphy_txiqlocal_chanspec); + else if (caltype == PHY_PERICAL_PARTIAL) + fullcal = false; + + if (pi->cal_type_override != PHY_PERICAL_AUTO) + fullcal = + (pi->cal_type_override == + PHY_PERICAL_FULL) ? true : false; + + if ((pi->mphase_cal_phase_id > MPHASE_CAL_STATE_INIT)) { + if (pi->nphy_txiqlocal_chanspec != pi->radio_chanspec) + wlc_phy_cal_perical_mphase_restart(pi); + } + + if ((pi->mphase_cal_phase_id == MPHASE_CAL_STATE_RXCAL)) + wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, 10000); + + wlapi_suspend_mac_and_wait(pi->sh->physhim); + + wlc_phyreg_enter((struct brcms_phy_pub *) pi); + + if ((pi->mphase_cal_phase_id == MPHASE_CAL_STATE_IDLE) || + (pi->mphase_cal_phase_id == MPHASE_CAL_STATE_INIT)) { + pi->nphy_cal_orig_pwr_idx[0] = + (u8) ((read_phy_reg(pi, 0x1ed) >> 8) & 0x7f); + pi->nphy_cal_orig_pwr_idx[1] = + (u8) ((read_phy_reg(pi, 0x1ee) >> 8) & 0x7f); + + if (pi->nphy_txpwrctrl != PHY_TPC_HW_OFF) { + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, + 0x110, 16, + pi->nphy_cal_orig_tx_gain); + } else { + pi->nphy_cal_orig_tx_gain[0] = 0; + pi->nphy_cal_orig_tx_gain[1] = 0; + } + } + target_gain = wlc_phy_get_tx_gain_nphy(pi); + tx_pwr_ctrl_state = pi->nphy_txpwrctrl; + wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); + + if (pi->antsel_type == ANTSEL_2x3) + wlc_phy_antsel_init((struct brcms_phy_pub *) pi, true); + + mphase = (pi->mphase_cal_phase_id != MPHASE_CAL_STATE_IDLE); + if (!mphase) { + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + wlc_phy_precal_txgain_nphy(pi); + pi->nphy_cal_target_gain = wlc_phy_get_tx_gain_nphy(pi); + restore_tx_gain = true; + + target_gain = pi->nphy_cal_target_gain; + } + if (0 == + wlc_phy_cal_txiqlo_nphy(pi, target_gain, fullcal, + mphase)) { + if (PHY_IPA(pi)) + wlc_phy_a4(pi, true); + + wlc_phyreg_exit((struct brcms_phy_pub *) pi); + wlapi_enable_mac(pi->sh->physhim); + wlapi_bmac_write_shm(pi->sh->physhim, M_CTS_DURATION, + 10000); + wlapi_suspend_mac_and_wait(pi->sh->physhim); + wlc_phyreg_enter((struct brcms_phy_pub *) pi); + + if (0 == wlc_phy_cal_rxiq_nphy(pi, target_gain, + (pi->first_cal_after_assoc || + (pi->cal_type_override == + PHY_PERICAL_FULL)) ? 2 : 0, false)) { + wlc_phy_savecal_nphy(pi); + + wlc_phy_txpwrctrl_coeff_setup_nphy(pi); + + pi->nphy_perical_last = pi->sh->now; + } + } + if (caltype != PHY_PERICAL_AUTO) + wlc_phy_rssi_cal_nphy(pi); + + if (pi->first_cal_after_assoc + || (pi->cal_type_override == PHY_PERICAL_FULL)) { + pi->first_cal_after_assoc = false; + wlc_phy_txpwrctrl_idle_tssi_nphy(pi); + wlc_phy_txpwrctrl_pwr_setup_nphy(pi); + } + + if (NREV_GE(pi->pubpi.phy_rev, 3)) + wlc_phy_radio205x_vcocal_nphy(pi); + } else { + switch (pi->mphase_cal_phase_id) { + case MPHASE_CAL_STATE_INIT: + pi->nphy_perical_last = pi->sh->now; + pi->nphy_txiqlocal_chanspec = pi->radio_chanspec; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) + wlc_phy_precal_txgain_nphy(pi); + + pi->nphy_cal_target_gain = wlc_phy_get_tx_gain_nphy(pi); + pi->mphase_cal_phase_id++; + break; + + case MPHASE_CAL_STATE_TXPHASE0: + case MPHASE_CAL_STATE_TXPHASE1: + case MPHASE_CAL_STATE_TXPHASE2: + case MPHASE_CAL_STATE_TXPHASE3: + case MPHASE_CAL_STATE_TXPHASE4: + case MPHASE_CAL_STATE_TXPHASE5: + if ((pi->radar_percal_mask & 0x10) != 0) + pi->nphy_rxcal_active = true; + + if (wlc_phy_cal_txiqlo_nphy + (pi, pi->nphy_cal_target_gain, fullcal, + true) != 0) { + + wlc_phy_cal_perical_mphase_reset(pi); + break; + } + + if (NREV_LE(pi->pubpi.phy_rev, 2) && + (pi->mphase_cal_phase_id == + MPHASE_CAL_STATE_TXPHASE4)) + pi->mphase_cal_phase_id += 2; + else + pi->mphase_cal_phase_id++; + break; + + case MPHASE_CAL_STATE_PAPDCAL: + if ((pi->radar_percal_mask & 0x2) != 0) + pi->nphy_rxcal_active = true; + + if (PHY_IPA(pi)) + wlc_phy_a4(pi, true); + + pi->mphase_cal_phase_id++; + break; + + case MPHASE_CAL_STATE_RXCAL: + if ((pi->radar_percal_mask & 0x1) != 0) + pi->nphy_rxcal_active = true; + if (wlc_phy_cal_rxiq_nphy(pi, target_gain, + (pi->first_cal_after_assoc || + (pi->cal_type_override == + PHY_PERICAL_FULL)) ? 2 : 0, + false) == 0) + wlc_phy_savecal_nphy(pi); + + pi->mphase_cal_phase_id++; + break; + + case MPHASE_CAL_STATE_RSSICAL: + if ((pi->radar_percal_mask & 0x4) != 0) + pi->nphy_rxcal_active = true; + wlc_phy_txpwrctrl_coeff_setup_nphy(pi); + wlc_phy_rssi_cal_nphy(pi); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) + wlc_phy_radio205x_vcocal_nphy(pi); + + restore_tx_gain = true; + + if (pi->first_cal_after_assoc) + pi->mphase_cal_phase_id++; + else + wlc_phy_cal_perical_mphase_reset(pi); + + break; + + case MPHASE_CAL_STATE_IDLETSSI: + if ((pi->radar_percal_mask & 0x8) != 0) + pi->nphy_rxcal_active = true; + + if (pi->first_cal_after_assoc) { + pi->first_cal_after_assoc = false; + wlc_phy_txpwrctrl_idle_tssi_nphy(pi); + wlc_phy_txpwrctrl_pwr_setup_nphy(pi); + } + + wlc_phy_cal_perical_mphase_reset(pi); + break; + + default: + wlc_phy_cal_perical_mphase_reset(pi); + break; + } + } + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + if (restore_tx_gain) { + if (tx_pwr_ctrl_state != PHY_TPC_HW_OFF) { + + wlc_phy_txpwr_index_nphy(pi, 1, + pi-> + nphy_cal_orig_pwr_idx + [0], false); + wlc_phy_txpwr_index_nphy(pi, 2, + pi-> + nphy_cal_orig_pwr_idx + [1], false); + + pi->nphy_txpwrindex[0].index = -1; + pi->nphy_txpwrindex[1].index = -1; + } else { + wlc_phy_txpwr_index_nphy(pi, (1 << 0), + (s8) (pi-> + nphy_txpwrindex + [0]. + index_internal), + false); + wlc_phy_txpwr_index_nphy(pi, (1 << 1), + (s8) (pi-> + nphy_txpwrindex + [1]. + index_internal), + false); + } + } + } + + wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); + wlc_phyreg_exit((struct brcms_phy_pub *) pi); + wlapi_enable_mac(pi->sh->physhim); +} + +int +wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, + bool fullcal, bool mphase) +{ + u16 val; + u16 tbl_buf[11]; + u8 cal_cnt; + u16 cal_cmd; + u8 num_cals, max_cal_cmds; + u16 core_no, cal_type; + u16 diq_start = 0; + u8 phy_bw; + u16 max_val; + u16 tone_freq; + u16 gain_save[2]; + u16 cal_gain[2]; + struct nphy_iqcal_params cal_params[2]; + u32 tbl_len; + void *tbl_ptr; + bool ladder_updated[2]; + u8 mphase_cal_lastphase = 0; + int bcmerror = 0; + bool phyhang_avoid_state = false; + + u16 tbl_tx_iqlo_cal_loft_ladder_20[] = { + 0x0300, 0x0500, 0x0700, 0x0900, 0x0d00, 0x1100, 0x1900, 0x1901, + 0x1902, + 0x1903, 0x1904, 0x1905, 0x1906, 0x1907, 0x2407, 0x3207, 0x4607, + 0x6407 + }; + + u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = { + 0x0200, 0x0300, 0x0600, 0x0900, 0x0d00, 0x1100, 0x1900, 0x2400, + 0x3200, + 0x4600, 0x6400, 0x6401, 0x6402, 0x6403, 0x6404, 0x6405, 0x6406, + 0x6407 + }; + + u16 tbl_tx_iqlo_cal_loft_ladder_40[] = { + 0x0200, 0x0300, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1201, + 0x1202, + 0x1203, 0x1204, 0x1205, 0x1206, 0x1207, 0x1907, 0x2307, 0x3207, + 0x4707 + }; + + u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = { + 0x0100, 0x0200, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1900, + 0x2300, + 0x3200, 0x4700, 0x4701, 0x4702, 0x4703, 0x4704, 0x4705, 0x4706, + 0x4707 + }; + + u16 tbl_tx_iqlo_cal_startcoefs[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000 + }; + + u16 tbl_tx_iqlo_cal_cmds_fullcal[] = { + 0x8123, 0x8264, 0x8086, 0x8245, 0x8056, + 0x9123, 0x9264, 0x9086, 0x9245, 0x9056 + }; + + u16 tbl_tx_iqlo_cal_cmds_recal[] = { + 0x8101, 0x8253, 0x8053, 0x8234, 0x8034, + 0x9101, 0x9253, 0x9053, 0x9234, 0x9034 + }; + + u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000 + }; + + u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = { + 0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234, + 0x9434, 0x9334, 0x9084, 0x9267, 0x9056, 0x9234 + }; + + u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = { + 0x8423, 0x8323, 0x8073, 0x8256, 0x8045, 0x8223, + 0x9423, 0x9323, 0x9073, 0x9256, 0x9045, 0x9223 + }; + + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + if (NREV_GE(pi->pubpi.phy_rev, 4)) { + phyhang_avoid_state = pi->phyhang_avoid; + pi->phyhang_avoid = false; + } + + if (CHSPEC_IS40(pi->radio_chanspec)) + phy_bw = 40; + else + phy_bw = 20; + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); + + for (core_no = 0; core_no <= 1; core_no++) { + wlc_phy_iqcal_gainparams_nphy(pi, core_no, target_gain, + &cal_params[core_no]); + cal_gain[core_no] = cal_params[core_no].cal_gain; + } + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, cal_gain); + + wlc_phy_txcal_radio_setup_nphy(pi); + + wlc_phy_txcal_physetup_nphy(pi); + + ladder_updated[0] = ladder_updated[1] = false; + if (!(NREV_GE(pi->pubpi.phy_rev, 6) || + (NREV_IS(pi->pubpi.phy_rev, 5) && PHY_IPA(pi) + && (CHSPEC_IS2G(pi->radio_chanspec))))) { + + if (phy_bw == 40) { + tbl_ptr = tbl_tx_iqlo_cal_loft_ladder_40; + tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_loft_ladder_40); + } else { + tbl_ptr = tbl_tx_iqlo_cal_loft_ladder_20; + tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_loft_ladder_20); + } + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 0, + 16, tbl_ptr); + + if (phy_bw == 40) { + tbl_ptr = tbl_tx_iqlo_cal_iqimb_ladder_40; + tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_iqimb_ladder_40); + } else { + tbl_ptr = tbl_tx_iqlo_cal_iqimb_ladder_20; + tbl_len = ARRAY_SIZE(tbl_tx_iqlo_cal_iqimb_ladder_20); + } + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 32, + 16, tbl_ptr); + } + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + write_phy_reg(pi, 0xc2, 0x8ad9); + else + write_phy_reg(pi, 0xc2, 0x8aa9); + + max_val = 250; + tone_freq = (phy_bw == 20) ? 2500 : 5000; + + if (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_TXPHASE0) { + wlc_phy_runsamples_nphy(pi, phy_bw * 8, 0xffff, 0, 1, 0, false); + bcmerror = 0; + } else { + bcmerror = + wlc_phy_tx_tone_nphy(pi, tone_freq, max_val, 1, 0, + false); + } + + if (bcmerror == 0) { + + if (pi->mphase_cal_phase_id > MPHASE_CAL_STATE_TXPHASE0) { + tbl_ptr = pi->mphase_txcal_bestcoeffs; + tbl_len = ARRAY_SIZE(pi->mphase_txcal_bestcoeffs); + if (NREV_LT(pi->pubpi.phy_rev, 3)) + tbl_len -= 2; + } else { + if ((!fullcal) && (pi->nphy_txiqlocal_coeffsvalid)) { + + tbl_ptr = pi->nphy_txiqlocal_bestc; + tbl_len = ARRAY_SIZE(pi->nphy_txiqlocal_bestc); + if (NREV_LT(pi->pubpi.phy_rev, 3)) + tbl_len -= 2; + } else { + + fullcal = true; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + tbl_ptr = + tbl_tx_iqlo_cal_startcoefs_nphyrev3; + tbl_len = ARRAY_SIZE( + tbl_tx_iqlo_cal_startcoefs_nphyrev3); + } else { + tbl_ptr = tbl_tx_iqlo_cal_startcoefs; + tbl_len = ARRAY_SIZE( + tbl_tx_iqlo_cal_startcoefs); + } + } + } + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, tbl_len, 64, + 16, tbl_ptr); + + if (fullcal) { + max_cal_cmds = (NREV_GE(pi->pubpi.phy_rev, 3)) ? + ARRAY_SIZE( + tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3) : + ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_fullcal); + } else { + max_cal_cmds = (NREV_GE(pi->pubpi.phy_rev, 3)) ? + ARRAY_SIZE( + tbl_tx_iqlo_cal_cmds_recal_nphyrev3) : + ARRAY_SIZE(tbl_tx_iqlo_cal_cmds_recal); + } + + if (mphase) { + cal_cnt = pi->mphase_txcal_cmdidx; + if ((cal_cnt + pi->mphase_txcal_numcmds) < max_cal_cmds) + num_cals = cal_cnt + pi->mphase_txcal_numcmds; + else + num_cals = max_cal_cmds; + } else { + cal_cnt = 0; + num_cals = max_cal_cmds; + } + + for (; cal_cnt < num_cals; cal_cnt++) { + + if (fullcal) { + cal_cmd = (NREV_GE(pi->pubpi.phy_rev, 3)) ? + tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3 + [cal_cnt] : + tbl_tx_iqlo_cal_cmds_fullcal[cal_cnt]; + } else { + cal_cmd = (NREV_GE(pi->pubpi.phy_rev, 3)) ? + tbl_tx_iqlo_cal_cmds_recal_nphyrev3[ + cal_cnt] + : tbl_tx_iqlo_cal_cmds_recal[cal_cnt]; + } + + core_no = ((cal_cmd & 0x3000) >> 12); + cal_type = ((cal_cmd & 0x0F00) >> 8); + + if (NREV_GE(pi->pubpi.phy_rev, 6) || + (NREV_IS(pi->pubpi.phy_rev, 5) && + PHY_IPA(pi) + && (CHSPEC_IS2G(pi->radio_chanspec)))) { + if (!ladder_updated[core_no]) { + wlc_phy_update_txcal_ladder_nphy( + pi, + core_no); + ladder_updated[core_no] = true; + } + } + + val = + (cal_params[core_no]. + ncorr[cal_type] << 8) | NPHY_N_GCTL; + write_phy_reg(pi, 0xc1, val); + + if ((cal_type == 1) || (cal_type == 3) + || (cal_type == 4)) { + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, + 1, 69 + core_no, 16, + tbl_buf); + + diq_start = tbl_buf[0]; + + tbl_buf[0] = 0; + wlc_phy_table_write_nphy(pi, + NPHY_TBL_ID_IQLOCAL, 1, + 69 + core_no, 16, + tbl_buf); + } + + write_phy_reg(pi, 0xc0, cal_cmd); + + SPINWAIT(((read_phy_reg(pi, 0xc0) & 0xc000) != 0), + 20000); + if (WARN(read_phy_reg(pi, 0xc0) & 0xc000, + "HW error: txiq calib")) + return -EIO; + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, + tbl_len, 96, 16, tbl_buf); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, + tbl_len, 64, 16, tbl_buf); + + if ((cal_type == 1) || (cal_type == 3) + || (cal_type == 4)) { + + tbl_buf[0] = diq_start; + + } + + } + + if (mphase) { + pi->mphase_txcal_cmdidx = num_cals; + if (pi->mphase_txcal_cmdidx >= max_cal_cmds) + pi->mphase_txcal_cmdidx = 0; + } + + mphase_cal_lastphase = + (NREV_LE(pi->pubpi.phy_rev, 2)) ? + MPHASE_CAL_STATE_TXPHASE4 : MPHASE_CAL_STATE_TXPHASE5; + + if (!mphase + || (pi->mphase_cal_phase_id == mphase_cal_lastphase)) { + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 96, + 16, tbl_buf); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 80, + 16, tbl_buf); + + if (NREV_LT(pi->pubpi.phy_rev, 2)) { + + tbl_buf[0] = 0; + tbl_buf[1] = 0; + tbl_buf[2] = 0; + tbl_buf[3] = 0; + + } + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 88, + 16, tbl_buf); + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 101, + 16, tbl_buf); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 85, + 16, tbl_buf); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 93, + 16, tbl_buf); + + tbl_len = ARRAY_SIZE(pi->nphy_txiqlocal_bestc); + if (NREV_LT(pi->pubpi.phy_rev, 3)) + tbl_len -= 2; + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, + tbl_len, 96, 16, + pi->nphy_txiqlocal_bestc); + + pi->nphy_txiqlocal_coeffsvalid = true; + pi->nphy_txiqlocal_chanspec = pi->radio_chanspec; + } else { + tbl_len = ARRAY_SIZE(pi->mphase_txcal_bestcoeffs); + if (NREV_LT(pi->pubpi.phy_rev, 3)) + tbl_len -= 2; + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, + tbl_len, 96, 16, + pi->mphase_txcal_bestcoeffs); + } + + wlc_phy_stopplayback_nphy(pi); + + write_phy_reg(pi, 0xc2, 0x0000); + + } + + wlc_phy_txcal_phycleanup_nphy(pi); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, + gain_save); + + wlc_phy_txcal_radio_cleanup_nphy(pi); + + if (NREV_LT(pi->pubpi.phy_rev, 2)) { + if (!mphase + || (pi->mphase_cal_phase_id == mphase_cal_lastphase)) + wlc_phy_tx_iq_war_nphy(pi); + } + + if (NREV_GE(pi->pubpi.phy_rev, 4)) + pi->phyhang_avoid = phyhang_avoid_state; + + wlc_phy_stay_in_carriersearch_nphy(pi, false); + + return bcmerror; +} + +static void wlc_phy_reapply_txcal_coeffs_nphy(struct brcms_phy *pi) +{ + u16 tbl_buf[7]; + + if ((pi->nphy_txiqlocal_chanspec == pi->radio_chanspec) && + (pi->nphy_txiqlocal_coeffsvalid)) { + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_IQLOCAL, + ARRAY_SIZE(tbl_buf), 80, 16, tbl_buf); + + if ((pi->nphy_txiqlocal_bestc[0] != tbl_buf[0]) || + (pi->nphy_txiqlocal_bestc[1] != tbl_buf[1]) || + (pi->nphy_txiqlocal_bestc[2] != tbl_buf[2]) || + (pi->nphy_txiqlocal_bestc[3] != tbl_buf[3])) { + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 80, + 16, pi->nphy_txiqlocal_bestc); + + tbl_buf[0] = 0; + tbl_buf[1] = 0; + tbl_buf[2] = 0; + tbl_buf[3] = 0; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 4, 88, + 16, tbl_buf); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 85, + 16, + &pi->nphy_txiqlocal_bestc[5]); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_IQLOCAL, 2, 93, + 16, + &pi->nphy_txiqlocal_bestc[5]); + } + } +} + +void +wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write, + struct nphy_iq_comp *pcomp) +{ + if (write) { + write_phy_reg(pi, 0x9a, pcomp->a0); + write_phy_reg(pi, 0x9b, pcomp->b0); + write_phy_reg(pi, 0x9c, pcomp->a1); + write_phy_reg(pi, 0x9d, pcomp->b1); + } else { + pcomp->a0 = read_phy_reg(pi, 0x9a); + pcomp->b0 = read_phy_reg(pi, 0x9b); + pcomp->a1 = read_phy_reg(pi, 0x9c); + pcomp->b1 = read_phy_reg(pi, 0x9d); + } +} + +void +wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est, + u16 num_samps, u8 wait_time, u8 wait_for_crs) +{ + u8 core; + + write_phy_reg(pi, 0x12b, num_samps); + mod_phy_reg(pi, 0x12a, (0xff << 0), (wait_time << 0)); + mod_phy_reg(pi, 0x129, NPHY_IqestCmd_iqMode, + (wait_for_crs) ? NPHY_IqestCmd_iqMode : 0); + + mod_phy_reg(pi, 0x129, NPHY_IqestCmd_iqstart, NPHY_IqestCmd_iqstart); + + SPINWAIT(((read_phy_reg(pi, 0x129) & NPHY_IqestCmd_iqstart) != 0), + 10000); + if (WARN(read_phy_reg(pi, 0x129) & NPHY_IqestCmd_iqstart, + "HW error: rxiq est")) + return; + + if ((read_phy_reg(pi, 0x129) & NPHY_IqestCmd_iqstart) == 0) { + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + est[core].i_pwr = + (read_phy_reg(pi, + NPHY_IqestipwrAccHi(core)) << 16) + | read_phy_reg(pi, NPHY_IqestipwrAccLo(core)); + est[core].q_pwr = + (read_phy_reg(pi, + NPHY_IqestqpwrAccHi(core)) << 16) + | read_phy_reg(pi, NPHY_IqestqpwrAccLo(core)); + est[core].iq_prod = + (read_phy_reg(pi, + NPHY_IqestIqAccHi(core)) << 16) | + read_phy_reg(pi, NPHY_IqestIqAccLo(core)); + } + } +} + +#define CAL_RETRY_CNT 2 +static void wlc_phy_calc_rx_iq_comp_nphy(struct brcms_phy *pi, u8 core_mask) +{ + u8 curr_core; + struct phy_iq_est est[PHY_CORE_MAX]; + struct nphy_iq_comp old_comp, new_comp; + s32 iq = 0; + u32 ii = 0, qq = 0; + s16 iq_nbits, qq_nbits, brsh, arsh; + s32 a, b, temp; + int bcmerror = 0; + uint cal_retry = 0; + + if (core_mask == 0x0) + return; + + wlc_phy_rx_iq_coeffs_nphy(pi, 0, &old_comp); + new_comp.a0 = new_comp.b0 = new_comp.a1 = new_comp.b1 = 0x0; + wlc_phy_rx_iq_coeffs_nphy(pi, 1, &new_comp); + +cal_try: + wlc_phy_rx_iq_est_nphy(pi, est, 0x4000, 32, 0); + + new_comp = old_comp; + + for (curr_core = 0; curr_core < pi->pubpi.phy_corenum; curr_core++) { + + if ((curr_core == PHY_CORE_0) && (core_mask & 0x1)) { + iq = est[curr_core].iq_prod; + ii = est[curr_core].i_pwr; + qq = est[curr_core].q_pwr; + } else if ((curr_core == PHY_CORE_1) && (core_mask & 0x2)) { + iq = est[curr_core].iq_prod; + ii = est[curr_core].i_pwr; + qq = est[curr_core].q_pwr; + } else { + continue; + } + + if ((ii + qq) < NPHY_MIN_RXIQ_PWR) { + bcmerror = -EBADE; + break; + } + + iq_nbits = wlc_phy_nbits(iq); + qq_nbits = wlc_phy_nbits(qq); + + arsh = 10 - (30 - iq_nbits); + if (arsh >= 0) { + a = (-(iq << (30 - iq_nbits)) + (ii >> (1 + arsh))); + temp = (s32) (ii >> arsh); + if (temp == 0) { + bcmerror = -EBADE; + break; + } + } else { + a = (-(iq << (30 - iq_nbits)) + (ii << (-1 - arsh))); + temp = (s32) (ii << -arsh); + if (temp == 0) { + bcmerror = -EBADE; + break; + } + } + + a /= temp; + + brsh = qq_nbits - 31 + 20; + if (brsh >= 0) { + b = (qq << (31 - qq_nbits)); + temp = (s32) (ii >> brsh); + if (temp == 0) { + bcmerror = -EBADE; + break; + } + } else { + b = (qq << (31 - qq_nbits)); + temp = (s32) (ii << -brsh); + if (temp == 0) { + bcmerror = -EBADE; + break; + } + } + b /= temp; + b -= a * a; + b = (s32) int_sqrt((unsigned long) b); + b -= (1 << 10); + + if ((curr_core == PHY_CORE_0) && (core_mask & 0x1)) { + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + new_comp.a0 = (s16) a & 0x3ff; + new_comp.b0 = (s16) b & 0x3ff; + } else { + + new_comp.a0 = (s16) b & 0x3ff; + new_comp.b0 = (s16) a & 0x3ff; + } + } + if ((curr_core == PHY_CORE_1) && (core_mask & 0x2)) { + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + new_comp.a1 = (s16) a & 0x3ff; + new_comp.b1 = (s16) b & 0x3ff; + } else { + + new_comp.a1 = (s16) b & 0x3ff; + new_comp.b1 = (s16) a & 0x3ff; + } + } + } + + if (bcmerror != 0) { + pr_debug("%s: Failed, cnt = %d\n", __func__, cal_retry); + + if (cal_retry < CAL_RETRY_CNT) { + cal_retry++; + goto cal_try; + } + + new_comp = old_comp; + } + + wlc_phy_rx_iq_coeffs_nphy(pi, 1, &new_comp); +} + +static void wlc_phy_rxcal_radio_setup_nphy(struct brcms_phy *pi, u8 rx_core) +{ + u16 offtune_val; + u16 bias_g = 0; + u16 bias_a = 0; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if (rx_core == PHY_CORE_0) { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + pi->tx_rx_cal_radio_saveregs[0] = + read_radio_reg(pi, + RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP); + pi->tx_rx_cal_radio_saveregs[1] = + read_radio_reg(pi, + RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN); + + write_radio_reg(pi, + RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP, + 0x3); + write_radio_reg(pi, + RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN, + 0xaf); + + } else { + pi->tx_rx_cal_radio_saveregs[0] = + read_radio_reg(pi, + RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP); + pi->tx_rx_cal_radio_saveregs[1] = + read_radio_reg(pi, + RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN); + + write_radio_reg( + pi, + RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP, + 0x3); + write_radio_reg( + pi, + RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN, + 0x7f); + } + + } else { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + pi->tx_rx_cal_radio_saveregs[0] = + read_radio_reg(pi, + RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP); + pi->tx_rx_cal_radio_saveregs[1] = + read_radio_reg(pi, + RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN); + + write_radio_reg( + pi, + RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP, + 0x3); + write_radio_reg( + pi, + RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN, + 0xaf); + + } else { + pi->tx_rx_cal_radio_saveregs[0] = + read_radio_reg(pi, + RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP); + pi->tx_rx_cal_radio_saveregs[1] = + read_radio_reg(pi, + RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN); + + write_radio_reg(pi, + RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP, + 0x3); + write_radio_reg(pi, + RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN, + 0x7f); + } + } + + } else { + if (rx_core == PHY_CORE_0) { + pi->tx_rx_cal_radio_saveregs[0] = + read_radio_reg(pi, + RADIO_2056_TX_RXIQCAL_TXMUX | + RADIO_2056_TX1); + pi->tx_rx_cal_radio_saveregs[1] = + read_radio_reg(pi, + RADIO_2056_RX_RXIQCAL_RXMUX | + RADIO_2056_RX0); + + if (pi->pubpi.radiorev >= 5) { + pi->tx_rx_cal_radio_saveregs[2] = + read_radio_reg(pi, + RADIO_2056_RX_RXSPARE2 | + RADIO_2056_RX0); + pi->tx_rx_cal_radio_saveregs[3] = + read_radio_reg(pi, + RADIO_2056_TX_TXSPARE2 | + RADIO_2056_TX1); + } + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + + if (pi->pubpi.radiorev >= 5) { + pi->tx_rx_cal_radio_saveregs[4] = + read_radio_reg(pi, + RADIO_2056_RX_LNAA_MASTER + | RADIO_2056_RX0); + + write_radio_reg( + pi, + RADIO_2056_RX_LNAA_MASTER + | RADIO_2056_RX0, 0x40); + + write_radio_reg(pi, + RADIO_2056_TX_TXSPARE2 | + RADIO_2056_TX1, bias_a); + + write_radio_reg(pi, + RADIO_2056_RX_RXSPARE2 | + RADIO_2056_RX0, bias_a); + } else { + pi->tx_rx_cal_radio_saveregs[4] = + read_radio_reg(pi, + RADIO_2056_RX_LNAA_TUNE + | RADIO_2056_RX0); + + offtune_val = + (pi->tx_rx_cal_radio_saveregs + [2] & 0xF0) >> 8; + offtune_val = + (offtune_val <= 0x7) ? 0xF : 0; + + mod_radio_reg(pi, + RADIO_2056_RX_LNAA_TUNE | + RADIO_2056_RX0, 0xF0, + (offtune_val << 8)); + } + + write_radio_reg(pi, + RADIO_2056_TX_RXIQCAL_TXMUX | + RADIO_2056_TX1, 0x9); + write_radio_reg(pi, + RADIO_2056_RX_RXIQCAL_RXMUX | + RADIO_2056_RX0, 0x9); + } else { + if (pi->pubpi.radiorev >= 5) { + pi->tx_rx_cal_radio_saveregs[4] = + read_radio_reg( + pi, + RADIO_2056_RX_LNAG_MASTER + | RADIO_2056_RX0); + + write_radio_reg( + pi, + RADIO_2056_RX_LNAG_MASTER + | RADIO_2056_RX0, 0x40); + + write_radio_reg( + pi, + RADIO_2056_TX_TXSPARE2 + | + RADIO_2056_TX1, bias_g); + + write_radio_reg( + pi, + RADIO_2056_RX_RXSPARE2 + | + RADIO_2056_RX0, bias_g); + + } else { + pi->tx_rx_cal_radio_saveregs[4] = + read_radio_reg( + pi, + RADIO_2056_RX_LNAG_TUNE + | RADIO_2056_RX0); + + offtune_val = + (pi-> + tx_rx_cal_radio_saveregs[2] & + 0xF0) >> 8; + offtune_val = + (offtune_val <= 0x7) ? 0xF : 0; + + mod_radio_reg(pi, + RADIO_2056_RX_LNAG_TUNE | + RADIO_2056_RX0, 0xF0, + (offtune_val << 8)); + } + + write_radio_reg(pi, + RADIO_2056_TX_RXIQCAL_TXMUX | + RADIO_2056_TX1, 0x6); + write_radio_reg(pi, + RADIO_2056_RX_RXIQCAL_RXMUX | + RADIO_2056_RX0, 0x6); + } + + } else { + pi->tx_rx_cal_radio_saveregs[0] = + read_radio_reg(pi, + RADIO_2056_TX_RXIQCAL_TXMUX | + RADIO_2056_TX0); + pi->tx_rx_cal_radio_saveregs[1] = + read_radio_reg(pi, + RADIO_2056_RX_RXIQCAL_RXMUX | + RADIO_2056_RX1); + + if (pi->pubpi.radiorev >= 5) { + pi->tx_rx_cal_radio_saveregs[2] = + read_radio_reg(pi, + RADIO_2056_RX_RXSPARE2 | + RADIO_2056_RX1); + pi->tx_rx_cal_radio_saveregs[3] = + read_radio_reg(pi, + RADIO_2056_TX_TXSPARE2 | + RADIO_2056_TX0); + } + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + + if (pi->pubpi.radiorev >= 5) { + pi->tx_rx_cal_radio_saveregs[4] = + read_radio_reg( + pi, + RADIO_2056_RX_LNAA_MASTER + | RADIO_2056_RX1); + + write_radio_reg( + pi, + RADIO_2056_RX_LNAA_MASTER | + RADIO_2056_RX1, 0x40); + + write_radio_reg( + pi, + RADIO_2056_TX_TXSPARE2 + | + RADIO_2056_TX0, bias_a); + + write_radio_reg( + pi, + RADIO_2056_RX_RXSPARE2 + | + RADIO_2056_RX1, bias_a); + } else { + pi->tx_rx_cal_radio_saveregs[4] = + read_radio_reg( + pi, + RADIO_2056_RX_LNAA_TUNE + | RADIO_2056_RX1); + + offtune_val = + (pi-> + tx_rx_cal_radio_saveregs[2] & + 0xF0) >> 8; + offtune_val = + (offtune_val <= 0x7) ? 0xF : 0; + + mod_radio_reg(pi, + RADIO_2056_RX_LNAA_TUNE | + RADIO_2056_RX1, 0xF0, + (offtune_val << 8)); + } + + write_radio_reg(pi, + RADIO_2056_TX_RXIQCAL_TXMUX | + RADIO_2056_TX0, 0x9); + write_radio_reg(pi, + RADIO_2056_RX_RXIQCAL_RXMUX | + RADIO_2056_RX1, 0x9); + } else { + if (pi->pubpi.radiorev >= 5) { + pi->tx_rx_cal_radio_saveregs[4] = + read_radio_reg( + pi, + RADIO_2056_RX_LNAG_MASTER + | RADIO_2056_RX1); + + write_radio_reg( + pi, + RADIO_2056_RX_LNAG_MASTER + | RADIO_2056_RX1, 0x40); + + write_radio_reg( + pi, + RADIO_2056_TX_TXSPARE2 + | + RADIO_2056_TX0, bias_g); + + write_radio_reg( + pi, + RADIO_2056_RX_RXSPARE2 + | + RADIO_2056_RX1, bias_g); + } else { + pi->tx_rx_cal_radio_saveregs[4] = + read_radio_reg( + pi, + RADIO_2056_RX_LNAG_TUNE + | RADIO_2056_RX1); + + offtune_val = + (pi-> + tx_rx_cal_radio_saveregs[2] & + 0xF0) >> 8; + offtune_val = + (offtune_val <= 0x7) ? 0xF : 0; + + mod_radio_reg(pi, + RADIO_2056_RX_LNAG_TUNE | + RADIO_2056_RX1, 0xF0, + (offtune_val << 8)); + } + + write_radio_reg(pi, + RADIO_2056_TX_RXIQCAL_TXMUX | + RADIO_2056_TX0, 0x6); + write_radio_reg(pi, + RADIO_2056_RX_RXIQCAL_RXMUX | + RADIO_2056_RX1, 0x6); + } + } + } +} + +static void wlc_phy_rxcal_radio_cleanup_nphy(struct brcms_phy *pi, u8 rx_core) +{ + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + if (rx_core == PHY_CORE_0) { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + write_radio_reg( + pi, + RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP, + pi-> + tx_rx_cal_radio_saveregs[0]); + write_radio_reg( + pi, + RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN, + pi-> + tx_rx_cal_radio_saveregs[1]); + + } else { + write_radio_reg( + pi, + RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP, + pi-> + tx_rx_cal_radio_saveregs[0]); + write_radio_reg( + pi, + RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN, + pi-> + tx_rx_cal_radio_saveregs[1]); + } + + } else { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + write_radio_reg( + pi, + RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP, + pi-> + tx_rx_cal_radio_saveregs[0]); + write_radio_reg( + pi, + RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN, + pi-> + tx_rx_cal_radio_saveregs[1]); + + } else { + write_radio_reg( + pi, + RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP, + pi-> + tx_rx_cal_radio_saveregs[0]); + write_radio_reg( + pi, + RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN, + pi-> + tx_rx_cal_radio_saveregs[1]); + } + } + + } else { + if (rx_core == PHY_CORE_0) { + write_radio_reg(pi, + RADIO_2056_TX_RXIQCAL_TXMUX | + RADIO_2056_TX1, + pi->tx_rx_cal_radio_saveregs[0]); + + write_radio_reg(pi, + RADIO_2056_RX_RXIQCAL_RXMUX | + RADIO_2056_RX0, + pi->tx_rx_cal_radio_saveregs[1]); + + if (pi->pubpi.radiorev >= 5) { + write_radio_reg(pi, + RADIO_2056_RX_RXSPARE2 | + RADIO_2056_RX0, + pi-> + tx_rx_cal_radio_saveregs[2]); + + write_radio_reg(pi, + RADIO_2056_TX_TXSPARE2 | + RADIO_2056_TX1, + pi-> + tx_rx_cal_radio_saveregs[3]); + } + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + if (pi->pubpi.radiorev >= 5) + write_radio_reg( + pi, + RADIO_2056_RX_LNAA_MASTER + | RADIO_2056_RX0, + pi-> + tx_rx_cal_radio_saveregs + [4]); + else + write_radio_reg( + pi, + RADIO_2056_RX_LNAA_TUNE + | RADIO_2056_RX0, + pi-> + tx_rx_cal_radio_saveregs + [4]); + } else { + if (pi->pubpi.radiorev >= 5) + write_radio_reg( + pi, + RADIO_2056_RX_LNAG_MASTER + | RADIO_2056_RX0, + pi-> + tx_rx_cal_radio_saveregs + [4]); + else + write_radio_reg( + pi, + RADIO_2056_RX_LNAG_TUNE + | RADIO_2056_RX0, + pi-> + tx_rx_cal_radio_saveregs + [4]); + } + + } else { + write_radio_reg(pi, + RADIO_2056_TX_RXIQCAL_TXMUX | + RADIO_2056_TX0, + pi->tx_rx_cal_radio_saveregs[0]); + + write_radio_reg(pi, + RADIO_2056_RX_RXIQCAL_RXMUX | + RADIO_2056_RX1, + pi->tx_rx_cal_radio_saveregs[1]); + + if (pi->pubpi.radiorev >= 5) { + write_radio_reg(pi, + RADIO_2056_RX_RXSPARE2 | + RADIO_2056_RX1, + pi-> + tx_rx_cal_radio_saveregs[2]); + + write_radio_reg(pi, + RADIO_2056_TX_TXSPARE2 | + RADIO_2056_TX0, + pi-> + tx_rx_cal_radio_saveregs[3]); + } + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + if (pi->pubpi.radiorev >= 5) + write_radio_reg( + pi, + RADIO_2056_RX_LNAA_MASTER + | RADIO_2056_RX1, + pi-> + tx_rx_cal_radio_saveregs + [4]); + else + write_radio_reg( + pi, + RADIO_2056_RX_LNAA_TUNE + | RADIO_2056_RX1, + pi-> + tx_rx_cal_radio_saveregs + [4]); + } else { + if (pi->pubpi.radiorev >= 5) + write_radio_reg( + pi, + RADIO_2056_RX_LNAG_MASTER + | RADIO_2056_RX1, + pi-> + tx_rx_cal_radio_saveregs + [4]); + else + write_radio_reg( + pi, + RADIO_2056_RX_LNAG_TUNE + | RADIO_2056_RX1, + pi-> + tx_rx_cal_radio_saveregs + [4]); + } + } + } +} + +static void wlc_phy_rxcal_physetup_nphy(struct brcms_phy *pi, u8 rx_core) +{ + u8 tx_core; + u16 rx_antval, tx_antval; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + tx_core = rx_core; + else + tx_core = (rx_core == PHY_CORE_0) ? 1 : 0; + + pi->tx_rx_cal_phy_saveregs[0] = read_phy_reg(pi, 0xa2); + pi->tx_rx_cal_phy_saveregs[1] = + read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : 0xa7); + pi->tx_rx_cal_phy_saveregs[2] = + read_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5); + pi->tx_rx_cal_phy_saveregs[3] = read_phy_reg(pi, 0x91); + pi->tx_rx_cal_phy_saveregs[4] = read_phy_reg(pi, 0x92); + pi->tx_rx_cal_phy_saveregs[5] = read_phy_reg(pi, 0x7a); + pi->tx_rx_cal_phy_saveregs[6] = read_phy_reg(pi, 0x7d); + pi->tx_rx_cal_phy_saveregs[7] = read_phy_reg(pi, 0xe7); + pi->tx_rx_cal_phy_saveregs[8] = read_phy_reg(pi, 0xec); + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + pi->tx_rx_cal_phy_saveregs[11] = read_phy_reg(pi, 0x342); + pi->tx_rx_cal_phy_saveregs[12] = read_phy_reg(pi, 0x343); + pi->tx_rx_cal_phy_saveregs[13] = read_phy_reg(pi, 0x346); + pi->tx_rx_cal_phy_saveregs[14] = read_phy_reg(pi, 0x347); + } + + pi->tx_rx_cal_phy_saveregs[9] = read_phy_reg(pi, 0x297); + pi->tx_rx_cal_phy_saveregs[10] = read_phy_reg(pi, 0x29b); + mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (0) << 0); + + mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 0), (0) << 0); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + mod_phy_reg(pi, 0xa2, (0xf << 0), (1 << tx_core) << 0); + + mod_phy_reg(pi, 0xa2, (0xf << 12), (1 << (1 - rx_core)) << 12); + + } else { + + mod_phy_reg(pi, 0xa2, (0xf << 12), (1 << tx_core) << 12); + mod_phy_reg(pi, 0xa2, (0xf << 0), (1 << tx_core) << 0); + mod_phy_reg(pi, 0xa2, (0xf << 4), (1 << rx_core) << 4); + mod_phy_reg(pi, 0xa2, (0xf << 8), (1 << rx_core) << 8); + } + + mod_phy_reg(pi, ((rx_core == PHY_CORE_0) ? 0xa6 : 0xa7), (0x1 << 2), 0); + mod_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5, + (0x1 << 2), (0x1 << 2)); + if (NREV_LT(pi->pubpi.phy_rev, 7)) { + mod_phy_reg(pi, ((rx_core == PHY_CORE_0) ? 0xa6 : 0xa7), + (0x1 << 0) | (0x1 << 1), 0); + mod_phy_reg(pi, (rx_core == PHY_CORE_0) ? + 0x8f : 0xa5, + (0x1 << 0) | (0x1 << 1), (0x1 << 0) | (0x1 << 1)); + } + + wlc_phy_rfctrlintc_override_nphy(pi, NPHY_RfctrlIntc_override_PA, 0, + RADIO_MIMO_CORESEL_CORE1 | + RADIO_MIMO_CORESEL_CORE2); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 3), + 0, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID0); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 9), 0, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 10), 1, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 0), 1, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 1), 1, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID2); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 11), 0, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + if (CHSPEC_IS40(pi->radio_chanspec)) + wlc_phy_rfctrl_override_nphy_rev7( + pi, + (0x1 << 7), + 2, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + else + wlc_phy_rfctrl_override_nphy_rev7( + pi, + (0x1 << 7), + 0, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 7), + 0, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + wlc_phy_rfctrl_override_nphy_rev7(pi, (0x1 << 5), 0, 0, 0, + NPHY_REV7_RFCTRLOVERRIDE_ID1); + } else { + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 3), 0, 3, 0); + } + + wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RX2TX); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + + wlc_phy_rfctrlintc_override_nphy(pi, + NPHY_RfctrlIntc_override_TRSW, + 0x1, rx_core + 1); + } else { + + if (rx_core == PHY_CORE_0) { + rx_antval = 0x1; + tx_antval = 0x8; + } else { + rx_antval = 0x4; + tx_antval = 0x2; + } + + wlc_phy_rfctrlintc_override_nphy(pi, + NPHY_RfctrlIntc_override_TRSW, + rx_antval, rx_core + 1); + wlc_phy_rfctrlintc_override_nphy(pi, + NPHY_RfctrlIntc_override_TRSW, + tx_antval, tx_core + 1); + } +} + +static void wlc_phy_rxcal_phycleanup_nphy(struct brcms_phy *pi, u8 rx_core) +{ + + write_phy_reg(pi, 0xa2, pi->tx_rx_cal_phy_saveregs[0]); + write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : 0xa7, + pi->tx_rx_cal_phy_saveregs[1]); + write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x8f : 0xa5, + pi->tx_rx_cal_phy_saveregs[2]); + write_phy_reg(pi, 0x91, pi->tx_rx_cal_phy_saveregs[3]); + write_phy_reg(pi, 0x92, pi->tx_rx_cal_phy_saveregs[4]); + + write_phy_reg(pi, 0x7a, pi->tx_rx_cal_phy_saveregs[5]); + write_phy_reg(pi, 0x7d, pi->tx_rx_cal_phy_saveregs[6]); + write_phy_reg(pi, 0xe7, pi->tx_rx_cal_phy_saveregs[7]); + write_phy_reg(pi, 0xec, pi->tx_rx_cal_phy_saveregs[8]); + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + write_phy_reg(pi, 0x342, pi->tx_rx_cal_phy_saveregs[11]); + write_phy_reg(pi, 0x343, pi->tx_rx_cal_phy_saveregs[12]); + write_phy_reg(pi, 0x346, pi->tx_rx_cal_phy_saveregs[13]); + write_phy_reg(pi, 0x347, pi->tx_rx_cal_phy_saveregs[14]); + } + + write_phy_reg(pi, 0x297, pi->tx_rx_cal_phy_saveregs[9]); + write_phy_reg(pi, 0x29b, pi->tx_rx_cal_phy_saveregs[10]); +} + +static void +wlc_phy_rxcal_gainctrl_nphy_rev5(struct brcms_phy *pi, u8 rx_core, + u16 *rxgain, u8 cal_type) +{ + + u16 num_samps; + struct phy_iq_est est[PHY_CORE_MAX]; + u8 tx_core; + struct nphy_iq_comp save_comp, zero_comp; + u32 i_pwr, q_pwr, curr_pwr, optim_pwr = 0, prev_pwr = 0, + thresh_pwr = 10000; + s16 desired_log2_pwr, actual_log2_pwr, delta_pwr; + bool gainctrl_done = false; + u8 mix_tia_gain = 3; + s8 optim_gaintbl_index = 0, prev_gaintbl_index = 0; + s8 curr_gaintbl_index = 3; + u8 gainctrl_dirn = NPHY_RXCAL_GAIN_INIT; + const struct nphy_ipa_txrxgain *nphy_rxcal_gaintbl; + u16 hpvga, lpf_biq1, lpf_biq0, lna2, lna1; + int fine_gain_idx; + s8 txpwrindex; + u16 nphy_rxcal_txgain[2]; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + tx_core = rx_core; + else + tx_core = 1 - rx_core; + + num_samps = 1024; + desired_log2_pwr = (cal_type == 0) ? 13 : 13; + + wlc_phy_rx_iq_coeffs_nphy(pi, 0, &save_comp); + zero_comp.a0 = zero_comp.b0 = zero_comp.a1 = zero_comp.b1 = 0x0; + wlc_phy_rx_iq_coeffs_nphy(pi, 1, &zero_comp); + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) + mix_tia_gain = 3; + else if (NREV_GE(pi->pubpi.phy_rev, 4)) + mix_tia_gain = 4; + else + mix_tia_gain = 6; + if (NREV_GE(pi->pubpi.phy_rev, 7)) + nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_5GHz_rev7; + else + nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_5GHz; + } else { + if (NREV_GE(pi->pubpi.phy_rev, 7)) + nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_2GHz_rev7; + else + nphy_rxcal_gaintbl = nphy_ipa_rxcal_gaintbl_2GHz; + } + + do { + + hpvga = (NREV_GE(pi->pubpi.phy_rev, 7)) ? + 0 : nphy_rxcal_gaintbl[curr_gaintbl_index].hpvga; + lpf_biq1 = nphy_rxcal_gaintbl[curr_gaintbl_index].lpf_biq1; + lpf_biq0 = nphy_rxcal_gaintbl[curr_gaintbl_index].lpf_biq0; + lna2 = nphy_rxcal_gaintbl[curr_gaintbl_index].lna2; + lna1 = nphy_rxcal_gaintbl[curr_gaintbl_index].lna1; + txpwrindex = nphy_rxcal_gaintbl[curr_gaintbl_index].txpwrindex; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + wlc_phy_rfctrl_override_1tomany_nphy( + pi, + NPHY_REV7_RfctrlOverride_cmd_rxgain, + ((lpf_biq1 << 12) | + (lpf_biq0 << 8) | + (mix_tia_gain << 4) | (lna2 << 2) + | lna1), 0x3, 0); + else + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), + ((hpvga << 12) | + (lpf_biq1 << 10) | + (lpf_biq0 << 8) | + (mix_tia_gain << 4) | + (lna2 << 2) | lna1), 0x3, + 0); + + pi->nphy_rxcal_pwr_idx[tx_core] = txpwrindex; + + if (txpwrindex == -1) { + nphy_rxcal_txgain[0] = 0x8ff0 | pi->nphy_gmval; + nphy_rxcal_txgain[1] = 0x8ff0 | pi->nphy_gmval; + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, + 2, 0x110, 16, + nphy_rxcal_txgain); + } else { + wlc_phy_txpwr_index_nphy(pi, tx_core + 1, txpwrindex, + false); + } + + wlc_phy_tx_tone_nphy(pi, (CHSPEC_IS40(pi->radio_chanspec)) ? + NPHY_RXCAL_TONEFREQ_40MHz : + NPHY_RXCAL_TONEFREQ_20MHz, + NPHY_RXCAL_TONEAMP, 0, cal_type, false); + + wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0); + i_pwr = (est[rx_core].i_pwr + num_samps / 2) / num_samps; + q_pwr = (est[rx_core].q_pwr + num_samps / 2) / num_samps; + curr_pwr = i_pwr + q_pwr; + + switch (gainctrl_dirn) { + case NPHY_RXCAL_GAIN_INIT: + if (curr_pwr > thresh_pwr) { + gainctrl_dirn = NPHY_RXCAL_GAIN_DOWN; + prev_gaintbl_index = curr_gaintbl_index; + curr_gaintbl_index--; + } else { + gainctrl_dirn = NPHY_RXCAL_GAIN_UP; + prev_gaintbl_index = curr_gaintbl_index; + curr_gaintbl_index++; + } + break; + + case NPHY_RXCAL_GAIN_UP: + if (curr_pwr > thresh_pwr) { + gainctrl_done = true; + optim_pwr = prev_pwr; + optim_gaintbl_index = prev_gaintbl_index; + } else { + prev_gaintbl_index = curr_gaintbl_index; + curr_gaintbl_index++; + } + break; + + case NPHY_RXCAL_GAIN_DOWN: + if (curr_pwr > thresh_pwr) { + prev_gaintbl_index = curr_gaintbl_index; + curr_gaintbl_index--; + } else { + gainctrl_done = true; + optim_pwr = curr_pwr; + optim_gaintbl_index = curr_gaintbl_index; + } + break; + + default: + break; + } + + if ((curr_gaintbl_index < 0) || + (curr_gaintbl_index > NPHY_IPA_RXCAL_MAXGAININDEX)) { + gainctrl_done = true; + optim_pwr = curr_pwr; + optim_gaintbl_index = prev_gaintbl_index; + } else { + prev_pwr = curr_pwr; + } + + wlc_phy_stopplayback_nphy(pi); + } while (!gainctrl_done); + + hpvga = nphy_rxcal_gaintbl[optim_gaintbl_index].hpvga; + lpf_biq1 = nphy_rxcal_gaintbl[optim_gaintbl_index].lpf_biq1; + lpf_biq0 = nphy_rxcal_gaintbl[optim_gaintbl_index].lpf_biq0; + lna2 = nphy_rxcal_gaintbl[optim_gaintbl_index].lna2; + lna1 = nphy_rxcal_gaintbl[optim_gaintbl_index].lna1; + txpwrindex = nphy_rxcal_gaintbl[optim_gaintbl_index].txpwrindex; + + actual_log2_pwr = wlc_phy_nbits(optim_pwr); + delta_pwr = desired_log2_pwr - actual_log2_pwr; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + fine_gain_idx = (int)lpf_biq1 + delta_pwr; + + if (fine_gain_idx + (int)lpf_biq0 > 10) + lpf_biq1 = 10 - lpf_biq0; + else + lpf_biq1 = (u16) max(fine_gain_idx, 0); + + wlc_phy_rfctrl_override_1tomany_nphy( + pi, + NPHY_REV7_RfctrlOverride_cmd_rxgain, + ((lpf_biq1 << 12) | + (lpf_biq0 << 8) | + (mix_tia_gain << 4) | + (lna2 << 2) | lna1), 0x3, + 0); + } else { + hpvga = (u16) max(min(((int)hpvga) + delta_pwr, 10), 0); + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), + ((hpvga << 12) | + (lpf_biq1 << 10) | + (lpf_biq0 << 8) | + (mix_tia_gain << 4) | + (lna2 << 2) | + lna1), 0x3, 0); + } + + if (rxgain != NULL) { + *rxgain++ = lna1; + *rxgain++ = lna2; + *rxgain++ = mix_tia_gain; + *rxgain++ = lpf_biq0; + *rxgain++ = lpf_biq1; + *rxgain = hpvga; + } + + wlc_phy_rx_iq_coeffs_nphy(pi, 1, &save_comp); +} + +static void +wlc_phy_rxcal_gainctrl_nphy(struct brcms_phy *pi, u8 rx_core, u16 *rxgain, + u8 cal_type) +{ + wlc_phy_rxcal_gainctrl_nphy_rev5(pi, rx_core, rxgain, cal_type); +} + +static u8 +wlc_phy_rc_sweep_nphy(struct brcms_phy *pi, u8 core_idx, u8 loopback_type) +{ + u32 target_bws[2] = { 9500, 21000 }; + u32 ref_tones[2] = { 3000, 6000 }; + u32 target_bw, ref_tone; + + u32 target_pwr_ratios[2] = { 28606, 18468 }; + u32 target_pwr_ratio, pwr_ratio, last_pwr_ratio = 0; + + u16 start_rccal_ovr_val = 128; + u16 txlpf_rccal_lpc_ovr_val = 128; + u16 rxlpf_rccal_hpc_ovr_val = 159; + + u16 orig_txlpf_rccal_lpc_ovr_val; + u16 orig_rxlpf_rccal_hpc_ovr_val; + u16 radio_addr_offset_rx; + u16 radio_addr_offset_tx; + u16 orig_dcBypass; + u16 orig_RxStrnFilt40Num[6]; + u16 orig_RxStrnFilt40Den[4]; + u16 orig_rfctrloverride[2]; + u16 orig_rfctrlauxreg[2]; + u16 orig_rfctrlrssiothers; + u16 tx_lpf_bw = 4; + + u16 rx_lpf_bw, rx_lpf_bws[2] = { 2, 4 }; + u16 lpf_hpc = 7, hpvga_hpc = 7; + + s8 rccal_stepsize; + u16 rccal_val, last_rccal_val = 0, best_rccal_val = 0; + u32 ref_iq_vals = 0, target_iq_vals = 0; + u16 num_samps, log_num_samps = 10; + struct phy_iq_est est[PHY_CORE_MAX]; + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + return 0; + + num_samps = (1 << log_num_samps); + + if (CHSPEC_IS40(pi->radio_chanspec)) { + target_bw = target_bws[1]; + target_pwr_ratio = target_pwr_ratios[1]; + ref_tone = ref_tones[1]; + rx_lpf_bw = rx_lpf_bws[1]; + } else { + target_bw = target_bws[0]; + target_pwr_ratio = target_pwr_ratios[0]; + ref_tone = ref_tones[0]; + rx_lpf_bw = rx_lpf_bws[0]; + } + + if (core_idx == 0) { + radio_addr_offset_rx = RADIO_2056_RX0; + radio_addr_offset_tx = + (loopback_type == 0) ? RADIO_2056_TX0 : RADIO_2056_TX1; + } else { + radio_addr_offset_rx = RADIO_2056_RX1; + radio_addr_offset_tx = + (loopback_type == 0) ? RADIO_2056_TX1 : RADIO_2056_TX0; + } + + orig_txlpf_rccal_lpc_ovr_val = + read_radio_reg(pi, + (RADIO_2056_TX_TXLPF_RCCAL | + radio_addr_offset_tx)); + orig_rxlpf_rccal_hpc_ovr_val = + read_radio_reg(pi, + (RADIO_2056_RX_RXLPF_RCCAL_HPC | + radio_addr_offset_rx)); + + orig_dcBypass = ((read_phy_reg(pi, 0x48) >> 8) & 1); + + orig_RxStrnFilt40Num[0] = read_phy_reg(pi, 0x267); + orig_RxStrnFilt40Num[1] = read_phy_reg(pi, 0x268); + orig_RxStrnFilt40Num[2] = read_phy_reg(pi, 0x269); + orig_RxStrnFilt40Den[0] = read_phy_reg(pi, 0x26a); + orig_RxStrnFilt40Den[1] = read_phy_reg(pi, 0x26b); + orig_RxStrnFilt40Num[3] = read_phy_reg(pi, 0x26c); + orig_RxStrnFilt40Num[4] = read_phy_reg(pi, 0x26d); + orig_RxStrnFilt40Num[5] = read_phy_reg(pi, 0x26e); + orig_RxStrnFilt40Den[2] = read_phy_reg(pi, 0x26f); + orig_RxStrnFilt40Den[3] = read_phy_reg(pi, 0x270); + + orig_rfctrloverride[0] = read_phy_reg(pi, 0xe7); + orig_rfctrloverride[1] = read_phy_reg(pi, 0xec); + orig_rfctrlauxreg[0] = read_phy_reg(pi, 0xf8); + orig_rfctrlauxreg[1] = read_phy_reg(pi, 0xfa); + orig_rfctrlrssiothers = read_phy_reg(pi, (core_idx == 0) ? 0x7a : 0x7d); + + write_radio_reg(pi, (RADIO_2056_TX_TXLPF_RCCAL | radio_addr_offset_tx), + txlpf_rccal_lpc_ovr_val); + + write_radio_reg(pi, + (RADIO_2056_RX_RXLPF_RCCAL_HPC | radio_addr_offset_rx), + rxlpf_rccal_hpc_ovr_val); + + mod_phy_reg(pi, 0x48, (0x1 << 8), (0x1 << 8)); + + write_phy_reg(pi, 0x267, 0x02d4); + write_phy_reg(pi, 0x268, 0x0000); + write_phy_reg(pi, 0x269, 0x0000); + write_phy_reg(pi, 0x26a, 0x0000); + write_phy_reg(pi, 0x26b, 0x0000); + write_phy_reg(pi, 0x26c, 0x02d4); + write_phy_reg(pi, 0x26d, 0x0000); + write_phy_reg(pi, 0x26e, 0x0000); + write_phy_reg(pi, 0x26f, 0x0000); + write_phy_reg(pi, 0x270, 0x0000); + + or_phy_reg(pi, (core_idx == 0) ? 0xe7 : 0xec, (0x1 << 8)); + or_phy_reg(pi, (core_idx == 0) ? 0xec : 0xe7, (0x1 << 15)); + or_phy_reg(pi, (core_idx == 0) ? 0xe7 : 0xec, (0x1 << 9)); + or_phy_reg(pi, (core_idx == 0) ? 0xe7 : 0xec, (0x1 << 10)); + + mod_phy_reg(pi, (core_idx == 0) ? 0xfa : 0xf8, + (0x7 << 10), (tx_lpf_bw << 10)); + mod_phy_reg(pi, (core_idx == 0) ? 0xf8 : 0xfa, + (0x7 << 0), (hpvga_hpc << 0)); + mod_phy_reg(pi, (core_idx == 0) ? 0xf8 : 0xfa, + (0x7 << 4), (lpf_hpc << 4)); + mod_phy_reg(pi, (core_idx == 0) ? 0x7a : 0x7d, + (0x7 << 8), (rx_lpf_bw << 8)); + + rccal_stepsize = 16; + rccal_val = start_rccal_ovr_val + rccal_stepsize; + + while (rccal_stepsize >= 0) { + write_radio_reg(pi, + (RADIO_2056_RX_RXLPF_RCCAL_LPC | + radio_addr_offset_rx), rccal_val); + + if (rccal_stepsize == 16) { + + wlc_phy_tx_tone_nphy(pi, ref_tone, NPHY_RXCAL_TONEAMP, + 0, 1, false); + udelay(2); + + wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0); + + if (core_idx == 0) + ref_iq_vals = + max_t(u32, (est[0].i_pwr + + est[0].q_pwr) >> + (log_num_samps + 1), + 1); + else + ref_iq_vals = + max_t(u32, (est[1].i_pwr + + est[1].q_pwr) >> + (log_num_samps + 1), + 1); + + wlc_phy_tx_tone_nphy(pi, target_bw, NPHY_RXCAL_TONEAMP, + 0, 1, false); + udelay(2); + } + + wlc_phy_rx_iq_est_nphy(pi, est, num_samps, 32, 0); + + if (core_idx == 0) + target_iq_vals = (est[0].i_pwr + est[0].q_pwr) >> + (log_num_samps + 1); + else + target_iq_vals = + (est[1].i_pwr + + est[1].q_pwr) >> (log_num_samps + 1); + + pwr_ratio = (uint) ((target_iq_vals << 16) / ref_iq_vals); + + if (rccal_stepsize == 0) + rccal_stepsize--; + else if (rccal_stepsize == 1) { + last_rccal_val = rccal_val; + rccal_val += (pwr_ratio > target_pwr_ratio) ? 1 : -1; + last_pwr_ratio = pwr_ratio; + rccal_stepsize--; + } else { + rccal_stepsize = (rccal_stepsize >> 1); + rccal_val += ((pwr_ratio > target_pwr_ratio) ? + rccal_stepsize : (-rccal_stepsize)); + } + + if (rccal_stepsize == -1) { + best_rccal_val = + (abs((int)last_pwr_ratio - + (int)target_pwr_ratio) < + abs((int)pwr_ratio - + (int)target_pwr_ratio)) ? last_rccal_val : + rccal_val; + + if (CHSPEC_IS40(pi->radio_chanspec)) { + if ((best_rccal_val > 140) + || (best_rccal_val < 135)) + best_rccal_val = 138; + } else { + if ((best_rccal_val > 142) + || (best_rccal_val < 137)) + best_rccal_val = 140; + } + + write_radio_reg(pi, + (RADIO_2056_RX_RXLPF_RCCAL_LPC | + radio_addr_offset_rx), best_rccal_val); + } + } + + wlc_phy_stopplayback_nphy(pi); + + write_radio_reg(pi, (RADIO_2056_TX_TXLPF_RCCAL | radio_addr_offset_tx), + orig_txlpf_rccal_lpc_ovr_val); + write_radio_reg(pi, + (RADIO_2056_RX_RXLPF_RCCAL_HPC | radio_addr_offset_rx), + orig_rxlpf_rccal_hpc_ovr_val); + + mod_phy_reg(pi, 0x48, (0x1 << 8), (orig_dcBypass << 8)); + + write_phy_reg(pi, 0x267, orig_RxStrnFilt40Num[0]); + write_phy_reg(pi, 0x268, orig_RxStrnFilt40Num[1]); + write_phy_reg(pi, 0x269, orig_RxStrnFilt40Num[2]); + write_phy_reg(pi, 0x26a, orig_RxStrnFilt40Den[0]); + write_phy_reg(pi, 0x26b, orig_RxStrnFilt40Den[1]); + write_phy_reg(pi, 0x26c, orig_RxStrnFilt40Num[3]); + write_phy_reg(pi, 0x26d, orig_RxStrnFilt40Num[4]); + write_phy_reg(pi, 0x26e, orig_RxStrnFilt40Num[5]); + write_phy_reg(pi, 0x26f, orig_RxStrnFilt40Den[2]); + write_phy_reg(pi, 0x270, orig_RxStrnFilt40Den[3]); + + write_phy_reg(pi, 0xe7, orig_rfctrloverride[0]); + write_phy_reg(pi, 0xec, orig_rfctrloverride[1]); + write_phy_reg(pi, 0xf8, orig_rfctrlauxreg[0]); + write_phy_reg(pi, 0xfa, orig_rfctrlauxreg[1]); + write_phy_reg(pi, (core_idx == 0) ? 0x7a : 0x7d, orig_rfctrlrssiothers); + + pi->nphy_anarxlpf_adjusted = false; + + return best_rccal_val - 0x80; +} + +#define WAIT_FOR_SCOPE 4000 +static int wlc_phy_cal_rxiq_nphy_rev3(struct brcms_phy *pi, + struct nphy_txgains target_gain, + u8 cal_type, bool debug) +{ + u16 orig_BBConfig; + u8 core_no, rx_core; + u8 best_rccal[2]; + u16 gain_save[2]; + u16 cal_gain[2]; + struct nphy_iqcal_params cal_params[2]; + u8 rxcore_state; + s8 rxlpf_rccal_hpc, txlpf_rccal_lpc; + s8 txlpf_idac; + bool phyhang_avoid_state = false; + bool skip_rxiqcal = false; + + orig_BBConfig = read_phy_reg(pi, 0x01); + mod_phy_reg(pi, 0x01, (0x1 << 15), 0); + + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + if (NREV_GE(pi->pubpi.phy_rev, 4)) { + phyhang_avoid_state = pi->phyhang_avoid; + pi->phyhang_avoid = false; + } + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); + + for (core_no = 0; core_no <= 1; core_no++) { + wlc_phy_iqcal_gainparams_nphy(pi, core_no, target_gain, + &cal_params[core_no]); + cal_gain[core_no] = cal_params[core_no].cal_gain; + } + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, cal_gain); + + rxcore_state = wlc_phy_rxcore_getstate_nphy( + (struct brcms_phy_pub *) pi); + + for (rx_core = 0; rx_core < pi->pubpi.phy_corenum; rx_core++) { + + skip_rxiqcal = + ((rxcore_state & (1 << rx_core)) == 0) ? true : false; + + wlc_phy_rxcal_physetup_nphy(pi, rx_core); + + wlc_phy_rxcal_radio_setup_nphy(pi, rx_core); + + if ((!skip_rxiqcal) && ((cal_type == 0) || (cal_type == 2))) { + + wlc_phy_rxcal_gainctrl_nphy(pi, rx_core, NULL, 0); + + wlc_phy_tx_tone_nphy(pi, + (CHSPEC_IS40( + pi->radio_chanspec)) ? + NPHY_RXCAL_TONEFREQ_40MHz : + NPHY_RXCAL_TONEFREQ_20MHz, + NPHY_RXCAL_TONEAMP, 0, cal_type, + false); + + if (debug) + mdelay(WAIT_FOR_SCOPE); + + wlc_phy_calc_rx_iq_comp_nphy(pi, rx_core + 1); + wlc_phy_stopplayback_nphy(pi); + } + + if (((cal_type == 1) || (cal_type == 2)) + && NREV_LT(pi->pubpi.phy_rev, 7)) { + + if (rx_core == PHY_CORE_1) { + + if (rxcore_state == 1) + wlc_phy_rxcore_setstate_nphy( + (struct brcms_phy_pub *) pi, 3); + + wlc_phy_rxcal_gainctrl_nphy(pi, rx_core, NULL, + 1); + + best_rccal[rx_core] = + wlc_phy_rc_sweep_nphy(pi, rx_core, 1); + pi->nphy_rccal_value = best_rccal[rx_core]; + + if (rxcore_state == 1) + wlc_phy_rxcore_setstate_nphy( + (struct brcms_phy_pub *) pi, + rxcore_state); + } + } + + wlc_phy_rxcal_radio_cleanup_nphy(pi, rx_core); + + wlc_phy_rxcal_phycleanup_nphy(pi, rx_core); + wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); + } + + if ((cal_type == 1) || (cal_type == 2)) { + + best_rccal[0] = best_rccal[1]; + write_radio_reg(pi, + (RADIO_2056_RX_RXLPF_RCCAL_LPC | + RADIO_2056_RX0), (best_rccal[0] | 0x80)); + + for (rx_core = 0; rx_core < pi->pubpi.phy_corenum; rx_core++) { + rxlpf_rccal_hpc = + (((int)best_rccal[rx_core] - 12) >> 1) + 10; + txlpf_rccal_lpc = ((int)best_rccal[rx_core] - 12) + 10; + + if (PHY_IPA(pi)) { + txlpf_rccal_lpc += + (pi->bw == WL_CHANSPEC_BW_40) ? 24 : 12; + txlpf_idac = (pi->bw == WL_CHANSPEC_BW_40) ? + 0x0e : 0x13; + WRITE_RADIO_REG2(pi, RADIO_2056, TX, rx_core, + TXLPF_IDAC_4, txlpf_idac); + } + + rxlpf_rccal_hpc = max(min_t(u8, rxlpf_rccal_hpc, 31), + 0); + txlpf_rccal_lpc = max(min_t(u8, txlpf_rccal_lpc, 31), + 0); + + write_radio_reg(pi, (RADIO_2056_RX_RXLPF_RCCAL_HPC | + ((rx_core == + PHY_CORE_0) ? RADIO_2056_RX0 : + RADIO_2056_RX1)), + (rxlpf_rccal_hpc | 0x80)); + + write_radio_reg(pi, (RADIO_2056_TX_TXLPF_RCCAL | + ((rx_core == + PHY_CORE_0) ? RADIO_2056_TX0 : + RADIO_2056_TX1)), + (txlpf_rccal_lpc | 0x80)); + } + } + + write_phy_reg(pi, 0x01, orig_BBConfig); + + wlc_phy_resetcca_nphy(pi); + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + wlc_phy_rfctrl_override_1tomany_nphy( + pi, + NPHY_REV7_RfctrlOverride_cmd_rxgain, + 0, 0x3, 1); + else + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 12), 0, 0x3, 1); + + wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, + gain_save); + + if (NREV_GE(pi->pubpi.phy_rev, 4)) + pi->phyhang_avoid = phyhang_avoid_state; + + wlc_phy_stay_in_carriersearch_nphy(pi, false); + + return 0; +} + +static int +wlc_phy_cal_rxiq_nphy_rev2(struct brcms_phy *pi, + struct nphy_txgains target_gain, bool debug) +{ + struct phy_iq_est est[PHY_CORE_MAX]; + u8 core_num, rx_core, tx_core; + u16 lna_vals[] = { 0x3, 0x3, 0x1 }; + u16 hpf1_vals[] = { 0x7, 0x2, 0x0 }; + u16 hpf2_vals[] = { 0x2, 0x0, 0x0 }; + s16 curr_hpf1, curr_hpf2, curr_hpf, curr_lna; + s16 desired_log2_pwr, actual_log2_pwr, hpf_change; + u16 orig_RfseqCoreActv, orig_AfectrlCore, orig_AfectrlOverride; + u16 orig_RfctrlIntcRx, orig_RfctrlIntcTx; + u16 num_samps; + u32 i_pwr, q_pwr, tot_pwr[3]; + u8 gain_pass, use_hpf_num; + u16 mask, val1, val2; + u16 core_no; + u16 gain_save[2]; + u16 cal_gain[2]; + struct nphy_iqcal_params cal_params[2]; + u8 phy_bw; + int bcmerror = 0; + bool first_playtone = true; + + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + if (NREV_LT(pi->pubpi.phy_rev, 2)) + wlc_phy_reapply_txcal_coeffs_nphy(pi); + + wlc_phy_table_read_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, gain_save); + + for (core_no = 0; core_no <= 1; core_no++) { + wlc_phy_iqcal_gainparams_nphy(pi, core_no, target_gain, + &cal_params[core_no]); + cal_gain[core_no] = cal_params[core_no].cal_gain; + } + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, cal_gain); + + num_samps = 1024; + desired_log2_pwr = 13; + + for (core_num = 0; core_num < 2; core_num++) { + + rx_core = core_num; + tx_core = 1 - core_num; + + orig_RfseqCoreActv = read_phy_reg(pi, 0xa2); + orig_AfectrlCore = read_phy_reg(pi, (rx_core == PHY_CORE_0) ? + 0xa6 : 0xa7); + orig_AfectrlOverride = read_phy_reg(pi, 0xa5); + orig_RfctrlIntcRx = read_phy_reg(pi, (rx_core == PHY_CORE_0) ? + 0x91 : 0x92); + orig_RfctrlIntcTx = read_phy_reg(pi, (tx_core == PHY_CORE_0) ? + 0x91 : 0x92); + + mod_phy_reg(pi, 0xa2, (0xf << 12), (1 << tx_core) << 12); + mod_phy_reg(pi, 0xa2, (0xf << 0), (1 << tx_core) << 0); + + or_phy_reg(pi, ((rx_core == PHY_CORE_0) ? 0xa6 : 0xa7), + ((0x1 << 1) | (0x1 << 2))); + or_phy_reg(pi, 0xa5, ((0x1 << 1) | (0x1 << 2))); + + if (((pi->nphy_rxcalparams) & 0xff000000)) + write_phy_reg(pi, + (rx_core == PHY_CORE_0) ? 0x91 : 0x92, + (CHSPEC_IS5G(pi->radio_chanspec) ? + 0x140 : 0x110)); + else + write_phy_reg(pi, + (rx_core == PHY_CORE_0) ? 0x91 : 0x92, + (CHSPEC_IS5G(pi->radio_chanspec) ? + 0x180 : 0x120)); + + write_phy_reg(pi, (tx_core == PHY_CORE_0) ? 0x91 : 0x92, + (CHSPEC_IS5G(pi->radio_chanspec) ? 0x148 : + 0x114)); + + mask = RADIO_2055_COUPLE_RX_MASK | RADIO_2055_COUPLE_TX_MASK; + if (rx_core == PHY_CORE_0) { + val1 = RADIO_2055_COUPLE_RX_MASK; + val2 = RADIO_2055_COUPLE_TX_MASK; + } else { + val1 = RADIO_2055_COUPLE_TX_MASK; + val2 = RADIO_2055_COUPLE_RX_MASK; + } + + if ((pi->nphy_rxcalparams & 0x10000)) { + mod_radio_reg(pi, RADIO_2055_CORE1_GEN_SPARE2, mask, + val1); + mod_radio_reg(pi, RADIO_2055_CORE2_GEN_SPARE2, mask, + val2); + } + + for (gain_pass = 0; gain_pass < 4; gain_pass++) { + + if (debug) + mdelay(WAIT_FOR_SCOPE); + + if (gain_pass < 3) { + curr_lna = lna_vals[gain_pass]; + curr_hpf1 = hpf1_vals[gain_pass]; + curr_hpf2 = hpf2_vals[gain_pass]; + } else { + + if (tot_pwr[1] > 10000) { + curr_lna = lna_vals[2]; + curr_hpf1 = hpf1_vals[2]; + curr_hpf2 = hpf2_vals[2]; + use_hpf_num = 1; + curr_hpf = curr_hpf1; + actual_log2_pwr = + wlc_phy_nbits(tot_pwr[2]); + } else { + if (tot_pwr[0] > 10000) { + curr_lna = lna_vals[1]; + curr_hpf1 = hpf1_vals[1]; + curr_hpf2 = hpf2_vals[1]; + use_hpf_num = 1; + curr_hpf = curr_hpf1; + actual_log2_pwr = + wlc_phy_nbits( + tot_pwr[1]); + } else { + curr_lna = lna_vals[0]; + curr_hpf1 = hpf1_vals[0]; + curr_hpf2 = hpf2_vals[0]; + use_hpf_num = 2; + curr_hpf = curr_hpf2; + actual_log2_pwr = + wlc_phy_nbits( + tot_pwr[0]); + } + } + + hpf_change = desired_log2_pwr - actual_log2_pwr; + curr_hpf += hpf_change; + curr_hpf = max(min_t(u16, curr_hpf, 10), 0); + if (use_hpf_num == 1) + curr_hpf1 = curr_hpf; + else + curr_hpf2 = curr_hpf; + } + + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 10), + ((curr_hpf2 << 8) | + (curr_hpf1 << 4) | + (curr_lna << 2)), 0x3, 0); + wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); + + wlc_phy_stopplayback_nphy(pi); + + if (first_playtone) { + bcmerror = wlc_phy_tx_tone_nphy(pi, 4000, + (u16) (pi->nphy_rxcalparams & + 0xffff), 0, 0, true); + first_playtone = false; + } else { + phy_bw = (CHSPEC_IS40(pi->radio_chanspec)) ? + 40 : 20; + wlc_phy_runsamples_nphy(pi, phy_bw * 8, 0xffff, + 0, 0, 0, true); + } + + if (bcmerror == 0) { + if (gain_pass < 3) { + + wlc_phy_rx_iq_est_nphy(pi, est, + num_samps, 32, + 0); + i_pwr = (est[rx_core].i_pwr + + num_samps / 2) / num_samps; + q_pwr = (est[rx_core].q_pwr + + num_samps / 2) / num_samps; + tot_pwr[gain_pass] = i_pwr + q_pwr; + } else { + + wlc_phy_calc_rx_iq_comp_nphy(pi, + (1 << + rx_core)); + } + + wlc_phy_stopplayback_nphy(pi); + } + + if (bcmerror != 0) + break; + } + + and_radio_reg(pi, RADIO_2055_CORE1_GEN_SPARE2, ~mask); + and_radio_reg(pi, RADIO_2055_CORE2_GEN_SPARE2, ~mask); + + write_phy_reg(pi, (tx_core == PHY_CORE_0) ? 0x91 : + 0x92, orig_RfctrlIntcTx); + write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0x91 : + 0x92, orig_RfctrlIntcRx); + write_phy_reg(pi, 0xa5, orig_AfectrlOverride); + write_phy_reg(pi, (rx_core == PHY_CORE_0) ? 0xa6 : + 0xa7, orig_AfectrlCore); + write_phy_reg(pi, 0xa2, orig_RfseqCoreActv); + + if (bcmerror != 0) + break; + } + + wlc_phy_rfctrl_override_nphy(pi, (0x1 << 10), 0, 0x3, 1); + wlc_phy_force_rfseq_nphy(pi, NPHY_RFSEQ_RESET2RX); + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_RFSEQ, 2, 0x110, 16, + gain_save); + + wlc_phy_stay_in_carriersearch_nphy(pi, false); + + return bcmerror; +} + +int +wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, + u8 cal_type, bool debug) +{ + if (NREV_GE(pi->pubpi.phy_rev, 7)) + cal_type = 0; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) + return wlc_phy_cal_rxiq_nphy_rev3(pi, target_gain, cal_type, + debug); + else + return wlc_phy_cal_rxiq_nphy_rev2(pi, target_gain, debug); +} + +void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi) +{ + uint core; + u32 txgain; + u16 rad_gain, dac_gain, bbmult, m1m2; + u8 txpi[2], chan_freq_range; + s32 rfpwr_offset; + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + if (pi->sh->sromrev < 4) { + txpi[0] = txpi[1] = 72; + } else { + + chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); + switch (chan_freq_range) { + case WL_CHAN_FREQ_RANGE_2G: + case WL_CHAN_FREQ_RANGE_5GL: + case WL_CHAN_FREQ_RANGE_5GM: + case WL_CHAN_FREQ_RANGE_5GH: + txpi[0] = 0; + txpi[1] = 0; + break; + default: + txpi[0] = txpi[1] = 91; + break; + } + } + + if (NREV_GE(pi->pubpi.phy_rev, 7)) + txpi[0] = txpi[1] = 30; + else if (NREV_GE(pi->pubpi.phy_rev, 3)) + txpi[0] = txpi[1] = 40; + + if (NREV_LT(pi->pubpi.phy_rev, 7)) { + + if ((txpi[0] < 40) || (txpi[0] > 100) || + (txpi[1] < 40) || (txpi[1] > 100)) + txpi[0] = txpi[1] = 91; + } + + pi->nphy_txpwrindex[PHY_CORE_0].index_internal = txpi[0]; + pi->nphy_txpwrindex[PHY_CORE_1].index_internal = txpi[1]; + pi->nphy_txpwrindex[PHY_CORE_0].index_internal_save = txpi[0]; + pi->nphy_txpwrindex[PHY_CORE_1].index_internal_save = txpi[1]; + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + uint phyrev = pi->pubpi.phy_rev; + + if (NREV_GE(phyrev, 3)) { + if (PHY_IPA(pi)) { + u32 *tx_gaintbl = + wlc_phy_get_ipa_gaintbl_nphy(pi); + txgain = tx_gaintbl[txpi[core]]; + } else { + if (CHSPEC_IS5G(pi->radio_chanspec)) { + if (NREV_IS(phyrev, 3)) { + txgain = + nphy_tpc_5GHz_txgain_rev3 + [txpi[core]]; + } else if (NREV_IS(phyrev, 4)) { + txgain = ( + pi->srom_fem5g.extpagain == + 3) ? + nphy_tpc_5GHz_txgain_HiPwrEPA + [txpi[core]] : + nphy_tpc_5GHz_txgain_rev4 + [txpi[core]]; + } else { + txgain = + nphy_tpc_5GHz_txgain_rev5 + [txpi[core]]; + } + } else { + if (NREV_GE(phyrev, 5) && + (pi->srom_fem2g.extpagain == 3)) { + txgain = + nphy_tpc_txgain_HiPwrEPA + [txpi[core]]; + } else { + txgain = nphy_tpc_txgain_rev3 + [txpi[core]]; + } + } + } + } else { + txgain = nphy_tpc_txgain[txpi[core]]; + } + + if (NREV_GE(phyrev, 3)) + rad_gain = (txgain >> 16) & ((1 << (32 - 16 + 1)) - 1); + else + rad_gain = (txgain >> 16) & ((1 << (28 - 16 + 1)) - 1); + + if (NREV_GE(phyrev, 7)) + dac_gain = (txgain >> 8) & ((1 << (10 - 8 + 1)) - 1); + else + dac_gain = (txgain >> 8) & ((1 << (13 - 8 + 1)) - 1); + + bbmult = (txgain >> 0) & ((1 << (7 - 0 + 1)) - 1); + + if (NREV_GE(phyrev, 3)) + mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : + 0xa5), (0x1 << 8), (0x1 << 8)); + else + mod_phy_reg(pi, 0xa5, (0x1 << 14), (0x1 << 14)); + + write_phy_reg(pi, (core == PHY_CORE_0) ? 0xaa : 0xab, dac_gain); + + wlc_phy_table_write_nphy(pi, 7, 1, (0x110 + core), 16, + &rad_gain); + + wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m1m2); + m1m2 &= ((core == PHY_CORE_0) ? 0x00ff : 0xff00); + m1m2 |= ((core == PHY_CORE_0) ? (bbmult << 8) : (bbmult << 0)); + wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m1m2); + + if (PHY_IPA(pi)) { + wlc_phy_table_read_nphy(pi, + (core == + PHY_CORE_0 ? + NPHY_TBL_ID_CORE1TXPWRCTL : + NPHY_TBL_ID_CORE2TXPWRCTL), 1, + 576 + txpi[core], 32, + &rfpwr_offset); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1ff << 4), + ((s16) rfpwr_offset) << 4); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 2), (1) << 2); + + } + } + + and_phy_reg(pi, 0xbf, (u16) (~(0x1f << 0))); + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +static void +wlc_phy_txpwr_nphy_srom_convert(u8 *srom_max, u16 *pwr_offset, + u8 tmp_max_pwr, u8 rate_start, + u8 rate_end) +{ + u8 rate; + u8 word_num, nibble_num; + u8 tmp_nibble; + + for (rate = rate_start; rate <= rate_end; rate++) { + word_num = (rate - rate_start) >> 2; + nibble_num = (rate - rate_start) & 0x3; + tmp_nibble = (pwr_offset[word_num] >> 4 * nibble_num) & 0xf; + + srom_max[rate] = tmp_max_pwr - 2 * tmp_nibble; + } +} + +static void +wlc_phy_txpwr_nphy_po_apply(u8 *srom_max, u8 pwr_offset, + u8 rate_start, u8 rate_end) +{ + u8 rate; + + for (rate = rate_start; rate <= rate_end; rate++) + srom_max[rate] -= 2 * pwr_offset; +} + +void +wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start, + u8 rate_mcs_end, u8 rate_ofdm_start) +{ + u8 rate1, rate2; + + rate2 = rate_ofdm_start; + for (rate1 = rate_mcs_start; rate1 <= rate_mcs_end - 1; rate1++) { + power[rate1] = power[rate2]; + rate2 += (rate1 == rate_mcs_start) ? 2 : 1; + } + power[rate_mcs_end] = power[rate_mcs_end - 1]; +} + +void +wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, u8 rate_ofdm_start, + u8 rate_ofdm_end, u8 rate_mcs_start) +{ + u8 rate1, rate2; + + for (rate1 = rate_ofdm_start, rate2 = rate_mcs_start; + rate1 <= rate_ofdm_end; rate1++, rate2++) { + power[rate1] = power[rate2]; + if (rate1 == rate_ofdm_start) + power[++rate1] = power[rate2]; + } +} + +void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi) +{ + uint rate1, rate2, band_num; + u8 tmp_bw40po = 0, tmp_cddpo = 0, tmp_stbcpo = 0; + u8 tmp_max_pwr = 0; + u16 pwr_offsets1[2], *pwr_offsets2 = NULL; + u8 *tx_srom_max_rate = NULL; + + for (band_num = 0; band_num < (CH_2G_GROUP + CH_5G_GROUP); + band_num++) { + switch (band_num) { + case 0: + + tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_2g, + pi->nphy_pwrctrl_info[1].max_pwr_2g); + + pwr_offsets1[0] = pi->cck2gpo; + wlc_phy_txpwr_nphy_srom_convert(pi->tx_srom_max_rate_2g, + pwr_offsets1, + tmp_max_pwr, + TXP_FIRST_CCK, + TXP_LAST_CCK); + + pwr_offsets1[0] = (u16) (pi->ofdm2gpo & 0xffff); + pwr_offsets1[1] = + (u16) (pi->ofdm2gpo >> 16) & 0xffff; + + pwr_offsets2 = pi->mcs2gpo; + + tmp_cddpo = pi->cdd2gpo; + tmp_stbcpo = pi->stbc2gpo; + tmp_bw40po = pi->bw402gpo; + + tx_srom_max_rate = pi->tx_srom_max_rate_2g; + break; + case 1: + + tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_5gm, + pi->nphy_pwrctrl_info[1].max_pwr_5gm); + + pwr_offsets1[0] = (u16) (pi->ofdm5gpo & 0xffff); + pwr_offsets1[1] = + (u16) (pi->ofdm5gpo >> 16) & 0xffff; + + pwr_offsets2 = pi->mcs5gpo; + + tmp_cddpo = pi->cdd5gpo; + tmp_stbcpo = pi->stbc5gpo; + tmp_bw40po = pi->bw405gpo; + + tx_srom_max_rate = pi->tx_srom_max_rate_5g_mid; + break; + case 2: + + tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_5gl, + pi->nphy_pwrctrl_info[1].max_pwr_5gl); + + pwr_offsets1[0] = (u16) (pi->ofdm5glpo & 0xffff); + pwr_offsets1[1] = + (u16) (pi->ofdm5glpo >> 16) & 0xffff; + + pwr_offsets2 = pi->mcs5glpo; + + tmp_cddpo = pi->cdd5glpo; + tmp_stbcpo = pi->stbc5glpo; + tmp_bw40po = pi->bw405glpo; + + tx_srom_max_rate = pi->tx_srom_max_rate_5g_low; + break; + case 3: + + tmp_max_pwr = min(pi->nphy_pwrctrl_info[0].max_pwr_5gh, + pi->nphy_pwrctrl_info[1].max_pwr_5gh); + + pwr_offsets1[0] = (u16) (pi->ofdm5ghpo & 0xffff); + pwr_offsets1[1] = + (u16) (pi->ofdm5ghpo >> 16) & 0xffff; + + pwr_offsets2 = pi->mcs5ghpo; + + tmp_cddpo = pi->cdd5ghpo; + tmp_stbcpo = pi->stbc5ghpo; + tmp_bw40po = pi->bw405ghpo; + + tx_srom_max_rate = pi->tx_srom_max_rate_5g_hi; + break; + } + + wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, pwr_offsets1, + tmp_max_pwr, TXP_FIRST_OFDM, + TXP_LAST_OFDM); + + wlc_phy_ofdm_to_mcs_powers_nphy(tx_srom_max_rate, + TXP_FIRST_MCS_20_SISO, + TXP_LAST_MCS_20_SISO, + TXP_FIRST_OFDM); + + wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, pwr_offsets2, + tmp_max_pwr, + TXP_FIRST_MCS_20_CDD, + TXP_LAST_MCS_20_CDD); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) + wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, tmp_cddpo, + TXP_FIRST_MCS_20_CDD, + TXP_LAST_MCS_20_CDD); + + wlc_phy_mcs_to_ofdm_powers_nphy(tx_srom_max_rate, + TXP_FIRST_OFDM_20_CDD, + TXP_LAST_OFDM_20_CDD, + TXP_FIRST_MCS_20_CDD); + + wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, pwr_offsets2, + tmp_max_pwr, + TXP_FIRST_MCS_20_STBC, + TXP_LAST_MCS_20_STBC); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) + wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, + tmp_stbcpo, + TXP_FIRST_MCS_20_STBC, + TXP_LAST_MCS_20_STBC); + + wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, + &pwr_offsets2[2], tmp_max_pwr, + TXP_FIRST_MCS_20_SDM, + TXP_LAST_MCS_20_SDM); + + if (NPHY_IS_SROM_REINTERPRET) { + + wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, + &pwr_offsets2[4], + tmp_max_pwr, + TXP_FIRST_MCS_40_SISO, + TXP_LAST_MCS_40_SISO); + + wlc_phy_mcs_to_ofdm_powers_nphy(tx_srom_max_rate, + TXP_FIRST_OFDM_40_SISO, + TXP_LAST_OFDM_40_SISO, + TXP_FIRST_MCS_40_SISO); + + wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, + &pwr_offsets2[4], + tmp_max_pwr, + TXP_FIRST_MCS_40_CDD, + TXP_LAST_MCS_40_CDD); + + wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, tmp_cddpo, + TXP_FIRST_MCS_40_CDD, + TXP_LAST_MCS_40_CDD); + + wlc_phy_mcs_to_ofdm_powers_nphy(tx_srom_max_rate, + TXP_FIRST_OFDM_40_CDD, + TXP_LAST_OFDM_40_CDD, + TXP_FIRST_MCS_40_CDD); + + wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, + &pwr_offsets2[4], + tmp_max_pwr, + TXP_FIRST_MCS_40_STBC, + TXP_LAST_MCS_40_STBC); + + wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, + tmp_stbcpo, + TXP_FIRST_MCS_40_STBC, + TXP_LAST_MCS_40_STBC); + + wlc_phy_txpwr_nphy_srom_convert(tx_srom_max_rate, + &pwr_offsets2[6], + tmp_max_pwr, + TXP_FIRST_MCS_40_SDM, + TXP_LAST_MCS_40_SDM); + } else { + + for (rate1 = TXP_FIRST_OFDM_40_SISO, rate2 = + TXP_FIRST_OFDM; + rate1 <= TXP_LAST_MCS_40_SDM; + rate1++, rate2++) + tx_srom_max_rate[rate1] = + tx_srom_max_rate[rate2]; + } + + if (NREV_GE(pi->pubpi.phy_rev, 3)) + wlc_phy_txpwr_nphy_po_apply(tx_srom_max_rate, + tmp_bw40po, + TXP_FIRST_OFDM_40_SISO, + TXP_LAST_MCS_40_SDM); + + tx_srom_max_rate[TXP_MCS_32] = + tx_srom_max_rate[TXP_FIRST_MCS_40_CDD]; + } + + return; +} + +void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi) +{ + u8 tx_pwr_ctrl_state; + wlc_phy_txpwr_limit_to_tbl_nphy(pi); + wlc_phy_txpwrctrl_pwr_setup_nphy(pi); + + tx_pwr_ctrl_state = pi->nphy_txpwrctrl; + + if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) { + wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, MCTL_PHYLOCK); + (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)); + udelay(1); + } + + wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); + + if (D11REV_IS(pi->sh->corerev, 11) || D11REV_IS(pi->sh->corerev, 12)) + wlapi_bmac_mctrl(pi->sh->physhim, MCTL_PHYLOCK, 0); +} + +static bool wlc_phy_txpwr_ison_nphy(struct brcms_phy *pi) +{ + return read_phy_reg((pi), 0x1e7) & ((0x1 << 15) | + (0x1 << 14) | (0x1 << 13)); +} + +u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi) +{ + u16 tmp; + u16 pwr_idx[2]; + + if (wlc_phy_txpwr_ison_nphy(pi)) { + pwr_idx[0] = wlc_phy_txpwr_idx_cur_get_nphy(pi, PHY_CORE_0); + pwr_idx[1] = wlc_phy_txpwr_idx_cur_get_nphy(pi, PHY_CORE_1); + + tmp = (pwr_idx[0] << 8) | pwr_idx[1]; + } else { + tmp = ((pi->nphy_txpwrindex[PHY_CORE_0].index_internal & 0xff) + << 8) | + (pi->nphy_txpwrindex[PHY_CORE_1].index_internal & 0xff); + } + + return tmp; +} + +void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi) +{ + if (PHY_IPA(pi) + && (pi->nphy_force_papd_cal + || (wlc_phy_txpwr_ison_nphy(pi) + && + (((u32) + abs(wlc_phy_txpwr_idx_cur_get_nphy(pi, 0) - + pi->nphy_papd_tx_gain_at_last_cal[0]) >= 4) + || ((u32) + abs(wlc_phy_txpwr_idx_cur_get_nphy(pi, 1) - + pi->nphy_papd_tx_gain_at_last_cal[1]) >= 4))))) + wlc_phy_a4(pi, true); +} + +void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type) +{ + u16 mask = 0, val = 0, ishw = 0; + u8 ctr; + uint core; + u32 tbl_offset; + u32 tbl_len; + u16 regval[84]; + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + switch (ctrl_type) { + case PHY_TPC_HW_OFF: + case PHY_TPC_HW_ON: + pi->nphy_txpwrctrl = ctrl_type; + break; + default: + break; + } + + if (ctrl_type == PHY_TPC_HW_OFF) { + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + + if (wlc_phy_txpwr_ison_nphy(pi)) { + for (core = 0; core < pi->pubpi.phy_corenum; + core++) + pi->nphy_txpwr_idx[core] = + wlc_phy_txpwr_idx_cur_get_nphy( + pi, + (u8) core); + } + + } + + tbl_len = 84; + tbl_offset = 64; + for (ctr = 0; ctr < tbl_len; ctr++) + regval[ctr] = 0; + wlc_phy_table_write_nphy(pi, 26, tbl_len, tbl_offset, 16, + regval); + wlc_phy_table_write_nphy(pi, 27, tbl_len, tbl_offset, 16, + regval); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) + and_phy_reg(pi, 0x1e7, + (u16) (~((0x1 << 15) | + (0x1 << 14) | (0x1 << 13)))); + else + and_phy_reg(pi, 0x1e7, + (u16) (~((0x1 << 14) | (0x1 << 13)))); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + or_phy_reg(pi, 0x8f, (0x1 << 8)); + or_phy_reg(pi, 0xa5, (0x1 << 8)); + } else { + or_phy_reg(pi, 0xa5, (0x1 << 14)); + } + + if (NREV_IS(pi->pubpi.phy_rev, 2)) + mod_phy_reg(pi, 0xdc, 0x00ff, 0x53); + else if (NREV_LT(pi->pubpi.phy_rev, 2)) + mod_phy_reg(pi, 0xdc, 0x00ff, 0x5a); + + if (NREV_LT(pi->pubpi.phy_rev, 2) && + pi->bw == WL_CHANSPEC_BW_40) + wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_IQSWAP_WAR, + MHF1_IQSWAP_WAR, BRCM_BAND_ALL); + + } else { + + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE1TXPWRCTL, 84, 64, + 8, pi->adj_pwr_tbl_nphy); + wlc_phy_table_write_nphy(pi, NPHY_TBL_ID_CORE2TXPWRCTL, 84, 64, + 8, pi->adj_pwr_tbl_nphy); + + ishw = (ctrl_type == PHY_TPC_HW_ON) ? 0x1 : 0x0; + mask = (0x1 << 14) | (0x1 << 13); + val = (ishw << 14) | (ishw << 13); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + mask |= (0x1 << 15); + val |= (ishw << 15); + } + + mod_phy_reg(pi, 0x1e7, mask, val); + + if (CHSPEC_IS5G(pi->radio_chanspec)) { + if (NREV_GE(pi->pubpi.phy_rev, 7)) { + mod_phy_reg(pi, 0x1e7, (0x7f << 0), 0x32); + mod_phy_reg(pi, 0x222, (0xff << 0), 0x32); + } else { + mod_phy_reg(pi, 0x1e7, (0x7f << 0), 0x64); + if (NREV_GT(pi->pubpi.phy_rev, 1)) + mod_phy_reg(pi, 0x222, + (0xff << 0), 0x64); + } + } + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + if ((pi->nphy_txpwr_idx[0] != 128) + && (pi->nphy_txpwr_idx[1] != 128)) + wlc_phy_txpwr_idx_cur_set_nphy(pi, + pi-> + nphy_txpwr_idx + [0], + pi-> + nphy_txpwr_idx + [1]); + } + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + and_phy_reg(pi, 0x8f, ~(0x1 << 8)); + and_phy_reg(pi, 0xa5, ~(0x1 << 8)); + } else { + and_phy_reg(pi, 0xa5, ~(0x1 << 14)); + } + + if (NREV_IS(pi->pubpi.phy_rev, 2)) + mod_phy_reg(pi, 0xdc, 0x00ff, 0x3b); + else if (NREV_LT(pi->pubpi.phy_rev, 2)) + mod_phy_reg(pi, 0xdc, 0x00ff, 0x40); + + if (NREV_LT(pi->pubpi.phy_rev, 2) && + pi->bw == WL_CHANSPEC_BW_40) + wlapi_bmac_mhf(pi->sh->physhim, MHF1, MHF1_IQSWAP_WAR, + 0x0, BRCM_BAND_ALL); + + if (PHY_IPA(pi)) { + mod_phy_reg(pi, (0 == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 2), (0) << 2); + + mod_phy_reg(pi, (1 == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 2), (0) << 2); + + } + + } + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +void +wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask, s8 txpwrindex, + bool restore_cals) +{ + u8 core, txpwrctl_tbl; + u16 tx_ind0, iq_ind0, lo_ind0; + u16 m1m2; + u32 txgain; + u16 rad_gain, dac_gain; + u8 bbmult; + u32 iqcomp; + u16 iqcomp_a, iqcomp_b; + u32 locomp; + u16 tmpval; + u8 tx_pwr_ctrl_state; + s32 rfpwr_offset; + u16 regval[2]; + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + + tx_ind0 = 192; + iq_ind0 = 320; + lo_ind0 = 448; + + for (core = 0; core < pi->pubpi.phy_corenum; core++) { + + if ((core_mask & (1 << core)) == 0) + continue; + + txpwrctl_tbl = (core == PHY_CORE_0) ? 26 : 27; + + if (txpwrindex < 0) { + if (pi->nphy_txpwrindex[core].index < 0) + continue; + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + mod_phy_reg(pi, 0x8f, + (0x1 << 8), + pi->nphy_txpwrindex[core]. + AfectrlOverride); + mod_phy_reg(pi, 0xa5, (0x1 << 8), + pi->nphy_txpwrindex[core]. + AfectrlOverride); + } else { + mod_phy_reg(pi, 0xa5, + (0x1 << 14), + pi->nphy_txpwrindex[core]. + AfectrlOverride); + } + + write_phy_reg(pi, (core == PHY_CORE_0) ? + 0xaa : 0xab, + pi->nphy_txpwrindex[core].AfeCtrlDacGain); + + wlc_phy_table_write_nphy(pi, 7, 1, (0x110 + core), 16, + &pi->nphy_txpwrindex[core]. + rad_gain); + + wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m1m2); + m1m2 &= ((core == PHY_CORE_0) ? 0x00ff : 0xff00); + m1m2 |= ((core == PHY_CORE_0) ? + (pi->nphy_txpwrindex[core].bbmult << 8) : + (pi->nphy_txpwrindex[core].bbmult << 0)); + wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m1m2); + + if (restore_cals) { + wlc_phy_table_write_nphy( + pi, 15, 2, (80 + 2 * core), 16, + &pi->nphy_txpwrindex[core].iqcomp_a); + wlc_phy_table_write_nphy( + pi, 15, 1, (85 + core), 16, + &pi->nphy_txpwrindex[core].locomp); + wlc_phy_table_write_nphy( + pi, 15, 1, (93 + core), 16, + &pi->nphy_txpwrindex[core].locomp); + } + + wlc_phy_txpwrctrl_enable_nphy(pi, pi->nphy_txpwrctrl); + + pi->nphy_txpwrindex[core].index_internal = + pi->nphy_txpwrindex[core].index_internal_save; + } else { + + if (pi->nphy_txpwrindex[core].index < 0) { + + if (NREV_GE(pi->pubpi.phy_rev, 3)) { + mod_phy_reg(pi, 0x8f, + (0x1 << 8), + pi->nphy_txpwrindex[core]. + AfectrlOverride); + mod_phy_reg(pi, 0xa5, (0x1 << 8), + pi->nphy_txpwrindex[core]. + AfectrlOverride); + } else { + pi->nphy_txpwrindex[core]. + AfectrlOverride = + read_phy_reg(pi, 0xa5); + } + + pi->nphy_txpwrindex[core].AfeCtrlDacGain = + read_phy_reg(pi, (core == PHY_CORE_0) ? + 0xaa : 0xab); + + wlc_phy_table_read_nphy(pi, 7, 1, + (0x110 + core), 16, + &pi-> + nphy_txpwrindex[core]. + rad_gain); + + wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, + &tmpval); + tmpval >>= ((core == PHY_CORE_0) ? 8 : 0); + tmpval &= 0xff; + pi->nphy_txpwrindex[core].bbmult = (u8) tmpval; + + wlc_phy_table_read_nphy(pi, 15, 2, + (80 + 2 * core), 16, + &pi-> + nphy_txpwrindex[core]. + iqcomp_a); + + wlc_phy_table_read_nphy(pi, 15, 1, (85 + core), + 16, + &pi-> + nphy_txpwrindex[core]. + locomp); + + pi->nphy_txpwrindex[core].index_internal_save = + pi->nphy_txpwrindex[core]. + index_internal; + } + + tx_pwr_ctrl_state = pi->nphy_txpwrctrl; + wlc_phy_txpwrctrl_enable_nphy(pi, PHY_TPC_HW_OFF); + + if (NREV_IS(pi->pubpi.phy_rev, 1)) + wlapi_bmac_phyclk_fgc(pi->sh->physhim, ON); + + wlc_phy_table_read_nphy(pi, txpwrctl_tbl, 1, + (tx_ind0 + txpwrindex), 32, + &txgain); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) + rad_gain = (txgain >> 16) & + ((1 << (32 - 16 + 1)) - 1); + else + rad_gain = (txgain >> 16) & + ((1 << (28 - 16 + 1)) - 1); + + dac_gain = (txgain >> 8) & ((1 << (13 - 8 + 1)) - 1); + bbmult = (txgain >> 0) & ((1 << (7 - 0 + 1)) - 1); + + if (NREV_GE(pi->pubpi.phy_rev, 3)) + mod_phy_reg(pi, ((core == PHY_CORE_0) ? 0x8f : + 0xa5), (0x1 << 8), (0x1 << 8)); + else + mod_phy_reg(pi, 0xa5, (0x1 << 14), (0x1 << 14)); + + write_phy_reg(pi, (core == PHY_CORE_0) ? + 0xaa : 0xab, dac_gain); + + wlc_phy_table_write_nphy(pi, 7, 1, (0x110 + core), 16, + &rad_gain); + + wlc_phy_table_read_nphy(pi, 15, 1, 87, 16, &m1m2); + m1m2 &= ((core == PHY_CORE_0) ? 0x00ff : 0xff00); + m1m2 |= ((core == PHY_CORE_0) ? + (bbmult << 8) : (bbmult << 0)); + + wlc_phy_table_write_nphy(pi, 15, 1, 87, 16, &m1m2); + + wlc_phy_table_read_nphy(pi, txpwrctl_tbl, 1, + (iq_ind0 + txpwrindex), 32, + &iqcomp); + iqcomp_a = (iqcomp >> 10) & ((1 << (19 - 10 + 1)) - 1); + iqcomp_b = (iqcomp >> 0) & ((1 << (9 - 0 + 1)) - 1); + + if (restore_cals) { + regval[0] = (u16) iqcomp_a; + regval[1] = (u16) iqcomp_b; + wlc_phy_table_write_nphy(pi, 15, 2, + (80 + 2 * core), 16, + regval); + } + + wlc_phy_table_read_nphy(pi, txpwrctl_tbl, 1, + (lo_ind0 + txpwrindex), 32, + &locomp); + if (restore_cals) + wlc_phy_table_write_nphy(pi, 15, 1, (85 + core), + 16, &locomp); + + if (NREV_IS(pi->pubpi.phy_rev, 1)) + wlapi_bmac_phyclk_fgc(pi->sh->physhim, OFF); + + if (PHY_IPA(pi)) { + wlc_phy_table_read_nphy(pi, + (core == PHY_CORE_0 ? + NPHY_TBL_ID_CORE1TXPWRCTL : + NPHY_TBL_ID_CORE2TXPWRCTL), + 1, 576 + txpwrindex, 32, + &rfpwr_offset); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1ff << 4), + ((s16) rfpwr_offset) << 4); + + mod_phy_reg(pi, (core == PHY_CORE_0) ? 0x297 : + 0x29b, (0x1 << 2), (1) << 2); + + } + + wlc_phy_txpwrctrl_enable_nphy(pi, tx_pwr_ctrl_state); + } + + pi->nphy_txpwrindex[core].index = txpwrindex; + } + + if (pi->phyhang_avoid) + wlc_phy_stay_in_carriersearch_nphy(pi, false); +} + +void +wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan, u8 *max_pwr, + u8 txp_rate_idx) +{ + u8 chan_freq_range; + + chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, chan); + switch (chan_freq_range) { + case WL_CHAN_FREQ_RANGE_2G: + *max_pwr = pi->tx_srom_max_rate_2g[txp_rate_idx]; + break; + case WL_CHAN_FREQ_RANGE_5GM: + *max_pwr = pi->tx_srom_max_rate_5g_mid[txp_rate_idx]; + break; + case WL_CHAN_FREQ_RANGE_5GL: + *max_pwr = pi->tx_srom_max_rate_5g_low[txp_rate_idx]; + break; + case WL_CHAN_FREQ_RANGE_5GH: + *max_pwr = pi->tx_srom_max_rate_5g_hi[txp_rate_idx]; + break; + default: + *max_pwr = pi->tx_srom_max_rate_2g[txp_rate_idx]; + break; + } + + return; +} + +void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, bool enable) +{ + u16 clip_off[] = { 0xffff, 0xffff }; + + if (enable) { + if (pi->nphy_deaf_count == 0) { + pi->classifier_state = + wlc_phy_classifier_nphy(pi, 0, 0); + wlc_phy_classifier_nphy(pi, (0x7 << 0), 4); + wlc_phy_clip_det_nphy(pi, 0, pi->clip_state); + wlc_phy_clip_det_nphy(pi, 1, clip_off); + } + + pi->nphy_deaf_count++; + + wlc_phy_resetcca_nphy(pi); + + } else { + pi->nphy_deaf_count--; + + if (pi->nphy_deaf_count == 0) { + wlc_phy_classifier_nphy(pi, (0x7 << 0), + pi->classifier_state); + wlc_phy_clip_det_nphy(pi, 1, pi->clip_state); + } + } +} + +void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode) +{ + wlapi_suspend_mac_and_wait(pi->sh->physhim); + + if (mode) { + if (pi->nphy_deaf_count == 0) + wlc_phy_stay_in_carriersearch_nphy(pi, true); + } else if (pi->nphy_deaf_count > 0) { + wlc_phy_stay_in_carriersearch_nphy(pi, false); + } + + wlapi_enable_mac(pi->sh->physhim); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c new file mode 100644 index 000000000000..faf1ebe76068 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2010 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 "phy_qmath.h" + +/* + * Description: This function make 16 bit unsigned multiplication. + * To fit the output into 16 bits the 32 bit multiplication result is right + * shifted by 16 bits. + */ +u16 qm_mulu16(u16 op1, u16 op2) +{ + return (u16) (((u32) op1 * (u32) op2) >> 16); +} + +/* + * Description: This function make 16 bit multiplication and return the result + * in 16 bits. To fit the multiplication result into 16 bits the multiplication + * result is right shifted by 15 bits. Right shifting 15 bits instead of 16 bits + * is done to remove the extra sign bit formed due to the multiplication. + * When both the 16bit inputs are 0x8000 then the output is saturated to + * 0x7fffffff. + */ +s16 qm_muls16(s16 op1, s16 op2) +{ + s32 result; + if (op1 == (s16) 0x8000 && op2 == (s16) 0x8000) + result = 0x7fffffff; + else + result = ((s32) (op1) * (s32) (op2)); + + return (s16) (result >> 15); +} + +/* + * Description: This function add two 32 bit numbers and return the 32bit + * result. If the result overflow 32 bits, the output will be saturated to + * 32bits. + */ +s32 qm_add32(s32 op1, s32 op2) +{ + s32 result; + result = op1 + op2; + if (op1 < 0 && op2 < 0 && result > 0) + result = 0x80000000; + else if (op1 > 0 && op2 > 0 && result < 0) + result = 0x7fffffff; + + return result; +} + +/* + * Description: This function add two 16 bit numbers and return the 16bit + * result. If the result overflow 16 bits, the output will be saturated to + * 16bits. + */ +s16 qm_add16(s16 op1, s16 op2) +{ + s16 result; + s32 temp = (s32) op1 + (s32) op2; + if (temp > (s32) 0x7fff) + result = (s16) 0x7fff; + else if (temp < (s32) 0xffff8000) + result = (s16) 0xffff8000; + else + result = (s16) temp; + + return result; +} + +/* + * Description: This function make 16 bit subtraction and return the 16bit + * result. If the result overflow 16 bits, the output will be saturated to + * 16bits. + */ +s16 qm_sub16(s16 op1, s16 op2) +{ + s16 result; + s32 temp = (s32) op1 - (s32) op2; + if (temp > (s32) 0x7fff) + result = (s16) 0x7fff; + else if (temp < (s32) 0xffff8000) + result = (s16) 0xffff8000; + else + result = (s16) temp; + + return result; +} + +/* + * Description: This function make a 32 bit saturated left shift when the + * specified shift is +ve. This function will make a 32 bit right shift when + * the specified shift is -ve. This function return the result after shifting + * operation. + */ +s32 qm_shl32(s32 op, int shift) +{ + int i; + s32 result; + result = op; + if (shift > 31) + shift = 31; + else if (shift < -31) + shift = -31; + if (shift >= 0) { + for (i = 0; i < shift; i++) + result = qm_add32(result, result); + } else { + result = result >> (-shift); + } + + return result; +} + +/* + * Description: This function make a 16 bit saturated left shift when the + * specified shift is +ve. This function will make a 16 bit right shift when + * the specified shift is -ve. This function return the result after shifting + * operation. + */ +s16 qm_shl16(s16 op, int shift) +{ + int i; + s16 result; + result = op; + if (shift > 15) + shift = 15; + else if (shift < -15) + shift = -15; + if (shift > 0) { + for (i = 0; i < shift; i++) + result = qm_add16(result, result); + } else { + result = result >> (-shift); + } + + return result; +} + +/* + * Description: This function make a 16 bit right shift when shift is +ve. + * This function make a 16 bit saturated left shift when shift is -ve. This + * function return the result of the shift operation. + */ +s16 qm_shr16(s16 op, int shift) +{ + return qm_shl16(op, -shift); +} + +/* + * Description: This function return the number of redundant sign bits in a + * 32 bit number. Example: qm_norm32(0x00000080) = 23 + */ +s16 qm_norm32(s32 op) +{ + u16 u16extraSignBits; + if (op == 0) { + return 31; + } else { + u16extraSignBits = 0; + while ((op >> 31) == (op >> 30)) { + u16extraSignBits++; + op = op << 1; + } + } + return u16extraSignBits; +} + +/* This table is log2(1+(i/32)) where i=[0:1:31], in q.15 format */ +static const s16 log_table[] = { + 0, + 1455, + 2866, + 4236, + 5568, + 6863, + 8124, + 9352, + 10549, + 11716, + 12855, + 13968, + 15055, + 16117, + 17156, + 18173, + 19168, + 20143, + 21098, + 22034, + 22952, + 23852, + 24736, + 25604, + 26455, + 27292, + 28114, + 28922, + 29717, + 30498, + 31267, + 32024 +}; + +#define LOG_TABLE_SIZE 32 /* log_table size */ +#define LOG2_LOG_TABLE_SIZE 5 /* log2(log_table size) */ +#define Q_LOG_TABLE 15 /* qformat of log_table */ +#define LOG10_2 19728 /* log10(2) in q.16 */ + +/* + * Description: + * This routine takes the input number N and its q format qN and compute + * the log10(N). This routine first normalizes the input no N. Then N is in + * mag*(2^x) format. mag is any number in the range 2^30-(2^31 - 1). + * Then log2(mag * 2^x) = log2(mag) + x is computed. From that + * log10(mag * 2^x) = log2(mag * 2^x) * log10(2) is computed. + * This routine looks the log2 value in the table considering + * LOG2_LOG_TABLE_SIZE+1 MSBs. As the MSB is always 1, only next + * LOG2_OF_LOG_TABLE_SIZE MSBs are used for table lookup. Next 16 MSBs are used + * for interpolation. + * Inputs: + * N - number to which log10 has to be found. + * qN - q format of N + * log10N - address where log10(N) will be written. + * qLog10N - address where log10N qformat will be written. + * Note/Problem: + * For accurate results input should be in normalized or near normalized form. + */ +void qm_log10(s32 N, s16 qN, s16 *log10N, s16 *qLog10N) +{ + s16 s16norm, s16tableIndex, s16errorApproximation; + u16 u16offset; + s32 s32log; + + /* normalize the N. */ + s16norm = qm_norm32(N); + N = N << s16norm; + + /* The qformat of N after normalization. + * -30 is added to treat the no as between 1.0 to 2.0 + * i.e. after adding the -30 to the qformat the decimal point will be + * just rigtht of the MSB. (i.e. after sign bit and 1st MSB). i.e. + * at the right side of 30th bit. + */ + qN = qN + s16norm - 30; + + /* take the table index as the LOG2_OF_LOG_TABLE_SIZE bits right of the + * MSB */ + s16tableIndex = (s16) (N >> (32 - (2 + LOG2_LOG_TABLE_SIZE))); + + /* remove the MSB. the MSB is always 1 after normalization. */ + s16tableIndex = + s16tableIndex & (s16) ((1 << LOG2_LOG_TABLE_SIZE) - 1); + + /* remove the (1+LOG2_OF_LOG_TABLE_SIZE) MSBs in the N. */ + N = N & ((1 << (32 - (2 + LOG2_LOG_TABLE_SIZE))) - 1); + + /* take the offset as the 16 MSBS after table index. + */ + u16offset = (u16) (N >> (32 - (2 + LOG2_LOG_TABLE_SIZE + 16))); + + /* look the log value in the table. */ + s32log = log_table[s16tableIndex]; /* q.15 format */ + + /* interpolate using the offset. q.15 format. */ + s16errorApproximation = (s16) qm_mulu16(u16offset, + (u16) (log_table[s16tableIndex + 1] - + log_table[s16tableIndex])); + + /* q.15 format */ + s32log = qm_add16((s16) s32log, s16errorApproximation); + + /* adjust for the qformat of the N as + * log2(mag * 2^x) = log2(mag) + x + */ + s32log = qm_add32(s32log, ((s32) -qN) << 15); /* q.15 format */ + + /* normalize the result. */ + s16norm = qm_norm32(s32log); + + /* bring all the important bits into lower 16 bits */ + /* q.15+s16norm-16 format */ + s32log = qm_shl32(s32log, s16norm - 16); + + /* compute the log10(N) by multiplying log2(N) with log10(2). + * as log10(mag * 2^x) = log2(mag * 2^x) * log10(2) + * log10N in q.15+s16norm-16+1 (LOG10_2 is in q.16) + */ + *log10N = qm_muls16((s16) s32log, (s16) LOG10_2); + + /* write the q format of the result. */ + *qLog10N = 15 + s16norm - 16 + 1; + + return; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.h new file mode 100644 index 000000000000..20e3783f921b --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010 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 _BRCM_QMATH_H_ +#define _BRCM_QMATH_H_ + +#include + +u16 qm_mulu16(u16 op1, u16 op2); + +s16 qm_muls16(s16 op1, s16 op2); + +s32 qm_add32(s32 op1, s32 op2); + +s16 qm_add16(s16 op1, s16 op2); + +s16 qm_sub16(s16 op1, s16 op2); + +s32 qm_shl32(s32 op, int shift); + +s16 qm_shl16(s16 op, int shift); + +s16 qm_shr16(s16 op, int shift); + +s16 qm_norm32(s32 op); + +void qm_log10(s32 N, s16 qN, s16 *log10N, s16 *qLog10N); + +#endif /* #ifndef _BRCM_QMATH_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_radio.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_radio.h new file mode 100644 index 000000000000..c3a675455ff5 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_radio.h @@ -0,0 +1,1533 @@ +/* + * Copyright (c) 2010 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 _BRCM_PHY_RADIO_H_ +#define _BRCM_PHY_RADIO_H_ + +#define RADIO_IDCODE 0x01 + +#define RADIO_DEFAULT_CORE 0 + +#define RXC0_RSSI_RST 0x80 +#define RXC0_MODE_RSSI 0x40 +#define RXC0_MODE_OFF 0x20 +#define RXC0_MODE_CM 0x10 +#define RXC0_LAN_LOAD 0x08 +#define RXC0_OFF_ADJ_MASK 0x07 + +#define TXC0_MODE_TXLPF 0x04 +#define TXC0_PA_TSSI_EN 0x02 +#define TXC0_TSSI_EN 0x01 + +#define TXC1_PA_GAIN_MASK 0x60 +#define TXC1_PA_GAIN_3DB 0x40 +#define TXC1_PA_GAIN_2DB 0x20 +#define TXC1_TX_MIX_GAIN 0x10 +#define TXC1_OFF_I_MASK 0x0c +#define TXC1_OFF_Q_MASK 0x03 + +#define RADIO_2055_READ_OFF 0x100 +#define RADIO_2057_READ_OFF 0x200 + +#define RADIO_2055_GEN_SPARE 0x00 +#define RADIO_2055_SP_PIN_PD 0x02 +#define RADIO_2055_SP_RSSI_CORE1 0x03 +#define RADIO_2055_SP_PD_MISC_CORE1 0x04 +#define RADIO_2055_SP_RSSI_CORE2 0x05 +#define RADIO_2055_SP_PD_MISC_CORE2 0x06 +#define RADIO_2055_SP_RX_GC1_CORE1 0x07 +#define RADIO_2055_SP_RX_GC2_CORE1 0x08 +#define RADIO_2055_SP_RX_GC1_CORE2 0x09 +#define RADIO_2055_SP_RX_GC2_CORE2 0x0a +#define RADIO_2055_SP_LPF_BW_SELECT_CORE1 0x0b +#define RADIO_2055_SP_LPF_BW_SELECT_CORE2 0x0c +#define RADIO_2055_SP_TX_GC1_CORE1 0x0d +#define RADIO_2055_SP_TX_GC2_CORE1 0x0e +#define RADIO_2055_SP_TX_GC1_CORE2 0x0f +#define RADIO_2055_SP_TX_GC2_CORE2 0x10 +#define RADIO_2055_MASTER_CNTRL1 0x11 +#define RADIO_2055_MASTER_CNTRL2 0x12 +#define RADIO_2055_PD_LGEN 0x13 +#define RADIO_2055_PD_PLL_TS 0x14 +#define RADIO_2055_PD_CORE1_LGBUF 0x15 +#define RADIO_2055_PD_CORE1_TX 0x16 +#define RADIO_2055_PD_CORE1_RXTX 0x17 +#define RADIO_2055_PD_CORE1_RSSI_MISC 0x18 +#define RADIO_2055_PD_CORE2_LGBUF 0x19 +#define RADIO_2055_PD_CORE2_TX 0x1a +#define RADIO_2055_PD_CORE2_RXTX 0x1b +#define RADIO_2055_PD_CORE2_RSSI_MISC 0x1c +#define RADIO_2055_PWRDET_LGEN 0x1d +#define RADIO_2055_PWRDET_LGBUF_CORE1 0x1e +#define RADIO_2055_PWRDET_RXTX_CORE1 0x1f +#define RADIO_2055_PWRDET_LGBUF_CORE2 0x20 +#define RADIO_2055_PWRDET_RXTX_CORE2 0x21 +#define RADIO_2055_RRCCAL_CNTRL_SPARE 0x22 +#define RADIO_2055_RRCCAL_N_OPT_SEL 0x23 +#define RADIO_2055_CAL_MISC 0x24 +#define RADIO_2055_CAL_COUNTER_OUT 0x25 +#define RADIO_2055_CAL_COUNTER_OUT2 0x26 +#define RADIO_2055_CAL_CVAR_CNTRL 0x27 +#define RADIO_2055_CAL_RVAR_CNTRL 0x28 +#define RADIO_2055_CAL_LPO_CNTRL 0x29 +#define RADIO_2055_CAL_TS 0x2a +#define RADIO_2055_CAL_RCCAL_READ_TS 0x2b +#define RADIO_2055_CAL_RCAL_READ_TS 0x2c +#define RADIO_2055_PAD_DRIVER 0x2d +#define RADIO_2055_XO_CNTRL1 0x2e +#define RADIO_2055_XO_CNTRL2 0x2f +#define RADIO_2055_XO_REGULATOR 0x30 +#define RADIO_2055_XO_MISC 0x31 +#define RADIO_2055_PLL_LF_C1 0x32 +#define RADIO_2055_PLL_CAL_VTH 0x33 +#define RADIO_2055_PLL_LF_C2 0x34 +#define RADIO_2055_PLL_REF 0x35 +#define RADIO_2055_PLL_LF_R1 0x36 +#define RADIO_2055_PLL_PFD_CP 0x37 +#define RADIO_2055_PLL_IDAC_CPOPAMP 0x38 +#define RADIO_2055_PLL_CP_REGULATOR 0x39 +#define RADIO_2055_PLL_RCAL 0x3a +#define RADIO_2055_RF_PLL_MOD0 0x3b +#define RADIO_2055_RF_PLL_MOD1 0x3c +#define RADIO_2055_RF_MMD_IDAC1 0x3d +#define RADIO_2055_RF_MMD_IDAC0 0x3e +#define RADIO_2055_RF_MMD_SPARE 0x3f +#define RADIO_2055_VCO_CAL1 0x40 +#define RADIO_2055_VCO_CAL2 0x41 +#define RADIO_2055_VCO_CAL3 0x42 +#define RADIO_2055_VCO_CAL4 0x43 +#define RADIO_2055_VCO_CAL5 0x44 +#define RADIO_2055_VCO_CAL6 0x45 +#define RADIO_2055_VCO_CAL7 0x46 +#define RADIO_2055_VCO_CAL8 0x47 +#define RADIO_2055_VCO_CAL9 0x48 +#define RADIO_2055_VCO_CAL10 0x49 +#define RADIO_2055_VCO_CAL11 0x4a +#define RADIO_2055_VCO_CAL12 0x4b +#define RADIO_2055_VCO_CAL13 0x4c +#define RADIO_2055_VCO_CAL14 0x4d +#define RADIO_2055_VCO_CAL15 0x4e +#define RADIO_2055_VCO_CAL16 0x4f +#define RADIO_2055_VCO_KVCO 0x50 +#define RADIO_2055_VCO_CAP_TAIL 0x51 +#define RADIO_2055_VCO_IDAC_VCO 0x52 +#define RADIO_2055_VCO_REGULATOR 0x53 +#define RADIO_2055_PLL_RF_VTH 0x54 +#define RADIO_2055_LGBUF_CEN_BUF 0x55 +#define RADIO_2055_LGEN_TUNE1 0x56 +#define RADIO_2055_LGEN_TUNE2 0x57 +#define RADIO_2055_LGEN_IDAC1 0x58 +#define RADIO_2055_LGEN_IDAC2 0x59 +#define RADIO_2055_LGEN_BIAS_CNT 0x5a +#define RADIO_2055_LGEN_BIAS_IDAC 0x5b +#define RADIO_2055_LGEN_RCAL 0x5c +#define RADIO_2055_LGEN_DIV 0x5d +#define RADIO_2055_LGEN_SPARE2 0x5e +#define RADIO_2055_CORE1_LGBUF_A_TUNE 0x5f +#define RADIO_2055_CORE1_LGBUF_G_TUNE 0x60 +#define RADIO_2055_CORE1_LGBUF_DIV 0x61 +#define RADIO_2055_CORE1_LGBUF_A_IDAC 0x62 +#define RADIO_2055_CORE1_LGBUF_G_IDAC 0x63 +#define RADIO_2055_CORE1_LGBUF_IDACFIL_OVR 0x64 +#define RADIO_2055_CORE1_LGBUF_SPARE 0x65 +#define RADIO_2055_CORE1_RXRF_SPC1 0x66 +#define RADIO_2055_CORE1_RXRF_REG1 0x67 +#define RADIO_2055_CORE1_RXRF_REG2 0x68 +#define RADIO_2055_CORE1_RXRF_RCAL 0x69 +#define RADIO_2055_CORE1_RXBB_BUFI_LPFCMP 0x6a +#define RADIO_2055_CORE1_RXBB_LPF 0x6b +#define RADIO_2055_CORE1_RXBB_MIDAC_HIPAS 0x6c +#define RADIO_2055_CORE1_RXBB_VGA1_IDAC 0x6d +#define RADIO_2055_CORE1_RXBB_VGA2_IDAC 0x6e +#define RADIO_2055_CORE1_RXBB_VGA3_IDAC 0x6f +#define RADIO_2055_CORE1_RXBB_BUFO_CTRL 0x70 +#define RADIO_2055_CORE1_RXBB_RCCAL_CTRL 0x71 +#define RADIO_2055_CORE1_RXBB_RSSI_CTRL1 0x72 +#define RADIO_2055_CORE1_RXBB_RSSI_CTRL2 0x73 +#define RADIO_2055_CORE1_RXBB_RSSI_CTRL3 0x74 +#define RADIO_2055_CORE1_RXBB_RSSI_CTRL4 0x75 +#define RADIO_2055_CORE1_RXBB_RSSI_CTRL5 0x76 +#define RADIO_2055_CORE1_RXBB_REGULATOR 0x77 +#define RADIO_2055_CORE1_RXBB_SPARE1 0x78 +#define RADIO_2055_CORE1_RXTXBB_RCAL 0x79 +#define RADIO_2055_CORE1_TXRF_SGM_PGA 0x7a +#define RADIO_2055_CORE1_TXRF_SGM_PAD 0x7b +#define RADIO_2055_CORE1_TXRF_CNTR_PGA1 0x7c +#define RADIO_2055_CORE1_TXRF_CNTR_PAD1 0x7d +#define RADIO_2055_CORE1_TX_RFPGA_IDAC 0x7e +#define RADIO_2055_CORE1_TX_PGA_PAD_TN 0x7f +#define RADIO_2055_CORE1_TX_PAD_IDAC1 0x80 +#define RADIO_2055_CORE1_TX_PAD_IDAC2 0x81 +#define RADIO_2055_CORE1_TX_MX_BGTRIM 0x82 +#define RADIO_2055_CORE1_TXRF_RCAL 0x83 +#define RADIO_2055_CORE1_TXRF_PAD_TSSI1 0x84 +#define RADIO_2055_CORE1_TXRF_PAD_TSSI2 0x85 +#define RADIO_2055_CORE1_TX_RF_SPARE 0x86 +#define RADIO_2055_CORE1_TXRF_IQCAL1 0x87 +#define RADIO_2055_CORE1_TXRF_IQCAL2 0x88 +#define RADIO_2055_CORE1_TXBB_RCCAL_CTRL 0x89 +#define RADIO_2055_CORE1_TXBB_LPF1 0x8a +#define RADIO_2055_CORE1_TX_VOS_CNCL 0x8b +#define RADIO_2055_CORE1_TX_LPF_MXGM_IDAC 0x8c +#define RADIO_2055_CORE1_TX_BB_MXGM 0x8d +#define RADIO_2055_CORE2_LGBUF_A_TUNE 0x8e +#define RADIO_2055_CORE2_LGBUF_G_TUNE 0x8f +#define RADIO_2055_CORE2_LGBUF_DIV 0x90 +#define RADIO_2055_CORE2_LGBUF_A_IDAC 0x91 +#define RADIO_2055_CORE2_LGBUF_G_IDAC 0x92 +#define RADIO_2055_CORE2_LGBUF_IDACFIL_OVR 0x93 +#define RADIO_2055_CORE2_LGBUF_SPARE 0x94 +#define RADIO_2055_CORE2_RXRF_SPC1 0x95 +#define RADIO_2055_CORE2_RXRF_REG1 0x96 +#define RADIO_2055_CORE2_RXRF_REG2 0x97 +#define RADIO_2055_CORE2_RXRF_RCAL 0x98 +#define RADIO_2055_CORE2_RXBB_BUFI_LPFCMP 0x99 +#define RADIO_2055_CORE2_RXBB_LPF 0x9a +#define RADIO_2055_CORE2_RXBB_MIDAC_HIPAS 0x9b +#define RADIO_2055_CORE2_RXBB_VGA1_IDAC 0x9c +#define RADIO_2055_CORE2_RXBB_VGA2_IDAC 0x9d +#define RADIO_2055_CORE2_RXBB_VGA3_IDAC 0x9e +#define RADIO_2055_CORE2_RXBB_BUFO_CTRL 0x9f +#define RADIO_2055_CORE2_RXBB_RCCAL_CTRL 0xa0 +#define RADIO_2055_CORE2_RXBB_RSSI_CTRL1 0xa1 +#define RADIO_2055_CORE2_RXBB_RSSI_CTRL2 0xa2 +#define RADIO_2055_CORE2_RXBB_RSSI_CTRL3 0xa3 +#define RADIO_2055_CORE2_RXBB_RSSI_CTRL4 0xa4 +#define RADIO_2055_CORE2_RXBB_RSSI_CTRL5 0xa5 +#define RADIO_2055_CORE2_RXBB_REGULATOR 0xa6 +#define RADIO_2055_CORE2_RXBB_SPARE1 0xa7 +#define RADIO_2055_CORE2_RXTXBB_RCAL 0xa8 +#define RADIO_2055_CORE2_TXRF_SGM_PGA 0xa9 +#define RADIO_2055_CORE2_TXRF_SGM_PAD 0xaa +#define RADIO_2055_CORE2_TXRF_CNTR_PGA1 0xab +#define RADIO_2055_CORE2_TXRF_CNTR_PAD1 0xac +#define RADIO_2055_CORE2_TX_RFPGA_IDAC 0xad +#define RADIO_2055_CORE2_TX_PGA_PAD_TN 0xae +#define RADIO_2055_CORE2_TX_PAD_IDAC1 0xaf +#define RADIO_2055_CORE2_TX_PAD_IDAC2 0xb0 +#define RADIO_2055_CORE2_TX_MX_BGTRIM 0xb1 +#define RADIO_2055_CORE2_TXRF_RCAL 0xb2 +#define RADIO_2055_CORE2_TXRF_PAD_TSSI1 0xb3 +#define RADIO_2055_CORE2_TXRF_PAD_TSSI2 0xb4 +#define RADIO_2055_CORE2_TX_RF_SPARE 0xb5 +#define RADIO_2055_CORE2_TXRF_IQCAL1 0xb6 +#define RADIO_2055_CORE2_TXRF_IQCAL2 0xb7 +#define RADIO_2055_CORE2_TXBB_RCCAL_CTRL 0xb8 +#define RADIO_2055_CORE2_TXBB_LPF1 0xb9 +#define RADIO_2055_CORE2_TX_VOS_CNCL 0xba +#define RADIO_2055_CORE2_TX_LPF_MXGM_IDAC 0xbb +#define RADIO_2055_CORE2_TX_BB_MXGM 0xbc +#define RADIO_2055_PRG_GC_HPVGA23_21 0xbd +#define RADIO_2055_PRG_GC_HPVGA23_22 0xbe +#define RADIO_2055_PRG_GC_HPVGA23_23 0xbf +#define RADIO_2055_PRG_GC_HPVGA23_24 0xc0 +#define RADIO_2055_PRG_GC_HPVGA23_25 0xc1 +#define RADIO_2055_PRG_GC_HPVGA23_26 0xc2 +#define RADIO_2055_PRG_GC_HPVGA23_27 0xc3 +#define RADIO_2055_PRG_GC_HPVGA23_28 0xc4 +#define RADIO_2055_PRG_GC_HPVGA23_29 0xc5 +#define RADIO_2055_PRG_GC_HPVGA23_30 0xc6 +#define RADIO_2055_CORE1_LNA_GAINBST 0xcd +#define RADIO_2055_CORE1_B0_NBRSSI_VCM 0xd2 +#define RADIO_2055_CORE1_GEN_SPARE2 0xd6 +#define RADIO_2055_CORE2_LNA_GAINBST 0xd9 +#define RADIO_2055_CORE2_B0_NBRSSI_VCM 0xde +#define RADIO_2055_CORE2_GEN_SPARE2 0xe2 + +#define RADIO_2055_GAINBST_GAIN_DB 6 +#define RADIO_2055_GAINBST_CODE 0x6 + +#define RADIO_2055_JTAGCTRL_MASK 0x04 +#define RADIO_2055_JTAGSYNC_MASK 0x08 +#define RADIO_2055_RRCAL_START 0x40 +#define RADIO_2055_RRCAL_RST_N 0x01 +#define RADIO_2055_CAL_LPO_ENABLE 0x80 +#define RADIO_2055_RCAL_DONE 0x80 +#define RADIO_2055_NBRSSI_VCM_I_MASK 0x03 +#define RADIO_2055_NBRSSI_VCM_I_SHIFT 0x00 +#define RADIO_2055_NBRSSI_VCM_Q_MASK 0x03 +#define RADIO_2055_NBRSSI_VCM_Q_SHIFT 0x00 +#define RADIO_2055_WBRSSI_VCM_IQ_MASK 0x0c +#define RADIO_2055_WBRSSI_VCM_IQ_SHIFT 0x02 +#define RADIO_2055_NBRSSI_PD 0x01 +#define RADIO_2055_WBRSSI_G1_PD 0x04 +#define RADIO_2055_WBRSSI_G2_PD 0x02 +#define RADIO_2055_NBRSSI_SEL 0x01 +#define RADIO_2055_WBRSSI_G1_SEL 0x04 +#define RADIO_2055_WBRSSI_G2_SEL 0x02 +#define RADIO_2055_COUPLE_RX_MASK 0x01 +#define RADIO_2055_COUPLE_TX_MASK 0x02 +#define RADIO_2055_GAINBST_DISABLE 0x02 +#define RADIO_2055_GAINBST_VAL_MASK 0x07 +#define RADIO_2055_RXMX_GC_MASK 0x0c + +#define RADIO_MIMO_CORESEL_OFF 0x0 +#define RADIO_MIMO_CORESEL_CORE1 0x1 +#define RADIO_MIMO_CORESEL_CORE2 0x2 +#define RADIO_MIMO_CORESEL_CORE3 0x3 +#define RADIO_MIMO_CORESEL_CORE4 0x4 +#define RADIO_MIMO_CORESEL_ALLRX 0x5 +#define RADIO_MIMO_CORESEL_ALLTX 0x6 +#define RADIO_MIMO_CORESEL_ALLRXTX 0x7 + +#define RADIO_2064_READ_OFF 0x200 + +#define RADIO_2064_REG000 0x0 +#define RADIO_2064_REG001 0x1 +#define RADIO_2064_REG002 0x2 +#define RADIO_2064_REG003 0x3 +#define RADIO_2064_REG004 0x4 +#define RADIO_2064_REG005 0x5 +#define RADIO_2064_REG006 0x6 +#define RADIO_2064_REG007 0x7 +#define RADIO_2064_REG008 0x8 +#define RADIO_2064_REG009 0x9 +#define RADIO_2064_REG00A 0xa +#define RADIO_2064_REG00B 0xb +#define RADIO_2064_REG00C 0xc +#define RADIO_2064_REG00D 0xd +#define RADIO_2064_REG00E 0xe +#define RADIO_2064_REG00F 0xf +#define RADIO_2064_REG010 0x10 +#define RADIO_2064_REG011 0x11 +#define RADIO_2064_REG012 0x12 +#define RADIO_2064_REG013 0x13 +#define RADIO_2064_REG014 0x14 +#define RADIO_2064_REG015 0x15 +#define RADIO_2064_REG016 0x16 +#define RADIO_2064_REG017 0x17 +#define RADIO_2064_REG018 0x18 +#define RADIO_2064_REG019 0x19 +#define RADIO_2064_REG01A 0x1a +#define RADIO_2064_REG01B 0x1b +#define RADIO_2064_REG01C 0x1c +#define RADIO_2064_REG01D 0x1d +#define RADIO_2064_REG01E 0x1e +#define RADIO_2064_REG01F 0x1f +#define RADIO_2064_REG020 0x20 +#define RADIO_2064_REG021 0x21 +#define RADIO_2064_REG022 0x22 +#define RADIO_2064_REG023 0x23 +#define RADIO_2064_REG024 0x24 +#define RADIO_2064_REG025 0x25 +#define RADIO_2064_REG026 0x26 +#define RADIO_2064_REG027 0x27 +#define RADIO_2064_REG028 0x28 +#define RADIO_2064_REG029 0x29 +#define RADIO_2064_REG02A 0x2a +#define RADIO_2064_REG02B 0x2b +#define RADIO_2064_REG02C 0x2c +#define RADIO_2064_REG02D 0x2d +#define RADIO_2064_REG02E 0x2e +#define RADIO_2064_REG02F 0x2f +#define RADIO_2064_REG030 0x30 +#define RADIO_2064_REG031 0x31 +#define RADIO_2064_REG032 0x32 +#define RADIO_2064_REG033 0x33 +#define RADIO_2064_REG034 0x34 +#define RADIO_2064_REG035 0x35 +#define RADIO_2064_REG036 0x36 +#define RADIO_2064_REG037 0x37 +#define RADIO_2064_REG038 0x38 +#define RADIO_2064_REG039 0x39 +#define RADIO_2064_REG03A 0x3a +#define RADIO_2064_REG03B 0x3b +#define RADIO_2064_REG03C 0x3c +#define RADIO_2064_REG03D 0x3d +#define RADIO_2064_REG03E 0x3e +#define RADIO_2064_REG03F 0x3f +#define RADIO_2064_REG040 0x40 +#define RADIO_2064_REG041 0x41 +#define RADIO_2064_REG042 0x42 +#define RADIO_2064_REG043 0x43 +#define RADIO_2064_REG044 0x44 +#define RADIO_2064_REG045 0x45 +#define RADIO_2064_REG046 0x46 +#define RADIO_2064_REG047 0x47 +#define RADIO_2064_REG048 0x48 +#define RADIO_2064_REG049 0x49 +#define RADIO_2064_REG04A 0x4a +#define RADIO_2064_REG04B 0x4b +#define RADIO_2064_REG04C 0x4c +#define RADIO_2064_REG04D 0x4d +#define RADIO_2064_REG04E 0x4e +#define RADIO_2064_REG04F 0x4f +#define RADIO_2064_REG050 0x50 +#define RADIO_2064_REG051 0x51 +#define RADIO_2064_REG052 0x52 +#define RADIO_2064_REG053 0x53 +#define RADIO_2064_REG054 0x54 +#define RADIO_2064_REG055 0x55 +#define RADIO_2064_REG056 0x56 +#define RADIO_2064_REG057 0x57 +#define RADIO_2064_REG058 0x58 +#define RADIO_2064_REG059 0x59 +#define RADIO_2064_REG05A 0x5a +#define RADIO_2064_REG05B 0x5b +#define RADIO_2064_REG05C 0x5c +#define RADIO_2064_REG05D 0x5d +#define RADIO_2064_REG05E 0x5e +#define RADIO_2064_REG05F 0x5f +#define RADIO_2064_REG060 0x60 +#define RADIO_2064_REG061 0x61 +#define RADIO_2064_REG062 0x62 +#define RADIO_2064_REG063 0x63 +#define RADIO_2064_REG064 0x64 +#define RADIO_2064_REG065 0x65 +#define RADIO_2064_REG066 0x66 +#define RADIO_2064_REG067 0x67 +#define RADIO_2064_REG068 0x68 +#define RADIO_2064_REG069 0x69 +#define RADIO_2064_REG06A 0x6a +#define RADIO_2064_REG06B 0x6b +#define RADIO_2064_REG06C 0x6c +#define RADIO_2064_REG06D 0x6d +#define RADIO_2064_REG06E 0x6e +#define RADIO_2064_REG06F 0x6f +#define RADIO_2064_REG070 0x70 +#define RADIO_2064_REG071 0x71 +#define RADIO_2064_REG072 0x72 +#define RADIO_2064_REG073 0x73 +#define RADIO_2064_REG074 0x74 +#define RADIO_2064_REG075 0x75 +#define RADIO_2064_REG076 0x76 +#define RADIO_2064_REG077 0x77 +#define RADIO_2064_REG078 0x78 +#define RADIO_2064_REG079 0x79 +#define RADIO_2064_REG07A 0x7a +#define RADIO_2064_REG07B 0x7b +#define RADIO_2064_REG07C 0x7c +#define RADIO_2064_REG07D 0x7d +#define RADIO_2064_REG07E 0x7e +#define RADIO_2064_REG07F 0x7f +#define RADIO_2064_REG080 0x80 +#define RADIO_2064_REG081 0x81 +#define RADIO_2064_REG082 0x82 +#define RADIO_2064_REG083 0x83 +#define RADIO_2064_REG084 0x84 +#define RADIO_2064_REG085 0x85 +#define RADIO_2064_REG086 0x86 +#define RADIO_2064_REG087 0x87 +#define RADIO_2064_REG088 0x88 +#define RADIO_2064_REG089 0x89 +#define RADIO_2064_REG08A 0x8a +#define RADIO_2064_REG08B 0x8b +#define RADIO_2064_REG08C 0x8c +#define RADIO_2064_REG08D 0x8d +#define RADIO_2064_REG08E 0x8e +#define RADIO_2064_REG08F 0x8f +#define RADIO_2064_REG090 0x90 +#define RADIO_2064_REG091 0x91 +#define RADIO_2064_REG092 0x92 +#define RADIO_2064_REG093 0x93 +#define RADIO_2064_REG094 0x94 +#define RADIO_2064_REG095 0x95 +#define RADIO_2064_REG096 0x96 +#define RADIO_2064_REG097 0x97 +#define RADIO_2064_REG098 0x98 +#define RADIO_2064_REG099 0x99 +#define RADIO_2064_REG09A 0x9a +#define RADIO_2064_REG09B 0x9b +#define RADIO_2064_REG09C 0x9c +#define RADIO_2064_REG09D 0x9d +#define RADIO_2064_REG09E 0x9e +#define RADIO_2064_REG09F 0x9f +#define RADIO_2064_REG0A0 0xa0 +#define RADIO_2064_REG0A1 0xa1 +#define RADIO_2064_REG0A2 0xa2 +#define RADIO_2064_REG0A3 0xa3 +#define RADIO_2064_REG0A4 0xa4 +#define RADIO_2064_REG0A5 0xa5 +#define RADIO_2064_REG0A6 0xa6 +#define RADIO_2064_REG0A7 0xa7 +#define RADIO_2064_REG0A8 0xa8 +#define RADIO_2064_REG0A9 0xa9 +#define RADIO_2064_REG0AA 0xaa +#define RADIO_2064_REG0AB 0xab +#define RADIO_2064_REG0AC 0xac +#define RADIO_2064_REG0AD 0xad +#define RADIO_2064_REG0AE 0xae +#define RADIO_2064_REG0AF 0xaf +#define RADIO_2064_REG0B0 0xb0 +#define RADIO_2064_REG0B1 0xb1 +#define RADIO_2064_REG0B2 0xb2 +#define RADIO_2064_REG0B3 0xb3 +#define RADIO_2064_REG0B4 0xb4 +#define RADIO_2064_REG0B5 0xb5 +#define RADIO_2064_REG0B6 0xb6 +#define RADIO_2064_REG0B7 0xb7 +#define RADIO_2064_REG0B8 0xb8 +#define RADIO_2064_REG0B9 0xb9 +#define RADIO_2064_REG0BA 0xba +#define RADIO_2064_REG0BB 0xbb +#define RADIO_2064_REG0BC 0xbc +#define RADIO_2064_REG0BD 0xbd +#define RADIO_2064_REG0BE 0xbe +#define RADIO_2064_REG0BF 0xbf +#define RADIO_2064_REG0C0 0xc0 +#define RADIO_2064_REG0C1 0xc1 +#define RADIO_2064_REG0C2 0xc2 +#define RADIO_2064_REG0C3 0xc3 +#define RADIO_2064_REG0C4 0xc4 +#define RADIO_2064_REG0C5 0xc5 +#define RADIO_2064_REG0C6 0xc6 +#define RADIO_2064_REG0C7 0xc7 +#define RADIO_2064_REG0C8 0xc8 +#define RADIO_2064_REG0C9 0xc9 +#define RADIO_2064_REG0CA 0xca +#define RADIO_2064_REG0CB 0xcb +#define RADIO_2064_REG0CC 0xcc +#define RADIO_2064_REG0CD 0xcd +#define RADIO_2064_REG0CE 0xce +#define RADIO_2064_REG0CF 0xcf +#define RADIO_2064_REG0D0 0xd0 +#define RADIO_2064_REG0D1 0xd1 +#define RADIO_2064_REG0D2 0xd2 +#define RADIO_2064_REG0D3 0xd3 +#define RADIO_2064_REG0D4 0xd4 +#define RADIO_2064_REG0D5 0xd5 +#define RADIO_2064_REG0D6 0xd6 +#define RADIO_2064_REG0D7 0xd7 +#define RADIO_2064_REG0D8 0xd8 +#define RADIO_2064_REG0D9 0xd9 +#define RADIO_2064_REG0DA 0xda +#define RADIO_2064_REG0DB 0xdb +#define RADIO_2064_REG0DC 0xdc +#define RADIO_2064_REG0DD 0xdd +#define RADIO_2064_REG0DE 0xde +#define RADIO_2064_REG0DF 0xdf +#define RADIO_2064_REG0E0 0xe0 +#define RADIO_2064_REG0E1 0xe1 +#define RADIO_2064_REG0E2 0xe2 +#define RADIO_2064_REG0E3 0xe3 +#define RADIO_2064_REG0E4 0xe4 +#define RADIO_2064_REG0E5 0xe5 +#define RADIO_2064_REG0E6 0xe6 +#define RADIO_2064_REG0E7 0xe7 +#define RADIO_2064_REG0E8 0xe8 +#define RADIO_2064_REG0E9 0xe9 +#define RADIO_2064_REG0EA 0xea +#define RADIO_2064_REG0EB 0xeb +#define RADIO_2064_REG0EC 0xec +#define RADIO_2064_REG0ED 0xed +#define RADIO_2064_REG0EE 0xee +#define RADIO_2064_REG0EF 0xef +#define RADIO_2064_REG0F0 0xf0 +#define RADIO_2064_REG0F1 0xf1 +#define RADIO_2064_REG0F2 0xf2 +#define RADIO_2064_REG0F3 0xf3 +#define RADIO_2064_REG0F4 0xf4 +#define RADIO_2064_REG0F5 0xf5 +#define RADIO_2064_REG0F6 0xf6 +#define RADIO_2064_REG0F7 0xf7 +#define RADIO_2064_REG0F8 0xf8 +#define RADIO_2064_REG0F9 0xf9 +#define RADIO_2064_REG0FA 0xfa +#define RADIO_2064_REG0FB 0xfb +#define RADIO_2064_REG0FC 0xfc +#define RADIO_2064_REG0FD 0xfd +#define RADIO_2064_REG0FE 0xfe +#define RADIO_2064_REG0FF 0xff +#define RADIO_2064_REG100 0x100 +#define RADIO_2064_REG101 0x101 +#define RADIO_2064_REG102 0x102 +#define RADIO_2064_REG103 0x103 +#define RADIO_2064_REG104 0x104 +#define RADIO_2064_REG105 0x105 +#define RADIO_2064_REG106 0x106 +#define RADIO_2064_REG107 0x107 +#define RADIO_2064_REG108 0x108 +#define RADIO_2064_REG109 0x109 +#define RADIO_2064_REG10A 0x10a +#define RADIO_2064_REG10B 0x10b +#define RADIO_2064_REG10C 0x10c +#define RADIO_2064_REG10D 0x10d +#define RADIO_2064_REG10E 0x10e +#define RADIO_2064_REG10F 0x10f +#define RADIO_2064_REG110 0x110 +#define RADIO_2064_REG111 0x111 +#define RADIO_2064_REG112 0x112 +#define RADIO_2064_REG113 0x113 +#define RADIO_2064_REG114 0x114 +#define RADIO_2064_REG115 0x115 +#define RADIO_2064_REG116 0x116 +#define RADIO_2064_REG117 0x117 +#define RADIO_2064_REG118 0x118 +#define RADIO_2064_REG119 0x119 +#define RADIO_2064_REG11A 0x11a +#define RADIO_2064_REG11B 0x11b +#define RADIO_2064_REG11C 0x11c +#define RADIO_2064_REG11D 0x11d +#define RADIO_2064_REG11E 0x11e +#define RADIO_2064_REG11F 0x11f +#define RADIO_2064_REG120 0x120 +#define RADIO_2064_REG121 0x121 +#define RADIO_2064_REG122 0x122 +#define RADIO_2064_REG123 0x123 +#define RADIO_2064_REG124 0x124 +#define RADIO_2064_REG125 0x125 +#define RADIO_2064_REG126 0x126 +#define RADIO_2064_REG127 0x127 +#define RADIO_2064_REG128 0x128 +#define RADIO_2064_REG129 0x129 +#define RADIO_2064_REG12A 0x12a +#define RADIO_2064_REG12B 0x12b +#define RADIO_2064_REG12C 0x12c +#define RADIO_2064_REG12D 0x12d +#define RADIO_2064_REG12E 0x12e +#define RADIO_2064_REG12F 0x12f +#define RADIO_2064_REG130 0x130 + +#define RADIO_2056_SYN (0x0 << 12) +#define RADIO_2056_TX0 (0x2 << 12) +#define RADIO_2056_TX1 (0x3 << 12) +#define RADIO_2056_RX0 (0x6 << 12) +#define RADIO_2056_RX1 (0x7 << 12) +#define RADIO_2056_ALLTX (0xe << 12) +#define RADIO_2056_ALLRX (0xf << 12) + +#define RADIO_2056_SYN_RESERVED_ADDR0 0x0 +#define RADIO_2056_SYN_IDCODE 0x1 +#define RADIO_2056_SYN_RESERVED_ADDR2 0x2 +#define RADIO_2056_SYN_RESERVED_ADDR3 0x3 +#define RADIO_2056_SYN_RESERVED_ADDR4 0x4 +#define RADIO_2056_SYN_RESERVED_ADDR5 0x5 +#define RADIO_2056_SYN_RESERVED_ADDR6 0x6 +#define RADIO_2056_SYN_RESERVED_ADDR7 0x7 +#define RADIO_2056_SYN_COM_CTRL 0x8 +#define RADIO_2056_SYN_COM_PU 0x9 +#define RADIO_2056_SYN_COM_OVR 0xa +#define RADIO_2056_SYN_COM_RESET 0xb +#define RADIO_2056_SYN_COM_RCAL 0xc +#define RADIO_2056_SYN_COM_RC_RXLPF 0xd +#define RADIO_2056_SYN_COM_RC_TXLPF 0xe +#define RADIO_2056_SYN_COM_RC_RXHPF 0xf +#define RADIO_2056_SYN_RESERVED_ADDR16 0x10 +#define RADIO_2056_SYN_RESERVED_ADDR17 0x11 +#define RADIO_2056_SYN_RESERVED_ADDR18 0x12 +#define RADIO_2056_SYN_RESERVED_ADDR19 0x13 +#define RADIO_2056_SYN_RESERVED_ADDR20 0x14 +#define RADIO_2056_SYN_RESERVED_ADDR21 0x15 +#define RADIO_2056_SYN_RESERVED_ADDR22 0x16 +#define RADIO_2056_SYN_RESERVED_ADDR23 0x17 +#define RADIO_2056_SYN_RESERVED_ADDR24 0x18 +#define RADIO_2056_SYN_RESERVED_ADDR25 0x19 +#define RADIO_2056_SYN_RESERVED_ADDR26 0x1a +#define RADIO_2056_SYN_RESERVED_ADDR27 0x1b +#define RADIO_2056_SYN_RESERVED_ADDR28 0x1c +#define RADIO_2056_SYN_RESERVED_ADDR29 0x1d +#define RADIO_2056_SYN_RESERVED_ADDR30 0x1e +#define RADIO_2056_SYN_RESERVED_ADDR31 0x1f +#define RADIO_2056_SYN_GPIO_MASTER1 0x20 +#define RADIO_2056_SYN_GPIO_MASTER2 0x21 +#define RADIO_2056_SYN_TOPBIAS_MASTER 0x22 +#define RADIO_2056_SYN_TOPBIAS_RCAL 0x23 +#define RADIO_2056_SYN_AFEREG 0x24 +#define RADIO_2056_SYN_TEMPPROCSENSE 0x25 +#define RADIO_2056_SYN_TEMPPROCSENSEIDAC 0x26 +#define RADIO_2056_SYN_TEMPPROCSENSERCAL 0x27 +#define RADIO_2056_SYN_LPO 0x28 +#define RADIO_2056_SYN_VDDCAL_MASTER 0x29 +#define RADIO_2056_SYN_VDDCAL_IDAC 0x2a +#define RADIO_2056_SYN_VDDCAL_STATUS 0x2b +#define RADIO_2056_SYN_RCAL_MASTER 0x2c +#define RADIO_2056_SYN_RCAL_CODE_OUT 0x2d +#define RADIO_2056_SYN_RCCAL_CTRL0 0x2e +#define RADIO_2056_SYN_RCCAL_CTRL1 0x2f +#define RADIO_2056_SYN_RCCAL_CTRL2 0x30 +#define RADIO_2056_SYN_RCCAL_CTRL3 0x31 +#define RADIO_2056_SYN_RCCAL_CTRL4 0x32 +#define RADIO_2056_SYN_RCCAL_CTRL5 0x33 +#define RADIO_2056_SYN_RCCAL_CTRL6 0x34 +#define RADIO_2056_SYN_RCCAL_CTRL7 0x35 +#define RADIO_2056_SYN_RCCAL_CTRL8 0x36 +#define RADIO_2056_SYN_RCCAL_CTRL9 0x37 +#define RADIO_2056_SYN_RCCAL_CTRL10 0x38 +#define RADIO_2056_SYN_RCCAL_CTRL11 0x39 +#define RADIO_2056_SYN_ZCAL_SPARE1 0x3a +#define RADIO_2056_SYN_ZCAL_SPARE2 0x3b +#define RADIO_2056_SYN_PLL_MAST1 0x3c +#define RADIO_2056_SYN_PLL_MAST2 0x3d +#define RADIO_2056_SYN_PLL_MAST3 0x3e +#define RADIO_2056_SYN_PLL_BIAS_RESET 0x3f +#define RADIO_2056_SYN_PLL_XTAL0 0x40 +#define RADIO_2056_SYN_PLL_XTAL1 0x41 +#define RADIO_2056_SYN_PLL_XTAL3 0x42 +#define RADIO_2056_SYN_PLL_XTAL4 0x43 +#define RADIO_2056_SYN_PLL_XTAL5 0x44 +#define RADIO_2056_SYN_PLL_XTAL6 0x45 +#define RADIO_2056_SYN_PLL_REFDIV 0x46 +#define RADIO_2056_SYN_PLL_PFD 0x47 +#define RADIO_2056_SYN_PLL_CP1 0x48 +#define RADIO_2056_SYN_PLL_CP2 0x49 +#define RADIO_2056_SYN_PLL_CP3 0x4a +#define RADIO_2056_SYN_PLL_LOOPFILTER1 0x4b +#define RADIO_2056_SYN_PLL_LOOPFILTER2 0x4c +#define RADIO_2056_SYN_PLL_LOOPFILTER3 0x4d +#define RADIO_2056_SYN_PLL_LOOPFILTER4 0x4e +#define RADIO_2056_SYN_PLL_LOOPFILTER5 0x4f +#define RADIO_2056_SYN_PLL_MMD1 0x50 +#define RADIO_2056_SYN_PLL_MMD2 0x51 +#define RADIO_2056_SYN_PLL_VCO1 0x52 +#define RADIO_2056_SYN_PLL_VCO2 0x53 +#define RADIO_2056_SYN_PLL_MONITOR1 0x54 +#define RADIO_2056_SYN_PLL_MONITOR2 0x55 +#define RADIO_2056_SYN_PLL_VCOCAL1 0x56 +#define RADIO_2056_SYN_PLL_VCOCAL2 0x57 +#define RADIO_2056_SYN_PLL_VCOCAL4 0x58 +#define RADIO_2056_SYN_PLL_VCOCAL5 0x59 +#define RADIO_2056_SYN_PLL_VCOCAL6 0x5a +#define RADIO_2056_SYN_PLL_VCOCAL7 0x5b +#define RADIO_2056_SYN_PLL_VCOCAL8 0x5c +#define RADIO_2056_SYN_PLL_VCOCAL9 0x5d +#define RADIO_2056_SYN_PLL_VCOCAL10 0x5e +#define RADIO_2056_SYN_PLL_VCOCAL11 0x5f +#define RADIO_2056_SYN_PLL_VCOCAL12 0x60 +#define RADIO_2056_SYN_PLL_VCOCAL13 0x61 +#define RADIO_2056_SYN_PLL_VREG 0x62 +#define RADIO_2056_SYN_PLL_STATUS1 0x63 +#define RADIO_2056_SYN_PLL_STATUS2 0x64 +#define RADIO_2056_SYN_PLL_STATUS3 0x65 +#define RADIO_2056_SYN_LOGEN_PU0 0x66 +#define RADIO_2056_SYN_LOGEN_PU1 0x67 +#define RADIO_2056_SYN_LOGEN_PU2 0x68 +#define RADIO_2056_SYN_LOGEN_PU3 0x69 +#define RADIO_2056_SYN_LOGEN_PU5 0x6a +#define RADIO_2056_SYN_LOGEN_PU6 0x6b +#define RADIO_2056_SYN_LOGEN_PU7 0x6c +#define RADIO_2056_SYN_LOGEN_PU8 0x6d +#define RADIO_2056_SYN_LOGEN_BIAS_RESET 0x6e +#define RADIO_2056_SYN_LOGEN_RCCR1 0x6f +#define RADIO_2056_SYN_LOGEN_VCOBUF1 0x70 +#define RADIO_2056_SYN_LOGEN_MIXER1 0x71 +#define RADIO_2056_SYN_LOGEN_MIXER2 0x72 +#define RADIO_2056_SYN_LOGEN_BUF1 0x73 +#define RADIO_2056_SYN_LOGENBUF2 0x74 +#define RADIO_2056_SYN_LOGEN_BUF3 0x75 +#define RADIO_2056_SYN_LOGEN_BUF4 0x76 +#define RADIO_2056_SYN_LOGEN_DIV1 0x77 +#define RADIO_2056_SYN_LOGEN_DIV2 0x78 +#define RADIO_2056_SYN_LOGEN_DIV3 0x79 +#define RADIO_2056_SYN_LOGEN_ACL1 0x7a +#define RADIO_2056_SYN_LOGEN_ACL2 0x7b +#define RADIO_2056_SYN_LOGEN_ACL3 0x7c +#define RADIO_2056_SYN_LOGEN_ACL4 0x7d +#define RADIO_2056_SYN_LOGEN_ACL5 0x7e +#define RADIO_2056_SYN_LOGEN_ACL6 0x7f +#define RADIO_2056_SYN_LOGEN_ACLOUT 0x80 +#define RADIO_2056_SYN_LOGEN_ACLCAL1 0x81 +#define RADIO_2056_SYN_LOGEN_ACLCAL2 0x82 +#define RADIO_2056_SYN_LOGEN_ACLCAL3 0x83 +#define RADIO_2056_SYN_CALEN 0x84 +#define RADIO_2056_SYN_LOGEN_PEAKDET1 0x85 +#define RADIO_2056_SYN_LOGEN_CORE_ACL_OVR 0x86 +#define RADIO_2056_SYN_LOGEN_RX_DIFF_ACL_OVR 0x87 +#define RADIO_2056_SYN_LOGEN_TX_DIFF_ACL_OVR 0x88 +#define RADIO_2056_SYN_LOGEN_RX_CMOS_ACL_OVR 0x89 +#define RADIO_2056_SYN_LOGEN_TX_CMOS_ACL_OVR 0x8a +#define RADIO_2056_SYN_LOGEN_VCOBUF2 0x8b +#define RADIO_2056_SYN_LOGEN_MIXER3 0x8c +#define RADIO_2056_SYN_LOGEN_BUF5 0x8d +#define RADIO_2056_SYN_LOGEN_BUF6 0x8e +#define RADIO_2056_SYN_LOGEN_CBUFRX1 0x8f +#define RADIO_2056_SYN_LOGEN_CBUFRX2 0x90 +#define RADIO_2056_SYN_LOGEN_CBUFRX3 0x91 +#define RADIO_2056_SYN_LOGEN_CBUFRX4 0x92 +#define RADIO_2056_SYN_LOGEN_CBUFTX1 0x93 +#define RADIO_2056_SYN_LOGEN_CBUFTX2 0x94 +#define RADIO_2056_SYN_LOGEN_CBUFTX3 0x95 +#define RADIO_2056_SYN_LOGEN_CBUFTX4 0x96 +#define RADIO_2056_SYN_LOGEN_CMOSRX1 0x97 +#define RADIO_2056_SYN_LOGEN_CMOSRX2 0x98 +#define RADIO_2056_SYN_LOGEN_CMOSRX3 0x99 +#define RADIO_2056_SYN_LOGEN_CMOSRX4 0x9a +#define RADIO_2056_SYN_LOGEN_CMOSTX1 0x9b +#define RADIO_2056_SYN_LOGEN_CMOSTX2 0x9c +#define RADIO_2056_SYN_LOGEN_CMOSTX3 0x9d +#define RADIO_2056_SYN_LOGEN_CMOSTX4 0x9e +#define RADIO_2056_SYN_LOGEN_VCOBUF2_OVRVAL 0x9f +#define RADIO_2056_SYN_LOGEN_MIXER3_OVRVAL 0xa0 +#define RADIO_2056_SYN_LOGEN_BUF5_OVRVAL 0xa1 +#define RADIO_2056_SYN_LOGEN_BUF6_OVRVAL 0xa2 +#define RADIO_2056_SYN_LOGEN_CBUFRX1_OVRVAL 0xa3 +#define RADIO_2056_SYN_LOGEN_CBUFRX2_OVRVAL 0xa4 +#define RADIO_2056_SYN_LOGEN_CBUFRX3_OVRVAL 0xa5 +#define RADIO_2056_SYN_LOGEN_CBUFRX4_OVRVAL 0xa6 +#define RADIO_2056_SYN_LOGEN_CBUFTX1_OVRVAL 0xa7 +#define RADIO_2056_SYN_LOGEN_CBUFTX2_OVRVAL 0xa8 +#define RADIO_2056_SYN_LOGEN_CBUFTX3_OVRVAL 0xa9 +#define RADIO_2056_SYN_LOGEN_CBUFTX4_OVRVAL 0xaa +#define RADIO_2056_SYN_LOGEN_CMOSRX1_OVRVAL 0xab +#define RADIO_2056_SYN_LOGEN_CMOSRX2_OVRVAL 0xac +#define RADIO_2056_SYN_LOGEN_CMOSRX3_OVRVAL 0xad +#define RADIO_2056_SYN_LOGEN_CMOSRX4_OVRVAL 0xae +#define RADIO_2056_SYN_LOGEN_CMOSTX1_OVRVAL 0xaf +#define RADIO_2056_SYN_LOGEN_CMOSTX2_OVRVAL 0xb0 +#define RADIO_2056_SYN_LOGEN_CMOSTX3_OVRVAL 0xb1 +#define RADIO_2056_SYN_LOGEN_CMOSTX4_OVRVAL 0xb2 +#define RADIO_2056_SYN_LOGEN_ACL_WAITCNT 0xb3 +#define RADIO_2056_SYN_LOGEN_CORE_CALVALID 0xb4 +#define RADIO_2056_SYN_LOGEN_RX_CMOS_CALVALID 0xb5 +#define RADIO_2056_SYN_LOGEN_TX_CMOS_VALID 0xb6 + +#define RADIO_2056_TX_RESERVED_ADDR0 0x0 +#define RADIO_2056_TX_IDCODE 0x1 +#define RADIO_2056_TX_RESERVED_ADDR2 0x2 +#define RADIO_2056_TX_RESERVED_ADDR3 0x3 +#define RADIO_2056_TX_RESERVED_ADDR4 0x4 +#define RADIO_2056_TX_RESERVED_ADDR5 0x5 +#define RADIO_2056_TX_RESERVED_ADDR6 0x6 +#define RADIO_2056_TX_RESERVED_ADDR7 0x7 +#define RADIO_2056_TX_COM_CTRL 0x8 +#define RADIO_2056_TX_COM_PU 0x9 +#define RADIO_2056_TX_COM_OVR 0xa +#define RADIO_2056_TX_COM_RESET 0xb +#define RADIO_2056_TX_COM_RCAL 0xc +#define RADIO_2056_TX_COM_RC_RXLPF 0xd +#define RADIO_2056_TX_COM_RC_TXLPF 0xe +#define RADIO_2056_TX_COM_RC_RXHPF 0xf +#define RADIO_2056_TX_RESERVED_ADDR16 0x10 +#define RADIO_2056_TX_RESERVED_ADDR17 0x11 +#define RADIO_2056_TX_RESERVED_ADDR18 0x12 +#define RADIO_2056_TX_RESERVED_ADDR19 0x13 +#define RADIO_2056_TX_RESERVED_ADDR20 0x14 +#define RADIO_2056_TX_RESERVED_ADDR21 0x15 +#define RADIO_2056_TX_RESERVED_ADDR22 0x16 +#define RADIO_2056_TX_RESERVED_ADDR23 0x17 +#define RADIO_2056_TX_RESERVED_ADDR24 0x18 +#define RADIO_2056_TX_RESERVED_ADDR25 0x19 +#define RADIO_2056_TX_RESERVED_ADDR26 0x1a +#define RADIO_2056_TX_RESERVED_ADDR27 0x1b +#define RADIO_2056_TX_RESERVED_ADDR28 0x1c +#define RADIO_2056_TX_RESERVED_ADDR29 0x1d +#define RADIO_2056_TX_RESERVED_ADDR30 0x1e +#define RADIO_2056_TX_RESERVED_ADDR31 0x1f +#define RADIO_2056_TX_IQCAL_GAIN_BW 0x20 +#define RADIO_2056_TX_LOFT_FINE_I 0x21 +#define RADIO_2056_TX_LOFT_FINE_Q 0x22 +#define RADIO_2056_TX_LOFT_COARSE_I 0x23 +#define RADIO_2056_TX_LOFT_COARSE_Q 0x24 +#define RADIO_2056_TX_TX_COM_MASTER1 0x25 +#define RADIO_2056_TX_TX_COM_MASTER2 0x26 +#define RADIO_2056_TX_RXIQCAL_TXMUX 0x27 +#define RADIO_2056_TX_TX_SSI_MASTER 0x28 +#define RADIO_2056_TX_IQCAL_VCM_HG 0x29 +#define RADIO_2056_TX_IQCAL_IDAC 0x2a +#define RADIO_2056_TX_TSSI_VCM 0x2b +#define RADIO_2056_TX_TX_AMP_DET 0x2c +#define RADIO_2056_TX_TX_SSI_MUX 0x2d +#define RADIO_2056_TX_TSSIA 0x2e +#define RADIO_2056_TX_TSSIG 0x2f +#define RADIO_2056_TX_TSSI_MISC1 0x30 +#define RADIO_2056_TX_TSSI_MISC2 0x31 +#define RADIO_2056_TX_TSSI_MISC3 0x32 +#define RADIO_2056_TX_PA_SPARE1 0x33 +#define RADIO_2056_TX_PA_SPARE2 0x34 +#define RADIO_2056_TX_INTPAA_MASTER 0x35 +#define RADIO_2056_TX_INTPAA_GAIN 0x36 +#define RADIO_2056_TX_INTPAA_BOOST_TUNE 0x37 +#define RADIO_2056_TX_INTPAA_IAUX_STAT 0x38 +#define RADIO_2056_TX_INTPAA_IAUX_DYN 0x39 +#define RADIO_2056_TX_INTPAA_IMAIN_STAT 0x3a +#define RADIO_2056_TX_INTPAA_IMAIN_DYN 0x3b +#define RADIO_2056_TX_INTPAA_CASCBIAS 0x3c +#define RADIO_2056_TX_INTPAA_PASLOPE 0x3d +#define RADIO_2056_TX_INTPAA_PA_MISC 0x3e +#define RADIO_2056_TX_INTPAG_MASTER 0x3f +#define RADIO_2056_TX_INTPAG_GAIN 0x40 +#define RADIO_2056_TX_INTPAG_BOOST_TUNE 0x41 +#define RADIO_2056_TX_INTPAG_IAUX_STAT 0x42 +#define RADIO_2056_TX_INTPAG_IAUX_DYN 0x43 +#define RADIO_2056_TX_INTPAG_IMAIN_STAT 0x44 +#define RADIO_2056_TX_INTPAG_IMAIN_DYN 0x45 +#define RADIO_2056_TX_INTPAG_CASCBIAS 0x46 +#define RADIO_2056_TX_INTPAG_PASLOPE 0x47 +#define RADIO_2056_TX_INTPAG_PA_MISC 0x48 +#define RADIO_2056_TX_PADA_MASTER 0x49 +#define RADIO_2056_TX_PADA_IDAC 0x4a +#define RADIO_2056_TX_PADA_CASCBIAS 0x4b +#define RADIO_2056_TX_PADA_GAIN 0x4c +#define RADIO_2056_TX_PADA_BOOST_TUNE 0x4d +#define RADIO_2056_TX_PADA_SLOPE 0x4e +#define RADIO_2056_TX_PADG_MASTER 0x4f +#define RADIO_2056_TX_PADG_IDAC 0x50 +#define RADIO_2056_TX_PADG_CASCBIAS 0x51 +#define RADIO_2056_TX_PADG_GAIN 0x52 +#define RADIO_2056_TX_PADG_BOOST_TUNE 0x53 +#define RADIO_2056_TX_PADG_SLOPE 0x54 +#define RADIO_2056_TX_PGAA_MASTER 0x55 +#define RADIO_2056_TX_PGAA_IDAC 0x56 +#define RADIO_2056_TX_PGAA_GAIN 0x57 +#define RADIO_2056_TX_PGAA_BOOST_TUNE 0x58 +#define RADIO_2056_TX_PGAA_SLOPE 0x59 +#define RADIO_2056_TX_PGAA_MISC 0x5a +#define RADIO_2056_TX_PGAG_MASTER 0x5b +#define RADIO_2056_TX_PGAG_IDAC 0x5c +#define RADIO_2056_TX_PGAG_GAIN 0x5d +#define RADIO_2056_TX_PGAG_BOOST_TUNE 0x5e +#define RADIO_2056_TX_PGAG_SLOPE 0x5f +#define RADIO_2056_TX_PGAG_MISC 0x60 +#define RADIO_2056_TX_MIXA_MASTER 0x61 +#define RADIO_2056_TX_MIXA_BOOST_TUNE 0x62 +#define RADIO_2056_TX_MIXG 0x63 +#define RADIO_2056_TX_MIXG_BOOST_TUNE 0x64 +#define RADIO_2056_TX_BB_GM_MASTER 0x65 +#define RADIO_2056_TX_GMBB_GM 0x66 +#define RADIO_2056_TX_GMBB_IDAC 0x67 +#define RADIO_2056_TX_TXLPF_MASTER 0x68 +#define RADIO_2056_TX_TXLPF_RCCAL 0x69 +#define RADIO_2056_TX_TXLPF_RCCAL_OFF0 0x6a +#define RADIO_2056_TX_TXLPF_RCCAL_OFF1 0x6b +#define RADIO_2056_TX_TXLPF_RCCAL_OFF2 0x6c +#define RADIO_2056_TX_TXLPF_RCCAL_OFF3 0x6d +#define RADIO_2056_TX_TXLPF_RCCAL_OFF4 0x6e +#define RADIO_2056_TX_TXLPF_RCCAL_OFF5 0x6f +#define RADIO_2056_TX_TXLPF_RCCAL_OFF6 0x70 +#define RADIO_2056_TX_TXLPF_BW 0x71 +#define RADIO_2056_TX_TXLPF_GAIN 0x72 +#define RADIO_2056_TX_TXLPF_IDAC 0x73 +#define RADIO_2056_TX_TXLPF_IDAC_0 0x74 +#define RADIO_2056_TX_TXLPF_IDAC_1 0x75 +#define RADIO_2056_TX_TXLPF_IDAC_2 0x76 +#define RADIO_2056_TX_TXLPF_IDAC_3 0x77 +#define RADIO_2056_TX_TXLPF_IDAC_4 0x78 +#define RADIO_2056_TX_TXLPF_IDAC_5 0x79 +#define RADIO_2056_TX_TXLPF_IDAC_6 0x7a +#define RADIO_2056_TX_TXLPF_OPAMP_IDAC 0x7b +#define RADIO_2056_TX_TXLPF_MISC 0x7c +#define RADIO_2056_TX_TXSPARE1 0x7d +#define RADIO_2056_TX_TXSPARE2 0x7e +#define RADIO_2056_TX_TXSPARE3 0x7f +#define RADIO_2056_TX_TXSPARE4 0x80 +#define RADIO_2056_TX_TXSPARE5 0x81 +#define RADIO_2056_TX_TXSPARE6 0x82 +#define RADIO_2056_TX_TXSPARE7 0x83 +#define RADIO_2056_TX_TXSPARE8 0x84 +#define RADIO_2056_TX_TXSPARE9 0x85 +#define RADIO_2056_TX_TXSPARE10 0x86 +#define RADIO_2056_TX_TXSPARE11 0x87 +#define RADIO_2056_TX_TXSPARE12 0x88 +#define RADIO_2056_TX_TXSPARE13 0x89 +#define RADIO_2056_TX_TXSPARE14 0x8a +#define RADIO_2056_TX_TXSPARE15 0x8b +#define RADIO_2056_TX_TXSPARE16 0x8c +#define RADIO_2056_TX_STATUS_INTPA_GAIN 0x8d +#define RADIO_2056_TX_STATUS_PAD_GAIN 0x8e +#define RADIO_2056_TX_STATUS_PGA_GAIN 0x8f +#define RADIO_2056_TX_STATUS_GM_TXLPF_GAIN 0x90 +#define RADIO_2056_TX_STATUS_TXLPF_BW 0x91 +#define RADIO_2056_TX_STATUS_TXLPF_RC 0x92 +#define RADIO_2056_TX_GMBB_IDAC0 0x93 +#define RADIO_2056_TX_GMBB_IDAC1 0x94 +#define RADIO_2056_TX_GMBB_IDAC2 0x95 +#define RADIO_2056_TX_GMBB_IDAC3 0x96 +#define RADIO_2056_TX_GMBB_IDAC4 0x97 +#define RADIO_2056_TX_GMBB_IDAC5 0x98 +#define RADIO_2056_TX_GMBB_IDAC6 0x99 +#define RADIO_2056_TX_GMBB_IDAC7 0x9a + +#define RADIO_2056_RX_RESERVED_ADDR0 0x0 +#define RADIO_2056_RX_IDCODE 0x1 +#define RADIO_2056_RX_RESERVED_ADDR2 0x2 +#define RADIO_2056_RX_RESERVED_ADDR3 0x3 +#define RADIO_2056_RX_RESERVED_ADDR4 0x4 +#define RADIO_2056_RX_RESERVED_ADDR5 0x5 +#define RADIO_2056_RX_RESERVED_ADDR6 0x6 +#define RADIO_2056_RX_RESERVED_ADDR7 0x7 +#define RADIO_2056_RX_COM_CTRL 0x8 +#define RADIO_2056_RX_COM_PU 0x9 +#define RADIO_2056_RX_COM_OVR 0xa +#define RADIO_2056_RX_COM_RESET 0xb +#define RADIO_2056_RX_COM_RCAL 0xc +#define RADIO_2056_RX_COM_RC_RXLPF 0xd +#define RADIO_2056_RX_COM_RC_TXLPF 0xe +#define RADIO_2056_RX_COM_RC_RXHPF 0xf +#define RADIO_2056_RX_RESERVED_ADDR16 0x10 +#define RADIO_2056_RX_RESERVED_ADDR17 0x11 +#define RADIO_2056_RX_RESERVED_ADDR18 0x12 +#define RADIO_2056_RX_RESERVED_ADDR19 0x13 +#define RADIO_2056_RX_RESERVED_ADDR20 0x14 +#define RADIO_2056_RX_RESERVED_ADDR21 0x15 +#define RADIO_2056_RX_RESERVED_ADDR22 0x16 +#define RADIO_2056_RX_RESERVED_ADDR23 0x17 +#define RADIO_2056_RX_RESERVED_ADDR24 0x18 +#define RADIO_2056_RX_RESERVED_ADDR25 0x19 +#define RADIO_2056_RX_RESERVED_ADDR26 0x1a +#define RADIO_2056_RX_RESERVED_ADDR27 0x1b +#define RADIO_2056_RX_RESERVED_ADDR28 0x1c +#define RADIO_2056_RX_RESERVED_ADDR29 0x1d +#define RADIO_2056_RX_RESERVED_ADDR30 0x1e +#define RADIO_2056_RX_RESERVED_ADDR31 0x1f +#define RADIO_2056_RX_RXIQCAL_RXMUX 0x20 +#define RADIO_2056_RX_RSSI_PU 0x21 +#define RADIO_2056_RX_RSSI_SEL 0x22 +#define RADIO_2056_RX_RSSI_GAIN 0x23 +#define RADIO_2056_RX_RSSI_NB_IDAC 0x24 +#define RADIO_2056_RX_RSSI_WB2I_IDAC_1 0x25 +#define RADIO_2056_RX_RSSI_WB2I_IDAC_2 0x26 +#define RADIO_2056_RX_RSSI_WB2Q_IDAC_1 0x27 +#define RADIO_2056_RX_RSSI_WB2Q_IDAC_2 0x28 +#define RADIO_2056_RX_RSSI_POLE 0x29 +#define RADIO_2056_RX_RSSI_WB1_IDAC 0x2a +#define RADIO_2056_RX_RSSI_MISC 0x2b +#define RADIO_2056_RX_LNAA_MASTER 0x2c +#define RADIO_2056_RX_LNAA_TUNE 0x2d +#define RADIO_2056_RX_LNAA_GAIN 0x2e +#define RADIO_2056_RX_LNA_A_SLOPE 0x2f +#define RADIO_2056_RX_BIASPOLE_LNAA1_IDAC 0x30 +#define RADIO_2056_RX_LNAA2_IDAC 0x31 +#define RADIO_2056_RX_LNA1A_MISC 0x32 +#define RADIO_2056_RX_LNAG_MASTER 0x33 +#define RADIO_2056_RX_LNAG_TUNE 0x34 +#define RADIO_2056_RX_LNAG_GAIN 0x35 +#define RADIO_2056_RX_LNA_G_SLOPE 0x36 +#define RADIO_2056_RX_BIASPOLE_LNAG1_IDAC 0x37 +#define RADIO_2056_RX_LNAG2_IDAC 0x38 +#define RADIO_2056_RX_LNA1G_MISC 0x39 +#define RADIO_2056_RX_MIXA_MASTER 0x3a +#define RADIO_2056_RX_MIXA_VCM 0x3b +#define RADIO_2056_RX_MIXA_CTRLPTAT 0x3c +#define RADIO_2056_RX_MIXA_LOB_BIAS 0x3d +#define RADIO_2056_RX_MIXA_CORE_IDAC 0x3e +#define RADIO_2056_RX_MIXA_CMFB_IDAC 0x3f +#define RADIO_2056_RX_MIXA_BIAS_AUX 0x40 +#define RADIO_2056_RX_MIXA_BIAS_MAIN 0x41 +#define RADIO_2056_RX_MIXA_BIAS_MISC 0x42 +#define RADIO_2056_RX_MIXA_MAST_BIAS 0x43 +#define RADIO_2056_RX_MIXG_MASTER 0x44 +#define RADIO_2056_RX_MIXG_VCM 0x45 +#define RADIO_2056_RX_MIXG_CTRLPTAT 0x46 +#define RADIO_2056_RX_MIXG_LOB_BIAS 0x47 +#define RADIO_2056_RX_MIXG_CORE_IDAC 0x48 +#define RADIO_2056_RX_MIXG_CMFB_IDAC 0x49 +#define RADIO_2056_RX_MIXG_BIAS_AUX 0x4a +#define RADIO_2056_RX_MIXG_BIAS_MAIN 0x4b +#define RADIO_2056_RX_MIXG_BIAS_MISC 0x4c +#define RADIO_2056_RX_MIXG_MAST_BIAS 0x4d +#define RADIO_2056_RX_TIA_MASTER 0x4e +#define RADIO_2056_RX_TIA_IOPAMP 0x4f +#define RADIO_2056_RX_TIA_QOPAMP 0x50 +#define RADIO_2056_RX_TIA_IMISC 0x51 +#define RADIO_2056_RX_TIA_QMISC 0x52 +#define RADIO_2056_RX_TIA_GAIN 0x53 +#define RADIO_2056_RX_TIA_SPARE1 0x54 +#define RADIO_2056_RX_TIA_SPARE2 0x55 +#define RADIO_2056_RX_BB_LPF_MASTER 0x56 +#define RADIO_2056_RX_AACI_MASTER 0x57 +#define RADIO_2056_RX_RXLPF_IDAC 0x58 +#define RADIO_2056_RX_RXLPF_OPAMPBIAS_LOWQ 0x59 +#define RADIO_2056_RX_RXLPF_OPAMPBIAS_HIGHQ 0x5a +#define RADIO_2056_RX_RXLPF_BIAS_DCCANCEL 0x5b +#define RADIO_2056_RX_RXLPF_OUTVCM 0x5c +#define RADIO_2056_RX_RXLPF_INVCM_BODY 0x5d +#define RADIO_2056_RX_RXLPF_CC_OP 0x5e +#define RADIO_2056_RX_RXLPF_GAIN 0x5f +#define RADIO_2056_RX_RXLPF_Q_BW 0x60 +#define RADIO_2056_RX_RXLPF_HP_CORNER_BW 0x61 +#define RADIO_2056_RX_RXLPF_RCCAL_HPC 0x62 +#define RADIO_2056_RX_RXHPF_OFF0 0x63 +#define RADIO_2056_RX_RXHPF_OFF1 0x64 +#define RADIO_2056_RX_RXHPF_OFF2 0x65 +#define RADIO_2056_RX_RXHPF_OFF3 0x66 +#define RADIO_2056_RX_RXHPF_OFF4 0x67 +#define RADIO_2056_RX_RXHPF_OFF5 0x68 +#define RADIO_2056_RX_RXHPF_OFF6 0x69 +#define RADIO_2056_RX_RXHPF_OFF7 0x6a +#define RADIO_2056_RX_RXLPF_RCCAL_LPC 0x6b +#define RADIO_2056_RX_RXLPF_OFF_0 0x6c +#define RADIO_2056_RX_RXLPF_OFF_1 0x6d +#define RADIO_2056_RX_RXLPF_OFF_2 0x6e +#define RADIO_2056_RX_RXLPF_OFF_3 0x6f +#define RADIO_2056_RX_RXLPF_OFF_4 0x70 +#define RADIO_2056_RX_UNUSED 0x71 +#define RADIO_2056_RX_VGA_MASTER 0x72 +#define RADIO_2056_RX_VGA_BIAS 0x73 +#define RADIO_2056_RX_VGA_BIAS_DCCANCEL 0x74 +#define RADIO_2056_RX_VGA_GAIN 0x75 +#define RADIO_2056_RX_VGA_HP_CORNER_BW 0x76 +#define RADIO_2056_RX_VGABUF_BIAS 0x77 +#define RADIO_2056_RX_VGABUF_GAIN_BW 0x78 +#define RADIO_2056_RX_TXFBMIX_A 0x79 +#define RADIO_2056_RX_TXFBMIX_G 0x7a +#define RADIO_2056_RX_RXSPARE1 0x7b +#define RADIO_2056_RX_RXSPARE2 0x7c +#define RADIO_2056_RX_RXSPARE3 0x7d +#define RADIO_2056_RX_RXSPARE4 0x7e +#define RADIO_2056_RX_RXSPARE5 0x7f +#define RADIO_2056_RX_RXSPARE6 0x80 +#define RADIO_2056_RX_RXSPARE7 0x81 +#define RADIO_2056_RX_RXSPARE8 0x82 +#define RADIO_2056_RX_RXSPARE9 0x83 +#define RADIO_2056_RX_RXSPARE10 0x84 +#define RADIO_2056_RX_RXSPARE11 0x85 +#define RADIO_2056_RX_RXSPARE12 0x86 +#define RADIO_2056_RX_RXSPARE13 0x87 +#define RADIO_2056_RX_RXSPARE14 0x88 +#define RADIO_2056_RX_RXSPARE15 0x89 +#define RADIO_2056_RX_RXSPARE16 0x8a +#define RADIO_2056_RX_STATUS_LNAA_GAIN 0x8b +#define RADIO_2056_RX_STATUS_LNAG_GAIN 0x8c +#define RADIO_2056_RX_STATUS_MIXTIA_GAIN 0x8d +#define RADIO_2056_RX_STATUS_RXLPF_GAIN 0x8e +#define RADIO_2056_RX_STATUS_VGA_BUF_GAIN 0x8f +#define RADIO_2056_RX_STATUS_RXLPF_Q 0x90 +#define RADIO_2056_RX_STATUS_RXLPF_BUF_BW 0x91 +#define RADIO_2056_RX_STATUS_RXLPF_VGA_HPC 0x92 +#define RADIO_2056_RX_STATUS_RXLPF_RC 0x93 +#define RADIO_2056_RX_STATUS_HPC_RC 0x94 + +#define RADIO_2056_LNA1_A_PU 0x01 +#define RADIO_2056_LNA2_A_PU 0x02 +#define RADIO_2056_LNA1_G_PU 0x01 +#define RADIO_2056_LNA2_G_PU 0x02 +#define RADIO_2056_MIXA_PU_I 0x01 +#define RADIO_2056_MIXA_PU_Q 0x02 +#define RADIO_2056_MIXA_PU_GM 0x10 +#define RADIO_2056_MIXG_PU_I 0x01 +#define RADIO_2056_MIXG_PU_Q 0x02 +#define RADIO_2056_MIXG_PU_GM 0x10 +#define RADIO_2056_TIA_PU 0x01 +#define RADIO_2056_BB_LPF_PU 0x20 +#define RADIO_2056_W1_PU 0x02 +#define RADIO_2056_W2_PU 0x04 +#define RADIO_2056_NB_PU 0x08 +#define RADIO_2056_RSSI_W1_SEL 0x02 +#define RADIO_2056_RSSI_W2_SEL 0x04 +#define RADIO_2056_RSSI_NB_SEL 0x08 +#define RADIO_2056_VCM_MASK 0x1c +#define RADIO_2056_RSSI_VCM_SHIFT 0x02 + +#define RADIO_2057_DACBUF_VINCM_CORE0 0x0 +#define RADIO_2057_IDCODE 0x1 +#define RADIO_2057_RCCAL_MASTER 0x2 +#define RADIO_2057_RCCAL_CAP_SIZE 0x3 +#define RADIO_2057_RCAL_CONFIG 0x4 +#define RADIO_2057_GPAIO_CONFIG 0x5 +#define RADIO_2057_GPAIO_SEL1 0x6 +#define RADIO_2057_GPAIO_SEL0 0x7 +#define RADIO_2057_CLPO_CONFIG 0x8 +#define RADIO_2057_BANDGAP_CONFIG 0x9 +#define RADIO_2057_BANDGAP_RCAL_TRIM 0xa +#define RADIO_2057_AFEREG_CONFIG 0xb +#define RADIO_2057_TEMPSENSE_CONFIG 0xc +#define RADIO_2057_XTAL_CONFIG1 0xd +#define RADIO_2057_XTAL_ICORE_SIZE 0xe +#define RADIO_2057_XTAL_BUF_SIZE 0xf +#define RADIO_2057_XTAL_PULLCAP_SIZE 0x10 +#define RADIO_2057_RFPLL_MASTER 0x11 +#define RADIO_2057_VCOMONITOR_VTH_L 0x12 +#define RADIO_2057_VCOMONITOR_VTH_H 0x13 +#define RADIO_2057_VCOCAL_BIASRESET_RFPLLREG_VOUT 0x14 +#define RADIO_2057_VCO_VARCSIZE_IDAC 0x15 +#define RADIO_2057_VCOCAL_COUNTVAL0 0x16 +#define RADIO_2057_VCOCAL_COUNTVAL1 0x17 +#define RADIO_2057_VCOCAL_INTCLK_COUNT 0x18 +#define RADIO_2057_VCOCAL_MASTER 0x19 +#define RADIO_2057_VCOCAL_NUMCAPCHANGE 0x1a +#define RADIO_2057_VCOCAL_WINSIZE 0x1b +#define RADIO_2057_VCOCAL_DELAY_AFTER_REFRESH 0x1c +#define RADIO_2057_VCOCAL_DELAY_AFTER_CLOSELOOP 0x1d +#define RADIO_2057_VCOCAL_DELAY_AFTER_OPENLOOP 0x1e +#define RADIO_2057_VCOCAL_DELAY_BEFORE_OPENLOOP 0x1f +#define RADIO_2057_VCO_FORCECAPEN_FORCECAP1 0x20 +#define RADIO_2057_VCO_FORCECAP0 0x21 +#define RADIO_2057_RFPLL_REFMASTER_SPAREXTALSIZE 0x22 +#define RADIO_2057_RFPLL_PFD_RESET_PW 0x23 +#define RADIO_2057_RFPLL_LOOPFILTER_R2 0x24 +#define RADIO_2057_RFPLL_LOOPFILTER_R1 0x25 +#define RADIO_2057_RFPLL_LOOPFILTER_C3 0x26 +#define RADIO_2057_RFPLL_LOOPFILTER_C2 0x27 +#define RADIO_2057_RFPLL_LOOPFILTER_C1 0x28 +#define RADIO_2057_CP_KPD_IDAC 0x29 +#define RADIO_2057_RFPLL_IDACS 0x2a +#define RADIO_2057_RFPLL_MISC_EN 0x2b +#define RADIO_2057_RFPLL_MMD0 0x2c +#define RADIO_2057_RFPLL_MMD1 0x2d +#define RADIO_2057_RFPLL_MISC_CAL_RESETN 0x2e +#define RADIO_2057_JTAGXTAL_SIZE_CPBIAS_FILTRES 0x2f +#define RADIO_2057_VCO_ALCREF_BBPLLXTAL_SIZE 0x30 +#define RADIO_2057_VCOCAL_READCAP0 0x31 +#define RADIO_2057_VCOCAL_READCAP1 0x32 +#define RADIO_2057_VCOCAL_STATUS 0x33 +#define RADIO_2057_LOGEN_PUS 0x34 +#define RADIO_2057_LOGEN_PTAT_RESETS 0x35 +#define RADIO_2057_VCOBUF_IDACS 0x36 +#define RADIO_2057_VCOBUF_TUNE 0x37 +#define RADIO_2057_CMOSBUF_TX2GQ_IDACS 0x38 +#define RADIO_2057_CMOSBUF_TX2GI_IDACS 0x39 +#define RADIO_2057_CMOSBUF_TX5GQ_IDACS 0x3a +#define RADIO_2057_CMOSBUF_TX5GI_IDACS 0x3b +#define RADIO_2057_CMOSBUF_RX2GQ_IDACS 0x3c +#define RADIO_2057_CMOSBUF_RX2GI_IDACS 0x3d +#define RADIO_2057_CMOSBUF_RX5GQ_IDACS 0x3e +#define RADIO_2057_CMOSBUF_RX5GI_IDACS 0x3f +#define RADIO_2057_LOGEN_MX2G_IDACS 0x40 +#define RADIO_2057_LOGEN_MX2G_TUNE 0x41 +#define RADIO_2057_LOGEN_MX5G_IDACS 0x42 +#define RADIO_2057_LOGEN_MX5G_TUNE 0x43 +#define RADIO_2057_LOGEN_MX5G_RCCR 0x44 +#define RADIO_2057_LOGEN_INDBUF2G_IDAC 0x45 +#define RADIO_2057_LOGEN_INDBUF2G_IBOOST 0x46 +#define RADIO_2057_LOGEN_INDBUF2G_TUNE 0x47 +#define RADIO_2057_LOGEN_INDBUF5G_IDAC 0x48 +#define RADIO_2057_LOGEN_INDBUF5G_IBOOST 0x49 +#define RADIO_2057_LOGEN_INDBUF5G_TUNE 0x4a +#define RADIO_2057_CMOSBUF_TX_RCCR 0x4b +#define RADIO_2057_CMOSBUF_RX_RCCR 0x4c +#define RADIO_2057_LOGEN_SEL_PKDET 0x4d +#define RADIO_2057_CMOSBUF_SHAREIQ_PTAT 0x4e +#define RADIO_2057_RXTXBIAS_CONFIG_CORE0 0x4f +#define RADIO_2057_TXGM_TXRF_PUS_CORE0 0x50 +#define RADIO_2057_TXGM_IDAC_BLEED_CORE0 0x51 +#define RADIO_2057_TXGM_GAIN_CORE0 0x56 +#define RADIO_2057_TXGM2G_PKDET_PUS_CORE0 0x57 +#define RADIO_2057_PAD2G_PTATS_CORE0 0x58 +#define RADIO_2057_PAD2G_IDACS_CORE0 0x59 +#define RADIO_2057_PAD2G_BOOST_PU_CORE0 0x5a +#define RADIO_2057_PAD2G_CASCV_GAIN_CORE0 0x5b +#define RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE0 0x5c +#define RADIO_2057_TXMIX2G_LODC_CORE0 0x5d +#define RADIO_2057_PAD2G_TUNE_PUS_CORE0 0x5e +#define RADIO_2057_IPA2G_GAIN_CORE0 0x5f +#define RADIO_2057_TSSI2G_SPARE1_CORE0 0x60 +#define RADIO_2057_TSSI2G_SPARE2_CORE0 0x61 +#define RADIO_2057_IPA2G_TUNEV_CASCV_PTAT_CORE0 0x62 +#define RADIO_2057_IPA2G_IMAIN_CORE0 0x63 +#define RADIO_2057_IPA2G_CASCONV_CORE0 0x64 +#define RADIO_2057_IPA2G_CASCOFFV_CORE0 0x65 +#define RADIO_2057_IPA2G_BIAS_FILTER_CORE0 0x66 +#define RADIO_2057_TX5G_PKDET_CORE0 0x69 +#define RADIO_2057_PGA_PTAT_TXGM5G_PU_CORE0 0x6a +#define RADIO_2057_PAD5G_PTATS1_CORE0 0x6b +#define RADIO_2057_PAD5G_CLASS_PTATS2_CORE0 0x6c +#define RADIO_2057_PGA_BOOSTPTAT_IMAIN_CORE0 0x6d +#define RADIO_2057_PAD5G_CASCV_IMAIN_CORE0 0x6e +#define RADIO_2057_TXMIX5G_IBOOST_PAD_IAUX_CORE0 0x6f +#define RADIO_2057_PGA_BOOST_TUNE_CORE0 0x70 +#define RADIO_2057_PGA_GAIN_CORE0 0x71 +#define RADIO_2057_PAD5G_CASCOFFV_GAIN_PUS_CORE0 0x72 +#define RADIO_2057_TXMIX5G_BOOST_TUNE_CORE0 0x73 +#define RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE0 0x74 +#define RADIO_2057_IPA5G_IAUX_CORE0 0x75 +#define RADIO_2057_IPA5G_GAIN_CORE0 0x76 +#define RADIO_2057_TSSI5G_SPARE1_CORE0 0x77 +#define RADIO_2057_TSSI5G_SPARE2_CORE0 0x78 +#define RADIO_2057_IPA5G_CASCOFFV_PU_CORE0 0x79 +#define RADIO_2057_IPA5G_PTAT_CORE0 0x7a +#define RADIO_2057_IPA5G_IMAIN_CORE0 0x7b +#define RADIO_2057_IPA5G_CASCONV_CORE0 0x7c +#define RADIO_2057_IPA5G_BIAS_FILTER_CORE0 0x7d +#define RADIO_2057_PAD_BIAS_FILTER_BWS_CORE0 0x80 +#define RADIO_2057_TR2G_CONFIG1_CORE0_NU 0x81 +#define RADIO_2057_TR2G_CONFIG2_CORE0_NU 0x82 +#define RADIO_2057_LNA5G_RFEN_CORE0 0x83 +#define RADIO_2057_TR5G_CONFIG2_CORE0_NU 0x84 +#define RADIO_2057_RXRFBIAS_IBOOST_PU_CORE0 0x85 +#define RADIO_2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE0 0x86 +#define RADIO_2057_RXGM_CMFBITAIL_AUXPTAT_CORE0 0x87 +#define RADIO_2057_RXMIX_ICORE_RXGM_IAUX_CORE0 0x88 +#define RADIO_2057_RXMIX_CMFBITAIL_PU_CORE0 0x89 +#define RADIO_2057_LNA2_IMAIN_PTAT_PU_CORE0 0x8a +#define RADIO_2057_LNA2_IAUX_PTAT_CORE0 0x8b +#define RADIO_2057_LNA1_IMAIN_PTAT_PU_CORE0 0x8c +#define RADIO_2057_LNA15G_INPUT_MATCH_TUNE_CORE0 0x8d +#define RADIO_2057_RXRFBIAS_BANDSEL_CORE0 0x8e +#define RADIO_2057_TIA_CONFIG_CORE0 0x8f +#define RADIO_2057_TIA_IQGAIN_CORE0 0x90 +#define RADIO_2057_TIA_IBIAS2_CORE0 0x91 +#define RADIO_2057_TIA_IBIAS1_CORE0 0x92 +#define RADIO_2057_TIA_SPARE_Q_CORE0 0x93 +#define RADIO_2057_TIA_SPARE_I_CORE0 0x94 +#define RADIO_2057_RXMIX2G_PUS_CORE0 0x95 +#define RADIO_2057_RXMIX2G_VCMREFS_CORE0 0x96 +#define RADIO_2057_RXMIX2G_LODC_QI_CORE0 0x97 +#define RADIO_2057_W12G_BW_LNA2G_PUS_CORE0 0x98 +#define RADIO_2057_LNA2G_GAIN_CORE0 0x99 +#define RADIO_2057_LNA2G_TUNE_CORE0 0x9a +#define RADIO_2057_RXMIX5G_PUS_CORE0 0x9b +#define RADIO_2057_RXMIX5G_VCMREFS_CORE0 0x9c +#define RADIO_2057_RXMIX5G_LODC_QI_CORE0 0x9d +#define RADIO_2057_W15G_BW_LNA5G_PUS_CORE0 0x9e +#define RADIO_2057_LNA5G_GAIN_CORE0 0x9f +#define RADIO_2057_LNA5G_TUNE_CORE0 0xa0 +#define RADIO_2057_LPFSEL_TXRX_RXBB_PUS_CORE0 0xa1 +#define RADIO_2057_RXBB_BIAS_MASTER_CORE0 0xa2 +#define RADIO_2057_RXBB_VGABUF_IDACS_CORE0 0xa3 +#define RADIO_2057_LPF_VCMREF_TXBUF_VCMREF_CORE0 0xa4 +#define RADIO_2057_TXBUF_VINCM_CORE0 0xa5 +#define RADIO_2057_TXBUF_IDACS_CORE0 0xa6 +#define RADIO_2057_LPF_RESP_RXBUF_BW_CORE0 0xa7 +#define RADIO_2057_RXBB_CC_CORE0 0xa8 +#define RADIO_2057_RXBB_SPARE3_CORE0 0xa9 +#define RADIO_2057_RXBB_RCCAL_HPC_CORE0 0xaa +#define RADIO_2057_LPF_IDACS_CORE0 0xab +#define RADIO_2057_LPFBYP_DCLOOP_BYP_IDAC_CORE0 0xac +#define RADIO_2057_TXBUF_GAIN_CORE0 0xad +#define RADIO_2057_AFELOOPBACK_AACI_RESP_CORE0 0xae +#define RADIO_2057_RXBUF_DEGEN_CORE0 0xaf +#define RADIO_2057_RXBB_SPARE2_CORE0 0xb0 +#define RADIO_2057_RXBB_SPARE1_CORE0 0xb1 +#define RADIO_2057_RSSI_MASTER_CORE0 0xb2 +#define RADIO_2057_W2_MASTER_CORE0 0xb3 +#define RADIO_2057_NB_MASTER_CORE0 0xb4 +#define RADIO_2057_W2_IDACS0_Q_CORE0 0xb5 +#define RADIO_2057_W2_IDACS1_Q_CORE0 0xb6 +#define RADIO_2057_W2_IDACS0_I_CORE0 0xb7 +#define RADIO_2057_W2_IDACS1_I_CORE0 0xb8 +#define RADIO_2057_RSSI_GPAIOSEL_W1_IDACS_CORE0 0xb9 +#define RADIO_2057_NB_IDACS_Q_CORE0 0xba +#define RADIO_2057_NB_IDACS_I_CORE0 0xbb +#define RADIO_2057_BACKUP4_CORE0 0xc1 +#define RADIO_2057_BACKUP3_CORE0 0xc2 +#define RADIO_2057_BACKUP2_CORE0 0xc3 +#define RADIO_2057_BACKUP1_CORE0 0xc4 +#define RADIO_2057_SPARE16_CORE0 0xc5 +#define RADIO_2057_SPARE15_CORE0 0xc6 +#define RADIO_2057_SPARE14_CORE0 0xc7 +#define RADIO_2057_SPARE13_CORE0 0xc8 +#define RADIO_2057_SPARE12_CORE0 0xc9 +#define RADIO_2057_SPARE11_CORE0 0xca +#define RADIO_2057_TX2G_BIAS_RESETS_CORE0 0xcb +#define RADIO_2057_TX5G_BIAS_RESETS_CORE0 0xcc +#define RADIO_2057_IQTEST_SEL_PU 0xcd +#define RADIO_2057_XTAL_CONFIG2 0xce +#define RADIO_2057_BUFS_MISC_LPFBW_CORE0 0xcf +#define RADIO_2057_TXLPF_RCCAL_CORE0 0xd0 +#define RADIO_2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE0 0xd1 +#define RADIO_2057_LPF_GAIN_CORE0 0xd2 +#define RADIO_2057_DACBUF_IDACS_BW_CORE0 0xd3 +#define RADIO_2057_RXTXBIAS_CONFIG_CORE1 0xd4 +#define RADIO_2057_TXGM_TXRF_PUS_CORE1 0xd5 +#define RADIO_2057_TXGM_IDAC_BLEED_CORE1 0xd6 +#define RADIO_2057_TXGM_GAIN_CORE1 0xdb +#define RADIO_2057_TXGM2G_PKDET_PUS_CORE1 0xdc +#define RADIO_2057_PAD2G_PTATS_CORE1 0xdd +#define RADIO_2057_PAD2G_IDACS_CORE1 0xde +#define RADIO_2057_PAD2G_BOOST_PU_CORE1 0xdf +#define RADIO_2057_PAD2G_CASCV_GAIN_CORE1 0xe0 +#define RADIO_2057_TXMIX2G_TUNE_BOOST_PU_CORE1 0xe1 +#define RADIO_2057_TXMIX2G_LODC_CORE1 0xe2 +#define RADIO_2057_PAD2G_TUNE_PUS_CORE1 0xe3 +#define RADIO_2057_IPA2G_GAIN_CORE1 0xe4 +#define RADIO_2057_TSSI2G_SPARE1_CORE1 0xe5 +#define RADIO_2057_TSSI2G_SPARE2_CORE1 0xe6 +#define RADIO_2057_IPA2G_TUNEV_CASCV_PTAT_CORE1 0xe7 +#define RADIO_2057_IPA2G_IMAIN_CORE1 0xe8 +#define RADIO_2057_IPA2G_CASCONV_CORE1 0xe9 +#define RADIO_2057_IPA2G_CASCOFFV_CORE1 0xea +#define RADIO_2057_IPA2G_BIAS_FILTER_CORE1 0xeb +#define RADIO_2057_TX5G_PKDET_CORE1 0xee +#define RADIO_2057_PGA_PTAT_TXGM5G_PU_CORE1 0xef +#define RADIO_2057_PAD5G_PTATS1_CORE1 0xf0 +#define RADIO_2057_PAD5G_CLASS_PTATS2_CORE1 0xf1 +#define RADIO_2057_PGA_BOOSTPTAT_IMAIN_CORE1 0xf2 +#define RADIO_2057_PAD5G_CASCV_IMAIN_CORE1 0xf3 +#define RADIO_2057_TXMIX5G_IBOOST_PAD_IAUX_CORE1 0xf4 +#define RADIO_2057_PGA_BOOST_TUNE_CORE1 0xf5 +#define RADIO_2057_PGA_GAIN_CORE1 0xf6 +#define RADIO_2057_PAD5G_CASCOFFV_GAIN_PUS_CORE1 0xf7 +#define RADIO_2057_TXMIX5G_BOOST_TUNE_CORE1 0xf8 +#define RADIO_2057_PAD5G_TUNE_MISC_PUS_CORE1 0xf9 +#define RADIO_2057_IPA5G_IAUX_CORE1 0xfa +#define RADIO_2057_IPA5G_GAIN_CORE1 0xfb +#define RADIO_2057_TSSI5G_SPARE1_CORE1 0xfc +#define RADIO_2057_TSSI5G_SPARE2_CORE1 0xfd +#define RADIO_2057_IPA5G_CASCOFFV_PU_CORE1 0xfe +#define RADIO_2057_IPA5G_PTAT_CORE1 0xff +#define RADIO_2057_IPA5G_IMAIN_CORE1 0x100 +#define RADIO_2057_IPA5G_CASCONV_CORE1 0x101 +#define RADIO_2057_IPA5G_BIAS_FILTER_CORE1 0x102 +#define RADIO_2057_PAD_BIAS_FILTER_BWS_CORE1 0x105 +#define RADIO_2057_TR2G_CONFIG1_CORE1_NU 0x106 +#define RADIO_2057_TR2G_CONFIG2_CORE1_NU 0x107 +#define RADIO_2057_LNA5G_RFEN_CORE1 0x108 +#define RADIO_2057_TR5G_CONFIG2_CORE1_NU 0x109 +#define RADIO_2057_RXRFBIAS_IBOOST_PU_CORE1 0x10a +#define RADIO_2057_RXRF_IABAND_RXGM_IMAIN_PTAT_CORE1 0x10b +#define RADIO_2057_RXGM_CMFBITAIL_AUXPTAT_CORE1 0x10c +#define RADIO_2057_RXMIX_ICORE_RXGM_IAUX_CORE1 0x10d +#define RADIO_2057_RXMIX_CMFBITAIL_PU_CORE1 0x10e +#define RADIO_2057_LNA2_IMAIN_PTAT_PU_CORE1 0x10f +#define RADIO_2057_LNA2_IAUX_PTAT_CORE1 0x110 +#define RADIO_2057_LNA1_IMAIN_PTAT_PU_CORE1 0x111 +#define RADIO_2057_LNA15G_INPUT_MATCH_TUNE_CORE1 0x112 +#define RADIO_2057_RXRFBIAS_BANDSEL_CORE1 0x113 +#define RADIO_2057_TIA_CONFIG_CORE1 0x114 +#define RADIO_2057_TIA_IQGAIN_CORE1 0x115 +#define RADIO_2057_TIA_IBIAS2_CORE1 0x116 +#define RADIO_2057_TIA_IBIAS1_CORE1 0x117 +#define RADIO_2057_TIA_SPARE_Q_CORE1 0x118 +#define RADIO_2057_TIA_SPARE_I_CORE1 0x119 +#define RADIO_2057_RXMIX2G_PUS_CORE1 0x11a +#define RADIO_2057_RXMIX2G_VCMREFS_CORE1 0x11b +#define RADIO_2057_RXMIX2G_LODC_QI_CORE1 0x11c +#define RADIO_2057_W12G_BW_LNA2G_PUS_CORE1 0x11d +#define RADIO_2057_LNA2G_GAIN_CORE1 0x11e +#define RADIO_2057_LNA2G_TUNE_CORE1 0x11f +#define RADIO_2057_RXMIX5G_PUS_CORE1 0x120 +#define RADIO_2057_RXMIX5G_VCMREFS_CORE1 0x121 +#define RADIO_2057_RXMIX5G_LODC_QI_CORE1 0x122 +#define RADIO_2057_W15G_BW_LNA5G_PUS_CORE1 0x123 +#define RADIO_2057_LNA5G_GAIN_CORE1 0x124 +#define RADIO_2057_LNA5G_TUNE_CORE1 0x125 +#define RADIO_2057_LPFSEL_TXRX_RXBB_PUS_CORE1 0x126 +#define RADIO_2057_RXBB_BIAS_MASTER_CORE1 0x127 +#define RADIO_2057_RXBB_VGABUF_IDACS_CORE1 0x128 +#define RADIO_2057_LPF_VCMREF_TXBUF_VCMREF_CORE1 0x129 +#define RADIO_2057_TXBUF_VINCM_CORE1 0x12a +#define RADIO_2057_TXBUF_IDACS_CORE1 0x12b +#define RADIO_2057_LPF_RESP_RXBUF_BW_CORE1 0x12c +#define RADIO_2057_RXBB_CC_CORE1 0x12d +#define RADIO_2057_RXBB_SPARE3_CORE1 0x12e +#define RADIO_2057_RXBB_RCCAL_HPC_CORE1 0x12f +#define RADIO_2057_LPF_IDACS_CORE1 0x130 +#define RADIO_2057_LPFBYP_DCLOOP_BYP_IDAC_CORE1 0x131 +#define RADIO_2057_TXBUF_GAIN_CORE1 0x132 +#define RADIO_2057_AFELOOPBACK_AACI_RESP_CORE1 0x133 +#define RADIO_2057_RXBUF_DEGEN_CORE1 0x134 +#define RADIO_2057_RXBB_SPARE2_CORE1 0x135 +#define RADIO_2057_RXBB_SPARE1_CORE1 0x136 +#define RADIO_2057_RSSI_MASTER_CORE1 0x137 +#define RADIO_2057_W2_MASTER_CORE1 0x138 +#define RADIO_2057_NB_MASTER_CORE1 0x139 +#define RADIO_2057_W2_IDACS0_Q_CORE1 0x13a +#define RADIO_2057_W2_IDACS1_Q_CORE1 0x13b +#define RADIO_2057_W2_IDACS0_I_CORE1 0x13c +#define RADIO_2057_W2_IDACS1_I_CORE1 0x13d +#define RADIO_2057_RSSI_GPAIOSEL_W1_IDACS_CORE1 0x13e +#define RADIO_2057_NB_IDACS_Q_CORE1 0x13f +#define RADIO_2057_NB_IDACS_I_CORE1 0x140 +#define RADIO_2057_BACKUP4_CORE1 0x146 +#define RADIO_2057_BACKUP3_CORE1 0x147 +#define RADIO_2057_BACKUP2_CORE1 0x148 +#define RADIO_2057_BACKUP1_CORE1 0x149 +#define RADIO_2057_SPARE16_CORE1 0x14a +#define RADIO_2057_SPARE15_CORE1 0x14b +#define RADIO_2057_SPARE14_CORE1 0x14c +#define RADIO_2057_SPARE13_CORE1 0x14d +#define RADIO_2057_SPARE12_CORE1 0x14e +#define RADIO_2057_SPARE11_CORE1 0x14f +#define RADIO_2057_TX2G_BIAS_RESETS_CORE1 0x150 +#define RADIO_2057_TX5G_BIAS_RESETS_CORE1 0x151 +#define RADIO_2057_SPARE8_CORE1 0x152 +#define RADIO_2057_SPARE7_CORE1 0x153 +#define RADIO_2057_BUFS_MISC_LPFBW_CORE1 0x154 +#define RADIO_2057_TXLPF_RCCAL_CORE1 0x155 +#define RADIO_2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE1 0x156 +#define RADIO_2057_LPF_GAIN_CORE1 0x157 +#define RADIO_2057_DACBUF_IDACS_BW_CORE1 0x158 +#define RADIO_2057_DACBUF_VINCM_CORE1 0x159 +#define RADIO_2057_RCCAL_START_R1_Q1_P1 0x15a +#define RADIO_2057_RCCAL_X1 0x15b +#define RADIO_2057_RCCAL_TRC0 0x15c +#define RADIO_2057_RCCAL_TRC1 0x15d +#define RADIO_2057_RCCAL_DONE_OSCCAP 0x15e +#define RADIO_2057_RCCAL_N0_0 0x15f +#define RADIO_2057_RCCAL_N0_1 0x160 +#define RADIO_2057_RCCAL_N1_0 0x161 +#define RADIO_2057_RCCAL_N1_1 0x162 +#define RADIO_2057_RCAL_STATUS 0x163 +#define RADIO_2057_XTALPUOVR_PINCTRL 0x164 +#define RADIO_2057_OVR_REG0 0x165 +#define RADIO_2057_OVR_REG1 0x166 +#define RADIO_2057_OVR_REG2 0x167 +#define RADIO_2057_OVR_REG3 0x168 +#define RADIO_2057_OVR_REG4 0x169 +#define RADIO_2057_RCCAL_SCAP_VAL 0x16a +#define RADIO_2057_RCCAL_BCAP_VAL 0x16b +#define RADIO_2057_RCCAL_HPC_VAL 0x16c +#define RADIO_2057_RCCAL_OVERRIDES 0x16d +#define RADIO_2057_TX0_IQCAL_GAIN_BW 0x170 +#define RADIO_2057_TX0_LOFT_FINE_I 0x171 +#define RADIO_2057_TX0_LOFT_FINE_Q 0x172 +#define RADIO_2057_TX0_LOFT_COARSE_I 0x173 +#define RADIO_2057_TX0_LOFT_COARSE_Q 0x174 +#define RADIO_2057_TX0_TX_SSI_MASTER 0x175 +#define RADIO_2057_TX0_IQCAL_VCM_HG 0x176 +#define RADIO_2057_TX0_IQCAL_IDAC 0x177 +#define RADIO_2057_TX0_TSSI_VCM 0x178 +#define RADIO_2057_TX0_TX_SSI_MUX 0x179 +#define RADIO_2057_TX0_TSSIA 0x17a +#define RADIO_2057_TX0_TSSIG 0x17b +#define RADIO_2057_TX0_TSSI_MISC1 0x17c +#define RADIO_2057_TX0_TXRXCOUPLE_2G_ATTEN 0x17d +#define RADIO_2057_TX0_TXRXCOUPLE_2G_PWRUP 0x17e +#define RADIO_2057_TX0_TXRXCOUPLE_5G_ATTEN 0x17f +#define RADIO_2057_TX0_TXRXCOUPLE_5G_PWRUP 0x180 +#define RADIO_2057_TX1_IQCAL_GAIN_BW 0x190 +#define RADIO_2057_TX1_LOFT_FINE_I 0x191 +#define RADIO_2057_TX1_LOFT_FINE_Q 0x192 +#define RADIO_2057_TX1_LOFT_COARSE_I 0x193 +#define RADIO_2057_TX1_LOFT_COARSE_Q 0x194 +#define RADIO_2057_TX1_TX_SSI_MASTER 0x195 +#define RADIO_2057_TX1_IQCAL_VCM_HG 0x196 +#define RADIO_2057_TX1_IQCAL_IDAC 0x197 +#define RADIO_2057_TX1_TSSI_VCM 0x198 +#define RADIO_2057_TX1_TX_SSI_MUX 0x199 +#define RADIO_2057_TX1_TSSIA 0x19a +#define RADIO_2057_TX1_TSSIG 0x19b +#define RADIO_2057_TX1_TSSI_MISC1 0x19c +#define RADIO_2057_TX1_TXRXCOUPLE_2G_ATTEN 0x19d +#define RADIO_2057_TX1_TXRXCOUPLE_2G_PWRUP 0x19e +#define RADIO_2057_TX1_TXRXCOUPLE_5G_ATTEN 0x19f +#define RADIO_2057_TX1_TXRXCOUPLE_5G_PWRUP 0x1a0 +#define RADIO_2057_AFE_VCM_CAL_MASTER_CORE0 0x1a1 +#define RADIO_2057_AFE_SET_VCM_I_CORE0 0x1a2 +#define RADIO_2057_AFE_SET_VCM_Q_CORE0 0x1a3 +#define RADIO_2057_AFE_STATUS_VCM_IQADC_CORE0 0x1a4 +#define RADIO_2057_AFE_STATUS_VCM_I_CORE0 0x1a5 +#define RADIO_2057_AFE_STATUS_VCM_Q_CORE0 0x1a6 +#define RADIO_2057_AFE_VCM_CAL_MASTER_CORE1 0x1a7 +#define RADIO_2057_AFE_SET_VCM_I_CORE1 0x1a8 +#define RADIO_2057_AFE_SET_VCM_Q_CORE1 0x1a9 +#define RADIO_2057_AFE_STATUS_VCM_IQADC_CORE1 0x1aa +#define RADIO_2057_AFE_STATUS_VCM_I_CORE1 0x1ab +#define RADIO_2057_AFE_STATUS_VCM_Q_CORE1 0x1ac + +#define RADIO_2057v7_DACBUF_VINCM_CORE0 0x1ad +#define RADIO_2057v7_RCCAL_MASTER 0x1ae +#define RADIO_2057v7_TR2G_CONFIG3_CORE0_NU 0x1af +#define RADIO_2057v7_TR2G_CONFIG3_CORE1_NU 0x1b0 +#define RADIO_2057v7_LOGEN_PUS1 0x1b1 +#define RADIO_2057v7_OVR_REG5 0x1b2 +#define RADIO_2057v7_OVR_REG6 0x1b3 +#define RADIO_2057v7_OVR_REG7 0x1b4 +#define RADIO_2057v7_OVR_REG8 0x1b5 +#define RADIO_2057v7_OVR_REG9 0x1b6 +#define RADIO_2057v7_OVR_REG10 0x1b7 +#define RADIO_2057v7_OVR_REG11 0x1b8 +#define RADIO_2057v7_OVR_REG12 0x1b9 +#define RADIO_2057v7_OVR_REG13 0x1ba +#define RADIO_2057v7_OVR_REG14 0x1bb +#define RADIO_2057v7_OVR_REG15 0x1bc +#define RADIO_2057v7_OVR_REG16 0x1bd +#define RADIO_2057v7_OVR_REG1 0x1be +#define RADIO_2057v7_OVR_REG18 0x1bf +#define RADIO_2057v7_OVR_REG19 0x1c0 +#define RADIO_2057v7_OVR_REG20 0x1c1 +#define RADIO_2057v7_OVR_REG21 0x1c2 +#define RADIO_2057v7_OVR_REG2 0x1c3 +#define RADIO_2057v7_OVR_REG23 0x1c4 +#define RADIO_2057v7_OVR_REG24 0x1c5 +#define RADIO_2057v7_OVR_REG25 0x1c6 +#define RADIO_2057v7_OVR_REG26 0x1c7 +#define RADIO_2057v7_OVR_REG27 0x1c8 +#define RADIO_2057v7_OVR_REG28 0x1c9 +#define RADIO_2057v7_IQTEST_SEL_PU2 0x1ca + +#define RADIO_2057_VCM_MASK 0x7 + +#endif /* _BRCM_PHY_RADIO_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phyreg_n.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phyreg_n.h new file mode 100644 index 000000000000..a97c3a799479 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phyreg_n.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2010 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. + */ + +#define NPHY_TBL_ID_GAIN1 0 +#define NPHY_TBL_ID_GAIN2 1 +#define NPHY_TBL_ID_GAINBITS1 2 +#define NPHY_TBL_ID_GAINBITS2 3 +#define NPHY_TBL_ID_GAINLIMIT 4 +#define NPHY_TBL_ID_WRSSIGainLimit 5 +#define NPHY_TBL_ID_RFSEQ 7 +#define NPHY_TBL_ID_AFECTRL 8 +#define NPHY_TBL_ID_ANTSWCTRLLUT 9 +#define NPHY_TBL_ID_IQLOCAL 15 +#define NPHY_TBL_ID_NOISEVAR 16 +#define NPHY_TBL_ID_SAMPLEPLAY 17 +#define NPHY_TBL_ID_CORE1TXPWRCTL 26 +#define NPHY_TBL_ID_CORE2TXPWRCTL 27 +#define NPHY_TBL_ID_CMPMETRICDATAWEIGHTTBL 30 + +#define NPHY_TBL_ID_EPSILONTBL0 31 +#define NPHY_TBL_ID_SCALARTBL0 32 +#define NPHY_TBL_ID_EPSILONTBL1 33 +#define NPHY_TBL_ID_SCALARTBL1 34 + +#define NPHY_TO_BPHY_OFF 0xc00 + +#define NPHY_BandControl_currentBand 0x0001 +#define RFCC_CHIP0_PU 0x0400 +#define RFCC_POR_FORCE 0x0040 +#define RFCC_OE_POR_FORCE 0x0080 +#define NPHY_RfctrlIntc_override_OFF 0 +#define NPHY_RfctrlIntc_override_TRSW 1 +#define NPHY_RfctrlIntc_override_PA 2 +#define NPHY_RfctrlIntc_override_EXT_LNA_PU 3 +#define NPHY_RfctrlIntc_override_EXT_LNA_GAIN 4 +#define RIFS_ENABLE 0x80 +#define BPHY_BAND_SEL_UP20 0x10 +#define NPHY_MLenable 0x02 + +#define NPHY_RfseqMode_CoreActv_override 0x0001 +#define NPHY_RfseqMode_Trigger_override 0x0002 +#define NPHY_RfseqCoreActv_TxRxChain0 (0x11) +#define NPHY_RfseqCoreActv_TxRxChain1 (0x22) + +#define NPHY_RfseqTrigger_rx2tx 0x0001 +#define NPHY_RfseqTrigger_tx2rx 0x0002 +#define NPHY_RfseqTrigger_updategainh 0x0004 +#define NPHY_RfseqTrigger_updategainl 0x0008 +#define NPHY_RfseqTrigger_updategainu 0x0010 +#define NPHY_RfseqTrigger_reset2rx 0x0020 +#define NPHY_RfseqStatus_rx2tx 0x0001 +#define NPHY_RfseqStatus_tx2rx 0x0002 +#define NPHY_RfseqStatus_updategainh 0x0004 +#define NPHY_RfseqStatus_updategainl 0x0008 +#define NPHY_RfseqStatus_updategainu 0x0010 +#define NPHY_RfseqStatus_reset2rx 0x0020 +#define NPHY_ClassifierCtrl_cck_en 0x1 +#define NPHY_ClassifierCtrl_ofdm_en 0x2 +#define NPHY_ClassifierCtrl_waited_en 0x4 +#define NPHY_IQFlip_ADC1 0x0001 +#define NPHY_IQFlip_ADC2 0x0010 +#define NPHY_sampleCmd_STOP 0x0002 + +#define RX_GF_OR_MM 0x0004 +#define RX_GF_MM_AUTO 0x0100 + +#define NPHY_iqloCalCmdGctl_IQLO_CAL_EN 0x8000 + +#define NPHY_IqestCmd_iqstart 0x1 +#define NPHY_IqestCmd_iqMode 0x2 + +#define NPHY_TxPwrCtrlCmd_pwrIndex_init 0x40 +#define NPHY_TxPwrCtrlCmd_pwrIndex_init_rev7 0x19 + +#define PRIM_SEL_UP20 0x8000 + +#define NPHY_RFSEQ_RX2TX 0x0 +#define NPHY_RFSEQ_TX2RX 0x1 +#define NPHY_RFSEQ_RESET2RX 0x2 +#define NPHY_RFSEQ_UPDATEGAINH 0x3 +#define NPHY_RFSEQ_UPDATEGAINL 0x4 +#define NPHY_RFSEQ_UPDATEGAINU 0x5 + +#define NPHY_RFSEQ_CMD_NOP 0x0 +#define NPHY_RFSEQ_CMD_RXG_FBW 0x1 +#define NPHY_RFSEQ_CMD_TR_SWITCH 0x2 +#define NPHY_RFSEQ_CMD_EXT_PA 0x3 +#define NPHY_RFSEQ_CMD_RXPD_TXPD 0x4 +#define NPHY_RFSEQ_CMD_TX_GAIN 0x5 +#define NPHY_RFSEQ_CMD_RX_GAIN 0x6 +#define NPHY_RFSEQ_CMD_SET_HPF_BW 0x7 +#define NPHY_RFSEQ_CMD_CLR_HIQ_DIS 0x8 +#define NPHY_RFSEQ_CMD_END 0xf + +#define NPHY_REV3_RFSEQ_CMD_NOP 0x0 +#define NPHY_REV3_RFSEQ_CMD_RXG_FBW 0x1 +#define NPHY_REV3_RFSEQ_CMD_TR_SWITCH 0x2 +#define NPHY_REV3_RFSEQ_CMD_INT_PA_PU 0x3 +#define NPHY_REV3_RFSEQ_CMD_EXT_PA 0x4 +#define NPHY_REV3_RFSEQ_CMD_RXPD_TXPD 0x5 +#define NPHY_REV3_RFSEQ_CMD_TX_GAIN 0x6 +#define NPHY_REV3_RFSEQ_CMD_RX_GAIN 0x7 +#define NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS 0x8 +#define NPHY_REV3_RFSEQ_CMD_SET_HPF_H_HPC 0x9 +#define NPHY_REV3_RFSEQ_CMD_SET_LPF_H_HPC 0xa +#define NPHY_REV3_RFSEQ_CMD_SET_HPF_M_HPC 0xb +#define NPHY_REV3_RFSEQ_CMD_SET_LPF_M_HPC 0xc +#define NPHY_REV3_RFSEQ_CMD_SET_HPF_L_HPC 0xd +#define NPHY_REV3_RFSEQ_CMD_SET_LPF_L_HPC 0xe +#define NPHY_REV3_RFSEQ_CMD_CLR_RXRX_BIAS 0xf +#define NPHY_REV3_RFSEQ_CMD_END 0x1f + +#define NPHY_RSSI_SEL_W1 0x0 +#define NPHY_RSSI_SEL_W2 0x1 +#define NPHY_RSSI_SEL_NB 0x2 +#define NPHY_RSSI_SEL_IQ 0x3 +#define NPHY_RSSI_SEL_TSSI_2G 0x4 +#define NPHY_RSSI_SEL_TSSI_5G 0x5 +#define NPHY_RSSI_SEL_TBD 0x6 + +#define NPHY_RAIL_I 0x0 +#define NPHY_RAIL_Q 0x1 + +#define NPHY_FORCESIG_DECODEGATEDCLKS 0x8 + +#define NPHY_REV7_RfctrlOverride_cmd_rxrf_pu 0x0 +#define NPHY_REV7_RfctrlOverride_cmd_rx_pu 0x1 +#define NPHY_REV7_RfctrlOverride_cmd_tx_pu 0x2 +#define NPHY_REV7_RfctrlOverride_cmd_rxgain 0x3 +#define NPHY_REV7_RfctrlOverride_cmd_txgain 0x4 + +#define NPHY_REV7_RXGAINCODE_RFMXGAIN_MASK 0x000ff +#define NPHY_REV7_RXGAINCODE_LPFGAIN_MASK 0x0ff00 +#define NPHY_REV7_RXGAINCODE_DVGAGAIN_MASK 0xf0000 + +#define NPHY_REV7_TXGAINCODE_TGAIN_MASK 0x7fff +#define NPHY_REV7_TXGAINCODE_LPFGAIN_MASK 0x8000 +#define NPHY_REV7_TXGAINCODE_BIQ0GAIN_SHIFT 14 + +#define NPHY_REV7_RFCTRLOVERRIDE_ID0 0x0 +#define NPHY_REV7_RFCTRLOVERRIDE_ID1 0x1 +#define NPHY_REV7_RFCTRLOVERRIDE_ID2 0x2 + +#define NPHY_IqestIqAccLo(core) ((core == 0) ? 0x12c : 0x134) + +#define NPHY_IqestIqAccHi(core) ((core == 0) ? 0x12d : 0x135) + +#define NPHY_IqestipwrAccLo(core) ((core == 0) ? 0x12e : 0x136) + +#define NPHY_IqestipwrAccHi(core) ((core == 0) ? 0x12f : 0x137) + +#define NPHY_IqestqpwrAccLo(core) ((core == 0) ? 0x130 : 0x138) + +#define NPHY_IqestqpwrAccHi(core) ((core == 0) ? 0x131 : 0x139) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.c new file mode 100644 index 000000000000..d7fa312214f3 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.c @@ -0,0 +1,3293 @@ +/* + * Copyright (c) 2010 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 "phytbl_lcn.h" + +static const u32 dot11lcn_gain_tbl_rev0[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000004, + 0x00000000, + 0x00000004, + 0x00000008, + 0x00000001, + 0x00000005, + 0x00000009, + 0x0000000d, + 0x0000004d, + 0x0000008d, + 0x0000000d, + 0x0000004d, + 0x0000008d, + 0x000000cd, + 0x0000004f, + 0x0000008f, + 0x000000cf, + 0x000000d3, + 0x00000113, + 0x00000513, + 0x00000913, + 0x00000953, + 0x00000d53, + 0x00001153, + 0x00001193, + 0x00005193, + 0x00009193, + 0x0000d193, + 0x00011193, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000004, + 0x00000000, + 0x00000004, + 0x00000008, + 0x00000001, + 0x00000005, + 0x00000009, + 0x0000000d, + 0x0000004d, + 0x0000008d, + 0x0000000d, + 0x0000004d, + 0x0000008d, + 0x000000cd, + 0x0000004f, + 0x0000008f, + 0x000000cf, + 0x000000d3, + 0x00000113, + 0x00000513, + 0x00000913, + 0x00000953, + 0x00000d53, + 0x00001153, + 0x00005153, + 0x00009153, + 0x0000d153, + 0x00011153, + 0x00015153, + 0x00019153, + 0x0001d153, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 dot11lcn_gain_tbl_rev1[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000008, + 0x00000004, + 0x00000008, + 0x00000001, + 0x00000005, + 0x00000009, + 0x0000000D, + 0x00000011, + 0x00000051, + 0x00000091, + 0x00000011, + 0x00000051, + 0x00000091, + 0x000000d1, + 0x00000053, + 0x00000093, + 0x000000d3, + 0x000000d7, + 0x00000117, + 0x00000517, + 0x00000917, + 0x00000957, + 0x00000d57, + 0x00001157, + 0x00001197, + 0x00005197, + 0x00009197, + 0x0000d197, + 0x00011197, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000008, + 0x00000004, + 0x00000008, + 0x00000001, + 0x00000005, + 0x00000009, + 0x0000000D, + 0x00000011, + 0x00000051, + 0x00000091, + 0x00000011, + 0x00000051, + 0x00000091, + 0x000000d1, + 0x00000053, + 0x00000093, + 0x000000d3, + 0x000000d7, + 0x00000117, + 0x00000517, + 0x00000917, + 0x00000957, + 0x00000d57, + 0x00001157, + 0x00005157, + 0x00009157, + 0x0000d157, + 0x00011157, + 0x00015157, + 0x00019157, + 0x0001d157, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u16 dot11lcn_aux_gain_idx_tbl_rev0[] = { + 0x0401, + 0x0402, + 0x0403, + 0x0404, + 0x0405, + 0x0406, + 0x0407, + 0x0408, + 0x0409, + 0x040a, + 0x058b, + 0x058c, + 0x058d, + 0x058e, + 0x058f, + 0x0090, + 0x0091, + 0x0092, + 0x0193, + 0x0194, + 0x0195, + 0x0196, + 0x0197, + 0x0198, + 0x0199, + 0x019a, + 0x019b, + 0x019c, + 0x019d, + 0x019e, + 0x019f, + 0x01a0, + 0x01a1, + 0x01a2, + 0x01a3, + 0x01a4, + 0x01a5, + 0x0000, +}; + +static const u32 dot11lcn_gain_idx_tbl_rev0[] = { + 0x00000000, + 0x00000000, + 0x10000000, + 0x00000000, + 0x20000000, + 0x00000000, + 0x30000000, + 0x00000000, + 0x40000000, + 0x00000000, + 0x50000000, + 0x00000000, + 0x60000000, + 0x00000000, + 0x70000000, + 0x00000000, + 0x80000000, + 0x00000000, + 0x90000000, + 0x00000008, + 0xa0000000, + 0x00000008, + 0xb0000000, + 0x00000008, + 0xc0000000, + 0x00000008, + 0xd0000000, + 0x00000008, + 0xe0000000, + 0x00000008, + 0xf0000000, + 0x00000008, + 0x00000000, + 0x00000009, + 0x10000000, + 0x00000009, + 0x20000000, + 0x00000019, + 0x30000000, + 0x00000019, + 0x40000000, + 0x00000019, + 0x50000000, + 0x00000019, + 0x60000000, + 0x00000019, + 0x70000000, + 0x00000019, + 0x80000000, + 0x00000019, + 0x90000000, + 0x00000019, + 0xa0000000, + 0x00000019, + 0xb0000000, + 0x00000019, + 0xc0000000, + 0x00000019, + 0xd0000000, + 0x00000019, + 0xe0000000, + 0x00000019, + 0xf0000000, + 0x00000019, + 0x00000000, + 0x0000001a, + 0x10000000, + 0x0000001a, + 0x20000000, + 0x0000001a, + 0x30000000, + 0x0000001a, + 0x40000000, + 0x0000001a, + 0x50000000, + 0x00000002, + 0x60000000, + 0x00000002, + 0x70000000, + 0x00000002, + 0x80000000, + 0x00000002, + 0x90000000, + 0x00000002, + 0xa0000000, + 0x00000002, + 0xb0000000, + 0x00000002, + 0xc0000000, + 0x0000000a, + 0xd0000000, + 0x0000000a, + 0xe0000000, + 0x0000000a, + 0xf0000000, + 0x0000000a, + 0x00000000, + 0x0000000b, + 0x10000000, + 0x0000000b, + 0x20000000, + 0x0000000b, + 0x30000000, + 0x0000000b, + 0x40000000, + 0x0000000b, + 0x50000000, + 0x0000001b, + 0x60000000, + 0x0000001b, + 0x70000000, + 0x0000001b, + 0x80000000, + 0x0000001b, + 0x90000000, + 0x0000001b, + 0xa0000000, + 0x0000001b, + 0xb0000000, + 0x0000001b, + 0xc0000000, + 0x0000001b, + 0xd0000000, + 0x0000001b, + 0xe0000000, + 0x0000001b, + 0xf0000000, + 0x0000001b, + 0x00000000, + 0x0000001c, + 0x10000000, + 0x0000001c, + 0x20000000, + 0x0000001c, + 0x30000000, + 0x0000001c, + 0x40000000, + 0x0000001c, + 0x50000000, + 0x0000001c, + 0x60000000, + 0x0000001c, + 0x70000000, + 0x0000001c, + 0x80000000, + 0x0000001c, + 0x90000000, + 0x0000001c, +}; + +static const u16 dot11lcn_aux_gain_idx_tbl_2G[] = { + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0001, + 0x0080, + 0x0081, + 0x0100, + 0x0101, + 0x0180, + 0x0181, + 0x0182, + 0x0183, + 0x0184, + 0x0185, + 0x0186, + 0x0187, + 0x0188, + 0x0285, + 0x0289, + 0x028a, + 0x028b, + 0x028c, + 0x028d, + 0x028e, + 0x028f, + 0x0290, + 0x0291, + 0x0292, + 0x0293, + 0x0294, + 0x0295, + 0x0296, + 0x0297, + 0x0298, + 0x0299, + 0x029a, + 0x0000 +}; + +static const u8 dot11lcn_gain_val_tbl_2G[] = { + 0xfc, + 0x02, + 0x08, + 0x0e, + 0x13, + 0x1b, + 0xfc, + 0x02, + 0x08, + 0x0e, + 0x13, + 0x1b, + 0xfc, + 0x00, + 0x0c, + 0x03, + 0xeb, + 0xfe, + 0x07, + 0x0b, + 0x0f, + 0xfb, + 0xfe, + 0x01, + 0x05, + 0x08, + 0x0b, + 0x0e, + 0x11, + 0x14, + 0x17, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0x06, + 0x09, + 0x0c, + 0x0f, + 0x12, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0x06, + 0x09, + 0x0c, + 0x0f, + 0x12, + 0x15, + 0x18, + 0x1b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00 +}; + +static const u32 dot11lcn_gain_idx_tbl_2G[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x10000000, + 0x00000000, + 0x00000000, + 0x00000008, + 0x10000000, + 0x00000008, + 0x00000000, + 0x00000010, + 0x10000000, + 0x00000010, + 0x00000000, + 0x00000018, + 0x10000000, + 0x00000018, + 0x20000000, + 0x00000018, + 0x30000000, + 0x00000018, + 0x40000000, + 0x00000018, + 0x50000000, + 0x00000018, + 0x60000000, + 0x00000018, + 0x70000000, + 0x00000018, + 0x80000000, + 0x00000018, + 0x50000000, + 0x00000028, + 0x90000000, + 0x00000028, + 0xa0000000, + 0x00000028, + 0xb0000000, + 0x00000028, + 0xc0000000, + 0x00000028, + 0xd0000000, + 0x00000028, + 0xe0000000, + 0x00000028, + 0xf0000000, + 0x00000028, + 0x00000000, + 0x00000029, + 0x10000000, + 0x00000029, + 0x20000000, + 0x00000029, + 0x30000000, + 0x00000029, + 0x40000000, + 0x00000029, + 0x50000000, + 0x00000029, + 0x60000000, + 0x00000029, + 0x70000000, + 0x00000029, + 0x80000000, + 0x00000029, + 0x90000000, + 0x00000029, + 0xa0000000, + 0x00000029, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x10000000, + 0x00000000, + 0x00000000, + 0x00000008, + 0x10000000, + 0x00000008, + 0x00000000, + 0x00000010, + 0x10000000, + 0x00000010, + 0x00000000, + 0x00000018, + 0x10000000, + 0x00000018, + 0x20000000, + 0x00000018, + 0x30000000, + 0x00000018, + 0x40000000, + 0x00000018, + 0x50000000, + 0x00000018, + 0x60000000, + 0x00000018, + 0x70000000, + 0x00000018, + 0x80000000, + 0x00000018, + 0x50000000, + 0x00000028, + 0x90000000, + 0x00000028, + 0xa0000000, + 0x00000028, + 0xb0000000, + 0x00000028, + 0xc0000000, + 0x00000028, + 0xd0000000, + 0x00000028, + 0xe0000000, + 0x00000028, + 0xf0000000, + 0x00000028, + 0x00000000, + 0x00000029, + 0x10000000, + 0x00000029, + 0x20000000, + 0x00000029, + 0x30000000, + 0x00000029, + 0x40000000, + 0x00000029, + 0x50000000, + 0x00000029, + 0x60000000, + 0x00000029, + 0x70000000, + 0x00000029, + 0x80000000, + 0x00000029, + 0x90000000, + 0x00000029, + 0xa0000000, + 0x00000029, + 0xb0000000, + 0x00000029, + 0xc0000000, + 0x00000029, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000 +}; + +static const u32 dot11lcn_gain_tbl_2G[] = { + 0x00000000, + 0x00000004, + 0x00000008, + 0x00000001, + 0x00000005, + 0x00000009, + 0x0000000d, + 0x0000004d, + 0x0000008d, + 0x00000049, + 0x00000089, + 0x000000c9, + 0x0000004b, + 0x0000008b, + 0x000000cb, + 0x000000cf, + 0x0000010f, + 0x0000050f, + 0x0000090f, + 0x0000094f, + 0x00000d4f, + 0x0000114f, + 0x0000118f, + 0x0000518f, + 0x0000918f, + 0x0000d18f, + 0x0001118f, + 0x0001518f, + 0x0001918f, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000 +}; + +static const u32 dot11lcn_gain_tbl_extlna_2G[] = { + 0x00000000, + 0x00000004, + 0x00000008, + 0x00000001, + 0x00000005, + 0x00000009, + 0x0000000d, + 0x00000003, + 0x00000007, + 0x0000000b, + 0x0000000f, + 0x0000004f, + 0x0000008f, + 0x000000cf, + 0x0000010f, + 0x0000014f, + 0x0000018f, + 0x0000058f, + 0x0000098f, + 0x00000d8f, + 0x00008000, + 0x00008004, + 0x00008008, + 0x00008001, + 0x00008005, + 0x00008009, + 0x0000800d, + 0x00008003, + 0x00008007, + 0x0000800b, + 0x0000800f, + 0x0000804f, + 0x0000808f, + 0x000080cf, + 0x0000810f, + 0x0000814f, + 0x0000818f, + 0x0000858f, + 0x0000898f, + 0x00008d8f, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000 +}; + +static const u16 dot11lcn_aux_gain_idx_tbl_extlna_2G[] = { + 0x0400, + 0x0400, + 0x0400, + 0x0400, + 0x0400, + 0x0400, + 0x0400, + 0x0400, + 0x0400, + 0x0401, + 0x0402, + 0x0403, + 0x0404, + 0x0483, + 0x0484, + 0x0485, + 0x0486, + 0x0583, + 0x0584, + 0x0585, + 0x0587, + 0x0588, + 0x0589, + 0x058a, + 0x0687, + 0x0688, + 0x0689, + 0x068a, + 0x068b, + 0x068c, + 0x068d, + 0x068e, + 0x068f, + 0x0690, + 0x0691, + 0x0692, + 0x0693, + 0x0000 +}; + +static const u8 dot11lcn_gain_val_tbl_extlna_2G[] = { + 0xfc, + 0x02, + 0x08, + 0x0e, + 0x13, + 0x1b, + 0xfc, + 0x02, + 0x08, + 0x0e, + 0x13, + 0x1b, + 0xfc, + 0x00, + 0x0f, + 0x03, + 0xeb, + 0xfe, + 0x07, + 0x0b, + 0x0f, + 0xfb, + 0xfe, + 0x01, + 0x05, + 0x08, + 0x0b, + 0x0e, + 0x11, + 0x14, + 0x17, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0x06, + 0x09, + 0x0c, + 0x0f, + 0x12, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0x06, + 0x09, + 0x0c, + 0x0f, + 0x12, + 0x15, + 0x18, + 0x1b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00 +}; + +static const u32 dot11lcn_gain_idx_tbl_extlna_2G[] = { + 0x00000000, + 0x00000040, + 0x00000000, + 0x00000040, + 0x00000000, + 0x00000040, + 0x00000000, + 0x00000040, + 0x00000000, + 0x00000040, + 0x00000000, + 0x00000040, + 0x00000000, + 0x00000040, + 0x00000000, + 0x00000040, + 0x00000000, + 0x00000040, + 0x10000000, + 0x00000040, + 0x20000000, + 0x00000040, + 0x30000000, + 0x00000040, + 0x40000000, + 0x00000040, + 0x30000000, + 0x00000048, + 0x40000000, + 0x00000048, + 0x50000000, + 0x00000048, + 0x60000000, + 0x00000048, + 0x30000000, + 0x00000058, + 0x40000000, + 0x00000058, + 0x50000000, + 0x00000058, + 0x70000000, + 0x00000058, + 0x80000000, + 0x00000058, + 0x90000000, + 0x00000058, + 0xa0000000, + 0x00000058, + 0x70000000, + 0x00000068, + 0x80000000, + 0x00000068, + 0x90000000, + 0x00000068, + 0xa0000000, + 0x00000068, + 0xb0000000, + 0x00000068, + 0xc0000000, + 0x00000068, + 0xd0000000, + 0x00000068, + 0xe0000000, + 0x00000068, + 0xf0000000, + 0x00000068, + 0x00000000, + 0x00000069, + 0x10000000, + 0x00000069, + 0x20000000, + 0x00000069, + 0x30000000, + 0x00000069, + 0x40000000, + 0x00000041, + 0x40000000, + 0x00000041, + 0x40000000, + 0x00000041, + 0x40000000, + 0x00000041, + 0x40000000, + 0x00000041, + 0x40000000, + 0x00000041, + 0x40000000, + 0x00000041, + 0x40000000, + 0x00000041, + 0x40000000, + 0x00000041, + 0x50000000, + 0x00000041, + 0x60000000, + 0x00000041, + 0x70000000, + 0x00000041, + 0x80000000, + 0x00000041, + 0x70000000, + 0x00000049, + 0x80000000, + 0x00000049, + 0x90000000, + 0x00000049, + 0xa0000000, + 0x00000049, + 0x70000000, + 0x00000059, + 0x80000000, + 0x00000059, + 0x90000000, + 0x00000059, + 0xb0000000, + 0x00000059, + 0xc0000000, + 0x00000059, + 0xd0000000, + 0x00000059, + 0xe0000000, + 0x00000059, + 0xb0000000, + 0x00000069, + 0xc0000000, + 0x00000069, + 0xd0000000, + 0x00000069, + 0xe0000000, + 0x00000069, + 0xf0000000, + 0x00000069, + 0x00000000, + 0x0000006a, + 0x10000000, + 0x0000006a, + 0x20000000, + 0x0000006a, + 0x30000000, + 0x0000006a, + 0x40000000, + 0x0000006a, + 0x50000000, + 0x0000006a, + 0x60000000, + 0x0000006a, + 0x70000000, + 0x0000006a, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000 +}; + +static const u32 dot11lcn_aux_gain_idx_tbl_5G[] = { + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0001, + 0x0002, + 0x0003, + 0x0004, + 0x0083, + 0x0084, + 0x0085, + 0x0086, + 0x0087, + 0x0186, + 0x0187, + 0x0188, + 0x0189, + 0x018a, + 0x018b, + 0x018c, + 0x018d, + 0x018e, + 0x018f, + 0x0190, + 0x0191, + 0x0192, + 0x0193, + 0x0194, + 0x0195, + 0x0196, + 0x0197, + 0x0198, + 0x0199, + 0x019a, + 0x019b, + 0x019c, + 0x019d, + 0x0000 +}; + +static const u32 dot11lcn_gain_val_tbl_5G[] = { + 0xf7, + 0xfd, + 0x00, + 0x04, + 0x04, + 0x04, + 0xf7, + 0xfd, + 0x00, + 0x04, + 0x04, + 0x04, + 0xf6, + 0x00, + 0x0c, + 0x03, + 0xeb, + 0xfe, + 0x06, + 0x0a, + 0x10, + 0x00, + 0x03, + 0x06, + 0x09, + 0x0c, + 0x0f, + 0x12, + 0x15, + 0x18, + 0x1b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0x06, + 0x09, + 0x0c, + 0x0f, + 0x12, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0x06, + 0x09, + 0x0c, + 0x0f, + 0x12, + 0x15, + 0x18, + 0x1b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00 +}; + +static const u32 dot11lcn_gain_idx_tbl_5G[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x10000000, + 0x00000000, + 0x20000000, + 0x00000000, + 0x30000000, + 0x00000000, + 0x40000000, + 0x00000000, + 0x30000000, + 0x00000008, + 0x40000000, + 0x00000008, + 0x50000000, + 0x00000008, + 0x60000000, + 0x00000008, + 0x70000000, + 0x00000008, + 0x60000000, + 0x00000018, + 0x70000000, + 0x00000018, + 0x80000000, + 0x00000018, + 0x90000000, + 0x00000018, + 0xa0000000, + 0x00000018, + 0xb0000000, + 0x00000018, + 0xc0000000, + 0x00000018, + 0xd0000000, + 0x00000018, + 0xe0000000, + 0x00000018, + 0xf0000000, + 0x00000018, + 0x00000000, + 0x00000019, + 0x10000000, + 0x00000019, + 0x20000000, + 0x00000019, + 0x30000000, + 0x00000019, + 0x40000000, + 0x00000019, + 0x50000000, + 0x00000019, + 0x60000000, + 0x00000019, + 0x70000000, + 0x00000019, + 0x80000000, + 0x00000019, + 0x90000000, + 0x00000019, + 0xa0000000, + 0x00000019, + 0xb0000000, + 0x00000019, + 0xc0000000, + 0x00000019, + 0xd0000000, + 0x00000019, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000 +}; + +static const u32 dot11lcn_gain_tbl_5G[] = { + 0x00000000, + 0x00000040, + 0x00000080, + 0x00000001, + 0x00000005, + 0x00000009, + 0x0000000d, + 0x00000011, + 0x00000015, + 0x00000055, + 0x00000095, + 0x00000017, + 0x0000001b, + 0x0000005b, + 0x0000009b, + 0x000000db, + 0x0000011b, + 0x0000015b, + 0x0000019b, + 0x0000059b, + 0x0000099b, + 0x00000d9b, + 0x0000119b, + 0x0000519b, + 0x0000919b, + 0x0000d19b, + 0x0001119b, + 0x0001519b, + 0x0001919b, + 0x0001d19b, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000 +}; + +const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[] = { + {&dot11lcn_gain_tbl_rev0, + ARRAY_SIZE(dot11lcn_gain_tbl_rev0), 18, + 0, 32} + , + {&dot11lcn_aux_gain_idx_tbl_rev0, + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16} + , + {&dot11lcn_gain_idx_tbl_rev0, + ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32} + , +}; + +static const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev1[] = { + {&dot11lcn_gain_tbl_rev1, + ARRAY_SIZE(dot11lcn_gain_tbl_rev1), 18, + 0, 32} + , + {&dot11lcn_aux_gain_idx_tbl_rev0, + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16} + , + {&dot11lcn_gain_idx_tbl_rev0, + ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32} + , +}; + +const struct phytbl_info dot11lcnphytbl_rx_gain_info_2G_rev2[] = { + {&dot11lcn_gain_tbl_2G, + ARRAY_SIZE(dot11lcn_gain_tbl_2G), 18, 0, + 32} + , + {&dot11lcn_aux_gain_idx_tbl_2G, + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_2G), 14, 0, 16} + , + {&dot11lcn_gain_idx_tbl_2G, + ARRAY_SIZE(dot11lcn_gain_idx_tbl_2G), + 13, 0, 32} + , + {&dot11lcn_gain_val_tbl_2G, + ARRAY_SIZE(dot11lcn_gain_val_tbl_2G), + 17, 0, 8} +}; + +const struct phytbl_info dot11lcnphytbl_rx_gain_info_5G_rev2[] = { + {&dot11lcn_gain_tbl_5G, + ARRAY_SIZE(dot11lcn_gain_tbl_5G), 18, 0, + 32} + , + {&dot11lcn_aux_gain_idx_tbl_5G, + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_5G), 14, 0, 16} + , + {&dot11lcn_gain_idx_tbl_5G, + ARRAY_SIZE(dot11lcn_gain_idx_tbl_5G), + 13, 0, 32} + , + {&dot11lcn_gain_val_tbl_5G, + ARRAY_SIZE(dot11lcn_gain_val_tbl_5G), + 17, 0, 8} +}; + +const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[] = { + {&dot11lcn_gain_tbl_extlna_2G, + ARRAY_SIZE(dot11lcn_gain_tbl_extlna_2G), 18, 0, 32} + , + {&dot11lcn_aux_gain_idx_tbl_extlna_2G, + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_extlna_2G), 14, 0, 16} + , + {&dot11lcn_gain_idx_tbl_extlna_2G, + ARRAY_SIZE(dot11lcn_gain_idx_tbl_extlna_2G), 13, 0, 32} + , + {&dot11lcn_gain_val_tbl_extlna_2G, + ARRAY_SIZE(dot11lcn_gain_val_tbl_extlna_2G), 17, 0, 8} +}; + +const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[] = { + {&dot11lcn_gain_tbl_5G, + ARRAY_SIZE(dot11lcn_gain_tbl_5G), 18, 0, + 32} + , + {&dot11lcn_aux_gain_idx_tbl_5G, + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_5G), 14, 0, 16} + , + {&dot11lcn_gain_idx_tbl_5G, + ARRAY_SIZE(dot11lcn_gain_idx_tbl_5G), + 13, 0, 32} + , + {&dot11lcn_gain_val_tbl_5G, + ARRAY_SIZE(dot11lcn_gain_val_tbl_5G), + 17, 0, 8} +}; + +const u32 dot11lcnphytbl_rx_gain_info_sz_rev0 = + ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_rev0); + +const u32 dot11lcnphytbl_rx_gain_info_2G_rev2_sz = + ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_2G_rev2); + +const u32 dot11lcnphytbl_rx_gain_info_5G_rev2_sz = + ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_5G_rev2); + +static const u16 dot11lcn_min_sig_sq_tbl_rev0[] = { + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, + 0x014d, +}; + +static const u16 dot11lcn_noise_scale_tbl_rev0[] = { + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, +}; + +static const u32 dot11lcn_fltr_ctrl_tbl_rev0[] = { + 0x000141f8, + 0x000021f8, + 0x000021fb, + 0x000041fb, + 0x0001fe4b, + 0x0000217b, + 0x00002133, + 0x000040eb, + 0x0001fea3, + 0x0000024b, +}; + +static const u32 dot11lcn_ps_ctrl_tbl_rev0[] = { + 0x00100001, + 0x00200010, + 0x00300001, + 0x00400010, + 0x00500022, + 0x00600122, + 0x00700222, + 0x00800322, + 0x00900422, + 0x00a00522, + 0x00b00622, + 0x00c00722, + 0x00d00822, + 0x00f00922, + 0x00100a22, + 0x00200b22, + 0x00300c22, + 0x00400d22, + 0x00500e22, + 0x00600f22, +}; + +static const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[] = { + 0x0007, + 0x0005, + 0x0006, + 0x0004, + 0x0007, + 0x0005, + 0x0006, + 0x0004, + 0x0007, + 0x0005, + 0x0006, + 0x0004, + 0x0007, + 0x0005, + 0x0006, + 0x0004, + 0x000b, + 0x000b, + 0x000a, + 0x000a, + 0x000b, + 0x000b, + 0x000a, + 0x000a, + 0x000b, + 0x000b, + 0x000a, + 0x000a, + 0x000b, + 0x000b, + 0x000a, + 0x000a, + 0x0007, + 0x0005, + 0x0006, + 0x0004, + 0x0007, + 0x0005, + 0x0006, + 0x0004, + 0x0007, + 0x0005, + 0x0006, + 0x0004, + 0x0007, + 0x0005, + 0x0006, + 0x0004, + 0x000b, + 0x000b, + 0x000a, + 0x000a, + 0x000b, + 0x000b, + 0x000a, + 0x000a, + 0x000b, + 0x000b, + 0x000a, + 0x000a, + 0x000b, + 0x000b, + 0x000a, + 0x000a, + +}; + +static const u16 dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[] = { + 0x0007, + 0x0005, + 0x0002, + 0x0000, + 0x0007, + 0x0005, + 0x0002, + 0x0000, + 0x0007, + 0x0005, + 0x0002, + 0x0000, + 0x0007, + 0x0005, + 0x0002, + 0x0000, + 0x0007, + 0x0007, + 0x0002, + 0x0002, + 0x0007, + 0x0007, + 0x0002, + 0x0002, + 0x0007, + 0x0007, + 0x0002, + 0x0002, + 0x0007, + 0x0007, + 0x0002, + 0x0002, + 0x0007, + 0x0005, + 0x0002, + 0x0000, + 0x0007, + 0x0005, + 0x0002, + 0x0000, + 0x0007, + 0x0005, + 0x0002, + 0x0000, + 0x0007, + 0x0005, + 0x0002, + 0x0000, + 0x0007, + 0x0007, + 0x0002, + 0x0002, + 0x0007, + 0x0007, + 0x0002, + 0x0002, + 0x0007, + 0x0007, + 0x0002, + 0x0002, + 0x0007, + 0x0007, + 0x0002, + 0x0002, +}; + +static const u16 dot11lcn_sw_ctrl_tbl_4313_epa_rev0[] = { + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, + 0x0002, + 0x0008, + 0x0004, + 0x0001, +}; + +static const u16 dot11lcn_sw_ctrl_tbl_4313_rev0[] = { + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, + 0x000a, + 0x0009, + 0x0006, + 0x0005, +}; + +static const u16 dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo[] = { + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, + 0x0005, + 0x0006, + 0x0009, + 0x000a, +}; + +static const u16 dot11lcn_sw_ctrl_tbl_rev0[] = { + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, + 0x0004, + 0x0004, + 0x0002, + 0x0002, +}; + +static const u8 dot11lcn_nf_table_rev0[] = { + 0x5f, + 0x36, + 0x29, + 0x1f, + 0x5f, + 0x36, + 0x29, + 0x1f, + 0x5f, + 0x36, + 0x29, + 0x1f, + 0x5f, + 0x36, + 0x29, + 0x1f, +}; + +static const u8 dot11lcn_gain_val_tbl_rev0[] = { + 0x09, + 0x0f, + 0x14, + 0x18, + 0xfe, + 0x07, + 0x0b, + 0x0f, + 0xfb, + 0xfe, + 0x01, + 0x05, + 0x08, + 0x0b, + 0x0e, + 0x11, + 0x14, + 0x17, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0x06, + 0x09, + 0x0c, + 0x0f, + 0x12, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0x06, + 0x09, + 0x0c, + 0x0f, + 0x12, + 0x15, + 0x18, + 0x1b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x03, + 0xeb, + 0x00, + 0x00, +}; + +static const u8 dot11lcn_spur_tbl_rev0[] = { + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x02, + 0x03, + 0x01, + 0x03, + 0x02, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x02, + 0x03, + 0x01, + 0x03, + 0x02, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, + 0x01, +}; + +static const u16 dot11lcn_unsup_mcs_tbl_rev0[] = { + 0x001a, + 0x0034, + 0x004e, + 0x0068, + 0x009c, + 0x00d0, + 0x00ea, + 0x0104, + 0x0034, + 0x0068, + 0x009c, + 0x00d0, + 0x0138, + 0x01a0, + 0x01d4, + 0x0208, + 0x004e, + 0x009c, + 0x00ea, + 0x0138, + 0x01d4, + 0x0270, + 0x02be, + 0x030c, + 0x0068, + 0x00d0, + 0x0138, + 0x01a0, + 0x0270, + 0x0340, + 0x03a8, + 0x0410, + 0x0018, + 0x009c, + 0x00d0, + 0x0104, + 0x00ea, + 0x0138, + 0x0186, + 0x00d0, + 0x0104, + 0x0104, + 0x0138, + 0x016c, + 0x016c, + 0x01a0, + 0x0138, + 0x0186, + 0x0186, + 0x01d4, + 0x0222, + 0x0222, + 0x0270, + 0x0104, + 0x0138, + 0x016c, + 0x0138, + 0x016c, + 0x01a0, + 0x01d4, + 0x01a0, + 0x01d4, + 0x0208, + 0x0208, + 0x023c, + 0x0186, + 0x01d4, + 0x0222, + 0x01d4, + 0x0222, + 0x0270, + 0x02be, + 0x0270, + 0x02be, + 0x030c, + 0x030c, + 0x035a, + 0x0036, + 0x006c, + 0x00a2, + 0x00d8, + 0x0144, + 0x01b0, + 0x01e6, + 0x021c, + 0x006c, + 0x00d8, + 0x0144, + 0x01b0, + 0x0288, + 0x0360, + 0x03cc, + 0x0438, + 0x00a2, + 0x0144, + 0x01e6, + 0x0288, + 0x03cc, + 0x0510, + 0x05b2, + 0x0654, + 0x00d8, + 0x01b0, + 0x0288, + 0x0360, + 0x0510, + 0x06c0, + 0x0798, + 0x0870, + 0x0018, + 0x0144, + 0x01b0, + 0x021c, + 0x01e6, + 0x0288, + 0x032a, + 0x01b0, + 0x021c, + 0x021c, + 0x0288, + 0x02f4, + 0x02f4, + 0x0360, + 0x0288, + 0x032a, + 0x032a, + 0x03cc, + 0x046e, + 0x046e, + 0x0510, + 0x021c, + 0x0288, + 0x02f4, + 0x0288, + 0x02f4, + 0x0360, + 0x03cc, + 0x0360, + 0x03cc, + 0x0438, + 0x0438, + 0x04a4, + 0x032a, + 0x03cc, + 0x046e, + 0x03cc, + 0x046e, + 0x0510, + 0x05b2, + 0x0510, + 0x05b2, + 0x0654, + 0x0654, + 0x06f6, +}; + +static const u16 dot11lcn_iq_local_tbl_rev0[] = { + 0x0200, + 0x0300, + 0x0400, + 0x0600, + 0x0800, + 0x0b00, + 0x1000, + 0x1001, + 0x1002, + 0x1003, + 0x1004, + 0x1005, + 0x1006, + 0x1007, + 0x1707, + 0x2007, + 0x2d07, + 0x4007, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0200, + 0x0300, + 0x0400, + 0x0600, + 0x0800, + 0x0b00, + 0x1000, + 0x1001, + 0x1002, + 0x1003, + 0x1004, + 0x1005, + 0x1006, + 0x1007, + 0x1707, + 0x2007, + 0x2d07, + 0x4007, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x4000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, +}; + +static const u32 dot11lcn_papd_compdelta_tbl_rev0[] = { + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, + 0x00080000, +}; + +const struct phytbl_info dot11lcnphytbl_info_rev0[] = { + {&dot11lcn_min_sig_sq_tbl_rev0, + ARRAY_SIZE(dot11lcn_min_sig_sq_tbl_rev0), 2, 0, 16} + , + {&dot11lcn_noise_scale_tbl_rev0, + ARRAY_SIZE(dot11lcn_noise_scale_tbl_rev0), 1, 0, 16} + , + {&dot11lcn_fltr_ctrl_tbl_rev0, + ARRAY_SIZE(dot11lcn_fltr_ctrl_tbl_rev0), 11, 0, 32} + , + {&dot11lcn_ps_ctrl_tbl_rev0, + ARRAY_SIZE(dot11lcn_ps_ctrl_tbl_rev0), 12, 0, 32} + , + {&dot11lcn_gain_idx_tbl_rev0, + ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32} + , + {&dot11lcn_aux_gain_idx_tbl_rev0, + ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16} + , + {&dot11lcn_sw_ctrl_tbl_rev0, + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_rev0), 15, 0, 16} + , + {&dot11lcn_nf_table_rev0, + ARRAY_SIZE(dot11lcn_nf_table_rev0), 16, + 0, 8} + , + {&dot11lcn_gain_val_tbl_rev0, + ARRAY_SIZE(dot11lcn_gain_val_tbl_rev0), 17, 0, 8} + , + {&dot11lcn_gain_tbl_rev0, + ARRAY_SIZE(dot11lcn_gain_tbl_rev0), 18, + 0, 32} + , + {&dot11lcn_spur_tbl_rev0, + ARRAY_SIZE(dot11lcn_spur_tbl_rev0), 20, + 0, 8} + , + {&dot11lcn_unsup_mcs_tbl_rev0, + ARRAY_SIZE(dot11lcn_unsup_mcs_tbl_rev0), 23, 0, 16} + , + {&dot11lcn_iq_local_tbl_rev0, + ARRAY_SIZE(dot11lcn_iq_local_tbl_rev0), 0, 0, 16} + , + {&dot11lcn_papd_compdelta_tbl_rev0, + ARRAY_SIZE(dot11lcn_papd_compdelta_tbl_rev0), 24, 0, 32} + , +}; + +const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313 = { + &dot11lcn_sw_ctrl_tbl_4313_rev0, + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_rev0), 15, 0, 16 +}; + +const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa = { + &dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo, + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo), 15, 0, 16 +}; + +const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa = { + &dot11lcn_sw_ctrl_tbl_4313_epa_rev0, + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_epa_rev0), 15, 0, 16 +}; + +const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa = { + &dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo, + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo), 15, 0, 16 +}; + +const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250 = { + &dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0, + ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0), 15, 0, 16 +}; + +const u32 dot11lcnphytbl_info_sz_rev0 = + ARRAY_SIZE(dot11lcnphytbl_info_rev0); + +const struct lcnphy_tx_gain_tbl_entry +dot11lcnphy_2GHz_extPA_gaintable_rev0[128] = { + {3, 0, 31, 0, 72}, + {3, 0, 31, 0, 70}, + {3, 0, 31, 0, 68}, + {3, 0, 30, 0, 67}, + {3, 0, 29, 0, 68}, + {3, 0, 28, 0, 68}, + {3, 0, 27, 0, 69}, + {3, 0, 26, 0, 70}, + {3, 0, 25, 0, 70}, + {3, 0, 24, 0, 71}, + {3, 0, 23, 0, 72}, + {3, 0, 23, 0, 70}, + {3, 0, 22, 0, 71}, + {3, 0, 21, 0, 72}, + {3, 0, 21, 0, 70}, + {3, 0, 21, 0, 68}, + {3, 0, 21, 0, 66}, + {3, 0, 21, 0, 64}, + {3, 0, 21, 0, 63}, + {3, 0, 20, 0, 64}, + {3, 0, 19, 0, 65}, + {3, 0, 19, 0, 64}, + {3, 0, 18, 0, 65}, + {3, 0, 18, 0, 64}, + {3, 0, 17, 0, 65}, + {3, 0, 17, 0, 64}, + {3, 0, 16, 0, 65}, + {3, 0, 16, 0, 64}, + {3, 0, 16, 0, 62}, + {3, 0, 16, 0, 60}, + {3, 0, 16, 0, 58}, + {3, 0, 15, 0, 61}, + {3, 0, 15, 0, 59}, + {3, 0, 14, 0, 61}, + {3, 0, 14, 0, 60}, + {3, 0, 14, 0, 58}, + {3, 0, 13, 0, 60}, + {3, 0, 13, 0, 59}, + {3, 0, 12, 0, 62}, + {3, 0, 12, 0, 60}, + {3, 0, 12, 0, 58}, + {3, 0, 11, 0, 62}, + {3, 0, 11, 0, 60}, + {3, 0, 11, 0, 59}, + {3, 0, 11, 0, 57}, + {3, 0, 10, 0, 61}, + {3, 0, 10, 0, 59}, + {3, 0, 10, 0, 57}, + {3, 0, 9, 0, 62}, + {3, 0, 9, 0, 60}, + {3, 0, 9, 0, 58}, + {3, 0, 9, 0, 57}, + {3, 0, 8, 0, 62}, + {3, 0, 8, 0, 60}, + {3, 0, 8, 0, 58}, + {3, 0, 8, 0, 57}, + {3, 0, 8, 0, 55}, + {3, 0, 7, 0, 61}, + {3, 0, 7, 0, 60}, + {3, 0, 7, 0, 58}, + {3, 0, 7, 0, 56}, + {3, 0, 7, 0, 55}, + {3, 0, 6, 0, 62}, + {3, 0, 6, 0, 60}, + {3, 0, 6, 0, 58}, + {3, 0, 6, 0, 57}, + {3, 0, 6, 0, 55}, + {3, 0, 6, 0, 54}, + {3, 0, 6, 0, 52}, + {3, 0, 5, 0, 61}, + {3, 0, 5, 0, 59}, + {3, 0, 5, 0, 57}, + {3, 0, 5, 0, 56}, + {3, 0, 5, 0, 54}, + {3, 0, 5, 0, 53}, + {3, 0, 5, 0, 51}, + {3, 0, 4, 0, 62}, + {3, 0, 4, 0, 60}, + {3, 0, 4, 0, 58}, + {3, 0, 4, 0, 57}, + {3, 0, 4, 0, 55}, + {3, 0, 4, 0, 54}, + {3, 0, 4, 0, 52}, + {3, 0, 4, 0, 51}, + {3, 0, 4, 0, 49}, + {3, 0, 4, 0, 48}, + {3, 0, 4, 0, 46}, + {3, 0, 3, 0, 60}, + {3, 0, 3, 0, 58}, + {3, 0, 3, 0, 57}, + {3, 0, 3, 0, 55}, + {3, 0, 3, 0, 54}, + {3, 0, 3, 0, 52}, + {3, 0, 3, 0, 51}, + {3, 0, 3, 0, 49}, + {3, 0, 3, 0, 48}, + {3, 0, 3, 0, 46}, + {3, 0, 3, 0, 45}, + {3, 0, 3, 0, 44}, + {3, 0, 3, 0, 43}, + {3, 0, 3, 0, 41}, + {3, 0, 2, 0, 61}, + {3, 0, 2, 0, 59}, + {3, 0, 2, 0, 57}, + {3, 0, 2, 0, 56}, + {3, 0, 2, 0, 54}, + {3, 0, 2, 0, 53}, + {3, 0, 2, 0, 51}, + {3, 0, 2, 0, 50}, + {3, 0, 2, 0, 48}, + {3, 0, 2, 0, 47}, + {3, 0, 2, 0, 46}, + {3, 0, 2, 0, 44}, + {3, 0, 2, 0, 43}, + {3, 0, 2, 0, 42}, + {3, 0, 2, 0, 41}, + {3, 0, 2, 0, 39}, + {3, 0, 2, 0, 38}, + {3, 0, 2, 0, 37}, + {3, 0, 2, 0, 36}, + {3, 0, 2, 0, 35}, + {3, 0, 2, 0, 34}, + {3, 0, 2, 0, 33}, + {3, 0, 2, 0, 32}, + {3, 0, 1, 0, 63}, + {3, 0, 1, 0, 61}, + {3, 0, 1, 0, 59}, + {3, 0, 1, 0, 57}, +}; + +const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[128] = { + {15, 0, 31, 0, 72}, + {15, 0, 31, 0, 70}, + {15, 0, 31, 0, 68}, + {15, 0, 30, 0, 68}, + {15, 0, 29, 0, 69}, + {15, 0, 28, 0, 69}, + {15, 0, 27, 0, 70}, + {15, 0, 26, 0, 70}, + {15, 0, 25, 0, 71}, + {15, 0, 24, 0, 72}, + {15, 0, 23, 0, 73}, + {15, 0, 23, 0, 71}, + {15, 0, 22, 0, 72}, + {15, 0, 21, 0, 73}, + {15, 0, 21, 0, 71}, + {15, 0, 21, 0, 69}, + {15, 0, 21, 0, 67}, + {15, 0, 21, 0, 65}, + {15, 0, 21, 0, 63}, + {15, 0, 20, 0, 65}, + {15, 0, 19, 0, 66}, + {15, 0, 19, 0, 64}, + {15, 0, 18, 0, 66}, + {15, 0, 18, 0, 64}, + {15, 0, 17, 0, 66}, + {15, 0, 17, 0, 64}, + {15, 0, 16, 0, 66}, + {15, 0, 16, 0, 64}, + {15, 0, 16, 0, 62}, + {15, 0, 16, 0, 61}, + {15, 0, 16, 0, 59}, + {15, 0, 15, 0, 61}, + {15, 0, 15, 0, 59}, + {15, 0, 14, 0, 62}, + {15, 0, 14, 0, 60}, + {15, 0, 14, 0, 58}, + {15, 0, 13, 0, 61}, + {15, 0, 13, 0, 59}, + {15, 0, 12, 0, 62}, + {15, 0, 12, 0, 61}, + {15, 0, 12, 0, 59}, + {15, 0, 11, 0, 62}, + {15, 0, 11, 0, 61}, + {15, 0, 11, 0, 59}, + {15, 0, 11, 0, 57}, + {15, 0, 10, 0, 61}, + {15, 0, 10, 0, 59}, + {15, 0, 10, 0, 58}, + {15, 0, 9, 0, 62}, + {15, 0, 9, 0, 61}, + {15, 0, 9, 0, 59}, + {15, 0, 9, 0, 57}, + {15, 0, 8, 0, 62}, + {15, 0, 8, 0, 61}, + {15, 0, 8, 0, 59}, + {15, 0, 8, 0, 57}, + {15, 0, 8, 0, 56}, + {15, 0, 8, 0, 54}, + {15, 0, 8, 0, 53}, + {15, 0, 8, 0, 51}, + {15, 0, 8, 0, 50}, + {7, 0, 7, 0, 69}, + {7, 0, 7, 0, 67}, + {7, 0, 7, 0, 65}, + {7, 0, 7, 0, 64}, + {7, 0, 7, 0, 62}, + {7, 0, 7, 0, 60}, + {7, 0, 7, 0, 58}, + {7, 0, 7, 0, 57}, + {7, 0, 7, 0, 55}, + {7, 0, 6, 0, 62}, + {7, 0, 6, 0, 61}, + {7, 0, 6, 0, 59}, + {7, 0, 6, 0, 57}, + {7, 0, 6, 0, 56}, + {7, 0, 6, 0, 54}, + {7, 0, 6, 0, 53}, + {7, 0, 5, 0, 61}, + {7, 0, 5, 0, 60}, + {7, 0, 5, 0, 58}, + {7, 0, 5, 0, 56}, + {7, 0, 5, 0, 55}, + {7, 0, 5, 0, 53}, + {7, 0, 5, 0, 52}, + {7, 0, 5, 0, 50}, + {7, 0, 5, 0, 49}, + {7, 0, 5, 0, 47}, + {7, 0, 4, 0, 57}, + {7, 0, 4, 0, 56}, + {7, 0, 4, 0, 54}, + {7, 0, 4, 0, 53}, + {7, 0, 4, 0, 51}, + {7, 0, 4, 0, 50}, + {7, 0, 4, 0, 48}, + {7, 0, 4, 0, 47}, + {7, 0, 4, 0, 46}, + {7, 0, 4, 0, 44}, + {7, 0, 4, 0, 43}, + {7, 0, 4, 0, 42}, + {7, 0, 4, 0, 41}, + {7, 0, 4, 0, 40}, + {7, 0, 3, 0, 51}, + {7, 0, 3, 0, 50}, + {7, 0, 3, 0, 48}, + {7, 0, 3, 0, 47}, + {7, 0, 3, 0, 46}, + {7, 0, 3, 0, 44}, + {7, 0, 3, 0, 43}, + {7, 0, 3, 0, 42}, + {7, 0, 3, 0, 41}, + {3, 0, 3, 0, 56}, + {3, 0, 3, 0, 54}, + {3, 0, 3, 0, 53}, + {3, 0, 3, 0, 51}, + {3, 0, 3, 0, 50}, + {3, 0, 3, 0, 48}, + {3, 0, 3, 0, 47}, + {3, 0, 3, 0, 46}, + {3, 0, 3, 0, 44}, + {3, 0, 3, 0, 43}, + {3, 0, 3, 0, 42}, + {3, 0, 3, 0, 41}, + {3, 0, 3, 0, 39}, + {3, 0, 3, 0, 38}, + {3, 0, 3, 0, 37}, + {3, 0, 3, 0, 36}, + {3, 0, 3, 0, 35}, + {3, 0, 3, 0, 34}, +}; + +const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[128] = { + {255, 255, 0xf0, 0, 152}, + {255, 255, 0xf0, 0, 147}, + {255, 255, 0xf0, 0, 143}, + {255, 255, 0xf0, 0, 139}, + {255, 255, 0xf0, 0, 135}, + {255, 255, 0xf0, 0, 131}, + {255, 255, 0xf0, 0, 128}, + {255, 255, 0xf0, 0, 124}, + {255, 255, 0xf0, 0, 121}, + {255, 255, 0xf0, 0, 117}, + {255, 255, 0xf0, 0, 114}, + {255, 255, 0xf0, 0, 111}, + {255, 255, 0xf0, 0, 107}, + {255, 255, 0xf0, 0, 104}, + {255, 255, 0xf0, 0, 101}, + {255, 255, 0xf0, 0, 99}, + {255, 255, 0xf0, 0, 96}, + {255, 255, 0xf0, 0, 93}, + {255, 255, 0xf0, 0, 90}, + {255, 255, 0xf0, 0, 88}, + {255, 255, 0xf0, 0, 85}, + {255, 255, 0xf0, 0, 83}, + {255, 255, 0xf0, 0, 81}, + {255, 255, 0xf0, 0, 78}, + {255, 255, 0xf0, 0, 76}, + {255, 255, 0xf0, 0, 74}, + {255, 255, 0xf0, 0, 72}, + {255, 255, 0xf0, 0, 70}, + {255, 255, 0xf0, 0, 68}, + {255, 255, 0xf0, 0, 66}, + {255, 255, 0xf0, 0, 64}, + {255, 248, 0xf0, 0, 64}, + {255, 241, 0xf0, 0, 64}, + {255, 251, 0xe0, 0, 64}, + {255, 244, 0xe0, 0, 64}, + {255, 254, 0xd0, 0, 64}, + {255, 246, 0xd0, 0, 64}, + {255, 239, 0xd0, 0, 64}, + {255, 249, 0xc0, 0, 64}, + {255, 242, 0xc0, 0, 64}, + {255, 255, 0xb0, 0, 64}, + {255, 248, 0xb0, 0, 64}, + {255, 241, 0xb0, 0, 64}, + {255, 254, 0xa0, 0, 64}, + {255, 246, 0xa0, 0, 64}, + {255, 239, 0xa0, 0, 64}, + {255, 255, 0x90, 0, 64}, + {255, 248, 0x90, 0, 64}, + {255, 241, 0x90, 0, 64}, + {255, 234, 0x90, 0, 64}, + {255, 255, 0x80, 0, 64}, + {255, 248, 0x80, 0, 64}, + {255, 241, 0x80, 0, 64}, + {255, 234, 0x80, 0, 64}, + {255, 255, 0x70, 0, 64}, + {255, 248, 0x70, 0, 64}, + {255, 241, 0x70, 0, 64}, + {255, 234, 0x70, 0, 64}, + {255, 227, 0x70, 0, 64}, + {255, 221, 0x70, 0, 64}, + {255, 215, 0x70, 0, 64}, + {255, 208, 0x70, 0, 64}, + {255, 203, 0x70, 0, 64}, + {255, 197, 0x70, 0, 64}, + {255, 255, 0x60, 0, 64}, + {255, 248, 0x60, 0, 64}, + {255, 241, 0x60, 0, 64}, + {255, 234, 0x60, 0, 64}, + {255, 227, 0x60, 0, 64}, + {255, 221, 0x60, 0, 64}, + {255, 255, 0x50, 0, 64}, + {255, 248, 0x50, 0, 64}, + {255, 241, 0x50, 0, 64}, + {255, 234, 0x50, 0, 64}, + {255, 227, 0x50, 0, 64}, + {255, 221, 0x50, 0, 64}, + {255, 215, 0x50, 0, 64}, + {255, 208, 0x50, 0, 64}, + {255, 255, 0x40, 0, 64}, + {255, 248, 0x40, 0, 64}, + {255, 241, 0x40, 0, 64}, + {255, 234, 0x40, 0, 64}, + {255, 227, 0x40, 0, 64}, + {255, 221, 0x40, 0, 64}, + {255, 215, 0x40, 0, 64}, + {255, 208, 0x40, 0, 64}, + {255, 203, 0x40, 0, 64}, + {255, 197, 0x40, 0, 64}, + {255, 255, 0x30, 0, 64}, + {255, 248, 0x30, 0, 64}, + {255, 241, 0x30, 0, 64}, + {255, 234, 0x30, 0, 64}, + {255, 227, 0x30, 0, 64}, + {255, 221, 0x30, 0, 64}, + {255, 215, 0x30, 0, 64}, + {255, 208, 0x30, 0, 64}, + {255, 203, 0x30, 0, 64}, + {255, 197, 0x30, 0, 64}, + {255, 191, 0x30, 0, 64}, + {255, 186, 0x30, 0, 64}, + {255, 181, 0x30, 0, 64}, + {255, 175, 0x30, 0, 64}, + {255, 255, 0x20, 0, 64}, + {255, 248, 0x20, 0, 64}, + {255, 241, 0x20, 0, 64}, + {255, 234, 0x20, 0, 64}, + {255, 227, 0x20, 0, 64}, + {255, 221, 0x20, 0, 64}, + {255, 215, 0x20, 0, 64}, + {255, 208, 0x20, 0, 64}, + {255, 203, 0x20, 0, 64}, + {255, 197, 0x20, 0, 64}, + {255, 191, 0x20, 0, 64}, + {255, 186, 0x20, 0, 64}, + {255, 181, 0x20, 0, 64}, + {255, 175, 0x20, 0, 64}, + {255, 170, 0x20, 0, 64}, + {255, 166, 0x20, 0, 64}, + {255, 161, 0x20, 0, 64}, + {255, 156, 0x20, 0, 64}, + {255, 152, 0x20, 0, 64}, + {255, 148, 0x20, 0, 64}, + {255, 143, 0x20, 0, 64}, + {255, 139, 0x20, 0, 64}, + {255, 135, 0x20, 0, 64}, + {255, 132, 0x20, 0, 64}, + {255, 255, 0x10, 0, 64}, + {255, 248, 0x10, 0, 64}, +}; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.h new file mode 100644 index 000000000000..489422a36085 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010 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 "phy_int.h" + +extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[]; +extern const u32 dot11lcnphytbl_rx_gain_info_sz_rev0; +extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313; +extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa; +extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa; +extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa_combo; +extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa; +extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250; + +extern const struct phytbl_info dot11lcnphytbl_info_rev0[]; +extern const u32 dot11lcnphytbl_info_sz_rev0; + +extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_2G_rev2[]; +extern const u32 dot11lcnphytbl_rx_gain_info_2G_rev2_sz; + +extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_5G_rev2[]; +extern const u32 dot11lcnphytbl_rx_gain_info_5G_rev2_sz; + +extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[]; + +extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[]; + +struct lcnphy_tx_gain_tbl_entry { + unsigned char gm; + unsigned char pga; + unsigned char pad; + unsigned char dac; + unsigned char bb_mult; +}; + +extern const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[]; + +extern const struct +lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_extPA_gaintable_rev0[]; + +extern const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[]; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.c new file mode 100644 index 000000000000..dbf50ef6cd75 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.c @@ -0,0 +1,10630 @@ +/* + * Copyright (c) 2010 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 "phytbl_n.h" + +static const u32 frame_struct_rev0[] = { + 0x08004a04, + 0x00100000, + 0x01000a05, + 0x00100020, + 0x09804506, + 0x00100030, + 0x09804507, + 0x00100030, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x08004a0c, + 0x00100004, + 0x01000a0d, + 0x00100024, + 0x0980450e, + 0x00100034, + 0x0980450f, + 0x00100034, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000a04, + 0x00100000, + 0x11008a05, + 0x00100020, + 0x1980c506, + 0x00100030, + 0x21810506, + 0x00100030, + 0x21810506, + 0x00100030, + 0x01800504, + 0x00100030, + 0x11808505, + 0x00100030, + 0x29814507, + 0x01100030, + 0x00000a04, + 0x00100000, + 0x11008a05, + 0x00100020, + 0x21810506, + 0x00100030, + 0x21810506, + 0x00100030, + 0x29814507, + 0x01100030, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000a0c, + 0x00100008, + 0x11008a0d, + 0x00100028, + 0x1980c50e, + 0x00100038, + 0x2181050e, + 0x00100038, + 0x2181050e, + 0x00100038, + 0x0180050c, + 0x00100038, + 0x1180850d, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000a0c, + 0x00100008, + 0x11008a0d, + 0x00100028, + 0x2181050e, + 0x00100038, + 0x2181050e, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x08004a04, + 0x00100000, + 0x01000a05, + 0x00100020, + 0x1980c506, + 0x00100030, + 0x1980c506, + 0x00100030, + 0x11808504, + 0x00100030, + 0x3981ca05, + 0x00100030, + 0x29814507, + 0x01100030, + 0x00000000, + 0x00000000, + 0x10008a04, + 0x00100000, + 0x3981ca05, + 0x00100030, + 0x1980c506, + 0x00100030, + 0x29814507, + 0x01100030, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x08004a0c, + 0x00100008, + 0x01000a0d, + 0x00100028, + 0x1980c50e, + 0x00100038, + 0x1980c50e, + 0x00100038, + 0x1180850c, + 0x00100038, + 0x3981ca0d, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000000, + 0x00000000, + 0x10008a0c, + 0x00100008, + 0x3981ca0d, + 0x00100038, + 0x1980c50e, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x40021404, + 0x00100000, + 0x02001405, + 0x00100040, + 0x0b004a06, + 0x01900060, + 0x13008a06, + 0x01900060, + 0x13008a06, + 0x01900060, + 0x43020a04, + 0x00100060, + 0x1b00ca05, + 0x00100060, + 0x23010a07, + 0x01500060, + 0x40021404, + 0x00100000, + 0x1a00d405, + 0x00100040, + 0x13008a06, + 0x01900060, + 0x13008a06, + 0x01900060, + 0x23010a07, + 0x01500060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x00100010, + 0x0200140d, + 0x00100050, + 0x0b004a0e, + 0x01900070, + 0x13008a0e, + 0x01900070, + 0x13008a0e, + 0x01900070, + 0x43020a0c, + 0x00100070, + 0x1b00ca0d, + 0x00100070, + 0x23010a0f, + 0x01500070, + 0x4002140c, + 0x00100010, + 0x1a00d40d, + 0x00100050, + 0x13008a0e, + 0x01900070, + 0x13008a0e, + 0x01900070, + 0x23010a0f, + 0x01500070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x50029404, + 0x00100000, + 0x32019405, + 0x00100040, + 0x0b004a06, + 0x01900060, + 0x0b004a06, + 0x01900060, + 0x5b02ca04, + 0x00100060, + 0x3b01d405, + 0x00100060, + 0x23010a07, + 0x01500060, + 0x00000000, + 0x00000000, + 0x5802d404, + 0x00100000, + 0x3b01d405, + 0x00100060, + 0x0b004a06, + 0x01900060, + 0x23010a07, + 0x01500060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x5002940c, + 0x00100010, + 0x3201940d, + 0x00100050, + 0x0b004a0e, + 0x01900070, + 0x0b004a0e, + 0x01900070, + 0x5b02ca0c, + 0x00100070, + 0x3b01d40d, + 0x00100070, + 0x23010a0f, + 0x01500070, + 0x00000000, + 0x00000000, + 0x5802d40c, + 0x00100010, + 0x3b01d40d, + 0x00100070, + 0x0b004a0e, + 0x01900070, + 0x23010a0f, + 0x01500070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x40021404, + 0x000f4800, + 0x62031405, + 0x00100040, + 0x53028a06, + 0x01900060, + 0x53028a07, + 0x01900060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x000f4808, + 0x6203140d, + 0x00100048, + 0x53028a0e, + 0x01900068, + 0x53028a0f, + 0x01900068, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000a0c, + 0x00100004, + 0x11008a0d, + 0x00100024, + 0x1980c50e, + 0x00100034, + 0x2181050e, + 0x00100034, + 0x2181050e, + 0x00100034, + 0x0180050c, + 0x00100038, + 0x1180850d, + 0x00100038, + 0x1181850d, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000a0c, + 0x00100008, + 0x11008a0d, + 0x00100028, + 0x2181050e, + 0x00100038, + 0x2181050e, + 0x00100038, + 0x1181850d, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x08004a04, + 0x00100000, + 0x01000a05, + 0x00100020, + 0x0180c506, + 0x00100030, + 0x0180c506, + 0x00100030, + 0x2180c50c, + 0x00100030, + 0x49820a0d, + 0x0016a130, + 0x41824a0d, + 0x0016a130, + 0x2981450f, + 0x01100030, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x2000ca0c, + 0x00100000, + 0x49820a0d, + 0x0016a130, + 0x1980c50e, + 0x00100030, + 0x41824a0d, + 0x0016a130, + 0x2981450f, + 0x01100030, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x00100008, + 0x0200140d, + 0x00100048, + 0x0b004a0e, + 0x01900068, + 0x13008a0e, + 0x01900068, + 0x13008a0e, + 0x01900068, + 0x43020a0c, + 0x00100070, + 0x1b00ca0d, + 0x00100070, + 0x1b014a0d, + 0x00100070, + 0x23010a0f, + 0x01500070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x00100010, + 0x1a00d40d, + 0x00100050, + 0x13008a0e, + 0x01900070, + 0x13008a0e, + 0x01900070, + 0x1b014a0d, + 0x00100070, + 0x23010a0f, + 0x01500070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x50029404, + 0x00100000, + 0x32019405, + 0x00100040, + 0x03004a06, + 0x01900060, + 0x03004a06, + 0x01900060, + 0x6b030a0c, + 0x00100060, + 0x4b02140d, + 0x0016a160, + 0x4302540d, + 0x0016a160, + 0x23010a0f, + 0x01500060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x6b03140c, + 0x00100060, + 0x4b02140d, + 0x0016a160, + 0x0b004a0e, + 0x01900060, + 0x4302540d, + 0x0016a160, + 0x23010a0f, + 0x01500060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x40021404, + 0x00100000, + 0x1a00d405, + 0x00100040, + 0x53028a06, + 0x01900060, + 0x5b02ca06, + 0x01900060, + 0x5b02ca06, + 0x01900060, + 0x43020a04, + 0x00100060, + 0x1b00ca05, + 0x00100060, + 0x53028a07, + 0x0190c060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x00100010, + 0x1a00d40d, + 0x00100050, + 0x53028a0e, + 0x01900070, + 0x5b02ca0e, + 0x01900070, + 0x5b02ca0e, + 0x01900070, + 0x43020a0c, + 0x00100070, + 0x1b00ca0d, + 0x00100070, + 0x53028a0f, + 0x0190c070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x40021404, + 0x00100000, + 0x1a00d405, + 0x00100040, + 0x5b02ca06, + 0x01900060, + 0x5b02ca06, + 0x01900060, + 0x53028a07, + 0x0190c060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x00100010, + 0x1a00d40d, + 0x00100050, + 0x5b02ca0e, + 0x01900070, + 0x5b02ca0e, + 0x01900070, + 0x53028a0f, + 0x0190c070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u8 frame_lut_rev0[] = { + 0x02, + 0x04, + 0x14, + 0x14, + 0x03, + 0x05, + 0x16, + 0x16, + 0x0a, + 0x0c, + 0x1c, + 0x1c, + 0x0b, + 0x0d, + 0x1e, + 0x1e, + 0x06, + 0x08, + 0x18, + 0x18, + 0x07, + 0x09, + 0x1a, + 0x1a, + 0x0e, + 0x10, + 0x20, + 0x28, + 0x0f, + 0x11, + 0x22, + 0x2a, +}; + +static const u32 tmap_tbl_rev0[] = { + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0xf1111110, + 0x11111111, + 0x11f11111, + 0x00000111, + 0x11000000, + 0x1111f111, + 0x11111111, + 0x111111f1, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x000aa888, + 0x88880000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa2222220, + 0x22222222, + 0x22c22222, + 0x00000222, + 0x22000000, + 0x2222a222, + 0x22222222, + 0x222222a2, + 0xf1111110, + 0x11111111, + 0x11f11111, + 0x00011111, + 0x11110000, + 0x1111f111, + 0x11111111, + 0x111111f1, + 0xa8aa88a0, + 0xa88888a8, + 0xa8a8a88a, + 0x00088aaa, + 0xaaaa0000, + 0xa8a8aa88, + 0xa88aaaaa, + 0xaaaa8a8a, + 0xaaa8aaa0, + 0x8aaa8aaa, + 0xaa8a8a8a, + 0x000aaa88, + 0x8aaa0000, + 0xaaa8a888, + 0x8aa88a8a, + 0x8a88a888, + 0x08080a00, + 0x0a08080a, + 0x080a0a08, + 0x00080808, + 0x080a0000, + 0x080a0808, + 0x080a0808, + 0x0a0a0a08, + 0xa0a0a0a0, + 0x80a0a080, + 0x8080a0a0, + 0x00008080, + 0x80a00000, + 0x80a080a0, + 0xa080a0a0, + 0x8080a0a0, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x99999000, + 0x9b9b99bb, + 0x9bb99999, + 0x9999b9b9, + 0x9b99bb90, + 0x9bbbbb9b, + 0x9b9b9bb9, + 0x00000999, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00aaa888, + 0x22000000, + 0x2222b222, + 0x22222222, + 0x222222b2, + 0xb2222220, + 0x22222222, + 0x22d22222, + 0x00000222, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x33000000, + 0x3333b333, + 0x33333333, + 0x333333b3, + 0xb3333330, + 0x33333333, + 0x33d33333, + 0x00000333, + 0x22000000, + 0x2222a222, + 0x22222222, + 0x222222a2, + 0xa2222220, + 0x22222222, + 0x22c22222, + 0x00000222, + 0x99b99b00, + 0x9b9b99bb, + 0x9bb99999, + 0x9999b9b9, + 0x9b99bb99, + 0x9bbbbb9b, + 0x9b9b9bb9, + 0x00000999, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa88, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x08aaa888, + 0x22222200, + 0x2222f222, + 0x22222222, + 0x222222f2, + 0x22222222, + 0x22222222, + 0x22f22222, + 0x00000222, + 0x11000000, + 0x1111f111, + 0x11111111, + 0x11111111, + 0xf1111111, + 0x11111111, + 0x11f11111, + 0x01111111, + 0xbb9bb900, + 0xb9b9bb99, + 0xb99bbbbb, + 0xbbbb9b9b, + 0xb9bb99bb, + 0xb99999b9, + 0xb9b9b99b, + 0x00000bbb, + 0xaa000000, + 0xa8a8aa88, + 0xa88aaaaa, + 0xaaaa8a8a, + 0xa8aa88aa, + 0xa88888a8, + 0xa8a8a88a, + 0x0a888aaa, + 0xaa000000, + 0xa8a8aa88, + 0xa88aaaaa, + 0xaaaa8a8a, + 0xa8aa88a0, + 0xa88888a8, + 0xa8a8a88a, + 0x00000aaa, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0xbbbbbb00, + 0x999bbbbb, + 0x9bb99b9b, + 0xb9b9b9bb, + 0xb9b99bbb, + 0xb9b9b9bb, + 0xb9bb9b99, + 0x00000999, + 0x8a000000, + 0xaa88a888, + 0xa88888aa, + 0xa88a8a88, + 0xa88aa88a, + 0x88a8aaaa, + 0xa8aa8aaa, + 0x0888a88a, + 0x0b0b0b00, + 0x090b0b0b, + 0x0b090b0b, + 0x0909090b, + 0x09090b0b, + 0x09090b0b, + 0x09090b09, + 0x00000909, + 0x0a000000, + 0x0a080808, + 0x080a080a, + 0x080a0a08, + 0x080a080a, + 0x0808080a, + 0x0a0a0a08, + 0x0808080a, + 0xb0b0b000, + 0x9090b0b0, + 0x90b09090, + 0xb0b0b090, + 0xb0b090b0, + 0x90b0b0b0, + 0xb0b09090, + 0x00000090, + 0x80000000, + 0xa080a080, + 0xa08080a0, + 0xa0808080, + 0xa080a080, + 0x80a0a0a0, + 0xa0a080a0, + 0x00a0a0a0, + 0x22000000, + 0x2222f222, + 0x22222222, + 0x222222f2, + 0xf2222220, + 0x22222222, + 0x22f22222, + 0x00000222, + 0x11000000, + 0x1111f111, + 0x11111111, + 0x111111f1, + 0xf1111110, + 0x11111111, + 0x11f11111, + 0x00000111, + 0x33000000, + 0x3333f333, + 0x33333333, + 0x333333f3, + 0xf3333330, + 0x33333333, + 0x33f33333, + 0x00000333, + 0x22000000, + 0x2222f222, + 0x22222222, + 0x222222f2, + 0xf2222220, + 0x22222222, + 0x22f22222, + 0x00000222, + 0x99000000, + 0x9b9b99bb, + 0x9bb99999, + 0x9999b9b9, + 0x9b99bb90, + 0x9bbbbb9b, + 0x9b9b9bb9, + 0x00000999, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88888000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00aaa888, + 0x88a88a00, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa88, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa88, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x08aaa888, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 tdtrn_tbl_rev0[] = { + 0x061c061c, + 0x0050ee68, + 0xf592fe36, + 0xfe5212f6, + 0x00000c38, + 0xfe5212f6, + 0xf592fe36, + 0x0050ee68, + 0x061c061c, + 0xee680050, + 0xfe36f592, + 0x12f6fe52, + 0x0c380000, + 0x12f6fe52, + 0xfe36f592, + 0xee680050, + 0x061c061c, + 0x0050ee68, + 0xf592fe36, + 0xfe5212f6, + 0x00000c38, + 0xfe5212f6, + 0xf592fe36, + 0x0050ee68, + 0x061c061c, + 0xee680050, + 0xfe36f592, + 0x12f6fe52, + 0x0c380000, + 0x12f6fe52, + 0xfe36f592, + 0xee680050, + 0x05e305e3, + 0x004def0c, + 0xf5f3fe47, + 0xfe611246, + 0x00000bc7, + 0xfe611246, + 0xf5f3fe47, + 0x004def0c, + 0x05e305e3, + 0xef0c004d, + 0xfe47f5f3, + 0x1246fe61, + 0x0bc70000, + 0x1246fe61, + 0xfe47f5f3, + 0xef0c004d, + 0x05e305e3, + 0x004def0c, + 0xf5f3fe47, + 0xfe611246, + 0x00000bc7, + 0xfe611246, + 0xf5f3fe47, + 0x004def0c, + 0x05e305e3, + 0xef0c004d, + 0xfe47f5f3, + 0x1246fe61, + 0x0bc70000, + 0x1246fe61, + 0xfe47f5f3, + 0xef0c004d, + 0xfa58fa58, + 0xf895043b, + 0xff4c09c0, + 0xfbc6ffa8, + 0xfb84f384, + 0x0798f6f9, + 0x05760122, + 0x058409f6, + 0x0b500000, + 0x05b7f542, + 0x08860432, + 0x06ddfee7, + 0xfb84f384, + 0xf9d90664, + 0xf7e8025c, + 0x00fff7bd, + 0x05a805a8, + 0xf7bd00ff, + 0x025cf7e8, + 0x0664f9d9, + 0xf384fb84, + 0xfee706dd, + 0x04320886, + 0xf54205b7, + 0x00000b50, + 0x09f60584, + 0x01220576, + 0xf6f90798, + 0xf384fb84, + 0xffa8fbc6, + 0x09c0ff4c, + 0x043bf895, + 0x02d402d4, + 0x07de0270, + 0xfc96079c, + 0xf90afe94, + 0xfe00ff2c, + 0x02d4065d, + 0x092a0096, + 0x0014fbb8, + 0xfd2cfd2c, + 0x076afb3c, + 0x0096f752, + 0xf991fd87, + 0xfb2c0200, + 0xfeb8f960, + 0x08e0fc96, + 0x049802a8, + 0xfd2cfd2c, + 0x02a80498, + 0xfc9608e0, + 0xf960feb8, + 0x0200fb2c, + 0xfd87f991, + 0xf7520096, + 0xfb3c076a, + 0xfd2cfd2c, + 0xfbb80014, + 0x0096092a, + 0x065d02d4, + 0xff2cfe00, + 0xfe94f90a, + 0x079cfc96, + 0x027007de, + 0x02d402d4, + 0x027007de, + 0x079cfc96, + 0xfe94f90a, + 0xff2cfe00, + 0x065d02d4, + 0x0096092a, + 0xfbb80014, + 0xfd2cfd2c, + 0xfb3c076a, + 0xf7520096, + 0xfd87f991, + 0x0200fb2c, + 0xf960feb8, + 0xfc9608e0, + 0x02a80498, + 0xfd2cfd2c, + 0x049802a8, + 0x08e0fc96, + 0xfeb8f960, + 0xfb2c0200, + 0xf991fd87, + 0x0096f752, + 0x076afb3c, + 0xfd2cfd2c, + 0x0014fbb8, + 0x092a0096, + 0x02d4065d, + 0xfe00ff2c, + 0xf90afe94, + 0xfc96079c, + 0x07de0270, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x062a0000, + 0xfefa0759, + 0x08b80908, + 0xf396fc2d, + 0xf9d6045c, + 0xfc4ef608, + 0xf748f596, + 0x07b207bf, + 0x062a062a, + 0xf84ef841, + 0xf748f596, + 0x03b209f8, + 0xf9d6045c, + 0x0c6a03d3, + 0x08b80908, + 0x0106f8a7, + 0x062a0000, + 0xfefaf8a7, + 0x08b8f6f8, + 0xf39603d3, + 0xf9d6fba4, + 0xfc4e09f8, + 0xf7480a6a, + 0x07b2f841, + 0x062af9d6, + 0xf84e07bf, + 0xf7480a6a, + 0x03b2f608, + 0xf9d6fba4, + 0x0c6afc2d, + 0x08b8f6f8, + 0x01060759, + 0x062a0000, + 0xfefa0759, + 0x08b80908, + 0xf396fc2d, + 0xf9d6045c, + 0xfc4ef608, + 0xf748f596, + 0x07b207bf, + 0x062a062a, + 0xf84ef841, + 0xf748f596, + 0x03b209f8, + 0xf9d6045c, + 0x0c6a03d3, + 0x08b80908, + 0x0106f8a7, + 0x062a0000, + 0xfefaf8a7, + 0x08b8f6f8, + 0xf39603d3, + 0xf9d6fba4, + 0xfc4e09f8, + 0xf7480a6a, + 0x07b2f841, + 0x062af9d6, + 0xf84e07bf, + 0xf7480a6a, + 0x03b2f608, + 0xf9d6fba4, + 0x0c6afc2d, + 0x08b8f6f8, + 0x01060759, + 0x061c061c, + 0xff30009d, + 0xffb21141, + 0xfd87fb54, + 0xf65dfe59, + 0x02eef99e, + 0x0166f03c, + 0xfff809b6, + 0x000008a4, + 0x000af42b, + 0x00eff577, + 0xfa840bf2, + 0xfc02ff51, + 0x08260f67, + 0xfff0036f, + 0x0842f9c3, + 0x00000000, + 0x063df7be, + 0xfc910010, + 0xf099f7da, + 0x00af03fe, + 0xf40e057c, + 0x0a89ff11, + 0x0bd5fff6, + 0xf75c0000, + 0xf64a0008, + 0x0fc4fe9a, + 0x0662fd12, + 0x01a709a3, + 0x04ac0279, + 0xeebf004e, + 0xff6300d0, + 0xf9e4f9e4, + 0x00d0ff63, + 0x004eeebf, + 0x027904ac, + 0x09a301a7, + 0xfd120662, + 0xfe9a0fc4, + 0x0008f64a, + 0x0000f75c, + 0xfff60bd5, + 0xff110a89, + 0x057cf40e, + 0x03fe00af, + 0xf7daf099, + 0x0010fc91, + 0xf7be063d, + 0x00000000, + 0xf9c30842, + 0x036ffff0, + 0x0f670826, + 0xff51fc02, + 0x0bf2fa84, + 0xf57700ef, + 0xf42b000a, + 0x08a40000, + 0x09b6fff8, + 0xf03c0166, + 0xf99e02ee, + 0xfe59f65d, + 0xfb54fd87, + 0x1141ffb2, + 0x009dff30, + 0x05e30000, + 0xff060705, + 0x085408a0, + 0xf425fc59, + 0xfa1d042a, + 0xfc78f67a, + 0xf7acf60e, + 0x075a0766, + 0x05e305e3, + 0xf8a6f89a, + 0xf7acf60e, + 0x03880986, + 0xfa1d042a, + 0x0bdb03a7, + 0x085408a0, + 0x00faf8fb, + 0x05e30000, + 0xff06f8fb, + 0x0854f760, + 0xf42503a7, + 0xfa1dfbd6, + 0xfc780986, + 0xf7ac09f2, + 0x075af89a, + 0x05e3fa1d, + 0xf8a60766, + 0xf7ac09f2, + 0x0388f67a, + 0xfa1dfbd6, + 0x0bdbfc59, + 0x0854f760, + 0x00fa0705, + 0x05e30000, + 0xff060705, + 0x085408a0, + 0xf425fc59, + 0xfa1d042a, + 0xfc78f67a, + 0xf7acf60e, + 0x075a0766, + 0x05e305e3, + 0xf8a6f89a, + 0xf7acf60e, + 0x03880986, + 0xfa1d042a, + 0x0bdb03a7, + 0x085408a0, + 0x00faf8fb, + 0x05e30000, + 0xff06f8fb, + 0x0854f760, + 0xf42503a7, + 0xfa1dfbd6, + 0xfc780986, + 0xf7ac09f2, + 0x075af89a, + 0x05e3fa1d, + 0xf8a60766, + 0xf7ac09f2, + 0x0388f67a, + 0xfa1dfbd6, + 0x0bdbfc59, + 0x0854f760, + 0x00fa0705, + 0xfa58fa58, + 0xf8f0fe00, + 0x0448073d, + 0xfdc9fe46, + 0xf9910258, + 0x089d0407, + 0xfd5cf71a, + 0x02affde0, + 0x083e0496, + 0xff5a0740, + 0xff7afd97, + 0x00fe01f1, + 0x0009082e, + 0xfa94ff75, + 0xfecdf8ea, + 0xffb0f693, + 0xfd2cfa58, + 0x0433ff16, + 0xfba405dd, + 0xfa610341, + 0x06a606cb, + 0x0039fd2d, + 0x0677fa97, + 0x01fa05e0, + 0xf896003e, + 0x075a068b, + 0x012cfc3e, + 0xfa23f98d, + 0xfc7cfd43, + 0xff90fc0d, + 0x01c10982, + 0x00c601d6, + 0xfd2cfd2c, + 0x01d600c6, + 0x098201c1, + 0xfc0dff90, + 0xfd43fc7c, + 0xf98dfa23, + 0xfc3e012c, + 0x068b075a, + 0x003ef896, + 0x05e001fa, + 0xfa970677, + 0xfd2d0039, + 0x06cb06a6, + 0x0341fa61, + 0x05ddfba4, + 0xff160433, + 0xfa58fd2c, + 0xf693ffb0, + 0xf8eafecd, + 0xff75fa94, + 0x082e0009, + 0x01f100fe, + 0xfd97ff7a, + 0x0740ff5a, + 0x0496083e, + 0xfde002af, + 0xf71afd5c, + 0x0407089d, + 0x0258f991, + 0xfe46fdc9, + 0x073d0448, + 0xfe00f8f0, + 0xfd2cfd2c, + 0xfce00500, + 0xfc09fddc, + 0xfe680157, + 0x04c70571, + 0xfc3aff21, + 0xfcd70228, + 0x056d0277, + 0x0200fe00, + 0x0022f927, + 0xfe3c032b, + 0xfc44ff3c, + 0x03e9fbdb, + 0x04570313, + 0x04c9ff5c, + 0x000d03b8, + 0xfa580000, + 0xfbe900d2, + 0xf9d0fe0b, + 0x0125fdf9, + 0x042501bf, + 0x0328fa2b, + 0xffa902f0, + 0xfa250157, + 0x0200fe00, + 0x03740438, + 0xff0405fd, + 0x030cfe52, + 0x0037fb39, + 0xff6904c5, + 0x04f8fd23, + 0xfd31fc1b, + 0xfd2cfd2c, + 0xfc1bfd31, + 0xfd2304f8, + 0x04c5ff69, + 0xfb390037, + 0xfe52030c, + 0x05fdff04, + 0x04380374, + 0xfe000200, + 0x0157fa25, + 0x02f0ffa9, + 0xfa2b0328, + 0x01bf0425, + 0xfdf90125, + 0xfe0bf9d0, + 0x00d2fbe9, + 0x0000fa58, + 0x03b8000d, + 0xff5c04c9, + 0x03130457, + 0xfbdb03e9, + 0xff3cfc44, + 0x032bfe3c, + 0xf9270022, + 0xfe000200, + 0x0277056d, + 0x0228fcd7, + 0xff21fc3a, + 0x057104c7, + 0x0157fe68, + 0xfddcfc09, + 0x0500fce0, + 0xfd2cfd2c, + 0x0500fce0, + 0xfddcfc09, + 0x0157fe68, + 0x057104c7, + 0xff21fc3a, + 0x0228fcd7, + 0x0277056d, + 0xfe000200, + 0xf9270022, + 0x032bfe3c, + 0xff3cfc44, + 0xfbdb03e9, + 0x03130457, + 0xff5c04c9, + 0x03b8000d, + 0x0000fa58, + 0x00d2fbe9, + 0xfe0bf9d0, + 0xfdf90125, + 0x01bf0425, + 0xfa2b0328, + 0x02f0ffa9, + 0x0157fa25, + 0xfe000200, + 0x04380374, + 0x05fdff04, + 0xfe52030c, + 0xfb390037, + 0x04c5ff69, + 0xfd2304f8, + 0xfc1bfd31, + 0xfd2cfd2c, + 0xfd31fc1b, + 0x04f8fd23, + 0xff6904c5, + 0x0037fb39, + 0x030cfe52, + 0xff0405fd, + 0x03740438, + 0x0200fe00, + 0xfa250157, + 0xffa902f0, + 0x0328fa2b, + 0x042501bf, + 0x0125fdf9, + 0xf9d0fe0b, + 0xfbe900d2, + 0xfa580000, + 0x000d03b8, + 0x04c9ff5c, + 0x04570313, + 0x03e9fbdb, + 0xfc44ff3c, + 0xfe3c032b, + 0x0022f927, + 0x0200fe00, + 0x056d0277, + 0xfcd70228, + 0xfc3aff21, + 0x04c70571, + 0xfe680157, + 0xfc09fddc, + 0xfce00500, + 0x05a80000, + 0xff1006be, + 0x0800084a, + 0xf49cfc7e, + 0xfa580400, + 0xfc9cf6da, + 0xf800f672, + 0x0710071c, + 0x05a805a8, + 0xf8f0f8e4, + 0xf800f672, + 0x03640926, + 0xfa580400, + 0x0b640382, + 0x0800084a, + 0x00f0f942, + 0x05a80000, + 0xff10f942, + 0x0800f7b6, + 0xf49c0382, + 0xfa58fc00, + 0xfc9c0926, + 0xf800098e, + 0x0710f8e4, + 0x05a8fa58, + 0xf8f0071c, + 0xf800098e, + 0x0364f6da, + 0xfa58fc00, + 0x0b64fc7e, + 0x0800f7b6, + 0x00f006be, + 0x05a80000, + 0xff1006be, + 0x0800084a, + 0xf49cfc7e, + 0xfa580400, + 0xfc9cf6da, + 0xf800f672, + 0x0710071c, + 0x05a805a8, + 0xf8f0f8e4, + 0xf800f672, + 0x03640926, + 0xfa580400, + 0x0b640382, + 0x0800084a, + 0x00f0f942, + 0x05a80000, + 0xff10f942, + 0x0800f7b6, + 0xf49c0382, + 0xfa58fc00, + 0xfc9c0926, + 0xf800098e, + 0x0710f8e4, + 0x05a8fa58, + 0xf8f0071c, + 0xf800098e, + 0x0364f6da, + 0xfa58fc00, + 0x0b64fc7e, + 0x0800f7b6, + 0x00f006be, +}; + +static const u32 intlv_tbl_rev0[] = { + 0x00802070, + 0x0671188d, + 0x0a60192c, + 0x0a300e46, + 0x00c1188d, + 0x080024d2, + 0x00000070, +}; + +static const u16 pilot_tbl_rev0[] = { + 0xff08, + 0xff08, + 0xff08, + 0xff08, + 0xff08, + 0xff08, + 0xff08, + 0xff08, + 0x80d5, + 0x80d5, + 0x80d5, + 0x80d5, + 0x80d5, + 0x80d5, + 0x80d5, + 0x80d5, + 0xff0a, + 0xff82, + 0xffa0, + 0xff28, + 0xffff, + 0xffff, + 0xffff, + 0xffff, + 0xff82, + 0xffa0, + 0xff28, + 0xff0a, + 0xffff, + 0xffff, + 0xffff, + 0xffff, + 0xf83f, + 0xfa1f, + 0xfa97, + 0xfab5, + 0xf2bd, + 0xf0bf, + 0xffff, + 0xffff, + 0xf017, + 0xf815, + 0xf215, + 0xf095, + 0xf035, + 0xf01d, + 0xffff, + 0xffff, + 0xff08, + 0xff02, + 0xff80, + 0xff20, + 0xff08, + 0xff02, + 0xff80, + 0xff20, + 0xf01f, + 0xf817, + 0xfa15, + 0xf295, + 0xf0b5, + 0xf03d, + 0xffff, + 0xffff, + 0xf82a, + 0xfa0a, + 0xfa82, + 0xfaa0, + 0xf2a8, + 0xf0aa, + 0xffff, + 0xffff, + 0xf002, + 0xf800, + 0xf200, + 0xf080, + 0xf020, + 0xf008, + 0xffff, + 0xffff, + 0xf00a, + 0xf802, + 0xfa00, + 0xf280, + 0xf0a0, + 0xf028, + 0xffff, + 0xffff, +}; + +static const u32 pltlut_tbl_rev0[] = { + 0x76540123, + 0x62407351, + 0x76543201, + 0x76540213, + 0x76540123, + 0x76430521, +}; + +static const u32 tdi_tbl20_ant0_rev0[] = { + 0x00091226, + 0x000a1429, + 0x000b56ad, + 0x000c58b0, + 0x000d5ab3, + 0x000e9cb6, + 0x000f9eba, + 0x0000c13d, + 0x00020301, + 0x00030504, + 0x00040708, + 0x0005090b, + 0x00064b8e, + 0x00095291, + 0x000a5494, + 0x000b9718, + 0x000c9927, + 0x000d9b2a, + 0x000edd2e, + 0x000fdf31, + 0x000101b4, + 0x000243b7, + 0x000345bb, + 0x000447be, + 0x00058982, + 0x00068c05, + 0x00099309, + 0x000a950c, + 0x000bd78f, + 0x000cd992, + 0x000ddb96, + 0x000f1d99, + 0x00005fa8, + 0x0001422c, + 0x0002842f, + 0x00038632, + 0x00048835, + 0x0005ca38, + 0x0006ccbc, + 0x0009d3bf, + 0x000b1603, + 0x000c1806, + 0x000d1a0a, + 0x000e1c0d, + 0x000f5e10, + 0x00008093, + 0x00018297, + 0x0002c49a, + 0x0003c680, + 0x0004c880, + 0x00060b00, + 0x00070d00, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 tdi_tbl20_ant1_rev0[] = { + 0x00014b26, + 0x00028d29, + 0x000393ad, + 0x00049630, + 0x0005d833, + 0x0006da36, + 0x00099c3a, + 0x000a9e3d, + 0x000bc081, + 0x000cc284, + 0x000dc488, + 0x000f068b, + 0x0000488e, + 0x00018b91, + 0x0002d214, + 0x0003d418, + 0x0004d6a7, + 0x000618aa, + 0x00071aae, + 0x0009dcb1, + 0x000b1eb4, + 0x000c0137, + 0x000d033b, + 0x000e053e, + 0x000f4702, + 0x00008905, + 0x00020c09, + 0x0003128c, + 0x0004148f, + 0x00051712, + 0x00065916, + 0x00091b19, + 0x000a1d28, + 0x000b5f2c, + 0x000c41af, + 0x000d43b2, + 0x000e85b5, + 0x000f87b8, + 0x0000c9bc, + 0x00024cbf, + 0x00035303, + 0x00045506, + 0x0005978a, + 0x0006998d, + 0x00095b90, + 0x000a5d93, + 0x000b9f97, + 0x000c821a, + 0x000d8400, + 0x000ec600, + 0x000fc800, + 0x00010a00, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 tdi_tbl40_ant0_rev0[] = { + 0x0011a346, + 0x00136ccf, + 0x0014f5d9, + 0x001641e2, + 0x0017cb6b, + 0x00195475, + 0x001b2383, + 0x001cad0c, + 0x001e7616, + 0x0000821f, + 0x00020ba8, + 0x0003d4b2, + 0x00056447, + 0x00072dd0, + 0x0008b6da, + 0x000a02e3, + 0x000b8c6c, + 0x000d15f6, + 0x0011e484, + 0x0013ae0d, + 0x00153717, + 0x00168320, + 0x00180ca9, + 0x00199633, + 0x001b6548, + 0x001ceed1, + 0x001eb7db, + 0x0000c3e4, + 0x00024d6d, + 0x000416f7, + 0x0005a585, + 0x00076f0f, + 0x0008f818, + 0x000a4421, + 0x000bcdab, + 0x000d9734, + 0x00122649, + 0x0013efd2, + 0x001578dc, + 0x0016c4e5, + 0x00184e6e, + 0x001a17f8, + 0x001ba686, + 0x001d3010, + 0x001ef999, + 0x00010522, + 0x00028eac, + 0x00045835, + 0x0005e74a, + 0x0007b0d3, + 0x00093a5d, + 0x000a85e6, + 0x000c0f6f, + 0x000dd8f9, + 0x00126787, + 0x00143111, + 0x0015ba9a, + 0x00170623, + 0x00188fad, + 0x001a5936, + 0x001be84b, + 0x001db1d4, + 0x001f3b5e, + 0x000146e7, + 0x00031070, + 0x000499fa, + 0x00062888, + 0x0007f212, + 0x00097b9b, + 0x000ac7a4, + 0x000c50ae, + 0x000e1a37, + 0x0012a94c, + 0x001472d5, + 0x0015fc5f, + 0x00174868, + 0x0018d171, + 0x001a9afb, + 0x001c2989, + 0x001df313, + 0x001f7c9c, + 0x000188a5, + 0x000351af, + 0x0004db38, + 0x0006aa4d, + 0x000833d7, + 0x0009bd60, + 0x000b0969, + 0x000c9273, + 0x000e5bfc, + 0x00132a8a, + 0x0014b414, + 0x00163d9d, + 0x001789a6, + 0x001912b0, + 0x001adc39, + 0x001c6bce, + 0x001e34d8, + 0x001fbe61, + 0x0001ca6a, + 0x00039374, + 0x00051cfd, + 0x0006ec0b, + 0x00087515, + 0x0009fe9e, + 0x000b4aa7, + 0x000cd3b1, + 0x000e9d3a, + 0x00000000, + 0x00000000, +}; + +static const u32 tdi_tbl40_ant1_rev0[] = { + 0x001edb36, + 0x000129ca, + 0x0002b353, + 0x00047cdd, + 0x0005c8e6, + 0x000791ef, + 0x00091bf9, + 0x000aaa07, + 0x000c3391, + 0x000dfd1a, + 0x00120923, + 0x0013d22d, + 0x00155c37, + 0x0016eacb, + 0x00187454, + 0x001a3dde, + 0x001b89e7, + 0x001d12f0, + 0x001f1cfa, + 0x00016b88, + 0x00033492, + 0x0004be1b, + 0x00060a24, + 0x0007d32e, + 0x00095d38, + 0x000aec4c, + 0x000c7555, + 0x000e3edf, + 0x00124ae8, + 0x001413f1, + 0x0015a37b, + 0x00172c89, + 0x0018b593, + 0x001a419c, + 0x001bcb25, + 0x001d942f, + 0x001f63b9, + 0x0001ad4d, + 0x00037657, + 0x0004c260, + 0x00068be9, + 0x000814f3, + 0x0009a47c, + 0x000b2d8a, + 0x000cb694, + 0x000e429d, + 0x00128c26, + 0x001455b0, + 0x0015e4ba, + 0x00176e4e, + 0x0018f758, + 0x001a8361, + 0x001c0cea, + 0x001dd674, + 0x001fa57d, + 0x0001ee8b, + 0x0003b795, + 0x0005039e, + 0x0006cd27, + 0x000856b1, + 0x0009e5c6, + 0x000b6f4f, + 0x000cf859, + 0x000e8462, + 0x00130deb, + 0x00149775, + 0x00162603, + 0x0017af8c, + 0x00193896, + 0x001ac49f, + 0x001c4e28, + 0x001e17b2, + 0x0000a6c7, + 0x00023050, + 0x0003f9da, + 0x00054563, + 0x00070eec, + 0x00089876, + 0x000a2704, + 0x000bb08d, + 0x000d3a17, + 0x001185a0, + 0x00134f29, + 0x0014d8b3, + 0x001667c8, + 0x0017f151, + 0x00197adb, + 0x001b0664, + 0x001c8fed, + 0x001e5977, + 0x0000e805, + 0x0002718f, + 0x00043b18, + 0x000586a1, + 0x0007502b, + 0x0008d9b4, + 0x000a68c9, + 0x000bf252, + 0x000dbbdc, + 0x0011c7e5, + 0x001390ee, + 0x00151a78, + 0x0016a906, + 0x00183290, + 0x0019bc19, + 0x001b4822, + 0x001cd12c, + 0x001e9ab5, + 0x00000000, + 0x00000000, +}; + +static const u16 bdi_tbl_rev0[] = { + 0x0070, + 0x0126, + 0x012c, + 0x0246, + 0x048d, + 0x04d2, +}; + +static const u32 chanest_tbl_rev0[] = { + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, +}; + +static const u8 mcs_tbl_rev0[] = { + 0x00, + 0x08, + 0x0a, + 0x10, + 0x12, + 0x19, + 0x1a, + 0x1c, + 0x40, + 0x48, + 0x4a, + 0x50, + 0x52, + 0x59, + 0x5a, + 0x5c, + 0x80, + 0x88, + 0x8a, + 0x90, + 0x92, + 0x99, + 0x9a, + 0x9c, + 0xc0, + 0xc8, + 0xca, + 0xd0, + 0xd2, + 0xd9, + 0xda, + 0xdc, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0x02, + 0x04, + 0x08, + 0x09, + 0x0a, + 0x0c, + 0x10, + 0x11, + 0x12, + 0x14, + 0x18, + 0x19, + 0x1a, + 0x1c, + 0x20, + 0x21, + 0x22, + 0x24, + 0x40, + 0x41, + 0x42, + 0x44, + 0x48, + 0x49, + 0x4a, + 0x4c, + 0x50, + 0x51, + 0x52, + 0x54, + 0x58, + 0x59, + 0x5a, + 0x5c, + 0x60, + 0x61, + 0x62, + 0x64, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +static const u32 noise_var_tbl0_rev0[] = { + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, +}; + +static const u32 noise_var_tbl1_rev0[] = { + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, +}; + +static const u8 est_pwr_lut_core0_rev0[] = { + 0x50, + 0x4f, + 0x4e, + 0x4d, + 0x4c, + 0x4b, + 0x4a, + 0x49, + 0x48, + 0x47, + 0x46, + 0x45, + 0x44, + 0x43, + 0x42, + 0x41, + 0x40, + 0x3f, + 0x3e, + 0x3d, + 0x3c, + 0x3b, + 0x3a, + 0x39, + 0x38, + 0x37, + 0x36, + 0x35, + 0x34, + 0x33, + 0x32, + 0x31, + 0x30, + 0x2f, + 0x2e, + 0x2d, + 0x2c, + 0x2b, + 0x2a, + 0x29, + 0x28, + 0x27, + 0x26, + 0x25, + 0x24, + 0x23, + 0x22, + 0x21, + 0x20, + 0x1f, + 0x1e, + 0x1d, + 0x1c, + 0x1b, + 0x1a, + 0x19, + 0x18, + 0x17, + 0x16, + 0x15, + 0x14, + 0x13, + 0x12, + 0x11, +}; + +static const u8 est_pwr_lut_core1_rev0[] = { + 0x50, + 0x4f, + 0x4e, + 0x4d, + 0x4c, + 0x4b, + 0x4a, + 0x49, + 0x48, + 0x47, + 0x46, + 0x45, + 0x44, + 0x43, + 0x42, + 0x41, + 0x40, + 0x3f, + 0x3e, + 0x3d, + 0x3c, + 0x3b, + 0x3a, + 0x39, + 0x38, + 0x37, + 0x36, + 0x35, + 0x34, + 0x33, + 0x32, + 0x31, + 0x30, + 0x2f, + 0x2e, + 0x2d, + 0x2c, + 0x2b, + 0x2a, + 0x29, + 0x28, + 0x27, + 0x26, + 0x25, + 0x24, + 0x23, + 0x22, + 0x21, + 0x20, + 0x1f, + 0x1e, + 0x1d, + 0x1c, + 0x1b, + 0x1a, + 0x19, + 0x18, + 0x17, + 0x16, + 0x15, + 0x14, + 0x13, + 0x12, + 0x11, +}; + +static const u8 adj_pwr_lut_core0_rev0[] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +static const u8 adj_pwr_lut_core1_rev0[] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +static const u32 gainctrl_lut_core0_rev0[] = { + 0x03cc2b44, + 0x03cc2b42, + 0x03cc2b40, + 0x03cc2b3e, + 0x03cc2b3d, + 0x03cc2b3b, + 0x03c82b44, + 0x03c82b42, + 0x03c82b40, + 0x03c82b3e, + 0x03c82b3d, + 0x03c82b3b, + 0x03c82b39, + 0x03c82b38, + 0x03c82b36, + 0x03c82b34, + 0x03c42b44, + 0x03c42b42, + 0x03c42b40, + 0x03c42b3e, + 0x03c42b3d, + 0x03c42b3b, + 0x03c42b39, + 0x03c42b38, + 0x03c42b36, + 0x03c42b34, + 0x03c42b33, + 0x03c42b32, + 0x03c42b30, + 0x03c42b2f, + 0x03c42b2d, + 0x03c02b44, + 0x03c02b42, + 0x03c02b40, + 0x03c02b3e, + 0x03c02b3d, + 0x03c02b3b, + 0x03c02b39, + 0x03c02b38, + 0x03c02b36, + 0x03c02b34, + 0x03b02b44, + 0x03b02b42, + 0x03b02b40, + 0x03b02b3e, + 0x03b02b3d, + 0x03b02b3b, + 0x03b02b39, + 0x03b02b38, + 0x03b02b36, + 0x03b02b34, + 0x03b02b33, + 0x03b02b32, + 0x03b02b30, + 0x03b02b2f, + 0x03b02b2d, + 0x03a02b44, + 0x03a02b42, + 0x03a02b40, + 0x03a02b3e, + 0x03a02b3d, + 0x03a02b3b, + 0x03a02b39, + 0x03a02b38, + 0x03a02b36, + 0x03a02b34, + 0x03902b44, + 0x03902b42, + 0x03902b40, + 0x03902b3e, + 0x03902b3d, + 0x03902b3b, + 0x03902b39, + 0x03902b38, + 0x03902b36, + 0x03902b34, + 0x03902b33, + 0x03902b32, + 0x03902b30, + 0x03802b44, + 0x03802b42, + 0x03802b40, + 0x03802b3e, + 0x03802b3d, + 0x03802b3b, + 0x03802b39, + 0x03802b38, + 0x03802b36, + 0x03802b34, + 0x03802b33, + 0x03802b32, + 0x03802b30, + 0x03802b2f, + 0x03802b2d, + 0x03802b2c, + 0x03802b2b, + 0x03802b2a, + 0x03802b29, + 0x03802b27, + 0x03802b26, + 0x03802b25, + 0x03802b24, + 0x03802b23, + 0x03802b22, + 0x03802b21, + 0x03802b20, + 0x03802b1f, + 0x03802b1e, + 0x03802b1e, + 0x03802b1d, + 0x03802b1c, + 0x03802b1b, + 0x03802b1a, + 0x03802b1a, + 0x03802b19, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x00002b00, +}; + +static const u32 gainctrl_lut_core1_rev0[] = { + 0x03cc2b44, + 0x03cc2b42, + 0x03cc2b40, + 0x03cc2b3e, + 0x03cc2b3d, + 0x03cc2b3b, + 0x03c82b44, + 0x03c82b42, + 0x03c82b40, + 0x03c82b3e, + 0x03c82b3d, + 0x03c82b3b, + 0x03c82b39, + 0x03c82b38, + 0x03c82b36, + 0x03c82b34, + 0x03c42b44, + 0x03c42b42, + 0x03c42b40, + 0x03c42b3e, + 0x03c42b3d, + 0x03c42b3b, + 0x03c42b39, + 0x03c42b38, + 0x03c42b36, + 0x03c42b34, + 0x03c42b33, + 0x03c42b32, + 0x03c42b30, + 0x03c42b2f, + 0x03c42b2d, + 0x03c02b44, + 0x03c02b42, + 0x03c02b40, + 0x03c02b3e, + 0x03c02b3d, + 0x03c02b3b, + 0x03c02b39, + 0x03c02b38, + 0x03c02b36, + 0x03c02b34, + 0x03b02b44, + 0x03b02b42, + 0x03b02b40, + 0x03b02b3e, + 0x03b02b3d, + 0x03b02b3b, + 0x03b02b39, + 0x03b02b38, + 0x03b02b36, + 0x03b02b34, + 0x03b02b33, + 0x03b02b32, + 0x03b02b30, + 0x03b02b2f, + 0x03b02b2d, + 0x03a02b44, + 0x03a02b42, + 0x03a02b40, + 0x03a02b3e, + 0x03a02b3d, + 0x03a02b3b, + 0x03a02b39, + 0x03a02b38, + 0x03a02b36, + 0x03a02b34, + 0x03902b44, + 0x03902b42, + 0x03902b40, + 0x03902b3e, + 0x03902b3d, + 0x03902b3b, + 0x03902b39, + 0x03902b38, + 0x03902b36, + 0x03902b34, + 0x03902b33, + 0x03902b32, + 0x03902b30, + 0x03802b44, + 0x03802b42, + 0x03802b40, + 0x03802b3e, + 0x03802b3d, + 0x03802b3b, + 0x03802b39, + 0x03802b38, + 0x03802b36, + 0x03802b34, + 0x03802b33, + 0x03802b32, + 0x03802b30, + 0x03802b2f, + 0x03802b2d, + 0x03802b2c, + 0x03802b2b, + 0x03802b2a, + 0x03802b29, + 0x03802b27, + 0x03802b26, + 0x03802b25, + 0x03802b24, + 0x03802b23, + 0x03802b22, + 0x03802b21, + 0x03802b20, + 0x03802b1f, + 0x03802b1e, + 0x03802b1e, + 0x03802b1d, + 0x03802b1c, + 0x03802b1b, + 0x03802b1a, + 0x03802b1a, + 0x03802b19, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x03802b18, + 0x00002b00, +}; + +static const u32 iq_lut_core0_rev0[] = { + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, +}; + +static const u32 iq_lut_core1_rev0[] = { + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, + 0x0000007f, +}; + +static const u16 loft_lut_core0_rev0[] = { + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, +}; + +static const u16 loft_lut_core1_rev0[] = { + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, + 0x0000, + 0x0101, + 0x0002, + 0x0103, +}; + +const struct phytbl_info mimophytbl_info_rev0_volatile[] = { + {&bdi_tbl_rev0, sizeof(bdi_tbl_rev0) / sizeof(bdi_tbl_rev0[0]), 21, 0, + 16} + , + {&pltlut_tbl_rev0, sizeof(pltlut_tbl_rev0) / sizeof(pltlut_tbl_rev0[0]), + 20, 0, 32} + , + {&gainctrl_lut_core0_rev0, + sizeof(gainctrl_lut_core0_rev0) / sizeof(gainctrl_lut_core0_rev0[0]), + 26, 192, 32} + , + {&gainctrl_lut_core1_rev0, + sizeof(gainctrl_lut_core1_rev0) / sizeof(gainctrl_lut_core1_rev0[0]), + 27, 192, 32} + , + + {&est_pwr_lut_core0_rev0, + sizeof(est_pwr_lut_core0_rev0) / sizeof(est_pwr_lut_core0_rev0[0]), 26, + 0, 8} + , + {&est_pwr_lut_core1_rev0, + sizeof(est_pwr_lut_core1_rev0) / sizeof(est_pwr_lut_core1_rev0[0]), 27, + 0, 8} + , + {&adj_pwr_lut_core0_rev0, + sizeof(adj_pwr_lut_core0_rev0) / sizeof(adj_pwr_lut_core0_rev0[0]), 26, + 64, 8} + , + {&adj_pwr_lut_core1_rev0, + sizeof(adj_pwr_lut_core1_rev0) / sizeof(adj_pwr_lut_core1_rev0[0]), 27, + 64, 8} + , + {&iq_lut_core0_rev0, + sizeof(iq_lut_core0_rev0) / sizeof(iq_lut_core0_rev0[0]), 26, 320, 32} + , + {&iq_lut_core1_rev0, + sizeof(iq_lut_core1_rev0) / sizeof(iq_lut_core1_rev0[0]), 27, 320, 32} + , + {&loft_lut_core0_rev0, + sizeof(loft_lut_core0_rev0) / sizeof(loft_lut_core0_rev0[0]), 26, 448, + 16} + , + {&loft_lut_core1_rev0, + sizeof(loft_lut_core1_rev0) / sizeof(loft_lut_core1_rev0[0]), 27, 448, + 16} + , +}; + +const struct phytbl_info mimophytbl_info_rev0[] = { + {&frame_struct_rev0, + sizeof(frame_struct_rev0) / sizeof(frame_struct_rev0[0]), 10, 0, 32} + , + {&frame_lut_rev0, sizeof(frame_lut_rev0) / sizeof(frame_lut_rev0[0]), + 24, 0, 8} + , + {&tmap_tbl_rev0, sizeof(tmap_tbl_rev0) / sizeof(tmap_tbl_rev0[0]), 12, + 0, 32} + , + {&tdtrn_tbl_rev0, sizeof(tdtrn_tbl_rev0) / sizeof(tdtrn_tbl_rev0[0]), + 14, 0, 32} + , + {&intlv_tbl_rev0, sizeof(intlv_tbl_rev0) / sizeof(intlv_tbl_rev0[0]), + 13, 0, 32} + , + {&pilot_tbl_rev0, sizeof(pilot_tbl_rev0) / sizeof(pilot_tbl_rev0[0]), + 11, 0, 16} + , + {&tdi_tbl20_ant0_rev0, + sizeof(tdi_tbl20_ant0_rev0) / sizeof(tdi_tbl20_ant0_rev0[0]), 19, 128, + 32} + , + {&tdi_tbl20_ant1_rev0, + sizeof(tdi_tbl20_ant1_rev0) / sizeof(tdi_tbl20_ant1_rev0[0]), 19, 256, + 32} + , + {&tdi_tbl40_ant0_rev0, + sizeof(tdi_tbl40_ant0_rev0) / sizeof(tdi_tbl40_ant0_rev0[0]), 19, 640, + 32} + , + {&tdi_tbl40_ant1_rev0, + sizeof(tdi_tbl40_ant1_rev0) / sizeof(tdi_tbl40_ant1_rev0[0]), 19, 768, + 32} + , + {&chanest_tbl_rev0, + sizeof(chanest_tbl_rev0) / sizeof(chanest_tbl_rev0[0]), 22, 0, 32} + , + {&mcs_tbl_rev0, sizeof(mcs_tbl_rev0) / sizeof(mcs_tbl_rev0[0]), 18, 0, + 8} + , + {&noise_var_tbl0_rev0, + sizeof(noise_var_tbl0_rev0) / sizeof(noise_var_tbl0_rev0[0]), 16, 0, + 32} + , + {&noise_var_tbl1_rev0, + sizeof(noise_var_tbl1_rev0) / sizeof(noise_var_tbl1_rev0[0]), 16, 128, + 32} + , +}; + +const u32 mimophytbl_info_sz_rev0 = + sizeof(mimophytbl_info_rev0) / sizeof(mimophytbl_info_rev0[0]); +const u32 mimophytbl_info_sz_rev0_volatile = + sizeof(mimophytbl_info_rev0_volatile) / + sizeof(mimophytbl_info_rev0_volatile[0]); + +static const u16 ant_swctrl_tbl_rev3[] = { + 0x0082, + 0x0082, + 0x0211, + 0x0222, + 0x0328, + 0x0000, + 0x0000, + 0x0000, + 0x0144, + 0x0000, + 0x0000, + 0x0000, + 0x0188, + 0x0000, + 0x0000, + 0x0000, + 0x0082, + 0x0082, + 0x0211, + 0x0222, + 0x0328, + 0x0000, + 0x0000, + 0x0000, + 0x0144, + 0x0000, + 0x0000, + 0x0000, + 0x0188, + 0x0000, + 0x0000, + 0x0000, +}; + +static const u16 ant_swctrl_tbl_rev3_1[] = { + 0x0022, + 0x0022, + 0x0011, + 0x0022, + 0x0022, + 0x0000, + 0x0000, + 0x0000, + 0x0011, + 0x0000, + 0x0000, + 0x0000, + 0x0022, + 0x0000, + 0x0000, + 0x0000, + 0x0022, + 0x0022, + 0x0011, + 0x0022, + 0x0022, + 0x0000, + 0x0000, + 0x0000, + 0x0011, + 0x0000, + 0x0000, + 0x0000, + 0x0022, + 0x0000, + 0x0000, + 0x0000, +}; + +static const u16 ant_swctrl_tbl_rev3_2[] = { + 0x0088, + 0x0088, + 0x0044, + 0x0088, + 0x0088, + 0x0000, + 0x0000, + 0x0000, + 0x0044, + 0x0000, + 0x0000, + 0x0000, + 0x0088, + 0x0000, + 0x0000, + 0x0000, + 0x0088, + 0x0088, + 0x0044, + 0x0088, + 0x0088, + 0x0000, + 0x0000, + 0x0000, + 0x0044, + 0x0000, + 0x0000, + 0x0000, + 0x0088, + 0x0000, + 0x0000, + 0x0000, +}; + +static const u16 ant_swctrl_tbl_rev3_3[] = { + 0x022, + 0x022, + 0x011, + 0x022, + 0x000, + 0x000, + 0x000, + 0x000, + 0x011, + 0x000, + 0x000, + 0x000, + 0x022, + 0x000, + 0x000, + 0x3cc, + 0x022, + 0x022, + 0x011, + 0x022, + 0x000, + 0x000, + 0x000, + 0x000, + 0x011, + 0x000, + 0x000, + 0x000, + 0x022, + 0x000, + 0x000, + 0x3cc +}; + +static const u32 frame_struct_rev3[] = { + 0x08004a04, + 0x00100000, + 0x01000a05, + 0x00100020, + 0x09804506, + 0x00100030, + 0x09804507, + 0x00100030, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x08004a0c, + 0x00100004, + 0x01000a0d, + 0x00100024, + 0x0980450e, + 0x00100034, + 0x0980450f, + 0x00100034, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000a04, + 0x00100000, + 0x11008a05, + 0x00100020, + 0x1980c506, + 0x00100030, + 0x21810506, + 0x00100030, + 0x21810506, + 0x00100030, + 0x01800504, + 0x00100030, + 0x11808505, + 0x00100030, + 0x29814507, + 0x01100030, + 0x00000a04, + 0x00100000, + 0x11008a05, + 0x00100020, + 0x21810506, + 0x00100030, + 0x21810506, + 0x00100030, + 0x29814507, + 0x01100030, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000a0c, + 0x00100008, + 0x11008a0d, + 0x00100028, + 0x1980c50e, + 0x00100038, + 0x2181050e, + 0x00100038, + 0x2181050e, + 0x00100038, + 0x0180050c, + 0x00100038, + 0x1180850d, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000a0c, + 0x00100008, + 0x11008a0d, + 0x00100028, + 0x2181050e, + 0x00100038, + 0x2181050e, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x08004a04, + 0x00100000, + 0x01000a05, + 0x00100020, + 0x1980c506, + 0x00100030, + 0x1980c506, + 0x00100030, + 0x11808504, + 0x00100030, + 0x3981ca05, + 0x00100030, + 0x29814507, + 0x01100030, + 0x00000000, + 0x00000000, + 0x10008a04, + 0x00100000, + 0x3981ca05, + 0x00100030, + 0x1980c506, + 0x00100030, + 0x29814507, + 0x01100030, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x08004a0c, + 0x00100008, + 0x01000a0d, + 0x00100028, + 0x1980c50e, + 0x00100038, + 0x1980c50e, + 0x00100038, + 0x1180850c, + 0x00100038, + 0x3981ca0d, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000000, + 0x00000000, + 0x10008a0c, + 0x00100008, + 0x3981ca0d, + 0x00100038, + 0x1980c50e, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x40021404, + 0x00100000, + 0x02001405, + 0x00100040, + 0x0b004a06, + 0x01900060, + 0x13008a06, + 0x01900060, + 0x13008a06, + 0x01900060, + 0x43020a04, + 0x00100060, + 0x1b00ca05, + 0x00100060, + 0x23010a07, + 0x01500060, + 0x40021404, + 0x00100000, + 0x1a00d405, + 0x00100040, + 0x13008a06, + 0x01900060, + 0x13008a06, + 0x01900060, + 0x23010a07, + 0x01500060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x00100010, + 0x0200140d, + 0x00100050, + 0x0b004a0e, + 0x01900070, + 0x13008a0e, + 0x01900070, + 0x13008a0e, + 0x01900070, + 0x43020a0c, + 0x00100070, + 0x1b00ca0d, + 0x00100070, + 0x23010a0f, + 0x01500070, + 0x4002140c, + 0x00100010, + 0x1a00d40d, + 0x00100050, + 0x13008a0e, + 0x01900070, + 0x13008a0e, + 0x01900070, + 0x23010a0f, + 0x01500070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x50029404, + 0x00100000, + 0x32019405, + 0x00100040, + 0x0b004a06, + 0x01900060, + 0x0b004a06, + 0x01900060, + 0x5b02ca04, + 0x00100060, + 0x3b01d405, + 0x00100060, + 0x23010a07, + 0x01500060, + 0x00000000, + 0x00000000, + 0x5802d404, + 0x00100000, + 0x3b01d405, + 0x00100060, + 0x0b004a06, + 0x01900060, + 0x23010a07, + 0x01500060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x5002940c, + 0x00100010, + 0x3201940d, + 0x00100050, + 0x0b004a0e, + 0x01900070, + 0x0b004a0e, + 0x01900070, + 0x5b02ca0c, + 0x00100070, + 0x3b01d40d, + 0x00100070, + 0x23010a0f, + 0x01500070, + 0x00000000, + 0x00000000, + 0x5802d40c, + 0x00100010, + 0x3b01d40d, + 0x00100070, + 0x0b004a0e, + 0x01900070, + 0x23010a0f, + 0x01500070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x40021404, + 0x000f4800, + 0x62031405, + 0x00100040, + 0x53028a06, + 0x01900060, + 0x53028a07, + 0x01900060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x000f4808, + 0x6203140d, + 0x00100048, + 0x53028a0e, + 0x01900068, + 0x53028a0f, + 0x01900068, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000a0c, + 0x00100004, + 0x11008a0d, + 0x00100024, + 0x1980c50e, + 0x00100034, + 0x2181050e, + 0x00100034, + 0x2181050e, + 0x00100034, + 0x0180050c, + 0x00100038, + 0x1180850d, + 0x00100038, + 0x1181850d, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000a0c, + 0x00100008, + 0x11008a0d, + 0x00100028, + 0x2181050e, + 0x00100038, + 0x2181050e, + 0x00100038, + 0x1181850d, + 0x00100038, + 0x2981450f, + 0x01100038, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x08004a04, + 0x00100000, + 0x01000a05, + 0x00100020, + 0x0180c506, + 0x00100030, + 0x0180c506, + 0x00100030, + 0x2180c50c, + 0x00100030, + 0x49820a0d, + 0x0016a130, + 0x41824a0d, + 0x0016a130, + 0x2981450f, + 0x01100030, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x2000ca0c, + 0x00100000, + 0x49820a0d, + 0x0016a130, + 0x1980c50e, + 0x00100030, + 0x41824a0d, + 0x0016a130, + 0x2981450f, + 0x01100030, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x00100008, + 0x0200140d, + 0x00100048, + 0x0b004a0e, + 0x01900068, + 0x13008a0e, + 0x01900068, + 0x13008a0e, + 0x01900068, + 0x43020a0c, + 0x00100070, + 0x1b00ca0d, + 0x00100070, + 0x1b014a0d, + 0x00100070, + 0x23010a0f, + 0x01500070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x00100010, + 0x1a00d40d, + 0x00100050, + 0x13008a0e, + 0x01900070, + 0x13008a0e, + 0x01900070, + 0x1b014a0d, + 0x00100070, + 0x23010a0f, + 0x01500070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x50029404, + 0x00100000, + 0x32019405, + 0x00100040, + 0x03004a06, + 0x01900060, + 0x03004a06, + 0x01900060, + 0x6b030a0c, + 0x00100060, + 0x4b02140d, + 0x0016a160, + 0x4302540d, + 0x0016a160, + 0x23010a0f, + 0x01500060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x6b03140c, + 0x00100060, + 0x4b02140d, + 0x0016a160, + 0x0b004a0e, + 0x01900060, + 0x4302540d, + 0x0016a160, + 0x23010a0f, + 0x01500060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x40021404, + 0x00100000, + 0x1a00d405, + 0x00100040, + 0x53028a06, + 0x01900060, + 0x5b02ca06, + 0x01900060, + 0x5b02ca06, + 0x01900060, + 0x43020a04, + 0x00100060, + 0x1b00ca05, + 0x00100060, + 0x53028a07, + 0x0190c060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x00100010, + 0x1a00d40d, + 0x00100050, + 0x53028a0e, + 0x01900070, + 0x5b02ca0e, + 0x01900070, + 0x5b02ca0e, + 0x01900070, + 0x43020a0c, + 0x00100070, + 0x1b00ca0d, + 0x00100070, + 0x53028a0f, + 0x0190c070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x40021404, + 0x00100000, + 0x1a00d405, + 0x00100040, + 0x5b02ca06, + 0x01900060, + 0x5b02ca06, + 0x01900060, + 0x53028a07, + 0x0190c060, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x4002140c, + 0x00100010, + 0x1a00d40d, + 0x00100050, + 0x5b02ca0e, + 0x01900070, + 0x5b02ca0e, + 0x01900070, + 0x53028a0f, + 0x0190c070, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u16 pilot_tbl_rev3[] = { + 0xff08, + 0xff08, + 0xff08, + 0xff08, + 0xff08, + 0xff08, + 0xff08, + 0xff08, + 0x80d5, + 0x80d5, + 0x80d5, + 0x80d5, + 0x80d5, + 0x80d5, + 0x80d5, + 0x80d5, + 0xff0a, + 0xff82, + 0xffa0, + 0xff28, + 0xffff, + 0xffff, + 0xffff, + 0xffff, + 0xff82, + 0xffa0, + 0xff28, + 0xff0a, + 0xffff, + 0xffff, + 0xffff, + 0xffff, + 0xf83f, + 0xfa1f, + 0xfa97, + 0xfab5, + 0xf2bd, + 0xf0bf, + 0xffff, + 0xffff, + 0xf017, + 0xf815, + 0xf215, + 0xf095, + 0xf035, + 0xf01d, + 0xffff, + 0xffff, + 0xff08, + 0xff02, + 0xff80, + 0xff20, + 0xff08, + 0xff02, + 0xff80, + 0xff20, + 0xf01f, + 0xf817, + 0xfa15, + 0xf295, + 0xf0b5, + 0xf03d, + 0xffff, + 0xffff, + 0xf82a, + 0xfa0a, + 0xfa82, + 0xfaa0, + 0xf2a8, + 0xf0aa, + 0xffff, + 0xffff, + 0xf002, + 0xf800, + 0xf200, + 0xf080, + 0xf020, + 0xf008, + 0xffff, + 0xffff, + 0xf00a, + 0xf802, + 0xfa00, + 0xf280, + 0xf0a0, + 0xf028, + 0xffff, + 0xffff, +}; + +static const u32 tmap_tbl_rev3[] = { + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0xf1111110, + 0x11111111, + 0x11f11111, + 0x00000111, + 0x11000000, + 0x1111f111, + 0x11111111, + 0x111111f1, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x000aa888, + 0x88880000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa2222220, + 0x22222222, + 0x22c22222, + 0x00000222, + 0x22000000, + 0x2222a222, + 0x22222222, + 0x222222a2, + 0xf1111110, + 0x11111111, + 0x11f11111, + 0x00011111, + 0x11110000, + 0x1111f111, + 0x11111111, + 0x111111f1, + 0xa8aa88a0, + 0xa88888a8, + 0xa8a8a88a, + 0x00088aaa, + 0xaaaa0000, + 0xa8a8aa88, + 0xa88aaaaa, + 0xaaaa8a8a, + 0xaaa8aaa0, + 0x8aaa8aaa, + 0xaa8a8a8a, + 0x000aaa88, + 0x8aaa0000, + 0xaaa8a888, + 0x8aa88a8a, + 0x8a88a888, + 0x08080a00, + 0x0a08080a, + 0x080a0a08, + 0x00080808, + 0x080a0000, + 0x080a0808, + 0x080a0808, + 0x0a0a0a08, + 0xa0a0a0a0, + 0x80a0a080, + 0x8080a0a0, + 0x00008080, + 0x80a00000, + 0x80a080a0, + 0xa080a0a0, + 0x8080a0a0, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x99999000, + 0x9b9b99bb, + 0x9bb99999, + 0x9999b9b9, + 0x9b99bb90, + 0x9bbbbb9b, + 0x9b9b9bb9, + 0x00000999, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00aaa888, + 0x22000000, + 0x2222b222, + 0x22222222, + 0x222222b2, + 0xb2222220, + 0x22222222, + 0x22d22222, + 0x00000222, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x33000000, + 0x3333b333, + 0x33333333, + 0x333333b3, + 0xb3333330, + 0x33333333, + 0x33d33333, + 0x00000333, + 0x22000000, + 0x2222a222, + 0x22222222, + 0x222222a2, + 0xa2222220, + 0x22222222, + 0x22c22222, + 0x00000222, + 0x99b99b00, + 0x9b9b99bb, + 0x9bb99999, + 0x9999b9b9, + 0x9b99bb99, + 0x9bbbbb9b, + 0x9b9b9bb9, + 0x00000999, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa88, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x08aaa888, + 0x22222200, + 0x2222f222, + 0x22222222, + 0x222222f2, + 0x22222222, + 0x22222222, + 0x22f22222, + 0x00000222, + 0x11000000, + 0x1111f111, + 0x11111111, + 0x11111111, + 0xf1111111, + 0x11111111, + 0x11f11111, + 0x01111111, + 0xbb9bb900, + 0xb9b9bb99, + 0xb99bbbbb, + 0xbbbb9b9b, + 0xb9bb99bb, + 0xb99999b9, + 0xb9b9b99b, + 0x00000bbb, + 0xaa000000, + 0xa8a8aa88, + 0xa88aaaaa, + 0xaaaa8a8a, + 0xa8aa88aa, + 0xa88888a8, + 0xa8a8a88a, + 0x0a888aaa, + 0xaa000000, + 0xa8a8aa88, + 0xa88aaaaa, + 0xaaaa8a8a, + 0xa8aa88a0, + 0xa88888a8, + 0xa8a8a88a, + 0x00000aaa, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0xbbbbbb00, + 0x999bbbbb, + 0x9bb99b9b, + 0xb9b9b9bb, + 0xb9b99bbb, + 0xb9b9b9bb, + 0xb9bb9b99, + 0x00000999, + 0x8a000000, + 0xaa88a888, + 0xa88888aa, + 0xa88a8a88, + 0xa88aa88a, + 0x88a8aaaa, + 0xa8aa8aaa, + 0x0888a88a, + 0x0b0b0b00, + 0x090b0b0b, + 0x0b090b0b, + 0x0909090b, + 0x09090b0b, + 0x09090b0b, + 0x09090b09, + 0x00000909, + 0x0a000000, + 0x0a080808, + 0x080a080a, + 0x080a0a08, + 0x080a080a, + 0x0808080a, + 0x0a0a0a08, + 0x0808080a, + 0xb0b0b000, + 0x9090b0b0, + 0x90b09090, + 0xb0b0b090, + 0xb0b090b0, + 0x90b0b0b0, + 0xb0b09090, + 0x00000090, + 0x80000000, + 0xa080a080, + 0xa08080a0, + 0xa0808080, + 0xa080a080, + 0x80a0a0a0, + 0xa0a080a0, + 0x00a0a0a0, + 0x22000000, + 0x2222f222, + 0x22222222, + 0x222222f2, + 0xf2222220, + 0x22222222, + 0x22f22222, + 0x00000222, + 0x11000000, + 0x1111f111, + 0x11111111, + 0x111111f1, + 0xf1111110, + 0x11111111, + 0x11f11111, + 0x00000111, + 0x33000000, + 0x3333f333, + 0x33333333, + 0x333333f3, + 0xf3333330, + 0x33333333, + 0x33f33333, + 0x00000333, + 0x22000000, + 0x2222f222, + 0x22222222, + 0x222222f2, + 0xf2222220, + 0x22222222, + 0x22f22222, + 0x00000222, + 0x99000000, + 0x9b9b99bb, + 0x9bb99999, + 0x9999b9b9, + 0x9b99bb90, + 0x9bbbbb9b, + 0x9b9b9bb9, + 0x00000999, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88888000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00aaa888, + 0x88a88a00, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa88, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa88, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x08aaa888, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 intlv_tbl_rev3[] = { + 0x00802070, + 0x0671188d, + 0x0a60192c, + 0x0a300e46, + 0x00c1188d, + 0x080024d2, + 0x00000070, +}; + +static const u32 tdtrn_tbl_rev3[] = { + 0x061c061c, + 0x0050ee68, + 0xf592fe36, + 0xfe5212f6, + 0x00000c38, + 0xfe5212f6, + 0xf592fe36, + 0x0050ee68, + 0x061c061c, + 0xee680050, + 0xfe36f592, + 0x12f6fe52, + 0x0c380000, + 0x12f6fe52, + 0xfe36f592, + 0xee680050, + 0x061c061c, + 0x0050ee68, + 0xf592fe36, + 0xfe5212f6, + 0x00000c38, + 0xfe5212f6, + 0xf592fe36, + 0x0050ee68, + 0x061c061c, + 0xee680050, + 0xfe36f592, + 0x12f6fe52, + 0x0c380000, + 0x12f6fe52, + 0xfe36f592, + 0xee680050, + 0x05e305e3, + 0x004def0c, + 0xf5f3fe47, + 0xfe611246, + 0x00000bc7, + 0xfe611246, + 0xf5f3fe47, + 0x004def0c, + 0x05e305e3, + 0xef0c004d, + 0xfe47f5f3, + 0x1246fe61, + 0x0bc70000, + 0x1246fe61, + 0xfe47f5f3, + 0xef0c004d, + 0x05e305e3, + 0x004def0c, + 0xf5f3fe47, + 0xfe611246, + 0x00000bc7, + 0xfe611246, + 0xf5f3fe47, + 0x004def0c, + 0x05e305e3, + 0xef0c004d, + 0xfe47f5f3, + 0x1246fe61, + 0x0bc70000, + 0x1246fe61, + 0xfe47f5f3, + 0xef0c004d, + 0xfa58fa58, + 0xf895043b, + 0xff4c09c0, + 0xfbc6ffa8, + 0xfb84f384, + 0x0798f6f9, + 0x05760122, + 0x058409f6, + 0x0b500000, + 0x05b7f542, + 0x08860432, + 0x06ddfee7, + 0xfb84f384, + 0xf9d90664, + 0xf7e8025c, + 0x00fff7bd, + 0x05a805a8, + 0xf7bd00ff, + 0x025cf7e8, + 0x0664f9d9, + 0xf384fb84, + 0xfee706dd, + 0x04320886, + 0xf54205b7, + 0x00000b50, + 0x09f60584, + 0x01220576, + 0xf6f90798, + 0xf384fb84, + 0xffa8fbc6, + 0x09c0ff4c, + 0x043bf895, + 0x02d402d4, + 0x07de0270, + 0xfc96079c, + 0xf90afe94, + 0xfe00ff2c, + 0x02d4065d, + 0x092a0096, + 0x0014fbb8, + 0xfd2cfd2c, + 0x076afb3c, + 0x0096f752, + 0xf991fd87, + 0xfb2c0200, + 0xfeb8f960, + 0x08e0fc96, + 0x049802a8, + 0xfd2cfd2c, + 0x02a80498, + 0xfc9608e0, + 0xf960feb8, + 0x0200fb2c, + 0xfd87f991, + 0xf7520096, + 0xfb3c076a, + 0xfd2cfd2c, + 0xfbb80014, + 0x0096092a, + 0x065d02d4, + 0xff2cfe00, + 0xfe94f90a, + 0x079cfc96, + 0x027007de, + 0x02d402d4, + 0x027007de, + 0x079cfc96, + 0xfe94f90a, + 0xff2cfe00, + 0x065d02d4, + 0x0096092a, + 0xfbb80014, + 0xfd2cfd2c, + 0xfb3c076a, + 0xf7520096, + 0xfd87f991, + 0x0200fb2c, + 0xf960feb8, + 0xfc9608e0, + 0x02a80498, + 0xfd2cfd2c, + 0x049802a8, + 0x08e0fc96, + 0xfeb8f960, + 0xfb2c0200, + 0xf991fd87, + 0x0096f752, + 0x076afb3c, + 0xfd2cfd2c, + 0x0014fbb8, + 0x092a0096, + 0x02d4065d, + 0xfe00ff2c, + 0xf90afe94, + 0xfc96079c, + 0x07de0270, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x062a0000, + 0xfefa0759, + 0x08b80908, + 0xf396fc2d, + 0xf9d6045c, + 0xfc4ef608, + 0xf748f596, + 0x07b207bf, + 0x062a062a, + 0xf84ef841, + 0xf748f596, + 0x03b209f8, + 0xf9d6045c, + 0x0c6a03d3, + 0x08b80908, + 0x0106f8a7, + 0x062a0000, + 0xfefaf8a7, + 0x08b8f6f8, + 0xf39603d3, + 0xf9d6fba4, + 0xfc4e09f8, + 0xf7480a6a, + 0x07b2f841, + 0x062af9d6, + 0xf84e07bf, + 0xf7480a6a, + 0x03b2f608, + 0xf9d6fba4, + 0x0c6afc2d, + 0x08b8f6f8, + 0x01060759, + 0x062a0000, + 0xfefa0759, + 0x08b80908, + 0xf396fc2d, + 0xf9d6045c, + 0xfc4ef608, + 0xf748f596, + 0x07b207bf, + 0x062a062a, + 0xf84ef841, + 0xf748f596, + 0x03b209f8, + 0xf9d6045c, + 0x0c6a03d3, + 0x08b80908, + 0x0106f8a7, + 0x062a0000, + 0xfefaf8a7, + 0x08b8f6f8, + 0xf39603d3, + 0xf9d6fba4, + 0xfc4e09f8, + 0xf7480a6a, + 0x07b2f841, + 0x062af9d6, + 0xf84e07bf, + 0xf7480a6a, + 0x03b2f608, + 0xf9d6fba4, + 0x0c6afc2d, + 0x08b8f6f8, + 0x01060759, + 0x061c061c, + 0xff30009d, + 0xffb21141, + 0xfd87fb54, + 0xf65dfe59, + 0x02eef99e, + 0x0166f03c, + 0xfff809b6, + 0x000008a4, + 0x000af42b, + 0x00eff577, + 0xfa840bf2, + 0xfc02ff51, + 0x08260f67, + 0xfff0036f, + 0x0842f9c3, + 0x00000000, + 0x063df7be, + 0xfc910010, + 0xf099f7da, + 0x00af03fe, + 0xf40e057c, + 0x0a89ff11, + 0x0bd5fff6, + 0xf75c0000, + 0xf64a0008, + 0x0fc4fe9a, + 0x0662fd12, + 0x01a709a3, + 0x04ac0279, + 0xeebf004e, + 0xff6300d0, + 0xf9e4f9e4, + 0x00d0ff63, + 0x004eeebf, + 0x027904ac, + 0x09a301a7, + 0xfd120662, + 0xfe9a0fc4, + 0x0008f64a, + 0x0000f75c, + 0xfff60bd5, + 0xff110a89, + 0x057cf40e, + 0x03fe00af, + 0xf7daf099, + 0x0010fc91, + 0xf7be063d, + 0x00000000, + 0xf9c30842, + 0x036ffff0, + 0x0f670826, + 0xff51fc02, + 0x0bf2fa84, + 0xf57700ef, + 0xf42b000a, + 0x08a40000, + 0x09b6fff8, + 0xf03c0166, + 0xf99e02ee, + 0xfe59f65d, + 0xfb54fd87, + 0x1141ffb2, + 0x009dff30, + 0x05e30000, + 0xff060705, + 0x085408a0, + 0xf425fc59, + 0xfa1d042a, + 0xfc78f67a, + 0xf7acf60e, + 0x075a0766, + 0x05e305e3, + 0xf8a6f89a, + 0xf7acf60e, + 0x03880986, + 0xfa1d042a, + 0x0bdb03a7, + 0x085408a0, + 0x00faf8fb, + 0x05e30000, + 0xff06f8fb, + 0x0854f760, + 0xf42503a7, + 0xfa1dfbd6, + 0xfc780986, + 0xf7ac09f2, + 0x075af89a, + 0x05e3fa1d, + 0xf8a60766, + 0xf7ac09f2, + 0x0388f67a, + 0xfa1dfbd6, + 0x0bdbfc59, + 0x0854f760, + 0x00fa0705, + 0x05e30000, + 0xff060705, + 0x085408a0, + 0xf425fc59, + 0xfa1d042a, + 0xfc78f67a, + 0xf7acf60e, + 0x075a0766, + 0x05e305e3, + 0xf8a6f89a, + 0xf7acf60e, + 0x03880986, + 0xfa1d042a, + 0x0bdb03a7, + 0x085408a0, + 0x00faf8fb, + 0x05e30000, + 0xff06f8fb, + 0x0854f760, + 0xf42503a7, + 0xfa1dfbd6, + 0xfc780986, + 0xf7ac09f2, + 0x075af89a, + 0x05e3fa1d, + 0xf8a60766, + 0xf7ac09f2, + 0x0388f67a, + 0xfa1dfbd6, + 0x0bdbfc59, + 0x0854f760, + 0x00fa0705, + 0xfa58fa58, + 0xf8f0fe00, + 0x0448073d, + 0xfdc9fe46, + 0xf9910258, + 0x089d0407, + 0xfd5cf71a, + 0x02affde0, + 0x083e0496, + 0xff5a0740, + 0xff7afd97, + 0x00fe01f1, + 0x0009082e, + 0xfa94ff75, + 0xfecdf8ea, + 0xffb0f693, + 0xfd2cfa58, + 0x0433ff16, + 0xfba405dd, + 0xfa610341, + 0x06a606cb, + 0x0039fd2d, + 0x0677fa97, + 0x01fa05e0, + 0xf896003e, + 0x075a068b, + 0x012cfc3e, + 0xfa23f98d, + 0xfc7cfd43, + 0xff90fc0d, + 0x01c10982, + 0x00c601d6, + 0xfd2cfd2c, + 0x01d600c6, + 0x098201c1, + 0xfc0dff90, + 0xfd43fc7c, + 0xf98dfa23, + 0xfc3e012c, + 0x068b075a, + 0x003ef896, + 0x05e001fa, + 0xfa970677, + 0xfd2d0039, + 0x06cb06a6, + 0x0341fa61, + 0x05ddfba4, + 0xff160433, + 0xfa58fd2c, + 0xf693ffb0, + 0xf8eafecd, + 0xff75fa94, + 0x082e0009, + 0x01f100fe, + 0xfd97ff7a, + 0x0740ff5a, + 0x0496083e, + 0xfde002af, + 0xf71afd5c, + 0x0407089d, + 0x0258f991, + 0xfe46fdc9, + 0x073d0448, + 0xfe00f8f0, + 0xfd2cfd2c, + 0xfce00500, + 0xfc09fddc, + 0xfe680157, + 0x04c70571, + 0xfc3aff21, + 0xfcd70228, + 0x056d0277, + 0x0200fe00, + 0x0022f927, + 0xfe3c032b, + 0xfc44ff3c, + 0x03e9fbdb, + 0x04570313, + 0x04c9ff5c, + 0x000d03b8, + 0xfa580000, + 0xfbe900d2, + 0xf9d0fe0b, + 0x0125fdf9, + 0x042501bf, + 0x0328fa2b, + 0xffa902f0, + 0xfa250157, + 0x0200fe00, + 0x03740438, + 0xff0405fd, + 0x030cfe52, + 0x0037fb39, + 0xff6904c5, + 0x04f8fd23, + 0xfd31fc1b, + 0xfd2cfd2c, + 0xfc1bfd31, + 0xfd2304f8, + 0x04c5ff69, + 0xfb390037, + 0xfe52030c, + 0x05fdff04, + 0x04380374, + 0xfe000200, + 0x0157fa25, + 0x02f0ffa9, + 0xfa2b0328, + 0x01bf0425, + 0xfdf90125, + 0xfe0bf9d0, + 0x00d2fbe9, + 0x0000fa58, + 0x03b8000d, + 0xff5c04c9, + 0x03130457, + 0xfbdb03e9, + 0xff3cfc44, + 0x032bfe3c, + 0xf9270022, + 0xfe000200, + 0x0277056d, + 0x0228fcd7, + 0xff21fc3a, + 0x057104c7, + 0x0157fe68, + 0xfddcfc09, + 0x0500fce0, + 0xfd2cfd2c, + 0x0500fce0, + 0xfddcfc09, + 0x0157fe68, + 0x057104c7, + 0xff21fc3a, + 0x0228fcd7, + 0x0277056d, + 0xfe000200, + 0xf9270022, + 0x032bfe3c, + 0xff3cfc44, + 0xfbdb03e9, + 0x03130457, + 0xff5c04c9, + 0x03b8000d, + 0x0000fa58, + 0x00d2fbe9, + 0xfe0bf9d0, + 0xfdf90125, + 0x01bf0425, + 0xfa2b0328, + 0x02f0ffa9, + 0x0157fa25, + 0xfe000200, + 0x04380374, + 0x05fdff04, + 0xfe52030c, + 0xfb390037, + 0x04c5ff69, + 0xfd2304f8, + 0xfc1bfd31, + 0xfd2cfd2c, + 0xfd31fc1b, + 0x04f8fd23, + 0xff6904c5, + 0x0037fb39, + 0x030cfe52, + 0xff0405fd, + 0x03740438, + 0x0200fe00, + 0xfa250157, + 0xffa902f0, + 0x0328fa2b, + 0x042501bf, + 0x0125fdf9, + 0xf9d0fe0b, + 0xfbe900d2, + 0xfa580000, + 0x000d03b8, + 0x04c9ff5c, + 0x04570313, + 0x03e9fbdb, + 0xfc44ff3c, + 0xfe3c032b, + 0x0022f927, + 0x0200fe00, + 0x056d0277, + 0xfcd70228, + 0xfc3aff21, + 0x04c70571, + 0xfe680157, + 0xfc09fddc, + 0xfce00500, + 0x05a80000, + 0xff1006be, + 0x0800084a, + 0xf49cfc7e, + 0xfa580400, + 0xfc9cf6da, + 0xf800f672, + 0x0710071c, + 0x05a805a8, + 0xf8f0f8e4, + 0xf800f672, + 0x03640926, + 0xfa580400, + 0x0b640382, + 0x0800084a, + 0x00f0f942, + 0x05a80000, + 0xff10f942, + 0x0800f7b6, + 0xf49c0382, + 0xfa58fc00, + 0xfc9c0926, + 0xf800098e, + 0x0710f8e4, + 0x05a8fa58, + 0xf8f0071c, + 0xf800098e, + 0x0364f6da, + 0xfa58fc00, + 0x0b64fc7e, + 0x0800f7b6, + 0x00f006be, + 0x05a80000, + 0xff1006be, + 0x0800084a, + 0xf49cfc7e, + 0xfa580400, + 0xfc9cf6da, + 0xf800f672, + 0x0710071c, + 0x05a805a8, + 0xf8f0f8e4, + 0xf800f672, + 0x03640926, + 0xfa580400, + 0x0b640382, + 0x0800084a, + 0x00f0f942, + 0x05a80000, + 0xff10f942, + 0x0800f7b6, + 0xf49c0382, + 0xfa58fc00, + 0xfc9c0926, + 0xf800098e, + 0x0710f8e4, + 0x05a8fa58, + 0xf8f0071c, + 0xf800098e, + 0x0364f6da, + 0xfa58fc00, + 0x0b64fc7e, + 0x0800f7b6, + 0x00f006be, +}; + +const u32 noise_var_tbl_rev3[] = { + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, + 0x02110211, + 0x0000014d, +}; + +static const u16 mcs_tbl_rev3[] = { + 0x0000, + 0x0008, + 0x000a, + 0x0010, + 0x0012, + 0x0019, + 0x001a, + 0x001c, + 0x0080, + 0x0088, + 0x008a, + 0x0090, + 0x0092, + 0x0099, + 0x009a, + 0x009c, + 0x0100, + 0x0108, + 0x010a, + 0x0110, + 0x0112, + 0x0119, + 0x011a, + 0x011c, + 0x0180, + 0x0188, + 0x018a, + 0x0190, + 0x0192, + 0x0199, + 0x019a, + 0x019c, + 0x0000, + 0x0098, + 0x00a0, + 0x00a8, + 0x009a, + 0x00a2, + 0x00aa, + 0x0120, + 0x0128, + 0x0128, + 0x0130, + 0x0138, + 0x0138, + 0x0140, + 0x0122, + 0x012a, + 0x012a, + 0x0132, + 0x013a, + 0x013a, + 0x0142, + 0x01a8, + 0x01b0, + 0x01b8, + 0x01b0, + 0x01b8, + 0x01c0, + 0x01c8, + 0x01c0, + 0x01c8, + 0x01d0, + 0x01d0, + 0x01d8, + 0x01aa, + 0x01b2, + 0x01ba, + 0x01b2, + 0x01ba, + 0x01c2, + 0x01ca, + 0x01c2, + 0x01ca, + 0x01d2, + 0x01d2, + 0x01da, + 0x0001, + 0x0002, + 0x0004, + 0x0009, + 0x000c, + 0x0011, + 0x0014, + 0x0018, + 0x0020, + 0x0021, + 0x0022, + 0x0024, + 0x0081, + 0x0082, + 0x0084, + 0x0089, + 0x008c, + 0x0091, + 0x0094, + 0x0098, + 0x00a0, + 0x00a1, + 0x00a2, + 0x00a4, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, + 0x0007, +}; + +static const u32 tdi_tbl20_ant0_rev3[] = { + 0x00091226, + 0x000a1429, + 0x000b56ad, + 0x000c58b0, + 0x000d5ab3, + 0x000e9cb6, + 0x000f9eba, + 0x0000c13d, + 0x00020301, + 0x00030504, + 0x00040708, + 0x0005090b, + 0x00064b8e, + 0x00095291, + 0x000a5494, + 0x000b9718, + 0x000c9927, + 0x000d9b2a, + 0x000edd2e, + 0x000fdf31, + 0x000101b4, + 0x000243b7, + 0x000345bb, + 0x000447be, + 0x00058982, + 0x00068c05, + 0x00099309, + 0x000a950c, + 0x000bd78f, + 0x000cd992, + 0x000ddb96, + 0x000f1d99, + 0x00005fa8, + 0x0001422c, + 0x0002842f, + 0x00038632, + 0x00048835, + 0x0005ca38, + 0x0006ccbc, + 0x0009d3bf, + 0x000b1603, + 0x000c1806, + 0x000d1a0a, + 0x000e1c0d, + 0x000f5e10, + 0x00008093, + 0x00018297, + 0x0002c49a, + 0x0003c680, + 0x0004c880, + 0x00060b00, + 0x00070d00, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 tdi_tbl20_ant1_rev3[] = { + 0x00014b26, + 0x00028d29, + 0x000393ad, + 0x00049630, + 0x0005d833, + 0x0006da36, + 0x00099c3a, + 0x000a9e3d, + 0x000bc081, + 0x000cc284, + 0x000dc488, + 0x000f068b, + 0x0000488e, + 0x00018b91, + 0x0002d214, + 0x0003d418, + 0x0004d6a7, + 0x000618aa, + 0x00071aae, + 0x0009dcb1, + 0x000b1eb4, + 0x000c0137, + 0x000d033b, + 0x000e053e, + 0x000f4702, + 0x00008905, + 0x00020c09, + 0x0003128c, + 0x0004148f, + 0x00051712, + 0x00065916, + 0x00091b19, + 0x000a1d28, + 0x000b5f2c, + 0x000c41af, + 0x000d43b2, + 0x000e85b5, + 0x000f87b8, + 0x0000c9bc, + 0x00024cbf, + 0x00035303, + 0x00045506, + 0x0005978a, + 0x0006998d, + 0x00095b90, + 0x000a5d93, + 0x000b9f97, + 0x000c821a, + 0x000d8400, + 0x000ec600, + 0x000fc800, + 0x00010a00, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 tdi_tbl40_ant0_rev3[] = { + 0x0011a346, + 0x00136ccf, + 0x0014f5d9, + 0x001641e2, + 0x0017cb6b, + 0x00195475, + 0x001b2383, + 0x001cad0c, + 0x001e7616, + 0x0000821f, + 0x00020ba8, + 0x0003d4b2, + 0x00056447, + 0x00072dd0, + 0x0008b6da, + 0x000a02e3, + 0x000b8c6c, + 0x000d15f6, + 0x0011e484, + 0x0013ae0d, + 0x00153717, + 0x00168320, + 0x00180ca9, + 0x00199633, + 0x001b6548, + 0x001ceed1, + 0x001eb7db, + 0x0000c3e4, + 0x00024d6d, + 0x000416f7, + 0x0005a585, + 0x00076f0f, + 0x0008f818, + 0x000a4421, + 0x000bcdab, + 0x000d9734, + 0x00122649, + 0x0013efd2, + 0x001578dc, + 0x0016c4e5, + 0x00184e6e, + 0x001a17f8, + 0x001ba686, + 0x001d3010, + 0x001ef999, + 0x00010522, + 0x00028eac, + 0x00045835, + 0x0005e74a, + 0x0007b0d3, + 0x00093a5d, + 0x000a85e6, + 0x000c0f6f, + 0x000dd8f9, + 0x00126787, + 0x00143111, + 0x0015ba9a, + 0x00170623, + 0x00188fad, + 0x001a5936, + 0x001be84b, + 0x001db1d4, + 0x001f3b5e, + 0x000146e7, + 0x00031070, + 0x000499fa, + 0x00062888, + 0x0007f212, + 0x00097b9b, + 0x000ac7a4, + 0x000c50ae, + 0x000e1a37, + 0x0012a94c, + 0x001472d5, + 0x0015fc5f, + 0x00174868, + 0x0018d171, + 0x001a9afb, + 0x001c2989, + 0x001df313, + 0x001f7c9c, + 0x000188a5, + 0x000351af, + 0x0004db38, + 0x0006aa4d, + 0x000833d7, + 0x0009bd60, + 0x000b0969, + 0x000c9273, + 0x000e5bfc, + 0x00132a8a, + 0x0014b414, + 0x00163d9d, + 0x001789a6, + 0x001912b0, + 0x001adc39, + 0x001c6bce, + 0x001e34d8, + 0x001fbe61, + 0x0001ca6a, + 0x00039374, + 0x00051cfd, + 0x0006ec0b, + 0x00087515, + 0x0009fe9e, + 0x000b4aa7, + 0x000cd3b1, + 0x000e9d3a, + 0x00000000, + 0x00000000, +}; + +static const u32 tdi_tbl40_ant1_rev3[] = { + 0x001edb36, + 0x000129ca, + 0x0002b353, + 0x00047cdd, + 0x0005c8e6, + 0x000791ef, + 0x00091bf9, + 0x000aaa07, + 0x000c3391, + 0x000dfd1a, + 0x00120923, + 0x0013d22d, + 0x00155c37, + 0x0016eacb, + 0x00187454, + 0x001a3dde, + 0x001b89e7, + 0x001d12f0, + 0x001f1cfa, + 0x00016b88, + 0x00033492, + 0x0004be1b, + 0x00060a24, + 0x0007d32e, + 0x00095d38, + 0x000aec4c, + 0x000c7555, + 0x000e3edf, + 0x00124ae8, + 0x001413f1, + 0x0015a37b, + 0x00172c89, + 0x0018b593, + 0x001a419c, + 0x001bcb25, + 0x001d942f, + 0x001f63b9, + 0x0001ad4d, + 0x00037657, + 0x0004c260, + 0x00068be9, + 0x000814f3, + 0x0009a47c, + 0x000b2d8a, + 0x000cb694, + 0x000e429d, + 0x00128c26, + 0x001455b0, + 0x0015e4ba, + 0x00176e4e, + 0x0018f758, + 0x001a8361, + 0x001c0cea, + 0x001dd674, + 0x001fa57d, + 0x0001ee8b, + 0x0003b795, + 0x0005039e, + 0x0006cd27, + 0x000856b1, + 0x0009e5c6, + 0x000b6f4f, + 0x000cf859, + 0x000e8462, + 0x00130deb, + 0x00149775, + 0x00162603, + 0x0017af8c, + 0x00193896, + 0x001ac49f, + 0x001c4e28, + 0x001e17b2, + 0x0000a6c7, + 0x00023050, + 0x0003f9da, + 0x00054563, + 0x00070eec, + 0x00089876, + 0x000a2704, + 0x000bb08d, + 0x000d3a17, + 0x001185a0, + 0x00134f29, + 0x0014d8b3, + 0x001667c8, + 0x0017f151, + 0x00197adb, + 0x001b0664, + 0x001c8fed, + 0x001e5977, + 0x0000e805, + 0x0002718f, + 0x00043b18, + 0x000586a1, + 0x0007502b, + 0x0008d9b4, + 0x000a68c9, + 0x000bf252, + 0x000dbbdc, + 0x0011c7e5, + 0x001390ee, + 0x00151a78, + 0x0016a906, + 0x00183290, + 0x0019bc19, + 0x001b4822, + 0x001cd12c, + 0x001e9ab5, + 0x00000000, + 0x00000000, +}; + +static const u32 pltlut_tbl_rev3[] = { + 0x76540213, + 0x62407351, + 0x76543210, + 0x76540213, + 0x76540213, + 0x76430521, +}; + +static const u32 chanest_tbl_rev3[] = { + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x44444444, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, + 0x10101010, +}; + +static const u8 frame_lut_rev3[] = { + 0x02, + 0x04, + 0x14, + 0x14, + 0x03, + 0x05, + 0x16, + 0x16, + 0x0a, + 0x0c, + 0x1c, + 0x1c, + 0x0b, + 0x0d, + 0x1e, + 0x1e, + 0x06, + 0x08, + 0x18, + 0x18, + 0x07, + 0x09, + 0x1a, + 0x1a, + 0x0e, + 0x10, + 0x20, + 0x28, + 0x0f, + 0x11, + 0x22, + 0x2a, +}; + +static const u8 est_pwr_lut_core0_rev3[] = { + 0x55, + 0x54, + 0x54, + 0x53, + 0x52, + 0x52, + 0x51, + 0x51, + 0x50, + 0x4f, + 0x4f, + 0x4e, + 0x4e, + 0x4d, + 0x4c, + 0x4c, + 0x4b, + 0x4a, + 0x49, + 0x49, + 0x48, + 0x47, + 0x46, + 0x46, + 0x45, + 0x44, + 0x43, + 0x42, + 0x41, + 0x40, + 0x40, + 0x3f, + 0x3e, + 0x3d, + 0x3c, + 0x3a, + 0x39, + 0x38, + 0x37, + 0x36, + 0x35, + 0x33, + 0x32, + 0x31, + 0x2f, + 0x2e, + 0x2c, + 0x2b, + 0x29, + 0x27, + 0x25, + 0x23, + 0x21, + 0x1f, + 0x1d, + 0x1a, + 0x18, + 0x15, + 0x12, + 0x0e, + 0x0b, + 0x07, + 0x02, + 0xfd, +}; + +static const u8 est_pwr_lut_core1_rev3[] = { + 0x55, + 0x54, + 0x54, + 0x53, + 0x52, + 0x52, + 0x51, + 0x51, + 0x50, + 0x4f, + 0x4f, + 0x4e, + 0x4e, + 0x4d, + 0x4c, + 0x4c, + 0x4b, + 0x4a, + 0x49, + 0x49, + 0x48, + 0x47, + 0x46, + 0x46, + 0x45, + 0x44, + 0x43, + 0x42, + 0x41, + 0x40, + 0x40, + 0x3f, + 0x3e, + 0x3d, + 0x3c, + 0x3a, + 0x39, + 0x38, + 0x37, + 0x36, + 0x35, + 0x33, + 0x32, + 0x31, + 0x2f, + 0x2e, + 0x2c, + 0x2b, + 0x29, + 0x27, + 0x25, + 0x23, + 0x21, + 0x1f, + 0x1d, + 0x1a, + 0x18, + 0x15, + 0x12, + 0x0e, + 0x0b, + 0x07, + 0x02, + 0xfd, +}; + +static const u8 adj_pwr_lut_core0_rev3[] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +static const u8 adj_pwr_lut_core1_rev3[] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +static const u32 gainctrl_lut_core0_rev3[] = { + 0x5bf70044, + 0x5bf70042, + 0x5bf70040, + 0x5bf7003e, + 0x5bf7003c, + 0x5bf7003b, + 0x5bf70039, + 0x5bf70037, + 0x5bf70036, + 0x5bf70034, + 0x5bf70033, + 0x5bf70031, + 0x5bf70030, + 0x5ba70044, + 0x5ba70042, + 0x5ba70040, + 0x5ba7003e, + 0x5ba7003c, + 0x5ba7003b, + 0x5ba70039, + 0x5ba70037, + 0x5ba70036, + 0x5ba70034, + 0x5ba70033, + 0x5b770044, + 0x5b770042, + 0x5b770040, + 0x5b77003e, + 0x5b77003c, + 0x5b77003b, + 0x5b770039, + 0x5b770037, + 0x5b770036, + 0x5b770034, + 0x5b770033, + 0x5b770031, + 0x5b770030, + 0x5b77002f, + 0x5b77002d, + 0x5b77002c, + 0x5b470044, + 0x5b470042, + 0x5b470040, + 0x5b47003e, + 0x5b47003c, + 0x5b47003b, + 0x5b470039, + 0x5b470037, + 0x5b470036, + 0x5b470034, + 0x5b470033, + 0x5b470031, + 0x5b470030, + 0x5b47002f, + 0x5b47002d, + 0x5b47002c, + 0x5b47002b, + 0x5b47002a, + 0x5b270044, + 0x5b270042, + 0x5b270040, + 0x5b27003e, + 0x5b27003c, + 0x5b27003b, + 0x5b270039, + 0x5b270037, + 0x5b270036, + 0x5b270034, + 0x5b270033, + 0x5b270031, + 0x5b270030, + 0x5b27002f, + 0x5b170044, + 0x5b170042, + 0x5b170040, + 0x5b17003e, + 0x5b17003c, + 0x5b17003b, + 0x5b170039, + 0x5b170037, + 0x5b170036, + 0x5b170034, + 0x5b170033, + 0x5b170031, + 0x5b170030, + 0x5b17002f, + 0x5b17002d, + 0x5b17002c, + 0x5b17002b, + 0x5b17002a, + 0x5b170028, + 0x5b170027, + 0x5b170026, + 0x5b170025, + 0x5b170024, + 0x5b170023, + 0x5b070044, + 0x5b070042, + 0x5b070040, + 0x5b07003e, + 0x5b07003c, + 0x5b07003b, + 0x5b070039, + 0x5b070037, + 0x5b070036, + 0x5b070034, + 0x5b070033, + 0x5b070031, + 0x5b070030, + 0x5b07002f, + 0x5b07002d, + 0x5b07002c, + 0x5b07002b, + 0x5b07002a, + 0x5b070028, + 0x5b070027, + 0x5b070026, + 0x5b070025, + 0x5b070024, + 0x5b070023, + 0x5b070022, + 0x5b070021, + 0x5b070020, + 0x5b07001f, + 0x5b07001e, + 0x5b07001d, + 0x5b07001d, + 0x5b07001c, +}; + +static const u32 gainctrl_lut_core1_rev3[] = { + 0x5bf70044, + 0x5bf70042, + 0x5bf70040, + 0x5bf7003e, + 0x5bf7003c, + 0x5bf7003b, + 0x5bf70039, + 0x5bf70037, + 0x5bf70036, + 0x5bf70034, + 0x5bf70033, + 0x5bf70031, + 0x5bf70030, + 0x5ba70044, + 0x5ba70042, + 0x5ba70040, + 0x5ba7003e, + 0x5ba7003c, + 0x5ba7003b, + 0x5ba70039, + 0x5ba70037, + 0x5ba70036, + 0x5ba70034, + 0x5ba70033, + 0x5b770044, + 0x5b770042, + 0x5b770040, + 0x5b77003e, + 0x5b77003c, + 0x5b77003b, + 0x5b770039, + 0x5b770037, + 0x5b770036, + 0x5b770034, + 0x5b770033, + 0x5b770031, + 0x5b770030, + 0x5b77002f, + 0x5b77002d, + 0x5b77002c, + 0x5b470044, + 0x5b470042, + 0x5b470040, + 0x5b47003e, + 0x5b47003c, + 0x5b47003b, + 0x5b470039, + 0x5b470037, + 0x5b470036, + 0x5b470034, + 0x5b470033, + 0x5b470031, + 0x5b470030, + 0x5b47002f, + 0x5b47002d, + 0x5b47002c, + 0x5b47002b, + 0x5b47002a, + 0x5b270044, + 0x5b270042, + 0x5b270040, + 0x5b27003e, + 0x5b27003c, + 0x5b27003b, + 0x5b270039, + 0x5b270037, + 0x5b270036, + 0x5b270034, + 0x5b270033, + 0x5b270031, + 0x5b270030, + 0x5b27002f, + 0x5b170044, + 0x5b170042, + 0x5b170040, + 0x5b17003e, + 0x5b17003c, + 0x5b17003b, + 0x5b170039, + 0x5b170037, + 0x5b170036, + 0x5b170034, + 0x5b170033, + 0x5b170031, + 0x5b170030, + 0x5b17002f, + 0x5b17002d, + 0x5b17002c, + 0x5b17002b, + 0x5b17002a, + 0x5b170028, + 0x5b170027, + 0x5b170026, + 0x5b170025, + 0x5b170024, + 0x5b170023, + 0x5b070044, + 0x5b070042, + 0x5b070040, + 0x5b07003e, + 0x5b07003c, + 0x5b07003b, + 0x5b070039, + 0x5b070037, + 0x5b070036, + 0x5b070034, + 0x5b070033, + 0x5b070031, + 0x5b070030, + 0x5b07002f, + 0x5b07002d, + 0x5b07002c, + 0x5b07002b, + 0x5b07002a, + 0x5b070028, + 0x5b070027, + 0x5b070026, + 0x5b070025, + 0x5b070024, + 0x5b070023, + 0x5b070022, + 0x5b070021, + 0x5b070020, + 0x5b07001f, + 0x5b07001e, + 0x5b07001d, + 0x5b07001d, + 0x5b07001c, +}; + +static const u32 iq_lut_core0_rev3[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 iq_lut_core1_rev3[] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u16 loft_lut_core0_rev3[] = { + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, +}; + +static const u16 loft_lut_core1_rev3[] = { + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, +}; + +static const u16 papd_comp_rfpwr_tbl_core0_rev3[] = { + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, +}; + +static const u16 papd_comp_rfpwr_tbl_core1_rev3[] = { + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x0036, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x002a, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x001e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x000e, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01fc, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01ee, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, + 0x01d6, +}; + +static const u32 papd_comp_epsilon_tbl_core0_rev3[] = { + 0x00000000, + 0x00001fa0, + 0x00019f78, + 0x0001df7e, + 0x03fa9f86, + 0x03fd1f90, + 0x03fe5f8a, + 0x03fb1f94, + 0x03fd9fa0, + 0x00009f98, + 0x03fd1fac, + 0x03ff9fa2, + 0x03fe9fae, + 0x00001fae, + 0x03fddfb4, + 0x03ff1fb8, + 0x03ff9fbc, + 0x03ffdfbe, + 0x03fe9fc2, + 0x03fedfc6, + 0x03fedfc6, + 0x03ff9fc8, + 0x03ff5fc6, + 0x03fedfc2, + 0x03ff9fc0, + 0x03ff5fac, + 0x03ff5fac, + 0x03ff9fa2, + 0x03ff9fa6, + 0x03ff9faa, + 0x03ff5fb0, + 0x03ff5fb4, + 0x03ff1fca, + 0x03ff5fce, + 0x03fcdfdc, + 0x03fb4006, + 0x00000030, + 0x03ff808a, + 0x03ff80da, + 0x0000016c, + 0x03ff8318, + 0x03ff063a, + 0x03fd8bd6, + 0x00014ffe, + 0x00034ffe, + 0x00034ffe, + 0x0003cffe, + 0x00040ffe, + 0x00040ffe, + 0x0003cffe, + 0x0003cffe, + 0x00020ffe, + 0x03fe0ffe, + 0x03fdcffe, + 0x03f94ffe, + 0x03f54ffe, + 0x03f44ffe, + 0x03ef8ffe, + 0x03ee0ffe, + 0x03ebcffe, + 0x03e8cffe, + 0x03e74ffe, + 0x03e4cffe, + 0x03e38ffe, +}; + +static const u32 papd_cal_scalars_tbl_core0_rev3[] = { + 0x05af005a, + 0x0571005e, + 0x05040066, + 0x04bd006c, + 0x047d0072, + 0x04430078, + 0x03f70081, + 0x03cb0087, + 0x03870091, + 0x035e0098, + 0x032e00a1, + 0x030300aa, + 0x02d800b4, + 0x02ae00bf, + 0x028900ca, + 0x026400d6, + 0x024100e3, + 0x022200f0, + 0x020200ff, + 0x01e5010e, + 0x01ca011e, + 0x01b0012f, + 0x01990140, + 0x01830153, + 0x016c0168, + 0x0158017d, + 0x01450193, + 0x013301ab, + 0x012101c5, + 0x011101e0, + 0x010201fc, + 0x00f4021a, + 0x00e6011d, + 0x00d9012e, + 0x00cd0140, + 0x00c20153, + 0x00b70167, + 0x00ac017c, + 0x00a30193, + 0x009a01ab, + 0x009101c4, + 0x008901df, + 0x008101fb, + 0x007a0219, + 0x00730239, + 0x006d025b, + 0x0067027e, + 0x006102a4, + 0x005c02cc, + 0x005602f6, + 0x00520323, + 0x004d0353, + 0x00490385, + 0x004503bb, + 0x004103f3, + 0x003d042f, + 0x003a046f, + 0x003704b2, + 0x003404f9, + 0x00310545, + 0x002e0596, + 0x002b05f5, + 0x00290640, + 0x002606a4, +}; + +static const u32 papd_comp_epsilon_tbl_core1_rev3[] = { + 0x00000000, + 0x00001fa0, + 0x00019f78, + 0x0001df7e, + 0x03fa9f86, + 0x03fd1f90, + 0x03fe5f8a, + 0x03fb1f94, + 0x03fd9fa0, + 0x00009f98, + 0x03fd1fac, + 0x03ff9fa2, + 0x03fe9fae, + 0x00001fae, + 0x03fddfb4, + 0x03ff1fb8, + 0x03ff9fbc, + 0x03ffdfbe, + 0x03fe9fc2, + 0x03fedfc6, + 0x03fedfc6, + 0x03ff9fc8, + 0x03ff5fc6, + 0x03fedfc2, + 0x03ff9fc0, + 0x03ff5fac, + 0x03ff5fac, + 0x03ff9fa2, + 0x03ff9fa6, + 0x03ff9faa, + 0x03ff5fb0, + 0x03ff5fb4, + 0x03ff1fca, + 0x03ff5fce, + 0x03fcdfdc, + 0x03fb4006, + 0x00000030, + 0x03ff808a, + 0x03ff80da, + 0x0000016c, + 0x03ff8318, + 0x03ff063a, + 0x03fd8bd6, + 0x00014ffe, + 0x00034ffe, + 0x00034ffe, + 0x0003cffe, + 0x00040ffe, + 0x00040ffe, + 0x0003cffe, + 0x0003cffe, + 0x00020ffe, + 0x03fe0ffe, + 0x03fdcffe, + 0x03f94ffe, + 0x03f54ffe, + 0x03f44ffe, + 0x03ef8ffe, + 0x03ee0ffe, + 0x03ebcffe, + 0x03e8cffe, + 0x03e74ffe, + 0x03e4cffe, + 0x03e38ffe, +}; + +static const u32 papd_cal_scalars_tbl_core1_rev3[] = { + 0x05af005a, + 0x0571005e, + 0x05040066, + 0x04bd006c, + 0x047d0072, + 0x04430078, + 0x03f70081, + 0x03cb0087, + 0x03870091, + 0x035e0098, + 0x032e00a1, + 0x030300aa, + 0x02d800b4, + 0x02ae00bf, + 0x028900ca, + 0x026400d6, + 0x024100e3, + 0x022200f0, + 0x020200ff, + 0x01e5010e, + 0x01ca011e, + 0x01b0012f, + 0x01990140, + 0x01830153, + 0x016c0168, + 0x0158017d, + 0x01450193, + 0x013301ab, + 0x012101c5, + 0x011101e0, + 0x010201fc, + 0x00f4021a, + 0x00e6011d, + 0x00d9012e, + 0x00cd0140, + 0x00c20153, + 0x00b70167, + 0x00ac017c, + 0x00a30193, + 0x009a01ab, + 0x009101c4, + 0x008901df, + 0x008101fb, + 0x007a0219, + 0x00730239, + 0x006d025b, + 0x0067027e, + 0x006102a4, + 0x005c02cc, + 0x005602f6, + 0x00520323, + 0x004d0353, + 0x00490385, + 0x004503bb, + 0x004103f3, + 0x003d042f, + 0x003a046f, + 0x003704b2, + 0x003404f9, + 0x00310545, + 0x002e0596, + 0x002b05f5, + 0x00290640, + 0x002606a4, +}; + +const struct phytbl_info mimophytbl_info_rev3_volatile[] = { + {&ant_swctrl_tbl_rev3, + sizeof(ant_swctrl_tbl_rev3) / sizeof(ant_swctrl_tbl_rev3[0]), 9, 0, 16} + , +}; + +const struct phytbl_info mimophytbl_info_rev3_volatile1[] = { + {&ant_swctrl_tbl_rev3_1, + sizeof(ant_swctrl_tbl_rev3_1) / sizeof(ant_swctrl_tbl_rev3_1[0]), 9, 0, + 16} + , +}; + +const struct phytbl_info mimophytbl_info_rev3_volatile2[] = { + {&ant_swctrl_tbl_rev3_2, + sizeof(ant_swctrl_tbl_rev3_2) / sizeof(ant_swctrl_tbl_rev3_2[0]), 9, 0, + 16} + , +}; + +const struct phytbl_info mimophytbl_info_rev3_volatile3[] = { + {&ant_swctrl_tbl_rev3_3, + sizeof(ant_swctrl_tbl_rev3_3) / sizeof(ant_swctrl_tbl_rev3_3[0]), 9, 0, + 16} + , +}; + +const struct phytbl_info mimophytbl_info_rev3[] = { + {&frame_struct_rev3, + sizeof(frame_struct_rev3) / sizeof(frame_struct_rev3[0]), 10, 0, 32} + , + {&pilot_tbl_rev3, sizeof(pilot_tbl_rev3) / sizeof(pilot_tbl_rev3[0]), + 11, 0, 16} + , + {&tmap_tbl_rev3, sizeof(tmap_tbl_rev3) / sizeof(tmap_tbl_rev3[0]), 12, + 0, 32} + , + {&intlv_tbl_rev3, sizeof(intlv_tbl_rev3) / sizeof(intlv_tbl_rev3[0]), + 13, 0, 32} + , + {&tdtrn_tbl_rev3, sizeof(tdtrn_tbl_rev3) / sizeof(tdtrn_tbl_rev3[0]), + 14, 0, 32} + , + {&noise_var_tbl_rev3, + sizeof(noise_var_tbl_rev3) / sizeof(noise_var_tbl_rev3[0]), 16, 0, 32} + , + {&mcs_tbl_rev3, sizeof(mcs_tbl_rev3) / sizeof(mcs_tbl_rev3[0]), 18, 0, + 16} + , + {&tdi_tbl20_ant0_rev3, + sizeof(tdi_tbl20_ant0_rev3) / sizeof(tdi_tbl20_ant0_rev3[0]), 19, 128, + 32} + , + {&tdi_tbl20_ant1_rev3, + sizeof(tdi_tbl20_ant1_rev3) / sizeof(tdi_tbl20_ant1_rev3[0]), 19, 256, + 32} + , + {&tdi_tbl40_ant0_rev3, + sizeof(tdi_tbl40_ant0_rev3) / sizeof(tdi_tbl40_ant0_rev3[0]), 19, 640, + 32} + , + {&tdi_tbl40_ant1_rev3, + sizeof(tdi_tbl40_ant1_rev3) / sizeof(tdi_tbl40_ant1_rev3[0]), 19, 768, + 32} + , + {&pltlut_tbl_rev3, sizeof(pltlut_tbl_rev3) / sizeof(pltlut_tbl_rev3[0]), + 20, 0, 32} + , + {&chanest_tbl_rev3, + sizeof(chanest_tbl_rev3) / sizeof(chanest_tbl_rev3[0]), 22, 0, 32} + , + {&frame_lut_rev3, sizeof(frame_lut_rev3) / sizeof(frame_lut_rev3[0]), + 24, 0, 8} + , + {&est_pwr_lut_core0_rev3, + sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26, + 0, 8} + , + {&est_pwr_lut_core1_rev3, + sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27, + 0, 8} + , + {&adj_pwr_lut_core0_rev3, + sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26, + 64, 8} + , + {&adj_pwr_lut_core1_rev3, + sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27, + 64, 8} + , + {&gainctrl_lut_core0_rev3, + sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]), + 26, 192, 32} + , + {&gainctrl_lut_core1_rev3, + sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]), + 27, 192, 32} + , + {&iq_lut_core0_rev3, + sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32} + , + {&iq_lut_core1_rev3, + sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32} + , + {&loft_lut_core0_rev3, + sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448, + 16} + , + {&loft_lut_core1_rev3, + sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448, + 16} +}; + +const u32 mimophytbl_info_sz_rev3 = + sizeof(mimophytbl_info_rev3) / sizeof(mimophytbl_info_rev3[0]); +const u32 mimophytbl_info_sz_rev3_volatile = + sizeof(mimophytbl_info_rev3_volatile) / + sizeof(mimophytbl_info_rev3_volatile[0]); +const u32 mimophytbl_info_sz_rev3_volatile1 = + sizeof(mimophytbl_info_rev3_volatile1) / + sizeof(mimophytbl_info_rev3_volatile1[0]); +const u32 mimophytbl_info_sz_rev3_volatile2 = + sizeof(mimophytbl_info_rev3_volatile2) / + sizeof(mimophytbl_info_rev3_volatile2[0]); +const u32 mimophytbl_info_sz_rev3_volatile3 = + sizeof(mimophytbl_info_rev3_volatile3) / + sizeof(mimophytbl_info_rev3_volatile3[0]); + +static const u32 tmap_tbl_rev7[] = { + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0xf1111110, + 0x11111111, + 0x11f11111, + 0x00000111, + 0x11000000, + 0x1111f111, + 0x11111111, + 0x111111f1, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x000aa888, + 0x88880000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa2222220, + 0x22222222, + 0x22c22222, + 0x00000222, + 0x22000000, + 0x2222a222, + 0x22222222, + 0x222222a2, + 0xf1111110, + 0x11111111, + 0x11f11111, + 0x00011111, + 0x11110000, + 0x1111f111, + 0x11111111, + 0x111111f1, + 0xa8aa88a0, + 0xa88888a8, + 0xa8a8a88a, + 0x00088aaa, + 0xaaaa0000, + 0xa8a8aa88, + 0xa88aaaaa, + 0xaaaa8a8a, + 0xaaa8aaa0, + 0x8aaa8aaa, + 0xaa8a8a8a, + 0x000aaa88, + 0x8aaa0000, + 0xaaa8a888, + 0x8aa88a8a, + 0x8a88a888, + 0x08080a00, + 0x0a08080a, + 0x080a0a08, + 0x00080808, + 0x080a0000, + 0x080a0808, + 0x080a0808, + 0x0a0a0a08, + 0xa0a0a0a0, + 0x80a0a080, + 0x8080a0a0, + 0x00008080, + 0x80a00000, + 0x80a080a0, + 0xa080a0a0, + 0x8080a0a0, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x99999000, + 0x9b9b99bb, + 0x9bb99999, + 0x9999b9b9, + 0x9b99bb90, + 0x9bbbbb9b, + 0x9b9b9bb9, + 0x00000999, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00aaa888, + 0x22000000, + 0x2222b222, + 0x22222222, + 0x222222b2, + 0xb2222220, + 0x22222222, + 0x22d22222, + 0x00000222, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x33000000, + 0x3333b333, + 0x33333333, + 0x333333b3, + 0xb3333330, + 0x33333333, + 0x33d33333, + 0x00000333, + 0x22000000, + 0x2222a222, + 0x22222222, + 0x222222a2, + 0xa2222220, + 0x22222222, + 0x22c22222, + 0x00000222, + 0x99b99b00, + 0x9b9b99bb, + 0x9bb99999, + 0x9999b9b9, + 0x9b99bb99, + 0x9bbbbb9b, + 0x9b9b9bb9, + 0x00000999, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa88, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x08aaa888, + 0x22222200, + 0x2222f222, + 0x22222222, + 0x222222f2, + 0x22222222, + 0x22222222, + 0x22f22222, + 0x00000222, + 0x11000000, + 0x1111f111, + 0x11111111, + 0x11111111, + 0xf1111111, + 0x11111111, + 0x11f11111, + 0x01111111, + 0xbb9bb900, + 0xb9b9bb99, + 0xb99bbbbb, + 0xbbbb9b9b, + 0xb9bb99bb, + 0xb99999b9, + 0xb9b9b99b, + 0x00000bbb, + 0xaa000000, + 0xa8a8aa88, + 0xa88aaaaa, + 0xaaaa8a8a, + 0xa8aa88aa, + 0xa88888a8, + 0xa8a8a88a, + 0x0a888aaa, + 0xaa000000, + 0xa8a8aa88, + 0xa88aaaaa, + 0xaaaa8a8a, + 0xa8aa88a0, + 0xa88888a8, + 0xa8a8a88a, + 0x00000aaa, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0xbbbbbb00, + 0x999bbbbb, + 0x9bb99b9b, + 0xb9b9b9bb, + 0xb9b99bbb, + 0xb9b9b9bb, + 0xb9bb9b99, + 0x00000999, + 0x8a000000, + 0xaa88a888, + 0xa88888aa, + 0xa88a8a88, + 0xa88aa88a, + 0x88a8aaaa, + 0xa8aa8aaa, + 0x0888a88a, + 0x0b0b0b00, + 0x090b0b0b, + 0x0b090b0b, + 0x0909090b, + 0x09090b0b, + 0x09090b0b, + 0x09090b09, + 0x00000909, + 0x0a000000, + 0x0a080808, + 0x080a080a, + 0x080a0a08, + 0x080a080a, + 0x0808080a, + 0x0a0a0a08, + 0x0808080a, + 0xb0b0b000, + 0x9090b0b0, + 0x90b09090, + 0xb0b0b090, + 0xb0b090b0, + 0x90b0b0b0, + 0xb0b09090, + 0x00000090, + 0x80000000, + 0xa080a080, + 0xa08080a0, + 0xa0808080, + 0xa080a080, + 0x80a0a0a0, + 0xa0a080a0, + 0x00a0a0a0, + 0x22000000, + 0x2222f222, + 0x22222222, + 0x222222f2, + 0xf2222220, + 0x22222222, + 0x22f22222, + 0x00000222, + 0x11000000, + 0x1111f111, + 0x11111111, + 0x111111f1, + 0xf1111110, + 0x11111111, + 0x11f11111, + 0x00000111, + 0x33000000, + 0x3333f333, + 0x33333333, + 0x333333f3, + 0xf3333330, + 0x33333333, + 0x33f33333, + 0x00000333, + 0x22000000, + 0x2222f222, + 0x22222222, + 0x222222f2, + 0xf2222220, + 0x22222222, + 0x22f22222, + 0x00000222, + 0x99000000, + 0x9b9b99bb, + 0x9bb99999, + 0x9999b9b9, + 0x9b99bb90, + 0x9bbbbb9b, + 0x9b9b9bb9, + 0x00000999, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88888000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00aaa888, + 0x88a88a00, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa88, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x000aa888, + 0x88880000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa88, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x08aaa888, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x11000000, + 0x1111a111, + 0x11111111, + 0x111111a1, + 0xa1111110, + 0x11111111, + 0x11c11111, + 0x00000111, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x88000000, + 0x8a8a88aa, + 0x8aa88888, + 0x8888a8a8, + 0x8a88aa80, + 0x8aaaaa8a, + 0x8a8a8aa8, + 0x00000888, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +const u32 noise_var_tbl_rev7[] = { + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, + 0x020c020c, + 0x0000014d, +}; + +static const u32 papd_comp_epsilon_tbl_core0_rev7[] = { + 0x00000000, + 0x00000000, + 0x00016023, + 0x00006028, + 0x00034036, + 0x0003402e, + 0x0007203c, + 0x0006e037, + 0x00070030, + 0x0009401f, + 0x0009a00f, + 0x000b600d, + 0x000c8007, + 0x000ce007, + 0x00101fff, + 0x00121ff9, + 0x0012e004, + 0x0014dffc, + 0x0016dff6, + 0x0018dfe9, + 0x001b3fe5, + 0x001c5fd0, + 0x001ddfc2, + 0x001f1fb6, + 0x00207fa4, + 0x00219f8f, + 0x0022ff7d, + 0x00247f6c, + 0x0024df5b, + 0x00267f4b, + 0x0027df3b, + 0x0029bf3b, + 0x002b5f2f, + 0x002d3f2e, + 0x002f5f2a, + 0x002fff15, + 0x00315f0b, + 0x0032defa, + 0x0033beeb, + 0x0034fed9, + 0x00353ec5, + 0x00361eb0, + 0x00363e9b, + 0x0036be87, + 0x0036be70, + 0x0038fe67, + 0x0044beb2, + 0x00513ef3, + 0x00595f11, + 0x00669f3d, + 0x0078dfdf, + 0x00a143aa, + 0x01642fff, + 0x0162afff, + 0x01620fff, + 0x0160cfff, + 0x015f0fff, + 0x015dafff, + 0x015bcfff, + 0x015bcfff, + 0x015b4fff, + 0x015acfff, + 0x01590fff, + 0x0156cfff, +}; + +static const u32 papd_cal_scalars_tbl_core0_rev7[] = { + 0x0b5e002d, + 0x0ae2002f, + 0x0a3b0032, + 0x09a70035, + 0x09220038, + 0x08ab003b, + 0x081f003f, + 0x07a20043, + 0x07340047, + 0x06d2004b, + 0x067a004f, + 0x06170054, + 0x05bf0059, + 0x0571005e, + 0x051e0064, + 0x04d3006a, + 0x04910070, + 0x044c0077, + 0x040f007e, + 0x03d90085, + 0x03a1008d, + 0x036f0095, + 0x033d009e, + 0x030b00a8, + 0x02e000b2, + 0x02b900bc, + 0x029200c7, + 0x026d00d3, + 0x024900e0, + 0x022900ed, + 0x020a00fb, + 0x01ec010a, + 0x01d20119, + 0x01b7012a, + 0x019e013c, + 0x0188014e, + 0x01720162, + 0x015d0177, + 0x0149018e, + 0x013701a5, + 0x012601be, + 0x011501d8, + 0x010601f4, + 0x00f70212, + 0x00e90231, + 0x00dc0253, + 0x00d00276, + 0x00c4029b, + 0x00b902c3, + 0x00af02ed, + 0x00a50319, + 0x009c0348, + 0x0093037a, + 0x008b03af, + 0x008303e6, + 0x007c0422, + 0x00750460, + 0x006e04a3, + 0x006804e9, + 0x00620533, + 0x005d0582, + 0x005805d6, + 0x0053062e, + 0x004e068c, +}; + +static const u32 papd_comp_epsilon_tbl_core1_rev7[] = { + 0x00000000, + 0x00000000, + 0x00016023, + 0x00006028, + 0x00034036, + 0x0003402e, + 0x0007203c, + 0x0006e037, + 0x00070030, + 0x0009401f, + 0x0009a00f, + 0x000b600d, + 0x000c8007, + 0x000ce007, + 0x00101fff, + 0x00121ff9, + 0x0012e004, + 0x0014dffc, + 0x0016dff6, + 0x0018dfe9, + 0x001b3fe5, + 0x001c5fd0, + 0x001ddfc2, + 0x001f1fb6, + 0x00207fa4, + 0x00219f8f, + 0x0022ff7d, + 0x00247f6c, + 0x0024df5b, + 0x00267f4b, + 0x0027df3b, + 0x0029bf3b, + 0x002b5f2f, + 0x002d3f2e, + 0x002f5f2a, + 0x002fff15, + 0x00315f0b, + 0x0032defa, + 0x0033beeb, + 0x0034fed9, + 0x00353ec5, + 0x00361eb0, + 0x00363e9b, + 0x0036be87, + 0x0036be70, + 0x0038fe67, + 0x0044beb2, + 0x00513ef3, + 0x00595f11, + 0x00669f3d, + 0x0078dfdf, + 0x00a143aa, + 0x01642fff, + 0x0162afff, + 0x01620fff, + 0x0160cfff, + 0x015f0fff, + 0x015dafff, + 0x015bcfff, + 0x015bcfff, + 0x015b4fff, + 0x015acfff, + 0x01590fff, + 0x0156cfff, +}; + +static const u32 papd_cal_scalars_tbl_core1_rev7[] = { + 0x0b5e002d, + 0x0ae2002f, + 0x0a3b0032, + 0x09a70035, + 0x09220038, + 0x08ab003b, + 0x081f003f, + 0x07a20043, + 0x07340047, + 0x06d2004b, + 0x067a004f, + 0x06170054, + 0x05bf0059, + 0x0571005e, + 0x051e0064, + 0x04d3006a, + 0x04910070, + 0x044c0077, + 0x040f007e, + 0x03d90085, + 0x03a1008d, + 0x036f0095, + 0x033d009e, + 0x030b00a8, + 0x02e000b2, + 0x02b900bc, + 0x029200c7, + 0x026d00d3, + 0x024900e0, + 0x022900ed, + 0x020a00fb, + 0x01ec010a, + 0x01d20119, + 0x01b7012a, + 0x019e013c, + 0x0188014e, + 0x01720162, + 0x015d0177, + 0x0149018e, + 0x013701a5, + 0x012601be, + 0x011501d8, + 0x010601f4, + 0x00f70212, + 0x00e90231, + 0x00dc0253, + 0x00d00276, + 0x00c4029b, + 0x00b902c3, + 0x00af02ed, + 0x00a50319, + 0x009c0348, + 0x0093037a, + 0x008b03af, + 0x008303e6, + 0x007c0422, + 0x00750460, + 0x006e04a3, + 0x006804e9, + 0x00620533, + 0x005d0582, + 0x005805d6, + 0x0053062e, + 0x004e068c, +}; + +const struct phytbl_info mimophytbl_info_rev7[] = { + {&frame_struct_rev3, + sizeof(frame_struct_rev3) / sizeof(frame_struct_rev3[0]), 10, 0, 32} + , + {&pilot_tbl_rev3, sizeof(pilot_tbl_rev3) / sizeof(pilot_tbl_rev3[0]), + 11, 0, 16} + , + {&tmap_tbl_rev7, sizeof(tmap_tbl_rev7) / sizeof(tmap_tbl_rev7[0]), 12, + 0, 32} + , + {&intlv_tbl_rev3, sizeof(intlv_tbl_rev3) / sizeof(intlv_tbl_rev3[0]), + 13, 0, 32} + , + {&tdtrn_tbl_rev3, sizeof(tdtrn_tbl_rev3) / sizeof(tdtrn_tbl_rev3[0]), + 14, 0, 32} + , + {&noise_var_tbl_rev7, + sizeof(noise_var_tbl_rev7) / sizeof(noise_var_tbl_rev7[0]), 16, 0, 32} + , + {&mcs_tbl_rev3, sizeof(mcs_tbl_rev3) / sizeof(mcs_tbl_rev3[0]), 18, 0, + 16} + , + {&tdi_tbl20_ant0_rev3, + sizeof(tdi_tbl20_ant0_rev3) / sizeof(tdi_tbl20_ant0_rev3[0]), 19, 128, + 32} + , + {&tdi_tbl20_ant1_rev3, + sizeof(tdi_tbl20_ant1_rev3) / sizeof(tdi_tbl20_ant1_rev3[0]), 19, 256, + 32} + , + {&tdi_tbl40_ant0_rev3, + sizeof(tdi_tbl40_ant0_rev3) / sizeof(tdi_tbl40_ant0_rev3[0]), 19, 640, + 32} + , + {&tdi_tbl40_ant1_rev3, + sizeof(tdi_tbl40_ant1_rev3) / sizeof(tdi_tbl40_ant1_rev3[0]), 19, 768, + 32} + , + {&pltlut_tbl_rev3, sizeof(pltlut_tbl_rev3) / sizeof(pltlut_tbl_rev3[0]), + 20, 0, 32} + , + {&chanest_tbl_rev3, + sizeof(chanest_tbl_rev3) / sizeof(chanest_tbl_rev3[0]), 22, 0, 32} + , + {&frame_lut_rev3, sizeof(frame_lut_rev3) / sizeof(frame_lut_rev3[0]), + 24, 0, 8} + , + {&est_pwr_lut_core0_rev3, + sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26, + 0, 8} + , + {&est_pwr_lut_core1_rev3, + sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27, + 0, 8} + , + {&adj_pwr_lut_core0_rev3, + sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26, + 64, 8} + , + {&adj_pwr_lut_core1_rev3, + sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27, + 64, 8} + , + {&gainctrl_lut_core0_rev3, + sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]), + 26, 192, 32} + , + {&gainctrl_lut_core1_rev3, + sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]), + 27, 192, 32} + , + {&iq_lut_core0_rev3, + sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32} + , + {&iq_lut_core1_rev3, + sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32} + , + {&loft_lut_core0_rev3, + sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448, + 16} + , + {&loft_lut_core1_rev3, + sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448, + 16} + , + {&papd_comp_rfpwr_tbl_core0_rev3, + sizeof(papd_comp_rfpwr_tbl_core0_rev3) / + sizeof(papd_comp_rfpwr_tbl_core0_rev3[0]), 26, 576, 16} + , + {&papd_comp_rfpwr_tbl_core1_rev3, + sizeof(papd_comp_rfpwr_tbl_core1_rev3) / + sizeof(papd_comp_rfpwr_tbl_core1_rev3[0]), 27, 576, 16} + , + {&papd_comp_epsilon_tbl_core0_rev7, + sizeof(papd_comp_epsilon_tbl_core0_rev7) / + sizeof(papd_comp_epsilon_tbl_core0_rev7[0]), 31, 0, 32} + , + {&papd_cal_scalars_tbl_core0_rev7, + sizeof(papd_cal_scalars_tbl_core0_rev7) / + sizeof(papd_cal_scalars_tbl_core0_rev7[0]), 32, 0, 32} + , + {&papd_comp_epsilon_tbl_core1_rev7, + sizeof(papd_comp_epsilon_tbl_core1_rev7) / + sizeof(papd_comp_epsilon_tbl_core1_rev7[0]), 33, 0, 32} + , + {&papd_cal_scalars_tbl_core1_rev7, + sizeof(papd_cal_scalars_tbl_core1_rev7) / + sizeof(papd_cal_scalars_tbl_core1_rev7[0]), 34, 0, 32} + , +}; + +const u32 mimophytbl_info_sz_rev7 = + sizeof(mimophytbl_info_rev7) / sizeof(mimophytbl_info_rev7[0]); + +const struct phytbl_info mimophytbl_info_rev16[] = { + {&noise_var_tbl_rev7, + sizeof(noise_var_tbl_rev7) / sizeof(noise_var_tbl_rev7[0]), 16, 0, 32} + , + {&est_pwr_lut_core0_rev3, + sizeof(est_pwr_lut_core0_rev3) / sizeof(est_pwr_lut_core0_rev3[0]), 26, + 0, 8} + , + {&est_pwr_lut_core1_rev3, + sizeof(est_pwr_lut_core1_rev3) / sizeof(est_pwr_lut_core1_rev3[0]), 27, + 0, 8} + , + {&adj_pwr_lut_core0_rev3, + sizeof(adj_pwr_lut_core0_rev3) / sizeof(adj_pwr_lut_core0_rev3[0]), 26, + 64, 8} + , + {&adj_pwr_lut_core1_rev3, + sizeof(adj_pwr_lut_core1_rev3) / sizeof(adj_pwr_lut_core1_rev3[0]), 27, + 64, 8} + , + {&gainctrl_lut_core0_rev3, + sizeof(gainctrl_lut_core0_rev3) / sizeof(gainctrl_lut_core0_rev3[0]), + 26, 192, 32} + , + {&gainctrl_lut_core1_rev3, + sizeof(gainctrl_lut_core1_rev3) / sizeof(gainctrl_lut_core1_rev3[0]), + 27, 192, 32} + , + {&iq_lut_core0_rev3, + sizeof(iq_lut_core0_rev3) / sizeof(iq_lut_core0_rev3[0]), 26, 320, 32} + , + {&iq_lut_core1_rev3, + sizeof(iq_lut_core1_rev3) / sizeof(iq_lut_core1_rev3[0]), 27, 320, 32} + , + {&loft_lut_core0_rev3, + sizeof(loft_lut_core0_rev3) / sizeof(loft_lut_core0_rev3[0]), 26, 448, + 16} + , + {&loft_lut_core1_rev3, + sizeof(loft_lut_core1_rev3) / sizeof(loft_lut_core1_rev3[0]), 27, 448, + 16} + , +}; + +const u32 mimophytbl_info_sz_rev16 = + sizeof(mimophytbl_info_rev16) / sizeof(mimophytbl_info_rev16[0]); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.h new file mode 100644 index 000000000000..dc8a84e85117 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010 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. + */ + +#define ANT_SWCTRL_TBL_REV3_IDX (0) + +#include +#include "phy_int.h" + +extern const struct phytbl_info mimophytbl_info_rev0[], + mimophytbl_info_rev0_volatile[]; + +extern const u32 mimophytbl_info_sz_rev0, + mimophytbl_info_sz_rev0_volatile; + +extern const struct phytbl_info mimophytbl_info_rev3[], + mimophytbl_info_rev3_volatile[], + mimophytbl_info_rev3_volatile1[], + mimophytbl_info_rev3_volatile2[], + mimophytbl_info_rev3_volatile3[]; + +extern const u32 mimophytbl_info_sz_rev3, + mimophytbl_info_sz_rev3_volatile, + mimophytbl_info_sz_rev3_volatile1, + mimophytbl_info_sz_rev3_volatile2, + mimophytbl_info_sz_rev3_volatile3; + +extern const u32 noise_var_tbl_rev3[]; + +extern const struct phytbl_info mimophytbl_info_rev7[]; + +extern const u32 mimophytbl_info_sz_rev7; + +extern const u32 noise_var_tbl_rev7[]; + +extern const struct phytbl_info mimophytbl_info_rev16[]; + +extern const u32 mimophytbl_info_sz_rev16; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c new file mode 100644 index 000000000000..a0de5db0cd64 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2010 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. + */ + +/* + * This is "two-way" interface, acting as the SHIM layer between driver + * and PHY layer. The driver can optionally call this translation layer + * to do some preprocessing, then reach PHY. On the PHY->driver direction, + * all calls go through this layer since PHY doesn't have access to the + * driver's brcms_hardware pointer. + */ +#include +#include + +#include "main.h" +#include "mac80211_if.h" +#include "phy_shim.h" + +/* PHY SHIM module specific state */ +struct phy_shim_info { + struct brcms_hardware *wlc_hw; /* pointer to main wlc_hw structure */ + struct brcms_c_info *wlc; /* pointer to main wlc structure */ + struct brcms_info *wl; /* pointer to os-specific private state */ +}; + +struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw, + struct brcms_info *wl, + struct brcms_c_info *wlc) { + struct phy_shim_info *physhim = NULL; + + physhim = kzalloc(sizeof(struct phy_shim_info), GFP_ATOMIC); + if (!physhim) + return NULL; + + physhim->wlc_hw = wlc_hw; + physhim->wlc = wlc; + physhim->wl = wl; + + return physhim; +} + +void wlc_phy_shim_detach(struct phy_shim_info *physhim) +{ + kfree(physhim); +} + +struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, + void (*fn)(struct brcms_phy *pi), + void *arg, const char *name) +{ + return (struct wlapi_timer *) + brcms_init_timer(physhim->wl, (void (*)(void *))fn, + arg, name); +} + +void wlapi_free_timer(struct wlapi_timer *t) +{ + brcms_free_timer((struct brcms_timer *)t); +} + +void +wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic) +{ + brcms_add_timer((struct brcms_timer *)t, ms, periodic); +} + +bool wlapi_del_timer(struct wlapi_timer *t) +{ + return brcms_del_timer((struct brcms_timer *)t); +} + +void wlapi_intrson(struct phy_shim_info *physhim) +{ + brcms_intrson(physhim->wl); +} + +u32 wlapi_intrsoff(struct phy_shim_info *physhim) +{ + return brcms_intrsoff(physhim->wl); +} + +void wlapi_intrsrestore(struct phy_shim_info *physhim, u32 macintmask) +{ + brcms_intrsrestore(physhim->wl, macintmask); +} + +void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, u16 v) +{ + brcms_b_write_shm(physhim->wlc_hw, offset, v); +} + +u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset) +{ + return brcms_b_read_shm(physhim->wlc_hw, offset); +} + +void +wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, u16 mask, + u16 val, int bands) +{ + brcms_b_mhf(physhim->wlc_hw, idx, mask, val, bands); +} + +void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags) +{ + brcms_b_corereset(physhim->wlc_hw, flags); +} + +void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim) +{ + brcms_c_suspend_mac_and_wait(physhim->wlc); +} + +void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode) +{ + brcms_b_switch_macfreq(physhim->wlc_hw, spurmode); +} + +void wlapi_enable_mac(struct phy_shim_info *physhim) +{ + brcms_c_enable_mac(physhim->wlc); +} + +void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, u32 val) +{ + brcms_b_mctrl(physhim->wlc_hw, mask, val); +} + +void wlapi_bmac_phy_reset(struct phy_shim_info *physhim) +{ + brcms_b_phy_reset(physhim->wlc_hw); +} + +void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw) +{ + brcms_b_bw_set(physhim->wlc_hw, bw); +} + +u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim) +{ + return brcms_b_get_txant(physhim->wlc_hw); +} + +void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk) +{ + brcms_b_phyclk_fgc(physhim->wlc_hw, clk); +} + +void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk) +{ + brcms_b_macphyclk_set(physhim->wlc_hw, clk); +} + +void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on) +{ + brcms_b_core_phypll_ctl(physhim->wlc_hw, on); +} + +void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim) +{ + brcms_b_core_phypll_reset(physhim->wlc_hw); +} + +void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info *physhim) +{ + brcms_c_ucode_wake_override_set(physhim->wlc_hw, + BRCMS_WAKE_OVERRIDE_PHYREG); +} + +void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info *physhim) +{ + brcms_c_ucode_wake_override_clear(physhim->wlc_hw, + BRCMS_WAKE_OVERRIDE_PHYREG); +} + +void +wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int offset, + int len, void *buf) +{ + brcms_b_write_template_ram(physhim->wlc_hw, offset, len, buf); +} + +u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, u8 rate) +{ + return brcms_b_rate_shm_offset(physhim->wlc_hw, rate); +} + +void wlapi_ucode_sample_init(struct phy_shim_info *physhim) +{ +} + +void +wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint offset, void *buf, + int len, u32 sel) +{ + brcms_b_copyfrom_objmem(physhim->wlc_hw, offset, buf, len, sel); +} + +void +wlapi_copyto_objmem(struct phy_shim_info *physhim, uint offset, const void *buf, + int l, u32 sel) +{ + brcms_b_copyto_objmem(physhim->wlc_hw, offset, buf, l, sel); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h new file mode 100644 index 000000000000..dd8774717ade --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2010 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. + */ + +/* + * phy_shim.h: stuff defined in phy_shim.c and included only by the phy + */ + +#ifndef _BRCM_PHY_SHIM_H_ +#define _BRCM_PHY_SHIM_H_ + +#include "types.h" + +#define RADAR_TYPE_NONE 0 /* Radar type None */ +#define RADAR_TYPE_ETSI_1 1 /* ETSI 1 Radar type */ +#define RADAR_TYPE_ETSI_2 2 /* ETSI 2 Radar type */ +#define RADAR_TYPE_ETSI_3 3 /* ETSI 3 Radar type */ +#define RADAR_TYPE_ITU_E 4 /* ITU E Radar type */ +#define RADAR_TYPE_ITU_K 5 /* ITU K Radar type */ +#define RADAR_TYPE_UNCLASSIFIED 6 /* Unclassified Radar type */ +#define RADAR_TYPE_BIN5 7 /* long pulse radar type */ +#define RADAR_TYPE_STG2 8 /* staggered-2 radar */ +#define RADAR_TYPE_STG3 9 /* staggered-3 radar */ +#define RADAR_TYPE_FRA 10 /* French radar */ + +/* French radar pulse widths */ +#define FRA_T1_20MHZ 52770 +#define FRA_T2_20MHZ 61538 +#define FRA_T3_20MHZ 66002 +#define FRA_T1_40MHZ 105541 +#define FRA_T2_40MHZ 123077 +#define FRA_T3_40MHZ 132004 +#define FRA_ERR_20MHZ 60 +#define FRA_ERR_40MHZ 120 + +#define ANTSEL_NA 0 /* No boardlevel selection available */ +#define ANTSEL_2x4 1 /* 2x4 boardlevel selection available */ +#define ANTSEL_2x3 2 /* 2x3 CB2 boardlevel selection available */ + +/* Rx Antenna diversity control values */ +#define ANT_RX_DIV_FORCE_0 0 /* Use antenna 0 */ +#define ANT_RX_DIV_FORCE_1 1 /* Use antenna 1 */ +#define ANT_RX_DIV_START_1 2 /* Choose starting with 1 */ +#define ANT_RX_DIV_START_0 3 /* Choose starting with 0 */ +#define ANT_RX_DIV_ENABLE 3 /* APHY bbConfig Enable RX Diversity */ +#define ANT_RX_DIV_DEF ANT_RX_DIV_START_0 /* default antdiv setting */ + +#define WL_ANT_RX_MAX 2 /* max 2 receive antennas */ +#define WL_ANT_HT_RX_MAX 3 /* max 3 receive antennas/cores */ +#define WL_ANT_IDX_1 0 /* antenna index 1 */ +#define WL_ANT_IDX_2 1 /* antenna index 2 */ + +/* values for n_preamble_type */ +#define BRCMS_N_PREAMBLE_MIXEDMODE 0 +#define BRCMS_N_PREAMBLE_GF 1 +#define BRCMS_N_PREAMBLE_GF_BRCM 2 + +#define WL_TX_POWER_RATES_LEGACY 45 +#define WL_TX_POWER_MCS20_FIRST 12 +#define WL_TX_POWER_MCS20_NUM 16 +#define WL_TX_POWER_MCS40_FIRST 28 +#define WL_TX_POWER_MCS40_NUM 17 + + +#define WL_TX_POWER_RATES 101 +#define WL_TX_POWER_CCK_FIRST 0 +#define WL_TX_POWER_CCK_NUM 4 +/* Index for first 20MHz OFDM SISO rate */ +#define WL_TX_POWER_OFDM_FIRST 4 +/* Index for first 20MHz OFDM CDD rate */ +#define WL_TX_POWER_OFDM20_CDD_FIRST 12 +/* Index for first 40MHz OFDM SISO rate */ +#define WL_TX_POWER_OFDM40_SISO_FIRST 52 +/* Index for first 40MHz OFDM CDD rate */ +#define WL_TX_POWER_OFDM40_CDD_FIRST 60 +#define WL_TX_POWER_OFDM_NUM 8 +/* Index for first 20MHz MCS SISO rate */ +#define WL_TX_POWER_MCS20_SISO_FIRST 20 +/* Index for first 20MHz MCS CDD rate */ +#define WL_TX_POWER_MCS20_CDD_FIRST 28 +/* Index for first 20MHz MCS STBC rate */ +#define WL_TX_POWER_MCS20_STBC_FIRST 36 +/* Index for first 20MHz MCS SDM rate */ +#define WL_TX_POWER_MCS20_SDM_FIRST 44 +/* Index for first 40MHz MCS SISO rate */ +#define WL_TX_POWER_MCS40_SISO_FIRST 68 +/* Index for first 40MHz MCS CDD rate */ +#define WL_TX_POWER_MCS40_CDD_FIRST 76 +/* Index for first 40MHz MCS STBC rate */ +#define WL_TX_POWER_MCS40_STBC_FIRST 84 +/* Index for first 40MHz MCS SDM rate */ +#define WL_TX_POWER_MCS40_SDM_FIRST 92 +#define WL_TX_POWER_MCS_1_STREAM_NUM 8 +#define WL_TX_POWER_MCS_2_STREAM_NUM 8 +/* Index for 40MHz rate MCS 32 */ +#define WL_TX_POWER_MCS_32 100 +#define WL_TX_POWER_MCS_32_NUM 1 + +/* sslpnphy specifics */ +/* Index for first 20MHz MCS SISO rate */ +#define WL_TX_POWER_MCS20_SISO_FIRST_SSN 12 + +/* struct tx_power::flags bits */ +#define WL_TX_POWER_F_ENABLED 1 +#define WL_TX_POWER_F_HW 2 +#define WL_TX_POWER_F_MIMO 4 +#define WL_TX_POWER_F_SISO 8 + +/* values to force tx/rx chain */ +#define BRCMS_N_TXRX_CHAIN0 0 +#define BRCMS_N_TXRX_CHAIN1 1 + +struct brcms_phy; + +struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw, + struct brcms_info *wl, + struct brcms_c_info *wlc); +void wlc_phy_shim_detach(struct phy_shim_info *physhim); + +/* PHY to WL utility functions */ +struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, + void (*fn)(struct brcms_phy *pi), + void *arg, const char *name); +void wlapi_free_timer(struct wlapi_timer *t); +void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic); +bool wlapi_del_timer(struct wlapi_timer *t); +void wlapi_intrson(struct phy_shim_info *physhim); +u32 wlapi_intrsoff(struct phy_shim_info *physhim); +void wlapi_intrsrestore(struct phy_shim_info *physhim, u32 macintmask); + +void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, u16 v); +u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset); +void wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, u16 mask, u16 val, + int bands); +void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags); +void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim); +void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode); +void wlapi_enable_mac(struct phy_shim_info *physhim); +void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, u32 val); +void wlapi_bmac_phy_reset(struct phy_shim_info *physhim); +void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw); +void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk); +void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk); +void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on); +void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim); +void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info *physhim); +void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info *physhim); +void wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int o, + int len, void *buf); +u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, u8 rate); +void wlapi_ucode_sample_init(struct phy_shim_info *physhim); +void wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint, void *buf, + int, u32 sel); +void wlapi_copyto_objmem(struct phy_shim_info *physhim, uint, const void *buf, + int, u32); + +void wlapi_high_update_phy_mode(struct phy_shim_info *physhim, u32 phy_mode); +u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim); + +#endif /* _BRCM_PHY_SHIM_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c new file mode 100644 index 000000000000..71b80381f3ad --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2011 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 +#include +#include "pub.h" +#include "aiutils.h" +#include "pmu.h" +#include "soc.h" + +/* + * external LPO crystal frequency + */ +#define EXT_ILP_HZ 32768 + +/* + * Duration for ILP clock frequency measurment in milliseconds + * + * remark: 1000 must be an integer multiple of this duration + */ +#define ILP_CALC_DUR 10 + +/* Fields in pmucontrol */ +#define PCTL_ILP_DIV_MASK 0xffff0000 +#define PCTL_ILP_DIV_SHIFT 16 +#define PCTL_PLL_PLLCTL_UPD 0x00000400 /* rev 2 */ +#define PCTL_NOILP_ON_WAIT 0x00000200 /* rev 1 */ +#define PCTL_HT_REQ_EN 0x00000100 +#define PCTL_ALP_REQ_EN 0x00000080 +#define PCTL_XTALFREQ_MASK 0x0000007c +#define PCTL_XTALFREQ_SHIFT 2 +#define PCTL_ILP_DIV_EN 0x00000002 +#define PCTL_LPO_SEL 0x00000001 + +/* ILP clock */ +#define ILP_CLOCK 32000 + +/* ALP clock on pre-PMU chips */ +#define ALP_CLOCK 20000000 + +/* pmustatus */ +#define PST_EXTLPOAVAIL 0x0100 +#define PST_WDRESET 0x0080 +#define PST_INTPEND 0x0040 +#define PST_SBCLKST 0x0030 +#define PST_SBCLKST_ILP 0x0010 +#define PST_SBCLKST_ALP 0x0020 +#define PST_SBCLKST_HT 0x0030 +#define PST_ALPAVAIL 0x0008 +#define PST_HTAVAIL 0x0004 +#define PST_RESINIT 0x0003 + +/* PMU resource bit position */ +#define PMURES_BIT(bit) (1 << (bit)) + +/* PMU corerev and chip specific PLL controls. + * PMU_PLL_XX where is PMU corerev and is an arbitrary + * number to differentiate different PLLs controlled by the same PMU rev. + */ + +/* pmu XtalFreqRatio */ +#define PMU_XTALFREQ_REG_ILPCTR_MASK 0x00001FFF +#define PMU_XTALFREQ_REG_MEASURE_MASK 0x80000000 +#define PMU_XTALFREQ_REG_MEASURE_SHIFT 31 + +/* 4313 resources */ +#define RES4313_BB_PU_RSRC 0 +#define RES4313_ILP_REQ_RSRC 1 +#define RES4313_XTAL_PU_RSRC 2 +#define RES4313_ALP_AVAIL_RSRC 3 +#define RES4313_RADIO_PU_RSRC 4 +#define RES4313_BG_PU_RSRC 5 +#define RES4313_VREG1P4_PU_RSRC 6 +#define RES4313_AFE_PWRSW_RSRC 7 +#define RES4313_RX_PWRSW_RSRC 8 +#define RES4313_TX_PWRSW_RSRC 9 +#define RES4313_BB_PWRSW_RSRC 10 +#define RES4313_SYNTH_PWRSW_RSRC 11 +#define RES4313_MISC_PWRSW_RSRC 12 +#define RES4313_BB_PLL_PWRSW_RSRC 13 +#define RES4313_HT_AVAIL_RSRC 14 +#define RES4313_MACPHY_CLK_AVAIL_RSRC 15 + +u16 si_pmu_fast_pwrup_delay(struct si_pub *sih) +{ + uint delay = PMU_MAX_TRANSITION_DLY; + + switch (ai_get_chip_id(sih)) { + case BCMA_CHIP_ID_BCM43224: + case BCMA_CHIP_ID_BCM43225: + case BCMA_CHIP_ID_BCM4313: + delay = 3700; + break; + default: + break; + } + + return (u16) delay; +} + +u32 si_pmu_measure_alpclk(struct si_pub *sih) +{ + struct si_info *sii = container_of(sih, struct si_info, pub); + struct bcma_device *core; + u32 alp_khz; + + if (ai_get_pmurev(sih) < 10) + return 0; + + /* Remember original core before switch to chipc */ + core = sii->icbus->drv_cc.core; + + if (bcma_read32(core, CHIPCREGOFFS(pmustatus)) & PST_EXTLPOAVAIL) { + u32 ilp_ctr, alp_hz; + + /* + * Enable the reg to measure the freq, + * in case it was disabled before + */ + bcma_write32(core, CHIPCREGOFFS(pmu_xtalfreq), + 1U << PMU_XTALFREQ_REG_MEASURE_SHIFT); + + /* Delay for well over 4 ILP clocks */ + udelay(1000); + + /* Read the latched number of ALP ticks per 4 ILP ticks */ + ilp_ctr = bcma_read32(core, CHIPCREGOFFS(pmu_xtalfreq)) & + PMU_XTALFREQ_REG_ILPCTR_MASK; + + /* + * Turn off the PMU_XTALFREQ_REG_MEASURE_SHIFT + * bit to save power + */ + bcma_write32(core, CHIPCREGOFFS(pmu_xtalfreq), 0); + + /* Calculate ALP frequency */ + alp_hz = (ilp_ctr * EXT_ILP_HZ) / 4; + + /* + * Round to nearest 100KHz, and at + * the same time convert to KHz + */ + alp_khz = (alp_hz + 50000) / 100000 * 100; + } else + alp_khz = 0; + + return alp_khz; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.h new file mode 100644 index 000000000000..a014bbc4f935 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010 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 _BRCM_PMU_H_ +#define _BRCM_PMU_H_ + +#include "types.h" + +u16 si_pmu_fast_pwrup_delay(struct si_pub *sih); +u32 si_pmu_measure_alpclk(struct si_pub *sih); + +#endif /* _BRCM_PMU_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pub.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pub.h new file mode 100644 index 000000000000..4da38cb4f318 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pub.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2010 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 _BRCM_PUB_H_ +#define _BRCM_PUB_H_ + +#include +#include +#include "types.h" +#include "defs.h" + +#define BRCMS_NUMRATES 16 /* max # of rates in a rateset */ + +/* phy types */ +#define PHY_TYPE_A 0 /* Phy type A */ +#define PHY_TYPE_G 2 /* Phy type G */ +#define PHY_TYPE_N 4 /* Phy type N */ +#define PHY_TYPE_LP 5 /* Phy type Low Power A/B/G */ +#define PHY_TYPE_SSN 6 /* Phy type Single Stream N */ +#define PHY_TYPE_LCN 8 /* Phy type Single Stream N */ +#define PHY_TYPE_LCNXN 9 /* Phy type 2-stream N */ +#define PHY_TYPE_HT 7 /* Phy type 3-Stream N */ + +/* bw */ +#define BRCMS_10_MHZ 10 /* 10Mhz nphy channel bandwidth */ +#define BRCMS_20_MHZ 20 /* 20Mhz nphy channel bandwidth */ +#define BRCMS_40_MHZ 40 /* 40Mhz nphy channel bandwidth */ + +#define BRCMS_RSSI_MINVAL -200 /* Low value, e.g. for forcing roam */ +#define BRCMS_RSSI_NO_SIGNAL -91 /* NDIS RSSI link quality cutoffs */ +#define BRCMS_RSSI_VERY_LOW -80 /* Very low quality cutoffs */ +#define BRCMS_RSSI_LOW -70 /* Low quality cutoffs */ +#define BRCMS_RSSI_GOOD -68 /* Good quality cutoffs */ +#define BRCMS_RSSI_VERY_GOOD -58 /* Very good quality cutoffs */ +#define BRCMS_RSSI_EXCELLENT -57 /* Excellent quality cutoffs */ + +/* a large TX Power as an init value to factor out of min() calculations, + * keep low enough to fit in an s8, units are .25 dBm + */ +#define BRCMS_TXPWR_MAX (127) /* ~32 dBm = 1,500 mW */ + +/* rate related definitions */ +#define BRCMS_RATE_FLAG 0x80 /* Flag to indicate it is a basic rate */ +#define BRCMS_RATE_MASK 0x7f /* Rate value mask w/o basic rate flag */ + +/* legacy rx Antenna diversity for SISO rates */ +#define ANT_RX_DIV_FORCE_0 0 /* Use antenna 0 */ +#define ANT_RX_DIV_FORCE_1 1 /* Use antenna 1 */ +#define ANT_RX_DIV_START_1 2 /* Choose starting with 1 */ +#define ANT_RX_DIV_START_0 3 /* Choose starting with 0 */ +#define ANT_RX_DIV_ENABLE 3 /* APHY bbConfig Enable RX Diversity */ +/* default antdiv setting */ +#define ANT_RX_DIV_DEF ANT_RX_DIV_START_0 + +/* legacy rx Antenna diversity for SISO rates */ +/* Tx on antenna 0, "legacy term Main" */ +#define ANT_TX_FORCE_0 0 +/* Tx on antenna 1, "legacy term Aux" */ +#define ANT_TX_FORCE_1 1 +/* Tx on phy's last good Rx antenna */ +#define ANT_TX_LAST_RX 3 +/* driver's default tx antenna setting */ +#define ANT_TX_DEF 3 + +/* Tx Chain values */ +/* def bitmap of txchain */ +#define TXCHAIN_DEF 0x1 +/* default bitmap of tx chains for nphy */ +#define TXCHAIN_DEF_NPHY 0x3 +/* default bitmap of tx chains for nphy */ +#define TXCHAIN_DEF_HTPHY 0x7 +/* def bitmap of rxchain */ +#define RXCHAIN_DEF 0x1 +/* default bitmap of rx chains for nphy */ +#define RXCHAIN_DEF_NPHY 0x3 +/* default bitmap of rx chains for nphy */ +#define RXCHAIN_DEF_HTPHY 0x7 +/* no antenna switch */ +#define ANTSWITCH_NONE 0 +/* antenna switch on 4321CB2, 2of3 */ +#define ANTSWITCH_TYPE_1 1 +/* antenna switch on 4321MPCI, 2of3 */ +#define ANTSWITCH_TYPE_2 2 +/* antenna switch on 4322, 2of3 */ +#define ANTSWITCH_TYPE_3 3 + +#define RXBUFSZ PKTBUFSZ + +#define MAX_STREAMS_SUPPORTED 4 /* max number of streams supported */ + +struct brcm_rateset { + /* # rates in this set */ + u32 count; + /* rates in 500kbps units w/hi bit set if basic */ + u8 rates[WL_NUMRATES]; +}; + +struct brcms_c_rateset { + uint count; /* number of rates in rates[] */ + /* rates in 500kbps units w/hi bit set if basic */ + u8 rates[BRCMS_NUMRATES]; + u8 htphy_membership; /* HT PHY Membership */ + u8 mcs[MCSSET_LEN]; /* supported mcs index bit map */ +}; + +/* All the HT-specific default advertised capabilities (including AMPDU) + * should be grouped here at one place + */ +#define AMPDU_DEF_MPDU_DENSITY 6 /* default mpdu density (110 ==> 4us) */ + +/* wlc internal bss_info */ +struct brcms_bss_info { + u8 BSSID[ETH_ALEN]; /* network BSSID */ + u16 flags; /* flags for internal attributes */ + u8 SSID_len; /* the length of SSID */ + u8 SSID[32]; /* SSID string */ + s16 RSSI; /* receive signal strength (in dBm) */ + s16 SNR; /* receive signal SNR in dB */ + u16 beacon_period; /* units are Kusec */ + u16 chanspec; /* Channel num, bw, ctrl_sb and band */ + struct brcms_c_rateset rateset; /* supported rates */ +}; + +#define MAC80211_PROMISC_BCNS (1 << 0) +#define MAC80211_SCAN (1 << 1) + +/* + * Public portion of common driver state structure. + * The wlc handle points at this. + */ +struct brcms_pub { + struct brcms_c_info *wlc; + struct ieee80211_hw *ieee_hw; + struct scb_ampdu *global_ampdu; + uint mac80211_state; + uint unit; /* device instance number */ + uint corerev; /* core revision */ + struct si_pub *sih; /* SI handle (cookie for siutils calls) */ + bool up; /* interface up and running */ + bool hw_off; /* HW is off */ + bool hw_up; /* one time hw up/down */ + bool _piomode; /* true if pio mode */ + uint _nbands; /* # bands supported */ + uint now; /* # elapsed seconds */ + + bool delayed_down; /* down delayed */ + bool associated; /* true:part of [I]BSS, false: not */ + /* (union of stas_associated, aps_associated) */ + bool _ampdu; /* ampdu enabled or not */ + u8 _n_enab; /* bitmap of 11N + HT support */ + + u8 cur_etheraddr[ETH_ALEN]; /* our local ethernet address */ + + u32 radio_disabled; /* bit vector for radio disabled reasons */ + + u16 boardrev; /* version # of particular board */ + u8 sromrev; /* version # of the srom */ + char srom_ccode[BRCM_CNTRY_BUF_SZ]; /* Country Code in SROM */ + u32 boardflags; /* Board specific flags from srom */ + u32 boardflags2; /* More board flags if sromrev >= 4 */ + bool phy_11ncapable; /* the PHY/HW is capable of 802.11N */ + + struct wl_cnt *_cnt; /* low-level counters in driver */ + struct dentry *dbgfs_dir; +}; + +enum wlc_par_id { + IOV_MPC = 1, + IOV_RTSTHRESH, + IOV_QTXPOWER, + IOV_BCN_LI_BCN /* Beacon listen interval in # of beacons */ +}; + +/*********************************************** + * Feature-related macros to optimize out code * + * ********************************************* + */ + +#define ENAB_1x1 0x01 +#define ENAB_2x2 0x02 +#define ENAB_3x3 0x04 +#define ENAB_4x4 0x08 +#define SUPPORT_11N (ENAB_1x1|ENAB_2x2) +#define SUPPORT_HT (ENAB_1x1|ENAB_2x2|ENAB_3x3) + +/* WL11N Support */ +#define AMPDU_AGG_HOST 1 + +/* network protection config */ +#define BRCMS_PROT_G_SPEC 1 /* SPEC g protection */ +#define BRCMS_PROT_G_OVR 2 /* SPEC g prot override */ +#define BRCMS_PROT_G_USER 3 /* gmode specified by user */ +#define BRCMS_PROT_OVERLAP 4 /* overlap */ +#define BRCMS_PROT_N_USER 10 /* nmode specified by user */ +#define BRCMS_PROT_N_CFG 11 /* n protection */ +#define BRCMS_PROT_N_CFG_OVR 12 /* n protection override */ +#define BRCMS_PROT_N_NONGF 13 /* non-GF protection */ +#define BRCMS_PROT_N_NONGF_OVR 14 /* non-GF protection override */ +#define BRCMS_PROT_N_PAM_OVR 15 /* n preamble override */ +#define BRCMS_PROT_N_OBSS 16 /* non-HT OBSS present */ + +/* + * 54g modes (basic bits may still be overridden) + * + * GMODE_LEGACY_B + * Rateset: 1b, 2b, 5.5, 11 + * Preamble: Long + * Shortslot: Off + * GMODE_AUTO + * Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54 + * Extended Rateset: 6, 9, 12, 48 + * Preamble: Long + * Shortslot: Auto + * GMODE_ONLY + * Rateset: 1b, 2b, 5.5b, 11b, 18, 24b, 36, 54 + * Extended Rateset: 6b, 9, 12b, 48 + * Preamble: Short required + * Shortslot: Auto + * GMODE_B_DEFERRED + * Rateset: 1b, 2b, 5.5b, 11b, 18, 24, 36, 54 + * Extended Rateset: 6, 9, 12, 48 + * Preamble: Long + * Shortslot: On + * GMODE_PERFORMANCE + * Rateset: 1b, 2b, 5.5b, 6b, 9, 11b, 12b, 18, 24b, 36, 48, 54 + * Preamble: Short required + * Shortslot: On and required + * GMODE_LRS + * Rateset: 1b, 2b, 5.5b, 11b + * Extended Rateset: 6, 9, 12, 18, 24, 36, 48, 54 + * Preamble: Long + * Shortslot: Auto + */ +#define GMODE_LEGACY_B 0 +#define GMODE_AUTO 1 +#define GMODE_ONLY 2 +#define GMODE_B_DEFERRED 3 +#define GMODE_PERFORMANCE 4 +#define GMODE_LRS 5 +#define GMODE_MAX 6 + +/* MCS values greater than this enable multiple streams */ +#define HIGHEST_SINGLE_STREAM_MCS 7 + +#define MAXBANDS 2 /* Maximum #of bands */ + +/* max number of antenna configurations */ +#define ANT_SELCFG_MAX 4 + +struct brcms_antselcfg { + u8 ant_config[ANT_SELCFG_MAX]; /* antenna configuration */ + u8 num_antcfg; /* number of available antenna configurations */ +}; + +/* common functions for every port */ +struct brcms_c_info *brcms_c_attach(struct brcms_info *wl, + struct bcma_device *core, uint unit, + bool piomode, uint *perr); +uint brcms_c_detach(struct brcms_c_info *wlc); +int brcms_c_up(struct brcms_c_info *wlc); +uint brcms_c_down(struct brcms_c_info *wlc); + +bool brcms_c_chipmatch(struct bcma_device *core); +void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx); +void brcms_c_reset(struct brcms_c_info *wlc); + +void brcms_c_intrson(struct brcms_c_info *wlc); +u32 brcms_c_intrsoff(struct brcms_c_info *wlc); +void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask); +bool brcms_c_intrsupd(struct brcms_c_info *wlc); +bool brcms_c_isr(struct brcms_c_info *wlc); +bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded); +bool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu, + struct ieee80211_hw *hw); +bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid); +void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val); +int brcms_c_get_header_len(void); +void brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset, + const u8 *addr); +void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, + const struct ieee80211_tx_queue_params *arg, + bool suspend); +struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc); +void brcms_c_ampdu_flush(struct brcms_c_info *wlc, struct ieee80211_sta *sta, + u16 tid); +void brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, + u8 ba_wsize, uint max_rx_ampdu_bytes); +int brcms_c_module_register(struct brcms_pub *pub, const char *name, + struct brcms_info *hdl, + int (*down_fn)(void *handle)); +int brcms_c_module_unregister(struct brcms_pub *pub, const char *name, + struct brcms_info *hdl); +void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc); +void brcms_c_enable_mac(struct brcms_c_info *wlc); +void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state); +void brcms_c_scan_start(struct brcms_c_info *wlc); +void brcms_c_scan_stop(struct brcms_c_info *wlc); +int brcms_c_get_curband(struct brcms_c_info *wlc); +int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel); +int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl); +void brcms_c_get_current_rateset(struct brcms_c_info *wlc, + struct brcm_rateset *currs); +int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs); +int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period); +u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx); +void brcms_c_set_shortslot_override(struct brcms_c_info *wlc, + s8 sslot_override); +void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval); +u64 brcms_c_tsf_get(struct brcms_c_info *wlc); +void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf); +int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr); +int brcms_c_get_tx_power(struct brcms_c_info *wlc); +bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc); +void brcms_c_mute(struct brcms_c_info *wlc, bool on); +bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc); +void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr); +void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, const u8 *bssid, + u8 *ssid, size_t ssid_len); +void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr); +void brcms_c_update_beacon(struct brcms_c_info *wlc); +void brcms_c_set_new_beacon(struct brcms_c_info *wlc, struct sk_buff *beacon, + u16 tim_offset, u16 dtim_period); +void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc, + struct sk_buff *probe_resp); +void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable); +void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, size_t ssid_len); + +#endif /* _BRCM_PUB_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.c new file mode 100644 index 000000000000..0a0c0ad4f96f --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.c @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2010 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 "d11.h" +#include "pub.h" +#include "rate.h" + +/* + * Rate info per rate: It tells whether a rate is ofdm or not and its phy_rate + * value + */ +const u8 rate_info[BRCM_MAXRATE + 1] = { + /* 0 1 2 3 4 5 6 7 8 9 */ +/* 0 */ 0x00, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 10 */ 0x00, 0x37, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x00, +/* 20 */ 0x00, 0x00, 0x6e, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, +/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, +/* 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 70 */ 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, +/* 100 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c +}; + +/* rates are in units of Kbps */ +const struct brcms_mcs_info mcs_table[MCS_TABLE_SIZE] = { + /* MCS 0: SS 1, MOD: BPSK, CR 1/2 */ + {6500, 13500, CEIL(6500 * 10, 9), CEIL(13500 * 10, 9), 0x00, + BRCM_RATE_6M}, + /* MCS 1: SS 1, MOD: QPSK, CR 1/2 */ + {13000, 27000, CEIL(13000 * 10, 9), CEIL(27000 * 10, 9), 0x08, + BRCM_RATE_12M}, + /* MCS 2: SS 1, MOD: QPSK, CR 3/4 */ + {19500, 40500, CEIL(19500 * 10, 9), CEIL(40500 * 10, 9), 0x0A, + BRCM_RATE_18M}, + /* MCS 3: SS 1, MOD: 16QAM, CR 1/2 */ + {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0x10, + BRCM_RATE_24M}, + /* MCS 4: SS 1, MOD: 16QAM, CR 3/4 */ + {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x12, + BRCM_RATE_36M}, + /* MCS 5: SS 1, MOD: 64QAM, CR 2/3 */ + {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0x19, + BRCM_RATE_48M}, + /* MCS 6: SS 1, MOD: 64QAM, CR 3/4 */ + {58500, 121500, CEIL(58500 * 10, 9), CEIL(121500 * 10, 9), 0x1A, + BRCM_RATE_54M}, + /* MCS 7: SS 1, MOD: 64QAM, CR 5/6 */ + {65000, 135000, CEIL(65000 * 10, 9), CEIL(135000 * 10, 9), 0x1C, + BRCM_RATE_54M}, + /* MCS 8: SS 2, MOD: BPSK, CR 1/2 */ + {13000, 27000, CEIL(13000 * 10, 9), CEIL(27000 * 10, 9), 0x40, + BRCM_RATE_6M}, + /* MCS 9: SS 2, MOD: QPSK, CR 1/2 */ + {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0x48, + BRCM_RATE_12M}, + /* MCS 10: SS 2, MOD: QPSK, CR 3/4 */ + {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x4A, + BRCM_RATE_18M}, + /* MCS 11: SS 2, MOD: 16QAM, CR 1/2 */ + {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0x50, + BRCM_RATE_24M}, + /* MCS 12: SS 2, MOD: 16QAM, CR 3/4 */ + {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0x52, + BRCM_RATE_36M}, + /* MCS 13: SS 2, MOD: 64QAM, CR 2/3 */ + {104000, 216000, CEIL(104000 * 10, 9), CEIL(216000 * 10, 9), 0x59, + BRCM_RATE_48M}, + /* MCS 14: SS 2, MOD: 64QAM, CR 3/4 */ + {117000, 243000, CEIL(117000 * 10, 9), CEIL(243000 * 10, 9), 0x5A, + BRCM_RATE_54M}, + /* MCS 15: SS 2, MOD: 64QAM, CR 5/6 */ + {130000, 270000, CEIL(130000 * 10, 9), CEIL(270000 * 10, 9), 0x5C, + BRCM_RATE_54M}, + /* MCS 16: SS 3, MOD: BPSK, CR 1/2 */ + {19500, 40500, CEIL(19500 * 10, 9), CEIL(40500 * 10, 9), 0x80, + BRCM_RATE_6M}, + /* MCS 17: SS 3, MOD: QPSK, CR 1/2 */ + {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x88, + BRCM_RATE_12M}, + /* MCS 18: SS 3, MOD: QPSK, CR 3/4 */ + {58500, 121500, CEIL(58500 * 10, 9), CEIL(121500 * 10, 9), 0x8A, + BRCM_RATE_18M}, + /* MCS 19: SS 3, MOD: 16QAM, CR 1/2 */ + {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0x90, + BRCM_RATE_24M}, + /* MCS 20: SS 3, MOD: 16QAM, CR 3/4 */ + {117000, 243000, CEIL(117000 * 10, 9), CEIL(243000 * 10, 9), 0x92, + BRCM_RATE_36M}, + /* MCS 21: SS 3, MOD: 64QAM, CR 2/3 */ + {156000, 324000, CEIL(156000 * 10, 9), CEIL(324000 * 10, 9), 0x99, + BRCM_RATE_48M}, + /* MCS 22: SS 3, MOD: 64QAM, CR 3/4 */ + {175500, 364500, CEIL(175500 * 10, 9), CEIL(364500 * 10, 9), 0x9A, + BRCM_RATE_54M}, + /* MCS 23: SS 3, MOD: 64QAM, CR 5/6 */ + {195000, 405000, CEIL(195000 * 10, 9), CEIL(405000 * 10, 9), 0x9B, + BRCM_RATE_54M}, + /* MCS 24: SS 4, MOD: BPSK, CR 1/2 */ + {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0xC0, + BRCM_RATE_6M}, + /* MCS 25: SS 4, MOD: QPSK, CR 1/2 */ + {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0xC8, + BRCM_RATE_12M}, + /* MCS 26: SS 4, MOD: QPSK, CR 3/4 */ + {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0xCA, + BRCM_RATE_18M}, + /* MCS 27: SS 4, MOD: 16QAM, CR 1/2 */ + {104000, 216000, CEIL(104000 * 10, 9), CEIL(216000 * 10, 9), 0xD0, + BRCM_RATE_24M}, + /* MCS 28: SS 4, MOD: 16QAM, CR 3/4 */ + {156000, 324000, CEIL(156000 * 10, 9), CEIL(324000 * 10, 9), 0xD2, + BRCM_RATE_36M}, + /* MCS 29: SS 4, MOD: 64QAM, CR 2/3 */ + {208000, 432000, CEIL(208000 * 10, 9), CEIL(432000 * 10, 9), 0xD9, + BRCM_RATE_48M}, + /* MCS 30: SS 4, MOD: 64QAM, CR 3/4 */ + {234000, 486000, CEIL(234000 * 10, 9), CEIL(486000 * 10, 9), 0xDA, + BRCM_RATE_54M}, + /* MCS 31: SS 4, MOD: 64QAM, CR 5/6 */ + {260000, 540000, CEIL(260000 * 10, 9), CEIL(540000 * 10, 9), 0xDB, + BRCM_RATE_54M}, + /* MCS 32: SS 1, MOD: BPSK, CR 1/2 */ + {0, 6000, 0, CEIL(6000 * 10, 9), 0x00, BRCM_RATE_6M}, +}; + +/* + * phycfg for legacy OFDM frames: code rate, modulation scheme, spatial streams + * Number of spatial streams: always 1 other fields: refer to table 78 of + * section 17.3.2.2 of the original .11a standard + */ +struct legacy_phycfg { + u32 rate_ofdm; /* ofdm mac rate */ + /* phy ctl byte 3, code rate, modulation type, # of streams */ + u8 tx_phy_ctl3; +}; + +/* Number of legacy_rate_cfg entries in the table */ +#define LEGACY_PHYCFG_TABLE_SIZE 12 + +/* + * In CCK mode LPPHY overloads OFDM Modulation bits with CCK Data Rate + * Eventually MIMOPHY would also be converted to this format + * 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps + */ +static const struct +legacy_phycfg legacy_phycfg_table[LEGACY_PHYCFG_TABLE_SIZE] = { + {BRCM_RATE_1M, 0x00}, /* CCK 1Mbps, data rate 0 */ + {BRCM_RATE_2M, 0x08}, /* CCK 2Mbps, data rate 1 */ + {BRCM_RATE_5M5, 0x10}, /* CCK 5.5Mbps, data rate 2 */ + {BRCM_RATE_11M, 0x18}, /* CCK 11Mbps, data rate 3 */ + /* OFDM 6Mbps, code rate 1/2, BPSK, 1 spatial stream */ + {BRCM_RATE_6M, 0x00}, + /* OFDM 9Mbps, code rate 3/4, BPSK, 1 spatial stream */ + {BRCM_RATE_9M, 0x02}, + /* OFDM 12Mbps, code rate 1/2, QPSK, 1 spatial stream */ + {BRCM_RATE_12M, 0x08}, + /* OFDM 18Mbps, code rate 3/4, QPSK, 1 spatial stream */ + {BRCM_RATE_18M, 0x0A}, + /* OFDM 24Mbps, code rate 1/2, 16-QAM, 1 spatial stream */ + {BRCM_RATE_24M, 0x10}, + /* OFDM 36Mbps, code rate 3/4, 16-QAM, 1 spatial stream */ + {BRCM_RATE_36M, 0x12}, + /* OFDM 48Mbps, code rate 2/3, 64-QAM, 1 spatial stream */ + {BRCM_RATE_48M, 0x19}, + /* OFDM 54Mbps, code rate 3/4, 64-QAM, 1 spatial stream */ + {BRCM_RATE_54M, 0x1A}, +}; + +/* Hardware rates (also encodes default basic rates) */ + +const struct brcms_c_rateset cck_ofdm_mimo_rates = { + 12, + /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48, */ + { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60, + /* 54 Mbps */ + 0x6c}, + 0x00, + { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +const struct brcms_c_rateset ofdm_mimo_rates = { + 8, + /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */ + { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c}, + 0x00, + { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +/* Default ratesets that include MCS32 for 40BW channels */ +static const struct brcms_c_rateset cck_ofdm_40bw_mimo_rates = { + 12, + /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48 */ + { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60, + /* 54 Mbps */ + 0x6c}, + 0x00, + { 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +static const struct brcms_c_rateset ofdm_40bw_mimo_rates = { + 8, + /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */ + { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c}, + 0x00, + { 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +const struct brcms_c_rateset cck_ofdm_rates = { + 12, + /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48,*/ + { 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60, + /*54 Mbps */ + 0x6c}, + 0x00, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +const struct brcms_c_rateset gphy_legacy_rates = { + 4, + /* 1b, 2b, 5.5b, 11b Mbps */ + { 0x82, 0x84, 0x8b, 0x96}, + 0x00, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +const struct brcms_c_rateset ofdm_rates = { + 8, + /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */ + { 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c}, + 0x00, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +const struct brcms_c_rateset cck_rates = { + 4, + /* 1b, 2b, 5.5, 11 Mbps */ + { 0x82, 0x84, 0x0b, 0x16}, + 0x00, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +/* check if rateset is valid. + * if check_brate is true, rateset without a basic rate is considered NOT valid. + */ +static bool brcms_c_rateset_valid(struct brcms_c_rateset *rs, bool check_brate) +{ + uint idx; + + if (!rs->count) + return false; + + if (!check_brate) + return true; + + /* error if no basic rates */ + for (idx = 0; idx < rs->count; idx++) { + if (rs->rates[idx] & BRCMS_RATE_FLAG) + return true; + } + return false; +} + +void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams) +{ + int i; + for (i = txstreams; i < MAX_STREAMS_SUPPORTED; i++) + rs->mcs[i] = 0; +} + +/* + * filter based on hardware rateset, and sort filtered rateset with basic + * bit(s) preserved, and check if resulting rateset is valid. +*/ +bool +brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs, + const struct brcms_c_rateset *hw_rs, + bool check_brate, u8 txstreams) +{ + u8 rateset[BRCM_MAXRATE + 1]; + u8 r; + uint count; + uint i; + + memset(rateset, 0, sizeof(rateset)); + count = rs->count; + + for (i = 0; i < count; i++) { + /* mask off "basic rate" bit, BRCMS_RATE_FLAG */ + r = (int)rs->rates[i] & BRCMS_RATE_MASK; + if ((r > BRCM_MAXRATE) || (rate_info[r] == 0)) + continue; + rateset[r] = rs->rates[i]; /* preserve basic bit! */ + } + + /* fill out the rates in order, looking at only supported rates */ + count = 0; + for (i = 0; i < hw_rs->count; i++) { + r = hw_rs->rates[i] & BRCMS_RATE_MASK; + if (rateset[r]) + rs->rates[count++] = rateset[r]; + } + + rs->count = count; + + /* only set the mcs rate bit if the equivalent hw mcs bit is set */ + for (i = 0; i < MCSSET_LEN; i++) + rs->mcs[i] = (rs->mcs[i] & hw_rs->mcs[i]); + + if (brcms_c_rateset_valid(rs, check_brate)) + return true; + else + return false; +} + +/* calculate the rate of a rx'd frame and return it as a ratespec */ +u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp) +{ + int phy_type; + u32 rspec = PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT; + + phy_type = + ((rxh->RxChan & RXS_CHAN_PHYTYPE_MASK) >> RXS_CHAN_PHYTYPE_SHIFT); + + if ((phy_type == PHY_TYPE_N) || (phy_type == PHY_TYPE_SSN) || + (phy_type == PHY_TYPE_LCN) || (phy_type == PHY_TYPE_HT)) { + switch (rxh->PhyRxStatus_0 & PRXS0_FT_MASK) { + case PRXS0_CCK: + rspec = + cck_phy2mac_rate( + ((struct cck_phy_hdr *) plcp)->signal); + break; + case PRXS0_OFDM: + rspec = + ofdm_phy2mac_rate( + ((struct ofdm_phy_hdr *) plcp)->rlpt[0]); + break; + case PRXS0_PREN: + rspec = (plcp[0] & MIMO_PLCP_MCS_MASK) | RSPEC_MIMORATE; + if (plcp[0] & MIMO_PLCP_40MHZ) { + /* indicate rspec is for 40 MHz mode */ + rspec &= ~RSPEC_BW_MASK; + rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); + } + break; + case PRXS0_STDN: + /* fallthru */ + default: + /* not supported, error condition */ + break; + } + if (plcp3_issgi(plcp[3])) + rspec |= RSPEC_SHORT_GI; + } else + if ((phy_type == PHY_TYPE_A) || (rxh->PhyRxStatus_0 & PRXS0_OFDM)) + rspec = ofdm_phy2mac_rate( + ((struct ofdm_phy_hdr *) plcp)->rlpt[0]); + else + rspec = cck_phy2mac_rate( + ((struct cck_phy_hdr *) plcp)->signal); + + return rspec; +} + +/* copy rateset src to dst as-is (no masking or sorting) */ +void brcms_c_rateset_copy(const struct brcms_c_rateset *src, + struct brcms_c_rateset *dst) +{ + memcpy(dst, src, sizeof(struct brcms_c_rateset)); +} + +/* + * Copy and selectively filter one rateset to another. + * 'basic_only' means only copy basic rates. + * 'rates' indicates cck (11b) and ofdm rates combinations. + * - 0: cck and ofdm + * - 1: cck only + * - 2: ofdm only + * 'xmask' is the copy mask (typically 0x7f or 0xff). + */ +void +brcms_c_rateset_filter(struct brcms_c_rateset *src, struct brcms_c_rateset *dst, + bool basic_only, u8 rates, uint xmask, bool mcsallow) +{ + uint i; + uint r; + uint count; + + count = 0; + for (i = 0; i < src->count; i++) { + r = src->rates[i]; + if (basic_only && !(r & BRCMS_RATE_FLAG)) + continue; + if (rates == BRCMS_RATES_CCK && + is_ofdm_rate((r & BRCMS_RATE_MASK))) + continue; + if (rates == BRCMS_RATES_OFDM && + is_cck_rate((r & BRCMS_RATE_MASK))) + continue; + dst->rates[count++] = r & xmask; + } + dst->count = count; + dst->htphy_membership = src->htphy_membership; + + if (mcsallow && rates != BRCMS_RATES_CCK) + memcpy(&dst->mcs[0], &src->mcs[0], MCSSET_LEN); + else + brcms_c_rateset_mcs_clear(dst); +} + +/* select rateset for a given phy_type and bandtype and filter it, sort it + * and fill rs_tgt with result + */ +void +brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt, + const struct brcms_c_rateset *rs_hw, + uint phy_type, int bandtype, bool cck_only, + uint rate_mask, bool mcsallow, u8 bw, u8 txstreams) +{ + const struct brcms_c_rateset *rs_dflt; + struct brcms_c_rateset rs_sel; + if ((PHYTYPE_IS(phy_type, PHY_TYPE_HT)) || + (PHYTYPE_IS(phy_type, PHY_TYPE_N)) || + (PHYTYPE_IS(phy_type, PHY_TYPE_LCN)) || + (PHYTYPE_IS(phy_type, PHY_TYPE_SSN))) { + if (bandtype == BRCM_BAND_5G) + rs_dflt = (bw == BRCMS_20_MHZ ? + &ofdm_mimo_rates : &ofdm_40bw_mimo_rates); + else + rs_dflt = (bw == BRCMS_20_MHZ ? + &cck_ofdm_mimo_rates : + &cck_ofdm_40bw_mimo_rates); + } else if (PHYTYPE_IS(phy_type, PHY_TYPE_LP)) { + rs_dflt = (bandtype == BRCM_BAND_5G) ? + &ofdm_rates : &cck_ofdm_rates; + } else if (PHYTYPE_IS(phy_type, PHY_TYPE_A)) { + rs_dflt = &ofdm_rates; + } else if (PHYTYPE_IS(phy_type, PHY_TYPE_G)) { + rs_dflt = &cck_ofdm_rates; + } else { + /* should not happen, error condition */ + rs_dflt = &cck_rates; /* force cck */ + } + + /* if hw rateset is not supplied, assign selected rateset to it */ + if (!rs_hw) + rs_hw = rs_dflt; + + brcms_c_rateset_copy(rs_dflt, &rs_sel); + brcms_c_rateset_mcs_upd(&rs_sel, txstreams); + brcms_c_rateset_filter(&rs_sel, rs_tgt, false, + cck_only ? BRCMS_RATES_CCK : BRCMS_RATES_CCK_OFDM, + rate_mask, mcsallow); + brcms_c_rate_hwrs_filter_sort_validate(rs_tgt, rs_hw, false, + mcsallow ? txstreams : 1); +} + +s16 brcms_c_rate_legacy_phyctl(uint rate) +{ + uint i; + for (i = 0; i < LEGACY_PHYCFG_TABLE_SIZE; i++) + if (rate == legacy_phycfg_table[i].rate_ofdm) + return legacy_phycfg_table[i].tx_phy_ctl3; + + return -1; +} + +void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset) +{ + uint i; + for (i = 0; i < MCSSET_LEN; i++) + rateset->mcs[i] = 0; +} + +void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, u8 txstreams) +{ + memcpy(&rateset->mcs[0], &cck_ofdm_mimo_rates.mcs[0], MCSSET_LEN); + brcms_c_rateset_mcs_upd(rateset, txstreams); +} + +/* Based on bandwidth passed, allow/disallow MCS 32 in the rateset */ +void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, u8 bw) +{ + if (bw == BRCMS_40_MHZ) + setbit(rateset->mcs, 32); + else + clrbit(rateset->mcs, 32); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.h new file mode 100644 index 000000000000..5bb88b78ed64 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2010 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 _BRCM_RATE_H_ +#define _BRCM_RATE_H_ + +#include "types.h" +#include "d11.h" +#include "phy_hal.h" + +extern const u8 rate_info[]; +extern const struct brcms_c_rateset cck_ofdm_mimo_rates; +extern const struct brcms_c_rateset ofdm_mimo_rates; +extern const struct brcms_c_rateset cck_ofdm_rates; +extern const struct brcms_c_rateset ofdm_rates; +extern const struct brcms_c_rateset cck_rates; +extern const struct brcms_c_rateset gphy_legacy_rates; +extern const struct brcms_c_rateset rate_limit_1_2; + +struct brcms_mcs_info { + /* phy rate in kbps [20Mhz] */ + u32 phy_rate_20; + /* phy rate in kbps [40Mhz] */ + u32 phy_rate_40; + /* phy rate in kbps [20Mhz] with SGI */ + u32 phy_rate_20_sgi; + /* phy rate in kbps [40Mhz] with SGI */ + u32 phy_rate_40_sgi; + /* phy ctl byte 3, code rate, modulation type, # of streams */ + u8 tx_phy_ctl3; + /* matching legacy ofdm rate in 500bkps */ + u8 leg_ofdm; +}; + +#define BRCMS_MAXMCS 32 /* max valid mcs index */ +#define MCS_TABLE_SIZE 33 /* Number of mcs entries in the table */ +extern const struct brcms_mcs_info mcs_table[]; + +#define MCS_TXS_MASK 0xc0 /* num tx streams - 1 bit mask */ +#define MCS_TXS_SHIFT 6 /* num tx streams - 1 bit shift */ + +/* returns num tx streams - 1 */ +static inline u8 mcs_2_txstreams(u8 mcs) +{ + return (mcs_table[mcs].tx_phy_ctl3 & MCS_TXS_MASK) >> MCS_TXS_SHIFT; +} + +static inline uint mcs_2_rate(u8 mcs, bool is40, bool sgi) +{ + if (sgi) { + if (is40) + return mcs_table[mcs].phy_rate_40_sgi; + return mcs_table[mcs].phy_rate_20_sgi; + } + if (is40) + return mcs_table[mcs].phy_rate_40; + + return mcs_table[mcs].phy_rate_20; +} + +/* Macro to use the rate_info table */ +#define BRCMS_RATE_MASK_FULL 0xff /* Rate value mask with basic rate flag */ + +/* + * rate spec : holds rate and mode specific information required to generate a + * tx frame. Legacy CCK and OFDM information is held in the same manner as was + * done in the past (in the lower byte) the upper 3 bytes primarily hold MIMO + * specific information + */ + +/* rate spec bit fields */ + +/* Either 500Kbps units or MIMO MCS idx */ +#define RSPEC_RATE_MASK 0x0000007F +/* mimo MCS is stored in RSPEC_RATE_MASK */ +#define RSPEC_MIMORATE 0x08000000 +/* mimo bw mask */ +#define RSPEC_BW_MASK 0x00000700 +/* mimo bw shift */ +#define RSPEC_BW_SHIFT 8 +/* mimo Space/Time/Frequency mode mask */ +#define RSPEC_STF_MASK 0x00003800 +/* mimo Space/Time/Frequency mode shift */ +#define RSPEC_STF_SHIFT 11 +/* mimo coding type mask */ +#define RSPEC_CT_MASK 0x0000C000 +/* mimo coding type shift */ +#define RSPEC_CT_SHIFT 14 +/* mimo num STC streams per PLCP defn. */ +#define RSPEC_STC_MASK 0x00300000 +/* mimo num STC streams per PLCP defn. */ +#define RSPEC_STC_SHIFT 20 +/* mimo bit indicates adv coding in use */ +#define RSPEC_LDPC_CODING 0x00400000 +/* mimo bit indicates short GI in use */ +#define RSPEC_SHORT_GI 0x00800000 +/* bit indicates override both rate & mode */ +#define RSPEC_OVERRIDE 0x80000000 +/* bit indicates override rate only */ +#define RSPEC_OVERRIDE_MCS_ONLY 0x40000000 + +static inline bool rspec_active(u32 rspec) +{ + return rspec & (RSPEC_RATE_MASK | RSPEC_MIMORATE); +} + +static inline u8 rspec_phytxbyte2(u32 rspec) +{ + return (rspec & 0xff00) >> 8; +} + +static inline u32 rspec_get_bw(u32 rspec) +{ + return (rspec & RSPEC_BW_MASK) >> RSPEC_BW_SHIFT; +} + +static inline bool rspec_issgi(u32 rspec) +{ + return (rspec & RSPEC_SHORT_GI) == RSPEC_SHORT_GI; +} + +static inline bool rspec_is40mhz(u32 rspec) +{ + u32 bw = rspec_get_bw(rspec); + + return bw == PHY_TXC1_BW_40MHZ || bw == PHY_TXC1_BW_40MHZ_DUP; +} + +static inline uint rspec2rate(u32 rspec) +{ + if (rspec & RSPEC_MIMORATE) + return mcs_2_rate(rspec & RSPEC_RATE_MASK, rspec_is40mhz(rspec), + rspec_issgi(rspec)); + return rspec & RSPEC_RATE_MASK; +} + +static inline u8 rspec_mimoplcp3(u32 rspec) +{ + return (rspec & 0xf00000) >> 16; +} + +static inline bool plcp3_issgi(u8 plcp) +{ + return (plcp & (RSPEC_SHORT_GI >> 16)) != 0; +} + +static inline uint rspec_stc(u32 rspec) +{ + return (rspec & RSPEC_STC_MASK) >> RSPEC_STC_SHIFT; +} + +static inline uint rspec_stf(u32 rspec) +{ + return (rspec & RSPEC_STF_MASK) >> RSPEC_STF_SHIFT; +} + +static inline bool is_mcs_rate(u32 ratespec) +{ + return (ratespec & RSPEC_MIMORATE) != 0; +} + +static inline bool is_ofdm_rate(u32 ratespec) +{ + return !is_mcs_rate(ratespec) && + (rate_info[ratespec & RSPEC_RATE_MASK] & BRCMS_RATE_FLAG); +} + +static inline bool is_cck_rate(u32 ratespec) +{ + u32 rate = (ratespec & BRCMS_RATE_MASK); + + return !is_mcs_rate(ratespec) && ( + rate == BRCM_RATE_1M || rate == BRCM_RATE_2M || + rate == BRCM_RATE_5M5 || rate == BRCM_RATE_11M); +} + +static inline bool is_single_stream(u8 mcs) +{ + return mcs <= HIGHEST_SINGLE_STREAM_MCS || mcs == 32; +} + +static inline u8 cck_rspec(u8 cck) +{ + return cck & RSPEC_RATE_MASK; +} + +/* Convert encoded rate value in plcp header to numerical rates in 500 KHz + * increments */ +static inline u8 ofdm_phy2mac_rate(u8 rlpt) +{ + return wlc_phy_get_ofdm_rate_lookup()[rlpt & 0x7]; +} + +static inline u8 cck_phy2mac_rate(u8 signal) +{ + return signal/5; +} + +/* Rates specified in brcms_c_rateset_filter() */ +#define BRCMS_RATES_CCK_OFDM 0 +#define BRCMS_RATES_CCK 1 +#define BRCMS_RATES_OFDM 2 + +/* sanitize, and sort a rateset with the basic bit(s) preserved, validate + * rateset */ +bool brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs, + const struct brcms_c_rateset *hw_rs, + bool check_brate, u8 txstreams); +/* copy rateset src to dst as-is (no masking or sorting) */ +void brcms_c_rateset_copy(const struct brcms_c_rateset *src, + struct brcms_c_rateset *dst); + +/* would be nice to have these documented ... */ +u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp); + +void brcms_c_rateset_filter(struct brcms_c_rateset *src, + struct brcms_c_rateset *dst, bool basic_only, + u8 rates, uint xmask, bool mcsallow); + +void brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt, + const struct brcms_c_rateset *rs_hw, uint phy_type, + int bandtype, bool cck_only, uint rate_mask, + bool mcsallow, u8 bw, u8 txstreams); + +s16 brcms_c_rate_legacy_phyctl(uint rate); + +void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams); +void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset); +void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, u8 txstreams); +void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, u8 bw); + +#endif /* _BRCM_RATE_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/scb.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/scb.h new file mode 100644 index 000000000000..3a3d73699f83 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/scb.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010 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 _BRCM_SCB_H_ +#define _BRCM_SCB_H_ + +#include +#include +#include +#include "types.h" + +#define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */ + +#define AMPDU_MAX_SCB_TID NUMPRIO + +/* scb flags */ +#define SCB_WMECAP 0x0040 +#define SCB_HTCAP 0x10000 /* HT (MIMO) capable device */ +#define SCB_IS40 0x80000 /* 40MHz capable */ +#define SCB_STBCCAP 0x40000000 /* STBC Capable */ + +#define SCB_MAGIC 0xbeefcafe + +/* structure to store per-tid state for the ampdu initiator */ +struct scb_ampdu_tid_ini { + u8 tid; /* initiator tid for easy lookup */ + /* tx retry count; indexed by seq modulo */ + u8 txretry[AMPDU_TX_BA_MAX_WSIZE]; + struct scb *scb; /* backptr for easy lookup */ + u8 ba_wsize; /* negotiated ba window size (in pdu) */ +}; + +struct scb_ampdu { + struct scb *scb; /* back pointer for easy reference */ + u8 mpdu_density; /* mpdu density */ + u8 max_pdu; /* max pdus allowed in ampdu */ + u8 release; /* # of mpdus released at a time */ + u16 min_len; /* min mpdu len to support the density */ + u32 max_rx_ampdu_bytes; /* max ampdu rcv length; 8k, 16k, 32k, 64k */ + + /* + * This could easily be a ini[] pointer and we keep this info in wl + * itself instead of having mac80211 hold it for us. Also could be made + * dynamic per tid instead of static. + */ + /* initiator info - per tid (NUMPRIO): */ + struct scb_ampdu_tid_ini ini[AMPDU_MAX_SCB_TID]; +}; + +/* station control block - one per remote MAC address */ +struct scb { + u32 magic; + u32 flags; /* various bit flags as defined below */ + u32 flags2; /* various bit flags2 as defined below */ + u8 state; /* current state bitfield of auth/assoc process */ + u8 ea[ETH_ALEN]; /* station address */ + uint fragresid[NUMPRIO];/* #bytes unused in frag buffer per prio */ + + u16 seqctl[NUMPRIO]; /* seqctl of last received frame (for dups) */ + /* seqctl of last received frame (for dups) for non-QoS data and + * management */ + u16 seqctl_nonqos; + u16 seqnum[NUMPRIO];/* WME: driver maintained sw seqnum per priority */ + + struct scb_ampdu scb_ampdu; /* AMPDU state including per tid info */ +}; + +#endif /* _BRCM_SCB_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.c new file mode 100644 index 000000000000..dd9162722495 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2010 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 "types.h" +#include "d11.h" +#include "rate.h" +#include "phy/phy_hal.h" +#include "channel.h" +#include "main.h" +#include "stf.h" +#include "debug.h" + +#define MIN_SPATIAL_EXPANSION 0 +#define MAX_SPATIAL_EXPANSION 1 + +#define BRCMS_STF_SS_STBC_RX(wlc) (BRCMS_ISNPHY(wlc->band) && \ + NREV_GT(wlc->band->phyrev, 3) && NREV_LE(wlc->band->phyrev, 6)) + +#define NSTS_1 1 +#define NSTS_2 2 +#define NSTS_3 3 +#define NSTS_4 4 + +static const u8 txcore_default[5] = { + (0), /* bitmap of the core enabled */ + (0x01), /* For Nsts = 1, enable core 1 */ + (0x03), /* For Nsts = 2, enable core 1 & 2 */ + (0x07), /* For Nsts = 3, enable core 1, 2 & 3 */ + (0x0f) /* For Nsts = 4, enable all cores */ +}; + +static void brcms_c_stf_stbc_rx_ht_update(struct brcms_c_info *wlc, int val) +{ + /* MIMOPHYs rev3-6 cannot receive STBC with only one rx core active */ + if (BRCMS_STF_SS_STBC_RX(wlc)) { + if ((wlc->stf->rxstreams == 1) && (val != HT_CAP_RX_STBC_NO)) + return; + } + + if (wlc->pub->up) { + brcms_c_update_beacon(wlc); + brcms_c_update_probe_resp(wlc, true); + } +} + +/* + * every WLC_TEMPSENSE_PERIOD seconds temperature check to decide whether to + * turn on/off txchain. + */ +void brcms_c_tempsense_upd(struct brcms_c_info *wlc) +{ + struct brcms_phy_pub *pi = wlc->band->pi; + uint active_chains, txchain; + + /* Check if the chip is too hot. Disable one Tx chain, if it is */ + /* high 4 bits are for Rx chain, low 4 bits are for Tx chain */ + active_chains = wlc_phy_stf_chain_active_get(pi); + txchain = active_chains & 0xf; + + if (wlc->stf->txchain == wlc->stf->hw_txchain) { + if (txchain && (txchain < wlc->stf->hw_txchain)) + /* turn off 1 tx chain */ + brcms_c_stf_txchain_set(wlc, txchain, true); + } else if (wlc->stf->txchain < wlc->stf->hw_txchain) { + if (txchain == wlc->stf->hw_txchain) + /* turn back on txchain */ + brcms_c_stf_txchain_set(wlc, txchain, true); + } +} + +void +brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, u16 *ss_algo_channel, + u16 chanspec) +{ + struct tx_power power; + u8 siso_mcs_id, cdd_mcs_id, stbc_mcs_id; + + /* Clear previous settings */ + *ss_algo_channel = 0; + + if (!wlc->pub->up) { + *ss_algo_channel = (u16) -1; + return; + } + + wlc_phy_txpower_get_current(wlc->band->pi, &power, + CHSPEC_CHANNEL(chanspec)); + + siso_mcs_id = (CHSPEC_IS40(chanspec)) ? + WL_TX_POWER_MCS40_SISO_FIRST : WL_TX_POWER_MCS20_SISO_FIRST; + cdd_mcs_id = (CHSPEC_IS40(chanspec)) ? + WL_TX_POWER_MCS40_CDD_FIRST : WL_TX_POWER_MCS20_CDD_FIRST; + stbc_mcs_id = (CHSPEC_IS40(chanspec)) ? + WL_TX_POWER_MCS40_STBC_FIRST : WL_TX_POWER_MCS20_STBC_FIRST; + + /* criteria to choose stf mode */ + + /* + * the "+3dbm (12 0.25db units)" is to account for the fact that with + * CDD, tx occurs on both chains + */ + if (power.target[siso_mcs_id] > (power.target[cdd_mcs_id] + 12)) + setbit(ss_algo_channel, PHY_TXC1_MODE_SISO); + else + setbit(ss_algo_channel, PHY_TXC1_MODE_CDD); + + /* + * STBC is ORed into to algo channel as STBC requires per-packet SCB + * capability check so cannot be default mode of operation. One of + * SISO, CDD have to be set + */ + if (power.target[siso_mcs_id] <= (power.target[stbc_mcs_id] + 12)) + setbit(ss_algo_channel, PHY_TXC1_MODE_STBC); +} + +static bool brcms_c_stf_stbc_tx_set(struct brcms_c_info *wlc, s32 int_val) +{ + if ((int_val != AUTO) && (int_val != OFF) && (int_val != ON)) + return false; + + if ((int_val == ON) && (wlc->stf->txstreams == 1)) + return false; + + wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = (s8) int_val; + wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = (s8) int_val; + + return true; +} + +bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val) +{ + if ((int_val != HT_CAP_RX_STBC_NO) + && (int_val != HT_CAP_RX_STBC_ONE_STREAM)) + return false; + + if (BRCMS_STF_SS_STBC_RX(wlc)) { + if ((int_val != HT_CAP_RX_STBC_NO) + && (wlc->stf->rxstreams == 1)) + return false; + } + + brcms_c_stf_stbc_rx_ht_update(wlc, int_val); + return true; +} + +static int brcms_c_stf_txcore_set(struct brcms_c_info *wlc, u8 Nsts, + u8 core_mask) +{ + brcms_dbg_ht(wlc->hw->d11core, "wl%d: Nsts %d core_mask %x\n", + wlc->pub->unit, Nsts, core_mask); + + if (hweight8(core_mask) > wlc->stf->txstreams) + core_mask = 0; + + if ((hweight8(core_mask) == wlc->stf->txstreams) && + ((core_mask & ~wlc->stf->txchain) + || !(core_mask & wlc->stf->txchain))) + core_mask = wlc->stf->txchain; + + wlc->stf->txcore[Nsts] = core_mask; + /* Nsts = 1..4, txcore index = 1..4 */ + if (Nsts == 1) { + /* Needs to update beacon and ucode generated response + * frames when 1 stream core map changed + */ + wlc->stf->phytxant = core_mask << PHY_TXC_ANT_SHIFT; + brcms_b_txant_set(wlc->hw, wlc->stf->phytxant); + if (wlc->clk) { + brcms_c_suspend_mac_and_wait(wlc); + brcms_c_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec); + brcms_c_enable_mac(wlc); + } + } + + return 0; +} + +static int brcms_c_stf_spatial_policy_set(struct brcms_c_info *wlc, int val) +{ + int i; + u8 core_mask = 0; + + brcms_dbg_ht(wlc->hw->d11core, "wl%d: val %x\n", wlc->pub->unit, + val); + + wlc->stf->spatial_policy = (s8) val; + for (i = 1; i <= MAX_STREAMS_SUPPORTED; i++) { + core_mask = (val == MAX_SPATIAL_EXPANSION) ? + wlc->stf->txchain : txcore_default[i]; + brcms_c_stf_txcore_set(wlc, (u8) i, core_mask); + } + return 0; +} + +/* + * Centralized txant update function. call it whenever wlc->stf->txant and/or + * wlc->stf->txchain change. + * + * Antennas are controlled by ucode indirectly, which drives PHY or GPIO to + * achieve various tx/rx antenna selection schemes + * + * legacy phy, bit 6 and bit 7 means antenna 0 and 1 respectively, bit6+bit7 + * means auto(last rx). + * for NREV<3, bit 6 and bit 7 means antenna 0 and 1 respectively, bit6+bit7 + * means last rx and do tx-antenna selection for SISO transmissions + * for NREV=3, bit 6 and bit _8_ means antenna 0 and 1 respectively, bit6+bit7 + * means last rx and do tx-antenna selection for SISO transmissions + * for NREV>=7, bit 6 and bit 7 mean antenna 0 and 1 respectively, nit6+bit7 + * means both cores active +*/ +static void _brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc) +{ + s8 txant; + + txant = (s8) wlc->stf->txant; + if (BRCMS_PHY_11N_CAP(wlc->band)) { + if (txant == ANT_TX_FORCE_0) { + wlc->stf->phytxant = PHY_TXC_ANT_0; + } else if (txant == ANT_TX_FORCE_1) { + wlc->stf->phytxant = PHY_TXC_ANT_1; + + if (BRCMS_ISNPHY(wlc->band) && + NREV_GE(wlc->band->phyrev, 3) + && NREV_LT(wlc->band->phyrev, 7)) + wlc->stf->phytxant = PHY_TXC_ANT_2; + } else { + if (BRCMS_ISLCNPHY(wlc->band) || + BRCMS_ISSSLPNPHY(wlc->band)) + wlc->stf->phytxant = PHY_TXC_LCNPHY_ANT_LAST; + else { + /* catch out of sync wlc->stf->txcore */ + WARN_ON(wlc->stf->txchain <= 0); + wlc->stf->phytxant = + wlc->stf->txchain << PHY_TXC_ANT_SHIFT; + } + } + } else { + if (txant == ANT_TX_FORCE_0) + wlc->stf->phytxant = PHY_TXC_OLD_ANT_0; + else if (txant == ANT_TX_FORCE_1) + wlc->stf->phytxant = PHY_TXC_OLD_ANT_1; + else + wlc->stf->phytxant = PHY_TXC_OLD_ANT_LAST; + } + + brcms_b_txant_set(wlc->hw, wlc->stf->phytxant); +} + +int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, bool force) +{ + u8 txchain = (u8) int_val; + u8 txstreams; + uint i; + + if (wlc->stf->txchain == txchain) + return 0; + + if ((txchain & ~wlc->stf->hw_txchain) + || !(txchain & wlc->stf->hw_txchain)) + return -EINVAL; + + /* + * if nrate override is configured to be non-SISO STF mode, reject + * reducing txchain to 1 + */ + txstreams = (u8) hweight8(txchain); + if (txstreams > MAX_STREAMS_SUPPORTED) + return -EINVAL; + + wlc->stf->txchain = txchain; + wlc->stf->txstreams = txstreams; + brcms_c_stf_stbc_tx_set(wlc, wlc->band->band_stf_stbc_tx); + brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); + brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); + wlc->stf->txant = + (wlc->stf->txstreams == 1) ? ANT_TX_FORCE_0 : ANT_TX_DEF; + _brcms_c_stf_phy_txant_upd(wlc); + + wlc_phy_stf_chain_set(wlc->band->pi, wlc->stf->txchain, + wlc->stf->rxchain); + + for (i = 1; i <= MAX_STREAMS_SUPPORTED; i++) + brcms_c_stf_txcore_set(wlc, (u8) i, txcore_default[i]); + + return 0; +} + +/* + * update wlc->stf->ss_opmode which represents the operational stf_ss mode + * we're using + */ +int brcms_c_stf_ss_update(struct brcms_c_info *wlc, struct brcms_band *band) +{ + int ret_code = 0; + u8 prev_stf_ss; + u8 upd_stf_ss; + + prev_stf_ss = wlc->stf->ss_opmode; + + /* + * NOTE: opmode can only be SISO or CDD as STBC is decided on a + * per-packet basis + */ + if (BRCMS_STBC_CAP_PHY(wlc) && + wlc->stf->ss_algosel_auto + && (wlc->stf->ss_algo_channel != (u16) -1)) { + upd_stf_ss = (wlc->stf->txstreams == 1 || + isset(&wlc->stf->ss_algo_channel, + PHY_TXC1_MODE_SISO)) ? + PHY_TXC1_MODE_SISO : PHY_TXC1_MODE_CDD; + } else { + if (wlc->band != band) + return ret_code; + upd_stf_ss = (wlc->stf->txstreams == 1) ? + PHY_TXC1_MODE_SISO : band->band_stf_ss_mode; + } + if (prev_stf_ss != upd_stf_ss) { + wlc->stf->ss_opmode = upd_stf_ss; + brcms_b_band_stf_ss_set(wlc->hw, upd_stf_ss); + } + + return ret_code; +} + +int brcms_c_stf_attach(struct brcms_c_info *wlc) +{ + wlc->bandstate[BAND_2G_INDEX]->band_stf_ss_mode = PHY_TXC1_MODE_SISO; + wlc->bandstate[BAND_5G_INDEX]->band_stf_ss_mode = PHY_TXC1_MODE_CDD; + + if (BRCMS_ISNPHY(wlc->band) && + (wlc_phy_txpower_hw_ctrl_get(wlc->band->pi) != PHY_TPC_HW_ON)) + wlc->bandstate[BAND_2G_INDEX]->band_stf_ss_mode = + PHY_TXC1_MODE_CDD; + brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); + brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); + + brcms_c_stf_stbc_rx_ht_update(wlc, HT_CAP_RX_STBC_NO); + wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = OFF; + wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = OFF; + + if (BRCMS_STBC_CAP_PHY(wlc)) { + wlc->stf->ss_algosel_auto = true; + /* Init the default value */ + wlc->stf->ss_algo_channel = (u16) -1; + } + return 0; +} + +void brcms_c_stf_detach(struct brcms_c_info *wlc) +{ +} + +void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc) +{ + _brcms_c_stf_phy_txant_upd(wlc); +} + +void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc) +{ + struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; + + /* get available rx/tx chains */ + wlc->stf->hw_txchain = sprom->txchain; + wlc->stf->hw_rxchain = sprom->rxchain; + + /* these parameter are intended to be used for all PHY types */ + if (wlc->stf->hw_txchain == 0 || wlc->stf->hw_txchain == 0xf) { + if (BRCMS_ISNPHY(wlc->band)) + wlc->stf->hw_txchain = TXCHAIN_DEF_NPHY; + else + wlc->stf->hw_txchain = TXCHAIN_DEF; + } + + wlc->stf->txchain = wlc->stf->hw_txchain; + wlc->stf->txstreams = (u8) hweight8(wlc->stf->hw_txchain); + + if (wlc->stf->hw_rxchain == 0 || wlc->stf->hw_rxchain == 0xf) { + if (BRCMS_ISNPHY(wlc->band)) + wlc->stf->hw_rxchain = RXCHAIN_DEF_NPHY; + else + wlc->stf->hw_rxchain = RXCHAIN_DEF; + } + + wlc->stf->rxchain = wlc->stf->hw_rxchain; + wlc->stf->rxstreams = (u8) hweight8(wlc->stf->hw_rxchain); + + /* initialize the txcore table */ + memcpy(wlc->stf->txcore, txcore_default, sizeof(wlc->stf->txcore)); + + /* default spatial_policy */ + wlc->stf->spatial_policy = MIN_SPATIAL_EXPANSION; + brcms_c_stf_spatial_policy_set(wlc, MIN_SPATIAL_EXPANSION); +} + +static u16 _brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, + u32 rspec) +{ + u16 phytxant = wlc->stf->phytxant; + + if (rspec_stf(rspec) != PHY_TXC1_MODE_SISO) + phytxant = wlc->stf->txchain << PHY_TXC_ANT_SHIFT; + else if (wlc->stf->txant == ANT_TX_DEF) + phytxant = wlc->stf->txchain << PHY_TXC_ANT_SHIFT; + phytxant &= PHY_TXC_ANT_MASK; + return phytxant; +} + +u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, u32 rspec) +{ + return _brcms_c_stf_phytxchain_sel(wlc, rspec); +} + +u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, u32 rspec) +{ + u16 phytxant = wlc->stf->phytxant; + u16 mask = PHY_TXC_ANT_MASK; + + /* for non-siso rates or default setting, use the available chains */ + if (BRCMS_ISNPHY(wlc->band)) { + phytxant = _brcms_c_stf_phytxchain_sel(wlc, rspec); + mask = PHY_TXC_HTANT_MASK; + } + phytxant |= phytxant & mask; + return phytxant; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.h new file mode 100644 index 000000000000..ba9493009a33 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010 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 _BRCM_STF_H_ +#define _BRCM_STF_H_ + +#include "types.h" + +int brcms_c_stf_attach(struct brcms_c_info *wlc); +void brcms_c_stf_detach(struct brcms_c_info *wlc); + +void brcms_c_tempsense_upd(struct brcms_c_info *wlc); +void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, + u16 *ss_algo_channel, u16 chanspec); +int brcms_c_stf_ss_update(struct brcms_c_info *wlc, struct brcms_band *band); +void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); +int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, bool force); +bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val); +void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); +void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc); +u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, u32 rspec); +u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, u32 rspec); + +#endif /* _BRCM_STF_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/types.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/types.h new file mode 100644 index 000000000000..ae1f3ad40d45 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/types.h @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2010 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 _BRCM_TYPES_H_ +#define _BRCM_TYPES_H_ + +#include +#include + +#define WL_CHAN_FREQ_RANGE_2G 0 +#define WL_CHAN_FREQ_RANGE_5GL 1 +#define WL_CHAN_FREQ_RANGE_5GM 2 +#define WL_CHAN_FREQ_RANGE_5GH 3 + +/* boardflags */ + +/* Board has gpio 9 controlling the PA */ +#define BFL_PACTRL 0x00000002 +/* Not ok to power down the chip pll and oscillator */ +#define BFL_NOPLLDOWN 0x00000020 +/* Board supports the Front End Module */ +#define BFL_FEM 0x00000800 +/* Board has an external LNA in 2.4GHz band */ +#define BFL_EXTLNA 0x00001000 +/* Board has no PA */ +#define BFL_NOPA 0x00010000 +/* Power topology uses BUCKBOOST */ +#define BFL_BUCKBOOST 0x00200000 +/* Board has FEM and switch to share antenna w/ BT */ +#define BFL_FEM_BT 0x00400000 +/* Power topology doesn't use CBUCK */ +#define BFL_NOCBUCK 0x00800000 +/* Power topology uses PALDO */ +#define BFL_PALDO 0x02000000 +/* Board has an external LNA in 5GHz band */ +#define BFL_EXTLNA_5GHz 0x10000000 + +/* boardflags2 */ + +/* Board has an external rxbb regulator */ +#define BFL2_RXBB_INT_REG_DIS 0x00000001 +/* Flag to implement alternative A-band PLL settings */ +#define BFL2_APLL_WAR 0x00000002 +/* Board permits enabling TX Power Control */ +#define BFL2_TXPWRCTRL_EN 0x00000004 +/* Board supports the 2X4 diversity switch */ +#define BFL2_2X4_DIV 0x00000008 +/* Board supports 5G band power gain */ +#define BFL2_5G_PWRGAIN 0x00000010 +/* Board overrides ASPM and Clkreq settings */ +#define BFL2_PCIEWAR_OVR 0x00000020 +#define BFL2_LEGACY 0x00000080 +/* 4321mcm93 board uses Skyworks FEM */ +#define BFL2_SKWRKFEM_BRD 0x00000100 +/* Board has a WAR for clock-harmonic spurs */ +#define BFL2_SPUR_WAR 0x00000200 +/* Flag to narrow G-band PLL loop b/w */ +#define BFL2_GPLL_WAR 0x00000400 +/* Tx CCK pkts on Ant 0 only */ +#define BFL2_SINGLEANT_CCK 0x00001000 +/* WAR to reduce and avoid clock-harmonic spurs in 2G */ +#define BFL2_2G_SPUR_WAR 0x00002000 +/* Flag to widen G-band PLL loop b/w */ +#define BFL2_GPLL_WAR2 0x00010000 +#define BFL2_IPALVLSHIFT_3P3 0x00020000 +/* Use internal envelope detector for TX IQCAL */ +#define BFL2_INTERNDET_TXIQCAL 0x00040000 +/* Keep the buffered Xtal output from radio "ON". Most drivers will turn it + * off without this flag to save power. */ +#define BFL2_XTALBUFOUTEN 0x00080000 + +/* + * board specific GPIO assignment, gpio 0-3 are also customer-configurable + * led + */ + +/* bit 9 controls the PA on new 4306 boards */ +#define BOARD_GPIO_PACTRL 0x200 +#define BOARD_GPIO_12 0x1000 +#define BOARD_GPIO_13 0x2000 + +/* **** Core type/rev defaults **** */ +#define D11CONF 0x0fffffb0 /* Supported D11 revs: 4, 5, 7-27 + * also need to update wlc.h MAXCOREREV + */ + +#define NCONF 0x000001ff /* Supported nphy revs: + * 0 4321a0 + * 1 4321a1 + * 2 4321b0/b1/c0/c1 + * 3 4322a0 + * 4 4322a1 + * 5 4716a0 + * 6 43222a0, 43224a0 + * 7 43226a0 + * 8 5357a0, 43236a0 + */ + +#define LCNCONF 0x00000007 /* Supported lcnphy revs: + * 0 4313a0, 4336a0, 4330a0 + * 1 + * 2 4330a0 + */ + +#define SSLPNCONF 0x0000000f /* Supported sslpnphy revs: + * 0 4329a0/k0 + * 1 4329b0/4329C0 + * 2 4319a0 + * 3 5356a0 + */ + +/******************************************************************** + * Phy/Core Configuration. Defines macros to to check core phy/rev * + * compile-time configuration. Defines default core support. * + * ****************************************************************** + */ + +/* Basic macros to check a configuration bitmask */ + +#define CONF_HAS(config, val) ((config) & (1 << (val))) +#define CONF_MSK(config, mask) ((config) & (mask)) +#define MSK_RANGE(low, hi) ((1 << ((hi)+1)) - (1 << (low))) +#define CONF_RANGE(config, low, hi) (CONF_MSK(config, MSK_RANGE(low, high))) + +#define CONF_IS(config, val) ((config) == (1 << (val))) +#define CONF_GE(config, val) ((config) & (0-(1 << (val)))) +#define CONF_GT(config, val) ((config) & (0-2*(1 << (val)))) +#define CONF_LT(config, val) ((config) & ((1 << (val))-1)) +#define CONF_LE(config, val) ((config) & (2*(1 << (val))-1)) + +/* Wrappers for some of the above, specific to config constants */ + +#define NCONF_HAS(val) CONF_HAS(NCONF, val) +#define NCONF_MSK(mask) CONF_MSK(NCONF, mask) +#define NCONF_IS(val) CONF_IS(NCONF, val) +#define NCONF_GE(val) CONF_GE(NCONF, val) +#define NCONF_GT(val) CONF_GT(NCONF, val) +#define NCONF_LT(val) CONF_LT(NCONF, val) +#define NCONF_LE(val) CONF_LE(NCONF, val) + +#define LCNCONF_HAS(val) CONF_HAS(LCNCONF, val) +#define LCNCONF_MSK(mask) CONF_MSK(LCNCONF, mask) +#define LCNCONF_IS(val) CONF_IS(LCNCONF, val) +#define LCNCONF_GE(val) CONF_GE(LCNCONF, val) +#define LCNCONF_GT(val) CONF_GT(LCNCONF, val) +#define LCNCONF_LT(val) CONF_LT(LCNCONF, val) +#define LCNCONF_LE(val) CONF_LE(LCNCONF, val) + +#define D11CONF_HAS(val) CONF_HAS(D11CONF, val) +#define D11CONF_MSK(mask) CONF_MSK(D11CONF, mask) +#define D11CONF_IS(val) CONF_IS(D11CONF, val) +#define D11CONF_GE(val) CONF_GE(D11CONF, val) +#define D11CONF_GT(val) CONF_GT(D11CONF, val) +#define D11CONF_LT(val) CONF_LT(D11CONF, val) +#define D11CONF_LE(val) CONF_LE(D11CONF, val) + +#define PHYCONF_HAS(val) CONF_HAS(PHYTYPE, val) +#define PHYCONF_IS(val) CONF_IS(PHYTYPE, val) + +#define NREV_IS(var, val) \ + (NCONF_HAS(val) && (NCONF_IS(val) || ((var) == (val)))) + +#define NREV_GE(var, val) \ + (NCONF_GE(val) && (!NCONF_LT(val) || ((var) >= (val)))) + +#define NREV_GT(var, val) \ + (NCONF_GT(val) && (!NCONF_LE(val) || ((var) > (val)))) + +#define NREV_LT(var, val) \ + (NCONF_LT(val) && (!NCONF_GE(val) || ((var) < (val)))) + +#define NREV_LE(var, val) \ + (NCONF_LE(val) && (!NCONF_GT(val) || ((var) <= (val)))) + +#define LCNREV_IS(var, val) \ + (LCNCONF_HAS(val) && (LCNCONF_IS(val) || ((var) == (val)))) + +#define LCNREV_GE(var, val) \ + (LCNCONF_GE(val) && (!LCNCONF_LT(val) || ((var) >= (val)))) + +#define LCNREV_GT(var, val) \ + (LCNCONF_GT(val) && (!LCNCONF_LE(val) || ((var) > (val)))) + +#define LCNREV_LT(var, val) \ + (LCNCONF_LT(val) && (!LCNCONF_GE(val) || ((var) < (val)))) + +#define LCNREV_LE(var, val) \ + (LCNCONF_LE(val) && (!LCNCONF_GT(val) || ((var) <= (val)))) + +#define D11REV_IS(var, val) \ + (D11CONF_HAS(val) && (D11CONF_IS(val) || ((var) == (val)))) + +#define D11REV_GE(var, val) \ + (D11CONF_GE(val) && (!D11CONF_LT(val) || ((var) >= (val)))) + +#define D11REV_GT(var, val) \ + (D11CONF_GT(val) && (!D11CONF_LE(val) || ((var) > (val)))) + +#define D11REV_LT(var, val) \ + (D11CONF_LT(val) && (!D11CONF_GE(val) || ((var) < (val)))) + +#define D11REV_LE(var, val) \ + (D11CONF_LE(val) && (!D11CONF_GT(val) || ((var) <= (val)))) + +#define PHYTYPE_IS(var, val)\ + (PHYCONF_HAS(val) && (PHYCONF_IS(val) || ((var) == (val)))) + +/* Set up PHYTYPE automatically: (depends on PHY_TYPE_X, from d11.h) */ + +#define _PHYCONF_N (1 << PHY_TYPE_N) +#define _PHYCONF_LCN (1 << PHY_TYPE_LCN) +#define _PHYCONF_SSLPN (1 << PHY_TYPE_SSN) + +#define PHYTYPE (_PHYCONF_N | _PHYCONF_LCN | _PHYCONF_SSLPN) + +/* Utility macro to identify 802.11n (HT) capable PHYs */ +#define PHYTYPE_11N_CAP(phytype) \ + (PHYTYPE_IS(phytype, PHY_TYPE_N) || \ + PHYTYPE_IS(phytype, PHY_TYPE_LCN) || \ + PHYTYPE_IS(phytype, PHY_TYPE_SSN)) + +/* Last but not least: shorter wlc-specific var checks */ +#define BRCMS_ISNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_N) +#define BRCMS_ISLCNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_LCN) +#define BRCMS_ISSSLPNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_SSN) + +#define BRCMS_PHY_11N_CAP(band) PHYTYPE_11N_CAP((band)->phytype) + +/********************************************************************** + * ------------- End of Core phy/rev configuration. ----------------- * + * ******************************************************************** + */ + +#define BCMMSG(dev, fmt, args...) \ +do { \ + if (brcm_msg_level & BRCM_DL_INFO) \ + wiphy_err(dev, "%s: " fmt, __func__, ##args); \ +} while (0) + +#ifdef CONFIG_BCM47XX +/* + * bcm4716 (which includes 4717 & 4718), plus 4706 on PCIe can reorder + * transactions. As a fix, a read after write is performed on certain places + * in the code. Older chips and the newer 5357 family don't require this fix. + */ +#define bcma_wflush16(c, o, v) \ + ({ bcma_write16(c, o, v); (void)bcma_read16(c, o); }) +#else +#define bcma_wflush16(c, o, v) bcma_write16(c, o, v) +#endif /* CONFIG_BCM47XX */ + +/* multi-bool data type: set of bools, mbool is true if any is set */ + +/* set one bool */ +#define mboolset(mb, bit) ((mb) |= (bit)) +/* clear one bool */ +#define mboolclr(mb, bit) ((mb) &= ~(bit)) +/* true if one bool is set */ +#define mboolisset(mb, bit) (((mb) & (bit)) != 0) +#define mboolmaskset(mb, mask, val) ((mb) = (((mb) & ~(mask)) | (val))) + +#define CEIL(x, y) (((x) + ((y)-1)) / (y)) + +/* forward declarations */ +struct wiphy; +struct ieee80211_sta; +struct ieee80211_tx_queue_params; +struct brcms_info; +struct brcms_c_info; +struct brcms_hardware; +struct brcms_band; +struct dma_pub; +struct si_pub; +struct tx_status; +struct d11rxhdr; +struct txpwr_limits; + +/* iovar structure */ +struct brcmu_iovar { + const char *name; /* name for lookup and display */ + u16 varid; /* id for switch */ + u16 flags; /* driver-specific flag bits */ + u16 type; /* base type of argument */ + u16 minlen; /* min length for buffer vars */ +}; + +/* brcm_msg_level is a bit vector with defs in defs.h */ +extern u32 brcm_msg_level; + +#endif /* _BRCM_TYPES_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.c new file mode 100644 index 000000000000..80e3ccf865e3 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2010 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 "types.h" +#include + +enum { + D11UCODE_NAMETAG_START = 0, + D11LCN0BSINITVALS24, + D11LCN0INITVALS24, + D11LCN1BSINITVALS24, + D11LCN1INITVALS24, + D11LCN2BSINITVALS24, + D11LCN2INITVALS24, + D11N0ABSINITVALS16, + D11N0BSINITVALS16, + D11N0INITVALS16, + D11UCODE_OVERSIGHT16_MIMO, + D11UCODE_OVERSIGHT16_MIMOSZ, + D11UCODE_OVERSIGHT24_LCN, + D11UCODE_OVERSIGHT24_LCNSZ, + D11UCODE_OVERSIGHT_BOMMAJOR, + D11UCODE_OVERSIGHT_BOMMINOR +}; + +int brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode) +{ + int rc; + + rc = brcms_check_firmwares(wl); + + rc = rc < 0 ? rc : + brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn0bsinitvals24, + D11LCN0BSINITVALS24); + rc = rc < 0 ? + rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn0initvals24, + D11LCN0INITVALS24); + rc = rc < 0 ? + rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn1bsinitvals24, + D11LCN1BSINITVALS24); + rc = rc < 0 ? + rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn1initvals24, + D11LCN1INITVALS24); + rc = rc < 0 ? rc : + brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn2bsinitvals24, + D11LCN2BSINITVALS24); + rc = rc < 0 ? + rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11lcn2initvals24, + D11LCN2INITVALS24); + rc = rc < 0 ? + rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0absinitvals16, + D11N0ABSINITVALS16); + rc = rc < 0 ? + rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0bsinitvals16, + D11N0BSINITVALS16); + rc = rc < 0 ? + rc : brcms_ucode_init_buf(wl, (void **)&ucode->d11n0initvals16, + D11N0INITVALS16); + rc = rc < 0 ? + rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_16_mimo, + D11UCODE_OVERSIGHT16_MIMO); + rc = rc < 0 ? + rc : brcms_ucode_init_uint(wl, &ucode->bcm43xx_16_mimosz, + D11UCODE_OVERSIGHT16_MIMOSZ); + rc = rc < 0 ? + rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_24_lcn, + D11UCODE_OVERSIGHT24_LCN); + rc = rc < 0 ? + rc : brcms_ucode_init_uint(wl, &ucode->bcm43xx_24_lcnsz, + D11UCODE_OVERSIGHT24_LCNSZ); + rc = rc < 0 ? + rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_bommajor, + D11UCODE_OVERSIGHT_BOMMAJOR); + rc = rc < 0 ? + rc : brcms_ucode_init_buf(wl, (void **)&ucode->bcm43xx_bomminor, + D11UCODE_OVERSIGHT_BOMMINOR); + return rc; +} + +void brcms_ucode_data_free(struct brcms_ucode *ucode) +{ + brcms_ucode_free_buf((void *)ucode->d11lcn0bsinitvals24); + brcms_ucode_free_buf((void *)ucode->d11lcn0initvals24); + brcms_ucode_free_buf((void *)ucode->d11lcn1bsinitvals24); + brcms_ucode_free_buf((void *)ucode->d11lcn1initvals24); + brcms_ucode_free_buf((void *)ucode->d11lcn2bsinitvals24); + brcms_ucode_free_buf((void *)ucode->d11lcn2initvals24); + brcms_ucode_free_buf((void *)ucode->d11n0absinitvals16); + brcms_ucode_free_buf((void *)ucode->d11n0bsinitvals16); + brcms_ucode_free_buf((void *)ucode->d11n0initvals16); + brcms_ucode_free_buf((void *)ucode->bcm43xx_16_mimo); + brcms_ucode_free_buf((void *)ucode->bcm43xx_24_lcn); + brcms_ucode_free_buf((void *)ucode->bcm43xx_bommajor); + brcms_ucode_free_buf((void *)ucode->bcm43xx_bomminor); +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.h new file mode 100644 index 000000000000..c87dd89bcb78 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2010 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 _BRCM_UCODE_H_ +#define _BRCM_UCODE_H_ + +#include "types.h" /* forward structure declarations */ + +#define MIN_FW_SIZE 40000 /* minimum firmware file size in bytes */ +#define MAX_FW_SIZE 150000 + +#define UCODE_LOADER_API_VER 0 + +struct d11init; + +struct brcms_ucode { + struct d11init *d11lcn0bsinitvals24; + struct d11init *d11lcn0initvals24; + struct d11init *d11lcn1bsinitvals24; + struct d11init *d11lcn1initvals24; + struct d11init *d11lcn2bsinitvals24; + struct d11init *d11lcn2initvals24; + struct d11init *d11n0absinitvals16; + struct d11init *d11n0bsinitvals16; + struct d11init *d11n0initvals16; + __le32 *bcm43xx_16_mimo; + size_t bcm43xx_16_mimosz; + __le32 *bcm43xx_24_lcn; + size_t bcm43xx_24_lcnsz; + u32 *bcm43xx_bommajor; + u32 *bcm43xx_bomminor; +}; + +int brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode); + +void brcms_ucode_data_free(struct brcms_ucode *ucode); + +int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, unsigned int idx); +int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, + unsigned int idx); +void brcms_ucode_free_buf(void *); +int brcms_check_firmwares(struct brcms_info *wl); + +#endif /* _BRCM_UCODE_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile new file mode 100644 index 000000000000..256c91f9ac4b --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile @@ -0,0 +1,23 @@ +# +# Makefile fragment for Broadcom 802.11n Networking Device Driver Utilities +# +# Copyright (c) 2011 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. + +ccflags-y := \ + -Idrivers/net/wireless/broadcom/brcm80211/brcmutil \ + -Idrivers/net/wireless/broadcom/brcm80211/include + +obj-$(CONFIG_BRCMUTIL) += brcmutil.o +brcmutil-objs = utils.o d11.o diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c new file mode 100644 index 000000000000..2b2522bdd8eb --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c @@ -0,0 +1,221 @@ +/* + * 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. + */ +/*********************channel spec common functions*********************/ + +#include + +#include +#include +#include + +static u16 d11n_sb(enum brcmu_chan_sb sb) +{ + switch (sb) { + case BRCMU_CHAN_SB_NONE: + return BRCMU_CHSPEC_D11N_SB_N; + case BRCMU_CHAN_SB_L: + return BRCMU_CHSPEC_D11N_SB_L; + case BRCMU_CHAN_SB_U: + return BRCMU_CHSPEC_D11N_SB_U; + default: + WARN_ON(1); + } + return 0; +} + +static u16 d11n_bw(enum brcmu_chan_bw bw) +{ + switch (bw) { + case BRCMU_CHAN_BW_20: + return BRCMU_CHSPEC_D11N_BW_20; + case BRCMU_CHAN_BW_40: + return BRCMU_CHSPEC_D11N_BW_40; + default: + WARN_ON(1); + } + return 0; +} + +static void brcmu_d11n_encchspec(struct brcmu_chan *ch) +{ + if (ch->bw == BRCMU_CHAN_BW_20) + ch->sb = BRCMU_CHAN_SB_NONE; + + ch->chspec = 0; + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK, + BRCMU_CHSPEC_CH_SHIFT, ch->chnum); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_SB_MASK, + 0, d11n_sb(ch->sb)); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_BW_MASK, + 0, d11n_bw(ch->bw)); + + if (ch->chnum <= CH_MAX_2G_CHANNEL) + ch->chspec |= BRCMU_CHSPEC_D11N_BND_2G; + else + ch->chspec |= BRCMU_CHSPEC_D11N_BND_5G; +} + +static u16 d11ac_bw(enum brcmu_chan_bw bw) +{ + switch (bw) { + case BRCMU_CHAN_BW_20: + return BRCMU_CHSPEC_D11AC_BW_20; + case BRCMU_CHAN_BW_40: + return BRCMU_CHSPEC_D11AC_BW_40; + case BRCMU_CHAN_BW_80: + return BRCMU_CHSPEC_D11AC_BW_80; + default: + WARN_ON(1); + } + return 0; +} + +static void brcmu_d11ac_encchspec(struct brcmu_chan *ch) +{ + if (ch->bw == BRCMU_CHAN_BW_20 || ch->sb == BRCMU_CHAN_SB_NONE) + ch->sb = BRCMU_CHAN_SB_L; + + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK, + BRCMU_CHSPEC_CH_SHIFT, ch->chnum); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK, + BRCMU_CHSPEC_D11AC_SB_SHIFT, ch->sb); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_BW_MASK, + 0, d11ac_bw(ch->bw)); + + ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK; + if (ch->chnum <= CH_MAX_2G_CHANNEL) + ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G; + else + ch->chspec |= BRCMU_CHSPEC_D11AC_BND_5G; +} + +static void brcmu_d11n_decchspec(struct brcmu_chan *ch) +{ + u16 val; + + ch->chnum = (u8)(ch->chspec & BRCMU_CHSPEC_CH_MASK); + + switch (ch->chspec & BRCMU_CHSPEC_D11N_BW_MASK) { + case BRCMU_CHSPEC_D11N_BW_20: + ch->bw = BRCMU_CHAN_BW_20; + ch->sb = BRCMU_CHAN_SB_NONE; + break; + case BRCMU_CHSPEC_D11N_BW_40: + ch->bw = BRCMU_CHAN_BW_40; + val = ch->chspec & BRCMU_CHSPEC_D11N_SB_MASK; + if (val == BRCMU_CHSPEC_D11N_SB_L) { + ch->sb = BRCMU_CHAN_SB_L; + ch->chnum -= CH_10MHZ_APART; + } else { + ch->sb = BRCMU_CHAN_SB_U; + ch->chnum += CH_10MHZ_APART; + } + break; + default: + WARN_ON_ONCE(1); + break; + } + + switch (ch->chspec & BRCMU_CHSPEC_D11N_BND_MASK) { + case BRCMU_CHSPEC_D11N_BND_5G: + ch->band = BRCMU_CHAN_BAND_5G; + break; + case BRCMU_CHSPEC_D11N_BND_2G: + ch->band = BRCMU_CHAN_BAND_2G; + break; + default: + WARN_ON_ONCE(1); + break; + } +} + +static void brcmu_d11ac_decchspec(struct brcmu_chan *ch) +{ + u16 val; + + ch->chnum = (u8)(ch->chspec & BRCMU_CHSPEC_CH_MASK); + + switch (ch->chspec & BRCMU_CHSPEC_D11AC_BW_MASK) { + case BRCMU_CHSPEC_D11AC_BW_20: + ch->bw = BRCMU_CHAN_BW_20; + ch->sb = BRCMU_CHAN_SB_NONE; + break; + case BRCMU_CHSPEC_D11AC_BW_40: + ch->bw = BRCMU_CHAN_BW_40; + val = ch->chspec & BRCMU_CHSPEC_D11AC_SB_MASK; + if (val == BRCMU_CHSPEC_D11AC_SB_L) { + ch->sb = BRCMU_CHAN_SB_L; + ch->chnum -= CH_10MHZ_APART; + } else if (val == BRCMU_CHSPEC_D11AC_SB_U) { + ch->sb = BRCMU_CHAN_SB_U; + ch->chnum += CH_10MHZ_APART; + } else { + WARN_ON_ONCE(1); + } + break; + case BRCMU_CHSPEC_D11AC_BW_80: + ch->bw = BRCMU_CHAN_BW_80; + ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK, + BRCMU_CHSPEC_D11AC_SB_SHIFT); + switch (ch->sb) { + case BRCMU_CHAN_SB_LL: + ch->chnum -= CH_30MHZ_APART; + break; + case BRCMU_CHAN_SB_LU: + ch->chnum -= CH_10MHZ_APART; + break; + case BRCMU_CHAN_SB_UL: + ch->chnum += CH_10MHZ_APART; + break; + case BRCMU_CHAN_SB_UU: + ch->chnum += CH_30MHZ_APART; + break; + default: + WARN_ON_ONCE(1); + break; + } + break; + case BRCMU_CHSPEC_D11AC_BW_8080: + case BRCMU_CHSPEC_D11AC_BW_160: + default: + WARN_ON_ONCE(1); + break; + } + + switch (ch->chspec & BRCMU_CHSPEC_D11AC_BND_MASK) { + case BRCMU_CHSPEC_D11AC_BND_5G: + ch->band = BRCMU_CHAN_BAND_5G; + break; + case BRCMU_CHSPEC_D11AC_BND_2G: + ch->band = BRCMU_CHAN_BAND_2G; + break; + default: + WARN_ON_ONCE(1); + break; + } +} + +void brcmu_d11_attach(struct brcmu_d11inf *d11inf) +{ + if (d11inf->io_type == BRCMU_D11N_IOTYPE) { + d11inf->encchspec = brcmu_d11n_encchspec; + d11inf->decchspec = brcmu_d11n_decchspec; + } else { + d11inf->encchspec = brcmu_d11ac_encchspec; + d11inf->decchspec = brcmu_d11ac_decchspec; + } +} +EXPORT_SYMBOL(brcmu_d11_attach); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c new file mode 100644 index 000000000000..0543607002fd --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2010 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +#include + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver utilities."); +MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards"); +MODULE_LICENSE("Dual BSD/GPL"); + +struct sk_buff *brcmu_pkt_buf_get_skb(uint len) +{ + struct sk_buff *skb; + + skb = dev_alloc_skb(len); + if (skb) { + skb_put(skb, len); + skb->priority = 0; + } + + return skb; +} +EXPORT_SYMBOL(brcmu_pkt_buf_get_skb); + +/* Free the driver packet. Free the tag if present */ +void brcmu_pkt_buf_free_skb(struct sk_buff *skb) +{ + if (!skb) + return; + + WARN_ON(skb->next); + dev_kfree_skb_any(skb); +} +EXPORT_SYMBOL(brcmu_pkt_buf_free_skb); + +/* + * osl multiple-precedence packet queue + * hi_prec is always >= the number of the highest non-empty precedence + */ +struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, + struct sk_buff *p) +{ + struct sk_buff_head *q; + + if (pktq_full(pq) || pktq_pfull(pq, prec)) + return NULL; + + q = &pq->q[prec].skblist; + skb_queue_tail(q, p); + pq->len++; + + if (pq->hi_prec < prec) + pq->hi_prec = (u8) prec; + + return p; +} +EXPORT_SYMBOL(brcmu_pktq_penq); + +struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, + struct sk_buff *p) +{ + struct sk_buff_head *q; + + if (pktq_full(pq) || pktq_pfull(pq, prec)) + return NULL; + + q = &pq->q[prec].skblist; + skb_queue_head(q, p); + pq->len++; + + if (pq->hi_prec < prec) + pq->hi_prec = (u8) prec; + + return p; +} +EXPORT_SYMBOL(brcmu_pktq_penq_head); + +struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec) +{ + struct sk_buff_head *q; + struct sk_buff *p; + + q = &pq->q[prec].skblist; + p = skb_dequeue(q); + if (p == NULL) + return NULL; + + pq->len--; + return p; +} +EXPORT_SYMBOL(brcmu_pktq_pdeq); + +/* + * precedence based dequeue with match function. Passing a NULL pointer + * for the match function parameter is considered to be a wildcard so + * any packet on the queue is returned. In that case it is no different + * from brcmu_pktq_pdeq() above. + */ +struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec, + bool (*match_fn)(struct sk_buff *skb, + void *arg), void *arg) +{ + struct sk_buff_head *q; + struct sk_buff *p, *next; + + q = &pq->q[prec].skblist; + skb_queue_walk_safe(q, p, next) { + if (match_fn == NULL || match_fn(p, arg)) { + skb_unlink(p, q); + pq->len--; + return p; + } + } + return NULL; +} +EXPORT_SYMBOL(brcmu_pktq_pdeq_match); + +struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec) +{ + struct sk_buff_head *q; + struct sk_buff *p; + + q = &pq->q[prec].skblist; + p = skb_dequeue_tail(q); + if (p == NULL) + return NULL; + + pq->len--; + return p; +} +EXPORT_SYMBOL(brcmu_pktq_pdeq_tail); + +void +brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir, + bool (*fn)(struct sk_buff *, void *), void *arg) +{ + struct sk_buff_head *q; + struct sk_buff *p, *next; + + q = &pq->q[prec].skblist; + skb_queue_walk_safe(q, p, next) { + if (fn == NULL || (*fn) (p, arg)) { + skb_unlink(p, q); + brcmu_pkt_buf_free_skb(p); + pq->len--; + } + } +} +EXPORT_SYMBOL(brcmu_pktq_pflush); + +void brcmu_pktq_flush(struct pktq *pq, bool dir, + bool (*fn)(struct sk_buff *, void *), void *arg) +{ + int prec; + for (prec = 0; prec < pq->num_prec; prec++) + brcmu_pktq_pflush(pq, prec, dir, fn, arg); +} +EXPORT_SYMBOL(brcmu_pktq_flush); + +void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len) +{ + int prec; + + /* pq is variable size; only zero out what's requested */ + memset(pq, 0, + offsetof(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec)); + + pq->num_prec = (u16) num_prec; + + pq->max = (u16) max_len; + + for (prec = 0; prec < num_prec; prec++) { + pq->q[prec].max = pq->max; + skb_queue_head_init(&pq->q[prec].skblist); + } +} +EXPORT_SYMBOL(brcmu_pktq_init); + +struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out) +{ + int prec; + + if (pq->len == 0) + return NULL; + + for (prec = 0; prec < pq->hi_prec; prec++) + if (!skb_queue_empty(&pq->q[prec].skblist)) + break; + + if (prec_out) + *prec_out = prec; + + return skb_peek_tail(&pq->q[prec].skblist); +} +EXPORT_SYMBOL(brcmu_pktq_peek_tail); + +/* Return sum of lengths of a specific set of precedences */ +int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp) +{ + int prec, len; + + len = 0; + + for (prec = 0; prec <= pq->hi_prec; prec++) + if (prec_bmp & (1 << prec)) + len += pq->q[prec].skblist.qlen; + + return len; +} +EXPORT_SYMBOL(brcmu_pktq_mlen); + +/* Priority dequeue from a specific set of precedences */ +struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, + int *prec_out) +{ + struct sk_buff_head *q; + struct sk_buff *p; + int prec; + + if (pq->len == 0) + return NULL; + + while ((prec = pq->hi_prec) > 0 && + skb_queue_empty(&pq->q[prec].skblist)) + pq->hi_prec--; + + while ((prec_bmp & (1 << prec)) == 0 || + skb_queue_empty(&pq->q[prec].skblist)) + if (prec-- == 0) + return NULL; + + q = &pq->q[prec].skblist; + p = skb_dequeue(q); + if (p == NULL) + return NULL; + + pq->len--; + + if (prec_out) + *prec_out = prec; + + return p; +} +EXPORT_SYMBOL(brcmu_pktq_mdeq); + +/* Produce a human-readable string for boardrev */ +char *brcmu_boardrev_str(u32 brev, char *buf) +{ + char c; + + if (brev < 0x100) { + snprintf(buf, BRCMU_BOARDREV_LEN, "%d.%d", + (brev & 0xf0) >> 4, brev & 0xf); + } else { + c = (brev & 0xf000) == 0x1000 ? 'P' : 'A'; + snprintf(buf, BRCMU_BOARDREV_LEN, "%c%03x", c, brev & 0xfff); + } + return buf; +} +EXPORT_SYMBOL(brcmu_boardrev_str); + +char *brcmu_dotrev_str(u32 dotrev, char *buf) +{ + u8 dotval[4]; + + if (!dotrev) { + snprintf(buf, BRCMU_DOTREV_LEN, "unknown"); + return buf; + } + dotval[0] = (dotrev >> 24) & 0xFF; + dotval[1] = (dotrev >> 16) & 0xFF; + dotval[2] = (dotrev >> 8) & 0xFF; + dotval[3] = dotrev & 0xFF; + + if (dotval[3]) + snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d.%d", dotval[0], + dotval[1], dotval[2], dotval[3]); + else if (dotval[2]) + snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d", dotval[0], + dotval[1], dotval[2]); + else + snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d", dotval[0], + dotval[1]); + + return buf; +} +EXPORT_SYMBOL(brcmu_dotrev_str); + +#if defined(DEBUG) +/* pretty hex print a pkt buffer chain */ +void brcmu_prpkt(const char *msg, struct sk_buff *p0) +{ + struct sk_buff *p; + + if (msg && (msg[0] != '\0')) + pr_debug("%s:\n", msg); + + for (p = p0; p; p = p->next) + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, p->data, p->len); +} +EXPORT_SYMBOL(brcmu_prpkt); + +void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + pr_debug("%pV", &vaf); + + va_end(args); + + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size); +} +EXPORT_SYMBOL(brcmu_dbg_hex_dump); + +#endif /* defined(DEBUG) */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h new file mode 100644 index 000000000000..aa06ea231db3 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2010 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 _BRCM_HW_IDS_H_ +#define _BRCM_HW_IDS_H_ + +#include +#include + +#define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c +#define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM + +/* Chipcommon Core Chip IDs */ +#define BRCM_CC_43143_CHIP_ID 43143 +#define BRCM_CC_43235_CHIP_ID 43235 +#define BRCM_CC_43236_CHIP_ID 43236 +#define BRCM_CC_43238_CHIP_ID 43238 +#define BRCM_CC_43241_CHIP_ID 0x4324 +#define BRCM_CC_43242_CHIP_ID 43242 +#define BRCM_CC_4329_CHIP_ID 0x4329 +#define BRCM_CC_4330_CHIP_ID 0x4330 +#define BRCM_CC_4334_CHIP_ID 0x4334 +#define BRCM_CC_43340_CHIP_ID 43340 +#define BRCM_CC_43362_CHIP_ID 43362 +#define BRCM_CC_4335_CHIP_ID 0x4335 +#define BRCM_CC_4339_CHIP_ID 0x4339 +#define BRCM_CC_43430_CHIP_ID 43430 +#define BRCM_CC_4345_CHIP_ID 0x4345 +#define BRCM_CC_4350_CHIP_ID 0x4350 +#define BRCM_CC_4354_CHIP_ID 0x4354 +#define BRCM_CC_4356_CHIP_ID 0x4356 +#define BRCM_CC_43566_CHIP_ID 43566 +#define BRCM_CC_43567_CHIP_ID 43567 +#define BRCM_CC_43569_CHIP_ID 43569 +#define BRCM_CC_43570_CHIP_ID 43570 +#define BRCM_CC_4358_CHIP_ID 0x4358 +#define BRCM_CC_43602_CHIP_ID 43602 +#define BRCM_CC_4365_CHIP_ID 0x4365 +#define BRCM_CC_4366_CHIP_ID 0x4366 +#define BRCM_CC_4371_CHIP_ID 0x4371 + +/* USB Device IDs */ +#define BRCM_USB_43143_DEVICE_ID 0xbd1e +#define BRCM_USB_43236_DEVICE_ID 0xbd17 +#define BRCM_USB_43242_DEVICE_ID 0xbd1f +#define BRCM_USB_43569_DEVICE_ID 0xbd27 +#define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc + +/* PCIE Device IDs */ +#define BRCM_PCIE_4350_DEVICE_ID 0x43a3 +#define BRCM_PCIE_4354_DEVICE_ID 0x43df +#define BRCM_PCIE_4356_DEVICE_ID 0x43ec +#define BRCM_PCIE_43567_DEVICE_ID 0x43d3 +#define BRCM_PCIE_43570_DEVICE_ID 0x43d9 +#define BRCM_PCIE_4358_DEVICE_ID 0x43e9 +#define BRCM_PCIE_43602_DEVICE_ID 0x43ba +#define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb +#define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc +#define BRCM_PCIE_43602_RAW_DEVICE_ID 43602 +#define BRCM_PCIE_4365_DEVICE_ID 0x43ca +#define BRCM_PCIE_4365_2G_DEVICE_ID 0x43cb +#define BRCM_PCIE_4365_5G_DEVICE_ID 0x43cc +#define BRCM_PCIE_4366_DEVICE_ID 0x43c3 +#define BRCM_PCIE_4366_2G_DEVICE_ID 0x43c4 +#define BRCM_PCIE_4366_5G_DEVICE_ID 0x43c5 +#define BRCM_PCIE_4371_DEVICE_ID 0x440d + + +/* brcmsmac IDs */ +#define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ +#define BCM43224_D11N_ID 0x4353 /* 43224 802.11n dualband device */ +#define BCM43224_D11N_ID_VEN1 0x0576 /* Vendor specific 43224 802.11n db */ +#define BCM43225_D11N2G_ID 0x4357 /* 43225 802.11n 2.4GHz device */ +#define BCM43236_D11N_ID 0x4346 /* 43236 802.11n dualband device */ +#define BCM43236_D11N2G_ID 0x4347 /* 43236 802.11n 2.4GHz device */ + +#define BCM4313_CHIP_ID 0x4313 +#define BCM43224_CHIP_ID 43224 + +#endif /* _BRCM_HW_IDS_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h new file mode 100644 index 000000000000..f9745ea8b3e0 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2010 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 _BRCMU_D11_H_ +#define _BRCMU_D11_H_ + +/* d11 io type */ +#define BRCMU_D11N_IOTYPE 1 +#define BRCMU_D11AC_IOTYPE 2 + +/* A chanspec (channel specification) holds the channel number, band, + * bandwidth and control sideband + */ + +/* chanspec binary format */ + +#define BRCMU_CHSPEC_INVALID 255 +/* bit 0~7 channel number + * for 80+80 channels: bit 0~3 low channel id, bit 4~7 high channel id + */ +#define BRCMU_CHSPEC_CH_MASK 0x00ff +#define BRCMU_CHSPEC_CH_SHIFT 0 +#define BRCMU_CHSPEC_CHL_MASK 0x000f +#define BRCMU_CHSPEC_CHL_SHIFT 0 +#define BRCMU_CHSPEC_CHH_MASK 0x00f0 +#define BRCMU_CHSPEC_CHH_SHIFT 4 + +/* bit 8~16 for dot 11n IO types + * bit 8~9 sideband + * bit 10~11 bandwidth + * bit 12~13 spectral band + * bit 14~15 not used + */ +#define BRCMU_CHSPEC_D11N_SB_MASK 0x0300 +#define BRCMU_CHSPEC_D11N_SB_SHIFT 8 +#define BRCMU_CHSPEC_D11N_SB_L 0x0100 /* control lower */ +#define BRCMU_CHSPEC_D11N_SB_U 0x0200 /* control upper */ +#define BRCMU_CHSPEC_D11N_SB_N 0x0300 /* none */ +#define BRCMU_CHSPEC_D11N_BW_MASK 0x0c00 +#define BRCMU_CHSPEC_D11N_BW_SHIFT 10 +#define BRCMU_CHSPEC_D11N_BW_10 0x0400 +#define BRCMU_CHSPEC_D11N_BW_20 0x0800 +#define BRCMU_CHSPEC_D11N_BW_40 0x0c00 +#define BRCMU_CHSPEC_D11N_BND_MASK 0x3000 +#define BRCMU_CHSPEC_D11N_BND_SHIFT 12 +#define BRCMU_CHSPEC_D11N_BND_5G 0x1000 +#define BRCMU_CHSPEC_D11N_BND_2G 0x2000 + +/* bit 8~16 for dot 11ac IO types + * bit 8~10 sideband + * bit 11~13 bandwidth + * bit 14~15 spectral band + */ +#define BRCMU_CHSPEC_D11AC_SB_MASK 0x0700 +#define BRCMU_CHSPEC_D11AC_SB_SHIFT 8 +#define BRCMU_CHSPEC_D11AC_SB_LLL 0x0000 +#define BRCMU_CHSPEC_D11AC_SB_LLU 0x0100 +#define BRCMU_CHSPEC_D11AC_SB_LUL 0x0200 +#define BRCMU_CHSPEC_D11AC_SB_LUU 0x0300 +#define BRCMU_CHSPEC_D11AC_SB_ULL 0x0400 +#define BRCMU_CHSPEC_D11AC_SB_ULU 0x0500 +#define BRCMU_CHSPEC_D11AC_SB_UUL 0x0600 +#define BRCMU_CHSPEC_D11AC_SB_UUU 0x0700 +#define BRCMU_CHSPEC_D11AC_SB_LL BRCMU_CHSPEC_D11AC_SB_LLL +#define BRCMU_CHSPEC_D11AC_SB_LU BRCMU_CHSPEC_D11AC_SB_LLU +#define BRCMU_CHSPEC_D11AC_SB_UL BRCMU_CHSPEC_D11AC_SB_LUL +#define BRCMU_CHSPEC_D11AC_SB_UU BRCMU_CHSPEC_D11AC_SB_LUU +#define BRCMU_CHSPEC_D11AC_SB_L BRCMU_CHSPEC_D11AC_SB_LLL +#define BRCMU_CHSPEC_D11AC_SB_U BRCMU_CHSPEC_D11AC_SB_LLU +#define BRCMU_CHSPEC_D11AC_BW_MASK 0x3800 +#define BRCMU_CHSPEC_D11AC_BW_SHIFT 11 +#define BRCMU_CHSPEC_D11AC_BW_5 0x0000 +#define BRCMU_CHSPEC_D11AC_BW_10 0x0800 +#define BRCMU_CHSPEC_D11AC_BW_20 0x1000 +#define BRCMU_CHSPEC_D11AC_BW_40 0x1800 +#define BRCMU_CHSPEC_D11AC_BW_80 0x2000 +#define BRCMU_CHSPEC_D11AC_BW_160 0x2800 +#define BRCMU_CHSPEC_D11AC_BW_8080 0x3000 +#define BRCMU_CHSPEC_D11AC_BND_MASK 0xc000 +#define BRCMU_CHSPEC_D11AC_BND_SHIFT 14 +#define BRCMU_CHSPEC_D11AC_BND_2G 0x0000 +#define BRCMU_CHSPEC_D11AC_BND_3G 0x4000 +#define BRCMU_CHSPEC_D11AC_BND_4G 0x8000 +#define BRCMU_CHSPEC_D11AC_BND_5G 0xc000 + +#define BRCMU_CHAN_BAND_2G 0 +#define BRCMU_CHAN_BAND_5G 1 + +enum brcmu_chan_bw { + BRCMU_CHAN_BW_20, + BRCMU_CHAN_BW_40, + BRCMU_CHAN_BW_80, + BRCMU_CHAN_BW_80P80, + BRCMU_CHAN_BW_160, +}; + +enum brcmu_chan_sb { + BRCMU_CHAN_SB_NONE = -1, + BRCMU_CHAN_SB_LLL, + BRCMU_CHAN_SB_LLU, + BRCMU_CHAN_SB_LUL, + BRCMU_CHAN_SB_LUU, + BRCMU_CHAN_SB_ULL, + BRCMU_CHAN_SB_ULU, + BRCMU_CHAN_SB_UUL, + BRCMU_CHAN_SB_UUU, + BRCMU_CHAN_SB_L = BRCMU_CHAN_SB_LLL, + BRCMU_CHAN_SB_U = BRCMU_CHAN_SB_LLU, + BRCMU_CHAN_SB_LL = BRCMU_CHAN_SB_LLL, + BRCMU_CHAN_SB_LU = BRCMU_CHAN_SB_LLU, + BRCMU_CHAN_SB_UL = BRCMU_CHAN_SB_LUL, + BRCMU_CHAN_SB_UU = BRCMU_CHAN_SB_LUU, +}; + +struct brcmu_chan { + u16 chspec; + u8 chnum; + u8 band; + enum brcmu_chan_bw bw; + enum brcmu_chan_sb sb; +}; + +struct brcmu_d11inf { + u8 io_type; + + void (*encchspec)(struct brcmu_chan *ch); + void (*decchspec)(struct brcmu_chan *ch); +}; + +void brcmu_d11_attach(struct brcmu_d11inf *d11inf); + +#endif /* _BRCMU_CHANNELS_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h new file mode 100644 index 000000000000..41969527b459 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2010 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 _BRCMU_UTILS_H_ +#define _BRCMU_UTILS_H_ + +#include + +/* + * Spin at most 'us' microseconds while 'exp' is true. + * Caller should explicitly test 'exp' when this completes + * and take appropriate error action if 'exp' is still true. + */ +#define SPINWAIT(exp, us) { \ + uint countdown = (us) + 9; \ + while ((exp) && (countdown >= 10)) {\ + udelay(10); \ + countdown -= 10; \ + } \ +} + +/* osl multi-precedence packet queue */ +#define PKTQ_LEN_DEFAULT 128 /* Max 128 packets */ +#define PKTQ_MAX_PREC 16 /* Maximum precedence levels */ + +#define BCME_STRLEN 64 /* Max string length for BCM errors */ + +/* the largest reasonable packet buffer driver uses for ethernet MTU in bytes */ +#define PKTBUFSZ 2048 + +#ifndef setbit +#ifndef NBBY /* the BSD family defines NBBY */ +#define NBBY 8 /* 8 bits per byte */ +#endif /* #ifndef NBBY */ +#define setbit(a, i) (((u8 *)a)[(i)/NBBY] |= 1<<((i)%NBBY)) +#define clrbit(a, i) (((u8 *)a)[(i)/NBBY] &= ~(1<<((i)%NBBY))) +#define isset(a, i) (((const u8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) +#define isclr(a, i) ((((const u8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0) +#endif /* setbit */ + +#define NBITS(type) (sizeof(type) * 8) +#define NBITVAL(nbits) (1 << (nbits)) +#define MAXBITVAL(nbits) ((1 << (nbits)) - 1) +#define NBITMASK(nbits) MAXBITVAL(nbits) +#define MAXNBVAL(nbyte) MAXBITVAL((nbyte) * 8) + +/* crc defines */ +#define CRC16_INIT_VALUE 0xffff /* Initial CRC16 checksum value */ +#define CRC16_GOOD_VALUE 0xf0b8 /* Good final CRC16 checksum value */ + +/* 18-bytes of Ethernet address buffer length */ +#define ETHER_ADDR_STR_LEN 18 + +struct pktq_prec { + struct sk_buff_head skblist; + u16 max; /* maximum number of queued packets */ +}; + +/* multi-priority pkt queue */ +struct pktq { + u16 num_prec; /* number of precedences in use */ + u16 hi_prec; /* rapid dequeue hint (>= highest non-empty prec) */ + u16 max; /* total max packets */ + u16 len; /* total number of packets */ + /* + * q array must be last since # of elements can be either + * PKTQ_MAX_PREC or 1 + */ + struct pktq_prec q[PKTQ_MAX_PREC]; +}; + +/* operations on a specific precedence in packet queue */ + +static inline int pktq_plen(struct pktq *pq, int prec) +{ + return pq->q[prec].skblist.qlen; +} + +static inline int pktq_pavail(struct pktq *pq, int prec) +{ + return pq->q[prec].max - pq->q[prec].skblist.qlen; +} + +static inline bool pktq_pfull(struct pktq *pq, int prec) +{ + return pq->q[prec].skblist.qlen >= pq->q[prec].max; +} + +static inline bool pktq_pempty(struct pktq *pq, int prec) +{ + return skb_queue_empty(&pq->q[prec].skblist); +} + +static inline struct sk_buff *pktq_ppeek(struct pktq *pq, int prec) +{ + return skb_peek(&pq->q[prec].skblist); +} + +static inline struct sk_buff *pktq_ppeek_tail(struct pktq *pq, int prec) +{ + return skb_peek_tail(&pq->q[prec].skblist); +} + +struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, struct sk_buff *p); +struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, + struct sk_buff *p); +struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec); +struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec); +struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec, + bool (*match_fn)(struct sk_buff *p, + void *arg), + void *arg); + +/* packet primitives */ +struct sk_buff *brcmu_pkt_buf_get_skb(uint len); +void brcmu_pkt_buf_free_skb(struct sk_buff *skb); + +/* Empty the queue at particular precedence level */ +/* callback function fn(pkt, arg) returns true if pkt belongs to if */ +void brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir, + bool (*fn)(struct sk_buff *, void *), void *arg); + +/* operations on a set of precedences in packet queue */ + +int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp); +struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out); + +/* operations on packet queue as a whole */ + +static inline int pktq_len(struct pktq *pq) +{ + return (int)pq->len; +} + +static inline int pktq_max(struct pktq *pq) +{ + return (int)pq->max; +} + +static inline int pktq_avail(struct pktq *pq) +{ + return (int)(pq->max - pq->len); +} + +static inline bool pktq_full(struct pktq *pq) +{ + return pq->len >= pq->max; +} + +static inline bool pktq_empty(struct pktq *pq) +{ + return pq->len == 0; +} + +void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len); +/* prec_out may be NULL if caller is not interested in return value */ +struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out); +void brcmu_pktq_flush(struct pktq *pq, bool dir, + bool (*fn)(struct sk_buff *, void *), void *arg); + +/* externs */ +/* ip address */ +struct ipv4_addr; + +/* + * bitfield macros using masking and shift + * + * remark: the mask parameter should be a shifted mask. + */ +static inline void brcmu_maskset32(u32 *var, u32 mask, u8 shift, u32 value) +{ + value = (value << shift) & mask; + *var = (*var & ~mask) | value; +} +static inline u32 brcmu_maskget32(u32 var, u32 mask, u8 shift) +{ + return (var & mask) >> shift; +} +static inline void brcmu_maskset16(u16 *var, u16 mask, u8 shift, u16 value) +{ + value = (value << shift) & mask; + *var = (*var & ~mask) | value; +} +static inline u16 brcmu_maskget16(u16 var, u16 mask, u8 shift) +{ + return (var & mask) >> shift; +} + +/* externs */ +/* format/print */ +#ifdef DEBUG +void brcmu_prpkt(const char *msg, struct sk_buff *p0); +#else +#define brcmu_prpkt(a, b) +#endif /* DEBUG */ + +#ifdef DEBUG +__printf(3, 4) +void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...); +#else +__printf(3, 4) +static inline +void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) +{ +} +#endif + +#define BRCMU_BOARDREV_LEN 8 +#define BRCMU_DOTREV_LEN 16 + +char *brcmu_boardrev_str(u32 brev, char *buf); +char *brcmu_dotrev_str(u32 dotrev, char *buf); + +#endif /* _BRCMU_UTILS_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h new file mode 100644 index 000000000000..76b5d3a86294 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2010 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 _BRCMU_WIFI_H_ +#define _BRCMU_WIFI_H_ + +#include /* for ETH_ALEN */ +#include /* for WLAN_PMKID_LEN */ + +/* + * A chanspec (u16) holds the channel number, band, bandwidth and control + * sideband + */ + +/* channel defines */ +#define CH_UPPER_SB 0x01 +#define CH_LOWER_SB 0x02 +#define CH_EWA_VALID 0x04 +#define CH_30MHZ_APART 6 +#define CH_20MHZ_APART 4 +#define CH_10MHZ_APART 2 +#define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */ +#define CH_MIN_2G_CHANNEL 1 +#define CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */ +#define CH_MIN_5G_CHANNEL 34 + +/* bandstate array indices */ +#define BAND_2G_INDEX 0 /* wlc->bandstate[x] index */ +#define BAND_5G_INDEX 1 /* wlc->bandstate[x] index */ + +/* + * max # supported channels. The max channel no is 216, this is that + 1 + * rounded up to a multiple of NBBY (8). DO NOT MAKE it > 255: channels are + * u8's all over +*/ +#define MAXCHANNEL 224 + +#define WL_CHANSPEC_CHAN_MASK 0x00ff +#define WL_CHANSPEC_CHAN_SHIFT 0 + +#define WL_CHANSPEC_CTL_SB_MASK 0x0300 +#define WL_CHANSPEC_CTL_SB_SHIFT 8 +#define WL_CHANSPEC_CTL_SB_LOWER 0x0100 +#define WL_CHANSPEC_CTL_SB_UPPER 0x0200 +#define WL_CHANSPEC_CTL_SB_NONE 0x0300 + +#define WL_CHANSPEC_BW_MASK 0x0C00 +#define WL_CHANSPEC_BW_SHIFT 10 +#define WL_CHANSPEC_BW_10 0x0400 +#define WL_CHANSPEC_BW_20 0x0800 +#define WL_CHANSPEC_BW_40 0x0C00 +#define WL_CHANSPEC_BW_80 0x2000 + +#define WL_CHANSPEC_BAND_MASK 0xf000 +#define WL_CHANSPEC_BAND_SHIFT 12 +#define WL_CHANSPEC_BAND_5G 0x1000 +#define WL_CHANSPEC_BAND_2G 0x2000 +#define INVCHANSPEC 255 + +#define WL_CHAN_VALID_HW (1 << 0) /* valid with current HW */ +#define WL_CHAN_VALID_SW (1 << 1) /* valid with country sett. */ +#define WL_CHAN_BAND_5G (1 << 2) /* 5GHz-band channel */ +#define WL_CHAN_RADAR (1 << 3) /* radar sensitive channel */ +#define WL_CHAN_INACTIVE (1 << 4) /* inactive due to radar */ +#define WL_CHAN_PASSIVE (1 << 5) /* channel in passive mode */ +#define WL_CHAN_RESTRICTED (1 << 6) /* restricted use channel */ + +/* values for band specific 40MHz capabilities */ +#define WLC_N_BW_20ALL 0 +#define WLC_N_BW_40ALL 1 +#define WLC_N_BW_20IN2G_40IN5G 2 + +#define WLC_BW_20MHZ_BIT BIT(0) +#define WLC_BW_40MHZ_BIT BIT(1) +#define WLC_BW_80MHZ_BIT BIT(2) +#define WLC_BW_160MHZ_BIT BIT(3) + +/* Bandwidth capabilities */ +#define WLC_BW_CAP_20MHZ (WLC_BW_20MHZ_BIT) +#define WLC_BW_CAP_40MHZ (WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT) +#define WLC_BW_CAP_80MHZ (WLC_BW_80MHZ_BIT|WLC_BW_40MHZ_BIT| \ + WLC_BW_20MHZ_BIT) +#define WLC_BW_CAP_160MHZ (WLC_BW_160MHZ_BIT|WLC_BW_80MHZ_BIT| \ + WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT) +#define WLC_BW_CAP_UNRESTRICTED 0xFF + +/* band types */ +#define WLC_BAND_AUTO 0 /* auto-select */ +#define WLC_BAND_5G 1 /* 5 Ghz */ +#define WLC_BAND_2G 2 /* 2.4 Ghz */ +#define WLC_BAND_ALL 3 /* all bands */ + +#define CHSPEC_CHANNEL(chspec) ((u8)((chspec) & WL_CHANSPEC_CHAN_MASK)) +#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK) + +#define CHSPEC_CTL_SB(chspec) ((chspec) & WL_CHANSPEC_CTL_SB_MASK) +#define CHSPEC_BW(chspec) ((chspec) & WL_CHANSPEC_BW_MASK) + +#define CHSPEC_IS10(chspec) \ + (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10) + +#define CHSPEC_IS20(chspec) \ + (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20) + +#define CHSPEC_IS40(chspec) \ + (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40) + +#define CHSPEC_IS80(chspec) \ + (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_80) + +#define CHSPEC_IS5G(chspec) \ + (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G) + +#define CHSPEC_IS2G(chspec) \ + (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G) + +#define CHSPEC_SB_NONE(chspec) \ + (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE) + +#define CHSPEC_SB_UPPER(chspec) \ + (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER) + +#define CHSPEC_SB_LOWER(chspec) \ + (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER) + +#define CHSPEC_CTL_CHAN(chspec) \ + ((CHSPEC_SB_LOWER(chspec)) ? \ + (lower_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \ + (upper_20_sb(((chspec) & WL_CHANSPEC_CHAN_MASK)))) + +#define CHSPEC2BAND(chspec) (CHSPEC_IS5G(chspec) ? BRCM_BAND_5G : BRCM_BAND_2G) + +#define CHANSPEC_STR_LEN 8 + +static inline int lower_20_sb(int channel) +{ + return channel > CH_10MHZ_APART ? (channel - CH_10MHZ_APART) : 0; +} + +static inline int upper_20_sb(int channel) +{ + return (channel < (MAXCHANNEL - CH_10MHZ_APART)) ? + channel + CH_10MHZ_APART : 0; +} + +static inline int chspec_bandunit(u16 chspec) +{ + return CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX; +} + +static inline u16 ch20mhz_chspec(int channel) +{ + u16 rc = channel <= CH_MAX_2G_CHANNEL ? + WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G; + + return (u16)((u16)channel | WL_CHANSPEC_BW_20 | + WL_CHANSPEC_CTL_SB_NONE | rc); +} + +static inline int next_20mhz_chan(int channel) +{ + return channel < (MAXCHANNEL - CH_20MHZ_APART) ? + channel + CH_20MHZ_APART : 0; +} + +/* defined rate in 500kbps */ +#define BRCM_MAXRATE 108 /* in 500kbps units */ +#define BRCM_RATE_1M 2 /* in 500kbps units */ +#define BRCM_RATE_2M 4 /* in 500kbps units */ +#define BRCM_RATE_5M5 11 /* in 500kbps units */ +#define BRCM_RATE_11M 22 /* in 500kbps units */ +#define BRCM_RATE_6M 12 /* in 500kbps units */ +#define BRCM_RATE_9M 18 /* in 500kbps units */ +#define BRCM_RATE_12M 24 /* in 500kbps units */ +#define BRCM_RATE_18M 36 /* in 500kbps units */ +#define BRCM_RATE_24M 48 /* in 500kbps units */ +#define BRCM_RATE_36M 72 /* in 500kbps units */ +#define BRCM_RATE_48M 96 /* in 500kbps units */ +#define BRCM_RATE_54M 108 /* in 500kbps units */ + +#define BRCM_2G_25MHZ_OFFSET 5 /* 2.4GHz band channel offset */ + +#define MCSSET_LEN 16 + +static inline bool ac_bitmap_tst(u8 bitmap, int prec) +{ + return (bitmap & (1 << (prec))) != 0; +} + +/* Enumerate crypto algorithms */ +#define CRYPTO_ALGO_OFF 0 +#define CRYPTO_ALGO_WEP1 1 +#define CRYPTO_ALGO_TKIP 2 +#define CRYPTO_ALGO_WEP128 3 +#define CRYPTO_ALGO_AES_CCM 4 +#define CRYPTO_ALGO_AES_RESERVED1 5 +#define CRYPTO_ALGO_AES_RESERVED2 6 +#define CRYPTO_ALGO_NALG 7 + +/* wireless security bitvec */ + +#define WEP_ENABLED 0x0001 +#define TKIP_ENABLED 0x0002 +#define AES_ENABLED 0x0004 +#define WSEC_SWFLAG 0x0008 +/* to go into transition mode without setting wep */ +#define SES_OW_ENABLED 0x0040 +/* MFP */ +#define MFP_CAPABLE 0x0200 +#define MFP_REQUIRED 0x0400 + +/* WPA authentication mode bitvec */ +#define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */ +#define WPA_AUTH_NONE 0x0001 /* none (IBSS) */ +#define WPA_AUTH_UNSPECIFIED 0x0002 /* over 802.1x */ +#define WPA_AUTH_PSK 0x0004 /* Pre-shared key */ +#define WPA_AUTH_RESERVED1 0x0008 +#define WPA_AUTH_RESERVED2 0x0010 + +#define WPA2_AUTH_RESERVED1 0x0020 +#define WPA2_AUTH_UNSPECIFIED 0x0040 /* over 802.1x */ +#define WPA2_AUTH_PSK 0x0080 /* Pre-shared key */ +#define WPA2_AUTH_RESERVED3 0x0200 +#define WPA2_AUTH_RESERVED4 0x0400 +#define WPA2_AUTH_RESERVED5 0x0800 + +/* pmkid */ +#define MAXPMKID 16 + +#define DOT11_DEFAULT_RTS_LEN 2347 +#define DOT11_DEFAULT_FRAG_LEN 2346 + +#define DOT11_ICV_AES_LEN 8 +#define DOT11_QOS_LEN 2 +#define DOT11_IV_MAX_LEN 8 +#define DOT11_A4_HDR_LEN 30 + +#define HT_CAP_RX_STBC_NO 0x0 +#define HT_CAP_RX_STBC_ONE_STREAM 0x1 + +struct pmkid { + u8 BSSID[ETH_ALEN]; + u8 PMKID[WLAN_PMKID_LEN]; +}; + +struct pmkid_list { + __le32 npmkid; + struct pmkid pmkid[1]; +}; + +struct pmkid_cand { + u8 BSSID[ETH_ALEN]; + u8 preauth; +}; + +struct pmkid_cand_list { + u32 npmkid_cand; + struct pmkid_cand pmkid_cand[1]; +}; + +#endif /* _BRCMU_WIFI_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h new file mode 100644 index 000000000000..e1fd499930a0 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2010 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 _SBCHIPC_H +#define _SBCHIPC_H + +#include "defs.h" /* for PAD macro */ + +#define CHIPCREGOFFS(field) offsetof(struct chipcregs, field) + +struct chipcregs { + u32 chipid; /* 0x0 */ + u32 capabilities; + u32 corecontrol; /* corerev >= 1 */ + u32 bist; + + /* OTP */ + u32 otpstatus; /* 0x10, corerev >= 10 */ + u32 otpcontrol; + u32 otpprog; + u32 otplayout; /* corerev >= 23 */ + + /* Interrupt control */ + u32 intstatus; /* 0x20 */ + u32 intmask; + + /* Chip specific regs */ + u32 chipcontrol; /* 0x28, rev >= 11 */ + u32 chipstatus; /* 0x2c, rev >= 11 */ + + /* Jtag Master */ + u32 jtagcmd; /* 0x30, rev >= 10 */ + u32 jtagir; + u32 jtagdr; + u32 jtagctrl; + + /* serial flash interface registers */ + u32 flashcontrol; /* 0x40 */ + u32 flashaddress; + u32 flashdata; + u32 PAD[1]; + + /* Silicon backplane configuration broadcast control */ + u32 broadcastaddress; /* 0x50 */ + u32 broadcastdata; + + /* gpio - cleared only by power-on-reset */ + u32 gpiopullup; /* 0x58, corerev >= 20 */ + u32 gpiopulldown; /* 0x5c, corerev >= 20 */ + u32 gpioin; /* 0x60 */ + u32 gpioout; /* 0x64 */ + u32 gpioouten; /* 0x68 */ + u32 gpiocontrol; /* 0x6C */ + u32 gpiointpolarity; /* 0x70 */ + u32 gpiointmask; /* 0x74 */ + + /* GPIO events corerev >= 11 */ + u32 gpioevent; + u32 gpioeventintmask; + + /* Watchdog timer */ + u32 watchdog; /* 0x80 */ + + /* GPIO events corerev >= 11 */ + u32 gpioeventintpolarity; + + /* GPIO based LED powersave registers corerev >= 16 */ + u32 gpiotimerval; /* 0x88 */ + u32 gpiotimeroutmask; + + /* clock control */ + u32 clockcontrol_n; /* 0x90 */ + u32 clockcontrol_sb; /* aka m0 */ + u32 clockcontrol_pci; /* aka m1 */ + u32 clockcontrol_m2; /* mii/uart/mipsref */ + u32 clockcontrol_m3; /* cpu */ + u32 clkdiv; /* corerev >= 3 */ + u32 gpiodebugsel; /* corerev >= 28 */ + u32 capabilities_ext; /* 0xac */ + + /* pll delay registers (corerev >= 4) */ + u32 pll_on_delay; /* 0xb0 */ + u32 fref_sel_delay; + u32 slow_clk_ctl; /* 5 < corerev < 10 */ + u32 PAD; + + /* Instaclock registers (corerev >= 10) */ + u32 system_clk_ctl; /* 0xc0 */ + u32 clkstatestretch; + u32 PAD[2]; + + /* Indirect backplane access (corerev >= 22) */ + u32 bp_addrlow; /* 0xd0 */ + u32 bp_addrhigh; + u32 bp_data; + u32 PAD; + u32 bp_indaccess; + u32 PAD[3]; + + /* More clock dividers (corerev >= 32) */ + u32 clkdiv2; + u32 PAD[2]; + + /* In AI chips, pointer to erom */ + u32 eromptr; /* 0xfc */ + + /* ExtBus control registers (corerev >= 3) */ + u32 pcmcia_config; /* 0x100 */ + u32 pcmcia_memwait; + u32 pcmcia_attrwait; + u32 pcmcia_iowait; + u32 ide_config; + u32 ide_memwait; + u32 ide_attrwait; + u32 ide_iowait; + u32 prog_config; + u32 prog_waitcount; + u32 flash_config; + u32 flash_waitcount; + u32 SECI_config; /* 0x130 SECI configuration */ + u32 PAD[3]; + + /* Enhanced Coexistence Interface (ECI) registers (corerev >= 21) */ + u32 eci_output; /* 0x140 */ + u32 eci_control; + u32 eci_inputlo; + u32 eci_inputmi; + u32 eci_inputhi; + u32 eci_inputintpolaritylo; + u32 eci_inputintpolaritymi; + u32 eci_inputintpolarityhi; + u32 eci_intmasklo; + u32 eci_intmaskmi; + u32 eci_intmaskhi; + u32 eci_eventlo; + u32 eci_eventmi; + u32 eci_eventhi; + u32 eci_eventmasklo; + u32 eci_eventmaskmi; + u32 eci_eventmaskhi; + u32 PAD[3]; + + /* SROM interface (corerev >= 32) */ + u32 sromcontrol; /* 0x190 */ + u32 sromaddress; + u32 sromdata; + u32 PAD[17]; + + /* Clock control and hardware workarounds (corerev >= 20) */ + u32 clk_ctl_st; /* 0x1e0 */ + u32 hw_war; + u32 PAD[70]; + + /* UARTs */ + u8 uart0data; /* 0x300 */ + u8 uart0imr; + u8 uart0fcr; + u8 uart0lcr; + u8 uart0mcr; + u8 uart0lsr; + u8 uart0msr; + u8 uart0scratch; + u8 PAD[248]; /* corerev >= 1 */ + + u8 uart1data; /* 0x400 */ + u8 uart1imr; + u8 uart1fcr; + u8 uart1lcr; + u8 uart1mcr; + u8 uart1lsr; + u8 uart1msr; + u8 uart1scratch; + u32 PAD[62]; + + /* save/restore, corerev >= 48 */ + u32 sr_capability; /* 0x500 */ + u32 sr_control0; /* 0x504 */ + u32 sr_control1; /* 0x508 */ + u32 gpio_control; /* 0x50C */ + u32 PAD[60]; + + /* PMU registers (corerev >= 20) */ + u32 pmucontrol; /* 0x600 */ + u32 pmucapabilities; + u32 pmustatus; + u32 res_state; + u32 res_pending; + u32 pmutimer; + u32 min_res_mask; + u32 max_res_mask; + u32 res_table_sel; + u32 res_dep_mask; + u32 res_updn_timer; + u32 res_timer; + u32 clkstretch; + u32 pmuwatchdog; + u32 gpiosel; /* 0x638, rev >= 1 */ + u32 gpioenable; /* 0x63c, rev >= 1 */ + u32 res_req_timer_sel; + u32 res_req_timer; + u32 res_req_mask; + u32 pmucapabilities_ext; /* 0x64c, pmurev >=15 */ + u32 chipcontrol_addr; /* 0x650 */ + u32 chipcontrol_data; /* 0x654 */ + u32 regcontrol_addr; + u32 regcontrol_data; + u32 pllcontrol_addr; + u32 pllcontrol_data; + u32 pmustrapopt; /* 0x668, corerev >= 28 */ + u32 pmu_xtalfreq; /* 0x66C, pmurev >= 10 */ + u32 retention_ctl; /* 0x670, pmurev >= 15 */ + u32 PAD[3]; + u32 retention_grpidx; /* 0x680 */ + u32 retention_grpctl; /* 0x684 */ + u32 PAD[94]; + u16 sromotp[768]; +}; + +/* chipid */ +#define CID_ID_MASK 0x0000ffff /* Chip Id mask */ +#define CID_REV_MASK 0x000f0000 /* Chip Revision mask */ +#define CID_REV_SHIFT 16 /* Chip Revision shift */ +#define CID_PKG_MASK 0x00f00000 /* Package Option mask */ +#define CID_PKG_SHIFT 20 /* Package Option shift */ +#define CID_CC_MASK 0x0f000000 /* CoreCount (corerev >= 4) */ +#define CID_CC_SHIFT 24 +#define CID_TYPE_MASK 0xf0000000 /* Chip Type */ +#define CID_TYPE_SHIFT 28 + +/* capabilities */ +#define CC_CAP_UARTS_MASK 0x00000003 /* Number of UARTs */ +#define CC_CAP_MIPSEB 0x00000004 /* MIPS is in big-endian mode */ +#define CC_CAP_UCLKSEL 0x00000018 /* UARTs clock select */ +/* UARTs are driven by internal divided clock */ +#define CC_CAP_UINTCLK 0x00000008 +#define CC_CAP_UARTGPIO 0x00000020 /* UARTs own GPIOs 15:12 */ +#define CC_CAP_EXTBUS_MASK 0x000000c0 /* External bus mask */ +#define CC_CAP_EXTBUS_NONE 0x00000000 /* No ExtBus present */ +#define CC_CAP_EXTBUS_FULL 0x00000040 /* ExtBus: PCMCIA, IDE & Prog */ +#define CC_CAP_EXTBUS_PROG 0x00000080 /* ExtBus: ProgIf only */ +#define CC_CAP_FLASH_MASK 0x00000700 /* Type of flash */ +#define CC_CAP_PLL_MASK 0x00038000 /* Type of PLL */ +#define CC_CAP_PWR_CTL 0x00040000 /* Power control */ +#define CC_CAP_OTPSIZE 0x00380000 /* OTP Size (0 = none) */ +#define CC_CAP_OTPSIZE_SHIFT 19 /* OTP Size shift */ +#define CC_CAP_OTPSIZE_BASE 5 /* OTP Size base */ +#define CC_CAP_JTAGP 0x00400000 /* JTAG Master Present */ +#define CC_CAP_ROM 0x00800000 /* Internal boot rom active */ +#define CC_CAP_BKPLN64 0x08000000 /* 64-bit backplane */ +#define CC_CAP_PMU 0x10000000 /* PMU Present, rev >= 20 */ +#define CC_CAP_SROM 0x40000000 /* Srom Present, rev >= 32 */ +/* Nand flash present, rev >= 35 */ +#define CC_CAP_NFLASH 0x80000000 + +#define CC_CAP2_SECI 0x00000001 /* SECI Present, rev >= 36 */ +/* GSIO (spi/i2c) present, rev >= 37 */ +#define CC_CAP2_GSIO 0x00000002 + +/* pmucapabilities */ +#define PCAP_REV_MASK 0x000000ff +#define PCAP_RC_MASK 0x00001f00 +#define PCAP_RC_SHIFT 8 +#define PCAP_TC_MASK 0x0001e000 +#define PCAP_TC_SHIFT 13 +#define PCAP_PC_MASK 0x001e0000 +#define PCAP_PC_SHIFT 17 +#define PCAP_VC_MASK 0x01e00000 +#define PCAP_VC_SHIFT 21 +#define PCAP_CC_MASK 0x1e000000 +#define PCAP_CC_SHIFT 25 +#define PCAP5_PC_MASK 0x003e0000 /* PMU corerev >= 5 */ +#define PCAP5_PC_SHIFT 17 +#define PCAP5_VC_MASK 0x07c00000 +#define PCAP5_VC_SHIFT 22 +#define PCAP5_CC_MASK 0xf8000000 +#define PCAP5_CC_SHIFT 27 +/* pmucapabilites_ext PMU rev >= 15 */ +#define PCAPEXT_SR_SUPPORTED_MASK (1 << 1) +/* retention_ctl PMU rev >= 15 */ +#define PMU_RCTL_MACPHY_DISABLE_MASK (1 << 26) +#define PMU_RCTL_LOGIC_DISABLE_MASK (1 << 27) + + +/* +* Maximum delay for the PMU state transition in us. +* This is an upper bound intended for spinwaits etc. +*/ +#define PMU_MAX_TRANSITION_DLY 15000 + +#endif /* _SBCHIPC_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/defs.h b/drivers/net/wireless/broadcom/brcm80211/include/defs.h new file mode 100644 index 000000000000..8d1e85e0ed51 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/include/defs.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010 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 _BRCM_DEFS_H_ +#define _BRCM_DEFS_H_ + +#include + +#define SI_BUS 0 +#define PCI_BUS 1 +#define PCMCIA_BUS 2 +#define SDIO_BUS 3 +#define JTAG_BUS 4 +#define USB_BUS 5 +#define SPI_BUS 6 + +#define OFF 0 +#define ON 1 /* ON = 1 */ +#define AUTO (-1) /* Auto = -1 */ + +/* + * Priority definitions according 802.1D + */ +#define PRIO_8021D_NONE 2 +#define PRIO_8021D_BK 1 +#define PRIO_8021D_BE 0 +#define PRIO_8021D_EE 3 +#define PRIO_8021D_CL 4 +#define PRIO_8021D_VI 5 +#define PRIO_8021D_VO 6 +#define PRIO_8021D_NC 7 + +#define MAXPRIO 7 +#define NUMPRIO (MAXPRIO + 1) + +#define WL_NUMRATES 16 /* max # of rates in a rateset */ + +#define BRCM_CNTRY_BUF_SZ 4 /* Country string is 3 bytes + NUL */ + +#define BRCM_SET_CHANNEL 30 +#define BRCM_SET_SRL 32 +#define BRCM_SET_LRL 34 +#define BRCM_SET_BCNPRD 76 + +#define BRCM_GET_CURR_RATESET 114 /* current rateset */ +#define BRCM_GET_PHYLIST 180 + +/* Bit masks for radio disabled status - returned by WL_GET_RADIO */ + +#define WL_RADIO_SW_DISABLE (1<<0) +#define WL_RADIO_HW_DISABLE (1<<1) +/* some countries don't support any channel */ +#define WL_RADIO_COUNTRY_DISABLE (1<<3) + +/* Override bit for SET_TXPWR. if set, ignore other level limits */ +#define WL_TXPWR_OVERRIDE (1U<<31) + +/* band types */ +#define BRCM_BAND_AUTO 0 /* auto-select */ +#define BRCM_BAND_5G 1 /* 5 Ghz */ +#define BRCM_BAND_2G 2 /* 2.4 Ghz */ +#define BRCM_BAND_ALL 3 /* all bands */ + +/* Debug levels */ +#define BRCM_DL_INFO 0x00000001 +#define BRCM_DL_MAC80211 0x00000002 +#define BRCM_DL_RX 0x00000004 +#define BRCM_DL_TX 0x00000008 +#define BRCM_DL_INT 0x00000010 +#define BRCM_DL_DMA 0x00000020 +#define BRCM_DL_HT 0x00000040 + +/* Values for PM */ +#define PM_OFF 0 +#define PM_MAX 1 +#define PM_FAST 2 + +/* + * Sonics Configuration Space Registers. + */ + +/* core sbconfig regs are top 256bytes of regs */ +#define SBCONFIGOFF 0xf00 + +/* cpp contortions to concatenate w/arg prescan */ +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif + +#endif /* _BRCM_DEFS_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/soc.h b/drivers/net/wireless/broadcom/brcm80211/include/soc.h new file mode 100644 index 000000000000..123cfa854a0d --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/include/soc.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2010 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 _BRCM_SOC_H +#define _BRCM_SOC_H + +#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */ + +/* Common core control flags */ +#define SICF_BIST_EN 0x8000 +#define SICF_PME_EN 0x4000 +#define SICF_CORE_BITS 0x3ffc +#define SICF_FGC 0x0002 +#define SICF_CLOCK_EN 0x0001 + +/* Common core status flags */ +#define SISF_BIST_DONE 0x8000 +#define SISF_BIST_ERROR 0x4000 +#define SISF_GATED_CLK 0x2000 +#define SISF_DMA64 0x1000 +#define SISF_CORE_BITS 0x0fff + +#endif /* _BRCM_SOC_H */ -- cgit v1.2.3-59-g8ed1b From 560424e9a979a7b460055bda497bb4522ba5cc87 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 20:09:02 +0200 Subject: cw1200: move under st vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 2 +- drivers/net/wireless/Makefile | 2 +- drivers/net/wireless/cw1200/Kconfig | 30 - drivers/net/wireless/cw1200/Makefile | 21 - drivers/net/wireless/cw1200/bh.c | 619 ------- drivers/net/wireless/cw1200/bh.h | 28 - drivers/net/wireless/cw1200/cw1200.h | 323 ---- drivers/net/wireless/cw1200/cw1200_sdio.c | 425 ----- drivers/net/wireless/cw1200/cw1200_spi.c | 476 ----- drivers/net/wireless/cw1200/debug.c | 430 ----- drivers/net/wireless/cw1200/debug.h | 93 - drivers/net/wireless/cw1200/fwio.c | 526 ------ drivers/net/wireless/cw1200/fwio.h | 39 - drivers/net/wireless/cw1200/hwbus.h | 33 - drivers/net/wireless/cw1200/hwio.c | 312 ---- drivers/net/wireless/cw1200/hwio.h | 247 --- drivers/net/wireless/cw1200/main.c | 601 ------- drivers/net/wireless/cw1200/pm.c | 367 ---- drivers/net/wireless/cw1200/pm.h | 43 - drivers/net/wireless/cw1200/queue.c | 581 ------- drivers/net/wireless/cw1200/queue.h | 116 -- drivers/net/wireless/cw1200/scan.c | 463 ----- drivers/net/wireless/cw1200/scan.h | 56 - drivers/net/wireless/cw1200/sta.c | 2399 -------------------------- drivers/net/wireless/cw1200/sta.h | 124 -- drivers/net/wireless/cw1200/txrx.c | 1472 ---------------- drivers/net/wireless/cw1200/txrx.h | 106 -- drivers/net/wireless/cw1200/wsm.c | 1822 ------------------- drivers/net/wireless/cw1200/wsm.h | 1870 -------------------- drivers/net/wireless/st/Kconfig | 16 + drivers/net/wireless/st/Makefile | 1 + drivers/net/wireless/st/cw1200/Kconfig | 30 + drivers/net/wireless/st/cw1200/Makefile | 21 + drivers/net/wireless/st/cw1200/bh.c | 619 +++++++ drivers/net/wireless/st/cw1200/bh.h | 28 + drivers/net/wireless/st/cw1200/cw1200.h | 323 ++++ drivers/net/wireless/st/cw1200/cw1200_sdio.c | 425 +++++ drivers/net/wireless/st/cw1200/cw1200_spi.c | 476 +++++ drivers/net/wireless/st/cw1200/debug.c | 430 +++++ drivers/net/wireless/st/cw1200/debug.h | 93 + drivers/net/wireless/st/cw1200/fwio.c | 526 ++++++ drivers/net/wireless/st/cw1200/fwio.h | 39 + drivers/net/wireless/st/cw1200/hwbus.h | 33 + drivers/net/wireless/st/cw1200/hwio.c | 312 ++++ drivers/net/wireless/st/cw1200/hwio.h | 247 +++ drivers/net/wireless/st/cw1200/main.c | 601 +++++++ drivers/net/wireless/st/cw1200/pm.c | 367 ++++ drivers/net/wireless/st/cw1200/pm.h | 43 + drivers/net/wireless/st/cw1200/queue.c | 581 +++++++ drivers/net/wireless/st/cw1200/queue.h | 116 ++ drivers/net/wireless/st/cw1200/scan.c | 463 +++++ drivers/net/wireless/st/cw1200/scan.h | 56 + drivers/net/wireless/st/cw1200/sta.c | 2399 ++++++++++++++++++++++++++ drivers/net/wireless/st/cw1200/sta.h | 124 ++ drivers/net/wireless/st/cw1200/txrx.c | 1472 ++++++++++++++++ drivers/net/wireless/st/cw1200/txrx.h | 106 ++ drivers/net/wireless/st/cw1200/wsm.c | 1822 +++++++++++++++++++ drivers/net/wireless/st/cw1200/wsm.h | 1870 ++++++++++++++++++++ 59 files changed, 13642 insertions(+), 13625 deletions(-) delete mode 100644 drivers/net/wireless/cw1200/Kconfig delete mode 100644 drivers/net/wireless/cw1200/Makefile delete mode 100644 drivers/net/wireless/cw1200/bh.c delete mode 100644 drivers/net/wireless/cw1200/bh.h delete mode 100644 drivers/net/wireless/cw1200/cw1200.h delete mode 100644 drivers/net/wireless/cw1200/cw1200_sdio.c delete mode 100644 drivers/net/wireless/cw1200/cw1200_spi.c delete mode 100644 drivers/net/wireless/cw1200/debug.c delete mode 100644 drivers/net/wireless/cw1200/debug.h delete mode 100644 drivers/net/wireless/cw1200/fwio.c delete mode 100644 drivers/net/wireless/cw1200/fwio.h delete mode 100644 drivers/net/wireless/cw1200/hwbus.h delete mode 100644 drivers/net/wireless/cw1200/hwio.c delete mode 100644 drivers/net/wireless/cw1200/hwio.h delete mode 100644 drivers/net/wireless/cw1200/main.c delete mode 100644 drivers/net/wireless/cw1200/pm.c delete mode 100644 drivers/net/wireless/cw1200/pm.h delete mode 100644 drivers/net/wireless/cw1200/queue.c delete mode 100644 drivers/net/wireless/cw1200/queue.h delete mode 100644 drivers/net/wireless/cw1200/scan.c delete mode 100644 drivers/net/wireless/cw1200/scan.h delete mode 100644 drivers/net/wireless/cw1200/sta.c delete mode 100644 drivers/net/wireless/cw1200/sta.h delete mode 100644 drivers/net/wireless/cw1200/txrx.c delete mode 100644 drivers/net/wireless/cw1200/txrx.h delete mode 100644 drivers/net/wireless/cw1200/wsm.c delete mode 100644 drivers/net/wireless/cw1200/wsm.h create mode 100644 drivers/net/wireless/st/Kconfig create mode 100644 drivers/net/wireless/st/Makefile create mode 100644 drivers/net/wireless/st/cw1200/Kconfig create mode 100644 drivers/net/wireless/st/cw1200/Makefile create mode 100644 drivers/net/wireless/st/cw1200/bh.c create mode 100644 drivers/net/wireless/st/cw1200/bh.h create mode 100644 drivers/net/wireless/st/cw1200/cw1200.h create mode 100644 drivers/net/wireless/st/cw1200/cw1200_sdio.c create mode 100644 drivers/net/wireless/st/cw1200/cw1200_spi.c create mode 100644 drivers/net/wireless/st/cw1200/debug.c create mode 100644 drivers/net/wireless/st/cw1200/debug.h create mode 100644 drivers/net/wireless/st/cw1200/fwio.c create mode 100644 drivers/net/wireless/st/cw1200/fwio.h create mode 100644 drivers/net/wireless/st/cw1200/hwbus.h create mode 100644 drivers/net/wireless/st/cw1200/hwio.c create mode 100644 drivers/net/wireless/st/cw1200/hwio.h create mode 100644 drivers/net/wireless/st/cw1200/main.c create mode 100644 drivers/net/wireless/st/cw1200/pm.c create mode 100644 drivers/net/wireless/st/cw1200/pm.h create mode 100644 drivers/net/wireless/st/cw1200/queue.c create mode 100644 drivers/net/wireless/st/cw1200/queue.h create mode 100644 drivers/net/wireless/st/cw1200/scan.c create mode 100644 drivers/net/wireless/st/cw1200/scan.h create mode 100644 drivers/net/wireless/st/cw1200/sta.c create mode 100644 drivers/net/wireless/st/cw1200/sta.h create mode 100644 drivers/net/wireless/st/cw1200/txrx.c create mode 100644 drivers/net/wireless/st/cw1200/txrx.h create mode 100644 drivers/net/wireless/st/cw1200/wsm.c create mode 100644 drivers/net/wireless/st/cw1200/wsm.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 748ee82f2ebe..4cbd4641e6fb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3100,7 +3100,7 @@ F: sound/pci/cs5535audio/ CW1200 WLAN driver M: Solomon Peachy S: Maintained -F: drivers/net/wireless/cw1200/ +F: drivers/net/wireless/st/cw1200/ CX18 VIDEO4LINUX DRIVER M: Andy Walls diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index c627d9e41ede..c1742f228fec 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -21,6 +21,7 @@ source "drivers/net/wireless/admtek/Kconfig" source "drivers/net/wireless/atmel/Kconfig" source "drivers/net/wireless/broadcom/Kconfig" source "drivers/net/wireless/cisco/Kconfig" +source "drivers/net/wireless/st/Kconfig" config PCMCIA_RAYCS tristate "Aviator/Raytheon 2.4GHz wireless support" @@ -173,7 +174,6 @@ source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig" source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig" source "drivers/net/wireless/mwifiex/Kconfig" -source "drivers/net/wireless/cw1200/Kconfig" source "drivers/net/wireless/rsi/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 41ebe5641c01..d7211a7949e6 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/ obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/ obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ +obj-$(CONFIG_WLAN_VENDOR_ST) += st/ obj-$(CONFIG_IPW2100) += ipw2x00/ obj-$(CONFIG_IPW2200) += ipw2x00/ @@ -47,5 +48,4 @@ obj-$(CONFIG_WL_TI) += ti/ obj-$(CONFIG_MWIFIEX) += mwifiex/ -obj-$(CONFIG_CW1200) += cw1200/ obj-$(CONFIG_RSI_91X) += rsi/ diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/cw1200/Kconfig deleted file mode 100644 index 0880742eab17..000000000000 --- a/drivers/net/wireless/cw1200/Kconfig +++ /dev/null @@ -1,30 +0,0 @@ -config CW1200 - tristate "CW1200 WLAN support" - depends on MAC80211 && CFG80211 - help - This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets. - This option just enables the driver core, see below for - specific bus support. - -if CW1200 - -config CW1200_WLAN_SDIO - tristate "Support SDIO platforms" - depends on CW1200 && MMC - help - Enable support for the CW1200 connected via an SDIO bus. - By default this driver only supports the Sagrad SG901-1091/1098 EVK - and similar designs that utilize a hardware reset circuit. To - support different CW1200 SDIO designs you will need to override - the default platform data by calling cw1200_sdio_set_platform_data() - in your board setup file. - -config CW1200_WLAN_SPI - tristate "Support SPI platforms" - depends on CW1200 && SPI - help - Enables support for the CW1200 connected via a SPI bus. You will - need to add appropriate platform data glue in your board setup - file. - -endif diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/cw1200/Makefile deleted file mode 100644 index b086aac6547a..000000000000 --- a/drivers/net/wireless/cw1200/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -cw1200_core-y := \ - fwio.o \ - txrx.o \ - main.o \ - queue.o \ - hwio.o \ - bh.o \ - wsm.o \ - sta.o \ - scan.o \ - debug.o -cw1200_core-$(CONFIG_PM) += pm.o - -# CFLAGS_sta.o += -DDEBUG - -cw1200_wlan_sdio-y := cw1200_sdio.o -cw1200_wlan_spi-y := cw1200_spi.o - -obj-$(CONFIG_CW1200) += cw1200_core.o -obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o -obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/cw1200/bh.c deleted file mode 100644 index 92d299aa257c..000000000000 --- a/drivers/net/wireless/cw1200/bh.c +++ /dev/null @@ -1,619 +0,0 @@ -/* - * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * Based on: - * ST-Ericsson UMAC CW1200 driver, which is - * Copyright (c) 2010, ST-Ericsson - * Author: Ajitpal Singh - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include "cw1200.h" -#include "bh.h" -#include "hwio.h" -#include "wsm.h" -#include "hwbus.h" -#include "debug.h" -#include "fwio.h" - -static int cw1200_bh(void *arg); - -#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) -/* an SPI message cannot be bigger than (2"12-1)*2 bytes - * "*2" to cvt to bytes - */ -#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2) -#define PIGGYBACK_CTRL_REG (2) -#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) - -/* Suspend state privates */ -enum cw1200_bh_pm_state { - CW1200_BH_RESUMED = 0, - CW1200_BH_SUSPEND, - CW1200_BH_SUSPENDED, - CW1200_BH_RESUME, -}; - -typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv, - u8 *data, size_t size); - -static void cw1200_bh_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, bh_work); - cw1200_bh(priv); -} - -int cw1200_register_bh(struct cw1200_common *priv) -{ - int err = 0; - /* Realtime workqueue */ - priv->bh_workqueue = alloc_workqueue("cw1200_bh", - WQ_MEM_RECLAIM | WQ_HIGHPRI - | WQ_CPU_INTENSIVE, 1); - - if (!priv->bh_workqueue) - return -ENOMEM; - - INIT_WORK(&priv->bh_work, cw1200_bh_work); - - pr_debug("[BH] register.\n"); - - atomic_set(&priv->bh_rx, 0); - atomic_set(&priv->bh_tx, 0); - atomic_set(&priv->bh_term, 0); - atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); - priv->bh_error = 0; - priv->hw_bufs_used = 0; - priv->buf_id_tx = 0; - priv->buf_id_rx = 0; - init_waitqueue_head(&priv->bh_wq); - init_waitqueue_head(&priv->bh_evt_wq); - - err = !queue_work(priv->bh_workqueue, &priv->bh_work); - WARN_ON(err); - return err; -} - -void cw1200_unregister_bh(struct cw1200_common *priv) -{ - atomic_add(1, &priv->bh_term); - wake_up(&priv->bh_wq); - - flush_workqueue(priv->bh_workqueue); - - destroy_workqueue(priv->bh_workqueue); - priv->bh_workqueue = NULL; - - pr_debug("[BH] unregistered.\n"); -} - -void cw1200_irq_handler(struct cw1200_common *priv) -{ - pr_debug("[BH] irq.\n"); - - /* Disable Interrupts! */ - /* NOTE: hwbus_ops->lock already held */ - __cw1200_irq_enable(priv, 0); - - if (/* WARN_ON */(priv->bh_error)) - return; - - if (atomic_add_return(1, &priv->bh_rx) == 1) - wake_up(&priv->bh_wq); -} -EXPORT_SYMBOL_GPL(cw1200_irq_handler); - -void cw1200_bh_wakeup(struct cw1200_common *priv) -{ - pr_debug("[BH] wakeup.\n"); - if (priv->bh_error) { - pr_err("[BH] wakeup failed (BH error)\n"); - return; - } - - if (atomic_add_return(1, &priv->bh_tx) == 1) - wake_up(&priv->bh_wq); -} - -int cw1200_bh_suspend(struct cw1200_common *priv) -{ - pr_debug("[BH] suspend.\n"); - if (priv->bh_error) { - wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n"); - return -EINVAL; - } - - atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND); - wake_up(&priv->bh_wq); - return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || - (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)), - 1 * HZ) ? 0 : -ETIMEDOUT; -} - -int cw1200_bh_resume(struct cw1200_common *priv) -{ - pr_debug("[BH] resume.\n"); - if (priv->bh_error) { - wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n"); - return -EINVAL; - } - - atomic_set(&priv->bh_suspend, CW1200_BH_RESUME); - wake_up(&priv->bh_wq); - return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || - (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)), - 1 * HZ) ? 0 : -ETIMEDOUT; -} - -static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv) -{ - ++priv->hw_bufs_used; -} - -int wsm_release_tx_buffer(struct cw1200_common *priv, int count) -{ - int ret = 0; - int hw_bufs_used = priv->hw_bufs_used; - - priv->hw_bufs_used -= count; - if (WARN_ON(priv->hw_bufs_used < 0)) - ret = -1; - else if (hw_bufs_used >= priv->wsm_caps.input_buffers) - ret = 1; - if (!priv->hw_bufs_used) - wake_up(&priv->bh_evt_wq); - return ret; -} - -static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv, - u16 *ctrl_reg) -{ - int ret; - - ret = cw1200_reg_read_16(priv, - ST90TDS_CONTROL_REG_ID, ctrl_reg); - if (ret) { - ret = cw1200_reg_read_16(priv, - ST90TDS_CONTROL_REG_ID, ctrl_reg); - if (ret) - pr_err("[BH] Failed to read control register.\n"); - } - - return ret; -} - -static int cw1200_device_wakeup(struct cw1200_common *priv) -{ - u16 ctrl_reg; - int ret; - - pr_debug("[BH] Device wakeup.\n"); - - /* First, set the dpll register */ - ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, - cw1200_dpll_from_clk(priv->hw_refclk)); - if (WARN_ON(ret)) - return ret; - - /* To force the device to be always-on, the host sets WLAN_UP to 1 */ - ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, - ST90TDS_CONT_WUP_BIT); - if (WARN_ON(ret)) - return ret; - - ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg); - if (WARN_ON(ret)) - return ret; - - /* If the device returns WLAN_RDY as 1, the device is active and will - * remain active. - */ - if (ctrl_reg & ST90TDS_CONT_RDY_BIT) { - pr_debug("[BH] Device awake.\n"); - return 1; - } - - return 0; -} - -/* Must be called from BH thraed. */ -void cw1200_enable_powersave(struct cw1200_common *priv, - bool enable) -{ - pr_debug("[BH] Powerave is %s.\n", - enable ? "enabled" : "disabled"); - priv->powersave_enabled = enable; -} - -static int cw1200_bh_rx_helper(struct cw1200_common *priv, - uint16_t *ctrl_reg, - int *tx) -{ - size_t read_len = 0; - struct sk_buff *skb_rx = NULL; - struct wsm_hdr *wsm; - size_t wsm_len; - u16 wsm_id; - u8 wsm_seq; - int rx_resync = 1; - - size_t alloc_len; - u8 *data; - - read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2; - if (!read_len) - return 0; /* No more work */ - - if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || - (read_len > EFFECTIVE_BUF_SIZE))) { - pr_debug("Invalid read len: %zu (%04x)", - read_len, *ctrl_reg); - goto err; - } - - /* Add SIZE of PIGGYBACK reg (CONTROL Reg) - * to the NEXT Message length + 2 Bytes for SKB - */ - read_len = read_len + 2; - - alloc_len = priv->hwbus_ops->align_size( - priv->hwbus_priv, read_len); - - /* Check if not exceeding CW1200 capabilities */ - if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) { - pr_debug("Read aligned len: %zu\n", - alloc_len); - } - - skb_rx = dev_alloc_skb(alloc_len); - if (WARN_ON(!skb_rx)) - goto err; - - skb_trim(skb_rx, 0); - skb_put(skb_rx, read_len); - data = skb_rx->data; - if (WARN_ON(!data)) - goto err; - - if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) { - pr_err("rx blew up, len %zu\n", alloc_len); - goto err; - } - - /* Piggyback */ - *ctrl_reg = __le16_to_cpu( - ((__le16 *)data)[alloc_len / 2 - 1]); - - wsm = (struct wsm_hdr *)data; - wsm_len = __le16_to_cpu(wsm->len); - if (WARN_ON(wsm_len > read_len)) - goto err; - - if (priv->wsm_enable_wsm_dumps) - print_hex_dump_bytes("<-- ", - DUMP_PREFIX_NONE, - data, wsm_len); - - wsm_id = __le16_to_cpu(wsm->id) & 0xFFF; - wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7; - - skb_trim(skb_rx, wsm_len); - - if (wsm_id == 0x0800) { - wsm_handle_exception(priv, - &data[sizeof(*wsm)], - wsm_len - sizeof(*wsm)); - goto err; - } else if (!rx_resync) { - if (WARN_ON(wsm_seq != priv->wsm_rx_seq)) - goto err; - } - priv->wsm_rx_seq = (wsm_seq + 1) & 7; - rx_resync = 0; - - if (wsm_id & 0x0400) { - int rc = wsm_release_tx_buffer(priv, 1); - if (WARN_ON(rc < 0)) - return rc; - else if (rc > 0) - *tx = 1; - } - - /* cw1200_wsm_rx takes care on SKB livetime */ - if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx))) - goto err; - - if (skb_rx) { - dev_kfree_skb(skb_rx); - skb_rx = NULL; - } - - return 0; - -err: - if (skb_rx) { - dev_kfree_skb(skb_rx); - skb_rx = NULL; - } - return -1; -} - -static int cw1200_bh_tx_helper(struct cw1200_common *priv, - int *pending_tx, - int *tx_burst) -{ - size_t tx_len; - u8 *data; - int ret; - struct wsm_hdr *wsm; - - if (priv->device_can_sleep) { - ret = cw1200_device_wakeup(priv); - if (WARN_ON(ret < 0)) { /* Error in wakeup */ - *pending_tx = 1; - return 0; - } else if (ret) { /* Woke up */ - priv->device_can_sleep = false; - } else { /* Did not awake */ - *pending_tx = 1; - return 0; - } - } - - wsm_alloc_tx_buffer(priv); - ret = wsm_get_tx(priv, &data, &tx_len, tx_burst); - if (ret <= 0) { - wsm_release_tx_buffer(priv, 1); - if (WARN_ON(ret < 0)) - return ret; /* Error */ - return 0; /* No work */ - } - - wsm = (struct wsm_hdr *)data; - BUG_ON(tx_len < sizeof(*wsm)); - BUG_ON(__le16_to_cpu(wsm->len) != tx_len); - - atomic_add(1, &priv->bh_tx); - - tx_len = priv->hwbus_ops->align_size( - priv->hwbus_priv, tx_len); - - /* Check if not exceeding CW1200 capabilities */ - if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE)) - pr_debug("Write aligned len: %zu\n", tx_len); - - wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX)); - wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq)); - - if (WARN_ON(cw1200_data_write(priv, data, tx_len))) { - pr_err("tx blew up, len %zu\n", tx_len); - wsm_release_tx_buffer(priv, 1); - return -1; /* Error */ - } - - if (priv->wsm_enable_wsm_dumps) - print_hex_dump_bytes("--> ", - DUMP_PREFIX_NONE, - data, - __le16_to_cpu(wsm->len)); - - wsm_txed(priv, data); - priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX; - - if (*tx_burst > 1) { - cw1200_debug_tx_burst(priv); - return 1; /* Work remains */ - } - - return 0; -} - -static int cw1200_bh(void *arg) -{ - struct cw1200_common *priv = arg; - int rx, tx, term, suspend; - u16 ctrl_reg = 0; - int tx_allowed; - int pending_tx = 0; - int tx_burst; - long status; - u32 dummy; - int ret; - - for (;;) { - if (!priv->hw_bufs_used && - priv->powersave_enabled && - !priv->device_can_sleep && - !atomic_read(&priv->recent_scan)) { - status = 1 * HZ; - pr_debug("[BH] Device wakedown. No data.\n"); - cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0); - priv->device_can_sleep = true; - } else if (priv->hw_bufs_used) { - /* Interrupt loss detection */ - status = 1 * HZ; - } else { - status = MAX_SCHEDULE_TIMEOUT; - } - - /* Dummy Read for SDIO retry mechanism*/ - if ((priv->hw_type != -1) && - (atomic_read(&priv->bh_rx) == 0) && - (atomic_read(&priv->bh_tx) == 0)) - cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID, - &dummy, sizeof(dummy)); - - pr_debug("[BH] waiting ...\n"); - status = wait_event_interruptible_timeout(priv->bh_wq, ({ - rx = atomic_xchg(&priv->bh_rx, 0); - tx = atomic_xchg(&priv->bh_tx, 0); - term = atomic_xchg(&priv->bh_term, 0); - suspend = pending_tx ? - 0 : atomic_read(&priv->bh_suspend); - (rx || tx || term || suspend || priv->bh_error); - }), status); - - pr_debug("[BH] - rx: %d, tx: %d, term: %d, bh_err: %d, suspend: %d, status: %ld\n", - rx, tx, term, suspend, priv->bh_error, status); - - /* Did an error occur? */ - if ((status < 0 && status != -ERESTARTSYS) || - term || priv->bh_error) { - break; - } - if (!status) { /* wait_event timed out */ - unsigned long timestamp = jiffies; - long timeout; - int pending = 0; - int i; - - /* Check to see if we have any outstanding frames */ - if (priv->hw_bufs_used && (!rx || !tx)) { - wiphy_warn(priv->hw->wiphy, - "Missed interrupt? (%d frames outstanding)\n", - priv->hw_bufs_used); - rx = 1; - - /* Get a timestamp of "oldest" frame */ - for (i = 0; i < 4; ++i) - pending += cw1200_queue_get_xmit_timestamp( - &priv->tx_queue[i], - ×tamp, - priv->pending_frame_id); - - /* Check if frame transmission is timed out. - * Add an extra second with respect to possible - * interrupt loss. - */ - timeout = timestamp + - WSM_CMD_LAST_CHANCE_TIMEOUT + - 1 * HZ - - jiffies; - - /* And terminate BH thread if the frame is "stuck" */ - if (pending && timeout < 0) { - wiphy_warn(priv->hw->wiphy, - "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n", - priv->hw_bufs_used, pending, - timestamp, jiffies); - break; - } - } else if (!priv->device_can_sleep && - !atomic_read(&priv->recent_scan)) { - pr_debug("[BH] Device wakedown. Timeout.\n"); - cw1200_reg_write_16(priv, - ST90TDS_CONTROL_REG_ID, 0); - priv->device_can_sleep = true; - } - goto done; - } else if (suspend) { - pr_debug("[BH] Device suspend.\n"); - if (priv->powersave_enabled) { - pr_debug("[BH] Device wakedown. Suspend.\n"); - cw1200_reg_write_16(priv, - ST90TDS_CONTROL_REG_ID, 0); - priv->device_can_sleep = true; - } - - atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED); - wake_up(&priv->bh_evt_wq); - status = wait_event_interruptible(priv->bh_wq, - CW1200_BH_RESUME == atomic_read(&priv->bh_suspend)); - if (status < 0) { - wiphy_err(priv->hw->wiphy, - "Failed to wait for resume: %ld.\n", - status); - break; - } - pr_debug("[BH] Device resume.\n"); - atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); - wake_up(&priv->bh_evt_wq); - atomic_add(1, &priv->bh_rx); - goto done; - } - - rx: - tx += pending_tx; - pending_tx = 0; - - if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg)) - break; - - /* Don't bother trying to rx unless we have data to read */ - if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) { - ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx); - if (ret < 0) - break; - /* Double up here if there's more data.. */ - if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) { - ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx); - if (ret < 0) - break; - } - } - - tx: - if (tx) { - tx = 0; - - BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers); - tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used; - tx_allowed = tx_burst > 0; - - if (!tx_allowed) { - /* Buffers full. Ensure we process tx - * after we handle rx.. - */ - pending_tx = tx; - goto done_rx; - } - ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst); - if (ret < 0) - break; - if (ret > 0) /* More to transmit */ - tx = ret; - - /* Re-read ctrl reg */ - if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg)) - break; - } - - done_rx: - if (priv->bh_error) - break; - if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) - goto rx; - if (tx) - goto tx; - - done: - /* Re-enable device interrupts */ - priv->hwbus_ops->lock(priv->hwbus_priv); - __cw1200_irq_enable(priv, 1); - priv->hwbus_ops->unlock(priv->hwbus_priv); - } - - /* Explicitly disable device interrupts */ - priv->hwbus_ops->lock(priv->hwbus_priv); - __cw1200_irq_enable(priv, 0); - priv->hwbus_ops->unlock(priv->hwbus_priv); - - if (!term) { - pr_err("[BH] Fatal error, exiting.\n"); - priv->bh_error = 1; - /* TODO: schedule_work(recovery) */ - } - return 0; -} diff --git a/drivers/net/wireless/cw1200/bh.h b/drivers/net/wireless/cw1200/bh.h deleted file mode 100644 index af6a4853728f..000000000000 --- a/drivers/net/wireless/cw1200/bh.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef CW1200_BH_H -#define CW1200_BH_H - -/* extern */ struct cw1200_common; - -int cw1200_register_bh(struct cw1200_common *priv); -void cw1200_unregister_bh(struct cw1200_common *priv); -void cw1200_irq_handler(struct cw1200_common *priv); -void cw1200_bh_wakeup(struct cw1200_common *priv); -int cw1200_bh_suspend(struct cw1200_common *priv); -int cw1200_bh_resume(struct cw1200_common *priv); -/* Must be called from BH thread. */ -void cw1200_enable_powersave(struct cw1200_common *priv, - bool enable); -int wsm_release_tx_buffer(struct cw1200_common *priv, int count); - -#endif /* CW1200_BH_H */ diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/cw1200/cw1200.h deleted file mode 100644 index 1ad7d3602520..000000000000 --- a/drivers/net/wireless/cw1200/cw1200.h +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Common private data for ST-Ericsson CW1200 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * Based on the mac80211 Prism54 code, which is - * Copyright (c) 2006, Michael Wu - * - * Based on the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef CW1200_H -#define CW1200_H - -#include -#include -#include -#include - -#include "queue.h" -#include "wsm.h" -#include "scan.h" -#include "txrx.h" -#include "pm.h" - -/* Forward declarations */ -struct hwbus_ops; -struct task_struct; -struct cw1200_debug_priv; -struct firmware; - -#define CW1200_MAX_CTRL_FRAME_LEN (0x1000) - -#define CW1200_MAX_STA_IN_AP_MODE (5) -#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1) -#define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2) -#define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3) -#define CW1200_MAX_REQUEUE_ATTEMPTS (5) - -#define CW1200_MAX_TID (8) - -#define CW1200_BLOCK_ACK_CNT (30) -#define CW1200_BLOCK_ACK_THLD (800) -#define CW1200_BLOCK_ACK_HIST (3) -#define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST) - -#define CW1200_JOIN_TIMEOUT (1 * HZ) -#define CW1200_AUTH_TIMEOUT (5 * HZ) - -struct cw1200_ht_info { - struct ieee80211_sta_ht_cap ht_cap; - enum nl80211_channel_type channel_type; - u16 operation_mode; -}; - -/* Please keep order */ -enum cw1200_join_status { - CW1200_JOIN_STATUS_PASSIVE = 0, - CW1200_JOIN_STATUS_MONITOR, - CW1200_JOIN_STATUS_JOINING, - CW1200_JOIN_STATUS_PRE_STA, - CW1200_JOIN_STATUS_STA, - CW1200_JOIN_STATUS_IBSS, - CW1200_JOIN_STATUS_AP, -}; - -enum cw1200_link_status { - CW1200_LINK_OFF, - CW1200_LINK_RESERVE, - CW1200_LINK_SOFT, - CW1200_LINK_HARD, - CW1200_LINK_RESET, - CW1200_LINK_RESET_REMAP, -}; - -extern int cw1200_power_mode; -extern const char * const cw1200_fw_types[]; - -struct cw1200_link_entry { - unsigned long timestamp; - enum cw1200_link_status status; - enum cw1200_link_status prev_status; - u8 mac[ETH_ALEN]; - u8 buffered[CW1200_MAX_TID]; - struct sk_buff_head rx_queue; -}; - -struct cw1200_common { - /* interfaces to the rest of the stack */ - struct ieee80211_hw *hw; - struct ieee80211_vif *vif; - struct device *pdev; - - /* Statistics */ - struct ieee80211_low_level_stats stats; - - /* Our macaddr */ - u8 mac_addr[ETH_ALEN]; - - /* Hardware interface */ - const struct hwbus_ops *hwbus_ops; - struct hwbus_priv *hwbus_priv; - - /* Hardware information */ - enum { - HIF_9000_SILICON_VERSATILE = 0, - HIF_8601_VERSATILE, - HIF_8601_SILICON, - } hw_type; - enum { - CW1200_HW_REV_CUT10 = 10, - CW1200_HW_REV_CUT11 = 11, - CW1200_HW_REV_CUT20 = 20, - CW1200_HW_REV_CUT22 = 22, - CW1X60_HW_REV = 40, - } hw_revision; - int hw_refclk; - bool hw_have_5ghz; - const struct firmware *sdd; - char *sdd_path; - - struct cw1200_debug_priv *debug; - - struct workqueue_struct *workqueue; - struct mutex conf_mutex; - - struct cw1200_queue tx_queue[4]; - struct cw1200_queue_stats tx_queue_stats; - int tx_burst_idx; - - /* firmware/hardware info */ - unsigned int tx_hdr_len; - - /* Radio data */ - int output_power; - - /* BBP/MAC state */ - struct ieee80211_rate *rates; - struct ieee80211_rate *mcs_rates; - struct ieee80211_channel *channel; - struct wsm_edca_params edca; - struct wsm_tx_queue_params tx_queue_params; - struct wsm_mib_association_mode association_mode; - struct wsm_set_bss_params bss_params; - struct cw1200_ht_info ht_info; - struct wsm_set_pm powersave_mode; - struct wsm_set_pm firmware_ps_mode; - int cqm_rssi_thold; - unsigned cqm_rssi_hyst; - bool cqm_use_rssi; - int cqm_beacon_loss_count; - int channel_switch_in_progress; - wait_queue_head_t channel_switch_done; - u8 long_frame_max_tx_count; - u8 short_frame_max_tx_count; - int mode; - bool enable_beacon; - int beacon_int; - bool listening; - struct wsm_rx_filter rx_filter; - struct wsm_mib_multicast_filter multicast_filter; - bool has_multicast_subscription; - bool disable_beacon_filter; - struct work_struct update_filtering_work; - struct work_struct set_beacon_wakeup_period_work; - - u8 ba_rx_tid_mask; - u8 ba_tx_tid_mask; - - struct cw1200_pm_state pm_state; - - struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; - struct wsm_uapsd_info uapsd_info; - bool setbssparams_done; - bool bt_present; - u8 conf_listen_interval; - u32 listen_interval; - u32 erp_info; - u32 rts_threshold; - - /* BH */ - atomic_t bh_rx; - atomic_t bh_tx; - atomic_t bh_term; - atomic_t bh_suspend; - - struct workqueue_struct *bh_workqueue; - struct work_struct bh_work; - - int bh_error; - wait_queue_head_t bh_wq; - wait_queue_head_t bh_evt_wq; - u8 buf_id_tx; - u8 buf_id_rx; - u8 wsm_rx_seq; - u8 wsm_tx_seq; - int hw_bufs_used; - bool powersave_enabled; - bool device_can_sleep; - - /* Scan status */ - struct cw1200_scan scan; - /* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid - * FW issue with sleeping/waking up. - */ - atomic_t recent_scan; - struct delayed_work clear_recent_scan_work; - - /* WSM */ - struct wsm_startup_ind wsm_caps; - struct mutex wsm_cmd_mux; - struct wsm_buf wsm_cmd_buf; - struct wsm_cmd wsm_cmd; - wait_queue_head_t wsm_cmd_wq; - wait_queue_head_t wsm_startup_done; - int firmware_ready; - atomic_t tx_lock; - - /* WSM debug */ - int wsm_enable_wsm_dumps; - - /* WSM Join */ - enum cw1200_join_status join_status; - u32 pending_frame_id; - bool join_pending; - struct delayed_work join_timeout; - struct work_struct unjoin_work; - struct work_struct join_complete_work; - int join_complete_status; - int join_dtim_period; - bool delayed_unjoin; - - /* TX/RX and security */ - s8 wep_default_key_id; - struct work_struct wep_key_work; - u32 key_map; - struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1]; - - /* AP powersave */ - u32 link_id_map; - struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE]; - struct work_struct link_id_work; - struct delayed_work link_id_gc_work; - u32 sta_asleep_mask; - u32 pspoll_mask; - bool aid0_bit_set; - spinlock_t ps_state_lock; /* Protect power save state */ - bool buffered_multicasts; - bool tx_multicast; - struct work_struct set_tim_work; - struct work_struct set_cts_work; - struct work_struct multicast_start_work; - struct work_struct multicast_stop_work; - struct timer_list mcast_timeout; - - /* WSM events and CQM implementation */ - spinlock_t event_queue_lock; /* Protect event queue */ - struct list_head event_queue; - struct work_struct event_handler; - - struct delayed_work bss_loss_work; - spinlock_t bss_loss_lock; /* Protect BSS loss state */ - int bss_loss_state; - u32 bss_loss_confirm_id; - int delayed_link_loss; - struct work_struct bss_params_work; - - /* TX rate policy cache */ - struct tx_policy_cache tx_policy_cache; - struct work_struct tx_policy_upload_work; - - /* legacy PS mode switch in suspend */ - int ps_mode_switch_in_progress; - wait_queue_head_t ps_mode_switch_done; - - /* Workaround for WFD testcase 6.1.10*/ - struct work_struct linkid_reset_work; - u8 action_frame_sa[ETH_ALEN]; - u8 action_linkid; -}; - -struct cw1200_sta_priv { - int link_id; -}; - -/* interfaces for the drivers */ -int cw1200_core_probe(const struct hwbus_ops *hwbus_ops, - struct hwbus_priv *hwbus, - struct device *pdev, - struct cw1200_common **pself, - int ref_clk, const u8 *macaddr, - const char *sdd_path, bool have_5ghz); -void cw1200_core_release(struct cw1200_common *self); - -#define FWLOAD_BLOCK_SIZE (1024) - -static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info) -{ - return ht_info->channel_type != NL80211_CHAN_NO_HT; -} - -static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info) -{ - return cw1200_is_ht(ht_info) && - (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && - !(ht_info->operation_mode & - IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); -} - -static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info) -{ - if (!cw1200_is_ht(ht_info)) - return 0; - return ht_info->ht_cap.ampdu_density; -} - -#endif /* CW1200_H */ diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c deleted file mode 100644 index d3acc85932a5..000000000000 --- a/drivers/net/wireless/cw1200/cw1200_sdio.c +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Mac80211 SDIO driver for ST-Ericsson CW1200 device - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cw1200.h" -#include "hwbus.h" -#include -#include "hwio.h" - -MODULE_AUTHOR("Dmitry Tarnyagin "); -MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver"); -MODULE_LICENSE("GPL"); - -#define SDIO_BLOCK_SIZE (512) - -/* Default platform data for Sagrad modules */ -static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = { - .ref_clk = 38400, - .have_5ghz = false, - .sdd_file = "sdd_sagrad_1091_1098.bin", -}; - -/* Allow platform data to be overridden */ -static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data; - -void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata) -{ - global_plat_data = pdata; -} - -struct hwbus_priv { - struct sdio_func *func; - struct cw1200_common *core; - const struct cw1200_platform_data_sdio *pdata; -}; - -#ifndef SDIO_VENDOR_ID_STE -#define SDIO_VENDOR_ID_STE 0x0020 -#endif - -#ifndef SDIO_DEVICE_ID_STE_CW1200 -#define SDIO_DEVICE_ID_STE_CW1200 0x2280 -#endif - -static const struct sdio_device_id cw1200_sdio_ids[] = { - { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) }, - { /* end: all zeroes */ }, -}; - -/* hwbus_ops implemetation */ - -static int cw1200_sdio_memcpy_fromio(struct hwbus_priv *self, - unsigned int addr, - void *dst, int count) -{ - return sdio_memcpy_fromio(self->func, dst, addr, count); -} - -static int cw1200_sdio_memcpy_toio(struct hwbus_priv *self, - unsigned int addr, - const void *src, int count) -{ - return sdio_memcpy_toio(self->func, addr, (void *)src, count); -} - -static void cw1200_sdio_lock(struct hwbus_priv *self) -{ - sdio_claim_host(self->func); -} - -static void cw1200_sdio_unlock(struct hwbus_priv *self) -{ - sdio_release_host(self->func); -} - -static void cw1200_sdio_irq_handler(struct sdio_func *func) -{ - struct hwbus_priv *self = sdio_get_drvdata(func); - - /* note: sdio_host already claimed here. */ - if (self->core) - cw1200_irq_handler(self->core); -} - -static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id) -{ - return IRQ_WAKE_THREAD; -} - -static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id) -{ - struct hwbus_priv *self = dev_id; - - if (self->core) { - cw1200_sdio_lock(self); - cw1200_irq_handler(self->core); - cw1200_sdio_unlock(self); - return IRQ_HANDLED; - } else { - return IRQ_NONE; - } -} - -static int cw1200_request_irq(struct hwbus_priv *self) -{ - int ret; - u8 cccr; - - cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret); - if (WARN_ON(ret)) - goto err; - - /* Master interrupt enable ... */ - cccr |= BIT(0); - - /* ... for our function */ - cccr |= BIT(self->func->num); - - sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret); - if (WARN_ON(ret)) - goto err; - - ret = enable_irq_wake(self->pdata->irq); - if (WARN_ON(ret)) - goto err; - - /* Request the IRQ */ - ret = request_threaded_irq(self->pdata->irq, cw1200_gpio_hardirq, - cw1200_gpio_irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - "cw1200_wlan_irq", self); - if (WARN_ON(ret)) - goto err; - - return 0; - -err: - return ret; -} - -static int cw1200_sdio_irq_subscribe(struct hwbus_priv *self) -{ - int ret = 0; - - pr_debug("SW IRQ subscribe\n"); - sdio_claim_host(self->func); - if (self->pdata->irq) - ret = cw1200_request_irq(self); - else - ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler); - - sdio_release_host(self->func); - return ret; -} - -static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self) -{ - int ret = 0; - - pr_debug("SW IRQ unsubscribe\n"); - - if (self->pdata->irq) { - disable_irq_wake(self->pdata->irq); - free_irq(self->pdata->irq, self); - } else { - sdio_claim_host(self->func); - ret = sdio_release_irq(self->func); - sdio_release_host(self->func); - } - return ret; -} - -static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata) -{ - if (pdata->reset) { - gpio_set_value(pdata->reset, 0); - msleep(30); /* Min is 2 * CLK32K cycles */ - gpio_free(pdata->reset); - } - - if (pdata->power_ctrl) - pdata->power_ctrl(pdata, false); - if (pdata->clk_ctrl) - pdata->clk_ctrl(pdata, false); - - return 0; -} - -static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata) -{ - /* Ensure I/Os are pulled low */ - if (pdata->reset) { - gpio_request(pdata->reset, "cw1200_wlan_reset"); - gpio_direction_output(pdata->reset, 0); - } - if (pdata->powerup) { - gpio_request(pdata->powerup, "cw1200_wlan_powerup"); - gpio_direction_output(pdata->powerup, 0); - } - if (pdata->reset || pdata->powerup) - msleep(10); /* Settle time? */ - - /* Enable 3v3 and 1v8 to hardware */ - if (pdata->power_ctrl) { - if (pdata->power_ctrl(pdata, true)) { - pr_err("power_ctrl() failed!\n"); - return -1; - } - } - - /* Enable CLK32K */ - if (pdata->clk_ctrl) { - if (pdata->clk_ctrl(pdata, true)) { - pr_err("clk_ctrl() failed!\n"); - return -1; - } - msleep(10); /* Delay until clock is stable for 2 cycles */ - } - - /* Enable POWERUP signal */ - if (pdata->powerup) { - gpio_set_value(pdata->powerup, 1); - msleep(250); /* or more..? */ - } - /* Enable RSTn signal */ - if (pdata->reset) { - gpio_set_value(pdata->reset, 1); - msleep(50); /* Or more..? */ - } - return 0; -} - -static size_t cw1200_sdio_align_size(struct hwbus_priv *self, size_t size) -{ - if (self->pdata->no_nptb) - size = round_up(size, SDIO_BLOCK_SIZE); - else - size = sdio_align_size(self->func, size); - - return size; -} - -static int cw1200_sdio_pm(struct hwbus_priv *self, bool suspend) -{ - int ret = 0; - - if (self->pdata->irq) - ret = irq_set_irq_wake(self->pdata->irq, suspend); - return ret; -} - -static struct hwbus_ops cw1200_sdio_hwbus_ops = { - .hwbus_memcpy_fromio = cw1200_sdio_memcpy_fromio, - .hwbus_memcpy_toio = cw1200_sdio_memcpy_toio, - .lock = cw1200_sdio_lock, - .unlock = cw1200_sdio_unlock, - .align_size = cw1200_sdio_align_size, - .power_mgmt = cw1200_sdio_pm, -}; - -/* Probe Function to be called by SDIO stack when device is discovered */ -static int cw1200_sdio_probe(struct sdio_func *func, - const struct sdio_device_id *id) -{ - struct hwbus_priv *self; - int status; - - pr_info("cw1200_wlan_sdio: Probe called\n"); - - /* We are only able to handle the wlan function */ - if (func->num != 0x01) - return -ENODEV; - - self = kzalloc(sizeof(*self), GFP_KERNEL); - if (!self) { - pr_err("Can't allocate SDIO hwbus_priv.\n"); - return -ENOMEM; - } - - func->card->quirks |= MMC_QUIRK_LENIENT_FN0; - - self->pdata = global_plat_data; /* FIXME */ - self->func = func; - sdio_set_drvdata(func, self); - sdio_claim_host(func); - sdio_enable_func(func); - sdio_release_host(func); - - status = cw1200_sdio_irq_subscribe(self); - - status = cw1200_core_probe(&cw1200_sdio_hwbus_ops, - self, &func->dev, &self->core, - self->pdata->ref_clk, - self->pdata->macaddr, - self->pdata->sdd_file, - self->pdata->have_5ghz); - if (status) { - cw1200_sdio_irq_unsubscribe(self); - sdio_claim_host(func); - sdio_disable_func(func); - sdio_release_host(func); - sdio_set_drvdata(func, NULL); - kfree(self); - } - - return status; -} - -/* Disconnect Function to be called by SDIO stack when - * device is disconnected - */ -static void cw1200_sdio_disconnect(struct sdio_func *func) -{ - struct hwbus_priv *self = sdio_get_drvdata(func); - - if (self) { - cw1200_sdio_irq_unsubscribe(self); - if (self->core) { - cw1200_core_release(self->core); - self->core = NULL; - } - sdio_claim_host(func); - sdio_disable_func(func); - sdio_release_host(func); - sdio_set_drvdata(func, NULL); - kfree(self); - } -} - -#ifdef CONFIG_PM -static int cw1200_sdio_suspend(struct device *dev) -{ - int ret; - struct sdio_func *func = dev_to_sdio_func(dev); - struct hwbus_priv *self = sdio_get_drvdata(func); - - if (!cw1200_can_suspend(self->core)) - return -EAGAIN; - - /* Notify SDIO that CW1200 will remain powered during suspend */ - ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); - if (ret) - pr_err("Error setting SDIO pm flags: %i\n", ret); - - return ret; -} - -static int cw1200_sdio_resume(struct device *dev) -{ - return 0; -} - -static const struct dev_pm_ops cw1200_pm_ops = { - .suspend = cw1200_sdio_suspend, - .resume = cw1200_sdio_resume, -}; -#endif - -static struct sdio_driver sdio_driver = { - .name = "cw1200_wlan_sdio", - .id_table = cw1200_sdio_ids, - .probe = cw1200_sdio_probe, - .remove = cw1200_sdio_disconnect, -#ifdef CONFIG_PM - .drv = { - .pm = &cw1200_pm_ops, - } -#endif -}; - -/* Init Module function -> Called by insmod */ -static int __init cw1200_sdio_init(void) -{ - const struct cw1200_platform_data_sdio *pdata; - int ret; - - /* FIXME -- this won't support multiple devices */ - pdata = global_plat_data; - - if (cw1200_sdio_on(pdata)) { - ret = -1; - goto err; - } - - ret = sdio_register_driver(&sdio_driver); - if (ret) - goto err; - - return 0; - -err: - cw1200_sdio_off(pdata); - return ret; -} - -/* Called at Driver Unloading */ -static void __exit cw1200_sdio_exit(void) -{ - const struct cw1200_platform_data_sdio *pdata; - - /* FIXME -- this won't support multiple devices */ - pdata = global_plat_data; - sdio_unregister_driver(&sdio_driver); - cw1200_sdio_off(pdata); -} - - -module_init(cw1200_sdio_init); -module_exit(cw1200_sdio_exit); diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c deleted file mode 100644 index a740083634d8..000000000000 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ /dev/null @@ -1,476 +0,0 @@ -/* - * Mac80211 SPI driver for ST-Ericsson CW1200 device - * - * Copyright (c) 2011, Sagrad Inc. - * Author: Solomon Peachy - * - * Based on cw1200_sdio.c - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "cw1200.h" -#include "hwbus.h" -#include -#include "hwio.h" - -MODULE_AUTHOR("Solomon Peachy "); -MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:cw1200_wlan_spi"); - -/* #define SPI_DEBUG */ - -struct hwbus_priv { - struct spi_device *func; - struct cw1200_common *core; - const struct cw1200_platform_data_spi *pdata; - spinlock_t lock; /* Serialize all bus operations */ - wait_queue_head_t wq; - int claimed; -}; - -#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2) -#define SET_WRITE 0x7FFF /* usage: and operation */ -#define SET_READ 0x8000 /* usage: or operation */ - -/* Notes on byte ordering: - LE: B0 B1 B2 B3 - BE: B3 B2 B1 B0 - - Hardware expects 32-bit data to be written as 16-bit BE words: - - B1 B0 B3 B2 -*/ - -static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self, - unsigned int addr, - void *dst, int count) -{ - int ret, i; - u16 regaddr; - struct spi_message m; - - struct spi_transfer t_addr = { - .tx_buf = ®addr, - .len = sizeof(regaddr), - }; - struct spi_transfer t_msg = { - .rx_buf = dst, - .len = count, - }; - - regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; - regaddr |= SET_READ; - regaddr |= (count>>1); - -#ifdef SPI_DEBUG - pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, regaddr); -#endif - - /* Header is LE16 */ - regaddr = cpu_to_le16(regaddr); - - /* We have to byteswap if the SPI bus is limited to 8b operation - or we are running on a Big Endian system - */ -#if defined(__LITTLE_ENDIAN) - if (self->func->bits_per_word == 8) -#endif - regaddr = swab16(regaddr); - - spi_message_init(&m); - spi_message_add_tail(&t_addr, &m); - spi_message_add_tail(&t_msg, &m); - ret = spi_sync(self->func, &m); - -#ifdef SPI_DEBUG - pr_info("READ : "); - for (i = 0; i < t_addr.len; i++) - printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); - printk(" : "); - for (i = 0; i < t_msg.len; i++) - printk("%02x ", ((u8 *)t_msg.rx_buf)[i]); - printk("\n"); -#endif - - /* We have to byteswap if the SPI bus is limited to 8b operation - or we are running on a Big Endian system - */ -#if defined(__LITTLE_ENDIAN) - if (self->func->bits_per_word == 8) -#endif - { - uint16_t *buf = (uint16_t *)dst; - for (i = 0; i < ((count + 1) >> 1); i++) - buf[i] = swab16(buf[i]); - } - - return ret; -} - -static int cw1200_spi_memcpy_toio(struct hwbus_priv *self, - unsigned int addr, - const void *src, int count) -{ - int rval, i; - u16 regaddr; - struct spi_transfer t_addr = { - .tx_buf = ®addr, - .len = sizeof(regaddr), - }; - struct spi_transfer t_msg = { - .tx_buf = src, - .len = count, - }; - struct spi_message m; - - regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; - regaddr &= SET_WRITE; - regaddr |= (count>>1); - -#ifdef SPI_DEBUG - pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr, regaddr); -#endif - - /* Header is LE16 */ - regaddr = cpu_to_le16(regaddr); - - /* We have to byteswap if the SPI bus is limited to 8b operation - or we are running on a Big Endian system - */ -#if defined(__LITTLE_ENDIAN) - if (self->func->bits_per_word == 8) -#endif - { - uint16_t *buf = (uint16_t *)src; - regaddr = swab16(regaddr); - for (i = 0; i < ((count + 1) >> 1); i++) - buf[i] = swab16(buf[i]); - } - -#ifdef SPI_DEBUG - pr_info("WRITE: "); - for (i = 0; i < t_addr.len; i++) - printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); - printk(" : "); - for (i = 0; i < t_msg.len; i++) - printk("%02x ", ((u8 *)t_msg.tx_buf)[i]); - printk("\n"); -#endif - - spi_message_init(&m); - spi_message_add_tail(&t_addr, &m); - spi_message_add_tail(&t_msg, &m); - rval = spi_sync(self->func, &m); - -#ifdef SPI_DEBUG - pr_info("WROTE: %d\n", m.actual_length); -#endif - -#if defined(__LITTLE_ENDIAN) - /* We have to byteswap if the SPI bus is limited to 8b operation */ - if (self->func->bits_per_word == 8) -#endif - { - uint16_t *buf = (uint16_t *)src; - for (i = 0; i < ((count + 1) >> 1); i++) - buf[i] = swab16(buf[i]); - } - return rval; -} - -static void cw1200_spi_lock(struct hwbus_priv *self) -{ - unsigned long flags; - - DECLARE_WAITQUEUE(wait, current); - - might_sleep(); - - add_wait_queue(&self->wq, &wait); - spin_lock_irqsave(&self->lock, flags); - while (1) { - set_current_state(TASK_UNINTERRUPTIBLE); - if (!self->claimed) - break; - spin_unlock_irqrestore(&self->lock, flags); - schedule(); - spin_lock_irqsave(&self->lock, flags); - } - set_current_state(TASK_RUNNING); - self->claimed = 1; - spin_unlock_irqrestore(&self->lock, flags); - remove_wait_queue(&self->wq, &wait); - - return; -} - -static void cw1200_spi_unlock(struct hwbus_priv *self) -{ - unsigned long flags; - - spin_lock_irqsave(&self->lock, flags); - self->claimed = 0; - spin_unlock_irqrestore(&self->lock, flags); - wake_up(&self->wq); - - return; -} - -static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id) -{ - struct hwbus_priv *self = dev_id; - - if (self->core) { - cw1200_spi_lock(self); - cw1200_irq_handler(self->core); - cw1200_spi_unlock(self); - return IRQ_HANDLED; - } else { - return IRQ_NONE; - } -} - -static int cw1200_spi_irq_subscribe(struct hwbus_priv *self) -{ - int ret; - - pr_debug("SW IRQ subscribe\n"); - - ret = request_threaded_irq(self->func->irq, NULL, - cw1200_spi_irq_handler, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - "cw1200_wlan_irq", self); - if (WARN_ON(ret < 0)) - goto exit; - - ret = enable_irq_wake(self->func->irq); - if (WARN_ON(ret)) - goto free_irq; - - return 0; - -free_irq: - free_irq(self->func->irq, self); -exit: - return ret; -} - -static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self) -{ - int ret = 0; - - pr_debug("SW IRQ unsubscribe\n"); - disable_irq_wake(self->func->irq); - free_irq(self->func->irq, self); - - return ret; -} - -static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata) -{ - if (pdata->reset) { - gpio_set_value(pdata->reset, 0); - msleep(30); /* Min is 2 * CLK32K cycles */ - gpio_free(pdata->reset); - } - - if (pdata->power_ctrl) - pdata->power_ctrl(pdata, false); - if (pdata->clk_ctrl) - pdata->clk_ctrl(pdata, false); - - return 0; -} - -static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata) -{ - /* Ensure I/Os are pulled low */ - if (pdata->reset) { - gpio_request(pdata->reset, "cw1200_wlan_reset"); - gpio_direction_output(pdata->reset, 0); - } - if (pdata->powerup) { - gpio_request(pdata->powerup, "cw1200_wlan_powerup"); - gpio_direction_output(pdata->powerup, 0); - } - if (pdata->reset || pdata->powerup) - msleep(10); /* Settle time? */ - - /* Enable 3v3 and 1v8 to hardware */ - if (pdata->power_ctrl) { - if (pdata->power_ctrl(pdata, true)) { - pr_err("power_ctrl() failed!\n"); - return -1; - } - } - - /* Enable CLK32K */ - if (pdata->clk_ctrl) { - if (pdata->clk_ctrl(pdata, true)) { - pr_err("clk_ctrl() failed!\n"); - return -1; - } - msleep(10); /* Delay until clock is stable for 2 cycles */ - } - - /* Enable POWERUP signal */ - if (pdata->powerup) { - gpio_set_value(pdata->powerup, 1); - msleep(250); /* or more..? */ - } - /* Enable RSTn signal */ - if (pdata->reset) { - gpio_set_value(pdata->reset, 1); - msleep(50); /* Or more..? */ - } - return 0; -} - -static size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size) -{ - return size & 1 ? size + 1 : size; -} - -static int cw1200_spi_pm(struct hwbus_priv *self, bool suspend) -{ - return irq_set_irq_wake(self->func->irq, suspend); -} - -static struct hwbus_ops cw1200_spi_hwbus_ops = { - .hwbus_memcpy_fromio = cw1200_spi_memcpy_fromio, - .hwbus_memcpy_toio = cw1200_spi_memcpy_toio, - .lock = cw1200_spi_lock, - .unlock = cw1200_spi_unlock, - .align_size = cw1200_spi_align_size, - .power_mgmt = cw1200_spi_pm, -}; - -/* Probe Function to be called by SPI stack when device is discovered */ -static int cw1200_spi_probe(struct spi_device *func) -{ - const struct cw1200_platform_data_spi *plat_data = - dev_get_platdata(&func->dev); - struct hwbus_priv *self; - int status; - - /* Sanity check speed */ - if (func->max_speed_hz > 52000000) - func->max_speed_hz = 52000000; - if (func->max_speed_hz < 1000000) - func->max_speed_hz = 1000000; - - /* Fix up transfer size */ - if (plat_data->spi_bits_per_word) - func->bits_per_word = plat_data->spi_bits_per_word; - if (!func->bits_per_word) - func->bits_per_word = 16; - - /* And finally.. */ - func->mode = SPI_MODE_0; - - pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n", - func->chip_select, func->mode, func->bits_per_word, - func->max_speed_hz); - - if (cw1200_spi_on(plat_data)) { - pr_err("spi_on() failed!\n"); - return -1; - } - - if (spi_setup(func)) { - pr_err("spi_setup() failed!\n"); - return -1; - } - - self = devm_kzalloc(&func->dev, sizeof(*self), GFP_KERNEL); - if (!self) { - pr_err("Can't allocate SPI hwbus_priv."); - return -ENOMEM; - } - - self->pdata = plat_data; - self->func = func; - spin_lock_init(&self->lock); - - spi_set_drvdata(func, self); - - init_waitqueue_head(&self->wq); - - status = cw1200_spi_irq_subscribe(self); - - status = cw1200_core_probe(&cw1200_spi_hwbus_ops, - self, &func->dev, &self->core, - self->pdata->ref_clk, - self->pdata->macaddr, - self->pdata->sdd_file, - self->pdata->have_5ghz); - - if (status) { - cw1200_spi_irq_unsubscribe(self); - cw1200_spi_off(plat_data); - } - - return status; -} - -/* Disconnect Function to be called by SPI stack when device is disconnected */ -static int cw1200_spi_disconnect(struct spi_device *func) -{ - struct hwbus_priv *self = spi_get_drvdata(func); - - if (self) { - cw1200_spi_irq_unsubscribe(self); - if (self->core) { - cw1200_core_release(self->core); - self->core = NULL; - } - } - cw1200_spi_off(dev_get_platdata(&func->dev)); - - return 0; -} - -#ifdef CONFIG_PM -static int cw1200_spi_suspend(struct device *dev) -{ - struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev)); - - if (!cw1200_can_suspend(self->core)) - return -EAGAIN; - - /* XXX notify host that we have to keep CW1200 powered on? */ - return 0; -} - -static SIMPLE_DEV_PM_OPS(cw1200_pm_ops, cw1200_spi_suspend, NULL); - -#endif - -static struct spi_driver spi_driver = { - .probe = cw1200_spi_probe, - .remove = cw1200_spi_disconnect, - .driver = { - .name = "cw1200_wlan_spi", -#ifdef CONFIG_PM - .pm = &cw1200_pm_ops, -#endif - }, -}; - -module_spi_driver(spi_driver); diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c deleted file mode 100644 index 34f97c31eecf..000000000000 --- a/drivers/net/wireless/cw1200/debug.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers - * DebugFS code - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include "cw1200.h" -#include "debug.h" -#include "fwio.h" - -/* join_status */ -static const char * const cw1200_debug_join_status[] = { - "passive", - "monitor", - "station (joining)", - "station (not authenticated yet)", - "station", - "adhoc", - "access point", -}; - -/* WSM_JOIN_PREAMBLE_... */ -static const char * const cw1200_debug_preamble[] = { - "long", - "short", - "long on 1 and 2 Mbps", -}; - - -static const char * const cw1200_debug_link_id[] = { - "OFF", - "REQ", - "SOFT", - "HARD", - "RESET", - "RESET_REMAP", -}; - -static const char *cw1200_debug_mode(int mode) -{ - switch (mode) { - case NL80211_IFTYPE_UNSPECIFIED: - return "unspecified"; - case NL80211_IFTYPE_MONITOR: - return "monitor"; - case NL80211_IFTYPE_STATION: - return "station"; - case NL80211_IFTYPE_ADHOC: - return "adhoc"; - case NL80211_IFTYPE_MESH_POINT: - return "mesh point"; - case NL80211_IFTYPE_AP: - return "access point"; - case NL80211_IFTYPE_P2P_CLIENT: - return "p2p client"; - case NL80211_IFTYPE_P2P_GO: - return "p2p go"; - default: - return "unsupported"; - } -} - -static void cw1200_queue_status_show(struct seq_file *seq, - struct cw1200_queue *q) -{ - int i; - seq_printf(seq, "Queue %d:\n", q->queue_id); - seq_printf(seq, " capacity: %zu\n", q->capacity); - seq_printf(seq, " queued: %zu\n", q->num_queued); - seq_printf(seq, " pending: %zu\n", q->num_pending); - seq_printf(seq, " sent: %zu\n", q->num_sent); - seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no"); - seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no"); - seq_puts(seq, " link map: 0-> "); - for (i = 0; i < q->stats->map_capacity; ++i) - seq_printf(seq, "%.2d ", q->link_map_cache[i]); - seq_printf(seq, "<-%zu\n", q->stats->map_capacity); -} - -static void cw1200_debug_print_map(struct seq_file *seq, - struct cw1200_common *priv, - const char *label, - u32 map) -{ - int i; - seq_printf(seq, "%s0-> ", label); - for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i) - seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : ".."); - seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1); -} - -static int cw1200_status_show(struct seq_file *seq, void *v) -{ - int i; - struct list_head *item; - struct cw1200_common *priv = seq->private; - struct cw1200_debug_priv *d = priv->debug; - - seq_puts(seq, "CW1200 Wireless LAN driver status\n"); - seq_printf(seq, "Hardware: %d.%d\n", - priv->wsm_caps.hw_id, - priv->wsm_caps.hw_subid); - seq_printf(seq, "Firmware: %s %d.%d\n", - cw1200_fw_types[priv->wsm_caps.fw_type], - priv->wsm_caps.fw_ver, - priv->wsm_caps.fw_build); - seq_printf(seq, "FW API: %d\n", - priv->wsm_caps.fw_api); - seq_printf(seq, "FW caps: 0x%.4X\n", - priv->wsm_caps.fw_cap); - seq_printf(seq, "FW label: '%s'\n", - priv->wsm_caps.fw_label); - seq_printf(seq, "Mode: %s%s\n", - cw1200_debug_mode(priv->mode), - priv->listening ? " (listening)" : ""); - seq_printf(seq, "Join state: %s\n", - cw1200_debug_join_status[priv->join_status]); - if (priv->channel) - seq_printf(seq, "Channel: %d%s\n", - priv->channel->hw_value, - priv->channel_switch_in_progress ? - " (switching)" : ""); - if (priv->rx_filter.promiscuous) - seq_puts(seq, "Filter: promisc\n"); - else if (priv->rx_filter.fcs) - seq_puts(seq, "Filter: fcs\n"); - if (priv->rx_filter.bssid) - seq_puts(seq, "Filter: bssid\n"); - if (!priv->disable_beacon_filter) - seq_puts(seq, "Filter: beacons\n"); - - if (priv->enable_beacon || - priv->mode == NL80211_IFTYPE_AP || - priv->mode == NL80211_IFTYPE_ADHOC || - priv->mode == NL80211_IFTYPE_MESH_POINT || - priv->mode == NL80211_IFTYPE_P2P_GO) - seq_printf(seq, "Beaconing: %s\n", - priv->enable_beacon ? - "enabled" : "disabled"); - - for (i = 0; i < 4; ++i) - seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i, - priv->edca.params[i].cwmin, - priv->edca.params[i].cwmax, - priv->edca.params[i].aifns, - priv->edca.params[i].txop_limit, - priv->edca.params[i].max_rx_lifetime); - - if (priv->join_status == CW1200_JOIN_STATUS_STA) { - static const char *pm_mode = "unknown"; - switch (priv->powersave_mode.mode) { - case WSM_PSM_ACTIVE: - pm_mode = "off"; - break; - case WSM_PSM_PS: - pm_mode = "on"; - break; - case WSM_PSM_FAST_PS: - pm_mode = "dynamic"; - break; - } - seq_printf(seq, "Preamble: %s\n", - cw1200_debug_preamble[priv->association_mode.preamble]); - seq_printf(seq, "AMPDU spcn: %d\n", - priv->association_mode.mpdu_start_spacing); - seq_printf(seq, "Basic rate: 0x%.8X\n", - le32_to_cpu(priv->association_mode.basic_rate_set)); - seq_printf(seq, "Bss lost: %d beacons\n", - priv->bss_params.beacon_lost_count); - seq_printf(seq, "AID: %d\n", - priv->bss_params.aid); - seq_printf(seq, "Rates: 0x%.8X\n", - priv->bss_params.operational_rate_set); - seq_printf(seq, "Powersave: %s\n", pm_mode); - } - seq_printf(seq, "HT: %s\n", - cw1200_is_ht(&priv->ht_info) ? "on" : "off"); - if (cw1200_is_ht(&priv->ht_info)) { - seq_printf(seq, "Greenfield: %s\n", - cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no"); - seq_printf(seq, "AMPDU dens: %d\n", - cw1200_ht_ampdu_density(&priv->ht_info)); - } - seq_printf(seq, "RSSI thold: %d\n", - priv->cqm_rssi_thold); - seq_printf(seq, "RSSI hyst: %d\n", - priv->cqm_rssi_hyst); - seq_printf(seq, "Long retr: %d\n", - priv->long_frame_max_tx_count); - seq_printf(seq, "Short retr: %d\n", - priv->short_frame_max_tx_count); - spin_lock_bh(&priv->tx_policy_cache.lock); - i = 0; - list_for_each(item, &priv->tx_policy_cache.used) - ++i; - spin_unlock_bh(&priv->tx_policy_cache.lock); - seq_printf(seq, "RC in use: %d\n", i); - - seq_puts(seq, "\n"); - for (i = 0; i < 4; ++i) { - cw1200_queue_status_show(seq, &priv->tx_queue[i]); - seq_puts(seq, "\n"); - } - - cw1200_debug_print_map(seq, priv, "Link map: ", - priv->link_id_map); - cw1200_debug_print_map(seq, priv, "Asleep map: ", - priv->sta_asleep_mask); - cw1200_debug_print_map(seq, priv, "PSPOLL map: ", - priv->pspoll_mask); - - seq_puts(seq, "\n"); - - for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { - if (priv->link_id_db[i].status) { - seq_printf(seq, "Link %d: %s, %pM\n", - i + 1, - cw1200_debug_link_id[priv->link_id_db[i].status], - priv->link_id_db[i].mac); - } - } - - seq_puts(seq, "\n"); - - seq_printf(seq, "BH status: %s\n", - atomic_read(&priv->bh_term) ? "terminated" : "alive"); - seq_printf(seq, "Pending RX: %d\n", - atomic_read(&priv->bh_rx)); - seq_printf(seq, "Pending TX: %d\n", - atomic_read(&priv->bh_tx)); - if (priv->bh_error) - seq_printf(seq, "BH errcode: %d\n", - priv->bh_error); - seq_printf(seq, "TX bufs: %d x %d bytes\n", - priv->wsm_caps.input_buffers, - priv->wsm_caps.input_buffer_size); - seq_printf(seq, "Used bufs: %d\n", - priv->hw_bufs_used); - seq_printf(seq, "Powermgmt: %s\n", - priv->powersave_enabled ? "on" : "off"); - seq_printf(seq, "Device: %s\n", - priv->device_can_sleep ? "asleep" : "awake"); - - spin_lock(&priv->wsm_cmd.lock); - seq_printf(seq, "WSM status: %s\n", - priv->wsm_cmd.done ? "idle" : "active"); - seq_printf(seq, "WSM cmd: 0x%.4X (%td bytes)\n", - priv->wsm_cmd.cmd, priv->wsm_cmd.len); - seq_printf(seq, "WSM retval: %d\n", - priv->wsm_cmd.ret); - spin_unlock(&priv->wsm_cmd.lock); - - seq_printf(seq, "Datapath: %s\n", - atomic_read(&priv->tx_lock) ? "locked" : "unlocked"); - if (atomic_read(&priv->tx_lock)) - seq_printf(seq, "TXlock cnt: %d\n", - atomic_read(&priv->tx_lock)); - - seq_printf(seq, "TXed: %d\n", - d->tx); - seq_printf(seq, "AGG TXed: %d\n", - d->tx_agg); - seq_printf(seq, "MULTI TXed: %d (%d)\n", - d->tx_multi, d->tx_multi_frames); - seq_printf(seq, "RXed: %d\n", - d->rx); - seq_printf(seq, "AGG RXed: %d\n", - d->rx_agg); - seq_printf(seq, "TX miss: %d\n", - d->tx_cache_miss); - seq_printf(seq, "TX align: %d\n", - d->tx_align); - seq_printf(seq, "TX burst: %d\n", - d->tx_burst); - seq_printf(seq, "TX TTL: %d\n", - d->tx_ttl); - seq_printf(seq, "Scan: %s\n", - atomic_read(&priv->scan.in_progress) ? "active" : "idle"); - - return 0; -} - -static int cw1200_status_open(struct inode *inode, struct file *file) -{ - return single_open(file, &cw1200_status_show, - inode->i_private); -} - -static const struct file_operations fops_status = { - .open = cw1200_status_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int cw1200_counters_show(struct seq_file *seq, void *v) -{ - int ret; - struct cw1200_common *priv = seq->private; - struct wsm_mib_counters_table counters; - - ret = wsm_get_counters_table(priv, &counters); - if (ret) - return ret; - -#define PUT_COUNTER(tab, name) \ - seq_printf(seq, "%s:" tab "%d\n", #name, \ - __le32_to_cpu(counters.name)) - - PUT_COUNTER("\t\t", plcp_errors); - PUT_COUNTER("\t\t", fcs_errors); - PUT_COUNTER("\t\t", tx_packets); - PUT_COUNTER("\t\t", rx_packets); - PUT_COUNTER("\t\t", rx_packet_errors); - PUT_COUNTER("\t", rx_decryption_failures); - PUT_COUNTER("\t\t", rx_mic_failures); - PUT_COUNTER("\t", rx_no_key_failures); - PUT_COUNTER("\t", tx_multicast_frames); - PUT_COUNTER("\t", tx_frames_success); - PUT_COUNTER("\t", tx_frame_failures); - PUT_COUNTER("\t", tx_frames_retried); - PUT_COUNTER("\t", tx_frames_multi_retried); - PUT_COUNTER("\t", rx_frame_duplicates); - PUT_COUNTER("\t\t", rts_success); - PUT_COUNTER("\t\t", rts_failures); - PUT_COUNTER("\t\t", ack_failures); - PUT_COUNTER("\t", rx_multicast_frames); - PUT_COUNTER("\t", rx_frames_success); - PUT_COUNTER("\t", rx_cmac_icv_errors); - PUT_COUNTER("\t\t", rx_cmac_replays); - PUT_COUNTER("\t", rx_mgmt_ccmp_replays); - -#undef PUT_COUNTER - - return 0; -} - -static int cw1200_counters_open(struct inode *inode, struct file *file) -{ - return single_open(file, &cw1200_counters_show, - inode->i_private); -} - -static const struct file_operations fops_counters = { - .open = cw1200_counters_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static ssize_t cw1200_wsm_dumps(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct cw1200_common *priv = file->private_data; - char buf[1]; - - if (!count) - return -EINVAL; - if (copy_from_user(buf, user_buf, 1)) - return -EFAULT; - - if (buf[0] == '1') - priv->wsm_enable_wsm_dumps = 1; - else - priv->wsm_enable_wsm_dumps = 0; - - return count; -} - -static const struct file_operations fops_wsm_dumps = { - .open = simple_open, - .write = cw1200_wsm_dumps, - .llseek = default_llseek, -}; - -int cw1200_debug_init(struct cw1200_common *priv) -{ - int ret = -ENOMEM; - struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), - GFP_KERNEL); - priv->debug = d; - if (!d) - return ret; - - d->debugfs_phy = debugfs_create_dir("cw1200", - priv->hw->wiphy->debugfsdir); - if (!d->debugfs_phy) - goto err; - - if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, - priv, &fops_status)) - goto err; - - if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy, - priv, &fops_counters)) - goto err; - - if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy, - priv, &fops_wsm_dumps)) - goto err; - - return 0; - -err: - priv->debug = NULL; - debugfs_remove_recursive(d->debugfs_phy); - kfree(d); - return ret; -} - -void cw1200_debug_release(struct cw1200_common *priv) -{ - struct cw1200_debug_priv *d = priv->debug; - if (d) { - debugfs_remove_recursive(d->debugfs_phy); - priv->debug = NULL; - kfree(d); - } -} diff --git a/drivers/net/wireless/cw1200/debug.h b/drivers/net/wireless/cw1200/debug.h deleted file mode 100644 index b525aba53bfc..000000000000 --- a/drivers/net/wireless/cw1200/debug.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * DebugFS code for ST-Ericsson CW1200 mac80211 driver - * - * Copyright (c) 2011, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef CW1200_DEBUG_H_INCLUDED -#define CW1200_DEBUG_H_INCLUDED - -struct cw1200_debug_priv { - struct dentry *debugfs_phy; - int tx; - int tx_agg; - int rx; - int rx_agg; - int tx_multi; - int tx_multi_frames; - int tx_cache_miss; - int tx_align; - int tx_ttl; - int tx_burst; - int ba_cnt; - int ba_acc; - int ba_cnt_rx; - int ba_acc_rx; -}; - -int cw1200_debug_init(struct cw1200_common *priv); -void cw1200_debug_release(struct cw1200_common *priv); - -static inline void cw1200_debug_txed(struct cw1200_common *priv) -{ - ++priv->debug->tx; -} - -static inline void cw1200_debug_txed_agg(struct cw1200_common *priv) -{ - ++priv->debug->tx_agg; -} - -static inline void cw1200_debug_txed_multi(struct cw1200_common *priv, - int count) -{ - ++priv->debug->tx_multi; - priv->debug->tx_multi_frames += count; -} - -static inline void cw1200_debug_rxed(struct cw1200_common *priv) -{ - ++priv->debug->rx; -} - -static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv) -{ - ++priv->debug->rx_agg; -} - -static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv) -{ - ++priv->debug->tx_cache_miss; -} - -static inline void cw1200_debug_tx_align(struct cw1200_common *priv) -{ - ++priv->debug->tx_align; -} - -static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) -{ - ++priv->debug->tx_ttl; -} - -static inline void cw1200_debug_tx_burst(struct cw1200_common *priv) -{ - ++priv->debug->tx_burst; -} - -static inline void cw1200_debug_ba(struct cw1200_common *priv, - int ba_cnt, int ba_acc, - int ba_cnt_rx, int ba_acc_rx) -{ - priv->debug->ba_cnt = ba_cnt; - priv->debug->ba_acc = ba_acc; - priv->debug->ba_cnt_rx = ba_cnt_rx; - priv->debug->ba_acc_rx = ba_acc_rx; -} - -#endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c deleted file mode 100644 index 30e7646d04af..000000000000 --- a/drivers/net/wireless/cw1200/fwio.c +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * Based on: - * ST-Ericsson UMAC CW1200 driver which is - * Copyright (c) 2010, ST-Ericsson - * Author: Ajitpal Singh - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include - -#include "cw1200.h" -#include "fwio.h" -#include "hwio.h" -#include "hwbus.h" -#include "bh.h" - -static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision) -{ - int hw_type = -1; - u32 silicon_type = (config_reg_val >> 24) & 0x7; - u32 silicon_vers = (config_reg_val >> 31) & 0x1; - - switch (silicon_type) { - case 0x00: - *major_revision = 1; - hw_type = HIF_9000_SILICON_VERSATILE; - break; - case 0x01: - case 0x02: /* CW1x00 */ - case 0x04: /* CW1x60 */ - *major_revision = silicon_type; - if (silicon_vers) - hw_type = HIF_8601_VERSATILE; - else - hw_type = HIF_8601_SILICON; - break; - default: - break; - } - - return hw_type; -} - -static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) -{ - int ret, block, num_blocks; - unsigned i; - u32 val32; - u32 put = 0, get = 0; - u8 *buf = NULL; - const char *fw_path; - const struct firmware *firmware = NULL; - - /* Macroses are local. */ -#define APB_WRITE(reg, val) \ - do { \ - ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \ - if (ret < 0) \ - goto exit; \ - } while (0) -#define APB_WRITE2(reg, val) \ - do { \ - ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \ - if (ret < 0) \ - goto free_buffer; \ - } while (0) -#define APB_READ(reg, val) \ - do { \ - ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \ - if (ret < 0) \ - goto free_buffer; \ - } while (0) -#define REG_WRITE(reg, val) \ - do { \ - ret = cw1200_reg_write_32(priv, (reg), (val)); \ - if (ret < 0) \ - goto exit; \ - } while (0) -#define REG_READ(reg, val) \ - do { \ - ret = cw1200_reg_read_32(priv, (reg), &(val)); \ - if (ret < 0) \ - goto exit; \ - } while (0) - - switch (priv->hw_revision) { - case CW1200_HW_REV_CUT10: - fw_path = FIRMWARE_CUT10; - if (!priv->sdd_path) - priv->sdd_path = SDD_FILE_10; - break; - case CW1200_HW_REV_CUT11: - fw_path = FIRMWARE_CUT11; - if (!priv->sdd_path) - priv->sdd_path = SDD_FILE_11; - break; - case CW1200_HW_REV_CUT20: - fw_path = FIRMWARE_CUT20; - if (!priv->sdd_path) - priv->sdd_path = SDD_FILE_20; - break; - case CW1200_HW_REV_CUT22: - fw_path = FIRMWARE_CUT22; - if (!priv->sdd_path) - priv->sdd_path = SDD_FILE_22; - break; - case CW1X60_HW_REV: - fw_path = FIRMWARE_CW1X60; - if (!priv->sdd_path) - priv->sdd_path = SDD_FILE_CW1X60; - break; - default: - pr_err("Invalid silicon revision %d.\n", priv->hw_revision); - return -EINVAL; - } - - /* Initialize common registers */ - APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); - APB_WRITE(DOWNLOAD_PUT_REG, 0); - APB_WRITE(DOWNLOAD_GET_REG, 0); - APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); - APB_WRITE(DOWNLOAD_FLAGS_REG, 0); - - /* Write the NOP Instruction */ - REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); - REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); - - /* Release CPU from RESET */ - REG_READ(ST90TDS_CONFIG_REG_ID, val32); - val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT; - REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); - - /* Enable Clock */ - val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT; - REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); - - /* Load a firmware file */ - ret = request_firmware(&firmware, fw_path, priv->pdev); - if (ret) { - pr_err("Can't load firmware file %s.\n", fw_path); - goto exit; - } - - buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); - if (!buf) { - pr_err("Can't allocate firmware load buffer.\n"); - ret = -ENOMEM; - goto firmware_release; - } - - /* Check if the bootloader is ready */ - for (i = 0; i < 100; i += 1 + i / 2) { - APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32); - if (val32 == DOWNLOAD_I_AM_HERE) - break; - mdelay(i); - } /* End of for loop */ - - if (val32 != DOWNLOAD_I_AM_HERE) { - pr_err("Bootloader is not ready.\n"); - ret = -ETIMEDOUT; - goto free_buffer; - } - - /* Calculcate number of download blocks */ - num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1; - - /* Updating the length in Download Ctrl Area */ - val32 = firmware->size; /* Explicit cast from size_t to u32 */ - APB_WRITE2(DOWNLOAD_IMAGE_SIZE_REG, val32); - - /* Firmware downloading loop */ - for (block = 0; block < num_blocks; block++) { - size_t tx_size; - size_t block_size; - - /* check the download status */ - APB_READ(DOWNLOAD_STATUS_REG, val32); - if (val32 != DOWNLOAD_PENDING) { - pr_err("Bootloader reported error %d.\n", val32); - ret = -EIO; - goto free_buffer; - } - - /* loop until put - get <= 24K */ - for (i = 0; i < 100; i++) { - APB_READ(DOWNLOAD_GET_REG, get); - if ((put - get) <= - (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) - break; - mdelay(i); - } - - if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) { - pr_err("Timeout waiting for FIFO.\n"); - ret = -ETIMEDOUT; - goto free_buffer; - } - - /* calculate the block size */ - tx_size = block_size = min_t(size_t, firmware->size - put, - DOWNLOAD_BLOCK_SIZE); - - memcpy(buf, &firmware->data[put], block_size); - if (block_size < DOWNLOAD_BLOCK_SIZE) { - memset(&buf[block_size], 0, - DOWNLOAD_BLOCK_SIZE - block_size); - tx_size = DOWNLOAD_BLOCK_SIZE; - } - - /* send the block to sram */ - ret = cw1200_apb_write(priv, - CW1200_APB(DOWNLOAD_FIFO_OFFSET + - (put & (DOWNLOAD_FIFO_SIZE - 1))), - buf, tx_size); - if (ret < 0) { - pr_err("Can't write firmware block @ %d!\n", - put & (DOWNLOAD_FIFO_SIZE - 1)); - goto free_buffer; - } - - /* update the put register */ - put += block_size; - APB_WRITE2(DOWNLOAD_PUT_REG, put); - } /* End of firmware download loop */ - - /* Wait for the download completion */ - for (i = 0; i < 300; i += 1 + i / 2) { - APB_READ(DOWNLOAD_STATUS_REG, val32); - if (val32 != DOWNLOAD_PENDING) - break; - mdelay(i); - } - if (val32 != DOWNLOAD_SUCCESS) { - pr_err("Wait for download completion failed: 0x%.8X\n", val32); - ret = -ETIMEDOUT; - goto free_buffer; - } else { - pr_info("Firmware download completed.\n"); - ret = 0; - } - -free_buffer: - kfree(buf); -firmware_release: - release_firmware(firmware); -exit: - return ret; - -#undef APB_WRITE -#undef APB_WRITE2 -#undef APB_READ -#undef REG_WRITE -#undef REG_READ -} - - -static int config_reg_read(struct cw1200_common *priv, u32 *val) -{ - switch (priv->hw_type) { - case HIF_9000_SILICON_VERSATILE: { - u16 val16; - int ret = cw1200_reg_read_16(priv, - ST90TDS_CONFIG_REG_ID, - &val16); - if (ret < 0) - return ret; - *val = val16; - return 0; - } - case HIF_8601_VERSATILE: - case HIF_8601_SILICON: - default: - cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val); - break; - } - return 0; -} - -static int config_reg_write(struct cw1200_common *priv, u32 val) -{ - switch (priv->hw_type) { - case HIF_9000_SILICON_VERSATILE: - return cw1200_reg_write_16(priv, - ST90TDS_CONFIG_REG_ID, - (u16)val); - case HIF_8601_VERSATILE: - case HIF_8601_SILICON: - default: - return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val); - } - return 0; -} - -int cw1200_load_firmware(struct cw1200_common *priv) -{ - int ret; - int i; - u32 val32; - u16 val16; - int major_revision = -1; - - /* Read CONFIG Register */ - ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); - if (ret < 0) { - pr_err("Can't read config register.\n"); - goto out; - } - - if (val32 == 0 || val32 == 0xffffffff) { - pr_err("Bad config register value (0x%08x)\n", val32); - ret = -EIO; - goto out; - } - - priv->hw_type = cw1200_get_hw_type(val32, &major_revision); - if (priv->hw_type < 0) { - pr_err("Can't deduce hardware type.\n"); - ret = -ENOTSUPP; - goto out; - } - - /* Set DPLL Reg value, and read back to confirm writes work */ - ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, - cw1200_dpll_from_clk(priv->hw_refclk)); - if (ret < 0) { - pr_err("Can't write DPLL register.\n"); - goto out; - } - - msleep(20); - - ret = cw1200_reg_read_32(priv, - ST90TDS_TSET_GEN_R_W_REG_ID, &val32); - if (ret < 0) { - pr_err("Can't read DPLL register.\n"); - goto out; - } - - if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) { - pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n", - cw1200_dpll_from_clk(priv->hw_refclk), val32); - ret = -EIO; - goto out; - } - - /* Set wakeup bit in device */ - ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16); - if (ret < 0) { - pr_err("set_wakeup: can't read control register.\n"); - goto out; - } - - ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, - val16 | ST90TDS_CONT_WUP_BIT); - if (ret < 0) { - pr_err("set_wakeup: can't write control register.\n"); - goto out; - } - - /* Wait for wakeup */ - for (i = 0; i < 300; i += (1 + i / 2)) { - ret = cw1200_reg_read_16(priv, - ST90TDS_CONTROL_REG_ID, &val16); - if (ret < 0) { - pr_err("wait_for_wakeup: can't read control register.\n"); - goto out; - } - - if (val16 & ST90TDS_CONT_RDY_BIT) - break; - - msleep(i); - } - - if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) { - pr_err("wait_for_wakeup: device is not responding.\n"); - ret = -ETIMEDOUT; - goto out; - } - - switch (major_revision) { - case 1: - /* CW1200 Hardware detection logic : Check for CUT1.1 */ - ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32); - if (ret) { - pr_err("HW detection: can't read CUT ID.\n"); - goto out; - } - - switch (val32) { - case CW1200_CUT_11_ID_STR: - pr_info("CW1x00 Cut 1.1 silicon detected.\n"); - priv->hw_revision = CW1200_HW_REV_CUT11; - break; - default: - pr_info("CW1x00 Cut 1.0 silicon detected.\n"); - priv->hw_revision = CW1200_HW_REV_CUT10; - break; - } - - /* According to ST-E, CUT<2.0 has busted BA TID0-3. - Just disable it entirely... - */ - priv->ba_rx_tid_mask = 0; - priv->ba_tx_tid_mask = 0; - break; - case 2: { - u32 ar1, ar2, ar3; - ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1); - if (ret) { - pr_err("(1) HW detection: can't read CUT ID\n"); - goto out; - } - ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2); - if (ret) { - pr_err("(2) HW detection: can't read CUT ID.\n"); - goto out; - } - - ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3); - if (ret) { - pr_err("(3) HW detection: can't read CUT ID.\n"); - goto out; - } - - if (ar1 == CW1200_CUT_22_ID_STR1 && - ar2 == CW1200_CUT_22_ID_STR2 && - ar3 == CW1200_CUT_22_ID_STR3) { - pr_info("CW1x00 Cut 2.2 silicon detected.\n"); - priv->hw_revision = CW1200_HW_REV_CUT22; - } else { - pr_info("CW1x00 Cut 2.0 silicon detected.\n"); - priv->hw_revision = CW1200_HW_REV_CUT20; - } - break; - } - case 4: - pr_info("CW1x60 silicon detected.\n"); - priv->hw_revision = CW1X60_HW_REV; - break; - default: - pr_err("Unsupported silicon major revision %d.\n", - major_revision); - ret = -ENOTSUPP; - goto out; - } - - /* Checking for access mode */ - ret = config_reg_read(priv, &val32); - if (ret < 0) { - pr_err("Can't read config register.\n"); - goto out; - } - - if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) { - pr_err("Device is already in QUEUE mode!\n"); - ret = -EINVAL; - goto out; - } - - switch (priv->hw_type) { - case HIF_8601_SILICON: - if (priv->hw_revision == CW1X60_HW_REV) { - pr_err("Can't handle CW1160/1260 firmware load yet.\n"); - ret = -ENOTSUPP; - goto out; - } - ret = cw1200_load_firmware_cw1200(priv); - break; - default: - pr_err("Can't perform firmware load for hw type %d.\n", - priv->hw_type); - ret = -ENOTSUPP; - goto out; - } - if (ret < 0) { - pr_err("Firmware load error.\n"); - goto out; - } - - /* Enable interrupt signalling */ - priv->hwbus_ops->lock(priv->hwbus_priv); - ret = __cw1200_irq_enable(priv, 1); - priv->hwbus_ops->unlock(priv->hwbus_priv); - if (ret < 0) - goto unsubscribe; - - /* Configure device for MESSSAGE MODE */ - ret = config_reg_read(priv, &val32); - if (ret < 0) { - pr_err("Can't read config register.\n"); - goto unsubscribe; - } - ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT); - if (ret < 0) { - pr_err("Can't write config register.\n"); - goto unsubscribe; - } - - /* Unless we read the CONFIG Register we are - * not able to get an interrupt - */ - mdelay(10); - config_reg_read(priv, &val32); - -out: - return ret; - -unsubscribe: - /* Disable interrupt signalling */ - priv->hwbus_ops->lock(priv->hwbus_priv); - ret = __cw1200_irq_enable(priv, 0); - priv->hwbus_ops->unlock(priv->hwbus_priv); - return ret; -} diff --git a/drivers/net/wireless/cw1200/fwio.h b/drivers/net/wireless/cw1200/fwio.h deleted file mode 100644 index ea3099362cdf..000000000000 --- a/drivers/net/wireless/cw1200/fwio.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Firmware API for mac80211 ST-Ericsson CW1200 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * Based on: - * ST-Ericsson UMAC CW1200 driver which is - * Copyright (c) 2010, ST-Ericsson - * Author: Ajitpal Singh - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef FWIO_H_INCLUDED -#define FWIO_H_INCLUDED - -#define BOOTLOADER_CW1X60 "boot_cw1x60.bin" -#define FIRMWARE_CW1X60 "wsm_cw1x60.bin" -#define FIRMWARE_CUT22 "wsm_22.bin" -#define FIRMWARE_CUT20 "wsm_20.bin" -#define FIRMWARE_CUT11 "wsm_11.bin" -#define FIRMWARE_CUT10 "wsm_10.bin" -#define SDD_FILE_CW1X60 "sdd_cw1x60.bin" -#define SDD_FILE_22 "sdd_22.bin" -#define SDD_FILE_20 "sdd_20.bin" -#define SDD_FILE_11 "sdd_11.bin" -#define SDD_FILE_10 "sdd_10.bin" - -int cw1200_load_firmware(struct cw1200_common *priv); - -/* SDD definitions */ -#define SDD_PTA_CFG_ELT_ID 0xEB -#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5 -u32 cw1200_dpll_from_clk(u16 clk); - -#endif diff --git a/drivers/net/wireless/cw1200/hwbus.h b/drivers/net/wireless/cw1200/hwbus.h deleted file mode 100644 index 8b2fc831c3de..000000000000 --- a/drivers/net/wireless/cw1200/hwbus.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Common hwbus abstraction layer interface for cw1200 wireless driver - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef CW1200_HWBUS_H -#define CW1200_HWBUS_H - -struct hwbus_priv; - -void cw1200_irq_handler(struct cw1200_common *priv); - -/* This MUST be wrapped with hwbus_ops->lock/unlock! */ -int __cw1200_irq_enable(struct cw1200_common *priv, int enable); - -struct hwbus_ops { - int (*hwbus_memcpy_fromio)(struct hwbus_priv *self, unsigned int addr, - void *dst, int count); - int (*hwbus_memcpy_toio)(struct hwbus_priv *self, unsigned int addr, - const void *src, int count); - void (*lock)(struct hwbus_priv *self); - void (*unlock)(struct hwbus_priv *self); - size_t (*align_size)(struct hwbus_priv *self, size_t size); - int (*power_mgmt)(struct hwbus_priv *self, bool suspend); -}; - -#endif /* CW1200_HWBUS_H */ diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/cw1200/hwio.c deleted file mode 100644 index ff230b7aeedd..000000000000 --- a/drivers/net/wireless/cw1200/hwio.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Low-level device IO routines for ST-Ericsson CW1200 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * Based on: - * ST-Ericsson UMAC CW1200 driver, which is - * Copyright (c) 2010, ST-Ericsson - * Author: Ajitpal Singh - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include - -#include "cw1200.h" -#include "hwio.h" -#include "hwbus.h" - - /* Sdio addr is 4*spi_addr */ -#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2) -#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \ - ((((buf_id) & 0x1F) << 7) \ - | (((mpf) & 1) << 6) \ - | (((rfu) & 1) << 5) \ - | (((reg_id_ofs) & 0x1F) << 0)) -#define MAX_RETRY 3 - - -static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr, - void *buf, size_t buf_len, int buf_id) -{ - u16 addr_sdio; - u32 sdio_reg_addr_17bit; - - /* Check if buffer is aligned to 4 byte boundary */ - if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { - pr_err("buffer is not aligned.\n"); - return -EINVAL; - } - - /* Convert to SDIO Register Address */ - addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); - sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); - - return priv->hwbus_ops->hwbus_memcpy_fromio(priv->hwbus_priv, - sdio_reg_addr_17bit, - buf, buf_len); -} - -static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr, - const void *buf, size_t buf_len, int buf_id) -{ - u16 addr_sdio; - u32 sdio_reg_addr_17bit; - - /* Convert to SDIO Register Address */ - addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); - sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); - - return priv->hwbus_ops->hwbus_memcpy_toio(priv->hwbus_priv, - sdio_reg_addr_17bit, - buf, buf_len); -} - -static inline int __cw1200_reg_read_32(struct cw1200_common *priv, - u16 addr, u32 *val) -{ - __le32 tmp; - int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0); - *val = le32_to_cpu(tmp); - return i; -} - -static inline int __cw1200_reg_write_32(struct cw1200_common *priv, - u16 addr, u32 val) -{ - __le32 tmp = cpu_to_le32(val); - return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0); -} - -static inline int __cw1200_reg_read_16(struct cw1200_common *priv, - u16 addr, u16 *val) -{ - __le16 tmp; - int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0); - *val = le16_to_cpu(tmp); - return i; -} - -static inline int __cw1200_reg_write_16(struct cw1200_common *priv, - u16 addr, u16 val) -{ - __le16 tmp = cpu_to_le16(val); - return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0); -} - -int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf, - size_t buf_len) -{ - int ret; - priv->hwbus_ops->lock(priv->hwbus_priv); - ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0); - priv->hwbus_ops->unlock(priv->hwbus_priv); - return ret; -} - -int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf, - size_t buf_len) -{ - int ret; - priv->hwbus_ops->lock(priv->hwbus_priv); - ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0); - priv->hwbus_ops->unlock(priv->hwbus_priv); - return ret; -} - -int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len) -{ - int ret, retry = 1; - int buf_id_rx = priv->buf_id_rx; - - priv->hwbus_ops->lock(priv->hwbus_priv); - - while (retry <= MAX_RETRY) { - ret = __cw1200_reg_read(priv, - ST90TDS_IN_OUT_QUEUE_REG_ID, buf, - buf_len, buf_id_rx + 1); - if (!ret) { - buf_id_rx = (buf_id_rx + 1) & 3; - priv->buf_id_rx = buf_id_rx; - break; - } else { - retry++; - mdelay(1); - pr_err("error :[%d]\n", ret); - } - } - - priv->hwbus_ops->unlock(priv->hwbus_priv); - return ret; -} - -int cw1200_data_write(struct cw1200_common *priv, const void *buf, - size_t buf_len) -{ - int ret, retry = 1; - int buf_id_tx = priv->buf_id_tx; - - priv->hwbus_ops->lock(priv->hwbus_priv); - - while (retry <= MAX_RETRY) { - ret = __cw1200_reg_write(priv, - ST90TDS_IN_OUT_QUEUE_REG_ID, buf, - buf_len, buf_id_tx); - if (!ret) { - buf_id_tx = (buf_id_tx + 1) & 31; - priv->buf_id_tx = buf_id_tx; - break; - } else { - retry++; - mdelay(1); - pr_err("error :[%d]\n", ret); - } - } - - priv->hwbus_ops->unlock(priv->hwbus_priv); - return ret; -} - -int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, - size_t buf_len, u32 prefetch, u16 port_addr) -{ - u32 val32 = 0; - int i, ret; - - if ((buf_len / 2) >= 0x1000) { - pr_err("Can't read more than 0xfff words.\n"); - return -EINVAL; - } - - priv->hwbus_ops->lock(priv->hwbus_priv); - /* Write address */ - ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); - if (ret < 0) { - pr_err("Can't write address register.\n"); - goto out; - } - - /* Read CONFIG Register Value - We will read 32 bits */ - ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); - if (ret < 0) { - pr_err("Can't read config register.\n"); - goto out; - } - - /* Set PREFETCH bit */ - ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, - val32 | prefetch); - if (ret < 0) { - pr_err("Can't write prefetch bit.\n"); - goto out; - } - - /* Check for PRE-FETCH bit to be cleared */ - for (i = 0; i < 20; i++) { - ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); - if (ret < 0) { - pr_err("Can't check prefetch bit.\n"); - goto out; - } - if (!(val32 & prefetch)) - break; - - mdelay(i); - } - - if (val32 & prefetch) { - pr_err("Prefetch bit is not cleared.\n"); - goto out; - } - - /* Read data port */ - ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0); - if (ret < 0) { - pr_err("Can't read data port.\n"); - goto out; - } - -out: - priv->hwbus_ops->unlock(priv->hwbus_priv); - return ret; -} - -int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, - size_t buf_len) -{ - int ret; - - if ((buf_len / 2) >= 0x1000) { - pr_err("Can't write more than 0xfff words.\n"); - return -EINVAL; - } - - priv->hwbus_ops->lock(priv->hwbus_priv); - - /* Write address */ - ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); - if (ret < 0) { - pr_err("Can't write address register.\n"); - goto out; - } - - /* Write data port */ - ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID, - buf, buf_len, 0); - if (ret < 0) { - pr_err("Can't write data port.\n"); - goto out; - } - -out: - priv->hwbus_ops->unlock(priv->hwbus_priv); - return ret; -} - -int __cw1200_irq_enable(struct cw1200_common *priv, int enable) -{ - u32 val32; - u16 val16; - int ret; - - if (HIF_8601_SILICON == priv->hw_type) { - ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); - if (ret < 0) { - pr_err("Can't read config register.\n"); - return ret; - } - - if (enable) - val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE; - else - val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE; - - ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32); - if (ret < 0) { - pr_err("Can't write config register.\n"); - return ret; - } - } else { - ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16); - if (ret < 0) { - pr_err("Can't read control register.\n"); - return ret; - } - - if (enable) - val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE; - else - val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE; - - ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16); - if (ret < 0) { - pr_err("Can't write control register.\n"); - return ret; - } - } - return 0; -} diff --git a/drivers/net/wireless/cw1200/hwio.h b/drivers/net/wireless/cw1200/hwio.h deleted file mode 100644 index ddf52669dc5b..000000000000 --- a/drivers/net/wireless/cw1200/hwio.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Low-level API for mac80211 ST-Ericsson CW1200 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * Based on: - * ST-Ericsson UMAC CW1200 driver which is - * Copyright (c) 2010, ST-Ericsson - * Author: Ajitpal Singh - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef CW1200_HWIO_H_INCLUDED -#define CW1200_HWIO_H_INCLUDED - -/* extern */ struct cw1200_common; - -#define CW1200_CUT_11_ID_STR (0x302E3830) -#define CW1200_CUT_22_ID_STR1 (0x302e3132) -#define CW1200_CUT_22_ID_STR2 (0x32302e30) -#define CW1200_CUT_22_ID_STR3 (0x3335) -#define CW1200_CUT_ID_ADDR (0xFFF17F90) -#define CW1200_CUT2_ID_ADDR (0xFFF1FF90) - -/* Download control area */ -/* boot loader start address in SRAM */ -#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000) -/* 32K, 0x4000 to 0xDFFF */ -#define DOWNLOAD_FIFO_OFFSET (0x00004000) -/* 32K */ -#define DOWNLOAD_FIFO_SIZE (0x00008000) -/* 128 bytes, 0xFF80 to 0xFFFF */ -#define DOWNLOAD_CTRL_OFFSET (0x0000FF80) -#define DOWNLOAD_CTRL_DATA_DWORDS (32-6) - -struct download_cntl_t { - /* size of whole firmware file (including Cheksum), host init */ - u32 image_size; - /* downloading flags */ - u32 flags; - /* No. of bytes put into the download, init & updated by host */ - u32 put; - /* last traced program counter, last ARM reg_pc */ - u32 trace_pc; - /* No. of bytes read from the download, host init, device updates */ - u32 get; - /* r0, boot losader status, host init to pending, device updates */ - u32 status; - /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */ - u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS]; -}; - -#define DOWNLOAD_IMAGE_SIZE_REG \ - (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size)) -#define DOWNLOAD_FLAGS_REG \ - (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags)) -#define DOWNLOAD_PUT_REG \ - (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put)) -#define DOWNLOAD_TRACE_PC_REG \ - (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc)) -#define DOWNLOAD_GET_REG \ - (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get)) -#define DOWNLOAD_STATUS_REG \ - (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status)) -#define DOWNLOAD_DEBUG_DATA_REG \ - (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data)) -#define DOWNLOAD_DEBUG_DATA_LEN (108) - -#define DOWNLOAD_BLOCK_SIZE (1024) - -/* For boot loader detection */ -#define DOWNLOAD_ARE_YOU_HERE (0x87654321) -#define DOWNLOAD_I_AM_HERE (0x12345678) - -/* Download error code */ -#define DOWNLOAD_PENDING (0xFFFFFFFF) -#define DOWNLOAD_SUCCESS (0) -#define DOWNLOAD_EXCEPTION (1) -#define DOWNLOAD_ERR_MEM_1 (2) -#define DOWNLOAD_ERR_MEM_2 (3) -#define DOWNLOAD_ERR_SOFTWARE (4) -#define DOWNLOAD_ERR_FILE_SIZE (5) -#define DOWNLOAD_ERR_CHECKSUM (6) -#define DOWNLOAD_ERR_OVERFLOW (7) -#define DOWNLOAD_ERR_IMAGE (8) -#define DOWNLOAD_ERR_HOST (9) -#define DOWNLOAD_ERR_ABORT (10) - - -#define SYS_BASE_ADDR_SILICON (0) -#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000) -#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON) - -#define CW1200_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr)) - -/* Device register definitions */ - -/* WBF - SPI Register Addresses */ -#define ST90TDS_ADDR_ID_BASE (0x0000) -/* 16/32 bits */ -#define ST90TDS_CONFIG_REG_ID (0x0000) -/* 16/32 bits */ -#define ST90TDS_CONTROL_REG_ID (0x0001) -/* 16 bits, Q mode W/R */ -#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002) -/* 32 bits, AHB bus R/W */ -#define ST90TDS_AHB_DPORT_REG_ID (0x0003) -/* 16/32 bits */ -#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004) -/* 32 bits, APB bus R/W */ -#define ST90TDS_SRAM_DPORT_REG_ID (0x0005) -/* 32 bits, t_settle/general */ -#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006) -/* 16 bits, Q mode read, no length */ -#define ST90TDS_FRAME_OUT_REG_ID (0x0007) -#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID) - -/* WBF - Control register bit set */ -/* next o/p length, bit 11 to 0 */ -#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF) -#define ST90TDS_CONT_WUP_BIT (BIT(12)) -#define ST90TDS_CONT_RDY_BIT (BIT(13)) -#define ST90TDS_CONT_IRQ_ENABLE (BIT(14)) -#define ST90TDS_CONT_RDY_ENABLE (BIT(15)) -#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15)) - -/* SPI Config register bit set */ -#define ST90TDS_CONFIG_FRAME_BIT (BIT(2)) -#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4)) -#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3)) -#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4)) -#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5)) -#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6)) -#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7)) -/* TBD: Sure??? */ -#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7)) -#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8)) -#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9)) -/* QueueM */ -#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10)) -/* AHB bus */ -#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11)) -#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12)) -/* APB bus */ -#define ST90TDS_CONFIG_PRFETCH_BIT (BIT(13)) -/* cpu reset */ -#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14)) -#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15)) - -/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */ -#define ST90TDS_CONF_IRQ_ENABLE (BIT(16)) -#define ST90TDS_CONF_RDY_ENABLE (BIT(17)) -#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17)) - -int cw1200_data_read(struct cw1200_common *priv, - void *buf, size_t buf_len); -int cw1200_data_write(struct cw1200_common *priv, - const void *buf, size_t buf_len); - -int cw1200_reg_read(struct cw1200_common *priv, u16 addr, - void *buf, size_t buf_len); -int cw1200_reg_write(struct cw1200_common *priv, u16 addr, - const void *buf, size_t buf_len); - -static inline int cw1200_reg_read_16(struct cw1200_common *priv, - u16 addr, u16 *val) -{ - __le32 tmp; - int i; - i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp)); - *val = le32_to_cpu(tmp) & 0xfffff; - return i; -} - -static inline int cw1200_reg_write_16(struct cw1200_common *priv, - u16 addr, u16 val) -{ - __le32 tmp = cpu_to_le32((u32)val); - return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp)); -} - -static inline int cw1200_reg_read_32(struct cw1200_common *priv, - u16 addr, u32 *val) -{ - __le32 tmp; - int i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp)); - *val = le32_to_cpu(tmp); - return i; -} - -static inline int cw1200_reg_write_32(struct cw1200_common *priv, - u16 addr, u32 val) -{ - __le32 tmp = cpu_to_le32(val); - return cw1200_reg_write(priv, addr, &tmp, sizeof(val)); -} - -int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, - size_t buf_len, u32 prefetch, u16 port_addr); -int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, - size_t buf_len); - -static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr, - void *buf, size_t buf_len) -{ - return cw1200_indirect_read(priv, addr, buf, buf_len, - ST90TDS_CONFIG_PRFETCH_BIT, - ST90TDS_SRAM_DPORT_REG_ID); -} - -static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr, - void *buf, size_t buf_len) -{ - return cw1200_indirect_read(priv, addr, buf, buf_len, - ST90TDS_CONFIG_AHB_PRFETCH_BIT, - ST90TDS_AHB_DPORT_REG_ID); -} - -static inline int cw1200_apb_read_32(struct cw1200_common *priv, - u32 addr, u32 *val) -{ - __le32 tmp; - int i = cw1200_apb_read(priv, addr, &tmp, sizeof(tmp)); - *val = le32_to_cpu(tmp); - return i; -} - -static inline int cw1200_apb_write_32(struct cw1200_common *priv, - u32 addr, u32 val) -{ - __le32 tmp = cpu_to_le32(val); - return cw1200_apb_write(priv, addr, &tmp, sizeof(val)); -} -static inline int cw1200_ahb_read_32(struct cw1200_common *priv, - u32 addr, u32 *val) -{ - __le32 tmp; - int i = cw1200_ahb_read(priv, addr, &tmp, sizeof(tmp)); - *val = le32_to_cpu(tmp); - return i; -} - -#endif /* CW1200_HWIO_H_INCLUDED */ diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c deleted file mode 100644 index 0e51e27d2e3f..000000000000 --- a/drivers/net/wireless/cw1200/main.c +++ /dev/null @@ -1,601 +0,0 @@ -/* - * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * Based on: - * Copyright (c) 2006, Michael Wu - * Copyright (c) 2007-2009, Christian Lamparter - * Copyright 2008, Johannes Berg - * - * Based on: - * - the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - stlc45xx driver - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "cw1200.h" -#include "txrx.h" -#include "hwbus.h" -#include "fwio.h" -#include "hwio.h" -#include "bh.h" -#include "sta.h" -#include "scan.h" -#include "debug.h" -#include "pm.h" - -MODULE_AUTHOR("Dmitry Tarnyagin "); -MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("cw1200_core"); - -/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */ -static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00}; -module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO); -MODULE_PARM_DESC(macaddr, "Override platform_data MAC address"); - -static char *cw1200_sdd_path; -module_param(cw1200_sdd_path, charp, 0644); -MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file"); -static int cw1200_refclk; -module_param(cw1200_refclk, int, 0644); -MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock"); - -int cw1200_power_mode = wsm_power_mode_quiescent; -module_param(cw1200_power_mode, int, 0644); -MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)"); - -#define RATETAB_ENT(_rate, _rateid, _flags) \ - { \ - .bitrate = (_rate), \ - .hw_value = (_rateid), \ - .flags = (_flags), \ - } - -static struct ieee80211_rate cw1200_rates[] = { - RATETAB_ENT(10, 0, 0), - RATETAB_ENT(20, 1, 0), - RATETAB_ENT(55, 2, 0), - RATETAB_ENT(110, 3, 0), - RATETAB_ENT(60, 6, 0), - RATETAB_ENT(90, 7, 0), - RATETAB_ENT(120, 8, 0), - RATETAB_ENT(180, 9, 0), - RATETAB_ENT(240, 10, 0), - RATETAB_ENT(360, 11, 0), - RATETAB_ENT(480, 12, 0), - RATETAB_ENT(540, 13, 0), -}; - -static struct ieee80211_rate cw1200_mcs_rates[] = { - RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), - RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), - RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), - RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), - RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), - RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), - RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), - RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), -}; - -#define cw1200_a_rates (cw1200_rates + 4) -#define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4) -#define cw1200_g_rates (cw1200_rates + 0) -#define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates)) -#define cw1200_n_rates (cw1200_mcs_rates) -#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates)) - - -#define CHAN2G(_channel, _freq, _flags) { \ - .band = IEEE80211_BAND_2GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_channel), \ - .flags = (_flags), \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} - -#define CHAN5G(_channel, _flags) { \ - .band = IEEE80211_BAND_5GHZ, \ - .center_freq = 5000 + (5 * (_channel)), \ - .hw_value = (_channel), \ - .flags = (_flags), \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} - -static struct ieee80211_channel cw1200_2ghz_chantable[] = { - CHAN2G(1, 2412, 0), - CHAN2G(2, 2417, 0), - CHAN2G(3, 2422, 0), - CHAN2G(4, 2427, 0), - CHAN2G(5, 2432, 0), - CHAN2G(6, 2437, 0), - CHAN2G(7, 2442, 0), - CHAN2G(8, 2447, 0), - CHAN2G(9, 2452, 0), - CHAN2G(10, 2457, 0), - CHAN2G(11, 2462, 0), - CHAN2G(12, 2467, 0), - CHAN2G(13, 2472, 0), - CHAN2G(14, 2484, 0), -}; - -static struct ieee80211_channel cw1200_5ghz_chantable[] = { - CHAN5G(34, 0), CHAN5G(36, 0), - CHAN5G(38, 0), CHAN5G(40, 0), - CHAN5G(42, 0), CHAN5G(44, 0), - CHAN5G(46, 0), CHAN5G(48, 0), - CHAN5G(52, 0), CHAN5G(56, 0), - CHAN5G(60, 0), CHAN5G(64, 0), - CHAN5G(100, 0), CHAN5G(104, 0), - CHAN5G(108, 0), CHAN5G(112, 0), - CHAN5G(116, 0), CHAN5G(120, 0), - CHAN5G(124, 0), CHAN5G(128, 0), - CHAN5G(132, 0), CHAN5G(136, 0), - CHAN5G(140, 0), CHAN5G(149, 0), - CHAN5G(153, 0), CHAN5G(157, 0), - CHAN5G(161, 0), CHAN5G(165, 0), - CHAN5G(184, 0), CHAN5G(188, 0), - CHAN5G(192, 0), CHAN5G(196, 0), - CHAN5G(200, 0), CHAN5G(204, 0), - CHAN5G(208, 0), CHAN5G(212, 0), - CHAN5G(216, 0), -}; - -static struct ieee80211_supported_band cw1200_band_2ghz = { - .channels = cw1200_2ghz_chantable, - .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable), - .bitrates = cw1200_g_rates, - .n_bitrates = cw1200_g_rates_size, - .ht_cap = { - .cap = IEEE80211_HT_CAP_GRN_FLD | - (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | - IEEE80211_HT_CAP_MAX_AMSDU, - .ht_supported = 1, - .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, - .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, - .mcs = { - .rx_mask[0] = 0xFF, - .rx_highest = __cpu_to_le16(0x41), - .tx_params = IEEE80211_HT_MCS_TX_DEFINED, - }, - }, -}; - -static struct ieee80211_supported_band cw1200_band_5ghz = { - .channels = cw1200_5ghz_chantable, - .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), - .bitrates = cw1200_a_rates, - .n_bitrates = cw1200_a_rates_size, - .ht_cap = { - .cap = IEEE80211_HT_CAP_GRN_FLD | - (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | - IEEE80211_HT_CAP_MAX_AMSDU, - .ht_supported = 1, - .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, - .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, - .mcs = { - .rx_mask[0] = 0xFF, - .rx_highest = __cpu_to_le16(0x41), - .tx_params = IEEE80211_HT_MCS_TX_DEFINED, - }, - }, -}; - -static const unsigned long cw1200_ttl[] = { - 1 * HZ, /* VO */ - 2 * HZ, /* VI */ - 5 * HZ, /* BE */ - 10 * HZ /* BK */ -}; - -static const struct ieee80211_ops cw1200_ops = { - .start = cw1200_start, - .stop = cw1200_stop, - .add_interface = cw1200_add_interface, - .remove_interface = cw1200_remove_interface, - .change_interface = cw1200_change_interface, - .tx = cw1200_tx, - .hw_scan = cw1200_hw_scan, - .set_tim = cw1200_set_tim, - .sta_notify = cw1200_sta_notify, - .sta_add = cw1200_sta_add, - .sta_remove = cw1200_sta_remove, - .set_key = cw1200_set_key, - .set_rts_threshold = cw1200_set_rts_threshold, - .config = cw1200_config, - .bss_info_changed = cw1200_bss_info_changed, - .prepare_multicast = cw1200_prepare_multicast, - .configure_filter = cw1200_configure_filter, - .conf_tx = cw1200_conf_tx, - .get_stats = cw1200_get_stats, - .ampdu_action = cw1200_ampdu_action, - .flush = cw1200_flush, -#ifdef CONFIG_PM - .suspend = cw1200_wow_suspend, - .resume = cw1200_wow_resume, -#endif - /* Intentionally not offloaded: */ - /*.channel_switch = cw1200_channel_switch, */ - /*.remain_on_channel = cw1200_remain_on_channel, */ - /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */ -}; - -static int cw1200_ba_rx_tids = -1; -static int cw1200_ba_tx_tids = -1; -module_param(cw1200_ba_rx_tids, int, 0644); -module_param(cw1200_ba_tx_tids, int, 0644); -MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs"); -MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs"); - -#ifdef CONFIG_PM -static const struct wiphy_wowlan_support cw1200_wowlan_support = { - /* Support only for limited wowlan functionalities */ - .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT, -}; -#endif - - -static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, - const bool have_5ghz) -{ - int i, band; - struct ieee80211_hw *hw; - struct cw1200_common *priv; - - hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops); - if (!hw) - return NULL; - - priv = hw->priv; - priv->hw = hw; - priv->hw_type = -1; - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - priv->rates = cw1200_rates; /* TODO: fetch from FW */ - priv->mcs_rates = cw1200_n_rates; - if (cw1200_ba_rx_tids != -1) - priv->ba_rx_tid_mask = cw1200_ba_rx_tids; - else - priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */ - if (cw1200_ba_tx_tids != -1) - priv->ba_tx_tid_mask = cw1200_ba_tx_tids; - else - priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */ - - ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); - ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); - ieee80211_hw_set(hw, AMPDU_AGGREGATION); - ieee80211_hw_set(hw, CONNECTION_MONITOR); - ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); - ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); - ieee80211_hw_set(hw, SIGNAL_DBM); - ieee80211_hw_set(hw, SUPPORTS_PS); - - hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO); - -#ifdef CONFIG_PM - hw->wiphy->wowlan = &cw1200_wowlan_support; -#endif - - hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; - - hw->queues = 4; - - priv->rts_threshold = -1; - - hw->max_rates = 8; - hw->max_rate_tries = 15; - hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM + - 8; /* TKIP IV */ - - hw->sta_data_size = sizeof(struct cw1200_sta_priv); - - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz; - if (have_5ghz) - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz; - - /* Channel params have to be cleared before registering wiphy again */ - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - struct ieee80211_supported_band *sband = hw->wiphy->bands[band]; - if (!sband) - continue; - for (i = 0; i < sband->n_channels; i++) { - sband->channels[i].flags = 0; - sband->channels[i].max_antenna_gain = 0; - sband->channels[i].max_power = 30; - } - } - - hw->wiphy->max_scan_ssids = 2; - hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; - - if (macaddr) - SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr); - else - SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template); - - /* Fix up mac address if necessary */ - if (hw->wiphy->perm_addr[3] == 0 && - hw->wiphy->perm_addr[4] == 0 && - hw->wiphy->perm_addr[5] == 0) { - get_random_bytes(&hw->wiphy->perm_addr[3], 3); - } - - mutex_init(&priv->wsm_cmd_mux); - mutex_init(&priv->conf_mutex); - priv->workqueue = create_singlethread_workqueue("cw1200_wq"); - sema_init(&priv->scan.lock, 1); - INIT_WORK(&priv->scan.work, cw1200_scan_work); - INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work); - INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout); - INIT_DELAYED_WORK(&priv->clear_recent_scan_work, - cw1200_clear_recent_scan_work); - INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout); - INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work); - INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work); - INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work); - INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work); - spin_lock_init(&priv->event_queue_lock); - INIT_LIST_HEAD(&priv->event_queue); - INIT_WORK(&priv->event_handler, cw1200_event_handler); - INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work); - INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work); - spin_lock_init(&priv->bss_loss_lock); - spin_lock_init(&priv->ps_state_lock); - INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work); - INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); - INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); - INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); - INIT_WORK(&priv->link_id_work, cw1200_link_id_work); - INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work); - INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset); - INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work); - INIT_WORK(&priv->set_beacon_wakeup_period_work, - cw1200_set_beacon_wakeup_period_work); - setup_timer(&priv->mcast_timeout, cw1200_mcast_timeout, - (unsigned long)priv); - - if (cw1200_queue_stats_init(&priv->tx_queue_stats, - CW1200_LINK_ID_MAX, - cw1200_skb_dtor, - priv)) { - ieee80211_free_hw(hw); - return NULL; - } - - for (i = 0; i < 4; ++i) { - if (cw1200_queue_init(&priv->tx_queue[i], - &priv->tx_queue_stats, i, 16, - cw1200_ttl[i])) { - for (; i > 0; i--) - cw1200_queue_deinit(&priv->tx_queue[i - 1]); - cw1200_queue_stats_deinit(&priv->tx_queue_stats); - ieee80211_free_hw(hw); - return NULL; - } - } - - init_waitqueue_head(&priv->channel_switch_done); - init_waitqueue_head(&priv->wsm_cmd_wq); - init_waitqueue_head(&priv->wsm_startup_done); - init_waitqueue_head(&priv->ps_mode_switch_done); - wsm_buf_init(&priv->wsm_cmd_buf); - spin_lock_init(&priv->wsm_cmd.lock); - priv->wsm_cmd.done = 1; - tx_policy_init(priv); - - return hw; -} - -static int cw1200_register_common(struct ieee80211_hw *dev) -{ - struct cw1200_common *priv = dev->priv; - int err; - -#ifdef CONFIG_PM - err = cw1200_pm_init(&priv->pm_state, priv); - if (err) { - pr_err("Cannot init PM. (%d).\n", - err); - return err; - } -#endif - - err = ieee80211_register_hw(dev); - if (err) { - pr_err("Cannot register device (%d).\n", - err); -#ifdef CONFIG_PM - cw1200_pm_deinit(&priv->pm_state); -#endif - return err; - } - - cw1200_debug_init(priv); - - pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy)); - return 0; -} - -static void cw1200_free_common(struct ieee80211_hw *dev) -{ - ieee80211_free_hw(dev); -} - -static void cw1200_unregister_common(struct ieee80211_hw *dev) -{ - struct cw1200_common *priv = dev->priv; - int i; - - ieee80211_unregister_hw(dev); - - del_timer_sync(&priv->mcast_timeout); - cw1200_unregister_bh(priv); - - cw1200_debug_release(priv); - - mutex_destroy(&priv->conf_mutex); - - wsm_buf_deinit(&priv->wsm_cmd_buf); - - destroy_workqueue(priv->workqueue); - priv->workqueue = NULL; - - if (priv->sdd) { - release_firmware(priv->sdd); - priv->sdd = NULL; - } - - for (i = 0; i < 4; ++i) - cw1200_queue_deinit(&priv->tx_queue[i]); - - cw1200_queue_stats_deinit(&priv->tx_queue_stats); -#ifdef CONFIG_PM - cw1200_pm_deinit(&priv->pm_state); -#endif -} - -/* Clock is in KHz */ -u32 cw1200_dpll_from_clk(u16 clk_khz) -{ - switch (clk_khz) { - case 0x32C8: /* 13000 KHz */ - return 0x1D89D241; - case 0x3E80: /* 16000 KHz */ - return 0x000001E1; - case 0x41A0: /* 16800 KHz */ - return 0x124931C1; - case 0x4B00: /* 19200 KHz */ - return 0x00000191; - case 0x5DC0: /* 24000 KHz */ - return 0x00000141; - case 0x6590: /* 26000 KHz */ - return 0x0EC4F121; - case 0x8340: /* 33600 KHz */ - return 0x092490E1; - case 0x9600: /* 38400 KHz */ - return 0x100010C1; - case 0x9C40: /* 40000 KHz */ - return 0x000000C1; - case 0xBB80: /* 48000 KHz */ - return 0x000000A1; - case 0xCB20: /* 52000 KHz */ - return 0x07627091; - default: - pr_err("Unknown Refclk freq (0x%04x), using 26000KHz\n", - clk_khz); - return 0x0EC4F121; - } -} - -int cw1200_core_probe(const struct hwbus_ops *hwbus_ops, - struct hwbus_priv *hwbus, - struct device *pdev, - struct cw1200_common **core, - int ref_clk, const u8 *macaddr, - const char *sdd_path, bool have_5ghz) -{ - int err = -EINVAL; - struct ieee80211_hw *dev; - struct cw1200_common *priv; - struct wsm_operational_mode mode = { - .power_mode = cw1200_power_mode, - .disable_more_flag_usage = true, - }; - - dev = cw1200_init_common(macaddr, have_5ghz); - if (!dev) - goto err; - - priv = dev->priv; - priv->hw_refclk = ref_clk; - if (cw1200_refclk) - priv->hw_refclk = cw1200_refclk; - - priv->sdd_path = (char *)sdd_path; - if (cw1200_sdd_path) - priv->sdd_path = cw1200_sdd_path; - - priv->hwbus_ops = hwbus_ops; - priv->hwbus_priv = hwbus; - priv->pdev = pdev; - SET_IEEE80211_DEV(priv->hw, pdev); - - /* Pass struct cw1200_common back up */ - *core = priv; - - err = cw1200_register_bh(priv); - if (err) - goto err1; - - err = cw1200_load_firmware(priv); - if (err) - goto err2; - - if (wait_event_interruptible_timeout(priv->wsm_startup_done, - priv->firmware_ready, - 3*HZ) <= 0) { - /* TODO: Need to find how to reset device - in QUEUE mode properly. - */ - pr_err("Timeout waiting on device startup\n"); - err = -ETIMEDOUT; - goto err2; - } - - /* Set low-power mode. */ - wsm_set_operational_mode(priv, &mode); - - /* Enable multi-TX confirmation */ - wsm_use_multi_tx_conf(priv, true); - - err = cw1200_register_common(dev); - if (err) - goto err2; - - return err; - -err2: - cw1200_unregister_bh(priv); -err1: - cw1200_free_common(dev); -err: - *core = NULL; - return err; -} -EXPORT_SYMBOL_GPL(cw1200_core_probe); - -void cw1200_core_release(struct cw1200_common *self) -{ - /* Disable device interrupts */ - self->hwbus_ops->lock(self->hwbus_priv); - __cw1200_irq_enable(self, 0); - self->hwbus_ops->unlock(self->hwbus_priv); - - /* And then clean up */ - cw1200_unregister_common(self->hw); - cw1200_free_common(self->hw); - return; -} -EXPORT_SYMBOL_GPL(cw1200_core_release); diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c deleted file mode 100644 index d2202ae92bdd..000000000000 --- a/drivers/net/wireless/cw1200/pm.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Mac80211 power management API for ST-Ericsson CW1200 drivers - * - * Copyright (c) 2011, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include "cw1200.h" -#include "pm.h" -#include "sta.h" -#include "bh.h" -#include "hwbus.h" - -#define CW1200_BEACON_SKIPPING_MULTIPLIER 3 - -struct cw1200_udp_port_filter { - struct wsm_udp_port_filter_hdr hdr; - /* Up to 4 filters are allowed. */ - struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS]; -} __packed; - -struct cw1200_ether_type_filter { - struct wsm_ether_type_filter_hdr hdr; - /* Up to 4 filters are allowed. */ - struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS]; -} __packed; - -static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = { - .hdr.num = 2, - .filters = { - [0] = { - .action = WSM_FILTER_ACTION_FILTER_OUT, - .type = WSM_FILTER_PORT_TYPE_DST, - .port = __cpu_to_le16(67), /* DHCP Bootps */ - }, - [1] = { - .action = WSM_FILTER_ACTION_FILTER_OUT, - .type = WSM_FILTER_PORT_TYPE_DST, - .port = __cpu_to_le16(68), /* DHCP Bootpc */ - }, - } -}; - -static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = { - .num = 0, -}; - -#ifndef ETH_P_WAPI -#define ETH_P_WAPI 0x88B4 -#endif - -static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = { - .hdr.num = 4, - .filters = { - [0] = { - .action = WSM_FILTER_ACTION_FILTER_IN, - .type = __cpu_to_le16(ETH_P_IP), - }, - [1] = { - .action = WSM_FILTER_ACTION_FILTER_IN, - .type = __cpu_to_le16(ETH_P_PAE), - }, - [2] = { - .action = WSM_FILTER_ACTION_FILTER_IN, - .type = __cpu_to_le16(ETH_P_WAPI), - }, - [3] = { - .action = WSM_FILTER_ACTION_FILTER_IN, - .type = __cpu_to_le16(ETH_P_ARP), - }, - }, -}; - -static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = { - .num = 0, -}; - -/* private */ -struct cw1200_suspend_state { - unsigned long bss_loss_tmo; - unsigned long join_tmo; - unsigned long direct_probe; - unsigned long link_id_gc; - bool beacon_skipping; - u8 prev_ps_mode; -}; - -static void cw1200_pm_stay_awake_tmo(unsigned long arg) -{ - /* XXX what's the point of this ? */ -} - -int cw1200_pm_init(struct cw1200_pm_state *pm, - struct cw1200_common *priv) -{ - spin_lock_init(&pm->lock); - - setup_timer(&pm->stay_awake, cw1200_pm_stay_awake_tmo, - (unsigned long)pm); - - return 0; -} - -void cw1200_pm_deinit(struct cw1200_pm_state *pm) -{ - del_timer_sync(&pm->stay_awake); -} - -void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, - unsigned long tmo) -{ - long cur_tmo; - spin_lock_bh(&pm->lock); - cur_tmo = pm->stay_awake.expires - jiffies; - if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo) - mod_timer(&pm->stay_awake, jiffies + tmo); - spin_unlock_bh(&pm->lock); -} - -static long cw1200_suspend_work(struct delayed_work *work) -{ - int ret = cancel_delayed_work(work); - long tmo; - if (ret > 0) { - /* Timer is pending */ - tmo = work->timer.expires - jiffies; - if (tmo < 0) - tmo = 0; - } else { - tmo = -1; - } - return tmo; -} - -static int cw1200_resume_work(struct cw1200_common *priv, - struct delayed_work *work, - unsigned long tmo) -{ - if ((long)tmo < 0) - return 1; - - return queue_delayed_work(priv->workqueue, work, tmo); -} - -int cw1200_can_suspend(struct cw1200_common *priv) -{ - if (atomic_read(&priv->bh_rx)) { - wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n"); - return 0; - } - return 1; -} -EXPORT_SYMBOL_GPL(cw1200_can_suspend); - -int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) -{ - struct cw1200_common *priv = hw->priv; - struct cw1200_pm_state *pm_state = &priv->pm_state; - struct cw1200_suspend_state *state; - int ret; - - spin_lock_bh(&pm_state->lock); - ret = timer_pending(&pm_state->stay_awake); - spin_unlock_bh(&pm_state->lock); - if (ret) - return -EAGAIN; - - /* Do not suspend when datapath is not idle */ - if (priv->tx_queue_stats.num_queued) - return -EBUSY; - - /* Make sure there is no configuration requests in progress. */ - if (!mutex_trylock(&priv->conf_mutex)) - return -EBUSY; - - /* Ensure pending operations are done. - * Note also that wow_suspend must return in ~2.5sec, before - * watchdog is triggered. - */ - if (priv->channel_switch_in_progress) - goto revert1; - - /* Do not suspend when join is pending */ - if (priv->join_pending) - goto revert1; - - /* Do not suspend when scanning */ - if (down_trylock(&priv->scan.lock)) - goto revert1; - - /* Lock TX. */ - wsm_lock_tx_async(priv); - - /* Wait to avoid possible race with bh code. - * But do not wait too long... - */ - if (wait_event_timeout(priv->bh_evt_wq, - !priv->hw_bufs_used, HZ / 10) <= 0) - goto revert2; - - /* Set UDP filter */ - wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr); - - /* Set ethernet frame type filter */ - wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr); - - /* Allocate state */ - state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL); - if (!state) - goto revert3; - - /* Change to legacy PS while going to suspend */ - if (!priv->vif->p2p && - priv->join_status == CW1200_JOIN_STATUS_STA && - priv->powersave_mode.mode != WSM_PSM_PS) { - state->prev_ps_mode = priv->powersave_mode.mode; - priv->powersave_mode.mode = WSM_PSM_PS; - cw1200_set_pm(priv, &priv->powersave_mode); - if (wait_event_interruptible_timeout(priv->ps_mode_switch_done, - !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) { - goto revert4; - } - } - - /* Store delayed work states. */ - state->bss_loss_tmo = - cw1200_suspend_work(&priv->bss_loss_work); - state->join_tmo = - cw1200_suspend_work(&priv->join_timeout); - state->direct_probe = - cw1200_suspend_work(&priv->scan.probe_work); - state->link_id_gc = - cw1200_suspend_work(&priv->link_id_gc_work); - - cancel_delayed_work_sync(&priv->clear_recent_scan_work); - atomic_set(&priv->recent_scan, 0); - - /* Enable beacon skipping */ - if (priv->join_status == CW1200_JOIN_STATUS_STA && - priv->join_dtim_period && - !priv->has_multicast_subscription) { - state->beacon_skipping = true; - wsm_set_beacon_wakeup_period(priv, - priv->join_dtim_period, - CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period); - } - - /* Stop serving thread */ - if (cw1200_bh_suspend(priv)) - goto revert5; - - ret = timer_pending(&priv->mcast_timeout); - if (ret) - goto revert6; - - /* Store suspend state */ - pm_state->suspend_state = state; - - /* Enable IRQ wake */ - ret = priv->hwbus_ops->power_mgmt(priv->hwbus_priv, true); - if (ret) { - wiphy_err(priv->hw->wiphy, - "PM request failed: %d. WoW is disabled.\n", ret); - cw1200_wow_resume(hw); - return -EBUSY; - } - - /* Force resume if event is coming from the device. */ - if (atomic_read(&priv->bh_rx)) { - cw1200_wow_resume(hw); - return -EAGAIN; - } - - return 0; - -revert6: - WARN_ON(cw1200_bh_resume(priv)); -revert5: - cw1200_resume_work(priv, &priv->bss_loss_work, - state->bss_loss_tmo); - cw1200_resume_work(priv, &priv->join_timeout, - state->join_tmo); - cw1200_resume_work(priv, &priv->scan.probe_work, - state->direct_probe); - cw1200_resume_work(priv, &priv->link_id_gc_work, - state->link_id_gc); -revert4: - kfree(state); -revert3: - wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); - wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); -revert2: - wsm_unlock_tx(priv); - up(&priv->scan.lock); -revert1: - mutex_unlock(&priv->conf_mutex); - return -EBUSY; -} - -int cw1200_wow_resume(struct ieee80211_hw *hw) -{ - struct cw1200_common *priv = hw->priv; - struct cw1200_pm_state *pm_state = &priv->pm_state; - struct cw1200_suspend_state *state; - - state = pm_state->suspend_state; - pm_state->suspend_state = NULL; - - /* Disable IRQ wake */ - priv->hwbus_ops->power_mgmt(priv->hwbus_priv, false); - - /* Scan.lock must be released before BH is resumed other way - * in case when BSS_LOST command arrived the processing of the - * command will be delayed. - */ - up(&priv->scan.lock); - - /* Resume BH thread */ - WARN_ON(cw1200_bh_resume(priv)); - - /* Restores previous PS mode */ - if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) { - priv->powersave_mode.mode = state->prev_ps_mode; - cw1200_set_pm(priv, &priv->powersave_mode); - } - - if (state->beacon_skipping) { - wsm_set_beacon_wakeup_period(priv, priv->beacon_int * - priv->join_dtim_period > - MAX_BEACON_SKIP_TIME_MS ? 1 : - priv->join_dtim_period, 0); - state->beacon_skipping = false; - } - - /* Resume delayed work */ - cw1200_resume_work(priv, &priv->bss_loss_work, - state->bss_loss_tmo); - cw1200_resume_work(priv, &priv->join_timeout, - state->join_tmo); - cw1200_resume_work(priv, &priv->scan.probe_work, - state->direct_probe); - cw1200_resume_work(priv, &priv->link_id_gc_work, - state->link_id_gc); - - /* Remove UDP port filter */ - wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); - - /* Remove ethernet frame type filter */ - wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); - - /* Unlock datapath */ - wsm_unlock_tx(priv); - - /* Unlock configuration mutex */ - mutex_unlock(&priv->conf_mutex); - - /* Free memory */ - kfree(state); - - return 0; -} diff --git a/drivers/net/wireless/cw1200/pm.h b/drivers/net/wireless/cw1200/pm.h deleted file mode 100644 index 3ed90ff22bb8..000000000000 --- a/drivers/net/wireless/cw1200/pm.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers - * - * Copyright (c) 2011, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef PM_H_INCLUDED -#define PM_H_INCLUDED - -/* ******************************************************************** */ -/* mac80211 API */ - -/* extern */ struct cw1200_common; -/* private */ struct cw1200_suspend_state; - -struct cw1200_pm_state { - struct cw1200_suspend_state *suspend_state; - struct timer_list stay_awake; - struct platform_device *pm_dev; - spinlock_t lock; /* Protect access */ -}; - -#ifdef CONFIG_PM -int cw1200_pm_init(struct cw1200_pm_state *pm, - struct cw1200_common *priv); -void cw1200_pm_deinit(struct cw1200_pm_state *pm); -int cw1200_wow_suspend(struct ieee80211_hw *hw, - struct cfg80211_wowlan *wowlan); -int cw1200_wow_resume(struct ieee80211_hw *hw); -int cw1200_can_suspend(struct cw1200_common *priv); -void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, - unsigned long tmo); -#else -static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, - unsigned long tmo) { -} -#endif -#endif diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/cw1200/queue.c deleted file mode 100644 index 0ba5ef9b3e7b..000000000000 --- a/drivers/net/wireless/cw1200/queue.c +++ /dev/null @@ -1,581 +0,0 @@ -/* - * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include "queue.h" -#include "cw1200.h" -#include "debug.h" - -/* private */ struct cw1200_queue_item -{ - struct list_head head; - struct sk_buff *skb; - u32 packet_id; - unsigned long queue_timestamp; - unsigned long xmit_timestamp; - struct cw1200_txpriv txpriv; - u8 generation; -}; - -static inline void __cw1200_queue_lock(struct cw1200_queue *queue) -{ - struct cw1200_queue_stats *stats = queue->stats; - if (queue->tx_locked_cnt++ == 0) { - pr_debug("[TX] Queue %d is locked.\n", - queue->queue_id); - ieee80211_stop_queue(stats->priv->hw, queue->queue_id); - } -} - -static inline void __cw1200_queue_unlock(struct cw1200_queue *queue) -{ - struct cw1200_queue_stats *stats = queue->stats; - BUG_ON(!queue->tx_locked_cnt); - if (--queue->tx_locked_cnt == 0) { - pr_debug("[TX] Queue %d is unlocked.\n", - queue->queue_id); - ieee80211_wake_queue(stats->priv->hw, queue->queue_id); - } -} - -static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation, - u8 *queue_id, u8 *item_generation, - u8 *item_id) -{ - *item_id = (packet_id >> 0) & 0xFF; - *item_generation = (packet_id >> 8) & 0xFF; - *queue_id = (packet_id >> 16) & 0xFF; - *queue_generation = (packet_id >> 24) & 0xFF; -} - -static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id, - u8 item_generation, u8 item_id) -{ - return ((u32)item_id << 0) | - ((u32)item_generation << 8) | - ((u32)queue_id << 16) | - ((u32)queue_generation << 24); -} - -static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats, - struct list_head *gc_list) -{ - struct cw1200_queue_item *item, *tmp; - - list_for_each_entry_safe(item, tmp, gc_list, head) { - list_del(&item->head); - stats->skb_dtor(stats->priv, item->skb, &item->txpriv); - kfree(item); - } -} - -static void cw1200_queue_register_post_gc(struct list_head *gc_list, - struct cw1200_queue_item *item) -{ - struct cw1200_queue_item *gc_item; - gc_item = kmalloc(sizeof(struct cw1200_queue_item), - GFP_ATOMIC); - BUG_ON(!gc_item); - memcpy(gc_item, item, sizeof(struct cw1200_queue_item)); - list_add_tail(&gc_item->head, gc_list); -} - -static void __cw1200_queue_gc(struct cw1200_queue *queue, - struct list_head *head, - bool unlock) -{ - struct cw1200_queue_stats *stats = queue->stats; - struct cw1200_queue_item *item = NULL, *tmp; - bool wakeup_stats = false; - - list_for_each_entry_safe(item, tmp, &queue->queue, head) { - if (jiffies - item->queue_timestamp < queue->ttl) - break; - --queue->num_queued; - --queue->link_map_cache[item->txpriv.link_id]; - spin_lock_bh(&stats->lock); - --stats->num_queued; - if (!--stats->link_map_cache[item->txpriv.link_id]) - wakeup_stats = true; - spin_unlock_bh(&stats->lock); - cw1200_debug_tx_ttl(stats->priv); - cw1200_queue_register_post_gc(head, item); - item->skb = NULL; - list_move_tail(&item->head, &queue->free_pool); - } - - if (wakeup_stats) - wake_up(&stats->wait_link_id_empty); - - if (queue->overfull) { - if (queue->num_queued <= (queue->capacity >> 1)) { - queue->overfull = false; - if (unlock) - __cw1200_queue_unlock(queue); - } else if (item) { - unsigned long tmo = item->queue_timestamp + queue->ttl; - mod_timer(&queue->gc, tmo); - cw1200_pm_stay_awake(&stats->priv->pm_state, - tmo - jiffies); - } - } -} - -static void cw1200_queue_gc(unsigned long arg) -{ - LIST_HEAD(list); - struct cw1200_queue *queue = - (struct cw1200_queue *)arg; - - spin_lock_bh(&queue->lock); - __cw1200_queue_gc(queue, &list, true); - spin_unlock_bh(&queue->lock); - cw1200_queue_post_gc(queue->stats, &list); -} - -int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, - size_t map_capacity, - cw1200_queue_skb_dtor_t skb_dtor, - struct cw1200_common *priv) -{ - memset(stats, 0, sizeof(*stats)); - stats->map_capacity = map_capacity; - stats->skb_dtor = skb_dtor; - stats->priv = priv; - spin_lock_init(&stats->lock); - init_waitqueue_head(&stats->wait_link_id_empty); - - stats->link_map_cache = kzalloc(sizeof(int) * map_capacity, - GFP_KERNEL); - if (!stats->link_map_cache) - return -ENOMEM; - - return 0; -} - -int cw1200_queue_init(struct cw1200_queue *queue, - struct cw1200_queue_stats *stats, - u8 queue_id, - size_t capacity, - unsigned long ttl) -{ - size_t i; - - memset(queue, 0, sizeof(*queue)); - queue->stats = stats; - queue->capacity = capacity; - queue->queue_id = queue_id; - queue->ttl = ttl; - INIT_LIST_HEAD(&queue->queue); - INIT_LIST_HEAD(&queue->pending); - INIT_LIST_HEAD(&queue->free_pool); - spin_lock_init(&queue->lock); - setup_timer(&queue->gc, cw1200_queue_gc, (unsigned long)queue); - - queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, - GFP_KERNEL); - if (!queue->pool) - return -ENOMEM; - - queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity, - GFP_KERNEL); - if (!queue->link_map_cache) { - kfree(queue->pool); - queue->pool = NULL; - return -ENOMEM; - } - - for (i = 0; i < capacity; ++i) - list_add_tail(&queue->pool[i].head, &queue->free_pool); - - return 0; -} - -int cw1200_queue_clear(struct cw1200_queue *queue) -{ - int i; - LIST_HEAD(gc_list); - struct cw1200_queue_stats *stats = queue->stats; - struct cw1200_queue_item *item, *tmp; - - spin_lock_bh(&queue->lock); - queue->generation++; - list_splice_tail_init(&queue->queue, &queue->pending); - list_for_each_entry_safe(item, tmp, &queue->pending, head) { - WARN_ON(!item->skb); - cw1200_queue_register_post_gc(&gc_list, item); - item->skb = NULL; - list_move_tail(&item->head, &queue->free_pool); - } - queue->num_queued = 0; - queue->num_pending = 0; - - spin_lock_bh(&stats->lock); - for (i = 0; i < stats->map_capacity; ++i) { - stats->num_queued -= queue->link_map_cache[i]; - stats->link_map_cache[i] -= queue->link_map_cache[i]; - queue->link_map_cache[i] = 0; - } - spin_unlock_bh(&stats->lock); - if (queue->overfull) { - queue->overfull = false; - __cw1200_queue_unlock(queue); - } - spin_unlock_bh(&queue->lock); - wake_up(&stats->wait_link_id_empty); - cw1200_queue_post_gc(stats, &gc_list); - return 0; -} - -void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats) -{ - kfree(stats->link_map_cache); - stats->link_map_cache = NULL; -} - -void cw1200_queue_deinit(struct cw1200_queue *queue) -{ - cw1200_queue_clear(queue); - del_timer_sync(&queue->gc); - INIT_LIST_HEAD(&queue->free_pool); - kfree(queue->pool); - kfree(queue->link_map_cache); - queue->pool = NULL; - queue->link_map_cache = NULL; - queue->capacity = 0; -} - -size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, - u32 link_id_map) -{ - size_t ret; - int i, bit; - size_t map_capacity = queue->stats->map_capacity; - - if (!link_id_map) - return 0; - - spin_lock_bh(&queue->lock); - if (link_id_map == (u32)-1) { - ret = queue->num_queued - queue->num_pending; - } else { - ret = 0; - for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) { - if (link_id_map & bit) - ret += queue->link_map_cache[i]; - } - } - spin_unlock_bh(&queue->lock); - return ret; -} - -int cw1200_queue_put(struct cw1200_queue *queue, - struct sk_buff *skb, - struct cw1200_txpriv *txpriv) -{ - int ret = 0; - LIST_HEAD(gc_list); - struct cw1200_queue_stats *stats = queue->stats; - - if (txpriv->link_id >= queue->stats->map_capacity) - return -EINVAL; - - spin_lock_bh(&queue->lock); - if (!WARN_ON(list_empty(&queue->free_pool))) { - struct cw1200_queue_item *item = list_first_entry( - &queue->free_pool, struct cw1200_queue_item, head); - BUG_ON(item->skb); - - list_move_tail(&item->head, &queue->queue); - item->skb = skb; - item->txpriv = *txpriv; - item->generation = 0; - item->packet_id = cw1200_queue_mk_packet_id(queue->generation, - queue->queue_id, - item->generation, - item - queue->pool); - item->queue_timestamp = jiffies; - - ++queue->num_queued; - ++queue->link_map_cache[txpriv->link_id]; - - spin_lock_bh(&stats->lock); - ++stats->num_queued; - ++stats->link_map_cache[txpriv->link_id]; - spin_unlock_bh(&stats->lock); - - /* TX may happen in parallel sometimes. - * Leave extra queue slots so we don't overflow. - */ - if (queue->overfull == false && - queue->num_queued >= - (queue->capacity - (num_present_cpus() - 1))) { - queue->overfull = true; - __cw1200_queue_lock(queue); - mod_timer(&queue->gc, jiffies); - } - } else { - ret = -ENOENT; - } - spin_unlock_bh(&queue->lock); - return ret; -} - -int cw1200_queue_get(struct cw1200_queue *queue, - u32 link_id_map, - struct wsm_tx **tx, - struct ieee80211_tx_info **tx_info, - const struct cw1200_txpriv **txpriv) -{ - int ret = -ENOENT; - struct cw1200_queue_item *item; - struct cw1200_queue_stats *stats = queue->stats; - bool wakeup_stats = false; - - spin_lock_bh(&queue->lock); - list_for_each_entry(item, &queue->queue, head) { - if (link_id_map & BIT(item->txpriv.link_id)) { - ret = 0; - break; - } - } - - if (!WARN_ON(ret)) { - *tx = (struct wsm_tx *)item->skb->data; - *tx_info = IEEE80211_SKB_CB(item->skb); - *txpriv = &item->txpriv; - (*tx)->packet_id = item->packet_id; - list_move_tail(&item->head, &queue->pending); - ++queue->num_pending; - --queue->link_map_cache[item->txpriv.link_id]; - item->xmit_timestamp = jiffies; - - spin_lock_bh(&stats->lock); - --stats->num_queued; - if (!--stats->link_map_cache[item->txpriv.link_id]) - wakeup_stats = true; - spin_unlock_bh(&stats->lock); - } - spin_unlock_bh(&queue->lock); - if (wakeup_stats) - wake_up(&stats->wait_link_id_empty); - return ret; -} - -int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id) -{ - int ret = 0; - u8 queue_generation, queue_id, item_generation, item_id; - struct cw1200_queue_item *item; - struct cw1200_queue_stats *stats = queue->stats; - - cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, - &item_generation, &item_id); - - item = &queue->pool[item_id]; - - spin_lock_bh(&queue->lock); - BUG_ON(queue_id != queue->queue_id); - if (queue_generation != queue->generation) { - ret = -ENOENT; - } else if (item_id >= (unsigned) queue->capacity) { - WARN_ON(1); - ret = -EINVAL; - } else if (item->generation != item_generation) { - WARN_ON(1); - ret = -ENOENT; - } else { - --queue->num_pending; - ++queue->link_map_cache[item->txpriv.link_id]; - - spin_lock_bh(&stats->lock); - ++stats->num_queued; - ++stats->link_map_cache[item->txpriv.link_id]; - spin_unlock_bh(&stats->lock); - - item->generation = ++item_generation; - item->packet_id = cw1200_queue_mk_packet_id(queue_generation, - queue_id, - item_generation, - item_id); - list_move(&item->head, &queue->queue); - } - spin_unlock_bh(&queue->lock); - return ret; -} - -int cw1200_queue_requeue_all(struct cw1200_queue *queue) -{ - struct cw1200_queue_item *item, *tmp; - struct cw1200_queue_stats *stats = queue->stats; - spin_lock_bh(&queue->lock); - - list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) { - --queue->num_pending; - ++queue->link_map_cache[item->txpriv.link_id]; - - spin_lock_bh(&stats->lock); - ++stats->num_queued; - ++stats->link_map_cache[item->txpriv.link_id]; - spin_unlock_bh(&stats->lock); - - ++item->generation; - item->packet_id = cw1200_queue_mk_packet_id(queue->generation, - queue->queue_id, - item->generation, - item - queue->pool); - list_move(&item->head, &queue->queue); - } - spin_unlock_bh(&queue->lock); - - return 0; -} - -int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id) -{ - int ret = 0; - u8 queue_generation, queue_id, item_generation, item_id; - struct cw1200_queue_item *item; - struct cw1200_queue_stats *stats = queue->stats; - struct sk_buff *gc_skb = NULL; - struct cw1200_txpriv gc_txpriv; - - cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, - &item_generation, &item_id); - - item = &queue->pool[item_id]; - - spin_lock_bh(&queue->lock); - BUG_ON(queue_id != queue->queue_id); - if (queue_generation != queue->generation) { - ret = -ENOENT; - } else if (item_id >= (unsigned) queue->capacity) { - WARN_ON(1); - ret = -EINVAL; - } else if (item->generation != item_generation) { - WARN_ON(1); - ret = -ENOENT; - } else { - gc_txpriv = item->txpriv; - gc_skb = item->skb; - item->skb = NULL; - --queue->num_pending; - --queue->num_queued; - ++queue->num_sent; - ++item->generation; - /* Do not use list_move_tail here, but list_move: - * try to utilize cache row. - */ - list_move(&item->head, &queue->free_pool); - - if (queue->overfull && - (queue->num_queued <= (queue->capacity >> 1))) { - queue->overfull = false; - __cw1200_queue_unlock(queue); - } - } - spin_unlock_bh(&queue->lock); - - if (gc_skb) - stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv); - - return ret; -} - -int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id, - struct sk_buff **skb, - const struct cw1200_txpriv **txpriv) -{ - int ret = 0; - u8 queue_generation, queue_id, item_generation, item_id; - struct cw1200_queue_item *item; - cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, - &item_generation, &item_id); - - item = &queue->pool[item_id]; - - spin_lock_bh(&queue->lock); - BUG_ON(queue_id != queue->queue_id); - if (queue_generation != queue->generation) { - ret = -ENOENT; - } else if (item_id >= (unsigned) queue->capacity) { - WARN_ON(1); - ret = -EINVAL; - } else if (item->generation != item_generation) { - WARN_ON(1); - ret = -ENOENT; - } else { - *skb = item->skb; - *txpriv = &item->txpriv; - } - spin_unlock_bh(&queue->lock); - return ret; -} - -void cw1200_queue_lock(struct cw1200_queue *queue) -{ - spin_lock_bh(&queue->lock); - __cw1200_queue_lock(queue); - spin_unlock_bh(&queue->lock); -} - -void cw1200_queue_unlock(struct cw1200_queue *queue) -{ - spin_lock_bh(&queue->lock); - __cw1200_queue_unlock(queue); - spin_unlock_bh(&queue->lock); -} - -bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, - unsigned long *timestamp, - u32 pending_frame_id) -{ - struct cw1200_queue_item *item; - bool ret; - - spin_lock_bh(&queue->lock); - ret = !list_empty(&queue->pending); - if (ret) { - list_for_each_entry(item, &queue->pending, head) { - if (item->packet_id != pending_frame_id) - if (time_before(item->xmit_timestamp, - *timestamp)) - *timestamp = item->xmit_timestamp; - } - } - spin_unlock_bh(&queue->lock); - return ret; -} - -bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, - u32 link_id_map) -{ - bool empty = true; - - spin_lock_bh(&stats->lock); - if (link_id_map == (u32)-1) { - empty = stats->num_queued == 0; - } else { - int i; - for (i = 0; i < stats->map_capacity; ++i) { - if (link_id_map & BIT(i)) { - if (stats->link_map_cache[i]) { - empty = false; - break; - } - } - } - } - spin_unlock_bh(&stats->lock); - - return empty; -} diff --git a/drivers/net/wireless/cw1200/queue.h b/drivers/net/wireless/cw1200/queue.h deleted file mode 100644 index 119f9c79c14e..000000000000 --- a/drivers/net/wireless/cw1200/queue.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef CW1200_QUEUE_H_INCLUDED -#define CW1200_QUEUE_H_INCLUDED - -/* private */ struct cw1200_queue_item; - -/* extern */ struct sk_buff; -/* extern */ struct wsm_tx; -/* extern */ struct cw1200_common; -/* extern */ struct ieee80211_tx_queue_stats; -/* extern */ struct cw1200_txpriv; - -/* forward */ struct cw1200_queue_stats; - -typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv, - struct sk_buff *skb, - const struct cw1200_txpriv *txpriv); - -struct cw1200_queue { - struct cw1200_queue_stats *stats; - size_t capacity; - size_t num_queued; - size_t num_pending; - size_t num_sent; - struct cw1200_queue_item *pool; - struct list_head queue; - struct list_head free_pool; - struct list_head pending; - int tx_locked_cnt; - int *link_map_cache; - bool overfull; - spinlock_t lock; /* Protect queue entry */ - u8 queue_id; - u8 generation; - struct timer_list gc; - unsigned long ttl; -}; - -struct cw1200_queue_stats { - spinlock_t lock; /* Protect stats entry */ - int *link_map_cache; - int num_queued; - size_t map_capacity; - wait_queue_head_t wait_link_id_empty; - cw1200_queue_skb_dtor_t skb_dtor; - struct cw1200_common *priv; -}; - -struct cw1200_txpriv { - u8 link_id; - u8 raw_link_id; - u8 tid; - u8 rate_id; - u8 offset; -}; - -int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, - size_t map_capacity, - cw1200_queue_skb_dtor_t skb_dtor, - struct cw1200_common *priv); -int cw1200_queue_init(struct cw1200_queue *queue, - struct cw1200_queue_stats *stats, - u8 queue_id, - size_t capacity, - unsigned long ttl); -int cw1200_queue_clear(struct cw1200_queue *queue); -void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats); -void cw1200_queue_deinit(struct cw1200_queue *queue); - -size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, - u32 link_id_map); -int cw1200_queue_put(struct cw1200_queue *queue, - struct sk_buff *skb, - struct cw1200_txpriv *txpriv); -int cw1200_queue_get(struct cw1200_queue *queue, - u32 link_id_map, - struct wsm_tx **tx, - struct ieee80211_tx_info **tx_info, - const struct cw1200_txpriv **txpriv); -int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id); -int cw1200_queue_requeue_all(struct cw1200_queue *queue); -int cw1200_queue_remove(struct cw1200_queue *queue, - u32 packet_id); -int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id, - struct sk_buff **skb, - const struct cw1200_txpriv **txpriv); -void cw1200_queue_lock(struct cw1200_queue *queue); -void cw1200_queue_unlock(struct cw1200_queue *queue); -bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, - unsigned long *timestamp, - u32 pending_frame_id); - -bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, - u32 link_id_map); - -static inline u8 cw1200_queue_get_queue_id(u32 packet_id) -{ - return (packet_id >> 16) & 0xFF; -} - -static inline u8 cw1200_queue_get_generation(u32 packet_id) -{ - return (packet_id >> 8) & 0xFF; -} - -#endif /* CW1200_QUEUE_H_INCLUDED */ diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c deleted file mode 100644 index bff81b8d4164..000000000000 --- a/drivers/net/wireless/cw1200/scan.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Scan implementation for ST-Ericsson CW1200 mac80211 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include "cw1200.h" -#include "scan.h" -#include "sta.h" -#include "pm.h" - -static void cw1200_scan_restart_delayed(struct cw1200_common *priv); - -static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) -{ - int ret, i; - int tmo = 2000; - - switch (priv->join_status) { - case CW1200_JOIN_STATUS_PRE_STA: - case CW1200_JOIN_STATUS_JOINING: - return -EBUSY; - default: - break; - } - - wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n", - scan->type, scan->num_channels, scan->flags); - - for (i = 0; i < scan->num_channels; ++i) - tmo += scan->ch[i].max_chan_time + 10; - - cancel_delayed_work_sync(&priv->clear_recent_scan_work); - atomic_set(&priv->scan.in_progress, 1); - atomic_set(&priv->recent_scan, 1); - cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo)); - queue_delayed_work(priv->workqueue, &priv->scan.timeout, - msecs_to_jiffies(tmo)); - ret = wsm_scan(priv, scan); - if (ret) { - atomic_set(&priv->scan.in_progress, 0); - cancel_delayed_work_sync(&priv->scan.timeout); - cw1200_scan_restart_delayed(priv); - } - return ret; -} - -int cw1200_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req) -{ - struct cw1200_common *priv = hw->priv; - struct cfg80211_scan_request *req = &hw_req->req; - struct wsm_template_frame frame = { - .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, - }; - int i, ret; - - if (!priv->vif) - return -EINVAL; - - /* Scan when P2P_GO corrupt firmware MiniAP mode */ - if (priv->join_status == CW1200_JOIN_STATUS_AP) - return -EOPNOTSUPP; - - if (req->n_ssids == 1 && !req->ssids[0].ssid_len) - req->n_ssids = 0; - - wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n", - req->n_ssids); - - if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) - return -EINVAL; - - frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, - req->ie_len); - if (!frame.skb) - return -ENOMEM; - - if (req->ie_len) - memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len); - - /* will be unlocked in cw1200_scan_work() */ - down(&priv->scan.lock); - mutex_lock(&priv->conf_mutex); - - ret = wsm_set_template_frame(priv, &frame); - if (!ret) { - /* Host want to be the probe responder. */ - ret = wsm_set_probe_responder(priv, true); - } - if (ret) { - mutex_unlock(&priv->conf_mutex); - up(&priv->scan.lock); - dev_kfree_skb(frame.skb); - return ret; - } - - wsm_lock_tx(priv); - - BUG_ON(priv->scan.req); - priv->scan.req = req; - priv->scan.n_ssids = 0; - priv->scan.status = 0; - priv->scan.begin = &req->channels[0]; - priv->scan.curr = priv->scan.begin; - priv->scan.end = &req->channels[req->n_channels]; - priv->scan.output_power = priv->output_power; - - for (i = 0; i < req->n_ssids; ++i) { - struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids]; - memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); - dst->length = req->ssids[i].ssid_len; - ++priv->scan.n_ssids; - } - - mutex_unlock(&priv->conf_mutex); - - if (frame.skb) - dev_kfree_skb(frame.skb); - queue_work(priv->workqueue, &priv->scan.work); - return 0; -} - -void cw1200_scan_work(struct work_struct *work) -{ - struct cw1200_common *priv = container_of(work, struct cw1200_common, - scan.work); - struct ieee80211_channel **it; - struct wsm_scan scan = { - .type = WSM_SCAN_TYPE_FOREGROUND, - .flags = WSM_SCAN_FLAG_SPLIT_METHOD, - }; - bool first_run = (priv->scan.begin == priv->scan.curr && - priv->scan.begin != priv->scan.end); - int i; - - if (first_run) { - /* Firmware gets crazy if scan request is sent - * when STA is joined but not yet associated. - * Force unjoin in this case. - */ - if (cancel_delayed_work_sync(&priv->join_timeout) > 0) - cw1200_join_timeout(&priv->join_timeout.work); - } - - mutex_lock(&priv->conf_mutex); - - if (first_run) { - if (priv->join_status == CW1200_JOIN_STATUS_STA && - !(priv->powersave_mode.mode & WSM_PSM_PS)) { - struct wsm_set_pm pm = priv->powersave_mode; - pm.mode = WSM_PSM_PS; - cw1200_set_pm(priv, &pm); - } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { - /* FW bug: driver has to restart p2p-dev mode - * after scan - */ - cw1200_disable_listening(priv); - } - } - - if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) { - if (priv->scan.output_power != priv->output_power) - wsm_set_output_power(priv, priv->output_power * 10); - if (priv->join_status == CW1200_JOIN_STATUS_STA && - !(priv->powersave_mode.mode & WSM_PSM_PS)) - cw1200_set_pm(priv, &priv->powersave_mode); - - if (priv->scan.status < 0) - wiphy_warn(priv->hw->wiphy, - "[SCAN] Scan failed (%d).\n", - priv->scan.status); - else if (priv->scan.req) - wiphy_dbg(priv->hw->wiphy, - "[SCAN] Scan completed.\n"); - else - wiphy_dbg(priv->hw->wiphy, - "[SCAN] Scan canceled.\n"); - - priv->scan.req = NULL; - cw1200_scan_restart_delayed(priv); - wsm_unlock_tx(priv); - mutex_unlock(&priv->conf_mutex); - ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0); - up(&priv->scan.lock); - return; - } else { - struct ieee80211_channel *first = *priv->scan.curr; - for (it = priv->scan.curr + 1, i = 1; - it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; - ++it, ++i) { - if ((*it)->band != first->band) - break; - if (((*it)->flags ^ first->flags) & - IEEE80211_CHAN_NO_IR) - break; - if (!(first->flags & IEEE80211_CHAN_NO_IR) && - (*it)->max_power != first->max_power) - break; - } - scan.band = first->band; - - if (priv->scan.req->no_cck) - scan.max_tx_rate = WSM_TRANSMIT_RATE_6; - else - scan.max_tx_rate = WSM_TRANSMIT_RATE_1; - scan.num_probes = - (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2; - scan.num_ssids = priv->scan.n_ssids; - scan.ssids = &priv->scan.ssids[0]; - scan.num_channels = it - priv->scan.curr; - /* TODO: Is it optimal? */ - scan.probe_delay = 100; - /* It is not stated in WSM specification, however - * FW team says that driver may not use FG scan - * when joined. - */ - if (priv->join_status == CW1200_JOIN_STATUS_STA) { - scan.type = WSM_SCAN_TYPE_BACKGROUND; - scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; - } - scan.ch = kzalloc( - sizeof(struct wsm_scan_ch) * (it - priv->scan.curr), - GFP_KERNEL); - if (!scan.ch) { - priv->scan.status = -ENOMEM; - goto fail; - } - for (i = 0; i < scan.num_channels; ++i) { - scan.ch[i].number = priv->scan.curr[i]->hw_value; - if (priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) { - scan.ch[i].min_chan_time = 50; - scan.ch[i].max_chan_time = 100; - } else { - scan.ch[i].min_chan_time = 10; - scan.ch[i].max_chan_time = 25; - } - } - if (!(first->flags & IEEE80211_CHAN_NO_IR) && - priv->scan.output_power != first->max_power) { - priv->scan.output_power = first->max_power; - wsm_set_output_power(priv, - priv->scan.output_power * 10); - } - priv->scan.status = cw1200_scan_start(priv, &scan); - kfree(scan.ch); - if (priv->scan.status) - goto fail; - priv->scan.curr = it; - } - mutex_unlock(&priv->conf_mutex); - return; - -fail: - priv->scan.curr = priv->scan.end; - mutex_unlock(&priv->conf_mutex); - queue_work(priv->workqueue, &priv->scan.work); - return; -} - -static void cw1200_scan_restart_delayed(struct cw1200_common *priv) -{ - /* FW bug: driver has to restart p2p-dev mode after scan. */ - if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { - cw1200_enable_listening(priv); - cw1200_update_filtering(priv); - } - - if (priv->delayed_unjoin) { - priv->delayed_unjoin = false; - if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) - wsm_unlock_tx(priv); - } else if (priv->delayed_link_loss) { - wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n"); - priv->delayed_link_loss = 0; - cw1200_cqm_bssloss_sm(priv, 1, 0, 0); - } -} - -static void cw1200_scan_complete(struct cw1200_common *priv) -{ - queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ); - if (priv->scan.direct_probe) { - wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n"); - cw1200_scan_restart_delayed(priv); - priv->scan.direct_probe = 0; - up(&priv->scan.lock); - wsm_unlock_tx(priv); - } else { - cw1200_scan_work(&priv->scan.work); - } -} - -void cw1200_scan_failed_cb(struct cw1200_common *priv) -{ - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) - /* STA is stopped. */ - return; - - if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { - priv->scan.status = -EIO; - queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); - } -} - - -void cw1200_scan_complete_cb(struct cw1200_common *priv, - struct wsm_scan_complete *arg) -{ - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) - /* STA is stopped. */ - return; - - if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { - priv->scan.status = 1; - queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); - } -} - -void cw1200_clear_recent_scan_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, - clear_recent_scan_work.work); - atomic_xchg(&priv->recent_scan, 0); -} - -void cw1200_scan_timeout(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, scan.timeout.work); - if (atomic_xchg(&priv->scan.in_progress, 0)) { - if (priv->scan.status > 0) { - priv->scan.status = 0; - } else if (!priv->scan.status) { - wiphy_warn(priv->hw->wiphy, - "Timeout waiting for scan complete notification.\n"); - priv->scan.status = -ETIMEDOUT; - priv->scan.curr = priv->scan.end; - wsm_stop_scan(priv); - } - cw1200_scan_complete(priv); - } -} - -void cw1200_probe_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, scan.probe_work.work); - u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); - struct cw1200_queue *queue = &priv->tx_queue[queue_id]; - const struct cw1200_txpriv *txpriv; - struct wsm_tx *wsm; - struct wsm_template_frame frame = { - .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, - }; - struct wsm_ssid ssids[1] = {{ - .length = 0, - } }; - struct wsm_scan_ch ch[1] = {{ - .min_chan_time = 0, - .max_chan_time = 10, - } }; - struct wsm_scan scan = { - .type = WSM_SCAN_TYPE_FOREGROUND, - .num_probes = 1, - .probe_delay = 0, - .num_channels = 1, - .ssids = ssids, - .ch = ch, - }; - u8 *ies; - size_t ies_len; - int ret; - - wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n"); - - mutex_lock(&priv->conf_mutex); - if (down_trylock(&priv->scan.lock)) { - /* Scan is already in progress. Requeue self. */ - schedule(); - queue_delayed_work(priv->workqueue, &priv->scan.probe_work, - msecs_to_jiffies(100)); - mutex_unlock(&priv->conf_mutex); - return; - } - - /* Make sure we still have a pending probe req */ - if (cw1200_queue_get_skb(queue, priv->pending_frame_id, - &frame.skb, &txpriv)) { - up(&priv->scan.lock); - mutex_unlock(&priv->conf_mutex); - wsm_unlock_tx(priv); - return; - } - wsm = (struct wsm_tx *)frame.skb->data; - scan.max_tx_rate = wsm->max_tx_rate; - scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? - WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; - if (priv->join_status == CW1200_JOIN_STATUS_STA || - priv->join_status == CW1200_JOIN_STATUS_IBSS) { - scan.type = WSM_SCAN_TYPE_BACKGROUND; - scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; - } - ch[0].number = priv->channel->hw_value; - - skb_pull(frame.skb, txpriv->offset); - - ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; - ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); - - if (ies_len) { - u8 *ssidie = - (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); - if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) { - u8 *nextie = &ssidie[2 + ssidie[1]]; - /* Remove SSID from the IE list. It has to be provided - * as a separate argument in cw1200_scan_start call - */ - - /* Store SSID localy */ - ssids[0].length = ssidie[1]; - memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length); - scan.num_ssids = 1; - - /* Remove SSID from IE list */ - ssidie[1] = 0; - memmove(&ssidie[2], nextie, &ies[ies_len] - nextie); - skb_trim(frame.skb, frame.skb->len - ssids[0].length); - } - } - - /* FW bug: driver has to restart p2p-dev mode after scan */ - if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) - cw1200_disable_listening(priv); - ret = wsm_set_template_frame(priv, &frame); - priv->scan.direct_probe = 1; - if (!ret) { - wsm_flush_tx(priv); - ret = cw1200_scan_start(priv, &scan); - } - mutex_unlock(&priv->conf_mutex); - - skb_push(frame.skb, txpriv->offset); - if (!ret) - IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK; - BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id)); - - if (ret) { - priv->scan.direct_probe = 0; - up(&priv->scan.lock); - wsm_unlock_tx(priv); - } - - return; -} diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/cw1200/scan.h deleted file mode 100644 index cc75459e5784..000000000000 --- a/drivers/net/wireless/cw1200/scan.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Scan interface for ST-Ericsson CW1200 mac80211 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef SCAN_H_INCLUDED -#define SCAN_H_INCLUDED - -#include -#include "wsm.h" - -/* external */ struct sk_buff; -/* external */ struct cfg80211_scan_request; -/* external */ struct ieee80211_channel; -/* external */ struct ieee80211_hw; -/* external */ struct work_struct; - -struct cw1200_scan { - struct semaphore lock; - struct work_struct work; - struct delayed_work timeout; - struct cfg80211_scan_request *req; - struct ieee80211_channel **begin; - struct ieee80211_channel **curr; - struct ieee80211_channel **end; - struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; - int output_power; - int n_ssids; - int status; - atomic_t in_progress; - /* Direct probe requests workaround */ - struct delayed_work probe_work; - int direct_probe; -}; - -int cw1200_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req); -void cw1200_scan_work(struct work_struct *work); -void cw1200_scan_timeout(struct work_struct *work); -void cw1200_clear_recent_scan_work(struct work_struct *work); -void cw1200_scan_complete_cb(struct cw1200_common *priv, - struct wsm_scan_complete *arg); -void cw1200_scan_failed_cb(struct cw1200_common *priv); - -/* ******************************************************************** */ -/* Raw probe requests TX workaround */ -void cw1200_probe_work(struct work_struct *work); - -#endif diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c deleted file mode 100644 index 95a7fdb3cc1c..000000000000 --- a/drivers/net/wireless/cw1200/sta.c +++ /dev/null @@ -1,2399 +0,0 @@ -/* - * Mac80211 STA API for ST-Ericsson CW1200 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include - -#include "cw1200.h" -#include "sta.h" -#include "fwio.h" -#include "bh.h" -#include "debug.h" - -#ifndef ERP_INFO_BYTE_OFFSET -#define ERP_INFO_BYTE_OFFSET 2 -#endif - -static void cw1200_do_join(struct cw1200_common *priv); -static void cw1200_do_unjoin(struct cw1200_common *priv); - -static int cw1200_upload_beacon(struct cw1200_common *priv); -static int cw1200_upload_pspoll(struct cw1200_common *priv); -static int cw1200_upload_null(struct cw1200_common *priv); -static int cw1200_upload_qosnull(struct cw1200_common *priv); -static int cw1200_start_ap(struct cw1200_common *priv); -static int cw1200_update_beaconing(struct cw1200_common *priv); -static int cw1200_enable_beaconing(struct cw1200_common *priv, - bool enable); -static void __cw1200_sta_notify(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - enum sta_notify_cmd notify_cmd, - int link_id); -static int __cw1200_flush(struct cw1200_common *priv, bool drop); - -static inline void __cw1200_free_event_queue(struct list_head *list) -{ - struct cw1200_wsm_event *event, *tmp; - list_for_each_entry_safe(event, tmp, list, link) { - list_del(&event->link); - kfree(event); - } -} - -/* ******************************************************************** */ -/* STA API */ - -int cw1200_start(struct ieee80211_hw *dev) -{ - struct cw1200_common *priv = dev->priv; - int ret = 0; - - cw1200_pm_stay_awake(&priv->pm_state, HZ); - - mutex_lock(&priv->conf_mutex); - - /* default EDCA */ - WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false); - WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false); - WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false); - WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false); - ret = wsm_set_edca_params(priv, &priv->edca); - if (ret) - goto out; - - ret = cw1200_set_uapsd_param(priv, &priv->edca); - if (ret) - goto out; - - priv->setbssparams_done = false; - - memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); - priv->mode = NL80211_IFTYPE_MONITOR; - priv->wep_default_key_id = -1; - - priv->cqm_beacon_loss_count = 10; - - ret = cw1200_setup_mac(priv); - if (ret) - goto out; - -out: - mutex_unlock(&priv->conf_mutex); - return ret; -} - -void cw1200_stop(struct ieee80211_hw *dev) -{ - struct cw1200_common *priv = dev->priv; - LIST_HEAD(list); - int i; - - wsm_lock_tx(priv); - - while (down_trylock(&priv->scan.lock)) { - /* Scan is in progress. Force it to stop. */ - priv->scan.req = NULL; - schedule(); - } - up(&priv->scan.lock); - - cancel_delayed_work_sync(&priv->scan.probe_work); - cancel_delayed_work_sync(&priv->scan.timeout); - cancel_delayed_work_sync(&priv->clear_recent_scan_work); - cancel_delayed_work_sync(&priv->join_timeout); - cw1200_cqm_bssloss_sm(priv, 0, 0, 0); - cancel_work_sync(&priv->unjoin_work); - cancel_delayed_work_sync(&priv->link_id_gc_work); - flush_workqueue(priv->workqueue); - del_timer_sync(&priv->mcast_timeout); - mutex_lock(&priv->conf_mutex); - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - priv->listening = false; - - spin_lock(&priv->event_queue_lock); - list_splice_init(&priv->event_queue, &list); - spin_unlock(&priv->event_queue_lock); - __cw1200_free_event_queue(&list); - - - priv->join_status = CW1200_JOIN_STATUS_PASSIVE; - priv->join_pending = false; - - for (i = 0; i < 4; i++) - cw1200_queue_clear(&priv->tx_queue[i]); - mutex_unlock(&priv->conf_mutex); - tx_policy_clean(priv); - - /* HACK! */ - if (atomic_xchg(&priv->tx_lock, 1) != 1) - pr_debug("[STA] TX is force-unlocked due to stop request.\n"); - - wsm_unlock_tx(priv); - atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */ -} - -static int cw1200_bssloss_mitigation = 1; -module_param(cw1200_bssloss_mitigation, int, 0644); -MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)"); - - -void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, - int init, int good, int bad) -{ - int tx = 0; - - priv->delayed_link_loss = 0; - cancel_work_sync(&priv->bss_params_work); - - pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n", - priv->bss_loss_state, - init, good, bad, - atomic_read(&priv->tx_lock), - priv->delayed_unjoin); - - /* If we have a pending unjoin */ - if (priv->delayed_unjoin) - return; - - if (init) { - queue_delayed_work(priv->workqueue, - &priv->bss_loss_work, - HZ); - priv->bss_loss_state = 0; - - /* Skip the confimration procedure in P2P case */ - if (!priv->vif->p2p && !atomic_read(&priv->tx_lock)) - tx = 1; - } else if (good) { - cancel_delayed_work_sync(&priv->bss_loss_work); - priv->bss_loss_state = 0; - queue_work(priv->workqueue, &priv->bss_params_work); - } else if (bad) { - /* XXX Should we just keep going until we time out? */ - if (priv->bss_loss_state < 3) - tx = 1; - } else { - cancel_delayed_work_sync(&priv->bss_loss_work); - priv->bss_loss_state = 0; - } - - /* Bypass mitigation if it's disabled */ - if (!cw1200_bssloss_mitigation) - tx = 0; - - /* Spit out a NULL packet to our AP if necessary */ - if (tx) { - struct sk_buff *skb; - - priv->bss_loss_state++; - - skb = ieee80211_nullfunc_get(priv->hw, priv->vif); - WARN_ON(!skb); - if (skb) - cw1200_tx(priv->hw, NULL, skb); - } -} - -int cw1200_add_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - int ret; - struct cw1200_common *priv = dev->priv; - /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ - - vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | - IEEE80211_VIF_SUPPORTS_UAPSD | - IEEE80211_VIF_SUPPORTS_CQM_RSSI; - - mutex_lock(&priv->conf_mutex); - - if (priv->mode != NL80211_IFTYPE_MONITOR) { - mutex_unlock(&priv->conf_mutex); - return -EOPNOTSUPP; - } - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_AP: - priv->mode = vif->type; - break; - default: - mutex_unlock(&priv->conf_mutex); - return -EOPNOTSUPP; - } - - priv->vif = vif; - memcpy(priv->mac_addr, vif->addr, ETH_ALEN); - ret = cw1200_setup_mac(priv); - /* Enable auto-calibration */ - /* Exception in subsequent channel switch; disabled. - * wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, - * &auto_calibration_mode, sizeof(auto_calibration_mode)); - */ - - mutex_unlock(&priv->conf_mutex); - return ret; -} - -void cw1200_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct cw1200_common *priv = dev->priv; - struct wsm_reset reset = { - .reset_statistics = true, - }; - int i; - - mutex_lock(&priv->conf_mutex); - switch (priv->join_status) { - case CW1200_JOIN_STATUS_JOINING: - case CW1200_JOIN_STATUS_PRE_STA: - case CW1200_JOIN_STATUS_STA: - case CW1200_JOIN_STATUS_IBSS: - wsm_lock_tx(priv); - if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) - wsm_unlock_tx(priv); - break; - case CW1200_JOIN_STATUS_AP: - for (i = 0; priv->link_id_map; ++i) { - if (priv->link_id_map & BIT(i)) { - reset.link_id = i; - wsm_reset(priv, &reset); - priv->link_id_map &= ~BIT(i); - } - } - memset(priv->link_id_db, 0, sizeof(priv->link_id_db)); - priv->sta_asleep_mask = 0; - priv->enable_beacon = false; - priv->tx_multicast = false; - priv->aid0_bit_set = false; - priv->buffered_multicasts = false; - priv->pspoll_mask = 0; - reset.link_id = 0; - wsm_reset(priv, &reset); - break; - case CW1200_JOIN_STATUS_MONITOR: - cw1200_update_listening(priv, false); - break; - default: - break; - } - priv->vif = NULL; - priv->mode = NL80211_IFTYPE_MONITOR; - eth_zero_addr(priv->mac_addr); - memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo)); - cw1200_free_keys(priv); - cw1200_setup_mac(priv); - priv->listening = false; - priv->join_status = CW1200_JOIN_STATUS_PASSIVE; - if (!__cw1200_flush(priv, true)) - wsm_unlock_tx(priv); - - mutex_unlock(&priv->conf_mutex); -} - -int cw1200_change_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - enum nl80211_iftype new_type, - bool p2p) -{ - int ret = 0; - pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type, - p2p, vif->type, vif->p2p); - - if (new_type != vif->type || vif->p2p != p2p) { - cw1200_remove_interface(dev, vif); - vif->type = new_type; - vif->p2p = p2p; - ret = cw1200_add_interface(dev, vif); - } - - return ret; -} - -int cw1200_config(struct ieee80211_hw *dev, u32 changed) -{ - int ret = 0; - struct cw1200_common *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; - - pr_debug("CONFIG CHANGED: %08x\n", changed); - - down(&priv->scan.lock); - mutex_lock(&priv->conf_mutex); - /* TODO: IEEE80211_CONF_CHANGE_QOS */ - /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */ - - if (changed & IEEE80211_CONF_CHANGE_POWER) { - priv->output_power = conf->power_level; - pr_debug("[STA] TX power: %d\n", priv->output_power); - wsm_set_output_power(priv, priv->output_power * 10); - } - - if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && - (priv->channel != conf->chandef.chan)) { - struct ieee80211_channel *ch = conf->chandef.chan; - struct wsm_switch_channel channel = { - .channel_number = ch->hw_value, - }; - pr_debug("[STA] Freq %d (wsm ch: %d).\n", - ch->center_freq, ch->hw_value); - - /* __cw1200_flush() implicitly locks tx, if successful */ - if (!__cw1200_flush(priv, false)) { - if (!wsm_switch_channel(priv, &channel)) { - ret = wait_event_timeout(priv->channel_switch_done, - !priv->channel_switch_in_progress, - 3 * HZ); - if (ret) { - /* Already unlocks if successful */ - priv->channel = ch; - ret = 0; - } else { - ret = -ETIMEDOUT; - } - } else { - /* Unlock if switch channel fails */ - wsm_unlock_tx(priv); - } - } - } - - if (changed & IEEE80211_CONF_CHANGE_PS) { - if (!(conf->flags & IEEE80211_CONF_PS)) - priv->powersave_mode.mode = WSM_PSM_ACTIVE; - else if (conf->dynamic_ps_timeout <= 0) - priv->powersave_mode.mode = WSM_PSM_PS; - else - priv->powersave_mode.mode = WSM_PSM_FAST_PS; - - /* Firmware requires that value for this 1-byte field must - * be specified in units of 500us. Values above the 128ms - * threshold are not supported. - */ - if (conf->dynamic_ps_timeout >= 0x80) - priv->powersave_mode.fast_psm_idle_period = 0xFF; - else - priv->powersave_mode.fast_psm_idle_period = - conf->dynamic_ps_timeout << 1; - - if (priv->join_status == CW1200_JOIN_STATUS_STA && - priv->bss_params.aid) - cw1200_set_pm(priv, &priv->powersave_mode); - } - - if (changed & IEEE80211_CONF_CHANGE_MONITOR) { - /* TBD: It looks like it's transparent - * there's a monitor interface present -- use this - * to determine for example whether to calculate - * timestamps for packets or not, do not use instead - * of filter flags! - */ - } - - if (changed & IEEE80211_CONF_CHANGE_IDLE) { - struct wsm_operational_mode mode = { - .power_mode = cw1200_power_mode, - .disable_more_flag_usage = true, - }; - - wsm_lock_tx(priv); - /* Disable p2p-dev mode forced by TX request */ - if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && - (conf->flags & IEEE80211_CONF_IDLE) && - !priv->listening) { - cw1200_disable_listening(priv); - priv->join_status = CW1200_JOIN_STATUS_PASSIVE; - } - wsm_set_operational_mode(priv, &mode); - wsm_unlock_tx(priv); - } - - if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { - pr_debug("[STA] Retry limits: %d (long), %d (short).\n", - conf->long_frame_max_tx_count, - conf->short_frame_max_tx_count); - spin_lock_bh(&priv->tx_policy_cache.lock); - priv->long_frame_max_tx_count = conf->long_frame_max_tx_count; - priv->short_frame_max_tx_count = - (conf->short_frame_max_tx_count < 0x0F) ? - conf->short_frame_max_tx_count : 0x0F; - priv->hw->max_rate_tries = priv->short_frame_max_tx_count; - spin_unlock_bh(&priv->tx_policy_cache.lock); - } - mutex_unlock(&priv->conf_mutex); - up(&priv->scan.lock); - return ret; -} - -void cw1200_update_filtering(struct cw1200_common *priv) -{ - int ret; - bool bssid_filtering = !priv->rx_filter.bssid; - bool is_p2p = priv->vif && priv->vif->p2p; - bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type; - - static struct wsm_beacon_filter_control bf_ctrl; - static struct wsm_mib_beacon_filter_table bf_tbl = { - .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC, - .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | - WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | - WSM_BEACON_FILTER_IE_HAS_APPEARED, - .entry[0].oui[0] = 0x50, - .entry[0].oui[1] = 0x6F, - .entry[0].oui[2] = 0x9A, - .entry[1].ie_id = WLAN_EID_HT_OPERATION, - .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | - WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | - WSM_BEACON_FILTER_IE_HAS_APPEARED, - .entry[2].ie_id = WLAN_EID_ERP_INFO, - .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | - WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | - WSM_BEACON_FILTER_IE_HAS_APPEARED, - }; - - if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) - return; - else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) - bssid_filtering = false; - - if (priv->disable_beacon_filter) { - bf_ctrl.enabled = 0; - bf_ctrl.bcn_count = 1; - bf_tbl.num = __cpu_to_le32(0); - } else if (is_p2p || !is_sta) { - bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE | - WSM_BEACON_FILTER_AUTO_ERP; - bf_ctrl.bcn_count = 0; - bf_tbl.num = __cpu_to_le32(2); - } else { - bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE; - bf_ctrl.bcn_count = 0; - bf_tbl.num = __cpu_to_le32(3); - } - - /* When acting as p2p client being connected to p2p GO, in order to - * receive frames from a different p2p device, turn off bssid filter. - * - * WARNING: FW dependency! - * This can only be used with FW WSM371 and its successors. - * In that FW version even with bssid filter turned off, - * device will block most of the unwanted frames. - */ - if (is_p2p) - bssid_filtering = false; - - ret = wsm_set_rx_filter(priv, &priv->rx_filter); - if (!ret) - ret = wsm_set_beacon_filter_table(priv, &bf_tbl); - if (!ret) - ret = wsm_beacon_filter_control(priv, &bf_ctrl); - if (!ret) - ret = wsm_set_bssid_filtering(priv, bssid_filtering); - if (!ret) - ret = wsm_set_multicast_filter(priv, &priv->multicast_filter); - if (ret) - wiphy_err(priv->hw->wiphy, - "Update filtering failed: %d.\n", ret); - return; -} - -void cw1200_update_filtering_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, - update_filtering_work); - - cw1200_update_filtering(priv); -} - -void cw1200_set_beacon_wakeup_period_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, - set_beacon_wakeup_period_work); - - wsm_set_beacon_wakeup_period(priv, - priv->beacon_int * priv->join_dtim_period > - MAX_BEACON_SKIP_TIME_MS ? 1 : - priv->join_dtim_period, 0); -} - -u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, - struct netdev_hw_addr_list *mc_list) -{ - static u8 broadcast_ipv6[ETH_ALEN] = { - 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 - }; - static u8 broadcast_ipv4[ETH_ALEN] = { - 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 - }; - struct cw1200_common *priv = hw->priv; - struct netdev_hw_addr *ha; - int count = 0; - - /* Disable multicast filtering */ - priv->has_multicast_subscription = false; - memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); - - if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) - return 0; - - /* Enable if requested */ - netdev_hw_addr_list_for_each(ha, mc_list) { - pr_debug("[STA] multicast: %pM\n", ha->addr); - memcpy(&priv->multicast_filter.macaddrs[count], - ha->addr, ETH_ALEN); - if (!ether_addr_equal(ha->addr, broadcast_ipv4) && - !ether_addr_equal(ha->addr, broadcast_ipv6)) - priv->has_multicast_subscription = true; - count++; - } - - if (count) { - priv->multicast_filter.enable = __cpu_to_le32(1); - priv->multicast_filter.num_addrs = __cpu_to_le32(count); - } - - return netdev_hw_addr_list_count(mc_list); -} - -void cw1200_configure_filter(struct ieee80211_hw *dev, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct cw1200_common *priv = dev->priv; - bool listening = !!(*total_flags & - (FIF_OTHER_BSS | - FIF_BCN_PRBRESP_PROMISC | - FIF_PROBE_REQ)); - - *total_flags &= FIF_OTHER_BSS | - FIF_FCSFAIL | - FIF_BCN_PRBRESP_PROMISC | - FIF_PROBE_REQ; - - down(&priv->scan.lock); - mutex_lock(&priv->conf_mutex); - - priv->rx_filter.promiscuous = 0; - priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS | - FIF_PROBE_REQ)) ? 1 : 0; - priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; - priv->disable_beacon_filter = !(*total_flags & - (FIF_BCN_PRBRESP_PROMISC | - FIF_PROBE_REQ)); - if (priv->listening != listening) { - priv->listening = listening; - wsm_lock_tx(priv); - cw1200_update_listening(priv, listening); - wsm_unlock_tx(priv); - } - cw1200_update_filtering(priv); - mutex_unlock(&priv->conf_mutex); - up(&priv->scan.lock); -} - -int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, - u16 queue, const struct ieee80211_tx_queue_params *params) -{ - struct cw1200_common *priv = dev->priv; - int ret = 0; - /* To prevent re-applying PM request OID again and again*/ - bool old_uapsd_flags; - - mutex_lock(&priv->conf_mutex); - - if (queue < dev->queues) { - old_uapsd_flags = le16_to_cpu(priv->uapsd_info.uapsd_flags); - - WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0); - ret = wsm_set_tx_queue_params(priv, - &priv->tx_queue_params.params[queue], queue); - if (ret) { - ret = -EINVAL; - goto out; - } - - WSM_EDCA_SET(&priv->edca, queue, params->aifs, - params->cw_min, params->cw_max, - params->txop, 0xc8, - params->uapsd); - ret = wsm_set_edca_params(priv, &priv->edca); - if (ret) { - ret = -EINVAL; - goto out; - } - - if (priv->mode == NL80211_IFTYPE_STATION) { - ret = cw1200_set_uapsd_param(priv, &priv->edca); - if (!ret && priv->setbssparams_done && - (priv->join_status == CW1200_JOIN_STATUS_STA) && - (old_uapsd_flags != le16_to_cpu(priv->uapsd_info.uapsd_flags))) - ret = cw1200_set_pm(priv, &priv->powersave_mode); - } - } else { - ret = -EINVAL; - } - -out: - mutex_unlock(&priv->conf_mutex); - return ret; -} - -int cw1200_get_stats(struct ieee80211_hw *dev, - struct ieee80211_low_level_stats *stats) -{ - struct cw1200_common *priv = dev->priv; - - memcpy(stats, &priv->stats, sizeof(*stats)); - return 0; -} - -int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) -{ - struct wsm_set_pm pm = *arg; - - if (priv->uapsd_info.uapsd_flags != 0) - pm.mode &= ~WSM_PSM_FAST_PS_FLAG; - - if (memcmp(&pm, &priv->firmware_ps_mode, - sizeof(struct wsm_set_pm))) { - priv->firmware_ps_mode = pm; - return wsm_set_pm(priv, &pm); - } else { - return 0; - } -} - -int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - int ret = -EOPNOTSUPP; - struct cw1200_common *priv = dev->priv; - struct ieee80211_key_seq seq; - - mutex_lock(&priv->conf_mutex); - - if (cmd == SET_KEY) { - u8 *peer_addr = NULL; - int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? - 1 : 0; - int idx = cw1200_alloc_key(priv); - struct wsm_add_key *wsm_key = &priv->keys[idx]; - - if (idx < 0) { - ret = -EINVAL; - goto finally; - } - - if (sta) - peer_addr = sta->addr; - - key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE | - IEEE80211_KEY_FLAG_RESERVE_TAILROOM; - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - if (key->keylen > 16) { - cw1200_free_key(priv, idx); - ret = -EINVAL; - goto finally; - } - - if (pairwise) { - wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; - memcpy(wsm_key->wep_pairwise.peer, - peer_addr, ETH_ALEN); - memcpy(wsm_key->wep_pairwise.keydata, - &key->key[0], key->keylen); - wsm_key->wep_pairwise.keylen = key->keylen; - } else { - wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; - memcpy(wsm_key->wep_group.keydata, - &key->key[0], key->keylen); - wsm_key->wep_group.keylen = key->keylen; - wsm_key->wep_group.keyid = key->keyidx; - } - break; - case WLAN_CIPHER_SUITE_TKIP: - ieee80211_get_key_rx_seq(key, 0, &seq); - if (pairwise) { - wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; - memcpy(wsm_key->tkip_pairwise.peer, - peer_addr, ETH_ALEN); - memcpy(wsm_key->tkip_pairwise.keydata, - &key->key[0], 16); - memcpy(wsm_key->tkip_pairwise.tx_mic_key, - &key->key[16], 8); - memcpy(wsm_key->tkip_pairwise.rx_mic_key, - &key->key[24], 8); - } else { - size_t mic_offset = - (priv->mode == NL80211_IFTYPE_AP) ? - 16 : 24; - wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; - memcpy(wsm_key->tkip_group.keydata, - &key->key[0], 16); - memcpy(wsm_key->tkip_group.rx_mic_key, - &key->key[mic_offset], 8); - - wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff; - wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff; - wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff; - wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff; - wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff; - wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff; - wsm_key->tkip_group.rx_seqnum[6] = 0; - wsm_key->tkip_group.rx_seqnum[7] = 0; - - wsm_key->tkip_group.keyid = key->keyidx; - } - break; - case WLAN_CIPHER_SUITE_CCMP: - ieee80211_get_key_rx_seq(key, 0, &seq); - if (pairwise) { - wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; - memcpy(wsm_key->aes_pairwise.peer, - peer_addr, ETH_ALEN); - memcpy(wsm_key->aes_pairwise.keydata, - &key->key[0], 16); - } else { - wsm_key->type = WSM_KEY_TYPE_AES_GROUP; - memcpy(wsm_key->aes_group.keydata, - &key->key[0], 16); - - wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5]; - wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4]; - wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3]; - wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2]; - wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1]; - wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0]; - wsm_key->aes_group.rx_seqnum[6] = 0; - wsm_key->aes_group.rx_seqnum[7] = 0; - wsm_key->aes_group.keyid = key->keyidx; - } - break; - case WLAN_CIPHER_SUITE_SMS4: - if (pairwise) { - wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; - memcpy(wsm_key->wapi_pairwise.peer, - peer_addr, ETH_ALEN); - memcpy(wsm_key->wapi_pairwise.keydata, - &key->key[0], 16); - memcpy(wsm_key->wapi_pairwise.mic_key, - &key->key[16], 16); - wsm_key->wapi_pairwise.keyid = key->keyidx; - } else { - wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; - memcpy(wsm_key->wapi_group.keydata, - &key->key[0], 16); - memcpy(wsm_key->wapi_group.mic_key, - &key->key[16], 16); - wsm_key->wapi_group.keyid = key->keyidx; - } - break; - default: - pr_warn("Unhandled key type %d\n", key->cipher); - cw1200_free_key(priv, idx); - ret = -EOPNOTSUPP; - goto finally; - } - ret = wsm_add_key(priv, wsm_key); - if (!ret) - key->hw_key_idx = idx; - else - cw1200_free_key(priv, idx); - } else if (cmd == DISABLE_KEY) { - struct wsm_remove_key wsm_key = { - .index = key->hw_key_idx, - }; - - if (wsm_key.index > WSM_KEY_MAX_INDEX) { - ret = -EINVAL; - goto finally; - } - - cw1200_free_key(priv, wsm_key.index); - ret = wsm_remove_key(priv, &wsm_key); - } else { - pr_warn("Unhandled key command %d\n", cmd); - } - -finally: - mutex_unlock(&priv->conf_mutex); - return ret; -} - -void cw1200_wep_key_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, wep_key_work); - u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); - struct cw1200_queue *queue = &priv->tx_queue[queue_id]; - __le32 wep_default_key_id = __cpu_to_le32( - priv->wep_default_key_id); - - pr_debug("[STA] Setting default WEP key: %d\n", - priv->wep_default_key_id); - wsm_flush_tx(priv); - wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, - &wep_default_key_id, sizeof(wep_default_key_id)); - cw1200_queue_requeue(queue, priv->pending_frame_id); - wsm_unlock_tx(priv); -} - -int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) -{ - int ret = 0; - __le32 val32; - struct cw1200_common *priv = hw->priv; - - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) - return 0; - - if (value != (u32) -1) - val32 = __cpu_to_le32(value); - else - val32 = 0; /* disabled */ - - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { - /* device is down, can _not_ set threshold */ - ret = -ENODEV; - goto out; - } - - if (priv->rts_threshold == value) - goto out; - - pr_debug("[STA] Setting RTS threshold: %d\n", - priv->rts_threshold); - - /* mutex_lock(&priv->conf_mutex); */ - ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, - &val32, sizeof(val32)); - if (!ret) - priv->rts_threshold = value; - /* mutex_unlock(&priv->conf_mutex); */ - -out: - return ret; -} - -/* If successful, LOCKS the TX queue! */ -static int __cw1200_flush(struct cw1200_common *priv, bool drop) -{ - int i, ret; - - for (;;) { - /* TODO: correct flush handling is required when dev_stop. - * Temporary workaround: 2s - */ - if (drop) { - for (i = 0; i < 4; ++i) - cw1200_queue_clear(&priv->tx_queue[i]); - } else { - ret = wait_event_timeout( - priv->tx_queue_stats.wait_link_id_empty, - cw1200_queue_stats_is_empty( - &priv->tx_queue_stats, -1), - 2 * HZ); - } - - if (!drop && ret <= 0) { - ret = -ETIMEDOUT; - break; - } else { - ret = 0; - } - - wsm_lock_tx(priv); - if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) { - /* Highly unlikely: WSM requeued frames. */ - wsm_unlock_tx(priv); - continue; - } - break; - } - return ret; -} - -void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 queues, bool drop) -{ - struct cw1200_common *priv = hw->priv; - - switch (priv->mode) { - case NL80211_IFTYPE_MONITOR: - drop = true; - break; - case NL80211_IFTYPE_AP: - if (!priv->enable_beacon) - drop = true; - break; - } - - if (!__cw1200_flush(priv, drop)) - wsm_unlock_tx(priv); - - return; -} - -/* ******************************************************************** */ -/* WSM callbacks */ - -void cw1200_free_event_queue(struct cw1200_common *priv) -{ - LIST_HEAD(list); - - spin_lock(&priv->event_queue_lock); - list_splice_init(&priv->event_queue, &list); - spin_unlock(&priv->event_queue_lock); - - __cw1200_free_event_queue(&list); -} - -void cw1200_event_handler(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, event_handler); - struct cw1200_wsm_event *event; - LIST_HEAD(list); - - spin_lock(&priv->event_queue_lock); - list_splice_init(&priv->event_queue, &list); - spin_unlock(&priv->event_queue_lock); - - list_for_each_entry(event, &list, link) { - switch (event->evt.id) { - case WSM_EVENT_ERROR: - pr_err("Unhandled WSM Error from LMAC\n"); - break; - case WSM_EVENT_BSS_LOST: - pr_debug("[CQM] BSS lost.\n"); - cancel_work_sync(&priv->unjoin_work); - if (!down_trylock(&priv->scan.lock)) { - cw1200_cqm_bssloss_sm(priv, 1, 0, 0); - up(&priv->scan.lock); - } else { - /* Scan is in progress. Delay reporting. - * Scan complete will trigger bss_loss_work - */ - priv->delayed_link_loss = 1; - /* Also start a watchdog. */ - queue_delayed_work(priv->workqueue, - &priv->bss_loss_work, 5*HZ); - } - break; - case WSM_EVENT_BSS_REGAINED: - pr_debug("[CQM] BSS regained.\n"); - cw1200_cqm_bssloss_sm(priv, 0, 0, 0); - cancel_work_sync(&priv->unjoin_work); - break; - case WSM_EVENT_RADAR_DETECTED: - wiphy_info(priv->hw->wiphy, "radar pulse detected\n"); - break; - case WSM_EVENT_RCPI_RSSI: - { - /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 - * RSSI = RCPI / 2 - 110 - */ - int rcpi_rssi = (int)(event->evt.data & 0xFF); - int cqm_evt; - if (priv->cqm_use_rssi) - rcpi_rssi = (s8)rcpi_rssi; - else - rcpi_rssi = rcpi_rssi / 2 - 110; - - cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ? - NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : - NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; - pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi); - ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, - GFP_KERNEL); - break; - } - case WSM_EVENT_BT_INACTIVE: - pr_warn("Unhandled BT INACTIVE from LMAC\n"); - break; - case WSM_EVENT_BT_ACTIVE: - pr_warn("Unhandled BT ACTIVE from LMAC\n"); - break; - } - } - __cw1200_free_event_queue(&list); -} - -void cw1200_bss_loss_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, bss_loss_work.work); - - pr_debug("[CQM] Reporting connection loss.\n"); - wsm_lock_tx(priv); - if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) - wsm_unlock_tx(priv); -} - -void cw1200_bss_params_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, bss_params_work); - mutex_lock(&priv->conf_mutex); - - priv->bss_params.reset_beacon_loss = 1; - wsm_set_bss_params(priv, &priv->bss_params); - priv->bss_params.reset_beacon_loss = 0; - - mutex_unlock(&priv->conf_mutex); -} - -/* ******************************************************************** */ -/* Internal API */ - -/* This function is called to Parse the SDD file - * to extract listen_interval and PTA related information - * sdd is a TLV: u8 id, u8 len, u8 data[] - */ -static int cw1200_parse_sdd_file(struct cw1200_common *priv) -{ - const u8 *p = priv->sdd->data; - int ret = 0; - - while (p + 2 <= priv->sdd->data + priv->sdd->size) { - if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) { - pr_warn("Malformed sdd structure\n"); - return -1; - } - switch (p[0]) { - case SDD_PTA_CFG_ELT_ID: { - u16 v; - if (p[1] < 4) { - pr_warn("SDD_PTA_CFG_ELT_ID malformed\n"); - ret = -1; - break; - } - v = le16_to_cpu(*((__le16 *)(p + 2))); - if (!v) /* non-zero means this is enabled */ - break; - - v = le16_to_cpu(*((__le16 *)(p + 4))); - priv->conf_listen_interval = (v >> 7) & 0x1F; - pr_debug("PTA found; Listen Interval %d\n", - priv->conf_listen_interval); - break; - } - case SDD_REFERENCE_FREQUENCY_ELT_ID: { - u16 clk = le16_to_cpu(*((__le16 *)(p + 2))); - if (clk != priv->hw_refclk) - pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n", - clk, priv->hw_refclk); - break; - } - default: - break; - } - p += p[1] + 2; - } - - if (!priv->bt_present) { - pr_debug("PTA element NOT found.\n"); - priv->conf_listen_interval = 0; - } - return ret; -} - -int cw1200_setup_mac(struct cw1200_common *priv) -{ - int ret = 0; - - /* NOTE: There is a bug in FW: it reports signal - * as RSSI if RSSI subscription is enabled. - * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. - * - * NOTE2: RSSI based reports have been switched to RCPI, since - * FW has a bug and RSSI reported values are not stable, - * what can leads to signal level oscilations in user-end applications - */ - struct wsm_rcpi_rssi_threshold threshold = { - .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | - WSM_RCPI_RSSI_DONT_USE_UPPER | - WSM_RCPI_RSSI_DONT_USE_LOWER, - .rollingAverageCount = 16, - }; - - struct wsm_configuration cfg = { - .dot11StationId = &priv->mac_addr[0], - }; - - /* Remember the decission here to make sure, we will handle - * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS - */ - if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI) - priv->cqm_use_rssi = true; - - if (!priv->sdd) { - ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev); - if (ret) { - pr_err("Can't load sdd file %s.\n", priv->sdd_path); - return ret; - } - cw1200_parse_sdd_file(priv); - } - - cfg.dpdData = priv->sdd->data; - cfg.dpdData_size = priv->sdd->size; - ret = wsm_configuration(priv, &cfg); - if (ret) - return ret; - - /* Configure RSSI/SCPI reporting as RSSI. */ - wsm_set_rcpi_rssi_threshold(priv, &threshold); - - return 0; -} - -static void cw1200_join_complete(struct cw1200_common *priv) -{ - pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status); - - priv->join_pending = false; - if (priv->join_complete_status) { - priv->join_status = CW1200_JOIN_STATUS_PASSIVE; - cw1200_update_listening(priv, priv->listening); - cw1200_do_unjoin(priv); - ieee80211_connection_loss(priv->vif); - } else { - if (priv->mode == NL80211_IFTYPE_ADHOC) - priv->join_status = CW1200_JOIN_STATUS_IBSS; - else - priv->join_status = CW1200_JOIN_STATUS_PRE_STA; - } - wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */ -} - -void cw1200_join_complete_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, join_complete_work); - mutex_lock(&priv->conf_mutex); - cw1200_join_complete(priv); - mutex_unlock(&priv->conf_mutex); -} - -void cw1200_join_complete_cb(struct cw1200_common *priv, - struct wsm_join_complete *arg) -{ - pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n", - arg->status); - - if (cancel_delayed_work(&priv->join_timeout)) { - priv->join_complete_status = arg->status; - queue_work(priv->workqueue, &priv->join_complete_work); - } -} - -/* MUST be called with tx_lock held! It will be unlocked for us. */ -static void cw1200_do_join(struct cw1200_common *priv) -{ - const u8 *bssid; - struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; - struct cfg80211_bss *bss = NULL; - struct wsm_protected_mgmt_policy mgmt_policy; - struct wsm_join join = { - .mode = conf->ibss_joined ? - WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, - .preamble_type = WSM_JOIN_PREAMBLE_LONG, - .probe_for_join = 1, - .atim_window = 0, - .basic_rate_set = cw1200_rate_mask_to_wsm(priv, - conf->basic_rates), - }; - if (delayed_work_pending(&priv->join_timeout)) { - pr_warn("[STA] - Join request already pending, skipping..\n"); - wsm_unlock_tx(priv); - return; - } - - if (priv->join_status) - cw1200_do_unjoin(priv); - - bssid = priv->vif->bss_conf.bssid; - - bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0, - IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); - - if (!bss && !conf->ibss_joined) { - wsm_unlock_tx(priv); - return; - } - - mutex_lock(&priv->conf_mutex); - - /* Under the conf lock: check scan status and - * bail out if it is in progress. - */ - if (atomic_read(&priv->scan.in_progress)) { - wsm_unlock_tx(priv); - goto done_put; - } - - priv->join_pending = true; - - /* Sanity check basic rates */ - if (!join.basic_rate_set) - join.basic_rate_set = 7; - - /* Sanity check beacon interval */ - if (!priv->beacon_int) - priv->beacon_int = 1; - - join.beacon_interval = priv->beacon_int; - - /* BT Coex related changes */ - if (priv->bt_present) { - if (((priv->conf_listen_interval * 100) % - priv->beacon_int) == 0) - priv->listen_interval = - ((priv->conf_listen_interval * 100) / - priv->beacon_int); - else - priv->listen_interval = - ((priv->conf_listen_interval * 100) / - priv->beacon_int + 1); - } - - if (priv->hw->conf.ps_dtim_period) - priv->join_dtim_period = priv->hw->conf.ps_dtim_period; - join.dtim_period = priv->join_dtim_period; - - join.channel_number = priv->channel->hw_value; - join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? - WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; - - memcpy(join.bssid, bssid, sizeof(join.bssid)); - - pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n", - join.bssid, - join.dtim_period, priv->beacon_int); - - if (!conf->ibss_joined) { - const u8 *ssidie; - rcu_read_lock(); - ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); - if (ssidie) { - join.ssid_len = ssidie[1]; - memcpy(join.ssid, &ssidie[2], join.ssid_len); - } - rcu_read_unlock(); - } - - if (priv->vif->p2p) { - join.flags |= WSM_JOIN_FLAGS_P2P_GO; - join.basic_rate_set = - cw1200_rate_mask_to_wsm(priv, 0xFF0); - } - - /* Enable asynchronous join calls */ - if (!conf->ibss_joined) { - join.flags |= WSM_JOIN_FLAGS_FORCE; - join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND; - } - - wsm_flush_tx(priv); - - /* Stay Awake for Join and Auth Timeouts and a bit more */ - cw1200_pm_stay_awake(&priv->pm_state, - CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT); - - cw1200_update_listening(priv, false); - - /* Turn on Block ACKs */ - wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask, - priv->ba_rx_tid_mask); - - /* Set up timeout */ - if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) { - priv->join_status = CW1200_JOIN_STATUS_JOINING; - queue_delayed_work(priv->workqueue, - &priv->join_timeout, - CW1200_JOIN_TIMEOUT); - } - - /* 802.11w protected mgmt frames */ - mgmt_policy.protectedMgmtEnable = 0; - mgmt_policy.unprotectedMgmtFramesAllowed = 1; - mgmt_policy.encryptionForAuthFrame = 1; - wsm_set_protected_mgmt_policy(priv, &mgmt_policy); - - /* Perform actual join */ - if (wsm_join(priv, &join)) { - pr_err("[STA] cw1200_join_work: wsm_join failed!\n"); - cancel_delayed_work_sync(&priv->join_timeout); - cw1200_update_listening(priv, priv->listening); - /* Tx lock still held, unjoin will clear it. */ - if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) - wsm_unlock_tx(priv); - } else { - if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND)) - cw1200_join_complete(priv); /* Will clear tx_lock */ - - /* Upload keys */ - cw1200_upload_keys(priv); - - /* Due to beacon filtering it is possible that the - * AP's beacon is not known for the mac80211 stack. - * Disable filtering temporary to make sure the stack - * receives at least one - */ - priv->disable_beacon_filter = true; - } - cw1200_update_filtering(priv); - -done_put: - mutex_unlock(&priv->conf_mutex); - if (bss) - cfg80211_put_bss(priv->hw->wiphy, bss); -} - -void cw1200_join_timeout(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, join_timeout.work); - pr_debug("[WSM] Join timed out.\n"); - wsm_lock_tx(priv); - if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) - wsm_unlock_tx(priv); -} - -static void cw1200_do_unjoin(struct cw1200_common *priv) -{ - struct wsm_reset reset = { - .reset_statistics = true, - }; - - cancel_delayed_work_sync(&priv->join_timeout); - - mutex_lock(&priv->conf_mutex); - priv->join_pending = false; - - if (atomic_read(&priv->scan.in_progress)) { - if (priv->delayed_unjoin) - wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n"); - else - priv->delayed_unjoin = true; - goto done; - } - - priv->delayed_link_loss = false; - - if (!priv->join_status) - goto done; - - if (priv->join_status == CW1200_JOIN_STATUS_AP) - goto done; - - cancel_work_sync(&priv->update_filtering_work); - cancel_work_sync(&priv->set_beacon_wakeup_period_work); - priv->join_status = CW1200_JOIN_STATUS_PASSIVE; - - /* Unjoin is a reset. */ - wsm_flush_tx(priv); - wsm_keep_alive_period(priv, 0); - wsm_reset(priv, &reset); - wsm_set_output_power(priv, priv->output_power * 10); - priv->join_dtim_period = 0; - cw1200_setup_mac(priv); - cw1200_free_event_queue(priv); - cancel_work_sync(&priv->event_handler); - cw1200_update_listening(priv, priv->listening); - cw1200_cqm_bssloss_sm(priv, 0, 0, 0); - - /* Disable Block ACKs */ - wsm_set_block_ack_policy(priv, 0, 0); - - priv->disable_beacon_filter = false; - cw1200_update_filtering(priv); - memset(&priv->association_mode, 0, - sizeof(priv->association_mode)); - memset(&priv->bss_params, 0, sizeof(priv->bss_params)); - priv->setbssparams_done = false; - memset(&priv->firmware_ps_mode, 0, - sizeof(priv->firmware_ps_mode)); - - pr_debug("[STA] Unjoin completed.\n"); - -done: - mutex_unlock(&priv->conf_mutex); -} - -void cw1200_unjoin_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, unjoin_work); - - cw1200_do_unjoin(priv); - - /* Tell the stack we're dead */ - ieee80211_connection_loss(priv->vif); - - wsm_unlock_tx(priv); -} - -int cw1200_enable_listening(struct cw1200_common *priv) -{ - struct wsm_start start = { - .mode = WSM_START_MODE_P2P_DEV, - .band = WSM_PHY_BAND_2_4G, - .beacon_interval = 100, - .dtim_period = 1, - .probe_delay = 0, - .basic_rate_set = 0x0F, - }; - - if (priv->channel) { - start.band = priv->channel->band == IEEE80211_BAND_5GHZ ? - WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; - start.channel_number = priv->channel->hw_value; - } else { - start.band = WSM_PHY_BAND_2_4G; - start.channel_number = 1; - } - - return wsm_start(priv, &start); -} - -int cw1200_disable_listening(struct cw1200_common *priv) -{ - int ret; - struct wsm_reset reset = { - .reset_statistics = true, - }; - ret = wsm_reset(priv, &reset); - return ret; -} - -void cw1200_update_listening(struct cw1200_common *priv, bool enabled) -{ - if (enabled) { - if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) { - if (!cw1200_enable_listening(priv)) - priv->join_status = CW1200_JOIN_STATUS_MONITOR; - wsm_set_probe_responder(priv, true); - } - } else { - if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { - if (!cw1200_disable_listening(priv)) - priv->join_status = CW1200_JOIN_STATUS_PASSIVE; - wsm_set_probe_responder(priv, false); - } - } -} - -int cw1200_set_uapsd_param(struct cw1200_common *priv, - const struct wsm_edca_params *arg) -{ - int ret; - u16 uapsd_flags = 0; - - /* Here's the mapping AC [queue, bit] - * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0] - */ - - if (arg->uapsd_enable[0]) - uapsd_flags |= 1 << 3; - - if (arg->uapsd_enable[1]) - uapsd_flags |= 1 << 2; - - if (arg->uapsd_enable[2]) - uapsd_flags |= 1 << 1; - - if (arg->uapsd_enable[3]) - uapsd_flags |= 1; - - /* Currently pseudo U-APSD operation is not supported, so setting - * MinAutoTriggerInterval, MaxAutoTriggerInterval and - * AutoTriggerStep to 0 - */ - - priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags); - priv->uapsd_info.min_auto_trigger_interval = 0; - priv->uapsd_info.max_auto_trigger_interval = 0; - priv->uapsd_info.auto_trigger_step = 0; - - ret = wsm_set_uapsd_info(priv, &priv->uapsd_info); - return ret; -} - -/* ******************************************************************** */ -/* AP API */ - -int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct cw1200_common *priv = hw->priv; - struct cw1200_sta_priv *sta_priv = - (struct cw1200_sta_priv *)&sta->drv_priv; - struct cw1200_link_entry *entry; - struct sk_buff *skb; - - if (priv->mode != NL80211_IFTYPE_AP) - return 0; - - sta_priv->link_id = cw1200_find_link_id(priv, sta->addr); - if (WARN_ON(!sta_priv->link_id)) { - wiphy_info(priv->hw->wiphy, - "[AP] No more link IDs available.\n"); - return -ENOENT; - } - - entry = &priv->link_id_db[sta_priv->link_id - 1]; - spin_lock_bh(&priv->ps_state_lock); - if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == - IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) - priv->sta_asleep_mask |= BIT(sta_priv->link_id); - entry->status = CW1200_LINK_HARD; - while ((skb = skb_dequeue(&entry->rx_queue))) - ieee80211_rx_irqsafe(priv->hw, skb); - spin_unlock_bh(&priv->ps_state_lock); - return 0; -} - -int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct cw1200_common *priv = hw->priv; - struct cw1200_sta_priv *sta_priv = - (struct cw1200_sta_priv *)&sta->drv_priv; - struct cw1200_link_entry *entry; - - if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) - return 0; - - entry = &priv->link_id_db[sta_priv->link_id - 1]; - spin_lock_bh(&priv->ps_state_lock); - entry->status = CW1200_LINK_RESERVE; - entry->timestamp = jiffies; - wsm_lock_tx_async(priv); - if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) - wsm_unlock_tx(priv); - spin_unlock_bh(&priv->ps_state_lock); - flush_workqueue(priv->workqueue); - return 0; -} - -static void __cw1200_sta_notify(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - enum sta_notify_cmd notify_cmd, - int link_id) -{ - struct cw1200_common *priv = dev->priv; - u32 bit, prev; - - /* Zero link id means "for all link IDs" */ - if (link_id) - bit = BIT(link_id); - else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE)) - bit = 0; - else - bit = priv->link_id_map; - prev = priv->sta_asleep_mask & bit; - - switch (notify_cmd) { - case STA_NOTIFY_SLEEP: - if (!prev) { - if (priv->buffered_multicasts && - !priv->sta_asleep_mask) - queue_work(priv->workqueue, - &priv->multicast_start_work); - priv->sta_asleep_mask |= bit; - } - break; - case STA_NOTIFY_AWAKE: - if (prev) { - priv->sta_asleep_mask &= ~bit; - priv->pspoll_mask &= ~bit; - if (priv->tx_multicast && link_id && - !priv->sta_asleep_mask) - queue_work(priv->workqueue, - &priv->multicast_stop_work); - cw1200_bh_wakeup(priv); - } - break; - } -} - -void cw1200_sta_notify(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - enum sta_notify_cmd notify_cmd, - struct ieee80211_sta *sta) -{ - struct cw1200_common *priv = dev->priv; - struct cw1200_sta_priv *sta_priv = - (struct cw1200_sta_priv *)&sta->drv_priv; - - spin_lock_bh(&priv->ps_state_lock); - __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id); - spin_unlock_bh(&priv->ps_state_lock); -} - -static void cw1200_ps_notify(struct cw1200_common *priv, - int link_id, bool ps) -{ - if (link_id > CW1200_MAX_STA_IN_AP_MODE) - return; - - pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n", - ps ? "Stop" : "Start", - link_id, priv->sta_asleep_mask); - - __cw1200_sta_notify(priv->hw, priv->vif, - ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id); -} - -static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) -{ - struct sk_buff *skb; - struct wsm_update_ie update_ie = { - .what = WSM_UPDATE_IE_BEACON, - .count = 1, - }; - u16 tim_offset, tim_length; - - pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis"); - - skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, - &tim_offset, &tim_length); - if (!skb) { - if (!__cw1200_flush(priv, true)) - wsm_unlock_tx(priv); - return -ENOENT; - } - - if (tim_offset && tim_length >= 6) { - /* Ignore DTIM count from mac80211: - * firmware handles DTIM internally. - */ - skb->data[tim_offset + 2] = 0; - - /* Set/reset aid0 bit */ - if (aid0_bit_set) - skb->data[tim_offset + 4] |= 1; - else - skb->data[tim_offset + 4] &= ~1; - } - - update_ie.ies = &skb->data[tim_offset]; - update_ie.length = tim_length; - wsm_update_ie(priv, &update_ie); - - dev_kfree_skb(skb); - - return 0; -} - -void cw1200_set_tim_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, set_tim_work); - (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set); -} - -int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, - bool set) -{ - struct cw1200_common *priv = dev->priv; - queue_work(priv->workqueue, &priv->set_tim_work); - return 0; -} - -void cw1200_set_cts_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, set_cts_work); - - u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; - struct wsm_update_ie update_ie = { - .what = WSM_UPDATE_IE_BEACON, - .count = 1, - .ies = erp_ie, - .length = 3, - }; - u32 erp_info; - __le32 use_cts_prot; - mutex_lock(&priv->conf_mutex); - erp_info = priv->erp_info; - mutex_unlock(&priv->conf_mutex); - use_cts_prot = - erp_info & WLAN_ERP_USE_PROTECTION ? - __cpu_to_le32(1) : 0; - - erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info; - - pr_debug("[STA] ERP information 0x%x\n", erp_info); - - wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, - &use_cts_prot, sizeof(use_cts_prot)); - wsm_update_ie(priv, &update_ie); - - return; -} - -static int cw1200_set_btcoexinfo(struct cw1200_common *priv) -{ - struct wsm_override_internal_txrate arg; - int ret = 0; - - if (priv->mode == NL80211_IFTYPE_STATION) { - /* Plumb PSPOLL and NULL template */ - cw1200_upload_pspoll(priv); - cw1200_upload_null(priv); - cw1200_upload_qosnull(priv); - } else { - return 0; - } - - memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); - - if (!priv->vif->p2p) { - /* STATION mode */ - if (priv->bss_params.operational_rate_set & ~0xF) { - pr_debug("[STA] STA has ERP rates\n"); - /* G or BG mode */ - arg.internalTxRate = (__ffs( - priv->bss_params.operational_rate_set & ~0xF)); - } else { - pr_debug("[STA] STA has non ERP rates\n"); - /* B only mode */ - arg.internalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set))); - } - arg.nonErpInternalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set))); - } else { - /* P2P mode */ - arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); - arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); - } - - pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n", - priv->mode, - arg.internalTxRate, - arg.nonErpInternalTxRate); - - ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, - &arg, sizeof(arg)); - - return ret; -} - -void cw1200_bss_info_changed(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) -{ - struct cw1200_common *priv = dev->priv; - bool do_join = false; - - mutex_lock(&priv->conf_mutex); - - pr_debug("BSS CHANGED: %08x\n", changed); - - /* TODO: BSS_CHANGED_QOS */ - /* TODO: BSS_CHANGED_TXPOWER */ - - if (changed & BSS_CHANGED_ARP_FILTER) { - struct wsm_mib_arp_ipv4_filter filter = {0}; - int i; - - pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n", - info->arp_addr_cnt); - - /* Currently only one IP address is supported by firmware. - * In case of more IPs arp filtering will be disabled. - */ - if (info->arp_addr_cnt > 0 && - info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { - for (i = 0; i < info->arp_addr_cnt; i++) { - filter.ipv4addrs[i] = info->arp_addr_list[i]; - pr_debug("[STA] addr[%d]: 0x%X\n", - i, filter.ipv4addrs[i]); - } - filter.enable = __cpu_to_le32(1); - } - - pr_debug("[STA] arp ip filter enable: %d\n", - __le32_to_cpu(filter.enable)); - - wsm_set_arp_ipv4_filter(priv, &filter); - } - - if (changed & - (BSS_CHANGED_BEACON | - BSS_CHANGED_AP_PROBE_RESP | - BSS_CHANGED_BSSID | - BSS_CHANGED_SSID | - BSS_CHANGED_IBSS)) { - pr_debug("BSS_CHANGED_BEACON\n"); - priv->beacon_int = info->beacon_int; - cw1200_update_beaconing(priv); - cw1200_upload_beacon(priv); - } - - if (changed & BSS_CHANGED_BEACON_ENABLED) { - pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon); - - if (priv->enable_beacon != info->enable_beacon) { - cw1200_enable_beaconing(priv, info->enable_beacon); - priv->enable_beacon = info->enable_beacon; - } - } - - if (changed & BSS_CHANGED_BEACON_INT) { - pr_debug("CHANGED_BEACON_INT\n"); - if (info->ibss_joined) - do_join = true; - else if (priv->join_status == CW1200_JOIN_STATUS_AP) - cw1200_update_beaconing(priv); - } - - /* assoc/disassoc, or maybe AID changed */ - if (changed & BSS_CHANGED_ASSOC) { - wsm_lock_tx(priv); - priv->wep_default_key_id = -1; - wsm_unlock_tx(priv); - } - - if (changed & BSS_CHANGED_BSSID) { - pr_debug("BSS_CHANGED_BSSID\n"); - do_join = true; - } - - if (changed & - (BSS_CHANGED_ASSOC | - BSS_CHANGED_BSSID | - BSS_CHANGED_IBSS | - BSS_CHANGED_BASIC_RATES | - BSS_CHANGED_HT)) { - pr_debug("BSS_CHANGED_ASSOC\n"); - if (info->assoc) { - if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) { - ieee80211_connection_loss(vif); - mutex_unlock(&priv->conf_mutex); - return; - } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) { - priv->join_status = CW1200_JOIN_STATUS_STA; - } - } else { - do_join = true; - } - - if (info->assoc || info->ibss_joined) { - struct ieee80211_sta *sta = NULL; - __le32 htprot = 0; - - if (info->dtim_period) - priv->join_dtim_period = info->dtim_period; - priv->beacon_int = info->beacon_int; - - rcu_read_lock(); - - if (info->bssid && !info->ibss_joined) - sta = ieee80211_find_sta(vif, info->bssid); - if (sta) { - priv->ht_info.ht_cap = sta->ht_cap; - priv->bss_params.operational_rate_set = - cw1200_rate_mask_to_wsm(priv, - sta->supp_rates[priv->channel->band]); - priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef); - priv->ht_info.operation_mode = info->ht_operation_mode; - } else { - memset(&priv->ht_info, 0, - sizeof(priv->ht_info)); - priv->bss_params.operational_rate_set = -1; - } - rcu_read_unlock(); - - /* Non Greenfield stations present */ - if (priv->ht_info.operation_mode & - IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) - htprot |= cpu_to_le32(WSM_NON_GREENFIELD_STA_PRESENT); - - /* Set HT protection method */ - htprot |= cpu_to_le32((priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2); - - /* TODO: - * STBC_param.dual_cts - * STBC_param.LSIG_TXOP_FILL - */ - - wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION, - &htprot, sizeof(htprot)); - - priv->association_mode.greenfield = - cw1200_ht_greenfield(&priv->ht_info); - priv->association_mode.flags = - WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | - WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | - WSM_ASSOCIATION_MODE_USE_HT_MODE | - WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | - WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; - priv->association_mode.preamble = - info->use_short_preamble ? - WSM_JOIN_PREAMBLE_SHORT : - WSM_JOIN_PREAMBLE_LONG; - priv->association_mode.basic_rate_set = __cpu_to_le32( - cw1200_rate_mask_to_wsm(priv, - info->basic_rates)); - priv->association_mode.mpdu_start_spacing = - cw1200_ht_ampdu_density(&priv->ht_info); - - cw1200_cqm_bssloss_sm(priv, 0, 0, 0); - cancel_work_sync(&priv->unjoin_work); - - priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count; - priv->bss_params.aid = info->aid; - - if (priv->join_dtim_period < 1) - priv->join_dtim_period = 1; - - pr_debug("[STA] DTIM %d, interval: %d\n", - priv->join_dtim_period, priv->beacon_int); - pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n", - priv->association_mode.preamble, - priv->association_mode.greenfield, - priv->bss_params.aid, - priv->bss_params.operational_rate_set, - priv->association_mode.basic_rate_set); - wsm_set_association_mode(priv, &priv->association_mode); - - if (!info->ibss_joined) { - wsm_keep_alive_period(priv, 30 /* sec */); - wsm_set_bss_params(priv, &priv->bss_params); - priv->setbssparams_done = true; - cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work); - cw1200_set_pm(priv, &priv->powersave_mode); - } - if (priv->vif->p2p) { - pr_debug("[STA] Setting p2p powersave configuration.\n"); - wsm_set_p2p_ps_modeinfo(priv, - &priv->p2p_ps_modeinfo); - } - if (priv->bt_present) - cw1200_set_btcoexinfo(priv); - } else { - memset(&priv->association_mode, 0, - sizeof(priv->association_mode)); - memset(&priv->bss_params, 0, sizeof(priv->bss_params)); - } - } - - /* ERP Protection */ - if (changed & (BSS_CHANGED_ASSOC | - BSS_CHANGED_ERP_CTS_PROT | - BSS_CHANGED_ERP_PREAMBLE)) { - u32 prev_erp_info = priv->erp_info; - if (info->use_cts_prot) - priv->erp_info |= WLAN_ERP_USE_PROTECTION; - else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) - priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; - - if (info->use_short_preamble) - priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE; - else - priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; - - pr_debug("[STA] ERP Protection: %x\n", priv->erp_info); - - if (prev_erp_info != priv->erp_info) - queue_work(priv->workqueue, &priv->set_cts_work); - } - - /* ERP Slottime */ - if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { - __le32 slot_time = info->use_short_slot ? - __cpu_to_le32(9) : __cpu_to_le32(20); - pr_debug("[STA] Slot time: %d us.\n", - __le32_to_cpu(slot_time)); - wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME, - &slot_time, sizeof(slot_time)); - } - - if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { - struct wsm_rcpi_rssi_threshold threshold = { - .rollingAverageCount = 8, - }; - pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n", - info->cqm_rssi_thold, info->cqm_rssi_hyst); - priv->cqm_rssi_thold = info->cqm_rssi_thold; - priv->cqm_rssi_hyst = info->cqm_rssi_hyst; - - if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { - /* RSSI subscription enabled */ - /* TODO: It's not a correct way of setting threshold. - * Upper and lower must be set equal here and adjusted - * in callback. However current implementation is much - * more relaible and stable. - */ - - /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 - * RSSI = RCPI / 2 - 110 - */ - if (priv->cqm_use_rssi) { - threshold.upperThreshold = - info->cqm_rssi_thold + info->cqm_rssi_hyst; - threshold.lowerThreshold = - info->cqm_rssi_thold; - threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; - } else { - threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2; - threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2; - } - threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE; - } else { - /* There is a bug in FW, see sta.c. We have to enable - * dummy subscription to get correct RSSI values. - */ - threshold.rssiRcpiMode |= - WSM_RCPI_RSSI_THRESHOLD_ENABLE | - WSM_RCPI_RSSI_DONT_USE_UPPER | - WSM_RCPI_RSSI_DONT_USE_LOWER; - if (priv->cqm_use_rssi) - threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; - } - wsm_set_rcpi_rssi_threshold(priv, &threshold); - } - mutex_unlock(&priv->conf_mutex); - - if (do_join) { - wsm_lock_tx(priv); - cw1200_do_join(priv); /* Will unlock it for us */ - } -} - -void cw1200_multicast_start_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, multicast_start_work); - long tmo = priv->join_dtim_period * - (priv->beacon_int + 20) * HZ / 1024; - - cancel_work_sync(&priv->multicast_stop_work); - - if (!priv->aid0_bit_set) { - wsm_lock_tx(priv); - cw1200_set_tim_impl(priv, true); - priv->aid0_bit_set = true; - mod_timer(&priv->mcast_timeout, jiffies + tmo); - wsm_unlock_tx(priv); - } -} - -void cw1200_multicast_stop_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, multicast_stop_work); - - if (priv->aid0_bit_set) { - del_timer_sync(&priv->mcast_timeout); - wsm_lock_tx(priv); - priv->aid0_bit_set = false; - cw1200_set_tim_impl(priv, false); - wsm_unlock_tx(priv); - } -} - -void cw1200_mcast_timeout(unsigned long arg) -{ - struct cw1200_common *priv = - (struct cw1200_common *)arg; - - wiphy_warn(priv->hw->wiphy, - "Multicast delivery timeout.\n"); - spin_lock_bh(&priv->ps_state_lock); - priv->tx_multicast = priv->aid0_bit_set && - priv->buffered_multicasts; - if (priv->tx_multicast) - cw1200_bh_wakeup(priv); - spin_unlock_bh(&priv->ps_state_lock); -} - -int cw1200_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) -{ - /* Aggregation is implemented fully in firmware, - * including block ack negotiation. Do not allow - * mac80211 stack to do anything: it interferes with - * the firmware. - */ - - /* Note that we still need this function stubbed. */ - return -ENOTSUPP; -} - -/* ******************************************************************** */ -/* WSM callback */ -void cw1200_suspend_resume(struct cw1200_common *priv, - struct wsm_suspend_resume *arg) -{ - pr_debug("[AP] %s: %s\n", - arg->stop ? "stop" : "start", - arg->multicast ? "broadcast" : "unicast"); - - if (arg->multicast) { - bool cancel_tmo = false; - spin_lock_bh(&priv->ps_state_lock); - if (arg->stop) { - priv->tx_multicast = false; - } else { - /* Firmware sends this indication every DTIM if there - * is a STA in powersave connected. There is no reason - * to suspend, following wakeup will consume much more - * power than it could be saved. - */ - cw1200_pm_stay_awake(&priv->pm_state, - priv->join_dtim_period * - (priv->beacon_int + 20) * HZ / 1024); - priv->tx_multicast = (priv->aid0_bit_set && - priv->buffered_multicasts); - if (priv->tx_multicast) { - cancel_tmo = true; - cw1200_bh_wakeup(priv); - } - } - spin_unlock_bh(&priv->ps_state_lock); - if (cancel_tmo) - del_timer_sync(&priv->mcast_timeout); - } else { - spin_lock_bh(&priv->ps_state_lock); - cw1200_ps_notify(priv, arg->link_id, arg->stop); - spin_unlock_bh(&priv->ps_state_lock); - if (!arg->stop) - cw1200_bh_wakeup(priv); - } - return; -} - -/* ******************************************************************** */ -/* AP privates */ - -static int cw1200_upload_beacon(struct cw1200_common *priv) -{ - int ret = 0; - struct ieee80211_mgmt *mgmt; - struct wsm_template_frame frame = { - .frame_type = WSM_FRAME_TYPE_BEACON, - }; - - u16 tim_offset; - u16 tim_len; - - if (priv->mode == NL80211_IFTYPE_STATION || - priv->mode == NL80211_IFTYPE_MONITOR || - priv->mode == NL80211_IFTYPE_UNSPECIFIED) - goto done; - - if (priv->vif->p2p) - frame.rate = WSM_TRANSMIT_RATE_6; - - frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, - &tim_offset, &tim_len); - if (!frame.skb) - return -ENOMEM; - - ret = wsm_set_template_frame(priv, &frame); - - if (ret) - goto done; - - /* TODO: Distill probe resp; remove TIM - * and any other beacon-specific IEs - */ - mgmt = (void *)frame.skb->data; - mgmt->frame_control = - __cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_PROBE_RESP); - - frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; - if (priv->vif->p2p) { - ret = wsm_set_probe_responder(priv, true); - } else { - ret = wsm_set_template_frame(priv, &frame); - wsm_set_probe_responder(priv, false); - } - -done: - dev_kfree_skb(frame.skb); - - return ret; -} - -static int cw1200_upload_pspoll(struct cw1200_common *priv) -{ - int ret = 0; - struct wsm_template_frame frame = { - .frame_type = WSM_FRAME_TYPE_PS_POLL, - .rate = 0xFF, - }; - - - frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); - if (!frame.skb) - return -ENOMEM; - - ret = wsm_set_template_frame(priv, &frame); - - dev_kfree_skb(frame.skb); - - return ret; -} - -static int cw1200_upload_null(struct cw1200_common *priv) -{ - int ret = 0; - struct wsm_template_frame frame = { - .frame_type = WSM_FRAME_TYPE_NULL, - .rate = 0xFF, - }; - - frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif); - if (!frame.skb) - return -ENOMEM; - - ret = wsm_set_template_frame(priv, &frame); - - dev_kfree_skb(frame.skb); - - return ret; -} - -static int cw1200_upload_qosnull(struct cw1200_common *priv) -{ - /* TODO: This needs to be implemented - - struct wsm_template_frame frame = { - .frame_type = WSM_FRAME_TYPE_QOS_NULL, - .rate = 0xFF, - }; - - frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif); - if (!frame.skb) - return -ENOMEM; - - ret = wsm_set_template_frame(priv, &frame); - - dev_kfree_skb(frame.skb); - - */ - return 0; -} - -static int cw1200_enable_beaconing(struct cw1200_common *priv, - bool enable) -{ - struct wsm_beacon_transmit transmit = { - .enable_beaconing = enable, - }; - - return wsm_beacon_transmit(priv, &transmit); -} - -static int cw1200_start_ap(struct cw1200_common *priv) -{ - int ret; - struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; - struct wsm_start start = { - .mode = priv->vif->p2p ? - WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, - .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? - WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, - .channel_number = priv->channel->hw_value, - .beacon_interval = conf->beacon_int, - .dtim_period = conf->dtim_period, - .preamble = conf->use_short_preamble ? - WSM_JOIN_PREAMBLE_SHORT : - WSM_JOIN_PREAMBLE_LONG, - .probe_delay = 100, - .basic_rate_set = cw1200_rate_mask_to_wsm(priv, - conf->basic_rates), - }; - struct wsm_operational_mode mode = { - .power_mode = cw1200_power_mode, - .disable_more_flag_usage = true, - }; - - memset(start.ssid, 0, sizeof(start.ssid)); - if (!conf->hidden_ssid) { - start.ssid_len = conf->ssid_len; - memcpy(start.ssid, conf->ssid, start.ssid_len); - } - - priv->beacon_int = conf->beacon_int; - priv->join_dtim_period = conf->dtim_period; - - memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); - - pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n", - start.channel_number, start.band, - start.beacon_interval, start.dtim_period, - start.basic_rate_set, - start.ssid_len, start.ssid); - ret = wsm_start(priv, &start); - if (!ret) - ret = cw1200_upload_keys(priv); - if (!ret && priv->vif->p2p) { - pr_debug("[AP] Setting p2p powersave configuration.\n"); - wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo); - } - if (!ret) { - wsm_set_block_ack_policy(priv, 0, 0); - priv->join_status = CW1200_JOIN_STATUS_AP; - cw1200_update_filtering(priv); - } - wsm_set_operational_mode(priv, &mode); - return ret; -} - -static int cw1200_update_beaconing(struct cw1200_common *priv) -{ - struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; - struct wsm_reset reset = { - .link_id = 0, - .reset_statistics = true, - }; - - if (priv->mode == NL80211_IFTYPE_AP) { - /* TODO: check if changed channel, band */ - if (priv->join_status != CW1200_JOIN_STATUS_AP || - priv->beacon_int != conf->beacon_int) { - pr_debug("ap restarting\n"); - wsm_lock_tx(priv); - if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE) - wsm_reset(priv, &reset); - priv->join_status = CW1200_JOIN_STATUS_PASSIVE; - cw1200_start_ap(priv); - wsm_unlock_tx(priv); - } else - pr_debug("ap started join_status: %d\n", - priv->join_status); - } - return 0; -} diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h deleted file mode 100644 index bebb3379017f..000000000000 --- a/drivers/net/wireless/cw1200/sta.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef STA_H_INCLUDED -#define STA_H_INCLUDED - -/* ******************************************************************** */ -/* mac80211 API */ - -int cw1200_start(struct ieee80211_hw *dev); -void cw1200_stop(struct ieee80211_hw *dev); -int cw1200_add_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif); -void cw1200_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif); -int cw1200_change_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - enum nl80211_iftype new_type, - bool p2p); -int cw1200_config(struct ieee80211_hw *dev, u32 changed); -void cw1200_configure_filter(struct ieee80211_hw *dev, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast); -int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, - u16 queue, const struct ieee80211_tx_queue_params *params); -int cw1200_get_stats(struct ieee80211_hw *dev, - struct ieee80211_low_level_stats *stats); -int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key); - -int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value); - -void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 queues, bool drop); - -u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, - struct netdev_hw_addr_list *mc_list); - -int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); - -/* ******************************************************************** */ -/* WSM callbacks */ - -void cw1200_join_complete_cb(struct cw1200_common *priv, - struct wsm_join_complete *arg); - -/* ******************************************************************** */ -/* WSM events */ - -void cw1200_free_event_queue(struct cw1200_common *priv); -void cw1200_event_handler(struct work_struct *work); -void cw1200_bss_loss_work(struct work_struct *work); -void cw1200_bss_params_work(struct work_struct *work); -void cw1200_keep_alive_work(struct work_struct *work); -void cw1200_tx_failure_work(struct work_struct *work); - -void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good, - int bad); -static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv, - int init, int good, int bad) -{ - spin_lock(&priv->bss_loss_lock); - __cw1200_cqm_bssloss_sm(priv, init, good, bad); - spin_unlock(&priv->bss_loss_lock); -} - -/* ******************************************************************** */ -/* Internal API */ - -int cw1200_setup_mac(struct cw1200_common *priv); -void cw1200_join_timeout(struct work_struct *work); -void cw1200_unjoin_work(struct work_struct *work); -void cw1200_join_complete_work(struct work_struct *work); -void cw1200_wep_key_work(struct work_struct *work); -void cw1200_update_listening(struct cw1200_common *priv, bool enabled); -void cw1200_update_filtering(struct cw1200_common *priv); -void cw1200_update_filtering_work(struct work_struct *work); -void cw1200_set_beacon_wakeup_period_work(struct work_struct *work); -int cw1200_enable_listening(struct cw1200_common *priv); -int cw1200_disable_listening(struct cw1200_common *priv); -int cw1200_set_uapsd_param(struct cw1200_common *priv, - const struct wsm_edca_params *arg); -void cw1200_ba_work(struct work_struct *work); -void cw1200_ba_timer(unsigned long arg); - -/* AP stuffs */ -int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, - bool set); -int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, - enum sta_notify_cmd notify_cmd, - struct ieee80211_sta *sta); -void cw1200_bss_info_changed(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed); -int cw1200_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu); - -void cw1200_suspend_resume(struct cw1200_common *priv, - struct wsm_suspend_resume *arg); -void cw1200_set_tim_work(struct work_struct *work); -void cw1200_set_cts_work(struct work_struct *work); -void cw1200_multicast_start_work(struct work_struct *work); -void cw1200_multicast_stop_work(struct work_struct *work); -void cw1200_mcast_timeout(unsigned long arg); - -#endif diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c deleted file mode 100644 index d28bd49cb5fd..000000000000 --- a/drivers/net/wireless/cw1200/txrx.c +++ /dev/null @@ -1,1472 +0,0 @@ -/* - * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include - -#include "cw1200.h" -#include "wsm.h" -#include "bh.h" -#include "sta.h" -#include "debug.h" - -#define CW1200_INVALID_RATE_ID (0xFF) - -static int cw1200_handle_action_rx(struct cw1200_common *priv, - struct sk_buff *skb); -static const struct ieee80211_rate * -cw1200_get_tx_rate(const struct cw1200_common *priv, - const struct ieee80211_tx_rate *rate); - -/* ******************************************************************** */ -/* TX queue lock / unlock */ - -static inline void cw1200_tx_queues_lock(struct cw1200_common *priv) -{ - int i; - for (i = 0; i < 4; ++i) - cw1200_queue_lock(&priv->tx_queue[i]); -} - -static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv) -{ - int i; - for (i = 0; i < 4; ++i) - cw1200_queue_unlock(&priv->tx_queue[i]); -} - -/* ******************************************************************** */ -/* TX policy cache implementation */ - -static void tx_policy_dump(struct tx_policy *policy) -{ - pr_debug("[TX policy] %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n", - policy->raw[0] & 0x0F, policy->raw[0] >> 4, - policy->raw[1] & 0x0F, policy->raw[1] >> 4, - policy->raw[2] & 0x0F, policy->raw[2] >> 4, - policy->raw[3] & 0x0F, policy->raw[3] >> 4, - policy->raw[4] & 0x0F, policy->raw[4] >> 4, - policy->raw[5] & 0x0F, policy->raw[5] >> 4, - policy->raw[6] & 0x0F, policy->raw[6] >> 4, - policy->raw[7] & 0x0F, policy->raw[7] >> 4, - policy->raw[8] & 0x0F, policy->raw[8] >> 4, - policy->raw[9] & 0x0F, policy->raw[9] >> 4, - policy->raw[10] & 0x0F, policy->raw[10] >> 4, - policy->raw[11] & 0x0F, policy->raw[11] >> 4, - policy->defined); -} - -static void tx_policy_build(const struct cw1200_common *priv, - /* [out] */ struct tx_policy *policy, - struct ieee80211_tx_rate *rates, size_t count) -{ - int i, j; - unsigned limit = priv->short_frame_max_tx_count; - unsigned total = 0; - BUG_ON(rates[0].idx < 0); - memset(policy, 0, sizeof(*policy)); - - /* Sort rates in descending order. */ - for (i = 1; i < count; ++i) { - if (rates[i].idx < 0) { - count = i; - break; - } - if (rates[i].idx > rates[i - 1].idx) { - struct ieee80211_tx_rate tmp = rates[i - 1]; - rates[i - 1] = rates[i]; - rates[i] = tmp; - } - } - - /* Eliminate duplicates. */ - total = rates[0].count; - for (i = 0, j = 1; j < count; ++j) { - if (rates[j].idx == rates[i].idx) { - rates[i].count += rates[j].count; - } else if (rates[j].idx > rates[i].idx) { - break; - } else { - ++i; - if (i != j) - rates[i] = rates[j]; - } - total += rates[j].count; - } - count = i + 1; - - /* Re-fill policy trying to keep every requested rate and with - * respect to the global max tx retransmission count. - */ - if (limit < count) - limit = count; - if (total > limit) { - for (i = 0; i < count; ++i) { - int left = count - i - 1; - if (rates[i].count > limit - left) - rates[i].count = limit - left; - limit -= rates[i].count; - } - } - - /* HACK!!! Device has problems (at least) switching from - * 54Mbps CTS to 1Mbps. This switch takes enormous amount - * of time (100-200 ms), leading to valuable throughput drop. - * As a workaround, additional g-rates are injected to the - * policy. - */ - if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && - rates[0].idx > 4 && rates[0].count > 2 && - rates[1].idx < 2) { - int mid_rate = (rates[0].idx + 4) >> 1; - - /* Decrease number of retries for the initial rate */ - rates[0].count -= 2; - - if (mid_rate != 4) { - /* Keep fallback rate at 1Mbps. */ - rates[3] = rates[1]; - - /* Inject 1 transmission on lowest g-rate */ - rates[2].idx = 4; - rates[2].count = 1; - rates[2].flags = rates[1].flags; - - /* Inject 1 transmission on mid-rate */ - rates[1].idx = mid_rate; - rates[1].count = 1; - - /* Fallback to 1 Mbps is a really bad thing, - * so let's try to increase probability of - * successful transmission on the lowest g rate - * even more - */ - if (rates[0].count >= 3) { - --rates[0].count; - ++rates[2].count; - } - - /* Adjust amount of rates defined */ - count += 2; - } else { - /* Keep fallback rate at 1Mbps. */ - rates[2] = rates[1]; - - /* Inject 2 transmissions on lowest g-rate */ - rates[1].idx = 4; - rates[1].count = 2; - - /* Adjust amount of rates defined */ - count += 1; - } - } - - policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1; - - for (i = 0; i < count; ++i) { - register unsigned rateid, off, shift, retries; - - rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value; - off = rateid >> 3; /* eq. rateid / 8 */ - shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ - - retries = rates[i].count; - if (retries > 0x0F) { - rates[i].count = 0x0f; - retries = 0x0F; - } - policy->tbl[off] |= __cpu_to_le32(retries << shift); - policy->retry_count += retries; - } - - pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d\n", - count, - rates[0].idx, rates[0].count, - rates[1].idx, rates[1].count, - rates[2].idx, rates[2].count, - rates[3].idx, rates[3].count); -} - -static inline bool tx_policy_is_equal(const struct tx_policy *wanted, - const struct tx_policy *cached) -{ - size_t count = wanted->defined >> 1; - if (wanted->defined > cached->defined) - return false; - if (count) { - if (memcmp(wanted->raw, cached->raw, count)) - return false; - } - if (wanted->defined & 1) { - if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F)) - return false; - } - return true; -} - -static int tx_policy_find(struct tx_policy_cache *cache, - const struct tx_policy *wanted) -{ - /* O(n) complexity. Not so good, but there's only 8 entries in - * the cache. - * Also lru helps to reduce search time. - */ - struct tx_policy_cache_entry *it; - /* First search for policy in "used" list */ - list_for_each_entry(it, &cache->used, link) { - if (tx_policy_is_equal(wanted, &it->policy)) - return it - cache->cache; - } - /* Then - in "free list" */ - list_for_each_entry(it, &cache->free, link) { - if (tx_policy_is_equal(wanted, &it->policy)) - return it - cache->cache; - } - return -1; -} - -static inline void tx_policy_use(struct tx_policy_cache *cache, - struct tx_policy_cache_entry *entry) -{ - ++entry->policy.usage_count; - list_move(&entry->link, &cache->used); -} - -static inline int tx_policy_release(struct tx_policy_cache *cache, - struct tx_policy_cache_entry *entry) -{ - int ret = --entry->policy.usage_count; - if (!ret) - list_move(&entry->link, &cache->free); - return ret; -} - -void tx_policy_clean(struct cw1200_common *priv) -{ - int idx, locked; - struct tx_policy_cache *cache = &priv->tx_policy_cache; - struct tx_policy_cache_entry *entry; - - cw1200_tx_queues_lock(priv); - spin_lock_bh(&cache->lock); - locked = list_empty(&cache->free); - - for (idx = 0; idx < TX_POLICY_CACHE_SIZE; idx++) { - entry = &cache->cache[idx]; - /* Policy usage count should be 0 at this time as all queues - should be empty - */ - if (WARN_ON(entry->policy.usage_count)) { - entry->policy.usage_count = 0; - list_move(&entry->link, &cache->free); - } - memset(&entry->policy, 0, sizeof(entry->policy)); - } - if (locked) - cw1200_tx_queues_unlock(priv); - - cw1200_tx_queues_unlock(priv); - spin_unlock_bh(&cache->lock); -} - -/* ******************************************************************** */ -/* External TX policy cache API */ - -void tx_policy_init(struct cw1200_common *priv) -{ - struct tx_policy_cache *cache = &priv->tx_policy_cache; - int i; - - memset(cache, 0, sizeof(*cache)); - - spin_lock_init(&cache->lock); - INIT_LIST_HEAD(&cache->used); - INIT_LIST_HEAD(&cache->free); - - for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) - list_add(&cache->cache[i].link, &cache->free); -} - -static int tx_policy_get(struct cw1200_common *priv, - struct ieee80211_tx_rate *rates, - size_t count, bool *renew) -{ - int idx; - struct tx_policy_cache *cache = &priv->tx_policy_cache; - struct tx_policy wanted; - - tx_policy_build(priv, &wanted, rates, count); - - spin_lock_bh(&cache->lock); - if (WARN_ON_ONCE(list_empty(&cache->free))) { - spin_unlock_bh(&cache->lock); - return CW1200_INVALID_RATE_ID; - } - idx = tx_policy_find(cache, &wanted); - if (idx >= 0) { - pr_debug("[TX policy] Used TX policy: %d\n", idx); - *renew = false; - } else { - struct tx_policy_cache_entry *entry; - *renew = true; - /* If policy is not found create a new one - * using the oldest entry in "free" list - */ - entry = list_entry(cache->free.prev, - struct tx_policy_cache_entry, link); - entry->policy = wanted; - idx = entry - cache->cache; - pr_debug("[TX policy] New TX policy: %d\n", idx); - tx_policy_dump(&entry->policy); - } - tx_policy_use(cache, &cache->cache[idx]); - if (list_empty(&cache->free)) { - /* Lock TX queues. */ - cw1200_tx_queues_lock(priv); - } - spin_unlock_bh(&cache->lock); - return idx; -} - -static void tx_policy_put(struct cw1200_common *priv, int idx) -{ - int usage, locked; - struct tx_policy_cache *cache = &priv->tx_policy_cache; - - spin_lock_bh(&cache->lock); - locked = list_empty(&cache->free); - usage = tx_policy_release(cache, &cache->cache[idx]); - if (locked && !usage) { - /* Unlock TX queues. */ - cw1200_tx_queues_unlock(priv); - } - spin_unlock_bh(&cache->lock); -} - -static int tx_policy_upload(struct cw1200_common *priv) -{ - struct tx_policy_cache *cache = &priv->tx_policy_cache; - int i; - struct wsm_set_tx_rate_retry_policy arg = { - .num = 0, - }; - spin_lock_bh(&cache->lock); - - /* Upload only modified entries. */ - for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) { - struct tx_policy *src = &cache->cache[i].policy; - if (src->retry_count && !src->uploaded) { - struct wsm_tx_rate_retry_policy *dst = - &arg.tbl[arg.num]; - dst->index = i; - dst->short_retries = priv->short_frame_max_tx_count; - dst->long_retries = priv->long_frame_max_tx_count; - - dst->flags = WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED | - WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT; - memcpy(dst->rate_count_indices, src->tbl, - sizeof(dst->rate_count_indices)); - src->uploaded = 1; - ++arg.num; - } - } - spin_unlock_bh(&cache->lock); - cw1200_debug_tx_cache_miss(priv); - pr_debug("[TX policy] Upload %d policies\n", arg.num); - return wsm_set_tx_rate_retry_policy(priv, &arg); -} - -void tx_policy_upload_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, tx_policy_upload_work); - - pr_debug("[TX] TX policy upload.\n"); - tx_policy_upload(priv); - - wsm_unlock_tx(priv); - cw1200_tx_queues_unlock(priv); -} - -/* ******************************************************************** */ -/* cw1200 TX implementation */ - -struct cw1200_txinfo { - struct sk_buff *skb; - unsigned queue; - struct ieee80211_tx_info *tx_info; - const struct ieee80211_rate *rate; - struct ieee80211_hdr *hdr; - size_t hdrlen; - const u8 *da; - struct cw1200_sta_priv *sta_priv; - struct ieee80211_sta *sta; - struct cw1200_txpriv txpriv; -}; - -u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates) -{ - u32 ret = 0; - int i; - for (i = 0; i < 32; ++i) { - if (rates & BIT(i)) - ret |= BIT(priv->rates[i].hw_value); - } - return ret; -} - -static const struct ieee80211_rate * -cw1200_get_tx_rate(const struct cw1200_common *priv, - const struct ieee80211_tx_rate *rate) -{ - if (rate->idx < 0) - return NULL; - if (rate->flags & IEEE80211_TX_RC_MCS) - return &priv->mcs_rates[rate->idx]; - return &priv->hw->wiphy->bands[priv->channel->band]-> - bitrates[rate->idx]; -} - -static int -cw1200_tx_h_calc_link_ids(struct cw1200_common *priv, - struct cw1200_txinfo *t) -{ - if (t->sta && t->sta_priv->link_id) - t->txpriv.raw_link_id = - t->txpriv.link_id = - t->sta_priv->link_id; - else if (priv->mode != NL80211_IFTYPE_AP) - t->txpriv.raw_link_id = - t->txpriv.link_id = 0; - else if (is_multicast_ether_addr(t->da)) { - if (priv->enable_beacon) { - t->txpriv.raw_link_id = 0; - t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM; - } else { - t->txpriv.raw_link_id = 0; - t->txpriv.link_id = 0; - } - } else { - t->txpriv.link_id = cw1200_find_link_id(priv, t->da); - if (!t->txpriv.link_id) - t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da); - if (!t->txpriv.link_id) { - wiphy_err(priv->hw->wiphy, - "No more link IDs available.\n"); - return -ENOENT; - } - t->txpriv.raw_link_id = t->txpriv.link_id; - } - if (t->txpriv.raw_link_id) - priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp = - jiffies; - if (t->sta && (t->sta->uapsd_queues & BIT(t->queue))) - t->txpriv.link_id = CW1200_LINK_ID_UAPSD; - return 0; -} - -static void -cw1200_tx_h_pm(struct cw1200_common *priv, - struct cw1200_txinfo *t) -{ - if (ieee80211_is_auth(t->hdr->frame_control)) { - u32 mask = ~BIT(t->txpriv.raw_link_id); - spin_lock_bh(&priv->ps_state_lock); - priv->sta_asleep_mask &= mask; - priv->pspoll_mask &= mask; - spin_unlock_bh(&priv->ps_state_lock); - } -} - -static void -cw1200_tx_h_calc_tid(struct cw1200_common *priv, - struct cw1200_txinfo *t) -{ - if (ieee80211_is_data_qos(t->hdr->frame_control)) { - u8 *qos = ieee80211_get_qos_ctl(t->hdr); - t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; - } else if (ieee80211_is_data(t->hdr->frame_control)) { - t->txpriv.tid = 0; - } -} - -static int -cw1200_tx_h_crypt(struct cw1200_common *priv, - struct cw1200_txinfo *t) -{ - if (!t->tx_info->control.hw_key || - !ieee80211_has_protected(t->hdr->frame_control)) - return 0; - - t->hdrlen += t->tx_info->control.hw_key->iv_len; - skb_put(t->skb, t->tx_info->control.hw_key->icv_len); - - if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) - skb_put(t->skb, 8); /* MIC space */ - - return 0; -} - -static int -cw1200_tx_h_align(struct cw1200_common *priv, - struct cw1200_txinfo *t, - u8 *flags) -{ - size_t offset = (size_t)t->skb->data & 3; - - if (!offset) - return 0; - - if (offset & 1) { - wiphy_err(priv->hw->wiphy, - "Bug: attempt to transmit a frame with wrong alignment: %zu\n", - offset); - return -EINVAL; - } - - if (skb_headroom(t->skb) < offset) { - wiphy_err(priv->hw->wiphy, - "Bug: no space allocated for DMA alignment. headroom: %d\n", - skb_headroom(t->skb)); - return -ENOMEM; - } - skb_push(t->skb, offset); - t->hdrlen += offset; - t->txpriv.offset += offset; - *flags |= WSM_TX_2BYTES_SHIFT; - cw1200_debug_tx_align(priv); - return 0; -} - -static int -cw1200_tx_h_action(struct cw1200_common *priv, - struct cw1200_txinfo *t) -{ - struct ieee80211_mgmt *mgmt = - (struct ieee80211_mgmt *)t->hdr; - if (ieee80211_is_action(t->hdr->frame_control) && - mgmt->u.action.category == WLAN_CATEGORY_BACK) - return 1; - else - return 0; -} - -/* Add WSM header */ -static struct wsm_tx * -cw1200_tx_h_wsm(struct cw1200_common *priv, - struct cw1200_txinfo *t) -{ - struct wsm_tx *wsm; - - if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) { - wiphy_err(priv->hw->wiphy, - "Bug: no space allocated for WSM header. headroom: %d\n", - skb_headroom(t->skb)); - return NULL; - } - - wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx)); - t->txpriv.offset += sizeof(struct wsm_tx); - memset(wsm, 0, sizeof(*wsm)); - wsm->hdr.len = __cpu_to_le16(t->skb->len); - wsm->hdr.id = __cpu_to_le16(0x0004); - wsm->queue_id = wsm_queue_id_to_wsm(t->queue); - return wsm; -} - -/* BT Coex specific handling */ -static void -cw1200_tx_h_bt(struct cw1200_common *priv, - struct cw1200_txinfo *t, - struct wsm_tx *wsm) -{ - u8 priority = 0; - - if (!priv->bt_present) - return; - - if (ieee80211_is_nullfunc(t->hdr->frame_control)) { - priority = WSM_EPTA_PRIORITY_MGT; - } else if (ieee80211_is_data(t->hdr->frame_control)) { - /* Skip LLC SNAP header (+6) */ - u8 *payload = &t->skb->data[t->hdrlen]; - __be16 *ethertype = (__be16 *)&payload[6]; - if (be16_to_cpu(*ethertype) == ETH_P_PAE) - priority = WSM_EPTA_PRIORITY_EAPOL; - } else if (ieee80211_is_assoc_req(t->hdr->frame_control) || - ieee80211_is_reassoc_req(t->hdr->frame_control)) { - struct ieee80211_mgmt *mgt_frame = - (struct ieee80211_mgmt *)t->hdr; - - if (le16_to_cpu(mgt_frame->u.assoc_req.listen_interval) < - priv->listen_interval) { - pr_debug("Modified Listen Interval to %d from %d\n", - priv->listen_interval, - mgt_frame->u.assoc_req.listen_interval); - /* Replace listen interval derieved from - * the one read from SDD - */ - mgt_frame->u.assoc_req.listen_interval = cpu_to_le16(priv->listen_interval); - } - } - - if (!priority) { - if (ieee80211_is_action(t->hdr->frame_control)) - priority = WSM_EPTA_PRIORITY_ACTION; - else if (ieee80211_is_mgmt(t->hdr->frame_control)) - priority = WSM_EPTA_PRIORITY_MGT; - else if ((wsm->queue_id == WSM_QUEUE_VOICE)) - priority = WSM_EPTA_PRIORITY_VOICE; - else if ((wsm->queue_id == WSM_QUEUE_VIDEO)) - priority = WSM_EPTA_PRIORITY_VIDEO; - else - priority = WSM_EPTA_PRIORITY_DATA; - } - - pr_debug("[TX] EPTA priority %d.\n", priority); - - wsm->flags |= priority << 1; -} - -static int -cw1200_tx_h_rate_policy(struct cw1200_common *priv, - struct cw1200_txinfo *t, - struct wsm_tx *wsm) -{ - bool tx_policy_renew = false; - - t->txpriv.rate_id = tx_policy_get(priv, - t->tx_info->control.rates, IEEE80211_TX_MAX_RATES, - &tx_policy_renew); - if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID) - return -EFAULT; - - wsm->flags |= t->txpriv.rate_id << 4; - - t->rate = cw1200_get_tx_rate(priv, - &t->tx_info->control.rates[0]), - wsm->max_tx_rate = t->rate->hw_value; - if (t->rate->flags & IEEE80211_TX_RC_MCS) { - if (cw1200_ht_greenfield(&priv->ht_info)) - wsm->ht_tx_parameters |= - __cpu_to_le32(WSM_HT_TX_GREENFIELD); - else - wsm->ht_tx_parameters |= - __cpu_to_le32(WSM_HT_TX_MIXED); - } - - if (tx_policy_renew) { - pr_debug("[TX] TX policy renew.\n"); - /* It's not so optimal to stop TX queues every now and then. - * Better to reimplement task scheduling with - * a counter. TODO. - */ - wsm_lock_tx_async(priv); - cw1200_tx_queues_lock(priv); - if (queue_work(priv->workqueue, - &priv->tx_policy_upload_work) <= 0) { - cw1200_tx_queues_unlock(priv); - wsm_unlock_tx(priv); - } - } - return 0; -} - -static bool -cw1200_tx_h_pm_state(struct cw1200_common *priv, - struct cw1200_txinfo *t) -{ - int was_buffered = 1; - - if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM && - !priv->buffered_multicasts) { - priv->buffered_multicasts = true; - if (priv->sta_asleep_mask) - queue_work(priv->workqueue, - &priv->multicast_start_work); - } - - if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID) - was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1].buffered[t->txpriv.tid]++; - - return !was_buffered; -} - -/* ******************************************************************** */ - -void cw1200_tx(struct ieee80211_hw *dev, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct cw1200_common *priv = dev->priv; - struct cw1200_txinfo t = { - .skb = skb, - .queue = skb_get_queue_mapping(skb), - .tx_info = IEEE80211_SKB_CB(skb), - .hdr = (struct ieee80211_hdr *)skb->data, - .txpriv.tid = CW1200_MAX_TID, - .txpriv.rate_id = CW1200_INVALID_RATE_ID, - }; - struct ieee80211_sta *sta; - struct wsm_tx *wsm; - bool tid_update = 0; - u8 flags = 0; - int ret; - - if (priv->bh_error) - goto drop; - - t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control); - t.da = ieee80211_get_DA(t.hdr); - if (control) { - t.sta = control->sta; - t.sta_priv = (struct cw1200_sta_priv *)&t.sta->drv_priv; - } - - if (WARN_ON(t.queue >= 4)) - goto drop; - - ret = cw1200_tx_h_calc_link_ids(priv, &t); - if (ret) - goto drop; - - pr_debug("[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n", - skb->len, t.queue, t.txpriv.link_id, - t.txpriv.raw_link_id); - - cw1200_tx_h_pm(priv, &t); - cw1200_tx_h_calc_tid(priv, &t); - ret = cw1200_tx_h_crypt(priv, &t); - if (ret) - goto drop; - ret = cw1200_tx_h_align(priv, &t, &flags); - if (ret) - goto drop; - ret = cw1200_tx_h_action(priv, &t); - if (ret) - goto drop; - wsm = cw1200_tx_h_wsm(priv, &t); - if (!wsm) { - ret = -ENOMEM; - goto drop; - } - wsm->flags |= flags; - cw1200_tx_h_bt(priv, &t, wsm); - ret = cw1200_tx_h_rate_policy(priv, &t, wsm); - if (ret) - goto drop; - - rcu_read_lock(); - sta = rcu_dereference(t.sta); - - spin_lock_bh(&priv->ps_state_lock); - { - tid_update = cw1200_tx_h_pm_state(priv, &t); - BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue], - t.skb, &t.txpriv)); - } - spin_unlock_bh(&priv->ps_state_lock); - - if (tid_update && sta) - ieee80211_sta_set_buffered(sta, t.txpriv.tid, true); - - rcu_read_unlock(); - - cw1200_bh_wakeup(priv); - - return; - -drop: - cw1200_skb_dtor(priv, skb, &t.txpriv); - return; -} - -/* ******************************************************************** */ - -static int cw1200_handle_action_rx(struct cw1200_common *priv, - struct sk_buff *skb) -{ - struct ieee80211_mgmt *mgmt = (void *)skb->data; - - /* Filter block ACK negotiation: fully controlled by firmware */ - if (mgmt->u.action.category == WLAN_CATEGORY_BACK) - return 1; - - return 0; -} - -static int cw1200_handle_pspoll(struct cw1200_common *priv, - struct sk_buff *skb) -{ - struct ieee80211_sta *sta; - struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data; - int link_id = 0; - u32 pspoll_mask = 0; - int drop = 1; - int i; - - if (priv->join_status != CW1200_JOIN_STATUS_AP) - goto done; - if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN)) - goto done; - - rcu_read_lock(); - sta = ieee80211_find_sta(priv->vif, pspoll->ta); - if (sta) { - struct cw1200_sta_priv *sta_priv; - sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; - link_id = sta_priv->link_id; - pspoll_mask = BIT(sta_priv->link_id); - } - rcu_read_unlock(); - if (!link_id) - goto done; - - priv->pspoll_mask |= pspoll_mask; - drop = 0; - - /* Do not report pspols if data for given link id is queued already. */ - for (i = 0; i < 4; ++i) { - if (cw1200_queue_get_num_queued(&priv->tx_queue[i], - pspoll_mask)) { - cw1200_bh_wakeup(priv); - drop = 1; - break; - } - } - pr_debug("[RX] PSPOLL: %s\n", drop ? "local" : "fwd"); -done: - return drop; -} - -/* ******************************************************************** */ - -void cw1200_tx_confirm_cb(struct cw1200_common *priv, - int link_id, - struct wsm_tx_confirm *arg) -{ - u8 queue_id = cw1200_queue_get_queue_id(arg->packet_id); - struct cw1200_queue *queue = &priv->tx_queue[queue_id]; - struct sk_buff *skb; - const struct cw1200_txpriv *txpriv; - - pr_debug("[TX] TX confirm: %d, %d.\n", - arg->status, arg->ack_failures); - - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { - /* STA is stopped. */ - return; - } - - if (WARN_ON(queue_id >= 4)) - return; - - if (arg->status) - pr_debug("TX failed: %d.\n", arg->status); - - if ((arg->status == WSM_REQUEUE) && - (arg->flags & WSM_TX_STATUS_REQUEUE)) { - /* "Requeue" means "implicit suspend" */ - struct wsm_suspend_resume suspend = { - .link_id = link_id, - .stop = 1, - .multicast = !link_id, - }; - cw1200_suspend_resume(priv, &suspend); - wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d). STAs asleep: 0x%.8X\n", - link_id, - cw1200_queue_get_generation(arg->packet_id) + 1, - priv->sta_asleep_mask); - cw1200_queue_requeue(queue, arg->packet_id); - spin_lock_bh(&priv->ps_state_lock); - if (!link_id) { - priv->buffered_multicasts = true; - if (priv->sta_asleep_mask) { - queue_work(priv->workqueue, - &priv->multicast_start_work); - } - } - spin_unlock_bh(&priv->ps_state_lock); - } else if (!cw1200_queue_get_skb(queue, arg->packet_id, - &skb, &txpriv)) { - struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); - int tx_count = arg->ack_failures; - u8 ht_flags = 0; - int i; - - if (cw1200_ht_greenfield(&priv->ht_info)) - ht_flags |= IEEE80211_TX_RC_GREEN_FIELD; - - spin_lock(&priv->bss_loss_lock); - if (priv->bss_loss_state && - arg->packet_id == priv->bss_loss_confirm_id) { - if (arg->status) { - /* Recovery failed */ - __cw1200_cqm_bssloss_sm(priv, 0, 0, 1); - } else { - /* Recovery succeeded */ - __cw1200_cqm_bssloss_sm(priv, 0, 1, 0); - } - } - spin_unlock(&priv->bss_loss_lock); - - if (!arg->status) { - tx->flags |= IEEE80211_TX_STAT_ACK; - ++tx_count; - cw1200_debug_txed(priv); - if (arg->flags & WSM_TX_STATUS_AGGREGATION) { - /* Do not report aggregation to mac80211: - * it confuses minstrel a lot. - */ - /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ - cw1200_debug_txed_agg(priv); - } - } else { - if (tx_count) - ++tx_count; - } - - for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { - if (tx->status.rates[i].count >= tx_count) { - tx->status.rates[i].count = tx_count; - break; - } - tx_count -= tx->status.rates[i].count; - if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS) - tx->status.rates[i].flags |= ht_flags; - } - - for (++i; i < IEEE80211_TX_MAX_RATES; ++i) { - tx->status.rates[i].count = 0; - tx->status.rates[i].idx = -1; - } - - /* Pull off any crypto trailers that we added on */ - if (tx->control.hw_key) { - skb_trim(skb, skb->len - tx->control.hw_key->icv_len); - if (tx->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) - skb_trim(skb, skb->len - 8); /* MIC space */ - } - cw1200_queue_remove(queue, arg->packet_id); - } - /* XXX TODO: Only wake if there are pending transmits.. */ - cw1200_bh_wakeup(priv); -} - -static void cw1200_notify_buffered_tx(struct cw1200_common *priv, - struct sk_buff *skb, int link_id, int tid) -{ - struct ieee80211_sta *sta; - struct ieee80211_hdr *hdr; - u8 *buffered; - u8 still_buffered = 0; - - if (link_id && tid < CW1200_MAX_TID) { - buffered = priv->link_id_db - [link_id - 1].buffered; - - spin_lock_bh(&priv->ps_state_lock); - if (!WARN_ON(!buffered[tid])) - still_buffered = --buffered[tid]; - spin_unlock_bh(&priv->ps_state_lock); - - if (!still_buffered && tid < CW1200_MAX_TID) { - hdr = (struct ieee80211_hdr *)skb->data; - rcu_read_lock(); - sta = ieee80211_find_sta(priv->vif, hdr->addr1); - if (sta) - ieee80211_sta_set_buffered(sta, tid, false); - rcu_read_unlock(); - } - } -} - -void cw1200_skb_dtor(struct cw1200_common *priv, - struct sk_buff *skb, - const struct cw1200_txpriv *txpriv) -{ - skb_pull(skb, txpriv->offset); - if (txpriv->rate_id != CW1200_INVALID_RATE_ID) { - cw1200_notify_buffered_tx(priv, skb, - txpriv->raw_link_id, txpriv->tid); - tx_policy_put(priv, txpriv->rate_id); - } - ieee80211_tx_status(priv->hw, skb); -} - -void cw1200_rx_cb(struct cw1200_common *priv, - struct wsm_rx *arg, - int link_id, - struct sk_buff **skb_p) -{ - struct sk_buff *skb = *skb_p; - struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); - struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; - struct cw1200_link_entry *entry = NULL; - unsigned long grace_period; - - bool early_data = false; - bool p2p = priv->vif && priv->vif->p2p; - size_t hdrlen; - hdr->flag = 0; - - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { - /* STA is stopped. */ - goto drop; - } - - if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) { - entry = &priv->link_id_db[link_id - 1]; - if (entry->status == CW1200_LINK_SOFT && - ieee80211_is_data(frame->frame_control)) - early_data = true; - entry->timestamp = jiffies; - } else if (p2p && - ieee80211_is_action(frame->frame_control) && - (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { - pr_debug("[RX] Going to MAP&RESET link ID\n"); - WARN_ON(work_pending(&priv->linkid_reset_work)); - memcpy(&priv->action_frame_sa[0], - ieee80211_get_SA(frame), ETH_ALEN); - priv->action_linkid = 0; - schedule_work(&priv->linkid_reset_work); - } - - if (link_id && p2p && - ieee80211_is_action(frame->frame_control) && - (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { - /* Link ID already exists for the ACTION frame. - * Reset and Remap - */ - WARN_ON(work_pending(&priv->linkid_reset_work)); - memcpy(&priv->action_frame_sa[0], - ieee80211_get_SA(frame), ETH_ALEN); - priv->action_linkid = link_id; - schedule_work(&priv->linkid_reset_work); - } - if (arg->status) { - if (arg->status == WSM_STATUS_MICFAILURE) { - pr_debug("[RX] MIC failure.\n"); - hdr->flag |= RX_FLAG_MMIC_ERROR; - } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { - pr_debug("[RX] No key found.\n"); - goto drop; - } else { - pr_debug("[RX] Receive failure: %d.\n", - arg->status); - goto drop; - } - } - - if (skb->len < sizeof(struct ieee80211_pspoll)) { - wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. Size is lesser than IEEE header.\n"); - goto drop; - } - - if (ieee80211_is_pspoll(frame->frame_control)) - if (cw1200_handle_pspoll(priv, skb)) - goto drop; - - hdr->band = ((arg->channel_number & 0xff00) || - (arg->channel_number > 14)) ? - IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; - hdr->freq = ieee80211_channel_to_frequency( - arg->channel_number, - hdr->band); - - if (arg->rx_rate >= 14) { - hdr->flag |= RX_FLAG_HT; - hdr->rate_idx = arg->rx_rate - 14; - } else if (arg->rx_rate >= 4) { - hdr->rate_idx = arg->rx_rate - 2; - } else { - hdr->rate_idx = arg->rx_rate; - } - - hdr->signal = (s8)arg->rcpi_rssi; - hdr->antenna = 0; - - hdrlen = ieee80211_hdrlen(frame->frame_control); - - if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { - size_t iv_len = 0, icv_len = 0; - - hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; - - /* Oops... There is no fast way to ask mac80211 about - * IV/ICV lengths. Even defineas are not exposed. - */ - switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { - case WSM_RX_STATUS_WEP: - iv_len = 4 /* WEP_IV_LEN */; - icv_len = 4 /* WEP_ICV_LEN */; - break; - case WSM_RX_STATUS_TKIP: - iv_len = 8 /* TKIP_IV_LEN */; - icv_len = 4 /* TKIP_ICV_LEN */ - + 8 /*MICHAEL_MIC_LEN*/; - hdr->flag |= RX_FLAG_MMIC_STRIPPED; - break; - case WSM_RX_STATUS_AES: - iv_len = 8 /* CCMP_HDR_LEN */; - icv_len = 8 /* CCMP_MIC_LEN */; - break; - case WSM_RX_STATUS_WAPI: - iv_len = 18 /* WAPI_HDR_LEN */; - icv_len = 16 /* WAPI_MIC_LEN */; - break; - default: - pr_warn("Unknown encryption type %d\n", - WSM_RX_STATUS_ENCRYPTION(arg->flags)); - goto drop; - } - - /* Firmware strips ICV in case of MIC failure. */ - if (arg->status == WSM_STATUS_MICFAILURE) - icv_len = 0; - - if (skb->len < hdrlen + iv_len + icv_len) { - wiphy_warn(priv->hw->wiphy, "Malformed SDU rx'ed. Size is lesser than crypto headers.\n"); - goto drop; - } - - /* Remove IV, ICV and MIC */ - skb_trim(skb, skb->len - icv_len); - memmove(skb->data + iv_len, skb->data, hdrlen); - skb_pull(skb, iv_len); - } - - /* Remove TSF from the end of frame */ - if (arg->flags & WSM_RX_STATUS_TSF_INCLUDED) { - memcpy(&hdr->mactime, skb->data + skb->len - 8, 8); - hdr->mactime = le64_to_cpu(hdr->mactime); - if (skb->len >= 8) - skb_trim(skb, skb->len - 8); - } else { - hdr->mactime = 0; - } - - cw1200_debug_rxed(priv); - if (arg->flags & WSM_RX_STATUS_AGGREGATE) - cw1200_debug_rxed_agg(priv); - - if (ieee80211_is_action(frame->frame_control) && - (arg->flags & WSM_RX_STATUS_ADDRESS1)) { - if (cw1200_handle_action_rx(priv, skb)) - return; - } else if (ieee80211_is_beacon(frame->frame_control) && - !arg->status && priv->vif && - ether_addr_equal(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid)) { - const u8 *tim_ie; - u8 *ies = ((struct ieee80211_mgmt *) - (skb->data))->u.beacon.variable; - size_t ies_len = skb->len - (ies - (u8 *)(skb->data)); - - tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len); - if (tim_ie) { - struct ieee80211_tim_ie *tim = - (struct ieee80211_tim_ie *)&tim_ie[2]; - - if (priv->join_dtim_period != tim->dtim_period) { - priv->join_dtim_period = tim->dtim_period; - queue_work(priv->workqueue, - &priv->set_beacon_wakeup_period_work); - } - } - - /* Disable beacon filter once we're associated... */ - if (priv->disable_beacon_filter && - (priv->vif->bss_conf.assoc || - priv->vif->bss_conf.ibss_joined)) { - priv->disable_beacon_filter = false; - queue_work(priv->workqueue, - &priv->update_filtering_work); - } - } - - /* Stay awake after frame is received to give - * userspace chance to react and acquire appropriate - * wakelock. - */ - if (ieee80211_is_auth(frame->frame_control)) - grace_period = 5 * HZ; - else if (ieee80211_is_deauth(frame->frame_control)) - grace_period = 5 * HZ; - else - grace_period = 1 * HZ; - cw1200_pm_stay_awake(&priv->pm_state, grace_period); - - if (early_data) { - spin_lock_bh(&priv->ps_state_lock); - /* Double-check status with lock held */ - if (entry->status == CW1200_LINK_SOFT) - skb_queue_tail(&entry->rx_queue, skb); - else - ieee80211_rx_irqsafe(priv->hw, skb); - spin_unlock_bh(&priv->ps_state_lock); - } else { - ieee80211_rx_irqsafe(priv->hw, skb); - } - *skb_p = NULL; - - return; - -drop: - /* TODO: update failure counters */ - return; -} - -/* ******************************************************************** */ -/* Security */ - -int cw1200_alloc_key(struct cw1200_common *priv) -{ - int idx; - - idx = ffs(~priv->key_map) - 1; - if (idx < 0 || idx > WSM_KEY_MAX_INDEX) - return -1; - - priv->key_map |= BIT(idx); - priv->keys[idx].index = idx; - return idx; -} - -void cw1200_free_key(struct cw1200_common *priv, int idx) -{ - BUG_ON(!(priv->key_map & BIT(idx))); - memset(&priv->keys[idx], 0, sizeof(priv->keys[idx])); - priv->key_map &= ~BIT(idx); -} - -void cw1200_free_keys(struct cw1200_common *priv) -{ - memset(&priv->keys, 0, sizeof(priv->keys)); - priv->key_map = 0; -} - -int cw1200_upload_keys(struct cw1200_common *priv) -{ - int idx, ret = 0; - for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx) - if (priv->key_map & BIT(idx)) { - ret = wsm_add_key(priv, &priv->keys[idx]); - if (ret < 0) - break; - } - return ret; -} - -/* Workaround for WFD test case 6.1.10 */ -void cw1200_link_id_reset(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, linkid_reset_work); - int temp_linkid; - - if (!priv->action_linkid) { - /* In GO mode we can receive ACTION frames without a linkID */ - temp_linkid = cw1200_alloc_link_id(priv, - &priv->action_frame_sa[0]); - WARN_ON(!temp_linkid); - if (temp_linkid) { - /* Make sure we execute the WQ */ - flush_workqueue(priv->workqueue); - /* Release the link ID */ - spin_lock_bh(&priv->ps_state_lock); - priv->link_id_db[temp_linkid - 1].prev_status = - priv->link_id_db[temp_linkid - 1].status; - priv->link_id_db[temp_linkid - 1].status = - CW1200_LINK_RESET; - spin_unlock_bh(&priv->ps_state_lock); - wsm_lock_tx_async(priv); - if (queue_work(priv->workqueue, - &priv->link_id_work) <= 0) - wsm_unlock_tx(priv); - } - } else { - spin_lock_bh(&priv->ps_state_lock); - priv->link_id_db[priv->action_linkid - 1].prev_status = - priv->link_id_db[priv->action_linkid - 1].status; - priv->link_id_db[priv->action_linkid - 1].status = - CW1200_LINK_RESET_REMAP; - spin_unlock_bh(&priv->ps_state_lock); - wsm_lock_tx_async(priv); - if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) - wsm_unlock_tx(priv); - flush_workqueue(priv->workqueue); - } -} - -int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac) -{ - int i, ret = 0; - spin_lock_bh(&priv->ps_state_lock); - for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { - if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && - priv->link_id_db[i].status) { - priv->link_id_db[i].timestamp = jiffies; - ret = i + 1; - break; - } - } - spin_unlock_bh(&priv->ps_state_lock); - return ret; -} - -int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) -{ - int i, ret = 0; - unsigned long max_inactivity = 0; - unsigned long now = jiffies; - - spin_lock_bh(&priv->ps_state_lock); - for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { - if (!priv->link_id_db[i].status) { - ret = i + 1; - break; - } else if (priv->link_id_db[i].status != CW1200_LINK_HARD && - !priv->tx_queue_stats.link_map_cache[i + 1]) { - unsigned long inactivity = - now - priv->link_id_db[i].timestamp; - if (inactivity < max_inactivity) - continue; - max_inactivity = inactivity; - ret = i + 1; - } - } - if (ret) { - struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1]; - pr_debug("[AP] STA added, link_id: %d\n", ret); - entry->status = CW1200_LINK_RESERVE; - memcpy(&entry->mac, mac, ETH_ALEN); - memset(&entry->buffered, 0, CW1200_MAX_TID); - skb_queue_head_init(&entry->rx_queue); - wsm_lock_tx_async(priv); - if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) - wsm_unlock_tx(priv); - } else { - wiphy_info(priv->hw->wiphy, - "[AP] Early: no more link IDs available.\n"); - } - - spin_unlock_bh(&priv->ps_state_lock); - return ret; -} - -void cw1200_link_id_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, link_id_work); - wsm_flush_tx(priv); - cw1200_link_id_gc_work(&priv->link_id_gc_work.work); - wsm_unlock_tx(priv); -} - -void cw1200_link_id_gc_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, link_id_gc_work.work); - struct wsm_reset reset = { - .reset_statistics = false, - }; - struct wsm_map_link map_link = { - .link_id = 0, - }; - unsigned long now = jiffies; - unsigned long next_gc = -1; - long ttl; - bool need_reset; - u32 mask; - int i; - - if (priv->join_status != CW1200_JOIN_STATUS_AP) - return; - - wsm_lock_tx(priv); - spin_lock_bh(&priv->ps_state_lock); - for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { - need_reset = false; - mask = BIT(i + 1); - if (priv->link_id_db[i].status == CW1200_LINK_RESERVE || - (priv->link_id_db[i].status == CW1200_LINK_HARD && - !(priv->link_id_map & mask))) { - if (priv->link_id_map & mask) { - priv->sta_asleep_mask &= ~mask; - priv->pspoll_mask &= ~mask; - need_reset = true; - } - priv->link_id_map |= mask; - if (priv->link_id_db[i].status != CW1200_LINK_HARD) - priv->link_id_db[i].status = CW1200_LINK_SOFT; - memcpy(map_link.mac_addr, priv->link_id_db[i].mac, - ETH_ALEN); - spin_unlock_bh(&priv->ps_state_lock); - if (need_reset) { - reset.link_id = i + 1; - wsm_reset(priv, &reset); - } - map_link.link_id = i + 1; - wsm_map_link(priv, &map_link); - next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT); - spin_lock_bh(&priv->ps_state_lock); - } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) { - ttl = priv->link_id_db[i].timestamp - now + - CW1200_LINK_ID_GC_TIMEOUT; - if (ttl <= 0) { - need_reset = true; - priv->link_id_db[i].status = CW1200_LINK_OFF; - priv->link_id_map &= ~mask; - priv->sta_asleep_mask &= ~mask; - priv->pspoll_mask &= ~mask; - eth_zero_addr(map_link.mac_addr); - spin_unlock_bh(&priv->ps_state_lock); - reset.link_id = i + 1; - wsm_reset(priv, &reset); - spin_lock_bh(&priv->ps_state_lock); - } else { - next_gc = min_t(unsigned long, next_gc, ttl); - } - } else if (priv->link_id_db[i].status == CW1200_LINK_RESET || - priv->link_id_db[i].status == - CW1200_LINK_RESET_REMAP) { - int status = priv->link_id_db[i].status; - priv->link_id_db[i].status = - priv->link_id_db[i].prev_status; - priv->link_id_db[i].timestamp = now; - reset.link_id = i + 1; - spin_unlock_bh(&priv->ps_state_lock); - wsm_reset(priv, &reset); - if (status == CW1200_LINK_RESET_REMAP) { - memcpy(map_link.mac_addr, - priv->link_id_db[i].mac, - ETH_ALEN); - map_link.link_id = i + 1; - wsm_map_link(priv, &map_link); - next_gc = min(next_gc, - CW1200_LINK_ID_GC_TIMEOUT); - } - spin_lock_bh(&priv->ps_state_lock); - } - if (need_reset) { - skb_queue_purge(&priv->link_id_db[i].rx_queue); - pr_debug("[AP] STA removed, link_id: %d\n", - reset.link_id); - } - } - spin_unlock_bh(&priv->ps_state_lock); - if (next_gc != -1) - queue_delayed_work(priv->workqueue, - &priv->link_id_gc_work, next_gc); - wsm_unlock_tx(priv); -} diff --git a/drivers/net/wireless/cw1200/txrx.h b/drivers/net/wireless/cw1200/txrx.h deleted file mode 100644 index 492a4e14213b..000000000000 --- a/drivers/net/wireless/cw1200/txrx.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Datapath interface for ST-Ericsson CW1200 mac80211 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef CW1200_TXRX_H -#define CW1200_TXRX_H - -#include - -/* extern */ struct ieee80211_hw; -/* extern */ struct sk_buff; -/* extern */ struct wsm_tx; -/* extern */ struct wsm_rx; -/* extern */ struct wsm_tx_confirm; -/* extern */ struct cw1200_txpriv; - -struct tx_policy { - union { - __le32 tbl[3]; - u8 raw[12]; - }; - u8 defined; - u8 usage_count; - u8 retry_count; - u8 uploaded; -}; - -struct tx_policy_cache_entry { - struct tx_policy policy; - struct list_head link; -}; - -#define TX_POLICY_CACHE_SIZE (8) -struct tx_policy_cache { - struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE]; - struct list_head used; - struct list_head free; - spinlock_t lock; /* Protect policy cache */ -}; - -/* ******************************************************************** */ -/* TX policy cache */ -/* Intention of TX policy cache is an overcomplicated WSM API. - * Device does not accept per-PDU tx retry sequence. - * It uses "tx retry policy id" instead, so driver code has to sync - * linux tx retry sequences with a retry policy table in the device. - */ -void tx_policy_init(struct cw1200_common *priv); -void tx_policy_upload_work(struct work_struct *work); -void tx_policy_clean(struct cw1200_common *priv); - -/* ******************************************************************** */ -/* TX implementation */ - -u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, - u32 rates); -void cw1200_tx(struct ieee80211_hw *dev, - struct ieee80211_tx_control *control, - struct sk_buff *skb); -void cw1200_skb_dtor(struct cw1200_common *priv, - struct sk_buff *skb, - const struct cw1200_txpriv *txpriv); - -/* ******************************************************************** */ -/* WSM callbacks */ - -void cw1200_tx_confirm_cb(struct cw1200_common *priv, - int link_id, - struct wsm_tx_confirm *arg); -void cw1200_rx_cb(struct cw1200_common *priv, - struct wsm_rx *arg, - int link_id, - struct sk_buff **skb_p); - -/* ******************************************************************** */ -/* Timeout */ - -void cw1200_tx_timeout(struct work_struct *work); - -/* ******************************************************************** */ -/* Security */ -int cw1200_alloc_key(struct cw1200_common *priv); -void cw1200_free_key(struct cw1200_common *priv, int idx); -void cw1200_free_keys(struct cw1200_common *priv); -int cw1200_upload_keys(struct cw1200_common *priv); - -/* ******************************************************************** */ -/* Workaround for WFD test case 6.1.10 */ -void cw1200_link_id_reset(struct work_struct *work); - -#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) - -int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac); -int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac); -void cw1200_link_id_work(struct work_struct *work); -void cw1200_link_id_gc_work(struct work_struct *work); - - -#endif /* CW1200_TXRX_H */ diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c deleted file mode 100644 index 9e0ca3048657..000000000000 --- a/drivers/net/wireless/cw1200/wsm.c +++ /dev/null @@ -1,1822 +0,0 @@ -/* - * WSM host interface (HI) implementation for - * ST-Ericsson CW1200 mac80211 drivers. - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include - -#include "cw1200.h" -#include "wsm.h" -#include "bh.h" -#include "sta.h" -#include "debug.h" - -#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ -#define WSM_CMD_START_TIMEOUT (7 * HZ) -#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ -#define WSM_CMD_MAX_TIMEOUT (3 * HZ) - -#define WSM_SKIP(buf, size) \ - do { \ - if ((buf)->data + size > (buf)->end) \ - goto underflow; \ - (buf)->data += size; \ - } while (0) - -#define WSM_GET(buf, ptr, size) \ - do { \ - if ((buf)->data + size > (buf)->end) \ - goto underflow; \ - memcpy(ptr, (buf)->data, size); \ - (buf)->data += size; \ - } while (0) - -#define __WSM_GET(buf, type, type2, cvt) \ - ({ \ - type val; \ - if ((buf)->data + sizeof(type) > (buf)->end) \ - goto underflow; \ - val = cvt(*(type2 *)(buf)->data); \ - (buf)->data += sizeof(type); \ - val; \ - }) - -#define WSM_GET8(buf) __WSM_GET(buf, u8, u8, (u8)) -#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16, __le16_to_cpu) -#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32, __le32_to_cpu) - -#define WSM_PUT(buf, ptr, size) \ - do { \ - if ((buf)->data + size > (buf)->end) \ - if (wsm_buf_reserve((buf), size)) \ - goto nomem; \ - memcpy((buf)->data, ptr, size); \ - (buf)->data += size; \ - } while (0) - -#define __WSM_PUT(buf, val, type, type2, cvt) \ - do { \ - if ((buf)->data + sizeof(type) > (buf)->end) \ - if (wsm_buf_reserve((buf), sizeof(type))) \ - goto nomem; \ - *(type2 *)(buf)->data = cvt(val); \ - (buf)->data += sizeof(type); \ - } while (0) - -#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, u8, (u8)) -#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __le16, __cpu_to_le16) -#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __le32, __cpu_to_le32) - -static void wsm_buf_reset(struct wsm_buf *buf); -static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); - -static int wsm_cmd_send(struct cw1200_common *priv, - struct wsm_buf *buf, - void *arg, u16 cmd, long tmo); - -#define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux)) -#define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux)) - -/* ******************************************************************** */ -/* WSM API implementation */ - -static int wsm_generic_confirm(struct cw1200_common *priv, - void *arg, - struct wsm_buf *buf) -{ - u32 status = WSM_GET32(buf); - if (status != WSM_STATUS_SUCCESS) - return -EINVAL; - return 0; - -underflow: - WARN_ON(1); - return -EINVAL; -} - -int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - wsm_cmd_lock(priv); - - WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); - WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); - WSM_PUT32(buf, arg->dot11RtsThreshold); - - /* DPD block. */ - WSM_PUT16(buf, arg->dpdData_size + 12); - WSM_PUT16(buf, 1); /* DPD version */ - WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); - WSM_PUT16(buf, 5); /* DPD flags */ - WSM_PUT(buf, arg->dpdData, arg->dpdData_size); - - ret = wsm_cmd_send(priv, buf, arg, - WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT); - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -static int wsm_configuration_confirm(struct cw1200_common *priv, - struct wsm_configuration *arg, - struct wsm_buf *buf) -{ - int i; - int status; - - status = WSM_GET32(buf); - if (WARN_ON(status != WSM_STATUS_SUCCESS)) - return -EINVAL; - - WSM_GET(buf, arg->dot11StationId, ETH_ALEN); - arg->dot11FrequencyBandsSupported = WSM_GET8(buf); - WSM_SKIP(buf, 1); - arg->supportedRateMask = WSM_GET32(buf); - for (i = 0; i < 2; ++i) { - arg->txPowerRange[i].min_power_level = WSM_GET32(buf); - arg->txPowerRange[i].max_power_level = WSM_GET32(buf); - arg->txPowerRange[i].stepping = WSM_GET32(buf); - } - return 0; - -underflow: - WARN_ON(1); - return -EINVAL; -} - -/* ******************************************************************** */ - -int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id); - - wsm_cmd_lock(priv); - - WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); - ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT); - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -struct wsm_mib { - u16 mib_id; - void *buf; - size_t buf_size; -}; - -int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, - size_t buf_size) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - struct wsm_mib mib_buf = { - .mib_id = mib_id, - .buf = _buf, - .buf_size = buf_size, - }; - wsm_cmd_lock(priv); - - WSM_PUT16(buf, mib_id); - WSM_PUT16(buf, 0); - - ret = wsm_cmd_send(priv, buf, &mib_buf, - WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT); - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -static int wsm_read_mib_confirm(struct cw1200_common *priv, - struct wsm_mib *arg, - struct wsm_buf *buf) -{ - u16 size; - if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) - return -EINVAL; - - if (WARN_ON(WSM_GET16(buf) != arg->mib_id)) - return -EINVAL; - - size = WSM_GET16(buf); - if (size > arg->buf_size) - size = arg->buf_size; - - WSM_GET(buf, arg->buf, size); - arg->buf_size = size; - return 0; - -underflow: - WARN_ON(1); - return -EINVAL; -} - -/* ******************************************************************** */ - -int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, - size_t buf_size) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - struct wsm_mib mib_buf = { - .mib_id = mib_id, - .buf = _buf, - .buf_size = buf_size, - }; - - wsm_cmd_lock(priv); - - WSM_PUT16(buf, mib_id); - WSM_PUT16(buf, buf_size); - WSM_PUT(buf, _buf, buf_size); - - ret = wsm_cmd_send(priv, buf, &mib_buf, - WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT); - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -static int wsm_write_mib_confirm(struct cw1200_common *priv, - struct wsm_mib *arg, - struct wsm_buf *buf) -{ - int ret; - - ret = wsm_generic_confirm(priv, arg, buf); - if (ret) - return ret; - - if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) { - /* OperationalMode: update PM status. */ - const char *p = arg->buf; - cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false); - } - return 0; -} - -/* ******************************************************************** */ - -int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg) -{ - int i; - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - if (arg->num_channels > 48) - return -EINVAL; - - if (arg->num_ssids > 2) - return -EINVAL; - - if (arg->band > 1) - return -EINVAL; - - wsm_cmd_lock(priv); - - WSM_PUT8(buf, arg->band); - WSM_PUT8(buf, arg->type); - WSM_PUT8(buf, arg->flags); - WSM_PUT8(buf, arg->max_tx_rate); - WSM_PUT32(buf, arg->auto_scan_interval); - WSM_PUT8(buf, arg->num_probes); - WSM_PUT8(buf, arg->num_channels); - WSM_PUT8(buf, arg->num_ssids); - WSM_PUT8(buf, arg->probe_delay); - - for (i = 0; i < arg->num_channels; ++i) { - WSM_PUT16(buf, arg->ch[i].number); - WSM_PUT16(buf, 0); - WSM_PUT32(buf, arg->ch[i].min_chan_time); - WSM_PUT32(buf, arg->ch[i].max_chan_time); - WSM_PUT32(buf, 0); - } - - for (i = 0; i < arg->num_ssids; ++i) { - WSM_PUT32(buf, arg->ssids[i].length); - WSM_PUT(buf, &arg->ssids[i].ssid[0], - sizeof(arg->ssids[i].ssid)); - } - - ret = wsm_cmd_send(priv, buf, NULL, - WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT); - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_stop_scan(struct cw1200_common *priv) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - wsm_cmd_lock(priv); - ret = wsm_cmd_send(priv, buf, NULL, - WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT); - wsm_cmd_unlock(priv); - return ret; -} - - -static int wsm_tx_confirm(struct cw1200_common *priv, - struct wsm_buf *buf, - int link_id) -{ - struct wsm_tx_confirm tx_confirm; - - tx_confirm.packet_id = WSM_GET32(buf); - tx_confirm.status = WSM_GET32(buf); - tx_confirm.tx_rate = WSM_GET8(buf); - tx_confirm.ack_failures = WSM_GET8(buf); - tx_confirm.flags = WSM_GET16(buf); - tx_confirm.media_delay = WSM_GET32(buf); - tx_confirm.tx_queue_delay = WSM_GET32(buf); - - cw1200_tx_confirm_cb(priv, link_id, &tx_confirm); - return 0; - -underflow: - WARN_ON(1); - return -EINVAL; -} - -static int wsm_multi_tx_confirm(struct cw1200_common *priv, - struct wsm_buf *buf, int link_id) -{ - int ret; - int count; - int i; - - count = WSM_GET32(buf); - if (WARN_ON(count <= 0)) - return -EINVAL; - - if (count > 1) { - /* We already released one buffer, now for the rest */ - ret = wsm_release_tx_buffer(priv, count - 1); - if (ret < 0) - return ret; - else if (ret > 0) - cw1200_bh_wakeup(priv); - } - - cw1200_debug_txed_multi(priv, count); - for (i = 0; i < count; ++i) { - ret = wsm_tx_confirm(priv, buf, link_id); - if (ret) - return ret; - } - return ret; - -underflow: - WARN_ON(1); - return -EINVAL; -} - -/* ******************************************************************** */ - -static int wsm_join_confirm(struct cw1200_common *priv, - struct wsm_join_cnf *arg, - struct wsm_buf *buf) -{ - arg->status = WSM_GET32(buf); - if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS) - return -EINVAL; - - arg->min_power_level = WSM_GET32(buf); - arg->max_power_level = WSM_GET32(buf); - - return 0; - -underflow: - WARN_ON(1); - return -EINVAL; -} - -int wsm_join(struct cw1200_common *priv, struct wsm_join *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - struct wsm_join_cnf resp; - wsm_cmd_lock(priv); - - WSM_PUT8(buf, arg->mode); - WSM_PUT8(buf, arg->band); - WSM_PUT16(buf, arg->channel_number); - WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); - WSM_PUT16(buf, arg->atim_window); - WSM_PUT8(buf, arg->preamble_type); - WSM_PUT8(buf, arg->probe_for_join); - WSM_PUT8(buf, arg->dtim_period); - WSM_PUT8(buf, arg->flags); - WSM_PUT32(buf, arg->ssid_len); - WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); - WSM_PUT32(buf, arg->beacon_interval); - WSM_PUT32(buf, arg->basic_rate_set); - - priv->tx_burst_idx = -1; - ret = wsm_cmd_send(priv, buf, &resp, - WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT); - /* TODO: Update state based on resp.min|max_power_level */ - - priv->join_complete_status = resp.status; - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_set_bss_params(struct cw1200_common *priv, - const struct wsm_set_bss_params *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - wsm_cmd_lock(priv); - - WSM_PUT8(buf, (arg->reset_beacon_loss ? 0x1 : 0)); - WSM_PUT8(buf, arg->beacon_lost_count); - WSM_PUT16(buf, arg->aid); - WSM_PUT32(buf, arg->operational_rate_set); - - ret = wsm_cmd_send(priv, buf, NULL, - WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - wsm_cmd_lock(priv); - - WSM_PUT(buf, arg, sizeof(*arg)); - - ret = wsm_cmd_send(priv, buf, NULL, - WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT); - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - wsm_cmd_lock(priv); - - WSM_PUT8(buf, arg->index); - WSM_PUT8(buf, 0); - WSM_PUT16(buf, 0); - - ret = wsm_cmd_send(priv, buf, NULL, - WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT); - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_set_tx_queue_params(struct cw1200_common *priv, - const struct wsm_set_tx_queue_params *arg, u8 id) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; - - wsm_cmd_lock(priv); - - WSM_PUT8(buf, queue_id_to_wmm_aci[id]); - WSM_PUT8(buf, 0); - WSM_PUT8(buf, arg->ackPolicy); - WSM_PUT8(buf, 0); - WSM_PUT32(buf, arg->maxTransmitLifetime); - WSM_PUT16(buf, arg->allowedMediumTime); - WSM_PUT16(buf, 0); - - ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT); - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_set_edca_params(struct cw1200_common *priv, - const struct wsm_edca_params *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - wsm_cmd_lock(priv); - - /* Implemented according to specification. */ - - WSM_PUT16(buf, arg->params[3].cwmin); - WSM_PUT16(buf, arg->params[2].cwmin); - WSM_PUT16(buf, arg->params[1].cwmin); - WSM_PUT16(buf, arg->params[0].cwmin); - - WSM_PUT16(buf, arg->params[3].cwmax); - WSM_PUT16(buf, arg->params[2].cwmax); - WSM_PUT16(buf, arg->params[1].cwmax); - WSM_PUT16(buf, arg->params[0].cwmax); - - WSM_PUT8(buf, arg->params[3].aifns); - WSM_PUT8(buf, arg->params[2].aifns); - WSM_PUT8(buf, arg->params[1].aifns); - WSM_PUT8(buf, arg->params[0].aifns); - - WSM_PUT16(buf, arg->params[3].txop_limit); - WSM_PUT16(buf, arg->params[2].txop_limit); - WSM_PUT16(buf, arg->params[1].txop_limit); - WSM_PUT16(buf, arg->params[0].txop_limit); - - WSM_PUT32(buf, arg->params[3].max_rx_lifetime); - WSM_PUT32(buf, arg->params[2].max_rx_lifetime); - WSM_PUT32(buf, arg->params[1].max_rx_lifetime); - WSM_PUT32(buf, arg->params[0].max_rx_lifetime); - - ret = wsm_cmd_send(priv, buf, NULL, - WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_switch_channel(struct cw1200_common *priv, - const struct wsm_switch_channel *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - wsm_cmd_lock(priv); - - WSM_PUT8(buf, arg->mode); - WSM_PUT8(buf, arg->switch_count); - WSM_PUT16(buf, arg->channel_number); - - priv->channel_switch_in_progress = 1; - - ret = wsm_cmd_send(priv, buf, NULL, - WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT); - if (ret) - priv->channel_switch_in_progress = 0; - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - priv->ps_mode_switch_in_progress = 1; - - wsm_cmd_lock(priv); - - WSM_PUT8(buf, arg->mode); - WSM_PUT8(buf, arg->fast_psm_idle_period); - WSM_PUT8(buf, arg->ap_psm_change_period); - WSM_PUT8(buf, arg->min_auto_pspoll_period); - - ret = wsm_cmd_send(priv, buf, NULL, - WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT); - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - wsm_cmd_lock(priv); - - WSM_PUT8(buf, arg->mode); - WSM_PUT8(buf, arg->band); - WSM_PUT16(buf, arg->channel_number); - WSM_PUT32(buf, arg->ct_window); - WSM_PUT32(buf, arg->beacon_interval); - WSM_PUT8(buf, arg->dtim_period); - WSM_PUT8(buf, arg->preamble); - WSM_PUT8(buf, arg->probe_delay); - WSM_PUT8(buf, arg->ssid_len); - WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); - WSM_PUT32(buf, arg->basic_rate_set); - - priv->tx_burst_idx = -1; - ret = wsm_cmd_send(priv, buf, NULL, - WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT); - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_beacon_transmit(struct cw1200_common *priv, - const struct wsm_beacon_transmit *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - wsm_cmd_lock(priv); - - WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0); - - ret = wsm_cmd_send(priv, buf, NULL, - WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT); - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_start_find(struct cw1200_common *priv) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - wsm_cmd_lock(priv); - ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT); - wsm_cmd_unlock(priv); - return ret; -} - -/* ******************************************************************** */ - -int wsm_stop_find(struct cw1200_common *priv) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - wsm_cmd_lock(priv); - ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT); - wsm_cmd_unlock(priv); - return ret; -} - -/* ******************************************************************** */ - -int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id); - - wsm_cmd_lock(priv); - - WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); - WSM_PUT16(buf, 0); - - ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT); - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ - -int wsm_update_ie(struct cw1200_common *priv, - const struct wsm_update_ie *arg) -{ - int ret; - struct wsm_buf *buf = &priv->wsm_cmd_buf; - - wsm_cmd_lock(priv); - - WSM_PUT16(buf, arg->what); - WSM_PUT16(buf, arg->count); - WSM_PUT(buf, arg->ies, arg->length); - - ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT); - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} - -/* ******************************************************************** */ -int wsm_set_probe_responder(struct cw1200_common *priv, bool enable) -{ - priv->rx_filter.probeResponder = enable; - return wsm_set_rx_filter(priv, &priv->rx_filter); -} - -/* ******************************************************************** */ -/* WSM indication events implementation */ -const char * const cw1200_fw_types[] = { - "ETF", - "WFM", - "WSM", - "HI test", - "Platform test" -}; - -static int wsm_startup_indication(struct cw1200_common *priv, - struct wsm_buf *buf) -{ - priv->wsm_caps.input_buffers = WSM_GET16(buf); - priv->wsm_caps.input_buffer_size = WSM_GET16(buf); - priv->wsm_caps.hw_id = WSM_GET16(buf); - priv->wsm_caps.hw_subid = WSM_GET16(buf); - priv->wsm_caps.status = WSM_GET16(buf); - priv->wsm_caps.fw_cap = WSM_GET16(buf); - priv->wsm_caps.fw_type = WSM_GET16(buf); - priv->wsm_caps.fw_api = WSM_GET16(buf); - priv->wsm_caps.fw_build = WSM_GET16(buf); - priv->wsm_caps.fw_ver = WSM_GET16(buf); - WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label)); - priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */ - - if (WARN_ON(priv->wsm_caps.status)) - return -EINVAL; - - if (WARN_ON(priv->wsm_caps.fw_type > 4)) - return -EINVAL; - - pr_info("CW1200 WSM init done.\n" - " Input buffers: %d x %d bytes\n" - " Hardware: %d.%d\n" - " %s firmware [%s], ver: %d, build: %d," - " api: %d, cap: 0x%.4X\n", - priv->wsm_caps.input_buffers, - priv->wsm_caps.input_buffer_size, - priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid, - cw1200_fw_types[priv->wsm_caps.fw_type], - priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver, - priv->wsm_caps.fw_build, - priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap); - - /* Disable unsupported frequency bands */ - if (!(priv->wsm_caps.fw_cap & 0x1)) - priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; - if (!(priv->wsm_caps.fw_cap & 0x2)) - priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; - - priv->firmware_ready = 1; - wake_up(&priv->wsm_startup_done); - return 0; - -underflow: - WARN_ON(1); - return -EINVAL; -} - -static int wsm_receive_indication(struct cw1200_common *priv, - int link_id, - struct wsm_buf *buf, - struct sk_buff **skb_p) -{ - struct wsm_rx rx; - struct ieee80211_hdr *hdr; - size_t hdr_len; - __le16 fctl; - - rx.status = WSM_GET32(buf); - rx.channel_number = WSM_GET16(buf); - rx.rx_rate = WSM_GET8(buf); - rx.rcpi_rssi = WSM_GET8(buf); - rx.flags = WSM_GET32(buf); - - /* FW Workaround: Drop probe resp or - beacon when RSSI is 0 - */ - hdr = (struct ieee80211_hdr *)(*skb_p)->data; - - if (!rx.rcpi_rssi && - (ieee80211_is_probe_resp(hdr->frame_control) || - ieee80211_is_beacon(hdr->frame_control))) - return 0; - - /* If no RSSI subscription has been made, - * convert RCPI to RSSI here - */ - if (!priv->cqm_use_rssi) - rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110; - - fctl = *(__le16 *)buf->data; - hdr_len = buf->data - buf->begin; - skb_pull(*skb_p, hdr_len); - if (!rx.status && ieee80211_is_deauth(fctl)) { - if (priv->join_status == CW1200_JOIN_STATUS_STA) { - /* Shedule unjoin work */ - pr_debug("[WSM] Issue unjoin command (RX).\n"); - wsm_lock_tx_async(priv); - if (queue_work(priv->workqueue, - &priv->unjoin_work) <= 0) - wsm_unlock_tx(priv); - } - } - cw1200_rx_cb(priv, &rx, link_id, skb_p); - if (*skb_p) - skb_push(*skb_p, hdr_len); - - return 0; - -underflow: - return -EINVAL; -} - -static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf) -{ - int first; - struct cw1200_wsm_event *event; - - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { - /* STA is stopped. */ - return 0; - } - - event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL); - if (!event) - return -ENOMEM; - - event->evt.id = WSM_GET32(buf); - event->evt.data = WSM_GET32(buf); - - pr_debug("[WSM] Event: %d(%d)\n", - event->evt.id, event->evt.data); - - spin_lock(&priv->event_queue_lock); - first = list_empty(&priv->event_queue); - list_add_tail(&event->link, &priv->event_queue); - spin_unlock(&priv->event_queue_lock); - - if (first) - queue_work(priv->workqueue, &priv->event_handler); - - return 0; - -underflow: - kfree(event); - return -EINVAL; -} - -static int wsm_channel_switch_indication(struct cw1200_common *priv, - struct wsm_buf *buf) -{ - WARN_ON(WSM_GET32(buf)); - - priv->channel_switch_in_progress = 0; - wake_up(&priv->channel_switch_done); - - wsm_unlock_tx(priv); - - return 0; - -underflow: - return -EINVAL; -} - -static int wsm_set_pm_indication(struct cw1200_common *priv, - struct wsm_buf *buf) -{ - /* TODO: Check buf (struct wsm_set_pm_complete) for validity */ - if (priv->ps_mode_switch_in_progress) { - priv->ps_mode_switch_in_progress = 0; - wake_up(&priv->ps_mode_switch_done); - } - return 0; -} - -static int wsm_scan_started(struct cw1200_common *priv, void *arg, - struct wsm_buf *buf) -{ - u32 status = WSM_GET32(buf); - if (status != WSM_STATUS_SUCCESS) { - cw1200_scan_failed_cb(priv); - return -EINVAL; - } - return 0; - -underflow: - WARN_ON(1); - return -EINVAL; -} - -static int wsm_scan_complete_indication(struct cw1200_common *priv, - struct wsm_buf *buf) -{ - struct wsm_scan_complete arg; - arg.status = WSM_GET32(buf); - arg.psm = WSM_GET8(buf); - arg.num_channels = WSM_GET8(buf); - cw1200_scan_complete_cb(priv, &arg); - - return 0; - -underflow: - return -EINVAL; -} - -static int wsm_join_complete_indication(struct cw1200_common *priv, - struct wsm_buf *buf) -{ - struct wsm_join_complete arg; - arg.status = WSM_GET32(buf); - pr_debug("[WSM] Join complete indication, status: %d\n", arg.status); - cw1200_join_complete_cb(priv, &arg); - - return 0; - -underflow: - return -EINVAL; -} - -static int wsm_find_complete_indication(struct cw1200_common *priv, - struct wsm_buf *buf) -{ - pr_warn("Implement find_complete_indication\n"); - return 0; -} - -static int wsm_ba_timeout_indication(struct cw1200_common *priv, - struct wsm_buf *buf) -{ - u32 dummy; - u8 tid; - u8 dummy2; - u8 addr[ETH_ALEN]; - - dummy = WSM_GET32(buf); - tid = WSM_GET8(buf); - dummy2 = WSM_GET8(buf); - WSM_GET(buf, addr, ETH_ALEN); - - pr_info("BlockACK timeout, tid %d, addr %pM\n", - tid, addr); - - return 0; - -underflow: - return -EINVAL; -} - -static int wsm_suspend_resume_indication(struct cw1200_common *priv, - int link_id, struct wsm_buf *buf) -{ - u32 flags; - struct wsm_suspend_resume arg; - - flags = WSM_GET32(buf); - arg.link_id = link_id; - arg.stop = !(flags & 1); - arg.multicast = !!(flags & 8); - arg.queue = (flags >> 1) & 3; - - cw1200_suspend_resume(priv, &arg); - - return 0; - -underflow: - return -EINVAL; -} - - -/* ******************************************************************** */ -/* WSM TX */ - -static int wsm_cmd_send(struct cw1200_common *priv, - struct wsm_buf *buf, - void *arg, u16 cmd, long tmo) -{ - size_t buf_len = buf->data - buf->begin; - int ret; - - /* Don't bother if we're dead. */ - if (priv->bh_error) { - ret = 0; - goto done; - } - - /* Block until the cmd buffer is completed. Tortuous. */ - spin_lock(&priv->wsm_cmd.lock); - while (!priv->wsm_cmd.done) { - spin_unlock(&priv->wsm_cmd.lock); - spin_lock(&priv->wsm_cmd.lock); - } - priv->wsm_cmd.done = 0; - spin_unlock(&priv->wsm_cmd.lock); - - if (cmd == WSM_WRITE_MIB_REQ_ID || - cmd == WSM_READ_MIB_REQ_ID) - pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n", - cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]), - buf_len); - else - pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len); - - /* Due to buggy SPI on CW1200, we need to - * pad the message by a few bytes to ensure - * that it's completely received. - */ - buf_len += 4; - - /* Fill HI message header */ - /* BH will add sequence number */ - ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); - ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); - - spin_lock(&priv->wsm_cmd.lock); - BUG_ON(priv->wsm_cmd.ptr); - priv->wsm_cmd.ptr = buf->begin; - priv->wsm_cmd.len = buf_len; - priv->wsm_cmd.arg = arg; - priv->wsm_cmd.cmd = cmd; - spin_unlock(&priv->wsm_cmd.lock); - - cw1200_bh_wakeup(priv); - - /* Wait for command completion */ - ret = wait_event_timeout(priv->wsm_cmd_wq, - priv->wsm_cmd.done, tmo); - - if (!ret && !priv->wsm_cmd.done) { - spin_lock(&priv->wsm_cmd.lock); - priv->wsm_cmd.done = 1; - priv->wsm_cmd.ptr = NULL; - spin_unlock(&priv->wsm_cmd.lock); - if (priv->bh_error) { - /* Return ok to help system cleanup */ - ret = 0; - } else { - pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd); - print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE, - buf->begin, buf_len); - pr_err("Outstanding outgoing frames: %d\n", priv->hw_bufs_used); - - /* Kill BH thread to report the error to the top layer. */ - atomic_add(1, &priv->bh_term); - wake_up(&priv->bh_wq); - ret = -ETIMEDOUT; - } - } else { - spin_lock(&priv->wsm_cmd.lock); - BUG_ON(!priv->wsm_cmd.done); - ret = priv->wsm_cmd.ret; - spin_unlock(&priv->wsm_cmd.lock); - } -done: - wsm_buf_reset(buf); - return ret; -} - -/* ******************************************************************** */ -/* WSM TX port control */ - -void wsm_lock_tx(struct cw1200_common *priv) -{ - wsm_cmd_lock(priv); - if (atomic_add_return(1, &priv->tx_lock) == 1) { - if (wsm_flush_tx(priv)) - pr_debug("[WSM] TX is locked.\n"); - } - wsm_cmd_unlock(priv); -} - -void wsm_lock_tx_async(struct cw1200_common *priv) -{ - if (atomic_add_return(1, &priv->tx_lock) == 1) - pr_debug("[WSM] TX is locked (async).\n"); -} - -bool wsm_flush_tx(struct cw1200_common *priv) -{ - unsigned long timestamp = jiffies; - bool pending = false; - long timeout; - int i; - - /* Flush must be called with TX lock held. */ - BUG_ON(!atomic_read(&priv->tx_lock)); - - /* First check if we really need to do something. - * It is safe to use unprotected access, as hw_bufs_used - * can only decrements. - */ - if (!priv->hw_bufs_used) - return true; - - if (priv->bh_error) { - /* In case of failure do not wait for magic. */ - pr_err("[WSM] Fatal error occurred, will not flush TX.\n"); - return false; - } else { - /* Get a timestamp of "oldest" frame */ - for (i = 0; i < 4; ++i) - pending |= cw1200_queue_get_xmit_timestamp( - &priv->tx_queue[i], - ×tamp, 0xffffffff); - /* If there's nothing pending, we're good */ - if (!pending) - return true; - - timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies; - if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq, - !priv->hw_bufs_used, - timeout) <= 0) { - /* Hmmm... Not good. Frame had stuck in firmware. */ - priv->bh_error = 1; - wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used); - wake_up(&priv->bh_wq); - return false; - } - - /* Ok, everything is flushed. */ - return true; - } -} - -void wsm_unlock_tx(struct cw1200_common *priv) -{ - int tx_lock; - tx_lock = atomic_sub_return(1, &priv->tx_lock); - BUG_ON(tx_lock < 0); - - if (tx_lock == 0) { - if (!priv->bh_error) - cw1200_bh_wakeup(priv); - pr_debug("[WSM] TX is unlocked.\n"); - } -} - -/* ******************************************************************** */ -/* WSM RX */ - -int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) -{ - struct wsm_buf buf; - u32 reason; - u32 reg[18]; - char fname[48]; - unsigned int i; - - static const char * const reason_str[] = { - "undefined instruction", - "prefetch abort", - "data abort", - "unknown error", - }; - - buf.begin = buf.data = data; - buf.end = &buf.begin[len]; - - reason = WSM_GET32(&buf); - for (i = 0; i < ARRAY_SIZE(reg); ++i) - reg[i] = WSM_GET32(&buf); - WSM_GET(&buf, fname, sizeof(fname)); - - if (reason < 4) - wiphy_err(priv->hw->wiphy, - "Firmware exception: %s.\n", - reason_str[reason]); - else - wiphy_err(priv->hw->wiphy, - "Firmware assert at %.*s, line %d\n", - (int) sizeof(fname), fname, reg[1]); - - for (i = 0; i < 12; i += 4) - wiphy_err(priv->hw->wiphy, - "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", - i + 0, reg[i + 0], i + 1, reg[i + 1], - i + 2, reg[i + 2], i + 3, reg[i + 3]); - wiphy_err(priv->hw->wiphy, - "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", - reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); - i += 4; - wiphy_err(priv->hw->wiphy, - "CPSR: 0x%.8X, SPSR: 0x%.8X\n", - reg[i + 0], reg[i + 1]); - - print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE, - fname, sizeof(fname)); - return 0; - -underflow: - wiphy_err(priv->hw->wiphy, "Firmware exception.\n"); - print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, - data, len); - return -EINVAL; -} - -int wsm_handle_rx(struct cw1200_common *priv, u16 id, - struct wsm_hdr *wsm, struct sk_buff **skb_p) -{ - int ret = 0; - struct wsm_buf wsm_buf; - int link_id = (id >> 6) & 0x0F; - - /* Strip link id. */ - id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); - - wsm_buf.begin = (u8 *)&wsm[0]; - wsm_buf.data = (u8 *)&wsm[1]; - wsm_buf.end = &wsm_buf.begin[__le16_to_cpu(wsm->len)]; - - pr_debug("[WSM] <<< 0x%.4X (%td)\n", id, - wsm_buf.end - wsm_buf.begin); - - if (id == WSM_TX_CONFIRM_IND_ID) { - ret = wsm_tx_confirm(priv, &wsm_buf, link_id); - } else if (id == WSM_MULTI_TX_CONFIRM_ID) { - ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id); - } else if (id & 0x0400) { - void *wsm_arg; - u16 wsm_cmd; - - /* Do not trust FW too much. Protection against repeated - * response and race condition removal (see above). - */ - spin_lock(&priv->wsm_cmd.lock); - wsm_arg = priv->wsm_cmd.arg; - wsm_cmd = priv->wsm_cmd.cmd & - ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); - priv->wsm_cmd.cmd = 0xFFFF; - spin_unlock(&priv->wsm_cmd.lock); - - if (WARN_ON((id & ~0x0400) != wsm_cmd)) { - /* Note that any non-zero is a fatal retcode. */ - ret = -EINVAL; - goto out; - } - - /* Note that wsm_arg can be NULL in case of timeout in - * wsm_cmd_send(). - */ - - switch (id) { - case WSM_READ_MIB_RESP_ID: - if (wsm_arg) - ret = wsm_read_mib_confirm(priv, wsm_arg, - &wsm_buf); - break; - case WSM_WRITE_MIB_RESP_ID: - if (wsm_arg) - ret = wsm_write_mib_confirm(priv, wsm_arg, - &wsm_buf); - break; - case WSM_START_SCAN_RESP_ID: - if (wsm_arg) - ret = wsm_scan_started(priv, wsm_arg, &wsm_buf); - break; - case WSM_CONFIGURATION_RESP_ID: - if (wsm_arg) - ret = wsm_configuration_confirm(priv, wsm_arg, - &wsm_buf); - break; - case WSM_JOIN_RESP_ID: - if (wsm_arg) - ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf); - break; - case WSM_STOP_SCAN_RESP_ID: - case WSM_RESET_RESP_ID: - case WSM_ADD_KEY_RESP_ID: - case WSM_REMOVE_KEY_RESP_ID: - case WSM_SET_PM_RESP_ID: - case WSM_SET_BSS_PARAMS_RESP_ID: - case 0x0412: /* set_tx_queue_params */ - case WSM_EDCA_PARAMS_RESP_ID: - case WSM_SWITCH_CHANNEL_RESP_ID: - case WSM_START_RESP_ID: - case WSM_BEACON_TRANSMIT_RESP_ID: - case 0x0419: /* start_find */ - case 0x041A: /* stop_find */ - case 0x041B: /* update_ie */ - case 0x041C: /* map_link */ - WARN_ON(wsm_arg != NULL); - ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf); - if (ret) { - wiphy_warn(priv->hw->wiphy, - "wsm_generic_confirm failed for request 0x%04x.\n", - id & ~0x0400); - - /* often 0x407 and 0x410 occur, this means we're dead.. */ - if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) { - wsm_lock_tx(priv); - if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) - wsm_unlock_tx(priv); - } - } - break; - default: - wiphy_warn(priv->hw->wiphy, - "Unrecognized confirmation 0x%04x\n", - id & ~0x0400); - } - - spin_lock(&priv->wsm_cmd.lock); - priv->wsm_cmd.ret = ret; - priv->wsm_cmd.done = 1; - spin_unlock(&priv->wsm_cmd.lock); - - ret = 0; /* Error response from device should ne stop BH. */ - - wake_up(&priv->wsm_cmd_wq); - } else if (id & 0x0800) { - switch (id) { - case WSM_STARTUP_IND_ID: - ret = wsm_startup_indication(priv, &wsm_buf); - break; - case WSM_RECEIVE_IND_ID: - ret = wsm_receive_indication(priv, link_id, - &wsm_buf, skb_p); - break; - case 0x0805: - ret = wsm_event_indication(priv, &wsm_buf); - break; - case WSM_SCAN_COMPLETE_IND_ID: - ret = wsm_scan_complete_indication(priv, &wsm_buf); - break; - case 0x0808: - ret = wsm_ba_timeout_indication(priv, &wsm_buf); - break; - case 0x0809: - ret = wsm_set_pm_indication(priv, &wsm_buf); - break; - case 0x080A: - ret = wsm_channel_switch_indication(priv, &wsm_buf); - break; - case 0x080B: - ret = wsm_find_complete_indication(priv, &wsm_buf); - break; - case 0x080C: - ret = wsm_suspend_resume_indication(priv, - link_id, &wsm_buf); - break; - case 0x080F: - ret = wsm_join_complete_indication(priv, &wsm_buf); - break; - default: - pr_warn("Unrecognised WSM ID %04x\n", id); - } - } else { - WARN_ON(1); - ret = -EINVAL; - } -out: - return ret; -} - -static bool wsm_handle_tx_data(struct cw1200_common *priv, - struct wsm_tx *wsm, - const struct ieee80211_tx_info *tx_info, - const struct cw1200_txpriv *txpriv, - struct cw1200_queue *queue) -{ - bool handled = false; - const struct ieee80211_hdr *frame = - (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset]; - __le16 fctl = frame->frame_control; - enum { - do_probe, - do_drop, - do_wep, - do_tx, - } action = do_tx; - - switch (priv->mode) { - case NL80211_IFTYPE_STATION: - if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) - action = do_tx; - else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) - action = do_drop; - break; - case NL80211_IFTYPE_AP: - if (!priv->join_status) { - action = do_drop; - } else if (!(BIT(txpriv->raw_link_id) & - (BIT(0) | priv->link_id_map))) { - wiphy_warn(priv->hw->wiphy, - "A frame with expired link id is dropped.\n"); - action = do_drop; - } - if (cw1200_queue_get_generation(wsm->packet_id) > - CW1200_MAX_REQUEUE_ATTEMPTS) { - /* HACK!!! WSM324 firmware has tendency to requeue - * multicast frames in a loop, causing performance - * drop and high power consumption of the driver. - * In this situation it is better just to drop - * the problematic frame. - */ - wiphy_warn(priv->hw->wiphy, - "Too many attempts to requeue a frame; dropped.\n"); - action = do_drop; - } - break; - case NL80211_IFTYPE_ADHOC: - if (priv->join_status != CW1200_JOIN_STATUS_IBSS) - action = do_drop; - break; - case NL80211_IFTYPE_MESH_POINT: - action = do_tx; /* TODO: Test me! */ - break; - case NL80211_IFTYPE_MONITOR: - default: - action = do_drop; - break; - } - - if (action == do_tx) { - if (ieee80211_is_nullfunc(fctl)) { - spin_lock(&priv->bss_loss_lock); - if (priv->bss_loss_state) { - priv->bss_loss_confirm_id = wsm->packet_id; - wsm->queue_id = WSM_QUEUE_VOICE; - } - spin_unlock(&priv->bss_loss_lock); - } else if (ieee80211_is_probe_req(fctl)) { - action = do_probe; - } else if (ieee80211_is_deauth(fctl) && - priv->mode != NL80211_IFTYPE_AP) { - pr_debug("[WSM] Issue unjoin command due to tx deauth.\n"); - wsm_lock_tx_async(priv); - if (queue_work(priv->workqueue, - &priv->unjoin_work) <= 0) - wsm_unlock_tx(priv); - } else if (ieee80211_has_protected(fctl) && - tx_info->control.hw_key && - tx_info->control.hw_key->keyidx != priv->wep_default_key_id && - (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 || - tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) { - action = do_wep; - } - } - - switch (action) { - case do_probe: - /* An interesting FW "feature". Device filters probe responses. - * The easiest way to get it back is to convert - * probe request into WSM start_scan command. - */ - pr_debug("[WSM] Convert probe request to scan.\n"); - wsm_lock_tx_async(priv); - priv->pending_frame_id = wsm->packet_id; - if (queue_delayed_work(priv->workqueue, - &priv->scan.probe_work, 0) <= 0) - wsm_unlock_tx(priv); - handled = true; - break; - case do_drop: - pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl); - BUG_ON(cw1200_queue_remove(queue, wsm->packet_id)); - handled = true; - break; - case do_wep: - pr_debug("[WSM] Issue set_default_wep_key.\n"); - wsm_lock_tx_async(priv); - priv->wep_default_key_id = tx_info->control.hw_key->keyidx; - priv->pending_frame_id = wsm->packet_id; - if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0) - wsm_unlock_tx(priv); - handled = true; - break; - case do_tx: - pr_debug("[WSM] Transmit frame.\n"); - break; - default: - /* Do nothing */ - break; - } - return handled; -} - -static int cw1200_get_prio_queue(struct cw1200_common *priv, - u32 link_id_map, int *total) -{ - static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) | - BIT(CW1200_LINK_ID_UAPSD); - struct wsm_edca_queue_params *edca; - unsigned score, best = -1; - int winner = -1; - int queued; - int i; - - /* search for a winner using edca params */ - for (i = 0; i < 4; ++i) { - queued = cw1200_queue_get_num_queued(&priv->tx_queue[i], - link_id_map); - if (!queued) - continue; - *total += queued; - edca = &priv->edca.params[i]; - score = ((edca->aifns + edca->cwmin) << 16) + - ((edca->cwmax - edca->cwmin) * - (get_random_int() & 0xFFFF)); - if (score < best && (winner < 0 || i != 3)) { - best = score; - winner = i; - } - } - - /* override winner if bursting */ - if (winner >= 0 && priv->tx_burst_idx >= 0 && - winner != priv->tx_burst_idx && - !cw1200_queue_get_num_queued( - &priv->tx_queue[winner], - link_id_map & urgent) && - cw1200_queue_get_num_queued( - &priv->tx_queue[priv->tx_burst_idx], - link_id_map)) - winner = priv->tx_burst_idx; - - return winner; -} - -static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, - struct cw1200_queue **queue_p, - u32 *tx_allowed_mask_p, - bool *more) -{ - int idx; - u32 tx_allowed_mask; - int total = 0; - - /* Search for a queue with multicast frames buffered */ - if (priv->tx_multicast) { - tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM); - idx = cw1200_get_prio_queue(priv, - tx_allowed_mask, &total); - if (idx >= 0) { - *more = total > 1; - goto found; - } - } - - /* Search for unicast traffic */ - tx_allowed_mask = ~priv->sta_asleep_mask; - tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD); - if (priv->sta_asleep_mask) { - tx_allowed_mask |= priv->pspoll_mask; - tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); - } else { - tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); - } - idx = cw1200_get_prio_queue(priv, - tx_allowed_mask, &total); - if (idx < 0) - return -ENOENT; - -found: - *queue_p = &priv->tx_queue[idx]; - *tx_allowed_mask_p = tx_allowed_mask; - return 0; -} - -int wsm_get_tx(struct cw1200_common *priv, u8 **data, - size_t *tx_len, int *burst) -{ - struct wsm_tx *wsm = NULL; - struct ieee80211_tx_info *tx_info; - struct cw1200_queue *queue = NULL; - int queue_num; - u32 tx_allowed_mask = 0; - const struct cw1200_txpriv *txpriv = NULL; - int count = 0; - - /* More is used only for broadcasts. */ - bool more = false; - - if (priv->wsm_cmd.ptr) { /* CMD request */ - ++count; - spin_lock(&priv->wsm_cmd.lock); - BUG_ON(!priv->wsm_cmd.ptr); - *data = priv->wsm_cmd.ptr; - *tx_len = priv->wsm_cmd.len; - *burst = 1; - spin_unlock(&priv->wsm_cmd.lock); - } else { - for (;;) { - int ret; - - if (atomic_add_return(0, &priv->tx_lock)) - break; - - spin_lock_bh(&priv->ps_state_lock); - - ret = wsm_get_tx_queue_and_mask(priv, &queue, - &tx_allowed_mask, &more); - queue_num = queue - priv->tx_queue; - - if (priv->buffered_multicasts && - (ret || !more) && - (priv->tx_multicast || !priv->sta_asleep_mask)) { - priv->buffered_multicasts = false; - if (priv->tx_multicast) { - priv->tx_multicast = false; - queue_work(priv->workqueue, - &priv->multicast_stop_work); - } - } - - spin_unlock_bh(&priv->ps_state_lock); - - if (ret) - break; - - if (cw1200_queue_get(queue, - tx_allowed_mask, - &wsm, &tx_info, &txpriv)) - continue; - - if (wsm_handle_tx_data(priv, wsm, - tx_info, txpriv, queue)) - continue; /* Handled by WSM */ - - wsm->hdr.id &= __cpu_to_le16( - ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); - wsm->hdr.id |= cpu_to_le16( - WSM_TX_LINK_ID(txpriv->raw_link_id)); - priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); - - *data = (u8 *)wsm; - *tx_len = __le16_to_cpu(wsm->hdr.len); - - /* allow bursting if txop is set */ - if (priv->edca.params[queue_num].txop_limit) - *burst = min(*burst, - (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1); - else - *burst = 1; - - /* store index of bursting queue */ - if (*burst > 1) - priv->tx_burst_idx = queue_num; - else - priv->tx_burst_idx = -1; - - if (more) { - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *) - &((u8 *)wsm)[txpriv->offset]; - /* more buffered multicast/broadcast frames - * ==> set MoreData flag in IEEE 802.11 header - * to inform PS STAs - */ - hdr->frame_control |= - cpu_to_le16(IEEE80211_FCTL_MOREDATA); - } - - pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n", - 0x0004, *tx_len, *data, - wsm->more ? 'M' : ' '); - ++count; - break; - } - } - - return count; -} - -void wsm_txed(struct cw1200_common *priv, u8 *data) -{ - if (data == priv->wsm_cmd.ptr) { - spin_lock(&priv->wsm_cmd.lock); - priv->wsm_cmd.ptr = NULL; - spin_unlock(&priv->wsm_cmd.lock); - } -} - -/* ******************************************************************** */ -/* WSM buffer */ - -void wsm_buf_init(struct wsm_buf *buf) -{ - BUG_ON(buf->begin); - buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); - buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin; - wsm_buf_reset(buf); -} - -void wsm_buf_deinit(struct wsm_buf *buf) -{ - kfree(buf->begin); - buf->begin = buf->data = buf->end = NULL; -} - -static void wsm_buf_reset(struct wsm_buf *buf) -{ - if (buf->begin) { - buf->data = &buf->begin[4]; - *(u32 *)buf->begin = 0; - } else { - buf->data = buf->begin; - } -} - -static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) -{ - size_t pos = buf->data - buf->begin; - size_t size = pos + extra_size; - - size = round_up(size, FWLOAD_BLOCK_SIZE); - - buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA); - if (buf->begin) { - buf->data = &buf->begin[pos]; - buf->end = &buf->begin[size]; - return 0; - } else { - buf->end = buf->data = buf->begin; - return -ENOMEM; - } -} diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/cw1200/wsm.h deleted file mode 100644 index 48086e849515..000000000000 --- a/drivers/net/wireless/cw1200/wsm.h +++ /dev/null @@ -1,1870 +0,0 @@ -/* - * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * Based on CW1200 UMAC WSM API, which is - * Copyright (C) ST-Ericsson SA 2010 - * Author: Stewart Mathers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef CW1200_WSM_H_INCLUDED -#define CW1200_WSM_H_INCLUDED - -#include - -struct cw1200_common; - -/* Bands */ -/* Radio band 2.412 -2.484 GHz. */ -#define WSM_PHY_BAND_2_4G (0) - -/* Radio band 4.9375-5.8250 GHz. */ -#define WSM_PHY_BAND_5G (1) - -/* Transmit rates */ -/* 1 Mbps ERP-DSSS */ -#define WSM_TRANSMIT_RATE_1 (0) - -/* 2 Mbps ERP-DSSS */ -#define WSM_TRANSMIT_RATE_2 (1) - -/* 5.5 Mbps ERP-CCK */ -#define WSM_TRANSMIT_RATE_5 (2) - -/* 11 Mbps ERP-CCK */ -#define WSM_TRANSMIT_RATE_11 (3) - -/* 22 Mbps ERP-PBCC (Not supported) */ -/* #define WSM_TRANSMIT_RATE_22 (4) */ - -/* 33 Mbps ERP-PBCC (Not supported) */ -/* #define WSM_TRANSMIT_RATE_33 (5) */ - -/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */ -#define WSM_TRANSMIT_RATE_6 (6) - -/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */ -#define WSM_TRANSMIT_RATE_9 (7) - -/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */ -#define WSM_TRANSMIT_RATE_12 (8) - -/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */ -#define WSM_TRANSMIT_RATE_18 (9) - -/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */ -#define WSM_TRANSMIT_RATE_24 (10) - -/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */ -#define WSM_TRANSMIT_RATE_36 (11) - -/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */ -#define WSM_TRANSMIT_RATE_48 (12) - -/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */ -#define WSM_TRANSMIT_RATE_54 (13) - -/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */ -#define WSM_TRANSMIT_RATE_HT_6 (14) - -/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */ -#define WSM_TRANSMIT_RATE_HT_13 (15) - -/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */ -#define WSM_TRANSMIT_RATE_HT_19 (16) - -/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */ -#define WSM_TRANSMIT_RATE_HT_26 (17) - -/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */ -#define WSM_TRANSMIT_RATE_HT_39 (18) - -/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */ -#define WSM_TRANSMIT_RATE_HT_52 (19) - -/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */ -#define WSM_TRANSMIT_RATE_HT_58 (20) - -/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */ -#define WSM_TRANSMIT_RATE_HT_65 (21) - -/* Scan types */ -/* Foreground scan */ -#define WSM_SCAN_TYPE_FOREGROUND (0) - -/* Background scan */ -#define WSM_SCAN_TYPE_BACKGROUND (1) - -/* Auto scan */ -#define WSM_SCAN_TYPE_AUTO (2) - -/* Scan flags */ -/* Forced background scan means if the station cannot */ -/* enter the power-save mode, it shall force to perform a */ -/* background scan. Only valid when ScanType is */ -/* background scan. */ -#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0)) - -/* The WLAN device scans one channel at a time so */ -/* that disturbance to the data traffic is minimized. */ -#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1)) - -/* Preamble Type. Long if not set. */ -#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2)) - -/* 11n Tx Mode. Mixed if not set. */ -#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3)) - -/* Scan constraints */ -/* Maximum number of channels to be scanned. */ -#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48) - -/* The maximum number of SSIDs that the device can scan for. */ -#define WSM_SCAN_MAX_NUM_OF_SSIDS (2) - -/* Power management modes */ -/* 802.11 Active mode */ -#define WSM_PSM_ACTIVE (0) - -/* 802.11 PS mode */ -#define WSM_PSM_PS BIT(0) - -/* Fast Power Save bit */ -#define WSM_PSM_FAST_PS_FLAG BIT(7) - -/* Dynamic aka Fast power save */ -#define WSM_PSM_FAST_PS (BIT(0) | BIT(7)) - -/* Undetermined */ -/* Note : Undetermined status is reported when the */ -/* NULL data frame used to advertise the PM mode to */ -/* the AP at Pre or Post Background Scan is not Acknowledged */ -#define WSM_PSM_UNKNOWN BIT(1) - -/* Queue IDs */ -/* best effort/legacy */ -#define WSM_QUEUE_BEST_EFFORT (0) - -/* background */ -#define WSM_QUEUE_BACKGROUND (1) - -/* video */ -#define WSM_QUEUE_VIDEO (2) - -/* voice */ -#define WSM_QUEUE_VOICE (3) - -/* HT TX parameters */ -/* Non-HT */ -#define WSM_HT_TX_NON_HT (0) - -/* Mixed format */ -#define WSM_HT_TX_MIXED (1) - -/* Greenfield format */ -#define WSM_HT_TX_GREENFIELD (2) - -/* STBC allowed */ -#define WSM_HT_TX_STBC (BIT(7)) - -/* EPTA prioirty flags for BT Coex */ -/* default epta priority */ -#define WSM_EPTA_PRIORITY_DEFAULT 4 -/* use for normal data */ -#define WSM_EPTA_PRIORITY_DATA 4 -/* use for connect/disconnect/roaming*/ -#define WSM_EPTA_PRIORITY_MGT 5 -/* use for action frames */ -#define WSM_EPTA_PRIORITY_ACTION 5 -/* use for AC_VI data */ -#define WSM_EPTA_PRIORITY_VIDEO 5 -/* use for AC_VO data */ -#define WSM_EPTA_PRIORITY_VOICE 6 -/* use for EAPOL exchange */ -#define WSM_EPTA_PRIORITY_EAPOL 7 - -/* TX status */ -/* Frame was sent aggregated */ -/* Only valid for WSM_SUCCESS status. */ -#define WSM_TX_STATUS_AGGREGATION (BIT(0)) - -/* Host should requeue this frame later. */ -/* Valid only when status is WSM_REQUEUE. */ -#define WSM_TX_STATUS_REQUEUE (BIT(1)) - -/* Normal Ack */ -#define WSM_TX_STATUS_NORMAL_ACK (0<<2) - -/* No Ack */ -#define WSM_TX_STATUS_NO_ACK (1<<2) - -/* No explicit acknowledgement */ -#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2) - -/* Block Ack */ -/* Only valid for WSM_SUCCESS status. */ -#define WSM_TX_STATUS_BLOCK_ACK (3<<2) - -/* RX status */ -/* Unencrypted */ -#define WSM_RX_STATUS_UNENCRYPTED (0<<0) - -/* WEP */ -#define WSM_RX_STATUS_WEP (1<<0) - -/* TKIP */ -#define WSM_RX_STATUS_TKIP (2<<0) - -/* AES */ -#define WSM_RX_STATUS_AES (3<<0) - -/* WAPI */ -#define WSM_RX_STATUS_WAPI (4<<0) - -/* Macro to fetch encryption subfield. */ -#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07) - -/* Frame was part of an aggregation */ -#define WSM_RX_STATUS_AGGREGATE (BIT(3)) - -/* Frame was first in the aggregation */ -#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4)) - -/* Frame was last in the aggregation */ -#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5)) - -/* Indicates a defragmented frame */ -#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6)) - -/* Indicates a Beacon frame */ -#define WSM_RX_STATUS_BEACON (BIT(7)) - -/* Indicates STA bit beacon TIM field */ -#define WSM_RX_STATUS_TIM (BIT(8)) - -/* Indicates Beacon frame's virtual bitmap contains multicast bit */ -#define WSM_RX_STATUS_MULTICAST (BIT(9)) - -/* Indicates frame contains a matching SSID */ -#define WSM_RX_STATUS_MATCHING_SSID (BIT(10)) - -/* Indicates frame contains a matching BSSI */ -#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11)) - -/* Indicates More bit set in Framectl field */ -#define WSM_RX_STATUS_MORE_DATA (BIT(12)) - -/* Indicates frame received during a measurement process */ -#define WSM_RX_STATUS_MEASUREMENT (BIT(13)) - -/* Indicates frame received as an HT packet */ -#define WSM_RX_STATUS_HT (BIT(14)) - -/* Indicates frame received with STBC */ -#define WSM_RX_STATUS_STBC (BIT(15)) - -/* Indicates Address 1 field matches dot11StationId */ -#define WSM_RX_STATUS_ADDRESS1 (BIT(16)) - -/* Indicates Group address present in the Address 1 field */ -#define WSM_RX_STATUS_GROUP (BIT(17)) - -/* Indicates Broadcast address present in the Address 1 field */ -#define WSM_RX_STATUS_BROADCAST (BIT(18)) - -/* Indicates group key used with encrypted frames */ -#define WSM_RX_STATUS_GROUP_KEY (BIT(19)) - -/* Macro to fetch encryption key index. */ -#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F) - -/* Indicates TSF inclusion after 802.11 frame body */ -#define WSM_RX_STATUS_TSF_INCLUDED (BIT(24)) - -/* Frame Control field starts at Frame offset + 2 */ -#define WSM_TX_2BYTES_SHIFT (BIT(7)) - -/* Join mode */ -/* IBSS */ -#define WSM_JOIN_MODE_IBSS (0) - -/* BSS */ -#define WSM_JOIN_MODE_BSS (1) - -/* PLCP preamble type */ -/* For long preamble */ -#define WSM_JOIN_PREAMBLE_LONG (0) - -/* For short preamble (Long for 1Mbps) */ -#define WSM_JOIN_PREAMBLE_SHORT (1) - -/* For short preamble (Long for 1 and 2Mbps) */ -#define WSM_JOIN_PREAMBLE_SHORT_2 (2) - -/* Join flags */ -/* Unsynchronized */ -#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0) -/* The BSS owner is a P2P GO */ -#define WSM_JOIN_FLAGS_P2P_GO BIT(1) -/* Force to join BSS with the BSSID and the - * SSID specified without waiting for beacons. The - * ProbeForJoin parameter is ignored. - */ -#define WSM_JOIN_FLAGS_FORCE BIT(2) -/* Give probe request/response higher - * priority over the BT traffic - */ -#define WSM_JOIN_FLAGS_PRIO BIT(3) -/* Issue immediate join confirmation and use - * join complete to notify about completion - */ -#define WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND BIT(5) - -/* Key types */ -#define WSM_KEY_TYPE_WEP_DEFAULT (0) -#define WSM_KEY_TYPE_WEP_PAIRWISE (1) -#define WSM_KEY_TYPE_TKIP_GROUP (2) -#define WSM_KEY_TYPE_TKIP_PAIRWISE (3) -#define WSM_KEY_TYPE_AES_GROUP (4) -#define WSM_KEY_TYPE_AES_PAIRWISE (5) -#define WSM_KEY_TYPE_WAPI_GROUP (6) -#define WSM_KEY_TYPE_WAPI_PAIRWISE (7) - -/* Key indexes */ -#define WSM_KEY_MAX_INDEX (10) - -/* ACK policy */ -#define WSM_ACK_POLICY_NORMAL (0) -#define WSM_ACK_POLICY_NO_ACK (1) - -/* Start modes */ -#define WSM_START_MODE_AP (0) /* Mini AP */ -#define WSM_START_MODE_P2P_GO (1) /* P2P GO */ -#define WSM_START_MODE_P2P_DEV (2) /* P2P device */ - -/* SetAssociationMode MIB flags */ -#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0)) -#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1)) -#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2)) -#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3)) -#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4)) - -/* RcpiRssiThreshold MIB flags */ -#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0)) -#define WSM_RCPI_RSSI_USE_RSSI (BIT(1)) -#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2)) -#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3)) - -/* Update-ie constants */ -#define WSM_UPDATE_IE_BEACON (BIT(0)) -#define WSM_UPDATE_IE_PROBE_RESP (BIT(1)) -#define WSM_UPDATE_IE_PROBE_REQ (BIT(2)) - -/* WSM events */ -/* Error */ -#define WSM_EVENT_ERROR (0) - -/* BSS lost */ -#define WSM_EVENT_BSS_LOST (1) - -/* BSS regained */ -#define WSM_EVENT_BSS_REGAINED (2) - -/* Radar detected */ -#define WSM_EVENT_RADAR_DETECTED (3) - -/* RCPI or RSSI threshold triggered */ -#define WSM_EVENT_RCPI_RSSI (4) - -/* BT inactive */ -#define WSM_EVENT_BT_INACTIVE (5) - -/* BT active */ -#define WSM_EVENT_BT_ACTIVE (6) - -/* MIB IDs */ -/* 4.1 dot11StationId */ -#define WSM_MIB_ID_DOT11_STATION_ID 0x0000 - -/* 4.2 dot11MaxtransmitMsduLifeTime */ -#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001 - -/* 4.3 dot11MaxReceiveLifeTime */ -#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002 - -/* 4.4 dot11SlotTime */ -#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003 - -/* 4.5 dot11GroupAddressesTable */ -#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004 -#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8 - -/* 4.6 dot11WepDefaultKeyId */ -#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005 - -/* 4.7 dot11CurrentTxPowerLevel */ -#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006 - -/* 4.8 dot11RTSThreshold */ -#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007 - -/* 4.9 NonErpProtection */ -#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000 - -/* 4.10 ArpIpAddressesTable */ -#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001 -#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1 - -/* 4.11 TemplateFrame */ -#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002 - -/* 4.12 RxFilter */ -#define WSM_MIB_ID_RX_FILTER 0x1003 - -/* 4.13 BeaconFilterTable */ -#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004 - -/* 4.14 BeaconFilterEnable */ -#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005 - -/* 4.15 OperationalPowerMode */ -#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006 - -/* 4.16 BeaconWakeUpPeriod */ -#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007 - -/* 4.17 RcpiRssiThreshold */ -#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009 - -/* 4.18 StatisticsTable */ -#define WSM_MIB_ID_STATISTICS_TABLE 0x100A - -/* 4.19 IbssPsConfig */ -#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B - -/* 4.20 CountersTable */ -#define WSM_MIB_ID_COUNTERS_TABLE 0x100C - -/* 4.21 BlockAckPolicy */ -#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E - -/* 4.22 OverrideInternalTxRate */ -#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F - -/* 4.23 SetAssociationMode */ -#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010 - -/* 4.24 UpdateEptaConfigData */ -#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011 - -/* 4.25 SelectCcaMethod */ -#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012 - -/* 4.26 SetUpasdInformation */ -#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013 - -/* 4.27 SetAutoCalibrationMode WBF00004073 */ -#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015 - -/* 4.28 SetTxRateRetryPolicy */ -#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016 - -/* 4.29 SetHostMessageTypeFilter */ -#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017 - -/* 4.30 P2PFindInfo */ -#define WSM_MIB_ID_P2P_FIND_INFO 0x1018 - -/* 4.31 P2PPsModeInfo */ -#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019 - -/* 4.32 SetEtherTypeDataFrameFilter */ -#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A - -/* 4.33 SetUDPPortDataFrameFilter */ -#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B - -/* 4.34 SetMagicDataFrameFilter */ -#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C - -/* 4.35 P2PDeviceInfo */ -#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D - -/* 4.36 SetWCDMABand */ -#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E - -/* 4.37 GroupTxSequenceCounter */ -#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F - -/* 4.38 ProtectedMgmtPolicy */ -#define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020 - -/* 4.39 SetHtProtection */ -#define WSM_MIB_ID_SET_HT_PROTECTION 0x1021 - -/* 4.40 GPIO Command */ -#define WSM_MIB_ID_GPIO_COMMAND 0x1022 - -/* 4.41 TSF Counter Value */ -#define WSM_MIB_ID_TSF_COUNTER 0x1023 - -/* Test Purposes Only */ -#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D - -/* 4.42 UseMultiTxConfMessage */ -#define WSM_MIB_USE_MULTI_TX_CONF 0x1024 - -/* 4.43 Keep-alive period */ -#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025 - -/* 4.44 Disable BSSID filter */ -#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026 - -/* Frame template types */ -#define WSM_FRAME_TYPE_PROBE_REQUEST (0) -#define WSM_FRAME_TYPE_BEACON (1) -#define WSM_FRAME_TYPE_NULL (2) -#define WSM_FRAME_TYPE_QOS_NULL (3) -#define WSM_FRAME_TYPE_PS_POLL (4) -#define WSM_FRAME_TYPE_PROBE_RESPONSE (5) - -#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */ - -/* Status */ -/* The WSM firmware has completed a request */ -/* successfully. */ -#define WSM_STATUS_SUCCESS (0) - -/* This is a generic failure code if other error codes do */ -/* not apply. */ -#define WSM_STATUS_FAILURE (1) - -/* A request contains one or more invalid parameters. */ -#define WSM_INVALID_PARAMETER (2) - -/* The request cannot perform because the device is in */ -/* an inappropriate mode. */ -#define WSM_ACCESS_DENIED (3) - -/* The frame received includes a decryption error. */ -#define WSM_STATUS_DECRYPTFAILURE (4) - -/* A MIC failure is detected in the received packets. */ -#define WSM_STATUS_MICFAILURE (5) - -/* The transmit request failed due to retry limit being */ -/* exceeded. */ -#define WSM_STATUS_RETRY_EXCEEDED (6) - -/* The transmit request failed due to MSDU life time */ -/* being exceeded. */ -#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7) - -/* The link to the AP is lost. */ -#define WSM_STATUS_LINK_LOST (8) - -/* No key was found for the encrypted frame */ -#define WSM_STATUS_NO_KEY_FOUND (9) - -/* Jammer was detected when transmitting this frame */ -#define WSM_STATUS_JAMMER_DETECTED (10) - -/* The message should be requeued later. */ -/* This is applicable only to Transmit */ -#define WSM_REQUEUE (11) - -/* Advanced filtering options */ -#define WSM_MAX_FILTER_ELEMENTS (4) - -#define WSM_FILTER_ACTION_IGNORE (0) -#define WSM_FILTER_ACTION_FILTER_IN (1) -#define WSM_FILTER_ACTION_FILTER_OUT (2) - -#define WSM_FILTER_PORT_TYPE_DST (0) -#define WSM_FILTER_PORT_TYPE_SRC (1) - -/* Actual header of WSM messages */ -struct wsm_hdr { - __le16 len; - __le16 id; -}; - -#define WSM_TX_SEQ_MAX (7) -#define WSM_TX_SEQ(seq) \ - ((seq & WSM_TX_SEQ_MAX) << 13) -#define WSM_TX_LINK_ID_MAX (0x0F) -#define WSM_TX_LINK_ID(link_id) \ - ((link_id & WSM_TX_LINK_ID_MAX) << 6) - -#define MAX_BEACON_SKIP_TIME_MS 1000 - -#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2) - -/* ******************************************************************** */ -/* WSM capability */ - -#define WSM_STARTUP_IND_ID 0x0801 - -struct wsm_startup_ind { - u16 input_buffers; - u16 input_buffer_size; - u16 status; - u16 hw_id; - u16 hw_subid; - u16 fw_cap; - u16 fw_type; - u16 fw_api; - u16 fw_build; - u16 fw_ver; - char fw_label[128]; - u32 config[4]; -}; - -/* ******************************************************************** */ -/* WSM commands */ - -/* 3.1 */ -#define WSM_CONFIGURATION_REQ_ID 0x0009 -#define WSM_CONFIGURATION_RESP_ID 0x0409 - -struct wsm_tx_power_range { - int min_power_level; - int max_power_level; - u32 stepping; -}; - -struct wsm_configuration { - /* [in] */ u32 dot11MaxTransmitMsduLifeTime; - /* [in] */ u32 dot11MaxReceiveLifeTime; - /* [in] */ u32 dot11RtsThreshold; - /* [in, out] */ u8 *dot11StationId; - /* [in] */ const void *dpdData; - /* [in] */ size_t dpdData_size; - /* [out] */ u8 dot11FrequencyBandsSupported; - /* [out] */ u32 supportedRateMask; - /* [out] */ struct wsm_tx_power_range txPowerRange[2]; -}; - -int wsm_configuration(struct cw1200_common *priv, - struct wsm_configuration *arg); - -/* 3.3 */ -#define WSM_RESET_REQ_ID 0x000A -#define WSM_RESET_RESP_ID 0x040A -struct wsm_reset { - /* [in] */ int link_id; - /* [in] */ bool reset_statistics; -}; - -int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg); - -/* 3.5 */ -#define WSM_READ_MIB_REQ_ID 0x0005 -#define WSM_READ_MIB_RESP_ID 0x0405 -int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *buf, - size_t buf_size); - -/* 3.7 */ -#define WSM_WRITE_MIB_REQ_ID 0x0006 -#define WSM_WRITE_MIB_RESP_ID 0x0406 -int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *buf, - size_t buf_size); - -/* 3.9 */ -#define WSM_START_SCAN_REQ_ID 0x0007 -#define WSM_START_SCAN_RESP_ID 0x0407 - -struct wsm_ssid { - u8 ssid[32]; - u32 length; -}; - -struct wsm_scan_ch { - u16 number; - u32 min_chan_time; - u32 max_chan_time; - u32 tx_power_level; -}; - -struct wsm_scan { - /* WSM_PHY_BAND_... */ - u8 band; - - /* WSM_SCAN_TYPE_... */ - u8 type; - - /* WSM_SCAN_FLAG_... */ - u8 flags; - - /* WSM_TRANSMIT_RATE_... */ - u8 max_tx_rate; - - /* Interval period in TUs that the device shall the re- */ - /* execute the requested scan. Max value supported by the device */ - /* is 256s. */ - u32 auto_scan_interval; - - /* Number of probe requests (per SSID) sent to one (1) */ - /* channel. Zero (0) means that none is send, which */ - /* means that a passive scan is to be done. Value */ - /* greater than zero (0) means that an active scan is to */ - /* be done. */ - u32 num_probes; - - /* Number of channels to be scanned. */ - /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */ - u8 num_channels; - - /* Number of SSID provided in the scan command (this */ - /* is zero (0) in broadcast scan) */ - /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */ - u8 num_ssids; - - /* The delay time (in microseconds) period */ - /* before sending a probe-request. */ - u8 probe_delay; - - /* SSIDs to be scanned [numOfSSIDs]; */ - struct wsm_ssid *ssids; - - /* Channels to be scanned [numOfChannels]; */ - struct wsm_scan_ch *ch; -}; - -int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg); - -/* 3.11 */ -#define WSM_STOP_SCAN_REQ_ID 0x0008 -#define WSM_STOP_SCAN_RESP_ID 0x0408 -int wsm_stop_scan(struct cw1200_common *priv); - -/* 3.13 */ -#define WSM_SCAN_COMPLETE_IND_ID 0x0806 -struct wsm_scan_complete { - /* WSM_STATUS_... */ - u32 status; - - /* WSM_PSM_... */ - u8 psm; - - /* Number of channels that the scan operation completed. */ - u8 num_channels; -}; - -/* 3.14 */ -#define WSM_TX_CONFIRM_IND_ID 0x0404 -#define WSM_MULTI_TX_CONFIRM_ID 0x041E - -struct wsm_tx_confirm { - /* Packet identifier used in wsm_tx. */ - u32 packet_id; - - /* WSM_STATUS_... */ - u32 status; - - /* WSM_TRANSMIT_RATE_... */ - u8 tx_rate; - - /* The number of times the frame was transmitted */ - /* without receiving an acknowledgement. */ - u8 ack_failures; - - /* WSM_TX_STATUS_... */ - u16 flags; - - /* The total time in microseconds that the frame spent in */ - /* the WLAN device before transmission as completed. */ - u32 media_delay; - - /* The total time in microseconds that the frame spent in */ - /* the WLAN device before transmission was started. */ - u32 tx_queue_delay; -}; - -/* 3.15 */ -typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv, - struct wsm_tx_confirm *arg); - -/* Note that ideology of wsm_tx struct is different against the rest of - * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input - * argument for WSM call, but a prepared bytestream to be sent to firmware. - * It is filled partly in cw1200_tx, partly in low-level WSM code. - * Please pay attention once again: ideology is different. - * - * Legend: - * - [in]: cw1200_tx must fill this field. - * - [wsm]: the field is filled by low-level WSM. - */ -struct wsm_tx { - /* common WSM header */ - struct wsm_hdr hdr; - - /* Packet identifier that meant to be used in completion. */ - u32 packet_id; /* Note this is actually a cookie */ - - /* WSM_TRANSMIT_RATE_... */ - u8 max_tx_rate; - - /* WSM_QUEUE_... */ - u8 queue_id; - - /* True: another packet is pending on the host for transmission. */ - u8 more; - - /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */ - /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */ - /* Bits 3:1 - PTA Priority */ - /* Bits 6:4 - Tx Rate Retry Policy */ - /* Bit 7 - Reserved */ - u8 flags; - - /* Should be 0. */ - u32 reserved; - - /* The elapsed time in TUs, after the initial transmission */ - /* of an MSDU, after which further attempts to transmit */ - /* the MSDU shall be terminated. Overrides the global */ - /* dot11MaxTransmitMsduLifeTime setting [optional] */ - /* Device will set the default value if this is 0. */ - __le32 expire_time; - - /* WSM_HT_TX_... */ - __le32 ht_tx_parameters; -} __packed; - -/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */ -#define WSM_TX_EXTRA_HEADROOM (28) - -/* 3.16 */ -#define WSM_RECEIVE_IND_ID 0x0804 - -struct wsm_rx { - /* WSM_STATUS_... */ - u32 status; - - /* Specifies the channel of the received packet. */ - u16 channel_number; - - /* WSM_TRANSMIT_RATE_... */ - u8 rx_rate; - - /* This value is expressed in signed Q8.0 format for */ - /* RSSI and unsigned Q7.1 format for RCPI. */ - u8 rcpi_rssi; - - /* WSM_RX_STATUS_... */ - u32 flags; -}; - -/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */ -#define WSM_RX_EXTRA_HEADROOM (16) - -typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg, - struct sk_buff **skb_p); - -/* 3.17 */ -struct wsm_event { - /* WSM_STATUS_... */ - /* [out] */ u32 id; - - /* Indication parameters. */ - /* For error indication, this shall be a 32-bit WSM status. */ - /* For RCPI or RSSI indication, this should be an 8-bit */ - /* RCPI or RSSI value. */ - /* [out] */ u32 data; -}; - -struct cw1200_wsm_event { - struct list_head link; - struct wsm_event evt; -}; - -/* 3.18 - 3.22 */ -/* Measurement. Skipped for now. Irrelevent. */ - -typedef void (*wsm_event_cb) (struct cw1200_common *priv, - struct wsm_event *arg); - -/* 3.23 */ -#define WSM_JOIN_REQ_ID 0x000B -#define WSM_JOIN_RESP_ID 0x040B - -struct wsm_join { - /* WSM_JOIN_MODE_... */ - u8 mode; - - /* WSM_PHY_BAND_... */ - u8 band; - - /* Specifies the channel number to join. The channel */ - /* number will be mapped to an actual frequency */ - /* according to the band */ - u16 channel_number; - - /* Specifies the BSSID of the BSS or IBSS to be joined */ - /* or the IBSS to be started. */ - u8 bssid[6]; - - /* ATIM window of IBSS */ - /* When ATIM window is zero the initiated IBSS does */ - /* not support power saving. */ - u16 atim_window; - - /* WSM_JOIN_PREAMBLE_... */ - u8 preamble_type; - - /* Specifies if a probe request should be send with the */ - /* specified SSID when joining to the network. */ - u8 probe_for_join; - - /* DTIM Period (In multiples of beacon interval) */ - u8 dtim_period; - - /* WSM_JOIN_FLAGS_... */ - u8 flags; - - /* Length of the SSID */ - u32 ssid_len; - - /* Specifies the SSID of the IBSS to join or start */ - u8 ssid[32]; - - /* Specifies the time between TBTTs in TUs */ - u32 beacon_interval; - - /* A bit mask that defines the BSS basic rate set. */ - u32 basic_rate_set; -}; - -struct wsm_join_cnf { - u32 status; - - /* Minimum transmission power level in units of 0.1dBm */ - u32 min_power_level; - - /* Maximum transmission power level in units of 0.1dBm */ - u32 max_power_level; -}; - -int wsm_join(struct cw1200_common *priv, struct wsm_join *arg); - -/* 3.24 */ -struct wsm_join_complete { - /* WSM_STATUS_... */ - u32 status; -}; - -/* 3.25 */ -#define WSM_SET_PM_REQ_ID 0x0010 -#define WSM_SET_PM_RESP_ID 0x0410 -struct wsm_set_pm { - /* WSM_PSM_... */ - u8 mode; - - /* in unit of 500us; 0 to use default */ - u8 fast_psm_idle_period; - - /* in unit of 500us; 0 to use default */ - u8 ap_psm_change_period; - - /* in unit of 500us; 0 to disable auto-pspoll */ - u8 min_auto_pspoll_period; -}; - -int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); - -/* 3.27 */ -struct wsm_set_pm_complete { - u8 psm; /* WSM_PSM_... */ -}; - -/* 3.28 */ -#define WSM_SET_BSS_PARAMS_REQ_ID 0x0011 -#define WSM_SET_BSS_PARAMS_RESP_ID 0x0411 -struct wsm_set_bss_params { - /* This resets the beacon loss counters only */ - u8 reset_beacon_loss; - - /* The number of lost consecutive beacons after which */ - /* the WLAN device should indicate the BSS-Lost event */ - /* to the WLAN host driver. */ - u8 beacon_lost_count; - - /* The AID received during the association process. */ - u16 aid; - - /* The operational rate set mask */ - u32 operational_rate_set; -}; - -int wsm_set_bss_params(struct cw1200_common *priv, - const struct wsm_set_bss_params *arg); - -/* 3.30 */ -#define WSM_ADD_KEY_REQ_ID 0x000C -#define WSM_ADD_KEY_RESP_ID 0x040C -struct wsm_add_key { - u8 type; /* WSM_KEY_TYPE_... */ - u8 index; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */ - u16 reserved; - union { - struct { - u8 peer[6]; /* MAC address of the peer station */ - u8 reserved; - u8 keylen; /* Key length in bytes */ - u8 keydata[16]; /* Key data */ - } __packed wep_pairwise; - struct { - u8 keyid; /* Unique per key identifier (0..3) */ - u8 keylen; /* Key length in bytes */ - u16 reserved; - u8 keydata[16]; /* Key data */ - } __packed wep_group; - struct { - u8 peer[6]; /* MAC address of the peer station */ - u16 reserved; - u8 keydata[16]; /* TKIP key data */ - u8 rx_mic_key[8]; /* Rx MIC key */ - u8 tx_mic_key[8]; /* Tx MIC key */ - } __packed tkip_pairwise; - struct { - u8 keydata[16]; /* TKIP key data */ - u8 rx_mic_key[8]; /* Rx MIC key */ - u8 keyid; /* Key ID */ - u8 reserved[3]; - u8 rx_seqnum[8]; /* Receive Sequence Counter */ - } __packed tkip_group; - struct { - u8 peer[6]; /* MAC address of the peer station */ - u16 reserved; - u8 keydata[16]; /* AES key data */ - } __packed aes_pairwise; - struct { - u8 keydata[16]; /* AES key data */ - u8 keyid; /* Key ID */ - u8 reserved[3]; - u8 rx_seqnum[8]; /* Receive Sequence Counter */ - } __packed aes_group; - struct { - u8 peer[6]; /* MAC address of the peer station */ - u8 keyid; /* Key ID */ - u8 reserved; - u8 keydata[16]; /* WAPI key data */ - u8 mic_key[16]; /* MIC key data */ - } __packed wapi_pairwise; - struct { - u8 keydata[16]; /* WAPI key data */ - u8 mic_key[16]; /* MIC key data */ - u8 keyid; /* Key ID */ - u8 reserved[3]; - } __packed wapi_group; - } __packed; -} __packed; - -int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg); - -/* 3.32 */ -#define WSM_REMOVE_KEY_REQ_ID 0x000D -#define WSM_REMOVE_KEY_RESP_ID 0x040D -struct wsm_remove_key { - u8 index; /* Key entry index : 0-10 */ -}; - -int wsm_remove_key(struct cw1200_common *priv, - const struct wsm_remove_key *arg); - -/* 3.34 */ -struct wsm_set_tx_queue_params { - /* WSM_ACK_POLICY_... */ - u8 ackPolicy; - - /* Medium Time of TSPEC (in 32us units) allowed per */ - /* One Second Averaging Period for this queue. */ - u16 allowedMediumTime; - - /* dot11MaxTransmitMsduLifetime to be used for the */ - /* specified queue. */ - u32 maxTransmitLifetime; -}; - -struct wsm_tx_queue_params { - /* NOTE: index is a linux queue id. */ - struct wsm_set_tx_queue_params params[4]; -}; - - -#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,\ - max_life_time) \ -do { \ - struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \ - p->ackPolicy = (ack_policy); \ - p->allowedMediumTime = (allowed_time); \ - p->maxTransmitLifetime = (max_life_time); \ -} while (0) - -int wsm_set_tx_queue_params(struct cw1200_common *priv, - const struct wsm_set_tx_queue_params *arg, u8 id); - -/* 3.36 */ -#define WSM_EDCA_PARAMS_REQ_ID 0x0013 -#define WSM_EDCA_PARAMS_RESP_ID 0x0413 -struct wsm_edca_queue_params { - /* CWmin (in slots) for the access class. */ - u16 cwmin; - - /* CWmax (in slots) for the access class. */ - u16 cwmax; - - /* AIFS (in slots) for the access class. */ - u16 aifns; - - /* TX OP Limit (in microseconds) for the access class. */ - u16 txop_limit; - - /* dot11MaxReceiveLifetime to be used for the specified */ - /* the access class. Overrides the global */ - /* dot11MaxReceiveLifetime value */ - u32 max_rx_lifetime; -}; - -struct wsm_edca_params { - /* NOTE: index is a linux queue id. */ - struct wsm_edca_queue_params params[4]; - bool uapsd_enable[4]; -}; - -#define TXOP_UNIT 32 -#define WSM_EDCA_SET(__edca, __queue, __aifs, __cw_min, __cw_max, __txop, __lifetime,\ - __uapsd) \ - do { \ - struct wsm_edca_queue_params *p = &(__edca)->params[__queue]; \ - p->cwmin = __cw_min; \ - p->cwmax = __cw_max; \ - p->aifns = __aifs; \ - p->txop_limit = ((__txop) * TXOP_UNIT); \ - p->max_rx_lifetime = __lifetime; \ - (__edca)->uapsd_enable[__queue] = (__uapsd); \ - } while (0) - -int wsm_set_edca_params(struct cw1200_common *priv, - const struct wsm_edca_params *arg); - -int wsm_set_uapsd_param(struct cw1200_common *priv, - const struct wsm_edca_params *arg); - -/* 3.38 */ -/* Set-System info. Skipped for now. Irrelevent. */ - -/* 3.40 */ -#define WSM_SWITCH_CHANNEL_REQ_ID 0x0016 -#define WSM_SWITCH_CHANNEL_RESP_ID 0x0416 - -struct wsm_switch_channel { - /* 1 - means the STA shall not transmit any further */ - /* frames until the channel switch has completed */ - u8 mode; - - /* Number of TBTTs until channel switch occurs. */ - /* 0 - indicates switch shall occur at any time */ - /* 1 - occurs immediately before the next TBTT */ - u8 switch_count; - - /* The new channel number to switch to. */ - /* Note this is defined as per section 2.7. */ - u16 channel_number; -}; - -int wsm_switch_channel(struct cw1200_common *priv, - const struct wsm_switch_channel *arg); - -typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv); - -#define WSM_START_REQ_ID 0x0017 -#define WSM_START_RESP_ID 0x0417 - -struct wsm_start { - /* WSM_START_MODE_... */ - /* [in] */ u8 mode; - - /* WSM_PHY_BAND_... */ - /* [in] */ u8 band; - - /* Channel number */ - /* [in] */ u16 channel_number; - - /* Client Traffic window in units of TU */ - /* Valid only when mode == ..._P2P */ - /* [in] */ u32 ct_window; - - /* Interval between two consecutive */ - /* beacon transmissions in TU. */ - /* [in] */ u32 beacon_interval; - - /* DTIM period in terms of beacon intervals */ - /* [in] */ u8 dtim_period; - - /* WSM_JOIN_PREAMBLE_... */ - /* [in] */ u8 preamble; - - /* The delay time (in microseconds) period */ - /* before sending a probe-request. */ - /* [in] */ u8 probe_delay; - - /* Length of the SSID */ - /* [in] */ u8 ssid_len; - - /* SSID of the BSS or P2P_GO to be started now. */ - /* [in] */ u8 ssid[32]; - - /* The basic supported rates for the MiniAP. */ - /* [in] */ u32 basic_rate_set; -}; - -int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg); - -#define WSM_BEACON_TRANSMIT_REQ_ID 0x0018 -#define WSM_BEACON_TRANSMIT_RESP_ID 0x0418 - -struct wsm_beacon_transmit { - /* 1: enable; 0: disable */ - /* [in] */ u8 enable_beaconing; -}; - -int wsm_beacon_transmit(struct cw1200_common *priv, - const struct wsm_beacon_transmit *arg); - -int wsm_start_find(struct cw1200_common *priv); - -int wsm_stop_find(struct cw1200_common *priv); - -typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status); - -struct wsm_suspend_resume { - /* See 3.52 */ - /* Link ID */ - /* [out] */ int link_id; - /* Stop sending further Tx requests down to device for this link */ - /* [out] */ bool stop; - /* Transmit multicast Frames */ - /* [out] */ bool multicast; - /* The AC on which Tx to be suspended /resumed. */ - /* This is applicable only for U-APSD */ - /* WSM_QUEUE_... */ - /* [out] */ int queue; -}; - -typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv, - struct wsm_suspend_resume *arg); - -/* 3.54 Update-IE request. */ -struct wsm_update_ie { - /* WSM_UPDATE_IE_... */ - /* [in] */ u16 what; - /* [in] */ u16 count; - /* [in] */ u8 *ies; - /* [in] */ size_t length; -}; - -int wsm_update_ie(struct cw1200_common *priv, - const struct wsm_update_ie *arg); - -/* 3.56 */ -struct wsm_map_link { - /* MAC address of the remote device */ - /* [in] */ u8 mac_addr[6]; - /* [in] */ u8 link_id; -}; - -int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg); - -/* ******************************************************************** */ -/* MIB shortcats */ - -static inline int wsm_set_output_power(struct cw1200_common *priv, - int power_level) -{ - __le32 val = __cpu_to_le32(power_level); - return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL, - &val, sizeof(val)); -} - -static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv, - unsigned dtim_interval, - unsigned listen_interval) -{ - struct { - u8 numBeaconPeriods; - u8 reserved; - __le16 listenInterval; - } val = { - dtim_interval, 0, __cpu_to_le16(listen_interval) - }; - - if (dtim_interval > 0xFF || listen_interval > 0xFFFF) - return -EINVAL; - else - return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD, - &val, sizeof(val)); -} - -struct wsm_rcpi_rssi_threshold { - u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */ - u8 lowerThreshold; - u8 upperThreshold; - u8 rollingAverageCount; -}; - -static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv, - struct wsm_rcpi_rssi_threshold *arg) -{ - return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg, - sizeof(*arg)); -} - -struct wsm_mib_counters_table { - __le32 plcp_errors; - __le32 fcs_errors; - __le32 tx_packets; - __le32 rx_packets; - __le32 rx_packet_errors; - __le32 rx_decryption_failures; - __le32 rx_mic_failures; - __le32 rx_no_key_failures; - __le32 tx_multicast_frames; - __le32 tx_frames_success; - __le32 tx_frame_failures; - __le32 tx_frames_retried; - __le32 tx_frames_multi_retried; - __le32 rx_frame_duplicates; - __le32 rts_success; - __le32 rts_failures; - __le32 ack_failures; - __le32 rx_multicast_frames; - __le32 rx_frames_success; - __le32 rx_cmac_icv_errors; - __le32 rx_cmac_replays; - __le32 rx_mgmt_ccmp_replays; -} __packed; - -static inline int wsm_get_counters_table(struct cw1200_common *priv, - struct wsm_mib_counters_table *arg) -{ - return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE, - arg, sizeof(*arg)); -} - -static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac) -{ - return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN); -} - -struct wsm_rx_filter { - bool promiscuous; - bool bssid; - bool fcs; - bool probeResponder; -}; - -static inline int wsm_set_rx_filter(struct cw1200_common *priv, - const struct wsm_rx_filter *arg) -{ - __le32 val = 0; - if (arg->promiscuous) - val |= __cpu_to_le32(BIT(0)); - if (arg->bssid) - val |= __cpu_to_le32(BIT(1)); - if (arg->fcs) - val |= __cpu_to_le32(BIT(2)); - if (arg->probeResponder) - val |= __cpu_to_le32(BIT(3)); - return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val)); -} - -int wsm_set_probe_responder(struct cw1200_common *priv, bool enable); - -#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0) -#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1) -#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2) - -struct wsm_beacon_filter_table_entry { - u8 ie_id; - u8 flags; - u8 oui[3]; - u8 match_data[3]; -} __packed; - -struct wsm_mib_beacon_filter_table { - __le32 num; - struct wsm_beacon_filter_table_entry entry[10]; -} __packed; - -static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv, - struct wsm_mib_beacon_filter_table *ft) -{ - size_t size = __le32_to_cpu(ft->num) * - sizeof(struct wsm_beacon_filter_table_entry) + - sizeof(__le32); - - return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size); -} - -#define WSM_BEACON_FILTER_ENABLE BIT(0) /* Enable/disable beacon filtering */ -#define WSM_BEACON_FILTER_AUTO_ERP BIT(1) /* If 1 FW will handle ERP IE changes internally */ - -struct wsm_beacon_filter_control { - int enabled; - int bcn_count; -}; - -static inline int wsm_beacon_filter_control(struct cw1200_common *priv, - struct wsm_beacon_filter_control *arg) -{ - struct { - __le32 enabled; - __le32 bcn_count; - } val; - val.enabled = __cpu_to_le32(arg->enabled); - val.bcn_count = __cpu_to_le32(arg->bcn_count); - return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val, - sizeof(val)); -} - -enum wsm_power_mode { - wsm_power_mode_active = 0, - wsm_power_mode_doze = 1, - wsm_power_mode_quiescent = 2, -}; - -struct wsm_operational_mode { - enum wsm_power_mode power_mode; - int disable_more_flag_usage; - int perform_ant_diversity; -}; - -static inline int wsm_set_operational_mode(struct cw1200_common *priv, - const struct wsm_operational_mode *arg) -{ - u8 val = arg->power_mode; - if (arg->disable_more_flag_usage) - val |= BIT(4); - if (arg->perform_ant_diversity) - val |= BIT(5); - return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val, - sizeof(val)); -} - -struct wsm_template_frame { - u8 frame_type; - u8 rate; - struct sk_buff *skb; -}; - -static inline int wsm_set_template_frame(struct cw1200_common *priv, - struct wsm_template_frame *arg) -{ - int ret; - u8 *p = skb_push(arg->skb, 4); - p[0] = arg->frame_type; - p[1] = arg->rate; - ((__le16 *)p)[1] = __cpu_to_le16(arg->skb->len - 4); - ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len); - skb_pull(arg->skb, 4); - return ret; -} - - -struct wsm_protected_mgmt_policy { - bool protectedMgmtEnable; - bool unprotectedMgmtFramesAllowed; - bool encryptionForAuthFrame; -}; - -static inline int wsm_set_protected_mgmt_policy(struct cw1200_common *priv, - struct wsm_protected_mgmt_policy *arg) -{ - __le32 val = 0; - int ret; - if (arg->protectedMgmtEnable) - val |= __cpu_to_le32(BIT(0)); - if (arg->unprotectedMgmtFramesAllowed) - val |= __cpu_to_le32(BIT(1)); - if (arg->encryptionForAuthFrame) - val |= __cpu_to_le32(BIT(2)); - ret = wsm_write_mib(priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY, - &val, sizeof(val)); - return ret; -} - -struct wsm_mib_block_ack_policy { - u8 tx_tid; - u8 reserved1; - u8 rx_tid; - u8 reserved2; -} __packed; - -static inline int wsm_set_block_ack_policy(struct cw1200_common *priv, - u8 tx_tid_policy, - u8 rx_tid_policy) -{ - struct wsm_mib_block_ack_policy val = { - .tx_tid = tx_tid_policy, - .rx_tid = rx_tid_policy, - }; - return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val, - sizeof(val)); -} - -struct wsm_mib_association_mode { - u8 flags; /* WSM_ASSOCIATION_MODE_... */ - u8 preamble; /* WSM_JOIN_PREAMBLE_... */ - u8 greenfield; /* 1 for greenfield */ - u8 mpdu_start_spacing; - __le32 basic_rate_set; -} __packed; - -static inline int wsm_set_association_mode(struct cw1200_common *priv, - struct wsm_mib_association_mode *arg) -{ - return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg, - sizeof(*arg)); -} - -#define WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED BIT(2) -#define WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT BIT(3) -struct wsm_tx_rate_retry_policy { - u8 index; - u8 short_retries; - u8 long_retries; - /* BIT(2) - Terminate retries when Tx rate retry policy - * finishes. - * BIT(3) - Count initial frame transmission as part of - * rate retry counting but not as a retry - * attempt - */ - u8 flags; - u8 rate_recoveries; - u8 reserved[3]; - __le32 rate_count_indices[3]; -} __packed; - -struct wsm_set_tx_rate_retry_policy { - u8 num; - u8 reserved[3]; - struct wsm_tx_rate_retry_policy tbl[8]; -} __packed; - -static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv, - struct wsm_set_tx_rate_retry_policy *arg) -{ - size_t size = 4 + arg->num * sizeof(struct wsm_tx_rate_retry_policy); - return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, - size); -} - -/* 4.32 SetEtherTypeDataFrameFilter */ -struct wsm_ether_type_filter_hdr { - u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */ - u8 reserved[3]; -} __packed; - -struct wsm_ether_type_filter { - u8 action; /* WSM_FILTER_ACTION_XXX */ - u8 reserved; - __le16 type; /* Type of ethernet frame */ -} __packed; - -static inline int wsm_set_ether_type_filter(struct cw1200_common *priv, - struct wsm_ether_type_filter_hdr *arg) -{ - size_t size = sizeof(struct wsm_ether_type_filter_hdr) + - arg->num * sizeof(struct wsm_ether_type_filter); - return wsm_write_mib(priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER, - arg, size); -} - -/* 4.33 SetUDPPortDataFrameFilter */ -struct wsm_udp_port_filter_hdr { - u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */ - u8 reserved[3]; -} __packed; - -struct wsm_udp_port_filter { - u8 action; /* WSM_FILTER_ACTION_XXX */ - u8 type; /* WSM_FILTER_PORT_TYPE_XXX */ - __le16 port; /* Port number */ -} __packed; - -static inline int wsm_set_udp_port_filter(struct cw1200_common *priv, - struct wsm_udp_port_filter_hdr *arg) -{ - size_t size = sizeof(struct wsm_udp_port_filter_hdr) + - arg->num * sizeof(struct wsm_udp_port_filter); - return wsm_write_mib(priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER, - arg, size); -} - -/* Undocumented MIBs: */ -/* 4.35 P2PDeviceInfo */ -#define D11_MAX_SSID_LEN (32) - -struct wsm_p2p_device_type { - __le16 category_id; - u8 oui[4]; - __le16 subcategory_id; -} __packed; - -struct wsm_p2p_device_info { - struct wsm_p2p_device_type primaryDevice; - u8 reserved1[3]; - u8 devname_size; - u8 local_devname[D11_MAX_SSID_LEN]; - u8 reserved2[3]; - u8 num_secdev_supported; - struct wsm_p2p_device_type secdevs[0]; -} __packed; - -/* 4.36 SetWCDMABand - WO */ -struct wsm_cdma_band { - u8 wcdma_band; - u8 reserved[3]; -} __packed; - -/* 4.37 GroupTxSequenceCounter - RO */ -struct wsm_group_tx_seq { - __le32 bits_47_16; - __le16 bits_15_00; - __le16 reserved; -} __packed; - -/* 4.39 SetHtProtection - WO */ -#define WSM_DUAL_CTS_PROT_ENB (1 << 0) -#define WSM_NON_GREENFIELD_STA_PRESENT (1 << 1) -#define WSM_HT_PROT_MODE__NO_PROT (0 << 2) -#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2) -#define WSM_HT_PROT_MODE__20_MHZ (2 << 2) -#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2) -#define WSM_LSIG_TXOP_PROT_FULL (1 << 4) -#define WSM_LARGE_L_LENGTH_PROT (1 << 5) - -struct wsm_ht_protection { - __le32 flags; -} __packed; - -/* 4.40 GPIO Command - R/W */ -#define WSM_GPIO_COMMAND_SETUP 0 -#define WSM_GPIO_COMMAND_READ 1 -#define WSM_GPIO_COMMAND_WRITE 2 -#define WSM_GPIO_COMMAND_RESET 3 -#define WSM_GPIO_ALL_PINS 0xFF - -struct wsm_gpio_command { - u8 command; - u8 pin; - __le16 config; -} __packed; - -/* 4.41 TSFCounter - RO */ -struct wsm_tsf_counter { - __le64 tsf_counter; -} __packed; - -/* 4.43 Keep alive period */ -struct wsm_keep_alive_period { - __le16 period; - u8 reserved[2]; -} __packed; - -static inline int wsm_keep_alive_period(struct cw1200_common *priv, - int period) -{ - struct wsm_keep_alive_period arg = { - .period = __cpu_to_le16(period), - }; - return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, - &arg, sizeof(arg)); -}; - -/* BSSID filtering */ -struct wsm_set_bssid_filtering { - u8 filter; - u8 reserved[3]; -} __packed; - -static inline int wsm_set_bssid_filtering(struct cw1200_common *priv, - bool enabled) -{ - struct wsm_set_bssid_filtering arg = { - .filter = !enabled, - }; - return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER, - &arg, sizeof(arg)); -} - -/* Multicast filtering - 4.5 */ -struct wsm_mib_multicast_filter { - __le32 enable; - __le32 num_addrs; - u8 macaddrs[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN]; -} __packed; - -static inline int wsm_set_multicast_filter(struct cw1200_common *priv, - struct wsm_mib_multicast_filter *fp) -{ - return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, - fp, sizeof(*fp)); -} - -/* ARP IPv4 filtering - 4.10 */ -struct wsm_mib_arp_ipv4_filter { - __le32 enable; - __be32 ipv4addrs[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES]; -} __packed; - -static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv, - struct wsm_mib_arp_ipv4_filter *fp) -{ - return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE, - fp, sizeof(*fp)); -} - -/* P2P Power Save Mode Info - 4.31 */ -struct wsm_p2p_ps_modeinfo { - u8 opp_ps_ct_window; - u8 count; - u8 reserved; - u8 dtim_count; - __le32 duration; - __le32 interval; - __le32 start_time; -} __packed; - -static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv, - struct wsm_p2p_ps_modeinfo *mi) -{ - return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, - mi, sizeof(*mi)); -} - -static inline int wsm_get_p2p_ps_modeinfo(struct cw1200_common *priv, - struct wsm_p2p_ps_modeinfo *mi) -{ - return wsm_read_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, - mi, sizeof(*mi)); -} - -/* UseMultiTxConfMessage */ - -static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv, - bool enabled) -{ - __le32 arg = enabled ? __cpu_to_le32(1) : 0; - - return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF, - &arg, sizeof(arg)); -} - - -/* 4.26 SetUpasdInformation */ -struct wsm_uapsd_info { - __le16 uapsd_flags; - __le16 min_auto_trigger_interval; - __le16 max_auto_trigger_interval; - __le16 auto_trigger_step; -}; - -static inline int wsm_set_uapsd_info(struct cw1200_common *priv, - struct wsm_uapsd_info *arg) -{ - return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION, - arg, sizeof(*arg)); -} - -/* 4.22 OverrideInternalTxRate */ -struct wsm_override_internal_txrate { - u8 internalTxRate; - u8 nonErpInternalTxRate; - u8 reserved[2]; -} __packed; - -static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv, - struct wsm_override_internal_txrate *arg) -{ - return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, - arg, sizeof(*arg)); -} - -/* ******************************************************************** */ -/* WSM TX port control */ - -void wsm_lock_tx(struct cw1200_common *priv); -void wsm_lock_tx_async(struct cw1200_common *priv); -bool wsm_flush_tx(struct cw1200_common *priv); -void wsm_unlock_tx(struct cw1200_common *priv); - -/* ******************************************************************** */ -/* WSM / BH API */ - -int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len); -int wsm_handle_rx(struct cw1200_common *priv, u16 id, struct wsm_hdr *wsm, - struct sk_buff **skb_p); - -/* ******************************************************************** */ -/* wsm_buf API */ - -struct wsm_buf { - u8 *begin; - u8 *data; - u8 *end; -}; - -void wsm_buf_init(struct wsm_buf *buf); -void wsm_buf_deinit(struct wsm_buf *buf); - -/* ******************************************************************** */ -/* wsm_cmd API */ - -struct wsm_cmd { - spinlock_t lock; /* Protect structure from multiple access */ - int done; - u8 *ptr; - size_t len; - void *arg; - int ret; - u16 cmd; -}; - -/* ******************************************************************** */ -/* WSM TX buffer access */ - -int wsm_get_tx(struct cw1200_common *priv, u8 **data, - size_t *tx_len, int *burst); -void wsm_txed(struct cw1200_common *priv, u8 *data); - -/* ******************************************************************** */ -/* Queue mapping: WSM <---> linux */ -/* Linux: VO VI BE BK */ -/* WSM: BE BK VI VO */ - -static inline u8 wsm_queue_id_to_linux(u8 queue_id) -{ - static const u8 queue_mapping[] = { - 2, 3, 1, 0 - }; - return queue_mapping[queue_id]; -} - -static inline u8 wsm_queue_id_to_wsm(u8 queue_id) -{ - static const u8 queue_mapping[] = { - 3, 2, 0, 1 - }; - return queue_mapping[queue_id]; -} - -#endif /* CW1200_HWIO_H_INCLUDED */ diff --git a/drivers/net/wireless/st/Kconfig b/drivers/net/wireless/st/Kconfig new file mode 100644 index 000000000000..969b4f6e53b5 --- /dev/null +++ b/drivers/net/wireless/st/Kconfig @@ -0,0 +1,16 @@ +config WLAN_VENDOR_ST + bool "STMicroelectronics devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_ST + +source "drivers/net/wireless/st/cw1200/Kconfig" + +endif # WLAN_VENDOR_ST diff --git a/drivers/net/wireless/st/Makefile b/drivers/net/wireless/st/Makefile new file mode 100644 index 000000000000..a60d6350ba46 --- /dev/null +++ b/drivers/net/wireless/st/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_CW1200) += cw1200/ diff --git a/drivers/net/wireless/st/cw1200/Kconfig b/drivers/net/wireless/st/cw1200/Kconfig new file mode 100644 index 000000000000..0880742eab17 --- /dev/null +++ b/drivers/net/wireless/st/cw1200/Kconfig @@ -0,0 +1,30 @@ +config CW1200 + tristate "CW1200 WLAN support" + depends on MAC80211 && CFG80211 + help + This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets. + This option just enables the driver core, see below for + specific bus support. + +if CW1200 + +config CW1200_WLAN_SDIO + tristate "Support SDIO platforms" + depends on CW1200 && MMC + help + Enable support for the CW1200 connected via an SDIO bus. + By default this driver only supports the Sagrad SG901-1091/1098 EVK + and similar designs that utilize a hardware reset circuit. To + support different CW1200 SDIO designs you will need to override + the default platform data by calling cw1200_sdio_set_platform_data() + in your board setup file. + +config CW1200_WLAN_SPI + tristate "Support SPI platforms" + depends on CW1200 && SPI + help + Enables support for the CW1200 connected via a SPI bus. You will + need to add appropriate platform data glue in your board setup + file. + +endif diff --git a/drivers/net/wireless/st/cw1200/Makefile b/drivers/net/wireless/st/cw1200/Makefile new file mode 100644 index 000000000000..b086aac6547a --- /dev/null +++ b/drivers/net/wireless/st/cw1200/Makefile @@ -0,0 +1,21 @@ +cw1200_core-y := \ + fwio.o \ + txrx.o \ + main.o \ + queue.o \ + hwio.o \ + bh.o \ + wsm.o \ + sta.o \ + scan.o \ + debug.o +cw1200_core-$(CONFIG_PM) += pm.o + +# CFLAGS_sta.o += -DDEBUG + +cw1200_wlan_sdio-y := cw1200_sdio.o +cw1200_wlan_spi-y := cw1200_spi.o + +obj-$(CONFIG_CW1200) += cw1200_core.o +obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o +obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o diff --git a/drivers/net/wireless/st/cw1200/bh.c b/drivers/net/wireless/st/cw1200/bh.c new file mode 100644 index 000000000000..92d299aa257c --- /dev/null +++ b/drivers/net/wireless/st/cw1200/bh.c @@ -0,0 +1,619 @@ +/* + * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver, which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "cw1200.h" +#include "bh.h" +#include "hwio.h" +#include "wsm.h" +#include "hwbus.h" +#include "debug.h" +#include "fwio.h" + +static int cw1200_bh(void *arg); + +#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) +/* an SPI message cannot be bigger than (2"12-1)*2 bytes + * "*2" to cvt to bytes + */ +#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2) +#define PIGGYBACK_CTRL_REG (2) +#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) + +/* Suspend state privates */ +enum cw1200_bh_pm_state { + CW1200_BH_RESUMED = 0, + CW1200_BH_SUSPEND, + CW1200_BH_SUSPENDED, + CW1200_BH_RESUME, +}; + +typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv, + u8 *data, size_t size); + +static void cw1200_bh_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, bh_work); + cw1200_bh(priv); +} + +int cw1200_register_bh(struct cw1200_common *priv) +{ + int err = 0; + /* Realtime workqueue */ + priv->bh_workqueue = alloc_workqueue("cw1200_bh", + WQ_MEM_RECLAIM | WQ_HIGHPRI + | WQ_CPU_INTENSIVE, 1); + + if (!priv->bh_workqueue) + return -ENOMEM; + + INIT_WORK(&priv->bh_work, cw1200_bh_work); + + pr_debug("[BH] register.\n"); + + atomic_set(&priv->bh_rx, 0); + atomic_set(&priv->bh_tx, 0); + atomic_set(&priv->bh_term, 0); + atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); + priv->bh_error = 0; + priv->hw_bufs_used = 0; + priv->buf_id_tx = 0; + priv->buf_id_rx = 0; + init_waitqueue_head(&priv->bh_wq); + init_waitqueue_head(&priv->bh_evt_wq); + + err = !queue_work(priv->bh_workqueue, &priv->bh_work); + WARN_ON(err); + return err; +} + +void cw1200_unregister_bh(struct cw1200_common *priv) +{ + atomic_add(1, &priv->bh_term); + wake_up(&priv->bh_wq); + + flush_workqueue(priv->bh_workqueue); + + destroy_workqueue(priv->bh_workqueue); + priv->bh_workqueue = NULL; + + pr_debug("[BH] unregistered.\n"); +} + +void cw1200_irq_handler(struct cw1200_common *priv) +{ + pr_debug("[BH] irq.\n"); + + /* Disable Interrupts! */ + /* NOTE: hwbus_ops->lock already held */ + __cw1200_irq_enable(priv, 0); + + if (/* WARN_ON */(priv->bh_error)) + return; + + if (atomic_add_return(1, &priv->bh_rx) == 1) + wake_up(&priv->bh_wq); +} +EXPORT_SYMBOL_GPL(cw1200_irq_handler); + +void cw1200_bh_wakeup(struct cw1200_common *priv) +{ + pr_debug("[BH] wakeup.\n"); + if (priv->bh_error) { + pr_err("[BH] wakeup failed (BH error)\n"); + return; + } + + if (atomic_add_return(1, &priv->bh_tx) == 1) + wake_up(&priv->bh_wq); +} + +int cw1200_bh_suspend(struct cw1200_common *priv) +{ + pr_debug("[BH] suspend.\n"); + if (priv->bh_error) { + wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n"); + return -EINVAL; + } + + atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND); + wake_up(&priv->bh_wq); + return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; +} + +int cw1200_bh_resume(struct cw1200_common *priv) +{ + pr_debug("[BH] resume.\n"); + if (priv->bh_error) { + wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n"); + return -EINVAL; + } + + atomic_set(&priv->bh_suspend, CW1200_BH_RESUME); + wake_up(&priv->bh_wq); + return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; +} + +static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv) +{ + ++priv->hw_bufs_used; +} + +int wsm_release_tx_buffer(struct cw1200_common *priv, int count) +{ + int ret = 0; + int hw_bufs_used = priv->hw_bufs_used; + + priv->hw_bufs_used -= count; + if (WARN_ON(priv->hw_bufs_used < 0)) + ret = -1; + else if (hw_bufs_used >= priv->wsm_caps.input_buffers) + ret = 1; + if (!priv->hw_bufs_used) + wake_up(&priv->bh_evt_wq); + return ret; +} + +static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv, + u16 *ctrl_reg) +{ + int ret; + + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) { + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) + pr_err("[BH] Failed to read control register.\n"); + } + + return ret; +} + +static int cw1200_device_wakeup(struct cw1200_common *priv) +{ + u16 ctrl_reg; + int ret; + + pr_debug("[BH] Device wakeup.\n"); + + /* First, set the dpll register */ + ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, + cw1200_dpll_from_clk(priv->hw_refclk)); + if (WARN_ON(ret)) + return ret; + + /* To force the device to be always-on, the host sets WLAN_UP to 1 */ + ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, + ST90TDS_CONT_WUP_BIT); + if (WARN_ON(ret)) + return ret; + + ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg); + if (WARN_ON(ret)) + return ret; + + /* If the device returns WLAN_RDY as 1, the device is active and will + * remain active. + */ + if (ctrl_reg & ST90TDS_CONT_RDY_BIT) { + pr_debug("[BH] Device awake.\n"); + return 1; + } + + return 0; +} + +/* Must be called from BH thraed. */ +void cw1200_enable_powersave(struct cw1200_common *priv, + bool enable) +{ + pr_debug("[BH] Powerave is %s.\n", + enable ? "enabled" : "disabled"); + priv->powersave_enabled = enable; +} + +static int cw1200_bh_rx_helper(struct cw1200_common *priv, + uint16_t *ctrl_reg, + int *tx) +{ + size_t read_len = 0; + struct sk_buff *skb_rx = NULL; + struct wsm_hdr *wsm; + size_t wsm_len; + u16 wsm_id; + u8 wsm_seq; + int rx_resync = 1; + + size_t alloc_len; + u8 *data; + + read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2; + if (!read_len) + return 0; /* No more work */ + + if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || + (read_len > EFFECTIVE_BUF_SIZE))) { + pr_debug("Invalid read len: %zu (%04x)", + read_len, *ctrl_reg); + goto err; + } + + /* Add SIZE of PIGGYBACK reg (CONTROL Reg) + * to the NEXT Message length + 2 Bytes for SKB + */ + read_len = read_len + 2; + + alloc_len = priv->hwbus_ops->align_size( + priv->hwbus_priv, read_len); + + /* Check if not exceeding CW1200 capabilities */ + if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) { + pr_debug("Read aligned len: %zu\n", + alloc_len); + } + + skb_rx = dev_alloc_skb(alloc_len); + if (WARN_ON(!skb_rx)) + goto err; + + skb_trim(skb_rx, 0); + skb_put(skb_rx, read_len); + data = skb_rx->data; + if (WARN_ON(!data)) + goto err; + + if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) { + pr_err("rx blew up, len %zu\n", alloc_len); + goto err; + } + + /* Piggyback */ + *ctrl_reg = __le16_to_cpu( + ((__le16 *)data)[alloc_len / 2 - 1]); + + wsm = (struct wsm_hdr *)data; + wsm_len = __le16_to_cpu(wsm->len); + if (WARN_ON(wsm_len > read_len)) + goto err; + + if (priv->wsm_enable_wsm_dumps) + print_hex_dump_bytes("<-- ", + DUMP_PREFIX_NONE, + data, wsm_len); + + wsm_id = __le16_to_cpu(wsm->id) & 0xFFF; + wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7; + + skb_trim(skb_rx, wsm_len); + + if (wsm_id == 0x0800) { + wsm_handle_exception(priv, + &data[sizeof(*wsm)], + wsm_len - sizeof(*wsm)); + goto err; + } else if (!rx_resync) { + if (WARN_ON(wsm_seq != priv->wsm_rx_seq)) + goto err; + } + priv->wsm_rx_seq = (wsm_seq + 1) & 7; + rx_resync = 0; + + if (wsm_id & 0x0400) { + int rc = wsm_release_tx_buffer(priv, 1); + if (WARN_ON(rc < 0)) + return rc; + else if (rc > 0) + *tx = 1; + } + + /* cw1200_wsm_rx takes care on SKB livetime */ + if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx))) + goto err; + + if (skb_rx) { + dev_kfree_skb(skb_rx); + skb_rx = NULL; + } + + return 0; + +err: + if (skb_rx) { + dev_kfree_skb(skb_rx); + skb_rx = NULL; + } + return -1; +} + +static int cw1200_bh_tx_helper(struct cw1200_common *priv, + int *pending_tx, + int *tx_burst) +{ + size_t tx_len; + u8 *data; + int ret; + struct wsm_hdr *wsm; + + if (priv->device_can_sleep) { + ret = cw1200_device_wakeup(priv); + if (WARN_ON(ret < 0)) { /* Error in wakeup */ + *pending_tx = 1; + return 0; + } else if (ret) { /* Woke up */ + priv->device_can_sleep = false; + } else { /* Did not awake */ + *pending_tx = 1; + return 0; + } + } + + wsm_alloc_tx_buffer(priv); + ret = wsm_get_tx(priv, &data, &tx_len, tx_burst); + if (ret <= 0) { + wsm_release_tx_buffer(priv, 1); + if (WARN_ON(ret < 0)) + return ret; /* Error */ + return 0; /* No work */ + } + + wsm = (struct wsm_hdr *)data; + BUG_ON(tx_len < sizeof(*wsm)); + BUG_ON(__le16_to_cpu(wsm->len) != tx_len); + + atomic_add(1, &priv->bh_tx); + + tx_len = priv->hwbus_ops->align_size( + priv->hwbus_priv, tx_len); + + /* Check if not exceeding CW1200 capabilities */ + if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE)) + pr_debug("Write aligned len: %zu\n", tx_len); + + wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX)); + wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq)); + + if (WARN_ON(cw1200_data_write(priv, data, tx_len))) { + pr_err("tx blew up, len %zu\n", tx_len); + wsm_release_tx_buffer(priv, 1); + return -1; /* Error */ + } + + if (priv->wsm_enable_wsm_dumps) + print_hex_dump_bytes("--> ", + DUMP_PREFIX_NONE, + data, + __le16_to_cpu(wsm->len)); + + wsm_txed(priv, data); + priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX; + + if (*tx_burst > 1) { + cw1200_debug_tx_burst(priv); + return 1; /* Work remains */ + } + + return 0; +} + +static int cw1200_bh(void *arg) +{ + struct cw1200_common *priv = arg; + int rx, tx, term, suspend; + u16 ctrl_reg = 0; + int tx_allowed; + int pending_tx = 0; + int tx_burst; + long status; + u32 dummy; + int ret; + + for (;;) { + if (!priv->hw_bufs_used && + priv->powersave_enabled && + !priv->device_can_sleep && + !atomic_read(&priv->recent_scan)) { + status = 1 * HZ; + pr_debug("[BH] Device wakedown. No data.\n"); + cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0); + priv->device_can_sleep = true; + } else if (priv->hw_bufs_used) { + /* Interrupt loss detection */ + status = 1 * HZ; + } else { + status = MAX_SCHEDULE_TIMEOUT; + } + + /* Dummy Read for SDIO retry mechanism*/ + if ((priv->hw_type != -1) && + (atomic_read(&priv->bh_rx) == 0) && + (atomic_read(&priv->bh_tx) == 0)) + cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID, + &dummy, sizeof(dummy)); + + pr_debug("[BH] waiting ...\n"); + status = wait_event_interruptible_timeout(priv->bh_wq, ({ + rx = atomic_xchg(&priv->bh_rx, 0); + tx = atomic_xchg(&priv->bh_tx, 0); + term = atomic_xchg(&priv->bh_term, 0); + suspend = pending_tx ? + 0 : atomic_read(&priv->bh_suspend); + (rx || tx || term || suspend || priv->bh_error); + }), status); + + pr_debug("[BH] - rx: %d, tx: %d, term: %d, bh_err: %d, suspend: %d, status: %ld\n", + rx, tx, term, suspend, priv->bh_error, status); + + /* Did an error occur? */ + if ((status < 0 && status != -ERESTARTSYS) || + term || priv->bh_error) { + break; + } + if (!status) { /* wait_event timed out */ + unsigned long timestamp = jiffies; + long timeout; + int pending = 0; + int i; + + /* Check to see if we have any outstanding frames */ + if (priv->hw_bufs_used && (!rx || !tx)) { + wiphy_warn(priv->hw->wiphy, + "Missed interrupt? (%d frames outstanding)\n", + priv->hw_bufs_used); + rx = 1; + + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + pending += cw1200_queue_get_xmit_timestamp( + &priv->tx_queue[i], + ×tamp, + priv->pending_frame_id); + + /* Check if frame transmission is timed out. + * Add an extra second with respect to possible + * interrupt loss. + */ + timeout = timestamp + + WSM_CMD_LAST_CHANCE_TIMEOUT + + 1 * HZ - + jiffies; + + /* And terminate BH thread if the frame is "stuck" */ + if (pending && timeout < 0) { + wiphy_warn(priv->hw->wiphy, + "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n", + priv->hw_bufs_used, pending, + timestamp, jiffies); + break; + } + } else if (!priv->device_can_sleep && + !atomic_read(&priv->recent_scan)) { + pr_debug("[BH] Device wakedown. Timeout.\n"); + cw1200_reg_write_16(priv, + ST90TDS_CONTROL_REG_ID, 0); + priv->device_can_sleep = true; + } + goto done; + } else if (suspend) { + pr_debug("[BH] Device suspend.\n"); + if (priv->powersave_enabled) { + pr_debug("[BH] Device wakedown. Suspend.\n"); + cw1200_reg_write_16(priv, + ST90TDS_CONTROL_REG_ID, 0); + priv->device_can_sleep = true; + } + + atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED); + wake_up(&priv->bh_evt_wq); + status = wait_event_interruptible(priv->bh_wq, + CW1200_BH_RESUME == atomic_read(&priv->bh_suspend)); + if (status < 0) { + wiphy_err(priv->hw->wiphy, + "Failed to wait for resume: %ld.\n", + status); + break; + } + pr_debug("[BH] Device resume.\n"); + atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); + wake_up(&priv->bh_evt_wq); + atomic_add(1, &priv->bh_rx); + goto done; + } + + rx: + tx += pending_tx; + pending_tx = 0; + + if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg)) + break; + + /* Don't bother trying to rx unless we have data to read */ + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) { + ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx); + if (ret < 0) + break; + /* Double up here if there's more data.. */ + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) { + ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx); + if (ret < 0) + break; + } + } + + tx: + if (tx) { + tx = 0; + + BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers); + tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used; + tx_allowed = tx_burst > 0; + + if (!tx_allowed) { + /* Buffers full. Ensure we process tx + * after we handle rx.. + */ + pending_tx = tx; + goto done_rx; + } + ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst); + if (ret < 0) + break; + if (ret > 0) /* More to transmit */ + tx = ret; + + /* Re-read ctrl reg */ + if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg)) + break; + } + + done_rx: + if (priv->bh_error) + break; + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) + goto rx; + if (tx) + goto tx; + + done: + /* Re-enable device interrupts */ + priv->hwbus_ops->lock(priv->hwbus_priv); + __cw1200_irq_enable(priv, 1); + priv->hwbus_ops->unlock(priv->hwbus_priv); + } + + /* Explicitly disable device interrupts */ + priv->hwbus_ops->lock(priv->hwbus_priv); + __cw1200_irq_enable(priv, 0); + priv->hwbus_ops->unlock(priv->hwbus_priv); + + if (!term) { + pr_err("[BH] Fatal error, exiting.\n"); + priv->bh_error = 1; + /* TODO: schedule_work(recovery) */ + } + return 0; +} diff --git a/drivers/net/wireless/st/cw1200/bh.h b/drivers/net/wireless/st/cw1200/bh.h new file mode 100644 index 000000000000..af6a4853728f --- /dev/null +++ b/drivers/net/wireless/st/cw1200/bh.h @@ -0,0 +1,28 @@ +/* + * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_BH_H +#define CW1200_BH_H + +/* extern */ struct cw1200_common; + +int cw1200_register_bh(struct cw1200_common *priv); +void cw1200_unregister_bh(struct cw1200_common *priv); +void cw1200_irq_handler(struct cw1200_common *priv); +void cw1200_bh_wakeup(struct cw1200_common *priv); +int cw1200_bh_suspend(struct cw1200_common *priv); +int cw1200_bh_resume(struct cw1200_common *priv); +/* Must be called from BH thread. */ +void cw1200_enable_powersave(struct cw1200_common *priv, + bool enable); +int wsm_release_tx_buffer(struct cw1200_common *priv, int count); + +#endif /* CW1200_BH_H */ diff --git a/drivers/net/wireless/st/cw1200/cw1200.h b/drivers/net/wireless/st/cw1200/cw1200.h new file mode 100644 index 000000000000..1ad7d3602520 --- /dev/null +++ b/drivers/net/wireless/st/cw1200/cw1200.h @@ -0,0 +1,323 @@ +/* + * Common private data for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on the mac80211 Prism54 code, which is + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_H +#define CW1200_H + +#include +#include +#include +#include + +#include "queue.h" +#include "wsm.h" +#include "scan.h" +#include "txrx.h" +#include "pm.h" + +/* Forward declarations */ +struct hwbus_ops; +struct task_struct; +struct cw1200_debug_priv; +struct firmware; + +#define CW1200_MAX_CTRL_FRAME_LEN (0x1000) + +#define CW1200_MAX_STA_IN_AP_MODE (5) +#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1) +#define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2) +#define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3) +#define CW1200_MAX_REQUEUE_ATTEMPTS (5) + +#define CW1200_MAX_TID (8) + +#define CW1200_BLOCK_ACK_CNT (30) +#define CW1200_BLOCK_ACK_THLD (800) +#define CW1200_BLOCK_ACK_HIST (3) +#define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST) + +#define CW1200_JOIN_TIMEOUT (1 * HZ) +#define CW1200_AUTH_TIMEOUT (5 * HZ) + +struct cw1200_ht_info { + struct ieee80211_sta_ht_cap ht_cap; + enum nl80211_channel_type channel_type; + u16 operation_mode; +}; + +/* Please keep order */ +enum cw1200_join_status { + CW1200_JOIN_STATUS_PASSIVE = 0, + CW1200_JOIN_STATUS_MONITOR, + CW1200_JOIN_STATUS_JOINING, + CW1200_JOIN_STATUS_PRE_STA, + CW1200_JOIN_STATUS_STA, + CW1200_JOIN_STATUS_IBSS, + CW1200_JOIN_STATUS_AP, +}; + +enum cw1200_link_status { + CW1200_LINK_OFF, + CW1200_LINK_RESERVE, + CW1200_LINK_SOFT, + CW1200_LINK_HARD, + CW1200_LINK_RESET, + CW1200_LINK_RESET_REMAP, +}; + +extern int cw1200_power_mode; +extern const char * const cw1200_fw_types[]; + +struct cw1200_link_entry { + unsigned long timestamp; + enum cw1200_link_status status; + enum cw1200_link_status prev_status; + u8 mac[ETH_ALEN]; + u8 buffered[CW1200_MAX_TID]; + struct sk_buff_head rx_queue; +}; + +struct cw1200_common { + /* interfaces to the rest of the stack */ + struct ieee80211_hw *hw; + struct ieee80211_vif *vif; + struct device *pdev; + + /* Statistics */ + struct ieee80211_low_level_stats stats; + + /* Our macaddr */ + u8 mac_addr[ETH_ALEN]; + + /* Hardware interface */ + const struct hwbus_ops *hwbus_ops; + struct hwbus_priv *hwbus_priv; + + /* Hardware information */ + enum { + HIF_9000_SILICON_VERSATILE = 0, + HIF_8601_VERSATILE, + HIF_8601_SILICON, + } hw_type; + enum { + CW1200_HW_REV_CUT10 = 10, + CW1200_HW_REV_CUT11 = 11, + CW1200_HW_REV_CUT20 = 20, + CW1200_HW_REV_CUT22 = 22, + CW1X60_HW_REV = 40, + } hw_revision; + int hw_refclk; + bool hw_have_5ghz; + const struct firmware *sdd; + char *sdd_path; + + struct cw1200_debug_priv *debug; + + struct workqueue_struct *workqueue; + struct mutex conf_mutex; + + struct cw1200_queue tx_queue[4]; + struct cw1200_queue_stats tx_queue_stats; + int tx_burst_idx; + + /* firmware/hardware info */ + unsigned int tx_hdr_len; + + /* Radio data */ + int output_power; + + /* BBP/MAC state */ + struct ieee80211_rate *rates; + struct ieee80211_rate *mcs_rates; + struct ieee80211_channel *channel; + struct wsm_edca_params edca; + struct wsm_tx_queue_params tx_queue_params; + struct wsm_mib_association_mode association_mode; + struct wsm_set_bss_params bss_params; + struct cw1200_ht_info ht_info; + struct wsm_set_pm powersave_mode; + struct wsm_set_pm firmware_ps_mode; + int cqm_rssi_thold; + unsigned cqm_rssi_hyst; + bool cqm_use_rssi; + int cqm_beacon_loss_count; + int channel_switch_in_progress; + wait_queue_head_t channel_switch_done; + u8 long_frame_max_tx_count; + u8 short_frame_max_tx_count; + int mode; + bool enable_beacon; + int beacon_int; + bool listening; + struct wsm_rx_filter rx_filter; + struct wsm_mib_multicast_filter multicast_filter; + bool has_multicast_subscription; + bool disable_beacon_filter; + struct work_struct update_filtering_work; + struct work_struct set_beacon_wakeup_period_work; + + u8 ba_rx_tid_mask; + u8 ba_tx_tid_mask; + + struct cw1200_pm_state pm_state; + + struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; + struct wsm_uapsd_info uapsd_info; + bool setbssparams_done; + bool bt_present; + u8 conf_listen_interval; + u32 listen_interval; + u32 erp_info; + u32 rts_threshold; + + /* BH */ + atomic_t bh_rx; + atomic_t bh_tx; + atomic_t bh_term; + atomic_t bh_suspend; + + struct workqueue_struct *bh_workqueue; + struct work_struct bh_work; + + int bh_error; + wait_queue_head_t bh_wq; + wait_queue_head_t bh_evt_wq; + u8 buf_id_tx; + u8 buf_id_rx; + u8 wsm_rx_seq; + u8 wsm_tx_seq; + int hw_bufs_used; + bool powersave_enabled; + bool device_can_sleep; + + /* Scan status */ + struct cw1200_scan scan; + /* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid + * FW issue with sleeping/waking up. + */ + atomic_t recent_scan; + struct delayed_work clear_recent_scan_work; + + /* WSM */ + struct wsm_startup_ind wsm_caps; + struct mutex wsm_cmd_mux; + struct wsm_buf wsm_cmd_buf; + struct wsm_cmd wsm_cmd; + wait_queue_head_t wsm_cmd_wq; + wait_queue_head_t wsm_startup_done; + int firmware_ready; + atomic_t tx_lock; + + /* WSM debug */ + int wsm_enable_wsm_dumps; + + /* WSM Join */ + enum cw1200_join_status join_status; + u32 pending_frame_id; + bool join_pending; + struct delayed_work join_timeout; + struct work_struct unjoin_work; + struct work_struct join_complete_work; + int join_complete_status; + int join_dtim_period; + bool delayed_unjoin; + + /* TX/RX and security */ + s8 wep_default_key_id; + struct work_struct wep_key_work; + u32 key_map; + struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1]; + + /* AP powersave */ + u32 link_id_map; + struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE]; + struct work_struct link_id_work; + struct delayed_work link_id_gc_work; + u32 sta_asleep_mask; + u32 pspoll_mask; + bool aid0_bit_set; + spinlock_t ps_state_lock; /* Protect power save state */ + bool buffered_multicasts; + bool tx_multicast; + struct work_struct set_tim_work; + struct work_struct set_cts_work; + struct work_struct multicast_start_work; + struct work_struct multicast_stop_work; + struct timer_list mcast_timeout; + + /* WSM events and CQM implementation */ + spinlock_t event_queue_lock; /* Protect event queue */ + struct list_head event_queue; + struct work_struct event_handler; + + struct delayed_work bss_loss_work; + spinlock_t bss_loss_lock; /* Protect BSS loss state */ + int bss_loss_state; + u32 bss_loss_confirm_id; + int delayed_link_loss; + struct work_struct bss_params_work; + + /* TX rate policy cache */ + struct tx_policy_cache tx_policy_cache; + struct work_struct tx_policy_upload_work; + + /* legacy PS mode switch in suspend */ + int ps_mode_switch_in_progress; + wait_queue_head_t ps_mode_switch_done; + + /* Workaround for WFD testcase 6.1.10*/ + struct work_struct linkid_reset_work; + u8 action_frame_sa[ETH_ALEN]; + u8 action_linkid; +}; + +struct cw1200_sta_priv { + int link_id; +}; + +/* interfaces for the drivers */ +int cw1200_core_probe(const struct hwbus_ops *hwbus_ops, + struct hwbus_priv *hwbus, + struct device *pdev, + struct cw1200_common **pself, + int ref_clk, const u8 *macaddr, + const char *sdd_path, bool have_5ghz); +void cw1200_core_release(struct cw1200_common *self); + +#define FWLOAD_BLOCK_SIZE (1024) + +static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info) +{ + return ht_info->channel_type != NL80211_CHAN_NO_HT; +} + +static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info) +{ + return cw1200_is_ht(ht_info) && + (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ht_info->operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); +} + +static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info) +{ + if (!cw1200_is_ht(ht_info)) + return 0; + return ht_info->ht_cap.ampdu_density; +} + +#endif /* CW1200_H */ diff --git a/drivers/net/wireless/st/cw1200/cw1200_sdio.c b/drivers/net/wireless/st/cw1200/cw1200_sdio.c new file mode 100644 index 000000000000..d3acc85932a5 --- /dev/null +++ b/drivers/net/wireless/st/cw1200/cw1200_sdio.c @@ -0,0 +1,425 @@ +/* + * Mac80211 SDIO driver for ST-Ericsson CW1200 device + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cw1200.h" +#include "hwbus.h" +#include +#include "hwio.h" + +MODULE_AUTHOR("Dmitry Tarnyagin "); +MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver"); +MODULE_LICENSE("GPL"); + +#define SDIO_BLOCK_SIZE (512) + +/* Default platform data for Sagrad modules */ +static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = { + .ref_clk = 38400, + .have_5ghz = false, + .sdd_file = "sdd_sagrad_1091_1098.bin", +}; + +/* Allow platform data to be overridden */ +static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data; + +void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata) +{ + global_plat_data = pdata; +} + +struct hwbus_priv { + struct sdio_func *func; + struct cw1200_common *core; + const struct cw1200_platform_data_sdio *pdata; +}; + +#ifndef SDIO_VENDOR_ID_STE +#define SDIO_VENDOR_ID_STE 0x0020 +#endif + +#ifndef SDIO_DEVICE_ID_STE_CW1200 +#define SDIO_DEVICE_ID_STE_CW1200 0x2280 +#endif + +static const struct sdio_device_id cw1200_sdio_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) }, + { /* end: all zeroes */ }, +}; + +/* hwbus_ops implemetation */ + +static int cw1200_sdio_memcpy_fromio(struct hwbus_priv *self, + unsigned int addr, + void *dst, int count) +{ + return sdio_memcpy_fromio(self->func, dst, addr, count); +} + +static int cw1200_sdio_memcpy_toio(struct hwbus_priv *self, + unsigned int addr, + const void *src, int count) +{ + return sdio_memcpy_toio(self->func, addr, (void *)src, count); +} + +static void cw1200_sdio_lock(struct hwbus_priv *self) +{ + sdio_claim_host(self->func); +} + +static void cw1200_sdio_unlock(struct hwbus_priv *self) +{ + sdio_release_host(self->func); +} + +static void cw1200_sdio_irq_handler(struct sdio_func *func) +{ + struct hwbus_priv *self = sdio_get_drvdata(func); + + /* note: sdio_host already claimed here. */ + if (self->core) + cw1200_irq_handler(self->core); +} + +static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id) +{ + return IRQ_WAKE_THREAD; +} + +static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id) +{ + struct hwbus_priv *self = dev_id; + + if (self->core) { + cw1200_sdio_lock(self); + cw1200_irq_handler(self->core); + cw1200_sdio_unlock(self); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +static int cw1200_request_irq(struct hwbus_priv *self) +{ + int ret; + u8 cccr; + + cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto err; + + /* Master interrupt enable ... */ + cccr |= BIT(0); + + /* ... for our function */ + cccr |= BIT(self->func->num); + + sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto err; + + ret = enable_irq_wake(self->pdata->irq); + if (WARN_ON(ret)) + goto err; + + /* Request the IRQ */ + ret = request_threaded_irq(self->pdata->irq, cw1200_gpio_hardirq, + cw1200_gpio_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "cw1200_wlan_irq", self); + if (WARN_ON(ret)) + goto err; + + return 0; + +err: + return ret; +} + +static int cw1200_sdio_irq_subscribe(struct hwbus_priv *self) +{ + int ret = 0; + + pr_debug("SW IRQ subscribe\n"); + sdio_claim_host(self->func); + if (self->pdata->irq) + ret = cw1200_request_irq(self); + else + ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler); + + sdio_release_host(self->func); + return ret; +} + +static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self) +{ + int ret = 0; + + pr_debug("SW IRQ unsubscribe\n"); + + if (self->pdata->irq) { + disable_irq_wake(self->pdata->irq); + free_irq(self->pdata->irq, self); + } else { + sdio_claim_host(self->func); + ret = sdio_release_irq(self->func); + sdio_release_host(self->func); + } + return ret; +} + +static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata) +{ + if (pdata->reset) { + gpio_set_value(pdata->reset, 0); + msleep(30); /* Min is 2 * CLK32K cycles */ + gpio_free(pdata->reset); + } + + if (pdata->power_ctrl) + pdata->power_ctrl(pdata, false); + if (pdata->clk_ctrl) + pdata->clk_ctrl(pdata, false); + + return 0; +} + +static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata) +{ + /* Ensure I/Os are pulled low */ + if (pdata->reset) { + gpio_request(pdata->reset, "cw1200_wlan_reset"); + gpio_direction_output(pdata->reset, 0); + } + if (pdata->powerup) { + gpio_request(pdata->powerup, "cw1200_wlan_powerup"); + gpio_direction_output(pdata->powerup, 0); + } + if (pdata->reset || pdata->powerup) + msleep(10); /* Settle time? */ + + /* Enable 3v3 and 1v8 to hardware */ + if (pdata->power_ctrl) { + if (pdata->power_ctrl(pdata, true)) { + pr_err("power_ctrl() failed!\n"); + return -1; + } + } + + /* Enable CLK32K */ + if (pdata->clk_ctrl) { + if (pdata->clk_ctrl(pdata, true)) { + pr_err("clk_ctrl() failed!\n"); + return -1; + } + msleep(10); /* Delay until clock is stable for 2 cycles */ + } + + /* Enable POWERUP signal */ + if (pdata->powerup) { + gpio_set_value(pdata->powerup, 1); + msleep(250); /* or more..? */ + } + /* Enable RSTn signal */ + if (pdata->reset) { + gpio_set_value(pdata->reset, 1); + msleep(50); /* Or more..? */ + } + return 0; +} + +static size_t cw1200_sdio_align_size(struct hwbus_priv *self, size_t size) +{ + if (self->pdata->no_nptb) + size = round_up(size, SDIO_BLOCK_SIZE); + else + size = sdio_align_size(self->func, size); + + return size; +} + +static int cw1200_sdio_pm(struct hwbus_priv *self, bool suspend) +{ + int ret = 0; + + if (self->pdata->irq) + ret = irq_set_irq_wake(self->pdata->irq, suspend); + return ret; +} + +static struct hwbus_ops cw1200_sdio_hwbus_ops = { + .hwbus_memcpy_fromio = cw1200_sdio_memcpy_fromio, + .hwbus_memcpy_toio = cw1200_sdio_memcpy_toio, + .lock = cw1200_sdio_lock, + .unlock = cw1200_sdio_unlock, + .align_size = cw1200_sdio_align_size, + .power_mgmt = cw1200_sdio_pm, +}; + +/* Probe Function to be called by SDIO stack when device is discovered */ +static int cw1200_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct hwbus_priv *self; + int status; + + pr_info("cw1200_wlan_sdio: Probe called\n"); + + /* We are only able to handle the wlan function */ + if (func->num != 0x01) + return -ENODEV; + + self = kzalloc(sizeof(*self), GFP_KERNEL); + if (!self) { + pr_err("Can't allocate SDIO hwbus_priv.\n"); + return -ENOMEM; + } + + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; + + self->pdata = global_plat_data; /* FIXME */ + self->func = func; + sdio_set_drvdata(func, self); + sdio_claim_host(func); + sdio_enable_func(func); + sdio_release_host(func); + + status = cw1200_sdio_irq_subscribe(self); + + status = cw1200_core_probe(&cw1200_sdio_hwbus_ops, + self, &func->dev, &self->core, + self->pdata->ref_clk, + self->pdata->macaddr, + self->pdata->sdd_file, + self->pdata->have_5ghz); + if (status) { + cw1200_sdio_irq_unsubscribe(self); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + sdio_set_drvdata(func, NULL); + kfree(self); + } + + return status; +} + +/* Disconnect Function to be called by SDIO stack when + * device is disconnected + */ +static void cw1200_sdio_disconnect(struct sdio_func *func) +{ + struct hwbus_priv *self = sdio_get_drvdata(func); + + if (self) { + cw1200_sdio_irq_unsubscribe(self); + if (self->core) { + cw1200_core_release(self->core); + self->core = NULL; + } + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + sdio_set_drvdata(func, NULL); + kfree(self); + } +} + +#ifdef CONFIG_PM +static int cw1200_sdio_suspend(struct device *dev) +{ + int ret; + struct sdio_func *func = dev_to_sdio_func(dev); + struct hwbus_priv *self = sdio_get_drvdata(func); + + if (!cw1200_can_suspend(self->core)) + return -EAGAIN; + + /* Notify SDIO that CW1200 will remain powered during suspend */ + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) + pr_err("Error setting SDIO pm flags: %i\n", ret); + + return ret; +} + +static int cw1200_sdio_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops cw1200_pm_ops = { + .suspend = cw1200_sdio_suspend, + .resume = cw1200_sdio_resume, +}; +#endif + +static struct sdio_driver sdio_driver = { + .name = "cw1200_wlan_sdio", + .id_table = cw1200_sdio_ids, + .probe = cw1200_sdio_probe, + .remove = cw1200_sdio_disconnect, +#ifdef CONFIG_PM + .drv = { + .pm = &cw1200_pm_ops, + } +#endif +}; + +/* Init Module function -> Called by insmod */ +static int __init cw1200_sdio_init(void) +{ + const struct cw1200_platform_data_sdio *pdata; + int ret; + + /* FIXME -- this won't support multiple devices */ + pdata = global_plat_data; + + if (cw1200_sdio_on(pdata)) { + ret = -1; + goto err; + } + + ret = sdio_register_driver(&sdio_driver); + if (ret) + goto err; + + return 0; + +err: + cw1200_sdio_off(pdata); + return ret; +} + +/* Called at Driver Unloading */ +static void __exit cw1200_sdio_exit(void) +{ + const struct cw1200_platform_data_sdio *pdata; + + /* FIXME -- this won't support multiple devices */ + pdata = global_plat_data; + sdio_unregister_driver(&sdio_driver); + cw1200_sdio_off(pdata); +} + + +module_init(cw1200_sdio_init); +module_exit(cw1200_sdio_exit); diff --git a/drivers/net/wireless/st/cw1200/cw1200_spi.c b/drivers/net/wireless/st/cw1200/cw1200_spi.c new file mode 100644 index 000000000000..a740083634d8 --- /dev/null +++ b/drivers/net/wireless/st/cw1200/cw1200_spi.c @@ -0,0 +1,476 @@ +/* + * Mac80211 SPI driver for ST-Ericsson CW1200 device + * + * Copyright (c) 2011, Sagrad Inc. + * Author: Solomon Peachy + * + * Based on cw1200_sdio.c + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cw1200.h" +#include "hwbus.h" +#include +#include "hwio.h" + +MODULE_AUTHOR("Solomon Peachy "); +MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:cw1200_wlan_spi"); + +/* #define SPI_DEBUG */ + +struct hwbus_priv { + struct spi_device *func; + struct cw1200_common *core; + const struct cw1200_platform_data_spi *pdata; + spinlock_t lock; /* Serialize all bus operations */ + wait_queue_head_t wq; + int claimed; +}; + +#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2) +#define SET_WRITE 0x7FFF /* usage: and operation */ +#define SET_READ 0x8000 /* usage: or operation */ + +/* Notes on byte ordering: + LE: B0 B1 B2 B3 + BE: B3 B2 B1 B0 + + Hardware expects 32-bit data to be written as 16-bit BE words: + + B1 B0 B3 B2 +*/ + +static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self, + unsigned int addr, + void *dst, int count) +{ + int ret, i; + u16 regaddr; + struct spi_message m; + + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .rx_buf = dst, + .len = count, + }; + + regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; + regaddr |= SET_READ; + regaddr |= (count>>1); + +#ifdef SPI_DEBUG + pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, regaddr); +#endif + + /* Header is LE16 */ + regaddr = cpu_to_le16(regaddr); + + /* We have to byteswap if the SPI bus is limited to 8b operation + or we are running on a Big Endian system + */ +#if defined(__LITTLE_ENDIAN) + if (self->func->bits_per_word == 8) +#endif + regaddr = swab16(regaddr); + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + ret = spi_sync(self->func, &m); + +#ifdef SPI_DEBUG + pr_info("READ : "); + for (i = 0; i < t_addr.len; i++) + printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); + printk(" : "); + for (i = 0; i < t_msg.len; i++) + printk("%02x ", ((u8 *)t_msg.rx_buf)[i]); + printk("\n"); +#endif + + /* We have to byteswap if the SPI bus is limited to 8b operation + or we are running on a Big Endian system + */ +#if defined(__LITTLE_ENDIAN) + if (self->func->bits_per_word == 8) +#endif + { + uint16_t *buf = (uint16_t *)dst; + for (i = 0; i < ((count + 1) >> 1); i++) + buf[i] = swab16(buf[i]); + } + + return ret; +} + +static int cw1200_spi_memcpy_toio(struct hwbus_priv *self, + unsigned int addr, + const void *src, int count) +{ + int rval, i; + u16 regaddr; + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .tx_buf = src, + .len = count, + }; + struct spi_message m; + + regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; + regaddr &= SET_WRITE; + regaddr |= (count>>1); + +#ifdef SPI_DEBUG + pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr, regaddr); +#endif + + /* Header is LE16 */ + regaddr = cpu_to_le16(regaddr); + + /* We have to byteswap if the SPI bus is limited to 8b operation + or we are running on a Big Endian system + */ +#if defined(__LITTLE_ENDIAN) + if (self->func->bits_per_word == 8) +#endif + { + uint16_t *buf = (uint16_t *)src; + regaddr = swab16(regaddr); + for (i = 0; i < ((count + 1) >> 1); i++) + buf[i] = swab16(buf[i]); + } + +#ifdef SPI_DEBUG + pr_info("WRITE: "); + for (i = 0; i < t_addr.len; i++) + printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); + printk(" : "); + for (i = 0; i < t_msg.len; i++) + printk("%02x ", ((u8 *)t_msg.tx_buf)[i]); + printk("\n"); +#endif + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + rval = spi_sync(self->func, &m); + +#ifdef SPI_DEBUG + pr_info("WROTE: %d\n", m.actual_length); +#endif + +#if defined(__LITTLE_ENDIAN) + /* We have to byteswap if the SPI bus is limited to 8b operation */ + if (self->func->bits_per_word == 8) +#endif + { + uint16_t *buf = (uint16_t *)src; + for (i = 0; i < ((count + 1) >> 1); i++) + buf[i] = swab16(buf[i]); + } + return rval; +} + +static void cw1200_spi_lock(struct hwbus_priv *self) +{ + unsigned long flags; + + DECLARE_WAITQUEUE(wait, current); + + might_sleep(); + + add_wait_queue(&self->wq, &wait); + spin_lock_irqsave(&self->lock, flags); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (!self->claimed) + break; + spin_unlock_irqrestore(&self->lock, flags); + schedule(); + spin_lock_irqsave(&self->lock, flags); + } + set_current_state(TASK_RUNNING); + self->claimed = 1; + spin_unlock_irqrestore(&self->lock, flags); + remove_wait_queue(&self->wq, &wait); + + return; +} + +static void cw1200_spi_unlock(struct hwbus_priv *self) +{ + unsigned long flags; + + spin_lock_irqsave(&self->lock, flags); + self->claimed = 0; + spin_unlock_irqrestore(&self->lock, flags); + wake_up(&self->wq); + + return; +} + +static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id) +{ + struct hwbus_priv *self = dev_id; + + if (self->core) { + cw1200_spi_lock(self); + cw1200_irq_handler(self->core); + cw1200_spi_unlock(self); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +static int cw1200_spi_irq_subscribe(struct hwbus_priv *self) +{ + int ret; + + pr_debug("SW IRQ subscribe\n"); + + ret = request_threaded_irq(self->func->irq, NULL, + cw1200_spi_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "cw1200_wlan_irq", self); + if (WARN_ON(ret < 0)) + goto exit; + + ret = enable_irq_wake(self->func->irq); + if (WARN_ON(ret)) + goto free_irq; + + return 0; + +free_irq: + free_irq(self->func->irq, self); +exit: + return ret; +} + +static int cw1200_spi_irq_unsubscribe(struct hwbus_priv *self) +{ + int ret = 0; + + pr_debug("SW IRQ unsubscribe\n"); + disable_irq_wake(self->func->irq); + free_irq(self->func->irq, self); + + return ret; +} + +static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata) +{ + if (pdata->reset) { + gpio_set_value(pdata->reset, 0); + msleep(30); /* Min is 2 * CLK32K cycles */ + gpio_free(pdata->reset); + } + + if (pdata->power_ctrl) + pdata->power_ctrl(pdata, false); + if (pdata->clk_ctrl) + pdata->clk_ctrl(pdata, false); + + return 0; +} + +static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata) +{ + /* Ensure I/Os are pulled low */ + if (pdata->reset) { + gpio_request(pdata->reset, "cw1200_wlan_reset"); + gpio_direction_output(pdata->reset, 0); + } + if (pdata->powerup) { + gpio_request(pdata->powerup, "cw1200_wlan_powerup"); + gpio_direction_output(pdata->powerup, 0); + } + if (pdata->reset || pdata->powerup) + msleep(10); /* Settle time? */ + + /* Enable 3v3 and 1v8 to hardware */ + if (pdata->power_ctrl) { + if (pdata->power_ctrl(pdata, true)) { + pr_err("power_ctrl() failed!\n"); + return -1; + } + } + + /* Enable CLK32K */ + if (pdata->clk_ctrl) { + if (pdata->clk_ctrl(pdata, true)) { + pr_err("clk_ctrl() failed!\n"); + return -1; + } + msleep(10); /* Delay until clock is stable for 2 cycles */ + } + + /* Enable POWERUP signal */ + if (pdata->powerup) { + gpio_set_value(pdata->powerup, 1); + msleep(250); /* or more..? */ + } + /* Enable RSTn signal */ + if (pdata->reset) { + gpio_set_value(pdata->reset, 1); + msleep(50); /* Or more..? */ + } + return 0; +} + +static size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size) +{ + return size & 1 ? size + 1 : size; +} + +static int cw1200_spi_pm(struct hwbus_priv *self, bool suspend) +{ + return irq_set_irq_wake(self->func->irq, suspend); +} + +static struct hwbus_ops cw1200_spi_hwbus_ops = { + .hwbus_memcpy_fromio = cw1200_spi_memcpy_fromio, + .hwbus_memcpy_toio = cw1200_spi_memcpy_toio, + .lock = cw1200_spi_lock, + .unlock = cw1200_spi_unlock, + .align_size = cw1200_spi_align_size, + .power_mgmt = cw1200_spi_pm, +}; + +/* Probe Function to be called by SPI stack when device is discovered */ +static int cw1200_spi_probe(struct spi_device *func) +{ + const struct cw1200_platform_data_spi *plat_data = + dev_get_platdata(&func->dev); + struct hwbus_priv *self; + int status; + + /* Sanity check speed */ + if (func->max_speed_hz > 52000000) + func->max_speed_hz = 52000000; + if (func->max_speed_hz < 1000000) + func->max_speed_hz = 1000000; + + /* Fix up transfer size */ + if (plat_data->spi_bits_per_word) + func->bits_per_word = plat_data->spi_bits_per_word; + if (!func->bits_per_word) + func->bits_per_word = 16; + + /* And finally.. */ + func->mode = SPI_MODE_0; + + pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n", + func->chip_select, func->mode, func->bits_per_word, + func->max_speed_hz); + + if (cw1200_spi_on(plat_data)) { + pr_err("spi_on() failed!\n"); + return -1; + } + + if (spi_setup(func)) { + pr_err("spi_setup() failed!\n"); + return -1; + } + + self = devm_kzalloc(&func->dev, sizeof(*self), GFP_KERNEL); + if (!self) { + pr_err("Can't allocate SPI hwbus_priv."); + return -ENOMEM; + } + + self->pdata = plat_data; + self->func = func; + spin_lock_init(&self->lock); + + spi_set_drvdata(func, self); + + init_waitqueue_head(&self->wq); + + status = cw1200_spi_irq_subscribe(self); + + status = cw1200_core_probe(&cw1200_spi_hwbus_ops, + self, &func->dev, &self->core, + self->pdata->ref_clk, + self->pdata->macaddr, + self->pdata->sdd_file, + self->pdata->have_5ghz); + + if (status) { + cw1200_spi_irq_unsubscribe(self); + cw1200_spi_off(plat_data); + } + + return status; +} + +/* Disconnect Function to be called by SPI stack when device is disconnected */ +static int cw1200_spi_disconnect(struct spi_device *func) +{ + struct hwbus_priv *self = spi_get_drvdata(func); + + if (self) { + cw1200_spi_irq_unsubscribe(self); + if (self->core) { + cw1200_core_release(self->core); + self->core = NULL; + } + } + cw1200_spi_off(dev_get_platdata(&func->dev)); + + return 0; +} + +#ifdef CONFIG_PM +static int cw1200_spi_suspend(struct device *dev) +{ + struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev)); + + if (!cw1200_can_suspend(self->core)) + return -EAGAIN; + + /* XXX notify host that we have to keep CW1200 powered on? */ + return 0; +} + +static SIMPLE_DEV_PM_OPS(cw1200_pm_ops, cw1200_spi_suspend, NULL); + +#endif + +static struct spi_driver spi_driver = { + .probe = cw1200_spi_probe, + .remove = cw1200_spi_disconnect, + .driver = { + .name = "cw1200_wlan_spi", +#ifdef CONFIG_PM + .pm = &cw1200_pm_ops, +#endif + }, +}; + +module_spi_driver(spi_driver); diff --git a/drivers/net/wireless/st/cw1200/debug.c b/drivers/net/wireless/st/cw1200/debug.c new file mode 100644 index 000000000000..34f97c31eecf --- /dev/null +++ b/drivers/net/wireless/st/cw1200/debug.c @@ -0,0 +1,430 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * DebugFS code + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include "cw1200.h" +#include "debug.h" +#include "fwio.h" + +/* join_status */ +static const char * const cw1200_debug_join_status[] = { + "passive", + "monitor", + "station (joining)", + "station (not authenticated yet)", + "station", + "adhoc", + "access point", +}; + +/* WSM_JOIN_PREAMBLE_... */ +static const char * const cw1200_debug_preamble[] = { + "long", + "short", + "long on 1 and 2 Mbps", +}; + + +static const char * const cw1200_debug_link_id[] = { + "OFF", + "REQ", + "SOFT", + "HARD", + "RESET", + "RESET_REMAP", +}; + +static const char *cw1200_debug_mode(int mode) +{ + switch (mode) { + case NL80211_IFTYPE_UNSPECIFIED: + return "unspecified"; + case NL80211_IFTYPE_MONITOR: + return "monitor"; + case NL80211_IFTYPE_STATION: + return "station"; + case NL80211_IFTYPE_ADHOC: + return "adhoc"; + case NL80211_IFTYPE_MESH_POINT: + return "mesh point"; + case NL80211_IFTYPE_AP: + return "access point"; + case NL80211_IFTYPE_P2P_CLIENT: + return "p2p client"; + case NL80211_IFTYPE_P2P_GO: + return "p2p go"; + default: + return "unsupported"; + } +} + +static void cw1200_queue_status_show(struct seq_file *seq, + struct cw1200_queue *q) +{ + int i; + seq_printf(seq, "Queue %d:\n", q->queue_id); + seq_printf(seq, " capacity: %zu\n", q->capacity); + seq_printf(seq, " queued: %zu\n", q->num_queued); + seq_printf(seq, " pending: %zu\n", q->num_pending); + seq_printf(seq, " sent: %zu\n", q->num_sent); + seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no"); + seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no"); + seq_puts(seq, " link map: 0-> "); + for (i = 0; i < q->stats->map_capacity; ++i) + seq_printf(seq, "%.2d ", q->link_map_cache[i]); + seq_printf(seq, "<-%zu\n", q->stats->map_capacity); +} + +static void cw1200_debug_print_map(struct seq_file *seq, + struct cw1200_common *priv, + const char *label, + u32 map) +{ + int i; + seq_printf(seq, "%s0-> ", label); + for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i) + seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : ".."); + seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1); +} + +static int cw1200_status_show(struct seq_file *seq, void *v) +{ + int i; + struct list_head *item; + struct cw1200_common *priv = seq->private; + struct cw1200_debug_priv *d = priv->debug; + + seq_puts(seq, "CW1200 Wireless LAN driver status\n"); + seq_printf(seq, "Hardware: %d.%d\n", + priv->wsm_caps.hw_id, + priv->wsm_caps.hw_subid); + seq_printf(seq, "Firmware: %s %d.%d\n", + cw1200_fw_types[priv->wsm_caps.fw_type], + priv->wsm_caps.fw_ver, + priv->wsm_caps.fw_build); + seq_printf(seq, "FW API: %d\n", + priv->wsm_caps.fw_api); + seq_printf(seq, "FW caps: 0x%.4X\n", + priv->wsm_caps.fw_cap); + seq_printf(seq, "FW label: '%s'\n", + priv->wsm_caps.fw_label); + seq_printf(seq, "Mode: %s%s\n", + cw1200_debug_mode(priv->mode), + priv->listening ? " (listening)" : ""); + seq_printf(seq, "Join state: %s\n", + cw1200_debug_join_status[priv->join_status]); + if (priv->channel) + seq_printf(seq, "Channel: %d%s\n", + priv->channel->hw_value, + priv->channel_switch_in_progress ? + " (switching)" : ""); + if (priv->rx_filter.promiscuous) + seq_puts(seq, "Filter: promisc\n"); + else if (priv->rx_filter.fcs) + seq_puts(seq, "Filter: fcs\n"); + if (priv->rx_filter.bssid) + seq_puts(seq, "Filter: bssid\n"); + if (!priv->disable_beacon_filter) + seq_puts(seq, "Filter: beacons\n"); + + if (priv->enable_beacon || + priv->mode == NL80211_IFTYPE_AP || + priv->mode == NL80211_IFTYPE_ADHOC || + priv->mode == NL80211_IFTYPE_MESH_POINT || + priv->mode == NL80211_IFTYPE_P2P_GO) + seq_printf(seq, "Beaconing: %s\n", + priv->enable_beacon ? + "enabled" : "disabled"); + + for (i = 0; i < 4; ++i) + seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i, + priv->edca.params[i].cwmin, + priv->edca.params[i].cwmax, + priv->edca.params[i].aifns, + priv->edca.params[i].txop_limit, + priv->edca.params[i].max_rx_lifetime); + + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + static const char *pm_mode = "unknown"; + switch (priv->powersave_mode.mode) { + case WSM_PSM_ACTIVE: + pm_mode = "off"; + break; + case WSM_PSM_PS: + pm_mode = "on"; + break; + case WSM_PSM_FAST_PS: + pm_mode = "dynamic"; + break; + } + seq_printf(seq, "Preamble: %s\n", + cw1200_debug_preamble[priv->association_mode.preamble]); + seq_printf(seq, "AMPDU spcn: %d\n", + priv->association_mode.mpdu_start_spacing); + seq_printf(seq, "Basic rate: 0x%.8X\n", + le32_to_cpu(priv->association_mode.basic_rate_set)); + seq_printf(seq, "Bss lost: %d beacons\n", + priv->bss_params.beacon_lost_count); + seq_printf(seq, "AID: %d\n", + priv->bss_params.aid); + seq_printf(seq, "Rates: 0x%.8X\n", + priv->bss_params.operational_rate_set); + seq_printf(seq, "Powersave: %s\n", pm_mode); + } + seq_printf(seq, "HT: %s\n", + cw1200_is_ht(&priv->ht_info) ? "on" : "off"); + if (cw1200_is_ht(&priv->ht_info)) { + seq_printf(seq, "Greenfield: %s\n", + cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no"); + seq_printf(seq, "AMPDU dens: %d\n", + cw1200_ht_ampdu_density(&priv->ht_info)); + } + seq_printf(seq, "RSSI thold: %d\n", + priv->cqm_rssi_thold); + seq_printf(seq, "RSSI hyst: %d\n", + priv->cqm_rssi_hyst); + seq_printf(seq, "Long retr: %d\n", + priv->long_frame_max_tx_count); + seq_printf(seq, "Short retr: %d\n", + priv->short_frame_max_tx_count); + spin_lock_bh(&priv->tx_policy_cache.lock); + i = 0; + list_for_each(item, &priv->tx_policy_cache.used) + ++i; + spin_unlock_bh(&priv->tx_policy_cache.lock); + seq_printf(seq, "RC in use: %d\n", i); + + seq_puts(seq, "\n"); + for (i = 0; i < 4; ++i) { + cw1200_queue_status_show(seq, &priv->tx_queue[i]); + seq_puts(seq, "\n"); + } + + cw1200_debug_print_map(seq, priv, "Link map: ", + priv->link_id_map); + cw1200_debug_print_map(seq, priv, "Asleep map: ", + priv->sta_asleep_mask); + cw1200_debug_print_map(seq, priv, "PSPOLL map: ", + priv->pspoll_mask); + + seq_puts(seq, "\n"); + + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (priv->link_id_db[i].status) { + seq_printf(seq, "Link %d: %s, %pM\n", + i + 1, + cw1200_debug_link_id[priv->link_id_db[i].status], + priv->link_id_db[i].mac); + } + } + + seq_puts(seq, "\n"); + + seq_printf(seq, "BH status: %s\n", + atomic_read(&priv->bh_term) ? "terminated" : "alive"); + seq_printf(seq, "Pending RX: %d\n", + atomic_read(&priv->bh_rx)); + seq_printf(seq, "Pending TX: %d\n", + atomic_read(&priv->bh_tx)); + if (priv->bh_error) + seq_printf(seq, "BH errcode: %d\n", + priv->bh_error); + seq_printf(seq, "TX bufs: %d x %d bytes\n", + priv->wsm_caps.input_buffers, + priv->wsm_caps.input_buffer_size); + seq_printf(seq, "Used bufs: %d\n", + priv->hw_bufs_used); + seq_printf(seq, "Powermgmt: %s\n", + priv->powersave_enabled ? "on" : "off"); + seq_printf(seq, "Device: %s\n", + priv->device_can_sleep ? "asleep" : "awake"); + + spin_lock(&priv->wsm_cmd.lock); + seq_printf(seq, "WSM status: %s\n", + priv->wsm_cmd.done ? "idle" : "active"); + seq_printf(seq, "WSM cmd: 0x%.4X (%td bytes)\n", + priv->wsm_cmd.cmd, priv->wsm_cmd.len); + seq_printf(seq, "WSM retval: %d\n", + priv->wsm_cmd.ret); + spin_unlock(&priv->wsm_cmd.lock); + + seq_printf(seq, "Datapath: %s\n", + atomic_read(&priv->tx_lock) ? "locked" : "unlocked"); + if (atomic_read(&priv->tx_lock)) + seq_printf(seq, "TXlock cnt: %d\n", + atomic_read(&priv->tx_lock)); + + seq_printf(seq, "TXed: %d\n", + d->tx); + seq_printf(seq, "AGG TXed: %d\n", + d->tx_agg); + seq_printf(seq, "MULTI TXed: %d (%d)\n", + d->tx_multi, d->tx_multi_frames); + seq_printf(seq, "RXed: %d\n", + d->rx); + seq_printf(seq, "AGG RXed: %d\n", + d->rx_agg); + seq_printf(seq, "TX miss: %d\n", + d->tx_cache_miss); + seq_printf(seq, "TX align: %d\n", + d->tx_align); + seq_printf(seq, "TX burst: %d\n", + d->tx_burst); + seq_printf(seq, "TX TTL: %d\n", + d->tx_ttl); + seq_printf(seq, "Scan: %s\n", + atomic_read(&priv->scan.in_progress) ? "active" : "idle"); + + return 0; +} + +static int cw1200_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, &cw1200_status_show, + inode->i_private); +} + +static const struct file_operations fops_status = { + .open = cw1200_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int cw1200_counters_show(struct seq_file *seq, void *v) +{ + int ret; + struct cw1200_common *priv = seq->private; + struct wsm_mib_counters_table counters; + + ret = wsm_get_counters_table(priv, &counters); + if (ret) + return ret; + +#define PUT_COUNTER(tab, name) \ + seq_printf(seq, "%s:" tab "%d\n", #name, \ + __le32_to_cpu(counters.name)) + + PUT_COUNTER("\t\t", plcp_errors); + PUT_COUNTER("\t\t", fcs_errors); + PUT_COUNTER("\t\t", tx_packets); + PUT_COUNTER("\t\t", rx_packets); + PUT_COUNTER("\t\t", rx_packet_errors); + PUT_COUNTER("\t", rx_decryption_failures); + PUT_COUNTER("\t\t", rx_mic_failures); + PUT_COUNTER("\t", rx_no_key_failures); + PUT_COUNTER("\t", tx_multicast_frames); + PUT_COUNTER("\t", tx_frames_success); + PUT_COUNTER("\t", tx_frame_failures); + PUT_COUNTER("\t", tx_frames_retried); + PUT_COUNTER("\t", tx_frames_multi_retried); + PUT_COUNTER("\t", rx_frame_duplicates); + PUT_COUNTER("\t\t", rts_success); + PUT_COUNTER("\t\t", rts_failures); + PUT_COUNTER("\t\t", ack_failures); + PUT_COUNTER("\t", rx_multicast_frames); + PUT_COUNTER("\t", rx_frames_success); + PUT_COUNTER("\t", rx_cmac_icv_errors); + PUT_COUNTER("\t\t", rx_cmac_replays); + PUT_COUNTER("\t", rx_mgmt_ccmp_replays); + +#undef PUT_COUNTER + + return 0; +} + +static int cw1200_counters_open(struct inode *inode, struct file *file) +{ + return single_open(file, &cw1200_counters_show, + inode->i_private); +} + +static const struct file_operations fops_counters = { + .open = cw1200_counters_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static ssize_t cw1200_wsm_dumps(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + char buf[1]; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + + if (buf[0] == '1') + priv->wsm_enable_wsm_dumps = 1; + else + priv->wsm_enable_wsm_dumps = 0; + + return count; +} + +static const struct file_operations fops_wsm_dumps = { + .open = simple_open, + .write = cw1200_wsm_dumps, + .llseek = default_llseek, +}; + +int cw1200_debug_init(struct cw1200_common *priv) +{ + int ret = -ENOMEM; + struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), + GFP_KERNEL); + priv->debug = d; + if (!d) + return ret; + + d->debugfs_phy = debugfs_create_dir("cw1200", + priv->hw->wiphy->debugfsdir); + if (!d->debugfs_phy) + goto err; + + if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, + priv, &fops_status)) + goto err; + + if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy, + priv, &fops_counters)) + goto err; + + if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy, + priv, &fops_wsm_dumps)) + goto err; + + return 0; + +err: + priv->debug = NULL; + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + return ret; +} + +void cw1200_debug_release(struct cw1200_common *priv) +{ + struct cw1200_debug_priv *d = priv->debug; + if (d) { + debugfs_remove_recursive(d->debugfs_phy); + priv->debug = NULL; + kfree(d); + } +} diff --git a/drivers/net/wireless/st/cw1200/debug.h b/drivers/net/wireless/st/cw1200/debug.h new file mode 100644 index 000000000000..b525aba53bfc --- /dev/null +++ b/drivers/net/wireless/st/cw1200/debug.h @@ -0,0 +1,93 @@ +/* + * DebugFS code for ST-Ericsson CW1200 mac80211 driver + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_DEBUG_H_INCLUDED +#define CW1200_DEBUG_H_INCLUDED + +struct cw1200_debug_priv { + struct dentry *debugfs_phy; + int tx; + int tx_agg; + int rx; + int rx_agg; + int tx_multi; + int tx_multi_frames; + int tx_cache_miss; + int tx_align; + int tx_ttl; + int tx_burst; + int ba_cnt; + int ba_acc; + int ba_cnt_rx; + int ba_acc_rx; +}; + +int cw1200_debug_init(struct cw1200_common *priv); +void cw1200_debug_release(struct cw1200_common *priv); + +static inline void cw1200_debug_txed(struct cw1200_common *priv) +{ + ++priv->debug->tx; +} + +static inline void cw1200_debug_txed_agg(struct cw1200_common *priv) +{ + ++priv->debug->tx_agg; +} + +static inline void cw1200_debug_txed_multi(struct cw1200_common *priv, + int count) +{ + ++priv->debug->tx_multi; + priv->debug->tx_multi_frames += count; +} + +static inline void cw1200_debug_rxed(struct cw1200_common *priv) +{ + ++priv->debug->rx; +} + +static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv) +{ + ++priv->debug->rx_agg; +} + +static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv) +{ + ++priv->debug->tx_cache_miss; +} + +static inline void cw1200_debug_tx_align(struct cw1200_common *priv) +{ + ++priv->debug->tx_align; +} + +static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) +{ + ++priv->debug->tx_ttl; +} + +static inline void cw1200_debug_tx_burst(struct cw1200_common *priv) +{ + ++priv->debug->tx_burst; +} + +static inline void cw1200_debug_ba(struct cw1200_common *priv, + int ba_cnt, int ba_acc, + int ba_cnt_rx, int ba_acc_rx) +{ + priv->debug->ba_cnt = ba_cnt; + priv->debug->ba_acc = ba_acc; + priv->debug->ba_cnt_rx = ba_cnt_rx; + priv->debug->ba_acc_rx = ba_acc_rx; +} + +#endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/net/wireless/st/cw1200/fwio.c b/drivers/net/wireless/st/cw1200/fwio.c new file mode 100644 index 000000000000..30e7646d04af --- /dev/null +++ b/drivers/net/wireless/st/cw1200/fwio.c @@ -0,0 +1,526 @@ +/* + * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "cw1200.h" +#include "fwio.h" +#include "hwio.h" +#include "hwbus.h" +#include "bh.h" + +static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision) +{ + int hw_type = -1; + u32 silicon_type = (config_reg_val >> 24) & 0x7; + u32 silicon_vers = (config_reg_val >> 31) & 0x1; + + switch (silicon_type) { + case 0x00: + *major_revision = 1; + hw_type = HIF_9000_SILICON_VERSATILE; + break; + case 0x01: + case 0x02: /* CW1x00 */ + case 0x04: /* CW1x60 */ + *major_revision = silicon_type; + if (silicon_vers) + hw_type = HIF_8601_VERSATILE; + else + hw_type = HIF_8601_SILICON; + break; + default: + break; + } + + return hw_type; +} + +static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) +{ + int ret, block, num_blocks; + unsigned i; + u32 val32; + u32 put = 0, get = 0; + u8 *buf = NULL; + const char *fw_path; + const struct firmware *firmware = NULL; + + /* Macroses are local. */ +#define APB_WRITE(reg, val) \ + do { \ + ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \ + if (ret < 0) \ + goto exit; \ + } while (0) +#define APB_WRITE2(reg, val) \ + do { \ + ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \ + if (ret < 0) \ + goto free_buffer; \ + } while (0) +#define APB_READ(reg, val) \ + do { \ + ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \ + if (ret < 0) \ + goto free_buffer; \ + } while (0) +#define REG_WRITE(reg, val) \ + do { \ + ret = cw1200_reg_write_32(priv, (reg), (val)); \ + if (ret < 0) \ + goto exit; \ + } while (0) +#define REG_READ(reg, val) \ + do { \ + ret = cw1200_reg_read_32(priv, (reg), &(val)); \ + if (ret < 0) \ + goto exit; \ + } while (0) + + switch (priv->hw_revision) { + case CW1200_HW_REV_CUT10: + fw_path = FIRMWARE_CUT10; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_10; + break; + case CW1200_HW_REV_CUT11: + fw_path = FIRMWARE_CUT11; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_11; + break; + case CW1200_HW_REV_CUT20: + fw_path = FIRMWARE_CUT20; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_20; + break; + case CW1200_HW_REV_CUT22: + fw_path = FIRMWARE_CUT22; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_22; + break; + case CW1X60_HW_REV: + fw_path = FIRMWARE_CW1X60; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_CW1X60; + break; + default: + pr_err("Invalid silicon revision %d.\n", priv->hw_revision); + return -EINVAL; + } + + /* Initialize common registers */ + APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); + APB_WRITE(DOWNLOAD_PUT_REG, 0); + APB_WRITE(DOWNLOAD_GET_REG, 0); + APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); + APB_WRITE(DOWNLOAD_FLAGS_REG, 0); + + /* Write the NOP Instruction */ + REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); + REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); + + /* Release CPU from RESET */ + REG_READ(ST90TDS_CONFIG_REG_ID, val32); + val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT; + REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); + + /* Enable Clock */ + val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT; + REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); + + /* Load a firmware file */ + ret = request_firmware(&firmware, fw_path, priv->pdev); + if (ret) { + pr_err("Can't load firmware file %s.\n", fw_path); + goto exit; + } + + buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); + if (!buf) { + pr_err("Can't allocate firmware load buffer.\n"); + ret = -ENOMEM; + goto firmware_release; + } + + /* Check if the bootloader is ready */ + for (i = 0; i < 100; i += 1 + i / 2) { + APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32); + if (val32 == DOWNLOAD_I_AM_HERE) + break; + mdelay(i); + } /* End of for loop */ + + if (val32 != DOWNLOAD_I_AM_HERE) { + pr_err("Bootloader is not ready.\n"); + ret = -ETIMEDOUT; + goto free_buffer; + } + + /* Calculcate number of download blocks */ + num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1; + + /* Updating the length in Download Ctrl Area */ + val32 = firmware->size; /* Explicit cast from size_t to u32 */ + APB_WRITE2(DOWNLOAD_IMAGE_SIZE_REG, val32); + + /* Firmware downloading loop */ + for (block = 0; block < num_blocks; block++) { + size_t tx_size; + size_t block_size; + + /* check the download status */ + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) { + pr_err("Bootloader reported error %d.\n", val32); + ret = -EIO; + goto free_buffer; + } + + /* loop until put - get <= 24K */ + for (i = 0; i < 100; i++) { + APB_READ(DOWNLOAD_GET_REG, get); + if ((put - get) <= + (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) + break; + mdelay(i); + } + + if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) { + pr_err("Timeout waiting for FIFO.\n"); + ret = -ETIMEDOUT; + goto free_buffer; + } + + /* calculate the block size */ + tx_size = block_size = min_t(size_t, firmware->size - put, + DOWNLOAD_BLOCK_SIZE); + + memcpy(buf, &firmware->data[put], block_size); + if (block_size < DOWNLOAD_BLOCK_SIZE) { + memset(&buf[block_size], 0, + DOWNLOAD_BLOCK_SIZE - block_size); + tx_size = DOWNLOAD_BLOCK_SIZE; + } + + /* send the block to sram */ + ret = cw1200_apb_write(priv, + CW1200_APB(DOWNLOAD_FIFO_OFFSET + + (put & (DOWNLOAD_FIFO_SIZE - 1))), + buf, tx_size); + if (ret < 0) { + pr_err("Can't write firmware block @ %d!\n", + put & (DOWNLOAD_FIFO_SIZE - 1)); + goto free_buffer; + } + + /* update the put register */ + put += block_size; + APB_WRITE2(DOWNLOAD_PUT_REG, put); + } /* End of firmware download loop */ + + /* Wait for the download completion */ + for (i = 0; i < 300; i += 1 + i / 2) { + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) + break; + mdelay(i); + } + if (val32 != DOWNLOAD_SUCCESS) { + pr_err("Wait for download completion failed: 0x%.8X\n", val32); + ret = -ETIMEDOUT; + goto free_buffer; + } else { + pr_info("Firmware download completed.\n"); + ret = 0; + } + +free_buffer: + kfree(buf); +firmware_release: + release_firmware(firmware); +exit: + return ret; + +#undef APB_WRITE +#undef APB_WRITE2 +#undef APB_READ +#undef REG_WRITE +#undef REG_READ +} + + +static int config_reg_read(struct cw1200_common *priv, u32 *val) +{ + switch (priv->hw_type) { + case HIF_9000_SILICON_VERSATILE: { + u16 val16; + int ret = cw1200_reg_read_16(priv, + ST90TDS_CONFIG_REG_ID, + &val16); + if (ret < 0) + return ret; + *val = val16; + return 0; + } + case HIF_8601_VERSATILE: + case HIF_8601_SILICON: + default: + cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val); + break; + } + return 0; +} + +static int config_reg_write(struct cw1200_common *priv, u32 val) +{ + switch (priv->hw_type) { + case HIF_9000_SILICON_VERSATILE: + return cw1200_reg_write_16(priv, + ST90TDS_CONFIG_REG_ID, + (u16)val); + case HIF_8601_VERSATILE: + case HIF_8601_SILICON: + default: + return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val); + } + return 0; +} + +int cw1200_load_firmware(struct cw1200_common *priv) +{ + int ret; + int i; + u32 val32; + u16 val16; + int major_revision = -1; + + /* Read CONFIG Register */ + ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto out; + } + + if (val32 == 0 || val32 == 0xffffffff) { + pr_err("Bad config register value (0x%08x)\n", val32); + ret = -EIO; + goto out; + } + + priv->hw_type = cw1200_get_hw_type(val32, &major_revision); + if (priv->hw_type < 0) { + pr_err("Can't deduce hardware type.\n"); + ret = -ENOTSUPP; + goto out; + } + + /* Set DPLL Reg value, and read back to confirm writes work */ + ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, + cw1200_dpll_from_clk(priv->hw_refclk)); + if (ret < 0) { + pr_err("Can't write DPLL register.\n"); + goto out; + } + + msleep(20); + + ret = cw1200_reg_read_32(priv, + ST90TDS_TSET_GEN_R_W_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read DPLL register.\n"); + goto out; + } + + if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) { + pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n", + cw1200_dpll_from_clk(priv->hw_refclk), val32); + ret = -EIO; + goto out; + } + + /* Set wakeup bit in device */ + ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16); + if (ret < 0) { + pr_err("set_wakeup: can't read control register.\n"); + goto out; + } + + ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, + val16 | ST90TDS_CONT_WUP_BIT); + if (ret < 0) { + pr_err("set_wakeup: can't write control register.\n"); + goto out; + } + + /* Wait for wakeup */ + for (i = 0; i < 300; i += (1 + i / 2)) { + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, &val16); + if (ret < 0) { + pr_err("wait_for_wakeup: can't read control register.\n"); + goto out; + } + + if (val16 & ST90TDS_CONT_RDY_BIT) + break; + + msleep(i); + } + + if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) { + pr_err("wait_for_wakeup: device is not responding.\n"); + ret = -ETIMEDOUT; + goto out; + } + + switch (major_revision) { + case 1: + /* CW1200 Hardware detection logic : Check for CUT1.1 */ + ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32); + if (ret) { + pr_err("HW detection: can't read CUT ID.\n"); + goto out; + } + + switch (val32) { + case CW1200_CUT_11_ID_STR: + pr_info("CW1x00 Cut 1.1 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT11; + break; + default: + pr_info("CW1x00 Cut 1.0 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT10; + break; + } + + /* According to ST-E, CUT<2.0 has busted BA TID0-3. + Just disable it entirely... + */ + priv->ba_rx_tid_mask = 0; + priv->ba_tx_tid_mask = 0; + break; + case 2: { + u32 ar1, ar2, ar3; + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1); + if (ret) { + pr_err("(1) HW detection: can't read CUT ID\n"); + goto out; + } + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2); + if (ret) { + pr_err("(2) HW detection: can't read CUT ID.\n"); + goto out; + } + + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3); + if (ret) { + pr_err("(3) HW detection: can't read CUT ID.\n"); + goto out; + } + + if (ar1 == CW1200_CUT_22_ID_STR1 && + ar2 == CW1200_CUT_22_ID_STR2 && + ar3 == CW1200_CUT_22_ID_STR3) { + pr_info("CW1x00 Cut 2.2 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT22; + } else { + pr_info("CW1x00 Cut 2.0 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT20; + } + break; + } + case 4: + pr_info("CW1x60 silicon detected.\n"); + priv->hw_revision = CW1X60_HW_REV; + break; + default: + pr_err("Unsupported silicon major revision %d.\n", + major_revision); + ret = -ENOTSUPP; + goto out; + } + + /* Checking for access mode */ + ret = config_reg_read(priv, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto out; + } + + if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) { + pr_err("Device is already in QUEUE mode!\n"); + ret = -EINVAL; + goto out; + } + + switch (priv->hw_type) { + case HIF_8601_SILICON: + if (priv->hw_revision == CW1X60_HW_REV) { + pr_err("Can't handle CW1160/1260 firmware load yet.\n"); + ret = -ENOTSUPP; + goto out; + } + ret = cw1200_load_firmware_cw1200(priv); + break; + default: + pr_err("Can't perform firmware load for hw type %d.\n", + priv->hw_type); + ret = -ENOTSUPP; + goto out; + } + if (ret < 0) { + pr_err("Firmware load error.\n"); + goto out; + } + + /* Enable interrupt signalling */ + priv->hwbus_ops->lock(priv->hwbus_priv); + ret = __cw1200_irq_enable(priv, 1); + priv->hwbus_ops->unlock(priv->hwbus_priv); + if (ret < 0) + goto unsubscribe; + + /* Configure device for MESSSAGE MODE */ + ret = config_reg_read(priv, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto unsubscribe; + } + ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT); + if (ret < 0) { + pr_err("Can't write config register.\n"); + goto unsubscribe; + } + + /* Unless we read the CONFIG Register we are + * not able to get an interrupt + */ + mdelay(10); + config_reg_read(priv, &val32); + +out: + return ret; + +unsubscribe: + /* Disable interrupt signalling */ + priv->hwbus_ops->lock(priv->hwbus_priv); + ret = __cw1200_irq_enable(priv, 0); + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} diff --git a/drivers/net/wireless/st/cw1200/fwio.h b/drivers/net/wireless/st/cw1200/fwio.h new file mode 100644 index 000000000000..ea3099362cdf --- /dev/null +++ b/drivers/net/wireless/st/cw1200/fwio.h @@ -0,0 +1,39 @@ +/* + * Firmware API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FWIO_H_INCLUDED +#define FWIO_H_INCLUDED + +#define BOOTLOADER_CW1X60 "boot_cw1x60.bin" +#define FIRMWARE_CW1X60 "wsm_cw1x60.bin" +#define FIRMWARE_CUT22 "wsm_22.bin" +#define FIRMWARE_CUT20 "wsm_20.bin" +#define FIRMWARE_CUT11 "wsm_11.bin" +#define FIRMWARE_CUT10 "wsm_10.bin" +#define SDD_FILE_CW1X60 "sdd_cw1x60.bin" +#define SDD_FILE_22 "sdd_22.bin" +#define SDD_FILE_20 "sdd_20.bin" +#define SDD_FILE_11 "sdd_11.bin" +#define SDD_FILE_10 "sdd_10.bin" + +int cw1200_load_firmware(struct cw1200_common *priv); + +/* SDD definitions */ +#define SDD_PTA_CFG_ELT_ID 0xEB +#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5 +u32 cw1200_dpll_from_clk(u16 clk); + +#endif diff --git a/drivers/net/wireless/st/cw1200/hwbus.h b/drivers/net/wireless/st/cw1200/hwbus.h new file mode 100644 index 000000000000..8b2fc831c3de --- /dev/null +++ b/drivers/net/wireless/st/cw1200/hwbus.h @@ -0,0 +1,33 @@ +/* + * Common hwbus abstraction layer interface for cw1200 wireless driver + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_HWBUS_H +#define CW1200_HWBUS_H + +struct hwbus_priv; + +void cw1200_irq_handler(struct cw1200_common *priv); + +/* This MUST be wrapped with hwbus_ops->lock/unlock! */ +int __cw1200_irq_enable(struct cw1200_common *priv, int enable); + +struct hwbus_ops { + int (*hwbus_memcpy_fromio)(struct hwbus_priv *self, unsigned int addr, + void *dst, int count); + int (*hwbus_memcpy_toio)(struct hwbus_priv *self, unsigned int addr, + const void *src, int count); + void (*lock)(struct hwbus_priv *self); + void (*unlock)(struct hwbus_priv *self); + size_t (*align_size)(struct hwbus_priv *self, size_t size); + int (*power_mgmt)(struct hwbus_priv *self, bool suspend); +}; + +#endif /* CW1200_HWBUS_H */ diff --git a/drivers/net/wireless/st/cw1200/hwio.c b/drivers/net/wireless/st/cw1200/hwio.c new file mode 100644 index 000000000000..ff230b7aeedd --- /dev/null +++ b/drivers/net/wireless/st/cw1200/hwio.c @@ -0,0 +1,312 @@ +/* + * Low-level device IO routines for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver, which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include "cw1200.h" +#include "hwio.h" +#include "hwbus.h" + + /* Sdio addr is 4*spi_addr */ +#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2) +#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \ + ((((buf_id) & 0x1F) << 7) \ + | (((mpf) & 1) << 6) \ + | (((rfu) & 1) << 5) \ + | (((reg_id_ofs) & 0x1F) << 0)) +#define MAX_RETRY 3 + + +static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr, + void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit; + + /* Check if buffer is aligned to 4 byte boundary */ + if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { + pr_err("buffer is not aligned.\n"); + return -EINVAL; + } + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + return priv->hwbus_ops->hwbus_memcpy_fromio(priv->hwbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr, + const void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit; + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + return priv->hwbus_ops->hwbus_memcpy_toio(priv->hwbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static inline int __cw1200_reg_read_32(struct cw1200_common *priv, + u16 addr, u32 *val) +{ + __le32 tmp; + int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0); + *val = le32_to_cpu(tmp); + return i; +} + +static inline int __cw1200_reg_write_32(struct cw1200_common *priv, + u16 addr, u32 val) +{ + __le32 tmp = cpu_to_le32(val); + return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0); +} + +static inline int __cw1200_reg_read_16(struct cw1200_common *priv, + u16 addr, u16 *val) +{ + __le16 tmp; + int i = __cw1200_reg_read(priv, addr, &tmp, sizeof(tmp), 0); + *val = le16_to_cpu(tmp); + return i; +} + +static inline int __cw1200_reg_write_16(struct cw1200_common *priv, + u16 addr, u16 val) +{ + __le16 tmp = cpu_to_le16(val); + return __cw1200_reg_write(priv, addr, &tmp, sizeof(tmp), 0); +} + +int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf, + size_t buf_len) +{ + int ret; + priv->hwbus_ops->lock(priv->hwbus_priv); + ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0); + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf, + size_t buf_len) +{ + int ret; + priv->hwbus_ops->lock(priv->hwbus_priv); + ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0); + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len) +{ + int ret, retry = 1; + int buf_id_rx = priv->buf_id_rx; + + priv->hwbus_ops->lock(priv->hwbus_priv); + + while (retry <= MAX_RETRY) { + ret = __cw1200_reg_read(priv, + ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_rx + 1); + if (!ret) { + buf_id_rx = (buf_id_rx + 1) & 3; + priv->buf_id_rx = buf_id_rx; + break; + } else { + retry++; + mdelay(1); + pr_err("error :[%d]\n", ret); + } + } + + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int cw1200_data_write(struct cw1200_common *priv, const void *buf, + size_t buf_len) +{ + int ret, retry = 1; + int buf_id_tx = priv->buf_id_tx; + + priv->hwbus_ops->lock(priv->hwbus_priv); + + while (retry <= MAX_RETRY) { + ret = __cw1200_reg_write(priv, + ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_tx); + if (!ret) { + buf_id_tx = (buf_id_tx + 1) & 31; + priv->buf_id_tx = buf_id_tx; + break; + } else { + retry++; + mdelay(1); + pr_err("error :[%d]\n", ret); + } + } + + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr) +{ + u32 val32 = 0; + int i, ret; + + if ((buf_len / 2) >= 0x1000) { + pr_err("Can't read more than 0xfff words.\n"); + return -EINVAL; + } + + priv->hwbus_ops->lock(priv->hwbus_priv); + /* Write address */ + ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + pr_err("Can't write address register.\n"); + goto out; + } + + /* Read CONFIG Register Value - We will read 32 bits */ + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto out; + } + + /* Set PREFETCH bit */ + ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, + val32 | prefetch); + if (ret < 0) { + pr_err("Can't write prefetch bit.\n"); + goto out; + } + + /* Check for PRE-FETCH bit to be cleared */ + for (i = 0; i < 20; i++) { + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't check prefetch bit.\n"); + goto out; + } + if (!(val32 & prefetch)) + break; + + mdelay(i); + } + + if (val32 & prefetch) { + pr_err("Prefetch bit is not cleared.\n"); + goto out; + } + + /* Read data port */ + ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0); + if (ret < 0) { + pr_err("Can't read data port.\n"); + goto out; + } + +out: + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, + size_t buf_len) +{ + int ret; + + if ((buf_len / 2) >= 0x1000) { + pr_err("Can't write more than 0xfff words.\n"); + return -EINVAL; + } + + priv->hwbus_ops->lock(priv->hwbus_priv); + + /* Write address */ + ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + pr_err("Can't write address register.\n"); + goto out; + } + + /* Write data port */ + ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID, + buf, buf_len, 0); + if (ret < 0) { + pr_err("Can't write data port.\n"); + goto out; + } + +out: + priv->hwbus_ops->unlock(priv->hwbus_priv); + return ret; +} + +int __cw1200_irq_enable(struct cw1200_common *priv, int enable) +{ + u32 val32; + u16 val16; + int ret; + + if (HIF_8601_SILICON == priv->hw_type) { + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + return ret; + } + + if (enable) + val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE; + else + val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE; + + ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32); + if (ret < 0) { + pr_err("Can't write config register.\n"); + return ret; + } + } else { + ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16); + if (ret < 0) { + pr_err("Can't read control register.\n"); + return ret; + } + + if (enable) + val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE; + else + val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE; + + ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16); + if (ret < 0) { + pr_err("Can't write control register.\n"); + return ret; + } + } + return 0; +} diff --git a/drivers/net/wireless/st/cw1200/hwio.h b/drivers/net/wireless/st/cw1200/hwio.h new file mode 100644 index 000000000000..ddf52669dc5b --- /dev/null +++ b/drivers/net/wireless/st/cw1200/hwio.h @@ -0,0 +1,247 @@ +/* + * Low-level API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_HWIO_H_INCLUDED +#define CW1200_HWIO_H_INCLUDED + +/* extern */ struct cw1200_common; + +#define CW1200_CUT_11_ID_STR (0x302E3830) +#define CW1200_CUT_22_ID_STR1 (0x302e3132) +#define CW1200_CUT_22_ID_STR2 (0x32302e30) +#define CW1200_CUT_22_ID_STR3 (0x3335) +#define CW1200_CUT_ID_ADDR (0xFFF17F90) +#define CW1200_CUT2_ID_ADDR (0xFFF1FF90) + +/* Download control area */ +/* boot loader start address in SRAM */ +#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000) +/* 32K, 0x4000 to 0xDFFF */ +#define DOWNLOAD_FIFO_OFFSET (0x00004000) +/* 32K */ +#define DOWNLOAD_FIFO_SIZE (0x00008000) +/* 128 bytes, 0xFF80 to 0xFFFF */ +#define DOWNLOAD_CTRL_OFFSET (0x0000FF80) +#define DOWNLOAD_CTRL_DATA_DWORDS (32-6) + +struct download_cntl_t { + /* size of whole firmware file (including Cheksum), host init */ + u32 image_size; + /* downloading flags */ + u32 flags; + /* No. of bytes put into the download, init & updated by host */ + u32 put; + /* last traced program counter, last ARM reg_pc */ + u32 trace_pc; + /* No. of bytes read from the download, host init, device updates */ + u32 get; + /* r0, boot losader status, host init to pending, device updates */ + u32 status; + /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */ + u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS]; +}; + +#define DOWNLOAD_IMAGE_SIZE_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size)) +#define DOWNLOAD_FLAGS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags)) +#define DOWNLOAD_PUT_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put)) +#define DOWNLOAD_TRACE_PC_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc)) +#define DOWNLOAD_GET_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get)) +#define DOWNLOAD_STATUS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status)) +#define DOWNLOAD_DEBUG_DATA_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data)) +#define DOWNLOAD_DEBUG_DATA_LEN (108) + +#define DOWNLOAD_BLOCK_SIZE (1024) + +/* For boot loader detection */ +#define DOWNLOAD_ARE_YOU_HERE (0x87654321) +#define DOWNLOAD_I_AM_HERE (0x12345678) + +/* Download error code */ +#define DOWNLOAD_PENDING (0xFFFFFFFF) +#define DOWNLOAD_SUCCESS (0) +#define DOWNLOAD_EXCEPTION (1) +#define DOWNLOAD_ERR_MEM_1 (2) +#define DOWNLOAD_ERR_MEM_2 (3) +#define DOWNLOAD_ERR_SOFTWARE (4) +#define DOWNLOAD_ERR_FILE_SIZE (5) +#define DOWNLOAD_ERR_CHECKSUM (6) +#define DOWNLOAD_ERR_OVERFLOW (7) +#define DOWNLOAD_ERR_IMAGE (8) +#define DOWNLOAD_ERR_HOST (9) +#define DOWNLOAD_ERR_ABORT (10) + + +#define SYS_BASE_ADDR_SILICON (0) +#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000) +#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON) + +#define CW1200_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr)) + +/* Device register definitions */ + +/* WBF - SPI Register Addresses */ +#define ST90TDS_ADDR_ID_BASE (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONFIG_REG_ID (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONTROL_REG_ID (0x0001) +/* 16 bits, Q mode W/R */ +#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002) +/* 32 bits, AHB bus R/W */ +#define ST90TDS_AHB_DPORT_REG_ID (0x0003) +/* 16/32 bits */ +#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004) +/* 32 bits, APB bus R/W */ +#define ST90TDS_SRAM_DPORT_REG_ID (0x0005) +/* 32 bits, t_settle/general */ +#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006) +/* 16 bits, Q mode read, no length */ +#define ST90TDS_FRAME_OUT_REG_ID (0x0007) +#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID) + +/* WBF - Control register bit set */ +/* next o/p length, bit 11 to 0 */ +#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF) +#define ST90TDS_CONT_WUP_BIT (BIT(12)) +#define ST90TDS_CONT_RDY_BIT (BIT(13)) +#define ST90TDS_CONT_IRQ_ENABLE (BIT(14)) +#define ST90TDS_CONT_RDY_ENABLE (BIT(15)) +#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15)) + +/* SPI Config register bit set */ +#define ST90TDS_CONFIG_FRAME_BIT (BIT(2)) +#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4)) +#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3)) +#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4)) +#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5)) +#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6)) +#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7)) +/* TBD: Sure??? */ +#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7)) +#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8)) +#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9)) +/* QueueM */ +#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10)) +/* AHB bus */ +#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11)) +#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12)) +/* APB bus */ +#define ST90TDS_CONFIG_PRFETCH_BIT (BIT(13)) +/* cpu reset */ +#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14)) +#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15)) + +/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */ +#define ST90TDS_CONF_IRQ_ENABLE (BIT(16)) +#define ST90TDS_CONF_RDY_ENABLE (BIT(17)) +#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17)) + +int cw1200_data_read(struct cw1200_common *priv, + void *buf, size_t buf_len); +int cw1200_data_write(struct cw1200_common *priv, + const void *buf, size_t buf_len); + +int cw1200_reg_read(struct cw1200_common *priv, u16 addr, + void *buf, size_t buf_len); +int cw1200_reg_write(struct cw1200_common *priv, u16 addr, + const void *buf, size_t buf_len); + +static inline int cw1200_reg_read_16(struct cw1200_common *priv, + u16 addr, u16 *val) +{ + __le32 tmp; + int i; + i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp)); + *val = le32_to_cpu(tmp) & 0xfffff; + return i; +} + +static inline int cw1200_reg_write_16(struct cw1200_common *priv, + u16 addr, u16 val) +{ + __le32 tmp = cpu_to_le32((u32)val); + return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp)); +} + +static inline int cw1200_reg_read_32(struct cw1200_common *priv, + u16 addr, u32 *val) +{ + __le32 tmp; + int i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp)); + *val = le32_to_cpu(tmp); + return i; +} + +static inline int cw1200_reg_write_32(struct cw1200_common *priv, + u16 addr, u32 val) +{ + __le32 tmp = cpu_to_le32(val); + return cw1200_reg_write(priv, addr, &tmp, sizeof(val)); +} + +int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr); +int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, + size_t buf_len); + +static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr, + void *buf, size_t buf_len) +{ + return cw1200_indirect_read(priv, addr, buf, buf_len, + ST90TDS_CONFIG_PRFETCH_BIT, + ST90TDS_SRAM_DPORT_REG_ID); +} + +static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr, + void *buf, size_t buf_len) +{ + return cw1200_indirect_read(priv, addr, buf, buf_len, + ST90TDS_CONFIG_AHB_PRFETCH_BIT, + ST90TDS_AHB_DPORT_REG_ID); +} + +static inline int cw1200_apb_read_32(struct cw1200_common *priv, + u32 addr, u32 *val) +{ + __le32 tmp; + int i = cw1200_apb_read(priv, addr, &tmp, sizeof(tmp)); + *val = le32_to_cpu(tmp); + return i; +} + +static inline int cw1200_apb_write_32(struct cw1200_common *priv, + u32 addr, u32 val) +{ + __le32 tmp = cpu_to_le32(val); + return cw1200_apb_write(priv, addr, &tmp, sizeof(val)); +} +static inline int cw1200_ahb_read_32(struct cw1200_common *priv, + u32 addr, u32 *val) +{ + __le32 tmp; + int i = cw1200_ahb_read(priv, addr, &tmp, sizeof(tmp)); + *val = le32_to_cpu(tmp); + return i; +} + +#endif /* CW1200_HWIO_H_INCLUDED */ diff --git a/drivers/net/wireless/st/cw1200/main.c b/drivers/net/wireless/st/cw1200/main.c new file mode 100644 index 000000000000..0e51e27d2e3f --- /dev/null +++ b/drivers/net/wireless/st/cw1200/main.c @@ -0,0 +1,601 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2007-2009, Christian Lamparter + * Copyright 2008, Johannes Berg + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cw1200.h" +#include "txrx.h" +#include "hwbus.h" +#include "fwio.h" +#include "hwio.h" +#include "bh.h" +#include "sta.h" +#include "scan.h" +#include "debug.h" +#include "pm.h" + +MODULE_AUTHOR("Dmitry Tarnyagin "); +MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("cw1200_core"); + +/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */ +static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00}; +module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(macaddr, "Override platform_data MAC address"); + +static char *cw1200_sdd_path; +module_param(cw1200_sdd_path, charp, 0644); +MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file"); +static int cw1200_refclk; +module_param(cw1200_refclk, int, 0644); +MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock"); + +int cw1200_power_mode = wsm_power_mode_quiescent; +module_param(cw1200_power_mode, int, 0644); +MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)"); + +#define RATETAB_ENT(_rate, _rateid, _flags) \ + { \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate cw1200_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, 0), + RATETAB_ENT(55, 2, 0), + RATETAB_ENT(110, 3, 0), + RATETAB_ENT(60, 6, 0), + RATETAB_ENT(90, 7, 0), + RATETAB_ENT(120, 8, 0), + RATETAB_ENT(180, 9, 0), + RATETAB_ENT(240, 10, 0), + RATETAB_ENT(360, 11, 0), + RATETAB_ENT(480, 12, 0), + RATETAB_ENT(540, 13, 0), +}; + +static struct ieee80211_rate cw1200_mcs_rates[] = { + RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), + RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), + RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), + RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), + RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), + RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), + RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), + RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), +}; + +#define cw1200_a_rates (cw1200_rates + 4) +#define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4) +#define cw1200_g_rates (cw1200_rates + 0) +#define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates)) +#define cw1200_n_rates (cw1200_mcs_rates) +#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates)) + + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel cw1200_2ghz_chantable[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static struct ieee80211_channel cw1200_5ghz_chantable[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(149, 0), + CHAN5G(153, 0), CHAN5G(157, 0), + CHAN5G(161, 0), CHAN5G(165, 0), + CHAN5G(184, 0), CHAN5G(188, 0), + CHAN5G(192, 0), CHAN5G(196, 0), + CHAN5G(200, 0), CHAN5G(204, 0), + CHAN5G(208, 0), CHAN5G(212, 0), + CHAN5G(216, 0), +}; + +static struct ieee80211_supported_band cw1200_band_2ghz = { + .channels = cw1200_2ghz_chantable, + .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable), + .bitrates = cw1200_g_rates, + .n_bitrates = cw1200_g_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static struct ieee80211_supported_band cw1200_band_5ghz = { + .channels = cw1200_5ghz_chantable, + .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), + .bitrates = cw1200_a_rates, + .n_bitrates = cw1200_a_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static const unsigned long cw1200_ttl[] = { + 1 * HZ, /* VO */ + 2 * HZ, /* VI */ + 5 * HZ, /* BE */ + 10 * HZ /* BK */ +}; + +static const struct ieee80211_ops cw1200_ops = { + .start = cw1200_start, + .stop = cw1200_stop, + .add_interface = cw1200_add_interface, + .remove_interface = cw1200_remove_interface, + .change_interface = cw1200_change_interface, + .tx = cw1200_tx, + .hw_scan = cw1200_hw_scan, + .set_tim = cw1200_set_tim, + .sta_notify = cw1200_sta_notify, + .sta_add = cw1200_sta_add, + .sta_remove = cw1200_sta_remove, + .set_key = cw1200_set_key, + .set_rts_threshold = cw1200_set_rts_threshold, + .config = cw1200_config, + .bss_info_changed = cw1200_bss_info_changed, + .prepare_multicast = cw1200_prepare_multicast, + .configure_filter = cw1200_configure_filter, + .conf_tx = cw1200_conf_tx, + .get_stats = cw1200_get_stats, + .ampdu_action = cw1200_ampdu_action, + .flush = cw1200_flush, +#ifdef CONFIG_PM + .suspend = cw1200_wow_suspend, + .resume = cw1200_wow_resume, +#endif + /* Intentionally not offloaded: */ + /*.channel_switch = cw1200_channel_switch, */ + /*.remain_on_channel = cw1200_remain_on_channel, */ + /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */ +}; + +static int cw1200_ba_rx_tids = -1; +static int cw1200_ba_tx_tids = -1; +module_param(cw1200_ba_rx_tids, int, 0644); +module_param(cw1200_ba_tx_tids, int, 0644); +MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs"); +MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs"); + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support cw1200_wowlan_support = { + /* Support only for limited wowlan functionalities */ + .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT, +}; +#endif + + +static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, + const bool have_5ghz) +{ + int i, band; + struct ieee80211_hw *hw; + struct cw1200_common *priv; + + hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops); + if (!hw) + return NULL; + + priv = hw->priv; + priv->hw = hw; + priv->hw_type = -1; + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->rates = cw1200_rates; /* TODO: fetch from FW */ + priv->mcs_rates = cw1200_n_rates; + if (cw1200_ba_rx_tids != -1) + priv->ba_rx_tid_mask = cw1200_ba_rx_tids; + else + priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */ + if (cw1200_ba_tx_tids != -1) + priv->ba_tx_tid_mask = cw1200_ba_tx_tids; + else + priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */ + + ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); + ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, CONNECTION_MONITOR); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, SUPPORTS_PS); + + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + +#ifdef CONFIG_PM + hw->wiphy->wowlan = &cw1200_wowlan_support; +#endif + + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + + hw->queues = 4; + + priv->rts_threshold = -1; + + hw->max_rates = 8; + hw->max_rate_tries = 15; + hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM + + 8; /* TKIP IV */ + + hw->sta_data_size = sizeof(struct cw1200_sta_priv); + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz; + if (have_5ghz) + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz; + + /* Channel params have to be cleared before registering wiphy again */ + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband = hw->wiphy->bands[band]; + if (!sband) + continue; + for (i = 0; i < sband->n_channels; i++) { + sband->channels[i].flags = 0; + sband->channels[i].max_antenna_gain = 0; + sband->channels[i].max_power = 30; + } + } + + hw->wiphy->max_scan_ssids = 2; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + + if (macaddr) + SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr); + else + SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template); + + /* Fix up mac address if necessary */ + if (hw->wiphy->perm_addr[3] == 0 && + hw->wiphy->perm_addr[4] == 0 && + hw->wiphy->perm_addr[5] == 0) { + get_random_bytes(&hw->wiphy->perm_addr[3], 3); + } + + mutex_init(&priv->wsm_cmd_mux); + mutex_init(&priv->conf_mutex); + priv->workqueue = create_singlethread_workqueue("cw1200_wq"); + sema_init(&priv->scan.lock, 1); + INIT_WORK(&priv->scan.work, cw1200_scan_work); + INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work); + INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout); + INIT_DELAYED_WORK(&priv->clear_recent_scan_work, + cw1200_clear_recent_scan_work); + INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout); + INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work); + INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work); + INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work); + INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work); + spin_lock_init(&priv->event_queue_lock); + INIT_LIST_HEAD(&priv->event_queue); + INIT_WORK(&priv->event_handler, cw1200_event_handler); + INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work); + INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work); + spin_lock_init(&priv->bss_loss_lock); + spin_lock_init(&priv->ps_state_lock); + INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work); + INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); + INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); + INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); + INIT_WORK(&priv->link_id_work, cw1200_link_id_work); + INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work); + INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset); + INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work); + INIT_WORK(&priv->set_beacon_wakeup_period_work, + cw1200_set_beacon_wakeup_period_work); + setup_timer(&priv->mcast_timeout, cw1200_mcast_timeout, + (unsigned long)priv); + + if (cw1200_queue_stats_init(&priv->tx_queue_stats, + CW1200_LINK_ID_MAX, + cw1200_skb_dtor, + priv)) { + ieee80211_free_hw(hw); + return NULL; + } + + for (i = 0; i < 4; ++i) { + if (cw1200_queue_init(&priv->tx_queue[i], + &priv->tx_queue_stats, i, 16, + cw1200_ttl[i])) { + for (; i > 0; i--) + cw1200_queue_deinit(&priv->tx_queue[i - 1]); + cw1200_queue_stats_deinit(&priv->tx_queue_stats); + ieee80211_free_hw(hw); + return NULL; + } + } + + init_waitqueue_head(&priv->channel_switch_done); + init_waitqueue_head(&priv->wsm_cmd_wq); + init_waitqueue_head(&priv->wsm_startup_done); + init_waitqueue_head(&priv->ps_mode_switch_done); + wsm_buf_init(&priv->wsm_cmd_buf); + spin_lock_init(&priv->wsm_cmd.lock); + priv->wsm_cmd.done = 1; + tx_policy_init(priv); + + return hw; +} + +static int cw1200_register_common(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int err; + +#ifdef CONFIG_PM + err = cw1200_pm_init(&priv->pm_state, priv); + if (err) { + pr_err("Cannot init PM. (%d).\n", + err); + return err; + } +#endif + + err = ieee80211_register_hw(dev); + if (err) { + pr_err("Cannot register device (%d).\n", + err); +#ifdef CONFIG_PM + cw1200_pm_deinit(&priv->pm_state); +#endif + return err; + } + + cw1200_debug_init(priv); + + pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy)); + return 0; +} + +static void cw1200_free_common(struct ieee80211_hw *dev) +{ + ieee80211_free_hw(dev); +} + +static void cw1200_unregister_common(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int i; + + ieee80211_unregister_hw(dev); + + del_timer_sync(&priv->mcast_timeout); + cw1200_unregister_bh(priv); + + cw1200_debug_release(priv); + + mutex_destroy(&priv->conf_mutex); + + wsm_buf_deinit(&priv->wsm_cmd_buf); + + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + + if (priv->sdd) { + release_firmware(priv->sdd); + priv->sdd = NULL; + } + + for (i = 0; i < 4; ++i) + cw1200_queue_deinit(&priv->tx_queue[i]); + + cw1200_queue_stats_deinit(&priv->tx_queue_stats); +#ifdef CONFIG_PM + cw1200_pm_deinit(&priv->pm_state); +#endif +} + +/* Clock is in KHz */ +u32 cw1200_dpll_from_clk(u16 clk_khz) +{ + switch (clk_khz) { + case 0x32C8: /* 13000 KHz */ + return 0x1D89D241; + case 0x3E80: /* 16000 KHz */ + return 0x000001E1; + case 0x41A0: /* 16800 KHz */ + return 0x124931C1; + case 0x4B00: /* 19200 KHz */ + return 0x00000191; + case 0x5DC0: /* 24000 KHz */ + return 0x00000141; + case 0x6590: /* 26000 KHz */ + return 0x0EC4F121; + case 0x8340: /* 33600 KHz */ + return 0x092490E1; + case 0x9600: /* 38400 KHz */ + return 0x100010C1; + case 0x9C40: /* 40000 KHz */ + return 0x000000C1; + case 0xBB80: /* 48000 KHz */ + return 0x000000A1; + case 0xCB20: /* 52000 KHz */ + return 0x07627091; + default: + pr_err("Unknown Refclk freq (0x%04x), using 26000KHz\n", + clk_khz); + return 0x0EC4F121; + } +} + +int cw1200_core_probe(const struct hwbus_ops *hwbus_ops, + struct hwbus_priv *hwbus, + struct device *pdev, + struct cw1200_common **core, + int ref_clk, const u8 *macaddr, + const char *sdd_path, bool have_5ghz) +{ + int err = -EINVAL; + struct ieee80211_hw *dev; + struct cw1200_common *priv; + struct wsm_operational_mode mode = { + .power_mode = cw1200_power_mode, + .disable_more_flag_usage = true, + }; + + dev = cw1200_init_common(macaddr, have_5ghz); + if (!dev) + goto err; + + priv = dev->priv; + priv->hw_refclk = ref_clk; + if (cw1200_refclk) + priv->hw_refclk = cw1200_refclk; + + priv->sdd_path = (char *)sdd_path; + if (cw1200_sdd_path) + priv->sdd_path = cw1200_sdd_path; + + priv->hwbus_ops = hwbus_ops; + priv->hwbus_priv = hwbus; + priv->pdev = pdev; + SET_IEEE80211_DEV(priv->hw, pdev); + + /* Pass struct cw1200_common back up */ + *core = priv; + + err = cw1200_register_bh(priv); + if (err) + goto err1; + + err = cw1200_load_firmware(priv); + if (err) + goto err2; + + if (wait_event_interruptible_timeout(priv->wsm_startup_done, + priv->firmware_ready, + 3*HZ) <= 0) { + /* TODO: Need to find how to reset device + in QUEUE mode properly. + */ + pr_err("Timeout waiting on device startup\n"); + err = -ETIMEDOUT; + goto err2; + } + + /* Set low-power mode. */ + wsm_set_operational_mode(priv, &mode); + + /* Enable multi-TX confirmation */ + wsm_use_multi_tx_conf(priv, true); + + err = cw1200_register_common(dev); + if (err) + goto err2; + + return err; + +err2: + cw1200_unregister_bh(priv); +err1: + cw1200_free_common(dev); +err: + *core = NULL; + return err; +} +EXPORT_SYMBOL_GPL(cw1200_core_probe); + +void cw1200_core_release(struct cw1200_common *self) +{ + /* Disable device interrupts */ + self->hwbus_ops->lock(self->hwbus_priv); + __cw1200_irq_enable(self, 0); + self->hwbus_ops->unlock(self->hwbus_priv); + + /* And then clean up */ + cw1200_unregister_common(self->hw); + cw1200_free_common(self->hw); + return; +} +EXPORT_SYMBOL_GPL(cw1200_core_release); diff --git a/drivers/net/wireless/st/cw1200/pm.c b/drivers/net/wireless/st/cw1200/pm.c new file mode 100644 index 000000000000..d2202ae92bdd --- /dev/null +++ b/drivers/net/wireless/st/cw1200/pm.c @@ -0,0 +1,367 @@ +/* + * Mac80211 power management API for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include "cw1200.h" +#include "pm.h" +#include "sta.h" +#include "bh.h" +#include "hwbus.h" + +#define CW1200_BEACON_SKIPPING_MULTIPLIER 3 + +struct cw1200_udp_port_filter { + struct wsm_udp_port_filter_hdr hdr; + /* Up to 4 filters are allowed. */ + struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS]; +} __packed; + +struct cw1200_ether_type_filter { + struct wsm_ether_type_filter_hdr hdr; + /* Up to 4 filters are allowed. */ + struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS]; +} __packed; + +static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = { + .hdr.num = 2, + .filters = { + [0] = { + .action = WSM_FILTER_ACTION_FILTER_OUT, + .type = WSM_FILTER_PORT_TYPE_DST, + .port = __cpu_to_le16(67), /* DHCP Bootps */ + }, + [1] = { + .action = WSM_FILTER_ACTION_FILTER_OUT, + .type = WSM_FILTER_PORT_TYPE_DST, + .port = __cpu_to_le16(68), /* DHCP Bootpc */ + }, + } +}; + +static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = { + .num = 0, +}; + +#ifndef ETH_P_WAPI +#define ETH_P_WAPI 0x88B4 +#endif + +static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = { + .hdr.num = 4, + .filters = { + [0] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_IP), + }, + [1] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_PAE), + }, + [2] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_WAPI), + }, + [3] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_ARP), + }, + }, +}; + +static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = { + .num = 0, +}; + +/* private */ +struct cw1200_suspend_state { + unsigned long bss_loss_tmo; + unsigned long join_tmo; + unsigned long direct_probe; + unsigned long link_id_gc; + bool beacon_skipping; + u8 prev_ps_mode; +}; + +static void cw1200_pm_stay_awake_tmo(unsigned long arg) +{ + /* XXX what's the point of this ? */ +} + +int cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv) +{ + spin_lock_init(&pm->lock); + + setup_timer(&pm->stay_awake, cw1200_pm_stay_awake_tmo, + (unsigned long)pm); + + return 0; +} + +void cw1200_pm_deinit(struct cw1200_pm_state *pm) +{ + del_timer_sync(&pm->stay_awake); +} + +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo) +{ + long cur_tmo; + spin_lock_bh(&pm->lock); + cur_tmo = pm->stay_awake.expires - jiffies; + if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo) + mod_timer(&pm->stay_awake, jiffies + tmo); + spin_unlock_bh(&pm->lock); +} + +static long cw1200_suspend_work(struct delayed_work *work) +{ + int ret = cancel_delayed_work(work); + long tmo; + if (ret > 0) { + /* Timer is pending */ + tmo = work->timer.expires - jiffies; + if (tmo < 0) + tmo = 0; + } else { + tmo = -1; + } + return tmo; +} + +static int cw1200_resume_work(struct cw1200_common *priv, + struct delayed_work *work, + unsigned long tmo) +{ + if ((long)tmo < 0) + return 1; + + return queue_delayed_work(priv->workqueue, work, tmo); +} + +int cw1200_can_suspend(struct cw1200_common *priv) +{ + if (atomic_read(&priv->bh_rx)) { + wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n"); + return 0; + } + return 1; +} +EXPORT_SYMBOL_GPL(cw1200_can_suspend); + +int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_pm_state *pm_state = &priv->pm_state; + struct cw1200_suspend_state *state; + int ret; + + spin_lock_bh(&pm_state->lock); + ret = timer_pending(&pm_state->stay_awake); + spin_unlock_bh(&pm_state->lock); + if (ret) + return -EAGAIN; + + /* Do not suspend when datapath is not idle */ + if (priv->tx_queue_stats.num_queued) + return -EBUSY; + + /* Make sure there is no configuration requests in progress. */ + if (!mutex_trylock(&priv->conf_mutex)) + return -EBUSY; + + /* Ensure pending operations are done. + * Note also that wow_suspend must return in ~2.5sec, before + * watchdog is triggered. + */ + if (priv->channel_switch_in_progress) + goto revert1; + + /* Do not suspend when join is pending */ + if (priv->join_pending) + goto revert1; + + /* Do not suspend when scanning */ + if (down_trylock(&priv->scan.lock)) + goto revert1; + + /* Lock TX. */ + wsm_lock_tx_async(priv); + + /* Wait to avoid possible race with bh code. + * But do not wait too long... + */ + if (wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, HZ / 10) <= 0) + goto revert2; + + /* Set UDP filter */ + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr); + + /* Set ethernet frame type filter */ + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr); + + /* Allocate state */ + state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL); + if (!state) + goto revert3; + + /* Change to legacy PS while going to suspend */ + if (!priv->vif->p2p && + priv->join_status == CW1200_JOIN_STATUS_STA && + priv->powersave_mode.mode != WSM_PSM_PS) { + state->prev_ps_mode = priv->powersave_mode.mode; + priv->powersave_mode.mode = WSM_PSM_PS; + cw1200_set_pm(priv, &priv->powersave_mode); + if (wait_event_interruptible_timeout(priv->ps_mode_switch_done, + !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) { + goto revert4; + } + } + + /* Store delayed work states. */ + state->bss_loss_tmo = + cw1200_suspend_work(&priv->bss_loss_work); + state->join_tmo = + cw1200_suspend_work(&priv->join_timeout); + state->direct_probe = + cw1200_suspend_work(&priv->scan.probe_work); + state->link_id_gc = + cw1200_suspend_work(&priv->link_id_gc_work); + + cancel_delayed_work_sync(&priv->clear_recent_scan_work); + atomic_set(&priv->recent_scan, 0); + + /* Enable beacon skipping */ + if (priv->join_status == CW1200_JOIN_STATUS_STA && + priv->join_dtim_period && + !priv->has_multicast_subscription) { + state->beacon_skipping = true; + wsm_set_beacon_wakeup_period(priv, + priv->join_dtim_period, + CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period); + } + + /* Stop serving thread */ + if (cw1200_bh_suspend(priv)) + goto revert5; + + ret = timer_pending(&priv->mcast_timeout); + if (ret) + goto revert6; + + /* Store suspend state */ + pm_state->suspend_state = state; + + /* Enable IRQ wake */ + ret = priv->hwbus_ops->power_mgmt(priv->hwbus_priv, true); + if (ret) { + wiphy_err(priv->hw->wiphy, + "PM request failed: %d. WoW is disabled.\n", ret); + cw1200_wow_resume(hw); + return -EBUSY; + } + + /* Force resume if event is coming from the device. */ + if (atomic_read(&priv->bh_rx)) { + cw1200_wow_resume(hw); + return -EAGAIN; + } + + return 0; + +revert6: + WARN_ON(cw1200_bh_resume(priv)); +revert5: + cw1200_resume_work(priv, &priv->bss_loss_work, + state->bss_loss_tmo); + cw1200_resume_work(priv, &priv->join_timeout, + state->join_tmo); + cw1200_resume_work(priv, &priv->scan.probe_work, + state->direct_probe); + cw1200_resume_work(priv, &priv->link_id_gc_work, + state->link_id_gc); +revert4: + kfree(state); +revert3: + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); +revert2: + wsm_unlock_tx(priv); + up(&priv->scan.lock); +revert1: + mutex_unlock(&priv->conf_mutex); + return -EBUSY; +} + +int cw1200_wow_resume(struct ieee80211_hw *hw) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_pm_state *pm_state = &priv->pm_state; + struct cw1200_suspend_state *state; + + state = pm_state->suspend_state; + pm_state->suspend_state = NULL; + + /* Disable IRQ wake */ + priv->hwbus_ops->power_mgmt(priv->hwbus_priv, false); + + /* Scan.lock must be released before BH is resumed other way + * in case when BSS_LOST command arrived the processing of the + * command will be delayed. + */ + up(&priv->scan.lock); + + /* Resume BH thread */ + WARN_ON(cw1200_bh_resume(priv)); + + /* Restores previous PS mode */ + if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) { + priv->powersave_mode.mode = state->prev_ps_mode; + cw1200_set_pm(priv, &priv->powersave_mode); + } + + if (state->beacon_skipping) { + wsm_set_beacon_wakeup_period(priv, priv->beacon_int * + priv->join_dtim_period > + MAX_BEACON_SKIP_TIME_MS ? 1 : + priv->join_dtim_period, 0); + state->beacon_skipping = false; + } + + /* Resume delayed work */ + cw1200_resume_work(priv, &priv->bss_loss_work, + state->bss_loss_tmo); + cw1200_resume_work(priv, &priv->join_timeout, + state->join_tmo); + cw1200_resume_work(priv, &priv->scan.probe_work, + state->direct_probe); + cw1200_resume_work(priv, &priv->link_id_gc_work, + state->link_id_gc); + + /* Remove UDP port filter */ + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); + + /* Remove ethernet frame type filter */ + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); + + /* Unlock datapath */ + wsm_unlock_tx(priv); + + /* Unlock configuration mutex */ + mutex_unlock(&priv->conf_mutex); + + /* Free memory */ + kfree(state); + + return 0; +} diff --git a/drivers/net/wireless/st/cw1200/pm.h b/drivers/net/wireless/st/cw1200/pm.h new file mode 100644 index 000000000000..3ed90ff22bb8 --- /dev/null +++ b/drivers/net/wireless/st/cw1200/pm.h @@ -0,0 +1,43 @@ +/* + * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PM_H_INCLUDED +#define PM_H_INCLUDED + +/* ******************************************************************** */ +/* mac80211 API */ + +/* extern */ struct cw1200_common; +/* private */ struct cw1200_suspend_state; + +struct cw1200_pm_state { + struct cw1200_suspend_state *suspend_state; + struct timer_list stay_awake; + struct platform_device *pm_dev; + spinlock_t lock; /* Protect access */ +}; + +#ifdef CONFIG_PM +int cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv); +void cw1200_pm_deinit(struct cw1200_pm_state *pm); +int cw1200_wow_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan); +int cw1200_wow_resume(struct ieee80211_hw *hw); +int cw1200_can_suspend(struct cw1200_common *priv); +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo); +#else +static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo) { +} +#endif +#endif diff --git a/drivers/net/wireless/st/cw1200/queue.c b/drivers/net/wireless/st/cw1200/queue.c new file mode 100644 index 000000000000..0ba5ef9b3e7b --- /dev/null +++ b/drivers/net/wireless/st/cw1200/queue.c @@ -0,0 +1,581 @@ +/* + * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include "queue.h" +#include "cw1200.h" +#include "debug.h" + +/* private */ struct cw1200_queue_item +{ + struct list_head head; + struct sk_buff *skb; + u32 packet_id; + unsigned long queue_timestamp; + unsigned long xmit_timestamp; + struct cw1200_txpriv txpriv; + u8 generation; +}; + +static inline void __cw1200_queue_lock(struct cw1200_queue *queue) +{ + struct cw1200_queue_stats *stats = queue->stats; + if (queue->tx_locked_cnt++ == 0) { + pr_debug("[TX] Queue %d is locked.\n", + queue->queue_id); + ieee80211_stop_queue(stats->priv->hw, queue->queue_id); + } +} + +static inline void __cw1200_queue_unlock(struct cw1200_queue *queue) +{ + struct cw1200_queue_stats *stats = queue->stats; + BUG_ON(!queue->tx_locked_cnt); + if (--queue->tx_locked_cnt == 0) { + pr_debug("[TX] Queue %d is unlocked.\n", + queue->queue_id); + ieee80211_wake_queue(stats->priv->hw, queue->queue_id); + } +} + +static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation, + u8 *queue_id, u8 *item_generation, + u8 *item_id) +{ + *item_id = (packet_id >> 0) & 0xFF; + *item_generation = (packet_id >> 8) & 0xFF; + *queue_id = (packet_id >> 16) & 0xFF; + *queue_generation = (packet_id >> 24) & 0xFF; +} + +static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id, + u8 item_generation, u8 item_id) +{ + return ((u32)item_id << 0) | + ((u32)item_generation << 8) | + ((u32)queue_id << 16) | + ((u32)queue_generation << 24); +} + +static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats, + struct list_head *gc_list) +{ + struct cw1200_queue_item *item, *tmp; + + list_for_each_entry_safe(item, tmp, gc_list, head) { + list_del(&item->head); + stats->skb_dtor(stats->priv, item->skb, &item->txpriv); + kfree(item); + } +} + +static void cw1200_queue_register_post_gc(struct list_head *gc_list, + struct cw1200_queue_item *item) +{ + struct cw1200_queue_item *gc_item; + gc_item = kmalloc(sizeof(struct cw1200_queue_item), + GFP_ATOMIC); + BUG_ON(!gc_item); + memcpy(gc_item, item, sizeof(struct cw1200_queue_item)); + list_add_tail(&gc_item->head, gc_list); +} + +static void __cw1200_queue_gc(struct cw1200_queue *queue, + struct list_head *head, + bool unlock) +{ + struct cw1200_queue_stats *stats = queue->stats; + struct cw1200_queue_item *item = NULL, *tmp; + bool wakeup_stats = false; + + list_for_each_entry_safe(item, tmp, &queue->queue, head) { + if (jiffies - item->queue_timestamp < queue->ttl) + break; + --queue->num_queued; + --queue->link_map_cache[item->txpriv.link_id]; + spin_lock_bh(&stats->lock); + --stats->num_queued; + if (!--stats->link_map_cache[item->txpriv.link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); + cw1200_debug_tx_ttl(stats->priv); + cw1200_queue_register_post_gc(head, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + } + + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + + if (queue->overfull) { + if (queue->num_queued <= (queue->capacity >> 1)) { + queue->overfull = false; + if (unlock) + __cw1200_queue_unlock(queue); + } else if (item) { + unsigned long tmo = item->queue_timestamp + queue->ttl; + mod_timer(&queue->gc, tmo); + cw1200_pm_stay_awake(&stats->priv->pm_state, + tmo - jiffies); + } + } +} + +static void cw1200_queue_gc(unsigned long arg) +{ + LIST_HEAD(list); + struct cw1200_queue *queue = + (struct cw1200_queue *)arg; + + spin_lock_bh(&queue->lock); + __cw1200_queue_gc(queue, &list, true); + spin_unlock_bh(&queue->lock); + cw1200_queue_post_gc(queue->stats, &list); +} + +int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, + size_t map_capacity, + cw1200_queue_skb_dtor_t skb_dtor, + struct cw1200_common *priv) +{ + memset(stats, 0, sizeof(*stats)); + stats->map_capacity = map_capacity; + stats->skb_dtor = skb_dtor; + stats->priv = priv; + spin_lock_init(&stats->lock); + init_waitqueue_head(&stats->wait_link_id_empty); + + stats->link_map_cache = kzalloc(sizeof(int) * map_capacity, + GFP_KERNEL); + if (!stats->link_map_cache) + return -ENOMEM; + + return 0; +} + +int cw1200_queue_init(struct cw1200_queue *queue, + struct cw1200_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl) +{ + size_t i; + + memset(queue, 0, sizeof(*queue)); + queue->stats = stats; + queue->capacity = capacity; + queue->queue_id = queue_id; + queue->ttl = ttl; + INIT_LIST_HEAD(&queue->queue); + INIT_LIST_HEAD(&queue->pending); + INIT_LIST_HEAD(&queue->free_pool); + spin_lock_init(&queue->lock); + setup_timer(&queue->gc, cw1200_queue_gc, (unsigned long)queue); + + queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, + GFP_KERNEL); + if (!queue->pool) + return -ENOMEM; + + queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity, + GFP_KERNEL); + if (!queue->link_map_cache) { + kfree(queue->pool); + queue->pool = NULL; + return -ENOMEM; + } + + for (i = 0; i < capacity; ++i) + list_add_tail(&queue->pool[i].head, &queue->free_pool); + + return 0; +} + +int cw1200_queue_clear(struct cw1200_queue *queue) +{ + int i; + LIST_HEAD(gc_list); + struct cw1200_queue_stats *stats = queue->stats; + struct cw1200_queue_item *item, *tmp; + + spin_lock_bh(&queue->lock); + queue->generation++; + list_splice_tail_init(&queue->queue, &queue->pending); + list_for_each_entry_safe(item, tmp, &queue->pending, head) { + WARN_ON(!item->skb); + cw1200_queue_register_post_gc(&gc_list, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + } + queue->num_queued = 0; + queue->num_pending = 0; + + spin_lock_bh(&stats->lock); + for (i = 0; i < stats->map_capacity; ++i) { + stats->num_queued -= queue->link_map_cache[i]; + stats->link_map_cache[i] -= queue->link_map_cache[i]; + queue->link_map_cache[i] = 0; + } + spin_unlock_bh(&stats->lock); + if (queue->overfull) { + queue->overfull = false; + __cw1200_queue_unlock(queue); + } + spin_unlock_bh(&queue->lock); + wake_up(&stats->wait_link_id_empty); + cw1200_queue_post_gc(stats, &gc_list); + return 0; +} + +void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats) +{ + kfree(stats->link_map_cache); + stats->link_map_cache = NULL; +} + +void cw1200_queue_deinit(struct cw1200_queue *queue) +{ + cw1200_queue_clear(queue); + del_timer_sync(&queue->gc); + INIT_LIST_HEAD(&queue->free_pool); + kfree(queue->pool); + kfree(queue->link_map_cache); + queue->pool = NULL; + queue->link_map_cache = NULL; + queue->capacity = 0; +} + +size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, + u32 link_id_map) +{ + size_t ret; + int i, bit; + size_t map_capacity = queue->stats->map_capacity; + + if (!link_id_map) + return 0; + + spin_lock_bh(&queue->lock); + if (link_id_map == (u32)-1) { + ret = queue->num_queued - queue->num_pending; + } else { + ret = 0; + for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) { + if (link_id_map & bit) + ret += queue->link_map_cache[i]; + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int cw1200_queue_put(struct cw1200_queue *queue, + struct sk_buff *skb, + struct cw1200_txpriv *txpriv) +{ + int ret = 0; + LIST_HEAD(gc_list); + struct cw1200_queue_stats *stats = queue->stats; + + if (txpriv->link_id >= queue->stats->map_capacity) + return -EINVAL; + + spin_lock_bh(&queue->lock); + if (!WARN_ON(list_empty(&queue->free_pool))) { + struct cw1200_queue_item *item = list_first_entry( + &queue->free_pool, struct cw1200_queue_item, head); + BUG_ON(item->skb); + + list_move_tail(&item->head, &queue->queue); + item->skb = skb; + item->txpriv = *txpriv; + item->generation = 0; + item->packet_id = cw1200_queue_mk_packet_id(queue->generation, + queue->queue_id, + item->generation, + item - queue->pool); + item->queue_timestamp = jiffies; + + ++queue->num_queued; + ++queue->link_map_cache[txpriv->link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[txpriv->link_id]; + spin_unlock_bh(&stats->lock); + + /* TX may happen in parallel sometimes. + * Leave extra queue slots so we don't overflow. + */ + if (queue->overfull == false && + queue->num_queued >= + (queue->capacity - (num_present_cpus() - 1))) { + queue->overfull = true; + __cw1200_queue_lock(queue); + mod_timer(&queue->gc, jiffies); + } + } else { + ret = -ENOENT; + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int cw1200_queue_get(struct cw1200_queue *queue, + u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + const struct cw1200_txpriv **txpriv) +{ + int ret = -ENOENT; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + bool wakeup_stats = false; + + spin_lock_bh(&queue->lock); + list_for_each_entry(item, &queue->queue, head) { + if (link_id_map & BIT(item->txpriv.link_id)) { + ret = 0; + break; + } + } + + if (!WARN_ON(ret)) { + *tx = (struct wsm_tx *)item->skb->data; + *tx_info = IEEE80211_SKB_CB(item->skb); + *txpriv = &item->txpriv; + (*tx)->packet_id = item->packet_id; + list_move_tail(&item->head, &queue->pending); + ++queue->num_pending; + --queue->link_map_cache[item->txpriv.link_id]; + item->xmit_timestamp = jiffies; + + spin_lock_bh(&stats->lock); + --stats->num_queued; + if (!--stats->link_map_cache[item->txpriv.link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); + } + spin_unlock_bh(&queue->lock); + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + return ret; +} + +int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + + cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (queue_generation != queue->generation) { + ret = -ENOENT; + } else if (item_id >= (unsigned) queue->capacity) { + WARN_ON(1); + ret = -EINVAL; + } else if (item->generation != item_generation) { + WARN_ON(1); + ret = -ENOENT; + } else { + --queue->num_pending; + ++queue->link_map_cache[item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + item->generation = ++item_generation; + item->packet_id = cw1200_queue_mk_packet_id(queue_generation, + queue_id, + item_generation, + item_id); + list_move(&item->head, &queue->queue); + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int cw1200_queue_requeue_all(struct cw1200_queue *queue) +{ + struct cw1200_queue_item *item, *tmp; + struct cw1200_queue_stats *stats = queue->stats; + spin_lock_bh(&queue->lock); + + list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) { + --queue->num_pending; + ++queue->link_map_cache[item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + ++item->generation; + item->packet_id = cw1200_queue_mk_packet_id(queue->generation, + queue->queue_id, + item->generation, + item - queue->pool); + list_move(&item->head, &queue->queue); + } + spin_unlock_bh(&queue->lock); + + return 0; +} + +int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + struct sk_buff *gc_skb = NULL; + struct cw1200_txpriv gc_txpriv; + + cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (queue_generation != queue->generation) { + ret = -ENOENT; + } else if (item_id >= (unsigned) queue->capacity) { + WARN_ON(1); + ret = -EINVAL; + } else if (item->generation != item_generation) { + WARN_ON(1); + ret = -ENOENT; + } else { + gc_txpriv = item->txpriv; + gc_skb = item->skb; + item->skb = NULL; + --queue->num_pending; + --queue->num_queued; + ++queue->num_sent; + ++item->generation; + /* Do not use list_move_tail here, but list_move: + * try to utilize cache row. + */ + list_move(&item->head, &queue->free_pool); + + if (queue->overfull && + (queue->num_queued <= (queue->capacity >> 1))) { + queue->overfull = false; + __cw1200_queue_unlock(queue); + } + } + spin_unlock_bh(&queue->lock); + + if (gc_skb) + stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv); + + return ret; +} + +int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id, + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (queue_generation != queue->generation) { + ret = -ENOENT; + } else if (item_id >= (unsigned) queue->capacity) { + WARN_ON(1); + ret = -EINVAL; + } else if (item->generation != item_generation) { + WARN_ON(1); + ret = -ENOENT; + } else { + *skb = item->skb; + *txpriv = &item->txpriv; + } + spin_unlock_bh(&queue->lock); + return ret; +} + +void cw1200_queue_lock(struct cw1200_queue *queue) +{ + spin_lock_bh(&queue->lock); + __cw1200_queue_lock(queue); + spin_unlock_bh(&queue->lock); +} + +void cw1200_queue_unlock(struct cw1200_queue *queue) +{ + spin_lock_bh(&queue->lock); + __cw1200_queue_unlock(queue); + spin_unlock_bh(&queue->lock); +} + +bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, + unsigned long *timestamp, + u32 pending_frame_id) +{ + struct cw1200_queue_item *item; + bool ret; + + spin_lock_bh(&queue->lock); + ret = !list_empty(&queue->pending); + if (ret) { + list_for_each_entry(item, &queue->pending, head) { + if (item->packet_id != pending_frame_id) + if (time_before(item->xmit_timestamp, + *timestamp)) + *timestamp = item->xmit_timestamp; + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + +bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, + u32 link_id_map) +{ + bool empty = true; + + spin_lock_bh(&stats->lock); + if (link_id_map == (u32)-1) { + empty = stats->num_queued == 0; + } else { + int i; + for (i = 0; i < stats->map_capacity; ++i) { + if (link_id_map & BIT(i)) { + if (stats->link_map_cache[i]) { + empty = false; + break; + } + } + } + } + spin_unlock_bh(&stats->lock); + + return empty; +} diff --git a/drivers/net/wireless/st/cw1200/queue.h b/drivers/net/wireless/st/cw1200/queue.h new file mode 100644 index 000000000000..119f9c79c14e --- /dev/null +++ b/drivers/net/wireless/st/cw1200/queue.h @@ -0,0 +1,116 @@ +/* + * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_QUEUE_H_INCLUDED +#define CW1200_QUEUE_H_INCLUDED + +/* private */ struct cw1200_queue_item; + +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct cw1200_common; +/* extern */ struct ieee80211_tx_queue_stats; +/* extern */ struct cw1200_txpriv; + +/* forward */ struct cw1200_queue_stats; + +typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv); + +struct cw1200_queue { + struct cw1200_queue_stats *stats; + size_t capacity; + size_t num_queued; + size_t num_pending; + size_t num_sent; + struct cw1200_queue_item *pool; + struct list_head queue; + struct list_head free_pool; + struct list_head pending; + int tx_locked_cnt; + int *link_map_cache; + bool overfull; + spinlock_t lock; /* Protect queue entry */ + u8 queue_id; + u8 generation; + struct timer_list gc; + unsigned long ttl; +}; + +struct cw1200_queue_stats { + spinlock_t lock; /* Protect stats entry */ + int *link_map_cache; + int num_queued; + size_t map_capacity; + wait_queue_head_t wait_link_id_empty; + cw1200_queue_skb_dtor_t skb_dtor; + struct cw1200_common *priv; +}; + +struct cw1200_txpriv { + u8 link_id; + u8 raw_link_id; + u8 tid; + u8 rate_id; + u8 offset; +}; + +int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, + size_t map_capacity, + cw1200_queue_skb_dtor_t skb_dtor, + struct cw1200_common *priv); +int cw1200_queue_init(struct cw1200_queue *queue, + struct cw1200_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl); +int cw1200_queue_clear(struct cw1200_queue *queue); +void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats); +void cw1200_queue_deinit(struct cw1200_queue *queue); + +size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, + u32 link_id_map); +int cw1200_queue_put(struct cw1200_queue *queue, + struct sk_buff *skb, + struct cw1200_txpriv *txpriv); +int cw1200_queue_get(struct cw1200_queue *queue, + u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + const struct cw1200_txpriv **txpriv); +int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id); +int cw1200_queue_requeue_all(struct cw1200_queue *queue); +int cw1200_queue_remove(struct cw1200_queue *queue, + u32 packet_id); +int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id, + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv); +void cw1200_queue_lock(struct cw1200_queue *queue); +void cw1200_queue_unlock(struct cw1200_queue *queue); +bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, + unsigned long *timestamp, + u32 pending_frame_id); + +bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, + u32 link_id_map); + +static inline u8 cw1200_queue_get_queue_id(u32 packet_id) +{ + return (packet_id >> 16) & 0xFF; +} + +static inline u8 cw1200_queue_get_generation(u32 packet_id) +{ + return (packet_id >> 8) & 0xFF; +} + +#endif /* CW1200_QUEUE_H_INCLUDED */ diff --git a/drivers/net/wireless/st/cw1200/scan.c b/drivers/net/wireless/st/cw1200/scan.c new file mode 100644 index 000000000000..bff81b8d4164 --- /dev/null +++ b/drivers/net/wireless/st/cw1200/scan.c @@ -0,0 +1,463 @@ +/* + * Scan implementation for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include "cw1200.h" +#include "scan.h" +#include "sta.h" +#include "pm.h" + +static void cw1200_scan_restart_delayed(struct cw1200_common *priv); + +static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) +{ + int ret, i; + int tmo = 2000; + + switch (priv->join_status) { + case CW1200_JOIN_STATUS_PRE_STA: + case CW1200_JOIN_STATUS_JOINING: + return -EBUSY; + default: + break; + } + + wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n", + scan->type, scan->num_channels, scan->flags); + + for (i = 0; i < scan->num_channels; ++i) + tmo += scan->ch[i].max_chan_time + 10; + + cancel_delayed_work_sync(&priv->clear_recent_scan_work); + atomic_set(&priv->scan.in_progress, 1); + atomic_set(&priv->recent_scan, 1); + cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo)); + queue_delayed_work(priv->workqueue, &priv->scan.timeout, + msecs_to_jiffies(tmo)); + ret = wsm_scan(priv, scan); + if (ret) { + atomic_set(&priv->scan.in_progress, 0); + cancel_delayed_work_sync(&priv->scan.timeout); + cw1200_scan_restart_delayed(priv); + } + return ret; +} + +int cw1200_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct cw1200_common *priv = hw->priv; + struct cfg80211_scan_request *req = &hw_req->req; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + int i, ret; + + if (!priv->vif) + return -EINVAL; + + /* Scan when P2P_GO corrupt firmware MiniAP mode */ + if (priv->join_status == CW1200_JOIN_STATUS_AP) + return -EOPNOTSUPP; + + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) + req->n_ssids = 0; + + wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n", + req->n_ssids); + + if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) + return -EINVAL; + + frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, + req->ie_len); + if (!frame.skb) + return -ENOMEM; + + if (req->ie_len) + memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len); + + /* will be unlocked in cw1200_scan_work() */ + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + + ret = wsm_set_template_frame(priv, &frame); + if (!ret) { + /* Host want to be the probe responder. */ + ret = wsm_set_probe_responder(priv, true); + } + if (ret) { + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); + dev_kfree_skb(frame.skb); + return ret; + } + + wsm_lock_tx(priv); + + BUG_ON(priv->scan.req); + priv->scan.req = req; + priv->scan.n_ssids = 0; + priv->scan.status = 0; + priv->scan.begin = &req->channels[0]; + priv->scan.curr = priv->scan.begin; + priv->scan.end = &req->channels[req->n_channels]; + priv->scan.output_power = priv->output_power; + + for (i = 0; i < req->n_ssids; ++i) { + struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids]; + memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); + dst->length = req->ssids[i].ssid_len; + ++priv->scan.n_ssids; + } + + mutex_unlock(&priv->conf_mutex); + + if (frame.skb) + dev_kfree_skb(frame.skb); + queue_work(priv->workqueue, &priv->scan.work); + return 0; +} + +void cw1200_scan_work(struct work_struct *work) +{ + struct cw1200_common *priv = container_of(work, struct cw1200_common, + scan.work); + struct ieee80211_channel **it; + struct wsm_scan scan = { + .type = WSM_SCAN_TYPE_FOREGROUND, + .flags = WSM_SCAN_FLAG_SPLIT_METHOD, + }; + bool first_run = (priv->scan.begin == priv->scan.curr && + priv->scan.begin != priv->scan.end); + int i; + + if (first_run) { + /* Firmware gets crazy if scan request is sent + * when STA is joined but not yet associated. + * Force unjoin in this case. + */ + if (cancel_delayed_work_sync(&priv->join_timeout) > 0) + cw1200_join_timeout(&priv->join_timeout.work); + } + + mutex_lock(&priv->conf_mutex); + + if (first_run) { + if (priv->join_status == CW1200_JOIN_STATUS_STA && + !(priv->powersave_mode.mode & WSM_PSM_PS)) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.mode = WSM_PSM_PS; + cw1200_set_pm(priv, &pm); + } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + /* FW bug: driver has to restart p2p-dev mode + * after scan + */ + cw1200_disable_listening(priv); + } + } + + if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) { + if (priv->scan.output_power != priv->output_power) + wsm_set_output_power(priv, priv->output_power * 10); + if (priv->join_status == CW1200_JOIN_STATUS_STA && + !(priv->powersave_mode.mode & WSM_PSM_PS)) + cw1200_set_pm(priv, &priv->powersave_mode); + + if (priv->scan.status < 0) + wiphy_warn(priv->hw->wiphy, + "[SCAN] Scan failed (%d).\n", + priv->scan.status); + else if (priv->scan.req) + wiphy_dbg(priv->hw->wiphy, + "[SCAN] Scan completed.\n"); + else + wiphy_dbg(priv->hw->wiphy, + "[SCAN] Scan canceled.\n"); + + priv->scan.req = NULL; + cw1200_scan_restart_delayed(priv); + wsm_unlock_tx(priv); + mutex_unlock(&priv->conf_mutex); + ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0); + up(&priv->scan.lock); + return; + } else { + struct ieee80211_channel *first = *priv->scan.curr; + for (it = priv->scan.curr + 1, i = 1; + it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; + ++it, ++i) { + if ((*it)->band != first->band) + break; + if (((*it)->flags ^ first->flags) & + IEEE80211_CHAN_NO_IR) + break; + if (!(first->flags & IEEE80211_CHAN_NO_IR) && + (*it)->max_power != first->max_power) + break; + } + scan.band = first->band; + + if (priv->scan.req->no_cck) + scan.max_tx_rate = WSM_TRANSMIT_RATE_6; + else + scan.max_tx_rate = WSM_TRANSMIT_RATE_1; + scan.num_probes = + (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2; + scan.num_ssids = priv->scan.n_ssids; + scan.ssids = &priv->scan.ssids[0]; + scan.num_channels = it - priv->scan.curr; + /* TODO: Is it optimal? */ + scan.probe_delay = 100; + /* It is not stated in WSM specification, however + * FW team says that driver may not use FG scan + * when joined. + */ + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + scan.type = WSM_SCAN_TYPE_BACKGROUND; + scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + } + scan.ch = kzalloc( + sizeof(struct wsm_scan_ch) * (it - priv->scan.curr), + GFP_KERNEL); + if (!scan.ch) { + priv->scan.status = -ENOMEM; + goto fail; + } + for (i = 0; i < scan.num_channels; ++i) { + scan.ch[i].number = priv->scan.curr[i]->hw_value; + if (priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) { + scan.ch[i].min_chan_time = 50; + scan.ch[i].max_chan_time = 100; + } else { + scan.ch[i].min_chan_time = 10; + scan.ch[i].max_chan_time = 25; + } + } + if (!(first->flags & IEEE80211_CHAN_NO_IR) && + priv->scan.output_power != first->max_power) { + priv->scan.output_power = first->max_power; + wsm_set_output_power(priv, + priv->scan.output_power * 10); + } + priv->scan.status = cw1200_scan_start(priv, &scan); + kfree(scan.ch); + if (priv->scan.status) + goto fail; + priv->scan.curr = it; + } + mutex_unlock(&priv->conf_mutex); + return; + +fail: + priv->scan.curr = priv->scan.end; + mutex_unlock(&priv->conf_mutex); + queue_work(priv->workqueue, &priv->scan.work); + return; +} + +static void cw1200_scan_restart_delayed(struct cw1200_common *priv) +{ + /* FW bug: driver has to restart p2p-dev mode after scan. */ + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + cw1200_enable_listening(priv); + cw1200_update_filtering(priv); + } + + if (priv->delayed_unjoin) { + priv->delayed_unjoin = false; + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } else if (priv->delayed_link_loss) { + wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n"); + priv->delayed_link_loss = 0; + cw1200_cqm_bssloss_sm(priv, 1, 0, 0); + } +} + +static void cw1200_scan_complete(struct cw1200_common *priv) +{ + queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ); + if (priv->scan.direct_probe) { + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n"); + cw1200_scan_restart_delayed(priv); + priv->scan.direct_probe = 0; + up(&priv->scan.lock); + wsm_unlock_tx(priv); + } else { + cw1200_scan_work(&priv->scan.work); + } +} + +void cw1200_scan_failed_cb(struct cw1200_common *priv) +{ + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + /* STA is stopped. */ + return; + + if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { + priv->scan.status = -EIO; + queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); + } +} + + +void cw1200_scan_complete_cb(struct cw1200_common *priv, + struct wsm_scan_complete *arg) +{ + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + /* STA is stopped. */ + return; + + if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { + priv->scan.status = 1; + queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); + } +} + +void cw1200_clear_recent_scan_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + clear_recent_scan_work.work); + atomic_xchg(&priv->recent_scan, 0); +} + +void cw1200_scan_timeout(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, scan.timeout.work); + if (atomic_xchg(&priv->scan.in_progress, 0)) { + if (priv->scan.status > 0) { + priv->scan.status = 0; + } else if (!priv->scan.status) { + wiphy_warn(priv->hw->wiphy, + "Timeout waiting for scan complete notification.\n"); + priv->scan.status = -ETIMEDOUT; + priv->scan.curr = priv->scan.end; + wsm_stop_scan(priv); + } + cw1200_scan_complete(priv); + } +} + +void cw1200_probe_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, scan.probe_work.work); + u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queue_id]; + const struct cw1200_txpriv *txpriv; + struct wsm_tx *wsm; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + struct wsm_ssid ssids[1] = {{ + .length = 0, + } }; + struct wsm_scan_ch ch[1] = {{ + .min_chan_time = 0, + .max_chan_time = 10, + } }; + struct wsm_scan scan = { + .type = WSM_SCAN_TYPE_FOREGROUND, + .num_probes = 1, + .probe_delay = 0, + .num_channels = 1, + .ssids = ssids, + .ch = ch, + }; + u8 *ies; + size_t ies_len; + int ret; + + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n"); + + mutex_lock(&priv->conf_mutex); + if (down_trylock(&priv->scan.lock)) { + /* Scan is already in progress. Requeue self. */ + schedule(); + queue_delayed_work(priv->workqueue, &priv->scan.probe_work, + msecs_to_jiffies(100)); + mutex_unlock(&priv->conf_mutex); + return; + } + + /* Make sure we still have a pending probe req */ + if (cw1200_queue_get_skb(queue, priv->pending_frame_id, + &frame.skb, &txpriv)) { + up(&priv->scan.lock); + mutex_unlock(&priv->conf_mutex); + wsm_unlock_tx(priv); + return; + } + wsm = (struct wsm_tx *)frame.skb->data; + scan.max_tx_rate = wsm->max_tx_rate; + scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + if (priv->join_status == CW1200_JOIN_STATUS_STA || + priv->join_status == CW1200_JOIN_STATUS_IBSS) { + scan.type = WSM_SCAN_TYPE_BACKGROUND; + scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + } + ch[0].number = priv->channel->hw_value; + + skb_pull(frame.skb, txpriv->offset); + + ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; + ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); + + if (ies_len) { + u8 *ssidie = + (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); + if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) { + u8 *nextie = &ssidie[2 + ssidie[1]]; + /* Remove SSID from the IE list. It has to be provided + * as a separate argument in cw1200_scan_start call + */ + + /* Store SSID localy */ + ssids[0].length = ssidie[1]; + memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length); + scan.num_ssids = 1; + + /* Remove SSID from IE list */ + ssidie[1] = 0; + memmove(&ssidie[2], nextie, &ies[ies_len] - nextie); + skb_trim(frame.skb, frame.skb->len - ssids[0].length); + } + } + + /* FW bug: driver has to restart p2p-dev mode after scan */ + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + cw1200_disable_listening(priv); + ret = wsm_set_template_frame(priv, &frame); + priv->scan.direct_probe = 1; + if (!ret) { + wsm_flush_tx(priv); + ret = cw1200_scan_start(priv, &scan); + } + mutex_unlock(&priv->conf_mutex); + + skb_push(frame.skb, txpriv->offset); + if (!ret) + IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK; + BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id)); + + if (ret) { + priv->scan.direct_probe = 0; + up(&priv->scan.lock); + wsm_unlock_tx(priv); + } + + return; +} diff --git a/drivers/net/wireless/st/cw1200/scan.h b/drivers/net/wireless/st/cw1200/scan.h new file mode 100644 index 000000000000..cc75459e5784 --- /dev/null +++ b/drivers/net/wireless/st/cw1200/scan.h @@ -0,0 +1,56 @@ +/* + * Scan interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SCAN_H_INCLUDED +#define SCAN_H_INCLUDED + +#include +#include "wsm.h" + +/* external */ struct sk_buff; +/* external */ struct cfg80211_scan_request; +/* external */ struct ieee80211_channel; +/* external */ struct ieee80211_hw; +/* external */ struct work_struct; + +struct cw1200_scan { + struct semaphore lock; + struct work_struct work; + struct delayed_work timeout; + struct cfg80211_scan_request *req; + struct ieee80211_channel **begin; + struct ieee80211_channel **curr; + struct ieee80211_channel **end; + struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; + int output_power; + int n_ssids; + int status; + atomic_t in_progress; + /* Direct probe requests workaround */ + struct delayed_work probe_work; + int direct_probe; +}; + +int cw1200_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req); +void cw1200_scan_work(struct work_struct *work); +void cw1200_scan_timeout(struct work_struct *work); +void cw1200_clear_recent_scan_work(struct work_struct *work); +void cw1200_scan_complete_cb(struct cw1200_common *priv, + struct wsm_scan_complete *arg); +void cw1200_scan_failed_cb(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* Raw probe requests TX workaround */ +void cw1200_probe_work(struct work_struct *work); + +#endif diff --git a/drivers/net/wireless/st/cw1200/sta.c b/drivers/net/wireless/st/cw1200/sta.c new file mode 100644 index 000000000000..95a7fdb3cc1c --- /dev/null +++ b/drivers/net/wireless/st/cw1200/sta.c @@ -0,0 +1,2399 @@ +/* + * Mac80211 STA API for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include "cw1200.h" +#include "sta.h" +#include "fwio.h" +#include "bh.h" +#include "debug.h" + +#ifndef ERP_INFO_BYTE_OFFSET +#define ERP_INFO_BYTE_OFFSET 2 +#endif + +static void cw1200_do_join(struct cw1200_common *priv); +static void cw1200_do_unjoin(struct cw1200_common *priv); + +static int cw1200_upload_beacon(struct cw1200_common *priv); +static int cw1200_upload_pspoll(struct cw1200_common *priv); +static int cw1200_upload_null(struct cw1200_common *priv); +static int cw1200_upload_qosnull(struct cw1200_common *priv); +static int cw1200_start_ap(struct cw1200_common *priv); +static int cw1200_update_beaconing(struct cw1200_common *priv); +static int cw1200_enable_beaconing(struct cw1200_common *priv, + bool enable); +static void __cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + int link_id); +static int __cw1200_flush(struct cw1200_common *priv, bool drop); + +static inline void __cw1200_free_event_queue(struct list_head *list) +{ + struct cw1200_wsm_event *event, *tmp; + list_for_each_entry_safe(event, tmp, list, link) { + list_del(&event->link); + kfree(event); + } +} + +/* ******************************************************************** */ +/* STA API */ + +int cw1200_start(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int ret = 0; + + cw1200_pm_stay_awake(&priv->pm_state, HZ); + + mutex_lock(&priv->conf_mutex); + + /* default EDCA */ + WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false); + ret = wsm_set_edca_params(priv, &priv->edca); + if (ret) + goto out; + + ret = cw1200_set_uapsd_param(priv, &priv->edca); + if (ret) + goto out; + + priv->setbssparams_done = false; + + memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); + priv->mode = NL80211_IFTYPE_MONITOR; + priv->wep_default_key_id = -1; + + priv->cqm_beacon_loss_count = 10; + + ret = cw1200_setup_mac(priv); + if (ret) + goto out; + +out: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_stop(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + LIST_HEAD(list); + int i; + + wsm_lock_tx(priv); + + while (down_trylock(&priv->scan.lock)) { + /* Scan is in progress. Force it to stop. */ + priv->scan.req = NULL; + schedule(); + } + up(&priv->scan.lock); + + cancel_delayed_work_sync(&priv->scan.probe_work); + cancel_delayed_work_sync(&priv->scan.timeout); + cancel_delayed_work_sync(&priv->clear_recent_scan_work); + cancel_delayed_work_sync(&priv->join_timeout); + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + cancel_work_sync(&priv->unjoin_work); + cancel_delayed_work_sync(&priv->link_id_gc_work); + flush_workqueue(priv->workqueue); + del_timer_sync(&priv->mcast_timeout); + mutex_lock(&priv->conf_mutex); + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->listening = false; + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + __cw1200_free_event_queue(&list); + + + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + priv->join_pending = false; + + for (i = 0; i < 4; i++) + cw1200_queue_clear(&priv->tx_queue[i]); + mutex_unlock(&priv->conf_mutex); + tx_policy_clean(priv); + + /* HACK! */ + if (atomic_xchg(&priv->tx_lock, 1) != 1) + pr_debug("[STA] TX is force-unlocked due to stop request.\n"); + + wsm_unlock_tx(priv); + atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */ +} + +static int cw1200_bssloss_mitigation = 1; +module_param(cw1200_bssloss_mitigation, int, 0644); +MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)"); + + +void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, + int init, int good, int bad) +{ + int tx = 0; + + priv->delayed_link_loss = 0; + cancel_work_sync(&priv->bss_params_work); + + pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n", + priv->bss_loss_state, + init, good, bad, + atomic_read(&priv->tx_lock), + priv->delayed_unjoin); + + /* If we have a pending unjoin */ + if (priv->delayed_unjoin) + return; + + if (init) { + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, + HZ); + priv->bss_loss_state = 0; + + /* Skip the confimration procedure in P2P case */ + if (!priv->vif->p2p && !atomic_read(&priv->tx_lock)) + tx = 1; + } else if (good) { + cancel_delayed_work_sync(&priv->bss_loss_work); + priv->bss_loss_state = 0; + queue_work(priv->workqueue, &priv->bss_params_work); + } else if (bad) { + /* XXX Should we just keep going until we time out? */ + if (priv->bss_loss_state < 3) + tx = 1; + } else { + cancel_delayed_work_sync(&priv->bss_loss_work); + priv->bss_loss_state = 0; + } + + /* Bypass mitigation if it's disabled */ + if (!cw1200_bssloss_mitigation) + tx = 0; + + /* Spit out a NULL packet to our AP if necessary */ + if (tx) { + struct sk_buff *skb; + + priv->bss_loss_state++; + + skb = ieee80211_nullfunc_get(priv->hw, priv->vif); + WARN_ON(!skb); + if (skb) + cw1200_tx(priv->hw, NULL, skb); + } +} + +int cw1200_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + int ret; + struct cw1200_common *priv = dev->priv; + /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ + + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_UAPSD | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; + + mutex_lock(&priv->conf_mutex); + + if (priv->mode != NL80211_IFTYPE_MONITOR) { + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + priv->mode = vif->type; + break; + default: + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + priv->vif = vif; + memcpy(priv->mac_addr, vif->addr, ETH_ALEN); + ret = cw1200_setup_mac(priv); + /* Enable auto-calibration */ + /* Exception in subsequent channel switch; disabled. + * wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, + * &auto_calibration_mode, sizeof(auto_calibration_mode)); + */ + + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct cw1200_common *priv = dev->priv; + struct wsm_reset reset = { + .reset_statistics = true, + }; + int i; + + mutex_lock(&priv->conf_mutex); + switch (priv->join_status) { + case CW1200_JOIN_STATUS_JOINING: + case CW1200_JOIN_STATUS_PRE_STA: + case CW1200_JOIN_STATUS_STA: + case CW1200_JOIN_STATUS_IBSS: + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + break; + case CW1200_JOIN_STATUS_AP: + for (i = 0; priv->link_id_map; ++i) { + if (priv->link_id_map & BIT(i)) { + reset.link_id = i; + wsm_reset(priv, &reset); + priv->link_id_map &= ~BIT(i); + } + } + memset(priv->link_id_db, 0, sizeof(priv->link_id_db)); + priv->sta_asleep_mask = 0; + priv->enable_beacon = false; + priv->tx_multicast = false; + priv->aid0_bit_set = false; + priv->buffered_multicasts = false; + priv->pspoll_mask = 0; + reset.link_id = 0; + wsm_reset(priv, &reset); + break; + case CW1200_JOIN_STATUS_MONITOR: + cw1200_update_listening(priv, false); + break; + default: + break; + } + priv->vif = NULL; + priv->mode = NL80211_IFTYPE_MONITOR; + eth_zero_addr(priv->mac_addr); + memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo)); + cw1200_free_keys(priv); + cw1200_setup_mac(priv); + priv->listening = false; + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + if (!__cw1200_flush(priv, true)) + wsm_unlock_tx(priv); + + mutex_unlock(&priv->conf_mutex); +} + +int cw1200_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p) +{ + int ret = 0; + pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type, + p2p, vif->type, vif->p2p); + + if (new_type != vif->type || vif->p2p != p2p) { + cw1200_remove_interface(dev, vif); + vif->type = new_type; + vif->p2p = p2p; + ret = cw1200_add_interface(dev, vif); + } + + return ret; +} + +int cw1200_config(struct ieee80211_hw *dev, u32 changed) +{ + int ret = 0; + struct cw1200_common *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + + pr_debug("CONFIG CHANGED: %08x\n", changed); + + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + /* TODO: IEEE80211_CONF_CHANGE_QOS */ + /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */ + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + priv->output_power = conf->power_level; + pr_debug("[STA] TX power: %d\n", priv->output_power); + wsm_set_output_power(priv, priv->output_power * 10); + } + + if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && + (priv->channel != conf->chandef.chan)) { + struct ieee80211_channel *ch = conf->chandef.chan; + struct wsm_switch_channel channel = { + .channel_number = ch->hw_value, + }; + pr_debug("[STA] Freq %d (wsm ch: %d).\n", + ch->center_freq, ch->hw_value); + + /* __cw1200_flush() implicitly locks tx, if successful */ + if (!__cw1200_flush(priv, false)) { + if (!wsm_switch_channel(priv, &channel)) { + ret = wait_event_timeout(priv->channel_switch_done, + !priv->channel_switch_in_progress, + 3 * HZ); + if (ret) { + /* Already unlocks if successful */ + priv->channel = ch; + ret = 0; + } else { + ret = -ETIMEDOUT; + } + } else { + /* Unlock if switch channel fails */ + wsm_unlock_tx(priv); + } + } + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + if (!(conf->flags & IEEE80211_CONF_PS)) + priv->powersave_mode.mode = WSM_PSM_ACTIVE; + else if (conf->dynamic_ps_timeout <= 0) + priv->powersave_mode.mode = WSM_PSM_PS; + else + priv->powersave_mode.mode = WSM_PSM_FAST_PS; + + /* Firmware requires that value for this 1-byte field must + * be specified in units of 500us. Values above the 128ms + * threshold are not supported. + */ + if (conf->dynamic_ps_timeout >= 0x80) + priv->powersave_mode.fast_psm_idle_period = 0xFF; + else + priv->powersave_mode.fast_psm_idle_period = + conf->dynamic_ps_timeout << 1; + + if (priv->join_status == CW1200_JOIN_STATUS_STA && + priv->bss_params.aid) + cw1200_set_pm(priv, &priv->powersave_mode); + } + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + /* TBD: It looks like it's transparent + * there's a monitor interface present -- use this + * to determine for example whether to calculate + * timestamps for packets or not, do not use instead + * of filter flags! + */ + } + + if (changed & IEEE80211_CONF_CHANGE_IDLE) { + struct wsm_operational_mode mode = { + .power_mode = cw1200_power_mode, + .disable_more_flag_usage = true, + }; + + wsm_lock_tx(priv); + /* Disable p2p-dev mode forced by TX request */ + if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && + (conf->flags & IEEE80211_CONF_IDLE) && + !priv->listening) { + cw1200_disable_listening(priv); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + } + wsm_set_operational_mode(priv, &mode); + wsm_unlock_tx(priv); + } + + if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { + pr_debug("[STA] Retry limits: %d (long), %d (short).\n", + conf->long_frame_max_tx_count, + conf->short_frame_max_tx_count); + spin_lock_bh(&priv->tx_policy_cache.lock); + priv->long_frame_max_tx_count = conf->long_frame_max_tx_count; + priv->short_frame_max_tx_count = + (conf->short_frame_max_tx_count < 0x0F) ? + conf->short_frame_max_tx_count : 0x0F; + priv->hw->max_rate_tries = priv->short_frame_max_tx_count; + spin_unlock_bh(&priv->tx_policy_cache.lock); + } + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); + return ret; +} + +void cw1200_update_filtering(struct cw1200_common *priv) +{ + int ret; + bool bssid_filtering = !priv->rx_filter.bssid; + bool is_p2p = priv->vif && priv->vif->p2p; + bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type; + + static struct wsm_beacon_filter_control bf_ctrl; + static struct wsm_mib_beacon_filter_table bf_tbl = { + .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC, + .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + .entry[0].oui[0] = 0x50, + .entry[0].oui[1] = 0x6F, + .entry[0].oui[2] = 0x9A, + .entry[1].ie_id = WLAN_EID_HT_OPERATION, + .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + .entry[2].ie_id = WLAN_EID_ERP_INFO, + .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + }; + + if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) + return; + else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + bssid_filtering = false; + + if (priv->disable_beacon_filter) { + bf_ctrl.enabled = 0; + bf_ctrl.bcn_count = 1; + bf_tbl.num = __cpu_to_le32(0); + } else if (is_p2p || !is_sta) { + bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE | + WSM_BEACON_FILTER_AUTO_ERP; + bf_ctrl.bcn_count = 0; + bf_tbl.num = __cpu_to_le32(2); + } else { + bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE; + bf_ctrl.bcn_count = 0; + bf_tbl.num = __cpu_to_le32(3); + } + + /* When acting as p2p client being connected to p2p GO, in order to + * receive frames from a different p2p device, turn off bssid filter. + * + * WARNING: FW dependency! + * This can only be used with FW WSM371 and its successors. + * In that FW version even with bssid filter turned off, + * device will block most of the unwanted frames. + */ + if (is_p2p) + bssid_filtering = false; + + ret = wsm_set_rx_filter(priv, &priv->rx_filter); + if (!ret) + ret = wsm_set_beacon_filter_table(priv, &bf_tbl); + if (!ret) + ret = wsm_beacon_filter_control(priv, &bf_ctrl); + if (!ret) + ret = wsm_set_bssid_filtering(priv, bssid_filtering); + if (!ret) + ret = wsm_set_multicast_filter(priv, &priv->multicast_filter); + if (ret) + wiphy_err(priv->hw->wiphy, + "Update filtering failed: %d.\n", ret); + return; +} + +void cw1200_update_filtering_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + update_filtering_work); + + cw1200_update_filtering(priv); +} + +void cw1200_set_beacon_wakeup_period_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + set_beacon_wakeup_period_work); + + wsm_set_beacon_wakeup_period(priv, + priv->beacon_int * priv->join_dtim_period > + MAX_BEACON_SKIP_TIME_MS ? 1 : + priv->join_dtim_period, 0); +} + +u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + static u8 broadcast_ipv6[ETH_ALEN] = { + 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 + }; + static u8 broadcast_ipv4[ETH_ALEN] = { + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 + }; + struct cw1200_common *priv = hw->priv; + struct netdev_hw_addr *ha; + int count = 0; + + /* Disable multicast filtering */ + priv->has_multicast_subscription = false; + memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); + + if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) + return 0; + + /* Enable if requested */ + netdev_hw_addr_list_for_each(ha, mc_list) { + pr_debug("[STA] multicast: %pM\n", ha->addr); + memcpy(&priv->multicast_filter.macaddrs[count], + ha->addr, ETH_ALEN); + if (!ether_addr_equal(ha->addr, broadcast_ipv4) && + !ether_addr_equal(ha->addr, broadcast_ipv6)) + priv->has_multicast_subscription = true; + count++; + } + + if (count) { + priv->multicast_filter.enable = __cpu_to_le32(1); + priv->multicast_filter.num_addrs = __cpu_to_le32(count); + } + + return netdev_hw_addr_list_count(mc_list); +} + +void cw1200_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct cw1200_common *priv = dev->priv; + bool listening = !!(*total_flags & + (FIF_OTHER_BSS | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ)); + + *total_flags &= FIF_OTHER_BSS | + FIF_FCSFAIL | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ; + + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + + priv->rx_filter.promiscuous = 0; + priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS | + FIF_PROBE_REQ)) ? 1 : 0; + priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; + priv->disable_beacon_filter = !(*total_flags & + (FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ)); + if (priv->listening != listening) { + priv->listening = listening; + wsm_lock_tx(priv); + cw1200_update_listening(priv, listening); + wsm_unlock_tx(priv); + } + cw1200_update_filtering(priv); + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); +} + +int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params) +{ + struct cw1200_common *priv = dev->priv; + int ret = 0; + /* To prevent re-applying PM request OID again and again*/ + bool old_uapsd_flags; + + mutex_lock(&priv->conf_mutex); + + if (queue < dev->queues) { + old_uapsd_flags = le16_to_cpu(priv->uapsd_info.uapsd_flags); + + WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0); + ret = wsm_set_tx_queue_params(priv, + &priv->tx_queue_params.params[queue], queue); + if (ret) { + ret = -EINVAL; + goto out; + } + + WSM_EDCA_SET(&priv->edca, queue, params->aifs, + params->cw_min, params->cw_max, + params->txop, 0xc8, + params->uapsd); + ret = wsm_set_edca_params(priv, &priv->edca); + if (ret) { + ret = -EINVAL; + goto out; + } + + if (priv->mode == NL80211_IFTYPE_STATION) { + ret = cw1200_set_uapsd_param(priv, &priv->edca); + if (!ret && priv->setbssparams_done && + (priv->join_status == CW1200_JOIN_STATUS_STA) && + (old_uapsd_flags != le16_to_cpu(priv->uapsd_info.uapsd_flags))) + ret = cw1200_set_pm(priv, &priv->powersave_mode); + } + } else { + ret = -EINVAL; + } + +out: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +int cw1200_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct cw1200_common *priv = dev->priv; + + memcpy(stats, &priv->stats, sizeof(*stats)); + return 0; +} + +int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) +{ + struct wsm_set_pm pm = *arg; + + if (priv->uapsd_info.uapsd_flags != 0) + pm.mode &= ~WSM_PSM_FAST_PS_FLAG; + + if (memcmp(&pm, &priv->firmware_ps_mode, + sizeof(struct wsm_set_pm))) { + priv->firmware_ps_mode = pm; + return wsm_set_pm(priv, &pm); + } else { + return 0; + } +} + +int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret = -EOPNOTSUPP; + struct cw1200_common *priv = dev->priv; + struct ieee80211_key_seq seq; + + mutex_lock(&priv->conf_mutex); + + if (cmd == SET_KEY) { + u8 *peer_addr = NULL; + int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? + 1 : 0; + int idx = cw1200_alloc_key(priv); + struct wsm_add_key *wsm_key = &priv->keys[idx]; + + if (idx < 0) { + ret = -EINVAL; + goto finally; + } + + if (sta) + peer_addr = sta->addr; + + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE | + IEEE80211_KEY_FLAG_RESERVE_TAILROOM; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (key->keylen > 16) { + cw1200_free_key(priv, idx); + ret = -EINVAL; + goto finally; + } + + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; + memcpy(wsm_key->wep_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wep_pairwise.keydata, + &key->key[0], key->keylen); + wsm_key->wep_pairwise.keylen = key->keylen; + } else { + wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; + memcpy(wsm_key->wep_group.keydata, + &key->key[0], key->keylen); + wsm_key->wep_group.keylen = key->keylen; + wsm_key->wep_group.keyid = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_TKIP: + ieee80211_get_key_rx_seq(key, 0, &seq); + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; + memcpy(wsm_key->tkip_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->tkip_pairwise.keydata, + &key->key[0], 16); + memcpy(wsm_key->tkip_pairwise.tx_mic_key, + &key->key[16], 8); + memcpy(wsm_key->tkip_pairwise.rx_mic_key, + &key->key[24], 8); + } else { + size_t mic_offset = + (priv->mode == NL80211_IFTYPE_AP) ? + 16 : 24; + wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; + memcpy(wsm_key->tkip_group.keydata, + &key->key[0], 16); + memcpy(wsm_key->tkip_group.rx_mic_key, + &key->key[mic_offset], 8); + + wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff; + wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff; + wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff; + wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff; + wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff; + wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff; + wsm_key->tkip_group.rx_seqnum[6] = 0; + wsm_key->tkip_group.rx_seqnum[7] = 0; + + wsm_key->tkip_group.keyid = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_CCMP: + ieee80211_get_key_rx_seq(key, 0, &seq); + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; + memcpy(wsm_key->aes_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->aes_pairwise.keydata, + &key->key[0], 16); + } else { + wsm_key->type = WSM_KEY_TYPE_AES_GROUP; + memcpy(wsm_key->aes_group.keydata, + &key->key[0], 16); + + wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5]; + wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4]; + wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3]; + wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2]; + wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1]; + wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0]; + wsm_key->aes_group.rx_seqnum[6] = 0; + wsm_key->aes_group.rx_seqnum[7] = 0; + wsm_key->aes_group.keyid = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_SMS4: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; + memcpy(wsm_key->wapi_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wapi_pairwise.keydata, + &key->key[0], 16); + memcpy(wsm_key->wapi_pairwise.mic_key, + &key->key[16], 16); + wsm_key->wapi_pairwise.keyid = key->keyidx; + } else { + wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; + memcpy(wsm_key->wapi_group.keydata, + &key->key[0], 16); + memcpy(wsm_key->wapi_group.mic_key, + &key->key[16], 16); + wsm_key->wapi_group.keyid = key->keyidx; + } + break; + default: + pr_warn("Unhandled key type %d\n", key->cipher); + cw1200_free_key(priv, idx); + ret = -EOPNOTSUPP; + goto finally; + } + ret = wsm_add_key(priv, wsm_key); + if (!ret) + key->hw_key_idx = idx; + else + cw1200_free_key(priv, idx); + } else if (cmd == DISABLE_KEY) { + struct wsm_remove_key wsm_key = { + .index = key->hw_key_idx, + }; + + if (wsm_key.index > WSM_KEY_MAX_INDEX) { + ret = -EINVAL; + goto finally; + } + + cw1200_free_key(priv, wsm_key.index); + ret = wsm_remove_key(priv, &wsm_key); + } else { + pr_warn("Unhandled key command %d\n", cmd); + } + +finally: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_wep_key_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, wep_key_work); + u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queue_id]; + __le32 wep_default_key_id = __cpu_to_le32( + priv->wep_default_key_id); + + pr_debug("[STA] Setting default WEP key: %d\n", + priv->wep_default_key_id); + wsm_flush_tx(priv); + wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, + &wep_default_key_id, sizeof(wep_default_key_id)); + cw1200_queue_requeue(queue, priv->pending_frame_id); + wsm_unlock_tx(priv); +} + +int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + int ret = 0; + __le32 val32; + struct cw1200_common *priv = hw->priv; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + return 0; + + if (value != (u32) -1) + val32 = __cpu_to_le32(value); + else + val32 = 0; /* disabled */ + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* device is down, can _not_ set threshold */ + ret = -ENODEV; + goto out; + } + + if (priv->rts_threshold == value) + goto out; + + pr_debug("[STA] Setting RTS threshold: %d\n", + priv->rts_threshold); + + /* mutex_lock(&priv->conf_mutex); */ + ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, + &val32, sizeof(val32)); + if (!ret) + priv->rts_threshold = value; + /* mutex_unlock(&priv->conf_mutex); */ + +out: + return ret; +} + +/* If successful, LOCKS the TX queue! */ +static int __cw1200_flush(struct cw1200_common *priv, bool drop) +{ + int i, ret; + + for (;;) { + /* TODO: correct flush handling is required when dev_stop. + * Temporary workaround: 2s + */ + if (drop) { + for (i = 0; i < 4; ++i) + cw1200_queue_clear(&priv->tx_queue[i]); + } else { + ret = wait_event_timeout( + priv->tx_queue_stats.wait_link_id_empty, + cw1200_queue_stats_is_empty( + &priv->tx_queue_stats, -1), + 2 * HZ); + } + + if (!drop && ret <= 0) { + ret = -ETIMEDOUT; + break; + } else { + ret = 0; + } + + wsm_lock_tx(priv); + if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) { + /* Highly unlikely: WSM requeued frames. */ + wsm_unlock_tx(priv); + continue; + } + break; + } + return ret; +} + +void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct cw1200_common *priv = hw->priv; + + switch (priv->mode) { + case NL80211_IFTYPE_MONITOR: + drop = true; + break; + case NL80211_IFTYPE_AP: + if (!priv->enable_beacon) + drop = true; + break; + } + + if (!__cw1200_flush(priv, drop)) + wsm_unlock_tx(priv); + + return; +} + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_free_event_queue(struct cw1200_common *priv) +{ + LIST_HEAD(list); + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + + __cw1200_free_event_queue(&list); +} + +void cw1200_event_handler(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, event_handler); + struct cw1200_wsm_event *event; + LIST_HEAD(list); + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + + list_for_each_entry(event, &list, link) { + switch (event->evt.id) { + case WSM_EVENT_ERROR: + pr_err("Unhandled WSM Error from LMAC\n"); + break; + case WSM_EVENT_BSS_LOST: + pr_debug("[CQM] BSS lost.\n"); + cancel_work_sync(&priv->unjoin_work); + if (!down_trylock(&priv->scan.lock)) { + cw1200_cqm_bssloss_sm(priv, 1, 0, 0); + up(&priv->scan.lock); + } else { + /* Scan is in progress. Delay reporting. + * Scan complete will trigger bss_loss_work + */ + priv->delayed_link_loss = 1; + /* Also start a watchdog. */ + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, 5*HZ); + } + break; + case WSM_EVENT_BSS_REGAINED: + pr_debug("[CQM] BSS regained.\n"); + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + cancel_work_sync(&priv->unjoin_work); + break; + case WSM_EVENT_RADAR_DETECTED: + wiphy_info(priv->hw->wiphy, "radar pulse detected\n"); + break; + case WSM_EVENT_RCPI_RSSI: + { + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 + */ + int rcpi_rssi = (int)(event->evt.data & 0xFF); + int cqm_evt; + if (priv->cqm_use_rssi) + rcpi_rssi = (s8)rcpi_rssi; + else + rcpi_rssi = rcpi_rssi / 2 - 110; + + cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi); + ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, + GFP_KERNEL); + break; + } + case WSM_EVENT_BT_INACTIVE: + pr_warn("Unhandled BT INACTIVE from LMAC\n"); + break; + case WSM_EVENT_BT_ACTIVE: + pr_warn("Unhandled BT ACTIVE from LMAC\n"); + break; + } + } + __cw1200_free_event_queue(&list); +} + +void cw1200_bss_loss_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, bss_loss_work.work); + + pr_debug("[CQM] Reporting connection loss.\n"); + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); +} + +void cw1200_bss_params_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, bss_params_work); + mutex_lock(&priv->conf_mutex); + + priv->bss_params.reset_beacon_loss = 1; + wsm_set_bss_params(priv, &priv->bss_params); + priv->bss_params.reset_beacon_loss = 0; + + mutex_unlock(&priv->conf_mutex); +} + +/* ******************************************************************** */ +/* Internal API */ + +/* This function is called to Parse the SDD file + * to extract listen_interval and PTA related information + * sdd is a TLV: u8 id, u8 len, u8 data[] + */ +static int cw1200_parse_sdd_file(struct cw1200_common *priv) +{ + const u8 *p = priv->sdd->data; + int ret = 0; + + while (p + 2 <= priv->sdd->data + priv->sdd->size) { + if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) { + pr_warn("Malformed sdd structure\n"); + return -1; + } + switch (p[0]) { + case SDD_PTA_CFG_ELT_ID: { + u16 v; + if (p[1] < 4) { + pr_warn("SDD_PTA_CFG_ELT_ID malformed\n"); + ret = -1; + break; + } + v = le16_to_cpu(*((__le16 *)(p + 2))); + if (!v) /* non-zero means this is enabled */ + break; + + v = le16_to_cpu(*((__le16 *)(p + 4))); + priv->conf_listen_interval = (v >> 7) & 0x1F; + pr_debug("PTA found; Listen Interval %d\n", + priv->conf_listen_interval); + break; + } + case SDD_REFERENCE_FREQUENCY_ELT_ID: { + u16 clk = le16_to_cpu(*((__le16 *)(p + 2))); + if (clk != priv->hw_refclk) + pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n", + clk, priv->hw_refclk); + break; + } + default: + break; + } + p += p[1] + 2; + } + + if (!priv->bt_present) { + pr_debug("PTA element NOT found.\n"); + priv->conf_listen_interval = 0; + } + return ret; +} + +int cw1200_setup_mac(struct cw1200_common *priv) +{ + int ret = 0; + + /* NOTE: There is a bug in FW: it reports signal + * as RSSI if RSSI subscription is enabled. + * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. + * + * NOTE2: RSSI based reports have been switched to RCPI, since + * FW has a bug and RSSI reported values are not stable, + * what can leads to signal level oscilations in user-end applications + */ + struct wsm_rcpi_rssi_threshold threshold = { + .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER, + .rollingAverageCount = 16, + }; + + struct wsm_configuration cfg = { + .dot11StationId = &priv->mac_addr[0], + }; + + /* Remember the decission here to make sure, we will handle + * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS + */ + if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI) + priv->cqm_use_rssi = true; + + if (!priv->sdd) { + ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev); + if (ret) { + pr_err("Can't load sdd file %s.\n", priv->sdd_path); + return ret; + } + cw1200_parse_sdd_file(priv); + } + + cfg.dpdData = priv->sdd->data; + cfg.dpdData_size = priv->sdd->size; + ret = wsm_configuration(priv, &cfg); + if (ret) + return ret; + + /* Configure RSSI/SCPI reporting as RSSI. */ + wsm_set_rcpi_rssi_threshold(priv, &threshold); + + return 0; +} + +static void cw1200_join_complete(struct cw1200_common *priv) +{ + pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status); + + priv->join_pending = false; + if (priv->join_complete_status) { + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + cw1200_update_listening(priv, priv->listening); + cw1200_do_unjoin(priv); + ieee80211_connection_loss(priv->vif); + } else { + if (priv->mode == NL80211_IFTYPE_ADHOC) + priv->join_status = CW1200_JOIN_STATUS_IBSS; + else + priv->join_status = CW1200_JOIN_STATUS_PRE_STA; + } + wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */ +} + +void cw1200_join_complete_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, join_complete_work); + mutex_lock(&priv->conf_mutex); + cw1200_join_complete(priv); + mutex_unlock(&priv->conf_mutex); +} + +void cw1200_join_complete_cb(struct cw1200_common *priv, + struct wsm_join_complete *arg) +{ + pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n", + arg->status); + + if (cancel_delayed_work(&priv->join_timeout)) { + priv->join_complete_status = arg->status; + queue_work(priv->workqueue, &priv->join_complete_work); + } +} + +/* MUST be called with tx_lock held! It will be unlocked for us. */ +static void cw1200_do_join(struct cw1200_common *priv) +{ + const u8 *bssid; + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct cfg80211_bss *bss = NULL; + struct wsm_protected_mgmt_policy mgmt_policy; + struct wsm_join join = { + .mode = conf->ibss_joined ? + WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, + .preamble_type = WSM_JOIN_PREAMBLE_LONG, + .probe_for_join = 1, + .atim_window = 0, + .basic_rate_set = cw1200_rate_mask_to_wsm(priv, + conf->basic_rates), + }; + if (delayed_work_pending(&priv->join_timeout)) { + pr_warn("[STA] - Join request already pending, skipping..\n"); + wsm_unlock_tx(priv); + return; + } + + if (priv->join_status) + cw1200_do_unjoin(priv); + + bssid = priv->vif->bss_conf.bssid; + + bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0, + IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); + + if (!bss && !conf->ibss_joined) { + wsm_unlock_tx(priv); + return; + } + + mutex_lock(&priv->conf_mutex); + + /* Under the conf lock: check scan status and + * bail out if it is in progress. + */ + if (atomic_read(&priv->scan.in_progress)) { + wsm_unlock_tx(priv); + goto done_put; + } + + priv->join_pending = true; + + /* Sanity check basic rates */ + if (!join.basic_rate_set) + join.basic_rate_set = 7; + + /* Sanity check beacon interval */ + if (!priv->beacon_int) + priv->beacon_int = 1; + + join.beacon_interval = priv->beacon_int; + + /* BT Coex related changes */ + if (priv->bt_present) { + if (((priv->conf_listen_interval * 100) % + priv->beacon_int) == 0) + priv->listen_interval = + ((priv->conf_listen_interval * 100) / + priv->beacon_int); + else + priv->listen_interval = + ((priv->conf_listen_interval * 100) / + priv->beacon_int + 1); + } + + if (priv->hw->conf.ps_dtim_period) + priv->join_dtim_period = priv->hw->conf.ps_dtim_period; + join.dtim_period = priv->join_dtim_period; + + join.channel_number = priv->channel->hw_value; + join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + + memcpy(join.bssid, bssid, sizeof(join.bssid)); + + pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n", + join.bssid, + join.dtim_period, priv->beacon_int); + + if (!conf->ibss_joined) { + const u8 *ssidie; + rcu_read_lock(); + ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (ssidie) { + join.ssid_len = ssidie[1]; + memcpy(join.ssid, &ssidie[2], join.ssid_len); + } + rcu_read_unlock(); + } + + if (priv->vif->p2p) { + join.flags |= WSM_JOIN_FLAGS_P2P_GO; + join.basic_rate_set = + cw1200_rate_mask_to_wsm(priv, 0xFF0); + } + + /* Enable asynchronous join calls */ + if (!conf->ibss_joined) { + join.flags |= WSM_JOIN_FLAGS_FORCE; + join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND; + } + + wsm_flush_tx(priv); + + /* Stay Awake for Join and Auth Timeouts and a bit more */ + cw1200_pm_stay_awake(&priv->pm_state, + CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT); + + cw1200_update_listening(priv, false); + + /* Turn on Block ACKs */ + wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask, + priv->ba_rx_tid_mask); + + /* Set up timeout */ + if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) { + priv->join_status = CW1200_JOIN_STATUS_JOINING; + queue_delayed_work(priv->workqueue, + &priv->join_timeout, + CW1200_JOIN_TIMEOUT); + } + + /* 802.11w protected mgmt frames */ + mgmt_policy.protectedMgmtEnable = 0; + mgmt_policy.unprotectedMgmtFramesAllowed = 1; + mgmt_policy.encryptionForAuthFrame = 1; + wsm_set_protected_mgmt_policy(priv, &mgmt_policy); + + /* Perform actual join */ + if (wsm_join(priv, &join)) { + pr_err("[STA] cw1200_join_work: wsm_join failed!\n"); + cancel_delayed_work_sync(&priv->join_timeout); + cw1200_update_listening(priv, priv->listening); + /* Tx lock still held, unjoin will clear it. */ + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } else { + if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND)) + cw1200_join_complete(priv); /* Will clear tx_lock */ + + /* Upload keys */ + cw1200_upload_keys(priv); + + /* Due to beacon filtering it is possible that the + * AP's beacon is not known for the mac80211 stack. + * Disable filtering temporary to make sure the stack + * receives at least one + */ + priv->disable_beacon_filter = true; + } + cw1200_update_filtering(priv); + +done_put: + mutex_unlock(&priv->conf_mutex); + if (bss) + cfg80211_put_bss(priv->hw->wiphy, bss); +} + +void cw1200_join_timeout(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, join_timeout.work); + pr_debug("[WSM] Join timed out.\n"); + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); +} + +static void cw1200_do_unjoin(struct cw1200_common *priv) +{ + struct wsm_reset reset = { + .reset_statistics = true, + }; + + cancel_delayed_work_sync(&priv->join_timeout); + + mutex_lock(&priv->conf_mutex); + priv->join_pending = false; + + if (atomic_read(&priv->scan.in_progress)) { + if (priv->delayed_unjoin) + wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n"); + else + priv->delayed_unjoin = true; + goto done; + } + + priv->delayed_link_loss = false; + + if (!priv->join_status) + goto done; + + if (priv->join_status == CW1200_JOIN_STATUS_AP) + goto done; + + cancel_work_sync(&priv->update_filtering_work); + cancel_work_sync(&priv->set_beacon_wakeup_period_work); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + + /* Unjoin is a reset. */ + wsm_flush_tx(priv); + wsm_keep_alive_period(priv, 0); + wsm_reset(priv, &reset); + wsm_set_output_power(priv, priv->output_power * 10); + priv->join_dtim_period = 0; + cw1200_setup_mac(priv); + cw1200_free_event_queue(priv); + cancel_work_sync(&priv->event_handler); + cw1200_update_listening(priv, priv->listening); + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + + /* Disable Block ACKs */ + wsm_set_block_ack_policy(priv, 0, 0); + + priv->disable_beacon_filter = false; + cw1200_update_filtering(priv); + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + priv->setbssparams_done = false; + memset(&priv->firmware_ps_mode, 0, + sizeof(priv->firmware_ps_mode)); + + pr_debug("[STA] Unjoin completed.\n"); + +done: + mutex_unlock(&priv->conf_mutex); +} + +void cw1200_unjoin_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, unjoin_work); + + cw1200_do_unjoin(priv); + + /* Tell the stack we're dead */ + ieee80211_connection_loss(priv->vif); + + wsm_unlock_tx(priv); +} + +int cw1200_enable_listening(struct cw1200_common *priv) +{ + struct wsm_start start = { + .mode = WSM_START_MODE_P2P_DEV, + .band = WSM_PHY_BAND_2_4G, + .beacon_interval = 100, + .dtim_period = 1, + .probe_delay = 0, + .basic_rate_set = 0x0F, + }; + + if (priv->channel) { + start.band = priv->channel->band == IEEE80211_BAND_5GHZ ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + start.channel_number = priv->channel->hw_value; + } else { + start.band = WSM_PHY_BAND_2_4G; + start.channel_number = 1; + } + + return wsm_start(priv, &start); +} + +int cw1200_disable_listening(struct cw1200_common *priv) +{ + int ret; + struct wsm_reset reset = { + .reset_statistics = true, + }; + ret = wsm_reset(priv, &reset); + return ret; +} + +void cw1200_update_listening(struct cw1200_common *priv, bool enabled) +{ + if (enabled) { + if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) { + if (!cw1200_enable_listening(priv)) + priv->join_status = CW1200_JOIN_STATUS_MONITOR; + wsm_set_probe_responder(priv, true); + } + } else { + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + if (!cw1200_disable_listening(priv)) + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + wsm_set_probe_responder(priv, false); + } + } +} + +int cw1200_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg) +{ + int ret; + u16 uapsd_flags = 0; + + /* Here's the mapping AC [queue, bit] + * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0] + */ + + if (arg->uapsd_enable[0]) + uapsd_flags |= 1 << 3; + + if (arg->uapsd_enable[1]) + uapsd_flags |= 1 << 2; + + if (arg->uapsd_enable[2]) + uapsd_flags |= 1 << 1; + + if (arg->uapsd_enable[3]) + uapsd_flags |= 1; + + /* Currently pseudo U-APSD operation is not supported, so setting + * MinAutoTriggerInterval, MaxAutoTriggerInterval and + * AutoTriggerStep to 0 + */ + + priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags); + priv->uapsd_info.min_auto_trigger_interval = 0; + priv->uapsd_info.max_auto_trigger_interval = 0; + priv->uapsd_info.auto_trigger_step = 0; + + ret = wsm_set_uapsd_info(priv, &priv->uapsd_info); + return ret; +} + +/* ******************************************************************** */ +/* AP API */ + +int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; + struct sk_buff *skb; + + if (priv->mode != NL80211_IFTYPE_AP) + return 0; + + sta_priv->link_id = cw1200_find_link_id(priv, sta->addr); + if (WARN_ON(!sta_priv->link_id)) { + wiphy_info(priv->hw->wiphy, + "[AP] No more link IDs available.\n"); + return -ENOENT; + } + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == + IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + priv->sta_asleep_mask |= BIT(sta_priv->link_id); + entry->status = CW1200_LINK_HARD; + while ((skb = skb_dequeue(&entry->rx_queue))) + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + return 0; +} + +int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; + + if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) + return 0; + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + entry->status = CW1200_LINK_RESERVE; + entry->timestamp = jiffies; + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + spin_unlock_bh(&priv->ps_state_lock); + flush_workqueue(priv->workqueue); + return 0; +} + +static void __cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + int link_id) +{ + struct cw1200_common *priv = dev->priv; + u32 bit, prev; + + /* Zero link id means "for all link IDs" */ + if (link_id) + bit = BIT(link_id); + else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE)) + bit = 0; + else + bit = priv->link_id_map; + prev = priv->sta_asleep_mask & bit; + + switch (notify_cmd) { + case STA_NOTIFY_SLEEP: + if (!prev) { + if (priv->buffered_multicasts && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_start_work); + priv->sta_asleep_mask |= bit; + } + break; + case STA_NOTIFY_AWAKE: + if (prev) { + priv->sta_asleep_mask &= ~bit; + priv->pspoll_mask &= ~bit; + if (priv->tx_multicast && link_id && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_stop_work); + cw1200_bh_wakeup(priv); + } + break; + } +} + +void cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = dev->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + + spin_lock_bh(&priv->ps_state_lock); + __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id); + spin_unlock_bh(&priv->ps_state_lock); +} + +static void cw1200_ps_notify(struct cw1200_common *priv, + int link_id, bool ps) +{ + if (link_id > CW1200_MAX_STA_IN_AP_MODE) + return; + + pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n", + ps ? "Stop" : "Start", + link_id, priv->sta_asleep_mask); + + __cw1200_sta_notify(priv->hw, priv->vif, + ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id); +} + +static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) +{ + struct sk_buff *skb; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + }; + u16 tim_offset, tim_length; + + pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis"); + + skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, + &tim_offset, &tim_length); + if (!skb) { + if (!__cw1200_flush(priv, true)) + wsm_unlock_tx(priv); + return -ENOENT; + } + + if (tim_offset && tim_length >= 6) { + /* Ignore DTIM count from mac80211: + * firmware handles DTIM internally. + */ + skb->data[tim_offset + 2] = 0; + + /* Set/reset aid0 bit */ + if (aid0_bit_set) + skb->data[tim_offset + 4] |= 1; + else + skb->data[tim_offset + 4] &= ~1; + } + + update_ie.ies = &skb->data[tim_offset]; + update_ie.length = tim_length; + wsm_update_ie(priv, &update_ie); + + dev_kfree_skb(skb); + + return 0; +} + +void cw1200_set_tim_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, set_tim_work); + (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set); +} + +int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set) +{ + struct cw1200_common *priv = dev->priv; + queue_work(priv->workqueue, &priv->set_tim_work); + return 0; +} + +void cw1200_set_cts_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, set_cts_work); + + u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + .ies = erp_ie, + .length = 3, + }; + u32 erp_info; + __le32 use_cts_prot; + mutex_lock(&priv->conf_mutex); + erp_info = priv->erp_info; + mutex_unlock(&priv->conf_mutex); + use_cts_prot = + erp_info & WLAN_ERP_USE_PROTECTION ? + __cpu_to_le32(1) : 0; + + erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info; + + pr_debug("[STA] ERP information 0x%x\n", erp_info); + + wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, + &use_cts_prot, sizeof(use_cts_prot)); + wsm_update_ie(priv, &update_ie); + + return; +} + +static int cw1200_set_btcoexinfo(struct cw1200_common *priv) +{ + struct wsm_override_internal_txrate arg; + int ret = 0; + + if (priv->mode == NL80211_IFTYPE_STATION) { + /* Plumb PSPOLL and NULL template */ + cw1200_upload_pspoll(priv); + cw1200_upload_null(priv); + cw1200_upload_qosnull(priv); + } else { + return 0; + } + + memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); + + if (!priv->vif->p2p) { + /* STATION mode */ + if (priv->bss_params.operational_rate_set & ~0xF) { + pr_debug("[STA] STA has ERP rates\n"); + /* G or BG mode */ + arg.internalTxRate = (__ffs( + priv->bss_params.operational_rate_set & ~0xF)); + } else { + pr_debug("[STA] STA has non ERP rates\n"); + /* B only mode */ + arg.internalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set))); + } + arg.nonErpInternalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set))); + } else { + /* P2P mode */ + arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); + arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); + } + + pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n", + priv->mode, + arg.internalTxRate, + arg.nonErpInternalTxRate); + + ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + &arg, sizeof(arg)); + + return ret; +} + +void cw1200_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct cw1200_common *priv = dev->priv; + bool do_join = false; + + mutex_lock(&priv->conf_mutex); + + pr_debug("BSS CHANGED: %08x\n", changed); + + /* TODO: BSS_CHANGED_QOS */ + /* TODO: BSS_CHANGED_TXPOWER */ + + if (changed & BSS_CHANGED_ARP_FILTER) { + struct wsm_mib_arp_ipv4_filter filter = {0}; + int i; + + pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n", + info->arp_addr_cnt); + + /* Currently only one IP address is supported by firmware. + * In case of more IPs arp filtering will be disabled. + */ + if (info->arp_addr_cnt > 0 && + info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { + for (i = 0; i < info->arp_addr_cnt; i++) { + filter.ipv4addrs[i] = info->arp_addr_list[i]; + pr_debug("[STA] addr[%d]: 0x%X\n", + i, filter.ipv4addrs[i]); + } + filter.enable = __cpu_to_le32(1); + } + + pr_debug("[STA] arp ip filter enable: %d\n", + __le32_to_cpu(filter.enable)); + + wsm_set_arp_ipv4_filter(priv, &filter); + } + + if (changed & + (BSS_CHANGED_BEACON | + BSS_CHANGED_AP_PROBE_RESP | + BSS_CHANGED_BSSID | + BSS_CHANGED_SSID | + BSS_CHANGED_IBSS)) { + pr_debug("BSS_CHANGED_BEACON\n"); + priv->beacon_int = info->beacon_int; + cw1200_update_beaconing(priv); + cw1200_upload_beacon(priv); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon); + + if (priv->enable_beacon != info->enable_beacon) { + cw1200_enable_beaconing(priv, info->enable_beacon); + priv->enable_beacon = info->enable_beacon; + } + } + + if (changed & BSS_CHANGED_BEACON_INT) { + pr_debug("CHANGED_BEACON_INT\n"); + if (info->ibss_joined) + do_join = true; + else if (priv->join_status == CW1200_JOIN_STATUS_AP) + cw1200_update_beaconing(priv); + } + + /* assoc/disassoc, or maybe AID changed */ + if (changed & BSS_CHANGED_ASSOC) { + wsm_lock_tx(priv); + priv->wep_default_key_id = -1; + wsm_unlock_tx(priv); + } + + if (changed & BSS_CHANGED_BSSID) { + pr_debug("BSS_CHANGED_BSSID\n"); + do_join = true; + } + + if (changed & + (BSS_CHANGED_ASSOC | + BSS_CHANGED_BSSID | + BSS_CHANGED_IBSS | + BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_HT)) { + pr_debug("BSS_CHANGED_ASSOC\n"); + if (info->assoc) { + if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) { + ieee80211_connection_loss(vif); + mutex_unlock(&priv->conf_mutex); + return; + } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) { + priv->join_status = CW1200_JOIN_STATUS_STA; + } + } else { + do_join = true; + } + + if (info->assoc || info->ibss_joined) { + struct ieee80211_sta *sta = NULL; + __le32 htprot = 0; + + if (info->dtim_period) + priv->join_dtim_period = info->dtim_period; + priv->beacon_int = info->beacon_int; + + rcu_read_lock(); + + if (info->bssid && !info->ibss_joined) + sta = ieee80211_find_sta(vif, info->bssid); + if (sta) { + priv->ht_info.ht_cap = sta->ht_cap; + priv->bss_params.operational_rate_set = + cw1200_rate_mask_to_wsm(priv, + sta->supp_rates[priv->channel->band]); + priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef); + priv->ht_info.operation_mode = info->ht_operation_mode; + } else { + memset(&priv->ht_info, 0, + sizeof(priv->ht_info)); + priv->bss_params.operational_rate_set = -1; + } + rcu_read_unlock(); + + /* Non Greenfield stations present */ + if (priv->ht_info.operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) + htprot |= cpu_to_le32(WSM_NON_GREENFIELD_STA_PRESENT); + + /* Set HT protection method */ + htprot |= cpu_to_le32((priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2); + + /* TODO: + * STBC_param.dual_cts + * STBC_param.LSIG_TXOP_FILL + */ + + wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION, + &htprot, sizeof(htprot)); + + priv->association_mode.greenfield = + cw1200_ht_greenfield(&priv->ht_info); + priv->association_mode.flags = + WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | + WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | + WSM_ASSOCIATION_MODE_USE_HT_MODE | + WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | + WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; + priv->association_mode.preamble = + info->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG; + priv->association_mode.basic_rate_set = __cpu_to_le32( + cw1200_rate_mask_to_wsm(priv, + info->basic_rates)); + priv->association_mode.mpdu_start_spacing = + cw1200_ht_ampdu_density(&priv->ht_info); + + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + cancel_work_sync(&priv->unjoin_work); + + priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count; + priv->bss_params.aid = info->aid; + + if (priv->join_dtim_period < 1) + priv->join_dtim_period = 1; + + pr_debug("[STA] DTIM %d, interval: %d\n", + priv->join_dtim_period, priv->beacon_int); + pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n", + priv->association_mode.preamble, + priv->association_mode.greenfield, + priv->bss_params.aid, + priv->bss_params.operational_rate_set, + priv->association_mode.basic_rate_set); + wsm_set_association_mode(priv, &priv->association_mode); + + if (!info->ibss_joined) { + wsm_keep_alive_period(priv, 30 /* sec */); + wsm_set_bss_params(priv, &priv->bss_params); + priv->setbssparams_done = true; + cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work); + cw1200_set_pm(priv, &priv->powersave_mode); + } + if (priv->vif->p2p) { + pr_debug("[STA] Setting p2p powersave configuration.\n"); + wsm_set_p2p_ps_modeinfo(priv, + &priv->p2p_ps_modeinfo); + } + if (priv->bt_present) + cw1200_set_btcoexinfo(priv); + } else { + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + } + } + + /* ERP Protection */ + if (changed & (BSS_CHANGED_ASSOC | + BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_ERP_PREAMBLE)) { + u32 prev_erp_info = priv->erp_info; + if (info->use_cts_prot) + priv->erp_info |= WLAN_ERP_USE_PROTECTION; + else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) + priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; + + if (info->use_short_preamble) + priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE; + else + priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; + + pr_debug("[STA] ERP Protection: %x\n", priv->erp_info); + + if (prev_erp_info != priv->erp_info) + queue_work(priv->workqueue, &priv->set_cts_work); + } + + /* ERP Slottime */ + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { + __le32 slot_time = info->use_short_slot ? + __cpu_to_le32(9) : __cpu_to_le32(20); + pr_debug("[STA] Slot time: %d us.\n", + __le32_to_cpu(slot_time)); + wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME, + &slot_time, sizeof(slot_time)); + } + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { + struct wsm_rcpi_rssi_threshold threshold = { + .rollingAverageCount = 8, + }; + pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n", + info->cqm_rssi_thold, info->cqm_rssi_hyst); + priv->cqm_rssi_thold = info->cqm_rssi_thold; + priv->cqm_rssi_hyst = info->cqm_rssi_hyst; + + if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { + /* RSSI subscription enabled */ + /* TODO: It's not a correct way of setting threshold. + * Upper and lower must be set equal here and adjusted + * in callback. However current implementation is much + * more relaible and stable. + */ + + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 + */ + if (priv->cqm_use_rssi) { + threshold.upperThreshold = + info->cqm_rssi_thold + info->cqm_rssi_hyst; + threshold.lowerThreshold = + info->cqm_rssi_thold; + threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; + } else { + threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2; + threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2; + } + threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE; + } else { + /* There is a bug in FW, see sta.c. We have to enable + * dummy subscription to get correct RSSI values. + */ + threshold.rssiRcpiMode |= + WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER; + if (priv->cqm_use_rssi) + threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; + } + wsm_set_rcpi_rssi_threshold(priv, &threshold); + } + mutex_unlock(&priv->conf_mutex); + + if (do_join) { + wsm_lock_tx(priv); + cw1200_do_join(priv); /* Will unlock it for us */ + } +} + +void cw1200_multicast_start_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, multicast_start_work); + long tmo = priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024; + + cancel_work_sync(&priv->multicast_stop_work); + + if (!priv->aid0_bit_set) { + wsm_lock_tx(priv); + cw1200_set_tim_impl(priv, true); + priv->aid0_bit_set = true; + mod_timer(&priv->mcast_timeout, jiffies + tmo); + wsm_unlock_tx(priv); + } +} + +void cw1200_multicast_stop_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, multicast_stop_work); + + if (priv->aid0_bit_set) { + del_timer_sync(&priv->mcast_timeout); + wsm_lock_tx(priv); + priv->aid0_bit_set = false; + cw1200_set_tim_impl(priv, false); + wsm_unlock_tx(priv); + } +} + +void cw1200_mcast_timeout(unsigned long arg) +{ + struct cw1200_common *priv = + (struct cw1200_common *)arg; + + wiphy_warn(priv->hw->wiphy, + "Multicast delivery timeout.\n"); + spin_lock_bh(&priv->ps_state_lock); + priv->tx_multicast = priv->aid0_bit_set && + priv->buffered_multicasts; + if (priv->tx_multicast) + cw1200_bh_wakeup(priv); + spin_unlock_bh(&priv->ps_state_lock); +} + +int cw1200_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size, bool amsdu) +{ + /* Aggregation is implemented fully in firmware, + * including block ack negotiation. Do not allow + * mac80211 stack to do anything: it interferes with + * the firmware. + */ + + /* Note that we still need this function stubbed. */ + return -ENOTSUPP; +} + +/* ******************************************************************** */ +/* WSM callback */ +void cw1200_suspend_resume(struct cw1200_common *priv, + struct wsm_suspend_resume *arg) +{ + pr_debug("[AP] %s: %s\n", + arg->stop ? "stop" : "start", + arg->multicast ? "broadcast" : "unicast"); + + if (arg->multicast) { + bool cancel_tmo = false; + spin_lock_bh(&priv->ps_state_lock); + if (arg->stop) { + priv->tx_multicast = false; + } else { + /* Firmware sends this indication every DTIM if there + * is a STA in powersave connected. There is no reason + * to suspend, following wakeup will consume much more + * power than it could be saved. + */ + cw1200_pm_stay_awake(&priv->pm_state, + priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024); + priv->tx_multicast = (priv->aid0_bit_set && + priv->buffered_multicasts); + if (priv->tx_multicast) { + cancel_tmo = true; + cw1200_bh_wakeup(priv); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (cancel_tmo) + del_timer_sync(&priv->mcast_timeout); + } else { + spin_lock_bh(&priv->ps_state_lock); + cw1200_ps_notify(priv, arg->link_id, arg->stop); + spin_unlock_bh(&priv->ps_state_lock); + if (!arg->stop) + cw1200_bh_wakeup(priv); + } + return; +} + +/* ******************************************************************** */ +/* AP privates */ + +static int cw1200_upload_beacon(struct cw1200_common *priv) +{ + int ret = 0; + struct ieee80211_mgmt *mgmt; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_BEACON, + }; + + u16 tim_offset; + u16 tim_len; + + if (priv->mode == NL80211_IFTYPE_STATION || + priv->mode == NL80211_IFTYPE_MONITOR || + priv->mode == NL80211_IFTYPE_UNSPECIFIED) + goto done; + + if (priv->vif->p2p) + frame.rate = WSM_TRANSMIT_RATE_6; + + frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, + &tim_offset, &tim_len); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + if (ret) + goto done; + + /* TODO: Distill probe resp; remove TIM + * and any other beacon-specific IEs + */ + mgmt = (void *)frame.skb->data; + mgmt->frame_control = + __cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + + frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; + if (priv->vif->p2p) { + ret = wsm_set_probe_responder(priv, true); + } else { + ret = wsm_set_template_frame(priv, &frame); + wsm_set_probe_responder(priv, false); + } + +done: + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_pspoll(struct cw1200_common *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PS_POLL, + .rate = 0xFF, + }; + + + frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_null(struct cw1200_common *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_NULL, + .rate = 0xFF, + }; + + frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_qosnull(struct cw1200_common *priv) +{ + /* TODO: This needs to be implemented + + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_QOS_NULL, + .rate = 0xFF, + }; + + frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + */ + return 0; +} + +static int cw1200_enable_beaconing(struct cw1200_common *priv, + bool enable) +{ + struct wsm_beacon_transmit transmit = { + .enable_beaconing = enable, + }; + + return wsm_beacon_transmit(priv, &transmit); +} + +static int cw1200_start_ap(struct cw1200_common *priv) +{ + int ret; + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct wsm_start start = { + .mode = priv->vif->p2p ? + WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, + .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, + .channel_number = priv->channel->hw_value, + .beacon_interval = conf->beacon_int, + .dtim_period = conf->dtim_period, + .preamble = conf->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG, + .probe_delay = 100, + .basic_rate_set = cw1200_rate_mask_to_wsm(priv, + conf->basic_rates), + }; + struct wsm_operational_mode mode = { + .power_mode = cw1200_power_mode, + .disable_more_flag_usage = true, + }; + + memset(start.ssid, 0, sizeof(start.ssid)); + if (!conf->hidden_ssid) { + start.ssid_len = conf->ssid_len; + memcpy(start.ssid, conf->ssid, start.ssid_len); + } + + priv->beacon_int = conf->beacon_int; + priv->join_dtim_period = conf->dtim_period; + + memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); + + pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n", + start.channel_number, start.band, + start.beacon_interval, start.dtim_period, + start.basic_rate_set, + start.ssid_len, start.ssid); + ret = wsm_start(priv, &start); + if (!ret) + ret = cw1200_upload_keys(priv); + if (!ret && priv->vif->p2p) { + pr_debug("[AP] Setting p2p powersave configuration.\n"); + wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo); + } + if (!ret) { + wsm_set_block_ack_policy(priv, 0, 0); + priv->join_status = CW1200_JOIN_STATUS_AP; + cw1200_update_filtering(priv); + } + wsm_set_operational_mode(priv, &mode); + return ret; +} + +static int cw1200_update_beaconing(struct cw1200_common *priv) +{ + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct wsm_reset reset = { + .link_id = 0, + .reset_statistics = true, + }; + + if (priv->mode == NL80211_IFTYPE_AP) { + /* TODO: check if changed channel, band */ + if (priv->join_status != CW1200_JOIN_STATUS_AP || + priv->beacon_int != conf->beacon_int) { + pr_debug("ap restarting\n"); + wsm_lock_tx(priv); + if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE) + wsm_reset(priv, &reset); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + cw1200_start_ap(priv); + wsm_unlock_tx(priv); + } else + pr_debug("ap started join_status: %d\n", + priv->join_status); + } + return 0; +} diff --git a/drivers/net/wireless/st/cw1200/sta.h b/drivers/net/wireless/st/cw1200/sta.h new file mode 100644 index 000000000000..bebb3379017f --- /dev/null +++ b/drivers/net/wireless/st/cw1200/sta.h @@ -0,0 +1,124 @@ +/* + * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef STA_H_INCLUDED +#define STA_H_INCLUDED + +/* ******************************************************************** */ +/* mac80211 API */ + +int cw1200_start(struct ieee80211_hw *dev); +void cw1200_stop(struct ieee80211_hw *dev); +int cw1200_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +void cw1200_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +int cw1200_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p); +int cw1200_config(struct ieee80211_hw *dev, u32 changed); +void cw1200_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast); +int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params); +int cw1200_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats); +int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); + +int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value); + +void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); + +u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list); + +int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_join_complete_cb(struct cw1200_common *priv, + struct wsm_join_complete *arg); + +/* ******************************************************************** */ +/* WSM events */ + +void cw1200_free_event_queue(struct cw1200_common *priv); +void cw1200_event_handler(struct work_struct *work); +void cw1200_bss_loss_work(struct work_struct *work); +void cw1200_bss_params_work(struct work_struct *work); +void cw1200_keep_alive_work(struct work_struct *work); +void cw1200_tx_failure_work(struct work_struct *work); + +void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good, + int bad); +static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv, + int init, int good, int bad) +{ + spin_lock(&priv->bss_loss_lock); + __cw1200_cqm_bssloss_sm(priv, init, good, bad); + spin_unlock(&priv->bss_loss_lock); +} + +/* ******************************************************************** */ +/* Internal API */ + +int cw1200_setup_mac(struct cw1200_common *priv); +void cw1200_join_timeout(struct work_struct *work); +void cw1200_unjoin_work(struct work_struct *work); +void cw1200_join_complete_work(struct work_struct *work); +void cw1200_wep_key_work(struct work_struct *work); +void cw1200_update_listening(struct cw1200_common *priv, bool enabled); +void cw1200_update_filtering(struct cw1200_common *priv); +void cw1200_update_filtering_work(struct work_struct *work); +void cw1200_set_beacon_wakeup_period_work(struct work_struct *work); +int cw1200_enable_listening(struct cw1200_common *priv); +int cw1200_disable_listening(struct cw1200_common *priv); +int cw1200_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg); +void cw1200_ba_work(struct work_struct *work); +void cw1200_ba_timer(unsigned long arg); + +/* AP stuffs */ +int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set); +int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta); +void cw1200_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed); +int cw1200_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size, bool amsdu); + +void cw1200_suspend_resume(struct cw1200_common *priv, + struct wsm_suspend_resume *arg); +void cw1200_set_tim_work(struct work_struct *work); +void cw1200_set_cts_work(struct work_struct *work); +void cw1200_multicast_start_work(struct work_struct *work); +void cw1200_multicast_stop_work(struct work_struct *work); +void cw1200_mcast_timeout(unsigned long arg); + +#endif diff --git a/drivers/net/wireless/st/cw1200/txrx.c b/drivers/net/wireless/st/cw1200/txrx.c new file mode 100644 index 000000000000..d28bd49cb5fd --- /dev/null +++ b/drivers/net/wireless/st/cw1200/txrx.c @@ -0,0 +1,1472 @@ +/* + * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "cw1200.h" +#include "wsm.h" +#include "bh.h" +#include "sta.h" +#include "debug.h" + +#define CW1200_INVALID_RATE_ID (0xFF) + +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb); +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate); + +/* ******************************************************************** */ +/* TX queue lock / unlock */ + +static inline void cw1200_tx_queues_lock(struct cw1200_common *priv) +{ + int i; + for (i = 0; i < 4; ++i) + cw1200_queue_lock(&priv->tx_queue[i]); +} + +static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv) +{ + int i; + for (i = 0; i < 4; ++i) + cw1200_queue_unlock(&priv->tx_queue[i]); +} + +/* ******************************************************************** */ +/* TX policy cache implementation */ + +static void tx_policy_dump(struct tx_policy *policy) +{ + pr_debug("[TX policy] %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n", + policy->raw[0] & 0x0F, policy->raw[0] >> 4, + policy->raw[1] & 0x0F, policy->raw[1] >> 4, + policy->raw[2] & 0x0F, policy->raw[2] >> 4, + policy->raw[3] & 0x0F, policy->raw[3] >> 4, + policy->raw[4] & 0x0F, policy->raw[4] >> 4, + policy->raw[5] & 0x0F, policy->raw[5] >> 4, + policy->raw[6] & 0x0F, policy->raw[6] >> 4, + policy->raw[7] & 0x0F, policy->raw[7] >> 4, + policy->raw[8] & 0x0F, policy->raw[8] >> 4, + policy->raw[9] & 0x0F, policy->raw[9] >> 4, + policy->raw[10] & 0x0F, policy->raw[10] >> 4, + policy->raw[11] & 0x0F, policy->raw[11] >> 4, + policy->defined); +} + +static void tx_policy_build(const struct cw1200_common *priv, + /* [out] */ struct tx_policy *policy, + struct ieee80211_tx_rate *rates, size_t count) +{ + int i, j; + unsigned limit = priv->short_frame_max_tx_count; + unsigned total = 0; + BUG_ON(rates[0].idx < 0); + memset(policy, 0, sizeof(*policy)); + + /* Sort rates in descending order. */ + for (i = 1; i < count; ++i) { + if (rates[i].idx < 0) { + count = i; + break; + } + if (rates[i].idx > rates[i - 1].idx) { + struct ieee80211_tx_rate tmp = rates[i - 1]; + rates[i - 1] = rates[i]; + rates[i] = tmp; + } + } + + /* Eliminate duplicates. */ + total = rates[0].count; + for (i = 0, j = 1; j < count; ++j) { + if (rates[j].idx == rates[i].idx) { + rates[i].count += rates[j].count; + } else if (rates[j].idx > rates[i].idx) { + break; + } else { + ++i; + if (i != j) + rates[i] = rates[j]; + } + total += rates[j].count; + } + count = i + 1; + + /* Re-fill policy trying to keep every requested rate and with + * respect to the global max tx retransmission count. + */ + if (limit < count) + limit = count; + if (total > limit) { + for (i = 0; i < count; ++i) { + int left = count - i - 1; + if (rates[i].count > limit - left) + rates[i].count = limit - left; + limit -= rates[i].count; + } + } + + /* HACK!!! Device has problems (at least) switching from + * 54Mbps CTS to 1Mbps. This switch takes enormous amount + * of time (100-200 ms), leading to valuable throughput drop. + * As a workaround, additional g-rates are injected to the + * policy. + */ + if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && + rates[0].idx > 4 && rates[0].count > 2 && + rates[1].idx < 2) { + int mid_rate = (rates[0].idx + 4) >> 1; + + /* Decrease number of retries for the initial rate */ + rates[0].count -= 2; + + if (mid_rate != 4) { + /* Keep fallback rate at 1Mbps. */ + rates[3] = rates[1]; + + /* Inject 1 transmission on lowest g-rate */ + rates[2].idx = 4; + rates[2].count = 1; + rates[2].flags = rates[1].flags; + + /* Inject 1 transmission on mid-rate */ + rates[1].idx = mid_rate; + rates[1].count = 1; + + /* Fallback to 1 Mbps is a really bad thing, + * so let's try to increase probability of + * successful transmission on the lowest g rate + * even more + */ + if (rates[0].count >= 3) { + --rates[0].count; + ++rates[2].count; + } + + /* Adjust amount of rates defined */ + count += 2; + } else { + /* Keep fallback rate at 1Mbps. */ + rates[2] = rates[1]; + + /* Inject 2 transmissions on lowest g-rate */ + rates[1].idx = 4; + rates[1].count = 2; + + /* Adjust amount of rates defined */ + count += 1; + } + } + + policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1; + + for (i = 0; i < count; ++i) { + register unsigned rateid, off, shift, retries; + + rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value; + off = rateid >> 3; /* eq. rateid / 8 */ + shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ + + retries = rates[i].count; + if (retries > 0x0F) { + rates[i].count = 0x0f; + retries = 0x0F; + } + policy->tbl[off] |= __cpu_to_le32(retries << shift); + policy->retry_count += retries; + } + + pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d\n", + count, + rates[0].idx, rates[0].count, + rates[1].idx, rates[1].count, + rates[2].idx, rates[2].count, + rates[3].idx, rates[3].count); +} + +static inline bool tx_policy_is_equal(const struct tx_policy *wanted, + const struct tx_policy *cached) +{ + size_t count = wanted->defined >> 1; + if (wanted->defined > cached->defined) + return false; + if (count) { + if (memcmp(wanted->raw, cached->raw, count)) + return false; + } + if (wanted->defined & 1) { + if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F)) + return false; + } + return true; +} + +static int tx_policy_find(struct tx_policy_cache *cache, + const struct tx_policy *wanted) +{ + /* O(n) complexity. Not so good, but there's only 8 entries in + * the cache. + * Also lru helps to reduce search time. + */ + struct tx_policy_cache_entry *it; + /* First search for policy in "used" list */ + list_for_each_entry(it, &cache->used, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + /* Then - in "free list" */ + list_for_each_entry(it, &cache->free, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + return -1; +} + +static inline void tx_policy_use(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + ++entry->policy.usage_count; + list_move(&entry->link, &cache->used); +} + +static inline int tx_policy_release(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + int ret = --entry->policy.usage_count; + if (!ret) + list_move(&entry->link, &cache->free); + return ret; +} + +void tx_policy_clean(struct cw1200_common *priv) +{ + int idx, locked; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + struct tx_policy_cache_entry *entry; + + cw1200_tx_queues_lock(priv); + spin_lock_bh(&cache->lock); + locked = list_empty(&cache->free); + + for (idx = 0; idx < TX_POLICY_CACHE_SIZE; idx++) { + entry = &cache->cache[idx]; + /* Policy usage count should be 0 at this time as all queues + should be empty + */ + if (WARN_ON(entry->policy.usage_count)) { + entry->policy.usage_count = 0; + list_move(&entry->link, &cache->free); + } + memset(&entry->policy, 0, sizeof(entry->policy)); + } + if (locked) + cw1200_tx_queues_unlock(priv); + + cw1200_tx_queues_unlock(priv); + spin_unlock_bh(&cache->lock); +} + +/* ******************************************************************** */ +/* External TX policy cache API */ + +void tx_policy_init(struct cw1200_common *priv) +{ + struct tx_policy_cache *cache = &priv->tx_policy_cache; + int i; + + memset(cache, 0, sizeof(*cache)); + + spin_lock_init(&cache->lock); + INIT_LIST_HEAD(&cache->used); + INIT_LIST_HEAD(&cache->free); + + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) + list_add(&cache->cache[i].link, &cache->free); +} + +static int tx_policy_get(struct cw1200_common *priv, + struct ieee80211_tx_rate *rates, + size_t count, bool *renew) +{ + int idx; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + struct tx_policy wanted; + + tx_policy_build(priv, &wanted, rates, count); + + spin_lock_bh(&cache->lock); + if (WARN_ON_ONCE(list_empty(&cache->free))) { + spin_unlock_bh(&cache->lock); + return CW1200_INVALID_RATE_ID; + } + idx = tx_policy_find(cache, &wanted); + if (idx >= 0) { + pr_debug("[TX policy] Used TX policy: %d\n", idx); + *renew = false; + } else { + struct tx_policy_cache_entry *entry; + *renew = true; + /* If policy is not found create a new one + * using the oldest entry in "free" list + */ + entry = list_entry(cache->free.prev, + struct tx_policy_cache_entry, link); + entry->policy = wanted; + idx = entry - cache->cache; + pr_debug("[TX policy] New TX policy: %d\n", idx); + tx_policy_dump(&entry->policy); + } + tx_policy_use(cache, &cache->cache[idx]); + if (list_empty(&cache->free)) { + /* Lock TX queues. */ + cw1200_tx_queues_lock(priv); + } + spin_unlock_bh(&cache->lock); + return idx; +} + +static void tx_policy_put(struct cw1200_common *priv, int idx) +{ + int usage, locked; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + + spin_lock_bh(&cache->lock); + locked = list_empty(&cache->free); + usage = tx_policy_release(cache, &cache->cache[idx]); + if (locked && !usage) { + /* Unlock TX queues. */ + cw1200_tx_queues_unlock(priv); + } + spin_unlock_bh(&cache->lock); +} + +static int tx_policy_upload(struct cw1200_common *priv) +{ + struct tx_policy_cache *cache = &priv->tx_policy_cache; + int i; + struct wsm_set_tx_rate_retry_policy arg = { + .num = 0, + }; + spin_lock_bh(&cache->lock); + + /* Upload only modified entries. */ + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) { + struct tx_policy *src = &cache->cache[i].policy; + if (src->retry_count && !src->uploaded) { + struct wsm_tx_rate_retry_policy *dst = + &arg.tbl[arg.num]; + dst->index = i; + dst->short_retries = priv->short_frame_max_tx_count; + dst->long_retries = priv->long_frame_max_tx_count; + + dst->flags = WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED | + WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT; + memcpy(dst->rate_count_indices, src->tbl, + sizeof(dst->rate_count_indices)); + src->uploaded = 1; + ++arg.num; + } + } + spin_unlock_bh(&cache->lock); + cw1200_debug_tx_cache_miss(priv); + pr_debug("[TX policy] Upload %d policies\n", arg.num); + return wsm_set_tx_rate_retry_policy(priv, &arg); +} + +void tx_policy_upload_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, tx_policy_upload_work); + + pr_debug("[TX] TX policy upload.\n"); + tx_policy_upload(priv); + + wsm_unlock_tx(priv); + cw1200_tx_queues_unlock(priv); +} + +/* ******************************************************************** */ +/* cw1200 TX implementation */ + +struct cw1200_txinfo { + struct sk_buff *skb; + unsigned queue; + struct ieee80211_tx_info *tx_info; + const struct ieee80211_rate *rate; + struct ieee80211_hdr *hdr; + size_t hdrlen; + const u8 *da; + struct cw1200_sta_priv *sta_priv; + struct ieee80211_sta *sta; + struct cw1200_txpriv txpriv; +}; + +u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates) +{ + u32 ret = 0; + int i; + for (i = 0; i < 32; ++i) { + if (rates & BIT(i)) + ret |= BIT(priv->rates[i].hw_value); + } + return ret; +} + +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate) +{ + if (rate->idx < 0) + return NULL; + if (rate->flags & IEEE80211_TX_RC_MCS) + return &priv->mcs_rates[rate->idx]; + return &priv->hw->wiphy->bands[priv->channel->band]-> + bitrates[rate->idx]; +} + +static int +cw1200_tx_h_calc_link_ids(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (t->sta && t->sta_priv->link_id) + t->txpriv.raw_link_id = + t->txpriv.link_id = + t->sta_priv->link_id; + else if (priv->mode != NL80211_IFTYPE_AP) + t->txpriv.raw_link_id = + t->txpriv.link_id = 0; + else if (is_multicast_ether_addr(t->da)) { + if (priv->enable_beacon) { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM; + } else { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = 0; + } + } else { + t->txpriv.link_id = cw1200_find_link_id(priv, t->da); + if (!t->txpriv.link_id) + t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da); + if (!t->txpriv.link_id) { + wiphy_err(priv->hw->wiphy, + "No more link IDs available.\n"); + return -ENOENT; + } + t->txpriv.raw_link_id = t->txpriv.link_id; + } + if (t->txpriv.raw_link_id) + priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp = + jiffies; + if (t->sta && (t->sta->uapsd_queues & BIT(t->queue))) + t->txpriv.link_id = CW1200_LINK_ID_UAPSD; + return 0; +} + +static void +cw1200_tx_h_pm(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (ieee80211_is_auth(t->hdr->frame_control)) { + u32 mask = ~BIT(t->txpriv.raw_link_id); + spin_lock_bh(&priv->ps_state_lock); + priv->sta_asleep_mask &= mask; + priv->pspoll_mask &= mask; + spin_unlock_bh(&priv->ps_state_lock); + } +} + +static void +cw1200_tx_h_calc_tid(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (ieee80211_is_data_qos(t->hdr->frame_control)) { + u8 *qos = ieee80211_get_qos_ctl(t->hdr); + t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; + } else if (ieee80211_is_data(t->hdr->frame_control)) { + t->txpriv.tid = 0; + } +} + +static int +cw1200_tx_h_crypt(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (!t->tx_info->control.hw_key || + !ieee80211_has_protected(t->hdr->frame_control)) + return 0; + + t->hdrlen += t->tx_info->control.hw_key->iv_len; + skb_put(t->skb, t->tx_info->control.hw_key->icv_len); + + if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) + skb_put(t->skb, 8); /* MIC space */ + + return 0; +} + +static int +cw1200_tx_h_align(struct cw1200_common *priv, + struct cw1200_txinfo *t, + u8 *flags) +{ + size_t offset = (size_t)t->skb->data & 3; + + if (!offset) + return 0; + + if (offset & 1) { + wiphy_err(priv->hw->wiphy, + "Bug: attempt to transmit a frame with wrong alignment: %zu\n", + offset); + return -EINVAL; + } + + if (skb_headroom(t->skb) < offset) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated for DMA alignment. headroom: %d\n", + skb_headroom(t->skb)); + return -ENOMEM; + } + skb_push(t->skb, offset); + t->hdrlen += offset; + t->txpriv.offset += offset; + *flags |= WSM_TX_2BYTES_SHIFT; + cw1200_debug_tx_align(priv); + return 0; +} + +static int +cw1200_tx_h_action(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + struct ieee80211_mgmt *mgmt = + (struct ieee80211_mgmt *)t->hdr; + if (ieee80211_is_action(t->hdr->frame_control) && + mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + else + return 0; +} + +/* Add WSM header */ +static struct wsm_tx * +cw1200_tx_h_wsm(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + struct wsm_tx *wsm; + + if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated for WSM header. headroom: %d\n", + skb_headroom(t->skb)); + return NULL; + } + + wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx)); + t->txpriv.offset += sizeof(struct wsm_tx); + memset(wsm, 0, sizeof(*wsm)); + wsm->hdr.len = __cpu_to_le16(t->skb->len); + wsm->hdr.id = __cpu_to_le16(0x0004); + wsm->queue_id = wsm_queue_id_to_wsm(t->queue); + return wsm; +} + +/* BT Coex specific handling */ +static void +cw1200_tx_h_bt(struct cw1200_common *priv, + struct cw1200_txinfo *t, + struct wsm_tx *wsm) +{ + u8 priority = 0; + + if (!priv->bt_present) + return; + + if (ieee80211_is_nullfunc(t->hdr->frame_control)) { + priority = WSM_EPTA_PRIORITY_MGT; + } else if (ieee80211_is_data(t->hdr->frame_control)) { + /* Skip LLC SNAP header (+6) */ + u8 *payload = &t->skb->data[t->hdrlen]; + __be16 *ethertype = (__be16 *)&payload[6]; + if (be16_to_cpu(*ethertype) == ETH_P_PAE) + priority = WSM_EPTA_PRIORITY_EAPOL; + } else if (ieee80211_is_assoc_req(t->hdr->frame_control) || + ieee80211_is_reassoc_req(t->hdr->frame_control)) { + struct ieee80211_mgmt *mgt_frame = + (struct ieee80211_mgmt *)t->hdr; + + if (le16_to_cpu(mgt_frame->u.assoc_req.listen_interval) < + priv->listen_interval) { + pr_debug("Modified Listen Interval to %d from %d\n", + priv->listen_interval, + mgt_frame->u.assoc_req.listen_interval); + /* Replace listen interval derieved from + * the one read from SDD + */ + mgt_frame->u.assoc_req.listen_interval = cpu_to_le16(priv->listen_interval); + } + } + + if (!priority) { + if (ieee80211_is_action(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_ACTION; + else if (ieee80211_is_mgmt(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_MGT; + else if ((wsm->queue_id == WSM_QUEUE_VOICE)) + priority = WSM_EPTA_PRIORITY_VOICE; + else if ((wsm->queue_id == WSM_QUEUE_VIDEO)) + priority = WSM_EPTA_PRIORITY_VIDEO; + else + priority = WSM_EPTA_PRIORITY_DATA; + } + + pr_debug("[TX] EPTA priority %d.\n", priority); + + wsm->flags |= priority << 1; +} + +static int +cw1200_tx_h_rate_policy(struct cw1200_common *priv, + struct cw1200_txinfo *t, + struct wsm_tx *wsm) +{ + bool tx_policy_renew = false; + + t->txpriv.rate_id = tx_policy_get(priv, + t->tx_info->control.rates, IEEE80211_TX_MAX_RATES, + &tx_policy_renew); + if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID) + return -EFAULT; + + wsm->flags |= t->txpriv.rate_id << 4; + + t->rate = cw1200_get_tx_rate(priv, + &t->tx_info->control.rates[0]), + wsm->max_tx_rate = t->rate->hw_value; + if (t->rate->flags & IEEE80211_TX_RC_MCS) { + if (cw1200_ht_greenfield(&priv->ht_info)) + wsm->ht_tx_parameters |= + __cpu_to_le32(WSM_HT_TX_GREENFIELD); + else + wsm->ht_tx_parameters |= + __cpu_to_le32(WSM_HT_TX_MIXED); + } + + if (tx_policy_renew) { + pr_debug("[TX] TX policy renew.\n"); + /* It's not so optimal to stop TX queues every now and then. + * Better to reimplement task scheduling with + * a counter. TODO. + */ + wsm_lock_tx_async(priv); + cw1200_tx_queues_lock(priv); + if (queue_work(priv->workqueue, + &priv->tx_policy_upload_work) <= 0) { + cw1200_tx_queues_unlock(priv); + wsm_unlock_tx(priv); + } + } + return 0; +} + +static bool +cw1200_tx_h_pm_state(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + int was_buffered = 1; + + if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM && + !priv->buffered_multicasts) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_start_work); + } + + if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID) + was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1].buffered[t->txpriv.tid]++; + + return !was_buffered; +} + +/* ******************************************************************** */ + +void cw1200_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct cw1200_common *priv = dev->priv; + struct cw1200_txinfo t = { + .skb = skb, + .queue = skb_get_queue_mapping(skb), + .tx_info = IEEE80211_SKB_CB(skb), + .hdr = (struct ieee80211_hdr *)skb->data, + .txpriv.tid = CW1200_MAX_TID, + .txpriv.rate_id = CW1200_INVALID_RATE_ID, + }; + struct ieee80211_sta *sta; + struct wsm_tx *wsm; + bool tid_update = 0; + u8 flags = 0; + int ret; + + if (priv->bh_error) + goto drop; + + t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control); + t.da = ieee80211_get_DA(t.hdr); + if (control) { + t.sta = control->sta; + t.sta_priv = (struct cw1200_sta_priv *)&t.sta->drv_priv; + } + + if (WARN_ON(t.queue >= 4)) + goto drop; + + ret = cw1200_tx_h_calc_link_ids(priv, &t); + if (ret) + goto drop; + + pr_debug("[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n", + skb->len, t.queue, t.txpriv.link_id, + t.txpriv.raw_link_id); + + cw1200_tx_h_pm(priv, &t); + cw1200_tx_h_calc_tid(priv, &t); + ret = cw1200_tx_h_crypt(priv, &t); + if (ret) + goto drop; + ret = cw1200_tx_h_align(priv, &t, &flags); + if (ret) + goto drop; + ret = cw1200_tx_h_action(priv, &t); + if (ret) + goto drop; + wsm = cw1200_tx_h_wsm(priv, &t); + if (!wsm) { + ret = -ENOMEM; + goto drop; + } + wsm->flags |= flags; + cw1200_tx_h_bt(priv, &t, wsm); + ret = cw1200_tx_h_rate_policy(priv, &t, wsm); + if (ret) + goto drop; + + rcu_read_lock(); + sta = rcu_dereference(t.sta); + + spin_lock_bh(&priv->ps_state_lock); + { + tid_update = cw1200_tx_h_pm_state(priv, &t); + BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue], + t.skb, &t.txpriv)); + } + spin_unlock_bh(&priv->ps_state_lock); + + if (tid_update && sta) + ieee80211_sta_set_buffered(sta, t.txpriv.tid, true); + + rcu_read_unlock(); + + cw1200_bh_wakeup(priv); + + return; + +drop: + cw1200_skb_dtor(priv, skb, &t.txpriv); + return; +} + +/* ******************************************************************** */ + +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + + /* Filter block ACK negotiation: fully controlled by firmware */ + if (mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + + return 0; +} + +static int cw1200_handle_pspoll(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_sta *sta; + struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data; + int link_id = 0; + u32 pspoll_mask = 0; + int drop = 1; + int i; + + if (priv->join_status != CW1200_JOIN_STATUS_AP) + goto done; + if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN)) + goto done; + + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, pspoll->ta); + if (sta) { + struct cw1200_sta_priv *sta_priv; + sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; + link_id = sta_priv->link_id; + pspoll_mask = BIT(sta_priv->link_id); + } + rcu_read_unlock(); + if (!link_id) + goto done; + + priv->pspoll_mask |= pspoll_mask; + drop = 0; + + /* Do not report pspols if data for given link id is queued already. */ + for (i = 0; i < 4; ++i) { + if (cw1200_queue_get_num_queued(&priv->tx_queue[i], + pspoll_mask)) { + cw1200_bh_wakeup(priv); + drop = 1; + break; + } + } + pr_debug("[RX] PSPOLL: %s\n", drop ? "local" : "fwd"); +done: + return drop; +} + +/* ******************************************************************** */ + +void cw1200_tx_confirm_cb(struct cw1200_common *priv, + int link_id, + struct wsm_tx_confirm *arg) +{ + u8 queue_id = cw1200_queue_get_queue_id(arg->packet_id); + struct cw1200_queue *queue = &priv->tx_queue[queue_id]; + struct sk_buff *skb; + const struct cw1200_txpriv *txpriv; + + pr_debug("[TX] TX confirm: %d, %d.\n", + arg->status, arg->ack_failures); + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* STA is stopped. */ + return; + } + + if (WARN_ON(queue_id >= 4)) + return; + + if (arg->status) + pr_debug("TX failed: %d.\n", arg->status); + + if ((arg->status == WSM_REQUEUE) && + (arg->flags & WSM_TX_STATUS_REQUEUE)) { + /* "Requeue" means "implicit suspend" */ + struct wsm_suspend_resume suspend = { + .link_id = link_id, + .stop = 1, + .multicast = !link_id, + }; + cw1200_suspend_resume(priv, &suspend); + wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d). STAs asleep: 0x%.8X\n", + link_id, + cw1200_queue_get_generation(arg->packet_id) + 1, + priv->sta_asleep_mask); + cw1200_queue_requeue(queue, arg->packet_id); + spin_lock_bh(&priv->ps_state_lock); + if (!link_id) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) { + queue_work(priv->workqueue, + &priv->multicast_start_work); + } + } + spin_unlock_bh(&priv->ps_state_lock); + } else if (!cw1200_queue_get_skb(queue, arg->packet_id, + &skb, &txpriv)) { + struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); + int tx_count = arg->ack_failures; + u8 ht_flags = 0; + int i; + + if (cw1200_ht_greenfield(&priv->ht_info)) + ht_flags |= IEEE80211_TX_RC_GREEN_FIELD; + + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_state && + arg->packet_id == priv->bss_loss_confirm_id) { + if (arg->status) { + /* Recovery failed */ + __cw1200_cqm_bssloss_sm(priv, 0, 0, 1); + } else { + /* Recovery succeeded */ + __cw1200_cqm_bssloss_sm(priv, 0, 1, 0); + } + } + spin_unlock(&priv->bss_loss_lock); + + if (!arg->status) { + tx->flags |= IEEE80211_TX_STAT_ACK; + ++tx_count; + cw1200_debug_txed(priv); + if (arg->flags & WSM_TX_STATUS_AGGREGATION) { + /* Do not report aggregation to mac80211: + * it confuses minstrel a lot. + */ + /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ + cw1200_debug_txed_agg(priv); + } + } else { + if (tx_count) + ++tx_count; + } + + for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { + if (tx->status.rates[i].count >= tx_count) { + tx->status.rates[i].count = tx_count; + break; + } + tx_count -= tx->status.rates[i].count; + if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS) + tx->status.rates[i].flags |= ht_flags; + } + + for (++i; i < IEEE80211_TX_MAX_RATES; ++i) { + tx->status.rates[i].count = 0; + tx->status.rates[i].idx = -1; + } + + /* Pull off any crypto trailers that we added on */ + if (tx->control.hw_key) { + skb_trim(skb, skb->len - tx->control.hw_key->icv_len); + if (tx->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) + skb_trim(skb, skb->len - 8); /* MIC space */ + } + cw1200_queue_remove(queue, arg->packet_id); + } + /* XXX TODO: Only wake if there are pending transmits.. */ + cw1200_bh_wakeup(priv); +} + +static void cw1200_notify_buffered_tx(struct cw1200_common *priv, + struct sk_buff *skb, int link_id, int tid) +{ + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + u8 *buffered; + u8 still_buffered = 0; + + if (link_id && tid < CW1200_MAX_TID) { + buffered = priv->link_id_db + [link_id - 1].buffered; + + spin_lock_bh(&priv->ps_state_lock); + if (!WARN_ON(!buffered[tid])) + still_buffered = --buffered[tid]; + spin_unlock_bh(&priv->ps_state_lock); + + if (!still_buffered && tid < CW1200_MAX_TID) { + hdr = (struct ieee80211_hdr *)skb->data; + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, hdr->addr1); + if (sta) + ieee80211_sta_set_buffered(sta, tid, false); + rcu_read_unlock(); + } + } +} + +void cw1200_skb_dtor(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv) +{ + skb_pull(skb, txpriv->offset); + if (txpriv->rate_id != CW1200_INVALID_RATE_ID) { + cw1200_notify_buffered_tx(priv, skb, + txpriv->raw_link_id, txpriv->tid); + tx_policy_put(priv, txpriv->rate_id); + } + ieee80211_tx_status(priv->hw, skb); +} + +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + int link_id, + struct sk_buff **skb_p) +{ + struct sk_buff *skb = *skb_p; + struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct cw1200_link_entry *entry = NULL; + unsigned long grace_period; + + bool early_data = false; + bool p2p = priv->vif && priv->vif->p2p; + size_t hdrlen; + hdr->flag = 0; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* STA is stopped. */ + goto drop; + } + + if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) { + entry = &priv->link_id_db[link_id - 1]; + if (entry->status == CW1200_LINK_SOFT && + ieee80211_is_data(frame->frame_control)) + early_data = true; + entry->timestamp = jiffies; + } else if (p2p && + ieee80211_is_action(frame->frame_control) && + (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + pr_debug("[RX] Going to MAP&RESET link ID\n"); + WARN_ON(work_pending(&priv->linkid_reset_work)); + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = 0; + schedule_work(&priv->linkid_reset_work); + } + + if (link_id && p2p && + ieee80211_is_action(frame->frame_control) && + (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + /* Link ID already exists for the ACTION frame. + * Reset and Remap + */ + WARN_ON(work_pending(&priv->linkid_reset_work)); + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = link_id; + schedule_work(&priv->linkid_reset_work); + } + if (arg->status) { + if (arg->status == WSM_STATUS_MICFAILURE) { + pr_debug("[RX] MIC failure.\n"); + hdr->flag |= RX_FLAG_MMIC_ERROR; + } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { + pr_debug("[RX] No key found.\n"); + goto drop; + } else { + pr_debug("[RX] Receive failure: %d.\n", + arg->status); + goto drop; + } + } + + if (skb->len < sizeof(struct ieee80211_pspoll)) { + wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. Size is lesser than IEEE header.\n"); + goto drop; + } + + if (ieee80211_is_pspoll(frame->frame_control)) + if (cw1200_handle_pspoll(priv, skb)) + goto drop; + + hdr->band = ((arg->channel_number & 0xff00) || + (arg->channel_number > 14)) ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + hdr->freq = ieee80211_channel_to_frequency( + arg->channel_number, + hdr->band); + + if (arg->rx_rate >= 14) { + hdr->flag |= RX_FLAG_HT; + hdr->rate_idx = arg->rx_rate - 14; + } else if (arg->rx_rate >= 4) { + hdr->rate_idx = arg->rx_rate - 2; + } else { + hdr->rate_idx = arg->rx_rate; + } + + hdr->signal = (s8)arg->rcpi_rssi; + hdr->antenna = 0; + + hdrlen = ieee80211_hdrlen(frame->frame_control); + + if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + size_t iv_len = 0, icv_len = 0; + + hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; + + /* Oops... There is no fast way to ask mac80211 about + * IV/ICV lengths. Even defineas are not exposed. + */ + switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + case WSM_RX_STATUS_WEP: + iv_len = 4 /* WEP_IV_LEN */; + icv_len = 4 /* WEP_ICV_LEN */; + break; + case WSM_RX_STATUS_TKIP: + iv_len = 8 /* TKIP_IV_LEN */; + icv_len = 4 /* TKIP_ICV_LEN */ + + 8 /*MICHAEL_MIC_LEN*/; + hdr->flag |= RX_FLAG_MMIC_STRIPPED; + break; + case WSM_RX_STATUS_AES: + iv_len = 8 /* CCMP_HDR_LEN */; + icv_len = 8 /* CCMP_MIC_LEN */; + break; + case WSM_RX_STATUS_WAPI: + iv_len = 18 /* WAPI_HDR_LEN */; + icv_len = 16 /* WAPI_MIC_LEN */; + break; + default: + pr_warn("Unknown encryption type %d\n", + WSM_RX_STATUS_ENCRYPTION(arg->flags)); + goto drop; + } + + /* Firmware strips ICV in case of MIC failure. */ + if (arg->status == WSM_STATUS_MICFAILURE) + icv_len = 0; + + if (skb->len < hdrlen + iv_len + icv_len) { + wiphy_warn(priv->hw->wiphy, "Malformed SDU rx'ed. Size is lesser than crypto headers.\n"); + goto drop; + } + + /* Remove IV, ICV and MIC */ + skb_trim(skb, skb->len - icv_len); + memmove(skb->data + iv_len, skb->data, hdrlen); + skb_pull(skb, iv_len); + } + + /* Remove TSF from the end of frame */ + if (arg->flags & WSM_RX_STATUS_TSF_INCLUDED) { + memcpy(&hdr->mactime, skb->data + skb->len - 8, 8); + hdr->mactime = le64_to_cpu(hdr->mactime); + if (skb->len >= 8) + skb_trim(skb, skb->len - 8); + } else { + hdr->mactime = 0; + } + + cw1200_debug_rxed(priv); + if (arg->flags & WSM_RX_STATUS_AGGREGATE) + cw1200_debug_rxed_agg(priv); + + if (ieee80211_is_action(frame->frame_control) && + (arg->flags & WSM_RX_STATUS_ADDRESS1)) { + if (cw1200_handle_action_rx(priv, skb)) + return; + } else if (ieee80211_is_beacon(frame->frame_control) && + !arg->status && priv->vif && + ether_addr_equal(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid)) { + const u8 *tim_ie; + u8 *ies = ((struct ieee80211_mgmt *) + (skb->data))->u.beacon.variable; + size_t ies_len = skb->len - (ies - (u8 *)(skb->data)); + + tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len); + if (tim_ie) { + struct ieee80211_tim_ie *tim = + (struct ieee80211_tim_ie *)&tim_ie[2]; + + if (priv->join_dtim_period != tim->dtim_period) { + priv->join_dtim_period = tim->dtim_period; + queue_work(priv->workqueue, + &priv->set_beacon_wakeup_period_work); + } + } + + /* Disable beacon filter once we're associated... */ + if (priv->disable_beacon_filter && + (priv->vif->bss_conf.assoc || + priv->vif->bss_conf.ibss_joined)) { + priv->disable_beacon_filter = false; + queue_work(priv->workqueue, + &priv->update_filtering_work); + } + } + + /* Stay awake after frame is received to give + * userspace chance to react and acquire appropriate + * wakelock. + */ + if (ieee80211_is_auth(frame->frame_control)) + grace_period = 5 * HZ; + else if (ieee80211_is_deauth(frame->frame_control)) + grace_period = 5 * HZ; + else + grace_period = 1 * HZ; + cw1200_pm_stay_awake(&priv->pm_state, grace_period); + + if (early_data) { + spin_lock_bh(&priv->ps_state_lock); + /* Double-check status with lock held */ + if (entry->status == CW1200_LINK_SOFT) + skb_queue_tail(&entry->rx_queue, skb); + else + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + } else { + ieee80211_rx_irqsafe(priv->hw, skb); + } + *skb_p = NULL; + + return; + +drop: + /* TODO: update failure counters */ + return; +} + +/* ******************************************************************** */ +/* Security */ + +int cw1200_alloc_key(struct cw1200_common *priv) +{ + int idx; + + idx = ffs(~priv->key_map) - 1; + if (idx < 0 || idx > WSM_KEY_MAX_INDEX) + return -1; + + priv->key_map |= BIT(idx); + priv->keys[idx].index = idx; + return idx; +} + +void cw1200_free_key(struct cw1200_common *priv, int idx) +{ + BUG_ON(!(priv->key_map & BIT(idx))); + memset(&priv->keys[idx], 0, sizeof(priv->keys[idx])); + priv->key_map &= ~BIT(idx); +} + +void cw1200_free_keys(struct cw1200_common *priv) +{ + memset(&priv->keys, 0, sizeof(priv->keys)); + priv->key_map = 0; +} + +int cw1200_upload_keys(struct cw1200_common *priv) +{ + int idx, ret = 0; + for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx) + if (priv->key_map & BIT(idx)) { + ret = wsm_add_key(priv, &priv->keys[idx]); + if (ret < 0) + break; + } + return ret; +} + +/* Workaround for WFD test case 6.1.10 */ +void cw1200_link_id_reset(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, linkid_reset_work); + int temp_linkid; + + if (!priv->action_linkid) { + /* In GO mode we can receive ACTION frames without a linkID */ + temp_linkid = cw1200_alloc_link_id(priv, + &priv->action_frame_sa[0]); + WARN_ON(!temp_linkid); + if (temp_linkid) { + /* Make sure we execute the WQ */ + flush_workqueue(priv->workqueue); + /* Release the link ID */ + spin_lock_bh(&priv->ps_state_lock); + priv->link_id_db[temp_linkid - 1].prev_status = + priv->link_id_db[temp_linkid - 1].status; + priv->link_id_db[temp_linkid - 1].status = + CW1200_LINK_RESET; + spin_unlock_bh(&priv->ps_state_lock); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + } + } else { + spin_lock_bh(&priv->ps_state_lock); + priv->link_id_db[priv->action_linkid - 1].prev_status = + priv->link_id_db[priv->action_linkid - 1].status; + priv->link_id_db[priv->action_linkid - 1].status = + CW1200_LINK_RESET_REMAP; + spin_unlock_bh(&priv->ps_state_lock); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + flush_workqueue(priv->workqueue); + } +} + +int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac) +{ + int i, ret = 0; + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && + priv->link_id_db[i].status) { + priv->link_id_db[i].timestamp = jiffies; + ret = i + 1; + break; + } + } + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) +{ + int i, ret = 0; + unsigned long max_inactivity = 0; + unsigned long now = jiffies; + + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (!priv->link_id_db[i].status) { + ret = i + 1; + break; + } else if (priv->link_id_db[i].status != CW1200_LINK_HARD && + !priv->tx_queue_stats.link_map_cache[i + 1]) { + unsigned long inactivity = + now - priv->link_id_db[i].timestamp; + if (inactivity < max_inactivity) + continue; + max_inactivity = inactivity; + ret = i + 1; + } + } + if (ret) { + struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1]; + pr_debug("[AP] STA added, link_id: %d\n", ret); + entry->status = CW1200_LINK_RESERVE; + memcpy(&entry->mac, mac, ETH_ALEN); + memset(&entry->buffered, 0, CW1200_MAX_TID); + skb_queue_head_init(&entry->rx_queue); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + } else { + wiphy_info(priv->hw->wiphy, + "[AP] Early: no more link IDs available.\n"); + } + + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +void cw1200_link_id_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, link_id_work); + wsm_flush_tx(priv); + cw1200_link_id_gc_work(&priv->link_id_gc_work.work); + wsm_unlock_tx(priv); +} + +void cw1200_link_id_gc_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, link_id_gc_work.work); + struct wsm_reset reset = { + .reset_statistics = false, + }; + struct wsm_map_link map_link = { + .link_id = 0, + }; + unsigned long now = jiffies; + unsigned long next_gc = -1; + long ttl; + bool need_reset; + u32 mask; + int i; + + if (priv->join_status != CW1200_JOIN_STATUS_AP) + return; + + wsm_lock_tx(priv); + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + need_reset = false; + mask = BIT(i + 1); + if (priv->link_id_db[i].status == CW1200_LINK_RESERVE || + (priv->link_id_db[i].status == CW1200_LINK_HARD && + !(priv->link_id_map & mask))) { + if (priv->link_id_map & mask) { + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + need_reset = true; + } + priv->link_id_map |= mask; + if (priv->link_id_db[i].status != CW1200_LINK_HARD) + priv->link_id_db[i].status = CW1200_LINK_SOFT; + memcpy(map_link.mac_addr, priv->link_id_db[i].mac, + ETH_ALEN); + spin_unlock_bh(&priv->ps_state_lock); + if (need_reset) { + reset.link_id = i + 1; + wsm_reset(priv, &reset); + } + map_link.link_id = i + 1; + wsm_map_link(priv, &map_link); + next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT); + spin_lock_bh(&priv->ps_state_lock); + } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) { + ttl = priv->link_id_db[i].timestamp - now + + CW1200_LINK_ID_GC_TIMEOUT; + if (ttl <= 0) { + need_reset = true; + priv->link_id_db[i].status = CW1200_LINK_OFF; + priv->link_id_map &= ~mask; + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + eth_zero_addr(map_link.mac_addr); + spin_unlock_bh(&priv->ps_state_lock); + reset.link_id = i + 1; + wsm_reset(priv, &reset); + spin_lock_bh(&priv->ps_state_lock); + } else { + next_gc = min_t(unsigned long, next_gc, ttl); + } + } else if (priv->link_id_db[i].status == CW1200_LINK_RESET || + priv->link_id_db[i].status == + CW1200_LINK_RESET_REMAP) { + int status = priv->link_id_db[i].status; + priv->link_id_db[i].status = + priv->link_id_db[i].prev_status; + priv->link_id_db[i].timestamp = now; + reset.link_id = i + 1; + spin_unlock_bh(&priv->ps_state_lock); + wsm_reset(priv, &reset); + if (status == CW1200_LINK_RESET_REMAP) { + memcpy(map_link.mac_addr, + priv->link_id_db[i].mac, + ETH_ALEN); + map_link.link_id = i + 1; + wsm_map_link(priv, &map_link); + next_gc = min(next_gc, + CW1200_LINK_ID_GC_TIMEOUT); + } + spin_lock_bh(&priv->ps_state_lock); + } + if (need_reset) { + skb_queue_purge(&priv->link_id_db[i].rx_queue); + pr_debug("[AP] STA removed, link_id: %d\n", + reset.link_id); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (next_gc != -1) + queue_delayed_work(priv->workqueue, + &priv->link_id_gc_work, next_gc); + wsm_unlock_tx(priv); +} diff --git a/drivers/net/wireless/st/cw1200/txrx.h b/drivers/net/wireless/st/cw1200/txrx.h new file mode 100644 index 000000000000..492a4e14213b --- /dev/null +++ b/drivers/net/wireless/st/cw1200/txrx.h @@ -0,0 +1,106 @@ +/* + * Datapath interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_TXRX_H +#define CW1200_TXRX_H + +#include + +/* extern */ struct ieee80211_hw; +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct wsm_rx; +/* extern */ struct wsm_tx_confirm; +/* extern */ struct cw1200_txpriv; + +struct tx_policy { + union { + __le32 tbl[3]; + u8 raw[12]; + }; + u8 defined; + u8 usage_count; + u8 retry_count; + u8 uploaded; +}; + +struct tx_policy_cache_entry { + struct tx_policy policy; + struct list_head link; +}; + +#define TX_POLICY_CACHE_SIZE (8) +struct tx_policy_cache { + struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE]; + struct list_head used; + struct list_head free; + spinlock_t lock; /* Protect policy cache */ +}; + +/* ******************************************************************** */ +/* TX policy cache */ +/* Intention of TX policy cache is an overcomplicated WSM API. + * Device does not accept per-PDU tx retry sequence. + * It uses "tx retry policy id" instead, so driver code has to sync + * linux tx retry sequences with a retry policy table in the device. + */ +void tx_policy_init(struct cw1200_common *priv); +void tx_policy_upload_work(struct work_struct *work); +void tx_policy_clean(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* TX implementation */ + +u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, + u32 rates); +void cw1200_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb); +void cw1200_skb_dtor(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv); + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_tx_confirm_cb(struct cw1200_common *priv, + int link_id, + struct wsm_tx_confirm *arg); +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + int link_id, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* Timeout */ + +void cw1200_tx_timeout(struct work_struct *work); + +/* ******************************************************************** */ +/* Security */ +int cw1200_alloc_key(struct cw1200_common *priv); +void cw1200_free_key(struct cw1200_common *priv, int idx); +void cw1200_free_keys(struct cw1200_common *priv); +int cw1200_upload_keys(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* Workaround for WFD test case 6.1.10 */ +void cw1200_link_id_reset(struct work_struct *work); + +#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) + +int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac); +int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac); +void cw1200_link_id_work(struct work_struct *work); +void cw1200_link_id_gc_work(struct work_struct *work); + + +#endif /* CW1200_TXRX_H */ diff --git a/drivers/net/wireless/st/cw1200/wsm.c b/drivers/net/wireless/st/cw1200/wsm.c new file mode 100644 index 000000000000..9e0ca3048657 --- /dev/null +++ b/drivers/net/wireless/st/cw1200/wsm.c @@ -0,0 +1,1822 @@ +/* + * WSM host interface (HI) implementation for + * ST-Ericsson CW1200 mac80211 drivers. + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include "cw1200.h" +#include "wsm.h" +#include "bh.h" +#include "sta.h" +#include "debug.h" + +#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ +#define WSM_CMD_START_TIMEOUT (7 * HZ) +#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ +#define WSM_CMD_MAX_TIMEOUT (3 * HZ) + +#define WSM_SKIP(buf, size) \ + do { \ + if ((buf)->data + size > (buf)->end) \ + goto underflow; \ + (buf)->data += size; \ + } while (0) + +#define WSM_GET(buf, ptr, size) \ + do { \ + if ((buf)->data + size > (buf)->end) \ + goto underflow; \ + memcpy(ptr, (buf)->data, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_GET(buf, type, type2, cvt) \ + ({ \ + type val; \ + if ((buf)->data + sizeof(type) > (buf)->end) \ + goto underflow; \ + val = cvt(*(type2 *)(buf)->data); \ + (buf)->data += sizeof(type); \ + val; \ + }) + +#define WSM_GET8(buf) __WSM_GET(buf, u8, u8, (u8)) +#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16, __le16_to_cpu) +#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32, __le32_to_cpu) + +#define WSM_PUT(buf, ptr, size) \ + do { \ + if ((buf)->data + size > (buf)->end) \ + if (wsm_buf_reserve((buf), size)) \ + goto nomem; \ + memcpy((buf)->data, ptr, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_PUT(buf, val, type, type2, cvt) \ + do { \ + if ((buf)->data + sizeof(type) > (buf)->end) \ + if (wsm_buf_reserve((buf), sizeof(type))) \ + goto nomem; \ + *(type2 *)(buf)->data = cvt(val); \ + (buf)->data += sizeof(type); \ + } while (0) + +#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, u8, (u8)) +#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __le16, __cpu_to_le16) +#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __le32, __cpu_to_le32) + +static void wsm_buf_reset(struct wsm_buf *buf); +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); + +static int wsm_cmd_send(struct cw1200_common *priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo); + +#define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux)) +#define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux)) + +/* ******************************************************************** */ +/* WSM API implementation */ + +static int wsm_generic_confirm(struct cw1200_common *priv, + void *arg, + struct wsm_buf *buf) +{ + u32 status = WSM_GET32(buf); + if (status != WSM_STATUS_SUCCESS) + return -EINVAL; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); + WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); + WSM_PUT32(buf, arg->dot11RtsThreshold); + + /* DPD block. */ + WSM_PUT16(buf, arg->dpdData_size + 12); + WSM_PUT16(buf, 1); /* DPD version */ + WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); + WSM_PUT16(buf, 5); /* DPD flags */ + WSM_PUT(buf, arg->dpdData, arg->dpdData_size); + + ret = wsm_cmd_send(priv, buf, arg, + WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_configuration_confirm(struct cw1200_common *priv, + struct wsm_configuration *arg, + struct wsm_buf *buf) +{ + int i; + int status; + + status = WSM_GET32(buf); + if (WARN_ON(status != WSM_STATUS_SUCCESS)) + return -EINVAL; + + WSM_GET(buf, arg->dot11StationId, ETH_ALEN); + arg->dot11FrequencyBandsSupported = WSM_GET8(buf); + WSM_SKIP(buf, 1); + arg->supportedRateMask = WSM_GET32(buf); + for (i = 0; i < 2; ++i) { + arg->txPowerRange[i].min_power_level = WSM_GET32(buf); + arg->txPowerRange[i].max_power_level = WSM_GET32(buf); + arg->txPowerRange[i].stepping = WSM_GET32(buf); + } + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id); + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); + ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +struct wsm_mib { + u16 mib_id; + void *buf; + size_t buf_size; +}; + +int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, + size_t buf_size) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mib_id = mib_id, + .buf = _buf, + .buf_size = buf_size, + }; + wsm_cmd_lock(priv); + + WSM_PUT16(buf, mib_id); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, &mib_buf, + WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_read_mib_confirm(struct cw1200_common *priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + u16 size; + if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) + return -EINVAL; + + if (WARN_ON(WSM_GET16(buf) != arg->mib_id)) + return -EINVAL; + + size = WSM_GET16(buf); + if (size > arg->buf_size) + size = arg->buf_size; + + WSM_GET(buf, arg->buf, size); + arg->buf_size = size; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, + size_t buf_size) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mib_id = mib_id, + .buf = _buf, + .buf_size = buf_size, + }; + + wsm_cmd_lock(priv); + + WSM_PUT16(buf, mib_id); + WSM_PUT16(buf, buf_size); + WSM_PUT(buf, _buf, buf_size); + + ret = wsm_cmd_send(priv, buf, &mib_buf, + WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_write_mib_confirm(struct cw1200_common *priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + int ret; + + ret = wsm_generic_confirm(priv, arg, buf); + if (ret) + return ret; + + if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) { + /* OperationalMode: update PM status. */ + const char *p = arg->buf; + cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false); + } + return 0; +} + +/* ******************************************************************** */ + +int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg) +{ + int i; + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + if (arg->num_channels > 48) + return -EINVAL; + + if (arg->num_ssids > 2) + return -EINVAL; + + if (arg->band > 1) + return -EINVAL; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->band); + WSM_PUT8(buf, arg->type); + WSM_PUT8(buf, arg->flags); + WSM_PUT8(buf, arg->max_tx_rate); + WSM_PUT32(buf, arg->auto_scan_interval); + WSM_PUT8(buf, arg->num_probes); + WSM_PUT8(buf, arg->num_channels); + WSM_PUT8(buf, arg->num_ssids); + WSM_PUT8(buf, arg->probe_delay); + + for (i = 0; i < arg->num_channels; ++i) { + WSM_PUT16(buf, arg->ch[i].number); + WSM_PUT16(buf, 0); + WSM_PUT32(buf, arg->ch[i].min_chan_time); + WSM_PUT32(buf, arg->ch[i].max_chan_time); + WSM_PUT32(buf, 0); + } + + for (i = 0; i < arg->num_ssids; ++i) { + WSM_PUT32(buf, arg->ssids[i].length); + WSM_PUT(buf, &arg->ssids[i].ssid[0], + sizeof(arg->ssids[i].ssid)); + } + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_stop_scan(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, + WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + + +static int wsm_tx_confirm(struct cw1200_common *priv, + struct wsm_buf *buf, + int link_id) +{ + struct wsm_tx_confirm tx_confirm; + + tx_confirm.packet_id = WSM_GET32(buf); + tx_confirm.status = WSM_GET32(buf); + tx_confirm.tx_rate = WSM_GET8(buf); + tx_confirm.ack_failures = WSM_GET8(buf); + tx_confirm.flags = WSM_GET16(buf); + tx_confirm.media_delay = WSM_GET32(buf); + tx_confirm.tx_queue_delay = WSM_GET32(buf); + + cw1200_tx_confirm_cb(priv, link_id, &tx_confirm); + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_multi_tx_confirm(struct cw1200_common *priv, + struct wsm_buf *buf, int link_id) +{ + int ret; + int count; + int i; + + count = WSM_GET32(buf); + if (WARN_ON(count <= 0)) + return -EINVAL; + + if (count > 1) { + /* We already released one buffer, now for the rest */ + ret = wsm_release_tx_buffer(priv, count - 1); + if (ret < 0) + return ret; + else if (ret > 0) + cw1200_bh_wakeup(priv); + } + + cw1200_debug_txed_multi(priv, count); + for (i = 0; i < count; ++i) { + ret = wsm_tx_confirm(priv, buf, link_id); + if (ret) + return ret; + } + return ret; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +static int wsm_join_confirm(struct cw1200_common *priv, + struct wsm_join_cnf *arg, + struct wsm_buf *buf) +{ + arg->status = WSM_GET32(buf); + if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS) + return -EINVAL; + + arg->min_power_level = WSM_GET32(buf); + arg->max_power_level = WSM_GET32(buf); + + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +int wsm_join(struct cw1200_common *priv, struct wsm_join *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_join_cnf resp; + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channel_number); + WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); + WSM_PUT16(buf, arg->atim_window); + WSM_PUT8(buf, arg->preamble_type); + WSM_PUT8(buf, arg->probe_for_join); + WSM_PUT8(buf, arg->dtim_period); + WSM_PUT8(buf, arg->flags); + WSM_PUT32(buf, arg->ssid_len); + WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); + WSM_PUT32(buf, arg->beacon_interval); + WSM_PUT32(buf, arg->basic_rate_set); + + priv->tx_burst_idx = -1; + ret = wsm_cmd_send(priv, buf, &resp, + WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT); + /* TODO: Update state based on resp.min|max_power_level */ + + priv->join_complete_status = resp.status; + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_bss_params(struct cw1200_common *priv, + const struct wsm_set_bss_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, (arg->reset_beacon_loss ? 0x1 : 0)); + WSM_PUT8(buf, arg->beacon_lost_count); + WSM_PUT16(buf, arg->aid); + WSM_PUT32(buf, arg->operational_rate_set); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT(buf, arg, sizeof(*arg)); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->index); + WSM_PUT8(buf, 0); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_tx_queue_params(struct cw1200_common *priv, + const struct wsm_set_tx_queue_params *arg, u8 id) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, queue_id_to_wmm_aci[id]); + WSM_PUT8(buf, 0); + WSM_PUT8(buf, arg->ackPolicy); + WSM_PUT8(buf, 0); + WSM_PUT32(buf, arg->maxTransmitLifetime); + WSM_PUT16(buf, arg->allowedMediumTime); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_edca_params(struct cw1200_common *priv, + const struct wsm_edca_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + /* Implemented according to specification. */ + + WSM_PUT16(buf, arg->params[3].cwmin); + WSM_PUT16(buf, arg->params[2].cwmin); + WSM_PUT16(buf, arg->params[1].cwmin); + WSM_PUT16(buf, arg->params[0].cwmin); + + WSM_PUT16(buf, arg->params[3].cwmax); + WSM_PUT16(buf, arg->params[2].cwmax); + WSM_PUT16(buf, arg->params[1].cwmax); + WSM_PUT16(buf, arg->params[0].cwmax); + + WSM_PUT8(buf, arg->params[3].aifns); + WSM_PUT8(buf, arg->params[2].aifns); + WSM_PUT8(buf, arg->params[1].aifns); + WSM_PUT8(buf, arg->params[0].aifns); + + WSM_PUT16(buf, arg->params[3].txop_limit); + WSM_PUT16(buf, arg->params[2].txop_limit); + WSM_PUT16(buf, arg->params[1].txop_limit); + WSM_PUT16(buf, arg->params[0].txop_limit); + + WSM_PUT32(buf, arg->params[3].max_rx_lifetime); + WSM_PUT32(buf, arg->params[2].max_rx_lifetime); + WSM_PUT32(buf, arg->params[1].max_rx_lifetime); + WSM_PUT32(buf, arg->params[0].max_rx_lifetime); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_switch_channel(struct cw1200_common *priv, + const struct wsm_switch_channel *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->switch_count); + WSM_PUT16(buf, arg->channel_number); + + priv->channel_switch_in_progress = 1; + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT); + if (ret) + priv->channel_switch_in_progress = 0; + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + priv->ps_mode_switch_in_progress = 1; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->fast_psm_idle_period); + WSM_PUT8(buf, arg->ap_psm_change_period); + WSM_PUT8(buf, arg->min_auto_pspoll_period); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channel_number); + WSM_PUT32(buf, arg->ct_window); + WSM_PUT32(buf, arg->beacon_interval); + WSM_PUT8(buf, arg->dtim_period); + WSM_PUT8(buf, arg->preamble); + WSM_PUT8(buf, arg->probe_delay); + WSM_PUT8(buf, arg->ssid_len); + WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); + WSM_PUT32(buf, arg->basic_rate_set); + + priv->tx_burst_idx = -1; + ret = wsm_cmd_send(priv, buf, NULL, + WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_beacon_transmit(struct cw1200_common *priv, + const struct wsm_beacon_transmit *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start_find(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_stop_find(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id); + + wsm_cmd_lock(priv); + + WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_update_ie(struct cw1200_common *priv, + const struct wsm_update_ie *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT16(buf, arg->what); + WSM_PUT16(buf, arg->count); + WSM_PUT(buf, arg->ies, arg->length); + + ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ +int wsm_set_probe_responder(struct cw1200_common *priv, bool enable) +{ + priv->rx_filter.probeResponder = enable; + return wsm_set_rx_filter(priv, &priv->rx_filter); +} + +/* ******************************************************************** */ +/* WSM indication events implementation */ +const char * const cw1200_fw_types[] = { + "ETF", + "WFM", + "WSM", + "HI test", + "Platform test" +}; + +static int wsm_startup_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + priv->wsm_caps.input_buffers = WSM_GET16(buf); + priv->wsm_caps.input_buffer_size = WSM_GET16(buf); + priv->wsm_caps.hw_id = WSM_GET16(buf); + priv->wsm_caps.hw_subid = WSM_GET16(buf); + priv->wsm_caps.status = WSM_GET16(buf); + priv->wsm_caps.fw_cap = WSM_GET16(buf); + priv->wsm_caps.fw_type = WSM_GET16(buf); + priv->wsm_caps.fw_api = WSM_GET16(buf); + priv->wsm_caps.fw_build = WSM_GET16(buf); + priv->wsm_caps.fw_ver = WSM_GET16(buf); + WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label)); + priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */ + + if (WARN_ON(priv->wsm_caps.status)) + return -EINVAL; + + if (WARN_ON(priv->wsm_caps.fw_type > 4)) + return -EINVAL; + + pr_info("CW1200 WSM init done.\n" + " Input buffers: %d x %d bytes\n" + " Hardware: %d.%d\n" + " %s firmware [%s], ver: %d, build: %d," + " api: %d, cap: 0x%.4X\n", + priv->wsm_caps.input_buffers, + priv->wsm_caps.input_buffer_size, + priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid, + cw1200_fw_types[priv->wsm_caps.fw_type], + priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver, + priv->wsm_caps.fw_build, + priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap); + + /* Disable unsupported frequency bands */ + if (!(priv->wsm_caps.fw_cap & 0x1)) + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; + if (!(priv->wsm_caps.fw_cap & 0x2)) + priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; + + priv->firmware_ready = 1; + wake_up(&priv->wsm_startup_done); + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_receive_indication(struct cw1200_common *priv, + int link_id, + struct wsm_buf *buf, + struct sk_buff **skb_p) +{ + struct wsm_rx rx; + struct ieee80211_hdr *hdr; + size_t hdr_len; + __le16 fctl; + + rx.status = WSM_GET32(buf); + rx.channel_number = WSM_GET16(buf); + rx.rx_rate = WSM_GET8(buf); + rx.rcpi_rssi = WSM_GET8(buf); + rx.flags = WSM_GET32(buf); + + /* FW Workaround: Drop probe resp or + beacon when RSSI is 0 + */ + hdr = (struct ieee80211_hdr *)(*skb_p)->data; + + if (!rx.rcpi_rssi && + (ieee80211_is_probe_resp(hdr->frame_control) || + ieee80211_is_beacon(hdr->frame_control))) + return 0; + + /* If no RSSI subscription has been made, + * convert RCPI to RSSI here + */ + if (!priv->cqm_use_rssi) + rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110; + + fctl = *(__le16 *)buf->data; + hdr_len = buf->data - buf->begin; + skb_pull(*skb_p, hdr_len); + if (!rx.status && ieee80211_is_deauth(fctl)) { + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + /* Shedule unjoin work */ + pr_debug("[WSM] Issue unjoin command (RX).\n"); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } + } + cw1200_rx_cb(priv, &rx, link_id, skb_p); + if (*skb_p) + skb_push(*skb_p, hdr_len); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf) +{ + int first; + struct cw1200_wsm_event *event; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* STA is stopped. */ + return 0; + } + + event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL); + if (!event) + return -ENOMEM; + + event->evt.id = WSM_GET32(buf); + event->evt.data = WSM_GET32(buf); + + pr_debug("[WSM] Event: %d(%d)\n", + event->evt.id, event->evt.data); + + spin_lock(&priv->event_queue_lock); + first = list_empty(&priv->event_queue); + list_add_tail(&event->link, &priv->event_queue); + spin_unlock(&priv->event_queue_lock); + + if (first) + queue_work(priv->workqueue, &priv->event_handler); + + return 0; + +underflow: + kfree(event); + return -EINVAL; +} + +static int wsm_channel_switch_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + WARN_ON(WSM_GET32(buf)); + + priv->channel_switch_in_progress = 0; + wake_up(&priv->channel_switch_done); + + wsm_unlock_tx(priv); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_set_pm_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + /* TODO: Check buf (struct wsm_set_pm_complete) for validity */ + if (priv->ps_mode_switch_in_progress) { + priv->ps_mode_switch_in_progress = 0; + wake_up(&priv->ps_mode_switch_done); + } + return 0; +} + +static int wsm_scan_started(struct cw1200_common *priv, void *arg, + struct wsm_buf *buf) +{ + u32 status = WSM_GET32(buf); + if (status != WSM_STATUS_SUCCESS) { + cw1200_scan_failed_cb(priv); + return -EINVAL; + } + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_scan_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + struct wsm_scan_complete arg; + arg.status = WSM_GET32(buf); + arg.psm = WSM_GET8(buf); + arg.num_channels = WSM_GET8(buf); + cw1200_scan_complete_cb(priv, &arg); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_join_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + struct wsm_join_complete arg; + arg.status = WSM_GET32(buf); + pr_debug("[WSM] Join complete indication, status: %d\n", arg.status); + cw1200_join_complete_cb(priv, &arg); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_find_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + pr_warn("Implement find_complete_indication\n"); + return 0; +} + +static int wsm_ba_timeout_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + u32 dummy; + u8 tid; + u8 dummy2; + u8 addr[ETH_ALEN]; + + dummy = WSM_GET32(buf); + tid = WSM_GET8(buf); + dummy2 = WSM_GET8(buf); + WSM_GET(buf, addr, ETH_ALEN); + + pr_info("BlockACK timeout, tid %d, addr %pM\n", + tid, addr); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_suspend_resume_indication(struct cw1200_common *priv, + int link_id, struct wsm_buf *buf) +{ + u32 flags; + struct wsm_suspend_resume arg; + + flags = WSM_GET32(buf); + arg.link_id = link_id; + arg.stop = !(flags & 1); + arg.multicast = !!(flags & 8); + arg.queue = (flags >> 1) & 3; + + cw1200_suspend_resume(priv, &arg); + + return 0; + +underflow: + return -EINVAL; +} + + +/* ******************************************************************** */ +/* WSM TX */ + +static int wsm_cmd_send(struct cw1200_common *priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo) +{ + size_t buf_len = buf->data - buf->begin; + int ret; + + /* Don't bother if we're dead. */ + if (priv->bh_error) { + ret = 0; + goto done; + } + + /* Block until the cmd buffer is completed. Tortuous. */ + spin_lock(&priv->wsm_cmd.lock); + while (!priv->wsm_cmd.done) { + spin_unlock(&priv->wsm_cmd.lock); + spin_lock(&priv->wsm_cmd.lock); + } + priv->wsm_cmd.done = 0; + spin_unlock(&priv->wsm_cmd.lock); + + if (cmd == WSM_WRITE_MIB_REQ_ID || + cmd == WSM_READ_MIB_REQ_ID) + pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n", + cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]), + buf_len); + else + pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len); + + /* Due to buggy SPI on CW1200, we need to + * pad the message by a few bytes to ensure + * that it's completely received. + */ + buf_len += 4; + + /* Fill HI message header */ + /* BH will add sequence number */ + ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); + ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); + + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(priv->wsm_cmd.ptr); + priv->wsm_cmd.ptr = buf->begin; + priv->wsm_cmd.len = buf_len; + priv->wsm_cmd.arg = arg; + priv->wsm_cmd.cmd = cmd; + spin_unlock(&priv->wsm_cmd.lock); + + cw1200_bh_wakeup(priv); + + /* Wait for command completion */ + ret = wait_event_timeout(priv->wsm_cmd_wq, + priv->wsm_cmd.done, tmo); + + if (!ret && !priv->wsm_cmd.done) { + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.done = 1; + priv->wsm_cmd.ptr = NULL; + spin_unlock(&priv->wsm_cmd.lock); + if (priv->bh_error) { + /* Return ok to help system cleanup */ + ret = 0; + } else { + pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd); + print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE, + buf->begin, buf_len); + pr_err("Outstanding outgoing frames: %d\n", priv->hw_bufs_used); + + /* Kill BH thread to report the error to the top layer. */ + atomic_add(1, &priv->bh_term); + wake_up(&priv->bh_wq); + ret = -ETIMEDOUT; + } + } else { + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(!priv->wsm_cmd.done); + ret = priv->wsm_cmd.ret; + spin_unlock(&priv->wsm_cmd.lock); + } +done: + wsm_buf_reset(buf); + return ret; +} + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct cw1200_common *priv) +{ + wsm_cmd_lock(priv); + if (atomic_add_return(1, &priv->tx_lock) == 1) { + if (wsm_flush_tx(priv)) + pr_debug("[WSM] TX is locked.\n"); + } + wsm_cmd_unlock(priv); +} + +void wsm_lock_tx_async(struct cw1200_common *priv) +{ + if (atomic_add_return(1, &priv->tx_lock) == 1) + pr_debug("[WSM] TX is locked (async).\n"); +} + +bool wsm_flush_tx(struct cw1200_common *priv) +{ + unsigned long timestamp = jiffies; + bool pending = false; + long timeout; + int i; + + /* Flush must be called with TX lock held. */ + BUG_ON(!atomic_read(&priv->tx_lock)); + + /* First check if we really need to do something. + * It is safe to use unprotected access, as hw_bufs_used + * can only decrements. + */ + if (!priv->hw_bufs_used) + return true; + + if (priv->bh_error) { + /* In case of failure do not wait for magic. */ + pr_err("[WSM] Fatal error occurred, will not flush TX.\n"); + return false; + } else { + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + pending |= cw1200_queue_get_xmit_timestamp( + &priv->tx_queue[i], + ×tamp, 0xffffffff); + /* If there's nothing pending, we're good */ + if (!pending) + return true; + + timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies; + if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, + timeout) <= 0) { + /* Hmmm... Not good. Frame had stuck in firmware. */ + priv->bh_error = 1; + wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used); + wake_up(&priv->bh_wq); + return false; + } + + /* Ok, everything is flushed. */ + return true; + } +} + +void wsm_unlock_tx(struct cw1200_common *priv) +{ + int tx_lock; + tx_lock = atomic_sub_return(1, &priv->tx_lock); + BUG_ON(tx_lock < 0); + + if (tx_lock == 0) { + if (!priv->bh_error) + cw1200_bh_wakeup(priv); + pr_debug("[WSM] TX is unlocked.\n"); + } +} + +/* ******************************************************************** */ +/* WSM RX */ + +int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) +{ + struct wsm_buf buf; + u32 reason; + u32 reg[18]; + char fname[48]; + unsigned int i; + + static const char * const reason_str[] = { + "undefined instruction", + "prefetch abort", + "data abort", + "unknown error", + }; + + buf.begin = buf.data = data; + buf.end = &buf.begin[len]; + + reason = WSM_GET32(&buf); + for (i = 0; i < ARRAY_SIZE(reg); ++i) + reg[i] = WSM_GET32(&buf); + WSM_GET(&buf, fname, sizeof(fname)); + + if (reason < 4) + wiphy_err(priv->hw->wiphy, + "Firmware exception: %s.\n", + reason_str[reason]); + else + wiphy_err(priv->hw->wiphy, + "Firmware assert at %.*s, line %d\n", + (int) sizeof(fname), fname, reg[1]); + + for (i = 0; i < 12; i += 4) + wiphy_err(priv->hw->wiphy, + "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", + i + 0, reg[i + 0], i + 1, reg[i + 1], + i + 2, reg[i + 2], i + 3, reg[i + 3]); + wiphy_err(priv->hw->wiphy, + "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", + reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); + i += 4; + wiphy_err(priv->hw->wiphy, + "CPSR: 0x%.8X, SPSR: 0x%.8X\n", + reg[i + 0], reg[i + 1]); + + print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE, + fname, sizeof(fname)); + return 0; + +underflow: + wiphy_err(priv->hw->wiphy, "Firmware exception.\n"); + print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, + data, len); + return -EINVAL; +} + +int wsm_handle_rx(struct cw1200_common *priv, u16 id, + struct wsm_hdr *wsm, struct sk_buff **skb_p) +{ + int ret = 0; + struct wsm_buf wsm_buf; + int link_id = (id >> 6) & 0x0F; + + /* Strip link id. */ + id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + + wsm_buf.begin = (u8 *)&wsm[0]; + wsm_buf.data = (u8 *)&wsm[1]; + wsm_buf.end = &wsm_buf.begin[__le16_to_cpu(wsm->len)]; + + pr_debug("[WSM] <<< 0x%.4X (%td)\n", id, + wsm_buf.end - wsm_buf.begin); + + if (id == WSM_TX_CONFIRM_IND_ID) { + ret = wsm_tx_confirm(priv, &wsm_buf, link_id); + } else if (id == WSM_MULTI_TX_CONFIRM_ID) { + ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id); + } else if (id & 0x0400) { + void *wsm_arg; + u16 wsm_cmd; + + /* Do not trust FW too much. Protection against repeated + * response and race condition removal (see above). + */ + spin_lock(&priv->wsm_cmd.lock); + wsm_arg = priv->wsm_cmd.arg; + wsm_cmd = priv->wsm_cmd.cmd & + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + priv->wsm_cmd.cmd = 0xFFFF; + spin_unlock(&priv->wsm_cmd.lock); + + if (WARN_ON((id & ~0x0400) != wsm_cmd)) { + /* Note that any non-zero is a fatal retcode. */ + ret = -EINVAL; + goto out; + } + + /* Note that wsm_arg can be NULL in case of timeout in + * wsm_cmd_send(). + */ + + switch (id) { + case WSM_READ_MIB_RESP_ID: + if (wsm_arg) + ret = wsm_read_mib_confirm(priv, wsm_arg, + &wsm_buf); + break; + case WSM_WRITE_MIB_RESP_ID: + if (wsm_arg) + ret = wsm_write_mib_confirm(priv, wsm_arg, + &wsm_buf); + break; + case WSM_START_SCAN_RESP_ID: + if (wsm_arg) + ret = wsm_scan_started(priv, wsm_arg, &wsm_buf); + break; + case WSM_CONFIGURATION_RESP_ID: + if (wsm_arg) + ret = wsm_configuration_confirm(priv, wsm_arg, + &wsm_buf); + break; + case WSM_JOIN_RESP_ID: + if (wsm_arg) + ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf); + break; + case WSM_STOP_SCAN_RESP_ID: + case WSM_RESET_RESP_ID: + case WSM_ADD_KEY_RESP_ID: + case WSM_REMOVE_KEY_RESP_ID: + case WSM_SET_PM_RESP_ID: + case WSM_SET_BSS_PARAMS_RESP_ID: + case 0x0412: /* set_tx_queue_params */ + case WSM_EDCA_PARAMS_RESP_ID: + case WSM_SWITCH_CHANNEL_RESP_ID: + case WSM_START_RESP_ID: + case WSM_BEACON_TRANSMIT_RESP_ID: + case 0x0419: /* start_find */ + case 0x041A: /* stop_find */ + case 0x041B: /* update_ie */ + case 0x041C: /* map_link */ + WARN_ON(wsm_arg != NULL); + ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf); + if (ret) { + wiphy_warn(priv->hw->wiphy, + "wsm_generic_confirm failed for request 0x%04x.\n", + id & ~0x0400); + + /* often 0x407 and 0x410 occur, this means we're dead.. */ + if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) { + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } + } + break; + default: + wiphy_warn(priv->hw->wiphy, + "Unrecognized confirmation 0x%04x\n", + id & ~0x0400); + } + + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.ret = ret; + priv->wsm_cmd.done = 1; + spin_unlock(&priv->wsm_cmd.lock); + + ret = 0; /* Error response from device should ne stop BH. */ + + wake_up(&priv->wsm_cmd_wq); + } else if (id & 0x0800) { + switch (id) { + case WSM_STARTUP_IND_ID: + ret = wsm_startup_indication(priv, &wsm_buf); + break; + case WSM_RECEIVE_IND_ID: + ret = wsm_receive_indication(priv, link_id, + &wsm_buf, skb_p); + break; + case 0x0805: + ret = wsm_event_indication(priv, &wsm_buf); + break; + case WSM_SCAN_COMPLETE_IND_ID: + ret = wsm_scan_complete_indication(priv, &wsm_buf); + break; + case 0x0808: + ret = wsm_ba_timeout_indication(priv, &wsm_buf); + break; + case 0x0809: + ret = wsm_set_pm_indication(priv, &wsm_buf); + break; + case 0x080A: + ret = wsm_channel_switch_indication(priv, &wsm_buf); + break; + case 0x080B: + ret = wsm_find_complete_indication(priv, &wsm_buf); + break; + case 0x080C: + ret = wsm_suspend_resume_indication(priv, + link_id, &wsm_buf); + break; + case 0x080F: + ret = wsm_join_complete_indication(priv, &wsm_buf); + break; + default: + pr_warn("Unrecognised WSM ID %04x\n", id); + } + } else { + WARN_ON(1); + ret = -EINVAL; + } +out: + return ret; +} + +static bool wsm_handle_tx_data(struct cw1200_common *priv, + struct wsm_tx *wsm, + const struct ieee80211_tx_info *tx_info, + const struct cw1200_txpriv *txpriv, + struct cw1200_queue *queue) +{ + bool handled = false; + const struct ieee80211_hdr *frame = + (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset]; + __le16 fctl = frame->frame_control; + enum { + do_probe, + do_drop, + do_wep, + do_tx, + } action = do_tx; + + switch (priv->mode) { + case NL80211_IFTYPE_STATION: + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + action = do_tx; + else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) + action = do_drop; + break; + case NL80211_IFTYPE_AP: + if (!priv->join_status) { + action = do_drop; + } else if (!(BIT(txpriv->raw_link_id) & + (BIT(0) | priv->link_id_map))) { + wiphy_warn(priv->hw->wiphy, + "A frame with expired link id is dropped.\n"); + action = do_drop; + } + if (cw1200_queue_get_generation(wsm->packet_id) > + CW1200_MAX_REQUEUE_ATTEMPTS) { + /* HACK!!! WSM324 firmware has tendency to requeue + * multicast frames in a loop, causing performance + * drop and high power consumption of the driver. + * In this situation it is better just to drop + * the problematic frame. + */ + wiphy_warn(priv->hw->wiphy, + "Too many attempts to requeue a frame; dropped.\n"); + action = do_drop; + } + break; + case NL80211_IFTYPE_ADHOC: + if (priv->join_status != CW1200_JOIN_STATUS_IBSS) + action = do_drop; + break; + case NL80211_IFTYPE_MESH_POINT: + action = do_tx; /* TODO: Test me! */ + break; + case NL80211_IFTYPE_MONITOR: + default: + action = do_drop; + break; + } + + if (action == do_tx) { + if (ieee80211_is_nullfunc(fctl)) { + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_state) { + priv->bss_loss_confirm_id = wsm->packet_id; + wsm->queue_id = WSM_QUEUE_VOICE; + } + spin_unlock(&priv->bss_loss_lock); + } else if (ieee80211_is_probe_req(fctl)) { + action = do_probe; + } else if (ieee80211_is_deauth(fctl) && + priv->mode != NL80211_IFTYPE_AP) { + pr_debug("[WSM] Issue unjoin command due to tx deauth.\n"); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } else if (ieee80211_has_protected(fctl) && + tx_info->control.hw_key && + tx_info->control.hw_key->keyidx != priv->wep_default_key_id && + (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 || + tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) { + action = do_wep; + } + } + + switch (action) { + case do_probe: + /* An interesting FW "feature". Device filters probe responses. + * The easiest way to get it back is to convert + * probe request into WSM start_scan command. + */ + pr_debug("[WSM] Convert probe request to scan.\n"); + wsm_lock_tx_async(priv); + priv->pending_frame_id = wsm->packet_id; + if (queue_delayed_work(priv->workqueue, + &priv->scan.probe_work, 0) <= 0) + wsm_unlock_tx(priv); + handled = true; + break; + case do_drop: + pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl); + BUG_ON(cw1200_queue_remove(queue, wsm->packet_id)); + handled = true; + break; + case do_wep: + pr_debug("[WSM] Issue set_default_wep_key.\n"); + wsm_lock_tx_async(priv); + priv->wep_default_key_id = tx_info->control.hw_key->keyidx; + priv->pending_frame_id = wsm->packet_id; + if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0) + wsm_unlock_tx(priv); + handled = true; + break; + case do_tx: + pr_debug("[WSM] Transmit frame.\n"); + break; + default: + /* Do nothing */ + break; + } + return handled; +} + +static int cw1200_get_prio_queue(struct cw1200_common *priv, + u32 link_id_map, int *total) +{ + static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) | + BIT(CW1200_LINK_ID_UAPSD); + struct wsm_edca_queue_params *edca; + unsigned score, best = -1; + int winner = -1; + int queued; + int i; + + /* search for a winner using edca params */ + for (i = 0; i < 4; ++i) { + queued = cw1200_queue_get_num_queued(&priv->tx_queue[i], + link_id_map); + if (!queued) + continue; + *total += queued; + edca = &priv->edca.params[i]; + score = ((edca->aifns + edca->cwmin) << 16) + + ((edca->cwmax - edca->cwmin) * + (get_random_int() & 0xFFFF)); + if (score < best && (winner < 0 || i != 3)) { + best = score; + winner = i; + } + } + + /* override winner if bursting */ + if (winner >= 0 && priv->tx_burst_idx >= 0 && + winner != priv->tx_burst_idx && + !cw1200_queue_get_num_queued( + &priv->tx_queue[winner], + link_id_map & urgent) && + cw1200_queue_get_num_queued( + &priv->tx_queue[priv->tx_burst_idx], + link_id_map)) + winner = priv->tx_burst_idx; + + return winner; +} + +static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, + struct cw1200_queue **queue_p, + u32 *tx_allowed_mask_p, + bool *more) +{ + int idx; + u32 tx_allowed_mask; + int total = 0; + + /* Search for a queue with multicast frames buffered */ + if (priv->tx_multicast) { + tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM); + idx = cw1200_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx >= 0) { + *more = total > 1; + goto found; + } + } + + /* Search for unicast traffic */ + tx_allowed_mask = ~priv->sta_asleep_mask; + tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD); + if (priv->sta_asleep_mask) { + tx_allowed_mask |= priv->pspoll_mask; + tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); + } else { + tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); + } + idx = cw1200_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx < 0) + return -ENOENT; + +found: + *queue_p = &priv->tx_queue[idx]; + *tx_allowed_mask_p = tx_allowed_mask; + return 0; +} + +int wsm_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + struct wsm_tx *wsm = NULL; + struct ieee80211_tx_info *tx_info; + struct cw1200_queue *queue = NULL; + int queue_num; + u32 tx_allowed_mask = 0; + const struct cw1200_txpriv *txpriv = NULL; + int count = 0; + + /* More is used only for broadcasts. */ + bool more = false; + + if (priv->wsm_cmd.ptr) { /* CMD request */ + ++count; + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(!priv->wsm_cmd.ptr); + *data = priv->wsm_cmd.ptr; + *tx_len = priv->wsm_cmd.len; + *burst = 1; + spin_unlock(&priv->wsm_cmd.lock); + } else { + for (;;) { + int ret; + + if (atomic_add_return(0, &priv->tx_lock)) + break; + + spin_lock_bh(&priv->ps_state_lock); + + ret = wsm_get_tx_queue_and_mask(priv, &queue, + &tx_allowed_mask, &more); + queue_num = queue - priv->tx_queue; + + if (priv->buffered_multicasts && + (ret || !more) && + (priv->tx_multicast || !priv->sta_asleep_mask)) { + priv->buffered_multicasts = false; + if (priv->tx_multicast) { + priv->tx_multicast = false; + queue_work(priv->workqueue, + &priv->multicast_stop_work); + } + } + + spin_unlock_bh(&priv->ps_state_lock); + + if (ret) + break; + + if (cw1200_queue_get(queue, + tx_allowed_mask, + &wsm, &tx_info, &txpriv)) + continue; + + if (wsm_handle_tx_data(priv, wsm, + tx_info, txpriv, queue)) + continue; /* Handled by WSM */ + + wsm->hdr.id &= __cpu_to_le16( + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); + wsm->hdr.id |= cpu_to_le16( + WSM_TX_LINK_ID(txpriv->raw_link_id)); + priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); + + *data = (u8 *)wsm; + *tx_len = __le16_to_cpu(wsm->hdr.len); + + /* allow bursting if txop is set */ + if (priv->edca.params[queue_num].txop_limit) + *burst = min(*burst, + (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1); + else + *burst = 1; + + /* store index of bursting queue */ + if (*burst > 1) + priv->tx_burst_idx = queue_num; + else + priv->tx_burst_idx = -1; + + if (more) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + /* more buffered multicast/broadcast frames + * ==> set MoreData flag in IEEE 802.11 header + * to inform PS STAs + */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + + pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n", + 0x0004, *tx_len, *data, + wsm->more ? 'M' : ' '); + ++count; + break; + } + } + + return count; +} + +void wsm_txed(struct cw1200_common *priv, u8 *data) +{ + if (data == priv->wsm_cmd.ptr) { + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.ptr = NULL; + spin_unlock(&priv->wsm_cmd.lock); + } +} + +/* ******************************************************************** */ +/* WSM buffer */ + +void wsm_buf_init(struct wsm_buf *buf) +{ + BUG_ON(buf->begin); + buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); + buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin; + wsm_buf_reset(buf); +} + +void wsm_buf_deinit(struct wsm_buf *buf) +{ + kfree(buf->begin); + buf->begin = buf->data = buf->end = NULL; +} + +static void wsm_buf_reset(struct wsm_buf *buf) +{ + if (buf->begin) { + buf->data = &buf->begin[4]; + *(u32 *)buf->begin = 0; + } else { + buf->data = buf->begin; + } +} + +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) +{ + size_t pos = buf->data - buf->begin; + size_t size = pos + extra_size; + + size = round_up(size, FWLOAD_BLOCK_SIZE); + + buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA); + if (buf->begin) { + buf->data = &buf->begin[pos]; + buf->end = &buf->begin[size]; + return 0; + } else { + buf->end = buf->data = buf->begin; + return -ENOMEM; + } +} diff --git a/drivers/net/wireless/st/cw1200/wsm.h b/drivers/net/wireless/st/cw1200/wsm.h new file mode 100644 index 000000000000..48086e849515 --- /dev/null +++ b/drivers/net/wireless/st/cw1200/wsm.h @@ -0,0 +1,1870 @@ +/* + * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on CW1200 UMAC WSM API, which is + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stewart Mathers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_WSM_H_INCLUDED +#define CW1200_WSM_H_INCLUDED + +#include + +struct cw1200_common; + +/* Bands */ +/* Radio band 2.412 -2.484 GHz. */ +#define WSM_PHY_BAND_2_4G (0) + +/* Radio band 4.9375-5.8250 GHz. */ +#define WSM_PHY_BAND_5G (1) + +/* Transmit rates */ +/* 1 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_1 (0) + +/* 2 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_2 (1) + +/* 5.5 Mbps ERP-CCK */ +#define WSM_TRANSMIT_RATE_5 (2) + +/* 11 Mbps ERP-CCK */ +#define WSM_TRANSMIT_RATE_11 (3) + +/* 22 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_22 (4) */ + +/* 33 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_33 (5) */ + +/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_6 (6) + +/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_9 (7) + +/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_12 (8) + +/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_18 (9) + +/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_24 (10) + +/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_36 (11) + +/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_48 (12) + +/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_54 (13) + +/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_6 (14) + +/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_13 (15) + +/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_19 (16) + +/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_26 (17) + +/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_39 (18) + +/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */ +#define WSM_TRANSMIT_RATE_HT_52 (19) + +/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_58 (20) + +/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */ +#define WSM_TRANSMIT_RATE_HT_65 (21) + +/* Scan types */ +/* Foreground scan */ +#define WSM_SCAN_TYPE_FOREGROUND (0) + +/* Background scan */ +#define WSM_SCAN_TYPE_BACKGROUND (1) + +/* Auto scan */ +#define WSM_SCAN_TYPE_AUTO (2) + +/* Scan flags */ +/* Forced background scan means if the station cannot */ +/* enter the power-save mode, it shall force to perform a */ +/* background scan. Only valid when ScanType is */ +/* background scan. */ +#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0)) + +/* The WLAN device scans one channel at a time so */ +/* that disturbance to the data traffic is minimized. */ +#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1)) + +/* Preamble Type. Long if not set. */ +#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2)) + +/* 11n Tx Mode. Mixed if not set. */ +#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3)) + +/* Scan constraints */ +/* Maximum number of channels to be scanned. */ +#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48) + +/* The maximum number of SSIDs that the device can scan for. */ +#define WSM_SCAN_MAX_NUM_OF_SSIDS (2) + +/* Power management modes */ +/* 802.11 Active mode */ +#define WSM_PSM_ACTIVE (0) + +/* 802.11 PS mode */ +#define WSM_PSM_PS BIT(0) + +/* Fast Power Save bit */ +#define WSM_PSM_FAST_PS_FLAG BIT(7) + +/* Dynamic aka Fast power save */ +#define WSM_PSM_FAST_PS (BIT(0) | BIT(7)) + +/* Undetermined */ +/* Note : Undetermined status is reported when the */ +/* NULL data frame used to advertise the PM mode to */ +/* the AP at Pre or Post Background Scan is not Acknowledged */ +#define WSM_PSM_UNKNOWN BIT(1) + +/* Queue IDs */ +/* best effort/legacy */ +#define WSM_QUEUE_BEST_EFFORT (0) + +/* background */ +#define WSM_QUEUE_BACKGROUND (1) + +/* video */ +#define WSM_QUEUE_VIDEO (2) + +/* voice */ +#define WSM_QUEUE_VOICE (3) + +/* HT TX parameters */ +/* Non-HT */ +#define WSM_HT_TX_NON_HT (0) + +/* Mixed format */ +#define WSM_HT_TX_MIXED (1) + +/* Greenfield format */ +#define WSM_HT_TX_GREENFIELD (2) + +/* STBC allowed */ +#define WSM_HT_TX_STBC (BIT(7)) + +/* EPTA prioirty flags for BT Coex */ +/* default epta priority */ +#define WSM_EPTA_PRIORITY_DEFAULT 4 +/* use for normal data */ +#define WSM_EPTA_PRIORITY_DATA 4 +/* use for connect/disconnect/roaming*/ +#define WSM_EPTA_PRIORITY_MGT 5 +/* use for action frames */ +#define WSM_EPTA_PRIORITY_ACTION 5 +/* use for AC_VI data */ +#define WSM_EPTA_PRIORITY_VIDEO 5 +/* use for AC_VO data */ +#define WSM_EPTA_PRIORITY_VOICE 6 +/* use for EAPOL exchange */ +#define WSM_EPTA_PRIORITY_EAPOL 7 + +/* TX status */ +/* Frame was sent aggregated */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_AGGREGATION (BIT(0)) + +/* Host should requeue this frame later. */ +/* Valid only when status is WSM_REQUEUE. */ +#define WSM_TX_STATUS_REQUEUE (BIT(1)) + +/* Normal Ack */ +#define WSM_TX_STATUS_NORMAL_ACK (0<<2) + +/* No Ack */ +#define WSM_TX_STATUS_NO_ACK (1<<2) + +/* No explicit acknowledgement */ +#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2) + +/* Block Ack */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_BLOCK_ACK (3<<2) + +/* RX status */ +/* Unencrypted */ +#define WSM_RX_STATUS_UNENCRYPTED (0<<0) + +/* WEP */ +#define WSM_RX_STATUS_WEP (1<<0) + +/* TKIP */ +#define WSM_RX_STATUS_TKIP (2<<0) + +/* AES */ +#define WSM_RX_STATUS_AES (3<<0) + +/* WAPI */ +#define WSM_RX_STATUS_WAPI (4<<0) + +/* Macro to fetch encryption subfield. */ +#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07) + +/* Frame was part of an aggregation */ +#define WSM_RX_STATUS_AGGREGATE (BIT(3)) + +/* Frame was first in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4)) + +/* Frame was last in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5)) + +/* Indicates a defragmented frame */ +#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6)) + +/* Indicates a Beacon frame */ +#define WSM_RX_STATUS_BEACON (BIT(7)) + +/* Indicates STA bit beacon TIM field */ +#define WSM_RX_STATUS_TIM (BIT(8)) + +/* Indicates Beacon frame's virtual bitmap contains multicast bit */ +#define WSM_RX_STATUS_MULTICAST (BIT(9)) + +/* Indicates frame contains a matching SSID */ +#define WSM_RX_STATUS_MATCHING_SSID (BIT(10)) + +/* Indicates frame contains a matching BSSI */ +#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11)) + +/* Indicates More bit set in Framectl field */ +#define WSM_RX_STATUS_MORE_DATA (BIT(12)) + +/* Indicates frame received during a measurement process */ +#define WSM_RX_STATUS_MEASUREMENT (BIT(13)) + +/* Indicates frame received as an HT packet */ +#define WSM_RX_STATUS_HT (BIT(14)) + +/* Indicates frame received with STBC */ +#define WSM_RX_STATUS_STBC (BIT(15)) + +/* Indicates Address 1 field matches dot11StationId */ +#define WSM_RX_STATUS_ADDRESS1 (BIT(16)) + +/* Indicates Group address present in the Address 1 field */ +#define WSM_RX_STATUS_GROUP (BIT(17)) + +/* Indicates Broadcast address present in the Address 1 field */ +#define WSM_RX_STATUS_BROADCAST (BIT(18)) + +/* Indicates group key used with encrypted frames */ +#define WSM_RX_STATUS_GROUP_KEY (BIT(19)) + +/* Macro to fetch encryption key index. */ +#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F) + +/* Indicates TSF inclusion after 802.11 frame body */ +#define WSM_RX_STATUS_TSF_INCLUDED (BIT(24)) + +/* Frame Control field starts at Frame offset + 2 */ +#define WSM_TX_2BYTES_SHIFT (BIT(7)) + +/* Join mode */ +/* IBSS */ +#define WSM_JOIN_MODE_IBSS (0) + +/* BSS */ +#define WSM_JOIN_MODE_BSS (1) + +/* PLCP preamble type */ +/* For long preamble */ +#define WSM_JOIN_PREAMBLE_LONG (0) + +/* For short preamble (Long for 1Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT (1) + +/* For short preamble (Long for 1 and 2Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT_2 (2) + +/* Join flags */ +/* Unsynchronized */ +#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0) +/* The BSS owner is a P2P GO */ +#define WSM_JOIN_FLAGS_P2P_GO BIT(1) +/* Force to join BSS with the BSSID and the + * SSID specified without waiting for beacons. The + * ProbeForJoin parameter is ignored. + */ +#define WSM_JOIN_FLAGS_FORCE BIT(2) +/* Give probe request/response higher + * priority over the BT traffic + */ +#define WSM_JOIN_FLAGS_PRIO BIT(3) +/* Issue immediate join confirmation and use + * join complete to notify about completion + */ +#define WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND BIT(5) + +/* Key types */ +#define WSM_KEY_TYPE_WEP_DEFAULT (0) +#define WSM_KEY_TYPE_WEP_PAIRWISE (1) +#define WSM_KEY_TYPE_TKIP_GROUP (2) +#define WSM_KEY_TYPE_TKIP_PAIRWISE (3) +#define WSM_KEY_TYPE_AES_GROUP (4) +#define WSM_KEY_TYPE_AES_PAIRWISE (5) +#define WSM_KEY_TYPE_WAPI_GROUP (6) +#define WSM_KEY_TYPE_WAPI_PAIRWISE (7) + +/* Key indexes */ +#define WSM_KEY_MAX_INDEX (10) + +/* ACK policy */ +#define WSM_ACK_POLICY_NORMAL (0) +#define WSM_ACK_POLICY_NO_ACK (1) + +/* Start modes */ +#define WSM_START_MODE_AP (0) /* Mini AP */ +#define WSM_START_MODE_P2P_GO (1) /* P2P GO */ +#define WSM_START_MODE_P2P_DEV (2) /* P2P device */ + +/* SetAssociationMode MIB flags */ +#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0)) +#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1)) +#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2)) +#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3)) +#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4)) + +/* RcpiRssiThreshold MIB flags */ +#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0)) +#define WSM_RCPI_RSSI_USE_RSSI (BIT(1)) +#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2)) +#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3)) + +/* Update-ie constants */ +#define WSM_UPDATE_IE_BEACON (BIT(0)) +#define WSM_UPDATE_IE_PROBE_RESP (BIT(1)) +#define WSM_UPDATE_IE_PROBE_REQ (BIT(2)) + +/* WSM events */ +/* Error */ +#define WSM_EVENT_ERROR (0) + +/* BSS lost */ +#define WSM_EVENT_BSS_LOST (1) + +/* BSS regained */ +#define WSM_EVENT_BSS_REGAINED (2) + +/* Radar detected */ +#define WSM_EVENT_RADAR_DETECTED (3) + +/* RCPI or RSSI threshold triggered */ +#define WSM_EVENT_RCPI_RSSI (4) + +/* BT inactive */ +#define WSM_EVENT_BT_INACTIVE (5) + +/* BT active */ +#define WSM_EVENT_BT_ACTIVE (6) + +/* MIB IDs */ +/* 4.1 dot11StationId */ +#define WSM_MIB_ID_DOT11_STATION_ID 0x0000 + +/* 4.2 dot11MaxtransmitMsduLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001 + +/* 4.3 dot11MaxReceiveLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002 + +/* 4.4 dot11SlotTime */ +#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003 + +/* 4.5 dot11GroupAddressesTable */ +#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004 +#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8 + +/* 4.6 dot11WepDefaultKeyId */ +#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005 + +/* 4.7 dot11CurrentTxPowerLevel */ +#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006 + +/* 4.8 dot11RTSThreshold */ +#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007 + +/* 4.9 NonErpProtection */ +#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000 + +/* 4.10 ArpIpAddressesTable */ +#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001 +#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1 + +/* 4.11 TemplateFrame */ +#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002 + +/* 4.12 RxFilter */ +#define WSM_MIB_ID_RX_FILTER 0x1003 + +/* 4.13 BeaconFilterTable */ +#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004 + +/* 4.14 BeaconFilterEnable */ +#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005 + +/* 4.15 OperationalPowerMode */ +#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006 + +/* 4.16 BeaconWakeUpPeriod */ +#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007 + +/* 4.17 RcpiRssiThreshold */ +#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009 + +/* 4.18 StatisticsTable */ +#define WSM_MIB_ID_STATISTICS_TABLE 0x100A + +/* 4.19 IbssPsConfig */ +#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B + +/* 4.20 CountersTable */ +#define WSM_MIB_ID_COUNTERS_TABLE 0x100C + +/* 4.21 BlockAckPolicy */ +#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E + +/* 4.22 OverrideInternalTxRate */ +#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F + +/* 4.23 SetAssociationMode */ +#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010 + +/* 4.24 UpdateEptaConfigData */ +#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011 + +/* 4.25 SelectCcaMethod */ +#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012 + +/* 4.26 SetUpasdInformation */ +#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013 + +/* 4.27 SetAutoCalibrationMode WBF00004073 */ +#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015 + +/* 4.28 SetTxRateRetryPolicy */ +#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016 + +/* 4.29 SetHostMessageTypeFilter */ +#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017 + +/* 4.30 P2PFindInfo */ +#define WSM_MIB_ID_P2P_FIND_INFO 0x1018 + +/* 4.31 P2PPsModeInfo */ +#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019 + +/* 4.32 SetEtherTypeDataFrameFilter */ +#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A + +/* 4.33 SetUDPPortDataFrameFilter */ +#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B + +/* 4.34 SetMagicDataFrameFilter */ +#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C + +/* 4.35 P2PDeviceInfo */ +#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D + +/* 4.36 SetWCDMABand */ +#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E + +/* 4.37 GroupTxSequenceCounter */ +#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F + +/* 4.38 ProtectedMgmtPolicy */ +#define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020 + +/* 4.39 SetHtProtection */ +#define WSM_MIB_ID_SET_HT_PROTECTION 0x1021 + +/* 4.40 GPIO Command */ +#define WSM_MIB_ID_GPIO_COMMAND 0x1022 + +/* 4.41 TSF Counter Value */ +#define WSM_MIB_ID_TSF_COUNTER 0x1023 + +/* Test Purposes Only */ +#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D + +/* 4.42 UseMultiTxConfMessage */ +#define WSM_MIB_USE_MULTI_TX_CONF 0x1024 + +/* 4.43 Keep-alive period */ +#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025 + +/* 4.44 Disable BSSID filter */ +#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026 + +/* Frame template types */ +#define WSM_FRAME_TYPE_PROBE_REQUEST (0) +#define WSM_FRAME_TYPE_BEACON (1) +#define WSM_FRAME_TYPE_NULL (2) +#define WSM_FRAME_TYPE_QOS_NULL (3) +#define WSM_FRAME_TYPE_PS_POLL (4) +#define WSM_FRAME_TYPE_PROBE_RESPONSE (5) + +#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */ + +/* Status */ +/* The WSM firmware has completed a request */ +/* successfully. */ +#define WSM_STATUS_SUCCESS (0) + +/* This is a generic failure code if other error codes do */ +/* not apply. */ +#define WSM_STATUS_FAILURE (1) + +/* A request contains one or more invalid parameters. */ +#define WSM_INVALID_PARAMETER (2) + +/* The request cannot perform because the device is in */ +/* an inappropriate mode. */ +#define WSM_ACCESS_DENIED (3) + +/* The frame received includes a decryption error. */ +#define WSM_STATUS_DECRYPTFAILURE (4) + +/* A MIC failure is detected in the received packets. */ +#define WSM_STATUS_MICFAILURE (5) + +/* The transmit request failed due to retry limit being */ +/* exceeded. */ +#define WSM_STATUS_RETRY_EXCEEDED (6) + +/* The transmit request failed due to MSDU life time */ +/* being exceeded. */ +#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7) + +/* The link to the AP is lost. */ +#define WSM_STATUS_LINK_LOST (8) + +/* No key was found for the encrypted frame */ +#define WSM_STATUS_NO_KEY_FOUND (9) + +/* Jammer was detected when transmitting this frame */ +#define WSM_STATUS_JAMMER_DETECTED (10) + +/* The message should be requeued later. */ +/* This is applicable only to Transmit */ +#define WSM_REQUEUE (11) + +/* Advanced filtering options */ +#define WSM_MAX_FILTER_ELEMENTS (4) + +#define WSM_FILTER_ACTION_IGNORE (0) +#define WSM_FILTER_ACTION_FILTER_IN (1) +#define WSM_FILTER_ACTION_FILTER_OUT (2) + +#define WSM_FILTER_PORT_TYPE_DST (0) +#define WSM_FILTER_PORT_TYPE_SRC (1) + +/* Actual header of WSM messages */ +struct wsm_hdr { + __le16 len; + __le16 id; +}; + +#define WSM_TX_SEQ_MAX (7) +#define WSM_TX_SEQ(seq) \ + ((seq & WSM_TX_SEQ_MAX) << 13) +#define WSM_TX_LINK_ID_MAX (0x0F) +#define WSM_TX_LINK_ID(link_id) \ + ((link_id & WSM_TX_LINK_ID_MAX) << 6) + +#define MAX_BEACON_SKIP_TIME_MS 1000 + +#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2) + +/* ******************************************************************** */ +/* WSM capability */ + +#define WSM_STARTUP_IND_ID 0x0801 + +struct wsm_startup_ind { + u16 input_buffers; + u16 input_buffer_size; + u16 status; + u16 hw_id; + u16 hw_subid; + u16 fw_cap; + u16 fw_type; + u16 fw_api; + u16 fw_build; + u16 fw_ver; + char fw_label[128]; + u32 config[4]; +}; + +/* ******************************************************************** */ +/* WSM commands */ + +/* 3.1 */ +#define WSM_CONFIGURATION_REQ_ID 0x0009 +#define WSM_CONFIGURATION_RESP_ID 0x0409 + +struct wsm_tx_power_range { + int min_power_level; + int max_power_level; + u32 stepping; +}; + +struct wsm_configuration { + /* [in] */ u32 dot11MaxTransmitMsduLifeTime; + /* [in] */ u32 dot11MaxReceiveLifeTime; + /* [in] */ u32 dot11RtsThreshold; + /* [in, out] */ u8 *dot11StationId; + /* [in] */ const void *dpdData; + /* [in] */ size_t dpdData_size; + /* [out] */ u8 dot11FrequencyBandsSupported; + /* [out] */ u32 supportedRateMask; + /* [out] */ struct wsm_tx_power_range txPowerRange[2]; +}; + +int wsm_configuration(struct cw1200_common *priv, + struct wsm_configuration *arg); + +/* 3.3 */ +#define WSM_RESET_REQ_ID 0x000A +#define WSM_RESET_RESP_ID 0x040A +struct wsm_reset { + /* [in] */ int link_id; + /* [in] */ bool reset_statistics; +}; + +int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg); + +/* 3.5 */ +#define WSM_READ_MIB_REQ_ID 0x0005 +#define WSM_READ_MIB_RESP_ID 0x0405 +int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *buf, + size_t buf_size); + +/* 3.7 */ +#define WSM_WRITE_MIB_REQ_ID 0x0006 +#define WSM_WRITE_MIB_RESP_ID 0x0406 +int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *buf, + size_t buf_size); + +/* 3.9 */ +#define WSM_START_SCAN_REQ_ID 0x0007 +#define WSM_START_SCAN_RESP_ID 0x0407 + +struct wsm_ssid { + u8 ssid[32]; + u32 length; +}; + +struct wsm_scan_ch { + u16 number; + u32 min_chan_time; + u32 max_chan_time; + u32 tx_power_level; +}; + +struct wsm_scan { + /* WSM_PHY_BAND_... */ + u8 band; + + /* WSM_SCAN_TYPE_... */ + u8 type; + + /* WSM_SCAN_FLAG_... */ + u8 flags; + + /* WSM_TRANSMIT_RATE_... */ + u8 max_tx_rate; + + /* Interval period in TUs that the device shall the re- */ + /* execute the requested scan. Max value supported by the device */ + /* is 256s. */ + u32 auto_scan_interval; + + /* Number of probe requests (per SSID) sent to one (1) */ + /* channel. Zero (0) means that none is send, which */ + /* means that a passive scan is to be done. Value */ + /* greater than zero (0) means that an active scan is to */ + /* be done. */ + u32 num_probes; + + /* Number of channels to be scanned. */ + /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */ + u8 num_channels; + + /* Number of SSID provided in the scan command (this */ + /* is zero (0) in broadcast scan) */ + /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */ + u8 num_ssids; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + u8 probe_delay; + + /* SSIDs to be scanned [numOfSSIDs]; */ + struct wsm_ssid *ssids; + + /* Channels to be scanned [numOfChannels]; */ + struct wsm_scan_ch *ch; +}; + +int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg); + +/* 3.11 */ +#define WSM_STOP_SCAN_REQ_ID 0x0008 +#define WSM_STOP_SCAN_RESP_ID 0x0408 +int wsm_stop_scan(struct cw1200_common *priv); + +/* 3.13 */ +#define WSM_SCAN_COMPLETE_IND_ID 0x0806 +struct wsm_scan_complete { + /* WSM_STATUS_... */ + u32 status; + + /* WSM_PSM_... */ + u8 psm; + + /* Number of channels that the scan operation completed. */ + u8 num_channels; +}; + +/* 3.14 */ +#define WSM_TX_CONFIRM_IND_ID 0x0404 +#define WSM_MULTI_TX_CONFIRM_ID 0x041E + +struct wsm_tx_confirm { + /* Packet identifier used in wsm_tx. */ + u32 packet_id; + + /* WSM_STATUS_... */ + u32 status; + + /* WSM_TRANSMIT_RATE_... */ + u8 tx_rate; + + /* The number of times the frame was transmitted */ + /* without receiving an acknowledgement. */ + u8 ack_failures; + + /* WSM_TX_STATUS_... */ + u16 flags; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission as completed. */ + u32 media_delay; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission was started. */ + u32 tx_queue_delay; +}; + +/* 3.15 */ +typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv, + struct wsm_tx_confirm *arg); + +/* Note that ideology of wsm_tx struct is different against the rest of + * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input + * argument for WSM call, but a prepared bytestream to be sent to firmware. + * It is filled partly in cw1200_tx, partly in low-level WSM code. + * Please pay attention once again: ideology is different. + * + * Legend: + * - [in]: cw1200_tx must fill this field. + * - [wsm]: the field is filled by low-level WSM. + */ +struct wsm_tx { + /* common WSM header */ + struct wsm_hdr hdr; + + /* Packet identifier that meant to be used in completion. */ + u32 packet_id; /* Note this is actually a cookie */ + + /* WSM_TRANSMIT_RATE_... */ + u8 max_tx_rate; + + /* WSM_QUEUE_... */ + u8 queue_id; + + /* True: another packet is pending on the host for transmission. */ + u8 more; + + /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */ + /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */ + /* Bits 3:1 - PTA Priority */ + /* Bits 6:4 - Tx Rate Retry Policy */ + /* Bit 7 - Reserved */ + u8 flags; + + /* Should be 0. */ + u32 reserved; + + /* The elapsed time in TUs, after the initial transmission */ + /* of an MSDU, after which further attempts to transmit */ + /* the MSDU shall be terminated. Overrides the global */ + /* dot11MaxTransmitMsduLifeTime setting [optional] */ + /* Device will set the default value if this is 0. */ + __le32 expire_time; + + /* WSM_HT_TX_... */ + __le32 ht_tx_parameters; +} __packed; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */ +#define WSM_TX_EXTRA_HEADROOM (28) + +/* 3.16 */ +#define WSM_RECEIVE_IND_ID 0x0804 + +struct wsm_rx { + /* WSM_STATUS_... */ + u32 status; + + /* Specifies the channel of the received packet. */ + u16 channel_number; + + /* WSM_TRANSMIT_RATE_... */ + u8 rx_rate; + + /* This value is expressed in signed Q8.0 format for */ + /* RSSI and unsigned Q7.1 format for RCPI. */ + u8 rcpi_rssi; + + /* WSM_RX_STATUS_... */ + u32 flags; +}; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */ +#define WSM_RX_EXTRA_HEADROOM (16) + +typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg, + struct sk_buff **skb_p); + +/* 3.17 */ +struct wsm_event { + /* WSM_STATUS_... */ + /* [out] */ u32 id; + + /* Indication parameters. */ + /* For error indication, this shall be a 32-bit WSM status. */ + /* For RCPI or RSSI indication, this should be an 8-bit */ + /* RCPI or RSSI value. */ + /* [out] */ u32 data; +}; + +struct cw1200_wsm_event { + struct list_head link; + struct wsm_event evt; +}; + +/* 3.18 - 3.22 */ +/* Measurement. Skipped for now. Irrelevent. */ + +typedef void (*wsm_event_cb) (struct cw1200_common *priv, + struct wsm_event *arg); + +/* 3.23 */ +#define WSM_JOIN_REQ_ID 0x000B +#define WSM_JOIN_RESP_ID 0x040B + +struct wsm_join { + /* WSM_JOIN_MODE_... */ + u8 mode; + + /* WSM_PHY_BAND_... */ + u8 band; + + /* Specifies the channel number to join. The channel */ + /* number will be mapped to an actual frequency */ + /* according to the band */ + u16 channel_number; + + /* Specifies the BSSID of the BSS or IBSS to be joined */ + /* or the IBSS to be started. */ + u8 bssid[6]; + + /* ATIM window of IBSS */ + /* When ATIM window is zero the initiated IBSS does */ + /* not support power saving. */ + u16 atim_window; + + /* WSM_JOIN_PREAMBLE_... */ + u8 preamble_type; + + /* Specifies if a probe request should be send with the */ + /* specified SSID when joining to the network. */ + u8 probe_for_join; + + /* DTIM Period (In multiples of beacon interval) */ + u8 dtim_period; + + /* WSM_JOIN_FLAGS_... */ + u8 flags; + + /* Length of the SSID */ + u32 ssid_len; + + /* Specifies the SSID of the IBSS to join or start */ + u8 ssid[32]; + + /* Specifies the time between TBTTs in TUs */ + u32 beacon_interval; + + /* A bit mask that defines the BSS basic rate set. */ + u32 basic_rate_set; +}; + +struct wsm_join_cnf { + u32 status; + + /* Minimum transmission power level in units of 0.1dBm */ + u32 min_power_level; + + /* Maximum transmission power level in units of 0.1dBm */ + u32 max_power_level; +}; + +int wsm_join(struct cw1200_common *priv, struct wsm_join *arg); + +/* 3.24 */ +struct wsm_join_complete { + /* WSM_STATUS_... */ + u32 status; +}; + +/* 3.25 */ +#define WSM_SET_PM_REQ_ID 0x0010 +#define WSM_SET_PM_RESP_ID 0x0410 +struct wsm_set_pm { + /* WSM_PSM_... */ + u8 mode; + + /* in unit of 500us; 0 to use default */ + u8 fast_psm_idle_period; + + /* in unit of 500us; 0 to use default */ + u8 ap_psm_change_period; + + /* in unit of 500us; 0 to disable auto-pspoll */ + u8 min_auto_pspoll_period; +}; + +int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); + +/* 3.27 */ +struct wsm_set_pm_complete { + u8 psm; /* WSM_PSM_... */ +}; + +/* 3.28 */ +#define WSM_SET_BSS_PARAMS_REQ_ID 0x0011 +#define WSM_SET_BSS_PARAMS_RESP_ID 0x0411 +struct wsm_set_bss_params { + /* This resets the beacon loss counters only */ + u8 reset_beacon_loss; + + /* The number of lost consecutive beacons after which */ + /* the WLAN device should indicate the BSS-Lost event */ + /* to the WLAN host driver. */ + u8 beacon_lost_count; + + /* The AID received during the association process. */ + u16 aid; + + /* The operational rate set mask */ + u32 operational_rate_set; +}; + +int wsm_set_bss_params(struct cw1200_common *priv, + const struct wsm_set_bss_params *arg); + +/* 3.30 */ +#define WSM_ADD_KEY_REQ_ID 0x000C +#define WSM_ADD_KEY_RESP_ID 0x040C +struct wsm_add_key { + u8 type; /* WSM_KEY_TYPE_... */ + u8 index; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */ + u16 reserved; + union { + struct { + u8 peer[6]; /* MAC address of the peer station */ + u8 reserved; + u8 keylen; /* Key length in bytes */ + u8 keydata[16]; /* Key data */ + } __packed wep_pairwise; + struct { + u8 keyid; /* Unique per key identifier (0..3) */ + u8 keylen; /* Key length in bytes */ + u16 reserved; + u8 keydata[16]; /* Key data */ + } __packed wep_group; + struct { + u8 peer[6]; /* MAC address of the peer station */ + u16 reserved; + u8 keydata[16]; /* TKIP key data */ + u8 rx_mic_key[8]; /* Rx MIC key */ + u8 tx_mic_key[8]; /* Tx MIC key */ + } __packed tkip_pairwise; + struct { + u8 keydata[16]; /* TKIP key data */ + u8 rx_mic_key[8]; /* Rx MIC key */ + u8 keyid; /* Key ID */ + u8 reserved[3]; + u8 rx_seqnum[8]; /* Receive Sequence Counter */ + } __packed tkip_group; + struct { + u8 peer[6]; /* MAC address of the peer station */ + u16 reserved; + u8 keydata[16]; /* AES key data */ + } __packed aes_pairwise; + struct { + u8 keydata[16]; /* AES key data */ + u8 keyid; /* Key ID */ + u8 reserved[3]; + u8 rx_seqnum[8]; /* Receive Sequence Counter */ + } __packed aes_group; + struct { + u8 peer[6]; /* MAC address of the peer station */ + u8 keyid; /* Key ID */ + u8 reserved; + u8 keydata[16]; /* WAPI key data */ + u8 mic_key[16]; /* MIC key data */ + } __packed wapi_pairwise; + struct { + u8 keydata[16]; /* WAPI key data */ + u8 mic_key[16]; /* MIC key data */ + u8 keyid; /* Key ID */ + u8 reserved[3]; + } __packed wapi_group; + } __packed; +} __packed; + +int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg); + +/* 3.32 */ +#define WSM_REMOVE_KEY_REQ_ID 0x000D +#define WSM_REMOVE_KEY_RESP_ID 0x040D +struct wsm_remove_key { + u8 index; /* Key entry index : 0-10 */ +}; + +int wsm_remove_key(struct cw1200_common *priv, + const struct wsm_remove_key *arg); + +/* 3.34 */ +struct wsm_set_tx_queue_params { + /* WSM_ACK_POLICY_... */ + u8 ackPolicy; + + /* Medium Time of TSPEC (in 32us units) allowed per */ + /* One Second Averaging Period for this queue. */ + u16 allowedMediumTime; + + /* dot11MaxTransmitMsduLifetime to be used for the */ + /* specified queue. */ + u32 maxTransmitLifetime; +}; + +struct wsm_tx_queue_params { + /* NOTE: index is a linux queue id. */ + struct wsm_set_tx_queue_params params[4]; +}; + + +#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,\ + max_life_time) \ +do { \ + struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \ + p->ackPolicy = (ack_policy); \ + p->allowedMediumTime = (allowed_time); \ + p->maxTransmitLifetime = (max_life_time); \ +} while (0) + +int wsm_set_tx_queue_params(struct cw1200_common *priv, + const struct wsm_set_tx_queue_params *arg, u8 id); + +/* 3.36 */ +#define WSM_EDCA_PARAMS_REQ_ID 0x0013 +#define WSM_EDCA_PARAMS_RESP_ID 0x0413 +struct wsm_edca_queue_params { + /* CWmin (in slots) for the access class. */ + u16 cwmin; + + /* CWmax (in slots) for the access class. */ + u16 cwmax; + + /* AIFS (in slots) for the access class. */ + u16 aifns; + + /* TX OP Limit (in microseconds) for the access class. */ + u16 txop_limit; + + /* dot11MaxReceiveLifetime to be used for the specified */ + /* the access class. Overrides the global */ + /* dot11MaxReceiveLifetime value */ + u32 max_rx_lifetime; +}; + +struct wsm_edca_params { + /* NOTE: index is a linux queue id. */ + struct wsm_edca_queue_params params[4]; + bool uapsd_enable[4]; +}; + +#define TXOP_UNIT 32 +#define WSM_EDCA_SET(__edca, __queue, __aifs, __cw_min, __cw_max, __txop, __lifetime,\ + __uapsd) \ + do { \ + struct wsm_edca_queue_params *p = &(__edca)->params[__queue]; \ + p->cwmin = __cw_min; \ + p->cwmax = __cw_max; \ + p->aifns = __aifs; \ + p->txop_limit = ((__txop) * TXOP_UNIT); \ + p->max_rx_lifetime = __lifetime; \ + (__edca)->uapsd_enable[__queue] = (__uapsd); \ + } while (0) + +int wsm_set_edca_params(struct cw1200_common *priv, + const struct wsm_edca_params *arg); + +int wsm_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg); + +/* 3.38 */ +/* Set-System info. Skipped for now. Irrelevent. */ + +/* 3.40 */ +#define WSM_SWITCH_CHANNEL_REQ_ID 0x0016 +#define WSM_SWITCH_CHANNEL_RESP_ID 0x0416 + +struct wsm_switch_channel { + /* 1 - means the STA shall not transmit any further */ + /* frames until the channel switch has completed */ + u8 mode; + + /* Number of TBTTs until channel switch occurs. */ + /* 0 - indicates switch shall occur at any time */ + /* 1 - occurs immediately before the next TBTT */ + u8 switch_count; + + /* The new channel number to switch to. */ + /* Note this is defined as per section 2.7. */ + u16 channel_number; +}; + +int wsm_switch_channel(struct cw1200_common *priv, + const struct wsm_switch_channel *arg); + +typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv); + +#define WSM_START_REQ_ID 0x0017 +#define WSM_START_RESP_ID 0x0417 + +struct wsm_start { + /* WSM_START_MODE_... */ + /* [in] */ u8 mode; + + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* Channel number */ + /* [in] */ u16 channel_number; + + /* Client Traffic window in units of TU */ + /* Valid only when mode == ..._P2P */ + /* [in] */ u32 ct_window; + + /* Interval between two consecutive */ + /* beacon transmissions in TU. */ + /* [in] */ u32 beacon_interval; + + /* DTIM period in terms of beacon intervals */ + /* [in] */ u8 dtim_period; + + /* WSM_JOIN_PREAMBLE_... */ + /* [in] */ u8 preamble; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + /* [in] */ u8 probe_delay; + + /* Length of the SSID */ + /* [in] */ u8 ssid_len; + + /* SSID of the BSS or P2P_GO to be started now. */ + /* [in] */ u8 ssid[32]; + + /* The basic supported rates for the MiniAP. */ + /* [in] */ u32 basic_rate_set; +}; + +int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg); + +#define WSM_BEACON_TRANSMIT_REQ_ID 0x0018 +#define WSM_BEACON_TRANSMIT_RESP_ID 0x0418 + +struct wsm_beacon_transmit { + /* 1: enable; 0: disable */ + /* [in] */ u8 enable_beaconing; +}; + +int wsm_beacon_transmit(struct cw1200_common *priv, + const struct wsm_beacon_transmit *arg); + +int wsm_start_find(struct cw1200_common *priv); + +int wsm_stop_find(struct cw1200_common *priv); + +typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status); + +struct wsm_suspend_resume { + /* See 3.52 */ + /* Link ID */ + /* [out] */ int link_id; + /* Stop sending further Tx requests down to device for this link */ + /* [out] */ bool stop; + /* Transmit multicast Frames */ + /* [out] */ bool multicast; + /* The AC on which Tx to be suspended /resumed. */ + /* This is applicable only for U-APSD */ + /* WSM_QUEUE_... */ + /* [out] */ int queue; +}; + +typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv, + struct wsm_suspend_resume *arg); + +/* 3.54 Update-IE request. */ +struct wsm_update_ie { + /* WSM_UPDATE_IE_... */ + /* [in] */ u16 what; + /* [in] */ u16 count; + /* [in] */ u8 *ies; + /* [in] */ size_t length; +}; + +int wsm_update_ie(struct cw1200_common *priv, + const struct wsm_update_ie *arg); + +/* 3.56 */ +struct wsm_map_link { + /* MAC address of the remote device */ + /* [in] */ u8 mac_addr[6]; + /* [in] */ u8 link_id; +}; + +int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg); + +/* ******************************************************************** */ +/* MIB shortcats */ + +static inline int wsm_set_output_power(struct cw1200_common *priv, + int power_level) +{ + __le32 val = __cpu_to_le32(power_level); + return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL, + &val, sizeof(val)); +} + +static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv, + unsigned dtim_interval, + unsigned listen_interval) +{ + struct { + u8 numBeaconPeriods; + u8 reserved; + __le16 listenInterval; + } val = { + dtim_interval, 0, __cpu_to_le16(listen_interval) + }; + + if (dtim_interval > 0xFF || listen_interval > 0xFFFF) + return -EINVAL; + else + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD, + &val, sizeof(val)); +} + +struct wsm_rcpi_rssi_threshold { + u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */ + u8 lowerThreshold; + u8 upperThreshold; + u8 rollingAverageCount; +}; + +static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv, + struct wsm_rcpi_rssi_threshold *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg, + sizeof(*arg)); +} + +struct wsm_mib_counters_table { + __le32 plcp_errors; + __le32 fcs_errors; + __le32 tx_packets; + __le32 rx_packets; + __le32 rx_packet_errors; + __le32 rx_decryption_failures; + __le32 rx_mic_failures; + __le32 rx_no_key_failures; + __le32 tx_multicast_frames; + __le32 tx_frames_success; + __le32 tx_frame_failures; + __le32 tx_frames_retried; + __le32 tx_frames_multi_retried; + __le32 rx_frame_duplicates; + __le32 rts_success; + __le32 rts_failures; + __le32 ack_failures; + __le32 rx_multicast_frames; + __le32 rx_frames_success; + __le32 rx_cmac_icv_errors; + __le32 rx_cmac_replays; + __le32 rx_mgmt_ccmp_replays; +} __packed; + +static inline int wsm_get_counters_table(struct cw1200_common *priv, + struct wsm_mib_counters_table *arg) +{ + return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE, + arg, sizeof(*arg)); +} + +static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac) +{ + return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN); +} + +struct wsm_rx_filter { + bool promiscuous; + bool bssid; + bool fcs; + bool probeResponder; +}; + +static inline int wsm_set_rx_filter(struct cw1200_common *priv, + const struct wsm_rx_filter *arg) +{ + __le32 val = 0; + if (arg->promiscuous) + val |= __cpu_to_le32(BIT(0)); + if (arg->bssid) + val |= __cpu_to_le32(BIT(1)); + if (arg->fcs) + val |= __cpu_to_le32(BIT(2)); + if (arg->probeResponder) + val |= __cpu_to_le32(BIT(3)); + return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val)); +} + +int wsm_set_probe_responder(struct cw1200_common *priv, bool enable); + +#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0) +#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1) +#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2) + +struct wsm_beacon_filter_table_entry { + u8 ie_id; + u8 flags; + u8 oui[3]; + u8 match_data[3]; +} __packed; + +struct wsm_mib_beacon_filter_table { + __le32 num; + struct wsm_beacon_filter_table_entry entry[10]; +} __packed; + +static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv, + struct wsm_mib_beacon_filter_table *ft) +{ + size_t size = __le32_to_cpu(ft->num) * + sizeof(struct wsm_beacon_filter_table_entry) + + sizeof(__le32); + + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size); +} + +#define WSM_BEACON_FILTER_ENABLE BIT(0) /* Enable/disable beacon filtering */ +#define WSM_BEACON_FILTER_AUTO_ERP BIT(1) /* If 1 FW will handle ERP IE changes internally */ + +struct wsm_beacon_filter_control { + int enabled; + int bcn_count; +}; + +static inline int wsm_beacon_filter_control(struct cw1200_common *priv, + struct wsm_beacon_filter_control *arg) +{ + struct { + __le32 enabled; + __le32 bcn_count; + } val; + val.enabled = __cpu_to_le32(arg->enabled); + val.bcn_count = __cpu_to_le32(arg->bcn_count); + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val, + sizeof(val)); +} + +enum wsm_power_mode { + wsm_power_mode_active = 0, + wsm_power_mode_doze = 1, + wsm_power_mode_quiescent = 2, +}; + +struct wsm_operational_mode { + enum wsm_power_mode power_mode; + int disable_more_flag_usage; + int perform_ant_diversity; +}; + +static inline int wsm_set_operational_mode(struct cw1200_common *priv, + const struct wsm_operational_mode *arg) +{ + u8 val = arg->power_mode; + if (arg->disable_more_flag_usage) + val |= BIT(4); + if (arg->perform_ant_diversity) + val |= BIT(5); + return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val, + sizeof(val)); +} + +struct wsm_template_frame { + u8 frame_type; + u8 rate; + struct sk_buff *skb; +}; + +static inline int wsm_set_template_frame(struct cw1200_common *priv, + struct wsm_template_frame *arg) +{ + int ret; + u8 *p = skb_push(arg->skb, 4); + p[0] = arg->frame_type; + p[1] = arg->rate; + ((__le16 *)p)[1] = __cpu_to_le16(arg->skb->len - 4); + ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len); + skb_pull(arg->skb, 4); + return ret; +} + + +struct wsm_protected_mgmt_policy { + bool protectedMgmtEnable; + bool unprotectedMgmtFramesAllowed; + bool encryptionForAuthFrame; +}; + +static inline int wsm_set_protected_mgmt_policy(struct cw1200_common *priv, + struct wsm_protected_mgmt_policy *arg) +{ + __le32 val = 0; + int ret; + if (arg->protectedMgmtEnable) + val |= __cpu_to_le32(BIT(0)); + if (arg->unprotectedMgmtFramesAllowed) + val |= __cpu_to_le32(BIT(1)); + if (arg->encryptionForAuthFrame) + val |= __cpu_to_le32(BIT(2)); + ret = wsm_write_mib(priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY, + &val, sizeof(val)); + return ret; +} + +struct wsm_mib_block_ack_policy { + u8 tx_tid; + u8 reserved1; + u8 rx_tid; + u8 reserved2; +} __packed; + +static inline int wsm_set_block_ack_policy(struct cw1200_common *priv, + u8 tx_tid_policy, + u8 rx_tid_policy) +{ + struct wsm_mib_block_ack_policy val = { + .tx_tid = tx_tid_policy, + .rx_tid = rx_tid_policy, + }; + return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val, + sizeof(val)); +} + +struct wsm_mib_association_mode { + u8 flags; /* WSM_ASSOCIATION_MODE_... */ + u8 preamble; /* WSM_JOIN_PREAMBLE_... */ + u8 greenfield; /* 1 for greenfield */ + u8 mpdu_start_spacing; + __le32 basic_rate_set; +} __packed; + +static inline int wsm_set_association_mode(struct cw1200_common *priv, + struct wsm_mib_association_mode *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg, + sizeof(*arg)); +} + +#define WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED BIT(2) +#define WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT BIT(3) +struct wsm_tx_rate_retry_policy { + u8 index; + u8 short_retries; + u8 long_retries; + /* BIT(2) - Terminate retries when Tx rate retry policy + * finishes. + * BIT(3) - Count initial frame transmission as part of + * rate retry counting but not as a retry + * attempt + */ + u8 flags; + u8 rate_recoveries; + u8 reserved[3]; + __le32 rate_count_indices[3]; +} __packed; + +struct wsm_set_tx_rate_retry_policy { + u8 num; + u8 reserved[3]; + struct wsm_tx_rate_retry_policy tbl[8]; +} __packed; + +static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv, + struct wsm_set_tx_rate_retry_policy *arg) +{ + size_t size = 4 + arg->num * sizeof(struct wsm_tx_rate_retry_policy); + return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, + size); +} + +/* 4.32 SetEtherTypeDataFrameFilter */ +struct wsm_ether_type_filter_hdr { + u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_ether_type_filter { + u8 action; /* WSM_FILTER_ACTION_XXX */ + u8 reserved; + __le16 type; /* Type of ethernet frame */ +} __packed; + +static inline int wsm_set_ether_type_filter(struct cw1200_common *priv, + struct wsm_ether_type_filter_hdr *arg) +{ + size_t size = sizeof(struct wsm_ether_type_filter_hdr) + + arg->num * sizeof(struct wsm_ether_type_filter); + return wsm_write_mib(priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER, + arg, size); +} + +/* 4.33 SetUDPPortDataFrameFilter */ +struct wsm_udp_port_filter_hdr { + u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_udp_port_filter { + u8 action; /* WSM_FILTER_ACTION_XXX */ + u8 type; /* WSM_FILTER_PORT_TYPE_XXX */ + __le16 port; /* Port number */ +} __packed; + +static inline int wsm_set_udp_port_filter(struct cw1200_common *priv, + struct wsm_udp_port_filter_hdr *arg) +{ + size_t size = sizeof(struct wsm_udp_port_filter_hdr) + + arg->num * sizeof(struct wsm_udp_port_filter); + return wsm_write_mib(priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER, + arg, size); +} + +/* Undocumented MIBs: */ +/* 4.35 P2PDeviceInfo */ +#define D11_MAX_SSID_LEN (32) + +struct wsm_p2p_device_type { + __le16 category_id; + u8 oui[4]; + __le16 subcategory_id; +} __packed; + +struct wsm_p2p_device_info { + struct wsm_p2p_device_type primaryDevice; + u8 reserved1[3]; + u8 devname_size; + u8 local_devname[D11_MAX_SSID_LEN]; + u8 reserved2[3]; + u8 num_secdev_supported; + struct wsm_p2p_device_type secdevs[0]; +} __packed; + +/* 4.36 SetWCDMABand - WO */ +struct wsm_cdma_band { + u8 wcdma_band; + u8 reserved[3]; +} __packed; + +/* 4.37 GroupTxSequenceCounter - RO */ +struct wsm_group_tx_seq { + __le32 bits_47_16; + __le16 bits_15_00; + __le16 reserved; +} __packed; + +/* 4.39 SetHtProtection - WO */ +#define WSM_DUAL_CTS_PROT_ENB (1 << 0) +#define WSM_NON_GREENFIELD_STA_PRESENT (1 << 1) +#define WSM_HT_PROT_MODE__NO_PROT (0 << 2) +#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2) +#define WSM_HT_PROT_MODE__20_MHZ (2 << 2) +#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2) +#define WSM_LSIG_TXOP_PROT_FULL (1 << 4) +#define WSM_LARGE_L_LENGTH_PROT (1 << 5) + +struct wsm_ht_protection { + __le32 flags; +} __packed; + +/* 4.40 GPIO Command - R/W */ +#define WSM_GPIO_COMMAND_SETUP 0 +#define WSM_GPIO_COMMAND_READ 1 +#define WSM_GPIO_COMMAND_WRITE 2 +#define WSM_GPIO_COMMAND_RESET 3 +#define WSM_GPIO_ALL_PINS 0xFF + +struct wsm_gpio_command { + u8 command; + u8 pin; + __le16 config; +} __packed; + +/* 4.41 TSFCounter - RO */ +struct wsm_tsf_counter { + __le64 tsf_counter; +} __packed; + +/* 4.43 Keep alive period */ +struct wsm_keep_alive_period { + __le16 period; + u8 reserved[2]; +} __packed; + +static inline int wsm_keep_alive_period(struct cw1200_common *priv, + int period) +{ + struct wsm_keep_alive_period arg = { + .period = __cpu_to_le16(period), + }; + return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, + &arg, sizeof(arg)); +}; + +/* BSSID filtering */ +struct wsm_set_bssid_filtering { + u8 filter; + u8 reserved[3]; +} __packed; + +static inline int wsm_set_bssid_filtering(struct cw1200_common *priv, + bool enabled) +{ + struct wsm_set_bssid_filtering arg = { + .filter = !enabled, + }; + return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER, + &arg, sizeof(arg)); +} + +/* Multicast filtering - 4.5 */ +struct wsm_mib_multicast_filter { + __le32 enable; + __le32 num_addrs; + u8 macaddrs[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN]; +} __packed; + +static inline int wsm_set_multicast_filter(struct cw1200_common *priv, + struct wsm_mib_multicast_filter *fp) +{ + return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + +/* ARP IPv4 filtering - 4.10 */ +struct wsm_mib_arp_ipv4_filter { + __le32 enable; + __be32 ipv4addrs[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES]; +} __packed; + +static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv, + struct wsm_mib_arp_ipv4_filter *fp) +{ + return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + +/* P2P Power Save Mode Info - 4.31 */ +struct wsm_p2p_ps_modeinfo { + u8 opp_ps_ct_window; + u8 count; + u8 reserved; + u8 dtim_count; + __le32 duration; + __le32 interval; + __le32 start_time; +} __packed; + +static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv, + struct wsm_p2p_ps_modeinfo *mi) +{ + return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi)); +} + +static inline int wsm_get_p2p_ps_modeinfo(struct cw1200_common *priv, + struct wsm_p2p_ps_modeinfo *mi) +{ + return wsm_read_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi)); +} + +/* UseMultiTxConfMessage */ + +static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv, + bool enabled) +{ + __le32 arg = enabled ? __cpu_to_le32(1) : 0; + + return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF, + &arg, sizeof(arg)); +} + + +/* 4.26 SetUpasdInformation */ +struct wsm_uapsd_info { + __le16 uapsd_flags; + __le16 min_auto_trigger_interval; + __le16 max_auto_trigger_interval; + __le16 auto_trigger_step; +}; + +static inline int wsm_set_uapsd_info(struct cw1200_common *priv, + struct wsm_uapsd_info *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION, + arg, sizeof(*arg)); +} + +/* 4.22 OverrideInternalTxRate */ +struct wsm_override_internal_txrate { + u8 internalTxRate; + u8 nonErpInternalTxRate; + u8 reserved[2]; +} __packed; + +static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv, + struct wsm_override_internal_txrate *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + arg, sizeof(*arg)); +} + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct cw1200_common *priv); +void wsm_lock_tx_async(struct cw1200_common *priv); +bool wsm_flush_tx(struct cw1200_common *priv); +void wsm_unlock_tx(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* WSM / BH API */ + +int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len); +int wsm_handle_rx(struct cw1200_common *priv, u16 id, struct wsm_hdr *wsm, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* wsm_buf API */ + +struct wsm_buf { + u8 *begin; + u8 *data; + u8 *end; +}; + +void wsm_buf_init(struct wsm_buf *buf); +void wsm_buf_deinit(struct wsm_buf *buf); + +/* ******************************************************************** */ +/* wsm_cmd API */ + +struct wsm_cmd { + spinlock_t lock; /* Protect structure from multiple access */ + int done; + u8 *ptr; + size_t len; + void *arg; + int ret; + u16 cmd; +}; + +/* ******************************************************************** */ +/* WSM TX buffer access */ + +int wsm_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst); +void wsm_txed(struct cw1200_common *priv, u8 *data); + +/* ******************************************************************** */ +/* Queue mapping: WSM <---> linux */ +/* Linux: VO VI BE BK */ +/* WSM: BE BK VI VO */ + +static inline u8 wsm_queue_id_to_linux(u8 queue_id) +{ + static const u8 queue_mapping[] = { + 2, 3, 1, 0 + }; + return queue_mapping[queue_id]; +} + +static inline u8 wsm_queue_id_to_wsm(u8 queue_id) +{ + static const u8 queue_mapping[] = { + 3, 2, 0, 1 + }; + return queue_mapping[queue_id]; +} + +#endif /* CW1200_HWIO_H_INCLUDED */ -- cgit v1.2.3-59-g8ed1b From 367a1092b555f4372a556ddb53970d25061c74d1 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 20:24:59 +0200 Subject: ipw2x00: move under intel vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 2 +- drivers/net/wireless/Makefile | 4 +- drivers/net/wireless/intel/Kconfig | 16 + drivers/net/wireless/intel/Makefile | 2 + drivers/net/wireless/intel/ipw2x00/Kconfig | 198 + drivers/net/wireless/intel/ipw2x00/Makefile | 14 + drivers/net/wireless/intel/ipw2x00/ipw.h | 23 + drivers/net/wireless/intel/ipw2x00/ipw2100.c | 8639 +++++++++++++ drivers/net/wireless/intel/ipw2x00/ipw2100.h | 1156 ++ drivers/net/wireless/intel/ipw2x00/ipw2200.c | 12061 +++++++++++++++++++ drivers/net/wireless/intel/ipw2x00/ipw2200.h | 2001 +++ drivers/net/wireless/intel/ipw2x00/libipw.h | 1008 ++ drivers/net/wireless/intel/ipw2x00/libipw_geo.c | 193 + drivers/net/wireless/intel/ipw2x00/libipw_module.c | 321 + drivers/net/wireless/intel/ipw2x00/libipw_rx.c | 1765 +++ drivers/net/wireless/intel/ipw2x00/libipw_tx.c | 536 + drivers/net/wireless/intel/ipw2x00/libipw_wx.c | 740 ++ drivers/net/wireless/ipw2x00/Kconfig | 198 - drivers/net/wireless/ipw2x00/Makefile | 14 - drivers/net/wireless/ipw2x00/ipw.h | 23 - drivers/net/wireless/ipw2x00/ipw2100.c | 8639 ------------- drivers/net/wireless/ipw2x00/ipw2100.h | 1156 -- drivers/net/wireless/ipw2x00/ipw2200.c | 12061 ------------------- drivers/net/wireless/ipw2x00/ipw2200.h | 2001 --- drivers/net/wireless/ipw2x00/libipw.h | 1008 -- drivers/net/wireless/ipw2x00/libipw_geo.c | 193 - drivers/net/wireless/ipw2x00/libipw_module.c | 321 - drivers/net/wireless/ipw2x00/libipw_rx.c | 1765 --- drivers/net/wireless/ipw2x00/libipw_tx.c | 536 - drivers/net/wireless/ipw2x00/libipw_wx.c | 740 -- 31 files changed, 28676 insertions(+), 28660 deletions(-) create mode 100644 drivers/net/wireless/intel/Kconfig create mode 100644 drivers/net/wireless/intel/Makefile create mode 100644 drivers/net/wireless/intel/ipw2x00/Kconfig create mode 100644 drivers/net/wireless/intel/ipw2x00/Makefile create mode 100644 drivers/net/wireless/intel/ipw2x00/ipw.h create mode 100644 drivers/net/wireless/intel/ipw2x00/ipw2100.c create mode 100644 drivers/net/wireless/intel/ipw2x00/ipw2100.h create mode 100644 drivers/net/wireless/intel/ipw2x00/ipw2200.c create mode 100644 drivers/net/wireless/intel/ipw2x00/ipw2200.h create mode 100644 drivers/net/wireless/intel/ipw2x00/libipw.h create mode 100644 drivers/net/wireless/intel/ipw2x00/libipw_geo.c create mode 100644 drivers/net/wireless/intel/ipw2x00/libipw_module.c create mode 100644 drivers/net/wireless/intel/ipw2x00/libipw_rx.c create mode 100644 drivers/net/wireless/intel/ipw2x00/libipw_tx.c create mode 100644 drivers/net/wireless/intel/ipw2x00/libipw_wx.c delete mode 100644 drivers/net/wireless/ipw2x00/Kconfig delete mode 100644 drivers/net/wireless/ipw2x00/Makefile delete mode 100644 drivers/net/wireless/ipw2x00/ipw.h delete mode 100644 drivers/net/wireless/ipw2x00/ipw2100.c delete mode 100644 drivers/net/wireless/ipw2x00/ipw2100.h delete mode 100644 drivers/net/wireless/ipw2x00/ipw2200.c delete mode 100644 drivers/net/wireless/ipw2x00/ipw2200.h delete mode 100644 drivers/net/wireless/ipw2x00/libipw.h delete mode 100644 drivers/net/wireless/ipw2x00/libipw_geo.c delete mode 100644 drivers/net/wireless/ipw2x00/libipw_module.c delete mode 100644 drivers/net/wireless/ipw2x00/libipw_rx.c delete mode 100644 drivers/net/wireless/ipw2x00/libipw_tx.c delete mode 100644 drivers/net/wireless/ipw2x00/libipw_wx.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 4cbd4641e6fb..b341dabeb579 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5607,7 +5607,7 @@ L: linux-wireless@vger.kernel.org S: Maintained F: Documentation/networking/README.ipw2100 F: Documentation/networking/README.ipw2200 -F: drivers/net/wireless/ipw2x00/ +F: drivers/net/wireless/intel/ipw2x00/ INTEL(R) TRACE HUB M: Alexander Shishkin diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index c1742f228fec..73e3b53caf45 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -21,6 +21,7 @@ source "drivers/net/wireless/admtek/Kconfig" source "drivers/net/wireless/atmel/Kconfig" source "drivers/net/wireless/broadcom/Kconfig" source "drivers/net/wireless/cisco/Kconfig" +source "drivers/net/wireless/intel/Kconfig" source "drivers/net/wireless/st/Kconfig" config PCMCIA_RAYCS @@ -161,7 +162,6 @@ config MWL8K source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/hostap/Kconfig" -source "drivers/net/wireless/ipw2x00/Kconfig" source "drivers/net/wireless/iwlwifi/Kconfig" source "drivers/net/wireless/iwlegacy/Kconfig" source "drivers/net/wireless/libertas/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index d7211a7949e6..52946d8dd31d 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -6,11 +6,9 @@ obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/ obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/ obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ +obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ -obj-$(CONFIG_IPW2100) += ipw2x00/ -obj-$(CONFIG_IPW2200) += ipw2x00/ - obj-$(CONFIG_HERMES) += orinoco/ obj-$(CONFIG_PRISM54) += prism54/ diff --git a/drivers/net/wireless/intel/Kconfig b/drivers/net/wireless/intel/Kconfig new file mode 100644 index 000000000000..3f8eacc4209a --- /dev/null +++ b/drivers/net/wireless/intel/Kconfig @@ -0,0 +1,16 @@ +config WLAN_VENDOR_INTEL + bool "Intel devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_INTEL + +source "drivers/net/wireless/intel/ipw2x00/Kconfig" + +endif # WLAN_VENDOR_INTEL diff --git a/drivers/net/wireless/intel/Makefile b/drivers/net/wireless/intel/Makefile new file mode 100644 index 000000000000..8e5dcb2d425e --- /dev/null +++ b/drivers/net/wireless/intel/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_IPW2100) += ipw2x00/ +obj-$(CONFIG_IPW2200) += ipw2x00/ diff --git a/drivers/net/wireless/intel/ipw2x00/Kconfig b/drivers/net/wireless/intel/ipw2x00/Kconfig new file mode 100644 index 000000000000..d6ec44d7a391 --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/Kconfig @@ -0,0 +1,198 @@ +# +# Intel Centrino wireless drivers +# + +config IPW2100 + tristate "Intel PRO/Wireless 2100 Network Connection" + depends on PCI && CFG80211 + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + select FW_LOADER + select LIB80211 + select LIBIPW + ---help--- + A driver for the Intel PRO/Wireless 2100 Network + Connection 802.11b wireless network adapter. + + See for information on + the capabilities currently enabled in this driver and for tips + for debugging issues and problems. + + In order to use this driver, you will need a firmware image for it. + You can obtain the firmware from + . Once you have the firmware image, you + will need to place it in /lib/firmware. + + You will also very likely need the Wireless Tools in order to + configure your card: + + . + + It is recommended that you compile this driver as a module (M) + rather than built-in (Y). This driver requires firmware at device + initialization time, and when built-in this typically happens + before the filesystem is accessible (hence firmware will be + unavailable and initialization will fail). If you do choose to build + this driver into your kernel image, you can avoid this problem by + including the firmware and a firmware loader in an initramfs. + +config IPW2100_MONITOR + bool "Enable promiscuous mode" + depends on IPW2100 + ---help--- + Enables promiscuous/monitor mode support for the ipw2100 driver. + With this feature compiled into the driver, you can switch to + promiscuous mode via the Wireless Tool's Monitor mode. While in this + mode, no packets can be sent. + +config IPW2100_DEBUG + bool "Enable full debugging output in IPW2100 module." + depends on IPW2100 + ---help--- + This option will enable debug tracing output for the IPW2100. + + This will result in the kernel module being ~60k larger. You can + control which debug output is sent to the kernel log by setting the + value in + + /sys/bus/pci/drivers/ipw2100/debug_level + + This entry will only exist if this option is enabled. + + If you are not trying to debug or develop the IPW2100 driver, you + most likely want to say N here. + +config IPW2200 + tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection" + depends on PCI && CFG80211 + select CFG80211_WEXT_EXPORT + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + select FW_LOADER + select LIB80211 + select LIBIPW + ---help--- + A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network + Connection adapters. + + See for + information on the capabilities currently enabled in this + driver and for tips for debugging issues and problems. + + In order to use this driver, you will need a firmware image for it. + You can obtain the firmware from + . See the above referenced README.ipw2200 + for information on where to install the firmware images. + + You will also very likely need the Wireless Tools in order to + configure your card: + + . + + It is recommended that you compile this driver as a module (M) + rather than built-in (Y). This driver requires firmware at device + initialization time, and when built-in this typically happens + before the filesystem is accessible (hence firmware will be + unavailable and initialization will fail). If you do choose to build + this driver into your kernel image, you can avoid this problem by + including the firmware and a firmware loader in an initramfs. + +config IPW2200_MONITOR + bool "Enable promiscuous mode" + depends on IPW2200 + ---help--- + Enables promiscuous/monitor mode support for the ipw2200 driver. + With this feature compiled into the driver, you can switch to + promiscuous mode via the Wireless Tool's Monitor mode. While in this + mode, no packets can be sent. + +config IPW2200_RADIOTAP + bool "Enable radiotap format 802.11 raw packet support" + depends on IPW2200_MONITOR + +config IPW2200_PROMISCUOUS + bool "Enable creation of a RF radiotap promiscuous interface" + depends on IPW2200_MONITOR + select IPW2200_RADIOTAP + ---help--- + Enables the creation of a second interface prefixed 'rtap'. + This second interface will provide every received in radiotap + format. + + This is useful for performing wireless network analysis while + maintaining an active association. + + Example usage: + + % modprobe ipw2200 rtap_iface=1 + % ifconfig rtap0 up + % tethereal -i rtap0 + + If you do not specify 'rtap_iface=1' as a module parameter then + the rtap interface will not be created and you will need to turn + it on via sysfs: + + % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface + +config IPW2200_QOS + bool "Enable QoS support" + depends on IPW2200 + +config IPW2200_DEBUG + bool "Enable full debugging output in IPW2200 module." + depends on IPW2200 + ---help--- + This option will enable low level debug tracing output for IPW2200. + + Note, normal debug code is already compiled in. This low level + debug option enables debug on hot paths (e.g Tx, Rx, ISR) and + will result in the kernel module being ~70 larger. Most users + will typically not need this high verbosity debug information. + + If you are not sure, say N here. + +config LIBIPW + tristate + depends on PCI && CFG80211 + select WIRELESS_EXT + select WEXT_SPY + select CRYPTO + select CRYPTO_ARC4 + select CRYPTO_ECB + select CRYPTO_AES + select CRYPTO_MICHAEL_MIC + select CRYPTO_ECB + select CRC32 + select LIB80211 + select LIB80211_CRYPT_WEP + select LIB80211_CRYPT_TKIP + select LIB80211_CRYPT_CCMP + ---help--- + This option enables the hardware independent IEEE 802.11 + networking stack. This component is deprecated in favor of the + mac80211 component. + +config LIBIPW_DEBUG + bool "Full debugging output for the LIBIPW component" + depends on LIBIPW + ---help--- + This option will enable debug tracing output for the + libipw component. + + This will result in the kernel module being ~70k larger. You + can control which debug output is sent to the kernel log by + setting the value in + + /proc/net/ieee80211/debug_level + + For example: + + % echo 0x00000FFO > /proc/net/ieee80211/debug_level + + For a list of values you can assign to debug_level, you + can look at the bit mask values in ieee80211.h + + If you are not trying to debug or develop the libipw + component, you most likely want to say N here. diff --git a/drivers/net/wireless/intel/ipw2x00/Makefile b/drivers/net/wireless/intel/ipw2x00/Makefile new file mode 100644 index 000000000000..aecd2cff462b --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the Intel Centrino wireless drivers +# + +obj-$(CONFIG_IPW2100) += ipw2100.o +obj-$(CONFIG_IPW2200) += ipw2200.o + +obj-$(CONFIG_LIBIPW) += libipw.o +libipw-objs := \ + libipw_module.o \ + libipw_tx.o \ + libipw_rx.o \ + libipw_wx.o \ + libipw_geo.o diff --git a/drivers/net/wireless/intel/ipw2x00/ipw.h b/drivers/net/wireless/intel/ipw2x00/ipw.h new file mode 100644 index 000000000000..4007bf5ed6f3 --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/ipw.h @@ -0,0 +1,23 @@ +/* + * Intel Pro/Wireless 2100, 2200BG, 2915ABG network connection driver + * + * Copyright 2012 Stanislav Yakovlev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __IPW_H__ +#define __IPW_H__ + +#include + +static const u32 ipw_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, +}; + +#endif diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c new file mode 100644 index 000000000000..36818c7f30b9 --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -0,0 +1,8639 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Intel Linux Wireless + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + + Portions of this file are based on the sample_* files provided by Wireless + Extensions 0.26 package and copyright (c) 1997-2003 Jean Tourrilhes + + + Portions of this file are based on the Host AP project, + Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + + Copyright (c) 2002-2003, Jouni Malinen + + Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and + ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c + available in the 2.4.25 kernel sources, and are copyright (c) Alan Cox + +******************************************************************************/ +/* + + Initial driver on which this is based was developed by Janusz Gorycki, + Maciej Urbaniak, and Maciej Sosnowski. + + Promiscuous mode support added by Jacek Wysoczynski and Maciej Urbaniak. + +Theory of Operation + +Tx - Commands and Data + +Firmware and host share a circular queue of Transmit Buffer Descriptors (TBDs) +Each TBD contains a pointer to the physical (dma_addr_t) address of data being +sent to the firmware as well as the length of the data. + +The host writes to the TBD queue at the WRITE index. The WRITE index points +to the _next_ packet to be written and is advanced when after the TBD has been +filled. + +The firmware pulls from the TBD queue at the READ index. The READ index points +to the currently being read entry, and is advanced once the firmware is +done with a packet. + +When data is sent to the firmware, the first TBD is used to indicate to the +firmware if a Command or Data is being sent. If it is Command, all of the +command information is contained within the physical address referred to by the +TBD. If it is Data, the first TBD indicates the type of data packet, number +of fragments, etc. The next TBD then refers to the actual packet location. + +The Tx flow cycle is as follows: + +1) ipw2100_tx() is called by kernel with SKB to transmit +2) Packet is move from the tx_free_list and appended to the transmit pending + list (tx_pend_list) +3) work is scheduled to move pending packets into the shared circular queue. +4) when placing packet in the circular queue, the incoming SKB is DMA mapped + to a physical address. That address is entered into a TBD. Two TBDs are + filled out. The first indicating a data packet, the second referring to the + actual payload data. +5) the packet is removed from tx_pend_list and placed on the end of the + firmware pending list (fw_pend_list) +6) firmware is notified that the WRITE index has +7) Once the firmware has processed the TBD, INTA is triggered. +8) For each Tx interrupt received from the firmware, the READ index is checked + to see which TBDs are done being processed. +9) For each TBD that has been processed, the ISR pulls the oldest packet + from the fw_pend_list. +10)The packet structure contained in the fw_pend_list is then used + to unmap the DMA address and to free the SKB originally passed to the driver + from the kernel. +11)The packet structure is placed onto the tx_free_list + +The above steps are the same for commands, only the msg_free_list/msg_pend_list +are used instead of tx_free_list/tx_pend_list + +... + +Critical Sections / Locking : + +There are two locks utilized. The first is the low level lock (priv->low_lock) +that protects the following: + +- Access to the Tx/Rx queue lists via priv->low_lock. The lists are as follows: + + tx_free_list : Holds pre-allocated Tx buffers. + TAIL modified in __ipw2100_tx_process() + HEAD modified in ipw2100_tx() + + tx_pend_list : Holds used Tx buffers waiting to go into the TBD ring + TAIL modified ipw2100_tx() + HEAD modified by ipw2100_tx_send_data() + + msg_free_list : Holds pre-allocated Msg (Command) buffers + TAIL modified in __ipw2100_tx_process() + HEAD modified in ipw2100_hw_send_command() + + msg_pend_list : Holds used Msg buffers waiting to go into the TBD ring + TAIL modified in ipw2100_hw_send_command() + HEAD modified in ipw2100_tx_send_commands() + + The flow of data on the TX side is as follows: + + MSG_FREE_LIST + COMMAND => MSG_PEND_LIST => TBD => MSG_FREE_LIST + TX_FREE_LIST + DATA => TX_PEND_LIST => TBD => TX_FREE_LIST + + The methods that work on the TBD ring are protected via priv->low_lock. + +- The internal data state of the device itself +- Access to the firmware read/write indexes for the BD queues + and associated logic + +All external entry functions are locked with the priv->action_lock to ensure +that only one external action is invoked at a time. + + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ipw2100.h" +#include "ipw.h" + +#define IPW2100_VERSION "git-1.2.2" + +#define DRV_NAME "ipw2100" +#define DRV_VERSION IPW2100_VERSION +#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver" +#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" + +static struct pm_qos_request ipw2100_pm_qos_req; + +/* Debugging stuff */ +#ifdef CONFIG_IPW2100_DEBUG +#define IPW2100_RX_DEBUG /* Reception debugging */ +#endif + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR(DRV_COPYRIGHT); +MODULE_LICENSE("GPL"); + +static int debug = 0; +static int network_mode = 0; +static int channel = 0; +static int associate = 0; +static int disable = 0; +#ifdef CONFIG_PM +static struct ipw2100_fw ipw2100_firmware; +#endif + +#include +module_param(debug, int, 0444); +module_param_named(mode, network_mode, int, 0444); +module_param(channel, int, 0444); +module_param(associate, int, 0444); +module_param(disable, int, 0444); + +MODULE_PARM_DESC(debug, "debug level"); +MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)"); +MODULE_PARM_DESC(channel, "channel"); +MODULE_PARM_DESC(associate, "auto associate when scanning (default off)"); +MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])"); + +static u32 ipw2100_debug_level = IPW_DL_NONE; + +#ifdef CONFIG_IPW2100_DEBUG +#define IPW_DEBUG(level, message...) \ +do { \ + if (ipw2100_debug_level & (level)) { \ + printk(KERN_DEBUG "ipw2100: %c %s ", \ + in_interrupt() ? 'I' : 'U', __func__); \ + printk(message); \ + } \ +} while (0) +#else +#define IPW_DEBUG(level, message...) do {} while (0) +#endif /* CONFIG_IPW2100_DEBUG */ + +#ifdef CONFIG_IPW2100_DEBUG +static const char *command_types[] = { + "undefined", + "unused", /* HOST_ATTENTION */ + "HOST_COMPLETE", + "unused", /* SLEEP */ + "unused", /* HOST_POWER_DOWN */ + "unused", + "SYSTEM_CONFIG", + "unused", /* SET_IMR */ + "SSID", + "MANDATORY_BSSID", + "AUTHENTICATION_TYPE", + "ADAPTER_ADDRESS", + "PORT_TYPE", + "INTERNATIONAL_MODE", + "CHANNEL", + "RTS_THRESHOLD", + "FRAG_THRESHOLD", + "POWER_MODE", + "TX_RATES", + "BASIC_TX_RATES", + "WEP_KEY_INFO", + "unused", + "unused", + "unused", + "unused", + "WEP_KEY_INDEX", + "WEP_FLAGS", + "ADD_MULTICAST", + "CLEAR_ALL_MULTICAST", + "BEACON_INTERVAL", + "ATIM_WINDOW", + "CLEAR_STATISTICS", + "undefined", + "undefined", + "undefined", + "undefined", + "TX_POWER_INDEX", + "undefined", + "undefined", + "undefined", + "undefined", + "undefined", + "undefined", + "BROADCAST_SCAN", + "CARD_DISABLE", + "PREFERRED_BSSID", + "SET_SCAN_OPTIONS", + "SCAN_DWELL_TIME", + "SWEEP_TABLE", + "AP_OR_STATION_TABLE", + "GROUP_ORDINALS", + "SHORT_RETRY_LIMIT", + "LONG_RETRY_LIMIT", + "unused", /* SAVE_CALIBRATION */ + "unused", /* RESTORE_CALIBRATION */ + "undefined", + "undefined", + "undefined", + "HOST_PRE_POWER_DOWN", + "unused", /* HOST_INTERRUPT_COALESCING */ + "undefined", + "CARD_DISABLE_PHY_OFF", + "MSDU_TX_RATES", + "undefined", + "SET_STATION_STAT_BITS", + "CLEAR_STATIONS_STAT_BITS", + "LEAP_ROGUE_MODE", + "SET_SECURITY_INFORMATION", + "DISASSOCIATION_BSSID", + "SET_WPA_ASS_IE" +}; +#endif + +static const long ipw2100_frequencies[] = { + 2412, 2417, 2422, 2427, + 2432, 2437, 2442, 2447, + 2452, 2457, 2462, 2467, + 2472, 2484 +}; + +#define FREQ_COUNT ARRAY_SIZE(ipw2100_frequencies) + +static struct ieee80211_rate ipw2100_bg_rates[] = { + { .bitrate = 10 }, + { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, +}; + +#define RATE_COUNT ARRAY_SIZE(ipw2100_bg_rates) + +/* Pre-decl until we get the code solid and then we can clean it up */ +static void ipw2100_tx_send_commands(struct ipw2100_priv *priv); +static void ipw2100_tx_send_data(struct ipw2100_priv *priv); +static int ipw2100_adapter_setup(struct ipw2100_priv *priv); + +static void ipw2100_queues_initialize(struct ipw2100_priv *priv); +static void ipw2100_queues_free(struct ipw2100_priv *priv); +static int ipw2100_queues_allocate(struct ipw2100_priv *priv); + +static int ipw2100_fw_download(struct ipw2100_priv *priv, + struct ipw2100_fw *fw); +static int ipw2100_get_firmware(struct ipw2100_priv *priv, + struct ipw2100_fw *fw); +static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf, + size_t max); +static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf, + size_t max); +static void ipw2100_release_firmware(struct ipw2100_priv *priv, + struct ipw2100_fw *fw); +static int ipw2100_ucode_download(struct ipw2100_priv *priv, + struct ipw2100_fw *fw); +static void ipw2100_wx_event_work(struct work_struct *work); +static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev); +static struct iw_handler_def ipw2100_wx_handler_def; + +static inline void read_register(struct net_device *dev, u32 reg, u32 * val) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + + *val = ioread32(priv->ioaddr + reg); + IPW_DEBUG_IO("r: 0x%08X => 0x%08X\n", reg, *val); +} + +static inline void write_register(struct net_device *dev, u32 reg, u32 val) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + + iowrite32(val, priv->ioaddr + reg); + IPW_DEBUG_IO("w: 0x%08X <= 0x%08X\n", reg, val); +} + +static inline void read_register_word(struct net_device *dev, u32 reg, + u16 * val) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + + *val = ioread16(priv->ioaddr + reg); + IPW_DEBUG_IO("r: 0x%08X => %04X\n", reg, *val); +} + +static inline void read_register_byte(struct net_device *dev, u32 reg, u8 * val) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + + *val = ioread8(priv->ioaddr + reg); + IPW_DEBUG_IO("r: 0x%08X => %02X\n", reg, *val); +} + +static inline void write_register_word(struct net_device *dev, u32 reg, u16 val) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + + iowrite16(val, priv->ioaddr + reg); + IPW_DEBUG_IO("w: 0x%08X <= %04X\n", reg, val); +} + +static inline void write_register_byte(struct net_device *dev, u32 reg, u8 val) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + + iowrite8(val, priv->ioaddr + reg); + IPW_DEBUG_IO("w: 0x%08X =< %02X\n", reg, val); +} + +static inline void read_nic_dword(struct net_device *dev, u32 addr, u32 * val) +{ + write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, + addr & IPW_REG_INDIRECT_ADDR_MASK); + read_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); +} + +static inline void write_nic_dword(struct net_device *dev, u32 addr, u32 val) +{ + write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, + addr & IPW_REG_INDIRECT_ADDR_MASK); + write_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); +} + +static inline void read_nic_word(struct net_device *dev, u32 addr, u16 * val) +{ + write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, + addr & IPW_REG_INDIRECT_ADDR_MASK); + read_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); +} + +static inline void write_nic_word(struct net_device *dev, u32 addr, u16 val) +{ + write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, + addr & IPW_REG_INDIRECT_ADDR_MASK); + write_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); +} + +static inline void read_nic_byte(struct net_device *dev, u32 addr, u8 * val) +{ + write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, + addr & IPW_REG_INDIRECT_ADDR_MASK); + read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); +} + +static inline void write_nic_byte(struct net_device *dev, u32 addr, u8 val) +{ + write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, + addr & IPW_REG_INDIRECT_ADDR_MASK); + write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); +} + +static inline void write_nic_auto_inc_address(struct net_device *dev, u32 addr) +{ + write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, + addr & IPW_REG_INDIRECT_ADDR_MASK); +} + +static inline void write_nic_dword_auto_inc(struct net_device *dev, u32 val) +{ + write_register(dev, IPW_REG_AUTOINCREMENT_DATA, val); +} + +static void write_nic_memory(struct net_device *dev, u32 addr, u32 len, + const u8 * buf) +{ + u32 aligned_addr; + u32 aligned_len; + u32 dif_len; + u32 i; + + /* read first nibble byte by byte */ + aligned_addr = addr & (~0x3); + dif_len = addr - aligned_addr; + if (dif_len) { + /* Start reading at aligned_addr + dif_len */ + write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, + aligned_addr); + for (i = dif_len; i < 4; i++, buf++) + write_register_byte(dev, + IPW_REG_INDIRECT_ACCESS_DATA + i, + *buf); + + len -= dif_len; + aligned_addr += 4; + } + + /* read DWs through autoincrement registers */ + write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr); + aligned_len = len & (~0x3); + for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4) + write_register(dev, IPW_REG_AUTOINCREMENT_DATA, *(u32 *) buf); + + /* copy the last nibble */ + dif_len = len - aligned_len; + write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr); + for (i = 0; i < dif_len; i++, buf++) + write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, + *buf); +} + +static void read_nic_memory(struct net_device *dev, u32 addr, u32 len, + u8 * buf) +{ + u32 aligned_addr; + u32 aligned_len; + u32 dif_len; + u32 i; + + /* read first nibble byte by byte */ + aligned_addr = addr & (~0x3); + dif_len = addr - aligned_addr; + if (dif_len) { + /* Start reading at aligned_addr + dif_len */ + write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, + aligned_addr); + for (i = dif_len; i < 4; i++, buf++) + read_register_byte(dev, + IPW_REG_INDIRECT_ACCESS_DATA + i, + buf); + + len -= dif_len; + aligned_addr += 4; + } + + /* read DWs through autoincrement registers */ + write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr); + aligned_len = len & (~0x3); + for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4) + read_register(dev, IPW_REG_AUTOINCREMENT_DATA, (u32 *) buf); + + /* copy the last nibble */ + dif_len = len - aligned_len; + write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr); + for (i = 0; i < dif_len; i++, buf++) + read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, buf); +} + +static bool ipw2100_hw_is_adapter_in_system(struct net_device *dev) +{ + u32 dbg; + + read_register(dev, IPW_REG_DOA_DEBUG_AREA_START, &dbg); + + return dbg == IPW_DATA_DOA_DEBUG_VALUE; +} + +static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord, + void *val, u32 * len) +{ + struct ipw2100_ordinals *ordinals = &priv->ordinals; + u32 addr; + u32 field_info; + u16 field_len; + u16 field_count; + u32 total_length; + + if (ordinals->table1_addr == 0) { + printk(KERN_WARNING DRV_NAME ": attempt to use fw ordinals " + "before they have been loaded.\n"); + return -EINVAL; + } + + if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) { + if (*len < IPW_ORD_TAB_1_ENTRY_SIZE) { + *len = IPW_ORD_TAB_1_ENTRY_SIZE; + + printk(KERN_WARNING DRV_NAME + ": ordinal buffer length too small, need %zd\n", + IPW_ORD_TAB_1_ENTRY_SIZE); + + return -EINVAL; + } + + read_nic_dword(priv->net_dev, + ordinals->table1_addr + (ord << 2), &addr); + read_nic_dword(priv->net_dev, addr, val); + + *len = IPW_ORD_TAB_1_ENTRY_SIZE; + + return 0; + } + + if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) { + + ord -= IPW_START_ORD_TAB_2; + + /* get the address of statistic */ + read_nic_dword(priv->net_dev, + ordinals->table2_addr + (ord << 3), &addr); + + /* get the second DW of statistics ; + * two 16-bit words - first is length, second is count */ + read_nic_dword(priv->net_dev, + ordinals->table2_addr + (ord << 3) + sizeof(u32), + &field_info); + + /* get each entry length */ + field_len = *((u16 *) & field_info); + + /* get number of entries */ + field_count = *(((u16 *) & field_info) + 1); + + /* abort if no enough memory */ + total_length = field_len * field_count; + if (total_length > *len) { + *len = total_length; + return -EINVAL; + } + + *len = total_length; + if (!total_length) + return 0; + + /* read the ordinal data from the SRAM */ + read_nic_memory(priv->net_dev, addr, total_length, val); + + return 0; + } + + printk(KERN_WARNING DRV_NAME ": ordinal %d neither in table 1 nor " + "in table 2\n", ord); + + return -EINVAL; +} + +static int ipw2100_set_ordinal(struct ipw2100_priv *priv, u32 ord, u32 * val, + u32 * len) +{ + struct ipw2100_ordinals *ordinals = &priv->ordinals; + u32 addr; + + if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) { + if (*len != IPW_ORD_TAB_1_ENTRY_SIZE) { + *len = IPW_ORD_TAB_1_ENTRY_SIZE; + IPW_DEBUG_INFO("wrong size\n"); + return -EINVAL; + } + + read_nic_dword(priv->net_dev, + ordinals->table1_addr + (ord << 2), &addr); + + write_nic_dword(priv->net_dev, addr, *val); + + *len = IPW_ORD_TAB_1_ENTRY_SIZE; + + return 0; + } + + IPW_DEBUG_INFO("wrong table\n"); + if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) + return -EINVAL; + + return -EINVAL; +} + +static char *snprint_line(char *buf, size_t count, + const u8 * data, u32 len, u32 ofs) +{ + int out, i, j, l; + char c; + + out = snprintf(buf, count, "%08X", ofs); + + for (l = 0, i = 0; i < 2; i++) { + out += snprintf(buf + out, count - out, " "); + for (j = 0; j < 8 && l < len; j++, l++) + out += snprintf(buf + out, count - out, "%02X ", + data[(i * 8 + j)]); + for (; j < 8; j++) + out += snprintf(buf + out, count - out, " "); + } + + out += snprintf(buf + out, count - out, " "); + for (l = 0, i = 0; i < 2; i++) { + out += snprintf(buf + out, count - out, " "); + for (j = 0; j < 8 && l < len; j++, l++) { + c = data[(i * 8 + j)]; + if (!isascii(c) || !isprint(c)) + c = '.'; + + out += snprintf(buf + out, count - out, "%c", c); + } + + for (; j < 8; j++) + out += snprintf(buf + out, count - out, " "); + } + + return buf; +} + +static void printk_buf(int level, const u8 * data, u32 len) +{ + char line[81]; + u32 ofs = 0; + if (!(ipw2100_debug_level & level)) + return; + + while (len) { + printk(KERN_DEBUG "%s\n", + snprint_line(line, sizeof(line), &data[ofs], + min(len, 16U), ofs)); + ofs += 16; + len -= min(len, 16U); + } +} + +#define MAX_RESET_BACKOFF 10 + +static void schedule_reset(struct ipw2100_priv *priv) +{ + unsigned long now = get_seconds(); + + /* If we haven't received a reset request within the backoff period, + * then we can reset the backoff interval so this reset occurs + * immediately */ + if (priv->reset_backoff && + (now - priv->last_reset > priv->reset_backoff)) + priv->reset_backoff = 0; + + priv->last_reset = get_seconds(); + + if (!(priv->status & STATUS_RESET_PENDING)) { + IPW_DEBUG_INFO("%s: Scheduling firmware restart (%ds).\n", + priv->net_dev->name, priv->reset_backoff); + netif_carrier_off(priv->net_dev); + netif_stop_queue(priv->net_dev); + priv->status |= STATUS_RESET_PENDING; + if (priv->reset_backoff) + schedule_delayed_work(&priv->reset_work, + priv->reset_backoff * HZ); + else + schedule_delayed_work(&priv->reset_work, 0); + + if (priv->reset_backoff < MAX_RESET_BACKOFF) + priv->reset_backoff++; + + wake_up_interruptible(&priv->wait_command_queue); + } else + IPW_DEBUG_INFO("%s: Firmware restart already in progress.\n", + priv->net_dev->name); + +} + +#define HOST_COMPLETE_TIMEOUT (2 * HZ) +static int ipw2100_hw_send_command(struct ipw2100_priv *priv, + struct host_command *cmd) +{ + struct list_head *element; + struct ipw2100_tx_packet *packet; + unsigned long flags; + int err = 0; + + IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n", + command_types[cmd->host_command], cmd->host_command, + cmd->host_command_length); + printk_buf(IPW_DL_HC, (u8 *) cmd->host_command_parameters, + cmd->host_command_length); + + spin_lock_irqsave(&priv->low_lock, flags); + + if (priv->fatal_error) { + IPW_DEBUG_INFO + ("Attempt to send command while hardware in fatal error condition.\n"); + err = -EIO; + goto fail_unlock; + } + + if (!(priv->status & STATUS_RUNNING)) { + IPW_DEBUG_INFO + ("Attempt to send command while hardware is not running.\n"); + err = -EIO; + goto fail_unlock; + } + + if (priv->status & STATUS_CMD_ACTIVE) { + IPW_DEBUG_INFO + ("Attempt to send command while another command is pending.\n"); + err = -EBUSY; + goto fail_unlock; + } + + if (list_empty(&priv->msg_free_list)) { + IPW_DEBUG_INFO("no available msg buffers\n"); + goto fail_unlock; + } + + priv->status |= STATUS_CMD_ACTIVE; + priv->messages_sent++; + + element = priv->msg_free_list.next; + + packet = list_entry(element, struct ipw2100_tx_packet, list); + packet->jiffy_start = jiffies; + + /* initialize the firmware command packet */ + packet->info.c_struct.cmd->host_command_reg = cmd->host_command; + packet->info.c_struct.cmd->host_command_reg1 = cmd->host_command1; + packet->info.c_struct.cmd->host_command_len_reg = + cmd->host_command_length; + packet->info.c_struct.cmd->sequence = cmd->host_command_sequence; + + memcpy(packet->info.c_struct.cmd->host_command_params_reg, + cmd->host_command_parameters, + sizeof(packet->info.c_struct.cmd->host_command_params_reg)); + + list_del(element); + DEC_STAT(&priv->msg_free_stat); + + list_add_tail(element, &priv->msg_pend_list); + INC_STAT(&priv->msg_pend_stat); + + ipw2100_tx_send_commands(priv); + ipw2100_tx_send_data(priv); + + spin_unlock_irqrestore(&priv->low_lock, flags); + + /* + * We must wait for this command to complete before another + * command can be sent... but if we wait more than 3 seconds + * then there is a problem. + */ + + err = + wait_event_interruptible_timeout(priv->wait_command_queue, + !(priv-> + status & STATUS_CMD_ACTIVE), + HOST_COMPLETE_TIMEOUT); + + if (err == 0) { + IPW_DEBUG_INFO("Command completion failed out after %dms.\n", + 1000 * (HOST_COMPLETE_TIMEOUT / HZ)); + priv->fatal_error = IPW2100_ERR_MSG_TIMEOUT; + priv->status &= ~STATUS_CMD_ACTIVE; + schedule_reset(priv); + return -EIO; + } + + if (priv->fatal_error) { + printk(KERN_WARNING DRV_NAME ": %s: firmware fatal error\n", + priv->net_dev->name); + return -EIO; + } + + /* !!!!! HACK TEST !!!!! + * When lots of debug trace statements are enabled, the driver + * doesn't seem to have as many firmware restart cycles... + * + * As a test, we're sticking in a 1/100s delay here */ + schedule_timeout_uninterruptible(msecs_to_jiffies(10)); + + return 0; + + fail_unlock: + spin_unlock_irqrestore(&priv->low_lock, flags); + + return err; +} + +/* + * Verify the values and data access of the hardware + * No locks needed or used. No functions called. + */ +static int ipw2100_verify(struct ipw2100_priv *priv) +{ + u32 data1, data2; + u32 address; + + u32 val1 = 0x76543210; + u32 val2 = 0xFEDCBA98; + + /* Domain 0 check - all values should be DOA_DEBUG */ + for (address = IPW_REG_DOA_DEBUG_AREA_START; + address < IPW_REG_DOA_DEBUG_AREA_END; address += sizeof(u32)) { + read_register(priv->net_dev, address, &data1); + if (data1 != IPW_DATA_DOA_DEBUG_VALUE) + return -EIO; + } + + /* Domain 1 check - use arbitrary read/write compare */ + for (address = 0; address < 5; address++) { + /* The memory area is not used now */ + write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32, + val1); + write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36, + val2); + read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32, + &data1); + read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36, + &data2); + if (val1 == data1 && val2 == data2) + return 0; + } + + return -EIO; +} + +/* + * + * Loop until the CARD_DISABLED bit is the same value as the + * supplied parameter + * + * TODO: See if it would be more efficient to do a wait/wake + * cycle and have the completion event trigger the wakeup + * + */ +#define IPW_CARD_DISABLE_COMPLETE_WAIT 100 // 100 milli +static int ipw2100_wait_for_card_state(struct ipw2100_priv *priv, int state) +{ + int i; + u32 card_state; + u32 len = sizeof(card_state); + int err; + + for (i = 0; i <= IPW_CARD_DISABLE_COMPLETE_WAIT * 1000; i += 50) { + err = ipw2100_get_ordinal(priv, IPW_ORD_CARD_DISABLED, + &card_state, &len); + if (err) { + IPW_DEBUG_INFO("Query of CARD_DISABLED ordinal " + "failed.\n"); + return 0; + } + + /* We'll break out if either the HW state says it is + * in the state we want, or if HOST_COMPLETE command + * finishes */ + if ((card_state == state) || + ((priv->status & STATUS_ENABLED) ? + IPW_HW_STATE_ENABLED : IPW_HW_STATE_DISABLED) == state) { + if (state == IPW_HW_STATE_ENABLED) + priv->status |= STATUS_ENABLED; + else + priv->status &= ~STATUS_ENABLED; + + return 0; + } + + udelay(50); + } + + IPW_DEBUG_INFO("ipw2100_wait_for_card_state to %s state timed out\n", + state ? "DISABLED" : "ENABLED"); + return -EIO; +} + +/********************************************************************* + Procedure : sw_reset_and_clock + Purpose : Asserts s/w reset, asserts clock initialization + and waits for clock stabilization + ********************************************************************/ +static int sw_reset_and_clock(struct ipw2100_priv *priv) +{ + int i; + u32 r; + + // assert s/w reset + write_register(priv->net_dev, IPW_REG_RESET_REG, + IPW_AUX_HOST_RESET_REG_SW_RESET); + + // wait for clock stabilization + for (i = 0; i < 1000; i++) { + udelay(IPW_WAIT_RESET_ARC_COMPLETE_DELAY); + + // check clock ready bit + read_register(priv->net_dev, IPW_REG_RESET_REG, &r); + if (r & IPW_AUX_HOST_RESET_REG_PRINCETON_RESET) + break; + } + + if (i == 1000) + return -EIO; // TODO: better error value + + /* set "initialization complete" bit to move adapter to + * D0 state */ + write_register(priv->net_dev, IPW_REG_GP_CNTRL, + IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE); + + /* wait for clock stabilization */ + for (i = 0; i < 10000; i++) { + udelay(IPW_WAIT_CLOCK_STABILIZATION_DELAY * 4); + + /* check clock ready bit */ + read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r); + if (r & IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY) + break; + } + + if (i == 10000) + return -EIO; /* TODO: better error value */ + + /* set D0 standby bit */ + read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r); + write_register(priv->net_dev, IPW_REG_GP_CNTRL, + r | IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY); + + return 0; +} + +/********************************************************************* + Procedure : ipw2100_download_firmware + Purpose : Initiaze adapter after power on. + The sequence is: + 1. assert s/w reset first! + 2. awake clocks & wait for clock stabilization + 3. hold ARC (don't ask me why...) + 4. load Dino ucode and reset/clock init again + 5. zero-out shared mem + 6. download f/w + *******************************************************************/ +static int ipw2100_download_firmware(struct ipw2100_priv *priv) +{ + u32 address; + int err; + +#ifndef CONFIG_PM + /* Fetch the firmware and microcode */ + struct ipw2100_fw ipw2100_firmware; +#endif + + if (priv->fatal_error) { + IPW_DEBUG_ERROR("%s: ipw2100_download_firmware called after " + "fatal error %d. Interface must be brought down.\n", + priv->net_dev->name, priv->fatal_error); + return -EINVAL; + } +#ifdef CONFIG_PM + if (!ipw2100_firmware.version) { + err = ipw2100_get_firmware(priv, &ipw2100_firmware); + if (err) { + IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n", + priv->net_dev->name, err); + priv->fatal_error = IPW2100_ERR_FW_LOAD; + goto fail; + } + } +#else + err = ipw2100_get_firmware(priv, &ipw2100_firmware); + if (err) { + IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n", + priv->net_dev->name, err); + priv->fatal_error = IPW2100_ERR_FW_LOAD; + goto fail; + } +#endif + priv->firmware_version = ipw2100_firmware.version; + + /* s/w reset and clock stabilization */ + err = sw_reset_and_clock(priv); + if (err) { + IPW_DEBUG_ERROR("%s: sw_reset_and_clock failed: %d\n", + priv->net_dev->name, err); + goto fail; + } + + err = ipw2100_verify(priv); + if (err) { + IPW_DEBUG_ERROR("%s: ipw2100_verify failed: %d\n", + priv->net_dev->name, err); + goto fail; + } + + /* Hold ARC */ + write_nic_dword(priv->net_dev, + IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x80000000); + + /* allow ARC to run */ + write_register(priv->net_dev, IPW_REG_RESET_REG, 0); + + /* load microcode */ + err = ipw2100_ucode_download(priv, &ipw2100_firmware); + if (err) { + printk(KERN_ERR DRV_NAME ": %s: Error loading microcode: %d\n", + priv->net_dev->name, err); + goto fail; + } + + /* release ARC */ + write_nic_dword(priv->net_dev, + IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x00000000); + + /* s/w reset and clock stabilization (again!!!) */ + err = sw_reset_and_clock(priv); + if (err) { + printk(KERN_ERR DRV_NAME + ": %s: sw_reset_and_clock failed: %d\n", + priv->net_dev->name, err); + goto fail; + } + + /* load f/w */ + err = ipw2100_fw_download(priv, &ipw2100_firmware); + if (err) { + IPW_DEBUG_ERROR("%s: Error loading firmware: %d\n", + priv->net_dev->name, err); + goto fail; + } +#ifndef CONFIG_PM + /* + * When the .resume method of the driver is called, the other + * part of the system, i.e. the ide driver could still stay in + * the suspend stage. This prevents us from loading the firmware + * from the disk. --YZ + */ + + /* free any storage allocated for firmware image */ + ipw2100_release_firmware(priv, &ipw2100_firmware); +#endif + + /* zero out Domain 1 area indirectly (Si requirement) */ + for (address = IPW_HOST_FW_SHARED_AREA0; + address < IPW_HOST_FW_SHARED_AREA0_END; address += 4) + write_nic_dword(priv->net_dev, address, 0); + for (address = IPW_HOST_FW_SHARED_AREA1; + address < IPW_HOST_FW_SHARED_AREA1_END; address += 4) + write_nic_dword(priv->net_dev, address, 0); + for (address = IPW_HOST_FW_SHARED_AREA2; + address < IPW_HOST_FW_SHARED_AREA2_END; address += 4) + write_nic_dword(priv->net_dev, address, 0); + for (address = IPW_HOST_FW_SHARED_AREA3; + address < IPW_HOST_FW_SHARED_AREA3_END; address += 4) + write_nic_dword(priv->net_dev, address, 0); + for (address = IPW_HOST_FW_INTERRUPT_AREA; + address < IPW_HOST_FW_INTERRUPT_AREA_END; address += 4) + write_nic_dword(priv->net_dev, address, 0); + + return 0; + + fail: + ipw2100_release_firmware(priv, &ipw2100_firmware); + return err; +} + +static inline void ipw2100_enable_interrupts(struct ipw2100_priv *priv) +{ + if (priv->status & STATUS_INT_ENABLED) + return; + priv->status |= STATUS_INT_ENABLED; + write_register(priv->net_dev, IPW_REG_INTA_MASK, IPW_INTERRUPT_MASK); +} + +static inline void ipw2100_disable_interrupts(struct ipw2100_priv *priv) +{ + if (!(priv->status & STATUS_INT_ENABLED)) + return; + priv->status &= ~STATUS_INT_ENABLED; + write_register(priv->net_dev, IPW_REG_INTA_MASK, 0x0); +} + +static void ipw2100_initialize_ordinals(struct ipw2100_priv *priv) +{ + struct ipw2100_ordinals *ord = &priv->ordinals; + + IPW_DEBUG_INFO("enter\n"); + + read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1, + &ord->table1_addr); + + read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2, + &ord->table2_addr); + + read_nic_dword(priv->net_dev, ord->table1_addr, &ord->table1_size); + read_nic_dword(priv->net_dev, ord->table2_addr, &ord->table2_size); + + ord->table2_size &= 0x0000FFFF; + + IPW_DEBUG_INFO("table 1 size: %d\n", ord->table1_size); + IPW_DEBUG_INFO("table 2 size: %d\n", ord->table2_size); + IPW_DEBUG_INFO("exit\n"); +} + +static inline void ipw2100_hw_set_gpio(struct ipw2100_priv *priv) +{ + u32 reg = 0; + /* + * Set GPIO 3 writable by FW; GPIO 1 writable + * by driver and enable clock + */ + reg = (IPW_BIT_GPIO_GPIO3_MASK | IPW_BIT_GPIO_GPIO1_ENABLE | + IPW_BIT_GPIO_LED_OFF); + write_register(priv->net_dev, IPW_REG_GPIO, reg); +} + +static int rf_kill_active(struct ipw2100_priv *priv) +{ +#define MAX_RF_KILL_CHECKS 5 +#define RF_KILL_CHECK_DELAY 40 + + unsigned short value = 0; + u32 reg = 0; + int i; + + if (!(priv->hw_features & HW_FEATURE_RFKILL)) { + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); + priv->status &= ~STATUS_RF_KILL_HW; + return 0; + } + + for (i = 0; i < MAX_RF_KILL_CHECKS; i++) { + udelay(RF_KILL_CHECK_DELAY); + read_register(priv->net_dev, IPW_REG_GPIO, ®); + value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1); + } + + if (value == 0) { + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); + priv->status |= STATUS_RF_KILL_HW; + } else { + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); + priv->status &= ~STATUS_RF_KILL_HW; + } + + return (value == 0); +} + +static int ipw2100_get_hw_features(struct ipw2100_priv *priv) +{ + u32 addr, len; + u32 val; + + /* + * EEPROM_SRAM_DB_START_ADDRESS using ordinal in ordinal table 1 + */ + len = sizeof(addr); + if (ipw2100_get_ordinal + (priv, IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, &addr, &len)) { + IPW_DEBUG_INFO("failed querying ordinals at line %d\n", + __LINE__); + return -EIO; + } + + IPW_DEBUG_INFO("EEPROM address: %08X\n", addr); + + /* + * EEPROM version is the byte at offset 0xfd in firmware + * We read 4 bytes, then shift out the byte we actually want */ + read_nic_dword(priv->net_dev, addr + 0xFC, &val); + priv->eeprom_version = (val >> 24) & 0xFF; + IPW_DEBUG_INFO("EEPROM version: %d\n", priv->eeprom_version); + + /* + * HW RF Kill enable is bit 0 in byte at offset 0x21 in firmware + * + * notice that the EEPROM bit is reverse polarity, i.e. + * bit = 0 signifies HW RF kill switch is supported + * bit = 1 signifies HW RF kill switch is NOT supported + */ + read_nic_dword(priv->net_dev, addr + 0x20, &val); + if (!((val >> 24) & 0x01)) + priv->hw_features |= HW_FEATURE_RFKILL; + + IPW_DEBUG_INFO("HW RF Kill: %ssupported.\n", + (priv->hw_features & HW_FEATURE_RFKILL) ? "" : "not "); + + return 0; +} + +/* + * Start firmware execution after power on and intialization + * The sequence is: + * 1. Release ARC + * 2. Wait for f/w initialization completes; + */ +static int ipw2100_start_adapter(struct ipw2100_priv *priv) +{ + int i; + u32 inta, inta_mask, gpio; + + IPW_DEBUG_INFO("enter\n"); + + if (priv->status & STATUS_RUNNING) + return 0; + + /* + * Initialize the hw - drive adapter to DO state by setting + * init_done bit. Wait for clk_ready bit and Download + * fw & dino ucode + */ + if (ipw2100_download_firmware(priv)) { + printk(KERN_ERR DRV_NAME + ": %s: Failed to power on the adapter.\n", + priv->net_dev->name); + return -EIO; + } + + /* Clear the Tx, Rx and Msg queues and the r/w indexes + * in the firmware RBD and TBD ring queue */ + ipw2100_queues_initialize(priv); + + ipw2100_hw_set_gpio(priv); + + /* TODO -- Look at disabling interrupts here to make sure none + * get fired during FW initialization */ + + /* Release ARC - clear reset bit */ + write_register(priv->net_dev, IPW_REG_RESET_REG, 0); + + /* wait for f/w intialization complete */ + IPW_DEBUG_FW("Waiting for f/w initialization to complete...\n"); + i = 5000; + do { + schedule_timeout_uninterruptible(msecs_to_jiffies(40)); + /* Todo... wait for sync command ... */ + + read_register(priv->net_dev, IPW_REG_INTA, &inta); + + /* check "init done" bit */ + if (inta & IPW2100_INTA_FW_INIT_DONE) { + /* reset "init done" bit */ + write_register(priv->net_dev, IPW_REG_INTA, + IPW2100_INTA_FW_INIT_DONE); + break; + } + + /* check error conditions : we check these after the firmware + * check so that if there is an error, the interrupt handler + * will see it and the adapter will be reset */ + if (inta & + (IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR)) { + /* clear error conditions */ + write_register(priv->net_dev, IPW_REG_INTA, + IPW2100_INTA_FATAL_ERROR | + IPW2100_INTA_PARITY_ERROR); + } + } while (--i); + + /* Clear out any pending INTAs since we aren't supposed to have + * interrupts enabled at this point... */ + read_register(priv->net_dev, IPW_REG_INTA, &inta); + read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask); + inta &= IPW_INTERRUPT_MASK; + /* Clear out any pending interrupts */ + if (inta & inta_mask) + write_register(priv->net_dev, IPW_REG_INTA, inta); + + IPW_DEBUG_FW("f/w initialization complete: %s\n", + i ? "SUCCESS" : "FAILED"); + + if (!i) { + printk(KERN_WARNING DRV_NAME + ": %s: Firmware did not initialize.\n", + priv->net_dev->name); + return -EIO; + } + + /* allow firmware to write to GPIO1 & GPIO3 */ + read_register(priv->net_dev, IPW_REG_GPIO, &gpio); + + gpio |= (IPW_BIT_GPIO_GPIO1_MASK | IPW_BIT_GPIO_GPIO3_MASK); + + write_register(priv->net_dev, IPW_REG_GPIO, gpio); + + /* Ready to receive commands */ + priv->status |= STATUS_RUNNING; + + /* The adapter has been reset; we are not associated */ + priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED); + + IPW_DEBUG_INFO("exit\n"); + + return 0; +} + +static inline void ipw2100_reset_fatalerror(struct ipw2100_priv *priv) +{ + if (!priv->fatal_error) + return; + + priv->fatal_errors[priv->fatal_index++] = priv->fatal_error; + priv->fatal_index %= IPW2100_ERROR_QUEUE; + priv->fatal_error = 0; +} + +/* NOTE: Our interrupt is disabled when this method is called */ +static int ipw2100_power_cycle_adapter(struct ipw2100_priv *priv) +{ + u32 reg; + int i; + + IPW_DEBUG_INFO("Power cycling the hardware.\n"); + + ipw2100_hw_set_gpio(priv); + + /* Step 1. Stop Master Assert */ + write_register(priv->net_dev, IPW_REG_RESET_REG, + IPW_AUX_HOST_RESET_REG_STOP_MASTER); + + /* Step 2. Wait for stop Master Assert + * (not more than 50us, otherwise ret error */ + i = 5; + do { + udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY); + read_register(priv->net_dev, IPW_REG_RESET_REG, ®); + + if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED) + break; + } while (--i); + + priv->status &= ~STATUS_RESET_PENDING; + + if (!i) { + IPW_DEBUG_INFO + ("exit - waited too long for master assert stop\n"); + return -EIO; + } + + write_register(priv->net_dev, IPW_REG_RESET_REG, + IPW_AUX_HOST_RESET_REG_SW_RESET); + + /* Reset any fatal_error conditions */ + ipw2100_reset_fatalerror(priv); + + /* At this point, the adapter is now stopped and disabled */ + priv->status &= ~(STATUS_RUNNING | STATUS_ASSOCIATING | + STATUS_ASSOCIATED | STATUS_ENABLED); + + return 0; +} + +/* + * Send the CARD_DISABLE_PHY_OFF command to the card to disable it + * + * After disabling, if the card was associated, a STATUS_ASSN_LOST will be sent. + * + * STATUS_CARD_DISABLE_NOTIFICATION will be sent regardless of + * if STATUS_ASSN_LOST is sent. + */ +static int ipw2100_hw_phy_off(struct ipw2100_priv *priv) +{ + +#define HW_PHY_OFF_LOOP_DELAY (msecs_to_jiffies(50)) + + struct host_command cmd = { + .host_command = CARD_DISABLE_PHY_OFF, + .host_command_sequence = 0, + .host_command_length = 0, + }; + int err, i; + u32 val1, val2; + + IPW_DEBUG_HC("CARD_DISABLE_PHY_OFF\n"); + + /* Turn off the radio */ + err = ipw2100_hw_send_command(priv, &cmd); + if (err) + return err; + + for (i = 0; i < 2500; i++) { + read_nic_dword(priv->net_dev, IPW2100_CONTROL_REG, &val1); + read_nic_dword(priv->net_dev, IPW2100_COMMAND, &val2); + + if ((val1 & IPW2100_CONTROL_PHY_OFF) && + (val2 & IPW2100_COMMAND_PHY_OFF)) + return 0; + + schedule_timeout_uninterruptible(HW_PHY_OFF_LOOP_DELAY); + } + + return -EIO; +} + +static int ipw2100_enable_adapter(struct ipw2100_priv *priv) +{ + struct host_command cmd = { + .host_command = HOST_COMPLETE, + .host_command_sequence = 0, + .host_command_length = 0 + }; + int err = 0; + + IPW_DEBUG_HC("HOST_COMPLETE\n"); + + if (priv->status & STATUS_ENABLED) + return 0; + + mutex_lock(&priv->adapter_mutex); + + if (rf_kill_active(priv)) { + IPW_DEBUG_HC("Command aborted due to RF kill active.\n"); + goto fail_up; + } + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) { + IPW_DEBUG_INFO("Failed to send HOST_COMPLETE command\n"); + goto fail_up; + } + + err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_ENABLED); + if (err) { + IPW_DEBUG_INFO("%s: card not responding to init command.\n", + priv->net_dev->name); + goto fail_up; + } + + if (priv->stop_hang_check) { + priv->stop_hang_check = 0; + schedule_delayed_work(&priv->hang_check, HZ / 2); + } + + fail_up: + mutex_unlock(&priv->adapter_mutex); + return err; +} + +static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv) +{ +#define HW_POWER_DOWN_DELAY (msecs_to_jiffies(100)) + + struct host_command cmd = { + .host_command = HOST_PRE_POWER_DOWN, + .host_command_sequence = 0, + .host_command_length = 0, + }; + int err, i; + u32 reg; + + if (!(priv->status & STATUS_RUNNING)) + return 0; + + priv->status |= STATUS_STOPPING; + + /* We can only shut down the card if the firmware is operational. So, + * if we haven't reset since a fatal_error, then we can not send the + * shutdown commands. */ + if (!priv->fatal_error) { + /* First, make sure the adapter is enabled so that the PHY_OFF + * command can shut it down */ + ipw2100_enable_adapter(priv); + + err = ipw2100_hw_phy_off(priv); + if (err) + printk(KERN_WARNING DRV_NAME + ": Error disabling radio %d\n", err); + + /* + * If in D0-standby mode going directly to D3 may cause a + * PCI bus violation. Therefore we must change out of the D0 + * state. + * + * Sending the PREPARE_FOR_POWER_DOWN will restrict the + * hardware from going into standby mode and will transition + * out of D0-standby if it is already in that state. + * + * STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the + * driver upon completion. Once received, the driver can + * proceed to the D3 state. + * + * Prepare for power down command to fw. This command would + * take HW out of D0-standby and prepare it for D3 state. + * + * Currently FW does not support event notification for this + * event. Therefore, skip waiting for it. Just wait a fixed + * 100ms + */ + IPW_DEBUG_HC("HOST_PRE_POWER_DOWN\n"); + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) + printk(KERN_WARNING DRV_NAME ": " + "%s: Power down command failed: Error %d\n", + priv->net_dev->name, err); + else + schedule_timeout_uninterruptible(HW_POWER_DOWN_DELAY); + } + + priv->status &= ~STATUS_ENABLED; + + /* + * Set GPIO 3 writable by FW; GPIO 1 writable + * by driver and enable clock + */ + ipw2100_hw_set_gpio(priv); + + /* + * Power down adapter. Sequence: + * 1. Stop master assert (RESET_REG[9]=1) + * 2. Wait for stop master (RESET_REG[8]==1) + * 3. S/w reset assert (RESET_REG[7] = 1) + */ + + /* Stop master assert */ + write_register(priv->net_dev, IPW_REG_RESET_REG, + IPW_AUX_HOST_RESET_REG_STOP_MASTER); + + /* wait stop master not more than 50 usec. + * Otherwise return error. */ + for (i = 5; i > 0; i--) { + udelay(10); + + /* Check master stop bit */ + read_register(priv->net_dev, IPW_REG_RESET_REG, ®); + + if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED) + break; + } + + if (i == 0) + printk(KERN_WARNING DRV_NAME + ": %s: Could now power down adapter.\n", + priv->net_dev->name); + + /* assert s/w reset */ + write_register(priv->net_dev, IPW_REG_RESET_REG, + IPW_AUX_HOST_RESET_REG_SW_RESET); + + priv->status &= ~(STATUS_RUNNING | STATUS_STOPPING); + + return 0; +} + +static int ipw2100_disable_adapter(struct ipw2100_priv *priv) +{ + struct host_command cmd = { + .host_command = CARD_DISABLE, + .host_command_sequence = 0, + .host_command_length = 0 + }; + int err = 0; + + IPW_DEBUG_HC("CARD_DISABLE\n"); + + if (!(priv->status & STATUS_ENABLED)) + return 0; + + /* Make sure we clear the associated state */ + priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); + + if (!priv->stop_hang_check) { + priv->stop_hang_check = 1; + cancel_delayed_work(&priv->hang_check); + } + + mutex_lock(&priv->adapter_mutex); + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) { + printk(KERN_WARNING DRV_NAME + ": exit - failed to send CARD_DISABLE command\n"); + goto fail_up; + } + + err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_DISABLED); + if (err) { + printk(KERN_WARNING DRV_NAME + ": exit - card failed to change to DISABLED\n"); + goto fail_up; + } + + IPW_DEBUG_INFO("TODO: implement scan state machine\n"); + + fail_up: + mutex_unlock(&priv->adapter_mutex); + return err; +} + +static int ipw2100_set_scan_options(struct ipw2100_priv *priv) +{ + struct host_command cmd = { + .host_command = SET_SCAN_OPTIONS, + .host_command_sequence = 0, + .host_command_length = 8 + }; + int err; + + IPW_DEBUG_INFO("enter\n"); + + IPW_DEBUG_SCAN("setting scan options\n"); + + cmd.host_command_parameters[0] = 0; + + if (!(priv->config & CFG_ASSOCIATE)) + cmd.host_command_parameters[0] |= IPW_SCAN_NOASSOCIATE; + if ((priv->ieee->sec.flags & SEC_ENABLED) && priv->ieee->sec.enabled) + cmd.host_command_parameters[0] |= IPW_SCAN_MIXED_CELL; + if (priv->config & CFG_PASSIVE_SCAN) + cmd.host_command_parameters[0] |= IPW_SCAN_PASSIVE; + + cmd.host_command_parameters[1] = priv->channel_mask; + + err = ipw2100_hw_send_command(priv, &cmd); + + IPW_DEBUG_HC("SET_SCAN_OPTIONS 0x%04X\n", + cmd.host_command_parameters[0]); + + return err; +} + +static int ipw2100_start_scan(struct ipw2100_priv *priv) +{ + struct host_command cmd = { + .host_command = BROADCAST_SCAN, + .host_command_sequence = 0, + .host_command_length = 4 + }; + int err; + + IPW_DEBUG_HC("START_SCAN\n"); + + cmd.host_command_parameters[0] = 0; + + /* No scanning if in monitor mode */ + if (priv->ieee->iw_mode == IW_MODE_MONITOR) + return 1; + + if (priv->status & STATUS_SCANNING) { + IPW_DEBUG_SCAN("Scan requested while already in scan...\n"); + return 0; + } + + IPW_DEBUG_INFO("enter\n"); + + /* Not clearing here; doing so makes iwlist always return nothing... + * + * We should modify the table logic to use aging tables vs. clearing + * the table on each scan start. + */ + IPW_DEBUG_SCAN("starting scan\n"); + + priv->status |= STATUS_SCANNING; + err = ipw2100_hw_send_command(priv, &cmd); + if (err) + priv->status &= ~STATUS_SCANNING; + + IPW_DEBUG_INFO("exit\n"); + + return err; +} + +static const struct libipw_geo ipw_geos[] = { + { /* Restricted */ + "---", + .bg_channels = 14, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}, {2467, 12}, + {2472, 13}, {2484, 14}}, + }, +}; + +static int ipw2100_up(struct ipw2100_priv *priv, int deferred) +{ + unsigned long flags; + int rc = 0; + u32 lock; + u32 ord_len = sizeof(lock); + + /* Age scan list entries found before suspend */ + if (priv->suspend_time) { + libipw_networks_age(priv->ieee, priv->suspend_time); + priv->suspend_time = 0; + } + + /* Quiet if manually disabled. */ + if (priv->status & STATUS_RF_KILL_SW) { + IPW_DEBUG_INFO("%s: Radio is disabled by Manual Disable " + "switch\n", priv->net_dev->name); + return 0; + } + + /* the ipw2100 hardware really doesn't want power management delays + * longer than 175usec + */ + pm_qos_update_request(&ipw2100_pm_qos_req, 175); + + /* If the interrupt is enabled, turn it off... */ + spin_lock_irqsave(&priv->low_lock, flags); + ipw2100_disable_interrupts(priv); + + /* Reset any fatal_error conditions */ + ipw2100_reset_fatalerror(priv); + spin_unlock_irqrestore(&priv->low_lock, flags); + + if (priv->status & STATUS_POWERED || + (priv->status & STATUS_RESET_PENDING)) { + /* Power cycle the card ... */ + if (ipw2100_power_cycle_adapter(priv)) { + printk(KERN_WARNING DRV_NAME + ": %s: Could not cycle adapter.\n", + priv->net_dev->name); + rc = 1; + goto exit; + } + } else + priv->status |= STATUS_POWERED; + + /* Load the firmware, start the clocks, etc. */ + if (ipw2100_start_adapter(priv)) { + printk(KERN_ERR DRV_NAME + ": %s: Failed to start the firmware.\n", + priv->net_dev->name); + rc = 1; + goto exit; + } + + ipw2100_initialize_ordinals(priv); + + /* Determine capabilities of this particular HW configuration */ + if (ipw2100_get_hw_features(priv)) { + printk(KERN_ERR DRV_NAME + ": %s: Failed to determine HW features.\n", + priv->net_dev->name); + rc = 1; + goto exit; + } + + /* Initialize the geo */ + libipw_set_geo(priv->ieee, &ipw_geos[0]); + priv->ieee->freq_band = LIBIPW_24GHZ_BAND; + + lock = LOCK_NONE; + if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) { + printk(KERN_ERR DRV_NAME + ": %s: Failed to clear ordinal lock.\n", + priv->net_dev->name); + rc = 1; + goto exit; + } + + priv->status &= ~STATUS_SCANNING; + + if (rf_kill_active(priv)) { + printk(KERN_INFO "%s: Radio is disabled by RF switch.\n", + priv->net_dev->name); + + if (priv->stop_rf_kill) { + priv->stop_rf_kill = 0; + schedule_delayed_work(&priv->rf_kill, + round_jiffies_relative(HZ)); + } + + deferred = 1; + } + + /* Turn on the interrupt so that commands can be processed */ + ipw2100_enable_interrupts(priv); + + /* Send all of the commands that must be sent prior to + * HOST_COMPLETE */ + if (ipw2100_adapter_setup(priv)) { + printk(KERN_ERR DRV_NAME ": %s: Failed to start the card.\n", + priv->net_dev->name); + rc = 1; + goto exit; + } + + if (!deferred) { + /* Enable the adapter - sends HOST_COMPLETE */ + if (ipw2100_enable_adapter(priv)) { + printk(KERN_ERR DRV_NAME ": " + "%s: failed in call to enable adapter.\n", + priv->net_dev->name); + ipw2100_hw_stop_adapter(priv); + rc = 1; + goto exit; + } + + /* Start a scan . . . */ + ipw2100_set_scan_options(priv); + ipw2100_start_scan(priv); + } + + exit: + return rc; +} + +static void ipw2100_down(struct ipw2100_priv *priv) +{ + unsigned long flags; + union iwreq_data wrqu = { + .ap_addr = { + .sa_family = ARPHRD_ETHER} + }; + int associated = priv->status & STATUS_ASSOCIATED; + + /* Kill the RF switch timer */ + if (!priv->stop_rf_kill) { + priv->stop_rf_kill = 1; + cancel_delayed_work(&priv->rf_kill); + } + + /* Kill the firmware hang check timer */ + if (!priv->stop_hang_check) { + priv->stop_hang_check = 1; + cancel_delayed_work(&priv->hang_check); + } + + /* Kill any pending resets */ + if (priv->status & STATUS_RESET_PENDING) + cancel_delayed_work(&priv->reset_work); + + /* Make sure the interrupt is on so that FW commands will be + * processed correctly */ + spin_lock_irqsave(&priv->low_lock, flags); + ipw2100_enable_interrupts(priv); + spin_unlock_irqrestore(&priv->low_lock, flags); + + if (ipw2100_hw_stop_adapter(priv)) + printk(KERN_ERR DRV_NAME ": %s: Error stopping adapter.\n", + priv->net_dev->name); + + /* Do not disable the interrupt until _after_ we disable + * the adaptor. Otherwise the CARD_DISABLE command will never + * be ack'd by the firmware */ + spin_lock_irqsave(&priv->low_lock, flags); + ipw2100_disable_interrupts(priv); + spin_unlock_irqrestore(&priv->low_lock, flags); + + pm_qos_update_request(&ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE); + + /* We have to signal any supplicant if we are disassociating */ + if (associated) + wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); + + priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); + netif_carrier_off(priv->net_dev); + netif_stop_queue(priv->net_dev); +} + +static int ipw2100_wdev_init(struct net_device *dev) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); + struct wireless_dev *wdev = &priv->ieee->wdev; + int i; + + memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); + + /* fill-out priv->ieee->bg_band */ + if (geo->bg_channels) { + struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; + + bg_band->band = IEEE80211_BAND_2GHZ; + bg_band->n_channels = geo->bg_channels; + bg_band->channels = kcalloc(geo->bg_channels, + sizeof(struct ieee80211_channel), + GFP_KERNEL); + if (!bg_band->channels) { + ipw2100_down(priv); + return -ENOMEM; + } + /* translate geo->bg to bg_band.channels */ + for (i = 0; i < geo->bg_channels; i++) { + bg_band->channels[i].band = IEEE80211_BAND_2GHZ; + bg_band->channels[i].center_freq = geo->bg[i].freq; + bg_band->channels[i].hw_value = geo->bg[i].channel; + bg_band->channels[i].max_power = geo->bg[i].max_power; + if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) + bg_band->channels[i].flags |= + IEEE80211_CHAN_NO_IR; + if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) + bg_band->channels[i].flags |= + IEEE80211_CHAN_NO_IR; + if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) + bg_band->channels[i].flags |= + IEEE80211_CHAN_RADAR; + /* No equivalent for LIBIPW_CH_80211H_RULES, + LIBIPW_CH_UNIFORM_SPREADING, or + LIBIPW_CH_B_ONLY... */ + } + /* point at bitrate info */ + bg_band->bitrates = ipw2100_bg_rates; + bg_band->n_bitrates = RATE_COUNT; + + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; + } + + wdev->wiphy->cipher_suites = ipw_cipher_suites; + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites); + + set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); + if (wiphy_register(wdev->wiphy)) + return -EIO; + return 0; +} + +static void ipw2100_reset_adapter(struct work_struct *work) +{ + struct ipw2100_priv *priv = + container_of(work, struct ipw2100_priv, reset_work.work); + unsigned long flags; + union iwreq_data wrqu = { + .ap_addr = { + .sa_family = ARPHRD_ETHER} + }; + int associated = priv->status & STATUS_ASSOCIATED; + + spin_lock_irqsave(&priv->low_lock, flags); + IPW_DEBUG_INFO(": %s: Restarting adapter.\n", priv->net_dev->name); + priv->resets++; + priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); + priv->status |= STATUS_SECURITY_UPDATED; + + /* Force a power cycle even if interface hasn't been opened + * yet */ + cancel_delayed_work(&priv->reset_work); + priv->status |= STATUS_RESET_PENDING; + spin_unlock_irqrestore(&priv->low_lock, flags); + + mutex_lock(&priv->action_mutex); + /* stop timed checks so that they don't interfere with reset */ + priv->stop_hang_check = 1; + cancel_delayed_work(&priv->hang_check); + + /* We have to signal any supplicant if we are disassociating */ + if (associated) + wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); + + ipw2100_up(priv, 0); + mutex_unlock(&priv->action_mutex); + +} + +static void isr_indicate_associated(struct ipw2100_priv *priv, u32 status) +{ + +#define MAC_ASSOCIATION_READ_DELAY (HZ) + int ret; + unsigned int len, essid_len; + char essid[IW_ESSID_MAX_SIZE]; + u32 txrate; + u32 chan; + char *txratename; + u8 bssid[ETH_ALEN]; + + /* + * TBD: BSSID is usually 00:00:00:00:00:00 here and not + * an actual MAC of the AP. Seems like FW sets this + * address too late. Read it later and expose through + * /proc or schedule a later task to query and update + */ + + essid_len = IW_ESSID_MAX_SIZE; + ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, + essid, &essid_len); + if (ret) { + IPW_DEBUG_INFO("failed querying ordinals at line %d\n", + __LINE__); + return; + } + + len = sizeof(u32); + ret = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &txrate, &len); + if (ret) { + IPW_DEBUG_INFO("failed querying ordinals at line %d\n", + __LINE__); + return; + } + + len = sizeof(u32); + ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &len); + if (ret) { + IPW_DEBUG_INFO("failed querying ordinals at line %d\n", + __LINE__); + return; + } + len = ETH_ALEN; + ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, bssid, + &len); + if (ret) { + IPW_DEBUG_INFO("failed querying ordinals at line %d\n", + __LINE__); + return; + } + memcpy(priv->ieee->bssid, bssid, ETH_ALEN); + + switch (txrate) { + case TX_RATE_1_MBIT: + txratename = "1Mbps"; + break; + case TX_RATE_2_MBIT: + txratename = "2Mbsp"; + break; + case TX_RATE_5_5_MBIT: + txratename = "5.5Mbps"; + break; + case TX_RATE_11_MBIT: + txratename = "11Mbps"; + break; + default: + IPW_DEBUG_INFO("Unknown rate: %d\n", txrate); + txratename = "unknown rate"; + break; + } + + IPW_DEBUG_INFO("%s: Associated with '%*pE' at %s, channel %d (BSSID=%pM)\n", + priv->net_dev->name, essid_len, essid, + txratename, chan, bssid); + + /* now we copy read ssid into dev */ + if (!(priv->config & CFG_STATIC_ESSID)) { + priv->essid_len = min((u8) essid_len, (u8) IW_ESSID_MAX_SIZE); + memcpy(priv->essid, essid, priv->essid_len); + } + priv->channel = chan; + memcpy(priv->bssid, bssid, ETH_ALEN); + + priv->status |= STATUS_ASSOCIATING; + priv->connect_start = get_seconds(); + + schedule_delayed_work(&priv->wx_event_work, HZ / 10); +} + +static int ipw2100_set_essid(struct ipw2100_priv *priv, char *essid, + int length, int batch_mode) +{ + int ssid_len = min(length, IW_ESSID_MAX_SIZE); + struct host_command cmd = { + .host_command = SSID, + .host_command_sequence = 0, + .host_command_length = ssid_len + }; + int err; + + IPW_DEBUG_HC("SSID: '%*pE'\n", ssid_len, essid); + + if (ssid_len) + memcpy(cmd.host_command_parameters, essid, ssid_len); + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) + return err; + } + + /* Bug in FW currently doesn't honor bit 0 in SET_SCAN_OPTIONS to + * disable auto association -- so we cheat by setting a bogus SSID */ + if (!ssid_len && !(priv->config & CFG_ASSOCIATE)) { + int i; + u8 *bogus = (u8 *) cmd.host_command_parameters; + for (i = 0; i < IW_ESSID_MAX_SIZE; i++) + bogus[i] = 0x18 + i; + cmd.host_command_length = IW_ESSID_MAX_SIZE; + } + + /* NOTE: We always send the SSID command even if the provided ESSID is + * the same as what we currently think is set. */ + + err = ipw2100_hw_send_command(priv, &cmd); + if (!err) { + memset(priv->essid + ssid_len, 0, IW_ESSID_MAX_SIZE - ssid_len); + memcpy(priv->essid, essid, ssid_len); + priv->essid_len = ssid_len; + } + + if (!batch_mode) { + if (ipw2100_enable_adapter(priv)) + err = -EIO; + } + + return err; +} + +static void isr_indicate_association_lost(struct ipw2100_priv *priv, u32 status) +{ + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, + "disassociated: '%*pE' %pM\n", priv->essid_len, priv->essid, + priv->bssid); + + priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); + + if (priv->status & STATUS_STOPPING) { + IPW_DEBUG_INFO("Card is stopping itself, discard ASSN_LOST.\n"); + return; + } + + eth_zero_addr(priv->bssid); + eth_zero_addr(priv->ieee->bssid); + + netif_carrier_off(priv->net_dev); + netif_stop_queue(priv->net_dev); + + if (!(priv->status & STATUS_RUNNING)) + return; + + if (priv->status & STATUS_SECURITY_UPDATED) + schedule_delayed_work(&priv->security_work, 0); + + schedule_delayed_work(&priv->wx_event_work, 0); +} + +static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status) +{ + IPW_DEBUG_INFO("%s: RF Kill state changed to radio OFF.\n", + priv->net_dev->name); + + /* RF_KILL is now enabled (else we wouldn't be here) */ + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); + priv->status |= STATUS_RF_KILL_HW; + + /* Make sure the RF Kill check timer is running */ + priv->stop_rf_kill = 0; + mod_delayed_work(system_wq, &priv->rf_kill, round_jiffies_relative(HZ)); +} + +static void ipw2100_scan_event(struct work_struct *work) +{ + struct ipw2100_priv *priv = container_of(work, struct ipw2100_priv, + scan_event.work); + union iwreq_data wrqu; + + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL); +} + +static void isr_scan_complete(struct ipw2100_priv *priv, u32 status) +{ + IPW_DEBUG_SCAN("scan complete\n"); + /* Age the scan results... */ + priv->ieee->scans++; + priv->status &= ~STATUS_SCANNING; + + /* Only userspace-requested scan completion events go out immediately */ + if (!priv->user_requested_scan) { + schedule_delayed_work(&priv->scan_event, + round_jiffies_relative(msecs_to_jiffies(4000))); + } else { + priv->user_requested_scan = 0; + mod_delayed_work(system_wq, &priv->scan_event, 0); + } +} + +#ifdef CONFIG_IPW2100_DEBUG +#define IPW2100_HANDLER(v, f) { v, f, # v } +struct ipw2100_status_indicator { + int status; + void (*cb) (struct ipw2100_priv * priv, u32 status); + char *name; +}; +#else +#define IPW2100_HANDLER(v, f) { v, f } +struct ipw2100_status_indicator { + int status; + void (*cb) (struct ipw2100_priv * priv, u32 status); +}; +#endif /* CONFIG_IPW2100_DEBUG */ + +static void isr_indicate_scanning(struct ipw2100_priv *priv, u32 status) +{ + IPW_DEBUG_SCAN("Scanning...\n"); + priv->status |= STATUS_SCANNING; +} + +static const struct ipw2100_status_indicator status_handlers[] = { + IPW2100_HANDLER(IPW_STATE_INITIALIZED, NULL), + IPW2100_HANDLER(IPW_STATE_COUNTRY_FOUND, NULL), + IPW2100_HANDLER(IPW_STATE_ASSOCIATED, isr_indicate_associated), + IPW2100_HANDLER(IPW_STATE_ASSN_LOST, isr_indicate_association_lost), + IPW2100_HANDLER(IPW_STATE_ASSN_CHANGED, NULL), + IPW2100_HANDLER(IPW_STATE_SCAN_COMPLETE, isr_scan_complete), + IPW2100_HANDLER(IPW_STATE_ENTERED_PSP, NULL), + IPW2100_HANDLER(IPW_STATE_LEFT_PSP, NULL), + IPW2100_HANDLER(IPW_STATE_RF_KILL, isr_indicate_rf_kill), + IPW2100_HANDLER(IPW_STATE_DISABLED, NULL), + IPW2100_HANDLER(IPW_STATE_POWER_DOWN, NULL), + IPW2100_HANDLER(IPW_STATE_SCANNING, isr_indicate_scanning), + IPW2100_HANDLER(-1, NULL) +}; + +static void isr_status_change(struct ipw2100_priv *priv, int status) +{ + int i; + + if (status == IPW_STATE_SCANNING && + priv->status & STATUS_ASSOCIATED && + !(priv->status & STATUS_SCANNING)) { + IPW_DEBUG_INFO("Scan detected while associated, with " + "no scan request. Restarting firmware.\n"); + + /* Wake up any sleeping jobs */ + schedule_reset(priv); + } + + for (i = 0; status_handlers[i].status != -1; i++) { + if (status == status_handlers[i].status) { + IPW_DEBUG_NOTIF("Status change: %s\n", + status_handlers[i].name); + if (status_handlers[i].cb) + status_handlers[i].cb(priv, status); + priv->wstats.status = status; + return; + } + } + + IPW_DEBUG_NOTIF("unknown status received: %04x\n", status); +} + +static void isr_rx_complete_command(struct ipw2100_priv *priv, + struct ipw2100_cmd_header *cmd) +{ +#ifdef CONFIG_IPW2100_DEBUG + if (cmd->host_command_reg < ARRAY_SIZE(command_types)) { + IPW_DEBUG_HC("Command completed '%s (%d)'\n", + command_types[cmd->host_command_reg], + cmd->host_command_reg); + } +#endif + if (cmd->host_command_reg == HOST_COMPLETE) + priv->status |= STATUS_ENABLED; + + if (cmd->host_command_reg == CARD_DISABLE) + priv->status &= ~STATUS_ENABLED; + + priv->status &= ~STATUS_CMD_ACTIVE; + + wake_up_interruptible(&priv->wait_command_queue); +} + +#ifdef CONFIG_IPW2100_DEBUG +static const char *frame_types[] = { + "COMMAND_STATUS_VAL", + "STATUS_CHANGE_VAL", + "P80211_DATA_VAL", + "P8023_DATA_VAL", + "HOST_NOTIFICATION_VAL" +}; +#endif + +static int ipw2100_alloc_skb(struct ipw2100_priv *priv, + struct ipw2100_rx_packet *packet) +{ + packet->skb = dev_alloc_skb(sizeof(struct ipw2100_rx)); + if (!packet->skb) + return -ENOMEM; + + packet->rxp = (struct ipw2100_rx *)packet->skb->data; + packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data, + sizeof(struct ipw2100_rx), + PCI_DMA_FROMDEVICE); + /* NOTE: pci_map_single does not return an error code, and 0 is a valid + * dma_addr */ + + return 0; +} + +#define SEARCH_ERROR 0xffffffff +#define SEARCH_FAIL 0xfffffffe +#define SEARCH_SUCCESS 0xfffffff0 +#define SEARCH_DISCARD 0 +#define SEARCH_SNAPSHOT 1 + +#define SNAPSHOT_ADDR(ofs) (priv->snapshot[((ofs) >> 12) & 0xff] + ((ofs) & 0xfff)) +static void ipw2100_snapshot_free(struct ipw2100_priv *priv) +{ + int i; + if (!priv->snapshot[0]) + return; + for (i = 0; i < 0x30; i++) + kfree(priv->snapshot[i]); + priv->snapshot[0] = NULL; +} + +#ifdef IPW2100_DEBUG_C3 +static int ipw2100_snapshot_alloc(struct ipw2100_priv *priv) +{ + int i; + if (priv->snapshot[0]) + return 1; + for (i = 0; i < 0x30; i++) { + priv->snapshot[i] = kmalloc(0x1000, GFP_ATOMIC); + if (!priv->snapshot[i]) { + IPW_DEBUG_INFO("%s: Error allocating snapshot " + "buffer %d\n", priv->net_dev->name, i); + while (i > 0) + kfree(priv->snapshot[--i]); + priv->snapshot[0] = NULL; + return 0; + } + } + + return 1; +} + +static u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 * in_buf, + size_t len, int mode) +{ + u32 i, j; + u32 tmp; + u8 *s, *d; + u32 ret; + + s = in_buf; + if (mode == SEARCH_SNAPSHOT) { + if (!ipw2100_snapshot_alloc(priv)) + mode = SEARCH_DISCARD; + } + + for (ret = SEARCH_FAIL, i = 0; i < 0x30000; i += 4) { + read_nic_dword(priv->net_dev, i, &tmp); + if (mode == SEARCH_SNAPSHOT) + *(u32 *) SNAPSHOT_ADDR(i) = tmp; + if (ret == SEARCH_FAIL) { + d = (u8 *) & tmp; + for (j = 0; j < 4; j++) { + if (*s != *d) { + s = in_buf; + continue; + } + + s++; + d++; + + if ((s - in_buf) == len) + ret = (i + j) - len + 1; + } + } else if (mode == SEARCH_DISCARD) + return ret; + } + + return ret; +} +#endif + +/* + * + * 0) Disconnect the SKB from the firmware (just unmap) + * 1) Pack the ETH header into the SKB + * 2) Pass the SKB to the network stack + * + * When packet is provided by the firmware, it contains the following: + * + * . libipw_hdr + * . libipw_snap_hdr + * + * The size of the constructed ethernet + * + */ +#ifdef IPW2100_RX_DEBUG +static u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH]; +#endif + +static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i) +{ +#ifdef IPW2100_DEBUG_C3 + struct ipw2100_status *status = &priv->status_queue.drv[i]; + u32 match, reg; + int j; +#endif + + IPW_DEBUG_INFO(": PCI latency error detected at 0x%04zX.\n", + i * sizeof(struct ipw2100_status)); + +#ifdef IPW2100_DEBUG_C3 + /* Halt the firmware so we can get a good image */ + write_register(priv->net_dev, IPW_REG_RESET_REG, + IPW_AUX_HOST_RESET_REG_STOP_MASTER); + j = 5; + do { + udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY); + read_register(priv->net_dev, IPW_REG_RESET_REG, ®); + + if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED) + break; + } while (j--); + + match = ipw2100_match_buf(priv, (u8 *) status, + sizeof(struct ipw2100_status), + SEARCH_SNAPSHOT); + if (match < SEARCH_SUCCESS) + IPW_DEBUG_INFO("%s: DMA status match in Firmware at " + "offset 0x%06X, length %d:\n", + priv->net_dev->name, match, + sizeof(struct ipw2100_status)); + else + IPW_DEBUG_INFO("%s: No DMA status match in " + "Firmware.\n", priv->net_dev->name); + + printk_buf((u8 *) priv->status_queue.drv, + sizeof(struct ipw2100_status) * RX_QUEUE_LENGTH); +#endif + + priv->fatal_error = IPW2100_ERR_C3_CORRUPTION; + priv->net_dev->stats.rx_errors++; + schedule_reset(priv); +} + +static void isr_rx(struct ipw2100_priv *priv, int i, + struct libipw_rx_stats *stats) +{ + struct net_device *dev = priv->net_dev; + struct ipw2100_status *status = &priv->status_queue.drv[i]; + struct ipw2100_rx_packet *packet = &priv->rx_buffers[i]; + + IPW_DEBUG_RX("Handler...\n"); + + if (unlikely(status->frame_size > skb_tailroom(packet->skb))) { + IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!" + " Dropping.\n", + dev->name, + status->frame_size, skb_tailroom(packet->skb)); + dev->stats.rx_errors++; + return; + } + + if (unlikely(!netif_running(dev))) { + dev->stats.rx_errors++; + priv->wstats.discard.misc++; + IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); + return; + } + + if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR && + !(priv->status & STATUS_ASSOCIATED))) { + IPW_DEBUG_DROP("Dropping packet while not associated.\n"); + priv->wstats.discard.misc++; + return; + } + + pci_unmap_single(priv->pci_dev, + packet->dma_addr, + sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE); + + skb_put(packet->skb, status->frame_size); + +#ifdef IPW2100_RX_DEBUG + /* Make a copy of the frame so we can dump it to the logs if + * libipw_rx fails */ + skb_copy_from_linear_data(packet->skb, packet_data, + min_t(u32, status->frame_size, + IPW_RX_NIC_BUFFER_LENGTH)); +#endif + + if (!libipw_rx(priv->ieee, packet->skb, stats)) { +#ifdef IPW2100_RX_DEBUG + IPW_DEBUG_DROP("%s: Non consumed packet:\n", + dev->name); + printk_buf(IPW_DL_DROP, packet_data, status->frame_size); +#endif + dev->stats.rx_errors++; + + /* libipw_rx failed, so it didn't free the SKB */ + dev_kfree_skb_any(packet->skb); + packet->skb = NULL; + } + + /* We need to allocate a new SKB and attach it to the RDB. */ + if (unlikely(ipw2100_alloc_skb(priv, packet))) { + printk(KERN_WARNING DRV_NAME ": " + "%s: Unable to allocate SKB onto RBD ring - disabling " + "adapter.\n", dev->name); + /* TODO: schedule adapter shutdown */ + IPW_DEBUG_INFO("TODO: Shutdown adapter...\n"); + } + + /* Update the RDB entry */ + priv->rx_queue.drv[i].host_addr = packet->dma_addr; +} + +#ifdef CONFIG_IPW2100_MONITOR + +static void isr_rx_monitor(struct ipw2100_priv *priv, int i, + struct libipw_rx_stats *stats) +{ + struct net_device *dev = priv->net_dev; + struct ipw2100_status *status = &priv->status_queue.drv[i]; + struct ipw2100_rx_packet *packet = &priv->rx_buffers[i]; + + /* Magic struct that slots into the radiotap header -- no reason + * to build this manually element by element, we can write it much + * more efficiently than we can parse it. ORDER MATTERS HERE */ + struct ipw_rt_hdr { + struct ieee80211_radiotap_header rt_hdr; + s8 rt_dbmsignal; /* signal in dbM, kluged to signed */ + } *ipw_rt; + + IPW_DEBUG_RX("Handler...\n"); + + if (unlikely(status->frame_size > skb_tailroom(packet->skb) - + sizeof(struct ipw_rt_hdr))) { + IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!" + " Dropping.\n", + dev->name, + status->frame_size, + skb_tailroom(packet->skb)); + dev->stats.rx_errors++; + return; + } + + if (unlikely(!netif_running(dev))) { + dev->stats.rx_errors++; + priv->wstats.discard.misc++; + IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); + return; + } + + if (unlikely(priv->config & CFG_CRC_CHECK && + status->flags & IPW_STATUS_FLAG_CRC_ERROR)) { + IPW_DEBUG_RX("CRC error in packet. Dropping.\n"); + dev->stats.rx_errors++; + return; + } + + pci_unmap_single(priv->pci_dev, packet->dma_addr, + sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE); + memmove(packet->skb->data + sizeof(struct ipw_rt_hdr), + packet->skb->data, status->frame_size); + + ipw_rt = (struct ipw_rt_hdr *) packet->skb->data; + + ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; + ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */ + ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total hdr+data */ + + ipw_rt->rt_hdr.it_present = cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL); + + ipw_rt->rt_dbmsignal = status->rssi + IPW2100_RSSI_TO_DBM; + + skb_put(packet->skb, status->frame_size + sizeof(struct ipw_rt_hdr)); + + if (!libipw_rx(priv->ieee, packet->skb, stats)) { + dev->stats.rx_errors++; + + /* libipw_rx failed, so it didn't free the SKB */ + dev_kfree_skb_any(packet->skb); + packet->skb = NULL; + } + + /* We need to allocate a new SKB and attach it to the RDB. */ + if (unlikely(ipw2100_alloc_skb(priv, packet))) { + IPW_DEBUG_WARNING( + "%s: Unable to allocate SKB onto RBD ring - disabling " + "adapter.\n", dev->name); + /* TODO: schedule adapter shutdown */ + IPW_DEBUG_INFO("TODO: Shutdown adapter...\n"); + } + + /* Update the RDB entry */ + priv->rx_queue.drv[i].host_addr = packet->dma_addr; +} + +#endif + +static int ipw2100_corruption_check(struct ipw2100_priv *priv, int i) +{ + struct ipw2100_status *status = &priv->status_queue.drv[i]; + struct ipw2100_rx *u = priv->rx_buffers[i].rxp; + u16 frame_type = status->status_fields & STATUS_TYPE_MASK; + + switch (frame_type) { + case COMMAND_STATUS_VAL: + return (status->frame_size != sizeof(u->rx_data.command)); + case STATUS_CHANGE_VAL: + return (status->frame_size != sizeof(u->rx_data.status)); + case HOST_NOTIFICATION_VAL: + return (status->frame_size < sizeof(u->rx_data.notification)); + case P80211_DATA_VAL: + case P8023_DATA_VAL: +#ifdef CONFIG_IPW2100_MONITOR + return 0; +#else + switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) { + case IEEE80211_FTYPE_MGMT: + case IEEE80211_FTYPE_CTL: + return 0; + case IEEE80211_FTYPE_DATA: + return (status->frame_size > + IPW_MAX_802_11_PAYLOAD_LENGTH); + } +#endif + } + + return 1; +} + +/* + * ipw2100 interrupts are disabled at this point, and the ISR + * is the only code that calls this method. So, we do not need + * to play with any locks. + * + * RX Queue works as follows: + * + * Read index - firmware places packet in entry identified by the + * Read index and advances Read index. In this manner, + * Read index will always point to the next packet to + * be filled--but not yet valid. + * + * Write index - driver fills this entry with an unused RBD entry. + * This entry has not filled by the firmware yet. + * + * In between the W and R indexes are the RBDs that have been received + * but not yet processed. + * + * The process of handling packets will start at WRITE + 1 and advance + * until it reaches the READ index. + * + * The WRITE index is cached in the variable 'priv->rx_queue.next'. + * + */ +static void __ipw2100_rx_process(struct ipw2100_priv *priv) +{ + struct ipw2100_bd_queue *rxq = &priv->rx_queue; + struct ipw2100_status_queue *sq = &priv->status_queue; + struct ipw2100_rx_packet *packet; + u16 frame_type; + u32 r, w, i, s; + struct ipw2100_rx *u; + struct libipw_rx_stats stats = { + .mac_time = jiffies, + }; + + read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_READ_INDEX, &r); + read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, &w); + + if (r >= rxq->entries) { + IPW_DEBUG_RX("exit - bad read index\n"); + return; + } + + i = (rxq->next + 1) % rxq->entries; + s = i; + while (i != r) { + /* IPW_DEBUG_RX("r = %d : w = %d : processing = %d\n", + r, rxq->next, i); */ + + packet = &priv->rx_buffers[i]; + + /* Sync the DMA for the RX buffer so CPU is sure to get + * the correct values */ + pci_dma_sync_single_for_cpu(priv->pci_dev, packet->dma_addr, + sizeof(struct ipw2100_rx), + PCI_DMA_FROMDEVICE); + + if (unlikely(ipw2100_corruption_check(priv, i))) { + ipw2100_corruption_detected(priv, i); + goto increment; + } + + u = packet->rxp; + frame_type = sq->drv[i].status_fields & STATUS_TYPE_MASK; + stats.rssi = sq->drv[i].rssi + IPW2100_RSSI_TO_DBM; + stats.len = sq->drv[i].frame_size; + + stats.mask = 0; + if (stats.rssi != 0) + stats.mask |= LIBIPW_STATMASK_RSSI; + stats.freq = LIBIPW_24GHZ_BAND; + + IPW_DEBUG_RX("%s: '%s' frame type received (%d).\n", + priv->net_dev->name, frame_types[frame_type], + stats.len); + + switch (frame_type) { + case COMMAND_STATUS_VAL: + /* Reset Rx watchdog */ + isr_rx_complete_command(priv, &u->rx_data.command); + break; + + case STATUS_CHANGE_VAL: + isr_status_change(priv, u->rx_data.status); + break; + + case P80211_DATA_VAL: + case P8023_DATA_VAL: +#ifdef CONFIG_IPW2100_MONITOR + if (priv->ieee->iw_mode == IW_MODE_MONITOR) { + isr_rx_monitor(priv, i, &stats); + break; + } +#endif + if (stats.len < sizeof(struct libipw_hdr_3addr)) + break; + switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) { + case IEEE80211_FTYPE_MGMT: + libipw_rx_mgt(priv->ieee, + &u->rx_data.header, &stats); + break; + + case IEEE80211_FTYPE_CTL: + break; + + case IEEE80211_FTYPE_DATA: + isr_rx(priv, i, &stats); + break; + + } + break; + } + + increment: + /* clear status field associated with this RBD */ + rxq->drv[i].status.info.field = 0; + + i = (i + 1) % rxq->entries; + } + + if (i != s) { + /* backtrack one entry, wrapping to end if at 0 */ + rxq->next = (i ? i : rxq->entries) - 1; + + write_register(priv->net_dev, + IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, rxq->next); + } +} + +/* + * __ipw2100_tx_process + * + * This routine will determine whether the next packet on + * the fw_pend_list has been processed by the firmware yet. + * + * If not, then it does nothing and returns. + * + * If so, then it removes the item from the fw_pend_list, frees + * any associated storage, and places the item back on the + * free list of its source (either msg_free_list or tx_free_list) + * + * TX Queue works as follows: + * + * Read index - points to the next TBD that the firmware will + * process. The firmware will read the data, and once + * done processing, it will advance the Read index. + * + * Write index - driver fills this entry with an constructed TBD + * entry. The Write index is not advanced until the + * packet has been configured. + * + * In between the W and R indexes are the TBDs that have NOT been + * processed. Lagging behind the R index are packets that have + * been processed but have not been freed by the driver. + * + * In order to free old storage, an internal index will be maintained + * that points to the next packet to be freed. When all used + * packets have been freed, the oldest index will be the same as the + * firmware's read index. + * + * The OLDEST index is cached in the variable 'priv->tx_queue.oldest' + * + * Because the TBD structure can not contain arbitrary data, the + * driver must keep an internal queue of cached allocations such that + * it can put that data back into the tx_free_list and msg_free_list + * for use by future command and data packets. + * + */ +static int __ipw2100_tx_process(struct ipw2100_priv *priv) +{ + struct ipw2100_bd_queue *txq = &priv->tx_queue; + struct ipw2100_bd *tbd; + struct list_head *element; + struct ipw2100_tx_packet *packet; + int descriptors_used; + int e, i; + u32 r, w, frag_num = 0; + + if (list_empty(&priv->fw_pend_list)) + return 0; + + element = priv->fw_pend_list.next; + + packet = list_entry(element, struct ipw2100_tx_packet, list); + tbd = &txq->drv[packet->index]; + + /* Determine how many TBD entries must be finished... */ + switch (packet->type) { + case COMMAND: + /* COMMAND uses only one slot; don't advance */ + descriptors_used = 1; + e = txq->oldest; + break; + + case DATA: + /* DATA uses two slots; advance and loop position. */ + descriptors_used = tbd->num_fragments; + frag_num = tbd->num_fragments - 1; + e = txq->oldest + frag_num; + e %= txq->entries; + break; + + default: + printk(KERN_WARNING DRV_NAME ": %s: Bad fw_pend_list entry!\n", + priv->net_dev->name); + return 0; + } + + /* if the last TBD is not done by NIC yet, then packet is + * not ready to be released. + * + */ + read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX, + &r); + read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX, + &w); + if (w != txq->next) + printk(KERN_WARNING DRV_NAME ": %s: write index mismatch\n", + priv->net_dev->name); + + /* + * txq->next is the index of the last packet written txq->oldest is + * the index of the r is the index of the next packet to be read by + * firmware + */ + + /* + * Quick graphic to help you visualize the following + * if / else statement + * + * ===>| s---->|=============== + * e>| + * | a | b | c | d | e | f | g | h | i | j | k | l + * r---->| + * w + * + * w - updated by driver + * r - updated by firmware + * s - start of oldest BD entry (txq->oldest) + * e - end of oldest BD entry + * + */ + if (!((r <= w && (e < r || e >= w)) || (e < r && e >= w))) { + IPW_DEBUG_TX("exit - no processed packets ready to release.\n"); + return 0; + } + + list_del(element); + DEC_STAT(&priv->fw_pend_stat); + +#ifdef CONFIG_IPW2100_DEBUG + { + i = txq->oldest; + IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i, + &txq->drv[i], + (u32) (txq->nic + i * sizeof(struct ipw2100_bd)), + txq->drv[i].host_addr, txq->drv[i].buf_length); + + if (packet->type == DATA) { + i = (i + 1) % txq->entries; + + IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i, + &txq->drv[i], + (u32) (txq->nic + i * + sizeof(struct ipw2100_bd)), + (u32) txq->drv[i].host_addr, + txq->drv[i].buf_length); + } + } +#endif + + switch (packet->type) { + case DATA: + if (txq->drv[txq->oldest].status.info.fields.txType != 0) + printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. " + "Expecting DATA TBD but pulled " + "something else: ids %d=%d.\n", + priv->net_dev->name, txq->oldest, packet->index); + + /* DATA packet; we have to unmap and free the SKB */ + for (i = 0; i < frag_num; i++) { + tbd = &txq->drv[(packet->index + 1 + i) % txq->entries]; + + IPW_DEBUG_TX("TX%d P=%08x L=%d\n", + (packet->index + 1 + i) % txq->entries, + tbd->host_addr, tbd->buf_length); + + pci_unmap_single(priv->pci_dev, + tbd->host_addr, + tbd->buf_length, PCI_DMA_TODEVICE); + } + + libipw_txb_free(packet->info.d_struct.txb); + packet->info.d_struct.txb = NULL; + + list_add_tail(element, &priv->tx_free_list); + INC_STAT(&priv->tx_free_stat); + + /* We have a free slot in the Tx queue, so wake up the + * transmit layer if it is stopped. */ + if (priv->status & STATUS_ASSOCIATED) + netif_wake_queue(priv->net_dev); + + /* A packet was processed by the hardware, so update the + * watchdog */ + priv->net_dev->trans_start = jiffies; + + break; + + case COMMAND: + if (txq->drv[txq->oldest].status.info.fields.txType != 1) + printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. " + "Expecting COMMAND TBD but pulled " + "something else: ids %d=%d.\n", + priv->net_dev->name, txq->oldest, packet->index); + +#ifdef CONFIG_IPW2100_DEBUG + if (packet->info.c_struct.cmd->host_command_reg < + ARRAY_SIZE(command_types)) + IPW_DEBUG_TX("Command '%s (%d)' processed: %d.\n", + command_types[packet->info.c_struct.cmd-> + host_command_reg], + packet->info.c_struct.cmd-> + host_command_reg, + packet->info.c_struct.cmd->cmd_status_reg); +#endif + + list_add_tail(element, &priv->msg_free_list); + INC_STAT(&priv->msg_free_stat); + break; + } + + /* advance oldest used TBD pointer to start of next entry */ + txq->oldest = (e + 1) % txq->entries; + /* increase available TBDs number */ + txq->available += descriptors_used; + SET_STAT(&priv->txq_stat, txq->available); + + IPW_DEBUG_TX("packet latency (send to process) %ld jiffies\n", + jiffies - packet->jiffy_start); + + return (!list_empty(&priv->fw_pend_list)); +} + +static inline void __ipw2100_tx_complete(struct ipw2100_priv *priv) +{ + int i = 0; + + while (__ipw2100_tx_process(priv) && i < 200) + i++; + + if (i == 200) { + printk(KERN_WARNING DRV_NAME ": " + "%s: Driver is running slow (%d iters).\n", + priv->net_dev->name, i); + } +} + +static void ipw2100_tx_send_commands(struct ipw2100_priv *priv) +{ + struct list_head *element; + struct ipw2100_tx_packet *packet; + struct ipw2100_bd_queue *txq = &priv->tx_queue; + struct ipw2100_bd *tbd; + int next = txq->next; + + while (!list_empty(&priv->msg_pend_list)) { + /* if there isn't enough space in TBD queue, then + * don't stuff a new one in. + * NOTE: 3 are needed as a command will take one, + * and there is a minimum of 2 that must be + * maintained between the r and w indexes + */ + if (txq->available <= 3) { + IPW_DEBUG_TX("no room in tx_queue\n"); + break; + } + + element = priv->msg_pend_list.next; + list_del(element); + DEC_STAT(&priv->msg_pend_stat); + + packet = list_entry(element, struct ipw2100_tx_packet, list); + + IPW_DEBUG_TX("using TBD at virt=%p, phys=%04X\n", + &txq->drv[txq->next], + (u32) (txq->nic + txq->next * + sizeof(struct ipw2100_bd))); + + packet->index = txq->next; + + tbd = &txq->drv[txq->next]; + + /* initialize TBD */ + tbd->host_addr = packet->info.c_struct.cmd_phys; + tbd->buf_length = sizeof(struct ipw2100_cmd_header); + /* not marking number of fragments causes problems + * with f/w debug version */ + tbd->num_fragments = 1; + tbd->status.info.field = + IPW_BD_STATUS_TX_FRAME_COMMAND | + IPW_BD_STATUS_TX_INTERRUPT_ENABLE; + + /* update TBD queue counters */ + txq->next++; + txq->next %= txq->entries; + txq->available--; + DEC_STAT(&priv->txq_stat); + + list_add_tail(element, &priv->fw_pend_list); + INC_STAT(&priv->fw_pend_stat); + } + + if (txq->next != next) { + /* kick off the DMA by notifying firmware the + * write index has moved; make sure TBD stores are sync'd */ + wmb(); + write_register(priv->net_dev, + IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX, + txq->next); + } +} + +/* + * ipw2100_tx_send_data + * + */ +static void ipw2100_tx_send_data(struct ipw2100_priv *priv) +{ + struct list_head *element; + struct ipw2100_tx_packet *packet; + struct ipw2100_bd_queue *txq = &priv->tx_queue; + struct ipw2100_bd *tbd; + int next = txq->next; + int i = 0; + struct ipw2100_data_header *ipw_hdr; + struct libipw_hdr_3addr *hdr; + + while (!list_empty(&priv->tx_pend_list)) { + /* if there isn't enough space in TBD queue, then + * don't stuff a new one in. + * NOTE: 4 are needed as a data will take two, + * and there is a minimum of 2 that must be + * maintained between the r and w indexes + */ + element = priv->tx_pend_list.next; + packet = list_entry(element, struct ipw2100_tx_packet, list); + + if (unlikely(1 + packet->info.d_struct.txb->nr_frags > + IPW_MAX_BDS)) { + /* TODO: Support merging buffers if more than + * IPW_MAX_BDS are used */ + IPW_DEBUG_INFO("%s: Maximum BD threshold exceeded. " + "Increase fragmentation level.\n", + priv->net_dev->name); + } + + if (txq->available <= 3 + packet->info.d_struct.txb->nr_frags) { + IPW_DEBUG_TX("no room in tx_queue\n"); + break; + } + + list_del(element); + DEC_STAT(&priv->tx_pend_stat); + + tbd = &txq->drv[txq->next]; + + packet->index = txq->next; + + ipw_hdr = packet->info.d_struct.data; + hdr = (struct libipw_hdr_3addr *)packet->info.d_struct.txb-> + fragments[0]->data; + + if (priv->ieee->iw_mode == IW_MODE_INFRA) { + /* To DS: Addr1 = BSSID, Addr2 = SA, + Addr3 = DA */ + memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN); + memcpy(ipw_hdr->dst_addr, hdr->addr3, ETH_ALEN); + } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) { + /* not From/To DS: Addr1 = DA, Addr2 = SA, + Addr3 = BSSID */ + memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN); + memcpy(ipw_hdr->dst_addr, hdr->addr1, ETH_ALEN); + } + + ipw_hdr->host_command_reg = SEND; + ipw_hdr->host_command_reg1 = 0; + + /* For now we only support host based encryption */ + ipw_hdr->needs_encryption = 0; + ipw_hdr->encrypted = packet->info.d_struct.txb->encrypted; + if (packet->info.d_struct.txb->nr_frags > 1) + ipw_hdr->fragment_size = + packet->info.d_struct.txb->frag_size - + LIBIPW_3ADDR_LEN; + else + ipw_hdr->fragment_size = 0; + + tbd->host_addr = packet->info.d_struct.data_phys; + tbd->buf_length = sizeof(struct ipw2100_data_header); + tbd->num_fragments = 1 + packet->info.d_struct.txb->nr_frags; + tbd->status.info.field = + IPW_BD_STATUS_TX_FRAME_802_3 | + IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT; + txq->next++; + txq->next %= txq->entries; + + IPW_DEBUG_TX("data header tbd TX%d P=%08x L=%d\n", + packet->index, tbd->host_addr, tbd->buf_length); +#ifdef CONFIG_IPW2100_DEBUG + if (packet->info.d_struct.txb->nr_frags > 1) + IPW_DEBUG_FRAG("fragment Tx: %d frames\n", + packet->info.d_struct.txb->nr_frags); +#endif + + for (i = 0; i < packet->info.d_struct.txb->nr_frags; i++) { + tbd = &txq->drv[txq->next]; + if (i == packet->info.d_struct.txb->nr_frags - 1) + tbd->status.info.field = + IPW_BD_STATUS_TX_FRAME_802_3 | + IPW_BD_STATUS_TX_INTERRUPT_ENABLE; + else + tbd->status.info.field = + IPW_BD_STATUS_TX_FRAME_802_3 | + IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT; + + tbd->buf_length = packet->info.d_struct.txb-> + fragments[i]->len - LIBIPW_3ADDR_LEN; + + tbd->host_addr = pci_map_single(priv->pci_dev, + packet->info.d_struct. + txb->fragments[i]-> + data + + LIBIPW_3ADDR_LEN, + tbd->buf_length, + PCI_DMA_TODEVICE); + + IPW_DEBUG_TX("data frag tbd TX%d P=%08x L=%d\n", + txq->next, tbd->host_addr, + tbd->buf_length); + + pci_dma_sync_single_for_device(priv->pci_dev, + tbd->host_addr, + tbd->buf_length, + PCI_DMA_TODEVICE); + + txq->next++; + txq->next %= txq->entries; + } + + txq->available -= 1 + packet->info.d_struct.txb->nr_frags; + SET_STAT(&priv->txq_stat, txq->available); + + list_add_tail(element, &priv->fw_pend_list); + INC_STAT(&priv->fw_pend_stat); + } + + if (txq->next != next) { + /* kick off the DMA by notifying firmware the + * write index has moved; make sure TBD stores are sync'd */ + write_register(priv->net_dev, + IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX, + txq->next); + } +} + +static void ipw2100_irq_tasklet(struct ipw2100_priv *priv) +{ + struct net_device *dev = priv->net_dev; + unsigned long flags; + u32 inta, tmp; + + spin_lock_irqsave(&priv->low_lock, flags); + ipw2100_disable_interrupts(priv); + + read_register(dev, IPW_REG_INTA, &inta); + + IPW_DEBUG_ISR("enter - INTA: 0x%08lX\n", + (unsigned long)inta & IPW_INTERRUPT_MASK); + + priv->in_isr++; + priv->interrupts++; + + /* We do not loop and keep polling for more interrupts as this + * is frowned upon and doesn't play nicely with other potentially + * chained IRQs */ + IPW_DEBUG_ISR("INTA: 0x%08lX\n", + (unsigned long)inta & IPW_INTERRUPT_MASK); + + if (inta & IPW2100_INTA_FATAL_ERROR) { + printk(KERN_WARNING DRV_NAME + ": Fatal interrupt. Scheduling firmware restart.\n"); + priv->inta_other++; + write_register(dev, IPW_REG_INTA, IPW2100_INTA_FATAL_ERROR); + + read_nic_dword(dev, IPW_NIC_FATAL_ERROR, &priv->fatal_error); + IPW_DEBUG_INFO("%s: Fatal error value: 0x%08X\n", + priv->net_dev->name, priv->fatal_error); + + read_nic_dword(dev, IPW_ERROR_ADDR(priv->fatal_error), &tmp); + IPW_DEBUG_INFO("%s: Fatal error address value: 0x%08X\n", + priv->net_dev->name, tmp); + + /* Wake up any sleeping jobs */ + schedule_reset(priv); + } + + if (inta & IPW2100_INTA_PARITY_ERROR) { + printk(KERN_ERR DRV_NAME + ": ***** PARITY ERROR INTERRUPT !!!!\n"); + priv->inta_other++; + write_register(dev, IPW_REG_INTA, IPW2100_INTA_PARITY_ERROR); + } + + if (inta & IPW2100_INTA_RX_TRANSFER) { + IPW_DEBUG_ISR("RX interrupt\n"); + + priv->rx_interrupts++; + + write_register(dev, IPW_REG_INTA, IPW2100_INTA_RX_TRANSFER); + + __ipw2100_rx_process(priv); + __ipw2100_tx_complete(priv); + } + + if (inta & IPW2100_INTA_TX_TRANSFER) { + IPW_DEBUG_ISR("TX interrupt\n"); + + priv->tx_interrupts++; + + write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_TRANSFER); + + __ipw2100_tx_complete(priv); + ipw2100_tx_send_commands(priv); + ipw2100_tx_send_data(priv); + } + + if (inta & IPW2100_INTA_TX_COMPLETE) { + IPW_DEBUG_ISR("TX complete\n"); + priv->inta_other++; + write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_COMPLETE); + + __ipw2100_tx_complete(priv); + } + + if (inta & IPW2100_INTA_EVENT_INTERRUPT) { + /* ipw2100_handle_event(dev); */ + priv->inta_other++; + write_register(dev, IPW_REG_INTA, IPW2100_INTA_EVENT_INTERRUPT); + } + + if (inta & IPW2100_INTA_FW_INIT_DONE) { + IPW_DEBUG_ISR("FW init done interrupt\n"); + priv->inta_other++; + + read_register(dev, IPW_REG_INTA, &tmp); + if (tmp & (IPW2100_INTA_FATAL_ERROR | + IPW2100_INTA_PARITY_ERROR)) { + write_register(dev, IPW_REG_INTA, + IPW2100_INTA_FATAL_ERROR | + IPW2100_INTA_PARITY_ERROR); + } + + write_register(dev, IPW_REG_INTA, IPW2100_INTA_FW_INIT_DONE); + } + + if (inta & IPW2100_INTA_STATUS_CHANGE) { + IPW_DEBUG_ISR("Status change interrupt\n"); + priv->inta_other++; + write_register(dev, IPW_REG_INTA, IPW2100_INTA_STATUS_CHANGE); + } + + if (inta & IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE) { + IPW_DEBUG_ISR("slave host mode interrupt\n"); + priv->inta_other++; + write_register(dev, IPW_REG_INTA, + IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE); + } + + priv->in_isr--; + ipw2100_enable_interrupts(priv); + + spin_unlock_irqrestore(&priv->low_lock, flags); + + IPW_DEBUG_ISR("exit\n"); +} + +static irqreturn_t ipw2100_interrupt(int irq, void *data) +{ + struct ipw2100_priv *priv = data; + u32 inta, inta_mask; + + if (!data) + return IRQ_NONE; + + spin_lock(&priv->low_lock); + + /* We check to see if we should be ignoring interrupts before + * we touch the hardware. During ucode load if we try and handle + * an interrupt we can cause keyboard problems as well as cause + * the ucode to fail to initialize */ + if (!(priv->status & STATUS_INT_ENABLED)) { + /* Shared IRQ */ + goto none; + } + + read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask); + read_register(priv->net_dev, IPW_REG_INTA, &inta); + + if (inta == 0xFFFFFFFF) { + /* Hardware disappeared */ + printk(KERN_WARNING DRV_NAME ": IRQ INTA == 0xFFFFFFFF\n"); + goto none; + } + + inta &= IPW_INTERRUPT_MASK; + + if (!(inta & inta_mask)) { + /* Shared interrupt */ + goto none; + } + + /* We disable the hardware interrupt here just to prevent unneeded + * calls to be made. We disable this again within the actual + * work tasklet, so if another part of the code re-enables the + * interrupt, that is fine */ + ipw2100_disable_interrupts(priv); + + tasklet_schedule(&priv->irq_tasklet); + spin_unlock(&priv->low_lock); + + return IRQ_HANDLED; + none: + spin_unlock(&priv->low_lock); + return IRQ_NONE; +} + +static netdev_tx_t ipw2100_tx(struct libipw_txb *txb, + struct net_device *dev, int pri) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + struct list_head *element; + struct ipw2100_tx_packet *packet; + unsigned long flags; + + spin_lock_irqsave(&priv->low_lock, flags); + + if (!(priv->status & STATUS_ASSOCIATED)) { + IPW_DEBUG_INFO("Can not transmit when not connected.\n"); + priv->net_dev->stats.tx_carrier_errors++; + netif_stop_queue(dev); + goto fail_unlock; + } + + if (list_empty(&priv->tx_free_list)) + goto fail_unlock; + + element = priv->tx_free_list.next; + packet = list_entry(element, struct ipw2100_tx_packet, list); + + packet->info.d_struct.txb = txb; + + IPW_DEBUG_TX("Sending fragment (%d bytes):\n", txb->fragments[0]->len); + printk_buf(IPW_DL_TX, txb->fragments[0]->data, txb->fragments[0]->len); + + packet->jiffy_start = jiffies; + + list_del(element); + DEC_STAT(&priv->tx_free_stat); + + list_add_tail(element, &priv->tx_pend_list); + INC_STAT(&priv->tx_pend_stat); + + ipw2100_tx_send_data(priv); + + spin_unlock_irqrestore(&priv->low_lock, flags); + return NETDEV_TX_OK; + +fail_unlock: + netif_stop_queue(dev); + spin_unlock_irqrestore(&priv->low_lock, flags); + return NETDEV_TX_BUSY; +} + +static int ipw2100_msg_allocate(struct ipw2100_priv *priv) +{ + int i, j, err = -EINVAL; + void *v; + dma_addr_t p; + + priv->msg_buffers = + kmalloc(IPW_COMMAND_POOL_SIZE * sizeof(struct ipw2100_tx_packet), + GFP_KERNEL); + if (!priv->msg_buffers) + return -ENOMEM; + + for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) { + v = pci_zalloc_consistent(priv->pci_dev, + sizeof(struct ipw2100_cmd_header), + &p); + if (!v) { + printk(KERN_ERR DRV_NAME ": " + "%s: PCI alloc failed for msg " + "buffers.\n", priv->net_dev->name); + err = -ENOMEM; + break; + } + + priv->msg_buffers[i].type = COMMAND; + priv->msg_buffers[i].info.c_struct.cmd = + (struct ipw2100_cmd_header *)v; + priv->msg_buffers[i].info.c_struct.cmd_phys = p; + } + + if (i == IPW_COMMAND_POOL_SIZE) + return 0; + + for (j = 0; j < i; j++) { + pci_free_consistent(priv->pci_dev, + sizeof(struct ipw2100_cmd_header), + priv->msg_buffers[j].info.c_struct.cmd, + priv->msg_buffers[j].info.c_struct. + cmd_phys); + } + + kfree(priv->msg_buffers); + priv->msg_buffers = NULL; + + return err; +} + +static int ipw2100_msg_initialize(struct ipw2100_priv *priv) +{ + int i; + + INIT_LIST_HEAD(&priv->msg_free_list); + INIT_LIST_HEAD(&priv->msg_pend_list); + + for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) + list_add_tail(&priv->msg_buffers[i].list, &priv->msg_free_list); + SET_STAT(&priv->msg_free_stat, i); + + return 0; +} + +static void ipw2100_msg_free(struct ipw2100_priv *priv) +{ + int i; + + if (!priv->msg_buffers) + return; + + for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) { + pci_free_consistent(priv->pci_dev, + sizeof(struct ipw2100_cmd_header), + priv->msg_buffers[i].info.c_struct.cmd, + priv->msg_buffers[i].info.c_struct. + cmd_phys); + } + + kfree(priv->msg_buffers); + priv->msg_buffers = NULL; +} + +static ssize_t show_pci(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pci_dev = container_of(d, struct pci_dev, dev); + char *out = buf; + int i, j; + u32 val; + + for (i = 0; i < 16; i++) { + out += sprintf(out, "[%08X] ", i * 16); + for (j = 0; j < 16; j += 4) { + pci_read_config_dword(pci_dev, i * 16 + j, &val); + out += sprintf(out, "%08X ", val); + } + out += sprintf(out, "\n"); + } + + return out - buf; +} + +static DEVICE_ATTR(pci, S_IRUGO, show_pci, NULL); + +static ssize_t show_cfg(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw2100_priv *p = dev_get_drvdata(d); + return sprintf(buf, "0x%08x\n", (int)p->config); +} + +static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL); + +static ssize_t show_status(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw2100_priv *p = dev_get_drvdata(d); + return sprintf(buf, "0x%08x\n", (int)p->status); +} + +static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); + +static ssize_t show_capability(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw2100_priv *p = dev_get_drvdata(d); + return sprintf(buf, "0x%08x\n", (int)p->capability); +} + +static DEVICE_ATTR(capability, S_IRUGO, show_capability, NULL); + +#define IPW2100_REG(x) { IPW_ ##x, #x } +static const struct { + u32 addr; + const char *name; +} hw_data[] = { +IPW2100_REG(REG_GP_CNTRL), + IPW2100_REG(REG_GPIO), + IPW2100_REG(REG_INTA), + IPW2100_REG(REG_INTA_MASK), IPW2100_REG(REG_RESET_REG),}; +#define IPW2100_NIC(x, s) { x, #x, s } +static const struct { + u32 addr; + const char *name; + size_t size; +} nic_data[] = { +IPW2100_NIC(IPW2100_CONTROL_REG, 2), + IPW2100_NIC(0x210014, 1), IPW2100_NIC(0x210000, 1),}; +#define IPW2100_ORD(x, d) { IPW_ORD_ ##x, #x, d } +static const struct { + u8 index; + const char *name; + const char *desc; +} ord_data[] = { +IPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"), + IPW2100_ORD(STAT_TX_HOST_COMPLETE, + "successful Host Tx's (MSDU)"), + IPW2100_ORD(STAT_TX_DIR_DATA, + "successful Directed Tx's (MSDU)"), + IPW2100_ORD(STAT_TX_DIR_DATA1, + "successful Directed Tx's (MSDU) @ 1MB"), + IPW2100_ORD(STAT_TX_DIR_DATA2, + "successful Directed Tx's (MSDU) @ 2MB"), + IPW2100_ORD(STAT_TX_DIR_DATA5_5, + "successful Directed Tx's (MSDU) @ 5_5MB"), + IPW2100_ORD(STAT_TX_DIR_DATA11, + "successful Directed Tx's (MSDU) @ 11MB"), + IPW2100_ORD(STAT_TX_NODIR_DATA1, + "successful Non_Directed Tx's (MSDU) @ 1MB"), + IPW2100_ORD(STAT_TX_NODIR_DATA2, + "successful Non_Directed Tx's (MSDU) @ 2MB"), + IPW2100_ORD(STAT_TX_NODIR_DATA5_5, + "successful Non_Directed Tx's (MSDU) @ 5.5MB"), + IPW2100_ORD(STAT_TX_NODIR_DATA11, + "successful Non_Directed Tx's (MSDU) @ 11MB"), + IPW2100_ORD(STAT_NULL_DATA, "successful NULL data Tx's"), + IPW2100_ORD(STAT_TX_RTS, "successful Tx RTS"), + IPW2100_ORD(STAT_TX_CTS, "successful Tx CTS"), + IPW2100_ORD(STAT_TX_ACK, "successful Tx ACK"), + IPW2100_ORD(STAT_TX_ASSN, "successful Association Tx's"), + IPW2100_ORD(STAT_TX_ASSN_RESP, + "successful Association response Tx's"), + IPW2100_ORD(STAT_TX_REASSN, + "successful Reassociation Tx's"), + IPW2100_ORD(STAT_TX_REASSN_RESP, + "successful Reassociation response Tx's"), + IPW2100_ORD(STAT_TX_PROBE, + "probes successfully transmitted"), + IPW2100_ORD(STAT_TX_PROBE_RESP, + "probe responses successfully transmitted"), + IPW2100_ORD(STAT_TX_BEACON, "tx beacon"), + IPW2100_ORD(STAT_TX_ATIM, "Tx ATIM"), + IPW2100_ORD(STAT_TX_DISASSN, + "successful Disassociation TX"), + IPW2100_ORD(STAT_TX_AUTH, "successful Authentication Tx"), + IPW2100_ORD(STAT_TX_DEAUTH, + "successful Deauthentication TX"), + IPW2100_ORD(STAT_TX_TOTAL_BYTES, + "Total successful Tx data bytes"), + IPW2100_ORD(STAT_TX_RETRIES, "Tx retries"), + IPW2100_ORD(STAT_TX_RETRY1, "Tx retries at 1MBPS"), + IPW2100_ORD(STAT_TX_RETRY2, "Tx retries at 2MBPS"), + IPW2100_ORD(STAT_TX_RETRY5_5, "Tx retries at 5.5MBPS"), + IPW2100_ORD(STAT_TX_RETRY11, "Tx retries at 11MBPS"), + IPW2100_ORD(STAT_TX_FAILURES, "Tx Failures"), + IPW2100_ORD(STAT_TX_MAX_TRIES_IN_HOP, + "times max tries in a hop failed"), + IPW2100_ORD(STAT_TX_DISASSN_FAIL, + "times disassociation failed"), + IPW2100_ORD(STAT_TX_ERR_CTS, "missed/bad CTS frames"), + IPW2100_ORD(STAT_TX_ERR_ACK, "tx err due to acks"), + IPW2100_ORD(STAT_RX_HOST, "packets passed to host"), + IPW2100_ORD(STAT_RX_DIR_DATA, "directed packets"), + IPW2100_ORD(STAT_RX_DIR_DATA1, "directed packets at 1MB"), + IPW2100_ORD(STAT_RX_DIR_DATA2, "directed packets at 2MB"), + IPW2100_ORD(STAT_RX_DIR_DATA5_5, + "directed packets at 5.5MB"), + IPW2100_ORD(STAT_RX_DIR_DATA11, "directed packets at 11MB"), + IPW2100_ORD(STAT_RX_NODIR_DATA, "nondirected packets"), + IPW2100_ORD(STAT_RX_NODIR_DATA1, + "nondirected packets at 1MB"), + IPW2100_ORD(STAT_RX_NODIR_DATA2, + "nondirected packets at 2MB"), + IPW2100_ORD(STAT_RX_NODIR_DATA5_5, + "nondirected packets at 5.5MB"), + IPW2100_ORD(STAT_RX_NODIR_DATA11, + "nondirected packets at 11MB"), + IPW2100_ORD(STAT_RX_NULL_DATA, "null data rx's"), + IPW2100_ORD(STAT_RX_RTS, "Rx RTS"), IPW2100_ORD(STAT_RX_CTS, + "Rx CTS"), + IPW2100_ORD(STAT_RX_ACK, "Rx ACK"), + IPW2100_ORD(STAT_RX_CFEND, "Rx CF End"), + IPW2100_ORD(STAT_RX_CFEND_ACK, "Rx CF End + CF Ack"), + IPW2100_ORD(STAT_RX_ASSN, "Association Rx's"), + IPW2100_ORD(STAT_RX_ASSN_RESP, "Association response Rx's"), + IPW2100_ORD(STAT_RX_REASSN, "Reassociation Rx's"), + IPW2100_ORD(STAT_RX_REASSN_RESP, + "Reassociation response Rx's"), + IPW2100_ORD(STAT_RX_PROBE, "probe Rx's"), + IPW2100_ORD(STAT_RX_PROBE_RESP, "probe response Rx's"), + IPW2100_ORD(STAT_RX_BEACON, "Rx beacon"), + IPW2100_ORD(STAT_RX_ATIM, "Rx ATIM"), + IPW2100_ORD(STAT_RX_DISASSN, "disassociation Rx"), + IPW2100_ORD(STAT_RX_AUTH, "authentication Rx"), + IPW2100_ORD(STAT_RX_DEAUTH, "deauthentication Rx"), + IPW2100_ORD(STAT_RX_TOTAL_BYTES, + "Total rx data bytes received"), + IPW2100_ORD(STAT_RX_ERR_CRC, "packets with Rx CRC error"), + IPW2100_ORD(STAT_RX_ERR_CRC1, "Rx CRC errors at 1MB"), + IPW2100_ORD(STAT_RX_ERR_CRC2, "Rx CRC errors at 2MB"), + IPW2100_ORD(STAT_RX_ERR_CRC5_5, "Rx CRC errors at 5.5MB"), + IPW2100_ORD(STAT_RX_ERR_CRC11, "Rx CRC errors at 11MB"), + IPW2100_ORD(STAT_RX_DUPLICATE1, + "duplicate rx packets at 1MB"), + IPW2100_ORD(STAT_RX_DUPLICATE2, + "duplicate rx packets at 2MB"), + IPW2100_ORD(STAT_RX_DUPLICATE5_5, + "duplicate rx packets at 5.5MB"), + IPW2100_ORD(STAT_RX_DUPLICATE11, + "duplicate rx packets at 11MB"), + IPW2100_ORD(STAT_RX_DUPLICATE, "duplicate rx packets"), + IPW2100_ORD(PERS_DB_LOCK, "locking fw permanent db"), + IPW2100_ORD(PERS_DB_SIZE, "size of fw permanent db"), + IPW2100_ORD(PERS_DB_ADDR, "address of fw permanent db"), + IPW2100_ORD(STAT_RX_INVALID_PROTOCOL, + "rx frames with invalid protocol"), + IPW2100_ORD(SYS_BOOT_TIME, "Boot time"), + IPW2100_ORD(STAT_RX_NO_BUFFER, + "rx frames rejected due to no buffer"), + IPW2100_ORD(STAT_RX_MISSING_FRAG, + "rx frames dropped due to missing fragment"), + IPW2100_ORD(STAT_RX_ORPHAN_FRAG, + "rx frames dropped due to non-sequential fragment"), + IPW2100_ORD(STAT_RX_ORPHAN_FRAME, + "rx frames dropped due to unmatched 1st frame"), + IPW2100_ORD(STAT_RX_FRAG_AGEOUT, + "rx frames dropped due to uncompleted frame"), + IPW2100_ORD(STAT_RX_ICV_ERRORS, + "ICV errors during decryption"), + IPW2100_ORD(STAT_PSP_SUSPENSION, "times adapter suspended"), + IPW2100_ORD(STAT_PSP_BCN_TIMEOUT, "beacon timeout"), + IPW2100_ORD(STAT_PSP_POLL_TIMEOUT, + "poll response timeouts"), + IPW2100_ORD(STAT_PSP_NONDIR_TIMEOUT, + "timeouts waiting for last {broad,multi}cast pkt"), + IPW2100_ORD(STAT_PSP_RX_DTIMS, "PSP DTIMs received"), + IPW2100_ORD(STAT_PSP_RX_TIMS, "PSP TIMs received"), + IPW2100_ORD(STAT_PSP_STATION_ID, "PSP Station ID"), + IPW2100_ORD(LAST_ASSN_TIME, "RTC time of last association"), + IPW2100_ORD(STAT_PERCENT_MISSED_BCNS, + "current calculation of % missed beacons"), + IPW2100_ORD(STAT_PERCENT_RETRIES, + "current calculation of % missed tx retries"), + IPW2100_ORD(ASSOCIATED_AP_PTR, + "0 if not associated, else pointer to AP table entry"), + IPW2100_ORD(AVAILABLE_AP_CNT, + "AP's decsribed in the AP table"), + IPW2100_ORD(AP_LIST_PTR, "Ptr to list of available APs"), + IPW2100_ORD(STAT_AP_ASSNS, "associations"), + IPW2100_ORD(STAT_ASSN_FAIL, "association failures"), + IPW2100_ORD(STAT_ASSN_RESP_FAIL, + "failures due to response fail"), + IPW2100_ORD(STAT_FULL_SCANS, "full scans"), + IPW2100_ORD(CARD_DISABLED, "Card Disabled"), + IPW2100_ORD(STAT_ROAM_INHIBIT, + "times roaming was inhibited due to activity"), + IPW2100_ORD(RSSI_AT_ASSN, + "RSSI of associated AP at time of association"), + IPW2100_ORD(STAT_ASSN_CAUSE1, + "reassociation: no probe response or TX on hop"), + IPW2100_ORD(STAT_ASSN_CAUSE2, + "reassociation: poor tx/rx quality"), + IPW2100_ORD(STAT_ASSN_CAUSE3, + "reassociation: tx/rx quality (excessive AP load"), + IPW2100_ORD(STAT_ASSN_CAUSE4, + "reassociation: AP RSSI level"), + IPW2100_ORD(STAT_ASSN_CAUSE5, + "reassociations due to load leveling"), + IPW2100_ORD(STAT_AUTH_FAIL, "times authentication failed"), + IPW2100_ORD(STAT_AUTH_RESP_FAIL, + "times authentication response failed"), + IPW2100_ORD(STATION_TABLE_CNT, + "entries in association table"), + IPW2100_ORD(RSSI_AVG_CURR, "Current avg RSSI"), + IPW2100_ORD(POWER_MGMT_MODE, "Power mode - 0=CAM, 1=PSP"), + IPW2100_ORD(COUNTRY_CODE, + "IEEE country code as recv'd from beacon"), + IPW2100_ORD(COUNTRY_CHANNELS, + "channels supported by country"), + IPW2100_ORD(RESET_CNT, "adapter resets (warm)"), + IPW2100_ORD(BEACON_INTERVAL, "Beacon interval"), + IPW2100_ORD(ANTENNA_DIVERSITY, + "TRUE if antenna diversity is disabled"), + IPW2100_ORD(DTIM_PERIOD, "beacon intervals between DTIMs"), + IPW2100_ORD(OUR_FREQ, + "current radio freq lower digits - channel ID"), + IPW2100_ORD(RTC_TIME, "current RTC time"), + IPW2100_ORD(PORT_TYPE, "operating mode"), + IPW2100_ORD(CURRENT_TX_RATE, "current tx rate"), + IPW2100_ORD(SUPPORTED_RATES, "supported tx rates"), + IPW2100_ORD(ATIM_WINDOW, "current ATIM Window"), + IPW2100_ORD(BASIC_RATES, "basic tx rates"), + IPW2100_ORD(NIC_HIGHEST_RATE, "NIC highest tx rate"), + IPW2100_ORD(AP_HIGHEST_RATE, "AP highest tx rate"), + IPW2100_ORD(CAPABILITIES, + "Management frame capability field"), + IPW2100_ORD(AUTH_TYPE, "Type of authentication"), + IPW2100_ORD(RADIO_TYPE, "Adapter card platform type"), + IPW2100_ORD(RTS_THRESHOLD, + "Min packet length for RTS handshaking"), + IPW2100_ORD(INT_MODE, "International mode"), + IPW2100_ORD(FRAGMENTATION_THRESHOLD, + "protocol frag threshold"), + IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_START_ADDRESS, + "EEPROM offset in SRAM"), + IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_SIZE, + "EEPROM size in SRAM"), + IPW2100_ORD(EEPROM_SKU_CAPABILITY, "EEPROM SKU Capability"), + IPW2100_ORD(EEPROM_IBSS_11B_CHANNELS, + "EEPROM IBSS 11b channel set"), + IPW2100_ORD(MAC_VERSION, "MAC Version"), + IPW2100_ORD(MAC_REVISION, "MAC Revision"), + IPW2100_ORD(RADIO_VERSION, "Radio Version"), + IPW2100_ORD(NIC_MANF_DATE_TIME, "MANF Date/Time STAMP"), + IPW2100_ORD(UCODE_VERSION, "Ucode Version"),}; + +static ssize_t show_registers(struct device *d, struct device_attribute *attr, + char *buf) +{ + int i; + struct ipw2100_priv *priv = dev_get_drvdata(d); + struct net_device *dev = priv->net_dev; + char *out = buf; + u32 val = 0; + + out += sprintf(out, "%30s [Address ] : Hex\n", "Register"); + + for (i = 0; i < ARRAY_SIZE(hw_data); i++) { + read_register(dev, hw_data[i].addr, &val); + out += sprintf(out, "%30s [%08X] : %08X\n", + hw_data[i].name, hw_data[i].addr, val); + } + + return out - buf; +} + +static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); + +static ssize_t show_hardware(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + struct net_device *dev = priv->net_dev; + char *out = buf; + int i; + + out += sprintf(out, "%30s [Address ] : Hex\n", "NIC entry"); + + for (i = 0; i < ARRAY_SIZE(nic_data); i++) { + u8 tmp8; + u16 tmp16; + u32 tmp32; + + switch (nic_data[i].size) { + case 1: + read_nic_byte(dev, nic_data[i].addr, &tmp8); + out += sprintf(out, "%30s [%08X] : %02X\n", + nic_data[i].name, nic_data[i].addr, + tmp8); + break; + case 2: + read_nic_word(dev, nic_data[i].addr, &tmp16); + out += sprintf(out, "%30s [%08X] : %04X\n", + nic_data[i].name, nic_data[i].addr, + tmp16); + break; + case 4: + read_nic_dword(dev, nic_data[i].addr, &tmp32); + out += sprintf(out, "%30s [%08X] : %08X\n", + nic_data[i].name, nic_data[i].addr, + tmp32); + break; + } + } + return out - buf; +} + +static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL); + +static ssize_t show_memory(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + struct net_device *dev = priv->net_dev; + static unsigned long loop = 0; + int len = 0; + u32 buffer[4]; + int i; + char line[81]; + + if (loop >= 0x30000) + loop = 0; + + /* sysfs provides us PAGE_SIZE buffer */ + while (len < PAGE_SIZE - 128 && loop < 0x30000) { + + if (priv->snapshot[0]) + for (i = 0; i < 4; i++) + buffer[i] = + *(u32 *) SNAPSHOT_ADDR(loop + i * 4); + else + for (i = 0; i < 4; i++) + read_nic_dword(dev, loop + i * 4, &buffer[i]); + + if (priv->dump_raw) + len += sprintf(buf + len, + "%c%c%c%c" + "%c%c%c%c" + "%c%c%c%c" + "%c%c%c%c", + ((u8 *) buffer)[0x0], + ((u8 *) buffer)[0x1], + ((u8 *) buffer)[0x2], + ((u8 *) buffer)[0x3], + ((u8 *) buffer)[0x4], + ((u8 *) buffer)[0x5], + ((u8 *) buffer)[0x6], + ((u8 *) buffer)[0x7], + ((u8 *) buffer)[0x8], + ((u8 *) buffer)[0x9], + ((u8 *) buffer)[0xa], + ((u8 *) buffer)[0xb], + ((u8 *) buffer)[0xc], + ((u8 *) buffer)[0xd], + ((u8 *) buffer)[0xe], + ((u8 *) buffer)[0xf]); + else + len += sprintf(buf + len, "%s\n", + snprint_line(line, sizeof(line), + (u8 *) buffer, 16, loop)); + loop += 16; + } + + return len; +} + +static ssize_t store_memory(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + struct net_device *dev = priv->net_dev; + const char *p = buf; + + (void)dev; /* kill unused-var warning for debug-only code */ + + if (count < 1) + return count; + + if (p[0] == '1' || + (count >= 2 && tolower(p[0]) == 'o' && tolower(p[1]) == 'n')) { + IPW_DEBUG_INFO("%s: Setting memory dump to RAW mode.\n", + dev->name); + priv->dump_raw = 1; + + } else if (p[0] == '0' || (count >= 2 && tolower(p[0]) == 'o' && + tolower(p[1]) == 'f')) { + IPW_DEBUG_INFO("%s: Setting memory dump to HEX mode.\n", + dev->name); + priv->dump_raw = 0; + + } else if (tolower(p[0]) == 'r') { + IPW_DEBUG_INFO("%s: Resetting firmware snapshot.\n", dev->name); + ipw2100_snapshot_free(priv); + + } else + IPW_DEBUG_INFO("%s: Usage: 0|on = HEX, 1|off = RAW, " + "reset = clear memory snapshot\n", dev->name); + + return count; +} + +static DEVICE_ATTR(memory, S_IWUSR | S_IRUGO, show_memory, store_memory); + +static ssize_t show_ordinals(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + u32 val = 0; + int len = 0; + u32 val_len; + static int loop = 0; + + if (priv->status & STATUS_RF_KILL_MASK) + return 0; + + if (loop >= ARRAY_SIZE(ord_data)) + loop = 0; + + /* sysfs provides us PAGE_SIZE buffer */ + while (len < PAGE_SIZE - 128 && loop < ARRAY_SIZE(ord_data)) { + val_len = sizeof(u32); + + if (ipw2100_get_ordinal(priv, ord_data[loop].index, &val, + &val_len)) + len += sprintf(buf + len, "[0x%02X] = ERROR %s\n", + ord_data[loop].index, + ord_data[loop].desc); + else + len += sprintf(buf + len, "[0x%02X] = 0x%08X %s\n", + ord_data[loop].index, val, + ord_data[loop].desc); + loop++; + } + + return len; +} + +static DEVICE_ATTR(ordinals, S_IRUGO, show_ordinals, NULL); + +static ssize_t show_stats(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + char *out = buf; + + out += sprintf(out, "interrupts: %d {tx: %d, rx: %d, other: %d}\n", + priv->interrupts, priv->tx_interrupts, + priv->rx_interrupts, priv->inta_other); + out += sprintf(out, "firmware resets: %d\n", priv->resets); + out += sprintf(out, "firmware hangs: %d\n", priv->hangs); +#ifdef CONFIG_IPW2100_DEBUG + out += sprintf(out, "packet mismatch image: %s\n", + priv->snapshot[0] ? "YES" : "NO"); +#endif + + return out - buf; +} + +static DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL); + +static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode) +{ + int err; + + if (mode == priv->ieee->iw_mode) + return 0; + + err = ipw2100_disable_adapter(priv); + if (err) { + printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n", + priv->net_dev->name, err); + return err; + } + + switch (mode) { + case IW_MODE_INFRA: + priv->net_dev->type = ARPHRD_ETHER; + break; + case IW_MODE_ADHOC: + priv->net_dev->type = ARPHRD_ETHER; + break; +#ifdef CONFIG_IPW2100_MONITOR + case IW_MODE_MONITOR: + priv->last_mode = priv->ieee->iw_mode; + priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; + break; +#endif /* CONFIG_IPW2100_MONITOR */ + } + + priv->ieee->iw_mode = mode; + +#ifdef CONFIG_PM + /* Indicate ipw2100_download_firmware download firmware + * from disk instead of memory. */ + ipw2100_firmware.version = 0; +#endif + + printk(KERN_INFO "%s: Resetting on mode change.\n", priv->net_dev->name); + priv->reset_backoff = 0; + schedule_reset(priv); + + return 0; +} + +static ssize_t show_internals(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + int len = 0; + +#define DUMP_VAR(x,y) len += sprintf(buf + len, # x ": %" y "\n", priv-> x) + + if (priv->status & STATUS_ASSOCIATED) + len += sprintf(buf + len, "connected: %lu\n", + get_seconds() - priv->connect_start); + else + len += sprintf(buf + len, "not connected\n"); + + DUMP_VAR(ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx], "p"); + DUMP_VAR(status, "08lx"); + DUMP_VAR(config, "08lx"); + DUMP_VAR(capability, "08lx"); + + len += + sprintf(buf + len, "last_rtc: %lu\n", + (unsigned long)priv->last_rtc); + + DUMP_VAR(fatal_error, "d"); + DUMP_VAR(stop_hang_check, "d"); + DUMP_VAR(stop_rf_kill, "d"); + DUMP_VAR(messages_sent, "d"); + + DUMP_VAR(tx_pend_stat.value, "d"); + DUMP_VAR(tx_pend_stat.hi, "d"); + + DUMP_VAR(tx_free_stat.value, "d"); + DUMP_VAR(tx_free_stat.lo, "d"); + + DUMP_VAR(msg_free_stat.value, "d"); + DUMP_VAR(msg_free_stat.lo, "d"); + + DUMP_VAR(msg_pend_stat.value, "d"); + DUMP_VAR(msg_pend_stat.hi, "d"); + + DUMP_VAR(fw_pend_stat.value, "d"); + DUMP_VAR(fw_pend_stat.hi, "d"); + + DUMP_VAR(txq_stat.value, "d"); + DUMP_VAR(txq_stat.lo, "d"); + + DUMP_VAR(ieee->scans, "d"); + DUMP_VAR(reset_backoff, "d"); + + return len; +} + +static DEVICE_ATTR(internals, S_IRUGO, show_internals, NULL); + +static ssize_t show_bssinfo(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + char essid[IW_ESSID_MAX_SIZE + 1]; + u8 bssid[ETH_ALEN]; + u32 chan = 0; + char *out = buf; + unsigned int length; + int ret; + + if (priv->status & STATUS_RF_KILL_MASK) + return 0; + + memset(essid, 0, sizeof(essid)); + memset(bssid, 0, sizeof(bssid)); + + length = IW_ESSID_MAX_SIZE; + ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, essid, &length); + if (ret) + IPW_DEBUG_INFO("failed querying ordinals at line %d\n", + __LINE__); + + length = sizeof(bssid); + ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, + bssid, &length); + if (ret) + IPW_DEBUG_INFO("failed querying ordinals at line %d\n", + __LINE__); + + length = sizeof(u32); + ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &length); + if (ret) + IPW_DEBUG_INFO("failed querying ordinals at line %d\n", + __LINE__); + + out += sprintf(out, "ESSID: %s\n", essid); + out += sprintf(out, "BSSID: %pM\n", bssid); + out += sprintf(out, "Channel: %d\n", chan); + + return out - buf; +} + +static DEVICE_ATTR(bssinfo, S_IRUGO, show_bssinfo, NULL); + +#ifdef CONFIG_IPW2100_DEBUG +static ssize_t show_debug_level(struct device_driver *d, char *buf) +{ + return sprintf(buf, "0x%08X\n", ipw2100_debug_level); +} + +static ssize_t store_debug_level(struct device_driver *d, + const char *buf, size_t count) +{ + u32 val; + int ret; + + ret = kstrtou32(buf, 0, &val); + if (ret) + IPW_DEBUG_INFO(": %s is not in hex or decimal form.\n", buf); + else + ipw2100_debug_level = val; + + return strnlen(buf, count); +} + +static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, show_debug_level, + store_debug_level); +#endif /* CONFIG_IPW2100_DEBUG */ + +static ssize_t show_fatal_error(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + char *out = buf; + int i; + + if (priv->fatal_error) + out += sprintf(out, "0x%08X\n", priv->fatal_error); + else + out += sprintf(out, "0\n"); + + for (i = 1; i <= IPW2100_ERROR_QUEUE; i++) { + if (!priv->fatal_errors[(priv->fatal_index - i) % + IPW2100_ERROR_QUEUE]) + continue; + + out += sprintf(out, "%d. 0x%08X\n", i, + priv->fatal_errors[(priv->fatal_index - i) % + IPW2100_ERROR_QUEUE]); + } + + return out - buf; +} + +static ssize_t store_fatal_error(struct device *d, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + schedule_reset(priv); + return count; +} + +static DEVICE_ATTR(fatal_error, S_IWUSR | S_IRUGO, show_fatal_error, + store_fatal_error); + +static ssize_t show_scan_age(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + return sprintf(buf, "%d\n", priv->ieee->scan_age); +} + +static ssize_t store_scan_age(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + struct net_device *dev = priv->net_dev; + unsigned long val; + int ret; + + (void)dev; /* kill unused-var warning for debug-only code */ + + IPW_DEBUG_INFO("enter\n"); + + ret = kstrtoul(buf, 0, &val); + if (ret) { + IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name); + } else { + priv->ieee->scan_age = val; + IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age); + } + + IPW_DEBUG_INFO("exit\n"); + return strnlen(buf, count); +} + +static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age); + +static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr, + char *buf) +{ + /* 0 - RF kill not enabled + 1 - SW based RF kill active (sysfs) + 2 - HW based RF kill active + 3 - Both HW and SW baed RF kill active */ + struct ipw2100_priv *priv = dev_get_drvdata(d); + int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) | + (rf_kill_active(priv) ? 0x2 : 0x0); + return sprintf(buf, "%i\n", val); +} + +static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio) +{ + if ((disable_radio ? 1 : 0) == + (priv->status & STATUS_RF_KILL_SW ? 1 : 0)) + return 0; + + IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n", + disable_radio ? "OFF" : "ON"); + + mutex_lock(&priv->action_mutex); + + if (disable_radio) { + priv->status |= STATUS_RF_KILL_SW; + ipw2100_down(priv); + } else { + priv->status &= ~STATUS_RF_KILL_SW; + if (rf_kill_active(priv)) { + IPW_DEBUG_RF_KILL("Can not turn radio back on - " + "disabled by HW switch\n"); + /* Make sure the RF_KILL check timer is running */ + priv->stop_rf_kill = 0; + mod_delayed_work(system_wq, &priv->rf_kill, + round_jiffies_relative(HZ)); + } else + schedule_reset(priv); + } + + mutex_unlock(&priv->action_mutex); + return 1; +} + +static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw2100_priv *priv = dev_get_drvdata(d); + ipw_radio_kill_sw(priv, buf[0] == '1'); + return count; +} + +static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill); + +static struct attribute *ipw2100_sysfs_entries[] = { + &dev_attr_hardware.attr, + &dev_attr_registers.attr, + &dev_attr_ordinals.attr, + &dev_attr_pci.attr, + &dev_attr_stats.attr, + &dev_attr_internals.attr, + &dev_attr_bssinfo.attr, + &dev_attr_memory.attr, + &dev_attr_scan_age.attr, + &dev_attr_fatal_error.attr, + &dev_attr_rf_kill.attr, + &dev_attr_cfg.attr, + &dev_attr_status.attr, + &dev_attr_capability.attr, + NULL, +}; + +static struct attribute_group ipw2100_attribute_group = { + .attrs = ipw2100_sysfs_entries, +}; + +static int status_queue_allocate(struct ipw2100_priv *priv, int entries) +{ + struct ipw2100_status_queue *q = &priv->status_queue; + + IPW_DEBUG_INFO("enter\n"); + + q->size = entries * sizeof(struct ipw2100_status); + q->drv = pci_zalloc_consistent(priv->pci_dev, q->size, &q->nic); + if (!q->drv) { + IPW_DEBUG_WARNING("Can not allocate status queue.\n"); + return -ENOMEM; + } + + IPW_DEBUG_INFO("exit\n"); + + return 0; +} + +static void status_queue_free(struct ipw2100_priv *priv) +{ + IPW_DEBUG_INFO("enter\n"); + + if (priv->status_queue.drv) { + pci_free_consistent(priv->pci_dev, priv->status_queue.size, + priv->status_queue.drv, + priv->status_queue.nic); + priv->status_queue.drv = NULL; + } + + IPW_DEBUG_INFO("exit\n"); +} + +static int bd_queue_allocate(struct ipw2100_priv *priv, + struct ipw2100_bd_queue *q, int entries) +{ + IPW_DEBUG_INFO("enter\n"); + + memset(q, 0, sizeof(struct ipw2100_bd_queue)); + + q->entries = entries; + q->size = entries * sizeof(struct ipw2100_bd); + q->drv = pci_zalloc_consistent(priv->pci_dev, q->size, &q->nic); + if (!q->drv) { + IPW_DEBUG_INFO + ("can't allocate shared memory for buffer descriptors\n"); + return -ENOMEM; + } + + IPW_DEBUG_INFO("exit\n"); + + return 0; +} + +static void bd_queue_free(struct ipw2100_priv *priv, struct ipw2100_bd_queue *q) +{ + IPW_DEBUG_INFO("enter\n"); + + if (!q) + return; + + if (q->drv) { + pci_free_consistent(priv->pci_dev, q->size, q->drv, q->nic); + q->drv = NULL; + } + + IPW_DEBUG_INFO("exit\n"); +} + +static void bd_queue_initialize(struct ipw2100_priv *priv, + struct ipw2100_bd_queue *q, u32 base, u32 size, + u32 r, u32 w) +{ + IPW_DEBUG_INFO("enter\n"); + + IPW_DEBUG_INFO("initializing bd queue at virt=%p, phys=%08x\n", q->drv, + (u32) q->nic); + + write_register(priv->net_dev, base, q->nic); + write_register(priv->net_dev, size, q->entries); + write_register(priv->net_dev, r, q->oldest); + write_register(priv->net_dev, w, q->next); + + IPW_DEBUG_INFO("exit\n"); +} + +static void ipw2100_kill_works(struct ipw2100_priv *priv) +{ + priv->stop_rf_kill = 1; + priv->stop_hang_check = 1; + cancel_delayed_work_sync(&priv->reset_work); + cancel_delayed_work_sync(&priv->security_work); + cancel_delayed_work_sync(&priv->wx_event_work); + cancel_delayed_work_sync(&priv->hang_check); + cancel_delayed_work_sync(&priv->rf_kill); + cancel_delayed_work_sync(&priv->scan_event); +} + +static int ipw2100_tx_allocate(struct ipw2100_priv *priv) +{ + int i, j, err = -EINVAL; + void *v; + dma_addr_t p; + + IPW_DEBUG_INFO("enter\n"); + + err = bd_queue_allocate(priv, &priv->tx_queue, TX_QUEUE_LENGTH); + if (err) { + IPW_DEBUG_ERROR("%s: failed bd_queue_allocate\n", + priv->net_dev->name); + return err; + } + + priv->tx_buffers = kmalloc_array(TX_PENDED_QUEUE_LENGTH, + sizeof(struct ipw2100_tx_packet), + GFP_ATOMIC); + if (!priv->tx_buffers) { + bd_queue_free(priv, &priv->tx_queue); + return -ENOMEM; + } + + for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) { + v = pci_alloc_consistent(priv->pci_dev, + sizeof(struct ipw2100_data_header), + &p); + if (!v) { + printk(KERN_ERR DRV_NAME + ": %s: PCI alloc failed for tx " "buffers.\n", + priv->net_dev->name); + err = -ENOMEM; + break; + } + + priv->tx_buffers[i].type = DATA; + priv->tx_buffers[i].info.d_struct.data = + (struct ipw2100_data_header *)v; + priv->tx_buffers[i].info.d_struct.data_phys = p; + priv->tx_buffers[i].info.d_struct.txb = NULL; + } + + if (i == TX_PENDED_QUEUE_LENGTH) + return 0; + + for (j = 0; j < i; j++) { + pci_free_consistent(priv->pci_dev, + sizeof(struct ipw2100_data_header), + priv->tx_buffers[j].info.d_struct.data, + priv->tx_buffers[j].info.d_struct. + data_phys); + } + + kfree(priv->tx_buffers); + priv->tx_buffers = NULL; + + return err; +} + +static void ipw2100_tx_initialize(struct ipw2100_priv *priv) +{ + int i; + + IPW_DEBUG_INFO("enter\n"); + + /* + * reinitialize packet info lists + */ + INIT_LIST_HEAD(&priv->fw_pend_list); + INIT_STAT(&priv->fw_pend_stat); + + /* + * reinitialize lists + */ + INIT_LIST_HEAD(&priv->tx_pend_list); + INIT_LIST_HEAD(&priv->tx_free_list); + INIT_STAT(&priv->tx_pend_stat); + INIT_STAT(&priv->tx_free_stat); + + for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) { + /* We simply drop any SKBs that have been queued for + * transmit */ + if (priv->tx_buffers[i].info.d_struct.txb) { + libipw_txb_free(priv->tx_buffers[i].info.d_struct. + txb); + priv->tx_buffers[i].info.d_struct.txb = NULL; + } + + list_add_tail(&priv->tx_buffers[i].list, &priv->tx_free_list); + } + + SET_STAT(&priv->tx_free_stat, i); + + priv->tx_queue.oldest = 0; + priv->tx_queue.available = priv->tx_queue.entries; + priv->tx_queue.next = 0; + INIT_STAT(&priv->txq_stat); + SET_STAT(&priv->txq_stat, priv->tx_queue.available); + + bd_queue_initialize(priv, &priv->tx_queue, + IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE, + IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE, + IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX, + IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX); + + IPW_DEBUG_INFO("exit\n"); + +} + +static void ipw2100_tx_free(struct ipw2100_priv *priv) +{ + int i; + + IPW_DEBUG_INFO("enter\n"); + + bd_queue_free(priv, &priv->tx_queue); + + if (!priv->tx_buffers) + return; + + for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) { + if (priv->tx_buffers[i].info.d_struct.txb) { + libipw_txb_free(priv->tx_buffers[i].info.d_struct. + txb); + priv->tx_buffers[i].info.d_struct.txb = NULL; + } + if (priv->tx_buffers[i].info.d_struct.data) + pci_free_consistent(priv->pci_dev, + sizeof(struct ipw2100_data_header), + priv->tx_buffers[i].info.d_struct. + data, + priv->tx_buffers[i].info.d_struct. + data_phys); + } + + kfree(priv->tx_buffers); + priv->tx_buffers = NULL; + + IPW_DEBUG_INFO("exit\n"); +} + +static int ipw2100_rx_allocate(struct ipw2100_priv *priv) +{ + int i, j, err = -EINVAL; + + IPW_DEBUG_INFO("enter\n"); + + err = bd_queue_allocate(priv, &priv->rx_queue, RX_QUEUE_LENGTH); + if (err) { + IPW_DEBUG_INFO("failed bd_queue_allocate\n"); + return err; + } + + err = status_queue_allocate(priv, RX_QUEUE_LENGTH); + if (err) { + IPW_DEBUG_INFO("failed status_queue_allocate\n"); + bd_queue_free(priv, &priv->rx_queue); + return err; + } + + /* + * allocate packets + */ + priv->rx_buffers = kmalloc(RX_QUEUE_LENGTH * + sizeof(struct ipw2100_rx_packet), + GFP_KERNEL); + if (!priv->rx_buffers) { + IPW_DEBUG_INFO("can't allocate rx packet buffer table\n"); + + bd_queue_free(priv, &priv->rx_queue); + + status_queue_free(priv); + + return -ENOMEM; + } + + for (i = 0; i < RX_QUEUE_LENGTH; i++) { + struct ipw2100_rx_packet *packet = &priv->rx_buffers[i]; + + err = ipw2100_alloc_skb(priv, packet); + if (unlikely(err)) { + err = -ENOMEM; + break; + } + + /* The BD holds the cache aligned address */ + priv->rx_queue.drv[i].host_addr = packet->dma_addr; + priv->rx_queue.drv[i].buf_length = IPW_RX_NIC_BUFFER_LENGTH; + priv->status_queue.drv[i].status_fields = 0; + } + + if (i == RX_QUEUE_LENGTH) + return 0; + + for (j = 0; j < i; j++) { + pci_unmap_single(priv->pci_dev, priv->rx_buffers[j].dma_addr, + sizeof(struct ipw2100_rx_packet), + PCI_DMA_FROMDEVICE); + dev_kfree_skb(priv->rx_buffers[j].skb); + } + + kfree(priv->rx_buffers); + priv->rx_buffers = NULL; + + bd_queue_free(priv, &priv->rx_queue); + + status_queue_free(priv); + + return err; +} + +static void ipw2100_rx_initialize(struct ipw2100_priv *priv) +{ + IPW_DEBUG_INFO("enter\n"); + + priv->rx_queue.oldest = 0; + priv->rx_queue.available = priv->rx_queue.entries - 1; + priv->rx_queue.next = priv->rx_queue.entries - 1; + + INIT_STAT(&priv->rxq_stat); + SET_STAT(&priv->rxq_stat, priv->rx_queue.available); + + bd_queue_initialize(priv, &priv->rx_queue, + IPW_MEM_HOST_SHARED_RX_BD_BASE, + IPW_MEM_HOST_SHARED_RX_BD_SIZE, + IPW_MEM_HOST_SHARED_RX_READ_INDEX, + IPW_MEM_HOST_SHARED_RX_WRITE_INDEX); + + /* set up the status queue */ + write_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_STATUS_BASE, + priv->status_queue.nic); + + IPW_DEBUG_INFO("exit\n"); +} + +static void ipw2100_rx_free(struct ipw2100_priv *priv) +{ + int i; + + IPW_DEBUG_INFO("enter\n"); + + bd_queue_free(priv, &priv->rx_queue); + status_queue_free(priv); + + if (!priv->rx_buffers) + return; + + for (i = 0; i < RX_QUEUE_LENGTH; i++) { + if (priv->rx_buffers[i].rxp) { + pci_unmap_single(priv->pci_dev, + priv->rx_buffers[i].dma_addr, + sizeof(struct ipw2100_rx), + PCI_DMA_FROMDEVICE); + dev_kfree_skb(priv->rx_buffers[i].skb); + } + } + + kfree(priv->rx_buffers); + priv->rx_buffers = NULL; + + IPW_DEBUG_INFO("exit\n"); +} + +static int ipw2100_read_mac_address(struct ipw2100_priv *priv) +{ + u32 length = ETH_ALEN; + u8 addr[ETH_ALEN]; + + int err; + + err = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ADAPTER_MAC, addr, &length); + if (err) { + IPW_DEBUG_INFO("MAC address read failed\n"); + return -EIO; + } + + memcpy(priv->net_dev->dev_addr, addr, ETH_ALEN); + IPW_DEBUG_INFO("card MAC is %pM\n", priv->net_dev->dev_addr); + + return 0; +} + +/******************************************************************** + * + * Firmware Commands + * + ********************************************************************/ + +static int ipw2100_set_mac_address(struct ipw2100_priv *priv, int batch_mode) +{ + struct host_command cmd = { + .host_command = ADAPTER_ADDRESS, + .host_command_sequence = 0, + .host_command_length = ETH_ALEN + }; + int err; + + IPW_DEBUG_HC("SET_MAC_ADDRESS\n"); + + IPW_DEBUG_INFO("enter\n"); + + if (priv->config & CFG_CUSTOM_MAC) { + memcpy(cmd.host_command_parameters, priv->mac_addr, ETH_ALEN); + memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN); + } else + memcpy(cmd.host_command_parameters, priv->net_dev->dev_addr, + ETH_ALEN); + + err = ipw2100_hw_send_command(priv, &cmd); + + IPW_DEBUG_INFO("exit\n"); + return err; +} + +static int ipw2100_set_port_type(struct ipw2100_priv *priv, u32 port_type, + int batch_mode) +{ + struct host_command cmd = { + .host_command = PORT_TYPE, + .host_command_sequence = 0, + .host_command_length = sizeof(u32) + }; + int err; + + switch (port_type) { + case IW_MODE_INFRA: + cmd.host_command_parameters[0] = IPW_BSS; + break; + case IW_MODE_ADHOC: + cmd.host_command_parameters[0] = IPW_IBSS; + break; + } + + IPW_DEBUG_HC("PORT_TYPE: %s\n", + port_type == IPW_IBSS ? "Ad-Hoc" : "Managed"); + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) { + printk(KERN_ERR DRV_NAME + ": %s: Could not disable adapter %d\n", + priv->net_dev->name, err); + return err; + } + } + + /* send cmd to firmware */ + err = ipw2100_hw_send_command(priv, &cmd); + + if (!batch_mode) + ipw2100_enable_adapter(priv); + + return err; +} + +static int ipw2100_set_channel(struct ipw2100_priv *priv, u32 channel, + int batch_mode) +{ + struct host_command cmd = { + .host_command = CHANNEL, + .host_command_sequence = 0, + .host_command_length = sizeof(u32) + }; + int err; + + cmd.host_command_parameters[0] = channel; + + IPW_DEBUG_HC("CHANNEL: %d\n", channel); + + /* If BSS then we don't support channel selection */ + if (priv->ieee->iw_mode == IW_MODE_INFRA) + return 0; + + if ((channel != 0) && + ((channel < REG_MIN_CHANNEL) || (channel > REG_MAX_CHANNEL))) + return -EINVAL; + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) + return err; + } + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) { + IPW_DEBUG_INFO("Failed to set channel to %d", channel); + return err; + } + + if (channel) + priv->config |= CFG_STATIC_CHANNEL; + else + priv->config &= ~CFG_STATIC_CHANNEL; + + priv->channel = channel; + + if (!batch_mode) { + err = ipw2100_enable_adapter(priv); + if (err) + return err; + } + + return 0; +} + +static int ipw2100_system_config(struct ipw2100_priv *priv, int batch_mode) +{ + struct host_command cmd = { + .host_command = SYSTEM_CONFIG, + .host_command_sequence = 0, + .host_command_length = 12, + }; + u32 ibss_mask, len = sizeof(u32); + int err; + + /* Set system configuration */ + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) + return err; + } + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) + cmd.host_command_parameters[0] |= IPW_CFG_IBSS_AUTO_START; + + cmd.host_command_parameters[0] |= IPW_CFG_IBSS_MASK | + IPW_CFG_BSS_MASK | IPW_CFG_802_1x_ENABLE; + + if (!(priv->config & CFG_LONG_PREAMBLE)) + cmd.host_command_parameters[0] |= IPW_CFG_PREAMBLE_AUTO; + + err = ipw2100_get_ordinal(priv, + IPW_ORD_EEPROM_IBSS_11B_CHANNELS, + &ibss_mask, &len); + if (err) + ibss_mask = IPW_IBSS_11B_DEFAULT_MASK; + + cmd.host_command_parameters[1] = REG_CHANNEL_MASK; + cmd.host_command_parameters[2] = REG_CHANNEL_MASK & ibss_mask; + + /* 11b only */ + /*cmd.host_command_parameters[0] |= DIVERSITY_ANTENNA_A; */ + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) + return err; + +/* If IPv6 is configured in the kernel then we don't want to filter out all + * of the multicast packets as IPv6 needs some. */ +#if !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) + cmd.host_command = ADD_MULTICAST; + cmd.host_command_sequence = 0; + cmd.host_command_length = 0; + + ipw2100_hw_send_command(priv, &cmd); +#endif + if (!batch_mode) { + err = ipw2100_enable_adapter(priv); + if (err) + return err; + } + + return 0; +} + +static int ipw2100_set_tx_rates(struct ipw2100_priv *priv, u32 rate, + int batch_mode) +{ + struct host_command cmd = { + .host_command = BASIC_TX_RATES, + .host_command_sequence = 0, + .host_command_length = 4 + }; + int err; + + cmd.host_command_parameters[0] = rate & TX_RATE_MASK; + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) + return err; + } + + /* Set BASIC TX Rate first */ + ipw2100_hw_send_command(priv, &cmd); + + /* Set TX Rate */ + cmd.host_command = TX_RATES; + ipw2100_hw_send_command(priv, &cmd); + + /* Set MSDU TX Rate */ + cmd.host_command = MSDU_TX_RATES; + ipw2100_hw_send_command(priv, &cmd); + + if (!batch_mode) { + err = ipw2100_enable_adapter(priv); + if (err) + return err; + } + + priv->tx_rates = rate; + + return 0; +} + +static int ipw2100_set_power_mode(struct ipw2100_priv *priv, int power_level) +{ + struct host_command cmd = { + .host_command = POWER_MODE, + .host_command_sequence = 0, + .host_command_length = 4 + }; + int err; + + cmd.host_command_parameters[0] = power_level; + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) + return err; + + if (power_level == IPW_POWER_MODE_CAM) + priv->power_mode = IPW_POWER_LEVEL(priv->power_mode); + else + priv->power_mode = IPW_POWER_ENABLED | power_level; + +#ifdef IPW2100_TX_POWER + if (priv->port_type == IBSS && priv->adhoc_power != DFTL_IBSS_TX_POWER) { + /* Set beacon interval */ + cmd.host_command = TX_POWER_INDEX; + cmd.host_command_parameters[0] = (u32) priv->adhoc_power; + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) + return err; + } +#endif + + return 0; +} + +static int ipw2100_set_rts_threshold(struct ipw2100_priv *priv, u32 threshold) +{ + struct host_command cmd = { + .host_command = RTS_THRESHOLD, + .host_command_sequence = 0, + .host_command_length = 4 + }; + int err; + + if (threshold & RTS_DISABLED) + cmd.host_command_parameters[0] = MAX_RTS_THRESHOLD; + else + cmd.host_command_parameters[0] = threshold & ~RTS_DISABLED; + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) + return err; + + priv->rts_threshold = threshold; + + return 0; +} + +#if 0 +int ipw2100_set_fragmentation_threshold(struct ipw2100_priv *priv, + u32 threshold, int batch_mode) +{ + struct host_command cmd = { + .host_command = FRAG_THRESHOLD, + .host_command_sequence = 0, + .host_command_length = 4, + .host_command_parameters[0] = 0, + }; + int err; + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) + return err; + } + + if (threshold == 0) + threshold = DEFAULT_FRAG_THRESHOLD; + else { + threshold = max(threshold, MIN_FRAG_THRESHOLD); + threshold = min(threshold, MAX_FRAG_THRESHOLD); + } + + cmd.host_command_parameters[0] = threshold; + + IPW_DEBUG_HC("FRAG_THRESHOLD: %u\n", threshold); + + err = ipw2100_hw_send_command(priv, &cmd); + + if (!batch_mode) + ipw2100_enable_adapter(priv); + + if (!err) + priv->frag_threshold = threshold; + + return err; +} +#endif + +static int ipw2100_set_short_retry(struct ipw2100_priv *priv, u32 retry) +{ + struct host_command cmd = { + .host_command = SHORT_RETRY_LIMIT, + .host_command_sequence = 0, + .host_command_length = 4 + }; + int err; + + cmd.host_command_parameters[0] = retry; + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) + return err; + + priv->short_retry_limit = retry; + + return 0; +} + +static int ipw2100_set_long_retry(struct ipw2100_priv *priv, u32 retry) +{ + struct host_command cmd = { + .host_command = LONG_RETRY_LIMIT, + .host_command_sequence = 0, + .host_command_length = 4 + }; + int err; + + cmd.host_command_parameters[0] = retry; + + err = ipw2100_hw_send_command(priv, &cmd); + if (err) + return err; + + priv->long_retry_limit = retry; + + return 0; +} + +static int ipw2100_set_mandatory_bssid(struct ipw2100_priv *priv, u8 * bssid, + int batch_mode) +{ + struct host_command cmd = { + .host_command = MANDATORY_BSSID, + .host_command_sequence = 0, + .host_command_length = (bssid == NULL) ? 0 : ETH_ALEN + }; + int err; + +#ifdef CONFIG_IPW2100_DEBUG + if (bssid != NULL) + IPW_DEBUG_HC("MANDATORY_BSSID: %pM\n", bssid); + else + IPW_DEBUG_HC("MANDATORY_BSSID: \n"); +#endif + /* if BSSID is empty then we disable mandatory bssid mode */ + if (bssid != NULL) + memcpy(cmd.host_command_parameters, bssid, ETH_ALEN); + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) + return err; + } + + err = ipw2100_hw_send_command(priv, &cmd); + + if (!batch_mode) + ipw2100_enable_adapter(priv); + + return err; +} + +static int ipw2100_disassociate_bssid(struct ipw2100_priv *priv) +{ + struct host_command cmd = { + .host_command = DISASSOCIATION_BSSID, + .host_command_sequence = 0, + .host_command_length = ETH_ALEN + }; + int err; + int len; + + IPW_DEBUG_HC("DISASSOCIATION_BSSID\n"); + + len = ETH_ALEN; + /* The Firmware currently ignores the BSSID and just disassociates from + * the currently associated AP -- but in the off chance that a future + * firmware does use the BSSID provided here, we go ahead and try and + * set it to the currently associated AP's BSSID */ + memcpy(cmd.host_command_parameters, priv->bssid, ETH_ALEN); + + err = ipw2100_hw_send_command(priv, &cmd); + + return err; +} + +static int ipw2100_set_wpa_ie(struct ipw2100_priv *, + struct ipw2100_wpa_assoc_frame *, int) + __attribute__ ((unused)); + +static int ipw2100_set_wpa_ie(struct ipw2100_priv *priv, + struct ipw2100_wpa_assoc_frame *wpa_frame, + int batch_mode) +{ + struct host_command cmd = { + .host_command = SET_WPA_IE, + .host_command_sequence = 0, + .host_command_length = sizeof(struct ipw2100_wpa_assoc_frame), + }; + int err; + + IPW_DEBUG_HC("SET_WPA_IE\n"); + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) + return err; + } + + memcpy(cmd.host_command_parameters, wpa_frame, + sizeof(struct ipw2100_wpa_assoc_frame)); + + err = ipw2100_hw_send_command(priv, &cmd); + + if (!batch_mode) { + if (ipw2100_enable_adapter(priv)) + err = -EIO; + } + + return err; +} + +struct security_info_params { + u32 allowed_ciphers; + u16 version; + u8 auth_mode; + u8 replay_counters_number; + u8 unicast_using_group; +} __packed; + +static int ipw2100_set_security_information(struct ipw2100_priv *priv, + int auth_mode, + int security_level, + int unicast_using_group, + int batch_mode) +{ + struct host_command cmd = { + .host_command = SET_SECURITY_INFORMATION, + .host_command_sequence = 0, + .host_command_length = sizeof(struct security_info_params) + }; + struct security_info_params *security = + (struct security_info_params *)&cmd.host_command_parameters; + int err; + memset(security, 0, sizeof(*security)); + + /* If shared key AP authentication is turned on, then we need to + * configure the firmware to try and use it. + * + * Actual data encryption/decryption is handled by the host. */ + security->auth_mode = auth_mode; + security->unicast_using_group = unicast_using_group; + + switch (security_level) { + default: + case SEC_LEVEL_0: + security->allowed_ciphers = IPW_NONE_CIPHER; + break; + case SEC_LEVEL_1: + security->allowed_ciphers = IPW_WEP40_CIPHER | + IPW_WEP104_CIPHER; + break; + case SEC_LEVEL_2: + security->allowed_ciphers = IPW_WEP40_CIPHER | + IPW_WEP104_CIPHER | IPW_TKIP_CIPHER; + break; + case SEC_LEVEL_2_CKIP: + security->allowed_ciphers = IPW_WEP40_CIPHER | + IPW_WEP104_CIPHER | IPW_CKIP_CIPHER; + break; + case SEC_LEVEL_3: + security->allowed_ciphers = IPW_WEP40_CIPHER | + IPW_WEP104_CIPHER | IPW_TKIP_CIPHER | IPW_CCMP_CIPHER; + break; + } + + IPW_DEBUG_HC + ("SET_SECURITY_INFORMATION: auth:%d cipher:0x%02X (level %d)\n", + security->auth_mode, security->allowed_ciphers, security_level); + + security->replay_counters_number = 0; + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) + return err; + } + + err = ipw2100_hw_send_command(priv, &cmd); + + if (!batch_mode) + ipw2100_enable_adapter(priv); + + return err; +} + +static int ipw2100_set_tx_power(struct ipw2100_priv *priv, u32 tx_power) +{ + struct host_command cmd = { + .host_command = TX_POWER_INDEX, + .host_command_sequence = 0, + .host_command_length = 4 + }; + int err = 0; + u32 tmp = tx_power; + + if (tx_power != IPW_TX_POWER_DEFAULT) + tmp = (tx_power - IPW_TX_POWER_MIN_DBM) * 16 / + (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM); + + cmd.host_command_parameters[0] = tmp; + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) + err = ipw2100_hw_send_command(priv, &cmd); + if (!err) + priv->tx_power = tx_power; + + return 0; +} + +static int ipw2100_set_ibss_beacon_interval(struct ipw2100_priv *priv, + u32 interval, int batch_mode) +{ + struct host_command cmd = { + .host_command = BEACON_INTERVAL, + .host_command_sequence = 0, + .host_command_length = 4 + }; + int err; + + cmd.host_command_parameters[0] = interval; + + IPW_DEBUG_INFO("enter\n"); + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) { + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) + return err; + } + + ipw2100_hw_send_command(priv, &cmd); + + if (!batch_mode) { + err = ipw2100_enable_adapter(priv); + if (err) + return err; + } + } + + IPW_DEBUG_INFO("exit\n"); + + return 0; +} + +static void ipw2100_queues_initialize(struct ipw2100_priv *priv) +{ + ipw2100_tx_initialize(priv); + ipw2100_rx_initialize(priv); + ipw2100_msg_initialize(priv); +} + +static void ipw2100_queues_free(struct ipw2100_priv *priv) +{ + ipw2100_tx_free(priv); + ipw2100_rx_free(priv); + ipw2100_msg_free(priv); +} + +static int ipw2100_queues_allocate(struct ipw2100_priv *priv) +{ + if (ipw2100_tx_allocate(priv) || + ipw2100_rx_allocate(priv) || ipw2100_msg_allocate(priv)) + goto fail; + + return 0; + + fail: + ipw2100_tx_free(priv); + ipw2100_rx_free(priv); + ipw2100_msg_free(priv); + return -ENOMEM; +} + +#define IPW_PRIVACY_CAPABLE 0x0008 + +static int ipw2100_set_wep_flags(struct ipw2100_priv *priv, u32 flags, + int batch_mode) +{ + struct host_command cmd = { + .host_command = WEP_FLAGS, + .host_command_sequence = 0, + .host_command_length = 4 + }; + int err; + + cmd.host_command_parameters[0] = flags; + + IPW_DEBUG_HC("WEP_FLAGS: flags = 0x%08X\n", flags); + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) { + printk(KERN_ERR DRV_NAME + ": %s: Could not disable adapter %d\n", + priv->net_dev->name, err); + return err; + } + } + + /* send cmd to firmware */ + err = ipw2100_hw_send_command(priv, &cmd); + + if (!batch_mode) + ipw2100_enable_adapter(priv); + + return err; +} + +struct ipw2100_wep_key { + u8 idx; + u8 len; + u8 key[13]; +}; + +/* Macros to ease up priting WEP keys */ +#define WEP_FMT_64 "%02X%02X%02X%02X-%02X" +#define WEP_FMT_128 "%02X%02X%02X%02X-%02X%02X%02X%02X-%02X%02X%02X" +#define WEP_STR_64(x) x[0],x[1],x[2],x[3],x[4] +#define WEP_STR_128(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10] + +/** + * Set a the wep key + * + * @priv: struct to work on + * @idx: index of the key we want to set + * @key: ptr to the key data to set + * @len: length of the buffer at @key + * @batch_mode: FIXME perform the operation in batch mode, not + * disabling the device. + * + * @returns 0 if OK, < 0 errno code on error. + * + * Fill out a command structure with the new wep key, length an + * index and send it down the wire. + */ +static int ipw2100_set_key(struct ipw2100_priv *priv, + int idx, char *key, int len, int batch_mode) +{ + int keylen = len ? (len <= 5 ? 5 : 13) : 0; + struct host_command cmd = { + .host_command = WEP_KEY_INFO, + .host_command_sequence = 0, + .host_command_length = sizeof(struct ipw2100_wep_key), + }; + struct ipw2100_wep_key *wep_key = (void *)cmd.host_command_parameters; + int err; + + IPW_DEBUG_HC("WEP_KEY_INFO: index = %d, len = %d/%d\n", + idx, keylen, len); + + /* NOTE: We don't check cached values in case the firmware was reset + * or some other problem is occurring. If the user is setting the key, + * then we push the change */ + + wep_key->idx = idx; + wep_key->len = keylen; + + if (keylen) { + memcpy(wep_key->key, key, len); + memset(wep_key->key + len, 0, keylen - len); + } + + /* Will be optimized out on debug not being configured in */ + if (keylen == 0) + IPW_DEBUG_WEP("%s: Clearing key %d\n", + priv->net_dev->name, wep_key->idx); + else if (keylen == 5) + IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_64 "\n", + priv->net_dev->name, wep_key->idx, wep_key->len, + WEP_STR_64(wep_key->key)); + else + IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_128 + "\n", + priv->net_dev->name, wep_key->idx, wep_key->len, + WEP_STR_128(wep_key->key)); + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + /* FIXME: IPG: shouldn't this prink be in _disable_adapter()? */ + if (err) { + printk(KERN_ERR DRV_NAME + ": %s: Could not disable adapter %d\n", + priv->net_dev->name, err); + return err; + } + } + + /* send cmd to firmware */ + err = ipw2100_hw_send_command(priv, &cmd); + + if (!batch_mode) { + int err2 = ipw2100_enable_adapter(priv); + if (err == 0) + err = err2; + } + return err; +} + +static int ipw2100_set_key_index(struct ipw2100_priv *priv, + int idx, int batch_mode) +{ + struct host_command cmd = { + .host_command = WEP_KEY_INDEX, + .host_command_sequence = 0, + .host_command_length = 4, + .host_command_parameters = {idx}, + }; + int err; + + IPW_DEBUG_HC("WEP_KEY_INDEX: index = %d\n", idx); + + if (idx < 0 || idx > 3) + return -EINVAL; + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) { + printk(KERN_ERR DRV_NAME + ": %s: Could not disable adapter %d\n", + priv->net_dev->name, err); + return err; + } + } + + /* send cmd to firmware */ + err = ipw2100_hw_send_command(priv, &cmd); + + if (!batch_mode) + ipw2100_enable_adapter(priv); + + return err; +} + +static int ipw2100_configure_security(struct ipw2100_priv *priv, int batch_mode) +{ + int i, err, auth_mode, sec_level, use_group; + + if (!(priv->status & STATUS_RUNNING)) + return 0; + + if (!batch_mode) { + err = ipw2100_disable_adapter(priv); + if (err) + return err; + } + + if (!priv->ieee->sec.enabled) { + err = + ipw2100_set_security_information(priv, IPW_AUTH_OPEN, + SEC_LEVEL_0, 0, 1); + } else { + auth_mode = IPW_AUTH_OPEN; + if (priv->ieee->sec.flags & SEC_AUTH_MODE) { + if (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY) + auth_mode = IPW_AUTH_SHARED; + else if (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP) + auth_mode = IPW_AUTH_LEAP_CISCO_ID; + } + + sec_level = SEC_LEVEL_0; + if (priv->ieee->sec.flags & SEC_LEVEL) + sec_level = priv->ieee->sec.level; + + use_group = 0; + if (priv->ieee->sec.flags & SEC_UNICAST_GROUP) + use_group = priv->ieee->sec.unicast_uses_group; + + err = + ipw2100_set_security_information(priv, auth_mode, sec_level, + use_group, 1); + } + + if (err) + goto exit; + + if (priv->ieee->sec.enabled) { + for (i = 0; i < 4; i++) { + if (!(priv->ieee->sec.flags & (1 << i))) { + memset(priv->ieee->sec.keys[i], 0, WEP_KEY_LEN); + priv->ieee->sec.key_sizes[i] = 0; + } else { + err = ipw2100_set_key(priv, i, + priv->ieee->sec.keys[i], + priv->ieee->sec. + key_sizes[i], 1); + if (err) + goto exit; + } + } + + ipw2100_set_key_index(priv, priv->ieee->crypt_info.tx_keyidx, 1); + } + + /* Always enable privacy so the Host can filter WEP packets if + * encrypted data is sent up */ + err = + ipw2100_set_wep_flags(priv, + priv->ieee->sec. + enabled ? IPW_PRIVACY_CAPABLE : 0, 1); + if (err) + goto exit; + + priv->status &= ~STATUS_SECURITY_UPDATED; + + exit: + if (!batch_mode) + ipw2100_enable_adapter(priv); + + return err; +} + +static void ipw2100_security_work(struct work_struct *work) +{ + struct ipw2100_priv *priv = + container_of(work, struct ipw2100_priv, security_work.work); + + /* If we happen to have reconnected before we get a chance to + * process this, then update the security settings--which causes + * a disassociation to occur */ + if (!(priv->status & STATUS_ASSOCIATED) && + priv->status & STATUS_SECURITY_UPDATED) + ipw2100_configure_security(priv, 0); +} + +static void shim__set_security(struct net_device *dev, + struct libipw_security *sec) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int i, force_update = 0; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) + goto done; + + for (i = 0; i < 4; i++) { + if (sec->flags & (1 << i)) { + priv->ieee->sec.key_sizes[i] = sec->key_sizes[i]; + if (sec->key_sizes[i] == 0) + priv->ieee->sec.flags &= ~(1 << i); + else + memcpy(priv->ieee->sec.keys[i], sec->keys[i], + sec->key_sizes[i]); + if (sec->level == SEC_LEVEL_1) { + priv->ieee->sec.flags |= (1 << i); + priv->status |= STATUS_SECURITY_UPDATED; + } else + priv->ieee->sec.flags &= ~(1 << i); + } + } + + if ((sec->flags & SEC_ACTIVE_KEY) && + priv->ieee->sec.active_key != sec->active_key) { + if (sec->active_key <= 3) { + priv->ieee->sec.active_key = sec->active_key; + priv->ieee->sec.flags |= SEC_ACTIVE_KEY; + } else + priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY; + + priv->status |= STATUS_SECURITY_UPDATED; + } + + if ((sec->flags & SEC_AUTH_MODE) && + (priv->ieee->sec.auth_mode != sec->auth_mode)) { + priv->ieee->sec.auth_mode = sec->auth_mode; + priv->ieee->sec.flags |= SEC_AUTH_MODE; + priv->status |= STATUS_SECURITY_UPDATED; + } + + if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) { + priv->ieee->sec.flags |= SEC_ENABLED; + priv->ieee->sec.enabled = sec->enabled; + priv->status |= STATUS_SECURITY_UPDATED; + force_update = 1; + } + + if (sec->flags & SEC_ENCRYPT) + priv->ieee->sec.encrypt = sec->encrypt; + + if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) { + priv->ieee->sec.level = sec->level; + priv->ieee->sec.flags |= SEC_LEVEL; + priv->status |= STATUS_SECURITY_UPDATED; + } + + IPW_DEBUG_WEP("Security flags: %c %c%c%c%c %c%c%c%c\n", + priv->ieee->sec.flags & (1 << 8) ? '1' : '0', + priv->ieee->sec.flags & (1 << 7) ? '1' : '0', + priv->ieee->sec.flags & (1 << 6) ? '1' : '0', + priv->ieee->sec.flags & (1 << 5) ? '1' : '0', + priv->ieee->sec.flags & (1 << 4) ? '1' : '0', + priv->ieee->sec.flags & (1 << 3) ? '1' : '0', + priv->ieee->sec.flags & (1 << 2) ? '1' : '0', + priv->ieee->sec.flags & (1 << 1) ? '1' : '0', + priv->ieee->sec.flags & (1 << 0) ? '1' : '0'); + +/* As a temporary work around to enable WPA until we figure out why + * wpa_supplicant toggles the security capability of the driver, which + * forces a disassocation with force_update... + * + * if (force_update || !(priv->status & STATUS_ASSOCIATED))*/ + if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) + ipw2100_configure_security(priv, 0); + done: + mutex_unlock(&priv->action_mutex); +} + +static int ipw2100_adapter_setup(struct ipw2100_priv *priv) +{ + int err; + int batch_mode = 1; + u8 *bssid; + + IPW_DEBUG_INFO("enter\n"); + + err = ipw2100_disable_adapter(priv); + if (err) + return err; +#ifdef CONFIG_IPW2100_MONITOR + if (priv->ieee->iw_mode == IW_MODE_MONITOR) { + err = ipw2100_set_channel(priv, priv->channel, batch_mode); + if (err) + return err; + + IPW_DEBUG_INFO("exit\n"); + + return 0; + } +#endif /* CONFIG_IPW2100_MONITOR */ + + err = ipw2100_read_mac_address(priv); + if (err) + return -EIO; + + err = ipw2100_set_mac_address(priv, batch_mode); + if (err) + return err; + + err = ipw2100_set_port_type(priv, priv->ieee->iw_mode, batch_mode); + if (err) + return err; + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) { + err = ipw2100_set_channel(priv, priv->channel, batch_mode); + if (err) + return err; + } + + err = ipw2100_system_config(priv, batch_mode); + if (err) + return err; + + err = ipw2100_set_tx_rates(priv, priv->tx_rates, batch_mode); + if (err) + return err; + + /* Default to power mode OFF */ + err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM); + if (err) + return err; + + err = ipw2100_set_rts_threshold(priv, priv->rts_threshold); + if (err) + return err; + + if (priv->config & CFG_STATIC_BSSID) + bssid = priv->bssid; + else + bssid = NULL; + err = ipw2100_set_mandatory_bssid(priv, bssid, batch_mode); + if (err) + return err; + + if (priv->config & CFG_STATIC_ESSID) + err = ipw2100_set_essid(priv, priv->essid, priv->essid_len, + batch_mode); + else + err = ipw2100_set_essid(priv, NULL, 0, batch_mode); + if (err) + return err; + + err = ipw2100_configure_security(priv, batch_mode); + if (err) + return err; + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) { + err = + ipw2100_set_ibss_beacon_interval(priv, + priv->beacon_interval, + batch_mode); + if (err) + return err; + + err = ipw2100_set_tx_power(priv, priv->tx_power); + if (err) + return err; + } + + /* + err = ipw2100_set_fragmentation_threshold( + priv, priv->frag_threshold, batch_mode); + if (err) + return err; + */ + + IPW_DEBUG_INFO("exit\n"); + + return 0; +} + +/************************************************************************* + * + * EXTERNALLY CALLED METHODS + * + *************************************************************************/ + +/* This method is called by the network layer -- not to be confused with + * ipw2100_set_mac_address() declared above called by this driver (and this + * method as well) to talk to the firmware */ +static int ipw2100_set_address(struct net_device *dev, void *p) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + struct sockaddr *addr = p; + int err = 0; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + mutex_lock(&priv->action_mutex); + + priv->config |= CFG_CUSTOM_MAC; + memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN); + + err = ipw2100_set_mac_address(priv, 0); + if (err) + goto done; + + priv->reset_backoff = 0; + mutex_unlock(&priv->action_mutex); + ipw2100_reset_adapter(&priv->reset_work.work); + return 0; + + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_open(struct net_device *dev) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + unsigned long flags; + IPW_DEBUG_INFO("dev->open\n"); + + spin_lock_irqsave(&priv->low_lock, flags); + if (priv->status & STATUS_ASSOCIATED) { + netif_carrier_on(dev); + netif_start_queue(dev); + } + spin_unlock_irqrestore(&priv->low_lock, flags); + + return 0; +} + +static int ipw2100_close(struct net_device *dev) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + unsigned long flags; + struct list_head *element; + struct ipw2100_tx_packet *packet; + + IPW_DEBUG_INFO("enter\n"); + + spin_lock_irqsave(&priv->low_lock, flags); + + if (priv->status & STATUS_ASSOCIATED) + netif_carrier_off(dev); + netif_stop_queue(dev); + + /* Flush the TX queue ... */ + while (!list_empty(&priv->tx_pend_list)) { + element = priv->tx_pend_list.next; + packet = list_entry(element, struct ipw2100_tx_packet, list); + + list_del(element); + DEC_STAT(&priv->tx_pend_stat); + + libipw_txb_free(packet->info.d_struct.txb); + packet->info.d_struct.txb = NULL; + + list_add_tail(element, &priv->tx_free_list); + INC_STAT(&priv->tx_free_stat); + } + spin_unlock_irqrestore(&priv->low_lock, flags); + + IPW_DEBUG_INFO("exit\n"); + + return 0; +} + +/* + * TODO: Fix this function... its just wrong + */ +static void ipw2100_tx_timeout(struct net_device *dev) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + + dev->stats.tx_errors++; + +#ifdef CONFIG_IPW2100_MONITOR + if (priv->ieee->iw_mode == IW_MODE_MONITOR) + return; +#endif + + IPW_DEBUG_INFO("%s: TX timed out. Scheduling firmware restart.\n", + dev->name); + schedule_reset(priv); +} + +static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value) +{ + /* This is called when wpa_supplicant loads and closes the driver + * interface. */ + priv->ieee->wpa_enabled = value; + return 0; +} + +static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value) +{ + + struct libipw_device *ieee = priv->ieee; + struct libipw_security sec = { + .flags = SEC_AUTH_MODE, + }; + int ret = 0; + + if (value & IW_AUTH_ALG_SHARED_KEY) { + sec.auth_mode = WLAN_AUTH_SHARED_KEY; + ieee->open_wep = 0; + } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) { + sec.auth_mode = WLAN_AUTH_OPEN; + ieee->open_wep = 1; + } else if (value & IW_AUTH_ALG_LEAP) { + sec.auth_mode = WLAN_AUTH_LEAP; + ieee->open_wep = 1; + } else + return -EINVAL; + + if (ieee->set_security) + ieee->set_security(ieee->dev, &sec); + else + ret = -EOPNOTSUPP; + + return ret; +} + +static void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv, + char *wpa_ie, int wpa_ie_len) +{ + + struct ipw2100_wpa_assoc_frame frame; + + frame.fixed_ie_mask = 0; + + /* copy WPA IE */ + memcpy(frame.var_ie, wpa_ie, wpa_ie_len); + frame.var_ie_len = wpa_ie_len; + + /* make sure WPA is enabled */ + ipw2100_wpa_enable(priv, 1); + ipw2100_set_wpa_ie(priv, &frame, 0); +} + +static void ipw_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + char fw_ver[64], ucode_ver[64]; + + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + + ipw2100_get_fwversion(priv, fw_ver, sizeof(fw_ver)); + ipw2100_get_ucodeversion(priv, ucode_ver, sizeof(ucode_ver)); + + snprintf(info->fw_version, sizeof(info->fw_version), "%s:%d:%s", + fw_ver, priv->eeprom_version, ucode_ver); + + strlcpy(info->bus_info, pci_name(priv->pci_dev), + sizeof(info->bus_info)); +} + +static u32 ipw2100_ethtool_get_link(struct net_device *dev) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + return (priv->status & STATUS_ASSOCIATED) ? 1 : 0; +} + +static const struct ethtool_ops ipw2100_ethtool_ops = { + .get_link = ipw2100_ethtool_get_link, + .get_drvinfo = ipw_ethtool_get_drvinfo, +}; + +static void ipw2100_hang_check(struct work_struct *work) +{ + struct ipw2100_priv *priv = + container_of(work, struct ipw2100_priv, hang_check.work); + unsigned long flags; + u32 rtc = 0xa5a5a5a5; + u32 len = sizeof(rtc); + int restart = 0; + + spin_lock_irqsave(&priv->low_lock, flags); + + if (priv->fatal_error != 0) { + /* If fatal_error is set then we need to restart */ + IPW_DEBUG_INFO("%s: Hardware fatal error detected.\n", + priv->net_dev->name); + + restart = 1; + } else if (ipw2100_get_ordinal(priv, IPW_ORD_RTC_TIME, &rtc, &len) || + (rtc == priv->last_rtc)) { + /* Check if firmware is hung */ + IPW_DEBUG_INFO("%s: Firmware RTC stalled.\n", + priv->net_dev->name); + + restart = 1; + } + + if (restart) { + /* Kill timer */ + priv->stop_hang_check = 1; + priv->hangs++; + + /* Restart the NIC */ + schedule_reset(priv); + } + + priv->last_rtc = rtc; + + if (!priv->stop_hang_check) + schedule_delayed_work(&priv->hang_check, HZ / 2); + + spin_unlock_irqrestore(&priv->low_lock, flags); +} + +static void ipw2100_rf_kill(struct work_struct *work) +{ + struct ipw2100_priv *priv = + container_of(work, struct ipw2100_priv, rf_kill.work); + unsigned long flags; + + spin_lock_irqsave(&priv->low_lock, flags); + + if (rf_kill_active(priv)) { + IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n"); + if (!priv->stop_rf_kill) + schedule_delayed_work(&priv->rf_kill, + round_jiffies_relative(HZ)); + goto exit_unlock; + } + + /* RF Kill is now disabled, so bring the device back up */ + + if (!(priv->status & STATUS_RF_KILL_MASK)) { + IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting " + "device\n"); + schedule_reset(priv); + } else + IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still " + "enabled\n"); + + exit_unlock: + spin_unlock_irqrestore(&priv->low_lock, flags); +} + +static void ipw2100_irq_tasklet(struct ipw2100_priv *priv); + +static const struct net_device_ops ipw2100_netdev_ops = { + .ndo_open = ipw2100_open, + .ndo_stop = ipw2100_close, + .ndo_start_xmit = libipw_xmit, + .ndo_change_mtu = libipw_change_mtu, + .ndo_tx_timeout = ipw2100_tx_timeout, + .ndo_set_mac_address = ipw2100_set_address, + .ndo_validate_addr = eth_validate_addr, +}; + +/* Look into using netdev destructor to shutdown libipw? */ + +static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, + void __iomem * ioaddr) +{ + struct ipw2100_priv *priv; + struct net_device *dev; + + dev = alloc_libipw(sizeof(struct ipw2100_priv), 0); + if (!dev) + return NULL; + priv = libipw_priv(dev); + priv->ieee = netdev_priv(dev); + priv->pci_dev = pci_dev; + priv->net_dev = dev; + priv->ioaddr = ioaddr; + + priv->ieee->hard_start_xmit = ipw2100_tx; + priv->ieee->set_security = shim__set_security; + + priv->ieee->perfect_rssi = -20; + priv->ieee->worst_rssi = -85; + + dev->netdev_ops = &ipw2100_netdev_ops; + dev->ethtool_ops = &ipw2100_ethtool_ops; + dev->wireless_handlers = &ipw2100_wx_handler_def; + priv->wireless_data.libipw = priv->ieee; + dev->wireless_data = &priv->wireless_data; + dev->watchdog_timeo = 3 * HZ; + dev->irq = 0; + + /* NOTE: We don't use the wireless_handlers hook + * in dev as the system will start throwing WX requests + * to us before we're actually initialized and it just + * ends up causing problems. So, we just handle + * the WX extensions through the ipw2100_ioctl interface */ + + /* memset() puts everything to 0, so we only have explicitly set + * those values that need to be something else */ + + /* If power management is turned on, default to AUTO mode */ + priv->power_mode = IPW_POWER_AUTO; + +#ifdef CONFIG_IPW2100_MONITOR + priv->config |= CFG_CRC_CHECK; +#endif + priv->ieee->wpa_enabled = 0; + priv->ieee->drop_unencrypted = 0; + priv->ieee->privacy_invoked = 0; + priv->ieee->ieee802_1x = 1; + + /* Set module parameters */ + switch (network_mode) { + case 1: + priv->ieee->iw_mode = IW_MODE_ADHOC; + break; +#ifdef CONFIG_IPW2100_MONITOR + case 2: + priv->ieee->iw_mode = IW_MODE_MONITOR; + break; +#endif + default: + case 0: + priv->ieee->iw_mode = IW_MODE_INFRA; + break; + } + + if (disable == 1) + priv->status |= STATUS_RF_KILL_SW; + + if (channel != 0 && + ((channel >= REG_MIN_CHANNEL) && (channel <= REG_MAX_CHANNEL))) { + priv->config |= CFG_STATIC_CHANNEL; + priv->channel = channel; + } + + if (associate) + priv->config |= CFG_ASSOCIATE; + + priv->beacon_interval = DEFAULT_BEACON_INTERVAL; + priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT; + priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT; + priv->rts_threshold = DEFAULT_RTS_THRESHOLD | RTS_DISABLED; + priv->frag_threshold = DEFAULT_FTS | FRAG_DISABLED; + priv->tx_power = IPW_TX_POWER_DEFAULT; + priv->tx_rates = DEFAULT_TX_RATES; + + strcpy(priv->nick, "ipw2100"); + + spin_lock_init(&priv->low_lock); + mutex_init(&priv->action_mutex); + mutex_init(&priv->adapter_mutex); + + init_waitqueue_head(&priv->wait_command_queue); + + netif_carrier_off(dev); + + INIT_LIST_HEAD(&priv->msg_free_list); + INIT_LIST_HEAD(&priv->msg_pend_list); + INIT_STAT(&priv->msg_free_stat); + INIT_STAT(&priv->msg_pend_stat); + + INIT_LIST_HEAD(&priv->tx_free_list); + INIT_LIST_HEAD(&priv->tx_pend_list); + INIT_STAT(&priv->tx_free_stat); + INIT_STAT(&priv->tx_pend_stat); + + INIT_LIST_HEAD(&priv->fw_pend_list); + INIT_STAT(&priv->fw_pend_stat); + + INIT_DELAYED_WORK(&priv->reset_work, ipw2100_reset_adapter); + INIT_DELAYED_WORK(&priv->security_work, ipw2100_security_work); + INIT_DELAYED_WORK(&priv->wx_event_work, ipw2100_wx_event_work); + INIT_DELAYED_WORK(&priv->hang_check, ipw2100_hang_check); + INIT_DELAYED_WORK(&priv->rf_kill, ipw2100_rf_kill); + INIT_DELAYED_WORK(&priv->scan_event, ipw2100_scan_event); + + tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) + ipw2100_irq_tasklet, (unsigned long)priv); + + /* NOTE: We do not start the deferred work for status checks yet */ + priv->stop_rf_kill = 1; + priv->stop_hang_check = 1; + + return dev; +} + +static int ipw2100_pci_init_one(struct pci_dev *pci_dev, + const struct pci_device_id *ent) +{ + void __iomem *ioaddr; + struct net_device *dev = NULL; + struct ipw2100_priv *priv = NULL; + int err = 0; + int registered = 0; + u32 val; + + IPW_DEBUG_INFO("enter\n"); + + if (!(pci_resource_flags(pci_dev, 0) & IORESOURCE_MEM)) { + IPW_DEBUG_INFO("weird - resource type is not memory\n"); + err = -ENODEV; + goto out; + } + + ioaddr = pci_iomap(pci_dev, 0, 0); + if (!ioaddr) { + printk(KERN_WARNING DRV_NAME + "Error calling ioremap_nocache.\n"); + err = -EIO; + goto fail; + } + + /* allocate and initialize our net_device */ + dev = ipw2100_alloc_device(pci_dev, ioaddr); + if (!dev) { + printk(KERN_WARNING DRV_NAME + "Error calling ipw2100_alloc_device.\n"); + err = -ENOMEM; + goto fail; + } + + /* set up PCI mappings for device */ + err = pci_enable_device(pci_dev); + if (err) { + printk(KERN_WARNING DRV_NAME + "Error calling pci_enable_device.\n"); + return err; + } + + priv = libipw_priv(dev); + + pci_set_master(pci_dev); + pci_set_drvdata(pci_dev, priv); + + err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + if (err) { + printk(KERN_WARNING DRV_NAME + "Error calling pci_set_dma_mask.\n"); + pci_disable_device(pci_dev); + return err; + } + + err = pci_request_regions(pci_dev, DRV_NAME); + if (err) { + printk(KERN_WARNING DRV_NAME + "Error calling pci_request_regions.\n"); + pci_disable_device(pci_dev); + return err; + } + + /* We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state */ + pci_read_config_dword(pci_dev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff); + + if (!ipw2100_hw_is_adapter_in_system(dev)) { + printk(KERN_WARNING DRV_NAME + "Device not found via register read.\n"); + err = -ENODEV; + goto fail; + } + + SET_NETDEV_DEV(dev, &pci_dev->dev); + + /* Force interrupts to be shut off on the device */ + priv->status |= STATUS_INT_ENABLED; + ipw2100_disable_interrupts(priv); + + /* Allocate and initialize the Tx/Rx queues and lists */ + if (ipw2100_queues_allocate(priv)) { + printk(KERN_WARNING DRV_NAME + "Error calling ipw2100_queues_allocate.\n"); + err = -ENOMEM; + goto fail; + } + ipw2100_queues_initialize(priv); + + err = request_irq(pci_dev->irq, + ipw2100_interrupt, IRQF_SHARED, dev->name, priv); + if (err) { + printk(KERN_WARNING DRV_NAME + "Error calling request_irq: %d.\n", pci_dev->irq); + goto fail; + } + dev->irq = pci_dev->irq; + + IPW_DEBUG_INFO("Attempting to register device...\n"); + + printk(KERN_INFO DRV_NAME + ": Detected Intel PRO/Wireless 2100 Network Connection\n"); + + err = ipw2100_up(priv, 1); + if (err) + goto fail; + + err = ipw2100_wdev_init(dev); + if (err) + goto fail; + registered = 1; + + /* Bring up the interface. Pre 0.46, after we registered the + * network device we would call ipw2100_up. This introduced a race + * condition with newer hotplug configurations (network was coming + * up and making calls before the device was initialized). + */ + err = register_netdev(dev); + if (err) { + printk(KERN_WARNING DRV_NAME + "Error calling register_netdev.\n"); + goto fail; + } + registered = 2; + + mutex_lock(&priv->action_mutex); + + IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev)); + + /* perform this after register_netdev so that dev->name is set */ + err = sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group); + if (err) + goto fail_unlock; + + /* If the RF Kill switch is disabled, go ahead and complete the + * startup sequence */ + if (!(priv->status & STATUS_RF_KILL_MASK)) { + /* Enable the adapter - sends HOST_COMPLETE */ + if (ipw2100_enable_adapter(priv)) { + printk(KERN_WARNING DRV_NAME + ": %s: failed in call to enable adapter.\n", + priv->net_dev->name); + ipw2100_hw_stop_adapter(priv); + err = -EIO; + goto fail_unlock; + } + + /* Start a scan . . . */ + ipw2100_set_scan_options(priv); + ipw2100_start_scan(priv); + } + + IPW_DEBUG_INFO("exit\n"); + + priv->status |= STATUS_INITIALIZED; + + mutex_unlock(&priv->action_mutex); +out: + return err; + + fail_unlock: + mutex_unlock(&priv->action_mutex); + fail: + if (dev) { + if (registered >= 2) + unregister_netdev(dev); + + if (registered) { + wiphy_unregister(priv->ieee->wdev.wiphy); + kfree(priv->ieee->bg_band.channels); + } + + ipw2100_hw_stop_adapter(priv); + + ipw2100_disable_interrupts(priv); + + if (dev->irq) + free_irq(dev->irq, priv); + + ipw2100_kill_works(priv); + + /* These are safe to call even if they weren't allocated */ + ipw2100_queues_free(priv); + sysfs_remove_group(&pci_dev->dev.kobj, + &ipw2100_attribute_group); + + free_libipw(dev, 0); + } + + pci_iounmap(pci_dev, ioaddr); + + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); + goto out; +} + +static void ipw2100_pci_remove_one(struct pci_dev *pci_dev) +{ + struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); + struct net_device *dev = priv->net_dev; + + mutex_lock(&priv->action_mutex); + + priv->status &= ~STATUS_INITIALIZED; + + sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group); + +#ifdef CONFIG_PM + if (ipw2100_firmware.version) + ipw2100_release_firmware(priv, &ipw2100_firmware); +#endif + /* Take down the hardware */ + ipw2100_down(priv); + + /* Release the mutex so that the network subsystem can + * complete any needed calls into the driver... */ + mutex_unlock(&priv->action_mutex); + + /* Unregister the device first - this results in close() + * being called if the device is open. If we free storage + * first, then close() will crash. + * FIXME: remove the comment above. */ + unregister_netdev(dev); + + ipw2100_kill_works(priv); + + ipw2100_queues_free(priv); + + /* Free potential debugging firmware snapshot */ + ipw2100_snapshot_free(priv); + + free_irq(dev->irq, priv); + + pci_iounmap(pci_dev, priv->ioaddr); + + /* wiphy_unregister needs to be here, before free_libipw */ + wiphy_unregister(priv->ieee->wdev.wiphy); + kfree(priv->ieee->bg_band.channels); + free_libipw(dev, 0); + + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); + + IPW_DEBUG_INFO("exit\n"); +} + +#ifdef CONFIG_PM +static int ipw2100_suspend(struct pci_dev *pci_dev, pm_message_t state) +{ + struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); + struct net_device *dev = priv->net_dev; + + IPW_DEBUG_INFO("%s: Going into suspend...\n", dev->name); + + mutex_lock(&priv->action_mutex); + if (priv->status & STATUS_INITIALIZED) { + /* Take down the device; powers it off, etc. */ + ipw2100_down(priv); + } + + /* Remove the PRESENT state of the device */ + netif_device_detach(dev); + + pci_save_state(pci_dev); + pci_disable_device(pci_dev); + pci_set_power_state(pci_dev, PCI_D3hot); + + priv->suspend_at = get_seconds(); + + mutex_unlock(&priv->action_mutex); + + return 0; +} + +static int ipw2100_resume(struct pci_dev *pci_dev) +{ + struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); + struct net_device *dev = priv->net_dev; + int err; + u32 val; + + if (IPW2100_PM_DISABLED) + return 0; + + mutex_lock(&priv->action_mutex); + + IPW_DEBUG_INFO("%s: Coming out of suspend...\n", dev->name); + + pci_set_power_state(pci_dev, PCI_D0); + err = pci_enable_device(pci_dev); + if (err) { + printk(KERN_ERR "%s: pci_enable_device failed on resume\n", + dev->name); + mutex_unlock(&priv->action_mutex); + return err; + } + pci_restore_state(pci_dev); + + /* + * Suspend/Resume resets the PCI configuration space, so we have to + * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries + * from interfering with C3 CPU state. pci_restore_state won't help + * here since it only restores the first 64 bytes pci config header. + */ + pci_read_config_dword(pci_dev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff); + + /* Set the device back into the PRESENT state; this will also wake + * the queue of needed */ + netif_device_attach(dev); + + priv->suspend_time = get_seconds() - priv->suspend_at; + + /* Bring the device back up */ + if (!(priv->status & STATUS_RF_KILL_SW)) + ipw2100_up(priv, 0); + + mutex_unlock(&priv->action_mutex); + + return 0; +} +#endif + +static void ipw2100_shutdown(struct pci_dev *pci_dev) +{ + struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); + + /* Take down the device; powers it off, etc. */ + ipw2100_down(priv); + + pci_disable_device(pci_dev); +} + +#define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x } + +static const struct pci_device_id ipw2100_pci_id_table[] = { + IPW2100_DEV_ID(0x2520), /* IN 2100A mPCI 3A */ + IPW2100_DEV_ID(0x2521), /* IN 2100A mPCI 3B */ + IPW2100_DEV_ID(0x2524), /* IN 2100A mPCI 3B */ + IPW2100_DEV_ID(0x2525), /* IN 2100A mPCI 3B */ + IPW2100_DEV_ID(0x2526), /* IN 2100A mPCI Gen A3 */ + IPW2100_DEV_ID(0x2522), /* IN 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2523), /* IN 2100 mPCI 3A */ + IPW2100_DEV_ID(0x2527), /* IN 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2528), /* IN 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2529), /* IN 2100 mPCI 3B */ + IPW2100_DEV_ID(0x252B), /* IN 2100 mPCI 3A */ + IPW2100_DEV_ID(0x252C), /* IN 2100 mPCI 3A */ + IPW2100_DEV_ID(0x252D), /* IN 2100 mPCI 3A */ + + IPW2100_DEV_ID(0x2550), /* IB 2100A mPCI 3B */ + IPW2100_DEV_ID(0x2551), /* IB 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2553), /* IB 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2554), /* IB 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2555), /* IB 2100 mPCI 3B */ + + IPW2100_DEV_ID(0x2560), /* DE 2100A mPCI 3A */ + IPW2100_DEV_ID(0x2562), /* DE 2100A mPCI 3A */ + IPW2100_DEV_ID(0x2563), /* DE 2100A mPCI 3A */ + IPW2100_DEV_ID(0x2561), /* DE 2100 mPCI 3A */ + IPW2100_DEV_ID(0x2565), /* DE 2100 mPCI 3A */ + IPW2100_DEV_ID(0x2566), /* DE 2100 mPCI 3A */ + IPW2100_DEV_ID(0x2567), /* DE 2100 mPCI 3A */ + + IPW2100_DEV_ID(0x2570), /* GA 2100 mPCI 3B */ + + IPW2100_DEV_ID(0x2580), /* TO 2100A mPCI 3B */ + IPW2100_DEV_ID(0x2582), /* TO 2100A mPCI 3B */ + IPW2100_DEV_ID(0x2583), /* TO 2100A mPCI 3B */ + IPW2100_DEV_ID(0x2581), /* TO 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2585), /* TO 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2586), /* TO 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2587), /* TO 2100 mPCI 3B */ + + IPW2100_DEV_ID(0x2590), /* SO 2100A mPCI 3B */ + IPW2100_DEV_ID(0x2592), /* SO 2100A mPCI 3B */ + IPW2100_DEV_ID(0x2591), /* SO 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2593), /* SO 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2596), /* SO 2100 mPCI 3B */ + IPW2100_DEV_ID(0x2598), /* SO 2100 mPCI 3B */ + + IPW2100_DEV_ID(0x25A0), /* HP 2100 mPCI 3B */ + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, ipw2100_pci_id_table); + +static struct pci_driver ipw2100_pci_driver = { + .name = DRV_NAME, + .id_table = ipw2100_pci_id_table, + .probe = ipw2100_pci_init_one, + .remove = ipw2100_pci_remove_one, +#ifdef CONFIG_PM + .suspend = ipw2100_suspend, + .resume = ipw2100_resume, +#endif + .shutdown = ipw2100_shutdown, +}; + +/** + * Initialize the ipw2100 driver/module + * + * @returns 0 if ok, < 0 errno node con error. + * + * Note: we cannot init the /proc stuff until the PCI driver is there, + * or we risk an unlikely race condition on someone accessing + * uninitialized data in the PCI dev struct through /proc. + */ +static int __init ipw2100_init(void) +{ + int ret; + + printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION); + printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT); + + pm_qos_add_request(&ipw2100_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + ret = pci_register_driver(&ipw2100_pci_driver); + if (ret) + goto out; + +#ifdef CONFIG_IPW2100_DEBUG + ipw2100_debug_level = debug; + ret = driver_create_file(&ipw2100_pci_driver.driver, + &driver_attr_debug_level); +#endif + +out: + return ret; +} + +/** + * Cleanup ipw2100 driver registration + */ +static void __exit ipw2100_exit(void) +{ + /* FIXME: IPG: check that we have no instances of the devices open */ +#ifdef CONFIG_IPW2100_DEBUG + driver_remove_file(&ipw2100_pci_driver.driver, + &driver_attr_debug_level); +#endif + pci_unregister_driver(&ipw2100_pci_driver); + pm_qos_remove_request(&ipw2100_pm_qos_req); +} + +module_init(ipw2100_init); +module_exit(ipw2100_exit); + +static int ipw2100_wx_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + if (!(priv->status & STATUS_ASSOCIATED)) + strcpy(wrqu->name, "unassociated"); + else + snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b"); + + IPW_DEBUG_WX("Name: %s\n", wrqu->name); + return 0; +} + +static int ipw2100_wx_set_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + struct iw_freq *fwrq = &wrqu->freq; + int err = 0; + + if (priv->ieee->iw_mode == IW_MODE_INFRA) + return -EOPNOTSUPP; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + /* if setting by freq convert to channel */ + if (fwrq->e == 1) { + if ((fwrq->m >= (int)2.412e8 && fwrq->m <= (int)2.487e8)) { + int f = fwrq->m / 100000; + int c = 0; + + while ((c < REG_MAX_CHANNEL) && + (f != ipw2100_frequencies[c])) + c++; + + /* hack to fall through */ + fwrq->e = 0; + fwrq->m = c + 1; + } + } + + if (fwrq->e > 0 || fwrq->m > 1000) { + err = -EOPNOTSUPP; + goto done; + } else { /* Set the channel */ + IPW_DEBUG_WX("SET Freq/Channel -> %d\n", fwrq->m); + err = ipw2100_set_channel(priv, fwrq->m, 0); + } + + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_get_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + wrqu->freq.e = 0; + + /* If we are associated, trying to associate, or have a statically + * configured CHANNEL then return that; otherwise return ANY */ + if (priv->config & CFG_STATIC_CHANNEL || + priv->status & STATUS_ASSOCIATED) + wrqu->freq.m = priv->channel; + else + wrqu->freq.m = 0; + + IPW_DEBUG_WX("GET Freq/Channel -> %d\n", priv->channel); + return 0; + +} + +static int ipw2100_wx_set_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int err = 0; + + IPW_DEBUG_WX("SET Mode -> %d\n", wrqu->mode); + + if (wrqu->mode == priv->ieee->iw_mode) + return 0; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + switch (wrqu->mode) { +#ifdef CONFIG_IPW2100_MONITOR + case IW_MODE_MONITOR: + err = ipw2100_switch_mode(priv, IW_MODE_MONITOR); + break; +#endif /* CONFIG_IPW2100_MONITOR */ + case IW_MODE_ADHOC: + err = ipw2100_switch_mode(priv, IW_MODE_ADHOC); + break; + case IW_MODE_INFRA: + case IW_MODE_AUTO: + default: + err = ipw2100_switch_mode(priv, IW_MODE_INFRA); + break; + } + + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_get_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + wrqu->mode = priv->ieee->iw_mode; + IPW_DEBUG_WX("GET Mode -> %d\n", wrqu->mode); + + return 0; +} + +#define POWER_MODES 5 + +/* Values are in microsecond */ +static const s32 timeout_duration[POWER_MODES] = { + 350000, + 250000, + 75000, + 37000, + 25000, +}; + +static const s32 period_duration[POWER_MODES] = { + 400000, + 700000, + 1000000, + 1000000, + 1000000 +}; + +static int ipw2100_wx_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + struct iw_range *range = (struct iw_range *)extra; + u16 val; + int i, level; + + wrqu->data.length = sizeof(*range); + memset(range, 0, sizeof(*range)); + + /* Let's try to keep this struct in the same order as in + * linux/include/wireless.h + */ + + /* TODO: See what values we can set, and remove the ones we can't + * set, or fill them with some default data. + */ + + /* ~5 Mb/s real (802.11b) */ + range->throughput = 5 * 1000 * 1000; + +// range->sensitivity; /* signal level threshold range */ + + range->max_qual.qual = 100; + /* TODO: Find real max RSSI and stick here */ + range->max_qual.level = 0; + range->max_qual.noise = 0; + range->max_qual.updated = 7; /* Updated all three */ + + range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */ + /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ + range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM; + range->avg_qual.noise = 0; + range->avg_qual.updated = 7; /* Updated all three */ + + range->num_bitrates = RATE_COUNT; + + for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) { + range->bitrate[i] = ipw2100_bg_rates[i].bitrate * 100 * 1000; + } + + range->min_rts = MIN_RTS_THRESHOLD; + range->max_rts = MAX_RTS_THRESHOLD; + range->min_frag = MIN_FRAG_THRESHOLD; + range->max_frag = MAX_FRAG_THRESHOLD; + + range->min_pmp = period_duration[0]; /* Minimal PM period */ + range->max_pmp = period_duration[POWER_MODES - 1]; /* Maximal PM period */ + range->min_pmt = timeout_duration[POWER_MODES - 1]; /* Minimal PM timeout */ + range->max_pmt = timeout_duration[0]; /* Maximal PM timeout */ + + /* How to decode max/min PM period */ + range->pmp_flags = IW_POWER_PERIOD; + /* How to decode max/min PM period */ + range->pmt_flags = IW_POWER_TIMEOUT; + /* What PM options are supported */ + range->pm_capa = IW_POWER_TIMEOUT | IW_POWER_PERIOD; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; /* Different token sizes */ + range->num_encoding_sizes = 2; /* Number of entry in the list */ + range->max_encoding_tokens = WEP_KEYS; /* Max number of tokens */ +// range->encoding_login_index; /* token index for login token */ + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) { + range->txpower_capa = IW_TXPOW_DBM; + range->num_txpower = IW_MAX_TXPOWER; + for (i = 0, level = (IPW_TX_POWER_MAX_DBM * 16); + i < IW_MAX_TXPOWER; + i++, level -= + ((IPW_TX_POWER_MAX_DBM - + IPW_TX_POWER_MIN_DBM) * 16) / (IW_MAX_TXPOWER - 1)) + range->txpower[i] = level / 16; + } else { + range->txpower_capa = 0; + range->num_txpower = 0; + } + + /* Set the Wireless Extension versions */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 18; + +// range->retry_capa; /* What retry options are supported */ +// range->retry_flags; /* How to decode max/min retry limit */ +// range->r_time_flags; /* How to decode max/min retry life */ +// range->min_retry; /* Minimal number of retries */ +// range->max_retry; /* Maximal number of retries */ +// range->min_r_time; /* Minimal retry lifetime */ +// range->max_r_time; /* Maximal retry lifetime */ + + range->num_channels = FREQ_COUNT; + + val = 0; + for (i = 0; i < FREQ_COUNT; i++) { + // TODO: Include only legal frequencies for some countries +// if (local->channel_mask & (1 << i)) { + range->freq[val].i = i + 1; + range->freq[val].m = ipw2100_frequencies[i] * 100000; + range->freq[val].e = 1; + val++; +// } + if (val == IW_MAX_FREQUENCIES) + break; + } + range->num_frequency = val; + + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWAP)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; + + IPW_DEBUG_WX("GET Range\n"); + + return 0; +} + +static int ipw2100_wx_set_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int err = 0; + + // sanity checks + if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) + return -EINVAL; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) || + is_zero_ether_addr(wrqu->ap_addr.sa_data)) { + /* we disable mandatory BSSID association */ + IPW_DEBUG_WX("exit - disable mandatory BSSID\n"); + priv->config &= ~CFG_STATIC_BSSID; + err = ipw2100_set_mandatory_bssid(priv, NULL, 0); + goto done; + } + + priv->config |= CFG_STATIC_BSSID; + memcpy(priv->mandatory_bssid_mac, wrqu->ap_addr.sa_data, ETH_ALEN); + + err = ipw2100_set_mandatory_bssid(priv, wrqu->ap_addr.sa_data, 0); + + IPW_DEBUG_WX("SET BSSID -> %pM\n", wrqu->ap_addr.sa_data); + + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_get_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + /* If we are associated, trying to associate, or have a statically + * configured BSSID then return that; otherwise return ANY */ + if (priv->config & CFG_STATIC_BSSID || priv->status & STATUS_ASSOCIATED) { + wrqu->ap_addr.sa_family = ARPHRD_ETHER; + memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN); + } else + eth_zero_addr(wrqu->ap_addr.sa_data); + + IPW_DEBUG_WX("Getting WAP BSSID: %pM\n", wrqu->ap_addr.sa_data); + return 0; +} + +static int ipw2100_wx_set_essid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + char *essid = ""; /* ANY */ + int length = 0; + int err = 0; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if (wrqu->essid.flags && wrqu->essid.length) { + length = wrqu->essid.length; + essid = extra; + } + + if (length == 0) { + IPW_DEBUG_WX("Setting ESSID to ANY\n"); + priv->config &= ~CFG_STATIC_ESSID; + err = ipw2100_set_essid(priv, NULL, 0, 0); + goto done; + } + + length = min(length, IW_ESSID_MAX_SIZE); + + priv->config |= CFG_STATIC_ESSID; + + if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) { + IPW_DEBUG_WX("ESSID set to current ESSID.\n"); + err = 0; + goto done; + } + + IPW_DEBUG_WX("Setting ESSID: '%*pE' (%d)\n", length, essid, length); + + priv->essid_len = length; + memcpy(priv->essid, essid, priv->essid_len); + + err = ipw2100_set_essid(priv, essid, length, 0); + + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_get_essid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + /* If we are associated, trying to associate, or have a statically + * configured ESSID then return that; otherwise return ANY */ + if (priv->config & CFG_STATIC_ESSID || priv->status & STATUS_ASSOCIATED) { + IPW_DEBUG_WX("Getting essid: '%*pE'\n", + priv->essid_len, priv->essid); + memcpy(extra, priv->essid, priv->essid_len); + wrqu->essid.length = priv->essid_len; + wrqu->essid.flags = 1; /* active */ + } else { + IPW_DEBUG_WX("Getting essid: ANY\n"); + wrqu->essid.length = 0; + wrqu->essid.flags = 0; /* active */ + } + + return 0; +} + +static int ipw2100_wx_set_nick(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + if (wrqu->data.length > IW_ESSID_MAX_SIZE) + return -E2BIG; + + wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick)); + memset(priv->nick, 0, sizeof(priv->nick)); + memcpy(priv->nick, extra, wrqu->data.length); + + IPW_DEBUG_WX("SET Nickname -> %s\n", priv->nick); + + return 0; +} + +static int ipw2100_wx_get_nick(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + wrqu->data.length = strlen(priv->nick); + memcpy(extra, priv->nick, wrqu->data.length); + wrqu->data.flags = 1; /* active */ + + IPW_DEBUG_WX("GET Nickname -> %s\n", extra); + + return 0; +} + +static int ipw2100_wx_set_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + u32 target_rate = wrqu->bitrate.value; + u32 rate; + int err = 0; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + rate = 0; + + if (target_rate == 1000000 || + (!wrqu->bitrate.fixed && target_rate > 1000000)) + rate |= TX_RATE_1_MBIT; + if (target_rate == 2000000 || + (!wrqu->bitrate.fixed && target_rate > 2000000)) + rate |= TX_RATE_2_MBIT; + if (target_rate == 5500000 || + (!wrqu->bitrate.fixed && target_rate > 5500000)) + rate |= TX_RATE_5_5_MBIT; + if (target_rate == 11000000 || + (!wrqu->bitrate.fixed && target_rate > 11000000)) + rate |= TX_RATE_11_MBIT; + if (rate == 0) + rate = DEFAULT_TX_RATES; + + err = ipw2100_set_tx_rates(priv, rate, 0); + + IPW_DEBUG_WX("SET Rate -> %04X\n", rate); + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_get_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int val; + unsigned int len = sizeof(val); + int err = 0; + + if (!(priv->status & STATUS_ENABLED) || + priv->status & STATUS_RF_KILL_MASK || + !(priv->status & STATUS_ASSOCIATED)) { + wrqu->bitrate.value = 0; + return 0; + } + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + err = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &val, &len); + if (err) { + IPW_DEBUG_WX("failed querying ordinals.\n"); + goto done; + } + + switch (val & TX_RATE_MASK) { + case TX_RATE_1_MBIT: + wrqu->bitrate.value = 1000000; + break; + case TX_RATE_2_MBIT: + wrqu->bitrate.value = 2000000; + break; + case TX_RATE_5_5_MBIT: + wrqu->bitrate.value = 5500000; + break; + case TX_RATE_11_MBIT: + wrqu->bitrate.value = 11000000; + break; + default: + wrqu->bitrate.value = 0; + } + + IPW_DEBUG_WX("GET Rate -> %d\n", wrqu->bitrate.value); + + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_set_rts(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int value, err; + + /* Auto RTS not yet supported */ + if (wrqu->rts.fixed == 0) + return -EINVAL; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if (wrqu->rts.disabled) + value = priv->rts_threshold | RTS_DISABLED; + else { + if (wrqu->rts.value < 1 || wrqu->rts.value > 2304) { + err = -EINVAL; + goto done; + } + value = wrqu->rts.value; + } + + err = ipw2100_set_rts_threshold(priv, value); + + IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X\n", value); + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_get_rts(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + wrqu->rts.value = priv->rts_threshold & ~RTS_DISABLED; + wrqu->rts.fixed = 1; /* no auto select */ + + /* If RTS is set to the default value, then it is disabled */ + wrqu->rts.disabled = (priv->rts_threshold & RTS_DISABLED) ? 1 : 0; + + IPW_DEBUG_WX("GET RTS Threshold -> 0x%08X\n", wrqu->rts.value); + + return 0; +} + +static int ipw2100_wx_set_txpow(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int err = 0, value; + + if (ipw_radio_kill_sw(priv, wrqu->txpower.disabled)) + return -EINPROGRESS; + + if (priv->ieee->iw_mode != IW_MODE_ADHOC) + return 0; + + if ((wrqu->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) + return -EINVAL; + + if (wrqu->txpower.fixed == 0) + value = IPW_TX_POWER_DEFAULT; + else { + if (wrqu->txpower.value < IPW_TX_POWER_MIN_DBM || + wrqu->txpower.value > IPW_TX_POWER_MAX_DBM) + return -EINVAL; + + value = wrqu->txpower.value; + } + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + err = ipw2100_set_tx_power(priv, value); + + IPW_DEBUG_WX("SET TX Power -> %d\n", value); + + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_get_txpow(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + wrqu->txpower.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0; + + if (priv->tx_power == IPW_TX_POWER_DEFAULT) { + wrqu->txpower.fixed = 0; + wrqu->txpower.value = IPW_TX_POWER_MAX_DBM; + } else { + wrqu->txpower.fixed = 1; + wrqu->txpower.value = priv->tx_power; + } + + wrqu->txpower.flags = IW_TXPOW_DBM; + + IPW_DEBUG_WX("GET TX Power -> %d\n", wrqu->txpower.value); + + return 0; +} + +static int ipw2100_wx_set_frag(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + if (!wrqu->frag.fixed) + return -EINVAL; + + if (wrqu->frag.disabled) { + priv->frag_threshold |= FRAG_DISABLED; + priv->ieee->fts = DEFAULT_FTS; + } else { + if (wrqu->frag.value < MIN_FRAG_THRESHOLD || + wrqu->frag.value > MAX_FRAG_THRESHOLD) + return -EINVAL; + + priv->ieee->fts = wrqu->frag.value & ~0x1; + priv->frag_threshold = priv->ieee->fts; + } + + IPW_DEBUG_WX("SET Frag Threshold -> %d\n", priv->ieee->fts); + + return 0; +} + +static int ipw2100_wx_get_frag(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + wrqu->frag.value = priv->frag_threshold & ~FRAG_DISABLED; + wrqu->frag.fixed = 0; /* no auto select */ + wrqu->frag.disabled = (priv->frag_threshold & FRAG_DISABLED) ? 1 : 0; + + IPW_DEBUG_WX("GET Frag Threshold -> %d\n", wrqu->frag.value); + + return 0; +} + +static int ipw2100_wx_set_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int err = 0; + + if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled) + return -EINVAL; + + if (!(wrqu->retry.flags & IW_RETRY_LIMIT)) + return 0; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if (wrqu->retry.flags & IW_RETRY_SHORT) { + err = ipw2100_set_short_retry(priv, wrqu->retry.value); + IPW_DEBUG_WX("SET Short Retry Limit -> %d\n", + wrqu->retry.value); + goto done; + } + + if (wrqu->retry.flags & IW_RETRY_LONG) { + err = ipw2100_set_long_retry(priv, wrqu->retry.value); + IPW_DEBUG_WX("SET Long Retry Limit -> %d\n", + wrqu->retry.value); + goto done; + } + + err = ipw2100_set_short_retry(priv, wrqu->retry.value); + if (!err) + err = ipw2100_set_long_retry(priv, wrqu->retry.value); + + IPW_DEBUG_WX("SET Both Retry Limits -> %d\n", wrqu->retry.value); + + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_get_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + wrqu->retry.disabled = 0; /* can't be disabled */ + + if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) + return -EINVAL; + + if (wrqu->retry.flags & IW_RETRY_LONG) { + wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG; + wrqu->retry.value = priv->long_retry_limit; + } else { + wrqu->retry.flags = + (priv->short_retry_limit != + priv->long_retry_limit) ? + IW_RETRY_LIMIT | IW_RETRY_SHORT : IW_RETRY_LIMIT; + + wrqu->retry.value = priv->short_retry_limit; + } + + IPW_DEBUG_WX("GET Retry -> %d\n", wrqu->retry.value); + + return 0; +} + +static int ipw2100_wx_set_scan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int err = 0; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + IPW_DEBUG_WX("Initiating scan...\n"); + + priv->user_requested_scan = 1; + if (ipw2100_set_scan_options(priv) || ipw2100_start_scan(priv)) { + IPW_DEBUG_WX("Start scan failed.\n"); + + /* TODO: Mark a scan as pending so when hardware initialized + * a scan starts */ + } + + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_get_scan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + return libipw_wx_get_scan(priv->ieee, info, wrqu, extra); +} + +/* + * Implementation based on code in hostap-driver v0.1.3 hostap_ioctl.c + */ +static int ipw2100_wx_set_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key) +{ + /* + * No check of STATUS_INITIALIZED required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + return libipw_wx_set_encode(priv->ieee, info, wrqu, key); +} + +static int ipw2100_wx_get_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + return libipw_wx_get_encode(priv->ieee, info, wrqu, key); +} + +static int ipw2100_wx_set_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int err = 0; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if (wrqu->power.disabled) { + priv->power_mode = IPW_POWER_LEVEL(priv->power_mode); + err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM); + IPW_DEBUG_WX("SET Power Management Mode -> off\n"); + goto done; + } + + switch (wrqu->power.flags & IW_POWER_MODE) { + case IW_POWER_ON: /* If not specified */ + case IW_POWER_MODE: /* If set all mask */ + case IW_POWER_ALL_R: /* If explicitly state all */ + break; + default: /* Otherwise we don't support it */ + IPW_DEBUG_WX("SET PM Mode: %X not supported.\n", + wrqu->power.flags); + err = -EOPNOTSUPP; + goto done; + } + + /* If the user hasn't specified a power management mode yet, default + * to BATTERY */ + priv->power_mode = IPW_POWER_ENABLED | priv->power_mode; + err = ipw2100_set_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode)); + + IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode); + + done: + mutex_unlock(&priv->action_mutex); + return err; + +} + +static int ipw2100_wx_get_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + if (!(priv->power_mode & IPW_POWER_ENABLED)) + wrqu->power.disabled = 1; + else { + wrqu->power.disabled = 0; + wrqu->power.flags = 0; + } + + IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode); + + return 0; +} + +/* + * WE-18 WPA support + */ + +/* SIOCSIWGENIE */ +static int ipw2100_wx_set_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + + struct ipw2100_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; + u8 *buf; + + if (!ieee->wpa_enabled) + return -EOPNOTSUPP; + + if (wrqu->data.length > MAX_WPA_IE_LEN || + (wrqu->data.length && extra == NULL)) + return -EINVAL; + + if (wrqu->data.length) { + buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + kfree(ieee->wpa_ie); + ieee->wpa_ie = buf; + ieee->wpa_ie_len = wrqu->data.length; + } else { + kfree(ieee->wpa_ie); + ieee->wpa_ie = NULL; + ieee->wpa_ie_len = 0; + } + + ipw2100_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len); + + return 0; +} + +/* SIOCGIWGENIE */ +static int ipw2100_wx_get_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; + + if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) { + wrqu->data.length = 0; + return 0; + } + + if (wrqu->data.length < ieee->wpa_ie_len) + return -E2BIG; + + wrqu->data.length = ieee->wpa_ie_len; + memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len); + + return 0; +} + +/* SIOCSIWAUTH */ +static int ipw2100_wx_set_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; + struct iw_param *param = &wrqu->param; + struct lib80211_crypt_data *crypt; + unsigned long flags; + int ret = 0; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * ipw2200 does not use these parameters + */ + break; + + case IW_AUTH_TKIP_COUNTERMEASURES: + crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; + if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) + break; + + flags = crypt->ops->get_flags(crypt->priv); + + if (param->value) + flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; + else + flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; + + crypt->ops->set_flags(flags, crypt->priv); + + break; + + case IW_AUTH_DROP_UNENCRYPTED:{ + /* HACK: + * + * wpa_supplicant calls set_wpa_enabled when the driver + * is loaded and unloaded, regardless of if WPA is being + * used. No other calls are made which can be used to + * determine if encryption will be used or not prior to + * association being expected. If encryption is not being + * used, drop_unencrypted is set to false, else true -- we + * can use this to determine if the CAP_PRIVACY_ON bit should + * be set. + */ + struct libipw_security sec = { + .flags = SEC_ENABLED, + .enabled = param->value, + }; + priv->ieee->drop_unencrypted = param->value; + /* We only change SEC_LEVEL for open mode. Others + * are set by ipw_wpa_set_encryption. + */ + if (!param->value) { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_0; + } else { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; + } + if (priv->ieee->set_security) + priv->ieee->set_security(priv->ieee->dev, &sec); + break; + } + + case IW_AUTH_80211_AUTH_ALG: + ret = ipw2100_wpa_set_auth_algs(priv, param->value); + break; + + case IW_AUTH_WPA_ENABLED: + ret = ipw2100_wpa_enable(priv, param->value); + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + ieee->ieee802_1x = param->value; + break; + + //case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: + ieee->privacy_invoked = param->value; + break; + + default: + return -EOPNOTSUPP; + } + return ret; +} + +/* SIOCGIWAUTH */ +static int ipw2100_wx_get_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; + struct lib80211_crypt_data *crypt; + struct iw_param *param = &wrqu->param; + int ret = 0; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * wpa_supplicant will control these internally + */ + ret = -EOPNOTSUPP; + break; + + case IW_AUTH_TKIP_COUNTERMEASURES: + crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; + if (!crypt || !crypt->ops->get_flags) { + IPW_DEBUG_WARNING("Can't get TKIP countermeasures: " + "crypt not set!\n"); + break; + } + + param->value = (crypt->ops->get_flags(crypt->priv) & + IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0; + + break; + + case IW_AUTH_DROP_UNENCRYPTED: + param->value = ieee->drop_unencrypted; + break; + + case IW_AUTH_80211_AUTH_ALG: + param->value = priv->ieee->sec.auth_mode; + break; + + case IW_AUTH_WPA_ENABLED: + param->value = ieee->wpa_enabled; + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + param->value = ieee->ieee802_1x; + break; + + case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: + param->value = ieee->privacy_invoked; + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + +/* SIOCSIWENCODEEXT */ +static int ipw2100_wx_set_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra); +} + +/* SIOCGIWENCODEEXT */ +static int ipw2100_wx_get_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra); +} + +/* SIOCSIWMLME */ +static int ipw2100_wx_set_mlme(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + struct iw_mlme *mlme = (struct iw_mlme *)extra; + __le16 reason; + + reason = cpu_to_le16(mlme->reason_code); + + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + // silently ignore + break; + + case IW_MLME_DISASSOC: + ipw2100_disassociate_bssid(priv); + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + +/* + * + * IWPRIV handlers + * + */ +#ifdef CONFIG_IPW2100_MONITOR +static int ipw2100_wx_set_promisc(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int *parms = (int *)extra; + int enable = (parms[0] > 0); + int err = 0; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if (enable) { + if (priv->ieee->iw_mode == IW_MODE_MONITOR) { + err = ipw2100_set_channel(priv, parms[1], 0); + goto done; + } + priv->channel = parms[1]; + err = ipw2100_switch_mode(priv, IW_MODE_MONITOR); + } else { + if (priv->ieee->iw_mode == IW_MODE_MONITOR) + err = ipw2100_switch_mode(priv, priv->last_mode); + } + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_reset(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + if (priv->status & STATUS_INITIALIZED) + schedule_reset(priv); + return 0; +} + +#endif + +static int ipw2100_wx_set_powermode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int err = 0, mode = *(int *)extra; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if ((mode < 0) || (mode > POWER_MODES)) + mode = IPW_POWER_AUTO; + + if (IPW_POWER_LEVEL(priv->power_mode) != mode) + err = ipw2100_set_power_mode(priv, mode); + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +#define MAX_POWER_STRING 80 +static int ipw2100_wx_get_powermode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + int level = IPW_POWER_LEVEL(priv->power_mode); + s32 timeout, period; + + if (!(priv->power_mode & IPW_POWER_ENABLED)) { + snprintf(extra, MAX_POWER_STRING, + "Power save level: %d (Off)", level); + } else { + switch (level) { + case IPW_POWER_MODE_CAM: + snprintf(extra, MAX_POWER_STRING, + "Power save level: %d (None)", level); + break; + case IPW_POWER_AUTO: + snprintf(extra, MAX_POWER_STRING, + "Power save level: %d (Auto)", level); + break; + default: + timeout = timeout_duration[level - 1] / 1000; + period = period_duration[level - 1] / 1000; + snprintf(extra, MAX_POWER_STRING, + "Power save level: %d " + "(Timeout %dms, Period %dms)", + level, timeout, period); + } + } + + wrqu->data.length = strlen(extra) + 1; + + return 0; +} + +static int ipw2100_wx_set_preamble(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int err, mode = *(int *)extra; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if (mode == 1) + priv->config |= CFG_LONG_PREAMBLE; + else if (mode == 0) + priv->config &= ~CFG_LONG_PREAMBLE; + else { + err = -EINVAL; + goto done; + } + + err = ipw2100_system_config(priv, 0); + + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_get_preamble(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + if (priv->config & CFG_LONG_PREAMBLE) + snprintf(wrqu->name, IFNAMSIZ, "long (1)"); + else + snprintf(wrqu->name, IFNAMSIZ, "auto (0)"); + + return 0; +} + +#ifdef CONFIG_IPW2100_MONITOR +static int ipw2100_wx_set_crc_check(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw2100_priv *priv = libipw_priv(dev); + int err, mode = *(int *)extra; + + mutex_lock(&priv->action_mutex); + if (!(priv->status & STATUS_INITIALIZED)) { + err = -EIO; + goto done; + } + + if (mode == 1) + priv->config |= CFG_CRC_CHECK; + else if (mode == 0) + priv->config &= ~CFG_CRC_CHECK; + else { + err = -EINVAL; + goto done; + } + err = 0; + + done: + mutex_unlock(&priv->action_mutex); + return err; +} + +static int ipw2100_wx_get_crc_check(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* + * This can be called at any time. No action lock required + */ + + struct ipw2100_priv *priv = libipw_priv(dev); + + if (priv->config & CFG_CRC_CHECK) + snprintf(wrqu->name, IFNAMSIZ, "CRC checked (1)"); + else + snprintf(wrqu->name, IFNAMSIZ, "CRC ignored (0)"); + + return 0; +} +#endif /* CONFIG_IPW2100_MONITOR */ + +static iw_handler ipw2100_wx_handlers[] = { + IW_HANDLER(SIOCGIWNAME, ipw2100_wx_get_name), + IW_HANDLER(SIOCSIWFREQ, ipw2100_wx_set_freq), + IW_HANDLER(SIOCGIWFREQ, ipw2100_wx_get_freq), + IW_HANDLER(SIOCSIWMODE, ipw2100_wx_set_mode), + IW_HANDLER(SIOCGIWMODE, ipw2100_wx_get_mode), + IW_HANDLER(SIOCGIWRANGE, ipw2100_wx_get_range), + IW_HANDLER(SIOCSIWAP, ipw2100_wx_set_wap), + IW_HANDLER(SIOCGIWAP, ipw2100_wx_get_wap), + IW_HANDLER(SIOCSIWMLME, ipw2100_wx_set_mlme), + IW_HANDLER(SIOCSIWSCAN, ipw2100_wx_set_scan), + IW_HANDLER(SIOCGIWSCAN, ipw2100_wx_get_scan), + IW_HANDLER(SIOCSIWESSID, ipw2100_wx_set_essid), + IW_HANDLER(SIOCGIWESSID, ipw2100_wx_get_essid), + IW_HANDLER(SIOCSIWNICKN, ipw2100_wx_set_nick), + IW_HANDLER(SIOCGIWNICKN, ipw2100_wx_get_nick), + IW_HANDLER(SIOCSIWRATE, ipw2100_wx_set_rate), + IW_HANDLER(SIOCGIWRATE, ipw2100_wx_get_rate), + IW_HANDLER(SIOCSIWRTS, ipw2100_wx_set_rts), + IW_HANDLER(SIOCGIWRTS, ipw2100_wx_get_rts), + IW_HANDLER(SIOCSIWFRAG, ipw2100_wx_set_frag), + IW_HANDLER(SIOCGIWFRAG, ipw2100_wx_get_frag), + IW_HANDLER(SIOCSIWTXPOW, ipw2100_wx_set_txpow), + IW_HANDLER(SIOCGIWTXPOW, ipw2100_wx_get_txpow), + IW_HANDLER(SIOCSIWRETRY, ipw2100_wx_set_retry), + IW_HANDLER(SIOCGIWRETRY, ipw2100_wx_get_retry), + IW_HANDLER(SIOCSIWENCODE, ipw2100_wx_set_encode), + IW_HANDLER(SIOCGIWENCODE, ipw2100_wx_get_encode), + IW_HANDLER(SIOCSIWPOWER, ipw2100_wx_set_power), + IW_HANDLER(SIOCGIWPOWER, ipw2100_wx_get_power), + IW_HANDLER(SIOCSIWGENIE, ipw2100_wx_set_genie), + IW_HANDLER(SIOCGIWGENIE, ipw2100_wx_get_genie), + IW_HANDLER(SIOCSIWAUTH, ipw2100_wx_set_auth), + IW_HANDLER(SIOCGIWAUTH, ipw2100_wx_get_auth), + IW_HANDLER(SIOCSIWENCODEEXT, ipw2100_wx_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, ipw2100_wx_get_encodeext), +}; + +#define IPW2100_PRIV_SET_MONITOR SIOCIWFIRSTPRIV +#define IPW2100_PRIV_RESET SIOCIWFIRSTPRIV+1 +#define IPW2100_PRIV_SET_POWER SIOCIWFIRSTPRIV+2 +#define IPW2100_PRIV_GET_POWER SIOCIWFIRSTPRIV+3 +#define IPW2100_PRIV_SET_LONGPREAMBLE SIOCIWFIRSTPRIV+4 +#define IPW2100_PRIV_GET_LONGPREAMBLE SIOCIWFIRSTPRIV+5 +#define IPW2100_PRIV_SET_CRC_CHECK SIOCIWFIRSTPRIV+6 +#define IPW2100_PRIV_GET_CRC_CHECK SIOCIWFIRSTPRIV+7 + +static const struct iw_priv_args ipw2100_private_args[] = { + +#ifdef CONFIG_IPW2100_MONITOR + { + IPW2100_PRIV_SET_MONITOR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"}, + { + IPW2100_PRIV_RESET, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"}, +#endif /* CONFIG_IPW2100_MONITOR */ + + { + IPW2100_PRIV_SET_POWER, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_power"}, + { + IPW2100_PRIV_GET_POWER, + 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_POWER_STRING, + "get_power"}, + { + IPW2100_PRIV_SET_LONGPREAMBLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"}, + { + IPW2100_PRIV_GET_LONGPREAMBLE, + 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_preamble"}, +#ifdef CONFIG_IPW2100_MONITOR + { + IPW2100_PRIV_SET_CRC_CHECK, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_crc_check"}, + { + IPW2100_PRIV_GET_CRC_CHECK, + 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_crc_check"}, +#endif /* CONFIG_IPW2100_MONITOR */ +}; + +static iw_handler ipw2100_private_handler[] = { +#ifdef CONFIG_IPW2100_MONITOR + ipw2100_wx_set_promisc, + ipw2100_wx_reset, +#else /* CONFIG_IPW2100_MONITOR */ + NULL, + NULL, +#endif /* CONFIG_IPW2100_MONITOR */ + ipw2100_wx_set_powermode, + ipw2100_wx_get_powermode, + ipw2100_wx_set_preamble, + ipw2100_wx_get_preamble, +#ifdef CONFIG_IPW2100_MONITOR + ipw2100_wx_set_crc_check, + ipw2100_wx_get_crc_check, +#else /* CONFIG_IPW2100_MONITOR */ + NULL, + NULL, +#endif /* CONFIG_IPW2100_MONITOR */ +}; + +/* + * Get wireless statistics. + * Called by /proc/net/wireless + * Also called by SIOCGIWSTATS + */ +static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev) +{ + enum { + POOR = 30, + FAIR = 60, + GOOD = 80, + VERY_GOOD = 90, + EXCELLENT = 95, + PERFECT = 100 + }; + int rssi_qual; + int tx_qual; + int beacon_qual; + int quality; + + struct ipw2100_priv *priv = libipw_priv(dev); + struct iw_statistics *wstats; + u32 rssi, tx_retries, missed_beacons, tx_failures; + u32 ord_len = sizeof(u32); + + if (!priv) + return (struct iw_statistics *)NULL; + + wstats = &priv->wstats; + + /* if hw is disabled, then ipw2100_get_ordinal() can't be called. + * ipw2100_wx_wireless_stats seems to be called before fw is + * initialized. STATUS_ASSOCIATED will only be set if the hw is up + * and associated; if not associcated, the values are all meaningless + * anyway, so set them all to NULL and INVALID */ + if (!(priv->status & STATUS_ASSOCIATED)) { + wstats->miss.beacon = 0; + wstats->discard.retries = 0; + wstats->qual.qual = 0; + wstats->qual.level = 0; + wstats->qual.noise = 0; + wstats->qual.updated = 7; + wstats->qual.updated |= IW_QUAL_NOISE_INVALID | + IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; + return wstats; + } + + if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_MISSED_BCNS, + &missed_beacons, &ord_len)) + goto fail_get_ordinal; + + /* If we don't have a connection the quality and level is 0 */ + if (!(priv->status & STATUS_ASSOCIATED)) { + wstats->qual.qual = 0; + wstats->qual.level = 0; + } else { + if (ipw2100_get_ordinal(priv, IPW_ORD_RSSI_AVG_CURR, + &rssi, &ord_len)) + goto fail_get_ordinal; + wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM; + if (rssi < 10) + rssi_qual = rssi * POOR / 10; + else if (rssi < 15) + rssi_qual = (rssi - 10) * (FAIR - POOR) / 5 + POOR; + else if (rssi < 20) + rssi_qual = (rssi - 15) * (GOOD - FAIR) / 5 + FAIR; + else if (rssi < 30) + rssi_qual = (rssi - 20) * (VERY_GOOD - GOOD) / + 10 + GOOD; + else + rssi_qual = (rssi - 30) * (PERFECT - VERY_GOOD) / + 10 + VERY_GOOD; + + if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_RETRIES, + &tx_retries, &ord_len)) + goto fail_get_ordinal; + + if (tx_retries > 75) + tx_qual = (90 - tx_retries) * POOR / 15; + else if (tx_retries > 70) + tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR; + else if (tx_retries > 65) + tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR; + else if (tx_retries > 50) + tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) / + 15 + GOOD; + else + tx_qual = (50 - tx_retries) * + (PERFECT - VERY_GOOD) / 50 + VERY_GOOD; + + if (missed_beacons > 50) + beacon_qual = (60 - missed_beacons) * POOR / 10; + else if (missed_beacons > 40) + beacon_qual = (50 - missed_beacons) * (FAIR - POOR) / + 10 + POOR; + else if (missed_beacons > 32) + beacon_qual = (40 - missed_beacons) * (GOOD - FAIR) / + 18 + FAIR; + else if (missed_beacons > 20) + beacon_qual = (32 - missed_beacons) * + (VERY_GOOD - GOOD) / 20 + GOOD; + else + beacon_qual = (20 - missed_beacons) * + (PERFECT - VERY_GOOD) / 20 + VERY_GOOD; + + quality = min(tx_qual, rssi_qual); + quality = min(beacon_qual, quality); + +#ifdef CONFIG_IPW2100_DEBUG + if (beacon_qual == quality) + IPW_DEBUG_WX("Quality clamped by Missed Beacons\n"); + else if (tx_qual == quality) + IPW_DEBUG_WX("Quality clamped by Tx Retries\n"); + else if (quality != 100) + IPW_DEBUG_WX("Quality clamped by Signal Strength\n"); + else + IPW_DEBUG_WX("Quality not clamped.\n"); +#endif + + wstats->qual.qual = quality; + wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM; + } + + wstats->qual.noise = 0; + wstats->qual.updated = 7; + wstats->qual.updated |= IW_QUAL_NOISE_INVALID; + + /* FIXME: this is percent and not a # */ + wstats->miss.beacon = missed_beacons; + + if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURES, + &tx_failures, &ord_len)) + goto fail_get_ordinal; + wstats->discard.retries = tx_failures; + + return wstats; + + fail_get_ordinal: + IPW_DEBUG_WX("failed querying ordinals.\n"); + + return (struct iw_statistics *)NULL; +} + +static struct iw_handler_def ipw2100_wx_handler_def = { + .standard = ipw2100_wx_handlers, + .num_standard = ARRAY_SIZE(ipw2100_wx_handlers), + .num_private = ARRAY_SIZE(ipw2100_private_handler), + .num_private_args = ARRAY_SIZE(ipw2100_private_args), + .private = (iw_handler *) ipw2100_private_handler, + .private_args = (struct iw_priv_args *)ipw2100_private_args, + .get_wireless_stats = ipw2100_wx_wireless_stats, +}; + +static void ipw2100_wx_event_work(struct work_struct *work) +{ + struct ipw2100_priv *priv = + container_of(work, struct ipw2100_priv, wx_event_work.work); + union iwreq_data wrqu; + unsigned int len = ETH_ALEN; + + if (priv->status & STATUS_STOPPING) + return; + + mutex_lock(&priv->action_mutex); + + IPW_DEBUG_WX("enter\n"); + + mutex_unlock(&priv->action_mutex); + + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* Fetch BSSID from the hardware */ + if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) || + priv->status & STATUS_RF_KILL_MASK || + ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, + &priv->bssid, &len)) { + eth_zero_addr(wrqu.ap_addr.sa_data); + } else { + /* We now have the BSSID, so can finish setting to the full + * associated state */ + memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN); + memcpy(priv->ieee->bssid, priv->bssid, ETH_ALEN); + priv->status &= ~STATUS_ASSOCIATING; + priv->status |= STATUS_ASSOCIATED; + netif_carrier_on(priv->net_dev); + netif_wake_queue(priv->net_dev); + } + + if (!(priv->status & STATUS_ASSOCIATED)) { + IPW_DEBUG_WX("Configuring ESSID\n"); + mutex_lock(&priv->action_mutex); + /* This is a disassociation event, so kick the firmware to + * look for another AP */ + if (priv->config & CFG_STATIC_ESSID) + ipw2100_set_essid(priv, priv->essid, priv->essid_len, + 0); + else + ipw2100_set_essid(priv, NULL, 0, 0); + mutex_unlock(&priv->action_mutex); + } + + wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); +} + +#define IPW2100_FW_MAJOR_VERSION 1 +#define IPW2100_FW_MINOR_VERSION 3 + +#define IPW2100_FW_MINOR(x) ((x & 0xff) >> 8) +#define IPW2100_FW_MAJOR(x) (x & 0xff) + +#define IPW2100_FW_VERSION ((IPW2100_FW_MINOR_VERSION << 8) | \ + IPW2100_FW_MAJOR_VERSION) + +#define IPW2100_FW_PREFIX "ipw2100-" __stringify(IPW2100_FW_MAJOR_VERSION) \ +"." __stringify(IPW2100_FW_MINOR_VERSION) + +#define IPW2100_FW_NAME(x) IPW2100_FW_PREFIX "" x ".fw" + +/* + +BINARY FIRMWARE HEADER FORMAT + +offset length desc +0 2 version +2 2 mode == 0:BSS,1:IBSS,2:MONITOR +4 4 fw_len +8 4 uc_len +C fw_len firmware data +12 + fw_len uc_len microcode data + +*/ + +struct ipw2100_fw_header { + short version; + short mode; + unsigned int fw_size; + unsigned int uc_size; +} __packed; + +static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw) +{ + struct ipw2100_fw_header *h = + (struct ipw2100_fw_header *)fw->fw_entry->data; + + if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) { + printk(KERN_WARNING DRV_NAME ": Firmware image not compatible " + "(detected version id of %u). " + "See Documentation/networking/README.ipw2100\n", + h->version); + return 1; + } + + fw->version = h->version; + fw->fw.data = fw->fw_entry->data + sizeof(struct ipw2100_fw_header); + fw->fw.size = h->fw_size; + fw->uc.data = fw->fw.data + h->fw_size; + fw->uc.size = h->uc_size; + + return 0; +} + +static int ipw2100_get_firmware(struct ipw2100_priv *priv, + struct ipw2100_fw *fw) +{ + char *fw_name; + int rc; + + IPW_DEBUG_INFO("%s: Using hotplug firmware load.\n", + priv->net_dev->name); + + switch (priv->ieee->iw_mode) { + case IW_MODE_ADHOC: + fw_name = IPW2100_FW_NAME("-i"); + break; +#ifdef CONFIG_IPW2100_MONITOR + case IW_MODE_MONITOR: + fw_name = IPW2100_FW_NAME("-p"); + break; +#endif + case IW_MODE_INFRA: + default: + fw_name = IPW2100_FW_NAME(""); + break; + } + + rc = request_firmware(&fw->fw_entry, fw_name, &priv->pci_dev->dev); + + if (rc < 0) { + printk(KERN_ERR DRV_NAME ": " + "%s: Firmware '%s' not available or load failed.\n", + priv->net_dev->name, fw_name); + return rc; + } + IPW_DEBUG_INFO("firmware data %p size %zd\n", fw->fw_entry->data, + fw->fw_entry->size); + + ipw2100_mod_firmware_load(fw); + + return 0; +} + +MODULE_FIRMWARE(IPW2100_FW_NAME("-i")); +#ifdef CONFIG_IPW2100_MONITOR +MODULE_FIRMWARE(IPW2100_FW_NAME("-p")); +#endif +MODULE_FIRMWARE(IPW2100_FW_NAME("")); + +static void ipw2100_release_firmware(struct ipw2100_priv *priv, + struct ipw2100_fw *fw) +{ + fw->version = 0; + release_firmware(fw->fw_entry); + fw->fw_entry = NULL; +} + +static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf, + size_t max) +{ + char ver[MAX_FW_VERSION_LEN]; + u32 len = MAX_FW_VERSION_LEN; + u32 tmp; + int i; + /* firmware version is an ascii string (max len of 14) */ + if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_FW_VER_NUM, ver, &len)) + return -EIO; + tmp = max; + if (len >= max) + len = max - 1; + for (i = 0; i < len; i++) + buf[i] = ver[i]; + buf[i] = '\0'; + return tmp; +} + +static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf, + size_t max) +{ + u32 ver; + u32 len = sizeof(ver); + /* microcode version is a 32 bit integer */ + if (ipw2100_get_ordinal(priv, IPW_ORD_UCODE_VERSION, &ver, &len)) + return -EIO; + return snprintf(buf, max, "%08X", ver); +} + +/* + * On exit, the firmware will have been freed from the fw list + */ +static int ipw2100_fw_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw) +{ + /* firmware is constructed of N contiguous entries, each entry is + * structured as: + * + * offset sie desc + * 0 4 address to write to + * 4 2 length of data run + * 6 length data + */ + unsigned int addr; + unsigned short len; + + const unsigned char *firmware_data = fw->fw.data; + unsigned int firmware_data_left = fw->fw.size; + + while (firmware_data_left > 0) { + addr = *(u32 *) (firmware_data); + firmware_data += 4; + firmware_data_left -= 4; + + len = *(u16 *) (firmware_data); + firmware_data += 2; + firmware_data_left -= 2; + + if (len > 32) { + printk(KERN_ERR DRV_NAME ": " + "Invalid firmware run-length of %d bytes\n", + len); + return -EINVAL; + } + + write_nic_memory(priv->net_dev, addr, len, firmware_data); + firmware_data += len; + firmware_data_left -= len; + } + + return 0; +} + +struct symbol_alive_response { + u8 cmd_id; + u8 seq_num; + u8 ucode_rev; + u8 eeprom_valid; + u16 valid_flags; + u8 IEEE_addr[6]; + u16 flags; + u16 pcb_rev; + u16 clock_settle_time; // 1us LSB + u16 powerup_settle_time; // 1us LSB + u16 hop_settle_time; // 1us LSB + u8 date[3]; // month, day, year + u8 time[2]; // hours, minutes + u8 ucode_valid; +}; + +static int ipw2100_ucode_download(struct ipw2100_priv *priv, + struct ipw2100_fw *fw) +{ + struct net_device *dev = priv->net_dev; + const unsigned char *microcode_data = fw->uc.data; + unsigned int microcode_data_left = fw->uc.size; + void __iomem *reg = priv->ioaddr; + + struct symbol_alive_response response; + int i, j; + u8 data; + + /* Symbol control */ + write_nic_word(dev, IPW2100_CONTROL_REG, 0x703); + readl(reg); + write_nic_word(dev, IPW2100_CONTROL_REG, 0x707); + readl(reg); + + /* HW config */ + write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */ + readl(reg); + write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */ + readl(reg); + + /* EN_CS_ACCESS bit to reset control store pointer */ + write_nic_byte(dev, 0x210000, 0x40); + readl(reg); + write_nic_byte(dev, 0x210000, 0x0); + readl(reg); + write_nic_byte(dev, 0x210000, 0x40); + readl(reg); + + /* copy microcode from buffer into Symbol */ + + while (microcode_data_left > 0) { + write_nic_byte(dev, 0x210010, *microcode_data++); + write_nic_byte(dev, 0x210010, *microcode_data++); + microcode_data_left -= 2; + } + + /* EN_CS_ACCESS bit to reset the control store pointer */ + write_nic_byte(dev, 0x210000, 0x0); + readl(reg); + + /* Enable System (Reg 0) + * first enable causes garbage in RX FIFO */ + write_nic_byte(dev, 0x210000, 0x0); + readl(reg); + write_nic_byte(dev, 0x210000, 0x80); + readl(reg); + + /* Reset External Baseband Reg */ + write_nic_word(dev, IPW2100_CONTROL_REG, 0x703); + readl(reg); + write_nic_word(dev, IPW2100_CONTROL_REG, 0x707); + readl(reg); + + /* HW Config (Reg 5) */ + write_nic_byte(dev, 0x210014, 0x72); // fifo width =16 + readl(reg); + write_nic_byte(dev, 0x210014, 0x72); // fifo width =16 + readl(reg); + + /* Enable System (Reg 0) + * second enable should be OK */ + write_nic_byte(dev, 0x210000, 0x00); // clear enable system + readl(reg); + write_nic_byte(dev, 0x210000, 0x80); // set enable system + + /* check Symbol is enabled - upped this from 5 as it wasn't always + * catching the update */ + for (i = 0; i < 10; i++) { + udelay(10); + + /* check Dino is enabled bit */ + read_nic_byte(dev, 0x210000, &data); + if (data & 0x1) + break; + } + + if (i == 10) { + printk(KERN_ERR DRV_NAME ": %s: Error initializing Symbol\n", + dev->name); + return -EIO; + } + + /* Get Symbol alive response */ + for (i = 0; i < 30; i++) { + /* Read alive response structure */ + for (j = 0; + j < (sizeof(struct symbol_alive_response) >> 1); j++) + read_nic_word(dev, 0x210004, ((u16 *) & response) + j); + + if ((response.cmd_id == 1) && (response.ucode_valid == 0x1)) + break; + udelay(10); + } + + if (i == 30) { + printk(KERN_ERR DRV_NAME + ": %s: No response from Symbol - hw not alive\n", + dev->name); + printk_buf(IPW_DL_ERROR, (u8 *) & response, sizeof(response)); + return -EIO; + } + + return 0; +} diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.h b/drivers/net/wireless/intel/ipw2x00/ipw2100.h new file mode 100644 index 000000000000..193947865efd --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.h @@ -0,0 +1,1156 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Intel Linux Wireless + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ +#ifndef _IPW2100_H +#define _IPW2100_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // new driver API + +#ifdef CONFIG_IPW2100_MONITOR +#include +#endif + +#include +#include + +#include "libipw.h" + +struct ipw2100_priv; +struct ipw2100_tx_packet; +struct ipw2100_rx_packet; + +#define IPW_DL_UNINIT 0x80000000 +#define IPW_DL_NONE 0x00000000 +#define IPW_DL_ALL 0x7FFFFFFF + +/* + * To use the debug system; + * + * If you are defining a new debug classification, simply add it to the #define + * list here in the form of: + * + * #define IPW_DL_xxxx VALUE + * + * shifting value to the left one bit from the previous entry. xxxx should be + * the name of the classification (for example, WEP) + * + * You then need to either add a IPW2100_xxxx_DEBUG() macro definition for your + * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want + * to send output to that classification. + * + * To add your debug level to the list of levels seen when you perform + * + * % cat /proc/net/ipw2100/debug_level + * + * you simply need to add your entry to the ipw2100_debug_levels array. + * + * If you do not see debug_level in /proc/net/ipw2100 then you do not have + * CONFIG_IPW2100_DEBUG defined in your kernel configuration + * + */ + +#define IPW_DL_ERROR (1<<0) +#define IPW_DL_WARNING (1<<1) +#define IPW_DL_INFO (1<<2) +#define IPW_DL_WX (1<<3) +#define IPW_DL_HC (1<<5) +#define IPW_DL_STATE (1<<6) + +#define IPW_DL_NOTIF (1<<10) +#define IPW_DL_SCAN (1<<11) +#define IPW_DL_ASSOC (1<<12) +#define IPW_DL_DROP (1<<13) + +#define IPW_DL_IOCTL (1<<14) +#define IPW_DL_RF_KILL (1<<17) + +#define IPW_DL_MANAGE (1<<15) +#define IPW_DL_FW (1<<16) + +#define IPW_DL_FRAG (1<<21) +#define IPW_DL_WEP (1<<22) +#define IPW_DL_TX (1<<23) +#define IPW_DL_RX (1<<24) +#define IPW_DL_ISR (1<<25) +#define IPW_DL_IO (1<<26) +#define IPW_DL_TRACE (1<<28) + +#define IPW_DEBUG_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a) +#define IPW_DEBUG_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a) +#define IPW_DEBUG_INFO(f...) IPW_DEBUG(IPW_DL_INFO, ## f) +#define IPW_DEBUG_WX(f...) IPW_DEBUG(IPW_DL_WX, ## f) +#define IPW_DEBUG_SCAN(f...) IPW_DEBUG(IPW_DL_SCAN, ## f) +#define IPW_DEBUG_NOTIF(f...) IPW_DEBUG(IPW_DL_NOTIF, ## f) +#define IPW_DEBUG_TRACE(f...) IPW_DEBUG(IPW_DL_TRACE, ## f) +#define IPW_DEBUG_RX(f...) IPW_DEBUG(IPW_DL_RX, ## f) +#define IPW_DEBUG_TX(f...) IPW_DEBUG(IPW_DL_TX, ## f) +#define IPW_DEBUG_ISR(f...) IPW_DEBUG(IPW_DL_ISR, ## f) +#define IPW_DEBUG_MANAGEMENT(f...) IPW_DEBUG(IPW_DL_MANAGE, ## f) +#define IPW_DEBUG_WEP(f...) IPW_DEBUG(IPW_DL_WEP, ## f) +#define IPW_DEBUG_HC(f...) IPW_DEBUG(IPW_DL_HC, ## f) +#define IPW_DEBUG_FRAG(f...) IPW_DEBUG(IPW_DL_FRAG, ## f) +#define IPW_DEBUG_FW(f...) IPW_DEBUG(IPW_DL_FW, ## f) +#define IPW_DEBUG_RF_KILL(f...) IPW_DEBUG(IPW_DL_RF_KILL, ## f) +#define IPW_DEBUG_DROP(f...) IPW_DEBUG(IPW_DL_DROP, ## f) +#define IPW_DEBUG_IO(f...) IPW_DEBUG(IPW_DL_IO, ## f) +#define IPW_DEBUG_IOCTL(f...) IPW_DEBUG(IPW_DL_IOCTL, ## f) +#define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) +#define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) + +enum { + IPW_HW_STATE_DISABLED = 1, + IPW_HW_STATE_ENABLED = 0 +}; + +extern const char *port_type_str[]; +extern const char *band_str[]; + +#define NUMBER_OF_BD_PER_COMMAND_PACKET 1 +#define NUMBER_OF_BD_PER_DATA_PACKET 2 + +#define IPW_MAX_BDS 6 +#define NUMBER_OF_OVERHEAD_BDS_PER_PACKETR 2 +#define NUMBER_OF_BDS_TO_LEAVE_FOR_COMMANDS 1 + +#define REQUIRED_SPACE_IN_RING_FOR_COMMAND_PACKET \ + (IPW_BD_QUEUE_W_R_MIN_SPARE + NUMBER_OF_BD_PER_COMMAND_PACKET) + +struct bd_status { + union { + struct { + u8 nlf:1, txType:2, intEnabled:1, reserved:4; + } fields; + u8 field; + } info; +} __packed; + +struct ipw2100_bd { + u32 host_addr; + u32 buf_length; + struct bd_status status; + /* number of fragments for frame (should be set only for + * 1st TBD) */ + u8 num_fragments; + u8 reserved[6]; +} __packed; + +#define IPW_BD_QUEUE_LENGTH(n) (1<value = (x)->hi = 0; \ + (x)->lo = 0x7fffffff; \ +} while (0) +#define SET_STAT(x,y) do { \ + (x)->value = y; \ + if ((x)->value > (x)->hi) (x)->hi = (x)->value; \ + if ((x)->value < (x)->lo) (x)->lo = (x)->value; \ +} while (0) +#define INC_STAT(x) do { if (++(x)->value > (x)->hi) (x)->hi = (x)->value; } \ +while (0) +#define DEC_STAT(x) do { if (--(x)->value < (x)->lo) (x)->lo = (x)->value; } \ +while (0) + +#define IPW2100_ERROR_QUEUE 5 + +/* Power management code: enable or disable? */ +enum { +#ifdef CONFIG_PM + IPW2100_PM_DISABLED = 0, + PM_STATE_SIZE = 16, +#else + IPW2100_PM_DISABLED = 1, + PM_STATE_SIZE = 0, +#endif +}; + +#define STATUS_POWERED (1<<0) +#define STATUS_CMD_ACTIVE (1<<1) /**< host command in progress */ +#define STATUS_RUNNING (1<<2) /* Card initialized, but not enabled */ +#define STATUS_ENABLED (1<<3) /* Card enabled -- can scan,Tx,Rx */ +#define STATUS_STOPPING (1<<4) /* Card is in shutdown phase */ +#define STATUS_INITIALIZED (1<<5) /* Card is ready for external calls */ +#define STATUS_ASSOCIATING (1<<9) /* Associated, but no BSSID yet */ +#define STATUS_ASSOCIATED (1<<10) /* Associated and BSSID valid */ +#define STATUS_INT_ENABLED (1<<11) +#define STATUS_RF_KILL_HW (1<<12) +#define STATUS_RF_KILL_SW (1<<13) +#define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW) +#define STATUS_EXIT_PENDING (1<<14) + +#define STATUS_SCAN_PENDING (1<<23) +#define STATUS_SCANNING (1<<24) +#define STATUS_SCAN_ABORTING (1<<25) +#define STATUS_SCAN_COMPLETE (1<<26) +#define STATUS_WX_EVENT_PENDING (1<<27) +#define STATUS_RESET_PENDING (1<<29) +#define STATUS_SECURITY_UPDATED (1<<30) /* Security sync needed */ + +/* Internal NIC states */ +#define IPW_STATE_INITIALIZED (1<<0) +#define IPW_STATE_COUNTRY_FOUND (1<<1) +#define IPW_STATE_ASSOCIATED (1<<2) +#define IPW_STATE_ASSN_LOST (1<<3) +#define IPW_STATE_ASSN_CHANGED (1<<4) +#define IPW_STATE_SCAN_COMPLETE (1<<5) +#define IPW_STATE_ENTERED_PSP (1<<6) +#define IPW_STATE_LEFT_PSP (1<<7) +#define IPW_STATE_RF_KILL (1<<8) +#define IPW_STATE_DISABLED (1<<9) +#define IPW_STATE_POWER_DOWN (1<<10) +#define IPW_STATE_SCANNING (1<<11) + +#define CFG_STATIC_CHANNEL (1<<0) /* Restrict assoc. to single channel */ +#define CFG_STATIC_ESSID (1<<1) /* Restrict assoc. to single SSID */ +#define CFG_STATIC_BSSID (1<<2) /* Restrict assoc. to single BSSID */ +#define CFG_CUSTOM_MAC (1<<3) +#define CFG_LONG_PREAMBLE (1<<4) +#define CFG_ASSOCIATE (1<<6) +#define CFG_FIXED_RATE (1<<7) +#define CFG_ADHOC_CREATE (1<<8) +#define CFG_PASSIVE_SCAN (1<<10) +#ifdef CONFIG_IPW2100_MONITOR +#define CFG_CRC_CHECK (1<<11) +#endif + +#define CAP_SHARED_KEY (1<<0) /* Off = OPEN */ +#define CAP_PRIVACY_ON (1<<1) /* Off = No privacy */ + +struct ipw2100_priv { + void __iomem *ioaddr; + + int stop_hang_check; /* Set 1 when shutting down to kill hang_check */ + int stop_rf_kill; /* Set 1 when shutting down to kill rf_kill */ + + struct libipw_device *ieee; + unsigned long status; + unsigned long config; + unsigned long capability; + + /* Statistics */ + int resets; + int reset_backoff; + + /* Context */ + u8 essid[IW_ESSID_MAX_SIZE]; + u8 essid_len; + u8 bssid[ETH_ALEN]; + u8 channel; + int last_mode; + + unsigned long connect_start; + unsigned long last_reset; + + u32 channel_mask; + u32 fatal_error; + u32 fatal_errors[IPW2100_ERROR_QUEUE]; + u32 fatal_index; + int eeprom_version; + int firmware_version; + unsigned long hw_features; + int hangs; + u32 last_rtc; + int dump_raw; /* 1 to dump raw bytes in /sys/.../memory */ + u8 *snapshot[0x30]; + + u8 mandatory_bssid_mac[ETH_ALEN]; + u8 mac_addr[ETH_ALEN]; + + int power_mode; + + int messages_sent; + + int short_retry_limit; + int long_retry_limit; + + u32 rts_threshold; + u32 frag_threshold; + + int in_isr; + + u32 tx_rates; + int tx_power; + u32 beacon_interval; + + char nick[IW_ESSID_MAX_SIZE + 1]; + + struct ipw2100_status_queue status_queue; + + struct statistic txq_stat; + struct statistic rxq_stat; + struct ipw2100_bd_queue rx_queue; + struct ipw2100_bd_queue tx_queue; + struct ipw2100_rx_packet *rx_buffers; + + struct statistic fw_pend_stat; + struct list_head fw_pend_list; + + struct statistic msg_free_stat; + struct statistic msg_pend_stat; + struct list_head msg_free_list; + struct list_head msg_pend_list; + struct ipw2100_tx_packet *msg_buffers; + + struct statistic tx_free_stat; + struct statistic tx_pend_stat; + struct list_head tx_free_list; + struct list_head tx_pend_list; + struct ipw2100_tx_packet *tx_buffers; + + struct ipw2100_ordinals ordinals; + + struct pci_dev *pci_dev; + + struct proc_dir_entry *dir_dev; + + struct net_device *net_dev; + struct iw_statistics wstats; + + struct iw_public_data wireless_data; + + struct tasklet_struct irq_tasklet; + + struct delayed_work reset_work; + struct delayed_work security_work; + struct delayed_work wx_event_work; + struct delayed_work hang_check; + struct delayed_work rf_kill; + struct delayed_work scan_event; + + int user_requested_scan; + + /* Track time in suspend */ + unsigned long suspend_at; + unsigned long suspend_time; + + u32 interrupts; + int tx_interrupts; + int rx_interrupts; + int inta_other; + + spinlock_t low_lock; + struct mutex action_mutex; + struct mutex adapter_mutex; + + wait_queue_head_t wait_command_queue; +}; + +/********************************************************* + * Host Command -> From Driver to FW + *********************************************************/ + +/** + * Host command identifiers + */ +#define HOST_COMPLETE 2 +#define SYSTEM_CONFIG 6 +#define SSID 8 +#define MANDATORY_BSSID 9 +#define AUTHENTICATION_TYPE 10 +#define ADAPTER_ADDRESS 11 +#define PORT_TYPE 12 +#define INTERNATIONAL_MODE 13 +#define CHANNEL 14 +#define RTS_THRESHOLD 15 +#define FRAG_THRESHOLD 16 +#define POWER_MODE 17 +#define TX_RATES 18 +#define BASIC_TX_RATES 19 +#define WEP_KEY_INFO 20 +#define WEP_KEY_INDEX 25 +#define WEP_FLAGS 26 +#define ADD_MULTICAST 27 +#define CLEAR_ALL_MULTICAST 28 +#define BEACON_INTERVAL 29 +#define ATIM_WINDOW 30 +#define CLEAR_STATISTICS 31 +#define SEND 33 +#define TX_POWER_INDEX 36 +#define BROADCAST_SCAN 43 +#define CARD_DISABLE 44 +#define PREFERRED_BSSID 45 +#define SET_SCAN_OPTIONS 46 +#define SCAN_DWELL_TIME 47 +#define SWEEP_TABLE 48 +#define AP_OR_STATION_TABLE 49 +#define GROUP_ORDINALS 50 +#define SHORT_RETRY_LIMIT 51 +#define LONG_RETRY_LIMIT 52 + +#define HOST_PRE_POWER_DOWN 58 +#define CARD_DISABLE_PHY_OFF 61 +#define MSDU_TX_RATES 62 + +/* Rogue AP Detection */ +#define SET_STATION_STAT_BITS 64 +#define CLEAR_STATIONS_STAT_BITS 65 +#define LEAP_ROGUE_MODE 66 //TODO tbw replaced by CFG_LEAP_ROGUE_AP +#define SET_SECURITY_INFORMATION 67 +#define DISASSOCIATION_BSSID 68 +#define SET_WPA_IE 69 + +/* system configuration bit mask: */ +#define IPW_CFG_MONITOR 0x00004 +#define IPW_CFG_PREAMBLE_AUTO 0x00010 +#define IPW_CFG_IBSS_AUTO_START 0x00020 +#define IPW_CFG_LOOPBACK 0x00100 +#define IPW_CFG_ANSWER_BCSSID_PROBE 0x00800 +#define IPW_CFG_BT_SIDEBAND_SIGNAL 0x02000 +#define IPW_CFG_802_1x_ENABLE 0x04000 +#define IPW_CFG_BSS_MASK 0x08000 +#define IPW_CFG_IBSS_MASK 0x10000 + +#define IPW_SCAN_NOASSOCIATE (1<<0) +#define IPW_SCAN_MIXED_CELL (1<<1) +/* RESERVED (1<<2) */ +#define IPW_SCAN_PASSIVE (1<<3) + +#define IPW_NIC_FATAL_ERROR 0x2A7F0 +#define IPW_ERROR_ADDR(x) (x & 0x3FFFF) +#define IPW_ERROR_CODE(x) ((x & 0xFF000000) >> 24) +#define IPW2100_ERR_C3_CORRUPTION (0x10 << 24) +#define IPW2100_ERR_MSG_TIMEOUT (0x11 << 24) +#define IPW2100_ERR_FW_LOAD (0x12 << 24) + +#define IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND 0x200 +#define IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x0D80 + +#define IPW_MEM_HOST_SHARED_RX_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x40) +#define IPW_MEM_HOST_SHARED_RX_STATUS_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x44) +#define IPW_MEM_HOST_SHARED_RX_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x48) +#define IPW_MEM_HOST_SHARED_RX_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0xa0) + +#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x00) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x04) +#define IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x80) + +#define IPW_MEM_HOST_SHARED_RX_WRITE_INDEX \ + (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND + 0x20) + +#define IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX \ + (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND) + +#define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1 (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x180) +#define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2 (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x184) + +#define IPW2100_INTA_TX_TRANSFER (0x00000001) // Bit 0 (LSB) +#define IPW2100_INTA_RX_TRANSFER (0x00000002) // Bit 1 +#define IPW2100_INTA_TX_COMPLETE (0x00000004) // Bit 2 +#define IPW2100_INTA_EVENT_INTERRUPT (0x00000008) // Bit 3 +#define IPW2100_INTA_STATUS_CHANGE (0x00000010) // Bit 4 +#define IPW2100_INTA_BEACON_PERIOD_EXPIRED (0x00000020) // Bit 5 +#define IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE (0x00010000) // Bit 16 +#define IPW2100_INTA_FW_INIT_DONE (0x01000000) // Bit 24 +#define IPW2100_INTA_FW_CALIBRATION_CALC (0x02000000) // Bit 25 +#define IPW2100_INTA_FATAL_ERROR (0x40000000) // Bit 30 +#define IPW2100_INTA_PARITY_ERROR (0x80000000) // Bit 31 (MSB) + +#define IPW_AUX_HOST_RESET_REG_PRINCETON_RESET (0x00000001) +#define IPW_AUX_HOST_RESET_REG_FORCE_NMI (0x00000002) +#define IPW_AUX_HOST_RESET_REG_PCI_HOST_CLUSTER_FATAL_NMI (0x00000004) +#define IPW_AUX_HOST_RESET_REG_CORE_FATAL_NMI (0x00000008) +#define IPW_AUX_HOST_RESET_REG_SW_RESET (0x00000080) +#define IPW_AUX_HOST_RESET_REG_MASTER_DISABLED (0x00000100) +#define IPW_AUX_HOST_RESET_REG_STOP_MASTER (0x00000200) + +#define IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY (0x00000001) // Bit 0 (LSB) +#define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY (0x00000002) // Bit 1 +#define IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE (0x00000004) // Bit 2 +#define IPW_AUX_HOST_GP_CNTRL_BITS_SYS_CONFIG (0x000007c0) // Bits 6-10 +#define IPW_AUX_HOST_GP_CNTRL_BIT_BUS_TYPE (0x00000200) // Bit 9 +#define IPW_AUX_HOST_GP_CNTRL_BIT_BAR0_BLOCK_SIZE (0x00000400) // Bit 10 +#define IPW_AUX_HOST_GP_CNTRL_BIT_USB_MODE (0x20000000) // Bit 29 +#define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_FORCES_SYS_CLK (0x40000000) // Bit 30 +#define IPW_AUX_HOST_GP_CNTRL_BIT_FW_FORCES_SYS_CLK (0x80000000) // Bit 31 (MSB) + +#define IPW_BIT_GPIO_GPIO1_MASK 0x0000000C +#define IPW_BIT_GPIO_GPIO3_MASK 0x000000C0 +#define IPW_BIT_GPIO_GPIO1_ENABLE 0x00000008 +#define IPW_BIT_GPIO_RF_KILL 0x00010000 + +#define IPW_BIT_GPIO_LED_OFF 0x00002000 // Bit 13 = 1 + +#define IPW_REG_DOMAIN_0_OFFSET 0x0000 +#define IPW_REG_DOMAIN_1_OFFSET IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + +#define IPW_REG_INTA IPW_REG_DOMAIN_0_OFFSET + 0x0008 +#define IPW_REG_INTA_MASK IPW_REG_DOMAIN_0_OFFSET + 0x000C +#define IPW_REG_INDIRECT_ACCESS_ADDRESS IPW_REG_DOMAIN_0_OFFSET + 0x0010 +#define IPW_REG_INDIRECT_ACCESS_DATA IPW_REG_DOMAIN_0_OFFSET + 0x0014 +#define IPW_REG_AUTOINCREMENT_ADDRESS IPW_REG_DOMAIN_0_OFFSET + 0x0018 +#define IPW_REG_AUTOINCREMENT_DATA IPW_REG_DOMAIN_0_OFFSET + 0x001C +#define IPW_REG_RESET_REG IPW_REG_DOMAIN_0_OFFSET + 0x0020 +#define IPW_REG_GP_CNTRL IPW_REG_DOMAIN_0_OFFSET + 0x0024 +#define IPW_REG_GPIO IPW_REG_DOMAIN_0_OFFSET + 0x0030 +#define IPW_REG_FW_TYPE IPW_REG_DOMAIN_1_OFFSET + 0x0188 +#define IPW_REG_FW_VERSION IPW_REG_DOMAIN_1_OFFSET + 0x018C +#define IPW_REG_FW_COMPATIBILITY_VERSION IPW_REG_DOMAIN_1_OFFSET + 0x0190 + +#define IPW_REG_INDIRECT_ADDR_MASK 0x00FFFFFC + +#define IPW_INTERRUPT_MASK 0xC1010013 + +#define IPW2100_CONTROL_REG 0x220000 +#define IPW2100_CONTROL_PHY_OFF 0x8 + +#define IPW2100_COMMAND 0x00300004 +#define IPW2100_COMMAND_PHY_ON 0x0 +#define IPW2100_COMMAND_PHY_OFF 0x1 + +/* in DEBUG_AREA, values of memory always 0xd55555d5 */ +#define IPW_REG_DOA_DEBUG_AREA_START IPW_REG_DOMAIN_0_OFFSET + 0x0090 +#define IPW_REG_DOA_DEBUG_AREA_END IPW_REG_DOMAIN_0_OFFSET + 0x00FF +#define IPW_DATA_DOA_DEBUG_VALUE 0xd55555d5 + +#define IPW_INTERNAL_REGISTER_HALT_AND_RESET 0x003000e0 + +#define IPW_WAIT_CLOCK_STABILIZATION_DELAY 50 // micro seconds +#define IPW_WAIT_RESET_ARC_COMPLETE_DELAY 10 // micro seconds +#define IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY 10 // micro seconds + +// BD ring queue read/write difference +#define IPW_BD_QUEUE_W_R_MIN_SPARE 2 + +#define IPW_CACHE_LINE_LENGTH_DEFAULT 0x80 + +#define IPW_CARD_DISABLE_PHY_OFF_COMPLETE_WAIT 100 // 100 milli +#define IPW_PREPARE_POWER_DOWN_COMPLETE_WAIT 100 // 100 milli + +#define IPW_HEADER_802_11_SIZE sizeof(struct libipw_hdr_3addr) +#define IPW_MAX_80211_PAYLOAD_SIZE 2304U +#define IPW_MAX_802_11_PAYLOAD_LENGTH 2312 +#define IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH 1536 +#define IPW_MIN_ACCEPTABLE_RX_FRAME_LENGTH 60 +#define IPW_MAX_ACCEPTABLE_RX_FRAME_LENGTH \ + (IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH + IPW_HEADER_802_11_SIZE - \ + sizeof(struct ethhdr)) + +#define IPW_802_11_FCS_LENGTH 4 +#define IPW_RX_NIC_BUFFER_LENGTH \ + (IPW_MAX_802_11_PAYLOAD_LENGTH + IPW_HEADER_802_11_SIZE + \ + IPW_802_11_FCS_LENGTH) + +#define IPW_802_11_PAYLOAD_OFFSET \ + (sizeof(struct libipw_hdr_3addr) + \ + sizeof(struct libipw_snap_hdr)) + +struct ipw2100_rx { + union { + unsigned char payload[IPW_RX_NIC_BUFFER_LENGTH]; + struct libipw_hdr_4addr header; + u32 status; + struct ipw2100_notification notification; + struct ipw2100_cmd_header command; + } rx_data; +} __packed; + +/* Bit 0-7 are for 802.11b tx rates - . Bit 5-7 are reserved */ +#define TX_RATE_1_MBIT 0x0001 +#define TX_RATE_2_MBIT 0x0002 +#define TX_RATE_5_5_MBIT 0x0004 +#define TX_RATE_11_MBIT 0x0008 +#define TX_RATE_MASK 0x000F +#define DEFAULT_TX_RATES 0x000F + +#define IPW_POWER_MODE_CAM 0x00 //(always on) +#define IPW_POWER_INDEX_1 0x01 +#define IPW_POWER_INDEX_2 0x02 +#define IPW_POWER_INDEX_3 0x03 +#define IPW_POWER_INDEX_4 0x04 +#define IPW_POWER_INDEX_5 0x05 +#define IPW_POWER_AUTO 0x06 +#define IPW_POWER_MASK 0x0F +#define IPW_POWER_ENABLED 0x10 +#define IPW_POWER_LEVEL(x) ((x) & IPW_POWER_MASK) + +#define IPW_TX_POWER_AUTO 0 +#define IPW_TX_POWER_ENHANCED 1 + +#define IPW_TX_POWER_DEFAULT 32 +#define IPW_TX_POWER_MIN 0 +#define IPW_TX_POWER_MAX 16 +#define IPW_TX_POWER_MIN_DBM (-12) +#define IPW_TX_POWER_MAX_DBM 16 + +#define FW_SCAN_DONOT_ASSOCIATE 0x0001 // Dont Attempt to Associate after Scan +#define FW_SCAN_PASSIVE 0x0008 // Force PASSSIVE Scan + +#define REG_MIN_CHANNEL 0 +#define REG_MAX_CHANNEL 14 + +#define REG_CHANNEL_MASK 0x00003FFF +#define IPW_IBSS_11B_DEFAULT_MASK 0x87ff + +#define DIVERSITY_EITHER 0 // Use both antennas +#define DIVERSITY_ANTENNA_A 1 // Use antenna A +#define DIVERSITY_ANTENNA_B 2 // Use antenna B + +#define HOST_COMMAND_WAIT 0 +#define HOST_COMMAND_NO_WAIT 1 + +#define LOCK_NONE 0 +#define LOCK_DRIVER 1 +#define LOCK_FW 2 + +#define TYPE_SWEEP_ORD 0x000D +#define TYPE_IBSS_STTN_ORD 0x000E +#define TYPE_BSS_AP_ORD 0x000F +#define TYPE_RAW_BEACON_ENTRY 0x0010 +#define TYPE_CALIBRATION_DATA 0x0011 +#define TYPE_ROGUE_AP_DATA 0x0012 +#define TYPE_ASSOCIATION_REQUEST 0x0013 +#define TYPE_REASSOCIATION_REQUEST 0x0014 + +#define HW_FEATURE_RFKILL 0x0001 +#define RF_KILLSWITCH_OFF 1 +#define RF_KILLSWITCH_ON 0 + +#define IPW_COMMAND_POOL_SIZE 40 + +#define IPW_START_ORD_TAB_1 1 +#define IPW_START_ORD_TAB_2 1000 + +#define IPW_ORD_TAB_1_ENTRY_SIZE sizeof(u32) + +#define IS_ORDINAL_TABLE_ONE(mgr,id) \ + ((id >= IPW_START_ORD_TAB_1) && (id < mgr->table1_size)) +#define IS_ORDINAL_TABLE_TWO(mgr,id) \ + ((id >= IPW_START_ORD_TAB_2) && (id < (mgr->table2_size + IPW_START_ORD_TAB_2))) + +#define BSS_ID_LENGTH 6 + +// Fixed size data: Ordinal Table 1 +typedef enum _ORDINAL_TABLE_1 { // NS - means Not Supported by FW +// Transmit statistics + IPW_ORD_STAT_TX_HOST_REQUESTS = 1, // # of requested Host Tx's (MSDU) + IPW_ORD_STAT_TX_HOST_COMPLETE, // # of successful Host Tx's (MSDU) + IPW_ORD_STAT_TX_DIR_DATA, // # of successful Directed Tx's (MSDU) + + IPW_ORD_STAT_TX_DIR_DATA1 = 4, // # of successful Directed Tx's (MSDU) @ 1MB + IPW_ORD_STAT_TX_DIR_DATA2, // # of successful Directed Tx's (MSDU) @ 2MB + IPW_ORD_STAT_TX_DIR_DATA5_5, // # of successful Directed Tx's (MSDU) @ 5_5MB + IPW_ORD_STAT_TX_DIR_DATA11, // # of successful Directed Tx's (MSDU) @ 11MB + IPW_ORD_STAT_TX_DIR_DATA22, // # of successful Directed Tx's (MSDU) @ 22MB + + IPW_ORD_STAT_TX_NODIR_DATA1 = 13, // # of successful Non_Directed Tx's (MSDU) @ 1MB + IPW_ORD_STAT_TX_NODIR_DATA2, // # of successful Non_Directed Tx's (MSDU) @ 2MB + IPW_ORD_STAT_TX_NODIR_DATA5_5, // # of successful Non_Directed Tx's (MSDU) @ 5.5MB + IPW_ORD_STAT_TX_NODIR_DATA11, // # of successful Non_Directed Tx's (MSDU) @ 11MB + + IPW_ORD_STAT_NULL_DATA = 21, // # of successful NULL data Tx's + IPW_ORD_STAT_TX_RTS, // # of successful Tx RTS + IPW_ORD_STAT_TX_CTS, // # of successful Tx CTS + IPW_ORD_STAT_TX_ACK, // # of successful Tx ACK + IPW_ORD_STAT_TX_ASSN, // # of successful Association Tx's + IPW_ORD_STAT_TX_ASSN_RESP, // # of successful Association response Tx's + IPW_ORD_STAT_TX_REASSN, // # of successful Reassociation Tx's + IPW_ORD_STAT_TX_REASSN_RESP, // # of successful Reassociation response Tx's + IPW_ORD_STAT_TX_PROBE, // # of probes successfully transmitted + IPW_ORD_STAT_TX_PROBE_RESP, // # of probe responses successfully transmitted + IPW_ORD_STAT_TX_BEACON, // # of tx beacon + IPW_ORD_STAT_TX_ATIM, // # of Tx ATIM + IPW_ORD_STAT_TX_DISASSN, // # of successful Disassociation TX + IPW_ORD_STAT_TX_AUTH, // # of successful Authentication Tx + IPW_ORD_STAT_TX_DEAUTH, // # of successful Deauthentication TX + + IPW_ORD_STAT_TX_TOTAL_BYTES = 41, // Total successful Tx data bytes + IPW_ORD_STAT_TX_RETRIES, // # of Tx retries + IPW_ORD_STAT_TX_RETRY1, // # of Tx retries at 1MBPS + IPW_ORD_STAT_TX_RETRY2, // # of Tx retries at 2MBPS + IPW_ORD_STAT_TX_RETRY5_5, // # of Tx retries at 5.5MBPS + IPW_ORD_STAT_TX_RETRY11, // # of Tx retries at 11MBPS + + IPW_ORD_STAT_TX_FAILURES = 51, // # of Tx Failures + IPW_ORD_STAT_TX_ABORT_AT_HOP, //NS // # of Tx's aborted at hop time + IPW_ORD_STAT_TX_MAX_TRIES_IN_HOP, // # of times max tries in a hop failed + IPW_ORD_STAT_TX_ABORT_LATE_DMA, //NS // # of times tx aborted due to late dma setup + IPW_ORD_STAT_TX_ABORT_STX, //NS // # of times backoff aborted + IPW_ORD_STAT_TX_DISASSN_FAIL, // # of times disassociation failed + IPW_ORD_STAT_TX_ERR_CTS, // # of missed/bad CTS frames + IPW_ORD_STAT_TX_BPDU, //NS // # of spanning tree BPDUs sent + IPW_ORD_STAT_TX_ERR_ACK, // # of tx err due to acks + + // Receive statistics + IPW_ORD_STAT_RX_HOST = 61, // # of packets passed to host + IPW_ORD_STAT_RX_DIR_DATA, // # of directed packets + IPW_ORD_STAT_RX_DIR_DATA1, // # of directed packets at 1MB + IPW_ORD_STAT_RX_DIR_DATA2, // # of directed packets at 2MB + IPW_ORD_STAT_RX_DIR_DATA5_5, // # of directed packets at 5.5MB + IPW_ORD_STAT_RX_DIR_DATA11, // # of directed packets at 11MB + IPW_ORD_STAT_RX_DIR_DATA22, // # of directed packets at 22MB + + IPW_ORD_STAT_RX_NODIR_DATA = 71, // # of nondirected packets + IPW_ORD_STAT_RX_NODIR_DATA1, // # of nondirected packets at 1MB + IPW_ORD_STAT_RX_NODIR_DATA2, // # of nondirected packets at 2MB + IPW_ORD_STAT_RX_NODIR_DATA5_5, // # of nondirected packets at 5.5MB + IPW_ORD_STAT_RX_NODIR_DATA11, // # of nondirected packets at 11MB + + IPW_ORD_STAT_RX_NULL_DATA = 80, // # of null data rx's + IPW_ORD_STAT_RX_POLL, //NS // # of poll rx + IPW_ORD_STAT_RX_RTS, // # of Rx RTS + IPW_ORD_STAT_RX_CTS, // # of Rx CTS + IPW_ORD_STAT_RX_ACK, // # of Rx ACK + IPW_ORD_STAT_RX_CFEND, // # of Rx CF End + IPW_ORD_STAT_RX_CFEND_ACK, // # of Rx CF End + CF Ack + IPW_ORD_STAT_RX_ASSN, // # of Association Rx's + IPW_ORD_STAT_RX_ASSN_RESP, // # of Association response Rx's + IPW_ORD_STAT_RX_REASSN, // # of Reassociation Rx's + IPW_ORD_STAT_RX_REASSN_RESP, // # of Reassociation response Rx's + IPW_ORD_STAT_RX_PROBE, // # of probe Rx's + IPW_ORD_STAT_RX_PROBE_RESP, // # of probe response Rx's + IPW_ORD_STAT_RX_BEACON, // # of Rx beacon + IPW_ORD_STAT_RX_ATIM, // # of Rx ATIM + IPW_ORD_STAT_RX_DISASSN, // # of disassociation Rx + IPW_ORD_STAT_RX_AUTH, // # of authentication Rx + IPW_ORD_STAT_RX_DEAUTH, // # of deauthentication Rx + + IPW_ORD_STAT_RX_TOTAL_BYTES = 101, // Total rx data bytes received + IPW_ORD_STAT_RX_ERR_CRC, // # of packets with Rx CRC error + IPW_ORD_STAT_RX_ERR_CRC1, // # of Rx CRC errors at 1MB + IPW_ORD_STAT_RX_ERR_CRC2, // # of Rx CRC errors at 2MB + IPW_ORD_STAT_RX_ERR_CRC5_5, // # of Rx CRC errors at 5.5MB + IPW_ORD_STAT_RX_ERR_CRC11, // # of Rx CRC errors at 11MB + + IPW_ORD_STAT_RX_DUPLICATE1 = 112, // # of duplicate rx packets at 1MB + IPW_ORD_STAT_RX_DUPLICATE2, // # of duplicate rx packets at 2MB + IPW_ORD_STAT_RX_DUPLICATE5_5, // # of duplicate rx packets at 5.5MB + IPW_ORD_STAT_RX_DUPLICATE11, // # of duplicate rx packets at 11MB + IPW_ORD_STAT_RX_DUPLICATE = 119, // # of duplicate rx packets + + IPW_ORD_PERS_DB_LOCK = 120, // # locking fw permanent db + IPW_ORD_PERS_DB_SIZE, // # size of fw permanent db + IPW_ORD_PERS_DB_ADDR, // # address of fw permanent db + IPW_ORD_STAT_RX_INVALID_PROTOCOL, // # of rx frames with invalid protocol + IPW_ORD_SYS_BOOT_TIME, // # Boot time + IPW_ORD_STAT_RX_NO_BUFFER, // # of rx frames rejected due to no buffer + IPW_ORD_STAT_RX_ABORT_LATE_DMA, //NS // # of rx frames rejected due to dma setup too late + IPW_ORD_STAT_RX_ABORT_AT_HOP, //NS // # of rx frames aborted due to hop + IPW_ORD_STAT_RX_MISSING_FRAG, // # of rx frames dropped due to missing fragment + IPW_ORD_STAT_RX_ORPHAN_FRAG, // # of rx frames dropped due to non-sequential fragment + IPW_ORD_STAT_RX_ORPHAN_FRAME, // # of rx frames dropped due to unmatched 1st frame + IPW_ORD_STAT_RX_FRAG_AGEOUT, // # of rx frames dropped due to uncompleted frame + IPW_ORD_STAT_RX_BAD_SSID, //NS // Bad SSID (unused) + IPW_ORD_STAT_RX_ICV_ERRORS, // # of ICV errors during decryption + +// PSP Statistics + IPW_ORD_STAT_PSP_SUSPENSION = 137, // # of times adapter suspended + IPW_ORD_STAT_PSP_BCN_TIMEOUT, // # of beacon timeout + IPW_ORD_STAT_PSP_POLL_TIMEOUT, // # of poll response timeouts + IPW_ORD_STAT_PSP_NONDIR_TIMEOUT, // # of timeouts waiting for last broadcast/muticast pkt + IPW_ORD_STAT_PSP_RX_DTIMS, // # of PSP DTIMs received + IPW_ORD_STAT_PSP_RX_TIMS, // # of PSP TIMs received + IPW_ORD_STAT_PSP_STATION_ID, // PSP Station ID + +// Association and roaming + IPW_ORD_LAST_ASSN_TIME = 147, // RTC time of last association + IPW_ORD_STAT_PERCENT_MISSED_BCNS, // current calculation of % missed beacons + IPW_ORD_STAT_PERCENT_RETRIES, // current calculation of % missed tx retries + IPW_ORD_ASSOCIATED_AP_PTR, // If associated, this is ptr to the associated + // AP table entry. set to 0 if not associated + IPW_ORD_AVAILABLE_AP_CNT, // # of AP's decsribed in the AP table + IPW_ORD_AP_LIST_PTR, // Ptr to list of available APs + IPW_ORD_STAT_AP_ASSNS, // # of associations + IPW_ORD_STAT_ASSN_FAIL, // # of association failures + IPW_ORD_STAT_ASSN_RESP_FAIL, // # of failuresdue to response fail + IPW_ORD_STAT_FULL_SCANS, // # of full scans + + IPW_ORD_CARD_DISABLED, // # Card Disabled + IPW_ORD_STAT_ROAM_INHIBIT, // # of times roaming was inhibited due to ongoing activity + IPW_FILLER_40, + IPW_ORD_RSSI_AT_ASSN = 160, // RSSI of associated AP at time of association + IPW_ORD_STAT_ASSN_CAUSE1, // # of reassociations due to no tx from AP in last N + // hops or no prob_ responses in last 3 minutes + IPW_ORD_STAT_ASSN_CAUSE2, // # of reassociations due to poor tx/rx quality + IPW_ORD_STAT_ASSN_CAUSE3, // # of reassociations due to tx/rx quality with excessive + // load at the AP + IPW_ORD_STAT_ASSN_CAUSE4, // # of reassociations due to AP RSSI level fell below + // eligible group + IPW_ORD_STAT_ASSN_CAUSE5, // # of reassociations due to load leveling + IPW_ORD_STAT_ASSN_CAUSE6, //NS // # of reassociations due to dropped by Ap + IPW_FILLER_41, + IPW_FILLER_42, + IPW_FILLER_43, + IPW_ORD_STAT_AUTH_FAIL, // # of times authentication failed + IPW_ORD_STAT_AUTH_RESP_FAIL, // # of times authentication response failed + IPW_ORD_STATION_TABLE_CNT, // # of entries in association table + +// Other statistics + IPW_ORD_RSSI_AVG_CURR = 173, // Current avg RSSI + IPW_ORD_STEST_RESULTS_CURR, //NS // Current self test results word + IPW_ORD_STEST_RESULTS_CUM, //NS // Cummulative self test results word + IPW_ORD_SELF_TEST_STATUS, //NS // + IPW_ORD_POWER_MGMT_MODE, // Power mode - 0=CAM, 1=PSP + IPW_ORD_POWER_MGMT_INDEX, //NS // + IPW_ORD_COUNTRY_CODE, // IEEE country code as recv'd from beacon + IPW_ORD_COUNTRY_CHANNELS, // channels supported by country +// IPW_ORD_COUNTRY_CHANNELS: +// For 11b the lower 2-byte are used for channels from 1-14 +// and the higher 2-byte are not used. + IPW_ORD_RESET_CNT, // # of adapter resets (warm) + IPW_ORD_BEACON_INTERVAL, // Beacon interval + + IPW_ORD_PRINCETON_VERSION = 184, //NS // Princeton Version + IPW_ORD_ANTENNA_DIVERSITY, // TRUE if antenna diversity is disabled + IPW_ORD_CCA_RSSI, //NS // CCA RSSI value (factory programmed) + IPW_ORD_STAT_EEPROM_UPDATE, //NS // # of times config EEPROM updated + IPW_ORD_DTIM_PERIOD, // # of beacon intervals between DTIMs + IPW_ORD_OUR_FREQ, // current radio freq lower digits - channel ID + + IPW_ORD_RTC_TIME = 190, // current RTC time + IPW_ORD_PORT_TYPE, // operating mode + IPW_ORD_CURRENT_TX_RATE, // current tx rate + IPW_ORD_SUPPORTED_RATES, // Bitmap of supported tx rates + IPW_ORD_ATIM_WINDOW, // current ATIM Window + IPW_ORD_BASIC_RATES, // bitmap of basic tx rates + IPW_ORD_NIC_HIGHEST_RATE, // bitmap of basic tx rates + IPW_ORD_AP_HIGHEST_RATE, // bitmap of basic tx rates + IPW_ORD_CAPABILITIES, // Management frame capability field + IPW_ORD_AUTH_TYPE, // Type of authentication + IPW_ORD_RADIO_TYPE, // Adapter card platform type + IPW_ORD_RTS_THRESHOLD = 201, // Min length of packet after which RTS handshaking is used + IPW_ORD_INT_MODE, // International mode + IPW_ORD_FRAGMENTATION_THRESHOLD, // protocol frag threshold + IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, // EEPROM offset in SRAM + IPW_ORD_EEPROM_SRAM_DB_BLOCK_SIZE, // EEPROM size in SRAM + IPW_ORD_EEPROM_SKU_CAPABILITY, // EEPROM SKU Capability 206 = + IPW_ORD_EEPROM_IBSS_11B_CHANNELS, // EEPROM IBSS 11b channel set + + IPW_ORD_MAC_VERSION = 209, // MAC Version + IPW_ORD_MAC_REVISION, // MAC Revision + IPW_ORD_RADIO_VERSION, // Radio Version + IPW_ORD_NIC_MANF_DATE_TIME, // MANF Date/Time STAMP + IPW_ORD_UCODE_VERSION, // Ucode Version + IPW_ORD_HW_RF_SWITCH_STATE = 214, // HW RF Kill Switch State +} ORDINALTABLE1; + +// ordinal table 2 +// Variable length data: +#define IPW_FIRST_VARIABLE_LENGTH_ORDINAL 1001 + +typedef enum _ORDINAL_TABLE_2 { // NS - means Not Supported by FW + IPW_ORD_STAT_BASE = 1000, // contains number of variable ORDs + IPW_ORD_STAT_ADAPTER_MAC = 1001, // 6 bytes: our adapter MAC address + IPW_ORD_STAT_PREFERRED_BSSID = 1002, // 6 bytes: BSSID of the preferred AP + IPW_ORD_STAT_MANDATORY_BSSID = 1003, // 6 bytes: BSSID of the mandatory AP + IPW_FILL_1, //NS // + IPW_ORD_STAT_COUNTRY_TEXT = 1005, // 36 bytes: Country name text, First two bytes are Country code + IPW_ORD_STAT_ASSN_SSID = 1006, // 32 bytes: ESSID String + IPW_ORD_STATION_TABLE = 1007, // ? bytes: Station/AP table (via Direct SSID Scans) + IPW_ORD_STAT_SWEEP_TABLE = 1008, // ? bytes: Sweep/Host Table table (via Broadcast Scans) + IPW_ORD_STAT_ROAM_LOG = 1009, // ? bytes: Roaming log + IPW_ORD_STAT_RATE_LOG = 1010, //NS // 0 bytes: Rate log + IPW_ORD_STAT_FIFO = 1011, //NS // 0 bytes: Fifo buffer data structures + IPW_ORD_STAT_FW_VER_NUM = 1012, // 14 bytes: fw version ID string as in (a.bb.ccc; "0.08.011") + IPW_ORD_STAT_FW_DATE = 1013, // 14 bytes: fw date string (mmm dd yyyy; "Mar 13 2002") + IPW_ORD_STAT_ASSN_AP_BSSID = 1014, // 6 bytes: MAC address of associated AP + IPW_ORD_STAT_DEBUG = 1015, //NS // ? bytes: + IPW_ORD_STAT_NIC_BPA_NUM = 1016, // 11 bytes: NIC BPA number in ASCII + IPW_ORD_STAT_UCODE_DATE = 1017, // 5 bytes: uCode date + IPW_ORD_SECURITY_NGOTIATION_RESULT = 1018, +} ORDINALTABLE2; // NS - means Not Supported by FW + +#define IPW_LAST_VARIABLE_LENGTH_ORDINAL 1018 + +#ifndef WIRELESS_SPY +#define WIRELESS_SPY // enable iwspy support +#endif + +#define IPW_HOST_FW_SHARED_AREA0 0x0002f200 +#define IPW_HOST_FW_SHARED_AREA0_END 0x0002f510 // 0x310 bytes + +#define IPW_HOST_FW_SHARED_AREA1 0x0002f610 +#define IPW_HOST_FW_SHARED_AREA1_END 0x0002f630 // 0x20 bytes + +#define IPW_HOST_FW_SHARED_AREA2 0x0002fa00 +#define IPW_HOST_FW_SHARED_AREA2_END 0x0002fa20 // 0x20 bytes + +#define IPW_HOST_FW_SHARED_AREA3 0x0002fc00 +#define IPW_HOST_FW_SHARED_AREA3_END 0x0002fc10 // 0x10 bytes + +#define IPW_HOST_FW_INTERRUPT_AREA 0x0002ff80 +#define IPW_HOST_FW_INTERRUPT_AREA_END 0x00030000 // 0x80 bytes + +struct ipw2100_fw_chunk { + unsigned char *buf; + long len; + long pos; + struct list_head list; +}; + +struct ipw2100_fw_chunk_set { + const void *data; + unsigned long size; +}; + +struct ipw2100_fw { + int version; + struct ipw2100_fw_chunk_set fw; + struct ipw2100_fw_chunk_set uc; + const struct firmware *fw_entry; +}; + +#define MAX_FW_VERSION_LEN 14 + +#endif /* _IPW2100_H */ diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c new file mode 100644 index 000000000000..ed0adaf1eec4 --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -0,0 +1,12061 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. + + 802.11 status code portion of this file from ethereal-0.10.6: + Copyright 2000, Axis Communications AB + Ethereal - Network traffic analyzer + By Gerald Combs + Copyright 1998 Gerald Combs + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Intel Linux Wireless + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ + +#include +#include +#include +#include "ipw2200.h" +#include "ipw.h" + + +#ifndef KBUILD_EXTMOD +#define VK "k" +#else +#define VK +#endif + +#ifdef CONFIG_IPW2200_DEBUG +#define VD "d" +#else +#define VD +#endif + +#ifdef CONFIG_IPW2200_MONITOR +#define VM "m" +#else +#define VM +#endif + +#ifdef CONFIG_IPW2200_PROMISCUOUS +#define VP "p" +#else +#define VP +#endif + +#ifdef CONFIG_IPW2200_RADIOTAP +#define VR "r" +#else +#define VR +#endif + +#ifdef CONFIG_IPW2200_QOS +#define VQ "q" +#else +#define VQ +#endif + +#define IPW2200_VERSION "1.2.2" VK VD VM VP VR VQ +#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver" +#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" +#define DRV_VERSION IPW2200_VERSION + +#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1) + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR(DRV_COPYRIGHT); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("ipw2200-ibss.fw"); +#ifdef CONFIG_IPW2200_MONITOR +MODULE_FIRMWARE("ipw2200-sniffer.fw"); +#endif +MODULE_FIRMWARE("ipw2200-bss.fw"); + +static int cmdlog = 0; +static int debug = 0; +static int default_channel = 0; +static int network_mode = 0; + +static u32 ipw_debug_level; +static int associate; +static int auto_create = 1; +static int led_support = 1; +static int disable = 0; +static int bt_coexist = 0; +static int hwcrypto = 0; +static int roaming = 1; +static const char ipw_modes[] = { + 'a', 'b', 'g', '?' +}; +static int antenna = CFG_SYS_ANTENNA_BOTH; + +#ifdef CONFIG_IPW2200_PROMISCUOUS +static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */ +#endif + +static struct ieee80211_rate ipw2200_rates[] = { + { .bitrate = 10 }, + { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60 }, + { .bitrate = 90 }, + { .bitrate = 120 }, + { .bitrate = 180 }, + { .bitrate = 240 }, + { .bitrate = 360 }, + { .bitrate = 480 }, + { .bitrate = 540 } +}; + +#define ipw2200_a_rates (ipw2200_rates + 4) +#define ipw2200_num_a_rates 8 +#define ipw2200_bg_rates (ipw2200_rates + 0) +#define ipw2200_num_bg_rates 12 + +/* Ugly macro to convert literal channel numbers into their mhz equivalents + * There are certianly some conditions that will break this (like feeding it '30') + * but they shouldn't arise since nothing talks on channel 30. */ +#define ieee80211chan2mhz(x) \ + (((x) <= 14) ? \ + (((x) == 14) ? 2484 : ((x) * 5) + 2407) : \ + ((x) + 1000) * 5) + +#ifdef CONFIG_IPW2200_QOS +static int qos_enable = 0; +static int qos_burst_enable = 0; +static int qos_no_ack_mask = 0; +static int burst_duration_CCK = 0; +static int burst_duration_OFDM = 0; + +static struct libipw_qos_parameters def_qos_parameters_OFDM = { + {QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM, + QOS_TX3_CW_MIN_OFDM}, + {QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM, + QOS_TX3_CW_MAX_OFDM}, + {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS}, + {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM}, + {QOS_TX0_TXOP_LIMIT_OFDM, QOS_TX1_TXOP_LIMIT_OFDM, + QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM} +}; + +static struct libipw_qos_parameters def_qos_parameters_CCK = { + {QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK, + QOS_TX3_CW_MIN_CCK}, + {QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK, + QOS_TX3_CW_MAX_CCK}, + {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS}, + {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM}, + {QOS_TX0_TXOP_LIMIT_CCK, QOS_TX1_TXOP_LIMIT_CCK, QOS_TX2_TXOP_LIMIT_CCK, + QOS_TX3_TXOP_LIMIT_CCK} +}; + +static struct libipw_qos_parameters def_parameters_OFDM = { + {DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM, + DEF_TX3_CW_MIN_OFDM}, + {DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM, + DEF_TX3_CW_MAX_OFDM}, + {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS}, + {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM}, + {DEF_TX0_TXOP_LIMIT_OFDM, DEF_TX1_TXOP_LIMIT_OFDM, + DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM} +}; + +static struct libipw_qos_parameters def_parameters_CCK = { + {DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK, + DEF_TX3_CW_MIN_CCK}, + {DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK, + DEF_TX3_CW_MAX_CCK}, + {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS}, + {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM}, + {DEF_TX0_TXOP_LIMIT_CCK, DEF_TX1_TXOP_LIMIT_CCK, DEF_TX2_TXOP_LIMIT_CCK, + DEF_TX3_TXOP_LIMIT_CCK} +}; + +static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; + +static int from_priority_to_tx_queue[] = { + IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2, IPW_TX_QUEUE_1, + IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4 +}; + +static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv); + +static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters + *qos_param); +static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element + *qos_param); +#endif /* CONFIG_IPW2200_QOS */ + +static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev); +static void ipw_remove_current_network(struct ipw_priv *priv); +static void ipw_rx(struct ipw_priv *priv); +static int ipw_queue_tx_reclaim(struct ipw_priv *priv, + struct clx2_tx_queue *txq, int qindex); +static int ipw_queue_reset(struct ipw_priv *priv); + +static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf, + int len, int sync); + +static void ipw_tx_queue_free(struct ipw_priv *); + +static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *); +static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *); +static void ipw_rx_queue_replenish(void *); +static int ipw_up(struct ipw_priv *); +static void ipw_bg_up(struct work_struct *work); +static void ipw_down(struct ipw_priv *); +static void ipw_bg_down(struct work_struct *work); +static int ipw_config(struct ipw_priv *); +static int init_supported_rates(struct ipw_priv *priv, + struct ipw_supported_rates *prates); +static void ipw_set_hwcrypto_keys(struct ipw_priv *); +static void ipw_send_wep_keys(struct ipw_priv *, int); + +static int snprint_line(char *buf, size_t count, + const u8 * data, u32 len, u32 ofs) +{ + int out, i, j, l; + char c; + + out = snprintf(buf, count, "%08X", ofs); + + for (l = 0, i = 0; i < 2; i++) { + out += snprintf(buf + out, count - out, " "); + for (j = 0; j < 8 && l < len; j++, l++) + out += snprintf(buf + out, count - out, "%02X ", + data[(i * 8 + j)]); + for (; j < 8; j++) + out += snprintf(buf + out, count - out, " "); + } + + out += snprintf(buf + out, count - out, " "); + for (l = 0, i = 0; i < 2; i++) { + out += snprintf(buf + out, count - out, " "); + for (j = 0; j < 8 && l < len; j++, l++) { + c = data[(i * 8 + j)]; + if (!isascii(c) || !isprint(c)) + c = '.'; + + out += snprintf(buf + out, count - out, "%c", c); + } + + for (; j < 8; j++) + out += snprintf(buf + out, count - out, " "); + } + + return out; +} + +static void printk_buf(int level, const u8 * data, u32 len) +{ + char line[81]; + u32 ofs = 0; + if (!(ipw_debug_level & level)) + return; + + while (len) { + snprint_line(line, sizeof(line), &data[ofs], + min(len, 16U), ofs); + printk(KERN_DEBUG "%s\n", line); + ofs += 16; + len -= min(len, 16U); + } +} + +static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len) +{ + size_t out = size; + u32 ofs = 0; + int total = 0; + + while (size && len) { + out = snprint_line(output, size, &data[ofs], + min_t(size_t, len, 16U), ofs); + + ofs += 16; + output += out; + size -= out; + len -= min_t(size_t, len, 16U); + total += out; + } + return total; +} + +/* alias for 32-bit indirect read (for SRAM/reg above 4K), with debug wrapper */ +static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg); +#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b) + +/* alias for 8-bit indirect read (for SRAM/reg above 4K), with debug wrapper */ +static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg); +#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b) + +/* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */ +static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value); +static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c) +{ + IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__, + __LINE__, (u32) (b), (u32) (c)); + _ipw_write_reg8(a, b, c); +} + +/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */ +static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value); +static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c) +{ + IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__, + __LINE__, (u32) (b), (u32) (c)); + _ipw_write_reg16(a, b, c); +} + +/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */ +static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value); +static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c) +{ + IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__, + __LINE__, (u32) (b), (u32) (c)); + _ipw_write_reg32(a, b, c); +} + +/* 8-bit direct write (low 4K) */ +static inline void _ipw_write8(struct ipw_priv *ipw, unsigned long ofs, + u8 val) +{ + writeb(val, ipw->hw_base + ofs); +} + +/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */ +#define ipw_write8(ipw, ofs, val) do { \ + IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, \ + __LINE__, (u32)(ofs), (u32)(val)); \ + _ipw_write8(ipw, ofs, val); \ +} while (0) + +/* 16-bit direct write (low 4K) */ +static inline void _ipw_write16(struct ipw_priv *ipw, unsigned long ofs, + u16 val) +{ + writew(val, ipw->hw_base + ofs); +} + +/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */ +#define ipw_write16(ipw, ofs, val) do { \ + IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, \ + __LINE__, (u32)(ofs), (u32)(val)); \ + _ipw_write16(ipw, ofs, val); \ +} while (0) + +/* 32-bit direct write (low 4K) */ +static inline void _ipw_write32(struct ipw_priv *ipw, unsigned long ofs, + u32 val) +{ + writel(val, ipw->hw_base + ofs); +} + +/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */ +#define ipw_write32(ipw, ofs, val) do { \ + IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, \ + __LINE__, (u32)(ofs), (u32)(val)); \ + _ipw_write32(ipw, ofs, val); \ +} while (0) + +/* 8-bit direct read (low 4K) */ +static inline u8 _ipw_read8(struct ipw_priv *ipw, unsigned long ofs) +{ + return readb(ipw->hw_base + ofs); +} + +/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */ +#define ipw_read8(ipw, ofs) ({ \ + IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", __FILE__, __LINE__, \ + (u32)(ofs)); \ + _ipw_read8(ipw, ofs); \ +}) + +/* 16-bit direct read (low 4K) */ +static inline u16 _ipw_read16(struct ipw_priv *ipw, unsigned long ofs) +{ + return readw(ipw->hw_base + ofs); +} + +/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */ +#define ipw_read16(ipw, ofs) ({ \ + IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", __FILE__, __LINE__, \ + (u32)(ofs)); \ + _ipw_read16(ipw, ofs); \ +}) + +/* 32-bit direct read (low 4K) */ +static inline u32 _ipw_read32(struct ipw_priv *ipw, unsigned long ofs) +{ + return readl(ipw->hw_base + ofs); +} + +/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */ +#define ipw_read32(ipw, ofs) ({ \ + IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", __FILE__, __LINE__, \ + (u32)(ofs)); \ + _ipw_read32(ipw, ofs); \ +}) + +static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int); +/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */ +#define ipw_read_indirect(a, b, c, d) ({ \ + IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %u bytes\n", __FILE__, \ + __LINE__, (u32)(b), (u32)(d)); \ + _ipw_read_indirect(a, b, c, d); \ +}) + +/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */ +static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data, + int num); +#define ipw_write_indirect(a, b, c, d) do { \ + IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %u bytes\n", __FILE__, \ + __LINE__, (u32)(b), (u32)(d)); \ + _ipw_write_indirect(a, b, c, d); \ +} while (0) + +/* 32-bit indirect write (above 4K) */ +static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value) +{ + IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value); + _ipw_write32(priv, IPW_INDIRECT_ADDR, reg); + _ipw_write32(priv, IPW_INDIRECT_DATA, value); +} + +/* 8-bit indirect write (above 4K) */ +static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value) +{ + u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */ + u32 dif_len = reg - aligned_addr; + + IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value); + _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); + _ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value); +} + +/* 16-bit indirect write (above 4K) */ +static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value) +{ + u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */ + u32 dif_len = (reg - aligned_addr) & (~0x1ul); + + IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value); + _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); + _ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value); +} + +/* 8-bit indirect read (above 4K) */ +static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg) +{ + u32 word; + _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK); + IPW_DEBUG_IO(" reg = 0x%8X :\n", reg); + word = _ipw_read32(priv, IPW_INDIRECT_DATA); + return (word >> ((reg & 0x3) * 8)) & 0xff; +} + +/* 32-bit indirect read (above 4K) */ +static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg) +{ + u32 value; + + IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg); + + _ipw_write32(priv, IPW_INDIRECT_ADDR, reg); + value = _ipw_read32(priv, IPW_INDIRECT_DATA); + IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x\n", reg, value); + return value; +} + +/* General purpose, no alignment requirement, iterative (multi-byte) read, */ +/* for area above 1st 4K of SRAM/reg space */ +static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf, + int num) +{ + u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */ + u32 dif_len = addr - aligned_addr; + u32 i; + + IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num); + + if (num <= 0) { + return; + } + + /* Read the first dword (or portion) byte by byte */ + if (unlikely(dif_len)) { + _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); + /* Start reading at aligned_addr + dif_len */ + for (i = dif_len; ((i < 4) && (num > 0)); i++, num--) + *buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i); + aligned_addr += 4; + } + + /* Read all of the middle dwords as dwords, with auto-increment */ + _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr); + for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4) + *(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA); + + /* Read the last dword (or portion) byte by byte */ + if (unlikely(num)) { + _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); + for (i = 0; num > 0; i++, num--) + *buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i); + } +} + +/* General purpose, no alignment requirement, iterative (multi-byte) write, */ +/* for area above 1st 4K of SRAM/reg space */ +static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf, + int num) +{ + u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */ + u32 dif_len = addr - aligned_addr; + u32 i; + + IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num); + + if (num <= 0) { + return; + } + + /* Write the first dword (or portion) byte by byte */ + if (unlikely(dif_len)) { + _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); + /* Start writing at aligned_addr + dif_len */ + for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++) + _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf); + aligned_addr += 4; + } + + /* Write all of the middle dwords as dwords, with auto-increment */ + _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr); + for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4) + _ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf); + + /* Write the last dword (or portion) byte by byte */ + if (unlikely(num)) { + _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); + for (i = 0; num > 0; i++, num--, buf++) + _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf); + } +} + +/* General purpose, no alignment requirement, iterative (multi-byte) write, */ +/* for 1st 4K of SRAM/regs space */ +static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf, + int num) +{ + memcpy_toio((priv->hw_base + addr), buf, num); +} + +/* Set bit(s) in low 4K of SRAM/regs */ +static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask) +{ + ipw_write32(priv, reg, ipw_read32(priv, reg) | mask); +} + +/* Clear bit(s) in low 4K of SRAM/regs */ +static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask) +{ + ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask); +} + +static inline void __ipw_enable_interrupts(struct ipw_priv *priv) +{ + if (priv->status & STATUS_INT_ENABLED) + return; + priv->status |= STATUS_INT_ENABLED; + ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL); +} + +static inline void __ipw_disable_interrupts(struct ipw_priv *priv) +{ + if (!(priv->status & STATUS_INT_ENABLED)) + return; + priv->status &= ~STATUS_INT_ENABLED; + ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL); +} + +static inline void ipw_enable_interrupts(struct ipw_priv *priv) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->irq_lock, flags); + __ipw_enable_interrupts(priv); + spin_unlock_irqrestore(&priv->irq_lock, flags); +} + +static inline void ipw_disable_interrupts(struct ipw_priv *priv) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->irq_lock, flags); + __ipw_disable_interrupts(priv); + spin_unlock_irqrestore(&priv->irq_lock, flags); +} + +static char *ipw_error_desc(u32 val) +{ + switch (val) { + case IPW_FW_ERROR_OK: + return "ERROR_OK"; + case IPW_FW_ERROR_FAIL: + return "ERROR_FAIL"; + case IPW_FW_ERROR_MEMORY_UNDERFLOW: + return "MEMORY_UNDERFLOW"; + case IPW_FW_ERROR_MEMORY_OVERFLOW: + return "MEMORY_OVERFLOW"; + case IPW_FW_ERROR_BAD_PARAM: + return "BAD_PARAM"; + case IPW_FW_ERROR_BAD_CHECKSUM: + return "BAD_CHECKSUM"; + case IPW_FW_ERROR_NMI_INTERRUPT: + return "NMI_INTERRUPT"; + case IPW_FW_ERROR_BAD_DATABASE: + return "BAD_DATABASE"; + case IPW_FW_ERROR_ALLOC_FAIL: + return "ALLOC_FAIL"; + case IPW_FW_ERROR_DMA_UNDERRUN: + return "DMA_UNDERRUN"; + case IPW_FW_ERROR_DMA_STATUS: + return "DMA_STATUS"; + case IPW_FW_ERROR_DINO_ERROR: + return "DINO_ERROR"; + case IPW_FW_ERROR_EEPROM_ERROR: + return "EEPROM_ERROR"; + case IPW_FW_ERROR_SYSASSERT: + return "SYSASSERT"; + case IPW_FW_ERROR_FATAL_ERROR: + return "FATAL_ERROR"; + default: + return "UNKNOWN_ERROR"; + } +} + +static void ipw_dump_error_log(struct ipw_priv *priv, + struct ipw_fw_error *error) +{ + u32 i; + + if (!error) { + IPW_ERROR("Error allocating and capturing error log. " + "Nothing to dump.\n"); + return; + } + + IPW_ERROR("Start IPW Error Log Dump:\n"); + IPW_ERROR("Status: 0x%08X, Config: %08X\n", + error->status, error->config); + + for (i = 0; i < error->elem_len; i++) + IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + ipw_error_desc(error->elem[i].desc), + error->elem[i].time, + error->elem[i].blink1, + error->elem[i].blink2, + error->elem[i].link1, + error->elem[i].link2, error->elem[i].data); + for (i = 0; i < error->log_len; i++) + IPW_ERROR("%i\t0x%08x\t%i\n", + error->log[i].time, + error->log[i].data, error->log[i].event); +} + +static inline int ipw_is_init(struct ipw_priv *priv) +{ + return (priv->status & STATUS_INIT) ? 1 : 0; +} + +static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len) +{ + u32 addr, field_info, field_len, field_count, total_len; + + IPW_DEBUG_ORD("ordinal = %i\n", ord); + + if (!priv || !val || !len) { + IPW_DEBUG_ORD("Invalid argument\n"); + return -EINVAL; + } + + /* verify device ordinal tables have been initialized */ + if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) { + IPW_DEBUG_ORD("Access ordinals before initialization\n"); + return -EINVAL; + } + + switch (IPW_ORD_TABLE_ID_MASK & ord) { + case IPW_ORD_TABLE_0_MASK: + /* + * TABLE 0: Direct access to a table of 32 bit values + * + * This is a very simple table with the data directly + * read from the table + */ + + /* remove the table id from the ordinal */ + ord &= IPW_ORD_TABLE_VALUE_MASK; + + /* boundary check */ + if (ord > priv->table0_len) { + IPW_DEBUG_ORD("ordinal value (%i) longer then " + "max (%i)\n", ord, priv->table0_len); + return -EINVAL; + } + + /* verify we have enough room to store the value */ + if (*len < sizeof(u32)) { + IPW_DEBUG_ORD("ordinal buffer length too small, " + "need %zd\n", sizeof(u32)); + return -EINVAL; + } + + IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n", + ord, priv->table0_addr + (ord << 2)); + + *len = sizeof(u32); + ord <<= 2; + *((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord); + break; + + case IPW_ORD_TABLE_1_MASK: + /* + * TABLE 1: Indirect access to a table of 32 bit values + * + * This is a fairly large table of u32 values each + * representing starting addr for the data (which is + * also a u32) + */ + + /* remove the table id from the ordinal */ + ord &= IPW_ORD_TABLE_VALUE_MASK; + + /* boundary check */ + if (ord > priv->table1_len) { + IPW_DEBUG_ORD("ordinal value too long\n"); + return -EINVAL; + } + + /* verify we have enough room to store the value */ + if (*len < sizeof(u32)) { + IPW_DEBUG_ORD("ordinal buffer length too small, " + "need %zd\n", sizeof(u32)); + return -EINVAL; + } + + *((u32 *) val) = + ipw_read_reg32(priv, (priv->table1_addr + (ord << 2))); + *len = sizeof(u32); + break; + + case IPW_ORD_TABLE_2_MASK: + /* + * TABLE 2: Indirect access to a table of variable sized values + * + * This table consist of six values, each containing + * - dword containing the starting offset of the data + * - dword containing the lengh in the first 16bits + * and the count in the second 16bits + */ + + /* remove the table id from the ordinal */ + ord &= IPW_ORD_TABLE_VALUE_MASK; + + /* boundary check */ + if (ord > priv->table2_len) { + IPW_DEBUG_ORD("ordinal value too long\n"); + return -EINVAL; + } + + /* get the address of statistic */ + addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3)); + + /* get the second DW of statistics ; + * two 16-bit words - first is length, second is count */ + field_info = + ipw_read_reg32(priv, + priv->table2_addr + (ord << 3) + + sizeof(u32)); + + /* get each entry length */ + field_len = *((u16 *) & field_info); + + /* get number of entries */ + field_count = *(((u16 *) & field_info) + 1); + + /* abort if not enough memory */ + total_len = field_len * field_count; + if (total_len > *len) { + *len = total_len; + return -EINVAL; + } + + *len = total_len; + if (!total_len) + return 0; + + IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, " + "field_info = 0x%08x\n", + addr, total_len, field_info); + ipw_read_indirect(priv, addr, val, total_len); + break; + + default: + IPW_DEBUG_ORD("Invalid ordinal!\n"); + return -EINVAL; + + } + + return 0; +} + +static void ipw_init_ordinals(struct ipw_priv *priv) +{ + priv->table0_addr = IPW_ORDINALS_TABLE_LOWER; + priv->table0_len = ipw_read32(priv, priv->table0_addr); + + IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n", + priv->table0_addr, priv->table0_len); + + priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1); + priv->table1_len = ipw_read_reg32(priv, priv->table1_addr); + + IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n", + priv->table1_addr, priv->table1_len); + + priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2); + priv->table2_len = ipw_read_reg32(priv, priv->table2_addr); + priv->table2_len &= 0x0000ffff; /* use first two bytes */ + + IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n", + priv->table2_addr, priv->table2_len); + +} + +static u32 ipw_register_toggle(u32 reg) +{ + reg &= ~IPW_START_STANDBY; + if (reg & IPW_GATE_ODMA) + reg &= ~IPW_GATE_ODMA; + if (reg & IPW_GATE_IDMA) + reg &= ~IPW_GATE_IDMA; + if (reg & IPW_GATE_ADMA) + reg &= ~IPW_GATE_ADMA; + return reg; +} + +/* + * LED behavior: + * - On radio ON, turn on any LEDs that require to be on during start + * - On initialization, start unassociated blink + * - On association, disable unassociated blink + * - On disassociation, start unassociated blink + * - On radio OFF, turn off any LEDs started during radio on + * + */ +#define LD_TIME_LINK_ON msecs_to_jiffies(300) +#define LD_TIME_LINK_OFF msecs_to_jiffies(2700) +#define LD_TIME_ACT_ON msecs_to_jiffies(250) + +static void ipw_led_link_on(struct ipw_priv *priv) +{ + unsigned long flags; + u32 led; + + /* If configured to not use LEDs, or nic_type is 1, + * then we don't toggle a LINK led */ + if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1) + return; + + spin_lock_irqsave(&priv->lock, flags); + + if (!(priv->status & STATUS_RF_KILL_MASK) && + !(priv->status & STATUS_LED_LINK_ON)) { + IPW_DEBUG_LED("Link LED On\n"); + led = ipw_read_reg32(priv, IPW_EVENT_REG); + led |= priv->led_association_on; + + led = ipw_register_toggle(led); + + IPW_DEBUG_LED("Reg: 0x%08X\n", led); + ipw_write_reg32(priv, IPW_EVENT_REG, led); + + priv->status |= STATUS_LED_LINK_ON; + + /* If we aren't associated, schedule turning the LED off */ + if (!(priv->status & STATUS_ASSOCIATED)) + schedule_delayed_work(&priv->led_link_off, + LD_TIME_LINK_ON); + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void ipw_bg_led_link_on(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, led_link_on.work); + mutex_lock(&priv->mutex); + ipw_led_link_on(priv); + mutex_unlock(&priv->mutex); +} + +static void ipw_led_link_off(struct ipw_priv *priv) +{ + unsigned long flags; + u32 led; + + /* If configured not to use LEDs, or nic type is 1, + * then we don't goggle the LINK led. */ + if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1) + return; + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->status & STATUS_LED_LINK_ON) { + led = ipw_read_reg32(priv, IPW_EVENT_REG); + led &= priv->led_association_off; + led = ipw_register_toggle(led); + + IPW_DEBUG_LED("Reg: 0x%08X\n", led); + ipw_write_reg32(priv, IPW_EVENT_REG, led); + + IPW_DEBUG_LED("Link LED Off\n"); + + priv->status &= ~STATUS_LED_LINK_ON; + + /* If we aren't associated and the radio is on, schedule + * turning the LED on (blink while unassociated) */ + if (!(priv->status & STATUS_RF_KILL_MASK) && + !(priv->status & STATUS_ASSOCIATED)) + schedule_delayed_work(&priv->led_link_on, + LD_TIME_LINK_OFF); + + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void ipw_bg_led_link_off(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, led_link_off.work); + mutex_lock(&priv->mutex); + ipw_led_link_off(priv); + mutex_unlock(&priv->mutex); +} + +static void __ipw_led_activity_on(struct ipw_priv *priv) +{ + u32 led; + + if (priv->config & CFG_NO_LED) + return; + + if (priv->status & STATUS_RF_KILL_MASK) + return; + + if (!(priv->status & STATUS_LED_ACT_ON)) { + led = ipw_read_reg32(priv, IPW_EVENT_REG); + led |= priv->led_activity_on; + + led = ipw_register_toggle(led); + + IPW_DEBUG_LED("Reg: 0x%08X\n", led); + ipw_write_reg32(priv, IPW_EVENT_REG, led); + + IPW_DEBUG_LED("Activity LED On\n"); + + priv->status |= STATUS_LED_ACT_ON; + + cancel_delayed_work(&priv->led_act_off); + schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON); + } else { + /* Reschedule LED off for full time period */ + cancel_delayed_work(&priv->led_act_off); + schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON); + } +} + +#if 0 +void ipw_led_activity_on(struct ipw_priv *priv) +{ + unsigned long flags; + spin_lock_irqsave(&priv->lock, flags); + __ipw_led_activity_on(priv); + spin_unlock_irqrestore(&priv->lock, flags); +} +#endif /* 0 */ + +static void ipw_led_activity_off(struct ipw_priv *priv) +{ + unsigned long flags; + u32 led; + + if (priv->config & CFG_NO_LED) + return; + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->status & STATUS_LED_ACT_ON) { + led = ipw_read_reg32(priv, IPW_EVENT_REG); + led &= priv->led_activity_off; + + led = ipw_register_toggle(led); + + IPW_DEBUG_LED("Reg: 0x%08X\n", led); + ipw_write_reg32(priv, IPW_EVENT_REG, led); + + IPW_DEBUG_LED("Activity LED Off\n"); + + priv->status &= ~STATUS_LED_ACT_ON; + } + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void ipw_bg_led_activity_off(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, led_act_off.work); + mutex_lock(&priv->mutex); + ipw_led_activity_off(priv); + mutex_unlock(&priv->mutex); +} + +static void ipw_led_band_on(struct ipw_priv *priv) +{ + unsigned long flags; + u32 led; + + /* Only nic type 1 supports mode LEDs */ + if (priv->config & CFG_NO_LED || + priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network) + return; + + spin_lock_irqsave(&priv->lock, flags); + + led = ipw_read_reg32(priv, IPW_EVENT_REG); + if (priv->assoc_network->mode == IEEE_A) { + led |= priv->led_ofdm_on; + led &= priv->led_association_off; + IPW_DEBUG_LED("Mode LED On: 802.11a\n"); + } else if (priv->assoc_network->mode == IEEE_G) { + led |= priv->led_ofdm_on; + led |= priv->led_association_on; + IPW_DEBUG_LED("Mode LED On: 802.11g\n"); + } else { + led &= priv->led_ofdm_off; + led |= priv->led_association_on; + IPW_DEBUG_LED("Mode LED On: 802.11b\n"); + } + + led = ipw_register_toggle(led); + + IPW_DEBUG_LED("Reg: 0x%08X\n", led); + ipw_write_reg32(priv, IPW_EVENT_REG, led); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void ipw_led_band_off(struct ipw_priv *priv) +{ + unsigned long flags; + u32 led; + + /* Only nic type 1 supports mode LEDs */ + if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1) + return; + + spin_lock_irqsave(&priv->lock, flags); + + led = ipw_read_reg32(priv, IPW_EVENT_REG); + led &= priv->led_ofdm_off; + led &= priv->led_association_off; + + led = ipw_register_toggle(led); + + IPW_DEBUG_LED("Reg: 0x%08X\n", led); + ipw_write_reg32(priv, IPW_EVENT_REG, led); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void ipw_led_radio_on(struct ipw_priv *priv) +{ + ipw_led_link_on(priv); +} + +static void ipw_led_radio_off(struct ipw_priv *priv) +{ + ipw_led_activity_off(priv); + ipw_led_link_off(priv); +} + +static void ipw_led_link_up(struct ipw_priv *priv) +{ + /* Set the Link Led on for all nic types */ + ipw_led_link_on(priv); +} + +static void ipw_led_link_down(struct ipw_priv *priv) +{ + ipw_led_activity_off(priv); + ipw_led_link_off(priv); + + if (priv->status & STATUS_RF_KILL_MASK) + ipw_led_radio_off(priv); +} + +static void ipw_led_init(struct ipw_priv *priv) +{ + priv->nic_type = priv->eeprom[EEPROM_NIC_TYPE]; + + /* Set the default PINs for the link and activity leds */ + priv->led_activity_on = IPW_ACTIVITY_LED; + priv->led_activity_off = ~(IPW_ACTIVITY_LED); + + priv->led_association_on = IPW_ASSOCIATED_LED; + priv->led_association_off = ~(IPW_ASSOCIATED_LED); + + /* Set the default PINs for the OFDM leds */ + priv->led_ofdm_on = IPW_OFDM_LED; + priv->led_ofdm_off = ~(IPW_OFDM_LED); + + switch (priv->nic_type) { + case EEPROM_NIC_TYPE_1: + /* In this NIC type, the LEDs are reversed.... */ + priv->led_activity_on = IPW_ASSOCIATED_LED; + priv->led_activity_off = ~(IPW_ASSOCIATED_LED); + priv->led_association_on = IPW_ACTIVITY_LED; + priv->led_association_off = ~(IPW_ACTIVITY_LED); + + if (!(priv->config & CFG_NO_LED)) + ipw_led_band_on(priv); + + /* And we don't blink link LEDs for this nic, so + * just return here */ + return; + + case EEPROM_NIC_TYPE_3: + case EEPROM_NIC_TYPE_2: + case EEPROM_NIC_TYPE_4: + case EEPROM_NIC_TYPE_0: + break; + + default: + IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n", + priv->nic_type); + priv->nic_type = EEPROM_NIC_TYPE_0; + break; + } + + if (!(priv->config & CFG_NO_LED)) { + if (priv->status & STATUS_ASSOCIATED) + ipw_led_link_on(priv); + else + ipw_led_link_off(priv); + } +} + +static void ipw_led_shutdown(struct ipw_priv *priv) +{ + ipw_led_activity_off(priv); + ipw_led_link_off(priv); + ipw_led_band_off(priv); + cancel_delayed_work(&priv->led_link_on); + cancel_delayed_work(&priv->led_link_off); + cancel_delayed_work(&priv->led_act_off); +} + +/* + * The following adds a new attribute to the sysfs representation + * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/) + * used for controlling the debug level. + * + * See the level definitions in ipw for details. + */ +static ssize_t show_debug_level(struct device_driver *d, char *buf) +{ + return sprintf(buf, "0x%08X\n", ipw_debug_level); +} + +static ssize_t store_debug_level(struct device_driver *d, const char *buf, + size_t count) +{ + char *p = (char *)buf; + u32 val; + + if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') { + p++; + if (p[0] == 'x' || p[0] == 'X') + p++; + val = simple_strtoul(p, &p, 16); + } else + val = simple_strtoul(p, &p, 10); + if (p == buf) + printk(KERN_INFO DRV_NAME + ": %s is not in hex or decimal form.\n", buf); + else + ipw_debug_level = val; + + return strnlen(buf, count); +} + +static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, + show_debug_level, store_debug_level); + +static inline u32 ipw_get_event_log_len(struct ipw_priv *priv) +{ + /* length = 1st dword in log */ + return ipw_read_reg32(priv, ipw_read32(priv, IPW_EVENT_LOG)); +} + +static void ipw_capture_event_log(struct ipw_priv *priv, + u32 log_len, struct ipw_event *log) +{ + u32 base; + + if (log_len) { + base = ipw_read32(priv, IPW_EVENT_LOG); + ipw_read_indirect(priv, base + sizeof(base) + sizeof(u32), + (u8 *) log, sizeof(*log) * log_len); + } +} + +static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv) +{ + struct ipw_fw_error *error; + u32 log_len = ipw_get_event_log_len(priv); + u32 base = ipw_read32(priv, IPW_ERROR_LOG); + u32 elem_len = ipw_read_reg32(priv, base); + + error = kmalloc(sizeof(*error) + + sizeof(*error->elem) * elem_len + + sizeof(*error->log) * log_len, GFP_ATOMIC); + if (!error) { + IPW_ERROR("Memory allocation for firmware error log " + "failed.\n"); + return NULL; + } + error->jiffies = jiffies; + error->status = priv->status; + error->config = priv->config; + error->elem_len = elem_len; + error->log_len = log_len; + error->elem = (struct ipw_error_elem *)error->payload; + error->log = (struct ipw_event *)(error->elem + elem_len); + + ipw_capture_event_log(priv, log_len, error->log); + + if (elem_len) + ipw_read_indirect(priv, base + sizeof(base), (u8 *) error->elem, + sizeof(*error->elem) * elem_len); + + return error; +} + +static ssize_t show_event_log(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + u32 log_len = ipw_get_event_log_len(priv); + u32 log_size; + struct ipw_event *log; + u32 len = 0, i; + + /* not using min() because of its strict type checking */ + log_size = PAGE_SIZE / sizeof(*log) > log_len ? + sizeof(*log) * log_len : PAGE_SIZE; + log = kzalloc(log_size, GFP_KERNEL); + if (!log) { + IPW_ERROR("Unable to allocate memory for log\n"); + return 0; + } + log_len = log_size / sizeof(*log); + ipw_capture_event_log(priv, log_len, log); + + len += snprintf(buf + len, PAGE_SIZE - len, "%08X", log_len); + for (i = 0; i < log_len; i++) + len += snprintf(buf + len, PAGE_SIZE - len, + "\n%08X%08X%08X", + log[i].time, log[i].event, log[i].data); + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + kfree(log); + return len; +} + +static DEVICE_ATTR(event_log, S_IRUGO, show_event_log, NULL); + +static ssize_t show_error(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + u32 len = 0, i; + if (!priv->error) + return 0; + len += snprintf(buf + len, PAGE_SIZE - len, + "%08lX%08X%08X%08X", + priv->error->jiffies, + priv->error->status, + priv->error->config, priv->error->elem_len); + for (i = 0; i < priv->error->elem_len; i++) + len += snprintf(buf + len, PAGE_SIZE - len, + "\n%08X%08X%08X%08X%08X%08X%08X", + priv->error->elem[i].time, + priv->error->elem[i].desc, + priv->error->elem[i].blink1, + priv->error->elem[i].blink2, + priv->error->elem[i].link1, + priv->error->elem[i].link2, + priv->error->elem[i].data); + + len += snprintf(buf + len, PAGE_SIZE - len, + "\n%08X", priv->error->log_len); + for (i = 0; i < priv->error->log_len; i++) + len += snprintf(buf + len, PAGE_SIZE - len, + "\n%08X%08X%08X", + priv->error->log[i].time, + priv->error->log[i].event, + priv->error->log[i].data); + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + return len; +} + +static ssize_t clear_error(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + + kfree(priv->error); + priv->error = NULL; + return count; +} + +static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error); + +static ssize_t show_cmd_log(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + u32 len = 0, i; + if (!priv->cmdlog) + return 0; + for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len; + (i != priv->cmdlog_pos) && (len < PAGE_SIZE); + i = (i + 1) % priv->cmdlog_len) { + len += + snprintf(buf + len, PAGE_SIZE - len, + "\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies, + priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd, + priv->cmdlog[i].cmd.len); + len += + snprintk_buf(buf + len, PAGE_SIZE - len, + (u8 *) priv->cmdlog[i].cmd.param, + priv->cmdlog[i].cmd.len); + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + } + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + return len; +} + +static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL); + +#ifdef CONFIG_IPW2200_PROMISCUOUS +static void ipw_prom_free(struct ipw_priv *priv); +static int ipw_prom_alloc(struct ipw_priv *priv); +static ssize_t store_rtap_iface(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + int rc = 0; + + if (count < 1) + return -EINVAL; + + switch (buf[0]) { + case '0': + if (!rtap_iface) + return count; + + if (netif_running(priv->prom_net_dev)) { + IPW_WARNING("Interface is up. Cannot unregister.\n"); + return count; + } + + ipw_prom_free(priv); + rtap_iface = 0; + break; + + case '1': + if (rtap_iface) + return count; + + rc = ipw_prom_alloc(priv); + if (!rc) + rtap_iface = 1; + break; + + default: + return -EINVAL; + } + + if (rc) { + IPW_ERROR("Failed to register promiscuous network " + "device (error %d).\n", rc); + } + + return count; +} + +static ssize_t show_rtap_iface(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + if (rtap_iface) + return sprintf(buf, "%s", priv->prom_net_dev->name); + else { + buf[0] = '-'; + buf[1] = '1'; + buf[2] = '\0'; + return 3; + } +} + +static DEVICE_ATTR(rtap_iface, S_IWUSR | S_IRUSR, show_rtap_iface, + store_rtap_iface); + +static ssize_t store_rtap_filter(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + + if (!priv->prom_priv) { + IPW_ERROR("Attempting to set filter without " + "rtap_iface enabled.\n"); + return -EPERM; + } + + priv->prom_priv->filter = simple_strtol(buf, NULL, 0); + + IPW_DEBUG_INFO("Setting rtap filter to " BIT_FMT16 "\n", + BIT_ARG16(priv->prom_priv->filter)); + + return count; +} + +static ssize_t show_rtap_filter(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + return sprintf(buf, "0x%04X", + priv->prom_priv ? priv->prom_priv->filter : 0); +} + +static DEVICE_ATTR(rtap_filter, S_IWUSR | S_IRUSR, show_rtap_filter, + store_rtap_filter); +#endif + +static ssize_t show_scan_age(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + return sprintf(buf, "%d\n", priv->ieee->scan_age); +} + +static ssize_t store_scan_age(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + struct net_device *dev = priv->net_dev; + char buffer[] = "00000000"; + unsigned long len = + (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1; + unsigned long val; + char *p = buffer; + + IPW_DEBUG_INFO("enter\n"); + + strncpy(buffer, buf, len); + buffer[len] = 0; + + if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') { + p++; + if (p[0] == 'x' || p[0] == 'X') + p++; + val = simple_strtoul(p, &p, 16); + } else + val = simple_strtoul(p, &p, 10); + if (p == buffer) { + IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name); + } else { + priv->ieee->scan_age = val; + IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age); + } + + IPW_DEBUG_INFO("exit\n"); + return len; +} + +static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age); + +static ssize_t show_led(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + return sprintf(buf, "%d\n", (priv->config & CFG_NO_LED) ? 0 : 1); +} + +static ssize_t store_led(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + + IPW_DEBUG_INFO("enter\n"); + + if (count == 0) + return 0; + + if (*buf == 0) { + IPW_DEBUG_LED("Disabling LED control.\n"); + priv->config |= CFG_NO_LED; + ipw_led_shutdown(priv); + } else { + IPW_DEBUG_LED("Enabling LED control.\n"); + priv->config &= ~CFG_NO_LED; + ipw_led_init(priv); + } + + IPW_DEBUG_INFO("exit\n"); + return count; +} + +static DEVICE_ATTR(led, S_IWUSR | S_IRUGO, show_led, store_led); + +static ssize_t show_status(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct ipw_priv *p = dev_get_drvdata(d); + return sprintf(buf, "0x%08x\n", (int)p->status); +} + +static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); + +static ssize_t show_cfg(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw_priv *p = dev_get_drvdata(d); + return sprintf(buf, "0x%08x\n", (int)p->config); +} + +static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL); + +static ssize_t show_nic_type(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + return sprintf(buf, "TYPE: %d\n", priv->nic_type); +} + +static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL); + +static ssize_t show_ucode_version(struct device *d, + struct device_attribute *attr, char *buf) +{ + u32 len = sizeof(u32), tmp = 0; + struct ipw_priv *p = dev_get_drvdata(d); + + if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len)) + return 0; + + return sprintf(buf, "0x%08x\n", tmp); +} + +static DEVICE_ATTR(ucode_version, S_IWUSR | S_IRUGO, show_ucode_version, NULL); + +static ssize_t show_rtc(struct device *d, struct device_attribute *attr, + char *buf) +{ + u32 len = sizeof(u32), tmp = 0; + struct ipw_priv *p = dev_get_drvdata(d); + + if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len)) + return 0; + + return sprintf(buf, "0x%08x\n", tmp); +} + +static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL); + +/* + * Add a device attribute to view/control the delay between eeprom + * operations. + */ +static ssize_t show_eeprom_delay(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct ipw_priv *p = dev_get_drvdata(d); + int n = p->eeprom_delay; + return sprintf(buf, "%i\n", n); +} +static ssize_t store_eeprom_delay(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *p = dev_get_drvdata(d); + sscanf(buf, "%i", &p->eeprom_delay); + return strnlen(buf, count); +} + +static DEVICE_ATTR(eeprom_delay, S_IWUSR | S_IRUGO, + show_eeprom_delay, store_eeprom_delay); + +static ssize_t show_command_event_reg(struct device *d, + struct device_attribute *attr, char *buf) +{ + u32 reg = 0; + struct ipw_priv *p = dev_get_drvdata(d); + + reg = ipw_read_reg32(p, IPW_INTERNAL_CMD_EVENT); + return sprintf(buf, "0x%08x\n", reg); +} +static ssize_t store_command_event_reg(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 reg; + struct ipw_priv *p = dev_get_drvdata(d); + + sscanf(buf, "%x", ®); + ipw_write_reg32(p, IPW_INTERNAL_CMD_EVENT, reg); + return strnlen(buf, count); +} + +static DEVICE_ATTR(command_event_reg, S_IWUSR | S_IRUGO, + show_command_event_reg, store_command_event_reg); + +static ssize_t show_mem_gpio_reg(struct device *d, + struct device_attribute *attr, char *buf) +{ + u32 reg = 0; + struct ipw_priv *p = dev_get_drvdata(d); + + reg = ipw_read_reg32(p, 0x301100); + return sprintf(buf, "0x%08x\n", reg); +} +static ssize_t store_mem_gpio_reg(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 reg; + struct ipw_priv *p = dev_get_drvdata(d); + + sscanf(buf, "%x", ®); + ipw_write_reg32(p, 0x301100, reg); + return strnlen(buf, count); +} + +static DEVICE_ATTR(mem_gpio_reg, S_IWUSR | S_IRUGO, + show_mem_gpio_reg, store_mem_gpio_reg); + +static ssize_t show_indirect_dword(struct device *d, + struct device_attribute *attr, char *buf) +{ + u32 reg = 0; + struct ipw_priv *priv = dev_get_drvdata(d); + + if (priv->status & STATUS_INDIRECT_DWORD) + reg = ipw_read_reg32(priv, priv->indirect_dword); + else + reg = 0; + + return sprintf(buf, "0x%08x\n", reg); +} +static ssize_t store_indirect_dword(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + + sscanf(buf, "%x", &priv->indirect_dword); + priv->status |= STATUS_INDIRECT_DWORD; + return strnlen(buf, count); +} + +static DEVICE_ATTR(indirect_dword, S_IWUSR | S_IRUGO, + show_indirect_dword, store_indirect_dword); + +static ssize_t show_indirect_byte(struct device *d, + struct device_attribute *attr, char *buf) +{ + u8 reg = 0; + struct ipw_priv *priv = dev_get_drvdata(d); + + if (priv->status & STATUS_INDIRECT_BYTE) + reg = ipw_read_reg8(priv, priv->indirect_byte); + else + reg = 0; + + return sprintf(buf, "0x%02x\n", reg); +} +static ssize_t store_indirect_byte(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + + sscanf(buf, "%x", &priv->indirect_byte); + priv->status |= STATUS_INDIRECT_BYTE; + return strnlen(buf, count); +} + +static DEVICE_ATTR(indirect_byte, S_IWUSR | S_IRUGO, + show_indirect_byte, store_indirect_byte); + +static ssize_t show_direct_dword(struct device *d, + struct device_attribute *attr, char *buf) +{ + u32 reg = 0; + struct ipw_priv *priv = dev_get_drvdata(d); + + if (priv->status & STATUS_DIRECT_DWORD) + reg = ipw_read32(priv, priv->direct_dword); + else + reg = 0; + + return sprintf(buf, "0x%08x\n", reg); +} +static ssize_t store_direct_dword(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + + sscanf(buf, "%x", &priv->direct_dword); + priv->status |= STATUS_DIRECT_DWORD; + return strnlen(buf, count); +} + +static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO, + show_direct_dword, store_direct_dword); + +static int rf_kill_active(struct ipw_priv *priv) +{ + if (0 == (ipw_read32(priv, 0x30) & 0x10000)) { + priv->status |= STATUS_RF_KILL_HW; + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); + } else { + priv->status &= ~STATUS_RF_KILL_HW; + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); + } + + return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0; +} + +static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr, + char *buf) +{ + /* 0 - RF kill not enabled + 1 - SW based RF kill active (sysfs) + 2 - HW based RF kill active + 3 - Both HW and SW baed RF kill active */ + struct ipw_priv *priv = dev_get_drvdata(d); + int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) | + (rf_kill_active(priv) ? 0x2 : 0x0); + return sprintf(buf, "%i\n", val); +} + +static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio) +{ + if ((disable_radio ? 1 : 0) == + ((priv->status & STATUS_RF_KILL_SW) ? 1 : 0)) + return 0; + + IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n", + disable_radio ? "OFF" : "ON"); + + if (disable_radio) { + priv->status |= STATUS_RF_KILL_SW; + + cancel_delayed_work(&priv->request_scan); + cancel_delayed_work(&priv->request_direct_scan); + cancel_delayed_work(&priv->request_passive_scan); + cancel_delayed_work(&priv->scan_event); + schedule_work(&priv->down); + } else { + priv->status &= ~STATUS_RF_KILL_SW; + if (rf_kill_active(priv)) { + IPW_DEBUG_RF_KILL("Can not turn radio back on - " + "disabled by HW switch\n"); + /* Make sure the RF_KILL check timer is running */ + cancel_delayed_work(&priv->rf_kill); + schedule_delayed_work(&priv->rf_kill, + round_jiffies_relative(2 * HZ)); + } else + schedule_work(&priv->up); + } + + return 1; +} + +static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + + ipw_radio_kill_sw(priv, buf[0] == '1'); + + return count; +} + +static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill); + +static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + int pos = 0, len = 0; + if (priv->config & CFG_SPEED_SCAN) { + while (priv->speed_scan[pos] != 0) + len += sprintf(&buf[len], "%d ", + priv->speed_scan[pos++]); + return len + sprintf(&buf[len], "\n"); + } + + return sprintf(buf, "0\n"); +} + +static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + int channel, pos = 0; + const char *p = buf; + + /* list of space separated channels to scan, optionally ending with 0 */ + while ((channel = simple_strtol(p, NULL, 0))) { + if (pos == MAX_SPEED_SCAN - 1) { + priv->speed_scan[pos] = 0; + break; + } + + if (libipw_is_valid_channel(priv->ieee, channel)) + priv->speed_scan[pos++] = channel; + else + IPW_WARNING("Skipping invalid channel request: %d\n", + channel); + p = strchr(p, ' '); + if (!p) + break; + while (*p == ' ' || *p == '\t') + p++; + } + + if (pos == 0) + priv->config &= ~CFG_SPEED_SCAN; + else { + priv->speed_scan_pos = 0; + priv->config |= CFG_SPEED_SCAN; + } + + return count; +} + +static DEVICE_ATTR(speed_scan, S_IWUSR | S_IRUGO, show_speed_scan, + store_speed_scan); + +static ssize_t show_net_stats(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + return sprintf(buf, "%c\n", (priv->config & CFG_NET_STATS) ? '1' : '0'); +} + +static ssize_t store_net_stats(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + if (buf[0] == '1') + priv->config |= CFG_NET_STATS; + else + priv->config &= ~CFG_NET_STATS; + + return count; +} + +static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO, + show_net_stats, store_net_stats); + +static ssize_t show_channels(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); + int len = 0, i; + + len = sprintf(&buf[len], + "Displaying %d channels in 2.4Ghz band " + "(802.11bg):\n", geo->bg_channels); + + for (i = 0; i < geo->bg_channels; i++) { + len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n", + geo->bg[i].channel, + geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT ? + " (radar spectrum)" : "", + ((geo->bg[i].flags & LIBIPW_CH_NO_IBSS) || + (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT)) + ? "" : ", IBSS", + geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY ? + "passive only" : "active/passive", + geo->bg[i].flags & LIBIPW_CH_B_ONLY ? + "B" : "B/G"); + } + + len += sprintf(&buf[len], + "Displaying %d channels in 5.2Ghz band " + "(802.11a):\n", geo->a_channels); + for (i = 0; i < geo->a_channels; i++) { + len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n", + geo->a[i].channel, + geo->a[i].flags & LIBIPW_CH_RADAR_DETECT ? + " (radar spectrum)" : "", + ((geo->a[i].flags & LIBIPW_CH_NO_IBSS) || + (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT)) + ? "" : ", IBSS", + geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY ? + "passive only" : "active/passive"); + } + + return len; +} + +static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL); + +static void notify_wx_assoc_event(struct ipw_priv *priv) +{ + union iwreq_data wrqu; + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + if (priv->status & STATUS_ASSOCIATED) + memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN); + else + eth_zero_addr(wrqu.ap_addr.sa_data); + wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); +} + +static void ipw_irq_tasklet(struct ipw_priv *priv) +{ + u32 inta, inta_mask, handled = 0; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&priv->irq_lock, flags); + + inta = ipw_read32(priv, IPW_INTA_RW); + inta_mask = ipw_read32(priv, IPW_INTA_MASK_R); + + if (inta == 0xFFFFFFFF) { + /* Hardware disappeared */ + IPW_WARNING("TASKLET INTA == 0xFFFFFFFF\n"); + /* Only handle the cached INTA values */ + inta = 0; + } + inta &= (IPW_INTA_MASK_ALL & inta_mask); + + /* Add any cached INTA values that need to be handled */ + inta |= priv->isr_inta; + + spin_unlock_irqrestore(&priv->irq_lock, flags); + + spin_lock_irqsave(&priv->lock, flags); + + /* handle all the justifications for the interrupt */ + if (inta & IPW_INTA_BIT_RX_TRANSFER) { + ipw_rx(priv); + handled |= IPW_INTA_BIT_RX_TRANSFER; + } + + if (inta & IPW_INTA_BIT_TX_CMD_QUEUE) { + IPW_DEBUG_HC("Command completed.\n"); + rc = ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1); + priv->status &= ~STATUS_HCMD_ACTIVE; + wake_up_interruptible(&priv->wait_command_queue); + handled |= IPW_INTA_BIT_TX_CMD_QUEUE; + } + + if (inta & IPW_INTA_BIT_TX_QUEUE_1) { + IPW_DEBUG_TX("TX_QUEUE_1\n"); + rc = ipw_queue_tx_reclaim(priv, &priv->txq[0], 0); + handled |= IPW_INTA_BIT_TX_QUEUE_1; + } + + if (inta & IPW_INTA_BIT_TX_QUEUE_2) { + IPW_DEBUG_TX("TX_QUEUE_2\n"); + rc = ipw_queue_tx_reclaim(priv, &priv->txq[1], 1); + handled |= IPW_INTA_BIT_TX_QUEUE_2; + } + + if (inta & IPW_INTA_BIT_TX_QUEUE_3) { + IPW_DEBUG_TX("TX_QUEUE_3\n"); + rc = ipw_queue_tx_reclaim(priv, &priv->txq[2], 2); + handled |= IPW_INTA_BIT_TX_QUEUE_3; + } + + if (inta & IPW_INTA_BIT_TX_QUEUE_4) { + IPW_DEBUG_TX("TX_QUEUE_4\n"); + rc = ipw_queue_tx_reclaim(priv, &priv->txq[3], 3); + handled |= IPW_INTA_BIT_TX_QUEUE_4; + } + + if (inta & IPW_INTA_BIT_STATUS_CHANGE) { + IPW_WARNING("STATUS_CHANGE\n"); + handled |= IPW_INTA_BIT_STATUS_CHANGE; + } + + if (inta & IPW_INTA_BIT_BEACON_PERIOD_EXPIRED) { + IPW_WARNING("TX_PERIOD_EXPIRED\n"); + handled |= IPW_INTA_BIT_BEACON_PERIOD_EXPIRED; + } + + if (inta & IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) { + IPW_WARNING("HOST_CMD_DONE\n"); + handled |= IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE; + } + + if (inta & IPW_INTA_BIT_FW_INITIALIZATION_DONE) { + IPW_WARNING("FW_INITIALIZATION_DONE\n"); + handled |= IPW_INTA_BIT_FW_INITIALIZATION_DONE; + } + + if (inta & IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) { + IPW_WARNING("PHY_OFF_DONE\n"); + handled |= IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE; + } + + if (inta & IPW_INTA_BIT_RF_KILL_DONE) { + IPW_DEBUG_RF_KILL("RF_KILL_DONE\n"); + priv->status |= STATUS_RF_KILL_HW; + wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); + wake_up_interruptible(&priv->wait_command_queue); + priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); + cancel_delayed_work(&priv->request_scan); + cancel_delayed_work(&priv->request_direct_scan); + cancel_delayed_work(&priv->request_passive_scan); + cancel_delayed_work(&priv->scan_event); + schedule_work(&priv->link_down); + schedule_delayed_work(&priv->rf_kill, 2 * HZ); + handled |= IPW_INTA_BIT_RF_KILL_DONE; + } + + if (inta & IPW_INTA_BIT_FATAL_ERROR) { + IPW_WARNING("Firmware error detected. Restarting.\n"); + if (priv->error) { + IPW_DEBUG_FW("Sysfs 'error' log already exists.\n"); + if (ipw_debug_level & IPW_DL_FW_ERRORS) { + struct ipw_fw_error *error = + ipw_alloc_error_log(priv); + ipw_dump_error_log(priv, error); + kfree(error); + } + } else { + priv->error = ipw_alloc_error_log(priv); + if (priv->error) + IPW_DEBUG_FW("Sysfs 'error' log captured.\n"); + else + IPW_DEBUG_FW("Error allocating sysfs 'error' " + "log.\n"); + if (ipw_debug_level & IPW_DL_FW_ERRORS) + ipw_dump_error_log(priv, priv->error); + } + + /* XXX: If hardware encryption is for WPA/WPA2, + * we have to notify the supplicant. */ + if (priv->ieee->sec.encrypt) { + priv->status &= ~STATUS_ASSOCIATED; + notify_wx_assoc_event(priv); + } + + /* Keep the restart process from trying to send host + * commands by clearing the INIT status bit */ + priv->status &= ~STATUS_INIT; + + /* Cancel currently queued command. */ + priv->status &= ~STATUS_HCMD_ACTIVE; + wake_up_interruptible(&priv->wait_command_queue); + + schedule_work(&priv->adapter_restart); + handled |= IPW_INTA_BIT_FATAL_ERROR; + } + + if (inta & IPW_INTA_BIT_PARITY_ERROR) { + IPW_ERROR("Parity error\n"); + handled |= IPW_INTA_BIT_PARITY_ERROR; + } + + if (handled != inta) { + IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + /* enable all interrupts */ + ipw_enable_interrupts(priv); +} + +#define IPW_CMD(x) case IPW_CMD_ ## x : return #x +static char *get_cmd_string(u8 cmd) +{ + switch (cmd) { + IPW_CMD(HOST_COMPLETE); + IPW_CMD(POWER_DOWN); + IPW_CMD(SYSTEM_CONFIG); + IPW_CMD(MULTICAST_ADDRESS); + IPW_CMD(SSID); + IPW_CMD(ADAPTER_ADDRESS); + IPW_CMD(PORT_TYPE); + IPW_CMD(RTS_THRESHOLD); + IPW_CMD(FRAG_THRESHOLD); + IPW_CMD(POWER_MODE); + IPW_CMD(WEP_KEY); + IPW_CMD(TGI_TX_KEY); + IPW_CMD(SCAN_REQUEST); + IPW_CMD(SCAN_REQUEST_EXT); + IPW_CMD(ASSOCIATE); + IPW_CMD(SUPPORTED_RATES); + IPW_CMD(SCAN_ABORT); + IPW_CMD(TX_FLUSH); + IPW_CMD(QOS_PARAMETERS); + IPW_CMD(DINO_CONFIG); + IPW_CMD(RSN_CAPABILITIES); + IPW_CMD(RX_KEY); + IPW_CMD(CARD_DISABLE); + IPW_CMD(SEED_NUMBER); + IPW_CMD(TX_POWER); + IPW_CMD(COUNTRY_INFO); + IPW_CMD(AIRONET_INFO); + IPW_CMD(AP_TX_POWER); + IPW_CMD(CCKM_INFO); + IPW_CMD(CCX_VER_INFO); + IPW_CMD(SET_CALIBRATION); + IPW_CMD(SENSITIVITY_CALIB); + IPW_CMD(RETRY_LIMIT); + IPW_CMD(IPW_PRE_POWER_DOWN); + IPW_CMD(VAP_BEACON_TEMPLATE); + IPW_CMD(VAP_DTIM_PERIOD); + IPW_CMD(EXT_SUPPORTED_RATES); + IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT); + IPW_CMD(VAP_QUIET_INTERVALS); + IPW_CMD(VAP_CHANNEL_SWITCH); + IPW_CMD(VAP_MANDATORY_CHANNELS); + IPW_CMD(VAP_CELL_PWR_LIMIT); + IPW_CMD(VAP_CF_PARAM_SET); + IPW_CMD(VAP_SET_BEACONING_STATE); + IPW_CMD(MEASUREMENT); + IPW_CMD(POWER_CAPABILITY); + IPW_CMD(SUPPORTED_CHANNELS); + IPW_CMD(TPC_REPORT); + IPW_CMD(WME_INFO); + IPW_CMD(PRODUCTION_COMMAND); + default: + return "UNKNOWN"; + } +} + +#define HOST_COMPLETE_TIMEOUT HZ + +static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd) +{ + int rc = 0; + unsigned long flags; + unsigned long now, end; + + spin_lock_irqsave(&priv->lock, flags); + if (priv->status & STATUS_HCMD_ACTIVE) { + IPW_ERROR("Failed to send %s: Already sending a command.\n", + get_cmd_string(cmd->cmd)); + spin_unlock_irqrestore(&priv->lock, flags); + return -EAGAIN; + } + + priv->status |= STATUS_HCMD_ACTIVE; + + if (priv->cmdlog) { + priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies; + priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd; + priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len; + memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param, + cmd->len); + priv->cmdlog[priv->cmdlog_pos].retcode = -1; + } + + IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n", + get_cmd_string(cmd->cmd), cmd->cmd, cmd->len, + priv->status); + +#ifndef DEBUG_CMD_WEP_KEY + if (cmd->cmd == IPW_CMD_WEP_KEY) + IPW_DEBUG_HC("WEP_KEY command masked out for secure.\n"); + else +#endif + printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len); + + rc = ipw_queue_tx_hcmd(priv, cmd->cmd, cmd->param, cmd->len, 0); + if (rc) { + priv->status &= ~STATUS_HCMD_ACTIVE; + IPW_ERROR("Failed to send %s: Reason %d\n", + get_cmd_string(cmd->cmd), rc); + spin_unlock_irqrestore(&priv->lock, flags); + goto exit; + } + spin_unlock_irqrestore(&priv->lock, flags); + + now = jiffies; + end = now + HOST_COMPLETE_TIMEOUT; +again: + rc = wait_event_interruptible_timeout(priv->wait_command_queue, + !(priv-> + status & STATUS_HCMD_ACTIVE), + end - now); + if (rc < 0) { + now = jiffies; + if (time_before(now, end)) + goto again; + rc = 0; + } + + if (rc == 0) { + spin_lock_irqsave(&priv->lock, flags); + if (priv->status & STATUS_HCMD_ACTIVE) { + IPW_ERROR("Failed to send %s: Command timed out.\n", + get_cmd_string(cmd->cmd)); + priv->status &= ~STATUS_HCMD_ACTIVE; + spin_unlock_irqrestore(&priv->lock, flags); + rc = -EIO; + goto exit; + } + spin_unlock_irqrestore(&priv->lock, flags); + } else + rc = 0; + + if (priv->status & STATUS_RF_KILL_HW) { + IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n", + get_cmd_string(cmd->cmd)); + rc = -EIO; + goto exit; + } + + exit: + if (priv->cmdlog) { + priv->cmdlog[priv->cmdlog_pos++].retcode = rc; + priv->cmdlog_pos %= priv->cmdlog_len; + } + return rc; +} + +static int ipw_send_cmd_simple(struct ipw_priv *priv, u8 command) +{ + struct host_cmd cmd = { + .cmd = command, + }; + + return __ipw_send_cmd(priv, &cmd); +} + +static int ipw_send_cmd_pdu(struct ipw_priv *priv, u8 command, u8 len, + void *data) +{ + struct host_cmd cmd = { + .cmd = command, + .len = len, + .param = data, + }; + + return __ipw_send_cmd(priv, &cmd); +} + +static int ipw_send_host_complete(struct ipw_priv *priv) +{ + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + return ipw_send_cmd_simple(priv, IPW_CMD_HOST_COMPLETE); +} + +static int ipw_send_system_config(struct ipw_priv *priv) +{ + return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG, + sizeof(priv->sys_config), + &priv->sys_config); +} + +static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len) +{ + if (!priv || !ssid) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + return ipw_send_cmd_pdu(priv, IPW_CMD_SSID, min(len, IW_ESSID_MAX_SIZE), + ssid); +} + +static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac) +{ + if (!priv || !mac) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + IPW_DEBUG_INFO("%s: Setting MAC to %pM\n", + priv->net_dev->name, mac); + + return ipw_send_cmd_pdu(priv, IPW_CMD_ADAPTER_ADDRESS, ETH_ALEN, mac); +} + +static void ipw_adapter_restart(void *adapter) +{ + struct ipw_priv *priv = adapter; + + if (priv->status & STATUS_RF_KILL_MASK) + return; + + ipw_down(priv); + + if (priv->assoc_network && + (priv->assoc_network->capability & WLAN_CAPABILITY_IBSS)) + ipw_remove_current_network(priv); + + if (ipw_up(priv)) { + IPW_ERROR("Failed to up device\n"); + return; + } +} + +static void ipw_bg_adapter_restart(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, adapter_restart); + mutex_lock(&priv->mutex); + ipw_adapter_restart(priv); + mutex_unlock(&priv->mutex); +} + +static void ipw_abort_scan(struct ipw_priv *priv); + +#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ) + +static void ipw_scan_check(void *data) +{ + struct ipw_priv *priv = data; + + if (priv->status & STATUS_SCAN_ABORTING) { + IPW_DEBUG_SCAN("Scan completion watchdog resetting " + "adapter after (%dms).\n", + jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG)); + schedule_work(&priv->adapter_restart); + } else if (priv->status & STATUS_SCANNING) { + IPW_DEBUG_SCAN("Scan completion watchdog aborting scan " + "after (%dms).\n", + jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG)); + ipw_abort_scan(priv); + schedule_delayed_work(&priv->scan_check, HZ); + } +} + +static void ipw_bg_scan_check(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, scan_check.work); + mutex_lock(&priv->mutex); + ipw_scan_check(priv); + mutex_unlock(&priv->mutex); +} + +static int ipw_send_scan_request_ext(struct ipw_priv *priv, + struct ipw_scan_request_ext *request) +{ + return ipw_send_cmd_pdu(priv, IPW_CMD_SCAN_REQUEST_EXT, + sizeof(*request), request); +} + +static int ipw_send_scan_abort(struct ipw_priv *priv) +{ + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + return ipw_send_cmd_simple(priv, IPW_CMD_SCAN_ABORT); +} + +static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens) +{ + struct ipw_sensitivity_calib calib = { + .beacon_rssi_raw = cpu_to_le16(sens), + }; + + return ipw_send_cmd_pdu(priv, IPW_CMD_SENSITIVITY_CALIB, sizeof(calib), + &calib); +} + +static int ipw_send_associate(struct ipw_priv *priv, + struct ipw_associate *associate) +{ + if (!priv || !associate) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + return ipw_send_cmd_pdu(priv, IPW_CMD_ASSOCIATE, sizeof(*associate), + associate); +} + +static int ipw_send_supported_rates(struct ipw_priv *priv, + struct ipw_supported_rates *rates) +{ + if (!priv || !rates) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + return ipw_send_cmd_pdu(priv, IPW_CMD_SUPPORTED_RATES, sizeof(*rates), + rates); +} + +static int ipw_set_random_seed(struct ipw_priv *priv) +{ + u32 val; + + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + get_random_bytes(&val, sizeof(val)); + + return ipw_send_cmd_pdu(priv, IPW_CMD_SEED_NUMBER, sizeof(val), &val); +} + +static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off) +{ + __le32 v = cpu_to_le32(phy_off); + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + return ipw_send_cmd_pdu(priv, IPW_CMD_CARD_DISABLE, sizeof(v), &v); +} + +static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power) +{ + if (!priv || !power) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + return ipw_send_cmd_pdu(priv, IPW_CMD_TX_POWER, sizeof(*power), power); +} + +static int ipw_set_tx_power(struct ipw_priv *priv) +{ + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); + struct ipw_tx_power tx_power; + s8 max_power; + int i; + + memset(&tx_power, 0, sizeof(tx_power)); + + /* configure device for 'G' band */ + tx_power.ieee_mode = IPW_G_MODE; + tx_power.num_channels = geo->bg_channels; + for (i = 0; i < geo->bg_channels; i++) { + max_power = geo->bg[i].max_power; + tx_power.channels_tx_power[i].channel_number = + geo->bg[i].channel; + tx_power.channels_tx_power[i].tx_power = max_power ? + min(max_power, priv->tx_power) : priv->tx_power; + } + if (ipw_send_tx_power(priv, &tx_power)) + return -EIO; + + /* configure device to also handle 'B' band */ + tx_power.ieee_mode = IPW_B_MODE; + if (ipw_send_tx_power(priv, &tx_power)) + return -EIO; + + /* configure device to also handle 'A' band */ + if (priv->ieee->abg_true) { + tx_power.ieee_mode = IPW_A_MODE; + tx_power.num_channels = geo->a_channels; + for (i = 0; i < tx_power.num_channels; i++) { + max_power = geo->a[i].max_power; + tx_power.channels_tx_power[i].channel_number = + geo->a[i].channel; + tx_power.channels_tx_power[i].tx_power = max_power ? + min(max_power, priv->tx_power) : priv->tx_power; + } + if (ipw_send_tx_power(priv, &tx_power)) + return -EIO; + } + return 0; +} + +static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts) +{ + struct ipw_rts_threshold rts_threshold = { + .rts_threshold = cpu_to_le16(rts), + }; + + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + return ipw_send_cmd_pdu(priv, IPW_CMD_RTS_THRESHOLD, + sizeof(rts_threshold), &rts_threshold); +} + +static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag) +{ + struct ipw_frag_threshold frag_threshold = { + .frag_threshold = cpu_to_le16(frag), + }; + + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + return ipw_send_cmd_pdu(priv, IPW_CMD_FRAG_THRESHOLD, + sizeof(frag_threshold), &frag_threshold); +} + +static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode) +{ + __le32 param; + + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + /* If on battery, set to 3, if AC set to CAM, else user + * level */ + switch (mode) { + case IPW_POWER_BATTERY: + param = cpu_to_le32(IPW_POWER_INDEX_3); + break; + case IPW_POWER_AC: + param = cpu_to_le32(IPW_POWER_MODE_CAM); + break; + default: + param = cpu_to_le32(mode); + break; + } + + return ipw_send_cmd_pdu(priv, IPW_CMD_POWER_MODE, sizeof(param), + ¶m); +} + +static int ipw_send_retry_limit(struct ipw_priv *priv, u8 slimit, u8 llimit) +{ + struct ipw_retry_limit retry_limit = { + .short_retry_limit = slimit, + .long_retry_limit = llimit + }; + + if (!priv) { + IPW_ERROR("Invalid args\n"); + return -1; + } + + return ipw_send_cmd_pdu(priv, IPW_CMD_RETRY_LIMIT, sizeof(retry_limit), + &retry_limit); +} + +/* + * The IPW device contains a Microwire compatible EEPROM that stores + * various data like the MAC address. Usually the firmware has exclusive + * access to the eeprom, but during device initialization (before the + * device driver has sent the HostComplete command to the firmware) the + * device driver has read access to the EEPROM by way of indirect addressing + * through a couple of memory mapped registers. + * + * The following is a simplified implementation for pulling data out of the + * the eeprom, along with some helper functions to find information in + * the per device private data's copy of the eeprom. + * + * NOTE: To better understand how these functions work (i.e what is a chip + * select and why do have to keep driving the eeprom clock?), read + * just about any data sheet for a Microwire compatible EEPROM. + */ + +/* write a 32 bit value into the indirect accessor register */ +static inline void eeprom_write_reg(struct ipw_priv *p, u32 data) +{ + ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data); + + /* the eeprom requires some time to complete the operation */ + udelay(p->eeprom_delay); +} + +/* perform a chip select operation */ +static void eeprom_cs(struct ipw_priv *priv) +{ + eeprom_write_reg(priv, 0); + eeprom_write_reg(priv, EEPROM_BIT_CS); + eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK); + eeprom_write_reg(priv, EEPROM_BIT_CS); +} + +/* perform a chip select operation */ +static void eeprom_disable_cs(struct ipw_priv *priv) +{ + eeprom_write_reg(priv, EEPROM_BIT_CS); + eeprom_write_reg(priv, 0); + eeprom_write_reg(priv, EEPROM_BIT_SK); +} + +/* push a single bit down to the eeprom */ +static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit) +{ + int d = (bit ? EEPROM_BIT_DI : 0); + eeprom_write_reg(p, EEPROM_BIT_CS | d); + eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK); +} + +/* push an opcode followed by an address down to the eeprom */ +static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr) +{ + int i; + + eeprom_cs(priv); + eeprom_write_bit(priv, 1); + eeprom_write_bit(priv, op & 2); + eeprom_write_bit(priv, op & 1); + for (i = 7; i >= 0; i--) { + eeprom_write_bit(priv, addr & (1 << i)); + } +} + +/* pull 16 bits off the eeprom, one bit at a time */ +static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr) +{ + int i; + u16 r = 0; + + /* Send READ Opcode */ + eeprom_op(priv, EEPROM_CMD_READ, addr); + + /* Send dummy bit */ + eeprom_write_reg(priv, EEPROM_BIT_CS); + + /* Read the byte off the eeprom one bit at a time */ + for (i = 0; i < 16; i++) { + u32 data = 0; + eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK); + eeprom_write_reg(priv, EEPROM_BIT_CS); + data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS); + r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0); + } + + /* Send another dummy bit */ + eeprom_write_reg(priv, 0); + eeprom_disable_cs(priv); + + return r; +} + +/* helper function for pulling the mac address out of the private */ +/* data's copy of the eeprom data */ +static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac) +{ + memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], ETH_ALEN); +} + +static void ipw_read_eeprom(struct ipw_priv *priv) +{ + int i; + __le16 *eeprom = (__le16 *) priv->eeprom; + + IPW_DEBUG_TRACE(">>\n"); + + /* read entire contents of eeprom into private buffer */ + for (i = 0; i < 128; i++) + eeprom[i] = cpu_to_le16(eeprom_read_u16(priv, (u8) i)); + + IPW_DEBUG_TRACE("<<\n"); +} + +/* + * Either the device driver (i.e. the host) or the firmware can + * load eeprom data into the designated region in SRAM. If neither + * happens then the FW will shutdown with a fatal error. + * + * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE + * bit needs region of shared SRAM needs to be non-zero. + */ +static void ipw_eeprom_init_sram(struct ipw_priv *priv) +{ + int i; + + IPW_DEBUG_TRACE(">>\n"); + + /* + If the data looks correct, then copy it to our private + copy. Otherwise let the firmware know to perform the operation + on its own. + */ + if (priv->eeprom[EEPROM_VERSION] != 0) { + IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n"); + + /* write the eeprom data to sram */ + for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++) + ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]); + + /* Do not load eeprom data on fatal error or suspend */ + ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0); + } else { + IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n"); + + /* Load eeprom data on fatal error or suspend */ + ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1); + } + + IPW_DEBUG_TRACE("<<\n"); +} + +static void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count) +{ + count >>= 2; + if (!count) + return; + _ipw_write32(priv, IPW_AUTOINC_ADDR, start); + while (count--) + _ipw_write32(priv, IPW_AUTOINC_DATA, 0); +} + +static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv) +{ + ipw_zero_memory(priv, IPW_SHARED_SRAM_DMA_CONTROL, + CB_NUMBER_OF_ELEMENTS_SMALL * + sizeof(struct command_block)); +} + +static int ipw_fw_dma_enable(struct ipw_priv *priv) +{ /* start dma engine but no transfers yet */ + + IPW_DEBUG_FW(">> :\n"); + + /* Start the dma */ + ipw_fw_dma_reset_command_blocks(priv); + + /* Write CB base address */ + ipw_write_reg32(priv, IPW_DMA_I_CB_BASE, IPW_SHARED_SRAM_DMA_CONTROL); + + IPW_DEBUG_FW("<< :\n"); + return 0; +} + +static void ipw_fw_dma_abort(struct ipw_priv *priv) +{ + u32 control = 0; + + IPW_DEBUG_FW(">> :\n"); + + /* set the Stop and Abort bit */ + control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT; + ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control); + priv->sram_desc.last_cb_index = 0; + + IPW_DEBUG_FW("<<\n"); +} + +static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index, + struct command_block *cb) +{ + u32 address = + IPW_SHARED_SRAM_DMA_CONTROL + + (sizeof(struct command_block) * index); + IPW_DEBUG_FW(">> :\n"); + + ipw_write_indirect(priv, address, (u8 *) cb, + (int)sizeof(struct command_block)); + + IPW_DEBUG_FW("<< :\n"); + return 0; + +} + +static int ipw_fw_dma_kick(struct ipw_priv *priv) +{ + u32 control = 0; + u32 index = 0; + + IPW_DEBUG_FW(">> :\n"); + + for (index = 0; index < priv->sram_desc.last_cb_index; index++) + ipw_fw_dma_write_command_block(priv, index, + &priv->sram_desc.cb_list[index]); + + /* Enable the DMA in the CSR register */ + ipw_clear_bit(priv, IPW_RESET_REG, + IPW_RESET_REG_MASTER_DISABLED | + IPW_RESET_REG_STOP_MASTER); + + /* Set the Start bit. */ + control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START; + ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control); + + IPW_DEBUG_FW("<< :\n"); + return 0; +} + +static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv) +{ + u32 address; + u32 register_value = 0; + u32 cb_fields_address = 0; + + IPW_DEBUG_FW(">> :\n"); + address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB); + IPW_DEBUG_FW_INFO("Current CB is 0x%x\n", address); + + /* Read the DMA Controlor register */ + register_value = ipw_read_reg32(priv, IPW_DMA_I_DMA_CONTROL); + IPW_DEBUG_FW_INFO("IPW_DMA_I_DMA_CONTROL is 0x%x\n", register_value); + + /* Print the CB values */ + cb_fields_address = address; + register_value = ipw_read_reg32(priv, cb_fields_address); + IPW_DEBUG_FW_INFO("Current CB Control Field is 0x%x\n", register_value); + + cb_fields_address += sizeof(u32); + register_value = ipw_read_reg32(priv, cb_fields_address); + IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x\n", register_value); + + cb_fields_address += sizeof(u32); + register_value = ipw_read_reg32(priv, cb_fields_address); + IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x\n", + register_value); + + cb_fields_address += sizeof(u32); + register_value = ipw_read_reg32(priv, cb_fields_address); + IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x\n", register_value); + + IPW_DEBUG_FW(">> :\n"); +} + +static int ipw_fw_dma_command_block_index(struct ipw_priv *priv) +{ + u32 current_cb_address = 0; + u32 current_cb_index = 0; + + IPW_DEBUG_FW("<< :\n"); + current_cb_address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB); + + current_cb_index = (current_cb_address - IPW_SHARED_SRAM_DMA_CONTROL) / + sizeof(struct command_block); + + IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X\n", + current_cb_index, current_cb_address); + + IPW_DEBUG_FW(">> :\n"); + return current_cb_index; + +} + +static int ipw_fw_dma_add_command_block(struct ipw_priv *priv, + u32 src_address, + u32 dest_address, + u32 length, + int interrupt_enabled, int is_last) +{ + + u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC | + CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG | + CB_DEST_SIZE_LONG; + struct command_block *cb; + u32 last_cb_element = 0; + + IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n", + src_address, dest_address, length); + + if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL) + return -1; + + last_cb_element = priv->sram_desc.last_cb_index; + cb = &priv->sram_desc.cb_list[last_cb_element]; + priv->sram_desc.last_cb_index++; + + /* Calculate the new CB control word */ + if (interrupt_enabled) + control |= CB_INT_ENABLED; + + if (is_last) + control |= CB_LAST_VALID; + + control |= length; + + /* Calculate the CB Element's checksum value */ + cb->status = control ^ src_address ^ dest_address; + + /* Copy the Source and Destination addresses */ + cb->dest_addr = dest_address; + cb->source_addr = src_address; + + /* Copy the Control Word last */ + cb->control = control; + + return 0; +} + +static int ipw_fw_dma_add_buffer(struct ipw_priv *priv, dma_addr_t *src_address, + int nr, u32 dest_address, u32 len) +{ + int ret, i; + u32 size; + + IPW_DEBUG_FW(">>\n"); + IPW_DEBUG_FW_INFO("nr=%d dest_address=0x%x len=0x%x\n", + nr, dest_address, len); + + for (i = 0; i < nr; i++) { + size = min_t(u32, len - i * CB_MAX_LENGTH, CB_MAX_LENGTH); + ret = ipw_fw_dma_add_command_block(priv, src_address[i], + dest_address + + i * CB_MAX_LENGTH, size, + 0, 0); + if (ret) { + IPW_DEBUG_FW_INFO(": Failed\n"); + return -1; + } else + IPW_DEBUG_FW_INFO(": Added new cb\n"); + } + + IPW_DEBUG_FW("<<\n"); + return 0; +} + +static int ipw_fw_dma_wait(struct ipw_priv *priv) +{ + u32 current_index = 0, previous_index; + u32 watchdog = 0; + + IPW_DEBUG_FW(">> :\n"); + + current_index = ipw_fw_dma_command_block_index(priv); + IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%08X\n", + (int)priv->sram_desc.last_cb_index); + + while (current_index < priv->sram_desc.last_cb_index) { + udelay(50); + previous_index = current_index; + current_index = ipw_fw_dma_command_block_index(priv); + + if (previous_index < current_index) { + watchdog = 0; + continue; + } + if (++watchdog > 400) { + IPW_DEBUG_FW_INFO("Timeout\n"); + ipw_fw_dma_dump_command_block(priv); + ipw_fw_dma_abort(priv); + return -1; + } + } + + ipw_fw_dma_abort(priv); + + /*Disable the DMA in the CSR register */ + ipw_set_bit(priv, IPW_RESET_REG, + IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER); + + IPW_DEBUG_FW("<< dmaWaitSync\n"); + return 0; +} + +static void ipw_remove_current_network(struct ipw_priv *priv) +{ + struct list_head *element, *safe; + struct libipw_network *network = NULL; + unsigned long flags; + + spin_lock_irqsave(&priv->ieee->lock, flags); + list_for_each_safe(element, safe, &priv->ieee->network_list) { + network = list_entry(element, struct libipw_network, list); + if (ether_addr_equal(network->bssid, priv->bssid)) { + list_del(element); + list_add_tail(&network->list, + &priv->ieee->network_free_list); + } + } + spin_unlock_irqrestore(&priv->ieee->lock, flags); +} + +/** + * Check that card is still alive. + * Reads debug register from domain0. + * If card is present, pre-defined value should + * be found there. + * + * @param priv + * @return 1 if card is present, 0 otherwise + */ +static inline int ipw_alive(struct ipw_priv *priv) +{ + return ipw_read32(priv, 0x90) == 0xd55555d5; +} + +/* timeout in msec, attempted in 10-msec quanta */ +static int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask, + int timeout) +{ + int i = 0; + + do { + if ((ipw_read32(priv, addr) & mask) == mask) + return i; + mdelay(10); + i += 10; + } while (i < timeout); + + return -ETIME; +} + +/* These functions load the firmware and micro code for the operation of + * the ipw hardware. It assumes the buffer has all the bits for the + * image and the caller is handling the memory allocation and clean up. + */ + +static int ipw_stop_master(struct ipw_priv *priv) +{ + int rc; + + IPW_DEBUG_TRACE(">>\n"); + /* stop master. typical delay - 0 */ + ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER); + + /* timeout is in msec, polled in 10-msec quanta */ + rc = ipw_poll_bit(priv, IPW_RESET_REG, + IPW_RESET_REG_MASTER_DISABLED, 100); + if (rc < 0) { + IPW_ERROR("wait for stop master failed after 100ms\n"); + return -1; + } + + IPW_DEBUG_INFO("stop master %dms\n", rc); + + return rc; +} + +static void ipw_arc_release(struct ipw_priv *priv) +{ + IPW_DEBUG_TRACE(">>\n"); + mdelay(5); + + ipw_clear_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET); + + /* no one knows timing, for safety add some delay */ + mdelay(5); +} + +struct fw_chunk { + __le32 address; + __le32 length; +}; + +static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len) +{ + int rc = 0, i, addr; + u8 cr = 0; + __le16 *image; + + image = (__le16 *) data; + + IPW_DEBUG_TRACE(">>\n"); + + rc = ipw_stop_master(priv); + + if (rc < 0) + return rc; + + for (addr = IPW_SHARED_LOWER_BOUND; + addr < IPW_REGISTER_DOMAIN1_END; addr += 4) { + ipw_write32(priv, addr, 0); + } + + /* no ucode (yet) */ + memset(&priv->dino_alive, 0, sizeof(priv->dino_alive)); + /* destroy DMA queues */ + /* reset sequence */ + + ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_ON); + ipw_arc_release(priv); + ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_OFF); + mdelay(1); + + /* reset PHY */ + ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, IPW_BASEBAND_POWER_DOWN); + mdelay(1); + + ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, 0); + mdelay(1); + + /* enable ucode store */ + ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0x0); + ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_CS); + mdelay(1); + + /* write ucode */ + /** + * @bug + * Do NOT set indirect address register once and then + * store data to indirect data register in the loop. + * It seems very reasonable, but in this case DINO do not + * accept ucode. It is essential to set address each time. + */ + /* load new ipw uCode */ + for (i = 0; i < len / 2; i++) + ipw_write_reg16(priv, IPW_BASEBAND_CONTROL_STORE, + le16_to_cpu(image[i])); + + /* enable DINO */ + ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0); + ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM); + + /* this is where the igx / win driver deveates from the VAP driver. */ + + /* wait for alive response */ + for (i = 0; i < 100; i++) { + /* poll for incoming data */ + cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS); + if (cr & DINO_RXFIFO_DATA) + break; + mdelay(1); + } + + if (cr & DINO_RXFIFO_DATA) { + /* alive_command_responce size is NOT multiple of 4 */ + __le32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4]; + + for (i = 0; i < ARRAY_SIZE(response_buffer); i++) + response_buffer[i] = + cpu_to_le32(ipw_read_reg32(priv, + IPW_BASEBAND_RX_FIFO_READ)); + memcpy(&priv->dino_alive, response_buffer, + sizeof(priv->dino_alive)); + if (priv->dino_alive.alive_command == 1 + && priv->dino_alive.ucode_valid == 1) { + rc = 0; + IPW_DEBUG_INFO + ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) " + "of %02d/%02d/%02d %02d:%02d\n", + priv->dino_alive.software_revision, + priv->dino_alive.software_revision, + priv->dino_alive.device_identifier, + priv->dino_alive.device_identifier, + priv->dino_alive.time_stamp[0], + priv->dino_alive.time_stamp[1], + priv->dino_alive.time_stamp[2], + priv->dino_alive.time_stamp[3], + priv->dino_alive.time_stamp[4]); + } else { + IPW_DEBUG_INFO("Microcode is not alive\n"); + rc = -EINVAL; + } + } else { + IPW_DEBUG_INFO("No alive response from DINO\n"); + rc = -ETIME; + } + + /* disable DINO, otherwise for some reason + firmware have problem getting alive resp. */ + ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0); + + return rc; +} + +static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len) +{ + int ret = -1; + int offset = 0; + struct fw_chunk *chunk; + int total_nr = 0; + int i; + struct pci_pool *pool; + void **virts; + dma_addr_t *phys; + + IPW_DEBUG_TRACE("<< :\n"); + + virts = kmalloc(sizeof(void *) * CB_NUMBER_OF_ELEMENTS_SMALL, + GFP_KERNEL); + if (!virts) + return -ENOMEM; + + phys = kmalloc(sizeof(dma_addr_t) * CB_NUMBER_OF_ELEMENTS_SMALL, + GFP_KERNEL); + if (!phys) { + kfree(virts); + return -ENOMEM; + } + pool = pci_pool_create("ipw2200", priv->pci_dev, CB_MAX_LENGTH, 0, 0); + if (!pool) { + IPW_ERROR("pci_pool_create failed\n"); + kfree(phys); + kfree(virts); + return -ENOMEM; + } + + /* Start the Dma */ + ret = ipw_fw_dma_enable(priv); + + /* the DMA is already ready this would be a bug. */ + BUG_ON(priv->sram_desc.last_cb_index > 0); + + do { + u32 chunk_len; + u8 *start; + int size; + int nr = 0; + + chunk = (struct fw_chunk *)(data + offset); + offset += sizeof(struct fw_chunk); + chunk_len = le32_to_cpu(chunk->length); + start = data + offset; + + nr = (chunk_len + CB_MAX_LENGTH - 1) / CB_MAX_LENGTH; + for (i = 0; i < nr; i++) { + virts[total_nr] = pci_pool_alloc(pool, GFP_KERNEL, + &phys[total_nr]); + if (!virts[total_nr]) { + ret = -ENOMEM; + goto out; + } + size = min_t(u32, chunk_len - i * CB_MAX_LENGTH, + CB_MAX_LENGTH); + memcpy(virts[total_nr], start, size); + start += size; + total_nr++; + /* We don't support fw chunk larger than 64*8K */ + BUG_ON(total_nr > CB_NUMBER_OF_ELEMENTS_SMALL); + } + + /* build DMA packet and queue up for sending */ + /* dma to chunk->address, the chunk->length bytes from data + + * offeset*/ + /* Dma loading */ + ret = ipw_fw_dma_add_buffer(priv, &phys[total_nr - nr], + nr, le32_to_cpu(chunk->address), + chunk_len); + if (ret) { + IPW_DEBUG_INFO("dmaAddBuffer Failed\n"); + goto out; + } + + offset += chunk_len; + } while (offset < len); + + /* Run the DMA and wait for the answer */ + ret = ipw_fw_dma_kick(priv); + if (ret) { + IPW_ERROR("dmaKick Failed\n"); + goto out; + } + + ret = ipw_fw_dma_wait(priv); + if (ret) { + IPW_ERROR("dmaWaitSync Failed\n"); + goto out; + } + out: + for (i = 0; i < total_nr; i++) + pci_pool_free(pool, virts[i], phys[i]); + + pci_pool_destroy(pool); + kfree(phys); + kfree(virts); + + return ret; +} + +/* stop nic */ +static int ipw_stop_nic(struct ipw_priv *priv) +{ + int rc = 0; + + /* stop */ + ipw_write32(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER); + + rc = ipw_poll_bit(priv, IPW_RESET_REG, + IPW_RESET_REG_MASTER_DISABLED, 500); + if (rc < 0) { + IPW_ERROR("wait for reg master disabled failed after 500ms\n"); + return rc; + } + + ipw_set_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET); + + return rc; +} + +static void ipw_start_nic(struct ipw_priv *priv) +{ + IPW_DEBUG_TRACE(">>\n"); + + /* prvHwStartNic release ARC */ + ipw_clear_bit(priv, IPW_RESET_REG, + IPW_RESET_REG_MASTER_DISABLED | + IPW_RESET_REG_STOP_MASTER | + CBD_RESET_REG_PRINCETON_RESET); + + /* enable power management */ + ipw_set_bit(priv, IPW_GP_CNTRL_RW, + IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY); + + IPW_DEBUG_TRACE("<<\n"); +} + +static int ipw_init_nic(struct ipw_priv *priv) +{ + int rc; + + IPW_DEBUG_TRACE(">>\n"); + /* reset */ + /*prvHwInitNic */ + /* set "initialization complete" bit to move adapter to D0 state */ + ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE); + + /* low-level PLL activation */ + ipw_write32(priv, IPW_READ_INT_REGISTER, + IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER); + + /* wait for clock stabilization */ + rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW, + IPW_GP_CNTRL_BIT_CLOCK_READY, 250); + if (rc < 0) + IPW_DEBUG_INFO("FAILED wait for clock stablization\n"); + + /* assert SW reset */ + ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET); + + udelay(10); + + /* set "initialization complete" bit to move adapter to D0 state */ + ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE); + + IPW_DEBUG_TRACE(">>\n"); + return 0; +} + +/* Call this function from process context, it will sleep in request_firmware. + * Probe is an ok place to call this from. + */ +static int ipw_reset_nic(struct ipw_priv *priv) +{ + int rc = 0; + unsigned long flags; + + IPW_DEBUG_TRACE(">>\n"); + + rc = ipw_init_nic(priv); + + spin_lock_irqsave(&priv->lock, flags); + /* Clear the 'host command active' bit... */ + priv->status &= ~STATUS_HCMD_ACTIVE; + wake_up_interruptible(&priv->wait_command_queue); + priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING); + wake_up_interruptible(&priv->wait_state); + spin_unlock_irqrestore(&priv->lock, flags); + + IPW_DEBUG_TRACE("<<\n"); + return rc; +} + + +struct ipw_fw { + __le32 ver; + __le32 boot_size; + __le32 ucode_size; + __le32 fw_size; + u8 data[0]; +}; + +static int ipw_get_fw(struct ipw_priv *priv, + const struct firmware **raw, const char *name) +{ + struct ipw_fw *fw; + int rc; + + /* ask firmware_class module to get the boot firmware off disk */ + rc = request_firmware(raw, name, &priv->pci_dev->dev); + if (rc < 0) { + IPW_ERROR("%s request_firmware failed: Reason %d\n", name, rc); + return rc; + } + + if ((*raw)->size < sizeof(*fw)) { + IPW_ERROR("%s is too small (%zd)\n", name, (*raw)->size); + return -EINVAL; + } + + fw = (void *)(*raw)->data; + + if ((*raw)->size < sizeof(*fw) + le32_to_cpu(fw->boot_size) + + le32_to_cpu(fw->ucode_size) + le32_to_cpu(fw->fw_size)) { + IPW_ERROR("%s is too small or corrupt (%zd)\n", + name, (*raw)->size); + return -EINVAL; + } + + IPW_DEBUG_INFO("Read firmware '%s' image v%d.%d (%zd bytes)\n", + name, + le32_to_cpu(fw->ver) >> 16, + le32_to_cpu(fw->ver) & 0xff, + (*raw)->size - sizeof(*fw)); + return 0; +} + +#define IPW_RX_BUF_SIZE (3000) + +static void ipw_rx_queue_reset(struct ipw_priv *priv, + struct ipw_rx_queue *rxq) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&rxq->lock, flags); + + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + + /* Fill the rx_used queue with _all_ of the Rx buffers */ + for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { + /* In the reset function, these buffers may have been allocated + * to an SKB, so we need to unmap and free potential storage */ + if (rxq->pool[i].skb != NULL) { + pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr, + IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + dev_kfree_skb(rxq->pool[i].skb); + rxq->pool[i].skb = NULL; + } + list_add_tail(&rxq->pool[i].list, &rxq->rx_used); + } + + /* Set us so that we have processed and used all buffers, but have + * not restocked the Rx queue with fresh buffers */ + rxq->read = rxq->write = 0; + rxq->free_count = 0; + spin_unlock_irqrestore(&rxq->lock, flags); +} + +#ifdef CONFIG_PM +static int fw_loaded = 0; +static const struct firmware *raw = NULL; + +static void free_firmware(void) +{ + if (fw_loaded) { + release_firmware(raw); + raw = NULL; + fw_loaded = 0; + } +} +#else +#define free_firmware() do {} while (0) +#endif + +static int ipw_load(struct ipw_priv *priv) +{ +#ifndef CONFIG_PM + const struct firmware *raw = NULL; +#endif + struct ipw_fw *fw; + u8 *boot_img, *ucode_img, *fw_img; + u8 *name = NULL; + int rc = 0, retries = 3; + + switch (priv->ieee->iw_mode) { + case IW_MODE_ADHOC: + name = "ipw2200-ibss.fw"; + break; +#ifdef CONFIG_IPW2200_MONITOR + case IW_MODE_MONITOR: + name = "ipw2200-sniffer.fw"; + break; +#endif + case IW_MODE_INFRA: + name = "ipw2200-bss.fw"; + break; + } + + if (!name) { + rc = -EINVAL; + goto error; + } + +#ifdef CONFIG_PM + if (!fw_loaded) { +#endif + rc = ipw_get_fw(priv, &raw, name); + if (rc < 0) + goto error; +#ifdef CONFIG_PM + } +#endif + + fw = (void *)raw->data; + boot_img = &fw->data[0]; + ucode_img = &fw->data[le32_to_cpu(fw->boot_size)]; + fw_img = &fw->data[le32_to_cpu(fw->boot_size) + + le32_to_cpu(fw->ucode_size)]; + + if (rc < 0) + goto error; + + if (!priv->rxq) + priv->rxq = ipw_rx_queue_alloc(priv); + else + ipw_rx_queue_reset(priv, priv->rxq); + if (!priv->rxq) { + IPW_ERROR("Unable to initialize Rx queue\n"); + rc = -ENOMEM; + goto error; + } + + retry: + /* Ensure interrupts are disabled */ + ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL); + priv->status &= ~STATUS_INT_ENABLED; + + /* ack pending interrupts */ + ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL); + + ipw_stop_nic(priv); + + rc = ipw_reset_nic(priv); + if (rc < 0) { + IPW_ERROR("Unable to reset NIC\n"); + goto error; + } + + ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND, + IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND); + + /* DMA the initial boot firmware into the device */ + rc = ipw_load_firmware(priv, boot_img, le32_to_cpu(fw->boot_size)); + if (rc < 0) { + IPW_ERROR("Unable to load boot firmware: %d\n", rc); + goto error; + } + + /* kick start the device */ + ipw_start_nic(priv); + + /* wait for the device to finish its initial startup sequence */ + rc = ipw_poll_bit(priv, IPW_INTA_RW, + IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500); + if (rc < 0) { + IPW_ERROR("device failed to boot initial fw image\n"); + goto error; + } + IPW_DEBUG_INFO("initial device response after %dms\n", rc); + + /* ack fw init done interrupt */ + ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE); + + /* DMA the ucode into the device */ + rc = ipw_load_ucode(priv, ucode_img, le32_to_cpu(fw->ucode_size)); + if (rc < 0) { + IPW_ERROR("Unable to load ucode: %d\n", rc); + goto error; + } + + /* stop nic */ + ipw_stop_nic(priv); + + /* DMA bss firmware into the device */ + rc = ipw_load_firmware(priv, fw_img, le32_to_cpu(fw->fw_size)); + if (rc < 0) { + IPW_ERROR("Unable to load firmware: %d\n", rc); + goto error; + } +#ifdef CONFIG_PM + fw_loaded = 1; +#endif + + ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0); + + rc = ipw_queue_reset(priv); + if (rc < 0) { + IPW_ERROR("Unable to initialize queues\n"); + goto error; + } + + /* Ensure interrupts are disabled */ + ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL); + /* ack pending interrupts */ + ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL); + + /* kick start the device */ + ipw_start_nic(priv); + + if (ipw_read32(priv, IPW_INTA_RW) & IPW_INTA_BIT_PARITY_ERROR) { + if (retries > 0) { + IPW_WARNING("Parity error. Retrying init.\n"); + retries--; + goto retry; + } + + IPW_ERROR("TODO: Handle parity error -- schedule restart?\n"); + rc = -EIO; + goto error; + } + + /* wait for the device */ + rc = ipw_poll_bit(priv, IPW_INTA_RW, + IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500); + if (rc < 0) { + IPW_ERROR("device failed to start within 500ms\n"); + goto error; + } + IPW_DEBUG_INFO("device response after %dms\n", rc); + + /* ack fw init done interrupt */ + ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE); + + /* read eeprom data */ + priv->eeprom_delay = 1; + ipw_read_eeprom(priv); + /* initialize the eeprom region of sram */ + ipw_eeprom_init_sram(priv); + + /* enable interrupts */ + ipw_enable_interrupts(priv); + + /* Ensure our queue has valid packets */ + ipw_rx_queue_replenish(priv); + + ipw_write32(priv, IPW_RX_READ_INDEX, priv->rxq->read); + + /* ack pending interrupts */ + ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL); + +#ifndef CONFIG_PM + release_firmware(raw); +#endif + return 0; + + error: + if (priv->rxq) { + ipw_rx_queue_free(priv, priv->rxq); + priv->rxq = NULL; + } + ipw_tx_queue_free(priv); + release_firmware(raw); +#ifdef CONFIG_PM + fw_loaded = 0; + raw = NULL; +#endif + + return rc; +} + +/** + * DMA services + * + * Theory of operation + * + * A queue is a circular buffers with 'Read' and 'Write' pointers. + * 2 empty entries always kept in the buffer to protect from overflow. + * + * For Tx queue, there are low mark and high mark limits. If, after queuing + * the packet for Tx, free space become < low mark, Tx queue stopped. When + * reclaiming packets (on 'tx done IRQ), if free space become > high mark, + * Tx queue resumed. + * + * The IPW operates with six queues, one receive queue in the device's + * sram, one transmit queue for sending commands to the device firmware, + * and four transmit queues for data. + * + * The four transmit queues allow for performing quality of service (qos) + * transmissions as per the 802.11 protocol. Currently Linux does not + * provide a mechanism to the user for utilizing prioritized queues, so + * we only utilize the first data transmit queue (queue1). + */ + +/** + * Driver allocates buffers of this size for Rx + */ + +/** + * ipw_rx_queue_space - Return number of free slots available in queue. + */ +static int ipw_rx_queue_space(const struct ipw_rx_queue *q) +{ + int s = q->read - q->write; + if (s <= 0) + s += RX_QUEUE_SIZE; + /* keep some buffer to not confuse full and empty queue */ + s -= 2; + if (s < 0) + s = 0; + return s; +} + +static inline int ipw_tx_queue_space(const struct clx2_queue *q) +{ + int s = q->last_used - q->first_empty; + if (s <= 0) + s += q->n_bd; + s -= 2; /* keep some reserve to not confuse empty and full situations */ + if (s < 0) + s = 0; + return s; +} + +static inline int ipw_queue_inc_wrap(int index, int n_bd) +{ + return (++index == n_bd) ? 0 : index; +} + +/** + * Initialize common DMA queue structure + * + * @param q queue to init + * @param count Number of BD's to allocate. Should be power of 2 + * @param read_register Address for 'read' register + * (not offset within BAR, full address) + * @param write_register Address for 'write' register + * (not offset within BAR, full address) + * @param base_register Address for 'base' register + * (not offset within BAR, full address) + * @param size Address for 'size' register + * (not offset within BAR, full address) + */ +static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q, + int count, u32 read, u32 write, u32 base, u32 size) +{ + q->n_bd = count; + + q->low_mark = q->n_bd / 4; + if (q->low_mark < 4) + q->low_mark = 4; + + q->high_mark = q->n_bd / 8; + if (q->high_mark < 2) + q->high_mark = 2; + + q->first_empty = q->last_used = 0; + q->reg_r = read; + q->reg_w = write; + + ipw_write32(priv, base, q->dma_addr); + ipw_write32(priv, size, count); + ipw_write32(priv, read, 0); + ipw_write32(priv, write, 0); + + _ipw_read32(priv, 0x90); +} + +static int ipw_queue_tx_init(struct ipw_priv *priv, + struct clx2_tx_queue *q, + int count, u32 read, u32 write, u32 base, u32 size) +{ + struct pci_dev *dev = priv->pci_dev; + + q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL); + if (!q->txb) { + IPW_ERROR("vmalloc for auxiliary BD structures failed\n"); + return -ENOMEM; + } + + q->bd = + pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr); + if (!q->bd) { + IPW_ERROR("pci_alloc_consistent(%zd) failed\n", + sizeof(q->bd[0]) * count); + kfree(q->txb); + q->txb = NULL; + return -ENOMEM; + } + + ipw_queue_init(priv, &q->q, count, read, write, base, size); + return 0; +} + +/** + * Free one TFD, those at index [txq->q.last_used]. + * Do NOT advance any indexes + * + * @param dev + * @param txq + */ +static void ipw_queue_tx_free_tfd(struct ipw_priv *priv, + struct clx2_tx_queue *txq) +{ + struct tfd_frame *bd = &txq->bd[txq->q.last_used]; + struct pci_dev *dev = priv->pci_dev; + int i; + + /* classify bd */ + if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE) + /* nothing to cleanup after for host commands */ + return; + + /* sanity check */ + if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) { + IPW_ERROR("Too many chunks: %i\n", + le32_to_cpu(bd->u.data.num_chunks)); + /** @todo issue fatal error, it is quite serious situation */ + return; + } + + /* unmap chunks if any */ + for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) { + pci_unmap_single(dev, le32_to_cpu(bd->u.data.chunk_ptr[i]), + le16_to_cpu(bd->u.data.chunk_len[i]), + PCI_DMA_TODEVICE); + if (txq->txb[txq->q.last_used]) { + libipw_txb_free(txq->txb[txq->q.last_used]); + txq->txb[txq->q.last_used] = NULL; + } + } +} + +/** + * Deallocate DMA queue. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * + * @param dev + * @param q + */ +static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq) +{ + struct clx2_queue *q = &txq->q; + struct pci_dev *dev = priv->pci_dev; + + if (q->n_bd == 0) + return; + + /* first, empty all BD's */ + for (; q->first_empty != q->last_used; + q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) { + ipw_queue_tx_free_tfd(priv, txq); + } + + /* free buffers belonging to queue itself */ + pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd, + q->dma_addr); + kfree(txq->txb); + + /* 0 fill whole structure */ + memset(txq, 0, sizeof(*txq)); +} + +/** + * Destroy all DMA queues and structures + * + * @param priv + */ +static void ipw_tx_queue_free(struct ipw_priv *priv) +{ + /* Tx CMD queue */ + ipw_queue_tx_free(priv, &priv->txq_cmd); + + /* Tx queues */ + ipw_queue_tx_free(priv, &priv->txq[0]); + ipw_queue_tx_free(priv, &priv->txq[1]); + ipw_queue_tx_free(priv, &priv->txq[2]); + ipw_queue_tx_free(priv, &priv->txq[3]); +} + +static void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid) +{ + /* First 3 bytes are manufacturer */ + bssid[0] = priv->mac_addr[0]; + bssid[1] = priv->mac_addr[1]; + bssid[2] = priv->mac_addr[2]; + + /* Last bytes are random */ + get_random_bytes(&bssid[3], ETH_ALEN - 3); + + bssid[0] &= 0xfe; /* clear multicast bit */ + bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */ +} + +static u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid) +{ + struct ipw_station_entry entry; + int i; + + for (i = 0; i < priv->num_stations; i++) { + if (ether_addr_equal(priv->stations[i], bssid)) { + /* Another node is active in network */ + priv->missed_adhoc_beacons = 0; + if (!(priv->config & CFG_STATIC_CHANNEL)) + /* when other nodes drop out, we drop out */ + priv->config &= ~CFG_ADHOC_PERSIST; + + return i; + } + } + + if (i == MAX_STATIONS) + return IPW_INVALID_STATION; + + IPW_DEBUG_SCAN("Adding AdHoc station: %pM\n", bssid); + + entry.reserved = 0; + entry.support_mode = 0; + memcpy(entry.mac_addr, bssid, ETH_ALEN); + memcpy(priv->stations[i], bssid, ETH_ALEN); + ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry), + &entry, sizeof(entry)); + priv->num_stations++; + + return i; +} + +static u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid) +{ + int i; + + for (i = 0; i < priv->num_stations; i++) + if (ether_addr_equal(priv->stations[i], bssid)) + return i; + + return IPW_INVALID_STATION; +} + +static void ipw_send_disassociate(struct ipw_priv *priv, int quiet) +{ + int err; + + if (priv->status & STATUS_ASSOCIATING) { + IPW_DEBUG_ASSOC("Disassociating while associating.\n"); + schedule_work(&priv->disassociate); + return; + } + + if (!(priv->status & STATUS_ASSOCIATED)) { + IPW_DEBUG_ASSOC("Disassociating while not associated.\n"); + return; + } + + IPW_DEBUG_ASSOC("Disassocation attempt from %pM " + "on channel %d.\n", + priv->assoc_request.bssid, + priv->assoc_request.channel); + + priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED); + priv->status |= STATUS_DISASSOCIATING; + + if (quiet) + priv->assoc_request.assoc_type = HC_DISASSOC_QUIET; + else + priv->assoc_request.assoc_type = HC_DISASSOCIATE; + + err = ipw_send_associate(priv, &priv->assoc_request); + if (err) { + IPW_DEBUG_HC("Attempt to send [dis]associate command " + "failed.\n"); + return; + } + +} + +static int ipw_disassociate(void *data) +{ + struct ipw_priv *priv = data; + if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) + return 0; + ipw_send_disassociate(data, 0); + netif_carrier_off(priv->net_dev); + return 1; +} + +static void ipw_bg_disassociate(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, disassociate); + mutex_lock(&priv->mutex); + ipw_disassociate(priv); + mutex_unlock(&priv->mutex); +} + +static void ipw_system_config(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, system_config); + +#ifdef CONFIG_IPW2200_PROMISCUOUS + if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) { + priv->sys_config.accept_all_data_frames = 1; + priv->sys_config.accept_non_directed_frames = 1; + priv->sys_config.accept_all_mgmt_bcpr = 1; + priv->sys_config.accept_all_mgmt_frames = 1; + } +#endif + + ipw_send_system_config(priv); +} + +struct ipw_status_code { + u16 status; + const char *reason; +}; + +static const struct ipw_status_code ipw_status_codes[] = { + {0x00, "Successful"}, + {0x01, "Unspecified failure"}, + {0x0A, "Cannot support all requested capabilities in the " + "Capability information field"}, + {0x0B, "Reassociation denied due to inability to confirm that " + "association exists"}, + {0x0C, "Association denied due to reason outside the scope of this " + "standard"}, + {0x0D, + "Responding station does not support the specified authentication " + "algorithm"}, + {0x0E, + "Received an Authentication frame with authentication sequence " + "transaction sequence number out of expected sequence"}, + {0x0F, "Authentication rejected because of challenge failure"}, + {0x10, "Authentication rejected due to timeout waiting for next " + "frame in sequence"}, + {0x11, "Association denied because AP is unable to handle additional " + "associated stations"}, + {0x12, + "Association denied due to requesting station not supporting all " + "of the datarates in the BSSBasicServiceSet Parameter"}, + {0x13, + "Association denied due to requesting station not supporting " + "short preamble operation"}, + {0x14, + "Association denied due to requesting station not supporting " + "PBCC encoding"}, + {0x15, + "Association denied due to requesting station not supporting " + "channel agility"}, + {0x19, + "Association denied due to requesting station not supporting " + "short slot operation"}, + {0x1A, + "Association denied due to requesting station not supporting " + "DSSS-OFDM operation"}, + {0x28, "Invalid Information Element"}, + {0x29, "Group Cipher is not valid"}, + {0x2A, "Pairwise Cipher is not valid"}, + {0x2B, "AKMP is not valid"}, + {0x2C, "Unsupported RSN IE version"}, + {0x2D, "Invalid RSN IE Capabilities"}, + {0x2E, "Cipher suite is rejected per security policy"}, +}; + +static const char *ipw_get_status_code(u16 status) +{ + int i; + for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++) + if (ipw_status_codes[i].status == (status & 0xff)) + return ipw_status_codes[i].reason; + return "Unknown status value."; +} + +static void inline average_init(struct average *avg) +{ + memset(avg, 0, sizeof(*avg)); +} + +#define DEPTH_RSSI 8 +#define DEPTH_NOISE 16 +static s16 exponential_average(s16 prev_avg, s16 val, u8 depth) +{ + return ((depth-1)*prev_avg + val)/depth; +} + +static void average_add(struct average *avg, s16 val) +{ + avg->sum -= avg->entries[avg->pos]; + avg->sum += val; + avg->entries[avg->pos++] = val; + if (unlikely(avg->pos == AVG_ENTRIES)) { + avg->init = 1; + avg->pos = 0; + } +} + +static s16 average_value(struct average *avg) +{ + if (!unlikely(avg->init)) { + if (avg->pos) + return avg->sum / avg->pos; + return 0; + } + + return avg->sum / AVG_ENTRIES; +} + +static void ipw_reset_stats(struct ipw_priv *priv) +{ + u32 len = sizeof(u32); + + priv->quality = 0; + + average_init(&priv->average_missed_beacons); + priv->exp_avg_rssi = -60; + priv->exp_avg_noise = -85 + 0x100; + + priv->last_rate = 0; + priv->last_missed_beacons = 0; + priv->last_rx_packets = 0; + priv->last_tx_packets = 0; + priv->last_tx_failures = 0; + + /* Firmware managed, reset only when NIC is restarted, so we have to + * normalize on the current value */ + ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, + &priv->last_rx_err, &len); + ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, + &priv->last_tx_failures, &len); + + /* Driver managed, reset with each association */ + priv->missed_adhoc_beacons = 0; + priv->missed_beacons = 0; + priv->tx_packets = 0; + priv->rx_packets = 0; + +} + +static u32 ipw_get_max_rate(struct ipw_priv *priv) +{ + u32 i = 0x80000000; + u32 mask = priv->rates_mask; + /* If currently associated in B mode, restrict the maximum + * rate match to B rates */ + if (priv->assoc_request.ieee_mode == IPW_B_MODE) + mask &= LIBIPW_CCK_RATES_MASK; + + /* TODO: Verify that the rate is supported by the current rates + * list. */ + + while (i && !(mask & i)) + i >>= 1; + switch (i) { + case LIBIPW_CCK_RATE_1MB_MASK: + return 1000000; + case LIBIPW_CCK_RATE_2MB_MASK: + return 2000000; + case LIBIPW_CCK_RATE_5MB_MASK: + return 5500000; + case LIBIPW_OFDM_RATE_6MB_MASK: + return 6000000; + case LIBIPW_OFDM_RATE_9MB_MASK: + return 9000000; + case LIBIPW_CCK_RATE_11MB_MASK: + return 11000000; + case LIBIPW_OFDM_RATE_12MB_MASK: + return 12000000; + case LIBIPW_OFDM_RATE_18MB_MASK: + return 18000000; + case LIBIPW_OFDM_RATE_24MB_MASK: + return 24000000; + case LIBIPW_OFDM_RATE_36MB_MASK: + return 36000000; + case LIBIPW_OFDM_RATE_48MB_MASK: + return 48000000; + case LIBIPW_OFDM_RATE_54MB_MASK: + return 54000000; + } + + if (priv->ieee->mode == IEEE_B) + return 11000000; + else + return 54000000; +} + +static u32 ipw_get_current_rate(struct ipw_priv *priv) +{ + u32 rate, len = sizeof(rate); + int err; + + if (!(priv->status & STATUS_ASSOCIATED)) + return 0; + + if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) { + err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate, + &len); + if (err) { + IPW_DEBUG_INFO("failed querying ordinals.\n"); + return 0; + } + } else + return ipw_get_max_rate(priv); + + switch (rate) { + case IPW_TX_RATE_1MB: + return 1000000; + case IPW_TX_RATE_2MB: + return 2000000; + case IPW_TX_RATE_5MB: + return 5500000; + case IPW_TX_RATE_6MB: + return 6000000; + case IPW_TX_RATE_9MB: + return 9000000; + case IPW_TX_RATE_11MB: + return 11000000; + case IPW_TX_RATE_12MB: + return 12000000; + case IPW_TX_RATE_18MB: + return 18000000; + case IPW_TX_RATE_24MB: + return 24000000; + case IPW_TX_RATE_36MB: + return 36000000; + case IPW_TX_RATE_48MB: + return 48000000; + case IPW_TX_RATE_54MB: + return 54000000; + } + + return 0; +} + +#define IPW_STATS_INTERVAL (2 * HZ) +static void ipw_gather_stats(struct ipw_priv *priv) +{ + u32 rx_err, rx_err_delta, rx_packets_delta; + u32 tx_failures, tx_failures_delta, tx_packets_delta; + u32 missed_beacons_percent, missed_beacons_delta; + u32 quality = 0; + u32 len = sizeof(u32); + s16 rssi; + u32 beacon_quality, signal_quality, tx_quality, rx_quality, + rate_quality; + u32 max_rate; + + if (!(priv->status & STATUS_ASSOCIATED)) { + priv->quality = 0; + return; + } + + /* Update the statistics */ + ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS, + &priv->missed_beacons, &len); + missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons; + priv->last_missed_beacons = priv->missed_beacons; + if (priv->assoc_request.beacon_interval) { + missed_beacons_percent = missed_beacons_delta * + (HZ * le16_to_cpu(priv->assoc_request.beacon_interval)) / + (IPW_STATS_INTERVAL * 10); + } else { + missed_beacons_percent = 0; + } + average_add(&priv->average_missed_beacons, missed_beacons_percent); + + ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len); + rx_err_delta = rx_err - priv->last_rx_err; + priv->last_rx_err = rx_err; + + ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len); + tx_failures_delta = tx_failures - priv->last_tx_failures; + priv->last_tx_failures = tx_failures; + + rx_packets_delta = priv->rx_packets - priv->last_rx_packets; + priv->last_rx_packets = priv->rx_packets; + + tx_packets_delta = priv->tx_packets - priv->last_tx_packets; + priv->last_tx_packets = priv->tx_packets; + + /* Calculate quality based on the following: + * + * Missed beacon: 100% = 0, 0% = 70% missed + * Rate: 60% = 1Mbs, 100% = Max + * Rx and Tx errors represent a straight % of total Rx/Tx + * RSSI: 100% = > -50, 0% = < -80 + * Rx errors: 100% = 0, 0% = 50% missed + * + * The lowest computed quality is used. + * + */ +#define BEACON_THRESHOLD 5 + beacon_quality = 100 - missed_beacons_percent; + if (beacon_quality < BEACON_THRESHOLD) + beacon_quality = 0; + else + beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 / + (100 - BEACON_THRESHOLD); + IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n", + beacon_quality, missed_beacons_percent); + + priv->last_rate = ipw_get_current_rate(priv); + max_rate = ipw_get_max_rate(priv); + rate_quality = priv->last_rate * 40 / max_rate + 60; + IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n", + rate_quality, priv->last_rate / 1000000); + + if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta) + rx_quality = 100 - (rx_err_delta * 100) / + (rx_packets_delta + rx_err_delta); + else + rx_quality = 100; + IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n", + rx_quality, rx_err_delta, rx_packets_delta); + + if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta) + tx_quality = 100 - (tx_failures_delta * 100) / + (tx_packets_delta + tx_failures_delta); + else + tx_quality = 100; + IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n", + tx_quality, tx_failures_delta, tx_packets_delta); + + rssi = priv->exp_avg_rssi; + signal_quality = + (100 * + (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) * + (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) - + (priv->ieee->perfect_rssi - rssi) * + (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) + + 62 * (priv->ieee->perfect_rssi - rssi))) / + ((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) * + (priv->ieee->perfect_rssi - priv->ieee->worst_rssi)); + if (signal_quality > 100) + signal_quality = 100; + else if (signal_quality < 1) + signal_quality = 0; + + IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n", + signal_quality, rssi); + + quality = min(rx_quality, signal_quality); + quality = min(tx_quality, quality); + quality = min(rate_quality, quality); + quality = min(beacon_quality, quality); + if (quality == beacon_quality) + IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n", + quality); + if (quality == rate_quality) + IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n", + quality); + if (quality == tx_quality) + IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n", + quality); + if (quality == rx_quality) + IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n", + quality); + if (quality == signal_quality) + IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n", + quality); + + priv->quality = quality; + + schedule_delayed_work(&priv->gather_stats, IPW_STATS_INTERVAL); +} + +static void ipw_bg_gather_stats(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, gather_stats.work); + mutex_lock(&priv->mutex); + ipw_gather_stats(priv); + mutex_unlock(&priv->mutex); +} + +/* Missed beacon behavior: + * 1st missed -> roaming_threshold, just wait, don't do any scan/roam. + * roaming_threshold -> disassociate_threshold, scan and roam for better signal. + * Above disassociate threshold, give up and stop scanning. + * Roaming is disabled if disassociate_threshold <= roaming_threshold */ +static void ipw_handle_missed_beacon(struct ipw_priv *priv, + int missed_count) +{ + priv->notif_missed_beacons = missed_count; + + if (missed_count > priv->disassociate_threshold && + priv->status & STATUS_ASSOCIATED) { + /* If associated and we've hit the missed + * beacon threshold, disassociate, turn + * off roaming, and abort any active scans */ + IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | + IPW_DL_STATE | IPW_DL_ASSOC, + "Missed beacon: %d - disassociate\n", missed_count); + priv->status &= ~STATUS_ROAMING; + if (priv->status & STATUS_SCANNING) { + IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | + IPW_DL_STATE, + "Aborting scan with missed beacon.\n"); + schedule_work(&priv->abort_scan); + } + + schedule_work(&priv->disassociate); + return; + } + + if (priv->status & STATUS_ROAMING) { + /* If we are currently roaming, then just + * print a debug statement... */ + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, + "Missed beacon: %d - roam in progress\n", + missed_count); + return; + } + + if (roaming && + (missed_count > priv->roaming_threshold && + missed_count <= priv->disassociate_threshold)) { + /* If we are not already roaming, set the ROAM + * bit in the status and kick off a scan. + * This can happen several times before we reach + * disassociate_threshold. */ + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, + "Missed beacon: %d - initiate " + "roaming\n", missed_count); + if (!(priv->status & STATUS_ROAMING)) { + priv->status |= STATUS_ROAMING; + if (!(priv->status & STATUS_SCANNING)) + schedule_delayed_work(&priv->request_scan, 0); + } + return; + } + + if (priv->status & STATUS_SCANNING && + missed_count > IPW_MB_SCAN_CANCEL_THRESHOLD) { + /* Stop scan to keep fw from getting + * stuck (only if we aren't roaming -- + * otherwise we'll never scan more than 2 or 3 + * channels..) */ + IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE, + "Aborting scan with missed beacon.\n"); + schedule_work(&priv->abort_scan); + } + + IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count); +} + +static void ipw_scan_event(struct work_struct *work) +{ + union iwreq_data wrqu; + + struct ipw_priv *priv = + container_of(work, struct ipw_priv, scan_event.work); + + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL); +} + +static void handle_scan_event(struct ipw_priv *priv) +{ + /* Only userspace-requested scan completion events go out immediately */ + if (!priv->user_requested_scan) { + schedule_delayed_work(&priv->scan_event, + round_jiffies_relative(msecs_to_jiffies(4000))); + } else { + priv->user_requested_scan = 0; + mod_delayed_work(system_wq, &priv->scan_event, 0); + } +} + +/** + * Handle host notification packet. + * Called from interrupt routine + */ +static void ipw_rx_notification(struct ipw_priv *priv, + struct ipw_rx_notification *notif) +{ + u16 size = le16_to_cpu(notif->size); + + IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, size); + + switch (notif->subtype) { + case HOST_NOTIFICATION_STATUS_ASSOCIATED:{ + struct notif_association *assoc = ¬if->u.assoc; + + switch (assoc->state) { + case CMAS_ASSOCIATED:{ + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, + "associated: '%*pE' %pM\n", + priv->essid_len, priv->essid, + priv->bssid); + + switch (priv->ieee->iw_mode) { + case IW_MODE_INFRA: + memcpy(priv->ieee->bssid, + priv->bssid, ETH_ALEN); + break; + + case IW_MODE_ADHOC: + memcpy(priv->ieee->bssid, + priv->bssid, ETH_ALEN); + + /* clear out the station table */ + priv->num_stations = 0; + + IPW_DEBUG_ASSOC + ("queueing adhoc check\n"); + schedule_delayed_work( + &priv->adhoc_check, + le16_to_cpu(priv-> + assoc_request. + beacon_interval)); + break; + } + + priv->status &= ~STATUS_ASSOCIATING; + priv->status |= STATUS_ASSOCIATED; + schedule_work(&priv->system_config); + +#ifdef CONFIG_IPW2200_QOS +#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \ + le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_control)) + if ((priv->status & STATUS_AUTH) && + (IPW_GET_PACKET_STYPE(¬if->u.raw) + == IEEE80211_STYPE_ASSOC_RESP)) { + if ((sizeof + (struct + libipw_assoc_response) + <= size) + && (size <= 2314)) { + struct + libipw_rx_stats + stats = { + .len = size - 1, + }; + + IPW_DEBUG_QOS + ("QoS Associate " + "size %d\n", size); + libipw_rx_mgt(priv-> + ieee, + (struct + libipw_hdr_4addr + *) + ¬if->u.raw, &stats); + } + } +#endif + + schedule_work(&priv->link_up); + + break; + } + + case CMAS_AUTHENTICATED:{ + if (priv-> + status & (STATUS_ASSOCIATED | + STATUS_AUTH)) { + struct notif_authenticate *auth + = ¬if->u.auth; + IPW_DEBUG(IPW_DL_NOTIF | + IPW_DL_STATE | + IPW_DL_ASSOC, + "deauthenticated: '%*pE' %pM: (0x%04X) - %s\n", + priv->essid_len, + priv->essid, + priv->bssid, + le16_to_cpu(auth->status), + ipw_get_status_code + (le16_to_cpu + (auth->status))); + + priv->status &= + ~(STATUS_ASSOCIATING | + STATUS_AUTH | + STATUS_ASSOCIATED); + + schedule_work(&priv->link_down); + break; + } + + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, + "authenticated: '%*pE' %pM\n", + priv->essid_len, priv->essid, + priv->bssid); + break; + } + + case CMAS_INIT:{ + if (priv->status & STATUS_AUTH) { + struct + libipw_assoc_response + *resp; + resp = + (struct + libipw_assoc_response + *)¬if->u.raw; + IPW_DEBUG(IPW_DL_NOTIF | + IPW_DL_STATE | + IPW_DL_ASSOC, + "association failed (0x%04X): %s\n", + le16_to_cpu(resp->status), + ipw_get_status_code + (le16_to_cpu + (resp->status))); + } + + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, + "disassociated: '%*pE' %pM\n", + priv->essid_len, priv->essid, + priv->bssid); + + priv->status &= + ~(STATUS_DISASSOCIATING | + STATUS_ASSOCIATING | + STATUS_ASSOCIATED | STATUS_AUTH); + if (priv->assoc_network + && (priv->assoc_network-> + capability & + WLAN_CAPABILITY_IBSS)) + ipw_remove_current_network + (priv); + + schedule_work(&priv->link_down); + + break; + } + + case CMAS_RX_ASSOC_RESP: + break; + + default: + IPW_ERROR("assoc: unknown (%d)\n", + assoc->state); + break; + } + + break; + } + + case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{ + struct notif_authenticate *auth = ¬if->u.auth; + switch (auth->state) { + case CMAS_AUTHENTICATED: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, + "authenticated: '%*pE' %pM\n", + priv->essid_len, priv->essid, + priv->bssid); + priv->status |= STATUS_AUTH; + break; + + case CMAS_INIT: + if (priv->status & STATUS_AUTH) { + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, + "authentication failed (0x%04X): %s\n", + le16_to_cpu(auth->status), + ipw_get_status_code(le16_to_cpu + (auth-> + status))); + } + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, + "deauthenticated: '%*pE' %pM\n", + priv->essid_len, priv->essid, + priv->bssid); + + priv->status &= ~(STATUS_ASSOCIATING | + STATUS_AUTH | + STATUS_ASSOCIATED); + + schedule_work(&priv->link_down); + break; + + case CMAS_TX_AUTH_SEQ_1: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, "AUTH_SEQ_1\n"); + break; + case CMAS_RX_AUTH_SEQ_2: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, "AUTH_SEQ_2\n"); + break; + case CMAS_AUTH_SEQ_1_PASS: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n"); + break; + case CMAS_AUTH_SEQ_1_FAIL: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n"); + break; + case CMAS_TX_AUTH_SEQ_3: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, "AUTH_SEQ_3\n"); + break; + case CMAS_RX_AUTH_SEQ_4: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n"); + break; + case CMAS_AUTH_SEQ_2_PASS: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n"); + break; + case CMAS_AUTH_SEQ_2_FAIL: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n"); + break; + case CMAS_TX_ASSOC: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, "TX_ASSOC\n"); + break; + case CMAS_RX_ASSOC_RESP: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, "RX_ASSOC_RESP\n"); + + break; + case CMAS_ASSOCIATED: + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | + IPW_DL_ASSOC, "ASSOCIATED\n"); + break; + default: + IPW_DEBUG_NOTIF("auth: failure - %d\n", + auth->state); + break; + } + break; + } + + case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{ + struct notif_channel_result *x = + ¬if->u.channel_result; + + if (size == sizeof(*x)) { + IPW_DEBUG_SCAN("Scan result for channel %d\n", + x->channel_num); + } else { + IPW_DEBUG_SCAN("Scan result of wrong size %d " + "(should be %zd)\n", + size, sizeof(*x)); + } + break; + } + + case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{ + struct notif_scan_complete *x = ¬if->u.scan_complete; + if (size == sizeof(*x)) { + IPW_DEBUG_SCAN + ("Scan completed: type %d, %d channels, " + "%d status\n", x->scan_type, + x->num_channels, x->status); + } else { + IPW_ERROR("Scan completed of wrong size %d " + "(should be %zd)\n", + size, sizeof(*x)); + } + + priv->status &= + ~(STATUS_SCANNING | STATUS_SCAN_ABORTING); + + wake_up_interruptible(&priv->wait_state); + cancel_delayed_work(&priv->scan_check); + + if (priv->status & STATUS_EXIT_PENDING) + break; + + priv->ieee->scans++; + +#ifdef CONFIG_IPW2200_MONITOR + if (priv->ieee->iw_mode == IW_MODE_MONITOR) { + priv->status |= STATUS_SCAN_FORCED; + schedule_delayed_work(&priv->request_scan, 0); + break; + } + priv->status &= ~STATUS_SCAN_FORCED; +#endif /* CONFIG_IPW2200_MONITOR */ + + /* Do queued direct scans first */ + if (priv->status & STATUS_DIRECT_SCAN_PENDING) + schedule_delayed_work(&priv->request_direct_scan, 0); + + if (!(priv->status & (STATUS_ASSOCIATED | + STATUS_ASSOCIATING | + STATUS_ROAMING | + STATUS_DISASSOCIATING))) + schedule_work(&priv->associate); + else if (priv->status & STATUS_ROAMING) { + if (x->status == SCAN_COMPLETED_STATUS_COMPLETE) + /* If a scan completed and we are in roam mode, then + * the scan that completed was the one requested as a + * result of entering roam... so, schedule the + * roam work */ + schedule_work(&priv->roam); + else + /* Don't schedule if we aborted the scan */ + priv->status &= ~STATUS_ROAMING; + } else if (priv->status & STATUS_SCAN_PENDING) + schedule_delayed_work(&priv->request_scan, 0); + else if (priv->config & CFG_BACKGROUND_SCAN + && priv->status & STATUS_ASSOCIATED) + schedule_delayed_work(&priv->request_scan, + round_jiffies_relative(HZ)); + + /* Send an empty event to user space. + * We don't send the received data on the event because + * it would require us to do complex transcoding, and + * we want to minimise the work done in the irq handler + * Use a request to extract the data. + * Also, we generate this even for any scan, regardless + * on how the scan was initiated. User space can just + * sync on periodic scan to get fresh data... + * Jean II */ + if (x->status == SCAN_COMPLETED_STATUS_COMPLETE) + handle_scan_event(priv); + break; + } + + case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{ + struct notif_frag_length *x = ¬if->u.frag_len; + + if (size == sizeof(*x)) + IPW_ERROR("Frag length: %d\n", + le16_to_cpu(x->frag_length)); + else + IPW_ERROR("Frag length of wrong size %d " + "(should be %zd)\n", + size, sizeof(*x)); + break; + } + + case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{ + struct notif_link_deterioration *x = + ¬if->u.link_deterioration; + + if (size == sizeof(*x)) { + IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, + "link deterioration: type %d, cnt %d\n", + x->silence_notification_type, + x->silence_count); + memcpy(&priv->last_link_deterioration, x, + sizeof(*x)); + } else { + IPW_ERROR("Link Deterioration of wrong size %d " + "(should be %zd)\n", + size, sizeof(*x)); + } + break; + } + + case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{ + IPW_ERROR("Dino config\n"); + if (priv->hcmd + && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG) + IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n"); + + break; + } + + case HOST_NOTIFICATION_STATUS_BEACON_STATE:{ + struct notif_beacon_state *x = ¬if->u.beacon_state; + if (size != sizeof(*x)) { + IPW_ERROR + ("Beacon state of wrong size %d (should " + "be %zd)\n", size, sizeof(*x)); + break; + } + + if (le32_to_cpu(x->state) == + HOST_NOTIFICATION_STATUS_BEACON_MISSING) + ipw_handle_missed_beacon(priv, + le32_to_cpu(x-> + number)); + + break; + } + + case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{ + struct notif_tgi_tx_key *x = ¬if->u.tgi_tx_key; + if (size == sizeof(*x)) { + IPW_ERROR("TGi Tx Key: state 0x%02x sec type " + "0x%02x station %d\n", + x->key_state, x->security_type, + x->station_index); + break; + } + + IPW_ERROR + ("TGi Tx Key of wrong size %d (should be %zd)\n", + size, sizeof(*x)); + break; + } + + case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{ + struct notif_calibration *x = ¬if->u.calibration; + + if (size == sizeof(*x)) { + memcpy(&priv->calib, x, sizeof(*x)); + IPW_DEBUG_INFO("TODO: Calibration\n"); + break; + } + + IPW_ERROR + ("Calibration of wrong size %d (should be %zd)\n", + size, sizeof(*x)); + break; + } + + case HOST_NOTIFICATION_NOISE_STATS:{ + if (size == sizeof(u32)) { + priv->exp_avg_noise = + exponential_average(priv->exp_avg_noise, + (u8) (le32_to_cpu(notif->u.noise.value) & 0xff), + DEPTH_NOISE); + break; + } + + IPW_ERROR + ("Noise stat is wrong size %d (should be %zd)\n", + size, sizeof(u32)); + break; + } + + default: + IPW_DEBUG_NOTIF("Unknown notification: " + "subtype=%d,flags=0x%2x,size=%d\n", + notif->subtype, notif->flags, size); + } +} + +/** + * Destroys all DMA structures and initialise them again + * + * @param priv + * @return error code + */ +static int ipw_queue_reset(struct ipw_priv *priv) +{ + int rc = 0; + /** @todo customize queue sizes */ + int nTx = 64, nTxCmd = 8; + ipw_tx_queue_free(priv); + /* Tx CMD queue */ + rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd, + IPW_TX_CMD_QUEUE_READ_INDEX, + IPW_TX_CMD_QUEUE_WRITE_INDEX, + IPW_TX_CMD_QUEUE_BD_BASE, + IPW_TX_CMD_QUEUE_BD_SIZE); + if (rc) { + IPW_ERROR("Tx Cmd queue init failed\n"); + goto error; + } + /* Tx queue(s) */ + rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx, + IPW_TX_QUEUE_0_READ_INDEX, + IPW_TX_QUEUE_0_WRITE_INDEX, + IPW_TX_QUEUE_0_BD_BASE, IPW_TX_QUEUE_0_BD_SIZE); + if (rc) { + IPW_ERROR("Tx 0 queue init failed\n"); + goto error; + } + rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx, + IPW_TX_QUEUE_1_READ_INDEX, + IPW_TX_QUEUE_1_WRITE_INDEX, + IPW_TX_QUEUE_1_BD_BASE, IPW_TX_QUEUE_1_BD_SIZE); + if (rc) { + IPW_ERROR("Tx 1 queue init failed\n"); + goto error; + } + rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx, + IPW_TX_QUEUE_2_READ_INDEX, + IPW_TX_QUEUE_2_WRITE_INDEX, + IPW_TX_QUEUE_2_BD_BASE, IPW_TX_QUEUE_2_BD_SIZE); + if (rc) { + IPW_ERROR("Tx 2 queue init failed\n"); + goto error; + } + rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx, + IPW_TX_QUEUE_3_READ_INDEX, + IPW_TX_QUEUE_3_WRITE_INDEX, + IPW_TX_QUEUE_3_BD_BASE, IPW_TX_QUEUE_3_BD_SIZE); + if (rc) { + IPW_ERROR("Tx 3 queue init failed\n"); + goto error; + } + /* statistics */ + priv->rx_bufs_min = 0; + priv->rx_pend_max = 0; + return rc; + + error: + ipw_tx_queue_free(priv); + return rc; +} + +/** + * Reclaim Tx queue entries no more used by NIC. + * + * When FW advances 'R' index, all entries between old and + * new 'R' index need to be reclaimed. As result, some free space + * forms. If there is enough free space (> low mark), wake Tx queue. + * + * @note Need to protect against garbage in 'R' index + * @param priv + * @param txq + * @param qindex + * @return Number of used entries remains in the queue + */ +static int ipw_queue_tx_reclaim(struct ipw_priv *priv, + struct clx2_tx_queue *txq, int qindex) +{ + u32 hw_tail; + int used; + struct clx2_queue *q = &txq->q; + + hw_tail = ipw_read32(priv, q->reg_r); + if (hw_tail >= q->n_bd) { + IPW_ERROR + ("Read index for DMA queue (%d) is out of range [0-%d)\n", + hw_tail, q->n_bd); + goto done; + } + for (; q->last_used != hw_tail; + q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) { + ipw_queue_tx_free_tfd(priv, txq); + priv->tx_packets++; + } + done: + if ((ipw_tx_queue_space(q) > q->low_mark) && + (qindex >= 0)) + netif_wake_queue(priv->net_dev); + used = q->first_empty - q->last_used; + if (used < 0) + used += q->n_bd; + + return used; +} + +static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf, + int len, int sync) +{ + struct clx2_tx_queue *txq = &priv->txq_cmd; + struct clx2_queue *q = &txq->q; + struct tfd_frame *tfd; + + if (ipw_tx_queue_space(q) < (sync ? 1 : 2)) { + IPW_ERROR("No space for Tx\n"); + return -EBUSY; + } + + tfd = &txq->bd[q->first_empty]; + txq->txb[q->first_empty] = NULL; + + memset(tfd, 0, sizeof(*tfd)); + tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE; + tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK; + priv->hcmd_seq++; + tfd->u.cmd.index = hcmd; + tfd->u.cmd.length = len; + memcpy(tfd->u.cmd.payload, buf, len); + q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd); + ipw_write32(priv, q->reg_w, q->first_empty); + _ipw_read32(priv, 0x90); + + return 0; +} + +/* + * Rx theory of operation + * + * The host allocates 32 DMA target addresses and passes the host address + * to the firmware at register IPW_RFDS_TABLE_LOWER + N * RFD_SIZE where N is + * 0 to 31 + * + * Rx Queue Indexes + * The host/firmware share two index registers for managing the Rx buffers. + * + * The READ index maps to the first position that the firmware may be writing + * to -- the driver can read up to (but not including) this position and get + * good data. + * The READ index is managed by the firmware once the card is enabled. + * + * The WRITE index maps to the last position the driver has read from -- the + * position preceding WRITE is the last slot the firmware can place a packet. + * + * The queue is empty (no good data) if WRITE = READ - 1, and is full if + * WRITE = READ. + * + * During initialization the host sets up the READ queue position to the first + * INDEX position, and WRITE to the last (READ - 1 wrapped) + * + * When the firmware places a packet in a buffer it will advance the READ index + * and fire the RX interrupt. The driver can then query the READ index and + * process as many packets as possible, moving the WRITE index forward as it + * resets the Rx queue buffers with new memory. + * + * The management in the driver is as follows: + * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When + * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled + * to replensish the ipw->rxq->rx_free. + * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the + * ipw->rxq is replenished and the READ INDEX is updated (updating the + * 'processed' and 'read' driver indexes as well) + * + A received packet is processed and handed to the kernel network stack, + * detached from the ipw->rxq. The driver 'processed' index is updated. + * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free + * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ + * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there + * were enough free buffers and RX_STALLED is set it is cleared. + * + * + * Driver sequence: + * + * ipw_rx_queue_alloc() Allocates rx_free + * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls + * ipw_rx_queue_restock + * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx + * queue, updates firmware pointers, and updates + * the WRITE index. If insufficient rx_free buffers + * are available, schedules ipw_rx_queue_replenish + * + * -- enable interrupts -- + * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the + * READ INDEX, detaching the SKB from the pool. + * Moves the packet buffer from queue to rx_used. + * Calls ipw_rx_queue_restock to refill any empty + * slots. + * ... + * + */ + +/* + * If there are slots in the RX queue that need to be restocked, + * and we have free pre-allocated buffers, fill the ranks as much + * as we can pulling from rx_free. + * + * This moves the 'write' index forward to catch up with 'processed', and + * also updates the memory address in the firmware to reference the new + * target buffer. + */ +static void ipw_rx_queue_restock(struct ipw_priv *priv) +{ + struct ipw_rx_queue *rxq = priv->rxq; + struct list_head *element; + struct ipw_rx_mem_buffer *rxb; + unsigned long flags; + int write; + + spin_lock_irqsave(&rxq->lock, flags); + write = rxq->write; + while ((ipw_rx_queue_space(rxq) > 0) && (rxq->free_count)) { + element = rxq->rx_free.next; + rxb = list_entry(element, struct ipw_rx_mem_buffer, list); + list_del(element); + + ipw_write32(priv, IPW_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE, + rxb->dma_addr); + rxq->queue[rxq->write] = rxb; + rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE; + rxq->free_count--; + } + spin_unlock_irqrestore(&rxq->lock, flags); + + /* If the pre-allocated buffer pool is dropping low, schedule to + * refill it */ + if (rxq->free_count <= RX_LOW_WATERMARK) + schedule_work(&priv->rx_replenish); + + /* If we've added more space for the firmware to place data, tell it */ + if (write != rxq->write) + ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write); +} + +/* + * Move all used packet from rx_used to rx_free, allocating a new SKB for each. + * Also restock the Rx queue via ipw_rx_queue_restock. + * + * This is called as a scheduled work item (except for during intialization) + */ +static void ipw_rx_queue_replenish(void *data) +{ + struct ipw_priv *priv = data; + struct ipw_rx_queue *rxq = priv->rxq; + struct list_head *element; + struct ipw_rx_mem_buffer *rxb; + unsigned long flags; + + spin_lock_irqsave(&rxq->lock, flags); + while (!list_empty(&rxq->rx_used)) { + element = rxq->rx_used.next; + rxb = list_entry(element, struct ipw_rx_mem_buffer, list); + rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC); + if (!rxb->skb) { + printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n", + priv->net_dev->name); + /* We don't reschedule replenish work here -- we will + * call the restock method and if it still needs + * more buffers it will schedule replenish */ + break; + } + list_del(element); + + rxb->dma_addr = + pci_map_single(priv->pci_dev, rxb->skb->data, + IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + } + spin_unlock_irqrestore(&rxq->lock, flags); + + ipw_rx_queue_restock(priv); +} + +static void ipw_bg_rx_queue_replenish(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, rx_replenish); + mutex_lock(&priv->mutex); + ipw_rx_queue_replenish(priv); + mutex_unlock(&priv->mutex); +} + +/* Assumes that the skb field of the buffers in 'pool' is kept accurate. + * If an SKB has been detached, the POOL needs to have its SKB set to NULL + * This free routine walks the list of POOL entries and if SKB is set to + * non NULL it is unmapped and freed + */ +static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq) +{ + int i; + + if (!rxq) + return; + + for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { + if (rxq->pool[i].skb != NULL) { + pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr, + IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + dev_kfree_skb(rxq->pool[i].skb); + } + } + + kfree(rxq); +} + +static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv) +{ + struct ipw_rx_queue *rxq; + int i; + + rxq = kzalloc(sizeof(*rxq), GFP_KERNEL); + if (unlikely(!rxq)) { + IPW_ERROR("memory allocation failed\n"); + return NULL; + } + spin_lock_init(&rxq->lock); + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + + /* Fill the rx_used queue with _all_ of the Rx buffers */ + for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) + list_add_tail(&rxq->pool[i].list, &rxq->rx_used); + + /* Set us so that we have processed and used all buffers, but have + * not restocked the Rx queue with fresh buffers */ + rxq->read = rxq->write = 0; + rxq->free_count = 0; + + return rxq; +} + +static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate) +{ + rate &= ~LIBIPW_BASIC_RATE_MASK; + if (ieee_mode == IEEE_A) { + switch (rate) { + case LIBIPW_OFDM_RATE_6MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ? + 1 : 0; + case LIBIPW_OFDM_RATE_9MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ? + 1 : 0; + case LIBIPW_OFDM_RATE_12MB: + return priv-> + rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_18MB: + return priv-> + rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_24MB: + return priv-> + rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_36MB: + return priv-> + rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_48MB: + return priv-> + rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_54MB: + return priv-> + rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0; + default: + return 0; + } + } + + /* B and G mixed */ + switch (rate) { + case LIBIPW_CCK_RATE_1MB: + return priv->rates_mask & LIBIPW_CCK_RATE_1MB_MASK ? 1 : 0; + case LIBIPW_CCK_RATE_2MB: + return priv->rates_mask & LIBIPW_CCK_RATE_2MB_MASK ? 1 : 0; + case LIBIPW_CCK_RATE_5MB: + return priv->rates_mask & LIBIPW_CCK_RATE_5MB_MASK ? 1 : 0; + case LIBIPW_CCK_RATE_11MB: + return priv->rates_mask & LIBIPW_CCK_RATE_11MB_MASK ? 1 : 0; + } + + /* If we are limited to B modulations, bail at this point */ + if (ieee_mode == IEEE_B) + return 0; + + /* G */ + switch (rate) { + case LIBIPW_OFDM_RATE_6MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_9MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_12MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_18MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_24MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_36MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_48MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_54MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0; + } + + return 0; +} + +static int ipw_compatible_rates(struct ipw_priv *priv, + const struct libipw_network *network, + struct ipw_supported_rates *rates) +{ + int num_rates, i; + + memset(rates, 0, sizeof(*rates)); + num_rates = min(network->rates_len, (u8) IPW_MAX_RATES); + rates->num_rates = 0; + for (i = 0; i < num_rates; i++) { + if (!ipw_is_rate_in_mask(priv, network->mode, + network->rates[i])) { + + if (network->rates[i] & LIBIPW_BASIC_RATE_MASK) { + IPW_DEBUG_SCAN("Adding masked mandatory " + "rate %02X\n", + network->rates[i]); + rates->supported_rates[rates->num_rates++] = + network->rates[i]; + continue; + } + + IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n", + network->rates[i], priv->rates_mask); + continue; + } + + rates->supported_rates[rates->num_rates++] = network->rates[i]; + } + + num_rates = min(network->rates_ex_len, + (u8) (IPW_MAX_RATES - num_rates)); + for (i = 0; i < num_rates; i++) { + if (!ipw_is_rate_in_mask(priv, network->mode, + network->rates_ex[i])) { + if (network->rates_ex[i] & LIBIPW_BASIC_RATE_MASK) { + IPW_DEBUG_SCAN("Adding masked mandatory " + "rate %02X\n", + network->rates_ex[i]); + rates->supported_rates[rates->num_rates++] = + network->rates[i]; + continue; + } + + IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n", + network->rates_ex[i], priv->rates_mask); + continue; + } + + rates->supported_rates[rates->num_rates++] = + network->rates_ex[i]; + } + + return 1; +} + +static void ipw_copy_rates(struct ipw_supported_rates *dest, + const struct ipw_supported_rates *src) +{ + u8 i; + for (i = 0; i < src->num_rates; i++) + dest->supported_rates[i] = src->supported_rates[i]; + dest->num_rates = src->num_rates; +} + +/* TODO: Look at sniffed packets in the air to determine if the basic rate + * mask should ever be used -- right now all callers to add the scan rates are + * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */ +static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates, + u8 modulation, u32 rate_mask) +{ + u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ? + LIBIPW_BASIC_RATE_MASK : 0; + + if (rate_mask & LIBIPW_CCK_RATE_1MB_MASK) + rates->supported_rates[rates->num_rates++] = + LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_1MB; + + if (rate_mask & LIBIPW_CCK_RATE_2MB_MASK) + rates->supported_rates[rates->num_rates++] = + LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_2MB; + + if (rate_mask & LIBIPW_CCK_RATE_5MB_MASK) + rates->supported_rates[rates->num_rates++] = basic_mask | + LIBIPW_CCK_RATE_5MB; + + if (rate_mask & LIBIPW_CCK_RATE_11MB_MASK) + rates->supported_rates[rates->num_rates++] = basic_mask | + LIBIPW_CCK_RATE_11MB; +} + +static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates, + u8 modulation, u32 rate_mask) +{ + u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ? + LIBIPW_BASIC_RATE_MASK : 0; + + if (rate_mask & LIBIPW_OFDM_RATE_6MB_MASK) + rates->supported_rates[rates->num_rates++] = basic_mask | + LIBIPW_OFDM_RATE_6MB; + + if (rate_mask & LIBIPW_OFDM_RATE_9MB_MASK) + rates->supported_rates[rates->num_rates++] = + LIBIPW_OFDM_RATE_9MB; + + if (rate_mask & LIBIPW_OFDM_RATE_12MB_MASK) + rates->supported_rates[rates->num_rates++] = basic_mask | + LIBIPW_OFDM_RATE_12MB; + + if (rate_mask & LIBIPW_OFDM_RATE_18MB_MASK) + rates->supported_rates[rates->num_rates++] = + LIBIPW_OFDM_RATE_18MB; + + if (rate_mask & LIBIPW_OFDM_RATE_24MB_MASK) + rates->supported_rates[rates->num_rates++] = basic_mask | + LIBIPW_OFDM_RATE_24MB; + + if (rate_mask & LIBIPW_OFDM_RATE_36MB_MASK) + rates->supported_rates[rates->num_rates++] = + LIBIPW_OFDM_RATE_36MB; + + if (rate_mask & LIBIPW_OFDM_RATE_48MB_MASK) + rates->supported_rates[rates->num_rates++] = + LIBIPW_OFDM_RATE_48MB; + + if (rate_mask & LIBIPW_OFDM_RATE_54MB_MASK) + rates->supported_rates[rates->num_rates++] = + LIBIPW_OFDM_RATE_54MB; +} + +struct ipw_network_match { + struct libipw_network *network; + struct ipw_supported_rates rates; +}; + +static int ipw_find_adhoc_network(struct ipw_priv *priv, + struct ipw_network_match *match, + struct libipw_network *network, + int roaming) +{ + struct ipw_supported_rates rates; + + /* Verify that this network's capability is compatible with the + * current mode (AdHoc or Infrastructure) */ + if ((priv->ieee->iw_mode == IW_MODE_ADHOC && + !(network->capability & WLAN_CAPABILITY_IBSS))) { + IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded due to capability mismatch.\n", + network->ssid_len, network->ssid, + network->bssid); + return 0; + } + + if (unlikely(roaming)) { + /* If we are roaming, then ensure check if this is a valid + * network to try and roam to */ + if ((network->ssid_len != match->network->ssid_len) || + memcmp(network->ssid, match->network->ssid, + network->ssid_len)) { + IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of non-network ESSID.\n", + network->ssid_len, network->ssid, + network->bssid); + return 0; + } + } else { + /* If an ESSID has been configured then compare the broadcast + * ESSID to ours */ + if ((priv->config & CFG_STATIC_ESSID) && + ((network->ssid_len != priv->essid_len) || + memcmp(network->ssid, priv->essid, + min(network->ssid_len, priv->essid_len)))) { + IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of ESSID mismatch: '%*pE'.\n", + network->ssid_len, network->ssid, + network->bssid, priv->essid_len, + priv->essid); + return 0; + } + } + + /* If the old network rate is better than this one, don't bother + * testing everything else. */ + + if (network->time_stamp[0] < match->network->time_stamp[0]) { + IPW_DEBUG_MERGE("Network '%*pE excluded because newer than current network.\n", + match->network->ssid_len, match->network->ssid); + return 0; + } else if (network->time_stamp[1] < match->network->time_stamp[1]) { + IPW_DEBUG_MERGE("Network '%*pE excluded because newer than current network.\n", + match->network->ssid_len, match->network->ssid); + return 0; + } + + /* Now go through and see if the requested network is valid... */ + if (priv->ieee->scan_age != 0 && + time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) { + IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of age: %ums.\n", + network->ssid_len, network->ssid, + network->bssid, + jiffies_to_msecs(jiffies - + network->last_scanned)); + return 0; + } + + if ((priv->config & CFG_STATIC_CHANNEL) && + (network->channel != priv->channel)) { + IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of channel mismatch: %d != %d.\n", + network->ssid_len, network->ssid, + network->bssid, + network->channel, priv->channel); + return 0; + } + + /* Verify privacy compatibility */ + if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) != + ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) { + IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of privacy mismatch: %s != %s.\n", + network->ssid_len, network->ssid, + network->bssid, + priv-> + capability & CAP_PRIVACY_ON ? "on" : "off", + network-> + capability & WLAN_CAPABILITY_PRIVACY ? "on" : + "off"); + return 0; + } + + if (ether_addr_equal(network->bssid, priv->bssid)) { + IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of the same BSSID match: %pM.\n", + network->ssid_len, network->ssid, + network->bssid, priv->bssid); + return 0; + } + + /* Filter out any incompatible freq / mode combinations */ + if (!libipw_is_valid_mode(priv->ieee, network->mode)) { + IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of invalid frequency/mode combination.\n", + network->ssid_len, network->ssid, + network->bssid); + return 0; + } + + /* Ensure that the rates supported by the driver are compatible with + * this AP, including verification of basic rates (mandatory) */ + if (!ipw_compatible_rates(priv, network, &rates)) { + IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because configured rate mask excludes AP mandatory rate.\n", + network->ssid_len, network->ssid, + network->bssid); + return 0; + } + + if (rates.num_rates == 0) { + IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of no compatible rates.\n", + network->ssid_len, network->ssid, + network->bssid); + return 0; + } + + /* TODO: Perform any further minimal comparititive tests. We do not + * want to put too much policy logic here; intelligent scan selection + * should occur within a generic IEEE 802.11 user space tool. */ + + /* Set up 'new' AP to this network */ + ipw_copy_rates(&match->rates, &rates); + match->network = network; + IPW_DEBUG_MERGE("Network '%*pE (%pM)' is a viable match.\n", + network->ssid_len, network->ssid, network->bssid); + + return 1; +} + +static void ipw_merge_adhoc_network(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, merge_networks); + struct libipw_network *network = NULL; + struct ipw_network_match match = { + .network = priv->assoc_network + }; + + if ((priv->status & STATUS_ASSOCIATED) && + (priv->ieee->iw_mode == IW_MODE_ADHOC)) { + /* First pass through ROAM process -- look for a better + * network */ + unsigned long flags; + + spin_lock_irqsave(&priv->ieee->lock, flags); + list_for_each_entry(network, &priv->ieee->network_list, list) { + if (network != priv->assoc_network) + ipw_find_adhoc_network(priv, &match, network, + 1); + } + spin_unlock_irqrestore(&priv->ieee->lock, flags); + + if (match.network == priv->assoc_network) { + IPW_DEBUG_MERGE("No better ADHOC in this network to " + "merge to.\n"); + return; + } + + mutex_lock(&priv->mutex); + if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) { + IPW_DEBUG_MERGE("remove network %*pE\n", + priv->essid_len, priv->essid); + ipw_remove_current_network(priv); + } + + ipw_disassociate(priv); + priv->assoc_network = match.network; + mutex_unlock(&priv->mutex); + return; + } +} + +static int ipw_best_network(struct ipw_priv *priv, + struct ipw_network_match *match, + struct libipw_network *network, int roaming) +{ + struct ipw_supported_rates rates; + + /* Verify that this network's capability is compatible with the + * current mode (AdHoc or Infrastructure) */ + if ((priv->ieee->iw_mode == IW_MODE_INFRA && + !(network->capability & WLAN_CAPABILITY_ESS)) || + (priv->ieee->iw_mode == IW_MODE_ADHOC && + !(network->capability & WLAN_CAPABILITY_IBSS))) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded due to capability mismatch.\n", + network->ssid_len, network->ssid, + network->bssid); + return 0; + } + + if (unlikely(roaming)) { + /* If we are roaming, then ensure check if this is a valid + * network to try and roam to */ + if ((network->ssid_len != match->network->ssid_len) || + memcmp(network->ssid, match->network->ssid, + network->ssid_len)) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of non-network ESSID.\n", + network->ssid_len, network->ssid, + network->bssid); + return 0; + } + } else { + /* If an ESSID has been configured then compare the broadcast + * ESSID to ours */ + if ((priv->config & CFG_STATIC_ESSID) && + ((network->ssid_len != priv->essid_len) || + memcmp(network->ssid, priv->essid, + min(network->ssid_len, priv->essid_len)))) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of ESSID mismatch: '%*pE'.\n", + network->ssid_len, network->ssid, + network->bssid, priv->essid_len, + priv->essid); + return 0; + } + } + + /* If the old network rate is better than this one, don't bother + * testing everything else. */ + if (match->network && match->network->stats.rssi > network->stats.rssi) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because '%*pE (%pM)' has a stronger signal.\n", + network->ssid_len, network->ssid, + network->bssid, match->network->ssid_len, + match->network->ssid, match->network->bssid); + return 0; + } + + /* If this network has already had an association attempt within the + * last 3 seconds, do not try and associate again... */ + if (network->last_associate && + time_after(network->last_associate + (HZ * 3UL), jiffies)) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of storming (%ums since last assoc attempt).\n", + network->ssid_len, network->ssid, + network->bssid, + jiffies_to_msecs(jiffies - + network->last_associate)); + return 0; + } + + /* Now go through and see if the requested network is valid... */ + if (priv->ieee->scan_age != 0 && + time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of age: %ums.\n", + network->ssid_len, network->ssid, + network->bssid, + jiffies_to_msecs(jiffies - + network->last_scanned)); + return 0; + } + + if ((priv->config & CFG_STATIC_CHANNEL) && + (network->channel != priv->channel)) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of channel mismatch: %d != %d.\n", + network->ssid_len, network->ssid, + network->bssid, + network->channel, priv->channel); + return 0; + } + + /* Verify privacy compatibility */ + if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) != + ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of privacy mismatch: %s != %s.\n", + network->ssid_len, network->ssid, + network->bssid, + priv->capability & CAP_PRIVACY_ON ? "on" : + "off", + network->capability & + WLAN_CAPABILITY_PRIVACY ? "on" : "off"); + return 0; + } + + if ((priv->config & CFG_STATIC_BSSID) && + !ether_addr_equal(network->bssid, priv->bssid)) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of BSSID mismatch: %pM.\n", + network->ssid_len, network->ssid, + network->bssid, priv->bssid); + return 0; + } + + /* Filter out any incompatible freq / mode combinations */ + if (!libipw_is_valid_mode(priv->ieee, network->mode)) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of invalid frequency/mode combination.\n", + network->ssid_len, network->ssid, + network->bssid); + return 0; + } + + /* Filter out invalid channel in current GEO */ + if (!libipw_is_valid_channel(priv->ieee, network->channel)) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of invalid channel in current GEO\n", + network->ssid_len, network->ssid, + network->bssid); + return 0; + } + + /* Ensure that the rates supported by the driver are compatible with + * this AP, including verification of basic rates (mandatory) */ + if (!ipw_compatible_rates(priv, network, &rates)) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because configured rate mask excludes AP mandatory rate.\n", + network->ssid_len, network->ssid, + network->bssid); + return 0; + } + + if (rates.num_rates == 0) { + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of no compatible rates.\n", + network->ssid_len, network->ssid, + network->bssid); + return 0; + } + + /* TODO: Perform any further minimal comparititive tests. We do not + * want to put too much policy logic here; intelligent scan selection + * should occur within a generic IEEE 802.11 user space tool. */ + + /* Set up 'new' AP to this network */ + ipw_copy_rates(&match->rates, &rates); + match->network = network; + + IPW_DEBUG_ASSOC("Network '%*pE (%pM)' is a viable match.\n", + network->ssid_len, network->ssid, network->bssid); + + return 1; +} + +static void ipw_adhoc_create(struct ipw_priv *priv, + struct libipw_network *network) +{ + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); + int i; + + /* + * For the purposes of scanning, we can set our wireless mode + * to trigger scans across combinations of bands, but when it + * comes to creating a new ad-hoc network, we have tell the FW + * exactly which band to use. + * + * We also have the possibility of an invalid channel for the + * chossen band. Attempting to create a new ad-hoc network + * with an invalid channel for wireless mode will trigger a + * FW fatal error. + * + */ + switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { + case LIBIPW_52GHZ_BAND: + network->mode = IEEE_A; + i = libipw_channel_to_index(priv->ieee, priv->channel); + BUG_ON(i == -1); + if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) { + IPW_WARNING("Overriding invalid channel\n"); + priv->channel = geo->a[0].channel; + } + break; + + case LIBIPW_24GHZ_BAND: + if (priv->ieee->mode & IEEE_G) + network->mode = IEEE_G; + else + network->mode = IEEE_B; + i = libipw_channel_to_index(priv->ieee, priv->channel); + BUG_ON(i == -1); + if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) { + IPW_WARNING("Overriding invalid channel\n"); + priv->channel = geo->bg[0].channel; + } + break; + + default: + IPW_WARNING("Overriding invalid channel\n"); + if (priv->ieee->mode & IEEE_A) { + network->mode = IEEE_A; + priv->channel = geo->a[0].channel; + } else if (priv->ieee->mode & IEEE_G) { + network->mode = IEEE_G; + priv->channel = geo->bg[0].channel; + } else { + network->mode = IEEE_B; + priv->channel = geo->bg[0].channel; + } + break; + } + + network->channel = priv->channel; + priv->config |= CFG_ADHOC_PERSIST; + ipw_create_bssid(priv, network->bssid); + network->ssid_len = priv->essid_len; + memcpy(network->ssid, priv->essid, priv->essid_len); + memset(&network->stats, 0, sizeof(network->stats)); + network->capability = WLAN_CAPABILITY_IBSS; + if (!(priv->config & CFG_PREAMBLE_LONG)) + network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE; + if (priv->capability & CAP_PRIVACY_ON) + network->capability |= WLAN_CAPABILITY_PRIVACY; + network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH); + memcpy(network->rates, priv->rates.supported_rates, network->rates_len); + network->rates_ex_len = priv->rates.num_rates - network->rates_len; + memcpy(network->rates_ex, + &priv->rates.supported_rates[network->rates_len], + network->rates_ex_len); + network->last_scanned = 0; + network->flags = 0; + network->last_associate = 0; + network->time_stamp[0] = 0; + network->time_stamp[1] = 0; + network->beacon_interval = 100; /* Default */ + network->listen_interval = 10; /* Default */ + network->atim_window = 0; /* Default */ + network->wpa_ie_len = 0; + network->rsn_ie_len = 0; +} + +static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index) +{ + struct ipw_tgi_tx_key key; + + if (!(priv->ieee->sec.flags & (1 << index))) + return; + + key.key_id = index; + memcpy(key.key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH); + key.security_type = type; + key.station_index = 0; /* always 0 for BSS */ + key.flags = 0; + /* 0 for new key; previous value of counter (after fatal error) */ + key.tx_counter[0] = cpu_to_le32(0); + key.tx_counter[1] = cpu_to_le32(0); + + ipw_send_cmd_pdu(priv, IPW_CMD_TGI_TX_KEY, sizeof(key), &key); +} + +static void ipw_send_wep_keys(struct ipw_priv *priv, int type) +{ + struct ipw_wep_key key; + int i; + + key.cmd_id = DINO_CMD_WEP_KEY; + key.seq_num = 0; + + /* Note: AES keys cannot be set for multiple times. + * Only set it at the first time. */ + for (i = 0; i < 4; i++) { + key.key_index = i | type; + if (!(priv->ieee->sec.flags & (1 << i))) { + key.key_size = 0; + continue; + } + + key.key_size = priv->ieee->sec.key_sizes[i]; + memcpy(key.key, priv->ieee->sec.keys[i], key.key_size); + + ipw_send_cmd_pdu(priv, IPW_CMD_WEP_KEY, sizeof(key), &key); + } +} + +static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level) +{ + if (priv->ieee->host_encrypt) + return; + + switch (level) { + case SEC_LEVEL_3: + priv->sys_config.disable_unicast_decryption = 0; + priv->ieee->host_decrypt = 0; + break; + case SEC_LEVEL_2: + priv->sys_config.disable_unicast_decryption = 1; + priv->ieee->host_decrypt = 1; + break; + case SEC_LEVEL_1: + priv->sys_config.disable_unicast_decryption = 0; + priv->ieee->host_decrypt = 0; + break; + case SEC_LEVEL_0: + priv->sys_config.disable_unicast_decryption = 1; + break; + default: + break; + } +} + +static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level) +{ + if (priv->ieee->host_encrypt) + return; + + switch (level) { + case SEC_LEVEL_3: + priv->sys_config.disable_multicast_decryption = 0; + break; + case SEC_LEVEL_2: + priv->sys_config.disable_multicast_decryption = 1; + break; + case SEC_LEVEL_1: + priv->sys_config.disable_multicast_decryption = 0; + break; + case SEC_LEVEL_0: + priv->sys_config.disable_multicast_decryption = 1; + break; + default: + break; + } +} + +static void ipw_set_hwcrypto_keys(struct ipw_priv *priv) +{ + switch (priv->ieee->sec.level) { + case SEC_LEVEL_3: + if (priv->ieee->sec.flags & SEC_ACTIVE_KEY) + ipw_send_tgi_tx_key(priv, + DCT_FLAG_EXT_SECURITY_CCM, + priv->ieee->sec.active_key); + + if (!priv->ieee->host_mc_decrypt) + ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM); + break; + case SEC_LEVEL_2: + if (priv->ieee->sec.flags & SEC_ACTIVE_KEY) + ipw_send_tgi_tx_key(priv, + DCT_FLAG_EXT_SECURITY_TKIP, + priv->ieee->sec.active_key); + break; + case SEC_LEVEL_1: + ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP); + ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level); + ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level); + break; + case SEC_LEVEL_0: + default: + break; + } +} + +static void ipw_adhoc_check(void *data) +{ + struct ipw_priv *priv = data; + + if (priv->missed_adhoc_beacons++ > priv->disassociate_threshold && + !(priv->config & CFG_ADHOC_PERSIST)) { + IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | + IPW_DL_STATE | IPW_DL_ASSOC, + "Missed beacon: %d - disassociate\n", + priv->missed_adhoc_beacons); + ipw_remove_current_network(priv); + ipw_disassociate(priv); + return; + } + + schedule_delayed_work(&priv->adhoc_check, + le16_to_cpu(priv->assoc_request.beacon_interval)); +} + +static void ipw_bg_adhoc_check(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, adhoc_check.work); + mutex_lock(&priv->mutex); + ipw_adhoc_check(priv); + mutex_unlock(&priv->mutex); +} + +static void ipw_debug_config(struct ipw_priv *priv) +{ + IPW_DEBUG_INFO("Scan completed, no valid APs matched " + "[CFG 0x%08X]\n", priv->config); + if (priv->config & CFG_STATIC_CHANNEL) + IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel); + else + IPW_DEBUG_INFO("Channel unlocked.\n"); + if (priv->config & CFG_STATIC_ESSID) + IPW_DEBUG_INFO("ESSID locked to '%*pE'\n", + priv->essid_len, priv->essid); + else + IPW_DEBUG_INFO("ESSID unlocked.\n"); + if (priv->config & CFG_STATIC_BSSID) + IPW_DEBUG_INFO("BSSID locked to %pM\n", priv->bssid); + else + IPW_DEBUG_INFO("BSSID unlocked.\n"); + if (priv->capability & CAP_PRIVACY_ON) + IPW_DEBUG_INFO("PRIVACY on\n"); + else + IPW_DEBUG_INFO("PRIVACY off\n"); + IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask); +} + +static void ipw_set_fixed_rate(struct ipw_priv *priv, int mode) +{ + /* TODO: Verify that this works... */ + struct ipw_fixed_rate fr; + u32 reg; + u16 mask = 0; + u16 new_tx_rates = priv->rates_mask; + + /* Identify 'current FW band' and match it with the fixed + * Tx rates */ + + switch (priv->ieee->freq_band) { + case LIBIPW_52GHZ_BAND: /* A only */ + /* IEEE_A */ + if (priv->rates_mask & ~LIBIPW_OFDM_RATES_MASK) { + /* Invalid fixed rate mask */ + IPW_DEBUG_WX + ("invalid fixed rate mask in ipw_set_fixed_rate\n"); + new_tx_rates = 0; + break; + } + + new_tx_rates >>= LIBIPW_OFDM_SHIFT_MASK_A; + break; + + default: /* 2.4Ghz or Mixed */ + /* IEEE_B */ + if (mode == IEEE_B) { + if (new_tx_rates & ~LIBIPW_CCK_RATES_MASK) { + /* Invalid fixed rate mask */ + IPW_DEBUG_WX + ("invalid fixed rate mask in ipw_set_fixed_rate\n"); + new_tx_rates = 0; + } + break; + } + + /* IEEE_G */ + if (new_tx_rates & ~(LIBIPW_CCK_RATES_MASK | + LIBIPW_OFDM_RATES_MASK)) { + /* Invalid fixed rate mask */ + IPW_DEBUG_WX + ("invalid fixed rate mask in ipw_set_fixed_rate\n"); + new_tx_rates = 0; + break; + } + + if (LIBIPW_OFDM_RATE_6MB_MASK & new_tx_rates) { + mask |= (LIBIPW_OFDM_RATE_6MB_MASK >> 1); + new_tx_rates &= ~LIBIPW_OFDM_RATE_6MB_MASK; + } + + if (LIBIPW_OFDM_RATE_9MB_MASK & new_tx_rates) { + mask |= (LIBIPW_OFDM_RATE_9MB_MASK >> 1); + new_tx_rates &= ~LIBIPW_OFDM_RATE_9MB_MASK; + } + + if (LIBIPW_OFDM_RATE_12MB_MASK & new_tx_rates) { + mask |= (LIBIPW_OFDM_RATE_12MB_MASK >> 1); + new_tx_rates &= ~LIBIPW_OFDM_RATE_12MB_MASK; + } + + new_tx_rates |= mask; + break; + } + + fr.tx_rates = cpu_to_le16(new_tx_rates); + + reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE); + ipw_write_reg32(priv, reg, *(u32 *) & fr); +} + +static void ipw_abort_scan(struct ipw_priv *priv) +{ + int err; + + if (priv->status & STATUS_SCAN_ABORTING) { + IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n"); + return; + } + priv->status |= STATUS_SCAN_ABORTING; + + err = ipw_send_scan_abort(priv); + if (err) + IPW_DEBUG_HC("Request to abort scan failed.\n"); +} + +static void ipw_add_scan_channels(struct ipw_priv *priv, + struct ipw_scan_request_ext *scan, + int scan_type) +{ + int channel_index = 0; + const struct libipw_geo *geo; + int i; + + geo = libipw_get_geo(priv->ieee); + + if (priv->ieee->freq_band & LIBIPW_52GHZ_BAND) { + int start = channel_index; + for (i = 0; i < geo->a_channels; i++) { + if ((priv->status & STATUS_ASSOCIATED) && + geo->a[i].channel == priv->channel) + continue; + channel_index++; + scan->channels_list[channel_index] = geo->a[i].channel; + ipw_set_scan_type(scan, channel_index, + geo->a[i]. + flags & LIBIPW_CH_PASSIVE_ONLY ? + IPW_SCAN_PASSIVE_FULL_DWELL_SCAN : + scan_type); + } + + if (start != channel_index) { + scan->channels_list[start] = (u8) (IPW_A_MODE << 6) | + (channel_index - start); + channel_index++; + } + } + + if (priv->ieee->freq_band & LIBIPW_24GHZ_BAND) { + int start = channel_index; + if (priv->config & CFG_SPEED_SCAN) { + int index; + u8 channels[LIBIPW_24GHZ_CHANNELS] = { + /* nop out the list */ + [0] = 0 + }; + + u8 channel; + while (channel_index < IPW_SCAN_CHANNELS - 1) { + channel = + priv->speed_scan[priv->speed_scan_pos]; + if (channel == 0) { + priv->speed_scan_pos = 0; + channel = priv->speed_scan[0]; + } + if ((priv->status & STATUS_ASSOCIATED) && + channel == priv->channel) { + priv->speed_scan_pos++; + continue; + } + + /* If this channel has already been + * added in scan, break from loop + * and this will be the first channel + * in the next scan. + */ + if (channels[channel - 1] != 0) + break; + + channels[channel - 1] = 1; + priv->speed_scan_pos++; + channel_index++; + scan->channels_list[channel_index] = channel; + index = + libipw_channel_to_index(priv->ieee, channel); + ipw_set_scan_type(scan, channel_index, + geo->bg[index]. + flags & + LIBIPW_CH_PASSIVE_ONLY ? + IPW_SCAN_PASSIVE_FULL_DWELL_SCAN + : scan_type); + } + } else { + for (i = 0; i < geo->bg_channels; i++) { + if ((priv->status & STATUS_ASSOCIATED) && + geo->bg[i].channel == priv->channel) + continue; + channel_index++; + scan->channels_list[channel_index] = + geo->bg[i].channel; + ipw_set_scan_type(scan, channel_index, + geo->bg[i]. + flags & + LIBIPW_CH_PASSIVE_ONLY ? + IPW_SCAN_PASSIVE_FULL_DWELL_SCAN + : scan_type); + } + } + + if (start != channel_index) { + scan->channels_list[start] = (u8) (IPW_B_MODE << 6) | + (channel_index - start); + } + } +} + +static int ipw_passive_dwell_time(struct ipw_priv *priv) +{ + /* staying on passive channels longer than the DTIM interval during a + * scan, while associated, causes the firmware to cancel the scan + * without notification. Hence, don't stay on passive channels longer + * than the beacon interval. + */ + if (priv->status & STATUS_ASSOCIATED + && priv->assoc_network->beacon_interval > 10) + return priv->assoc_network->beacon_interval - 10; + else + return 120; +} + +static int ipw_request_scan_helper(struct ipw_priv *priv, int type, int direct) +{ + struct ipw_scan_request_ext scan; + int err = 0, scan_type; + + if (!(priv->status & STATUS_INIT) || + (priv->status & STATUS_EXIT_PENDING)) + return 0; + + mutex_lock(&priv->mutex); + + if (direct && (priv->direct_scan_ssid_len == 0)) { + IPW_DEBUG_HC("Direct scan requested but no SSID to scan for\n"); + priv->status &= ~STATUS_DIRECT_SCAN_PENDING; + goto done; + } + + if (priv->status & STATUS_SCANNING) { + IPW_DEBUG_HC("Concurrent scan requested. Queuing.\n"); + priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING : + STATUS_SCAN_PENDING; + goto done; + } + + if (!(priv->status & STATUS_SCAN_FORCED) && + priv->status & STATUS_SCAN_ABORTING) { + IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n"); + priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING : + STATUS_SCAN_PENDING; + goto done; + } + + if (priv->status & STATUS_RF_KILL_MASK) { + IPW_DEBUG_HC("Queuing scan due to RF Kill activation\n"); + priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING : + STATUS_SCAN_PENDING; + goto done; + } + + memset(&scan, 0, sizeof(scan)); + scan.full_scan_index = cpu_to_le32(libipw_get_scans(priv->ieee)); + + if (type == IW_SCAN_TYPE_PASSIVE) { + IPW_DEBUG_WX("use passive scanning\n"); + scan_type = IPW_SCAN_PASSIVE_FULL_DWELL_SCAN; + scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = + cpu_to_le16(ipw_passive_dwell_time(priv)); + ipw_add_scan_channels(priv, &scan, scan_type); + goto send_request; + } + + /* Use active scan by default. */ + if (priv->config & CFG_SPEED_SCAN) + scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] = + cpu_to_le16(30); + else + scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] = + cpu_to_le16(20); + + scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] = + cpu_to_le16(20); + + scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = + cpu_to_le16(ipw_passive_dwell_time(priv)); + scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20); + +#ifdef CONFIG_IPW2200_MONITOR + if (priv->ieee->iw_mode == IW_MODE_MONITOR) { + u8 channel; + u8 band = 0; + + switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { + case LIBIPW_52GHZ_BAND: + band = (u8) (IPW_A_MODE << 6) | 1; + channel = priv->channel; + break; + + case LIBIPW_24GHZ_BAND: + band = (u8) (IPW_B_MODE << 6) | 1; + channel = priv->channel; + break; + + default: + band = (u8) (IPW_B_MODE << 6) | 1; + channel = 9; + break; + } + + scan.channels_list[0] = band; + scan.channels_list[1] = channel; + ipw_set_scan_type(&scan, 1, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN); + + /* NOTE: The card will sit on this channel for this time + * period. Scan aborts are timing sensitive and frequently + * result in firmware restarts. As such, it is best to + * set a small dwell_time here and just keep re-issuing + * scans. Otherwise fast channel hopping will not actually + * hop channels. + * + * TODO: Move SPEED SCAN support to all modes and bands */ + scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = + cpu_to_le16(2000); + } else { +#endif /* CONFIG_IPW2200_MONITOR */ + /* Honor direct scans first, otherwise if we are roaming make + * this a direct scan for the current network. Finally, + * ensure that every other scan is a fast channel hop scan */ + if (direct) { + err = ipw_send_ssid(priv, priv->direct_scan_ssid, + priv->direct_scan_ssid_len); + if (err) { + IPW_DEBUG_HC("Attempt to send SSID command " + "failed\n"); + goto done; + } + + scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN; + } else if ((priv->status & STATUS_ROAMING) + || (!(priv->status & STATUS_ASSOCIATED) + && (priv->config & CFG_STATIC_ESSID) + && (le32_to_cpu(scan.full_scan_index) % 2))) { + err = ipw_send_ssid(priv, priv->essid, priv->essid_len); + if (err) { + IPW_DEBUG_HC("Attempt to send SSID command " + "failed.\n"); + goto done; + } + + scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN; + } else + scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN; + + ipw_add_scan_channels(priv, &scan, scan_type); +#ifdef CONFIG_IPW2200_MONITOR + } +#endif + +send_request: + err = ipw_send_scan_request_ext(priv, &scan); + if (err) { + IPW_DEBUG_HC("Sending scan command failed: %08X\n", err); + goto done; + } + + priv->status |= STATUS_SCANNING; + if (direct) { + priv->status &= ~STATUS_DIRECT_SCAN_PENDING; + priv->direct_scan_ssid_len = 0; + } else + priv->status &= ~STATUS_SCAN_PENDING; + + schedule_delayed_work(&priv->scan_check, IPW_SCAN_CHECK_WATCHDOG); +done: + mutex_unlock(&priv->mutex); + return err; +} + +static void ipw_request_passive_scan(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, request_passive_scan.work); + ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE, 0); +} + +static void ipw_request_scan(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, request_scan.work); + ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 0); +} + +static void ipw_request_direct_scan(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, request_direct_scan.work); + ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 1); +} + +static void ipw_bg_abort_scan(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, abort_scan); + mutex_lock(&priv->mutex); + ipw_abort_scan(priv); + mutex_unlock(&priv->mutex); +} + +static int ipw_wpa_enable(struct ipw_priv *priv, int value) +{ + /* This is called when wpa_supplicant loads and closes the driver + * interface. */ + priv->ieee->wpa_enabled = value; + return 0; +} + +static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value) +{ + struct libipw_device *ieee = priv->ieee; + struct libipw_security sec = { + .flags = SEC_AUTH_MODE, + }; + int ret = 0; + + if (value & IW_AUTH_ALG_SHARED_KEY) { + sec.auth_mode = WLAN_AUTH_SHARED_KEY; + ieee->open_wep = 0; + } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) { + sec.auth_mode = WLAN_AUTH_OPEN; + ieee->open_wep = 1; + } else if (value & IW_AUTH_ALG_LEAP) { + sec.auth_mode = WLAN_AUTH_LEAP; + ieee->open_wep = 1; + } else + return -EINVAL; + + if (ieee->set_security) + ieee->set_security(ieee->dev, &sec); + else + ret = -EOPNOTSUPP; + + return ret; +} + +static void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie, + int wpa_ie_len) +{ + /* make sure WPA is enabled */ + ipw_wpa_enable(priv, 1); +} + +static int ipw_set_rsn_capa(struct ipw_priv *priv, + char *capabilities, int length) +{ + IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n"); + + return ipw_send_cmd_pdu(priv, IPW_CMD_RSN_CAPABILITIES, length, + capabilities); +} + +/* + * WE-18 support + */ + +/* SIOCSIWGENIE */ +static int ipw_wx_set_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; + u8 *buf; + int err = 0; + + if (wrqu->data.length > MAX_WPA_IE_LEN || + (wrqu->data.length && extra == NULL)) + return -EINVAL; + + if (wrqu->data.length) { + buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL); + if (buf == NULL) { + err = -ENOMEM; + goto out; + } + + kfree(ieee->wpa_ie); + ieee->wpa_ie = buf; + ieee->wpa_ie_len = wrqu->data.length; + } else { + kfree(ieee->wpa_ie); + ieee->wpa_ie = NULL; + ieee->wpa_ie_len = 0; + } + + ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len); + out: + return err; +} + +/* SIOCGIWGENIE */ +static int ipw_wx_get_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; + int err = 0; + + if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) { + wrqu->data.length = 0; + goto out; + } + + if (wrqu->data.length < ieee->wpa_ie_len) { + err = -E2BIG; + goto out; + } + + wrqu->data.length = ieee->wpa_ie_len; + memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len); + + out: + return err; +} + +static int wext_cipher2level(int cipher) +{ + switch (cipher) { + case IW_AUTH_CIPHER_NONE: + return SEC_LEVEL_0; + case IW_AUTH_CIPHER_WEP40: + case IW_AUTH_CIPHER_WEP104: + return SEC_LEVEL_1; + case IW_AUTH_CIPHER_TKIP: + return SEC_LEVEL_2; + case IW_AUTH_CIPHER_CCMP: + return SEC_LEVEL_3; + default: + return -1; + } +} + +/* SIOCSIWAUTH */ +static int ipw_wx_set_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; + struct iw_param *param = &wrqu->param; + struct lib80211_crypt_data *crypt; + unsigned long flags; + int ret = 0; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + break; + case IW_AUTH_CIPHER_PAIRWISE: + ipw_set_hw_decrypt_unicast(priv, + wext_cipher2level(param->value)); + break; + case IW_AUTH_CIPHER_GROUP: + ipw_set_hw_decrypt_multicast(priv, + wext_cipher2level(param->value)); + break; + case IW_AUTH_KEY_MGMT: + /* + * ipw2200 does not use these parameters + */ + break; + + case IW_AUTH_TKIP_COUNTERMEASURES: + crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; + if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) + break; + + flags = crypt->ops->get_flags(crypt->priv); + + if (param->value) + flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; + else + flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; + + crypt->ops->set_flags(flags, crypt->priv); + + break; + + case IW_AUTH_DROP_UNENCRYPTED:{ + /* HACK: + * + * wpa_supplicant calls set_wpa_enabled when the driver + * is loaded and unloaded, regardless of if WPA is being + * used. No other calls are made which can be used to + * determine if encryption will be used or not prior to + * association being expected. If encryption is not being + * used, drop_unencrypted is set to false, else true -- we + * can use this to determine if the CAP_PRIVACY_ON bit should + * be set. + */ + struct libipw_security sec = { + .flags = SEC_ENABLED, + .enabled = param->value, + }; + priv->ieee->drop_unencrypted = param->value; + /* We only change SEC_LEVEL for open mode. Others + * are set by ipw_wpa_set_encryption. + */ + if (!param->value) { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_0; + } else { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; + } + if (priv->ieee->set_security) + priv->ieee->set_security(priv->ieee->dev, &sec); + break; + } + + case IW_AUTH_80211_AUTH_ALG: + ret = ipw_wpa_set_auth_algs(priv, param->value); + break; + + case IW_AUTH_WPA_ENABLED: + ret = ipw_wpa_enable(priv, param->value); + ipw_disassociate(priv); + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + ieee->ieee802_1x = param->value; + break; + + case IW_AUTH_PRIVACY_INVOKED: + ieee->privacy_invoked = param->value; + break; + + default: + return -EOPNOTSUPP; + } + return ret; +} + +/* SIOCGIWAUTH */ +static int ipw_wx_get_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; + struct lib80211_crypt_data *crypt; + struct iw_param *param = &wrqu->param; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * wpa_supplicant will control these internally + */ + return -EOPNOTSUPP; + + case IW_AUTH_TKIP_COUNTERMEASURES: + crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; + if (!crypt || !crypt->ops->get_flags) + break; + + param->value = (crypt->ops->get_flags(crypt->priv) & + IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0; + + break; + + case IW_AUTH_DROP_UNENCRYPTED: + param->value = ieee->drop_unencrypted; + break; + + case IW_AUTH_80211_AUTH_ALG: + param->value = ieee->sec.auth_mode; + break; + + case IW_AUTH_WPA_ENABLED: + param->value = ieee->wpa_enabled; + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + param->value = ieee->ieee802_1x; + break; + + case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: + param->value = ieee->privacy_invoked; + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + +/* SIOCSIWENCODEEXT */ +static int ipw_wx_set_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + + if (hwcrypto) { + if (ext->alg == IW_ENCODE_ALG_TKIP) { + /* IPW HW can't build TKIP MIC, + host decryption still needed */ + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) + priv->ieee->host_mc_decrypt = 1; + else { + priv->ieee->host_encrypt = 0; + priv->ieee->host_encrypt_msdu = 1; + priv->ieee->host_decrypt = 1; + } + } else { + priv->ieee->host_encrypt = 0; + priv->ieee->host_encrypt_msdu = 0; + priv->ieee->host_decrypt = 0; + priv->ieee->host_mc_decrypt = 0; + } + } + + return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra); +} + +/* SIOCGIWENCODEEXT */ +static int ipw_wx_get_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra); +} + +/* SIOCSIWMLME */ +static int ipw_wx_set_mlme(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + struct iw_mlme *mlme = (struct iw_mlme *)extra; + __le16 reason; + + reason = cpu_to_le16(mlme->reason_code); + + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + /* silently ignore */ + break; + + case IW_MLME_DISASSOC: + ipw_disassociate(priv); + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + +#ifdef CONFIG_IPW2200_QOS + +/* QoS */ +/* +* get the modulation type of the current network or +* the card current mode +*/ +static u8 ipw_qos_current_mode(struct ipw_priv * priv) +{ + u8 mode = 0; + + if (priv->status & STATUS_ASSOCIATED) { + unsigned long flags; + + spin_lock_irqsave(&priv->ieee->lock, flags); + mode = priv->assoc_network->mode; + spin_unlock_irqrestore(&priv->ieee->lock, flags); + } else { + mode = priv->ieee->mode; + } + IPW_DEBUG_QOS("QoS network/card mode %d\n", mode); + return mode; +} + +/* +* Handle management frame beacon and probe response +*/ +static int ipw_qos_handle_probe_response(struct ipw_priv *priv, + int active_network, + struct libipw_network *network) +{ + u32 size = sizeof(struct libipw_qos_parameters); + + if (network->capability & WLAN_CAPABILITY_IBSS) + network->qos_data.active = network->qos_data.supported; + + if (network->flags & NETWORK_HAS_QOS_MASK) { + if (active_network && + (network->flags & NETWORK_HAS_QOS_PARAMETERS)) + network->qos_data.active = network->qos_data.supported; + + if ((network->qos_data.active == 1) && (active_network == 1) && + (network->flags & NETWORK_HAS_QOS_PARAMETERS) && + (network->qos_data.old_param_count != + network->qos_data.param_count)) { + network->qos_data.old_param_count = + network->qos_data.param_count; + schedule_work(&priv->qos_activate); + IPW_DEBUG_QOS("QoS parameters change call " + "qos_activate\n"); + } + } else { + if ((priv->ieee->mode == IEEE_B) || (network->mode == IEEE_B)) + memcpy(&network->qos_data.parameters, + &def_parameters_CCK, size); + else + memcpy(&network->qos_data.parameters, + &def_parameters_OFDM, size); + + if ((network->qos_data.active == 1) && (active_network == 1)) { + IPW_DEBUG_QOS("QoS was disabled call qos_activate\n"); + schedule_work(&priv->qos_activate); + } + + network->qos_data.active = 0; + network->qos_data.supported = 0; + } + if ((priv->status & STATUS_ASSOCIATED) && + (priv->ieee->iw_mode == IW_MODE_ADHOC) && (active_network == 0)) { + if (!ether_addr_equal(network->bssid, priv->bssid)) + if (network->capability & WLAN_CAPABILITY_IBSS) + if ((network->ssid_len == + priv->assoc_network->ssid_len) && + !memcmp(network->ssid, + priv->assoc_network->ssid, + network->ssid_len)) { + schedule_work(&priv->merge_networks); + } + } + + return 0; +} + +/* +* This function set up the firmware to support QoS. It sends +* IPW_CMD_QOS_PARAMETERS and IPW_CMD_WME_INFO +*/ +static int ipw_qos_activate(struct ipw_priv *priv, + struct libipw_qos_data *qos_network_data) +{ + int err; + struct libipw_qos_parameters qos_parameters[QOS_QOS_SETS]; + struct libipw_qos_parameters *active_one = NULL; + u32 size = sizeof(struct libipw_qos_parameters); + u32 burst_duration; + int i; + u8 type; + + type = ipw_qos_current_mode(priv); + + active_one = &(qos_parameters[QOS_PARAM_SET_DEF_CCK]); + memcpy(active_one, priv->qos_data.def_qos_parm_CCK, size); + active_one = &(qos_parameters[QOS_PARAM_SET_DEF_OFDM]); + memcpy(active_one, priv->qos_data.def_qos_parm_OFDM, size); + + if (qos_network_data == NULL) { + if (type == IEEE_B) { + IPW_DEBUG_QOS("QoS activate network mode %d\n", type); + active_one = &def_parameters_CCK; + } else + active_one = &def_parameters_OFDM; + + memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size); + burst_duration = ipw_qos_get_burst_duration(priv); + for (i = 0; i < QOS_QUEUE_NUM; i++) + qos_parameters[QOS_PARAM_SET_ACTIVE].tx_op_limit[i] = + cpu_to_le16(burst_duration); + } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) { + if (type == IEEE_B) { + IPW_DEBUG_QOS("QoS activate IBSS network mode %d\n", + type); + if (priv->qos_data.qos_enable == 0) + active_one = &def_parameters_CCK; + else + active_one = priv->qos_data.def_qos_parm_CCK; + } else { + if (priv->qos_data.qos_enable == 0) + active_one = &def_parameters_OFDM; + else + active_one = priv->qos_data.def_qos_parm_OFDM; + } + memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size); + } else { + unsigned long flags; + int active; + + spin_lock_irqsave(&priv->ieee->lock, flags); + active_one = &(qos_network_data->parameters); + qos_network_data->old_param_count = + qos_network_data->param_count; + memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size); + active = qos_network_data->supported; + spin_unlock_irqrestore(&priv->ieee->lock, flags); + + if (active == 0) { + burst_duration = ipw_qos_get_burst_duration(priv); + for (i = 0; i < QOS_QUEUE_NUM; i++) + qos_parameters[QOS_PARAM_SET_ACTIVE]. + tx_op_limit[i] = cpu_to_le16(burst_duration); + } + } + + IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n"); + err = ipw_send_qos_params_command(priv, &qos_parameters[0]); + if (err) + IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n"); + + return err; +} + +/* +* send IPW_CMD_WME_INFO to the firmware +*/ +static int ipw_qos_set_info_element(struct ipw_priv *priv) +{ + int ret = 0; + struct libipw_qos_information_element qos_info; + + if (priv == NULL) + return -1; + + qos_info.elementID = QOS_ELEMENT_ID; + qos_info.length = sizeof(struct libipw_qos_information_element) - 2; + + qos_info.version = QOS_VERSION_1; + qos_info.ac_info = 0; + + memcpy(qos_info.qui, qos_oui, QOS_OUI_LEN); + qos_info.qui_type = QOS_OUI_TYPE; + qos_info.qui_subtype = QOS_OUI_INFO_SUB_TYPE; + + ret = ipw_send_qos_info_command(priv, &qos_info); + if (ret != 0) { + IPW_DEBUG_QOS("QoS error calling ipw_send_qos_info_command\n"); + } + return ret; +} + +/* +* Set the QoS parameter with the association request structure +*/ +static int ipw_qos_association(struct ipw_priv *priv, + struct libipw_network *network) +{ + int err = 0; + struct libipw_qos_data *qos_data = NULL; + struct libipw_qos_data ibss_data = { + .supported = 1, + .active = 1, + }; + + switch (priv->ieee->iw_mode) { + case IW_MODE_ADHOC: + BUG_ON(!(network->capability & WLAN_CAPABILITY_IBSS)); + + qos_data = &ibss_data; + break; + + case IW_MODE_INFRA: + qos_data = &network->qos_data; + break; + + default: + BUG(); + break; + } + + err = ipw_qos_activate(priv, qos_data); + if (err) { + priv->assoc_request.policy_support &= ~HC_QOS_SUPPORT_ASSOC; + return err; + } + + if (priv->qos_data.qos_enable && qos_data->supported) { + IPW_DEBUG_QOS("QoS will be enabled for this association\n"); + priv->assoc_request.policy_support |= HC_QOS_SUPPORT_ASSOC; + return ipw_qos_set_info_element(priv); + } + + return 0; +} + +/* +* handling the beaconing responses. if we get different QoS setting +* off the network from the associated setting, adjust the QoS +* setting +*/ +static int ipw_qos_association_resp(struct ipw_priv *priv, + struct libipw_network *network) +{ + int ret = 0; + unsigned long flags; + u32 size = sizeof(struct libipw_qos_parameters); + int set_qos_param = 0; + + if ((priv == NULL) || (network == NULL) || + (priv->assoc_network == NULL)) + return ret; + + if (!(priv->status & STATUS_ASSOCIATED)) + return ret; + + if ((priv->ieee->iw_mode != IW_MODE_INFRA)) + return ret; + + spin_lock_irqsave(&priv->ieee->lock, flags); + if (network->flags & NETWORK_HAS_QOS_PARAMETERS) { + memcpy(&priv->assoc_network->qos_data, &network->qos_data, + sizeof(struct libipw_qos_data)); + priv->assoc_network->qos_data.active = 1; + if ((network->qos_data.old_param_count != + network->qos_data.param_count)) { + set_qos_param = 1; + network->qos_data.old_param_count = + network->qos_data.param_count; + } + + } else { + if ((network->mode == IEEE_B) || (priv->ieee->mode == IEEE_B)) + memcpy(&priv->assoc_network->qos_data.parameters, + &def_parameters_CCK, size); + else + memcpy(&priv->assoc_network->qos_data.parameters, + &def_parameters_OFDM, size); + priv->assoc_network->qos_data.active = 0; + priv->assoc_network->qos_data.supported = 0; + set_qos_param = 1; + } + + spin_unlock_irqrestore(&priv->ieee->lock, flags); + + if (set_qos_param == 1) + schedule_work(&priv->qos_activate); + + return ret; +} + +static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv) +{ + u32 ret = 0; + + if ((priv == NULL)) + return 0; + + if (!(priv->ieee->modulation & LIBIPW_OFDM_MODULATION)) + ret = priv->qos_data.burst_duration_CCK; + else + ret = priv->qos_data.burst_duration_OFDM; + + return ret; +} + +/* +* Initialize the setting of QoS global +*/ +static void ipw_qos_init(struct ipw_priv *priv, int enable, + int burst_enable, u32 burst_duration_CCK, + u32 burst_duration_OFDM) +{ + priv->qos_data.qos_enable = enable; + + if (priv->qos_data.qos_enable) { + priv->qos_data.def_qos_parm_CCK = &def_qos_parameters_CCK; + priv->qos_data.def_qos_parm_OFDM = &def_qos_parameters_OFDM; + IPW_DEBUG_QOS("QoS is enabled\n"); + } else { + priv->qos_data.def_qos_parm_CCK = &def_parameters_CCK; + priv->qos_data.def_qos_parm_OFDM = &def_parameters_OFDM; + IPW_DEBUG_QOS("QoS is not enabled\n"); + } + + priv->qos_data.burst_enable = burst_enable; + + if (burst_enable) { + priv->qos_data.burst_duration_CCK = burst_duration_CCK; + priv->qos_data.burst_duration_OFDM = burst_duration_OFDM; + } else { + priv->qos_data.burst_duration_CCK = 0; + priv->qos_data.burst_duration_OFDM = 0; + } +} + +/* +* map the packet priority to the right TX Queue +*/ +static int ipw_get_tx_queue_number(struct ipw_priv *priv, u16 priority) +{ + if (priority > 7 || !priv->qos_data.qos_enable) + priority = 0; + + return from_priority_to_tx_queue[priority] - 1; +} + +static int ipw_is_qos_active(struct net_device *dev, + struct sk_buff *skb) +{ + struct ipw_priv *priv = libipw_priv(dev); + struct libipw_qos_data *qos_data = NULL; + int active, supported; + u8 *daddr = skb->data + ETH_ALEN; + int unicast = !is_multicast_ether_addr(daddr); + + if (!(priv->status & STATUS_ASSOCIATED)) + return 0; + + qos_data = &priv->assoc_network->qos_data; + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) { + if (unicast == 0) + qos_data->active = 0; + else + qos_data->active = qos_data->supported; + } + active = qos_data->active; + supported = qos_data->supported; + IPW_DEBUG_QOS("QoS %d network is QoS active %d supported %d " + "unicast %d\n", + priv->qos_data.qos_enable, active, supported, unicast); + if (active && priv->qos_data.qos_enable) + return 1; + + return 0; + +} +/* +* add QoS parameter to the TX command +*/ +static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv, + u16 priority, + struct tfd_data *tfd) +{ + int tx_queue_id = 0; + + + tx_queue_id = from_priority_to_tx_queue[priority] - 1; + tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED; + + if (priv->qos_data.qos_no_ack_mask & (1UL << tx_queue_id)) { + tfd->tx_flags &= ~DCT_FLAG_ACK_REQD; + tfd->tfd.tfd_26.mchdr.qos_ctrl |= cpu_to_le16(CTRL_QOS_NO_ACK); + } + return 0; +} + +/* +* background support to run QoS activate functionality +*/ +static void ipw_bg_qos_activate(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, qos_activate); + + mutex_lock(&priv->mutex); + + if (priv->status & STATUS_ASSOCIATED) + ipw_qos_activate(priv, &(priv->assoc_network->qos_data)); + + mutex_unlock(&priv->mutex); +} + +static int ipw_handle_probe_response(struct net_device *dev, + struct libipw_probe_response *resp, + struct libipw_network *network) +{ + struct ipw_priv *priv = libipw_priv(dev); + int active_network = ((priv->status & STATUS_ASSOCIATED) && + (network == priv->assoc_network)); + + ipw_qos_handle_probe_response(priv, active_network, network); + + return 0; +} + +static int ipw_handle_beacon(struct net_device *dev, + struct libipw_beacon *resp, + struct libipw_network *network) +{ + struct ipw_priv *priv = libipw_priv(dev); + int active_network = ((priv->status & STATUS_ASSOCIATED) && + (network == priv->assoc_network)); + + ipw_qos_handle_probe_response(priv, active_network, network); + + return 0; +} + +static int ipw_handle_assoc_response(struct net_device *dev, + struct libipw_assoc_response *resp, + struct libipw_network *network) +{ + struct ipw_priv *priv = libipw_priv(dev); + ipw_qos_association_resp(priv, network); + return 0; +} + +static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters + *qos_param) +{ + return ipw_send_cmd_pdu(priv, IPW_CMD_QOS_PARAMETERS, + sizeof(*qos_param) * 3, qos_param); +} + +static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element + *qos_param) +{ + return ipw_send_cmd_pdu(priv, IPW_CMD_WME_INFO, sizeof(*qos_param), + qos_param); +} + +#endif /* CONFIG_IPW2200_QOS */ + +static int ipw_associate_network(struct ipw_priv *priv, + struct libipw_network *network, + struct ipw_supported_rates *rates, int roaming) +{ + int err; + + if (priv->config & CFG_FIXED_RATE) + ipw_set_fixed_rate(priv, network->mode); + + if (!(priv->config & CFG_STATIC_ESSID)) { + priv->essid_len = min(network->ssid_len, + (u8) IW_ESSID_MAX_SIZE); + memcpy(priv->essid, network->ssid, priv->essid_len); + } + + network->last_associate = jiffies; + + memset(&priv->assoc_request, 0, sizeof(priv->assoc_request)); + priv->assoc_request.channel = network->channel; + priv->assoc_request.auth_key = 0; + + if ((priv->capability & CAP_PRIVACY_ON) && + (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)) { + priv->assoc_request.auth_type = AUTH_SHARED_KEY; + priv->assoc_request.auth_key = priv->ieee->sec.active_key; + + if (priv->ieee->sec.level == SEC_LEVEL_1) + ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP); + + } else if ((priv->capability & CAP_PRIVACY_ON) && + (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP)) + priv->assoc_request.auth_type = AUTH_LEAP; + else + priv->assoc_request.auth_type = AUTH_OPEN; + + if (priv->ieee->wpa_ie_len) { + priv->assoc_request.policy_support = cpu_to_le16(0x02); /* RSN active */ + ipw_set_rsn_capa(priv, priv->ieee->wpa_ie, + priv->ieee->wpa_ie_len); + } + + /* + * It is valid for our ieee device to support multiple modes, but + * when it comes to associating to a given network we have to choose + * just one mode. + */ + if (network->mode & priv->ieee->mode & IEEE_A) + priv->assoc_request.ieee_mode = IPW_A_MODE; + else if (network->mode & priv->ieee->mode & IEEE_G) + priv->assoc_request.ieee_mode = IPW_G_MODE; + else if (network->mode & priv->ieee->mode & IEEE_B) + priv->assoc_request.ieee_mode = IPW_B_MODE; + + priv->assoc_request.capability = cpu_to_le16(network->capability); + if ((network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) + && !(priv->config & CFG_PREAMBLE_LONG)) { + priv->assoc_request.preamble_length = DCT_FLAG_SHORT_PREAMBLE; + } else { + priv->assoc_request.preamble_length = DCT_FLAG_LONG_PREAMBLE; + + /* Clear the short preamble if we won't be supporting it */ + priv->assoc_request.capability &= + ~cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); + } + + /* Clear capability bits that aren't used in Ad Hoc */ + if (priv->ieee->iw_mode == IW_MODE_ADHOC) + priv->assoc_request.capability &= + ~cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME); + + IPW_DEBUG_ASSOC("%ssociation attempt: '%*pE', channel %d, 802.11%c [%d], %s[:%s], enc=%s%s%s%c%c\n", + roaming ? "Rea" : "A", + priv->essid_len, priv->essid, + network->channel, + ipw_modes[priv->assoc_request.ieee_mode], + rates->num_rates, + (priv->assoc_request.preamble_length == + DCT_FLAG_LONG_PREAMBLE) ? "long" : "short", + network->capability & + WLAN_CAPABILITY_SHORT_PREAMBLE ? "short" : "long", + priv->capability & CAP_PRIVACY_ON ? "on " : "off", + priv->capability & CAP_PRIVACY_ON ? + (priv->capability & CAP_SHARED_KEY ? "(shared)" : + "(open)") : "", + priv->capability & CAP_PRIVACY_ON ? " key=" : "", + priv->capability & CAP_PRIVACY_ON ? + '1' + priv->ieee->sec.active_key : '.', + priv->capability & CAP_PRIVACY_ON ? '.' : ' '); + + priv->assoc_request.beacon_interval = cpu_to_le16(network->beacon_interval); + if ((priv->ieee->iw_mode == IW_MODE_ADHOC) && + (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) { + priv->assoc_request.assoc_type = HC_IBSS_START; + priv->assoc_request.assoc_tsf_msw = 0; + priv->assoc_request.assoc_tsf_lsw = 0; + } else { + if (unlikely(roaming)) + priv->assoc_request.assoc_type = HC_REASSOCIATE; + else + priv->assoc_request.assoc_type = HC_ASSOCIATE; + priv->assoc_request.assoc_tsf_msw = cpu_to_le32(network->time_stamp[1]); + priv->assoc_request.assoc_tsf_lsw = cpu_to_le32(network->time_stamp[0]); + } + + memcpy(priv->assoc_request.bssid, network->bssid, ETH_ALEN); + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) { + eth_broadcast_addr(priv->assoc_request.dest); + priv->assoc_request.atim_window = cpu_to_le16(network->atim_window); + } else { + memcpy(priv->assoc_request.dest, network->bssid, ETH_ALEN); + priv->assoc_request.atim_window = 0; + } + + priv->assoc_request.listen_interval = cpu_to_le16(network->listen_interval); + + err = ipw_send_ssid(priv, priv->essid, priv->essid_len); + if (err) { + IPW_DEBUG_HC("Attempt to send SSID command failed.\n"); + return err; + } + + rates->ieee_mode = priv->assoc_request.ieee_mode; + rates->purpose = IPW_RATE_CONNECT; + ipw_send_supported_rates(priv, rates); + + if (priv->assoc_request.ieee_mode == IPW_G_MODE) + priv->sys_config.dot11g_auto_detection = 1; + else + priv->sys_config.dot11g_auto_detection = 0; + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) + priv->sys_config.answer_broadcast_ssid_probe = 1; + else + priv->sys_config.answer_broadcast_ssid_probe = 0; + + err = ipw_send_system_config(priv); + if (err) { + IPW_DEBUG_HC("Attempt to send sys config command failed.\n"); + return err; + } + + IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi); + err = ipw_set_sensitivity(priv, network->stats.rssi + IPW_RSSI_TO_DBM); + if (err) { + IPW_DEBUG_HC("Attempt to send associate command failed.\n"); + return err; + } + + /* + * If preemption is enabled, it is possible for the association + * to complete before we return from ipw_send_associate. Therefore + * we have to be sure and update our priviate data first. + */ + priv->channel = network->channel; + memcpy(priv->bssid, network->bssid, ETH_ALEN); + priv->status |= STATUS_ASSOCIATING; + priv->status &= ~STATUS_SECURITY_UPDATED; + + priv->assoc_network = network; + +#ifdef CONFIG_IPW2200_QOS + ipw_qos_association(priv, network); +#endif + + err = ipw_send_associate(priv, &priv->assoc_request); + if (err) { + IPW_DEBUG_HC("Attempt to send associate command failed.\n"); + return err; + } + + IPW_DEBUG(IPW_DL_STATE, "associating: '%*pE' %pM\n", + priv->essid_len, priv->essid, priv->bssid); + + return 0; +} + +static void ipw_roam(void *data) +{ + struct ipw_priv *priv = data; + struct libipw_network *network = NULL; + struct ipw_network_match match = { + .network = priv->assoc_network + }; + + /* The roaming process is as follows: + * + * 1. Missed beacon threshold triggers the roaming process by + * setting the status ROAM bit and requesting a scan. + * 2. When the scan completes, it schedules the ROAM work + * 3. The ROAM work looks at all of the known networks for one that + * is a better network than the currently associated. If none + * found, the ROAM process is over (ROAM bit cleared) + * 4. If a better network is found, a disassociation request is + * sent. + * 5. When the disassociation completes, the roam work is again + * scheduled. The second time through, the driver is no longer + * associated, and the newly selected network is sent an + * association request. + * 6. At this point ,the roaming process is complete and the ROAM + * status bit is cleared. + */ + + /* If we are no longer associated, and the roaming bit is no longer + * set, then we are not actively roaming, so just return */ + if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING))) + return; + + if (priv->status & STATUS_ASSOCIATED) { + /* First pass through ROAM process -- look for a better + * network */ + unsigned long flags; + u8 rssi = priv->assoc_network->stats.rssi; + priv->assoc_network->stats.rssi = -128; + spin_lock_irqsave(&priv->ieee->lock, flags); + list_for_each_entry(network, &priv->ieee->network_list, list) { + if (network != priv->assoc_network) + ipw_best_network(priv, &match, network, 1); + } + spin_unlock_irqrestore(&priv->ieee->lock, flags); + priv->assoc_network->stats.rssi = rssi; + + if (match.network == priv->assoc_network) { + IPW_DEBUG_ASSOC("No better APs in this network to " + "roam to.\n"); + priv->status &= ~STATUS_ROAMING; + ipw_debug_config(priv); + return; + } + + ipw_send_disassociate(priv, 1); + priv->assoc_network = match.network; + + return; + } + + /* Second pass through ROAM process -- request association */ + ipw_compatible_rates(priv, priv->assoc_network, &match.rates); + ipw_associate_network(priv, priv->assoc_network, &match.rates, 1); + priv->status &= ~STATUS_ROAMING; +} + +static void ipw_bg_roam(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, roam); + mutex_lock(&priv->mutex); + ipw_roam(priv); + mutex_unlock(&priv->mutex); +} + +static int ipw_associate(void *data) +{ + struct ipw_priv *priv = data; + + struct libipw_network *network = NULL; + struct ipw_network_match match = { + .network = NULL + }; + struct ipw_supported_rates *rates; + struct list_head *element; + unsigned long flags; + + if (priv->ieee->iw_mode == IW_MODE_MONITOR) { + IPW_DEBUG_ASSOC("Not attempting association (monitor mode)\n"); + return 0; + } + + if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { + IPW_DEBUG_ASSOC("Not attempting association (already in " + "progress)\n"); + return 0; + } + + if (priv->status & STATUS_DISASSOCIATING) { + IPW_DEBUG_ASSOC("Not attempting association (in " + "disassociating)\n "); + schedule_work(&priv->associate); + return 0; + } + + if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) { + IPW_DEBUG_ASSOC("Not attempting association (scanning or not " + "initialized)\n"); + return 0; + } + + if (!(priv->config & CFG_ASSOCIATE) && + !(priv->config & (CFG_STATIC_ESSID | CFG_STATIC_BSSID))) { + IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n"); + return 0; + } + + /* Protect our use of the network_list */ + spin_lock_irqsave(&priv->ieee->lock, flags); + list_for_each_entry(network, &priv->ieee->network_list, list) + ipw_best_network(priv, &match, network, 0); + + network = match.network; + rates = &match.rates; + + if (network == NULL && + priv->ieee->iw_mode == IW_MODE_ADHOC && + priv->config & CFG_ADHOC_CREATE && + priv->config & CFG_STATIC_ESSID && + priv->config & CFG_STATIC_CHANNEL) { + /* Use oldest network if the free list is empty */ + if (list_empty(&priv->ieee->network_free_list)) { + struct libipw_network *oldest = NULL; + struct libipw_network *target; + + list_for_each_entry(target, &priv->ieee->network_list, list) { + if ((oldest == NULL) || + (target->last_scanned < oldest->last_scanned)) + oldest = target; + } + + /* If there are no more slots, expire the oldest */ + list_del(&oldest->list); + target = oldest; + IPW_DEBUG_ASSOC("Expired '%*pE' (%pM) from network list.\n", + target->ssid_len, target->ssid, + target->bssid); + list_add_tail(&target->list, + &priv->ieee->network_free_list); + } + + element = priv->ieee->network_free_list.next; + network = list_entry(element, struct libipw_network, list); + ipw_adhoc_create(priv, network); + rates = &priv->rates; + list_del(element); + list_add_tail(&network->list, &priv->ieee->network_list); + } + spin_unlock_irqrestore(&priv->ieee->lock, flags); + + /* If we reached the end of the list, then we don't have any valid + * matching APs */ + if (!network) { + ipw_debug_config(priv); + + if (!(priv->status & STATUS_SCANNING)) { + if (!(priv->config & CFG_SPEED_SCAN)) + schedule_delayed_work(&priv->request_scan, + SCAN_INTERVAL); + else + schedule_delayed_work(&priv->request_scan, 0); + } + + return 0; + } + + ipw_associate_network(priv, network, rates, 0); + + return 1; +} + +static void ipw_bg_associate(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, associate); + mutex_lock(&priv->mutex); + ipw_associate(priv); + mutex_unlock(&priv->mutex); +} + +static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + u16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = le16_to_cpu(hdr->frame_control); + if (!(fc & IEEE80211_FCTL_PROTECTED)) + return; + + fc &= ~IEEE80211_FCTL_PROTECTED; + hdr->frame_control = cpu_to_le16(fc); + switch (priv->ieee->sec.level) { + case SEC_LEVEL_3: + /* Remove CCMP HDR */ + memmove(skb->data + LIBIPW_3ADDR_LEN, + skb->data + LIBIPW_3ADDR_LEN + 8, + skb->len - LIBIPW_3ADDR_LEN - 8); + skb_trim(skb, skb->len - 16); /* CCMP_HDR_LEN + CCMP_MIC_LEN */ + break; + case SEC_LEVEL_2: + break; + case SEC_LEVEL_1: + /* Remove IV */ + memmove(skb->data + LIBIPW_3ADDR_LEN, + skb->data + LIBIPW_3ADDR_LEN + 4, + skb->len - LIBIPW_3ADDR_LEN - 4); + skb_trim(skb, skb->len - 8); /* IV + ICV */ + break; + case SEC_LEVEL_0: + break; + default: + printk(KERN_ERR "Unknown security level %d\n", + priv->ieee->sec.level); + break; + } +} + +static void ipw_handle_data_packet(struct ipw_priv *priv, + struct ipw_rx_mem_buffer *rxb, + struct libipw_rx_stats *stats) +{ + struct net_device *dev = priv->net_dev; + struct libipw_hdr_4addr *hdr; + struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; + + /* We received data from the HW, so stop the watchdog */ + dev->trans_start = jiffies; + + /* We only process data packets if the + * interface is open */ + if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) > + skb_tailroom(rxb->skb))) { + dev->stats.rx_errors++; + priv->wstats.discard.misc++; + IPW_DEBUG_DROP("Corruption detected! Oh no!\n"); + return; + } else if (unlikely(!netif_running(priv->net_dev))) { + dev->stats.rx_dropped++; + priv->wstats.discard.misc++; + IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); + return; + } + + /* Advance skb->data to the start of the actual payload */ + skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data)); + + /* Set the size of the skb to the size of the frame */ + skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length)); + + IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len); + + /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */ + hdr = (struct libipw_hdr_4addr *)rxb->skb->data; + if (priv->ieee->iw_mode != IW_MODE_MONITOR && + (is_multicast_ether_addr(hdr->addr1) ? + !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt)) + ipw_rebuild_decrypted_skb(priv, rxb->skb); + + if (!libipw_rx(priv->ieee, rxb->skb, stats)) + dev->stats.rx_errors++; + else { /* libipw_rx succeeded, so it now owns the SKB */ + rxb->skb = NULL; + __ipw_led_activity_on(priv); + } +} + +#ifdef CONFIG_IPW2200_RADIOTAP +static void ipw_handle_data_packet_monitor(struct ipw_priv *priv, + struct ipw_rx_mem_buffer *rxb, + struct libipw_rx_stats *stats) +{ + struct net_device *dev = priv->net_dev; + struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; + struct ipw_rx_frame *frame = &pkt->u.frame; + + /* initial pull of some data */ + u16 received_channel = frame->received_channel; + u8 antennaAndPhy = frame->antennaAndPhy; + s8 antsignal = frame->rssi_dbm - IPW_RSSI_TO_DBM; /* call it signed anyhow */ + u16 pktrate = frame->rate; + + /* Magic struct that slots into the radiotap header -- no reason + * to build this manually element by element, we can write it much + * more efficiently than we can parse it. ORDER MATTERS HERE */ + struct ipw_rt_hdr *ipw_rt; + + unsigned short len = le16_to_cpu(pkt->u.frame.length); + + /* We received data from the HW, so stop the watchdog */ + dev->trans_start = jiffies; + + /* We only process data packets if the + * interface is open */ + if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) > + skb_tailroom(rxb->skb))) { + dev->stats.rx_errors++; + priv->wstats.discard.misc++; + IPW_DEBUG_DROP("Corruption detected! Oh no!\n"); + return; + } else if (unlikely(!netif_running(priv->net_dev))) { + dev->stats.rx_dropped++; + priv->wstats.discard.misc++; + IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); + return; + } + + /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use + * that now */ + if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) { + /* FIXME: Should alloc bigger skb instead */ + dev->stats.rx_dropped++; + priv->wstats.discard.misc++; + IPW_DEBUG_DROP("Dropping too large packet in monitor\n"); + return; + } + + /* copy the frame itself */ + memmove(rxb->skb->data + sizeof(struct ipw_rt_hdr), + rxb->skb->data + IPW_RX_FRAME_SIZE, len); + + ipw_rt = (struct ipw_rt_hdr *)rxb->skb->data; + + ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; + ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */ + ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total header+data */ + + /* Big bitfield of all the fields we provide in radiotap */ + ipw_rt->rt_hdr.it_present = cpu_to_le32( + (1 << IEEE80211_RADIOTAP_TSFT) | + (1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_RATE) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | + (1 << IEEE80211_RADIOTAP_ANTENNA)); + + /* Zero the flags, we'll add to them as we go */ + ipw_rt->rt_flags = 0; + ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 | + frame->parent_tsf[2] << 16 | + frame->parent_tsf[1] << 8 | + frame->parent_tsf[0]); + + /* Convert signal to DBM */ + ipw_rt->rt_dbmsignal = antsignal; + ipw_rt->rt_dbmnoise = (s8) le16_to_cpu(frame->noise); + + /* Convert the channel data and set the flags */ + ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel)); + if (received_channel > 14) { /* 802.11a */ + ipw_rt->rt_chbitmask = + cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ)); + } else if (antennaAndPhy & 32) { /* 802.11b */ + ipw_rt->rt_chbitmask = + cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ)); + } else { /* 802.11g */ + ipw_rt->rt_chbitmask = + cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ); + } + + /* set the rate in multiples of 500k/s */ + switch (pktrate) { + case IPW_TX_RATE_1MB: + ipw_rt->rt_rate = 2; + break; + case IPW_TX_RATE_2MB: + ipw_rt->rt_rate = 4; + break; + case IPW_TX_RATE_5MB: + ipw_rt->rt_rate = 10; + break; + case IPW_TX_RATE_6MB: + ipw_rt->rt_rate = 12; + break; + case IPW_TX_RATE_9MB: + ipw_rt->rt_rate = 18; + break; + case IPW_TX_RATE_11MB: + ipw_rt->rt_rate = 22; + break; + case IPW_TX_RATE_12MB: + ipw_rt->rt_rate = 24; + break; + case IPW_TX_RATE_18MB: + ipw_rt->rt_rate = 36; + break; + case IPW_TX_RATE_24MB: + ipw_rt->rt_rate = 48; + break; + case IPW_TX_RATE_36MB: + ipw_rt->rt_rate = 72; + break; + case IPW_TX_RATE_48MB: + ipw_rt->rt_rate = 96; + break; + case IPW_TX_RATE_54MB: + ipw_rt->rt_rate = 108; + break; + default: + ipw_rt->rt_rate = 0; + break; + } + + /* antenna number */ + ipw_rt->rt_antenna = (antennaAndPhy & 3); /* Is this right? */ + + /* set the preamble flag if we have it */ + if ((antennaAndPhy & 64)) + ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + + /* Set the size of the skb to the size of the frame */ + skb_put(rxb->skb, len + sizeof(struct ipw_rt_hdr)); + + IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len); + + if (!libipw_rx(priv->ieee, rxb->skb, stats)) + dev->stats.rx_errors++; + else { /* libipw_rx succeeded, so it now owns the SKB */ + rxb->skb = NULL; + /* no LED during capture */ + } +} +#endif + +#ifdef CONFIG_IPW2200_PROMISCUOUS +#define libipw_is_probe_response(fc) \ + ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && \ + (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP ) + +#define libipw_is_management(fc) \ + ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) + +#define libipw_is_control(fc) \ + ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) + +#define libipw_is_data(fc) \ + ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) + +#define libipw_is_assoc_request(fc) \ + ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ) + +#define libipw_is_reassoc_request(fc) \ + ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ) + +static void ipw_handle_promiscuous_rx(struct ipw_priv *priv, + struct ipw_rx_mem_buffer *rxb, + struct libipw_rx_stats *stats) +{ + struct net_device *dev = priv->prom_net_dev; + struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; + struct ipw_rx_frame *frame = &pkt->u.frame; + struct ipw_rt_hdr *ipw_rt; + + /* First cache any information we need before we overwrite + * the information provided in the skb from the hardware */ + struct ieee80211_hdr *hdr; + u16 channel = frame->received_channel; + u8 phy_flags = frame->antennaAndPhy; + s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM; + s8 noise = (s8) le16_to_cpu(frame->noise); + u8 rate = frame->rate; + unsigned short len = le16_to_cpu(pkt->u.frame.length); + struct sk_buff *skb; + int hdr_only = 0; + u16 filter = priv->prom_priv->filter; + + /* If the filter is set to not include Rx frames then return */ + if (filter & IPW_PROM_NO_RX) + return; + + /* We received data from the HW, so stop the watchdog */ + dev->trans_start = jiffies; + + if (unlikely((len + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) { + dev->stats.rx_errors++; + IPW_DEBUG_DROP("Corruption detected! Oh no!\n"); + return; + } + + /* We only process data packets if the interface is open */ + if (unlikely(!netif_running(dev))) { + dev->stats.rx_dropped++; + IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); + return; + } + + /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use + * that now */ + if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) { + /* FIXME: Should alloc bigger skb instead */ + dev->stats.rx_dropped++; + IPW_DEBUG_DROP("Dropping too large packet in monitor\n"); + return; + } + + hdr = (void *)rxb->skb->data + IPW_RX_FRAME_SIZE; + if (libipw_is_management(le16_to_cpu(hdr->frame_control))) { + if (filter & IPW_PROM_NO_MGMT) + return; + if (filter & IPW_PROM_MGMT_HEADER_ONLY) + hdr_only = 1; + } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) { + if (filter & IPW_PROM_NO_CTL) + return; + if (filter & IPW_PROM_CTL_HEADER_ONLY) + hdr_only = 1; + } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) { + if (filter & IPW_PROM_NO_DATA) + return; + if (filter & IPW_PROM_DATA_HEADER_ONLY) + hdr_only = 1; + } + + /* Copy the SKB since this is for the promiscuous side */ + skb = skb_copy(rxb->skb, GFP_ATOMIC); + if (skb == NULL) { + IPW_ERROR("skb_clone failed for promiscuous copy.\n"); + return; + } + + /* copy the frame data to write after where the radiotap header goes */ + ipw_rt = (void *)skb->data; + + if (hdr_only) + len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control)); + + memcpy(ipw_rt->payload, hdr, len); + + ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; + ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */ + ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(*ipw_rt)); /* total header+data */ + + /* Set the size of the skb to the size of the frame */ + skb_put(skb, sizeof(*ipw_rt) + len); + + /* Big bitfield of all the fields we provide in radiotap */ + ipw_rt->rt_hdr.it_present = cpu_to_le32( + (1 << IEEE80211_RADIOTAP_TSFT) | + (1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_RATE) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | + (1 << IEEE80211_RADIOTAP_ANTENNA)); + + /* Zero the flags, we'll add to them as we go */ + ipw_rt->rt_flags = 0; + ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 | + frame->parent_tsf[2] << 16 | + frame->parent_tsf[1] << 8 | + frame->parent_tsf[0]); + + /* Convert to DBM */ + ipw_rt->rt_dbmsignal = signal; + ipw_rt->rt_dbmnoise = noise; + + /* Convert the channel data and set the flags */ + ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(channel)); + if (channel > 14) { /* 802.11a */ + ipw_rt->rt_chbitmask = + cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ)); + } else if (phy_flags & (1 << 5)) { /* 802.11b */ + ipw_rt->rt_chbitmask = + cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ)); + } else { /* 802.11g */ + ipw_rt->rt_chbitmask = + cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ); + } + + /* set the rate in multiples of 500k/s */ + switch (rate) { + case IPW_TX_RATE_1MB: + ipw_rt->rt_rate = 2; + break; + case IPW_TX_RATE_2MB: + ipw_rt->rt_rate = 4; + break; + case IPW_TX_RATE_5MB: + ipw_rt->rt_rate = 10; + break; + case IPW_TX_RATE_6MB: + ipw_rt->rt_rate = 12; + break; + case IPW_TX_RATE_9MB: + ipw_rt->rt_rate = 18; + break; + case IPW_TX_RATE_11MB: + ipw_rt->rt_rate = 22; + break; + case IPW_TX_RATE_12MB: + ipw_rt->rt_rate = 24; + break; + case IPW_TX_RATE_18MB: + ipw_rt->rt_rate = 36; + break; + case IPW_TX_RATE_24MB: + ipw_rt->rt_rate = 48; + break; + case IPW_TX_RATE_36MB: + ipw_rt->rt_rate = 72; + break; + case IPW_TX_RATE_48MB: + ipw_rt->rt_rate = 96; + break; + case IPW_TX_RATE_54MB: + ipw_rt->rt_rate = 108; + break; + default: + ipw_rt->rt_rate = 0; + break; + } + + /* antenna number */ + ipw_rt->rt_antenna = (phy_flags & 3); + + /* set the preamble flag if we have it */ + if (phy_flags & (1 << 6)) + ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + + IPW_DEBUG_RX("Rx packet of %d bytes.\n", skb->len); + + if (!libipw_rx(priv->prom_priv->ieee, skb, stats)) { + dev->stats.rx_errors++; + dev_kfree_skb_any(skb); + } +} +#endif + +static int is_network_packet(struct ipw_priv *priv, + struct libipw_hdr_4addr *header) +{ + /* Filter incoming packets to determine if they are targeted toward + * this network, discarding packets coming from ourselves */ + switch (priv->ieee->iw_mode) { + case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */ + /* packets from our adapter are dropped (echo) */ + if (ether_addr_equal(header->addr2, priv->net_dev->dev_addr)) + return 0; + + /* {broad,multi}cast packets to our BSSID go through */ + if (is_multicast_ether_addr(header->addr1)) + return ether_addr_equal(header->addr3, priv->bssid); + + /* packets to our adapter go through */ + return ether_addr_equal(header->addr1, + priv->net_dev->dev_addr); + + case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */ + /* packets from our adapter are dropped (echo) */ + if (ether_addr_equal(header->addr3, priv->net_dev->dev_addr)) + return 0; + + /* {broad,multi}cast packets to our BSS go through */ + if (is_multicast_ether_addr(header->addr1)) + return ether_addr_equal(header->addr2, priv->bssid); + + /* packets to our adapter go through */ + return ether_addr_equal(header->addr1, + priv->net_dev->dev_addr); + } + + return 1; +} + +#define IPW_PACKET_RETRY_TIME HZ + +static int is_duplicate_packet(struct ipw_priv *priv, + struct libipw_hdr_4addr *header) +{ + u16 sc = le16_to_cpu(header->seq_ctl); + u16 seq = WLAN_GET_SEQ_SEQ(sc); + u16 frag = WLAN_GET_SEQ_FRAG(sc); + u16 *last_seq, *last_frag; + unsigned long *last_time; + + switch (priv->ieee->iw_mode) { + case IW_MODE_ADHOC: + { + struct list_head *p; + struct ipw_ibss_seq *entry = NULL; + u8 *mac = header->addr2; + int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE; + + list_for_each(p, &priv->ibss_mac_hash[index]) { + entry = + list_entry(p, struct ipw_ibss_seq, list); + if (ether_addr_equal(entry->mac, mac)) + break; + } + if (p == &priv->ibss_mac_hash[index]) { + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) { + IPW_ERROR + ("Cannot malloc new mac entry\n"); + return 0; + } + memcpy(entry->mac, mac, ETH_ALEN); + entry->seq_num = seq; + entry->frag_num = frag; + entry->packet_time = jiffies; + list_add(&entry->list, + &priv->ibss_mac_hash[index]); + return 0; + } + last_seq = &entry->seq_num; + last_frag = &entry->frag_num; + last_time = &entry->packet_time; + break; + } + case IW_MODE_INFRA: + last_seq = &priv->last_seq_num; + last_frag = &priv->last_frag_num; + last_time = &priv->last_packet_time; + break; + default: + return 0; + } + if ((*last_seq == seq) && + time_after(*last_time + IPW_PACKET_RETRY_TIME, jiffies)) { + if (*last_frag == frag) + goto drop; + if (*last_frag + 1 != frag) + /* out-of-order fragment */ + goto drop; + } else + *last_seq = seq; + + *last_frag = frag; + *last_time = jiffies; + return 0; + + drop: + /* Comment this line now since we observed the card receives + * duplicate packets but the FCTL_RETRY bit is not set in the + * IBSS mode with fragmentation enabled. + BUG_ON(!(le16_to_cpu(header->frame_control) & IEEE80211_FCTL_RETRY)); */ + return 1; +} + +static void ipw_handle_mgmt_packet(struct ipw_priv *priv, + struct ipw_rx_mem_buffer *rxb, + struct libipw_rx_stats *stats) +{ + struct sk_buff *skb = rxb->skb; + struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data; + struct libipw_hdr_4addr *header = (struct libipw_hdr_4addr *) + (skb->data + IPW_RX_FRAME_SIZE); + + libipw_rx_mgt(priv->ieee, header, stats); + + if (priv->ieee->iw_mode == IW_MODE_ADHOC && + ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) == + IEEE80211_STYPE_PROBE_RESP) || + (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) == + IEEE80211_STYPE_BEACON))) { + if (ether_addr_equal(header->addr3, priv->bssid)) + ipw_add_station(priv, header->addr2); + } + + if (priv->config & CFG_NET_STATS) { + IPW_DEBUG_HC("sending stat packet\n"); + + /* Set the size of the skb to the size of the full + * ipw header and 802.11 frame */ + skb_put(skb, le16_to_cpu(pkt->u.frame.length) + + IPW_RX_FRAME_SIZE); + + /* Advance past the ipw packet header to the 802.11 frame */ + skb_pull(skb, IPW_RX_FRAME_SIZE); + + /* Push the libipw_rx_stats before the 802.11 frame */ + memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats)); + + skb->dev = priv->ieee->dev; + + /* Point raw at the libipw_stats */ + skb_reset_mac_header(skb); + + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = cpu_to_be16(ETH_P_80211_STATS); + memset(skb->cb, 0, sizeof(rxb->skb->cb)); + netif_rx(skb); + rxb->skb = NULL; + } +} + +/* + * Main entry function for receiving a packet with 80211 headers. This + * should be called when ever the FW has notified us that there is a new + * skb in the receive queue. + */ +static void ipw_rx(struct ipw_priv *priv) +{ + struct ipw_rx_mem_buffer *rxb; + struct ipw_rx_packet *pkt; + struct libipw_hdr_4addr *header; + u32 r, w, i; + u8 network_packet; + u8 fill_rx = 0; + + r = ipw_read32(priv, IPW_RX_READ_INDEX); + w = ipw_read32(priv, IPW_RX_WRITE_INDEX); + i = priv->rxq->read; + + if (ipw_rx_queue_space (priv->rxq) > (RX_QUEUE_SIZE / 2)) + fill_rx = 1; + + while (i != r) { + rxb = priv->rxq->queue[i]; + if (unlikely(rxb == NULL)) { + printk(KERN_CRIT "Queue not allocated!\n"); + break; + } + priv->rxq->queue[i] = NULL; + + pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr, + IPW_RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + + pkt = (struct ipw_rx_packet *)rxb->skb->data; + IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n", + pkt->header.message_type, + pkt->header.rx_seq_num, pkt->header.control_bits); + + switch (pkt->header.message_type) { + case RX_FRAME_TYPE: /* 802.11 frame */ { + struct libipw_rx_stats stats = { + .rssi = pkt->u.frame.rssi_dbm - + IPW_RSSI_TO_DBM, + .signal = + pkt->u.frame.rssi_dbm - + IPW_RSSI_TO_DBM + 0x100, + .noise = + le16_to_cpu(pkt->u.frame.noise), + .rate = pkt->u.frame.rate, + .mac_time = jiffies, + .received_channel = + pkt->u.frame.received_channel, + .freq = + (pkt->u.frame. + control & (1 << 0)) ? + LIBIPW_24GHZ_BAND : + LIBIPW_52GHZ_BAND, + .len = le16_to_cpu(pkt->u.frame.length), + }; + + if (stats.rssi != 0) + stats.mask |= LIBIPW_STATMASK_RSSI; + if (stats.signal != 0) + stats.mask |= LIBIPW_STATMASK_SIGNAL; + if (stats.noise != 0) + stats.mask |= LIBIPW_STATMASK_NOISE; + if (stats.rate != 0) + stats.mask |= LIBIPW_STATMASK_RATE; + + priv->rx_packets++; + +#ifdef CONFIG_IPW2200_PROMISCUOUS + if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) + ipw_handle_promiscuous_rx(priv, rxb, &stats); +#endif + +#ifdef CONFIG_IPW2200_MONITOR + if (priv->ieee->iw_mode == IW_MODE_MONITOR) { +#ifdef CONFIG_IPW2200_RADIOTAP + + ipw_handle_data_packet_monitor(priv, + rxb, + &stats); +#else + ipw_handle_data_packet(priv, rxb, + &stats); +#endif + break; + } +#endif + + header = + (struct libipw_hdr_4addr *)(rxb->skb-> + data + + IPW_RX_FRAME_SIZE); + /* TODO: Check Ad-Hoc dest/source and make sure + * that we are actually parsing these packets + * correctly -- we should probably use the + * frame control of the packet and disregard + * the current iw_mode */ + + network_packet = + is_network_packet(priv, header); + if (network_packet && priv->assoc_network) { + priv->assoc_network->stats.rssi = + stats.rssi; + priv->exp_avg_rssi = + exponential_average(priv->exp_avg_rssi, + stats.rssi, DEPTH_RSSI); + } + + IPW_DEBUG_RX("Frame: len=%u\n", + le16_to_cpu(pkt->u.frame.length)); + + if (le16_to_cpu(pkt->u.frame.length) < + libipw_get_hdrlen(le16_to_cpu( + header->frame_ctl))) { + IPW_DEBUG_DROP + ("Received packet is too small. " + "Dropping.\n"); + priv->net_dev->stats.rx_errors++; + priv->wstats.discard.misc++; + break; + } + + switch (WLAN_FC_GET_TYPE + (le16_to_cpu(header->frame_ctl))) { + + case IEEE80211_FTYPE_MGMT: + ipw_handle_mgmt_packet(priv, rxb, + &stats); + break; + + case IEEE80211_FTYPE_CTL: + break; + + case IEEE80211_FTYPE_DATA: + if (unlikely(!network_packet || + is_duplicate_packet(priv, + header))) + { + IPW_DEBUG_DROP("Dropping: " + "%pM, " + "%pM, " + "%pM\n", + header->addr1, + header->addr2, + header->addr3); + break; + } + + ipw_handle_data_packet(priv, rxb, + &stats); + + break; + } + break; + } + + case RX_HOST_NOTIFICATION_TYPE:{ + IPW_DEBUG_RX + ("Notification: subtype=%02X flags=%02X size=%d\n", + pkt->u.notification.subtype, + pkt->u.notification.flags, + le16_to_cpu(pkt->u.notification.size)); + ipw_rx_notification(priv, &pkt->u.notification); + break; + } + + default: + IPW_DEBUG_RX("Bad Rx packet of type %d\n", + pkt->header.message_type); + break; + } + + /* For now we just don't re-use anything. We can tweak this + * later to try and re-use notification packets and SKBs that + * fail to Rx correctly */ + if (rxb->skb != NULL) { + dev_kfree_skb_any(rxb->skb); + rxb->skb = NULL; + } + + pci_unmap_single(priv->pci_dev, rxb->dma_addr, + IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + list_add_tail(&rxb->list, &priv->rxq->rx_used); + + i = (i + 1) % RX_QUEUE_SIZE; + + /* If there are a lot of unsued frames, restock the Rx queue + * so the ucode won't assert */ + if (fill_rx) { + priv->rxq->read = i; + ipw_rx_queue_replenish(priv); + } + } + + /* Backtrack one entry */ + priv->rxq->read = i; + ipw_rx_queue_restock(priv); +} + +#define DEFAULT_RTS_THRESHOLD 2304U +#define MIN_RTS_THRESHOLD 1U +#define MAX_RTS_THRESHOLD 2304U +#define DEFAULT_BEACON_INTERVAL 100U +#define DEFAULT_SHORT_RETRY_LIMIT 7U +#define DEFAULT_LONG_RETRY_LIMIT 4U + +/** + * ipw_sw_reset + * @option: options to control different reset behaviour + * 0 = reset everything except the 'disable' module_param + * 1 = reset everything and print out driver info (for probe only) + * 2 = reset everything + */ +static int ipw_sw_reset(struct ipw_priv *priv, int option) +{ + int band, modulation; + int old_mode = priv->ieee->iw_mode; + + /* Initialize module parameter values here */ + priv->config = 0; + + /* We default to disabling the LED code as right now it causes + * too many systems to lock up... */ + if (!led_support) + priv->config |= CFG_NO_LED; + + if (associate) + priv->config |= CFG_ASSOCIATE; + else + IPW_DEBUG_INFO("Auto associate disabled.\n"); + + if (auto_create) + priv->config |= CFG_ADHOC_CREATE; + else + IPW_DEBUG_INFO("Auto adhoc creation disabled.\n"); + + priv->config &= ~CFG_STATIC_ESSID; + priv->essid_len = 0; + memset(priv->essid, 0, IW_ESSID_MAX_SIZE); + + if (disable && option) { + priv->status |= STATUS_RF_KILL_SW; + IPW_DEBUG_INFO("Radio disabled.\n"); + } + + if (default_channel != 0) { + priv->config |= CFG_STATIC_CHANNEL; + priv->channel = default_channel; + IPW_DEBUG_INFO("Bind to static channel %d\n", default_channel); + /* TODO: Validate that provided channel is in range */ + } +#ifdef CONFIG_IPW2200_QOS + ipw_qos_init(priv, qos_enable, qos_burst_enable, + burst_duration_CCK, burst_duration_OFDM); +#endif /* CONFIG_IPW2200_QOS */ + + switch (network_mode) { + case 1: + priv->ieee->iw_mode = IW_MODE_ADHOC; + priv->net_dev->type = ARPHRD_ETHER; + + break; +#ifdef CONFIG_IPW2200_MONITOR + case 2: + priv->ieee->iw_mode = IW_MODE_MONITOR; +#ifdef CONFIG_IPW2200_RADIOTAP + priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; +#else + priv->net_dev->type = ARPHRD_IEEE80211; +#endif + break; +#endif + default: + case 0: + priv->net_dev->type = ARPHRD_ETHER; + priv->ieee->iw_mode = IW_MODE_INFRA; + break; + } + + if (hwcrypto) { + priv->ieee->host_encrypt = 0; + priv->ieee->host_encrypt_msdu = 0; + priv->ieee->host_decrypt = 0; + priv->ieee->host_mc_decrypt = 0; + } + IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off"); + + /* IPW2200/2915 is abled to do hardware fragmentation. */ + priv->ieee->host_open_frag = 0; + + if ((priv->pci_dev->device == 0x4223) || + (priv->pci_dev->device == 0x4224)) { + if (option == 1) + printk(KERN_INFO DRV_NAME + ": Detected Intel PRO/Wireless 2915ABG Network " + "Connection\n"); + priv->ieee->abg_true = 1; + band = LIBIPW_52GHZ_BAND | LIBIPW_24GHZ_BAND; + modulation = LIBIPW_OFDM_MODULATION | + LIBIPW_CCK_MODULATION; + priv->adapter = IPW_2915ABG; + priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B; + } else { + if (option == 1) + printk(KERN_INFO DRV_NAME + ": Detected Intel PRO/Wireless 2200BG Network " + "Connection\n"); + + priv->ieee->abg_true = 0; + band = LIBIPW_24GHZ_BAND; + modulation = LIBIPW_OFDM_MODULATION | + LIBIPW_CCK_MODULATION; + priv->adapter = IPW_2200BG; + priv->ieee->mode = IEEE_G | IEEE_B; + } + + priv->ieee->freq_band = band; + priv->ieee->modulation = modulation; + + priv->rates_mask = LIBIPW_DEFAULT_RATES_MASK; + + priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT; + priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT; + + priv->rts_threshold = DEFAULT_RTS_THRESHOLD; + priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT; + priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT; + + /* If power management is turned on, default to AC mode */ + priv->power_mode = IPW_POWER_AC; + priv->tx_power = IPW_TX_POWER_DEFAULT; + + return old_mode == priv->ieee->iw_mode; +} + +/* + * This file defines the Wireless Extension handlers. It does not + * define any methods of hardware manipulation and relies on the + * functions defined in ipw_main to provide the HW interaction. + * + * The exception to this is the use of the ipw_get_ordinal() + * function used to poll the hardware vs. making unnecessary calls. + * + */ + +static int ipw_set_channel(struct ipw_priv *priv, u8 channel) +{ + if (channel == 0) { + IPW_DEBUG_INFO("Setting channel to ANY (0)\n"); + priv->config &= ~CFG_STATIC_CHANNEL; + IPW_DEBUG_ASSOC("Attempting to associate with new " + "parameters.\n"); + ipw_associate(priv); + return 0; + } + + priv->config |= CFG_STATIC_CHANNEL; + + if (priv->channel == channel) { + IPW_DEBUG_INFO("Request to set channel to current value (%d)\n", + channel); + return 0; + } + + IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel); + priv->channel = channel; + +#ifdef CONFIG_IPW2200_MONITOR + if (priv->ieee->iw_mode == IW_MODE_MONITOR) { + int i; + if (priv->status & STATUS_SCANNING) { + IPW_DEBUG_SCAN("Scan abort triggered due to " + "channel change.\n"); + ipw_abort_scan(priv); + } + + for (i = 1000; i && (priv->status & STATUS_SCANNING); i--) + udelay(10); + + if (priv->status & STATUS_SCANNING) + IPW_DEBUG_SCAN("Still scanning...\n"); + else + IPW_DEBUG_SCAN("Took %dms to abort current scan\n", + 1000 - i); + + return 0; + } +#endif /* CONFIG_IPW2200_MONITOR */ + + /* Network configuration changed -- force [re]association */ + IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n"); + if (!ipw_disassociate(priv)) + ipw_associate(priv); + + return 0; +} + +static int ipw_wx_set_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); + struct iw_freq *fwrq = &wrqu->freq; + int ret = 0, i; + u8 channel, flags; + int band; + + if (fwrq->m == 0) { + IPW_DEBUG_WX("SET Freq/Channel -> any\n"); + mutex_lock(&priv->mutex); + ret = ipw_set_channel(priv, 0); + mutex_unlock(&priv->mutex); + return ret; + } + /* if setting by freq convert to channel */ + if (fwrq->e == 1) { + channel = libipw_freq_to_channel(priv->ieee, fwrq->m); + if (channel == 0) + return -EINVAL; + } else + channel = fwrq->m; + + if (!(band = libipw_is_valid_channel(priv->ieee, channel))) + return -EINVAL; + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) { + i = libipw_channel_to_index(priv->ieee, channel); + if (i == -1) + return -EINVAL; + + flags = (band == LIBIPW_24GHZ_BAND) ? + geo->bg[i].flags : geo->a[i].flags; + if (flags & LIBIPW_CH_PASSIVE_ONLY) { + IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n"); + return -EINVAL; + } + } + + IPW_DEBUG_WX("SET Freq/Channel -> %d\n", fwrq->m); + mutex_lock(&priv->mutex); + ret = ipw_set_channel(priv, channel); + mutex_unlock(&priv->mutex); + return ret; +} + +static int ipw_wx_get_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + + wrqu->freq.e = 0; + + /* If we are associated, trying to associate, or have a statically + * configured CHANNEL then return that; otherwise return ANY */ + mutex_lock(&priv->mutex); + if (priv->config & CFG_STATIC_CHANNEL || + priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) { + int i; + + i = libipw_channel_to_index(priv->ieee, priv->channel); + BUG_ON(i == -1); + wrqu->freq.e = 1; + + switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { + case LIBIPW_52GHZ_BAND: + wrqu->freq.m = priv->ieee->geo.a[i].freq * 100000; + break; + + case LIBIPW_24GHZ_BAND: + wrqu->freq.m = priv->ieee->geo.bg[i].freq * 100000; + break; + + default: + BUG(); + } + } else + wrqu->freq.m = 0; + + mutex_unlock(&priv->mutex); + IPW_DEBUG_WX("GET Freq/Channel -> %d\n", priv->channel); + return 0; +} + +static int ipw_wx_set_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + int err = 0; + + IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode); + + switch (wrqu->mode) { +#ifdef CONFIG_IPW2200_MONITOR + case IW_MODE_MONITOR: +#endif + case IW_MODE_ADHOC: + case IW_MODE_INFRA: + break; + case IW_MODE_AUTO: + wrqu->mode = IW_MODE_INFRA; + break; + default: + return -EINVAL; + } + if (wrqu->mode == priv->ieee->iw_mode) + return 0; + + mutex_lock(&priv->mutex); + + ipw_sw_reset(priv, 0); + +#ifdef CONFIG_IPW2200_MONITOR + if (priv->ieee->iw_mode == IW_MODE_MONITOR) + priv->net_dev->type = ARPHRD_ETHER; + + if (wrqu->mode == IW_MODE_MONITOR) +#ifdef CONFIG_IPW2200_RADIOTAP + priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; +#else + priv->net_dev->type = ARPHRD_IEEE80211; +#endif +#endif /* CONFIG_IPW2200_MONITOR */ + + /* Free the existing firmware and reset the fw_loaded + * flag so ipw_load() will bring in the new firmware */ + free_firmware(); + + priv->ieee->iw_mode = wrqu->mode; + + schedule_work(&priv->adapter_restart); + mutex_unlock(&priv->mutex); + return err; +} + +static int ipw_wx_get_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + wrqu->mode = priv->ieee->iw_mode; + IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode); + mutex_unlock(&priv->mutex); + return 0; +} + +/* Values are in microsecond */ +static const s32 timeout_duration[] = { + 350000, + 250000, + 75000, + 37000, + 25000, +}; + +static const s32 period_duration[] = { + 400000, + 700000, + 1000000, + 1000000, + 1000000 +}; + +static int ipw_wx_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + struct iw_range *range = (struct iw_range *)extra; + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); + int i = 0, j; + + wrqu->data.length = sizeof(*range); + memset(range, 0, sizeof(*range)); + + /* 54Mbs == ~27 Mb/s real (802.11g) */ + range->throughput = 27 * 1000 * 1000; + + range->max_qual.qual = 100; + /* TODO: Find real max RSSI and stick here */ + range->max_qual.level = 0; + range->max_qual.noise = 0; + range->max_qual.updated = 7; /* Updated all three */ + + range->avg_qual.qual = 70; + /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ + range->avg_qual.level = 0; /* FIXME to real average level */ + range->avg_qual.noise = 0; + range->avg_qual.updated = 7; /* Updated all three */ + mutex_lock(&priv->mutex); + range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES); + + for (i = 0; i < range->num_bitrates; i++) + range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) * + 500000; + + range->max_rts = DEFAULT_RTS_THRESHOLD; + range->min_frag = MIN_FRAG_THRESHOLD; + range->max_frag = MAX_FRAG_THRESHOLD; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = WEP_KEYS; + + /* Set the Wireless Extension versions */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 18; + + i = 0; + if (priv->ieee->mode & (IEEE_B | IEEE_G)) { + for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES; j++) { + if ((priv->ieee->iw_mode == IW_MODE_ADHOC) && + (geo->bg[j].flags & LIBIPW_CH_PASSIVE_ONLY)) + continue; + + range->freq[i].i = geo->bg[j].channel; + range->freq[i].m = geo->bg[j].freq * 100000; + range->freq[i].e = 1; + i++; + } + } + + if (priv->ieee->mode & IEEE_A) { + for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES; j++) { + if ((priv->ieee->iw_mode == IW_MODE_ADHOC) && + (geo->a[j].flags & LIBIPW_CH_PASSIVE_ONLY)) + continue; + + range->freq[i].i = geo->a[j].channel; + range->freq[i].m = geo->a[j].freq * 100000; + range->freq[i].e = 1; + i++; + } + } + + range->num_channels = i; + range->num_frequency = i; + + mutex_unlock(&priv->mutex); + + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; + + range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE; + + IPW_DEBUG_WX("GET Range\n"); + return 0; +} + +static int ipw_wx_set_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + + if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) + return -EINVAL; + mutex_lock(&priv->mutex); + if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) || + is_zero_ether_addr(wrqu->ap_addr.sa_data)) { + /* we disable mandatory BSSID association */ + IPW_DEBUG_WX("Setting AP BSSID to ANY\n"); + priv->config &= ~CFG_STATIC_BSSID; + IPW_DEBUG_ASSOC("Attempting to associate with new " + "parameters.\n"); + ipw_associate(priv); + mutex_unlock(&priv->mutex); + return 0; + } + + priv->config |= CFG_STATIC_BSSID; + if (ether_addr_equal(priv->bssid, wrqu->ap_addr.sa_data)) { + IPW_DEBUG_WX("BSSID set to current BSSID.\n"); + mutex_unlock(&priv->mutex); + return 0; + } + + IPW_DEBUG_WX("Setting mandatory BSSID to %pM\n", + wrqu->ap_addr.sa_data); + + memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN); + + /* Network configuration changed -- force [re]association */ + IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n"); + if (!ipw_disassociate(priv)) + ipw_associate(priv); + + mutex_unlock(&priv->mutex); + return 0; +} + +static int ipw_wx_get_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + + /* If we are associated, trying to associate, or have a statically + * configured BSSID then return that; otherwise return ANY */ + mutex_lock(&priv->mutex); + if (priv->config & CFG_STATIC_BSSID || + priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { + wrqu->ap_addr.sa_family = ARPHRD_ETHER; + memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN); + } else + eth_zero_addr(wrqu->ap_addr.sa_data); + + IPW_DEBUG_WX("Getting WAP BSSID: %pM\n", + wrqu->ap_addr.sa_data); + mutex_unlock(&priv->mutex); + return 0; +} + +static int ipw_wx_set_essid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + int length; + + mutex_lock(&priv->mutex); + + if (!wrqu->essid.flags) + { + IPW_DEBUG_WX("Setting ESSID to ANY\n"); + ipw_disassociate(priv); + priv->config &= ~CFG_STATIC_ESSID; + ipw_associate(priv); + mutex_unlock(&priv->mutex); + return 0; + } + + length = min((int)wrqu->essid.length, IW_ESSID_MAX_SIZE); + + priv->config |= CFG_STATIC_ESSID; + + if (priv->essid_len == length && !memcmp(priv->essid, extra, length) + && (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) { + IPW_DEBUG_WX("ESSID set to current ESSID.\n"); + mutex_unlock(&priv->mutex); + return 0; + } + + IPW_DEBUG_WX("Setting ESSID: '%*pE' (%d)\n", length, extra, length); + + priv->essid_len = length; + memcpy(priv->essid, extra, priv->essid_len); + + /* Network configuration changed -- force [re]association */ + IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n"); + if (!ipw_disassociate(priv)) + ipw_associate(priv); + + mutex_unlock(&priv->mutex); + return 0; +} + +static int ipw_wx_get_essid(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + + /* If we are associated, trying to associate, or have a statically + * configured ESSID then return that; otherwise return ANY */ + mutex_lock(&priv->mutex); + if (priv->config & CFG_STATIC_ESSID || + priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { + IPW_DEBUG_WX("Getting essid: '%*pE'\n", + priv->essid_len, priv->essid); + memcpy(extra, priv->essid, priv->essid_len); + wrqu->essid.length = priv->essid_len; + wrqu->essid.flags = 1; /* active */ + } else { + IPW_DEBUG_WX("Getting essid: ANY\n"); + wrqu->essid.length = 0; + wrqu->essid.flags = 0; /* active */ + } + mutex_unlock(&priv->mutex); + return 0; +} + +static int ipw_wx_set_nick(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + + IPW_DEBUG_WX("Setting nick to '%s'\n", extra); + if (wrqu->data.length > IW_ESSID_MAX_SIZE) + return -E2BIG; + mutex_lock(&priv->mutex); + wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick)); + memset(priv->nick, 0, sizeof(priv->nick)); + memcpy(priv->nick, extra, wrqu->data.length); + IPW_DEBUG_TRACE("<<\n"); + mutex_unlock(&priv->mutex); + return 0; + +} + +static int ipw_wx_get_nick(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + IPW_DEBUG_WX("Getting nick\n"); + mutex_lock(&priv->mutex); + wrqu->data.length = strlen(priv->nick); + memcpy(extra, priv->nick, wrqu->data.length); + wrqu->data.flags = 1; /* active */ + mutex_unlock(&priv->mutex); + return 0; +} + +static int ipw_wx_set_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + int err = 0; + + IPW_DEBUG_WX("Setting roaming threshold to %d\n", wrqu->sens.value); + IPW_DEBUG_WX("Setting disassociate threshold to %d\n", 3*wrqu->sens.value); + mutex_lock(&priv->mutex); + + if (wrqu->sens.fixed == 0) + { + priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT; + priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT; + goto out; + } + if ((wrqu->sens.value > IPW_MB_ROAMING_THRESHOLD_MAX) || + (wrqu->sens.value < IPW_MB_ROAMING_THRESHOLD_MIN)) { + err = -EINVAL; + goto out; + } + + priv->roaming_threshold = wrqu->sens.value; + priv->disassociate_threshold = 3*wrqu->sens.value; + out: + mutex_unlock(&priv->mutex); + return err; +} + +static int ipw_wx_get_sens(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + wrqu->sens.fixed = 1; + wrqu->sens.value = priv->roaming_threshold; + mutex_unlock(&priv->mutex); + + IPW_DEBUG_WX("GET roaming threshold -> %s %d\n", + wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value); + + return 0; +} + +static int ipw_wx_set_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + /* TODO: We should use semaphores or locks for access to priv */ + struct ipw_priv *priv = libipw_priv(dev); + u32 target_rate = wrqu->bitrate.value; + u32 fixed, mask; + + /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */ + /* value = X, fixed = 1 means only rate X */ + /* value = X, fixed = 0 means all rates lower equal X */ + + if (target_rate == -1) { + fixed = 0; + mask = LIBIPW_DEFAULT_RATES_MASK; + /* Now we should reassociate */ + goto apply; + } + + mask = 0; + fixed = wrqu->bitrate.fixed; + + if (target_rate == 1000000 || !fixed) + mask |= LIBIPW_CCK_RATE_1MB_MASK; + if (target_rate == 1000000) + goto apply; + + if (target_rate == 2000000 || !fixed) + mask |= LIBIPW_CCK_RATE_2MB_MASK; + if (target_rate == 2000000) + goto apply; + + if (target_rate == 5500000 || !fixed) + mask |= LIBIPW_CCK_RATE_5MB_MASK; + if (target_rate == 5500000) + goto apply; + + if (target_rate == 6000000 || !fixed) + mask |= LIBIPW_OFDM_RATE_6MB_MASK; + if (target_rate == 6000000) + goto apply; + + if (target_rate == 9000000 || !fixed) + mask |= LIBIPW_OFDM_RATE_9MB_MASK; + if (target_rate == 9000000) + goto apply; + + if (target_rate == 11000000 || !fixed) + mask |= LIBIPW_CCK_RATE_11MB_MASK; + if (target_rate == 11000000) + goto apply; + + if (target_rate == 12000000 || !fixed) + mask |= LIBIPW_OFDM_RATE_12MB_MASK; + if (target_rate == 12000000) + goto apply; + + if (target_rate == 18000000 || !fixed) + mask |= LIBIPW_OFDM_RATE_18MB_MASK; + if (target_rate == 18000000) + goto apply; + + if (target_rate == 24000000 || !fixed) + mask |= LIBIPW_OFDM_RATE_24MB_MASK; + if (target_rate == 24000000) + goto apply; + + if (target_rate == 36000000 || !fixed) + mask |= LIBIPW_OFDM_RATE_36MB_MASK; + if (target_rate == 36000000) + goto apply; + + if (target_rate == 48000000 || !fixed) + mask |= LIBIPW_OFDM_RATE_48MB_MASK; + if (target_rate == 48000000) + goto apply; + + if (target_rate == 54000000 || !fixed) + mask |= LIBIPW_OFDM_RATE_54MB_MASK; + if (target_rate == 54000000) + goto apply; + + IPW_DEBUG_WX("invalid rate specified, returning error\n"); + return -EINVAL; + + apply: + IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n", + mask, fixed ? "fixed" : "sub-rates"); + mutex_lock(&priv->mutex); + if (mask == LIBIPW_DEFAULT_RATES_MASK) { + priv->config &= ~CFG_FIXED_RATE; + ipw_set_fixed_rate(priv, priv->ieee->mode); + } else + priv->config |= CFG_FIXED_RATE; + + if (priv->rates_mask == mask) { + IPW_DEBUG_WX("Mask set to current mask.\n"); + mutex_unlock(&priv->mutex); + return 0; + } + + priv->rates_mask = mask; + + /* Network configuration changed -- force [re]association */ + IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n"); + if (!ipw_disassociate(priv)) + ipw_associate(priv); + + mutex_unlock(&priv->mutex); + return 0; +} + +static int ipw_wx_get_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + wrqu->bitrate.value = priv->last_rate; + wrqu->bitrate.fixed = (priv->config & CFG_FIXED_RATE) ? 1 : 0; + mutex_unlock(&priv->mutex); + IPW_DEBUG_WX("GET Rate -> %d\n", wrqu->bitrate.value); + return 0; +} + +static int ipw_wx_set_rts(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + if (wrqu->rts.disabled || !wrqu->rts.fixed) + priv->rts_threshold = DEFAULT_RTS_THRESHOLD; + else { + if (wrqu->rts.value < MIN_RTS_THRESHOLD || + wrqu->rts.value > MAX_RTS_THRESHOLD) { + mutex_unlock(&priv->mutex); + return -EINVAL; + } + priv->rts_threshold = wrqu->rts.value; + } + + ipw_send_rts_threshold(priv, priv->rts_threshold); + mutex_unlock(&priv->mutex); + IPW_DEBUG_WX("SET RTS Threshold -> %d\n", priv->rts_threshold); + return 0; +} + +static int ipw_wx_get_rts(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + wrqu->rts.value = priv->rts_threshold; + wrqu->rts.fixed = 0; /* no auto select */ + wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD); + mutex_unlock(&priv->mutex); + IPW_DEBUG_WX("GET RTS Threshold -> %d\n", wrqu->rts.value); + return 0; +} + +static int ipw_wx_set_txpow(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + int err = 0; + + mutex_lock(&priv->mutex); + if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) { + err = -EINPROGRESS; + goto out; + } + + if (!wrqu->power.fixed) + wrqu->power.value = IPW_TX_POWER_DEFAULT; + + if (wrqu->power.flags != IW_TXPOW_DBM) { + err = -EINVAL; + goto out; + } + + if ((wrqu->power.value > IPW_TX_POWER_MAX) || + (wrqu->power.value < IPW_TX_POWER_MIN)) { + err = -EINVAL; + goto out; + } + + priv->tx_power = wrqu->power.value; + err = ipw_set_tx_power(priv); + out: + mutex_unlock(&priv->mutex); + return err; +} + +static int ipw_wx_get_txpow(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + wrqu->power.value = priv->tx_power; + wrqu->power.fixed = 1; + wrqu->power.flags = IW_TXPOW_DBM; + wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0; + mutex_unlock(&priv->mutex); + + IPW_DEBUG_WX("GET TX Power -> %s %d\n", + wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value); + + return 0; +} + +static int ipw_wx_set_frag(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + if (wrqu->frag.disabled || !wrqu->frag.fixed) + priv->ieee->fts = DEFAULT_FTS; + else { + if (wrqu->frag.value < MIN_FRAG_THRESHOLD || + wrqu->frag.value > MAX_FRAG_THRESHOLD) { + mutex_unlock(&priv->mutex); + return -EINVAL; + } + + priv->ieee->fts = wrqu->frag.value & ~0x1; + } + + ipw_send_frag_threshold(priv, wrqu->frag.value); + mutex_unlock(&priv->mutex); + IPW_DEBUG_WX("SET Frag Threshold -> %d\n", wrqu->frag.value); + return 0; +} + +static int ipw_wx_get_frag(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + wrqu->frag.value = priv->ieee->fts; + wrqu->frag.fixed = 0; /* no auto select */ + wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS); + mutex_unlock(&priv->mutex); + IPW_DEBUG_WX("GET Frag Threshold -> %d\n", wrqu->frag.value); + + return 0; +} + +static int ipw_wx_set_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + + if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled) + return -EINVAL; + + if (!(wrqu->retry.flags & IW_RETRY_LIMIT)) + return 0; + + if (wrqu->retry.value < 0 || wrqu->retry.value >= 255) + return -EINVAL; + + mutex_lock(&priv->mutex); + if (wrqu->retry.flags & IW_RETRY_SHORT) + priv->short_retry_limit = (u8) wrqu->retry.value; + else if (wrqu->retry.flags & IW_RETRY_LONG) + priv->long_retry_limit = (u8) wrqu->retry.value; + else { + priv->short_retry_limit = (u8) wrqu->retry.value; + priv->long_retry_limit = (u8) wrqu->retry.value; + } + + ipw_send_retry_limit(priv, priv->short_retry_limit, + priv->long_retry_limit); + mutex_unlock(&priv->mutex); + IPW_DEBUG_WX("SET retry limit -> short:%d long:%d\n", + priv->short_retry_limit, priv->long_retry_limit); + return 0; +} + +static int ipw_wx_get_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + + mutex_lock(&priv->mutex); + wrqu->retry.disabled = 0; + + if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { + mutex_unlock(&priv->mutex); + return -EINVAL; + } + + if (wrqu->retry.flags & IW_RETRY_LONG) { + wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG; + wrqu->retry.value = priv->long_retry_limit; + } else if (wrqu->retry.flags & IW_RETRY_SHORT) { + wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT; + wrqu->retry.value = priv->short_retry_limit; + } else { + wrqu->retry.flags = IW_RETRY_LIMIT; + wrqu->retry.value = priv->short_retry_limit; + } + mutex_unlock(&priv->mutex); + + IPW_DEBUG_WX("GET retry -> %d\n", wrqu->retry.value); + + return 0; +} + +static int ipw_wx_set_scan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + struct iw_scan_req *req = (struct iw_scan_req *)extra; + struct delayed_work *work = NULL; + + mutex_lock(&priv->mutex); + + priv->user_requested_scan = 1; + + if (wrqu->data.length == sizeof(struct iw_scan_req)) { + if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { + int len = min((int)req->essid_len, + (int)sizeof(priv->direct_scan_ssid)); + memcpy(priv->direct_scan_ssid, req->essid, len); + priv->direct_scan_ssid_len = len; + work = &priv->request_direct_scan; + } else if (req->scan_type == IW_SCAN_TYPE_PASSIVE) { + work = &priv->request_passive_scan; + } + } else { + /* Normal active broadcast scan */ + work = &priv->request_scan; + } + + mutex_unlock(&priv->mutex); + + IPW_DEBUG_WX("Start scan\n"); + + schedule_delayed_work(work, 0); + + return 0; +} + +static int ipw_wx_get_scan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + return libipw_wx_get_scan(priv->ieee, info, wrqu, extra); +} + +static int ipw_wx_set_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key) +{ + struct ipw_priv *priv = libipw_priv(dev); + int ret; + u32 cap = priv->capability; + + mutex_lock(&priv->mutex); + ret = libipw_wx_set_encode(priv->ieee, info, wrqu, key); + + /* In IBSS mode, we need to notify the firmware to update + * the beacon info after we changed the capability. */ + if (cap != priv->capability && + priv->ieee->iw_mode == IW_MODE_ADHOC && + priv->status & STATUS_ASSOCIATED) + ipw_disassociate(priv); + + mutex_unlock(&priv->mutex); + return ret; +} + +static int ipw_wx_get_encode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key) +{ + struct ipw_priv *priv = libipw_priv(dev); + return libipw_wx_get_encode(priv->ieee, info, wrqu, key); +} + +static int ipw_wx_set_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + int err; + mutex_lock(&priv->mutex); + if (wrqu->power.disabled) { + priv->power_mode = IPW_POWER_LEVEL(priv->power_mode); + err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM); + if (err) { + IPW_DEBUG_WX("failed setting power mode.\n"); + mutex_unlock(&priv->mutex); + return err; + } + IPW_DEBUG_WX("SET Power Management Mode -> off\n"); + mutex_unlock(&priv->mutex); + return 0; + } + + switch (wrqu->power.flags & IW_POWER_MODE) { + case IW_POWER_ON: /* If not specified */ + case IW_POWER_MODE: /* If set all mask */ + case IW_POWER_ALL_R: /* If explicitly state all */ + break; + default: /* Otherwise we don't support it */ + IPW_DEBUG_WX("SET PM Mode: %X not supported.\n", + wrqu->power.flags); + mutex_unlock(&priv->mutex); + return -EOPNOTSUPP; + } + + /* If the user hasn't specified a power management mode yet, default + * to BATTERY */ + if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC) + priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY; + else + priv->power_mode = IPW_POWER_ENABLED | priv->power_mode; + + err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode)); + if (err) { + IPW_DEBUG_WX("failed setting power mode.\n"); + mutex_unlock(&priv->mutex); + return err; + } + + IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode); + mutex_unlock(&priv->mutex); + return 0; +} + +static int ipw_wx_get_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + if (!(priv->power_mode & IPW_POWER_ENABLED)) + wrqu->power.disabled = 1; + else + wrqu->power.disabled = 0; + + mutex_unlock(&priv->mutex); + IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode); + + return 0; +} + +static int ipw_wx_set_powermode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + int mode = *(int *)extra; + int err; + + mutex_lock(&priv->mutex); + if ((mode < 1) || (mode > IPW_POWER_LIMIT)) + mode = IPW_POWER_AC; + + if (IPW_POWER_LEVEL(priv->power_mode) != mode) { + err = ipw_send_power_mode(priv, mode); + if (err) { + IPW_DEBUG_WX("failed setting power mode.\n"); + mutex_unlock(&priv->mutex); + return err; + } + priv->power_mode = IPW_POWER_ENABLED | mode; + } + mutex_unlock(&priv->mutex); + return 0; +} + +#define MAX_WX_STRING 80 +static int ipw_wx_get_powermode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + int level = IPW_POWER_LEVEL(priv->power_mode); + char *p = extra; + + p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level); + + switch (level) { + case IPW_POWER_AC: + p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)"); + break; + case IPW_POWER_BATTERY: + p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)"); + break; + default: + p += snprintf(p, MAX_WX_STRING - (p - extra), + "(Timeout %dms, Period %dms)", + timeout_duration[level - 1] / 1000, + period_duration[level - 1] / 1000); + } + + if (!(priv->power_mode & IPW_POWER_ENABLED)) + p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF"); + + wrqu->data.length = p - extra + 1; + + return 0; +} + +static int ipw_wx_set_wireless_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + int mode = *(int *)extra; + u8 band = 0, modulation = 0; + + if (mode == 0 || mode & ~IEEE_MODE_MASK) { + IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode); + return -EINVAL; + } + mutex_lock(&priv->mutex); + if (priv->adapter == IPW_2915ABG) { + priv->ieee->abg_true = 1; + if (mode & IEEE_A) { + band |= LIBIPW_52GHZ_BAND; + modulation |= LIBIPW_OFDM_MODULATION; + } else + priv->ieee->abg_true = 0; + } else { + if (mode & IEEE_A) { + IPW_WARNING("Attempt to set 2200BG into " + "802.11a mode\n"); + mutex_unlock(&priv->mutex); + return -EINVAL; + } + + priv->ieee->abg_true = 0; + } + + if (mode & IEEE_B) { + band |= LIBIPW_24GHZ_BAND; + modulation |= LIBIPW_CCK_MODULATION; + } else + priv->ieee->abg_true = 0; + + if (mode & IEEE_G) { + band |= LIBIPW_24GHZ_BAND; + modulation |= LIBIPW_OFDM_MODULATION; + } else + priv->ieee->abg_true = 0; + + priv->ieee->mode = mode; + priv->ieee->freq_band = band; + priv->ieee->modulation = modulation; + init_supported_rates(priv, &priv->rates); + + /* Network configuration changed -- force [re]association */ + IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n"); + if (!ipw_disassociate(priv)) { + ipw_send_supported_rates(priv, &priv->rates); + ipw_associate(priv); + } + + /* Update the band LEDs */ + ipw_led_band_on(priv); + + IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n", + mode & IEEE_A ? 'a' : '.', + mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.'); + mutex_unlock(&priv->mutex); + return 0; +} + +static int ipw_wx_get_wireless_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + switch (priv->ieee->mode) { + case IEEE_A: + strncpy(extra, "802.11a (1)", MAX_WX_STRING); + break; + case IEEE_B: + strncpy(extra, "802.11b (2)", MAX_WX_STRING); + break; + case IEEE_A | IEEE_B: + strncpy(extra, "802.11ab (3)", MAX_WX_STRING); + break; + case IEEE_G: + strncpy(extra, "802.11g (4)", MAX_WX_STRING); + break; + case IEEE_A | IEEE_G: + strncpy(extra, "802.11ag (5)", MAX_WX_STRING); + break; + case IEEE_B | IEEE_G: + strncpy(extra, "802.11bg (6)", MAX_WX_STRING); + break; + case IEEE_A | IEEE_B | IEEE_G: + strncpy(extra, "802.11abg (7)", MAX_WX_STRING); + break; + default: + strncpy(extra, "unknown", MAX_WX_STRING); + break; + } + extra[MAX_WX_STRING - 1] = '\0'; + + IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra); + + wrqu->data.length = strlen(extra) + 1; + mutex_unlock(&priv->mutex); + + return 0; +} + +static int ipw_wx_set_preamble(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + int mode = *(int *)extra; + mutex_lock(&priv->mutex); + /* Switching from SHORT -> LONG requires a disassociation */ + if (mode == 1) { + if (!(priv->config & CFG_PREAMBLE_LONG)) { + priv->config |= CFG_PREAMBLE_LONG; + + /* Network configuration changed -- force [re]association */ + IPW_DEBUG_ASSOC + ("[re]association triggered due to preamble change.\n"); + if (!ipw_disassociate(priv)) + ipw_associate(priv); + } + goto done; + } + + if (mode == 0) { + priv->config &= ~CFG_PREAMBLE_LONG; + goto done; + } + mutex_unlock(&priv->mutex); + return -EINVAL; + + done: + mutex_unlock(&priv->mutex); + return 0; +} + +static int ipw_wx_get_preamble(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + mutex_lock(&priv->mutex); + if (priv->config & CFG_PREAMBLE_LONG) + snprintf(wrqu->name, IFNAMSIZ, "long (1)"); + else + snprintf(wrqu->name, IFNAMSIZ, "auto (0)"); + mutex_unlock(&priv->mutex); + return 0; +} + +#ifdef CONFIG_IPW2200_MONITOR +static int ipw_wx_set_monitor(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + int *parms = (int *)extra; + int enable = (parms[0] > 0); + mutex_lock(&priv->mutex); + IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]); + if (enable) { + if (priv->ieee->iw_mode != IW_MODE_MONITOR) { +#ifdef CONFIG_IPW2200_RADIOTAP + priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; +#else + priv->net_dev->type = ARPHRD_IEEE80211; +#endif + schedule_work(&priv->adapter_restart); + } + + ipw_set_channel(priv, parms[1]); + } else { + if (priv->ieee->iw_mode != IW_MODE_MONITOR) { + mutex_unlock(&priv->mutex); + return 0; + } + priv->net_dev->type = ARPHRD_ETHER; + schedule_work(&priv->adapter_restart); + } + mutex_unlock(&priv->mutex); + return 0; +} + +#endif /* CONFIG_IPW2200_MONITOR */ + +static int ipw_wx_reset(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + IPW_DEBUG_WX("RESET\n"); + schedule_work(&priv->adapter_restart); + return 0; +} + +static int ipw_wx_sw_reset(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ipw_priv *priv = libipw_priv(dev); + union iwreq_data wrqu_sec = { + .encoding = { + .flags = IW_ENCODE_DISABLED, + }, + }; + int ret; + + IPW_DEBUG_WX("SW_RESET\n"); + + mutex_lock(&priv->mutex); + + ret = ipw_sw_reset(priv, 2); + if (!ret) { + free_firmware(); + ipw_adapter_restart(priv); + } + + /* The SW reset bit might have been toggled on by the 'disable' + * module parameter, so take appropriate action */ + ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW); + + mutex_unlock(&priv->mutex); + libipw_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL); + mutex_lock(&priv->mutex); + + if (!(priv->status & STATUS_RF_KILL_MASK)) { + /* Configuration likely changed -- force [re]association */ + IPW_DEBUG_ASSOC("[re]association triggered due to sw " + "reset.\n"); + if (!ipw_disassociate(priv)) + ipw_associate(priv); + } + + mutex_unlock(&priv->mutex); + + return 0; +} + +/* Rebase the WE IOCTLs to zero for the handler array */ +static iw_handler ipw_wx_handlers[] = { + IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), + IW_HANDLER(SIOCSIWFREQ, ipw_wx_set_freq), + IW_HANDLER(SIOCGIWFREQ, ipw_wx_get_freq), + IW_HANDLER(SIOCSIWMODE, ipw_wx_set_mode), + IW_HANDLER(SIOCGIWMODE, ipw_wx_get_mode), + IW_HANDLER(SIOCSIWSENS, ipw_wx_set_sens), + IW_HANDLER(SIOCGIWSENS, ipw_wx_get_sens), + IW_HANDLER(SIOCGIWRANGE, ipw_wx_get_range), + IW_HANDLER(SIOCSIWAP, ipw_wx_set_wap), + IW_HANDLER(SIOCGIWAP, ipw_wx_get_wap), + IW_HANDLER(SIOCSIWSCAN, ipw_wx_set_scan), + IW_HANDLER(SIOCGIWSCAN, ipw_wx_get_scan), + IW_HANDLER(SIOCSIWESSID, ipw_wx_set_essid), + IW_HANDLER(SIOCGIWESSID, ipw_wx_get_essid), + IW_HANDLER(SIOCSIWNICKN, ipw_wx_set_nick), + IW_HANDLER(SIOCGIWNICKN, ipw_wx_get_nick), + IW_HANDLER(SIOCSIWRATE, ipw_wx_set_rate), + IW_HANDLER(SIOCGIWRATE, ipw_wx_get_rate), + IW_HANDLER(SIOCSIWRTS, ipw_wx_set_rts), + IW_HANDLER(SIOCGIWRTS, ipw_wx_get_rts), + IW_HANDLER(SIOCSIWFRAG, ipw_wx_set_frag), + IW_HANDLER(SIOCGIWFRAG, ipw_wx_get_frag), + IW_HANDLER(SIOCSIWTXPOW, ipw_wx_set_txpow), + IW_HANDLER(SIOCGIWTXPOW, ipw_wx_get_txpow), + IW_HANDLER(SIOCSIWRETRY, ipw_wx_set_retry), + IW_HANDLER(SIOCGIWRETRY, ipw_wx_get_retry), + IW_HANDLER(SIOCSIWENCODE, ipw_wx_set_encode), + IW_HANDLER(SIOCGIWENCODE, ipw_wx_get_encode), + IW_HANDLER(SIOCSIWPOWER, ipw_wx_set_power), + IW_HANDLER(SIOCGIWPOWER, ipw_wx_get_power), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWGENIE, ipw_wx_set_genie), + IW_HANDLER(SIOCGIWGENIE, ipw_wx_get_genie), + IW_HANDLER(SIOCSIWMLME, ipw_wx_set_mlme), + IW_HANDLER(SIOCSIWAUTH, ipw_wx_set_auth), + IW_HANDLER(SIOCGIWAUTH, ipw_wx_get_auth), + IW_HANDLER(SIOCSIWENCODEEXT, ipw_wx_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, ipw_wx_get_encodeext), +}; + +enum { + IPW_PRIV_SET_POWER = SIOCIWFIRSTPRIV, + IPW_PRIV_GET_POWER, + IPW_PRIV_SET_MODE, + IPW_PRIV_GET_MODE, + IPW_PRIV_SET_PREAMBLE, + IPW_PRIV_GET_PREAMBLE, + IPW_PRIV_RESET, + IPW_PRIV_SW_RESET, +#ifdef CONFIG_IPW2200_MONITOR + IPW_PRIV_SET_MONITOR, +#endif +}; + +static struct iw_priv_args ipw_priv_args[] = { + { + .cmd = IPW_PRIV_SET_POWER, + .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + .name = "set_power"}, + { + .cmd = IPW_PRIV_GET_POWER, + .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + .name = "get_power"}, + { + .cmd = IPW_PRIV_SET_MODE, + .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + .name = "set_mode"}, + { + .cmd = IPW_PRIV_GET_MODE, + .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + .name = "get_mode"}, + { + .cmd = IPW_PRIV_SET_PREAMBLE, + .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + .name = "set_preamble"}, + { + .cmd = IPW_PRIV_GET_PREAMBLE, + .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, + .name = "get_preamble"}, + { + IPW_PRIV_RESET, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"}, + { + IPW_PRIV_SW_RESET, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "sw_reset"}, +#ifdef CONFIG_IPW2200_MONITOR + { + IPW_PRIV_SET_MONITOR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"}, +#endif /* CONFIG_IPW2200_MONITOR */ +}; + +static iw_handler ipw_priv_handler[] = { + ipw_wx_set_powermode, + ipw_wx_get_powermode, + ipw_wx_set_wireless_mode, + ipw_wx_get_wireless_mode, + ipw_wx_set_preamble, + ipw_wx_get_preamble, + ipw_wx_reset, + ipw_wx_sw_reset, +#ifdef CONFIG_IPW2200_MONITOR + ipw_wx_set_monitor, +#endif +}; + +static struct iw_handler_def ipw_wx_handler_def = { + .standard = ipw_wx_handlers, + .num_standard = ARRAY_SIZE(ipw_wx_handlers), + .num_private = ARRAY_SIZE(ipw_priv_handler), + .num_private_args = ARRAY_SIZE(ipw_priv_args), + .private = ipw_priv_handler, + .private_args = ipw_priv_args, + .get_wireless_stats = ipw_get_wireless_stats, +}; + +/* + * Get wireless statistics. + * Called by /proc/net/wireless + * Also called by SIOCGIWSTATS + */ +static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev) +{ + struct ipw_priv *priv = libipw_priv(dev); + struct iw_statistics *wstats; + + wstats = &priv->wstats; + + /* if hw is disabled, then ipw_get_ordinal() can't be called. + * netdev->get_wireless_stats seems to be called before fw is + * initialized. STATUS_ASSOCIATED will only be set if the hw is up + * and associated; if not associcated, the values are all meaningless + * anyway, so set them all to NULL and INVALID */ + if (!(priv->status & STATUS_ASSOCIATED)) { + wstats->miss.beacon = 0; + wstats->discard.retries = 0; + wstats->qual.qual = 0; + wstats->qual.level = 0; + wstats->qual.noise = 0; + wstats->qual.updated = 7; + wstats->qual.updated |= IW_QUAL_NOISE_INVALID | + IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; + return wstats; + } + + wstats->qual.qual = priv->quality; + wstats->qual.level = priv->exp_avg_rssi; + wstats->qual.noise = priv->exp_avg_noise; + wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | + IW_QUAL_NOISE_UPDATED | IW_QUAL_DBM; + + wstats->miss.beacon = average_value(&priv->average_missed_beacons); + wstats->discard.retries = priv->last_tx_failures; + wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable; + +/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len)) + goto fail_get_ordinal; + wstats->discard.retries += tx_retry; */ + + return wstats; +} + +/* net device stuff */ + +static void init_sys_config(struct ipw_sys_config *sys_config) +{ + memset(sys_config, 0, sizeof(struct ipw_sys_config)); + sys_config->bt_coexistence = 0; + sys_config->answer_broadcast_ssid_probe = 0; + sys_config->accept_all_data_frames = 0; + sys_config->accept_non_directed_frames = 1; + sys_config->exclude_unicast_unencrypted = 0; + sys_config->disable_unicast_decryption = 1; + sys_config->exclude_multicast_unencrypted = 0; + sys_config->disable_multicast_decryption = 1; + if (antenna < CFG_SYS_ANTENNA_BOTH || antenna > CFG_SYS_ANTENNA_B) + antenna = CFG_SYS_ANTENNA_BOTH; + sys_config->antenna_diversity = antenna; + sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */ + sys_config->dot11g_auto_detection = 0; + sys_config->enable_cts_to_self = 0; + sys_config->bt_coexist_collision_thr = 0; + sys_config->pass_noise_stats_to_host = 1; /* 1 -- fix for 256 */ + sys_config->silence_threshold = 0x1e; +} + +static int ipw_net_open(struct net_device *dev) +{ + IPW_DEBUG_INFO("dev->open\n"); + netif_start_queue(dev); + return 0; +} + +static int ipw_net_stop(struct net_device *dev) +{ + IPW_DEBUG_INFO("dev->close\n"); + netif_stop_queue(dev); + return 0; +} + +/* +todo: + +modify to send one tfd per fragment instead of using chunking. otherwise +we need to heavily modify the libipw_skb_to_txb. +*/ + +static int ipw_tx_skb(struct ipw_priv *priv, struct libipw_txb *txb, + int pri) +{ + struct libipw_hdr_3addrqos *hdr = (struct libipw_hdr_3addrqos *) + txb->fragments[0]->data; + int i = 0; + struct tfd_frame *tfd; +#ifdef CONFIG_IPW2200_QOS + int tx_id = ipw_get_tx_queue_number(priv, pri); + struct clx2_tx_queue *txq = &priv->txq[tx_id]; +#else + struct clx2_tx_queue *txq = &priv->txq[0]; +#endif + struct clx2_queue *q = &txq->q; + u8 id, hdr_len, unicast; + int fc; + + if (!(priv->status & STATUS_ASSOCIATED)) + goto drop; + + hdr_len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + switch (priv->ieee->iw_mode) { + case IW_MODE_ADHOC: + unicast = !is_multicast_ether_addr(hdr->addr1); + id = ipw_find_station(priv, hdr->addr1); + if (id == IPW_INVALID_STATION) { + id = ipw_add_station(priv, hdr->addr1); + if (id == IPW_INVALID_STATION) { + IPW_WARNING("Attempt to send data to " + "invalid cell: %pM\n", + hdr->addr1); + goto drop; + } + } + break; + + case IW_MODE_INFRA: + default: + unicast = !is_multicast_ether_addr(hdr->addr3); + id = 0; + break; + } + + tfd = &txq->bd[q->first_empty]; + txq->txb[q->first_empty] = txb; + memset(tfd, 0, sizeof(*tfd)); + tfd->u.data.station_number = id; + + tfd->control_flags.message_type = TX_FRAME_TYPE; + tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK; + + tfd->u.data.cmd_id = DINO_CMD_TX; + tfd->u.data.len = cpu_to_le16(txb->payload_size); + + if (priv->assoc_request.ieee_mode == IPW_B_MODE) + tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK; + else + tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_OFDM; + + if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE) + tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE; + + fc = le16_to_cpu(hdr->frame_ctl); + hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS); + + memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len); + + if (likely(unicast)) + tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD; + + if (txb->encrypted && !priv->ieee->host_encrypt) { + switch (priv->ieee->sec.level) { + case SEC_LEVEL_3: + tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |= + cpu_to_le16(IEEE80211_FCTL_PROTECTED); + /* XXX: ACK flag must be set for CCMP even if it + * is a multicast/broadcast packet, because CCMP + * group communication encrypted by GTK is + * actually done by the AP. */ + if (!unicast) + tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD; + + tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP; + tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_CCM; + tfd->u.data.key_index = 0; + tfd->u.data.key_index |= DCT_WEP_INDEX_USE_IMMEDIATE; + break; + case SEC_LEVEL_2: + tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |= + cpu_to_le16(IEEE80211_FCTL_PROTECTED); + tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP; + tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_TKIP; + tfd->u.data.key_index = DCT_WEP_INDEX_USE_IMMEDIATE; + break; + case SEC_LEVEL_1: + tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |= + cpu_to_le16(IEEE80211_FCTL_PROTECTED); + tfd->u.data.key_index = priv->ieee->crypt_info.tx_keyidx; + if (priv->ieee->sec.key_sizes[priv->ieee->crypt_info.tx_keyidx] <= + 40) + tfd->u.data.key_index |= DCT_WEP_KEY_64Bit; + else + tfd->u.data.key_index |= DCT_WEP_KEY_128Bit; + break; + case SEC_LEVEL_0: + break; + default: + printk(KERN_ERR "Unknown security level %d\n", + priv->ieee->sec.level); + break; + } + } else + /* No hardware encryption */ + tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP; + +#ifdef CONFIG_IPW2200_QOS + if (fc & IEEE80211_STYPE_QOS_DATA) + ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data)); +#endif /* CONFIG_IPW2200_QOS */ + + /* payload */ + tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2), + txb->nr_frags)); + IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n", + txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks)); + for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) { + IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n", + i, le32_to_cpu(tfd->u.data.num_chunks), + txb->fragments[i]->len - hdr_len); + IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n", + i, tfd->u.data.num_chunks, + txb->fragments[i]->len - hdr_len); + printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len, + txb->fragments[i]->len - hdr_len); + + tfd->u.data.chunk_ptr[i] = + cpu_to_le32(pci_map_single + (priv->pci_dev, + txb->fragments[i]->data + hdr_len, + txb->fragments[i]->len - hdr_len, + PCI_DMA_TODEVICE)); + tfd->u.data.chunk_len[i] = + cpu_to_le16(txb->fragments[i]->len - hdr_len); + } + + if (i != txb->nr_frags) { + struct sk_buff *skb; + u16 remaining_bytes = 0; + int j; + + for (j = i; j < txb->nr_frags; j++) + remaining_bytes += txb->fragments[j]->len - hdr_len; + + printk(KERN_INFO "Trying to reallocate for %d bytes\n", + remaining_bytes); + skb = alloc_skb(remaining_bytes, GFP_ATOMIC); + if (skb != NULL) { + tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes); + for (j = i; j < txb->nr_frags; j++) { + int size = txb->fragments[j]->len - hdr_len; + + printk(KERN_INFO "Adding frag %d %d...\n", + j, size); + memcpy(skb_put(skb, size), + txb->fragments[j]->data + hdr_len, size); + } + dev_kfree_skb_any(txb->fragments[i]); + txb->fragments[i] = skb; + tfd->u.data.chunk_ptr[i] = + cpu_to_le32(pci_map_single + (priv->pci_dev, skb->data, + remaining_bytes, + PCI_DMA_TODEVICE)); + + le32_add_cpu(&tfd->u.data.num_chunks, 1); + } + } + + /* kick DMA */ + q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd); + ipw_write32(priv, q->reg_w, q->first_empty); + + if (ipw_tx_queue_space(q) < q->high_mark) + netif_stop_queue(priv->net_dev); + + return NETDEV_TX_OK; + + drop: + IPW_DEBUG_DROP("Silently dropping Tx packet.\n"); + libipw_txb_free(txb); + return NETDEV_TX_OK; +} + +static int ipw_net_is_queue_full(struct net_device *dev, int pri) +{ + struct ipw_priv *priv = libipw_priv(dev); +#ifdef CONFIG_IPW2200_QOS + int tx_id = ipw_get_tx_queue_number(priv, pri); + struct clx2_tx_queue *txq = &priv->txq[tx_id]; +#else + struct clx2_tx_queue *txq = &priv->txq[0]; +#endif /* CONFIG_IPW2200_QOS */ + + if (ipw_tx_queue_space(&txq->q) < txq->q.high_mark) + return 1; + + return 0; +} + +#ifdef CONFIG_IPW2200_PROMISCUOUS +static void ipw_handle_promiscuous_tx(struct ipw_priv *priv, + struct libipw_txb *txb) +{ + struct libipw_rx_stats dummystats; + struct ieee80211_hdr *hdr; + u8 n; + u16 filter = priv->prom_priv->filter; + int hdr_only = 0; + + if (filter & IPW_PROM_NO_TX) + return; + + memset(&dummystats, 0, sizeof(dummystats)); + + /* Filtering of fragment chains is done against the first fragment */ + hdr = (void *)txb->fragments[0]->data; + if (libipw_is_management(le16_to_cpu(hdr->frame_control))) { + if (filter & IPW_PROM_NO_MGMT) + return; + if (filter & IPW_PROM_MGMT_HEADER_ONLY) + hdr_only = 1; + } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) { + if (filter & IPW_PROM_NO_CTL) + return; + if (filter & IPW_PROM_CTL_HEADER_ONLY) + hdr_only = 1; + } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) { + if (filter & IPW_PROM_NO_DATA) + return; + if (filter & IPW_PROM_DATA_HEADER_ONLY) + hdr_only = 1; + } + + for(n=0; nnr_frags; ++n) { + struct sk_buff *src = txb->fragments[n]; + struct sk_buff *dst; + struct ieee80211_radiotap_header *rt_hdr; + int len; + + if (hdr_only) { + hdr = (void *)src->data; + len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control)); + } else + len = src->len; + + dst = alloc_skb(len + sizeof(*rt_hdr) + sizeof(u16)*2, GFP_ATOMIC); + if (!dst) + continue; + + rt_hdr = (void *)skb_put(dst, sizeof(*rt_hdr)); + + rt_hdr->it_version = PKTHDR_RADIOTAP_VERSION; + rt_hdr->it_pad = 0; + rt_hdr->it_present = 0; /* after all, it's just an idea */ + rt_hdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL); + + *(__le16*)skb_put(dst, sizeof(u16)) = cpu_to_le16( + ieee80211chan2mhz(priv->channel)); + if (priv->channel > 14) /* 802.11a */ + *(__le16*)skb_put(dst, sizeof(u16)) = + cpu_to_le16(IEEE80211_CHAN_OFDM | + IEEE80211_CHAN_5GHZ); + else if (priv->ieee->mode == IEEE_B) /* 802.11b */ + *(__le16*)skb_put(dst, sizeof(u16)) = + cpu_to_le16(IEEE80211_CHAN_CCK | + IEEE80211_CHAN_2GHZ); + else /* 802.11g */ + *(__le16*)skb_put(dst, sizeof(u16)) = + cpu_to_le16(IEEE80211_CHAN_OFDM | + IEEE80211_CHAN_2GHZ); + + rt_hdr->it_len = cpu_to_le16(dst->len); + + skb_copy_from_linear_data(src, skb_put(dst, len), len); + + if (!libipw_rx(priv->prom_priv->ieee, dst, &dummystats)) + dev_kfree_skb_any(dst); + } +} +#endif + +static netdev_tx_t ipw_net_hard_start_xmit(struct libipw_txb *txb, + struct net_device *dev, int pri) +{ + struct ipw_priv *priv = libipw_priv(dev); + unsigned long flags; + netdev_tx_t ret; + + IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size); + spin_lock_irqsave(&priv->lock, flags); + +#ifdef CONFIG_IPW2200_PROMISCUOUS + if (rtap_iface && netif_running(priv->prom_net_dev)) + ipw_handle_promiscuous_tx(priv, txb); +#endif + + ret = ipw_tx_skb(priv, txb, pri); + if (ret == NETDEV_TX_OK) + __ipw_led_activity_on(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static void ipw_net_set_multicast_list(struct net_device *dev) +{ + +} + +static int ipw_net_set_mac_address(struct net_device *dev, void *p) +{ + struct ipw_priv *priv = libipw_priv(dev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + mutex_lock(&priv->mutex); + priv->config |= CFG_CUSTOM_MAC; + memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN); + printk(KERN_INFO "%s: Setting MAC to %pM\n", + priv->net_dev->name, priv->mac_addr); + schedule_work(&priv->adapter_restart); + mutex_unlock(&priv->mutex); + return 0; +} + +static void ipw_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct ipw_priv *p = libipw_priv(dev); + char vers[64]; + char date[32]; + u32 len; + + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + + len = sizeof(vers); + ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len); + len = sizeof(date); + ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len); + + snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)", + vers, date); + strlcpy(info->bus_info, pci_name(p->pci_dev), + sizeof(info->bus_info)); +} + +static u32 ipw_ethtool_get_link(struct net_device *dev) +{ + struct ipw_priv *priv = libipw_priv(dev); + return (priv->status & STATUS_ASSOCIATED) != 0; +} + +static int ipw_ethtool_get_eeprom_len(struct net_device *dev) +{ + return IPW_EEPROM_IMAGE_SIZE; +} + +static int ipw_ethtool_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 * bytes) +{ + struct ipw_priv *p = libipw_priv(dev); + + if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE) + return -EINVAL; + mutex_lock(&p->mutex); + memcpy(bytes, &p->eeprom[eeprom->offset], eeprom->len); + mutex_unlock(&p->mutex); + return 0; +} + +static int ipw_ethtool_set_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 * bytes) +{ + struct ipw_priv *p = libipw_priv(dev); + int i; + + if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE) + return -EINVAL; + mutex_lock(&p->mutex); + memcpy(&p->eeprom[eeprom->offset], bytes, eeprom->len); + for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++) + ipw_write8(p, i + IPW_EEPROM_DATA, p->eeprom[i]); + mutex_unlock(&p->mutex); + return 0; +} + +static const struct ethtool_ops ipw_ethtool_ops = { + .get_link = ipw_ethtool_get_link, + .get_drvinfo = ipw_ethtool_get_drvinfo, + .get_eeprom_len = ipw_ethtool_get_eeprom_len, + .get_eeprom = ipw_ethtool_get_eeprom, + .set_eeprom = ipw_ethtool_set_eeprom, +}; + +static irqreturn_t ipw_isr(int irq, void *data) +{ + struct ipw_priv *priv = data; + u32 inta, inta_mask; + + if (!priv) + return IRQ_NONE; + + spin_lock(&priv->irq_lock); + + if (!(priv->status & STATUS_INT_ENABLED)) { + /* IRQ is disabled */ + goto none; + } + + inta = ipw_read32(priv, IPW_INTA_RW); + inta_mask = ipw_read32(priv, IPW_INTA_MASK_R); + + if (inta == 0xFFFFFFFF) { + /* Hardware disappeared */ + IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n"); + goto none; + } + + if (!(inta & (IPW_INTA_MASK_ALL & inta_mask))) { + /* Shared interrupt */ + goto none; + } + + /* tell the device to stop sending interrupts */ + __ipw_disable_interrupts(priv); + + /* ack current interrupts */ + inta &= (IPW_INTA_MASK_ALL & inta_mask); + ipw_write32(priv, IPW_INTA_RW, inta); + + /* Cache INTA value for our tasklet */ + priv->isr_inta = inta; + + tasklet_schedule(&priv->irq_tasklet); + + spin_unlock(&priv->irq_lock); + + return IRQ_HANDLED; + none: + spin_unlock(&priv->irq_lock); + return IRQ_NONE; +} + +static void ipw_rf_kill(void *adapter) +{ + struct ipw_priv *priv = adapter; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + if (rf_kill_active(priv)) { + IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n"); + schedule_delayed_work(&priv->rf_kill, 2 * HZ); + goto exit_unlock; + } + + /* RF Kill is now disabled, so bring the device back up */ + + if (!(priv->status & STATUS_RF_KILL_MASK)) { + IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting " + "device\n"); + + /* we can not do an adapter restart while inside an irq lock */ + schedule_work(&priv->adapter_restart); + } else + IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still " + "enabled\n"); + + exit_unlock: + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void ipw_bg_rf_kill(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, rf_kill.work); + mutex_lock(&priv->mutex); + ipw_rf_kill(priv); + mutex_unlock(&priv->mutex); +} + +static void ipw_link_up(struct ipw_priv *priv) +{ + priv->last_seq_num = -1; + priv->last_frag_num = -1; + priv->last_packet_time = 0; + + netif_carrier_on(priv->net_dev); + + cancel_delayed_work(&priv->request_scan); + cancel_delayed_work(&priv->request_direct_scan); + cancel_delayed_work(&priv->request_passive_scan); + cancel_delayed_work(&priv->scan_event); + ipw_reset_stats(priv); + /* Ensure the rate is updated immediately */ + priv->last_rate = ipw_get_current_rate(priv); + ipw_gather_stats(priv); + ipw_led_link_up(priv); + notify_wx_assoc_event(priv); + + if (priv->config & CFG_BACKGROUND_SCAN) + schedule_delayed_work(&priv->request_scan, HZ); +} + +static void ipw_bg_link_up(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, link_up); + mutex_lock(&priv->mutex); + ipw_link_up(priv); + mutex_unlock(&priv->mutex); +} + +static void ipw_link_down(struct ipw_priv *priv) +{ + ipw_led_link_down(priv); + netif_carrier_off(priv->net_dev); + notify_wx_assoc_event(priv); + + /* Cancel any queued work ... */ + cancel_delayed_work(&priv->request_scan); + cancel_delayed_work(&priv->request_direct_scan); + cancel_delayed_work(&priv->request_passive_scan); + cancel_delayed_work(&priv->adhoc_check); + cancel_delayed_work(&priv->gather_stats); + + ipw_reset_stats(priv); + + if (!(priv->status & STATUS_EXIT_PENDING)) { + /* Queue up another scan... */ + schedule_delayed_work(&priv->request_scan, 0); + } else + cancel_delayed_work(&priv->scan_event); +} + +static void ipw_bg_link_down(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, link_down); + mutex_lock(&priv->mutex); + ipw_link_down(priv); + mutex_unlock(&priv->mutex); +} + +static int ipw_setup_deferred_work(struct ipw_priv *priv) +{ + int ret = 0; + + init_waitqueue_head(&priv->wait_command_queue); + init_waitqueue_head(&priv->wait_state); + + INIT_DELAYED_WORK(&priv->adhoc_check, ipw_bg_adhoc_check); + INIT_WORK(&priv->associate, ipw_bg_associate); + INIT_WORK(&priv->disassociate, ipw_bg_disassociate); + INIT_WORK(&priv->system_config, ipw_system_config); + INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish); + INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart); + INIT_DELAYED_WORK(&priv->rf_kill, ipw_bg_rf_kill); + INIT_WORK(&priv->up, ipw_bg_up); + INIT_WORK(&priv->down, ipw_bg_down); + INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan); + INIT_DELAYED_WORK(&priv->request_direct_scan, ipw_request_direct_scan); + INIT_DELAYED_WORK(&priv->request_passive_scan, ipw_request_passive_scan); + INIT_DELAYED_WORK(&priv->scan_event, ipw_scan_event); + INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats); + INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan); + INIT_WORK(&priv->roam, ipw_bg_roam); + INIT_DELAYED_WORK(&priv->scan_check, ipw_bg_scan_check); + INIT_WORK(&priv->link_up, ipw_bg_link_up); + INIT_WORK(&priv->link_down, ipw_bg_link_down); + INIT_DELAYED_WORK(&priv->led_link_on, ipw_bg_led_link_on); + INIT_DELAYED_WORK(&priv->led_link_off, ipw_bg_led_link_off); + INIT_DELAYED_WORK(&priv->led_act_off, ipw_bg_led_activity_off); + INIT_WORK(&priv->merge_networks, ipw_merge_adhoc_network); + +#ifdef CONFIG_IPW2200_QOS + INIT_WORK(&priv->qos_activate, ipw_bg_qos_activate); +#endif /* CONFIG_IPW2200_QOS */ + + tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) + ipw_irq_tasklet, (unsigned long)priv); + + return ret; +} + +static void shim__set_security(struct net_device *dev, + struct libipw_security *sec) +{ + struct ipw_priv *priv = libipw_priv(dev); + int i; + for (i = 0; i < 4; i++) { + if (sec->flags & (1 << i)) { + priv->ieee->sec.encode_alg[i] = sec->encode_alg[i]; + priv->ieee->sec.key_sizes[i] = sec->key_sizes[i]; + if (sec->key_sizes[i] == 0) + priv->ieee->sec.flags &= ~(1 << i); + else { + memcpy(priv->ieee->sec.keys[i], sec->keys[i], + sec->key_sizes[i]); + priv->ieee->sec.flags |= (1 << i); + } + priv->status |= STATUS_SECURITY_UPDATED; + } else if (sec->level != SEC_LEVEL_1) + priv->ieee->sec.flags &= ~(1 << i); + } + + if (sec->flags & SEC_ACTIVE_KEY) { + if (sec->active_key <= 3) { + priv->ieee->sec.active_key = sec->active_key; + priv->ieee->sec.flags |= SEC_ACTIVE_KEY; + } else + priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY; + priv->status |= STATUS_SECURITY_UPDATED; + } else + priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY; + + if ((sec->flags & SEC_AUTH_MODE) && + (priv->ieee->sec.auth_mode != sec->auth_mode)) { + priv->ieee->sec.auth_mode = sec->auth_mode; + priv->ieee->sec.flags |= SEC_AUTH_MODE; + if (sec->auth_mode == WLAN_AUTH_SHARED_KEY) + priv->capability |= CAP_SHARED_KEY; + else + priv->capability &= ~CAP_SHARED_KEY; + priv->status |= STATUS_SECURITY_UPDATED; + } + + if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) { + priv->ieee->sec.flags |= SEC_ENABLED; + priv->ieee->sec.enabled = sec->enabled; + priv->status |= STATUS_SECURITY_UPDATED; + if (sec->enabled) + priv->capability |= CAP_PRIVACY_ON; + else + priv->capability &= ~CAP_PRIVACY_ON; + } + + if (sec->flags & SEC_ENCRYPT) + priv->ieee->sec.encrypt = sec->encrypt; + + if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) { + priv->ieee->sec.level = sec->level; + priv->ieee->sec.flags |= SEC_LEVEL; + priv->status |= STATUS_SECURITY_UPDATED; + } + + if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT)) + ipw_set_hwcrypto_keys(priv); + + /* To match current functionality of ipw2100 (which works well w/ + * various supplicants, we don't force a disassociate if the + * privacy capability changes ... */ +#if 0 + if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) && + (((priv->assoc_request.capability & + cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && !sec->enabled) || + (!(priv->assoc_request.capability & + cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && sec->enabled))) { + IPW_DEBUG_ASSOC("Disassociating due to capability " + "change.\n"); + ipw_disassociate(priv); + } +#endif +} + +static int init_supported_rates(struct ipw_priv *priv, + struct ipw_supported_rates *rates) +{ + /* TODO: Mask out rates based on priv->rates_mask */ + + memset(rates, 0, sizeof(*rates)); + /* configure supported rates */ + switch (priv->ieee->freq_band) { + case LIBIPW_52GHZ_BAND: + rates->ieee_mode = IPW_A_MODE; + rates->purpose = IPW_RATE_CAPABILITIES; + ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION, + LIBIPW_OFDM_DEFAULT_RATES_MASK); + break; + + default: /* Mixed or 2.4Ghz */ + rates->ieee_mode = IPW_G_MODE; + rates->purpose = IPW_RATE_CAPABILITIES; + ipw_add_cck_scan_rates(rates, LIBIPW_CCK_MODULATION, + LIBIPW_CCK_DEFAULT_RATES_MASK); + if (priv->ieee->modulation & LIBIPW_OFDM_MODULATION) { + ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION, + LIBIPW_OFDM_DEFAULT_RATES_MASK); + } + break; + } + + return 0; +} + +static int ipw_config(struct ipw_priv *priv) +{ + /* This is only called from ipw_up, which resets/reloads the firmware + so, we don't need to first disable the card before we configure + it */ + if (ipw_set_tx_power(priv)) + goto error; + + /* initialize adapter address */ + if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr)) + goto error; + + /* set basic system config settings */ + init_sys_config(&priv->sys_config); + + /* Support Bluetooth if we have BT h/w on board, and user wants to. + * Does not support BT priority yet (don't abort or defer our Tx) */ + if (bt_coexist) { + unsigned char bt_caps = priv->eeprom[EEPROM_SKU_CAPABILITY]; + + if (bt_caps & EEPROM_SKU_CAP_BT_CHANNEL_SIG) + priv->sys_config.bt_coexistence + |= CFG_BT_COEXISTENCE_SIGNAL_CHNL; + if (bt_caps & EEPROM_SKU_CAP_BT_OOB) + priv->sys_config.bt_coexistence + |= CFG_BT_COEXISTENCE_OOB; + } + +#ifdef CONFIG_IPW2200_PROMISCUOUS + if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) { + priv->sys_config.accept_all_data_frames = 1; + priv->sys_config.accept_non_directed_frames = 1; + priv->sys_config.accept_all_mgmt_bcpr = 1; + priv->sys_config.accept_all_mgmt_frames = 1; + } +#endif + + if (priv->ieee->iw_mode == IW_MODE_ADHOC) + priv->sys_config.answer_broadcast_ssid_probe = 1; + else + priv->sys_config.answer_broadcast_ssid_probe = 0; + + if (ipw_send_system_config(priv)) + goto error; + + init_supported_rates(priv, &priv->rates); + if (ipw_send_supported_rates(priv, &priv->rates)) + goto error; + + /* Set request-to-send threshold */ + if (priv->rts_threshold) { + if (ipw_send_rts_threshold(priv, priv->rts_threshold)) + goto error; + } +#ifdef CONFIG_IPW2200_QOS + IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n"); + ipw_qos_activate(priv, NULL); +#endif /* CONFIG_IPW2200_QOS */ + + if (ipw_set_random_seed(priv)) + goto error; + + /* final state transition to the RUN state */ + if (ipw_send_host_complete(priv)) + goto error; + + priv->status |= STATUS_INIT; + + ipw_led_init(priv); + ipw_led_radio_on(priv); + priv->notif_missed_beacons = 0; + + /* Set hardware WEP key if it is configured. */ + if ((priv->capability & CAP_PRIVACY_ON) && + (priv->ieee->sec.level == SEC_LEVEL_1) && + !(priv->ieee->host_encrypt || priv->ieee->host_decrypt)) + ipw_set_hwcrypto_keys(priv); + + return 0; + + error: + return -EIO; +} + +/* + * NOTE: + * + * These tables have been tested in conjunction with the + * Intel PRO/Wireless 2200BG and 2915ABG Network Connection Adapters. + * + * Altering this values, using it on other hardware, or in geographies + * not intended for resale of the above mentioned Intel adapters has + * not been tested. + * + * Remember to update the table in README.ipw2200 when changing this + * table. + * + */ +static const struct libipw_geo ipw_geos[] = { + { /* Restricted */ + "---", + .bg_channels = 11, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}}, + }, + + { /* Custom US/Canada */ + "ZZF", + .bg_channels = 11, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}}, + .a_channels = 8, + .a = {{5180, 36}, + {5200, 40}, + {5220, 44}, + {5240, 48}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}}, + }, + + { /* Rest of World */ + "ZZD", + .bg_channels = 13, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}, {2467, 12}, + {2472, 13}}, + }, + + { /* Custom USA & Europe & High */ + "ZZA", + .bg_channels = 11, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}}, + .a_channels = 13, + .a = {{5180, 36}, + {5200, 40}, + {5220, 44}, + {5240, 48}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, + {5745, 149}, + {5765, 153}, + {5785, 157}, + {5805, 161}, + {5825, 165}}, + }, + + { /* Custom NA & Europe */ + "ZZB", + .bg_channels = 11, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}}, + .a_channels = 13, + .a = {{5180, 36}, + {5200, 40}, + {5220, 44}, + {5240, 48}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, + {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, + {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, + {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, + {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, + {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, + }, + + { /* Custom Japan */ + "ZZC", + .bg_channels = 11, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}}, + .a_channels = 4, + .a = {{5170, 34}, {5190, 38}, + {5210, 42}, {5230, 46}}, + }, + + { /* Custom */ + "ZZM", + .bg_channels = 11, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}}, + }, + + { /* Europe */ + "ZZE", + .bg_channels = 13, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}, {2467, 12}, + {2472, 13}}, + .a_channels = 19, + .a = {{5180, 36}, + {5200, 40}, + {5220, 44}, + {5240, 48}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, + {5500, 100, LIBIPW_CH_PASSIVE_ONLY}, + {5520, 104, LIBIPW_CH_PASSIVE_ONLY}, + {5540, 108, LIBIPW_CH_PASSIVE_ONLY}, + {5560, 112, LIBIPW_CH_PASSIVE_ONLY}, + {5580, 116, LIBIPW_CH_PASSIVE_ONLY}, + {5600, 120, LIBIPW_CH_PASSIVE_ONLY}, + {5620, 124, LIBIPW_CH_PASSIVE_ONLY}, + {5640, 128, LIBIPW_CH_PASSIVE_ONLY}, + {5660, 132, LIBIPW_CH_PASSIVE_ONLY}, + {5680, 136, LIBIPW_CH_PASSIVE_ONLY}, + {5700, 140, LIBIPW_CH_PASSIVE_ONLY}}, + }, + + { /* Custom Japan */ + "ZZJ", + .bg_channels = 14, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}, {2467, 12}, + {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY}}, + .a_channels = 4, + .a = {{5170, 34}, {5190, 38}, + {5210, 42}, {5230, 46}}, + }, + + { /* Rest of World */ + "ZZR", + .bg_channels = 14, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}, {2467, 12}, + {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY | + LIBIPW_CH_PASSIVE_ONLY}}, + }, + + { /* High Band */ + "ZZH", + .bg_channels = 13, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}, + {2467, 12, LIBIPW_CH_PASSIVE_ONLY}, + {2472, 13, LIBIPW_CH_PASSIVE_ONLY}}, + .a_channels = 4, + .a = {{5745, 149}, {5765, 153}, + {5785, 157}, {5805, 161}}, + }, + + { /* Custom Europe */ + "ZZG", + .bg_channels = 13, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}, + {2467, 12}, {2472, 13}}, + .a_channels = 4, + .a = {{5180, 36}, {5200, 40}, + {5220, 44}, {5240, 48}}, + }, + + { /* Europe */ + "ZZK", + .bg_channels = 13, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}, + {2467, 12, LIBIPW_CH_PASSIVE_ONLY}, + {2472, 13, LIBIPW_CH_PASSIVE_ONLY}}, + .a_channels = 24, + .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY}, + {5200, 40, LIBIPW_CH_PASSIVE_ONLY}, + {5220, 44, LIBIPW_CH_PASSIVE_ONLY}, + {5240, 48, LIBIPW_CH_PASSIVE_ONLY}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, + {5500, 100, LIBIPW_CH_PASSIVE_ONLY}, + {5520, 104, LIBIPW_CH_PASSIVE_ONLY}, + {5540, 108, LIBIPW_CH_PASSIVE_ONLY}, + {5560, 112, LIBIPW_CH_PASSIVE_ONLY}, + {5580, 116, LIBIPW_CH_PASSIVE_ONLY}, + {5600, 120, LIBIPW_CH_PASSIVE_ONLY}, + {5620, 124, LIBIPW_CH_PASSIVE_ONLY}, + {5640, 128, LIBIPW_CH_PASSIVE_ONLY}, + {5660, 132, LIBIPW_CH_PASSIVE_ONLY}, + {5680, 136, LIBIPW_CH_PASSIVE_ONLY}, + {5700, 140, LIBIPW_CH_PASSIVE_ONLY}, + {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, + {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, + {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, + {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, + {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, + }, + + { /* Europe */ + "ZZL", + .bg_channels = 11, + .bg = {{2412, 1}, {2417, 2}, {2422, 3}, + {2427, 4}, {2432, 5}, {2437, 6}, + {2442, 7}, {2447, 8}, {2452, 9}, + {2457, 10}, {2462, 11}}, + .a_channels = 13, + .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY}, + {5200, 40, LIBIPW_CH_PASSIVE_ONLY}, + {5220, 44, LIBIPW_CH_PASSIVE_ONLY}, + {5240, 48, LIBIPW_CH_PASSIVE_ONLY}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, + {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, + {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, + {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, + {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, + {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, + } +}; + +static void ipw_set_geo(struct ipw_priv *priv) +{ + int j; + + for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) { + if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE], + ipw_geos[j].name, 3)) + break; + } + + if (j == ARRAY_SIZE(ipw_geos)) { + IPW_WARNING("SKU [%c%c%c] not recognized.\n", + priv->eeprom[EEPROM_COUNTRY_CODE + 0], + priv->eeprom[EEPROM_COUNTRY_CODE + 1], + priv->eeprom[EEPROM_COUNTRY_CODE + 2]); + j = 0; + } + + libipw_set_geo(priv->ieee, &ipw_geos[j]); +} + +#define MAX_HW_RESTARTS 5 +static int ipw_up(struct ipw_priv *priv) +{ + int rc, i; + + /* Age scan list entries found before suspend */ + if (priv->suspend_time) { + libipw_networks_age(priv->ieee, priv->suspend_time); + priv->suspend_time = 0; + } + + if (priv->status & STATUS_EXIT_PENDING) + return -EIO; + + if (cmdlog && !priv->cmdlog) { + priv->cmdlog = kcalloc(cmdlog, sizeof(*priv->cmdlog), + GFP_KERNEL); + if (priv->cmdlog == NULL) { + IPW_ERROR("Error allocating %d command log entries.\n", + cmdlog); + return -ENOMEM; + } else { + priv->cmdlog_len = cmdlog; + } + } + + for (i = 0; i < MAX_HW_RESTARTS; i++) { + /* Load the microcode, firmware, and eeprom. + * Also start the clocks. */ + rc = ipw_load(priv); + if (rc) { + IPW_ERROR("Unable to load firmware: %d\n", rc); + return rc; + } + + ipw_init_ordinals(priv); + if (!(priv->config & CFG_CUSTOM_MAC)) + eeprom_parse_mac(priv, priv->mac_addr); + memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN); + + ipw_set_geo(priv); + + if (priv->status & STATUS_RF_KILL_SW) { + IPW_WARNING("Radio disabled by module parameter.\n"); + return 0; + } else if (rf_kill_active(priv)) { + IPW_WARNING("Radio Frequency Kill Switch is On:\n" + "Kill switch must be turned off for " + "wireless networking to work.\n"); + schedule_delayed_work(&priv->rf_kill, 2 * HZ); + return 0; + } + + rc = ipw_config(priv); + if (!rc) { + IPW_DEBUG_INFO("Configured device on count %i\n", i); + + /* If configure to try and auto-associate, kick + * off a scan. */ + schedule_delayed_work(&priv->request_scan, 0); + + return 0; + } + + IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc); + IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n", + i, MAX_HW_RESTARTS); + + /* We had an error bringing up the hardware, so take it + * all the way back down so we can try again */ + ipw_down(priv); + } + + /* tried to restart and config the device for as long as our + * patience could withstand */ + IPW_ERROR("Unable to initialize device after %d attempts.\n", i); + + return -EIO; +} + +static void ipw_bg_up(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, up); + mutex_lock(&priv->mutex); + ipw_up(priv); + mutex_unlock(&priv->mutex); +} + +static void ipw_deinit(struct ipw_priv *priv) +{ + int i; + + if (priv->status & STATUS_SCANNING) { + IPW_DEBUG_INFO("Aborting scan during shutdown.\n"); + ipw_abort_scan(priv); + } + + if (priv->status & STATUS_ASSOCIATED) { + IPW_DEBUG_INFO("Disassociating during shutdown.\n"); + ipw_disassociate(priv); + } + + ipw_led_shutdown(priv); + + /* Wait up to 1s for status to change to not scanning and not + * associated (disassociation can take a while for a ful 802.11 + * exchange */ + for (i = 1000; i && (priv->status & + (STATUS_DISASSOCIATING | + STATUS_ASSOCIATED | STATUS_SCANNING)); i--) + udelay(10); + + if (priv->status & (STATUS_DISASSOCIATING | + STATUS_ASSOCIATED | STATUS_SCANNING)) + IPW_DEBUG_INFO("Still associated or scanning...\n"); + else + IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i); + + /* Attempt to disable the card */ + ipw_send_card_disable(priv, 0); + + priv->status &= ~STATUS_INIT; +} + +static void ipw_down(struct ipw_priv *priv) +{ + int exit_pending = priv->status & STATUS_EXIT_PENDING; + + priv->status |= STATUS_EXIT_PENDING; + + if (ipw_is_init(priv)) + ipw_deinit(priv); + + /* Wipe out the EXIT_PENDING status bit if we are not actually + * exiting the module */ + if (!exit_pending) + priv->status &= ~STATUS_EXIT_PENDING; + + /* tell the device to stop sending interrupts */ + ipw_disable_interrupts(priv); + + /* Clear all bits but the RF Kill */ + priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING; + netif_carrier_off(priv->net_dev); + + ipw_stop_nic(priv); + + ipw_led_radio_off(priv); +} + +static void ipw_bg_down(struct work_struct *work) +{ + struct ipw_priv *priv = + container_of(work, struct ipw_priv, down); + mutex_lock(&priv->mutex); + ipw_down(priv); + mutex_unlock(&priv->mutex); +} + +static int ipw_wdev_init(struct net_device *dev) +{ + int i, rc = 0; + struct ipw_priv *priv = libipw_priv(dev); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); + struct wireless_dev *wdev = &priv->ieee->wdev; + + memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); + + /* fill-out priv->ieee->bg_band */ + if (geo->bg_channels) { + struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; + + bg_band->band = IEEE80211_BAND_2GHZ; + bg_band->n_channels = geo->bg_channels; + bg_band->channels = kcalloc(geo->bg_channels, + sizeof(struct ieee80211_channel), + GFP_KERNEL); + if (!bg_band->channels) { + rc = -ENOMEM; + goto out; + } + /* translate geo->bg to bg_band.channels */ + for (i = 0; i < geo->bg_channels; i++) { + bg_band->channels[i].band = IEEE80211_BAND_2GHZ; + bg_band->channels[i].center_freq = geo->bg[i].freq; + bg_band->channels[i].hw_value = geo->bg[i].channel; + bg_band->channels[i].max_power = geo->bg[i].max_power; + if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) + bg_band->channels[i].flags |= + IEEE80211_CHAN_NO_IR; + if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) + bg_band->channels[i].flags |= + IEEE80211_CHAN_NO_IR; + if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) + bg_band->channels[i].flags |= + IEEE80211_CHAN_RADAR; + /* No equivalent for LIBIPW_CH_80211H_RULES, + LIBIPW_CH_UNIFORM_SPREADING, or + LIBIPW_CH_B_ONLY... */ + } + /* point at bitrate info */ + bg_band->bitrates = ipw2200_bg_rates; + bg_band->n_bitrates = ipw2200_num_bg_rates; + + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; + } + + /* fill-out priv->ieee->a_band */ + if (geo->a_channels) { + struct ieee80211_supported_band *a_band = &priv->ieee->a_band; + + a_band->band = IEEE80211_BAND_5GHZ; + a_band->n_channels = geo->a_channels; + a_band->channels = kcalloc(geo->a_channels, + sizeof(struct ieee80211_channel), + GFP_KERNEL); + if (!a_band->channels) { + rc = -ENOMEM; + goto out; + } + /* translate geo->a to a_band.channels */ + for (i = 0; i < geo->a_channels; i++) { + a_band->channels[i].band = IEEE80211_BAND_5GHZ; + a_band->channels[i].center_freq = geo->a[i].freq; + a_band->channels[i].hw_value = geo->a[i].channel; + a_band->channels[i].max_power = geo->a[i].max_power; + if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) + a_band->channels[i].flags |= + IEEE80211_CHAN_NO_IR; + if (geo->a[i].flags & LIBIPW_CH_NO_IBSS) + a_band->channels[i].flags |= + IEEE80211_CHAN_NO_IR; + if (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT) + a_band->channels[i].flags |= + IEEE80211_CHAN_RADAR; + /* No equivalent for LIBIPW_CH_80211H_RULES, + LIBIPW_CH_UNIFORM_SPREADING, or + LIBIPW_CH_B_ONLY... */ + } + /* point at bitrate info */ + a_band->bitrates = ipw2200_a_rates; + a_band->n_bitrates = ipw2200_num_a_rates; + + wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = a_band; + } + + wdev->wiphy->cipher_suites = ipw_cipher_suites; + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites); + + set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); + + /* With that information in place, we can now register the wiphy... */ + if (wiphy_register(wdev->wiphy)) + rc = -EIO; +out: + return rc; +} + +/* PCI driver stuff */ +static const struct pci_device_id card_ids[] = { + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0}, + {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0}, + {PCI_VDEVICE(INTEL, 0x104f), 0}, + {PCI_VDEVICE(INTEL, 0x4220), 0}, /* BG */ + {PCI_VDEVICE(INTEL, 0x4221), 0}, /* BG */ + {PCI_VDEVICE(INTEL, 0x4223), 0}, /* ABG */ + {PCI_VDEVICE(INTEL, 0x4224), 0}, /* ABG */ + + /* required last entry */ + {0,} +}; + +MODULE_DEVICE_TABLE(pci, card_ids); + +static struct attribute *ipw_sysfs_entries[] = { + &dev_attr_rf_kill.attr, + &dev_attr_direct_dword.attr, + &dev_attr_indirect_byte.attr, + &dev_attr_indirect_dword.attr, + &dev_attr_mem_gpio_reg.attr, + &dev_attr_command_event_reg.attr, + &dev_attr_nic_type.attr, + &dev_attr_status.attr, + &dev_attr_cfg.attr, + &dev_attr_error.attr, + &dev_attr_event_log.attr, + &dev_attr_cmd_log.attr, + &dev_attr_eeprom_delay.attr, + &dev_attr_ucode_version.attr, + &dev_attr_rtc.attr, + &dev_attr_scan_age.attr, + &dev_attr_led.attr, + &dev_attr_speed_scan.attr, + &dev_attr_net_stats.attr, + &dev_attr_channels.attr, +#ifdef CONFIG_IPW2200_PROMISCUOUS + &dev_attr_rtap_iface.attr, + &dev_attr_rtap_filter.attr, +#endif + NULL +}; + +static struct attribute_group ipw_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = ipw_sysfs_entries, +}; + +#ifdef CONFIG_IPW2200_PROMISCUOUS +static int ipw_prom_open(struct net_device *dev) +{ + struct ipw_prom_priv *prom_priv = libipw_priv(dev); + struct ipw_priv *priv = prom_priv->priv; + + IPW_DEBUG_INFO("prom dev->open\n"); + netif_carrier_off(dev); + + if (priv->ieee->iw_mode != IW_MODE_MONITOR) { + priv->sys_config.accept_all_data_frames = 1; + priv->sys_config.accept_non_directed_frames = 1; + priv->sys_config.accept_all_mgmt_bcpr = 1; + priv->sys_config.accept_all_mgmt_frames = 1; + + ipw_send_system_config(priv); + } + + return 0; +} + +static int ipw_prom_stop(struct net_device *dev) +{ + struct ipw_prom_priv *prom_priv = libipw_priv(dev); + struct ipw_priv *priv = prom_priv->priv; + + IPW_DEBUG_INFO("prom dev->stop\n"); + + if (priv->ieee->iw_mode != IW_MODE_MONITOR) { + priv->sys_config.accept_all_data_frames = 0; + priv->sys_config.accept_non_directed_frames = 0; + priv->sys_config.accept_all_mgmt_bcpr = 0; + priv->sys_config.accept_all_mgmt_frames = 0; + + ipw_send_system_config(priv); + } + + return 0; +} + +static netdev_tx_t ipw_prom_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + IPW_DEBUG_INFO("prom dev->xmit\n"); + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static const struct net_device_ops ipw_prom_netdev_ops = { + .ndo_open = ipw_prom_open, + .ndo_stop = ipw_prom_stop, + .ndo_start_xmit = ipw_prom_hard_start_xmit, + .ndo_change_mtu = libipw_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; + +static int ipw_prom_alloc(struct ipw_priv *priv) +{ + int rc = 0; + + if (priv->prom_net_dev) + return -EPERM; + + priv->prom_net_dev = alloc_libipw(sizeof(struct ipw_prom_priv), 1); + if (priv->prom_net_dev == NULL) + return -ENOMEM; + + priv->prom_priv = libipw_priv(priv->prom_net_dev); + priv->prom_priv->ieee = netdev_priv(priv->prom_net_dev); + priv->prom_priv->priv = priv; + + strcpy(priv->prom_net_dev->name, "rtap%d"); + memcpy(priv->prom_net_dev->dev_addr, priv->mac_addr, ETH_ALEN); + + priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP; + priv->prom_net_dev->netdev_ops = &ipw_prom_netdev_ops; + + priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR; + SET_NETDEV_DEV(priv->prom_net_dev, &priv->pci_dev->dev); + + rc = register_netdev(priv->prom_net_dev); + if (rc) { + free_libipw(priv->prom_net_dev, 1); + priv->prom_net_dev = NULL; + return rc; + } + + return 0; +} + +static void ipw_prom_free(struct ipw_priv *priv) +{ + if (!priv->prom_net_dev) + return; + + unregister_netdev(priv->prom_net_dev); + free_libipw(priv->prom_net_dev, 1); + + priv->prom_net_dev = NULL; +} + +#endif + +static const struct net_device_ops ipw_netdev_ops = { + .ndo_open = ipw_net_open, + .ndo_stop = ipw_net_stop, + .ndo_set_rx_mode = ipw_net_set_multicast_list, + .ndo_set_mac_address = ipw_net_set_mac_address, + .ndo_start_xmit = libipw_xmit, + .ndo_change_mtu = libipw_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +static int ipw_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int err = 0; + struct net_device *net_dev; + void __iomem *base; + u32 length, val; + struct ipw_priv *priv; + int i; + + net_dev = alloc_libipw(sizeof(struct ipw_priv), 0); + if (net_dev == NULL) { + err = -ENOMEM; + goto out; + } + + priv = libipw_priv(net_dev); + priv->ieee = netdev_priv(net_dev); + + priv->net_dev = net_dev; + priv->pci_dev = pdev; + ipw_debug_level = debug; + spin_lock_init(&priv->irq_lock); + spin_lock_init(&priv->lock); + for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) + INIT_LIST_HEAD(&priv->ibss_mac_hash[i]); + + mutex_init(&priv->mutex); + if (pci_enable_device(pdev)) { + err = -ENODEV; + goto out_free_libipw; + } + + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (!err) + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) { + printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n"); + goto out_pci_disable_device; + } + + pci_set_drvdata(pdev, priv); + + err = pci_request_regions(pdev, DRV_NAME); + if (err) + goto out_pci_disable_device; + + /* We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state */ + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + + length = pci_resource_len(pdev, 0); + priv->hw_len = length; + + base = pci_ioremap_bar(pdev, 0); + if (!base) { + err = -ENODEV; + goto out_pci_release_regions; + } + + priv->hw_base = base; + IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length); + IPW_DEBUG_INFO("pci_resource_base = %p\n", base); + + err = ipw_setup_deferred_work(priv); + if (err) { + IPW_ERROR("Unable to setup deferred work\n"); + goto out_iounmap; + } + + ipw_sw_reset(priv, 1); + + err = request_irq(pdev->irq, ipw_isr, IRQF_SHARED, DRV_NAME, priv); + if (err) { + IPW_ERROR("Error allocating IRQ %d\n", pdev->irq); + goto out_iounmap; + } + + SET_NETDEV_DEV(net_dev, &pdev->dev); + + mutex_lock(&priv->mutex); + + priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit; + priv->ieee->set_security = shim__set_security; + priv->ieee->is_queue_full = ipw_net_is_queue_full; + +#ifdef CONFIG_IPW2200_QOS + priv->ieee->is_qos_active = ipw_is_qos_active; + priv->ieee->handle_probe_response = ipw_handle_beacon; + priv->ieee->handle_beacon = ipw_handle_probe_response; + priv->ieee->handle_assoc_response = ipw_handle_assoc_response; +#endif /* CONFIG_IPW2200_QOS */ + + priv->ieee->perfect_rssi = -20; + priv->ieee->worst_rssi = -85; + + net_dev->netdev_ops = &ipw_netdev_ops; + priv->wireless_data.spy_data = &priv->ieee->spy_data; + net_dev->wireless_data = &priv->wireless_data; + net_dev->wireless_handlers = &ipw_wx_handler_def; + net_dev->ethtool_ops = &ipw_ethtool_ops; + + err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group); + if (err) { + IPW_ERROR("failed to create sysfs device attributes\n"); + mutex_unlock(&priv->mutex); + goto out_release_irq; + } + + if (ipw_up(priv)) { + mutex_unlock(&priv->mutex); + err = -EIO; + goto out_remove_sysfs; + } + + mutex_unlock(&priv->mutex); + + err = ipw_wdev_init(net_dev); + if (err) { + IPW_ERROR("failed to register wireless device\n"); + goto out_remove_sysfs; + } + + err = register_netdev(net_dev); + if (err) { + IPW_ERROR("failed to register network device\n"); + goto out_unregister_wiphy; + } + +#ifdef CONFIG_IPW2200_PROMISCUOUS + if (rtap_iface) { + err = ipw_prom_alloc(priv); + if (err) { + IPW_ERROR("Failed to register promiscuous network " + "device (error %d).\n", err); + unregister_netdev(priv->net_dev); + goto out_unregister_wiphy; + } + } +#endif + + printk(KERN_INFO DRV_NAME ": Detected geography %s (%d 802.11bg " + "channels, %d 802.11a channels)\n", + priv->ieee->geo.name, priv->ieee->geo.bg_channels, + priv->ieee->geo.a_channels); + + return 0; + + out_unregister_wiphy: + wiphy_unregister(priv->ieee->wdev.wiphy); + kfree(priv->ieee->a_band.channels); + kfree(priv->ieee->bg_band.channels); + out_remove_sysfs: + sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group); + out_release_irq: + free_irq(pdev->irq, priv); + out_iounmap: + iounmap(priv->hw_base); + out_pci_release_regions: + pci_release_regions(pdev); + out_pci_disable_device: + pci_disable_device(pdev); + out_free_libipw: + free_libipw(priv->net_dev, 0); + out: + return err; +} + +static void ipw_pci_remove(struct pci_dev *pdev) +{ + struct ipw_priv *priv = pci_get_drvdata(pdev); + struct list_head *p, *q; + int i; + + if (!priv) + return; + + mutex_lock(&priv->mutex); + + priv->status |= STATUS_EXIT_PENDING; + ipw_down(priv); + sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group); + + mutex_unlock(&priv->mutex); + + unregister_netdev(priv->net_dev); + + if (priv->rxq) { + ipw_rx_queue_free(priv, priv->rxq); + priv->rxq = NULL; + } + ipw_tx_queue_free(priv); + + if (priv->cmdlog) { + kfree(priv->cmdlog); + priv->cmdlog = NULL; + } + + /* make sure all works are inactive */ + cancel_delayed_work_sync(&priv->adhoc_check); + cancel_work_sync(&priv->associate); + cancel_work_sync(&priv->disassociate); + cancel_work_sync(&priv->system_config); + cancel_work_sync(&priv->rx_replenish); + cancel_work_sync(&priv->adapter_restart); + cancel_delayed_work_sync(&priv->rf_kill); + cancel_work_sync(&priv->up); + cancel_work_sync(&priv->down); + cancel_delayed_work_sync(&priv->request_scan); + cancel_delayed_work_sync(&priv->request_direct_scan); + cancel_delayed_work_sync(&priv->request_passive_scan); + cancel_delayed_work_sync(&priv->scan_event); + cancel_delayed_work_sync(&priv->gather_stats); + cancel_work_sync(&priv->abort_scan); + cancel_work_sync(&priv->roam); + cancel_delayed_work_sync(&priv->scan_check); + cancel_work_sync(&priv->link_up); + cancel_work_sync(&priv->link_down); + cancel_delayed_work_sync(&priv->led_link_on); + cancel_delayed_work_sync(&priv->led_link_off); + cancel_delayed_work_sync(&priv->led_act_off); + cancel_work_sync(&priv->merge_networks); + + /* Free MAC hash list for ADHOC */ + for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) { + list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) { + list_del(p); + kfree(list_entry(p, struct ipw_ibss_seq, list)); + } + } + + kfree(priv->error); + priv->error = NULL; + +#ifdef CONFIG_IPW2200_PROMISCUOUS + ipw_prom_free(priv); +#endif + + free_irq(pdev->irq, priv); + iounmap(priv->hw_base); + pci_release_regions(pdev); + pci_disable_device(pdev); + /* wiphy_unregister needs to be here, before free_libipw */ + wiphy_unregister(priv->ieee->wdev.wiphy); + kfree(priv->ieee->a_band.channels); + kfree(priv->ieee->bg_band.channels); + free_libipw(priv->net_dev, 0); + free_firmware(); +} + +#ifdef CONFIG_PM +static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct ipw_priv *priv = pci_get_drvdata(pdev); + struct net_device *dev = priv->net_dev; + + printk(KERN_INFO "%s: Going into suspend...\n", dev->name); + + /* Take down the device; powers it off, etc. */ + ipw_down(priv); + + /* Remove the PRESENT state of the device */ + netif_device_detach(dev); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + priv->suspend_at = get_seconds(); + + return 0; +} + +static int ipw_pci_resume(struct pci_dev *pdev) +{ + struct ipw_priv *priv = pci_get_drvdata(pdev); + struct net_device *dev = priv->net_dev; + int err; + u32 val; + + printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name); + + pci_set_power_state(pdev, PCI_D0); + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "%s: pci_enable_device failed on resume\n", + dev->name); + return err; + } + pci_restore_state(pdev); + + /* + * Suspend/Resume resets the PCI configuration space, so we have to + * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries + * from interfering with C3 CPU state. pci_restore_state won't help + * here since it only restores the first 64 bytes pci config header. + */ + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + + /* Set the device back into the PRESENT state; this will also wake + * the queue of needed */ + netif_device_attach(dev); + + priv->suspend_time = get_seconds() - priv->suspend_at; + + /* Bring the device back up */ + schedule_work(&priv->up); + + return 0; +} +#endif + +static void ipw_pci_shutdown(struct pci_dev *pdev) +{ + struct ipw_priv *priv = pci_get_drvdata(pdev); + + /* Take down the device; powers it off, etc. */ + ipw_down(priv); + + pci_disable_device(pdev); +} + +/* driver initialization stuff */ +static struct pci_driver ipw_driver = { + .name = DRV_NAME, + .id_table = card_ids, + .probe = ipw_pci_probe, + .remove = ipw_pci_remove, +#ifdef CONFIG_PM + .suspend = ipw_pci_suspend, + .resume = ipw_pci_resume, +#endif + .shutdown = ipw_pci_shutdown, +}; + +static int __init ipw_init(void) +{ + int ret; + + printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n"); + printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n"); + + ret = pci_register_driver(&ipw_driver); + if (ret) { + IPW_ERROR("Unable to initialize PCI module\n"); + return ret; + } + + ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level); + if (ret) { + IPW_ERROR("Unable to create driver sysfs file\n"); + pci_unregister_driver(&ipw_driver); + return ret; + } + + return ret; +} + +static void __exit ipw_exit(void) +{ + driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level); + pci_unregister_driver(&ipw_driver); +} + +module_param(disable, int, 0444); +MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])"); + +module_param(associate, int, 0444); +MODULE_PARM_DESC(associate, "auto associate when scanning (default off)"); + +module_param(auto_create, int, 0444); +MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)"); + +module_param_named(led, led_support, int, 0444); +MODULE_PARM_DESC(led, "enable led control on some systems (default 1 on)"); + +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "debug output mask"); + +module_param_named(channel, default_channel, int, 0444); +MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])"); + +#ifdef CONFIG_IPW2200_PROMISCUOUS +module_param(rtap_iface, int, 0444); +MODULE_PARM_DESC(rtap_iface, "create the rtap interface (1 - create, default 0)"); +#endif + +#ifdef CONFIG_IPW2200_QOS +module_param(qos_enable, int, 0444); +MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis"); + +module_param(qos_burst_enable, int, 0444); +MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode"); + +module_param(qos_no_ack_mask, int, 0444); +MODULE_PARM_DESC(qos_no_ack_mask, "mask Tx_Queue to no ack"); + +module_param(burst_duration_CCK, int, 0444); +MODULE_PARM_DESC(burst_duration_CCK, "set CCK burst value"); + +module_param(burst_duration_OFDM, int, 0444); +MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value"); +#endif /* CONFIG_IPW2200_QOS */ + +#ifdef CONFIG_IPW2200_MONITOR +module_param_named(mode, network_mode, int, 0444); +MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)"); +#else +module_param_named(mode, network_mode, int, 0444); +MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)"); +#endif + +module_param(bt_coexist, int, 0444); +MODULE_PARM_DESC(bt_coexist, "enable bluetooth coexistence (default off)"); + +module_param(hwcrypto, int, 0444); +MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default off)"); + +module_param(cmdlog, int, 0444); +MODULE_PARM_DESC(cmdlog, + "allocate a ring buffer for logging firmware commands"); + +module_param(roaming, int, 0444); +MODULE_PARM_DESC(roaming, "enable roaming support (default on)"); + +module_param(antenna, int, 0444); +MODULE_PARM_DESC(antenna, "select antenna 1=Main, 3=Aux, default 0 [both], 2=slow_diversity (choose the one with lower background noise)"); + +module_exit(ipw_exit); +module_init(ipw_init); diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.h b/drivers/net/wireless/intel/ipw2x00/ipw2200.h new file mode 100644 index 000000000000..aa301d1eee3c --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.h @@ -0,0 +1,2001 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Intel Linux Wireless + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ + +#ifndef __ipw2200_h__ +#define __ipw2200_h__ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define DRV_NAME "ipw2200" + +#include + +#include "libipw.h" + +/* Authentication and Association States */ +enum connection_manager_assoc_states { + CMAS_INIT = 0, + CMAS_TX_AUTH_SEQ_1, + CMAS_RX_AUTH_SEQ_2, + CMAS_AUTH_SEQ_1_PASS, + CMAS_AUTH_SEQ_1_FAIL, + CMAS_TX_AUTH_SEQ_3, + CMAS_RX_AUTH_SEQ_4, + CMAS_AUTH_SEQ_2_PASS, + CMAS_AUTH_SEQ_2_FAIL, + CMAS_AUTHENTICATED, + CMAS_TX_ASSOC, + CMAS_RX_ASSOC_RESP, + CMAS_ASSOCIATED, + CMAS_LAST +}; + +#define IPW_WAIT (1<<0) +#define IPW_QUIET (1<<1) +#define IPW_ROAMING (1<<2) + +#define IPW_POWER_MODE_CAM 0x00 //(always on) +#define IPW_POWER_INDEX_1 0x01 +#define IPW_POWER_INDEX_2 0x02 +#define IPW_POWER_INDEX_3 0x03 +#define IPW_POWER_INDEX_4 0x04 +#define IPW_POWER_INDEX_5 0x05 +#define IPW_POWER_AC 0x06 +#define IPW_POWER_BATTERY 0x07 +#define IPW_POWER_LIMIT 0x07 +#define IPW_POWER_MASK 0x0F +#define IPW_POWER_ENABLED 0x10 +#define IPW_POWER_LEVEL(x) ((x) & IPW_POWER_MASK) + +#define IPW_CMD_HOST_COMPLETE 2 +#define IPW_CMD_POWER_DOWN 4 +#define IPW_CMD_SYSTEM_CONFIG 6 +#define IPW_CMD_MULTICAST_ADDRESS 7 +#define IPW_CMD_SSID 8 +#define IPW_CMD_ADAPTER_ADDRESS 11 +#define IPW_CMD_PORT_TYPE 12 +#define IPW_CMD_RTS_THRESHOLD 15 +#define IPW_CMD_FRAG_THRESHOLD 16 +#define IPW_CMD_POWER_MODE 17 +#define IPW_CMD_WEP_KEY 18 +#define IPW_CMD_TGI_TX_KEY 19 +#define IPW_CMD_SCAN_REQUEST 20 +#define IPW_CMD_ASSOCIATE 21 +#define IPW_CMD_SUPPORTED_RATES 22 +#define IPW_CMD_SCAN_ABORT 23 +#define IPW_CMD_TX_FLUSH 24 +#define IPW_CMD_QOS_PARAMETERS 25 +#define IPW_CMD_SCAN_REQUEST_EXT 26 +#define IPW_CMD_DINO_CONFIG 30 +#define IPW_CMD_RSN_CAPABILITIES 31 +#define IPW_CMD_RX_KEY 32 +#define IPW_CMD_CARD_DISABLE 33 +#define IPW_CMD_SEED_NUMBER 34 +#define IPW_CMD_TX_POWER 35 +#define IPW_CMD_COUNTRY_INFO 36 +#define IPW_CMD_AIRONET_INFO 37 +#define IPW_CMD_AP_TX_POWER 38 +#define IPW_CMD_CCKM_INFO 39 +#define IPW_CMD_CCX_VER_INFO 40 +#define IPW_CMD_SET_CALIBRATION 41 +#define IPW_CMD_SENSITIVITY_CALIB 42 +#define IPW_CMD_RETRY_LIMIT 51 +#define IPW_CMD_IPW_PRE_POWER_DOWN 58 +#define IPW_CMD_VAP_BEACON_TEMPLATE 60 +#define IPW_CMD_VAP_DTIM_PERIOD 61 +#define IPW_CMD_EXT_SUPPORTED_RATES 62 +#define IPW_CMD_VAP_LOCAL_TX_PWR_CONSTRAINT 63 +#define IPW_CMD_VAP_QUIET_INTERVALS 64 +#define IPW_CMD_VAP_CHANNEL_SWITCH 65 +#define IPW_CMD_VAP_MANDATORY_CHANNELS 66 +#define IPW_CMD_VAP_CELL_PWR_LIMIT 67 +#define IPW_CMD_VAP_CF_PARAM_SET 68 +#define IPW_CMD_VAP_SET_BEACONING_STATE 69 +#define IPW_CMD_MEASUREMENT 80 +#define IPW_CMD_POWER_CAPABILITY 81 +#define IPW_CMD_SUPPORTED_CHANNELS 82 +#define IPW_CMD_TPC_REPORT 83 +#define IPW_CMD_WME_INFO 84 +#define IPW_CMD_PRODUCTION_COMMAND 85 +#define IPW_CMD_LINKSYS_EOU_INFO 90 + +#define RFD_SIZE 4 +#define NUM_TFD_CHUNKS 6 + +#define TX_QUEUE_SIZE 32 +#define RX_QUEUE_SIZE 32 + +#define DINO_CMD_WEP_KEY 0x08 +#define DINO_CMD_TX 0x0B +#define DCT_ANTENNA_A 0x01 +#define DCT_ANTENNA_B 0x02 + +#define IPW_A_MODE 0 +#define IPW_B_MODE 1 +#define IPW_G_MODE 2 + +/* + * TX Queue Flag Definitions + */ + +/* tx wep key definition */ +#define DCT_WEP_KEY_NOT_IMMIDIATE 0x00 +#define DCT_WEP_KEY_64Bit 0x40 +#define DCT_WEP_KEY_128Bit 0x80 +#define DCT_WEP_KEY_128bitIV 0xC0 +#define DCT_WEP_KEY_SIZE_MASK 0xC0 + +#define DCT_WEP_KEY_INDEX_MASK 0x0F +#define DCT_WEP_INDEX_USE_IMMEDIATE 0x20 + +/* abort attempt if mgmt frame is rx'd */ +#define DCT_FLAG_ABORT_MGMT 0x01 + +/* require CTS */ +#define DCT_FLAG_CTS_REQUIRED 0x02 + +/* use short preamble */ +#define DCT_FLAG_LONG_PREAMBLE 0x00 +#define DCT_FLAG_SHORT_PREAMBLE 0x04 + +/* RTS/CTS first */ +#define DCT_FLAG_RTS_REQD 0x08 + +/* dont calculate duration field */ +#define DCT_FLAG_DUR_SET 0x10 + +/* even if MAC WEP set (allows pre-encrypt) */ +#define DCT_FLAG_NO_WEP 0x20 + +/* overwrite TSF field */ +#define DCT_FLAG_TSF_REQD 0x40 + +/* ACK rx is expected to follow */ +#define DCT_FLAG_ACK_REQD 0x80 + +/* TX flags extension */ +#define DCT_FLAG_EXT_MODE_CCK 0x01 +#define DCT_FLAG_EXT_MODE_OFDM 0x00 + +#define DCT_FLAG_EXT_SECURITY_WEP 0x00 +#define DCT_FLAG_EXT_SECURITY_NO DCT_FLAG_EXT_SECURITY_WEP +#define DCT_FLAG_EXT_SECURITY_CKIP 0x04 +#define DCT_FLAG_EXT_SECURITY_CCM 0x08 +#define DCT_FLAG_EXT_SECURITY_TKIP 0x0C +#define DCT_FLAG_EXT_SECURITY_MASK 0x0C + +#define DCT_FLAG_EXT_QOS_ENABLED 0x10 + +#define DCT_FLAG_EXT_HC_NO_SIFS_PIFS 0x00 +#define DCT_FLAG_EXT_HC_SIFS 0x20 +#define DCT_FLAG_EXT_HC_PIFS 0x40 + +#define TX_RX_TYPE_MASK 0xFF +#define TX_FRAME_TYPE 0x00 +#define TX_HOST_COMMAND_TYPE 0x01 +#define RX_FRAME_TYPE 0x09 +#define RX_HOST_NOTIFICATION_TYPE 0x03 +#define RX_HOST_CMD_RESPONSE_TYPE 0x04 +#define RX_TX_FRAME_RESPONSE_TYPE 0x05 +#define TFD_NEED_IRQ_MASK 0x04 + +#define HOST_CMD_DINO_CONFIG 30 + +#define HOST_NOTIFICATION_STATUS_ASSOCIATED 10 +#define HOST_NOTIFICATION_STATUS_AUTHENTICATE 11 +#define HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT 12 +#define HOST_NOTIFICATION_STATUS_SCAN_COMPLETED 13 +#define HOST_NOTIFICATION_STATUS_FRAG_LENGTH 14 +#define HOST_NOTIFICATION_STATUS_LINK_DETERIORATION 15 +#define HOST_NOTIFICATION_DINO_CONFIG_RESPONSE 16 +#define HOST_NOTIFICATION_STATUS_BEACON_STATE 17 +#define HOST_NOTIFICATION_STATUS_TGI_TX_KEY 18 +#define HOST_NOTIFICATION_TX_STATUS 19 +#define HOST_NOTIFICATION_CALIB_KEEP_RESULTS 20 +#define HOST_NOTIFICATION_MEASUREMENT_STARTED 21 +#define HOST_NOTIFICATION_MEASUREMENT_ENDED 22 +#define HOST_NOTIFICATION_CHANNEL_SWITCHED 23 +#define HOST_NOTIFICATION_RX_DURING_QUIET_PERIOD 24 +#define HOST_NOTIFICATION_NOISE_STATS 25 +#define HOST_NOTIFICATION_S36_MEASUREMENT_ACCEPTED 30 +#define HOST_NOTIFICATION_S36_MEASUREMENT_REFUSED 31 + +#define HOST_NOTIFICATION_STATUS_BEACON_MISSING 1 +#define IPW_MB_SCAN_CANCEL_THRESHOLD 3 +#define IPW_MB_ROAMING_THRESHOLD_MIN 1 +#define IPW_MB_ROAMING_THRESHOLD_DEFAULT 8 +#define IPW_MB_ROAMING_THRESHOLD_MAX 30 +#define IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT 3*IPW_MB_ROAMING_THRESHOLD_DEFAULT +#define IPW_REAL_RATE_RX_PACKET_THRESHOLD 300 + +#define MACADRR_BYTE_LEN 6 + +#define DCR_TYPE_AP 0x01 +#define DCR_TYPE_WLAP 0x02 +#define DCR_TYPE_MU_ESS 0x03 +#define DCR_TYPE_MU_IBSS 0x04 +#define DCR_TYPE_MU_PIBSS 0x05 +#define DCR_TYPE_SNIFFER 0x06 +#define DCR_TYPE_MU_BSS DCR_TYPE_MU_ESS + +/* QoS definitions */ + +#define CW_MIN_OFDM 15 +#define CW_MAX_OFDM 1023 +#define CW_MIN_CCK 31 +#define CW_MAX_CCK 1023 + +#define QOS_TX0_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) +#define QOS_TX1_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) +#define QOS_TX2_CW_MIN_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/2 - 1) +#define QOS_TX3_CW_MIN_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/4 - 1) + +#define QOS_TX0_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) +#define QOS_TX1_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) +#define QOS_TX2_CW_MIN_CCK cpu_to_le16((CW_MIN_CCK + 1)/2 - 1) +#define QOS_TX3_CW_MIN_CCK cpu_to_le16((CW_MIN_CCK + 1)/4 - 1) + +#define QOS_TX0_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) +#define QOS_TX1_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) +#define QOS_TX2_CW_MAX_OFDM cpu_to_le16(CW_MIN_OFDM) +#define QOS_TX3_CW_MAX_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/2 - 1) + +#define QOS_TX0_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) +#define QOS_TX1_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) +#define QOS_TX2_CW_MAX_CCK cpu_to_le16(CW_MIN_CCK) +#define QOS_TX3_CW_MAX_CCK cpu_to_le16((CW_MIN_CCK + 1)/2 - 1) + +#define QOS_TX0_AIFS (3 - QOS_AIFSN_MIN_VALUE) +#define QOS_TX1_AIFS (7 - QOS_AIFSN_MIN_VALUE) +#define QOS_TX2_AIFS (2 - QOS_AIFSN_MIN_VALUE) +#define QOS_TX3_AIFS (2 - QOS_AIFSN_MIN_VALUE) + +#define QOS_TX0_ACM 0 +#define QOS_TX1_ACM 0 +#define QOS_TX2_ACM 0 +#define QOS_TX3_ACM 0 + +#define QOS_TX0_TXOP_LIMIT_CCK 0 +#define QOS_TX1_TXOP_LIMIT_CCK 0 +#define QOS_TX2_TXOP_LIMIT_CCK cpu_to_le16(6016) +#define QOS_TX3_TXOP_LIMIT_CCK cpu_to_le16(3264) + +#define QOS_TX0_TXOP_LIMIT_OFDM 0 +#define QOS_TX1_TXOP_LIMIT_OFDM 0 +#define QOS_TX2_TXOP_LIMIT_OFDM cpu_to_le16(3008) +#define QOS_TX3_TXOP_LIMIT_OFDM cpu_to_le16(1504) + +#define DEF_TX0_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) +#define DEF_TX1_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) +#define DEF_TX2_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) +#define DEF_TX3_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) + +#define DEF_TX0_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) +#define DEF_TX1_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) +#define DEF_TX2_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) +#define DEF_TX3_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) + +#define DEF_TX0_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) +#define DEF_TX1_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) +#define DEF_TX2_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) +#define DEF_TX3_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) + +#define DEF_TX0_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) +#define DEF_TX1_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) +#define DEF_TX2_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) +#define DEF_TX3_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) + +#define DEF_TX0_AIFS 0 +#define DEF_TX1_AIFS 0 +#define DEF_TX2_AIFS 0 +#define DEF_TX3_AIFS 0 + +#define DEF_TX0_ACM 0 +#define DEF_TX1_ACM 0 +#define DEF_TX2_ACM 0 +#define DEF_TX3_ACM 0 + +#define DEF_TX0_TXOP_LIMIT_CCK 0 +#define DEF_TX1_TXOP_LIMIT_CCK 0 +#define DEF_TX2_TXOP_LIMIT_CCK 0 +#define DEF_TX3_TXOP_LIMIT_CCK 0 + +#define DEF_TX0_TXOP_LIMIT_OFDM 0 +#define DEF_TX1_TXOP_LIMIT_OFDM 0 +#define DEF_TX2_TXOP_LIMIT_OFDM 0 +#define DEF_TX3_TXOP_LIMIT_OFDM 0 + +#define QOS_QOS_SETS 3 +#define QOS_PARAM_SET_ACTIVE 0 +#define QOS_PARAM_SET_DEF_CCK 1 +#define QOS_PARAM_SET_DEF_OFDM 2 + +#define CTRL_QOS_NO_ACK (0x0020) + +#define IPW_TX_QUEUE_1 1 +#define IPW_TX_QUEUE_2 2 +#define IPW_TX_QUEUE_3 3 +#define IPW_TX_QUEUE_4 4 + +/* QoS sturctures */ +struct ipw_qos_info { + int qos_enable; + struct libipw_qos_parameters *def_qos_parm_OFDM; + struct libipw_qos_parameters *def_qos_parm_CCK; + u32 burst_duration_CCK; + u32 burst_duration_OFDM; + u16 qos_no_ack_mask; + int burst_enable; +}; + +/**************************************************************/ +/** + * Generic queue structure + * + * Contains common data for Rx and Tx queues + */ +struct clx2_queue { + int n_bd; /**< number of BDs in this queue */ + int first_empty; /**< 1-st empty entry (index) */ + int last_used; /**< last used entry (index) */ + u32 reg_w; /**< 'write' reg (queue head), addr in domain 1 */ + u32 reg_r; /**< 'read' reg (queue tail), addr in domain 1 */ + dma_addr_t dma_addr; /**< physical addr for BD's */ + int low_mark; /**< low watermark, resume queue if free space more than this */ + int high_mark; /**< high watermark, stop queue if free space less than this */ +} __packed; /* XXX */ + +struct machdr32 { + __le16 frame_ctl; + __le16 duration; // watch out for endians! + u8 addr1[MACADRR_BYTE_LEN]; + u8 addr2[MACADRR_BYTE_LEN]; + u8 addr3[MACADRR_BYTE_LEN]; + __le16 seq_ctrl; // more endians! + u8 addr4[MACADRR_BYTE_LEN]; + __le16 qos_ctrl; +} __packed; + +struct machdr30 { + __le16 frame_ctl; + __le16 duration; // watch out for endians! + u8 addr1[MACADRR_BYTE_LEN]; + u8 addr2[MACADRR_BYTE_LEN]; + u8 addr3[MACADRR_BYTE_LEN]; + __le16 seq_ctrl; // more endians! + u8 addr4[MACADRR_BYTE_LEN]; +} __packed; + +struct machdr26 { + __le16 frame_ctl; + __le16 duration; // watch out for endians! + u8 addr1[MACADRR_BYTE_LEN]; + u8 addr2[MACADRR_BYTE_LEN]; + u8 addr3[MACADRR_BYTE_LEN]; + __le16 seq_ctrl; // more endians! + __le16 qos_ctrl; +} __packed; + +struct machdr24 { + __le16 frame_ctl; + __le16 duration; // watch out for endians! + u8 addr1[MACADRR_BYTE_LEN]; + u8 addr2[MACADRR_BYTE_LEN]; + u8 addr3[MACADRR_BYTE_LEN]; + __le16 seq_ctrl; // more endians! +} __packed; + +// TX TFD with 32 byte MAC Header +struct tx_tfd_32 { + struct machdr32 mchdr; // 32 + __le32 uivplaceholder[2]; // 8 +} __packed; + +// TX TFD with 30 byte MAC Header +struct tx_tfd_30 { + struct machdr30 mchdr; // 30 + u8 reserved[2]; // 2 + __le32 uivplaceholder[2]; // 8 +} __packed; + +// tx tfd with 26 byte mac header +struct tx_tfd_26 { + struct machdr26 mchdr; // 26 + u8 reserved1[2]; // 2 + __le32 uivplaceholder[2]; // 8 + u8 reserved2[4]; // 4 +} __packed; + +// tx tfd with 24 byte mac header +struct tx_tfd_24 { + struct machdr24 mchdr; // 24 + __le32 uivplaceholder[2]; // 8 + u8 reserved[8]; // 8 +} __packed; + +#define DCT_WEP_KEY_FIELD_LENGTH 16 + +struct tfd_command { + u8 index; + u8 length; + __le16 reserved; + u8 payload[0]; +} __packed; + +struct tfd_data { + /* Header */ + __le32 work_area_ptr; + u8 station_number; /* 0 for BSS */ + u8 reserved1; + __le16 reserved2; + + /* Tx Parameters */ + u8 cmd_id; + u8 seq_num; + __le16 len; + u8 priority; + u8 tx_flags; + u8 tx_flags_ext; + u8 key_index; + u8 wepkey[DCT_WEP_KEY_FIELD_LENGTH]; + u8 rate; + u8 antenna; + __le16 next_packet_duration; + __le16 next_frag_len; + __le16 back_off_counter; //////txop; + u8 retrylimit; + __le16 cwcurrent; + u8 reserved3; + + /* 802.11 MAC Header */ + union { + struct tx_tfd_24 tfd_24; + struct tx_tfd_26 tfd_26; + struct tx_tfd_30 tfd_30; + struct tx_tfd_32 tfd_32; + } tfd; + + /* Payload DMA info */ + __le32 num_chunks; + __le32 chunk_ptr[NUM_TFD_CHUNKS]; + __le16 chunk_len[NUM_TFD_CHUNKS]; +} __packed; + +struct txrx_control_flags { + u8 message_type; + u8 rx_seq_num; + u8 control_bits; + u8 reserved; +} __packed; + +#define TFD_SIZE 128 +#define TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH (TFD_SIZE - sizeof(struct txrx_control_flags)) + +struct tfd_frame { + struct txrx_control_flags control_flags; + union { + struct tfd_data data; + struct tfd_command cmd; + u8 raw[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH]; + } u; +} __packed; + +typedef void destructor_func(const void *); + +/** + * Tx Queue for DMA. Queue consists of circular buffer of + * BD's and required locking structures. + */ +struct clx2_tx_queue { + struct clx2_queue q; + struct tfd_frame *bd; + struct libipw_txb **txb; +}; + +/* + * RX related structures and functions + */ +#define RX_FREE_BUFFERS 32 +#define RX_LOW_WATERMARK 8 + +#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 +#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 +#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 + +// Used for passing to driver number of successes and failures per rate +struct rate_histogram { + union { + __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } success; + union { + __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } failed; +} __packed; + +/* statistics command response */ +struct ipw_cmd_stats { + u8 cmd_id; + u8 seq_num; + __le16 good_sfd; + __le16 bad_plcp; + __le16 wrong_bssid; + __le16 valid_mpdu; + __le16 bad_mac_header; + __le16 reserved_frame_types; + __le16 rx_ina; + __le16 bad_crc32; + __le16 invalid_cts; + __le16 invalid_acks; + __le16 long_distance_ina_fina; + __le16 dsp_silence_unreachable; + __le16 accumulated_rssi; + __le16 rx_ovfl_frame_tossed; + __le16 rssi_silence_threshold; + __le16 rx_ovfl_frame_supplied; + __le16 last_rx_frame_signal; + __le16 last_rx_frame_noise; + __le16 rx_autodetec_no_ofdm; + __le16 rx_autodetec_no_barker; + __le16 reserved; +} __packed; + +struct notif_channel_result { + u8 channel_num; + struct ipw_cmd_stats stats; + u8 uReserved; +} __packed; + +#define SCAN_COMPLETED_STATUS_COMPLETE 1 +#define SCAN_COMPLETED_STATUS_ABORTED 2 + +struct notif_scan_complete { + u8 scan_type; + u8 num_channels; + u8 status; + u8 reserved; +} __packed; + +struct notif_frag_length { + __le16 frag_length; + __le16 reserved; +} __packed; + +struct notif_beacon_state { + __le32 state; + __le32 number; +} __packed; + +struct notif_tgi_tx_key { + u8 key_state; + u8 security_type; + u8 station_index; + u8 reserved; +} __packed; + +#define SILENCE_OVER_THRESH (1) +#define SILENCE_UNDER_THRESH (2) + +struct notif_link_deterioration { + struct ipw_cmd_stats stats; + u8 rate; + u8 modulation; + struct rate_histogram histogram; + u8 silence_notification_type; /* SILENCE_OVER/UNDER_THRESH */ + __le16 silence_count; +} __packed; + +struct notif_association { + u8 state; +} __packed; + +struct notif_authenticate { + u8 state; + struct machdr24 addr; + __le16 status; +} __packed; + +struct notif_calibration { + u8 data[104]; +} __packed; + +struct notif_noise { + __le32 value; +} __packed; + +struct ipw_rx_notification { + u8 reserved[8]; + u8 subtype; + u8 flags; + __le16 size; + union { + struct notif_association assoc; + struct notif_authenticate auth; + struct notif_channel_result channel_result; + struct notif_scan_complete scan_complete; + struct notif_frag_length frag_len; + struct notif_beacon_state beacon_state; + struct notif_tgi_tx_key tgi_tx_key; + struct notif_link_deterioration link_deterioration; + struct notif_calibration calibration; + struct notif_noise noise; + u8 raw[0]; + } u; +} __packed; + +struct ipw_rx_frame { + __le32 reserved1; + u8 parent_tsf[4]; // fw_use[0] is boolean for OUR_TSF_IS_GREATER + u8 received_channel; // The channel that this frame was received on. + // Note that for .11b this does not have to be + // the same as the channel that it was sent. + // Filled by LMAC + u8 frameStatus; + u8 rate; + u8 rssi; + u8 agc; + u8 rssi_dbm; + __le16 signal; + __le16 noise; + u8 antennaAndPhy; + u8 control; // control bit should be on in bg + u8 rtscts_rate; // rate of rts or cts (in rts cts sequence rate + // is identical) + u8 rtscts_seen; // 0x1 RTS seen ; 0x2 CTS seen + __le16 length; + u8 data[0]; +} __packed; + +struct ipw_rx_header { + u8 message_type; + u8 rx_seq_num; + u8 control_bits; + u8 reserved; +} __packed; + +struct ipw_rx_packet { + struct ipw_rx_header header; + union { + struct ipw_rx_frame frame; + struct ipw_rx_notification notification; + } u; +} __packed; + +#define IPW_RX_NOTIFICATION_SIZE sizeof(struct ipw_rx_header) + 12 +#define IPW_RX_FRAME_SIZE (unsigned int)(sizeof(struct ipw_rx_header) + \ + sizeof(struct ipw_rx_frame)) + +struct ipw_rx_mem_buffer { + dma_addr_t dma_addr; + struct sk_buff *skb; + struct list_head list; +}; /* Not transferred over network, so not __packed */ + +struct ipw_rx_queue { + struct ipw_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; + struct ipw_rx_mem_buffer *queue[RX_QUEUE_SIZE]; + u32 processed; /* Internal index to last handled Rx packet */ + u32 read; /* Shared index to newest available Rx buffer */ + u32 write; /* Shared index to oldest written Rx packet */ + u32 free_count; /* Number of pre-allocated buffers in rx_free */ + /* Each of these lists is used as a FIFO for ipw_rx_mem_buffers */ + struct list_head rx_free; /* Own an SKBs */ + struct list_head rx_used; /* No SKB allocated */ + spinlock_t lock; +}; /* Not transferred over network, so not __packed */ + +struct alive_command_responce { + u8 alive_command; + u8 sequence_number; + __le16 software_revision; + u8 device_identifier; + u8 reserved1[5]; + __le16 reserved2; + __le16 reserved3; + __le16 clock_settle_time; + __le16 powerup_settle_time; + __le16 reserved4; + u8 time_stamp[5]; /* month, day, year, hours, minutes */ + u8 ucode_valid; +} __packed; + +#define IPW_MAX_RATES 12 + +struct ipw_rates { + u8 num_rates; + u8 rates[IPW_MAX_RATES]; +} __packed; + +struct command_block { + unsigned int control; + u32 source_addr; + u32 dest_addr; + unsigned int status; +} __packed; + +#define CB_NUMBER_OF_ELEMENTS_SMALL 64 +struct fw_image_desc { + unsigned long last_cb_index; + unsigned long current_cb_index; + struct command_block cb_list[CB_NUMBER_OF_ELEMENTS_SMALL]; + void *v_addr; + unsigned long p_addr; + unsigned long len; +}; + +struct ipw_sys_config { + u8 bt_coexistence; + u8 reserved1; + u8 answer_broadcast_ssid_probe; + u8 accept_all_data_frames; + u8 accept_non_directed_frames; + u8 exclude_unicast_unencrypted; + u8 disable_unicast_decryption; + u8 exclude_multicast_unencrypted; + u8 disable_multicast_decryption; + u8 antenna_diversity; + u8 pass_crc_to_host; + u8 dot11g_auto_detection; + u8 enable_cts_to_self; + u8 enable_multicast_filtering; + u8 bt_coexist_collision_thr; + u8 silence_threshold; + u8 accept_all_mgmt_bcpr; + u8 accept_all_mgmt_frames; + u8 pass_noise_stats_to_host; + u8 reserved3; +} __packed; + +struct ipw_multicast_addr { + u8 num_of_multicast_addresses; + u8 reserved[3]; + u8 mac1[6]; + u8 mac2[6]; + u8 mac3[6]; + u8 mac4[6]; +} __packed; + +#define DCW_WEP_KEY_INDEX_MASK 0x03 /* bits [0:1] */ +#define DCW_WEP_KEY_SEC_TYPE_MASK 0x30 /* bits [4:5] */ + +#define DCW_WEP_KEY_SEC_TYPE_WEP 0x00 +#define DCW_WEP_KEY_SEC_TYPE_CCM 0x20 +#define DCW_WEP_KEY_SEC_TYPE_TKIP 0x30 + +#define DCW_WEP_KEY_INVALID_SIZE 0x00 /* 0 = Invalid key */ +#define DCW_WEP_KEY64Bit_SIZE 0x05 /* 64-bit encryption */ +#define DCW_WEP_KEY128Bit_SIZE 0x0D /* 128-bit encryption */ +#define DCW_CCM_KEY128Bit_SIZE 0x10 /* 128-bit key */ +//#define DCW_WEP_KEY128BitIV_SIZE 0x10 /* 128-bit key and 128-bit IV */ + +struct ipw_wep_key { + u8 cmd_id; + u8 seq_num; + u8 key_index; + u8 key_size; + u8 key[16]; +} __packed; + +struct ipw_tgi_tx_key { + u8 key_id; + u8 security_type; + u8 station_index; + u8 flags; + u8 key[16]; + __le32 tx_counter[2]; +} __packed; + +#define IPW_SCAN_CHANNELS 54 + +struct ipw_scan_request { + u8 scan_type; + __le16 dwell_time; + u8 channels_list[IPW_SCAN_CHANNELS]; + u8 channels_reserved[3]; +} __packed; + +enum { + IPW_SCAN_PASSIVE_TILL_FIRST_BEACON_SCAN = 0, + IPW_SCAN_PASSIVE_FULL_DWELL_SCAN, + IPW_SCAN_ACTIVE_DIRECT_SCAN, + IPW_SCAN_ACTIVE_BROADCAST_SCAN, + IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN, + IPW_SCAN_TYPES +}; + +struct ipw_scan_request_ext { + __le32 full_scan_index; + u8 channels_list[IPW_SCAN_CHANNELS]; + u8 scan_type[IPW_SCAN_CHANNELS / 2]; + u8 reserved; + __le16 dwell_time[IPW_SCAN_TYPES]; +} __packed; + +static inline u8 ipw_get_scan_type(struct ipw_scan_request_ext *scan, u8 index) +{ + if (index % 2) + return scan->scan_type[index / 2] & 0x0F; + else + return (scan->scan_type[index / 2] & 0xF0) >> 4; +} + +static inline void ipw_set_scan_type(struct ipw_scan_request_ext *scan, + u8 index, u8 scan_type) +{ + if (index % 2) + scan->scan_type[index / 2] = + (scan->scan_type[index / 2] & 0xF0) | (scan_type & 0x0F); + else + scan->scan_type[index / 2] = + (scan->scan_type[index / 2] & 0x0F) | + ((scan_type & 0x0F) << 4); +} + +struct ipw_associate { + u8 channel; +#ifdef __LITTLE_ENDIAN_BITFIELD + u8 auth_type:4, auth_key:4; +#else + u8 auth_key:4, auth_type:4; +#endif + u8 assoc_type; + u8 reserved; + __le16 policy_support; + u8 preamble_length; + u8 ieee_mode; + u8 bssid[ETH_ALEN]; + __le32 assoc_tsf_msw; + __le32 assoc_tsf_lsw; + __le16 capability; + __le16 listen_interval; + __le16 beacon_interval; + u8 dest[ETH_ALEN]; + __le16 atim_window; + u8 smr; + u8 reserved1; + __le16 reserved2; +} __packed; + +struct ipw_supported_rates { + u8 ieee_mode; + u8 num_rates; + u8 purpose; + u8 reserved; + u8 supported_rates[IPW_MAX_RATES]; +} __packed; + +struct ipw_rts_threshold { + __le16 rts_threshold; + __le16 reserved; +} __packed; + +struct ipw_frag_threshold { + __le16 frag_threshold; + __le16 reserved; +} __packed; + +struct ipw_retry_limit { + u8 short_retry_limit; + u8 long_retry_limit; + __le16 reserved; +} __packed; + +struct ipw_dino_config { + __le32 dino_config_addr; + __le16 dino_config_size; + u8 dino_response; + u8 reserved; +} __packed; + +struct ipw_aironet_info { + u8 id; + u8 length; + __le16 reserved; +} __packed; + +struct ipw_rx_key { + u8 station_index; + u8 key_type; + u8 key_id; + u8 key_flag; + u8 key[16]; + u8 station_address[6]; + u8 key_index; + u8 reserved; +} __packed; + +struct ipw_country_channel_info { + u8 first_channel; + u8 no_channels; + s8 max_tx_power; +} __packed; + +struct ipw_country_info { + u8 id; + u8 length; + u8 country_str[IEEE80211_COUNTRY_STRING_LEN]; + struct ipw_country_channel_info groups[7]; +} __packed; + +struct ipw_channel_tx_power { + u8 channel_number; + s8 tx_power; +} __packed; + +#define SCAN_ASSOCIATED_INTERVAL (HZ) +#define SCAN_INTERVAL (HZ / 10) +#define MAX_A_CHANNELS 37 +#define MAX_B_CHANNELS 14 + +struct ipw_tx_power { + u8 num_channels; + u8 ieee_mode; + struct ipw_channel_tx_power channels_tx_power[MAX_A_CHANNELS]; +} __packed; + +struct ipw_rsn_capabilities { + u8 id; + u8 length; + __le16 version; +} __packed; + +struct ipw_sensitivity_calib { + __le16 beacon_rssi_raw; + __le16 reserved; +} __packed; + +/** + * Host command structure. + * + * On input, the following fields should be filled: + * - cmd + * - len + * - status_len + * - param (if needed) + * + * On output, + * - \a status contains status; + * - \a param filled with status parameters. + */ +struct ipw_cmd { /* XXX */ + u32 cmd; /**< Host command */ + u32 status;/**< Status */ + u32 status_len; + /**< How many 32 bit parameters in the status */ + u32 len; /**< incoming parameters length, bytes */ + /** + * command parameters. + * There should be enough space for incoming and + * outcoming parameters. + * Incoming parameters listed 1-st, followed by outcoming params. + * nParams=(len+3)/4+status_len + */ + u32 param[0]; +} __packed; + +#define STATUS_HCMD_ACTIVE (1<<0) /**< host command in progress */ + +#define STATUS_INT_ENABLED (1<<1) +#define STATUS_RF_KILL_HW (1<<2) +#define STATUS_RF_KILL_SW (1<<3) +#define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW) + +#define STATUS_INIT (1<<5) +#define STATUS_AUTH (1<<6) +#define STATUS_ASSOCIATED (1<<7) +#define STATUS_STATE_MASK (STATUS_INIT | STATUS_AUTH | STATUS_ASSOCIATED) + +#define STATUS_ASSOCIATING (1<<8) +#define STATUS_DISASSOCIATING (1<<9) +#define STATUS_ROAMING (1<<10) +#define STATUS_EXIT_PENDING (1<<11) +#define STATUS_DISASSOC_PENDING (1<<12) +#define STATUS_STATE_PENDING (1<<13) + +#define STATUS_DIRECT_SCAN_PENDING (1<<19) +#define STATUS_SCAN_PENDING (1<<20) +#define STATUS_SCANNING (1<<21) +#define STATUS_SCAN_ABORTING (1<<22) +#define STATUS_SCAN_FORCED (1<<23) + +#define STATUS_LED_LINK_ON (1<<24) +#define STATUS_LED_ACT_ON (1<<25) + +#define STATUS_INDIRECT_BYTE (1<<28) /* sysfs entry configured for access */ +#define STATUS_INDIRECT_DWORD (1<<29) /* sysfs entry configured for access */ +#define STATUS_DIRECT_DWORD (1<<30) /* sysfs entry configured for access */ + +#define STATUS_SECURITY_UPDATED (1<<31) /* Security sync needed */ + +#define CFG_STATIC_CHANNEL (1<<0) /* Restrict assoc. to single channel */ +#define CFG_STATIC_ESSID (1<<1) /* Restrict assoc. to single SSID */ +#define CFG_STATIC_BSSID (1<<2) /* Restrict assoc. to single BSSID */ +#define CFG_CUSTOM_MAC (1<<3) +#define CFG_PREAMBLE_LONG (1<<4) +#define CFG_ADHOC_PERSIST (1<<5) +#define CFG_ASSOCIATE (1<<6) +#define CFG_FIXED_RATE (1<<7) +#define CFG_ADHOC_CREATE (1<<8) +#define CFG_NO_LED (1<<9) +#define CFG_BACKGROUND_SCAN (1<<10) +#define CFG_SPEED_SCAN (1<<11) +#define CFG_NET_STATS (1<<12) + +#define CAP_SHARED_KEY (1<<0) /* Off = OPEN */ +#define CAP_PRIVACY_ON (1<<1) /* Off = No privacy */ + +#define MAX_STATIONS 32 +#define IPW_INVALID_STATION (0xff) + +struct ipw_station_entry { + u8 mac_addr[ETH_ALEN]; + u8 reserved; + u8 support_mode; +}; + +#define AVG_ENTRIES 8 +struct average { + s16 entries[AVG_ENTRIES]; + u8 pos; + u8 init; + s32 sum; +}; + +#define MAX_SPEED_SCAN 100 +#define IPW_IBSS_MAC_HASH_SIZE 31 + +struct ipw_ibss_seq { + u8 mac[ETH_ALEN]; + u16 seq_num; + u16 frag_num; + unsigned long packet_time; + struct list_head list; +}; + +struct ipw_error_elem { /* XXX */ + u32 desc; + u32 time; + u32 blink1; + u32 blink2; + u32 link1; + u32 link2; + u32 data; +}; + +struct ipw_event { /* XXX */ + u32 event; + u32 time; + u32 data; +} __packed; + +struct ipw_fw_error { /* XXX */ + unsigned long jiffies; + u32 status; + u32 config; + u32 elem_len; + u32 log_len; + struct ipw_error_elem *elem; + struct ipw_event *log; + u8 payload[0]; +} __packed; + +#ifdef CONFIG_IPW2200_PROMISCUOUS + +enum ipw_prom_filter { + IPW_PROM_CTL_HEADER_ONLY = (1 << 0), + IPW_PROM_MGMT_HEADER_ONLY = (1 << 1), + IPW_PROM_DATA_HEADER_ONLY = (1 << 2), + IPW_PROM_ALL_HEADER_ONLY = 0xf, /* bits 0..3 */ + IPW_PROM_NO_TX = (1 << 4), + IPW_PROM_NO_RX = (1 << 5), + IPW_PROM_NO_CTL = (1 << 6), + IPW_PROM_NO_MGMT = (1 << 7), + IPW_PROM_NO_DATA = (1 << 8), +}; + +struct ipw_priv; +struct ipw_prom_priv { + struct ipw_priv *priv; + struct libipw_device *ieee; + enum ipw_prom_filter filter; + int tx_packets; + int rx_packets; +}; +#endif + +#if defined(CONFIG_IPW2200_RADIOTAP) || defined(CONFIG_IPW2200_PROMISCUOUS) +/* Magic struct that slots into the radiotap header -- no reason + * to build this manually element by element, we can write it much + * more efficiently than we can parse it. ORDER MATTERS HERE + * + * When sent to us via the simulated Rx interface in sysfs, the entire + * structure is provided regardless of any bits unset. + */ +struct ipw_rt_hdr { + struct ieee80211_radiotap_header rt_hdr; + u64 rt_tsf; /* TSF */ /* XXX */ + u8 rt_flags; /* radiotap packet flags */ + u8 rt_rate; /* rate in 500kb/s */ + __le16 rt_channel; /* channel in mhz */ + __le16 rt_chbitmask; /* channel bitfield */ + s8 rt_dbmsignal; /* signal in dbM, kluged to signed */ + s8 rt_dbmnoise; + u8 rt_antenna; /* antenna number */ + u8 payload[0]; /* payload... */ +} __packed; +#endif + +struct ipw_priv { + /* ieee device used by generic ieee processing code */ + struct libipw_device *ieee; + + spinlock_t lock; + spinlock_t irq_lock; + struct mutex mutex; + + /* basic pci-network driver stuff */ + struct pci_dev *pci_dev; + struct net_device *net_dev; + +#ifdef CONFIG_IPW2200_PROMISCUOUS + /* Promiscuous mode */ + struct ipw_prom_priv *prom_priv; + struct net_device *prom_net_dev; +#endif + + /* pci hardware address support */ + void __iomem *hw_base; + unsigned long hw_len; + + struct fw_image_desc sram_desc; + + /* result of ucode download */ + struct alive_command_responce dino_alive; + + wait_queue_head_t wait_command_queue; + wait_queue_head_t wait_state; + + /* Rx and Tx DMA processing queues */ + struct ipw_rx_queue *rxq; + struct clx2_tx_queue txq_cmd; + struct clx2_tx_queue txq[4]; + u32 status; + u32 config; + u32 capability; + + struct average average_missed_beacons; + s16 exp_avg_rssi; + s16 exp_avg_noise; + u32 port_type; + int rx_bufs_min; /**< minimum number of bufs in Rx queue */ + int rx_pend_max; /**< maximum pending buffers for one IRQ */ + u32 hcmd_seq; /**< sequence number for hcmd */ + u32 disassociate_threshold; + u32 roaming_threshold; + + struct ipw_associate assoc_request; + struct libipw_network *assoc_network; + + unsigned long ts_scan_abort; + struct ipw_supported_rates rates; + struct ipw_rates phy[3]; /**< PHY restrictions, per band */ + struct ipw_rates supp; /**< software defined */ + struct ipw_rates extended; /**< use for corresp. IE, AP only */ + + struct notif_link_deterioration last_link_deterioration; /** for statistics */ + struct ipw_cmd *hcmd; /**< host command currently executed */ + + wait_queue_head_t hcmd_wq; /**< host command waits for execution */ + u32 tsf_bcn[2]; /**< TSF from latest beacon */ + + struct notif_calibration calib; /**< last calibration */ + + /* ordinal interface with firmware */ + u32 table0_addr; + u32 table0_len; + u32 table1_addr; + u32 table1_len; + u32 table2_addr; + u32 table2_len; + + /* context information */ + u8 essid[IW_ESSID_MAX_SIZE]; + u8 essid_len; + u8 nick[IW_ESSID_MAX_SIZE]; + u16 rates_mask; + u8 channel; + struct ipw_sys_config sys_config; + u32 power_mode; + u8 bssid[ETH_ALEN]; + u16 rts_threshold; + u8 mac_addr[ETH_ALEN]; + u8 num_stations; + u8 stations[MAX_STATIONS][ETH_ALEN]; + u8 short_retry_limit; + u8 long_retry_limit; + + u32 notif_missed_beacons; + + /* Statistics and counters normalized with each association */ + u32 last_missed_beacons; + u32 last_tx_packets; + u32 last_rx_packets; + u32 last_tx_failures; + u32 last_rx_err; + u32 last_rate; + + u32 missed_adhoc_beacons; + u32 missed_beacons; + u32 rx_packets; + u32 tx_packets; + u32 quality; + + u8 speed_scan[MAX_SPEED_SCAN]; + u8 speed_scan_pos; + + u16 last_seq_num; + u16 last_frag_num; + unsigned long last_packet_time; + struct list_head ibss_mac_hash[IPW_IBSS_MAC_HASH_SIZE]; + + /* eeprom */ + u8 eeprom[0x100]; /* 256 bytes of eeprom */ + u8 country[4]; + int eeprom_delay; + + struct iw_statistics wstats; + + struct iw_public_data wireless_data; + + int user_requested_scan; + u8 direct_scan_ssid[IW_ESSID_MAX_SIZE]; + u8 direct_scan_ssid_len; + + struct delayed_work adhoc_check; + struct work_struct associate; + struct work_struct disassociate; + struct work_struct system_config; + struct work_struct rx_replenish; + struct delayed_work request_scan; + struct delayed_work request_direct_scan; + struct delayed_work request_passive_scan; + struct delayed_work scan_event; + struct work_struct adapter_restart; + struct delayed_work rf_kill; + struct work_struct up; + struct work_struct down; + struct delayed_work gather_stats; + struct work_struct abort_scan; + struct work_struct roam; + struct delayed_work scan_check; + struct work_struct link_up; + struct work_struct link_down; + + struct tasklet_struct irq_tasklet; + + /* LED related variables and work_struct */ + u8 nic_type; + u32 led_activity_on; + u32 led_activity_off; + u32 led_association_on; + u32 led_association_off; + u32 led_ofdm_on; + u32 led_ofdm_off; + + struct delayed_work led_link_on; + struct delayed_work led_link_off; + struct delayed_work led_act_off; + struct work_struct merge_networks; + + struct ipw_cmd_log *cmdlog; + int cmdlog_len; + int cmdlog_pos; + +#define IPW_2200BG 1 +#define IPW_2915ABG 2 + u8 adapter; + + s8 tx_power; + + /* Track time in suspend */ + unsigned long suspend_at; + unsigned long suspend_time; + +#ifdef CONFIG_PM + u32 pm_state[16]; +#endif + + struct ipw_fw_error *error; + + /* network state */ + + /* Used to pass the current INTA value from ISR to Tasklet */ + u32 isr_inta; + + /* QoS */ + struct ipw_qos_info qos_data; + struct work_struct qos_activate; + /*********************************/ + + /* debugging info */ + u32 indirect_dword; + u32 direct_dword; + u32 indirect_byte; +}; /*ipw_priv */ + +/* debug macros */ + +/* Debug and printf string expansion helpers for printing bitfields */ +#define BIT_FMT8 "%c%c%c%c-%c%c%c%c" +#define BIT_FMT16 BIT_FMT8 ":" BIT_FMT8 +#define BIT_FMT32 BIT_FMT16 " " BIT_FMT16 + +#define BITC(x,y) (((x>>y)&1)?'1':'0') +#define BIT_ARG8(x) \ +BITC(x,7),BITC(x,6),BITC(x,5),BITC(x,4),\ +BITC(x,3),BITC(x,2),BITC(x,1),BITC(x,0) + +#define BIT_ARG16(x) \ +BITC(x,15),BITC(x,14),BITC(x,13),BITC(x,12),\ +BITC(x,11),BITC(x,10),BITC(x,9),BITC(x,8),\ +BIT_ARG8(x) + +#define BIT_ARG32(x) \ +BITC(x,31),BITC(x,30),BITC(x,29),BITC(x,28),\ +BITC(x,27),BITC(x,26),BITC(x,25),BITC(x,24),\ +BITC(x,23),BITC(x,22),BITC(x,21),BITC(x,20),\ +BITC(x,19),BITC(x,18),BITC(x,17),BITC(x,16),\ +BIT_ARG16(x) + + +#define IPW_DEBUG(level, fmt, args...) \ +do { if (ipw_debug_level & (level)) \ + printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) + +#ifdef CONFIG_IPW2200_DEBUG +#define IPW_LL_DEBUG(level, fmt, args...) \ +do { if (ipw_debug_level & (level)) \ + printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) +#else +#define IPW_LL_DEBUG(level, fmt, args...) do {} while (0) +#endif /* CONFIG_IPW2200_DEBUG */ + +/* + * To use the debug system; + * + * If you are defining a new debug classification, simply add it to the #define + * list here in the form of: + * + * #define IPW_DL_xxxx VALUE + * + * shifting value to the left one bit from the previous entry. xxxx should be + * the name of the classification (for example, WEP) + * + * You then need to either add a IPW_xxxx_DEBUG() macro definition for your + * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want + * to send output to that classification. + * + * To add your debug level to the list of levels seen when you perform + * + * % cat /proc/net/ipw/debug_level + * + * you simply need to add your entry to the ipw_debug_levels array. + * + * If you do not see debug_level in /proc/net/ipw then you do not have + * CONFIG_IPW2200_DEBUG defined in your kernel configuration + * + */ + +#define IPW_DL_ERROR (1<<0) +#define IPW_DL_WARNING (1<<1) +#define IPW_DL_INFO (1<<2) +#define IPW_DL_WX (1<<3) +#define IPW_DL_HOST_COMMAND (1<<5) +#define IPW_DL_STATE (1<<6) + +#define IPW_DL_NOTIF (1<<10) +#define IPW_DL_SCAN (1<<11) +#define IPW_DL_ASSOC (1<<12) +#define IPW_DL_DROP (1<<13) +#define IPW_DL_IOCTL (1<<14) + +#define IPW_DL_MANAGE (1<<15) +#define IPW_DL_FW (1<<16) +#define IPW_DL_RF_KILL (1<<17) +#define IPW_DL_FW_ERRORS (1<<18) + +#define IPW_DL_LED (1<<19) + +#define IPW_DL_ORD (1<<20) + +#define IPW_DL_FRAG (1<<21) +#define IPW_DL_WEP (1<<22) +#define IPW_DL_TX (1<<23) +#define IPW_DL_RX (1<<24) +#define IPW_DL_ISR (1<<25) +#define IPW_DL_FW_INFO (1<<26) +#define IPW_DL_IO (1<<27) +#define IPW_DL_TRACE (1<<28) + +#define IPW_DL_STATS (1<<29) +#define IPW_DL_MERGE (1<<30) +#define IPW_DL_QOS (1<<31) + +#define IPW_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a) +#define IPW_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a) +#define IPW_DEBUG_INFO(f, a...) IPW_DEBUG(IPW_DL_INFO, f, ## a) + +#define IPW_DEBUG_WX(f, a...) IPW_DEBUG(IPW_DL_WX, f, ## a) +#define IPW_DEBUG_SCAN(f, a...) IPW_DEBUG(IPW_DL_SCAN, f, ## a) +#define IPW_DEBUG_TRACE(f, a...) IPW_LL_DEBUG(IPW_DL_TRACE, f, ## a) +#define IPW_DEBUG_RX(f, a...) IPW_LL_DEBUG(IPW_DL_RX, f, ## a) +#define IPW_DEBUG_TX(f, a...) IPW_LL_DEBUG(IPW_DL_TX, f, ## a) +#define IPW_DEBUG_ISR(f, a...) IPW_LL_DEBUG(IPW_DL_ISR, f, ## a) +#define IPW_DEBUG_MANAGEMENT(f, a...) IPW_DEBUG(IPW_DL_MANAGE, f, ## a) +#define IPW_DEBUG_LED(f, a...) IPW_LL_DEBUG(IPW_DL_LED, f, ## a) +#define IPW_DEBUG_WEP(f, a...) IPW_LL_DEBUG(IPW_DL_WEP, f, ## a) +#define IPW_DEBUG_HC(f, a...) IPW_LL_DEBUG(IPW_DL_HOST_COMMAND, f, ## a) +#define IPW_DEBUG_FRAG(f, a...) IPW_LL_DEBUG(IPW_DL_FRAG, f, ## a) +#define IPW_DEBUG_FW(f, a...) IPW_LL_DEBUG(IPW_DL_FW, f, ## a) +#define IPW_DEBUG_RF_KILL(f, a...) IPW_DEBUG(IPW_DL_RF_KILL, f, ## a) +#define IPW_DEBUG_DROP(f, a...) IPW_DEBUG(IPW_DL_DROP, f, ## a) +#define IPW_DEBUG_IO(f, a...) IPW_LL_DEBUG(IPW_DL_IO, f, ## a) +#define IPW_DEBUG_ORD(f, a...) IPW_LL_DEBUG(IPW_DL_ORD, f, ## a) +#define IPW_DEBUG_FW_INFO(f, a...) IPW_LL_DEBUG(IPW_DL_FW_INFO, f, ## a) +#define IPW_DEBUG_NOTIF(f, a...) IPW_DEBUG(IPW_DL_NOTIF, f, ## a) +#define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) +#define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) +#define IPW_DEBUG_STATS(f, a...) IPW_LL_DEBUG(IPW_DL_STATS, f, ## a) +#define IPW_DEBUG_MERGE(f, a...) IPW_LL_DEBUG(IPW_DL_MERGE, f, ## a) +#define IPW_DEBUG_QOS(f, a...) IPW_LL_DEBUG(IPW_DL_QOS, f, ## a) + +#include + +/* +* Register bit definitions +*/ + +#define IPW_INTA_RW 0x00000008 +#define IPW_INTA_MASK_R 0x0000000C +#define IPW_INDIRECT_ADDR 0x00000010 +#define IPW_INDIRECT_DATA 0x00000014 +#define IPW_AUTOINC_ADDR 0x00000018 +#define IPW_AUTOINC_DATA 0x0000001C +#define IPW_RESET_REG 0x00000020 +#define IPW_GP_CNTRL_RW 0x00000024 + +#define IPW_READ_INT_REGISTER 0xFF4 + +#define IPW_GP_CNTRL_BIT_INIT_DONE 0x00000004 + +#define IPW_REGISTER_DOMAIN1_END 0x00001000 +#define IPW_SRAM_READ_INT_REGISTER 0x00000ff4 + +#define IPW_SHARED_LOWER_BOUND 0x00000200 +#define IPW_INTERRUPT_AREA_LOWER_BOUND 0x00000f80 + +#define IPW_NIC_SRAM_LOWER_BOUND 0x00000000 +#define IPW_NIC_SRAM_UPPER_BOUND 0x00030000 + +#define IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER (1 << 29) +#define IPW_GP_CNTRL_BIT_CLOCK_READY 0x00000001 +#define IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY 0x00000002 + +/* + * RESET Register Bit Indexes + */ +#define CBD_RESET_REG_PRINCETON_RESET (1<<0) +#define IPW_START_STANDBY (1<<2) +#define IPW_ACTIVITY_LED (1<<4) +#define IPW_ASSOCIATED_LED (1<<5) +#define IPW_OFDM_LED (1<<6) +#define IPW_RESET_REG_SW_RESET (1<<7) +#define IPW_RESET_REG_MASTER_DISABLED (1<<8) +#define IPW_RESET_REG_STOP_MASTER (1<<9) +#define IPW_GATE_ODMA (1<<25) +#define IPW_GATE_IDMA (1<<26) +#define IPW_ARC_KESHET_CONFIG (1<<27) +#define IPW_GATE_ADMA (1<<29) + +#define IPW_CSR_CIS_UPPER_BOUND 0x00000200 +#define IPW_DOMAIN_0_END 0x1000 +#define CLX_MEM_BAR_SIZE 0x1000 + +/* Dino/baseband control registers bits */ + +#define DINO_ENABLE_SYSTEM 0x80 /* 1 = baseband processor on, 0 = reset */ +#define DINO_ENABLE_CS 0x40 /* 1 = enable ucode load */ +#define DINO_RXFIFO_DATA 0x01 /* 1 = data available */ +#define IPW_BASEBAND_CONTROL_STATUS 0X00200000 +#define IPW_BASEBAND_TX_FIFO_WRITE 0X00200004 +#define IPW_BASEBAND_RX_FIFO_READ 0X00200004 +#define IPW_BASEBAND_CONTROL_STORE 0X00200010 + +#define IPW_INTERNAL_CMD_EVENT 0X00300004 +#define IPW_BASEBAND_POWER_DOWN 0x00000001 + +#define IPW_MEM_HALT_AND_RESET 0x003000e0 + +/* defgroup bits_halt_reset MEM_HALT_AND_RESET register bits */ +#define IPW_BIT_HALT_RESET_ON 0x80000000 +#define IPW_BIT_HALT_RESET_OFF 0x00000000 + +#define CB_LAST_VALID 0x20000000 +#define CB_INT_ENABLED 0x40000000 +#define CB_VALID 0x80000000 +#define CB_SRC_LE 0x08000000 +#define CB_DEST_LE 0x04000000 +#define CB_SRC_AUTOINC 0x00800000 +#define CB_SRC_IO_GATED 0x00400000 +#define CB_DEST_AUTOINC 0x00080000 +#define CB_SRC_SIZE_LONG 0x00200000 +#define CB_DEST_SIZE_LONG 0x00020000 + +/* DMA DEFINES */ + +#define DMA_CONTROL_SMALL_CB_CONST_VALUE 0x00540000 +#define DMA_CB_STOP_AND_ABORT 0x00000C00 +#define DMA_CB_START 0x00000100 + +#define IPW_SHARED_SRAM_SIZE 0x00030000 +#define IPW_SHARED_SRAM_DMA_CONTROL 0x00027000 +#define CB_MAX_LENGTH 0x1FFF + +#define IPW_HOST_EEPROM_DATA_SRAM_SIZE 0xA18 +#define IPW_EEPROM_IMAGE_SIZE 0x100 + +/* DMA defs */ +#define IPW_DMA_I_CURRENT_CB 0x003000D0 +#define IPW_DMA_O_CURRENT_CB 0x003000D4 +#define IPW_DMA_I_DMA_CONTROL 0x003000A4 +#define IPW_DMA_I_CB_BASE 0x003000A0 + +#define IPW_TX_CMD_QUEUE_BD_BASE 0x00000200 +#define IPW_TX_CMD_QUEUE_BD_SIZE 0x00000204 +#define IPW_TX_QUEUE_0_BD_BASE 0x00000208 +#define IPW_TX_QUEUE_0_BD_SIZE (0x0000020C) +#define IPW_TX_QUEUE_1_BD_BASE 0x00000210 +#define IPW_TX_QUEUE_1_BD_SIZE 0x00000214 +#define IPW_TX_QUEUE_2_BD_BASE 0x00000218 +#define IPW_TX_QUEUE_2_BD_SIZE (0x0000021C) +#define IPW_TX_QUEUE_3_BD_BASE 0x00000220 +#define IPW_TX_QUEUE_3_BD_SIZE 0x00000224 +#define IPW_RX_BD_BASE 0x00000240 +#define IPW_RX_BD_SIZE 0x00000244 +#define IPW_RFDS_TABLE_LOWER 0x00000500 + +#define IPW_TX_CMD_QUEUE_READ_INDEX 0x00000280 +#define IPW_TX_QUEUE_0_READ_INDEX 0x00000284 +#define IPW_TX_QUEUE_1_READ_INDEX 0x00000288 +#define IPW_TX_QUEUE_2_READ_INDEX (0x0000028C) +#define IPW_TX_QUEUE_3_READ_INDEX 0x00000290 +#define IPW_RX_READ_INDEX (0x000002A0) + +#define IPW_TX_CMD_QUEUE_WRITE_INDEX (0x00000F80) +#define IPW_TX_QUEUE_0_WRITE_INDEX (0x00000F84) +#define IPW_TX_QUEUE_1_WRITE_INDEX (0x00000F88) +#define IPW_TX_QUEUE_2_WRITE_INDEX (0x00000F8C) +#define IPW_TX_QUEUE_3_WRITE_INDEX (0x00000F90) +#define IPW_RX_WRITE_INDEX (0x00000FA0) + +/* + * EEPROM Related Definitions + */ + +#define IPW_EEPROM_DATA_SRAM_ADDRESS (IPW_SHARED_LOWER_BOUND + 0x814) +#define IPW_EEPROM_DATA_SRAM_SIZE (IPW_SHARED_LOWER_BOUND + 0x818) +#define IPW_EEPROM_LOAD_DISABLE (IPW_SHARED_LOWER_BOUND + 0x81C) +#define IPW_EEPROM_DATA (IPW_SHARED_LOWER_BOUND + 0x820) +#define IPW_EEPROM_UPPER_ADDRESS (IPW_SHARED_LOWER_BOUND + 0x9E0) + +#define IPW_STATION_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0xA0C) +#define IPW_STATION_TABLE_UPPER (IPW_SHARED_LOWER_BOUND + 0xB0C) +#define IPW_REQUEST_ATIM (IPW_SHARED_LOWER_BOUND + 0xB0C) +#define IPW_ATIM_SENT (IPW_SHARED_LOWER_BOUND + 0xB10) +#define IPW_WHO_IS_AWAKE (IPW_SHARED_LOWER_BOUND + 0xB14) +#define IPW_DURING_ATIM_WINDOW (IPW_SHARED_LOWER_BOUND + 0xB18) + +#define MSB 1 +#define LSB 0 +#define WORD_TO_BYTE(_word) ((_word) * sizeof(u16)) + +#define GET_EEPROM_ADDR(_wordoffset,_byteoffset) \ + ( WORD_TO_BYTE(_wordoffset) + (_byteoffset) ) + +/* EEPROM access by BYTE */ +#define EEPROM_PME_CAPABILITY (GET_EEPROM_ADDR(0x09,MSB)) /* 1 byte */ +#define EEPROM_MAC_ADDRESS (GET_EEPROM_ADDR(0x21,LSB)) /* 6 byte */ +#define EEPROM_VERSION (GET_EEPROM_ADDR(0x24,MSB)) /* 1 byte */ +#define EEPROM_NIC_TYPE (GET_EEPROM_ADDR(0x25,LSB)) /* 1 byte */ +#define EEPROM_SKU_CAPABILITY (GET_EEPROM_ADDR(0x25,MSB)) /* 1 byte */ +#define EEPROM_COUNTRY_CODE (GET_EEPROM_ADDR(0x26,LSB)) /* 3 bytes */ +#define EEPROM_IBSS_CHANNELS_BG (GET_EEPROM_ADDR(0x28,LSB)) /* 2 bytes */ +#define EEPROM_IBSS_CHANNELS_A (GET_EEPROM_ADDR(0x29,MSB)) /* 5 bytes */ +#define EEPROM_BSS_CHANNELS_BG (GET_EEPROM_ADDR(0x2c,LSB)) /* 2 bytes */ +#define EEPROM_HW_VERSION (GET_EEPROM_ADDR(0x72,LSB)) /* 2 bytes */ + +/* NIC type as found in the one byte EEPROM_NIC_TYPE offset */ +#define EEPROM_NIC_TYPE_0 0 +#define EEPROM_NIC_TYPE_1 1 +#define EEPROM_NIC_TYPE_2 2 +#define EEPROM_NIC_TYPE_3 3 +#define EEPROM_NIC_TYPE_4 4 + +/* Bluetooth Coexistence capabilities as found in EEPROM_SKU_CAPABILITY */ +#define EEPROM_SKU_CAP_BT_CHANNEL_SIG 0x01 /* we can tell BT our channel # */ +#define EEPROM_SKU_CAP_BT_PRIORITY 0x02 /* BT can take priority over us */ +#define EEPROM_SKU_CAP_BT_OOB 0x04 /* we can signal BT out-of-band */ + +#define FW_MEM_REG_LOWER_BOUND 0x00300000 +#define FW_MEM_REG_EEPROM_ACCESS (FW_MEM_REG_LOWER_BOUND + 0x40) +#define IPW_EVENT_REG (FW_MEM_REG_LOWER_BOUND + 0x04) +#define EEPROM_BIT_SK (1<<0) +#define EEPROM_BIT_CS (1<<1) +#define EEPROM_BIT_DI (1<<2) +#define EEPROM_BIT_DO (1<<4) + +#define EEPROM_CMD_READ 0x2 + +/* Interrupts masks */ +#define IPW_INTA_NONE 0x00000000 + +#define IPW_INTA_BIT_RX_TRANSFER 0x00000002 +#define IPW_INTA_BIT_STATUS_CHANGE 0x00000010 +#define IPW_INTA_BIT_BEACON_PERIOD_EXPIRED 0x00000020 + +//Inta Bits for CF +#define IPW_INTA_BIT_TX_CMD_QUEUE 0x00000800 +#define IPW_INTA_BIT_TX_QUEUE_1 0x00001000 +#define IPW_INTA_BIT_TX_QUEUE_2 0x00002000 +#define IPW_INTA_BIT_TX_QUEUE_3 0x00004000 +#define IPW_INTA_BIT_TX_QUEUE_4 0x00008000 + +#define IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE 0x00010000 + +#define IPW_INTA_BIT_PREPARE_FOR_POWER_DOWN 0x00100000 +#define IPW_INTA_BIT_POWER_DOWN 0x00200000 + +#define IPW_INTA_BIT_FW_INITIALIZATION_DONE 0x01000000 +#define IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE 0x02000000 +#define IPW_INTA_BIT_RF_KILL_DONE 0x04000000 +#define IPW_INTA_BIT_FATAL_ERROR 0x40000000 +#define IPW_INTA_BIT_PARITY_ERROR 0x80000000 + +/* Interrupts enabled at init time. */ +#define IPW_INTA_MASK_ALL \ + (IPW_INTA_BIT_TX_QUEUE_1 | \ + IPW_INTA_BIT_TX_QUEUE_2 | \ + IPW_INTA_BIT_TX_QUEUE_3 | \ + IPW_INTA_BIT_TX_QUEUE_4 | \ + IPW_INTA_BIT_TX_CMD_QUEUE | \ + IPW_INTA_BIT_RX_TRANSFER | \ + IPW_INTA_BIT_FATAL_ERROR | \ + IPW_INTA_BIT_PARITY_ERROR | \ + IPW_INTA_BIT_STATUS_CHANGE | \ + IPW_INTA_BIT_FW_INITIALIZATION_DONE | \ + IPW_INTA_BIT_BEACON_PERIOD_EXPIRED | \ + IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE | \ + IPW_INTA_BIT_PREPARE_FOR_POWER_DOWN | \ + IPW_INTA_BIT_POWER_DOWN | \ + IPW_INTA_BIT_RF_KILL_DONE ) + +/* FW event log definitions */ +#define EVENT_ELEM_SIZE (3 * sizeof(u32)) +#define EVENT_START_OFFSET (1 * sizeof(u32) + 2 * sizeof(u16)) + +/* FW error log definitions */ +#define ERROR_ELEM_SIZE (7 * sizeof(u32)) +#define ERROR_START_OFFSET (1 * sizeof(u32)) + +/* TX power level (dbm) */ +#define IPW_TX_POWER_MIN -12 +#define IPW_TX_POWER_MAX 20 +#define IPW_TX_POWER_DEFAULT IPW_TX_POWER_MAX + +enum { + IPW_FW_ERROR_OK = 0, + IPW_FW_ERROR_FAIL, + IPW_FW_ERROR_MEMORY_UNDERFLOW, + IPW_FW_ERROR_MEMORY_OVERFLOW, + IPW_FW_ERROR_BAD_PARAM, + IPW_FW_ERROR_BAD_CHECKSUM, + IPW_FW_ERROR_NMI_INTERRUPT, + IPW_FW_ERROR_BAD_DATABASE, + IPW_FW_ERROR_ALLOC_FAIL, + IPW_FW_ERROR_DMA_UNDERRUN, + IPW_FW_ERROR_DMA_STATUS, + IPW_FW_ERROR_DINO_ERROR, + IPW_FW_ERROR_EEPROM_ERROR, + IPW_FW_ERROR_SYSASSERT, + IPW_FW_ERROR_FATAL_ERROR +}; + +#define AUTH_OPEN 0 +#define AUTH_SHARED_KEY 1 +#define AUTH_LEAP 2 +#define AUTH_IGNORE 3 + +#define HC_ASSOCIATE 0 +#define HC_REASSOCIATE 1 +#define HC_DISASSOCIATE 2 +#define HC_IBSS_START 3 +#define HC_IBSS_RECONF 4 +#define HC_DISASSOC_QUIET 5 + +#define HC_QOS_SUPPORT_ASSOC cpu_to_le16(0x01) + +#define IPW_RATE_CAPABILITIES 1 +#define IPW_RATE_CONNECT 0 + +/* + * Rate values and masks + */ +#define IPW_TX_RATE_1MB 0x0A +#define IPW_TX_RATE_2MB 0x14 +#define IPW_TX_RATE_5MB 0x37 +#define IPW_TX_RATE_6MB 0x0D +#define IPW_TX_RATE_9MB 0x0F +#define IPW_TX_RATE_11MB 0x6E +#define IPW_TX_RATE_12MB 0x05 +#define IPW_TX_RATE_18MB 0x07 +#define IPW_TX_RATE_24MB 0x09 +#define IPW_TX_RATE_36MB 0x0B +#define IPW_TX_RATE_48MB 0x01 +#define IPW_TX_RATE_54MB 0x03 + +#define IPW_ORD_TABLE_ID_MASK 0x0000FF00 +#define IPW_ORD_TABLE_VALUE_MASK 0x000000FF + +#define IPW_ORD_TABLE_0_MASK 0x0000F000 +#define IPW_ORD_TABLE_1_MASK 0x0000F100 +#define IPW_ORD_TABLE_2_MASK 0x0000F200 +#define IPW_ORD_TABLE_3_MASK 0x0000F300 +#define IPW_ORD_TABLE_4_MASK 0x0000F400 +#define IPW_ORD_TABLE_5_MASK 0x0000F500 +#define IPW_ORD_TABLE_6_MASK 0x0000F600 +#define IPW_ORD_TABLE_7_MASK 0x0000F700 + +/* + * Table 0 Entries (all entries are 32 bits) + */ +enum { + IPW_ORD_STAT_TX_CURR_RATE = IPW_ORD_TABLE_0_MASK + 1, + IPW_ORD_STAT_FRAG_TRESHOLD, + IPW_ORD_STAT_RTS_THRESHOLD, + IPW_ORD_STAT_TX_HOST_REQUESTS, + IPW_ORD_STAT_TX_HOST_COMPLETE, + IPW_ORD_STAT_TX_DIR_DATA, + IPW_ORD_STAT_TX_DIR_DATA_B_1, + IPW_ORD_STAT_TX_DIR_DATA_B_2, + IPW_ORD_STAT_TX_DIR_DATA_B_5_5, + IPW_ORD_STAT_TX_DIR_DATA_B_11, + /* Hole */ + + IPW_ORD_STAT_TX_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 19, + IPW_ORD_STAT_TX_DIR_DATA_G_2, + IPW_ORD_STAT_TX_DIR_DATA_G_5_5, + IPW_ORD_STAT_TX_DIR_DATA_G_6, + IPW_ORD_STAT_TX_DIR_DATA_G_9, + IPW_ORD_STAT_TX_DIR_DATA_G_11, + IPW_ORD_STAT_TX_DIR_DATA_G_12, + IPW_ORD_STAT_TX_DIR_DATA_G_18, + IPW_ORD_STAT_TX_DIR_DATA_G_24, + IPW_ORD_STAT_TX_DIR_DATA_G_36, + IPW_ORD_STAT_TX_DIR_DATA_G_48, + IPW_ORD_STAT_TX_DIR_DATA_G_54, + IPW_ORD_STAT_TX_NON_DIR_DATA, + IPW_ORD_STAT_TX_NON_DIR_DATA_B_1, + IPW_ORD_STAT_TX_NON_DIR_DATA_B_2, + IPW_ORD_STAT_TX_NON_DIR_DATA_B_5_5, + IPW_ORD_STAT_TX_NON_DIR_DATA_B_11, + /* Hole */ + + IPW_ORD_STAT_TX_NON_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 44, + IPW_ORD_STAT_TX_NON_DIR_DATA_G_2, + IPW_ORD_STAT_TX_NON_DIR_DATA_G_5_5, + IPW_ORD_STAT_TX_NON_DIR_DATA_G_6, + IPW_ORD_STAT_TX_NON_DIR_DATA_G_9, + IPW_ORD_STAT_TX_NON_DIR_DATA_G_11, + IPW_ORD_STAT_TX_NON_DIR_DATA_G_12, + IPW_ORD_STAT_TX_NON_DIR_DATA_G_18, + IPW_ORD_STAT_TX_NON_DIR_DATA_G_24, + IPW_ORD_STAT_TX_NON_DIR_DATA_G_36, + IPW_ORD_STAT_TX_NON_DIR_DATA_G_48, + IPW_ORD_STAT_TX_NON_DIR_DATA_G_54, + IPW_ORD_STAT_TX_RETRY, + IPW_ORD_STAT_TX_FAILURE, + IPW_ORD_STAT_RX_ERR_CRC, + IPW_ORD_STAT_RX_ERR_ICV, + IPW_ORD_STAT_RX_NO_BUFFER, + IPW_ORD_STAT_FULL_SCANS, + IPW_ORD_STAT_PARTIAL_SCANS, + IPW_ORD_STAT_TGH_ABORTED_SCANS, + IPW_ORD_STAT_TX_TOTAL_BYTES, + IPW_ORD_STAT_CURR_RSSI_RAW, + IPW_ORD_STAT_RX_BEACON, + IPW_ORD_STAT_MISSED_BEACONS, + IPW_ORD_TABLE_0_LAST +}; + +#define IPW_RSSI_TO_DBM 112 + +/* Table 1 Entries + */ +enum { + IPW_ORD_TABLE_1_LAST = IPW_ORD_TABLE_1_MASK | 1, +}; + +/* + * Table 2 Entries + * + * FW_VERSION: 16 byte string + * FW_DATE: 16 byte string (only 14 bytes used) + * UCODE_VERSION: 4 byte version code + * UCODE_DATE: 5 bytes code code + * ADDAPTER_MAC: 6 byte MAC address + * RTC: 4 byte clock + */ +enum { + IPW_ORD_STAT_FW_VERSION = IPW_ORD_TABLE_2_MASK | 1, + IPW_ORD_STAT_FW_DATE, + IPW_ORD_STAT_UCODE_VERSION, + IPW_ORD_STAT_UCODE_DATE, + IPW_ORD_STAT_ADAPTER_MAC, + IPW_ORD_STAT_RTC, + IPW_ORD_TABLE_2_LAST +}; + +/* Table 3 */ +enum { + IPW_ORD_STAT_TX_PACKET = IPW_ORD_TABLE_3_MASK | 0, + IPW_ORD_STAT_TX_PACKET_FAILURE, + IPW_ORD_STAT_TX_PACKET_SUCCESS, + IPW_ORD_STAT_TX_PACKET_ABORTED, + IPW_ORD_TABLE_3_LAST +}; + +/* Table 4 */ +enum { + IPW_ORD_TABLE_4_LAST = IPW_ORD_TABLE_4_MASK +}; + +/* Table 5 */ +enum { + IPW_ORD_STAT_AVAILABLE_AP_COUNT = IPW_ORD_TABLE_5_MASK, + IPW_ORD_STAT_AP_ASSNS, + IPW_ORD_STAT_ROAM, + IPW_ORD_STAT_ROAM_CAUSE_MISSED_BEACONS, + IPW_ORD_STAT_ROAM_CAUSE_UNASSOC, + IPW_ORD_STAT_ROAM_CAUSE_RSSI, + IPW_ORD_STAT_ROAM_CAUSE_LINK_QUALITY, + IPW_ORD_STAT_ROAM_CAUSE_AP_LOAD_BALANCE, + IPW_ORD_STAT_ROAM_CAUSE_AP_NO_TX, + IPW_ORD_STAT_LINK_UP, + IPW_ORD_STAT_LINK_DOWN, + IPW_ORD_ANTENNA_DIVERSITY, + IPW_ORD_CURR_FREQ, + IPW_ORD_TABLE_5_LAST +}; + +/* Table 6 */ +enum { + IPW_ORD_COUNTRY_CODE = IPW_ORD_TABLE_6_MASK, + IPW_ORD_CURR_BSSID, + IPW_ORD_CURR_SSID, + IPW_ORD_TABLE_6_LAST +}; + +/* Table 7 */ +enum { + IPW_ORD_STAT_PERCENT_MISSED_BEACONS = IPW_ORD_TABLE_7_MASK, + IPW_ORD_STAT_PERCENT_TX_RETRIES, + IPW_ORD_STAT_PERCENT_LINK_QUALITY, + IPW_ORD_STAT_CURR_RSSI_DBM, + IPW_ORD_TABLE_7_LAST +}; + +#define IPW_ERROR_LOG (IPW_SHARED_LOWER_BOUND + 0x410) +#define IPW_EVENT_LOG (IPW_SHARED_LOWER_BOUND + 0x414) +#define IPW_ORDINALS_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0x500) +#define IPW_ORDINALS_TABLE_0 (IPW_SHARED_LOWER_BOUND + 0x180) +#define IPW_ORDINALS_TABLE_1 (IPW_SHARED_LOWER_BOUND + 0x184) +#define IPW_ORDINALS_TABLE_2 (IPW_SHARED_LOWER_BOUND + 0x188) +#define IPW_MEM_FIXED_OVERRIDE (IPW_SHARED_LOWER_BOUND + 0x41C) + +struct ipw_fixed_rate { + __le16 tx_rates; + __le16 reserved; +} __packed; + +#define IPW_INDIRECT_ADDR_MASK (~0x3ul) + +struct host_cmd { + u8 cmd; + u8 len; + u16 reserved; + u32 *param; +} __packed; /* XXX */ + +struct cmdlog_host_cmd { + u8 cmd; + u8 len; + __le16 reserved; + char param[124]; +} __packed; + +struct ipw_cmd_log { + unsigned long jiffies; + int retcode; + struct cmdlog_host_cmd cmd; +}; + +/* SysConfig command parameters ... */ +/* bt_coexistence param */ +#define CFG_BT_COEXISTENCE_SIGNAL_CHNL 0x01 /* tell BT our chnl # */ +#define CFG_BT_COEXISTENCE_DEFER 0x02 /* defer our Tx if BT traffic */ +#define CFG_BT_COEXISTENCE_KILL 0x04 /* kill our Tx if BT traffic */ +#define CFG_BT_COEXISTENCE_WME_OVER_BT 0x08 /* multimedia extensions */ +#define CFG_BT_COEXISTENCE_OOB 0x10 /* signal BT via out-of-band */ + +/* clear-to-send to self param */ +#define CFG_CTS_TO_ITSELF_ENABLED_MIN 0x00 +#define CFG_CTS_TO_ITSELF_ENABLED_MAX 0x01 +#define CFG_CTS_TO_ITSELF_ENABLED_DEF CFG_CTS_TO_ITSELF_ENABLED_MIN + +/* Antenna diversity param (h/w can select best antenna, based on signal) */ +#define CFG_SYS_ANTENNA_BOTH 0x00 /* NIC selects best antenna */ +#define CFG_SYS_ANTENNA_A 0x01 /* force antenna A */ +#define CFG_SYS_ANTENNA_B 0x03 /* force antenna B */ +#define CFG_SYS_ANTENNA_SLOW_DIV 0x02 /* consider background noise */ + +#define IPW_MAX_CONFIG_RETRIES 10 + +#endif /* __ipw2200_h__ */ diff --git a/drivers/net/wireless/intel/ipw2x00/libipw.h b/drivers/net/wireless/intel/ipw2x00/libipw.h new file mode 100644 index 000000000000..b0571618c2ed --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/libipw.h @@ -0,0 +1,1008 @@ +/* + * Merged with mainline ieee80211.h in Aug 2004. Original ieee802_11 + * remains copyright by the original authors + * + * Portions of the merged code are based on Host AP (software wireless + * LAN access point) driver for Intersil Prism2/2.5/3. + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * Adaption to a generic IEEE 802.11 stack by James Ketrenos + * + * Copyright (c) 2004-2005, Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + * + * API Version History + * 1.0.x -- Initial version + * 1.1.x -- Added radiotap, QoS, TIM, libipw_geo APIs, + * various structure changes, and crypto API init method + */ +#ifndef LIBIPW_H +#define LIBIPW_H +#include /* ETH_ALEN */ +#include /* ARRAY_SIZE */ +#include +#include + +#include +#include + +#define LIBIPW_VERSION "git-1.1.13" + +#define LIBIPW_DATA_LEN 2304 +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + 6.2.1.1.2. + + The figure in section 7.1.2 suggests a body size of up to 2312 + bytes is allowed, which is a bit confusing, I suspect this + represents the 2304 bytes of real data, plus a possible 8 bytes of + WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ + +#define LIBIPW_1ADDR_LEN 10 +#define LIBIPW_2ADDR_LEN 16 +#define LIBIPW_3ADDR_LEN 24 +#define LIBIPW_4ADDR_LEN 30 +#define LIBIPW_FCS_LEN 4 +#define LIBIPW_HLEN (LIBIPW_4ADDR_LEN) +#define LIBIPW_FRAME_LEN (LIBIPW_DATA_LEN + LIBIPW_HLEN) + +#define MIN_FRAG_THRESHOLD 256U +#define MAX_FRAG_THRESHOLD 2346U + +/* QOS control */ +#define LIBIPW_QCTL_TID 0x000F + +/* debug macros */ + +#ifdef CONFIG_LIBIPW_DEBUG +extern u32 libipw_debug_level; +#define LIBIPW_DEBUG(level, fmt, args...) \ +do { if (libipw_debug_level & (level)) \ + printk(KERN_DEBUG "libipw: %c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) +#else +#define LIBIPW_DEBUG(level, fmt, args...) do {} while (0) +#endif /* CONFIG_LIBIPW_DEBUG */ + +/* + * To use the debug system: + * + * If you are defining a new debug classification, simply add it to the #define + * list here in the form of: + * + * #define LIBIPW_DL_xxxx VALUE + * + * shifting value to the left one bit from the previous entry. xxxx should be + * the name of the classification (for example, WEP) + * + * You then need to either add a LIBIPW_xxxx_DEBUG() macro definition for your + * classification, or use LIBIPW_DEBUG(LIBIPW_DL_xxxx, ...) whenever you want + * to send output to that classification. + * + * To add your debug level to the list of levels seen when you perform + * + * % cat /proc/net/ieee80211/debug_level + * + * you simply need to add your entry to the libipw_debug_level array. + * + * If you do not see debug_level in /proc/net/ieee80211 then you do not have + * CONFIG_LIBIPW_DEBUG defined in your kernel configuration + * + */ + +#define LIBIPW_DL_INFO (1<<0) +#define LIBIPW_DL_WX (1<<1) +#define LIBIPW_DL_SCAN (1<<2) +#define LIBIPW_DL_STATE (1<<3) +#define LIBIPW_DL_MGMT (1<<4) +#define LIBIPW_DL_FRAG (1<<5) +#define LIBIPW_DL_DROP (1<<7) + +#define LIBIPW_DL_TX (1<<8) +#define LIBIPW_DL_RX (1<<9) +#define LIBIPW_DL_QOS (1<<31) + +#define LIBIPW_ERROR(f, a...) printk(KERN_ERR "libipw: " f, ## a) +#define LIBIPW_WARNING(f, a...) printk(KERN_WARNING "libipw: " f, ## a) +#define LIBIPW_DEBUG_INFO(f, a...) LIBIPW_DEBUG(LIBIPW_DL_INFO, f, ## a) + +#define LIBIPW_DEBUG_WX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_WX, f, ## a) +#define LIBIPW_DEBUG_SCAN(f, a...) LIBIPW_DEBUG(LIBIPW_DL_SCAN, f, ## a) +#define LIBIPW_DEBUG_STATE(f, a...) LIBIPW_DEBUG(LIBIPW_DL_STATE, f, ## a) +#define LIBIPW_DEBUG_MGMT(f, a...) LIBIPW_DEBUG(LIBIPW_DL_MGMT, f, ## a) +#define LIBIPW_DEBUG_FRAG(f, a...) LIBIPW_DEBUG(LIBIPW_DL_FRAG, f, ## a) +#define LIBIPW_DEBUG_DROP(f, a...) LIBIPW_DEBUG(LIBIPW_DL_DROP, f, ## a) +#define LIBIPW_DEBUG_TX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_TX, f, ## a) +#define LIBIPW_DEBUG_RX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_RX, f, ## a) +#define LIBIPW_DEBUG_QOS(f, a...) LIBIPW_DEBUG(LIBIPW_DL_QOS, f, ## a) +#include +#include /* ARPHRD_ETHER */ + +#ifndef WIRELESS_SPY +#define WIRELESS_SPY /* enable iwspy support */ +#endif +#include /* new driver API */ + +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW (ETH_P_ECONET + 1) +#endif + +/* IEEE 802.11 defines */ + +#define P80211_OUI_LEN 3 + +struct libipw_snap_hdr { + + u8 dsap; /* always 0xAA */ + u8 ssap; /* always 0xAA */ + u8 ctrl; /* always 0x03 */ + u8 oui[P80211_OUI_LEN]; /* organizational universal id */ + +} __packed; + +#define SNAP_SIZE sizeof(struct libipw_snap_hdr) + +#define WLAN_FC_GET_VERS(fc) ((fc) & IEEE80211_FCTL_VERS) +#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE) +#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG) +#define WLAN_GET_SEQ_SEQ(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) + +#define LIBIPW_STATMASK_SIGNAL (1<<0) +#define LIBIPW_STATMASK_RSSI (1<<1) +#define LIBIPW_STATMASK_NOISE (1<<2) +#define LIBIPW_STATMASK_RATE (1<<3) +#define LIBIPW_STATMASK_WEMASK 0x7 + +#define LIBIPW_CCK_MODULATION (1<<0) +#define LIBIPW_OFDM_MODULATION (1<<1) + +#define LIBIPW_24GHZ_BAND (1<<0) +#define LIBIPW_52GHZ_BAND (1<<1) + +#define LIBIPW_CCK_RATE_1MB 0x02 +#define LIBIPW_CCK_RATE_2MB 0x04 +#define LIBIPW_CCK_RATE_5MB 0x0B +#define LIBIPW_CCK_RATE_11MB 0x16 +#define LIBIPW_OFDM_RATE_6MB 0x0C +#define LIBIPW_OFDM_RATE_9MB 0x12 +#define LIBIPW_OFDM_RATE_12MB 0x18 +#define LIBIPW_OFDM_RATE_18MB 0x24 +#define LIBIPW_OFDM_RATE_24MB 0x30 +#define LIBIPW_OFDM_RATE_36MB 0x48 +#define LIBIPW_OFDM_RATE_48MB 0x60 +#define LIBIPW_OFDM_RATE_54MB 0x6C +#define LIBIPW_BASIC_RATE_MASK 0x80 + +#define LIBIPW_CCK_RATE_1MB_MASK (1<<0) +#define LIBIPW_CCK_RATE_2MB_MASK (1<<1) +#define LIBIPW_CCK_RATE_5MB_MASK (1<<2) +#define LIBIPW_CCK_RATE_11MB_MASK (1<<3) +#define LIBIPW_OFDM_RATE_6MB_MASK (1<<4) +#define LIBIPW_OFDM_RATE_9MB_MASK (1<<5) +#define LIBIPW_OFDM_RATE_12MB_MASK (1<<6) +#define LIBIPW_OFDM_RATE_18MB_MASK (1<<7) +#define LIBIPW_OFDM_RATE_24MB_MASK (1<<8) +#define LIBIPW_OFDM_RATE_36MB_MASK (1<<9) +#define LIBIPW_OFDM_RATE_48MB_MASK (1<<10) +#define LIBIPW_OFDM_RATE_54MB_MASK (1<<11) + +#define LIBIPW_CCK_RATES_MASK 0x0000000F +#define LIBIPW_CCK_BASIC_RATES_MASK (LIBIPW_CCK_RATE_1MB_MASK | \ + LIBIPW_CCK_RATE_2MB_MASK) +#define LIBIPW_CCK_DEFAULT_RATES_MASK (LIBIPW_CCK_BASIC_RATES_MASK | \ + LIBIPW_CCK_RATE_5MB_MASK | \ + LIBIPW_CCK_RATE_11MB_MASK) + +#define LIBIPW_OFDM_RATES_MASK 0x00000FF0 +#define LIBIPW_OFDM_BASIC_RATES_MASK (LIBIPW_OFDM_RATE_6MB_MASK | \ + LIBIPW_OFDM_RATE_12MB_MASK | \ + LIBIPW_OFDM_RATE_24MB_MASK) +#define LIBIPW_OFDM_DEFAULT_RATES_MASK (LIBIPW_OFDM_BASIC_RATES_MASK | \ + LIBIPW_OFDM_RATE_9MB_MASK | \ + LIBIPW_OFDM_RATE_18MB_MASK | \ + LIBIPW_OFDM_RATE_36MB_MASK | \ + LIBIPW_OFDM_RATE_48MB_MASK | \ + LIBIPW_OFDM_RATE_54MB_MASK) +#define LIBIPW_DEFAULT_RATES_MASK (LIBIPW_OFDM_DEFAULT_RATES_MASK | \ + LIBIPW_CCK_DEFAULT_RATES_MASK) + +#define LIBIPW_NUM_OFDM_RATES 8 +#define LIBIPW_NUM_CCK_RATES 4 +#define LIBIPW_OFDM_SHIFT_MASK_A 4 + +/* NOTE: This data is for statistical purposes; not all hardware provides this + * information for frames received. + * For libipw_rx_mgt, you need to set at least the 'len' parameter. + */ +struct libipw_rx_stats { + u32 mac_time; + s8 rssi; + u8 signal; + u8 noise; + u16 rate; /* in 100 kbps */ + u8 received_channel; + u8 control; + u8 mask; + u8 freq; + u16 len; + u64 tsf; + u32 beacon_time; +}; + +/* IEEE 802.11 requires that STA supports concurrent reception of at least + * three fragmented frames. This define can be increased to support more + * concurrent frames, but it should be noted that each entry can consume about + * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ +#define LIBIPW_FRAG_CACHE_LEN 4 + +struct libipw_frag_entry { + unsigned long first_frag_time; + unsigned int seq; + unsigned int last_frag; + struct sk_buff *skb; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; +}; + +struct libipw_stats { + unsigned int tx_unicast_frames; + unsigned int tx_multicast_frames; + unsigned int tx_fragments; + unsigned int tx_unicast_octets; + unsigned int tx_multicast_octets; + unsigned int tx_deferred_transmissions; + unsigned int tx_single_retry_frames; + unsigned int tx_multiple_retry_frames; + unsigned int tx_retry_limit_exceeded; + unsigned int tx_discards; + unsigned int rx_unicast_frames; + unsigned int rx_multicast_frames; + unsigned int rx_fragments; + unsigned int rx_unicast_octets; + unsigned int rx_multicast_octets; + unsigned int rx_fcs_errors; + unsigned int rx_discards_no_buffer; + unsigned int tx_discards_wrong_sa; + unsigned int rx_discards_undecryptable; + unsigned int rx_message_in_msg_fragments; + unsigned int rx_message_in_bad_msg_fragments; +}; + +struct libipw_device; + +#define SEC_KEY_1 (1<<0) +#define SEC_KEY_2 (1<<1) +#define SEC_KEY_3 (1<<2) +#define SEC_KEY_4 (1<<3) +#define SEC_ACTIVE_KEY (1<<4) +#define SEC_AUTH_MODE (1<<5) +#define SEC_UNICAST_GROUP (1<<6) +#define SEC_LEVEL (1<<7) +#define SEC_ENABLED (1<<8) +#define SEC_ENCRYPT (1<<9) + +#define SEC_LEVEL_0 0 /* None */ +#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */ +#define SEC_LEVEL_2 2 /* Level 1 + TKIP */ +#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */ +#define SEC_LEVEL_3 4 /* Level 2 + CCMP */ + +#define SEC_ALG_NONE 0 +#define SEC_ALG_WEP 1 +#define SEC_ALG_TKIP 2 +#define SEC_ALG_CCMP 3 + +#define WEP_KEYS 4 +#define WEP_KEY_LEN 13 +#define SCM_KEY_LEN 32 +#define SCM_TEMPORAL_KEY_LENGTH 16 + +struct libipw_security { + u16 active_key:2, enabled:1, unicast_uses_group:1, encrypt:1; + u8 auth_mode; + u8 encode_alg[WEP_KEYS]; + u8 key_sizes[WEP_KEYS]; + u8 keys[WEP_KEYS][SCM_KEY_LEN]; + u8 level; + u16 flags; +} __packed; + +/* + + 802.11 data frame from AP + + ,-------------------------------------------------------------------. +Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | + |------|------|---------|---------|---------|------|---------|------| +Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs | + | | tion | (BSSID) | | | ence | data | | + `-------------------------------------------------------------------' + +Total: 28-2340 bytes + +*/ + +#define BEACON_PROBE_SSID_ID_POSITION 12 + +struct libipw_hdr_1addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 payload[0]; +} __packed; + +struct libipw_hdr_2addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 payload[0]; +} __packed; + +struct libipw_hdr_3addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctl; + u8 payload[0]; +} __packed; + +struct libipw_hdr_4addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctl; + u8 addr4[ETH_ALEN]; + u8 payload[0]; +} __packed; + +struct libipw_hdr_3addrqos { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctl; + u8 payload[0]; + __le16 qos_ctl; +} __packed; + +struct libipw_info_element { + u8 id; + u8 len; + u8 data[0]; +} __packed; + +/* + * These are the data types that can make up management packets + * + u16 auth_algorithm; + u16 auth_sequence; + u16 beacon_interval; + u16 capability; + u8 current_ap[ETH_ALEN]; + u16 listen_interval; + struct { + u16 association_id:14, reserved:2; + } __packed; + u32 time_stamp[2]; + u16 reason; + u16 status; +*/ + +struct libipw_auth { + struct libipw_hdr_3addr header; + __le16 algorithm; + __le16 transaction; + __le16 status; + /* challenge */ + struct libipw_info_element info_element[0]; +} __packed; + +struct libipw_channel_switch { + u8 id; + u8 len; + u8 mode; + u8 channel; + u8 count; +} __packed; + +struct libipw_action { + struct libipw_hdr_3addr header; + u8 category; + u8 action; + union { + struct libipw_action_exchange { + u8 token; + struct libipw_info_element info_element[0]; + } exchange; + struct libipw_channel_switch channel_switch; + + } format; +} __packed; + +struct libipw_disassoc { + struct libipw_hdr_3addr header; + __le16 reason; +} __packed; + +/* Alias deauth for disassoc */ +#define libipw_deauth libipw_disassoc + +struct libipw_probe_request { + struct libipw_hdr_3addr header; + /* SSID, supported rates */ + struct libipw_info_element info_element[0]; +} __packed; + +struct libipw_probe_response { + struct libipw_hdr_3addr header; + __le32 time_stamp[2]; + __le16 beacon_interval; + __le16 capability; + /* SSID, supported rates, FH params, DS params, + * CF params, IBSS params, TIM (if beacon), RSN */ + struct libipw_info_element info_element[0]; +} __packed; + +/* Alias beacon for probe_response */ +#define libipw_beacon libipw_probe_response + +struct libipw_assoc_request { + struct libipw_hdr_3addr header; + __le16 capability; + __le16 listen_interval; + /* SSID, supported rates, RSN */ + struct libipw_info_element info_element[0]; +} __packed; + +struct libipw_reassoc_request { + struct libipw_hdr_3addr header; + __le16 capability; + __le16 listen_interval; + u8 current_ap[ETH_ALEN]; + struct libipw_info_element info_element[0]; +} __packed; + +struct libipw_assoc_response { + struct libipw_hdr_3addr header; + __le16 capability; + __le16 status; + __le16 aid; + /* supported rates */ + struct libipw_info_element info_element[0]; +} __packed; + +struct libipw_txb { + u8 nr_frags; + u8 encrypted; + u8 rts_included; + u8 reserved; + u16 frag_size; + u16 payload_size; + struct sk_buff *fragments[0]; +}; + +/* SWEEP TABLE ENTRIES NUMBER */ +#define MAX_SWEEP_TAB_ENTRIES 42 +#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7 +/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs + * only use 8, and then use extended rates for the remaining supported + * rates. Other APs, however, stick all of their supported rates on the + * main rates information element... */ +#define MAX_RATES_LENGTH ((u8)12) +#define MAX_RATES_EX_LENGTH ((u8)16) +#define MAX_NETWORK_COUNT 128 + +#define CRC_LENGTH 4U + +#define MAX_WPA_IE_LEN 64 + +#define NETWORK_HAS_OFDM (1<<1) +#define NETWORK_HAS_CCK (1<<2) + +/* QoS structure */ +#define NETWORK_HAS_QOS_PARAMETERS (1<<3) +#define NETWORK_HAS_QOS_INFORMATION (1<<4) +#define NETWORK_HAS_QOS_MASK (NETWORK_HAS_QOS_PARAMETERS | \ + NETWORK_HAS_QOS_INFORMATION) + +/* 802.11h */ +#define NETWORK_HAS_POWER_CONSTRAINT (1<<5) +#define NETWORK_HAS_CSA (1<<6) +#define NETWORK_HAS_QUIET (1<<7) +#define NETWORK_HAS_IBSS_DFS (1<<8) +#define NETWORK_HAS_TPC_REPORT (1<<9) + +#define NETWORK_HAS_ERP_VALUE (1<<10) + +#define QOS_QUEUE_NUM 4 +#define QOS_OUI_LEN 3 +#define QOS_OUI_TYPE 2 +#define QOS_ELEMENT_ID 221 +#define QOS_OUI_INFO_SUB_TYPE 0 +#define QOS_OUI_PARAM_SUB_TYPE 1 +#define QOS_VERSION_1 1 +#define QOS_AIFSN_MIN_VALUE 2 + +struct libipw_qos_information_element { + u8 elementID; + u8 length; + u8 qui[QOS_OUI_LEN]; + u8 qui_type; + u8 qui_subtype; + u8 version; + u8 ac_info; +} __packed; + +struct libipw_qos_ac_parameter { + u8 aci_aifsn; + u8 ecw_min_max; + __le16 tx_op_limit; +} __packed; + +struct libipw_qos_parameter_info { + struct libipw_qos_information_element info_element; + u8 reserved; + struct libipw_qos_ac_parameter ac_params_record[QOS_QUEUE_NUM]; +} __packed; + +struct libipw_qos_parameters { + __le16 cw_min[QOS_QUEUE_NUM]; + __le16 cw_max[QOS_QUEUE_NUM]; + u8 aifs[QOS_QUEUE_NUM]; + u8 flag[QOS_QUEUE_NUM]; + __le16 tx_op_limit[QOS_QUEUE_NUM]; +} __packed; + +struct libipw_qos_data { + struct libipw_qos_parameters parameters; + int active; + int supported; + u8 param_count; + u8 old_param_count; +}; + +struct libipw_tim_parameters { + u8 tim_count; + u8 tim_period; +} __packed; + +/*******************************************************/ + +struct libipw_tpc_report { + u8 transmit_power; + u8 link_margin; +} __packed; + +struct libipw_channel_map { + u8 channel; + u8 map; +} __packed; + +struct libipw_ibss_dfs { + struct libipw_info_element ie; + u8 owner[ETH_ALEN]; + u8 recovery_interval; + struct libipw_channel_map channel_map[0]; +}; + +struct libipw_csa { + u8 mode; + u8 channel; + u8 count; +} __packed; + +struct libipw_quiet { + u8 count; + u8 period; + u8 duration; + u8 offset; +} __packed; + +struct libipw_network { + /* These entries are used to identify a unique network */ + u8 bssid[ETH_ALEN]; + u8 channel; + /* Ensure null-terminated for any debug msgs */ + u8 ssid[IW_ESSID_MAX_SIZE + 1]; + u8 ssid_len; + + struct libipw_qos_data qos_data; + + /* These are network statistics */ + struct libipw_rx_stats stats; + u16 capability; + u8 rates[MAX_RATES_LENGTH]; + u8 rates_len; + u8 rates_ex[MAX_RATES_EX_LENGTH]; + u8 rates_ex_len; + unsigned long last_scanned; + u8 mode; + u32 flags; + u32 last_associate; + u32 time_stamp[2]; + u16 beacon_interval; + u16 listen_interval; + u16 atim_window; + u8 erp_value; + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + struct libipw_tim_parameters tim; + + /* 802.11h info */ + + /* Power Constraint - mandatory if spctrm mgmt required */ + u8 power_constraint; + + /* TPC Report - mandatory if spctrm mgmt required */ + struct libipw_tpc_report tpc_report; + + /* Channel Switch Announcement - optional if spctrm mgmt required */ + struct libipw_csa csa; + + /* Quiet - optional if spctrm mgmt required */ + struct libipw_quiet quiet; + + struct list_head list; +}; + +enum libipw_state { + LIBIPW_UNINITIALIZED = 0, + LIBIPW_INITIALIZED, + LIBIPW_ASSOCIATING, + LIBIPW_ASSOCIATED, + LIBIPW_AUTHENTICATING, + LIBIPW_AUTHENTICATED, + LIBIPW_SHUTDOWN +}; + +#define DEFAULT_MAX_SCAN_AGE (15 * HZ) +#define DEFAULT_FTS 2346 + +#define CFG_LIBIPW_RESERVE_FCS (1<<0) +#define CFG_LIBIPW_COMPUTE_FCS (1<<1) +#define CFG_LIBIPW_RTS (1<<2) + +#define LIBIPW_24GHZ_MIN_CHANNEL 1 +#define LIBIPW_24GHZ_MAX_CHANNEL 14 +#define LIBIPW_24GHZ_CHANNELS (LIBIPW_24GHZ_MAX_CHANNEL - \ + LIBIPW_24GHZ_MIN_CHANNEL + 1) + +#define LIBIPW_52GHZ_MIN_CHANNEL 34 +#define LIBIPW_52GHZ_MAX_CHANNEL 165 +#define LIBIPW_52GHZ_CHANNELS (LIBIPW_52GHZ_MAX_CHANNEL - \ + LIBIPW_52GHZ_MIN_CHANNEL + 1) + +enum { + LIBIPW_CH_PASSIVE_ONLY = (1 << 0), + LIBIPW_CH_80211H_RULES = (1 << 1), + LIBIPW_CH_B_ONLY = (1 << 2), + LIBIPW_CH_NO_IBSS = (1 << 3), + LIBIPW_CH_UNIFORM_SPREADING = (1 << 4), + LIBIPW_CH_RADAR_DETECT = (1 << 5), + LIBIPW_CH_INVALID = (1 << 6), +}; + +struct libipw_channel { + u32 freq; /* in MHz */ + u8 channel; + u8 flags; + u8 max_power; /* in dBm */ +}; + +struct libipw_geo { + u8 name[4]; + u8 bg_channels; + u8 a_channels; + struct libipw_channel bg[LIBIPW_24GHZ_CHANNELS]; + struct libipw_channel a[LIBIPW_52GHZ_CHANNELS]; +}; + +struct libipw_device { + struct net_device *dev; + struct wireless_dev wdev; + struct libipw_security sec; + + /* Bookkeeping structures */ + struct libipw_stats ieee_stats; + + struct libipw_geo geo; + struct ieee80211_supported_band bg_band; + struct ieee80211_supported_band a_band; + + /* Probe / Beacon management */ + struct list_head network_free_list; + struct list_head network_list; + struct libipw_network *networks[MAX_NETWORK_COUNT]; + int scans; + int scan_age; + + int iw_mode; /* operating mode (IW_MODE_*) */ + struct iw_spy_data spy_data; /* iwspy support */ + + spinlock_t lock; + + int tx_headroom; /* Set to size of any additional room needed at front + * of allocated Tx SKBs */ + u32 config; + + /* WEP and other encryption related settings at the device level */ + int open_wep; /* Set to 1 to allow unencrypted frames */ + + /* If the host performs {en,de}cryption, then set to 1 */ + int host_encrypt; + int host_encrypt_msdu; + int host_decrypt; + /* host performs multicast decryption */ + int host_mc_decrypt; + + /* host should strip IV and ICV from protected frames */ + /* meaningful only when hardware decryption is being used */ + int host_strip_iv_icv; + + int host_open_frag; + int ieee802_1x; /* is IEEE 802.1X used */ + + /* WPA data */ + int wpa_enabled; + int drop_unencrypted; + int privacy_invoked; + size_t wpa_ie_len; + u8 *wpa_ie; + + struct lib80211_crypt_info crypt_info; + + int bcrx_sta_key; /* use individual keys to override default keys even + * with RX of broad/multicast frames */ + + /* Fragmentation structures */ + struct libipw_frag_entry frag_cache[LIBIPW_FRAG_CACHE_LEN]; + unsigned int frag_next_idx; + u16 fts; /* Fragmentation Threshold */ + u16 rts; /* RTS threshold */ + + /* Association info */ + u8 bssid[ETH_ALEN]; + + enum libipw_state state; + + int mode; /* A, B, G */ + int modulation; /* CCK, OFDM */ + int freq_band; /* 2.4Ghz, 5.2Ghz, Mixed */ + int abg_true; /* ABG flag */ + + int perfect_rssi; + int worst_rssi; + + u16 prev_seq_ctl; /* used to drop duplicate frames */ + + /* Callback functions */ + void (*set_security) (struct net_device * dev, + struct libipw_security * sec); + netdev_tx_t (*hard_start_xmit) (struct libipw_txb * txb, + struct net_device * dev, int pri); + int (*is_queue_full) (struct net_device * dev, int pri); + + int (*handle_management) (struct net_device * dev, + struct libipw_network * network, u16 type); + int (*is_qos_active) (struct net_device *dev, struct sk_buff *skb); + + /* Typical STA methods */ + int (*handle_auth) (struct net_device * dev, + struct libipw_auth * auth); + int (*handle_deauth) (struct net_device * dev, + struct libipw_deauth * auth); + int (*handle_action) (struct net_device * dev, + struct libipw_action * action, + struct libipw_rx_stats * stats); + int (*handle_disassoc) (struct net_device * dev, + struct libipw_disassoc * assoc); + int (*handle_beacon) (struct net_device * dev, + struct libipw_beacon * beacon, + struct libipw_network * network); + int (*handle_probe_response) (struct net_device * dev, + struct libipw_probe_response * resp, + struct libipw_network * network); + int (*handle_probe_request) (struct net_device * dev, + struct libipw_probe_request * req, + struct libipw_rx_stats * stats); + int (*handle_assoc_response) (struct net_device * dev, + struct libipw_assoc_response * resp, + struct libipw_network * network); + + /* Typical AP methods */ + int (*handle_assoc_request) (struct net_device * dev); + int (*handle_reassoc_request) (struct net_device * dev, + struct libipw_reassoc_request * req); + + /* This must be the last item so that it points to the data + * allocated beyond this structure by alloc_libipw */ + u8 priv[0]; +}; + +#define IEEE_A (1<<0) +#define IEEE_B (1<<1) +#define IEEE_G (1<<2) +#define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G) + +static inline void *libipw_priv(struct net_device *dev) +{ + return ((struct libipw_device *)netdev_priv(dev))->priv; +} + +static inline int libipw_is_valid_mode(struct libipw_device *ieee, + int mode) +{ + /* + * It is possible for both access points and our device to support + * combinations of modes, so as long as there is one valid combination + * of ap/device supported modes, then return success + * + */ + if ((mode & IEEE_A) && + (ieee->modulation & LIBIPW_OFDM_MODULATION) && + (ieee->freq_band & LIBIPW_52GHZ_BAND)) + return 1; + + if ((mode & IEEE_G) && + (ieee->modulation & LIBIPW_OFDM_MODULATION) && + (ieee->freq_band & LIBIPW_24GHZ_BAND)) + return 1; + + if ((mode & IEEE_B) && + (ieee->modulation & LIBIPW_CCK_MODULATION) && + (ieee->freq_band & LIBIPW_24GHZ_BAND)) + return 1; + + return 0; +} + +static inline int libipw_get_hdrlen(u16 fc) +{ + int hdrlen = LIBIPW_3ADDR_LEN; + u16 stype = WLAN_FC_GET_STYPE(fc); + + switch (WLAN_FC_GET_TYPE(fc)) { + case IEEE80211_FTYPE_DATA: + if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS)) + hdrlen = LIBIPW_4ADDR_LEN; + if (stype & IEEE80211_STYPE_QOS_DATA) + hdrlen += 2; + break; + case IEEE80211_FTYPE_CTL: + switch (WLAN_FC_GET_STYPE(fc)) { + case IEEE80211_STYPE_CTS: + case IEEE80211_STYPE_ACK: + hdrlen = LIBIPW_1ADDR_LEN; + break; + default: + hdrlen = LIBIPW_2ADDR_LEN; + break; + } + break; + } + + return hdrlen; +} + +static inline u8 *libipw_get_payload(struct ieee80211_hdr *hdr) +{ + switch (libipw_get_hdrlen(le16_to_cpu(hdr->frame_control))) { + case LIBIPW_1ADDR_LEN: + return ((struct libipw_hdr_1addr *)hdr)->payload; + case LIBIPW_2ADDR_LEN: + return ((struct libipw_hdr_2addr *)hdr)->payload; + case LIBIPW_3ADDR_LEN: + return ((struct libipw_hdr_3addr *)hdr)->payload; + case LIBIPW_4ADDR_LEN: + return ((struct libipw_hdr_4addr *)hdr)->payload; + } + return NULL; +} + +static inline int libipw_is_ofdm_rate(u8 rate) +{ + switch (rate & ~LIBIPW_BASIC_RATE_MASK) { + case LIBIPW_OFDM_RATE_6MB: + case LIBIPW_OFDM_RATE_9MB: + case LIBIPW_OFDM_RATE_12MB: + case LIBIPW_OFDM_RATE_18MB: + case LIBIPW_OFDM_RATE_24MB: + case LIBIPW_OFDM_RATE_36MB: + case LIBIPW_OFDM_RATE_48MB: + case LIBIPW_OFDM_RATE_54MB: + return 1; + } + return 0; +} + +static inline int libipw_is_cck_rate(u8 rate) +{ + switch (rate & ~LIBIPW_BASIC_RATE_MASK) { + case LIBIPW_CCK_RATE_1MB: + case LIBIPW_CCK_RATE_2MB: + case LIBIPW_CCK_RATE_5MB: + case LIBIPW_CCK_RATE_11MB: + return 1; + } + return 0; +} + +/* libipw.c */ +void free_libipw(struct net_device *dev, int monitor); +struct net_device *alloc_libipw(int sizeof_priv, int monitor); +int libipw_change_mtu(struct net_device *dev, int new_mtu); + +void libipw_networks_age(struct libipw_device *ieee, unsigned long age_secs); + +int libipw_set_encryption(struct libipw_device *ieee); + +/* libipw_tx.c */ +netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev); +void libipw_txb_free(struct libipw_txb *); + +/* libipw_rx.c */ +void libipw_rx_any(struct libipw_device *ieee, struct sk_buff *skb, + struct libipw_rx_stats *stats); +int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, + struct libipw_rx_stats *rx_stats); +/* make sure to set stats->len */ +void libipw_rx_mgt(struct libipw_device *ieee, struct libipw_hdr_4addr *header, + struct libipw_rx_stats *stats); + +/* libipw_geo.c */ +const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee); +void libipw_set_geo(struct libipw_device *ieee, const struct libipw_geo *geo); + +int libipw_is_valid_channel(struct libipw_device *ieee, u8 channel); +int libipw_channel_to_index(struct libipw_device *ieee, u8 channel); +u8 libipw_freq_to_channel(struct libipw_device *ieee, u32 freq); +u8 libipw_get_channel_flags(struct libipw_device *ieee, u8 channel); +const struct libipw_channel *libipw_get_channel(struct libipw_device *ieee, + u8 channel); +u32 libipw_channel_to_freq(struct libipw_device *ieee, u8 channel); + +/* libipw_wx.c */ +int libipw_wx_get_scan(struct libipw_device *ieee, struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +int libipw_wx_set_encode(struct libipw_device *ieee, + struct iw_request_info *info, union iwreq_data *wrqu, + char *key); +int libipw_wx_get_encode(struct libipw_device *ieee, + struct iw_request_info *info, union iwreq_data *wrqu, + char *key); +int libipw_wx_set_encodeext(struct libipw_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); +int libipw_wx_get_encodeext(struct libipw_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +static inline void libipw_increment_scans(struct libipw_device *ieee) +{ + ieee->scans++; +} + +static inline int libipw_get_scans(struct libipw_device *ieee) +{ + return ieee->scans; +} + +#endif /* LIBIPW_H */ diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_geo.c b/drivers/net/wireless/intel/ipw2x00/libipw_geo.c new file mode 100644 index 000000000000..218f2a32de21 --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/libipw_geo.c @@ -0,0 +1,193 @@ +/****************************************************************************** + + Copyright(c) 2005 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Intel Linux Wireless + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libipw.h" + +int libipw_is_valid_channel(struct libipw_device *ieee, u8 channel) +{ + int i; + + /* Driver needs to initialize the geography map before using + * these helper functions */ + if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) + return 0; + + if (ieee->freq_band & LIBIPW_24GHZ_BAND) + for (i = 0; i < ieee->geo.bg_channels; i++) + /* NOTE: If G mode is currently supported but + * this is a B only channel, we don't see it + * as valid. */ + if ((ieee->geo.bg[i].channel == channel) && + !(ieee->geo.bg[i].flags & LIBIPW_CH_INVALID) && + (!(ieee->mode & IEEE_G) || + !(ieee->geo.bg[i].flags & LIBIPW_CH_B_ONLY))) + return LIBIPW_24GHZ_BAND; + + if (ieee->freq_band & LIBIPW_52GHZ_BAND) + for (i = 0; i < ieee->geo.a_channels; i++) + if ((ieee->geo.a[i].channel == channel) && + !(ieee->geo.a[i].flags & LIBIPW_CH_INVALID)) + return LIBIPW_52GHZ_BAND; + + return 0; +} + +int libipw_channel_to_index(struct libipw_device *ieee, u8 channel) +{ + int i; + + /* Driver needs to initialize the geography map before using + * these helper functions */ + if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) + return -1; + + if (ieee->freq_band & LIBIPW_24GHZ_BAND) + for (i = 0; i < ieee->geo.bg_channels; i++) + if (ieee->geo.bg[i].channel == channel) + return i; + + if (ieee->freq_band & LIBIPW_52GHZ_BAND) + for (i = 0; i < ieee->geo.a_channels; i++) + if (ieee->geo.a[i].channel == channel) + return i; + + return -1; +} + +u32 libipw_channel_to_freq(struct libipw_device * ieee, u8 channel) +{ + const struct libipw_channel * ch; + + /* Driver needs to initialize the geography map before using + * these helper functions */ + if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) + return 0; + + ch = libipw_get_channel(ieee, channel); + if (!ch->channel) + return 0; + return ch->freq; +} + +u8 libipw_freq_to_channel(struct libipw_device * ieee, u32 freq) +{ + int i; + + /* Driver needs to initialize the geography map before using + * these helper functions */ + if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) + return 0; + + freq /= 100000; + + if (ieee->freq_band & LIBIPW_24GHZ_BAND) + for (i = 0; i < ieee->geo.bg_channels; i++) + if (ieee->geo.bg[i].freq == freq) + return ieee->geo.bg[i].channel; + + if (ieee->freq_band & LIBIPW_52GHZ_BAND) + for (i = 0; i < ieee->geo.a_channels; i++) + if (ieee->geo.a[i].freq == freq) + return ieee->geo.a[i].channel; + + return 0; +} + +void libipw_set_geo(struct libipw_device *ieee, + const struct libipw_geo *geo) +{ + memcpy(ieee->geo.name, geo->name, 3); + ieee->geo.name[3] = '\0'; + ieee->geo.bg_channels = geo->bg_channels; + ieee->geo.a_channels = geo->a_channels; + memcpy(ieee->geo.bg, geo->bg, geo->bg_channels * + sizeof(struct libipw_channel)); + memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels * + sizeof(struct libipw_channel)); +} + +const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee) +{ + return &ieee->geo; +} + +u8 libipw_get_channel_flags(struct libipw_device * ieee, u8 channel) +{ + int index = libipw_channel_to_index(ieee, channel); + + if (index == -1) + return LIBIPW_CH_INVALID; + + if (channel <= LIBIPW_24GHZ_CHANNELS) + return ieee->geo.bg[index].flags; + + return ieee->geo.a[index].flags; +} + +static const struct libipw_channel bad_channel = { + .channel = 0, + .flags = LIBIPW_CH_INVALID, + .max_power = 0, +}; + +const struct libipw_channel *libipw_get_channel(struct libipw_device + *ieee, u8 channel) +{ + int index = libipw_channel_to_index(ieee, channel); + + if (index == -1) + return &bad_channel; + + if (channel <= LIBIPW_24GHZ_CHANNELS) + return &ieee->geo.bg[index]; + + return &ieee->geo.a[index]; +} + +EXPORT_SYMBOL(libipw_get_channel); +EXPORT_SYMBOL(libipw_get_channel_flags); +EXPORT_SYMBOL(libipw_is_valid_channel); +EXPORT_SYMBOL(libipw_freq_to_channel); +EXPORT_SYMBOL(libipw_channel_to_freq); +EXPORT_SYMBOL(libipw_channel_to_index); +EXPORT_SYMBOL(libipw_set_geo); +EXPORT_SYMBOL(libipw_get_geo); diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_module.c b/drivers/net/wireless/intel/ipw2x00/libipw_module.c new file mode 100644 index 000000000000..60f28740f6af --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/libipw_module.c @@ -0,0 +1,321 @@ +/******************************************************************************* + + Copyright(c) 2004-2005 Intel Corporation. All rights reserved. + + Portions of this file are based on the WEP enablement code provided by the + Host AP project hostap-drivers v0.1.3 + Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + + Copyright (c) 2002-2003, Jouni Malinen + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Intel Linux Wireless + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libipw.h" + +#define DRV_DESCRIPTION "802.11 data/management/control stack" +#define DRV_NAME "libipw" +#define DRV_PROCNAME "ieee80211" +#define DRV_VERSION LIBIPW_VERSION +#define DRV_COPYRIGHT "Copyright (C) 2004-2005 Intel Corporation " + +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR(DRV_COPYRIGHT); +MODULE_LICENSE("GPL"); + +static struct cfg80211_ops libipw_config_ops = { }; +static void *libipw_wiphy_privid = &libipw_wiphy_privid; + +static int libipw_networks_allocate(struct libipw_device *ieee) +{ + int i, j; + + for (i = 0; i < MAX_NETWORK_COUNT; i++) { + ieee->networks[i] = kzalloc(sizeof(struct libipw_network), + GFP_KERNEL); + if (!ieee->networks[i]) { + LIBIPW_ERROR("Out of memory allocating beacons\n"); + for (j = 0; j < i; j++) + kfree(ieee->networks[j]); + return -ENOMEM; + } + } + + return 0; +} + +static inline void libipw_networks_free(struct libipw_device *ieee) +{ + int i; + + for (i = 0; i < MAX_NETWORK_COUNT; i++) + kfree(ieee->networks[i]); +} + +void libipw_networks_age(struct libipw_device *ieee, + unsigned long age_secs) +{ + struct libipw_network *network = NULL; + unsigned long flags; + unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); + + spin_lock_irqsave(&ieee->lock, flags); + list_for_each_entry(network, &ieee->network_list, list) { + network->last_scanned -= age_jiffies; + } + spin_unlock_irqrestore(&ieee->lock, flags); +} +EXPORT_SYMBOL(libipw_networks_age); + +static void libipw_networks_initialize(struct libipw_device *ieee) +{ + int i; + + INIT_LIST_HEAD(&ieee->network_free_list); + INIT_LIST_HEAD(&ieee->network_list); + for (i = 0; i < MAX_NETWORK_COUNT; i++) + list_add_tail(&ieee->networks[i]->list, + &ieee->network_free_list); +} + +int libipw_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > LIBIPW_DATA_LEN)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} +EXPORT_SYMBOL(libipw_change_mtu); + +struct net_device *alloc_libipw(int sizeof_priv, int monitor) +{ + struct libipw_device *ieee; + struct net_device *dev; + int err; + + LIBIPW_DEBUG_INFO("Initializing...\n"); + + dev = alloc_etherdev(sizeof(struct libipw_device) + sizeof_priv); + if (!dev) + goto failed; + + ieee = netdev_priv(dev); + + ieee->dev = dev; + + if (!monitor) { + ieee->wdev.wiphy = wiphy_new(&libipw_config_ops, 0); + if (!ieee->wdev.wiphy) { + LIBIPW_ERROR("Unable to allocate wiphy.\n"); + goto failed_free_netdev; + } + + ieee->dev->ieee80211_ptr = &ieee->wdev; + ieee->wdev.iftype = NL80211_IFTYPE_STATION; + + /* Fill-out wiphy structure bits we know... Not enough info + here to call set_wiphy_dev or set MAC address or channel info + -- have to do that in ->ndo_init... */ + ieee->wdev.wiphy->privid = libipw_wiphy_privid; + + ieee->wdev.wiphy->max_scan_ssids = 1; + ieee->wdev.wiphy->max_scan_ie_len = 0; + ieee->wdev.wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_ADHOC); + } + + err = libipw_networks_allocate(ieee); + if (err) { + LIBIPW_ERROR("Unable to allocate beacon storage: %d\n", err); + goto failed_free_wiphy; + } + libipw_networks_initialize(ieee); + + /* Default fragmentation threshold is maximum payload size */ + ieee->fts = DEFAULT_FTS; + ieee->rts = DEFAULT_FTS; + ieee->scan_age = DEFAULT_MAX_SCAN_AGE; + ieee->open_wep = 1; + + /* Default to enabling full open WEP with host based encrypt/decrypt */ + ieee->host_encrypt = 1; + ieee->host_decrypt = 1; + ieee->host_mc_decrypt = 1; + + /* Host fragmentation in Open mode. Default is enabled. + * Note: host fragmentation is always enabled if host encryption + * is enabled. For cards can do hardware encryption, they must do + * hardware fragmentation as well. So we don't need a variable + * like host_enc_frag. */ + ieee->host_open_frag = 1; + ieee->ieee802_1x = 1; /* Default to supporting 802.1x */ + + spin_lock_init(&ieee->lock); + + lib80211_crypt_info_init(&ieee->crypt_info, dev->name, &ieee->lock); + + ieee->wpa_enabled = 0; + ieee->drop_unencrypted = 0; + ieee->privacy_invoked = 0; + + return dev; + +failed_free_wiphy: + if (!monitor) + wiphy_free(ieee->wdev.wiphy); +failed_free_netdev: + free_netdev(dev); +failed: + return NULL; +} +EXPORT_SYMBOL(alloc_libipw); + +void free_libipw(struct net_device *dev, int monitor) +{ + struct libipw_device *ieee = netdev_priv(dev); + + lib80211_crypt_info_free(&ieee->crypt_info); + + libipw_networks_free(ieee); + + /* free cfg80211 resources */ + if (!monitor) + wiphy_free(ieee->wdev.wiphy); + + free_netdev(dev); +} +EXPORT_SYMBOL(free_libipw); + +#ifdef CONFIG_LIBIPW_DEBUG + +static int debug = 0; +u32 libipw_debug_level = 0; +EXPORT_SYMBOL_GPL(libipw_debug_level); +static struct proc_dir_entry *libipw_proc = NULL; + +static int debug_level_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "0x%08X\n", libipw_debug_level); + return 0; +} + +static int debug_level_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, debug_level_proc_show, NULL); +} + +static ssize_t debug_level_proc_write(struct file *file, + const char __user *buffer, size_t count, loff_t *pos) +{ + char buf[] = "0x00000000\n"; + size_t len = min(sizeof(buf) - 1, count); + unsigned long val; + + if (copy_from_user(buf, buffer, len)) + return count; + buf[len] = 0; + if (sscanf(buf, "%li", &val) != 1) + printk(KERN_INFO DRV_NAME + ": %s is not in hex or decimal form.\n", buf); + else + libipw_debug_level = val; + + return strnlen(buf, len); +} + +static const struct file_operations debug_level_proc_fops = { + .owner = THIS_MODULE, + .open = debug_level_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = debug_level_proc_write, +}; +#endif /* CONFIG_LIBIPW_DEBUG */ + +static int __init libipw_init(void) +{ +#ifdef CONFIG_LIBIPW_DEBUG + struct proc_dir_entry *e; + + libipw_debug_level = debug; + libipw_proc = proc_mkdir(DRV_PROCNAME, init_net.proc_net); + if (libipw_proc == NULL) { + LIBIPW_ERROR("Unable to create " DRV_PROCNAME + " proc directory\n"); + return -EIO; + } + e = proc_create("debug_level", S_IRUGO | S_IWUSR, libipw_proc, + &debug_level_proc_fops); + if (!e) { + remove_proc_entry(DRV_PROCNAME, init_net.proc_net); + libipw_proc = NULL; + return -EIO; + } +#endif /* CONFIG_LIBIPW_DEBUG */ + + printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n"); + printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n"); + + return 0; +} + +static void __exit libipw_exit(void) +{ +#ifdef CONFIG_LIBIPW_DEBUG + if (libipw_proc) { + remove_proc_entry("debug_level", libipw_proc); + remove_proc_entry(DRV_PROCNAME, init_net.proc_net); + libipw_proc = NULL; + } +#endif /* CONFIG_LIBIPW_DEBUG */ +} + +#ifdef CONFIG_LIBIPW_DEBUG +#include +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "debug output mask"); +#endif /* CONFIG_LIBIPW_DEBUG */ + +module_exit(libipw_exit); +module_init(libipw_init); diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_rx.c b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c new file mode 100644 index 000000000000..cef7f7d79cd9 --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c @@ -0,0 +1,1765 @@ +/* + * Original code based Host AP (software wireless LAN access point) driver + * for Intersil Prism2/2.5/3 - hostap.o module, common routines + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * Copyright (c) 2004-2005, Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libipw.h" + +static void libipw_monitor_rx(struct libipw_device *ieee, + struct sk_buff *skb, + struct libipw_rx_stats *rx_stats) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + u16 fc = le16_to_cpu(hdr->frame_control); + + skb->dev = ieee->dev; + skb_reset_mac_header(skb); + skb_pull(skb, libipw_get_hdrlen(fc)); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_80211_RAW); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + +/* Called only as a tasklet (software IRQ) */ +static struct libipw_frag_entry *libipw_frag_cache_find(struct + libipw_device + *ieee, + unsigned int seq, + unsigned int frag, + u8 * src, + u8 * dst) +{ + struct libipw_frag_entry *entry; + int i; + + for (i = 0; i < LIBIPW_FRAG_CACHE_LEN; i++) { + entry = &ieee->frag_cache[i]; + if (entry->skb != NULL && + time_after(jiffies, entry->first_frag_time + 2 * HZ)) { + LIBIPW_DEBUG_FRAG("expiring fragment cache entry " + "seq=%u last_frag=%u\n", + entry->seq, entry->last_frag); + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; + } + + if (entry->skb != NULL && entry->seq == seq && + (entry->last_frag + 1 == frag || frag == -1) && + ether_addr_equal(entry->src_addr, src) && + ether_addr_equal(entry->dst_addr, dst)) + return entry; + } + + return NULL; +} + +/* Called only as a tasklet (software IRQ) */ +static struct sk_buff *libipw_frag_cache_get(struct libipw_device *ieee, + struct libipw_hdr_4addr *hdr) +{ + struct sk_buff *skb = NULL; + u16 sc; + unsigned int frag, seq; + struct libipw_frag_entry *entry; + + sc = le16_to_cpu(hdr->seq_ctl); + frag = WLAN_GET_SEQ_FRAG(sc); + seq = WLAN_GET_SEQ_SEQ(sc); + + if (frag == 0) { + /* Reserve enough space to fit maximum frame length */ + skb = dev_alloc_skb(ieee->dev->mtu + + sizeof(struct libipw_hdr_4addr) + + 8 /* LLC */ + + 2 /* alignment */ + + 8 /* WEP */ + ETH_ALEN /* WDS */ ); + if (skb == NULL) + return NULL; + + entry = &ieee->frag_cache[ieee->frag_next_idx]; + ieee->frag_next_idx++; + if (ieee->frag_next_idx >= LIBIPW_FRAG_CACHE_LEN) + ieee->frag_next_idx = 0; + + if (entry->skb != NULL) + dev_kfree_skb_any(entry->skb); + + entry->first_frag_time = jiffies; + entry->seq = seq; + entry->last_frag = frag; + entry->skb = skb; + memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); + memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); + } else { + /* received a fragment of a frame for which the head fragment + * should have already been received */ + entry = libipw_frag_cache_find(ieee, seq, frag, hdr->addr2, + hdr->addr1); + if (entry != NULL) { + entry->last_frag = frag; + skb = entry->skb; + } + } + + return skb; +} + +/* Called only as a tasklet (software IRQ) */ +static int libipw_frag_cache_invalidate(struct libipw_device *ieee, + struct libipw_hdr_4addr *hdr) +{ + u16 sc; + unsigned int seq; + struct libipw_frag_entry *entry; + + sc = le16_to_cpu(hdr->seq_ctl); + seq = WLAN_GET_SEQ_SEQ(sc); + + entry = libipw_frag_cache_find(ieee, seq, -1, hdr->addr2, + hdr->addr1); + + if (entry == NULL) { + LIBIPW_DEBUG_FRAG("could not invalidate fragment cache " + "entry (seq=%u)\n", seq); + return -1; + } + + entry->skb = NULL; + return 0; +} + +#ifdef NOT_YET +/* libipw_rx_frame_mgtmt + * + * Responsible for handling management control frames + * + * Called by libipw_rx */ +static int +libipw_rx_frame_mgmt(struct libipw_device *ieee, struct sk_buff *skb, + struct libipw_rx_stats *rx_stats, u16 type, + u16 stype) +{ + if (ieee->iw_mode == IW_MODE_MASTER) { + printk(KERN_DEBUG "%s: Master mode not yet supported.\n", + ieee->dev->name); + return 0; +/* + hostap_update_sta_ps(ieee, (struct hostap_libipw_hdr_4addr *) + skb->data);*/ + } + + if (ieee->hostapd && type == WLAN_FC_TYPE_MGMT) { + if (stype == WLAN_FC_STYPE_BEACON && + ieee->iw_mode == IW_MODE_MASTER) { + struct sk_buff *skb2; + /* Process beacon frames also in kernel driver to + * update STA(AP) table statistics */ + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) + hostap_rx(skb2->dev, skb2, rx_stats); + } + + /* send management frames to the user space daemon for + * processing */ + ieee->apdevstats.rx_packets++; + ieee->apdevstats.rx_bytes += skb->len; + prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT); + return 0; + } + + if (ieee->iw_mode == IW_MODE_MASTER) { + if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) { + printk(KERN_DEBUG "%s: unknown management frame " + "(type=0x%02x, stype=0x%02x) dropped\n", + skb->dev->name, type, stype); + return -1; + } + + hostap_rx(skb->dev, skb, rx_stats); + return 0; + } + + printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame " + "received in non-Host AP mode\n", skb->dev->name); + return -1; +} +#endif + +/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ +/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ +static unsigned char libipw_rfc1042_header[] = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + +/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ +static unsigned char libipw_bridge_tunnel_header[] = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; +/* No encapsulation header if EtherType < 0x600 (=length) */ + +/* Called by libipw_rx_frame_decrypt */ +static int libipw_is_eapol_frame(struct libipw_device *ieee, + struct sk_buff *skb) +{ + struct net_device *dev = ieee->dev; + u16 fc, ethertype; + struct libipw_hdr_3addr *hdr; + u8 *pos; + + if (skb->len < 24) + return 0; + + hdr = (struct libipw_hdr_3addr *)skb->data; + fc = le16_to_cpu(hdr->frame_ctl); + + /* check that the frame is unicast frame to us */ + if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_TODS && + ether_addr_equal(hdr->addr1, dev->dev_addr) && + ether_addr_equal(hdr->addr3, dev->dev_addr)) { + /* ToDS frame with own addr BSSID and DA */ + } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_FROMDS && + ether_addr_equal(hdr->addr1, dev->dev_addr)) { + /* FromDS frame with own addr as DA */ + } else + return 0; + + if (skb->len < 24 + 8) + return 0; + + /* check for port access entity Ethernet type */ + pos = skb->data + 24; + ethertype = (pos[6] << 8) | pos[7]; + if (ethertype == ETH_P_PAE) + return 1; + + return 0; +} + +/* Called only as a tasklet (software IRQ), by libipw_rx */ +static int +libipw_rx_frame_decrypt(struct libipw_device *ieee, struct sk_buff *skb, + struct lib80211_crypt_data *crypt) +{ + struct libipw_hdr_3addr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) + return 0; + + hdr = (struct libipw_hdr_3addr *)skb->data; + hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + LIBIPW_DEBUG_DROP("decryption failed (SA=%pM) res=%d\n", + hdr->addr2, res); + if (res == -2) + LIBIPW_DEBUG_DROP("Decryption failed ICV " + "mismatch (key %d)\n", + skb->data[hdrlen + 3] >> 6); + ieee->ieee_stats.rx_discards_undecryptable++; + return -1; + } + + return res; +} + +/* Called only as a tasklet (software IRQ), by libipw_rx */ +static int +libipw_rx_frame_decrypt_msdu(struct libipw_device *ieee, + struct sk_buff *skb, int keyidx, + struct lib80211_crypt_data *crypt) +{ + struct libipw_hdr_3addr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) + return 0; + + hdr = (struct libipw_hdr_3addr *)skb->data; + hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" + " (SA=%pM keyidx=%d)\n", ieee->dev->name, hdr->addr2, + keyidx); + return -1; + } + + return 0; +} + +/* All received frames are sent to this function. @skb contains the frame in + * IEEE 802.11 format, i.e., in the format it was sent over air. + * This function is called only as a tasklet (software IRQ). */ +int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, + struct libipw_rx_stats *rx_stats) +{ + struct net_device *dev = ieee->dev; + struct libipw_hdr_4addr *hdr; + size_t hdrlen; + u16 fc, type, stype, sc; + unsigned int frag; + u8 *payload; + u16 ethertype; +#ifdef NOT_YET + struct net_device *wds = NULL; + struct sk_buff *skb2 = NULL; + struct net_device *wds = NULL; + int frame_authorized = 0; + int from_assoc_ap = 0; + void *sta = NULL; +#endif + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + struct lib80211_crypt_data *crypt = NULL; + int keyidx = 0; + int can_be_decrypted = 0; + + hdr = (struct libipw_hdr_4addr *)skb->data; + if (skb->len < 10) { + printk(KERN_INFO "%s: SKB length < 10\n", dev->name); + goto rx_dropped; + } + + fc = le16_to_cpu(hdr->frame_ctl); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + sc = le16_to_cpu(hdr->seq_ctl); + frag = WLAN_GET_SEQ_FRAG(sc); + hdrlen = libipw_get_hdrlen(fc); + + if (skb->len < hdrlen) { + printk(KERN_INFO "%s: invalid SKB length %d\n", + dev->name, skb->len); + goto rx_dropped; + } + + /* Put this code here so that we avoid duplicating it in all + * Rx paths. - Jean II */ +#ifdef CONFIG_WIRELESS_EXT +#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ + /* If spy monitoring on */ + if (ieee->spy_data.spy_number > 0) { + struct iw_quality wstats; + + wstats.updated = 0; + if (rx_stats->mask & LIBIPW_STATMASK_RSSI) { + wstats.level = rx_stats->signal; + wstats.updated |= IW_QUAL_LEVEL_UPDATED; + } else + wstats.updated |= IW_QUAL_LEVEL_INVALID; + + if (rx_stats->mask & LIBIPW_STATMASK_NOISE) { + wstats.noise = rx_stats->noise; + wstats.updated |= IW_QUAL_NOISE_UPDATED; + } else + wstats.updated |= IW_QUAL_NOISE_INVALID; + + if (rx_stats->mask & LIBIPW_STATMASK_SIGNAL) { + wstats.qual = rx_stats->signal; + wstats.updated |= IW_QUAL_QUAL_UPDATED; + } else + wstats.updated |= IW_QUAL_QUAL_INVALID; + + /* Update spy records */ + wireless_spy_update(ieee->dev, hdr->addr2, &wstats); + } +#endif /* IW_WIRELESS_SPY */ +#endif /* CONFIG_WIRELESS_EXT */ + +#ifdef NOT_YET + hostap_update_rx_stats(local->ap, hdr, rx_stats); +#endif + + if (ieee->iw_mode == IW_MODE_MONITOR) { + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + libipw_monitor_rx(ieee, skb, rx_stats); + return 1; + } + + can_be_decrypted = (is_multicast_ether_addr(hdr->addr1) || + is_broadcast_ether_addr(hdr->addr2)) ? + ieee->host_mc_decrypt : ieee->host_decrypt; + + if (can_be_decrypted) { + if (skb->len >= hdrlen + 3) { + /* Top two-bits of byte 3 are the key index */ + keyidx = skb->data[hdrlen + 3] >> 6; + } + + /* ieee->crypt[] is WEP_KEY (4) in length. Given that keyidx + * is only allowed 2-bits of storage, no value of keyidx can + * be provided via above code that would result in keyidx + * being out of range */ + crypt = ieee->crypt_info.crypt[keyidx]; + +#ifdef NOT_YET + sta = NULL; + + /* Use station specific key to override default keys if the + * receiver address is a unicast address ("individual RA"). If + * bcrx_sta_key parameter is set, station specific key is used + * even with broad/multicast targets (this is against IEEE + * 802.11, but makes it easier to use different keys with + * stations that do not support WEP key mapping). */ + + if (is_unicast_ether_addr(hdr->addr1) || local->bcrx_sta_key) + (void)hostap_handle_sta_crypto(local, hdr, &crypt, + &sta); +#endif + + /* allow NULL decrypt to indicate an station specific override + * for default encryption */ + if (crypt && (crypt->ops == NULL || + crypt->ops->decrypt_mpdu == NULL)) + crypt = NULL; + + if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) { + /* This seems to be triggered by some (multicast?) + * frames from other than current BSS, so just drop the + * frames silently instead of filling system log with + * these reports. */ + LIBIPW_DEBUG_DROP("Decryption failed (not set)" + " (SA=%pM)\n", hdr->addr2); + ieee->ieee_stats.rx_discards_undecryptable++; + goto rx_dropped; + } + } +#ifdef NOT_YET + if (type != WLAN_FC_TYPE_DATA) { + if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH && + fc & IEEE80211_FCTL_PROTECTED && ieee->host_decrypt && + (keyidx = hostap_rx_frame_decrypt(ieee, skb, crypt)) < 0) { + printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " + "from %pM\n", dev->name, hdr->addr2); + /* TODO: could inform hostapd about this so that it + * could send auth failure report */ + goto rx_dropped; + } + + if (libipw_rx_frame_mgmt(ieee, skb, rx_stats, type, stype)) + goto rx_dropped; + else + goto rx_exit; + } +#endif + /* drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.29) */ + if (sc == ieee->prev_seq_ctl) + goto rx_dropped; + else + ieee->prev_seq_ctl = sc; + + /* Data frame - extract src/dst addresses */ + if (skb->len < LIBIPW_3ADDR_LEN) + goto rx_dropped; + + switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { + case IEEE80211_FCTL_FROMDS: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr3, ETH_ALEN); + break; + case IEEE80211_FCTL_TODS: + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + break; + case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: + if (skb->len < LIBIPW_4ADDR_LEN) + goto rx_dropped; + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr4, ETH_ALEN); + break; + case 0: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + break; + } + +#ifdef NOT_YET + if (hostap_rx_frame_wds(ieee, hdr, fc, &wds)) + goto rx_dropped; + if (wds) { + skb->dev = dev = wds; + stats = hostap_get_stats(dev); + } + + if (ieee->iw_mode == IW_MODE_MASTER && !wds && + (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_FROMDS && ieee->stadev && + ether_addr_equal(hdr->addr2, ieee->assoc_ap_addr)) { + /* Frame from BSSID of the AP for which we are a client */ + skb->dev = dev = ieee->stadev; + stats = hostap_get_stats(dev); + from_assoc_ap = 1; + } +#endif + +#ifdef NOT_YET + if ((ieee->iw_mode == IW_MODE_MASTER || + ieee->iw_mode == IW_MODE_REPEAT) && !from_assoc_ap) { + switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats, + wds != NULL)) { + case AP_RX_CONTINUE_NOT_AUTHORIZED: + frame_authorized = 0; + break; + case AP_RX_CONTINUE: + frame_authorized = 1; + break; + case AP_RX_DROP: + goto rx_dropped; + case AP_RX_EXIT: + goto rx_exit; + } + } +#endif + + /* Nullfunc frames may have PS-bit set, so they must be passed to + * hostap_handle_sta_rx() before being dropped here. */ + + stype &= ~IEEE80211_STYPE_QOS_DATA; + + if (stype != IEEE80211_STYPE_DATA && + stype != IEEE80211_STYPE_DATA_CFACK && + stype != IEEE80211_STYPE_DATA_CFPOLL && + stype != IEEE80211_STYPE_DATA_CFACKPOLL) { + if (stype != IEEE80211_STYPE_NULLFUNC) + LIBIPW_DEBUG_DROP("RX: dropped data frame " + "with no data (type=0x%02x, " + "subtype=0x%02x, len=%d)\n", + type, stype, skb->len); + goto rx_dropped; + } + + /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ + + if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && + (keyidx = libipw_rx_frame_decrypt(ieee, skb, crypt)) < 0) + goto rx_dropped; + + hdr = (struct libipw_hdr_4addr *)skb->data; + + /* skb: hdr + (possibly fragmented) plaintext payload */ + // PR: FIXME: hostap has additional conditions in the "if" below: + // ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && + if ((frag != 0) || (fc & IEEE80211_FCTL_MOREFRAGS)) { + int flen; + struct sk_buff *frag_skb = libipw_frag_cache_get(ieee, hdr); + LIBIPW_DEBUG_FRAG("Rx Fragment received (%u)\n", frag); + + if (!frag_skb) { + LIBIPW_DEBUG(LIBIPW_DL_RX | LIBIPW_DL_FRAG, + "Rx cannot get skb from fragment " + "cache (morefrag=%d seq=%u frag=%u)\n", + (fc & IEEE80211_FCTL_MOREFRAGS) != 0, + WLAN_GET_SEQ_SEQ(sc), frag); + goto rx_dropped; + } + + flen = skb->len; + if (frag != 0) + flen -= hdrlen; + + if (frag_skb->tail + flen > frag_skb->end) { + printk(KERN_WARNING "%s: host decrypted and " + "reassembled frame did not fit skb\n", + dev->name); + libipw_frag_cache_invalidate(ieee, hdr); + goto rx_dropped; + } + + if (frag == 0) { + /* copy first fragment (including full headers) into + * beginning of the fragment cache skb */ + skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), flen); + } else { + /* append frame payload to the end of the fragment + * cache skb */ + skb_copy_from_linear_data_offset(skb, hdrlen, + skb_put(frag_skb, flen), flen); + } + dev_kfree_skb_any(skb); + skb = NULL; + + if (fc & IEEE80211_FCTL_MOREFRAGS) { + /* more fragments expected - leave the skb in fragment + * cache for now; it will be delivered to upper layers + * after all fragments have been received */ + goto rx_exit; + } + + /* this was the last fragment and the frame will be + * delivered, so remove skb from fragment cache */ + skb = frag_skb; + hdr = (struct libipw_hdr_4addr *)skb->data; + libipw_frag_cache_invalidate(ieee, hdr); + } + + /* skb: hdr + (possible reassembled) full MSDU payload; possibly still + * encrypted/authenticated */ + if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && + libipw_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) + goto rx_dropped; + + hdr = (struct libipw_hdr_4addr *)skb->data; + if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep) { + if ( /*ieee->ieee802_1x && */ + libipw_is_eapol_frame(ieee, skb)) { + /* pass unencrypted EAPOL frames even if encryption is + * configured */ + } else { + LIBIPW_DEBUG_DROP("encryption configured, but RX " + "frame not encrypted (SA=%pM)\n", + hdr->addr2); + goto rx_dropped; + } + } + + if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep && + !libipw_is_eapol_frame(ieee, skb)) { + LIBIPW_DEBUG_DROP("dropped unencrypted RX data " + "frame from %pM (drop_unencrypted=1)\n", + hdr->addr2); + goto rx_dropped; + } + + /* If the frame was decrypted in hardware, we may need to strip off + * any security data (IV, ICV, etc) that was left behind */ + if (!can_be_decrypted && (fc & IEEE80211_FCTL_PROTECTED) && + ieee->host_strip_iv_icv) { + int trimlen = 0; + + /* Top two-bits of byte 3 are the key index */ + if (skb->len >= hdrlen + 3) + keyidx = skb->data[hdrlen + 3] >> 6; + + /* To strip off any security data which appears before the + * payload, we simply increase hdrlen (as the header gets + * chopped off immediately below). For the security data which + * appears after the payload, we use skb_trim. */ + + switch (ieee->sec.encode_alg[keyidx]) { + case SEC_ALG_WEP: + /* 4 byte IV */ + hdrlen += 4; + /* 4 byte ICV */ + trimlen = 4; + break; + case SEC_ALG_TKIP: + /* 4 byte IV, 4 byte ExtIV */ + hdrlen += 8; + /* 8 byte MIC, 4 byte ICV */ + trimlen = 12; + break; + case SEC_ALG_CCMP: + /* 8 byte CCMP header */ + hdrlen += 8; + /* 8 byte MIC */ + trimlen = 8; + break; + } + + if (skb->len < trimlen) + goto rx_dropped; + + __skb_trim(skb, skb->len - trimlen); + + if (skb->len < hdrlen) + goto rx_dropped; + } + + /* skb: hdr + (possible reassembled) full plaintext payload */ + + payload = skb->data + hdrlen; + ethertype = (payload[6] << 8) | payload[7]; + +#ifdef NOT_YET + /* If IEEE 802.1X is used, check whether the port is authorized to send + * the received frame. */ + if (ieee->ieee802_1x && ieee->iw_mode == IW_MODE_MASTER) { + if (ethertype == ETH_P_PAE) { + printk(KERN_DEBUG "%s: RX: IEEE 802.1X frame\n", + dev->name); + if (ieee->hostapd && ieee->apdev) { + /* Send IEEE 802.1X frames to the user + * space daemon for processing */ + prism2_rx_80211(ieee->apdev, skb, rx_stats, + PRISM2_RX_MGMT); + ieee->apdevstats.rx_packets++; + ieee->apdevstats.rx_bytes += skb->len; + goto rx_exit; + } + } else if (!frame_authorized) { + printk(KERN_DEBUG "%s: dropped frame from " + "unauthorized port (IEEE 802.1X): " + "ethertype=0x%04x\n", dev->name, ethertype); + goto rx_dropped; + } + } +#endif + + /* convert hdr + possible LLC headers into Ethernet header */ + if (skb->len - hdrlen >= 8 && + ((memcmp(payload, libipw_rfc1042_header, SNAP_SIZE) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + memcmp(payload, libipw_bridge_tunnel_header, SNAP_SIZE) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation and + * replace EtherType */ + skb_pull(skb, hdrlen + SNAP_SIZE); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } else { + __be16 len; + /* Leave Ethernet header part of hdr and full payload */ + skb_pull(skb, hdrlen); + len = htons(skb->len); + memcpy(skb_push(skb, 2), &len, 2); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } + +#ifdef NOT_YET + if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_TODS) && skb->len >= ETH_HLEN + ETH_ALEN) { + /* Non-standard frame: get addr4 from its bogus location after + * the payload */ + skb_copy_to_linear_data_offset(skb, ETH_ALEN, + skb->data + skb->len - ETH_ALEN, + ETH_ALEN); + skb_trim(skb, skb->len - ETH_ALEN); + } +#endif + + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + +#ifdef NOT_YET + if (ieee->iw_mode == IW_MODE_MASTER && !wds && ieee->ap->bridge_packets) { + if (is_multicast_ether_addr(dst)) { + /* copy multicast frame both to the higher layers and + * to the wireless media */ + ieee->ap->bridged_multicast++; + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2 == NULL) + printk(KERN_DEBUG "%s: skb_clone failed for " + "multicast frame\n", dev->name); + } else if (hostap_is_sta_assoc(ieee->ap, dst)) { + /* send frame directly to the associated STA using + * wireless media and not passing to higher layers */ + ieee->ap->bridged_unicast++; + skb2 = skb; + skb = NULL; + } + } + + if (skb2 != NULL) { + /* send to wireless media */ + skb2->dev = dev; + skb2->protocol = htons(ETH_P_802_3); + skb_reset_mac_header(skb2); + skb_reset_network_header(skb2); + /* skb2->network_header += ETH_HLEN; */ + dev_queue_xmit(skb2); + } +#endif + + if (skb) { + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */ + if (netif_rx(skb) == NET_RX_DROP) { + /* netif_rx always succeeds, but it might drop + * the packet. If it drops the packet, we log that + * in our stats. */ + LIBIPW_DEBUG_DROP + ("RX: netif_rx dropped the packet\n"); + dev->stats.rx_dropped++; + } + } + + rx_exit: +#ifdef NOT_YET + if (sta) + hostap_handle_sta_release(sta); +#endif + return 1; + + rx_dropped: + dev->stats.rx_dropped++; + + /* Returning 0 indicates to caller that we have not handled the SKB-- + * so it is still allocated and can be used again by underlying + * hardware as a DMA target */ + return 0; +} + +/* Filter out unrelated packets, call libipw_rx[_mgt] + * This function takes over the skb, it should not be used again after calling + * this function. */ +void libipw_rx_any(struct libipw_device *ieee, + struct sk_buff *skb, struct libipw_rx_stats *stats) +{ + struct libipw_hdr_4addr *hdr; + int is_packet_for_us; + u16 fc; + + if (ieee->iw_mode == IW_MODE_MONITOR) { + if (!libipw_rx(ieee, skb, stats)) + dev_kfree_skb_irq(skb); + return; + } + + if (skb->len < sizeof(struct ieee80211_hdr)) + goto drop_free; + + hdr = (struct libipw_hdr_4addr *)skb->data; + fc = le16_to_cpu(hdr->frame_ctl); + + if ((fc & IEEE80211_FCTL_VERS) != 0) + goto drop_free; + + switch (fc & IEEE80211_FCTL_FTYPE) { + case IEEE80211_FTYPE_MGMT: + if (skb->len < sizeof(struct libipw_hdr_3addr)) + goto drop_free; + libipw_rx_mgt(ieee, hdr, stats); + dev_kfree_skb_irq(skb); + return; + case IEEE80211_FTYPE_DATA: + break; + case IEEE80211_FTYPE_CTL: + return; + default: + return; + } + + is_packet_for_us = 0; + switch (ieee->iw_mode) { + case IW_MODE_ADHOC: + /* our BSS and not from/to DS */ + if (ether_addr_equal(hdr->addr3, ieee->bssid)) + if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) { + /* promisc: get all */ + if (ieee->dev->flags & IFF_PROMISC) + is_packet_for_us = 1; + /* to us */ + else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr)) + is_packet_for_us = 1; + /* mcast */ + else if (is_multicast_ether_addr(hdr->addr1)) + is_packet_for_us = 1; + } + break; + case IW_MODE_INFRA: + /* our BSS (== from our AP) and from DS */ + if (ether_addr_equal(hdr->addr2, ieee->bssid)) + if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) { + /* promisc: get all */ + if (ieee->dev->flags & IFF_PROMISC) + is_packet_for_us = 1; + /* to us */ + else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr)) + is_packet_for_us = 1; + /* mcast */ + else if (is_multicast_ether_addr(hdr->addr1)) { + /* not our own packet bcasted from AP */ + if (!ether_addr_equal(hdr->addr3, ieee->dev->dev_addr)) + is_packet_for_us = 1; + } + } + break; + default: + /* ? */ + break; + } + + if (is_packet_for_us) + if (!libipw_rx(ieee, skb, stats)) + dev_kfree_skb_irq(skb); + return; + +drop_free: + dev_kfree_skb_irq(skb); + ieee->dev->stats.rx_dropped++; +} + +#define MGMT_FRAME_FIXED_PART_LENGTH 0x24 + +static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; + +/* +* Make the structure we read from the beacon packet to have +* the right values +*/ +static int libipw_verify_qos_info(struct libipw_qos_information_element + *info_element, int sub_type) +{ + + if (info_element->qui_subtype != sub_type) + return -1; + if (memcmp(info_element->qui, qos_oui, QOS_OUI_LEN)) + return -1; + if (info_element->qui_type != QOS_OUI_TYPE) + return -1; + if (info_element->version != QOS_VERSION_1) + return -1; + + return 0; +} + +/* + * Parse a QoS parameter element + */ +static int libipw_read_qos_param_element(struct libipw_qos_parameter_info + *element_param, struct libipw_info_element + *info_element) +{ + int ret = 0; + u16 size = sizeof(struct libipw_qos_parameter_info) - 2; + + if ((info_element == NULL) || (element_param == NULL)) + return -1; + + if (info_element->id == QOS_ELEMENT_ID && info_element->len == size) { + memcpy(element_param->info_element.qui, info_element->data, + info_element->len); + element_param->info_element.elementID = info_element->id; + element_param->info_element.length = info_element->len; + } else + ret = -1; + if (ret == 0) + ret = libipw_verify_qos_info(&element_param->info_element, + QOS_OUI_PARAM_SUB_TYPE); + return ret; +} + +/* + * Parse a QoS information element + */ +static int libipw_read_qos_info_element(struct + libipw_qos_information_element + *element_info, struct libipw_info_element + *info_element) +{ + int ret = 0; + u16 size = sizeof(struct libipw_qos_information_element) - 2; + + if (element_info == NULL) + return -1; + if (info_element == NULL) + return -1; + + if ((info_element->id == QOS_ELEMENT_ID) && (info_element->len == size)) { + memcpy(element_info->qui, info_element->data, + info_element->len); + element_info->elementID = info_element->id; + element_info->length = info_element->len; + } else + ret = -1; + + if (ret == 0) + ret = libipw_verify_qos_info(element_info, + QOS_OUI_INFO_SUB_TYPE); + return ret; +} + +/* + * Write QoS parameters from the ac parameters. + */ +static int libipw_qos_convert_ac_to_parameters(struct + libipw_qos_parameter_info + *param_elm, struct + libipw_qos_parameters + *qos_param) +{ + int rc = 0; + int i; + struct libipw_qos_ac_parameter *ac_params; + u32 txop; + u8 cw_min; + u8 cw_max; + + for (i = 0; i < QOS_QUEUE_NUM; i++) { + ac_params = &(param_elm->ac_params_record[i]); + + qos_param->aifs[i] = (ac_params->aci_aifsn) & 0x0F; + qos_param->aifs[i] -= (qos_param->aifs[i] < 2) ? 0 : 2; + + cw_min = ac_params->ecw_min_max & 0x0F; + qos_param->cw_min[i] = cpu_to_le16((1 << cw_min) - 1); + + cw_max = (ac_params->ecw_min_max & 0xF0) >> 4; + qos_param->cw_max[i] = cpu_to_le16((1 << cw_max) - 1); + + qos_param->flag[i] = + (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00; + + txop = le16_to_cpu(ac_params->tx_op_limit) * 32; + qos_param->tx_op_limit[i] = cpu_to_le16(txop); + } + return rc; +} + +/* + * we have a generic data element which it may contain QoS information or + * parameters element. check the information element length to decide + * which type to read + */ +static int libipw_parse_qos_info_param_IE(struct libipw_info_element + *info_element, + struct libipw_network *network) +{ + int rc = 0; + struct libipw_qos_parameters *qos_param = NULL; + struct libipw_qos_information_element qos_info_element; + + rc = libipw_read_qos_info_element(&qos_info_element, info_element); + + if (rc == 0) { + network->qos_data.param_count = qos_info_element.ac_info & 0x0F; + network->flags |= NETWORK_HAS_QOS_INFORMATION; + } else { + struct libipw_qos_parameter_info param_element; + + rc = libipw_read_qos_param_element(¶m_element, + info_element); + if (rc == 0) { + qos_param = &(network->qos_data.parameters); + libipw_qos_convert_ac_to_parameters(¶m_element, + qos_param); + network->flags |= NETWORK_HAS_QOS_PARAMETERS; + network->qos_data.param_count = + param_element.info_element.ac_info & 0x0F; + } + } + + if (rc == 0) { + LIBIPW_DEBUG_QOS("QoS is supported\n"); + network->qos_data.supported = 1; + } + return rc; +} + +#ifdef CONFIG_LIBIPW_DEBUG +#define MFIE_STRING(x) case WLAN_EID_ ##x: return #x + +static const char *get_info_element_string(u16 id) +{ + switch (id) { + MFIE_STRING(SSID); + MFIE_STRING(SUPP_RATES); + MFIE_STRING(FH_PARAMS); + MFIE_STRING(DS_PARAMS); + MFIE_STRING(CF_PARAMS); + MFIE_STRING(TIM); + MFIE_STRING(IBSS_PARAMS); + MFIE_STRING(COUNTRY); + MFIE_STRING(REQUEST); + MFIE_STRING(CHALLENGE); + MFIE_STRING(PWR_CONSTRAINT); + MFIE_STRING(PWR_CAPABILITY); + MFIE_STRING(TPC_REQUEST); + MFIE_STRING(TPC_REPORT); + MFIE_STRING(SUPPORTED_CHANNELS); + MFIE_STRING(CHANNEL_SWITCH); + MFIE_STRING(MEASURE_REQUEST); + MFIE_STRING(MEASURE_REPORT); + MFIE_STRING(QUIET); + MFIE_STRING(IBSS_DFS); + MFIE_STRING(ERP_INFO); + MFIE_STRING(RSN); + MFIE_STRING(EXT_SUPP_RATES); + MFIE_STRING(VENDOR_SPECIFIC); + MFIE_STRING(QOS_PARAMETER); + default: + return "UNKNOWN"; + } +} +#endif + +static int libipw_parse_info_param(struct libipw_info_element + *info_element, u16 length, + struct libipw_network *network) +{ + u8 i; +#ifdef CONFIG_LIBIPW_DEBUG + char rates_str[64]; + char *p; +#endif + + while (length >= sizeof(*info_element)) { + if (sizeof(*info_element) + info_element->len > length) { + LIBIPW_DEBUG_MGMT("Info elem: parse failed: " + "info_element->len + 2 > left : " + "info_element->len+2=%zd left=%d, id=%d.\n", + info_element->len + + sizeof(*info_element), + length, info_element->id); + /* We stop processing but don't return an error here + * because some misbehaviour APs break this rule. ie. + * Orinoco AP1000. */ + break; + } + + switch (info_element->id) { + case WLAN_EID_SSID: + network->ssid_len = min(info_element->len, + (u8) IW_ESSID_MAX_SIZE); + memcpy(network->ssid, info_element->data, + network->ssid_len); + if (network->ssid_len < IW_ESSID_MAX_SIZE) + memset(network->ssid + network->ssid_len, 0, + IW_ESSID_MAX_SIZE - network->ssid_len); + + LIBIPW_DEBUG_MGMT("WLAN_EID_SSID: '%*pE' len=%d.\n", + network->ssid_len, network->ssid, + network->ssid_len); + break; + + case WLAN_EID_SUPP_RATES: +#ifdef CONFIG_LIBIPW_DEBUG + p = rates_str; +#endif + network->rates_len = min(info_element->len, + MAX_RATES_LENGTH); + for (i = 0; i < network->rates_len; i++) { + network->rates[i] = info_element->data[i]; +#ifdef CONFIG_LIBIPW_DEBUG + p += snprintf(p, sizeof(rates_str) - + (p - rates_str), "%02X ", + network->rates[i]); +#endif + if (libipw_is_ofdm_rate + (info_element->data[i])) { + network->flags |= NETWORK_HAS_OFDM; + if (info_element->data[i] & + LIBIPW_BASIC_RATE_MASK) + network->flags &= + ~NETWORK_HAS_CCK; + } + } + + LIBIPW_DEBUG_MGMT("WLAN_EID_SUPP_RATES: '%s' (%d)\n", + rates_str, network->rates_len); + break; + + case WLAN_EID_EXT_SUPP_RATES: +#ifdef CONFIG_LIBIPW_DEBUG + p = rates_str; +#endif + network->rates_ex_len = min(info_element->len, + MAX_RATES_EX_LENGTH); + for (i = 0; i < network->rates_ex_len; i++) { + network->rates_ex[i] = info_element->data[i]; +#ifdef CONFIG_LIBIPW_DEBUG + p += snprintf(p, sizeof(rates_str) - + (p - rates_str), "%02X ", + network->rates_ex[i]); +#endif + if (libipw_is_ofdm_rate + (info_element->data[i])) { + network->flags |= NETWORK_HAS_OFDM; + if (info_element->data[i] & + LIBIPW_BASIC_RATE_MASK) + network->flags &= + ~NETWORK_HAS_CCK; + } + } + + LIBIPW_DEBUG_MGMT("WLAN_EID_EXT_SUPP_RATES: '%s' (%d)\n", + rates_str, network->rates_ex_len); + break; + + case WLAN_EID_DS_PARAMS: + LIBIPW_DEBUG_MGMT("WLAN_EID_DS_PARAMS: %d\n", + info_element->data[0]); + network->channel = info_element->data[0]; + break; + + case WLAN_EID_FH_PARAMS: + LIBIPW_DEBUG_MGMT("WLAN_EID_FH_PARAMS: ignored\n"); + break; + + case WLAN_EID_CF_PARAMS: + LIBIPW_DEBUG_MGMT("WLAN_EID_CF_PARAMS: ignored\n"); + break; + + case WLAN_EID_TIM: + network->tim.tim_count = info_element->data[0]; + network->tim.tim_period = info_element->data[1]; + LIBIPW_DEBUG_MGMT("WLAN_EID_TIM: partially ignored\n"); + break; + + case WLAN_EID_ERP_INFO: + network->erp_value = info_element->data[0]; + network->flags |= NETWORK_HAS_ERP_VALUE; + LIBIPW_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n", + network->erp_value); + break; + + case WLAN_EID_IBSS_PARAMS: + network->atim_window = info_element->data[0]; + LIBIPW_DEBUG_MGMT("WLAN_EID_IBSS_PARAMS: %d\n", + network->atim_window); + break; + + case WLAN_EID_CHALLENGE: + LIBIPW_DEBUG_MGMT("WLAN_EID_CHALLENGE: ignored\n"); + break; + + case WLAN_EID_VENDOR_SPECIFIC: + LIBIPW_DEBUG_MGMT("WLAN_EID_VENDOR_SPECIFIC: %d bytes\n", + info_element->len); + if (!libipw_parse_qos_info_param_IE(info_element, + network)) + break; + + if (info_element->len >= 4 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x50 && + info_element->data[2] == 0xf2 && + info_element->data[3] == 0x01) { + network->wpa_ie_len = min(info_element->len + 2, + MAX_WPA_IE_LEN); + memcpy(network->wpa_ie, info_element, + network->wpa_ie_len); + } + break; + + case WLAN_EID_RSN: + LIBIPW_DEBUG_MGMT("WLAN_EID_RSN: %d bytes\n", + info_element->len); + network->rsn_ie_len = min(info_element->len + 2, + MAX_WPA_IE_LEN); + memcpy(network->rsn_ie, info_element, + network->rsn_ie_len); + break; + + case WLAN_EID_QOS_PARAMETER: + printk(KERN_ERR + "QoS Error need to parse QOS_PARAMETER IE\n"); + break; + /* 802.11h */ + case WLAN_EID_PWR_CONSTRAINT: + network->power_constraint = info_element->data[0]; + network->flags |= NETWORK_HAS_POWER_CONSTRAINT; + break; + + case WLAN_EID_CHANNEL_SWITCH: + network->power_constraint = info_element->data[0]; + network->flags |= NETWORK_HAS_CSA; + break; + + case WLAN_EID_QUIET: + network->quiet.count = info_element->data[0]; + network->quiet.period = info_element->data[1]; + network->quiet.duration = info_element->data[2]; + network->quiet.offset = info_element->data[3]; + network->flags |= NETWORK_HAS_QUIET; + break; + + case WLAN_EID_IBSS_DFS: + network->flags |= NETWORK_HAS_IBSS_DFS; + break; + + case WLAN_EID_TPC_REPORT: + network->tpc_report.transmit_power = + info_element->data[0]; + network->tpc_report.link_margin = info_element->data[1]; + network->flags |= NETWORK_HAS_TPC_REPORT; + break; + + default: + LIBIPW_DEBUG_MGMT + ("Unsupported info element: %s (%d)\n", + get_info_element_string(info_element->id), + info_element->id); + break; + } + + length -= sizeof(*info_element) + info_element->len; + info_element = + (struct libipw_info_element *)&info_element-> + data[info_element->len]; + } + + return 0; +} + +static int libipw_handle_assoc_resp(struct libipw_device *ieee, struct libipw_assoc_response + *frame, struct libipw_rx_stats *stats) +{ + struct libipw_network network_resp = { }; + struct libipw_network *network = &network_resp; + struct net_device *dev = ieee->dev; + + network->flags = 0; + network->qos_data.active = 0; + network->qos_data.supported = 0; + network->qos_data.param_count = 0; + network->qos_data.old_param_count = 0; + + //network->atim_window = le16_to_cpu(frame->aid) & (0x3FFF); + network->atim_window = le16_to_cpu(frame->aid); + network->listen_interval = le16_to_cpu(frame->status); + memcpy(network->bssid, frame->header.addr3, ETH_ALEN); + network->capability = le16_to_cpu(frame->capability); + network->last_scanned = jiffies; + network->rates_len = network->rates_ex_len = 0; + network->last_associate = 0; + network->ssid_len = 0; + network->erp_value = + (network->capability & WLAN_CAPABILITY_IBSS) ? 0x3 : 0x0; + + if (stats->freq == LIBIPW_52GHZ_BAND) { + /* for A band (No DS info) */ + network->channel = stats->received_channel; + } else + network->flags |= NETWORK_HAS_CCK; + + network->wpa_ie_len = 0; + network->rsn_ie_len = 0; + + if (libipw_parse_info_param + (frame->info_element, stats->len - sizeof(*frame), network)) + return 1; + + network->mode = 0; + if (stats->freq == LIBIPW_52GHZ_BAND) + network->mode = IEEE_A; + else { + if (network->flags & NETWORK_HAS_OFDM) + network->mode |= IEEE_G; + if (network->flags & NETWORK_HAS_CCK) + network->mode |= IEEE_B; + } + + memcpy(&network->stats, stats, sizeof(network->stats)); + + if (ieee->handle_assoc_response != NULL) + ieee->handle_assoc_response(dev, frame, network); + + return 0; +} + +/***************************************************/ + +static int libipw_network_init(struct libipw_device *ieee, struct libipw_probe_response + *beacon, + struct libipw_network *network, + struct libipw_rx_stats *stats) +{ + network->qos_data.active = 0; + network->qos_data.supported = 0; + network->qos_data.param_count = 0; + network->qos_data.old_param_count = 0; + + /* Pull out fixed field data */ + memcpy(network->bssid, beacon->header.addr3, ETH_ALEN); + network->capability = le16_to_cpu(beacon->capability); + network->last_scanned = jiffies; + network->time_stamp[0] = le32_to_cpu(beacon->time_stamp[0]); + network->time_stamp[1] = le32_to_cpu(beacon->time_stamp[1]); + network->beacon_interval = le16_to_cpu(beacon->beacon_interval); + /* Where to pull this? beacon->listen_interval; */ + network->listen_interval = 0x0A; + network->rates_len = network->rates_ex_len = 0; + network->last_associate = 0; + network->ssid_len = 0; + network->flags = 0; + network->atim_window = 0; + network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ? + 0x3 : 0x0; + + if (stats->freq == LIBIPW_52GHZ_BAND) { + /* for A band (No DS info) */ + network->channel = stats->received_channel; + } else + network->flags |= NETWORK_HAS_CCK; + + network->wpa_ie_len = 0; + network->rsn_ie_len = 0; + + if (libipw_parse_info_param + (beacon->info_element, stats->len - sizeof(*beacon), network)) + return 1; + + network->mode = 0; + if (stats->freq == LIBIPW_52GHZ_BAND) + network->mode = IEEE_A; + else { + if (network->flags & NETWORK_HAS_OFDM) + network->mode |= IEEE_G; + if (network->flags & NETWORK_HAS_CCK) + network->mode |= IEEE_B; + } + + if (network->mode == 0) { + LIBIPW_DEBUG_SCAN("Filtered out '%*pE (%pM)' network.\n", + network->ssid_len, network->ssid, + network->bssid); + return 1; + } + + memcpy(&network->stats, stats, sizeof(network->stats)); + + return 0; +} + +static inline int is_same_network(struct libipw_network *src, + struct libipw_network *dst) +{ + /* A network is only a duplicate if the channel, BSSID, and ESSID + * all match. We treat all with the same BSSID and channel + * as one network */ + return ((src->ssid_len == dst->ssid_len) && + (src->channel == dst->channel) && + ether_addr_equal_64bits(src->bssid, dst->bssid) && + !memcmp(src->ssid, dst->ssid, src->ssid_len)); +} + +static void update_network(struct libipw_network *dst, + struct libipw_network *src) +{ + int qos_active; + u8 old_param; + + /* We only update the statistics if they were created by receiving + * the network information on the actual channel the network is on. + * + * This keeps beacons received on neighbor channels from bringing + * down the signal level of an AP. */ + if (dst->channel == src->stats.received_channel) + memcpy(&dst->stats, &src->stats, + sizeof(struct libipw_rx_stats)); + else + LIBIPW_DEBUG_SCAN("Network %pM info received " + "off channel (%d vs. %d)\n", src->bssid, + dst->channel, src->stats.received_channel); + + dst->capability = src->capability; + memcpy(dst->rates, src->rates, src->rates_len); + dst->rates_len = src->rates_len; + memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len); + dst->rates_ex_len = src->rates_ex_len; + + dst->mode = src->mode; + dst->flags = src->flags; + dst->time_stamp[0] = src->time_stamp[0]; + dst->time_stamp[1] = src->time_stamp[1]; + + dst->beacon_interval = src->beacon_interval; + dst->listen_interval = src->listen_interval; + dst->atim_window = src->atim_window; + dst->erp_value = src->erp_value; + dst->tim = src->tim; + + memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len); + dst->wpa_ie_len = src->wpa_ie_len; + memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len); + dst->rsn_ie_len = src->rsn_ie_len; + + dst->last_scanned = jiffies; + qos_active = src->qos_data.active; + old_param = dst->qos_data.old_param_count; + if (dst->flags & NETWORK_HAS_QOS_MASK) + memcpy(&dst->qos_data, &src->qos_data, + sizeof(struct libipw_qos_data)); + else { + dst->qos_data.supported = src->qos_data.supported; + dst->qos_data.param_count = src->qos_data.param_count; + } + + if (dst->qos_data.supported == 1) { + if (dst->ssid_len) + LIBIPW_DEBUG_QOS + ("QoS the network %s is QoS supported\n", + dst->ssid); + else + LIBIPW_DEBUG_QOS + ("QoS the network is QoS supported\n"); + } + dst->qos_data.active = qos_active; + dst->qos_data.old_param_count = old_param; + + /* dst->last_associate is not overwritten */ +} + +static inline int is_beacon(__le16 fc) +{ + return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == IEEE80211_STYPE_BEACON); +} + +static void libipw_process_probe_response(struct libipw_device + *ieee, struct + libipw_probe_response + *beacon, struct libipw_rx_stats + *stats) +{ + struct net_device *dev = ieee->dev; + struct libipw_network network = { }; + struct libipw_network *target; + struct libipw_network *oldest = NULL; +#ifdef CONFIG_LIBIPW_DEBUG + struct libipw_info_element *info_element = beacon->info_element; +#endif + unsigned long flags; + + LIBIPW_DEBUG_SCAN("'%*pE' (%pM): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n", + info_element->len, info_element->data, + beacon->header.addr3, + (beacon->capability & cpu_to_le16(1 << 0xf)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0xe)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0xd)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0xc)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0xb)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0xa)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0x9)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0x8)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0x7)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0x6)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0x5)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0x4)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0x3)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0x2)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0x1)) ? '1' : '0', + (beacon->capability & cpu_to_le16(1 << 0x0)) ? '1' : '0'); + + if (libipw_network_init(ieee, beacon, &network, stats)) { + LIBIPW_DEBUG_SCAN("Dropped '%*pE' (%pM) via %s.\n", + info_element->len, info_element->data, + beacon->header.addr3, + is_beacon(beacon->header.frame_ctl) ? + "BEACON" : "PROBE RESPONSE"); + return; + } + + /* The network parsed correctly -- so now we scan our known networks + * to see if we can find it in our list. + * + * NOTE: This search is definitely not optimized. Once its doing + * the "right thing" we'll optimize it for efficiency if + * necessary */ + + /* Search for this entry in the list and update it if it is + * already there. */ + + spin_lock_irqsave(&ieee->lock, flags); + + list_for_each_entry(target, &ieee->network_list, list) { + if (is_same_network(target, &network)) + break; + + if ((oldest == NULL) || + time_before(target->last_scanned, oldest->last_scanned)) + oldest = target; + } + + /* If we didn't find a match, then get a new network slot to initialize + * with this beacon's information */ + if (&target->list == &ieee->network_list) { + if (list_empty(&ieee->network_free_list)) { + /* If there are no more slots, expire the oldest */ + list_del(&oldest->list); + target = oldest; + LIBIPW_DEBUG_SCAN("Expired '%*pE' (%pM) from network list.\n", + target->ssid_len, target->ssid, + target->bssid); + } else { + /* Otherwise just pull from the free list */ + target = list_entry(ieee->network_free_list.next, + struct libipw_network, list); + list_del(ieee->network_free_list.next); + } + +#ifdef CONFIG_LIBIPW_DEBUG + LIBIPW_DEBUG_SCAN("Adding '%*pE' (%pM) via %s.\n", + network.ssid_len, network.ssid, + network.bssid, + is_beacon(beacon->header.frame_ctl) ? + "BEACON" : "PROBE RESPONSE"); +#endif + memcpy(target, &network, sizeof(*target)); + list_add_tail(&target->list, &ieee->network_list); + } else { + LIBIPW_DEBUG_SCAN("Updating '%*pE' (%pM) via %s.\n", + target->ssid_len, target->ssid, + target->bssid, + is_beacon(beacon->header.frame_ctl) ? + "BEACON" : "PROBE RESPONSE"); + update_network(target, &network); + } + + spin_unlock_irqrestore(&ieee->lock, flags); + + if (is_beacon(beacon->header.frame_ctl)) { + if (ieee->handle_beacon != NULL) + ieee->handle_beacon(dev, beacon, target); + } else { + if (ieee->handle_probe_response != NULL) + ieee->handle_probe_response(dev, beacon, target); + } +} + +void libipw_rx_mgt(struct libipw_device *ieee, + struct libipw_hdr_4addr *header, + struct libipw_rx_stats *stats) +{ + switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))) { + case IEEE80211_STYPE_ASSOC_RESP: + LIBIPW_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu + (header->frame_ctl))); + libipw_handle_assoc_resp(ieee, + (struct libipw_assoc_response *) + header, stats); + break; + + case IEEE80211_STYPE_REASSOC_RESP: + LIBIPW_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu + (header->frame_ctl))); + break; + + case IEEE80211_STYPE_PROBE_REQ: + LIBIPW_DEBUG_MGMT("received auth (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu + (header->frame_ctl))); + + if (ieee->handle_probe_request != NULL) + ieee->handle_probe_request(ieee->dev, + (struct + libipw_probe_request *) + header, stats); + break; + + case IEEE80211_STYPE_PROBE_RESP: + LIBIPW_DEBUG_MGMT("received PROBE RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu + (header->frame_ctl))); + LIBIPW_DEBUG_SCAN("Probe response\n"); + libipw_process_probe_response(ieee, + (struct + libipw_probe_response *) + header, stats); + break; + + case IEEE80211_STYPE_BEACON: + LIBIPW_DEBUG_MGMT("received BEACON (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu + (header->frame_ctl))); + LIBIPW_DEBUG_SCAN("Beacon\n"); + libipw_process_probe_response(ieee, + (struct + libipw_probe_response *) + header, stats); + break; + case IEEE80211_STYPE_AUTH: + + LIBIPW_DEBUG_MGMT("received auth (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu + (header->frame_ctl))); + + if (ieee->handle_auth != NULL) + ieee->handle_auth(ieee->dev, + (struct libipw_auth *)header); + break; + + case IEEE80211_STYPE_DISASSOC: + if (ieee->handle_disassoc != NULL) + ieee->handle_disassoc(ieee->dev, + (struct libipw_disassoc *) + header); + break; + + case IEEE80211_STYPE_ACTION: + LIBIPW_DEBUG_MGMT("ACTION\n"); + if (ieee->handle_action) + ieee->handle_action(ieee->dev, + (struct libipw_action *) + header, stats); + break; + + case IEEE80211_STYPE_REASSOC_REQ: + LIBIPW_DEBUG_MGMT("received reassoc (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu + (header->frame_ctl))); + + LIBIPW_DEBUG_MGMT("%s: LIBIPW_REASSOC_REQ received\n", + ieee->dev->name); + if (ieee->handle_reassoc_request != NULL) + ieee->handle_reassoc_request(ieee->dev, + (struct libipw_reassoc_request *) + header); + break; + + case IEEE80211_STYPE_ASSOC_REQ: + LIBIPW_DEBUG_MGMT("received assoc (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu + (header->frame_ctl))); + + LIBIPW_DEBUG_MGMT("%s: LIBIPW_ASSOC_REQ received\n", + ieee->dev->name); + if (ieee->handle_assoc_request != NULL) + ieee->handle_assoc_request(ieee->dev); + break; + + case IEEE80211_STYPE_DEAUTH: + LIBIPW_DEBUG_MGMT("DEAUTH\n"); + if (ieee->handle_deauth != NULL) + ieee->handle_deauth(ieee->dev, + (struct libipw_deauth *) + header); + break; + default: + LIBIPW_DEBUG_MGMT("received UNKNOWN (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu + (header->frame_ctl))); + LIBIPW_DEBUG_MGMT("%s: Unknown management packet: %d\n", + ieee->dev->name, + WLAN_FC_GET_STYPE(le16_to_cpu + (header->frame_ctl))); + break; + } +} + +EXPORT_SYMBOL_GPL(libipw_rx_any); +EXPORT_SYMBOL(libipw_rx_mgt); +EXPORT_SYMBOL(libipw_rx); diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_tx.c b/drivers/net/wireless/intel/ipw2x00/libipw_tx.c new file mode 100644 index 000000000000..e8c039879b05 --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/libipw_tx.c @@ -0,0 +1,536 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Intel Linux Wireless + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libipw.h" + +/* + +802.11 Data Frame + + ,-------------------------------------------------------------------. +Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | + |------|------|---------|---------|---------|------|---------|------| +Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs | + | | tion | (BSSID) | | | ence | data | | + `--------------------------------------------------| |------' +Total: 28 non-data bytes `----.----' + | + .- 'Frame data' expands, if WEP enabled, to <----------' + | + V + ,-----------------------. +Bytes | 4 | 0-2296 | 4 | + |-----|-----------|-----| +Desc. | IV | Encrypted | ICV | + | | Packet | | + `-----| |-----' + `-----.-----' + | + .- 'Encrypted Packet' expands to + | + V + ,---------------------------------------------------. +Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 | + |------|------|---------|----------|------|---------| +Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP | + | DSAP | SSAP | | | | Packet | + | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | | + `---------------------------------------------------- +Total: 8 non-data bytes + +802.3 Ethernet Data Frame + + ,-----------------------------------------. +Bytes | 6 | 6 | 2 | Variable | 4 | + |-------|-------|------|-----------|------| +Desc. | Dest. | Source| Type | IP Packet | fcs | + | MAC | MAC | | | | + `-----------------------------------------' +Total: 18 non-data bytes + +In the event that fragmentation is required, the incoming payload is split into +N parts of size ieee->fts. The first fragment contains the SNAP header and the +remaining packets are just data. + +If encryption is enabled, each fragment payload size is reduced by enough space +to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP) +So if you have 1500 bytes of payload with ieee->fts set to 500 without +encryption it will take 3 frames. With WEP it will take 4 frames as the +payload of each frame is reduced to 492 bytes. + +* SKB visualization +* +* ,- skb->data +* | +* | ETHERNET HEADER ,-<-- PAYLOAD +* | | 14 bytes from skb->data +* | 2 bytes for Type --> ,T. | (sizeof ethhdr) +* | | | | +* |,-Dest.--. ,--Src.---. | | | +* | 6 bytes| | 6 bytes | | | | +* v | | | | | | +* 0 | v 1 | v | v 2 +* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +* ^ | ^ | ^ | +* | | | | | | +* | | | | `T' <---- 2 bytes for Type +* | | | | +* | | '---SNAP--' <-------- 6 bytes for SNAP +* | | +* `-IV--' <-------------------- 4 bytes for IV (WEP) +* +* SNAP HEADER +* +*/ + +static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 }; +static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 }; + +static int libipw_copy_snap(u8 * data, __be16 h_proto) +{ + struct libipw_snap_hdr *snap; + u8 *oui; + + snap = (struct libipw_snap_hdr *)data; + snap->dsap = 0xaa; + snap->ssap = 0xaa; + snap->ctrl = 0x03; + + if (h_proto == htons(ETH_P_AARP) || h_proto == htons(ETH_P_IPX)) + oui = P802_1H_OUI; + else + oui = RFC1042_OUI; + snap->oui[0] = oui[0]; + snap->oui[1] = oui[1]; + snap->oui[2] = oui[2]; + + memcpy(data + SNAP_SIZE, &h_proto, sizeof(u16)); + + return SNAP_SIZE + sizeof(u16); +} + +static int libipw_encrypt_fragment(struct libipw_device *ieee, + struct sk_buff *frag, int hdr_len) +{ + struct lib80211_crypt_data *crypt = + ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; + int res; + + if (crypt == NULL) + return -1; + + /* To encrypt, frame format is: + * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */ + atomic_inc(&crypt->refcnt); + res = 0; + if (crypt->ops && crypt->ops->encrypt_mpdu) + res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv); + + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_INFO "%s: Encryption failed: len=%d.\n", + ieee->dev->name, frag->len); + ieee->ieee_stats.tx_discards++; + return -1; + } + + return 0; +} + +void libipw_txb_free(struct libipw_txb *txb) +{ + int i; + if (unlikely(!txb)) + return; + for (i = 0; i < txb->nr_frags; i++) + if (txb->fragments[i]) + dev_kfree_skb_any(txb->fragments[i]); + kfree(txb); +} + +static struct libipw_txb *libipw_alloc_txb(int nr_frags, int txb_size, + int headroom, gfp_t gfp_mask) +{ + struct libipw_txb *txb; + int i; + txb = kmalloc(sizeof(struct libipw_txb) + (sizeof(u8 *) * nr_frags), + gfp_mask); + if (!txb) + return NULL; + + memset(txb, 0, sizeof(struct libipw_txb)); + txb->nr_frags = nr_frags; + txb->frag_size = txb_size; + + for (i = 0; i < nr_frags; i++) { + txb->fragments[i] = __dev_alloc_skb(txb_size + headroom, + gfp_mask); + if (unlikely(!txb->fragments[i])) { + i--; + break; + } + skb_reserve(txb->fragments[i], headroom); + } + if (unlikely(i != nr_frags)) { + while (i >= 0) + dev_kfree_skb_any(txb->fragments[i--]); + kfree(txb); + return NULL; + } + return txb; +} + +static int libipw_classify(struct sk_buff *skb) +{ + struct ethhdr *eth; + struct iphdr *ip; + + eth = (struct ethhdr *)skb->data; + if (eth->h_proto != htons(ETH_P_IP)) + return 0; + + ip = ip_hdr(skb); + switch (ip->tos & 0xfc) { + case 0x20: + return 2; + case 0x40: + return 1; + case 0x60: + return 3; + case 0x80: + return 4; + case 0xa0: + return 5; + case 0xc0: + return 6; + case 0xe0: + return 7; + default: + return 0; + } +} + +/* Incoming skb is converted to a txb which consists of + * a block of 802.11 fragment packets (stored as skbs) */ +netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct libipw_device *ieee = netdev_priv(dev); + struct libipw_txb *txb = NULL; + struct libipw_hdr_3addrqos *frag_hdr; + int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size, + rts_required; + unsigned long flags; + int encrypt, host_encrypt, host_encrypt_msdu; + __be16 ether_type; + int bytes, fc, hdr_len; + struct sk_buff *skb_frag; + struct libipw_hdr_3addrqos header = {/* Ensure zero initialized */ + .duration_id = 0, + .seq_ctl = 0, + .qos_ctl = 0 + }; + u8 dest[ETH_ALEN], src[ETH_ALEN]; + struct lib80211_crypt_data *crypt; + int priority = skb->priority; + int snapped = 0; + + if (ieee->is_queue_full && (*ieee->is_queue_full) (dev, priority)) + return NETDEV_TX_BUSY; + + spin_lock_irqsave(&ieee->lock, flags); + + /* If there is no driver handler to take the TXB, dont' bother + * creating it... */ + if (!ieee->hard_start_xmit) { + printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name); + goto success; + } + + if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) { + printk(KERN_WARNING "%s: skb too small (%d).\n", + ieee->dev->name, skb->len); + goto success; + } + + ether_type = ((struct ethhdr *)skb->data)->h_proto; + + crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; + + encrypt = !(ether_type == htons(ETH_P_PAE) && ieee->ieee802_1x) && + ieee->sec.encrypt; + + host_encrypt = ieee->host_encrypt && encrypt && crypt; + host_encrypt_msdu = ieee->host_encrypt_msdu && encrypt && crypt; + + if (!encrypt && ieee->ieee802_1x && + ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) { + dev->stats.tx_dropped++; + goto success; + } + + /* Save source and destination addresses */ + skb_copy_from_linear_data(skb, dest, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN); + + if (host_encrypt) + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | + IEEE80211_FCTL_PROTECTED; + else + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; + + if (ieee->iw_mode == IW_MODE_INFRA) { + fc |= IEEE80211_FCTL_TODS; + /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ + memcpy(header.addr1, ieee->bssid, ETH_ALEN); + memcpy(header.addr2, src, ETH_ALEN); + memcpy(header.addr3, dest, ETH_ALEN); + } else if (ieee->iw_mode == IW_MODE_ADHOC) { + /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ + memcpy(header.addr1, dest, ETH_ALEN); + memcpy(header.addr2, src, ETH_ALEN); + memcpy(header.addr3, ieee->bssid, ETH_ALEN); + } + hdr_len = LIBIPW_3ADDR_LEN; + + if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) { + fc |= IEEE80211_STYPE_QOS_DATA; + hdr_len += 2; + + skb->priority = libipw_classify(skb); + header.qos_ctl |= cpu_to_le16(skb->priority & LIBIPW_QCTL_TID); + } + header.frame_ctl = cpu_to_le16(fc); + + /* Advance the SKB to the start of the payload */ + skb_pull(skb, sizeof(struct ethhdr)); + + /* Determine total amount of storage required for TXB packets */ + bytes = skb->len + SNAP_SIZE + sizeof(u16); + + /* Encrypt msdu first on the whole data packet. */ + if ((host_encrypt || host_encrypt_msdu) && + crypt && crypt->ops && crypt->ops->encrypt_msdu) { + int res = 0; + int len = bytes + hdr_len + crypt->ops->extra_msdu_prefix_len + + crypt->ops->extra_msdu_postfix_len; + struct sk_buff *skb_new = dev_alloc_skb(len); + + if (unlikely(!skb_new)) + goto failed; + + skb_reserve(skb_new, crypt->ops->extra_msdu_prefix_len); + memcpy(skb_put(skb_new, hdr_len), &header, hdr_len); + snapped = 1; + libipw_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)), + ether_type); + skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len); + res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv); + if (res < 0) { + LIBIPW_ERROR("msdu encryption failed\n"); + dev_kfree_skb_any(skb_new); + goto failed; + } + dev_kfree_skb_any(skb); + skb = skb_new; + bytes += crypt->ops->extra_msdu_prefix_len + + crypt->ops->extra_msdu_postfix_len; + skb_pull(skb, hdr_len); + } + + if (host_encrypt || ieee->host_open_frag) { + /* Determine fragmentation size based on destination (multicast + * and broadcast are not fragmented) */ + if (is_multicast_ether_addr(dest) || + is_broadcast_ether_addr(dest)) + frag_size = MAX_FRAG_THRESHOLD; + else + frag_size = ieee->fts; + + /* Determine amount of payload per fragment. Regardless of if + * this stack is providing the full 802.11 header, one will + * eventually be affixed to this fragment -- so we must account + * for it when determining the amount of payload space. */ + bytes_per_frag = frag_size - hdr_len; + if (ieee->config & + (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) + bytes_per_frag -= LIBIPW_FCS_LEN; + + /* Each fragment may need to have room for encryption + * pre/postfix */ + if (host_encrypt) + bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len + + crypt->ops->extra_mpdu_postfix_len; + + /* Number of fragments is the total + * bytes_per_frag / payload_per_fragment */ + nr_frags = bytes / bytes_per_frag; + bytes_last_frag = bytes % bytes_per_frag; + if (bytes_last_frag) + nr_frags++; + else + bytes_last_frag = bytes_per_frag; + } else { + nr_frags = 1; + bytes_per_frag = bytes_last_frag = bytes; + frag_size = bytes + hdr_len; + } + + rts_required = (frag_size > ieee->rts + && ieee->config & CFG_LIBIPW_RTS); + if (rts_required) + nr_frags++; + + /* When we allocate the TXB we allocate enough space for the reserve + * and full fragment bytes (bytes_per_frag doesn't include prefix, + * postfix, header, FCS, etc.) */ + txb = libipw_alloc_txb(nr_frags, frag_size, + ieee->tx_headroom, GFP_ATOMIC); + if (unlikely(!txb)) { + printk(KERN_WARNING "%s: Could not allocate TXB\n", + ieee->dev->name); + goto failed; + } + txb->encrypted = encrypt; + if (host_encrypt) + txb->payload_size = frag_size * (nr_frags - 1) + + bytes_last_frag; + else + txb->payload_size = bytes; + + if (rts_required) { + skb_frag = txb->fragments[0]; + frag_hdr = + (struct libipw_hdr_3addrqos *)skb_put(skb_frag, hdr_len); + + /* + * Set header frame_ctl to the RTS. + */ + header.frame_ctl = + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); + memcpy(frag_hdr, &header, hdr_len); + + /* + * Restore header frame_ctl to the original data setting. + */ + header.frame_ctl = cpu_to_le16(fc); + + if (ieee->config & + (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) + skb_put(skb_frag, 4); + + txb->rts_included = 1; + i = 1; + } else + i = 0; + + for (; i < nr_frags; i++) { + skb_frag = txb->fragments[i]; + + if (host_encrypt) + skb_reserve(skb_frag, + crypt->ops->extra_mpdu_prefix_len); + + frag_hdr = + (struct libipw_hdr_3addrqos *)skb_put(skb_frag, hdr_len); + memcpy(frag_hdr, &header, hdr_len); + + /* If this is not the last fragment, then add the MOREFRAGS + * bit to the frame control */ + if (i != nr_frags - 1) { + frag_hdr->frame_ctl = + cpu_to_le16(fc | IEEE80211_FCTL_MOREFRAGS); + bytes = bytes_per_frag; + } else { + /* The last fragment takes the remaining length */ + bytes = bytes_last_frag; + } + + if (i == 0 && !snapped) { + libipw_copy_snap(skb_put + (skb_frag, SNAP_SIZE + sizeof(u16)), + ether_type); + bytes -= SNAP_SIZE + sizeof(u16); + } + + skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes); + + /* Advance the SKB... */ + skb_pull(skb, bytes); + + /* Encryption routine will move the header forward in order + * to insert the IV between the header and the payload */ + if (host_encrypt) + libipw_encrypt_fragment(ieee, skb_frag, hdr_len); + + if (ieee->config & + (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) + skb_put(skb_frag, 4); + } + + success: + spin_unlock_irqrestore(&ieee->lock, flags); + + dev_kfree_skb_any(skb); + + if (txb) { + netdev_tx_t ret = (*ieee->hard_start_xmit)(txb, dev, priority); + if (ret == NETDEV_TX_OK) { + dev->stats.tx_packets++; + dev->stats.tx_bytes += txb->payload_size; + return NETDEV_TX_OK; + } + + libipw_txb_free(txb); + } + + return NETDEV_TX_OK; + + failed: + spin_unlock_irqrestore(&ieee->lock, flags); + netif_stop_queue(dev); + dev->stats.tx_errors++; + return NETDEV_TX_BUSY; +} +EXPORT_SYMBOL(libipw_xmit); + +EXPORT_SYMBOL(libipw_txb_free); diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_wx.c b/drivers/net/wireless/intel/ipw2x00/libipw_wx.c new file mode 100644 index 000000000000..dd29f46d086b --- /dev/null +++ b/drivers/net/wireless/intel/ipw2x00/libipw_wx.c @@ -0,0 +1,740 @@ +/****************************************************************************** + + Copyright(c) 2004-2005 Intel Corporation. All rights reserved. + + Portions of this file are based on the WEP enablement code provided by the + Host AP project hostap-drivers v0.1.3 + Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + + Copyright (c) 2002-2003, Jouni Malinen + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + Intel Linux Wireless + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "libipw.h" + +static const char *libipw_modes[] = { + "?", "a", "b", "ab", "g", "ag", "bg", "abg" +}; + +static inline unsigned int elapsed_jiffies_msecs(unsigned long start) +{ + unsigned long end = jiffies; + + if (end >= start) + return jiffies_to_msecs(end - start); + + return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); +} + +#define MAX_CUSTOM_LEN 64 +static char *libipw_translate_scan(struct libipw_device *ieee, + char *start, char *stop, + struct libipw_network *network, + struct iw_request_info *info) +{ + char custom[MAX_CUSTOM_LEN]; + char *p; + struct iw_event iwe; + int i, j; + char *current_val; /* For rates */ + u8 rate; + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); + + /* Remaining entries will be displayed in the order we provide them */ + + /* Add the ESSID */ + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + iwe.u.data.length = min(network->ssid_len, (u8) 32); + start = iwe_stream_add_point(info, start, stop, + &iwe, network->ssid); + + /* Add the protocol name */ + iwe.cmd = SIOCGIWNAME; + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", + libipw_modes[network->mode]); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); + + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { + if (network->capability & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + + start = iwe_stream_add_event(info, start, stop, + &iwe, IW_EV_UINT_LEN); + } + + /* Add channel and frequency */ + /* Note : userspace automatically computes channel using iwrange */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel); + iwe.u.freq.e = 6; + iwe.u.freq.i = 0; + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (network->capability & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + start = iwe_stream_add_point(info, start, stop, + &iwe, network->ssid); + + /* Add basic and extended rates */ + /* Rate : stuffing multiple values in a single event require a bit + * more of magic - Jean II */ + current_val = start + iwe_stream_lcp_len(info); + iwe.cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + + for (i = 0, j = 0; i < network->rates_len;) { + if (j < network->rates_ex_len && + ((network->rates_ex[j] & 0x7F) < + (network->rates[i] & 0x7F))) + rate = network->rates_ex[j++] & 0x7F; + else + rate = network->rates[i++] & 0x7F; + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe.u.bitrate.value = ((rate & 0x7f) * 500000); + /* Add new value to event */ + current_val = iwe_stream_add_value(info, start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); + } + for (; j < network->rates_ex_len; j++) { + rate = network->rates_ex[j] & 0x7F; + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe.u.bitrate.value = ((rate & 0x7f) * 500000); + /* Add new value to event */ + current_val = iwe_stream_add_value(info, start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); + } + /* Check if we added any rate */ + if ((current_val - start) > iwe_stream_lcp_len(info)) + start = current_val; + + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | + IW_QUAL_NOISE_UPDATED; + + if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) { + iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID | + IW_QUAL_LEVEL_INVALID; + iwe.u.qual.qual = 0; + } else { + if (ieee->perfect_rssi == ieee->worst_rssi) + iwe.u.qual.qual = 100; + else + iwe.u.qual.qual = + (100 * + (ieee->perfect_rssi - ieee->worst_rssi) * + (ieee->perfect_rssi - ieee->worst_rssi) - + (ieee->perfect_rssi - network->stats.rssi) * + (15 * (ieee->perfect_rssi - ieee->worst_rssi) + + 62 * (ieee->perfect_rssi - + network->stats.rssi))) / + ((ieee->perfect_rssi - + ieee->worst_rssi) * (ieee->perfect_rssi - + ieee->worst_rssi)); + if (iwe.u.qual.qual > 100) + iwe.u.qual.qual = 100; + else if (iwe.u.qual.qual < 1) + iwe.u.qual.qual = 0; + } + + if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) { + iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID; + iwe.u.qual.noise = 0; + } else { + iwe.u.qual.noise = network->stats.noise; + } + + if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) { + iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID; + iwe.u.qual.level = 0; + } else { + iwe.u.qual.level = network->stats.signal; + } + + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); + + iwe.cmd = IWEVCUSTOM; + p = custom; + + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(info, start, stop, &iwe, custom); + + memset(&iwe, 0, sizeof(iwe)); + if (network->wpa_ie_len) { + char buf[MAX_WPA_IE_LEN]; + memcpy(buf, network->wpa_ie, network->wpa_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = network->wpa_ie_len; + start = iwe_stream_add_point(info, start, stop, &iwe, buf); + } + + memset(&iwe, 0, sizeof(iwe)); + if (network->rsn_ie_len) { + char buf[MAX_WPA_IE_LEN]; + memcpy(buf, network->rsn_ie, network->rsn_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = network->rsn_ie_len; + start = iwe_stream_add_point(info, start, stop, &iwe, buf); + } + + /* Add EXTRA: Age to display seconds since last beacon/probe response + * for given network. */ + iwe.cmd = IWEVCUSTOM; + p = custom; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + " Last beacon: %ums ago", + elapsed_jiffies_msecs(network->last_scanned)); + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(info, start, stop, &iwe, custom); + + /* Add spectrum management information */ + iwe.cmd = -1; + p = custom; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: "); + + if (libipw_get_channel_flags(ieee, network->channel) & + LIBIPW_CH_INVALID) { + iwe.cmd = IWEVCUSTOM; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID "); + } + + if (libipw_get_channel_flags(ieee, network->channel) & + LIBIPW_CH_RADAR_DETECT) { + iwe.cmd = IWEVCUSTOM; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS "); + } + + if (iwe.cmd == IWEVCUSTOM) { + iwe.u.data.length = p - custom; + start = iwe_stream_add_point(info, start, stop, &iwe, custom); + } + + return start; +} + +#define SCAN_ITEM_SIZE 128 + +int libipw_wx_get_scan(struct libipw_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct libipw_network *network; + unsigned long flags; + int err = 0; + + char *ev = extra; + char *stop = ev + wrqu->data.length; + int i = 0; + + LIBIPW_DEBUG_WX("Getting scan\n"); + + spin_lock_irqsave(&ieee->lock, flags); + + list_for_each_entry(network, &ieee->network_list, list) { + i++; + if (stop - ev < SCAN_ITEM_SIZE) { + err = -E2BIG; + break; + } + + if (ieee->scan_age == 0 || + time_after(network->last_scanned + ieee->scan_age, jiffies)) + ev = libipw_translate_scan(ieee, ev, stop, network, + info); + else { + LIBIPW_DEBUG_SCAN("Not showing network '%*pE (%pM)' due to age (%ums).\n", + network->ssid_len, network->ssid, + network->bssid, + elapsed_jiffies_msecs( + network->last_scanned)); + } + } + + spin_unlock_irqrestore(&ieee->lock, flags); + + wrqu->data.length = ev - extra; + wrqu->data.flags = 0; + + LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i); + + return err; +} + +int libipw_wx_set_encode(struct libipw_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + struct iw_point *erq = &(wrqu->encoding); + struct net_device *dev = ieee->dev; + struct libipw_security sec = { + .flags = 0 + }; + int i, key, key_provided, len; + struct lib80211_crypt_data **crypt; + int host_crypto = ieee->host_encrypt || ieee->host_decrypt; + + LIBIPW_DEBUG_WX("SET_ENCODE\n"); + + key = erq->flags & IW_ENCODE_INDEX; + if (key) { + if (key > WEP_KEYS) + return -EINVAL; + key--; + key_provided = 1; + } else { + key_provided = 0; + key = ieee->crypt_info.tx_keyidx; + } + + LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ? + "provided" : "default"); + + crypt = &ieee->crypt_info.crypt[key]; + + if (erq->flags & IW_ENCODE_DISABLED) { + if (key_provided && *crypt) { + LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n", + key); + lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); + } else + LIBIPW_DEBUG_WX("Disabling encryption.\n"); + + /* Check all the keys to see if any are still configured, + * and if no key index was provided, de-init them all */ + for (i = 0; i < WEP_KEYS; i++) { + if (ieee->crypt_info.crypt[i] != NULL) { + if (key_provided) + break; + lib80211_crypt_delayed_deinit(&ieee->crypt_info, + &ieee->crypt_info.crypt[i]); + } + } + + if (i == WEP_KEYS) { + sec.enabled = 0; + sec.encrypt = 0; + sec.level = SEC_LEVEL_0; + sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT; + } + + goto done; + } + + sec.enabled = 1; + sec.encrypt = 1; + sec.flags |= SEC_ENABLED | SEC_ENCRYPT; + + if (*crypt != NULL && (*crypt)->ops != NULL && + strcmp((*crypt)->ops->name, "WEP") != 0) { + /* changing to use WEP; deinit previously used algorithm + * on this key */ + lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); + } + + if (*crypt == NULL && host_crypto) { + struct lib80211_crypt_data *new_crypt; + + /* take WEP into use */ + new_crypt = kzalloc(sizeof(struct lib80211_crypt_data), + GFP_KERNEL); + if (new_crypt == NULL) + return -ENOMEM; + new_crypt->ops = lib80211_get_crypto_ops("WEP"); + if (!new_crypt->ops) { + request_module("lib80211_crypt_wep"); + new_crypt->ops = lib80211_get_crypto_ops("WEP"); + } + + if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + new_crypt->priv = new_crypt->ops->init(key); + + if (!new_crypt->ops || !new_crypt->priv) { + kfree(new_crypt); + new_crypt = NULL; + + printk(KERN_WARNING "%s: could not initialize WEP: " + "load module lib80211_crypt_wep\n", dev->name); + return -EOPNOTSUPP; + } + *crypt = new_crypt; + } + + /* If a new key was provided, set it up */ + if (erq->length > 0) { + len = erq->length <= 5 ? 5 : 13; + memcpy(sec.keys[key], keybuf, erq->length); + if (len > erq->length) + memset(sec.keys[key] + erq->length, 0, + len - erq->length); + LIBIPW_DEBUG_WX("Setting key %d to '%*pE' (%d:%d bytes)\n", + key, len, sec.keys[key], + erq->length, len); + sec.key_sizes[key] = len; + if (*crypt) + (*crypt)->ops->set_key(sec.keys[key], len, NULL, + (*crypt)->priv); + sec.flags |= (1 << key); + /* This ensures a key will be activated if no key is + * explicitly set */ + if (key == sec.active_key) + sec.flags |= SEC_ACTIVE_KEY; + + } else { + if (host_crypto) { + len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN, + NULL, (*crypt)->priv); + if (len == 0) { + /* Set a default key of all 0 */ + LIBIPW_DEBUG_WX("Setting key %d to all " + "zero.\n", key); + memset(sec.keys[key], 0, 13); + (*crypt)->ops->set_key(sec.keys[key], 13, NULL, + (*crypt)->priv); + sec.key_sizes[key] = 13; + sec.flags |= (1 << key); + } + } + /* No key data - just set the default TX key index */ + if (key_provided) { + LIBIPW_DEBUG_WX("Setting key %d to default Tx " + "key.\n", key); + ieee->crypt_info.tx_keyidx = key; + sec.active_key = key; + sec.flags |= SEC_ACTIVE_KEY; + } + } + if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) { + ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED); + sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : + WLAN_AUTH_SHARED_KEY; + sec.flags |= SEC_AUTH_MODE; + LIBIPW_DEBUG_WX("Auth: %s\n", + sec.auth_mode == WLAN_AUTH_OPEN ? + "OPEN" : "SHARED KEY"); + } + + /* For now we just support WEP, so only set that security level... + * TODO: When WPA is added this is one place that needs to change */ + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */ + sec.encode_alg[key] = SEC_ALG_WEP; + + done: + if (ieee->set_security) + ieee->set_security(dev, &sec); + + return 0; +} + +int libipw_wx_get_encode(struct libipw_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + struct iw_point *erq = &(wrqu->encoding); + int len, key; + struct lib80211_crypt_data *crypt; + struct libipw_security *sec = &ieee->sec; + + LIBIPW_DEBUG_WX("GET_ENCODE\n"); + + key = erq->flags & IW_ENCODE_INDEX; + if (key) { + if (key > WEP_KEYS) + return -EINVAL; + key--; + } else + key = ieee->crypt_info.tx_keyidx; + + crypt = ieee->crypt_info.crypt[key]; + erq->flags = key + 1; + + if (!sec->enabled) { + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + return 0; + } + + len = sec->key_sizes[key]; + memcpy(keybuf, sec->keys[key], len); + + erq->length = len; + erq->flags |= IW_ENCODE_ENABLED; + + if (ieee->open_wep) + erq->flags |= IW_ENCODE_OPEN; + else + erq->flags |= IW_ENCODE_RESTRICTED; + + return 0; +} + +int libipw_wx_set_encodeext(struct libipw_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct net_device *dev = ieee->dev; + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int i, idx, ret = 0; + int group_key = 0; + const char *alg, *module; + struct lib80211_crypto_ops *ops; + struct lib80211_crypt_data **crypt; + + struct libipw_security sec = { + .flags = 0, + }; + + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if (idx < 1 || idx > WEP_KEYS) + return -EINVAL; + idx--; + } else + idx = ieee->crypt_info.tx_keyidx; + + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { + crypt = &ieee->crypt_info.crypt[idx]; + group_key = 1; + } else { + /* some Cisco APs use idx>0 for unicast in dynamic WEP */ + if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP) + return -EINVAL; + if (ieee->iw_mode == IW_MODE_INFRA) + crypt = &ieee->crypt_info.crypt[idx]; + else + return -EINVAL; + } + + sec.flags |= SEC_ENABLED | SEC_ENCRYPT; + if ((encoding->flags & IW_ENCODE_DISABLED) || + ext->alg == IW_ENCODE_ALG_NONE) { + if (*crypt) + lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); + + for (i = 0; i < WEP_KEYS; i++) + if (ieee->crypt_info.crypt[i] != NULL) + break; + + if (i == WEP_KEYS) { + sec.enabled = 0; + sec.encrypt = 0; + sec.level = SEC_LEVEL_0; + sec.flags |= SEC_LEVEL; + } + goto done; + } + + sec.enabled = 1; + sec.encrypt = 1; + + if (group_key ? !ieee->host_mc_decrypt : + !(ieee->host_encrypt || ieee->host_decrypt || + ieee->host_encrypt_msdu)) + goto skip_host_crypt; + + switch (ext->alg) { + case IW_ENCODE_ALG_WEP: + alg = "WEP"; + module = "lib80211_crypt_wep"; + break; + case IW_ENCODE_ALG_TKIP: + alg = "TKIP"; + module = "lib80211_crypt_tkip"; + break; + case IW_ENCODE_ALG_CCMP: + alg = "CCMP"; + module = "lib80211_crypt_ccmp"; + break; + default: + LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n", + dev->name, ext->alg); + ret = -EINVAL; + goto done; + } + + ops = lib80211_get_crypto_ops(alg); + if (ops == NULL) { + request_module(module); + ops = lib80211_get_crypto_ops(alg); + } + if (ops == NULL) { + LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n", + dev->name, ext->alg); + ret = -EINVAL; + goto done; + } + + if (*crypt == NULL || (*crypt)->ops != ops) { + struct lib80211_crypt_data *new_crypt; + + lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); + + new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL); + if (new_crypt == NULL) { + ret = -ENOMEM; + goto done; + } + new_crypt->ops = ops; + if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + new_crypt->priv = new_crypt->ops->init(idx); + if (new_crypt->priv == NULL) { + kfree(new_crypt); + ret = -EINVAL; + goto done; + } + *crypt = new_crypt; + } + + if (ext->key_len > 0 && (*crypt)->ops->set_key && + (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, + (*crypt)->priv) < 0) { + LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name); + ret = -EINVAL; + goto done; + } + + skip_host_crypt: + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + ieee->crypt_info.tx_keyidx = idx; + sec.active_key = idx; + sec.flags |= SEC_ACTIVE_KEY; + } + + if (ext->alg != IW_ENCODE_ALG_NONE) { + memcpy(sec.keys[idx], ext->key, ext->key_len); + sec.key_sizes[idx] = ext->key_len; + sec.flags |= (1 << idx); + if (ext->alg == IW_ENCODE_ALG_WEP) { + sec.encode_alg[idx] = SEC_ALG_WEP; + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; + } else if (ext->alg == IW_ENCODE_ALG_TKIP) { + sec.encode_alg[idx] = SEC_ALG_TKIP; + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_2; + } else if (ext->alg == IW_ENCODE_ALG_CCMP) { + sec.encode_alg[idx] = SEC_ALG_CCMP; + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_3; + } + /* Don't set sec level for group keys. */ + if (group_key) + sec.flags &= ~SEC_LEVEL; + } + done: + if (ieee->set_security) + ieee->set_security(dev, &sec); + + return ret; +} + +int libipw_wx_get_encodeext(struct libipw_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + struct libipw_security *sec = &ieee->sec; + int idx, max_key_len; + + max_key_len = encoding->length - sizeof(*ext); + if (max_key_len < 0) + return -EINVAL; + + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if (idx < 1 || idx > WEP_KEYS) + return -EINVAL; + idx--; + } else + idx = ieee->crypt_info.tx_keyidx; + + if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) && + ext->alg != IW_ENCODE_ALG_WEP) + if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA) + return -EINVAL; + + encoding->flags = idx + 1; + memset(ext, 0, sizeof(*ext)); + + if (!sec->enabled) { + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + encoding->flags |= IW_ENCODE_DISABLED; + } else { + if (sec->encode_alg[idx] == SEC_ALG_WEP) + ext->alg = IW_ENCODE_ALG_WEP; + else if (sec->encode_alg[idx] == SEC_ALG_TKIP) + ext->alg = IW_ENCODE_ALG_TKIP; + else if (sec->encode_alg[idx] == SEC_ALG_CCMP) + ext->alg = IW_ENCODE_ALG_CCMP; + else + return -EINVAL; + + ext->key_len = sec->key_sizes[idx]; + memcpy(ext->key, sec->keys[idx], ext->key_len); + encoding->flags |= IW_ENCODE_ENABLED; + if (ext->key_len && + (ext->alg == IW_ENCODE_ALG_TKIP || + ext->alg == IW_ENCODE_ALG_CCMP)) + ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID; + + } + + return 0; +} + +EXPORT_SYMBOL(libipw_wx_set_encodeext); +EXPORT_SYMBOL(libipw_wx_get_encodeext); + +EXPORT_SYMBOL(libipw_wx_get_scan); +EXPORT_SYMBOL(libipw_wx_set_encode); +EXPORT_SYMBOL(libipw_wx_get_encode); diff --git a/drivers/net/wireless/ipw2x00/Kconfig b/drivers/net/wireless/ipw2x00/Kconfig deleted file mode 100644 index d6ec44d7a391..000000000000 --- a/drivers/net/wireless/ipw2x00/Kconfig +++ /dev/null @@ -1,198 +0,0 @@ -# -# Intel Centrino wireless drivers -# - -config IPW2100 - tristate "Intel PRO/Wireless 2100 Network Connection" - depends on PCI && CFG80211 - select WIRELESS_EXT - select WEXT_SPY - select WEXT_PRIV - select FW_LOADER - select LIB80211 - select LIBIPW - ---help--- - A driver for the Intel PRO/Wireless 2100 Network - Connection 802.11b wireless network adapter. - - See for information on - the capabilities currently enabled in this driver and for tips - for debugging issues and problems. - - In order to use this driver, you will need a firmware image for it. - You can obtain the firmware from - . Once you have the firmware image, you - will need to place it in /lib/firmware. - - You will also very likely need the Wireless Tools in order to - configure your card: - - . - - It is recommended that you compile this driver as a module (M) - rather than built-in (Y). This driver requires firmware at device - initialization time, and when built-in this typically happens - before the filesystem is accessible (hence firmware will be - unavailable and initialization will fail). If you do choose to build - this driver into your kernel image, you can avoid this problem by - including the firmware and a firmware loader in an initramfs. - -config IPW2100_MONITOR - bool "Enable promiscuous mode" - depends on IPW2100 - ---help--- - Enables promiscuous/monitor mode support for the ipw2100 driver. - With this feature compiled into the driver, you can switch to - promiscuous mode via the Wireless Tool's Monitor mode. While in this - mode, no packets can be sent. - -config IPW2100_DEBUG - bool "Enable full debugging output in IPW2100 module." - depends on IPW2100 - ---help--- - This option will enable debug tracing output for the IPW2100. - - This will result in the kernel module being ~60k larger. You can - control which debug output is sent to the kernel log by setting the - value in - - /sys/bus/pci/drivers/ipw2100/debug_level - - This entry will only exist if this option is enabled. - - If you are not trying to debug or develop the IPW2100 driver, you - most likely want to say N here. - -config IPW2200 - tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection" - depends on PCI && CFG80211 - select CFG80211_WEXT_EXPORT - select WIRELESS_EXT - select WEXT_SPY - select WEXT_PRIV - select FW_LOADER - select LIB80211 - select LIBIPW - ---help--- - A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network - Connection adapters. - - See for - information on the capabilities currently enabled in this - driver and for tips for debugging issues and problems. - - In order to use this driver, you will need a firmware image for it. - You can obtain the firmware from - . See the above referenced README.ipw2200 - for information on where to install the firmware images. - - You will also very likely need the Wireless Tools in order to - configure your card: - - . - - It is recommended that you compile this driver as a module (M) - rather than built-in (Y). This driver requires firmware at device - initialization time, and when built-in this typically happens - before the filesystem is accessible (hence firmware will be - unavailable and initialization will fail). If you do choose to build - this driver into your kernel image, you can avoid this problem by - including the firmware and a firmware loader in an initramfs. - -config IPW2200_MONITOR - bool "Enable promiscuous mode" - depends on IPW2200 - ---help--- - Enables promiscuous/monitor mode support for the ipw2200 driver. - With this feature compiled into the driver, you can switch to - promiscuous mode via the Wireless Tool's Monitor mode. While in this - mode, no packets can be sent. - -config IPW2200_RADIOTAP - bool "Enable radiotap format 802.11 raw packet support" - depends on IPW2200_MONITOR - -config IPW2200_PROMISCUOUS - bool "Enable creation of a RF radiotap promiscuous interface" - depends on IPW2200_MONITOR - select IPW2200_RADIOTAP - ---help--- - Enables the creation of a second interface prefixed 'rtap'. - This second interface will provide every received in radiotap - format. - - This is useful for performing wireless network analysis while - maintaining an active association. - - Example usage: - - % modprobe ipw2200 rtap_iface=1 - % ifconfig rtap0 up - % tethereal -i rtap0 - - If you do not specify 'rtap_iface=1' as a module parameter then - the rtap interface will not be created and you will need to turn - it on via sysfs: - - % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface - -config IPW2200_QOS - bool "Enable QoS support" - depends on IPW2200 - -config IPW2200_DEBUG - bool "Enable full debugging output in IPW2200 module." - depends on IPW2200 - ---help--- - This option will enable low level debug tracing output for IPW2200. - - Note, normal debug code is already compiled in. This low level - debug option enables debug on hot paths (e.g Tx, Rx, ISR) and - will result in the kernel module being ~70 larger. Most users - will typically not need this high verbosity debug information. - - If you are not sure, say N here. - -config LIBIPW - tristate - depends on PCI && CFG80211 - select WIRELESS_EXT - select WEXT_SPY - select CRYPTO - select CRYPTO_ARC4 - select CRYPTO_ECB - select CRYPTO_AES - select CRYPTO_MICHAEL_MIC - select CRYPTO_ECB - select CRC32 - select LIB80211 - select LIB80211_CRYPT_WEP - select LIB80211_CRYPT_TKIP - select LIB80211_CRYPT_CCMP - ---help--- - This option enables the hardware independent IEEE 802.11 - networking stack. This component is deprecated in favor of the - mac80211 component. - -config LIBIPW_DEBUG - bool "Full debugging output for the LIBIPW component" - depends on LIBIPW - ---help--- - This option will enable debug tracing output for the - libipw component. - - This will result in the kernel module being ~70k larger. You - can control which debug output is sent to the kernel log by - setting the value in - - /proc/net/ieee80211/debug_level - - For example: - - % echo 0x00000FFO > /proc/net/ieee80211/debug_level - - For a list of values you can assign to debug_level, you - can look at the bit mask values in ieee80211.h - - If you are not trying to debug or develop the libipw - component, you most likely want to say N here. diff --git a/drivers/net/wireless/ipw2x00/Makefile b/drivers/net/wireless/ipw2x00/Makefile deleted file mode 100644 index aecd2cff462b..000000000000 --- a/drivers/net/wireless/ipw2x00/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# -# Makefile for the Intel Centrino wireless drivers -# - -obj-$(CONFIG_IPW2100) += ipw2100.o -obj-$(CONFIG_IPW2200) += ipw2200.o - -obj-$(CONFIG_LIBIPW) += libipw.o -libipw-objs := \ - libipw_module.o \ - libipw_tx.o \ - libipw_rx.o \ - libipw_wx.o \ - libipw_geo.o diff --git a/drivers/net/wireless/ipw2x00/ipw.h b/drivers/net/wireless/ipw2x00/ipw.h deleted file mode 100644 index 4007bf5ed6f3..000000000000 --- a/drivers/net/wireless/ipw2x00/ipw.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Intel Pro/Wireless 2100, 2200BG, 2915ABG network connection driver - * - * Copyright 2012 Stanislav Yakovlev - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __IPW_H__ -#define __IPW_H__ - -#include - -static const u32 ipw_cipher_suites[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, -}; - -#endif diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c deleted file mode 100644 index 36818c7f30b9..000000000000 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ /dev/null @@ -1,8639 +0,0 @@ -/****************************************************************************** - - Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. - - This program is free software; you can redistribute it and/or modify it - under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - The full GNU General Public License is included in this distribution in the - file called LICENSE. - - Contact Information: - Intel Linux Wireless - Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - - Portions of this file are based on the sample_* files provided by Wireless - Extensions 0.26 package and copyright (c) 1997-2003 Jean Tourrilhes - - - Portions of this file are based on the Host AP project, - Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - - Copyright (c) 2002-2003, Jouni Malinen - - Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and - ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c - available in the 2.4.25 kernel sources, and are copyright (c) Alan Cox - -******************************************************************************/ -/* - - Initial driver on which this is based was developed by Janusz Gorycki, - Maciej Urbaniak, and Maciej Sosnowski. - - Promiscuous mode support added by Jacek Wysoczynski and Maciej Urbaniak. - -Theory of Operation - -Tx - Commands and Data - -Firmware and host share a circular queue of Transmit Buffer Descriptors (TBDs) -Each TBD contains a pointer to the physical (dma_addr_t) address of data being -sent to the firmware as well as the length of the data. - -The host writes to the TBD queue at the WRITE index. The WRITE index points -to the _next_ packet to be written and is advanced when after the TBD has been -filled. - -The firmware pulls from the TBD queue at the READ index. The READ index points -to the currently being read entry, and is advanced once the firmware is -done with a packet. - -When data is sent to the firmware, the first TBD is used to indicate to the -firmware if a Command or Data is being sent. If it is Command, all of the -command information is contained within the physical address referred to by the -TBD. If it is Data, the first TBD indicates the type of data packet, number -of fragments, etc. The next TBD then refers to the actual packet location. - -The Tx flow cycle is as follows: - -1) ipw2100_tx() is called by kernel with SKB to transmit -2) Packet is move from the tx_free_list and appended to the transmit pending - list (tx_pend_list) -3) work is scheduled to move pending packets into the shared circular queue. -4) when placing packet in the circular queue, the incoming SKB is DMA mapped - to a physical address. That address is entered into a TBD. Two TBDs are - filled out. The first indicating a data packet, the second referring to the - actual payload data. -5) the packet is removed from tx_pend_list and placed on the end of the - firmware pending list (fw_pend_list) -6) firmware is notified that the WRITE index has -7) Once the firmware has processed the TBD, INTA is triggered. -8) For each Tx interrupt received from the firmware, the READ index is checked - to see which TBDs are done being processed. -9) For each TBD that has been processed, the ISR pulls the oldest packet - from the fw_pend_list. -10)The packet structure contained in the fw_pend_list is then used - to unmap the DMA address and to free the SKB originally passed to the driver - from the kernel. -11)The packet structure is placed onto the tx_free_list - -The above steps are the same for commands, only the msg_free_list/msg_pend_list -are used instead of tx_free_list/tx_pend_list - -... - -Critical Sections / Locking : - -There are two locks utilized. The first is the low level lock (priv->low_lock) -that protects the following: - -- Access to the Tx/Rx queue lists via priv->low_lock. The lists are as follows: - - tx_free_list : Holds pre-allocated Tx buffers. - TAIL modified in __ipw2100_tx_process() - HEAD modified in ipw2100_tx() - - tx_pend_list : Holds used Tx buffers waiting to go into the TBD ring - TAIL modified ipw2100_tx() - HEAD modified by ipw2100_tx_send_data() - - msg_free_list : Holds pre-allocated Msg (Command) buffers - TAIL modified in __ipw2100_tx_process() - HEAD modified in ipw2100_hw_send_command() - - msg_pend_list : Holds used Msg buffers waiting to go into the TBD ring - TAIL modified in ipw2100_hw_send_command() - HEAD modified in ipw2100_tx_send_commands() - - The flow of data on the TX side is as follows: - - MSG_FREE_LIST + COMMAND => MSG_PEND_LIST => TBD => MSG_FREE_LIST - TX_FREE_LIST + DATA => TX_PEND_LIST => TBD => TX_FREE_LIST - - The methods that work on the TBD ring are protected via priv->low_lock. - -- The internal data state of the device itself -- Access to the firmware read/write indexes for the BD queues - and associated logic - -All external entry functions are locked with the priv->action_lock to ensure -that only one external action is invoked at a time. - - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "ipw2100.h" -#include "ipw.h" - -#define IPW2100_VERSION "git-1.2.2" - -#define DRV_NAME "ipw2100" -#define DRV_VERSION IPW2100_VERSION -#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver" -#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" - -static struct pm_qos_request ipw2100_pm_qos_req; - -/* Debugging stuff */ -#ifdef CONFIG_IPW2100_DEBUG -#define IPW2100_RX_DEBUG /* Reception debugging */ -#endif - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); -MODULE_AUTHOR(DRV_COPYRIGHT); -MODULE_LICENSE("GPL"); - -static int debug = 0; -static int network_mode = 0; -static int channel = 0; -static int associate = 0; -static int disable = 0; -#ifdef CONFIG_PM -static struct ipw2100_fw ipw2100_firmware; -#endif - -#include -module_param(debug, int, 0444); -module_param_named(mode, network_mode, int, 0444); -module_param(channel, int, 0444); -module_param(associate, int, 0444); -module_param(disable, int, 0444); - -MODULE_PARM_DESC(debug, "debug level"); -MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)"); -MODULE_PARM_DESC(channel, "channel"); -MODULE_PARM_DESC(associate, "auto associate when scanning (default off)"); -MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])"); - -static u32 ipw2100_debug_level = IPW_DL_NONE; - -#ifdef CONFIG_IPW2100_DEBUG -#define IPW_DEBUG(level, message...) \ -do { \ - if (ipw2100_debug_level & (level)) { \ - printk(KERN_DEBUG "ipw2100: %c %s ", \ - in_interrupt() ? 'I' : 'U', __func__); \ - printk(message); \ - } \ -} while (0) -#else -#define IPW_DEBUG(level, message...) do {} while (0) -#endif /* CONFIG_IPW2100_DEBUG */ - -#ifdef CONFIG_IPW2100_DEBUG -static const char *command_types[] = { - "undefined", - "unused", /* HOST_ATTENTION */ - "HOST_COMPLETE", - "unused", /* SLEEP */ - "unused", /* HOST_POWER_DOWN */ - "unused", - "SYSTEM_CONFIG", - "unused", /* SET_IMR */ - "SSID", - "MANDATORY_BSSID", - "AUTHENTICATION_TYPE", - "ADAPTER_ADDRESS", - "PORT_TYPE", - "INTERNATIONAL_MODE", - "CHANNEL", - "RTS_THRESHOLD", - "FRAG_THRESHOLD", - "POWER_MODE", - "TX_RATES", - "BASIC_TX_RATES", - "WEP_KEY_INFO", - "unused", - "unused", - "unused", - "unused", - "WEP_KEY_INDEX", - "WEP_FLAGS", - "ADD_MULTICAST", - "CLEAR_ALL_MULTICAST", - "BEACON_INTERVAL", - "ATIM_WINDOW", - "CLEAR_STATISTICS", - "undefined", - "undefined", - "undefined", - "undefined", - "TX_POWER_INDEX", - "undefined", - "undefined", - "undefined", - "undefined", - "undefined", - "undefined", - "BROADCAST_SCAN", - "CARD_DISABLE", - "PREFERRED_BSSID", - "SET_SCAN_OPTIONS", - "SCAN_DWELL_TIME", - "SWEEP_TABLE", - "AP_OR_STATION_TABLE", - "GROUP_ORDINALS", - "SHORT_RETRY_LIMIT", - "LONG_RETRY_LIMIT", - "unused", /* SAVE_CALIBRATION */ - "unused", /* RESTORE_CALIBRATION */ - "undefined", - "undefined", - "undefined", - "HOST_PRE_POWER_DOWN", - "unused", /* HOST_INTERRUPT_COALESCING */ - "undefined", - "CARD_DISABLE_PHY_OFF", - "MSDU_TX_RATES", - "undefined", - "SET_STATION_STAT_BITS", - "CLEAR_STATIONS_STAT_BITS", - "LEAP_ROGUE_MODE", - "SET_SECURITY_INFORMATION", - "DISASSOCIATION_BSSID", - "SET_WPA_ASS_IE" -}; -#endif - -static const long ipw2100_frequencies[] = { - 2412, 2417, 2422, 2427, - 2432, 2437, 2442, 2447, - 2452, 2457, 2462, 2467, - 2472, 2484 -}; - -#define FREQ_COUNT ARRAY_SIZE(ipw2100_frequencies) - -static struct ieee80211_rate ipw2100_bg_rates[] = { - { .bitrate = 10 }, - { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, -}; - -#define RATE_COUNT ARRAY_SIZE(ipw2100_bg_rates) - -/* Pre-decl until we get the code solid and then we can clean it up */ -static void ipw2100_tx_send_commands(struct ipw2100_priv *priv); -static void ipw2100_tx_send_data(struct ipw2100_priv *priv); -static int ipw2100_adapter_setup(struct ipw2100_priv *priv); - -static void ipw2100_queues_initialize(struct ipw2100_priv *priv); -static void ipw2100_queues_free(struct ipw2100_priv *priv); -static int ipw2100_queues_allocate(struct ipw2100_priv *priv); - -static int ipw2100_fw_download(struct ipw2100_priv *priv, - struct ipw2100_fw *fw); -static int ipw2100_get_firmware(struct ipw2100_priv *priv, - struct ipw2100_fw *fw); -static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf, - size_t max); -static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf, - size_t max); -static void ipw2100_release_firmware(struct ipw2100_priv *priv, - struct ipw2100_fw *fw); -static int ipw2100_ucode_download(struct ipw2100_priv *priv, - struct ipw2100_fw *fw); -static void ipw2100_wx_event_work(struct work_struct *work); -static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev); -static struct iw_handler_def ipw2100_wx_handler_def; - -static inline void read_register(struct net_device *dev, u32 reg, u32 * val) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - - *val = ioread32(priv->ioaddr + reg); - IPW_DEBUG_IO("r: 0x%08X => 0x%08X\n", reg, *val); -} - -static inline void write_register(struct net_device *dev, u32 reg, u32 val) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - - iowrite32(val, priv->ioaddr + reg); - IPW_DEBUG_IO("w: 0x%08X <= 0x%08X\n", reg, val); -} - -static inline void read_register_word(struct net_device *dev, u32 reg, - u16 * val) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - - *val = ioread16(priv->ioaddr + reg); - IPW_DEBUG_IO("r: 0x%08X => %04X\n", reg, *val); -} - -static inline void read_register_byte(struct net_device *dev, u32 reg, u8 * val) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - - *val = ioread8(priv->ioaddr + reg); - IPW_DEBUG_IO("r: 0x%08X => %02X\n", reg, *val); -} - -static inline void write_register_word(struct net_device *dev, u32 reg, u16 val) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - - iowrite16(val, priv->ioaddr + reg); - IPW_DEBUG_IO("w: 0x%08X <= %04X\n", reg, val); -} - -static inline void write_register_byte(struct net_device *dev, u32 reg, u8 val) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - - iowrite8(val, priv->ioaddr + reg); - IPW_DEBUG_IO("w: 0x%08X =< %02X\n", reg, val); -} - -static inline void read_nic_dword(struct net_device *dev, u32 addr, u32 * val) -{ - write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, - addr & IPW_REG_INDIRECT_ADDR_MASK); - read_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); -} - -static inline void write_nic_dword(struct net_device *dev, u32 addr, u32 val) -{ - write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, - addr & IPW_REG_INDIRECT_ADDR_MASK); - write_register(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); -} - -static inline void read_nic_word(struct net_device *dev, u32 addr, u16 * val) -{ - write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, - addr & IPW_REG_INDIRECT_ADDR_MASK); - read_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); -} - -static inline void write_nic_word(struct net_device *dev, u32 addr, u16 val) -{ - write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, - addr & IPW_REG_INDIRECT_ADDR_MASK); - write_register_word(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); -} - -static inline void read_nic_byte(struct net_device *dev, u32 addr, u8 * val) -{ - write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, - addr & IPW_REG_INDIRECT_ADDR_MASK); - read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); -} - -static inline void write_nic_byte(struct net_device *dev, u32 addr, u8 val) -{ - write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, - addr & IPW_REG_INDIRECT_ADDR_MASK); - write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA, val); -} - -static inline void write_nic_auto_inc_address(struct net_device *dev, u32 addr) -{ - write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, - addr & IPW_REG_INDIRECT_ADDR_MASK); -} - -static inline void write_nic_dword_auto_inc(struct net_device *dev, u32 val) -{ - write_register(dev, IPW_REG_AUTOINCREMENT_DATA, val); -} - -static void write_nic_memory(struct net_device *dev, u32 addr, u32 len, - const u8 * buf) -{ - u32 aligned_addr; - u32 aligned_len; - u32 dif_len; - u32 i; - - /* read first nibble byte by byte */ - aligned_addr = addr & (~0x3); - dif_len = addr - aligned_addr; - if (dif_len) { - /* Start reading at aligned_addr + dif_len */ - write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, - aligned_addr); - for (i = dif_len; i < 4; i++, buf++) - write_register_byte(dev, - IPW_REG_INDIRECT_ACCESS_DATA + i, - *buf); - - len -= dif_len; - aligned_addr += 4; - } - - /* read DWs through autoincrement registers */ - write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr); - aligned_len = len & (~0x3); - for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4) - write_register(dev, IPW_REG_AUTOINCREMENT_DATA, *(u32 *) buf); - - /* copy the last nibble */ - dif_len = len - aligned_len; - write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr); - for (i = 0; i < dif_len; i++, buf++) - write_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, - *buf); -} - -static void read_nic_memory(struct net_device *dev, u32 addr, u32 len, - u8 * buf) -{ - u32 aligned_addr; - u32 aligned_len; - u32 dif_len; - u32 i; - - /* read first nibble byte by byte */ - aligned_addr = addr & (~0x3); - dif_len = addr - aligned_addr; - if (dif_len) { - /* Start reading at aligned_addr + dif_len */ - write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, - aligned_addr); - for (i = dif_len; i < 4; i++, buf++) - read_register_byte(dev, - IPW_REG_INDIRECT_ACCESS_DATA + i, - buf); - - len -= dif_len; - aligned_addr += 4; - } - - /* read DWs through autoincrement registers */ - write_register(dev, IPW_REG_AUTOINCREMENT_ADDRESS, aligned_addr); - aligned_len = len & (~0x3); - for (i = 0; i < aligned_len; i += 4, buf += 4, aligned_addr += 4) - read_register(dev, IPW_REG_AUTOINCREMENT_DATA, (u32 *) buf); - - /* copy the last nibble */ - dif_len = len - aligned_len; - write_register(dev, IPW_REG_INDIRECT_ACCESS_ADDRESS, aligned_addr); - for (i = 0; i < dif_len; i++, buf++) - read_register_byte(dev, IPW_REG_INDIRECT_ACCESS_DATA + i, buf); -} - -static bool ipw2100_hw_is_adapter_in_system(struct net_device *dev) -{ - u32 dbg; - - read_register(dev, IPW_REG_DOA_DEBUG_AREA_START, &dbg); - - return dbg == IPW_DATA_DOA_DEBUG_VALUE; -} - -static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord, - void *val, u32 * len) -{ - struct ipw2100_ordinals *ordinals = &priv->ordinals; - u32 addr; - u32 field_info; - u16 field_len; - u16 field_count; - u32 total_length; - - if (ordinals->table1_addr == 0) { - printk(KERN_WARNING DRV_NAME ": attempt to use fw ordinals " - "before they have been loaded.\n"); - return -EINVAL; - } - - if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) { - if (*len < IPW_ORD_TAB_1_ENTRY_SIZE) { - *len = IPW_ORD_TAB_1_ENTRY_SIZE; - - printk(KERN_WARNING DRV_NAME - ": ordinal buffer length too small, need %zd\n", - IPW_ORD_TAB_1_ENTRY_SIZE); - - return -EINVAL; - } - - read_nic_dword(priv->net_dev, - ordinals->table1_addr + (ord << 2), &addr); - read_nic_dword(priv->net_dev, addr, val); - - *len = IPW_ORD_TAB_1_ENTRY_SIZE; - - return 0; - } - - if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) { - - ord -= IPW_START_ORD_TAB_2; - - /* get the address of statistic */ - read_nic_dword(priv->net_dev, - ordinals->table2_addr + (ord << 3), &addr); - - /* get the second DW of statistics ; - * two 16-bit words - first is length, second is count */ - read_nic_dword(priv->net_dev, - ordinals->table2_addr + (ord << 3) + sizeof(u32), - &field_info); - - /* get each entry length */ - field_len = *((u16 *) & field_info); - - /* get number of entries */ - field_count = *(((u16 *) & field_info) + 1); - - /* abort if no enough memory */ - total_length = field_len * field_count; - if (total_length > *len) { - *len = total_length; - return -EINVAL; - } - - *len = total_length; - if (!total_length) - return 0; - - /* read the ordinal data from the SRAM */ - read_nic_memory(priv->net_dev, addr, total_length, val); - - return 0; - } - - printk(KERN_WARNING DRV_NAME ": ordinal %d neither in table 1 nor " - "in table 2\n", ord); - - return -EINVAL; -} - -static int ipw2100_set_ordinal(struct ipw2100_priv *priv, u32 ord, u32 * val, - u32 * len) -{ - struct ipw2100_ordinals *ordinals = &priv->ordinals; - u32 addr; - - if (IS_ORDINAL_TABLE_ONE(ordinals, ord)) { - if (*len != IPW_ORD_TAB_1_ENTRY_SIZE) { - *len = IPW_ORD_TAB_1_ENTRY_SIZE; - IPW_DEBUG_INFO("wrong size\n"); - return -EINVAL; - } - - read_nic_dword(priv->net_dev, - ordinals->table1_addr + (ord << 2), &addr); - - write_nic_dword(priv->net_dev, addr, *val); - - *len = IPW_ORD_TAB_1_ENTRY_SIZE; - - return 0; - } - - IPW_DEBUG_INFO("wrong table\n"); - if (IS_ORDINAL_TABLE_TWO(ordinals, ord)) - return -EINVAL; - - return -EINVAL; -} - -static char *snprint_line(char *buf, size_t count, - const u8 * data, u32 len, u32 ofs) -{ - int out, i, j, l; - char c; - - out = snprintf(buf, count, "%08X", ofs); - - for (l = 0, i = 0; i < 2; i++) { - out += snprintf(buf + out, count - out, " "); - for (j = 0; j < 8 && l < len; j++, l++) - out += snprintf(buf + out, count - out, "%02X ", - data[(i * 8 + j)]); - for (; j < 8; j++) - out += snprintf(buf + out, count - out, " "); - } - - out += snprintf(buf + out, count - out, " "); - for (l = 0, i = 0; i < 2; i++) { - out += snprintf(buf + out, count - out, " "); - for (j = 0; j < 8 && l < len; j++, l++) { - c = data[(i * 8 + j)]; - if (!isascii(c) || !isprint(c)) - c = '.'; - - out += snprintf(buf + out, count - out, "%c", c); - } - - for (; j < 8; j++) - out += snprintf(buf + out, count - out, " "); - } - - return buf; -} - -static void printk_buf(int level, const u8 * data, u32 len) -{ - char line[81]; - u32 ofs = 0; - if (!(ipw2100_debug_level & level)) - return; - - while (len) { - printk(KERN_DEBUG "%s\n", - snprint_line(line, sizeof(line), &data[ofs], - min(len, 16U), ofs)); - ofs += 16; - len -= min(len, 16U); - } -} - -#define MAX_RESET_BACKOFF 10 - -static void schedule_reset(struct ipw2100_priv *priv) -{ - unsigned long now = get_seconds(); - - /* If we haven't received a reset request within the backoff period, - * then we can reset the backoff interval so this reset occurs - * immediately */ - if (priv->reset_backoff && - (now - priv->last_reset > priv->reset_backoff)) - priv->reset_backoff = 0; - - priv->last_reset = get_seconds(); - - if (!(priv->status & STATUS_RESET_PENDING)) { - IPW_DEBUG_INFO("%s: Scheduling firmware restart (%ds).\n", - priv->net_dev->name, priv->reset_backoff); - netif_carrier_off(priv->net_dev); - netif_stop_queue(priv->net_dev); - priv->status |= STATUS_RESET_PENDING; - if (priv->reset_backoff) - schedule_delayed_work(&priv->reset_work, - priv->reset_backoff * HZ); - else - schedule_delayed_work(&priv->reset_work, 0); - - if (priv->reset_backoff < MAX_RESET_BACKOFF) - priv->reset_backoff++; - - wake_up_interruptible(&priv->wait_command_queue); - } else - IPW_DEBUG_INFO("%s: Firmware restart already in progress.\n", - priv->net_dev->name); - -} - -#define HOST_COMPLETE_TIMEOUT (2 * HZ) -static int ipw2100_hw_send_command(struct ipw2100_priv *priv, - struct host_command *cmd) -{ - struct list_head *element; - struct ipw2100_tx_packet *packet; - unsigned long flags; - int err = 0; - - IPW_DEBUG_HC("Sending %s command (#%d), %d bytes\n", - command_types[cmd->host_command], cmd->host_command, - cmd->host_command_length); - printk_buf(IPW_DL_HC, (u8 *) cmd->host_command_parameters, - cmd->host_command_length); - - spin_lock_irqsave(&priv->low_lock, flags); - - if (priv->fatal_error) { - IPW_DEBUG_INFO - ("Attempt to send command while hardware in fatal error condition.\n"); - err = -EIO; - goto fail_unlock; - } - - if (!(priv->status & STATUS_RUNNING)) { - IPW_DEBUG_INFO - ("Attempt to send command while hardware is not running.\n"); - err = -EIO; - goto fail_unlock; - } - - if (priv->status & STATUS_CMD_ACTIVE) { - IPW_DEBUG_INFO - ("Attempt to send command while another command is pending.\n"); - err = -EBUSY; - goto fail_unlock; - } - - if (list_empty(&priv->msg_free_list)) { - IPW_DEBUG_INFO("no available msg buffers\n"); - goto fail_unlock; - } - - priv->status |= STATUS_CMD_ACTIVE; - priv->messages_sent++; - - element = priv->msg_free_list.next; - - packet = list_entry(element, struct ipw2100_tx_packet, list); - packet->jiffy_start = jiffies; - - /* initialize the firmware command packet */ - packet->info.c_struct.cmd->host_command_reg = cmd->host_command; - packet->info.c_struct.cmd->host_command_reg1 = cmd->host_command1; - packet->info.c_struct.cmd->host_command_len_reg = - cmd->host_command_length; - packet->info.c_struct.cmd->sequence = cmd->host_command_sequence; - - memcpy(packet->info.c_struct.cmd->host_command_params_reg, - cmd->host_command_parameters, - sizeof(packet->info.c_struct.cmd->host_command_params_reg)); - - list_del(element); - DEC_STAT(&priv->msg_free_stat); - - list_add_tail(element, &priv->msg_pend_list); - INC_STAT(&priv->msg_pend_stat); - - ipw2100_tx_send_commands(priv); - ipw2100_tx_send_data(priv); - - spin_unlock_irqrestore(&priv->low_lock, flags); - - /* - * We must wait for this command to complete before another - * command can be sent... but if we wait more than 3 seconds - * then there is a problem. - */ - - err = - wait_event_interruptible_timeout(priv->wait_command_queue, - !(priv-> - status & STATUS_CMD_ACTIVE), - HOST_COMPLETE_TIMEOUT); - - if (err == 0) { - IPW_DEBUG_INFO("Command completion failed out after %dms.\n", - 1000 * (HOST_COMPLETE_TIMEOUT / HZ)); - priv->fatal_error = IPW2100_ERR_MSG_TIMEOUT; - priv->status &= ~STATUS_CMD_ACTIVE; - schedule_reset(priv); - return -EIO; - } - - if (priv->fatal_error) { - printk(KERN_WARNING DRV_NAME ": %s: firmware fatal error\n", - priv->net_dev->name); - return -EIO; - } - - /* !!!!! HACK TEST !!!!! - * When lots of debug trace statements are enabled, the driver - * doesn't seem to have as many firmware restart cycles... - * - * As a test, we're sticking in a 1/100s delay here */ - schedule_timeout_uninterruptible(msecs_to_jiffies(10)); - - return 0; - - fail_unlock: - spin_unlock_irqrestore(&priv->low_lock, flags); - - return err; -} - -/* - * Verify the values and data access of the hardware - * No locks needed or used. No functions called. - */ -static int ipw2100_verify(struct ipw2100_priv *priv) -{ - u32 data1, data2; - u32 address; - - u32 val1 = 0x76543210; - u32 val2 = 0xFEDCBA98; - - /* Domain 0 check - all values should be DOA_DEBUG */ - for (address = IPW_REG_DOA_DEBUG_AREA_START; - address < IPW_REG_DOA_DEBUG_AREA_END; address += sizeof(u32)) { - read_register(priv->net_dev, address, &data1); - if (data1 != IPW_DATA_DOA_DEBUG_VALUE) - return -EIO; - } - - /* Domain 1 check - use arbitrary read/write compare */ - for (address = 0; address < 5; address++) { - /* The memory area is not used now */ - write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32, - val1); - write_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36, - val2); - read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x32, - &data1); - read_register(priv->net_dev, IPW_REG_DOMAIN_1_OFFSET + 0x36, - &data2); - if (val1 == data1 && val2 == data2) - return 0; - } - - return -EIO; -} - -/* - * - * Loop until the CARD_DISABLED bit is the same value as the - * supplied parameter - * - * TODO: See if it would be more efficient to do a wait/wake - * cycle and have the completion event trigger the wakeup - * - */ -#define IPW_CARD_DISABLE_COMPLETE_WAIT 100 // 100 milli -static int ipw2100_wait_for_card_state(struct ipw2100_priv *priv, int state) -{ - int i; - u32 card_state; - u32 len = sizeof(card_state); - int err; - - for (i = 0; i <= IPW_CARD_DISABLE_COMPLETE_WAIT * 1000; i += 50) { - err = ipw2100_get_ordinal(priv, IPW_ORD_CARD_DISABLED, - &card_state, &len); - if (err) { - IPW_DEBUG_INFO("Query of CARD_DISABLED ordinal " - "failed.\n"); - return 0; - } - - /* We'll break out if either the HW state says it is - * in the state we want, or if HOST_COMPLETE command - * finishes */ - if ((card_state == state) || - ((priv->status & STATUS_ENABLED) ? - IPW_HW_STATE_ENABLED : IPW_HW_STATE_DISABLED) == state) { - if (state == IPW_HW_STATE_ENABLED) - priv->status |= STATUS_ENABLED; - else - priv->status &= ~STATUS_ENABLED; - - return 0; - } - - udelay(50); - } - - IPW_DEBUG_INFO("ipw2100_wait_for_card_state to %s state timed out\n", - state ? "DISABLED" : "ENABLED"); - return -EIO; -} - -/********************************************************************* - Procedure : sw_reset_and_clock - Purpose : Asserts s/w reset, asserts clock initialization - and waits for clock stabilization - ********************************************************************/ -static int sw_reset_and_clock(struct ipw2100_priv *priv) -{ - int i; - u32 r; - - // assert s/w reset - write_register(priv->net_dev, IPW_REG_RESET_REG, - IPW_AUX_HOST_RESET_REG_SW_RESET); - - // wait for clock stabilization - for (i = 0; i < 1000; i++) { - udelay(IPW_WAIT_RESET_ARC_COMPLETE_DELAY); - - // check clock ready bit - read_register(priv->net_dev, IPW_REG_RESET_REG, &r); - if (r & IPW_AUX_HOST_RESET_REG_PRINCETON_RESET) - break; - } - - if (i == 1000) - return -EIO; // TODO: better error value - - /* set "initialization complete" bit to move adapter to - * D0 state */ - write_register(priv->net_dev, IPW_REG_GP_CNTRL, - IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE); - - /* wait for clock stabilization */ - for (i = 0; i < 10000; i++) { - udelay(IPW_WAIT_CLOCK_STABILIZATION_DELAY * 4); - - /* check clock ready bit */ - read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r); - if (r & IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY) - break; - } - - if (i == 10000) - return -EIO; /* TODO: better error value */ - - /* set D0 standby bit */ - read_register(priv->net_dev, IPW_REG_GP_CNTRL, &r); - write_register(priv->net_dev, IPW_REG_GP_CNTRL, - r | IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY); - - return 0; -} - -/********************************************************************* - Procedure : ipw2100_download_firmware - Purpose : Initiaze adapter after power on. - The sequence is: - 1. assert s/w reset first! - 2. awake clocks & wait for clock stabilization - 3. hold ARC (don't ask me why...) - 4. load Dino ucode and reset/clock init again - 5. zero-out shared mem - 6. download f/w - *******************************************************************/ -static int ipw2100_download_firmware(struct ipw2100_priv *priv) -{ - u32 address; - int err; - -#ifndef CONFIG_PM - /* Fetch the firmware and microcode */ - struct ipw2100_fw ipw2100_firmware; -#endif - - if (priv->fatal_error) { - IPW_DEBUG_ERROR("%s: ipw2100_download_firmware called after " - "fatal error %d. Interface must be brought down.\n", - priv->net_dev->name, priv->fatal_error); - return -EINVAL; - } -#ifdef CONFIG_PM - if (!ipw2100_firmware.version) { - err = ipw2100_get_firmware(priv, &ipw2100_firmware); - if (err) { - IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n", - priv->net_dev->name, err); - priv->fatal_error = IPW2100_ERR_FW_LOAD; - goto fail; - } - } -#else - err = ipw2100_get_firmware(priv, &ipw2100_firmware); - if (err) { - IPW_DEBUG_ERROR("%s: ipw2100_get_firmware failed: %d\n", - priv->net_dev->name, err); - priv->fatal_error = IPW2100_ERR_FW_LOAD; - goto fail; - } -#endif - priv->firmware_version = ipw2100_firmware.version; - - /* s/w reset and clock stabilization */ - err = sw_reset_and_clock(priv); - if (err) { - IPW_DEBUG_ERROR("%s: sw_reset_and_clock failed: %d\n", - priv->net_dev->name, err); - goto fail; - } - - err = ipw2100_verify(priv); - if (err) { - IPW_DEBUG_ERROR("%s: ipw2100_verify failed: %d\n", - priv->net_dev->name, err); - goto fail; - } - - /* Hold ARC */ - write_nic_dword(priv->net_dev, - IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x80000000); - - /* allow ARC to run */ - write_register(priv->net_dev, IPW_REG_RESET_REG, 0); - - /* load microcode */ - err = ipw2100_ucode_download(priv, &ipw2100_firmware); - if (err) { - printk(KERN_ERR DRV_NAME ": %s: Error loading microcode: %d\n", - priv->net_dev->name, err); - goto fail; - } - - /* release ARC */ - write_nic_dword(priv->net_dev, - IPW_INTERNAL_REGISTER_HALT_AND_RESET, 0x00000000); - - /* s/w reset and clock stabilization (again!!!) */ - err = sw_reset_and_clock(priv); - if (err) { - printk(KERN_ERR DRV_NAME - ": %s: sw_reset_and_clock failed: %d\n", - priv->net_dev->name, err); - goto fail; - } - - /* load f/w */ - err = ipw2100_fw_download(priv, &ipw2100_firmware); - if (err) { - IPW_DEBUG_ERROR("%s: Error loading firmware: %d\n", - priv->net_dev->name, err); - goto fail; - } -#ifndef CONFIG_PM - /* - * When the .resume method of the driver is called, the other - * part of the system, i.e. the ide driver could still stay in - * the suspend stage. This prevents us from loading the firmware - * from the disk. --YZ - */ - - /* free any storage allocated for firmware image */ - ipw2100_release_firmware(priv, &ipw2100_firmware); -#endif - - /* zero out Domain 1 area indirectly (Si requirement) */ - for (address = IPW_HOST_FW_SHARED_AREA0; - address < IPW_HOST_FW_SHARED_AREA0_END; address += 4) - write_nic_dword(priv->net_dev, address, 0); - for (address = IPW_HOST_FW_SHARED_AREA1; - address < IPW_HOST_FW_SHARED_AREA1_END; address += 4) - write_nic_dword(priv->net_dev, address, 0); - for (address = IPW_HOST_FW_SHARED_AREA2; - address < IPW_HOST_FW_SHARED_AREA2_END; address += 4) - write_nic_dword(priv->net_dev, address, 0); - for (address = IPW_HOST_FW_SHARED_AREA3; - address < IPW_HOST_FW_SHARED_AREA3_END; address += 4) - write_nic_dword(priv->net_dev, address, 0); - for (address = IPW_HOST_FW_INTERRUPT_AREA; - address < IPW_HOST_FW_INTERRUPT_AREA_END; address += 4) - write_nic_dword(priv->net_dev, address, 0); - - return 0; - - fail: - ipw2100_release_firmware(priv, &ipw2100_firmware); - return err; -} - -static inline void ipw2100_enable_interrupts(struct ipw2100_priv *priv) -{ - if (priv->status & STATUS_INT_ENABLED) - return; - priv->status |= STATUS_INT_ENABLED; - write_register(priv->net_dev, IPW_REG_INTA_MASK, IPW_INTERRUPT_MASK); -} - -static inline void ipw2100_disable_interrupts(struct ipw2100_priv *priv) -{ - if (!(priv->status & STATUS_INT_ENABLED)) - return; - priv->status &= ~STATUS_INT_ENABLED; - write_register(priv->net_dev, IPW_REG_INTA_MASK, 0x0); -} - -static void ipw2100_initialize_ordinals(struct ipw2100_priv *priv) -{ - struct ipw2100_ordinals *ord = &priv->ordinals; - - IPW_DEBUG_INFO("enter\n"); - - read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1, - &ord->table1_addr); - - read_register(priv->net_dev, IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2, - &ord->table2_addr); - - read_nic_dword(priv->net_dev, ord->table1_addr, &ord->table1_size); - read_nic_dword(priv->net_dev, ord->table2_addr, &ord->table2_size); - - ord->table2_size &= 0x0000FFFF; - - IPW_DEBUG_INFO("table 1 size: %d\n", ord->table1_size); - IPW_DEBUG_INFO("table 2 size: %d\n", ord->table2_size); - IPW_DEBUG_INFO("exit\n"); -} - -static inline void ipw2100_hw_set_gpio(struct ipw2100_priv *priv) -{ - u32 reg = 0; - /* - * Set GPIO 3 writable by FW; GPIO 1 writable - * by driver and enable clock - */ - reg = (IPW_BIT_GPIO_GPIO3_MASK | IPW_BIT_GPIO_GPIO1_ENABLE | - IPW_BIT_GPIO_LED_OFF); - write_register(priv->net_dev, IPW_REG_GPIO, reg); -} - -static int rf_kill_active(struct ipw2100_priv *priv) -{ -#define MAX_RF_KILL_CHECKS 5 -#define RF_KILL_CHECK_DELAY 40 - - unsigned short value = 0; - u32 reg = 0; - int i; - - if (!(priv->hw_features & HW_FEATURE_RFKILL)) { - wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); - priv->status &= ~STATUS_RF_KILL_HW; - return 0; - } - - for (i = 0; i < MAX_RF_KILL_CHECKS; i++) { - udelay(RF_KILL_CHECK_DELAY); - read_register(priv->net_dev, IPW_REG_GPIO, ®); - value = (value << 1) | ((reg & IPW_BIT_GPIO_RF_KILL) ? 0 : 1); - } - - if (value == 0) { - wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); - priv->status |= STATUS_RF_KILL_HW; - } else { - wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); - priv->status &= ~STATUS_RF_KILL_HW; - } - - return (value == 0); -} - -static int ipw2100_get_hw_features(struct ipw2100_priv *priv) -{ - u32 addr, len; - u32 val; - - /* - * EEPROM_SRAM_DB_START_ADDRESS using ordinal in ordinal table 1 - */ - len = sizeof(addr); - if (ipw2100_get_ordinal - (priv, IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, &addr, &len)) { - IPW_DEBUG_INFO("failed querying ordinals at line %d\n", - __LINE__); - return -EIO; - } - - IPW_DEBUG_INFO("EEPROM address: %08X\n", addr); - - /* - * EEPROM version is the byte at offset 0xfd in firmware - * We read 4 bytes, then shift out the byte we actually want */ - read_nic_dword(priv->net_dev, addr + 0xFC, &val); - priv->eeprom_version = (val >> 24) & 0xFF; - IPW_DEBUG_INFO("EEPROM version: %d\n", priv->eeprom_version); - - /* - * HW RF Kill enable is bit 0 in byte at offset 0x21 in firmware - * - * notice that the EEPROM bit is reverse polarity, i.e. - * bit = 0 signifies HW RF kill switch is supported - * bit = 1 signifies HW RF kill switch is NOT supported - */ - read_nic_dword(priv->net_dev, addr + 0x20, &val); - if (!((val >> 24) & 0x01)) - priv->hw_features |= HW_FEATURE_RFKILL; - - IPW_DEBUG_INFO("HW RF Kill: %ssupported.\n", - (priv->hw_features & HW_FEATURE_RFKILL) ? "" : "not "); - - return 0; -} - -/* - * Start firmware execution after power on and intialization - * The sequence is: - * 1. Release ARC - * 2. Wait for f/w initialization completes; - */ -static int ipw2100_start_adapter(struct ipw2100_priv *priv) -{ - int i; - u32 inta, inta_mask, gpio; - - IPW_DEBUG_INFO("enter\n"); - - if (priv->status & STATUS_RUNNING) - return 0; - - /* - * Initialize the hw - drive adapter to DO state by setting - * init_done bit. Wait for clk_ready bit and Download - * fw & dino ucode - */ - if (ipw2100_download_firmware(priv)) { - printk(KERN_ERR DRV_NAME - ": %s: Failed to power on the adapter.\n", - priv->net_dev->name); - return -EIO; - } - - /* Clear the Tx, Rx and Msg queues and the r/w indexes - * in the firmware RBD and TBD ring queue */ - ipw2100_queues_initialize(priv); - - ipw2100_hw_set_gpio(priv); - - /* TODO -- Look at disabling interrupts here to make sure none - * get fired during FW initialization */ - - /* Release ARC - clear reset bit */ - write_register(priv->net_dev, IPW_REG_RESET_REG, 0); - - /* wait for f/w intialization complete */ - IPW_DEBUG_FW("Waiting for f/w initialization to complete...\n"); - i = 5000; - do { - schedule_timeout_uninterruptible(msecs_to_jiffies(40)); - /* Todo... wait for sync command ... */ - - read_register(priv->net_dev, IPW_REG_INTA, &inta); - - /* check "init done" bit */ - if (inta & IPW2100_INTA_FW_INIT_DONE) { - /* reset "init done" bit */ - write_register(priv->net_dev, IPW_REG_INTA, - IPW2100_INTA_FW_INIT_DONE); - break; - } - - /* check error conditions : we check these after the firmware - * check so that if there is an error, the interrupt handler - * will see it and the adapter will be reset */ - if (inta & - (IPW2100_INTA_FATAL_ERROR | IPW2100_INTA_PARITY_ERROR)) { - /* clear error conditions */ - write_register(priv->net_dev, IPW_REG_INTA, - IPW2100_INTA_FATAL_ERROR | - IPW2100_INTA_PARITY_ERROR); - } - } while (--i); - - /* Clear out any pending INTAs since we aren't supposed to have - * interrupts enabled at this point... */ - read_register(priv->net_dev, IPW_REG_INTA, &inta); - read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask); - inta &= IPW_INTERRUPT_MASK; - /* Clear out any pending interrupts */ - if (inta & inta_mask) - write_register(priv->net_dev, IPW_REG_INTA, inta); - - IPW_DEBUG_FW("f/w initialization complete: %s\n", - i ? "SUCCESS" : "FAILED"); - - if (!i) { - printk(KERN_WARNING DRV_NAME - ": %s: Firmware did not initialize.\n", - priv->net_dev->name); - return -EIO; - } - - /* allow firmware to write to GPIO1 & GPIO3 */ - read_register(priv->net_dev, IPW_REG_GPIO, &gpio); - - gpio |= (IPW_BIT_GPIO_GPIO1_MASK | IPW_BIT_GPIO_GPIO3_MASK); - - write_register(priv->net_dev, IPW_REG_GPIO, gpio); - - /* Ready to receive commands */ - priv->status |= STATUS_RUNNING; - - /* The adapter has been reset; we are not associated */ - priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED); - - IPW_DEBUG_INFO("exit\n"); - - return 0; -} - -static inline void ipw2100_reset_fatalerror(struct ipw2100_priv *priv) -{ - if (!priv->fatal_error) - return; - - priv->fatal_errors[priv->fatal_index++] = priv->fatal_error; - priv->fatal_index %= IPW2100_ERROR_QUEUE; - priv->fatal_error = 0; -} - -/* NOTE: Our interrupt is disabled when this method is called */ -static int ipw2100_power_cycle_adapter(struct ipw2100_priv *priv) -{ - u32 reg; - int i; - - IPW_DEBUG_INFO("Power cycling the hardware.\n"); - - ipw2100_hw_set_gpio(priv); - - /* Step 1. Stop Master Assert */ - write_register(priv->net_dev, IPW_REG_RESET_REG, - IPW_AUX_HOST_RESET_REG_STOP_MASTER); - - /* Step 2. Wait for stop Master Assert - * (not more than 50us, otherwise ret error */ - i = 5; - do { - udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY); - read_register(priv->net_dev, IPW_REG_RESET_REG, ®); - - if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED) - break; - } while (--i); - - priv->status &= ~STATUS_RESET_PENDING; - - if (!i) { - IPW_DEBUG_INFO - ("exit - waited too long for master assert stop\n"); - return -EIO; - } - - write_register(priv->net_dev, IPW_REG_RESET_REG, - IPW_AUX_HOST_RESET_REG_SW_RESET); - - /* Reset any fatal_error conditions */ - ipw2100_reset_fatalerror(priv); - - /* At this point, the adapter is now stopped and disabled */ - priv->status &= ~(STATUS_RUNNING | STATUS_ASSOCIATING | - STATUS_ASSOCIATED | STATUS_ENABLED); - - return 0; -} - -/* - * Send the CARD_DISABLE_PHY_OFF command to the card to disable it - * - * After disabling, if the card was associated, a STATUS_ASSN_LOST will be sent. - * - * STATUS_CARD_DISABLE_NOTIFICATION will be sent regardless of - * if STATUS_ASSN_LOST is sent. - */ -static int ipw2100_hw_phy_off(struct ipw2100_priv *priv) -{ - -#define HW_PHY_OFF_LOOP_DELAY (msecs_to_jiffies(50)) - - struct host_command cmd = { - .host_command = CARD_DISABLE_PHY_OFF, - .host_command_sequence = 0, - .host_command_length = 0, - }; - int err, i; - u32 val1, val2; - - IPW_DEBUG_HC("CARD_DISABLE_PHY_OFF\n"); - - /* Turn off the radio */ - err = ipw2100_hw_send_command(priv, &cmd); - if (err) - return err; - - for (i = 0; i < 2500; i++) { - read_nic_dword(priv->net_dev, IPW2100_CONTROL_REG, &val1); - read_nic_dword(priv->net_dev, IPW2100_COMMAND, &val2); - - if ((val1 & IPW2100_CONTROL_PHY_OFF) && - (val2 & IPW2100_COMMAND_PHY_OFF)) - return 0; - - schedule_timeout_uninterruptible(HW_PHY_OFF_LOOP_DELAY); - } - - return -EIO; -} - -static int ipw2100_enable_adapter(struct ipw2100_priv *priv) -{ - struct host_command cmd = { - .host_command = HOST_COMPLETE, - .host_command_sequence = 0, - .host_command_length = 0 - }; - int err = 0; - - IPW_DEBUG_HC("HOST_COMPLETE\n"); - - if (priv->status & STATUS_ENABLED) - return 0; - - mutex_lock(&priv->adapter_mutex); - - if (rf_kill_active(priv)) { - IPW_DEBUG_HC("Command aborted due to RF kill active.\n"); - goto fail_up; - } - - err = ipw2100_hw_send_command(priv, &cmd); - if (err) { - IPW_DEBUG_INFO("Failed to send HOST_COMPLETE command\n"); - goto fail_up; - } - - err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_ENABLED); - if (err) { - IPW_DEBUG_INFO("%s: card not responding to init command.\n", - priv->net_dev->name); - goto fail_up; - } - - if (priv->stop_hang_check) { - priv->stop_hang_check = 0; - schedule_delayed_work(&priv->hang_check, HZ / 2); - } - - fail_up: - mutex_unlock(&priv->adapter_mutex); - return err; -} - -static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv) -{ -#define HW_POWER_DOWN_DELAY (msecs_to_jiffies(100)) - - struct host_command cmd = { - .host_command = HOST_PRE_POWER_DOWN, - .host_command_sequence = 0, - .host_command_length = 0, - }; - int err, i; - u32 reg; - - if (!(priv->status & STATUS_RUNNING)) - return 0; - - priv->status |= STATUS_STOPPING; - - /* We can only shut down the card if the firmware is operational. So, - * if we haven't reset since a fatal_error, then we can not send the - * shutdown commands. */ - if (!priv->fatal_error) { - /* First, make sure the adapter is enabled so that the PHY_OFF - * command can shut it down */ - ipw2100_enable_adapter(priv); - - err = ipw2100_hw_phy_off(priv); - if (err) - printk(KERN_WARNING DRV_NAME - ": Error disabling radio %d\n", err); - - /* - * If in D0-standby mode going directly to D3 may cause a - * PCI bus violation. Therefore we must change out of the D0 - * state. - * - * Sending the PREPARE_FOR_POWER_DOWN will restrict the - * hardware from going into standby mode and will transition - * out of D0-standby if it is already in that state. - * - * STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the - * driver upon completion. Once received, the driver can - * proceed to the D3 state. - * - * Prepare for power down command to fw. This command would - * take HW out of D0-standby and prepare it for D3 state. - * - * Currently FW does not support event notification for this - * event. Therefore, skip waiting for it. Just wait a fixed - * 100ms - */ - IPW_DEBUG_HC("HOST_PRE_POWER_DOWN\n"); - - err = ipw2100_hw_send_command(priv, &cmd); - if (err) - printk(KERN_WARNING DRV_NAME ": " - "%s: Power down command failed: Error %d\n", - priv->net_dev->name, err); - else - schedule_timeout_uninterruptible(HW_POWER_DOWN_DELAY); - } - - priv->status &= ~STATUS_ENABLED; - - /* - * Set GPIO 3 writable by FW; GPIO 1 writable - * by driver and enable clock - */ - ipw2100_hw_set_gpio(priv); - - /* - * Power down adapter. Sequence: - * 1. Stop master assert (RESET_REG[9]=1) - * 2. Wait for stop master (RESET_REG[8]==1) - * 3. S/w reset assert (RESET_REG[7] = 1) - */ - - /* Stop master assert */ - write_register(priv->net_dev, IPW_REG_RESET_REG, - IPW_AUX_HOST_RESET_REG_STOP_MASTER); - - /* wait stop master not more than 50 usec. - * Otherwise return error. */ - for (i = 5; i > 0; i--) { - udelay(10); - - /* Check master stop bit */ - read_register(priv->net_dev, IPW_REG_RESET_REG, ®); - - if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED) - break; - } - - if (i == 0) - printk(KERN_WARNING DRV_NAME - ": %s: Could now power down adapter.\n", - priv->net_dev->name); - - /* assert s/w reset */ - write_register(priv->net_dev, IPW_REG_RESET_REG, - IPW_AUX_HOST_RESET_REG_SW_RESET); - - priv->status &= ~(STATUS_RUNNING | STATUS_STOPPING); - - return 0; -} - -static int ipw2100_disable_adapter(struct ipw2100_priv *priv) -{ - struct host_command cmd = { - .host_command = CARD_DISABLE, - .host_command_sequence = 0, - .host_command_length = 0 - }; - int err = 0; - - IPW_DEBUG_HC("CARD_DISABLE\n"); - - if (!(priv->status & STATUS_ENABLED)) - return 0; - - /* Make sure we clear the associated state */ - priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); - - if (!priv->stop_hang_check) { - priv->stop_hang_check = 1; - cancel_delayed_work(&priv->hang_check); - } - - mutex_lock(&priv->adapter_mutex); - - err = ipw2100_hw_send_command(priv, &cmd); - if (err) { - printk(KERN_WARNING DRV_NAME - ": exit - failed to send CARD_DISABLE command\n"); - goto fail_up; - } - - err = ipw2100_wait_for_card_state(priv, IPW_HW_STATE_DISABLED); - if (err) { - printk(KERN_WARNING DRV_NAME - ": exit - card failed to change to DISABLED\n"); - goto fail_up; - } - - IPW_DEBUG_INFO("TODO: implement scan state machine\n"); - - fail_up: - mutex_unlock(&priv->adapter_mutex); - return err; -} - -static int ipw2100_set_scan_options(struct ipw2100_priv *priv) -{ - struct host_command cmd = { - .host_command = SET_SCAN_OPTIONS, - .host_command_sequence = 0, - .host_command_length = 8 - }; - int err; - - IPW_DEBUG_INFO("enter\n"); - - IPW_DEBUG_SCAN("setting scan options\n"); - - cmd.host_command_parameters[0] = 0; - - if (!(priv->config & CFG_ASSOCIATE)) - cmd.host_command_parameters[0] |= IPW_SCAN_NOASSOCIATE; - if ((priv->ieee->sec.flags & SEC_ENABLED) && priv->ieee->sec.enabled) - cmd.host_command_parameters[0] |= IPW_SCAN_MIXED_CELL; - if (priv->config & CFG_PASSIVE_SCAN) - cmd.host_command_parameters[0] |= IPW_SCAN_PASSIVE; - - cmd.host_command_parameters[1] = priv->channel_mask; - - err = ipw2100_hw_send_command(priv, &cmd); - - IPW_DEBUG_HC("SET_SCAN_OPTIONS 0x%04X\n", - cmd.host_command_parameters[0]); - - return err; -} - -static int ipw2100_start_scan(struct ipw2100_priv *priv) -{ - struct host_command cmd = { - .host_command = BROADCAST_SCAN, - .host_command_sequence = 0, - .host_command_length = 4 - }; - int err; - - IPW_DEBUG_HC("START_SCAN\n"); - - cmd.host_command_parameters[0] = 0; - - /* No scanning if in monitor mode */ - if (priv->ieee->iw_mode == IW_MODE_MONITOR) - return 1; - - if (priv->status & STATUS_SCANNING) { - IPW_DEBUG_SCAN("Scan requested while already in scan...\n"); - return 0; - } - - IPW_DEBUG_INFO("enter\n"); - - /* Not clearing here; doing so makes iwlist always return nothing... - * - * We should modify the table logic to use aging tables vs. clearing - * the table on each scan start. - */ - IPW_DEBUG_SCAN("starting scan\n"); - - priv->status |= STATUS_SCANNING; - err = ipw2100_hw_send_command(priv, &cmd); - if (err) - priv->status &= ~STATUS_SCANNING; - - IPW_DEBUG_INFO("exit\n"); - - return err; -} - -static const struct libipw_geo ipw_geos[] = { - { /* Restricted */ - "---", - .bg_channels = 14, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}, {2467, 12}, - {2472, 13}, {2484, 14}}, - }, -}; - -static int ipw2100_up(struct ipw2100_priv *priv, int deferred) -{ - unsigned long flags; - int rc = 0; - u32 lock; - u32 ord_len = sizeof(lock); - - /* Age scan list entries found before suspend */ - if (priv->suspend_time) { - libipw_networks_age(priv->ieee, priv->suspend_time); - priv->suspend_time = 0; - } - - /* Quiet if manually disabled. */ - if (priv->status & STATUS_RF_KILL_SW) { - IPW_DEBUG_INFO("%s: Radio is disabled by Manual Disable " - "switch\n", priv->net_dev->name); - return 0; - } - - /* the ipw2100 hardware really doesn't want power management delays - * longer than 175usec - */ - pm_qos_update_request(&ipw2100_pm_qos_req, 175); - - /* If the interrupt is enabled, turn it off... */ - spin_lock_irqsave(&priv->low_lock, flags); - ipw2100_disable_interrupts(priv); - - /* Reset any fatal_error conditions */ - ipw2100_reset_fatalerror(priv); - spin_unlock_irqrestore(&priv->low_lock, flags); - - if (priv->status & STATUS_POWERED || - (priv->status & STATUS_RESET_PENDING)) { - /* Power cycle the card ... */ - if (ipw2100_power_cycle_adapter(priv)) { - printk(KERN_WARNING DRV_NAME - ": %s: Could not cycle adapter.\n", - priv->net_dev->name); - rc = 1; - goto exit; - } - } else - priv->status |= STATUS_POWERED; - - /* Load the firmware, start the clocks, etc. */ - if (ipw2100_start_adapter(priv)) { - printk(KERN_ERR DRV_NAME - ": %s: Failed to start the firmware.\n", - priv->net_dev->name); - rc = 1; - goto exit; - } - - ipw2100_initialize_ordinals(priv); - - /* Determine capabilities of this particular HW configuration */ - if (ipw2100_get_hw_features(priv)) { - printk(KERN_ERR DRV_NAME - ": %s: Failed to determine HW features.\n", - priv->net_dev->name); - rc = 1; - goto exit; - } - - /* Initialize the geo */ - libipw_set_geo(priv->ieee, &ipw_geos[0]); - priv->ieee->freq_band = LIBIPW_24GHZ_BAND; - - lock = LOCK_NONE; - if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) { - printk(KERN_ERR DRV_NAME - ": %s: Failed to clear ordinal lock.\n", - priv->net_dev->name); - rc = 1; - goto exit; - } - - priv->status &= ~STATUS_SCANNING; - - if (rf_kill_active(priv)) { - printk(KERN_INFO "%s: Radio is disabled by RF switch.\n", - priv->net_dev->name); - - if (priv->stop_rf_kill) { - priv->stop_rf_kill = 0; - schedule_delayed_work(&priv->rf_kill, - round_jiffies_relative(HZ)); - } - - deferred = 1; - } - - /* Turn on the interrupt so that commands can be processed */ - ipw2100_enable_interrupts(priv); - - /* Send all of the commands that must be sent prior to - * HOST_COMPLETE */ - if (ipw2100_adapter_setup(priv)) { - printk(KERN_ERR DRV_NAME ": %s: Failed to start the card.\n", - priv->net_dev->name); - rc = 1; - goto exit; - } - - if (!deferred) { - /* Enable the adapter - sends HOST_COMPLETE */ - if (ipw2100_enable_adapter(priv)) { - printk(KERN_ERR DRV_NAME ": " - "%s: failed in call to enable adapter.\n", - priv->net_dev->name); - ipw2100_hw_stop_adapter(priv); - rc = 1; - goto exit; - } - - /* Start a scan . . . */ - ipw2100_set_scan_options(priv); - ipw2100_start_scan(priv); - } - - exit: - return rc; -} - -static void ipw2100_down(struct ipw2100_priv *priv) -{ - unsigned long flags; - union iwreq_data wrqu = { - .ap_addr = { - .sa_family = ARPHRD_ETHER} - }; - int associated = priv->status & STATUS_ASSOCIATED; - - /* Kill the RF switch timer */ - if (!priv->stop_rf_kill) { - priv->stop_rf_kill = 1; - cancel_delayed_work(&priv->rf_kill); - } - - /* Kill the firmware hang check timer */ - if (!priv->stop_hang_check) { - priv->stop_hang_check = 1; - cancel_delayed_work(&priv->hang_check); - } - - /* Kill any pending resets */ - if (priv->status & STATUS_RESET_PENDING) - cancel_delayed_work(&priv->reset_work); - - /* Make sure the interrupt is on so that FW commands will be - * processed correctly */ - spin_lock_irqsave(&priv->low_lock, flags); - ipw2100_enable_interrupts(priv); - spin_unlock_irqrestore(&priv->low_lock, flags); - - if (ipw2100_hw_stop_adapter(priv)) - printk(KERN_ERR DRV_NAME ": %s: Error stopping adapter.\n", - priv->net_dev->name); - - /* Do not disable the interrupt until _after_ we disable - * the adaptor. Otherwise the CARD_DISABLE command will never - * be ack'd by the firmware */ - spin_lock_irqsave(&priv->low_lock, flags); - ipw2100_disable_interrupts(priv); - spin_unlock_irqrestore(&priv->low_lock, flags); - - pm_qos_update_request(&ipw2100_pm_qos_req, PM_QOS_DEFAULT_VALUE); - - /* We have to signal any supplicant if we are disassociating */ - if (associated) - wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); - - priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); - netif_carrier_off(priv->net_dev); - netif_stop_queue(priv->net_dev); -} - -static int ipw2100_wdev_init(struct net_device *dev) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - const struct libipw_geo *geo = libipw_get_geo(priv->ieee); - struct wireless_dev *wdev = &priv->ieee->wdev; - int i; - - memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); - - /* fill-out priv->ieee->bg_band */ - if (geo->bg_channels) { - struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; - - bg_band->band = IEEE80211_BAND_2GHZ; - bg_band->n_channels = geo->bg_channels; - bg_band->channels = kcalloc(geo->bg_channels, - sizeof(struct ieee80211_channel), - GFP_KERNEL); - if (!bg_band->channels) { - ipw2100_down(priv); - return -ENOMEM; - } - /* translate geo->bg to bg_band.channels */ - for (i = 0; i < geo->bg_channels; i++) { - bg_band->channels[i].band = IEEE80211_BAND_2GHZ; - bg_band->channels[i].center_freq = geo->bg[i].freq; - bg_band->channels[i].hw_value = geo->bg[i].channel; - bg_band->channels[i].max_power = geo->bg[i].max_power; - if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) - bg_band->channels[i].flags |= - IEEE80211_CHAN_NO_IR; - if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) - bg_band->channels[i].flags |= - IEEE80211_CHAN_NO_IR; - if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) - bg_band->channels[i].flags |= - IEEE80211_CHAN_RADAR; - /* No equivalent for LIBIPW_CH_80211H_RULES, - LIBIPW_CH_UNIFORM_SPREADING, or - LIBIPW_CH_B_ONLY... */ - } - /* point at bitrate info */ - bg_band->bitrates = ipw2100_bg_rates; - bg_band->n_bitrates = RATE_COUNT; - - wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; - } - - wdev->wiphy->cipher_suites = ipw_cipher_suites; - wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites); - - set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); - if (wiphy_register(wdev->wiphy)) - return -EIO; - return 0; -} - -static void ipw2100_reset_adapter(struct work_struct *work) -{ - struct ipw2100_priv *priv = - container_of(work, struct ipw2100_priv, reset_work.work); - unsigned long flags; - union iwreq_data wrqu = { - .ap_addr = { - .sa_family = ARPHRD_ETHER} - }; - int associated = priv->status & STATUS_ASSOCIATED; - - spin_lock_irqsave(&priv->low_lock, flags); - IPW_DEBUG_INFO(": %s: Restarting adapter.\n", priv->net_dev->name); - priv->resets++; - priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); - priv->status |= STATUS_SECURITY_UPDATED; - - /* Force a power cycle even if interface hasn't been opened - * yet */ - cancel_delayed_work(&priv->reset_work); - priv->status |= STATUS_RESET_PENDING; - spin_unlock_irqrestore(&priv->low_lock, flags); - - mutex_lock(&priv->action_mutex); - /* stop timed checks so that they don't interfere with reset */ - priv->stop_hang_check = 1; - cancel_delayed_work(&priv->hang_check); - - /* We have to signal any supplicant if we are disassociating */ - if (associated) - wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); - - ipw2100_up(priv, 0); - mutex_unlock(&priv->action_mutex); - -} - -static void isr_indicate_associated(struct ipw2100_priv *priv, u32 status) -{ - -#define MAC_ASSOCIATION_READ_DELAY (HZ) - int ret; - unsigned int len, essid_len; - char essid[IW_ESSID_MAX_SIZE]; - u32 txrate; - u32 chan; - char *txratename; - u8 bssid[ETH_ALEN]; - - /* - * TBD: BSSID is usually 00:00:00:00:00:00 here and not - * an actual MAC of the AP. Seems like FW sets this - * address too late. Read it later and expose through - * /proc or schedule a later task to query and update - */ - - essid_len = IW_ESSID_MAX_SIZE; - ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, - essid, &essid_len); - if (ret) { - IPW_DEBUG_INFO("failed querying ordinals at line %d\n", - __LINE__); - return; - } - - len = sizeof(u32); - ret = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &txrate, &len); - if (ret) { - IPW_DEBUG_INFO("failed querying ordinals at line %d\n", - __LINE__); - return; - } - - len = sizeof(u32); - ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &len); - if (ret) { - IPW_DEBUG_INFO("failed querying ordinals at line %d\n", - __LINE__); - return; - } - len = ETH_ALEN; - ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, bssid, - &len); - if (ret) { - IPW_DEBUG_INFO("failed querying ordinals at line %d\n", - __LINE__); - return; - } - memcpy(priv->ieee->bssid, bssid, ETH_ALEN); - - switch (txrate) { - case TX_RATE_1_MBIT: - txratename = "1Mbps"; - break; - case TX_RATE_2_MBIT: - txratename = "2Mbsp"; - break; - case TX_RATE_5_5_MBIT: - txratename = "5.5Mbps"; - break; - case TX_RATE_11_MBIT: - txratename = "11Mbps"; - break; - default: - IPW_DEBUG_INFO("Unknown rate: %d\n", txrate); - txratename = "unknown rate"; - break; - } - - IPW_DEBUG_INFO("%s: Associated with '%*pE' at %s, channel %d (BSSID=%pM)\n", - priv->net_dev->name, essid_len, essid, - txratename, chan, bssid); - - /* now we copy read ssid into dev */ - if (!(priv->config & CFG_STATIC_ESSID)) { - priv->essid_len = min((u8) essid_len, (u8) IW_ESSID_MAX_SIZE); - memcpy(priv->essid, essid, priv->essid_len); - } - priv->channel = chan; - memcpy(priv->bssid, bssid, ETH_ALEN); - - priv->status |= STATUS_ASSOCIATING; - priv->connect_start = get_seconds(); - - schedule_delayed_work(&priv->wx_event_work, HZ / 10); -} - -static int ipw2100_set_essid(struct ipw2100_priv *priv, char *essid, - int length, int batch_mode) -{ - int ssid_len = min(length, IW_ESSID_MAX_SIZE); - struct host_command cmd = { - .host_command = SSID, - .host_command_sequence = 0, - .host_command_length = ssid_len - }; - int err; - - IPW_DEBUG_HC("SSID: '%*pE'\n", ssid_len, essid); - - if (ssid_len) - memcpy(cmd.host_command_parameters, essid, ssid_len); - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) - return err; - } - - /* Bug in FW currently doesn't honor bit 0 in SET_SCAN_OPTIONS to - * disable auto association -- so we cheat by setting a bogus SSID */ - if (!ssid_len && !(priv->config & CFG_ASSOCIATE)) { - int i; - u8 *bogus = (u8 *) cmd.host_command_parameters; - for (i = 0; i < IW_ESSID_MAX_SIZE; i++) - bogus[i] = 0x18 + i; - cmd.host_command_length = IW_ESSID_MAX_SIZE; - } - - /* NOTE: We always send the SSID command even if the provided ESSID is - * the same as what we currently think is set. */ - - err = ipw2100_hw_send_command(priv, &cmd); - if (!err) { - memset(priv->essid + ssid_len, 0, IW_ESSID_MAX_SIZE - ssid_len); - memcpy(priv->essid, essid, ssid_len); - priv->essid_len = ssid_len; - } - - if (!batch_mode) { - if (ipw2100_enable_adapter(priv)) - err = -EIO; - } - - return err; -} - -static void isr_indicate_association_lost(struct ipw2100_priv *priv, u32 status) -{ - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | IPW_DL_ASSOC, - "disassociated: '%*pE' %pM\n", priv->essid_len, priv->essid, - priv->bssid); - - priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); - - if (priv->status & STATUS_STOPPING) { - IPW_DEBUG_INFO("Card is stopping itself, discard ASSN_LOST.\n"); - return; - } - - eth_zero_addr(priv->bssid); - eth_zero_addr(priv->ieee->bssid); - - netif_carrier_off(priv->net_dev); - netif_stop_queue(priv->net_dev); - - if (!(priv->status & STATUS_RUNNING)) - return; - - if (priv->status & STATUS_SECURITY_UPDATED) - schedule_delayed_work(&priv->security_work, 0); - - schedule_delayed_work(&priv->wx_event_work, 0); -} - -static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status) -{ - IPW_DEBUG_INFO("%s: RF Kill state changed to radio OFF.\n", - priv->net_dev->name); - - /* RF_KILL is now enabled (else we wouldn't be here) */ - wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); - priv->status |= STATUS_RF_KILL_HW; - - /* Make sure the RF Kill check timer is running */ - priv->stop_rf_kill = 0; - mod_delayed_work(system_wq, &priv->rf_kill, round_jiffies_relative(HZ)); -} - -static void ipw2100_scan_event(struct work_struct *work) -{ - struct ipw2100_priv *priv = container_of(work, struct ipw2100_priv, - scan_event.work); - union iwreq_data wrqu; - - wrqu.data.length = 0; - wrqu.data.flags = 0; - wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL); -} - -static void isr_scan_complete(struct ipw2100_priv *priv, u32 status) -{ - IPW_DEBUG_SCAN("scan complete\n"); - /* Age the scan results... */ - priv->ieee->scans++; - priv->status &= ~STATUS_SCANNING; - - /* Only userspace-requested scan completion events go out immediately */ - if (!priv->user_requested_scan) { - schedule_delayed_work(&priv->scan_event, - round_jiffies_relative(msecs_to_jiffies(4000))); - } else { - priv->user_requested_scan = 0; - mod_delayed_work(system_wq, &priv->scan_event, 0); - } -} - -#ifdef CONFIG_IPW2100_DEBUG -#define IPW2100_HANDLER(v, f) { v, f, # v } -struct ipw2100_status_indicator { - int status; - void (*cb) (struct ipw2100_priv * priv, u32 status); - char *name; -}; -#else -#define IPW2100_HANDLER(v, f) { v, f } -struct ipw2100_status_indicator { - int status; - void (*cb) (struct ipw2100_priv * priv, u32 status); -}; -#endif /* CONFIG_IPW2100_DEBUG */ - -static void isr_indicate_scanning(struct ipw2100_priv *priv, u32 status) -{ - IPW_DEBUG_SCAN("Scanning...\n"); - priv->status |= STATUS_SCANNING; -} - -static const struct ipw2100_status_indicator status_handlers[] = { - IPW2100_HANDLER(IPW_STATE_INITIALIZED, NULL), - IPW2100_HANDLER(IPW_STATE_COUNTRY_FOUND, NULL), - IPW2100_HANDLER(IPW_STATE_ASSOCIATED, isr_indicate_associated), - IPW2100_HANDLER(IPW_STATE_ASSN_LOST, isr_indicate_association_lost), - IPW2100_HANDLER(IPW_STATE_ASSN_CHANGED, NULL), - IPW2100_HANDLER(IPW_STATE_SCAN_COMPLETE, isr_scan_complete), - IPW2100_HANDLER(IPW_STATE_ENTERED_PSP, NULL), - IPW2100_HANDLER(IPW_STATE_LEFT_PSP, NULL), - IPW2100_HANDLER(IPW_STATE_RF_KILL, isr_indicate_rf_kill), - IPW2100_HANDLER(IPW_STATE_DISABLED, NULL), - IPW2100_HANDLER(IPW_STATE_POWER_DOWN, NULL), - IPW2100_HANDLER(IPW_STATE_SCANNING, isr_indicate_scanning), - IPW2100_HANDLER(-1, NULL) -}; - -static void isr_status_change(struct ipw2100_priv *priv, int status) -{ - int i; - - if (status == IPW_STATE_SCANNING && - priv->status & STATUS_ASSOCIATED && - !(priv->status & STATUS_SCANNING)) { - IPW_DEBUG_INFO("Scan detected while associated, with " - "no scan request. Restarting firmware.\n"); - - /* Wake up any sleeping jobs */ - schedule_reset(priv); - } - - for (i = 0; status_handlers[i].status != -1; i++) { - if (status == status_handlers[i].status) { - IPW_DEBUG_NOTIF("Status change: %s\n", - status_handlers[i].name); - if (status_handlers[i].cb) - status_handlers[i].cb(priv, status); - priv->wstats.status = status; - return; - } - } - - IPW_DEBUG_NOTIF("unknown status received: %04x\n", status); -} - -static void isr_rx_complete_command(struct ipw2100_priv *priv, - struct ipw2100_cmd_header *cmd) -{ -#ifdef CONFIG_IPW2100_DEBUG - if (cmd->host_command_reg < ARRAY_SIZE(command_types)) { - IPW_DEBUG_HC("Command completed '%s (%d)'\n", - command_types[cmd->host_command_reg], - cmd->host_command_reg); - } -#endif - if (cmd->host_command_reg == HOST_COMPLETE) - priv->status |= STATUS_ENABLED; - - if (cmd->host_command_reg == CARD_DISABLE) - priv->status &= ~STATUS_ENABLED; - - priv->status &= ~STATUS_CMD_ACTIVE; - - wake_up_interruptible(&priv->wait_command_queue); -} - -#ifdef CONFIG_IPW2100_DEBUG -static const char *frame_types[] = { - "COMMAND_STATUS_VAL", - "STATUS_CHANGE_VAL", - "P80211_DATA_VAL", - "P8023_DATA_VAL", - "HOST_NOTIFICATION_VAL" -}; -#endif - -static int ipw2100_alloc_skb(struct ipw2100_priv *priv, - struct ipw2100_rx_packet *packet) -{ - packet->skb = dev_alloc_skb(sizeof(struct ipw2100_rx)); - if (!packet->skb) - return -ENOMEM; - - packet->rxp = (struct ipw2100_rx *)packet->skb->data; - packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data, - sizeof(struct ipw2100_rx), - PCI_DMA_FROMDEVICE); - /* NOTE: pci_map_single does not return an error code, and 0 is a valid - * dma_addr */ - - return 0; -} - -#define SEARCH_ERROR 0xffffffff -#define SEARCH_FAIL 0xfffffffe -#define SEARCH_SUCCESS 0xfffffff0 -#define SEARCH_DISCARD 0 -#define SEARCH_SNAPSHOT 1 - -#define SNAPSHOT_ADDR(ofs) (priv->snapshot[((ofs) >> 12) & 0xff] + ((ofs) & 0xfff)) -static void ipw2100_snapshot_free(struct ipw2100_priv *priv) -{ - int i; - if (!priv->snapshot[0]) - return; - for (i = 0; i < 0x30; i++) - kfree(priv->snapshot[i]); - priv->snapshot[0] = NULL; -} - -#ifdef IPW2100_DEBUG_C3 -static int ipw2100_snapshot_alloc(struct ipw2100_priv *priv) -{ - int i; - if (priv->snapshot[0]) - return 1; - for (i = 0; i < 0x30; i++) { - priv->snapshot[i] = kmalloc(0x1000, GFP_ATOMIC); - if (!priv->snapshot[i]) { - IPW_DEBUG_INFO("%s: Error allocating snapshot " - "buffer %d\n", priv->net_dev->name, i); - while (i > 0) - kfree(priv->snapshot[--i]); - priv->snapshot[0] = NULL; - return 0; - } - } - - return 1; -} - -static u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 * in_buf, - size_t len, int mode) -{ - u32 i, j; - u32 tmp; - u8 *s, *d; - u32 ret; - - s = in_buf; - if (mode == SEARCH_SNAPSHOT) { - if (!ipw2100_snapshot_alloc(priv)) - mode = SEARCH_DISCARD; - } - - for (ret = SEARCH_FAIL, i = 0; i < 0x30000; i += 4) { - read_nic_dword(priv->net_dev, i, &tmp); - if (mode == SEARCH_SNAPSHOT) - *(u32 *) SNAPSHOT_ADDR(i) = tmp; - if (ret == SEARCH_FAIL) { - d = (u8 *) & tmp; - for (j = 0; j < 4; j++) { - if (*s != *d) { - s = in_buf; - continue; - } - - s++; - d++; - - if ((s - in_buf) == len) - ret = (i + j) - len + 1; - } - } else if (mode == SEARCH_DISCARD) - return ret; - } - - return ret; -} -#endif - -/* - * - * 0) Disconnect the SKB from the firmware (just unmap) - * 1) Pack the ETH header into the SKB - * 2) Pass the SKB to the network stack - * - * When packet is provided by the firmware, it contains the following: - * - * . libipw_hdr - * . libipw_snap_hdr - * - * The size of the constructed ethernet - * - */ -#ifdef IPW2100_RX_DEBUG -static u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH]; -#endif - -static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i) -{ -#ifdef IPW2100_DEBUG_C3 - struct ipw2100_status *status = &priv->status_queue.drv[i]; - u32 match, reg; - int j; -#endif - - IPW_DEBUG_INFO(": PCI latency error detected at 0x%04zX.\n", - i * sizeof(struct ipw2100_status)); - -#ifdef IPW2100_DEBUG_C3 - /* Halt the firmware so we can get a good image */ - write_register(priv->net_dev, IPW_REG_RESET_REG, - IPW_AUX_HOST_RESET_REG_STOP_MASTER); - j = 5; - do { - udelay(IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY); - read_register(priv->net_dev, IPW_REG_RESET_REG, ®); - - if (reg & IPW_AUX_HOST_RESET_REG_MASTER_DISABLED) - break; - } while (j--); - - match = ipw2100_match_buf(priv, (u8 *) status, - sizeof(struct ipw2100_status), - SEARCH_SNAPSHOT); - if (match < SEARCH_SUCCESS) - IPW_DEBUG_INFO("%s: DMA status match in Firmware at " - "offset 0x%06X, length %d:\n", - priv->net_dev->name, match, - sizeof(struct ipw2100_status)); - else - IPW_DEBUG_INFO("%s: No DMA status match in " - "Firmware.\n", priv->net_dev->name); - - printk_buf((u8 *) priv->status_queue.drv, - sizeof(struct ipw2100_status) * RX_QUEUE_LENGTH); -#endif - - priv->fatal_error = IPW2100_ERR_C3_CORRUPTION; - priv->net_dev->stats.rx_errors++; - schedule_reset(priv); -} - -static void isr_rx(struct ipw2100_priv *priv, int i, - struct libipw_rx_stats *stats) -{ - struct net_device *dev = priv->net_dev; - struct ipw2100_status *status = &priv->status_queue.drv[i]; - struct ipw2100_rx_packet *packet = &priv->rx_buffers[i]; - - IPW_DEBUG_RX("Handler...\n"); - - if (unlikely(status->frame_size > skb_tailroom(packet->skb))) { - IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!" - " Dropping.\n", - dev->name, - status->frame_size, skb_tailroom(packet->skb)); - dev->stats.rx_errors++; - return; - } - - if (unlikely(!netif_running(dev))) { - dev->stats.rx_errors++; - priv->wstats.discard.misc++; - IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); - return; - } - - if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR && - !(priv->status & STATUS_ASSOCIATED))) { - IPW_DEBUG_DROP("Dropping packet while not associated.\n"); - priv->wstats.discard.misc++; - return; - } - - pci_unmap_single(priv->pci_dev, - packet->dma_addr, - sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE); - - skb_put(packet->skb, status->frame_size); - -#ifdef IPW2100_RX_DEBUG - /* Make a copy of the frame so we can dump it to the logs if - * libipw_rx fails */ - skb_copy_from_linear_data(packet->skb, packet_data, - min_t(u32, status->frame_size, - IPW_RX_NIC_BUFFER_LENGTH)); -#endif - - if (!libipw_rx(priv->ieee, packet->skb, stats)) { -#ifdef IPW2100_RX_DEBUG - IPW_DEBUG_DROP("%s: Non consumed packet:\n", - dev->name); - printk_buf(IPW_DL_DROP, packet_data, status->frame_size); -#endif - dev->stats.rx_errors++; - - /* libipw_rx failed, so it didn't free the SKB */ - dev_kfree_skb_any(packet->skb); - packet->skb = NULL; - } - - /* We need to allocate a new SKB and attach it to the RDB. */ - if (unlikely(ipw2100_alloc_skb(priv, packet))) { - printk(KERN_WARNING DRV_NAME ": " - "%s: Unable to allocate SKB onto RBD ring - disabling " - "adapter.\n", dev->name); - /* TODO: schedule adapter shutdown */ - IPW_DEBUG_INFO("TODO: Shutdown adapter...\n"); - } - - /* Update the RDB entry */ - priv->rx_queue.drv[i].host_addr = packet->dma_addr; -} - -#ifdef CONFIG_IPW2100_MONITOR - -static void isr_rx_monitor(struct ipw2100_priv *priv, int i, - struct libipw_rx_stats *stats) -{ - struct net_device *dev = priv->net_dev; - struct ipw2100_status *status = &priv->status_queue.drv[i]; - struct ipw2100_rx_packet *packet = &priv->rx_buffers[i]; - - /* Magic struct that slots into the radiotap header -- no reason - * to build this manually element by element, we can write it much - * more efficiently than we can parse it. ORDER MATTERS HERE */ - struct ipw_rt_hdr { - struct ieee80211_radiotap_header rt_hdr; - s8 rt_dbmsignal; /* signal in dbM, kluged to signed */ - } *ipw_rt; - - IPW_DEBUG_RX("Handler...\n"); - - if (unlikely(status->frame_size > skb_tailroom(packet->skb) - - sizeof(struct ipw_rt_hdr))) { - IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!" - " Dropping.\n", - dev->name, - status->frame_size, - skb_tailroom(packet->skb)); - dev->stats.rx_errors++; - return; - } - - if (unlikely(!netif_running(dev))) { - dev->stats.rx_errors++; - priv->wstats.discard.misc++; - IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); - return; - } - - if (unlikely(priv->config & CFG_CRC_CHECK && - status->flags & IPW_STATUS_FLAG_CRC_ERROR)) { - IPW_DEBUG_RX("CRC error in packet. Dropping.\n"); - dev->stats.rx_errors++; - return; - } - - pci_unmap_single(priv->pci_dev, packet->dma_addr, - sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE); - memmove(packet->skb->data + sizeof(struct ipw_rt_hdr), - packet->skb->data, status->frame_size); - - ipw_rt = (struct ipw_rt_hdr *) packet->skb->data; - - ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; - ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */ - ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total hdr+data */ - - ipw_rt->rt_hdr.it_present = cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL); - - ipw_rt->rt_dbmsignal = status->rssi + IPW2100_RSSI_TO_DBM; - - skb_put(packet->skb, status->frame_size + sizeof(struct ipw_rt_hdr)); - - if (!libipw_rx(priv->ieee, packet->skb, stats)) { - dev->stats.rx_errors++; - - /* libipw_rx failed, so it didn't free the SKB */ - dev_kfree_skb_any(packet->skb); - packet->skb = NULL; - } - - /* We need to allocate a new SKB and attach it to the RDB. */ - if (unlikely(ipw2100_alloc_skb(priv, packet))) { - IPW_DEBUG_WARNING( - "%s: Unable to allocate SKB onto RBD ring - disabling " - "adapter.\n", dev->name); - /* TODO: schedule adapter shutdown */ - IPW_DEBUG_INFO("TODO: Shutdown adapter...\n"); - } - - /* Update the RDB entry */ - priv->rx_queue.drv[i].host_addr = packet->dma_addr; -} - -#endif - -static int ipw2100_corruption_check(struct ipw2100_priv *priv, int i) -{ - struct ipw2100_status *status = &priv->status_queue.drv[i]; - struct ipw2100_rx *u = priv->rx_buffers[i].rxp; - u16 frame_type = status->status_fields & STATUS_TYPE_MASK; - - switch (frame_type) { - case COMMAND_STATUS_VAL: - return (status->frame_size != sizeof(u->rx_data.command)); - case STATUS_CHANGE_VAL: - return (status->frame_size != sizeof(u->rx_data.status)); - case HOST_NOTIFICATION_VAL: - return (status->frame_size < sizeof(u->rx_data.notification)); - case P80211_DATA_VAL: - case P8023_DATA_VAL: -#ifdef CONFIG_IPW2100_MONITOR - return 0; -#else - switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) { - case IEEE80211_FTYPE_MGMT: - case IEEE80211_FTYPE_CTL: - return 0; - case IEEE80211_FTYPE_DATA: - return (status->frame_size > - IPW_MAX_802_11_PAYLOAD_LENGTH); - } -#endif - } - - return 1; -} - -/* - * ipw2100 interrupts are disabled at this point, and the ISR - * is the only code that calls this method. So, we do not need - * to play with any locks. - * - * RX Queue works as follows: - * - * Read index - firmware places packet in entry identified by the - * Read index and advances Read index. In this manner, - * Read index will always point to the next packet to - * be filled--but not yet valid. - * - * Write index - driver fills this entry with an unused RBD entry. - * This entry has not filled by the firmware yet. - * - * In between the W and R indexes are the RBDs that have been received - * but not yet processed. - * - * The process of handling packets will start at WRITE + 1 and advance - * until it reaches the READ index. - * - * The WRITE index is cached in the variable 'priv->rx_queue.next'. - * - */ -static void __ipw2100_rx_process(struct ipw2100_priv *priv) -{ - struct ipw2100_bd_queue *rxq = &priv->rx_queue; - struct ipw2100_status_queue *sq = &priv->status_queue; - struct ipw2100_rx_packet *packet; - u16 frame_type; - u32 r, w, i, s; - struct ipw2100_rx *u; - struct libipw_rx_stats stats = { - .mac_time = jiffies, - }; - - read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_READ_INDEX, &r); - read_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, &w); - - if (r >= rxq->entries) { - IPW_DEBUG_RX("exit - bad read index\n"); - return; - } - - i = (rxq->next + 1) % rxq->entries; - s = i; - while (i != r) { - /* IPW_DEBUG_RX("r = %d : w = %d : processing = %d\n", - r, rxq->next, i); */ - - packet = &priv->rx_buffers[i]; - - /* Sync the DMA for the RX buffer so CPU is sure to get - * the correct values */ - pci_dma_sync_single_for_cpu(priv->pci_dev, packet->dma_addr, - sizeof(struct ipw2100_rx), - PCI_DMA_FROMDEVICE); - - if (unlikely(ipw2100_corruption_check(priv, i))) { - ipw2100_corruption_detected(priv, i); - goto increment; - } - - u = packet->rxp; - frame_type = sq->drv[i].status_fields & STATUS_TYPE_MASK; - stats.rssi = sq->drv[i].rssi + IPW2100_RSSI_TO_DBM; - stats.len = sq->drv[i].frame_size; - - stats.mask = 0; - if (stats.rssi != 0) - stats.mask |= LIBIPW_STATMASK_RSSI; - stats.freq = LIBIPW_24GHZ_BAND; - - IPW_DEBUG_RX("%s: '%s' frame type received (%d).\n", - priv->net_dev->name, frame_types[frame_type], - stats.len); - - switch (frame_type) { - case COMMAND_STATUS_VAL: - /* Reset Rx watchdog */ - isr_rx_complete_command(priv, &u->rx_data.command); - break; - - case STATUS_CHANGE_VAL: - isr_status_change(priv, u->rx_data.status); - break; - - case P80211_DATA_VAL: - case P8023_DATA_VAL: -#ifdef CONFIG_IPW2100_MONITOR - if (priv->ieee->iw_mode == IW_MODE_MONITOR) { - isr_rx_monitor(priv, i, &stats); - break; - } -#endif - if (stats.len < sizeof(struct libipw_hdr_3addr)) - break; - switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) { - case IEEE80211_FTYPE_MGMT: - libipw_rx_mgt(priv->ieee, - &u->rx_data.header, &stats); - break; - - case IEEE80211_FTYPE_CTL: - break; - - case IEEE80211_FTYPE_DATA: - isr_rx(priv, i, &stats); - break; - - } - break; - } - - increment: - /* clear status field associated with this RBD */ - rxq->drv[i].status.info.field = 0; - - i = (i + 1) % rxq->entries; - } - - if (i != s) { - /* backtrack one entry, wrapping to end if at 0 */ - rxq->next = (i ? i : rxq->entries) - 1; - - write_register(priv->net_dev, - IPW_MEM_HOST_SHARED_RX_WRITE_INDEX, rxq->next); - } -} - -/* - * __ipw2100_tx_process - * - * This routine will determine whether the next packet on - * the fw_pend_list has been processed by the firmware yet. - * - * If not, then it does nothing and returns. - * - * If so, then it removes the item from the fw_pend_list, frees - * any associated storage, and places the item back on the - * free list of its source (either msg_free_list or tx_free_list) - * - * TX Queue works as follows: - * - * Read index - points to the next TBD that the firmware will - * process. The firmware will read the data, and once - * done processing, it will advance the Read index. - * - * Write index - driver fills this entry with an constructed TBD - * entry. The Write index is not advanced until the - * packet has been configured. - * - * In between the W and R indexes are the TBDs that have NOT been - * processed. Lagging behind the R index are packets that have - * been processed but have not been freed by the driver. - * - * In order to free old storage, an internal index will be maintained - * that points to the next packet to be freed. When all used - * packets have been freed, the oldest index will be the same as the - * firmware's read index. - * - * The OLDEST index is cached in the variable 'priv->tx_queue.oldest' - * - * Because the TBD structure can not contain arbitrary data, the - * driver must keep an internal queue of cached allocations such that - * it can put that data back into the tx_free_list and msg_free_list - * for use by future command and data packets. - * - */ -static int __ipw2100_tx_process(struct ipw2100_priv *priv) -{ - struct ipw2100_bd_queue *txq = &priv->tx_queue; - struct ipw2100_bd *tbd; - struct list_head *element; - struct ipw2100_tx_packet *packet; - int descriptors_used; - int e, i; - u32 r, w, frag_num = 0; - - if (list_empty(&priv->fw_pend_list)) - return 0; - - element = priv->fw_pend_list.next; - - packet = list_entry(element, struct ipw2100_tx_packet, list); - tbd = &txq->drv[packet->index]; - - /* Determine how many TBD entries must be finished... */ - switch (packet->type) { - case COMMAND: - /* COMMAND uses only one slot; don't advance */ - descriptors_used = 1; - e = txq->oldest; - break; - - case DATA: - /* DATA uses two slots; advance and loop position. */ - descriptors_used = tbd->num_fragments; - frag_num = tbd->num_fragments - 1; - e = txq->oldest + frag_num; - e %= txq->entries; - break; - - default: - printk(KERN_WARNING DRV_NAME ": %s: Bad fw_pend_list entry!\n", - priv->net_dev->name); - return 0; - } - - /* if the last TBD is not done by NIC yet, then packet is - * not ready to be released. - * - */ - read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX, - &r); - read_register(priv->net_dev, IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX, - &w); - if (w != txq->next) - printk(KERN_WARNING DRV_NAME ": %s: write index mismatch\n", - priv->net_dev->name); - - /* - * txq->next is the index of the last packet written txq->oldest is - * the index of the r is the index of the next packet to be read by - * firmware - */ - - /* - * Quick graphic to help you visualize the following - * if / else statement - * - * ===>| s---->|=============== - * e>| - * | a | b | c | d | e | f | g | h | i | j | k | l - * r---->| - * w - * - * w - updated by driver - * r - updated by firmware - * s - start of oldest BD entry (txq->oldest) - * e - end of oldest BD entry - * - */ - if (!((r <= w && (e < r || e >= w)) || (e < r && e >= w))) { - IPW_DEBUG_TX("exit - no processed packets ready to release.\n"); - return 0; - } - - list_del(element); - DEC_STAT(&priv->fw_pend_stat); - -#ifdef CONFIG_IPW2100_DEBUG - { - i = txq->oldest; - IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i, - &txq->drv[i], - (u32) (txq->nic + i * sizeof(struct ipw2100_bd)), - txq->drv[i].host_addr, txq->drv[i].buf_length); - - if (packet->type == DATA) { - i = (i + 1) % txq->entries; - - IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i, - &txq->drv[i], - (u32) (txq->nic + i * - sizeof(struct ipw2100_bd)), - (u32) txq->drv[i].host_addr, - txq->drv[i].buf_length); - } - } -#endif - - switch (packet->type) { - case DATA: - if (txq->drv[txq->oldest].status.info.fields.txType != 0) - printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. " - "Expecting DATA TBD but pulled " - "something else: ids %d=%d.\n", - priv->net_dev->name, txq->oldest, packet->index); - - /* DATA packet; we have to unmap and free the SKB */ - for (i = 0; i < frag_num; i++) { - tbd = &txq->drv[(packet->index + 1 + i) % txq->entries]; - - IPW_DEBUG_TX("TX%d P=%08x L=%d\n", - (packet->index + 1 + i) % txq->entries, - tbd->host_addr, tbd->buf_length); - - pci_unmap_single(priv->pci_dev, - tbd->host_addr, - tbd->buf_length, PCI_DMA_TODEVICE); - } - - libipw_txb_free(packet->info.d_struct.txb); - packet->info.d_struct.txb = NULL; - - list_add_tail(element, &priv->tx_free_list); - INC_STAT(&priv->tx_free_stat); - - /* We have a free slot in the Tx queue, so wake up the - * transmit layer if it is stopped. */ - if (priv->status & STATUS_ASSOCIATED) - netif_wake_queue(priv->net_dev); - - /* A packet was processed by the hardware, so update the - * watchdog */ - priv->net_dev->trans_start = jiffies; - - break; - - case COMMAND: - if (txq->drv[txq->oldest].status.info.fields.txType != 1) - printk(KERN_WARNING DRV_NAME ": %s: Queue mismatch. " - "Expecting COMMAND TBD but pulled " - "something else: ids %d=%d.\n", - priv->net_dev->name, txq->oldest, packet->index); - -#ifdef CONFIG_IPW2100_DEBUG - if (packet->info.c_struct.cmd->host_command_reg < - ARRAY_SIZE(command_types)) - IPW_DEBUG_TX("Command '%s (%d)' processed: %d.\n", - command_types[packet->info.c_struct.cmd-> - host_command_reg], - packet->info.c_struct.cmd-> - host_command_reg, - packet->info.c_struct.cmd->cmd_status_reg); -#endif - - list_add_tail(element, &priv->msg_free_list); - INC_STAT(&priv->msg_free_stat); - break; - } - - /* advance oldest used TBD pointer to start of next entry */ - txq->oldest = (e + 1) % txq->entries; - /* increase available TBDs number */ - txq->available += descriptors_used; - SET_STAT(&priv->txq_stat, txq->available); - - IPW_DEBUG_TX("packet latency (send to process) %ld jiffies\n", - jiffies - packet->jiffy_start); - - return (!list_empty(&priv->fw_pend_list)); -} - -static inline void __ipw2100_tx_complete(struct ipw2100_priv *priv) -{ - int i = 0; - - while (__ipw2100_tx_process(priv) && i < 200) - i++; - - if (i == 200) { - printk(KERN_WARNING DRV_NAME ": " - "%s: Driver is running slow (%d iters).\n", - priv->net_dev->name, i); - } -} - -static void ipw2100_tx_send_commands(struct ipw2100_priv *priv) -{ - struct list_head *element; - struct ipw2100_tx_packet *packet; - struct ipw2100_bd_queue *txq = &priv->tx_queue; - struct ipw2100_bd *tbd; - int next = txq->next; - - while (!list_empty(&priv->msg_pend_list)) { - /* if there isn't enough space in TBD queue, then - * don't stuff a new one in. - * NOTE: 3 are needed as a command will take one, - * and there is a minimum of 2 that must be - * maintained between the r and w indexes - */ - if (txq->available <= 3) { - IPW_DEBUG_TX("no room in tx_queue\n"); - break; - } - - element = priv->msg_pend_list.next; - list_del(element); - DEC_STAT(&priv->msg_pend_stat); - - packet = list_entry(element, struct ipw2100_tx_packet, list); - - IPW_DEBUG_TX("using TBD at virt=%p, phys=%04X\n", - &txq->drv[txq->next], - (u32) (txq->nic + txq->next * - sizeof(struct ipw2100_bd))); - - packet->index = txq->next; - - tbd = &txq->drv[txq->next]; - - /* initialize TBD */ - tbd->host_addr = packet->info.c_struct.cmd_phys; - tbd->buf_length = sizeof(struct ipw2100_cmd_header); - /* not marking number of fragments causes problems - * with f/w debug version */ - tbd->num_fragments = 1; - tbd->status.info.field = - IPW_BD_STATUS_TX_FRAME_COMMAND | - IPW_BD_STATUS_TX_INTERRUPT_ENABLE; - - /* update TBD queue counters */ - txq->next++; - txq->next %= txq->entries; - txq->available--; - DEC_STAT(&priv->txq_stat); - - list_add_tail(element, &priv->fw_pend_list); - INC_STAT(&priv->fw_pend_stat); - } - - if (txq->next != next) { - /* kick off the DMA by notifying firmware the - * write index has moved; make sure TBD stores are sync'd */ - wmb(); - write_register(priv->net_dev, - IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX, - txq->next); - } -} - -/* - * ipw2100_tx_send_data - * - */ -static void ipw2100_tx_send_data(struct ipw2100_priv *priv) -{ - struct list_head *element; - struct ipw2100_tx_packet *packet; - struct ipw2100_bd_queue *txq = &priv->tx_queue; - struct ipw2100_bd *tbd; - int next = txq->next; - int i = 0; - struct ipw2100_data_header *ipw_hdr; - struct libipw_hdr_3addr *hdr; - - while (!list_empty(&priv->tx_pend_list)) { - /* if there isn't enough space in TBD queue, then - * don't stuff a new one in. - * NOTE: 4 are needed as a data will take two, - * and there is a minimum of 2 that must be - * maintained between the r and w indexes - */ - element = priv->tx_pend_list.next; - packet = list_entry(element, struct ipw2100_tx_packet, list); - - if (unlikely(1 + packet->info.d_struct.txb->nr_frags > - IPW_MAX_BDS)) { - /* TODO: Support merging buffers if more than - * IPW_MAX_BDS are used */ - IPW_DEBUG_INFO("%s: Maximum BD threshold exceeded. " - "Increase fragmentation level.\n", - priv->net_dev->name); - } - - if (txq->available <= 3 + packet->info.d_struct.txb->nr_frags) { - IPW_DEBUG_TX("no room in tx_queue\n"); - break; - } - - list_del(element); - DEC_STAT(&priv->tx_pend_stat); - - tbd = &txq->drv[txq->next]; - - packet->index = txq->next; - - ipw_hdr = packet->info.d_struct.data; - hdr = (struct libipw_hdr_3addr *)packet->info.d_struct.txb-> - fragments[0]->data; - - if (priv->ieee->iw_mode == IW_MODE_INFRA) { - /* To DS: Addr1 = BSSID, Addr2 = SA, - Addr3 = DA */ - memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN); - memcpy(ipw_hdr->dst_addr, hdr->addr3, ETH_ALEN); - } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) { - /* not From/To DS: Addr1 = DA, Addr2 = SA, - Addr3 = BSSID */ - memcpy(ipw_hdr->src_addr, hdr->addr2, ETH_ALEN); - memcpy(ipw_hdr->dst_addr, hdr->addr1, ETH_ALEN); - } - - ipw_hdr->host_command_reg = SEND; - ipw_hdr->host_command_reg1 = 0; - - /* For now we only support host based encryption */ - ipw_hdr->needs_encryption = 0; - ipw_hdr->encrypted = packet->info.d_struct.txb->encrypted; - if (packet->info.d_struct.txb->nr_frags > 1) - ipw_hdr->fragment_size = - packet->info.d_struct.txb->frag_size - - LIBIPW_3ADDR_LEN; - else - ipw_hdr->fragment_size = 0; - - tbd->host_addr = packet->info.d_struct.data_phys; - tbd->buf_length = sizeof(struct ipw2100_data_header); - tbd->num_fragments = 1 + packet->info.d_struct.txb->nr_frags; - tbd->status.info.field = - IPW_BD_STATUS_TX_FRAME_802_3 | - IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT; - txq->next++; - txq->next %= txq->entries; - - IPW_DEBUG_TX("data header tbd TX%d P=%08x L=%d\n", - packet->index, tbd->host_addr, tbd->buf_length); -#ifdef CONFIG_IPW2100_DEBUG - if (packet->info.d_struct.txb->nr_frags > 1) - IPW_DEBUG_FRAG("fragment Tx: %d frames\n", - packet->info.d_struct.txb->nr_frags); -#endif - - for (i = 0; i < packet->info.d_struct.txb->nr_frags; i++) { - tbd = &txq->drv[txq->next]; - if (i == packet->info.d_struct.txb->nr_frags - 1) - tbd->status.info.field = - IPW_BD_STATUS_TX_FRAME_802_3 | - IPW_BD_STATUS_TX_INTERRUPT_ENABLE; - else - tbd->status.info.field = - IPW_BD_STATUS_TX_FRAME_802_3 | - IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT; - - tbd->buf_length = packet->info.d_struct.txb-> - fragments[i]->len - LIBIPW_3ADDR_LEN; - - tbd->host_addr = pci_map_single(priv->pci_dev, - packet->info.d_struct. - txb->fragments[i]-> - data + - LIBIPW_3ADDR_LEN, - tbd->buf_length, - PCI_DMA_TODEVICE); - - IPW_DEBUG_TX("data frag tbd TX%d P=%08x L=%d\n", - txq->next, tbd->host_addr, - tbd->buf_length); - - pci_dma_sync_single_for_device(priv->pci_dev, - tbd->host_addr, - tbd->buf_length, - PCI_DMA_TODEVICE); - - txq->next++; - txq->next %= txq->entries; - } - - txq->available -= 1 + packet->info.d_struct.txb->nr_frags; - SET_STAT(&priv->txq_stat, txq->available); - - list_add_tail(element, &priv->fw_pend_list); - INC_STAT(&priv->fw_pend_stat); - } - - if (txq->next != next) { - /* kick off the DMA by notifying firmware the - * write index has moved; make sure TBD stores are sync'd */ - write_register(priv->net_dev, - IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX, - txq->next); - } -} - -static void ipw2100_irq_tasklet(struct ipw2100_priv *priv) -{ - struct net_device *dev = priv->net_dev; - unsigned long flags; - u32 inta, tmp; - - spin_lock_irqsave(&priv->low_lock, flags); - ipw2100_disable_interrupts(priv); - - read_register(dev, IPW_REG_INTA, &inta); - - IPW_DEBUG_ISR("enter - INTA: 0x%08lX\n", - (unsigned long)inta & IPW_INTERRUPT_MASK); - - priv->in_isr++; - priv->interrupts++; - - /* We do not loop and keep polling for more interrupts as this - * is frowned upon and doesn't play nicely with other potentially - * chained IRQs */ - IPW_DEBUG_ISR("INTA: 0x%08lX\n", - (unsigned long)inta & IPW_INTERRUPT_MASK); - - if (inta & IPW2100_INTA_FATAL_ERROR) { - printk(KERN_WARNING DRV_NAME - ": Fatal interrupt. Scheduling firmware restart.\n"); - priv->inta_other++; - write_register(dev, IPW_REG_INTA, IPW2100_INTA_FATAL_ERROR); - - read_nic_dword(dev, IPW_NIC_FATAL_ERROR, &priv->fatal_error); - IPW_DEBUG_INFO("%s: Fatal error value: 0x%08X\n", - priv->net_dev->name, priv->fatal_error); - - read_nic_dword(dev, IPW_ERROR_ADDR(priv->fatal_error), &tmp); - IPW_DEBUG_INFO("%s: Fatal error address value: 0x%08X\n", - priv->net_dev->name, tmp); - - /* Wake up any sleeping jobs */ - schedule_reset(priv); - } - - if (inta & IPW2100_INTA_PARITY_ERROR) { - printk(KERN_ERR DRV_NAME - ": ***** PARITY ERROR INTERRUPT !!!!\n"); - priv->inta_other++; - write_register(dev, IPW_REG_INTA, IPW2100_INTA_PARITY_ERROR); - } - - if (inta & IPW2100_INTA_RX_TRANSFER) { - IPW_DEBUG_ISR("RX interrupt\n"); - - priv->rx_interrupts++; - - write_register(dev, IPW_REG_INTA, IPW2100_INTA_RX_TRANSFER); - - __ipw2100_rx_process(priv); - __ipw2100_tx_complete(priv); - } - - if (inta & IPW2100_INTA_TX_TRANSFER) { - IPW_DEBUG_ISR("TX interrupt\n"); - - priv->tx_interrupts++; - - write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_TRANSFER); - - __ipw2100_tx_complete(priv); - ipw2100_tx_send_commands(priv); - ipw2100_tx_send_data(priv); - } - - if (inta & IPW2100_INTA_TX_COMPLETE) { - IPW_DEBUG_ISR("TX complete\n"); - priv->inta_other++; - write_register(dev, IPW_REG_INTA, IPW2100_INTA_TX_COMPLETE); - - __ipw2100_tx_complete(priv); - } - - if (inta & IPW2100_INTA_EVENT_INTERRUPT) { - /* ipw2100_handle_event(dev); */ - priv->inta_other++; - write_register(dev, IPW_REG_INTA, IPW2100_INTA_EVENT_INTERRUPT); - } - - if (inta & IPW2100_INTA_FW_INIT_DONE) { - IPW_DEBUG_ISR("FW init done interrupt\n"); - priv->inta_other++; - - read_register(dev, IPW_REG_INTA, &tmp); - if (tmp & (IPW2100_INTA_FATAL_ERROR | - IPW2100_INTA_PARITY_ERROR)) { - write_register(dev, IPW_REG_INTA, - IPW2100_INTA_FATAL_ERROR | - IPW2100_INTA_PARITY_ERROR); - } - - write_register(dev, IPW_REG_INTA, IPW2100_INTA_FW_INIT_DONE); - } - - if (inta & IPW2100_INTA_STATUS_CHANGE) { - IPW_DEBUG_ISR("Status change interrupt\n"); - priv->inta_other++; - write_register(dev, IPW_REG_INTA, IPW2100_INTA_STATUS_CHANGE); - } - - if (inta & IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE) { - IPW_DEBUG_ISR("slave host mode interrupt\n"); - priv->inta_other++; - write_register(dev, IPW_REG_INTA, - IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE); - } - - priv->in_isr--; - ipw2100_enable_interrupts(priv); - - spin_unlock_irqrestore(&priv->low_lock, flags); - - IPW_DEBUG_ISR("exit\n"); -} - -static irqreturn_t ipw2100_interrupt(int irq, void *data) -{ - struct ipw2100_priv *priv = data; - u32 inta, inta_mask; - - if (!data) - return IRQ_NONE; - - spin_lock(&priv->low_lock); - - /* We check to see if we should be ignoring interrupts before - * we touch the hardware. During ucode load if we try and handle - * an interrupt we can cause keyboard problems as well as cause - * the ucode to fail to initialize */ - if (!(priv->status & STATUS_INT_ENABLED)) { - /* Shared IRQ */ - goto none; - } - - read_register(priv->net_dev, IPW_REG_INTA_MASK, &inta_mask); - read_register(priv->net_dev, IPW_REG_INTA, &inta); - - if (inta == 0xFFFFFFFF) { - /* Hardware disappeared */ - printk(KERN_WARNING DRV_NAME ": IRQ INTA == 0xFFFFFFFF\n"); - goto none; - } - - inta &= IPW_INTERRUPT_MASK; - - if (!(inta & inta_mask)) { - /* Shared interrupt */ - goto none; - } - - /* We disable the hardware interrupt here just to prevent unneeded - * calls to be made. We disable this again within the actual - * work tasklet, so if another part of the code re-enables the - * interrupt, that is fine */ - ipw2100_disable_interrupts(priv); - - tasklet_schedule(&priv->irq_tasklet); - spin_unlock(&priv->low_lock); - - return IRQ_HANDLED; - none: - spin_unlock(&priv->low_lock); - return IRQ_NONE; -} - -static netdev_tx_t ipw2100_tx(struct libipw_txb *txb, - struct net_device *dev, int pri) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - struct list_head *element; - struct ipw2100_tx_packet *packet; - unsigned long flags; - - spin_lock_irqsave(&priv->low_lock, flags); - - if (!(priv->status & STATUS_ASSOCIATED)) { - IPW_DEBUG_INFO("Can not transmit when not connected.\n"); - priv->net_dev->stats.tx_carrier_errors++; - netif_stop_queue(dev); - goto fail_unlock; - } - - if (list_empty(&priv->tx_free_list)) - goto fail_unlock; - - element = priv->tx_free_list.next; - packet = list_entry(element, struct ipw2100_tx_packet, list); - - packet->info.d_struct.txb = txb; - - IPW_DEBUG_TX("Sending fragment (%d bytes):\n", txb->fragments[0]->len); - printk_buf(IPW_DL_TX, txb->fragments[0]->data, txb->fragments[0]->len); - - packet->jiffy_start = jiffies; - - list_del(element); - DEC_STAT(&priv->tx_free_stat); - - list_add_tail(element, &priv->tx_pend_list); - INC_STAT(&priv->tx_pend_stat); - - ipw2100_tx_send_data(priv); - - spin_unlock_irqrestore(&priv->low_lock, flags); - return NETDEV_TX_OK; - -fail_unlock: - netif_stop_queue(dev); - spin_unlock_irqrestore(&priv->low_lock, flags); - return NETDEV_TX_BUSY; -} - -static int ipw2100_msg_allocate(struct ipw2100_priv *priv) -{ - int i, j, err = -EINVAL; - void *v; - dma_addr_t p; - - priv->msg_buffers = - kmalloc(IPW_COMMAND_POOL_SIZE * sizeof(struct ipw2100_tx_packet), - GFP_KERNEL); - if (!priv->msg_buffers) - return -ENOMEM; - - for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) { - v = pci_zalloc_consistent(priv->pci_dev, - sizeof(struct ipw2100_cmd_header), - &p); - if (!v) { - printk(KERN_ERR DRV_NAME ": " - "%s: PCI alloc failed for msg " - "buffers.\n", priv->net_dev->name); - err = -ENOMEM; - break; - } - - priv->msg_buffers[i].type = COMMAND; - priv->msg_buffers[i].info.c_struct.cmd = - (struct ipw2100_cmd_header *)v; - priv->msg_buffers[i].info.c_struct.cmd_phys = p; - } - - if (i == IPW_COMMAND_POOL_SIZE) - return 0; - - for (j = 0; j < i; j++) { - pci_free_consistent(priv->pci_dev, - sizeof(struct ipw2100_cmd_header), - priv->msg_buffers[j].info.c_struct.cmd, - priv->msg_buffers[j].info.c_struct. - cmd_phys); - } - - kfree(priv->msg_buffers); - priv->msg_buffers = NULL; - - return err; -} - -static int ipw2100_msg_initialize(struct ipw2100_priv *priv) -{ - int i; - - INIT_LIST_HEAD(&priv->msg_free_list); - INIT_LIST_HEAD(&priv->msg_pend_list); - - for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) - list_add_tail(&priv->msg_buffers[i].list, &priv->msg_free_list); - SET_STAT(&priv->msg_free_stat, i); - - return 0; -} - -static void ipw2100_msg_free(struct ipw2100_priv *priv) -{ - int i; - - if (!priv->msg_buffers) - return; - - for (i = 0; i < IPW_COMMAND_POOL_SIZE; i++) { - pci_free_consistent(priv->pci_dev, - sizeof(struct ipw2100_cmd_header), - priv->msg_buffers[i].info.c_struct.cmd, - priv->msg_buffers[i].info.c_struct. - cmd_phys); - } - - kfree(priv->msg_buffers); - priv->msg_buffers = NULL; -} - -static ssize_t show_pci(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct pci_dev *pci_dev = container_of(d, struct pci_dev, dev); - char *out = buf; - int i, j; - u32 val; - - for (i = 0; i < 16; i++) { - out += sprintf(out, "[%08X] ", i * 16); - for (j = 0; j < 16; j += 4) { - pci_read_config_dword(pci_dev, i * 16 + j, &val); - out += sprintf(out, "%08X ", val); - } - out += sprintf(out, "\n"); - } - - return out - buf; -} - -static DEVICE_ATTR(pci, S_IRUGO, show_pci, NULL); - -static ssize_t show_cfg(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw2100_priv *p = dev_get_drvdata(d); - return sprintf(buf, "0x%08x\n", (int)p->config); -} - -static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL); - -static ssize_t show_status(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw2100_priv *p = dev_get_drvdata(d); - return sprintf(buf, "0x%08x\n", (int)p->status); -} - -static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); - -static ssize_t show_capability(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw2100_priv *p = dev_get_drvdata(d); - return sprintf(buf, "0x%08x\n", (int)p->capability); -} - -static DEVICE_ATTR(capability, S_IRUGO, show_capability, NULL); - -#define IPW2100_REG(x) { IPW_ ##x, #x } -static const struct { - u32 addr; - const char *name; -} hw_data[] = { -IPW2100_REG(REG_GP_CNTRL), - IPW2100_REG(REG_GPIO), - IPW2100_REG(REG_INTA), - IPW2100_REG(REG_INTA_MASK), IPW2100_REG(REG_RESET_REG),}; -#define IPW2100_NIC(x, s) { x, #x, s } -static const struct { - u32 addr; - const char *name; - size_t size; -} nic_data[] = { -IPW2100_NIC(IPW2100_CONTROL_REG, 2), - IPW2100_NIC(0x210014, 1), IPW2100_NIC(0x210000, 1),}; -#define IPW2100_ORD(x, d) { IPW_ORD_ ##x, #x, d } -static const struct { - u8 index; - const char *name; - const char *desc; -} ord_data[] = { -IPW2100_ORD(STAT_TX_HOST_REQUESTS, "requested Host Tx's (MSDU)"), - IPW2100_ORD(STAT_TX_HOST_COMPLETE, - "successful Host Tx's (MSDU)"), - IPW2100_ORD(STAT_TX_DIR_DATA, - "successful Directed Tx's (MSDU)"), - IPW2100_ORD(STAT_TX_DIR_DATA1, - "successful Directed Tx's (MSDU) @ 1MB"), - IPW2100_ORD(STAT_TX_DIR_DATA2, - "successful Directed Tx's (MSDU) @ 2MB"), - IPW2100_ORD(STAT_TX_DIR_DATA5_5, - "successful Directed Tx's (MSDU) @ 5_5MB"), - IPW2100_ORD(STAT_TX_DIR_DATA11, - "successful Directed Tx's (MSDU) @ 11MB"), - IPW2100_ORD(STAT_TX_NODIR_DATA1, - "successful Non_Directed Tx's (MSDU) @ 1MB"), - IPW2100_ORD(STAT_TX_NODIR_DATA2, - "successful Non_Directed Tx's (MSDU) @ 2MB"), - IPW2100_ORD(STAT_TX_NODIR_DATA5_5, - "successful Non_Directed Tx's (MSDU) @ 5.5MB"), - IPW2100_ORD(STAT_TX_NODIR_DATA11, - "successful Non_Directed Tx's (MSDU) @ 11MB"), - IPW2100_ORD(STAT_NULL_DATA, "successful NULL data Tx's"), - IPW2100_ORD(STAT_TX_RTS, "successful Tx RTS"), - IPW2100_ORD(STAT_TX_CTS, "successful Tx CTS"), - IPW2100_ORD(STAT_TX_ACK, "successful Tx ACK"), - IPW2100_ORD(STAT_TX_ASSN, "successful Association Tx's"), - IPW2100_ORD(STAT_TX_ASSN_RESP, - "successful Association response Tx's"), - IPW2100_ORD(STAT_TX_REASSN, - "successful Reassociation Tx's"), - IPW2100_ORD(STAT_TX_REASSN_RESP, - "successful Reassociation response Tx's"), - IPW2100_ORD(STAT_TX_PROBE, - "probes successfully transmitted"), - IPW2100_ORD(STAT_TX_PROBE_RESP, - "probe responses successfully transmitted"), - IPW2100_ORD(STAT_TX_BEACON, "tx beacon"), - IPW2100_ORD(STAT_TX_ATIM, "Tx ATIM"), - IPW2100_ORD(STAT_TX_DISASSN, - "successful Disassociation TX"), - IPW2100_ORD(STAT_TX_AUTH, "successful Authentication Tx"), - IPW2100_ORD(STAT_TX_DEAUTH, - "successful Deauthentication TX"), - IPW2100_ORD(STAT_TX_TOTAL_BYTES, - "Total successful Tx data bytes"), - IPW2100_ORD(STAT_TX_RETRIES, "Tx retries"), - IPW2100_ORD(STAT_TX_RETRY1, "Tx retries at 1MBPS"), - IPW2100_ORD(STAT_TX_RETRY2, "Tx retries at 2MBPS"), - IPW2100_ORD(STAT_TX_RETRY5_5, "Tx retries at 5.5MBPS"), - IPW2100_ORD(STAT_TX_RETRY11, "Tx retries at 11MBPS"), - IPW2100_ORD(STAT_TX_FAILURES, "Tx Failures"), - IPW2100_ORD(STAT_TX_MAX_TRIES_IN_HOP, - "times max tries in a hop failed"), - IPW2100_ORD(STAT_TX_DISASSN_FAIL, - "times disassociation failed"), - IPW2100_ORD(STAT_TX_ERR_CTS, "missed/bad CTS frames"), - IPW2100_ORD(STAT_TX_ERR_ACK, "tx err due to acks"), - IPW2100_ORD(STAT_RX_HOST, "packets passed to host"), - IPW2100_ORD(STAT_RX_DIR_DATA, "directed packets"), - IPW2100_ORD(STAT_RX_DIR_DATA1, "directed packets at 1MB"), - IPW2100_ORD(STAT_RX_DIR_DATA2, "directed packets at 2MB"), - IPW2100_ORD(STAT_RX_DIR_DATA5_5, - "directed packets at 5.5MB"), - IPW2100_ORD(STAT_RX_DIR_DATA11, "directed packets at 11MB"), - IPW2100_ORD(STAT_RX_NODIR_DATA, "nondirected packets"), - IPW2100_ORD(STAT_RX_NODIR_DATA1, - "nondirected packets at 1MB"), - IPW2100_ORD(STAT_RX_NODIR_DATA2, - "nondirected packets at 2MB"), - IPW2100_ORD(STAT_RX_NODIR_DATA5_5, - "nondirected packets at 5.5MB"), - IPW2100_ORD(STAT_RX_NODIR_DATA11, - "nondirected packets at 11MB"), - IPW2100_ORD(STAT_RX_NULL_DATA, "null data rx's"), - IPW2100_ORD(STAT_RX_RTS, "Rx RTS"), IPW2100_ORD(STAT_RX_CTS, - "Rx CTS"), - IPW2100_ORD(STAT_RX_ACK, "Rx ACK"), - IPW2100_ORD(STAT_RX_CFEND, "Rx CF End"), - IPW2100_ORD(STAT_RX_CFEND_ACK, "Rx CF End + CF Ack"), - IPW2100_ORD(STAT_RX_ASSN, "Association Rx's"), - IPW2100_ORD(STAT_RX_ASSN_RESP, "Association response Rx's"), - IPW2100_ORD(STAT_RX_REASSN, "Reassociation Rx's"), - IPW2100_ORD(STAT_RX_REASSN_RESP, - "Reassociation response Rx's"), - IPW2100_ORD(STAT_RX_PROBE, "probe Rx's"), - IPW2100_ORD(STAT_RX_PROBE_RESP, "probe response Rx's"), - IPW2100_ORD(STAT_RX_BEACON, "Rx beacon"), - IPW2100_ORD(STAT_RX_ATIM, "Rx ATIM"), - IPW2100_ORD(STAT_RX_DISASSN, "disassociation Rx"), - IPW2100_ORD(STAT_RX_AUTH, "authentication Rx"), - IPW2100_ORD(STAT_RX_DEAUTH, "deauthentication Rx"), - IPW2100_ORD(STAT_RX_TOTAL_BYTES, - "Total rx data bytes received"), - IPW2100_ORD(STAT_RX_ERR_CRC, "packets with Rx CRC error"), - IPW2100_ORD(STAT_RX_ERR_CRC1, "Rx CRC errors at 1MB"), - IPW2100_ORD(STAT_RX_ERR_CRC2, "Rx CRC errors at 2MB"), - IPW2100_ORD(STAT_RX_ERR_CRC5_5, "Rx CRC errors at 5.5MB"), - IPW2100_ORD(STAT_RX_ERR_CRC11, "Rx CRC errors at 11MB"), - IPW2100_ORD(STAT_RX_DUPLICATE1, - "duplicate rx packets at 1MB"), - IPW2100_ORD(STAT_RX_DUPLICATE2, - "duplicate rx packets at 2MB"), - IPW2100_ORD(STAT_RX_DUPLICATE5_5, - "duplicate rx packets at 5.5MB"), - IPW2100_ORD(STAT_RX_DUPLICATE11, - "duplicate rx packets at 11MB"), - IPW2100_ORD(STAT_RX_DUPLICATE, "duplicate rx packets"), - IPW2100_ORD(PERS_DB_LOCK, "locking fw permanent db"), - IPW2100_ORD(PERS_DB_SIZE, "size of fw permanent db"), - IPW2100_ORD(PERS_DB_ADDR, "address of fw permanent db"), - IPW2100_ORD(STAT_RX_INVALID_PROTOCOL, - "rx frames with invalid protocol"), - IPW2100_ORD(SYS_BOOT_TIME, "Boot time"), - IPW2100_ORD(STAT_RX_NO_BUFFER, - "rx frames rejected due to no buffer"), - IPW2100_ORD(STAT_RX_MISSING_FRAG, - "rx frames dropped due to missing fragment"), - IPW2100_ORD(STAT_RX_ORPHAN_FRAG, - "rx frames dropped due to non-sequential fragment"), - IPW2100_ORD(STAT_RX_ORPHAN_FRAME, - "rx frames dropped due to unmatched 1st frame"), - IPW2100_ORD(STAT_RX_FRAG_AGEOUT, - "rx frames dropped due to uncompleted frame"), - IPW2100_ORD(STAT_RX_ICV_ERRORS, - "ICV errors during decryption"), - IPW2100_ORD(STAT_PSP_SUSPENSION, "times adapter suspended"), - IPW2100_ORD(STAT_PSP_BCN_TIMEOUT, "beacon timeout"), - IPW2100_ORD(STAT_PSP_POLL_TIMEOUT, - "poll response timeouts"), - IPW2100_ORD(STAT_PSP_NONDIR_TIMEOUT, - "timeouts waiting for last {broad,multi}cast pkt"), - IPW2100_ORD(STAT_PSP_RX_DTIMS, "PSP DTIMs received"), - IPW2100_ORD(STAT_PSP_RX_TIMS, "PSP TIMs received"), - IPW2100_ORD(STAT_PSP_STATION_ID, "PSP Station ID"), - IPW2100_ORD(LAST_ASSN_TIME, "RTC time of last association"), - IPW2100_ORD(STAT_PERCENT_MISSED_BCNS, - "current calculation of % missed beacons"), - IPW2100_ORD(STAT_PERCENT_RETRIES, - "current calculation of % missed tx retries"), - IPW2100_ORD(ASSOCIATED_AP_PTR, - "0 if not associated, else pointer to AP table entry"), - IPW2100_ORD(AVAILABLE_AP_CNT, - "AP's decsribed in the AP table"), - IPW2100_ORD(AP_LIST_PTR, "Ptr to list of available APs"), - IPW2100_ORD(STAT_AP_ASSNS, "associations"), - IPW2100_ORD(STAT_ASSN_FAIL, "association failures"), - IPW2100_ORD(STAT_ASSN_RESP_FAIL, - "failures due to response fail"), - IPW2100_ORD(STAT_FULL_SCANS, "full scans"), - IPW2100_ORD(CARD_DISABLED, "Card Disabled"), - IPW2100_ORD(STAT_ROAM_INHIBIT, - "times roaming was inhibited due to activity"), - IPW2100_ORD(RSSI_AT_ASSN, - "RSSI of associated AP at time of association"), - IPW2100_ORD(STAT_ASSN_CAUSE1, - "reassociation: no probe response or TX on hop"), - IPW2100_ORD(STAT_ASSN_CAUSE2, - "reassociation: poor tx/rx quality"), - IPW2100_ORD(STAT_ASSN_CAUSE3, - "reassociation: tx/rx quality (excessive AP load"), - IPW2100_ORD(STAT_ASSN_CAUSE4, - "reassociation: AP RSSI level"), - IPW2100_ORD(STAT_ASSN_CAUSE5, - "reassociations due to load leveling"), - IPW2100_ORD(STAT_AUTH_FAIL, "times authentication failed"), - IPW2100_ORD(STAT_AUTH_RESP_FAIL, - "times authentication response failed"), - IPW2100_ORD(STATION_TABLE_CNT, - "entries in association table"), - IPW2100_ORD(RSSI_AVG_CURR, "Current avg RSSI"), - IPW2100_ORD(POWER_MGMT_MODE, "Power mode - 0=CAM, 1=PSP"), - IPW2100_ORD(COUNTRY_CODE, - "IEEE country code as recv'd from beacon"), - IPW2100_ORD(COUNTRY_CHANNELS, - "channels supported by country"), - IPW2100_ORD(RESET_CNT, "adapter resets (warm)"), - IPW2100_ORD(BEACON_INTERVAL, "Beacon interval"), - IPW2100_ORD(ANTENNA_DIVERSITY, - "TRUE if antenna diversity is disabled"), - IPW2100_ORD(DTIM_PERIOD, "beacon intervals between DTIMs"), - IPW2100_ORD(OUR_FREQ, - "current radio freq lower digits - channel ID"), - IPW2100_ORD(RTC_TIME, "current RTC time"), - IPW2100_ORD(PORT_TYPE, "operating mode"), - IPW2100_ORD(CURRENT_TX_RATE, "current tx rate"), - IPW2100_ORD(SUPPORTED_RATES, "supported tx rates"), - IPW2100_ORD(ATIM_WINDOW, "current ATIM Window"), - IPW2100_ORD(BASIC_RATES, "basic tx rates"), - IPW2100_ORD(NIC_HIGHEST_RATE, "NIC highest tx rate"), - IPW2100_ORD(AP_HIGHEST_RATE, "AP highest tx rate"), - IPW2100_ORD(CAPABILITIES, - "Management frame capability field"), - IPW2100_ORD(AUTH_TYPE, "Type of authentication"), - IPW2100_ORD(RADIO_TYPE, "Adapter card platform type"), - IPW2100_ORD(RTS_THRESHOLD, - "Min packet length for RTS handshaking"), - IPW2100_ORD(INT_MODE, "International mode"), - IPW2100_ORD(FRAGMENTATION_THRESHOLD, - "protocol frag threshold"), - IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_START_ADDRESS, - "EEPROM offset in SRAM"), - IPW2100_ORD(EEPROM_SRAM_DB_BLOCK_SIZE, - "EEPROM size in SRAM"), - IPW2100_ORD(EEPROM_SKU_CAPABILITY, "EEPROM SKU Capability"), - IPW2100_ORD(EEPROM_IBSS_11B_CHANNELS, - "EEPROM IBSS 11b channel set"), - IPW2100_ORD(MAC_VERSION, "MAC Version"), - IPW2100_ORD(MAC_REVISION, "MAC Revision"), - IPW2100_ORD(RADIO_VERSION, "Radio Version"), - IPW2100_ORD(NIC_MANF_DATE_TIME, "MANF Date/Time STAMP"), - IPW2100_ORD(UCODE_VERSION, "Ucode Version"),}; - -static ssize_t show_registers(struct device *d, struct device_attribute *attr, - char *buf) -{ - int i; - struct ipw2100_priv *priv = dev_get_drvdata(d); - struct net_device *dev = priv->net_dev; - char *out = buf; - u32 val = 0; - - out += sprintf(out, "%30s [Address ] : Hex\n", "Register"); - - for (i = 0; i < ARRAY_SIZE(hw_data); i++) { - read_register(dev, hw_data[i].addr, &val); - out += sprintf(out, "%30s [%08X] : %08X\n", - hw_data[i].name, hw_data[i].addr, val); - } - - return out - buf; -} - -static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); - -static ssize_t show_hardware(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - struct net_device *dev = priv->net_dev; - char *out = buf; - int i; - - out += sprintf(out, "%30s [Address ] : Hex\n", "NIC entry"); - - for (i = 0; i < ARRAY_SIZE(nic_data); i++) { - u8 tmp8; - u16 tmp16; - u32 tmp32; - - switch (nic_data[i].size) { - case 1: - read_nic_byte(dev, nic_data[i].addr, &tmp8); - out += sprintf(out, "%30s [%08X] : %02X\n", - nic_data[i].name, nic_data[i].addr, - tmp8); - break; - case 2: - read_nic_word(dev, nic_data[i].addr, &tmp16); - out += sprintf(out, "%30s [%08X] : %04X\n", - nic_data[i].name, nic_data[i].addr, - tmp16); - break; - case 4: - read_nic_dword(dev, nic_data[i].addr, &tmp32); - out += sprintf(out, "%30s [%08X] : %08X\n", - nic_data[i].name, nic_data[i].addr, - tmp32); - break; - } - } - return out - buf; -} - -static DEVICE_ATTR(hardware, S_IRUGO, show_hardware, NULL); - -static ssize_t show_memory(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - struct net_device *dev = priv->net_dev; - static unsigned long loop = 0; - int len = 0; - u32 buffer[4]; - int i; - char line[81]; - - if (loop >= 0x30000) - loop = 0; - - /* sysfs provides us PAGE_SIZE buffer */ - while (len < PAGE_SIZE - 128 && loop < 0x30000) { - - if (priv->snapshot[0]) - for (i = 0; i < 4; i++) - buffer[i] = - *(u32 *) SNAPSHOT_ADDR(loop + i * 4); - else - for (i = 0; i < 4; i++) - read_nic_dword(dev, loop + i * 4, &buffer[i]); - - if (priv->dump_raw) - len += sprintf(buf + len, - "%c%c%c%c" - "%c%c%c%c" - "%c%c%c%c" - "%c%c%c%c", - ((u8 *) buffer)[0x0], - ((u8 *) buffer)[0x1], - ((u8 *) buffer)[0x2], - ((u8 *) buffer)[0x3], - ((u8 *) buffer)[0x4], - ((u8 *) buffer)[0x5], - ((u8 *) buffer)[0x6], - ((u8 *) buffer)[0x7], - ((u8 *) buffer)[0x8], - ((u8 *) buffer)[0x9], - ((u8 *) buffer)[0xa], - ((u8 *) buffer)[0xb], - ((u8 *) buffer)[0xc], - ((u8 *) buffer)[0xd], - ((u8 *) buffer)[0xe], - ((u8 *) buffer)[0xf]); - else - len += sprintf(buf + len, "%s\n", - snprint_line(line, sizeof(line), - (u8 *) buffer, 16, loop)); - loop += 16; - } - - return len; -} - -static ssize_t store_memory(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - struct net_device *dev = priv->net_dev; - const char *p = buf; - - (void)dev; /* kill unused-var warning for debug-only code */ - - if (count < 1) - return count; - - if (p[0] == '1' || - (count >= 2 && tolower(p[0]) == 'o' && tolower(p[1]) == 'n')) { - IPW_DEBUG_INFO("%s: Setting memory dump to RAW mode.\n", - dev->name); - priv->dump_raw = 1; - - } else if (p[0] == '0' || (count >= 2 && tolower(p[0]) == 'o' && - tolower(p[1]) == 'f')) { - IPW_DEBUG_INFO("%s: Setting memory dump to HEX mode.\n", - dev->name); - priv->dump_raw = 0; - - } else if (tolower(p[0]) == 'r') { - IPW_DEBUG_INFO("%s: Resetting firmware snapshot.\n", dev->name); - ipw2100_snapshot_free(priv); - - } else - IPW_DEBUG_INFO("%s: Usage: 0|on = HEX, 1|off = RAW, " - "reset = clear memory snapshot\n", dev->name); - - return count; -} - -static DEVICE_ATTR(memory, S_IWUSR | S_IRUGO, show_memory, store_memory); - -static ssize_t show_ordinals(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - u32 val = 0; - int len = 0; - u32 val_len; - static int loop = 0; - - if (priv->status & STATUS_RF_KILL_MASK) - return 0; - - if (loop >= ARRAY_SIZE(ord_data)) - loop = 0; - - /* sysfs provides us PAGE_SIZE buffer */ - while (len < PAGE_SIZE - 128 && loop < ARRAY_SIZE(ord_data)) { - val_len = sizeof(u32); - - if (ipw2100_get_ordinal(priv, ord_data[loop].index, &val, - &val_len)) - len += sprintf(buf + len, "[0x%02X] = ERROR %s\n", - ord_data[loop].index, - ord_data[loop].desc); - else - len += sprintf(buf + len, "[0x%02X] = 0x%08X %s\n", - ord_data[loop].index, val, - ord_data[loop].desc); - loop++; - } - - return len; -} - -static DEVICE_ATTR(ordinals, S_IRUGO, show_ordinals, NULL); - -static ssize_t show_stats(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - char *out = buf; - - out += sprintf(out, "interrupts: %d {tx: %d, rx: %d, other: %d}\n", - priv->interrupts, priv->tx_interrupts, - priv->rx_interrupts, priv->inta_other); - out += sprintf(out, "firmware resets: %d\n", priv->resets); - out += sprintf(out, "firmware hangs: %d\n", priv->hangs); -#ifdef CONFIG_IPW2100_DEBUG - out += sprintf(out, "packet mismatch image: %s\n", - priv->snapshot[0] ? "YES" : "NO"); -#endif - - return out - buf; -} - -static DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL); - -static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode) -{ - int err; - - if (mode == priv->ieee->iw_mode) - return 0; - - err = ipw2100_disable_adapter(priv); - if (err) { - printk(KERN_ERR DRV_NAME ": %s: Could not disable adapter %d\n", - priv->net_dev->name, err); - return err; - } - - switch (mode) { - case IW_MODE_INFRA: - priv->net_dev->type = ARPHRD_ETHER; - break; - case IW_MODE_ADHOC: - priv->net_dev->type = ARPHRD_ETHER; - break; -#ifdef CONFIG_IPW2100_MONITOR - case IW_MODE_MONITOR: - priv->last_mode = priv->ieee->iw_mode; - priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; - break; -#endif /* CONFIG_IPW2100_MONITOR */ - } - - priv->ieee->iw_mode = mode; - -#ifdef CONFIG_PM - /* Indicate ipw2100_download_firmware download firmware - * from disk instead of memory. */ - ipw2100_firmware.version = 0; -#endif - - printk(KERN_INFO "%s: Resetting on mode change.\n", priv->net_dev->name); - priv->reset_backoff = 0; - schedule_reset(priv); - - return 0; -} - -static ssize_t show_internals(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - int len = 0; - -#define DUMP_VAR(x,y) len += sprintf(buf + len, # x ": %" y "\n", priv-> x) - - if (priv->status & STATUS_ASSOCIATED) - len += sprintf(buf + len, "connected: %lu\n", - get_seconds() - priv->connect_start); - else - len += sprintf(buf + len, "not connected\n"); - - DUMP_VAR(ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx], "p"); - DUMP_VAR(status, "08lx"); - DUMP_VAR(config, "08lx"); - DUMP_VAR(capability, "08lx"); - - len += - sprintf(buf + len, "last_rtc: %lu\n", - (unsigned long)priv->last_rtc); - - DUMP_VAR(fatal_error, "d"); - DUMP_VAR(stop_hang_check, "d"); - DUMP_VAR(stop_rf_kill, "d"); - DUMP_VAR(messages_sent, "d"); - - DUMP_VAR(tx_pend_stat.value, "d"); - DUMP_VAR(tx_pend_stat.hi, "d"); - - DUMP_VAR(tx_free_stat.value, "d"); - DUMP_VAR(tx_free_stat.lo, "d"); - - DUMP_VAR(msg_free_stat.value, "d"); - DUMP_VAR(msg_free_stat.lo, "d"); - - DUMP_VAR(msg_pend_stat.value, "d"); - DUMP_VAR(msg_pend_stat.hi, "d"); - - DUMP_VAR(fw_pend_stat.value, "d"); - DUMP_VAR(fw_pend_stat.hi, "d"); - - DUMP_VAR(txq_stat.value, "d"); - DUMP_VAR(txq_stat.lo, "d"); - - DUMP_VAR(ieee->scans, "d"); - DUMP_VAR(reset_backoff, "d"); - - return len; -} - -static DEVICE_ATTR(internals, S_IRUGO, show_internals, NULL); - -static ssize_t show_bssinfo(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - char essid[IW_ESSID_MAX_SIZE + 1]; - u8 bssid[ETH_ALEN]; - u32 chan = 0; - char *out = buf; - unsigned int length; - int ret; - - if (priv->status & STATUS_RF_KILL_MASK) - return 0; - - memset(essid, 0, sizeof(essid)); - memset(bssid, 0, sizeof(bssid)); - - length = IW_ESSID_MAX_SIZE; - ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_SSID, essid, &length); - if (ret) - IPW_DEBUG_INFO("failed querying ordinals at line %d\n", - __LINE__); - - length = sizeof(bssid); - ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, - bssid, &length); - if (ret) - IPW_DEBUG_INFO("failed querying ordinals at line %d\n", - __LINE__); - - length = sizeof(u32); - ret = ipw2100_get_ordinal(priv, IPW_ORD_OUR_FREQ, &chan, &length); - if (ret) - IPW_DEBUG_INFO("failed querying ordinals at line %d\n", - __LINE__); - - out += sprintf(out, "ESSID: %s\n", essid); - out += sprintf(out, "BSSID: %pM\n", bssid); - out += sprintf(out, "Channel: %d\n", chan); - - return out - buf; -} - -static DEVICE_ATTR(bssinfo, S_IRUGO, show_bssinfo, NULL); - -#ifdef CONFIG_IPW2100_DEBUG -static ssize_t show_debug_level(struct device_driver *d, char *buf) -{ - return sprintf(buf, "0x%08X\n", ipw2100_debug_level); -} - -static ssize_t store_debug_level(struct device_driver *d, - const char *buf, size_t count) -{ - u32 val; - int ret; - - ret = kstrtou32(buf, 0, &val); - if (ret) - IPW_DEBUG_INFO(": %s is not in hex or decimal form.\n", buf); - else - ipw2100_debug_level = val; - - return strnlen(buf, count); -} - -static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, show_debug_level, - store_debug_level); -#endif /* CONFIG_IPW2100_DEBUG */ - -static ssize_t show_fatal_error(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - char *out = buf; - int i; - - if (priv->fatal_error) - out += sprintf(out, "0x%08X\n", priv->fatal_error); - else - out += sprintf(out, "0\n"); - - for (i = 1; i <= IPW2100_ERROR_QUEUE; i++) { - if (!priv->fatal_errors[(priv->fatal_index - i) % - IPW2100_ERROR_QUEUE]) - continue; - - out += sprintf(out, "%d. 0x%08X\n", i, - priv->fatal_errors[(priv->fatal_index - i) % - IPW2100_ERROR_QUEUE]); - } - - return out - buf; -} - -static ssize_t store_fatal_error(struct device *d, - struct device_attribute *attr, const char *buf, - size_t count) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - schedule_reset(priv); - return count; -} - -static DEVICE_ATTR(fatal_error, S_IWUSR | S_IRUGO, show_fatal_error, - store_fatal_error); - -static ssize_t show_scan_age(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - return sprintf(buf, "%d\n", priv->ieee->scan_age); -} - -static ssize_t store_scan_age(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - struct net_device *dev = priv->net_dev; - unsigned long val; - int ret; - - (void)dev; /* kill unused-var warning for debug-only code */ - - IPW_DEBUG_INFO("enter\n"); - - ret = kstrtoul(buf, 0, &val); - if (ret) { - IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name); - } else { - priv->ieee->scan_age = val; - IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age); - } - - IPW_DEBUG_INFO("exit\n"); - return strnlen(buf, count); -} - -static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age); - -static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr, - char *buf) -{ - /* 0 - RF kill not enabled - 1 - SW based RF kill active (sysfs) - 2 - HW based RF kill active - 3 - Both HW and SW baed RF kill active */ - struct ipw2100_priv *priv = dev_get_drvdata(d); - int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) | - (rf_kill_active(priv) ? 0x2 : 0x0); - return sprintf(buf, "%i\n", val); -} - -static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio) -{ - if ((disable_radio ? 1 : 0) == - (priv->status & STATUS_RF_KILL_SW ? 1 : 0)) - return 0; - - IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n", - disable_radio ? "OFF" : "ON"); - - mutex_lock(&priv->action_mutex); - - if (disable_radio) { - priv->status |= STATUS_RF_KILL_SW; - ipw2100_down(priv); - } else { - priv->status &= ~STATUS_RF_KILL_SW; - if (rf_kill_active(priv)) { - IPW_DEBUG_RF_KILL("Can not turn radio back on - " - "disabled by HW switch\n"); - /* Make sure the RF_KILL check timer is running */ - priv->stop_rf_kill = 0; - mod_delayed_work(system_wq, &priv->rf_kill, - round_jiffies_relative(HZ)); - } else - schedule_reset(priv); - } - - mutex_unlock(&priv->action_mutex); - return 1; -} - -static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw2100_priv *priv = dev_get_drvdata(d); - ipw_radio_kill_sw(priv, buf[0] == '1'); - return count; -} - -static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill); - -static struct attribute *ipw2100_sysfs_entries[] = { - &dev_attr_hardware.attr, - &dev_attr_registers.attr, - &dev_attr_ordinals.attr, - &dev_attr_pci.attr, - &dev_attr_stats.attr, - &dev_attr_internals.attr, - &dev_attr_bssinfo.attr, - &dev_attr_memory.attr, - &dev_attr_scan_age.attr, - &dev_attr_fatal_error.attr, - &dev_attr_rf_kill.attr, - &dev_attr_cfg.attr, - &dev_attr_status.attr, - &dev_attr_capability.attr, - NULL, -}; - -static struct attribute_group ipw2100_attribute_group = { - .attrs = ipw2100_sysfs_entries, -}; - -static int status_queue_allocate(struct ipw2100_priv *priv, int entries) -{ - struct ipw2100_status_queue *q = &priv->status_queue; - - IPW_DEBUG_INFO("enter\n"); - - q->size = entries * sizeof(struct ipw2100_status); - q->drv = pci_zalloc_consistent(priv->pci_dev, q->size, &q->nic); - if (!q->drv) { - IPW_DEBUG_WARNING("Can not allocate status queue.\n"); - return -ENOMEM; - } - - IPW_DEBUG_INFO("exit\n"); - - return 0; -} - -static void status_queue_free(struct ipw2100_priv *priv) -{ - IPW_DEBUG_INFO("enter\n"); - - if (priv->status_queue.drv) { - pci_free_consistent(priv->pci_dev, priv->status_queue.size, - priv->status_queue.drv, - priv->status_queue.nic); - priv->status_queue.drv = NULL; - } - - IPW_DEBUG_INFO("exit\n"); -} - -static int bd_queue_allocate(struct ipw2100_priv *priv, - struct ipw2100_bd_queue *q, int entries) -{ - IPW_DEBUG_INFO("enter\n"); - - memset(q, 0, sizeof(struct ipw2100_bd_queue)); - - q->entries = entries; - q->size = entries * sizeof(struct ipw2100_bd); - q->drv = pci_zalloc_consistent(priv->pci_dev, q->size, &q->nic); - if (!q->drv) { - IPW_DEBUG_INFO - ("can't allocate shared memory for buffer descriptors\n"); - return -ENOMEM; - } - - IPW_DEBUG_INFO("exit\n"); - - return 0; -} - -static void bd_queue_free(struct ipw2100_priv *priv, struct ipw2100_bd_queue *q) -{ - IPW_DEBUG_INFO("enter\n"); - - if (!q) - return; - - if (q->drv) { - pci_free_consistent(priv->pci_dev, q->size, q->drv, q->nic); - q->drv = NULL; - } - - IPW_DEBUG_INFO("exit\n"); -} - -static void bd_queue_initialize(struct ipw2100_priv *priv, - struct ipw2100_bd_queue *q, u32 base, u32 size, - u32 r, u32 w) -{ - IPW_DEBUG_INFO("enter\n"); - - IPW_DEBUG_INFO("initializing bd queue at virt=%p, phys=%08x\n", q->drv, - (u32) q->nic); - - write_register(priv->net_dev, base, q->nic); - write_register(priv->net_dev, size, q->entries); - write_register(priv->net_dev, r, q->oldest); - write_register(priv->net_dev, w, q->next); - - IPW_DEBUG_INFO("exit\n"); -} - -static void ipw2100_kill_works(struct ipw2100_priv *priv) -{ - priv->stop_rf_kill = 1; - priv->stop_hang_check = 1; - cancel_delayed_work_sync(&priv->reset_work); - cancel_delayed_work_sync(&priv->security_work); - cancel_delayed_work_sync(&priv->wx_event_work); - cancel_delayed_work_sync(&priv->hang_check); - cancel_delayed_work_sync(&priv->rf_kill); - cancel_delayed_work_sync(&priv->scan_event); -} - -static int ipw2100_tx_allocate(struct ipw2100_priv *priv) -{ - int i, j, err = -EINVAL; - void *v; - dma_addr_t p; - - IPW_DEBUG_INFO("enter\n"); - - err = bd_queue_allocate(priv, &priv->tx_queue, TX_QUEUE_LENGTH); - if (err) { - IPW_DEBUG_ERROR("%s: failed bd_queue_allocate\n", - priv->net_dev->name); - return err; - } - - priv->tx_buffers = kmalloc_array(TX_PENDED_QUEUE_LENGTH, - sizeof(struct ipw2100_tx_packet), - GFP_ATOMIC); - if (!priv->tx_buffers) { - bd_queue_free(priv, &priv->tx_queue); - return -ENOMEM; - } - - for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) { - v = pci_alloc_consistent(priv->pci_dev, - sizeof(struct ipw2100_data_header), - &p); - if (!v) { - printk(KERN_ERR DRV_NAME - ": %s: PCI alloc failed for tx " "buffers.\n", - priv->net_dev->name); - err = -ENOMEM; - break; - } - - priv->tx_buffers[i].type = DATA; - priv->tx_buffers[i].info.d_struct.data = - (struct ipw2100_data_header *)v; - priv->tx_buffers[i].info.d_struct.data_phys = p; - priv->tx_buffers[i].info.d_struct.txb = NULL; - } - - if (i == TX_PENDED_QUEUE_LENGTH) - return 0; - - for (j = 0; j < i; j++) { - pci_free_consistent(priv->pci_dev, - sizeof(struct ipw2100_data_header), - priv->tx_buffers[j].info.d_struct.data, - priv->tx_buffers[j].info.d_struct. - data_phys); - } - - kfree(priv->tx_buffers); - priv->tx_buffers = NULL; - - return err; -} - -static void ipw2100_tx_initialize(struct ipw2100_priv *priv) -{ - int i; - - IPW_DEBUG_INFO("enter\n"); - - /* - * reinitialize packet info lists - */ - INIT_LIST_HEAD(&priv->fw_pend_list); - INIT_STAT(&priv->fw_pend_stat); - - /* - * reinitialize lists - */ - INIT_LIST_HEAD(&priv->tx_pend_list); - INIT_LIST_HEAD(&priv->tx_free_list); - INIT_STAT(&priv->tx_pend_stat); - INIT_STAT(&priv->tx_free_stat); - - for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) { - /* We simply drop any SKBs that have been queued for - * transmit */ - if (priv->tx_buffers[i].info.d_struct.txb) { - libipw_txb_free(priv->tx_buffers[i].info.d_struct. - txb); - priv->tx_buffers[i].info.d_struct.txb = NULL; - } - - list_add_tail(&priv->tx_buffers[i].list, &priv->tx_free_list); - } - - SET_STAT(&priv->tx_free_stat, i); - - priv->tx_queue.oldest = 0; - priv->tx_queue.available = priv->tx_queue.entries; - priv->tx_queue.next = 0; - INIT_STAT(&priv->txq_stat); - SET_STAT(&priv->txq_stat, priv->tx_queue.available); - - bd_queue_initialize(priv, &priv->tx_queue, - IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE, - IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE, - IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX, - IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX); - - IPW_DEBUG_INFO("exit\n"); - -} - -static void ipw2100_tx_free(struct ipw2100_priv *priv) -{ - int i; - - IPW_DEBUG_INFO("enter\n"); - - bd_queue_free(priv, &priv->tx_queue); - - if (!priv->tx_buffers) - return; - - for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) { - if (priv->tx_buffers[i].info.d_struct.txb) { - libipw_txb_free(priv->tx_buffers[i].info.d_struct. - txb); - priv->tx_buffers[i].info.d_struct.txb = NULL; - } - if (priv->tx_buffers[i].info.d_struct.data) - pci_free_consistent(priv->pci_dev, - sizeof(struct ipw2100_data_header), - priv->tx_buffers[i].info.d_struct. - data, - priv->tx_buffers[i].info.d_struct. - data_phys); - } - - kfree(priv->tx_buffers); - priv->tx_buffers = NULL; - - IPW_DEBUG_INFO("exit\n"); -} - -static int ipw2100_rx_allocate(struct ipw2100_priv *priv) -{ - int i, j, err = -EINVAL; - - IPW_DEBUG_INFO("enter\n"); - - err = bd_queue_allocate(priv, &priv->rx_queue, RX_QUEUE_LENGTH); - if (err) { - IPW_DEBUG_INFO("failed bd_queue_allocate\n"); - return err; - } - - err = status_queue_allocate(priv, RX_QUEUE_LENGTH); - if (err) { - IPW_DEBUG_INFO("failed status_queue_allocate\n"); - bd_queue_free(priv, &priv->rx_queue); - return err; - } - - /* - * allocate packets - */ - priv->rx_buffers = kmalloc(RX_QUEUE_LENGTH * - sizeof(struct ipw2100_rx_packet), - GFP_KERNEL); - if (!priv->rx_buffers) { - IPW_DEBUG_INFO("can't allocate rx packet buffer table\n"); - - bd_queue_free(priv, &priv->rx_queue); - - status_queue_free(priv); - - return -ENOMEM; - } - - for (i = 0; i < RX_QUEUE_LENGTH; i++) { - struct ipw2100_rx_packet *packet = &priv->rx_buffers[i]; - - err = ipw2100_alloc_skb(priv, packet); - if (unlikely(err)) { - err = -ENOMEM; - break; - } - - /* The BD holds the cache aligned address */ - priv->rx_queue.drv[i].host_addr = packet->dma_addr; - priv->rx_queue.drv[i].buf_length = IPW_RX_NIC_BUFFER_LENGTH; - priv->status_queue.drv[i].status_fields = 0; - } - - if (i == RX_QUEUE_LENGTH) - return 0; - - for (j = 0; j < i; j++) { - pci_unmap_single(priv->pci_dev, priv->rx_buffers[j].dma_addr, - sizeof(struct ipw2100_rx_packet), - PCI_DMA_FROMDEVICE); - dev_kfree_skb(priv->rx_buffers[j].skb); - } - - kfree(priv->rx_buffers); - priv->rx_buffers = NULL; - - bd_queue_free(priv, &priv->rx_queue); - - status_queue_free(priv); - - return err; -} - -static void ipw2100_rx_initialize(struct ipw2100_priv *priv) -{ - IPW_DEBUG_INFO("enter\n"); - - priv->rx_queue.oldest = 0; - priv->rx_queue.available = priv->rx_queue.entries - 1; - priv->rx_queue.next = priv->rx_queue.entries - 1; - - INIT_STAT(&priv->rxq_stat); - SET_STAT(&priv->rxq_stat, priv->rx_queue.available); - - bd_queue_initialize(priv, &priv->rx_queue, - IPW_MEM_HOST_SHARED_RX_BD_BASE, - IPW_MEM_HOST_SHARED_RX_BD_SIZE, - IPW_MEM_HOST_SHARED_RX_READ_INDEX, - IPW_MEM_HOST_SHARED_RX_WRITE_INDEX); - - /* set up the status queue */ - write_register(priv->net_dev, IPW_MEM_HOST_SHARED_RX_STATUS_BASE, - priv->status_queue.nic); - - IPW_DEBUG_INFO("exit\n"); -} - -static void ipw2100_rx_free(struct ipw2100_priv *priv) -{ - int i; - - IPW_DEBUG_INFO("enter\n"); - - bd_queue_free(priv, &priv->rx_queue); - status_queue_free(priv); - - if (!priv->rx_buffers) - return; - - for (i = 0; i < RX_QUEUE_LENGTH; i++) { - if (priv->rx_buffers[i].rxp) { - pci_unmap_single(priv->pci_dev, - priv->rx_buffers[i].dma_addr, - sizeof(struct ipw2100_rx), - PCI_DMA_FROMDEVICE); - dev_kfree_skb(priv->rx_buffers[i].skb); - } - } - - kfree(priv->rx_buffers); - priv->rx_buffers = NULL; - - IPW_DEBUG_INFO("exit\n"); -} - -static int ipw2100_read_mac_address(struct ipw2100_priv *priv) -{ - u32 length = ETH_ALEN; - u8 addr[ETH_ALEN]; - - int err; - - err = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ADAPTER_MAC, addr, &length); - if (err) { - IPW_DEBUG_INFO("MAC address read failed\n"); - return -EIO; - } - - memcpy(priv->net_dev->dev_addr, addr, ETH_ALEN); - IPW_DEBUG_INFO("card MAC is %pM\n", priv->net_dev->dev_addr); - - return 0; -} - -/******************************************************************** - * - * Firmware Commands - * - ********************************************************************/ - -static int ipw2100_set_mac_address(struct ipw2100_priv *priv, int batch_mode) -{ - struct host_command cmd = { - .host_command = ADAPTER_ADDRESS, - .host_command_sequence = 0, - .host_command_length = ETH_ALEN - }; - int err; - - IPW_DEBUG_HC("SET_MAC_ADDRESS\n"); - - IPW_DEBUG_INFO("enter\n"); - - if (priv->config & CFG_CUSTOM_MAC) { - memcpy(cmd.host_command_parameters, priv->mac_addr, ETH_ALEN); - memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN); - } else - memcpy(cmd.host_command_parameters, priv->net_dev->dev_addr, - ETH_ALEN); - - err = ipw2100_hw_send_command(priv, &cmd); - - IPW_DEBUG_INFO("exit\n"); - return err; -} - -static int ipw2100_set_port_type(struct ipw2100_priv *priv, u32 port_type, - int batch_mode) -{ - struct host_command cmd = { - .host_command = PORT_TYPE, - .host_command_sequence = 0, - .host_command_length = sizeof(u32) - }; - int err; - - switch (port_type) { - case IW_MODE_INFRA: - cmd.host_command_parameters[0] = IPW_BSS; - break; - case IW_MODE_ADHOC: - cmd.host_command_parameters[0] = IPW_IBSS; - break; - } - - IPW_DEBUG_HC("PORT_TYPE: %s\n", - port_type == IPW_IBSS ? "Ad-Hoc" : "Managed"); - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) { - printk(KERN_ERR DRV_NAME - ": %s: Could not disable adapter %d\n", - priv->net_dev->name, err); - return err; - } - } - - /* send cmd to firmware */ - err = ipw2100_hw_send_command(priv, &cmd); - - if (!batch_mode) - ipw2100_enable_adapter(priv); - - return err; -} - -static int ipw2100_set_channel(struct ipw2100_priv *priv, u32 channel, - int batch_mode) -{ - struct host_command cmd = { - .host_command = CHANNEL, - .host_command_sequence = 0, - .host_command_length = sizeof(u32) - }; - int err; - - cmd.host_command_parameters[0] = channel; - - IPW_DEBUG_HC("CHANNEL: %d\n", channel); - - /* If BSS then we don't support channel selection */ - if (priv->ieee->iw_mode == IW_MODE_INFRA) - return 0; - - if ((channel != 0) && - ((channel < REG_MIN_CHANNEL) || (channel > REG_MAX_CHANNEL))) - return -EINVAL; - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) - return err; - } - - err = ipw2100_hw_send_command(priv, &cmd); - if (err) { - IPW_DEBUG_INFO("Failed to set channel to %d", channel); - return err; - } - - if (channel) - priv->config |= CFG_STATIC_CHANNEL; - else - priv->config &= ~CFG_STATIC_CHANNEL; - - priv->channel = channel; - - if (!batch_mode) { - err = ipw2100_enable_adapter(priv); - if (err) - return err; - } - - return 0; -} - -static int ipw2100_system_config(struct ipw2100_priv *priv, int batch_mode) -{ - struct host_command cmd = { - .host_command = SYSTEM_CONFIG, - .host_command_sequence = 0, - .host_command_length = 12, - }; - u32 ibss_mask, len = sizeof(u32); - int err; - - /* Set system configuration */ - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) - return err; - } - - if (priv->ieee->iw_mode == IW_MODE_ADHOC) - cmd.host_command_parameters[0] |= IPW_CFG_IBSS_AUTO_START; - - cmd.host_command_parameters[0] |= IPW_CFG_IBSS_MASK | - IPW_CFG_BSS_MASK | IPW_CFG_802_1x_ENABLE; - - if (!(priv->config & CFG_LONG_PREAMBLE)) - cmd.host_command_parameters[0] |= IPW_CFG_PREAMBLE_AUTO; - - err = ipw2100_get_ordinal(priv, - IPW_ORD_EEPROM_IBSS_11B_CHANNELS, - &ibss_mask, &len); - if (err) - ibss_mask = IPW_IBSS_11B_DEFAULT_MASK; - - cmd.host_command_parameters[1] = REG_CHANNEL_MASK; - cmd.host_command_parameters[2] = REG_CHANNEL_MASK & ibss_mask; - - /* 11b only */ - /*cmd.host_command_parameters[0] |= DIVERSITY_ANTENNA_A; */ - - err = ipw2100_hw_send_command(priv, &cmd); - if (err) - return err; - -/* If IPv6 is configured in the kernel then we don't want to filter out all - * of the multicast packets as IPv6 needs some. */ -#if !defined(CONFIG_IPV6) && !defined(CONFIG_IPV6_MODULE) - cmd.host_command = ADD_MULTICAST; - cmd.host_command_sequence = 0; - cmd.host_command_length = 0; - - ipw2100_hw_send_command(priv, &cmd); -#endif - if (!batch_mode) { - err = ipw2100_enable_adapter(priv); - if (err) - return err; - } - - return 0; -} - -static int ipw2100_set_tx_rates(struct ipw2100_priv *priv, u32 rate, - int batch_mode) -{ - struct host_command cmd = { - .host_command = BASIC_TX_RATES, - .host_command_sequence = 0, - .host_command_length = 4 - }; - int err; - - cmd.host_command_parameters[0] = rate & TX_RATE_MASK; - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) - return err; - } - - /* Set BASIC TX Rate first */ - ipw2100_hw_send_command(priv, &cmd); - - /* Set TX Rate */ - cmd.host_command = TX_RATES; - ipw2100_hw_send_command(priv, &cmd); - - /* Set MSDU TX Rate */ - cmd.host_command = MSDU_TX_RATES; - ipw2100_hw_send_command(priv, &cmd); - - if (!batch_mode) { - err = ipw2100_enable_adapter(priv); - if (err) - return err; - } - - priv->tx_rates = rate; - - return 0; -} - -static int ipw2100_set_power_mode(struct ipw2100_priv *priv, int power_level) -{ - struct host_command cmd = { - .host_command = POWER_MODE, - .host_command_sequence = 0, - .host_command_length = 4 - }; - int err; - - cmd.host_command_parameters[0] = power_level; - - err = ipw2100_hw_send_command(priv, &cmd); - if (err) - return err; - - if (power_level == IPW_POWER_MODE_CAM) - priv->power_mode = IPW_POWER_LEVEL(priv->power_mode); - else - priv->power_mode = IPW_POWER_ENABLED | power_level; - -#ifdef IPW2100_TX_POWER - if (priv->port_type == IBSS && priv->adhoc_power != DFTL_IBSS_TX_POWER) { - /* Set beacon interval */ - cmd.host_command = TX_POWER_INDEX; - cmd.host_command_parameters[0] = (u32) priv->adhoc_power; - - err = ipw2100_hw_send_command(priv, &cmd); - if (err) - return err; - } -#endif - - return 0; -} - -static int ipw2100_set_rts_threshold(struct ipw2100_priv *priv, u32 threshold) -{ - struct host_command cmd = { - .host_command = RTS_THRESHOLD, - .host_command_sequence = 0, - .host_command_length = 4 - }; - int err; - - if (threshold & RTS_DISABLED) - cmd.host_command_parameters[0] = MAX_RTS_THRESHOLD; - else - cmd.host_command_parameters[0] = threshold & ~RTS_DISABLED; - - err = ipw2100_hw_send_command(priv, &cmd); - if (err) - return err; - - priv->rts_threshold = threshold; - - return 0; -} - -#if 0 -int ipw2100_set_fragmentation_threshold(struct ipw2100_priv *priv, - u32 threshold, int batch_mode) -{ - struct host_command cmd = { - .host_command = FRAG_THRESHOLD, - .host_command_sequence = 0, - .host_command_length = 4, - .host_command_parameters[0] = 0, - }; - int err; - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) - return err; - } - - if (threshold == 0) - threshold = DEFAULT_FRAG_THRESHOLD; - else { - threshold = max(threshold, MIN_FRAG_THRESHOLD); - threshold = min(threshold, MAX_FRAG_THRESHOLD); - } - - cmd.host_command_parameters[0] = threshold; - - IPW_DEBUG_HC("FRAG_THRESHOLD: %u\n", threshold); - - err = ipw2100_hw_send_command(priv, &cmd); - - if (!batch_mode) - ipw2100_enable_adapter(priv); - - if (!err) - priv->frag_threshold = threshold; - - return err; -} -#endif - -static int ipw2100_set_short_retry(struct ipw2100_priv *priv, u32 retry) -{ - struct host_command cmd = { - .host_command = SHORT_RETRY_LIMIT, - .host_command_sequence = 0, - .host_command_length = 4 - }; - int err; - - cmd.host_command_parameters[0] = retry; - - err = ipw2100_hw_send_command(priv, &cmd); - if (err) - return err; - - priv->short_retry_limit = retry; - - return 0; -} - -static int ipw2100_set_long_retry(struct ipw2100_priv *priv, u32 retry) -{ - struct host_command cmd = { - .host_command = LONG_RETRY_LIMIT, - .host_command_sequence = 0, - .host_command_length = 4 - }; - int err; - - cmd.host_command_parameters[0] = retry; - - err = ipw2100_hw_send_command(priv, &cmd); - if (err) - return err; - - priv->long_retry_limit = retry; - - return 0; -} - -static int ipw2100_set_mandatory_bssid(struct ipw2100_priv *priv, u8 * bssid, - int batch_mode) -{ - struct host_command cmd = { - .host_command = MANDATORY_BSSID, - .host_command_sequence = 0, - .host_command_length = (bssid == NULL) ? 0 : ETH_ALEN - }; - int err; - -#ifdef CONFIG_IPW2100_DEBUG - if (bssid != NULL) - IPW_DEBUG_HC("MANDATORY_BSSID: %pM\n", bssid); - else - IPW_DEBUG_HC("MANDATORY_BSSID: \n"); -#endif - /* if BSSID is empty then we disable mandatory bssid mode */ - if (bssid != NULL) - memcpy(cmd.host_command_parameters, bssid, ETH_ALEN); - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) - return err; - } - - err = ipw2100_hw_send_command(priv, &cmd); - - if (!batch_mode) - ipw2100_enable_adapter(priv); - - return err; -} - -static int ipw2100_disassociate_bssid(struct ipw2100_priv *priv) -{ - struct host_command cmd = { - .host_command = DISASSOCIATION_BSSID, - .host_command_sequence = 0, - .host_command_length = ETH_ALEN - }; - int err; - int len; - - IPW_DEBUG_HC("DISASSOCIATION_BSSID\n"); - - len = ETH_ALEN; - /* The Firmware currently ignores the BSSID and just disassociates from - * the currently associated AP -- but in the off chance that a future - * firmware does use the BSSID provided here, we go ahead and try and - * set it to the currently associated AP's BSSID */ - memcpy(cmd.host_command_parameters, priv->bssid, ETH_ALEN); - - err = ipw2100_hw_send_command(priv, &cmd); - - return err; -} - -static int ipw2100_set_wpa_ie(struct ipw2100_priv *, - struct ipw2100_wpa_assoc_frame *, int) - __attribute__ ((unused)); - -static int ipw2100_set_wpa_ie(struct ipw2100_priv *priv, - struct ipw2100_wpa_assoc_frame *wpa_frame, - int batch_mode) -{ - struct host_command cmd = { - .host_command = SET_WPA_IE, - .host_command_sequence = 0, - .host_command_length = sizeof(struct ipw2100_wpa_assoc_frame), - }; - int err; - - IPW_DEBUG_HC("SET_WPA_IE\n"); - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) - return err; - } - - memcpy(cmd.host_command_parameters, wpa_frame, - sizeof(struct ipw2100_wpa_assoc_frame)); - - err = ipw2100_hw_send_command(priv, &cmd); - - if (!batch_mode) { - if (ipw2100_enable_adapter(priv)) - err = -EIO; - } - - return err; -} - -struct security_info_params { - u32 allowed_ciphers; - u16 version; - u8 auth_mode; - u8 replay_counters_number; - u8 unicast_using_group; -} __packed; - -static int ipw2100_set_security_information(struct ipw2100_priv *priv, - int auth_mode, - int security_level, - int unicast_using_group, - int batch_mode) -{ - struct host_command cmd = { - .host_command = SET_SECURITY_INFORMATION, - .host_command_sequence = 0, - .host_command_length = sizeof(struct security_info_params) - }; - struct security_info_params *security = - (struct security_info_params *)&cmd.host_command_parameters; - int err; - memset(security, 0, sizeof(*security)); - - /* If shared key AP authentication is turned on, then we need to - * configure the firmware to try and use it. - * - * Actual data encryption/decryption is handled by the host. */ - security->auth_mode = auth_mode; - security->unicast_using_group = unicast_using_group; - - switch (security_level) { - default: - case SEC_LEVEL_0: - security->allowed_ciphers = IPW_NONE_CIPHER; - break; - case SEC_LEVEL_1: - security->allowed_ciphers = IPW_WEP40_CIPHER | - IPW_WEP104_CIPHER; - break; - case SEC_LEVEL_2: - security->allowed_ciphers = IPW_WEP40_CIPHER | - IPW_WEP104_CIPHER | IPW_TKIP_CIPHER; - break; - case SEC_LEVEL_2_CKIP: - security->allowed_ciphers = IPW_WEP40_CIPHER | - IPW_WEP104_CIPHER | IPW_CKIP_CIPHER; - break; - case SEC_LEVEL_3: - security->allowed_ciphers = IPW_WEP40_CIPHER | - IPW_WEP104_CIPHER | IPW_TKIP_CIPHER | IPW_CCMP_CIPHER; - break; - } - - IPW_DEBUG_HC - ("SET_SECURITY_INFORMATION: auth:%d cipher:0x%02X (level %d)\n", - security->auth_mode, security->allowed_ciphers, security_level); - - security->replay_counters_number = 0; - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) - return err; - } - - err = ipw2100_hw_send_command(priv, &cmd); - - if (!batch_mode) - ipw2100_enable_adapter(priv); - - return err; -} - -static int ipw2100_set_tx_power(struct ipw2100_priv *priv, u32 tx_power) -{ - struct host_command cmd = { - .host_command = TX_POWER_INDEX, - .host_command_sequence = 0, - .host_command_length = 4 - }; - int err = 0; - u32 tmp = tx_power; - - if (tx_power != IPW_TX_POWER_DEFAULT) - tmp = (tx_power - IPW_TX_POWER_MIN_DBM) * 16 / - (IPW_TX_POWER_MAX_DBM - IPW_TX_POWER_MIN_DBM); - - cmd.host_command_parameters[0] = tmp; - - if (priv->ieee->iw_mode == IW_MODE_ADHOC) - err = ipw2100_hw_send_command(priv, &cmd); - if (!err) - priv->tx_power = tx_power; - - return 0; -} - -static int ipw2100_set_ibss_beacon_interval(struct ipw2100_priv *priv, - u32 interval, int batch_mode) -{ - struct host_command cmd = { - .host_command = BEACON_INTERVAL, - .host_command_sequence = 0, - .host_command_length = 4 - }; - int err; - - cmd.host_command_parameters[0] = interval; - - IPW_DEBUG_INFO("enter\n"); - - if (priv->ieee->iw_mode == IW_MODE_ADHOC) { - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) - return err; - } - - ipw2100_hw_send_command(priv, &cmd); - - if (!batch_mode) { - err = ipw2100_enable_adapter(priv); - if (err) - return err; - } - } - - IPW_DEBUG_INFO("exit\n"); - - return 0; -} - -static void ipw2100_queues_initialize(struct ipw2100_priv *priv) -{ - ipw2100_tx_initialize(priv); - ipw2100_rx_initialize(priv); - ipw2100_msg_initialize(priv); -} - -static void ipw2100_queues_free(struct ipw2100_priv *priv) -{ - ipw2100_tx_free(priv); - ipw2100_rx_free(priv); - ipw2100_msg_free(priv); -} - -static int ipw2100_queues_allocate(struct ipw2100_priv *priv) -{ - if (ipw2100_tx_allocate(priv) || - ipw2100_rx_allocate(priv) || ipw2100_msg_allocate(priv)) - goto fail; - - return 0; - - fail: - ipw2100_tx_free(priv); - ipw2100_rx_free(priv); - ipw2100_msg_free(priv); - return -ENOMEM; -} - -#define IPW_PRIVACY_CAPABLE 0x0008 - -static int ipw2100_set_wep_flags(struct ipw2100_priv *priv, u32 flags, - int batch_mode) -{ - struct host_command cmd = { - .host_command = WEP_FLAGS, - .host_command_sequence = 0, - .host_command_length = 4 - }; - int err; - - cmd.host_command_parameters[0] = flags; - - IPW_DEBUG_HC("WEP_FLAGS: flags = 0x%08X\n", flags); - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) { - printk(KERN_ERR DRV_NAME - ": %s: Could not disable adapter %d\n", - priv->net_dev->name, err); - return err; - } - } - - /* send cmd to firmware */ - err = ipw2100_hw_send_command(priv, &cmd); - - if (!batch_mode) - ipw2100_enable_adapter(priv); - - return err; -} - -struct ipw2100_wep_key { - u8 idx; - u8 len; - u8 key[13]; -}; - -/* Macros to ease up priting WEP keys */ -#define WEP_FMT_64 "%02X%02X%02X%02X-%02X" -#define WEP_FMT_128 "%02X%02X%02X%02X-%02X%02X%02X%02X-%02X%02X%02X" -#define WEP_STR_64(x) x[0],x[1],x[2],x[3],x[4] -#define WEP_STR_128(x) x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9],x[10] - -/** - * Set a the wep key - * - * @priv: struct to work on - * @idx: index of the key we want to set - * @key: ptr to the key data to set - * @len: length of the buffer at @key - * @batch_mode: FIXME perform the operation in batch mode, not - * disabling the device. - * - * @returns 0 if OK, < 0 errno code on error. - * - * Fill out a command structure with the new wep key, length an - * index and send it down the wire. - */ -static int ipw2100_set_key(struct ipw2100_priv *priv, - int idx, char *key, int len, int batch_mode) -{ - int keylen = len ? (len <= 5 ? 5 : 13) : 0; - struct host_command cmd = { - .host_command = WEP_KEY_INFO, - .host_command_sequence = 0, - .host_command_length = sizeof(struct ipw2100_wep_key), - }; - struct ipw2100_wep_key *wep_key = (void *)cmd.host_command_parameters; - int err; - - IPW_DEBUG_HC("WEP_KEY_INFO: index = %d, len = %d/%d\n", - idx, keylen, len); - - /* NOTE: We don't check cached values in case the firmware was reset - * or some other problem is occurring. If the user is setting the key, - * then we push the change */ - - wep_key->idx = idx; - wep_key->len = keylen; - - if (keylen) { - memcpy(wep_key->key, key, len); - memset(wep_key->key + len, 0, keylen - len); - } - - /* Will be optimized out on debug not being configured in */ - if (keylen == 0) - IPW_DEBUG_WEP("%s: Clearing key %d\n", - priv->net_dev->name, wep_key->idx); - else if (keylen == 5) - IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_64 "\n", - priv->net_dev->name, wep_key->idx, wep_key->len, - WEP_STR_64(wep_key->key)); - else - IPW_DEBUG_WEP("%s: idx: %d, len: %d key: " WEP_FMT_128 - "\n", - priv->net_dev->name, wep_key->idx, wep_key->len, - WEP_STR_128(wep_key->key)); - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - /* FIXME: IPG: shouldn't this prink be in _disable_adapter()? */ - if (err) { - printk(KERN_ERR DRV_NAME - ": %s: Could not disable adapter %d\n", - priv->net_dev->name, err); - return err; - } - } - - /* send cmd to firmware */ - err = ipw2100_hw_send_command(priv, &cmd); - - if (!batch_mode) { - int err2 = ipw2100_enable_adapter(priv); - if (err == 0) - err = err2; - } - return err; -} - -static int ipw2100_set_key_index(struct ipw2100_priv *priv, - int idx, int batch_mode) -{ - struct host_command cmd = { - .host_command = WEP_KEY_INDEX, - .host_command_sequence = 0, - .host_command_length = 4, - .host_command_parameters = {idx}, - }; - int err; - - IPW_DEBUG_HC("WEP_KEY_INDEX: index = %d\n", idx); - - if (idx < 0 || idx > 3) - return -EINVAL; - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) { - printk(KERN_ERR DRV_NAME - ": %s: Could not disable adapter %d\n", - priv->net_dev->name, err); - return err; - } - } - - /* send cmd to firmware */ - err = ipw2100_hw_send_command(priv, &cmd); - - if (!batch_mode) - ipw2100_enable_adapter(priv); - - return err; -} - -static int ipw2100_configure_security(struct ipw2100_priv *priv, int batch_mode) -{ - int i, err, auth_mode, sec_level, use_group; - - if (!(priv->status & STATUS_RUNNING)) - return 0; - - if (!batch_mode) { - err = ipw2100_disable_adapter(priv); - if (err) - return err; - } - - if (!priv->ieee->sec.enabled) { - err = - ipw2100_set_security_information(priv, IPW_AUTH_OPEN, - SEC_LEVEL_0, 0, 1); - } else { - auth_mode = IPW_AUTH_OPEN; - if (priv->ieee->sec.flags & SEC_AUTH_MODE) { - if (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY) - auth_mode = IPW_AUTH_SHARED; - else if (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP) - auth_mode = IPW_AUTH_LEAP_CISCO_ID; - } - - sec_level = SEC_LEVEL_0; - if (priv->ieee->sec.flags & SEC_LEVEL) - sec_level = priv->ieee->sec.level; - - use_group = 0; - if (priv->ieee->sec.flags & SEC_UNICAST_GROUP) - use_group = priv->ieee->sec.unicast_uses_group; - - err = - ipw2100_set_security_information(priv, auth_mode, sec_level, - use_group, 1); - } - - if (err) - goto exit; - - if (priv->ieee->sec.enabled) { - for (i = 0; i < 4; i++) { - if (!(priv->ieee->sec.flags & (1 << i))) { - memset(priv->ieee->sec.keys[i], 0, WEP_KEY_LEN); - priv->ieee->sec.key_sizes[i] = 0; - } else { - err = ipw2100_set_key(priv, i, - priv->ieee->sec.keys[i], - priv->ieee->sec. - key_sizes[i], 1); - if (err) - goto exit; - } - } - - ipw2100_set_key_index(priv, priv->ieee->crypt_info.tx_keyidx, 1); - } - - /* Always enable privacy so the Host can filter WEP packets if - * encrypted data is sent up */ - err = - ipw2100_set_wep_flags(priv, - priv->ieee->sec. - enabled ? IPW_PRIVACY_CAPABLE : 0, 1); - if (err) - goto exit; - - priv->status &= ~STATUS_SECURITY_UPDATED; - - exit: - if (!batch_mode) - ipw2100_enable_adapter(priv); - - return err; -} - -static void ipw2100_security_work(struct work_struct *work) -{ - struct ipw2100_priv *priv = - container_of(work, struct ipw2100_priv, security_work.work); - - /* If we happen to have reconnected before we get a chance to - * process this, then update the security settings--which causes - * a disassociation to occur */ - if (!(priv->status & STATUS_ASSOCIATED) && - priv->status & STATUS_SECURITY_UPDATED) - ipw2100_configure_security(priv, 0); -} - -static void shim__set_security(struct net_device *dev, - struct libipw_security *sec) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int i, force_update = 0; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) - goto done; - - for (i = 0; i < 4; i++) { - if (sec->flags & (1 << i)) { - priv->ieee->sec.key_sizes[i] = sec->key_sizes[i]; - if (sec->key_sizes[i] == 0) - priv->ieee->sec.flags &= ~(1 << i); - else - memcpy(priv->ieee->sec.keys[i], sec->keys[i], - sec->key_sizes[i]); - if (sec->level == SEC_LEVEL_1) { - priv->ieee->sec.flags |= (1 << i); - priv->status |= STATUS_SECURITY_UPDATED; - } else - priv->ieee->sec.flags &= ~(1 << i); - } - } - - if ((sec->flags & SEC_ACTIVE_KEY) && - priv->ieee->sec.active_key != sec->active_key) { - if (sec->active_key <= 3) { - priv->ieee->sec.active_key = sec->active_key; - priv->ieee->sec.flags |= SEC_ACTIVE_KEY; - } else - priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY; - - priv->status |= STATUS_SECURITY_UPDATED; - } - - if ((sec->flags & SEC_AUTH_MODE) && - (priv->ieee->sec.auth_mode != sec->auth_mode)) { - priv->ieee->sec.auth_mode = sec->auth_mode; - priv->ieee->sec.flags |= SEC_AUTH_MODE; - priv->status |= STATUS_SECURITY_UPDATED; - } - - if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) { - priv->ieee->sec.flags |= SEC_ENABLED; - priv->ieee->sec.enabled = sec->enabled; - priv->status |= STATUS_SECURITY_UPDATED; - force_update = 1; - } - - if (sec->flags & SEC_ENCRYPT) - priv->ieee->sec.encrypt = sec->encrypt; - - if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) { - priv->ieee->sec.level = sec->level; - priv->ieee->sec.flags |= SEC_LEVEL; - priv->status |= STATUS_SECURITY_UPDATED; - } - - IPW_DEBUG_WEP("Security flags: %c %c%c%c%c %c%c%c%c\n", - priv->ieee->sec.flags & (1 << 8) ? '1' : '0', - priv->ieee->sec.flags & (1 << 7) ? '1' : '0', - priv->ieee->sec.flags & (1 << 6) ? '1' : '0', - priv->ieee->sec.flags & (1 << 5) ? '1' : '0', - priv->ieee->sec.flags & (1 << 4) ? '1' : '0', - priv->ieee->sec.flags & (1 << 3) ? '1' : '0', - priv->ieee->sec.flags & (1 << 2) ? '1' : '0', - priv->ieee->sec.flags & (1 << 1) ? '1' : '0', - priv->ieee->sec.flags & (1 << 0) ? '1' : '0'); - -/* As a temporary work around to enable WPA until we figure out why - * wpa_supplicant toggles the security capability of the driver, which - * forces a disassocation with force_update... - * - * if (force_update || !(priv->status & STATUS_ASSOCIATED))*/ - if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) - ipw2100_configure_security(priv, 0); - done: - mutex_unlock(&priv->action_mutex); -} - -static int ipw2100_adapter_setup(struct ipw2100_priv *priv) -{ - int err; - int batch_mode = 1; - u8 *bssid; - - IPW_DEBUG_INFO("enter\n"); - - err = ipw2100_disable_adapter(priv); - if (err) - return err; -#ifdef CONFIG_IPW2100_MONITOR - if (priv->ieee->iw_mode == IW_MODE_MONITOR) { - err = ipw2100_set_channel(priv, priv->channel, batch_mode); - if (err) - return err; - - IPW_DEBUG_INFO("exit\n"); - - return 0; - } -#endif /* CONFIG_IPW2100_MONITOR */ - - err = ipw2100_read_mac_address(priv); - if (err) - return -EIO; - - err = ipw2100_set_mac_address(priv, batch_mode); - if (err) - return err; - - err = ipw2100_set_port_type(priv, priv->ieee->iw_mode, batch_mode); - if (err) - return err; - - if (priv->ieee->iw_mode == IW_MODE_ADHOC) { - err = ipw2100_set_channel(priv, priv->channel, batch_mode); - if (err) - return err; - } - - err = ipw2100_system_config(priv, batch_mode); - if (err) - return err; - - err = ipw2100_set_tx_rates(priv, priv->tx_rates, batch_mode); - if (err) - return err; - - /* Default to power mode OFF */ - err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM); - if (err) - return err; - - err = ipw2100_set_rts_threshold(priv, priv->rts_threshold); - if (err) - return err; - - if (priv->config & CFG_STATIC_BSSID) - bssid = priv->bssid; - else - bssid = NULL; - err = ipw2100_set_mandatory_bssid(priv, bssid, batch_mode); - if (err) - return err; - - if (priv->config & CFG_STATIC_ESSID) - err = ipw2100_set_essid(priv, priv->essid, priv->essid_len, - batch_mode); - else - err = ipw2100_set_essid(priv, NULL, 0, batch_mode); - if (err) - return err; - - err = ipw2100_configure_security(priv, batch_mode); - if (err) - return err; - - if (priv->ieee->iw_mode == IW_MODE_ADHOC) { - err = - ipw2100_set_ibss_beacon_interval(priv, - priv->beacon_interval, - batch_mode); - if (err) - return err; - - err = ipw2100_set_tx_power(priv, priv->tx_power); - if (err) - return err; - } - - /* - err = ipw2100_set_fragmentation_threshold( - priv, priv->frag_threshold, batch_mode); - if (err) - return err; - */ - - IPW_DEBUG_INFO("exit\n"); - - return 0; -} - -/************************************************************************* - * - * EXTERNALLY CALLED METHODS - * - *************************************************************************/ - -/* This method is called by the network layer -- not to be confused with - * ipw2100_set_mac_address() declared above called by this driver (and this - * method as well) to talk to the firmware */ -static int ipw2100_set_address(struct net_device *dev, void *p) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - struct sockaddr *addr = p; - int err = 0; - - if (!is_valid_ether_addr(addr->sa_data)) - return -EADDRNOTAVAIL; - - mutex_lock(&priv->action_mutex); - - priv->config |= CFG_CUSTOM_MAC; - memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN); - - err = ipw2100_set_mac_address(priv, 0); - if (err) - goto done; - - priv->reset_backoff = 0; - mutex_unlock(&priv->action_mutex); - ipw2100_reset_adapter(&priv->reset_work.work); - return 0; - - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_open(struct net_device *dev) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - unsigned long flags; - IPW_DEBUG_INFO("dev->open\n"); - - spin_lock_irqsave(&priv->low_lock, flags); - if (priv->status & STATUS_ASSOCIATED) { - netif_carrier_on(dev); - netif_start_queue(dev); - } - spin_unlock_irqrestore(&priv->low_lock, flags); - - return 0; -} - -static int ipw2100_close(struct net_device *dev) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - unsigned long flags; - struct list_head *element; - struct ipw2100_tx_packet *packet; - - IPW_DEBUG_INFO("enter\n"); - - spin_lock_irqsave(&priv->low_lock, flags); - - if (priv->status & STATUS_ASSOCIATED) - netif_carrier_off(dev); - netif_stop_queue(dev); - - /* Flush the TX queue ... */ - while (!list_empty(&priv->tx_pend_list)) { - element = priv->tx_pend_list.next; - packet = list_entry(element, struct ipw2100_tx_packet, list); - - list_del(element); - DEC_STAT(&priv->tx_pend_stat); - - libipw_txb_free(packet->info.d_struct.txb); - packet->info.d_struct.txb = NULL; - - list_add_tail(element, &priv->tx_free_list); - INC_STAT(&priv->tx_free_stat); - } - spin_unlock_irqrestore(&priv->low_lock, flags); - - IPW_DEBUG_INFO("exit\n"); - - return 0; -} - -/* - * TODO: Fix this function... its just wrong - */ -static void ipw2100_tx_timeout(struct net_device *dev) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - - dev->stats.tx_errors++; - -#ifdef CONFIG_IPW2100_MONITOR - if (priv->ieee->iw_mode == IW_MODE_MONITOR) - return; -#endif - - IPW_DEBUG_INFO("%s: TX timed out. Scheduling firmware restart.\n", - dev->name); - schedule_reset(priv); -} - -static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value) -{ - /* This is called when wpa_supplicant loads and closes the driver - * interface. */ - priv->ieee->wpa_enabled = value; - return 0; -} - -static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value) -{ - - struct libipw_device *ieee = priv->ieee; - struct libipw_security sec = { - .flags = SEC_AUTH_MODE, - }; - int ret = 0; - - if (value & IW_AUTH_ALG_SHARED_KEY) { - sec.auth_mode = WLAN_AUTH_SHARED_KEY; - ieee->open_wep = 0; - } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) { - sec.auth_mode = WLAN_AUTH_OPEN; - ieee->open_wep = 1; - } else if (value & IW_AUTH_ALG_LEAP) { - sec.auth_mode = WLAN_AUTH_LEAP; - ieee->open_wep = 1; - } else - return -EINVAL; - - if (ieee->set_security) - ieee->set_security(ieee->dev, &sec); - else - ret = -EOPNOTSUPP; - - return ret; -} - -static void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv, - char *wpa_ie, int wpa_ie_len) -{ - - struct ipw2100_wpa_assoc_frame frame; - - frame.fixed_ie_mask = 0; - - /* copy WPA IE */ - memcpy(frame.var_ie, wpa_ie, wpa_ie_len); - frame.var_ie_len = wpa_ie_len; - - /* make sure WPA is enabled */ - ipw2100_wpa_enable(priv, 1); - ipw2100_set_wpa_ie(priv, &frame, 0); -} - -static void ipw_ethtool_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - char fw_ver[64], ucode_ver[64]; - - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - - ipw2100_get_fwversion(priv, fw_ver, sizeof(fw_ver)); - ipw2100_get_ucodeversion(priv, ucode_ver, sizeof(ucode_ver)); - - snprintf(info->fw_version, sizeof(info->fw_version), "%s:%d:%s", - fw_ver, priv->eeprom_version, ucode_ver); - - strlcpy(info->bus_info, pci_name(priv->pci_dev), - sizeof(info->bus_info)); -} - -static u32 ipw2100_ethtool_get_link(struct net_device *dev) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - return (priv->status & STATUS_ASSOCIATED) ? 1 : 0; -} - -static const struct ethtool_ops ipw2100_ethtool_ops = { - .get_link = ipw2100_ethtool_get_link, - .get_drvinfo = ipw_ethtool_get_drvinfo, -}; - -static void ipw2100_hang_check(struct work_struct *work) -{ - struct ipw2100_priv *priv = - container_of(work, struct ipw2100_priv, hang_check.work); - unsigned long flags; - u32 rtc = 0xa5a5a5a5; - u32 len = sizeof(rtc); - int restart = 0; - - spin_lock_irqsave(&priv->low_lock, flags); - - if (priv->fatal_error != 0) { - /* If fatal_error is set then we need to restart */ - IPW_DEBUG_INFO("%s: Hardware fatal error detected.\n", - priv->net_dev->name); - - restart = 1; - } else if (ipw2100_get_ordinal(priv, IPW_ORD_RTC_TIME, &rtc, &len) || - (rtc == priv->last_rtc)) { - /* Check if firmware is hung */ - IPW_DEBUG_INFO("%s: Firmware RTC stalled.\n", - priv->net_dev->name); - - restart = 1; - } - - if (restart) { - /* Kill timer */ - priv->stop_hang_check = 1; - priv->hangs++; - - /* Restart the NIC */ - schedule_reset(priv); - } - - priv->last_rtc = rtc; - - if (!priv->stop_hang_check) - schedule_delayed_work(&priv->hang_check, HZ / 2); - - spin_unlock_irqrestore(&priv->low_lock, flags); -} - -static void ipw2100_rf_kill(struct work_struct *work) -{ - struct ipw2100_priv *priv = - container_of(work, struct ipw2100_priv, rf_kill.work); - unsigned long flags; - - spin_lock_irqsave(&priv->low_lock, flags); - - if (rf_kill_active(priv)) { - IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n"); - if (!priv->stop_rf_kill) - schedule_delayed_work(&priv->rf_kill, - round_jiffies_relative(HZ)); - goto exit_unlock; - } - - /* RF Kill is now disabled, so bring the device back up */ - - if (!(priv->status & STATUS_RF_KILL_MASK)) { - IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting " - "device\n"); - schedule_reset(priv); - } else - IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still " - "enabled\n"); - - exit_unlock: - spin_unlock_irqrestore(&priv->low_lock, flags); -} - -static void ipw2100_irq_tasklet(struct ipw2100_priv *priv); - -static const struct net_device_ops ipw2100_netdev_ops = { - .ndo_open = ipw2100_open, - .ndo_stop = ipw2100_close, - .ndo_start_xmit = libipw_xmit, - .ndo_change_mtu = libipw_change_mtu, - .ndo_tx_timeout = ipw2100_tx_timeout, - .ndo_set_mac_address = ipw2100_set_address, - .ndo_validate_addr = eth_validate_addr, -}; - -/* Look into using netdev destructor to shutdown libipw? */ - -static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, - void __iomem * ioaddr) -{ - struct ipw2100_priv *priv; - struct net_device *dev; - - dev = alloc_libipw(sizeof(struct ipw2100_priv), 0); - if (!dev) - return NULL; - priv = libipw_priv(dev); - priv->ieee = netdev_priv(dev); - priv->pci_dev = pci_dev; - priv->net_dev = dev; - priv->ioaddr = ioaddr; - - priv->ieee->hard_start_xmit = ipw2100_tx; - priv->ieee->set_security = shim__set_security; - - priv->ieee->perfect_rssi = -20; - priv->ieee->worst_rssi = -85; - - dev->netdev_ops = &ipw2100_netdev_ops; - dev->ethtool_ops = &ipw2100_ethtool_ops; - dev->wireless_handlers = &ipw2100_wx_handler_def; - priv->wireless_data.libipw = priv->ieee; - dev->wireless_data = &priv->wireless_data; - dev->watchdog_timeo = 3 * HZ; - dev->irq = 0; - - /* NOTE: We don't use the wireless_handlers hook - * in dev as the system will start throwing WX requests - * to us before we're actually initialized and it just - * ends up causing problems. So, we just handle - * the WX extensions through the ipw2100_ioctl interface */ - - /* memset() puts everything to 0, so we only have explicitly set - * those values that need to be something else */ - - /* If power management is turned on, default to AUTO mode */ - priv->power_mode = IPW_POWER_AUTO; - -#ifdef CONFIG_IPW2100_MONITOR - priv->config |= CFG_CRC_CHECK; -#endif - priv->ieee->wpa_enabled = 0; - priv->ieee->drop_unencrypted = 0; - priv->ieee->privacy_invoked = 0; - priv->ieee->ieee802_1x = 1; - - /* Set module parameters */ - switch (network_mode) { - case 1: - priv->ieee->iw_mode = IW_MODE_ADHOC; - break; -#ifdef CONFIG_IPW2100_MONITOR - case 2: - priv->ieee->iw_mode = IW_MODE_MONITOR; - break; -#endif - default: - case 0: - priv->ieee->iw_mode = IW_MODE_INFRA; - break; - } - - if (disable == 1) - priv->status |= STATUS_RF_KILL_SW; - - if (channel != 0 && - ((channel >= REG_MIN_CHANNEL) && (channel <= REG_MAX_CHANNEL))) { - priv->config |= CFG_STATIC_CHANNEL; - priv->channel = channel; - } - - if (associate) - priv->config |= CFG_ASSOCIATE; - - priv->beacon_interval = DEFAULT_BEACON_INTERVAL; - priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT; - priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT; - priv->rts_threshold = DEFAULT_RTS_THRESHOLD | RTS_DISABLED; - priv->frag_threshold = DEFAULT_FTS | FRAG_DISABLED; - priv->tx_power = IPW_TX_POWER_DEFAULT; - priv->tx_rates = DEFAULT_TX_RATES; - - strcpy(priv->nick, "ipw2100"); - - spin_lock_init(&priv->low_lock); - mutex_init(&priv->action_mutex); - mutex_init(&priv->adapter_mutex); - - init_waitqueue_head(&priv->wait_command_queue); - - netif_carrier_off(dev); - - INIT_LIST_HEAD(&priv->msg_free_list); - INIT_LIST_HEAD(&priv->msg_pend_list); - INIT_STAT(&priv->msg_free_stat); - INIT_STAT(&priv->msg_pend_stat); - - INIT_LIST_HEAD(&priv->tx_free_list); - INIT_LIST_HEAD(&priv->tx_pend_list); - INIT_STAT(&priv->tx_free_stat); - INIT_STAT(&priv->tx_pend_stat); - - INIT_LIST_HEAD(&priv->fw_pend_list); - INIT_STAT(&priv->fw_pend_stat); - - INIT_DELAYED_WORK(&priv->reset_work, ipw2100_reset_adapter); - INIT_DELAYED_WORK(&priv->security_work, ipw2100_security_work); - INIT_DELAYED_WORK(&priv->wx_event_work, ipw2100_wx_event_work); - INIT_DELAYED_WORK(&priv->hang_check, ipw2100_hang_check); - INIT_DELAYED_WORK(&priv->rf_kill, ipw2100_rf_kill); - INIT_DELAYED_WORK(&priv->scan_event, ipw2100_scan_event); - - tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) - ipw2100_irq_tasklet, (unsigned long)priv); - - /* NOTE: We do not start the deferred work for status checks yet */ - priv->stop_rf_kill = 1; - priv->stop_hang_check = 1; - - return dev; -} - -static int ipw2100_pci_init_one(struct pci_dev *pci_dev, - const struct pci_device_id *ent) -{ - void __iomem *ioaddr; - struct net_device *dev = NULL; - struct ipw2100_priv *priv = NULL; - int err = 0; - int registered = 0; - u32 val; - - IPW_DEBUG_INFO("enter\n"); - - if (!(pci_resource_flags(pci_dev, 0) & IORESOURCE_MEM)) { - IPW_DEBUG_INFO("weird - resource type is not memory\n"); - err = -ENODEV; - goto out; - } - - ioaddr = pci_iomap(pci_dev, 0, 0); - if (!ioaddr) { - printk(KERN_WARNING DRV_NAME - "Error calling ioremap_nocache.\n"); - err = -EIO; - goto fail; - } - - /* allocate and initialize our net_device */ - dev = ipw2100_alloc_device(pci_dev, ioaddr); - if (!dev) { - printk(KERN_WARNING DRV_NAME - "Error calling ipw2100_alloc_device.\n"); - err = -ENOMEM; - goto fail; - } - - /* set up PCI mappings for device */ - err = pci_enable_device(pci_dev); - if (err) { - printk(KERN_WARNING DRV_NAME - "Error calling pci_enable_device.\n"); - return err; - } - - priv = libipw_priv(dev); - - pci_set_master(pci_dev); - pci_set_drvdata(pci_dev, priv); - - err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); - if (err) { - printk(KERN_WARNING DRV_NAME - "Error calling pci_set_dma_mask.\n"); - pci_disable_device(pci_dev); - return err; - } - - err = pci_request_regions(pci_dev, DRV_NAME); - if (err) { - printk(KERN_WARNING DRV_NAME - "Error calling pci_request_regions.\n"); - pci_disable_device(pci_dev); - return err; - } - - /* We disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state */ - pci_read_config_dword(pci_dev, 0x40, &val); - if ((val & 0x0000ff00) != 0) - pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff); - - if (!ipw2100_hw_is_adapter_in_system(dev)) { - printk(KERN_WARNING DRV_NAME - "Device not found via register read.\n"); - err = -ENODEV; - goto fail; - } - - SET_NETDEV_DEV(dev, &pci_dev->dev); - - /* Force interrupts to be shut off on the device */ - priv->status |= STATUS_INT_ENABLED; - ipw2100_disable_interrupts(priv); - - /* Allocate and initialize the Tx/Rx queues and lists */ - if (ipw2100_queues_allocate(priv)) { - printk(KERN_WARNING DRV_NAME - "Error calling ipw2100_queues_allocate.\n"); - err = -ENOMEM; - goto fail; - } - ipw2100_queues_initialize(priv); - - err = request_irq(pci_dev->irq, - ipw2100_interrupt, IRQF_SHARED, dev->name, priv); - if (err) { - printk(KERN_WARNING DRV_NAME - "Error calling request_irq: %d.\n", pci_dev->irq); - goto fail; - } - dev->irq = pci_dev->irq; - - IPW_DEBUG_INFO("Attempting to register device...\n"); - - printk(KERN_INFO DRV_NAME - ": Detected Intel PRO/Wireless 2100 Network Connection\n"); - - err = ipw2100_up(priv, 1); - if (err) - goto fail; - - err = ipw2100_wdev_init(dev); - if (err) - goto fail; - registered = 1; - - /* Bring up the interface. Pre 0.46, after we registered the - * network device we would call ipw2100_up. This introduced a race - * condition with newer hotplug configurations (network was coming - * up and making calls before the device was initialized). - */ - err = register_netdev(dev); - if (err) { - printk(KERN_WARNING DRV_NAME - "Error calling register_netdev.\n"); - goto fail; - } - registered = 2; - - mutex_lock(&priv->action_mutex); - - IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev)); - - /* perform this after register_netdev so that dev->name is set */ - err = sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group); - if (err) - goto fail_unlock; - - /* If the RF Kill switch is disabled, go ahead and complete the - * startup sequence */ - if (!(priv->status & STATUS_RF_KILL_MASK)) { - /* Enable the adapter - sends HOST_COMPLETE */ - if (ipw2100_enable_adapter(priv)) { - printk(KERN_WARNING DRV_NAME - ": %s: failed in call to enable adapter.\n", - priv->net_dev->name); - ipw2100_hw_stop_adapter(priv); - err = -EIO; - goto fail_unlock; - } - - /* Start a scan . . . */ - ipw2100_set_scan_options(priv); - ipw2100_start_scan(priv); - } - - IPW_DEBUG_INFO("exit\n"); - - priv->status |= STATUS_INITIALIZED; - - mutex_unlock(&priv->action_mutex); -out: - return err; - - fail_unlock: - mutex_unlock(&priv->action_mutex); - fail: - if (dev) { - if (registered >= 2) - unregister_netdev(dev); - - if (registered) { - wiphy_unregister(priv->ieee->wdev.wiphy); - kfree(priv->ieee->bg_band.channels); - } - - ipw2100_hw_stop_adapter(priv); - - ipw2100_disable_interrupts(priv); - - if (dev->irq) - free_irq(dev->irq, priv); - - ipw2100_kill_works(priv); - - /* These are safe to call even if they weren't allocated */ - ipw2100_queues_free(priv); - sysfs_remove_group(&pci_dev->dev.kobj, - &ipw2100_attribute_group); - - free_libipw(dev, 0); - } - - pci_iounmap(pci_dev, ioaddr); - - pci_release_regions(pci_dev); - pci_disable_device(pci_dev); - goto out; -} - -static void ipw2100_pci_remove_one(struct pci_dev *pci_dev) -{ - struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); - struct net_device *dev = priv->net_dev; - - mutex_lock(&priv->action_mutex); - - priv->status &= ~STATUS_INITIALIZED; - - sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group); - -#ifdef CONFIG_PM - if (ipw2100_firmware.version) - ipw2100_release_firmware(priv, &ipw2100_firmware); -#endif - /* Take down the hardware */ - ipw2100_down(priv); - - /* Release the mutex so that the network subsystem can - * complete any needed calls into the driver... */ - mutex_unlock(&priv->action_mutex); - - /* Unregister the device first - this results in close() - * being called if the device is open. If we free storage - * first, then close() will crash. - * FIXME: remove the comment above. */ - unregister_netdev(dev); - - ipw2100_kill_works(priv); - - ipw2100_queues_free(priv); - - /* Free potential debugging firmware snapshot */ - ipw2100_snapshot_free(priv); - - free_irq(dev->irq, priv); - - pci_iounmap(pci_dev, priv->ioaddr); - - /* wiphy_unregister needs to be here, before free_libipw */ - wiphy_unregister(priv->ieee->wdev.wiphy); - kfree(priv->ieee->bg_band.channels); - free_libipw(dev, 0); - - pci_release_regions(pci_dev); - pci_disable_device(pci_dev); - - IPW_DEBUG_INFO("exit\n"); -} - -#ifdef CONFIG_PM -static int ipw2100_suspend(struct pci_dev *pci_dev, pm_message_t state) -{ - struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); - struct net_device *dev = priv->net_dev; - - IPW_DEBUG_INFO("%s: Going into suspend...\n", dev->name); - - mutex_lock(&priv->action_mutex); - if (priv->status & STATUS_INITIALIZED) { - /* Take down the device; powers it off, etc. */ - ipw2100_down(priv); - } - - /* Remove the PRESENT state of the device */ - netif_device_detach(dev); - - pci_save_state(pci_dev); - pci_disable_device(pci_dev); - pci_set_power_state(pci_dev, PCI_D3hot); - - priv->suspend_at = get_seconds(); - - mutex_unlock(&priv->action_mutex); - - return 0; -} - -static int ipw2100_resume(struct pci_dev *pci_dev) -{ - struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); - struct net_device *dev = priv->net_dev; - int err; - u32 val; - - if (IPW2100_PM_DISABLED) - return 0; - - mutex_lock(&priv->action_mutex); - - IPW_DEBUG_INFO("%s: Coming out of suspend...\n", dev->name); - - pci_set_power_state(pci_dev, PCI_D0); - err = pci_enable_device(pci_dev); - if (err) { - printk(KERN_ERR "%s: pci_enable_device failed on resume\n", - dev->name); - mutex_unlock(&priv->action_mutex); - return err; - } - pci_restore_state(pci_dev); - - /* - * Suspend/Resume resets the PCI configuration space, so we have to - * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries - * from interfering with C3 CPU state. pci_restore_state won't help - * here since it only restores the first 64 bytes pci config header. - */ - pci_read_config_dword(pci_dev, 0x40, &val); - if ((val & 0x0000ff00) != 0) - pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff); - - /* Set the device back into the PRESENT state; this will also wake - * the queue of needed */ - netif_device_attach(dev); - - priv->suspend_time = get_seconds() - priv->suspend_at; - - /* Bring the device back up */ - if (!(priv->status & STATUS_RF_KILL_SW)) - ipw2100_up(priv, 0); - - mutex_unlock(&priv->action_mutex); - - return 0; -} -#endif - -static void ipw2100_shutdown(struct pci_dev *pci_dev) -{ - struct ipw2100_priv *priv = pci_get_drvdata(pci_dev); - - /* Take down the device; powers it off, etc. */ - ipw2100_down(priv); - - pci_disable_device(pci_dev); -} - -#define IPW2100_DEV_ID(x) { PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, x } - -static const struct pci_device_id ipw2100_pci_id_table[] = { - IPW2100_DEV_ID(0x2520), /* IN 2100A mPCI 3A */ - IPW2100_DEV_ID(0x2521), /* IN 2100A mPCI 3B */ - IPW2100_DEV_ID(0x2524), /* IN 2100A mPCI 3B */ - IPW2100_DEV_ID(0x2525), /* IN 2100A mPCI 3B */ - IPW2100_DEV_ID(0x2526), /* IN 2100A mPCI Gen A3 */ - IPW2100_DEV_ID(0x2522), /* IN 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2523), /* IN 2100 mPCI 3A */ - IPW2100_DEV_ID(0x2527), /* IN 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2528), /* IN 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2529), /* IN 2100 mPCI 3B */ - IPW2100_DEV_ID(0x252B), /* IN 2100 mPCI 3A */ - IPW2100_DEV_ID(0x252C), /* IN 2100 mPCI 3A */ - IPW2100_DEV_ID(0x252D), /* IN 2100 mPCI 3A */ - - IPW2100_DEV_ID(0x2550), /* IB 2100A mPCI 3B */ - IPW2100_DEV_ID(0x2551), /* IB 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2553), /* IB 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2554), /* IB 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2555), /* IB 2100 mPCI 3B */ - - IPW2100_DEV_ID(0x2560), /* DE 2100A mPCI 3A */ - IPW2100_DEV_ID(0x2562), /* DE 2100A mPCI 3A */ - IPW2100_DEV_ID(0x2563), /* DE 2100A mPCI 3A */ - IPW2100_DEV_ID(0x2561), /* DE 2100 mPCI 3A */ - IPW2100_DEV_ID(0x2565), /* DE 2100 mPCI 3A */ - IPW2100_DEV_ID(0x2566), /* DE 2100 mPCI 3A */ - IPW2100_DEV_ID(0x2567), /* DE 2100 mPCI 3A */ - - IPW2100_DEV_ID(0x2570), /* GA 2100 mPCI 3B */ - - IPW2100_DEV_ID(0x2580), /* TO 2100A mPCI 3B */ - IPW2100_DEV_ID(0x2582), /* TO 2100A mPCI 3B */ - IPW2100_DEV_ID(0x2583), /* TO 2100A mPCI 3B */ - IPW2100_DEV_ID(0x2581), /* TO 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2585), /* TO 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2586), /* TO 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2587), /* TO 2100 mPCI 3B */ - - IPW2100_DEV_ID(0x2590), /* SO 2100A mPCI 3B */ - IPW2100_DEV_ID(0x2592), /* SO 2100A mPCI 3B */ - IPW2100_DEV_ID(0x2591), /* SO 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2593), /* SO 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2596), /* SO 2100 mPCI 3B */ - IPW2100_DEV_ID(0x2598), /* SO 2100 mPCI 3B */ - - IPW2100_DEV_ID(0x25A0), /* HP 2100 mPCI 3B */ - {0,}, -}; - -MODULE_DEVICE_TABLE(pci, ipw2100_pci_id_table); - -static struct pci_driver ipw2100_pci_driver = { - .name = DRV_NAME, - .id_table = ipw2100_pci_id_table, - .probe = ipw2100_pci_init_one, - .remove = ipw2100_pci_remove_one, -#ifdef CONFIG_PM - .suspend = ipw2100_suspend, - .resume = ipw2100_resume, -#endif - .shutdown = ipw2100_shutdown, -}; - -/** - * Initialize the ipw2100 driver/module - * - * @returns 0 if ok, < 0 errno node con error. - * - * Note: we cannot init the /proc stuff until the PCI driver is there, - * or we risk an unlikely race condition on someone accessing - * uninitialized data in the PCI dev struct through /proc. - */ -static int __init ipw2100_init(void) -{ - int ret; - - printk(KERN_INFO DRV_NAME ": %s, %s\n", DRV_DESCRIPTION, DRV_VERSION); - printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT); - - pm_qos_add_request(&ipw2100_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); - - ret = pci_register_driver(&ipw2100_pci_driver); - if (ret) - goto out; - -#ifdef CONFIG_IPW2100_DEBUG - ipw2100_debug_level = debug; - ret = driver_create_file(&ipw2100_pci_driver.driver, - &driver_attr_debug_level); -#endif - -out: - return ret; -} - -/** - * Cleanup ipw2100 driver registration - */ -static void __exit ipw2100_exit(void) -{ - /* FIXME: IPG: check that we have no instances of the devices open */ -#ifdef CONFIG_IPW2100_DEBUG - driver_remove_file(&ipw2100_pci_driver.driver, - &driver_attr_debug_level); -#endif - pci_unregister_driver(&ipw2100_pci_driver); - pm_qos_remove_request(&ipw2100_pm_qos_req); -} - -module_init(ipw2100_init); -module_exit(ipw2100_exit); - -static int ipw2100_wx_get_name(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - if (!(priv->status & STATUS_ASSOCIATED)) - strcpy(wrqu->name, "unassociated"); - else - snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b"); - - IPW_DEBUG_WX("Name: %s\n", wrqu->name); - return 0; -} - -static int ipw2100_wx_set_freq(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - struct iw_freq *fwrq = &wrqu->freq; - int err = 0; - - if (priv->ieee->iw_mode == IW_MODE_INFRA) - return -EOPNOTSUPP; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - /* if setting by freq convert to channel */ - if (fwrq->e == 1) { - if ((fwrq->m >= (int)2.412e8 && fwrq->m <= (int)2.487e8)) { - int f = fwrq->m / 100000; - int c = 0; - - while ((c < REG_MAX_CHANNEL) && - (f != ipw2100_frequencies[c])) - c++; - - /* hack to fall through */ - fwrq->e = 0; - fwrq->m = c + 1; - } - } - - if (fwrq->e > 0 || fwrq->m > 1000) { - err = -EOPNOTSUPP; - goto done; - } else { /* Set the channel */ - IPW_DEBUG_WX("SET Freq/Channel -> %d\n", fwrq->m); - err = ipw2100_set_channel(priv, fwrq->m, 0); - } - - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_get_freq(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - wrqu->freq.e = 0; - - /* If we are associated, trying to associate, or have a statically - * configured CHANNEL then return that; otherwise return ANY */ - if (priv->config & CFG_STATIC_CHANNEL || - priv->status & STATUS_ASSOCIATED) - wrqu->freq.m = priv->channel; - else - wrqu->freq.m = 0; - - IPW_DEBUG_WX("GET Freq/Channel -> %d\n", priv->channel); - return 0; - -} - -static int ipw2100_wx_set_mode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int err = 0; - - IPW_DEBUG_WX("SET Mode -> %d\n", wrqu->mode); - - if (wrqu->mode == priv->ieee->iw_mode) - return 0; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - switch (wrqu->mode) { -#ifdef CONFIG_IPW2100_MONITOR - case IW_MODE_MONITOR: - err = ipw2100_switch_mode(priv, IW_MODE_MONITOR); - break; -#endif /* CONFIG_IPW2100_MONITOR */ - case IW_MODE_ADHOC: - err = ipw2100_switch_mode(priv, IW_MODE_ADHOC); - break; - case IW_MODE_INFRA: - case IW_MODE_AUTO: - default: - err = ipw2100_switch_mode(priv, IW_MODE_INFRA); - break; - } - - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_get_mode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - wrqu->mode = priv->ieee->iw_mode; - IPW_DEBUG_WX("GET Mode -> %d\n", wrqu->mode); - - return 0; -} - -#define POWER_MODES 5 - -/* Values are in microsecond */ -static const s32 timeout_duration[POWER_MODES] = { - 350000, - 250000, - 75000, - 37000, - 25000, -}; - -static const s32 period_duration[POWER_MODES] = { - 400000, - 700000, - 1000000, - 1000000, - 1000000 -}; - -static int ipw2100_wx_get_range(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - struct iw_range *range = (struct iw_range *)extra; - u16 val; - int i, level; - - wrqu->data.length = sizeof(*range); - memset(range, 0, sizeof(*range)); - - /* Let's try to keep this struct in the same order as in - * linux/include/wireless.h - */ - - /* TODO: See what values we can set, and remove the ones we can't - * set, or fill them with some default data. - */ - - /* ~5 Mb/s real (802.11b) */ - range->throughput = 5 * 1000 * 1000; - -// range->sensitivity; /* signal level threshold range */ - - range->max_qual.qual = 100; - /* TODO: Find real max RSSI and stick here */ - range->max_qual.level = 0; - range->max_qual.noise = 0; - range->max_qual.updated = 7; /* Updated all three */ - - range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */ - /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ - range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM; - range->avg_qual.noise = 0; - range->avg_qual.updated = 7; /* Updated all three */ - - range->num_bitrates = RATE_COUNT; - - for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) { - range->bitrate[i] = ipw2100_bg_rates[i].bitrate * 100 * 1000; - } - - range->min_rts = MIN_RTS_THRESHOLD; - range->max_rts = MAX_RTS_THRESHOLD; - range->min_frag = MIN_FRAG_THRESHOLD; - range->max_frag = MAX_FRAG_THRESHOLD; - - range->min_pmp = period_duration[0]; /* Minimal PM period */ - range->max_pmp = period_duration[POWER_MODES - 1]; /* Maximal PM period */ - range->min_pmt = timeout_duration[POWER_MODES - 1]; /* Minimal PM timeout */ - range->max_pmt = timeout_duration[0]; /* Maximal PM timeout */ - - /* How to decode max/min PM period */ - range->pmp_flags = IW_POWER_PERIOD; - /* How to decode max/min PM period */ - range->pmt_flags = IW_POWER_TIMEOUT; - /* What PM options are supported */ - range->pm_capa = IW_POWER_TIMEOUT | IW_POWER_PERIOD; - - range->encoding_size[0] = 5; - range->encoding_size[1] = 13; /* Different token sizes */ - range->num_encoding_sizes = 2; /* Number of entry in the list */ - range->max_encoding_tokens = WEP_KEYS; /* Max number of tokens */ -// range->encoding_login_index; /* token index for login token */ - - if (priv->ieee->iw_mode == IW_MODE_ADHOC) { - range->txpower_capa = IW_TXPOW_DBM; - range->num_txpower = IW_MAX_TXPOWER; - for (i = 0, level = (IPW_TX_POWER_MAX_DBM * 16); - i < IW_MAX_TXPOWER; - i++, level -= - ((IPW_TX_POWER_MAX_DBM - - IPW_TX_POWER_MIN_DBM) * 16) / (IW_MAX_TXPOWER - 1)) - range->txpower[i] = level / 16; - } else { - range->txpower_capa = 0; - range->num_txpower = 0; - } - - /* Set the Wireless Extension versions */ - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 18; - -// range->retry_capa; /* What retry options are supported */ -// range->retry_flags; /* How to decode max/min retry limit */ -// range->r_time_flags; /* How to decode max/min retry life */ -// range->min_retry; /* Minimal number of retries */ -// range->max_retry; /* Maximal number of retries */ -// range->min_r_time; /* Minimal retry lifetime */ -// range->max_r_time; /* Maximal retry lifetime */ - - range->num_channels = FREQ_COUNT; - - val = 0; - for (i = 0; i < FREQ_COUNT; i++) { - // TODO: Include only legal frequencies for some countries -// if (local->channel_mask & (1 << i)) { - range->freq[val].i = i + 1; - range->freq[val].m = ipw2100_frequencies[i] * 100000; - range->freq[val].e = 1; - val++; -// } - if (val == IW_MAX_FREQUENCIES) - break; - } - range->num_frequency = val; - - /* Event capability (kernel + driver) */ - range->event_capa[0] = (IW_EVENT_CAPA_K_0 | - IW_EVENT_CAPA_MASK(SIOCGIWAP)); - range->event_capa[1] = IW_EVENT_CAPA_K_1; - - range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | - IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; - - IPW_DEBUG_WX("GET Range\n"); - - return 0; -} - -static int ipw2100_wx_set_wap(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int err = 0; - - // sanity checks - if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) - return -EINVAL; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) || - is_zero_ether_addr(wrqu->ap_addr.sa_data)) { - /* we disable mandatory BSSID association */ - IPW_DEBUG_WX("exit - disable mandatory BSSID\n"); - priv->config &= ~CFG_STATIC_BSSID; - err = ipw2100_set_mandatory_bssid(priv, NULL, 0); - goto done; - } - - priv->config |= CFG_STATIC_BSSID; - memcpy(priv->mandatory_bssid_mac, wrqu->ap_addr.sa_data, ETH_ALEN); - - err = ipw2100_set_mandatory_bssid(priv, wrqu->ap_addr.sa_data, 0); - - IPW_DEBUG_WX("SET BSSID -> %pM\n", wrqu->ap_addr.sa_data); - - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_get_wap(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - /* If we are associated, trying to associate, or have a statically - * configured BSSID then return that; otherwise return ANY */ - if (priv->config & CFG_STATIC_BSSID || priv->status & STATUS_ASSOCIATED) { - wrqu->ap_addr.sa_family = ARPHRD_ETHER; - memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN); - } else - eth_zero_addr(wrqu->ap_addr.sa_data); - - IPW_DEBUG_WX("Getting WAP BSSID: %pM\n", wrqu->ap_addr.sa_data); - return 0; -} - -static int ipw2100_wx_set_essid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - char *essid = ""; /* ANY */ - int length = 0; - int err = 0; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - if (wrqu->essid.flags && wrqu->essid.length) { - length = wrqu->essid.length; - essid = extra; - } - - if (length == 0) { - IPW_DEBUG_WX("Setting ESSID to ANY\n"); - priv->config &= ~CFG_STATIC_ESSID; - err = ipw2100_set_essid(priv, NULL, 0, 0); - goto done; - } - - length = min(length, IW_ESSID_MAX_SIZE); - - priv->config |= CFG_STATIC_ESSID; - - if (priv->essid_len == length && !memcmp(priv->essid, extra, length)) { - IPW_DEBUG_WX("ESSID set to current ESSID.\n"); - err = 0; - goto done; - } - - IPW_DEBUG_WX("Setting ESSID: '%*pE' (%d)\n", length, essid, length); - - priv->essid_len = length; - memcpy(priv->essid, essid, priv->essid_len); - - err = ipw2100_set_essid(priv, essid, length, 0); - - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_get_essid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - /* If we are associated, trying to associate, or have a statically - * configured ESSID then return that; otherwise return ANY */ - if (priv->config & CFG_STATIC_ESSID || priv->status & STATUS_ASSOCIATED) { - IPW_DEBUG_WX("Getting essid: '%*pE'\n", - priv->essid_len, priv->essid); - memcpy(extra, priv->essid, priv->essid_len); - wrqu->essid.length = priv->essid_len; - wrqu->essid.flags = 1; /* active */ - } else { - IPW_DEBUG_WX("Getting essid: ANY\n"); - wrqu->essid.length = 0; - wrqu->essid.flags = 0; /* active */ - } - - return 0; -} - -static int ipw2100_wx_set_nick(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - if (wrqu->data.length > IW_ESSID_MAX_SIZE) - return -E2BIG; - - wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick)); - memset(priv->nick, 0, sizeof(priv->nick)); - memcpy(priv->nick, extra, wrqu->data.length); - - IPW_DEBUG_WX("SET Nickname -> %s\n", priv->nick); - - return 0; -} - -static int ipw2100_wx_get_nick(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - wrqu->data.length = strlen(priv->nick); - memcpy(extra, priv->nick, wrqu->data.length); - wrqu->data.flags = 1; /* active */ - - IPW_DEBUG_WX("GET Nickname -> %s\n", extra); - - return 0; -} - -static int ipw2100_wx_set_rate(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - u32 target_rate = wrqu->bitrate.value; - u32 rate; - int err = 0; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - rate = 0; - - if (target_rate == 1000000 || - (!wrqu->bitrate.fixed && target_rate > 1000000)) - rate |= TX_RATE_1_MBIT; - if (target_rate == 2000000 || - (!wrqu->bitrate.fixed && target_rate > 2000000)) - rate |= TX_RATE_2_MBIT; - if (target_rate == 5500000 || - (!wrqu->bitrate.fixed && target_rate > 5500000)) - rate |= TX_RATE_5_5_MBIT; - if (target_rate == 11000000 || - (!wrqu->bitrate.fixed && target_rate > 11000000)) - rate |= TX_RATE_11_MBIT; - if (rate == 0) - rate = DEFAULT_TX_RATES; - - err = ipw2100_set_tx_rates(priv, rate, 0); - - IPW_DEBUG_WX("SET Rate -> %04X\n", rate); - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_get_rate(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int val; - unsigned int len = sizeof(val); - int err = 0; - - if (!(priv->status & STATUS_ENABLED) || - priv->status & STATUS_RF_KILL_MASK || - !(priv->status & STATUS_ASSOCIATED)) { - wrqu->bitrate.value = 0; - return 0; - } - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - err = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &val, &len); - if (err) { - IPW_DEBUG_WX("failed querying ordinals.\n"); - goto done; - } - - switch (val & TX_RATE_MASK) { - case TX_RATE_1_MBIT: - wrqu->bitrate.value = 1000000; - break; - case TX_RATE_2_MBIT: - wrqu->bitrate.value = 2000000; - break; - case TX_RATE_5_5_MBIT: - wrqu->bitrate.value = 5500000; - break; - case TX_RATE_11_MBIT: - wrqu->bitrate.value = 11000000; - break; - default: - wrqu->bitrate.value = 0; - } - - IPW_DEBUG_WX("GET Rate -> %d\n", wrqu->bitrate.value); - - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_set_rts(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int value, err; - - /* Auto RTS not yet supported */ - if (wrqu->rts.fixed == 0) - return -EINVAL; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - if (wrqu->rts.disabled) - value = priv->rts_threshold | RTS_DISABLED; - else { - if (wrqu->rts.value < 1 || wrqu->rts.value > 2304) { - err = -EINVAL; - goto done; - } - value = wrqu->rts.value; - } - - err = ipw2100_set_rts_threshold(priv, value); - - IPW_DEBUG_WX("SET RTS Threshold -> 0x%08X\n", value); - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_get_rts(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - wrqu->rts.value = priv->rts_threshold & ~RTS_DISABLED; - wrqu->rts.fixed = 1; /* no auto select */ - - /* If RTS is set to the default value, then it is disabled */ - wrqu->rts.disabled = (priv->rts_threshold & RTS_DISABLED) ? 1 : 0; - - IPW_DEBUG_WX("GET RTS Threshold -> 0x%08X\n", wrqu->rts.value); - - return 0; -} - -static int ipw2100_wx_set_txpow(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int err = 0, value; - - if (ipw_radio_kill_sw(priv, wrqu->txpower.disabled)) - return -EINPROGRESS; - - if (priv->ieee->iw_mode != IW_MODE_ADHOC) - return 0; - - if ((wrqu->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) - return -EINVAL; - - if (wrqu->txpower.fixed == 0) - value = IPW_TX_POWER_DEFAULT; - else { - if (wrqu->txpower.value < IPW_TX_POWER_MIN_DBM || - wrqu->txpower.value > IPW_TX_POWER_MAX_DBM) - return -EINVAL; - - value = wrqu->txpower.value; - } - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - err = ipw2100_set_tx_power(priv, value); - - IPW_DEBUG_WX("SET TX Power -> %d\n", value); - - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_get_txpow(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - wrqu->txpower.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0; - - if (priv->tx_power == IPW_TX_POWER_DEFAULT) { - wrqu->txpower.fixed = 0; - wrqu->txpower.value = IPW_TX_POWER_MAX_DBM; - } else { - wrqu->txpower.fixed = 1; - wrqu->txpower.value = priv->tx_power; - } - - wrqu->txpower.flags = IW_TXPOW_DBM; - - IPW_DEBUG_WX("GET TX Power -> %d\n", wrqu->txpower.value); - - return 0; -} - -static int ipw2100_wx_set_frag(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - if (!wrqu->frag.fixed) - return -EINVAL; - - if (wrqu->frag.disabled) { - priv->frag_threshold |= FRAG_DISABLED; - priv->ieee->fts = DEFAULT_FTS; - } else { - if (wrqu->frag.value < MIN_FRAG_THRESHOLD || - wrqu->frag.value > MAX_FRAG_THRESHOLD) - return -EINVAL; - - priv->ieee->fts = wrqu->frag.value & ~0x1; - priv->frag_threshold = priv->ieee->fts; - } - - IPW_DEBUG_WX("SET Frag Threshold -> %d\n", priv->ieee->fts); - - return 0; -} - -static int ipw2100_wx_get_frag(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - wrqu->frag.value = priv->frag_threshold & ~FRAG_DISABLED; - wrqu->frag.fixed = 0; /* no auto select */ - wrqu->frag.disabled = (priv->frag_threshold & FRAG_DISABLED) ? 1 : 0; - - IPW_DEBUG_WX("GET Frag Threshold -> %d\n", wrqu->frag.value); - - return 0; -} - -static int ipw2100_wx_set_retry(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int err = 0; - - if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled) - return -EINVAL; - - if (!(wrqu->retry.flags & IW_RETRY_LIMIT)) - return 0; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - if (wrqu->retry.flags & IW_RETRY_SHORT) { - err = ipw2100_set_short_retry(priv, wrqu->retry.value); - IPW_DEBUG_WX("SET Short Retry Limit -> %d\n", - wrqu->retry.value); - goto done; - } - - if (wrqu->retry.flags & IW_RETRY_LONG) { - err = ipw2100_set_long_retry(priv, wrqu->retry.value); - IPW_DEBUG_WX("SET Long Retry Limit -> %d\n", - wrqu->retry.value); - goto done; - } - - err = ipw2100_set_short_retry(priv, wrqu->retry.value); - if (!err) - err = ipw2100_set_long_retry(priv, wrqu->retry.value); - - IPW_DEBUG_WX("SET Both Retry Limits -> %d\n", wrqu->retry.value); - - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_get_retry(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - wrqu->retry.disabled = 0; /* can't be disabled */ - - if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) - return -EINVAL; - - if (wrqu->retry.flags & IW_RETRY_LONG) { - wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG; - wrqu->retry.value = priv->long_retry_limit; - } else { - wrqu->retry.flags = - (priv->short_retry_limit != - priv->long_retry_limit) ? - IW_RETRY_LIMIT | IW_RETRY_SHORT : IW_RETRY_LIMIT; - - wrqu->retry.value = priv->short_retry_limit; - } - - IPW_DEBUG_WX("GET Retry -> %d\n", wrqu->retry.value); - - return 0; -} - -static int ipw2100_wx_set_scan(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int err = 0; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - IPW_DEBUG_WX("Initiating scan...\n"); - - priv->user_requested_scan = 1; - if (ipw2100_set_scan_options(priv) || ipw2100_start_scan(priv)) { - IPW_DEBUG_WX("Start scan failed.\n"); - - /* TODO: Mark a scan as pending so when hardware initialized - * a scan starts */ - } - - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_get_scan(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - return libipw_wx_get_scan(priv->ieee, info, wrqu, extra); -} - -/* - * Implementation based on code in hostap-driver v0.1.3 hostap_ioctl.c - */ -static int ipw2100_wx_set_encode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key) -{ - /* - * No check of STATUS_INITIALIZED required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - return libipw_wx_set_encode(priv->ieee, info, wrqu, key); -} - -static int ipw2100_wx_get_encode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - return libipw_wx_get_encode(priv->ieee, info, wrqu, key); -} - -static int ipw2100_wx_set_power(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int err = 0; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - if (wrqu->power.disabled) { - priv->power_mode = IPW_POWER_LEVEL(priv->power_mode); - err = ipw2100_set_power_mode(priv, IPW_POWER_MODE_CAM); - IPW_DEBUG_WX("SET Power Management Mode -> off\n"); - goto done; - } - - switch (wrqu->power.flags & IW_POWER_MODE) { - case IW_POWER_ON: /* If not specified */ - case IW_POWER_MODE: /* If set all mask */ - case IW_POWER_ALL_R: /* If explicitly state all */ - break; - default: /* Otherwise we don't support it */ - IPW_DEBUG_WX("SET PM Mode: %X not supported.\n", - wrqu->power.flags); - err = -EOPNOTSUPP; - goto done; - } - - /* If the user hasn't specified a power management mode yet, default - * to BATTERY */ - priv->power_mode = IPW_POWER_ENABLED | priv->power_mode; - err = ipw2100_set_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode)); - - IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode); - - done: - mutex_unlock(&priv->action_mutex); - return err; - -} - -static int ipw2100_wx_get_power(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - if (!(priv->power_mode & IPW_POWER_ENABLED)) - wrqu->power.disabled = 1; - else { - wrqu->power.disabled = 0; - wrqu->power.flags = 0; - } - - IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode); - - return 0; -} - -/* - * WE-18 WPA support - */ - -/* SIOCSIWGENIE */ -static int ipw2100_wx_set_genie(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - - struct ipw2100_priv *priv = libipw_priv(dev); - struct libipw_device *ieee = priv->ieee; - u8 *buf; - - if (!ieee->wpa_enabled) - return -EOPNOTSUPP; - - if (wrqu->data.length > MAX_WPA_IE_LEN || - (wrqu->data.length && extra == NULL)) - return -EINVAL; - - if (wrqu->data.length) { - buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - kfree(ieee->wpa_ie); - ieee->wpa_ie = buf; - ieee->wpa_ie_len = wrqu->data.length; - } else { - kfree(ieee->wpa_ie); - ieee->wpa_ie = NULL; - ieee->wpa_ie_len = 0; - } - - ipw2100_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len); - - return 0; -} - -/* SIOCGIWGENIE */ -static int ipw2100_wx_get_genie(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - struct libipw_device *ieee = priv->ieee; - - if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) { - wrqu->data.length = 0; - return 0; - } - - if (wrqu->data.length < ieee->wpa_ie_len) - return -E2BIG; - - wrqu->data.length = ieee->wpa_ie_len; - memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len); - - return 0; -} - -/* SIOCSIWAUTH */ -static int ipw2100_wx_set_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - struct libipw_device *ieee = priv->ieee; - struct iw_param *param = &wrqu->param; - struct lib80211_crypt_data *crypt; - unsigned long flags; - int ret = 0; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_PAIRWISE: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_KEY_MGMT: - /* - * ipw2200 does not use these parameters - */ - break; - - case IW_AUTH_TKIP_COUNTERMEASURES: - crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; - if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) - break; - - flags = crypt->ops->get_flags(crypt->priv); - - if (param->value) - flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; - else - flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; - - crypt->ops->set_flags(flags, crypt->priv); - - break; - - case IW_AUTH_DROP_UNENCRYPTED:{ - /* HACK: - * - * wpa_supplicant calls set_wpa_enabled when the driver - * is loaded and unloaded, regardless of if WPA is being - * used. No other calls are made which can be used to - * determine if encryption will be used or not prior to - * association being expected. If encryption is not being - * used, drop_unencrypted is set to false, else true -- we - * can use this to determine if the CAP_PRIVACY_ON bit should - * be set. - */ - struct libipw_security sec = { - .flags = SEC_ENABLED, - .enabled = param->value, - }; - priv->ieee->drop_unencrypted = param->value; - /* We only change SEC_LEVEL for open mode. Others - * are set by ipw_wpa_set_encryption. - */ - if (!param->value) { - sec.flags |= SEC_LEVEL; - sec.level = SEC_LEVEL_0; - } else { - sec.flags |= SEC_LEVEL; - sec.level = SEC_LEVEL_1; - } - if (priv->ieee->set_security) - priv->ieee->set_security(priv->ieee->dev, &sec); - break; - } - - case IW_AUTH_80211_AUTH_ALG: - ret = ipw2100_wpa_set_auth_algs(priv, param->value); - break; - - case IW_AUTH_WPA_ENABLED: - ret = ipw2100_wpa_enable(priv, param->value); - break; - - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - ieee->ieee802_1x = param->value; - break; - - //case IW_AUTH_ROAMING_CONTROL: - case IW_AUTH_PRIVACY_INVOKED: - ieee->privacy_invoked = param->value; - break; - - default: - return -EOPNOTSUPP; - } - return ret; -} - -/* SIOCGIWAUTH */ -static int ipw2100_wx_get_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - struct libipw_device *ieee = priv->ieee; - struct lib80211_crypt_data *crypt; - struct iw_param *param = &wrqu->param; - int ret = 0; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_PAIRWISE: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_KEY_MGMT: - /* - * wpa_supplicant will control these internally - */ - ret = -EOPNOTSUPP; - break; - - case IW_AUTH_TKIP_COUNTERMEASURES: - crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; - if (!crypt || !crypt->ops->get_flags) { - IPW_DEBUG_WARNING("Can't get TKIP countermeasures: " - "crypt not set!\n"); - break; - } - - param->value = (crypt->ops->get_flags(crypt->priv) & - IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0; - - break; - - case IW_AUTH_DROP_UNENCRYPTED: - param->value = ieee->drop_unencrypted; - break; - - case IW_AUTH_80211_AUTH_ALG: - param->value = priv->ieee->sec.auth_mode; - break; - - case IW_AUTH_WPA_ENABLED: - param->value = ieee->wpa_enabled; - break; - - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - param->value = ieee->ieee802_1x; - break; - - case IW_AUTH_ROAMING_CONTROL: - case IW_AUTH_PRIVACY_INVOKED: - param->value = ieee->privacy_invoked; - break; - - default: - return -EOPNOTSUPP; - } - return 0; -} - -/* SIOCSIWENCODEEXT */ -static int ipw2100_wx_set_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra); -} - -/* SIOCGIWENCODEEXT */ -static int ipw2100_wx_get_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra); -} - -/* SIOCSIWMLME */ -static int ipw2100_wx_set_mlme(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - struct iw_mlme *mlme = (struct iw_mlme *)extra; - __le16 reason; - - reason = cpu_to_le16(mlme->reason_code); - - switch (mlme->cmd) { - case IW_MLME_DEAUTH: - // silently ignore - break; - - case IW_MLME_DISASSOC: - ipw2100_disassociate_bssid(priv); - break; - - default: - return -EOPNOTSUPP; - } - return 0; -} - -/* - * - * IWPRIV handlers - * - */ -#ifdef CONFIG_IPW2100_MONITOR -static int ipw2100_wx_set_promisc(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int *parms = (int *)extra; - int enable = (parms[0] > 0); - int err = 0; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - if (enable) { - if (priv->ieee->iw_mode == IW_MODE_MONITOR) { - err = ipw2100_set_channel(priv, parms[1], 0); - goto done; - } - priv->channel = parms[1]; - err = ipw2100_switch_mode(priv, IW_MODE_MONITOR); - } else { - if (priv->ieee->iw_mode == IW_MODE_MONITOR) - err = ipw2100_switch_mode(priv, priv->last_mode); - } - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_reset(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - if (priv->status & STATUS_INITIALIZED) - schedule_reset(priv); - return 0; -} - -#endif - -static int ipw2100_wx_set_powermode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int err = 0, mode = *(int *)extra; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - if ((mode < 0) || (mode > POWER_MODES)) - mode = IPW_POWER_AUTO; - - if (IPW_POWER_LEVEL(priv->power_mode) != mode) - err = ipw2100_set_power_mode(priv, mode); - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -#define MAX_POWER_STRING 80 -static int ipw2100_wx_get_powermode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - int level = IPW_POWER_LEVEL(priv->power_mode); - s32 timeout, period; - - if (!(priv->power_mode & IPW_POWER_ENABLED)) { - snprintf(extra, MAX_POWER_STRING, - "Power save level: %d (Off)", level); - } else { - switch (level) { - case IPW_POWER_MODE_CAM: - snprintf(extra, MAX_POWER_STRING, - "Power save level: %d (None)", level); - break; - case IPW_POWER_AUTO: - snprintf(extra, MAX_POWER_STRING, - "Power save level: %d (Auto)", level); - break; - default: - timeout = timeout_duration[level - 1] / 1000; - period = period_duration[level - 1] / 1000; - snprintf(extra, MAX_POWER_STRING, - "Power save level: %d " - "(Timeout %dms, Period %dms)", - level, timeout, period); - } - } - - wrqu->data.length = strlen(extra) + 1; - - return 0; -} - -static int ipw2100_wx_set_preamble(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int err, mode = *(int *)extra; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - if (mode == 1) - priv->config |= CFG_LONG_PREAMBLE; - else if (mode == 0) - priv->config &= ~CFG_LONG_PREAMBLE; - else { - err = -EINVAL; - goto done; - } - - err = ipw2100_system_config(priv, 0); - - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_get_preamble(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - if (priv->config & CFG_LONG_PREAMBLE) - snprintf(wrqu->name, IFNAMSIZ, "long (1)"); - else - snprintf(wrqu->name, IFNAMSIZ, "auto (0)"); - - return 0; -} - -#ifdef CONFIG_IPW2100_MONITOR -static int ipw2100_wx_set_crc_check(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw2100_priv *priv = libipw_priv(dev); - int err, mode = *(int *)extra; - - mutex_lock(&priv->action_mutex); - if (!(priv->status & STATUS_INITIALIZED)) { - err = -EIO; - goto done; - } - - if (mode == 1) - priv->config |= CFG_CRC_CHECK; - else if (mode == 0) - priv->config &= ~CFG_CRC_CHECK; - else { - err = -EINVAL; - goto done; - } - err = 0; - - done: - mutex_unlock(&priv->action_mutex); - return err; -} - -static int ipw2100_wx_get_crc_check(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* - * This can be called at any time. No action lock required - */ - - struct ipw2100_priv *priv = libipw_priv(dev); - - if (priv->config & CFG_CRC_CHECK) - snprintf(wrqu->name, IFNAMSIZ, "CRC checked (1)"); - else - snprintf(wrqu->name, IFNAMSIZ, "CRC ignored (0)"); - - return 0; -} -#endif /* CONFIG_IPW2100_MONITOR */ - -static iw_handler ipw2100_wx_handlers[] = { - IW_HANDLER(SIOCGIWNAME, ipw2100_wx_get_name), - IW_HANDLER(SIOCSIWFREQ, ipw2100_wx_set_freq), - IW_HANDLER(SIOCGIWFREQ, ipw2100_wx_get_freq), - IW_HANDLER(SIOCSIWMODE, ipw2100_wx_set_mode), - IW_HANDLER(SIOCGIWMODE, ipw2100_wx_get_mode), - IW_HANDLER(SIOCGIWRANGE, ipw2100_wx_get_range), - IW_HANDLER(SIOCSIWAP, ipw2100_wx_set_wap), - IW_HANDLER(SIOCGIWAP, ipw2100_wx_get_wap), - IW_HANDLER(SIOCSIWMLME, ipw2100_wx_set_mlme), - IW_HANDLER(SIOCSIWSCAN, ipw2100_wx_set_scan), - IW_HANDLER(SIOCGIWSCAN, ipw2100_wx_get_scan), - IW_HANDLER(SIOCSIWESSID, ipw2100_wx_set_essid), - IW_HANDLER(SIOCGIWESSID, ipw2100_wx_get_essid), - IW_HANDLER(SIOCSIWNICKN, ipw2100_wx_set_nick), - IW_HANDLER(SIOCGIWNICKN, ipw2100_wx_get_nick), - IW_HANDLER(SIOCSIWRATE, ipw2100_wx_set_rate), - IW_HANDLER(SIOCGIWRATE, ipw2100_wx_get_rate), - IW_HANDLER(SIOCSIWRTS, ipw2100_wx_set_rts), - IW_HANDLER(SIOCGIWRTS, ipw2100_wx_get_rts), - IW_HANDLER(SIOCSIWFRAG, ipw2100_wx_set_frag), - IW_HANDLER(SIOCGIWFRAG, ipw2100_wx_get_frag), - IW_HANDLER(SIOCSIWTXPOW, ipw2100_wx_set_txpow), - IW_HANDLER(SIOCGIWTXPOW, ipw2100_wx_get_txpow), - IW_HANDLER(SIOCSIWRETRY, ipw2100_wx_set_retry), - IW_HANDLER(SIOCGIWRETRY, ipw2100_wx_get_retry), - IW_HANDLER(SIOCSIWENCODE, ipw2100_wx_set_encode), - IW_HANDLER(SIOCGIWENCODE, ipw2100_wx_get_encode), - IW_HANDLER(SIOCSIWPOWER, ipw2100_wx_set_power), - IW_HANDLER(SIOCGIWPOWER, ipw2100_wx_get_power), - IW_HANDLER(SIOCSIWGENIE, ipw2100_wx_set_genie), - IW_HANDLER(SIOCGIWGENIE, ipw2100_wx_get_genie), - IW_HANDLER(SIOCSIWAUTH, ipw2100_wx_set_auth), - IW_HANDLER(SIOCGIWAUTH, ipw2100_wx_get_auth), - IW_HANDLER(SIOCSIWENCODEEXT, ipw2100_wx_set_encodeext), - IW_HANDLER(SIOCGIWENCODEEXT, ipw2100_wx_get_encodeext), -}; - -#define IPW2100_PRIV_SET_MONITOR SIOCIWFIRSTPRIV -#define IPW2100_PRIV_RESET SIOCIWFIRSTPRIV+1 -#define IPW2100_PRIV_SET_POWER SIOCIWFIRSTPRIV+2 -#define IPW2100_PRIV_GET_POWER SIOCIWFIRSTPRIV+3 -#define IPW2100_PRIV_SET_LONGPREAMBLE SIOCIWFIRSTPRIV+4 -#define IPW2100_PRIV_GET_LONGPREAMBLE SIOCIWFIRSTPRIV+5 -#define IPW2100_PRIV_SET_CRC_CHECK SIOCIWFIRSTPRIV+6 -#define IPW2100_PRIV_GET_CRC_CHECK SIOCIWFIRSTPRIV+7 - -static const struct iw_priv_args ipw2100_private_args[] = { - -#ifdef CONFIG_IPW2100_MONITOR - { - IPW2100_PRIV_SET_MONITOR, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"}, - { - IPW2100_PRIV_RESET, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"}, -#endif /* CONFIG_IPW2100_MONITOR */ - - { - IPW2100_PRIV_SET_POWER, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_power"}, - { - IPW2100_PRIV_GET_POWER, - 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_POWER_STRING, - "get_power"}, - { - IPW2100_PRIV_SET_LONGPREAMBLE, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"}, - { - IPW2100_PRIV_GET_LONGPREAMBLE, - 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_preamble"}, -#ifdef CONFIG_IPW2100_MONITOR - { - IPW2100_PRIV_SET_CRC_CHECK, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_crc_check"}, - { - IPW2100_PRIV_GET_CRC_CHECK, - 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, "get_crc_check"}, -#endif /* CONFIG_IPW2100_MONITOR */ -}; - -static iw_handler ipw2100_private_handler[] = { -#ifdef CONFIG_IPW2100_MONITOR - ipw2100_wx_set_promisc, - ipw2100_wx_reset, -#else /* CONFIG_IPW2100_MONITOR */ - NULL, - NULL, -#endif /* CONFIG_IPW2100_MONITOR */ - ipw2100_wx_set_powermode, - ipw2100_wx_get_powermode, - ipw2100_wx_set_preamble, - ipw2100_wx_get_preamble, -#ifdef CONFIG_IPW2100_MONITOR - ipw2100_wx_set_crc_check, - ipw2100_wx_get_crc_check, -#else /* CONFIG_IPW2100_MONITOR */ - NULL, - NULL, -#endif /* CONFIG_IPW2100_MONITOR */ -}; - -/* - * Get wireless statistics. - * Called by /proc/net/wireless - * Also called by SIOCGIWSTATS - */ -static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev) -{ - enum { - POOR = 30, - FAIR = 60, - GOOD = 80, - VERY_GOOD = 90, - EXCELLENT = 95, - PERFECT = 100 - }; - int rssi_qual; - int tx_qual; - int beacon_qual; - int quality; - - struct ipw2100_priv *priv = libipw_priv(dev); - struct iw_statistics *wstats; - u32 rssi, tx_retries, missed_beacons, tx_failures; - u32 ord_len = sizeof(u32); - - if (!priv) - return (struct iw_statistics *)NULL; - - wstats = &priv->wstats; - - /* if hw is disabled, then ipw2100_get_ordinal() can't be called. - * ipw2100_wx_wireless_stats seems to be called before fw is - * initialized. STATUS_ASSOCIATED will only be set if the hw is up - * and associated; if not associcated, the values are all meaningless - * anyway, so set them all to NULL and INVALID */ - if (!(priv->status & STATUS_ASSOCIATED)) { - wstats->miss.beacon = 0; - wstats->discard.retries = 0; - wstats->qual.qual = 0; - wstats->qual.level = 0; - wstats->qual.noise = 0; - wstats->qual.updated = 7; - wstats->qual.updated |= IW_QUAL_NOISE_INVALID | - IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; - return wstats; - } - - if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_MISSED_BCNS, - &missed_beacons, &ord_len)) - goto fail_get_ordinal; - - /* If we don't have a connection the quality and level is 0 */ - if (!(priv->status & STATUS_ASSOCIATED)) { - wstats->qual.qual = 0; - wstats->qual.level = 0; - } else { - if (ipw2100_get_ordinal(priv, IPW_ORD_RSSI_AVG_CURR, - &rssi, &ord_len)) - goto fail_get_ordinal; - wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM; - if (rssi < 10) - rssi_qual = rssi * POOR / 10; - else if (rssi < 15) - rssi_qual = (rssi - 10) * (FAIR - POOR) / 5 + POOR; - else if (rssi < 20) - rssi_qual = (rssi - 15) * (GOOD - FAIR) / 5 + FAIR; - else if (rssi < 30) - rssi_qual = (rssi - 20) * (VERY_GOOD - GOOD) / - 10 + GOOD; - else - rssi_qual = (rssi - 30) * (PERFECT - VERY_GOOD) / - 10 + VERY_GOOD; - - if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_PERCENT_RETRIES, - &tx_retries, &ord_len)) - goto fail_get_ordinal; - - if (tx_retries > 75) - tx_qual = (90 - tx_retries) * POOR / 15; - else if (tx_retries > 70) - tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR; - else if (tx_retries > 65) - tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR; - else if (tx_retries > 50) - tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) / - 15 + GOOD; - else - tx_qual = (50 - tx_retries) * - (PERFECT - VERY_GOOD) / 50 + VERY_GOOD; - - if (missed_beacons > 50) - beacon_qual = (60 - missed_beacons) * POOR / 10; - else if (missed_beacons > 40) - beacon_qual = (50 - missed_beacons) * (FAIR - POOR) / - 10 + POOR; - else if (missed_beacons > 32) - beacon_qual = (40 - missed_beacons) * (GOOD - FAIR) / - 18 + FAIR; - else if (missed_beacons > 20) - beacon_qual = (32 - missed_beacons) * - (VERY_GOOD - GOOD) / 20 + GOOD; - else - beacon_qual = (20 - missed_beacons) * - (PERFECT - VERY_GOOD) / 20 + VERY_GOOD; - - quality = min(tx_qual, rssi_qual); - quality = min(beacon_qual, quality); - -#ifdef CONFIG_IPW2100_DEBUG - if (beacon_qual == quality) - IPW_DEBUG_WX("Quality clamped by Missed Beacons\n"); - else if (tx_qual == quality) - IPW_DEBUG_WX("Quality clamped by Tx Retries\n"); - else if (quality != 100) - IPW_DEBUG_WX("Quality clamped by Signal Strength\n"); - else - IPW_DEBUG_WX("Quality not clamped.\n"); -#endif - - wstats->qual.qual = quality; - wstats->qual.level = rssi + IPW2100_RSSI_TO_DBM; - } - - wstats->qual.noise = 0; - wstats->qual.updated = 7; - wstats->qual.updated |= IW_QUAL_NOISE_INVALID; - - /* FIXME: this is percent and not a # */ - wstats->miss.beacon = missed_beacons; - - if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURES, - &tx_failures, &ord_len)) - goto fail_get_ordinal; - wstats->discard.retries = tx_failures; - - return wstats; - - fail_get_ordinal: - IPW_DEBUG_WX("failed querying ordinals.\n"); - - return (struct iw_statistics *)NULL; -} - -static struct iw_handler_def ipw2100_wx_handler_def = { - .standard = ipw2100_wx_handlers, - .num_standard = ARRAY_SIZE(ipw2100_wx_handlers), - .num_private = ARRAY_SIZE(ipw2100_private_handler), - .num_private_args = ARRAY_SIZE(ipw2100_private_args), - .private = (iw_handler *) ipw2100_private_handler, - .private_args = (struct iw_priv_args *)ipw2100_private_args, - .get_wireless_stats = ipw2100_wx_wireless_stats, -}; - -static void ipw2100_wx_event_work(struct work_struct *work) -{ - struct ipw2100_priv *priv = - container_of(work, struct ipw2100_priv, wx_event_work.work); - union iwreq_data wrqu; - unsigned int len = ETH_ALEN; - - if (priv->status & STATUS_STOPPING) - return; - - mutex_lock(&priv->action_mutex); - - IPW_DEBUG_WX("enter\n"); - - mutex_unlock(&priv->action_mutex); - - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - - /* Fetch BSSID from the hardware */ - if (!(priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) || - priv->status & STATUS_RF_KILL_MASK || - ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, - &priv->bssid, &len)) { - eth_zero_addr(wrqu.ap_addr.sa_data); - } else { - /* We now have the BSSID, so can finish setting to the full - * associated state */ - memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN); - memcpy(priv->ieee->bssid, priv->bssid, ETH_ALEN); - priv->status &= ~STATUS_ASSOCIATING; - priv->status |= STATUS_ASSOCIATED; - netif_carrier_on(priv->net_dev); - netif_wake_queue(priv->net_dev); - } - - if (!(priv->status & STATUS_ASSOCIATED)) { - IPW_DEBUG_WX("Configuring ESSID\n"); - mutex_lock(&priv->action_mutex); - /* This is a disassociation event, so kick the firmware to - * look for another AP */ - if (priv->config & CFG_STATIC_ESSID) - ipw2100_set_essid(priv, priv->essid, priv->essid_len, - 0); - else - ipw2100_set_essid(priv, NULL, 0, 0); - mutex_unlock(&priv->action_mutex); - } - - wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); -} - -#define IPW2100_FW_MAJOR_VERSION 1 -#define IPW2100_FW_MINOR_VERSION 3 - -#define IPW2100_FW_MINOR(x) ((x & 0xff) >> 8) -#define IPW2100_FW_MAJOR(x) (x & 0xff) - -#define IPW2100_FW_VERSION ((IPW2100_FW_MINOR_VERSION << 8) | \ - IPW2100_FW_MAJOR_VERSION) - -#define IPW2100_FW_PREFIX "ipw2100-" __stringify(IPW2100_FW_MAJOR_VERSION) \ -"." __stringify(IPW2100_FW_MINOR_VERSION) - -#define IPW2100_FW_NAME(x) IPW2100_FW_PREFIX "" x ".fw" - -/* - -BINARY FIRMWARE HEADER FORMAT - -offset length desc -0 2 version -2 2 mode == 0:BSS,1:IBSS,2:MONITOR -4 4 fw_len -8 4 uc_len -C fw_len firmware data -12 + fw_len uc_len microcode data - -*/ - -struct ipw2100_fw_header { - short version; - short mode; - unsigned int fw_size; - unsigned int uc_size; -} __packed; - -static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw) -{ - struct ipw2100_fw_header *h = - (struct ipw2100_fw_header *)fw->fw_entry->data; - - if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) { - printk(KERN_WARNING DRV_NAME ": Firmware image not compatible " - "(detected version id of %u). " - "See Documentation/networking/README.ipw2100\n", - h->version); - return 1; - } - - fw->version = h->version; - fw->fw.data = fw->fw_entry->data + sizeof(struct ipw2100_fw_header); - fw->fw.size = h->fw_size; - fw->uc.data = fw->fw.data + h->fw_size; - fw->uc.size = h->uc_size; - - return 0; -} - -static int ipw2100_get_firmware(struct ipw2100_priv *priv, - struct ipw2100_fw *fw) -{ - char *fw_name; - int rc; - - IPW_DEBUG_INFO("%s: Using hotplug firmware load.\n", - priv->net_dev->name); - - switch (priv->ieee->iw_mode) { - case IW_MODE_ADHOC: - fw_name = IPW2100_FW_NAME("-i"); - break; -#ifdef CONFIG_IPW2100_MONITOR - case IW_MODE_MONITOR: - fw_name = IPW2100_FW_NAME("-p"); - break; -#endif - case IW_MODE_INFRA: - default: - fw_name = IPW2100_FW_NAME(""); - break; - } - - rc = request_firmware(&fw->fw_entry, fw_name, &priv->pci_dev->dev); - - if (rc < 0) { - printk(KERN_ERR DRV_NAME ": " - "%s: Firmware '%s' not available or load failed.\n", - priv->net_dev->name, fw_name); - return rc; - } - IPW_DEBUG_INFO("firmware data %p size %zd\n", fw->fw_entry->data, - fw->fw_entry->size); - - ipw2100_mod_firmware_load(fw); - - return 0; -} - -MODULE_FIRMWARE(IPW2100_FW_NAME("-i")); -#ifdef CONFIG_IPW2100_MONITOR -MODULE_FIRMWARE(IPW2100_FW_NAME("-p")); -#endif -MODULE_FIRMWARE(IPW2100_FW_NAME("")); - -static void ipw2100_release_firmware(struct ipw2100_priv *priv, - struct ipw2100_fw *fw) -{ - fw->version = 0; - release_firmware(fw->fw_entry); - fw->fw_entry = NULL; -} - -static int ipw2100_get_fwversion(struct ipw2100_priv *priv, char *buf, - size_t max) -{ - char ver[MAX_FW_VERSION_LEN]; - u32 len = MAX_FW_VERSION_LEN; - u32 tmp; - int i; - /* firmware version is an ascii string (max len of 14) */ - if (ipw2100_get_ordinal(priv, IPW_ORD_STAT_FW_VER_NUM, ver, &len)) - return -EIO; - tmp = max; - if (len >= max) - len = max - 1; - for (i = 0; i < len; i++) - buf[i] = ver[i]; - buf[i] = '\0'; - return tmp; -} - -static int ipw2100_get_ucodeversion(struct ipw2100_priv *priv, char *buf, - size_t max) -{ - u32 ver; - u32 len = sizeof(ver); - /* microcode version is a 32 bit integer */ - if (ipw2100_get_ordinal(priv, IPW_ORD_UCODE_VERSION, &ver, &len)) - return -EIO; - return snprintf(buf, max, "%08X", ver); -} - -/* - * On exit, the firmware will have been freed from the fw list - */ -static int ipw2100_fw_download(struct ipw2100_priv *priv, struct ipw2100_fw *fw) -{ - /* firmware is constructed of N contiguous entries, each entry is - * structured as: - * - * offset sie desc - * 0 4 address to write to - * 4 2 length of data run - * 6 length data - */ - unsigned int addr; - unsigned short len; - - const unsigned char *firmware_data = fw->fw.data; - unsigned int firmware_data_left = fw->fw.size; - - while (firmware_data_left > 0) { - addr = *(u32 *) (firmware_data); - firmware_data += 4; - firmware_data_left -= 4; - - len = *(u16 *) (firmware_data); - firmware_data += 2; - firmware_data_left -= 2; - - if (len > 32) { - printk(KERN_ERR DRV_NAME ": " - "Invalid firmware run-length of %d bytes\n", - len); - return -EINVAL; - } - - write_nic_memory(priv->net_dev, addr, len, firmware_data); - firmware_data += len; - firmware_data_left -= len; - } - - return 0; -} - -struct symbol_alive_response { - u8 cmd_id; - u8 seq_num; - u8 ucode_rev; - u8 eeprom_valid; - u16 valid_flags; - u8 IEEE_addr[6]; - u16 flags; - u16 pcb_rev; - u16 clock_settle_time; // 1us LSB - u16 powerup_settle_time; // 1us LSB - u16 hop_settle_time; // 1us LSB - u8 date[3]; // month, day, year - u8 time[2]; // hours, minutes - u8 ucode_valid; -}; - -static int ipw2100_ucode_download(struct ipw2100_priv *priv, - struct ipw2100_fw *fw) -{ - struct net_device *dev = priv->net_dev; - const unsigned char *microcode_data = fw->uc.data; - unsigned int microcode_data_left = fw->uc.size; - void __iomem *reg = priv->ioaddr; - - struct symbol_alive_response response; - int i, j; - u8 data; - - /* Symbol control */ - write_nic_word(dev, IPW2100_CONTROL_REG, 0x703); - readl(reg); - write_nic_word(dev, IPW2100_CONTROL_REG, 0x707); - readl(reg); - - /* HW config */ - write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */ - readl(reg); - write_nic_byte(dev, 0x210014, 0x72); /* fifo width =16 */ - readl(reg); - - /* EN_CS_ACCESS bit to reset control store pointer */ - write_nic_byte(dev, 0x210000, 0x40); - readl(reg); - write_nic_byte(dev, 0x210000, 0x0); - readl(reg); - write_nic_byte(dev, 0x210000, 0x40); - readl(reg); - - /* copy microcode from buffer into Symbol */ - - while (microcode_data_left > 0) { - write_nic_byte(dev, 0x210010, *microcode_data++); - write_nic_byte(dev, 0x210010, *microcode_data++); - microcode_data_left -= 2; - } - - /* EN_CS_ACCESS bit to reset the control store pointer */ - write_nic_byte(dev, 0x210000, 0x0); - readl(reg); - - /* Enable System (Reg 0) - * first enable causes garbage in RX FIFO */ - write_nic_byte(dev, 0x210000, 0x0); - readl(reg); - write_nic_byte(dev, 0x210000, 0x80); - readl(reg); - - /* Reset External Baseband Reg */ - write_nic_word(dev, IPW2100_CONTROL_REG, 0x703); - readl(reg); - write_nic_word(dev, IPW2100_CONTROL_REG, 0x707); - readl(reg); - - /* HW Config (Reg 5) */ - write_nic_byte(dev, 0x210014, 0x72); // fifo width =16 - readl(reg); - write_nic_byte(dev, 0x210014, 0x72); // fifo width =16 - readl(reg); - - /* Enable System (Reg 0) - * second enable should be OK */ - write_nic_byte(dev, 0x210000, 0x00); // clear enable system - readl(reg); - write_nic_byte(dev, 0x210000, 0x80); // set enable system - - /* check Symbol is enabled - upped this from 5 as it wasn't always - * catching the update */ - for (i = 0; i < 10; i++) { - udelay(10); - - /* check Dino is enabled bit */ - read_nic_byte(dev, 0x210000, &data); - if (data & 0x1) - break; - } - - if (i == 10) { - printk(KERN_ERR DRV_NAME ": %s: Error initializing Symbol\n", - dev->name); - return -EIO; - } - - /* Get Symbol alive response */ - for (i = 0; i < 30; i++) { - /* Read alive response structure */ - for (j = 0; - j < (sizeof(struct symbol_alive_response) >> 1); j++) - read_nic_word(dev, 0x210004, ((u16 *) & response) + j); - - if ((response.cmd_id == 1) && (response.ucode_valid == 0x1)) - break; - udelay(10); - } - - if (i == 30) { - printk(KERN_ERR DRV_NAME - ": %s: No response from Symbol - hw not alive\n", - dev->name); - printk_buf(IPW_DL_ERROR, (u8 *) & response, sizeof(response)); - return -EIO; - } - - return 0; -} diff --git a/drivers/net/wireless/ipw2x00/ipw2100.h b/drivers/net/wireless/ipw2x00/ipw2100.h deleted file mode 100644 index 193947865efd..000000000000 --- a/drivers/net/wireless/ipw2x00/ipw2100.h +++ /dev/null @@ -1,1156 +0,0 @@ -/****************************************************************************** - - Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. - - This program is free software; you can redistribute it and/or modify it - under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - The full GNU General Public License is included in this distribution in the - file called LICENSE. - - Contact Information: - Intel Linux Wireless - Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - -******************************************************************************/ -#ifndef _IPW2100_H -#define _IPW2100_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // new driver API - -#ifdef CONFIG_IPW2100_MONITOR -#include -#endif - -#include -#include - -#include "libipw.h" - -struct ipw2100_priv; -struct ipw2100_tx_packet; -struct ipw2100_rx_packet; - -#define IPW_DL_UNINIT 0x80000000 -#define IPW_DL_NONE 0x00000000 -#define IPW_DL_ALL 0x7FFFFFFF - -/* - * To use the debug system; - * - * If you are defining a new debug classification, simply add it to the #define - * list here in the form of: - * - * #define IPW_DL_xxxx VALUE - * - * shifting value to the left one bit from the previous entry. xxxx should be - * the name of the classification (for example, WEP) - * - * You then need to either add a IPW2100_xxxx_DEBUG() macro definition for your - * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want - * to send output to that classification. - * - * To add your debug level to the list of levels seen when you perform - * - * % cat /proc/net/ipw2100/debug_level - * - * you simply need to add your entry to the ipw2100_debug_levels array. - * - * If you do not see debug_level in /proc/net/ipw2100 then you do not have - * CONFIG_IPW2100_DEBUG defined in your kernel configuration - * - */ - -#define IPW_DL_ERROR (1<<0) -#define IPW_DL_WARNING (1<<1) -#define IPW_DL_INFO (1<<2) -#define IPW_DL_WX (1<<3) -#define IPW_DL_HC (1<<5) -#define IPW_DL_STATE (1<<6) - -#define IPW_DL_NOTIF (1<<10) -#define IPW_DL_SCAN (1<<11) -#define IPW_DL_ASSOC (1<<12) -#define IPW_DL_DROP (1<<13) - -#define IPW_DL_IOCTL (1<<14) -#define IPW_DL_RF_KILL (1<<17) - -#define IPW_DL_MANAGE (1<<15) -#define IPW_DL_FW (1<<16) - -#define IPW_DL_FRAG (1<<21) -#define IPW_DL_WEP (1<<22) -#define IPW_DL_TX (1<<23) -#define IPW_DL_RX (1<<24) -#define IPW_DL_ISR (1<<25) -#define IPW_DL_IO (1<<26) -#define IPW_DL_TRACE (1<<28) - -#define IPW_DEBUG_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a) -#define IPW_DEBUG_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a) -#define IPW_DEBUG_INFO(f...) IPW_DEBUG(IPW_DL_INFO, ## f) -#define IPW_DEBUG_WX(f...) IPW_DEBUG(IPW_DL_WX, ## f) -#define IPW_DEBUG_SCAN(f...) IPW_DEBUG(IPW_DL_SCAN, ## f) -#define IPW_DEBUG_NOTIF(f...) IPW_DEBUG(IPW_DL_NOTIF, ## f) -#define IPW_DEBUG_TRACE(f...) IPW_DEBUG(IPW_DL_TRACE, ## f) -#define IPW_DEBUG_RX(f...) IPW_DEBUG(IPW_DL_RX, ## f) -#define IPW_DEBUG_TX(f...) IPW_DEBUG(IPW_DL_TX, ## f) -#define IPW_DEBUG_ISR(f...) IPW_DEBUG(IPW_DL_ISR, ## f) -#define IPW_DEBUG_MANAGEMENT(f...) IPW_DEBUG(IPW_DL_MANAGE, ## f) -#define IPW_DEBUG_WEP(f...) IPW_DEBUG(IPW_DL_WEP, ## f) -#define IPW_DEBUG_HC(f...) IPW_DEBUG(IPW_DL_HC, ## f) -#define IPW_DEBUG_FRAG(f...) IPW_DEBUG(IPW_DL_FRAG, ## f) -#define IPW_DEBUG_FW(f...) IPW_DEBUG(IPW_DL_FW, ## f) -#define IPW_DEBUG_RF_KILL(f...) IPW_DEBUG(IPW_DL_RF_KILL, ## f) -#define IPW_DEBUG_DROP(f...) IPW_DEBUG(IPW_DL_DROP, ## f) -#define IPW_DEBUG_IO(f...) IPW_DEBUG(IPW_DL_IO, ## f) -#define IPW_DEBUG_IOCTL(f...) IPW_DEBUG(IPW_DL_IOCTL, ## f) -#define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) -#define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) - -enum { - IPW_HW_STATE_DISABLED = 1, - IPW_HW_STATE_ENABLED = 0 -}; - -extern const char *port_type_str[]; -extern const char *band_str[]; - -#define NUMBER_OF_BD_PER_COMMAND_PACKET 1 -#define NUMBER_OF_BD_PER_DATA_PACKET 2 - -#define IPW_MAX_BDS 6 -#define NUMBER_OF_OVERHEAD_BDS_PER_PACKETR 2 -#define NUMBER_OF_BDS_TO_LEAVE_FOR_COMMANDS 1 - -#define REQUIRED_SPACE_IN_RING_FOR_COMMAND_PACKET \ - (IPW_BD_QUEUE_W_R_MIN_SPARE + NUMBER_OF_BD_PER_COMMAND_PACKET) - -struct bd_status { - union { - struct { - u8 nlf:1, txType:2, intEnabled:1, reserved:4; - } fields; - u8 field; - } info; -} __packed; - -struct ipw2100_bd { - u32 host_addr; - u32 buf_length; - struct bd_status status; - /* number of fragments for frame (should be set only for - * 1st TBD) */ - u8 num_fragments; - u8 reserved[6]; -} __packed; - -#define IPW_BD_QUEUE_LENGTH(n) (1<value = (x)->hi = 0; \ - (x)->lo = 0x7fffffff; \ -} while (0) -#define SET_STAT(x,y) do { \ - (x)->value = y; \ - if ((x)->value > (x)->hi) (x)->hi = (x)->value; \ - if ((x)->value < (x)->lo) (x)->lo = (x)->value; \ -} while (0) -#define INC_STAT(x) do { if (++(x)->value > (x)->hi) (x)->hi = (x)->value; } \ -while (0) -#define DEC_STAT(x) do { if (--(x)->value < (x)->lo) (x)->lo = (x)->value; } \ -while (0) - -#define IPW2100_ERROR_QUEUE 5 - -/* Power management code: enable or disable? */ -enum { -#ifdef CONFIG_PM - IPW2100_PM_DISABLED = 0, - PM_STATE_SIZE = 16, -#else - IPW2100_PM_DISABLED = 1, - PM_STATE_SIZE = 0, -#endif -}; - -#define STATUS_POWERED (1<<0) -#define STATUS_CMD_ACTIVE (1<<1) /**< host command in progress */ -#define STATUS_RUNNING (1<<2) /* Card initialized, but not enabled */ -#define STATUS_ENABLED (1<<3) /* Card enabled -- can scan,Tx,Rx */ -#define STATUS_STOPPING (1<<4) /* Card is in shutdown phase */ -#define STATUS_INITIALIZED (1<<5) /* Card is ready for external calls */ -#define STATUS_ASSOCIATING (1<<9) /* Associated, but no BSSID yet */ -#define STATUS_ASSOCIATED (1<<10) /* Associated and BSSID valid */ -#define STATUS_INT_ENABLED (1<<11) -#define STATUS_RF_KILL_HW (1<<12) -#define STATUS_RF_KILL_SW (1<<13) -#define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW) -#define STATUS_EXIT_PENDING (1<<14) - -#define STATUS_SCAN_PENDING (1<<23) -#define STATUS_SCANNING (1<<24) -#define STATUS_SCAN_ABORTING (1<<25) -#define STATUS_SCAN_COMPLETE (1<<26) -#define STATUS_WX_EVENT_PENDING (1<<27) -#define STATUS_RESET_PENDING (1<<29) -#define STATUS_SECURITY_UPDATED (1<<30) /* Security sync needed */ - -/* Internal NIC states */ -#define IPW_STATE_INITIALIZED (1<<0) -#define IPW_STATE_COUNTRY_FOUND (1<<1) -#define IPW_STATE_ASSOCIATED (1<<2) -#define IPW_STATE_ASSN_LOST (1<<3) -#define IPW_STATE_ASSN_CHANGED (1<<4) -#define IPW_STATE_SCAN_COMPLETE (1<<5) -#define IPW_STATE_ENTERED_PSP (1<<6) -#define IPW_STATE_LEFT_PSP (1<<7) -#define IPW_STATE_RF_KILL (1<<8) -#define IPW_STATE_DISABLED (1<<9) -#define IPW_STATE_POWER_DOWN (1<<10) -#define IPW_STATE_SCANNING (1<<11) - -#define CFG_STATIC_CHANNEL (1<<0) /* Restrict assoc. to single channel */ -#define CFG_STATIC_ESSID (1<<1) /* Restrict assoc. to single SSID */ -#define CFG_STATIC_BSSID (1<<2) /* Restrict assoc. to single BSSID */ -#define CFG_CUSTOM_MAC (1<<3) -#define CFG_LONG_PREAMBLE (1<<4) -#define CFG_ASSOCIATE (1<<6) -#define CFG_FIXED_RATE (1<<7) -#define CFG_ADHOC_CREATE (1<<8) -#define CFG_PASSIVE_SCAN (1<<10) -#ifdef CONFIG_IPW2100_MONITOR -#define CFG_CRC_CHECK (1<<11) -#endif - -#define CAP_SHARED_KEY (1<<0) /* Off = OPEN */ -#define CAP_PRIVACY_ON (1<<1) /* Off = No privacy */ - -struct ipw2100_priv { - void __iomem *ioaddr; - - int stop_hang_check; /* Set 1 when shutting down to kill hang_check */ - int stop_rf_kill; /* Set 1 when shutting down to kill rf_kill */ - - struct libipw_device *ieee; - unsigned long status; - unsigned long config; - unsigned long capability; - - /* Statistics */ - int resets; - int reset_backoff; - - /* Context */ - u8 essid[IW_ESSID_MAX_SIZE]; - u8 essid_len; - u8 bssid[ETH_ALEN]; - u8 channel; - int last_mode; - - unsigned long connect_start; - unsigned long last_reset; - - u32 channel_mask; - u32 fatal_error; - u32 fatal_errors[IPW2100_ERROR_QUEUE]; - u32 fatal_index; - int eeprom_version; - int firmware_version; - unsigned long hw_features; - int hangs; - u32 last_rtc; - int dump_raw; /* 1 to dump raw bytes in /sys/.../memory */ - u8 *snapshot[0x30]; - - u8 mandatory_bssid_mac[ETH_ALEN]; - u8 mac_addr[ETH_ALEN]; - - int power_mode; - - int messages_sent; - - int short_retry_limit; - int long_retry_limit; - - u32 rts_threshold; - u32 frag_threshold; - - int in_isr; - - u32 tx_rates; - int tx_power; - u32 beacon_interval; - - char nick[IW_ESSID_MAX_SIZE + 1]; - - struct ipw2100_status_queue status_queue; - - struct statistic txq_stat; - struct statistic rxq_stat; - struct ipw2100_bd_queue rx_queue; - struct ipw2100_bd_queue tx_queue; - struct ipw2100_rx_packet *rx_buffers; - - struct statistic fw_pend_stat; - struct list_head fw_pend_list; - - struct statistic msg_free_stat; - struct statistic msg_pend_stat; - struct list_head msg_free_list; - struct list_head msg_pend_list; - struct ipw2100_tx_packet *msg_buffers; - - struct statistic tx_free_stat; - struct statistic tx_pend_stat; - struct list_head tx_free_list; - struct list_head tx_pend_list; - struct ipw2100_tx_packet *tx_buffers; - - struct ipw2100_ordinals ordinals; - - struct pci_dev *pci_dev; - - struct proc_dir_entry *dir_dev; - - struct net_device *net_dev; - struct iw_statistics wstats; - - struct iw_public_data wireless_data; - - struct tasklet_struct irq_tasklet; - - struct delayed_work reset_work; - struct delayed_work security_work; - struct delayed_work wx_event_work; - struct delayed_work hang_check; - struct delayed_work rf_kill; - struct delayed_work scan_event; - - int user_requested_scan; - - /* Track time in suspend */ - unsigned long suspend_at; - unsigned long suspend_time; - - u32 interrupts; - int tx_interrupts; - int rx_interrupts; - int inta_other; - - spinlock_t low_lock; - struct mutex action_mutex; - struct mutex adapter_mutex; - - wait_queue_head_t wait_command_queue; -}; - -/********************************************************* - * Host Command -> From Driver to FW - *********************************************************/ - -/** - * Host command identifiers - */ -#define HOST_COMPLETE 2 -#define SYSTEM_CONFIG 6 -#define SSID 8 -#define MANDATORY_BSSID 9 -#define AUTHENTICATION_TYPE 10 -#define ADAPTER_ADDRESS 11 -#define PORT_TYPE 12 -#define INTERNATIONAL_MODE 13 -#define CHANNEL 14 -#define RTS_THRESHOLD 15 -#define FRAG_THRESHOLD 16 -#define POWER_MODE 17 -#define TX_RATES 18 -#define BASIC_TX_RATES 19 -#define WEP_KEY_INFO 20 -#define WEP_KEY_INDEX 25 -#define WEP_FLAGS 26 -#define ADD_MULTICAST 27 -#define CLEAR_ALL_MULTICAST 28 -#define BEACON_INTERVAL 29 -#define ATIM_WINDOW 30 -#define CLEAR_STATISTICS 31 -#define SEND 33 -#define TX_POWER_INDEX 36 -#define BROADCAST_SCAN 43 -#define CARD_DISABLE 44 -#define PREFERRED_BSSID 45 -#define SET_SCAN_OPTIONS 46 -#define SCAN_DWELL_TIME 47 -#define SWEEP_TABLE 48 -#define AP_OR_STATION_TABLE 49 -#define GROUP_ORDINALS 50 -#define SHORT_RETRY_LIMIT 51 -#define LONG_RETRY_LIMIT 52 - -#define HOST_PRE_POWER_DOWN 58 -#define CARD_DISABLE_PHY_OFF 61 -#define MSDU_TX_RATES 62 - -/* Rogue AP Detection */ -#define SET_STATION_STAT_BITS 64 -#define CLEAR_STATIONS_STAT_BITS 65 -#define LEAP_ROGUE_MODE 66 //TODO tbw replaced by CFG_LEAP_ROGUE_AP -#define SET_SECURITY_INFORMATION 67 -#define DISASSOCIATION_BSSID 68 -#define SET_WPA_IE 69 - -/* system configuration bit mask: */ -#define IPW_CFG_MONITOR 0x00004 -#define IPW_CFG_PREAMBLE_AUTO 0x00010 -#define IPW_CFG_IBSS_AUTO_START 0x00020 -#define IPW_CFG_LOOPBACK 0x00100 -#define IPW_CFG_ANSWER_BCSSID_PROBE 0x00800 -#define IPW_CFG_BT_SIDEBAND_SIGNAL 0x02000 -#define IPW_CFG_802_1x_ENABLE 0x04000 -#define IPW_CFG_BSS_MASK 0x08000 -#define IPW_CFG_IBSS_MASK 0x10000 - -#define IPW_SCAN_NOASSOCIATE (1<<0) -#define IPW_SCAN_MIXED_CELL (1<<1) -/* RESERVED (1<<2) */ -#define IPW_SCAN_PASSIVE (1<<3) - -#define IPW_NIC_FATAL_ERROR 0x2A7F0 -#define IPW_ERROR_ADDR(x) (x & 0x3FFFF) -#define IPW_ERROR_CODE(x) ((x & 0xFF000000) >> 24) -#define IPW2100_ERR_C3_CORRUPTION (0x10 << 24) -#define IPW2100_ERR_MSG_TIMEOUT (0x11 << 24) -#define IPW2100_ERR_FW_LOAD (0x12 << 24) - -#define IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND 0x200 -#define IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x0D80 - -#define IPW_MEM_HOST_SHARED_RX_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x40) -#define IPW_MEM_HOST_SHARED_RX_STATUS_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x44) -#define IPW_MEM_HOST_SHARED_RX_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x48) -#define IPW_MEM_HOST_SHARED_RX_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0xa0) - -#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_BASE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x00) -#define IPW_MEM_HOST_SHARED_TX_QUEUE_BD_SIZE (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x04) -#define IPW_MEM_HOST_SHARED_TX_QUEUE_READ_INDEX (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x80) - -#define IPW_MEM_HOST_SHARED_RX_WRITE_INDEX \ - (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND + 0x20) - -#define IPW_MEM_HOST_SHARED_TX_QUEUE_WRITE_INDEX \ - (IPW_MEM_SRAM_HOST_INTERRUPT_AREA_LOWER_BOUND) - -#define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_1 (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x180) -#define IPW_MEM_HOST_SHARED_ORDINALS_TABLE_2 (IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND + 0x184) - -#define IPW2100_INTA_TX_TRANSFER (0x00000001) // Bit 0 (LSB) -#define IPW2100_INTA_RX_TRANSFER (0x00000002) // Bit 1 -#define IPW2100_INTA_TX_COMPLETE (0x00000004) // Bit 2 -#define IPW2100_INTA_EVENT_INTERRUPT (0x00000008) // Bit 3 -#define IPW2100_INTA_STATUS_CHANGE (0x00000010) // Bit 4 -#define IPW2100_INTA_BEACON_PERIOD_EXPIRED (0x00000020) // Bit 5 -#define IPW2100_INTA_SLAVE_MODE_HOST_COMMAND_DONE (0x00010000) // Bit 16 -#define IPW2100_INTA_FW_INIT_DONE (0x01000000) // Bit 24 -#define IPW2100_INTA_FW_CALIBRATION_CALC (0x02000000) // Bit 25 -#define IPW2100_INTA_FATAL_ERROR (0x40000000) // Bit 30 -#define IPW2100_INTA_PARITY_ERROR (0x80000000) // Bit 31 (MSB) - -#define IPW_AUX_HOST_RESET_REG_PRINCETON_RESET (0x00000001) -#define IPW_AUX_HOST_RESET_REG_FORCE_NMI (0x00000002) -#define IPW_AUX_HOST_RESET_REG_PCI_HOST_CLUSTER_FATAL_NMI (0x00000004) -#define IPW_AUX_HOST_RESET_REG_CORE_FATAL_NMI (0x00000008) -#define IPW_AUX_HOST_RESET_REG_SW_RESET (0x00000080) -#define IPW_AUX_HOST_RESET_REG_MASTER_DISABLED (0x00000100) -#define IPW_AUX_HOST_RESET_REG_STOP_MASTER (0x00000200) - -#define IPW_AUX_HOST_GP_CNTRL_BIT_CLOCK_READY (0x00000001) // Bit 0 (LSB) -#define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY (0x00000002) // Bit 1 -#define IPW_AUX_HOST_GP_CNTRL_BIT_INIT_DONE (0x00000004) // Bit 2 -#define IPW_AUX_HOST_GP_CNTRL_BITS_SYS_CONFIG (0x000007c0) // Bits 6-10 -#define IPW_AUX_HOST_GP_CNTRL_BIT_BUS_TYPE (0x00000200) // Bit 9 -#define IPW_AUX_HOST_GP_CNTRL_BIT_BAR0_BLOCK_SIZE (0x00000400) // Bit 10 -#define IPW_AUX_HOST_GP_CNTRL_BIT_USB_MODE (0x20000000) // Bit 29 -#define IPW_AUX_HOST_GP_CNTRL_BIT_HOST_FORCES_SYS_CLK (0x40000000) // Bit 30 -#define IPW_AUX_HOST_GP_CNTRL_BIT_FW_FORCES_SYS_CLK (0x80000000) // Bit 31 (MSB) - -#define IPW_BIT_GPIO_GPIO1_MASK 0x0000000C -#define IPW_BIT_GPIO_GPIO3_MASK 0x000000C0 -#define IPW_BIT_GPIO_GPIO1_ENABLE 0x00000008 -#define IPW_BIT_GPIO_RF_KILL 0x00010000 - -#define IPW_BIT_GPIO_LED_OFF 0x00002000 // Bit 13 = 1 - -#define IPW_REG_DOMAIN_0_OFFSET 0x0000 -#define IPW_REG_DOMAIN_1_OFFSET IPW_MEM_SRAM_HOST_SHARED_LOWER_BOUND - -#define IPW_REG_INTA IPW_REG_DOMAIN_0_OFFSET + 0x0008 -#define IPW_REG_INTA_MASK IPW_REG_DOMAIN_0_OFFSET + 0x000C -#define IPW_REG_INDIRECT_ACCESS_ADDRESS IPW_REG_DOMAIN_0_OFFSET + 0x0010 -#define IPW_REG_INDIRECT_ACCESS_DATA IPW_REG_DOMAIN_0_OFFSET + 0x0014 -#define IPW_REG_AUTOINCREMENT_ADDRESS IPW_REG_DOMAIN_0_OFFSET + 0x0018 -#define IPW_REG_AUTOINCREMENT_DATA IPW_REG_DOMAIN_0_OFFSET + 0x001C -#define IPW_REG_RESET_REG IPW_REG_DOMAIN_0_OFFSET + 0x0020 -#define IPW_REG_GP_CNTRL IPW_REG_DOMAIN_0_OFFSET + 0x0024 -#define IPW_REG_GPIO IPW_REG_DOMAIN_0_OFFSET + 0x0030 -#define IPW_REG_FW_TYPE IPW_REG_DOMAIN_1_OFFSET + 0x0188 -#define IPW_REG_FW_VERSION IPW_REG_DOMAIN_1_OFFSET + 0x018C -#define IPW_REG_FW_COMPATIBILITY_VERSION IPW_REG_DOMAIN_1_OFFSET + 0x0190 - -#define IPW_REG_INDIRECT_ADDR_MASK 0x00FFFFFC - -#define IPW_INTERRUPT_MASK 0xC1010013 - -#define IPW2100_CONTROL_REG 0x220000 -#define IPW2100_CONTROL_PHY_OFF 0x8 - -#define IPW2100_COMMAND 0x00300004 -#define IPW2100_COMMAND_PHY_ON 0x0 -#define IPW2100_COMMAND_PHY_OFF 0x1 - -/* in DEBUG_AREA, values of memory always 0xd55555d5 */ -#define IPW_REG_DOA_DEBUG_AREA_START IPW_REG_DOMAIN_0_OFFSET + 0x0090 -#define IPW_REG_DOA_DEBUG_AREA_END IPW_REG_DOMAIN_0_OFFSET + 0x00FF -#define IPW_DATA_DOA_DEBUG_VALUE 0xd55555d5 - -#define IPW_INTERNAL_REGISTER_HALT_AND_RESET 0x003000e0 - -#define IPW_WAIT_CLOCK_STABILIZATION_DELAY 50 // micro seconds -#define IPW_WAIT_RESET_ARC_COMPLETE_DELAY 10 // micro seconds -#define IPW_WAIT_RESET_MASTER_ASSERT_COMPLETE_DELAY 10 // micro seconds - -// BD ring queue read/write difference -#define IPW_BD_QUEUE_W_R_MIN_SPARE 2 - -#define IPW_CACHE_LINE_LENGTH_DEFAULT 0x80 - -#define IPW_CARD_DISABLE_PHY_OFF_COMPLETE_WAIT 100 // 100 milli -#define IPW_PREPARE_POWER_DOWN_COMPLETE_WAIT 100 // 100 milli - -#define IPW_HEADER_802_11_SIZE sizeof(struct libipw_hdr_3addr) -#define IPW_MAX_80211_PAYLOAD_SIZE 2304U -#define IPW_MAX_802_11_PAYLOAD_LENGTH 2312 -#define IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH 1536 -#define IPW_MIN_ACCEPTABLE_RX_FRAME_LENGTH 60 -#define IPW_MAX_ACCEPTABLE_RX_FRAME_LENGTH \ - (IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH + IPW_HEADER_802_11_SIZE - \ - sizeof(struct ethhdr)) - -#define IPW_802_11_FCS_LENGTH 4 -#define IPW_RX_NIC_BUFFER_LENGTH \ - (IPW_MAX_802_11_PAYLOAD_LENGTH + IPW_HEADER_802_11_SIZE + \ - IPW_802_11_FCS_LENGTH) - -#define IPW_802_11_PAYLOAD_OFFSET \ - (sizeof(struct libipw_hdr_3addr) + \ - sizeof(struct libipw_snap_hdr)) - -struct ipw2100_rx { - union { - unsigned char payload[IPW_RX_NIC_BUFFER_LENGTH]; - struct libipw_hdr_4addr header; - u32 status; - struct ipw2100_notification notification; - struct ipw2100_cmd_header command; - } rx_data; -} __packed; - -/* Bit 0-7 are for 802.11b tx rates - . Bit 5-7 are reserved */ -#define TX_RATE_1_MBIT 0x0001 -#define TX_RATE_2_MBIT 0x0002 -#define TX_RATE_5_5_MBIT 0x0004 -#define TX_RATE_11_MBIT 0x0008 -#define TX_RATE_MASK 0x000F -#define DEFAULT_TX_RATES 0x000F - -#define IPW_POWER_MODE_CAM 0x00 //(always on) -#define IPW_POWER_INDEX_1 0x01 -#define IPW_POWER_INDEX_2 0x02 -#define IPW_POWER_INDEX_3 0x03 -#define IPW_POWER_INDEX_4 0x04 -#define IPW_POWER_INDEX_5 0x05 -#define IPW_POWER_AUTO 0x06 -#define IPW_POWER_MASK 0x0F -#define IPW_POWER_ENABLED 0x10 -#define IPW_POWER_LEVEL(x) ((x) & IPW_POWER_MASK) - -#define IPW_TX_POWER_AUTO 0 -#define IPW_TX_POWER_ENHANCED 1 - -#define IPW_TX_POWER_DEFAULT 32 -#define IPW_TX_POWER_MIN 0 -#define IPW_TX_POWER_MAX 16 -#define IPW_TX_POWER_MIN_DBM (-12) -#define IPW_TX_POWER_MAX_DBM 16 - -#define FW_SCAN_DONOT_ASSOCIATE 0x0001 // Dont Attempt to Associate after Scan -#define FW_SCAN_PASSIVE 0x0008 // Force PASSSIVE Scan - -#define REG_MIN_CHANNEL 0 -#define REG_MAX_CHANNEL 14 - -#define REG_CHANNEL_MASK 0x00003FFF -#define IPW_IBSS_11B_DEFAULT_MASK 0x87ff - -#define DIVERSITY_EITHER 0 // Use both antennas -#define DIVERSITY_ANTENNA_A 1 // Use antenna A -#define DIVERSITY_ANTENNA_B 2 // Use antenna B - -#define HOST_COMMAND_WAIT 0 -#define HOST_COMMAND_NO_WAIT 1 - -#define LOCK_NONE 0 -#define LOCK_DRIVER 1 -#define LOCK_FW 2 - -#define TYPE_SWEEP_ORD 0x000D -#define TYPE_IBSS_STTN_ORD 0x000E -#define TYPE_BSS_AP_ORD 0x000F -#define TYPE_RAW_BEACON_ENTRY 0x0010 -#define TYPE_CALIBRATION_DATA 0x0011 -#define TYPE_ROGUE_AP_DATA 0x0012 -#define TYPE_ASSOCIATION_REQUEST 0x0013 -#define TYPE_REASSOCIATION_REQUEST 0x0014 - -#define HW_FEATURE_RFKILL 0x0001 -#define RF_KILLSWITCH_OFF 1 -#define RF_KILLSWITCH_ON 0 - -#define IPW_COMMAND_POOL_SIZE 40 - -#define IPW_START_ORD_TAB_1 1 -#define IPW_START_ORD_TAB_2 1000 - -#define IPW_ORD_TAB_1_ENTRY_SIZE sizeof(u32) - -#define IS_ORDINAL_TABLE_ONE(mgr,id) \ - ((id >= IPW_START_ORD_TAB_1) && (id < mgr->table1_size)) -#define IS_ORDINAL_TABLE_TWO(mgr,id) \ - ((id >= IPW_START_ORD_TAB_2) && (id < (mgr->table2_size + IPW_START_ORD_TAB_2))) - -#define BSS_ID_LENGTH 6 - -// Fixed size data: Ordinal Table 1 -typedef enum _ORDINAL_TABLE_1 { // NS - means Not Supported by FW -// Transmit statistics - IPW_ORD_STAT_TX_HOST_REQUESTS = 1, // # of requested Host Tx's (MSDU) - IPW_ORD_STAT_TX_HOST_COMPLETE, // # of successful Host Tx's (MSDU) - IPW_ORD_STAT_TX_DIR_DATA, // # of successful Directed Tx's (MSDU) - - IPW_ORD_STAT_TX_DIR_DATA1 = 4, // # of successful Directed Tx's (MSDU) @ 1MB - IPW_ORD_STAT_TX_DIR_DATA2, // # of successful Directed Tx's (MSDU) @ 2MB - IPW_ORD_STAT_TX_DIR_DATA5_5, // # of successful Directed Tx's (MSDU) @ 5_5MB - IPW_ORD_STAT_TX_DIR_DATA11, // # of successful Directed Tx's (MSDU) @ 11MB - IPW_ORD_STAT_TX_DIR_DATA22, // # of successful Directed Tx's (MSDU) @ 22MB - - IPW_ORD_STAT_TX_NODIR_DATA1 = 13, // # of successful Non_Directed Tx's (MSDU) @ 1MB - IPW_ORD_STAT_TX_NODIR_DATA2, // # of successful Non_Directed Tx's (MSDU) @ 2MB - IPW_ORD_STAT_TX_NODIR_DATA5_5, // # of successful Non_Directed Tx's (MSDU) @ 5.5MB - IPW_ORD_STAT_TX_NODIR_DATA11, // # of successful Non_Directed Tx's (MSDU) @ 11MB - - IPW_ORD_STAT_NULL_DATA = 21, // # of successful NULL data Tx's - IPW_ORD_STAT_TX_RTS, // # of successful Tx RTS - IPW_ORD_STAT_TX_CTS, // # of successful Tx CTS - IPW_ORD_STAT_TX_ACK, // # of successful Tx ACK - IPW_ORD_STAT_TX_ASSN, // # of successful Association Tx's - IPW_ORD_STAT_TX_ASSN_RESP, // # of successful Association response Tx's - IPW_ORD_STAT_TX_REASSN, // # of successful Reassociation Tx's - IPW_ORD_STAT_TX_REASSN_RESP, // # of successful Reassociation response Tx's - IPW_ORD_STAT_TX_PROBE, // # of probes successfully transmitted - IPW_ORD_STAT_TX_PROBE_RESP, // # of probe responses successfully transmitted - IPW_ORD_STAT_TX_BEACON, // # of tx beacon - IPW_ORD_STAT_TX_ATIM, // # of Tx ATIM - IPW_ORD_STAT_TX_DISASSN, // # of successful Disassociation TX - IPW_ORD_STAT_TX_AUTH, // # of successful Authentication Tx - IPW_ORD_STAT_TX_DEAUTH, // # of successful Deauthentication TX - - IPW_ORD_STAT_TX_TOTAL_BYTES = 41, // Total successful Tx data bytes - IPW_ORD_STAT_TX_RETRIES, // # of Tx retries - IPW_ORD_STAT_TX_RETRY1, // # of Tx retries at 1MBPS - IPW_ORD_STAT_TX_RETRY2, // # of Tx retries at 2MBPS - IPW_ORD_STAT_TX_RETRY5_5, // # of Tx retries at 5.5MBPS - IPW_ORD_STAT_TX_RETRY11, // # of Tx retries at 11MBPS - - IPW_ORD_STAT_TX_FAILURES = 51, // # of Tx Failures - IPW_ORD_STAT_TX_ABORT_AT_HOP, //NS // # of Tx's aborted at hop time - IPW_ORD_STAT_TX_MAX_TRIES_IN_HOP, // # of times max tries in a hop failed - IPW_ORD_STAT_TX_ABORT_LATE_DMA, //NS // # of times tx aborted due to late dma setup - IPW_ORD_STAT_TX_ABORT_STX, //NS // # of times backoff aborted - IPW_ORD_STAT_TX_DISASSN_FAIL, // # of times disassociation failed - IPW_ORD_STAT_TX_ERR_CTS, // # of missed/bad CTS frames - IPW_ORD_STAT_TX_BPDU, //NS // # of spanning tree BPDUs sent - IPW_ORD_STAT_TX_ERR_ACK, // # of tx err due to acks - - // Receive statistics - IPW_ORD_STAT_RX_HOST = 61, // # of packets passed to host - IPW_ORD_STAT_RX_DIR_DATA, // # of directed packets - IPW_ORD_STAT_RX_DIR_DATA1, // # of directed packets at 1MB - IPW_ORD_STAT_RX_DIR_DATA2, // # of directed packets at 2MB - IPW_ORD_STAT_RX_DIR_DATA5_5, // # of directed packets at 5.5MB - IPW_ORD_STAT_RX_DIR_DATA11, // # of directed packets at 11MB - IPW_ORD_STAT_RX_DIR_DATA22, // # of directed packets at 22MB - - IPW_ORD_STAT_RX_NODIR_DATA = 71, // # of nondirected packets - IPW_ORD_STAT_RX_NODIR_DATA1, // # of nondirected packets at 1MB - IPW_ORD_STAT_RX_NODIR_DATA2, // # of nondirected packets at 2MB - IPW_ORD_STAT_RX_NODIR_DATA5_5, // # of nondirected packets at 5.5MB - IPW_ORD_STAT_RX_NODIR_DATA11, // # of nondirected packets at 11MB - - IPW_ORD_STAT_RX_NULL_DATA = 80, // # of null data rx's - IPW_ORD_STAT_RX_POLL, //NS // # of poll rx - IPW_ORD_STAT_RX_RTS, // # of Rx RTS - IPW_ORD_STAT_RX_CTS, // # of Rx CTS - IPW_ORD_STAT_RX_ACK, // # of Rx ACK - IPW_ORD_STAT_RX_CFEND, // # of Rx CF End - IPW_ORD_STAT_RX_CFEND_ACK, // # of Rx CF End + CF Ack - IPW_ORD_STAT_RX_ASSN, // # of Association Rx's - IPW_ORD_STAT_RX_ASSN_RESP, // # of Association response Rx's - IPW_ORD_STAT_RX_REASSN, // # of Reassociation Rx's - IPW_ORD_STAT_RX_REASSN_RESP, // # of Reassociation response Rx's - IPW_ORD_STAT_RX_PROBE, // # of probe Rx's - IPW_ORD_STAT_RX_PROBE_RESP, // # of probe response Rx's - IPW_ORD_STAT_RX_BEACON, // # of Rx beacon - IPW_ORD_STAT_RX_ATIM, // # of Rx ATIM - IPW_ORD_STAT_RX_DISASSN, // # of disassociation Rx - IPW_ORD_STAT_RX_AUTH, // # of authentication Rx - IPW_ORD_STAT_RX_DEAUTH, // # of deauthentication Rx - - IPW_ORD_STAT_RX_TOTAL_BYTES = 101, // Total rx data bytes received - IPW_ORD_STAT_RX_ERR_CRC, // # of packets with Rx CRC error - IPW_ORD_STAT_RX_ERR_CRC1, // # of Rx CRC errors at 1MB - IPW_ORD_STAT_RX_ERR_CRC2, // # of Rx CRC errors at 2MB - IPW_ORD_STAT_RX_ERR_CRC5_5, // # of Rx CRC errors at 5.5MB - IPW_ORD_STAT_RX_ERR_CRC11, // # of Rx CRC errors at 11MB - - IPW_ORD_STAT_RX_DUPLICATE1 = 112, // # of duplicate rx packets at 1MB - IPW_ORD_STAT_RX_DUPLICATE2, // # of duplicate rx packets at 2MB - IPW_ORD_STAT_RX_DUPLICATE5_5, // # of duplicate rx packets at 5.5MB - IPW_ORD_STAT_RX_DUPLICATE11, // # of duplicate rx packets at 11MB - IPW_ORD_STAT_RX_DUPLICATE = 119, // # of duplicate rx packets - - IPW_ORD_PERS_DB_LOCK = 120, // # locking fw permanent db - IPW_ORD_PERS_DB_SIZE, // # size of fw permanent db - IPW_ORD_PERS_DB_ADDR, // # address of fw permanent db - IPW_ORD_STAT_RX_INVALID_PROTOCOL, // # of rx frames with invalid protocol - IPW_ORD_SYS_BOOT_TIME, // # Boot time - IPW_ORD_STAT_RX_NO_BUFFER, // # of rx frames rejected due to no buffer - IPW_ORD_STAT_RX_ABORT_LATE_DMA, //NS // # of rx frames rejected due to dma setup too late - IPW_ORD_STAT_RX_ABORT_AT_HOP, //NS // # of rx frames aborted due to hop - IPW_ORD_STAT_RX_MISSING_FRAG, // # of rx frames dropped due to missing fragment - IPW_ORD_STAT_RX_ORPHAN_FRAG, // # of rx frames dropped due to non-sequential fragment - IPW_ORD_STAT_RX_ORPHAN_FRAME, // # of rx frames dropped due to unmatched 1st frame - IPW_ORD_STAT_RX_FRAG_AGEOUT, // # of rx frames dropped due to uncompleted frame - IPW_ORD_STAT_RX_BAD_SSID, //NS // Bad SSID (unused) - IPW_ORD_STAT_RX_ICV_ERRORS, // # of ICV errors during decryption - -// PSP Statistics - IPW_ORD_STAT_PSP_SUSPENSION = 137, // # of times adapter suspended - IPW_ORD_STAT_PSP_BCN_TIMEOUT, // # of beacon timeout - IPW_ORD_STAT_PSP_POLL_TIMEOUT, // # of poll response timeouts - IPW_ORD_STAT_PSP_NONDIR_TIMEOUT, // # of timeouts waiting for last broadcast/muticast pkt - IPW_ORD_STAT_PSP_RX_DTIMS, // # of PSP DTIMs received - IPW_ORD_STAT_PSP_RX_TIMS, // # of PSP TIMs received - IPW_ORD_STAT_PSP_STATION_ID, // PSP Station ID - -// Association and roaming - IPW_ORD_LAST_ASSN_TIME = 147, // RTC time of last association - IPW_ORD_STAT_PERCENT_MISSED_BCNS, // current calculation of % missed beacons - IPW_ORD_STAT_PERCENT_RETRIES, // current calculation of % missed tx retries - IPW_ORD_ASSOCIATED_AP_PTR, // If associated, this is ptr to the associated - // AP table entry. set to 0 if not associated - IPW_ORD_AVAILABLE_AP_CNT, // # of AP's decsribed in the AP table - IPW_ORD_AP_LIST_PTR, // Ptr to list of available APs - IPW_ORD_STAT_AP_ASSNS, // # of associations - IPW_ORD_STAT_ASSN_FAIL, // # of association failures - IPW_ORD_STAT_ASSN_RESP_FAIL, // # of failuresdue to response fail - IPW_ORD_STAT_FULL_SCANS, // # of full scans - - IPW_ORD_CARD_DISABLED, // # Card Disabled - IPW_ORD_STAT_ROAM_INHIBIT, // # of times roaming was inhibited due to ongoing activity - IPW_FILLER_40, - IPW_ORD_RSSI_AT_ASSN = 160, // RSSI of associated AP at time of association - IPW_ORD_STAT_ASSN_CAUSE1, // # of reassociations due to no tx from AP in last N - // hops or no prob_ responses in last 3 minutes - IPW_ORD_STAT_ASSN_CAUSE2, // # of reassociations due to poor tx/rx quality - IPW_ORD_STAT_ASSN_CAUSE3, // # of reassociations due to tx/rx quality with excessive - // load at the AP - IPW_ORD_STAT_ASSN_CAUSE4, // # of reassociations due to AP RSSI level fell below - // eligible group - IPW_ORD_STAT_ASSN_CAUSE5, // # of reassociations due to load leveling - IPW_ORD_STAT_ASSN_CAUSE6, //NS // # of reassociations due to dropped by Ap - IPW_FILLER_41, - IPW_FILLER_42, - IPW_FILLER_43, - IPW_ORD_STAT_AUTH_FAIL, // # of times authentication failed - IPW_ORD_STAT_AUTH_RESP_FAIL, // # of times authentication response failed - IPW_ORD_STATION_TABLE_CNT, // # of entries in association table - -// Other statistics - IPW_ORD_RSSI_AVG_CURR = 173, // Current avg RSSI - IPW_ORD_STEST_RESULTS_CURR, //NS // Current self test results word - IPW_ORD_STEST_RESULTS_CUM, //NS // Cummulative self test results word - IPW_ORD_SELF_TEST_STATUS, //NS // - IPW_ORD_POWER_MGMT_MODE, // Power mode - 0=CAM, 1=PSP - IPW_ORD_POWER_MGMT_INDEX, //NS // - IPW_ORD_COUNTRY_CODE, // IEEE country code as recv'd from beacon - IPW_ORD_COUNTRY_CHANNELS, // channels supported by country -// IPW_ORD_COUNTRY_CHANNELS: -// For 11b the lower 2-byte are used for channels from 1-14 -// and the higher 2-byte are not used. - IPW_ORD_RESET_CNT, // # of adapter resets (warm) - IPW_ORD_BEACON_INTERVAL, // Beacon interval - - IPW_ORD_PRINCETON_VERSION = 184, //NS // Princeton Version - IPW_ORD_ANTENNA_DIVERSITY, // TRUE if antenna diversity is disabled - IPW_ORD_CCA_RSSI, //NS // CCA RSSI value (factory programmed) - IPW_ORD_STAT_EEPROM_UPDATE, //NS // # of times config EEPROM updated - IPW_ORD_DTIM_PERIOD, // # of beacon intervals between DTIMs - IPW_ORD_OUR_FREQ, // current radio freq lower digits - channel ID - - IPW_ORD_RTC_TIME = 190, // current RTC time - IPW_ORD_PORT_TYPE, // operating mode - IPW_ORD_CURRENT_TX_RATE, // current tx rate - IPW_ORD_SUPPORTED_RATES, // Bitmap of supported tx rates - IPW_ORD_ATIM_WINDOW, // current ATIM Window - IPW_ORD_BASIC_RATES, // bitmap of basic tx rates - IPW_ORD_NIC_HIGHEST_RATE, // bitmap of basic tx rates - IPW_ORD_AP_HIGHEST_RATE, // bitmap of basic tx rates - IPW_ORD_CAPABILITIES, // Management frame capability field - IPW_ORD_AUTH_TYPE, // Type of authentication - IPW_ORD_RADIO_TYPE, // Adapter card platform type - IPW_ORD_RTS_THRESHOLD = 201, // Min length of packet after which RTS handshaking is used - IPW_ORD_INT_MODE, // International mode - IPW_ORD_FRAGMENTATION_THRESHOLD, // protocol frag threshold - IPW_ORD_EEPROM_SRAM_DB_BLOCK_START_ADDRESS, // EEPROM offset in SRAM - IPW_ORD_EEPROM_SRAM_DB_BLOCK_SIZE, // EEPROM size in SRAM - IPW_ORD_EEPROM_SKU_CAPABILITY, // EEPROM SKU Capability 206 = - IPW_ORD_EEPROM_IBSS_11B_CHANNELS, // EEPROM IBSS 11b channel set - - IPW_ORD_MAC_VERSION = 209, // MAC Version - IPW_ORD_MAC_REVISION, // MAC Revision - IPW_ORD_RADIO_VERSION, // Radio Version - IPW_ORD_NIC_MANF_DATE_TIME, // MANF Date/Time STAMP - IPW_ORD_UCODE_VERSION, // Ucode Version - IPW_ORD_HW_RF_SWITCH_STATE = 214, // HW RF Kill Switch State -} ORDINALTABLE1; - -// ordinal table 2 -// Variable length data: -#define IPW_FIRST_VARIABLE_LENGTH_ORDINAL 1001 - -typedef enum _ORDINAL_TABLE_2 { // NS - means Not Supported by FW - IPW_ORD_STAT_BASE = 1000, // contains number of variable ORDs - IPW_ORD_STAT_ADAPTER_MAC = 1001, // 6 bytes: our adapter MAC address - IPW_ORD_STAT_PREFERRED_BSSID = 1002, // 6 bytes: BSSID of the preferred AP - IPW_ORD_STAT_MANDATORY_BSSID = 1003, // 6 bytes: BSSID of the mandatory AP - IPW_FILL_1, //NS // - IPW_ORD_STAT_COUNTRY_TEXT = 1005, // 36 bytes: Country name text, First two bytes are Country code - IPW_ORD_STAT_ASSN_SSID = 1006, // 32 bytes: ESSID String - IPW_ORD_STATION_TABLE = 1007, // ? bytes: Station/AP table (via Direct SSID Scans) - IPW_ORD_STAT_SWEEP_TABLE = 1008, // ? bytes: Sweep/Host Table table (via Broadcast Scans) - IPW_ORD_STAT_ROAM_LOG = 1009, // ? bytes: Roaming log - IPW_ORD_STAT_RATE_LOG = 1010, //NS // 0 bytes: Rate log - IPW_ORD_STAT_FIFO = 1011, //NS // 0 bytes: Fifo buffer data structures - IPW_ORD_STAT_FW_VER_NUM = 1012, // 14 bytes: fw version ID string as in (a.bb.ccc; "0.08.011") - IPW_ORD_STAT_FW_DATE = 1013, // 14 bytes: fw date string (mmm dd yyyy; "Mar 13 2002") - IPW_ORD_STAT_ASSN_AP_BSSID = 1014, // 6 bytes: MAC address of associated AP - IPW_ORD_STAT_DEBUG = 1015, //NS // ? bytes: - IPW_ORD_STAT_NIC_BPA_NUM = 1016, // 11 bytes: NIC BPA number in ASCII - IPW_ORD_STAT_UCODE_DATE = 1017, // 5 bytes: uCode date - IPW_ORD_SECURITY_NGOTIATION_RESULT = 1018, -} ORDINALTABLE2; // NS - means Not Supported by FW - -#define IPW_LAST_VARIABLE_LENGTH_ORDINAL 1018 - -#ifndef WIRELESS_SPY -#define WIRELESS_SPY // enable iwspy support -#endif - -#define IPW_HOST_FW_SHARED_AREA0 0x0002f200 -#define IPW_HOST_FW_SHARED_AREA0_END 0x0002f510 // 0x310 bytes - -#define IPW_HOST_FW_SHARED_AREA1 0x0002f610 -#define IPW_HOST_FW_SHARED_AREA1_END 0x0002f630 // 0x20 bytes - -#define IPW_HOST_FW_SHARED_AREA2 0x0002fa00 -#define IPW_HOST_FW_SHARED_AREA2_END 0x0002fa20 // 0x20 bytes - -#define IPW_HOST_FW_SHARED_AREA3 0x0002fc00 -#define IPW_HOST_FW_SHARED_AREA3_END 0x0002fc10 // 0x10 bytes - -#define IPW_HOST_FW_INTERRUPT_AREA 0x0002ff80 -#define IPW_HOST_FW_INTERRUPT_AREA_END 0x00030000 // 0x80 bytes - -struct ipw2100_fw_chunk { - unsigned char *buf; - long len; - long pos; - struct list_head list; -}; - -struct ipw2100_fw_chunk_set { - const void *data; - unsigned long size; -}; - -struct ipw2100_fw { - int version; - struct ipw2100_fw_chunk_set fw; - struct ipw2100_fw_chunk_set uc; - const struct firmware *fw_entry; -}; - -#define MAX_FW_VERSION_LEN 14 - -#endif /* _IPW2100_H */ diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c deleted file mode 100644 index ed0adaf1eec4..000000000000 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ /dev/null @@ -1,12061 +0,0 @@ -/****************************************************************************** - - Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. - - 802.11 status code portion of this file from ethereal-0.10.6: - Copyright 2000, Axis Communications AB - Ethereal - Network traffic analyzer - By Gerald Combs - Copyright 1998 Gerald Combs - - This program is free software; you can redistribute it and/or modify it - under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - The full GNU General Public License is included in this distribution in the - file called LICENSE. - - Contact Information: - Intel Linux Wireless - Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - -******************************************************************************/ - -#include -#include -#include -#include "ipw2200.h" -#include "ipw.h" - - -#ifndef KBUILD_EXTMOD -#define VK "k" -#else -#define VK -#endif - -#ifdef CONFIG_IPW2200_DEBUG -#define VD "d" -#else -#define VD -#endif - -#ifdef CONFIG_IPW2200_MONITOR -#define VM "m" -#else -#define VM -#endif - -#ifdef CONFIG_IPW2200_PROMISCUOUS -#define VP "p" -#else -#define VP -#endif - -#ifdef CONFIG_IPW2200_RADIOTAP -#define VR "r" -#else -#define VR -#endif - -#ifdef CONFIG_IPW2200_QOS -#define VQ "q" -#else -#define VQ -#endif - -#define IPW2200_VERSION "1.2.2" VK VD VM VP VR VQ -#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2200/2915 Network Driver" -#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" -#define DRV_VERSION IPW2200_VERSION - -#define ETH_P_80211_STATS (ETH_P_80211_RAW + 1) - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); -MODULE_AUTHOR(DRV_COPYRIGHT); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("ipw2200-ibss.fw"); -#ifdef CONFIG_IPW2200_MONITOR -MODULE_FIRMWARE("ipw2200-sniffer.fw"); -#endif -MODULE_FIRMWARE("ipw2200-bss.fw"); - -static int cmdlog = 0; -static int debug = 0; -static int default_channel = 0; -static int network_mode = 0; - -static u32 ipw_debug_level; -static int associate; -static int auto_create = 1; -static int led_support = 1; -static int disable = 0; -static int bt_coexist = 0; -static int hwcrypto = 0; -static int roaming = 1; -static const char ipw_modes[] = { - 'a', 'b', 'g', '?' -}; -static int antenna = CFG_SYS_ANTENNA_BOTH; - -#ifdef CONFIG_IPW2200_PROMISCUOUS -static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */ -#endif - -static struct ieee80211_rate ipw2200_rates[] = { - { .bitrate = 10 }, - { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 60 }, - { .bitrate = 90 }, - { .bitrate = 120 }, - { .bitrate = 180 }, - { .bitrate = 240 }, - { .bitrate = 360 }, - { .bitrate = 480 }, - { .bitrate = 540 } -}; - -#define ipw2200_a_rates (ipw2200_rates + 4) -#define ipw2200_num_a_rates 8 -#define ipw2200_bg_rates (ipw2200_rates + 0) -#define ipw2200_num_bg_rates 12 - -/* Ugly macro to convert literal channel numbers into their mhz equivalents - * There are certianly some conditions that will break this (like feeding it '30') - * but they shouldn't arise since nothing talks on channel 30. */ -#define ieee80211chan2mhz(x) \ - (((x) <= 14) ? \ - (((x) == 14) ? 2484 : ((x) * 5) + 2407) : \ - ((x) + 1000) * 5) - -#ifdef CONFIG_IPW2200_QOS -static int qos_enable = 0; -static int qos_burst_enable = 0; -static int qos_no_ack_mask = 0; -static int burst_duration_CCK = 0; -static int burst_duration_OFDM = 0; - -static struct libipw_qos_parameters def_qos_parameters_OFDM = { - {QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM, - QOS_TX3_CW_MIN_OFDM}, - {QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM, - QOS_TX3_CW_MAX_OFDM}, - {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS}, - {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM}, - {QOS_TX0_TXOP_LIMIT_OFDM, QOS_TX1_TXOP_LIMIT_OFDM, - QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM} -}; - -static struct libipw_qos_parameters def_qos_parameters_CCK = { - {QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK, - QOS_TX3_CW_MIN_CCK}, - {QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK, - QOS_TX3_CW_MAX_CCK}, - {QOS_TX0_AIFS, QOS_TX1_AIFS, QOS_TX2_AIFS, QOS_TX3_AIFS}, - {QOS_TX0_ACM, QOS_TX1_ACM, QOS_TX2_ACM, QOS_TX3_ACM}, - {QOS_TX0_TXOP_LIMIT_CCK, QOS_TX1_TXOP_LIMIT_CCK, QOS_TX2_TXOP_LIMIT_CCK, - QOS_TX3_TXOP_LIMIT_CCK} -}; - -static struct libipw_qos_parameters def_parameters_OFDM = { - {DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM, - DEF_TX3_CW_MIN_OFDM}, - {DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM, - DEF_TX3_CW_MAX_OFDM}, - {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS}, - {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM}, - {DEF_TX0_TXOP_LIMIT_OFDM, DEF_TX1_TXOP_LIMIT_OFDM, - DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM} -}; - -static struct libipw_qos_parameters def_parameters_CCK = { - {DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK, - DEF_TX3_CW_MIN_CCK}, - {DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK, - DEF_TX3_CW_MAX_CCK}, - {DEF_TX0_AIFS, DEF_TX1_AIFS, DEF_TX2_AIFS, DEF_TX3_AIFS}, - {DEF_TX0_ACM, DEF_TX1_ACM, DEF_TX2_ACM, DEF_TX3_ACM}, - {DEF_TX0_TXOP_LIMIT_CCK, DEF_TX1_TXOP_LIMIT_CCK, DEF_TX2_TXOP_LIMIT_CCK, - DEF_TX3_TXOP_LIMIT_CCK} -}; - -static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; - -static int from_priority_to_tx_queue[] = { - IPW_TX_QUEUE_1, IPW_TX_QUEUE_2, IPW_TX_QUEUE_2, IPW_TX_QUEUE_1, - IPW_TX_QUEUE_3, IPW_TX_QUEUE_3, IPW_TX_QUEUE_4, IPW_TX_QUEUE_4 -}; - -static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv); - -static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters - *qos_param); -static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element - *qos_param); -#endif /* CONFIG_IPW2200_QOS */ - -static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev); -static void ipw_remove_current_network(struct ipw_priv *priv); -static void ipw_rx(struct ipw_priv *priv); -static int ipw_queue_tx_reclaim(struct ipw_priv *priv, - struct clx2_tx_queue *txq, int qindex); -static int ipw_queue_reset(struct ipw_priv *priv); - -static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf, - int len, int sync); - -static void ipw_tx_queue_free(struct ipw_priv *); - -static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *); -static void ipw_rx_queue_free(struct ipw_priv *, struct ipw_rx_queue *); -static void ipw_rx_queue_replenish(void *); -static int ipw_up(struct ipw_priv *); -static void ipw_bg_up(struct work_struct *work); -static void ipw_down(struct ipw_priv *); -static void ipw_bg_down(struct work_struct *work); -static int ipw_config(struct ipw_priv *); -static int init_supported_rates(struct ipw_priv *priv, - struct ipw_supported_rates *prates); -static void ipw_set_hwcrypto_keys(struct ipw_priv *); -static void ipw_send_wep_keys(struct ipw_priv *, int); - -static int snprint_line(char *buf, size_t count, - const u8 * data, u32 len, u32 ofs) -{ - int out, i, j, l; - char c; - - out = snprintf(buf, count, "%08X", ofs); - - for (l = 0, i = 0; i < 2; i++) { - out += snprintf(buf + out, count - out, " "); - for (j = 0; j < 8 && l < len; j++, l++) - out += snprintf(buf + out, count - out, "%02X ", - data[(i * 8 + j)]); - for (; j < 8; j++) - out += snprintf(buf + out, count - out, " "); - } - - out += snprintf(buf + out, count - out, " "); - for (l = 0, i = 0; i < 2; i++) { - out += snprintf(buf + out, count - out, " "); - for (j = 0; j < 8 && l < len; j++, l++) { - c = data[(i * 8 + j)]; - if (!isascii(c) || !isprint(c)) - c = '.'; - - out += snprintf(buf + out, count - out, "%c", c); - } - - for (; j < 8; j++) - out += snprintf(buf + out, count - out, " "); - } - - return out; -} - -static void printk_buf(int level, const u8 * data, u32 len) -{ - char line[81]; - u32 ofs = 0; - if (!(ipw_debug_level & level)) - return; - - while (len) { - snprint_line(line, sizeof(line), &data[ofs], - min(len, 16U), ofs); - printk(KERN_DEBUG "%s\n", line); - ofs += 16; - len -= min(len, 16U); - } -} - -static int snprintk_buf(u8 * output, size_t size, const u8 * data, size_t len) -{ - size_t out = size; - u32 ofs = 0; - int total = 0; - - while (size && len) { - out = snprint_line(output, size, &data[ofs], - min_t(size_t, len, 16U), ofs); - - ofs += 16; - output += out; - size -= out; - len -= min_t(size_t, len, 16U); - total += out; - } - return total; -} - -/* alias for 32-bit indirect read (for SRAM/reg above 4K), with debug wrapper */ -static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg); -#define ipw_read_reg32(a, b) _ipw_read_reg32(a, b) - -/* alias for 8-bit indirect read (for SRAM/reg above 4K), with debug wrapper */ -static u8 _ipw_read_reg8(struct ipw_priv *ipw, u32 reg); -#define ipw_read_reg8(a, b) _ipw_read_reg8(a, b) - -/* 8-bit indirect write (for SRAM/reg above 4K), with debug wrapper */ -static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value); -static inline void ipw_write_reg8(struct ipw_priv *a, u32 b, u8 c) -{ - IPW_DEBUG_IO("%s %d: write_indirect8(0x%08X, 0x%08X)\n", __FILE__, - __LINE__, (u32) (b), (u32) (c)); - _ipw_write_reg8(a, b, c); -} - -/* 16-bit indirect write (for SRAM/reg above 4K), with debug wrapper */ -static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value); -static inline void ipw_write_reg16(struct ipw_priv *a, u32 b, u16 c) -{ - IPW_DEBUG_IO("%s %d: write_indirect16(0x%08X, 0x%08X)\n", __FILE__, - __LINE__, (u32) (b), (u32) (c)); - _ipw_write_reg16(a, b, c); -} - -/* 32-bit indirect write (for SRAM/reg above 4K), with debug wrapper */ -static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value); -static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c) -{ - IPW_DEBUG_IO("%s %d: write_indirect32(0x%08X, 0x%08X)\n", __FILE__, - __LINE__, (u32) (b), (u32) (c)); - _ipw_write_reg32(a, b, c); -} - -/* 8-bit direct write (low 4K) */ -static inline void _ipw_write8(struct ipw_priv *ipw, unsigned long ofs, - u8 val) -{ - writeb(val, ipw->hw_base + ofs); -} - -/* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */ -#define ipw_write8(ipw, ofs, val) do { \ - IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, \ - __LINE__, (u32)(ofs), (u32)(val)); \ - _ipw_write8(ipw, ofs, val); \ -} while (0) - -/* 16-bit direct write (low 4K) */ -static inline void _ipw_write16(struct ipw_priv *ipw, unsigned long ofs, - u16 val) -{ - writew(val, ipw->hw_base + ofs); -} - -/* 16-bit direct write (for low 4K of SRAM/regs), with debug wrapper */ -#define ipw_write16(ipw, ofs, val) do { \ - IPW_DEBUG_IO("%s %d: write_direct16(0x%08X, 0x%08X)\n", __FILE__, \ - __LINE__, (u32)(ofs), (u32)(val)); \ - _ipw_write16(ipw, ofs, val); \ -} while (0) - -/* 32-bit direct write (low 4K) */ -static inline void _ipw_write32(struct ipw_priv *ipw, unsigned long ofs, - u32 val) -{ - writel(val, ipw->hw_base + ofs); -} - -/* 32-bit direct write (for low 4K of SRAM/regs), with debug wrapper */ -#define ipw_write32(ipw, ofs, val) do { \ - IPW_DEBUG_IO("%s %d: write_direct32(0x%08X, 0x%08X)\n", __FILE__, \ - __LINE__, (u32)(ofs), (u32)(val)); \ - _ipw_write32(ipw, ofs, val); \ -} while (0) - -/* 8-bit direct read (low 4K) */ -static inline u8 _ipw_read8(struct ipw_priv *ipw, unsigned long ofs) -{ - return readb(ipw->hw_base + ofs); -} - -/* alias to 8-bit direct read (low 4K of SRAM/regs), with debug wrapper */ -#define ipw_read8(ipw, ofs) ({ \ - IPW_DEBUG_IO("%s %d: read_direct8(0x%08X)\n", __FILE__, __LINE__, \ - (u32)(ofs)); \ - _ipw_read8(ipw, ofs); \ -}) - -/* 16-bit direct read (low 4K) */ -static inline u16 _ipw_read16(struct ipw_priv *ipw, unsigned long ofs) -{ - return readw(ipw->hw_base + ofs); -} - -/* alias to 16-bit direct read (low 4K of SRAM/regs), with debug wrapper */ -#define ipw_read16(ipw, ofs) ({ \ - IPW_DEBUG_IO("%s %d: read_direct16(0x%08X)\n", __FILE__, __LINE__, \ - (u32)(ofs)); \ - _ipw_read16(ipw, ofs); \ -}) - -/* 32-bit direct read (low 4K) */ -static inline u32 _ipw_read32(struct ipw_priv *ipw, unsigned long ofs) -{ - return readl(ipw->hw_base + ofs); -} - -/* alias to 32-bit direct read (low 4K of SRAM/regs), with debug wrapper */ -#define ipw_read32(ipw, ofs) ({ \ - IPW_DEBUG_IO("%s %d: read_direct32(0x%08X)\n", __FILE__, __LINE__, \ - (u32)(ofs)); \ - _ipw_read32(ipw, ofs); \ -}) - -static void _ipw_read_indirect(struct ipw_priv *, u32, u8 *, int); -/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */ -#define ipw_read_indirect(a, b, c, d) ({ \ - IPW_DEBUG_IO("%s %d: read_indirect(0x%08X) %u bytes\n", __FILE__, \ - __LINE__, (u32)(b), (u32)(d)); \ - _ipw_read_indirect(a, b, c, d); \ -}) - -/* alias to multi-byte read (SRAM/regs above 4K), with debug wrapper */ -static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * data, - int num); -#define ipw_write_indirect(a, b, c, d) do { \ - IPW_DEBUG_IO("%s %d: write_indirect(0x%08X) %u bytes\n", __FILE__, \ - __LINE__, (u32)(b), (u32)(d)); \ - _ipw_write_indirect(a, b, c, d); \ -} while (0) - -/* 32-bit indirect write (above 4K) */ -static void _ipw_write_reg32(struct ipw_priv *priv, u32 reg, u32 value) -{ - IPW_DEBUG_IO(" %p : reg = 0x%8X : value = 0x%8X\n", priv, reg, value); - _ipw_write32(priv, IPW_INDIRECT_ADDR, reg); - _ipw_write32(priv, IPW_INDIRECT_DATA, value); -} - -/* 8-bit indirect write (above 4K) */ -static void _ipw_write_reg8(struct ipw_priv *priv, u32 reg, u8 value) -{ - u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */ - u32 dif_len = reg - aligned_addr; - - IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value); - _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); - _ipw_write8(priv, IPW_INDIRECT_DATA + dif_len, value); -} - -/* 16-bit indirect write (above 4K) */ -static void _ipw_write_reg16(struct ipw_priv *priv, u32 reg, u16 value) -{ - u32 aligned_addr = reg & IPW_INDIRECT_ADDR_MASK; /* dword align */ - u32 dif_len = (reg - aligned_addr) & (~0x1ul); - - IPW_DEBUG_IO(" reg = 0x%8X : value = 0x%8X\n", reg, value); - _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); - _ipw_write16(priv, IPW_INDIRECT_DATA + dif_len, value); -} - -/* 8-bit indirect read (above 4K) */ -static u8 _ipw_read_reg8(struct ipw_priv *priv, u32 reg) -{ - u32 word; - _ipw_write32(priv, IPW_INDIRECT_ADDR, reg & IPW_INDIRECT_ADDR_MASK); - IPW_DEBUG_IO(" reg = 0x%8X :\n", reg); - word = _ipw_read32(priv, IPW_INDIRECT_DATA); - return (word >> ((reg & 0x3) * 8)) & 0xff; -} - -/* 32-bit indirect read (above 4K) */ -static u32 _ipw_read_reg32(struct ipw_priv *priv, u32 reg) -{ - u32 value; - - IPW_DEBUG_IO("%p : reg = 0x%08x\n", priv, reg); - - _ipw_write32(priv, IPW_INDIRECT_ADDR, reg); - value = _ipw_read32(priv, IPW_INDIRECT_DATA); - IPW_DEBUG_IO(" reg = 0x%4X : value = 0x%4x\n", reg, value); - return value; -} - -/* General purpose, no alignment requirement, iterative (multi-byte) read, */ -/* for area above 1st 4K of SRAM/reg space */ -static void _ipw_read_indirect(struct ipw_priv *priv, u32 addr, u8 * buf, - int num) -{ - u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */ - u32 dif_len = addr - aligned_addr; - u32 i; - - IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num); - - if (num <= 0) { - return; - } - - /* Read the first dword (or portion) byte by byte */ - if (unlikely(dif_len)) { - _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); - /* Start reading at aligned_addr + dif_len */ - for (i = dif_len; ((i < 4) && (num > 0)); i++, num--) - *buf++ = _ipw_read8(priv, IPW_INDIRECT_DATA + i); - aligned_addr += 4; - } - - /* Read all of the middle dwords as dwords, with auto-increment */ - _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr); - for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4) - *(u32 *) buf = _ipw_read32(priv, IPW_AUTOINC_DATA); - - /* Read the last dword (or portion) byte by byte */ - if (unlikely(num)) { - _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); - for (i = 0; num > 0; i++, num--) - *buf++ = ipw_read8(priv, IPW_INDIRECT_DATA + i); - } -} - -/* General purpose, no alignment requirement, iterative (multi-byte) write, */ -/* for area above 1st 4K of SRAM/reg space */ -static void _ipw_write_indirect(struct ipw_priv *priv, u32 addr, u8 * buf, - int num) -{ - u32 aligned_addr = addr & IPW_INDIRECT_ADDR_MASK; /* dword align */ - u32 dif_len = addr - aligned_addr; - u32 i; - - IPW_DEBUG_IO("addr = %i, buf = %p, num = %i\n", addr, buf, num); - - if (num <= 0) { - return; - } - - /* Write the first dword (or portion) byte by byte */ - if (unlikely(dif_len)) { - _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); - /* Start writing at aligned_addr + dif_len */ - for (i = dif_len; ((i < 4) && (num > 0)); i++, num--, buf++) - _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf); - aligned_addr += 4; - } - - /* Write all of the middle dwords as dwords, with auto-increment */ - _ipw_write32(priv, IPW_AUTOINC_ADDR, aligned_addr); - for (; num >= 4; buf += 4, aligned_addr += 4, num -= 4) - _ipw_write32(priv, IPW_AUTOINC_DATA, *(u32 *) buf); - - /* Write the last dword (or portion) byte by byte */ - if (unlikely(num)) { - _ipw_write32(priv, IPW_INDIRECT_ADDR, aligned_addr); - for (i = 0; num > 0; i++, num--, buf++) - _ipw_write8(priv, IPW_INDIRECT_DATA + i, *buf); - } -} - -/* General purpose, no alignment requirement, iterative (multi-byte) write, */ -/* for 1st 4K of SRAM/regs space */ -static void ipw_write_direct(struct ipw_priv *priv, u32 addr, void *buf, - int num) -{ - memcpy_toio((priv->hw_base + addr), buf, num); -} - -/* Set bit(s) in low 4K of SRAM/regs */ -static inline void ipw_set_bit(struct ipw_priv *priv, u32 reg, u32 mask) -{ - ipw_write32(priv, reg, ipw_read32(priv, reg) | mask); -} - -/* Clear bit(s) in low 4K of SRAM/regs */ -static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask) -{ - ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask); -} - -static inline void __ipw_enable_interrupts(struct ipw_priv *priv) -{ - if (priv->status & STATUS_INT_ENABLED) - return; - priv->status |= STATUS_INT_ENABLED; - ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL); -} - -static inline void __ipw_disable_interrupts(struct ipw_priv *priv) -{ - if (!(priv->status & STATUS_INT_ENABLED)) - return; - priv->status &= ~STATUS_INT_ENABLED; - ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL); -} - -static inline void ipw_enable_interrupts(struct ipw_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->irq_lock, flags); - __ipw_enable_interrupts(priv); - spin_unlock_irqrestore(&priv->irq_lock, flags); -} - -static inline void ipw_disable_interrupts(struct ipw_priv *priv) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->irq_lock, flags); - __ipw_disable_interrupts(priv); - spin_unlock_irqrestore(&priv->irq_lock, flags); -} - -static char *ipw_error_desc(u32 val) -{ - switch (val) { - case IPW_FW_ERROR_OK: - return "ERROR_OK"; - case IPW_FW_ERROR_FAIL: - return "ERROR_FAIL"; - case IPW_FW_ERROR_MEMORY_UNDERFLOW: - return "MEMORY_UNDERFLOW"; - case IPW_FW_ERROR_MEMORY_OVERFLOW: - return "MEMORY_OVERFLOW"; - case IPW_FW_ERROR_BAD_PARAM: - return "BAD_PARAM"; - case IPW_FW_ERROR_BAD_CHECKSUM: - return "BAD_CHECKSUM"; - case IPW_FW_ERROR_NMI_INTERRUPT: - return "NMI_INTERRUPT"; - case IPW_FW_ERROR_BAD_DATABASE: - return "BAD_DATABASE"; - case IPW_FW_ERROR_ALLOC_FAIL: - return "ALLOC_FAIL"; - case IPW_FW_ERROR_DMA_UNDERRUN: - return "DMA_UNDERRUN"; - case IPW_FW_ERROR_DMA_STATUS: - return "DMA_STATUS"; - case IPW_FW_ERROR_DINO_ERROR: - return "DINO_ERROR"; - case IPW_FW_ERROR_EEPROM_ERROR: - return "EEPROM_ERROR"; - case IPW_FW_ERROR_SYSASSERT: - return "SYSASSERT"; - case IPW_FW_ERROR_FATAL_ERROR: - return "FATAL_ERROR"; - default: - return "UNKNOWN_ERROR"; - } -} - -static void ipw_dump_error_log(struct ipw_priv *priv, - struct ipw_fw_error *error) -{ - u32 i; - - if (!error) { - IPW_ERROR("Error allocating and capturing error log. " - "Nothing to dump.\n"); - return; - } - - IPW_ERROR("Start IPW Error Log Dump:\n"); - IPW_ERROR("Status: 0x%08X, Config: %08X\n", - error->status, error->config); - - for (i = 0; i < error->elem_len; i++) - IPW_ERROR("%s %i 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", - ipw_error_desc(error->elem[i].desc), - error->elem[i].time, - error->elem[i].blink1, - error->elem[i].blink2, - error->elem[i].link1, - error->elem[i].link2, error->elem[i].data); - for (i = 0; i < error->log_len; i++) - IPW_ERROR("%i\t0x%08x\t%i\n", - error->log[i].time, - error->log[i].data, error->log[i].event); -} - -static inline int ipw_is_init(struct ipw_priv *priv) -{ - return (priv->status & STATUS_INIT) ? 1 : 0; -} - -static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len) -{ - u32 addr, field_info, field_len, field_count, total_len; - - IPW_DEBUG_ORD("ordinal = %i\n", ord); - - if (!priv || !val || !len) { - IPW_DEBUG_ORD("Invalid argument\n"); - return -EINVAL; - } - - /* verify device ordinal tables have been initialized */ - if (!priv->table0_addr || !priv->table1_addr || !priv->table2_addr) { - IPW_DEBUG_ORD("Access ordinals before initialization\n"); - return -EINVAL; - } - - switch (IPW_ORD_TABLE_ID_MASK & ord) { - case IPW_ORD_TABLE_0_MASK: - /* - * TABLE 0: Direct access to a table of 32 bit values - * - * This is a very simple table with the data directly - * read from the table - */ - - /* remove the table id from the ordinal */ - ord &= IPW_ORD_TABLE_VALUE_MASK; - - /* boundary check */ - if (ord > priv->table0_len) { - IPW_DEBUG_ORD("ordinal value (%i) longer then " - "max (%i)\n", ord, priv->table0_len); - return -EINVAL; - } - - /* verify we have enough room to store the value */ - if (*len < sizeof(u32)) { - IPW_DEBUG_ORD("ordinal buffer length too small, " - "need %zd\n", sizeof(u32)); - return -EINVAL; - } - - IPW_DEBUG_ORD("Reading TABLE0[%i] from offset 0x%08x\n", - ord, priv->table0_addr + (ord << 2)); - - *len = sizeof(u32); - ord <<= 2; - *((u32 *) val) = ipw_read32(priv, priv->table0_addr + ord); - break; - - case IPW_ORD_TABLE_1_MASK: - /* - * TABLE 1: Indirect access to a table of 32 bit values - * - * This is a fairly large table of u32 values each - * representing starting addr for the data (which is - * also a u32) - */ - - /* remove the table id from the ordinal */ - ord &= IPW_ORD_TABLE_VALUE_MASK; - - /* boundary check */ - if (ord > priv->table1_len) { - IPW_DEBUG_ORD("ordinal value too long\n"); - return -EINVAL; - } - - /* verify we have enough room to store the value */ - if (*len < sizeof(u32)) { - IPW_DEBUG_ORD("ordinal buffer length too small, " - "need %zd\n", sizeof(u32)); - return -EINVAL; - } - - *((u32 *) val) = - ipw_read_reg32(priv, (priv->table1_addr + (ord << 2))); - *len = sizeof(u32); - break; - - case IPW_ORD_TABLE_2_MASK: - /* - * TABLE 2: Indirect access to a table of variable sized values - * - * This table consist of six values, each containing - * - dword containing the starting offset of the data - * - dword containing the lengh in the first 16bits - * and the count in the second 16bits - */ - - /* remove the table id from the ordinal */ - ord &= IPW_ORD_TABLE_VALUE_MASK; - - /* boundary check */ - if (ord > priv->table2_len) { - IPW_DEBUG_ORD("ordinal value too long\n"); - return -EINVAL; - } - - /* get the address of statistic */ - addr = ipw_read_reg32(priv, priv->table2_addr + (ord << 3)); - - /* get the second DW of statistics ; - * two 16-bit words - first is length, second is count */ - field_info = - ipw_read_reg32(priv, - priv->table2_addr + (ord << 3) + - sizeof(u32)); - - /* get each entry length */ - field_len = *((u16 *) & field_info); - - /* get number of entries */ - field_count = *(((u16 *) & field_info) + 1); - - /* abort if not enough memory */ - total_len = field_len * field_count; - if (total_len > *len) { - *len = total_len; - return -EINVAL; - } - - *len = total_len; - if (!total_len) - return 0; - - IPW_DEBUG_ORD("addr = 0x%08x, total_len = %i, " - "field_info = 0x%08x\n", - addr, total_len, field_info); - ipw_read_indirect(priv, addr, val, total_len); - break; - - default: - IPW_DEBUG_ORD("Invalid ordinal!\n"); - return -EINVAL; - - } - - return 0; -} - -static void ipw_init_ordinals(struct ipw_priv *priv) -{ - priv->table0_addr = IPW_ORDINALS_TABLE_LOWER; - priv->table0_len = ipw_read32(priv, priv->table0_addr); - - IPW_DEBUG_ORD("table 0 offset at 0x%08x, len = %i\n", - priv->table0_addr, priv->table0_len); - - priv->table1_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_1); - priv->table1_len = ipw_read_reg32(priv, priv->table1_addr); - - IPW_DEBUG_ORD("table 1 offset at 0x%08x, len = %i\n", - priv->table1_addr, priv->table1_len); - - priv->table2_addr = ipw_read32(priv, IPW_ORDINALS_TABLE_2); - priv->table2_len = ipw_read_reg32(priv, priv->table2_addr); - priv->table2_len &= 0x0000ffff; /* use first two bytes */ - - IPW_DEBUG_ORD("table 2 offset at 0x%08x, len = %i\n", - priv->table2_addr, priv->table2_len); - -} - -static u32 ipw_register_toggle(u32 reg) -{ - reg &= ~IPW_START_STANDBY; - if (reg & IPW_GATE_ODMA) - reg &= ~IPW_GATE_ODMA; - if (reg & IPW_GATE_IDMA) - reg &= ~IPW_GATE_IDMA; - if (reg & IPW_GATE_ADMA) - reg &= ~IPW_GATE_ADMA; - return reg; -} - -/* - * LED behavior: - * - On radio ON, turn on any LEDs that require to be on during start - * - On initialization, start unassociated blink - * - On association, disable unassociated blink - * - On disassociation, start unassociated blink - * - On radio OFF, turn off any LEDs started during radio on - * - */ -#define LD_TIME_LINK_ON msecs_to_jiffies(300) -#define LD_TIME_LINK_OFF msecs_to_jiffies(2700) -#define LD_TIME_ACT_ON msecs_to_jiffies(250) - -static void ipw_led_link_on(struct ipw_priv *priv) -{ - unsigned long flags; - u32 led; - - /* If configured to not use LEDs, or nic_type is 1, - * then we don't toggle a LINK led */ - if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1) - return; - - spin_lock_irqsave(&priv->lock, flags); - - if (!(priv->status & STATUS_RF_KILL_MASK) && - !(priv->status & STATUS_LED_LINK_ON)) { - IPW_DEBUG_LED("Link LED On\n"); - led = ipw_read_reg32(priv, IPW_EVENT_REG); - led |= priv->led_association_on; - - led = ipw_register_toggle(led); - - IPW_DEBUG_LED("Reg: 0x%08X\n", led); - ipw_write_reg32(priv, IPW_EVENT_REG, led); - - priv->status |= STATUS_LED_LINK_ON; - - /* If we aren't associated, schedule turning the LED off */ - if (!(priv->status & STATUS_ASSOCIATED)) - schedule_delayed_work(&priv->led_link_off, - LD_TIME_LINK_ON); - } - - spin_unlock_irqrestore(&priv->lock, flags); -} - -static void ipw_bg_led_link_on(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, led_link_on.work); - mutex_lock(&priv->mutex); - ipw_led_link_on(priv); - mutex_unlock(&priv->mutex); -} - -static void ipw_led_link_off(struct ipw_priv *priv) -{ - unsigned long flags; - u32 led; - - /* If configured not to use LEDs, or nic type is 1, - * then we don't goggle the LINK led. */ - if (priv->config & CFG_NO_LED || priv->nic_type == EEPROM_NIC_TYPE_1) - return; - - spin_lock_irqsave(&priv->lock, flags); - - if (priv->status & STATUS_LED_LINK_ON) { - led = ipw_read_reg32(priv, IPW_EVENT_REG); - led &= priv->led_association_off; - led = ipw_register_toggle(led); - - IPW_DEBUG_LED("Reg: 0x%08X\n", led); - ipw_write_reg32(priv, IPW_EVENT_REG, led); - - IPW_DEBUG_LED("Link LED Off\n"); - - priv->status &= ~STATUS_LED_LINK_ON; - - /* If we aren't associated and the radio is on, schedule - * turning the LED on (blink while unassociated) */ - if (!(priv->status & STATUS_RF_KILL_MASK) && - !(priv->status & STATUS_ASSOCIATED)) - schedule_delayed_work(&priv->led_link_on, - LD_TIME_LINK_OFF); - - } - - spin_unlock_irqrestore(&priv->lock, flags); -} - -static void ipw_bg_led_link_off(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, led_link_off.work); - mutex_lock(&priv->mutex); - ipw_led_link_off(priv); - mutex_unlock(&priv->mutex); -} - -static void __ipw_led_activity_on(struct ipw_priv *priv) -{ - u32 led; - - if (priv->config & CFG_NO_LED) - return; - - if (priv->status & STATUS_RF_KILL_MASK) - return; - - if (!(priv->status & STATUS_LED_ACT_ON)) { - led = ipw_read_reg32(priv, IPW_EVENT_REG); - led |= priv->led_activity_on; - - led = ipw_register_toggle(led); - - IPW_DEBUG_LED("Reg: 0x%08X\n", led); - ipw_write_reg32(priv, IPW_EVENT_REG, led); - - IPW_DEBUG_LED("Activity LED On\n"); - - priv->status |= STATUS_LED_ACT_ON; - - cancel_delayed_work(&priv->led_act_off); - schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON); - } else { - /* Reschedule LED off for full time period */ - cancel_delayed_work(&priv->led_act_off); - schedule_delayed_work(&priv->led_act_off, LD_TIME_ACT_ON); - } -} - -#if 0 -void ipw_led_activity_on(struct ipw_priv *priv) -{ - unsigned long flags; - spin_lock_irqsave(&priv->lock, flags); - __ipw_led_activity_on(priv); - spin_unlock_irqrestore(&priv->lock, flags); -} -#endif /* 0 */ - -static void ipw_led_activity_off(struct ipw_priv *priv) -{ - unsigned long flags; - u32 led; - - if (priv->config & CFG_NO_LED) - return; - - spin_lock_irqsave(&priv->lock, flags); - - if (priv->status & STATUS_LED_ACT_ON) { - led = ipw_read_reg32(priv, IPW_EVENT_REG); - led &= priv->led_activity_off; - - led = ipw_register_toggle(led); - - IPW_DEBUG_LED("Reg: 0x%08X\n", led); - ipw_write_reg32(priv, IPW_EVENT_REG, led); - - IPW_DEBUG_LED("Activity LED Off\n"); - - priv->status &= ~STATUS_LED_ACT_ON; - } - - spin_unlock_irqrestore(&priv->lock, flags); -} - -static void ipw_bg_led_activity_off(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, led_act_off.work); - mutex_lock(&priv->mutex); - ipw_led_activity_off(priv); - mutex_unlock(&priv->mutex); -} - -static void ipw_led_band_on(struct ipw_priv *priv) -{ - unsigned long flags; - u32 led; - - /* Only nic type 1 supports mode LEDs */ - if (priv->config & CFG_NO_LED || - priv->nic_type != EEPROM_NIC_TYPE_1 || !priv->assoc_network) - return; - - spin_lock_irqsave(&priv->lock, flags); - - led = ipw_read_reg32(priv, IPW_EVENT_REG); - if (priv->assoc_network->mode == IEEE_A) { - led |= priv->led_ofdm_on; - led &= priv->led_association_off; - IPW_DEBUG_LED("Mode LED On: 802.11a\n"); - } else if (priv->assoc_network->mode == IEEE_G) { - led |= priv->led_ofdm_on; - led |= priv->led_association_on; - IPW_DEBUG_LED("Mode LED On: 802.11g\n"); - } else { - led &= priv->led_ofdm_off; - led |= priv->led_association_on; - IPW_DEBUG_LED("Mode LED On: 802.11b\n"); - } - - led = ipw_register_toggle(led); - - IPW_DEBUG_LED("Reg: 0x%08X\n", led); - ipw_write_reg32(priv, IPW_EVENT_REG, led); - - spin_unlock_irqrestore(&priv->lock, flags); -} - -static void ipw_led_band_off(struct ipw_priv *priv) -{ - unsigned long flags; - u32 led; - - /* Only nic type 1 supports mode LEDs */ - if (priv->config & CFG_NO_LED || priv->nic_type != EEPROM_NIC_TYPE_1) - return; - - spin_lock_irqsave(&priv->lock, flags); - - led = ipw_read_reg32(priv, IPW_EVENT_REG); - led &= priv->led_ofdm_off; - led &= priv->led_association_off; - - led = ipw_register_toggle(led); - - IPW_DEBUG_LED("Reg: 0x%08X\n", led); - ipw_write_reg32(priv, IPW_EVENT_REG, led); - - spin_unlock_irqrestore(&priv->lock, flags); -} - -static void ipw_led_radio_on(struct ipw_priv *priv) -{ - ipw_led_link_on(priv); -} - -static void ipw_led_radio_off(struct ipw_priv *priv) -{ - ipw_led_activity_off(priv); - ipw_led_link_off(priv); -} - -static void ipw_led_link_up(struct ipw_priv *priv) -{ - /* Set the Link Led on for all nic types */ - ipw_led_link_on(priv); -} - -static void ipw_led_link_down(struct ipw_priv *priv) -{ - ipw_led_activity_off(priv); - ipw_led_link_off(priv); - - if (priv->status & STATUS_RF_KILL_MASK) - ipw_led_radio_off(priv); -} - -static void ipw_led_init(struct ipw_priv *priv) -{ - priv->nic_type = priv->eeprom[EEPROM_NIC_TYPE]; - - /* Set the default PINs for the link and activity leds */ - priv->led_activity_on = IPW_ACTIVITY_LED; - priv->led_activity_off = ~(IPW_ACTIVITY_LED); - - priv->led_association_on = IPW_ASSOCIATED_LED; - priv->led_association_off = ~(IPW_ASSOCIATED_LED); - - /* Set the default PINs for the OFDM leds */ - priv->led_ofdm_on = IPW_OFDM_LED; - priv->led_ofdm_off = ~(IPW_OFDM_LED); - - switch (priv->nic_type) { - case EEPROM_NIC_TYPE_1: - /* In this NIC type, the LEDs are reversed.... */ - priv->led_activity_on = IPW_ASSOCIATED_LED; - priv->led_activity_off = ~(IPW_ASSOCIATED_LED); - priv->led_association_on = IPW_ACTIVITY_LED; - priv->led_association_off = ~(IPW_ACTIVITY_LED); - - if (!(priv->config & CFG_NO_LED)) - ipw_led_band_on(priv); - - /* And we don't blink link LEDs for this nic, so - * just return here */ - return; - - case EEPROM_NIC_TYPE_3: - case EEPROM_NIC_TYPE_2: - case EEPROM_NIC_TYPE_4: - case EEPROM_NIC_TYPE_0: - break; - - default: - IPW_DEBUG_INFO("Unknown NIC type from EEPROM: %d\n", - priv->nic_type); - priv->nic_type = EEPROM_NIC_TYPE_0; - break; - } - - if (!(priv->config & CFG_NO_LED)) { - if (priv->status & STATUS_ASSOCIATED) - ipw_led_link_on(priv); - else - ipw_led_link_off(priv); - } -} - -static void ipw_led_shutdown(struct ipw_priv *priv) -{ - ipw_led_activity_off(priv); - ipw_led_link_off(priv); - ipw_led_band_off(priv); - cancel_delayed_work(&priv->led_link_on); - cancel_delayed_work(&priv->led_link_off); - cancel_delayed_work(&priv->led_act_off); -} - -/* - * The following adds a new attribute to the sysfs representation - * of this device driver (i.e. a new file in /sys/bus/pci/drivers/ipw/) - * used for controlling the debug level. - * - * See the level definitions in ipw for details. - */ -static ssize_t show_debug_level(struct device_driver *d, char *buf) -{ - return sprintf(buf, "0x%08X\n", ipw_debug_level); -} - -static ssize_t store_debug_level(struct device_driver *d, const char *buf, - size_t count) -{ - char *p = (char *)buf; - u32 val; - - if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') { - p++; - if (p[0] == 'x' || p[0] == 'X') - p++; - val = simple_strtoul(p, &p, 16); - } else - val = simple_strtoul(p, &p, 10); - if (p == buf) - printk(KERN_INFO DRV_NAME - ": %s is not in hex or decimal form.\n", buf); - else - ipw_debug_level = val; - - return strnlen(buf, count); -} - -static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, - show_debug_level, store_debug_level); - -static inline u32 ipw_get_event_log_len(struct ipw_priv *priv) -{ - /* length = 1st dword in log */ - return ipw_read_reg32(priv, ipw_read32(priv, IPW_EVENT_LOG)); -} - -static void ipw_capture_event_log(struct ipw_priv *priv, - u32 log_len, struct ipw_event *log) -{ - u32 base; - - if (log_len) { - base = ipw_read32(priv, IPW_EVENT_LOG); - ipw_read_indirect(priv, base + sizeof(base) + sizeof(u32), - (u8 *) log, sizeof(*log) * log_len); - } -} - -static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv) -{ - struct ipw_fw_error *error; - u32 log_len = ipw_get_event_log_len(priv); - u32 base = ipw_read32(priv, IPW_ERROR_LOG); - u32 elem_len = ipw_read_reg32(priv, base); - - error = kmalloc(sizeof(*error) + - sizeof(*error->elem) * elem_len + - sizeof(*error->log) * log_len, GFP_ATOMIC); - if (!error) { - IPW_ERROR("Memory allocation for firmware error log " - "failed.\n"); - return NULL; - } - error->jiffies = jiffies; - error->status = priv->status; - error->config = priv->config; - error->elem_len = elem_len; - error->log_len = log_len; - error->elem = (struct ipw_error_elem *)error->payload; - error->log = (struct ipw_event *)(error->elem + elem_len); - - ipw_capture_event_log(priv, log_len, error->log); - - if (elem_len) - ipw_read_indirect(priv, base + sizeof(base), (u8 *) error->elem, - sizeof(*error->elem) * elem_len); - - return error; -} - -static ssize_t show_event_log(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - u32 log_len = ipw_get_event_log_len(priv); - u32 log_size; - struct ipw_event *log; - u32 len = 0, i; - - /* not using min() because of its strict type checking */ - log_size = PAGE_SIZE / sizeof(*log) > log_len ? - sizeof(*log) * log_len : PAGE_SIZE; - log = kzalloc(log_size, GFP_KERNEL); - if (!log) { - IPW_ERROR("Unable to allocate memory for log\n"); - return 0; - } - log_len = log_size / sizeof(*log); - ipw_capture_event_log(priv, log_len, log); - - len += snprintf(buf + len, PAGE_SIZE - len, "%08X", log_len); - for (i = 0; i < log_len; i++) - len += snprintf(buf + len, PAGE_SIZE - len, - "\n%08X%08X%08X", - log[i].time, log[i].event, log[i].data); - len += snprintf(buf + len, PAGE_SIZE - len, "\n"); - kfree(log); - return len; -} - -static DEVICE_ATTR(event_log, S_IRUGO, show_event_log, NULL); - -static ssize_t show_error(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - u32 len = 0, i; - if (!priv->error) - return 0; - len += snprintf(buf + len, PAGE_SIZE - len, - "%08lX%08X%08X%08X", - priv->error->jiffies, - priv->error->status, - priv->error->config, priv->error->elem_len); - for (i = 0; i < priv->error->elem_len; i++) - len += snprintf(buf + len, PAGE_SIZE - len, - "\n%08X%08X%08X%08X%08X%08X%08X", - priv->error->elem[i].time, - priv->error->elem[i].desc, - priv->error->elem[i].blink1, - priv->error->elem[i].blink2, - priv->error->elem[i].link1, - priv->error->elem[i].link2, - priv->error->elem[i].data); - - len += snprintf(buf + len, PAGE_SIZE - len, - "\n%08X", priv->error->log_len); - for (i = 0; i < priv->error->log_len; i++) - len += snprintf(buf + len, PAGE_SIZE - len, - "\n%08X%08X%08X", - priv->error->log[i].time, - priv->error->log[i].event, - priv->error->log[i].data); - len += snprintf(buf + len, PAGE_SIZE - len, "\n"); - return len; -} - -static ssize_t clear_error(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - - kfree(priv->error); - priv->error = NULL; - return count; -} - -static DEVICE_ATTR(error, S_IRUGO | S_IWUSR, show_error, clear_error); - -static ssize_t show_cmd_log(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - u32 len = 0, i; - if (!priv->cmdlog) - return 0; - for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len; - (i != priv->cmdlog_pos) && (len < PAGE_SIZE); - i = (i + 1) % priv->cmdlog_len) { - len += - snprintf(buf + len, PAGE_SIZE - len, - "\n%08lX%08X%08X%08X\n", priv->cmdlog[i].jiffies, - priv->cmdlog[i].retcode, priv->cmdlog[i].cmd.cmd, - priv->cmdlog[i].cmd.len); - len += - snprintk_buf(buf + len, PAGE_SIZE - len, - (u8 *) priv->cmdlog[i].cmd.param, - priv->cmdlog[i].cmd.len); - len += snprintf(buf + len, PAGE_SIZE - len, "\n"); - } - len += snprintf(buf + len, PAGE_SIZE - len, "\n"); - return len; -} - -static DEVICE_ATTR(cmd_log, S_IRUGO, show_cmd_log, NULL); - -#ifdef CONFIG_IPW2200_PROMISCUOUS -static void ipw_prom_free(struct ipw_priv *priv); -static int ipw_prom_alloc(struct ipw_priv *priv); -static ssize_t store_rtap_iface(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - int rc = 0; - - if (count < 1) - return -EINVAL; - - switch (buf[0]) { - case '0': - if (!rtap_iface) - return count; - - if (netif_running(priv->prom_net_dev)) { - IPW_WARNING("Interface is up. Cannot unregister.\n"); - return count; - } - - ipw_prom_free(priv); - rtap_iface = 0; - break; - - case '1': - if (rtap_iface) - return count; - - rc = ipw_prom_alloc(priv); - if (!rc) - rtap_iface = 1; - break; - - default: - return -EINVAL; - } - - if (rc) { - IPW_ERROR("Failed to register promiscuous network " - "device (error %d).\n", rc); - } - - return count; -} - -static ssize_t show_rtap_iface(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - if (rtap_iface) - return sprintf(buf, "%s", priv->prom_net_dev->name); - else { - buf[0] = '-'; - buf[1] = '1'; - buf[2] = '\0'; - return 3; - } -} - -static DEVICE_ATTR(rtap_iface, S_IWUSR | S_IRUSR, show_rtap_iface, - store_rtap_iface); - -static ssize_t store_rtap_filter(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - - if (!priv->prom_priv) { - IPW_ERROR("Attempting to set filter without " - "rtap_iface enabled.\n"); - return -EPERM; - } - - priv->prom_priv->filter = simple_strtol(buf, NULL, 0); - - IPW_DEBUG_INFO("Setting rtap filter to " BIT_FMT16 "\n", - BIT_ARG16(priv->prom_priv->filter)); - - return count; -} - -static ssize_t show_rtap_filter(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - return sprintf(buf, "0x%04X", - priv->prom_priv ? priv->prom_priv->filter : 0); -} - -static DEVICE_ATTR(rtap_filter, S_IWUSR | S_IRUSR, show_rtap_filter, - store_rtap_filter); -#endif - -static ssize_t show_scan_age(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - return sprintf(buf, "%d\n", priv->ieee->scan_age); -} - -static ssize_t store_scan_age(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - struct net_device *dev = priv->net_dev; - char buffer[] = "00000000"; - unsigned long len = - (sizeof(buffer) - 1) > count ? count : sizeof(buffer) - 1; - unsigned long val; - char *p = buffer; - - IPW_DEBUG_INFO("enter\n"); - - strncpy(buffer, buf, len); - buffer[len] = 0; - - if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') { - p++; - if (p[0] == 'x' || p[0] == 'X') - p++; - val = simple_strtoul(p, &p, 16); - } else - val = simple_strtoul(p, &p, 10); - if (p == buffer) { - IPW_DEBUG_INFO("%s: user supplied invalid value.\n", dev->name); - } else { - priv->ieee->scan_age = val; - IPW_DEBUG_INFO("set scan_age = %u\n", priv->ieee->scan_age); - } - - IPW_DEBUG_INFO("exit\n"); - return len; -} - -static DEVICE_ATTR(scan_age, S_IWUSR | S_IRUGO, show_scan_age, store_scan_age); - -static ssize_t show_led(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - return sprintf(buf, "%d\n", (priv->config & CFG_NO_LED) ? 0 : 1); -} - -static ssize_t store_led(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - - IPW_DEBUG_INFO("enter\n"); - - if (count == 0) - return 0; - - if (*buf == 0) { - IPW_DEBUG_LED("Disabling LED control.\n"); - priv->config |= CFG_NO_LED; - ipw_led_shutdown(priv); - } else { - IPW_DEBUG_LED("Enabling LED control.\n"); - priv->config &= ~CFG_NO_LED; - ipw_led_init(priv); - } - - IPW_DEBUG_INFO("exit\n"); - return count; -} - -static DEVICE_ATTR(led, S_IWUSR | S_IRUGO, show_led, store_led); - -static ssize_t show_status(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct ipw_priv *p = dev_get_drvdata(d); - return sprintf(buf, "0x%08x\n", (int)p->status); -} - -static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); - -static ssize_t show_cfg(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw_priv *p = dev_get_drvdata(d); - return sprintf(buf, "0x%08x\n", (int)p->config); -} - -static DEVICE_ATTR(cfg, S_IRUGO, show_cfg, NULL); - -static ssize_t show_nic_type(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - return sprintf(buf, "TYPE: %d\n", priv->nic_type); -} - -static DEVICE_ATTR(nic_type, S_IRUGO, show_nic_type, NULL); - -static ssize_t show_ucode_version(struct device *d, - struct device_attribute *attr, char *buf) -{ - u32 len = sizeof(u32), tmp = 0; - struct ipw_priv *p = dev_get_drvdata(d); - - if (ipw_get_ordinal(p, IPW_ORD_STAT_UCODE_VERSION, &tmp, &len)) - return 0; - - return sprintf(buf, "0x%08x\n", tmp); -} - -static DEVICE_ATTR(ucode_version, S_IWUSR | S_IRUGO, show_ucode_version, NULL); - -static ssize_t show_rtc(struct device *d, struct device_attribute *attr, - char *buf) -{ - u32 len = sizeof(u32), tmp = 0; - struct ipw_priv *p = dev_get_drvdata(d); - - if (ipw_get_ordinal(p, IPW_ORD_STAT_RTC, &tmp, &len)) - return 0; - - return sprintf(buf, "0x%08x\n", tmp); -} - -static DEVICE_ATTR(rtc, S_IWUSR | S_IRUGO, show_rtc, NULL); - -/* - * Add a device attribute to view/control the delay between eeprom - * operations. - */ -static ssize_t show_eeprom_delay(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct ipw_priv *p = dev_get_drvdata(d); - int n = p->eeprom_delay; - return sprintf(buf, "%i\n", n); -} -static ssize_t store_eeprom_delay(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *p = dev_get_drvdata(d); - sscanf(buf, "%i", &p->eeprom_delay); - return strnlen(buf, count); -} - -static DEVICE_ATTR(eeprom_delay, S_IWUSR | S_IRUGO, - show_eeprom_delay, store_eeprom_delay); - -static ssize_t show_command_event_reg(struct device *d, - struct device_attribute *attr, char *buf) -{ - u32 reg = 0; - struct ipw_priv *p = dev_get_drvdata(d); - - reg = ipw_read_reg32(p, IPW_INTERNAL_CMD_EVENT); - return sprintf(buf, "0x%08x\n", reg); -} -static ssize_t store_command_event_reg(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 reg; - struct ipw_priv *p = dev_get_drvdata(d); - - sscanf(buf, "%x", ®); - ipw_write_reg32(p, IPW_INTERNAL_CMD_EVENT, reg); - return strnlen(buf, count); -} - -static DEVICE_ATTR(command_event_reg, S_IWUSR | S_IRUGO, - show_command_event_reg, store_command_event_reg); - -static ssize_t show_mem_gpio_reg(struct device *d, - struct device_attribute *attr, char *buf) -{ - u32 reg = 0; - struct ipw_priv *p = dev_get_drvdata(d); - - reg = ipw_read_reg32(p, 0x301100); - return sprintf(buf, "0x%08x\n", reg); -} -static ssize_t store_mem_gpio_reg(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - u32 reg; - struct ipw_priv *p = dev_get_drvdata(d); - - sscanf(buf, "%x", ®); - ipw_write_reg32(p, 0x301100, reg); - return strnlen(buf, count); -} - -static DEVICE_ATTR(mem_gpio_reg, S_IWUSR | S_IRUGO, - show_mem_gpio_reg, store_mem_gpio_reg); - -static ssize_t show_indirect_dword(struct device *d, - struct device_attribute *attr, char *buf) -{ - u32 reg = 0; - struct ipw_priv *priv = dev_get_drvdata(d); - - if (priv->status & STATUS_INDIRECT_DWORD) - reg = ipw_read_reg32(priv, priv->indirect_dword); - else - reg = 0; - - return sprintf(buf, "0x%08x\n", reg); -} -static ssize_t store_indirect_dword(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - - sscanf(buf, "%x", &priv->indirect_dword); - priv->status |= STATUS_INDIRECT_DWORD; - return strnlen(buf, count); -} - -static DEVICE_ATTR(indirect_dword, S_IWUSR | S_IRUGO, - show_indirect_dword, store_indirect_dword); - -static ssize_t show_indirect_byte(struct device *d, - struct device_attribute *attr, char *buf) -{ - u8 reg = 0; - struct ipw_priv *priv = dev_get_drvdata(d); - - if (priv->status & STATUS_INDIRECT_BYTE) - reg = ipw_read_reg8(priv, priv->indirect_byte); - else - reg = 0; - - return sprintf(buf, "0x%02x\n", reg); -} -static ssize_t store_indirect_byte(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - - sscanf(buf, "%x", &priv->indirect_byte); - priv->status |= STATUS_INDIRECT_BYTE; - return strnlen(buf, count); -} - -static DEVICE_ATTR(indirect_byte, S_IWUSR | S_IRUGO, - show_indirect_byte, store_indirect_byte); - -static ssize_t show_direct_dword(struct device *d, - struct device_attribute *attr, char *buf) -{ - u32 reg = 0; - struct ipw_priv *priv = dev_get_drvdata(d); - - if (priv->status & STATUS_DIRECT_DWORD) - reg = ipw_read32(priv, priv->direct_dword); - else - reg = 0; - - return sprintf(buf, "0x%08x\n", reg); -} -static ssize_t store_direct_dword(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - - sscanf(buf, "%x", &priv->direct_dword); - priv->status |= STATUS_DIRECT_DWORD; - return strnlen(buf, count); -} - -static DEVICE_ATTR(direct_dword, S_IWUSR | S_IRUGO, - show_direct_dword, store_direct_dword); - -static int rf_kill_active(struct ipw_priv *priv) -{ - if (0 == (ipw_read32(priv, 0x30) & 0x10000)) { - priv->status |= STATUS_RF_KILL_HW; - wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); - } else { - priv->status &= ~STATUS_RF_KILL_HW; - wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, false); - } - - return (priv->status & STATUS_RF_KILL_HW) ? 1 : 0; -} - -static ssize_t show_rf_kill(struct device *d, struct device_attribute *attr, - char *buf) -{ - /* 0 - RF kill not enabled - 1 - SW based RF kill active (sysfs) - 2 - HW based RF kill active - 3 - Both HW and SW baed RF kill active */ - struct ipw_priv *priv = dev_get_drvdata(d); - int val = ((priv->status & STATUS_RF_KILL_SW) ? 0x1 : 0x0) | - (rf_kill_active(priv) ? 0x2 : 0x0); - return sprintf(buf, "%i\n", val); -} - -static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio) -{ - if ((disable_radio ? 1 : 0) == - ((priv->status & STATUS_RF_KILL_SW) ? 1 : 0)) - return 0; - - IPW_DEBUG_RF_KILL("Manual SW RF Kill set to: RADIO %s\n", - disable_radio ? "OFF" : "ON"); - - if (disable_radio) { - priv->status |= STATUS_RF_KILL_SW; - - cancel_delayed_work(&priv->request_scan); - cancel_delayed_work(&priv->request_direct_scan); - cancel_delayed_work(&priv->request_passive_scan); - cancel_delayed_work(&priv->scan_event); - schedule_work(&priv->down); - } else { - priv->status &= ~STATUS_RF_KILL_SW; - if (rf_kill_active(priv)) { - IPW_DEBUG_RF_KILL("Can not turn radio back on - " - "disabled by HW switch\n"); - /* Make sure the RF_KILL check timer is running */ - cancel_delayed_work(&priv->rf_kill); - schedule_delayed_work(&priv->rf_kill, - round_jiffies_relative(2 * HZ)); - } else - schedule_work(&priv->up); - } - - return 1; -} - -static ssize_t store_rf_kill(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - - ipw_radio_kill_sw(priv, buf[0] == '1'); - - return count; -} - -static DEVICE_ATTR(rf_kill, S_IWUSR | S_IRUGO, show_rf_kill, store_rf_kill); - -static ssize_t show_speed_scan(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - int pos = 0, len = 0; - if (priv->config & CFG_SPEED_SCAN) { - while (priv->speed_scan[pos] != 0) - len += sprintf(&buf[len], "%d ", - priv->speed_scan[pos++]); - return len + sprintf(&buf[len], "\n"); - } - - return sprintf(buf, "0\n"); -} - -static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - int channel, pos = 0; - const char *p = buf; - - /* list of space separated channels to scan, optionally ending with 0 */ - while ((channel = simple_strtol(p, NULL, 0))) { - if (pos == MAX_SPEED_SCAN - 1) { - priv->speed_scan[pos] = 0; - break; - } - - if (libipw_is_valid_channel(priv->ieee, channel)) - priv->speed_scan[pos++] = channel; - else - IPW_WARNING("Skipping invalid channel request: %d\n", - channel); - p = strchr(p, ' '); - if (!p) - break; - while (*p == ' ' || *p == '\t') - p++; - } - - if (pos == 0) - priv->config &= ~CFG_SPEED_SCAN; - else { - priv->speed_scan_pos = 0; - priv->config |= CFG_SPEED_SCAN; - } - - return count; -} - -static DEVICE_ATTR(speed_scan, S_IWUSR | S_IRUGO, show_speed_scan, - store_speed_scan); - -static ssize_t show_net_stats(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - return sprintf(buf, "%c\n", (priv->config & CFG_NET_STATS) ? '1' : '0'); -} - -static ssize_t store_net_stats(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - if (buf[0] == '1') - priv->config |= CFG_NET_STATS; - else - priv->config &= ~CFG_NET_STATS; - - return count; -} - -static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO, - show_net_stats, store_net_stats); - -static ssize_t show_channels(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct ipw_priv *priv = dev_get_drvdata(d); - const struct libipw_geo *geo = libipw_get_geo(priv->ieee); - int len = 0, i; - - len = sprintf(&buf[len], - "Displaying %d channels in 2.4Ghz band " - "(802.11bg):\n", geo->bg_channels); - - for (i = 0; i < geo->bg_channels; i++) { - len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n", - geo->bg[i].channel, - geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT ? - " (radar spectrum)" : "", - ((geo->bg[i].flags & LIBIPW_CH_NO_IBSS) || - (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT)) - ? "" : ", IBSS", - geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY ? - "passive only" : "active/passive", - geo->bg[i].flags & LIBIPW_CH_B_ONLY ? - "B" : "B/G"); - } - - len += sprintf(&buf[len], - "Displaying %d channels in 5.2Ghz band " - "(802.11a):\n", geo->a_channels); - for (i = 0; i < geo->a_channels; i++) { - len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n", - geo->a[i].channel, - geo->a[i].flags & LIBIPW_CH_RADAR_DETECT ? - " (radar spectrum)" : "", - ((geo->a[i].flags & LIBIPW_CH_NO_IBSS) || - (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT)) - ? "" : ", IBSS", - geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY ? - "passive only" : "active/passive"); - } - - return len; -} - -static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL); - -static void notify_wx_assoc_event(struct ipw_priv *priv) -{ - union iwreq_data wrqu; - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (priv->status & STATUS_ASSOCIATED) - memcpy(wrqu.ap_addr.sa_data, priv->bssid, ETH_ALEN); - else - eth_zero_addr(wrqu.ap_addr.sa_data); - wireless_send_event(priv->net_dev, SIOCGIWAP, &wrqu, NULL); -} - -static void ipw_irq_tasklet(struct ipw_priv *priv) -{ - u32 inta, inta_mask, handled = 0; - unsigned long flags; - int rc = 0; - - spin_lock_irqsave(&priv->irq_lock, flags); - - inta = ipw_read32(priv, IPW_INTA_RW); - inta_mask = ipw_read32(priv, IPW_INTA_MASK_R); - - if (inta == 0xFFFFFFFF) { - /* Hardware disappeared */ - IPW_WARNING("TASKLET INTA == 0xFFFFFFFF\n"); - /* Only handle the cached INTA values */ - inta = 0; - } - inta &= (IPW_INTA_MASK_ALL & inta_mask); - - /* Add any cached INTA values that need to be handled */ - inta |= priv->isr_inta; - - spin_unlock_irqrestore(&priv->irq_lock, flags); - - spin_lock_irqsave(&priv->lock, flags); - - /* handle all the justifications for the interrupt */ - if (inta & IPW_INTA_BIT_RX_TRANSFER) { - ipw_rx(priv); - handled |= IPW_INTA_BIT_RX_TRANSFER; - } - - if (inta & IPW_INTA_BIT_TX_CMD_QUEUE) { - IPW_DEBUG_HC("Command completed.\n"); - rc = ipw_queue_tx_reclaim(priv, &priv->txq_cmd, -1); - priv->status &= ~STATUS_HCMD_ACTIVE; - wake_up_interruptible(&priv->wait_command_queue); - handled |= IPW_INTA_BIT_TX_CMD_QUEUE; - } - - if (inta & IPW_INTA_BIT_TX_QUEUE_1) { - IPW_DEBUG_TX("TX_QUEUE_1\n"); - rc = ipw_queue_tx_reclaim(priv, &priv->txq[0], 0); - handled |= IPW_INTA_BIT_TX_QUEUE_1; - } - - if (inta & IPW_INTA_BIT_TX_QUEUE_2) { - IPW_DEBUG_TX("TX_QUEUE_2\n"); - rc = ipw_queue_tx_reclaim(priv, &priv->txq[1], 1); - handled |= IPW_INTA_BIT_TX_QUEUE_2; - } - - if (inta & IPW_INTA_BIT_TX_QUEUE_3) { - IPW_DEBUG_TX("TX_QUEUE_3\n"); - rc = ipw_queue_tx_reclaim(priv, &priv->txq[2], 2); - handled |= IPW_INTA_BIT_TX_QUEUE_3; - } - - if (inta & IPW_INTA_BIT_TX_QUEUE_4) { - IPW_DEBUG_TX("TX_QUEUE_4\n"); - rc = ipw_queue_tx_reclaim(priv, &priv->txq[3], 3); - handled |= IPW_INTA_BIT_TX_QUEUE_4; - } - - if (inta & IPW_INTA_BIT_STATUS_CHANGE) { - IPW_WARNING("STATUS_CHANGE\n"); - handled |= IPW_INTA_BIT_STATUS_CHANGE; - } - - if (inta & IPW_INTA_BIT_BEACON_PERIOD_EXPIRED) { - IPW_WARNING("TX_PERIOD_EXPIRED\n"); - handled |= IPW_INTA_BIT_BEACON_PERIOD_EXPIRED; - } - - if (inta & IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE) { - IPW_WARNING("HOST_CMD_DONE\n"); - handled |= IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE; - } - - if (inta & IPW_INTA_BIT_FW_INITIALIZATION_DONE) { - IPW_WARNING("FW_INITIALIZATION_DONE\n"); - handled |= IPW_INTA_BIT_FW_INITIALIZATION_DONE; - } - - if (inta & IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE) { - IPW_WARNING("PHY_OFF_DONE\n"); - handled |= IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE; - } - - if (inta & IPW_INTA_BIT_RF_KILL_DONE) { - IPW_DEBUG_RF_KILL("RF_KILL_DONE\n"); - priv->status |= STATUS_RF_KILL_HW; - wiphy_rfkill_set_hw_state(priv->ieee->wdev.wiphy, true); - wake_up_interruptible(&priv->wait_command_queue); - priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING); - cancel_delayed_work(&priv->request_scan); - cancel_delayed_work(&priv->request_direct_scan); - cancel_delayed_work(&priv->request_passive_scan); - cancel_delayed_work(&priv->scan_event); - schedule_work(&priv->link_down); - schedule_delayed_work(&priv->rf_kill, 2 * HZ); - handled |= IPW_INTA_BIT_RF_KILL_DONE; - } - - if (inta & IPW_INTA_BIT_FATAL_ERROR) { - IPW_WARNING("Firmware error detected. Restarting.\n"); - if (priv->error) { - IPW_DEBUG_FW("Sysfs 'error' log already exists.\n"); - if (ipw_debug_level & IPW_DL_FW_ERRORS) { - struct ipw_fw_error *error = - ipw_alloc_error_log(priv); - ipw_dump_error_log(priv, error); - kfree(error); - } - } else { - priv->error = ipw_alloc_error_log(priv); - if (priv->error) - IPW_DEBUG_FW("Sysfs 'error' log captured.\n"); - else - IPW_DEBUG_FW("Error allocating sysfs 'error' " - "log.\n"); - if (ipw_debug_level & IPW_DL_FW_ERRORS) - ipw_dump_error_log(priv, priv->error); - } - - /* XXX: If hardware encryption is for WPA/WPA2, - * we have to notify the supplicant. */ - if (priv->ieee->sec.encrypt) { - priv->status &= ~STATUS_ASSOCIATED; - notify_wx_assoc_event(priv); - } - - /* Keep the restart process from trying to send host - * commands by clearing the INIT status bit */ - priv->status &= ~STATUS_INIT; - - /* Cancel currently queued command. */ - priv->status &= ~STATUS_HCMD_ACTIVE; - wake_up_interruptible(&priv->wait_command_queue); - - schedule_work(&priv->adapter_restart); - handled |= IPW_INTA_BIT_FATAL_ERROR; - } - - if (inta & IPW_INTA_BIT_PARITY_ERROR) { - IPW_ERROR("Parity error\n"); - handled |= IPW_INTA_BIT_PARITY_ERROR; - } - - if (handled != inta) { - IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled); - } - - spin_unlock_irqrestore(&priv->lock, flags); - - /* enable all interrupts */ - ipw_enable_interrupts(priv); -} - -#define IPW_CMD(x) case IPW_CMD_ ## x : return #x -static char *get_cmd_string(u8 cmd) -{ - switch (cmd) { - IPW_CMD(HOST_COMPLETE); - IPW_CMD(POWER_DOWN); - IPW_CMD(SYSTEM_CONFIG); - IPW_CMD(MULTICAST_ADDRESS); - IPW_CMD(SSID); - IPW_CMD(ADAPTER_ADDRESS); - IPW_CMD(PORT_TYPE); - IPW_CMD(RTS_THRESHOLD); - IPW_CMD(FRAG_THRESHOLD); - IPW_CMD(POWER_MODE); - IPW_CMD(WEP_KEY); - IPW_CMD(TGI_TX_KEY); - IPW_CMD(SCAN_REQUEST); - IPW_CMD(SCAN_REQUEST_EXT); - IPW_CMD(ASSOCIATE); - IPW_CMD(SUPPORTED_RATES); - IPW_CMD(SCAN_ABORT); - IPW_CMD(TX_FLUSH); - IPW_CMD(QOS_PARAMETERS); - IPW_CMD(DINO_CONFIG); - IPW_CMD(RSN_CAPABILITIES); - IPW_CMD(RX_KEY); - IPW_CMD(CARD_DISABLE); - IPW_CMD(SEED_NUMBER); - IPW_CMD(TX_POWER); - IPW_CMD(COUNTRY_INFO); - IPW_CMD(AIRONET_INFO); - IPW_CMD(AP_TX_POWER); - IPW_CMD(CCKM_INFO); - IPW_CMD(CCX_VER_INFO); - IPW_CMD(SET_CALIBRATION); - IPW_CMD(SENSITIVITY_CALIB); - IPW_CMD(RETRY_LIMIT); - IPW_CMD(IPW_PRE_POWER_DOWN); - IPW_CMD(VAP_BEACON_TEMPLATE); - IPW_CMD(VAP_DTIM_PERIOD); - IPW_CMD(EXT_SUPPORTED_RATES); - IPW_CMD(VAP_LOCAL_TX_PWR_CONSTRAINT); - IPW_CMD(VAP_QUIET_INTERVALS); - IPW_CMD(VAP_CHANNEL_SWITCH); - IPW_CMD(VAP_MANDATORY_CHANNELS); - IPW_CMD(VAP_CELL_PWR_LIMIT); - IPW_CMD(VAP_CF_PARAM_SET); - IPW_CMD(VAP_SET_BEACONING_STATE); - IPW_CMD(MEASUREMENT); - IPW_CMD(POWER_CAPABILITY); - IPW_CMD(SUPPORTED_CHANNELS); - IPW_CMD(TPC_REPORT); - IPW_CMD(WME_INFO); - IPW_CMD(PRODUCTION_COMMAND); - default: - return "UNKNOWN"; - } -} - -#define HOST_COMPLETE_TIMEOUT HZ - -static int __ipw_send_cmd(struct ipw_priv *priv, struct host_cmd *cmd) -{ - int rc = 0; - unsigned long flags; - unsigned long now, end; - - spin_lock_irqsave(&priv->lock, flags); - if (priv->status & STATUS_HCMD_ACTIVE) { - IPW_ERROR("Failed to send %s: Already sending a command.\n", - get_cmd_string(cmd->cmd)); - spin_unlock_irqrestore(&priv->lock, flags); - return -EAGAIN; - } - - priv->status |= STATUS_HCMD_ACTIVE; - - if (priv->cmdlog) { - priv->cmdlog[priv->cmdlog_pos].jiffies = jiffies; - priv->cmdlog[priv->cmdlog_pos].cmd.cmd = cmd->cmd; - priv->cmdlog[priv->cmdlog_pos].cmd.len = cmd->len; - memcpy(priv->cmdlog[priv->cmdlog_pos].cmd.param, cmd->param, - cmd->len); - priv->cmdlog[priv->cmdlog_pos].retcode = -1; - } - - IPW_DEBUG_HC("%s command (#%d) %d bytes: 0x%08X\n", - get_cmd_string(cmd->cmd), cmd->cmd, cmd->len, - priv->status); - -#ifndef DEBUG_CMD_WEP_KEY - if (cmd->cmd == IPW_CMD_WEP_KEY) - IPW_DEBUG_HC("WEP_KEY command masked out for secure.\n"); - else -#endif - printk_buf(IPW_DL_HOST_COMMAND, (u8 *) cmd->param, cmd->len); - - rc = ipw_queue_tx_hcmd(priv, cmd->cmd, cmd->param, cmd->len, 0); - if (rc) { - priv->status &= ~STATUS_HCMD_ACTIVE; - IPW_ERROR("Failed to send %s: Reason %d\n", - get_cmd_string(cmd->cmd), rc); - spin_unlock_irqrestore(&priv->lock, flags); - goto exit; - } - spin_unlock_irqrestore(&priv->lock, flags); - - now = jiffies; - end = now + HOST_COMPLETE_TIMEOUT; -again: - rc = wait_event_interruptible_timeout(priv->wait_command_queue, - !(priv-> - status & STATUS_HCMD_ACTIVE), - end - now); - if (rc < 0) { - now = jiffies; - if (time_before(now, end)) - goto again; - rc = 0; - } - - if (rc == 0) { - spin_lock_irqsave(&priv->lock, flags); - if (priv->status & STATUS_HCMD_ACTIVE) { - IPW_ERROR("Failed to send %s: Command timed out.\n", - get_cmd_string(cmd->cmd)); - priv->status &= ~STATUS_HCMD_ACTIVE; - spin_unlock_irqrestore(&priv->lock, flags); - rc = -EIO; - goto exit; - } - spin_unlock_irqrestore(&priv->lock, flags); - } else - rc = 0; - - if (priv->status & STATUS_RF_KILL_HW) { - IPW_ERROR("Failed to send %s: Aborted due to RF kill switch.\n", - get_cmd_string(cmd->cmd)); - rc = -EIO; - goto exit; - } - - exit: - if (priv->cmdlog) { - priv->cmdlog[priv->cmdlog_pos++].retcode = rc; - priv->cmdlog_pos %= priv->cmdlog_len; - } - return rc; -} - -static int ipw_send_cmd_simple(struct ipw_priv *priv, u8 command) -{ - struct host_cmd cmd = { - .cmd = command, - }; - - return __ipw_send_cmd(priv, &cmd); -} - -static int ipw_send_cmd_pdu(struct ipw_priv *priv, u8 command, u8 len, - void *data) -{ - struct host_cmd cmd = { - .cmd = command, - .len = len, - .param = data, - }; - - return __ipw_send_cmd(priv, &cmd); -} - -static int ipw_send_host_complete(struct ipw_priv *priv) -{ - if (!priv) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - return ipw_send_cmd_simple(priv, IPW_CMD_HOST_COMPLETE); -} - -static int ipw_send_system_config(struct ipw_priv *priv) -{ - return ipw_send_cmd_pdu(priv, IPW_CMD_SYSTEM_CONFIG, - sizeof(priv->sys_config), - &priv->sys_config); -} - -static int ipw_send_ssid(struct ipw_priv *priv, u8 * ssid, int len) -{ - if (!priv || !ssid) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - return ipw_send_cmd_pdu(priv, IPW_CMD_SSID, min(len, IW_ESSID_MAX_SIZE), - ssid); -} - -static int ipw_send_adapter_address(struct ipw_priv *priv, u8 * mac) -{ - if (!priv || !mac) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - IPW_DEBUG_INFO("%s: Setting MAC to %pM\n", - priv->net_dev->name, mac); - - return ipw_send_cmd_pdu(priv, IPW_CMD_ADAPTER_ADDRESS, ETH_ALEN, mac); -} - -static void ipw_adapter_restart(void *adapter) -{ - struct ipw_priv *priv = adapter; - - if (priv->status & STATUS_RF_KILL_MASK) - return; - - ipw_down(priv); - - if (priv->assoc_network && - (priv->assoc_network->capability & WLAN_CAPABILITY_IBSS)) - ipw_remove_current_network(priv); - - if (ipw_up(priv)) { - IPW_ERROR("Failed to up device\n"); - return; - } -} - -static void ipw_bg_adapter_restart(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, adapter_restart); - mutex_lock(&priv->mutex); - ipw_adapter_restart(priv); - mutex_unlock(&priv->mutex); -} - -static void ipw_abort_scan(struct ipw_priv *priv); - -#define IPW_SCAN_CHECK_WATCHDOG (5 * HZ) - -static void ipw_scan_check(void *data) -{ - struct ipw_priv *priv = data; - - if (priv->status & STATUS_SCAN_ABORTING) { - IPW_DEBUG_SCAN("Scan completion watchdog resetting " - "adapter after (%dms).\n", - jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG)); - schedule_work(&priv->adapter_restart); - } else if (priv->status & STATUS_SCANNING) { - IPW_DEBUG_SCAN("Scan completion watchdog aborting scan " - "after (%dms).\n", - jiffies_to_msecs(IPW_SCAN_CHECK_WATCHDOG)); - ipw_abort_scan(priv); - schedule_delayed_work(&priv->scan_check, HZ); - } -} - -static void ipw_bg_scan_check(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, scan_check.work); - mutex_lock(&priv->mutex); - ipw_scan_check(priv); - mutex_unlock(&priv->mutex); -} - -static int ipw_send_scan_request_ext(struct ipw_priv *priv, - struct ipw_scan_request_ext *request) -{ - return ipw_send_cmd_pdu(priv, IPW_CMD_SCAN_REQUEST_EXT, - sizeof(*request), request); -} - -static int ipw_send_scan_abort(struct ipw_priv *priv) -{ - if (!priv) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - return ipw_send_cmd_simple(priv, IPW_CMD_SCAN_ABORT); -} - -static int ipw_set_sensitivity(struct ipw_priv *priv, u16 sens) -{ - struct ipw_sensitivity_calib calib = { - .beacon_rssi_raw = cpu_to_le16(sens), - }; - - return ipw_send_cmd_pdu(priv, IPW_CMD_SENSITIVITY_CALIB, sizeof(calib), - &calib); -} - -static int ipw_send_associate(struct ipw_priv *priv, - struct ipw_associate *associate) -{ - if (!priv || !associate) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - return ipw_send_cmd_pdu(priv, IPW_CMD_ASSOCIATE, sizeof(*associate), - associate); -} - -static int ipw_send_supported_rates(struct ipw_priv *priv, - struct ipw_supported_rates *rates) -{ - if (!priv || !rates) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - return ipw_send_cmd_pdu(priv, IPW_CMD_SUPPORTED_RATES, sizeof(*rates), - rates); -} - -static int ipw_set_random_seed(struct ipw_priv *priv) -{ - u32 val; - - if (!priv) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - get_random_bytes(&val, sizeof(val)); - - return ipw_send_cmd_pdu(priv, IPW_CMD_SEED_NUMBER, sizeof(val), &val); -} - -static int ipw_send_card_disable(struct ipw_priv *priv, u32 phy_off) -{ - __le32 v = cpu_to_le32(phy_off); - if (!priv) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - return ipw_send_cmd_pdu(priv, IPW_CMD_CARD_DISABLE, sizeof(v), &v); -} - -static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power) -{ - if (!priv || !power) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - return ipw_send_cmd_pdu(priv, IPW_CMD_TX_POWER, sizeof(*power), power); -} - -static int ipw_set_tx_power(struct ipw_priv *priv) -{ - const struct libipw_geo *geo = libipw_get_geo(priv->ieee); - struct ipw_tx_power tx_power; - s8 max_power; - int i; - - memset(&tx_power, 0, sizeof(tx_power)); - - /* configure device for 'G' band */ - tx_power.ieee_mode = IPW_G_MODE; - tx_power.num_channels = geo->bg_channels; - for (i = 0; i < geo->bg_channels; i++) { - max_power = geo->bg[i].max_power; - tx_power.channels_tx_power[i].channel_number = - geo->bg[i].channel; - tx_power.channels_tx_power[i].tx_power = max_power ? - min(max_power, priv->tx_power) : priv->tx_power; - } - if (ipw_send_tx_power(priv, &tx_power)) - return -EIO; - - /* configure device to also handle 'B' band */ - tx_power.ieee_mode = IPW_B_MODE; - if (ipw_send_tx_power(priv, &tx_power)) - return -EIO; - - /* configure device to also handle 'A' band */ - if (priv->ieee->abg_true) { - tx_power.ieee_mode = IPW_A_MODE; - tx_power.num_channels = geo->a_channels; - for (i = 0; i < tx_power.num_channels; i++) { - max_power = geo->a[i].max_power; - tx_power.channels_tx_power[i].channel_number = - geo->a[i].channel; - tx_power.channels_tx_power[i].tx_power = max_power ? - min(max_power, priv->tx_power) : priv->tx_power; - } - if (ipw_send_tx_power(priv, &tx_power)) - return -EIO; - } - return 0; -} - -static int ipw_send_rts_threshold(struct ipw_priv *priv, u16 rts) -{ - struct ipw_rts_threshold rts_threshold = { - .rts_threshold = cpu_to_le16(rts), - }; - - if (!priv) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - return ipw_send_cmd_pdu(priv, IPW_CMD_RTS_THRESHOLD, - sizeof(rts_threshold), &rts_threshold); -} - -static int ipw_send_frag_threshold(struct ipw_priv *priv, u16 frag) -{ - struct ipw_frag_threshold frag_threshold = { - .frag_threshold = cpu_to_le16(frag), - }; - - if (!priv) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - return ipw_send_cmd_pdu(priv, IPW_CMD_FRAG_THRESHOLD, - sizeof(frag_threshold), &frag_threshold); -} - -static int ipw_send_power_mode(struct ipw_priv *priv, u32 mode) -{ - __le32 param; - - if (!priv) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - /* If on battery, set to 3, if AC set to CAM, else user - * level */ - switch (mode) { - case IPW_POWER_BATTERY: - param = cpu_to_le32(IPW_POWER_INDEX_3); - break; - case IPW_POWER_AC: - param = cpu_to_le32(IPW_POWER_MODE_CAM); - break; - default: - param = cpu_to_le32(mode); - break; - } - - return ipw_send_cmd_pdu(priv, IPW_CMD_POWER_MODE, sizeof(param), - ¶m); -} - -static int ipw_send_retry_limit(struct ipw_priv *priv, u8 slimit, u8 llimit) -{ - struct ipw_retry_limit retry_limit = { - .short_retry_limit = slimit, - .long_retry_limit = llimit - }; - - if (!priv) { - IPW_ERROR("Invalid args\n"); - return -1; - } - - return ipw_send_cmd_pdu(priv, IPW_CMD_RETRY_LIMIT, sizeof(retry_limit), - &retry_limit); -} - -/* - * The IPW device contains a Microwire compatible EEPROM that stores - * various data like the MAC address. Usually the firmware has exclusive - * access to the eeprom, but during device initialization (before the - * device driver has sent the HostComplete command to the firmware) the - * device driver has read access to the EEPROM by way of indirect addressing - * through a couple of memory mapped registers. - * - * The following is a simplified implementation for pulling data out of the - * the eeprom, along with some helper functions to find information in - * the per device private data's copy of the eeprom. - * - * NOTE: To better understand how these functions work (i.e what is a chip - * select and why do have to keep driving the eeprom clock?), read - * just about any data sheet for a Microwire compatible EEPROM. - */ - -/* write a 32 bit value into the indirect accessor register */ -static inline void eeprom_write_reg(struct ipw_priv *p, u32 data) -{ - ipw_write_reg32(p, FW_MEM_REG_EEPROM_ACCESS, data); - - /* the eeprom requires some time to complete the operation */ - udelay(p->eeprom_delay); -} - -/* perform a chip select operation */ -static void eeprom_cs(struct ipw_priv *priv) -{ - eeprom_write_reg(priv, 0); - eeprom_write_reg(priv, EEPROM_BIT_CS); - eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK); - eeprom_write_reg(priv, EEPROM_BIT_CS); -} - -/* perform a chip select operation */ -static void eeprom_disable_cs(struct ipw_priv *priv) -{ - eeprom_write_reg(priv, EEPROM_BIT_CS); - eeprom_write_reg(priv, 0); - eeprom_write_reg(priv, EEPROM_BIT_SK); -} - -/* push a single bit down to the eeprom */ -static inline void eeprom_write_bit(struct ipw_priv *p, u8 bit) -{ - int d = (bit ? EEPROM_BIT_DI : 0); - eeprom_write_reg(p, EEPROM_BIT_CS | d); - eeprom_write_reg(p, EEPROM_BIT_CS | d | EEPROM_BIT_SK); -} - -/* push an opcode followed by an address down to the eeprom */ -static void eeprom_op(struct ipw_priv *priv, u8 op, u8 addr) -{ - int i; - - eeprom_cs(priv); - eeprom_write_bit(priv, 1); - eeprom_write_bit(priv, op & 2); - eeprom_write_bit(priv, op & 1); - for (i = 7; i >= 0; i--) { - eeprom_write_bit(priv, addr & (1 << i)); - } -} - -/* pull 16 bits off the eeprom, one bit at a time */ -static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr) -{ - int i; - u16 r = 0; - - /* Send READ Opcode */ - eeprom_op(priv, EEPROM_CMD_READ, addr); - - /* Send dummy bit */ - eeprom_write_reg(priv, EEPROM_BIT_CS); - - /* Read the byte off the eeprom one bit at a time */ - for (i = 0; i < 16; i++) { - u32 data = 0; - eeprom_write_reg(priv, EEPROM_BIT_CS | EEPROM_BIT_SK); - eeprom_write_reg(priv, EEPROM_BIT_CS); - data = ipw_read_reg32(priv, FW_MEM_REG_EEPROM_ACCESS); - r = (r << 1) | ((data & EEPROM_BIT_DO) ? 1 : 0); - } - - /* Send another dummy bit */ - eeprom_write_reg(priv, 0); - eeprom_disable_cs(priv); - - return r; -} - -/* helper function for pulling the mac address out of the private */ -/* data's copy of the eeprom data */ -static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac) -{ - memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], ETH_ALEN); -} - -static void ipw_read_eeprom(struct ipw_priv *priv) -{ - int i; - __le16 *eeprom = (__le16 *) priv->eeprom; - - IPW_DEBUG_TRACE(">>\n"); - - /* read entire contents of eeprom into private buffer */ - for (i = 0; i < 128; i++) - eeprom[i] = cpu_to_le16(eeprom_read_u16(priv, (u8) i)); - - IPW_DEBUG_TRACE("<<\n"); -} - -/* - * Either the device driver (i.e. the host) or the firmware can - * load eeprom data into the designated region in SRAM. If neither - * happens then the FW will shutdown with a fatal error. - * - * In order to signal the FW to load the EEPROM, the EEPROM_LOAD_DISABLE - * bit needs region of shared SRAM needs to be non-zero. - */ -static void ipw_eeprom_init_sram(struct ipw_priv *priv) -{ - int i; - - IPW_DEBUG_TRACE(">>\n"); - - /* - If the data looks correct, then copy it to our private - copy. Otherwise let the firmware know to perform the operation - on its own. - */ - if (priv->eeprom[EEPROM_VERSION] != 0) { - IPW_DEBUG_INFO("Writing EEPROM data into SRAM\n"); - - /* write the eeprom data to sram */ - for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++) - ipw_write8(priv, IPW_EEPROM_DATA + i, priv->eeprom[i]); - - /* Do not load eeprom data on fatal error or suspend */ - ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0); - } else { - IPW_DEBUG_INFO("Enabling FW initializationg of SRAM\n"); - - /* Load eeprom data on fatal error or suspend */ - ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 1); - } - - IPW_DEBUG_TRACE("<<\n"); -} - -static void ipw_zero_memory(struct ipw_priv *priv, u32 start, u32 count) -{ - count >>= 2; - if (!count) - return; - _ipw_write32(priv, IPW_AUTOINC_ADDR, start); - while (count--) - _ipw_write32(priv, IPW_AUTOINC_DATA, 0); -} - -static inline void ipw_fw_dma_reset_command_blocks(struct ipw_priv *priv) -{ - ipw_zero_memory(priv, IPW_SHARED_SRAM_DMA_CONTROL, - CB_NUMBER_OF_ELEMENTS_SMALL * - sizeof(struct command_block)); -} - -static int ipw_fw_dma_enable(struct ipw_priv *priv) -{ /* start dma engine but no transfers yet */ - - IPW_DEBUG_FW(">> :\n"); - - /* Start the dma */ - ipw_fw_dma_reset_command_blocks(priv); - - /* Write CB base address */ - ipw_write_reg32(priv, IPW_DMA_I_CB_BASE, IPW_SHARED_SRAM_DMA_CONTROL); - - IPW_DEBUG_FW("<< :\n"); - return 0; -} - -static void ipw_fw_dma_abort(struct ipw_priv *priv) -{ - u32 control = 0; - - IPW_DEBUG_FW(">> :\n"); - - /* set the Stop and Abort bit */ - control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_STOP_AND_ABORT; - ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control); - priv->sram_desc.last_cb_index = 0; - - IPW_DEBUG_FW("<<\n"); -} - -static int ipw_fw_dma_write_command_block(struct ipw_priv *priv, int index, - struct command_block *cb) -{ - u32 address = - IPW_SHARED_SRAM_DMA_CONTROL + - (sizeof(struct command_block) * index); - IPW_DEBUG_FW(">> :\n"); - - ipw_write_indirect(priv, address, (u8 *) cb, - (int)sizeof(struct command_block)); - - IPW_DEBUG_FW("<< :\n"); - return 0; - -} - -static int ipw_fw_dma_kick(struct ipw_priv *priv) -{ - u32 control = 0; - u32 index = 0; - - IPW_DEBUG_FW(">> :\n"); - - for (index = 0; index < priv->sram_desc.last_cb_index; index++) - ipw_fw_dma_write_command_block(priv, index, - &priv->sram_desc.cb_list[index]); - - /* Enable the DMA in the CSR register */ - ipw_clear_bit(priv, IPW_RESET_REG, - IPW_RESET_REG_MASTER_DISABLED | - IPW_RESET_REG_STOP_MASTER); - - /* Set the Start bit. */ - control = DMA_CONTROL_SMALL_CB_CONST_VALUE | DMA_CB_START; - ipw_write_reg32(priv, IPW_DMA_I_DMA_CONTROL, control); - - IPW_DEBUG_FW("<< :\n"); - return 0; -} - -static void ipw_fw_dma_dump_command_block(struct ipw_priv *priv) -{ - u32 address; - u32 register_value = 0; - u32 cb_fields_address = 0; - - IPW_DEBUG_FW(">> :\n"); - address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB); - IPW_DEBUG_FW_INFO("Current CB is 0x%x\n", address); - - /* Read the DMA Controlor register */ - register_value = ipw_read_reg32(priv, IPW_DMA_I_DMA_CONTROL); - IPW_DEBUG_FW_INFO("IPW_DMA_I_DMA_CONTROL is 0x%x\n", register_value); - - /* Print the CB values */ - cb_fields_address = address; - register_value = ipw_read_reg32(priv, cb_fields_address); - IPW_DEBUG_FW_INFO("Current CB Control Field is 0x%x\n", register_value); - - cb_fields_address += sizeof(u32); - register_value = ipw_read_reg32(priv, cb_fields_address); - IPW_DEBUG_FW_INFO("Current CB Source Field is 0x%x\n", register_value); - - cb_fields_address += sizeof(u32); - register_value = ipw_read_reg32(priv, cb_fields_address); - IPW_DEBUG_FW_INFO("Current CB Destination Field is 0x%x\n", - register_value); - - cb_fields_address += sizeof(u32); - register_value = ipw_read_reg32(priv, cb_fields_address); - IPW_DEBUG_FW_INFO("Current CB Status Field is 0x%x\n", register_value); - - IPW_DEBUG_FW(">> :\n"); -} - -static int ipw_fw_dma_command_block_index(struct ipw_priv *priv) -{ - u32 current_cb_address = 0; - u32 current_cb_index = 0; - - IPW_DEBUG_FW("<< :\n"); - current_cb_address = ipw_read_reg32(priv, IPW_DMA_I_CURRENT_CB); - - current_cb_index = (current_cb_address - IPW_SHARED_SRAM_DMA_CONTROL) / - sizeof(struct command_block); - - IPW_DEBUG_FW_INFO("Current CB index 0x%x address = 0x%X\n", - current_cb_index, current_cb_address); - - IPW_DEBUG_FW(">> :\n"); - return current_cb_index; - -} - -static int ipw_fw_dma_add_command_block(struct ipw_priv *priv, - u32 src_address, - u32 dest_address, - u32 length, - int interrupt_enabled, int is_last) -{ - - u32 control = CB_VALID | CB_SRC_LE | CB_DEST_LE | CB_SRC_AUTOINC | - CB_SRC_IO_GATED | CB_DEST_AUTOINC | CB_SRC_SIZE_LONG | - CB_DEST_SIZE_LONG; - struct command_block *cb; - u32 last_cb_element = 0; - - IPW_DEBUG_FW_INFO("src_address=0x%x dest_address=0x%x length=0x%x\n", - src_address, dest_address, length); - - if (priv->sram_desc.last_cb_index >= CB_NUMBER_OF_ELEMENTS_SMALL) - return -1; - - last_cb_element = priv->sram_desc.last_cb_index; - cb = &priv->sram_desc.cb_list[last_cb_element]; - priv->sram_desc.last_cb_index++; - - /* Calculate the new CB control word */ - if (interrupt_enabled) - control |= CB_INT_ENABLED; - - if (is_last) - control |= CB_LAST_VALID; - - control |= length; - - /* Calculate the CB Element's checksum value */ - cb->status = control ^ src_address ^ dest_address; - - /* Copy the Source and Destination addresses */ - cb->dest_addr = dest_address; - cb->source_addr = src_address; - - /* Copy the Control Word last */ - cb->control = control; - - return 0; -} - -static int ipw_fw_dma_add_buffer(struct ipw_priv *priv, dma_addr_t *src_address, - int nr, u32 dest_address, u32 len) -{ - int ret, i; - u32 size; - - IPW_DEBUG_FW(">>\n"); - IPW_DEBUG_FW_INFO("nr=%d dest_address=0x%x len=0x%x\n", - nr, dest_address, len); - - for (i = 0; i < nr; i++) { - size = min_t(u32, len - i * CB_MAX_LENGTH, CB_MAX_LENGTH); - ret = ipw_fw_dma_add_command_block(priv, src_address[i], - dest_address + - i * CB_MAX_LENGTH, size, - 0, 0); - if (ret) { - IPW_DEBUG_FW_INFO(": Failed\n"); - return -1; - } else - IPW_DEBUG_FW_INFO(": Added new cb\n"); - } - - IPW_DEBUG_FW("<<\n"); - return 0; -} - -static int ipw_fw_dma_wait(struct ipw_priv *priv) -{ - u32 current_index = 0, previous_index; - u32 watchdog = 0; - - IPW_DEBUG_FW(">> :\n"); - - current_index = ipw_fw_dma_command_block_index(priv); - IPW_DEBUG_FW_INFO("sram_desc.last_cb_index:0x%08X\n", - (int)priv->sram_desc.last_cb_index); - - while (current_index < priv->sram_desc.last_cb_index) { - udelay(50); - previous_index = current_index; - current_index = ipw_fw_dma_command_block_index(priv); - - if (previous_index < current_index) { - watchdog = 0; - continue; - } - if (++watchdog > 400) { - IPW_DEBUG_FW_INFO("Timeout\n"); - ipw_fw_dma_dump_command_block(priv); - ipw_fw_dma_abort(priv); - return -1; - } - } - - ipw_fw_dma_abort(priv); - - /*Disable the DMA in the CSR register */ - ipw_set_bit(priv, IPW_RESET_REG, - IPW_RESET_REG_MASTER_DISABLED | IPW_RESET_REG_STOP_MASTER); - - IPW_DEBUG_FW("<< dmaWaitSync\n"); - return 0; -} - -static void ipw_remove_current_network(struct ipw_priv *priv) -{ - struct list_head *element, *safe; - struct libipw_network *network = NULL; - unsigned long flags; - - spin_lock_irqsave(&priv->ieee->lock, flags); - list_for_each_safe(element, safe, &priv->ieee->network_list) { - network = list_entry(element, struct libipw_network, list); - if (ether_addr_equal(network->bssid, priv->bssid)) { - list_del(element); - list_add_tail(&network->list, - &priv->ieee->network_free_list); - } - } - spin_unlock_irqrestore(&priv->ieee->lock, flags); -} - -/** - * Check that card is still alive. - * Reads debug register from domain0. - * If card is present, pre-defined value should - * be found there. - * - * @param priv - * @return 1 if card is present, 0 otherwise - */ -static inline int ipw_alive(struct ipw_priv *priv) -{ - return ipw_read32(priv, 0x90) == 0xd55555d5; -} - -/* timeout in msec, attempted in 10-msec quanta */ -static int ipw_poll_bit(struct ipw_priv *priv, u32 addr, u32 mask, - int timeout) -{ - int i = 0; - - do { - if ((ipw_read32(priv, addr) & mask) == mask) - return i; - mdelay(10); - i += 10; - } while (i < timeout); - - return -ETIME; -} - -/* These functions load the firmware and micro code for the operation of - * the ipw hardware. It assumes the buffer has all the bits for the - * image and the caller is handling the memory allocation and clean up. - */ - -static int ipw_stop_master(struct ipw_priv *priv) -{ - int rc; - - IPW_DEBUG_TRACE(">>\n"); - /* stop master. typical delay - 0 */ - ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER); - - /* timeout is in msec, polled in 10-msec quanta */ - rc = ipw_poll_bit(priv, IPW_RESET_REG, - IPW_RESET_REG_MASTER_DISABLED, 100); - if (rc < 0) { - IPW_ERROR("wait for stop master failed after 100ms\n"); - return -1; - } - - IPW_DEBUG_INFO("stop master %dms\n", rc); - - return rc; -} - -static void ipw_arc_release(struct ipw_priv *priv) -{ - IPW_DEBUG_TRACE(">>\n"); - mdelay(5); - - ipw_clear_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET); - - /* no one knows timing, for safety add some delay */ - mdelay(5); -} - -struct fw_chunk { - __le32 address; - __le32 length; -}; - -static int ipw_load_ucode(struct ipw_priv *priv, u8 * data, size_t len) -{ - int rc = 0, i, addr; - u8 cr = 0; - __le16 *image; - - image = (__le16 *) data; - - IPW_DEBUG_TRACE(">>\n"); - - rc = ipw_stop_master(priv); - - if (rc < 0) - return rc; - - for (addr = IPW_SHARED_LOWER_BOUND; - addr < IPW_REGISTER_DOMAIN1_END; addr += 4) { - ipw_write32(priv, addr, 0); - } - - /* no ucode (yet) */ - memset(&priv->dino_alive, 0, sizeof(priv->dino_alive)); - /* destroy DMA queues */ - /* reset sequence */ - - ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_ON); - ipw_arc_release(priv); - ipw_write_reg32(priv, IPW_MEM_HALT_AND_RESET, IPW_BIT_HALT_RESET_OFF); - mdelay(1); - - /* reset PHY */ - ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, IPW_BASEBAND_POWER_DOWN); - mdelay(1); - - ipw_write_reg32(priv, IPW_INTERNAL_CMD_EVENT, 0); - mdelay(1); - - /* enable ucode store */ - ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0x0); - ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_CS); - mdelay(1); - - /* write ucode */ - /** - * @bug - * Do NOT set indirect address register once and then - * store data to indirect data register in the loop. - * It seems very reasonable, but in this case DINO do not - * accept ucode. It is essential to set address each time. - */ - /* load new ipw uCode */ - for (i = 0; i < len / 2; i++) - ipw_write_reg16(priv, IPW_BASEBAND_CONTROL_STORE, - le16_to_cpu(image[i])); - - /* enable DINO */ - ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0); - ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, DINO_ENABLE_SYSTEM); - - /* this is where the igx / win driver deveates from the VAP driver. */ - - /* wait for alive response */ - for (i = 0; i < 100; i++) { - /* poll for incoming data */ - cr = ipw_read_reg8(priv, IPW_BASEBAND_CONTROL_STATUS); - if (cr & DINO_RXFIFO_DATA) - break; - mdelay(1); - } - - if (cr & DINO_RXFIFO_DATA) { - /* alive_command_responce size is NOT multiple of 4 */ - __le32 response_buffer[(sizeof(priv->dino_alive) + 3) / 4]; - - for (i = 0; i < ARRAY_SIZE(response_buffer); i++) - response_buffer[i] = - cpu_to_le32(ipw_read_reg32(priv, - IPW_BASEBAND_RX_FIFO_READ)); - memcpy(&priv->dino_alive, response_buffer, - sizeof(priv->dino_alive)); - if (priv->dino_alive.alive_command == 1 - && priv->dino_alive.ucode_valid == 1) { - rc = 0; - IPW_DEBUG_INFO - ("Microcode OK, rev. %d (0x%x) dev. %d (0x%x) " - "of %02d/%02d/%02d %02d:%02d\n", - priv->dino_alive.software_revision, - priv->dino_alive.software_revision, - priv->dino_alive.device_identifier, - priv->dino_alive.device_identifier, - priv->dino_alive.time_stamp[0], - priv->dino_alive.time_stamp[1], - priv->dino_alive.time_stamp[2], - priv->dino_alive.time_stamp[3], - priv->dino_alive.time_stamp[4]); - } else { - IPW_DEBUG_INFO("Microcode is not alive\n"); - rc = -EINVAL; - } - } else { - IPW_DEBUG_INFO("No alive response from DINO\n"); - rc = -ETIME; - } - - /* disable DINO, otherwise for some reason - firmware have problem getting alive resp. */ - ipw_write_reg8(priv, IPW_BASEBAND_CONTROL_STATUS, 0); - - return rc; -} - -static int ipw_load_firmware(struct ipw_priv *priv, u8 * data, size_t len) -{ - int ret = -1; - int offset = 0; - struct fw_chunk *chunk; - int total_nr = 0; - int i; - struct pci_pool *pool; - void **virts; - dma_addr_t *phys; - - IPW_DEBUG_TRACE("<< :\n"); - - virts = kmalloc(sizeof(void *) * CB_NUMBER_OF_ELEMENTS_SMALL, - GFP_KERNEL); - if (!virts) - return -ENOMEM; - - phys = kmalloc(sizeof(dma_addr_t) * CB_NUMBER_OF_ELEMENTS_SMALL, - GFP_KERNEL); - if (!phys) { - kfree(virts); - return -ENOMEM; - } - pool = pci_pool_create("ipw2200", priv->pci_dev, CB_MAX_LENGTH, 0, 0); - if (!pool) { - IPW_ERROR("pci_pool_create failed\n"); - kfree(phys); - kfree(virts); - return -ENOMEM; - } - - /* Start the Dma */ - ret = ipw_fw_dma_enable(priv); - - /* the DMA is already ready this would be a bug. */ - BUG_ON(priv->sram_desc.last_cb_index > 0); - - do { - u32 chunk_len; - u8 *start; - int size; - int nr = 0; - - chunk = (struct fw_chunk *)(data + offset); - offset += sizeof(struct fw_chunk); - chunk_len = le32_to_cpu(chunk->length); - start = data + offset; - - nr = (chunk_len + CB_MAX_LENGTH - 1) / CB_MAX_LENGTH; - for (i = 0; i < nr; i++) { - virts[total_nr] = pci_pool_alloc(pool, GFP_KERNEL, - &phys[total_nr]); - if (!virts[total_nr]) { - ret = -ENOMEM; - goto out; - } - size = min_t(u32, chunk_len - i * CB_MAX_LENGTH, - CB_MAX_LENGTH); - memcpy(virts[total_nr], start, size); - start += size; - total_nr++; - /* We don't support fw chunk larger than 64*8K */ - BUG_ON(total_nr > CB_NUMBER_OF_ELEMENTS_SMALL); - } - - /* build DMA packet and queue up for sending */ - /* dma to chunk->address, the chunk->length bytes from data + - * offeset*/ - /* Dma loading */ - ret = ipw_fw_dma_add_buffer(priv, &phys[total_nr - nr], - nr, le32_to_cpu(chunk->address), - chunk_len); - if (ret) { - IPW_DEBUG_INFO("dmaAddBuffer Failed\n"); - goto out; - } - - offset += chunk_len; - } while (offset < len); - - /* Run the DMA and wait for the answer */ - ret = ipw_fw_dma_kick(priv); - if (ret) { - IPW_ERROR("dmaKick Failed\n"); - goto out; - } - - ret = ipw_fw_dma_wait(priv); - if (ret) { - IPW_ERROR("dmaWaitSync Failed\n"); - goto out; - } - out: - for (i = 0; i < total_nr; i++) - pci_pool_free(pool, virts[i], phys[i]); - - pci_pool_destroy(pool); - kfree(phys); - kfree(virts); - - return ret; -} - -/* stop nic */ -static int ipw_stop_nic(struct ipw_priv *priv) -{ - int rc = 0; - - /* stop */ - ipw_write32(priv, IPW_RESET_REG, IPW_RESET_REG_STOP_MASTER); - - rc = ipw_poll_bit(priv, IPW_RESET_REG, - IPW_RESET_REG_MASTER_DISABLED, 500); - if (rc < 0) { - IPW_ERROR("wait for reg master disabled failed after 500ms\n"); - return rc; - } - - ipw_set_bit(priv, IPW_RESET_REG, CBD_RESET_REG_PRINCETON_RESET); - - return rc; -} - -static void ipw_start_nic(struct ipw_priv *priv) -{ - IPW_DEBUG_TRACE(">>\n"); - - /* prvHwStartNic release ARC */ - ipw_clear_bit(priv, IPW_RESET_REG, - IPW_RESET_REG_MASTER_DISABLED | - IPW_RESET_REG_STOP_MASTER | - CBD_RESET_REG_PRINCETON_RESET); - - /* enable power management */ - ipw_set_bit(priv, IPW_GP_CNTRL_RW, - IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY); - - IPW_DEBUG_TRACE("<<\n"); -} - -static int ipw_init_nic(struct ipw_priv *priv) -{ - int rc; - - IPW_DEBUG_TRACE(">>\n"); - /* reset */ - /*prvHwInitNic */ - /* set "initialization complete" bit to move adapter to D0 state */ - ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE); - - /* low-level PLL activation */ - ipw_write32(priv, IPW_READ_INT_REGISTER, - IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER); - - /* wait for clock stabilization */ - rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW, - IPW_GP_CNTRL_BIT_CLOCK_READY, 250); - if (rc < 0) - IPW_DEBUG_INFO("FAILED wait for clock stablization\n"); - - /* assert SW reset */ - ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET); - - udelay(10); - - /* set "initialization complete" bit to move adapter to D0 state */ - ipw_set_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_INIT_DONE); - - IPW_DEBUG_TRACE(">>\n"); - return 0; -} - -/* Call this function from process context, it will sleep in request_firmware. - * Probe is an ok place to call this from. - */ -static int ipw_reset_nic(struct ipw_priv *priv) -{ - int rc = 0; - unsigned long flags; - - IPW_DEBUG_TRACE(">>\n"); - - rc = ipw_init_nic(priv); - - spin_lock_irqsave(&priv->lock, flags); - /* Clear the 'host command active' bit... */ - priv->status &= ~STATUS_HCMD_ACTIVE; - wake_up_interruptible(&priv->wait_command_queue); - priv->status &= ~(STATUS_SCANNING | STATUS_SCAN_ABORTING); - wake_up_interruptible(&priv->wait_state); - spin_unlock_irqrestore(&priv->lock, flags); - - IPW_DEBUG_TRACE("<<\n"); - return rc; -} - - -struct ipw_fw { - __le32 ver; - __le32 boot_size; - __le32 ucode_size; - __le32 fw_size; - u8 data[0]; -}; - -static int ipw_get_fw(struct ipw_priv *priv, - const struct firmware **raw, const char *name) -{ - struct ipw_fw *fw; - int rc; - - /* ask firmware_class module to get the boot firmware off disk */ - rc = request_firmware(raw, name, &priv->pci_dev->dev); - if (rc < 0) { - IPW_ERROR("%s request_firmware failed: Reason %d\n", name, rc); - return rc; - } - - if ((*raw)->size < sizeof(*fw)) { - IPW_ERROR("%s is too small (%zd)\n", name, (*raw)->size); - return -EINVAL; - } - - fw = (void *)(*raw)->data; - - if ((*raw)->size < sizeof(*fw) + le32_to_cpu(fw->boot_size) + - le32_to_cpu(fw->ucode_size) + le32_to_cpu(fw->fw_size)) { - IPW_ERROR("%s is too small or corrupt (%zd)\n", - name, (*raw)->size); - return -EINVAL; - } - - IPW_DEBUG_INFO("Read firmware '%s' image v%d.%d (%zd bytes)\n", - name, - le32_to_cpu(fw->ver) >> 16, - le32_to_cpu(fw->ver) & 0xff, - (*raw)->size - sizeof(*fw)); - return 0; -} - -#define IPW_RX_BUF_SIZE (3000) - -static void ipw_rx_queue_reset(struct ipw_priv *priv, - struct ipw_rx_queue *rxq) -{ - unsigned long flags; - int i; - - spin_lock_irqsave(&rxq->lock, flags); - - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - - /* Fill the rx_used queue with _all_ of the Rx buffers */ - for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { - /* In the reset function, these buffers may have been allocated - * to an SKB, so we need to unmap and free potential storage */ - if (rxq->pool[i].skb != NULL) { - pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr, - IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); - dev_kfree_skb(rxq->pool[i].skb); - rxq->pool[i].skb = NULL; - } - list_add_tail(&rxq->pool[i].list, &rxq->rx_used); - } - - /* Set us so that we have processed and used all buffers, but have - * not restocked the Rx queue with fresh buffers */ - rxq->read = rxq->write = 0; - rxq->free_count = 0; - spin_unlock_irqrestore(&rxq->lock, flags); -} - -#ifdef CONFIG_PM -static int fw_loaded = 0; -static const struct firmware *raw = NULL; - -static void free_firmware(void) -{ - if (fw_loaded) { - release_firmware(raw); - raw = NULL; - fw_loaded = 0; - } -} -#else -#define free_firmware() do {} while (0) -#endif - -static int ipw_load(struct ipw_priv *priv) -{ -#ifndef CONFIG_PM - const struct firmware *raw = NULL; -#endif - struct ipw_fw *fw; - u8 *boot_img, *ucode_img, *fw_img; - u8 *name = NULL; - int rc = 0, retries = 3; - - switch (priv->ieee->iw_mode) { - case IW_MODE_ADHOC: - name = "ipw2200-ibss.fw"; - break; -#ifdef CONFIG_IPW2200_MONITOR - case IW_MODE_MONITOR: - name = "ipw2200-sniffer.fw"; - break; -#endif - case IW_MODE_INFRA: - name = "ipw2200-bss.fw"; - break; - } - - if (!name) { - rc = -EINVAL; - goto error; - } - -#ifdef CONFIG_PM - if (!fw_loaded) { -#endif - rc = ipw_get_fw(priv, &raw, name); - if (rc < 0) - goto error; -#ifdef CONFIG_PM - } -#endif - - fw = (void *)raw->data; - boot_img = &fw->data[0]; - ucode_img = &fw->data[le32_to_cpu(fw->boot_size)]; - fw_img = &fw->data[le32_to_cpu(fw->boot_size) + - le32_to_cpu(fw->ucode_size)]; - - if (rc < 0) - goto error; - - if (!priv->rxq) - priv->rxq = ipw_rx_queue_alloc(priv); - else - ipw_rx_queue_reset(priv, priv->rxq); - if (!priv->rxq) { - IPW_ERROR("Unable to initialize Rx queue\n"); - rc = -ENOMEM; - goto error; - } - - retry: - /* Ensure interrupts are disabled */ - ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL); - priv->status &= ~STATUS_INT_ENABLED; - - /* ack pending interrupts */ - ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL); - - ipw_stop_nic(priv); - - rc = ipw_reset_nic(priv); - if (rc < 0) { - IPW_ERROR("Unable to reset NIC\n"); - goto error; - } - - ipw_zero_memory(priv, IPW_NIC_SRAM_LOWER_BOUND, - IPW_NIC_SRAM_UPPER_BOUND - IPW_NIC_SRAM_LOWER_BOUND); - - /* DMA the initial boot firmware into the device */ - rc = ipw_load_firmware(priv, boot_img, le32_to_cpu(fw->boot_size)); - if (rc < 0) { - IPW_ERROR("Unable to load boot firmware: %d\n", rc); - goto error; - } - - /* kick start the device */ - ipw_start_nic(priv); - - /* wait for the device to finish its initial startup sequence */ - rc = ipw_poll_bit(priv, IPW_INTA_RW, - IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500); - if (rc < 0) { - IPW_ERROR("device failed to boot initial fw image\n"); - goto error; - } - IPW_DEBUG_INFO("initial device response after %dms\n", rc); - - /* ack fw init done interrupt */ - ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE); - - /* DMA the ucode into the device */ - rc = ipw_load_ucode(priv, ucode_img, le32_to_cpu(fw->ucode_size)); - if (rc < 0) { - IPW_ERROR("Unable to load ucode: %d\n", rc); - goto error; - } - - /* stop nic */ - ipw_stop_nic(priv); - - /* DMA bss firmware into the device */ - rc = ipw_load_firmware(priv, fw_img, le32_to_cpu(fw->fw_size)); - if (rc < 0) { - IPW_ERROR("Unable to load firmware: %d\n", rc); - goto error; - } -#ifdef CONFIG_PM - fw_loaded = 1; -#endif - - ipw_write32(priv, IPW_EEPROM_LOAD_DISABLE, 0); - - rc = ipw_queue_reset(priv); - if (rc < 0) { - IPW_ERROR("Unable to initialize queues\n"); - goto error; - } - - /* Ensure interrupts are disabled */ - ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL); - /* ack pending interrupts */ - ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL); - - /* kick start the device */ - ipw_start_nic(priv); - - if (ipw_read32(priv, IPW_INTA_RW) & IPW_INTA_BIT_PARITY_ERROR) { - if (retries > 0) { - IPW_WARNING("Parity error. Retrying init.\n"); - retries--; - goto retry; - } - - IPW_ERROR("TODO: Handle parity error -- schedule restart?\n"); - rc = -EIO; - goto error; - } - - /* wait for the device */ - rc = ipw_poll_bit(priv, IPW_INTA_RW, - IPW_INTA_BIT_FW_INITIALIZATION_DONE, 500); - if (rc < 0) { - IPW_ERROR("device failed to start within 500ms\n"); - goto error; - } - IPW_DEBUG_INFO("device response after %dms\n", rc); - - /* ack fw init done interrupt */ - ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE); - - /* read eeprom data */ - priv->eeprom_delay = 1; - ipw_read_eeprom(priv); - /* initialize the eeprom region of sram */ - ipw_eeprom_init_sram(priv); - - /* enable interrupts */ - ipw_enable_interrupts(priv); - - /* Ensure our queue has valid packets */ - ipw_rx_queue_replenish(priv); - - ipw_write32(priv, IPW_RX_READ_INDEX, priv->rxq->read); - - /* ack pending interrupts */ - ipw_write32(priv, IPW_INTA_RW, IPW_INTA_MASK_ALL); - -#ifndef CONFIG_PM - release_firmware(raw); -#endif - return 0; - - error: - if (priv->rxq) { - ipw_rx_queue_free(priv, priv->rxq); - priv->rxq = NULL; - } - ipw_tx_queue_free(priv); - release_firmware(raw); -#ifdef CONFIG_PM - fw_loaded = 0; - raw = NULL; -#endif - - return rc; -} - -/** - * DMA services - * - * Theory of operation - * - * A queue is a circular buffers with 'Read' and 'Write' pointers. - * 2 empty entries always kept in the buffer to protect from overflow. - * - * For Tx queue, there are low mark and high mark limits. If, after queuing - * the packet for Tx, free space become < low mark, Tx queue stopped. When - * reclaiming packets (on 'tx done IRQ), if free space become > high mark, - * Tx queue resumed. - * - * The IPW operates with six queues, one receive queue in the device's - * sram, one transmit queue for sending commands to the device firmware, - * and four transmit queues for data. - * - * The four transmit queues allow for performing quality of service (qos) - * transmissions as per the 802.11 protocol. Currently Linux does not - * provide a mechanism to the user for utilizing prioritized queues, so - * we only utilize the first data transmit queue (queue1). - */ - -/** - * Driver allocates buffers of this size for Rx - */ - -/** - * ipw_rx_queue_space - Return number of free slots available in queue. - */ -static int ipw_rx_queue_space(const struct ipw_rx_queue *q) -{ - int s = q->read - q->write; - if (s <= 0) - s += RX_QUEUE_SIZE; - /* keep some buffer to not confuse full and empty queue */ - s -= 2; - if (s < 0) - s = 0; - return s; -} - -static inline int ipw_tx_queue_space(const struct clx2_queue *q) -{ - int s = q->last_used - q->first_empty; - if (s <= 0) - s += q->n_bd; - s -= 2; /* keep some reserve to not confuse empty and full situations */ - if (s < 0) - s = 0; - return s; -} - -static inline int ipw_queue_inc_wrap(int index, int n_bd) -{ - return (++index == n_bd) ? 0 : index; -} - -/** - * Initialize common DMA queue structure - * - * @param q queue to init - * @param count Number of BD's to allocate. Should be power of 2 - * @param read_register Address for 'read' register - * (not offset within BAR, full address) - * @param write_register Address for 'write' register - * (not offset within BAR, full address) - * @param base_register Address for 'base' register - * (not offset within BAR, full address) - * @param size Address for 'size' register - * (not offset within BAR, full address) - */ -static void ipw_queue_init(struct ipw_priv *priv, struct clx2_queue *q, - int count, u32 read, u32 write, u32 base, u32 size) -{ - q->n_bd = count; - - q->low_mark = q->n_bd / 4; - if (q->low_mark < 4) - q->low_mark = 4; - - q->high_mark = q->n_bd / 8; - if (q->high_mark < 2) - q->high_mark = 2; - - q->first_empty = q->last_used = 0; - q->reg_r = read; - q->reg_w = write; - - ipw_write32(priv, base, q->dma_addr); - ipw_write32(priv, size, count); - ipw_write32(priv, read, 0); - ipw_write32(priv, write, 0); - - _ipw_read32(priv, 0x90); -} - -static int ipw_queue_tx_init(struct ipw_priv *priv, - struct clx2_tx_queue *q, - int count, u32 read, u32 write, u32 base, u32 size) -{ - struct pci_dev *dev = priv->pci_dev; - - q->txb = kmalloc(sizeof(q->txb[0]) * count, GFP_KERNEL); - if (!q->txb) { - IPW_ERROR("vmalloc for auxiliary BD structures failed\n"); - return -ENOMEM; - } - - q->bd = - pci_alloc_consistent(dev, sizeof(q->bd[0]) * count, &q->q.dma_addr); - if (!q->bd) { - IPW_ERROR("pci_alloc_consistent(%zd) failed\n", - sizeof(q->bd[0]) * count); - kfree(q->txb); - q->txb = NULL; - return -ENOMEM; - } - - ipw_queue_init(priv, &q->q, count, read, write, base, size); - return 0; -} - -/** - * Free one TFD, those at index [txq->q.last_used]. - * Do NOT advance any indexes - * - * @param dev - * @param txq - */ -static void ipw_queue_tx_free_tfd(struct ipw_priv *priv, - struct clx2_tx_queue *txq) -{ - struct tfd_frame *bd = &txq->bd[txq->q.last_used]; - struct pci_dev *dev = priv->pci_dev; - int i; - - /* classify bd */ - if (bd->control_flags.message_type == TX_HOST_COMMAND_TYPE) - /* nothing to cleanup after for host commands */ - return; - - /* sanity check */ - if (le32_to_cpu(bd->u.data.num_chunks) > NUM_TFD_CHUNKS) { - IPW_ERROR("Too many chunks: %i\n", - le32_to_cpu(bd->u.data.num_chunks)); - /** @todo issue fatal error, it is quite serious situation */ - return; - } - - /* unmap chunks if any */ - for (i = 0; i < le32_to_cpu(bd->u.data.num_chunks); i++) { - pci_unmap_single(dev, le32_to_cpu(bd->u.data.chunk_ptr[i]), - le16_to_cpu(bd->u.data.chunk_len[i]), - PCI_DMA_TODEVICE); - if (txq->txb[txq->q.last_used]) { - libipw_txb_free(txq->txb[txq->q.last_used]); - txq->txb[txq->q.last_used] = NULL; - } - } -} - -/** - * Deallocate DMA queue. - * - * Empty queue by removing and destroying all BD's. - * Free all buffers. - * - * @param dev - * @param q - */ -static void ipw_queue_tx_free(struct ipw_priv *priv, struct clx2_tx_queue *txq) -{ - struct clx2_queue *q = &txq->q; - struct pci_dev *dev = priv->pci_dev; - - if (q->n_bd == 0) - return; - - /* first, empty all BD's */ - for (; q->first_empty != q->last_used; - q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) { - ipw_queue_tx_free_tfd(priv, txq); - } - - /* free buffers belonging to queue itself */ - pci_free_consistent(dev, sizeof(txq->bd[0]) * q->n_bd, txq->bd, - q->dma_addr); - kfree(txq->txb); - - /* 0 fill whole structure */ - memset(txq, 0, sizeof(*txq)); -} - -/** - * Destroy all DMA queues and structures - * - * @param priv - */ -static void ipw_tx_queue_free(struct ipw_priv *priv) -{ - /* Tx CMD queue */ - ipw_queue_tx_free(priv, &priv->txq_cmd); - - /* Tx queues */ - ipw_queue_tx_free(priv, &priv->txq[0]); - ipw_queue_tx_free(priv, &priv->txq[1]); - ipw_queue_tx_free(priv, &priv->txq[2]); - ipw_queue_tx_free(priv, &priv->txq[3]); -} - -static void ipw_create_bssid(struct ipw_priv *priv, u8 * bssid) -{ - /* First 3 bytes are manufacturer */ - bssid[0] = priv->mac_addr[0]; - bssid[1] = priv->mac_addr[1]; - bssid[2] = priv->mac_addr[2]; - - /* Last bytes are random */ - get_random_bytes(&bssid[3], ETH_ALEN - 3); - - bssid[0] &= 0xfe; /* clear multicast bit */ - bssid[0] |= 0x02; /* set local assignment bit (IEEE802) */ -} - -static u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid) -{ - struct ipw_station_entry entry; - int i; - - for (i = 0; i < priv->num_stations; i++) { - if (ether_addr_equal(priv->stations[i], bssid)) { - /* Another node is active in network */ - priv->missed_adhoc_beacons = 0; - if (!(priv->config & CFG_STATIC_CHANNEL)) - /* when other nodes drop out, we drop out */ - priv->config &= ~CFG_ADHOC_PERSIST; - - return i; - } - } - - if (i == MAX_STATIONS) - return IPW_INVALID_STATION; - - IPW_DEBUG_SCAN("Adding AdHoc station: %pM\n", bssid); - - entry.reserved = 0; - entry.support_mode = 0; - memcpy(entry.mac_addr, bssid, ETH_ALEN); - memcpy(priv->stations[i], bssid, ETH_ALEN); - ipw_write_direct(priv, IPW_STATION_TABLE_LOWER + i * sizeof(entry), - &entry, sizeof(entry)); - priv->num_stations++; - - return i; -} - -static u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid) -{ - int i; - - for (i = 0; i < priv->num_stations; i++) - if (ether_addr_equal(priv->stations[i], bssid)) - return i; - - return IPW_INVALID_STATION; -} - -static void ipw_send_disassociate(struct ipw_priv *priv, int quiet) -{ - int err; - - if (priv->status & STATUS_ASSOCIATING) { - IPW_DEBUG_ASSOC("Disassociating while associating.\n"); - schedule_work(&priv->disassociate); - return; - } - - if (!(priv->status & STATUS_ASSOCIATED)) { - IPW_DEBUG_ASSOC("Disassociating while not associated.\n"); - return; - } - - IPW_DEBUG_ASSOC("Disassocation attempt from %pM " - "on channel %d.\n", - priv->assoc_request.bssid, - priv->assoc_request.channel); - - priv->status &= ~(STATUS_ASSOCIATING | STATUS_ASSOCIATED); - priv->status |= STATUS_DISASSOCIATING; - - if (quiet) - priv->assoc_request.assoc_type = HC_DISASSOC_QUIET; - else - priv->assoc_request.assoc_type = HC_DISASSOCIATE; - - err = ipw_send_associate(priv, &priv->assoc_request); - if (err) { - IPW_DEBUG_HC("Attempt to send [dis]associate command " - "failed.\n"); - return; - } - -} - -static int ipw_disassociate(void *data) -{ - struct ipw_priv *priv = data; - if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) - return 0; - ipw_send_disassociate(data, 0); - netif_carrier_off(priv->net_dev); - return 1; -} - -static void ipw_bg_disassociate(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, disassociate); - mutex_lock(&priv->mutex); - ipw_disassociate(priv); - mutex_unlock(&priv->mutex); -} - -static void ipw_system_config(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, system_config); - -#ifdef CONFIG_IPW2200_PROMISCUOUS - if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) { - priv->sys_config.accept_all_data_frames = 1; - priv->sys_config.accept_non_directed_frames = 1; - priv->sys_config.accept_all_mgmt_bcpr = 1; - priv->sys_config.accept_all_mgmt_frames = 1; - } -#endif - - ipw_send_system_config(priv); -} - -struct ipw_status_code { - u16 status; - const char *reason; -}; - -static const struct ipw_status_code ipw_status_codes[] = { - {0x00, "Successful"}, - {0x01, "Unspecified failure"}, - {0x0A, "Cannot support all requested capabilities in the " - "Capability information field"}, - {0x0B, "Reassociation denied due to inability to confirm that " - "association exists"}, - {0x0C, "Association denied due to reason outside the scope of this " - "standard"}, - {0x0D, - "Responding station does not support the specified authentication " - "algorithm"}, - {0x0E, - "Received an Authentication frame with authentication sequence " - "transaction sequence number out of expected sequence"}, - {0x0F, "Authentication rejected because of challenge failure"}, - {0x10, "Authentication rejected due to timeout waiting for next " - "frame in sequence"}, - {0x11, "Association denied because AP is unable to handle additional " - "associated stations"}, - {0x12, - "Association denied due to requesting station not supporting all " - "of the datarates in the BSSBasicServiceSet Parameter"}, - {0x13, - "Association denied due to requesting station not supporting " - "short preamble operation"}, - {0x14, - "Association denied due to requesting station not supporting " - "PBCC encoding"}, - {0x15, - "Association denied due to requesting station not supporting " - "channel agility"}, - {0x19, - "Association denied due to requesting station not supporting " - "short slot operation"}, - {0x1A, - "Association denied due to requesting station not supporting " - "DSSS-OFDM operation"}, - {0x28, "Invalid Information Element"}, - {0x29, "Group Cipher is not valid"}, - {0x2A, "Pairwise Cipher is not valid"}, - {0x2B, "AKMP is not valid"}, - {0x2C, "Unsupported RSN IE version"}, - {0x2D, "Invalid RSN IE Capabilities"}, - {0x2E, "Cipher suite is rejected per security policy"}, -}; - -static const char *ipw_get_status_code(u16 status) -{ - int i; - for (i = 0; i < ARRAY_SIZE(ipw_status_codes); i++) - if (ipw_status_codes[i].status == (status & 0xff)) - return ipw_status_codes[i].reason; - return "Unknown status value."; -} - -static void inline average_init(struct average *avg) -{ - memset(avg, 0, sizeof(*avg)); -} - -#define DEPTH_RSSI 8 -#define DEPTH_NOISE 16 -static s16 exponential_average(s16 prev_avg, s16 val, u8 depth) -{ - return ((depth-1)*prev_avg + val)/depth; -} - -static void average_add(struct average *avg, s16 val) -{ - avg->sum -= avg->entries[avg->pos]; - avg->sum += val; - avg->entries[avg->pos++] = val; - if (unlikely(avg->pos == AVG_ENTRIES)) { - avg->init = 1; - avg->pos = 0; - } -} - -static s16 average_value(struct average *avg) -{ - if (!unlikely(avg->init)) { - if (avg->pos) - return avg->sum / avg->pos; - return 0; - } - - return avg->sum / AVG_ENTRIES; -} - -static void ipw_reset_stats(struct ipw_priv *priv) -{ - u32 len = sizeof(u32); - - priv->quality = 0; - - average_init(&priv->average_missed_beacons); - priv->exp_avg_rssi = -60; - priv->exp_avg_noise = -85 + 0x100; - - priv->last_rate = 0; - priv->last_missed_beacons = 0; - priv->last_rx_packets = 0; - priv->last_tx_packets = 0; - priv->last_tx_failures = 0; - - /* Firmware managed, reset only when NIC is restarted, so we have to - * normalize on the current value */ - ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, - &priv->last_rx_err, &len); - ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, - &priv->last_tx_failures, &len); - - /* Driver managed, reset with each association */ - priv->missed_adhoc_beacons = 0; - priv->missed_beacons = 0; - priv->tx_packets = 0; - priv->rx_packets = 0; - -} - -static u32 ipw_get_max_rate(struct ipw_priv *priv) -{ - u32 i = 0x80000000; - u32 mask = priv->rates_mask; - /* If currently associated in B mode, restrict the maximum - * rate match to B rates */ - if (priv->assoc_request.ieee_mode == IPW_B_MODE) - mask &= LIBIPW_CCK_RATES_MASK; - - /* TODO: Verify that the rate is supported by the current rates - * list. */ - - while (i && !(mask & i)) - i >>= 1; - switch (i) { - case LIBIPW_CCK_RATE_1MB_MASK: - return 1000000; - case LIBIPW_CCK_RATE_2MB_MASK: - return 2000000; - case LIBIPW_CCK_RATE_5MB_MASK: - return 5500000; - case LIBIPW_OFDM_RATE_6MB_MASK: - return 6000000; - case LIBIPW_OFDM_RATE_9MB_MASK: - return 9000000; - case LIBIPW_CCK_RATE_11MB_MASK: - return 11000000; - case LIBIPW_OFDM_RATE_12MB_MASK: - return 12000000; - case LIBIPW_OFDM_RATE_18MB_MASK: - return 18000000; - case LIBIPW_OFDM_RATE_24MB_MASK: - return 24000000; - case LIBIPW_OFDM_RATE_36MB_MASK: - return 36000000; - case LIBIPW_OFDM_RATE_48MB_MASK: - return 48000000; - case LIBIPW_OFDM_RATE_54MB_MASK: - return 54000000; - } - - if (priv->ieee->mode == IEEE_B) - return 11000000; - else - return 54000000; -} - -static u32 ipw_get_current_rate(struct ipw_priv *priv) -{ - u32 rate, len = sizeof(rate); - int err; - - if (!(priv->status & STATUS_ASSOCIATED)) - return 0; - - if (priv->tx_packets > IPW_REAL_RATE_RX_PACKET_THRESHOLD) { - err = ipw_get_ordinal(priv, IPW_ORD_STAT_TX_CURR_RATE, &rate, - &len); - if (err) { - IPW_DEBUG_INFO("failed querying ordinals.\n"); - return 0; - } - } else - return ipw_get_max_rate(priv); - - switch (rate) { - case IPW_TX_RATE_1MB: - return 1000000; - case IPW_TX_RATE_2MB: - return 2000000; - case IPW_TX_RATE_5MB: - return 5500000; - case IPW_TX_RATE_6MB: - return 6000000; - case IPW_TX_RATE_9MB: - return 9000000; - case IPW_TX_RATE_11MB: - return 11000000; - case IPW_TX_RATE_12MB: - return 12000000; - case IPW_TX_RATE_18MB: - return 18000000; - case IPW_TX_RATE_24MB: - return 24000000; - case IPW_TX_RATE_36MB: - return 36000000; - case IPW_TX_RATE_48MB: - return 48000000; - case IPW_TX_RATE_54MB: - return 54000000; - } - - return 0; -} - -#define IPW_STATS_INTERVAL (2 * HZ) -static void ipw_gather_stats(struct ipw_priv *priv) -{ - u32 rx_err, rx_err_delta, rx_packets_delta; - u32 tx_failures, tx_failures_delta, tx_packets_delta; - u32 missed_beacons_percent, missed_beacons_delta; - u32 quality = 0; - u32 len = sizeof(u32); - s16 rssi; - u32 beacon_quality, signal_quality, tx_quality, rx_quality, - rate_quality; - u32 max_rate; - - if (!(priv->status & STATUS_ASSOCIATED)) { - priv->quality = 0; - return; - } - - /* Update the statistics */ - ipw_get_ordinal(priv, IPW_ORD_STAT_MISSED_BEACONS, - &priv->missed_beacons, &len); - missed_beacons_delta = priv->missed_beacons - priv->last_missed_beacons; - priv->last_missed_beacons = priv->missed_beacons; - if (priv->assoc_request.beacon_interval) { - missed_beacons_percent = missed_beacons_delta * - (HZ * le16_to_cpu(priv->assoc_request.beacon_interval)) / - (IPW_STATS_INTERVAL * 10); - } else { - missed_beacons_percent = 0; - } - average_add(&priv->average_missed_beacons, missed_beacons_percent); - - ipw_get_ordinal(priv, IPW_ORD_STAT_RX_ERR_CRC, &rx_err, &len); - rx_err_delta = rx_err - priv->last_rx_err; - priv->last_rx_err = rx_err; - - ipw_get_ordinal(priv, IPW_ORD_STAT_TX_FAILURE, &tx_failures, &len); - tx_failures_delta = tx_failures - priv->last_tx_failures; - priv->last_tx_failures = tx_failures; - - rx_packets_delta = priv->rx_packets - priv->last_rx_packets; - priv->last_rx_packets = priv->rx_packets; - - tx_packets_delta = priv->tx_packets - priv->last_tx_packets; - priv->last_tx_packets = priv->tx_packets; - - /* Calculate quality based on the following: - * - * Missed beacon: 100% = 0, 0% = 70% missed - * Rate: 60% = 1Mbs, 100% = Max - * Rx and Tx errors represent a straight % of total Rx/Tx - * RSSI: 100% = > -50, 0% = < -80 - * Rx errors: 100% = 0, 0% = 50% missed - * - * The lowest computed quality is used. - * - */ -#define BEACON_THRESHOLD 5 - beacon_quality = 100 - missed_beacons_percent; - if (beacon_quality < BEACON_THRESHOLD) - beacon_quality = 0; - else - beacon_quality = (beacon_quality - BEACON_THRESHOLD) * 100 / - (100 - BEACON_THRESHOLD); - IPW_DEBUG_STATS("Missed beacon: %3d%% (%d%%)\n", - beacon_quality, missed_beacons_percent); - - priv->last_rate = ipw_get_current_rate(priv); - max_rate = ipw_get_max_rate(priv); - rate_quality = priv->last_rate * 40 / max_rate + 60; - IPW_DEBUG_STATS("Rate quality : %3d%% (%dMbs)\n", - rate_quality, priv->last_rate / 1000000); - - if (rx_packets_delta > 100 && rx_packets_delta + rx_err_delta) - rx_quality = 100 - (rx_err_delta * 100) / - (rx_packets_delta + rx_err_delta); - else - rx_quality = 100; - IPW_DEBUG_STATS("Rx quality : %3d%% (%u errors, %u packets)\n", - rx_quality, rx_err_delta, rx_packets_delta); - - if (tx_packets_delta > 100 && tx_packets_delta + tx_failures_delta) - tx_quality = 100 - (tx_failures_delta * 100) / - (tx_packets_delta + tx_failures_delta); - else - tx_quality = 100; - IPW_DEBUG_STATS("Tx quality : %3d%% (%u errors, %u packets)\n", - tx_quality, tx_failures_delta, tx_packets_delta); - - rssi = priv->exp_avg_rssi; - signal_quality = - (100 * - (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) * - (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) - - (priv->ieee->perfect_rssi - rssi) * - (15 * (priv->ieee->perfect_rssi - priv->ieee->worst_rssi) + - 62 * (priv->ieee->perfect_rssi - rssi))) / - ((priv->ieee->perfect_rssi - priv->ieee->worst_rssi) * - (priv->ieee->perfect_rssi - priv->ieee->worst_rssi)); - if (signal_quality > 100) - signal_quality = 100; - else if (signal_quality < 1) - signal_quality = 0; - - IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n", - signal_quality, rssi); - - quality = min(rx_quality, signal_quality); - quality = min(tx_quality, quality); - quality = min(rate_quality, quality); - quality = min(beacon_quality, quality); - if (quality == beacon_quality) - IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n", - quality); - if (quality == rate_quality) - IPW_DEBUG_STATS("Quality (%d%%): Clamped to rate quality.\n", - quality); - if (quality == tx_quality) - IPW_DEBUG_STATS("Quality (%d%%): Clamped to Tx quality.\n", - quality); - if (quality == rx_quality) - IPW_DEBUG_STATS("Quality (%d%%): Clamped to Rx quality.\n", - quality); - if (quality == signal_quality) - IPW_DEBUG_STATS("Quality (%d%%): Clamped to signal quality.\n", - quality); - - priv->quality = quality; - - schedule_delayed_work(&priv->gather_stats, IPW_STATS_INTERVAL); -} - -static void ipw_bg_gather_stats(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, gather_stats.work); - mutex_lock(&priv->mutex); - ipw_gather_stats(priv); - mutex_unlock(&priv->mutex); -} - -/* Missed beacon behavior: - * 1st missed -> roaming_threshold, just wait, don't do any scan/roam. - * roaming_threshold -> disassociate_threshold, scan and roam for better signal. - * Above disassociate threshold, give up and stop scanning. - * Roaming is disabled if disassociate_threshold <= roaming_threshold */ -static void ipw_handle_missed_beacon(struct ipw_priv *priv, - int missed_count) -{ - priv->notif_missed_beacons = missed_count; - - if (missed_count > priv->disassociate_threshold && - priv->status & STATUS_ASSOCIATED) { - /* If associated and we've hit the missed - * beacon threshold, disassociate, turn - * off roaming, and abort any active scans */ - IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | - IPW_DL_STATE | IPW_DL_ASSOC, - "Missed beacon: %d - disassociate\n", missed_count); - priv->status &= ~STATUS_ROAMING; - if (priv->status & STATUS_SCANNING) { - IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | - IPW_DL_STATE, - "Aborting scan with missed beacon.\n"); - schedule_work(&priv->abort_scan); - } - - schedule_work(&priv->disassociate); - return; - } - - if (priv->status & STATUS_ROAMING) { - /* If we are currently roaming, then just - * print a debug statement... */ - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, - "Missed beacon: %d - roam in progress\n", - missed_count); - return; - } - - if (roaming && - (missed_count > priv->roaming_threshold && - missed_count <= priv->disassociate_threshold)) { - /* If we are not already roaming, set the ROAM - * bit in the status and kick off a scan. - * This can happen several times before we reach - * disassociate_threshold. */ - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, - "Missed beacon: %d - initiate " - "roaming\n", missed_count); - if (!(priv->status & STATUS_ROAMING)) { - priv->status |= STATUS_ROAMING; - if (!(priv->status & STATUS_SCANNING)) - schedule_delayed_work(&priv->request_scan, 0); - } - return; - } - - if (priv->status & STATUS_SCANNING && - missed_count > IPW_MB_SCAN_CANCEL_THRESHOLD) { - /* Stop scan to keep fw from getting - * stuck (only if we aren't roaming -- - * otherwise we'll never scan more than 2 or 3 - * channels..) */ - IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | IPW_DL_STATE, - "Aborting scan with missed beacon.\n"); - schedule_work(&priv->abort_scan); - } - - IPW_DEBUG_NOTIF("Missed beacon: %d\n", missed_count); -} - -static void ipw_scan_event(struct work_struct *work) -{ - union iwreq_data wrqu; - - struct ipw_priv *priv = - container_of(work, struct ipw_priv, scan_event.work); - - wrqu.data.length = 0; - wrqu.data.flags = 0; - wireless_send_event(priv->net_dev, SIOCGIWSCAN, &wrqu, NULL); -} - -static void handle_scan_event(struct ipw_priv *priv) -{ - /* Only userspace-requested scan completion events go out immediately */ - if (!priv->user_requested_scan) { - schedule_delayed_work(&priv->scan_event, - round_jiffies_relative(msecs_to_jiffies(4000))); - } else { - priv->user_requested_scan = 0; - mod_delayed_work(system_wq, &priv->scan_event, 0); - } -} - -/** - * Handle host notification packet. - * Called from interrupt routine - */ -static void ipw_rx_notification(struct ipw_priv *priv, - struct ipw_rx_notification *notif) -{ - u16 size = le16_to_cpu(notif->size); - - IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, size); - - switch (notif->subtype) { - case HOST_NOTIFICATION_STATUS_ASSOCIATED:{ - struct notif_association *assoc = ¬if->u.assoc; - - switch (assoc->state) { - case CMAS_ASSOCIATED:{ - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, - "associated: '%*pE' %pM\n", - priv->essid_len, priv->essid, - priv->bssid); - - switch (priv->ieee->iw_mode) { - case IW_MODE_INFRA: - memcpy(priv->ieee->bssid, - priv->bssid, ETH_ALEN); - break; - - case IW_MODE_ADHOC: - memcpy(priv->ieee->bssid, - priv->bssid, ETH_ALEN); - - /* clear out the station table */ - priv->num_stations = 0; - - IPW_DEBUG_ASSOC - ("queueing adhoc check\n"); - schedule_delayed_work( - &priv->adhoc_check, - le16_to_cpu(priv-> - assoc_request. - beacon_interval)); - break; - } - - priv->status &= ~STATUS_ASSOCIATING; - priv->status |= STATUS_ASSOCIATED; - schedule_work(&priv->system_config); - -#ifdef CONFIG_IPW2200_QOS -#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \ - le16_to_cpu(((struct ieee80211_hdr *)(x))->frame_control)) - if ((priv->status & STATUS_AUTH) && - (IPW_GET_PACKET_STYPE(¬if->u.raw) - == IEEE80211_STYPE_ASSOC_RESP)) { - if ((sizeof - (struct - libipw_assoc_response) - <= size) - && (size <= 2314)) { - struct - libipw_rx_stats - stats = { - .len = size - 1, - }; - - IPW_DEBUG_QOS - ("QoS Associate " - "size %d\n", size); - libipw_rx_mgt(priv-> - ieee, - (struct - libipw_hdr_4addr - *) - ¬if->u.raw, &stats); - } - } -#endif - - schedule_work(&priv->link_up); - - break; - } - - case CMAS_AUTHENTICATED:{ - if (priv-> - status & (STATUS_ASSOCIATED | - STATUS_AUTH)) { - struct notif_authenticate *auth - = ¬if->u.auth; - IPW_DEBUG(IPW_DL_NOTIF | - IPW_DL_STATE | - IPW_DL_ASSOC, - "deauthenticated: '%*pE' %pM: (0x%04X) - %s\n", - priv->essid_len, - priv->essid, - priv->bssid, - le16_to_cpu(auth->status), - ipw_get_status_code - (le16_to_cpu - (auth->status))); - - priv->status &= - ~(STATUS_ASSOCIATING | - STATUS_AUTH | - STATUS_ASSOCIATED); - - schedule_work(&priv->link_down); - break; - } - - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, - "authenticated: '%*pE' %pM\n", - priv->essid_len, priv->essid, - priv->bssid); - break; - } - - case CMAS_INIT:{ - if (priv->status & STATUS_AUTH) { - struct - libipw_assoc_response - *resp; - resp = - (struct - libipw_assoc_response - *)¬if->u.raw; - IPW_DEBUG(IPW_DL_NOTIF | - IPW_DL_STATE | - IPW_DL_ASSOC, - "association failed (0x%04X): %s\n", - le16_to_cpu(resp->status), - ipw_get_status_code - (le16_to_cpu - (resp->status))); - } - - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, - "disassociated: '%*pE' %pM\n", - priv->essid_len, priv->essid, - priv->bssid); - - priv->status &= - ~(STATUS_DISASSOCIATING | - STATUS_ASSOCIATING | - STATUS_ASSOCIATED | STATUS_AUTH); - if (priv->assoc_network - && (priv->assoc_network-> - capability & - WLAN_CAPABILITY_IBSS)) - ipw_remove_current_network - (priv); - - schedule_work(&priv->link_down); - - break; - } - - case CMAS_RX_ASSOC_RESP: - break; - - default: - IPW_ERROR("assoc: unknown (%d)\n", - assoc->state); - break; - } - - break; - } - - case HOST_NOTIFICATION_STATUS_AUTHENTICATE:{ - struct notif_authenticate *auth = ¬if->u.auth; - switch (auth->state) { - case CMAS_AUTHENTICATED: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, - "authenticated: '%*pE' %pM\n", - priv->essid_len, priv->essid, - priv->bssid); - priv->status |= STATUS_AUTH; - break; - - case CMAS_INIT: - if (priv->status & STATUS_AUTH) { - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, - "authentication failed (0x%04X): %s\n", - le16_to_cpu(auth->status), - ipw_get_status_code(le16_to_cpu - (auth-> - status))); - } - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, - "deauthenticated: '%*pE' %pM\n", - priv->essid_len, priv->essid, - priv->bssid); - - priv->status &= ~(STATUS_ASSOCIATING | - STATUS_AUTH | - STATUS_ASSOCIATED); - - schedule_work(&priv->link_down); - break; - - case CMAS_TX_AUTH_SEQ_1: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, "AUTH_SEQ_1\n"); - break; - case CMAS_RX_AUTH_SEQ_2: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, "AUTH_SEQ_2\n"); - break; - case CMAS_AUTH_SEQ_1_PASS: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, "AUTH_SEQ_1_PASS\n"); - break; - case CMAS_AUTH_SEQ_1_FAIL: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, "AUTH_SEQ_1_FAIL\n"); - break; - case CMAS_TX_AUTH_SEQ_3: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, "AUTH_SEQ_3\n"); - break; - case CMAS_RX_AUTH_SEQ_4: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, "RX_AUTH_SEQ_4\n"); - break; - case CMAS_AUTH_SEQ_2_PASS: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, "AUTH_SEQ_2_PASS\n"); - break; - case CMAS_AUTH_SEQ_2_FAIL: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, "AUT_SEQ_2_FAIL\n"); - break; - case CMAS_TX_ASSOC: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, "TX_ASSOC\n"); - break; - case CMAS_RX_ASSOC_RESP: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, "RX_ASSOC_RESP\n"); - - break; - case CMAS_ASSOCIATED: - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | - IPW_DL_ASSOC, "ASSOCIATED\n"); - break; - default: - IPW_DEBUG_NOTIF("auth: failure - %d\n", - auth->state); - break; - } - break; - } - - case HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT:{ - struct notif_channel_result *x = - ¬if->u.channel_result; - - if (size == sizeof(*x)) { - IPW_DEBUG_SCAN("Scan result for channel %d\n", - x->channel_num); - } else { - IPW_DEBUG_SCAN("Scan result of wrong size %d " - "(should be %zd)\n", - size, sizeof(*x)); - } - break; - } - - case HOST_NOTIFICATION_STATUS_SCAN_COMPLETED:{ - struct notif_scan_complete *x = ¬if->u.scan_complete; - if (size == sizeof(*x)) { - IPW_DEBUG_SCAN - ("Scan completed: type %d, %d channels, " - "%d status\n", x->scan_type, - x->num_channels, x->status); - } else { - IPW_ERROR("Scan completed of wrong size %d " - "(should be %zd)\n", - size, sizeof(*x)); - } - - priv->status &= - ~(STATUS_SCANNING | STATUS_SCAN_ABORTING); - - wake_up_interruptible(&priv->wait_state); - cancel_delayed_work(&priv->scan_check); - - if (priv->status & STATUS_EXIT_PENDING) - break; - - priv->ieee->scans++; - -#ifdef CONFIG_IPW2200_MONITOR - if (priv->ieee->iw_mode == IW_MODE_MONITOR) { - priv->status |= STATUS_SCAN_FORCED; - schedule_delayed_work(&priv->request_scan, 0); - break; - } - priv->status &= ~STATUS_SCAN_FORCED; -#endif /* CONFIG_IPW2200_MONITOR */ - - /* Do queued direct scans first */ - if (priv->status & STATUS_DIRECT_SCAN_PENDING) - schedule_delayed_work(&priv->request_direct_scan, 0); - - if (!(priv->status & (STATUS_ASSOCIATED | - STATUS_ASSOCIATING | - STATUS_ROAMING | - STATUS_DISASSOCIATING))) - schedule_work(&priv->associate); - else if (priv->status & STATUS_ROAMING) { - if (x->status == SCAN_COMPLETED_STATUS_COMPLETE) - /* If a scan completed and we are in roam mode, then - * the scan that completed was the one requested as a - * result of entering roam... so, schedule the - * roam work */ - schedule_work(&priv->roam); - else - /* Don't schedule if we aborted the scan */ - priv->status &= ~STATUS_ROAMING; - } else if (priv->status & STATUS_SCAN_PENDING) - schedule_delayed_work(&priv->request_scan, 0); - else if (priv->config & CFG_BACKGROUND_SCAN - && priv->status & STATUS_ASSOCIATED) - schedule_delayed_work(&priv->request_scan, - round_jiffies_relative(HZ)); - - /* Send an empty event to user space. - * We don't send the received data on the event because - * it would require us to do complex transcoding, and - * we want to minimise the work done in the irq handler - * Use a request to extract the data. - * Also, we generate this even for any scan, regardless - * on how the scan was initiated. User space can just - * sync on periodic scan to get fresh data... - * Jean II */ - if (x->status == SCAN_COMPLETED_STATUS_COMPLETE) - handle_scan_event(priv); - break; - } - - case HOST_NOTIFICATION_STATUS_FRAG_LENGTH:{ - struct notif_frag_length *x = ¬if->u.frag_len; - - if (size == sizeof(*x)) - IPW_ERROR("Frag length: %d\n", - le16_to_cpu(x->frag_length)); - else - IPW_ERROR("Frag length of wrong size %d " - "(should be %zd)\n", - size, sizeof(*x)); - break; - } - - case HOST_NOTIFICATION_STATUS_LINK_DETERIORATION:{ - struct notif_link_deterioration *x = - ¬if->u.link_deterioration; - - if (size == sizeof(*x)) { - IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE, - "link deterioration: type %d, cnt %d\n", - x->silence_notification_type, - x->silence_count); - memcpy(&priv->last_link_deterioration, x, - sizeof(*x)); - } else { - IPW_ERROR("Link Deterioration of wrong size %d " - "(should be %zd)\n", - size, sizeof(*x)); - } - break; - } - - case HOST_NOTIFICATION_DINO_CONFIG_RESPONSE:{ - IPW_ERROR("Dino config\n"); - if (priv->hcmd - && priv->hcmd->cmd != HOST_CMD_DINO_CONFIG) - IPW_ERROR("Unexpected DINO_CONFIG_RESPONSE\n"); - - break; - } - - case HOST_NOTIFICATION_STATUS_BEACON_STATE:{ - struct notif_beacon_state *x = ¬if->u.beacon_state; - if (size != sizeof(*x)) { - IPW_ERROR - ("Beacon state of wrong size %d (should " - "be %zd)\n", size, sizeof(*x)); - break; - } - - if (le32_to_cpu(x->state) == - HOST_NOTIFICATION_STATUS_BEACON_MISSING) - ipw_handle_missed_beacon(priv, - le32_to_cpu(x-> - number)); - - break; - } - - case HOST_NOTIFICATION_STATUS_TGI_TX_KEY:{ - struct notif_tgi_tx_key *x = ¬if->u.tgi_tx_key; - if (size == sizeof(*x)) { - IPW_ERROR("TGi Tx Key: state 0x%02x sec type " - "0x%02x station %d\n", - x->key_state, x->security_type, - x->station_index); - break; - } - - IPW_ERROR - ("TGi Tx Key of wrong size %d (should be %zd)\n", - size, sizeof(*x)); - break; - } - - case HOST_NOTIFICATION_CALIB_KEEP_RESULTS:{ - struct notif_calibration *x = ¬if->u.calibration; - - if (size == sizeof(*x)) { - memcpy(&priv->calib, x, sizeof(*x)); - IPW_DEBUG_INFO("TODO: Calibration\n"); - break; - } - - IPW_ERROR - ("Calibration of wrong size %d (should be %zd)\n", - size, sizeof(*x)); - break; - } - - case HOST_NOTIFICATION_NOISE_STATS:{ - if (size == sizeof(u32)) { - priv->exp_avg_noise = - exponential_average(priv->exp_avg_noise, - (u8) (le32_to_cpu(notif->u.noise.value) & 0xff), - DEPTH_NOISE); - break; - } - - IPW_ERROR - ("Noise stat is wrong size %d (should be %zd)\n", - size, sizeof(u32)); - break; - } - - default: - IPW_DEBUG_NOTIF("Unknown notification: " - "subtype=%d,flags=0x%2x,size=%d\n", - notif->subtype, notif->flags, size); - } -} - -/** - * Destroys all DMA structures and initialise them again - * - * @param priv - * @return error code - */ -static int ipw_queue_reset(struct ipw_priv *priv) -{ - int rc = 0; - /** @todo customize queue sizes */ - int nTx = 64, nTxCmd = 8; - ipw_tx_queue_free(priv); - /* Tx CMD queue */ - rc = ipw_queue_tx_init(priv, &priv->txq_cmd, nTxCmd, - IPW_TX_CMD_QUEUE_READ_INDEX, - IPW_TX_CMD_QUEUE_WRITE_INDEX, - IPW_TX_CMD_QUEUE_BD_BASE, - IPW_TX_CMD_QUEUE_BD_SIZE); - if (rc) { - IPW_ERROR("Tx Cmd queue init failed\n"); - goto error; - } - /* Tx queue(s) */ - rc = ipw_queue_tx_init(priv, &priv->txq[0], nTx, - IPW_TX_QUEUE_0_READ_INDEX, - IPW_TX_QUEUE_0_WRITE_INDEX, - IPW_TX_QUEUE_0_BD_BASE, IPW_TX_QUEUE_0_BD_SIZE); - if (rc) { - IPW_ERROR("Tx 0 queue init failed\n"); - goto error; - } - rc = ipw_queue_tx_init(priv, &priv->txq[1], nTx, - IPW_TX_QUEUE_1_READ_INDEX, - IPW_TX_QUEUE_1_WRITE_INDEX, - IPW_TX_QUEUE_1_BD_BASE, IPW_TX_QUEUE_1_BD_SIZE); - if (rc) { - IPW_ERROR("Tx 1 queue init failed\n"); - goto error; - } - rc = ipw_queue_tx_init(priv, &priv->txq[2], nTx, - IPW_TX_QUEUE_2_READ_INDEX, - IPW_TX_QUEUE_2_WRITE_INDEX, - IPW_TX_QUEUE_2_BD_BASE, IPW_TX_QUEUE_2_BD_SIZE); - if (rc) { - IPW_ERROR("Tx 2 queue init failed\n"); - goto error; - } - rc = ipw_queue_tx_init(priv, &priv->txq[3], nTx, - IPW_TX_QUEUE_3_READ_INDEX, - IPW_TX_QUEUE_3_WRITE_INDEX, - IPW_TX_QUEUE_3_BD_BASE, IPW_TX_QUEUE_3_BD_SIZE); - if (rc) { - IPW_ERROR("Tx 3 queue init failed\n"); - goto error; - } - /* statistics */ - priv->rx_bufs_min = 0; - priv->rx_pend_max = 0; - return rc; - - error: - ipw_tx_queue_free(priv); - return rc; -} - -/** - * Reclaim Tx queue entries no more used by NIC. - * - * When FW advances 'R' index, all entries between old and - * new 'R' index need to be reclaimed. As result, some free space - * forms. If there is enough free space (> low mark), wake Tx queue. - * - * @note Need to protect against garbage in 'R' index - * @param priv - * @param txq - * @param qindex - * @return Number of used entries remains in the queue - */ -static int ipw_queue_tx_reclaim(struct ipw_priv *priv, - struct clx2_tx_queue *txq, int qindex) -{ - u32 hw_tail; - int used; - struct clx2_queue *q = &txq->q; - - hw_tail = ipw_read32(priv, q->reg_r); - if (hw_tail >= q->n_bd) { - IPW_ERROR - ("Read index for DMA queue (%d) is out of range [0-%d)\n", - hw_tail, q->n_bd); - goto done; - } - for (; q->last_used != hw_tail; - q->last_used = ipw_queue_inc_wrap(q->last_used, q->n_bd)) { - ipw_queue_tx_free_tfd(priv, txq); - priv->tx_packets++; - } - done: - if ((ipw_tx_queue_space(q) > q->low_mark) && - (qindex >= 0)) - netif_wake_queue(priv->net_dev); - used = q->first_empty - q->last_used; - if (used < 0) - used += q->n_bd; - - return used; -} - -static int ipw_queue_tx_hcmd(struct ipw_priv *priv, int hcmd, void *buf, - int len, int sync) -{ - struct clx2_tx_queue *txq = &priv->txq_cmd; - struct clx2_queue *q = &txq->q; - struct tfd_frame *tfd; - - if (ipw_tx_queue_space(q) < (sync ? 1 : 2)) { - IPW_ERROR("No space for Tx\n"); - return -EBUSY; - } - - tfd = &txq->bd[q->first_empty]; - txq->txb[q->first_empty] = NULL; - - memset(tfd, 0, sizeof(*tfd)); - tfd->control_flags.message_type = TX_HOST_COMMAND_TYPE; - tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK; - priv->hcmd_seq++; - tfd->u.cmd.index = hcmd; - tfd->u.cmd.length = len; - memcpy(tfd->u.cmd.payload, buf, len); - q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd); - ipw_write32(priv, q->reg_w, q->first_empty); - _ipw_read32(priv, 0x90); - - return 0; -} - -/* - * Rx theory of operation - * - * The host allocates 32 DMA target addresses and passes the host address - * to the firmware at register IPW_RFDS_TABLE_LOWER + N * RFD_SIZE where N is - * 0 to 31 - * - * Rx Queue Indexes - * The host/firmware share two index registers for managing the Rx buffers. - * - * The READ index maps to the first position that the firmware may be writing - * to -- the driver can read up to (but not including) this position and get - * good data. - * The READ index is managed by the firmware once the card is enabled. - * - * The WRITE index maps to the last position the driver has read from -- the - * position preceding WRITE is the last slot the firmware can place a packet. - * - * The queue is empty (no good data) if WRITE = READ - 1, and is full if - * WRITE = READ. - * - * During initialization the host sets up the READ queue position to the first - * INDEX position, and WRITE to the last (READ - 1 wrapped) - * - * When the firmware places a packet in a buffer it will advance the READ index - * and fire the RX interrupt. The driver can then query the READ index and - * process as many packets as possible, moving the WRITE index forward as it - * resets the Rx queue buffers with new memory. - * - * The management in the driver is as follows: - * + A list of pre-allocated SKBs is stored in ipw->rxq->rx_free. When - * ipw->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled - * to replensish the ipw->rxq->rx_free. - * + In ipw_rx_queue_replenish (scheduled) if 'processed' != 'read' then the - * ipw->rxq is replenished and the READ INDEX is updated (updating the - * 'processed' and 'read' driver indexes as well) - * + A received packet is processed and handed to the kernel network stack, - * detached from the ipw->rxq. The driver 'processed' index is updated. - * + The Host/Firmware ipw->rxq is replenished at tasklet time from the rx_free - * list. If there are no allocated buffers in ipw->rxq->rx_free, the READ - * INDEX is not incremented and ipw->status(RX_STALLED) is set. If there - * were enough free buffers and RX_STALLED is set it is cleared. - * - * - * Driver sequence: - * - * ipw_rx_queue_alloc() Allocates rx_free - * ipw_rx_queue_replenish() Replenishes rx_free list from rx_used, and calls - * ipw_rx_queue_restock - * ipw_rx_queue_restock() Moves available buffers from rx_free into Rx - * queue, updates firmware pointers, and updates - * the WRITE index. If insufficient rx_free buffers - * are available, schedules ipw_rx_queue_replenish - * - * -- enable interrupts -- - * ISR - ipw_rx() Detach ipw_rx_mem_buffers from pool up to the - * READ INDEX, detaching the SKB from the pool. - * Moves the packet buffer from queue to rx_used. - * Calls ipw_rx_queue_restock to refill any empty - * slots. - * ... - * - */ - -/* - * If there are slots in the RX queue that need to be restocked, - * and we have free pre-allocated buffers, fill the ranks as much - * as we can pulling from rx_free. - * - * This moves the 'write' index forward to catch up with 'processed', and - * also updates the memory address in the firmware to reference the new - * target buffer. - */ -static void ipw_rx_queue_restock(struct ipw_priv *priv) -{ - struct ipw_rx_queue *rxq = priv->rxq; - struct list_head *element; - struct ipw_rx_mem_buffer *rxb; - unsigned long flags; - int write; - - spin_lock_irqsave(&rxq->lock, flags); - write = rxq->write; - while ((ipw_rx_queue_space(rxq) > 0) && (rxq->free_count)) { - element = rxq->rx_free.next; - rxb = list_entry(element, struct ipw_rx_mem_buffer, list); - list_del(element); - - ipw_write32(priv, IPW_RFDS_TABLE_LOWER + rxq->write * RFD_SIZE, - rxb->dma_addr); - rxq->queue[rxq->write] = rxb; - rxq->write = (rxq->write + 1) % RX_QUEUE_SIZE; - rxq->free_count--; - } - spin_unlock_irqrestore(&rxq->lock, flags); - - /* If the pre-allocated buffer pool is dropping low, schedule to - * refill it */ - if (rxq->free_count <= RX_LOW_WATERMARK) - schedule_work(&priv->rx_replenish); - - /* If we've added more space for the firmware to place data, tell it */ - if (write != rxq->write) - ipw_write32(priv, IPW_RX_WRITE_INDEX, rxq->write); -} - -/* - * Move all used packet from rx_used to rx_free, allocating a new SKB for each. - * Also restock the Rx queue via ipw_rx_queue_restock. - * - * This is called as a scheduled work item (except for during intialization) - */ -static void ipw_rx_queue_replenish(void *data) -{ - struct ipw_priv *priv = data; - struct ipw_rx_queue *rxq = priv->rxq; - struct list_head *element; - struct ipw_rx_mem_buffer *rxb; - unsigned long flags; - - spin_lock_irqsave(&rxq->lock, flags); - while (!list_empty(&rxq->rx_used)) { - element = rxq->rx_used.next; - rxb = list_entry(element, struct ipw_rx_mem_buffer, list); - rxb->skb = alloc_skb(IPW_RX_BUF_SIZE, GFP_ATOMIC); - if (!rxb->skb) { - printk(KERN_CRIT "%s: Can not allocate SKB buffers.\n", - priv->net_dev->name); - /* We don't reschedule replenish work here -- we will - * call the restock method and if it still needs - * more buffers it will schedule replenish */ - break; - } - list_del(element); - - rxb->dma_addr = - pci_map_single(priv->pci_dev, rxb->skb->data, - IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); - - list_add_tail(&rxb->list, &rxq->rx_free); - rxq->free_count++; - } - spin_unlock_irqrestore(&rxq->lock, flags); - - ipw_rx_queue_restock(priv); -} - -static void ipw_bg_rx_queue_replenish(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, rx_replenish); - mutex_lock(&priv->mutex); - ipw_rx_queue_replenish(priv); - mutex_unlock(&priv->mutex); -} - -/* Assumes that the skb field of the buffers in 'pool' is kept accurate. - * If an SKB has been detached, the POOL needs to have its SKB set to NULL - * This free routine walks the list of POOL entries and if SKB is set to - * non NULL it is unmapped and freed - */ -static void ipw_rx_queue_free(struct ipw_priv *priv, struct ipw_rx_queue *rxq) -{ - int i; - - if (!rxq) - return; - - for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { - if (rxq->pool[i].skb != NULL) { - pci_unmap_single(priv->pci_dev, rxq->pool[i].dma_addr, - IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); - dev_kfree_skb(rxq->pool[i].skb); - } - } - - kfree(rxq); -} - -static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv) -{ - struct ipw_rx_queue *rxq; - int i; - - rxq = kzalloc(sizeof(*rxq), GFP_KERNEL); - if (unlikely(!rxq)) { - IPW_ERROR("memory allocation failed\n"); - return NULL; - } - spin_lock_init(&rxq->lock); - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - - /* Fill the rx_used queue with _all_ of the Rx buffers */ - for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) - list_add_tail(&rxq->pool[i].list, &rxq->rx_used); - - /* Set us so that we have processed and used all buffers, but have - * not restocked the Rx queue with fresh buffers */ - rxq->read = rxq->write = 0; - rxq->free_count = 0; - - return rxq; -} - -static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate) -{ - rate &= ~LIBIPW_BASIC_RATE_MASK; - if (ieee_mode == IEEE_A) { - switch (rate) { - case LIBIPW_OFDM_RATE_6MB: - return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ? - 1 : 0; - case LIBIPW_OFDM_RATE_9MB: - return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ? - 1 : 0; - case LIBIPW_OFDM_RATE_12MB: - return priv-> - rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_18MB: - return priv-> - rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_24MB: - return priv-> - rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_36MB: - return priv-> - rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_48MB: - return priv-> - rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_54MB: - return priv-> - rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0; - default: - return 0; - } - } - - /* B and G mixed */ - switch (rate) { - case LIBIPW_CCK_RATE_1MB: - return priv->rates_mask & LIBIPW_CCK_RATE_1MB_MASK ? 1 : 0; - case LIBIPW_CCK_RATE_2MB: - return priv->rates_mask & LIBIPW_CCK_RATE_2MB_MASK ? 1 : 0; - case LIBIPW_CCK_RATE_5MB: - return priv->rates_mask & LIBIPW_CCK_RATE_5MB_MASK ? 1 : 0; - case LIBIPW_CCK_RATE_11MB: - return priv->rates_mask & LIBIPW_CCK_RATE_11MB_MASK ? 1 : 0; - } - - /* If we are limited to B modulations, bail at this point */ - if (ieee_mode == IEEE_B) - return 0; - - /* G */ - switch (rate) { - case LIBIPW_OFDM_RATE_6MB: - return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_9MB: - return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_12MB: - return priv->rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_18MB: - return priv->rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_24MB: - return priv->rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_36MB: - return priv->rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_48MB: - return priv->rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0; - case LIBIPW_OFDM_RATE_54MB: - return priv->rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0; - } - - return 0; -} - -static int ipw_compatible_rates(struct ipw_priv *priv, - const struct libipw_network *network, - struct ipw_supported_rates *rates) -{ - int num_rates, i; - - memset(rates, 0, sizeof(*rates)); - num_rates = min(network->rates_len, (u8) IPW_MAX_RATES); - rates->num_rates = 0; - for (i = 0; i < num_rates; i++) { - if (!ipw_is_rate_in_mask(priv, network->mode, - network->rates[i])) { - - if (network->rates[i] & LIBIPW_BASIC_RATE_MASK) { - IPW_DEBUG_SCAN("Adding masked mandatory " - "rate %02X\n", - network->rates[i]); - rates->supported_rates[rates->num_rates++] = - network->rates[i]; - continue; - } - - IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n", - network->rates[i], priv->rates_mask); - continue; - } - - rates->supported_rates[rates->num_rates++] = network->rates[i]; - } - - num_rates = min(network->rates_ex_len, - (u8) (IPW_MAX_RATES - num_rates)); - for (i = 0; i < num_rates; i++) { - if (!ipw_is_rate_in_mask(priv, network->mode, - network->rates_ex[i])) { - if (network->rates_ex[i] & LIBIPW_BASIC_RATE_MASK) { - IPW_DEBUG_SCAN("Adding masked mandatory " - "rate %02X\n", - network->rates_ex[i]); - rates->supported_rates[rates->num_rates++] = - network->rates[i]; - continue; - } - - IPW_DEBUG_SCAN("Rate %02X masked : 0x%08X\n", - network->rates_ex[i], priv->rates_mask); - continue; - } - - rates->supported_rates[rates->num_rates++] = - network->rates_ex[i]; - } - - return 1; -} - -static void ipw_copy_rates(struct ipw_supported_rates *dest, - const struct ipw_supported_rates *src) -{ - u8 i; - for (i = 0; i < src->num_rates; i++) - dest->supported_rates[i] = src->supported_rates[i]; - dest->num_rates = src->num_rates; -} - -/* TODO: Look at sniffed packets in the air to determine if the basic rate - * mask should ever be used -- right now all callers to add the scan rates are - * set with the modulation = CCK, so BASIC_RATE_MASK is never set... */ -static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates, - u8 modulation, u32 rate_mask) -{ - u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ? - LIBIPW_BASIC_RATE_MASK : 0; - - if (rate_mask & LIBIPW_CCK_RATE_1MB_MASK) - rates->supported_rates[rates->num_rates++] = - LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_1MB; - - if (rate_mask & LIBIPW_CCK_RATE_2MB_MASK) - rates->supported_rates[rates->num_rates++] = - LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_2MB; - - if (rate_mask & LIBIPW_CCK_RATE_5MB_MASK) - rates->supported_rates[rates->num_rates++] = basic_mask | - LIBIPW_CCK_RATE_5MB; - - if (rate_mask & LIBIPW_CCK_RATE_11MB_MASK) - rates->supported_rates[rates->num_rates++] = basic_mask | - LIBIPW_CCK_RATE_11MB; -} - -static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates, - u8 modulation, u32 rate_mask) -{ - u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ? - LIBIPW_BASIC_RATE_MASK : 0; - - if (rate_mask & LIBIPW_OFDM_RATE_6MB_MASK) - rates->supported_rates[rates->num_rates++] = basic_mask | - LIBIPW_OFDM_RATE_6MB; - - if (rate_mask & LIBIPW_OFDM_RATE_9MB_MASK) - rates->supported_rates[rates->num_rates++] = - LIBIPW_OFDM_RATE_9MB; - - if (rate_mask & LIBIPW_OFDM_RATE_12MB_MASK) - rates->supported_rates[rates->num_rates++] = basic_mask | - LIBIPW_OFDM_RATE_12MB; - - if (rate_mask & LIBIPW_OFDM_RATE_18MB_MASK) - rates->supported_rates[rates->num_rates++] = - LIBIPW_OFDM_RATE_18MB; - - if (rate_mask & LIBIPW_OFDM_RATE_24MB_MASK) - rates->supported_rates[rates->num_rates++] = basic_mask | - LIBIPW_OFDM_RATE_24MB; - - if (rate_mask & LIBIPW_OFDM_RATE_36MB_MASK) - rates->supported_rates[rates->num_rates++] = - LIBIPW_OFDM_RATE_36MB; - - if (rate_mask & LIBIPW_OFDM_RATE_48MB_MASK) - rates->supported_rates[rates->num_rates++] = - LIBIPW_OFDM_RATE_48MB; - - if (rate_mask & LIBIPW_OFDM_RATE_54MB_MASK) - rates->supported_rates[rates->num_rates++] = - LIBIPW_OFDM_RATE_54MB; -} - -struct ipw_network_match { - struct libipw_network *network; - struct ipw_supported_rates rates; -}; - -static int ipw_find_adhoc_network(struct ipw_priv *priv, - struct ipw_network_match *match, - struct libipw_network *network, - int roaming) -{ - struct ipw_supported_rates rates; - - /* Verify that this network's capability is compatible with the - * current mode (AdHoc or Infrastructure) */ - if ((priv->ieee->iw_mode == IW_MODE_ADHOC && - !(network->capability & WLAN_CAPABILITY_IBSS))) { - IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded due to capability mismatch.\n", - network->ssid_len, network->ssid, - network->bssid); - return 0; - } - - if (unlikely(roaming)) { - /* If we are roaming, then ensure check if this is a valid - * network to try and roam to */ - if ((network->ssid_len != match->network->ssid_len) || - memcmp(network->ssid, match->network->ssid, - network->ssid_len)) { - IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of non-network ESSID.\n", - network->ssid_len, network->ssid, - network->bssid); - return 0; - } - } else { - /* If an ESSID has been configured then compare the broadcast - * ESSID to ours */ - if ((priv->config & CFG_STATIC_ESSID) && - ((network->ssid_len != priv->essid_len) || - memcmp(network->ssid, priv->essid, - min(network->ssid_len, priv->essid_len)))) { - IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of ESSID mismatch: '%*pE'.\n", - network->ssid_len, network->ssid, - network->bssid, priv->essid_len, - priv->essid); - return 0; - } - } - - /* If the old network rate is better than this one, don't bother - * testing everything else. */ - - if (network->time_stamp[0] < match->network->time_stamp[0]) { - IPW_DEBUG_MERGE("Network '%*pE excluded because newer than current network.\n", - match->network->ssid_len, match->network->ssid); - return 0; - } else if (network->time_stamp[1] < match->network->time_stamp[1]) { - IPW_DEBUG_MERGE("Network '%*pE excluded because newer than current network.\n", - match->network->ssid_len, match->network->ssid); - return 0; - } - - /* Now go through and see if the requested network is valid... */ - if (priv->ieee->scan_age != 0 && - time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) { - IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of age: %ums.\n", - network->ssid_len, network->ssid, - network->bssid, - jiffies_to_msecs(jiffies - - network->last_scanned)); - return 0; - } - - if ((priv->config & CFG_STATIC_CHANNEL) && - (network->channel != priv->channel)) { - IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of channel mismatch: %d != %d.\n", - network->ssid_len, network->ssid, - network->bssid, - network->channel, priv->channel); - return 0; - } - - /* Verify privacy compatibility */ - if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) != - ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) { - IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of privacy mismatch: %s != %s.\n", - network->ssid_len, network->ssid, - network->bssid, - priv-> - capability & CAP_PRIVACY_ON ? "on" : "off", - network-> - capability & WLAN_CAPABILITY_PRIVACY ? "on" : - "off"); - return 0; - } - - if (ether_addr_equal(network->bssid, priv->bssid)) { - IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of the same BSSID match: %pM.\n", - network->ssid_len, network->ssid, - network->bssid, priv->bssid); - return 0; - } - - /* Filter out any incompatible freq / mode combinations */ - if (!libipw_is_valid_mode(priv->ieee, network->mode)) { - IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of invalid frequency/mode combination.\n", - network->ssid_len, network->ssid, - network->bssid); - return 0; - } - - /* Ensure that the rates supported by the driver are compatible with - * this AP, including verification of basic rates (mandatory) */ - if (!ipw_compatible_rates(priv, network, &rates)) { - IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because configured rate mask excludes AP mandatory rate.\n", - network->ssid_len, network->ssid, - network->bssid); - return 0; - } - - if (rates.num_rates == 0) { - IPW_DEBUG_MERGE("Network '%*pE (%pM)' excluded because of no compatible rates.\n", - network->ssid_len, network->ssid, - network->bssid); - return 0; - } - - /* TODO: Perform any further minimal comparititive tests. We do not - * want to put too much policy logic here; intelligent scan selection - * should occur within a generic IEEE 802.11 user space tool. */ - - /* Set up 'new' AP to this network */ - ipw_copy_rates(&match->rates, &rates); - match->network = network; - IPW_DEBUG_MERGE("Network '%*pE (%pM)' is a viable match.\n", - network->ssid_len, network->ssid, network->bssid); - - return 1; -} - -static void ipw_merge_adhoc_network(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, merge_networks); - struct libipw_network *network = NULL; - struct ipw_network_match match = { - .network = priv->assoc_network - }; - - if ((priv->status & STATUS_ASSOCIATED) && - (priv->ieee->iw_mode == IW_MODE_ADHOC)) { - /* First pass through ROAM process -- look for a better - * network */ - unsigned long flags; - - spin_lock_irqsave(&priv->ieee->lock, flags); - list_for_each_entry(network, &priv->ieee->network_list, list) { - if (network != priv->assoc_network) - ipw_find_adhoc_network(priv, &match, network, - 1); - } - spin_unlock_irqrestore(&priv->ieee->lock, flags); - - if (match.network == priv->assoc_network) { - IPW_DEBUG_MERGE("No better ADHOC in this network to " - "merge to.\n"); - return; - } - - mutex_lock(&priv->mutex); - if ((priv->ieee->iw_mode == IW_MODE_ADHOC)) { - IPW_DEBUG_MERGE("remove network %*pE\n", - priv->essid_len, priv->essid); - ipw_remove_current_network(priv); - } - - ipw_disassociate(priv); - priv->assoc_network = match.network; - mutex_unlock(&priv->mutex); - return; - } -} - -static int ipw_best_network(struct ipw_priv *priv, - struct ipw_network_match *match, - struct libipw_network *network, int roaming) -{ - struct ipw_supported_rates rates; - - /* Verify that this network's capability is compatible with the - * current mode (AdHoc or Infrastructure) */ - if ((priv->ieee->iw_mode == IW_MODE_INFRA && - !(network->capability & WLAN_CAPABILITY_ESS)) || - (priv->ieee->iw_mode == IW_MODE_ADHOC && - !(network->capability & WLAN_CAPABILITY_IBSS))) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded due to capability mismatch.\n", - network->ssid_len, network->ssid, - network->bssid); - return 0; - } - - if (unlikely(roaming)) { - /* If we are roaming, then ensure check if this is a valid - * network to try and roam to */ - if ((network->ssid_len != match->network->ssid_len) || - memcmp(network->ssid, match->network->ssid, - network->ssid_len)) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of non-network ESSID.\n", - network->ssid_len, network->ssid, - network->bssid); - return 0; - } - } else { - /* If an ESSID has been configured then compare the broadcast - * ESSID to ours */ - if ((priv->config & CFG_STATIC_ESSID) && - ((network->ssid_len != priv->essid_len) || - memcmp(network->ssid, priv->essid, - min(network->ssid_len, priv->essid_len)))) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of ESSID mismatch: '%*pE'.\n", - network->ssid_len, network->ssid, - network->bssid, priv->essid_len, - priv->essid); - return 0; - } - } - - /* If the old network rate is better than this one, don't bother - * testing everything else. */ - if (match->network && match->network->stats.rssi > network->stats.rssi) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because '%*pE (%pM)' has a stronger signal.\n", - network->ssid_len, network->ssid, - network->bssid, match->network->ssid_len, - match->network->ssid, match->network->bssid); - return 0; - } - - /* If this network has already had an association attempt within the - * last 3 seconds, do not try and associate again... */ - if (network->last_associate && - time_after(network->last_associate + (HZ * 3UL), jiffies)) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of storming (%ums since last assoc attempt).\n", - network->ssid_len, network->ssid, - network->bssid, - jiffies_to_msecs(jiffies - - network->last_associate)); - return 0; - } - - /* Now go through and see if the requested network is valid... */ - if (priv->ieee->scan_age != 0 && - time_after(jiffies, network->last_scanned + priv->ieee->scan_age)) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of age: %ums.\n", - network->ssid_len, network->ssid, - network->bssid, - jiffies_to_msecs(jiffies - - network->last_scanned)); - return 0; - } - - if ((priv->config & CFG_STATIC_CHANNEL) && - (network->channel != priv->channel)) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of channel mismatch: %d != %d.\n", - network->ssid_len, network->ssid, - network->bssid, - network->channel, priv->channel); - return 0; - } - - /* Verify privacy compatibility */ - if (((priv->capability & CAP_PRIVACY_ON) ? 1 : 0) != - ((network->capability & WLAN_CAPABILITY_PRIVACY) ? 1 : 0)) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of privacy mismatch: %s != %s.\n", - network->ssid_len, network->ssid, - network->bssid, - priv->capability & CAP_PRIVACY_ON ? "on" : - "off", - network->capability & - WLAN_CAPABILITY_PRIVACY ? "on" : "off"); - return 0; - } - - if ((priv->config & CFG_STATIC_BSSID) && - !ether_addr_equal(network->bssid, priv->bssid)) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of BSSID mismatch: %pM.\n", - network->ssid_len, network->ssid, - network->bssid, priv->bssid); - return 0; - } - - /* Filter out any incompatible freq / mode combinations */ - if (!libipw_is_valid_mode(priv->ieee, network->mode)) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of invalid frequency/mode combination.\n", - network->ssid_len, network->ssid, - network->bssid); - return 0; - } - - /* Filter out invalid channel in current GEO */ - if (!libipw_is_valid_channel(priv->ieee, network->channel)) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of invalid channel in current GEO\n", - network->ssid_len, network->ssid, - network->bssid); - return 0; - } - - /* Ensure that the rates supported by the driver are compatible with - * this AP, including verification of basic rates (mandatory) */ - if (!ipw_compatible_rates(priv, network, &rates)) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because configured rate mask excludes AP mandatory rate.\n", - network->ssid_len, network->ssid, - network->bssid); - return 0; - } - - if (rates.num_rates == 0) { - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' excluded because of no compatible rates.\n", - network->ssid_len, network->ssid, - network->bssid); - return 0; - } - - /* TODO: Perform any further minimal comparititive tests. We do not - * want to put too much policy logic here; intelligent scan selection - * should occur within a generic IEEE 802.11 user space tool. */ - - /* Set up 'new' AP to this network */ - ipw_copy_rates(&match->rates, &rates); - match->network = network; - - IPW_DEBUG_ASSOC("Network '%*pE (%pM)' is a viable match.\n", - network->ssid_len, network->ssid, network->bssid); - - return 1; -} - -static void ipw_adhoc_create(struct ipw_priv *priv, - struct libipw_network *network) -{ - const struct libipw_geo *geo = libipw_get_geo(priv->ieee); - int i; - - /* - * For the purposes of scanning, we can set our wireless mode - * to trigger scans across combinations of bands, but when it - * comes to creating a new ad-hoc network, we have tell the FW - * exactly which band to use. - * - * We also have the possibility of an invalid channel for the - * chossen band. Attempting to create a new ad-hoc network - * with an invalid channel for wireless mode will trigger a - * FW fatal error. - * - */ - switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { - case LIBIPW_52GHZ_BAND: - network->mode = IEEE_A; - i = libipw_channel_to_index(priv->ieee, priv->channel); - BUG_ON(i == -1); - if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) { - IPW_WARNING("Overriding invalid channel\n"); - priv->channel = geo->a[0].channel; - } - break; - - case LIBIPW_24GHZ_BAND: - if (priv->ieee->mode & IEEE_G) - network->mode = IEEE_G; - else - network->mode = IEEE_B; - i = libipw_channel_to_index(priv->ieee, priv->channel); - BUG_ON(i == -1); - if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) { - IPW_WARNING("Overriding invalid channel\n"); - priv->channel = geo->bg[0].channel; - } - break; - - default: - IPW_WARNING("Overriding invalid channel\n"); - if (priv->ieee->mode & IEEE_A) { - network->mode = IEEE_A; - priv->channel = geo->a[0].channel; - } else if (priv->ieee->mode & IEEE_G) { - network->mode = IEEE_G; - priv->channel = geo->bg[0].channel; - } else { - network->mode = IEEE_B; - priv->channel = geo->bg[0].channel; - } - break; - } - - network->channel = priv->channel; - priv->config |= CFG_ADHOC_PERSIST; - ipw_create_bssid(priv, network->bssid); - network->ssid_len = priv->essid_len; - memcpy(network->ssid, priv->essid, priv->essid_len); - memset(&network->stats, 0, sizeof(network->stats)); - network->capability = WLAN_CAPABILITY_IBSS; - if (!(priv->config & CFG_PREAMBLE_LONG)) - network->capability |= WLAN_CAPABILITY_SHORT_PREAMBLE; - if (priv->capability & CAP_PRIVACY_ON) - network->capability |= WLAN_CAPABILITY_PRIVACY; - network->rates_len = min(priv->rates.num_rates, MAX_RATES_LENGTH); - memcpy(network->rates, priv->rates.supported_rates, network->rates_len); - network->rates_ex_len = priv->rates.num_rates - network->rates_len; - memcpy(network->rates_ex, - &priv->rates.supported_rates[network->rates_len], - network->rates_ex_len); - network->last_scanned = 0; - network->flags = 0; - network->last_associate = 0; - network->time_stamp[0] = 0; - network->time_stamp[1] = 0; - network->beacon_interval = 100; /* Default */ - network->listen_interval = 10; /* Default */ - network->atim_window = 0; /* Default */ - network->wpa_ie_len = 0; - network->rsn_ie_len = 0; -} - -static void ipw_send_tgi_tx_key(struct ipw_priv *priv, int type, int index) -{ - struct ipw_tgi_tx_key key; - - if (!(priv->ieee->sec.flags & (1 << index))) - return; - - key.key_id = index; - memcpy(key.key, priv->ieee->sec.keys[index], SCM_TEMPORAL_KEY_LENGTH); - key.security_type = type; - key.station_index = 0; /* always 0 for BSS */ - key.flags = 0; - /* 0 for new key; previous value of counter (after fatal error) */ - key.tx_counter[0] = cpu_to_le32(0); - key.tx_counter[1] = cpu_to_le32(0); - - ipw_send_cmd_pdu(priv, IPW_CMD_TGI_TX_KEY, sizeof(key), &key); -} - -static void ipw_send_wep_keys(struct ipw_priv *priv, int type) -{ - struct ipw_wep_key key; - int i; - - key.cmd_id = DINO_CMD_WEP_KEY; - key.seq_num = 0; - - /* Note: AES keys cannot be set for multiple times. - * Only set it at the first time. */ - for (i = 0; i < 4; i++) { - key.key_index = i | type; - if (!(priv->ieee->sec.flags & (1 << i))) { - key.key_size = 0; - continue; - } - - key.key_size = priv->ieee->sec.key_sizes[i]; - memcpy(key.key, priv->ieee->sec.keys[i], key.key_size); - - ipw_send_cmd_pdu(priv, IPW_CMD_WEP_KEY, sizeof(key), &key); - } -} - -static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level) -{ - if (priv->ieee->host_encrypt) - return; - - switch (level) { - case SEC_LEVEL_3: - priv->sys_config.disable_unicast_decryption = 0; - priv->ieee->host_decrypt = 0; - break; - case SEC_LEVEL_2: - priv->sys_config.disable_unicast_decryption = 1; - priv->ieee->host_decrypt = 1; - break; - case SEC_LEVEL_1: - priv->sys_config.disable_unicast_decryption = 0; - priv->ieee->host_decrypt = 0; - break; - case SEC_LEVEL_0: - priv->sys_config.disable_unicast_decryption = 1; - break; - default: - break; - } -} - -static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level) -{ - if (priv->ieee->host_encrypt) - return; - - switch (level) { - case SEC_LEVEL_3: - priv->sys_config.disable_multicast_decryption = 0; - break; - case SEC_LEVEL_2: - priv->sys_config.disable_multicast_decryption = 1; - break; - case SEC_LEVEL_1: - priv->sys_config.disable_multicast_decryption = 0; - break; - case SEC_LEVEL_0: - priv->sys_config.disable_multicast_decryption = 1; - break; - default: - break; - } -} - -static void ipw_set_hwcrypto_keys(struct ipw_priv *priv) -{ - switch (priv->ieee->sec.level) { - case SEC_LEVEL_3: - if (priv->ieee->sec.flags & SEC_ACTIVE_KEY) - ipw_send_tgi_tx_key(priv, - DCT_FLAG_EXT_SECURITY_CCM, - priv->ieee->sec.active_key); - - if (!priv->ieee->host_mc_decrypt) - ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM); - break; - case SEC_LEVEL_2: - if (priv->ieee->sec.flags & SEC_ACTIVE_KEY) - ipw_send_tgi_tx_key(priv, - DCT_FLAG_EXT_SECURITY_TKIP, - priv->ieee->sec.active_key); - break; - case SEC_LEVEL_1: - ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP); - ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level); - ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level); - break; - case SEC_LEVEL_0: - default: - break; - } -} - -static void ipw_adhoc_check(void *data) -{ - struct ipw_priv *priv = data; - - if (priv->missed_adhoc_beacons++ > priv->disassociate_threshold && - !(priv->config & CFG_ADHOC_PERSIST)) { - IPW_DEBUG(IPW_DL_INFO | IPW_DL_NOTIF | - IPW_DL_STATE | IPW_DL_ASSOC, - "Missed beacon: %d - disassociate\n", - priv->missed_adhoc_beacons); - ipw_remove_current_network(priv); - ipw_disassociate(priv); - return; - } - - schedule_delayed_work(&priv->adhoc_check, - le16_to_cpu(priv->assoc_request.beacon_interval)); -} - -static void ipw_bg_adhoc_check(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, adhoc_check.work); - mutex_lock(&priv->mutex); - ipw_adhoc_check(priv); - mutex_unlock(&priv->mutex); -} - -static void ipw_debug_config(struct ipw_priv *priv) -{ - IPW_DEBUG_INFO("Scan completed, no valid APs matched " - "[CFG 0x%08X]\n", priv->config); - if (priv->config & CFG_STATIC_CHANNEL) - IPW_DEBUG_INFO("Channel locked to %d\n", priv->channel); - else - IPW_DEBUG_INFO("Channel unlocked.\n"); - if (priv->config & CFG_STATIC_ESSID) - IPW_DEBUG_INFO("ESSID locked to '%*pE'\n", - priv->essid_len, priv->essid); - else - IPW_DEBUG_INFO("ESSID unlocked.\n"); - if (priv->config & CFG_STATIC_BSSID) - IPW_DEBUG_INFO("BSSID locked to %pM\n", priv->bssid); - else - IPW_DEBUG_INFO("BSSID unlocked.\n"); - if (priv->capability & CAP_PRIVACY_ON) - IPW_DEBUG_INFO("PRIVACY on\n"); - else - IPW_DEBUG_INFO("PRIVACY off\n"); - IPW_DEBUG_INFO("RATE MASK: 0x%08X\n", priv->rates_mask); -} - -static void ipw_set_fixed_rate(struct ipw_priv *priv, int mode) -{ - /* TODO: Verify that this works... */ - struct ipw_fixed_rate fr; - u32 reg; - u16 mask = 0; - u16 new_tx_rates = priv->rates_mask; - - /* Identify 'current FW band' and match it with the fixed - * Tx rates */ - - switch (priv->ieee->freq_band) { - case LIBIPW_52GHZ_BAND: /* A only */ - /* IEEE_A */ - if (priv->rates_mask & ~LIBIPW_OFDM_RATES_MASK) { - /* Invalid fixed rate mask */ - IPW_DEBUG_WX - ("invalid fixed rate mask in ipw_set_fixed_rate\n"); - new_tx_rates = 0; - break; - } - - new_tx_rates >>= LIBIPW_OFDM_SHIFT_MASK_A; - break; - - default: /* 2.4Ghz or Mixed */ - /* IEEE_B */ - if (mode == IEEE_B) { - if (new_tx_rates & ~LIBIPW_CCK_RATES_MASK) { - /* Invalid fixed rate mask */ - IPW_DEBUG_WX - ("invalid fixed rate mask in ipw_set_fixed_rate\n"); - new_tx_rates = 0; - } - break; - } - - /* IEEE_G */ - if (new_tx_rates & ~(LIBIPW_CCK_RATES_MASK | - LIBIPW_OFDM_RATES_MASK)) { - /* Invalid fixed rate mask */ - IPW_DEBUG_WX - ("invalid fixed rate mask in ipw_set_fixed_rate\n"); - new_tx_rates = 0; - break; - } - - if (LIBIPW_OFDM_RATE_6MB_MASK & new_tx_rates) { - mask |= (LIBIPW_OFDM_RATE_6MB_MASK >> 1); - new_tx_rates &= ~LIBIPW_OFDM_RATE_6MB_MASK; - } - - if (LIBIPW_OFDM_RATE_9MB_MASK & new_tx_rates) { - mask |= (LIBIPW_OFDM_RATE_9MB_MASK >> 1); - new_tx_rates &= ~LIBIPW_OFDM_RATE_9MB_MASK; - } - - if (LIBIPW_OFDM_RATE_12MB_MASK & new_tx_rates) { - mask |= (LIBIPW_OFDM_RATE_12MB_MASK >> 1); - new_tx_rates &= ~LIBIPW_OFDM_RATE_12MB_MASK; - } - - new_tx_rates |= mask; - break; - } - - fr.tx_rates = cpu_to_le16(new_tx_rates); - - reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE); - ipw_write_reg32(priv, reg, *(u32 *) & fr); -} - -static void ipw_abort_scan(struct ipw_priv *priv) -{ - int err; - - if (priv->status & STATUS_SCAN_ABORTING) { - IPW_DEBUG_HC("Ignoring concurrent scan abort request.\n"); - return; - } - priv->status |= STATUS_SCAN_ABORTING; - - err = ipw_send_scan_abort(priv); - if (err) - IPW_DEBUG_HC("Request to abort scan failed.\n"); -} - -static void ipw_add_scan_channels(struct ipw_priv *priv, - struct ipw_scan_request_ext *scan, - int scan_type) -{ - int channel_index = 0; - const struct libipw_geo *geo; - int i; - - geo = libipw_get_geo(priv->ieee); - - if (priv->ieee->freq_band & LIBIPW_52GHZ_BAND) { - int start = channel_index; - for (i = 0; i < geo->a_channels; i++) { - if ((priv->status & STATUS_ASSOCIATED) && - geo->a[i].channel == priv->channel) - continue; - channel_index++; - scan->channels_list[channel_index] = geo->a[i].channel; - ipw_set_scan_type(scan, channel_index, - geo->a[i]. - flags & LIBIPW_CH_PASSIVE_ONLY ? - IPW_SCAN_PASSIVE_FULL_DWELL_SCAN : - scan_type); - } - - if (start != channel_index) { - scan->channels_list[start] = (u8) (IPW_A_MODE << 6) | - (channel_index - start); - channel_index++; - } - } - - if (priv->ieee->freq_band & LIBIPW_24GHZ_BAND) { - int start = channel_index; - if (priv->config & CFG_SPEED_SCAN) { - int index; - u8 channels[LIBIPW_24GHZ_CHANNELS] = { - /* nop out the list */ - [0] = 0 - }; - - u8 channel; - while (channel_index < IPW_SCAN_CHANNELS - 1) { - channel = - priv->speed_scan[priv->speed_scan_pos]; - if (channel == 0) { - priv->speed_scan_pos = 0; - channel = priv->speed_scan[0]; - } - if ((priv->status & STATUS_ASSOCIATED) && - channel == priv->channel) { - priv->speed_scan_pos++; - continue; - } - - /* If this channel has already been - * added in scan, break from loop - * and this will be the first channel - * in the next scan. - */ - if (channels[channel - 1] != 0) - break; - - channels[channel - 1] = 1; - priv->speed_scan_pos++; - channel_index++; - scan->channels_list[channel_index] = channel; - index = - libipw_channel_to_index(priv->ieee, channel); - ipw_set_scan_type(scan, channel_index, - geo->bg[index]. - flags & - LIBIPW_CH_PASSIVE_ONLY ? - IPW_SCAN_PASSIVE_FULL_DWELL_SCAN - : scan_type); - } - } else { - for (i = 0; i < geo->bg_channels; i++) { - if ((priv->status & STATUS_ASSOCIATED) && - geo->bg[i].channel == priv->channel) - continue; - channel_index++; - scan->channels_list[channel_index] = - geo->bg[i].channel; - ipw_set_scan_type(scan, channel_index, - geo->bg[i]. - flags & - LIBIPW_CH_PASSIVE_ONLY ? - IPW_SCAN_PASSIVE_FULL_DWELL_SCAN - : scan_type); - } - } - - if (start != channel_index) { - scan->channels_list[start] = (u8) (IPW_B_MODE << 6) | - (channel_index - start); - } - } -} - -static int ipw_passive_dwell_time(struct ipw_priv *priv) -{ - /* staying on passive channels longer than the DTIM interval during a - * scan, while associated, causes the firmware to cancel the scan - * without notification. Hence, don't stay on passive channels longer - * than the beacon interval. - */ - if (priv->status & STATUS_ASSOCIATED - && priv->assoc_network->beacon_interval > 10) - return priv->assoc_network->beacon_interval - 10; - else - return 120; -} - -static int ipw_request_scan_helper(struct ipw_priv *priv, int type, int direct) -{ - struct ipw_scan_request_ext scan; - int err = 0, scan_type; - - if (!(priv->status & STATUS_INIT) || - (priv->status & STATUS_EXIT_PENDING)) - return 0; - - mutex_lock(&priv->mutex); - - if (direct && (priv->direct_scan_ssid_len == 0)) { - IPW_DEBUG_HC("Direct scan requested but no SSID to scan for\n"); - priv->status &= ~STATUS_DIRECT_SCAN_PENDING; - goto done; - } - - if (priv->status & STATUS_SCANNING) { - IPW_DEBUG_HC("Concurrent scan requested. Queuing.\n"); - priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING : - STATUS_SCAN_PENDING; - goto done; - } - - if (!(priv->status & STATUS_SCAN_FORCED) && - priv->status & STATUS_SCAN_ABORTING) { - IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n"); - priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING : - STATUS_SCAN_PENDING; - goto done; - } - - if (priv->status & STATUS_RF_KILL_MASK) { - IPW_DEBUG_HC("Queuing scan due to RF Kill activation\n"); - priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING : - STATUS_SCAN_PENDING; - goto done; - } - - memset(&scan, 0, sizeof(scan)); - scan.full_scan_index = cpu_to_le32(libipw_get_scans(priv->ieee)); - - if (type == IW_SCAN_TYPE_PASSIVE) { - IPW_DEBUG_WX("use passive scanning\n"); - scan_type = IPW_SCAN_PASSIVE_FULL_DWELL_SCAN; - scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = - cpu_to_le16(ipw_passive_dwell_time(priv)); - ipw_add_scan_channels(priv, &scan, scan_type); - goto send_request; - } - - /* Use active scan by default. */ - if (priv->config & CFG_SPEED_SCAN) - scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] = - cpu_to_le16(30); - else - scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] = - cpu_to_le16(20); - - scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] = - cpu_to_le16(20); - - scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = - cpu_to_le16(ipw_passive_dwell_time(priv)); - scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20); - -#ifdef CONFIG_IPW2200_MONITOR - if (priv->ieee->iw_mode == IW_MODE_MONITOR) { - u8 channel; - u8 band = 0; - - switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { - case LIBIPW_52GHZ_BAND: - band = (u8) (IPW_A_MODE << 6) | 1; - channel = priv->channel; - break; - - case LIBIPW_24GHZ_BAND: - band = (u8) (IPW_B_MODE << 6) | 1; - channel = priv->channel; - break; - - default: - band = (u8) (IPW_B_MODE << 6) | 1; - channel = 9; - break; - } - - scan.channels_list[0] = band; - scan.channels_list[1] = channel; - ipw_set_scan_type(&scan, 1, IPW_SCAN_PASSIVE_FULL_DWELL_SCAN); - - /* NOTE: The card will sit on this channel for this time - * period. Scan aborts are timing sensitive and frequently - * result in firmware restarts. As such, it is best to - * set a small dwell_time here and just keep re-issuing - * scans. Otherwise fast channel hopping will not actually - * hop channels. - * - * TODO: Move SPEED SCAN support to all modes and bands */ - scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = - cpu_to_le16(2000); - } else { -#endif /* CONFIG_IPW2200_MONITOR */ - /* Honor direct scans first, otherwise if we are roaming make - * this a direct scan for the current network. Finally, - * ensure that every other scan is a fast channel hop scan */ - if (direct) { - err = ipw_send_ssid(priv, priv->direct_scan_ssid, - priv->direct_scan_ssid_len); - if (err) { - IPW_DEBUG_HC("Attempt to send SSID command " - "failed\n"); - goto done; - } - - scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN; - } else if ((priv->status & STATUS_ROAMING) - || (!(priv->status & STATUS_ASSOCIATED) - && (priv->config & CFG_STATIC_ESSID) - && (le32_to_cpu(scan.full_scan_index) % 2))) { - err = ipw_send_ssid(priv, priv->essid, priv->essid_len); - if (err) { - IPW_DEBUG_HC("Attempt to send SSID command " - "failed.\n"); - goto done; - } - - scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN; - } else - scan_type = IPW_SCAN_ACTIVE_BROADCAST_SCAN; - - ipw_add_scan_channels(priv, &scan, scan_type); -#ifdef CONFIG_IPW2200_MONITOR - } -#endif - -send_request: - err = ipw_send_scan_request_ext(priv, &scan); - if (err) { - IPW_DEBUG_HC("Sending scan command failed: %08X\n", err); - goto done; - } - - priv->status |= STATUS_SCANNING; - if (direct) { - priv->status &= ~STATUS_DIRECT_SCAN_PENDING; - priv->direct_scan_ssid_len = 0; - } else - priv->status &= ~STATUS_SCAN_PENDING; - - schedule_delayed_work(&priv->scan_check, IPW_SCAN_CHECK_WATCHDOG); -done: - mutex_unlock(&priv->mutex); - return err; -} - -static void ipw_request_passive_scan(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, request_passive_scan.work); - ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE, 0); -} - -static void ipw_request_scan(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, request_scan.work); - ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 0); -} - -static void ipw_request_direct_scan(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, request_direct_scan.work); - ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 1); -} - -static void ipw_bg_abort_scan(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, abort_scan); - mutex_lock(&priv->mutex); - ipw_abort_scan(priv); - mutex_unlock(&priv->mutex); -} - -static int ipw_wpa_enable(struct ipw_priv *priv, int value) -{ - /* This is called when wpa_supplicant loads and closes the driver - * interface. */ - priv->ieee->wpa_enabled = value; - return 0; -} - -static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value) -{ - struct libipw_device *ieee = priv->ieee; - struct libipw_security sec = { - .flags = SEC_AUTH_MODE, - }; - int ret = 0; - - if (value & IW_AUTH_ALG_SHARED_KEY) { - sec.auth_mode = WLAN_AUTH_SHARED_KEY; - ieee->open_wep = 0; - } else if (value & IW_AUTH_ALG_OPEN_SYSTEM) { - sec.auth_mode = WLAN_AUTH_OPEN; - ieee->open_wep = 1; - } else if (value & IW_AUTH_ALG_LEAP) { - sec.auth_mode = WLAN_AUTH_LEAP; - ieee->open_wep = 1; - } else - return -EINVAL; - - if (ieee->set_security) - ieee->set_security(ieee->dev, &sec); - else - ret = -EOPNOTSUPP; - - return ret; -} - -static void ipw_wpa_assoc_frame(struct ipw_priv *priv, char *wpa_ie, - int wpa_ie_len) -{ - /* make sure WPA is enabled */ - ipw_wpa_enable(priv, 1); -} - -static int ipw_set_rsn_capa(struct ipw_priv *priv, - char *capabilities, int length) -{ - IPW_DEBUG_HC("HOST_CMD_RSN_CAPABILITIES\n"); - - return ipw_send_cmd_pdu(priv, IPW_CMD_RSN_CAPABILITIES, length, - capabilities); -} - -/* - * WE-18 support - */ - -/* SIOCSIWGENIE */ -static int ipw_wx_set_genie(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - struct libipw_device *ieee = priv->ieee; - u8 *buf; - int err = 0; - - if (wrqu->data.length > MAX_WPA_IE_LEN || - (wrqu->data.length && extra == NULL)) - return -EINVAL; - - if (wrqu->data.length) { - buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL); - if (buf == NULL) { - err = -ENOMEM; - goto out; - } - - kfree(ieee->wpa_ie); - ieee->wpa_ie = buf; - ieee->wpa_ie_len = wrqu->data.length; - } else { - kfree(ieee->wpa_ie); - ieee->wpa_ie = NULL; - ieee->wpa_ie_len = 0; - } - - ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len); - out: - return err; -} - -/* SIOCGIWGENIE */ -static int ipw_wx_get_genie(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - struct libipw_device *ieee = priv->ieee; - int err = 0; - - if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) { - wrqu->data.length = 0; - goto out; - } - - if (wrqu->data.length < ieee->wpa_ie_len) { - err = -E2BIG; - goto out; - } - - wrqu->data.length = ieee->wpa_ie_len; - memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len); - - out: - return err; -} - -static int wext_cipher2level(int cipher) -{ - switch (cipher) { - case IW_AUTH_CIPHER_NONE: - return SEC_LEVEL_0; - case IW_AUTH_CIPHER_WEP40: - case IW_AUTH_CIPHER_WEP104: - return SEC_LEVEL_1; - case IW_AUTH_CIPHER_TKIP: - return SEC_LEVEL_2; - case IW_AUTH_CIPHER_CCMP: - return SEC_LEVEL_3; - default: - return -1; - } -} - -/* SIOCSIWAUTH */ -static int ipw_wx_set_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - struct libipw_device *ieee = priv->ieee; - struct iw_param *param = &wrqu->param; - struct lib80211_crypt_data *crypt; - unsigned long flags; - int ret = 0; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - break; - case IW_AUTH_CIPHER_PAIRWISE: - ipw_set_hw_decrypt_unicast(priv, - wext_cipher2level(param->value)); - break; - case IW_AUTH_CIPHER_GROUP: - ipw_set_hw_decrypt_multicast(priv, - wext_cipher2level(param->value)); - break; - case IW_AUTH_KEY_MGMT: - /* - * ipw2200 does not use these parameters - */ - break; - - case IW_AUTH_TKIP_COUNTERMEASURES: - crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; - if (!crypt || !crypt->ops->set_flags || !crypt->ops->get_flags) - break; - - flags = crypt->ops->get_flags(crypt->priv); - - if (param->value) - flags |= IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; - else - flags &= ~IEEE80211_CRYPTO_TKIP_COUNTERMEASURES; - - crypt->ops->set_flags(flags, crypt->priv); - - break; - - case IW_AUTH_DROP_UNENCRYPTED:{ - /* HACK: - * - * wpa_supplicant calls set_wpa_enabled when the driver - * is loaded and unloaded, regardless of if WPA is being - * used. No other calls are made which can be used to - * determine if encryption will be used or not prior to - * association being expected. If encryption is not being - * used, drop_unencrypted is set to false, else true -- we - * can use this to determine if the CAP_PRIVACY_ON bit should - * be set. - */ - struct libipw_security sec = { - .flags = SEC_ENABLED, - .enabled = param->value, - }; - priv->ieee->drop_unencrypted = param->value; - /* We only change SEC_LEVEL for open mode. Others - * are set by ipw_wpa_set_encryption. - */ - if (!param->value) { - sec.flags |= SEC_LEVEL; - sec.level = SEC_LEVEL_0; - } else { - sec.flags |= SEC_LEVEL; - sec.level = SEC_LEVEL_1; - } - if (priv->ieee->set_security) - priv->ieee->set_security(priv->ieee->dev, &sec); - break; - } - - case IW_AUTH_80211_AUTH_ALG: - ret = ipw_wpa_set_auth_algs(priv, param->value); - break; - - case IW_AUTH_WPA_ENABLED: - ret = ipw_wpa_enable(priv, param->value); - ipw_disassociate(priv); - break; - - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - ieee->ieee802_1x = param->value; - break; - - case IW_AUTH_PRIVACY_INVOKED: - ieee->privacy_invoked = param->value; - break; - - default: - return -EOPNOTSUPP; - } - return ret; -} - -/* SIOCGIWAUTH */ -static int ipw_wx_get_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - struct libipw_device *ieee = priv->ieee; - struct lib80211_crypt_data *crypt; - struct iw_param *param = &wrqu->param; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_PAIRWISE: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_KEY_MGMT: - /* - * wpa_supplicant will control these internally - */ - return -EOPNOTSUPP; - - case IW_AUTH_TKIP_COUNTERMEASURES: - crypt = priv->ieee->crypt_info.crypt[priv->ieee->crypt_info.tx_keyidx]; - if (!crypt || !crypt->ops->get_flags) - break; - - param->value = (crypt->ops->get_flags(crypt->priv) & - IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) ? 1 : 0; - - break; - - case IW_AUTH_DROP_UNENCRYPTED: - param->value = ieee->drop_unencrypted; - break; - - case IW_AUTH_80211_AUTH_ALG: - param->value = ieee->sec.auth_mode; - break; - - case IW_AUTH_WPA_ENABLED: - param->value = ieee->wpa_enabled; - break; - - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - param->value = ieee->ieee802_1x; - break; - - case IW_AUTH_ROAMING_CONTROL: - case IW_AUTH_PRIVACY_INVOKED: - param->value = ieee->privacy_invoked; - break; - - default: - return -EOPNOTSUPP; - } - return 0; -} - -/* SIOCSIWENCODEEXT */ -static int ipw_wx_set_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - - if (hwcrypto) { - if (ext->alg == IW_ENCODE_ALG_TKIP) { - /* IPW HW can't build TKIP MIC, - host decryption still needed */ - if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) - priv->ieee->host_mc_decrypt = 1; - else { - priv->ieee->host_encrypt = 0; - priv->ieee->host_encrypt_msdu = 1; - priv->ieee->host_decrypt = 1; - } - } else { - priv->ieee->host_encrypt = 0; - priv->ieee->host_encrypt_msdu = 0; - priv->ieee->host_decrypt = 0; - priv->ieee->host_mc_decrypt = 0; - } - } - - return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra); -} - -/* SIOCGIWENCODEEXT */ -static int ipw_wx_get_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra); -} - -/* SIOCSIWMLME */ -static int ipw_wx_set_mlme(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - struct iw_mlme *mlme = (struct iw_mlme *)extra; - __le16 reason; - - reason = cpu_to_le16(mlme->reason_code); - - switch (mlme->cmd) { - case IW_MLME_DEAUTH: - /* silently ignore */ - break; - - case IW_MLME_DISASSOC: - ipw_disassociate(priv); - break; - - default: - return -EOPNOTSUPP; - } - return 0; -} - -#ifdef CONFIG_IPW2200_QOS - -/* QoS */ -/* -* get the modulation type of the current network or -* the card current mode -*/ -static u8 ipw_qos_current_mode(struct ipw_priv * priv) -{ - u8 mode = 0; - - if (priv->status & STATUS_ASSOCIATED) { - unsigned long flags; - - spin_lock_irqsave(&priv->ieee->lock, flags); - mode = priv->assoc_network->mode; - spin_unlock_irqrestore(&priv->ieee->lock, flags); - } else { - mode = priv->ieee->mode; - } - IPW_DEBUG_QOS("QoS network/card mode %d\n", mode); - return mode; -} - -/* -* Handle management frame beacon and probe response -*/ -static int ipw_qos_handle_probe_response(struct ipw_priv *priv, - int active_network, - struct libipw_network *network) -{ - u32 size = sizeof(struct libipw_qos_parameters); - - if (network->capability & WLAN_CAPABILITY_IBSS) - network->qos_data.active = network->qos_data.supported; - - if (network->flags & NETWORK_HAS_QOS_MASK) { - if (active_network && - (network->flags & NETWORK_HAS_QOS_PARAMETERS)) - network->qos_data.active = network->qos_data.supported; - - if ((network->qos_data.active == 1) && (active_network == 1) && - (network->flags & NETWORK_HAS_QOS_PARAMETERS) && - (network->qos_data.old_param_count != - network->qos_data.param_count)) { - network->qos_data.old_param_count = - network->qos_data.param_count; - schedule_work(&priv->qos_activate); - IPW_DEBUG_QOS("QoS parameters change call " - "qos_activate\n"); - } - } else { - if ((priv->ieee->mode == IEEE_B) || (network->mode == IEEE_B)) - memcpy(&network->qos_data.parameters, - &def_parameters_CCK, size); - else - memcpy(&network->qos_data.parameters, - &def_parameters_OFDM, size); - - if ((network->qos_data.active == 1) && (active_network == 1)) { - IPW_DEBUG_QOS("QoS was disabled call qos_activate\n"); - schedule_work(&priv->qos_activate); - } - - network->qos_data.active = 0; - network->qos_data.supported = 0; - } - if ((priv->status & STATUS_ASSOCIATED) && - (priv->ieee->iw_mode == IW_MODE_ADHOC) && (active_network == 0)) { - if (!ether_addr_equal(network->bssid, priv->bssid)) - if (network->capability & WLAN_CAPABILITY_IBSS) - if ((network->ssid_len == - priv->assoc_network->ssid_len) && - !memcmp(network->ssid, - priv->assoc_network->ssid, - network->ssid_len)) { - schedule_work(&priv->merge_networks); - } - } - - return 0; -} - -/* -* This function set up the firmware to support QoS. It sends -* IPW_CMD_QOS_PARAMETERS and IPW_CMD_WME_INFO -*/ -static int ipw_qos_activate(struct ipw_priv *priv, - struct libipw_qos_data *qos_network_data) -{ - int err; - struct libipw_qos_parameters qos_parameters[QOS_QOS_SETS]; - struct libipw_qos_parameters *active_one = NULL; - u32 size = sizeof(struct libipw_qos_parameters); - u32 burst_duration; - int i; - u8 type; - - type = ipw_qos_current_mode(priv); - - active_one = &(qos_parameters[QOS_PARAM_SET_DEF_CCK]); - memcpy(active_one, priv->qos_data.def_qos_parm_CCK, size); - active_one = &(qos_parameters[QOS_PARAM_SET_DEF_OFDM]); - memcpy(active_one, priv->qos_data.def_qos_parm_OFDM, size); - - if (qos_network_data == NULL) { - if (type == IEEE_B) { - IPW_DEBUG_QOS("QoS activate network mode %d\n", type); - active_one = &def_parameters_CCK; - } else - active_one = &def_parameters_OFDM; - - memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size); - burst_duration = ipw_qos_get_burst_duration(priv); - for (i = 0; i < QOS_QUEUE_NUM; i++) - qos_parameters[QOS_PARAM_SET_ACTIVE].tx_op_limit[i] = - cpu_to_le16(burst_duration); - } else if (priv->ieee->iw_mode == IW_MODE_ADHOC) { - if (type == IEEE_B) { - IPW_DEBUG_QOS("QoS activate IBSS network mode %d\n", - type); - if (priv->qos_data.qos_enable == 0) - active_one = &def_parameters_CCK; - else - active_one = priv->qos_data.def_qos_parm_CCK; - } else { - if (priv->qos_data.qos_enable == 0) - active_one = &def_parameters_OFDM; - else - active_one = priv->qos_data.def_qos_parm_OFDM; - } - memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size); - } else { - unsigned long flags; - int active; - - spin_lock_irqsave(&priv->ieee->lock, flags); - active_one = &(qos_network_data->parameters); - qos_network_data->old_param_count = - qos_network_data->param_count; - memcpy(&qos_parameters[QOS_PARAM_SET_ACTIVE], active_one, size); - active = qos_network_data->supported; - spin_unlock_irqrestore(&priv->ieee->lock, flags); - - if (active == 0) { - burst_duration = ipw_qos_get_burst_duration(priv); - for (i = 0; i < QOS_QUEUE_NUM; i++) - qos_parameters[QOS_PARAM_SET_ACTIVE]. - tx_op_limit[i] = cpu_to_le16(burst_duration); - } - } - - IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n"); - err = ipw_send_qos_params_command(priv, &qos_parameters[0]); - if (err) - IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n"); - - return err; -} - -/* -* send IPW_CMD_WME_INFO to the firmware -*/ -static int ipw_qos_set_info_element(struct ipw_priv *priv) -{ - int ret = 0; - struct libipw_qos_information_element qos_info; - - if (priv == NULL) - return -1; - - qos_info.elementID = QOS_ELEMENT_ID; - qos_info.length = sizeof(struct libipw_qos_information_element) - 2; - - qos_info.version = QOS_VERSION_1; - qos_info.ac_info = 0; - - memcpy(qos_info.qui, qos_oui, QOS_OUI_LEN); - qos_info.qui_type = QOS_OUI_TYPE; - qos_info.qui_subtype = QOS_OUI_INFO_SUB_TYPE; - - ret = ipw_send_qos_info_command(priv, &qos_info); - if (ret != 0) { - IPW_DEBUG_QOS("QoS error calling ipw_send_qos_info_command\n"); - } - return ret; -} - -/* -* Set the QoS parameter with the association request structure -*/ -static int ipw_qos_association(struct ipw_priv *priv, - struct libipw_network *network) -{ - int err = 0; - struct libipw_qos_data *qos_data = NULL; - struct libipw_qos_data ibss_data = { - .supported = 1, - .active = 1, - }; - - switch (priv->ieee->iw_mode) { - case IW_MODE_ADHOC: - BUG_ON(!(network->capability & WLAN_CAPABILITY_IBSS)); - - qos_data = &ibss_data; - break; - - case IW_MODE_INFRA: - qos_data = &network->qos_data; - break; - - default: - BUG(); - break; - } - - err = ipw_qos_activate(priv, qos_data); - if (err) { - priv->assoc_request.policy_support &= ~HC_QOS_SUPPORT_ASSOC; - return err; - } - - if (priv->qos_data.qos_enable && qos_data->supported) { - IPW_DEBUG_QOS("QoS will be enabled for this association\n"); - priv->assoc_request.policy_support |= HC_QOS_SUPPORT_ASSOC; - return ipw_qos_set_info_element(priv); - } - - return 0; -} - -/* -* handling the beaconing responses. if we get different QoS setting -* off the network from the associated setting, adjust the QoS -* setting -*/ -static int ipw_qos_association_resp(struct ipw_priv *priv, - struct libipw_network *network) -{ - int ret = 0; - unsigned long flags; - u32 size = sizeof(struct libipw_qos_parameters); - int set_qos_param = 0; - - if ((priv == NULL) || (network == NULL) || - (priv->assoc_network == NULL)) - return ret; - - if (!(priv->status & STATUS_ASSOCIATED)) - return ret; - - if ((priv->ieee->iw_mode != IW_MODE_INFRA)) - return ret; - - spin_lock_irqsave(&priv->ieee->lock, flags); - if (network->flags & NETWORK_HAS_QOS_PARAMETERS) { - memcpy(&priv->assoc_network->qos_data, &network->qos_data, - sizeof(struct libipw_qos_data)); - priv->assoc_network->qos_data.active = 1; - if ((network->qos_data.old_param_count != - network->qos_data.param_count)) { - set_qos_param = 1; - network->qos_data.old_param_count = - network->qos_data.param_count; - } - - } else { - if ((network->mode == IEEE_B) || (priv->ieee->mode == IEEE_B)) - memcpy(&priv->assoc_network->qos_data.parameters, - &def_parameters_CCK, size); - else - memcpy(&priv->assoc_network->qos_data.parameters, - &def_parameters_OFDM, size); - priv->assoc_network->qos_data.active = 0; - priv->assoc_network->qos_data.supported = 0; - set_qos_param = 1; - } - - spin_unlock_irqrestore(&priv->ieee->lock, flags); - - if (set_qos_param == 1) - schedule_work(&priv->qos_activate); - - return ret; -} - -static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv) -{ - u32 ret = 0; - - if ((priv == NULL)) - return 0; - - if (!(priv->ieee->modulation & LIBIPW_OFDM_MODULATION)) - ret = priv->qos_data.burst_duration_CCK; - else - ret = priv->qos_data.burst_duration_OFDM; - - return ret; -} - -/* -* Initialize the setting of QoS global -*/ -static void ipw_qos_init(struct ipw_priv *priv, int enable, - int burst_enable, u32 burst_duration_CCK, - u32 burst_duration_OFDM) -{ - priv->qos_data.qos_enable = enable; - - if (priv->qos_data.qos_enable) { - priv->qos_data.def_qos_parm_CCK = &def_qos_parameters_CCK; - priv->qos_data.def_qos_parm_OFDM = &def_qos_parameters_OFDM; - IPW_DEBUG_QOS("QoS is enabled\n"); - } else { - priv->qos_data.def_qos_parm_CCK = &def_parameters_CCK; - priv->qos_data.def_qos_parm_OFDM = &def_parameters_OFDM; - IPW_DEBUG_QOS("QoS is not enabled\n"); - } - - priv->qos_data.burst_enable = burst_enable; - - if (burst_enable) { - priv->qos_data.burst_duration_CCK = burst_duration_CCK; - priv->qos_data.burst_duration_OFDM = burst_duration_OFDM; - } else { - priv->qos_data.burst_duration_CCK = 0; - priv->qos_data.burst_duration_OFDM = 0; - } -} - -/* -* map the packet priority to the right TX Queue -*/ -static int ipw_get_tx_queue_number(struct ipw_priv *priv, u16 priority) -{ - if (priority > 7 || !priv->qos_data.qos_enable) - priority = 0; - - return from_priority_to_tx_queue[priority] - 1; -} - -static int ipw_is_qos_active(struct net_device *dev, - struct sk_buff *skb) -{ - struct ipw_priv *priv = libipw_priv(dev); - struct libipw_qos_data *qos_data = NULL; - int active, supported; - u8 *daddr = skb->data + ETH_ALEN; - int unicast = !is_multicast_ether_addr(daddr); - - if (!(priv->status & STATUS_ASSOCIATED)) - return 0; - - qos_data = &priv->assoc_network->qos_data; - - if (priv->ieee->iw_mode == IW_MODE_ADHOC) { - if (unicast == 0) - qos_data->active = 0; - else - qos_data->active = qos_data->supported; - } - active = qos_data->active; - supported = qos_data->supported; - IPW_DEBUG_QOS("QoS %d network is QoS active %d supported %d " - "unicast %d\n", - priv->qos_data.qos_enable, active, supported, unicast); - if (active && priv->qos_data.qos_enable) - return 1; - - return 0; - -} -/* -* add QoS parameter to the TX command -*/ -static int ipw_qos_set_tx_queue_command(struct ipw_priv *priv, - u16 priority, - struct tfd_data *tfd) -{ - int tx_queue_id = 0; - - - tx_queue_id = from_priority_to_tx_queue[priority] - 1; - tfd->tx_flags_ext |= DCT_FLAG_EXT_QOS_ENABLED; - - if (priv->qos_data.qos_no_ack_mask & (1UL << tx_queue_id)) { - tfd->tx_flags &= ~DCT_FLAG_ACK_REQD; - tfd->tfd.tfd_26.mchdr.qos_ctrl |= cpu_to_le16(CTRL_QOS_NO_ACK); - } - return 0; -} - -/* -* background support to run QoS activate functionality -*/ -static void ipw_bg_qos_activate(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, qos_activate); - - mutex_lock(&priv->mutex); - - if (priv->status & STATUS_ASSOCIATED) - ipw_qos_activate(priv, &(priv->assoc_network->qos_data)); - - mutex_unlock(&priv->mutex); -} - -static int ipw_handle_probe_response(struct net_device *dev, - struct libipw_probe_response *resp, - struct libipw_network *network) -{ - struct ipw_priv *priv = libipw_priv(dev); - int active_network = ((priv->status & STATUS_ASSOCIATED) && - (network == priv->assoc_network)); - - ipw_qos_handle_probe_response(priv, active_network, network); - - return 0; -} - -static int ipw_handle_beacon(struct net_device *dev, - struct libipw_beacon *resp, - struct libipw_network *network) -{ - struct ipw_priv *priv = libipw_priv(dev); - int active_network = ((priv->status & STATUS_ASSOCIATED) && - (network == priv->assoc_network)); - - ipw_qos_handle_probe_response(priv, active_network, network); - - return 0; -} - -static int ipw_handle_assoc_response(struct net_device *dev, - struct libipw_assoc_response *resp, - struct libipw_network *network) -{ - struct ipw_priv *priv = libipw_priv(dev); - ipw_qos_association_resp(priv, network); - return 0; -} - -static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters - *qos_param) -{ - return ipw_send_cmd_pdu(priv, IPW_CMD_QOS_PARAMETERS, - sizeof(*qos_param) * 3, qos_param); -} - -static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element - *qos_param) -{ - return ipw_send_cmd_pdu(priv, IPW_CMD_WME_INFO, sizeof(*qos_param), - qos_param); -} - -#endif /* CONFIG_IPW2200_QOS */ - -static int ipw_associate_network(struct ipw_priv *priv, - struct libipw_network *network, - struct ipw_supported_rates *rates, int roaming) -{ - int err; - - if (priv->config & CFG_FIXED_RATE) - ipw_set_fixed_rate(priv, network->mode); - - if (!(priv->config & CFG_STATIC_ESSID)) { - priv->essid_len = min(network->ssid_len, - (u8) IW_ESSID_MAX_SIZE); - memcpy(priv->essid, network->ssid, priv->essid_len); - } - - network->last_associate = jiffies; - - memset(&priv->assoc_request, 0, sizeof(priv->assoc_request)); - priv->assoc_request.channel = network->channel; - priv->assoc_request.auth_key = 0; - - if ((priv->capability & CAP_PRIVACY_ON) && - (priv->ieee->sec.auth_mode == WLAN_AUTH_SHARED_KEY)) { - priv->assoc_request.auth_type = AUTH_SHARED_KEY; - priv->assoc_request.auth_key = priv->ieee->sec.active_key; - - if (priv->ieee->sec.level == SEC_LEVEL_1) - ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP); - - } else if ((priv->capability & CAP_PRIVACY_ON) && - (priv->ieee->sec.auth_mode == WLAN_AUTH_LEAP)) - priv->assoc_request.auth_type = AUTH_LEAP; - else - priv->assoc_request.auth_type = AUTH_OPEN; - - if (priv->ieee->wpa_ie_len) { - priv->assoc_request.policy_support = cpu_to_le16(0x02); /* RSN active */ - ipw_set_rsn_capa(priv, priv->ieee->wpa_ie, - priv->ieee->wpa_ie_len); - } - - /* - * It is valid for our ieee device to support multiple modes, but - * when it comes to associating to a given network we have to choose - * just one mode. - */ - if (network->mode & priv->ieee->mode & IEEE_A) - priv->assoc_request.ieee_mode = IPW_A_MODE; - else if (network->mode & priv->ieee->mode & IEEE_G) - priv->assoc_request.ieee_mode = IPW_G_MODE; - else if (network->mode & priv->ieee->mode & IEEE_B) - priv->assoc_request.ieee_mode = IPW_B_MODE; - - priv->assoc_request.capability = cpu_to_le16(network->capability); - if ((network->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) - && !(priv->config & CFG_PREAMBLE_LONG)) { - priv->assoc_request.preamble_length = DCT_FLAG_SHORT_PREAMBLE; - } else { - priv->assoc_request.preamble_length = DCT_FLAG_LONG_PREAMBLE; - - /* Clear the short preamble if we won't be supporting it */ - priv->assoc_request.capability &= - ~cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); - } - - /* Clear capability bits that aren't used in Ad Hoc */ - if (priv->ieee->iw_mode == IW_MODE_ADHOC) - priv->assoc_request.capability &= - ~cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME); - - IPW_DEBUG_ASSOC("%ssociation attempt: '%*pE', channel %d, 802.11%c [%d], %s[:%s], enc=%s%s%s%c%c\n", - roaming ? "Rea" : "A", - priv->essid_len, priv->essid, - network->channel, - ipw_modes[priv->assoc_request.ieee_mode], - rates->num_rates, - (priv->assoc_request.preamble_length == - DCT_FLAG_LONG_PREAMBLE) ? "long" : "short", - network->capability & - WLAN_CAPABILITY_SHORT_PREAMBLE ? "short" : "long", - priv->capability & CAP_PRIVACY_ON ? "on " : "off", - priv->capability & CAP_PRIVACY_ON ? - (priv->capability & CAP_SHARED_KEY ? "(shared)" : - "(open)") : "", - priv->capability & CAP_PRIVACY_ON ? " key=" : "", - priv->capability & CAP_PRIVACY_ON ? - '1' + priv->ieee->sec.active_key : '.', - priv->capability & CAP_PRIVACY_ON ? '.' : ' '); - - priv->assoc_request.beacon_interval = cpu_to_le16(network->beacon_interval); - if ((priv->ieee->iw_mode == IW_MODE_ADHOC) && - (network->time_stamp[0] == 0) && (network->time_stamp[1] == 0)) { - priv->assoc_request.assoc_type = HC_IBSS_START; - priv->assoc_request.assoc_tsf_msw = 0; - priv->assoc_request.assoc_tsf_lsw = 0; - } else { - if (unlikely(roaming)) - priv->assoc_request.assoc_type = HC_REASSOCIATE; - else - priv->assoc_request.assoc_type = HC_ASSOCIATE; - priv->assoc_request.assoc_tsf_msw = cpu_to_le32(network->time_stamp[1]); - priv->assoc_request.assoc_tsf_lsw = cpu_to_le32(network->time_stamp[0]); - } - - memcpy(priv->assoc_request.bssid, network->bssid, ETH_ALEN); - - if (priv->ieee->iw_mode == IW_MODE_ADHOC) { - eth_broadcast_addr(priv->assoc_request.dest); - priv->assoc_request.atim_window = cpu_to_le16(network->atim_window); - } else { - memcpy(priv->assoc_request.dest, network->bssid, ETH_ALEN); - priv->assoc_request.atim_window = 0; - } - - priv->assoc_request.listen_interval = cpu_to_le16(network->listen_interval); - - err = ipw_send_ssid(priv, priv->essid, priv->essid_len); - if (err) { - IPW_DEBUG_HC("Attempt to send SSID command failed.\n"); - return err; - } - - rates->ieee_mode = priv->assoc_request.ieee_mode; - rates->purpose = IPW_RATE_CONNECT; - ipw_send_supported_rates(priv, rates); - - if (priv->assoc_request.ieee_mode == IPW_G_MODE) - priv->sys_config.dot11g_auto_detection = 1; - else - priv->sys_config.dot11g_auto_detection = 0; - - if (priv->ieee->iw_mode == IW_MODE_ADHOC) - priv->sys_config.answer_broadcast_ssid_probe = 1; - else - priv->sys_config.answer_broadcast_ssid_probe = 0; - - err = ipw_send_system_config(priv); - if (err) { - IPW_DEBUG_HC("Attempt to send sys config command failed.\n"); - return err; - } - - IPW_DEBUG_ASSOC("Association sensitivity: %d\n", network->stats.rssi); - err = ipw_set_sensitivity(priv, network->stats.rssi + IPW_RSSI_TO_DBM); - if (err) { - IPW_DEBUG_HC("Attempt to send associate command failed.\n"); - return err; - } - - /* - * If preemption is enabled, it is possible for the association - * to complete before we return from ipw_send_associate. Therefore - * we have to be sure and update our priviate data first. - */ - priv->channel = network->channel; - memcpy(priv->bssid, network->bssid, ETH_ALEN); - priv->status |= STATUS_ASSOCIATING; - priv->status &= ~STATUS_SECURITY_UPDATED; - - priv->assoc_network = network; - -#ifdef CONFIG_IPW2200_QOS - ipw_qos_association(priv, network); -#endif - - err = ipw_send_associate(priv, &priv->assoc_request); - if (err) { - IPW_DEBUG_HC("Attempt to send associate command failed.\n"); - return err; - } - - IPW_DEBUG(IPW_DL_STATE, "associating: '%*pE' %pM\n", - priv->essid_len, priv->essid, priv->bssid); - - return 0; -} - -static void ipw_roam(void *data) -{ - struct ipw_priv *priv = data; - struct libipw_network *network = NULL; - struct ipw_network_match match = { - .network = priv->assoc_network - }; - - /* The roaming process is as follows: - * - * 1. Missed beacon threshold triggers the roaming process by - * setting the status ROAM bit and requesting a scan. - * 2. When the scan completes, it schedules the ROAM work - * 3. The ROAM work looks at all of the known networks for one that - * is a better network than the currently associated. If none - * found, the ROAM process is over (ROAM bit cleared) - * 4. If a better network is found, a disassociation request is - * sent. - * 5. When the disassociation completes, the roam work is again - * scheduled. The second time through, the driver is no longer - * associated, and the newly selected network is sent an - * association request. - * 6. At this point ,the roaming process is complete and the ROAM - * status bit is cleared. - */ - - /* If we are no longer associated, and the roaming bit is no longer - * set, then we are not actively roaming, so just return */ - if (!(priv->status & (STATUS_ASSOCIATED | STATUS_ROAMING))) - return; - - if (priv->status & STATUS_ASSOCIATED) { - /* First pass through ROAM process -- look for a better - * network */ - unsigned long flags; - u8 rssi = priv->assoc_network->stats.rssi; - priv->assoc_network->stats.rssi = -128; - spin_lock_irqsave(&priv->ieee->lock, flags); - list_for_each_entry(network, &priv->ieee->network_list, list) { - if (network != priv->assoc_network) - ipw_best_network(priv, &match, network, 1); - } - spin_unlock_irqrestore(&priv->ieee->lock, flags); - priv->assoc_network->stats.rssi = rssi; - - if (match.network == priv->assoc_network) { - IPW_DEBUG_ASSOC("No better APs in this network to " - "roam to.\n"); - priv->status &= ~STATUS_ROAMING; - ipw_debug_config(priv); - return; - } - - ipw_send_disassociate(priv, 1); - priv->assoc_network = match.network; - - return; - } - - /* Second pass through ROAM process -- request association */ - ipw_compatible_rates(priv, priv->assoc_network, &match.rates); - ipw_associate_network(priv, priv->assoc_network, &match.rates, 1); - priv->status &= ~STATUS_ROAMING; -} - -static void ipw_bg_roam(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, roam); - mutex_lock(&priv->mutex); - ipw_roam(priv); - mutex_unlock(&priv->mutex); -} - -static int ipw_associate(void *data) -{ - struct ipw_priv *priv = data; - - struct libipw_network *network = NULL; - struct ipw_network_match match = { - .network = NULL - }; - struct ipw_supported_rates *rates; - struct list_head *element; - unsigned long flags; - - if (priv->ieee->iw_mode == IW_MODE_MONITOR) { - IPW_DEBUG_ASSOC("Not attempting association (monitor mode)\n"); - return 0; - } - - if (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { - IPW_DEBUG_ASSOC("Not attempting association (already in " - "progress)\n"); - return 0; - } - - if (priv->status & STATUS_DISASSOCIATING) { - IPW_DEBUG_ASSOC("Not attempting association (in " - "disassociating)\n "); - schedule_work(&priv->associate); - return 0; - } - - if (!ipw_is_init(priv) || (priv->status & STATUS_SCANNING)) { - IPW_DEBUG_ASSOC("Not attempting association (scanning or not " - "initialized)\n"); - return 0; - } - - if (!(priv->config & CFG_ASSOCIATE) && - !(priv->config & (CFG_STATIC_ESSID | CFG_STATIC_BSSID))) { - IPW_DEBUG_ASSOC("Not attempting association (associate=0)\n"); - return 0; - } - - /* Protect our use of the network_list */ - spin_lock_irqsave(&priv->ieee->lock, flags); - list_for_each_entry(network, &priv->ieee->network_list, list) - ipw_best_network(priv, &match, network, 0); - - network = match.network; - rates = &match.rates; - - if (network == NULL && - priv->ieee->iw_mode == IW_MODE_ADHOC && - priv->config & CFG_ADHOC_CREATE && - priv->config & CFG_STATIC_ESSID && - priv->config & CFG_STATIC_CHANNEL) { - /* Use oldest network if the free list is empty */ - if (list_empty(&priv->ieee->network_free_list)) { - struct libipw_network *oldest = NULL; - struct libipw_network *target; - - list_for_each_entry(target, &priv->ieee->network_list, list) { - if ((oldest == NULL) || - (target->last_scanned < oldest->last_scanned)) - oldest = target; - } - - /* If there are no more slots, expire the oldest */ - list_del(&oldest->list); - target = oldest; - IPW_DEBUG_ASSOC("Expired '%*pE' (%pM) from network list.\n", - target->ssid_len, target->ssid, - target->bssid); - list_add_tail(&target->list, - &priv->ieee->network_free_list); - } - - element = priv->ieee->network_free_list.next; - network = list_entry(element, struct libipw_network, list); - ipw_adhoc_create(priv, network); - rates = &priv->rates; - list_del(element); - list_add_tail(&network->list, &priv->ieee->network_list); - } - spin_unlock_irqrestore(&priv->ieee->lock, flags); - - /* If we reached the end of the list, then we don't have any valid - * matching APs */ - if (!network) { - ipw_debug_config(priv); - - if (!(priv->status & STATUS_SCANNING)) { - if (!(priv->config & CFG_SPEED_SCAN)) - schedule_delayed_work(&priv->request_scan, - SCAN_INTERVAL); - else - schedule_delayed_work(&priv->request_scan, 0); - } - - return 0; - } - - ipw_associate_network(priv, network, rates, 0); - - return 1; -} - -static void ipw_bg_associate(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, associate); - mutex_lock(&priv->mutex); - ipw_associate(priv); - mutex_unlock(&priv->mutex); -} - -static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv, - struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr; - u16 fc; - - hdr = (struct ieee80211_hdr *)skb->data; - fc = le16_to_cpu(hdr->frame_control); - if (!(fc & IEEE80211_FCTL_PROTECTED)) - return; - - fc &= ~IEEE80211_FCTL_PROTECTED; - hdr->frame_control = cpu_to_le16(fc); - switch (priv->ieee->sec.level) { - case SEC_LEVEL_3: - /* Remove CCMP HDR */ - memmove(skb->data + LIBIPW_3ADDR_LEN, - skb->data + LIBIPW_3ADDR_LEN + 8, - skb->len - LIBIPW_3ADDR_LEN - 8); - skb_trim(skb, skb->len - 16); /* CCMP_HDR_LEN + CCMP_MIC_LEN */ - break; - case SEC_LEVEL_2: - break; - case SEC_LEVEL_1: - /* Remove IV */ - memmove(skb->data + LIBIPW_3ADDR_LEN, - skb->data + LIBIPW_3ADDR_LEN + 4, - skb->len - LIBIPW_3ADDR_LEN - 4); - skb_trim(skb, skb->len - 8); /* IV + ICV */ - break; - case SEC_LEVEL_0: - break; - default: - printk(KERN_ERR "Unknown security level %d\n", - priv->ieee->sec.level); - break; - } -} - -static void ipw_handle_data_packet(struct ipw_priv *priv, - struct ipw_rx_mem_buffer *rxb, - struct libipw_rx_stats *stats) -{ - struct net_device *dev = priv->net_dev; - struct libipw_hdr_4addr *hdr; - struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; - - /* We received data from the HW, so stop the watchdog */ - dev->trans_start = jiffies; - - /* We only process data packets if the - * interface is open */ - if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) > - skb_tailroom(rxb->skb))) { - dev->stats.rx_errors++; - priv->wstats.discard.misc++; - IPW_DEBUG_DROP("Corruption detected! Oh no!\n"); - return; - } else if (unlikely(!netif_running(priv->net_dev))) { - dev->stats.rx_dropped++; - priv->wstats.discard.misc++; - IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); - return; - } - - /* Advance skb->data to the start of the actual payload */ - skb_reserve(rxb->skb, offsetof(struct ipw_rx_packet, u.frame.data)); - - /* Set the size of the skb to the size of the frame */ - skb_put(rxb->skb, le16_to_cpu(pkt->u.frame.length)); - - IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len); - - /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */ - hdr = (struct libipw_hdr_4addr *)rxb->skb->data; - if (priv->ieee->iw_mode != IW_MODE_MONITOR && - (is_multicast_ether_addr(hdr->addr1) ? - !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt)) - ipw_rebuild_decrypted_skb(priv, rxb->skb); - - if (!libipw_rx(priv->ieee, rxb->skb, stats)) - dev->stats.rx_errors++; - else { /* libipw_rx succeeded, so it now owns the SKB */ - rxb->skb = NULL; - __ipw_led_activity_on(priv); - } -} - -#ifdef CONFIG_IPW2200_RADIOTAP -static void ipw_handle_data_packet_monitor(struct ipw_priv *priv, - struct ipw_rx_mem_buffer *rxb, - struct libipw_rx_stats *stats) -{ - struct net_device *dev = priv->net_dev; - struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; - struct ipw_rx_frame *frame = &pkt->u.frame; - - /* initial pull of some data */ - u16 received_channel = frame->received_channel; - u8 antennaAndPhy = frame->antennaAndPhy; - s8 antsignal = frame->rssi_dbm - IPW_RSSI_TO_DBM; /* call it signed anyhow */ - u16 pktrate = frame->rate; - - /* Magic struct that slots into the radiotap header -- no reason - * to build this manually element by element, we can write it much - * more efficiently than we can parse it. ORDER MATTERS HERE */ - struct ipw_rt_hdr *ipw_rt; - - unsigned short len = le16_to_cpu(pkt->u.frame.length); - - /* We received data from the HW, so stop the watchdog */ - dev->trans_start = jiffies; - - /* We only process data packets if the - * interface is open */ - if (unlikely((le16_to_cpu(pkt->u.frame.length) + IPW_RX_FRAME_SIZE) > - skb_tailroom(rxb->skb))) { - dev->stats.rx_errors++; - priv->wstats.discard.misc++; - IPW_DEBUG_DROP("Corruption detected! Oh no!\n"); - return; - } else if (unlikely(!netif_running(priv->net_dev))) { - dev->stats.rx_dropped++; - priv->wstats.discard.misc++; - IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); - return; - } - - /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use - * that now */ - if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) { - /* FIXME: Should alloc bigger skb instead */ - dev->stats.rx_dropped++; - priv->wstats.discard.misc++; - IPW_DEBUG_DROP("Dropping too large packet in monitor\n"); - return; - } - - /* copy the frame itself */ - memmove(rxb->skb->data + sizeof(struct ipw_rt_hdr), - rxb->skb->data + IPW_RX_FRAME_SIZE, len); - - ipw_rt = (struct ipw_rt_hdr *)rxb->skb->data; - - ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; - ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */ - ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct ipw_rt_hdr)); /* total header+data */ - - /* Big bitfield of all the fields we provide in radiotap */ - ipw_rt->rt_hdr.it_present = cpu_to_le32( - (1 << IEEE80211_RADIOTAP_TSFT) | - (1 << IEEE80211_RADIOTAP_FLAGS) | - (1 << IEEE80211_RADIOTAP_RATE) | - (1 << IEEE80211_RADIOTAP_CHANNEL) | - (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | - (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | - (1 << IEEE80211_RADIOTAP_ANTENNA)); - - /* Zero the flags, we'll add to them as we go */ - ipw_rt->rt_flags = 0; - ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 | - frame->parent_tsf[2] << 16 | - frame->parent_tsf[1] << 8 | - frame->parent_tsf[0]); - - /* Convert signal to DBM */ - ipw_rt->rt_dbmsignal = antsignal; - ipw_rt->rt_dbmnoise = (s8) le16_to_cpu(frame->noise); - - /* Convert the channel data and set the flags */ - ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel)); - if (received_channel > 14) { /* 802.11a */ - ipw_rt->rt_chbitmask = - cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ)); - } else if (antennaAndPhy & 32) { /* 802.11b */ - ipw_rt->rt_chbitmask = - cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ)); - } else { /* 802.11g */ - ipw_rt->rt_chbitmask = - cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ); - } - - /* set the rate in multiples of 500k/s */ - switch (pktrate) { - case IPW_TX_RATE_1MB: - ipw_rt->rt_rate = 2; - break; - case IPW_TX_RATE_2MB: - ipw_rt->rt_rate = 4; - break; - case IPW_TX_RATE_5MB: - ipw_rt->rt_rate = 10; - break; - case IPW_TX_RATE_6MB: - ipw_rt->rt_rate = 12; - break; - case IPW_TX_RATE_9MB: - ipw_rt->rt_rate = 18; - break; - case IPW_TX_RATE_11MB: - ipw_rt->rt_rate = 22; - break; - case IPW_TX_RATE_12MB: - ipw_rt->rt_rate = 24; - break; - case IPW_TX_RATE_18MB: - ipw_rt->rt_rate = 36; - break; - case IPW_TX_RATE_24MB: - ipw_rt->rt_rate = 48; - break; - case IPW_TX_RATE_36MB: - ipw_rt->rt_rate = 72; - break; - case IPW_TX_RATE_48MB: - ipw_rt->rt_rate = 96; - break; - case IPW_TX_RATE_54MB: - ipw_rt->rt_rate = 108; - break; - default: - ipw_rt->rt_rate = 0; - break; - } - - /* antenna number */ - ipw_rt->rt_antenna = (antennaAndPhy & 3); /* Is this right? */ - - /* set the preamble flag if we have it */ - if ((antennaAndPhy & 64)) - ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; - - /* Set the size of the skb to the size of the frame */ - skb_put(rxb->skb, len + sizeof(struct ipw_rt_hdr)); - - IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len); - - if (!libipw_rx(priv->ieee, rxb->skb, stats)) - dev->stats.rx_errors++; - else { /* libipw_rx succeeded, so it now owns the SKB */ - rxb->skb = NULL; - /* no LED during capture */ - } -} -#endif - -#ifdef CONFIG_IPW2200_PROMISCUOUS -#define libipw_is_probe_response(fc) \ - ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && \ - (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP ) - -#define libipw_is_management(fc) \ - ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) - -#define libipw_is_control(fc) \ - ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) - -#define libipw_is_data(fc) \ - ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) - -#define libipw_is_assoc_request(fc) \ - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ) - -#define libipw_is_reassoc_request(fc) \ - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ) - -static void ipw_handle_promiscuous_rx(struct ipw_priv *priv, - struct ipw_rx_mem_buffer *rxb, - struct libipw_rx_stats *stats) -{ - struct net_device *dev = priv->prom_net_dev; - struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; - struct ipw_rx_frame *frame = &pkt->u.frame; - struct ipw_rt_hdr *ipw_rt; - - /* First cache any information we need before we overwrite - * the information provided in the skb from the hardware */ - struct ieee80211_hdr *hdr; - u16 channel = frame->received_channel; - u8 phy_flags = frame->antennaAndPhy; - s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM; - s8 noise = (s8) le16_to_cpu(frame->noise); - u8 rate = frame->rate; - unsigned short len = le16_to_cpu(pkt->u.frame.length); - struct sk_buff *skb; - int hdr_only = 0; - u16 filter = priv->prom_priv->filter; - - /* If the filter is set to not include Rx frames then return */ - if (filter & IPW_PROM_NO_RX) - return; - - /* We received data from the HW, so stop the watchdog */ - dev->trans_start = jiffies; - - if (unlikely((len + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) { - dev->stats.rx_errors++; - IPW_DEBUG_DROP("Corruption detected! Oh no!\n"); - return; - } - - /* We only process data packets if the interface is open */ - if (unlikely(!netif_running(dev))) { - dev->stats.rx_dropped++; - IPW_DEBUG_DROP("Dropping packet while interface is not up.\n"); - return; - } - - /* Libpcap 0.9.3+ can handle variable length radiotap, so we'll use - * that now */ - if (len > IPW_RX_BUF_SIZE - sizeof(struct ipw_rt_hdr)) { - /* FIXME: Should alloc bigger skb instead */ - dev->stats.rx_dropped++; - IPW_DEBUG_DROP("Dropping too large packet in monitor\n"); - return; - } - - hdr = (void *)rxb->skb->data + IPW_RX_FRAME_SIZE; - if (libipw_is_management(le16_to_cpu(hdr->frame_control))) { - if (filter & IPW_PROM_NO_MGMT) - return; - if (filter & IPW_PROM_MGMT_HEADER_ONLY) - hdr_only = 1; - } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) { - if (filter & IPW_PROM_NO_CTL) - return; - if (filter & IPW_PROM_CTL_HEADER_ONLY) - hdr_only = 1; - } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) { - if (filter & IPW_PROM_NO_DATA) - return; - if (filter & IPW_PROM_DATA_HEADER_ONLY) - hdr_only = 1; - } - - /* Copy the SKB since this is for the promiscuous side */ - skb = skb_copy(rxb->skb, GFP_ATOMIC); - if (skb == NULL) { - IPW_ERROR("skb_clone failed for promiscuous copy.\n"); - return; - } - - /* copy the frame data to write after where the radiotap header goes */ - ipw_rt = (void *)skb->data; - - if (hdr_only) - len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control)); - - memcpy(ipw_rt->payload, hdr, len); - - ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; - ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */ - ipw_rt->rt_hdr.it_len = cpu_to_le16(sizeof(*ipw_rt)); /* total header+data */ - - /* Set the size of the skb to the size of the frame */ - skb_put(skb, sizeof(*ipw_rt) + len); - - /* Big bitfield of all the fields we provide in radiotap */ - ipw_rt->rt_hdr.it_present = cpu_to_le32( - (1 << IEEE80211_RADIOTAP_TSFT) | - (1 << IEEE80211_RADIOTAP_FLAGS) | - (1 << IEEE80211_RADIOTAP_RATE) | - (1 << IEEE80211_RADIOTAP_CHANNEL) | - (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | - (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | - (1 << IEEE80211_RADIOTAP_ANTENNA)); - - /* Zero the flags, we'll add to them as we go */ - ipw_rt->rt_flags = 0; - ipw_rt->rt_tsf = (u64)(frame->parent_tsf[3] << 24 | - frame->parent_tsf[2] << 16 | - frame->parent_tsf[1] << 8 | - frame->parent_tsf[0]); - - /* Convert to DBM */ - ipw_rt->rt_dbmsignal = signal; - ipw_rt->rt_dbmnoise = noise; - - /* Convert the channel data and set the flags */ - ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(channel)); - if (channel > 14) { /* 802.11a */ - ipw_rt->rt_chbitmask = - cpu_to_le16((IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ)); - } else if (phy_flags & (1 << 5)) { /* 802.11b */ - ipw_rt->rt_chbitmask = - cpu_to_le16((IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ)); - } else { /* 802.11g */ - ipw_rt->rt_chbitmask = - cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ); - } - - /* set the rate in multiples of 500k/s */ - switch (rate) { - case IPW_TX_RATE_1MB: - ipw_rt->rt_rate = 2; - break; - case IPW_TX_RATE_2MB: - ipw_rt->rt_rate = 4; - break; - case IPW_TX_RATE_5MB: - ipw_rt->rt_rate = 10; - break; - case IPW_TX_RATE_6MB: - ipw_rt->rt_rate = 12; - break; - case IPW_TX_RATE_9MB: - ipw_rt->rt_rate = 18; - break; - case IPW_TX_RATE_11MB: - ipw_rt->rt_rate = 22; - break; - case IPW_TX_RATE_12MB: - ipw_rt->rt_rate = 24; - break; - case IPW_TX_RATE_18MB: - ipw_rt->rt_rate = 36; - break; - case IPW_TX_RATE_24MB: - ipw_rt->rt_rate = 48; - break; - case IPW_TX_RATE_36MB: - ipw_rt->rt_rate = 72; - break; - case IPW_TX_RATE_48MB: - ipw_rt->rt_rate = 96; - break; - case IPW_TX_RATE_54MB: - ipw_rt->rt_rate = 108; - break; - default: - ipw_rt->rt_rate = 0; - break; - } - - /* antenna number */ - ipw_rt->rt_antenna = (phy_flags & 3); - - /* set the preamble flag if we have it */ - if (phy_flags & (1 << 6)) - ipw_rt->rt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; - - IPW_DEBUG_RX("Rx packet of %d bytes.\n", skb->len); - - if (!libipw_rx(priv->prom_priv->ieee, skb, stats)) { - dev->stats.rx_errors++; - dev_kfree_skb_any(skb); - } -} -#endif - -static int is_network_packet(struct ipw_priv *priv, - struct libipw_hdr_4addr *header) -{ - /* Filter incoming packets to determine if they are targeted toward - * this network, discarding packets coming from ourselves */ - switch (priv->ieee->iw_mode) { - case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */ - /* packets from our adapter are dropped (echo) */ - if (ether_addr_equal(header->addr2, priv->net_dev->dev_addr)) - return 0; - - /* {broad,multi}cast packets to our BSSID go through */ - if (is_multicast_ether_addr(header->addr1)) - return ether_addr_equal(header->addr3, priv->bssid); - - /* packets to our adapter go through */ - return ether_addr_equal(header->addr1, - priv->net_dev->dev_addr); - - case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */ - /* packets from our adapter are dropped (echo) */ - if (ether_addr_equal(header->addr3, priv->net_dev->dev_addr)) - return 0; - - /* {broad,multi}cast packets to our BSS go through */ - if (is_multicast_ether_addr(header->addr1)) - return ether_addr_equal(header->addr2, priv->bssid); - - /* packets to our adapter go through */ - return ether_addr_equal(header->addr1, - priv->net_dev->dev_addr); - } - - return 1; -} - -#define IPW_PACKET_RETRY_TIME HZ - -static int is_duplicate_packet(struct ipw_priv *priv, - struct libipw_hdr_4addr *header) -{ - u16 sc = le16_to_cpu(header->seq_ctl); - u16 seq = WLAN_GET_SEQ_SEQ(sc); - u16 frag = WLAN_GET_SEQ_FRAG(sc); - u16 *last_seq, *last_frag; - unsigned long *last_time; - - switch (priv->ieee->iw_mode) { - case IW_MODE_ADHOC: - { - struct list_head *p; - struct ipw_ibss_seq *entry = NULL; - u8 *mac = header->addr2; - int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE; - - list_for_each(p, &priv->ibss_mac_hash[index]) { - entry = - list_entry(p, struct ipw_ibss_seq, list); - if (ether_addr_equal(entry->mac, mac)) - break; - } - if (p == &priv->ibss_mac_hash[index]) { - entry = kmalloc(sizeof(*entry), GFP_ATOMIC); - if (!entry) { - IPW_ERROR - ("Cannot malloc new mac entry\n"); - return 0; - } - memcpy(entry->mac, mac, ETH_ALEN); - entry->seq_num = seq; - entry->frag_num = frag; - entry->packet_time = jiffies; - list_add(&entry->list, - &priv->ibss_mac_hash[index]); - return 0; - } - last_seq = &entry->seq_num; - last_frag = &entry->frag_num; - last_time = &entry->packet_time; - break; - } - case IW_MODE_INFRA: - last_seq = &priv->last_seq_num; - last_frag = &priv->last_frag_num; - last_time = &priv->last_packet_time; - break; - default: - return 0; - } - if ((*last_seq == seq) && - time_after(*last_time + IPW_PACKET_RETRY_TIME, jiffies)) { - if (*last_frag == frag) - goto drop; - if (*last_frag + 1 != frag) - /* out-of-order fragment */ - goto drop; - } else - *last_seq = seq; - - *last_frag = frag; - *last_time = jiffies; - return 0; - - drop: - /* Comment this line now since we observed the card receives - * duplicate packets but the FCTL_RETRY bit is not set in the - * IBSS mode with fragmentation enabled. - BUG_ON(!(le16_to_cpu(header->frame_control) & IEEE80211_FCTL_RETRY)); */ - return 1; -} - -static void ipw_handle_mgmt_packet(struct ipw_priv *priv, - struct ipw_rx_mem_buffer *rxb, - struct libipw_rx_stats *stats) -{ - struct sk_buff *skb = rxb->skb; - struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data; - struct libipw_hdr_4addr *header = (struct libipw_hdr_4addr *) - (skb->data + IPW_RX_FRAME_SIZE); - - libipw_rx_mgt(priv->ieee, header, stats); - - if (priv->ieee->iw_mode == IW_MODE_ADHOC && - ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) == - IEEE80211_STYPE_PROBE_RESP) || - (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) == - IEEE80211_STYPE_BEACON))) { - if (ether_addr_equal(header->addr3, priv->bssid)) - ipw_add_station(priv, header->addr2); - } - - if (priv->config & CFG_NET_STATS) { - IPW_DEBUG_HC("sending stat packet\n"); - - /* Set the size of the skb to the size of the full - * ipw header and 802.11 frame */ - skb_put(skb, le16_to_cpu(pkt->u.frame.length) + - IPW_RX_FRAME_SIZE); - - /* Advance past the ipw packet header to the 802.11 frame */ - skb_pull(skb, IPW_RX_FRAME_SIZE); - - /* Push the libipw_rx_stats before the 802.11 frame */ - memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats)); - - skb->dev = priv->ieee->dev; - - /* Point raw at the libipw_stats */ - skb_reset_mac_header(skb); - - skb->pkt_type = PACKET_OTHERHOST; - skb->protocol = cpu_to_be16(ETH_P_80211_STATS); - memset(skb->cb, 0, sizeof(rxb->skb->cb)); - netif_rx(skb); - rxb->skb = NULL; - } -} - -/* - * Main entry function for receiving a packet with 80211 headers. This - * should be called when ever the FW has notified us that there is a new - * skb in the receive queue. - */ -static void ipw_rx(struct ipw_priv *priv) -{ - struct ipw_rx_mem_buffer *rxb; - struct ipw_rx_packet *pkt; - struct libipw_hdr_4addr *header; - u32 r, w, i; - u8 network_packet; - u8 fill_rx = 0; - - r = ipw_read32(priv, IPW_RX_READ_INDEX); - w = ipw_read32(priv, IPW_RX_WRITE_INDEX); - i = priv->rxq->read; - - if (ipw_rx_queue_space (priv->rxq) > (RX_QUEUE_SIZE / 2)) - fill_rx = 1; - - while (i != r) { - rxb = priv->rxq->queue[i]; - if (unlikely(rxb == NULL)) { - printk(KERN_CRIT "Queue not allocated!\n"); - break; - } - priv->rxq->queue[i] = NULL; - - pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr, - IPW_RX_BUF_SIZE, - PCI_DMA_FROMDEVICE); - - pkt = (struct ipw_rx_packet *)rxb->skb->data; - IPW_DEBUG_RX("Packet: type=%02X seq=%02X bits=%02X\n", - pkt->header.message_type, - pkt->header.rx_seq_num, pkt->header.control_bits); - - switch (pkt->header.message_type) { - case RX_FRAME_TYPE: /* 802.11 frame */ { - struct libipw_rx_stats stats = { - .rssi = pkt->u.frame.rssi_dbm - - IPW_RSSI_TO_DBM, - .signal = - pkt->u.frame.rssi_dbm - - IPW_RSSI_TO_DBM + 0x100, - .noise = - le16_to_cpu(pkt->u.frame.noise), - .rate = pkt->u.frame.rate, - .mac_time = jiffies, - .received_channel = - pkt->u.frame.received_channel, - .freq = - (pkt->u.frame. - control & (1 << 0)) ? - LIBIPW_24GHZ_BAND : - LIBIPW_52GHZ_BAND, - .len = le16_to_cpu(pkt->u.frame.length), - }; - - if (stats.rssi != 0) - stats.mask |= LIBIPW_STATMASK_RSSI; - if (stats.signal != 0) - stats.mask |= LIBIPW_STATMASK_SIGNAL; - if (stats.noise != 0) - stats.mask |= LIBIPW_STATMASK_NOISE; - if (stats.rate != 0) - stats.mask |= LIBIPW_STATMASK_RATE; - - priv->rx_packets++; - -#ifdef CONFIG_IPW2200_PROMISCUOUS - if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) - ipw_handle_promiscuous_rx(priv, rxb, &stats); -#endif - -#ifdef CONFIG_IPW2200_MONITOR - if (priv->ieee->iw_mode == IW_MODE_MONITOR) { -#ifdef CONFIG_IPW2200_RADIOTAP - - ipw_handle_data_packet_monitor(priv, - rxb, - &stats); -#else - ipw_handle_data_packet(priv, rxb, - &stats); -#endif - break; - } -#endif - - header = - (struct libipw_hdr_4addr *)(rxb->skb-> - data + - IPW_RX_FRAME_SIZE); - /* TODO: Check Ad-Hoc dest/source and make sure - * that we are actually parsing these packets - * correctly -- we should probably use the - * frame control of the packet and disregard - * the current iw_mode */ - - network_packet = - is_network_packet(priv, header); - if (network_packet && priv->assoc_network) { - priv->assoc_network->stats.rssi = - stats.rssi; - priv->exp_avg_rssi = - exponential_average(priv->exp_avg_rssi, - stats.rssi, DEPTH_RSSI); - } - - IPW_DEBUG_RX("Frame: len=%u\n", - le16_to_cpu(pkt->u.frame.length)); - - if (le16_to_cpu(pkt->u.frame.length) < - libipw_get_hdrlen(le16_to_cpu( - header->frame_ctl))) { - IPW_DEBUG_DROP - ("Received packet is too small. " - "Dropping.\n"); - priv->net_dev->stats.rx_errors++; - priv->wstats.discard.misc++; - break; - } - - switch (WLAN_FC_GET_TYPE - (le16_to_cpu(header->frame_ctl))) { - - case IEEE80211_FTYPE_MGMT: - ipw_handle_mgmt_packet(priv, rxb, - &stats); - break; - - case IEEE80211_FTYPE_CTL: - break; - - case IEEE80211_FTYPE_DATA: - if (unlikely(!network_packet || - is_duplicate_packet(priv, - header))) - { - IPW_DEBUG_DROP("Dropping: " - "%pM, " - "%pM, " - "%pM\n", - header->addr1, - header->addr2, - header->addr3); - break; - } - - ipw_handle_data_packet(priv, rxb, - &stats); - - break; - } - break; - } - - case RX_HOST_NOTIFICATION_TYPE:{ - IPW_DEBUG_RX - ("Notification: subtype=%02X flags=%02X size=%d\n", - pkt->u.notification.subtype, - pkt->u.notification.flags, - le16_to_cpu(pkt->u.notification.size)); - ipw_rx_notification(priv, &pkt->u.notification); - break; - } - - default: - IPW_DEBUG_RX("Bad Rx packet of type %d\n", - pkt->header.message_type); - break; - } - - /* For now we just don't re-use anything. We can tweak this - * later to try and re-use notification packets and SKBs that - * fail to Rx correctly */ - if (rxb->skb != NULL) { - dev_kfree_skb_any(rxb->skb); - rxb->skb = NULL; - } - - pci_unmap_single(priv->pci_dev, rxb->dma_addr, - IPW_RX_BUF_SIZE, PCI_DMA_FROMDEVICE); - list_add_tail(&rxb->list, &priv->rxq->rx_used); - - i = (i + 1) % RX_QUEUE_SIZE; - - /* If there are a lot of unsued frames, restock the Rx queue - * so the ucode won't assert */ - if (fill_rx) { - priv->rxq->read = i; - ipw_rx_queue_replenish(priv); - } - } - - /* Backtrack one entry */ - priv->rxq->read = i; - ipw_rx_queue_restock(priv); -} - -#define DEFAULT_RTS_THRESHOLD 2304U -#define MIN_RTS_THRESHOLD 1U -#define MAX_RTS_THRESHOLD 2304U -#define DEFAULT_BEACON_INTERVAL 100U -#define DEFAULT_SHORT_RETRY_LIMIT 7U -#define DEFAULT_LONG_RETRY_LIMIT 4U - -/** - * ipw_sw_reset - * @option: options to control different reset behaviour - * 0 = reset everything except the 'disable' module_param - * 1 = reset everything and print out driver info (for probe only) - * 2 = reset everything - */ -static int ipw_sw_reset(struct ipw_priv *priv, int option) -{ - int band, modulation; - int old_mode = priv->ieee->iw_mode; - - /* Initialize module parameter values here */ - priv->config = 0; - - /* We default to disabling the LED code as right now it causes - * too many systems to lock up... */ - if (!led_support) - priv->config |= CFG_NO_LED; - - if (associate) - priv->config |= CFG_ASSOCIATE; - else - IPW_DEBUG_INFO("Auto associate disabled.\n"); - - if (auto_create) - priv->config |= CFG_ADHOC_CREATE; - else - IPW_DEBUG_INFO("Auto adhoc creation disabled.\n"); - - priv->config &= ~CFG_STATIC_ESSID; - priv->essid_len = 0; - memset(priv->essid, 0, IW_ESSID_MAX_SIZE); - - if (disable && option) { - priv->status |= STATUS_RF_KILL_SW; - IPW_DEBUG_INFO("Radio disabled.\n"); - } - - if (default_channel != 0) { - priv->config |= CFG_STATIC_CHANNEL; - priv->channel = default_channel; - IPW_DEBUG_INFO("Bind to static channel %d\n", default_channel); - /* TODO: Validate that provided channel is in range */ - } -#ifdef CONFIG_IPW2200_QOS - ipw_qos_init(priv, qos_enable, qos_burst_enable, - burst_duration_CCK, burst_duration_OFDM); -#endif /* CONFIG_IPW2200_QOS */ - - switch (network_mode) { - case 1: - priv->ieee->iw_mode = IW_MODE_ADHOC; - priv->net_dev->type = ARPHRD_ETHER; - - break; -#ifdef CONFIG_IPW2200_MONITOR - case 2: - priv->ieee->iw_mode = IW_MODE_MONITOR; -#ifdef CONFIG_IPW2200_RADIOTAP - priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; -#else - priv->net_dev->type = ARPHRD_IEEE80211; -#endif - break; -#endif - default: - case 0: - priv->net_dev->type = ARPHRD_ETHER; - priv->ieee->iw_mode = IW_MODE_INFRA; - break; - } - - if (hwcrypto) { - priv->ieee->host_encrypt = 0; - priv->ieee->host_encrypt_msdu = 0; - priv->ieee->host_decrypt = 0; - priv->ieee->host_mc_decrypt = 0; - } - IPW_DEBUG_INFO("Hardware crypto [%s]\n", hwcrypto ? "on" : "off"); - - /* IPW2200/2915 is abled to do hardware fragmentation. */ - priv->ieee->host_open_frag = 0; - - if ((priv->pci_dev->device == 0x4223) || - (priv->pci_dev->device == 0x4224)) { - if (option == 1) - printk(KERN_INFO DRV_NAME - ": Detected Intel PRO/Wireless 2915ABG Network " - "Connection\n"); - priv->ieee->abg_true = 1; - band = LIBIPW_52GHZ_BAND | LIBIPW_24GHZ_BAND; - modulation = LIBIPW_OFDM_MODULATION | - LIBIPW_CCK_MODULATION; - priv->adapter = IPW_2915ABG; - priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B; - } else { - if (option == 1) - printk(KERN_INFO DRV_NAME - ": Detected Intel PRO/Wireless 2200BG Network " - "Connection\n"); - - priv->ieee->abg_true = 0; - band = LIBIPW_24GHZ_BAND; - modulation = LIBIPW_OFDM_MODULATION | - LIBIPW_CCK_MODULATION; - priv->adapter = IPW_2200BG; - priv->ieee->mode = IEEE_G | IEEE_B; - } - - priv->ieee->freq_band = band; - priv->ieee->modulation = modulation; - - priv->rates_mask = LIBIPW_DEFAULT_RATES_MASK; - - priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT; - priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT; - - priv->rts_threshold = DEFAULT_RTS_THRESHOLD; - priv->short_retry_limit = DEFAULT_SHORT_RETRY_LIMIT; - priv->long_retry_limit = DEFAULT_LONG_RETRY_LIMIT; - - /* If power management is turned on, default to AC mode */ - priv->power_mode = IPW_POWER_AC; - priv->tx_power = IPW_TX_POWER_DEFAULT; - - return old_mode == priv->ieee->iw_mode; -} - -/* - * This file defines the Wireless Extension handlers. It does not - * define any methods of hardware manipulation and relies on the - * functions defined in ipw_main to provide the HW interaction. - * - * The exception to this is the use of the ipw_get_ordinal() - * function used to poll the hardware vs. making unnecessary calls. - * - */ - -static int ipw_set_channel(struct ipw_priv *priv, u8 channel) -{ - if (channel == 0) { - IPW_DEBUG_INFO("Setting channel to ANY (0)\n"); - priv->config &= ~CFG_STATIC_CHANNEL; - IPW_DEBUG_ASSOC("Attempting to associate with new " - "parameters.\n"); - ipw_associate(priv); - return 0; - } - - priv->config |= CFG_STATIC_CHANNEL; - - if (priv->channel == channel) { - IPW_DEBUG_INFO("Request to set channel to current value (%d)\n", - channel); - return 0; - } - - IPW_DEBUG_INFO("Setting channel to %i\n", (int)channel); - priv->channel = channel; - -#ifdef CONFIG_IPW2200_MONITOR - if (priv->ieee->iw_mode == IW_MODE_MONITOR) { - int i; - if (priv->status & STATUS_SCANNING) { - IPW_DEBUG_SCAN("Scan abort triggered due to " - "channel change.\n"); - ipw_abort_scan(priv); - } - - for (i = 1000; i && (priv->status & STATUS_SCANNING); i--) - udelay(10); - - if (priv->status & STATUS_SCANNING) - IPW_DEBUG_SCAN("Still scanning...\n"); - else - IPW_DEBUG_SCAN("Took %dms to abort current scan\n", - 1000 - i); - - return 0; - } -#endif /* CONFIG_IPW2200_MONITOR */ - - /* Network configuration changed -- force [re]association */ - IPW_DEBUG_ASSOC("[re]association triggered due to channel change.\n"); - if (!ipw_disassociate(priv)) - ipw_associate(priv); - - return 0; -} - -static int ipw_wx_set_freq(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - const struct libipw_geo *geo = libipw_get_geo(priv->ieee); - struct iw_freq *fwrq = &wrqu->freq; - int ret = 0, i; - u8 channel, flags; - int band; - - if (fwrq->m == 0) { - IPW_DEBUG_WX("SET Freq/Channel -> any\n"); - mutex_lock(&priv->mutex); - ret = ipw_set_channel(priv, 0); - mutex_unlock(&priv->mutex); - return ret; - } - /* if setting by freq convert to channel */ - if (fwrq->e == 1) { - channel = libipw_freq_to_channel(priv->ieee, fwrq->m); - if (channel == 0) - return -EINVAL; - } else - channel = fwrq->m; - - if (!(band = libipw_is_valid_channel(priv->ieee, channel))) - return -EINVAL; - - if (priv->ieee->iw_mode == IW_MODE_ADHOC) { - i = libipw_channel_to_index(priv->ieee, channel); - if (i == -1) - return -EINVAL; - - flags = (band == LIBIPW_24GHZ_BAND) ? - geo->bg[i].flags : geo->a[i].flags; - if (flags & LIBIPW_CH_PASSIVE_ONLY) { - IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n"); - return -EINVAL; - } - } - - IPW_DEBUG_WX("SET Freq/Channel -> %d\n", fwrq->m); - mutex_lock(&priv->mutex); - ret = ipw_set_channel(priv, channel); - mutex_unlock(&priv->mutex); - return ret; -} - -static int ipw_wx_get_freq(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - - wrqu->freq.e = 0; - - /* If we are associated, trying to associate, or have a statically - * configured CHANNEL then return that; otherwise return ANY */ - mutex_lock(&priv->mutex); - if (priv->config & CFG_STATIC_CHANNEL || - priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) { - int i; - - i = libipw_channel_to_index(priv->ieee, priv->channel); - BUG_ON(i == -1); - wrqu->freq.e = 1; - - switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { - case LIBIPW_52GHZ_BAND: - wrqu->freq.m = priv->ieee->geo.a[i].freq * 100000; - break; - - case LIBIPW_24GHZ_BAND: - wrqu->freq.m = priv->ieee->geo.bg[i].freq * 100000; - break; - - default: - BUG(); - } - } else - wrqu->freq.m = 0; - - mutex_unlock(&priv->mutex); - IPW_DEBUG_WX("GET Freq/Channel -> %d\n", priv->channel); - return 0; -} - -static int ipw_wx_set_mode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - int err = 0; - - IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode); - - switch (wrqu->mode) { -#ifdef CONFIG_IPW2200_MONITOR - case IW_MODE_MONITOR: -#endif - case IW_MODE_ADHOC: - case IW_MODE_INFRA: - break; - case IW_MODE_AUTO: - wrqu->mode = IW_MODE_INFRA; - break; - default: - return -EINVAL; - } - if (wrqu->mode == priv->ieee->iw_mode) - return 0; - - mutex_lock(&priv->mutex); - - ipw_sw_reset(priv, 0); - -#ifdef CONFIG_IPW2200_MONITOR - if (priv->ieee->iw_mode == IW_MODE_MONITOR) - priv->net_dev->type = ARPHRD_ETHER; - - if (wrqu->mode == IW_MODE_MONITOR) -#ifdef CONFIG_IPW2200_RADIOTAP - priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; -#else - priv->net_dev->type = ARPHRD_IEEE80211; -#endif -#endif /* CONFIG_IPW2200_MONITOR */ - - /* Free the existing firmware and reset the fw_loaded - * flag so ipw_load() will bring in the new firmware */ - free_firmware(); - - priv->ieee->iw_mode = wrqu->mode; - - schedule_work(&priv->adapter_restart); - mutex_unlock(&priv->mutex); - return err; -} - -static int ipw_wx_get_mode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - wrqu->mode = priv->ieee->iw_mode; - IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode); - mutex_unlock(&priv->mutex); - return 0; -} - -/* Values are in microsecond */ -static const s32 timeout_duration[] = { - 350000, - 250000, - 75000, - 37000, - 25000, -}; - -static const s32 period_duration[] = { - 400000, - 700000, - 1000000, - 1000000, - 1000000 -}; - -static int ipw_wx_get_range(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - struct iw_range *range = (struct iw_range *)extra; - const struct libipw_geo *geo = libipw_get_geo(priv->ieee); - int i = 0, j; - - wrqu->data.length = sizeof(*range); - memset(range, 0, sizeof(*range)); - - /* 54Mbs == ~27 Mb/s real (802.11g) */ - range->throughput = 27 * 1000 * 1000; - - range->max_qual.qual = 100; - /* TODO: Find real max RSSI and stick here */ - range->max_qual.level = 0; - range->max_qual.noise = 0; - range->max_qual.updated = 7; /* Updated all three */ - - range->avg_qual.qual = 70; - /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ - range->avg_qual.level = 0; /* FIXME to real average level */ - range->avg_qual.noise = 0; - range->avg_qual.updated = 7; /* Updated all three */ - mutex_lock(&priv->mutex); - range->num_bitrates = min(priv->rates.num_rates, (u8) IW_MAX_BITRATES); - - for (i = 0; i < range->num_bitrates; i++) - range->bitrate[i] = (priv->rates.supported_rates[i] & 0x7F) * - 500000; - - range->max_rts = DEFAULT_RTS_THRESHOLD; - range->min_frag = MIN_FRAG_THRESHOLD; - range->max_frag = MAX_FRAG_THRESHOLD; - - range->encoding_size[0] = 5; - range->encoding_size[1] = 13; - range->num_encoding_sizes = 2; - range->max_encoding_tokens = WEP_KEYS; - - /* Set the Wireless Extension versions */ - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 18; - - i = 0; - if (priv->ieee->mode & (IEEE_B | IEEE_G)) { - for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES; j++) { - if ((priv->ieee->iw_mode == IW_MODE_ADHOC) && - (geo->bg[j].flags & LIBIPW_CH_PASSIVE_ONLY)) - continue; - - range->freq[i].i = geo->bg[j].channel; - range->freq[i].m = geo->bg[j].freq * 100000; - range->freq[i].e = 1; - i++; - } - } - - if (priv->ieee->mode & IEEE_A) { - for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES; j++) { - if ((priv->ieee->iw_mode == IW_MODE_ADHOC) && - (geo->a[j].flags & LIBIPW_CH_PASSIVE_ONLY)) - continue; - - range->freq[i].i = geo->a[j].channel; - range->freq[i].m = geo->a[j].freq * 100000; - range->freq[i].e = 1; - i++; - } - } - - range->num_channels = i; - range->num_frequency = i; - - mutex_unlock(&priv->mutex); - - /* Event capability (kernel + driver) */ - range->event_capa[0] = (IW_EVENT_CAPA_K_0 | - IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | - IW_EVENT_CAPA_MASK(SIOCGIWAP) | - IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); - range->event_capa[1] = IW_EVENT_CAPA_K_1; - - range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | - IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; - - range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE; - - IPW_DEBUG_WX("GET Range\n"); - return 0; -} - -static int ipw_wx_set_wap(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - - if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) - return -EINVAL; - mutex_lock(&priv->mutex); - if (is_broadcast_ether_addr(wrqu->ap_addr.sa_data) || - is_zero_ether_addr(wrqu->ap_addr.sa_data)) { - /* we disable mandatory BSSID association */ - IPW_DEBUG_WX("Setting AP BSSID to ANY\n"); - priv->config &= ~CFG_STATIC_BSSID; - IPW_DEBUG_ASSOC("Attempting to associate with new " - "parameters.\n"); - ipw_associate(priv); - mutex_unlock(&priv->mutex); - return 0; - } - - priv->config |= CFG_STATIC_BSSID; - if (ether_addr_equal(priv->bssid, wrqu->ap_addr.sa_data)) { - IPW_DEBUG_WX("BSSID set to current BSSID.\n"); - mutex_unlock(&priv->mutex); - return 0; - } - - IPW_DEBUG_WX("Setting mandatory BSSID to %pM\n", - wrqu->ap_addr.sa_data); - - memcpy(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN); - - /* Network configuration changed -- force [re]association */ - IPW_DEBUG_ASSOC("[re]association triggered due to BSSID change.\n"); - if (!ipw_disassociate(priv)) - ipw_associate(priv); - - mutex_unlock(&priv->mutex); - return 0; -} - -static int ipw_wx_get_wap(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - - /* If we are associated, trying to associate, or have a statically - * configured BSSID then return that; otherwise return ANY */ - mutex_lock(&priv->mutex); - if (priv->config & CFG_STATIC_BSSID || - priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { - wrqu->ap_addr.sa_family = ARPHRD_ETHER; - memcpy(wrqu->ap_addr.sa_data, priv->bssid, ETH_ALEN); - } else - eth_zero_addr(wrqu->ap_addr.sa_data); - - IPW_DEBUG_WX("Getting WAP BSSID: %pM\n", - wrqu->ap_addr.sa_data); - mutex_unlock(&priv->mutex); - return 0; -} - -static int ipw_wx_set_essid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - int length; - - mutex_lock(&priv->mutex); - - if (!wrqu->essid.flags) - { - IPW_DEBUG_WX("Setting ESSID to ANY\n"); - ipw_disassociate(priv); - priv->config &= ~CFG_STATIC_ESSID; - ipw_associate(priv); - mutex_unlock(&priv->mutex); - return 0; - } - - length = min((int)wrqu->essid.length, IW_ESSID_MAX_SIZE); - - priv->config |= CFG_STATIC_ESSID; - - if (priv->essid_len == length && !memcmp(priv->essid, extra, length) - && (priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING))) { - IPW_DEBUG_WX("ESSID set to current ESSID.\n"); - mutex_unlock(&priv->mutex); - return 0; - } - - IPW_DEBUG_WX("Setting ESSID: '%*pE' (%d)\n", length, extra, length); - - priv->essid_len = length; - memcpy(priv->essid, extra, priv->essid_len); - - /* Network configuration changed -- force [re]association */ - IPW_DEBUG_ASSOC("[re]association triggered due to ESSID change.\n"); - if (!ipw_disassociate(priv)) - ipw_associate(priv); - - mutex_unlock(&priv->mutex); - return 0; -} - -static int ipw_wx_get_essid(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - - /* If we are associated, trying to associate, or have a statically - * configured ESSID then return that; otherwise return ANY */ - mutex_lock(&priv->mutex); - if (priv->config & CFG_STATIC_ESSID || - priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) { - IPW_DEBUG_WX("Getting essid: '%*pE'\n", - priv->essid_len, priv->essid); - memcpy(extra, priv->essid, priv->essid_len); - wrqu->essid.length = priv->essid_len; - wrqu->essid.flags = 1; /* active */ - } else { - IPW_DEBUG_WX("Getting essid: ANY\n"); - wrqu->essid.length = 0; - wrqu->essid.flags = 0; /* active */ - } - mutex_unlock(&priv->mutex); - return 0; -} - -static int ipw_wx_set_nick(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - - IPW_DEBUG_WX("Setting nick to '%s'\n", extra); - if (wrqu->data.length > IW_ESSID_MAX_SIZE) - return -E2BIG; - mutex_lock(&priv->mutex); - wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick)); - memset(priv->nick, 0, sizeof(priv->nick)); - memcpy(priv->nick, extra, wrqu->data.length); - IPW_DEBUG_TRACE("<<\n"); - mutex_unlock(&priv->mutex); - return 0; - -} - -static int ipw_wx_get_nick(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - IPW_DEBUG_WX("Getting nick\n"); - mutex_lock(&priv->mutex); - wrqu->data.length = strlen(priv->nick); - memcpy(extra, priv->nick, wrqu->data.length); - wrqu->data.flags = 1; /* active */ - mutex_unlock(&priv->mutex); - return 0; -} - -static int ipw_wx_set_sens(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - int err = 0; - - IPW_DEBUG_WX("Setting roaming threshold to %d\n", wrqu->sens.value); - IPW_DEBUG_WX("Setting disassociate threshold to %d\n", 3*wrqu->sens.value); - mutex_lock(&priv->mutex); - - if (wrqu->sens.fixed == 0) - { - priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT; - priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT; - goto out; - } - if ((wrqu->sens.value > IPW_MB_ROAMING_THRESHOLD_MAX) || - (wrqu->sens.value < IPW_MB_ROAMING_THRESHOLD_MIN)) { - err = -EINVAL; - goto out; - } - - priv->roaming_threshold = wrqu->sens.value; - priv->disassociate_threshold = 3*wrqu->sens.value; - out: - mutex_unlock(&priv->mutex); - return err; -} - -static int ipw_wx_get_sens(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - wrqu->sens.fixed = 1; - wrqu->sens.value = priv->roaming_threshold; - mutex_unlock(&priv->mutex); - - IPW_DEBUG_WX("GET roaming threshold -> %s %d\n", - wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value); - - return 0; -} - -static int ipw_wx_set_rate(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - /* TODO: We should use semaphores or locks for access to priv */ - struct ipw_priv *priv = libipw_priv(dev); - u32 target_rate = wrqu->bitrate.value; - u32 fixed, mask; - - /* value = -1, fixed = 0 means auto only, so we should use all rates offered by AP */ - /* value = X, fixed = 1 means only rate X */ - /* value = X, fixed = 0 means all rates lower equal X */ - - if (target_rate == -1) { - fixed = 0; - mask = LIBIPW_DEFAULT_RATES_MASK; - /* Now we should reassociate */ - goto apply; - } - - mask = 0; - fixed = wrqu->bitrate.fixed; - - if (target_rate == 1000000 || !fixed) - mask |= LIBIPW_CCK_RATE_1MB_MASK; - if (target_rate == 1000000) - goto apply; - - if (target_rate == 2000000 || !fixed) - mask |= LIBIPW_CCK_RATE_2MB_MASK; - if (target_rate == 2000000) - goto apply; - - if (target_rate == 5500000 || !fixed) - mask |= LIBIPW_CCK_RATE_5MB_MASK; - if (target_rate == 5500000) - goto apply; - - if (target_rate == 6000000 || !fixed) - mask |= LIBIPW_OFDM_RATE_6MB_MASK; - if (target_rate == 6000000) - goto apply; - - if (target_rate == 9000000 || !fixed) - mask |= LIBIPW_OFDM_RATE_9MB_MASK; - if (target_rate == 9000000) - goto apply; - - if (target_rate == 11000000 || !fixed) - mask |= LIBIPW_CCK_RATE_11MB_MASK; - if (target_rate == 11000000) - goto apply; - - if (target_rate == 12000000 || !fixed) - mask |= LIBIPW_OFDM_RATE_12MB_MASK; - if (target_rate == 12000000) - goto apply; - - if (target_rate == 18000000 || !fixed) - mask |= LIBIPW_OFDM_RATE_18MB_MASK; - if (target_rate == 18000000) - goto apply; - - if (target_rate == 24000000 || !fixed) - mask |= LIBIPW_OFDM_RATE_24MB_MASK; - if (target_rate == 24000000) - goto apply; - - if (target_rate == 36000000 || !fixed) - mask |= LIBIPW_OFDM_RATE_36MB_MASK; - if (target_rate == 36000000) - goto apply; - - if (target_rate == 48000000 || !fixed) - mask |= LIBIPW_OFDM_RATE_48MB_MASK; - if (target_rate == 48000000) - goto apply; - - if (target_rate == 54000000 || !fixed) - mask |= LIBIPW_OFDM_RATE_54MB_MASK; - if (target_rate == 54000000) - goto apply; - - IPW_DEBUG_WX("invalid rate specified, returning error\n"); - return -EINVAL; - - apply: - IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n", - mask, fixed ? "fixed" : "sub-rates"); - mutex_lock(&priv->mutex); - if (mask == LIBIPW_DEFAULT_RATES_MASK) { - priv->config &= ~CFG_FIXED_RATE; - ipw_set_fixed_rate(priv, priv->ieee->mode); - } else - priv->config |= CFG_FIXED_RATE; - - if (priv->rates_mask == mask) { - IPW_DEBUG_WX("Mask set to current mask.\n"); - mutex_unlock(&priv->mutex); - return 0; - } - - priv->rates_mask = mask; - - /* Network configuration changed -- force [re]association */ - IPW_DEBUG_ASSOC("[re]association triggered due to rates change.\n"); - if (!ipw_disassociate(priv)) - ipw_associate(priv); - - mutex_unlock(&priv->mutex); - return 0; -} - -static int ipw_wx_get_rate(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - wrqu->bitrate.value = priv->last_rate; - wrqu->bitrate.fixed = (priv->config & CFG_FIXED_RATE) ? 1 : 0; - mutex_unlock(&priv->mutex); - IPW_DEBUG_WX("GET Rate -> %d\n", wrqu->bitrate.value); - return 0; -} - -static int ipw_wx_set_rts(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - if (wrqu->rts.disabled || !wrqu->rts.fixed) - priv->rts_threshold = DEFAULT_RTS_THRESHOLD; - else { - if (wrqu->rts.value < MIN_RTS_THRESHOLD || - wrqu->rts.value > MAX_RTS_THRESHOLD) { - mutex_unlock(&priv->mutex); - return -EINVAL; - } - priv->rts_threshold = wrqu->rts.value; - } - - ipw_send_rts_threshold(priv, priv->rts_threshold); - mutex_unlock(&priv->mutex); - IPW_DEBUG_WX("SET RTS Threshold -> %d\n", priv->rts_threshold); - return 0; -} - -static int ipw_wx_get_rts(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - wrqu->rts.value = priv->rts_threshold; - wrqu->rts.fixed = 0; /* no auto select */ - wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD); - mutex_unlock(&priv->mutex); - IPW_DEBUG_WX("GET RTS Threshold -> %d\n", wrqu->rts.value); - return 0; -} - -static int ipw_wx_set_txpow(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - int err = 0; - - mutex_lock(&priv->mutex); - if (ipw_radio_kill_sw(priv, wrqu->power.disabled)) { - err = -EINPROGRESS; - goto out; - } - - if (!wrqu->power.fixed) - wrqu->power.value = IPW_TX_POWER_DEFAULT; - - if (wrqu->power.flags != IW_TXPOW_DBM) { - err = -EINVAL; - goto out; - } - - if ((wrqu->power.value > IPW_TX_POWER_MAX) || - (wrqu->power.value < IPW_TX_POWER_MIN)) { - err = -EINVAL; - goto out; - } - - priv->tx_power = wrqu->power.value; - err = ipw_set_tx_power(priv); - out: - mutex_unlock(&priv->mutex); - return err; -} - -static int ipw_wx_get_txpow(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - wrqu->power.value = priv->tx_power; - wrqu->power.fixed = 1; - wrqu->power.flags = IW_TXPOW_DBM; - wrqu->power.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0; - mutex_unlock(&priv->mutex); - - IPW_DEBUG_WX("GET TX Power -> %s %d\n", - wrqu->power.disabled ? "OFF" : "ON", wrqu->power.value); - - return 0; -} - -static int ipw_wx_set_frag(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - if (wrqu->frag.disabled || !wrqu->frag.fixed) - priv->ieee->fts = DEFAULT_FTS; - else { - if (wrqu->frag.value < MIN_FRAG_THRESHOLD || - wrqu->frag.value > MAX_FRAG_THRESHOLD) { - mutex_unlock(&priv->mutex); - return -EINVAL; - } - - priv->ieee->fts = wrqu->frag.value & ~0x1; - } - - ipw_send_frag_threshold(priv, wrqu->frag.value); - mutex_unlock(&priv->mutex); - IPW_DEBUG_WX("SET Frag Threshold -> %d\n", wrqu->frag.value); - return 0; -} - -static int ipw_wx_get_frag(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - wrqu->frag.value = priv->ieee->fts; - wrqu->frag.fixed = 0; /* no auto select */ - wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FTS); - mutex_unlock(&priv->mutex); - IPW_DEBUG_WX("GET Frag Threshold -> %d\n", wrqu->frag.value); - - return 0; -} - -static int ipw_wx_set_retry(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - - if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled) - return -EINVAL; - - if (!(wrqu->retry.flags & IW_RETRY_LIMIT)) - return 0; - - if (wrqu->retry.value < 0 || wrqu->retry.value >= 255) - return -EINVAL; - - mutex_lock(&priv->mutex); - if (wrqu->retry.flags & IW_RETRY_SHORT) - priv->short_retry_limit = (u8) wrqu->retry.value; - else if (wrqu->retry.flags & IW_RETRY_LONG) - priv->long_retry_limit = (u8) wrqu->retry.value; - else { - priv->short_retry_limit = (u8) wrqu->retry.value; - priv->long_retry_limit = (u8) wrqu->retry.value; - } - - ipw_send_retry_limit(priv, priv->short_retry_limit, - priv->long_retry_limit); - mutex_unlock(&priv->mutex); - IPW_DEBUG_WX("SET retry limit -> short:%d long:%d\n", - priv->short_retry_limit, priv->long_retry_limit); - return 0; -} - -static int ipw_wx_get_retry(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - - mutex_lock(&priv->mutex); - wrqu->retry.disabled = 0; - - if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { - mutex_unlock(&priv->mutex); - return -EINVAL; - } - - if (wrqu->retry.flags & IW_RETRY_LONG) { - wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG; - wrqu->retry.value = priv->long_retry_limit; - } else if (wrqu->retry.flags & IW_RETRY_SHORT) { - wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT; - wrqu->retry.value = priv->short_retry_limit; - } else { - wrqu->retry.flags = IW_RETRY_LIMIT; - wrqu->retry.value = priv->short_retry_limit; - } - mutex_unlock(&priv->mutex); - - IPW_DEBUG_WX("GET retry -> %d\n", wrqu->retry.value); - - return 0; -} - -static int ipw_wx_set_scan(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - struct iw_scan_req *req = (struct iw_scan_req *)extra; - struct delayed_work *work = NULL; - - mutex_lock(&priv->mutex); - - priv->user_requested_scan = 1; - - if (wrqu->data.length == sizeof(struct iw_scan_req)) { - if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { - int len = min((int)req->essid_len, - (int)sizeof(priv->direct_scan_ssid)); - memcpy(priv->direct_scan_ssid, req->essid, len); - priv->direct_scan_ssid_len = len; - work = &priv->request_direct_scan; - } else if (req->scan_type == IW_SCAN_TYPE_PASSIVE) { - work = &priv->request_passive_scan; - } - } else { - /* Normal active broadcast scan */ - work = &priv->request_scan; - } - - mutex_unlock(&priv->mutex); - - IPW_DEBUG_WX("Start scan\n"); - - schedule_delayed_work(work, 0); - - return 0; -} - -static int ipw_wx_get_scan(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - return libipw_wx_get_scan(priv->ieee, info, wrqu, extra); -} - -static int ipw_wx_set_encode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key) -{ - struct ipw_priv *priv = libipw_priv(dev); - int ret; - u32 cap = priv->capability; - - mutex_lock(&priv->mutex); - ret = libipw_wx_set_encode(priv->ieee, info, wrqu, key); - - /* In IBSS mode, we need to notify the firmware to update - * the beacon info after we changed the capability. */ - if (cap != priv->capability && - priv->ieee->iw_mode == IW_MODE_ADHOC && - priv->status & STATUS_ASSOCIATED) - ipw_disassociate(priv); - - mutex_unlock(&priv->mutex); - return ret; -} - -static int ipw_wx_get_encode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key) -{ - struct ipw_priv *priv = libipw_priv(dev); - return libipw_wx_get_encode(priv->ieee, info, wrqu, key); -} - -static int ipw_wx_set_power(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - int err; - mutex_lock(&priv->mutex); - if (wrqu->power.disabled) { - priv->power_mode = IPW_POWER_LEVEL(priv->power_mode); - err = ipw_send_power_mode(priv, IPW_POWER_MODE_CAM); - if (err) { - IPW_DEBUG_WX("failed setting power mode.\n"); - mutex_unlock(&priv->mutex); - return err; - } - IPW_DEBUG_WX("SET Power Management Mode -> off\n"); - mutex_unlock(&priv->mutex); - return 0; - } - - switch (wrqu->power.flags & IW_POWER_MODE) { - case IW_POWER_ON: /* If not specified */ - case IW_POWER_MODE: /* If set all mask */ - case IW_POWER_ALL_R: /* If explicitly state all */ - break; - default: /* Otherwise we don't support it */ - IPW_DEBUG_WX("SET PM Mode: %X not supported.\n", - wrqu->power.flags); - mutex_unlock(&priv->mutex); - return -EOPNOTSUPP; - } - - /* If the user hasn't specified a power management mode yet, default - * to BATTERY */ - if (IPW_POWER_LEVEL(priv->power_mode) == IPW_POWER_AC) - priv->power_mode = IPW_POWER_ENABLED | IPW_POWER_BATTERY; - else - priv->power_mode = IPW_POWER_ENABLED | priv->power_mode; - - err = ipw_send_power_mode(priv, IPW_POWER_LEVEL(priv->power_mode)); - if (err) { - IPW_DEBUG_WX("failed setting power mode.\n"); - mutex_unlock(&priv->mutex); - return err; - } - - IPW_DEBUG_WX("SET Power Management Mode -> 0x%02X\n", priv->power_mode); - mutex_unlock(&priv->mutex); - return 0; -} - -static int ipw_wx_get_power(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - if (!(priv->power_mode & IPW_POWER_ENABLED)) - wrqu->power.disabled = 1; - else - wrqu->power.disabled = 0; - - mutex_unlock(&priv->mutex); - IPW_DEBUG_WX("GET Power Management Mode -> %02X\n", priv->power_mode); - - return 0; -} - -static int ipw_wx_set_powermode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - int mode = *(int *)extra; - int err; - - mutex_lock(&priv->mutex); - if ((mode < 1) || (mode > IPW_POWER_LIMIT)) - mode = IPW_POWER_AC; - - if (IPW_POWER_LEVEL(priv->power_mode) != mode) { - err = ipw_send_power_mode(priv, mode); - if (err) { - IPW_DEBUG_WX("failed setting power mode.\n"); - mutex_unlock(&priv->mutex); - return err; - } - priv->power_mode = IPW_POWER_ENABLED | mode; - } - mutex_unlock(&priv->mutex); - return 0; -} - -#define MAX_WX_STRING 80 -static int ipw_wx_get_powermode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - int level = IPW_POWER_LEVEL(priv->power_mode); - char *p = extra; - - p += snprintf(p, MAX_WX_STRING, "Power save level: %d ", level); - - switch (level) { - case IPW_POWER_AC: - p += snprintf(p, MAX_WX_STRING - (p - extra), "(AC)"); - break; - case IPW_POWER_BATTERY: - p += snprintf(p, MAX_WX_STRING - (p - extra), "(BATTERY)"); - break; - default: - p += snprintf(p, MAX_WX_STRING - (p - extra), - "(Timeout %dms, Period %dms)", - timeout_duration[level - 1] / 1000, - period_duration[level - 1] / 1000); - } - - if (!(priv->power_mode & IPW_POWER_ENABLED)) - p += snprintf(p, MAX_WX_STRING - (p - extra), " OFF"); - - wrqu->data.length = p - extra + 1; - - return 0; -} - -static int ipw_wx_set_wireless_mode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - int mode = *(int *)extra; - u8 band = 0, modulation = 0; - - if (mode == 0 || mode & ~IEEE_MODE_MASK) { - IPW_WARNING("Attempt to set invalid wireless mode: %d\n", mode); - return -EINVAL; - } - mutex_lock(&priv->mutex); - if (priv->adapter == IPW_2915ABG) { - priv->ieee->abg_true = 1; - if (mode & IEEE_A) { - band |= LIBIPW_52GHZ_BAND; - modulation |= LIBIPW_OFDM_MODULATION; - } else - priv->ieee->abg_true = 0; - } else { - if (mode & IEEE_A) { - IPW_WARNING("Attempt to set 2200BG into " - "802.11a mode\n"); - mutex_unlock(&priv->mutex); - return -EINVAL; - } - - priv->ieee->abg_true = 0; - } - - if (mode & IEEE_B) { - band |= LIBIPW_24GHZ_BAND; - modulation |= LIBIPW_CCK_MODULATION; - } else - priv->ieee->abg_true = 0; - - if (mode & IEEE_G) { - band |= LIBIPW_24GHZ_BAND; - modulation |= LIBIPW_OFDM_MODULATION; - } else - priv->ieee->abg_true = 0; - - priv->ieee->mode = mode; - priv->ieee->freq_band = band; - priv->ieee->modulation = modulation; - init_supported_rates(priv, &priv->rates); - - /* Network configuration changed -- force [re]association */ - IPW_DEBUG_ASSOC("[re]association triggered due to mode change.\n"); - if (!ipw_disassociate(priv)) { - ipw_send_supported_rates(priv, &priv->rates); - ipw_associate(priv); - } - - /* Update the band LEDs */ - ipw_led_band_on(priv); - - IPW_DEBUG_WX("PRIV SET MODE: %c%c%c\n", - mode & IEEE_A ? 'a' : '.', - mode & IEEE_B ? 'b' : '.', mode & IEEE_G ? 'g' : '.'); - mutex_unlock(&priv->mutex); - return 0; -} - -static int ipw_wx_get_wireless_mode(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - switch (priv->ieee->mode) { - case IEEE_A: - strncpy(extra, "802.11a (1)", MAX_WX_STRING); - break; - case IEEE_B: - strncpy(extra, "802.11b (2)", MAX_WX_STRING); - break; - case IEEE_A | IEEE_B: - strncpy(extra, "802.11ab (3)", MAX_WX_STRING); - break; - case IEEE_G: - strncpy(extra, "802.11g (4)", MAX_WX_STRING); - break; - case IEEE_A | IEEE_G: - strncpy(extra, "802.11ag (5)", MAX_WX_STRING); - break; - case IEEE_B | IEEE_G: - strncpy(extra, "802.11bg (6)", MAX_WX_STRING); - break; - case IEEE_A | IEEE_B | IEEE_G: - strncpy(extra, "802.11abg (7)", MAX_WX_STRING); - break; - default: - strncpy(extra, "unknown", MAX_WX_STRING); - break; - } - extra[MAX_WX_STRING - 1] = '\0'; - - IPW_DEBUG_WX("PRIV GET MODE: %s\n", extra); - - wrqu->data.length = strlen(extra) + 1; - mutex_unlock(&priv->mutex); - - return 0; -} - -static int ipw_wx_set_preamble(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - int mode = *(int *)extra; - mutex_lock(&priv->mutex); - /* Switching from SHORT -> LONG requires a disassociation */ - if (mode == 1) { - if (!(priv->config & CFG_PREAMBLE_LONG)) { - priv->config |= CFG_PREAMBLE_LONG; - - /* Network configuration changed -- force [re]association */ - IPW_DEBUG_ASSOC - ("[re]association triggered due to preamble change.\n"); - if (!ipw_disassociate(priv)) - ipw_associate(priv); - } - goto done; - } - - if (mode == 0) { - priv->config &= ~CFG_PREAMBLE_LONG; - goto done; - } - mutex_unlock(&priv->mutex); - return -EINVAL; - - done: - mutex_unlock(&priv->mutex); - return 0; -} - -static int ipw_wx_get_preamble(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - mutex_lock(&priv->mutex); - if (priv->config & CFG_PREAMBLE_LONG) - snprintf(wrqu->name, IFNAMSIZ, "long (1)"); - else - snprintf(wrqu->name, IFNAMSIZ, "auto (0)"); - mutex_unlock(&priv->mutex); - return 0; -} - -#ifdef CONFIG_IPW2200_MONITOR -static int ipw_wx_set_monitor(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - int *parms = (int *)extra; - int enable = (parms[0] > 0); - mutex_lock(&priv->mutex); - IPW_DEBUG_WX("SET MONITOR: %d %d\n", enable, parms[1]); - if (enable) { - if (priv->ieee->iw_mode != IW_MODE_MONITOR) { -#ifdef CONFIG_IPW2200_RADIOTAP - priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP; -#else - priv->net_dev->type = ARPHRD_IEEE80211; -#endif - schedule_work(&priv->adapter_restart); - } - - ipw_set_channel(priv, parms[1]); - } else { - if (priv->ieee->iw_mode != IW_MODE_MONITOR) { - mutex_unlock(&priv->mutex); - return 0; - } - priv->net_dev->type = ARPHRD_ETHER; - schedule_work(&priv->adapter_restart); - } - mutex_unlock(&priv->mutex); - return 0; -} - -#endif /* CONFIG_IPW2200_MONITOR */ - -static int ipw_wx_reset(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - IPW_DEBUG_WX("RESET\n"); - schedule_work(&priv->adapter_restart); - return 0; -} - -static int ipw_wx_sw_reset(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = libipw_priv(dev); - union iwreq_data wrqu_sec = { - .encoding = { - .flags = IW_ENCODE_DISABLED, - }, - }; - int ret; - - IPW_DEBUG_WX("SW_RESET\n"); - - mutex_lock(&priv->mutex); - - ret = ipw_sw_reset(priv, 2); - if (!ret) { - free_firmware(); - ipw_adapter_restart(priv); - } - - /* The SW reset bit might have been toggled on by the 'disable' - * module parameter, so take appropriate action */ - ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW); - - mutex_unlock(&priv->mutex); - libipw_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL); - mutex_lock(&priv->mutex); - - if (!(priv->status & STATUS_RF_KILL_MASK)) { - /* Configuration likely changed -- force [re]association */ - IPW_DEBUG_ASSOC("[re]association triggered due to sw " - "reset.\n"); - if (!ipw_disassociate(priv)) - ipw_associate(priv); - } - - mutex_unlock(&priv->mutex); - - return 0; -} - -/* Rebase the WE IOCTLs to zero for the handler array */ -static iw_handler ipw_wx_handlers[] = { - IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), - IW_HANDLER(SIOCSIWFREQ, ipw_wx_set_freq), - IW_HANDLER(SIOCGIWFREQ, ipw_wx_get_freq), - IW_HANDLER(SIOCSIWMODE, ipw_wx_set_mode), - IW_HANDLER(SIOCGIWMODE, ipw_wx_get_mode), - IW_HANDLER(SIOCSIWSENS, ipw_wx_set_sens), - IW_HANDLER(SIOCGIWSENS, ipw_wx_get_sens), - IW_HANDLER(SIOCGIWRANGE, ipw_wx_get_range), - IW_HANDLER(SIOCSIWAP, ipw_wx_set_wap), - IW_HANDLER(SIOCGIWAP, ipw_wx_get_wap), - IW_HANDLER(SIOCSIWSCAN, ipw_wx_set_scan), - IW_HANDLER(SIOCGIWSCAN, ipw_wx_get_scan), - IW_HANDLER(SIOCSIWESSID, ipw_wx_set_essid), - IW_HANDLER(SIOCGIWESSID, ipw_wx_get_essid), - IW_HANDLER(SIOCSIWNICKN, ipw_wx_set_nick), - IW_HANDLER(SIOCGIWNICKN, ipw_wx_get_nick), - IW_HANDLER(SIOCSIWRATE, ipw_wx_set_rate), - IW_HANDLER(SIOCGIWRATE, ipw_wx_get_rate), - IW_HANDLER(SIOCSIWRTS, ipw_wx_set_rts), - IW_HANDLER(SIOCGIWRTS, ipw_wx_get_rts), - IW_HANDLER(SIOCSIWFRAG, ipw_wx_set_frag), - IW_HANDLER(SIOCGIWFRAG, ipw_wx_get_frag), - IW_HANDLER(SIOCSIWTXPOW, ipw_wx_set_txpow), - IW_HANDLER(SIOCGIWTXPOW, ipw_wx_get_txpow), - IW_HANDLER(SIOCSIWRETRY, ipw_wx_set_retry), - IW_HANDLER(SIOCGIWRETRY, ipw_wx_get_retry), - IW_HANDLER(SIOCSIWENCODE, ipw_wx_set_encode), - IW_HANDLER(SIOCGIWENCODE, ipw_wx_get_encode), - IW_HANDLER(SIOCSIWPOWER, ipw_wx_set_power), - IW_HANDLER(SIOCGIWPOWER, ipw_wx_get_power), - IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), - IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), - IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), - IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), - IW_HANDLER(SIOCSIWGENIE, ipw_wx_set_genie), - IW_HANDLER(SIOCGIWGENIE, ipw_wx_get_genie), - IW_HANDLER(SIOCSIWMLME, ipw_wx_set_mlme), - IW_HANDLER(SIOCSIWAUTH, ipw_wx_set_auth), - IW_HANDLER(SIOCGIWAUTH, ipw_wx_get_auth), - IW_HANDLER(SIOCSIWENCODEEXT, ipw_wx_set_encodeext), - IW_HANDLER(SIOCGIWENCODEEXT, ipw_wx_get_encodeext), -}; - -enum { - IPW_PRIV_SET_POWER = SIOCIWFIRSTPRIV, - IPW_PRIV_GET_POWER, - IPW_PRIV_SET_MODE, - IPW_PRIV_GET_MODE, - IPW_PRIV_SET_PREAMBLE, - IPW_PRIV_GET_PREAMBLE, - IPW_PRIV_RESET, - IPW_PRIV_SW_RESET, -#ifdef CONFIG_IPW2200_MONITOR - IPW_PRIV_SET_MONITOR, -#endif -}; - -static struct iw_priv_args ipw_priv_args[] = { - { - .cmd = IPW_PRIV_SET_POWER, - .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - .name = "set_power"}, - { - .cmd = IPW_PRIV_GET_POWER, - .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, - .name = "get_power"}, - { - .cmd = IPW_PRIV_SET_MODE, - .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - .name = "set_mode"}, - { - .cmd = IPW_PRIV_GET_MODE, - .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, - .name = "get_mode"}, - { - .cmd = IPW_PRIV_SET_PREAMBLE, - .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - .name = "set_preamble"}, - { - .cmd = IPW_PRIV_GET_PREAMBLE, - .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | IFNAMSIZ, - .name = "get_preamble"}, - { - IPW_PRIV_RESET, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "reset"}, - { - IPW_PRIV_SW_RESET, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 0, 0, "sw_reset"}, -#ifdef CONFIG_IPW2200_MONITOR - { - IPW_PRIV_SET_MONITOR, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"}, -#endif /* CONFIG_IPW2200_MONITOR */ -}; - -static iw_handler ipw_priv_handler[] = { - ipw_wx_set_powermode, - ipw_wx_get_powermode, - ipw_wx_set_wireless_mode, - ipw_wx_get_wireless_mode, - ipw_wx_set_preamble, - ipw_wx_get_preamble, - ipw_wx_reset, - ipw_wx_sw_reset, -#ifdef CONFIG_IPW2200_MONITOR - ipw_wx_set_monitor, -#endif -}; - -static struct iw_handler_def ipw_wx_handler_def = { - .standard = ipw_wx_handlers, - .num_standard = ARRAY_SIZE(ipw_wx_handlers), - .num_private = ARRAY_SIZE(ipw_priv_handler), - .num_private_args = ARRAY_SIZE(ipw_priv_args), - .private = ipw_priv_handler, - .private_args = ipw_priv_args, - .get_wireless_stats = ipw_get_wireless_stats, -}; - -/* - * Get wireless statistics. - * Called by /proc/net/wireless - * Also called by SIOCGIWSTATS - */ -static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev) -{ - struct ipw_priv *priv = libipw_priv(dev); - struct iw_statistics *wstats; - - wstats = &priv->wstats; - - /* if hw is disabled, then ipw_get_ordinal() can't be called. - * netdev->get_wireless_stats seems to be called before fw is - * initialized. STATUS_ASSOCIATED will only be set if the hw is up - * and associated; if not associcated, the values are all meaningless - * anyway, so set them all to NULL and INVALID */ - if (!(priv->status & STATUS_ASSOCIATED)) { - wstats->miss.beacon = 0; - wstats->discard.retries = 0; - wstats->qual.qual = 0; - wstats->qual.level = 0; - wstats->qual.noise = 0; - wstats->qual.updated = 7; - wstats->qual.updated |= IW_QUAL_NOISE_INVALID | - IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; - return wstats; - } - - wstats->qual.qual = priv->quality; - wstats->qual.level = priv->exp_avg_rssi; - wstats->qual.noise = priv->exp_avg_noise; - wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | - IW_QUAL_NOISE_UPDATED | IW_QUAL_DBM; - - wstats->miss.beacon = average_value(&priv->average_missed_beacons); - wstats->discard.retries = priv->last_tx_failures; - wstats->discard.code = priv->ieee->ieee_stats.rx_discards_undecryptable; - -/* if (ipw_get_ordinal(priv, IPW_ORD_STAT_TX_RETRY, &tx_retry, &len)) - goto fail_get_ordinal; - wstats->discard.retries += tx_retry; */ - - return wstats; -} - -/* net device stuff */ - -static void init_sys_config(struct ipw_sys_config *sys_config) -{ - memset(sys_config, 0, sizeof(struct ipw_sys_config)); - sys_config->bt_coexistence = 0; - sys_config->answer_broadcast_ssid_probe = 0; - sys_config->accept_all_data_frames = 0; - sys_config->accept_non_directed_frames = 1; - sys_config->exclude_unicast_unencrypted = 0; - sys_config->disable_unicast_decryption = 1; - sys_config->exclude_multicast_unencrypted = 0; - sys_config->disable_multicast_decryption = 1; - if (antenna < CFG_SYS_ANTENNA_BOTH || antenna > CFG_SYS_ANTENNA_B) - antenna = CFG_SYS_ANTENNA_BOTH; - sys_config->antenna_diversity = antenna; - sys_config->pass_crc_to_host = 0; /* TODO: See if 1 gives us FCS */ - sys_config->dot11g_auto_detection = 0; - sys_config->enable_cts_to_self = 0; - sys_config->bt_coexist_collision_thr = 0; - sys_config->pass_noise_stats_to_host = 1; /* 1 -- fix for 256 */ - sys_config->silence_threshold = 0x1e; -} - -static int ipw_net_open(struct net_device *dev) -{ - IPW_DEBUG_INFO("dev->open\n"); - netif_start_queue(dev); - return 0; -} - -static int ipw_net_stop(struct net_device *dev) -{ - IPW_DEBUG_INFO("dev->close\n"); - netif_stop_queue(dev); - return 0; -} - -/* -todo: - -modify to send one tfd per fragment instead of using chunking. otherwise -we need to heavily modify the libipw_skb_to_txb. -*/ - -static int ipw_tx_skb(struct ipw_priv *priv, struct libipw_txb *txb, - int pri) -{ - struct libipw_hdr_3addrqos *hdr = (struct libipw_hdr_3addrqos *) - txb->fragments[0]->data; - int i = 0; - struct tfd_frame *tfd; -#ifdef CONFIG_IPW2200_QOS - int tx_id = ipw_get_tx_queue_number(priv, pri); - struct clx2_tx_queue *txq = &priv->txq[tx_id]; -#else - struct clx2_tx_queue *txq = &priv->txq[0]; -#endif - struct clx2_queue *q = &txq->q; - u8 id, hdr_len, unicast; - int fc; - - if (!(priv->status & STATUS_ASSOCIATED)) - goto drop; - - hdr_len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); - switch (priv->ieee->iw_mode) { - case IW_MODE_ADHOC: - unicast = !is_multicast_ether_addr(hdr->addr1); - id = ipw_find_station(priv, hdr->addr1); - if (id == IPW_INVALID_STATION) { - id = ipw_add_station(priv, hdr->addr1); - if (id == IPW_INVALID_STATION) { - IPW_WARNING("Attempt to send data to " - "invalid cell: %pM\n", - hdr->addr1); - goto drop; - } - } - break; - - case IW_MODE_INFRA: - default: - unicast = !is_multicast_ether_addr(hdr->addr3); - id = 0; - break; - } - - tfd = &txq->bd[q->first_empty]; - txq->txb[q->first_empty] = txb; - memset(tfd, 0, sizeof(*tfd)); - tfd->u.data.station_number = id; - - tfd->control_flags.message_type = TX_FRAME_TYPE; - tfd->control_flags.control_bits = TFD_NEED_IRQ_MASK; - - tfd->u.data.cmd_id = DINO_CMD_TX; - tfd->u.data.len = cpu_to_le16(txb->payload_size); - - if (priv->assoc_request.ieee_mode == IPW_B_MODE) - tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK; - else - tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_OFDM; - - if (priv->assoc_request.preamble_length == DCT_FLAG_SHORT_PREAMBLE) - tfd->u.data.tx_flags |= DCT_FLAG_SHORT_PREAMBLE; - - fc = le16_to_cpu(hdr->frame_ctl); - hdr->frame_ctl = cpu_to_le16(fc & ~IEEE80211_FCTL_MOREFRAGS); - - memcpy(&tfd->u.data.tfd.tfd_24.mchdr, hdr, hdr_len); - - if (likely(unicast)) - tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD; - - if (txb->encrypted && !priv->ieee->host_encrypt) { - switch (priv->ieee->sec.level) { - case SEC_LEVEL_3: - tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |= - cpu_to_le16(IEEE80211_FCTL_PROTECTED); - /* XXX: ACK flag must be set for CCMP even if it - * is a multicast/broadcast packet, because CCMP - * group communication encrypted by GTK is - * actually done by the AP. */ - if (!unicast) - tfd->u.data.tx_flags |= DCT_FLAG_ACK_REQD; - - tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP; - tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_CCM; - tfd->u.data.key_index = 0; - tfd->u.data.key_index |= DCT_WEP_INDEX_USE_IMMEDIATE; - break; - case SEC_LEVEL_2: - tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |= - cpu_to_le16(IEEE80211_FCTL_PROTECTED); - tfd->u.data.tx_flags &= ~DCT_FLAG_NO_WEP; - tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_SECURITY_TKIP; - tfd->u.data.key_index = DCT_WEP_INDEX_USE_IMMEDIATE; - break; - case SEC_LEVEL_1: - tfd->u.data.tfd.tfd_24.mchdr.frame_ctl |= - cpu_to_le16(IEEE80211_FCTL_PROTECTED); - tfd->u.data.key_index = priv->ieee->crypt_info.tx_keyidx; - if (priv->ieee->sec.key_sizes[priv->ieee->crypt_info.tx_keyidx] <= - 40) - tfd->u.data.key_index |= DCT_WEP_KEY_64Bit; - else - tfd->u.data.key_index |= DCT_WEP_KEY_128Bit; - break; - case SEC_LEVEL_0: - break; - default: - printk(KERN_ERR "Unknown security level %d\n", - priv->ieee->sec.level); - break; - } - } else - /* No hardware encryption */ - tfd->u.data.tx_flags |= DCT_FLAG_NO_WEP; - -#ifdef CONFIG_IPW2200_QOS - if (fc & IEEE80211_STYPE_QOS_DATA) - ipw_qos_set_tx_queue_command(priv, pri, &(tfd->u.data)); -#endif /* CONFIG_IPW2200_QOS */ - - /* payload */ - tfd->u.data.num_chunks = cpu_to_le32(min((u8) (NUM_TFD_CHUNKS - 2), - txb->nr_frags)); - IPW_DEBUG_FRAG("%i fragments being sent as %i chunks.\n", - txb->nr_frags, le32_to_cpu(tfd->u.data.num_chunks)); - for (i = 0; i < le32_to_cpu(tfd->u.data.num_chunks); i++) { - IPW_DEBUG_FRAG("Adding fragment %i of %i (%d bytes).\n", - i, le32_to_cpu(tfd->u.data.num_chunks), - txb->fragments[i]->len - hdr_len); - IPW_DEBUG_TX("Dumping TX packet frag %i of %i (%d bytes):\n", - i, tfd->u.data.num_chunks, - txb->fragments[i]->len - hdr_len); - printk_buf(IPW_DL_TX, txb->fragments[i]->data + hdr_len, - txb->fragments[i]->len - hdr_len); - - tfd->u.data.chunk_ptr[i] = - cpu_to_le32(pci_map_single - (priv->pci_dev, - txb->fragments[i]->data + hdr_len, - txb->fragments[i]->len - hdr_len, - PCI_DMA_TODEVICE)); - tfd->u.data.chunk_len[i] = - cpu_to_le16(txb->fragments[i]->len - hdr_len); - } - - if (i != txb->nr_frags) { - struct sk_buff *skb; - u16 remaining_bytes = 0; - int j; - - for (j = i; j < txb->nr_frags; j++) - remaining_bytes += txb->fragments[j]->len - hdr_len; - - printk(KERN_INFO "Trying to reallocate for %d bytes\n", - remaining_bytes); - skb = alloc_skb(remaining_bytes, GFP_ATOMIC); - if (skb != NULL) { - tfd->u.data.chunk_len[i] = cpu_to_le16(remaining_bytes); - for (j = i; j < txb->nr_frags; j++) { - int size = txb->fragments[j]->len - hdr_len; - - printk(KERN_INFO "Adding frag %d %d...\n", - j, size); - memcpy(skb_put(skb, size), - txb->fragments[j]->data + hdr_len, size); - } - dev_kfree_skb_any(txb->fragments[i]); - txb->fragments[i] = skb; - tfd->u.data.chunk_ptr[i] = - cpu_to_le32(pci_map_single - (priv->pci_dev, skb->data, - remaining_bytes, - PCI_DMA_TODEVICE)); - - le32_add_cpu(&tfd->u.data.num_chunks, 1); - } - } - - /* kick DMA */ - q->first_empty = ipw_queue_inc_wrap(q->first_empty, q->n_bd); - ipw_write32(priv, q->reg_w, q->first_empty); - - if (ipw_tx_queue_space(q) < q->high_mark) - netif_stop_queue(priv->net_dev); - - return NETDEV_TX_OK; - - drop: - IPW_DEBUG_DROP("Silently dropping Tx packet.\n"); - libipw_txb_free(txb); - return NETDEV_TX_OK; -} - -static int ipw_net_is_queue_full(struct net_device *dev, int pri) -{ - struct ipw_priv *priv = libipw_priv(dev); -#ifdef CONFIG_IPW2200_QOS - int tx_id = ipw_get_tx_queue_number(priv, pri); - struct clx2_tx_queue *txq = &priv->txq[tx_id]; -#else - struct clx2_tx_queue *txq = &priv->txq[0]; -#endif /* CONFIG_IPW2200_QOS */ - - if (ipw_tx_queue_space(&txq->q) < txq->q.high_mark) - return 1; - - return 0; -} - -#ifdef CONFIG_IPW2200_PROMISCUOUS -static void ipw_handle_promiscuous_tx(struct ipw_priv *priv, - struct libipw_txb *txb) -{ - struct libipw_rx_stats dummystats; - struct ieee80211_hdr *hdr; - u8 n; - u16 filter = priv->prom_priv->filter; - int hdr_only = 0; - - if (filter & IPW_PROM_NO_TX) - return; - - memset(&dummystats, 0, sizeof(dummystats)); - - /* Filtering of fragment chains is done against the first fragment */ - hdr = (void *)txb->fragments[0]->data; - if (libipw_is_management(le16_to_cpu(hdr->frame_control))) { - if (filter & IPW_PROM_NO_MGMT) - return; - if (filter & IPW_PROM_MGMT_HEADER_ONLY) - hdr_only = 1; - } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) { - if (filter & IPW_PROM_NO_CTL) - return; - if (filter & IPW_PROM_CTL_HEADER_ONLY) - hdr_only = 1; - } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) { - if (filter & IPW_PROM_NO_DATA) - return; - if (filter & IPW_PROM_DATA_HEADER_ONLY) - hdr_only = 1; - } - - for(n=0; nnr_frags; ++n) { - struct sk_buff *src = txb->fragments[n]; - struct sk_buff *dst; - struct ieee80211_radiotap_header *rt_hdr; - int len; - - if (hdr_only) { - hdr = (void *)src->data; - len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control)); - } else - len = src->len; - - dst = alloc_skb(len + sizeof(*rt_hdr) + sizeof(u16)*2, GFP_ATOMIC); - if (!dst) - continue; - - rt_hdr = (void *)skb_put(dst, sizeof(*rt_hdr)); - - rt_hdr->it_version = PKTHDR_RADIOTAP_VERSION; - rt_hdr->it_pad = 0; - rt_hdr->it_present = 0; /* after all, it's just an idea */ - rt_hdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL); - - *(__le16*)skb_put(dst, sizeof(u16)) = cpu_to_le16( - ieee80211chan2mhz(priv->channel)); - if (priv->channel > 14) /* 802.11a */ - *(__le16*)skb_put(dst, sizeof(u16)) = - cpu_to_le16(IEEE80211_CHAN_OFDM | - IEEE80211_CHAN_5GHZ); - else if (priv->ieee->mode == IEEE_B) /* 802.11b */ - *(__le16*)skb_put(dst, sizeof(u16)) = - cpu_to_le16(IEEE80211_CHAN_CCK | - IEEE80211_CHAN_2GHZ); - else /* 802.11g */ - *(__le16*)skb_put(dst, sizeof(u16)) = - cpu_to_le16(IEEE80211_CHAN_OFDM | - IEEE80211_CHAN_2GHZ); - - rt_hdr->it_len = cpu_to_le16(dst->len); - - skb_copy_from_linear_data(src, skb_put(dst, len), len); - - if (!libipw_rx(priv->prom_priv->ieee, dst, &dummystats)) - dev_kfree_skb_any(dst); - } -} -#endif - -static netdev_tx_t ipw_net_hard_start_xmit(struct libipw_txb *txb, - struct net_device *dev, int pri) -{ - struct ipw_priv *priv = libipw_priv(dev); - unsigned long flags; - netdev_tx_t ret; - - IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size); - spin_lock_irqsave(&priv->lock, flags); - -#ifdef CONFIG_IPW2200_PROMISCUOUS - if (rtap_iface && netif_running(priv->prom_net_dev)) - ipw_handle_promiscuous_tx(priv, txb); -#endif - - ret = ipw_tx_skb(priv, txb, pri); - if (ret == NETDEV_TX_OK) - __ipw_led_activity_on(priv); - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; -} - -static void ipw_net_set_multicast_list(struct net_device *dev) -{ - -} - -static int ipw_net_set_mac_address(struct net_device *dev, void *p) -{ - struct ipw_priv *priv = libipw_priv(dev); - struct sockaddr *addr = p; - - if (!is_valid_ether_addr(addr->sa_data)) - return -EADDRNOTAVAIL; - mutex_lock(&priv->mutex); - priv->config |= CFG_CUSTOM_MAC; - memcpy(priv->mac_addr, addr->sa_data, ETH_ALEN); - printk(KERN_INFO "%s: Setting MAC to %pM\n", - priv->net_dev->name, priv->mac_addr); - schedule_work(&priv->adapter_restart); - mutex_unlock(&priv->mutex); - return 0; -} - -static void ipw_ethtool_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - struct ipw_priv *p = libipw_priv(dev); - char vers[64]; - char date[32]; - u32 len; - - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - - len = sizeof(vers); - ipw_get_ordinal(p, IPW_ORD_STAT_FW_VERSION, vers, &len); - len = sizeof(date); - ipw_get_ordinal(p, IPW_ORD_STAT_FW_DATE, date, &len); - - snprintf(info->fw_version, sizeof(info->fw_version), "%s (%s)", - vers, date); - strlcpy(info->bus_info, pci_name(p->pci_dev), - sizeof(info->bus_info)); -} - -static u32 ipw_ethtool_get_link(struct net_device *dev) -{ - struct ipw_priv *priv = libipw_priv(dev); - return (priv->status & STATUS_ASSOCIATED) != 0; -} - -static int ipw_ethtool_get_eeprom_len(struct net_device *dev) -{ - return IPW_EEPROM_IMAGE_SIZE; -} - -static int ipw_ethtool_get_eeprom(struct net_device *dev, - struct ethtool_eeprom *eeprom, u8 * bytes) -{ - struct ipw_priv *p = libipw_priv(dev); - - if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE) - return -EINVAL; - mutex_lock(&p->mutex); - memcpy(bytes, &p->eeprom[eeprom->offset], eeprom->len); - mutex_unlock(&p->mutex); - return 0; -} - -static int ipw_ethtool_set_eeprom(struct net_device *dev, - struct ethtool_eeprom *eeprom, u8 * bytes) -{ - struct ipw_priv *p = libipw_priv(dev); - int i; - - if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE) - return -EINVAL; - mutex_lock(&p->mutex); - memcpy(&p->eeprom[eeprom->offset], bytes, eeprom->len); - for (i = 0; i < IPW_EEPROM_IMAGE_SIZE; i++) - ipw_write8(p, i + IPW_EEPROM_DATA, p->eeprom[i]); - mutex_unlock(&p->mutex); - return 0; -} - -static const struct ethtool_ops ipw_ethtool_ops = { - .get_link = ipw_ethtool_get_link, - .get_drvinfo = ipw_ethtool_get_drvinfo, - .get_eeprom_len = ipw_ethtool_get_eeprom_len, - .get_eeprom = ipw_ethtool_get_eeprom, - .set_eeprom = ipw_ethtool_set_eeprom, -}; - -static irqreturn_t ipw_isr(int irq, void *data) -{ - struct ipw_priv *priv = data; - u32 inta, inta_mask; - - if (!priv) - return IRQ_NONE; - - spin_lock(&priv->irq_lock); - - if (!(priv->status & STATUS_INT_ENABLED)) { - /* IRQ is disabled */ - goto none; - } - - inta = ipw_read32(priv, IPW_INTA_RW); - inta_mask = ipw_read32(priv, IPW_INTA_MASK_R); - - if (inta == 0xFFFFFFFF) { - /* Hardware disappeared */ - IPW_WARNING("IRQ INTA == 0xFFFFFFFF\n"); - goto none; - } - - if (!(inta & (IPW_INTA_MASK_ALL & inta_mask))) { - /* Shared interrupt */ - goto none; - } - - /* tell the device to stop sending interrupts */ - __ipw_disable_interrupts(priv); - - /* ack current interrupts */ - inta &= (IPW_INTA_MASK_ALL & inta_mask); - ipw_write32(priv, IPW_INTA_RW, inta); - - /* Cache INTA value for our tasklet */ - priv->isr_inta = inta; - - tasklet_schedule(&priv->irq_tasklet); - - spin_unlock(&priv->irq_lock); - - return IRQ_HANDLED; - none: - spin_unlock(&priv->irq_lock); - return IRQ_NONE; -} - -static void ipw_rf_kill(void *adapter) -{ - struct ipw_priv *priv = adapter; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - - if (rf_kill_active(priv)) { - IPW_DEBUG_RF_KILL("RF Kill active, rescheduling GPIO check\n"); - schedule_delayed_work(&priv->rf_kill, 2 * HZ); - goto exit_unlock; - } - - /* RF Kill is now disabled, so bring the device back up */ - - if (!(priv->status & STATUS_RF_KILL_MASK)) { - IPW_DEBUG_RF_KILL("HW RF Kill no longer active, restarting " - "device\n"); - - /* we can not do an adapter restart while inside an irq lock */ - schedule_work(&priv->adapter_restart); - } else - IPW_DEBUG_RF_KILL("HW RF Kill deactivated. SW RF Kill still " - "enabled\n"); - - exit_unlock: - spin_unlock_irqrestore(&priv->lock, flags); -} - -static void ipw_bg_rf_kill(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, rf_kill.work); - mutex_lock(&priv->mutex); - ipw_rf_kill(priv); - mutex_unlock(&priv->mutex); -} - -static void ipw_link_up(struct ipw_priv *priv) -{ - priv->last_seq_num = -1; - priv->last_frag_num = -1; - priv->last_packet_time = 0; - - netif_carrier_on(priv->net_dev); - - cancel_delayed_work(&priv->request_scan); - cancel_delayed_work(&priv->request_direct_scan); - cancel_delayed_work(&priv->request_passive_scan); - cancel_delayed_work(&priv->scan_event); - ipw_reset_stats(priv); - /* Ensure the rate is updated immediately */ - priv->last_rate = ipw_get_current_rate(priv); - ipw_gather_stats(priv); - ipw_led_link_up(priv); - notify_wx_assoc_event(priv); - - if (priv->config & CFG_BACKGROUND_SCAN) - schedule_delayed_work(&priv->request_scan, HZ); -} - -static void ipw_bg_link_up(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, link_up); - mutex_lock(&priv->mutex); - ipw_link_up(priv); - mutex_unlock(&priv->mutex); -} - -static void ipw_link_down(struct ipw_priv *priv) -{ - ipw_led_link_down(priv); - netif_carrier_off(priv->net_dev); - notify_wx_assoc_event(priv); - - /* Cancel any queued work ... */ - cancel_delayed_work(&priv->request_scan); - cancel_delayed_work(&priv->request_direct_scan); - cancel_delayed_work(&priv->request_passive_scan); - cancel_delayed_work(&priv->adhoc_check); - cancel_delayed_work(&priv->gather_stats); - - ipw_reset_stats(priv); - - if (!(priv->status & STATUS_EXIT_PENDING)) { - /* Queue up another scan... */ - schedule_delayed_work(&priv->request_scan, 0); - } else - cancel_delayed_work(&priv->scan_event); -} - -static void ipw_bg_link_down(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, link_down); - mutex_lock(&priv->mutex); - ipw_link_down(priv); - mutex_unlock(&priv->mutex); -} - -static int ipw_setup_deferred_work(struct ipw_priv *priv) -{ - int ret = 0; - - init_waitqueue_head(&priv->wait_command_queue); - init_waitqueue_head(&priv->wait_state); - - INIT_DELAYED_WORK(&priv->adhoc_check, ipw_bg_adhoc_check); - INIT_WORK(&priv->associate, ipw_bg_associate); - INIT_WORK(&priv->disassociate, ipw_bg_disassociate); - INIT_WORK(&priv->system_config, ipw_system_config); - INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish); - INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart); - INIT_DELAYED_WORK(&priv->rf_kill, ipw_bg_rf_kill); - INIT_WORK(&priv->up, ipw_bg_up); - INIT_WORK(&priv->down, ipw_bg_down); - INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan); - INIT_DELAYED_WORK(&priv->request_direct_scan, ipw_request_direct_scan); - INIT_DELAYED_WORK(&priv->request_passive_scan, ipw_request_passive_scan); - INIT_DELAYED_WORK(&priv->scan_event, ipw_scan_event); - INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats); - INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan); - INIT_WORK(&priv->roam, ipw_bg_roam); - INIT_DELAYED_WORK(&priv->scan_check, ipw_bg_scan_check); - INIT_WORK(&priv->link_up, ipw_bg_link_up); - INIT_WORK(&priv->link_down, ipw_bg_link_down); - INIT_DELAYED_WORK(&priv->led_link_on, ipw_bg_led_link_on); - INIT_DELAYED_WORK(&priv->led_link_off, ipw_bg_led_link_off); - INIT_DELAYED_WORK(&priv->led_act_off, ipw_bg_led_activity_off); - INIT_WORK(&priv->merge_networks, ipw_merge_adhoc_network); - -#ifdef CONFIG_IPW2200_QOS - INIT_WORK(&priv->qos_activate, ipw_bg_qos_activate); -#endif /* CONFIG_IPW2200_QOS */ - - tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) - ipw_irq_tasklet, (unsigned long)priv); - - return ret; -} - -static void shim__set_security(struct net_device *dev, - struct libipw_security *sec) -{ - struct ipw_priv *priv = libipw_priv(dev); - int i; - for (i = 0; i < 4; i++) { - if (sec->flags & (1 << i)) { - priv->ieee->sec.encode_alg[i] = sec->encode_alg[i]; - priv->ieee->sec.key_sizes[i] = sec->key_sizes[i]; - if (sec->key_sizes[i] == 0) - priv->ieee->sec.flags &= ~(1 << i); - else { - memcpy(priv->ieee->sec.keys[i], sec->keys[i], - sec->key_sizes[i]); - priv->ieee->sec.flags |= (1 << i); - } - priv->status |= STATUS_SECURITY_UPDATED; - } else if (sec->level != SEC_LEVEL_1) - priv->ieee->sec.flags &= ~(1 << i); - } - - if (sec->flags & SEC_ACTIVE_KEY) { - if (sec->active_key <= 3) { - priv->ieee->sec.active_key = sec->active_key; - priv->ieee->sec.flags |= SEC_ACTIVE_KEY; - } else - priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY; - priv->status |= STATUS_SECURITY_UPDATED; - } else - priv->ieee->sec.flags &= ~SEC_ACTIVE_KEY; - - if ((sec->flags & SEC_AUTH_MODE) && - (priv->ieee->sec.auth_mode != sec->auth_mode)) { - priv->ieee->sec.auth_mode = sec->auth_mode; - priv->ieee->sec.flags |= SEC_AUTH_MODE; - if (sec->auth_mode == WLAN_AUTH_SHARED_KEY) - priv->capability |= CAP_SHARED_KEY; - else - priv->capability &= ~CAP_SHARED_KEY; - priv->status |= STATUS_SECURITY_UPDATED; - } - - if (sec->flags & SEC_ENABLED && priv->ieee->sec.enabled != sec->enabled) { - priv->ieee->sec.flags |= SEC_ENABLED; - priv->ieee->sec.enabled = sec->enabled; - priv->status |= STATUS_SECURITY_UPDATED; - if (sec->enabled) - priv->capability |= CAP_PRIVACY_ON; - else - priv->capability &= ~CAP_PRIVACY_ON; - } - - if (sec->flags & SEC_ENCRYPT) - priv->ieee->sec.encrypt = sec->encrypt; - - if (sec->flags & SEC_LEVEL && priv->ieee->sec.level != sec->level) { - priv->ieee->sec.level = sec->level; - priv->ieee->sec.flags |= SEC_LEVEL; - priv->status |= STATUS_SECURITY_UPDATED; - } - - if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT)) - ipw_set_hwcrypto_keys(priv); - - /* To match current functionality of ipw2100 (which works well w/ - * various supplicants, we don't force a disassociate if the - * privacy capability changes ... */ -#if 0 - if ((priv->status & (STATUS_ASSOCIATED | STATUS_ASSOCIATING)) && - (((priv->assoc_request.capability & - cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && !sec->enabled) || - (!(priv->assoc_request.capability & - cpu_to_le16(WLAN_CAPABILITY_PRIVACY)) && sec->enabled))) { - IPW_DEBUG_ASSOC("Disassociating due to capability " - "change.\n"); - ipw_disassociate(priv); - } -#endif -} - -static int init_supported_rates(struct ipw_priv *priv, - struct ipw_supported_rates *rates) -{ - /* TODO: Mask out rates based on priv->rates_mask */ - - memset(rates, 0, sizeof(*rates)); - /* configure supported rates */ - switch (priv->ieee->freq_band) { - case LIBIPW_52GHZ_BAND: - rates->ieee_mode = IPW_A_MODE; - rates->purpose = IPW_RATE_CAPABILITIES; - ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION, - LIBIPW_OFDM_DEFAULT_RATES_MASK); - break; - - default: /* Mixed or 2.4Ghz */ - rates->ieee_mode = IPW_G_MODE; - rates->purpose = IPW_RATE_CAPABILITIES; - ipw_add_cck_scan_rates(rates, LIBIPW_CCK_MODULATION, - LIBIPW_CCK_DEFAULT_RATES_MASK); - if (priv->ieee->modulation & LIBIPW_OFDM_MODULATION) { - ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION, - LIBIPW_OFDM_DEFAULT_RATES_MASK); - } - break; - } - - return 0; -} - -static int ipw_config(struct ipw_priv *priv) -{ - /* This is only called from ipw_up, which resets/reloads the firmware - so, we don't need to first disable the card before we configure - it */ - if (ipw_set_tx_power(priv)) - goto error; - - /* initialize adapter address */ - if (ipw_send_adapter_address(priv, priv->net_dev->dev_addr)) - goto error; - - /* set basic system config settings */ - init_sys_config(&priv->sys_config); - - /* Support Bluetooth if we have BT h/w on board, and user wants to. - * Does not support BT priority yet (don't abort or defer our Tx) */ - if (bt_coexist) { - unsigned char bt_caps = priv->eeprom[EEPROM_SKU_CAPABILITY]; - - if (bt_caps & EEPROM_SKU_CAP_BT_CHANNEL_SIG) - priv->sys_config.bt_coexistence - |= CFG_BT_COEXISTENCE_SIGNAL_CHNL; - if (bt_caps & EEPROM_SKU_CAP_BT_OOB) - priv->sys_config.bt_coexistence - |= CFG_BT_COEXISTENCE_OOB; - } - -#ifdef CONFIG_IPW2200_PROMISCUOUS - if (priv->prom_net_dev && netif_running(priv->prom_net_dev)) { - priv->sys_config.accept_all_data_frames = 1; - priv->sys_config.accept_non_directed_frames = 1; - priv->sys_config.accept_all_mgmt_bcpr = 1; - priv->sys_config.accept_all_mgmt_frames = 1; - } -#endif - - if (priv->ieee->iw_mode == IW_MODE_ADHOC) - priv->sys_config.answer_broadcast_ssid_probe = 1; - else - priv->sys_config.answer_broadcast_ssid_probe = 0; - - if (ipw_send_system_config(priv)) - goto error; - - init_supported_rates(priv, &priv->rates); - if (ipw_send_supported_rates(priv, &priv->rates)) - goto error; - - /* Set request-to-send threshold */ - if (priv->rts_threshold) { - if (ipw_send_rts_threshold(priv, priv->rts_threshold)) - goto error; - } -#ifdef CONFIG_IPW2200_QOS - IPW_DEBUG_QOS("QoS: call ipw_qos_activate\n"); - ipw_qos_activate(priv, NULL); -#endif /* CONFIG_IPW2200_QOS */ - - if (ipw_set_random_seed(priv)) - goto error; - - /* final state transition to the RUN state */ - if (ipw_send_host_complete(priv)) - goto error; - - priv->status |= STATUS_INIT; - - ipw_led_init(priv); - ipw_led_radio_on(priv); - priv->notif_missed_beacons = 0; - - /* Set hardware WEP key if it is configured. */ - if ((priv->capability & CAP_PRIVACY_ON) && - (priv->ieee->sec.level == SEC_LEVEL_1) && - !(priv->ieee->host_encrypt || priv->ieee->host_decrypt)) - ipw_set_hwcrypto_keys(priv); - - return 0; - - error: - return -EIO; -} - -/* - * NOTE: - * - * These tables have been tested in conjunction with the - * Intel PRO/Wireless 2200BG and 2915ABG Network Connection Adapters. - * - * Altering this values, using it on other hardware, or in geographies - * not intended for resale of the above mentioned Intel adapters has - * not been tested. - * - * Remember to update the table in README.ipw2200 when changing this - * table. - * - */ -static const struct libipw_geo ipw_geos[] = { - { /* Restricted */ - "---", - .bg_channels = 11, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}}, - }, - - { /* Custom US/Canada */ - "ZZF", - .bg_channels = 11, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}}, - .a_channels = 8, - .a = {{5180, 36}, - {5200, 40}, - {5220, 44}, - {5240, 48}, - {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, - {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, - {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, - {5320, 64, LIBIPW_CH_PASSIVE_ONLY}}, - }, - - { /* Rest of World */ - "ZZD", - .bg_channels = 13, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}, {2467, 12}, - {2472, 13}}, - }, - - { /* Custom USA & Europe & High */ - "ZZA", - .bg_channels = 11, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}}, - .a_channels = 13, - .a = {{5180, 36}, - {5200, 40}, - {5220, 44}, - {5240, 48}, - {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, - {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, - {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, - {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, - {5745, 149}, - {5765, 153}, - {5785, 157}, - {5805, 161}, - {5825, 165}}, - }, - - { /* Custom NA & Europe */ - "ZZB", - .bg_channels = 11, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}}, - .a_channels = 13, - .a = {{5180, 36}, - {5200, 40}, - {5220, 44}, - {5240, 48}, - {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, - {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, - {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, - {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, - {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, - {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, - {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, - {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, - {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, - }, - - { /* Custom Japan */ - "ZZC", - .bg_channels = 11, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}}, - .a_channels = 4, - .a = {{5170, 34}, {5190, 38}, - {5210, 42}, {5230, 46}}, - }, - - { /* Custom */ - "ZZM", - .bg_channels = 11, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}}, - }, - - { /* Europe */ - "ZZE", - .bg_channels = 13, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}, {2467, 12}, - {2472, 13}}, - .a_channels = 19, - .a = {{5180, 36}, - {5200, 40}, - {5220, 44}, - {5240, 48}, - {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, - {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, - {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, - {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, - {5500, 100, LIBIPW_CH_PASSIVE_ONLY}, - {5520, 104, LIBIPW_CH_PASSIVE_ONLY}, - {5540, 108, LIBIPW_CH_PASSIVE_ONLY}, - {5560, 112, LIBIPW_CH_PASSIVE_ONLY}, - {5580, 116, LIBIPW_CH_PASSIVE_ONLY}, - {5600, 120, LIBIPW_CH_PASSIVE_ONLY}, - {5620, 124, LIBIPW_CH_PASSIVE_ONLY}, - {5640, 128, LIBIPW_CH_PASSIVE_ONLY}, - {5660, 132, LIBIPW_CH_PASSIVE_ONLY}, - {5680, 136, LIBIPW_CH_PASSIVE_ONLY}, - {5700, 140, LIBIPW_CH_PASSIVE_ONLY}}, - }, - - { /* Custom Japan */ - "ZZJ", - .bg_channels = 14, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}, {2467, 12}, - {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY}}, - .a_channels = 4, - .a = {{5170, 34}, {5190, 38}, - {5210, 42}, {5230, 46}}, - }, - - { /* Rest of World */ - "ZZR", - .bg_channels = 14, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}, {2467, 12}, - {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY | - LIBIPW_CH_PASSIVE_ONLY}}, - }, - - { /* High Band */ - "ZZH", - .bg_channels = 13, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}, - {2467, 12, LIBIPW_CH_PASSIVE_ONLY}, - {2472, 13, LIBIPW_CH_PASSIVE_ONLY}}, - .a_channels = 4, - .a = {{5745, 149}, {5765, 153}, - {5785, 157}, {5805, 161}}, - }, - - { /* Custom Europe */ - "ZZG", - .bg_channels = 13, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}, - {2467, 12}, {2472, 13}}, - .a_channels = 4, - .a = {{5180, 36}, {5200, 40}, - {5220, 44}, {5240, 48}}, - }, - - { /* Europe */ - "ZZK", - .bg_channels = 13, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}, - {2467, 12, LIBIPW_CH_PASSIVE_ONLY}, - {2472, 13, LIBIPW_CH_PASSIVE_ONLY}}, - .a_channels = 24, - .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY}, - {5200, 40, LIBIPW_CH_PASSIVE_ONLY}, - {5220, 44, LIBIPW_CH_PASSIVE_ONLY}, - {5240, 48, LIBIPW_CH_PASSIVE_ONLY}, - {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, - {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, - {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, - {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, - {5500, 100, LIBIPW_CH_PASSIVE_ONLY}, - {5520, 104, LIBIPW_CH_PASSIVE_ONLY}, - {5540, 108, LIBIPW_CH_PASSIVE_ONLY}, - {5560, 112, LIBIPW_CH_PASSIVE_ONLY}, - {5580, 116, LIBIPW_CH_PASSIVE_ONLY}, - {5600, 120, LIBIPW_CH_PASSIVE_ONLY}, - {5620, 124, LIBIPW_CH_PASSIVE_ONLY}, - {5640, 128, LIBIPW_CH_PASSIVE_ONLY}, - {5660, 132, LIBIPW_CH_PASSIVE_ONLY}, - {5680, 136, LIBIPW_CH_PASSIVE_ONLY}, - {5700, 140, LIBIPW_CH_PASSIVE_ONLY}, - {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, - {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, - {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, - {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, - {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, - }, - - { /* Europe */ - "ZZL", - .bg_channels = 11, - .bg = {{2412, 1}, {2417, 2}, {2422, 3}, - {2427, 4}, {2432, 5}, {2437, 6}, - {2442, 7}, {2447, 8}, {2452, 9}, - {2457, 10}, {2462, 11}}, - .a_channels = 13, - .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY}, - {5200, 40, LIBIPW_CH_PASSIVE_ONLY}, - {5220, 44, LIBIPW_CH_PASSIVE_ONLY}, - {5240, 48, LIBIPW_CH_PASSIVE_ONLY}, - {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, - {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, - {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, - {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, - {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, - {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, - {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, - {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, - {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, - } -}; - -static void ipw_set_geo(struct ipw_priv *priv) -{ - int j; - - for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) { - if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE], - ipw_geos[j].name, 3)) - break; - } - - if (j == ARRAY_SIZE(ipw_geos)) { - IPW_WARNING("SKU [%c%c%c] not recognized.\n", - priv->eeprom[EEPROM_COUNTRY_CODE + 0], - priv->eeprom[EEPROM_COUNTRY_CODE + 1], - priv->eeprom[EEPROM_COUNTRY_CODE + 2]); - j = 0; - } - - libipw_set_geo(priv->ieee, &ipw_geos[j]); -} - -#define MAX_HW_RESTARTS 5 -static int ipw_up(struct ipw_priv *priv) -{ - int rc, i; - - /* Age scan list entries found before suspend */ - if (priv->suspend_time) { - libipw_networks_age(priv->ieee, priv->suspend_time); - priv->suspend_time = 0; - } - - if (priv->status & STATUS_EXIT_PENDING) - return -EIO; - - if (cmdlog && !priv->cmdlog) { - priv->cmdlog = kcalloc(cmdlog, sizeof(*priv->cmdlog), - GFP_KERNEL); - if (priv->cmdlog == NULL) { - IPW_ERROR("Error allocating %d command log entries.\n", - cmdlog); - return -ENOMEM; - } else { - priv->cmdlog_len = cmdlog; - } - } - - for (i = 0; i < MAX_HW_RESTARTS; i++) { - /* Load the microcode, firmware, and eeprom. - * Also start the clocks. */ - rc = ipw_load(priv); - if (rc) { - IPW_ERROR("Unable to load firmware: %d\n", rc); - return rc; - } - - ipw_init_ordinals(priv); - if (!(priv->config & CFG_CUSTOM_MAC)) - eeprom_parse_mac(priv, priv->mac_addr); - memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN); - - ipw_set_geo(priv); - - if (priv->status & STATUS_RF_KILL_SW) { - IPW_WARNING("Radio disabled by module parameter.\n"); - return 0; - } else if (rf_kill_active(priv)) { - IPW_WARNING("Radio Frequency Kill Switch is On:\n" - "Kill switch must be turned off for " - "wireless networking to work.\n"); - schedule_delayed_work(&priv->rf_kill, 2 * HZ); - return 0; - } - - rc = ipw_config(priv); - if (!rc) { - IPW_DEBUG_INFO("Configured device on count %i\n", i); - - /* If configure to try and auto-associate, kick - * off a scan. */ - schedule_delayed_work(&priv->request_scan, 0); - - return 0; - } - - IPW_DEBUG_INFO("Device configuration failed: 0x%08X\n", rc); - IPW_DEBUG_INFO("Failed to config device on retry %d of %d\n", - i, MAX_HW_RESTARTS); - - /* We had an error bringing up the hardware, so take it - * all the way back down so we can try again */ - ipw_down(priv); - } - - /* tried to restart and config the device for as long as our - * patience could withstand */ - IPW_ERROR("Unable to initialize device after %d attempts.\n", i); - - return -EIO; -} - -static void ipw_bg_up(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, up); - mutex_lock(&priv->mutex); - ipw_up(priv); - mutex_unlock(&priv->mutex); -} - -static void ipw_deinit(struct ipw_priv *priv) -{ - int i; - - if (priv->status & STATUS_SCANNING) { - IPW_DEBUG_INFO("Aborting scan during shutdown.\n"); - ipw_abort_scan(priv); - } - - if (priv->status & STATUS_ASSOCIATED) { - IPW_DEBUG_INFO("Disassociating during shutdown.\n"); - ipw_disassociate(priv); - } - - ipw_led_shutdown(priv); - - /* Wait up to 1s for status to change to not scanning and not - * associated (disassociation can take a while for a ful 802.11 - * exchange */ - for (i = 1000; i && (priv->status & - (STATUS_DISASSOCIATING | - STATUS_ASSOCIATED | STATUS_SCANNING)); i--) - udelay(10); - - if (priv->status & (STATUS_DISASSOCIATING | - STATUS_ASSOCIATED | STATUS_SCANNING)) - IPW_DEBUG_INFO("Still associated or scanning...\n"); - else - IPW_DEBUG_INFO("Took %dms to de-init\n", 1000 - i); - - /* Attempt to disable the card */ - ipw_send_card_disable(priv, 0); - - priv->status &= ~STATUS_INIT; -} - -static void ipw_down(struct ipw_priv *priv) -{ - int exit_pending = priv->status & STATUS_EXIT_PENDING; - - priv->status |= STATUS_EXIT_PENDING; - - if (ipw_is_init(priv)) - ipw_deinit(priv); - - /* Wipe out the EXIT_PENDING status bit if we are not actually - * exiting the module */ - if (!exit_pending) - priv->status &= ~STATUS_EXIT_PENDING; - - /* tell the device to stop sending interrupts */ - ipw_disable_interrupts(priv); - - /* Clear all bits but the RF Kill */ - priv->status &= STATUS_RF_KILL_MASK | STATUS_EXIT_PENDING; - netif_carrier_off(priv->net_dev); - - ipw_stop_nic(priv); - - ipw_led_radio_off(priv); -} - -static void ipw_bg_down(struct work_struct *work) -{ - struct ipw_priv *priv = - container_of(work, struct ipw_priv, down); - mutex_lock(&priv->mutex); - ipw_down(priv); - mutex_unlock(&priv->mutex); -} - -static int ipw_wdev_init(struct net_device *dev) -{ - int i, rc = 0; - struct ipw_priv *priv = libipw_priv(dev); - const struct libipw_geo *geo = libipw_get_geo(priv->ieee); - struct wireless_dev *wdev = &priv->ieee->wdev; - - memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); - - /* fill-out priv->ieee->bg_band */ - if (geo->bg_channels) { - struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; - - bg_band->band = IEEE80211_BAND_2GHZ; - bg_band->n_channels = geo->bg_channels; - bg_band->channels = kcalloc(geo->bg_channels, - sizeof(struct ieee80211_channel), - GFP_KERNEL); - if (!bg_band->channels) { - rc = -ENOMEM; - goto out; - } - /* translate geo->bg to bg_band.channels */ - for (i = 0; i < geo->bg_channels; i++) { - bg_band->channels[i].band = IEEE80211_BAND_2GHZ; - bg_band->channels[i].center_freq = geo->bg[i].freq; - bg_band->channels[i].hw_value = geo->bg[i].channel; - bg_band->channels[i].max_power = geo->bg[i].max_power; - if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) - bg_band->channels[i].flags |= - IEEE80211_CHAN_NO_IR; - if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) - bg_band->channels[i].flags |= - IEEE80211_CHAN_NO_IR; - if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) - bg_band->channels[i].flags |= - IEEE80211_CHAN_RADAR; - /* No equivalent for LIBIPW_CH_80211H_RULES, - LIBIPW_CH_UNIFORM_SPREADING, or - LIBIPW_CH_B_ONLY... */ - } - /* point at bitrate info */ - bg_band->bitrates = ipw2200_bg_rates; - bg_band->n_bitrates = ipw2200_num_bg_rates; - - wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; - } - - /* fill-out priv->ieee->a_band */ - if (geo->a_channels) { - struct ieee80211_supported_band *a_band = &priv->ieee->a_band; - - a_band->band = IEEE80211_BAND_5GHZ; - a_band->n_channels = geo->a_channels; - a_band->channels = kcalloc(geo->a_channels, - sizeof(struct ieee80211_channel), - GFP_KERNEL); - if (!a_band->channels) { - rc = -ENOMEM; - goto out; - } - /* translate geo->a to a_band.channels */ - for (i = 0; i < geo->a_channels; i++) { - a_band->channels[i].band = IEEE80211_BAND_5GHZ; - a_band->channels[i].center_freq = geo->a[i].freq; - a_band->channels[i].hw_value = geo->a[i].channel; - a_band->channels[i].max_power = geo->a[i].max_power; - if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) - a_band->channels[i].flags |= - IEEE80211_CHAN_NO_IR; - if (geo->a[i].flags & LIBIPW_CH_NO_IBSS) - a_band->channels[i].flags |= - IEEE80211_CHAN_NO_IR; - if (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT) - a_band->channels[i].flags |= - IEEE80211_CHAN_RADAR; - /* No equivalent for LIBIPW_CH_80211H_RULES, - LIBIPW_CH_UNIFORM_SPREADING, or - LIBIPW_CH_B_ONLY... */ - } - /* point at bitrate info */ - a_band->bitrates = ipw2200_a_rates; - a_band->n_bitrates = ipw2200_num_a_rates; - - wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = a_band; - } - - wdev->wiphy->cipher_suites = ipw_cipher_suites; - wdev->wiphy->n_cipher_suites = ARRAY_SIZE(ipw_cipher_suites); - - set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); - - /* With that information in place, we can now register the wiphy... */ - if (wiphy_register(wdev->wiphy)) - rc = -EIO; -out: - return rc; -} - -/* PCI driver stuff */ -static const struct pci_device_id card_ids[] = { - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2701, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2702, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2711, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2712, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2721, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2722, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2731, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2732, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2741, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x103c, 0x2741, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2742, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2751, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2752, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2753, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0}, - {PCI_VDEVICE(INTEL, 0x104f), 0}, - {PCI_VDEVICE(INTEL, 0x4220), 0}, /* BG */ - {PCI_VDEVICE(INTEL, 0x4221), 0}, /* BG */ - {PCI_VDEVICE(INTEL, 0x4223), 0}, /* ABG */ - {PCI_VDEVICE(INTEL, 0x4224), 0}, /* ABG */ - - /* required last entry */ - {0,} -}; - -MODULE_DEVICE_TABLE(pci, card_ids); - -static struct attribute *ipw_sysfs_entries[] = { - &dev_attr_rf_kill.attr, - &dev_attr_direct_dword.attr, - &dev_attr_indirect_byte.attr, - &dev_attr_indirect_dword.attr, - &dev_attr_mem_gpio_reg.attr, - &dev_attr_command_event_reg.attr, - &dev_attr_nic_type.attr, - &dev_attr_status.attr, - &dev_attr_cfg.attr, - &dev_attr_error.attr, - &dev_attr_event_log.attr, - &dev_attr_cmd_log.attr, - &dev_attr_eeprom_delay.attr, - &dev_attr_ucode_version.attr, - &dev_attr_rtc.attr, - &dev_attr_scan_age.attr, - &dev_attr_led.attr, - &dev_attr_speed_scan.attr, - &dev_attr_net_stats.attr, - &dev_attr_channels.attr, -#ifdef CONFIG_IPW2200_PROMISCUOUS - &dev_attr_rtap_iface.attr, - &dev_attr_rtap_filter.attr, -#endif - NULL -}; - -static struct attribute_group ipw_attribute_group = { - .name = NULL, /* put in device directory */ - .attrs = ipw_sysfs_entries, -}; - -#ifdef CONFIG_IPW2200_PROMISCUOUS -static int ipw_prom_open(struct net_device *dev) -{ - struct ipw_prom_priv *prom_priv = libipw_priv(dev); - struct ipw_priv *priv = prom_priv->priv; - - IPW_DEBUG_INFO("prom dev->open\n"); - netif_carrier_off(dev); - - if (priv->ieee->iw_mode != IW_MODE_MONITOR) { - priv->sys_config.accept_all_data_frames = 1; - priv->sys_config.accept_non_directed_frames = 1; - priv->sys_config.accept_all_mgmt_bcpr = 1; - priv->sys_config.accept_all_mgmt_frames = 1; - - ipw_send_system_config(priv); - } - - return 0; -} - -static int ipw_prom_stop(struct net_device *dev) -{ - struct ipw_prom_priv *prom_priv = libipw_priv(dev); - struct ipw_priv *priv = prom_priv->priv; - - IPW_DEBUG_INFO("prom dev->stop\n"); - - if (priv->ieee->iw_mode != IW_MODE_MONITOR) { - priv->sys_config.accept_all_data_frames = 0; - priv->sys_config.accept_non_directed_frames = 0; - priv->sys_config.accept_all_mgmt_bcpr = 0; - priv->sys_config.accept_all_mgmt_frames = 0; - - ipw_send_system_config(priv); - } - - return 0; -} - -static netdev_tx_t ipw_prom_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - IPW_DEBUG_INFO("prom dev->xmit\n"); - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -static const struct net_device_ops ipw_prom_netdev_ops = { - .ndo_open = ipw_prom_open, - .ndo_stop = ipw_prom_stop, - .ndo_start_xmit = ipw_prom_hard_start_xmit, - .ndo_change_mtu = libipw_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int ipw_prom_alloc(struct ipw_priv *priv) -{ - int rc = 0; - - if (priv->prom_net_dev) - return -EPERM; - - priv->prom_net_dev = alloc_libipw(sizeof(struct ipw_prom_priv), 1); - if (priv->prom_net_dev == NULL) - return -ENOMEM; - - priv->prom_priv = libipw_priv(priv->prom_net_dev); - priv->prom_priv->ieee = netdev_priv(priv->prom_net_dev); - priv->prom_priv->priv = priv; - - strcpy(priv->prom_net_dev->name, "rtap%d"); - memcpy(priv->prom_net_dev->dev_addr, priv->mac_addr, ETH_ALEN); - - priv->prom_net_dev->type = ARPHRD_IEEE80211_RADIOTAP; - priv->prom_net_dev->netdev_ops = &ipw_prom_netdev_ops; - - priv->prom_priv->ieee->iw_mode = IW_MODE_MONITOR; - SET_NETDEV_DEV(priv->prom_net_dev, &priv->pci_dev->dev); - - rc = register_netdev(priv->prom_net_dev); - if (rc) { - free_libipw(priv->prom_net_dev, 1); - priv->prom_net_dev = NULL; - return rc; - } - - return 0; -} - -static void ipw_prom_free(struct ipw_priv *priv) -{ - if (!priv->prom_net_dev) - return; - - unregister_netdev(priv->prom_net_dev); - free_libipw(priv->prom_net_dev, 1); - - priv->prom_net_dev = NULL; -} - -#endif - -static const struct net_device_ops ipw_netdev_ops = { - .ndo_open = ipw_net_open, - .ndo_stop = ipw_net_stop, - .ndo_set_rx_mode = ipw_net_set_multicast_list, - .ndo_set_mac_address = ipw_net_set_mac_address, - .ndo_start_xmit = libipw_xmit, - .ndo_change_mtu = libipw_change_mtu, - .ndo_validate_addr = eth_validate_addr, -}; - -static int ipw_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int err = 0; - struct net_device *net_dev; - void __iomem *base; - u32 length, val; - struct ipw_priv *priv; - int i; - - net_dev = alloc_libipw(sizeof(struct ipw_priv), 0); - if (net_dev == NULL) { - err = -ENOMEM; - goto out; - } - - priv = libipw_priv(net_dev); - priv->ieee = netdev_priv(net_dev); - - priv->net_dev = net_dev; - priv->pci_dev = pdev; - ipw_debug_level = debug; - spin_lock_init(&priv->irq_lock); - spin_lock_init(&priv->lock); - for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) - INIT_LIST_HEAD(&priv->ibss_mac_hash[i]); - - mutex_init(&priv->mutex); - if (pci_enable_device(pdev)) { - err = -ENODEV; - goto out_free_libipw; - } - - pci_set_master(pdev); - - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (!err) - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (err) { - printk(KERN_WARNING DRV_NAME ": No suitable DMA available.\n"); - goto out_pci_disable_device; - } - - pci_set_drvdata(pdev, priv); - - err = pci_request_regions(pdev, DRV_NAME); - if (err) - goto out_pci_disable_device; - - /* We disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state */ - pci_read_config_dword(pdev, 0x40, &val); - if ((val & 0x0000ff00) != 0) - pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); - - length = pci_resource_len(pdev, 0); - priv->hw_len = length; - - base = pci_ioremap_bar(pdev, 0); - if (!base) { - err = -ENODEV; - goto out_pci_release_regions; - } - - priv->hw_base = base; - IPW_DEBUG_INFO("pci_resource_len = 0x%08x\n", length); - IPW_DEBUG_INFO("pci_resource_base = %p\n", base); - - err = ipw_setup_deferred_work(priv); - if (err) { - IPW_ERROR("Unable to setup deferred work\n"); - goto out_iounmap; - } - - ipw_sw_reset(priv, 1); - - err = request_irq(pdev->irq, ipw_isr, IRQF_SHARED, DRV_NAME, priv); - if (err) { - IPW_ERROR("Error allocating IRQ %d\n", pdev->irq); - goto out_iounmap; - } - - SET_NETDEV_DEV(net_dev, &pdev->dev); - - mutex_lock(&priv->mutex); - - priv->ieee->hard_start_xmit = ipw_net_hard_start_xmit; - priv->ieee->set_security = shim__set_security; - priv->ieee->is_queue_full = ipw_net_is_queue_full; - -#ifdef CONFIG_IPW2200_QOS - priv->ieee->is_qos_active = ipw_is_qos_active; - priv->ieee->handle_probe_response = ipw_handle_beacon; - priv->ieee->handle_beacon = ipw_handle_probe_response; - priv->ieee->handle_assoc_response = ipw_handle_assoc_response; -#endif /* CONFIG_IPW2200_QOS */ - - priv->ieee->perfect_rssi = -20; - priv->ieee->worst_rssi = -85; - - net_dev->netdev_ops = &ipw_netdev_ops; - priv->wireless_data.spy_data = &priv->ieee->spy_data; - net_dev->wireless_data = &priv->wireless_data; - net_dev->wireless_handlers = &ipw_wx_handler_def; - net_dev->ethtool_ops = &ipw_ethtool_ops; - - err = sysfs_create_group(&pdev->dev.kobj, &ipw_attribute_group); - if (err) { - IPW_ERROR("failed to create sysfs device attributes\n"); - mutex_unlock(&priv->mutex); - goto out_release_irq; - } - - if (ipw_up(priv)) { - mutex_unlock(&priv->mutex); - err = -EIO; - goto out_remove_sysfs; - } - - mutex_unlock(&priv->mutex); - - err = ipw_wdev_init(net_dev); - if (err) { - IPW_ERROR("failed to register wireless device\n"); - goto out_remove_sysfs; - } - - err = register_netdev(net_dev); - if (err) { - IPW_ERROR("failed to register network device\n"); - goto out_unregister_wiphy; - } - -#ifdef CONFIG_IPW2200_PROMISCUOUS - if (rtap_iface) { - err = ipw_prom_alloc(priv); - if (err) { - IPW_ERROR("Failed to register promiscuous network " - "device (error %d).\n", err); - unregister_netdev(priv->net_dev); - goto out_unregister_wiphy; - } - } -#endif - - printk(KERN_INFO DRV_NAME ": Detected geography %s (%d 802.11bg " - "channels, %d 802.11a channels)\n", - priv->ieee->geo.name, priv->ieee->geo.bg_channels, - priv->ieee->geo.a_channels); - - return 0; - - out_unregister_wiphy: - wiphy_unregister(priv->ieee->wdev.wiphy); - kfree(priv->ieee->a_band.channels); - kfree(priv->ieee->bg_band.channels); - out_remove_sysfs: - sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group); - out_release_irq: - free_irq(pdev->irq, priv); - out_iounmap: - iounmap(priv->hw_base); - out_pci_release_regions: - pci_release_regions(pdev); - out_pci_disable_device: - pci_disable_device(pdev); - out_free_libipw: - free_libipw(priv->net_dev, 0); - out: - return err; -} - -static void ipw_pci_remove(struct pci_dev *pdev) -{ - struct ipw_priv *priv = pci_get_drvdata(pdev); - struct list_head *p, *q; - int i; - - if (!priv) - return; - - mutex_lock(&priv->mutex); - - priv->status |= STATUS_EXIT_PENDING; - ipw_down(priv); - sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group); - - mutex_unlock(&priv->mutex); - - unregister_netdev(priv->net_dev); - - if (priv->rxq) { - ipw_rx_queue_free(priv, priv->rxq); - priv->rxq = NULL; - } - ipw_tx_queue_free(priv); - - if (priv->cmdlog) { - kfree(priv->cmdlog); - priv->cmdlog = NULL; - } - - /* make sure all works are inactive */ - cancel_delayed_work_sync(&priv->adhoc_check); - cancel_work_sync(&priv->associate); - cancel_work_sync(&priv->disassociate); - cancel_work_sync(&priv->system_config); - cancel_work_sync(&priv->rx_replenish); - cancel_work_sync(&priv->adapter_restart); - cancel_delayed_work_sync(&priv->rf_kill); - cancel_work_sync(&priv->up); - cancel_work_sync(&priv->down); - cancel_delayed_work_sync(&priv->request_scan); - cancel_delayed_work_sync(&priv->request_direct_scan); - cancel_delayed_work_sync(&priv->request_passive_scan); - cancel_delayed_work_sync(&priv->scan_event); - cancel_delayed_work_sync(&priv->gather_stats); - cancel_work_sync(&priv->abort_scan); - cancel_work_sync(&priv->roam); - cancel_delayed_work_sync(&priv->scan_check); - cancel_work_sync(&priv->link_up); - cancel_work_sync(&priv->link_down); - cancel_delayed_work_sync(&priv->led_link_on); - cancel_delayed_work_sync(&priv->led_link_off); - cancel_delayed_work_sync(&priv->led_act_off); - cancel_work_sync(&priv->merge_networks); - - /* Free MAC hash list for ADHOC */ - for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) { - list_for_each_safe(p, q, &priv->ibss_mac_hash[i]) { - list_del(p); - kfree(list_entry(p, struct ipw_ibss_seq, list)); - } - } - - kfree(priv->error); - priv->error = NULL; - -#ifdef CONFIG_IPW2200_PROMISCUOUS - ipw_prom_free(priv); -#endif - - free_irq(pdev->irq, priv); - iounmap(priv->hw_base); - pci_release_regions(pdev); - pci_disable_device(pdev); - /* wiphy_unregister needs to be here, before free_libipw */ - wiphy_unregister(priv->ieee->wdev.wiphy); - kfree(priv->ieee->a_band.channels); - kfree(priv->ieee->bg_band.channels); - free_libipw(priv->net_dev, 0); - free_firmware(); -} - -#ifdef CONFIG_PM -static int ipw_pci_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct ipw_priv *priv = pci_get_drvdata(pdev); - struct net_device *dev = priv->net_dev; - - printk(KERN_INFO "%s: Going into suspend...\n", dev->name); - - /* Take down the device; powers it off, etc. */ - ipw_down(priv); - - /* Remove the PRESENT state of the device */ - netif_device_detach(dev); - - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - - priv->suspend_at = get_seconds(); - - return 0; -} - -static int ipw_pci_resume(struct pci_dev *pdev) -{ - struct ipw_priv *priv = pci_get_drvdata(pdev); - struct net_device *dev = priv->net_dev; - int err; - u32 val; - - printk(KERN_INFO "%s: Coming out of suspend...\n", dev->name); - - pci_set_power_state(pdev, PCI_D0); - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR "%s: pci_enable_device failed on resume\n", - dev->name); - return err; - } - pci_restore_state(pdev); - - /* - * Suspend/Resume resets the PCI configuration space, so we have to - * re-disable the RETRY_TIMEOUT register (0x41) to keep PCI Tx retries - * from interfering with C3 CPU state. pci_restore_state won't help - * here since it only restores the first 64 bytes pci config header. - */ - pci_read_config_dword(pdev, 0x40, &val); - if ((val & 0x0000ff00) != 0) - pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); - - /* Set the device back into the PRESENT state; this will also wake - * the queue of needed */ - netif_device_attach(dev); - - priv->suspend_time = get_seconds() - priv->suspend_at; - - /* Bring the device back up */ - schedule_work(&priv->up); - - return 0; -} -#endif - -static void ipw_pci_shutdown(struct pci_dev *pdev) -{ - struct ipw_priv *priv = pci_get_drvdata(pdev); - - /* Take down the device; powers it off, etc. */ - ipw_down(priv); - - pci_disable_device(pdev); -} - -/* driver initialization stuff */ -static struct pci_driver ipw_driver = { - .name = DRV_NAME, - .id_table = card_ids, - .probe = ipw_pci_probe, - .remove = ipw_pci_remove, -#ifdef CONFIG_PM - .suspend = ipw_pci_suspend, - .resume = ipw_pci_resume, -#endif - .shutdown = ipw_pci_shutdown, -}; - -static int __init ipw_init(void) -{ - int ret; - - printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n"); - printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n"); - - ret = pci_register_driver(&ipw_driver); - if (ret) { - IPW_ERROR("Unable to initialize PCI module\n"); - return ret; - } - - ret = driver_create_file(&ipw_driver.driver, &driver_attr_debug_level); - if (ret) { - IPW_ERROR("Unable to create driver sysfs file\n"); - pci_unregister_driver(&ipw_driver); - return ret; - } - - return ret; -} - -static void __exit ipw_exit(void) -{ - driver_remove_file(&ipw_driver.driver, &driver_attr_debug_level); - pci_unregister_driver(&ipw_driver); -} - -module_param(disable, int, 0444); -MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])"); - -module_param(associate, int, 0444); -MODULE_PARM_DESC(associate, "auto associate when scanning (default off)"); - -module_param(auto_create, int, 0444); -MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)"); - -module_param_named(led, led_support, int, 0444); -MODULE_PARM_DESC(led, "enable led control on some systems (default 1 on)"); - -module_param(debug, int, 0444); -MODULE_PARM_DESC(debug, "debug output mask"); - -module_param_named(channel, default_channel, int, 0444); -MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])"); - -#ifdef CONFIG_IPW2200_PROMISCUOUS -module_param(rtap_iface, int, 0444); -MODULE_PARM_DESC(rtap_iface, "create the rtap interface (1 - create, default 0)"); -#endif - -#ifdef CONFIG_IPW2200_QOS -module_param(qos_enable, int, 0444); -MODULE_PARM_DESC(qos_enable, "enable all QoS functionalitis"); - -module_param(qos_burst_enable, int, 0444); -MODULE_PARM_DESC(qos_burst_enable, "enable QoS burst mode"); - -module_param(qos_no_ack_mask, int, 0444); -MODULE_PARM_DESC(qos_no_ack_mask, "mask Tx_Queue to no ack"); - -module_param(burst_duration_CCK, int, 0444); -MODULE_PARM_DESC(burst_duration_CCK, "set CCK burst value"); - -module_param(burst_duration_OFDM, int, 0444); -MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value"); -#endif /* CONFIG_IPW2200_QOS */ - -#ifdef CONFIG_IPW2200_MONITOR -module_param_named(mode, network_mode, int, 0444); -MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)"); -#else -module_param_named(mode, network_mode, int, 0444); -MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)"); -#endif - -module_param(bt_coexist, int, 0444); -MODULE_PARM_DESC(bt_coexist, "enable bluetooth coexistence (default off)"); - -module_param(hwcrypto, int, 0444); -MODULE_PARM_DESC(hwcrypto, "enable hardware crypto (default off)"); - -module_param(cmdlog, int, 0444); -MODULE_PARM_DESC(cmdlog, - "allocate a ring buffer for logging firmware commands"); - -module_param(roaming, int, 0444); -MODULE_PARM_DESC(roaming, "enable roaming support (default on)"); - -module_param(antenna, int, 0444); -MODULE_PARM_DESC(antenna, "select antenna 1=Main, 3=Aux, default 0 [both], 2=slow_diversity (choose the one with lower background noise)"); - -module_exit(ipw_exit); -module_init(ipw_init); diff --git a/drivers/net/wireless/ipw2x00/ipw2200.h b/drivers/net/wireless/ipw2x00/ipw2200.h deleted file mode 100644 index aa301d1eee3c..000000000000 --- a/drivers/net/wireless/ipw2x00/ipw2200.h +++ /dev/null @@ -1,2001 +0,0 @@ -/****************************************************************************** - - Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved. - - This program is free software; you can redistribute it and/or modify it - under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - The full GNU General Public License is included in this distribution in the - file called LICENSE. - - Contact Information: - Intel Linux Wireless - Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - -******************************************************************************/ - -#ifndef __ipw2200_h__ -#define __ipw2200_h__ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#define DRV_NAME "ipw2200" - -#include - -#include "libipw.h" - -/* Authentication and Association States */ -enum connection_manager_assoc_states { - CMAS_INIT = 0, - CMAS_TX_AUTH_SEQ_1, - CMAS_RX_AUTH_SEQ_2, - CMAS_AUTH_SEQ_1_PASS, - CMAS_AUTH_SEQ_1_FAIL, - CMAS_TX_AUTH_SEQ_3, - CMAS_RX_AUTH_SEQ_4, - CMAS_AUTH_SEQ_2_PASS, - CMAS_AUTH_SEQ_2_FAIL, - CMAS_AUTHENTICATED, - CMAS_TX_ASSOC, - CMAS_RX_ASSOC_RESP, - CMAS_ASSOCIATED, - CMAS_LAST -}; - -#define IPW_WAIT (1<<0) -#define IPW_QUIET (1<<1) -#define IPW_ROAMING (1<<2) - -#define IPW_POWER_MODE_CAM 0x00 //(always on) -#define IPW_POWER_INDEX_1 0x01 -#define IPW_POWER_INDEX_2 0x02 -#define IPW_POWER_INDEX_3 0x03 -#define IPW_POWER_INDEX_4 0x04 -#define IPW_POWER_INDEX_5 0x05 -#define IPW_POWER_AC 0x06 -#define IPW_POWER_BATTERY 0x07 -#define IPW_POWER_LIMIT 0x07 -#define IPW_POWER_MASK 0x0F -#define IPW_POWER_ENABLED 0x10 -#define IPW_POWER_LEVEL(x) ((x) & IPW_POWER_MASK) - -#define IPW_CMD_HOST_COMPLETE 2 -#define IPW_CMD_POWER_DOWN 4 -#define IPW_CMD_SYSTEM_CONFIG 6 -#define IPW_CMD_MULTICAST_ADDRESS 7 -#define IPW_CMD_SSID 8 -#define IPW_CMD_ADAPTER_ADDRESS 11 -#define IPW_CMD_PORT_TYPE 12 -#define IPW_CMD_RTS_THRESHOLD 15 -#define IPW_CMD_FRAG_THRESHOLD 16 -#define IPW_CMD_POWER_MODE 17 -#define IPW_CMD_WEP_KEY 18 -#define IPW_CMD_TGI_TX_KEY 19 -#define IPW_CMD_SCAN_REQUEST 20 -#define IPW_CMD_ASSOCIATE 21 -#define IPW_CMD_SUPPORTED_RATES 22 -#define IPW_CMD_SCAN_ABORT 23 -#define IPW_CMD_TX_FLUSH 24 -#define IPW_CMD_QOS_PARAMETERS 25 -#define IPW_CMD_SCAN_REQUEST_EXT 26 -#define IPW_CMD_DINO_CONFIG 30 -#define IPW_CMD_RSN_CAPABILITIES 31 -#define IPW_CMD_RX_KEY 32 -#define IPW_CMD_CARD_DISABLE 33 -#define IPW_CMD_SEED_NUMBER 34 -#define IPW_CMD_TX_POWER 35 -#define IPW_CMD_COUNTRY_INFO 36 -#define IPW_CMD_AIRONET_INFO 37 -#define IPW_CMD_AP_TX_POWER 38 -#define IPW_CMD_CCKM_INFO 39 -#define IPW_CMD_CCX_VER_INFO 40 -#define IPW_CMD_SET_CALIBRATION 41 -#define IPW_CMD_SENSITIVITY_CALIB 42 -#define IPW_CMD_RETRY_LIMIT 51 -#define IPW_CMD_IPW_PRE_POWER_DOWN 58 -#define IPW_CMD_VAP_BEACON_TEMPLATE 60 -#define IPW_CMD_VAP_DTIM_PERIOD 61 -#define IPW_CMD_EXT_SUPPORTED_RATES 62 -#define IPW_CMD_VAP_LOCAL_TX_PWR_CONSTRAINT 63 -#define IPW_CMD_VAP_QUIET_INTERVALS 64 -#define IPW_CMD_VAP_CHANNEL_SWITCH 65 -#define IPW_CMD_VAP_MANDATORY_CHANNELS 66 -#define IPW_CMD_VAP_CELL_PWR_LIMIT 67 -#define IPW_CMD_VAP_CF_PARAM_SET 68 -#define IPW_CMD_VAP_SET_BEACONING_STATE 69 -#define IPW_CMD_MEASUREMENT 80 -#define IPW_CMD_POWER_CAPABILITY 81 -#define IPW_CMD_SUPPORTED_CHANNELS 82 -#define IPW_CMD_TPC_REPORT 83 -#define IPW_CMD_WME_INFO 84 -#define IPW_CMD_PRODUCTION_COMMAND 85 -#define IPW_CMD_LINKSYS_EOU_INFO 90 - -#define RFD_SIZE 4 -#define NUM_TFD_CHUNKS 6 - -#define TX_QUEUE_SIZE 32 -#define RX_QUEUE_SIZE 32 - -#define DINO_CMD_WEP_KEY 0x08 -#define DINO_CMD_TX 0x0B -#define DCT_ANTENNA_A 0x01 -#define DCT_ANTENNA_B 0x02 - -#define IPW_A_MODE 0 -#define IPW_B_MODE 1 -#define IPW_G_MODE 2 - -/* - * TX Queue Flag Definitions - */ - -/* tx wep key definition */ -#define DCT_WEP_KEY_NOT_IMMIDIATE 0x00 -#define DCT_WEP_KEY_64Bit 0x40 -#define DCT_WEP_KEY_128Bit 0x80 -#define DCT_WEP_KEY_128bitIV 0xC0 -#define DCT_WEP_KEY_SIZE_MASK 0xC0 - -#define DCT_WEP_KEY_INDEX_MASK 0x0F -#define DCT_WEP_INDEX_USE_IMMEDIATE 0x20 - -/* abort attempt if mgmt frame is rx'd */ -#define DCT_FLAG_ABORT_MGMT 0x01 - -/* require CTS */ -#define DCT_FLAG_CTS_REQUIRED 0x02 - -/* use short preamble */ -#define DCT_FLAG_LONG_PREAMBLE 0x00 -#define DCT_FLAG_SHORT_PREAMBLE 0x04 - -/* RTS/CTS first */ -#define DCT_FLAG_RTS_REQD 0x08 - -/* dont calculate duration field */ -#define DCT_FLAG_DUR_SET 0x10 - -/* even if MAC WEP set (allows pre-encrypt) */ -#define DCT_FLAG_NO_WEP 0x20 - -/* overwrite TSF field */ -#define DCT_FLAG_TSF_REQD 0x40 - -/* ACK rx is expected to follow */ -#define DCT_FLAG_ACK_REQD 0x80 - -/* TX flags extension */ -#define DCT_FLAG_EXT_MODE_CCK 0x01 -#define DCT_FLAG_EXT_MODE_OFDM 0x00 - -#define DCT_FLAG_EXT_SECURITY_WEP 0x00 -#define DCT_FLAG_EXT_SECURITY_NO DCT_FLAG_EXT_SECURITY_WEP -#define DCT_FLAG_EXT_SECURITY_CKIP 0x04 -#define DCT_FLAG_EXT_SECURITY_CCM 0x08 -#define DCT_FLAG_EXT_SECURITY_TKIP 0x0C -#define DCT_FLAG_EXT_SECURITY_MASK 0x0C - -#define DCT_FLAG_EXT_QOS_ENABLED 0x10 - -#define DCT_FLAG_EXT_HC_NO_SIFS_PIFS 0x00 -#define DCT_FLAG_EXT_HC_SIFS 0x20 -#define DCT_FLAG_EXT_HC_PIFS 0x40 - -#define TX_RX_TYPE_MASK 0xFF -#define TX_FRAME_TYPE 0x00 -#define TX_HOST_COMMAND_TYPE 0x01 -#define RX_FRAME_TYPE 0x09 -#define RX_HOST_NOTIFICATION_TYPE 0x03 -#define RX_HOST_CMD_RESPONSE_TYPE 0x04 -#define RX_TX_FRAME_RESPONSE_TYPE 0x05 -#define TFD_NEED_IRQ_MASK 0x04 - -#define HOST_CMD_DINO_CONFIG 30 - -#define HOST_NOTIFICATION_STATUS_ASSOCIATED 10 -#define HOST_NOTIFICATION_STATUS_AUTHENTICATE 11 -#define HOST_NOTIFICATION_STATUS_SCAN_CHANNEL_RESULT 12 -#define HOST_NOTIFICATION_STATUS_SCAN_COMPLETED 13 -#define HOST_NOTIFICATION_STATUS_FRAG_LENGTH 14 -#define HOST_NOTIFICATION_STATUS_LINK_DETERIORATION 15 -#define HOST_NOTIFICATION_DINO_CONFIG_RESPONSE 16 -#define HOST_NOTIFICATION_STATUS_BEACON_STATE 17 -#define HOST_NOTIFICATION_STATUS_TGI_TX_KEY 18 -#define HOST_NOTIFICATION_TX_STATUS 19 -#define HOST_NOTIFICATION_CALIB_KEEP_RESULTS 20 -#define HOST_NOTIFICATION_MEASUREMENT_STARTED 21 -#define HOST_NOTIFICATION_MEASUREMENT_ENDED 22 -#define HOST_NOTIFICATION_CHANNEL_SWITCHED 23 -#define HOST_NOTIFICATION_RX_DURING_QUIET_PERIOD 24 -#define HOST_NOTIFICATION_NOISE_STATS 25 -#define HOST_NOTIFICATION_S36_MEASUREMENT_ACCEPTED 30 -#define HOST_NOTIFICATION_S36_MEASUREMENT_REFUSED 31 - -#define HOST_NOTIFICATION_STATUS_BEACON_MISSING 1 -#define IPW_MB_SCAN_CANCEL_THRESHOLD 3 -#define IPW_MB_ROAMING_THRESHOLD_MIN 1 -#define IPW_MB_ROAMING_THRESHOLD_DEFAULT 8 -#define IPW_MB_ROAMING_THRESHOLD_MAX 30 -#define IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT 3*IPW_MB_ROAMING_THRESHOLD_DEFAULT -#define IPW_REAL_RATE_RX_PACKET_THRESHOLD 300 - -#define MACADRR_BYTE_LEN 6 - -#define DCR_TYPE_AP 0x01 -#define DCR_TYPE_WLAP 0x02 -#define DCR_TYPE_MU_ESS 0x03 -#define DCR_TYPE_MU_IBSS 0x04 -#define DCR_TYPE_MU_PIBSS 0x05 -#define DCR_TYPE_SNIFFER 0x06 -#define DCR_TYPE_MU_BSS DCR_TYPE_MU_ESS - -/* QoS definitions */ - -#define CW_MIN_OFDM 15 -#define CW_MAX_OFDM 1023 -#define CW_MIN_CCK 31 -#define CW_MAX_CCK 1023 - -#define QOS_TX0_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) -#define QOS_TX1_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) -#define QOS_TX2_CW_MIN_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/2 - 1) -#define QOS_TX3_CW_MIN_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/4 - 1) - -#define QOS_TX0_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) -#define QOS_TX1_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) -#define QOS_TX2_CW_MIN_CCK cpu_to_le16((CW_MIN_CCK + 1)/2 - 1) -#define QOS_TX3_CW_MIN_CCK cpu_to_le16((CW_MIN_CCK + 1)/4 - 1) - -#define QOS_TX0_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) -#define QOS_TX1_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) -#define QOS_TX2_CW_MAX_OFDM cpu_to_le16(CW_MIN_OFDM) -#define QOS_TX3_CW_MAX_OFDM cpu_to_le16((CW_MIN_OFDM + 1)/2 - 1) - -#define QOS_TX0_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) -#define QOS_TX1_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) -#define QOS_TX2_CW_MAX_CCK cpu_to_le16(CW_MIN_CCK) -#define QOS_TX3_CW_MAX_CCK cpu_to_le16((CW_MIN_CCK + 1)/2 - 1) - -#define QOS_TX0_AIFS (3 - QOS_AIFSN_MIN_VALUE) -#define QOS_TX1_AIFS (7 - QOS_AIFSN_MIN_VALUE) -#define QOS_TX2_AIFS (2 - QOS_AIFSN_MIN_VALUE) -#define QOS_TX3_AIFS (2 - QOS_AIFSN_MIN_VALUE) - -#define QOS_TX0_ACM 0 -#define QOS_TX1_ACM 0 -#define QOS_TX2_ACM 0 -#define QOS_TX3_ACM 0 - -#define QOS_TX0_TXOP_LIMIT_CCK 0 -#define QOS_TX1_TXOP_LIMIT_CCK 0 -#define QOS_TX2_TXOP_LIMIT_CCK cpu_to_le16(6016) -#define QOS_TX3_TXOP_LIMIT_CCK cpu_to_le16(3264) - -#define QOS_TX0_TXOP_LIMIT_OFDM 0 -#define QOS_TX1_TXOP_LIMIT_OFDM 0 -#define QOS_TX2_TXOP_LIMIT_OFDM cpu_to_le16(3008) -#define QOS_TX3_TXOP_LIMIT_OFDM cpu_to_le16(1504) - -#define DEF_TX0_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) -#define DEF_TX1_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) -#define DEF_TX2_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) -#define DEF_TX3_CW_MIN_OFDM cpu_to_le16(CW_MIN_OFDM) - -#define DEF_TX0_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) -#define DEF_TX1_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) -#define DEF_TX2_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) -#define DEF_TX3_CW_MIN_CCK cpu_to_le16(CW_MIN_CCK) - -#define DEF_TX0_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) -#define DEF_TX1_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) -#define DEF_TX2_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) -#define DEF_TX3_CW_MAX_OFDM cpu_to_le16(CW_MAX_OFDM) - -#define DEF_TX0_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) -#define DEF_TX1_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) -#define DEF_TX2_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) -#define DEF_TX3_CW_MAX_CCK cpu_to_le16(CW_MAX_CCK) - -#define DEF_TX0_AIFS 0 -#define DEF_TX1_AIFS 0 -#define DEF_TX2_AIFS 0 -#define DEF_TX3_AIFS 0 - -#define DEF_TX0_ACM 0 -#define DEF_TX1_ACM 0 -#define DEF_TX2_ACM 0 -#define DEF_TX3_ACM 0 - -#define DEF_TX0_TXOP_LIMIT_CCK 0 -#define DEF_TX1_TXOP_LIMIT_CCK 0 -#define DEF_TX2_TXOP_LIMIT_CCK 0 -#define DEF_TX3_TXOP_LIMIT_CCK 0 - -#define DEF_TX0_TXOP_LIMIT_OFDM 0 -#define DEF_TX1_TXOP_LIMIT_OFDM 0 -#define DEF_TX2_TXOP_LIMIT_OFDM 0 -#define DEF_TX3_TXOP_LIMIT_OFDM 0 - -#define QOS_QOS_SETS 3 -#define QOS_PARAM_SET_ACTIVE 0 -#define QOS_PARAM_SET_DEF_CCK 1 -#define QOS_PARAM_SET_DEF_OFDM 2 - -#define CTRL_QOS_NO_ACK (0x0020) - -#define IPW_TX_QUEUE_1 1 -#define IPW_TX_QUEUE_2 2 -#define IPW_TX_QUEUE_3 3 -#define IPW_TX_QUEUE_4 4 - -/* QoS sturctures */ -struct ipw_qos_info { - int qos_enable; - struct libipw_qos_parameters *def_qos_parm_OFDM; - struct libipw_qos_parameters *def_qos_parm_CCK; - u32 burst_duration_CCK; - u32 burst_duration_OFDM; - u16 qos_no_ack_mask; - int burst_enable; -}; - -/**************************************************************/ -/** - * Generic queue structure - * - * Contains common data for Rx and Tx queues - */ -struct clx2_queue { - int n_bd; /**< number of BDs in this queue */ - int first_empty; /**< 1-st empty entry (index) */ - int last_used; /**< last used entry (index) */ - u32 reg_w; /**< 'write' reg (queue head), addr in domain 1 */ - u32 reg_r; /**< 'read' reg (queue tail), addr in domain 1 */ - dma_addr_t dma_addr; /**< physical addr for BD's */ - int low_mark; /**< low watermark, resume queue if free space more than this */ - int high_mark; /**< high watermark, stop queue if free space less than this */ -} __packed; /* XXX */ - -struct machdr32 { - __le16 frame_ctl; - __le16 duration; // watch out for endians! - u8 addr1[MACADRR_BYTE_LEN]; - u8 addr2[MACADRR_BYTE_LEN]; - u8 addr3[MACADRR_BYTE_LEN]; - __le16 seq_ctrl; // more endians! - u8 addr4[MACADRR_BYTE_LEN]; - __le16 qos_ctrl; -} __packed; - -struct machdr30 { - __le16 frame_ctl; - __le16 duration; // watch out for endians! - u8 addr1[MACADRR_BYTE_LEN]; - u8 addr2[MACADRR_BYTE_LEN]; - u8 addr3[MACADRR_BYTE_LEN]; - __le16 seq_ctrl; // more endians! - u8 addr4[MACADRR_BYTE_LEN]; -} __packed; - -struct machdr26 { - __le16 frame_ctl; - __le16 duration; // watch out for endians! - u8 addr1[MACADRR_BYTE_LEN]; - u8 addr2[MACADRR_BYTE_LEN]; - u8 addr3[MACADRR_BYTE_LEN]; - __le16 seq_ctrl; // more endians! - __le16 qos_ctrl; -} __packed; - -struct machdr24 { - __le16 frame_ctl; - __le16 duration; // watch out for endians! - u8 addr1[MACADRR_BYTE_LEN]; - u8 addr2[MACADRR_BYTE_LEN]; - u8 addr3[MACADRR_BYTE_LEN]; - __le16 seq_ctrl; // more endians! -} __packed; - -// TX TFD with 32 byte MAC Header -struct tx_tfd_32 { - struct machdr32 mchdr; // 32 - __le32 uivplaceholder[2]; // 8 -} __packed; - -// TX TFD with 30 byte MAC Header -struct tx_tfd_30 { - struct machdr30 mchdr; // 30 - u8 reserved[2]; // 2 - __le32 uivplaceholder[2]; // 8 -} __packed; - -// tx tfd with 26 byte mac header -struct tx_tfd_26 { - struct machdr26 mchdr; // 26 - u8 reserved1[2]; // 2 - __le32 uivplaceholder[2]; // 8 - u8 reserved2[4]; // 4 -} __packed; - -// tx tfd with 24 byte mac header -struct tx_tfd_24 { - struct machdr24 mchdr; // 24 - __le32 uivplaceholder[2]; // 8 - u8 reserved[8]; // 8 -} __packed; - -#define DCT_WEP_KEY_FIELD_LENGTH 16 - -struct tfd_command { - u8 index; - u8 length; - __le16 reserved; - u8 payload[0]; -} __packed; - -struct tfd_data { - /* Header */ - __le32 work_area_ptr; - u8 station_number; /* 0 for BSS */ - u8 reserved1; - __le16 reserved2; - - /* Tx Parameters */ - u8 cmd_id; - u8 seq_num; - __le16 len; - u8 priority; - u8 tx_flags; - u8 tx_flags_ext; - u8 key_index; - u8 wepkey[DCT_WEP_KEY_FIELD_LENGTH]; - u8 rate; - u8 antenna; - __le16 next_packet_duration; - __le16 next_frag_len; - __le16 back_off_counter; //////txop; - u8 retrylimit; - __le16 cwcurrent; - u8 reserved3; - - /* 802.11 MAC Header */ - union { - struct tx_tfd_24 tfd_24; - struct tx_tfd_26 tfd_26; - struct tx_tfd_30 tfd_30; - struct tx_tfd_32 tfd_32; - } tfd; - - /* Payload DMA info */ - __le32 num_chunks; - __le32 chunk_ptr[NUM_TFD_CHUNKS]; - __le16 chunk_len[NUM_TFD_CHUNKS]; -} __packed; - -struct txrx_control_flags { - u8 message_type; - u8 rx_seq_num; - u8 control_bits; - u8 reserved; -} __packed; - -#define TFD_SIZE 128 -#define TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH (TFD_SIZE - sizeof(struct txrx_control_flags)) - -struct tfd_frame { - struct txrx_control_flags control_flags; - union { - struct tfd_data data; - struct tfd_command cmd; - u8 raw[TFD_CMD_IMMEDIATE_PAYLOAD_LENGTH]; - } u; -} __packed; - -typedef void destructor_func(const void *); - -/** - * Tx Queue for DMA. Queue consists of circular buffer of - * BD's and required locking structures. - */ -struct clx2_tx_queue { - struct clx2_queue q; - struct tfd_frame *bd; - struct libipw_txb **txb; -}; - -/* - * RX related structures and functions - */ -#define RX_FREE_BUFFERS 32 -#define RX_LOW_WATERMARK 8 - -#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 -#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 -#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 - -// Used for passing to driver number of successes and failures per rate -struct rate_histogram { - union { - __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; - __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; - __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; - } success; - union { - __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; - __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; - __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; - } failed; -} __packed; - -/* statistics command response */ -struct ipw_cmd_stats { - u8 cmd_id; - u8 seq_num; - __le16 good_sfd; - __le16 bad_plcp; - __le16 wrong_bssid; - __le16 valid_mpdu; - __le16 bad_mac_header; - __le16 reserved_frame_types; - __le16 rx_ina; - __le16 bad_crc32; - __le16 invalid_cts; - __le16 invalid_acks; - __le16 long_distance_ina_fina; - __le16 dsp_silence_unreachable; - __le16 accumulated_rssi; - __le16 rx_ovfl_frame_tossed; - __le16 rssi_silence_threshold; - __le16 rx_ovfl_frame_supplied; - __le16 last_rx_frame_signal; - __le16 last_rx_frame_noise; - __le16 rx_autodetec_no_ofdm; - __le16 rx_autodetec_no_barker; - __le16 reserved; -} __packed; - -struct notif_channel_result { - u8 channel_num; - struct ipw_cmd_stats stats; - u8 uReserved; -} __packed; - -#define SCAN_COMPLETED_STATUS_COMPLETE 1 -#define SCAN_COMPLETED_STATUS_ABORTED 2 - -struct notif_scan_complete { - u8 scan_type; - u8 num_channels; - u8 status; - u8 reserved; -} __packed; - -struct notif_frag_length { - __le16 frag_length; - __le16 reserved; -} __packed; - -struct notif_beacon_state { - __le32 state; - __le32 number; -} __packed; - -struct notif_tgi_tx_key { - u8 key_state; - u8 security_type; - u8 station_index; - u8 reserved; -} __packed; - -#define SILENCE_OVER_THRESH (1) -#define SILENCE_UNDER_THRESH (2) - -struct notif_link_deterioration { - struct ipw_cmd_stats stats; - u8 rate; - u8 modulation; - struct rate_histogram histogram; - u8 silence_notification_type; /* SILENCE_OVER/UNDER_THRESH */ - __le16 silence_count; -} __packed; - -struct notif_association { - u8 state; -} __packed; - -struct notif_authenticate { - u8 state; - struct machdr24 addr; - __le16 status; -} __packed; - -struct notif_calibration { - u8 data[104]; -} __packed; - -struct notif_noise { - __le32 value; -} __packed; - -struct ipw_rx_notification { - u8 reserved[8]; - u8 subtype; - u8 flags; - __le16 size; - union { - struct notif_association assoc; - struct notif_authenticate auth; - struct notif_channel_result channel_result; - struct notif_scan_complete scan_complete; - struct notif_frag_length frag_len; - struct notif_beacon_state beacon_state; - struct notif_tgi_tx_key tgi_tx_key; - struct notif_link_deterioration link_deterioration; - struct notif_calibration calibration; - struct notif_noise noise; - u8 raw[0]; - } u; -} __packed; - -struct ipw_rx_frame { - __le32 reserved1; - u8 parent_tsf[4]; // fw_use[0] is boolean for OUR_TSF_IS_GREATER - u8 received_channel; // The channel that this frame was received on. - // Note that for .11b this does not have to be - // the same as the channel that it was sent. - // Filled by LMAC - u8 frameStatus; - u8 rate; - u8 rssi; - u8 agc; - u8 rssi_dbm; - __le16 signal; - __le16 noise; - u8 antennaAndPhy; - u8 control; // control bit should be on in bg - u8 rtscts_rate; // rate of rts or cts (in rts cts sequence rate - // is identical) - u8 rtscts_seen; // 0x1 RTS seen ; 0x2 CTS seen - __le16 length; - u8 data[0]; -} __packed; - -struct ipw_rx_header { - u8 message_type; - u8 rx_seq_num; - u8 control_bits; - u8 reserved; -} __packed; - -struct ipw_rx_packet { - struct ipw_rx_header header; - union { - struct ipw_rx_frame frame; - struct ipw_rx_notification notification; - } u; -} __packed; - -#define IPW_RX_NOTIFICATION_SIZE sizeof(struct ipw_rx_header) + 12 -#define IPW_RX_FRAME_SIZE (unsigned int)(sizeof(struct ipw_rx_header) + \ - sizeof(struct ipw_rx_frame)) - -struct ipw_rx_mem_buffer { - dma_addr_t dma_addr; - struct sk_buff *skb; - struct list_head list; -}; /* Not transferred over network, so not __packed */ - -struct ipw_rx_queue { - struct ipw_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; - struct ipw_rx_mem_buffer *queue[RX_QUEUE_SIZE]; - u32 processed; /* Internal index to last handled Rx packet */ - u32 read; /* Shared index to newest available Rx buffer */ - u32 write; /* Shared index to oldest written Rx packet */ - u32 free_count; /* Number of pre-allocated buffers in rx_free */ - /* Each of these lists is used as a FIFO for ipw_rx_mem_buffers */ - struct list_head rx_free; /* Own an SKBs */ - struct list_head rx_used; /* No SKB allocated */ - spinlock_t lock; -}; /* Not transferred over network, so not __packed */ - -struct alive_command_responce { - u8 alive_command; - u8 sequence_number; - __le16 software_revision; - u8 device_identifier; - u8 reserved1[5]; - __le16 reserved2; - __le16 reserved3; - __le16 clock_settle_time; - __le16 powerup_settle_time; - __le16 reserved4; - u8 time_stamp[5]; /* month, day, year, hours, minutes */ - u8 ucode_valid; -} __packed; - -#define IPW_MAX_RATES 12 - -struct ipw_rates { - u8 num_rates; - u8 rates[IPW_MAX_RATES]; -} __packed; - -struct command_block { - unsigned int control; - u32 source_addr; - u32 dest_addr; - unsigned int status; -} __packed; - -#define CB_NUMBER_OF_ELEMENTS_SMALL 64 -struct fw_image_desc { - unsigned long last_cb_index; - unsigned long current_cb_index; - struct command_block cb_list[CB_NUMBER_OF_ELEMENTS_SMALL]; - void *v_addr; - unsigned long p_addr; - unsigned long len; -}; - -struct ipw_sys_config { - u8 bt_coexistence; - u8 reserved1; - u8 answer_broadcast_ssid_probe; - u8 accept_all_data_frames; - u8 accept_non_directed_frames; - u8 exclude_unicast_unencrypted; - u8 disable_unicast_decryption; - u8 exclude_multicast_unencrypted; - u8 disable_multicast_decryption; - u8 antenna_diversity; - u8 pass_crc_to_host; - u8 dot11g_auto_detection; - u8 enable_cts_to_self; - u8 enable_multicast_filtering; - u8 bt_coexist_collision_thr; - u8 silence_threshold; - u8 accept_all_mgmt_bcpr; - u8 accept_all_mgmt_frames; - u8 pass_noise_stats_to_host; - u8 reserved3; -} __packed; - -struct ipw_multicast_addr { - u8 num_of_multicast_addresses; - u8 reserved[3]; - u8 mac1[6]; - u8 mac2[6]; - u8 mac3[6]; - u8 mac4[6]; -} __packed; - -#define DCW_WEP_KEY_INDEX_MASK 0x03 /* bits [0:1] */ -#define DCW_WEP_KEY_SEC_TYPE_MASK 0x30 /* bits [4:5] */ - -#define DCW_WEP_KEY_SEC_TYPE_WEP 0x00 -#define DCW_WEP_KEY_SEC_TYPE_CCM 0x20 -#define DCW_WEP_KEY_SEC_TYPE_TKIP 0x30 - -#define DCW_WEP_KEY_INVALID_SIZE 0x00 /* 0 = Invalid key */ -#define DCW_WEP_KEY64Bit_SIZE 0x05 /* 64-bit encryption */ -#define DCW_WEP_KEY128Bit_SIZE 0x0D /* 128-bit encryption */ -#define DCW_CCM_KEY128Bit_SIZE 0x10 /* 128-bit key */ -//#define DCW_WEP_KEY128BitIV_SIZE 0x10 /* 128-bit key and 128-bit IV */ - -struct ipw_wep_key { - u8 cmd_id; - u8 seq_num; - u8 key_index; - u8 key_size; - u8 key[16]; -} __packed; - -struct ipw_tgi_tx_key { - u8 key_id; - u8 security_type; - u8 station_index; - u8 flags; - u8 key[16]; - __le32 tx_counter[2]; -} __packed; - -#define IPW_SCAN_CHANNELS 54 - -struct ipw_scan_request { - u8 scan_type; - __le16 dwell_time; - u8 channels_list[IPW_SCAN_CHANNELS]; - u8 channels_reserved[3]; -} __packed; - -enum { - IPW_SCAN_PASSIVE_TILL_FIRST_BEACON_SCAN = 0, - IPW_SCAN_PASSIVE_FULL_DWELL_SCAN, - IPW_SCAN_ACTIVE_DIRECT_SCAN, - IPW_SCAN_ACTIVE_BROADCAST_SCAN, - IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN, - IPW_SCAN_TYPES -}; - -struct ipw_scan_request_ext { - __le32 full_scan_index; - u8 channels_list[IPW_SCAN_CHANNELS]; - u8 scan_type[IPW_SCAN_CHANNELS / 2]; - u8 reserved; - __le16 dwell_time[IPW_SCAN_TYPES]; -} __packed; - -static inline u8 ipw_get_scan_type(struct ipw_scan_request_ext *scan, u8 index) -{ - if (index % 2) - return scan->scan_type[index / 2] & 0x0F; - else - return (scan->scan_type[index / 2] & 0xF0) >> 4; -} - -static inline void ipw_set_scan_type(struct ipw_scan_request_ext *scan, - u8 index, u8 scan_type) -{ - if (index % 2) - scan->scan_type[index / 2] = - (scan->scan_type[index / 2] & 0xF0) | (scan_type & 0x0F); - else - scan->scan_type[index / 2] = - (scan->scan_type[index / 2] & 0x0F) | - ((scan_type & 0x0F) << 4); -} - -struct ipw_associate { - u8 channel; -#ifdef __LITTLE_ENDIAN_BITFIELD - u8 auth_type:4, auth_key:4; -#else - u8 auth_key:4, auth_type:4; -#endif - u8 assoc_type; - u8 reserved; - __le16 policy_support; - u8 preamble_length; - u8 ieee_mode; - u8 bssid[ETH_ALEN]; - __le32 assoc_tsf_msw; - __le32 assoc_tsf_lsw; - __le16 capability; - __le16 listen_interval; - __le16 beacon_interval; - u8 dest[ETH_ALEN]; - __le16 atim_window; - u8 smr; - u8 reserved1; - __le16 reserved2; -} __packed; - -struct ipw_supported_rates { - u8 ieee_mode; - u8 num_rates; - u8 purpose; - u8 reserved; - u8 supported_rates[IPW_MAX_RATES]; -} __packed; - -struct ipw_rts_threshold { - __le16 rts_threshold; - __le16 reserved; -} __packed; - -struct ipw_frag_threshold { - __le16 frag_threshold; - __le16 reserved; -} __packed; - -struct ipw_retry_limit { - u8 short_retry_limit; - u8 long_retry_limit; - __le16 reserved; -} __packed; - -struct ipw_dino_config { - __le32 dino_config_addr; - __le16 dino_config_size; - u8 dino_response; - u8 reserved; -} __packed; - -struct ipw_aironet_info { - u8 id; - u8 length; - __le16 reserved; -} __packed; - -struct ipw_rx_key { - u8 station_index; - u8 key_type; - u8 key_id; - u8 key_flag; - u8 key[16]; - u8 station_address[6]; - u8 key_index; - u8 reserved; -} __packed; - -struct ipw_country_channel_info { - u8 first_channel; - u8 no_channels; - s8 max_tx_power; -} __packed; - -struct ipw_country_info { - u8 id; - u8 length; - u8 country_str[IEEE80211_COUNTRY_STRING_LEN]; - struct ipw_country_channel_info groups[7]; -} __packed; - -struct ipw_channel_tx_power { - u8 channel_number; - s8 tx_power; -} __packed; - -#define SCAN_ASSOCIATED_INTERVAL (HZ) -#define SCAN_INTERVAL (HZ / 10) -#define MAX_A_CHANNELS 37 -#define MAX_B_CHANNELS 14 - -struct ipw_tx_power { - u8 num_channels; - u8 ieee_mode; - struct ipw_channel_tx_power channels_tx_power[MAX_A_CHANNELS]; -} __packed; - -struct ipw_rsn_capabilities { - u8 id; - u8 length; - __le16 version; -} __packed; - -struct ipw_sensitivity_calib { - __le16 beacon_rssi_raw; - __le16 reserved; -} __packed; - -/** - * Host command structure. - * - * On input, the following fields should be filled: - * - cmd - * - len - * - status_len - * - param (if needed) - * - * On output, - * - \a status contains status; - * - \a param filled with status parameters. - */ -struct ipw_cmd { /* XXX */ - u32 cmd; /**< Host command */ - u32 status;/**< Status */ - u32 status_len; - /**< How many 32 bit parameters in the status */ - u32 len; /**< incoming parameters length, bytes */ - /** - * command parameters. - * There should be enough space for incoming and - * outcoming parameters. - * Incoming parameters listed 1-st, followed by outcoming params. - * nParams=(len+3)/4+status_len - */ - u32 param[0]; -} __packed; - -#define STATUS_HCMD_ACTIVE (1<<0) /**< host command in progress */ - -#define STATUS_INT_ENABLED (1<<1) -#define STATUS_RF_KILL_HW (1<<2) -#define STATUS_RF_KILL_SW (1<<3) -#define STATUS_RF_KILL_MASK (STATUS_RF_KILL_HW | STATUS_RF_KILL_SW) - -#define STATUS_INIT (1<<5) -#define STATUS_AUTH (1<<6) -#define STATUS_ASSOCIATED (1<<7) -#define STATUS_STATE_MASK (STATUS_INIT | STATUS_AUTH | STATUS_ASSOCIATED) - -#define STATUS_ASSOCIATING (1<<8) -#define STATUS_DISASSOCIATING (1<<9) -#define STATUS_ROAMING (1<<10) -#define STATUS_EXIT_PENDING (1<<11) -#define STATUS_DISASSOC_PENDING (1<<12) -#define STATUS_STATE_PENDING (1<<13) - -#define STATUS_DIRECT_SCAN_PENDING (1<<19) -#define STATUS_SCAN_PENDING (1<<20) -#define STATUS_SCANNING (1<<21) -#define STATUS_SCAN_ABORTING (1<<22) -#define STATUS_SCAN_FORCED (1<<23) - -#define STATUS_LED_LINK_ON (1<<24) -#define STATUS_LED_ACT_ON (1<<25) - -#define STATUS_INDIRECT_BYTE (1<<28) /* sysfs entry configured for access */ -#define STATUS_INDIRECT_DWORD (1<<29) /* sysfs entry configured for access */ -#define STATUS_DIRECT_DWORD (1<<30) /* sysfs entry configured for access */ - -#define STATUS_SECURITY_UPDATED (1<<31) /* Security sync needed */ - -#define CFG_STATIC_CHANNEL (1<<0) /* Restrict assoc. to single channel */ -#define CFG_STATIC_ESSID (1<<1) /* Restrict assoc. to single SSID */ -#define CFG_STATIC_BSSID (1<<2) /* Restrict assoc. to single BSSID */ -#define CFG_CUSTOM_MAC (1<<3) -#define CFG_PREAMBLE_LONG (1<<4) -#define CFG_ADHOC_PERSIST (1<<5) -#define CFG_ASSOCIATE (1<<6) -#define CFG_FIXED_RATE (1<<7) -#define CFG_ADHOC_CREATE (1<<8) -#define CFG_NO_LED (1<<9) -#define CFG_BACKGROUND_SCAN (1<<10) -#define CFG_SPEED_SCAN (1<<11) -#define CFG_NET_STATS (1<<12) - -#define CAP_SHARED_KEY (1<<0) /* Off = OPEN */ -#define CAP_PRIVACY_ON (1<<1) /* Off = No privacy */ - -#define MAX_STATIONS 32 -#define IPW_INVALID_STATION (0xff) - -struct ipw_station_entry { - u8 mac_addr[ETH_ALEN]; - u8 reserved; - u8 support_mode; -}; - -#define AVG_ENTRIES 8 -struct average { - s16 entries[AVG_ENTRIES]; - u8 pos; - u8 init; - s32 sum; -}; - -#define MAX_SPEED_SCAN 100 -#define IPW_IBSS_MAC_HASH_SIZE 31 - -struct ipw_ibss_seq { - u8 mac[ETH_ALEN]; - u16 seq_num; - u16 frag_num; - unsigned long packet_time; - struct list_head list; -}; - -struct ipw_error_elem { /* XXX */ - u32 desc; - u32 time; - u32 blink1; - u32 blink2; - u32 link1; - u32 link2; - u32 data; -}; - -struct ipw_event { /* XXX */ - u32 event; - u32 time; - u32 data; -} __packed; - -struct ipw_fw_error { /* XXX */ - unsigned long jiffies; - u32 status; - u32 config; - u32 elem_len; - u32 log_len; - struct ipw_error_elem *elem; - struct ipw_event *log; - u8 payload[0]; -} __packed; - -#ifdef CONFIG_IPW2200_PROMISCUOUS - -enum ipw_prom_filter { - IPW_PROM_CTL_HEADER_ONLY = (1 << 0), - IPW_PROM_MGMT_HEADER_ONLY = (1 << 1), - IPW_PROM_DATA_HEADER_ONLY = (1 << 2), - IPW_PROM_ALL_HEADER_ONLY = 0xf, /* bits 0..3 */ - IPW_PROM_NO_TX = (1 << 4), - IPW_PROM_NO_RX = (1 << 5), - IPW_PROM_NO_CTL = (1 << 6), - IPW_PROM_NO_MGMT = (1 << 7), - IPW_PROM_NO_DATA = (1 << 8), -}; - -struct ipw_priv; -struct ipw_prom_priv { - struct ipw_priv *priv; - struct libipw_device *ieee; - enum ipw_prom_filter filter; - int tx_packets; - int rx_packets; -}; -#endif - -#if defined(CONFIG_IPW2200_RADIOTAP) || defined(CONFIG_IPW2200_PROMISCUOUS) -/* Magic struct that slots into the radiotap header -- no reason - * to build this manually element by element, we can write it much - * more efficiently than we can parse it. ORDER MATTERS HERE - * - * When sent to us via the simulated Rx interface in sysfs, the entire - * structure is provided regardless of any bits unset. - */ -struct ipw_rt_hdr { - struct ieee80211_radiotap_header rt_hdr; - u64 rt_tsf; /* TSF */ /* XXX */ - u8 rt_flags; /* radiotap packet flags */ - u8 rt_rate; /* rate in 500kb/s */ - __le16 rt_channel; /* channel in mhz */ - __le16 rt_chbitmask; /* channel bitfield */ - s8 rt_dbmsignal; /* signal in dbM, kluged to signed */ - s8 rt_dbmnoise; - u8 rt_antenna; /* antenna number */ - u8 payload[0]; /* payload... */ -} __packed; -#endif - -struct ipw_priv { - /* ieee device used by generic ieee processing code */ - struct libipw_device *ieee; - - spinlock_t lock; - spinlock_t irq_lock; - struct mutex mutex; - - /* basic pci-network driver stuff */ - struct pci_dev *pci_dev; - struct net_device *net_dev; - -#ifdef CONFIG_IPW2200_PROMISCUOUS - /* Promiscuous mode */ - struct ipw_prom_priv *prom_priv; - struct net_device *prom_net_dev; -#endif - - /* pci hardware address support */ - void __iomem *hw_base; - unsigned long hw_len; - - struct fw_image_desc sram_desc; - - /* result of ucode download */ - struct alive_command_responce dino_alive; - - wait_queue_head_t wait_command_queue; - wait_queue_head_t wait_state; - - /* Rx and Tx DMA processing queues */ - struct ipw_rx_queue *rxq; - struct clx2_tx_queue txq_cmd; - struct clx2_tx_queue txq[4]; - u32 status; - u32 config; - u32 capability; - - struct average average_missed_beacons; - s16 exp_avg_rssi; - s16 exp_avg_noise; - u32 port_type; - int rx_bufs_min; /**< minimum number of bufs in Rx queue */ - int rx_pend_max; /**< maximum pending buffers for one IRQ */ - u32 hcmd_seq; /**< sequence number for hcmd */ - u32 disassociate_threshold; - u32 roaming_threshold; - - struct ipw_associate assoc_request; - struct libipw_network *assoc_network; - - unsigned long ts_scan_abort; - struct ipw_supported_rates rates; - struct ipw_rates phy[3]; /**< PHY restrictions, per band */ - struct ipw_rates supp; /**< software defined */ - struct ipw_rates extended; /**< use for corresp. IE, AP only */ - - struct notif_link_deterioration last_link_deterioration; /** for statistics */ - struct ipw_cmd *hcmd; /**< host command currently executed */ - - wait_queue_head_t hcmd_wq; /**< host command waits for execution */ - u32 tsf_bcn[2]; /**< TSF from latest beacon */ - - struct notif_calibration calib; /**< last calibration */ - - /* ordinal interface with firmware */ - u32 table0_addr; - u32 table0_len; - u32 table1_addr; - u32 table1_len; - u32 table2_addr; - u32 table2_len; - - /* context information */ - u8 essid[IW_ESSID_MAX_SIZE]; - u8 essid_len; - u8 nick[IW_ESSID_MAX_SIZE]; - u16 rates_mask; - u8 channel; - struct ipw_sys_config sys_config; - u32 power_mode; - u8 bssid[ETH_ALEN]; - u16 rts_threshold; - u8 mac_addr[ETH_ALEN]; - u8 num_stations; - u8 stations[MAX_STATIONS][ETH_ALEN]; - u8 short_retry_limit; - u8 long_retry_limit; - - u32 notif_missed_beacons; - - /* Statistics and counters normalized with each association */ - u32 last_missed_beacons; - u32 last_tx_packets; - u32 last_rx_packets; - u32 last_tx_failures; - u32 last_rx_err; - u32 last_rate; - - u32 missed_adhoc_beacons; - u32 missed_beacons; - u32 rx_packets; - u32 tx_packets; - u32 quality; - - u8 speed_scan[MAX_SPEED_SCAN]; - u8 speed_scan_pos; - - u16 last_seq_num; - u16 last_frag_num; - unsigned long last_packet_time; - struct list_head ibss_mac_hash[IPW_IBSS_MAC_HASH_SIZE]; - - /* eeprom */ - u8 eeprom[0x100]; /* 256 bytes of eeprom */ - u8 country[4]; - int eeprom_delay; - - struct iw_statistics wstats; - - struct iw_public_data wireless_data; - - int user_requested_scan; - u8 direct_scan_ssid[IW_ESSID_MAX_SIZE]; - u8 direct_scan_ssid_len; - - struct delayed_work adhoc_check; - struct work_struct associate; - struct work_struct disassociate; - struct work_struct system_config; - struct work_struct rx_replenish; - struct delayed_work request_scan; - struct delayed_work request_direct_scan; - struct delayed_work request_passive_scan; - struct delayed_work scan_event; - struct work_struct adapter_restart; - struct delayed_work rf_kill; - struct work_struct up; - struct work_struct down; - struct delayed_work gather_stats; - struct work_struct abort_scan; - struct work_struct roam; - struct delayed_work scan_check; - struct work_struct link_up; - struct work_struct link_down; - - struct tasklet_struct irq_tasklet; - - /* LED related variables and work_struct */ - u8 nic_type; - u32 led_activity_on; - u32 led_activity_off; - u32 led_association_on; - u32 led_association_off; - u32 led_ofdm_on; - u32 led_ofdm_off; - - struct delayed_work led_link_on; - struct delayed_work led_link_off; - struct delayed_work led_act_off; - struct work_struct merge_networks; - - struct ipw_cmd_log *cmdlog; - int cmdlog_len; - int cmdlog_pos; - -#define IPW_2200BG 1 -#define IPW_2915ABG 2 - u8 adapter; - - s8 tx_power; - - /* Track time in suspend */ - unsigned long suspend_at; - unsigned long suspend_time; - -#ifdef CONFIG_PM - u32 pm_state[16]; -#endif - - struct ipw_fw_error *error; - - /* network state */ - - /* Used to pass the current INTA value from ISR to Tasklet */ - u32 isr_inta; - - /* QoS */ - struct ipw_qos_info qos_data; - struct work_struct qos_activate; - /*********************************/ - - /* debugging info */ - u32 indirect_dword; - u32 direct_dword; - u32 indirect_byte; -}; /*ipw_priv */ - -/* debug macros */ - -/* Debug and printf string expansion helpers for printing bitfields */ -#define BIT_FMT8 "%c%c%c%c-%c%c%c%c" -#define BIT_FMT16 BIT_FMT8 ":" BIT_FMT8 -#define BIT_FMT32 BIT_FMT16 " " BIT_FMT16 - -#define BITC(x,y) (((x>>y)&1)?'1':'0') -#define BIT_ARG8(x) \ -BITC(x,7),BITC(x,6),BITC(x,5),BITC(x,4),\ -BITC(x,3),BITC(x,2),BITC(x,1),BITC(x,0) - -#define BIT_ARG16(x) \ -BITC(x,15),BITC(x,14),BITC(x,13),BITC(x,12),\ -BITC(x,11),BITC(x,10),BITC(x,9),BITC(x,8),\ -BIT_ARG8(x) - -#define BIT_ARG32(x) \ -BITC(x,31),BITC(x,30),BITC(x,29),BITC(x,28),\ -BITC(x,27),BITC(x,26),BITC(x,25),BITC(x,24),\ -BITC(x,23),BITC(x,22),BITC(x,21),BITC(x,20),\ -BITC(x,19),BITC(x,18),BITC(x,17),BITC(x,16),\ -BIT_ARG16(x) - - -#define IPW_DEBUG(level, fmt, args...) \ -do { if (ipw_debug_level & (level)) \ - printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) - -#ifdef CONFIG_IPW2200_DEBUG -#define IPW_LL_DEBUG(level, fmt, args...) \ -do { if (ipw_debug_level & (level)) \ - printk(KERN_DEBUG DRV_NAME": %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) -#else -#define IPW_LL_DEBUG(level, fmt, args...) do {} while (0) -#endif /* CONFIG_IPW2200_DEBUG */ - -/* - * To use the debug system; - * - * If you are defining a new debug classification, simply add it to the #define - * list here in the form of: - * - * #define IPW_DL_xxxx VALUE - * - * shifting value to the left one bit from the previous entry. xxxx should be - * the name of the classification (for example, WEP) - * - * You then need to either add a IPW_xxxx_DEBUG() macro definition for your - * classification, or use IPW_DEBUG(IPW_DL_xxxx, ...) whenever you want - * to send output to that classification. - * - * To add your debug level to the list of levels seen when you perform - * - * % cat /proc/net/ipw/debug_level - * - * you simply need to add your entry to the ipw_debug_levels array. - * - * If you do not see debug_level in /proc/net/ipw then you do not have - * CONFIG_IPW2200_DEBUG defined in your kernel configuration - * - */ - -#define IPW_DL_ERROR (1<<0) -#define IPW_DL_WARNING (1<<1) -#define IPW_DL_INFO (1<<2) -#define IPW_DL_WX (1<<3) -#define IPW_DL_HOST_COMMAND (1<<5) -#define IPW_DL_STATE (1<<6) - -#define IPW_DL_NOTIF (1<<10) -#define IPW_DL_SCAN (1<<11) -#define IPW_DL_ASSOC (1<<12) -#define IPW_DL_DROP (1<<13) -#define IPW_DL_IOCTL (1<<14) - -#define IPW_DL_MANAGE (1<<15) -#define IPW_DL_FW (1<<16) -#define IPW_DL_RF_KILL (1<<17) -#define IPW_DL_FW_ERRORS (1<<18) - -#define IPW_DL_LED (1<<19) - -#define IPW_DL_ORD (1<<20) - -#define IPW_DL_FRAG (1<<21) -#define IPW_DL_WEP (1<<22) -#define IPW_DL_TX (1<<23) -#define IPW_DL_RX (1<<24) -#define IPW_DL_ISR (1<<25) -#define IPW_DL_FW_INFO (1<<26) -#define IPW_DL_IO (1<<27) -#define IPW_DL_TRACE (1<<28) - -#define IPW_DL_STATS (1<<29) -#define IPW_DL_MERGE (1<<30) -#define IPW_DL_QOS (1<<31) - -#define IPW_ERROR(f, a...) printk(KERN_ERR DRV_NAME ": " f, ## a) -#define IPW_WARNING(f, a...) printk(KERN_WARNING DRV_NAME ": " f, ## a) -#define IPW_DEBUG_INFO(f, a...) IPW_DEBUG(IPW_DL_INFO, f, ## a) - -#define IPW_DEBUG_WX(f, a...) IPW_DEBUG(IPW_DL_WX, f, ## a) -#define IPW_DEBUG_SCAN(f, a...) IPW_DEBUG(IPW_DL_SCAN, f, ## a) -#define IPW_DEBUG_TRACE(f, a...) IPW_LL_DEBUG(IPW_DL_TRACE, f, ## a) -#define IPW_DEBUG_RX(f, a...) IPW_LL_DEBUG(IPW_DL_RX, f, ## a) -#define IPW_DEBUG_TX(f, a...) IPW_LL_DEBUG(IPW_DL_TX, f, ## a) -#define IPW_DEBUG_ISR(f, a...) IPW_LL_DEBUG(IPW_DL_ISR, f, ## a) -#define IPW_DEBUG_MANAGEMENT(f, a...) IPW_DEBUG(IPW_DL_MANAGE, f, ## a) -#define IPW_DEBUG_LED(f, a...) IPW_LL_DEBUG(IPW_DL_LED, f, ## a) -#define IPW_DEBUG_WEP(f, a...) IPW_LL_DEBUG(IPW_DL_WEP, f, ## a) -#define IPW_DEBUG_HC(f, a...) IPW_LL_DEBUG(IPW_DL_HOST_COMMAND, f, ## a) -#define IPW_DEBUG_FRAG(f, a...) IPW_LL_DEBUG(IPW_DL_FRAG, f, ## a) -#define IPW_DEBUG_FW(f, a...) IPW_LL_DEBUG(IPW_DL_FW, f, ## a) -#define IPW_DEBUG_RF_KILL(f, a...) IPW_DEBUG(IPW_DL_RF_KILL, f, ## a) -#define IPW_DEBUG_DROP(f, a...) IPW_DEBUG(IPW_DL_DROP, f, ## a) -#define IPW_DEBUG_IO(f, a...) IPW_LL_DEBUG(IPW_DL_IO, f, ## a) -#define IPW_DEBUG_ORD(f, a...) IPW_LL_DEBUG(IPW_DL_ORD, f, ## a) -#define IPW_DEBUG_FW_INFO(f, a...) IPW_LL_DEBUG(IPW_DL_FW_INFO, f, ## a) -#define IPW_DEBUG_NOTIF(f, a...) IPW_DEBUG(IPW_DL_NOTIF, f, ## a) -#define IPW_DEBUG_STATE(f, a...) IPW_DEBUG(IPW_DL_STATE | IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) -#define IPW_DEBUG_ASSOC(f, a...) IPW_DEBUG(IPW_DL_ASSOC | IPW_DL_INFO, f, ## a) -#define IPW_DEBUG_STATS(f, a...) IPW_LL_DEBUG(IPW_DL_STATS, f, ## a) -#define IPW_DEBUG_MERGE(f, a...) IPW_LL_DEBUG(IPW_DL_MERGE, f, ## a) -#define IPW_DEBUG_QOS(f, a...) IPW_LL_DEBUG(IPW_DL_QOS, f, ## a) - -#include - -/* -* Register bit definitions -*/ - -#define IPW_INTA_RW 0x00000008 -#define IPW_INTA_MASK_R 0x0000000C -#define IPW_INDIRECT_ADDR 0x00000010 -#define IPW_INDIRECT_DATA 0x00000014 -#define IPW_AUTOINC_ADDR 0x00000018 -#define IPW_AUTOINC_DATA 0x0000001C -#define IPW_RESET_REG 0x00000020 -#define IPW_GP_CNTRL_RW 0x00000024 - -#define IPW_READ_INT_REGISTER 0xFF4 - -#define IPW_GP_CNTRL_BIT_INIT_DONE 0x00000004 - -#define IPW_REGISTER_DOMAIN1_END 0x00001000 -#define IPW_SRAM_READ_INT_REGISTER 0x00000ff4 - -#define IPW_SHARED_LOWER_BOUND 0x00000200 -#define IPW_INTERRUPT_AREA_LOWER_BOUND 0x00000f80 - -#define IPW_NIC_SRAM_LOWER_BOUND 0x00000000 -#define IPW_NIC_SRAM_UPPER_BOUND 0x00030000 - -#define IPW_BIT_INT_HOST_SRAM_READ_INT_REGISTER (1 << 29) -#define IPW_GP_CNTRL_BIT_CLOCK_READY 0x00000001 -#define IPW_GP_CNTRL_BIT_HOST_ALLOWS_STANDBY 0x00000002 - -/* - * RESET Register Bit Indexes - */ -#define CBD_RESET_REG_PRINCETON_RESET (1<<0) -#define IPW_START_STANDBY (1<<2) -#define IPW_ACTIVITY_LED (1<<4) -#define IPW_ASSOCIATED_LED (1<<5) -#define IPW_OFDM_LED (1<<6) -#define IPW_RESET_REG_SW_RESET (1<<7) -#define IPW_RESET_REG_MASTER_DISABLED (1<<8) -#define IPW_RESET_REG_STOP_MASTER (1<<9) -#define IPW_GATE_ODMA (1<<25) -#define IPW_GATE_IDMA (1<<26) -#define IPW_ARC_KESHET_CONFIG (1<<27) -#define IPW_GATE_ADMA (1<<29) - -#define IPW_CSR_CIS_UPPER_BOUND 0x00000200 -#define IPW_DOMAIN_0_END 0x1000 -#define CLX_MEM_BAR_SIZE 0x1000 - -/* Dino/baseband control registers bits */ - -#define DINO_ENABLE_SYSTEM 0x80 /* 1 = baseband processor on, 0 = reset */ -#define DINO_ENABLE_CS 0x40 /* 1 = enable ucode load */ -#define DINO_RXFIFO_DATA 0x01 /* 1 = data available */ -#define IPW_BASEBAND_CONTROL_STATUS 0X00200000 -#define IPW_BASEBAND_TX_FIFO_WRITE 0X00200004 -#define IPW_BASEBAND_RX_FIFO_READ 0X00200004 -#define IPW_BASEBAND_CONTROL_STORE 0X00200010 - -#define IPW_INTERNAL_CMD_EVENT 0X00300004 -#define IPW_BASEBAND_POWER_DOWN 0x00000001 - -#define IPW_MEM_HALT_AND_RESET 0x003000e0 - -/* defgroup bits_halt_reset MEM_HALT_AND_RESET register bits */ -#define IPW_BIT_HALT_RESET_ON 0x80000000 -#define IPW_BIT_HALT_RESET_OFF 0x00000000 - -#define CB_LAST_VALID 0x20000000 -#define CB_INT_ENABLED 0x40000000 -#define CB_VALID 0x80000000 -#define CB_SRC_LE 0x08000000 -#define CB_DEST_LE 0x04000000 -#define CB_SRC_AUTOINC 0x00800000 -#define CB_SRC_IO_GATED 0x00400000 -#define CB_DEST_AUTOINC 0x00080000 -#define CB_SRC_SIZE_LONG 0x00200000 -#define CB_DEST_SIZE_LONG 0x00020000 - -/* DMA DEFINES */ - -#define DMA_CONTROL_SMALL_CB_CONST_VALUE 0x00540000 -#define DMA_CB_STOP_AND_ABORT 0x00000C00 -#define DMA_CB_START 0x00000100 - -#define IPW_SHARED_SRAM_SIZE 0x00030000 -#define IPW_SHARED_SRAM_DMA_CONTROL 0x00027000 -#define CB_MAX_LENGTH 0x1FFF - -#define IPW_HOST_EEPROM_DATA_SRAM_SIZE 0xA18 -#define IPW_EEPROM_IMAGE_SIZE 0x100 - -/* DMA defs */ -#define IPW_DMA_I_CURRENT_CB 0x003000D0 -#define IPW_DMA_O_CURRENT_CB 0x003000D4 -#define IPW_DMA_I_DMA_CONTROL 0x003000A4 -#define IPW_DMA_I_CB_BASE 0x003000A0 - -#define IPW_TX_CMD_QUEUE_BD_BASE 0x00000200 -#define IPW_TX_CMD_QUEUE_BD_SIZE 0x00000204 -#define IPW_TX_QUEUE_0_BD_BASE 0x00000208 -#define IPW_TX_QUEUE_0_BD_SIZE (0x0000020C) -#define IPW_TX_QUEUE_1_BD_BASE 0x00000210 -#define IPW_TX_QUEUE_1_BD_SIZE 0x00000214 -#define IPW_TX_QUEUE_2_BD_BASE 0x00000218 -#define IPW_TX_QUEUE_2_BD_SIZE (0x0000021C) -#define IPW_TX_QUEUE_3_BD_BASE 0x00000220 -#define IPW_TX_QUEUE_3_BD_SIZE 0x00000224 -#define IPW_RX_BD_BASE 0x00000240 -#define IPW_RX_BD_SIZE 0x00000244 -#define IPW_RFDS_TABLE_LOWER 0x00000500 - -#define IPW_TX_CMD_QUEUE_READ_INDEX 0x00000280 -#define IPW_TX_QUEUE_0_READ_INDEX 0x00000284 -#define IPW_TX_QUEUE_1_READ_INDEX 0x00000288 -#define IPW_TX_QUEUE_2_READ_INDEX (0x0000028C) -#define IPW_TX_QUEUE_3_READ_INDEX 0x00000290 -#define IPW_RX_READ_INDEX (0x000002A0) - -#define IPW_TX_CMD_QUEUE_WRITE_INDEX (0x00000F80) -#define IPW_TX_QUEUE_0_WRITE_INDEX (0x00000F84) -#define IPW_TX_QUEUE_1_WRITE_INDEX (0x00000F88) -#define IPW_TX_QUEUE_2_WRITE_INDEX (0x00000F8C) -#define IPW_TX_QUEUE_3_WRITE_INDEX (0x00000F90) -#define IPW_RX_WRITE_INDEX (0x00000FA0) - -/* - * EEPROM Related Definitions - */ - -#define IPW_EEPROM_DATA_SRAM_ADDRESS (IPW_SHARED_LOWER_BOUND + 0x814) -#define IPW_EEPROM_DATA_SRAM_SIZE (IPW_SHARED_LOWER_BOUND + 0x818) -#define IPW_EEPROM_LOAD_DISABLE (IPW_SHARED_LOWER_BOUND + 0x81C) -#define IPW_EEPROM_DATA (IPW_SHARED_LOWER_BOUND + 0x820) -#define IPW_EEPROM_UPPER_ADDRESS (IPW_SHARED_LOWER_BOUND + 0x9E0) - -#define IPW_STATION_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0xA0C) -#define IPW_STATION_TABLE_UPPER (IPW_SHARED_LOWER_BOUND + 0xB0C) -#define IPW_REQUEST_ATIM (IPW_SHARED_LOWER_BOUND + 0xB0C) -#define IPW_ATIM_SENT (IPW_SHARED_LOWER_BOUND + 0xB10) -#define IPW_WHO_IS_AWAKE (IPW_SHARED_LOWER_BOUND + 0xB14) -#define IPW_DURING_ATIM_WINDOW (IPW_SHARED_LOWER_BOUND + 0xB18) - -#define MSB 1 -#define LSB 0 -#define WORD_TO_BYTE(_word) ((_word) * sizeof(u16)) - -#define GET_EEPROM_ADDR(_wordoffset,_byteoffset) \ - ( WORD_TO_BYTE(_wordoffset) + (_byteoffset) ) - -/* EEPROM access by BYTE */ -#define EEPROM_PME_CAPABILITY (GET_EEPROM_ADDR(0x09,MSB)) /* 1 byte */ -#define EEPROM_MAC_ADDRESS (GET_EEPROM_ADDR(0x21,LSB)) /* 6 byte */ -#define EEPROM_VERSION (GET_EEPROM_ADDR(0x24,MSB)) /* 1 byte */ -#define EEPROM_NIC_TYPE (GET_EEPROM_ADDR(0x25,LSB)) /* 1 byte */ -#define EEPROM_SKU_CAPABILITY (GET_EEPROM_ADDR(0x25,MSB)) /* 1 byte */ -#define EEPROM_COUNTRY_CODE (GET_EEPROM_ADDR(0x26,LSB)) /* 3 bytes */ -#define EEPROM_IBSS_CHANNELS_BG (GET_EEPROM_ADDR(0x28,LSB)) /* 2 bytes */ -#define EEPROM_IBSS_CHANNELS_A (GET_EEPROM_ADDR(0x29,MSB)) /* 5 bytes */ -#define EEPROM_BSS_CHANNELS_BG (GET_EEPROM_ADDR(0x2c,LSB)) /* 2 bytes */ -#define EEPROM_HW_VERSION (GET_EEPROM_ADDR(0x72,LSB)) /* 2 bytes */ - -/* NIC type as found in the one byte EEPROM_NIC_TYPE offset */ -#define EEPROM_NIC_TYPE_0 0 -#define EEPROM_NIC_TYPE_1 1 -#define EEPROM_NIC_TYPE_2 2 -#define EEPROM_NIC_TYPE_3 3 -#define EEPROM_NIC_TYPE_4 4 - -/* Bluetooth Coexistence capabilities as found in EEPROM_SKU_CAPABILITY */ -#define EEPROM_SKU_CAP_BT_CHANNEL_SIG 0x01 /* we can tell BT our channel # */ -#define EEPROM_SKU_CAP_BT_PRIORITY 0x02 /* BT can take priority over us */ -#define EEPROM_SKU_CAP_BT_OOB 0x04 /* we can signal BT out-of-band */ - -#define FW_MEM_REG_LOWER_BOUND 0x00300000 -#define FW_MEM_REG_EEPROM_ACCESS (FW_MEM_REG_LOWER_BOUND + 0x40) -#define IPW_EVENT_REG (FW_MEM_REG_LOWER_BOUND + 0x04) -#define EEPROM_BIT_SK (1<<0) -#define EEPROM_BIT_CS (1<<1) -#define EEPROM_BIT_DI (1<<2) -#define EEPROM_BIT_DO (1<<4) - -#define EEPROM_CMD_READ 0x2 - -/* Interrupts masks */ -#define IPW_INTA_NONE 0x00000000 - -#define IPW_INTA_BIT_RX_TRANSFER 0x00000002 -#define IPW_INTA_BIT_STATUS_CHANGE 0x00000010 -#define IPW_INTA_BIT_BEACON_PERIOD_EXPIRED 0x00000020 - -//Inta Bits for CF -#define IPW_INTA_BIT_TX_CMD_QUEUE 0x00000800 -#define IPW_INTA_BIT_TX_QUEUE_1 0x00001000 -#define IPW_INTA_BIT_TX_QUEUE_2 0x00002000 -#define IPW_INTA_BIT_TX_QUEUE_3 0x00004000 -#define IPW_INTA_BIT_TX_QUEUE_4 0x00008000 - -#define IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE 0x00010000 - -#define IPW_INTA_BIT_PREPARE_FOR_POWER_DOWN 0x00100000 -#define IPW_INTA_BIT_POWER_DOWN 0x00200000 - -#define IPW_INTA_BIT_FW_INITIALIZATION_DONE 0x01000000 -#define IPW_INTA_BIT_FW_CARD_DISABLE_PHY_OFF_DONE 0x02000000 -#define IPW_INTA_BIT_RF_KILL_DONE 0x04000000 -#define IPW_INTA_BIT_FATAL_ERROR 0x40000000 -#define IPW_INTA_BIT_PARITY_ERROR 0x80000000 - -/* Interrupts enabled at init time. */ -#define IPW_INTA_MASK_ALL \ - (IPW_INTA_BIT_TX_QUEUE_1 | \ - IPW_INTA_BIT_TX_QUEUE_2 | \ - IPW_INTA_BIT_TX_QUEUE_3 | \ - IPW_INTA_BIT_TX_QUEUE_4 | \ - IPW_INTA_BIT_TX_CMD_QUEUE | \ - IPW_INTA_BIT_RX_TRANSFER | \ - IPW_INTA_BIT_FATAL_ERROR | \ - IPW_INTA_BIT_PARITY_ERROR | \ - IPW_INTA_BIT_STATUS_CHANGE | \ - IPW_INTA_BIT_FW_INITIALIZATION_DONE | \ - IPW_INTA_BIT_BEACON_PERIOD_EXPIRED | \ - IPW_INTA_BIT_SLAVE_MODE_HOST_CMD_DONE | \ - IPW_INTA_BIT_PREPARE_FOR_POWER_DOWN | \ - IPW_INTA_BIT_POWER_DOWN | \ - IPW_INTA_BIT_RF_KILL_DONE ) - -/* FW event log definitions */ -#define EVENT_ELEM_SIZE (3 * sizeof(u32)) -#define EVENT_START_OFFSET (1 * sizeof(u32) + 2 * sizeof(u16)) - -/* FW error log definitions */ -#define ERROR_ELEM_SIZE (7 * sizeof(u32)) -#define ERROR_START_OFFSET (1 * sizeof(u32)) - -/* TX power level (dbm) */ -#define IPW_TX_POWER_MIN -12 -#define IPW_TX_POWER_MAX 20 -#define IPW_TX_POWER_DEFAULT IPW_TX_POWER_MAX - -enum { - IPW_FW_ERROR_OK = 0, - IPW_FW_ERROR_FAIL, - IPW_FW_ERROR_MEMORY_UNDERFLOW, - IPW_FW_ERROR_MEMORY_OVERFLOW, - IPW_FW_ERROR_BAD_PARAM, - IPW_FW_ERROR_BAD_CHECKSUM, - IPW_FW_ERROR_NMI_INTERRUPT, - IPW_FW_ERROR_BAD_DATABASE, - IPW_FW_ERROR_ALLOC_FAIL, - IPW_FW_ERROR_DMA_UNDERRUN, - IPW_FW_ERROR_DMA_STATUS, - IPW_FW_ERROR_DINO_ERROR, - IPW_FW_ERROR_EEPROM_ERROR, - IPW_FW_ERROR_SYSASSERT, - IPW_FW_ERROR_FATAL_ERROR -}; - -#define AUTH_OPEN 0 -#define AUTH_SHARED_KEY 1 -#define AUTH_LEAP 2 -#define AUTH_IGNORE 3 - -#define HC_ASSOCIATE 0 -#define HC_REASSOCIATE 1 -#define HC_DISASSOCIATE 2 -#define HC_IBSS_START 3 -#define HC_IBSS_RECONF 4 -#define HC_DISASSOC_QUIET 5 - -#define HC_QOS_SUPPORT_ASSOC cpu_to_le16(0x01) - -#define IPW_RATE_CAPABILITIES 1 -#define IPW_RATE_CONNECT 0 - -/* - * Rate values and masks - */ -#define IPW_TX_RATE_1MB 0x0A -#define IPW_TX_RATE_2MB 0x14 -#define IPW_TX_RATE_5MB 0x37 -#define IPW_TX_RATE_6MB 0x0D -#define IPW_TX_RATE_9MB 0x0F -#define IPW_TX_RATE_11MB 0x6E -#define IPW_TX_RATE_12MB 0x05 -#define IPW_TX_RATE_18MB 0x07 -#define IPW_TX_RATE_24MB 0x09 -#define IPW_TX_RATE_36MB 0x0B -#define IPW_TX_RATE_48MB 0x01 -#define IPW_TX_RATE_54MB 0x03 - -#define IPW_ORD_TABLE_ID_MASK 0x0000FF00 -#define IPW_ORD_TABLE_VALUE_MASK 0x000000FF - -#define IPW_ORD_TABLE_0_MASK 0x0000F000 -#define IPW_ORD_TABLE_1_MASK 0x0000F100 -#define IPW_ORD_TABLE_2_MASK 0x0000F200 -#define IPW_ORD_TABLE_3_MASK 0x0000F300 -#define IPW_ORD_TABLE_4_MASK 0x0000F400 -#define IPW_ORD_TABLE_5_MASK 0x0000F500 -#define IPW_ORD_TABLE_6_MASK 0x0000F600 -#define IPW_ORD_TABLE_7_MASK 0x0000F700 - -/* - * Table 0 Entries (all entries are 32 bits) - */ -enum { - IPW_ORD_STAT_TX_CURR_RATE = IPW_ORD_TABLE_0_MASK + 1, - IPW_ORD_STAT_FRAG_TRESHOLD, - IPW_ORD_STAT_RTS_THRESHOLD, - IPW_ORD_STAT_TX_HOST_REQUESTS, - IPW_ORD_STAT_TX_HOST_COMPLETE, - IPW_ORD_STAT_TX_DIR_DATA, - IPW_ORD_STAT_TX_DIR_DATA_B_1, - IPW_ORD_STAT_TX_DIR_DATA_B_2, - IPW_ORD_STAT_TX_DIR_DATA_B_5_5, - IPW_ORD_STAT_TX_DIR_DATA_B_11, - /* Hole */ - - IPW_ORD_STAT_TX_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 19, - IPW_ORD_STAT_TX_DIR_DATA_G_2, - IPW_ORD_STAT_TX_DIR_DATA_G_5_5, - IPW_ORD_STAT_TX_DIR_DATA_G_6, - IPW_ORD_STAT_TX_DIR_DATA_G_9, - IPW_ORD_STAT_TX_DIR_DATA_G_11, - IPW_ORD_STAT_TX_DIR_DATA_G_12, - IPW_ORD_STAT_TX_DIR_DATA_G_18, - IPW_ORD_STAT_TX_DIR_DATA_G_24, - IPW_ORD_STAT_TX_DIR_DATA_G_36, - IPW_ORD_STAT_TX_DIR_DATA_G_48, - IPW_ORD_STAT_TX_DIR_DATA_G_54, - IPW_ORD_STAT_TX_NON_DIR_DATA, - IPW_ORD_STAT_TX_NON_DIR_DATA_B_1, - IPW_ORD_STAT_TX_NON_DIR_DATA_B_2, - IPW_ORD_STAT_TX_NON_DIR_DATA_B_5_5, - IPW_ORD_STAT_TX_NON_DIR_DATA_B_11, - /* Hole */ - - IPW_ORD_STAT_TX_NON_DIR_DATA_G_1 = IPW_ORD_TABLE_0_MASK + 44, - IPW_ORD_STAT_TX_NON_DIR_DATA_G_2, - IPW_ORD_STAT_TX_NON_DIR_DATA_G_5_5, - IPW_ORD_STAT_TX_NON_DIR_DATA_G_6, - IPW_ORD_STAT_TX_NON_DIR_DATA_G_9, - IPW_ORD_STAT_TX_NON_DIR_DATA_G_11, - IPW_ORD_STAT_TX_NON_DIR_DATA_G_12, - IPW_ORD_STAT_TX_NON_DIR_DATA_G_18, - IPW_ORD_STAT_TX_NON_DIR_DATA_G_24, - IPW_ORD_STAT_TX_NON_DIR_DATA_G_36, - IPW_ORD_STAT_TX_NON_DIR_DATA_G_48, - IPW_ORD_STAT_TX_NON_DIR_DATA_G_54, - IPW_ORD_STAT_TX_RETRY, - IPW_ORD_STAT_TX_FAILURE, - IPW_ORD_STAT_RX_ERR_CRC, - IPW_ORD_STAT_RX_ERR_ICV, - IPW_ORD_STAT_RX_NO_BUFFER, - IPW_ORD_STAT_FULL_SCANS, - IPW_ORD_STAT_PARTIAL_SCANS, - IPW_ORD_STAT_TGH_ABORTED_SCANS, - IPW_ORD_STAT_TX_TOTAL_BYTES, - IPW_ORD_STAT_CURR_RSSI_RAW, - IPW_ORD_STAT_RX_BEACON, - IPW_ORD_STAT_MISSED_BEACONS, - IPW_ORD_TABLE_0_LAST -}; - -#define IPW_RSSI_TO_DBM 112 - -/* Table 1 Entries - */ -enum { - IPW_ORD_TABLE_1_LAST = IPW_ORD_TABLE_1_MASK | 1, -}; - -/* - * Table 2 Entries - * - * FW_VERSION: 16 byte string - * FW_DATE: 16 byte string (only 14 bytes used) - * UCODE_VERSION: 4 byte version code - * UCODE_DATE: 5 bytes code code - * ADDAPTER_MAC: 6 byte MAC address - * RTC: 4 byte clock - */ -enum { - IPW_ORD_STAT_FW_VERSION = IPW_ORD_TABLE_2_MASK | 1, - IPW_ORD_STAT_FW_DATE, - IPW_ORD_STAT_UCODE_VERSION, - IPW_ORD_STAT_UCODE_DATE, - IPW_ORD_STAT_ADAPTER_MAC, - IPW_ORD_STAT_RTC, - IPW_ORD_TABLE_2_LAST -}; - -/* Table 3 */ -enum { - IPW_ORD_STAT_TX_PACKET = IPW_ORD_TABLE_3_MASK | 0, - IPW_ORD_STAT_TX_PACKET_FAILURE, - IPW_ORD_STAT_TX_PACKET_SUCCESS, - IPW_ORD_STAT_TX_PACKET_ABORTED, - IPW_ORD_TABLE_3_LAST -}; - -/* Table 4 */ -enum { - IPW_ORD_TABLE_4_LAST = IPW_ORD_TABLE_4_MASK -}; - -/* Table 5 */ -enum { - IPW_ORD_STAT_AVAILABLE_AP_COUNT = IPW_ORD_TABLE_5_MASK, - IPW_ORD_STAT_AP_ASSNS, - IPW_ORD_STAT_ROAM, - IPW_ORD_STAT_ROAM_CAUSE_MISSED_BEACONS, - IPW_ORD_STAT_ROAM_CAUSE_UNASSOC, - IPW_ORD_STAT_ROAM_CAUSE_RSSI, - IPW_ORD_STAT_ROAM_CAUSE_LINK_QUALITY, - IPW_ORD_STAT_ROAM_CAUSE_AP_LOAD_BALANCE, - IPW_ORD_STAT_ROAM_CAUSE_AP_NO_TX, - IPW_ORD_STAT_LINK_UP, - IPW_ORD_STAT_LINK_DOWN, - IPW_ORD_ANTENNA_DIVERSITY, - IPW_ORD_CURR_FREQ, - IPW_ORD_TABLE_5_LAST -}; - -/* Table 6 */ -enum { - IPW_ORD_COUNTRY_CODE = IPW_ORD_TABLE_6_MASK, - IPW_ORD_CURR_BSSID, - IPW_ORD_CURR_SSID, - IPW_ORD_TABLE_6_LAST -}; - -/* Table 7 */ -enum { - IPW_ORD_STAT_PERCENT_MISSED_BEACONS = IPW_ORD_TABLE_7_MASK, - IPW_ORD_STAT_PERCENT_TX_RETRIES, - IPW_ORD_STAT_PERCENT_LINK_QUALITY, - IPW_ORD_STAT_CURR_RSSI_DBM, - IPW_ORD_TABLE_7_LAST -}; - -#define IPW_ERROR_LOG (IPW_SHARED_LOWER_BOUND + 0x410) -#define IPW_EVENT_LOG (IPW_SHARED_LOWER_BOUND + 0x414) -#define IPW_ORDINALS_TABLE_LOWER (IPW_SHARED_LOWER_BOUND + 0x500) -#define IPW_ORDINALS_TABLE_0 (IPW_SHARED_LOWER_BOUND + 0x180) -#define IPW_ORDINALS_TABLE_1 (IPW_SHARED_LOWER_BOUND + 0x184) -#define IPW_ORDINALS_TABLE_2 (IPW_SHARED_LOWER_BOUND + 0x188) -#define IPW_MEM_FIXED_OVERRIDE (IPW_SHARED_LOWER_BOUND + 0x41C) - -struct ipw_fixed_rate { - __le16 tx_rates; - __le16 reserved; -} __packed; - -#define IPW_INDIRECT_ADDR_MASK (~0x3ul) - -struct host_cmd { - u8 cmd; - u8 len; - u16 reserved; - u32 *param; -} __packed; /* XXX */ - -struct cmdlog_host_cmd { - u8 cmd; - u8 len; - __le16 reserved; - char param[124]; -} __packed; - -struct ipw_cmd_log { - unsigned long jiffies; - int retcode; - struct cmdlog_host_cmd cmd; -}; - -/* SysConfig command parameters ... */ -/* bt_coexistence param */ -#define CFG_BT_COEXISTENCE_SIGNAL_CHNL 0x01 /* tell BT our chnl # */ -#define CFG_BT_COEXISTENCE_DEFER 0x02 /* defer our Tx if BT traffic */ -#define CFG_BT_COEXISTENCE_KILL 0x04 /* kill our Tx if BT traffic */ -#define CFG_BT_COEXISTENCE_WME_OVER_BT 0x08 /* multimedia extensions */ -#define CFG_BT_COEXISTENCE_OOB 0x10 /* signal BT via out-of-band */ - -/* clear-to-send to self param */ -#define CFG_CTS_TO_ITSELF_ENABLED_MIN 0x00 -#define CFG_CTS_TO_ITSELF_ENABLED_MAX 0x01 -#define CFG_CTS_TO_ITSELF_ENABLED_DEF CFG_CTS_TO_ITSELF_ENABLED_MIN - -/* Antenna diversity param (h/w can select best antenna, based on signal) */ -#define CFG_SYS_ANTENNA_BOTH 0x00 /* NIC selects best antenna */ -#define CFG_SYS_ANTENNA_A 0x01 /* force antenna A */ -#define CFG_SYS_ANTENNA_B 0x03 /* force antenna B */ -#define CFG_SYS_ANTENNA_SLOW_DIV 0x02 /* consider background noise */ - -#define IPW_MAX_CONFIG_RETRIES 10 - -#endif /* __ipw2200_h__ */ diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/ipw2x00/libipw.h deleted file mode 100644 index b0571618c2ed..000000000000 --- a/drivers/net/wireless/ipw2x00/libipw.h +++ /dev/null @@ -1,1008 +0,0 @@ -/* - * Merged with mainline ieee80211.h in Aug 2004. Original ieee802_11 - * remains copyright by the original authors - * - * Portions of the merged code are based on Host AP (software wireless - * LAN access point) driver for Intersil Prism2/2.5/3. - * - * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2003, Jouni Malinen - * - * Adaption to a generic IEEE 802.11 stack by James Ketrenos - * - * Copyright (c) 2004-2005, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. See README and COPYING for - * more details. - * - * API Version History - * 1.0.x -- Initial version - * 1.1.x -- Added radiotap, QoS, TIM, libipw_geo APIs, - * various structure changes, and crypto API init method - */ -#ifndef LIBIPW_H -#define LIBIPW_H -#include /* ETH_ALEN */ -#include /* ARRAY_SIZE */ -#include -#include - -#include -#include - -#define LIBIPW_VERSION "git-1.1.13" - -#define LIBIPW_DATA_LEN 2304 -/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section - 6.2.1.1.2. - - The figure in section 7.1.2 suggests a body size of up to 2312 - bytes is allowed, which is a bit confusing, I suspect this - represents the 2304 bytes of real data, plus a possible 8 bytes of - WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ - -#define LIBIPW_1ADDR_LEN 10 -#define LIBIPW_2ADDR_LEN 16 -#define LIBIPW_3ADDR_LEN 24 -#define LIBIPW_4ADDR_LEN 30 -#define LIBIPW_FCS_LEN 4 -#define LIBIPW_HLEN (LIBIPW_4ADDR_LEN) -#define LIBIPW_FRAME_LEN (LIBIPW_DATA_LEN + LIBIPW_HLEN) - -#define MIN_FRAG_THRESHOLD 256U -#define MAX_FRAG_THRESHOLD 2346U - -/* QOS control */ -#define LIBIPW_QCTL_TID 0x000F - -/* debug macros */ - -#ifdef CONFIG_LIBIPW_DEBUG -extern u32 libipw_debug_level; -#define LIBIPW_DEBUG(level, fmt, args...) \ -do { if (libipw_debug_level & (level)) \ - printk(KERN_DEBUG "libipw: %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) -#else -#define LIBIPW_DEBUG(level, fmt, args...) do {} while (0) -#endif /* CONFIG_LIBIPW_DEBUG */ - -/* - * To use the debug system: - * - * If you are defining a new debug classification, simply add it to the #define - * list here in the form of: - * - * #define LIBIPW_DL_xxxx VALUE - * - * shifting value to the left one bit from the previous entry. xxxx should be - * the name of the classification (for example, WEP) - * - * You then need to either add a LIBIPW_xxxx_DEBUG() macro definition for your - * classification, or use LIBIPW_DEBUG(LIBIPW_DL_xxxx, ...) whenever you want - * to send output to that classification. - * - * To add your debug level to the list of levels seen when you perform - * - * % cat /proc/net/ieee80211/debug_level - * - * you simply need to add your entry to the libipw_debug_level array. - * - * If you do not see debug_level in /proc/net/ieee80211 then you do not have - * CONFIG_LIBIPW_DEBUG defined in your kernel configuration - * - */ - -#define LIBIPW_DL_INFO (1<<0) -#define LIBIPW_DL_WX (1<<1) -#define LIBIPW_DL_SCAN (1<<2) -#define LIBIPW_DL_STATE (1<<3) -#define LIBIPW_DL_MGMT (1<<4) -#define LIBIPW_DL_FRAG (1<<5) -#define LIBIPW_DL_DROP (1<<7) - -#define LIBIPW_DL_TX (1<<8) -#define LIBIPW_DL_RX (1<<9) -#define LIBIPW_DL_QOS (1<<31) - -#define LIBIPW_ERROR(f, a...) printk(KERN_ERR "libipw: " f, ## a) -#define LIBIPW_WARNING(f, a...) printk(KERN_WARNING "libipw: " f, ## a) -#define LIBIPW_DEBUG_INFO(f, a...) LIBIPW_DEBUG(LIBIPW_DL_INFO, f, ## a) - -#define LIBIPW_DEBUG_WX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_WX, f, ## a) -#define LIBIPW_DEBUG_SCAN(f, a...) LIBIPW_DEBUG(LIBIPW_DL_SCAN, f, ## a) -#define LIBIPW_DEBUG_STATE(f, a...) LIBIPW_DEBUG(LIBIPW_DL_STATE, f, ## a) -#define LIBIPW_DEBUG_MGMT(f, a...) LIBIPW_DEBUG(LIBIPW_DL_MGMT, f, ## a) -#define LIBIPW_DEBUG_FRAG(f, a...) LIBIPW_DEBUG(LIBIPW_DL_FRAG, f, ## a) -#define LIBIPW_DEBUG_DROP(f, a...) LIBIPW_DEBUG(LIBIPW_DL_DROP, f, ## a) -#define LIBIPW_DEBUG_TX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_TX, f, ## a) -#define LIBIPW_DEBUG_RX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_RX, f, ## a) -#define LIBIPW_DEBUG_QOS(f, a...) LIBIPW_DEBUG(LIBIPW_DL_QOS, f, ## a) -#include -#include /* ARPHRD_ETHER */ - -#ifndef WIRELESS_SPY -#define WIRELESS_SPY /* enable iwspy support */ -#endif -#include /* new driver API */ - -#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ - -#ifndef ETH_P_80211_RAW -#define ETH_P_80211_RAW (ETH_P_ECONET + 1) -#endif - -/* IEEE 802.11 defines */ - -#define P80211_OUI_LEN 3 - -struct libipw_snap_hdr { - - u8 dsap; /* always 0xAA */ - u8 ssap; /* always 0xAA */ - u8 ctrl; /* always 0x03 */ - u8 oui[P80211_OUI_LEN]; /* organizational universal id */ - -} __packed; - -#define SNAP_SIZE sizeof(struct libipw_snap_hdr) - -#define WLAN_FC_GET_VERS(fc) ((fc) & IEEE80211_FCTL_VERS) -#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE) -#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE) - -#define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG) -#define WLAN_GET_SEQ_SEQ(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) - -#define LIBIPW_STATMASK_SIGNAL (1<<0) -#define LIBIPW_STATMASK_RSSI (1<<1) -#define LIBIPW_STATMASK_NOISE (1<<2) -#define LIBIPW_STATMASK_RATE (1<<3) -#define LIBIPW_STATMASK_WEMASK 0x7 - -#define LIBIPW_CCK_MODULATION (1<<0) -#define LIBIPW_OFDM_MODULATION (1<<1) - -#define LIBIPW_24GHZ_BAND (1<<0) -#define LIBIPW_52GHZ_BAND (1<<1) - -#define LIBIPW_CCK_RATE_1MB 0x02 -#define LIBIPW_CCK_RATE_2MB 0x04 -#define LIBIPW_CCK_RATE_5MB 0x0B -#define LIBIPW_CCK_RATE_11MB 0x16 -#define LIBIPW_OFDM_RATE_6MB 0x0C -#define LIBIPW_OFDM_RATE_9MB 0x12 -#define LIBIPW_OFDM_RATE_12MB 0x18 -#define LIBIPW_OFDM_RATE_18MB 0x24 -#define LIBIPW_OFDM_RATE_24MB 0x30 -#define LIBIPW_OFDM_RATE_36MB 0x48 -#define LIBIPW_OFDM_RATE_48MB 0x60 -#define LIBIPW_OFDM_RATE_54MB 0x6C -#define LIBIPW_BASIC_RATE_MASK 0x80 - -#define LIBIPW_CCK_RATE_1MB_MASK (1<<0) -#define LIBIPW_CCK_RATE_2MB_MASK (1<<1) -#define LIBIPW_CCK_RATE_5MB_MASK (1<<2) -#define LIBIPW_CCK_RATE_11MB_MASK (1<<3) -#define LIBIPW_OFDM_RATE_6MB_MASK (1<<4) -#define LIBIPW_OFDM_RATE_9MB_MASK (1<<5) -#define LIBIPW_OFDM_RATE_12MB_MASK (1<<6) -#define LIBIPW_OFDM_RATE_18MB_MASK (1<<7) -#define LIBIPW_OFDM_RATE_24MB_MASK (1<<8) -#define LIBIPW_OFDM_RATE_36MB_MASK (1<<9) -#define LIBIPW_OFDM_RATE_48MB_MASK (1<<10) -#define LIBIPW_OFDM_RATE_54MB_MASK (1<<11) - -#define LIBIPW_CCK_RATES_MASK 0x0000000F -#define LIBIPW_CCK_BASIC_RATES_MASK (LIBIPW_CCK_RATE_1MB_MASK | \ - LIBIPW_CCK_RATE_2MB_MASK) -#define LIBIPW_CCK_DEFAULT_RATES_MASK (LIBIPW_CCK_BASIC_RATES_MASK | \ - LIBIPW_CCK_RATE_5MB_MASK | \ - LIBIPW_CCK_RATE_11MB_MASK) - -#define LIBIPW_OFDM_RATES_MASK 0x00000FF0 -#define LIBIPW_OFDM_BASIC_RATES_MASK (LIBIPW_OFDM_RATE_6MB_MASK | \ - LIBIPW_OFDM_RATE_12MB_MASK | \ - LIBIPW_OFDM_RATE_24MB_MASK) -#define LIBIPW_OFDM_DEFAULT_RATES_MASK (LIBIPW_OFDM_BASIC_RATES_MASK | \ - LIBIPW_OFDM_RATE_9MB_MASK | \ - LIBIPW_OFDM_RATE_18MB_MASK | \ - LIBIPW_OFDM_RATE_36MB_MASK | \ - LIBIPW_OFDM_RATE_48MB_MASK | \ - LIBIPW_OFDM_RATE_54MB_MASK) -#define LIBIPW_DEFAULT_RATES_MASK (LIBIPW_OFDM_DEFAULT_RATES_MASK | \ - LIBIPW_CCK_DEFAULT_RATES_MASK) - -#define LIBIPW_NUM_OFDM_RATES 8 -#define LIBIPW_NUM_CCK_RATES 4 -#define LIBIPW_OFDM_SHIFT_MASK_A 4 - -/* NOTE: This data is for statistical purposes; not all hardware provides this - * information for frames received. - * For libipw_rx_mgt, you need to set at least the 'len' parameter. - */ -struct libipw_rx_stats { - u32 mac_time; - s8 rssi; - u8 signal; - u8 noise; - u16 rate; /* in 100 kbps */ - u8 received_channel; - u8 control; - u8 mask; - u8 freq; - u16 len; - u64 tsf; - u32 beacon_time; -}; - -/* IEEE 802.11 requires that STA supports concurrent reception of at least - * three fragmented frames. This define can be increased to support more - * concurrent frames, but it should be noted that each entry can consume about - * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ -#define LIBIPW_FRAG_CACHE_LEN 4 - -struct libipw_frag_entry { - unsigned long first_frag_time; - unsigned int seq; - unsigned int last_frag; - struct sk_buff *skb; - u8 src_addr[ETH_ALEN]; - u8 dst_addr[ETH_ALEN]; -}; - -struct libipw_stats { - unsigned int tx_unicast_frames; - unsigned int tx_multicast_frames; - unsigned int tx_fragments; - unsigned int tx_unicast_octets; - unsigned int tx_multicast_octets; - unsigned int tx_deferred_transmissions; - unsigned int tx_single_retry_frames; - unsigned int tx_multiple_retry_frames; - unsigned int tx_retry_limit_exceeded; - unsigned int tx_discards; - unsigned int rx_unicast_frames; - unsigned int rx_multicast_frames; - unsigned int rx_fragments; - unsigned int rx_unicast_octets; - unsigned int rx_multicast_octets; - unsigned int rx_fcs_errors; - unsigned int rx_discards_no_buffer; - unsigned int tx_discards_wrong_sa; - unsigned int rx_discards_undecryptable; - unsigned int rx_message_in_msg_fragments; - unsigned int rx_message_in_bad_msg_fragments; -}; - -struct libipw_device; - -#define SEC_KEY_1 (1<<0) -#define SEC_KEY_2 (1<<1) -#define SEC_KEY_3 (1<<2) -#define SEC_KEY_4 (1<<3) -#define SEC_ACTIVE_KEY (1<<4) -#define SEC_AUTH_MODE (1<<5) -#define SEC_UNICAST_GROUP (1<<6) -#define SEC_LEVEL (1<<7) -#define SEC_ENABLED (1<<8) -#define SEC_ENCRYPT (1<<9) - -#define SEC_LEVEL_0 0 /* None */ -#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */ -#define SEC_LEVEL_2 2 /* Level 1 + TKIP */ -#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */ -#define SEC_LEVEL_3 4 /* Level 2 + CCMP */ - -#define SEC_ALG_NONE 0 -#define SEC_ALG_WEP 1 -#define SEC_ALG_TKIP 2 -#define SEC_ALG_CCMP 3 - -#define WEP_KEYS 4 -#define WEP_KEY_LEN 13 -#define SCM_KEY_LEN 32 -#define SCM_TEMPORAL_KEY_LENGTH 16 - -struct libipw_security { - u16 active_key:2, enabled:1, unicast_uses_group:1, encrypt:1; - u8 auth_mode; - u8 encode_alg[WEP_KEYS]; - u8 key_sizes[WEP_KEYS]; - u8 keys[WEP_KEYS][SCM_KEY_LEN]; - u8 level; - u16 flags; -} __packed; - -/* - - 802.11 data frame from AP - - ,-------------------------------------------------------------------. -Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | - |------|------|---------|---------|---------|------|---------|------| -Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs | - | | tion | (BSSID) | | | ence | data | | - `-------------------------------------------------------------------' - -Total: 28-2340 bytes - -*/ - -#define BEACON_PROBE_SSID_ID_POSITION 12 - -struct libipw_hdr_1addr { - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 payload[0]; -} __packed; - -struct libipw_hdr_2addr { - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 payload[0]; -} __packed; - -struct libipw_hdr_3addr { - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - __le16 seq_ctl; - u8 payload[0]; -} __packed; - -struct libipw_hdr_4addr { - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - __le16 seq_ctl; - u8 addr4[ETH_ALEN]; - u8 payload[0]; -} __packed; - -struct libipw_hdr_3addrqos { - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - __le16 seq_ctl; - u8 payload[0]; - __le16 qos_ctl; -} __packed; - -struct libipw_info_element { - u8 id; - u8 len; - u8 data[0]; -} __packed; - -/* - * These are the data types that can make up management packets - * - u16 auth_algorithm; - u16 auth_sequence; - u16 beacon_interval; - u16 capability; - u8 current_ap[ETH_ALEN]; - u16 listen_interval; - struct { - u16 association_id:14, reserved:2; - } __packed; - u32 time_stamp[2]; - u16 reason; - u16 status; -*/ - -struct libipw_auth { - struct libipw_hdr_3addr header; - __le16 algorithm; - __le16 transaction; - __le16 status; - /* challenge */ - struct libipw_info_element info_element[0]; -} __packed; - -struct libipw_channel_switch { - u8 id; - u8 len; - u8 mode; - u8 channel; - u8 count; -} __packed; - -struct libipw_action { - struct libipw_hdr_3addr header; - u8 category; - u8 action; - union { - struct libipw_action_exchange { - u8 token; - struct libipw_info_element info_element[0]; - } exchange; - struct libipw_channel_switch channel_switch; - - } format; -} __packed; - -struct libipw_disassoc { - struct libipw_hdr_3addr header; - __le16 reason; -} __packed; - -/* Alias deauth for disassoc */ -#define libipw_deauth libipw_disassoc - -struct libipw_probe_request { - struct libipw_hdr_3addr header; - /* SSID, supported rates */ - struct libipw_info_element info_element[0]; -} __packed; - -struct libipw_probe_response { - struct libipw_hdr_3addr header; - __le32 time_stamp[2]; - __le16 beacon_interval; - __le16 capability; - /* SSID, supported rates, FH params, DS params, - * CF params, IBSS params, TIM (if beacon), RSN */ - struct libipw_info_element info_element[0]; -} __packed; - -/* Alias beacon for probe_response */ -#define libipw_beacon libipw_probe_response - -struct libipw_assoc_request { - struct libipw_hdr_3addr header; - __le16 capability; - __le16 listen_interval; - /* SSID, supported rates, RSN */ - struct libipw_info_element info_element[0]; -} __packed; - -struct libipw_reassoc_request { - struct libipw_hdr_3addr header; - __le16 capability; - __le16 listen_interval; - u8 current_ap[ETH_ALEN]; - struct libipw_info_element info_element[0]; -} __packed; - -struct libipw_assoc_response { - struct libipw_hdr_3addr header; - __le16 capability; - __le16 status; - __le16 aid; - /* supported rates */ - struct libipw_info_element info_element[0]; -} __packed; - -struct libipw_txb { - u8 nr_frags; - u8 encrypted; - u8 rts_included; - u8 reserved; - u16 frag_size; - u16 payload_size; - struct sk_buff *fragments[0]; -}; - -/* SWEEP TABLE ENTRIES NUMBER */ -#define MAX_SWEEP_TAB_ENTRIES 42 -#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7 -/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs - * only use 8, and then use extended rates for the remaining supported - * rates. Other APs, however, stick all of their supported rates on the - * main rates information element... */ -#define MAX_RATES_LENGTH ((u8)12) -#define MAX_RATES_EX_LENGTH ((u8)16) -#define MAX_NETWORK_COUNT 128 - -#define CRC_LENGTH 4U - -#define MAX_WPA_IE_LEN 64 - -#define NETWORK_HAS_OFDM (1<<1) -#define NETWORK_HAS_CCK (1<<2) - -/* QoS structure */ -#define NETWORK_HAS_QOS_PARAMETERS (1<<3) -#define NETWORK_HAS_QOS_INFORMATION (1<<4) -#define NETWORK_HAS_QOS_MASK (NETWORK_HAS_QOS_PARAMETERS | \ - NETWORK_HAS_QOS_INFORMATION) - -/* 802.11h */ -#define NETWORK_HAS_POWER_CONSTRAINT (1<<5) -#define NETWORK_HAS_CSA (1<<6) -#define NETWORK_HAS_QUIET (1<<7) -#define NETWORK_HAS_IBSS_DFS (1<<8) -#define NETWORK_HAS_TPC_REPORT (1<<9) - -#define NETWORK_HAS_ERP_VALUE (1<<10) - -#define QOS_QUEUE_NUM 4 -#define QOS_OUI_LEN 3 -#define QOS_OUI_TYPE 2 -#define QOS_ELEMENT_ID 221 -#define QOS_OUI_INFO_SUB_TYPE 0 -#define QOS_OUI_PARAM_SUB_TYPE 1 -#define QOS_VERSION_1 1 -#define QOS_AIFSN_MIN_VALUE 2 - -struct libipw_qos_information_element { - u8 elementID; - u8 length; - u8 qui[QOS_OUI_LEN]; - u8 qui_type; - u8 qui_subtype; - u8 version; - u8 ac_info; -} __packed; - -struct libipw_qos_ac_parameter { - u8 aci_aifsn; - u8 ecw_min_max; - __le16 tx_op_limit; -} __packed; - -struct libipw_qos_parameter_info { - struct libipw_qos_information_element info_element; - u8 reserved; - struct libipw_qos_ac_parameter ac_params_record[QOS_QUEUE_NUM]; -} __packed; - -struct libipw_qos_parameters { - __le16 cw_min[QOS_QUEUE_NUM]; - __le16 cw_max[QOS_QUEUE_NUM]; - u8 aifs[QOS_QUEUE_NUM]; - u8 flag[QOS_QUEUE_NUM]; - __le16 tx_op_limit[QOS_QUEUE_NUM]; -} __packed; - -struct libipw_qos_data { - struct libipw_qos_parameters parameters; - int active; - int supported; - u8 param_count; - u8 old_param_count; -}; - -struct libipw_tim_parameters { - u8 tim_count; - u8 tim_period; -} __packed; - -/*******************************************************/ - -struct libipw_tpc_report { - u8 transmit_power; - u8 link_margin; -} __packed; - -struct libipw_channel_map { - u8 channel; - u8 map; -} __packed; - -struct libipw_ibss_dfs { - struct libipw_info_element ie; - u8 owner[ETH_ALEN]; - u8 recovery_interval; - struct libipw_channel_map channel_map[0]; -}; - -struct libipw_csa { - u8 mode; - u8 channel; - u8 count; -} __packed; - -struct libipw_quiet { - u8 count; - u8 period; - u8 duration; - u8 offset; -} __packed; - -struct libipw_network { - /* These entries are used to identify a unique network */ - u8 bssid[ETH_ALEN]; - u8 channel; - /* Ensure null-terminated for any debug msgs */ - u8 ssid[IW_ESSID_MAX_SIZE + 1]; - u8 ssid_len; - - struct libipw_qos_data qos_data; - - /* These are network statistics */ - struct libipw_rx_stats stats; - u16 capability; - u8 rates[MAX_RATES_LENGTH]; - u8 rates_len; - u8 rates_ex[MAX_RATES_EX_LENGTH]; - u8 rates_ex_len; - unsigned long last_scanned; - u8 mode; - u32 flags; - u32 last_associate; - u32 time_stamp[2]; - u16 beacon_interval; - u16 listen_interval; - u16 atim_window; - u8 erp_value; - u8 wpa_ie[MAX_WPA_IE_LEN]; - size_t wpa_ie_len; - u8 rsn_ie[MAX_WPA_IE_LEN]; - size_t rsn_ie_len; - struct libipw_tim_parameters tim; - - /* 802.11h info */ - - /* Power Constraint - mandatory if spctrm mgmt required */ - u8 power_constraint; - - /* TPC Report - mandatory if spctrm mgmt required */ - struct libipw_tpc_report tpc_report; - - /* Channel Switch Announcement - optional if spctrm mgmt required */ - struct libipw_csa csa; - - /* Quiet - optional if spctrm mgmt required */ - struct libipw_quiet quiet; - - struct list_head list; -}; - -enum libipw_state { - LIBIPW_UNINITIALIZED = 0, - LIBIPW_INITIALIZED, - LIBIPW_ASSOCIATING, - LIBIPW_ASSOCIATED, - LIBIPW_AUTHENTICATING, - LIBIPW_AUTHENTICATED, - LIBIPW_SHUTDOWN -}; - -#define DEFAULT_MAX_SCAN_AGE (15 * HZ) -#define DEFAULT_FTS 2346 - -#define CFG_LIBIPW_RESERVE_FCS (1<<0) -#define CFG_LIBIPW_COMPUTE_FCS (1<<1) -#define CFG_LIBIPW_RTS (1<<2) - -#define LIBIPW_24GHZ_MIN_CHANNEL 1 -#define LIBIPW_24GHZ_MAX_CHANNEL 14 -#define LIBIPW_24GHZ_CHANNELS (LIBIPW_24GHZ_MAX_CHANNEL - \ - LIBIPW_24GHZ_MIN_CHANNEL + 1) - -#define LIBIPW_52GHZ_MIN_CHANNEL 34 -#define LIBIPW_52GHZ_MAX_CHANNEL 165 -#define LIBIPW_52GHZ_CHANNELS (LIBIPW_52GHZ_MAX_CHANNEL - \ - LIBIPW_52GHZ_MIN_CHANNEL + 1) - -enum { - LIBIPW_CH_PASSIVE_ONLY = (1 << 0), - LIBIPW_CH_80211H_RULES = (1 << 1), - LIBIPW_CH_B_ONLY = (1 << 2), - LIBIPW_CH_NO_IBSS = (1 << 3), - LIBIPW_CH_UNIFORM_SPREADING = (1 << 4), - LIBIPW_CH_RADAR_DETECT = (1 << 5), - LIBIPW_CH_INVALID = (1 << 6), -}; - -struct libipw_channel { - u32 freq; /* in MHz */ - u8 channel; - u8 flags; - u8 max_power; /* in dBm */ -}; - -struct libipw_geo { - u8 name[4]; - u8 bg_channels; - u8 a_channels; - struct libipw_channel bg[LIBIPW_24GHZ_CHANNELS]; - struct libipw_channel a[LIBIPW_52GHZ_CHANNELS]; -}; - -struct libipw_device { - struct net_device *dev; - struct wireless_dev wdev; - struct libipw_security sec; - - /* Bookkeeping structures */ - struct libipw_stats ieee_stats; - - struct libipw_geo geo; - struct ieee80211_supported_band bg_band; - struct ieee80211_supported_band a_band; - - /* Probe / Beacon management */ - struct list_head network_free_list; - struct list_head network_list; - struct libipw_network *networks[MAX_NETWORK_COUNT]; - int scans; - int scan_age; - - int iw_mode; /* operating mode (IW_MODE_*) */ - struct iw_spy_data spy_data; /* iwspy support */ - - spinlock_t lock; - - int tx_headroom; /* Set to size of any additional room needed at front - * of allocated Tx SKBs */ - u32 config; - - /* WEP and other encryption related settings at the device level */ - int open_wep; /* Set to 1 to allow unencrypted frames */ - - /* If the host performs {en,de}cryption, then set to 1 */ - int host_encrypt; - int host_encrypt_msdu; - int host_decrypt; - /* host performs multicast decryption */ - int host_mc_decrypt; - - /* host should strip IV and ICV from protected frames */ - /* meaningful only when hardware decryption is being used */ - int host_strip_iv_icv; - - int host_open_frag; - int ieee802_1x; /* is IEEE 802.1X used */ - - /* WPA data */ - int wpa_enabled; - int drop_unencrypted; - int privacy_invoked; - size_t wpa_ie_len; - u8 *wpa_ie; - - struct lib80211_crypt_info crypt_info; - - int bcrx_sta_key; /* use individual keys to override default keys even - * with RX of broad/multicast frames */ - - /* Fragmentation structures */ - struct libipw_frag_entry frag_cache[LIBIPW_FRAG_CACHE_LEN]; - unsigned int frag_next_idx; - u16 fts; /* Fragmentation Threshold */ - u16 rts; /* RTS threshold */ - - /* Association info */ - u8 bssid[ETH_ALEN]; - - enum libipw_state state; - - int mode; /* A, B, G */ - int modulation; /* CCK, OFDM */ - int freq_band; /* 2.4Ghz, 5.2Ghz, Mixed */ - int abg_true; /* ABG flag */ - - int perfect_rssi; - int worst_rssi; - - u16 prev_seq_ctl; /* used to drop duplicate frames */ - - /* Callback functions */ - void (*set_security) (struct net_device * dev, - struct libipw_security * sec); - netdev_tx_t (*hard_start_xmit) (struct libipw_txb * txb, - struct net_device * dev, int pri); - int (*is_queue_full) (struct net_device * dev, int pri); - - int (*handle_management) (struct net_device * dev, - struct libipw_network * network, u16 type); - int (*is_qos_active) (struct net_device *dev, struct sk_buff *skb); - - /* Typical STA methods */ - int (*handle_auth) (struct net_device * dev, - struct libipw_auth * auth); - int (*handle_deauth) (struct net_device * dev, - struct libipw_deauth * auth); - int (*handle_action) (struct net_device * dev, - struct libipw_action * action, - struct libipw_rx_stats * stats); - int (*handle_disassoc) (struct net_device * dev, - struct libipw_disassoc * assoc); - int (*handle_beacon) (struct net_device * dev, - struct libipw_beacon * beacon, - struct libipw_network * network); - int (*handle_probe_response) (struct net_device * dev, - struct libipw_probe_response * resp, - struct libipw_network * network); - int (*handle_probe_request) (struct net_device * dev, - struct libipw_probe_request * req, - struct libipw_rx_stats * stats); - int (*handle_assoc_response) (struct net_device * dev, - struct libipw_assoc_response * resp, - struct libipw_network * network); - - /* Typical AP methods */ - int (*handle_assoc_request) (struct net_device * dev); - int (*handle_reassoc_request) (struct net_device * dev, - struct libipw_reassoc_request * req); - - /* This must be the last item so that it points to the data - * allocated beyond this structure by alloc_libipw */ - u8 priv[0]; -}; - -#define IEEE_A (1<<0) -#define IEEE_B (1<<1) -#define IEEE_G (1<<2) -#define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G) - -static inline void *libipw_priv(struct net_device *dev) -{ - return ((struct libipw_device *)netdev_priv(dev))->priv; -} - -static inline int libipw_is_valid_mode(struct libipw_device *ieee, - int mode) -{ - /* - * It is possible for both access points and our device to support - * combinations of modes, so as long as there is one valid combination - * of ap/device supported modes, then return success - * - */ - if ((mode & IEEE_A) && - (ieee->modulation & LIBIPW_OFDM_MODULATION) && - (ieee->freq_band & LIBIPW_52GHZ_BAND)) - return 1; - - if ((mode & IEEE_G) && - (ieee->modulation & LIBIPW_OFDM_MODULATION) && - (ieee->freq_band & LIBIPW_24GHZ_BAND)) - return 1; - - if ((mode & IEEE_B) && - (ieee->modulation & LIBIPW_CCK_MODULATION) && - (ieee->freq_band & LIBIPW_24GHZ_BAND)) - return 1; - - return 0; -} - -static inline int libipw_get_hdrlen(u16 fc) -{ - int hdrlen = LIBIPW_3ADDR_LEN; - u16 stype = WLAN_FC_GET_STYPE(fc); - - switch (WLAN_FC_GET_TYPE(fc)) { - case IEEE80211_FTYPE_DATA: - if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS)) - hdrlen = LIBIPW_4ADDR_LEN; - if (stype & IEEE80211_STYPE_QOS_DATA) - hdrlen += 2; - break; - case IEEE80211_FTYPE_CTL: - switch (WLAN_FC_GET_STYPE(fc)) { - case IEEE80211_STYPE_CTS: - case IEEE80211_STYPE_ACK: - hdrlen = LIBIPW_1ADDR_LEN; - break; - default: - hdrlen = LIBIPW_2ADDR_LEN; - break; - } - break; - } - - return hdrlen; -} - -static inline u8 *libipw_get_payload(struct ieee80211_hdr *hdr) -{ - switch (libipw_get_hdrlen(le16_to_cpu(hdr->frame_control))) { - case LIBIPW_1ADDR_LEN: - return ((struct libipw_hdr_1addr *)hdr)->payload; - case LIBIPW_2ADDR_LEN: - return ((struct libipw_hdr_2addr *)hdr)->payload; - case LIBIPW_3ADDR_LEN: - return ((struct libipw_hdr_3addr *)hdr)->payload; - case LIBIPW_4ADDR_LEN: - return ((struct libipw_hdr_4addr *)hdr)->payload; - } - return NULL; -} - -static inline int libipw_is_ofdm_rate(u8 rate) -{ - switch (rate & ~LIBIPW_BASIC_RATE_MASK) { - case LIBIPW_OFDM_RATE_6MB: - case LIBIPW_OFDM_RATE_9MB: - case LIBIPW_OFDM_RATE_12MB: - case LIBIPW_OFDM_RATE_18MB: - case LIBIPW_OFDM_RATE_24MB: - case LIBIPW_OFDM_RATE_36MB: - case LIBIPW_OFDM_RATE_48MB: - case LIBIPW_OFDM_RATE_54MB: - return 1; - } - return 0; -} - -static inline int libipw_is_cck_rate(u8 rate) -{ - switch (rate & ~LIBIPW_BASIC_RATE_MASK) { - case LIBIPW_CCK_RATE_1MB: - case LIBIPW_CCK_RATE_2MB: - case LIBIPW_CCK_RATE_5MB: - case LIBIPW_CCK_RATE_11MB: - return 1; - } - return 0; -} - -/* libipw.c */ -void free_libipw(struct net_device *dev, int monitor); -struct net_device *alloc_libipw(int sizeof_priv, int monitor); -int libipw_change_mtu(struct net_device *dev, int new_mtu); - -void libipw_networks_age(struct libipw_device *ieee, unsigned long age_secs); - -int libipw_set_encryption(struct libipw_device *ieee); - -/* libipw_tx.c */ -netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev); -void libipw_txb_free(struct libipw_txb *); - -/* libipw_rx.c */ -void libipw_rx_any(struct libipw_device *ieee, struct sk_buff *skb, - struct libipw_rx_stats *stats); -int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, - struct libipw_rx_stats *rx_stats); -/* make sure to set stats->len */ -void libipw_rx_mgt(struct libipw_device *ieee, struct libipw_hdr_4addr *header, - struct libipw_rx_stats *stats); - -/* libipw_geo.c */ -const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee); -void libipw_set_geo(struct libipw_device *ieee, const struct libipw_geo *geo); - -int libipw_is_valid_channel(struct libipw_device *ieee, u8 channel); -int libipw_channel_to_index(struct libipw_device *ieee, u8 channel); -u8 libipw_freq_to_channel(struct libipw_device *ieee, u32 freq); -u8 libipw_get_channel_flags(struct libipw_device *ieee, u8 channel); -const struct libipw_channel *libipw_get_channel(struct libipw_device *ieee, - u8 channel); -u32 libipw_channel_to_freq(struct libipw_device *ieee, u8 channel); - -/* libipw_wx.c */ -int libipw_wx_get_scan(struct libipw_device *ieee, struct iw_request_info *info, - union iwreq_data *wrqu, char *key); -int libipw_wx_set_encode(struct libipw_device *ieee, - struct iw_request_info *info, union iwreq_data *wrqu, - char *key); -int libipw_wx_get_encode(struct libipw_device *ieee, - struct iw_request_info *info, union iwreq_data *wrqu, - char *key); -int libipw_wx_set_encodeext(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra); -int libipw_wx_get_encodeext(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra); - -static inline void libipw_increment_scans(struct libipw_device *ieee) -{ - ieee->scans++; -} - -static inline int libipw_get_scans(struct libipw_device *ieee) -{ - return ieee->scans; -} - -#endif /* LIBIPW_H */ diff --git a/drivers/net/wireless/ipw2x00/libipw_geo.c b/drivers/net/wireless/ipw2x00/libipw_geo.c deleted file mode 100644 index 218f2a32de21..000000000000 --- a/drivers/net/wireless/ipw2x00/libipw_geo.c +++ /dev/null @@ -1,193 +0,0 @@ -/****************************************************************************** - - Copyright(c) 2005 Intel Corporation. All rights reserved. - - This program is free software; you can redistribute it and/or modify it - under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - The full GNU General Public License is included in this distribution in the - file called LICENSE. - - Contact Information: - Intel Linux Wireless - Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - -******************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libipw.h" - -int libipw_is_valid_channel(struct libipw_device *ieee, u8 channel) -{ - int i; - - /* Driver needs to initialize the geography map before using - * these helper functions */ - if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) - return 0; - - if (ieee->freq_band & LIBIPW_24GHZ_BAND) - for (i = 0; i < ieee->geo.bg_channels; i++) - /* NOTE: If G mode is currently supported but - * this is a B only channel, we don't see it - * as valid. */ - if ((ieee->geo.bg[i].channel == channel) && - !(ieee->geo.bg[i].flags & LIBIPW_CH_INVALID) && - (!(ieee->mode & IEEE_G) || - !(ieee->geo.bg[i].flags & LIBIPW_CH_B_ONLY))) - return LIBIPW_24GHZ_BAND; - - if (ieee->freq_band & LIBIPW_52GHZ_BAND) - for (i = 0; i < ieee->geo.a_channels; i++) - if ((ieee->geo.a[i].channel == channel) && - !(ieee->geo.a[i].flags & LIBIPW_CH_INVALID)) - return LIBIPW_52GHZ_BAND; - - return 0; -} - -int libipw_channel_to_index(struct libipw_device *ieee, u8 channel) -{ - int i; - - /* Driver needs to initialize the geography map before using - * these helper functions */ - if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) - return -1; - - if (ieee->freq_band & LIBIPW_24GHZ_BAND) - for (i = 0; i < ieee->geo.bg_channels; i++) - if (ieee->geo.bg[i].channel == channel) - return i; - - if (ieee->freq_band & LIBIPW_52GHZ_BAND) - for (i = 0; i < ieee->geo.a_channels; i++) - if (ieee->geo.a[i].channel == channel) - return i; - - return -1; -} - -u32 libipw_channel_to_freq(struct libipw_device * ieee, u8 channel) -{ - const struct libipw_channel * ch; - - /* Driver needs to initialize the geography map before using - * these helper functions */ - if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) - return 0; - - ch = libipw_get_channel(ieee, channel); - if (!ch->channel) - return 0; - return ch->freq; -} - -u8 libipw_freq_to_channel(struct libipw_device * ieee, u32 freq) -{ - int i; - - /* Driver needs to initialize the geography map before using - * these helper functions */ - if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) - return 0; - - freq /= 100000; - - if (ieee->freq_band & LIBIPW_24GHZ_BAND) - for (i = 0; i < ieee->geo.bg_channels; i++) - if (ieee->geo.bg[i].freq == freq) - return ieee->geo.bg[i].channel; - - if (ieee->freq_band & LIBIPW_52GHZ_BAND) - for (i = 0; i < ieee->geo.a_channels; i++) - if (ieee->geo.a[i].freq == freq) - return ieee->geo.a[i].channel; - - return 0; -} - -void libipw_set_geo(struct libipw_device *ieee, - const struct libipw_geo *geo) -{ - memcpy(ieee->geo.name, geo->name, 3); - ieee->geo.name[3] = '\0'; - ieee->geo.bg_channels = geo->bg_channels; - ieee->geo.a_channels = geo->a_channels; - memcpy(ieee->geo.bg, geo->bg, geo->bg_channels * - sizeof(struct libipw_channel)); - memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels * - sizeof(struct libipw_channel)); -} - -const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee) -{ - return &ieee->geo; -} - -u8 libipw_get_channel_flags(struct libipw_device * ieee, u8 channel) -{ - int index = libipw_channel_to_index(ieee, channel); - - if (index == -1) - return LIBIPW_CH_INVALID; - - if (channel <= LIBIPW_24GHZ_CHANNELS) - return ieee->geo.bg[index].flags; - - return ieee->geo.a[index].flags; -} - -static const struct libipw_channel bad_channel = { - .channel = 0, - .flags = LIBIPW_CH_INVALID, - .max_power = 0, -}; - -const struct libipw_channel *libipw_get_channel(struct libipw_device - *ieee, u8 channel) -{ - int index = libipw_channel_to_index(ieee, channel); - - if (index == -1) - return &bad_channel; - - if (channel <= LIBIPW_24GHZ_CHANNELS) - return &ieee->geo.bg[index]; - - return &ieee->geo.a[index]; -} - -EXPORT_SYMBOL(libipw_get_channel); -EXPORT_SYMBOL(libipw_get_channel_flags); -EXPORT_SYMBOL(libipw_is_valid_channel); -EXPORT_SYMBOL(libipw_freq_to_channel); -EXPORT_SYMBOL(libipw_channel_to_freq); -EXPORT_SYMBOL(libipw_channel_to_index); -EXPORT_SYMBOL(libipw_set_geo); -EXPORT_SYMBOL(libipw_get_geo); diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c deleted file mode 100644 index 60f28740f6af..000000000000 --- a/drivers/net/wireless/ipw2x00/libipw_module.c +++ /dev/null @@ -1,321 +0,0 @@ -/******************************************************************************* - - Copyright(c) 2004-2005 Intel Corporation. All rights reserved. - - Portions of this file are based on the WEP enablement code provided by the - Host AP project hostap-drivers v0.1.3 - Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - - Copyright (c) 2002-2003, Jouni Malinen - - This program is free software; you can redistribute it and/or modify it - under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - The full GNU General Public License is included in this distribution in the - file called LICENSE. - - Contact Information: - Intel Linux Wireless - Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - -*******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libipw.h" - -#define DRV_DESCRIPTION "802.11 data/management/control stack" -#define DRV_NAME "libipw" -#define DRV_PROCNAME "ieee80211" -#define DRV_VERSION LIBIPW_VERSION -#define DRV_COPYRIGHT "Copyright (C) 2004-2005 Intel Corporation " - -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_AUTHOR(DRV_COPYRIGHT); -MODULE_LICENSE("GPL"); - -static struct cfg80211_ops libipw_config_ops = { }; -static void *libipw_wiphy_privid = &libipw_wiphy_privid; - -static int libipw_networks_allocate(struct libipw_device *ieee) -{ - int i, j; - - for (i = 0; i < MAX_NETWORK_COUNT; i++) { - ieee->networks[i] = kzalloc(sizeof(struct libipw_network), - GFP_KERNEL); - if (!ieee->networks[i]) { - LIBIPW_ERROR("Out of memory allocating beacons\n"); - for (j = 0; j < i; j++) - kfree(ieee->networks[j]); - return -ENOMEM; - } - } - - return 0; -} - -static inline void libipw_networks_free(struct libipw_device *ieee) -{ - int i; - - for (i = 0; i < MAX_NETWORK_COUNT; i++) - kfree(ieee->networks[i]); -} - -void libipw_networks_age(struct libipw_device *ieee, - unsigned long age_secs) -{ - struct libipw_network *network = NULL; - unsigned long flags; - unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); - - spin_lock_irqsave(&ieee->lock, flags); - list_for_each_entry(network, &ieee->network_list, list) { - network->last_scanned -= age_jiffies; - } - spin_unlock_irqrestore(&ieee->lock, flags); -} -EXPORT_SYMBOL(libipw_networks_age); - -static void libipw_networks_initialize(struct libipw_device *ieee) -{ - int i; - - INIT_LIST_HEAD(&ieee->network_free_list); - INIT_LIST_HEAD(&ieee->network_list); - for (i = 0; i < MAX_NETWORK_COUNT; i++) - list_add_tail(&ieee->networks[i]->list, - &ieee->network_free_list); -} - -int libipw_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > LIBIPW_DATA_LEN)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} -EXPORT_SYMBOL(libipw_change_mtu); - -struct net_device *alloc_libipw(int sizeof_priv, int monitor) -{ - struct libipw_device *ieee; - struct net_device *dev; - int err; - - LIBIPW_DEBUG_INFO("Initializing...\n"); - - dev = alloc_etherdev(sizeof(struct libipw_device) + sizeof_priv); - if (!dev) - goto failed; - - ieee = netdev_priv(dev); - - ieee->dev = dev; - - if (!monitor) { - ieee->wdev.wiphy = wiphy_new(&libipw_config_ops, 0); - if (!ieee->wdev.wiphy) { - LIBIPW_ERROR("Unable to allocate wiphy.\n"); - goto failed_free_netdev; - } - - ieee->dev->ieee80211_ptr = &ieee->wdev; - ieee->wdev.iftype = NL80211_IFTYPE_STATION; - - /* Fill-out wiphy structure bits we know... Not enough info - here to call set_wiphy_dev or set MAC address or channel info - -- have to do that in ->ndo_init... */ - ieee->wdev.wiphy->privid = libipw_wiphy_privid; - - ieee->wdev.wiphy->max_scan_ssids = 1; - ieee->wdev.wiphy->max_scan_ie_len = 0; - ieee->wdev.wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) - | BIT(NL80211_IFTYPE_ADHOC); - } - - err = libipw_networks_allocate(ieee); - if (err) { - LIBIPW_ERROR("Unable to allocate beacon storage: %d\n", err); - goto failed_free_wiphy; - } - libipw_networks_initialize(ieee); - - /* Default fragmentation threshold is maximum payload size */ - ieee->fts = DEFAULT_FTS; - ieee->rts = DEFAULT_FTS; - ieee->scan_age = DEFAULT_MAX_SCAN_AGE; - ieee->open_wep = 1; - - /* Default to enabling full open WEP with host based encrypt/decrypt */ - ieee->host_encrypt = 1; - ieee->host_decrypt = 1; - ieee->host_mc_decrypt = 1; - - /* Host fragmentation in Open mode. Default is enabled. - * Note: host fragmentation is always enabled if host encryption - * is enabled. For cards can do hardware encryption, they must do - * hardware fragmentation as well. So we don't need a variable - * like host_enc_frag. */ - ieee->host_open_frag = 1; - ieee->ieee802_1x = 1; /* Default to supporting 802.1x */ - - spin_lock_init(&ieee->lock); - - lib80211_crypt_info_init(&ieee->crypt_info, dev->name, &ieee->lock); - - ieee->wpa_enabled = 0; - ieee->drop_unencrypted = 0; - ieee->privacy_invoked = 0; - - return dev; - -failed_free_wiphy: - if (!monitor) - wiphy_free(ieee->wdev.wiphy); -failed_free_netdev: - free_netdev(dev); -failed: - return NULL; -} -EXPORT_SYMBOL(alloc_libipw); - -void free_libipw(struct net_device *dev, int monitor) -{ - struct libipw_device *ieee = netdev_priv(dev); - - lib80211_crypt_info_free(&ieee->crypt_info); - - libipw_networks_free(ieee); - - /* free cfg80211 resources */ - if (!monitor) - wiphy_free(ieee->wdev.wiphy); - - free_netdev(dev); -} -EXPORT_SYMBOL(free_libipw); - -#ifdef CONFIG_LIBIPW_DEBUG - -static int debug = 0; -u32 libipw_debug_level = 0; -EXPORT_SYMBOL_GPL(libipw_debug_level); -static struct proc_dir_entry *libipw_proc = NULL; - -static int debug_level_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "0x%08X\n", libipw_debug_level); - return 0; -} - -static int debug_level_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, debug_level_proc_show, NULL); -} - -static ssize_t debug_level_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *pos) -{ - char buf[] = "0x00000000\n"; - size_t len = min(sizeof(buf) - 1, count); - unsigned long val; - - if (copy_from_user(buf, buffer, len)) - return count; - buf[len] = 0; - if (sscanf(buf, "%li", &val) != 1) - printk(KERN_INFO DRV_NAME - ": %s is not in hex or decimal form.\n", buf); - else - libipw_debug_level = val; - - return strnlen(buf, len); -} - -static const struct file_operations debug_level_proc_fops = { - .owner = THIS_MODULE, - .open = debug_level_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = debug_level_proc_write, -}; -#endif /* CONFIG_LIBIPW_DEBUG */ - -static int __init libipw_init(void) -{ -#ifdef CONFIG_LIBIPW_DEBUG - struct proc_dir_entry *e; - - libipw_debug_level = debug; - libipw_proc = proc_mkdir(DRV_PROCNAME, init_net.proc_net); - if (libipw_proc == NULL) { - LIBIPW_ERROR("Unable to create " DRV_PROCNAME - " proc directory\n"); - return -EIO; - } - e = proc_create("debug_level", S_IRUGO | S_IWUSR, libipw_proc, - &debug_level_proc_fops); - if (!e) { - remove_proc_entry(DRV_PROCNAME, init_net.proc_net); - libipw_proc = NULL; - return -EIO; - } -#endif /* CONFIG_LIBIPW_DEBUG */ - - printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n"); - printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n"); - - return 0; -} - -static void __exit libipw_exit(void) -{ -#ifdef CONFIG_LIBIPW_DEBUG - if (libipw_proc) { - remove_proc_entry("debug_level", libipw_proc); - remove_proc_entry(DRV_PROCNAME, init_net.proc_net); - libipw_proc = NULL; - } -#endif /* CONFIG_LIBIPW_DEBUG */ -} - -#ifdef CONFIG_LIBIPW_DEBUG -#include -module_param(debug, int, 0444); -MODULE_PARM_DESC(debug, "debug output mask"); -#endif /* CONFIG_LIBIPW_DEBUG */ - -module_exit(libipw_exit); -module_init(libipw_init); diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/ipw2x00/libipw_rx.c deleted file mode 100644 index cef7f7d79cd9..000000000000 --- a/drivers/net/wireless/ipw2x00/libipw_rx.c +++ /dev/null @@ -1,1765 +0,0 @@ -/* - * Original code based Host AP (software wireless LAN access point) driver - * for Intersil Prism2/2.5/3 - hostap.o module, common routines - * - * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2003, Jouni Malinen - * Copyright (c) 2004-2005, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. See README and COPYING for - * more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "libipw.h" - -static void libipw_monitor_rx(struct libipw_device *ieee, - struct sk_buff *skb, - struct libipw_rx_stats *rx_stats) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - u16 fc = le16_to_cpu(hdr->frame_control); - - skb->dev = ieee->dev; - skb_reset_mac_header(skb); - skb_pull(skb, libipw_get_hdrlen(fc)); - skb->pkt_type = PACKET_OTHERHOST; - skb->protocol = htons(ETH_P_80211_RAW); - memset(skb->cb, 0, sizeof(skb->cb)); - netif_rx(skb); -} - -/* Called only as a tasklet (software IRQ) */ -static struct libipw_frag_entry *libipw_frag_cache_find(struct - libipw_device - *ieee, - unsigned int seq, - unsigned int frag, - u8 * src, - u8 * dst) -{ - struct libipw_frag_entry *entry; - int i; - - for (i = 0; i < LIBIPW_FRAG_CACHE_LEN; i++) { - entry = &ieee->frag_cache[i]; - if (entry->skb != NULL && - time_after(jiffies, entry->first_frag_time + 2 * HZ)) { - LIBIPW_DEBUG_FRAG("expiring fragment cache entry " - "seq=%u last_frag=%u\n", - entry->seq, entry->last_frag); - dev_kfree_skb_any(entry->skb); - entry->skb = NULL; - } - - if (entry->skb != NULL && entry->seq == seq && - (entry->last_frag + 1 == frag || frag == -1) && - ether_addr_equal(entry->src_addr, src) && - ether_addr_equal(entry->dst_addr, dst)) - return entry; - } - - return NULL; -} - -/* Called only as a tasklet (software IRQ) */ -static struct sk_buff *libipw_frag_cache_get(struct libipw_device *ieee, - struct libipw_hdr_4addr *hdr) -{ - struct sk_buff *skb = NULL; - u16 sc; - unsigned int frag, seq; - struct libipw_frag_entry *entry; - - sc = le16_to_cpu(hdr->seq_ctl); - frag = WLAN_GET_SEQ_FRAG(sc); - seq = WLAN_GET_SEQ_SEQ(sc); - - if (frag == 0) { - /* Reserve enough space to fit maximum frame length */ - skb = dev_alloc_skb(ieee->dev->mtu + - sizeof(struct libipw_hdr_4addr) + - 8 /* LLC */ + - 2 /* alignment */ + - 8 /* WEP */ + ETH_ALEN /* WDS */ ); - if (skb == NULL) - return NULL; - - entry = &ieee->frag_cache[ieee->frag_next_idx]; - ieee->frag_next_idx++; - if (ieee->frag_next_idx >= LIBIPW_FRAG_CACHE_LEN) - ieee->frag_next_idx = 0; - - if (entry->skb != NULL) - dev_kfree_skb_any(entry->skb); - - entry->first_frag_time = jiffies; - entry->seq = seq; - entry->last_frag = frag; - entry->skb = skb; - memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); - memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); - } else { - /* received a fragment of a frame for which the head fragment - * should have already been received */ - entry = libipw_frag_cache_find(ieee, seq, frag, hdr->addr2, - hdr->addr1); - if (entry != NULL) { - entry->last_frag = frag; - skb = entry->skb; - } - } - - return skb; -} - -/* Called only as a tasklet (software IRQ) */ -static int libipw_frag_cache_invalidate(struct libipw_device *ieee, - struct libipw_hdr_4addr *hdr) -{ - u16 sc; - unsigned int seq; - struct libipw_frag_entry *entry; - - sc = le16_to_cpu(hdr->seq_ctl); - seq = WLAN_GET_SEQ_SEQ(sc); - - entry = libipw_frag_cache_find(ieee, seq, -1, hdr->addr2, - hdr->addr1); - - if (entry == NULL) { - LIBIPW_DEBUG_FRAG("could not invalidate fragment cache " - "entry (seq=%u)\n", seq); - return -1; - } - - entry->skb = NULL; - return 0; -} - -#ifdef NOT_YET -/* libipw_rx_frame_mgtmt - * - * Responsible for handling management control frames - * - * Called by libipw_rx */ -static int -libipw_rx_frame_mgmt(struct libipw_device *ieee, struct sk_buff *skb, - struct libipw_rx_stats *rx_stats, u16 type, - u16 stype) -{ - if (ieee->iw_mode == IW_MODE_MASTER) { - printk(KERN_DEBUG "%s: Master mode not yet supported.\n", - ieee->dev->name); - return 0; -/* - hostap_update_sta_ps(ieee, (struct hostap_libipw_hdr_4addr *) - skb->data);*/ - } - - if (ieee->hostapd && type == WLAN_FC_TYPE_MGMT) { - if (stype == WLAN_FC_STYPE_BEACON && - ieee->iw_mode == IW_MODE_MASTER) { - struct sk_buff *skb2; - /* Process beacon frames also in kernel driver to - * update STA(AP) table statistics */ - skb2 = skb_clone(skb, GFP_ATOMIC); - if (skb2) - hostap_rx(skb2->dev, skb2, rx_stats); - } - - /* send management frames to the user space daemon for - * processing */ - ieee->apdevstats.rx_packets++; - ieee->apdevstats.rx_bytes += skb->len; - prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT); - return 0; - } - - if (ieee->iw_mode == IW_MODE_MASTER) { - if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) { - printk(KERN_DEBUG "%s: unknown management frame " - "(type=0x%02x, stype=0x%02x) dropped\n", - skb->dev->name, type, stype); - return -1; - } - - hostap_rx(skb->dev, skb, rx_stats); - return 0; - } - - printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame " - "received in non-Host AP mode\n", skb->dev->name); - return -1; -} -#endif - -/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ -/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ -static unsigned char libipw_rfc1042_header[] = - { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; - -/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ -static unsigned char libipw_bridge_tunnel_header[] = - { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; -/* No encapsulation header if EtherType < 0x600 (=length) */ - -/* Called by libipw_rx_frame_decrypt */ -static int libipw_is_eapol_frame(struct libipw_device *ieee, - struct sk_buff *skb) -{ - struct net_device *dev = ieee->dev; - u16 fc, ethertype; - struct libipw_hdr_3addr *hdr; - u8 *pos; - - if (skb->len < 24) - return 0; - - hdr = (struct libipw_hdr_3addr *)skb->data; - fc = le16_to_cpu(hdr->frame_ctl); - - /* check that the frame is unicast frame to us */ - if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == - IEEE80211_FCTL_TODS && - ether_addr_equal(hdr->addr1, dev->dev_addr) && - ether_addr_equal(hdr->addr3, dev->dev_addr)) { - /* ToDS frame with own addr BSSID and DA */ - } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == - IEEE80211_FCTL_FROMDS && - ether_addr_equal(hdr->addr1, dev->dev_addr)) { - /* FromDS frame with own addr as DA */ - } else - return 0; - - if (skb->len < 24 + 8) - return 0; - - /* check for port access entity Ethernet type */ - pos = skb->data + 24; - ethertype = (pos[6] << 8) | pos[7]; - if (ethertype == ETH_P_PAE) - return 1; - - return 0; -} - -/* Called only as a tasklet (software IRQ), by libipw_rx */ -static int -libipw_rx_frame_decrypt(struct libipw_device *ieee, struct sk_buff *skb, - struct lib80211_crypt_data *crypt) -{ - struct libipw_hdr_3addr *hdr; - int res, hdrlen; - - if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) - return 0; - - hdr = (struct libipw_hdr_3addr *)skb->data; - hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); - - atomic_inc(&crypt->refcnt); - res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); - atomic_dec(&crypt->refcnt); - if (res < 0) { - LIBIPW_DEBUG_DROP("decryption failed (SA=%pM) res=%d\n", - hdr->addr2, res); - if (res == -2) - LIBIPW_DEBUG_DROP("Decryption failed ICV " - "mismatch (key %d)\n", - skb->data[hdrlen + 3] >> 6); - ieee->ieee_stats.rx_discards_undecryptable++; - return -1; - } - - return res; -} - -/* Called only as a tasklet (software IRQ), by libipw_rx */ -static int -libipw_rx_frame_decrypt_msdu(struct libipw_device *ieee, - struct sk_buff *skb, int keyidx, - struct lib80211_crypt_data *crypt) -{ - struct libipw_hdr_3addr *hdr; - int res, hdrlen; - - if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) - return 0; - - hdr = (struct libipw_hdr_3addr *)skb->data; - hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); - - atomic_inc(&crypt->refcnt); - res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); - atomic_dec(&crypt->refcnt); - if (res < 0) { - printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" - " (SA=%pM keyidx=%d)\n", ieee->dev->name, hdr->addr2, - keyidx); - return -1; - } - - return 0; -} - -/* All received frames are sent to this function. @skb contains the frame in - * IEEE 802.11 format, i.e., in the format it was sent over air. - * This function is called only as a tasklet (software IRQ). */ -int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, - struct libipw_rx_stats *rx_stats) -{ - struct net_device *dev = ieee->dev; - struct libipw_hdr_4addr *hdr; - size_t hdrlen; - u16 fc, type, stype, sc; - unsigned int frag; - u8 *payload; - u16 ethertype; -#ifdef NOT_YET - struct net_device *wds = NULL; - struct sk_buff *skb2 = NULL; - struct net_device *wds = NULL; - int frame_authorized = 0; - int from_assoc_ap = 0; - void *sta = NULL; -#endif - u8 dst[ETH_ALEN]; - u8 src[ETH_ALEN]; - struct lib80211_crypt_data *crypt = NULL; - int keyidx = 0; - int can_be_decrypted = 0; - - hdr = (struct libipw_hdr_4addr *)skb->data; - if (skb->len < 10) { - printk(KERN_INFO "%s: SKB length < 10\n", dev->name); - goto rx_dropped; - } - - fc = le16_to_cpu(hdr->frame_ctl); - type = WLAN_FC_GET_TYPE(fc); - stype = WLAN_FC_GET_STYPE(fc); - sc = le16_to_cpu(hdr->seq_ctl); - frag = WLAN_GET_SEQ_FRAG(sc); - hdrlen = libipw_get_hdrlen(fc); - - if (skb->len < hdrlen) { - printk(KERN_INFO "%s: invalid SKB length %d\n", - dev->name, skb->len); - goto rx_dropped; - } - - /* Put this code here so that we avoid duplicating it in all - * Rx paths. - Jean II */ -#ifdef CONFIG_WIRELESS_EXT -#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ - /* If spy monitoring on */ - if (ieee->spy_data.spy_number > 0) { - struct iw_quality wstats; - - wstats.updated = 0; - if (rx_stats->mask & LIBIPW_STATMASK_RSSI) { - wstats.level = rx_stats->signal; - wstats.updated |= IW_QUAL_LEVEL_UPDATED; - } else - wstats.updated |= IW_QUAL_LEVEL_INVALID; - - if (rx_stats->mask & LIBIPW_STATMASK_NOISE) { - wstats.noise = rx_stats->noise; - wstats.updated |= IW_QUAL_NOISE_UPDATED; - } else - wstats.updated |= IW_QUAL_NOISE_INVALID; - - if (rx_stats->mask & LIBIPW_STATMASK_SIGNAL) { - wstats.qual = rx_stats->signal; - wstats.updated |= IW_QUAL_QUAL_UPDATED; - } else - wstats.updated |= IW_QUAL_QUAL_INVALID; - - /* Update spy records */ - wireless_spy_update(ieee->dev, hdr->addr2, &wstats); - } -#endif /* IW_WIRELESS_SPY */ -#endif /* CONFIG_WIRELESS_EXT */ - -#ifdef NOT_YET - hostap_update_rx_stats(local->ap, hdr, rx_stats); -#endif - - if (ieee->iw_mode == IW_MODE_MONITOR) { - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - libipw_monitor_rx(ieee, skb, rx_stats); - return 1; - } - - can_be_decrypted = (is_multicast_ether_addr(hdr->addr1) || - is_broadcast_ether_addr(hdr->addr2)) ? - ieee->host_mc_decrypt : ieee->host_decrypt; - - if (can_be_decrypted) { - if (skb->len >= hdrlen + 3) { - /* Top two-bits of byte 3 are the key index */ - keyidx = skb->data[hdrlen + 3] >> 6; - } - - /* ieee->crypt[] is WEP_KEY (4) in length. Given that keyidx - * is only allowed 2-bits of storage, no value of keyidx can - * be provided via above code that would result in keyidx - * being out of range */ - crypt = ieee->crypt_info.crypt[keyidx]; - -#ifdef NOT_YET - sta = NULL; - - /* Use station specific key to override default keys if the - * receiver address is a unicast address ("individual RA"). If - * bcrx_sta_key parameter is set, station specific key is used - * even with broad/multicast targets (this is against IEEE - * 802.11, but makes it easier to use different keys with - * stations that do not support WEP key mapping). */ - - if (is_unicast_ether_addr(hdr->addr1) || local->bcrx_sta_key) - (void)hostap_handle_sta_crypto(local, hdr, &crypt, - &sta); -#endif - - /* allow NULL decrypt to indicate an station specific override - * for default encryption */ - if (crypt && (crypt->ops == NULL || - crypt->ops->decrypt_mpdu == NULL)) - crypt = NULL; - - if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) { - /* This seems to be triggered by some (multicast?) - * frames from other than current BSS, so just drop the - * frames silently instead of filling system log with - * these reports. */ - LIBIPW_DEBUG_DROP("Decryption failed (not set)" - " (SA=%pM)\n", hdr->addr2); - ieee->ieee_stats.rx_discards_undecryptable++; - goto rx_dropped; - } - } -#ifdef NOT_YET - if (type != WLAN_FC_TYPE_DATA) { - if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH && - fc & IEEE80211_FCTL_PROTECTED && ieee->host_decrypt && - (keyidx = hostap_rx_frame_decrypt(ieee, skb, crypt)) < 0) { - printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " - "from %pM\n", dev->name, hdr->addr2); - /* TODO: could inform hostapd about this so that it - * could send auth failure report */ - goto rx_dropped; - } - - if (libipw_rx_frame_mgmt(ieee, skb, rx_stats, type, stype)) - goto rx_dropped; - else - goto rx_exit; - } -#endif - /* drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.29) */ - if (sc == ieee->prev_seq_ctl) - goto rx_dropped; - else - ieee->prev_seq_ctl = sc; - - /* Data frame - extract src/dst addresses */ - if (skb->len < LIBIPW_3ADDR_LEN) - goto rx_dropped; - - switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { - case IEEE80211_FCTL_FROMDS: - memcpy(dst, hdr->addr1, ETH_ALEN); - memcpy(src, hdr->addr3, ETH_ALEN); - break; - case IEEE80211_FCTL_TODS: - memcpy(dst, hdr->addr3, ETH_ALEN); - memcpy(src, hdr->addr2, ETH_ALEN); - break; - case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: - if (skb->len < LIBIPW_4ADDR_LEN) - goto rx_dropped; - memcpy(dst, hdr->addr3, ETH_ALEN); - memcpy(src, hdr->addr4, ETH_ALEN); - break; - case 0: - memcpy(dst, hdr->addr1, ETH_ALEN); - memcpy(src, hdr->addr2, ETH_ALEN); - break; - } - -#ifdef NOT_YET - if (hostap_rx_frame_wds(ieee, hdr, fc, &wds)) - goto rx_dropped; - if (wds) { - skb->dev = dev = wds; - stats = hostap_get_stats(dev); - } - - if (ieee->iw_mode == IW_MODE_MASTER && !wds && - (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == - IEEE80211_FCTL_FROMDS && ieee->stadev && - ether_addr_equal(hdr->addr2, ieee->assoc_ap_addr)) { - /* Frame from BSSID of the AP for which we are a client */ - skb->dev = dev = ieee->stadev; - stats = hostap_get_stats(dev); - from_assoc_ap = 1; - } -#endif - -#ifdef NOT_YET - if ((ieee->iw_mode == IW_MODE_MASTER || - ieee->iw_mode == IW_MODE_REPEAT) && !from_assoc_ap) { - switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats, - wds != NULL)) { - case AP_RX_CONTINUE_NOT_AUTHORIZED: - frame_authorized = 0; - break; - case AP_RX_CONTINUE: - frame_authorized = 1; - break; - case AP_RX_DROP: - goto rx_dropped; - case AP_RX_EXIT: - goto rx_exit; - } - } -#endif - - /* Nullfunc frames may have PS-bit set, so they must be passed to - * hostap_handle_sta_rx() before being dropped here. */ - - stype &= ~IEEE80211_STYPE_QOS_DATA; - - if (stype != IEEE80211_STYPE_DATA && - stype != IEEE80211_STYPE_DATA_CFACK && - stype != IEEE80211_STYPE_DATA_CFPOLL && - stype != IEEE80211_STYPE_DATA_CFACKPOLL) { - if (stype != IEEE80211_STYPE_NULLFUNC) - LIBIPW_DEBUG_DROP("RX: dropped data frame " - "with no data (type=0x%02x, " - "subtype=0x%02x, len=%d)\n", - type, stype, skb->len); - goto rx_dropped; - } - - /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ - - if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && - (keyidx = libipw_rx_frame_decrypt(ieee, skb, crypt)) < 0) - goto rx_dropped; - - hdr = (struct libipw_hdr_4addr *)skb->data; - - /* skb: hdr + (possibly fragmented) plaintext payload */ - // PR: FIXME: hostap has additional conditions in the "if" below: - // ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && - if ((frag != 0) || (fc & IEEE80211_FCTL_MOREFRAGS)) { - int flen; - struct sk_buff *frag_skb = libipw_frag_cache_get(ieee, hdr); - LIBIPW_DEBUG_FRAG("Rx Fragment received (%u)\n", frag); - - if (!frag_skb) { - LIBIPW_DEBUG(LIBIPW_DL_RX | LIBIPW_DL_FRAG, - "Rx cannot get skb from fragment " - "cache (morefrag=%d seq=%u frag=%u)\n", - (fc & IEEE80211_FCTL_MOREFRAGS) != 0, - WLAN_GET_SEQ_SEQ(sc), frag); - goto rx_dropped; - } - - flen = skb->len; - if (frag != 0) - flen -= hdrlen; - - if (frag_skb->tail + flen > frag_skb->end) { - printk(KERN_WARNING "%s: host decrypted and " - "reassembled frame did not fit skb\n", - dev->name); - libipw_frag_cache_invalidate(ieee, hdr); - goto rx_dropped; - } - - if (frag == 0) { - /* copy first fragment (including full headers) into - * beginning of the fragment cache skb */ - skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), flen); - } else { - /* append frame payload to the end of the fragment - * cache skb */ - skb_copy_from_linear_data_offset(skb, hdrlen, - skb_put(frag_skb, flen), flen); - } - dev_kfree_skb_any(skb); - skb = NULL; - - if (fc & IEEE80211_FCTL_MOREFRAGS) { - /* more fragments expected - leave the skb in fragment - * cache for now; it will be delivered to upper layers - * after all fragments have been received */ - goto rx_exit; - } - - /* this was the last fragment and the frame will be - * delivered, so remove skb from fragment cache */ - skb = frag_skb; - hdr = (struct libipw_hdr_4addr *)skb->data; - libipw_frag_cache_invalidate(ieee, hdr); - } - - /* skb: hdr + (possible reassembled) full MSDU payload; possibly still - * encrypted/authenticated */ - if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && - libipw_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) - goto rx_dropped; - - hdr = (struct libipw_hdr_4addr *)skb->data; - if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep) { - if ( /*ieee->ieee802_1x && */ - libipw_is_eapol_frame(ieee, skb)) { - /* pass unencrypted EAPOL frames even if encryption is - * configured */ - } else { - LIBIPW_DEBUG_DROP("encryption configured, but RX " - "frame not encrypted (SA=%pM)\n", - hdr->addr2); - goto rx_dropped; - } - } - - if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep && - !libipw_is_eapol_frame(ieee, skb)) { - LIBIPW_DEBUG_DROP("dropped unencrypted RX data " - "frame from %pM (drop_unencrypted=1)\n", - hdr->addr2); - goto rx_dropped; - } - - /* If the frame was decrypted in hardware, we may need to strip off - * any security data (IV, ICV, etc) that was left behind */ - if (!can_be_decrypted && (fc & IEEE80211_FCTL_PROTECTED) && - ieee->host_strip_iv_icv) { - int trimlen = 0; - - /* Top two-bits of byte 3 are the key index */ - if (skb->len >= hdrlen + 3) - keyidx = skb->data[hdrlen + 3] >> 6; - - /* To strip off any security data which appears before the - * payload, we simply increase hdrlen (as the header gets - * chopped off immediately below). For the security data which - * appears after the payload, we use skb_trim. */ - - switch (ieee->sec.encode_alg[keyidx]) { - case SEC_ALG_WEP: - /* 4 byte IV */ - hdrlen += 4; - /* 4 byte ICV */ - trimlen = 4; - break; - case SEC_ALG_TKIP: - /* 4 byte IV, 4 byte ExtIV */ - hdrlen += 8; - /* 8 byte MIC, 4 byte ICV */ - trimlen = 12; - break; - case SEC_ALG_CCMP: - /* 8 byte CCMP header */ - hdrlen += 8; - /* 8 byte MIC */ - trimlen = 8; - break; - } - - if (skb->len < trimlen) - goto rx_dropped; - - __skb_trim(skb, skb->len - trimlen); - - if (skb->len < hdrlen) - goto rx_dropped; - } - - /* skb: hdr + (possible reassembled) full plaintext payload */ - - payload = skb->data + hdrlen; - ethertype = (payload[6] << 8) | payload[7]; - -#ifdef NOT_YET - /* If IEEE 802.1X is used, check whether the port is authorized to send - * the received frame. */ - if (ieee->ieee802_1x && ieee->iw_mode == IW_MODE_MASTER) { - if (ethertype == ETH_P_PAE) { - printk(KERN_DEBUG "%s: RX: IEEE 802.1X frame\n", - dev->name); - if (ieee->hostapd && ieee->apdev) { - /* Send IEEE 802.1X frames to the user - * space daemon for processing */ - prism2_rx_80211(ieee->apdev, skb, rx_stats, - PRISM2_RX_MGMT); - ieee->apdevstats.rx_packets++; - ieee->apdevstats.rx_bytes += skb->len; - goto rx_exit; - } - } else if (!frame_authorized) { - printk(KERN_DEBUG "%s: dropped frame from " - "unauthorized port (IEEE 802.1X): " - "ethertype=0x%04x\n", dev->name, ethertype); - goto rx_dropped; - } - } -#endif - - /* convert hdr + possible LLC headers into Ethernet header */ - if (skb->len - hdrlen >= 8 && - ((memcmp(payload, libipw_rfc1042_header, SNAP_SIZE) == 0 && - ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || - memcmp(payload, libipw_bridge_tunnel_header, SNAP_SIZE) == 0)) { - /* remove RFC1042 or Bridge-Tunnel encapsulation and - * replace EtherType */ - skb_pull(skb, hdrlen + SNAP_SIZE); - memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); - } else { - __be16 len; - /* Leave Ethernet header part of hdr and full payload */ - skb_pull(skb, hdrlen); - len = htons(skb->len); - memcpy(skb_push(skb, 2), &len, 2); - memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); - } - -#ifdef NOT_YET - if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == - IEEE80211_FCTL_TODS) && skb->len >= ETH_HLEN + ETH_ALEN) { - /* Non-standard frame: get addr4 from its bogus location after - * the payload */ - skb_copy_to_linear_data_offset(skb, ETH_ALEN, - skb->data + skb->len - ETH_ALEN, - ETH_ALEN); - skb_trim(skb, skb->len - ETH_ALEN); - } -#endif - - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - -#ifdef NOT_YET - if (ieee->iw_mode == IW_MODE_MASTER && !wds && ieee->ap->bridge_packets) { - if (is_multicast_ether_addr(dst)) { - /* copy multicast frame both to the higher layers and - * to the wireless media */ - ieee->ap->bridged_multicast++; - skb2 = skb_clone(skb, GFP_ATOMIC); - if (skb2 == NULL) - printk(KERN_DEBUG "%s: skb_clone failed for " - "multicast frame\n", dev->name); - } else if (hostap_is_sta_assoc(ieee->ap, dst)) { - /* send frame directly to the associated STA using - * wireless media and not passing to higher layers */ - ieee->ap->bridged_unicast++; - skb2 = skb; - skb = NULL; - } - } - - if (skb2 != NULL) { - /* send to wireless media */ - skb2->dev = dev; - skb2->protocol = htons(ETH_P_802_3); - skb_reset_mac_header(skb2); - skb_reset_network_header(skb2); - /* skb2->network_header += ETH_HLEN; */ - dev_queue_xmit(skb2); - } -#endif - - if (skb) { - skb->protocol = eth_type_trans(skb, dev); - memset(skb->cb, 0, sizeof(skb->cb)); - skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */ - if (netif_rx(skb) == NET_RX_DROP) { - /* netif_rx always succeeds, but it might drop - * the packet. If it drops the packet, we log that - * in our stats. */ - LIBIPW_DEBUG_DROP - ("RX: netif_rx dropped the packet\n"); - dev->stats.rx_dropped++; - } - } - - rx_exit: -#ifdef NOT_YET - if (sta) - hostap_handle_sta_release(sta); -#endif - return 1; - - rx_dropped: - dev->stats.rx_dropped++; - - /* Returning 0 indicates to caller that we have not handled the SKB-- - * so it is still allocated and can be used again by underlying - * hardware as a DMA target */ - return 0; -} - -/* Filter out unrelated packets, call libipw_rx[_mgt] - * This function takes over the skb, it should not be used again after calling - * this function. */ -void libipw_rx_any(struct libipw_device *ieee, - struct sk_buff *skb, struct libipw_rx_stats *stats) -{ - struct libipw_hdr_4addr *hdr; - int is_packet_for_us; - u16 fc; - - if (ieee->iw_mode == IW_MODE_MONITOR) { - if (!libipw_rx(ieee, skb, stats)) - dev_kfree_skb_irq(skb); - return; - } - - if (skb->len < sizeof(struct ieee80211_hdr)) - goto drop_free; - - hdr = (struct libipw_hdr_4addr *)skb->data; - fc = le16_to_cpu(hdr->frame_ctl); - - if ((fc & IEEE80211_FCTL_VERS) != 0) - goto drop_free; - - switch (fc & IEEE80211_FCTL_FTYPE) { - case IEEE80211_FTYPE_MGMT: - if (skb->len < sizeof(struct libipw_hdr_3addr)) - goto drop_free; - libipw_rx_mgt(ieee, hdr, stats); - dev_kfree_skb_irq(skb); - return; - case IEEE80211_FTYPE_DATA: - break; - case IEEE80211_FTYPE_CTL: - return; - default: - return; - } - - is_packet_for_us = 0; - switch (ieee->iw_mode) { - case IW_MODE_ADHOC: - /* our BSS and not from/to DS */ - if (ether_addr_equal(hdr->addr3, ieee->bssid)) - if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) { - /* promisc: get all */ - if (ieee->dev->flags & IFF_PROMISC) - is_packet_for_us = 1; - /* to us */ - else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr)) - is_packet_for_us = 1; - /* mcast */ - else if (is_multicast_ether_addr(hdr->addr1)) - is_packet_for_us = 1; - } - break; - case IW_MODE_INFRA: - /* our BSS (== from our AP) and from DS */ - if (ether_addr_equal(hdr->addr2, ieee->bssid)) - if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) { - /* promisc: get all */ - if (ieee->dev->flags & IFF_PROMISC) - is_packet_for_us = 1; - /* to us */ - else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr)) - is_packet_for_us = 1; - /* mcast */ - else if (is_multicast_ether_addr(hdr->addr1)) { - /* not our own packet bcasted from AP */ - if (!ether_addr_equal(hdr->addr3, ieee->dev->dev_addr)) - is_packet_for_us = 1; - } - } - break; - default: - /* ? */ - break; - } - - if (is_packet_for_us) - if (!libipw_rx(ieee, skb, stats)) - dev_kfree_skb_irq(skb); - return; - -drop_free: - dev_kfree_skb_irq(skb); - ieee->dev->stats.rx_dropped++; -} - -#define MGMT_FRAME_FIXED_PART_LENGTH 0x24 - -static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; - -/* -* Make the structure we read from the beacon packet to have -* the right values -*/ -static int libipw_verify_qos_info(struct libipw_qos_information_element - *info_element, int sub_type) -{ - - if (info_element->qui_subtype != sub_type) - return -1; - if (memcmp(info_element->qui, qos_oui, QOS_OUI_LEN)) - return -1; - if (info_element->qui_type != QOS_OUI_TYPE) - return -1; - if (info_element->version != QOS_VERSION_1) - return -1; - - return 0; -} - -/* - * Parse a QoS parameter element - */ -static int libipw_read_qos_param_element(struct libipw_qos_parameter_info - *element_param, struct libipw_info_element - *info_element) -{ - int ret = 0; - u16 size = sizeof(struct libipw_qos_parameter_info) - 2; - - if ((info_element == NULL) || (element_param == NULL)) - return -1; - - if (info_element->id == QOS_ELEMENT_ID && info_element->len == size) { - memcpy(element_param->info_element.qui, info_element->data, - info_element->len); - element_param->info_element.elementID = info_element->id; - element_param->info_element.length = info_element->len; - } else - ret = -1; - if (ret == 0) - ret = libipw_verify_qos_info(&element_param->info_element, - QOS_OUI_PARAM_SUB_TYPE); - return ret; -} - -/* - * Parse a QoS information element - */ -static int libipw_read_qos_info_element(struct - libipw_qos_information_element - *element_info, struct libipw_info_element - *info_element) -{ - int ret = 0; - u16 size = sizeof(struct libipw_qos_information_element) - 2; - - if (element_info == NULL) - return -1; - if (info_element == NULL) - return -1; - - if ((info_element->id == QOS_ELEMENT_ID) && (info_element->len == size)) { - memcpy(element_info->qui, info_element->data, - info_element->len); - element_info->elementID = info_element->id; - element_info->length = info_element->len; - } else - ret = -1; - - if (ret == 0) - ret = libipw_verify_qos_info(element_info, - QOS_OUI_INFO_SUB_TYPE); - return ret; -} - -/* - * Write QoS parameters from the ac parameters. - */ -static int libipw_qos_convert_ac_to_parameters(struct - libipw_qos_parameter_info - *param_elm, struct - libipw_qos_parameters - *qos_param) -{ - int rc = 0; - int i; - struct libipw_qos_ac_parameter *ac_params; - u32 txop; - u8 cw_min; - u8 cw_max; - - for (i = 0; i < QOS_QUEUE_NUM; i++) { - ac_params = &(param_elm->ac_params_record[i]); - - qos_param->aifs[i] = (ac_params->aci_aifsn) & 0x0F; - qos_param->aifs[i] -= (qos_param->aifs[i] < 2) ? 0 : 2; - - cw_min = ac_params->ecw_min_max & 0x0F; - qos_param->cw_min[i] = cpu_to_le16((1 << cw_min) - 1); - - cw_max = (ac_params->ecw_min_max & 0xF0) >> 4; - qos_param->cw_max[i] = cpu_to_le16((1 << cw_max) - 1); - - qos_param->flag[i] = - (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00; - - txop = le16_to_cpu(ac_params->tx_op_limit) * 32; - qos_param->tx_op_limit[i] = cpu_to_le16(txop); - } - return rc; -} - -/* - * we have a generic data element which it may contain QoS information or - * parameters element. check the information element length to decide - * which type to read - */ -static int libipw_parse_qos_info_param_IE(struct libipw_info_element - *info_element, - struct libipw_network *network) -{ - int rc = 0; - struct libipw_qos_parameters *qos_param = NULL; - struct libipw_qos_information_element qos_info_element; - - rc = libipw_read_qos_info_element(&qos_info_element, info_element); - - if (rc == 0) { - network->qos_data.param_count = qos_info_element.ac_info & 0x0F; - network->flags |= NETWORK_HAS_QOS_INFORMATION; - } else { - struct libipw_qos_parameter_info param_element; - - rc = libipw_read_qos_param_element(¶m_element, - info_element); - if (rc == 0) { - qos_param = &(network->qos_data.parameters); - libipw_qos_convert_ac_to_parameters(¶m_element, - qos_param); - network->flags |= NETWORK_HAS_QOS_PARAMETERS; - network->qos_data.param_count = - param_element.info_element.ac_info & 0x0F; - } - } - - if (rc == 0) { - LIBIPW_DEBUG_QOS("QoS is supported\n"); - network->qos_data.supported = 1; - } - return rc; -} - -#ifdef CONFIG_LIBIPW_DEBUG -#define MFIE_STRING(x) case WLAN_EID_ ##x: return #x - -static const char *get_info_element_string(u16 id) -{ - switch (id) { - MFIE_STRING(SSID); - MFIE_STRING(SUPP_RATES); - MFIE_STRING(FH_PARAMS); - MFIE_STRING(DS_PARAMS); - MFIE_STRING(CF_PARAMS); - MFIE_STRING(TIM); - MFIE_STRING(IBSS_PARAMS); - MFIE_STRING(COUNTRY); - MFIE_STRING(REQUEST); - MFIE_STRING(CHALLENGE); - MFIE_STRING(PWR_CONSTRAINT); - MFIE_STRING(PWR_CAPABILITY); - MFIE_STRING(TPC_REQUEST); - MFIE_STRING(TPC_REPORT); - MFIE_STRING(SUPPORTED_CHANNELS); - MFIE_STRING(CHANNEL_SWITCH); - MFIE_STRING(MEASURE_REQUEST); - MFIE_STRING(MEASURE_REPORT); - MFIE_STRING(QUIET); - MFIE_STRING(IBSS_DFS); - MFIE_STRING(ERP_INFO); - MFIE_STRING(RSN); - MFIE_STRING(EXT_SUPP_RATES); - MFIE_STRING(VENDOR_SPECIFIC); - MFIE_STRING(QOS_PARAMETER); - default: - return "UNKNOWN"; - } -} -#endif - -static int libipw_parse_info_param(struct libipw_info_element - *info_element, u16 length, - struct libipw_network *network) -{ - u8 i; -#ifdef CONFIG_LIBIPW_DEBUG - char rates_str[64]; - char *p; -#endif - - while (length >= sizeof(*info_element)) { - if (sizeof(*info_element) + info_element->len > length) { - LIBIPW_DEBUG_MGMT("Info elem: parse failed: " - "info_element->len + 2 > left : " - "info_element->len+2=%zd left=%d, id=%d.\n", - info_element->len + - sizeof(*info_element), - length, info_element->id); - /* We stop processing but don't return an error here - * because some misbehaviour APs break this rule. ie. - * Orinoco AP1000. */ - break; - } - - switch (info_element->id) { - case WLAN_EID_SSID: - network->ssid_len = min(info_element->len, - (u8) IW_ESSID_MAX_SIZE); - memcpy(network->ssid, info_element->data, - network->ssid_len); - if (network->ssid_len < IW_ESSID_MAX_SIZE) - memset(network->ssid + network->ssid_len, 0, - IW_ESSID_MAX_SIZE - network->ssid_len); - - LIBIPW_DEBUG_MGMT("WLAN_EID_SSID: '%*pE' len=%d.\n", - network->ssid_len, network->ssid, - network->ssid_len); - break; - - case WLAN_EID_SUPP_RATES: -#ifdef CONFIG_LIBIPW_DEBUG - p = rates_str; -#endif - network->rates_len = min(info_element->len, - MAX_RATES_LENGTH); - for (i = 0; i < network->rates_len; i++) { - network->rates[i] = info_element->data[i]; -#ifdef CONFIG_LIBIPW_DEBUG - p += snprintf(p, sizeof(rates_str) - - (p - rates_str), "%02X ", - network->rates[i]); -#endif - if (libipw_is_ofdm_rate - (info_element->data[i])) { - network->flags |= NETWORK_HAS_OFDM; - if (info_element->data[i] & - LIBIPW_BASIC_RATE_MASK) - network->flags &= - ~NETWORK_HAS_CCK; - } - } - - LIBIPW_DEBUG_MGMT("WLAN_EID_SUPP_RATES: '%s' (%d)\n", - rates_str, network->rates_len); - break; - - case WLAN_EID_EXT_SUPP_RATES: -#ifdef CONFIG_LIBIPW_DEBUG - p = rates_str; -#endif - network->rates_ex_len = min(info_element->len, - MAX_RATES_EX_LENGTH); - for (i = 0; i < network->rates_ex_len; i++) { - network->rates_ex[i] = info_element->data[i]; -#ifdef CONFIG_LIBIPW_DEBUG - p += snprintf(p, sizeof(rates_str) - - (p - rates_str), "%02X ", - network->rates_ex[i]); -#endif - if (libipw_is_ofdm_rate - (info_element->data[i])) { - network->flags |= NETWORK_HAS_OFDM; - if (info_element->data[i] & - LIBIPW_BASIC_RATE_MASK) - network->flags &= - ~NETWORK_HAS_CCK; - } - } - - LIBIPW_DEBUG_MGMT("WLAN_EID_EXT_SUPP_RATES: '%s' (%d)\n", - rates_str, network->rates_ex_len); - break; - - case WLAN_EID_DS_PARAMS: - LIBIPW_DEBUG_MGMT("WLAN_EID_DS_PARAMS: %d\n", - info_element->data[0]); - network->channel = info_element->data[0]; - break; - - case WLAN_EID_FH_PARAMS: - LIBIPW_DEBUG_MGMT("WLAN_EID_FH_PARAMS: ignored\n"); - break; - - case WLAN_EID_CF_PARAMS: - LIBIPW_DEBUG_MGMT("WLAN_EID_CF_PARAMS: ignored\n"); - break; - - case WLAN_EID_TIM: - network->tim.tim_count = info_element->data[0]; - network->tim.tim_period = info_element->data[1]; - LIBIPW_DEBUG_MGMT("WLAN_EID_TIM: partially ignored\n"); - break; - - case WLAN_EID_ERP_INFO: - network->erp_value = info_element->data[0]; - network->flags |= NETWORK_HAS_ERP_VALUE; - LIBIPW_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n", - network->erp_value); - break; - - case WLAN_EID_IBSS_PARAMS: - network->atim_window = info_element->data[0]; - LIBIPW_DEBUG_MGMT("WLAN_EID_IBSS_PARAMS: %d\n", - network->atim_window); - break; - - case WLAN_EID_CHALLENGE: - LIBIPW_DEBUG_MGMT("WLAN_EID_CHALLENGE: ignored\n"); - break; - - case WLAN_EID_VENDOR_SPECIFIC: - LIBIPW_DEBUG_MGMT("WLAN_EID_VENDOR_SPECIFIC: %d bytes\n", - info_element->len); - if (!libipw_parse_qos_info_param_IE(info_element, - network)) - break; - - if (info_element->len >= 4 && - info_element->data[0] == 0x00 && - info_element->data[1] == 0x50 && - info_element->data[2] == 0xf2 && - info_element->data[3] == 0x01) { - network->wpa_ie_len = min(info_element->len + 2, - MAX_WPA_IE_LEN); - memcpy(network->wpa_ie, info_element, - network->wpa_ie_len); - } - break; - - case WLAN_EID_RSN: - LIBIPW_DEBUG_MGMT("WLAN_EID_RSN: %d bytes\n", - info_element->len); - network->rsn_ie_len = min(info_element->len + 2, - MAX_WPA_IE_LEN); - memcpy(network->rsn_ie, info_element, - network->rsn_ie_len); - break; - - case WLAN_EID_QOS_PARAMETER: - printk(KERN_ERR - "QoS Error need to parse QOS_PARAMETER IE\n"); - break; - /* 802.11h */ - case WLAN_EID_PWR_CONSTRAINT: - network->power_constraint = info_element->data[0]; - network->flags |= NETWORK_HAS_POWER_CONSTRAINT; - break; - - case WLAN_EID_CHANNEL_SWITCH: - network->power_constraint = info_element->data[0]; - network->flags |= NETWORK_HAS_CSA; - break; - - case WLAN_EID_QUIET: - network->quiet.count = info_element->data[0]; - network->quiet.period = info_element->data[1]; - network->quiet.duration = info_element->data[2]; - network->quiet.offset = info_element->data[3]; - network->flags |= NETWORK_HAS_QUIET; - break; - - case WLAN_EID_IBSS_DFS: - network->flags |= NETWORK_HAS_IBSS_DFS; - break; - - case WLAN_EID_TPC_REPORT: - network->tpc_report.transmit_power = - info_element->data[0]; - network->tpc_report.link_margin = info_element->data[1]; - network->flags |= NETWORK_HAS_TPC_REPORT; - break; - - default: - LIBIPW_DEBUG_MGMT - ("Unsupported info element: %s (%d)\n", - get_info_element_string(info_element->id), - info_element->id); - break; - } - - length -= sizeof(*info_element) + info_element->len; - info_element = - (struct libipw_info_element *)&info_element-> - data[info_element->len]; - } - - return 0; -} - -static int libipw_handle_assoc_resp(struct libipw_device *ieee, struct libipw_assoc_response - *frame, struct libipw_rx_stats *stats) -{ - struct libipw_network network_resp = { }; - struct libipw_network *network = &network_resp; - struct net_device *dev = ieee->dev; - - network->flags = 0; - network->qos_data.active = 0; - network->qos_data.supported = 0; - network->qos_data.param_count = 0; - network->qos_data.old_param_count = 0; - - //network->atim_window = le16_to_cpu(frame->aid) & (0x3FFF); - network->atim_window = le16_to_cpu(frame->aid); - network->listen_interval = le16_to_cpu(frame->status); - memcpy(network->bssid, frame->header.addr3, ETH_ALEN); - network->capability = le16_to_cpu(frame->capability); - network->last_scanned = jiffies; - network->rates_len = network->rates_ex_len = 0; - network->last_associate = 0; - network->ssid_len = 0; - network->erp_value = - (network->capability & WLAN_CAPABILITY_IBSS) ? 0x3 : 0x0; - - if (stats->freq == LIBIPW_52GHZ_BAND) { - /* for A band (No DS info) */ - network->channel = stats->received_channel; - } else - network->flags |= NETWORK_HAS_CCK; - - network->wpa_ie_len = 0; - network->rsn_ie_len = 0; - - if (libipw_parse_info_param - (frame->info_element, stats->len - sizeof(*frame), network)) - return 1; - - network->mode = 0; - if (stats->freq == LIBIPW_52GHZ_BAND) - network->mode = IEEE_A; - else { - if (network->flags & NETWORK_HAS_OFDM) - network->mode |= IEEE_G; - if (network->flags & NETWORK_HAS_CCK) - network->mode |= IEEE_B; - } - - memcpy(&network->stats, stats, sizeof(network->stats)); - - if (ieee->handle_assoc_response != NULL) - ieee->handle_assoc_response(dev, frame, network); - - return 0; -} - -/***************************************************/ - -static int libipw_network_init(struct libipw_device *ieee, struct libipw_probe_response - *beacon, - struct libipw_network *network, - struct libipw_rx_stats *stats) -{ - network->qos_data.active = 0; - network->qos_data.supported = 0; - network->qos_data.param_count = 0; - network->qos_data.old_param_count = 0; - - /* Pull out fixed field data */ - memcpy(network->bssid, beacon->header.addr3, ETH_ALEN); - network->capability = le16_to_cpu(beacon->capability); - network->last_scanned = jiffies; - network->time_stamp[0] = le32_to_cpu(beacon->time_stamp[0]); - network->time_stamp[1] = le32_to_cpu(beacon->time_stamp[1]); - network->beacon_interval = le16_to_cpu(beacon->beacon_interval); - /* Where to pull this? beacon->listen_interval; */ - network->listen_interval = 0x0A; - network->rates_len = network->rates_ex_len = 0; - network->last_associate = 0; - network->ssid_len = 0; - network->flags = 0; - network->atim_window = 0; - network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ? - 0x3 : 0x0; - - if (stats->freq == LIBIPW_52GHZ_BAND) { - /* for A band (No DS info) */ - network->channel = stats->received_channel; - } else - network->flags |= NETWORK_HAS_CCK; - - network->wpa_ie_len = 0; - network->rsn_ie_len = 0; - - if (libipw_parse_info_param - (beacon->info_element, stats->len - sizeof(*beacon), network)) - return 1; - - network->mode = 0; - if (stats->freq == LIBIPW_52GHZ_BAND) - network->mode = IEEE_A; - else { - if (network->flags & NETWORK_HAS_OFDM) - network->mode |= IEEE_G; - if (network->flags & NETWORK_HAS_CCK) - network->mode |= IEEE_B; - } - - if (network->mode == 0) { - LIBIPW_DEBUG_SCAN("Filtered out '%*pE (%pM)' network.\n", - network->ssid_len, network->ssid, - network->bssid); - return 1; - } - - memcpy(&network->stats, stats, sizeof(network->stats)); - - return 0; -} - -static inline int is_same_network(struct libipw_network *src, - struct libipw_network *dst) -{ - /* A network is only a duplicate if the channel, BSSID, and ESSID - * all match. We treat all with the same BSSID and channel - * as one network */ - return ((src->ssid_len == dst->ssid_len) && - (src->channel == dst->channel) && - ether_addr_equal_64bits(src->bssid, dst->bssid) && - !memcmp(src->ssid, dst->ssid, src->ssid_len)); -} - -static void update_network(struct libipw_network *dst, - struct libipw_network *src) -{ - int qos_active; - u8 old_param; - - /* We only update the statistics if they were created by receiving - * the network information on the actual channel the network is on. - * - * This keeps beacons received on neighbor channels from bringing - * down the signal level of an AP. */ - if (dst->channel == src->stats.received_channel) - memcpy(&dst->stats, &src->stats, - sizeof(struct libipw_rx_stats)); - else - LIBIPW_DEBUG_SCAN("Network %pM info received " - "off channel (%d vs. %d)\n", src->bssid, - dst->channel, src->stats.received_channel); - - dst->capability = src->capability; - memcpy(dst->rates, src->rates, src->rates_len); - dst->rates_len = src->rates_len; - memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len); - dst->rates_ex_len = src->rates_ex_len; - - dst->mode = src->mode; - dst->flags = src->flags; - dst->time_stamp[0] = src->time_stamp[0]; - dst->time_stamp[1] = src->time_stamp[1]; - - dst->beacon_interval = src->beacon_interval; - dst->listen_interval = src->listen_interval; - dst->atim_window = src->atim_window; - dst->erp_value = src->erp_value; - dst->tim = src->tim; - - memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len); - dst->wpa_ie_len = src->wpa_ie_len; - memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len); - dst->rsn_ie_len = src->rsn_ie_len; - - dst->last_scanned = jiffies; - qos_active = src->qos_data.active; - old_param = dst->qos_data.old_param_count; - if (dst->flags & NETWORK_HAS_QOS_MASK) - memcpy(&dst->qos_data, &src->qos_data, - sizeof(struct libipw_qos_data)); - else { - dst->qos_data.supported = src->qos_data.supported; - dst->qos_data.param_count = src->qos_data.param_count; - } - - if (dst->qos_data.supported == 1) { - if (dst->ssid_len) - LIBIPW_DEBUG_QOS - ("QoS the network %s is QoS supported\n", - dst->ssid); - else - LIBIPW_DEBUG_QOS - ("QoS the network is QoS supported\n"); - } - dst->qos_data.active = qos_active; - dst->qos_data.old_param_count = old_param; - - /* dst->last_associate is not overwritten */ -} - -static inline int is_beacon(__le16 fc) -{ - return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == IEEE80211_STYPE_BEACON); -} - -static void libipw_process_probe_response(struct libipw_device - *ieee, struct - libipw_probe_response - *beacon, struct libipw_rx_stats - *stats) -{ - struct net_device *dev = ieee->dev; - struct libipw_network network = { }; - struct libipw_network *target; - struct libipw_network *oldest = NULL; -#ifdef CONFIG_LIBIPW_DEBUG - struct libipw_info_element *info_element = beacon->info_element; -#endif - unsigned long flags; - - LIBIPW_DEBUG_SCAN("'%*pE' (%pM): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n", - info_element->len, info_element->data, - beacon->header.addr3, - (beacon->capability & cpu_to_le16(1 << 0xf)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0xe)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0xd)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0xc)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0xb)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0xa)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0x9)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0x8)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0x7)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0x6)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0x5)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0x4)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0x3)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0x2)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0x1)) ? '1' : '0', - (beacon->capability & cpu_to_le16(1 << 0x0)) ? '1' : '0'); - - if (libipw_network_init(ieee, beacon, &network, stats)) { - LIBIPW_DEBUG_SCAN("Dropped '%*pE' (%pM) via %s.\n", - info_element->len, info_element->data, - beacon->header.addr3, - is_beacon(beacon->header.frame_ctl) ? - "BEACON" : "PROBE RESPONSE"); - return; - } - - /* The network parsed correctly -- so now we scan our known networks - * to see if we can find it in our list. - * - * NOTE: This search is definitely not optimized. Once its doing - * the "right thing" we'll optimize it for efficiency if - * necessary */ - - /* Search for this entry in the list and update it if it is - * already there. */ - - spin_lock_irqsave(&ieee->lock, flags); - - list_for_each_entry(target, &ieee->network_list, list) { - if (is_same_network(target, &network)) - break; - - if ((oldest == NULL) || - time_before(target->last_scanned, oldest->last_scanned)) - oldest = target; - } - - /* If we didn't find a match, then get a new network slot to initialize - * with this beacon's information */ - if (&target->list == &ieee->network_list) { - if (list_empty(&ieee->network_free_list)) { - /* If there are no more slots, expire the oldest */ - list_del(&oldest->list); - target = oldest; - LIBIPW_DEBUG_SCAN("Expired '%*pE' (%pM) from network list.\n", - target->ssid_len, target->ssid, - target->bssid); - } else { - /* Otherwise just pull from the free list */ - target = list_entry(ieee->network_free_list.next, - struct libipw_network, list); - list_del(ieee->network_free_list.next); - } - -#ifdef CONFIG_LIBIPW_DEBUG - LIBIPW_DEBUG_SCAN("Adding '%*pE' (%pM) via %s.\n", - network.ssid_len, network.ssid, - network.bssid, - is_beacon(beacon->header.frame_ctl) ? - "BEACON" : "PROBE RESPONSE"); -#endif - memcpy(target, &network, sizeof(*target)); - list_add_tail(&target->list, &ieee->network_list); - } else { - LIBIPW_DEBUG_SCAN("Updating '%*pE' (%pM) via %s.\n", - target->ssid_len, target->ssid, - target->bssid, - is_beacon(beacon->header.frame_ctl) ? - "BEACON" : "PROBE RESPONSE"); - update_network(target, &network); - } - - spin_unlock_irqrestore(&ieee->lock, flags); - - if (is_beacon(beacon->header.frame_ctl)) { - if (ieee->handle_beacon != NULL) - ieee->handle_beacon(dev, beacon, target); - } else { - if (ieee->handle_probe_response != NULL) - ieee->handle_probe_response(dev, beacon, target); - } -} - -void libipw_rx_mgt(struct libipw_device *ieee, - struct libipw_hdr_4addr *header, - struct libipw_rx_stats *stats) -{ - switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))) { - case IEEE80211_STYPE_ASSOC_RESP: - LIBIPW_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n", - WLAN_FC_GET_STYPE(le16_to_cpu - (header->frame_ctl))); - libipw_handle_assoc_resp(ieee, - (struct libipw_assoc_response *) - header, stats); - break; - - case IEEE80211_STYPE_REASSOC_RESP: - LIBIPW_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n", - WLAN_FC_GET_STYPE(le16_to_cpu - (header->frame_ctl))); - break; - - case IEEE80211_STYPE_PROBE_REQ: - LIBIPW_DEBUG_MGMT("received auth (%d)\n", - WLAN_FC_GET_STYPE(le16_to_cpu - (header->frame_ctl))); - - if (ieee->handle_probe_request != NULL) - ieee->handle_probe_request(ieee->dev, - (struct - libipw_probe_request *) - header, stats); - break; - - case IEEE80211_STYPE_PROBE_RESP: - LIBIPW_DEBUG_MGMT("received PROBE RESPONSE (%d)\n", - WLAN_FC_GET_STYPE(le16_to_cpu - (header->frame_ctl))); - LIBIPW_DEBUG_SCAN("Probe response\n"); - libipw_process_probe_response(ieee, - (struct - libipw_probe_response *) - header, stats); - break; - - case IEEE80211_STYPE_BEACON: - LIBIPW_DEBUG_MGMT("received BEACON (%d)\n", - WLAN_FC_GET_STYPE(le16_to_cpu - (header->frame_ctl))); - LIBIPW_DEBUG_SCAN("Beacon\n"); - libipw_process_probe_response(ieee, - (struct - libipw_probe_response *) - header, stats); - break; - case IEEE80211_STYPE_AUTH: - - LIBIPW_DEBUG_MGMT("received auth (%d)\n", - WLAN_FC_GET_STYPE(le16_to_cpu - (header->frame_ctl))); - - if (ieee->handle_auth != NULL) - ieee->handle_auth(ieee->dev, - (struct libipw_auth *)header); - break; - - case IEEE80211_STYPE_DISASSOC: - if (ieee->handle_disassoc != NULL) - ieee->handle_disassoc(ieee->dev, - (struct libipw_disassoc *) - header); - break; - - case IEEE80211_STYPE_ACTION: - LIBIPW_DEBUG_MGMT("ACTION\n"); - if (ieee->handle_action) - ieee->handle_action(ieee->dev, - (struct libipw_action *) - header, stats); - break; - - case IEEE80211_STYPE_REASSOC_REQ: - LIBIPW_DEBUG_MGMT("received reassoc (%d)\n", - WLAN_FC_GET_STYPE(le16_to_cpu - (header->frame_ctl))); - - LIBIPW_DEBUG_MGMT("%s: LIBIPW_REASSOC_REQ received\n", - ieee->dev->name); - if (ieee->handle_reassoc_request != NULL) - ieee->handle_reassoc_request(ieee->dev, - (struct libipw_reassoc_request *) - header); - break; - - case IEEE80211_STYPE_ASSOC_REQ: - LIBIPW_DEBUG_MGMT("received assoc (%d)\n", - WLAN_FC_GET_STYPE(le16_to_cpu - (header->frame_ctl))); - - LIBIPW_DEBUG_MGMT("%s: LIBIPW_ASSOC_REQ received\n", - ieee->dev->name); - if (ieee->handle_assoc_request != NULL) - ieee->handle_assoc_request(ieee->dev); - break; - - case IEEE80211_STYPE_DEAUTH: - LIBIPW_DEBUG_MGMT("DEAUTH\n"); - if (ieee->handle_deauth != NULL) - ieee->handle_deauth(ieee->dev, - (struct libipw_deauth *) - header); - break; - default: - LIBIPW_DEBUG_MGMT("received UNKNOWN (%d)\n", - WLAN_FC_GET_STYPE(le16_to_cpu - (header->frame_ctl))); - LIBIPW_DEBUG_MGMT("%s: Unknown management packet: %d\n", - ieee->dev->name, - WLAN_FC_GET_STYPE(le16_to_cpu - (header->frame_ctl))); - break; - } -} - -EXPORT_SYMBOL_GPL(libipw_rx_any); -EXPORT_SYMBOL(libipw_rx_mgt); -EXPORT_SYMBOL(libipw_rx); diff --git a/drivers/net/wireless/ipw2x00/libipw_tx.c b/drivers/net/wireless/ipw2x00/libipw_tx.c deleted file mode 100644 index e8c039879b05..000000000000 --- a/drivers/net/wireless/ipw2x00/libipw_tx.c +++ /dev/null @@ -1,536 +0,0 @@ -/****************************************************************************** - - Copyright(c) 2003 - 2005 Intel Corporation. All rights reserved. - - This program is free software; you can redistribute it and/or modify it - under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - The full GNU General Public License is included in this distribution in the - file called LICENSE. - - Contact Information: - Intel Linux Wireless - Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - -******************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libipw.h" - -/* - -802.11 Data Frame - - ,-------------------------------------------------------------------. -Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | - |------|------|---------|---------|---------|------|---------|------| -Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs | - | | tion | (BSSID) | | | ence | data | | - `--------------------------------------------------| |------' -Total: 28 non-data bytes `----.----' - | - .- 'Frame data' expands, if WEP enabled, to <----------' - | - V - ,-----------------------. -Bytes | 4 | 0-2296 | 4 | - |-----|-----------|-----| -Desc. | IV | Encrypted | ICV | - | | Packet | | - `-----| |-----' - `-----.-----' - | - .- 'Encrypted Packet' expands to - | - V - ,---------------------------------------------------. -Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 | - |------|------|---------|----------|------|---------| -Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP | - | DSAP | SSAP | | | | Packet | - | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | | - `---------------------------------------------------- -Total: 8 non-data bytes - -802.3 Ethernet Data Frame - - ,-----------------------------------------. -Bytes | 6 | 6 | 2 | Variable | 4 | - |-------|-------|------|-----------|------| -Desc. | Dest. | Source| Type | IP Packet | fcs | - | MAC | MAC | | | | - `-----------------------------------------' -Total: 18 non-data bytes - -In the event that fragmentation is required, the incoming payload is split into -N parts of size ieee->fts. The first fragment contains the SNAP header and the -remaining packets are just data. - -If encryption is enabled, each fragment payload size is reduced by enough space -to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP) -So if you have 1500 bytes of payload with ieee->fts set to 500 without -encryption it will take 3 frames. With WEP it will take 4 frames as the -payload of each frame is reduced to 492 bytes. - -* SKB visualization -* -* ,- skb->data -* | -* | ETHERNET HEADER ,-<-- PAYLOAD -* | | 14 bytes from skb->data -* | 2 bytes for Type --> ,T. | (sizeof ethhdr) -* | | | | -* |,-Dest.--. ,--Src.---. | | | -* | 6 bytes| | 6 bytes | | | | -* v | | | | | | -* 0 | v 1 | v | v 2 -* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 -* ^ | ^ | ^ | -* | | | | | | -* | | | | `T' <---- 2 bytes for Type -* | | | | -* | | '---SNAP--' <-------- 6 bytes for SNAP -* | | -* `-IV--' <-------------------- 4 bytes for IV (WEP) -* -* SNAP HEADER -* -*/ - -static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 }; -static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 }; - -static int libipw_copy_snap(u8 * data, __be16 h_proto) -{ - struct libipw_snap_hdr *snap; - u8 *oui; - - snap = (struct libipw_snap_hdr *)data; - snap->dsap = 0xaa; - snap->ssap = 0xaa; - snap->ctrl = 0x03; - - if (h_proto == htons(ETH_P_AARP) || h_proto == htons(ETH_P_IPX)) - oui = P802_1H_OUI; - else - oui = RFC1042_OUI; - snap->oui[0] = oui[0]; - snap->oui[1] = oui[1]; - snap->oui[2] = oui[2]; - - memcpy(data + SNAP_SIZE, &h_proto, sizeof(u16)); - - return SNAP_SIZE + sizeof(u16); -} - -static int libipw_encrypt_fragment(struct libipw_device *ieee, - struct sk_buff *frag, int hdr_len) -{ - struct lib80211_crypt_data *crypt = - ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; - int res; - - if (crypt == NULL) - return -1; - - /* To encrypt, frame format is: - * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */ - atomic_inc(&crypt->refcnt); - res = 0; - if (crypt->ops && crypt->ops->encrypt_mpdu) - res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv); - - atomic_dec(&crypt->refcnt); - if (res < 0) { - printk(KERN_INFO "%s: Encryption failed: len=%d.\n", - ieee->dev->name, frag->len); - ieee->ieee_stats.tx_discards++; - return -1; - } - - return 0; -} - -void libipw_txb_free(struct libipw_txb *txb) -{ - int i; - if (unlikely(!txb)) - return; - for (i = 0; i < txb->nr_frags; i++) - if (txb->fragments[i]) - dev_kfree_skb_any(txb->fragments[i]); - kfree(txb); -} - -static struct libipw_txb *libipw_alloc_txb(int nr_frags, int txb_size, - int headroom, gfp_t gfp_mask) -{ - struct libipw_txb *txb; - int i; - txb = kmalloc(sizeof(struct libipw_txb) + (sizeof(u8 *) * nr_frags), - gfp_mask); - if (!txb) - return NULL; - - memset(txb, 0, sizeof(struct libipw_txb)); - txb->nr_frags = nr_frags; - txb->frag_size = txb_size; - - for (i = 0; i < nr_frags; i++) { - txb->fragments[i] = __dev_alloc_skb(txb_size + headroom, - gfp_mask); - if (unlikely(!txb->fragments[i])) { - i--; - break; - } - skb_reserve(txb->fragments[i], headroom); - } - if (unlikely(i != nr_frags)) { - while (i >= 0) - dev_kfree_skb_any(txb->fragments[i--]); - kfree(txb); - return NULL; - } - return txb; -} - -static int libipw_classify(struct sk_buff *skb) -{ - struct ethhdr *eth; - struct iphdr *ip; - - eth = (struct ethhdr *)skb->data; - if (eth->h_proto != htons(ETH_P_IP)) - return 0; - - ip = ip_hdr(skb); - switch (ip->tos & 0xfc) { - case 0x20: - return 2; - case 0x40: - return 1; - case 0x60: - return 3; - case 0x80: - return 4; - case 0xa0: - return 5; - case 0xc0: - return 6; - case 0xe0: - return 7; - default: - return 0; - } -} - -/* Incoming skb is converted to a txb which consists of - * a block of 802.11 fragment packets (stored as skbs) */ -netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct libipw_device *ieee = netdev_priv(dev); - struct libipw_txb *txb = NULL; - struct libipw_hdr_3addrqos *frag_hdr; - int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size, - rts_required; - unsigned long flags; - int encrypt, host_encrypt, host_encrypt_msdu; - __be16 ether_type; - int bytes, fc, hdr_len; - struct sk_buff *skb_frag; - struct libipw_hdr_3addrqos header = {/* Ensure zero initialized */ - .duration_id = 0, - .seq_ctl = 0, - .qos_ctl = 0 - }; - u8 dest[ETH_ALEN], src[ETH_ALEN]; - struct lib80211_crypt_data *crypt; - int priority = skb->priority; - int snapped = 0; - - if (ieee->is_queue_full && (*ieee->is_queue_full) (dev, priority)) - return NETDEV_TX_BUSY; - - spin_lock_irqsave(&ieee->lock, flags); - - /* If there is no driver handler to take the TXB, dont' bother - * creating it... */ - if (!ieee->hard_start_xmit) { - printk(KERN_WARNING "%s: No xmit handler.\n", ieee->dev->name); - goto success; - } - - if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) { - printk(KERN_WARNING "%s: skb too small (%d).\n", - ieee->dev->name, skb->len); - goto success; - } - - ether_type = ((struct ethhdr *)skb->data)->h_proto; - - crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; - - encrypt = !(ether_type == htons(ETH_P_PAE) && ieee->ieee802_1x) && - ieee->sec.encrypt; - - host_encrypt = ieee->host_encrypt && encrypt && crypt; - host_encrypt_msdu = ieee->host_encrypt_msdu && encrypt && crypt; - - if (!encrypt && ieee->ieee802_1x && - ieee->drop_unencrypted && ether_type != htons(ETH_P_PAE)) { - dev->stats.tx_dropped++; - goto success; - } - - /* Save source and destination addresses */ - skb_copy_from_linear_data(skb, dest, ETH_ALEN); - skb_copy_from_linear_data_offset(skb, ETH_ALEN, src, ETH_ALEN); - - if (host_encrypt) - fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | - IEEE80211_FCTL_PROTECTED; - else - fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; - - if (ieee->iw_mode == IW_MODE_INFRA) { - fc |= IEEE80211_FCTL_TODS; - /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ - memcpy(header.addr1, ieee->bssid, ETH_ALEN); - memcpy(header.addr2, src, ETH_ALEN); - memcpy(header.addr3, dest, ETH_ALEN); - } else if (ieee->iw_mode == IW_MODE_ADHOC) { - /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ - memcpy(header.addr1, dest, ETH_ALEN); - memcpy(header.addr2, src, ETH_ALEN); - memcpy(header.addr3, ieee->bssid, ETH_ALEN); - } - hdr_len = LIBIPW_3ADDR_LEN; - - if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) { - fc |= IEEE80211_STYPE_QOS_DATA; - hdr_len += 2; - - skb->priority = libipw_classify(skb); - header.qos_ctl |= cpu_to_le16(skb->priority & LIBIPW_QCTL_TID); - } - header.frame_ctl = cpu_to_le16(fc); - - /* Advance the SKB to the start of the payload */ - skb_pull(skb, sizeof(struct ethhdr)); - - /* Determine total amount of storage required for TXB packets */ - bytes = skb->len + SNAP_SIZE + sizeof(u16); - - /* Encrypt msdu first on the whole data packet. */ - if ((host_encrypt || host_encrypt_msdu) && - crypt && crypt->ops && crypt->ops->encrypt_msdu) { - int res = 0; - int len = bytes + hdr_len + crypt->ops->extra_msdu_prefix_len + - crypt->ops->extra_msdu_postfix_len; - struct sk_buff *skb_new = dev_alloc_skb(len); - - if (unlikely(!skb_new)) - goto failed; - - skb_reserve(skb_new, crypt->ops->extra_msdu_prefix_len); - memcpy(skb_put(skb_new, hdr_len), &header, hdr_len); - snapped = 1; - libipw_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)), - ether_type); - skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len); - res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv); - if (res < 0) { - LIBIPW_ERROR("msdu encryption failed\n"); - dev_kfree_skb_any(skb_new); - goto failed; - } - dev_kfree_skb_any(skb); - skb = skb_new; - bytes += crypt->ops->extra_msdu_prefix_len + - crypt->ops->extra_msdu_postfix_len; - skb_pull(skb, hdr_len); - } - - if (host_encrypt || ieee->host_open_frag) { - /* Determine fragmentation size based on destination (multicast - * and broadcast are not fragmented) */ - if (is_multicast_ether_addr(dest) || - is_broadcast_ether_addr(dest)) - frag_size = MAX_FRAG_THRESHOLD; - else - frag_size = ieee->fts; - - /* Determine amount of payload per fragment. Regardless of if - * this stack is providing the full 802.11 header, one will - * eventually be affixed to this fragment -- so we must account - * for it when determining the amount of payload space. */ - bytes_per_frag = frag_size - hdr_len; - if (ieee->config & - (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) - bytes_per_frag -= LIBIPW_FCS_LEN; - - /* Each fragment may need to have room for encryption - * pre/postfix */ - if (host_encrypt) - bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len + - crypt->ops->extra_mpdu_postfix_len; - - /* Number of fragments is the total - * bytes_per_frag / payload_per_fragment */ - nr_frags = bytes / bytes_per_frag; - bytes_last_frag = bytes % bytes_per_frag; - if (bytes_last_frag) - nr_frags++; - else - bytes_last_frag = bytes_per_frag; - } else { - nr_frags = 1; - bytes_per_frag = bytes_last_frag = bytes; - frag_size = bytes + hdr_len; - } - - rts_required = (frag_size > ieee->rts - && ieee->config & CFG_LIBIPW_RTS); - if (rts_required) - nr_frags++; - - /* When we allocate the TXB we allocate enough space for the reserve - * and full fragment bytes (bytes_per_frag doesn't include prefix, - * postfix, header, FCS, etc.) */ - txb = libipw_alloc_txb(nr_frags, frag_size, - ieee->tx_headroom, GFP_ATOMIC); - if (unlikely(!txb)) { - printk(KERN_WARNING "%s: Could not allocate TXB\n", - ieee->dev->name); - goto failed; - } - txb->encrypted = encrypt; - if (host_encrypt) - txb->payload_size = frag_size * (nr_frags - 1) + - bytes_last_frag; - else - txb->payload_size = bytes; - - if (rts_required) { - skb_frag = txb->fragments[0]; - frag_hdr = - (struct libipw_hdr_3addrqos *)skb_put(skb_frag, hdr_len); - - /* - * Set header frame_ctl to the RTS. - */ - header.frame_ctl = - cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); - memcpy(frag_hdr, &header, hdr_len); - - /* - * Restore header frame_ctl to the original data setting. - */ - header.frame_ctl = cpu_to_le16(fc); - - if (ieee->config & - (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) - skb_put(skb_frag, 4); - - txb->rts_included = 1; - i = 1; - } else - i = 0; - - for (; i < nr_frags; i++) { - skb_frag = txb->fragments[i]; - - if (host_encrypt) - skb_reserve(skb_frag, - crypt->ops->extra_mpdu_prefix_len); - - frag_hdr = - (struct libipw_hdr_3addrqos *)skb_put(skb_frag, hdr_len); - memcpy(frag_hdr, &header, hdr_len); - - /* If this is not the last fragment, then add the MOREFRAGS - * bit to the frame control */ - if (i != nr_frags - 1) { - frag_hdr->frame_ctl = - cpu_to_le16(fc | IEEE80211_FCTL_MOREFRAGS); - bytes = bytes_per_frag; - } else { - /* The last fragment takes the remaining length */ - bytes = bytes_last_frag; - } - - if (i == 0 && !snapped) { - libipw_copy_snap(skb_put - (skb_frag, SNAP_SIZE + sizeof(u16)), - ether_type); - bytes -= SNAP_SIZE + sizeof(u16); - } - - skb_copy_from_linear_data(skb, skb_put(skb_frag, bytes), bytes); - - /* Advance the SKB... */ - skb_pull(skb, bytes); - - /* Encryption routine will move the header forward in order - * to insert the IV between the header and the payload */ - if (host_encrypt) - libipw_encrypt_fragment(ieee, skb_frag, hdr_len); - - if (ieee->config & - (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) - skb_put(skb_frag, 4); - } - - success: - spin_unlock_irqrestore(&ieee->lock, flags); - - dev_kfree_skb_any(skb); - - if (txb) { - netdev_tx_t ret = (*ieee->hard_start_xmit)(txb, dev, priority); - if (ret == NETDEV_TX_OK) { - dev->stats.tx_packets++; - dev->stats.tx_bytes += txb->payload_size; - return NETDEV_TX_OK; - } - - libipw_txb_free(txb); - } - - return NETDEV_TX_OK; - - failed: - spin_unlock_irqrestore(&ieee->lock, flags); - netif_stop_queue(dev); - dev->stats.tx_errors++; - return NETDEV_TX_BUSY; -} -EXPORT_SYMBOL(libipw_xmit); - -EXPORT_SYMBOL(libipw_txb_free); diff --git a/drivers/net/wireless/ipw2x00/libipw_wx.c b/drivers/net/wireless/ipw2x00/libipw_wx.c deleted file mode 100644 index dd29f46d086b..000000000000 --- a/drivers/net/wireless/ipw2x00/libipw_wx.c +++ /dev/null @@ -1,740 +0,0 @@ -/****************************************************************************** - - Copyright(c) 2004-2005 Intel Corporation. All rights reserved. - - Portions of this file are based on the WEP enablement code provided by the - Host AP project hostap-drivers v0.1.3 - Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - - Copyright (c) 2002-2003, Jouni Malinen - - This program is free software; you can redistribute it and/or modify it - under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - The full GNU General Public License is included in this distribution in the - file called LICENSE. - - Contact Information: - Intel Linux Wireless - Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - -******************************************************************************/ - -#include -#include -#include -#include -#include - -#include -#include - -#include "libipw.h" - -static const char *libipw_modes[] = { - "?", "a", "b", "ab", "g", "ag", "bg", "abg" -}; - -static inline unsigned int elapsed_jiffies_msecs(unsigned long start) -{ - unsigned long end = jiffies; - - if (end >= start) - return jiffies_to_msecs(end - start); - - return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); -} - -#define MAX_CUSTOM_LEN 64 -static char *libipw_translate_scan(struct libipw_device *ieee, - char *start, char *stop, - struct libipw_network *network, - struct iw_request_info *info) -{ - char custom[MAX_CUSTOM_LEN]; - char *p; - struct iw_event iwe; - int i, j; - char *current_val; /* For rates */ - u8 rate; - - /* First entry *MUST* be the AP MAC address */ - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN); - start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); - - /* Remaining entries will be displayed in the order we provide them */ - - /* Add the ESSID */ - iwe.cmd = SIOCGIWESSID; - iwe.u.data.flags = 1; - iwe.u.data.length = min(network->ssid_len, (u8) 32); - start = iwe_stream_add_point(info, start, stop, - &iwe, network->ssid); - - /* Add the protocol name */ - iwe.cmd = SIOCGIWNAME; - snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", - libipw_modes[network->mode]); - start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); - - /* Add mode */ - iwe.cmd = SIOCGIWMODE; - if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { - if (network->capability & WLAN_CAPABILITY_ESS) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_ADHOC; - - start = iwe_stream_add_event(info, start, stop, - &iwe, IW_EV_UINT_LEN); - } - - /* Add channel and frequency */ - /* Note : userspace automatically computes channel using iwrange */ - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel); - iwe.u.freq.e = 6; - iwe.u.freq.i = 0; - start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); - - /* Add encryption capability */ - iwe.cmd = SIOCGIWENCODE; - if (network->capability & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - start = iwe_stream_add_point(info, start, stop, - &iwe, network->ssid); - - /* Add basic and extended rates */ - /* Rate : stuffing multiple values in a single event require a bit - * more of magic - Jean II */ - current_val = start + iwe_stream_lcp_len(info); - iwe.cmd = SIOCGIWRATE; - /* Those two flags are ignored... */ - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; - - for (i = 0, j = 0; i < network->rates_len;) { - if (j < network->rates_ex_len && - ((network->rates_ex[j] & 0x7F) < - (network->rates[i] & 0x7F))) - rate = network->rates_ex[j++] & 0x7F; - else - rate = network->rates[i++] & 0x7F; - /* Bit rate given in 500 kb/s units (+ 0x80) */ - iwe.u.bitrate.value = ((rate & 0x7f) * 500000); - /* Add new value to event */ - current_val = iwe_stream_add_value(info, start, current_val, - stop, &iwe, IW_EV_PARAM_LEN); - } - for (; j < network->rates_ex_len; j++) { - rate = network->rates_ex[j] & 0x7F; - /* Bit rate given in 500 kb/s units (+ 0x80) */ - iwe.u.bitrate.value = ((rate & 0x7f) * 500000); - /* Add new value to event */ - current_val = iwe_stream_add_value(info, start, current_val, - stop, &iwe, IW_EV_PARAM_LEN); - } - /* Check if we added any rate */ - if ((current_val - start) > iwe_stream_lcp_len(info)) - start = current_val; - - /* Add quality statistics */ - iwe.cmd = IWEVQUAL; - iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | - IW_QUAL_NOISE_UPDATED; - - if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) { - iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID | - IW_QUAL_LEVEL_INVALID; - iwe.u.qual.qual = 0; - } else { - if (ieee->perfect_rssi == ieee->worst_rssi) - iwe.u.qual.qual = 100; - else - iwe.u.qual.qual = - (100 * - (ieee->perfect_rssi - ieee->worst_rssi) * - (ieee->perfect_rssi - ieee->worst_rssi) - - (ieee->perfect_rssi - network->stats.rssi) * - (15 * (ieee->perfect_rssi - ieee->worst_rssi) + - 62 * (ieee->perfect_rssi - - network->stats.rssi))) / - ((ieee->perfect_rssi - - ieee->worst_rssi) * (ieee->perfect_rssi - - ieee->worst_rssi)); - if (iwe.u.qual.qual > 100) - iwe.u.qual.qual = 100; - else if (iwe.u.qual.qual < 1) - iwe.u.qual.qual = 0; - } - - if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) { - iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID; - iwe.u.qual.noise = 0; - } else { - iwe.u.qual.noise = network->stats.noise; - } - - if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) { - iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID; - iwe.u.qual.level = 0; - } else { - iwe.u.qual.level = network->stats.signal; - } - - start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); - - iwe.cmd = IWEVCUSTOM; - p = custom; - - iwe.u.data.length = p - custom; - if (iwe.u.data.length) - start = iwe_stream_add_point(info, start, stop, &iwe, custom); - - memset(&iwe, 0, sizeof(iwe)); - if (network->wpa_ie_len) { - char buf[MAX_WPA_IE_LEN]; - memcpy(buf, network->wpa_ie, network->wpa_ie_len); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = network->wpa_ie_len; - start = iwe_stream_add_point(info, start, stop, &iwe, buf); - } - - memset(&iwe, 0, sizeof(iwe)); - if (network->rsn_ie_len) { - char buf[MAX_WPA_IE_LEN]; - memcpy(buf, network->rsn_ie, network->rsn_ie_len); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = network->rsn_ie_len; - start = iwe_stream_add_point(info, start, stop, &iwe, buf); - } - - /* Add EXTRA: Age to display seconds since last beacon/probe response - * for given network. */ - iwe.cmd = IWEVCUSTOM; - p = custom; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), - " Last beacon: %ums ago", - elapsed_jiffies_msecs(network->last_scanned)); - iwe.u.data.length = p - custom; - if (iwe.u.data.length) - start = iwe_stream_add_point(info, start, stop, &iwe, custom); - - /* Add spectrum management information */ - iwe.cmd = -1; - p = custom; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: "); - - if (libipw_get_channel_flags(ieee, network->channel) & - LIBIPW_CH_INVALID) { - iwe.cmd = IWEVCUSTOM; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID "); - } - - if (libipw_get_channel_flags(ieee, network->channel) & - LIBIPW_CH_RADAR_DETECT) { - iwe.cmd = IWEVCUSTOM; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS "); - } - - if (iwe.cmd == IWEVCUSTOM) { - iwe.u.data.length = p - custom; - start = iwe_stream_add_point(info, start, stop, &iwe, custom); - } - - return start; -} - -#define SCAN_ITEM_SIZE 128 - -int libipw_wx_get_scan(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct libipw_network *network; - unsigned long flags; - int err = 0; - - char *ev = extra; - char *stop = ev + wrqu->data.length; - int i = 0; - - LIBIPW_DEBUG_WX("Getting scan\n"); - - spin_lock_irqsave(&ieee->lock, flags); - - list_for_each_entry(network, &ieee->network_list, list) { - i++; - if (stop - ev < SCAN_ITEM_SIZE) { - err = -E2BIG; - break; - } - - if (ieee->scan_age == 0 || - time_after(network->last_scanned + ieee->scan_age, jiffies)) - ev = libipw_translate_scan(ieee, ev, stop, network, - info); - else { - LIBIPW_DEBUG_SCAN("Not showing network '%*pE (%pM)' due to age (%ums).\n", - network->ssid_len, network->ssid, - network->bssid, - elapsed_jiffies_msecs( - network->last_scanned)); - } - } - - spin_unlock_irqrestore(&ieee->lock, flags); - - wrqu->data.length = ev - extra; - wrqu->data.flags = 0; - - LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i); - - return err; -} - -int libipw_wx_set_encode(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *keybuf) -{ - struct iw_point *erq = &(wrqu->encoding); - struct net_device *dev = ieee->dev; - struct libipw_security sec = { - .flags = 0 - }; - int i, key, key_provided, len; - struct lib80211_crypt_data **crypt; - int host_crypto = ieee->host_encrypt || ieee->host_decrypt; - - LIBIPW_DEBUG_WX("SET_ENCODE\n"); - - key = erq->flags & IW_ENCODE_INDEX; - if (key) { - if (key > WEP_KEYS) - return -EINVAL; - key--; - key_provided = 1; - } else { - key_provided = 0; - key = ieee->crypt_info.tx_keyidx; - } - - LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ? - "provided" : "default"); - - crypt = &ieee->crypt_info.crypt[key]; - - if (erq->flags & IW_ENCODE_DISABLED) { - if (key_provided && *crypt) { - LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n", - key); - lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); - } else - LIBIPW_DEBUG_WX("Disabling encryption.\n"); - - /* Check all the keys to see if any are still configured, - * and if no key index was provided, de-init them all */ - for (i = 0; i < WEP_KEYS; i++) { - if (ieee->crypt_info.crypt[i] != NULL) { - if (key_provided) - break; - lib80211_crypt_delayed_deinit(&ieee->crypt_info, - &ieee->crypt_info.crypt[i]); - } - } - - if (i == WEP_KEYS) { - sec.enabled = 0; - sec.encrypt = 0; - sec.level = SEC_LEVEL_0; - sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT; - } - - goto done; - } - - sec.enabled = 1; - sec.encrypt = 1; - sec.flags |= SEC_ENABLED | SEC_ENCRYPT; - - if (*crypt != NULL && (*crypt)->ops != NULL && - strcmp((*crypt)->ops->name, "WEP") != 0) { - /* changing to use WEP; deinit previously used algorithm - * on this key */ - lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); - } - - if (*crypt == NULL && host_crypto) { - struct lib80211_crypt_data *new_crypt; - - /* take WEP into use */ - new_crypt = kzalloc(sizeof(struct lib80211_crypt_data), - GFP_KERNEL); - if (new_crypt == NULL) - return -ENOMEM; - new_crypt->ops = lib80211_get_crypto_ops("WEP"); - if (!new_crypt->ops) { - request_module("lib80211_crypt_wep"); - new_crypt->ops = lib80211_get_crypto_ops("WEP"); - } - - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) - new_crypt->priv = new_crypt->ops->init(key); - - if (!new_crypt->ops || !new_crypt->priv) { - kfree(new_crypt); - new_crypt = NULL; - - printk(KERN_WARNING "%s: could not initialize WEP: " - "load module lib80211_crypt_wep\n", dev->name); - return -EOPNOTSUPP; - } - *crypt = new_crypt; - } - - /* If a new key was provided, set it up */ - if (erq->length > 0) { - len = erq->length <= 5 ? 5 : 13; - memcpy(sec.keys[key], keybuf, erq->length); - if (len > erq->length) - memset(sec.keys[key] + erq->length, 0, - len - erq->length); - LIBIPW_DEBUG_WX("Setting key %d to '%*pE' (%d:%d bytes)\n", - key, len, sec.keys[key], - erq->length, len); - sec.key_sizes[key] = len; - if (*crypt) - (*crypt)->ops->set_key(sec.keys[key], len, NULL, - (*crypt)->priv); - sec.flags |= (1 << key); - /* This ensures a key will be activated if no key is - * explicitly set */ - if (key == sec.active_key) - sec.flags |= SEC_ACTIVE_KEY; - - } else { - if (host_crypto) { - len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN, - NULL, (*crypt)->priv); - if (len == 0) { - /* Set a default key of all 0 */ - LIBIPW_DEBUG_WX("Setting key %d to all " - "zero.\n", key); - memset(sec.keys[key], 0, 13); - (*crypt)->ops->set_key(sec.keys[key], 13, NULL, - (*crypt)->priv); - sec.key_sizes[key] = 13; - sec.flags |= (1 << key); - } - } - /* No key data - just set the default TX key index */ - if (key_provided) { - LIBIPW_DEBUG_WX("Setting key %d to default Tx " - "key.\n", key); - ieee->crypt_info.tx_keyidx = key; - sec.active_key = key; - sec.flags |= SEC_ACTIVE_KEY; - } - } - if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) { - ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED); - sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : - WLAN_AUTH_SHARED_KEY; - sec.flags |= SEC_AUTH_MODE; - LIBIPW_DEBUG_WX("Auth: %s\n", - sec.auth_mode == WLAN_AUTH_OPEN ? - "OPEN" : "SHARED KEY"); - } - - /* For now we just support WEP, so only set that security level... - * TODO: When WPA is added this is one place that needs to change */ - sec.flags |= SEC_LEVEL; - sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */ - sec.encode_alg[key] = SEC_ALG_WEP; - - done: - if (ieee->set_security) - ieee->set_security(dev, &sec); - - return 0; -} - -int libipw_wx_get_encode(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *keybuf) -{ - struct iw_point *erq = &(wrqu->encoding); - int len, key; - struct lib80211_crypt_data *crypt; - struct libipw_security *sec = &ieee->sec; - - LIBIPW_DEBUG_WX("GET_ENCODE\n"); - - key = erq->flags & IW_ENCODE_INDEX; - if (key) { - if (key > WEP_KEYS) - return -EINVAL; - key--; - } else - key = ieee->crypt_info.tx_keyidx; - - crypt = ieee->crypt_info.crypt[key]; - erq->flags = key + 1; - - if (!sec->enabled) { - erq->length = 0; - erq->flags |= IW_ENCODE_DISABLED; - return 0; - } - - len = sec->key_sizes[key]; - memcpy(keybuf, sec->keys[key], len); - - erq->length = len; - erq->flags |= IW_ENCODE_ENABLED; - - if (ieee->open_wep) - erq->flags |= IW_ENCODE_OPEN; - else - erq->flags |= IW_ENCODE_RESTRICTED; - - return 0; -} - -int libipw_wx_set_encodeext(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct net_device *dev = ieee->dev; - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int i, idx, ret = 0; - int group_key = 0; - const char *alg, *module; - struct lib80211_crypto_ops *ops; - struct lib80211_crypt_data **crypt; - - struct libipw_security sec = { - .flags = 0, - }; - - idx = encoding->flags & IW_ENCODE_INDEX; - if (idx) { - if (idx < 1 || idx > WEP_KEYS) - return -EINVAL; - idx--; - } else - idx = ieee->crypt_info.tx_keyidx; - - if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { - crypt = &ieee->crypt_info.crypt[idx]; - group_key = 1; - } else { - /* some Cisco APs use idx>0 for unicast in dynamic WEP */ - if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP) - return -EINVAL; - if (ieee->iw_mode == IW_MODE_INFRA) - crypt = &ieee->crypt_info.crypt[idx]; - else - return -EINVAL; - } - - sec.flags |= SEC_ENABLED | SEC_ENCRYPT; - if ((encoding->flags & IW_ENCODE_DISABLED) || - ext->alg == IW_ENCODE_ALG_NONE) { - if (*crypt) - lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); - - for (i = 0; i < WEP_KEYS; i++) - if (ieee->crypt_info.crypt[i] != NULL) - break; - - if (i == WEP_KEYS) { - sec.enabled = 0; - sec.encrypt = 0; - sec.level = SEC_LEVEL_0; - sec.flags |= SEC_LEVEL; - } - goto done; - } - - sec.enabled = 1; - sec.encrypt = 1; - - if (group_key ? !ieee->host_mc_decrypt : - !(ieee->host_encrypt || ieee->host_decrypt || - ieee->host_encrypt_msdu)) - goto skip_host_crypt; - - switch (ext->alg) { - case IW_ENCODE_ALG_WEP: - alg = "WEP"; - module = "lib80211_crypt_wep"; - break; - case IW_ENCODE_ALG_TKIP: - alg = "TKIP"; - module = "lib80211_crypt_tkip"; - break; - case IW_ENCODE_ALG_CCMP: - alg = "CCMP"; - module = "lib80211_crypt_ccmp"; - break; - default: - LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n", - dev->name, ext->alg); - ret = -EINVAL; - goto done; - } - - ops = lib80211_get_crypto_ops(alg); - if (ops == NULL) { - request_module(module); - ops = lib80211_get_crypto_ops(alg); - } - if (ops == NULL) { - LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n", - dev->name, ext->alg); - ret = -EINVAL; - goto done; - } - - if (*crypt == NULL || (*crypt)->ops != ops) { - struct lib80211_crypt_data *new_crypt; - - lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); - - new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL); - if (new_crypt == NULL) { - ret = -ENOMEM; - goto done; - } - new_crypt->ops = ops; - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) - new_crypt->priv = new_crypt->ops->init(idx); - if (new_crypt->priv == NULL) { - kfree(new_crypt); - ret = -EINVAL; - goto done; - } - *crypt = new_crypt; - } - - if (ext->key_len > 0 && (*crypt)->ops->set_key && - (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, - (*crypt)->priv) < 0) { - LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name); - ret = -EINVAL; - goto done; - } - - skip_host_crypt: - if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { - ieee->crypt_info.tx_keyidx = idx; - sec.active_key = idx; - sec.flags |= SEC_ACTIVE_KEY; - } - - if (ext->alg != IW_ENCODE_ALG_NONE) { - memcpy(sec.keys[idx], ext->key, ext->key_len); - sec.key_sizes[idx] = ext->key_len; - sec.flags |= (1 << idx); - if (ext->alg == IW_ENCODE_ALG_WEP) { - sec.encode_alg[idx] = SEC_ALG_WEP; - sec.flags |= SEC_LEVEL; - sec.level = SEC_LEVEL_1; - } else if (ext->alg == IW_ENCODE_ALG_TKIP) { - sec.encode_alg[idx] = SEC_ALG_TKIP; - sec.flags |= SEC_LEVEL; - sec.level = SEC_LEVEL_2; - } else if (ext->alg == IW_ENCODE_ALG_CCMP) { - sec.encode_alg[idx] = SEC_ALG_CCMP; - sec.flags |= SEC_LEVEL; - sec.level = SEC_LEVEL_3; - } - /* Don't set sec level for group keys. */ - if (group_key) - sec.flags &= ~SEC_LEVEL; - } - done: - if (ieee->set_security) - ieee->set_security(dev, &sec); - - return ret; -} - -int libipw_wx_get_encodeext(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - struct libipw_security *sec = &ieee->sec; - int idx, max_key_len; - - max_key_len = encoding->length - sizeof(*ext); - if (max_key_len < 0) - return -EINVAL; - - idx = encoding->flags & IW_ENCODE_INDEX; - if (idx) { - if (idx < 1 || idx > WEP_KEYS) - return -EINVAL; - idx--; - } else - idx = ieee->crypt_info.tx_keyidx; - - if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) && - ext->alg != IW_ENCODE_ALG_WEP) - if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA) - return -EINVAL; - - encoding->flags = idx + 1; - memset(ext, 0, sizeof(*ext)); - - if (!sec->enabled) { - ext->alg = IW_ENCODE_ALG_NONE; - ext->key_len = 0; - encoding->flags |= IW_ENCODE_DISABLED; - } else { - if (sec->encode_alg[idx] == SEC_ALG_WEP) - ext->alg = IW_ENCODE_ALG_WEP; - else if (sec->encode_alg[idx] == SEC_ALG_TKIP) - ext->alg = IW_ENCODE_ALG_TKIP; - else if (sec->encode_alg[idx] == SEC_ALG_CCMP) - ext->alg = IW_ENCODE_ALG_CCMP; - else - return -EINVAL; - - ext->key_len = sec->key_sizes[idx]; - memcpy(ext->key, sec->keys[idx], ext->key_len); - encoding->flags |= IW_ENCODE_ENABLED; - if (ext->key_len && - (ext->alg == IW_ENCODE_ALG_TKIP || - ext->alg == IW_ENCODE_ALG_CCMP)) - ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID; - - } - - return 0; -} - -EXPORT_SYMBOL(libipw_wx_set_encodeext); -EXPORT_SYMBOL(libipw_wx_get_encodeext); - -EXPORT_SYMBOL(libipw_wx_get_scan); -EXPORT_SYMBOL(libipw_wx_set_encode); -EXPORT_SYMBOL(libipw_wx_get_encode); -- cgit v1.2.3-59-g8ed1b From 7ac9a364c1721a863ecc6cc9aba66e10114908db Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 20:37:11 +0200 Subject: iwlegacy: move under intel directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 1 - drivers/net/wireless/Makefile | 1 - drivers/net/wireless/intel/Kconfig | 1 + drivers/net/wireless/intel/Makefile | 2 + drivers/net/wireless/intel/iwlegacy/3945-debug.c | 511 ++ drivers/net/wireless/intel/iwlegacy/3945-mac.c | 3959 +++++++++++ drivers/net/wireless/intel/iwlegacy/3945-rs.c | 979 +++ drivers/net/wireless/intel/iwlegacy/3945.c | 2741 ++++++++ drivers/net/wireless/intel/iwlegacy/3945.h | 593 ++ drivers/net/wireless/intel/iwlegacy/4965-calib.c | 934 +++ drivers/net/wireless/intel/iwlegacy/4965-debug.c | 752 +++ drivers/net/wireless/intel/iwlegacy/4965-mac.c | 6868 ++++++++++++++++++++ drivers/net/wireless/intel/iwlegacy/4965-rs.c | 2835 ++++++++ drivers/net/wireless/intel/iwlegacy/4965.c | 1950 ++++++ drivers/net/wireless/intel/iwlegacy/4965.h | 1285 ++++ drivers/net/wireless/intel/iwlegacy/Kconfig | 100 + drivers/net/wireless/intel/iwlegacy/Makefile | 17 + drivers/net/wireless/intel/iwlegacy/commands.h | 3370 ++++++++++ drivers/net/wireless/intel/iwlegacy/common.c | 5586 ++++++++++++++++ drivers/net/wireless/intel/iwlegacy/common.h | 3084 +++++++++ drivers/net/wireless/intel/iwlegacy/csr.h | 419 ++ drivers/net/wireless/intel/iwlegacy/debug.c | 1426 ++++ drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h | 92 + drivers/net/wireless/intel/iwlegacy/prph.h | 522 ++ drivers/net/wireless/iwlegacy/3945-debug.c | 511 -- drivers/net/wireless/iwlegacy/3945-mac.c | 3959 ----------- drivers/net/wireless/iwlegacy/3945-rs.c | 979 --- drivers/net/wireless/iwlegacy/3945.c | 2741 -------- drivers/net/wireless/iwlegacy/3945.h | 593 -- drivers/net/wireless/iwlegacy/4965-calib.c | 934 --- drivers/net/wireless/iwlegacy/4965-debug.c | 752 --- drivers/net/wireless/iwlegacy/4965-mac.c | 6868 -------------------- drivers/net/wireless/iwlegacy/4965-rs.c | 2835 -------- drivers/net/wireless/iwlegacy/4965.c | 1950 ------ drivers/net/wireless/iwlegacy/4965.h | 1285 ---- drivers/net/wireless/iwlegacy/Kconfig | 100 - drivers/net/wireless/iwlegacy/Makefile | 17 - drivers/net/wireless/iwlegacy/commands.h | 3370 ---------- drivers/net/wireless/iwlegacy/common.c | 5586 ---------------- drivers/net/wireless/iwlegacy/common.h | 3084 --------- drivers/net/wireless/iwlegacy/csr.h | 419 -- drivers/net/wireless/iwlegacy/debug.c | 1426 ---- drivers/net/wireless/iwlegacy/iwl-spectrum.h | 92 - drivers/net/wireless/iwlegacy/prph.h | 522 -- 45 files changed, 38027 insertions(+), 38026 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlegacy/3945-debug.c create mode 100644 drivers/net/wireless/intel/iwlegacy/3945-mac.c create mode 100644 drivers/net/wireless/intel/iwlegacy/3945-rs.c create mode 100644 drivers/net/wireless/intel/iwlegacy/3945.c create mode 100644 drivers/net/wireless/intel/iwlegacy/3945.h create mode 100644 drivers/net/wireless/intel/iwlegacy/4965-calib.c create mode 100644 drivers/net/wireless/intel/iwlegacy/4965-debug.c create mode 100644 drivers/net/wireless/intel/iwlegacy/4965-mac.c create mode 100644 drivers/net/wireless/intel/iwlegacy/4965-rs.c create mode 100644 drivers/net/wireless/intel/iwlegacy/4965.c create mode 100644 drivers/net/wireless/intel/iwlegacy/4965.h create mode 100644 drivers/net/wireless/intel/iwlegacy/Kconfig create mode 100644 drivers/net/wireless/intel/iwlegacy/Makefile create mode 100644 drivers/net/wireless/intel/iwlegacy/commands.h create mode 100644 drivers/net/wireless/intel/iwlegacy/common.c create mode 100644 drivers/net/wireless/intel/iwlegacy/common.h create mode 100644 drivers/net/wireless/intel/iwlegacy/csr.h create mode 100644 drivers/net/wireless/intel/iwlegacy/debug.c create mode 100644 drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h create mode 100644 drivers/net/wireless/intel/iwlegacy/prph.h delete mode 100644 drivers/net/wireless/iwlegacy/3945-debug.c delete mode 100644 drivers/net/wireless/iwlegacy/3945-mac.c delete mode 100644 drivers/net/wireless/iwlegacy/3945-rs.c delete mode 100644 drivers/net/wireless/iwlegacy/3945.c delete mode 100644 drivers/net/wireless/iwlegacy/3945.h delete mode 100644 drivers/net/wireless/iwlegacy/4965-calib.c delete mode 100644 drivers/net/wireless/iwlegacy/4965-debug.c delete mode 100644 drivers/net/wireless/iwlegacy/4965-mac.c delete mode 100644 drivers/net/wireless/iwlegacy/4965-rs.c delete mode 100644 drivers/net/wireless/iwlegacy/4965.c delete mode 100644 drivers/net/wireless/iwlegacy/4965.h delete mode 100644 drivers/net/wireless/iwlegacy/Kconfig delete mode 100644 drivers/net/wireless/iwlegacy/Makefile delete mode 100644 drivers/net/wireless/iwlegacy/commands.h delete mode 100644 drivers/net/wireless/iwlegacy/common.c delete mode 100644 drivers/net/wireless/iwlegacy/common.h delete mode 100644 drivers/net/wireless/iwlegacy/csr.h delete mode 100644 drivers/net/wireless/iwlegacy/debug.c delete mode 100644 drivers/net/wireless/iwlegacy/iwl-spectrum.h delete mode 100644 drivers/net/wireless/iwlegacy/prph.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index b341dabeb579..83f9c2ba6962 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5641,7 +5641,7 @@ INTEL WIRELESS 3945ABG/BG, 4965AGN (iwlegacy) M: Stanislaw Gruszka L: linux-wireless@vger.kernel.org S: Supported -F: drivers/net/wireless/iwlegacy/ +F: drivers/net/wireless/intel/iwlegacy/ INTEL WIRELESS WIFI LINK (iwlwifi) M: Johannes Berg diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 73e3b53caf45..f293831e2878 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -163,7 +163,6 @@ config MWL8K source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/iwlwifi/Kconfig" -source "drivers/net/wireless/iwlegacy/Kconfig" source "drivers/net/wireless/libertas/Kconfig" source "drivers/net/wireless/orinoco/Kconfig" source "drivers/net/wireless/p54/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 52946d8dd31d..135aa8753f69 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -31,7 +31,6 @@ obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ obj-$(CONFIG_MWL8K) += mwl8k.o obj-$(CONFIG_IWLWIFI) += iwlwifi/ -obj-$(CONFIG_IWLEGACY) += iwlegacy/ obj-$(CONFIG_RT2X00) += rt2x00/ obj-$(CONFIG_WL_MEDIATEK) += mediatek/ diff --git a/drivers/net/wireless/intel/Kconfig b/drivers/net/wireless/intel/Kconfig index 3f8eacc4209a..0a7cd61e528c 100644 --- a/drivers/net/wireless/intel/Kconfig +++ b/drivers/net/wireless/intel/Kconfig @@ -12,5 +12,6 @@ config WLAN_VENDOR_INTEL if WLAN_VENDOR_INTEL source "drivers/net/wireless/intel/ipw2x00/Kconfig" +source "drivers/net/wireless/intel/iwlegacy/Kconfig" endif # WLAN_VENDOR_INTEL diff --git a/drivers/net/wireless/intel/Makefile b/drivers/net/wireless/intel/Makefile index 8e5dcb2d425e..cec507d3c6bf 100644 --- a/drivers/net/wireless/intel/Makefile +++ b/drivers/net/wireless/intel/Makefile @@ -1,2 +1,4 @@ obj-$(CONFIG_IPW2100) += ipw2x00/ obj-$(CONFIG_IPW2200) += ipw2x00/ + +obj-$(CONFIG_IWLEGACY) += iwlegacy/ diff --git a/drivers/net/wireless/intel/iwlegacy/3945-debug.c b/drivers/net/wireless/intel/iwlegacy/3945-debug.c new file mode 100644 index 000000000000..c1b4441fb8b2 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/3945-debug.c @@ -0,0 +1,511 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + +#include "common.h" +#include "3945.h" + +static int +il3945_stats_flag(struct il_priv *il, char *buf, int bufsz) +{ + int p = 0; + + p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", + le32_to_cpu(il->_3945.stats.flag)); + if (le32_to_cpu(il->_3945.stats.flag) & UCODE_STATS_CLEAR_MSK) + p += scnprintf(buf + p, bufsz - p, + "\tStatistics have been cleared\n"); + p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", + (le32_to_cpu(il->_3945.stats.flag) & + UCODE_STATS_FREQUENCY_MSK) ? "2.4 GHz" : "5.2 GHz"); + p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", + (le32_to_cpu(il->_3945.stats.flag) & + UCODE_STATS_NARROW_BAND_MSK) ? "enabled" : "disabled"); + return p; +} + +static ssize_t +il3945_ucode_rx_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + int pos = 0; + char *buf; + int bufsz = + sizeof(struct iwl39_stats_rx_phy) * 40 + + sizeof(struct iwl39_stats_rx_non_phy) * 40 + 400; + ssize_t ret; + struct iwl39_stats_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; + struct iwl39_stats_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; + struct iwl39_stats_rx_non_phy *general, *accum_general; + struct iwl39_stats_rx_non_phy *delta_general, *max_general; + + if (!il_is_alive(il)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IL_ERR("Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* + * The statistic information display here is based on + * the last stats notification from uCode + * might not reflect the current uCode activity + */ + ofdm = &il->_3945.stats.rx.ofdm; + cck = &il->_3945.stats.rx.cck; + general = &il->_3945.stats.rx.general; + accum_ofdm = &il->_3945.accum_stats.rx.ofdm; + accum_cck = &il->_3945.accum_stats.rx.cck; + accum_general = &il->_3945.accum_stats.rx.general; + delta_ofdm = &il->_3945.delta_stats.rx.ofdm; + delta_cck = &il->_3945.delta_stats.rx.cck; + delta_general = &il->_3945.delta_stats.rx.general; + max_ofdm = &il->_3945.max_delta.rx.ofdm; + max_cck = &il->_3945.max_delta.rx.cck; + max_general = &il->_3945.max_delta.rx.general; + + pos += il3945_stats_flag(il, buf, bufsz); + pos += + scnprintf(buf + pos, bufsz - pos, + "%-32s current" + "acumulative delta max\n", + "Statistics_Rx - OFDM:"); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "ina_cnt:", + le32_to_cpu(ofdm->ina_cnt), accum_ofdm->ina_cnt, + delta_ofdm->ina_cnt, max_ofdm->ina_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "fina_cnt:", + le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, + delta_ofdm->fina_cnt, max_ofdm->fina_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "plcp_err:", + le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, + delta_ofdm->plcp_err, max_ofdm->plcp_err); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "crc32_err:", + le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, + delta_ofdm->crc32_err, max_ofdm->crc32_err); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "overrun_err:", + le32_to_cpu(ofdm->overrun_err), accum_ofdm->overrun_err, + delta_ofdm->overrun_err, max_ofdm->overrun_err); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "early_overrun_err:", + le32_to_cpu(ofdm->early_overrun_err), + accum_ofdm->early_overrun_err, + delta_ofdm->early_overrun_err, + max_ofdm->early_overrun_err); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "crc32_good:", + le32_to_cpu(ofdm->crc32_good), accum_ofdm->crc32_good, + delta_ofdm->crc32_good, max_ofdm->crc32_good); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "false_alarm_cnt:", + le32_to_cpu(ofdm->false_alarm_cnt), + accum_ofdm->false_alarm_cnt, delta_ofdm->false_alarm_cnt, + max_ofdm->false_alarm_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "fina_sync_err_cnt:", + le32_to_cpu(ofdm->fina_sync_err_cnt), + accum_ofdm->fina_sync_err_cnt, + delta_ofdm->fina_sync_err_cnt, + max_ofdm->fina_sync_err_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "sfd_timeout:", + le32_to_cpu(ofdm->sfd_timeout), accum_ofdm->sfd_timeout, + delta_ofdm->sfd_timeout, max_ofdm->sfd_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "fina_timeout:", + le32_to_cpu(ofdm->fina_timeout), accum_ofdm->fina_timeout, + delta_ofdm->fina_timeout, max_ofdm->fina_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "unresponded_rts:", + le32_to_cpu(ofdm->unresponded_rts), + accum_ofdm->unresponded_rts, delta_ofdm->unresponded_rts, + max_ofdm->unresponded_rts); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", + "rxe_frame_lmt_ovrun:", + le32_to_cpu(ofdm->rxe_frame_limit_overrun), + accum_ofdm->rxe_frame_limit_overrun, + delta_ofdm->rxe_frame_limit_overrun, + max_ofdm->rxe_frame_limit_overrun); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "sent_ack_cnt:", + le32_to_cpu(ofdm->sent_ack_cnt), accum_ofdm->sent_ack_cnt, + delta_ofdm->sent_ack_cnt, max_ofdm->sent_ack_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "sent_cts_cnt:", + le32_to_cpu(ofdm->sent_cts_cnt), accum_ofdm->sent_cts_cnt, + delta_ofdm->sent_cts_cnt, max_ofdm->sent_cts_cnt); + + pos += + scnprintf(buf + pos, bufsz - pos, + "%-32s current" + "acumulative delta max\n", + "Statistics_Rx - CCK:"); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "ina_cnt:", + le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, + delta_cck->ina_cnt, max_cck->ina_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "fina_cnt:", + le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, + delta_cck->fina_cnt, max_cck->fina_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "plcp_err:", + le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, + delta_cck->plcp_err, max_cck->plcp_err); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "crc32_err:", + le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, + delta_cck->crc32_err, max_cck->crc32_err); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "overrun_err:", + le32_to_cpu(cck->overrun_err), accum_cck->overrun_err, + delta_cck->overrun_err, max_cck->overrun_err); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "early_overrun_err:", + le32_to_cpu(cck->early_overrun_err), + accum_cck->early_overrun_err, + delta_cck->early_overrun_err, max_cck->early_overrun_err); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "crc32_good:", + le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, + delta_cck->crc32_good, max_cck->crc32_good); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "false_alarm_cnt:", + le32_to_cpu(cck->false_alarm_cnt), + accum_cck->false_alarm_cnt, delta_cck->false_alarm_cnt, + max_cck->false_alarm_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "fina_sync_err_cnt:", + le32_to_cpu(cck->fina_sync_err_cnt), + accum_cck->fina_sync_err_cnt, + delta_cck->fina_sync_err_cnt, max_cck->fina_sync_err_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "sfd_timeout:", + le32_to_cpu(cck->sfd_timeout), accum_cck->sfd_timeout, + delta_cck->sfd_timeout, max_cck->sfd_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "fina_timeout:", + le32_to_cpu(cck->fina_timeout), accum_cck->fina_timeout, + delta_cck->fina_timeout, max_cck->fina_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "unresponded_rts:", + le32_to_cpu(cck->unresponded_rts), + accum_cck->unresponded_rts, delta_cck->unresponded_rts, + max_cck->unresponded_rts); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", + "rxe_frame_lmt_ovrun:", + le32_to_cpu(cck->rxe_frame_limit_overrun), + accum_cck->rxe_frame_limit_overrun, + delta_cck->rxe_frame_limit_overrun, + max_cck->rxe_frame_limit_overrun); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "sent_ack_cnt:", + le32_to_cpu(cck->sent_ack_cnt), accum_cck->sent_ack_cnt, + delta_cck->sent_ack_cnt, max_cck->sent_ack_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "sent_cts_cnt:", + le32_to_cpu(cck->sent_cts_cnt), accum_cck->sent_cts_cnt, + delta_cck->sent_cts_cnt, max_cck->sent_cts_cnt); + + pos += + scnprintf(buf + pos, bufsz - pos, + "%-32s current" + "acumulative delta max\n", + "Statistics_Rx - GENERAL:"); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "bogus_cts:", + le32_to_cpu(general->bogus_cts), accum_general->bogus_cts, + delta_general->bogus_cts, max_general->bogus_cts); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "bogus_ack:", + le32_to_cpu(general->bogus_ack), accum_general->bogus_ack, + delta_general->bogus_ack, max_general->bogus_ack); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "non_bssid_frames:", + le32_to_cpu(general->non_bssid_frames), + accum_general->non_bssid_frames, + delta_general->non_bssid_frames, + max_general->non_bssid_frames); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "filtered_frames:", + le32_to_cpu(general->filtered_frames), + accum_general->filtered_frames, + delta_general->filtered_frames, + max_general->filtered_frames); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", + "non_channel_beacons:", + le32_to_cpu(general->non_channel_beacons), + accum_general->non_channel_beacons, + delta_general->non_channel_beacons, + max_general->non_channel_beacons); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il3945_ucode_tx_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + int pos = 0; + char *buf; + int bufsz = (sizeof(struct iwl39_stats_tx) * 48) + 250; + ssize_t ret; + struct iwl39_stats_tx *tx, *accum_tx, *delta_tx, *max_tx; + + if (!il_is_alive(il)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IL_ERR("Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* + * The statistic information display here is based on + * the last stats notification from uCode + * might not reflect the current uCode activity + */ + tx = &il->_3945.stats.tx; + accum_tx = &il->_3945.accum_stats.tx; + delta_tx = &il->_3945.delta_stats.tx; + max_tx = &il->_3945.max_delta.tx; + pos += il3945_stats_flag(il, buf, bufsz); + pos += + scnprintf(buf + pos, bufsz - pos, + "%-32s current" + "acumulative delta max\n", + "Statistics_Tx:"); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "preamble:", + le32_to_cpu(tx->preamble_cnt), accum_tx->preamble_cnt, + delta_tx->preamble_cnt, max_tx->preamble_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "rx_detected_cnt:", + le32_to_cpu(tx->rx_detected_cnt), + accum_tx->rx_detected_cnt, delta_tx->rx_detected_cnt, + max_tx->rx_detected_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "bt_prio_defer_cnt:", + le32_to_cpu(tx->bt_prio_defer_cnt), + accum_tx->bt_prio_defer_cnt, delta_tx->bt_prio_defer_cnt, + max_tx->bt_prio_defer_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "bt_prio_kill_cnt:", + le32_to_cpu(tx->bt_prio_kill_cnt), + accum_tx->bt_prio_kill_cnt, delta_tx->bt_prio_kill_cnt, + max_tx->bt_prio_kill_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "few_bytes_cnt:", + le32_to_cpu(tx->few_bytes_cnt), accum_tx->few_bytes_cnt, + delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "cts_timeout:", + le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, + delta_tx->cts_timeout, max_tx->cts_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "ack_timeout:", + le32_to_cpu(tx->ack_timeout), accum_tx->ack_timeout, + delta_tx->ack_timeout, max_tx->ack_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "expected_ack_cnt:", + le32_to_cpu(tx->expected_ack_cnt), + accum_tx->expected_ack_cnt, delta_tx->expected_ack_cnt, + max_tx->expected_ack_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "actual_ack_cnt:", + le32_to_cpu(tx->actual_ack_cnt), accum_tx->actual_ack_cnt, + delta_tx->actual_ack_cnt, max_tx->actual_ack_cnt); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il3945_ucode_general_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct iwl39_stats_general) * 10 + 300; + ssize_t ret; + struct iwl39_stats_general *general, *accum_general; + struct iwl39_stats_general *delta_general, *max_general; + struct stats_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; + struct iwl39_stats_div *div, *accum_div, *delta_div, *max_div; + + if (!il_is_alive(il)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IL_ERR("Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* + * The statistic information display here is based on + * the last stats notification from uCode + * might not reflect the current uCode activity + */ + general = &il->_3945.stats.general; + dbg = &il->_3945.stats.general.dbg; + div = &il->_3945.stats.general.div; + accum_general = &il->_3945.accum_stats.general; + delta_general = &il->_3945.delta_stats.general; + max_general = &il->_3945.max_delta.general; + accum_dbg = &il->_3945.accum_stats.general.dbg; + delta_dbg = &il->_3945.delta_stats.general.dbg; + max_dbg = &il->_3945.max_delta.general.dbg; + accum_div = &il->_3945.accum_stats.general.div; + delta_div = &il->_3945.delta_stats.general.div; + max_div = &il->_3945.max_delta.general.div; + pos += il3945_stats_flag(il, buf, bufsz); + pos += + scnprintf(buf + pos, bufsz - pos, + "%-32s current" + "acumulative delta max\n", + "Statistics_General:"); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "burst_check:", + le32_to_cpu(dbg->burst_check), accum_dbg->burst_check, + delta_dbg->burst_check, max_dbg->burst_check); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "burst_count:", + le32_to_cpu(dbg->burst_count), accum_dbg->burst_count, + delta_dbg->burst_count, max_dbg->burst_count); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "sleep_time:", + le32_to_cpu(general->sleep_time), + accum_general->sleep_time, delta_general->sleep_time, + max_general->sleep_time); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "slots_out:", + le32_to_cpu(general->slots_out), accum_general->slots_out, + delta_general->slots_out, max_general->slots_out); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "slots_idle:", + le32_to_cpu(general->slots_idle), + accum_general->slots_idle, delta_general->slots_idle, + max_general->slots_idle); + pos += + scnprintf(buf + pos, bufsz - pos, "ttl_timestamp:\t\t\t%u\n", + le32_to_cpu(general->ttl_timestamp)); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "tx_on_a:", + le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, + delta_div->tx_on_a, max_div->tx_on_a); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "tx_on_b:", + le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, + delta_div->tx_on_b, max_div->tx_on_b); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "exec_time:", + le32_to_cpu(div->exec_time), accum_div->exec_time, + delta_div->exec_time, max_div->exec_time); + pos += + scnprintf(buf + pos, bufsz - pos, + " %-30s %10u %10u %10u %10u\n", "probe_time:", + le32_to_cpu(div->probe_time), accum_div->probe_time, + delta_div->probe_time, max_div->probe_time); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +const struct il_debugfs_ops il3945_debugfs_ops = { + .rx_stats_read = il3945_ucode_rx_stats_read, + .tx_stats_read = il3945_ucode_tx_stats_read, + .general_stats_read = il3945_ucode_general_stats_read, +}; diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c new file mode 100644 index 000000000000..af1b3e6839fa --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -0,0 +1,3959 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define DRV_NAME "iwl3945" + +#include "commands.h" +#include "common.h" +#include "3945.h" +#include "iwl-spectrum.h" + +/* + * module name, copyright, version, etc. + */ + +#define DRV_DESCRIPTION \ +"Intel(R) PRO/Wireless 3945ABG/BG Network Connection driver for Linux" + +#ifdef CONFIG_IWLEGACY_DEBUG +#define VD "d" +#else +#define VD +#endif + +/* + * add "s" to indicate spectrum measurement included. + * we add it here to be consistent with previous releases in which + * this was configurable. + */ +#define DRV_VERSION IWLWIFI_VERSION VD "s" +#define DRV_COPYRIGHT "Copyright(c) 2003-2011 Intel Corporation" +#define DRV_AUTHOR "" + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_LICENSE("GPL"); + + /* module parameters */ +struct il_mod_params il3945_mod_params = { + .sw_crypto = 1, + .restart_fw = 1, + .disable_hw_scan = 1, + /* the rest are 0 by default */ +}; + +/** + * il3945_get_antenna_flags - Get antenna flags for RXON command + * @il: eeprom and antenna fields are used to determine antenna flags + * + * il->eeprom39 is used to determine if antenna AUX/MAIN are reversed + * il3945_mod_params.antenna specifies the antenna diversity mode: + * + * IL_ANTENNA_DIVERSITY - NIC selects best antenna by itself + * IL_ANTENNA_MAIN - Force MAIN antenna + * IL_ANTENNA_AUX - Force AUX antenna + */ +__le32 +il3945_get_antenna_flags(const struct il_priv *il) +{ + struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; + + switch (il3945_mod_params.antenna) { + case IL_ANTENNA_DIVERSITY: + return 0; + + case IL_ANTENNA_MAIN: + if (eeprom->antenna_switch_type) + return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK; + return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK; + + case IL_ANTENNA_AUX: + if (eeprom->antenna_switch_type) + return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK; + return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK; + } + + /* bad antenna selector value */ + IL_ERR("Bad antenna selector value (0x%x)\n", + il3945_mod_params.antenna); + + return 0; /* "diversity" is default if error */ +} + +static int +il3945_set_ccmp_dynamic_key_info(struct il_priv *il, + struct ieee80211_key_conf *keyconf, u8 sta_id) +{ + unsigned long flags; + __le16 key_flags = 0; + int ret; + + key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK); + key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + + if (sta_id == il->hw_params.bcast_id) + key_flags |= STA_KEY_MULTICAST_MSK; + + keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + keyconf->hw_key_idx = keyconf->keyidx; + key_flags &= ~STA_KEY_FLG_INVALID; + + spin_lock_irqsave(&il->sta_lock, flags); + il->stations[sta_id].keyinfo.cipher = keyconf->cipher; + il->stations[sta_id].keyinfo.keylen = keyconf->keylen; + memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); + + memcpy(il->stations[sta_id].sta.key.key, keyconf->key, keyconf->keylen); + + if ((il->stations[sta_id].sta.key. + key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) + il->stations[sta_id].sta.key.key_offset = + il_get_free_ucode_key_idx(il); + /* else, we are overriding an existing key => no need to allocated room + * in uCode. */ + + WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, + "no space for a new key"); + + il->stations[sta_id].sta.key.key_flags = key_flags; + il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; + il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + + D_INFO("hwcrypto: modify ucode station key info\n"); + + ret = il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC); + + spin_unlock_irqrestore(&il->sta_lock, flags); + + return ret; +} + +static int +il3945_set_tkip_dynamic_key_info(struct il_priv *il, + struct ieee80211_key_conf *keyconf, u8 sta_id) +{ + return -EOPNOTSUPP; +} + +static int +il3945_set_wep_dynamic_key_info(struct il_priv *il, + struct ieee80211_key_conf *keyconf, u8 sta_id) +{ + return -EOPNOTSUPP; +} + +static int +il3945_clear_sta_key_info(struct il_priv *il, u8 sta_id) +{ + unsigned long flags; + struct il_addsta_cmd sta_cmd; + + spin_lock_irqsave(&il->sta_lock, flags); + memset(&il->stations[sta_id].keyinfo, 0, sizeof(struct il_hw_key)); + memset(&il->stations[sta_id].sta.key, 0, sizeof(struct il4965_keyinfo)); + il->stations[sta_id].sta.key.key_flags = STA_KEY_FLG_NO_ENC; + il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; + il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &il->stations[sta_id].sta, + sizeof(struct il_addsta_cmd)); + spin_unlock_irqrestore(&il->sta_lock, flags); + + D_INFO("hwcrypto: clear ucode station key info\n"); + return il_send_add_sta(il, &sta_cmd, CMD_SYNC); +} + +static int +il3945_set_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, + u8 sta_id) +{ + int ret = 0; + + keyconf->hw_key_idx = HW_KEY_DYNAMIC; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + ret = il3945_set_ccmp_dynamic_key_info(il, keyconf, sta_id); + break; + case WLAN_CIPHER_SUITE_TKIP: + ret = il3945_set_tkip_dynamic_key_info(il, keyconf, sta_id); + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + ret = il3945_set_wep_dynamic_key_info(il, keyconf, sta_id); + break; + default: + IL_ERR("Unknown alg: %s alg=%x\n", __func__, keyconf->cipher); + ret = -EINVAL; + } + + D_WEP("Set dynamic key: alg=%x len=%d idx=%d sta=%d ret=%d\n", + keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret); + + return ret; +} + +static int +il3945_remove_static_key(struct il_priv *il) +{ + int ret = -EOPNOTSUPP; + + return ret; +} + +static int +il3945_set_static_key(struct il_priv *il, struct ieee80211_key_conf *key) +{ + if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) + return -EOPNOTSUPP; + + IL_ERR("Static key invalid: cipher %x\n", key->cipher); + return -EINVAL; +} + +static void +il3945_clear_free_frames(struct il_priv *il) +{ + struct list_head *element; + + D_INFO("%d frames on pre-allocated heap on clear.\n", il->frames_count); + + while (!list_empty(&il->free_frames)) { + element = il->free_frames.next; + list_del(element); + kfree(list_entry(element, struct il3945_frame, list)); + il->frames_count--; + } + + if (il->frames_count) { + IL_WARN("%d frames still in use. Did we lose one?\n", + il->frames_count); + il->frames_count = 0; + } +} + +static struct il3945_frame * +il3945_get_free_frame(struct il_priv *il) +{ + struct il3945_frame *frame; + struct list_head *element; + if (list_empty(&il->free_frames)) { + frame = kzalloc(sizeof(*frame), GFP_KERNEL); + if (!frame) { + IL_ERR("Could not allocate frame!\n"); + return NULL; + } + + il->frames_count++; + return frame; + } + + element = il->free_frames.next; + list_del(element); + return list_entry(element, struct il3945_frame, list); +} + +static void +il3945_free_frame(struct il_priv *il, struct il3945_frame *frame) +{ + memset(frame, 0, sizeof(*frame)); + list_add(&frame->list, &il->free_frames); +} + +unsigned int +il3945_fill_beacon_frame(struct il_priv *il, struct ieee80211_hdr *hdr, + int left) +{ + + if (!il_is_associated(il) || !il->beacon_skb) + return 0; + + if (il->beacon_skb->len > left) + return 0; + + memcpy(hdr, il->beacon_skb->data, il->beacon_skb->len); + + return il->beacon_skb->len; +} + +static int +il3945_send_beacon_cmd(struct il_priv *il) +{ + struct il3945_frame *frame; + unsigned int frame_size; + int rc; + u8 rate; + + frame = il3945_get_free_frame(il); + + if (!frame) { + IL_ERR("Could not obtain free frame buffer for beacon " + "command.\n"); + return -ENOMEM; + } + + rate = il_get_lowest_plcp(il); + + frame_size = il3945_hw_get_beacon_cmd(il, frame, rate); + + rc = il_send_cmd_pdu(il, C_TX_BEACON, frame_size, &frame->u.cmd[0]); + + il3945_free_frame(il, frame); + + return rc; +} + +static void +il3945_unset_hw_params(struct il_priv *il) +{ + if (il->_3945.shared_virt) + dma_free_coherent(&il->pci_dev->dev, + sizeof(struct il3945_shared), + il->_3945.shared_virt, il->_3945.shared_phys); +} + +static void +il3945_build_tx_cmd_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info, + struct il_device_cmd *cmd, + struct sk_buff *skb_frag, int sta_id) +{ + struct il3945_tx_cmd *tx_cmd = (struct il3945_tx_cmd *)cmd->cmd.payload; + struct il_hw_key *keyinfo = &il->stations[sta_id].keyinfo; + + tx_cmd->sec_ctl = 0; + + switch (keyinfo->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + tx_cmd->sec_ctl = TX_CMD_SEC_CCM; + memcpy(tx_cmd->key, keyinfo->key, keyinfo->keylen); + D_TX("tx_cmd with AES hwcrypto\n"); + break; + + case WLAN_CIPHER_SUITE_TKIP: + break; + + case WLAN_CIPHER_SUITE_WEP104: + tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; + /* fall through */ + case WLAN_CIPHER_SUITE_WEP40: + tx_cmd->sec_ctl |= + TX_CMD_SEC_WEP | (info->control.hw_key-> + hw_key_idx & TX_CMD_SEC_MSK) << + TX_CMD_SEC_SHIFT; + + memcpy(&tx_cmd->key[3], keyinfo->key, keyinfo->keylen); + + D_TX("Configuring packet for WEP encryption " "with key %d\n", + info->control.hw_key->hw_key_idx); + break; + + default: + IL_ERR("Unknown encode cipher %x\n", keyinfo->cipher); + break; + } +} + +/* + * handle build C_TX command notification. + */ +static void +il3945_build_tx_cmd_basic(struct il_priv *il, struct il_device_cmd *cmd, + struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, u8 std_id) +{ + struct il3945_tx_cmd *tx_cmd = (struct il3945_tx_cmd *)cmd->cmd.payload; + __le32 tx_flags = tx_cmd->tx_flags; + __le16 fc = hdr->frame_control; + + tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + tx_flags |= TX_CMD_FLG_ACK_MSK; + if (ieee80211_is_mgmt(fc)) + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + if (ieee80211_is_probe_resp(fc) && + !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) + tx_flags |= TX_CMD_FLG_TSF_MSK; + } else { + tx_flags &= (~TX_CMD_FLG_ACK_MSK); + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + } + + tx_cmd->sta_id = std_id; + if (ieee80211_has_morefrags(fc)) + tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; + + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tx_cmd->tid_tspec = qc[0] & 0xf; + tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; + } else { + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + } + + il_tx_cmd_protection(il, info, fc, &tx_flags); + + tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); + if (ieee80211_is_mgmt(fc)) { + if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); + else + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); + } else { + tx_cmd->timeout.pm_frame_timeout = 0; + } + + tx_cmd->driver_txop = 0; + tx_cmd->tx_flags = tx_flags; + tx_cmd->next_frame_len = 0; +} + +/* + * start C_TX command process + */ +static int +il3945_tx_skb(struct il_priv *il, + struct ieee80211_sta *sta, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct il3945_tx_cmd *tx_cmd; + struct il_tx_queue *txq = NULL; + struct il_queue *q = NULL; + struct il_device_cmd *out_cmd; + struct il_cmd_meta *out_meta; + dma_addr_t phys_addr; + dma_addr_t txcmd_phys; + int txq_id = skb_get_queue_mapping(skb); + u16 len, idx, hdr_len; + u16 firstlen, secondlen; + u8 id; + u8 unicast; + u8 sta_id; + u8 tid = 0; + __le16 fc; + u8 wait_write_ptr = 0; + unsigned long flags; + + spin_lock_irqsave(&il->lock, flags); + if (il_is_rfkill(il)) { + D_DROP("Dropping - RF KILL\n"); + goto drop_unlock; + } + + if ((ieee80211_get_tx_rate(il->hw, info)->hw_value & 0xFF) == + IL_INVALID_RATE) { + IL_ERR("ERROR: No TX rate available.\n"); + goto drop_unlock; + } + + unicast = !is_multicast_ether_addr(hdr->addr1); + id = 0; + + fc = hdr->frame_control; + +#ifdef CONFIG_IWLEGACY_DEBUG + if (ieee80211_is_auth(fc)) + D_TX("Sending AUTH frame\n"); + else if (ieee80211_is_assoc_req(fc)) + D_TX("Sending ASSOC frame\n"); + else if (ieee80211_is_reassoc_req(fc)) + D_TX("Sending REASSOC frame\n"); +#endif + + spin_unlock_irqrestore(&il->lock, flags); + + hdr_len = ieee80211_hdrlen(fc); + + /* Find idx into station table for destination station */ + sta_id = il_sta_id_or_broadcast(il, sta); + if (sta_id == IL_INVALID_STATION) { + D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1); + goto drop; + } + + D_RATE("station Id %d\n", sta_id); + + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + if (unlikely(tid >= MAX_TID_COUNT)) + goto drop; + } + + /* Descriptor for chosen Tx queue */ + txq = &il->txq[txq_id]; + q = &txq->q; + + if ((il_queue_space(q) < q->high_mark)) + goto drop; + + spin_lock_irqsave(&il->lock, flags); + + idx = il_get_cmd_idx(q, q->write_ptr, 0); + + txq->skbs[q->write_ptr] = skb; + + /* Init first empty entry in queue's array of Tx/cmd buffers */ + out_cmd = txq->cmd[idx]; + out_meta = &txq->meta[idx]; + tx_cmd = (struct il3945_tx_cmd *)out_cmd->cmd.payload; + memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); + memset(tx_cmd, 0, sizeof(*tx_cmd)); + + /* + * Set up the Tx-command (not MAC!) header. + * Store the chosen Tx queue and TFD idx within the sequence field; + * after Tx, uCode's Tx response will return this value so driver can + * locate the frame within the tx queue and do post-tx processing. + */ + out_cmd->hdr.cmd = C_TX; + out_cmd->hdr.sequence = + cpu_to_le16((u16) + (QUEUE_TO_SEQ(txq_id) | IDX_TO_SEQ(q->write_ptr))); + + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, hdr_len); + + if (info->control.hw_key) + il3945_build_tx_cmd_hwcrypto(il, info, out_cmd, skb, sta_id); + + /* TODO need this for burst mode later on */ + il3945_build_tx_cmd_basic(il, out_cmd, info, hdr, sta_id); + + il3945_hw_build_tx_cmd_rate(il, out_cmd, info, hdr, sta_id); + + /* Total # bytes to be transmitted */ + tx_cmd->len = cpu_to_le16((u16) skb->len); + + tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_A_MSK; + tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_B_MSK; + + /* + * Use the first empty entry in this queue's command buffer array + * to contain the Tx command and MAC header concatenated together + * (payload data will be in another buffer). + * Size of this varies, due to varying MAC header length. + * If end is not dword aligned, we'll have 2 extra bytes at the end + * of the MAC header (device reads on dword boundaries). + * We'll tell device about this padding later. + */ + len = + sizeof(struct il3945_tx_cmd) + sizeof(struct il_cmd_header) + + hdr_len; + firstlen = (len + 3) & ~3; + + /* Physical address of this Tx command's header (not MAC header!), + * within command buffer array. */ + txcmd_phys = + pci_map_single(il->pci_dev, &out_cmd->hdr, firstlen, + PCI_DMA_TODEVICE); + if (unlikely(pci_dma_mapping_error(il->pci_dev, txcmd_phys))) + goto drop_unlock; + + /* Set up TFD's 2nd entry to point directly to remainder of skb, + * if any (802.11 null frames have no payload). */ + secondlen = skb->len - hdr_len; + if (secondlen > 0) { + phys_addr = + pci_map_single(il->pci_dev, skb->data + hdr_len, secondlen, + PCI_DMA_TODEVICE); + if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr))) + goto drop_unlock; + } + + /* Add buffer containing Tx command and MAC(!) header to TFD's + * first entry */ + il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, firstlen, 1, 0); + dma_unmap_addr_set(out_meta, mapping, txcmd_phys); + dma_unmap_len_set(out_meta, len, firstlen); + if (secondlen > 0) + il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, secondlen, 0, + U32_PAD(secondlen)); + + if (!ieee80211_has_morefrags(hdr->frame_control)) { + txq->need_update = 1; + } else { + wait_write_ptr = 1; + txq->need_update = 0; + } + + il_update_stats(il, true, fc, skb->len); + + D_TX("sequence nr = 0X%x\n", le16_to_cpu(out_cmd->hdr.sequence)); + D_TX("tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); + il_print_hex_dump(il, IL_DL_TX, tx_cmd, sizeof(*tx_cmd)); + il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd->hdr, + ieee80211_hdrlen(fc)); + + /* Tell device the write idx *just past* this latest filled TFD */ + q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); + il_txq_update_write_ptr(il, txq); + spin_unlock_irqrestore(&il->lock, flags); + + if (il_queue_space(q) < q->high_mark && il->mac80211_registered) { + if (wait_write_ptr) { + spin_lock_irqsave(&il->lock, flags); + txq->need_update = 1; + il_txq_update_write_ptr(il, txq); + spin_unlock_irqrestore(&il->lock, flags); + } + + il_stop_queue(il, txq); + } + + return 0; + +drop_unlock: + spin_unlock_irqrestore(&il->lock, flags); +drop: + return -1; +} + +static int +il3945_get_measurement(struct il_priv *il, + struct ieee80211_measurement_params *params, u8 type) +{ + struct il_spectrum_cmd spectrum; + struct il_rx_pkt *pkt; + struct il_host_cmd cmd = { + .id = C_SPECTRUM_MEASUREMENT, + .data = (void *)&spectrum, + .flags = CMD_WANT_SKB, + }; + u32 add_time = le64_to_cpu(params->start_time); + int rc; + int spectrum_resp_status; + int duration = le16_to_cpu(params->duration); + + if (il_is_associated(il)) + add_time = + il_usecs_to_beacons(il, + le64_to_cpu(params->start_time) - + il->_3945.last_tsf, + le16_to_cpu(il->timing.beacon_interval)); + + memset(&spectrum, 0, sizeof(spectrum)); + + spectrum.channel_count = cpu_to_le16(1); + spectrum.flags = + RXON_FLG_TSF2HOST_MSK | RXON_FLG_ANT_A_MSK | RXON_FLG_DIS_DIV_MSK; + spectrum.filter_flags = MEASUREMENT_FILTER_FLAG; + cmd.len = sizeof(spectrum); + spectrum.len = cpu_to_le16(cmd.len - sizeof(spectrum.len)); + + if (il_is_associated(il)) + spectrum.start_time = + il_add_beacon_time(il, il->_3945.last_beacon_time, add_time, + le16_to_cpu(il->timing.beacon_interval)); + else + spectrum.start_time = 0; + + spectrum.channels[0].duration = cpu_to_le32(duration * TIME_UNIT); + spectrum.channels[0].channel = params->channel; + spectrum.channels[0].type = type; + if (il->active.flags & RXON_FLG_BAND_24G_MSK) + spectrum.flags |= + RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK | + RXON_FLG_TGG_PROTECT_MSK; + + rc = il_send_cmd_sync(il, &cmd); + if (rc) + return rc; + + pkt = (struct il_rx_pkt *)cmd.reply_page; + if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { + IL_ERR("Bad return from N_RX_ON_ASSOC command\n"); + rc = -EIO; + } + + spectrum_resp_status = le16_to_cpu(pkt->u.spectrum.status); + switch (spectrum_resp_status) { + case 0: /* Command will be handled */ + if (pkt->u.spectrum.id != 0xff) { + D_INFO("Replaced existing measurement: %d\n", + pkt->u.spectrum.id); + il->measurement_status &= ~MEASUREMENT_READY; + } + il->measurement_status |= MEASUREMENT_ACTIVE; + rc = 0; + break; + + case 1: /* Command will not be handled */ + rc = -EAGAIN; + break; + } + + il_free_pages(il, cmd.reply_page); + + return rc; +} + +static void +il3945_hdl_alive(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_alive_resp *palive; + struct delayed_work *pwork; + + palive = &pkt->u.alive_frame; + + D_INFO("Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n", + palive->is_valid, palive->ver_type, palive->ver_subtype); + + if (palive->ver_subtype == INITIALIZE_SUBTYPE) { + D_INFO("Initialization Alive received.\n"); + memcpy(&il->card_alive_init, &pkt->u.alive_frame, + sizeof(struct il_alive_resp)); + pwork = &il->init_alive_start; + } else { + D_INFO("Runtime Alive received.\n"); + memcpy(&il->card_alive, &pkt->u.alive_frame, + sizeof(struct il_alive_resp)); + pwork = &il->alive_start; + il3945_disable_events(il); + } + + /* We delay the ALIVE response by 5ms to + * give the HW RF Kill time to activate... */ + if (palive->is_valid == UCODE_VALID_OK) + queue_delayed_work(il->workqueue, pwork, msecs_to_jiffies(5)); + else + IL_WARN("uCode did not respond OK.\n"); +} + +static void +il3945_hdl_add_sta(struct il_priv *il, struct il_rx_buf *rxb) +{ +#ifdef CONFIG_IWLEGACY_DEBUG + struct il_rx_pkt *pkt = rxb_addr(rxb); +#endif + + D_RX("Received C_ADD_STA: 0x%02X\n", pkt->u.status); +} + +static void +il3945_hdl_beacon(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il3945_beacon_notif *beacon = &(pkt->u.beacon_status); +#ifdef CONFIG_IWLEGACY_DEBUG + u8 rate = beacon->beacon_notify_hdr.rate; + + D_RX("beacon status %x retries %d iss %d " "tsf %d %d rate %d\n", + le32_to_cpu(beacon->beacon_notify_hdr.status) & TX_STATUS_MSK, + beacon->beacon_notify_hdr.failure_frame, + le32_to_cpu(beacon->ibss_mgr_status), + le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate); +#endif + + il->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); + +} + +/* Handle notification from uCode that card's power state is changing + * due to software, hardware, or critical temperature RFKILL */ +static void +il3945_hdl_card_state(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); + unsigned long status = il->status; + + IL_WARN("Card state received: HW:%s SW:%s\n", + (flags & HW_CARD_DISABLED) ? "Kill" : "On", + (flags & SW_CARD_DISABLED) ? "Kill" : "On"); + + _il_wr(il, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + if (flags & HW_CARD_DISABLED) + set_bit(S_RFKILL, &il->status); + else + clear_bit(S_RFKILL, &il->status); + + il_scan_cancel(il); + + if ((test_bit(S_RFKILL, &status) != + test_bit(S_RFKILL, &il->status))) + wiphy_rfkill_set_hw_state(il->hw->wiphy, + test_bit(S_RFKILL, &il->status)); + else + wake_up(&il->wait_command_queue); +} + +/** + * il3945_setup_handlers - Initialize Rx handler callbacks + * + * Setup the RX handlers for each of the reply types sent from the uCode + * to the host. + * + * This function chains into the hardware specific files for them to setup + * any hardware specific handlers as well. + */ +static void +il3945_setup_handlers(struct il_priv *il) +{ + il->handlers[N_ALIVE] = il3945_hdl_alive; + il->handlers[C_ADD_STA] = il3945_hdl_add_sta; + il->handlers[N_ERROR] = il_hdl_error; + il->handlers[N_CHANNEL_SWITCH] = il_hdl_csa; + il->handlers[N_SPECTRUM_MEASUREMENT] = il_hdl_spectrum_measurement; + il->handlers[N_PM_SLEEP] = il_hdl_pm_sleep; + il->handlers[N_PM_DEBUG_STATS] = il_hdl_pm_debug_stats; + il->handlers[N_BEACON] = il3945_hdl_beacon; + + /* + * The same handler is used for both the REPLY to a discrete + * stats request from the host as well as for the periodic + * stats notifications (after received beacons) from the uCode. + */ + il->handlers[C_STATS] = il3945_hdl_c_stats; + il->handlers[N_STATS] = il3945_hdl_stats; + + il_setup_rx_scan_handlers(il); + il->handlers[N_CARD_STATE] = il3945_hdl_card_state; + + /* Set up hardware specific Rx handlers */ + il3945_hw_handler_setup(il); +} + +/************************** RX-FUNCTIONS ****************************/ +/* + * Rx theory of operation + * + * The host allocates 32 DMA target addresses and passes the host address + * to the firmware at register IL_RFDS_TBL_LOWER + N * RFD_SIZE where N is + * 0 to 31 + * + * Rx Queue Indexes + * The host/firmware share two idx registers for managing the Rx buffers. + * + * The READ idx maps to the first position that the firmware may be writing + * to -- the driver can read up to (but not including) this position and get + * good data. + * The READ idx is managed by the firmware once the card is enabled. + * + * The WRITE idx maps to the last position the driver has read from -- the + * position preceding WRITE is the last slot the firmware can place a packet. + * + * The queue is empty (no good data) if WRITE = READ - 1, and is full if + * WRITE = READ. + * + * During initialization, the host sets up the READ queue position to the first + * IDX position, and WRITE to the last (READ - 1 wrapped) + * + * When the firmware places a packet in a buffer, it will advance the READ idx + * and fire the RX interrupt. The driver can then query the READ idx and + * process as many packets as possible, moving the WRITE idx forward as it + * resets the Rx queue buffers with new memory. + * + * The management in the driver is as follows: + * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When + * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled + * to replenish the iwl->rxq->rx_free. + * + In il3945_rx_replenish (scheduled) if 'processed' != 'read' then the + * iwl->rxq is replenished and the READ IDX is updated (updating the + * 'processed' and 'read' driver idxes as well) + * + A received packet is processed and handed to the kernel network stack, + * detached from the iwl->rxq. The driver 'processed' idx is updated. + * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free + * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ + * IDX is not incremented and iwl->status(RX_STALLED) is set. If there + * were enough free buffers and RX_STALLED is set it is cleared. + * + * + * Driver sequence: + * + * il3945_rx_replenish() Replenishes rx_free list from rx_used, and calls + * il3945_rx_queue_restock + * il3945_rx_queue_restock() Moves available buffers from rx_free into Rx + * queue, updates firmware pointers, and updates + * the WRITE idx. If insufficient rx_free buffers + * are available, schedules il3945_rx_replenish + * + * -- enable interrupts -- + * ISR - il3945_rx() Detach il_rx_bufs from pool up to the + * READ IDX, detaching the SKB from the pool. + * Moves the packet buffer from queue to rx_used. + * Calls il3945_rx_queue_restock to refill any empty + * slots. + * ... + * + */ + +/** + * il3945_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr + */ +static inline __le32 +il3945_dma_addr2rbd_ptr(struct il_priv *il, dma_addr_t dma_addr) +{ + return cpu_to_le32((u32) dma_addr); +} + +/** + * il3945_rx_queue_restock - refill RX queue from pre-allocated pool + * + * If there are slots in the RX queue that need to be restocked, + * and we have free pre-allocated buffers, fill the ranks as much + * as we can, pulling from rx_free. + * + * This moves the 'write' idx forward to catch up with 'processed', and + * also updates the memory address in the firmware to reference the new + * target buffer. + */ +static void +il3945_rx_queue_restock(struct il_priv *il) +{ + struct il_rx_queue *rxq = &il->rxq; + struct list_head *element; + struct il_rx_buf *rxb; + unsigned long flags; + int write; + + spin_lock_irqsave(&rxq->lock, flags); + write = rxq->write & ~0x7; + while (il_rx_queue_space(rxq) > 0 && rxq->free_count) { + /* Get next free Rx buffer, remove from free list */ + element = rxq->rx_free.next; + rxb = list_entry(element, struct il_rx_buf, list); + list_del(element); + + /* Point to Rx buffer via next RBD in circular buffer */ + rxq->bd[rxq->write] = + il3945_dma_addr2rbd_ptr(il, rxb->page_dma); + rxq->queue[rxq->write] = rxb; + rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; + rxq->free_count--; + } + spin_unlock_irqrestore(&rxq->lock, flags); + /* If the pre-allocated buffer pool is dropping low, schedule to + * refill it */ + if (rxq->free_count <= RX_LOW_WATERMARK) + queue_work(il->workqueue, &il->rx_replenish); + + /* If we've added more space for the firmware to place data, tell it. + * Increment device's write pointer in multiples of 8. */ + if (rxq->write_actual != (rxq->write & ~0x7) || + abs(rxq->write - rxq->read) > 7) { + spin_lock_irqsave(&rxq->lock, flags); + rxq->need_update = 1; + spin_unlock_irqrestore(&rxq->lock, flags); + il_rx_queue_update_write_ptr(il, rxq); + } +} + +/** + * il3945_rx_replenish - Move all used packet from rx_used to rx_free + * + * When moving to rx_free an SKB is allocated for the slot. + * + * Also restock the Rx queue via il3945_rx_queue_restock. + * This is called as a scheduled work item (except for during initialization) + */ +static void +il3945_rx_allocate(struct il_priv *il, gfp_t priority) +{ + struct il_rx_queue *rxq = &il->rxq; + struct list_head *element; + struct il_rx_buf *rxb; + struct page *page; + dma_addr_t page_dma; + unsigned long flags; + gfp_t gfp_mask = priority; + + while (1) { + spin_lock_irqsave(&rxq->lock, flags); + if (list_empty(&rxq->rx_used)) { + spin_unlock_irqrestore(&rxq->lock, flags); + return; + } + spin_unlock_irqrestore(&rxq->lock, flags); + + if (rxq->free_count > RX_LOW_WATERMARK) + gfp_mask |= __GFP_NOWARN; + + if (il->hw_params.rx_page_order > 0) + gfp_mask |= __GFP_COMP; + + /* Alloc a new receive buffer */ + page = alloc_pages(gfp_mask, il->hw_params.rx_page_order); + if (!page) { + if (net_ratelimit()) + D_INFO("Failed to allocate SKB buffer.\n"); + if (rxq->free_count <= RX_LOW_WATERMARK && + net_ratelimit()) + IL_ERR("Failed to allocate SKB buffer with %0x." + "Only %u free buffers remaining.\n", + priority, rxq->free_count); + /* We don't reschedule replenish work here -- we will + * call the restock method and if it still needs + * more buffers it will schedule replenish */ + break; + } + + /* Get physical address of RB/SKB */ + page_dma = + pci_map_page(il->pci_dev, page, 0, + PAGE_SIZE << il->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + + if (unlikely(pci_dma_mapping_error(il->pci_dev, page_dma))) { + __free_pages(page, il->hw_params.rx_page_order); + break; + } + + spin_lock_irqsave(&rxq->lock, flags); + + if (list_empty(&rxq->rx_used)) { + spin_unlock_irqrestore(&rxq->lock, flags); + pci_unmap_page(il->pci_dev, page_dma, + PAGE_SIZE << il->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + __free_pages(page, il->hw_params.rx_page_order); + return; + } + + element = rxq->rx_used.next; + rxb = list_entry(element, struct il_rx_buf, list); + list_del(element); + + rxb->page = page; + rxb->page_dma = page_dma; + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + il->alloc_rxb_page++; + + spin_unlock_irqrestore(&rxq->lock, flags); + } +} + +void +il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq) +{ + unsigned long flags; + int i; + spin_lock_irqsave(&rxq->lock, flags); + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + /* Fill the rx_used queue with _all_ of the Rx buffers */ + for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { + /* In the reset function, these buffers may have been allocated + * to an SKB, so we need to unmap and free potential storage */ + if (rxq->pool[i].page != NULL) { + pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, + PAGE_SIZE << il->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + __il_free_pages(il, rxq->pool[i].page); + rxq->pool[i].page = NULL; + } + list_add_tail(&rxq->pool[i].list, &rxq->rx_used); + } + + /* Set us so that we have processed and used all buffers, but have + * not restocked the Rx queue with fresh buffers */ + rxq->read = rxq->write = 0; + rxq->write_actual = 0; + rxq->free_count = 0; + spin_unlock_irqrestore(&rxq->lock, flags); +} + +void +il3945_rx_replenish(void *data) +{ + struct il_priv *il = data; + unsigned long flags; + + il3945_rx_allocate(il, GFP_KERNEL); + + spin_lock_irqsave(&il->lock, flags); + il3945_rx_queue_restock(il); + spin_unlock_irqrestore(&il->lock, flags); +} + +static void +il3945_rx_replenish_now(struct il_priv *il) +{ + il3945_rx_allocate(il, GFP_ATOMIC); + + il3945_rx_queue_restock(il); +} + +/* Assumes that the skb field of the buffers in 'pool' is kept accurate. + * If an SKB has been detached, the POOL needs to have its SKB set to NULL + * This free routine walks the list of POOL entries and if SKB is set to + * non NULL it is unmapped and freed + */ +static void +il3945_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq) +{ + int i; + for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { + if (rxq->pool[i].page != NULL) { + pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, + PAGE_SIZE << il->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + __il_free_pages(il, rxq->pool[i].page); + rxq->pool[i].page = NULL; + } + } + + dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, + rxq->bd_dma); + dma_free_coherent(&il->pci_dev->dev, sizeof(struct il_rb_status), + rxq->rb_stts, rxq->rb_stts_dma); + rxq->bd = NULL; + rxq->rb_stts = NULL; +} + +/* Convert linear signal-to-noise ratio into dB */ +static u8 ratio2dB[100] = { +/* 0 1 2 3 4 5 6 7 8 9 */ + 0, 0, 6, 10, 12, 14, 16, 17, 18, 19, /* 00 - 09 */ + 20, 21, 22, 22, 23, 23, 24, 25, 26, 26, /* 10 - 19 */ + 26, 26, 26, 27, 27, 28, 28, 28, 29, 29, /* 20 - 29 */ + 29, 30, 30, 30, 31, 31, 31, 31, 32, 32, /* 30 - 39 */ + 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, /* 40 - 49 */ + 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, /* 50 - 59 */ + 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, /* 60 - 69 */ + 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, /* 70 - 79 */ + 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, /* 80 - 89 */ + 39, 39, 39, 39, 39, 40, 40, 40, 40, 40 /* 90 - 99 */ +}; + +/* Calculates a relative dB value from a ratio of linear + * (i.e. not dB) signal levels. + * Conversion assumes that levels are voltages (20*log), not powers (10*log). */ +int +il3945_calc_db_from_ratio(int sig_ratio) +{ + /* 1000:1 or higher just report as 60 dB */ + if (sig_ratio >= 1000) + return 60; + + /* 100:1 or higher, divide by 10 and use table, + * add 20 dB to make up for divide by 10 */ + if (sig_ratio >= 100) + return 20 + (int)ratio2dB[sig_ratio / 10]; + + /* We shouldn't see this */ + if (sig_ratio < 1) + return 0; + + /* Use table for ratios 1:1 - 99:1 */ + return (int)ratio2dB[sig_ratio]; +} + +/** + * il3945_rx_handle - Main entry function for receiving responses from uCode + * + * Uses the il->handlers callback function array to invoke + * the appropriate handlers, including command responses, + * frame-received notifications, and other notifications. + */ +static void +il3945_rx_handle(struct il_priv *il) +{ + struct il_rx_buf *rxb; + struct il_rx_pkt *pkt; + struct il_rx_queue *rxq = &il->rxq; + u32 r, i; + int reclaim; + unsigned long flags; + u8 fill_rx = 0; + u32 count = 8; + int total_empty = 0; + + /* uCode's read idx (stored in shared DRAM) indicates the last Rx + * buffer that the driver may process (last buffer filled by ucode). */ + r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF; + i = rxq->read; + + /* calculate total frames need to be restock after handling RX */ + total_empty = r - rxq->write_actual; + if (total_empty < 0) + total_empty += RX_QUEUE_SIZE; + + if (total_empty > (RX_QUEUE_SIZE / 2)) + fill_rx = 1; + /* Rx interrupt, but nothing sent from uCode */ + if (i == r) + D_RX("r = %d, i = %d\n", r, i); + + while (i != r) { + int len; + + rxb = rxq->queue[i]; + + /* If an RXB doesn't have a Rx queue slot associated with it, + * then a bug has been introduced in the queue refilling + * routines -- catch it here */ + BUG_ON(rxb == NULL); + + rxq->queue[i] = NULL; + + pci_unmap_page(il->pci_dev, rxb->page_dma, + PAGE_SIZE << il->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + pkt = rxb_addr(rxb); + + len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; + len += sizeof(u32); /* account for status word */ + + reclaim = il_need_reclaim(il, pkt); + + /* Based on type of command response or notification, + * handle those that need handling via function in + * handlers table. See il3945_setup_handlers() */ + if (il->handlers[pkt->hdr.cmd]) { + D_RX("r = %d, i = %d, %s, 0x%02x\n", r, i, + il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); + il->isr_stats.handlers[pkt->hdr.cmd]++; + il->handlers[pkt->hdr.cmd] (il, rxb); + } else { + /* No handling needed */ + D_RX("r %d i %d No handler needed for %s, 0x%02x\n", r, + i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); + } + + /* + * XXX: After here, we should always check rxb->page + * against NULL before touching it or its virtual + * memory (pkt). Because some handler might have + * already taken or freed the pages. + */ + + if (reclaim) { + /* Invoke any callbacks, transfer the buffer to caller, + * and fire off the (possibly) blocking il_send_cmd() + * as we reclaim the driver command queue */ + if (rxb->page) + il_tx_cmd_complete(il, rxb); + else + IL_WARN("Claim null rxb?\n"); + } + + /* Reuse the page if possible. For notification packets and + * SKBs that fail to Rx correctly, add them back into the + * rx_free list for reuse later. */ + spin_lock_irqsave(&rxq->lock, flags); + if (rxb->page != NULL) { + rxb->page_dma = + pci_map_page(il->pci_dev, rxb->page, 0, + PAGE_SIZE << il->hw_params. + rx_page_order, PCI_DMA_FROMDEVICE); + if (unlikely(pci_dma_mapping_error(il->pci_dev, + rxb->page_dma))) { + __il_free_pages(il, rxb->page); + rxb->page = NULL; + list_add_tail(&rxb->list, &rxq->rx_used); + } else { + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + } + } else + list_add_tail(&rxb->list, &rxq->rx_used); + + spin_unlock_irqrestore(&rxq->lock, flags); + + i = (i + 1) & RX_QUEUE_MASK; + /* If there are a lot of unused frames, + * restock the Rx queue so ucode won't assert. */ + if (fill_rx) { + count++; + if (count >= 8) { + rxq->read = i; + il3945_rx_replenish_now(il); + count = 0; + } + } + } + + /* Backtrack one entry */ + rxq->read = i; + if (fill_rx) + il3945_rx_replenish_now(il); + else + il3945_rx_queue_restock(il); +} + +/* call this function to flush any scheduled tasklet */ +static inline void +il3945_synchronize_irq(struct il_priv *il) +{ + /* wait to make sure we flush pending tasklet */ + synchronize_irq(il->pci_dev->irq); + tasklet_kill(&il->irq_tasklet); +} + +static const char * +il3945_desc_lookup(int i) +{ + switch (i) { + case 1: + return "FAIL"; + case 2: + return "BAD_PARAM"; + case 3: + return "BAD_CHECKSUM"; + case 4: + return "NMI_INTERRUPT"; + case 5: + return "SYSASSERT"; + case 6: + return "FATAL_ERROR"; + } + + return "UNKNOWN"; +} + +#define ERROR_START_OFFSET (1 * sizeof(u32)) +#define ERROR_ELEM_SIZE (7 * sizeof(u32)) + +void +il3945_dump_nic_error_log(struct il_priv *il) +{ + u32 i; + u32 desc, time, count, base, data1; + u32 blink1, blink2, ilink1, ilink2; + + base = le32_to_cpu(il->card_alive.error_event_table_ptr); + + if (!il3945_hw_valid_rtc_data_addr(base)) { + IL_ERR("Not valid error log pointer 0x%08X\n", base); + return; + } + + count = il_read_targ_mem(il, base); + + if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { + IL_ERR("Start IWL Error Log Dump:\n"); + IL_ERR("Status: 0x%08lX, count: %d\n", il->status, count); + } + + IL_ERR("Desc Time asrtPC blink2 " + "ilink1 nmiPC Line\n"); + for (i = ERROR_START_OFFSET; + i < (count * ERROR_ELEM_SIZE) + ERROR_START_OFFSET; + i += ERROR_ELEM_SIZE) { + desc = il_read_targ_mem(il, base + i); + time = il_read_targ_mem(il, base + i + 1 * sizeof(u32)); + blink1 = il_read_targ_mem(il, base + i + 2 * sizeof(u32)); + blink2 = il_read_targ_mem(il, base + i + 3 * sizeof(u32)); + ilink1 = il_read_targ_mem(il, base + i + 4 * sizeof(u32)); + ilink2 = il_read_targ_mem(il, base + i + 5 * sizeof(u32)); + data1 = il_read_targ_mem(il, base + i + 6 * sizeof(u32)); + + IL_ERR("%-13s (0x%X) %010u 0x%05X 0x%05X 0x%05X 0x%05X %u\n\n", + il3945_desc_lookup(desc), desc, time, blink1, blink2, + ilink1, ilink2, data1); + } +} + +static void +il3945_irq_tasklet(struct il_priv *il) +{ + u32 inta, handled = 0; + u32 inta_fh; + unsigned long flags; +#ifdef CONFIG_IWLEGACY_DEBUG + u32 inta_mask; +#endif + + spin_lock_irqsave(&il->lock, flags); + + /* Ack/clear/reset pending uCode interrupts. + * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, + * and will clear only when CSR_FH_INT_STATUS gets cleared. */ + inta = _il_rd(il, CSR_INT); + _il_wr(il, CSR_INT, inta); + + /* Ack/clear/reset pending flow-handler (DMA) interrupts. + * Any new interrupts that happen after this, either while we're + * in this tasklet, or later, will show up in next ISR/tasklet. */ + inta_fh = _il_rd(il, CSR_FH_INT_STATUS); + _il_wr(il, CSR_FH_INT_STATUS, inta_fh); + +#ifdef CONFIG_IWLEGACY_DEBUG + if (il_get_debug_level(il) & IL_DL_ISR) { + /* just for debug */ + inta_mask = _il_rd(il, CSR_INT_MASK); + D_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, + inta_mask, inta_fh); + } +#endif + + spin_unlock_irqrestore(&il->lock, flags); + + /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not + * atomic, make sure that inta covers all the interrupts that + * we've discovered, even if FH interrupt came in just after + * reading CSR_INT. */ + if (inta_fh & CSR39_FH_INT_RX_MASK) + inta |= CSR_INT_BIT_FH_RX; + if (inta_fh & CSR39_FH_INT_TX_MASK) + inta |= CSR_INT_BIT_FH_TX; + + /* Now service all interrupt bits discovered above. */ + if (inta & CSR_INT_BIT_HW_ERR) { + IL_ERR("Hardware error detected. Restarting.\n"); + + /* Tell the device to stop sending interrupts */ + il_disable_interrupts(il); + + il->isr_stats.hw++; + il_irq_handle_error(il); + + handled |= CSR_INT_BIT_HW_ERR; + + return; + } +#ifdef CONFIG_IWLEGACY_DEBUG + if (il_get_debug_level(il) & (IL_DL_ISR)) { + /* NIC fires this, but we don't use it, redundant with WAKEUP */ + if (inta & CSR_INT_BIT_SCD) { + D_ISR("Scheduler finished to transmit " + "the frame/frames.\n"); + il->isr_stats.sch++; + } + + /* Alive notification via Rx interrupt will do the real work */ + if (inta & CSR_INT_BIT_ALIVE) { + D_ISR("Alive interrupt\n"); + il->isr_stats.alive++; + } + } +#endif + /* Safely ignore these bits for debug checks below */ + inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); + + /* Error detected by uCode */ + if (inta & CSR_INT_BIT_SW_ERR) { + IL_ERR("Microcode SW error detected. " "Restarting 0x%X.\n", + inta); + il->isr_stats.sw++; + il_irq_handle_error(il); + handled |= CSR_INT_BIT_SW_ERR; + } + + /* uCode wakes up after power-down sleep */ + if (inta & CSR_INT_BIT_WAKEUP) { + D_ISR("Wakeup interrupt\n"); + il_rx_queue_update_write_ptr(il, &il->rxq); + + spin_lock_irqsave(&il->lock, flags); + il_txq_update_write_ptr(il, &il->txq[0]); + il_txq_update_write_ptr(il, &il->txq[1]); + il_txq_update_write_ptr(il, &il->txq[2]); + il_txq_update_write_ptr(il, &il->txq[3]); + il_txq_update_write_ptr(il, &il->txq[4]); + spin_unlock_irqrestore(&il->lock, flags); + + il->isr_stats.wakeup++; + handled |= CSR_INT_BIT_WAKEUP; + } + + /* All uCode command responses, including Tx command responses, + * Rx "responses" (frame-received notification), and other + * notifications from uCode come through here*/ + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { + il3945_rx_handle(il); + il->isr_stats.rx++; + handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); + } + + if (inta & CSR_INT_BIT_FH_TX) { + D_ISR("Tx interrupt\n"); + il->isr_stats.tx++; + + _il_wr(il, CSR_FH_INT_STATUS, (1 << 6)); + il_wr(il, FH39_TCSR_CREDIT(FH39_SRVC_CHNL), 0x0); + handled |= CSR_INT_BIT_FH_TX; + } + + if (inta & ~handled) { + IL_ERR("Unhandled INTA bits 0x%08x\n", inta & ~handled); + il->isr_stats.unhandled++; + } + + if (inta & ~il->inta_mask) { + IL_WARN("Disabled INTA bits 0x%08x were pending\n", + inta & ~il->inta_mask); + IL_WARN(" with inta_fh = 0x%08x\n", inta_fh); + } + + /* Re-enable all interrupts */ + /* only Re-enable if disabled by irq */ + if (test_bit(S_INT_ENABLED, &il->status)) + il_enable_interrupts(il); + +#ifdef CONFIG_IWLEGACY_DEBUG + if (il_get_debug_level(il) & (IL_DL_ISR)) { + inta = _il_rd(il, CSR_INT); + inta_mask = _il_rd(il, CSR_INT_MASK); + inta_fh = _il_rd(il, CSR_FH_INT_STATUS); + D_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, " + "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); + } +#endif +} + +static int +il3945_get_channels_for_scan(struct il_priv *il, enum ieee80211_band band, + u8 is_active, u8 n_probes, + struct il3945_scan_channel *scan_ch, + struct ieee80211_vif *vif) +{ + struct ieee80211_channel *chan; + const struct ieee80211_supported_band *sband; + const struct il_channel_info *ch_info; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int added, i; + + sband = il_get_hw_mode(il, band); + if (!sband) + return 0; + + active_dwell = il_get_active_dwell_time(il, band, n_probes); + passive_dwell = il_get_passive_dwell_time(il, band, vif); + + if (passive_dwell <= active_dwell) + passive_dwell = active_dwell + 1; + + for (i = 0, added = 0; i < il->scan_request->n_channels; i++) { + chan = il->scan_request->channels[i]; + + if (chan->band != band) + continue; + + scan_ch->channel = chan->hw_value; + + ch_info = il_get_channel_info(il, band, scan_ch->channel); + if (!il_is_channel_valid(ch_info)) { + D_SCAN("Channel %d is INVALID for this band.\n", + scan_ch->channel); + continue; + } + + scan_ch->active_dwell = cpu_to_le16(active_dwell); + scan_ch->passive_dwell = cpu_to_le16(passive_dwell); + /* If passive , set up for auto-switch + * and use long active_dwell time. + */ + if (!is_active || il_is_channel_passive(ch_info) || + (chan->flags & IEEE80211_CHAN_NO_IR)) { + scan_ch->type = 0; /* passive */ + if (IL_UCODE_API(il->ucode_ver) == 1) + scan_ch->active_dwell = + cpu_to_le16(passive_dwell - 1); + } else { + scan_ch->type = 1; /* active */ + } + + /* Set direct probe bits. These may be used both for active + * scan channels (probes gets sent right away), + * or for passive channels (probes get se sent only after + * hearing clear Rx packet).*/ + if (IL_UCODE_API(il->ucode_ver) >= 2) { + if (n_probes) + scan_ch->type |= IL39_SCAN_PROBE_MASK(n_probes); + } else { + /* uCode v1 does not allow setting direct probe bits on + * passive channel. */ + if ((scan_ch->type & 1) && n_probes) + scan_ch->type |= IL39_SCAN_PROBE_MASK(n_probes); + } + + /* Set txpower levels to defaults */ + scan_ch->tpc.dsp_atten = 110; + /* scan_pwr_info->tpc.dsp_atten; */ + + /*scan_pwr_info->tpc.tx_gain; */ + if (band == IEEE80211_BAND_5GHZ) + scan_ch->tpc.tx_gain = ((1 << 5) | (3 << 3)) | 3; + else { + scan_ch->tpc.tx_gain = ((1 << 5) | (5 << 3)); + /* NOTE: if we were doing 6Mb OFDM for scans we'd use + * power level: + * scan_ch->tpc.tx_gain = ((1 << 5) | (2 << 3)) | 3; + */ + } + + D_SCAN("Scanning %d [%s %d]\n", scan_ch->channel, + (scan_ch->type & 1) ? "ACTIVE" : "PASSIVE", + (scan_ch->type & 1) ? active_dwell : passive_dwell); + + scan_ch++; + added++; + } + + D_SCAN("total channels to scan %d\n", added); + return added; +} + +static void +il3945_init_hw_rates(struct il_priv *il, struct ieee80211_rate *rates) +{ + int i; + + for (i = 0; i < RATE_COUNT_LEGACY; i++) { + rates[i].bitrate = il3945_rates[i].ieee * 5; + rates[i].hw_value = i; /* Rate scaling will work on idxes */ + rates[i].hw_value_short = i; + rates[i].flags = 0; + if (i > IL39_LAST_OFDM_RATE || i < IL_FIRST_OFDM_RATE) { + /* + * If CCK != 1M then set short preamble rate flag. + */ + rates[i].flags |= + (il3945_rates[i].plcp == + 10) ? 0 : IEEE80211_RATE_SHORT_PREAMBLE; + } + } +} + +/****************************************************************************** + * + * uCode download functions + * + ******************************************************************************/ + +static void +il3945_dealloc_ucode_pci(struct il_priv *il) +{ + il_free_fw_desc(il->pci_dev, &il->ucode_code); + il_free_fw_desc(il->pci_dev, &il->ucode_data); + il_free_fw_desc(il->pci_dev, &il->ucode_data_backup); + il_free_fw_desc(il->pci_dev, &il->ucode_init); + il_free_fw_desc(il->pci_dev, &il->ucode_init_data); + il_free_fw_desc(il->pci_dev, &il->ucode_boot); +} + +/** + * il3945_verify_inst_full - verify runtime uCode image in card vs. host, + * looking at all data. + */ +static int +il3945_verify_inst_full(struct il_priv *il, __le32 * image, u32 len) +{ + u32 val; + u32 save_len = len; + int rc = 0; + u32 errcnt; + + D_INFO("ucode inst image size is %u\n", len); + + il_wr(il, HBUS_TARG_MEM_RADDR, IL39_RTC_INST_LOWER_BOUND); + + errcnt = 0; + for (; len > 0; len -= sizeof(u32), image++) { + /* read data comes through single port, auto-incr addr */ + /* NOTE: Use the debugless read so we don't flood kernel log + * if IL_DL_IO is set */ + val = _il_rd(il, HBUS_TARG_MEM_RDAT); + if (val != le32_to_cpu(*image)) { + IL_ERR("uCode INST section is invalid at " + "offset 0x%x, is 0x%x, s/b 0x%x\n", + save_len - len, val, le32_to_cpu(*image)); + rc = -EIO; + errcnt++; + if (errcnt >= 20) + break; + } + } + + if (!errcnt) + D_INFO("ucode image in INSTRUCTION memory is good\n"); + + return rc; +} + +/** + * il3945_verify_inst_sparse - verify runtime uCode image in card vs. host, + * using sample data 100 bytes apart. If these sample points are good, + * it's a pretty good bet that everything between them is good, too. + */ +static int +il3945_verify_inst_sparse(struct il_priv *il, __le32 * image, u32 len) +{ + u32 val; + int rc = 0; + u32 errcnt = 0; + u32 i; + + D_INFO("ucode inst image size is %u\n", len); + + for (i = 0; i < len; i += 100, image += 100 / sizeof(u32)) { + /* read data comes through single port, auto-incr addr */ + /* NOTE: Use the debugless read so we don't flood kernel log + * if IL_DL_IO is set */ + il_wr(il, HBUS_TARG_MEM_RADDR, i + IL39_RTC_INST_LOWER_BOUND); + val = _il_rd(il, HBUS_TARG_MEM_RDAT); + if (val != le32_to_cpu(*image)) { +#if 0 /* Enable this if you want to see details */ + IL_ERR("uCode INST section is invalid at " + "offset 0x%x, is 0x%x, s/b 0x%x\n", i, val, + *image); +#endif + rc = -EIO; + errcnt++; + if (errcnt >= 3) + break; + } + } + + return rc; +} + +/** + * il3945_verify_ucode - determine which instruction image is in SRAM, + * and verify its contents + */ +static int +il3945_verify_ucode(struct il_priv *il) +{ + __le32 *image; + u32 len; + int rc = 0; + + /* Try bootstrap */ + image = (__le32 *) il->ucode_boot.v_addr; + len = il->ucode_boot.len; + rc = il3945_verify_inst_sparse(il, image, len); + if (rc == 0) { + D_INFO("Bootstrap uCode is good in inst SRAM\n"); + return 0; + } + + /* Try initialize */ + image = (__le32 *) il->ucode_init.v_addr; + len = il->ucode_init.len; + rc = il3945_verify_inst_sparse(il, image, len); + if (rc == 0) { + D_INFO("Initialize uCode is good in inst SRAM\n"); + return 0; + } + + /* Try runtime/protocol */ + image = (__le32 *) il->ucode_code.v_addr; + len = il->ucode_code.len; + rc = il3945_verify_inst_sparse(il, image, len); + if (rc == 0) { + D_INFO("Runtime uCode is good in inst SRAM\n"); + return 0; + } + + IL_ERR("NO VALID UCODE IMAGE IN INSTRUCTION SRAM!!\n"); + + /* Since nothing seems to match, show first several data entries in + * instruction SRAM, so maybe visual inspection will give a clue. + * Selection of bootstrap image (vs. other images) is arbitrary. */ + image = (__le32 *) il->ucode_boot.v_addr; + len = il->ucode_boot.len; + rc = il3945_verify_inst_full(il, image, len); + + return rc; +} + +static void +il3945_nic_start(struct il_priv *il) +{ + /* Remove all resets to allow NIC to operate */ + _il_wr(il, CSR_RESET, 0); +} + +#define IL3945_UCODE_GET(item) \ +static u32 il3945_ucode_get_##item(const struct il_ucode_header *ucode)\ +{ \ + return le32_to_cpu(ucode->v1.item); \ +} + +static u32 +il3945_ucode_get_header_size(u32 api_ver) +{ + return 24; +} + +static u8 * +il3945_ucode_get_data(const struct il_ucode_header *ucode) +{ + return (u8 *) ucode->v1.data; +} + +IL3945_UCODE_GET(inst_size); +IL3945_UCODE_GET(data_size); +IL3945_UCODE_GET(init_size); +IL3945_UCODE_GET(init_data_size); +IL3945_UCODE_GET(boot_size); + +/** + * il3945_read_ucode - Read uCode images from disk file. + * + * Copy into buffers for card to fetch via bus-mastering + */ +static int +il3945_read_ucode(struct il_priv *il) +{ + const struct il_ucode_header *ucode; + int ret = -EINVAL, idx; + const struct firmware *ucode_raw; + /* firmware file name contains uCode/driver compatibility version */ + const char *name_pre = il->cfg->fw_name_pre; + const unsigned int api_max = il->cfg->ucode_api_max; + const unsigned int api_min = il->cfg->ucode_api_min; + char buf[25]; + u8 *src; + size_t len; + u32 api_ver, inst_size, data_size, init_size, init_data_size, boot_size; + + /* Ask kernel firmware_class module to get the boot firmware off disk. + * request_firmware() is synchronous, file is in memory on return. */ + for (idx = api_max; idx >= api_min; idx--) { + sprintf(buf, "%s%u%s", name_pre, idx, ".ucode"); + ret = request_firmware(&ucode_raw, buf, &il->pci_dev->dev); + if (ret < 0) { + IL_ERR("%s firmware file req failed: %d\n", buf, ret); + if (ret == -ENOENT) + continue; + else + goto error; + } else { + if (idx < api_max) + IL_ERR("Loaded firmware %s, " + "which is deprecated. " + " Please use API v%u instead.\n", buf, + api_max); + D_INFO("Got firmware '%s' file " + "(%zd bytes) from disk\n", buf, ucode_raw->size); + break; + } + } + + if (ret < 0) + goto error; + + /* Make sure that we got at least our header! */ + if (ucode_raw->size < il3945_ucode_get_header_size(1)) { + IL_ERR("File size way too small!\n"); + ret = -EINVAL; + goto err_release; + } + + /* Data from ucode file: header followed by uCode images */ + ucode = (struct il_ucode_header *)ucode_raw->data; + + il->ucode_ver = le32_to_cpu(ucode->ver); + api_ver = IL_UCODE_API(il->ucode_ver); + inst_size = il3945_ucode_get_inst_size(ucode); + data_size = il3945_ucode_get_data_size(ucode); + init_size = il3945_ucode_get_init_size(ucode); + init_data_size = il3945_ucode_get_init_data_size(ucode); + boot_size = il3945_ucode_get_boot_size(ucode); + src = il3945_ucode_get_data(ucode); + + /* api_ver should match the api version forming part of the + * firmware filename ... but we don't check for that and only rely + * on the API version read from firmware header from here on forward */ + + if (api_ver < api_min || api_ver > api_max) { + IL_ERR("Driver unable to support your firmware API. " + "Driver supports v%u, firmware is v%u.\n", api_max, + api_ver); + il->ucode_ver = 0; + ret = -EINVAL; + goto err_release; + } + if (api_ver != api_max) + IL_ERR("Firmware has old API version. Expected %u, " + "got %u. New firmware can be obtained " + "from http://www.intellinuxwireless.org.\n", api_max, + api_ver); + + IL_INFO("loaded firmware version %u.%u.%u.%u\n", + IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver), + IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver)); + + snprintf(il->hw->wiphy->fw_version, sizeof(il->hw->wiphy->fw_version), + "%u.%u.%u.%u", IL_UCODE_MAJOR(il->ucode_ver), + IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver), + IL_UCODE_SERIAL(il->ucode_ver)); + + D_INFO("f/w package hdr ucode version raw = 0x%x\n", il->ucode_ver); + D_INFO("f/w package hdr runtime inst size = %u\n", inst_size); + D_INFO("f/w package hdr runtime data size = %u\n", data_size); + D_INFO("f/w package hdr init inst size = %u\n", init_size); + D_INFO("f/w package hdr init data size = %u\n", init_data_size); + D_INFO("f/w package hdr boot inst size = %u\n", boot_size); + + /* Verify size of file vs. image size info in file's header */ + if (ucode_raw->size != + il3945_ucode_get_header_size(api_ver) + inst_size + data_size + + init_size + init_data_size + boot_size) { + + D_INFO("uCode file size %zd does not match expected size\n", + ucode_raw->size); + ret = -EINVAL; + goto err_release; + } + + /* Verify that uCode images will fit in card's SRAM */ + if (inst_size > IL39_MAX_INST_SIZE) { + D_INFO("uCode instr len %d too large to fit in\n", inst_size); + ret = -EINVAL; + goto err_release; + } + + if (data_size > IL39_MAX_DATA_SIZE) { + D_INFO("uCode data len %d too large to fit in\n", data_size); + ret = -EINVAL; + goto err_release; + } + if (init_size > IL39_MAX_INST_SIZE) { + D_INFO("uCode init instr len %d too large to fit in\n", + init_size); + ret = -EINVAL; + goto err_release; + } + if (init_data_size > IL39_MAX_DATA_SIZE) { + D_INFO("uCode init data len %d too large to fit in\n", + init_data_size); + ret = -EINVAL; + goto err_release; + } + if (boot_size > IL39_MAX_BSM_SIZE) { + D_INFO("uCode boot instr len %d too large to fit in\n", + boot_size); + ret = -EINVAL; + goto err_release; + } + + /* Allocate ucode buffers for card's bus-master loading ... */ + + /* Runtime instructions and 2 copies of data: + * 1) unmodified from disk + * 2) backup cache for save/restore during power-downs */ + il->ucode_code.len = inst_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_code); + + il->ucode_data.len = data_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_data); + + il->ucode_data_backup.len = data_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_data_backup); + + if (!il->ucode_code.v_addr || !il->ucode_data.v_addr || + !il->ucode_data_backup.v_addr) + goto err_pci_alloc; + + /* Initialization instructions and data */ + if (init_size && init_data_size) { + il->ucode_init.len = init_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_init); + + il->ucode_init_data.len = init_data_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_init_data); + + if (!il->ucode_init.v_addr || !il->ucode_init_data.v_addr) + goto err_pci_alloc; + } + + /* Bootstrap (instructions only, no data) */ + if (boot_size) { + il->ucode_boot.len = boot_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_boot); + + if (!il->ucode_boot.v_addr) + goto err_pci_alloc; + } + + /* Copy images into buffers for card's bus-master reads ... */ + + /* Runtime instructions (first block of data in file) */ + len = inst_size; + D_INFO("Copying (but not loading) uCode instr len %zd\n", len); + memcpy(il->ucode_code.v_addr, src, len); + src += len; + + D_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", + il->ucode_code.v_addr, (u32) il->ucode_code.p_addr); + + /* Runtime data (2nd block) + * NOTE: Copy into backup buffer will be done in il3945_up() */ + len = data_size; + D_INFO("Copying (but not loading) uCode data len %zd\n", len); + memcpy(il->ucode_data.v_addr, src, len); + memcpy(il->ucode_data_backup.v_addr, src, len); + src += len; + + /* Initialization instructions (3rd block) */ + if (init_size) { + len = init_size; + D_INFO("Copying (but not loading) init instr len %zd\n", len); + memcpy(il->ucode_init.v_addr, src, len); + src += len; + } + + /* Initialization data (4th block) */ + if (init_data_size) { + len = init_data_size; + D_INFO("Copying (but not loading) init data len %zd\n", len); + memcpy(il->ucode_init_data.v_addr, src, len); + src += len; + } + + /* Bootstrap instructions (5th block) */ + len = boot_size; + D_INFO("Copying (but not loading) boot instr len %zd\n", len); + memcpy(il->ucode_boot.v_addr, src, len); + + /* We have our copies now, allow OS release its copies */ + release_firmware(ucode_raw); + return 0; + +err_pci_alloc: + IL_ERR("failed to allocate pci memory\n"); + ret = -ENOMEM; + il3945_dealloc_ucode_pci(il); + +err_release: + release_firmware(ucode_raw); + +error: + return ret; +} + +/** + * il3945_set_ucode_ptrs - Set uCode address location + * + * Tell initialization uCode where to find runtime uCode. + * + * BSM registers initially contain pointers to initialization uCode. + * We need to replace them to load runtime uCode inst and data, + * and to save runtime data when powering down. + */ +static int +il3945_set_ucode_ptrs(struct il_priv *il) +{ + dma_addr_t pinst; + dma_addr_t pdata; + + /* bits 31:0 for 3945 */ + pinst = il->ucode_code.p_addr; + pdata = il->ucode_data_backup.p_addr; + + /* Tell bootstrap uCode where to find image to load */ + il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); + il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); + il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, il->ucode_data.len); + + /* Inst byte count must be last to set up, bit 31 signals uCode + * that all new ptr/size info is in place */ + il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, + il->ucode_code.len | BSM_DRAM_INST_LOAD); + + D_INFO("Runtime uCode pointers are set.\n"); + + return 0; +} + +/** + * il3945_init_alive_start - Called after N_ALIVE notification received + * + * Called after N_ALIVE notification received from "initialize" uCode. + * + * Tell "initialize" uCode to go ahead and load the runtime uCode. + */ +static void +il3945_init_alive_start(struct il_priv *il) +{ + /* Check alive response for "valid" sign from uCode */ + if (il->card_alive_init.is_valid != UCODE_VALID_OK) { + /* We had an error bringing up the hardware, so take it + * all the way back down so we can try again */ + D_INFO("Initialize Alive failed.\n"); + goto restart; + } + + /* Bootstrap uCode has loaded initialize uCode ... verify inst image. + * This is a paranoid check, because we would not have gotten the + * "initialize" alive if code weren't properly loaded. */ + if (il3945_verify_ucode(il)) { + /* Runtime instruction load was bad; + * take it all the way back down so we can try again */ + D_INFO("Bad \"initialize\" uCode load.\n"); + goto restart; + } + + /* Send pointers to protocol/runtime uCode image ... init code will + * load and launch runtime uCode, which will send us another "Alive" + * notification. */ + D_INFO("Initialization Alive received.\n"); + if (il3945_set_ucode_ptrs(il)) { + /* Runtime instruction load won't happen; + * take it all the way back down so we can try again */ + D_INFO("Couldn't set up uCode pointers.\n"); + goto restart; + } + return; + +restart: + queue_work(il->workqueue, &il->restart); +} + +/** + * il3945_alive_start - called after N_ALIVE notification received + * from protocol/runtime uCode (initialization uCode's + * Alive gets handled by il3945_init_alive_start()). + */ +static void +il3945_alive_start(struct il_priv *il) +{ + int thermal_spin = 0; + u32 rfkill; + + D_INFO("Runtime Alive received.\n"); + + if (il->card_alive.is_valid != UCODE_VALID_OK) { + /* We had an error bringing up the hardware, so take it + * all the way back down so we can try again */ + D_INFO("Alive failed.\n"); + goto restart; + } + + /* Initialize uCode has loaded Runtime uCode ... verify inst image. + * This is a paranoid check, because we would not have gotten the + * "runtime" alive if code weren't properly loaded. */ + if (il3945_verify_ucode(il)) { + /* Runtime instruction load was bad; + * take it all the way back down so we can try again */ + D_INFO("Bad runtime uCode load.\n"); + goto restart; + } + + rfkill = il_rd_prph(il, APMG_RFKILL_REG); + D_INFO("RFKILL status: 0x%x\n", rfkill); + + if (rfkill & 0x1) { + clear_bit(S_RFKILL, &il->status); + /* if RFKILL is not on, then wait for thermal + * sensor in adapter to kick in */ + while (il3945_hw_get_temperature(il) == 0) { + thermal_spin++; + udelay(10); + } + + if (thermal_spin) + D_INFO("Thermal calibration took %dus\n", + thermal_spin * 10); + } else + set_bit(S_RFKILL, &il->status); + + /* After the ALIVE response, we can send commands to 3945 uCode */ + set_bit(S_ALIVE, &il->status); + + /* Enable watchdog to monitor the driver tx queues */ + il_setup_watchdog(il); + + if (il_is_rfkill(il)) + return; + + ieee80211_wake_queues(il->hw); + + il->active_rate = RATES_MASK_3945; + + il_power_update_mode(il, true); + + if (il_is_associated(il)) { + struct il3945_rxon_cmd *active_rxon = + (struct il3945_rxon_cmd *)(&il->active); + + il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + } else { + /* Initialize our rx_config data */ + il_connection_init_rx_config(il); + } + + /* Configure Bluetooth device coexistence support */ + il_send_bt_config(il); + + set_bit(S_READY, &il->status); + + /* Configure the adapter for unassociated operation */ + il3945_commit_rxon(il); + + il3945_reg_txpower_periodic(il); + + D_INFO("ALIVE processing complete.\n"); + wake_up(&il->wait_command_queue); + + return; + +restart: + queue_work(il->workqueue, &il->restart); +} + +static void il3945_cancel_deferred_work(struct il_priv *il); + +static void +__il3945_down(struct il_priv *il) +{ + unsigned long flags; + int exit_pending; + + D_INFO(DRV_NAME " is going down\n"); + + il_scan_cancel_timeout(il, 200); + + exit_pending = test_and_set_bit(S_EXIT_PENDING, &il->status); + + /* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set + * to prevent rearm timer */ + del_timer_sync(&il->watchdog); + + /* Station information will now be cleared in device */ + il_clear_ucode_stations(il); + il_dealloc_bcast_stations(il); + il_clear_driver_stations(il); + + /* Unblock any waiting calls */ + wake_up_all(&il->wait_command_queue); + + /* Wipe out the EXIT_PENDING status bit if we are not actually + * exiting the module */ + if (!exit_pending) + clear_bit(S_EXIT_PENDING, &il->status); + + /* stop and reset the on-board processor */ + _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + + /* tell the device to stop sending interrupts */ + spin_lock_irqsave(&il->lock, flags); + il_disable_interrupts(il); + spin_unlock_irqrestore(&il->lock, flags); + il3945_synchronize_irq(il); + + if (il->mac80211_registered) + ieee80211_stop_queues(il->hw); + + /* If we have not previously called il3945_init() then + * clear all bits but the RF Kill bits and return */ + if (!il_is_init(il)) { + il->status = + test_bit(S_RFKILL, &il->status) << S_RFKILL | + test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | + test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; + goto exit; + } + + /* ...otherwise clear out all the status bits but the RF Kill + * bit and continue taking the NIC down. */ + il->status &= + test_bit(S_RFKILL, &il->status) << S_RFKILL | + test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | + test_bit(S_FW_ERROR, &il->status) << S_FW_ERROR | + test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; + + /* + * We disabled and synchronized interrupt, and priv->mutex is taken, so + * here is the only thread which will program device registers, but + * still have lockdep assertions, so we are taking reg_lock. + */ + spin_lock_irq(&il->reg_lock); + /* FIXME: il_grab_nic_access if rfkill is off ? */ + + il3945_hw_txq_ctx_stop(il); + il3945_hw_rxq_stop(il); + /* Power-down device's busmaster DMA clocks */ + _il_wr_prph(il, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); + udelay(5); + /* Stop the device, and put it in low power state */ + _il_apm_stop(il); + + spin_unlock_irq(&il->reg_lock); + + il3945_hw_txq_ctx_free(il); +exit: + memset(&il->card_alive, 0, sizeof(struct il_alive_resp)); + + if (il->beacon_skb) + dev_kfree_skb(il->beacon_skb); + il->beacon_skb = NULL; + + /* clear out any free frames */ + il3945_clear_free_frames(il); +} + +static void +il3945_down(struct il_priv *il) +{ + mutex_lock(&il->mutex); + __il3945_down(il); + mutex_unlock(&il->mutex); + + il3945_cancel_deferred_work(il); +} + +#define MAX_HW_RESTARTS 5 + +static int +il3945_alloc_bcast_station(struct il_priv *il) +{ + unsigned long flags; + u8 sta_id; + + spin_lock_irqsave(&il->sta_lock, flags); + sta_id = il_prep_station(il, il_bcast_addr, false, NULL); + if (sta_id == IL_INVALID_STATION) { + IL_ERR("Unable to prepare broadcast station\n"); + spin_unlock_irqrestore(&il->sta_lock, flags); + + return -EINVAL; + } + + il->stations[sta_id].used |= IL_STA_DRIVER_ACTIVE; + il->stations[sta_id].used |= IL_STA_BCAST; + spin_unlock_irqrestore(&il->sta_lock, flags); + + return 0; +} + +static int +__il3945_up(struct il_priv *il) +{ + int rc, i; + + rc = il3945_alloc_bcast_station(il); + if (rc) + return rc; + + if (test_bit(S_EXIT_PENDING, &il->status)) { + IL_WARN("Exit pending; will not bring the NIC up\n"); + return -EIO; + } + + if (!il->ucode_data_backup.v_addr || !il->ucode_data.v_addr) { + IL_ERR("ucode not available for device bring up\n"); + return -EIO; + } + + /* If platform's RF_KILL switch is NOT set to KILL */ + if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) + clear_bit(S_RFKILL, &il->status); + else { + set_bit(S_RFKILL, &il->status); + return -ERFKILL; + } + + _il_wr(il, CSR_INT, 0xFFFFFFFF); + + rc = il3945_hw_nic_init(il); + if (rc) { + IL_ERR("Unable to int nic\n"); + return rc; + } + + /* make sure rfkill handshake bits are cleared */ + _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + /* clear (again), then enable host interrupts */ + _il_wr(il, CSR_INT, 0xFFFFFFFF); + il_enable_interrupts(il); + + /* really make sure rfkill handshake bits are cleared */ + _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + + /* Copy original ucode data image from disk into backup cache. + * This will be used to initialize the on-board processor's + * data SRAM for a clean start when the runtime program first loads. */ + memcpy(il->ucode_data_backup.v_addr, il->ucode_data.v_addr, + il->ucode_data.len); + + /* We return success when we resume from suspend and rf_kill is on. */ + if (test_bit(S_RFKILL, &il->status)) + return 0; + + for (i = 0; i < MAX_HW_RESTARTS; i++) { + + /* load bootstrap state machine, + * load bootstrap program into processor's memory, + * prepare to load the "initialize" uCode */ + rc = il->ops->load_ucode(il); + + if (rc) { + IL_ERR("Unable to set up bootstrap uCode: %d\n", rc); + continue; + } + + /* start card; "initialize" will load runtime ucode */ + il3945_nic_start(il); + + D_INFO(DRV_NAME " is coming up\n"); + + return 0; + } + + set_bit(S_EXIT_PENDING, &il->status); + __il3945_down(il); + clear_bit(S_EXIT_PENDING, &il->status); + + /* tried to restart and config the device for as long as our + * patience could withstand */ + IL_ERR("Unable to initialize device after %d attempts.\n", i); + return -EIO; +} + +/***************************************************************************** + * + * Workqueue callbacks + * + *****************************************************************************/ + +static void +il3945_bg_init_alive_start(struct work_struct *data) +{ + struct il_priv *il = + container_of(data, struct il_priv, init_alive_start.work); + + mutex_lock(&il->mutex); + if (test_bit(S_EXIT_PENDING, &il->status)) + goto out; + + il3945_init_alive_start(il); +out: + mutex_unlock(&il->mutex); +} + +static void +il3945_bg_alive_start(struct work_struct *data) +{ + struct il_priv *il = + container_of(data, struct il_priv, alive_start.work); + + mutex_lock(&il->mutex); + if (test_bit(S_EXIT_PENDING, &il->status) || il->txq == NULL) + goto out; + + il3945_alive_start(il); +out: + mutex_unlock(&il->mutex); +} + +/* + * 3945 cannot interrupt driver when hardware rf kill switch toggles; + * driver must poll CSR_GP_CNTRL_REG register for change. This register + * *is* readable even when device has been SW_RESET into low power mode + * (e.g. during RF KILL). + */ +static void +il3945_rfkill_poll(struct work_struct *data) +{ + struct il_priv *il = + container_of(data, struct il_priv, _3945.rfkill_poll.work); + bool old_rfkill = test_bit(S_RFKILL, &il->status); + bool new_rfkill = + !(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); + + if (new_rfkill != old_rfkill) { + if (new_rfkill) + set_bit(S_RFKILL, &il->status); + else + clear_bit(S_RFKILL, &il->status); + + wiphy_rfkill_set_hw_state(il->hw->wiphy, new_rfkill); + + D_RF_KILL("RF_KILL bit toggled to %s.\n", + new_rfkill ? "disable radio" : "enable radio"); + } + + /* Keep this running, even if radio now enabled. This will be + * cancelled in mac_start() if system decides to start again */ + queue_delayed_work(il->workqueue, &il->_3945.rfkill_poll, + round_jiffies_relative(2 * HZ)); + +} + +int +il3945_request_scan(struct il_priv *il, struct ieee80211_vif *vif) +{ + struct il_host_cmd cmd = { + .id = C_SCAN, + .len = sizeof(struct il3945_scan_cmd), + .flags = CMD_SIZE_HUGE, + }; + struct il3945_scan_cmd *scan; + u8 n_probes = 0; + enum ieee80211_band band; + bool is_active = false; + int ret; + u16 len; + + lockdep_assert_held(&il->mutex); + + if (!il->scan_cmd) { + il->scan_cmd = + kmalloc(sizeof(struct il3945_scan_cmd) + IL_MAX_SCAN_SIZE, + GFP_KERNEL); + if (!il->scan_cmd) { + D_SCAN("Fail to allocate scan memory\n"); + return -ENOMEM; + } + } + scan = il->scan_cmd; + memset(scan, 0, sizeof(struct il3945_scan_cmd) + IL_MAX_SCAN_SIZE); + + scan->quiet_plcp_th = IL_PLCP_QUIET_THRESH; + scan->quiet_time = IL_ACTIVE_QUIET_TIME; + + if (il_is_associated(il)) { + u16 interval; + u32 extra; + u32 suspend_time = 100; + u32 scan_suspend_time = 100; + + D_INFO("Scanning while associated...\n"); + + interval = vif->bss_conf.beacon_int; + + scan->suspend_time = 0; + scan->max_out_time = cpu_to_le32(200 * 1024); + if (!interval) + interval = suspend_time; + /* + * suspend time format: + * 0-19: beacon interval in usec (time before exec.) + * 20-23: 0 + * 24-31: number of beacons (suspend between channels) + */ + + extra = (suspend_time / interval) << 24; + scan_suspend_time = + 0xFF0FFFFF & (extra | ((suspend_time % interval) * 1024)); + + scan->suspend_time = cpu_to_le32(scan_suspend_time); + D_SCAN("suspend_time 0x%X beacon interval %d\n", + scan_suspend_time, interval); + } + + if (il->scan_request->n_ssids) { + int i, p = 0; + D_SCAN("Kicking off active scan\n"); + for (i = 0; i < il->scan_request->n_ssids; i++) { + /* always does wildcard anyway */ + if (!il->scan_request->ssids[i].ssid_len) + continue; + scan->direct_scan[p].id = WLAN_EID_SSID; + scan->direct_scan[p].len = + il->scan_request->ssids[i].ssid_len; + memcpy(scan->direct_scan[p].ssid, + il->scan_request->ssids[i].ssid, + il->scan_request->ssids[i].ssid_len); + n_probes++; + p++; + } + is_active = true; + } else + D_SCAN("Kicking off passive scan.\n"); + + /* We don't build a direct scan probe request; the uCode will do + * that based on the direct_mask added to each channel entry */ + scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; + scan->tx_cmd.sta_id = il->hw_params.bcast_id; + scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + /* flags + rate selection */ + + switch (il->scan_band) { + case IEEE80211_BAND_2GHZ: + scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; + scan->tx_cmd.rate = RATE_1M_PLCP; + band = IEEE80211_BAND_2GHZ; + break; + case IEEE80211_BAND_5GHZ: + scan->tx_cmd.rate = RATE_6M_PLCP; + band = IEEE80211_BAND_5GHZ; + break; + default: + IL_WARN("Invalid scan band\n"); + return -EIO; + } + + /* + * If active scaning is requested but a certain channel is marked + * passive, we can do active scanning if we detect transmissions. For + * passive only scanning disable switching to active on any channel. + */ + scan->good_CRC_th = + is_active ? IL_GOOD_CRC_TH_DEFAULT : IL_GOOD_CRC_TH_NEVER; + + len = + il_fill_probe_req(il, (struct ieee80211_mgmt *)scan->data, + vif->addr, il->scan_request->ie, + il->scan_request->ie_len, + IL_MAX_SCAN_SIZE - sizeof(*scan)); + scan->tx_cmd.len = cpu_to_le16(len); + + /* select Rx antennas */ + scan->flags |= il3945_get_antenna_flags(il); + + scan->channel_count = + il3945_get_channels_for_scan(il, band, is_active, n_probes, + (void *)&scan->data[len], vif); + if (scan->channel_count == 0) { + D_SCAN("channel count %d\n", scan->channel_count); + return -EIO; + } + + cmd.len += + le16_to_cpu(scan->tx_cmd.len) + + scan->channel_count * sizeof(struct il3945_scan_channel); + cmd.data = scan; + scan->len = cpu_to_le16(cmd.len); + + set_bit(S_SCAN_HW, &il->status); + ret = il_send_cmd_sync(il, &cmd); + if (ret) + clear_bit(S_SCAN_HW, &il->status); + return ret; +} + +void +il3945_post_scan(struct il_priv *il) +{ + /* + * Since setting the RXON may have been deferred while + * performing the scan, fire one off if needed + */ + if (memcmp(&il->staging, &il->active, sizeof(il->staging))) + il3945_commit_rxon(il); +} + +static void +il3945_bg_restart(struct work_struct *data) +{ + struct il_priv *il = container_of(data, struct il_priv, restart); + + if (test_bit(S_EXIT_PENDING, &il->status)) + return; + + if (test_and_clear_bit(S_FW_ERROR, &il->status)) { + mutex_lock(&il->mutex); + il->is_open = 0; + mutex_unlock(&il->mutex); + il3945_down(il); + ieee80211_restart_hw(il->hw); + } else { + il3945_down(il); + + mutex_lock(&il->mutex); + if (test_bit(S_EXIT_PENDING, &il->status)) { + mutex_unlock(&il->mutex); + return; + } + + __il3945_up(il); + mutex_unlock(&il->mutex); + } +} + +static void +il3945_bg_rx_replenish(struct work_struct *data) +{ + struct il_priv *il = container_of(data, struct il_priv, rx_replenish); + + mutex_lock(&il->mutex); + if (test_bit(S_EXIT_PENDING, &il->status)) + goto out; + + il3945_rx_replenish(il); +out: + mutex_unlock(&il->mutex); +} + +void +il3945_post_associate(struct il_priv *il) +{ + int rc = 0; + struct ieee80211_conf *conf = NULL; + + if (!il->vif || !il->is_open) + return; + + D_ASSOC("Associated as %d to: %pM\n", il->vif->bss_conf.aid, + il->active.bssid_addr); + + if (test_bit(S_EXIT_PENDING, &il->status)) + return; + + il_scan_cancel_timeout(il, 200); + + conf = &il->hw->conf; + + il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + il3945_commit_rxon(il); + + rc = il_send_rxon_timing(il); + if (rc) + IL_WARN("C_RXON_TIMING failed - " "Attempting to continue.\n"); + + il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + + il->staging.assoc_id = cpu_to_le16(il->vif->bss_conf.aid); + + D_ASSOC("assoc id %d beacon interval %d\n", il->vif->bss_conf.aid, + il->vif->bss_conf.beacon_int); + + if (il->vif->bss_conf.use_short_preamble) + il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + else + il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + + if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { + if (il->vif->bss_conf.use_short_slot) + il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + } + + il3945_commit_rxon(il); + + switch (il->vif->type) { + case NL80211_IFTYPE_STATION: + il3945_rate_scale_init(il->hw, IL_AP_ID); + break; + case NL80211_IFTYPE_ADHOC: + il3945_send_beacon_cmd(il); + break; + default: + IL_ERR("%s Should not be called in %d mode\n", __func__, + il->vif->type); + break; + } +} + +/***************************************************************************** + * + * mac80211 entry point functions + * + *****************************************************************************/ + +#define UCODE_READY_TIMEOUT (2 * HZ) + +static int +il3945_mac_start(struct ieee80211_hw *hw) +{ + struct il_priv *il = hw->priv; + int ret; + + /* we should be verifying the device is ready to be opened */ + mutex_lock(&il->mutex); + D_MAC80211("enter\n"); + + /* fetch ucode file from disk, alloc and copy to bus-master buffers ... + * ucode filename and max sizes are card-specific. */ + + if (!il->ucode_code.len) { + ret = il3945_read_ucode(il); + if (ret) { + IL_ERR("Could not read microcode: %d\n", ret); + mutex_unlock(&il->mutex); + goto out_release_irq; + } + } + + ret = __il3945_up(il); + + mutex_unlock(&il->mutex); + + if (ret) + goto out_release_irq; + + D_INFO("Start UP work.\n"); + + /* Wait for START_ALIVE from ucode. Otherwise callbacks from + * mac80211 will not be run successfully. */ + ret = wait_event_timeout(il->wait_command_queue, + test_bit(S_READY, &il->status), + UCODE_READY_TIMEOUT); + if (!ret) { + if (!test_bit(S_READY, &il->status)) { + IL_ERR("Wait for START_ALIVE timeout after %dms.\n", + jiffies_to_msecs(UCODE_READY_TIMEOUT)); + ret = -ETIMEDOUT; + goto out_release_irq; + } + } + + /* ucode is running and will send rfkill notifications, + * no need to poll the killswitch state anymore */ + cancel_delayed_work(&il->_3945.rfkill_poll); + + il->is_open = 1; + D_MAC80211("leave\n"); + return 0; + +out_release_irq: + il->is_open = 0; + D_MAC80211("leave - failed\n"); + return ret; +} + +static void +il3945_mac_stop(struct ieee80211_hw *hw) +{ + struct il_priv *il = hw->priv; + + D_MAC80211("enter\n"); + + if (!il->is_open) { + D_MAC80211("leave - skip\n"); + return; + } + + il->is_open = 0; + + il3945_down(il); + + flush_workqueue(il->workqueue); + + /* start polling the killswitch state again */ + queue_delayed_work(il->workqueue, &il->_3945.rfkill_poll, + round_jiffies_relative(2 * HZ)); + + D_MAC80211("leave\n"); +} + +static void +il3945_mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct il_priv *il = hw->priv; + + D_MAC80211("enter\n"); + + D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, + ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); + + if (il3945_tx_skb(il, control->sta, skb)) + dev_kfree_skb_any(skb); + + D_MAC80211("leave\n"); +} + +void +il3945_config_ap(struct il_priv *il) +{ + struct ieee80211_vif *vif = il->vif; + int rc = 0; + + if (test_bit(S_EXIT_PENDING, &il->status)) + return; + + /* The following should be done only at AP bring up */ + if (!(il_is_associated(il))) { + + /* RXON - unassoc (to set timing command) */ + il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + il3945_commit_rxon(il); + + /* RXON Timing */ + rc = il_send_rxon_timing(il); + if (rc) + IL_WARN("C_RXON_TIMING failed - " + "Attempting to continue.\n"); + + il->staging.assoc_id = 0; + + if (vif->bss_conf.use_short_preamble) + il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + else + il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + + if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { + if (vif->bss_conf.use_short_slot) + il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + } + /* restore RXON assoc */ + il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + il3945_commit_rxon(il); + } + il3945_send_beacon_cmd(il); +} + +static int +il3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct il_priv *il = hw->priv; + int ret = 0; + u8 sta_id = IL_INVALID_STATION; + u8 static_key; + + D_MAC80211("enter\n"); + + if (il3945_mod_params.sw_crypto) { + D_MAC80211("leave - hwcrypto disabled\n"); + return -EOPNOTSUPP; + } + + /* + * To support IBSS RSN, don't program group keys in IBSS, the + * hardware will then not attempt to decrypt the frames. + */ + if (vif->type == NL80211_IFTYPE_ADHOC && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + D_MAC80211("leave - IBSS RSN\n"); + return -EOPNOTSUPP; + } + + static_key = !il_is_associated(il); + + if (!static_key) { + sta_id = il_sta_id_or_broadcast(il, sta); + if (sta_id == IL_INVALID_STATION) { + D_MAC80211("leave - station not found\n"); + return -EINVAL; + } + } + + mutex_lock(&il->mutex); + il_scan_cancel_timeout(il, 100); + + switch (cmd) { + case SET_KEY: + if (static_key) + ret = il3945_set_static_key(il, key); + else + ret = il3945_set_dynamic_key(il, key, sta_id); + D_MAC80211("enable hwcrypto key\n"); + break; + case DISABLE_KEY: + if (static_key) + ret = il3945_remove_static_key(il); + else + ret = il3945_clear_sta_key_info(il, sta_id); + D_MAC80211("disable hwcrypto key\n"); + break; + default: + ret = -EINVAL; + } + + D_MAC80211("leave ret %d\n", ret); + mutex_unlock(&il->mutex); + + return ret; +} + +static int +il3945_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct il_priv *il = hw->priv; + struct il3945_sta_priv *sta_priv = (void *)sta->drv_priv; + int ret; + bool is_ap = vif->type == NL80211_IFTYPE_STATION; + u8 sta_id; + + mutex_lock(&il->mutex); + D_INFO("station %pM\n", sta->addr); + sta_priv->common.sta_id = IL_INVALID_STATION; + + ret = il_add_station_common(il, sta->addr, is_ap, sta, &sta_id); + if (ret) { + IL_ERR("Unable to add station %pM (%d)\n", sta->addr, ret); + /* Should we return success if return code is EEXIST ? */ + mutex_unlock(&il->mutex); + return ret; + } + + sta_priv->common.sta_id = sta_id; + + /* Initialize rate scaling */ + D_INFO("Initializing rate scaling for station %pM\n", sta->addr); + il3945_rs_rate_init(il, sta, sta_id); + mutex_unlock(&il->mutex); + + return 0; +} + +static void +il3945_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct il_priv *il = hw->priv; + __le32 filter_or = 0, filter_nand = 0; + +#define CHK(test, flag) do { \ + if (*total_flags & (test)) \ + filter_or |= (flag); \ + else \ + filter_nand |= (flag); \ + } while (0) + + D_MAC80211("Enter: changed: 0x%x, total: 0x%x\n", changed_flags, + *total_flags); + + CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK); + CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK); + CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); + +#undef CHK + + mutex_lock(&il->mutex); + + il->staging.filter_flags &= ~filter_nand; + il->staging.filter_flags |= filter_or; + + /* + * Not committing directly because hardware can perform a scan, + * but even if hw is ready, committing here breaks for some reason, + * we'll eventually commit the filter flags change anyway. + */ + + mutex_unlock(&il->mutex); + + /* + * Receiving all multicast frames is always enabled by the + * default flags setup in il_connection_init_rx_config() + * since we currently do not support programming multicast + * filters into the device. + */ + *total_flags &= + FIF_OTHER_BSS | FIF_ALLMULTI | + FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; +} + +/***************************************************************************** + * + * sysfs attributes + * + *****************************************************************************/ + +#ifdef CONFIG_IWLEGACY_DEBUG + +/* + * The following adds a new attribute to the sysfs representation + * of this device driver (i.e. a new file in /sys/bus/pci/drivers/iwl/) + * used for controlling the debug level. + * + * See the level definitions in iwl for details. + * + * The debug_level being managed using sysfs below is a per device debug + * level that is used instead of the global debug level if it (the per + * device debug level) is set. + */ +static ssize_t +il3945_show_debug_level(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + return sprintf(buf, "0x%08X\n", il_get_debug_level(il)); +} + +static ssize_t +il3945_store_debug_level(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct il_priv *il = dev_get_drvdata(d); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 0, &val); + if (ret) + IL_INFO("%s is not in hex or decimal form.\n", buf); + else + il->debug_level = val; + + return strnlen(buf, count); +} + +static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, il3945_show_debug_level, + il3945_store_debug_level); + +#endif /* CONFIG_IWLEGACY_DEBUG */ + +static ssize_t +il3945_show_temperature(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + + if (!il_is_alive(il)) + return -EAGAIN; + + return sprintf(buf, "%d\n", il3945_hw_get_temperature(il)); +} + +static DEVICE_ATTR(temperature, S_IRUGO, il3945_show_temperature, NULL); + +static ssize_t +il3945_show_tx_power(struct device *d, struct device_attribute *attr, char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + return sprintf(buf, "%d\n", il->tx_power_user_lmt); +} + +static ssize_t +il3945_store_tx_power(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct il_priv *il = dev_get_drvdata(d); + char *p = (char *)buf; + u32 val; + + val = simple_strtoul(p, &p, 10); + if (p == buf) + IL_INFO(": %s is not in decimal form.\n", buf); + else + il3945_hw_reg_set_txpower(il, val); + + return count; +} + +static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, il3945_show_tx_power, + il3945_store_tx_power); + +static ssize_t +il3945_show_flags(struct device *d, struct device_attribute *attr, char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + + return sprintf(buf, "0x%04X\n", il->active.flags); +} + +static ssize_t +il3945_store_flags(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct il_priv *il = dev_get_drvdata(d); + u32 flags = simple_strtoul(buf, NULL, 0); + + mutex_lock(&il->mutex); + if (le32_to_cpu(il->staging.flags) != flags) { + /* Cancel any currently running scans... */ + if (il_scan_cancel_timeout(il, 100)) + IL_WARN("Could not cancel scan.\n"); + else { + D_INFO("Committing rxon.flags = 0x%04X\n", flags); + il->staging.flags = cpu_to_le32(flags); + il3945_commit_rxon(il); + } + } + mutex_unlock(&il->mutex); + + return count; +} + +static DEVICE_ATTR(flags, S_IWUSR | S_IRUGO, il3945_show_flags, + il3945_store_flags); + +static ssize_t +il3945_show_filter_flags(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + + return sprintf(buf, "0x%04X\n", le32_to_cpu(il->active.filter_flags)); +} + +static ssize_t +il3945_store_filter_flags(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct il_priv *il = dev_get_drvdata(d); + u32 filter_flags = simple_strtoul(buf, NULL, 0); + + mutex_lock(&il->mutex); + if (le32_to_cpu(il->staging.filter_flags) != filter_flags) { + /* Cancel any currently running scans... */ + if (il_scan_cancel_timeout(il, 100)) + IL_WARN("Could not cancel scan.\n"); + else { + D_INFO("Committing rxon.filter_flags = " "0x%04X\n", + filter_flags); + il->staging.filter_flags = cpu_to_le32(filter_flags); + il3945_commit_rxon(il); + } + } + mutex_unlock(&il->mutex); + + return count; +} + +static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, il3945_show_filter_flags, + il3945_store_filter_flags); + +static ssize_t +il3945_show_measurement(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + struct il_spectrum_notification measure_report; + u32 size = sizeof(measure_report), len = 0, ofs = 0; + u8 *data = (u8 *) &measure_report; + unsigned long flags; + + spin_lock_irqsave(&il->lock, flags); + if (!(il->measurement_status & MEASUREMENT_READY)) { + spin_unlock_irqrestore(&il->lock, flags); + return 0; + } + memcpy(&measure_report, &il->measure_report, size); + il->measurement_status = 0; + spin_unlock_irqrestore(&il->lock, flags); + + while (size && PAGE_SIZE - len) { + hex_dump_to_buffer(data + ofs, size, 16, 1, buf + len, + PAGE_SIZE - len, true); + len = strlen(buf); + if (PAGE_SIZE - len) + buf[len++] = '\n'; + + ofs += 16; + size -= min(size, 16U); + } + + return len; +} + +static ssize_t +il3945_store_measurement(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct il_priv *il = dev_get_drvdata(d); + struct ieee80211_measurement_params params = { + .channel = le16_to_cpu(il->active.channel), + .start_time = cpu_to_le64(il->_3945.last_tsf), + .duration = cpu_to_le16(1), + }; + u8 type = IL_MEASURE_BASIC; + u8 buffer[32]; + u8 channel; + + if (count) { + char *p = buffer; + strlcpy(buffer, buf, sizeof(buffer)); + channel = simple_strtoul(p, NULL, 0); + if (channel) + params.channel = channel; + + p = buffer; + while (*p && *p != ' ') + p++; + if (*p) + type = simple_strtoul(p + 1, NULL, 0); + } + + D_INFO("Invoking measurement of type %d on " "channel %d (for '%s')\n", + type, params.channel, buf); + il3945_get_measurement(il, ¶ms, type); + + return count; +} + +static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR, il3945_show_measurement, + il3945_store_measurement); + +static ssize_t +il3945_store_retry_rate(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct il_priv *il = dev_get_drvdata(d); + + il->retry_rate = simple_strtoul(buf, NULL, 0); + if (il->retry_rate <= 0) + il->retry_rate = 1; + + return count; +} + +static ssize_t +il3945_show_retry_rate(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + return sprintf(buf, "%d", il->retry_rate); +} + +static DEVICE_ATTR(retry_rate, S_IWUSR | S_IRUSR, il3945_show_retry_rate, + il3945_store_retry_rate); + +static ssize_t +il3945_show_channels(struct device *d, struct device_attribute *attr, char *buf) +{ + /* all this shit doesn't belong into sysfs anyway */ + return 0; +} + +static DEVICE_ATTR(channels, S_IRUSR, il3945_show_channels, NULL); + +static ssize_t +il3945_show_antenna(struct device *d, struct device_attribute *attr, char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + + if (!il_is_alive(il)) + return -EAGAIN; + + return sprintf(buf, "%d\n", il3945_mod_params.antenna); +} + +static ssize_t +il3945_store_antenna(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct il_priv *il __maybe_unused = dev_get_drvdata(d); + int ant; + + if (count == 0) + return 0; + + if (sscanf(buf, "%1i", &ant) != 1) { + D_INFO("not in hex or decimal form.\n"); + return count; + } + + if (ant >= 0 && ant <= 2) { + D_INFO("Setting antenna select to %d.\n", ant); + il3945_mod_params.antenna = (enum il3945_antenna)ant; + } else + D_INFO("Bad antenna select value %d.\n", ant); + + return count; +} + +static DEVICE_ATTR(antenna, S_IWUSR | S_IRUGO, il3945_show_antenna, + il3945_store_antenna); + +static ssize_t +il3945_show_status(struct device *d, struct device_attribute *attr, char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + if (!il_is_alive(il)) + return -EAGAIN; + return sprintf(buf, "0x%08x\n", (int)il->status); +} + +static DEVICE_ATTR(status, S_IRUGO, il3945_show_status, NULL); + +static ssize_t +il3945_dump_error_log(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct il_priv *il = dev_get_drvdata(d); + char *p = (char *)buf; + + if (p[0] == '1') + il3945_dump_nic_error_log(il); + + return strnlen(buf, count); +} + +static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, il3945_dump_error_log); + +/***************************************************************************** + * + * driver setup and tear down + * + *****************************************************************************/ + +static void +il3945_setup_deferred_work(struct il_priv *il) +{ + il->workqueue = create_singlethread_workqueue(DRV_NAME); + + init_waitqueue_head(&il->wait_command_queue); + + INIT_WORK(&il->restart, il3945_bg_restart); + INIT_WORK(&il->rx_replenish, il3945_bg_rx_replenish); + INIT_DELAYED_WORK(&il->init_alive_start, il3945_bg_init_alive_start); + INIT_DELAYED_WORK(&il->alive_start, il3945_bg_alive_start); + INIT_DELAYED_WORK(&il->_3945.rfkill_poll, il3945_rfkill_poll); + + il_setup_scan_deferred_work(il); + + il3945_hw_setup_deferred_work(il); + + setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il); + + tasklet_init(&il->irq_tasklet, + (void (*)(unsigned long))il3945_irq_tasklet, + (unsigned long)il); +} + +static void +il3945_cancel_deferred_work(struct il_priv *il) +{ + il3945_hw_cancel_deferred_work(il); + + cancel_delayed_work_sync(&il->init_alive_start); + cancel_delayed_work(&il->alive_start); + + il_cancel_scan_deferred_work(il); +} + +static struct attribute *il3945_sysfs_entries[] = { + &dev_attr_antenna.attr, + &dev_attr_channels.attr, + &dev_attr_dump_errors.attr, + &dev_attr_flags.attr, + &dev_attr_filter_flags.attr, + &dev_attr_measurement.attr, + &dev_attr_retry_rate.attr, + &dev_attr_status.attr, + &dev_attr_temperature.attr, + &dev_attr_tx_power.attr, +#ifdef CONFIG_IWLEGACY_DEBUG + &dev_attr_debug_level.attr, +#endif + NULL +}; + +static struct attribute_group il3945_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = il3945_sysfs_entries, +}; + +static struct ieee80211_ops il3945_mac_ops __read_mostly = { + .tx = il3945_mac_tx, + .start = il3945_mac_start, + .stop = il3945_mac_stop, + .add_interface = il_mac_add_interface, + .remove_interface = il_mac_remove_interface, + .change_interface = il_mac_change_interface, + .config = il_mac_config, + .configure_filter = il3945_configure_filter, + .set_key = il3945_mac_set_key, + .conf_tx = il_mac_conf_tx, + .reset_tsf = il_mac_reset_tsf, + .bss_info_changed = il_mac_bss_info_changed, + .hw_scan = il_mac_hw_scan, + .sta_add = il3945_mac_sta_add, + .sta_remove = il_mac_sta_remove, + .tx_last_beacon = il_mac_tx_last_beacon, + .flush = il_mac_flush, +}; + +static int +il3945_init_drv(struct il_priv *il) +{ + int ret; + struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; + + il->retry_rate = 1; + il->beacon_skb = NULL; + + spin_lock_init(&il->sta_lock); + spin_lock_init(&il->hcmd_lock); + + INIT_LIST_HEAD(&il->free_frames); + + mutex_init(&il->mutex); + + il->ieee_channels = NULL; + il->ieee_rates = NULL; + il->band = IEEE80211_BAND_2GHZ; + + il->iw_mode = NL80211_IFTYPE_STATION; + il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF; + + /* initialize force reset */ + il->force_reset.reset_duration = IL_DELAY_NEXT_FORCE_FW_RELOAD; + + if (eeprom->version < EEPROM_3945_EEPROM_VERSION) { + IL_WARN("Unsupported EEPROM version: 0x%04X\n", + eeprom->version); + ret = -EINVAL; + goto err; + } + ret = il_init_channel_map(il); + if (ret) { + IL_ERR("initializing regulatory failed: %d\n", ret); + goto err; + } + + /* Set up txpower settings in driver for all channels */ + if (il3945_txpower_set_from_eeprom(il)) { + ret = -EIO; + goto err_free_channel_map; + } + + ret = il_init_geos(il); + if (ret) { + IL_ERR("initializing geos failed: %d\n", ret); + goto err_free_channel_map; + } + il3945_init_hw_rates(il, il->ieee_rates); + + return 0; + +err_free_channel_map: + il_free_channel_map(il); +err: + return ret; +} + +#define IL3945_MAX_PROBE_REQUEST 200 + +static int +il3945_setup_mac(struct il_priv *il) +{ + int ret; + struct ieee80211_hw *hw = il->hw; + + hw->rate_control_algorithm = "iwl-3945-rs"; + hw->sta_data_size = sizeof(struct il3945_sta_priv); + hw->vif_data_size = sizeof(struct il_vif_priv); + + /* Tell mac80211 our characteristics */ + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, SPECTRUM_MGMT); + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); + + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; + + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX_3945; + /* we create the 802.11 header and a zero-length SSID element */ + hw->wiphy->max_scan_ie_len = IL3945_MAX_PROBE_REQUEST - 24 - 2; + + /* Default value; 4 EDCA QOS priorities */ + hw->queues = 4; + + if (il->bands[IEEE80211_BAND_2GHZ].n_channels) + il->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &il->bands[IEEE80211_BAND_2GHZ]; + + if (il->bands[IEEE80211_BAND_5GHZ].n_channels) + il->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &il->bands[IEEE80211_BAND_5GHZ]; + + il_leds_init(il); + + ret = ieee80211_register_hw(il->hw); + if (ret) { + IL_ERR("Failed to register hw (error %d)\n", ret); + return ret; + } + il->mac80211_registered = 1; + + return 0; +} + +static int +il3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = 0; + struct il_priv *il; + struct ieee80211_hw *hw; + struct il_cfg *cfg = (struct il_cfg *)(ent->driver_data); + struct il3945_eeprom *eeprom; + unsigned long flags; + + /*********************** + * 1. Allocating HW data + * ********************/ + + hw = ieee80211_alloc_hw(sizeof(struct il_priv), &il3945_mac_ops); + if (!hw) { + err = -ENOMEM; + goto out; + } + il = hw->priv; + il->hw = hw; + SET_IEEE80211_DEV(hw, &pdev->dev); + + il->cmd_queue = IL39_CMD_QUEUE_NUM; + + /* + * Disabling hardware scan means that mac80211 will perform scans + * "the hard way", rather than using device's scan. + */ + if (il3945_mod_params.disable_hw_scan) { + D_INFO("Disabling hw_scan\n"); + il3945_mac_ops.hw_scan = NULL; + } + + D_INFO("*** LOAD DRIVER ***\n"); + il->cfg = cfg; + il->ops = &il3945_ops; +#ifdef CONFIG_IWLEGACY_DEBUGFS + il->debugfs_ops = &il3945_debugfs_ops; +#endif + il->pci_dev = pdev; + il->inta_mask = CSR_INI_SET_MASK; + + /*************************** + * 2. Initializing PCI bus + * *************************/ + pci_disable_link_state(pdev, + PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM); + + if (pci_enable_device(pdev)) { + err = -ENODEV; + goto out_ieee80211_free_hw; + } + + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (!err) + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) { + IL_WARN("No suitable DMA available.\n"); + goto out_pci_disable_device; + } + + pci_set_drvdata(pdev, il); + err = pci_request_regions(pdev, DRV_NAME); + if (err) + goto out_pci_disable_device; + + /*********************** + * 3. Read REV Register + * ********************/ + il->hw_base = pci_ioremap_bar(pdev, 0); + if (!il->hw_base) { + err = -ENODEV; + goto out_pci_release_regions; + } + + D_INFO("pci_resource_len = 0x%08llx\n", + (unsigned long long)pci_resource_len(pdev, 0)); + D_INFO("pci_resource_base = %p\n", il->hw_base); + + /* We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state */ + pci_write_config_byte(pdev, 0x41, 0x00); + + /* these spin locks will be used in apm_init and EEPROM access + * we should init now + */ + spin_lock_init(&il->reg_lock); + spin_lock_init(&il->lock); + + /* + * stop and reset the on-board processor just in case it is in a + * strange state ... like being left stranded by a primary kernel + * and this is now the kdump kernel trying to start up + */ + _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + + /*********************** + * 4. Read EEPROM + * ********************/ + + /* Read the EEPROM */ + err = il_eeprom_init(il); + if (err) { + IL_ERR("Unable to init EEPROM\n"); + goto out_iounmap; + } + /* MAC Address location in EEPROM same for 3945/4965 */ + eeprom = (struct il3945_eeprom *)il->eeprom; + D_INFO("MAC address: %pM\n", eeprom->mac_address); + SET_IEEE80211_PERM_ADDR(il->hw, eeprom->mac_address); + + /*********************** + * 5. Setup HW Constants + * ********************/ + /* Device-specific setup */ + err = il3945_hw_set_hw_params(il); + if (err) { + IL_ERR("failed to set hw settings\n"); + goto out_eeprom_free; + } + + /*********************** + * 6. Setup il + * ********************/ + + err = il3945_init_drv(il); + if (err) { + IL_ERR("initializing driver failed\n"); + goto out_unset_hw_params; + } + + IL_INFO("Detected Intel Wireless WiFi Link %s\n", il->cfg->name); + + /*********************** + * 7. Setup Services + * ********************/ + + spin_lock_irqsave(&il->lock, flags); + il_disable_interrupts(il); + spin_unlock_irqrestore(&il->lock, flags); + + pci_enable_msi(il->pci_dev); + + err = request_irq(il->pci_dev->irq, il_isr, IRQF_SHARED, DRV_NAME, il); + if (err) { + IL_ERR("Error allocating IRQ %d\n", il->pci_dev->irq); + goto out_disable_msi; + } + + err = sysfs_create_group(&pdev->dev.kobj, &il3945_attribute_group); + if (err) { + IL_ERR("failed to create sysfs device attributes\n"); + goto out_release_irq; + } + + il_set_rxon_channel(il, &il->bands[IEEE80211_BAND_2GHZ].channels[5]); + il3945_setup_deferred_work(il); + il3945_setup_handlers(il); + il_power_initialize(il); + + /********************************* + * 8. Setup and Register mac80211 + * *******************************/ + + il_enable_interrupts(il); + + err = il3945_setup_mac(il); + if (err) + goto out_remove_sysfs; + + err = il_dbgfs_register(il, DRV_NAME); + if (err) + IL_ERR("failed to create debugfs files. Ignoring error: %d\n", + err); + + /* Start monitoring the killswitch */ + queue_delayed_work(il->workqueue, &il->_3945.rfkill_poll, 2 * HZ); + + return 0; + +out_remove_sysfs: + destroy_workqueue(il->workqueue); + il->workqueue = NULL; + sysfs_remove_group(&pdev->dev.kobj, &il3945_attribute_group); +out_release_irq: + free_irq(il->pci_dev->irq, il); +out_disable_msi: + pci_disable_msi(il->pci_dev); + il_free_geos(il); + il_free_channel_map(il); +out_unset_hw_params: + il3945_unset_hw_params(il); +out_eeprom_free: + il_eeprom_free(il); +out_iounmap: + iounmap(il->hw_base); +out_pci_release_regions: + pci_release_regions(pdev); +out_pci_disable_device: + pci_disable_device(pdev); +out_ieee80211_free_hw: + ieee80211_free_hw(il->hw); +out: + return err; +} + +static void +il3945_pci_remove(struct pci_dev *pdev) +{ + struct il_priv *il = pci_get_drvdata(pdev); + unsigned long flags; + + if (!il) + return; + + D_INFO("*** UNLOAD DRIVER ***\n"); + + il_dbgfs_unregister(il); + + set_bit(S_EXIT_PENDING, &il->status); + + il_leds_exit(il); + + if (il->mac80211_registered) { + ieee80211_unregister_hw(il->hw); + il->mac80211_registered = 0; + } else { + il3945_down(il); + } + + /* + * Make sure device is reset to low power before unloading driver. + * This may be redundant with il_down(), but there are paths to + * run il_down() without calling apm_ops.stop(), and there are + * paths to avoid running il_down() at all before leaving driver. + * This (inexpensive) call *makes sure* device is reset. + */ + il_apm_stop(il); + + /* make sure we flush any pending irq or + * tasklet for the driver + */ + spin_lock_irqsave(&il->lock, flags); + il_disable_interrupts(il); + spin_unlock_irqrestore(&il->lock, flags); + + il3945_synchronize_irq(il); + + sysfs_remove_group(&pdev->dev.kobj, &il3945_attribute_group); + + cancel_delayed_work_sync(&il->_3945.rfkill_poll); + + il3945_dealloc_ucode_pci(il); + + if (il->rxq.bd) + il3945_rx_queue_free(il, &il->rxq); + il3945_hw_txq_ctx_free(il); + + il3945_unset_hw_params(il); + + /*netif_stop_queue(dev); */ + flush_workqueue(il->workqueue); + + /* ieee80211_unregister_hw calls il3945_mac_stop, which flushes + * il->workqueue... so we can't take down the workqueue + * until now... */ + destroy_workqueue(il->workqueue); + il->workqueue = NULL; + + free_irq(pdev->irq, il); + pci_disable_msi(pdev); + + iounmap(il->hw_base); + pci_release_regions(pdev); + pci_disable_device(pdev); + + il_free_channel_map(il); + il_free_geos(il); + kfree(il->scan_cmd); + if (il->beacon_skb) + dev_kfree_skb(il->beacon_skb); + + ieee80211_free_hw(il->hw); +} + +/***************************************************************************** + * + * driver and module entry point + * + *****************************************************************************/ + +static struct pci_driver il3945_driver = { + .name = DRV_NAME, + .id_table = il3945_hw_card_ids, + .probe = il3945_pci_probe, + .remove = il3945_pci_remove, + .driver.pm = IL_LEGACY_PM_OPS, +}; + +static int __init +il3945_init(void) +{ + + int ret; + pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); + pr_info(DRV_COPYRIGHT "\n"); + + ret = il3945_rate_control_register(); + if (ret) { + pr_err("Unable to register rate control algorithm: %d\n", ret); + return ret; + } + + ret = pci_register_driver(&il3945_driver); + if (ret) { + pr_err("Unable to initialize PCI module\n"); + goto error_register; + } + + return ret; + +error_register: + il3945_rate_control_unregister(); + return ret; +} + +static void __exit +il3945_exit(void) +{ + pci_unregister_driver(&il3945_driver); + il3945_rate_control_unregister(); +} + +MODULE_FIRMWARE(IL3945_MODULE_FIRMWARE(IL3945_UCODE_API_MAX)); + +module_param_named(antenna, il3945_mod_params.antenna, int, S_IRUGO); +MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); +module_param_named(swcrypto, il3945_mod_params.sw_crypto, int, S_IRUGO); +MODULE_PARM_DESC(swcrypto, "using software crypto (default 1 [software])"); +module_param_named(disable_hw_scan, il3945_mod_params.disable_hw_scan, int, + S_IRUGO); +MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 1)"); +#ifdef CONFIG_IWLEGACY_DEBUG +module_param_named(debug, il_debug_level, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "debug output mask"); +#endif +module_param_named(fw_restart, il3945_mod_params.restart_fw, int, S_IRUGO); +MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); + +module_exit(il3945_exit); +module_init(il3945_init); diff --git a/drivers/net/wireless/intel/iwlegacy/3945-rs.c b/drivers/net/wireless/intel/iwlegacy/3945-rs.c new file mode 100644 index 000000000000..76b0729ade17 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/3945-rs.c @@ -0,0 +1,979 @@ +/****************************************************************************** + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "commands.h" +#include "3945.h" + +#define RS_NAME "iwl-3945-rs" + +static s32 il3945_expected_tpt_g[RATE_COUNT_3945] = { + 7, 13, 35, 58, 0, 0, 76, 104, 130, 168, 191, 202 +}; + +static s32 il3945_expected_tpt_g_prot[RATE_COUNT_3945] = { + 7, 13, 35, 58, 0, 0, 0, 80, 93, 113, 123, 125 +}; + +static s32 il3945_expected_tpt_a[RATE_COUNT_3945] = { + 0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186 +}; + +static s32 il3945_expected_tpt_b[RATE_COUNT_3945] = { + 7, 13, 35, 58, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +struct il3945_tpt_entry { + s8 min_rssi; + u8 idx; +}; + +static struct il3945_tpt_entry il3945_tpt_table_a[] = { + {-60, RATE_54M_IDX}, + {-64, RATE_48M_IDX}, + {-72, RATE_36M_IDX}, + {-80, RATE_24M_IDX}, + {-84, RATE_18M_IDX}, + {-85, RATE_12M_IDX}, + {-87, RATE_9M_IDX}, + {-89, RATE_6M_IDX} +}; + +static struct il3945_tpt_entry il3945_tpt_table_g[] = { + {-60, RATE_54M_IDX}, + {-64, RATE_48M_IDX}, + {-68, RATE_36M_IDX}, + {-80, RATE_24M_IDX}, + {-84, RATE_18M_IDX}, + {-85, RATE_12M_IDX}, + {-86, RATE_11M_IDX}, + {-88, RATE_5M_IDX}, + {-90, RATE_2M_IDX}, + {-92, RATE_1M_IDX} +}; + +#define RATE_MAX_WINDOW 62 +#define RATE_FLUSH (3*HZ) +#define RATE_WIN_FLUSH (HZ/2) +#define IL39_RATE_HIGH_TH 11520 +#define IL_SUCCESS_UP_TH 8960 +#define IL_SUCCESS_DOWN_TH 10880 +#define RATE_MIN_FAILURE_TH 6 +#define RATE_MIN_SUCCESS_TH 8 +#define RATE_DECREASE_TH 1920 +#define RATE_RETRY_TH 15 + +static u8 +il3945_get_rate_idx_by_rssi(s32 rssi, enum ieee80211_band band) +{ + u32 idx = 0; + u32 table_size = 0; + struct il3945_tpt_entry *tpt_table = NULL; + + if (rssi < IL_MIN_RSSI_VAL || rssi > IL_MAX_RSSI_VAL) + rssi = IL_MIN_RSSI_VAL; + + switch (band) { + case IEEE80211_BAND_2GHZ: + tpt_table = il3945_tpt_table_g; + table_size = ARRAY_SIZE(il3945_tpt_table_g); + break; + case IEEE80211_BAND_5GHZ: + tpt_table = il3945_tpt_table_a; + table_size = ARRAY_SIZE(il3945_tpt_table_a); + break; + default: + BUG(); + break; + } + + while (idx < table_size && rssi < tpt_table[idx].min_rssi) + idx++; + + idx = min(idx, table_size - 1); + + return tpt_table[idx].idx; +} + +static void +il3945_clear_win(struct il3945_rate_scale_data *win) +{ + win->data = 0; + win->success_counter = 0; + win->success_ratio = -1; + win->counter = 0; + win->average_tpt = IL_INVALID_VALUE; + win->stamp = 0; +} + +/** + * il3945_rate_scale_flush_wins - flush out the rate scale wins + * + * Returns the number of wins that have gathered data but were + * not flushed. If there were any that were not flushed, then + * reschedule the rate flushing routine. + */ +static int +il3945_rate_scale_flush_wins(struct il3945_rs_sta *rs_sta) +{ + int unflushed = 0; + int i; + unsigned long flags; + struct il_priv *il __maybe_unused = rs_sta->il; + + /* + * For each rate, if we have collected data on that rate + * and it has been more than RATE_WIN_FLUSH + * since we flushed, clear out the gathered stats + */ + for (i = 0; i < RATE_COUNT_3945; i++) { + if (!rs_sta->win[i].counter) + continue; + + spin_lock_irqsave(&rs_sta->lock, flags); + if (time_after(jiffies, rs_sta->win[i].stamp + RATE_WIN_FLUSH)) { + D_RATE("flushing %d samples of rate " "idx %d\n", + rs_sta->win[i].counter, i); + il3945_clear_win(&rs_sta->win[i]); + } else + unflushed++; + spin_unlock_irqrestore(&rs_sta->lock, flags); + } + + return unflushed; +} + +#define RATE_FLUSH_MAX 5000 /* msec */ +#define RATE_FLUSH_MIN 50 /* msec */ +#define IL_AVERAGE_PACKETS 1500 + +static void +il3945_bg_rate_scale_flush(unsigned long data) +{ + struct il3945_rs_sta *rs_sta = (void *)data; + struct il_priv *il __maybe_unused = rs_sta->il; + int unflushed = 0; + unsigned long flags; + u32 packet_count, duration, pps; + + D_RATE("enter\n"); + + unflushed = il3945_rate_scale_flush_wins(rs_sta); + + spin_lock_irqsave(&rs_sta->lock, flags); + + /* Number of packets Rx'd since last time this timer ran */ + packet_count = (rs_sta->tx_packets - rs_sta->last_tx_packets) + 1; + + rs_sta->last_tx_packets = rs_sta->tx_packets + 1; + + if (unflushed) { + duration = + jiffies_to_msecs(jiffies - rs_sta->last_partial_flush); + + D_RATE("Tx'd %d packets in %dms\n", packet_count, duration); + + /* Determine packets per second */ + if (duration) + pps = (packet_count * 1000) / duration; + else + pps = 0; + + if (pps) { + duration = (IL_AVERAGE_PACKETS * 1000) / pps; + if (duration < RATE_FLUSH_MIN) + duration = RATE_FLUSH_MIN; + else if (duration > RATE_FLUSH_MAX) + duration = RATE_FLUSH_MAX; + } else + duration = RATE_FLUSH_MAX; + + rs_sta->flush_time = msecs_to_jiffies(duration); + + D_RATE("new flush period: %d msec ave %d\n", duration, + packet_count); + + mod_timer(&rs_sta->rate_scale_flush, + jiffies + rs_sta->flush_time); + + rs_sta->last_partial_flush = jiffies; + } else { + rs_sta->flush_time = RATE_FLUSH; + rs_sta->flush_pending = 0; + } + /* If there weren't any unflushed entries, we don't schedule the timer + * to run again */ + + rs_sta->last_flush = jiffies; + + spin_unlock_irqrestore(&rs_sta->lock, flags); + + D_RATE("leave\n"); +} + +/** + * il3945_collect_tx_data - Update the success/failure sliding win + * + * We keep a sliding win of the last 64 packets transmitted + * at this rate. win->data contains the bitmask of successful + * packets. + */ +static void +il3945_collect_tx_data(struct il3945_rs_sta *rs_sta, + struct il3945_rate_scale_data *win, int success, + int retries, int idx) +{ + unsigned long flags; + s32 fail_count; + struct il_priv *il __maybe_unused = rs_sta->il; + + if (!retries) { + D_RATE("leave: retries == 0 -- should be at least 1\n"); + return; + } + + spin_lock_irqsave(&rs_sta->lock, flags); + + /* + * Keep track of only the latest 62 tx frame attempts in this rate's + * history win; anything older isn't really relevant any more. + * If we have filled up the sliding win, drop the oldest attempt; + * if the oldest attempt (highest bit in bitmap) shows "success", + * subtract "1" from the success counter (this is the main reason + * we keep these bitmaps!). + * */ + while (retries > 0) { + if (win->counter >= RATE_MAX_WINDOW) { + + /* remove earliest */ + win->counter = RATE_MAX_WINDOW - 1; + + if (win->data & (1ULL << (RATE_MAX_WINDOW - 1))) { + win->data &= ~(1ULL << (RATE_MAX_WINDOW - 1)); + win->success_counter--; + } + } + + /* Increment frames-attempted counter */ + win->counter++; + + /* Shift bitmap by one frame (throw away oldest history), + * OR in "1", and increment "success" if this + * frame was successful. */ + win->data <<= 1; + if (success > 0) { + win->success_counter++; + win->data |= 0x1; + success--; + } + + retries--; + } + + /* Calculate current success ratio, avoid divide-by-0! */ + if (win->counter > 0) + win->success_ratio = + 128 * (100 * win->success_counter) / win->counter; + else + win->success_ratio = IL_INVALID_VALUE; + + fail_count = win->counter - win->success_counter; + + /* Calculate average throughput, if we have enough history. */ + if (fail_count >= RATE_MIN_FAILURE_TH || + win->success_counter >= RATE_MIN_SUCCESS_TH) + win->average_tpt = + ((win->success_ratio * rs_sta->expected_tpt[idx] + + 64) / 128); + else + win->average_tpt = IL_INVALID_VALUE; + + /* Tag this win as having been updated */ + win->stamp = jiffies; + + spin_unlock_irqrestore(&rs_sta->lock, flags); +} + +/* + * Called after adding a new station to initialize rate scaling + */ +void +il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, u8 sta_id) +{ + struct ieee80211_hw *hw = il->hw; + struct ieee80211_conf *conf = &il->hw->conf; + struct il3945_sta_priv *psta; + struct il3945_rs_sta *rs_sta; + struct ieee80211_supported_band *sband; + int i; + + D_INFO("enter\n"); + if (sta_id == il->hw_params.bcast_id) + goto out; + + psta = (struct il3945_sta_priv *)sta->drv_priv; + rs_sta = &psta->rs_sta; + sband = hw->wiphy->bands[conf->chandef.chan->band]; + + rs_sta->il = il; + + rs_sta->start_rate = RATE_INVALID; + + /* default to just 802.11b */ + rs_sta->expected_tpt = il3945_expected_tpt_b; + + rs_sta->last_partial_flush = jiffies; + rs_sta->last_flush = jiffies; + rs_sta->flush_time = RATE_FLUSH; + rs_sta->last_tx_packets = 0; + + rs_sta->rate_scale_flush.data = (unsigned long)rs_sta; + rs_sta->rate_scale_flush.function = il3945_bg_rate_scale_flush; + + for (i = 0; i < RATE_COUNT_3945; i++) + il3945_clear_win(&rs_sta->win[i]); + + /* TODO: what is a good starting rate for STA? About middle? Maybe not + * the lowest or the highest rate.. Could consider using RSSI from + * previous packets? Need to have IEEE 802.1X auth succeed immediately + * after assoc.. */ + + for (i = sband->n_bitrates - 1; i >= 0; i--) { + if (sta->supp_rates[sband->band] & (1 << i)) { + rs_sta->last_txrate_idx = i; + break; + } + } + + il->_3945.sta_supp_rates = sta->supp_rates[sband->band]; + /* For 5 GHz band it start at IL_FIRST_OFDM_RATE */ + if (sband->band == IEEE80211_BAND_5GHZ) { + rs_sta->last_txrate_idx += IL_FIRST_OFDM_RATE; + il->_3945.sta_supp_rates <<= IL_FIRST_OFDM_RATE; + } + +out: + il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS; + + D_INFO("leave\n"); +} + +static void * +il3945_rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) +{ + return hw->priv; +} + +/* rate scale requires free function to be implemented */ +static void +il3945_rs_free(void *il) +{ +} + +static void * +il3945_rs_alloc_sta(void *il_priv, struct ieee80211_sta *sta, gfp_t gfp) +{ + struct il3945_rs_sta *rs_sta; + struct il3945_sta_priv *psta = (void *)sta->drv_priv; + struct il_priv *il __maybe_unused = il_priv; + + D_RATE("enter\n"); + + rs_sta = &psta->rs_sta; + + spin_lock_init(&rs_sta->lock); + init_timer(&rs_sta->rate_scale_flush); + + D_RATE("leave\n"); + + return rs_sta; +} + +static void +il3945_rs_free_sta(void *il_priv, struct ieee80211_sta *sta, void *il_sta) +{ + struct il3945_rs_sta *rs_sta = il_sta; + + /* + * Be careful not to use any members of il3945_rs_sta (like trying + * to use il_priv to print out debugging) since it may not be fully + * initialized at this point. + */ + del_timer_sync(&rs_sta->rate_scale_flush); +} + +/** + * il3945_rs_tx_status - Update rate control values based on Tx results + * + * NOTE: Uses il_priv->retry_rate for the # of retries attempted by + * the hardware for each rate. + */ +static void +il3945_rs_tx_status(void *il_rate, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *il_sta, + struct sk_buff *skb) +{ + s8 retries = 0, current_count; + int scale_rate_idx, first_idx, last_idx; + unsigned long flags; + struct il_priv *il = (struct il_priv *)il_rate; + struct il3945_rs_sta *rs_sta = il_sta; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + D_RATE("enter\n"); + + retries = info->status.rates[0].count; + /* Sanity Check for retries */ + if (retries > RATE_RETRY_TH) + retries = RATE_RETRY_TH; + + first_idx = sband->bitrates[info->status.rates[0].idx].hw_value; + if (first_idx < 0 || first_idx >= RATE_COUNT_3945) { + D_RATE("leave: Rate out of bounds: %d\n", first_idx); + return; + } + + if (!il_sta) { + D_RATE("leave: No STA il data to update!\n"); + return; + } + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (!rs_sta->il) { + D_RATE("leave: STA il data uninitialized!\n"); + return; + } + + rs_sta->tx_packets++; + + scale_rate_idx = first_idx; + last_idx = first_idx; + + /* + * Update the win for each rate. We determine which rates + * were Tx'd based on the total number of retries vs. the number + * of retries configured for each rate -- currently set to the + * il value 'retry_rate' vs. rate specific + * + * On exit from this while loop last_idx indicates the rate + * at which the frame was finally transmitted (or failed if no + * ACK) + */ + while (retries > 1) { + if ((retries - 1) < il->retry_rate) { + current_count = (retries - 1); + last_idx = scale_rate_idx; + } else { + current_count = il->retry_rate; + last_idx = il3945_rs_next_rate(il, scale_rate_idx); + } + + /* Update this rate accounting for as many retries + * as was used for it (per current_count) */ + il3945_collect_tx_data(rs_sta, &rs_sta->win[scale_rate_idx], 0, + current_count, scale_rate_idx); + D_RATE("Update rate %d for %d retries.\n", scale_rate_idx, + current_count); + + retries -= current_count; + + scale_rate_idx = last_idx; + } + + /* Update the last idx win with success/failure based on ACK */ + D_RATE("Update rate %d with %s.\n", last_idx, + (info->flags & IEEE80211_TX_STAT_ACK) ? "success" : "failure"); + il3945_collect_tx_data(rs_sta, &rs_sta->win[last_idx], + info->flags & IEEE80211_TX_STAT_ACK, 1, + last_idx); + + /* We updated the rate scale win -- if its been more than + * flush_time since the last run, schedule the flush + * again */ + spin_lock_irqsave(&rs_sta->lock, flags); + + if (!rs_sta->flush_pending && + time_after(jiffies, rs_sta->last_flush + rs_sta->flush_time)) { + + rs_sta->last_partial_flush = jiffies; + rs_sta->flush_pending = 1; + mod_timer(&rs_sta->rate_scale_flush, + jiffies + rs_sta->flush_time); + } + + spin_unlock_irqrestore(&rs_sta->lock, flags); + + D_RATE("leave\n"); +} + +static u16 +il3945_get_adjacent_rate(struct il3945_rs_sta *rs_sta, u8 idx, u16 rate_mask, + enum ieee80211_band band) +{ + u8 high = RATE_INVALID; + u8 low = RATE_INVALID; + struct il_priv *il __maybe_unused = rs_sta->il; + + /* 802.11A walks to the next literal adjacent rate in + * the rate table */ + if (unlikely(band == IEEE80211_BAND_5GHZ)) { + int i; + u32 mask; + + /* Find the previous rate that is in the rate mask */ + i = idx - 1; + for (mask = (1 << i); i >= 0; i--, mask >>= 1) { + if (rate_mask & mask) { + low = i; + break; + } + } + + /* Find the next rate that is in the rate mask */ + i = idx + 1; + for (mask = (1 << i); i < RATE_COUNT_3945; i++, mask <<= 1) { + if (rate_mask & mask) { + high = i; + break; + } + } + + return (high << 8) | low; + } + + low = idx; + while (low != RATE_INVALID) { + if (rs_sta->tgg) + low = il3945_rates[low].prev_rs_tgg; + else + low = il3945_rates[low].prev_rs; + if (low == RATE_INVALID) + break; + if (rate_mask & (1 << low)) + break; + D_RATE("Skipping masked lower rate: %d\n", low); + } + + high = idx; + while (high != RATE_INVALID) { + if (rs_sta->tgg) + high = il3945_rates[high].next_rs_tgg; + else + high = il3945_rates[high].next_rs; + if (high == RATE_INVALID) + break; + if (rate_mask & (1 << high)) + break; + D_RATE("Skipping masked higher rate: %d\n", high); + } + + return (high << 8) | low; +} + +/** + * il3945_rs_get_rate - find the rate for the requested packet + * + * Returns the ieee80211_rate structure allocated by the driver. + * + * The rate control algorithm has no internal mapping between hw_mode's + * rate ordering and the rate ordering used by the rate control algorithm. + * + * The rate control algorithm uses a single table of rates that goes across + * the entire A/B/G spectrum vs. being limited to just one particular + * hw_mode. + * + * As such, we can't convert the idx obtained below into the hw_mode's + * rate table and must reference the driver allocated rate table + * + */ +static void +il3945_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta, + struct ieee80211_tx_rate_control *txrc) +{ + struct ieee80211_supported_band *sband = txrc->sband; + struct sk_buff *skb = txrc->skb; + u8 low = RATE_INVALID; + u8 high = RATE_INVALID; + u16 high_low; + int idx; + struct il3945_rs_sta *rs_sta = il_sta; + struct il3945_rate_scale_data *win = NULL; + int current_tpt = IL_INVALID_VALUE; + int low_tpt = IL_INVALID_VALUE; + int high_tpt = IL_INVALID_VALUE; + u32 fail_count; + s8 scale_action = 0; + unsigned long flags; + u16 rate_mask; + s8 max_rate_idx = -1; + struct il_priv *il __maybe_unused = (struct il_priv *)il_r; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + D_RATE("enter\n"); + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (rs_sta && !rs_sta->il) { + D_RATE("Rate scaling information not initialized yet.\n"); + il_sta = NULL; + } + + if (rate_control_send_low(sta, il_sta, txrc)) + return; + + rate_mask = sta->supp_rates[sband->band]; + + /* get user max rate if set */ + max_rate_idx = txrc->max_rate_idx; + if (sband->band == IEEE80211_BAND_5GHZ && max_rate_idx != -1) + max_rate_idx += IL_FIRST_OFDM_RATE; + if (max_rate_idx < 0 || max_rate_idx >= RATE_COUNT) + max_rate_idx = -1; + + idx = min(rs_sta->last_txrate_idx & 0xffff, RATE_COUNT_3945 - 1); + + if (sband->band == IEEE80211_BAND_5GHZ) + rate_mask = rate_mask << IL_FIRST_OFDM_RATE; + + spin_lock_irqsave(&rs_sta->lock, flags); + + /* for recent assoc, choose best rate regarding + * to rssi value + */ + if (rs_sta->start_rate != RATE_INVALID) { + if (rs_sta->start_rate < idx && + (rate_mask & (1 << rs_sta->start_rate))) + idx = rs_sta->start_rate; + rs_sta->start_rate = RATE_INVALID; + } + + /* force user max rate if set by user */ + if (max_rate_idx != -1 && max_rate_idx < idx) { + if (rate_mask & (1 << max_rate_idx)) + idx = max_rate_idx; + } + + win = &(rs_sta->win[idx]); + + fail_count = win->counter - win->success_counter; + + if (fail_count < RATE_MIN_FAILURE_TH && + win->success_counter < RATE_MIN_SUCCESS_TH) { + spin_unlock_irqrestore(&rs_sta->lock, flags); + + D_RATE("Invalid average_tpt on rate %d: " + "counter: %d, success_counter: %d, " + "expected_tpt is %sNULL\n", idx, win->counter, + win->success_counter, + rs_sta->expected_tpt ? "not " : ""); + + /* Can't calculate this yet; not enough history */ + win->average_tpt = IL_INVALID_VALUE; + goto out; + + } + + current_tpt = win->average_tpt; + + high_low = + il3945_get_adjacent_rate(rs_sta, idx, rate_mask, sband->band); + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + /* If user set max rate, dont allow higher than user constrain */ + if (max_rate_idx != -1 && max_rate_idx < high) + high = RATE_INVALID; + + /* Collect Measured throughputs of adjacent rates */ + if (low != RATE_INVALID) + low_tpt = rs_sta->win[low].average_tpt; + + if (high != RATE_INVALID) + high_tpt = rs_sta->win[high].average_tpt; + + spin_unlock_irqrestore(&rs_sta->lock, flags); + + scale_action = 0; + + /* Low success ratio , need to drop the rate */ + if (win->success_ratio < RATE_DECREASE_TH || !current_tpt) { + D_RATE("decrease rate because of low success_ratio\n"); + scale_action = -1; + /* No throughput measured yet for adjacent rates, + * try increase */ + } else if (low_tpt == IL_INVALID_VALUE && high_tpt == IL_INVALID_VALUE) { + + if (high != RATE_INVALID && + win->success_ratio >= RATE_INCREASE_TH) + scale_action = 1; + else if (low != RATE_INVALID) + scale_action = 0; + + /* Both adjacent throughputs are measured, but neither one has + * better throughput; we're using the best rate, don't change + * it! */ + } else if (low_tpt != IL_INVALID_VALUE && high_tpt != IL_INVALID_VALUE + && low_tpt < current_tpt && high_tpt < current_tpt) { + + D_RATE("No action -- low [%d] & high [%d] < " + "current_tpt [%d]\n", low_tpt, high_tpt, current_tpt); + scale_action = 0; + + /* At least one of the rates has better throughput */ + } else { + if (high_tpt != IL_INVALID_VALUE) { + + /* High rate has better throughput, Increase + * rate */ + if (high_tpt > current_tpt && + win->success_ratio >= RATE_INCREASE_TH) + scale_action = 1; + else { + D_RATE("decrease rate because of high tpt\n"); + scale_action = 0; + } + } else if (low_tpt != IL_INVALID_VALUE) { + if (low_tpt > current_tpt) { + D_RATE("decrease rate because of low tpt\n"); + scale_action = -1; + } else if (win->success_ratio >= RATE_INCREASE_TH) { + /* Lower rate has better + * throughput,decrease rate */ + scale_action = 1; + } + } + } + + /* Sanity check; asked for decrease, but success rate or throughput + * has been good at old rate. Don't change it. */ + if (scale_action == -1 && low != RATE_INVALID && + (win->success_ratio > RATE_HIGH_TH || + current_tpt > 100 * rs_sta->expected_tpt[low])) + scale_action = 0; + + switch (scale_action) { + case -1: + /* Decrese rate */ + if (low != RATE_INVALID) + idx = low; + break; + case 1: + /* Increase rate */ + if (high != RATE_INVALID) + idx = high; + + break; + case 0: + default: + /* No change */ + break; + } + + D_RATE("Selected %d (action %d) - low %d high %d\n", idx, scale_action, + low, high); + +out: + + if (sband->band == IEEE80211_BAND_5GHZ) { + if (WARN_ON_ONCE(idx < IL_FIRST_OFDM_RATE)) + idx = IL_FIRST_OFDM_RATE; + rs_sta->last_txrate_idx = idx; + info->control.rates[0].idx = idx - IL_FIRST_OFDM_RATE; + } else { + rs_sta->last_txrate_idx = idx; + info->control.rates[0].idx = rs_sta->last_txrate_idx; + } + info->control.rates[0].count = 1; + + D_RATE("leave: %d\n", idx); +} + +#ifdef CONFIG_MAC80211_DEBUGFS + +static ssize_t +il3945_sta_dbgfs_stats_table_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *buff; + int desc = 0; + int j; + ssize_t ret; + struct il3945_rs_sta *lq_sta = file->private_data; + + buff = kmalloc(1024, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + desc += + sprintf(buff + desc, + "tx packets=%d last rate idx=%d\n" + "rate=0x%X flush time %d\n", lq_sta->tx_packets, + lq_sta->last_txrate_idx, lq_sta->start_rate, + jiffies_to_msecs(lq_sta->flush_time)); + for (j = 0; j < RATE_COUNT_3945; j++) { + desc += + sprintf(buff + desc, "counter=%d success=%d %%=%d\n", + lq_sta->win[j].counter, + lq_sta->win[j].success_counter, + lq_sta->win[j].success_ratio); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_stats_table_ops = { + .read = il3945_sta_dbgfs_stats_table_read, + .open = simple_open, + .llseek = default_llseek, +}; + +static void +il3945_add_debugfs(void *il, void *il_sta, struct dentry *dir) +{ + struct il3945_rs_sta *lq_sta = il_sta; + + lq_sta->rs_sta_dbgfs_stats_table_file = + debugfs_create_file("rate_stats_table", 0600, dir, lq_sta, + &rs_sta_dbgfs_stats_table_ops); + +} + +static void +il3945_remove_debugfs(void *il, void *il_sta) +{ + struct il3945_rs_sta *lq_sta = il_sta; + debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); +} +#endif + +/* + * Initialization of rate scaling information is done by driver after + * the station is added. Since mac80211 calls this function before a + * station is added we ignore it. + */ +static void +il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *il_sta) +{ +} + +static const struct rate_control_ops rs_ops = { + .name = RS_NAME, + .tx_status = il3945_rs_tx_status, + .get_rate = il3945_rs_get_rate, + .rate_init = il3945_rs_rate_init_stub, + .alloc = il3945_rs_alloc, + .free = il3945_rs_free, + .alloc_sta = il3945_rs_alloc_sta, + .free_sta = il3945_rs_free_sta, +#ifdef CONFIG_MAC80211_DEBUGFS + .add_sta_debugfs = il3945_add_debugfs, + .remove_sta_debugfs = il3945_remove_debugfs, +#endif + +}; + +void +il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) +{ + struct il_priv *il = hw->priv; + s32 rssi = 0; + unsigned long flags; + struct il3945_rs_sta *rs_sta; + struct ieee80211_sta *sta; + struct il3945_sta_priv *psta; + + D_RATE("enter\n"); + + rcu_read_lock(); + + sta = ieee80211_find_sta(il->vif, il->stations[sta_id].sta.sta.addr); + if (!sta) { + D_RATE("Unable to find station to initialize rate scaling.\n"); + rcu_read_unlock(); + return; + } + + psta = (void *)sta->drv_priv; + rs_sta = &psta->rs_sta; + + spin_lock_irqsave(&rs_sta->lock, flags); + + rs_sta->tgg = 0; + switch (il->band) { + case IEEE80211_BAND_2GHZ: + /* TODO: this always does G, not a regression */ + if (il->active.flags & RXON_FLG_TGG_PROTECT_MSK) { + rs_sta->tgg = 1; + rs_sta->expected_tpt = il3945_expected_tpt_g_prot; + } else + rs_sta->expected_tpt = il3945_expected_tpt_g; + break; + case IEEE80211_BAND_5GHZ: + rs_sta->expected_tpt = il3945_expected_tpt_a; + break; + default: + BUG(); + break; + } + + spin_unlock_irqrestore(&rs_sta->lock, flags); + + rssi = il->_3945.last_rx_rssi; + if (rssi == 0) + rssi = IL_MIN_RSSI_VAL; + + D_RATE("Network RSSI: %d\n", rssi); + + rs_sta->start_rate = il3945_get_rate_idx_by_rssi(rssi, il->band); + + D_RATE("leave: rssi %d assign rate idx: " "%d (plcp 0x%x)\n", rssi, + rs_sta->start_rate, il3945_rates[rs_sta->start_rate].plcp); + rcu_read_unlock(); +} + +int +il3945_rate_control_register(void) +{ + return ieee80211_rate_control_register(&rs_ops); +} + +void +il3945_rate_control_unregister(void) +{ + ieee80211_rate_control_unregister(&rs_ops); +} diff --git a/drivers/net/wireless/intel/iwlegacy/3945.c b/drivers/net/wireless/intel/iwlegacy/3945.c new file mode 100644 index 000000000000..93bdf684babe --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/3945.c @@ -0,0 +1,2741 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "3945.h" + +/* Send led command */ +static int +il3945_send_led_cmd(struct il_priv *il, struct il_led_cmd *led_cmd) +{ + struct il_host_cmd cmd = { + .id = C_LEDS, + .len = sizeof(struct il_led_cmd), + .data = led_cmd, + .flags = CMD_ASYNC, + .callback = NULL, + }; + + return il_send_cmd(il, &cmd); +} + +#define IL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np) \ + [RATE_##r##M_IDX] = { RATE_##r##M_PLCP, \ + RATE_##r##M_IEEE, \ + RATE_##ip##M_IDX, \ + RATE_##in##M_IDX, \ + RATE_##rp##M_IDX, \ + RATE_##rn##M_IDX, \ + RATE_##pp##M_IDX, \ + RATE_##np##M_IDX, \ + RATE_##r##M_IDX_TBL, \ + RATE_##ip##M_IDX_TBL } + +/* + * Parameter order: + * rate, prev rate, next rate, prev tgg rate, next tgg rate + * + * If there isn't a valid next or previous rate then INV is used which + * maps to RATE_INVALID + * + */ +const struct il3945_rate_info il3945_rates[RATE_COUNT_3945] = { + IL_DECLARE_RATE_INFO(1, INV, 2, INV, 2, INV, 2), /* 1mbps */ + IL_DECLARE_RATE_INFO(2, 1, 5, 1, 5, 1, 5), /* 2mbps */ + IL_DECLARE_RATE_INFO(5, 2, 6, 2, 11, 2, 11), /*5.5mbps */ + IL_DECLARE_RATE_INFO(11, 9, 12, 5, 12, 5, 18), /* 11mbps */ + IL_DECLARE_RATE_INFO(6, 5, 9, 5, 11, 5, 11), /* 6mbps */ + IL_DECLARE_RATE_INFO(9, 6, 11, 5, 11, 5, 11), /* 9mbps */ + IL_DECLARE_RATE_INFO(12, 11, 18, 11, 18, 11, 18), /* 12mbps */ + IL_DECLARE_RATE_INFO(18, 12, 24, 12, 24, 11, 24), /* 18mbps */ + IL_DECLARE_RATE_INFO(24, 18, 36, 18, 36, 18, 36), /* 24mbps */ + IL_DECLARE_RATE_INFO(36, 24, 48, 24, 48, 24, 48), /* 36mbps */ + IL_DECLARE_RATE_INFO(48, 36, 54, 36, 54, 36, 54), /* 48mbps */ + IL_DECLARE_RATE_INFO(54, 48, INV, 48, INV, 48, INV), /* 54mbps */ +}; + +static inline u8 +il3945_get_prev_ieee_rate(u8 rate_idx) +{ + u8 rate = il3945_rates[rate_idx].prev_ieee; + + if (rate == RATE_INVALID) + rate = rate_idx; + return rate; +} + +/* 1 = enable the il3945_disable_events() function */ +#define IL_EVT_DISABLE (0) +#define IL_EVT_DISABLE_SIZE (1532/32) + +/** + * il3945_disable_events - Disable selected events in uCode event log + * + * Disable an event by writing "1"s into "disable" + * bitmap in SRAM. Bit position corresponds to Event # (id/type). + * Default values of 0 enable uCode events to be logged. + * Use for only special debugging. This function is just a placeholder as-is, + * you'll need to provide the special bits! ... + * ... and set IL_EVT_DISABLE to 1. */ +void +il3945_disable_events(struct il_priv *il) +{ + int i; + u32 base; /* SRAM address of event log header */ + u32 disable_ptr; /* SRAM address of event-disable bitmap array */ + u32 array_size; /* # of u32 entries in array */ + static const u32 evt_disable[IL_EVT_DISABLE_SIZE] = { + 0x00000000, /* 31 - 0 Event id numbers */ + 0x00000000, /* 63 - 32 */ + 0x00000000, /* 95 - 64 */ + 0x00000000, /* 127 - 96 */ + 0x00000000, /* 159 - 128 */ + 0x00000000, /* 191 - 160 */ + 0x00000000, /* 223 - 192 */ + 0x00000000, /* 255 - 224 */ + 0x00000000, /* 287 - 256 */ + 0x00000000, /* 319 - 288 */ + 0x00000000, /* 351 - 320 */ + 0x00000000, /* 383 - 352 */ + 0x00000000, /* 415 - 384 */ + 0x00000000, /* 447 - 416 */ + 0x00000000, /* 479 - 448 */ + 0x00000000, /* 511 - 480 */ + 0x00000000, /* 543 - 512 */ + 0x00000000, /* 575 - 544 */ + 0x00000000, /* 607 - 576 */ + 0x00000000, /* 639 - 608 */ + 0x00000000, /* 671 - 640 */ + 0x00000000, /* 703 - 672 */ + 0x00000000, /* 735 - 704 */ + 0x00000000, /* 767 - 736 */ + 0x00000000, /* 799 - 768 */ + 0x00000000, /* 831 - 800 */ + 0x00000000, /* 863 - 832 */ + 0x00000000, /* 895 - 864 */ + 0x00000000, /* 927 - 896 */ + 0x00000000, /* 959 - 928 */ + 0x00000000, /* 991 - 960 */ + 0x00000000, /* 1023 - 992 */ + 0x00000000, /* 1055 - 1024 */ + 0x00000000, /* 1087 - 1056 */ + 0x00000000, /* 1119 - 1088 */ + 0x00000000, /* 1151 - 1120 */ + 0x00000000, /* 1183 - 1152 */ + 0x00000000, /* 1215 - 1184 */ + 0x00000000, /* 1247 - 1216 */ + 0x00000000, /* 1279 - 1248 */ + 0x00000000, /* 1311 - 1280 */ + 0x00000000, /* 1343 - 1312 */ + 0x00000000, /* 1375 - 1344 */ + 0x00000000, /* 1407 - 1376 */ + 0x00000000, /* 1439 - 1408 */ + 0x00000000, /* 1471 - 1440 */ + 0x00000000, /* 1503 - 1472 */ + }; + + base = le32_to_cpu(il->card_alive.log_event_table_ptr); + if (!il3945_hw_valid_rtc_data_addr(base)) { + IL_ERR("Invalid event log pointer 0x%08X\n", base); + return; + } + + disable_ptr = il_read_targ_mem(il, base + (4 * sizeof(u32))); + array_size = il_read_targ_mem(il, base + (5 * sizeof(u32))); + + if (IL_EVT_DISABLE && array_size == IL_EVT_DISABLE_SIZE) { + D_INFO("Disabling selected uCode log events at 0x%x\n", + disable_ptr); + for (i = 0; i < IL_EVT_DISABLE_SIZE; i++) + il_write_targ_mem(il, disable_ptr + (i * sizeof(u32)), + evt_disable[i]); + + } else { + D_INFO("Selected uCode log events may be disabled\n"); + D_INFO(" by writing \"1\"s into disable bitmap\n"); + D_INFO(" in SRAM at 0x%x, size %d u32s\n", disable_ptr, + array_size); + } + +} + +static int +il3945_hwrate_to_plcp_idx(u8 plcp) +{ + int idx; + + for (idx = 0; idx < RATE_COUNT_3945; idx++) + if (il3945_rates[idx].plcp == plcp) + return idx; + return -1; +} + +#ifdef CONFIG_IWLEGACY_DEBUG +#define TX_STATUS_ENTRY(x) case TX_3945_STATUS_FAIL_ ## x: return #x + +static const char * +il3945_get_tx_fail_reason(u32 status) +{ + switch (status & TX_STATUS_MSK) { + case TX_3945_STATUS_SUCCESS: + return "SUCCESS"; + TX_STATUS_ENTRY(SHORT_LIMIT); + TX_STATUS_ENTRY(LONG_LIMIT); + TX_STATUS_ENTRY(FIFO_UNDERRUN); + TX_STATUS_ENTRY(MGMNT_ABORT); + TX_STATUS_ENTRY(NEXT_FRAG); + TX_STATUS_ENTRY(LIFE_EXPIRE); + TX_STATUS_ENTRY(DEST_PS); + TX_STATUS_ENTRY(ABORTED); + TX_STATUS_ENTRY(BT_RETRY); + TX_STATUS_ENTRY(STA_INVALID); + TX_STATUS_ENTRY(FRAG_DROPPED); + TX_STATUS_ENTRY(TID_DISABLE); + TX_STATUS_ENTRY(FRAME_FLUSHED); + TX_STATUS_ENTRY(INSUFFICIENT_CF_POLL); + TX_STATUS_ENTRY(TX_LOCKED); + TX_STATUS_ENTRY(NO_BEACON_ON_RADAR); + } + + return "UNKNOWN"; +} +#else +static inline const char * +il3945_get_tx_fail_reason(u32 status) +{ + return ""; +} +#endif + +/* + * get ieee prev rate from rate scale table. + * for A and B mode we need to overright prev + * value + */ +int +il3945_rs_next_rate(struct il_priv *il, int rate) +{ + int next_rate = il3945_get_prev_ieee_rate(rate); + + switch (il->band) { + case IEEE80211_BAND_5GHZ: + if (rate == RATE_12M_IDX) + next_rate = RATE_9M_IDX; + else if (rate == RATE_6M_IDX) + next_rate = RATE_6M_IDX; + break; + case IEEE80211_BAND_2GHZ: + if (!(il->_3945.sta_supp_rates & IL_OFDM_RATES_MASK) && + il_is_associated(il)) { + if (rate == RATE_11M_IDX) + next_rate = RATE_5M_IDX; + } + break; + + default: + break; + } + + return next_rate; +} + +/** + * il3945_tx_queue_reclaim - Reclaim Tx queue entries already Tx'd + * + * When FW advances 'R' idx, all entries between old and new 'R' idx + * need to be reclaimed. As result, some free space forms. If there is + * enough free space (> low mark), wake the stack that feeds us. + */ +static void +il3945_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx) +{ + struct il_tx_queue *txq = &il->txq[txq_id]; + struct il_queue *q = &txq->q; + struct sk_buff *skb; + + BUG_ON(txq_id == IL39_CMD_QUEUE_NUM); + + for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; + q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { + + skb = txq->skbs[txq->q.read_ptr]; + ieee80211_tx_status_irqsafe(il->hw, skb); + txq->skbs[txq->q.read_ptr] = NULL; + il->ops->txq_free_tfd(il, txq); + } + + if (il_queue_space(q) > q->low_mark && txq_id >= 0 && + txq_id != IL39_CMD_QUEUE_NUM && il->mac80211_registered) + il_wake_queue(il, txq); +} + +/** + * il3945_hdl_tx - Handle Tx response + */ +static void +il3945_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + u16 sequence = le16_to_cpu(pkt->hdr.sequence); + int txq_id = SEQ_TO_QUEUE(sequence); + int idx = SEQ_TO_IDX(sequence); + struct il_tx_queue *txq = &il->txq[txq_id]; + struct ieee80211_tx_info *info; + struct il3945_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; + u32 status = le32_to_cpu(tx_resp->status); + int rate_idx; + int fail; + + if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) { + IL_ERR("Read idx for DMA queue txq_id (%d) idx %d " + "is out of range [0-%d] %d %d\n", txq_id, idx, + txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr); + return; + } + + /* + * Firmware will not transmit frame on passive channel, if it not yet + * received some valid frame on that channel. When this error happen + * we have to wait until firmware will unblock itself i.e. when we + * note received beacon or other frame. We unblock queues in + * il3945_pass_packet_to_mac80211 or in il_mac_bss_info_changed. + */ + if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) && + il->iw_mode == NL80211_IFTYPE_STATION) { + il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Stopped queues - RX waiting on passive channel\n"); + } + + txq->time_stamp = jiffies; + info = IEEE80211_SKB_CB(txq->skbs[txq->q.read_ptr]); + ieee80211_tx_info_clear_status(info); + + /* Fill the MRR chain with some info about on-chip retransmissions */ + rate_idx = il3945_hwrate_to_plcp_idx(tx_resp->rate); + if (info->band == IEEE80211_BAND_5GHZ) + rate_idx -= IL_FIRST_OFDM_RATE; + + fail = tx_resp->failure_frame; + + info->status.rates[0].idx = rate_idx; + info->status.rates[0].count = fail + 1; /* add final attempt */ + + /* tx_status->rts_retry_count = tx_resp->failure_rts; */ + info->flags |= + ((status & TX_STATUS_MSK) == + TX_STATUS_SUCCESS) ? IEEE80211_TX_STAT_ACK : 0; + + D_TX("Tx queue %d Status %s (0x%08x) plcp rate %d retries %d\n", txq_id, + il3945_get_tx_fail_reason(status), status, tx_resp->rate, + tx_resp->failure_frame); + + D_TX_REPLY("Tx queue reclaim %d\n", idx); + il3945_tx_queue_reclaim(il, txq_id, idx); + + if (status & TX_ABORT_REQUIRED_MSK) + IL_ERR("TODO: Implement Tx ABORT REQUIRED!!!\n"); +} + +/***************************************************************************** + * + * Intel PRO/Wireless 3945ABG/BG Network Connection + * + * RX handler implementations + * + *****************************************************************************/ +#ifdef CONFIG_IWLEGACY_DEBUGFS +static void +il3945_accumulative_stats(struct il_priv *il, __le32 * stats) +{ + int i; + __le32 *prev_stats; + u32 *accum_stats; + u32 *delta, *max_delta; + + prev_stats = (__le32 *) &il->_3945.stats; + accum_stats = (u32 *) &il->_3945.accum_stats; + delta = (u32 *) &il->_3945.delta_stats; + max_delta = (u32 *) &il->_3945.max_delta; + + for (i = sizeof(__le32); i < sizeof(struct il3945_notif_stats); + i += + sizeof(__le32), stats++, prev_stats++, delta++, max_delta++, + accum_stats++) { + if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) { + *delta = + (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats)); + *accum_stats += *delta; + if (*delta > *max_delta) + *max_delta = *delta; + } + } + + /* reset accumulative stats for "no-counter" type stats */ + il->_3945.accum_stats.general.temperature = + il->_3945.stats.general.temperature; + il->_3945.accum_stats.general.ttl_timestamp = + il->_3945.stats.general.ttl_timestamp; +} +#endif + +void +il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + + D_RX("Statistics notification received (%d vs %d).\n", + (int)sizeof(struct il3945_notif_stats), + le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK); +#ifdef CONFIG_IWLEGACY_DEBUGFS + il3945_accumulative_stats(il, (__le32 *) &pkt->u.raw); +#endif + + memcpy(&il->_3945.stats, pkt->u.raw, sizeof(il->_3945.stats)); +} + +void +il3945_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + __le32 *flag = (__le32 *) &pkt->u.raw; + + if (le32_to_cpu(*flag) & UCODE_STATS_CLEAR_MSK) { +#ifdef CONFIG_IWLEGACY_DEBUGFS + memset(&il->_3945.accum_stats, 0, + sizeof(struct il3945_notif_stats)); + memset(&il->_3945.delta_stats, 0, + sizeof(struct il3945_notif_stats)); + memset(&il->_3945.max_delta, 0, + sizeof(struct il3945_notif_stats)); +#endif + D_RX("Statistics have been cleared\n"); + } + il3945_hdl_stats(il, rxb); +} + +/****************************************************************************** + * + * Misc. internal state and helper functions + * + ******************************************************************************/ + +/* This is necessary only for a number of stats, see the caller. */ +static int +il3945_is_network_packet(struct il_priv *il, struct ieee80211_hdr *header) +{ + /* Filter incoming packets to determine if they are targeted toward + * this network, discarding packets coming from ourselves */ + switch (il->iw_mode) { + case NL80211_IFTYPE_ADHOC: /* Header: Dest. | Source | BSSID */ + /* packets to our IBSS update information */ + return ether_addr_equal_64bits(header->addr3, il->bssid); + case NL80211_IFTYPE_STATION: /* Header: Dest. | AP{BSSID} | Source */ + /* packets to our IBSS update information */ + return ether_addr_equal_64bits(header->addr2, il->bssid); + default: + return 1; + } +} + +#define SMALL_PACKET_SIZE 256 + +static void +il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb, + struct ieee80211_rx_status *stats) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IL_RX_DATA(pkt); + struct il3945_rx_frame_hdr *rx_hdr = IL_RX_HDR(pkt); + struct il3945_rx_frame_end *rx_end = IL_RX_END(pkt); + u32 len = le16_to_cpu(rx_hdr->len); + struct sk_buff *skb; + __le16 fc = hdr->frame_control; + u32 fraglen = PAGE_SIZE << il->hw_params.rx_page_order; + + /* We received data from the HW, so stop the watchdog */ + if (unlikely(len + IL39_RX_FRAME_SIZE > fraglen)) { + D_DROP("Corruption detected!\n"); + return; + } + + /* We only process data packets if the interface is open */ + if (unlikely(!il->is_open)) { + D_DROP("Dropping packet while interface is not open.\n"); + return; + } + + if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) { + il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Woke queues - frame received on passive channel\n"); + } + + skb = dev_alloc_skb(SMALL_PACKET_SIZE); + if (!skb) { + IL_ERR("dev_alloc_skb failed\n"); + return; + } + + if (!il3945_mod_params.sw_crypto) + il_set_decrypted_flag(il, (struct ieee80211_hdr *)pkt, + le32_to_cpu(rx_end->status), stats); + + /* If frame is small enough to fit into skb->head, copy it + * and do not consume a full page + */ + if (len <= SMALL_PACKET_SIZE) { + memcpy(skb_put(skb, len), rx_hdr->payload, len); + } else { + skb_add_rx_frag(skb, 0, rxb->page, + (void *)rx_hdr->payload - (void *)pkt, len, + fraglen); + il->alloc_rxb_page--; + rxb->page = NULL; + } + il_update_stats(il, false, fc, len); + memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); + + ieee80211_rx(il->hw, skb); +} + +#define IL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6) + +static void +il3945_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct ieee80211_hdr *header; + struct ieee80211_rx_status rx_status = {}; + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il3945_rx_frame_stats *rx_stats = IL_RX_STATS(pkt); + struct il3945_rx_frame_hdr *rx_hdr = IL_RX_HDR(pkt); + struct il3945_rx_frame_end *rx_end = IL_RX_END(pkt); + u16 rx_stats_sig_avg __maybe_unused = le16_to_cpu(rx_stats->sig_avg); + u16 rx_stats_noise_diff __maybe_unused = + le16_to_cpu(rx_stats->noise_diff); + u8 network_packet; + + rx_status.flag = 0; + rx_status.mactime = le64_to_cpu(rx_end->timestamp); + rx_status.band = + (rx_hdr-> + phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ; + rx_status.freq = + ieee80211_channel_to_frequency(le16_to_cpu(rx_hdr->channel), + rx_status.band); + + rx_status.rate_idx = il3945_hwrate_to_plcp_idx(rx_hdr->rate); + if (rx_status.band == IEEE80211_BAND_5GHZ) + rx_status.rate_idx -= IL_FIRST_OFDM_RATE; + + rx_status.antenna = + (le16_to_cpu(rx_hdr->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> + 4; + + /* set the preamble flag if appropriate */ + if (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) + rx_status.flag |= RX_FLAG_SHORTPRE; + + if ((unlikely(rx_stats->phy_count > 20))) { + D_DROP("dsp size out of range [0,20]: %d\n", + rx_stats->phy_count); + return; + } + + if (!(rx_end->status & RX_RES_STATUS_NO_CRC32_ERROR) || + !(rx_end->status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { + D_RX("Bad CRC or FIFO: 0x%08X.\n", rx_end->status); + return; + } + + /* Convert 3945's rssi indicator to dBm */ + rx_status.signal = rx_stats->rssi - IL39_RSSI_OFFSET; + + D_STATS("Rssi %d sig_avg %d noise_diff %d\n", rx_status.signal, + rx_stats_sig_avg, rx_stats_noise_diff); + + header = (struct ieee80211_hdr *)IL_RX_DATA(pkt); + + network_packet = il3945_is_network_packet(il, header); + + D_STATS("[%c] %d RSSI:%d Signal:%u, Rate:%u\n", + network_packet ? '*' : ' ', le16_to_cpu(rx_hdr->channel), + rx_status.signal, rx_status.signal, rx_status.rate_idx); + + if (network_packet) { + il->_3945.last_beacon_time = + le32_to_cpu(rx_end->beacon_timestamp); + il->_3945.last_tsf = le64_to_cpu(rx_end->timestamp); + il->_3945.last_rx_rssi = rx_status.signal; + } + + il3945_pass_packet_to_mac80211(il, rxb, &rx_status); +} + +int +il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, + dma_addr_t addr, u16 len, u8 reset, u8 pad) +{ + int count; + struct il_queue *q; + struct il3945_tfd *tfd, *tfd_tmp; + + q = &txq->q; + tfd_tmp = (struct il3945_tfd *)txq->tfds; + tfd = &tfd_tmp[q->write_ptr]; + + if (reset) + memset(tfd, 0, sizeof(*tfd)); + + count = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags)); + + if (count >= NUM_TFD_CHUNKS || count < 0) { + IL_ERR("Error can not send more than %d chunks\n", + NUM_TFD_CHUNKS); + return -EINVAL; + } + + tfd->tbs[count].addr = cpu_to_le32(addr); + tfd->tbs[count].len = cpu_to_le32(len); + + count++; + + tfd->control_flags = + cpu_to_le32(TFD_CTL_COUNT_SET(count) | TFD_CTL_PAD_SET(pad)); + + return 0; +} + +/** + * il3945_hw_txq_free_tfd - Free one TFD, those at idx [txq->q.read_ptr] + * + * Does NOT advance any idxes + */ +void +il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq) +{ + struct il3945_tfd *tfd_tmp = (struct il3945_tfd *)txq->tfds; + int idx = txq->q.read_ptr; + struct il3945_tfd *tfd = &tfd_tmp[idx]; + struct pci_dev *dev = il->pci_dev; + int i; + int counter; + + /* sanity check */ + counter = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags)); + if (counter > NUM_TFD_CHUNKS) { + IL_ERR("Too many chunks: %i\n", counter); + /* @todo issue fatal error, it is quite serious situation */ + return; + } + + /* Unmap tx_cmd */ + if (counter) + pci_unmap_single(dev, dma_unmap_addr(&txq->meta[idx], mapping), + dma_unmap_len(&txq->meta[idx], len), + PCI_DMA_TODEVICE); + + /* unmap chunks if any */ + + for (i = 1; i < counter; i++) + pci_unmap_single(dev, le32_to_cpu(tfd->tbs[i].addr), + le32_to_cpu(tfd->tbs[i].len), + PCI_DMA_TODEVICE); + + /* free SKB */ + if (txq->skbs) { + struct sk_buff *skb = txq->skbs[txq->q.read_ptr]; + + /* can be called from irqs-disabled context */ + if (skb) { + dev_kfree_skb_any(skb); + txq->skbs[txq->q.read_ptr] = NULL; + } + } +} + +/** + * il3945_hw_build_tx_cmd_rate - Add rate portion to TX_CMD: + * +*/ +void +il3945_hw_build_tx_cmd_rate(struct il_priv *il, struct il_device_cmd *cmd, + struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, int sta_id) +{ + u16 hw_value = ieee80211_get_tx_rate(il->hw, info)->hw_value; + u16 rate_idx = min(hw_value & 0xffff, RATE_COUNT_3945 - 1); + u16 rate_mask; + int rate; + const u8 rts_retry_limit = 7; + u8 data_retry_limit; + __le32 tx_flags; + __le16 fc = hdr->frame_control; + struct il3945_tx_cmd *tx_cmd = (struct il3945_tx_cmd *)cmd->cmd.payload; + + rate = il3945_rates[rate_idx].plcp; + tx_flags = tx_cmd->tx_flags; + + /* We need to figure out how to get the sta->supp_rates while + * in this running context */ + rate_mask = RATES_MASK_3945; + + /* Set retry limit on DATA packets and Probe Responses */ + if (ieee80211_is_probe_resp(fc)) + data_retry_limit = 3; + else + data_retry_limit = IL_DEFAULT_TX_RETRY; + tx_cmd->data_retry_limit = data_retry_limit; + /* Set retry limit on RTS packets */ + tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit); + + tx_cmd->rate = rate; + tx_cmd->tx_flags = tx_flags; + + /* OFDM */ + tx_cmd->supp_rates[0] = + ((rate_mask & IL_OFDM_RATES_MASK) >> IL_FIRST_OFDM_RATE) & 0xFF; + + /* CCK */ + tx_cmd->supp_rates[1] = (rate_mask & 0xF); + + D_RATE("Tx sta id: %d, rate: %d (plcp), flags: 0x%4X " + "cck/ofdm mask: 0x%x/0x%x\n", sta_id, tx_cmd->rate, + le32_to_cpu(tx_cmd->tx_flags), tx_cmd->supp_rates[1], + tx_cmd->supp_rates[0]); +} + +static u8 +il3945_sync_sta(struct il_priv *il, int sta_id, u16 tx_rate) +{ + unsigned long flags_spin; + struct il_station_entry *station; + + if (sta_id == IL_INVALID_STATION) + return IL_INVALID_STATION; + + spin_lock_irqsave(&il->sta_lock, flags_spin); + station = &il->stations[sta_id]; + + station->sta.sta.modify_mask = STA_MODIFY_TX_RATE_MSK; + station->sta.rate_n_flags = cpu_to_le16(tx_rate); + station->sta.mode = STA_CONTROL_MODIFY_MSK; + il_send_add_sta(il, &station->sta, CMD_ASYNC); + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + + D_RATE("SCALE sync station %d to rate %d\n", sta_id, tx_rate); + return sta_id; +} + +static void +il3945_set_pwr_vmain(struct il_priv *il) +{ +/* + * (for documentation purposes) + * to set power to V_AUX, do + + if (pci_pme_capable(il->pci_dev, PCI_D3cold)) { + il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VAUX, + ~APMG_PS_CTRL_MSK_PWR_SRC); + + _il_poll_bit(il, CSR_GPIO_IN, + CSR_GPIO_IN_VAL_VAUX_PWR_SRC, + CSR_GPIO_IN_BIT_AUX_POWER, 5000); + } + */ + + il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, + ~APMG_PS_CTRL_MSK_PWR_SRC); + + _il_poll_bit(il, CSR_GPIO_IN, CSR_GPIO_IN_VAL_VMAIN_PWR_SRC, + CSR_GPIO_IN_BIT_AUX_POWER, 5000); +} + +static int +il3945_rx_init(struct il_priv *il, struct il_rx_queue *rxq) +{ + il_wr(il, FH39_RCSR_RBD_BASE(0), rxq->bd_dma); + il_wr(il, FH39_RCSR_RPTR_ADDR(0), rxq->rb_stts_dma); + il_wr(il, FH39_RCSR_WPTR(0), 0); + il_wr(il, FH39_RCSR_CONFIG(0), + FH39_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN_ENABLE | + FH39_RCSR_RX_CONFIG_REG_VAL_RDRBD_EN_ENABLE | + FH39_RCSR_RX_CONFIG_REG_BIT_WR_STTS_EN | + FH39_RCSR_RX_CONFIG_REG_VAL_MAX_FRAG_SIZE_128 | (RX_QUEUE_SIZE_LOG + << + FH39_RCSR_RX_CONFIG_REG_POS_RBDC_SIZE) + | FH39_RCSR_RX_CONFIG_REG_VAL_IRQ_DEST_INT_HOST | (1 << + FH39_RCSR_RX_CONFIG_REG_POS_IRQ_RBTH) + | FH39_RCSR_RX_CONFIG_REG_VAL_MSG_MODE_FH); + + /* fake read to flush all prev I/O */ + il_rd(il, FH39_RSSR_CTRL); + + return 0; +} + +static int +il3945_tx_reset(struct il_priv *il) +{ + /* bypass mode */ + il_wr_prph(il, ALM_SCD_MODE_REG, 0x2); + + /* RA 0 is active */ + il_wr_prph(il, ALM_SCD_ARASTAT_REG, 0x01); + + /* all 6 fifo are active */ + il_wr_prph(il, ALM_SCD_TXFACT_REG, 0x3f); + + il_wr_prph(il, ALM_SCD_SBYP_MODE_1_REG, 0x010000); + il_wr_prph(il, ALM_SCD_SBYP_MODE_2_REG, 0x030002); + il_wr_prph(il, ALM_SCD_TXF4MF_REG, 0x000004); + il_wr_prph(il, ALM_SCD_TXF5MF_REG, 0x000005); + + il_wr(il, FH39_TSSR_CBB_BASE, il->_3945.shared_phys); + + il_wr(il, FH39_TSSR_MSG_CONFIG, + FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON | + FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON | + FH39_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B | + FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON | + FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON | + FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH | + FH39_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH); + + return 0; +} + +/** + * il3945_txq_ctx_reset - Reset TX queue context + * + * Destroys all DMA structures and initialize them again + */ +static int +il3945_txq_ctx_reset(struct il_priv *il) +{ + int rc, txq_id; + + il3945_hw_txq_ctx_free(il); + + /* allocate tx queue structure */ + rc = il_alloc_txq_mem(il); + if (rc) + return rc; + + /* Tx CMD queue */ + rc = il3945_tx_reset(il); + if (rc) + goto error; + + /* Tx queue(s) */ + for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) { + rc = il_tx_queue_init(il, txq_id); + if (rc) { + IL_ERR("Tx %d queue init failed\n", txq_id); + goto error; + } + } + + return rc; + +error: + il3945_hw_txq_ctx_free(il); + return rc; +} + +/* + * Start up 3945's basic functionality after it has been reset + * (e.g. after platform boot, or shutdown via il_apm_stop()) + * NOTE: This does not load uCode nor start the embedded processor + */ +static int +il3945_apm_init(struct il_priv *il) +{ + int ret = il_apm_init(il); + + /* Clear APMG (NIC's internal power management) interrupts */ + il_wr_prph(il, APMG_RTC_INT_MSK_REG, 0x0); + il_wr_prph(il, APMG_RTC_INT_STT_REG, 0xFFFFFFFF); + + /* Reset radio chip */ + il_set_bits_prph(il, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); + udelay(5); + il_clear_bits_prph(il, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); + + return ret; +} + +static void +il3945_nic_config(struct il_priv *il) +{ + struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; + unsigned long flags; + u8 rev_id = il->pci_dev->revision; + + spin_lock_irqsave(&il->lock, flags); + + /* Determine HW type */ + D_INFO("HW Revision ID = 0x%X\n", rev_id); + + if (rev_id & PCI_CFG_REV_ID_BIT_RTP) + D_INFO("RTP type\n"); + else if (rev_id & PCI_CFG_REV_ID_BIT_BASIC_SKU) { + D_INFO("3945 RADIO-MB type\n"); + il_set_bit(il, CSR_HW_IF_CONFIG_REG, + CSR39_HW_IF_CONFIG_REG_BIT_3945_MB); + } else { + D_INFO("3945 RADIO-MM type\n"); + il_set_bit(il, CSR_HW_IF_CONFIG_REG, + CSR39_HW_IF_CONFIG_REG_BIT_3945_MM); + } + + if (EEPROM_SKU_CAP_OP_MODE_MRC == eeprom->sku_cap) { + D_INFO("SKU OP mode is mrc\n"); + il_set_bit(il, CSR_HW_IF_CONFIG_REG, + CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC); + } else + D_INFO("SKU OP mode is basic\n"); + + if ((eeprom->board_revision & 0xF0) == 0xD0) { + D_INFO("3945ABG revision is 0x%X\n", eeprom->board_revision); + il_set_bit(il, CSR_HW_IF_CONFIG_REG, + CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE); + } else { + D_INFO("3945ABG revision is 0x%X\n", eeprom->board_revision); + il_clear_bit(il, CSR_HW_IF_CONFIG_REG, + CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE); + } + + if (eeprom->almgor_m_version <= 1) { + il_set_bit(il, CSR_HW_IF_CONFIG_REG, + CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A); + D_INFO("Card M type A version is 0x%X\n", + eeprom->almgor_m_version); + } else { + D_INFO("Card M type B version is 0x%X\n", + eeprom->almgor_m_version); + il_set_bit(il, CSR_HW_IF_CONFIG_REG, + CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B); + } + spin_unlock_irqrestore(&il->lock, flags); + + if (eeprom->sku_cap & EEPROM_SKU_CAP_SW_RF_KILL_ENABLE) + D_RF_KILL("SW RF KILL supported in EEPROM.\n"); + + if (eeprom->sku_cap & EEPROM_SKU_CAP_HW_RF_KILL_ENABLE) + D_RF_KILL("HW RF KILL supported in EEPROM.\n"); +} + +int +il3945_hw_nic_init(struct il_priv *il) +{ + int rc; + unsigned long flags; + struct il_rx_queue *rxq = &il->rxq; + + spin_lock_irqsave(&il->lock, flags); + il3945_apm_init(il); + spin_unlock_irqrestore(&il->lock, flags); + + il3945_set_pwr_vmain(il); + il3945_nic_config(il); + + /* Allocate the RX queue, or reset if it is already allocated */ + if (!rxq->bd) { + rc = il_rx_queue_alloc(il); + if (rc) { + IL_ERR("Unable to initialize Rx queue\n"); + return -ENOMEM; + } + } else + il3945_rx_queue_reset(il, rxq); + + il3945_rx_replenish(il); + + il3945_rx_init(il, rxq); + + /* Look at using this instead: + rxq->need_update = 1; + il_rx_queue_update_write_ptr(il, rxq); + */ + + il_wr(il, FH39_RCSR_WPTR(0), rxq->write & ~7); + + rc = il3945_txq_ctx_reset(il); + if (rc) + return rc; + + set_bit(S_INIT, &il->status); + + return 0; +} + +/** + * il3945_hw_txq_ctx_free - Free TXQ Context + * + * Destroy all TX DMA queues and structures + */ +void +il3945_hw_txq_ctx_free(struct il_priv *il) +{ + int txq_id; + + /* Tx queues */ + if (il->txq) + for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) + if (txq_id == IL39_CMD_QUEUE_NUM) + il_cmd_queue_free(il); + else + il_tx_queue_free(il, txq_id); + + /* free tx queue structure */ + il_free_txq_mem(il); +} + +void +il3945_hw_txq_ctx_stop(struct il_priv *il) +{ + int txq_id; + + /* stop SCD */ + _il_wr_prph(il, ALM_SCD_MODE_REG, 0); + _il_wr_prph(il, ALM_SCD_TXFACT_REG, 0); + + /* reset TFD queues */ + for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) { + _il_wr(il, FH39_TCSR_CONFIG(txq_id), 0x0); + _il_poll_bit(il, FH39_TSSR_TX_STATUS, + FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id), + FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id), + 1000); + } +} + +/** + * il3945_hw_reg_adjust_power_by_temp + * return idx delta into power gain settings table +*/ +static int +il3945_hw_reg_adjust_power_by_temp(int new_reading, int old_reading) +{ + return (new_reading - old_reading) * (-11) / 100; +} + +/** + * il3945_hw_reg_temp_out_of_range - Keep temperature in sane range + */ +static inline int +il3945_hw_reg_temp_out_of_range(int temperature) +{ + return (temperature < -260 || temperature > 25) ? 1 : 0; +} + +int +il3945_hw_get_temperature(struct il_priv *il) +{ + return _il_rd(il, CSR_UCODE_DRV_GP2); +} + +/** + * il3945_hw_reg_txpower_get_temperature + * get the current temperature by reading from NIC +*/ +static int +il3945_hw_reg_txpower_get_temperature(struct il_priv *il) +{ + struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; + int temperature; + + temperature = il3945_hw_get_temperature(il); + + /* driver's okay range is -260 to +25. + * human readable okay range is 0 to +285 */ + D_INFO("Temperature: %d\n", temperature + IL_TEMP_CONVERT); + + /* handle insane temp reading */ + if (il3945_hw_reg_temp_out_of_range(temperature)) { + IL_ERR("Error bad temperature value %d\n", temperature); + + /* if really really hot(?), + * substitute the 3rd band/group's temp measured at factory */ + if (il->last_temperature > 100) + temperature = eeprom->groups[2].temperature; + else /* else use most recent "sane" value from driver */ + temperature = il->last_temperature; + } + + return temperature; /* raw, not "human readable" */ +} + +/* Adjust Txpower only if temperature variance is greater than threshold. + * + * Both are lower than older versions' 9 degrees */ +#define IL_TEMPERATURE_LIMIT_TIMER 6 + +/** + * il3945_is_temp_calib_needed - determines if new calibration is needed + * + * records new temperature in tx_mgr->temperature. + * replaces tx_mgr->last_temperature *only* if calib needed + * (assumes caller will actually do the calibration!). */ +static int +il3945_is_temp_calib_needed(struct il_priv *il) +{ + int temp_diff; + + il->temperature = il3945_hw_reg_txpower_get_temperature(il); + temp_diff = il->temperature - il->last_temperature; + + /* get absolute value */ + if (temp_diff < 0) { + D_POWER("Getting cooler, delta %d,\n", temp_diff); + temp_diff = -temp_diff; + } else if (temp_diff == 0) + D_POWER("Same temp,\n"); + else + D_POWER("Getting warmer, delta %d,\n", temp_diff); + + /* if we don't need calibration, *don't* update last_temperature */ + if (temp_diff < IL_TEMPERATURE_LIMIT_TIMER) { + D_POWER("Timed thermal calib not needed\n"); + return 0; + } + + D_POWER("Timed thermal calib needed\n"); + + /* assume that caller will actually do calib ... + * update the "last temperature" value */ + il->last_temperature = il->temperature; + return 1; +} + +#define IL_MAX_GAIN_ENTRIES 78 +#define IL_CCK_FROM_OFDM_POWER_DIFF -5 +#define IL_CCK_FROM_OFDM_IDX_DIFF (10) + +/* radio and DSP power table, each step is 1/2 dB. + * 1st number is for RF analog gain, 2nd number is for DSP pre-DAC gain. */ +static struct il3945_tx_power power_gain_table[2][IL_MAX_GAIN_ENTRIES] = { + { + {251, 127}, /* 2.4 GHz, highest power */ + {251, 127}, + {251, 127}, + {251, 127}, + {251, 125}, + {251, 110}, + {251, 105}, + {251, 98}, + {187, 125}, + {187, 115}, + {187, 108}, + {187, 99}, + {243, 119}, + {243, 111}, + {243, 105}, + {243, 97}, + {243, 92}, + {211, 106}, + {211, 100}, + {179, 120}, + {179, 113}, + {179, 107}, + {147, 125}, + {147, 119}, + {147, 112}, + {147, 106}, + {147, 101}, + {147, 97}, + {147, 91}, + {115, 107}, + {235, 121}, + {235, 115}, + {235, 109}, + {203, 127}, + {203, 121}, + {203, 115}, + {203, 108}, + {203, 102}, + {203, 96}, + {203, 92}, + {171, 110}, + {171, 104}, + {171, 98}, + {139, 116}, + {227, 125}, + {227, 119}, + {227, 113}, + {227, 107}, + {227, 101}, + {227, 96}, + {195, 113}, + {195, 106}, + {195, 102}, + {195, 95}, + {163, 113}, + {163, 106}, + {163, 102}, + {163, 95}, + {131, 113}, + {131, 106}, + {131, 102}, + {131, 95}, + {99, 113}, + {99, 106}, + {99, 102}, + {99, 95}, + {67, 113}, + {67, 106}, + {67, 102}, + {67, 95}, + {35, 113}, + {35, 106}, + {35, 102}, + {35, 95}, + {3, 113}, + {3, 106}, + {3, 102}, + {3, 95} /* 2.4 GHz, lowest power */ + }, + { + {251, 127}, /* 5.x GHz, highest power */ + {251, 120}, + {251, 114}, + {219, 119}, + {219, 101}, + {187, 113}, + {187, 102}, + {155, 114}, + {155, 103}, + {123, 117}, + {123, 107}, + {123, 99}, + {123, 92}, + {91, 108}, + {59, 125}, + {59, 118}, + {59, 109}, + {59, 102}, + {59, 96}, + {59, 90}, + {27, 104}, + {27, 98}, + {27, 92}, + {115, 118}, + {115, 111}, + {115, 104}, + {83, 126}, + {83, 121}, + {83, 113}, + {83, 105}, + {83, 99}, + {51, 118}, + {51, 111}, + {51, 104}, + {51, 98}, + {19, 116}, + {19, 109}, + {19, 102}, + {19, 98}, + {19, 93}, + {171, 113}, + {171, 107}, + {171, 99}, + {139, 120}, + {139, 113}, + {139, 107}, + {139, 99}, + {107, 120}, + {107, 113}, + {107, 107}, + {107, 99}, + {75, 120}, + {75, 113}, + {75, 107}, + {75, 99}, + {43, 120}, + {43, 113}, + {43, 107}, + {43, 99}, + {11, 120}, + {11, 113}, + {11, 107}, + {11, 99}, + {131, 107}, + {131, 99}, + {99, 120}, + {99, 113}, + {99, 107}, + {99, 99}, + {67, 120}, + {67, 113}, + {67, 107}, + {67, 99}, + {35, 120}, + {35, 113}, + {35, 107}, + {35, 99}, + {3, 120} /* 5.x GHz, lowest power */ + } +}; + +static inline u8 +il3945_hw_reg_fix_power_idx(int idx) +{ + if (idx < 0) + return 0; + if (idx >= IL_MAX_GAIN_ENTRIES) + return IL_MAX_GAIN_ENTRIES - 1; + return (u8) idx; +} + +/* Kick off thermal recalibration check every 60 seconds */ +#define REG_RECALIB_PERIOD (60) + +/** + * il3945_hw_reg_set_scan_power - Set Tx power for scan probe requests + * + * Set (in our channel info database) the direct scan Tx power for 1 Mbit (CCK) + * or 6 Mbit (OFDM) rates. + */ +static void +il3945_hw_reg_set_scan_power(struct il_priv *il, u32 scan_tbl_idx, s32 rate_idx, + const s8 *clip_pwrs, + struct il_channel_info *ch_info, int band_idx) +{ + struct il3945_scan_power_info *scan_power_info; + s8 power; + u8 power_idx; + + scan_power_info = &ch_info->scan_pwr_info[scan_tbl_idx]; + + /* use this channel group's 6Mbit clipping/saturation pwr, + * but cap at regulatory scan power restriction (set during init + * based on eeprom channel data) for this channel. */ + power = min(ch_info->scan_power, clip_pwrs[RATE_6M_IDX_TBL]); + + power = min(power, il->tx_power_user_lmt); + scan_power_info->requested_power = power; + + /* find difference between new scan *power* and current "normal" + * Tx *power* for 6Mb. Use this difference (x2) to adjust the + * current "normal" temperature-compensated Tx power *idx* for + * this rate (1Mb or 6Mb) to yield new temp-compensated scan power + * *idx*. */ + power_idx = + ch_info->power_info[rate_idx].power_table_idx - (power - + ch_info-> + power_info + [RATE_6M_IDX_TBL]. + requested_power) * + 2; + + /* store reference idx that we use when adjusting *all* scan + * powers. So we can accommodate user (all channel) or spectrum + * management (single channel) power changes "between" temperature + * feedback compensation procedures. + * don't force fit this reference idx into gain table; it may be a + * negative number. This will help avoid errors when we're at + * the lower bounds (highest gains, for warmest temperatures) + * of the table. */ + + /* don't exceed table bounds for "real" setting */ + power_idx = il3945_hw_reg_fix_power_idx(power_idx); + + scan_power_info->power_table_idx = power_idx; + scan_power_info->tpc.tx_gain = + power_gain_table[band_idx][power_idx].tx_gain; + scan_power_info->tpc.dsp_atten = + power_gain_table[band_idx][power_idx].dsp_atten; +} + +/** + * il3945_send_tx_power - fill in Tx Power command with gain settings + * + * Configures power settings for all rates for the current channel, + * using values from channel info struct, and send to NIC + */ +static int +il3945_send_tx_power(struct il_priv *il) +{ + int rate_idx, i; + const struct il_channel_info *ch_info = NULL; + struct il3945_txpowertable_cmd txpower = { + .channel = il->active.channel, + }; + u16 chan; + + if (WARN_ONCE + (test_bit(S_SCAN_HW, &il->status), + "TX Power requested while scanning!\n")) + return -EAGAIN; + + chan = le16_to_cpu(il->active.channel); + + txpower.band = (il->band == IEEE80211_BAND_5GHZ) ? 0 : 1; + ch_info = il_get_channel_info(il, il->band, chan); + if (!ch_info) { + IL_ERR("Failed to get channel info for channel %d [%d]\n", chan, + il->band); + return -EINVAL; + } + + if (!il_is_channel_valid(ch_info)) { + D_POWER("Not calling TX_PWR_TBL_CMD on " "non-Tx channel.\n"); + return 0; + } + + /* fill cmd with power settings for all rates for current channel */ + /* Fill OFDM rate */ + for (rate_idx = IL_FIRST_OFDM_RATE, i = 0; + rate_idx <= IL39_LAST_OFDM_RATE; rate_idx++, i++) { + + txpower.power[i].tpc = ch_info->power_info[i].tpc; + txpower.power[i].rate = il3945_rates[rate_idx].plcp; + + D_POWER("ch %d:%d rf %d dsp %3d rate code 0x%02x\n", + le16_to_cpu(txpower.channel), txpower.band, + txpower.power[i].tpc.tx_gain, + txpower.power[i].tpc.dsp_atten, txpower.power[i].rate); + } + /* Fill CCK rates */ + for (rate_idx = IL_FIRST_CCK_RATE; rate_idx <= IL_LAST_CCK_RATE; + rate_idx++, i++) { + txpower.power[i].tpc = ch_info->power_info[i].tpc; + txpower.power[i].rate = il3945_rates[rate_idx].plcp; + + D_POWER("ch %d:%d rf %d dsp %3d rate code 0x%02x\n", + le16_to_cpu(txpower.channel), txpower.band, + txpower.power[i].tpc.tx_gain, + txpower.power[i].tpc.dsp_atten, txpower.power[i].rate); + } + + return il_send_cmd_pdu(il, C_TX_PWR_TBL, + sizeof(struct il3945_txpowertable_cmd), + &txpower); + +} + +/** + * il3945_hw_reg_set_new_power - Configures power tables at new levels + * @ch_info: Channel to update. Uses power_info.requested_power. + * + * Replace requested_power and base_power_idx ch_info fields for + * one channel. + * + * Called if user or spectrum management changes power preferences. + * Takes into account h/w and modulation limitations (clip power). + * + * This does *not* send anything to NIC, just sets up ch_info for one channel. + * + * NOTE: reg_compensate_for_temperature_dif() *must* be run after this to + * properly fill out the scan powers, and actual h/w gain settings, + * and send changes to NIC + */ +static int +il3945_hw_reg_set_new_power(struct il_priv *il, struct il_channel_info *ch_info) +{ + struct il3945_channel_power_info *power_info; + int power_changed = 0; + int i; + const s8 *clip_pwrs; + int power; + + /* Get this chnlgrp's rate-to-max/clip-powers table */ + clip_pwrs = il->_3945.clip_groups[ch_info->group_idx].clip_powers; + + /* Get this channel's rate-to-current-power settings table */ + power_info = ch_info->power_info; + + /* update OFDM Txpower settings */ + for (i = RATE_6M_IDX_TBL; i <= RATE_54M_IDX_TBL; i++, ++power_info) { + int delta_idx; + + /* limit new power to be no more than h/w capability */ + power = min(ch_info->curr_txpow, clip_pwrs[i]); + if (power == power_info->requested_power) + continue; + + /* find difference between old and new requested powers, + * update base (non-temp-compensated) power idx */ + delta_idx = (power - power_info->requested_power) * 2; + power_info->base_power_idx -= delta_idx; + + /* save new requested power value */ + power_info->requested_power = power; + + power_changed = 1; + } + + /* update CCK Txpower settings, based on OFDM 12M setting ... + * ... all CCK power settings for a given channel are the *same*. */ + if (power_changed) { + power = + ch_info->power_info[RATE_12M_IDX_TBL].requested_power + + IL_CCK_FROM_OFDM_POWER_DIFF; + + /* do all CCK rates' il3945_channel_power_info structures */ + for (i = RATE_1M_IDX_TBL; i <= RATE_11M_IDX_TBL; i++) { + power_info->requested_power = power; + power_info->base_power_idx = + ch_info->power_info[RATE_12M_IDX_TBL]. + base_power_idx + IL_CCK_FROM_OFDM_IDX_DIFF; + ++power_info; + } + } + + return 0; +} + +/** + * il3945_hw_reg_get_ch_txpower_limit - returns new power limit for channel + * + * NOTE: Returned power limit may be less (but not more) than requested, + * based strictly on regulatory (eeprom and spectrum mgt) limitations + * (no consideration for h/w clipping limitations). + */ +static int +il3945_hw_reg_get_ch_txpower_limit(struct il_channel_info *ch_info) +{ + s8 max_power; + +#if 0 + /* if we're using TGd limits, use lower of TGd or EEPROM */ + if (ch_info->tgd_data.max_power != 0) + max_power = + min(ch_info->tgd_data.max_power, + ch_info->eeprom.max_power_avg); + + /* else just use EEPROM limits */ + else +#endif + max_power = ch_info->eeprom.max_power_avg; + + return min(max_power, ch_info->max_power_avg); +} + +/** + * il3945_hw_reg_comp_txpower_temp - Compensate for temperature + * + * Compensate txpower settings of *all* channels for temperature. + * This only accounts for the difference between current temperature + * and the factory calibration temperatures, and bases the new settings + * on the channel's base_power_idx. + * + * If RxOn is "associated", this sends the new Txpower to NIC! + */ +static int +il3945_hw_reg_comp_txpower_temp(struct il_priv *il) +{ + struct il_channel_info *ch_info = NULL; + struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; + int delta_idx; + const s8 *clip_pwrs; /* array of h/w max power levels for each rate */ + u8 a_band; + u8 rate_idx; + u8 scan_tbl_idx; + u8 i; + int ref_temp; + int temperature = il->temperature; + + if (il->disable_tx_power_cal || test_bit(S_SCANNING, &il->status)) { + /* do not perform tx power calibration */ + return 0; + } + /* set up new Tx power info for each and every channel, 2.4 and 5.x */ + for (i = 0; i < il->channel_count; i++) { + ch_info = &il->channel_info[i]; + a_band = il_is_channel_a_band(ch_info); + + /* Get this chnlgrp's factory calibration temperature */ + ref_temp = (s16) eeprom->groups[ch_info->group_idx].temperature; + + /* get power idx adjustment based on current and factory + * temps */ + delta_idx = + il3945_hw_reg_adjust_power_by_temp(temperature, ref_temp); + + /* set tx power value for all rates, OFDM and CCK */ + for (rate_idx = 0; rate_idx < RATE_COUNT_3945; rate_idx++) { + int power_idx = + ch_info->power_info[rate_idx].base_power_idx; + + /* temperature compensate */ + power_idx += delta_idx; + + /* stay within table range */ + power_idx = il3945_hw_reg_fix_power_idx(power_idx); + ch_info->power_info[rate_idx].power_table_idx = + (u8) power_idx; + ch_info->power_info[rate_idx].tpc = + power_gain_table[a_band][power_idx]; + } + + /* Get this chnlgrp's rate-to-max/clip-powers table */ + clip_pwrs = + il->_3945.clip_groups[ch_info->group_idx].clip_powers; + + /* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */ + for (scan_tbl_idx = 0; scan_tbl_idx < IL_NUM_SCAN_RATES; + scan_tbl_idx++) { + s32 actual_idx = + (scan_tbl_idx == + 0) ? RATE_1M_IDX_TBL : RATE_6M_IDX_TBL; + il3945_hw_reg_set_scan_power(il, scan_tbl_idx, + actual_idx, clip_pwrs, + ch_info, a_band); + } + } + + /* send Txpower command for current channel to ucode */ + return il->ops->send_tx_power(il); +} + +int +il3945_hw_reg_set_txpower(struct il_priv *il, s8 power) +{ + struct il_channel_info *ch_info; + s8 max_power; + u8 a_band; + u8 i; + + if (il->tx_power_user_lmt == power) { + D_POWER("Requested Tx power same as current " "limit: %ddBm.\n", + power); + return 0; + } + + D_POWER("Setting upper limit clamp to %ddBm.\n", power); + il->tx_power_user_lmt = power; + + /* set up new Tx powers for each and every channel, 2.4 and 5.x */ + + for (i = 0; i < il->channel_count; i++) { + ch_info = &il->channel_info[i]; + a_band = il_is_channel_a_band(ch_info); + + /* find minimum power of all user and regulatory constraints + * (does not consider h/w clipping limitations) */ + max_power = il3945_hw_reg_get_ch_txpower_limit(ch_info); + max_power = min(power, max_power); + if (max_power != ch_info->curr_txpow) { + ch_info->curr_txpow = max_power; + + /* this considers the h/w clipping limitations */ + il3945_hw_reg_set_new_power(il, ch_info); + } + } + + /* update txpower settings for all channels, + * send to NIC if associated. */ + il3945_is_temp_calib_needed(il); + il3945_hw_reg_comp_txpower_temp(il); + + return 0; +} + +static int +il3945_send_rxon_assoc(struct il_priv *il) +{ + int rc = 0; + struct il_rx_pkt *pkt; + struct il3945_rxon_assoc_cmd rxon_assoc; + struct il_host_cmd cmd = { + .id = C_RXON_ASSOC, + .len = sizeof(rxon_assoc), + .flags = CMD_WANT_SKB, + .data = &rxon_assoc, + }; + const struct il_rxon_cmd *rxon1 = &il->staging; + const struct il_rxon_cmd *rxon2 = &il->active; + + if (rxon1->flags == rxon2->flags && + rxon1->filter_flags == rxon2->filter_flags && + rxon1->cck_basic_rates == rxon2->cck_basic_rates && + rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates) { + D_INFO("Using current RXON_ASSOC. Not resending.\n"); + return 0; + } + + rxon_assoc.flags = il->staging.flags; + rxon_assoc.filter_flags = il->staging.filter_flags; + rxon_assoc.ofdm_basic_rates = il->staging.ofdm_basic_rates; + rxon_assoc.cck_basic_rates = il->staging.cck_basic_rates; + rxon_assoc.reserved = 0; + + rc = il_send_cmd_sync(il, &cmd); + if (rc) + return rc; + + pkt = (struct il_rx_pkt *)cmd.reply_page; + if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { + IL_ERR("Bad return from C_RXON_ASSOC command\n"); + rc = -EIO; + } + + il_free_pages(il, cmd.reply_page); + + return rc; +} + +/** + * il3945_commit_rxon - commit staging_rxon to hardware + * + * The RXON command in staging_rxon is committed to the hardware and + * the active_rxon structure is updated with the new data. This + * function correctly transitions out of the RXON_ASSOC_MSK state if + * a HW tune is required based on the RXON structure changes. + */ +int +il3945_commit_rxon(struct il_priv *il) +{ + /* cast away the const for active_rxon in this function */ + struct il3945_rxon_cmd *active_rxon = (void *)&il->active; + struct il3945_rxon_cmd *staging_rxon = (void *)&il->staging; + int rc = 0; + bool new_assoc = !!(staging_rxon->filter_flags & RXON_FILTER_ASSOC_MSK); + + if (test_bit(S_EXIT_PENDING, &il->status)) + return -EINVAL; + + if (!il_is_alive(il)) + return -1; + + /* always get timestamp with Rx frame */ + staging_rxon->flags |= RXON_FLG_TSF2HOST_MSK; + + /* select antenna */ + staging_rxon->flags &= ~(RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_SEL_MSK); + staging_rxon->flags |= il3945_get_antenna_flags(il); + + rc = il_check_rxon_cmd(il); + if (rc) { + IL_ERR("Invalid RXON configuration. Not committing.\n"); + return -EINVAL; + } + + /* If we don't need to send a full RXON, we can use + * il3945_rxon_assoc_cmd which is used to reconfigure filter + * and other flags for the current radio configuration. */ + if (!il_full_rxon_required(il)) { + rc = il_send_rxon_assoc(il); + if (rc) { + IL_ERR("Error setting RXON_ASSOC " + "configuration (%d).\n", rc); + return rc; + } + + memcpy(active_rxon, staging_rxon, sizeof(*active_rxon)); + /* + * We do not commit tx power settings while channel changing, + * do it now if tx power changed. + */ + il_set_tx_power(il, il->tx_power_next, false); + return 0; + } + + /* If we are currently associated and the new config requires + * an RXON_ASSOC and the new config wants the associated mask enabled, + * we must clear the associated from the active configuration + * before we apply the new config */ + if (il_is_associated(il) && new_assoc) { + D_INFO("Toggling associated bit on current RXON\n"); + active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + + /* + * reserved4 and 5 could have been filled by the iwlcore code. + * Let's clear them before pushing to the 3945. + */ + active_rxon->reserved4 = 0; + active_rxon->reserved5 = 0; + rc = il_send_cmd_pdu(il, C_RXON, sizeof(struct il3945_rxon_cmd), + &il->active); + + /* If the mask clearing failed then we set + * active_rxon back to what it was previously */ + if (rc) { + active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK; + IL_ERR("Error clearing ASSOC_MSK on current " + "configuration (%d).\n", rc); + return rc; + } + il_clear_ucode_stations(il); + il_restore_stations(il); + } + + D_INFO("Sending RXON\n" "* with%s RXON_FILTER_ASSOC_MSK\n" + "* channel = %d\n" "* bssid = %pM\n", (new_assoc ? "" : "out"), + le16_to_cpu(staging_rxon->channel), staging_rxon->bssid_addr); + + /* + * reserved4 and 5 could have been filled by the iwlcore code. + * Let's clear them before pushing to the 3945. + */ + staging_rxon->reserved4 = 0; + staging_rxon->reserved5 = 0; + + il_set_rxon_hwcrypto(il, !il3945_mod_params.sw_crypto); + + /* Apply the new configuration */ + rc = il_send_cmd_pdu(il, C_RXON, sizeof(struct il3945_rxon_cmd), + staging_rxon); + if (rc) { + IL_ERR("Error setting new configuration (%d).\n", rc); + return rc; + } + + memcpy(active_rxon, staging_rxon, sizeof(*active_rxon)); + + if (!new_assoc) { + il_clear_ucode_stations(il); + il_restore_stations(il); + } + + /* If we issue a new RXON command which required a tune then we must + * send a new TXPOWER command or we won't be able to Tx any frames */ + rc = il_set_tx_power(il, il->tx_power_next, true); + if (rc) { + IL_ERR("Error setting Tx power (%d).\n", rc); + return rc; + } + + /* Init the hardware's rate fallback order based on the band */ + rc = il3945_init_hw_rate_table(il); + if (rc) { + IL_ERR("Error setting HW rate table: %02X\n", rc); + return -EIO; + } + + return 0; +} + +/** + * il3945_reg_txpower_periodic - called when time to check our temperature. + * + * -- reset periodic timer + * -- see if temp has changed enough to warrant re-calibration ... if so: + * -- correct coeffs for temp (can reset temp timer) + * -- save this temp as "last", + * -- send new set of gain settings to NIC + * NOTE: This should continue working, even when we're not associated, + * so we can keep our internal table of scan powers current. */ +void +il3945_reg_txpower_periodic(struct il_priv *il) +{ + /* This will kick in the "brute force" + * il3945_hw_reg_comp_txpower_temp() below */ + if (!il3945_is_temp_calib_needed(il)) + goto reschedule; + + /* Set up a new set of temp-adjusted TxPowers, send to NIC. + * This is based *only* on current temperature, + * ignoring any previous power measurements */ + il3945_hw_reg_comp_txpower_temp(il); + +reschedule: + queue_delayed_work(il->workqueue, &il->_3945.thermal_periodic, + REG_RECALIB_PERIOD * HZ); +} + +static void +il3945_bg_reg_txpower_periodic(struct work_struct *work) +{ + struct il_priv *il = container_of(work, struct il_priv, + _3945.thermal_periodic.work); + + mutex_lock(&il->mutex); + if (test_bit(S_EXIT_PENDING, &il->status) || il->txq == NULL) + goto out; + + il3945_reg_txpower_periodic(il); +out: + mutex_unlock(&il->mutex); +} + +/** + * il3945_hw_reg_get_ch_grp_idx - find the channel-group idx (0-4) for channel. + * + * This function is used when initializing channel-info structs. + * + * NOTE: These channel groups do *NOT* match the bands above! + * These channel groups are based on factory-tested channels; + * on A-band, EEPROM's "group frequency" entries represent the top + * channel in each group 1-4. Group 5 All B/G channels are in group 0. + */ +static u16 +il3945_hw_reg_get_ch_grp_idx(struct il_priv *il, + const struct il_channel_info *ch_info) +{ + struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; + struct il3945_eeprom_txpower_group *ch_grp = &eeprom->groups[0]; + u8 group; + u16 group_idx = 0; /* based on factory calib frequencies */ + u8 grp_channel; + + /* Find the group idx for the channel ... don't use idx 1(?) */ + if (il_is_channel_a_band(ch_info)) { + for (group = 1; group < 5; group++) { + grp_channel = ch_grp[group].group_channel; + if (ch_info->channel <= grp_channel) { + group_idx = group; + break; + } + } + /* group 4 has a few channels *above* its factory cal freq */ + if (group == 5) + group_idx = 4; + } else + group_idx = 0; /* 2.4 GHz, group 0 */ + + D_POWER("Chnl %d mapped to grp %d\n", ch_info->channel, group_idx); + return group_idx; +} + +/** + * il3945_hw_reg_get_matched_power_idx - Interpolate to get nominal idx + * + * Interpolate to get nominal (i.e. at factory calibration temperature) idx + * into radio/DSP gain settings table for requested power. + */ +static int +il3945_hw_reg_get_matched_power_idx(struct il_priv *il, s8 requested_power, + s32 setting_idx, s32 *new_idx) +{ + const struct il3945_eeprom_txpower_group *chnl_grp = NULL; + struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; + s32 idx0, idx1; + s32 power = 2 * requested_power; + s32 i; + const struct il3945_eeprom_txpower_sample *samples; + s32 gains0, gains1; + s32 res; + s32 denominator; + + chnl_grp = &eeprom->groups[setting_idx]; + samples = chnl_grp->samples; + for (i = 0; i < 5; i++) { + if (power == samples[i].power) { + *new_idx = samples[i].gain_idx; + return 0; + } + } + + if (power > samples[1].power) { + idx0 = 0; + idx1 = 1; + } else if (power > samples[2].power) { + idx0 = 1; + idx1 = 2; + } else if (power > samples[3].power) { + idx0 = 2; + idx1 = 3; + } else { + idx0 = 3; + idx1 = 4; + } + + denominator = (s32) samples[idx1].power - (s32) samples[idx0].power; + if (denominator == 0) + return -EINVAL; + gains0 = (s32) samples[idx0].gain_idx * (1 << 19); + gains1 = (s32) samples[idx1].gain_idx * (1 << 19); + res = + gains0 + (gains1 - gains0) * ((s32) power - + (s32) samples[idx0].power) / + denominator + (1 << 18); + *new_idx = res >> 19; + return 0; +} + +static void +il3945_hw_reg_init_channel_groups(struct il_priv *il) +{ + u32 i; + s32 rate_idx; + struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; + const struct il3945_eeprom_txpower_group *group; + + D_POWER("Initializing factory calib info from EEPROM\n"); + + for (i = 0; i < IL_NUM_TX_CALIB_GROUPS; i++) { + s8 *clip_pwrs; /* table of power levels for each rate */ + s8 satur_pwr; /* saturation power for each chnl group */ + group = &eeprom->groups[i]; + + /* sanity check on factory saturation power value */ + if (group->saturation_power < 40) { + IL_WARN("Error: saturation power is %d, " + "less than minimum expected 40\n", + group->saturation_power); + return; + } + + /* + * Derive requested power levels for each rate, based on + * hardware capabilities (saturation power for band). + * Basic value is 3dB down from saturation, with further + * power reductions for highest 3 data rates. These + * backoffs provide headroom for high rate modulation + * power peaks, without too much distortion (clipping). + */ + /* we'll fill in this array with h/w max power levels */ + clip_pwrs = (s8 *) il->_3945.clip_groups[i].clip_powers; + + /* divide factory saturation power by 2 to find -3dB level */ + satur_pwr = (s8) (group->saturation_power >> 1); + + /* fill in channel group's nominal powers for each rate */ + for (rate_idx = 0; rate_idx < RATE_COUNT_3945; + rate_idx++, clip_pwrs++) { + switch (rate_idx) { + case RATE_36M_IDX_TBL: + if (i == 0) /* B/G */ + *clip_pwrs = satur_pwr; + else /* A */ + *clip_pwrs = satur_pwr - 5; + break; + case RATE_48M_IDX_TBL: + if (i == 0) + *clip_pwrs = satur_pwr - 7; + else + *clip_pwrs = satur_pwr - 10; + break; + case RATE_54M_IDX_TBL: + if (i == 0) + *clip_pwrs = satur_pwr - 9; + else + *clip_pwrs = satur_pwr - 12; + break; + default: + *clip_pwrs = satur_pwr; + break; + } + } + } +} + +/** + * il3945_txpower_set_from_eeprom - Set channel power info based on EEPROM + * + * Second pass (during init) to set up il->channel_info + * + * Set up Tx-power settings in our channel info database for each VALID + * (for this geo/SKU) channel, at all Tx data rates, based on eeprom values + * and current temperature. + * + * Since this is based on current temperature (at init time), these values may + * not be valid for very long, but it gives us a starting/default point, + * and allows us to active (i.e. using Tx) scan. + * + * This does *not* write values to NIC, just sets up our internal table. + */ +int +il3945_txpower_set_from_eeprom(struct il_priv *il) +{ + struct il_channel_info *ch_info = NULL; + struct il3945_channel_power_info *pwr_info; + struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; + int delta_idx; + u8 rate_idx; + u8 scan_tbl_idx; + const s8 *clip_pwrs; /* array of power levels for each rate */ + u8 gain, dsp_atten; + s8 power; + u8 pwr_idx, base_pwr_idx, a_band; + u8 i; + int temperature; + + /* save temperature reference, + * so we can determine next time to calibrate */ + temperature = il3945_hw_reg_txpower_get_temperature(il); + il->last_temperature = temperature; + + il3945_hw_reg_init_channel_groups(il); + + /* initialize Tx power info for each and every channel, 2.4 and 5.x */ + for (i = 0, ch_info = il->channel_info; i < il->channel_count; + i++, ch_info++) { + a_band = il_is_channel_a_band(ch_info); + if (!il_is_channel_valid(ch_info)) + continue; + + /* find this channel's channel group (*not* "band") idx */ + ch_info->group_idx = il3945_hw_reg_get_ch_grp_idx(il, ch_info); + + /* Get this chnlgrp's rate->max/clip-powers table */ + clip_pwrs = + il->_3945.clip_groups[ch_info->group_idx].clip_powers; + + /* calculate power idx *adjustment* value according to + * diff between current temperature and factory temperature */ + delta_idx = + il3945_hw_reg_adjust_power_by_temp(temperature, + eeprom->groups[ch_info-> + group_idx]. + temperature); + + D_POWER("Delta idx for channel %d: %d [%d]\n", ch_info->channel, + delta_idx, temperature + IL_TEMP_CONVERT); + + /* set tx power value for all OFDM rates */ + for (rate_idx = 0; rate_idx < IL_OFDM_RATES; rate_idx++) { + s32 uninitialized_var(power_idx); + int rc; + + /* use channel group's clip-power table, + * but don't exceed channel's max power */ + s8 pwr = min(ch_info->max_power_avg, + clip_pwrs[rate_idx]); + + pwr_info = &ch_info->power_info[rate_idx]; + + /* get base (i.e. at factory-measured temperature) + * power table idx for this rate's power */ + rc = il3945_hw_reg_get_matched_power_idx(il, pwr, + ch_info-> + group_idx, + &power_idx); + if (rc) { + IL_ERR("Invalid power idx\n"); + return rc; + } + pwr_info->base_power_idx = (u8) power_idx; + + /* temperature compensate */ + power_idx += delta_idx; + + /* stay within range of gain table */ + power_idx = il3945_hw_reg_fix_power_idx(power_idx); + + /* fill 1 OFDM rate's il3945_channel_power_info struct */ + pwr_info->requested_power = pwr; + pwr_info->power_table_idx = (u8) power_idx; + pwr_info->tpc.tx_gain = + power_gain_table[a_band][power_idx].tx_gain; + pwr_info->tpc.dsp_atten = + power_gain_table[a_band][power_idx].dsp_atten; + } + + /* set tx power for CCK rates, based on OFDM 12 Mbit settings */ + pwr_info = &ch_info->power_info[RATE_12M_IDX_TBL]; + power = pwr_info->requested_power + IL_CCK_FROM_OFDM_POWER_DIFF; + pwr_idx = pwr_info->power_table_idx + IL_CCK_FROM_OFDM_IDX_DIFF; + base_pwr_idx = + pwr_info->base_power_idx + IL_CCK_FROM_OFDM_IDX_DIFF; + + /* stay within table range */ + pwr_idx = il3945_hw_reg_fix_power_idx(pwr_idx); + gain = power_gain_table[a_band][pwr_idx].tx_gain; + dsp_atten = power_gain_table[a_band][pwr_idx].dsp_atten; + + /* fill each CCK rate's il3945_channel_power_info structure + * NOTE: All CCK-rate Txpwrs are the same for a given chnl! + * NOTE: CCK rates start at end of OFDM rates! */ + for (rate_idx = 0; rate_idx < IL_CCK_RATES; rate_idx++) { + pwr_info = + &ch_info->power_info[rate_idx + IL_OFDM_RATES]; + pwr_info->requested_power = power; + pwr_info->power_table_idx = pwr_idx; + pwr_info->base_power_idx = base_pwr_idx; + pwr_info->tpc.tx_gain = gain; + pwr_info->tpc.dsp_atten = dsp_atten; + } + + /* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */ + for (scan_tbl_idx = 0; scan_tbl_idx < IL_NUM_SCAN_RATES; + scan_tbl_idx++) { + s32 actual_idx = + (scan_tbl_idx == + 0) ? RATE_1M_IDX_TBL : RATE_6M_IDX_TBL; + il3945_hw_reg_set_scan_power(il, scan_tbl_idx, + actual_idx, clip_pwrs, + ch_info, a_band); + } + } + + return 0; +} + +int +il3945_hw_rxq_stop(struct il_priv *il) +{ + int ret; + + _il_wr(il, FH39_RCSR_CONFIG(0), 0); + ret = _il_poll_bit(il, FH39_RSSR_STATUS, + FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, + FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, + 1000); + if (ret < 0) + IL_ERR("Can't stop Rx DMA.\n"); + + return 0; +} + +int +il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq) +{ + int txq_id = txq->q.id; + + struct il3945_shared *shared_data = il->_3945.shared_virt; + + shared_data->tx_base_ptr[txq_id] = cpu_to_le32((u32) txq->q.dma_addr); + + il_wr(il, FH39_CBCC_CTRL(txq_id), 0); + il_wr(il, FH39_CBCC_BASE(txq_id), 0); + + il_wr(il, FH39_TCSR_CONFIG(txq_id), + FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT | + FH39_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF | + FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD | + FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL | + FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE); + + /* fake read to flush all prev. writes */ + _il_rd(il, FH39_TSSR_CBB_BASE); + + return 0; +} + +/* + * HCMD utils + */ +static u16 +il3945_get_hcmd_size(u8 cmd_id, u16 len) +{ + switch (cmd_id) { + case C_RXON: + return sizeof(struct il3945_rxon_cmd); + case C_POWER_TBL: + return sizeof(struct il3945_powertable_cmd); + default: + return len; + } +} + +static u16 +il3945_build_addsta_hcmd(const struct il_addsta_cmd *cmd, u8 * data) +{ + struct il3945_addsta_cmd *addsta = (struct il3945_addsta_cmd *)data; + addsta->mode = cmd->mode; + memcpy(&addsta->sta, &cmd->sta, sizeof(struct sta_id_modify)); + memcpy(&addsta->key, &cmd->key, sizeof(struct il4965_keyinfo)); + addsta->station_flags = cmd->station_flags; + addsta->station_flags_msk = cmd->station_flags_msk; + addsta->tid_disable_tx = cpu_to_le16(0); + addsta->rate_n_flags = cmd->rate_n_flags; + addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid; + addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid; + addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn; + + return (u16) sizeof(struct il3945_addsta_cmd); +} + +static int +il3945_add_bssid_station(struct il_priv *il, const u8 * addr, u8 * sta_id_r) +{ + int ret; + u8 sta_id; + unsigned long flags; + + if (sta_id_r) + *sta_id_r = IL_INVALID_STATION; + + ret = il_add_station_common(il, addr, 0, NULL, &sta_id); + if (ret) { + IL_ERR("Unable to add station %pM\n", addr); + return ret; + } + + if (sta_id_r) + *sta_id_r = sta_id; + + spin_lock_irqsave(&il->sta_lock, flags); + il->stations[sta_id].used |= IL_STA_LOCAL; + spin_unlock_irqrestore(&il->sta_lock, flags); + + return 0; +} + +static int +il3945_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif, + bool add) +{ + struct il_vif_priv *vif_priv = (void *)vif->drv_priv; + int ret; + + if (add) { + ret = + il3945_add_bssid_station(il, vif->bss_conf.bssid, + &vif_priv->ibss_bssid_sta_id); + if (ret) + return ret; + + il3945_sync_sta(il, vif_priv->ibss_bssid_sta_id, + (il->band == + IEEE80211_BAND_5GHZ) ? RATE_6M_PLCP : + RATE_1M_PLCP); + il3945_rate_scale_init(il->hw, vif_priv->ibss_bssid_sta_id); + + return 0; + } + + return il_remove_station(il, vif_priv->ibss_bssid_sta_id, + vif->bss_conf.bssid); +} + +/** + * il3945_init_hw_rate_table - Initialize the hardware rate fallback table + */ +int +il3945_init_hw_rate_table(struct il_priv *il) +{ + int rc, i, idx, prev_idx; + struct il3945_rate_scaling_cmd rate_cmd = { + .reserved = {0, 0, 0}, + }; + struct il3945_rate_scaling_info *table = rate_cmd.table; + + for (i = 0; i < ARRAY_SIZE(il3945_rates); i++) { + idx = il3945_rates[i].table_rs_idx; + + table[idx].rate_n_flags = cpu_to_le16(il3945_rates[i].plcp); + table[idx].try_cnt = il->retry_rate; + prev_idx = il3945_get_prev_ieee_rate(i); + table[idx].next_rate_idx = il3945_rates[prev_idx].table_rs_idx; + } + + switch (il->band) { + case IEEE80211_BAND_5GHZ: + D_RATE("Select A mode rate scale\n"); + /* If one of the following CCK rates is used, + * have it fall back to the 6M OFDM rate */ + for (i = RATE_1M_IDX_TBL; i <= RATE_11M_IDX_TBL; i++) + table[i].next_rate_idx = + il3945_rates[IL_FIRST_OFDM_RATE].table_rs_idx; + + /* Don't fall back to CCK rates */ + table[RATE_12M_IDX_TBL].next_rate_idx = RATE_9M_IDX_TBL; + + /* Don't drop out of OFDM rates */ + table[RATE_6M_IDX_TBL].next_rate_idx = + il3945_rates[IL_FIRST_OFDM_RATE].table_rs_idx; + break; + + case IEEE80211_BAND_2GHZ: + D_RATE("Select B/G mode rate scale\n"); + /* If an OFDM rate is used, have it fall back to the + * 1M CCK rates */ + + if (!(il->_3945.sta_supp_rates & IL_OFDM_RATES_MASK) && + il_is_associated(il)) { + + idx = IL_FIRST_CCK_RATE; + for (i = RATE_6M_IDX_TBL; i <= RATE_54M_IDX_TBL; i++) + table[i].next_rate_idx = + il3945_rates[idx].table_rs_idx; + + idx = RATE_11M_IDX_TBL; + /* CCK shouldn't fall back to OFDM... */ + table[idx].next_rate_idx = RATE_5M_IDX_TBL; + } + break; + + default: + WARN_ON(1); + break; + } + + /* Update the rate scaling for control frame Tx */ + rate_cmd.table_id = 0; + rc = il_send_cmd_pdu(il, C_RATE_SCALE, sizeof(rate_cmd), &rate_cmd); + if (rc) + return rc; + + /* Update the rate scaling for data frame Tx */ + rate_cmd.table_id = 1; + return il_send_cmd_pdu(il, C_RATE_SCALE, sizeof(rate_cmd), &rate_cmd); +} + +/* Called when initializing driver */ +int +il3945_hw_set_hw_params(struct il_priv *il) +{ + memset((void *)&il->hw_params, 0, sizeof(struct il_hw_params)); + + il->_3945.shared_virt = + dma_alloc_coherent(&il->pci_dev->dev, sizeof(struct il3945_shared), + &il->_3945.shared_phys, GFP_KERNEL); + if (!il->_3945.shared_virt) + return -ENOMEM; + + il->hw_params.bcast_id = IL3945_BROADCAST_ID; + + /* Assign number of Usable TX queues */ + il->hw_params.max_txq_num = il->cfg->num_of_queues; + + il->hw_params.tfd_size = sizeof(struct il3945_tfd); + il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_3K); + il->hw_params.max_rxq_size = RX_QUEUE_SIZE; + il->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; + il->hw_params.max_stations = IL3945_STATION_COUNT; + + il->sta_key_max_num = STA_KEY_MAX_NUM; + + il->hw_params.rx_wrt_ptr_reg = FH39_RSCSR_CHNL0_WPTR; + il->hw_params.max_beacon_itrvl = IL39_MAX_UCODE_BEACON_INTERVAL; + il->hw_params.beacon_time_tsf_bits = IL3945_EXT_BEACON_TIME_POS; + + return 0; +} + +unsigned int +il3945_hw_get_beacon_cmd(struct il_priv *il, struct il3945_frame *frame, + u8 rate) +{ + struct il3945_tx_beacon_cmd *tx_beacon_cmd; + unsigned int frame_size; + + tx_beacon_cmd = (struct il3945_tx_beacon_cmd *)&frame->u; + memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); + + tx_beacon_cmd->tx.sta_id = il->hw_params.bcast_id; + tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + frame_size = + il3945_fill_beacon_frame(il, tx_beacon_cmd->frame, + sizeof(frame->u) - sizeof(*tx_beacon_cmd)); + + BUG_ON(frame_size > MAX_MPDU_SIZE); + tx_beacon_cmd->tx.len = cpu_to_le16((u16) frame_size); + + tx_beacon_cmd->tx.rate = rate; + tx_beacon_cmd->tx.tx_flags = + (TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK); + + /* supp_rates[0] == OFDM start at IL_FIRST_OFDM_RATE */ + tx_beacon_cmd->tx.supp_rates[0] = + (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; + + tx_beacon_cmd->tx.supp_rates[1] = (IL_CCK_BASIC_RATES_MASK & 0xF); + + return sizeof(struct il3945_tx_beacon_cmd) + frame_size; +} + +void +il3945_hw_handler_setup(struct il_priv *il) +{ + il->handlers[C_TX] = il3945_hdl_tx; + il->handlers[N_3945_RX] = il3945_hdl_rx; +} + +void +il3945_hw_setup_deferred_work(struct il_priv *il) +{ + INIT_DELAYED_WORK(&il->_3945.thermal_periodic, + il3945_bg_reg_txpower_periodic); +} + +void +il3945_hw_cancel_deferred_work(struct il_priv *il) +{ + cancel_delayed_work(&il->_3945.thermal_periodic); +} + +/* check contents of special bootstrap uCode SRAM */ +static int +il3945_verify_bsm(struct il_priv *il) +{ + __le32 *image = il->ucode_boot.v_addr; + u32 len = il->ucode_boot.len; + u32 reg; + u32 val; + + D_INFO("Begin verify bsm\n"); + + /* verify BSM SRAM contents */ + val = il_rd_prph(il, BSM_WR_DWCOUNT_REG); + for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; + reg += sizeof(u32), image++) { + val = il_rd_prph(il, reg); + if (val != le32_to_cpu(*image)) { + IL_ERR("BSM uCode verification failed at " + "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n", + BSM_SRAM_LOWER_BOUND, reg - BSM_SRAM_LOWER_BOUND, + len, val, le32_to_cpu(*image)); + return -EIO; + } + } + + D_INFO("BSM bootstrap uCode image OK\n"); + + return 0; +} + +/****************************************************************************** + * + * EEPROM related functions + * + ******************************************************************************/ + +/* + * Clear the OWNER_MSK, to establish driver (instead of uCode running on + * embedded controller) as EEPROM reader; each read is a series of pulses + * to/from the EEPROM chip, not a single event, so even reads could conflict + * if they weren't arbitrated by some ownership mechanism. Here, the driver + * simply claims ownership, which should be safe when this function is called + * (i.e. before loading uCode!). + */ +static int +il3945_eeprom_acquire_semaphore(struct il_priv *il) +{ + _il_clear_bit(il, CSR_EEPROM_GP, CSR_EEPROM_GP_IF_OWNER_MSK); + return 0; +} + +static void +il3945_eeprom_release_semaphore(struct il_priv *il) +{ + return; +} + + /** + * il3945_load_bsm - Load bootstrap instructions + * + * BSM operation: + * + * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program + * in special SRAM that does not power down during RFKILL. When powering back + * up after power-saving sleeps (or during initial uCode load), the BSM loads + * the bootstrap program into the on-board processor, and starts it. + * + * The bootstrap program loads (via DMA) instructions and data for a new + * program from host DRAM locations indicated by the host driver in the + * BSM_DRAM_* registers. Once the new program is loaded, it starts + * automatically. + * + * When initializing the NIC, the host driver points the BSM to the + * "initialize" uCode image. This uCode sets up some internal data, then + * notifies host via "initialize alive" that it is complete. + * + * The host then replaces the BSM_DRAM_* pointer values to point to the + * normal runtime uCode instructions and a backup uCode data cache buffer + * (filled initially with starting data values for the on-board processor), + * then triggers the "initialize" uCode to load and launch the runtime uCode, + * which begins normal operation. + * + * When doing a power-save shutdown, runtime uCode saves data SRAM into + * the backup data cache in DRAM before SRAM is powered down. + * + * When powering back up, the BSM loads the bootstrap program. This reloads + * the runtime uCode instructions and the backup data cache into SRAM, + * and re-launches the runtime uCode from where it left off. + */ +static int +il3945_load_bsm(struct il_priv *il) +{ + __le32 *image = il->ucode_boot.v_addr; + u32 len = il->ucode_boot.len; + dma_addr_t pinst; + dma_addr_t pdata; + u32 inst_len; + u32 data_len; + int rc; + int i; + u32 done; + u32 reg_offset; + + D_INFO("Begin load bsm\n"); + + /* make sure bootstrap program is no larger than BSM's SRAM size */ + if (len > IL39_MAX_BSM_SIZE) + return -EINVAL; + + /* Tell bootstrap uCode where to find the "Initialize" uCode + * in host DRAM ... host DRAM physical address bits 31:0 for 3945. + * NOTE: il3945_initialize_alive_start() will replace these values, + * after the "initialize" uCode has run, to point to + * runtime/protocol instructions and backup data cache. */ + pinst = il->ucode_init.p_addr; + pdata = il->ucode_init_data.p_addr; + inst_len = il->ucode_init.len; + data_len = il->ucode_init_data.len; + + il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); + il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); + il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); + il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); + + /* Fill BSM memory with bootstrap instructions */ + for (reg_offset = BSM_SRAM_LOWER_BOUND; + reg_offset < BSM_SRAM_LOWER_BOUND + len; + reg_offset += sizeof(u32), image++) + _il_wr_prph(il, reg_offset, le32_to_cpu(*image)); + + rc = il3945_verify_bsm(il); + if (rc) + return rc; + + /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ + il_wr_prph(il, BSM_WR_MEM_SRC_REG, 0x0); + il_wr_prph(il, BSM_WR_MEM_DST_REG, IL39_RTC_INST_LOWER_BOUND); + il_wr_prph(il, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); + + /* Load bootstrap code into instruction SRAM now, + * to prepare to load "initialize" uCode */ + il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); + + /* Wait for load of bootstrap uCode to finish */ + for (i = 0; i < 100; i++) { + done = il_rd_prph(il, BSM_WR_CTRL_REG); + if (!(done & BSM_WR_CTRL_REG_BIT_START)) + break; + udelay(10); + } + if (i < 100) + D_INFO("BSM write complete, poll %d iterations\n", i); + else { + IL_ERR("BSM write did not complete!\n"); + return -EIO; + } + + /* Enable future boot loads whenever power management unit triggers it + * (e.g. when powering back up after power-save shutdown) */ + il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); + + return 0; +} + +const struct il_ops il3945_ops = { + .txq_attach_buf_to_tfd = il3945_hw_txq_attach_buf_to_tfd, + .txq_free_tfd = il3945_hw_txq_free_tfd, + .txq_init = il3945_hw_tx_queue_init, + .load_ucode = il3945_load_bsm, + .dump_nic_error_log = il3945_dump_nic_error_log, + .apm_init = il3945_apm_init, + .send_tx_power = il3945_send_tx_power, + .is_valid_rtc_data_addr = il3945_hw_valid_rtc_data_addr, + .eeprom_acquire_semaphore = il3945_eeprom_acquire_semaphore, + .eeprom_release_semaphore = il3945_eeprom_release_semaphore, + + .rxon_assoc = il3945_send_rxon_assoc, + .commit_rxon = il3945_commit_rxon, + + .get_hcmd_size = il3945_get_hcmd_size, + .build_addsta_hcmd = il3945_build_addsta_hcmd, + .request_scan = il3945_request_scan, + .post_scan = il3945_post_scan, + + .post_associate = il3945_post_associate, + .config_ap = il3945_config_ap, + .manage_ibss_station = il3945_manage_ibss_station, + + .send_led_cmd = il3945_send_led_cmd, +}; + +static struct il_cfg il3945_bg_cfg = { + .name = "3945BG", + .fw_name_pre = IL3945_FW_PRE, + .ucode_api_max = IL3945_UCODE_API_MAX, + .ucode_api_min = IL3945_UCODE_API_MIN, + .sku = IL_SKU_G, + .eeprom_ver = EEPROM_3945_EEPROM_VERSION, + .mod_params = &il3945_mod_params, + .led_mode = IL_LED_BLINK, + + .eeprom_size = IL3945_EEPROM_IMG_SIZE, + .num_of_queues = IL39_NUM_QUEUES, + .pll_cfg_val = CSR39_ANA_PLL_CFG_VAL, + .set_l0s = false, + .use_bsm = true, + .led_compensation = 64, + .wd_timeout = IL_DEF_WD_TIMEOUT, + + .regulatory_bands = { + EEPROM_REGULATORY_BAND_1_CHANNELS, + EEPROM_REGULATORY_BAND_2_CHANNELS, + EEPROM_REGULATORY_BAND_3_CHANNELS, + EEPROM_REGULATORY_BAND_4_CHANNELS, + EEPROM_REGULATORY_BAND_5_CHANNELS, + EEPROM_REGULATORY_BAND_NO_HT40, + EEPROM_REGULATORY_BAND_NO_HT40, + }, +}; + +static struct il_cfg il3945_abg_cfg = { + .name = "3945ABG", + .fw_name_pre = IL3945_FW_PRE, + .ucode_api_max = IL3945_UCODE_API_MAX, + .ucode_api_min = IL3945_UCODE_API_MIN, + .sku = IL_SKU_A | IL_SKU_G, + .eeprom_ver = EEPROM_3945_EEPROM_VERSION, + .mod_params = &il3945_mod_params, + .led_mode = IL_LED_BLINK, + + .eeprom_size = IL3945_EEPROM_IMG_SIZE, + .num_of_queues = IL39_NUM_QUEUES, + .pll_cfg_val = CSR39_ANA_PLL_CFG_VAL, + .set_l0s = false, + .use_bsm = true, + .led_compensation = 64, + .wd_timeout = IL_DEF_WD_TIMEOUT, + + .regulatory_bands = { + EEPROM_REGULATORY_BAND_1_CHANNELS, + EEPROM_REGULATORY_BAND_2_CHANNELS, + EEPROM_REGULATORY_BAND_3_CHANNELS, + EEPROM_REGULATORY_BAND_4_CHANNELS, + EEPROM_REGULATORY_BAND_5_CHANNELS, + EEPROM_REGULATORY_BAND_NO_HT40, + EEPROM_REGULATORY_BAND_NO_HT40, + }, +}; + +const struct pci_device_id il3945_hw_card_ids[] = { + {IL_PCI_DEVICE(0x4222, 0x1005, il3945_bg_cfg)}, + {IL_PCI_DEVICE(0x4222, 0x1034, il3945_bg_cfg)}, + {IL_PCI_DEVICE(0x4222, 0x1044, il3945_bg_cfg)}, + {IL_PCI_DEVICE(0x4227, 0x1014, il3945_bg_cfg)}, + {IL_PCI_DEVICE(0x4222, PCI_ANY_ID, il3945_abg_cfg)}, + {IL_PCI_DEVICE(0x4227, PCI_ANY_ID, il3945_abg_cfg)}, + {0} +}; + +MODULE_DEVICE_TABLE(pci, il3945_hw_card_ids); diff --git a/drivers/net/wireless/intel/iwlegacy/3945.h b/drivers/net/wireless/intel/iwlegacy/3945.h new file mode 100644 index 000000000000..00030d43a194 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/3945.h @@ -0,0 +1,593 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __il_3945_h__ +#define __il_3945_h__ + +#include /* for struct pci_device_id */ +#include +#include + +/* Hardware specific file defines the PCI IDs table for that hardware module */ +extern const struct pci_device_id il3945_hw_card_ids[]; + +#include "common.h" + +extern const struct il_ops il3945_ops; + +/* Highest firmware API version supported */ +#define IL3945_UCODE_API_MAX 2 + +/* Lowest firmware API version supported */ +#define IL3945_UCODE_API_MIN 1 + +#define IL3945_FW_PRE "iwlwifi-3945-" +#define _IL3945_MODULE_FIRMWARE(api) IL3945_FW_PRE #api ".ucode" +#define IL3945_MODULE_FIRMWARE(api) _IL3945_MODULE_FIRMWARE(api) + +/* Default noise level to report when noise measurement is not available. + * This may be because we're: + * 1) Not associated (4965, no beacon stats being sent to driver) + * 2) Scanning (noise measurement does not apply to associated channel) + * 3) Receiving CCK (3945 delivers noise info only for OFDM frames) + * Use default noise value of -127 ... this is below the range of measurable + * Rx dBm for either 3945 or 4965, so it can indicate "unmeasurable" to user. + * Also, -127 works better than 0 when averaging frames with/without + * noise info (e.g. averaging might be done in app); measured dBm values are + * always negative ... using a negative value as the default keeps all + * averages within an s8's (used in some apps) range of negative values. */ +#define IL_NOISE_MEAS_NOT_AVAILABLE (-127) + +/* Module parameters accessible from iwl-*.c */ +extern struct il_mod_params il3945_mod_params; + +struct il3945_rate_scale_data { + u64 data; + s32 success_counter; + s32 success_ratio; + s32 counter; + s32 average_tpt; + unsigned long stamp; +}; + +struct il3945_rs_sta { + spinlock_t lock; + struct il_priv *il; + s32 *expected_tpt; + unsigned long last_partial_flush; + unsigned long last_flush; + u32 flush_time; + u32 last_tx_packets; + u32 tx_packets; + u8 tgg; + u8 flush_pending; + u8 start_rate; + struct timer_list rate_scale_flush; + struct il3945_rate_scale_data win[RATE_COUNT_3945]; +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *rs_sta_dbgfs_stats_table_file; +#endif + + /* used to be in sta_info */ + int last_txrate_idx; +}; + +/* + * The common struct MUST be first because it is shared between + * 3945 and 4965! + */ +struct il3945_sta_priv { + struct il_station_priv_common common; + struct il3945_rs_sta rs_sta; +}; + +enum il3945_antenna { + IL_ANTENNA_DIVERSITY, + IL_ANTENNA_MAIN, + IL_ANTENNA_AUX +}; + +/* + * RTS threshold here is total size [2347] minus 4 FCS bytes + * Per spec: + * a value of 0 means RTS on all data/management packets + * a value > max MSDU size means no RTS + * else RTS for data/management frames where MPDU is larger + * than RTS value. + */ +#define DEFAULT_RTS_THRESHOLD 2347U +#define MIN_RTS_THRESHOLD 0U +#define MAX_RTS_THRESHOLD 2347U +#define MAX_MSDU_SIZE 2304U +#define MAX_MPDU_SIZE 2346U +#define DEFAULT_BEACON_INTERVAL 100U +#define DEFAULT_SHORT_RETRY_LIMIT 7U +#define DEFAULT_LONG_RETRY_LIMIT 4U + +#define IL_TX_FIFO_AC0 0 +#define IL_TX_FIFO_AC1 1 +#define IL_TX_FIFO_AC2 2 +#define IL_TX_FIFO_AC3 3 +#define IL_TX_FIFO_HCCA_1 5 +#define IL_TX_FIFO_HCCA_2 6 +#define IL_TX_FIFO_NONE 7 + +#define IEEE80211_DATA_LEN 2304 +#define IEEE80211_4ADDR_LEN 30 +#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) +#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) + +struct il3945_frame { + union { + struct ieee80211_hdr frame; + struct il3945_tx_beacon_cmd beacon; + u8 raw[IEEE80211_FRAME_LEN]; + u8 cmd[360]; + } u; + struct list_head list; +}; + +#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 +#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 +#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 + +#define IL_SUPPORTED_RATES_IE_LEN 8 + +#define SCAN_INTERVAL 100 + +#define MAX_TID_COUNT 9 + +#define IL_INVALID_RATE 0xFF +#define IL_INVALID_VALUE -1 + +#define STA_PS_STATUS_WAKE 0 +#define STA_PS_STATUS_SLEEP 1 + +struct il3945_ibss_seq { + u8 mac[ETH_ALEN]; + u16 seq_num; + u16 frag_num; + unsigned long packet_time; + struct list_head list; +}; + +#define IL_RX_HDR(x) ((struct il3945_rx_frame_hdr *)(\ + x->u.rx_frame.stats.payload + \ + x->u.rx_frame.stats.phy_count)) +#define IL_RX_END(x) ((struct il3945_rx_frame_end *)(\ + IL_RX_HDR(x)->payload + \ + le16_to_cpu(IL_RX_HDR(x)->len))) +#define IL_RX_STATS(x) (&x->u.rx_frame.stats) +#define IL_RX_DATA(x) (IL_RX_HDR(x)->payload) + +/****************************************************************************** + * + * Functions implemented in iwl3945-base.c which are forward declared here + * for use by iwl-*.c + * + *****************************************************************************/ +int il3945_calc_db_from_ratio(int sig_ratio); +void il3945_rx_replenish(void *data); +void il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq); +unsigned int il3945_fill_beacon_frame(struct il_priv *il, + struct ieee80211_hdr *hdr, int left); +int il3945_dump_nic_event_log(struct il_priv *il, bool full_log, char **buf, + bool display); +void il3945_dump_nic_error_log(struct il_priv *il); + +/****************************************************************************** + * + * Functions implemented in iwl-[34]*.c which are forward declared here + * for use by iwl3945-base.c + * + * NOTE: The implementation of these functions are hardware specific + * which is why they are in the hardware specific files (vs. iwl-base.c) + * + * Naming convention -- + * il3945_ <-- Its part of iwlwifi (should be changed to il3945_) + * il3945_hw_ <-- Hardware specific (implemented in iwl-XXXX.c by all HW) + * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) + * il3945_bg_ <-- Called from work queue context + * il3945_mac_ <-- mac80211 callback + * + ****************************************************************************/ +void il3945_hw_handler_setup(struct il_priv *il); +void il3945_hw_setup_deferred_work(struct il_priv *il); +void il3945_hw_cancel_deferred_work(struct il_priv *il); +int il3945_hw_rxq_stop(struct il_priv *il); +int il3945_hw_set_hw_params(struct il_priv *il); +int il3945_hw_nic_init(struct il_priv *il); +int il3945_hw_nic_stop_master(struct il_priv *il); +void il3945_hw_txq_ctx_free(struct il_priv *il); +void il3945_hw_txq_ctx_stop(struct il_priv *il); +int il3945_hw_nic_reset(struct il_priv *il); +int il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, + dma_addr_t addr, u16 len, u8 reset, u8 pad); +void il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq); +int il3945_hw_get_temperature(struct il_priv *il); +int il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq); +unsigned int il3945_hw_get_beacon_cmd(struct il_priv *il, + struct il3945_frame *frame, u8 rate); +void il3945_hw_build_tx_cmd_rate(struct il_priv *il, struct il_device_cmd *cmd, + struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, int sta_id); +int il3945_hw_reg_send_txpower(struct il_priv *il); +int il3945_hw_reg_set_txpower(struct il_priv *il, s8 power); +void il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb); +void il3945_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb); +void il3945_disable_events(struct il_priv *il); +int il4965_get_temperature(const struct il_priv *il); +void il3945_post_associate(struct il_priv *il); +void il3945_config_ap(struct il_priv *il); + +int il3945_commit_rxon(struct il_priv *il); + +/** + * il3945_hw_find_station - Find station id for a given BSSID + * @bssid: MAC address of station ID to find + * + * NOTE: This should not be hardware specific but the code has + * not yet been merged into a single common layer for managing the + * station tables. + */ +u8 il3945_hw_find_station(struct il_priv *il, const u8 *bssid); + +__le32 il3945_get_antenna_flags(const struct il_priv *il); +int il3945_init_hw_rate_table(struct il_priv *il); +void il3945_reg_txpower_periodic(struct il_priv *il); +int il3945_txpower_set_from_eeprom(struct il_priv *il); + +int il3945_rs_next_rate(struct il_priv *il, int rate); + +/* scanning */ +int il3945_request_scan(struct il_priv *il, struct ieee80211_vif *vif); +void il3945_post_scan(struct il_priv *il); + +/* rates */ +extern const struct il3945_rate_info il3945_rates[RATE_COUNT_3945]; + +/* RSSI to dBm */ +#define IL39_RSSI_OFFSET 95 + +/* + * EEPROM related constants, enums, and structures. + */ +#define EEPROM_SKU_CAP_OP_MODE_MRC (1 << 7) + +/* + * Mapping of a Tx power level, at factory calibration temperature, + * to a radio/DSP gain table idx. + * One for each of 5 "sample" power levels in each band. + * v_det is measured at the factory, using the 3945's built-in power amplifier + * (PA) output voltage detector. This same detector is used during Tx of + * long packets in normal operation to provide feedback as to proper output + * level. + * Data copied from EEPROM. + * DO NOT ALTER THIS STRUCTURE!!! + */ +struct il3945_eeprom_txpower_sample { + u8 gain_idx; /* idx into power (gain) setup table ... */ + s8 power; /* ... for this pwr level for this chnl group */ + u16 v_det; /* PA output voltage */ +} __packed; + +/* + * Mappings of Tx power levels -> nominal radio/DSP gain table idxes. + * One for each channel group (a.k.a. "band") (1 for BG, 4 for A). + * Tx power setup code interpolates between the 5 "sample" power levels + * to determine the nominal setup for a requested power level. + * Data copied from EEPROM. + * DO NOT ALTER THIS STRUCTURE!!! + */ +struct il3945_eeprom_txpower_group { + struct il3945_eeprom_txpower_sample samples[5]; /* 5 power levels */ + s32 a, b, c, d, e; /* coefficients for voltage->power + * formula (signed) */ + s32 Fa, Fb, Fc, Fd, Fe; /* these modify coeffs based on + * frequency (signed) */ + s8 saturation_power; /* highest power possible by h/w in this + * band */ + u8 group_channel; /* "representative" channel # in this band */ + s16 temperature; /* h/w temperature at factory calib this band + * (signed) */ +} __packed; + +/* + * Temperature-based Tx-power compensation data, not band-specific. + * These coefficients are use to modify a/b/c/d/e coeffs based on + * difference between current temperature and factory calib temperature. + * Data copied from EEPROM. + */ +struct il3945_eeprom_temperature_corr { + u32 Ta; + u32 Tb; + u32 Tc; + u32 Td; + u32 Te; +} __packed; + +/* + * EEPROM map + */ +struct il3945_eeprom { + u8 reserved0[16]; + u16 device_id; /* abs.ofs: 16 */ + u8 reserved1[2]; + u16 pmc; /* abs.ofs: 20 */ + u8 reserved2[20]; + u8 mac_address[6]; /* abs.ofs: 42 */ + u8 reserved3[58]; + u16 board_revision; /* abs.ofs: 106 */ + u8 reserved4[11]; + u8 board_pba_number[9]; /* abs.ofs: 119 */ + u8 reserved5[8]; + u16 version; /* abs.ofs: 136 */ + u8 sku_cap; /* abs.ofs: 138 */ + u8 leds_mode; /* abs.ofs: 139 */ + u16 oem_mode; + u16 wowlan_mode; /* abs.ofs: 142 */ + u16 leds_time_interval; /* abs.ofs: 144 */ + u8 leds_off_time; /* abs.ofs: 146 */ + u8 leds_on_time; /* abs.ofs: 147 */ + u8 almgor_m_version; /* abs.ofs: 148 */ + u8 antenna_switch_type; /* abs.ofs: 149 */ + u8 reserved6[42]; + u8 sku_id[4]; /* abs.ofs: 192 */ + +/* + * Per-channel regulatory data. + * + * Each channel that *might* be supported by 3945 has a fixed location + * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory + * txpower (MSB). + * + * Entries immediately below are for 20 MHz channel width. + * + * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 + */ + u16 band_1_count; /* abs.ofs: 196 */ + struct il_eeprom_channel band_1_channels[14]; /* abs.ofs: 198 */ + +/* + * 4.9 GHz channels 183, 184, 185, 187, 188, 189, 192, 196, + * 5.0 GHz channels 7, 8, 11, 12, 16 + * (4915-5080MHz) (none of these is ever supported) + */ + u16 band_2_count; /* abs.ofs: 226 */ + struct il_eeprom_channel band_2_channels[13]; /* abs.ofs: 228 */ + +/* + * 5.2 GHz channels 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 + * (5170-5320MHz) + */ + u16 band_3_count; /* abs.ofs: 254 */ + struct il_eeprom_channel band_3_channels[12]; /* abs.ofs: 256 */ + +/* + * 5.5 GHz channels 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 + * (5500-5700MHz) + */ + u16 band_4_count; /* abs.ofs: 280 */ + struct il_eeprom_channel band_4_channels[11]; /* abs.ofs: 282 */ + +/* + * 5.7 GHz channels 145, 149, 153, 157, 161, 165 + * (5725-5825MHz) + */ + u16 band_5_count; /* abs.ofs: 304 */ + struct il_eeprom_channel band_5_channels[6]; /* abs.ofs: 306 */ + + u8 reserved9[194]; + +/* + * 3945 Txpower calibration data. + */ +#define IL_NUM_TX_CALIB_GROUPS 5 + struct il3945_eeprom_txpower_group groups[IL_NUM_TX_CALIB_GROUPS]; +/* abs.ofs: 512 */ + struct il3945_eeprom_temperature_corr corrections; /* abs.ofs: 832 */ + u8 reserved16[172]; /* fill out to full 1024 byte block */ +} __packed; + +#define IL3945_EEPROM_IMG_SIZE 1024 + +/* End of EEPROM */ + +#define PCI_CFG_REV_ID_BIT_BASIC_SKU (0x40) /* bit 6 */ +#define PCI_CFG_REV_ID_BIT_RTP (0x80) /* bit 7 */ + +/* 4 DATA + 1 CMD. There are 2 HCCA queues that are not used. */ +#define IL39_NUM_QUEUES 5 +#define IL39_CMD_QUEUE_NUM 4 + +#define IL_DEFAULT_TX_RETRY 15 + +/*********************************************/ + +#define RFD_SIZE 4 +#define NUM_TFD_CHUNKS 4 + +#define TFD_CTL_COUNT_SET(n) (n << 24) +#define TFD_CTL_COUNT_GET(ctl) ((ctl >> 24) & 7) +#define TFD_CTL_PAD_SET(n) (n << 28) +#define TFD_CTL_PAD_GET(ctl) (ctl >> 28) + +/* Sizes and addresses for instruction and data memory (SRAM) in + * 3945's embedded processor. Driver access is via HBUS_TARG_MEM_* regs. */ +#define IL39_RTC_INST_LOWER_BOUND (0x000000) +#define IL39_RTC_INST_UPPER_BOUND (0x014000) + +#define IL39_RTC_DATA_LOWER_BOUND (0x800000) +#define IL39_RTC_DATA_UPPER_BOUND (0x808000) + +#define IL39_RTC_INST_SIZE (IL39_RTC_INST_UPPER_BOUND - \ + IL39_RTC_INST_LOWER_BOUND) +#define IL39_RTC_DATA_SIZE (IL39_RTC_DATA_UPPER_BOUND - \ + IL39_RTC_DATA_LOWER_BOUND) + +#define IL39_MAX_INST_SIZE IL39_RTC_INST_SIZE +#define IL39_MAX_DATA_SIZE IL39_RTC_DATA_SIZE + +/* Size of uCode instruction memory in bootstrap state machine */ +#define IL39_MAX_BSM_SIZE IL39_RTC_INST_SIZE + +static inline int +il3945_hw_valid_rtc_data_addr(u32 addr) +{ + return (addr >= IL39_RTC_DATA_LOWER_BOUND && + addr < IL39_RTC_DATA_UPPER_BOUND); +} + +/* Base physical address of il3945_shared is provided to FH39_TSSR_CBB_BASE + * and &il3945_shared.rx_read_ptr[0] is provided to FH39_RCSR_RPTR_ADDR(0) */ +struct il3945_shared { + __le32 tx_base_ptr[8]; +} __packed; + +/************************************/ +/* iwl3945 Flow Handler Definitions */ +/************************************/ + +/** + * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) + * Addresses are offsets from device's PCI hardware base address. + */ +#define FH39_MEM_LOWER_BOUND (0x0800) +#define FH39_MEM_UPPER_BOUND (0x1000) + +#define FH39_CBCC_TBL (FH39_MEM_LOWER_BOUND + 0x140) +#define FH39_TFDB_TBL (FH39_MEM_LOWER_BOUND + 0x180) +#define FH39_RCSR_TBL (FH39_MEM_LOWER_BOUND + 0x400) +#define FH39_RSSR_TBL (FH39_MEM_LOWER_BOUND + 0x4c0) +#define FH39_TCSR_TBL (FH39_MEM_LOWER_BOUND + 0x500) +#define FH39_TSSR_TBL (FH39_MEM_LOWER_BOUND + 0x680) + +/* TFDB (Transmit Frame Buffer Descriptor) */ +#define FH39_TFDB(_ch, buf) (FH39_TFDB_TBL + \ + ((_ch) * 2 + (buf)) * 0x28) +#define FH39_TFDB_CHNL_BUF_CTRL_REG(_ch) (FH39_TFDB_TBL + 0x50 * (_ch)) + +/* CBCC channel is [0,2] */ +#define FH39_CBCC(_ch) (FH39_CBCC_TBL + (_ch) * 0x8) +#define FH39_CBCC_CTRL(_ch) (FH39_CBCC(_ch) + 0x00) +#define FH39_CBCC_BASE(_ch) (FH39_CBCC(_ch) + 0x04) + +/* RCSR channel is [0,2] */ +#define FH39_RCSR(_ch) (FH39_RCSR_TBL + (_ch) * 0x40) +#define FH39_RCSR_CONFIG(_ch) (FH39_RCSR(_ch) + 0x00) +#define FH39_RCSR_RBD_BASE(_ch) (FH39_RCSR(_ch) + 0x04) +#define FH39_RCSR_WPTR(_ch) (FH39_RCSR(_ch) + 0x20) +#define FH39_RCSR_RPTR_ADDR(_ch) (FH39_RCSR(_ch) + 0x24) + +#define FH39_RSCSR_CHNL0_WPTR (FH39_RCSR_WPTR(0)) + +/* RSSR */ +#define FH39_RSSR_CTRL (FH39_RSSR_TBL + 0x000) +#define FH39_RSSR_STATUS (FH39_RSSR_TBL + 0x004) + +/* TCSR */ +#define FH39_TCSR(_ch) (FH39_TCSR_TBL + (_ch) * 0x20) +#define FH39_TCSR_CONFIG(_ch) (FH39_TCSR(_ch) + 0x00) +#define FH39_TCSR_CREDIT(_ch) (FH39_TCSR(_ch) + 0x04) +#define FH39_TCSR_BUFF_STTS(_ch) (FH39_TCSR(_ch) + 0x08) + +/* TSSR */ +#define FH39_TSSR_CBB_BASE (FH39_TSSR_TBL + 0x000) +#define FH39_TSSR_MSG_CONFIG (FH39_TSSR_TBL + 0x008) +#define FH39_TSSR_TX_STATUS (FH39_TSSR_TBL + 0x010) + +/* DBM */ + +#define FH39_SRVC_CHNL (6) + +#define FH39_RCSR_RX_CONFIG_REG_POS_RBDC_SIZE (20) +#define FH39_RCSR_RX_CONFIG_REG_POS_IRQ_RBTH (4) + +#define FH39_RCSR_RX_CONFIG_REG_BIT_WR_STTS_EN (0x08000000) + +#define FH39_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN_ENABLE (0x80000000) + +#define FH39_RCSR_RX_CONFIG_REG_VAL_RDRBD_EN_ENABLE (0x20000000) + +#define FH39_RCSR_RX_CONFIG_REG_VAL_MAX_FRAG_SIZE_128 (0x01000000) + +#define FH39_RCSR_RX_CONFIG_REG_VAL_IRQ_DEST_INT_HOST (0x00001000) + +#define FH39_RCSR_RX_CONFIG_REG_VAL_MSG_MODE_FH (0x00000000) + +#define FH39_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000) +#define FH39_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRIVER (0x00000001) + +#define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE_VAL (0x00000000) +#define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL (0x00000008) + +#define FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000) + +#define FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000) + +#define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000) +#define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000) + +#define FH39_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00004000) + +#define FH39_TCSR_CHNL_TX_BUF_STS_REG_BIT_TFDB_WPTR (0x00000001) + +#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON (0xFF000000) +#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON (0x00FF0000) + +#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B (0x00000400) + +#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON (0x00000100) +#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON (0x00000080) + +#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH (0x00000020) +#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH (0x00000005) + +#define FH39_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_ch) (BIT(_ch) << 24) +#define FH39_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_ch) (BIT(_ch) << 16) + +#define FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_ch) \ + (FH39_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_ch) | \ + FH39_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_ch)) + +#define FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) + +struct il3945_tfd_tb { + __le32 addr; + __le32 len; +} __packed; + +struct il3945_tfd { + __le32 control_flags; + struct il3945_tfd_tb tbs[4]; + u8 __pad[28]; +} __packed; + +#ifdef CONFIG_IWLEGACY_DEBUGFS +extern const struct il_debugfs_ops il3945_debugfs_ops; +#endif + +#endif diff --git a/drivers/net/wireless/intel/iwlegacy/4965-calib.c b/drivers/net/wireless/intel/iwlegacy/4965-calib.c new file mode 100644 index 000000000000..e78bdefb8952 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/4965-calib.c @@ -0,0 +1,934 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include +#include + +#include "common.h" +#include "4965.h" + +/***************************************************************************** + * INIT calibrations framework + *****************************************************************************/ + +struct stats_general_data { + u32 beacon_silence_rssi_a; + u32 beacon_silence_rssi_b; + u32 beacon_silence_rssi_c; + u32 beacon_energy_a; + u32 beacon_energy_b; + u32 beacon_energy_c; +}; + +/***************************************************************************** + * RUNTIME calibrations framework + *****************************************************************************/ + +/* "false alarms" are signals that our DSP tries to lock onto, + * but then determines that they are either noise, or transmissions + * from a distant wireless network (also "noise", really) that get + * "stepped on" by stronger transmissions within our own network. + * This algorithm attempts to set a sensitivity level that is high + * enough to receive all of our own network traffic, but not so + * high that our DSP gets too busy trying to lock onto non-network + * activity/noise. */ +static int +il4965_sens_energy_cck(struct il_priv *il, u32 norm_fa, u32 rx_enable_time, + struct stats_general_data *rx_info) +{ + u32 max_nrg_cck = 0; + int i = 0; + u8 max_silence_rssi = 0; + u32 silence_ref = 0; + u8 silence_rssi_a = 0; + u8 silence_rssi_b = 0; + u8 silence_rssi_c = 0; + u32 val; + + /* "false_alarms" values below are cross-multiplications to assess the + * numbers of false alarms within the measured period of actual Rx + * (Rx is off when we're txing), vs the min/max expected false alarms + * (some should be expected if rx is sensitive enough) in a + * hypothetical listening period of 200 time units (TU), 204.8 msec: + * + * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time + * + * */ + u32 false_alarms = norm_fa * 200 * 1024; + u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; + u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; + struct il_sensitivity_data *data = NULL; + const struct il_sensitivity_ranges *ranges = il->hw_params.sens; + + data = &(il->sensitivity_data); + + data->nrg_auto_corr_silence_diff = 0; + + /* Find max silence rssi among all 3 receivers. + * This is background noise, which may include transmissions from other + * networks, measured during silence before our network's beacon */ + silence_rssi_a = + (u8) ((rx_info->beacon_silence_rssi_a & ALL_BAND_FILTER) >> 8); + silence_rssi_b = + (u8) ((rx_info->beacon_silence_rssi_b & ALL_BAND_FILTER) >> 8); + silence_rssi_c = + (u8) ((rx_info->beacon_silence_rssi_c & ALL_BAND_FILTER) >> 8); + + val = max(silence_rssi_b, silence_rssi_c); + max_silence_rssi = max(silence_rssi_a, (u8) val); + + /* Store silence rssi in 20-beacon history table */ + data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; + data->nrg_silence_idx++; + if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) + data->nrg_silence_idx = 0; + + /* Find max silence rssi across 20 beacon history */ + for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { + val = data->nrg_silence_rssi[i]; + silence_ref = max(silence_ref, val); + } + D_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n", silence_rssi_a, + silence_rssi_b, silence_rssi_c, silence_ref); + + /* Find max rx energy (min value!) among all 3 receivers, + * measured during beacon frame. + * Save it in 10-beacon history table. */ + i = data->nrg_energy_idx; + val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); + data->nrg_value[i] = min(rx_info->beacon_energy_a, val); + + data->nrg_energy_idx++; + if (data->nrg_energy_idx >= 10) + data->nrg_energy_idx = 0; + + /* Find min rx energy (max value) across 10 beacon history. + * This is the minimum signal level that we want to receive well. + * Add backoff (margin so we don't miss slightly lower energy frames). + * This establishes an upper bound (min value) for energy threshold. */ + max_nrg_cck = data->nrg_value[0]; + for (i = 1; i < 10; i++) + max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); + max_nrg_cck += 6; + + D_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", + rx_info->beacon_energy_a, rx_info->beacon_energy_b, + rx_info->beacon_energy_c, max_nrg_cck - 6); + + /* Count number of consecutive beacons with fewer-than-desired + * false alarms. */ + if (false_alarms < min_false_alarms) + data->num_in_cck_no_fa++; + else + data->num_in_cck_no_fa = 0; + D_CALIB("consecutive bcns with few false alarms = %u\n", + data->num_in_cck_no_fa); + + /* If we got too many false alarms this time, reduce sensitivity */ + if (false_alarms > max_false_alarms && + data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK) { + D_CALIB("norm FA %u > max FA %u\n", false_alarms, + max_false_alarms); + D_CALIB("... reducing sensitivity\n"); + data->nrg_curr_state = IL_FA_TOO_MANY; + /* Store for "fewer than desired" on later beacon */ + data->nrg_silence_ref = silence_ref; + + /* increase energy threshold (reduce nrg value) + * to decrease sensitivity */ + data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; + /* Else if we got fewer than desired, increase sensitivity */ + } else if (false_alarms < min_false_alarms) { + data->nrg_curr_state = IL_FA_TOO_FEW; + + /* Compare silence level with silence level for most recent + * healthy number or too many false alarms */ + data->nrg_auto_corr_silence_diff = + (s32) data->nrg_silence_ref - (s32) silence_ref; + + D_CALIB("norm FA %u < min FA %u, silence diff %d\n", + false_alarms, min_false_alarms, + data->nrg_auto_corr_silence_diff); + + /* Increase value to increase sensitivity, but only if: + * 1a) previous beacon did *not* have *too many* false alarms + * 1b) AND there's a significant difference in Rx levels + * from a previous beacon with too many, or healthy # FAs + * OR 2) We've seen a lot of beacons (100) with too few + * false alarms */ + if (data->nrg_prev_state != IL_FA_TOO_MANY && + (data->nrg_auto_corr_silence_diff > NRG_DIFF || + data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) { + + D_CALIB("... increasing sensitivity\n"); + /* Increase nrg value to increase sensitivity */ + val = data->nrg_th_cck + NRG_STEP_CCK; + data->nrg_th_cck = min((u32) ranges->min_nrg_cck, val); + } else { + D_CALIB("... but not changing sensitivity\n"); + } + + /* Else we got a healthy number of false alarms, keep status quo */ + } else { + D_CALIB(" FA in safe zone\n"); + data->nrg_curr_state = IL_FA_GOOD_RANGE; + + /* Store for use in "fewer than desired" with later beacon */ + data->nrg_silence_ref = silence_ref; + + /* If previous beacon had too many false alarms, + * give it some extra margin by reducing sensitivity again + * (but don't go below measured energy of desired Rx) */ + if (IL_FA_TOO_MANY == data->nrg_prev_state) { + D_CALIB("... increasing margin\n"); + if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) + data->nrg_th_cck -= NRG_MARGIN; + else + data->nrg_th_cck = max_nrg_cck; + } + } + + /* Make sure the energy threshold does not go above the measured + * energy of the desired Rx signals (reduced by backoff margin), + * or else we might start missing Rx frames. + * Lower value is higher energy, so we use max()! + */ + data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); + D_CALIB("new nrg_th_cck %u\n", data->nrg_th_cck); + + data->nrg_prev_state = data->nrg_curr_state; + + /* Auto-correlation CCK algorithm */ + if (false_alarms > min_false_alarms) { + + /* increase auto_corr values to decrease sensitivity + * so the DSP won't be disturbed by the noise + */ + if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) + data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; + else { + val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; + data->auto_corr_cck = + min((u32) ranges->auto_corr_max_cck, val); + } + val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; + data->auto_corr_cck_mrc = + min((u32) ranges->auto_corr_max_cck_mrc, val); + } else if (false_alarms < min_false_alarms && + (data->nrg_auto_corr_silence_diff > NRG_DIFF || + data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) { + + /* Decrease auto_corr values to increase sensitivity */ + val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; + data->auto_corr_cck = max((u32) ranges->auto_corr_min_cck, val); + val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; + data->auto_corr_cck_mrc = + max((u32) ranges->auto_corr_min_cck_mrc, val); + } + + return 0; +} + +static int +il4965_sens_auto_corr_ofdm(struct il_priv *il, u32 norm_fa, u32 rx_enable_time) +{ + u32 val; + u32 false_alarms = norm_fa * 200 * 1024; + u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; + u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; + struct il_sensitivity_data *data = NULL; + const struct il_sensitivity_ranges *ranges = il->hw_params.sens; + + data = &(il->sensitivity_data); + + /* If we got too many false alarms this time, reduce sensitivity */ + if (false_alarms > max_false_alarms) { + + D_CALIB("norm FA %u > max FA %u)\n", false_alarms, + max_false_alarms); + + val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm = + min((u32) ranges->auto_corr_max_ofdm, val); + + val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc = + min((u32) ranges->auto_corr_max_ofdm_mrc, val); + + val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_x1 = + min((u32) ranges->auto_corr_max_ofdm_x1, val); + + val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc_x1 = + min((u32) ranges->auto_corr_max_ofdm_mrc_x1, val); + } + + /* Else if we got fewer than desired, increase sensitivity */ + else if (false_alarms < min_false_alarms) { + + D_CALIB("norm FA %u < min FA %u\n", false_alarms, + min_false_alarms); + + val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm = + max((u32) ranges->auto_corr_min_ofdm, val); + + val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc = + max((u32) ranges->auto_corr_min_ofdm_mrc, val); + + val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_x1 = + max((u32) ranges->auto_corr_min_ofdm_x1, val); + + val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc_x1 = + max((u32) ranges->auto_corr_min_ofdm_mrc_x1, val); + } else { + D_CALIB("min FA %u < norm FA %u < max FA %u OK\n", + min_false_alarms, false_alarms, max_false_alarms); + } + return 0; +} + +static void +il4965_prepare_legacy_sensitivity_tbl(struct il_priv *il, + struct il_sensitivity_data *data, + __le16 *tbl) +{ + tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX] = + cpu_to_le16((u16) data->auto_corr_ofdm); + tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX] = + cpu_to_le16((u16) data->auto_corr_ofdm_mrc); + tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX] = + cpu_to_le16((u16) data->auto_corr_ofdm_x1); + tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX] = + cpu_to_le16((u16) data->auto_corr_ofdm_mrc_x1); + + tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX] = + cpu_to_le16((u16) data->auto_corr_cck); + tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX] = + cpu_to_le16((u16) data->auto_corr_cck_mrc); + + tbl[HD_MIN_ENERGY_CCK_DET_IDX] = cpu_to_le16((u16) data->nrg_th_cck); + tbl[HD_MIN_ENERGY_OFDM_DET_IDX] = cpu_to_le16((u16) data->nrg_th_ofdm); + + tbl[HD_BARKER_CORR_TH_ADD_MIN_IDX] = + cpu_to_le16(data->barker_corr_th_min); + tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_IDX] = + cpu_to_le16(data->barker_corr_th_min_mrc); + tbl[HD_OFDM_ENERGY_TH_IN_IDX] = cpu_to_le16(data->nrg_th_cca); + + D_CALIB("ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", + data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, + data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, + data->nrg_th_ofdm); + + D_CALIB("cck: ac %u mrc %u thresh %u\n", data->auto_corr_cck, + data->auto_corr_cck_mrc, data->nrg_th_cck); +} + +/* Prepare a C_SENSITIVITY, send to uCode if values have changed */ +static int +il4965_sensitivity_write(struct il_priv *il) +{ + struct il_sensitivity_cmd cmd; + struct il_sensitivity_data *data = NULL; + struct il_host_cmd cmd_out = { + .id = C_SENSITIVITY, + .len = sizeof(struct il_sensitivity_cmd), + .flags = CMD_ASYNC, + .data = &cmd, + }; + + data = &(il->sensitivity_data); + + memset(&cmd, 0, sizeof(cmd)); + + il4965_prepare_legacy_sensitivity_tbl(il, data, &cmd.table[0]); + + /* Update uCode's "work" table, and copy it to DSP */ + cmd.control = C_SENSITIVITY_CONTROL_WORK_TBL; + + /* Don't send command to uCode if nothing has changed */ + if (!memcmp + (&cmd.table[0], &(il->sensitivity_tbl[0]), + sizeof(u16) * HD_TBL_SIZE)) { + D_CALIB("No change in C_SENSITIVITY\n"); + return 0; + } + + /* Copy table for comparison next time */ + memcpy(&(il->sensitivity_tbl[0]), &(cmd.table[0]), + sizeof(u16) * HD_TBL_SIZE); + + return il_send_cmd(il, &cmd_out); +} + +void +il4965_init_sensitivity(struct il_priv *il) +{ + int ret = 0; + int i; + struct il_sensitivity_data *data = NULL; + const struct il_sensitivity_ranges *ranges = il->hw_params.sens; + + if (il->disable_sens_cal) + return; + + D_CALIB("Start il4965_init_sensitivity\n"); + + /* Clear driver's sensitivity algo data */ + data = &(il->sensitivity_data); + + if (ranges == NULL) + return; + + memset(data, 0, sizeof(struct il_sensitivity_data)); + + data->num_in_cck_no_fa = 0; + data->nrg_curr_state = IL_FA_TOO_MANY; + data->nrg_prev_state = IL_FA_TOO_MANY; + data->nrg_silence_ref = 0; + data->nrg_silence_idx = 0; + data->nrg_energy_idx = 0; + + for (i = 0; i < 10; i++) + data->nrg_value[i] = 0; + + for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) + data->nrg_silence_rssi[i] = 0; + + data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; + data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; + data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; + data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; + data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; + data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; + data->nrg_th_cck = ranges->nrg_th_cck; + data->nrg_th_ofdm = ranges->nrg_th_ofdm; + data->barker_corr_th_min = ranges->barker_corr_th_min; + data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; + data->nrg_th_cca = ranges->nrg_th_cca; + + data->last_bad_plcp_cnt_ofdm = 0; + data->last_fa_cnt_ofdm = 0; + data->last_bad_plcp_cnt_cck = 0; + data->last_fa_cnt_cck = 0; + + ret |= il4965_sensitivity_write(il); + D_CALIB("<disable_sens_cal) + return; + + data = &(il->sensitivity_data); + + if (!il_is_any_associated(il)) { + D_CALIB("<< - not associated\n"); + return; + } + + spin_lock_irqsave(&il->lock, flags); + + rx_info = &(((struct il_notif_stats *)resp)->rx.general); + ofdm = &(((struct il_notif_stats *)resp)->rx.ofdm); + cck = &(((struct il_notif_stats *)resp)->rx.cck); + + if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { + D_CALIB("<< invalid data.\n"); + spin_unlock_irqrestore(&il->lock, flags); + return; + } + + /* Extract Statistics: */ + rx_enable_time = le32_to_cpu(rx_info->channel_load); + fa_cck = le32_to_cpu(cck->false_alarm_cnt); + fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); + bad_plcp_cck = le32_to_cpu(cck->plcp_err); + bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); + + statis.beacon_silence_rssi_a = + le32_to_cpu(rx_info->beacon_silence_rssi_a); + statis.beacon_silence_rssi_b = + le32_to_cpu(rx_info->beacon_silence_rssi_b); + statis.beacon_silence_rssi_c = + le32_to_cpu(rx_info->beacon_silence_rssi_c); + statis.beacon_energy_a = le32_to_cpu(rx_info->beacon_energy_a); + statis.beacon_energy_b = le32_to_cpu(rx_info->beacon_energy_b); + statis.beacon_energy_c = le32_to_cpu(rx_info->beacon_energy_c); + + spin_unlock_irqrestore(&il->lock, flags); + + D_CALIB("rx_enable_time = %u usecs\n", rx_enable_time); + + if (!rx_enable_time) { + D_CALIB("<< RX Enable Time == 0!\n"); + return; + } + + /* These stats increase monotonically, and do not reset + * at each beacon. Calculate difference from last value, or just + * use the new stats value if it has reset or wrapped around. */ + if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) + data->last_bad_plcp_cnt_cck = bad_plcp_cck; + else { + bad_plcp_cck -= data->last_bad_plcp_cnt_cck; + data->last_bad_plcp_cnt_cck += bad_plcp_cck; + } + + if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) + data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; + else { + bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; + data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; + } + + if (data->last_fa_cnt_ofdm > fa_ofdm) + data->last_fa_cnt_ofdm = fa_ofdm; + else { + fa_ofdm -= data->last_fa_cnt_ofdm; + data->last_fa_cnt_ofdm += fa_ofdm; + } + + if (data->last_fa_cnt_cck > fa_cck) + data->last_fa_cnt_cck = fa_cck; + else { + fa_cck -= data->last_fa_cnt_cck; + data->last_fa_cnt_cck += fa_cck; + } + + /* Total aborted signal locks */ + norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; + norm_fa_cck = fa_cck + bad_plcp_cck; + + D_CALIB("cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, + bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); + + il4965_sens_auto_corr_ofdm(il, norm_fa_ofdm, rx_enable_time); + il4965_sens_energy_cck(il, norm_fa_cck, rx_enable_time, &statis); + + il4965_sensitivity_write(il); +} + +static inline u8 +il4965_find_first_chain(u8 mask) +{ + if (mask & ANT_A) + return CHAIN_A; + if (mask & ANT_B) + return CHAIN_B; + return CHAIN_C; +} + +/** + * Run disconnected antenna algorithm to find out which antennas are + * disconnected. + */ +static void +il4965_find_disconn_antenna(struct il_priv *il, u32 * average_sig, + struct il_chain_noise_data *data) +{ + u32 active_chains = 0; + u32 max_average_sig; + u16 max_average_sig_antenna_i; + u8 num_tx_chains; + u8 first_chain; + u16 i = 0; + + average_sig[0] = + data->chain_signal_a / + il->cfg->chain_noise_num_beacons; + average_sig[1] = + data->chain_signal_b / + il->cfg->chain_noise_num_beacons; + average_sig[2] = + data->chain_signal_c / + il->cfg->chain_noise_num_beacons; + + if (average_sig[0] >= average_sig[1]) { + max_average_sig = average_sig[0]; + max_average_sig_antenna_i = 0; + active_chains = (1 << max_average_sig_antenna_i); + } else { + max_average_sig = average_sig[1]; + max_average_sig_antenna_i = 1; + active_chains = (1 << max_average_sig_antenna_i); + } + + if (average_sig[2] >= max_average_sig) { + max_average_sig = average_sig[2]; + max_average_sig_antenna_i = 2; + active_chains = (1 << max_average_sig_antenna_i); + } + + D_CALIB("average_sig: a %d b %d c %d\n", average_sig[0], average_sig[1], + average_sig[2]); + D_CALIB("max_average_sig = %d, antenna %d\n", max_average_sig, + max_average_sig_antenna_i); + + /* Compare signal strengths for all 3 receivers. */ + for (i = 0; i < NUM_RX_CHAINS; i++) { + if (i != max_average_sig_antenna_i) { + s32 rssi_delta = (max_average_sig - average_sig[i]); + + /* If signal is very weak, compared with + * strongest, mark it as disconnected. */ + if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) + data->disconn_array[i] = 1; + else + active_chains |= (1 << i); + D_CALIB("i = %d rssiDelta = %d " + "disconn_array[i] = %d\n", i, rssi_delta, + data->disconn_array[i]); + } + } + + /* + * The above algorithm sometimes fails when the ucode + * reports 0 for all chains. It's not clear why that + * happens to start with, but it is then causing trouble + * because this can make us enable more chains than the + * hardware really has. + * + * To be safe, simply mask out any chains that we know + * are not on the device. + */ + active_chains &= il->hw_params.valid_rx_ant; + + num_tx_chains = 0; + for (i = 0; i < NUM_RX_CHAINS; i++) { + /* loops on all the bits of + * il->hw_setting.valid_tx_ant */ + u8 ant_msk = (1 << i); + if (!(il->hw_params.valid_tx_ant & ant_msk)) + continue; + + num_tx_chains++; + if (data->disconn_array[i] == 0) + /* there is a Tx antenna connected */ + break; + if (num_tx_chains == il->hw_params.tx_chains_num && + data->disconn_array[i]) { + /* + * If all chains are disconnected + * connect the first valid tx chain + */ + first_chain = + il4965_find_first_chain(il->cfg->valid_tx_ant); + data->disconn_array[first_chain] = 0; + active_chains |= BIT(first_chain); + D_CALIB("All Tx chains are disconnected" + "- declare %d as connected\n", first_chain); + break; + } + } + + if (active_chains != il->hw_params.valid_rx_ant && + active_chains != il->chain_noise_data.active_chains) + D_CALIB("Detected that not all antennas are connected! " + "Connected: %#x, valid: %#x.\n", active_chains, + il->hw_params.valid_rx_ant); + + /* Save for use within RXON, TX, SCAN commands, etc. */ + data->active_chains = active_chains; + D_CALIB("active_chains (bitwise) = 0x%x\n", active_chains); +} + +static void +il4965_gain_computation(struct il_priv *il, u32 * average_noise, + u16 min_average_noise_antenna_i, u32 min_average_noise, + u8 default_chain) +{ + int i, ret; + struct il_chain_noise_data *data = &il->chain_noise_data; + + data->delta_gain_code[min_average_noise_antenna_i] = 0; + + for (i = default_chain; i < NUM_RX_CHAINS; i++) { + s32 delta_g = 0; + + if (!data->disconn_array[i] && + data->delta_gain_code[i] == + CHAIN_NOISE_DELTA_GAIN_INIT_VAL) { + delta_g = average_noise[i] - min_average_noise; + data->delta_gain_code[i] = (u8) ((delta_g * 10) / 15); + data->delta_gain_code[i] = + min(data->delta_gain_code[i], + (u8) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); + + data->delta_gain_code[i] = + (data->delta_gain_code[i] | (1 << 2)); + } else { + data->delta_gain_code[i] = 0; + } + } + D_CALIB("delta_gain_codes: a %d b %d c %d\n", data->delta_gain_code[0], + data->delta_gain_code[1], data->delta_gain_code[2]); + + /* Differential gain gets sent to uCode only once */ + if (!data->radio_write) { + struct il_calib_diff_gain_cmd cmd; + data->radio_write = 1; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD; + cmd.diff_gain_a = data->delta_gain_code[0]; + cmd.diff_gain_b = data->delta_gain_code[1]; + cmd.diff_gain_c = data->delta_gain_code[2]; + ret = il_send_cmd_pdu(il, C_PHY_CALIBRATION, sizeof(cmd), &cmd); + if (ret) + D_CALIB("fail sending cmd " "C_PHY_CALIBRATION\n"); + + /* TODO we might want recalculate + * rx_chain in rxon cmd */ + + /* Mark so we run this algo only once! */ + data->state = IL_CHAIN_NOISE_CALIBRATED; + } +} + +/* + * Accumulate 16 beacons of signal and noise stats for each of + * 3 receivers/antennas/rx-chains, then figure out: + * 1) Which antennas are connected. + * 2) Differential rx gain settings to balance the 3 receivers. + */ +void +il4965_chain_noise_calibration(struct il_priv *il, void *stat_resp) +{ + struct il_chain_noise_data *data = NULL; + + u32 chain_noise_a; + u32 chain_noise_b; + u32 chain_noise_c; + u32 chain_sig_a; + u32 chain_sig_b; + u32 chain_sig_c; + u32 average_sig[NUM_RX_CHAINS] = { INITIALIZATION_VALUE }; + u32 average_noise[NUM_RX_CHAINS] = { INITIALIZATION_VALUE }; + u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; + u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; + u16 i = 0; + u16 rxon_chnum = INITIALIZATION_VALUE; + u16 stat_chnum = INITIALIZATION_VALUE; + u8 rxon_band24; + u8 stat_band24; + unsigned long flags; + struct stats_rx_non_phy *rx_info; + + if (il->disable_chain_noise_cal) + return; + + data = &(il->chain_noise_data); + + /* + * Accumulate just the first "chain_noise_num_beacons" after + * the first association, then we're done forever. + */ + if (data->state != IL_CHAIN_NOISE_ACCUMULATE) { + if (data->state == IL_CHAIN_NOISE_ALIVE) + D_CALIB("Wait for noise calib reset\n"); + return; + } + + spin_lock_irqsave(&il->lock, flags); + + rx_info = &(((struct il_notif_stats *)stat_resp)->rx.general); + + if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { + D_CALIB(" << Interference data unavailable\n"); + spin_unlock_irqrestore(&il->lock, flags); + return; + } + + rxon_band24 = !!(il->staging.flags & RXON_FLG_BAND_24G_MSK); + rxon_chnum = le16_to_cpu(il->staging.channel); + + stat_band24 = + !!(((struct il_notif_stats *)stat_resp)-> + flag & STATS_REPLY_FLG_BAND_24G_MSK); + stat_chnum = + le32_to_cpu(((struct il_notif_stats *)stat_resp)->flag) >> 16; + + /* Make sure we accumulate data for just the associated channel + * (even if scanning). */ + if (rxon_chnum != stat_chnum || rxon_band24 != stat_band24) { + D_CALIB("Stats not from chan=%d, band24=%d\n", rxon_chnum, + rxon_band24); + spin_unlock_irqrestore(&il->lock, flags); + return; + } + + /* + * Accumulate beacon stats values across + * "chain_noise_num_beacons" + */ + chain_noise_a = + le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; + chain_noise_b = + le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; + chain_noise_c = + le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; + + chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; + chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; + chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; + + spin_unlock_irqrestore(&il->lock, flags); + + data->beacon_count++; + + data->chain_noise_a = (chain_noise_a + data->chain_noise_a); + data->chain_noise_b = (chain_noise_b + data->chain_noise_b); + data->chain_noise_c = (chain_noise_c + data->chain_noise_c); + + data->chain_signal_a = (chain_sig_a + data->chain_signal_a); + data->chain_signal_b = (chain_sig_b + data->chain_signal_b); + data->chain_signal_c = (chain_sig_c + data->chain_signal_c); + + D_CALIB("chan=%d, band24=%d, beacon=%d\n", rxon_chnum, rxon_band24, + data->beacon_count); + D_CALIB("chain_sig: a %d b %d c %d\n", chain_sig_a, chain_sig_b, + chain_sig_c); + D_CALIB("chain_noise: a %d b %d c %d\n", chain_noise_a, chain_noise_b, + chain_noise_c); + + /* If this is the "chain_noise_num_beacons", determine: + * 1) Disconnected antennas (using signal strengths) + * 2) Differential gain (using silence noise) to balance receivers */ + if (data->beacon_count != il->cfg->chain_noise_num_beacons) + return; + + /* Analyze signal for disconnected antenna */ + il4965_find_disconn_antenna(il, average_sig, data); + + /* Analyze noise for rx balance */ + average_noise[0] = + data->chain_noise_a / il->cfg->chain_noise_num_beacons; + average_noise[1] = + data->chain_noise_b / il->cfg->chain_noise_num_beacons; + average_noise[2] = + data->chain_noise_c / il->cfg->chain_noise_num_beacons; + + for (i = 0; i < NUM_RX_CHAINS; i++) { + if (!data->disconn_array[i] && + average_noise[i] <= min_average_noise) { + /* This means that chain i is active and has + * lower noise values so far: */ + min_average_noise = average_noise[i]; + min_average_noise_antenna_i = i; + } + } + + D_CALIB("average_noise: a %d b %d c %d\n", average_noise[0], + average_noise[1], average_noise[2]); + + D_CALIB("min_average_noise = %d, antenna %d\n", min_average_noise, + min_average_noise_antenna_i); + + il4965_gain_computation(il, average_noise, min_average_noise_antenna_i, + min_average_noise, + il4965_find_first_chain(il->cfg->valid_rx_ant)); + + /* Some power changes may have been made during the calibration. + * Update and commit the RXON + */ + if (il->ops->update_chain_flags) + il->ops->update_chain_flags(il); + + data->state = IL_CHAIN_NOISE_DONE; + il_power_update_mode(il, false); +} + +void +il4965_reset_run_time_calib(struct il_priv *il) +{ + int i; + memset(&(il->sensitivity_data), 0, sizeof(struct il_sensitivity_data)); + memset(&(il->chain_noise_data), 0, sizeof(struct il_chain_noise_data)); + for (i = 0; i < NUM_RX_CHAINS; i++) + il->chain_noise_data.delta_gain_code[i] = + CHAIN_NOISE_DELTA_GAIN_INIT_VAL; + + /* Ask for stats now, the uCode will send notification + * periodically after association */ + il_send_stats_request(il, CMD_ASYNC, true); +} diff --git a/drivers/net/wireless/intel/iwlegacy/4965-debug.c b/drivers/net/wireless/intel/iwlegacy/4965-debug.c new file mode 100644 index 000000000000..e0597bfdddb8 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/4965-debug.c @@ -0,0 +1,752 @@ +/****************************************************************************** +* +* GPL LICENSE SUMMARY +* +* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of version 2 of the GNU General Public License as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, +* USA +* +* The full GNU General Public License is included in this distribution +* in the file called LICENSE.GPL. +* +* Contact Information: +* Intel Linux Wireless +* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 +*****************************************************************************/ +#include "common.h" +#include "4965.h" + +static const char *fmt_value = " %-30s %10u\n"; +static const char *fmt_table = " %-30s %10u %10u %10u %10u\n"; +static const char *fmt_header = + "%-32s current cumulative delta max\n"; + +static int +il4965_stats_flag(struct il_priv *il, char *buf, int bufsz) +{ + int p = 0; + u32 flag; + + flag = le32_to_cpu(il->_4965.stats.flag); + + p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", flag); + if (flag & UCODE_STATS_CLEAR_MSK) + p += scnprintf(buf + p, bufsz - p, + "\tStatistics have been cleared\n"); + p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", + (flag & UCODE_STATS_FREQUENCY_MSK) ? "2.4 GHz" : + "5.2 GHz"); + p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", + (flag & UCODE_STATS_NARROW_BAND_MSK) ? "enabled" : + "disabled"); + + return p; +} + +static ssize_t +il4965_ucode_rx_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + int pos = 0; + char *buf; + int bufsz = + sizeof(struct stats_rx_phy) * 40 + + sizeof(struct stats_rx_non_phy) * 40 + + sizeof(struct stats_rx_ht_phy) * 40 + 400; + ssize_t ret; + struct stats_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; + struct stats_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; + struct stats_rx_non_phy *general, *accum_general; + struct stats_rx_non_phy *delta_general, *max_general; + struct stats_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht; + + if (!il_is_alive(il)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IL_ERR("Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* + * the statistic information display here is based on + * the last stats notification from uCode + * might not reflect the current uCode activity + */ + ofdm = &il->_4965.stats.rx.ofdm; + cck = &il->_4965.stats.rx.cck; + general = &il->_4965.stats.rx.general; + ht = &il->_4965.stats.rx.ofdm_ht; + accum_ofdm = &il->_4965.accum_stats.rx.ofdm; + accum_cck = &il->_4965.accum_stats.rx.cck; + accum_general = &il->_4965.accum_stats.rx.general; + accum_ht = &il->_4965.accum_stats.rx.ofdm_ht; + delta_ofdm = &il->_4965.delta_stats.rx.ofdm; + delta_cck = &il->_4965.delta_stats.rx.cck; + delta_general = &il->_4965.delta_stats.rx.general; + delta_ht = &il->_4965.delta_stats.rx.ofdm_ht; + max_ofdm = &il->_4965.max_delta.rx.ofdm; + max_cck = &il->_4965.max_delta.rx.cck; + max_general = &il->_4965.max_delta.rx.general; + max_ht = &il->_4965.max_delta.rx.ofdm_ht; + + pos += il4965_stats_flag(il, buf, bufsz); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - OFDM:"); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "ina_cnt:", + le32_to_cpu(ofdm->ina_cnt), accum_ofdm->ina_cnt, + delta_ofdm->ina_cnt, max_ofdm->ina_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_cnt:", + le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, + delta_ofdm->fina_cnt, max_ofdm->fina_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", + le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, + delta_ofdm->plcp_err, max_ofdm->plcp_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", + le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, + delta_ofdm->crc32_err, max_ofdm->crc32_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", + le32_to_cpu(ofdm->overrun_err), accum_ofdm->overrun_err, + delta_ofdm->overrun_err, max_ofdm->overrun_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", + le32_to_cpu(ofdm->early_overrun_err), + accum_ofdm->early_overrun_err, + delta_ofdm->early_overrun_err, + max_ofdm->early_overrun_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", + le32_to_cpu(ofdm->crc32_good), accum_ofdm->crc32_good, + delta_ofdm->crc32_good, max_ofdm->crc32_good); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "false_alarm_cnt:", + le32_to_cpu(ofdm->false_alarm_cnt), + accum_ofdm->false_alarm_cnt, delta_ofdm->false_alarm_cnt, + max_ofdm->false_alarm_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_sync_err_cnt:", + le32_to_cpu(ofdm->fina_sync_err_cnt), + accum_ofdm->fina_sync_err_cnt, + delta_ofdm->fina_sync_err_cnt, + max_ofdm->fina_sync_err_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "sfd_timeout:", + le32_to_cpu(ofdm->sfd_timeout), accum_ofdm->sfd_timeout, + delta_ofdm->sfd_timeout, max_ofdm->sfd_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_timeout:", + le32_to_cpu(ofdm->fina_timeout), accum_ofdm->fina_timeout, + delta_ofdm->fina_timeout, max_ofdm->fina_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "unresponded_rts:", + le32_to_cpu(ofdm->unresponded_rts), + accum_ofdm->unresponded_rts, delta_ofdm->unresponded_rts, + max_ofdm->unresponded_rts); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "rxe_frame_lmt_ovrun:", + le32_to_cpu(ofdm->rxe_frame_limit_overrun), + accum_ofdm->rxe_frame_limit_overrun, + delta_ofdm->rxe_frame_limit_overrun, + max_ofdm->rxe_frame_limit_overrun); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ack_cnt:", + le32_to_cpu(ofdm->sent_ack_cnt), accum_ofdm->sent_ack_cnt, + delta_ofdm->sent_ack_cnt, max_ofdm->sent_ack_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_cts_cnt:", + le32_to_cpu(ofdm->sent_cts_cnt), accum_ofdm->sent_cts_cnt, + delta_ofdm->sent_cts_cnt, max_ofdm->sent_cts_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ba_rsp_cnt:", + le32_to_cpu(ofdm->sent_ba_rsp_cnt), + accum_ofdm->sent_ba_rsp_cnt, delta_ofdm->sent_ba_rsp_cnt, + max_ofdm->sent_ba_rsp_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_self_kill:", + le32_to_cpu(ofdm->dsp_self_kill), + accum_ofdm->dsp_self_kill, delta_ofdm->dsp_self_kill, + max_ofdm->dsp_self_kill); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", + le32_to_cpu(ofdm->mh_format_err), + accum_ofdm->mh_format_err, delta_ofdm->mh_format_err, + max_ofdm->mh_format_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "re_acq_main_rssi_sum:", + le32_to_cpu(ofdm->re_acq_main_rssi_sum), + accum_ofdm->re_acq_main_rssi_sum, + delta_ofdm->re_acq_main_rssi_sum, + max_ofdm->re_acq_main_rssi_sum); + + pos += + scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - CCK:"); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "ina_cnt:", + le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, + delta_cck->ina_cnt, max_cck->ina_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_cnt:", + le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, + delta_cck->fina_cnt, max_cck->fina_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", + le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, + delta_cck->plcp_err, max_cck->plcp_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", + le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, + delta_cck->crc32_err, max_cck->crc32_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", + le32_to_cpu(cck->overrun_err), accum_cck->overrun_err, + delta_cck->overrun_err, max_cck->overrun_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", + le32_to_cpu(cck->early_overrun_err), + accum_cck->early_overrun_err, + delta_cck->early_overrun_err, max_cck->early_overrun_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", + le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, + delta_cck->crc32_good, max_cck->crc32_good); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "false_alarm_cnt:", + le32_to_cpu(cck->false_alarm_cnt), + accum_cck->false_alarm_cnt, delta_cck->false_alarm_cnt, + max_cck->false_alarm_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_sync_err_cnt:", + le32_to_cpu(cck->fina_sync_err_cnt), + accum_cck->fina_sync_err_cnt, + delta_cck->fina_sync_err_cnt, max_cck->fina_sync_err_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "sfd_timeout:", + le32_to_cpu(cck->sfd_timeout), accum_cck->sfd_timeout, + delta_cck->sfd_timeout, max_cck->sfd_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_timeout:", + le32_to_cpu(cck->fina_timeout), accum_cck->fina_timeout, + delta_cck->fina_timeout, max_cck->fina_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "unresponded_rts:", + le32_to_cpu(cck->unresponded_rts), + accum_cck->unresponded_rts, delta_cck->unresponded_rts, + max_cck->unresponded_rts); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "rxe_frame_lmt_ovrun:", + le32_to_cpu(cck->rxe_frame_limit_overrun), + accum_cck->rxe_frame_limit_overrun, + delta_cck->rxe_frame_limit_overrun, + max_cck->rxe_frame_limit_overrun); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ack_cnt:", + le32_to_cpu(cck->sent_ack_cnt), accum_cck->sent_ack_cnt, + delta_cck->sent_ack_cnt, max_cck->sent_ack_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_cts_cnt:", + le32_to_cpu(cck->sent_cts_cnt), accum_cck->sent_cts_cnt, + delta_cck->sent_cts_cnt, max_cck->sent_cts_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ba_rsp_cnt:", + le32_to_cpu(cck->sent_ba_rsp_cnt), + accum_cck->sent_ba_rsp_cnt, delta_cck->sent_ba_rsp_cnt, + max_cck->sent_ba_rsp_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_self_kill:", + le32_to_cpu(cck->dsp_self_kill), accum_cck->dsp_self_kill, + delta_cck->dsp_self_kill, max_cck->dsp_self_kill); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", + le32_to_cpu(cck->mh_format_err), accum_cck->mh_format_err, + delta_cck->mh_format_err, max_cck->mh_format_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "re_acq_main_rssi_sum:", + le32_to_cpu(cck->re_acq_main_rssi_sum), + accum_cck->re_acq_main_rssi_sum, + delta_cck->re_acq_main_rssi_sum, + max_cck->re_acq_main_rssi_sum); + + pos += + scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - GENERAL:"); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "bogus_cts:", + le32_to_cpu(general->bogus_cts), accum_general->bogus_cts, + delta_general->bogus_cts, max_general->bogus_cts); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "bogus_ack:", + le32_to_cpu(general->bogus_ack), accum_general->bogus_ack, + delta_general->bogus_ack, max_general->bogus_ack); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "non_bssid_frames:", + le32_to_cpu(general->non_bssid_frames), + accum_general->non_bssid_frames, + delta_general->non_bssid_frames, + max_general->non_bssid_frames); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "filtered_frames:", + le32_to_cpu(general->filtered_frames), + accum_general->filtered_frames, + delta_general->filtered_frames, + max_general->filtered_frames); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "non_channel_beacons:", + le32_to_cpu(general->non_channel_beacons), + accum_general->non_channel_beacons, + delta_general->non_channel_beacons, + max_general->non_channel_beacons); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "channel_beacons:", + le32_to_cpu(general->channel_beacons), + accum_general->channel_beacons, + delta_general->channel_beacons, + max_general->channel_beacons); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "num_missed_bcon:", + le32_to_cpu(general->num_missed_bcon), + accum_general->num_missed_bcon, + delta_general->num_missed_bcon, + max_general->num_missed_bcon); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "adc_rx_saturation_time:", + le32_to_cpu(general->adc_rx_saturation_time), + accum_general->adc_rx_saturation_time, + delta_general->adc_rx_saturation_time, + max_general->adc_rx_saturation_time); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "ina_detect_search_tm:", + le32_to_cpu(general->ina_detection_search_time), + accum_general->ina_detection_search_time, + delta_general->ina_detection_search_time, + max_general->ina_detection_search_time); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "beacon_silence_rssi_a:", + le32_to_cpu(general->beacon_silence_rssi_a), + accum_general->beacon_silence_rssi_a, + delta_general->beacon_silence_rssi_a, + max_general->beacon_silence_rssi_a); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "beacon_silence_rssi_b:", + le32_to_cpu(general->beacon_silence_rssi_b), + accum_general->beacon_silence_rssi_b, + delta_general->beacon_silence_rssi_b, + max_general->beacon_silence_rssi_b); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "beacon_silence_rssi_c:", + le32_to_cpu(general->beacon_silence_rssi_c), + accum_general->beacon_silence_rssi_c, + delta_general->beacon_silence_rssi_c, + max_general->beacon_silence_rssi_c); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "interference_data_flag:", + le32_to_cpu(general->interference_data_flag), + accum_general->interference_data_flag, + delta_general->interference_data_flag, + max_general->interference_data_flag); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "channel_load:", + le32_to_cpu(general->channel_load), + accum_general->channel_load, delta_general->channel_load, + max_general->channel_load); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_false_alarms:", + le32_to_cpu(general->dsp_false_alarms), + accum_general->dsp_false_alarms, + delta_general->dsp_false_alarms, + max_general->dsp_false_alarms); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_a:", + le32_to_cpu(general->beacon_rssi_a), + accum_general->beacon_rssi_a, + delta_general->beacon_rssi_a, max_general->beacon_rssi_a); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_b:", + le32_to_cpu(general->beacon_rssi_b), + accum_general->beacon_rssi_b, + delta_general->beacon_rssi_b, max_general->beacon_rssi_b); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_c:", + le32_to_cpu(general->beacon_rssi_c), + accum_general->beacon_rssi_c, + delta_general->beacon_rssi_c, max_general->beacon_rssi_c); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_a:", + le32_to_cpu(general->beacon_energy_a), + accum_general->beacon_energy_a, + delta_general->beacon_energy_a, + max_general->beacon_energy_a); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_b:", + le32_to_cpu(general->beacon_energy_b), + accum_general->beacon_energy_b, + delta_general->beacon_energy_b, + max_general->beacon_energy_b); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_c:", + le32_to_cpu(general->beacon_energy_c), + accum_general->beacon_energy_c, + delta_general->beacon_energy_c, + max_general->beacon_energy_c); + + pos += + scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - OFDM_HT:"); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", + le32_to_cpu(ht->plcp_err), accum_ht->plcp_err, + delta_ht->plcp_err, max_ht->plcp_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", + le32_to_cpu(ht->overrun_err), accum_ht->overrun_err, + delta_ht->overrun_err, max_ht->overrun_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", + le32_to_cpu(ht->early_overrun_err), + accum_ht->early_overrun_err, delta_ht->early_overrun_err, + max_ht->early_overrun_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", + le32_to_cpu(ht->crc32_good), accum_ht->crc32_good, + delta_ht->crc32_good, max_ht->crc32_good); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", + le32_to_cpu(ht->crc32_err), accum_ht->crc32_err, + delta_ht->crc32_err, max_ht->crc32_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", + le32_to_cpu(ht->mh_format_err), accum_ht->mh_format_err, + delta_ht->mh_format_err, max_ht->mh_format_err); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_crc32_good:", + le32_to_cpu(ht->agg_crc32_good), accum_ht->agg_crc32_good, + delta_ht->agg_crc32_good, max_ht->agg_crc32_good); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_mpdu_cnt:", + le32_to_cpu(ht->agg_mpdu_cnt), accum_ht->agg_mpdu_cnt, + delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_cnt:", + le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt, + delta_ht->agg_cnt, max_ht->agg_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "unsupport_mcs:", + le32_to_cpu(ht->unsupport_mcs), accum_ht->unsupport_mcs, + delta_ht->unsupport_mcs, max_ht->unsupport_mcs); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il4965_ucode_tx_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + int pos = 0; + char *buf; + int bufsz = (sizeof(struct stats_tx) * 48) + 250; + ssize_t ret; + struct stats_tx *tx, *accum_tx, *delta_tx, *max_tx; + + if (!il_is_alive(il)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IL_ERR("Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* the statistic information display here is based on + * the last stats notification from uCode + * might not reflect the current uCode activity + */ + tx = &il->_4965.stats.tx; + accum_tx = &il->_4965.accum_stats.tx; + delta_tx = &il->_4965.delta_stats.tx; + max_tx = &il->_4965.max_delta.tx; + + pos += il4965_stats_flag(il, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Tx:"); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "preamble:", + le32_to_cpu(tx->preamble_cnt), accum_tx->preamble_cnt, + delta_tx->preamble_cnt, max_tx->preamble_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "rx_detected_cnt:", + le32_to_cpu(tx->rx_detected_cnt), + accum_tx->rx_detected_cnt, delta_tx->rx_detected_cnt, + max_tx->rx_detected_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "bt_prio_defer_cnt:", + le32_to_cpu(tx->bt_prio_defer_cnt), + accum_tx->bt_prio_defer_cnt, delta_tx->bt_prio_defer_cnt, + max_tx->bt_prio_defer_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "bt_prio_kill_cnt:", + le32_to_cpu(tx->bt_prio_kill_cnt), + accum_tx->bt_prio_kill_cnt, delta_tx->bt_prio_kill_cnt, + max_tx->bt_prio_kill_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "few_bytes_cnt:", + le32_to_cpu(tx->few_bytes_cnt), accum_tx->few_bytes_cnt, + delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "cts_timeout:", + le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, + delta_tx->cts_timeout, max_tx->cts_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "ack_timeout:", + le32_to_cpu(tx->ack_timeout), accum_tx->ack_timeout, + delta_tx->ack_timeout, max_tx->ack_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "expected_ack_cnt:", + le32_to_cpu(tx->expected_ack_cnt), + accum_tx->expected_ack_cnt, delta_tx->expected_ack_cnt, + max_tx->expected_ack_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "actual_ack_cnt:", + le32_to_cpu(tx->actual_ack_cnt), accum_tx->actual_ack_cnt, + delta_tx->actual_ack_cnt, max_tx->actual_ack_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "dump_msdu_cnt:", + le32_to_cpu(tx->dump_msdu_cnt), accum_tx->dump_msdu_cnt, + delta_tx->dump_msdu_cnt, max_tx->dump_msdu_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "abort_nxt_frame_mismatch:", + le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt), + accum_tx->burst_abort_next_frame_mismatch_cnt, + delta_tx->burst_abort_next_frame_mismatch_cnt, + max_tx->burst_abort_next_frame_mismatch_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "abort_missing_nxt_frame:", + le32_to_cpu(tx->burst_abort_missing_next_frame_cnt), + accum_tx->burst_abort_missing_next_frame_cnt, + delta_tx->burst_abort_missing_next_frame_cnt, + max_tx->burst_abort_missing_next_frame_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "cts_timeout_collision:", + le32_to_cpu(tx->cts_timeout_collision), + accum_tx->cts_timeout_collision, + delta_tx->cts_timeout_collision, + max_tx->cts_timeout_collision); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "ack_ba_timeout_collision:", + le32_to_cpu(tx->ack_or_ba_timeout_collision), + accum_tx->ack_or_ba_timeout_collision, + delta_tx->ack_or_ba_timeout_collision, + max_tx->ack_or_ba_timeout_collision); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "agg ba_timeout:", + le32_to_cpu(tx->agg.ba_timeout), accum_tx->agg.ba_timeout, + delta_tx->agg.ba_timeout, max_tx->agg.ba_timeout); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "agg ba_resched_frames:", + le32_to_cpu(tx->agg.ba_reschedule_frames), + accum_tx->agg.ba_reschedule_frames, + delta_tx->agg.ba_reschedule_frames, + max_tx->agg.ba_reschedule_frames); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "agg scd_query_agg_frame:", + le32_to_cpu(tx->agg.scd_query_agg_frame_cnt), + accum_tx->agg.scd_query_agg_frame_cnt, + delta_tx->agg.scd_query_agg_frame_cnt, + max_tx->agg.scd_query_agg_frame_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "agg scd_query_no_agg:", + le32_to_cpu(tx->agg.scd_query_no_agg), + accum_tx->agg.scd_query_no_agg, + delta_tx->agg.scd_query_no_agg, + max_tx->agg.scd_query_no_agg); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "agg scd_query_agg:", + le32_to_cpu(tx->agg.scd_query_agg), + accum_tx->agg.scd_query_agg, delta_tx->agg.scd_query_agg, + max_tx->agg.scd_query_agg); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "agg scd_query_mismatch:", + le32_to_cpu(tx->agg.scd_query_mismatch), + accum_tx->agg.scd_query_mismatch, + delta_tx->agg.scd_query_mismatch, + max_tx->agg.scd_query_mismatch); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "agg frame_not_ready:", + le32_to_cpu(tx->agg.frame_not_ready), + accum_tx->agg.frame_not_ready, + delta_tx->agg.frame_not_ready, + max_tx->agg.frame_not_ready); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "agg underrun:", + le32_to_cpu(tx->agg.underrun), accum_tx->agg.underrun, + delta_tx->agg.underrun, max_tx->agg.underrun); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "agg bt_prio_kill:", + le32_to_cpu(tx->agg.bt_prio_kill), + accum_tx->agg.bt_prio_kill, delta_tx->agg.bt_prio_kill, + max_tx->agg.bt_prio_kill); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "agg rx_ba_rsp_cnt:", + le32_to_cpu(tx->agg.rx_ba_rsp_cnt), + accum_tx->agg.rx_ba_rsp_cnt, delta_tx->agg.rx_ba_rsp_cnt, + max_tx->agg.rx_ba_rsp_cnt); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il4965_ucode_general_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct stats_general) * 10 + 300; + ssize_t ret; + struct stats_general_common *general, *accum_general; + struct stats_general_common *delta_general, *max_general; + struct stats_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; + struct stats_div *div, *accum_div, *delta_div, *max_div; + + if (!il_is_alive(il)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IL_ERR("Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* the statistic information display here is based on + * the last stats notification from uCode + * might not reflect the current uCode activity + */ + general = &il->_4965.stats.general.common; + dbg = &il->_4965.stats.general.common.dbg; + div = &il->_4965.stats.general.common.div; + accum_general = &il->_4965.accum_stats.general.common; + accum_dbg = &il->_4965.accum_stats.general.common.dbg; + accum_div = &il->_4965.accum_stats.general.common.div; + delta_general = &il->_4965.delta_stats.general.common; + max_general = &il->_4965.max_delta.general.common; + delta_dbg = &il->_4965.delta_stats.general.common.dbg; + max_dbg = &il->_4965.max_delta.general.common.dbg; + delta_div = &il->_4965.delta_stats.general.common.div; + max_div = &il->_4965.max_delta.general.common.div; + + pos += il4965_stats_flag(il, buf, bufsz); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_General:"); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_value, "temperature:", + le32_to_cpu(general->temperature)); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_value, "ttl_timestamp:", + le32_to_cpu(general->ttl_timestamp)); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "burst_check:", + le32_to_cpu(dbg->burst_check), accum_dbg->burst_check, + delta_dbg->burst_check, max_dbg->burst_check); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "burst_count:", + le32_to_cpu(dbg->burst_count), accum_dbg->burst_count, + delta_dbg->burst_count, max_dbg->burst_count); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, + "wait_for_silence_timeout_count:", + le32_to_cpu(dbg->wait_for_silence_timeout_cnt), + accum_dbg->wait_for_silence_timeout_cnt, + delta_dbg->wait_for_silence_timeout_cnt, + max_dbg->wait_for_silence_timeout_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "sleep_time:", + le32_to_cpu(general->sleep_time), + accum_general->sleep_time, delta_general->sleep_time, + max_general->sleep_time); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "slots_out:", + le32_to_cpu(general->slots_out), accum_general->slots_out, + delta_general->slots_out, max_general->slots_out); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "slots_idle:", + le32_to_cpu(general->slots_idle), + accum_general->slots_idle, delta_general->slots_idle, + max_general->slots_idle); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "tx_on_a:", + le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, + delta_div->tx_on_a, max_div->tx_on_a); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "tx_on_b:", + le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, + delta_div->tx_on_b, max_div->tx_on_b); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "exec_time:", + le32_to_cpu(div->exec_time), accum_div->exec_time, + delta_div->exec_time, max_div->exec_time); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "probe_time:", + le32_to_cpu(div->probe_time), accum_div->probe_time, + delta_div->probe_time, max_div->probe_time); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "rx_enable_counter:", + le32_to_cpu(general->rx_enable_counter), + accum_general->rx_enable_counter, + delta_general->rx_enable_counter, + max_general->rx_enable_counter); + pos += + scnprintf(buf + pos, bufsz - pos, fmt_table, "num_of_sos_states:", + le32_to_cpu(general->num_of_sos_states), + accum_general->num_of_sos_states, + delta_general->num_of_sos_states, + max_general->num_of_sos_states); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +const struct il_debugfs_ops il4965_debugfs_ops = { + .rx_stats_read = il4965_ucode_rx_stats_read, + .tx_stats_read = il4965_ucode_tx_stats_read, + .general_stats_read = il4965_ucode_general_stats_read, +}; diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c new file mode 100644 index 000000000000..6656215a13a9 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -0,0 +1,6868 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define DRV_NAME "iwl4965" + +#include "common.h" +#include "4965.h" + +/****************************************************************************** + * + * module boiler plate + * + ******************************************************************************/ + +/* + * module name, copyright, version, etc. + */ +#define DRV_DESCRIPTION "Intel(R) Wireless WiFi 4965 driver for Linux" + +#ifdef CONFIG_IWLEGACY_DEBUG +#define VD "d" +#else +#define VD +#endif + +#define DRV_VERSION IWLWIFI_VERSION VD + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("iwl4965"); + +void +il4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status) +{ + if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { + IL_ERR("Tx flush command to flush out all frames\n"); + if (!test_bit(S_EXIT_PENDING, &il->status)) + queue_work(il->workqueue, &il->tx_flush); + } +} + +/* + * EEPROM + */ +struct il_mod_params il4965_mod_params = { + .restart_fw = 1, + /* the rest are 0 by default */ +}; + +void +il4965_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq) +{ + unsigned long flags; + int i; + spin_lock_irqsave(&rxq->lock, flags); + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + /* Fill the rx_used queue with _all_ of the Rx buffers */ + for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { + /* In the reset function, these buffers may have been allocated + * to an SKB, so we need to unmap and free potential storage */ + if (rxq->pool[i].page != NULL) { + pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, + PAGE_SIZE << il->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + __il_free_pages(il, rxq->pool[i].page); + rxq->pool[i].page = NULL; + } + list_add_tail(&rxq->pool[i].list, &rxq->rx_used); + } + + for (i = 0; i < RX_QUEUE_SIZE; i++) + rxq->queue[i] = NULL; + + /* Set us so that we have processed and used all buffers, but have + * not restocked the Rx queue with fresh buffers */ + rxq->read = rxq->write = 0; + rxq->write_actual = 0; + rxq->free_count = 0; + spin_unlock_irqrestore(&rxq->lock, flags); +} + +int +il4965_rx_init(struct il_priv *il, struct il_rx_queue *rxq) +{ + u32 rb_size; + const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ + u32 rb_timeout = 0; + + if (il->cfg->mod_params->amsdu_size_8K) + rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; + else + rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; + + /* Stop Rx DMA */ + il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0); + + /* Reset driver's Rx queue write idx */ + il_wr(il, FH49_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); + + /* Tell device where to find RBD circular buffer in DRAM */ + il_wr(il, FH49_RSCSR_CHNL0_RBDCB_BASE_REG, (u32) (rxq->bd_dma >> 8)); + + /* Tell device where in DRAM to update its Rx status */ + il_wr(il, FH49_RSCSR_CHNL0_STTS_WPTR_REG, rxq->rb_stts_dma >> 4); + + /* Enable Rx DMA + * Direct rx interrupts to hosts + * Rx buffer size 4 or 8k + * RB timeout 0x10 + * 256 RBDs + */ + il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, + FH49_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | + FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | + FH49_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | + rb_size | + (rb_timeout << FH49_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | + (rfdnlog << FH49_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); + + /* Set interrupt coalescing timer to default (2048 usecs) */ + il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_TIMEOUT_DEF); + + return 0; +} + +static void +il4965_set_pwr_vmain(struct il_priv *il) +{ +/* + * (for documentation purposes) + * to set power to V_AUX, do: + + if (pci_pme_capable(il->pci_dev, PCI_D3cold)) + il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VAUX, + ~APMG_PS_CTRL_MSK_PWR_SRC); + */ + + il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, + ~APMG_PS_CTRL_MSK_PWR_SRC); +} + +int +il4965_hw_nic_init(struct il_priv *il) +{ + unsigned long flags; + struct il_rx_queue *rxq = &il->rxq; + int ret; + + spin_lock_irqsave(&il->lock, flags); + il_apm_init(il); + /* Set interrupt coalescing calibration timer to default (512 usecs) */ + il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_CALIB_TIMEOUT_DEF); + spin_unlock_irqrestore(&il->lock, flags); + + il4965_set_pwr_vmain(il); + il4965_nic_config(il); + + /* Allocate the RX queue, or reset if it is already allocated */ + if (!rxq->bd) { + ret = il_rx_queue_alloc(il); + if (ret) { + IL_ERR("Unable to initialize Rx queue\n"); + return -ENOMEM; + } + } else + il4965_rx_queue_reset(il, rxq); + + il4965_rx_replenish(il); + + il4965_rx_init(il, rxq); + + spin_lock_irqsave(&il->lock, flags); + + rxq->need_update = 1; + il_rx_queue_update_write_ptr(il, rxq); + + spin_unlock_irqrestore(&il->lock, flags); + + /* Allocate or reset and init all Tx and Command queues */ + if (!il->txq) { + ret = il4965_txq_ctx_alloc(il); + if (ret) + return ret; + } else + il4965_txq_ctx_reset(il); + + set_bit(S_INIT, &il->status); + + return 0; +} + +/** + * il4965_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr + */ +static inline __le32 +il4965_dma_addr2rbd_ptr(struct il_priv *il, dma_addr_t dma_addr) +{ + return cpu_to_le32((u32) (dma_addr >> 8)); +} + +/** + * il4965_rx_queue_restock - refill RX queue from pre-allocated pool + * + * If there are slots in the RX queue that need to be restocked, + * and we have free pre-allocated buffers, fill the ranks as much + * as we can, pulling from rx_free. + * + * This moves the 'write' idx forward to catch up with 'processed', and + * also updates the memory address in the firmware to reference the new + * target buffer. + */ +void +il4965_rx_queue_restock(struct il_priv *il) +{ + struct il_rx_queue *rxq = &il->rxq; + struct list_head *element; + struct il_rx_buf *rxb; + unsigned long flags; + + spin_lock_irqsave(&rxq->lock, flags); + while (il_rx_queue_space(rxq) > 0 && rxq->free_count) { + /* The overwritten rxb must be a used one */ + rxb = rxq->queue[rxq->write]; + BUG_ON(rxb && rxb->page); + + /* Get next free Rx buffer, remove from free list */ + element = rxq->rx_free.next; + rxb = list_entry(element, struct il_rx_buf, list); + list_del(element); + + /* Point to Rx buffer via next RBD in circular buffer */ + rxq->bd[rxq->write] = + il4965_dma_addr2rbd_ptr(il, rxb->page_dma); + rxq->queue[rxq->write] = rxb; + rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; + rxq->free_count--; + } + spin_unlock_irqrestore(&rxq->lock, flags); + /* If the pre-allocated buffer pool is dropping low, schedule to + * refill it */ + if (rxq->free_count <= RX_LOW_WATERMARK) + queue_work(il->workqueue, &il->rx_replenish); + + /* If we've added more space for the firmware to place data, tell it. + * Increment device's write pointer in multiples of 8. */ + if (rxq->write_actual != (rxq->write & ~0x7)) { + spin_lock_irqsave(&rxq->lock, flags); + rxq->need_update = 1; + spin_unlock_irqrestore(&rxq->lock, flags); + il_rx_queue_update_write_ptr(il, rxq); + } +} + +/** + * il4965_rx_replenish - Move all used packet from rx_used to rx_free + * + * When moving to rx_free an SKB is allocated for the slot. + * + * Also restock the Rx queue via il_rx_queue_restock. + * This is called as a scheduled work item (except for during initialization) + */ +static void +il4965_rx_allocate(struct il_priv *il, gfp_t priority) +{ + struct il_rx_queue *rxq = &il->rxq; + struct list_head *element; + struct il_rx_buf *rxb; + struct page *page; + dma_addr_t page_dma; + unsigned long flags; + gfp_t gfp_mask = priority; + + while (1) { + spin_lock_irqsave(&rxq->lock, flags); + if (list_empty(&rxq->rx_used)) { + spin_unlock_irqrestore(&rxq->lock, flags); + return; + } + spin_unlock_irqrestore(&rxq->lock, flags); + + if (rxq->free_count > RX_LOW_WATERMARK) + gfp_mask |= __GFP_NOWARN; + + if (il->hw_params.rx_page_order > 0) + gfp_mask |= __GFP_COMP; + + /* Alloc a new receive buffer */ + page = alloc_pages(gfp_mask, il->hw_params.rx_page_order); + if (!page) { + if (net_ratelimit()) + D_INFO("alloc_pages failed, " "order: %d\n", + il->hw_params.rx_page_order); + + if (rxq->free_count <= RX_LOW_WATERMARK && + net_ratelimit()) + IL_ERR("Failed to alloc_pages with %s. " + "Only %u free buffers remaining.\n", + priority == + GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL", + rxq->free_count); + /* We don't reschedule replenish work here -- we will + * call the restock method and if it still needs + * more buffers it will schedule replenish */ + return; + } + + /* Get physical address of the RB */ + page_dma = + pci_map_page(il->pci_dev, page, 0, + PAGE_SIZE << il->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + if (unlikely(pci_dma_mapping_error(il->pci_dev, page_dma))) { + __free_pages(page, il->hw_params.rx_page_order); + break; + } + + spin_lock_irqsave(&rxq->lock, flags); + + if (list_empty(&rxq->rx_used)) { + spin_unlock_irqrestore(&rxq->lock, flags); + pci_unmap_page(il->pci_dev, page_dma, + PAGE_SIZE << il->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + __free_pages(page, il->hw_params.rx_page_order); + return; + } + + element = rxq->rx_used.next; + rxb = list_entry(element, struct il_rx_buf, list); + list_del(element); + + BUG_ON(rxb->page); + + rxb->page = page; + rxb->page_dma = page_dma; + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + il->alloc_rxb_page++; + + spin_unlock_irqrestore(&rxq->lock, flags); + } +} + +void +il4965_rx_replenish(struct il_priv *il) +{ + unsigned long flags; + + il4965_rx_allocate(il, GFP_KERNEL); + + spin_lock_irqsave(&il->lock, flags); + il4965_rx_queue_restock(il); + spin_unlock_irqrestore(&il->lock, flags); +} + +void +il4965_rx_replenish_now(struct il_priv *il) +{ + il4965_rx_allocate(il, GFP_ATOMIC); + + il4965_rx_queue_restock(il); +} + +/* Assumes that the skb field of the buffers in 'pool' is kept accurate. + * If an SKB has been detached, the POOL needs to have its SKB set to NULL + * This free routine walks the list of POOL entries and if SKB is set to + * non NULL it is unmapped and freed + */ +void +il4965_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq) +{ + int i; + for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { + if (rxq->pool[i].page != NULL) { + pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, + PAGE_SIZE << il->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + __il_free_pages(il, rxq->pool[i].page); + rxq->pool[i].page = NULL; + } + } + + dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, + rxq->bd_dma); + dma_free_coherent(&il->pci_dev->dev, sizeof(struct il_rb_status), + rxq->rb_stts, rxq->rb_stts_dma); + rxq->bd = NULL; + rxq->rb_stts = NULL; +} + +int +il4965_rxq_stop(struct il_priv *il) +{ + int ret; + + _il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0); + ret = _il_poll_bit(il, FH49_MEM_RSSR_RX_STATUS_REG, + FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, + FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, + 1000); + if (ret < 0) + IL_ERR("Can't stop Rx DMA.\n"); + + return 0; +} + +int +il4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band) +{ + int idx = 0; + int band_offset = 0; + + /* HT rate format: mac80211 wants an MCS number, which is just LSB */ + if (rate_n_flags & RATE_MCS_HT_MSK) { + idx = (rate_n_flags & 0xff); + return idx; + /* Legacy rate format, search for match in table */ + } else { + if (band == IEEE80211_BAND_5GHZ) + band_offset = IL_FIRST_OFDM_RATE; + for (idx = band_offset; idx < RATE_COUNT_LEGACY; idx++) + if (il_rates[idx].plcp == (rate_n_flags & 0xFF)) + return idx - band_offset; + } + + return -1; +} + +static int +il4965_calc_rssi(struct il_priv *il, struct il_rx_phy_res *rx_resp) +{ + /* data from PHY/DSP regarding signal strength, etc., + * contents are always there, not configurable by host. */ + struct il4965_rx_non_cfg_phy *ncphy = + (struct il4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy_buf; + u32 agc = + (le16_to_cpu(ncphy->agc_info) & IL49_AGC_DB_MASK) >> + IL49_AGC_DB_POS; + + u32 valid_antennae = + (le16_to_cpu(rx_resp->phy_flags) & IL49_RX_PHY_FLAGS_ANTENNAE_MASK) + >> IL49_RX_PHY_FLAGS_ANTENNAE_OFFSET; + u8 max_rssi = 0; + u32 i; + + /* Find max rssi among 3 possible receivers. + * These values are measured by the digital signal processor (DSP). + * They should stay fairly constant even as the signal strength varies, + * if the radio's automatic gain control (AGC) is working right. + * AGC value (see below) will provide the "interesting" info. */ + for (i = 0; i < 3; i++) + if (valid_antennae & (1 << i)) + max_rssi = max(ncphy->rssi_info[i << 1], max_rssi); + + D_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n", + ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4], + max_rssi, agc); + + /* dBm = max_rssi dB - agc dB - constant. + * Higher AGC (higher radio gain) means lower signal. */ + return max_rssi - agc - IL4965_RSSI_OFFSET; +} + +static u32 +il4965_translate_rx_status(struct il_priv *il, u32 decrypt_in) +{ + u32 decrypt_out = 0; + + if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) == + RX_RES_STATUS_STATION_FOUND) + decrypt_out |= + (RX_RES_STATUS_STATION_FOUND | + RX_RES_STATUS_NO_STATION_INFO_MISMATCH); + + decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK); + + /* packet was not encrypted */ + if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == + RX_RES_STATUS_SEC_TYPE_NONE) + return decrypt_out; + + /* packet was encrypted with unknown alg */ + if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == + RX_RES_STATUS_SEC_TYPE_ERR) + return decrypt_out; + + /* decryption was not done in HW */ + if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) != + RX_MPDU_RES_STATUS_DEC_DONE_MSK) + return decrypt_out; + + switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) { + + case RX_RES_STATUS_SEC_TYPE_CCMP: + /* alg is CCM: check MIC only */ + if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK)) + /* Bad MIC */ + decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; + else + decrypt_out |= RX_RES_STATUS_DECRYPT_OK; + + break; + + case RX_RES_STATUS_SEC_TYPE_TKIP: + if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) { + /* Bad TTAK */ + decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK; + break; + } + /* fall through if TTAK OK */ + default: + if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK)) + decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; + else + decrypt_out |= RX_RES_STATUS_DECRYPT_OK; + break; + } + + D_RX("decrypt_in:0x%x decrypt_out = 0x%x\n", decrypt_in, decrypt_out); + + return decrypt_out; +} + +#define SMALL_PACKET_SIZE 256 + +static void +il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr, + u32 len, u32 ampdu_status, struct il_rx_buf *rxb, + struct ieee80211_rx_status *stats) +{ + struct sk_buff *skb; + __le16 fc = hdr->frame_control; + + /* We only process data packets if the interface is open */ + if (unlikely(!il->is_open)) { + D_DROP("Dropping packet while interface is not open.\n"); + return; + } + + if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) { + il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Woke queues - frame received on passive channel\n"); + } + + /* In case of HW accelerated crypto and bad decryption, drop */ + if (!il->cfg->mod_params->sw_crypto && + il_set_decrypted_flag(il, hdr, ampdu_status, stats)) + return; + + skb = dev_alloc_skb(SMALL_PACKET_SIZE); + if (!skb) { + IL_ERR("dev_alloc_skb failed\n"); + return; + } + + if (len <= SMALL_PACKET_SIZE) { + memcpy(skb_put(skb, len), hdr, len); + } else { + skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), + len, PAGE_SIZE << il->hw_params.rx_page_order); + il->alloc_rxb_page--; + rxb->page = NULL; + } + + il_update_stats(il, false, fc, len); + memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); + + ieee80211_rx(il->hw, skb); +} + +/* Called for N_RX (legacy ABG frames), or + * N_RX_MPDU (HT high-throughput N frames). */ +static void +il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct ieee80211_hdr *header; + struct ieee80211_rx_status rx_status = {}; + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_rx_phy_res *phy_res; + __le32 rx_pkt_status; + struct il_rx_mpdu_res_start *amsdu; + u32 len; + u32 ampdu_status; + u32 rate_n_flags; + + /** + * N_RX and N_RX_MPDU are handled differently. + * N_RX: physical layer info is in this buffer + * N_RX_MPDU: physical layer info was sent in separate + * command and cached in il->last_phy_res + * + * Here we set up local variables depending on which command is + * received. + */ + if (pkt->hdr.cmd == N_RX) { + phy_res = (struct il_rx_phy_res *)pkt->u.raw; + header = + (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*phy_res) + + phy_res->cfg_phy_cnt); + + len = le16_to_cpu(phy_res->byte_count); + rx_pkt_status = + *(__le32 *) (pkt->u.raw + sizeof(*phy_res) + + phy_res->cfg_phy_cnt + len); + ampdu_status = le32_to_cpu(rx_pkt_status); + } else { + if (!il->_4965.last_phy_res_valid) { + IL_ERR("MPDU frame without cached PHY data\n"); + return; + } + phy_res = &il->_4965.last_phy_res; + amsdu = (struct il_rx_mpdu_res_start *)pkt->u.raw; + header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu)); + len = le16_to_cpu(amsdu->byte_count); + rx_pkt_status = *(__le32 *) (pkt->u.raw + sizeof(*amsdu) + len); + ampdu_status = + il4965_translate_rx_status(il, le32_to_cpu(rx_pkt_status)); + } + + if ((unlikely(phy_res->cfg_phy_cnt > 20))) { + D_DROP("dsp size out of range [0,20]: %d\n", + phy_res->cfg_phy_cnt); + return; + } + + if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || + !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { + D_RX("Bad CRC or FIFO: 0x%08X.\n", le32_to_cpu(rx_pkt_status)); + return; + } + + /* This will be used in several places later */ + rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); + + /* rx_status carries information about the packet to mac80211 */ + rx_status.mactime = le64_to_cpu(phy_res->timestamp); + rx_status.band = + (phy_res-> + phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ; + rx_status.freq = + ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel), + rx_status.band); + rx_status.rate_idx = + il4965_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band); + rx_status.flag = 0; + + /* TSF isn't reliable. In order to allow smooth user experience, + * this W/A doesn't propagate it to the mac80211 */ + /*rx_status.flag |= RX_FLAG_MACTIME_START; */ + + il->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); + + /* Find max signal strength (dBm) among 3 antenna/receiver chains */ + rx_status.signal = il4965_calc_rssi(il, phy_res); + + D_STATS("Rssi %d, TSF %llu\n", rx_status.signal, + (unsigned long long)rx_status.mactime); + + /* + * "antenna number" + * + * It seems that the antenna field in the phy flags value + * is actually a bit field. This is undefined by radiotap, + * it wants an actual antenna number but I always get "7" + * for most legacy frames I receive indicating that the + * same frame was received on all three RX chains. + * + * I think this field should be removed in favor of a + * new 802.11n radiotap field "RX chains" that is defined + * as a bitmask. + */ + rx_status.antenna = + (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> + RX_RES_PHY_FLAGS_ANTENNA_POS; + + /* set the preamble flag if appropriate */ + if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) + rx_status.flag |= RX_FLAG_SHORTPRE; + + /* Set up the HT phy flags */ + if (rate_n_flags & RATE_MCS_HT_MSK) + rx_status.flag |= RX_FLAG_HT; + if (rate_n_flags & RATE_MCS_HT40_MSK) + rx_status.flag |= RX_FLAG_40MHZ; + if (rate_n_flags & RATE_MCS_SGI_MSK) + rx_status.flag |= RX_FLAG_SHORT_GI; + + if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) { + /* We know which subframes of an A-MPDU belong + * together since we get a single PHY response + * from the firmware for all of them. + */ + + rx_status.flag |= RX_FLAG_AMPDU_DETAILS; + rx_status.ampdu_reference = il->_4965.ampdu_ref; + } + + il4965_pass_packet_to_mac80211(il, header, len, ampdu_status, rxb, + &rx_status); +} + +/* Cache phy data (Rx signal strength, etc) for HT frame (N_RX_PHY). + * This will be used later in il_hdl_rx() for N_RX_MPDU. */ +static void +il4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + il->_4965.last_phy_res_valid = true; + il->_4965.ampdu_ref++; + memcpy(&il->_4965.last_phy_res, pkt->u.raw, + sizeof(struct il_rx_phy_res)); +} + +static int +il4965_get_channels_for_scan(struct il_priv *il, struct ieee80211_vif *vif, + enum ieee80211_band band, u8 is_active, + u8 n_probes, struct il_scan_channel *scan_ch) +{ + struct ieee80211_channel *chan; + const struct ieee80211_supported_band *sband; + const struct il_channel_info *ch_info; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int added, i; + u16 channel; + + sband = il_get_hw_mode(il, band); + if (!sband) + return 0; + + active_dwell = il_get_active_dwell_time(il, band, n_probes); + passive_dwell = il_get_passive_dwell_time(il, band, vif); + + if (passive_dwell <= active_dwell) + passive_dwell = active_dwell + 1; + + for (i = 0, added = 0; i < il->scan_request->n_channels; i++) { + chan = il->scan_request->channels[i]; + + if (chan->band != band) + continue; + + channel = chan->hw_value; + scan_ch->channel = cpu_to_le16(channel); + + ch_info = il_get_channel_info(il, band, channel); + if (!il_is_channel_valid(ch_info)) { + D_SCAN("Channel %d is INVALID for this band.\n", + channel); + continue; + } + + if (!is_active || il_is_channel_passive(ch_info) || + (chan->flags & IEEE80211_CHAN_NO_IR)) + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + else + scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; + + if (n_probes) + scan_ch->type |= IL_SCAN_PROBE_MASK(n_probes); + + scan_ch->active_dwell = cpu_to_le16(active_dwell); + scan_ch->passive_dwell = cpu_to_le16(passive_dwell); + + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + + /* NOTE: if we were doing 6Mb OFDM for scans we'd use + * power level: + * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; + */ + if (band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + + D_SCAN("Scanning ch=%d prob=0x%X [%s %d]\n", channel, + le32_to_cpu(scan_ch->type), + (scan_ch-> + type & SCAN_CHANNEL_TYPE_ACTIVE) ? "ACTIVE" : "PASSIVE", + (scan_ch-> + type & SCAN_CHANNEL_TYPE_ACTIVE) ? active_dwell : + passive_dwell); + + scan_ch++; + added++; + } + + D_SCAN("total channels to scan %d\n", added); + return added; +} + +static void +il4965_toggle_tx_ant(struct il_priv *il, u8 *ant, u8 valid) +{ + int i; + u8 ind = *ant; + + for (i = 0; i < RATE_ANT_NUM - 1; i++) { + ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; + if (valid & BIT(ind)) { + *ant = ind; + return; + } + } +} + +int +il4965_request_scan(struct il_priv *il, struct ieee80211_vif *vif) +{ + struct il_host_cmd cmd = { + .id = C_SCAN, + .len = sizeof(struct il_scan_cmd), + .flags = CMD_SIZE_HUGE, + }; + struct il_scan_cmd *scan; + u32 rate_flags = 0; + u16 cmd_len; + u16 rx_chain = 0; + enum ieee80211_band band; + u8 n_probes = 0; + u8 rx_ant = il->hw_params.valid_rx_ant; + u8 rate; + bool is_active = false; + int chan_mod; + u8 active_chains; + u8 scan_tx_antennas = il->hw_params.valid_tx_ant; + int ret; + + lockdep_assert_held(&il->mutex); + + if (!il->scan_cmd) { + il->scan_cmd = + kmalloc(sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE, + GFP_KERNEL); + if (!il->scan_cmd) { + D_SCAN("fail to allocate memory for scan\n"); + return -ENOMEM; + } + } + scan = il->scan_cmd; + memset(scan, 0, sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE); + + scan->quiet_plcp_th = IL_PLCP_QUIET_THRESH; + scan->quiet_time = IL_ACTIVE_QUIET_TIME; + + if (il_is_any_associated(il)) { + u16 interval; + u32 extra; + u32 suspend_time = 100; + u32 scan_suspend_time = 100; + + D_INFO("Scanning while associated...\n"); + interval = vif->bss_conf.beacon_int; + + scan->suspend_time = 0; + scan->max_out_time = cpu_to_le32(200 * 1024); + if (!interval) + interval = suspend_time; + + extra = (suspend_time / interval) << 22; + scan_suspend_time = + (extra | ((suspend_time % interval) * 1024)); + scan->suspend_time = cpu_to_le32(scan_suspend_time); + D_SCAN("suspend_time 0x%X beacon interval %d\n", + scan_suspend_time, interval); + } + + if (il->scan_request->n_ssids) { + int i, p = 0; + D_SCAN("Kicking off active scan\n"); + for (i = 0; i < il->scan_request->n_ssids; i++) { + /* always does wildcard anyway */ + if (!il->scan_request->ssids[i].ssid_len) + continue; + scan->direct_scan[p].id = WLAN_EID_SSID; + scan->direct_scan[p].len = + il->scan_request->ssids[i].ssid_len; + memcpy(scan->direct_scan[p].ssid, + il->scan_request->ssids[i].ssid, + il->scan_request->ssids[i].ssid_len); + n_probes++; + p++; + } + is_active = true; + } else + D_SCAN("Start passive scan.\n"); + + scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; + scan->tx_cmd.sta_id = il->hw_params.bcast_id; + scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + switch (il->scan_band) { + case IEEE80211_BAND_2GHZ: + scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; + chan_mod = + le32_to_cpu(il->active.flags & RXON_FLG_CHANNEL_MODE_MSK) >> + RXON_FLG_CHANNEL_MODE_POS; + if (chan_mod == CHANNEL_MODE_PURE_40) { + rate = RATE_6M_PLCP; + } else { + rate = RATE_1M_PLCP; + rate_flags = RATE_MCS_CCK_MSK; + } + break; + case IEEE80211_BAND_5GHZ: + rate = RATE_6M_PLCP; + break; + default: + IL_WARN("Invalid scan band\n"); + return -EIO; + } + + /* + * If active scanning is requested but a certain channel is + * marked passive, we can do active scanning if we detect + * transmissions. + * + * There is an issue with some firmware versions that triggers + * a sysassert on a "good CRC threshold" of zero (== disabled), + * on a radar channel even though this means that we should NOT + * send probes. + * + * The "good CRC threshold" is the number of frames that we + * need to receive during our dwell time on a channel before + * sending out probes -- setting this to a huge value will + * mean we never reach it, but at the same time work around + * the aforementioned issue. Thus use IL_GOOD_CRC_TH_NEVER + * here instead of IL_GOOD_CRC_TH_DISABLED. + */ + scan->good_CRC_th = + is_active ? IL_GOOD_CRC_TH_DEFAULT : IL_GOOD_CRC_TH_NEVER; + + band = il->scan_band; + + if (il->cfg->scan_rx_antennas[band]) + rx_ant = il->cfg->scan_rx_antennas[band]; + + il4965_toggle_tx_ant(il, &il->scan_tx_ant[band], scan_tx_antennas); + rate_flags |= BIT(il->scan_tx_ant[band]) << RATE_MCS_ANT_POS; + scan->tx_cmd.rate_n_flags = cpu_to_le32(rate | rate_flags); + + /* In power save mode use one chain, otherwise use all chains */ + if (test_bit(S_POWER_PMI, &il->status)) { + /* rx_ant has been set to all valid chains previously */ + active_chains = + rx_ant & ((u8) (il->chain_noise_data.active_chains)); + if (!active_chains) + active_chains = rx_ant; + + D_SCAN("chain_noise_data.active_chains: %u\n", + il->chain_noise_data.active_chains); + + rx_ant = il4965_first_antenna(active_chains); + } + + /* MIMO is not used here, but value is required */ + rx_chain |= il->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS; + rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; + rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; + rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; + scan->rx_chain = cpu_to_le16(rx_chain); + + cmd_len = + il_fill_probe_req(il, (struct ieee80211_mgmt *)scan->data, + vif->addr, il->scan_request->ie, + il->scan_request->ie_len, + IL_MAX_SCAN_SIZE - sizeof(*scan)); + scan->tx_cmd.len = cpu_to_le16(cmd_len); + + scan->filter_flags |= + (RXON_FILTER_ACCEPT_GRP_MSK | RXON_FILTER_BCON_AWARE_MSK); + + scan->channel_count = + il4965_get_channels_for_scan(il, vif, band, is_active, n_probes, + (void *)&scan->data[cmd_len]); + if (scan->channel_count == 0) { + D_SCAN("channel count %d\n", scan->channel_count); + return -EIO; + } + + cmd.len += + le16_to_cpu(scan->tx_cmd.len) + + scan->channel_count * sizeof(struct il_scan_channel); + cmd.data = scan; + scan->len = cpu_to_le16(cmd.len); + + set_bit(S_SCAN_HW, &il->status); + + ret = il_send_cmd_sync(il, &cmd); + if (ret) + clear_bit(S_SCAN_HW, &il->status); + + return ret; +} + +int +il4965_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif, + bool add) +{ + struct il_vif_priv *vif_priv = (void *)vif->drv_priv; + + if (add) + return il4965_add_bssid_station(il, vif->bss_conf.bssid, + &vif_priv->ibss_bssid_sta_id); + return il_remove_station(il, vif_priv->ibss_bssid_sta_id, + vif->bss_conf.bssid); +} + +void +il4965_free_tfds_in_queue(struct il_priv *il, int sta_id, int tid, int freed) +{ + lockdep_assert_held(&il->sta_lock); + + if (il->stations[sta_id].tid[tid].tfds_in_queue >= freed) + il->stations[sta_id].tid[tid].tfds_in_queue -= freed; + else { + D_TX("free more than tfds_in_queue (%u:%d)\n", + il->stations[sta_id].tid[tid].tfds_in_queue, freed); + il->stations[sta_id].tid[tid].tfds_in_queue = 0; + } +} + +#define IL_TX_QUEUE_MSK 0xfffff + +static bool +il4965_is_single_rx_stream(struct il_priv *il) +{ + return il->current_ht_config.smps == IEEE80211_SMPS_STATIC || + il->current_ht_config.single_chain_sufficient; +} + +#define IL_NUM_RX_CHAINS_MULTIPLE 3 +#define IL_NUM_RX_CHAINS_SINGLE 2 +#define IL_NUM_IDLE_CHAINS_DUAL 2 +#define IL_NUM_IDLE_CHAINS_SINGLE 1 + +/* + * Determine how many receiver/antenna chains to use. + * + * More provides better reception via diversity. Fewer saves power + * at the expense of throughput, but only when not in powersave to + * start with. + * + * MIMO (dual stream) requires at least 2, but works better with 3. + * This does not determine *which* chains to use, just how many. + */ +static int +il4965_get_active_rx_chain_count(struct il_priv *il) +{ + /* # of Rx chains to use when expecting MIMO. */ + if (il4965_is_single_rx_stream(il)) + return IL_NUM_RX_CHAINS_SINGLE; + else + return IL_NUM_RX_CHAINS_MULTIPLE; +} + +/* + * When we are in power saving mode, unless device support spatial + * multiplexing power save, use the active count for rx chain count. + */ +static int +il4965_get_idle_rx_chain_count(struct il_priv *il, int active_cnt) +{ + /* # Rx chains when idling, depending on SMPS mode */ + switch (il->current_ht_config.smps) { + case IEEE80211_SMPS_STATIC: + case IEEE80211_SMPS_DYNAMIC: + return IL_NUM_IDLE_CHAINS_SINGLE; + case IEEE80211_SMPS_OFF: + return active_cnt; + default: + WARN(1, "invalid SMPS mode %d", il->current_ht_config.smps); + return active_cnt; + } +} + +/* up to 4 chains */ +static u8 +il4965_count_chain_bitmap(u32 chain_bitmap) +{ + u8 res; + res = (chain_bitmap & BIT(0)) >> 0; + res += (chain_bitmap & BIT(1)) >> 1; + res += (chain_bitmap & BIT(2)) >> 2; + res += (chain_bitmap & BIT(3)) >> 3; + return res; +} + +/** + * il4965_set_rxon_chain - Set up Rx chain usage in "staging" RXON image + * + * Selects how many and which Rx receivers/antennas/chains to use. + * This should not be used for scan command ... it puts data in wrong place. + */ +void +il4965_set_rxon_chain(struct il_priv *il) +{ + bool is_single = il4965_is_single_rx_stream(il); + bool is_cam = !test_bit(S_POWER_PMI, &il->status); + u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; + u32 active_chains; + u16 rx_chain; + + /* Tell uCode which antennas are actually connected. + * Before first association, we assume all antennas are connected. + * Just after first association, il4965_chain_noise_calibration() + * checks which antennas actually *are* connected. */ + if (il->chain_noise_data.active_chains) + active_chains = il->chain_noise_data.active_chains; + else + active_chains = il->hw_params.valid_rx_ant; + + rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; + + /* How many receivers should we use? */ + active_rx_cnt = il4965_get_active_rx_chain_count(il); + idle_rx_cnt = il4965_get_idle_rx_chain_count(il, active_rx_cnt); + + /* correct rx chain count according hw settings + * and chain noise calibration + */ + valid_rx_cnt = il4965_count_chain_bitmap(active_chains); + if (valid_rx_cnt < active_rx_cnt) + active_rx_cnt = valid_rx_cnt; + + if (valid_rx_cnt < idle_rx_cnt) + idle_rx_cnt = valid_rx_cnt; + + rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; + rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; + + il->staging.rx_chain = cpu_to_le16(rx_chain); + + if (!is_single && active_rx_cnt >= IL_NUM_RX_CHAINS_SINGLE && is_cam) + il->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; + else + il->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; + + D_ASSOC("rx_chain=0x%X active=%d idle=%d\n", il->staging.rx_chain, + active_rx_cnt, idle_rx_cnt); + + WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || + active_rx_cnt < idle_rx_cnt); +} + +static const char * +il4965_get_fh_string(int cmd) +{ + switch (cmd) { + IL_CMD(FH49_RSCSR_CHNL0_STTS_WPTR_REG); + IL_CMD(FH49_RSCSR_CHNL0_RBDCB_BASE_REG); + IL_CMD(FH49_RSCSR_CHNL0_WPTR); + IL_CMD(FH49_MEM_RCSR_CHNL0_CONFIG_REG); + IL_CMD(FH49_MEM_RSSR_SHARED_CTRL_REG); + IL_CMD(FH49_MEM_RSSR_RX_STATUS_REG); + IL_CMD(FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); + IL_CMD(FH49_TSSR_TX_STATUS_REG); + IL_CMD(FH49_TSSR_TX_ERROR_REG); + default: + return "UNKNOWN"; + } +} + +int +il4965_dump_fh(struct il_priv *il, char **buf, bool display) +{ + int i; +#ifdef CONFIG_IWLEGACY_DEBUG + int pos = 0; + size_t bufsz = 0; +#endif + static const u32 fh_tbl[] = { + FH49_RSCSR_CHNL0_STTS_WPTR_REG, + FH49_RSCSR_CHNL0_RBDCB_BASE_REG, + FH49_RSCSR_CHNL0_WPTR, + FH49_MEM_RCSR_CHNL0_CONFIG_REG, + FH49_MEM_RSSR_SHARED_CTRL_REG, + FH49_MEM_RSSR_RX_STATUS_REG, + FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, + FH49_TSSR_TX_STATUS_REG, + FH49_TSSR_TX_ERROR_REG + }; +#ifdef CONFIG_IWLEGACY_DEBUG + if (display) { + bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + pos += + scnprintf(*buf + pos, bufsz - pos, "FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + pos += + scnprintf(*buf + pos, bufsz - pos, + " %34s: 0X%08x\n", + il4965_get_fh_string(fh_tbl[i]), + il_rd(il, fh_tbl[i])); + } + return pos; + } +#endif + IL_ERR("FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + IL_ERR(" %34s: 0X%08x\n", il4965_get_fh_string(fh_tbl[i]), + il_rd(il, fh_tbl[i])); + } + return 0; +} + +static void +il4965_hdl_missed_beacon(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_missed_beacon_notif *missed_beacon; + + missed_beacon = &pkt->u.missed_beacon; + if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > + il->missed_beacon_threshold) { + D_CALIB("missed bcn cnsq %d totl %d rcd %d expctd %d\n", + le32_to_cpu(missed_beacon->consecutive_missed_beacons), + le32_to_cpu(missed_beacon->total_missed_becons), + le32_to_cpu(missed_beacon->num_recvd_beacons), + le32_to_cpu(missed_beacon->num_expected_beacons)); + if (!test_bit(S_SCANNING, &il->status)) + il4965_init_sensitivity(il); + } +} + +/* Calculate noise level, based on measurements during network silence just + * before arriving beacon. This measurement can be done only if we know + * exactly when to expect beacons, therefore only when we're associated. */ +static void +il4965_rx_calc_noise(struct il_priv *il) +{ + struct stats_rx_non_phy *rx_info; + int num_active_rx = 0; + int total_silence = 0; + int bcn_silence_a, bcn_silence_b, bcn_silence_c; + int last_rx_noise; + + rx_info = &(il->_4965.stats.rx.general); + bcn_silence_a = + le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; + bcn_silence_b = + le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; + bcn_silence_c = + le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; + + if (bcn_silence_a) { + total_silence += bcn_silence_a; + num_active_rx++; + } + if (bcn_silence_b) { + total_silence += bcn_silence_b; + num_active_rx++; + } + if (bcn_silence_c) { + total_silence += bcn_silence_c; + num_active_rx++; + } + + /* Average among active antennas */ + if (num_active_rx) + last_rx_noise = (total_silence / num_active_rx) - 107; + else + last_rx_noise = IL_NOISE_MEAS_NOT_AVAILABLE; + + D_CALIB("inband silence a %u, b %u, c %u, dBm %d\n", bcn_silence_a, + bcn_silence_b, bcn_silence_c, last_rx_noise); +} + +#ifdef CONFIG_IWLEGACY_DEBUGFS +/* + * based on the assumption of all stats counter are in DWORD + * FIXME: This function is for debugging, do not deal with + * the case of counters roll-over. + */ +static void +il4965_accumulative_stats(struct il_priv *il, __le32 * stats) +{ + int i, size; + __le32 *prev_stats; + u32 *accum_stats; + u32 *delta, *max_delta; + struct stats_general_common *general, *accum_general; + struct stats_tx *tx, *accum_tx; + + prev_stats = (__le32 *) &il->_4965.stats; + accum_stats = (u32 *) &il->_4965.accum_stats; + size = sizeof(struct il_notif_stats); + general = &il->_4965.stats.general.common; + accum_general = &il->_4965.accum_stats.general.common; + tx = &il->_4965.stats.tx; + accum_tx = &il->_4965.accum_stats.tx; + delta = (u32 *) &il->_4965.delta_stats; + max_delta = (u32 *) &il->_4965.max_delta; + + for (i = sizeof(__le32); i < size; + i += + sizeof(__le32), stats++, prev_stats++, delta++, max_delta++, + accum_stats++) { + if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) { + *delta = + (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats)); + *accum_stats += *delta; + if (*delta > *max_delta) + *max_delta = *delta; + } + } + + /* reset accumulative stats for "no-counter" type stats */ + accum_general->temperature = general->temperature; + accum_general->ttl_timestamp = general->ttl_timestamp; +} +#endif + +static void +il4965_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb) +{ + const int recalib_seconds = 60; + bool change; + struct il_rx_pkt *pkt = rxb_addr(rxb); + + D_RX("Statistics notification received (%d vs %d).\n", + (int)sizeof(struct il_notif_stats), + le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK); + + change = + ((il->_4965.stats.general.common.temperature != + pkt->u.stats.general.common.temperature) || + ((il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK) != + (pkt->u.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK))); +#ifdef CONFIG_IWLEGACY_DEBUGFS + il4965_accumulative_stats(il, (__le32 *) &pkt->u.stats); +#endif + + /* TODO: reading some of stats is unneeded */ + memcpy(&il->_4965.stats, &pkt->u.stats, sizeof(il->_4965.stats)); + + set_bit(S_STATS, &il->status); + + /* + * Reschedule the stats timer to occur in recalib_seconds to ensure + * we get a thermal update even if the uCode doesn't give us one + */ + mod_timer(&il->stats_periodic, + jiffies + msecs_to_jiffies(recalib_seconds * 1000)); + + if (unlikely(!test_bit(S_SCANNING, &il->status)) && + (pkt->hdr.cmd == N_STATS)) { + il4965_rx_calc_noise(il); + queue_work(il->workqueue, &il->run_time_calib_work); + } + + if (change) + il4965_temperature_calib(il); +} + +static void +il4965_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + + if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATS_CLEAR_MSK) { +#ifdef CONFIG_IWLEGACY_DEBUGFS + memset(&il->_4965.accum_stats, 0, + sizeof(struct il_notif_stats)); + memset(&il->_4965.delta_stats, 0, + sizeof(struct il_notif_stats)); + memset(&il->_4965.max_delta, 0, sizeof(struct il_notif_stats)); +#endif + D_RX("Statistics have been cleared\n"); + } + il4965_hdl_stats(il, rxb); +} + + +/* + * mac80211 queues, ACs, hardware queues, FIFOs. + * + * Cf. http://wireless.kernel.org/en/developers/Documentation/mac80211/queues + * + * Mac80211 uses the following numbers, which we get as from it + * by way of skb_get_queue_mapping(skb): + * + * VO 0 + * VI 1 + * BE 2 + * BK 3 + * + * + * Regular (not A-MPDU) frames are put into hardware queues corresponding + * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their + * own queue per aggregation session (RA/TID combination), such queues are + * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In + * order to map frames to the right queue, we also need an AC->hw queue + * mapping. This is implemented here. + * + * Due to the way hw queues are set up (by the hw specific modules like + * 4965.c), the AC->hw queue mapping is the identity + * mapping. + */ + +static const u8 tid_to_ac[] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO +}; + +static inline int +il4965_get_ac_from_tid(u16 tid) +{ + if (likely(tid < ARRAY_SIZE(tid_to_ac))) + return tid_to_ac[tid]; + + /* no support for TIDs 8-15 yet */ + return -EINVAL; +} + +static inline int +il4965_get_fifo_from_tid(u16 tid) +{ + const u8 ac_to_fifo[] = { + IL_TX_FIFO_VO, + IL_TX_FIFO_VI, + IL_TX_FIFO_BE, + IL_TX_FIFO_BK, + }; + + if (likely(tid < ARRAY_SIZE(tid_to_ac))) + return ac_to_fifo[tid_to_ac[tid]]; + + /* no support for TIDs 8-15 yet */ + return -EINVAL; +} + +/* + * handle build C_TX command notification. + */ +static void +il4965_tx_cmd_build_basic(struct il_priv *il, struct sk_buff *skb, + struct il_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, u8 std_id) +{ + __le16 fc = hdr->frame_control; + __le32 tx_flags = tx_cmd->tx_flags; + + tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + tx_flags |= TX_CMD_FLG_ACK_MSK; + if (ieee80211_is_mgmt(fc)) + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + if (ieee80211_is_probe_resp(fc) && + !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) + tx_flags |= TX_CMD_FLG_TSF_MSK; + } else { + tx_flags &= (~TX_CMD_FLG_ACK_MSK); + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + } + + if (ieee80211_is_back_req(fc)) + tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; + + tx_cmd->sta_id = std_id; + if (ieee80211_has_morefrags(fc)) + tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; + + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tx_cmd->tid_tspec = qc[0] & 0xf; + tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; + } else { + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + } + + il_tx_cmd_protection(il, info, fc, &tx_flags); + + tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); + if (ieee80211_is_mgmt(fc)) { + if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); + else + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); + } else { + tx_cmd->timeout.pm_frame_timeout = 0; + } + + tx_cmd->driver_txop = 0; + tx_cmd->tx_flags = tx_flags; + tx_cmd->next_frame_len = 0; +} + +static void +il4965_tx_cmd_build_rate(struct il_priv *il, + struct il_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + __le16 fc) +{ + const u8 rts_retry_limit = 60; + u32 rate_flags; + int rate_idx; + u8 data_retry_limit; + u8 rate_plcp; + + /* Set retry limit on DATA packets and Probe Responses */ + if (ieee80211_is_probe_resp(fc)) + data_retry_limit = 3; + else + data_retry_limit = IL4965_DEFAULT_TX_RETRY; + tx_cmd->data_retry_limit = data_retry_limit; + /* Set retry limit on RTS packets */ + tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit); + + /* DATA packets will use the uCode station table for rate/antenna + * selection */ + if (ieee80211_is_data(fc)) { + tx_cmd->initial_rate_idx = 0; + tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; + return; + } + + /** + * If the current TX rate stored in mac80211 has the MCS bit set, it's + * not really a TX rate. Thus, we use the lowest supported rate for + * this band. Also use the lowest supported rate if the stored rate + * idx is invalid. + */ + rate_idx = info->control.rates[0].idx; + if ((info->control.rates[0].flags & IEEE80211_TX_RC_MCS) || rate_idx < 0 + || rate_idx > RATE_COUNT_LEGACY) + rate_idx = rate_lowest_index(&il->bands[info->band], sta); + /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ + if (info->band == IEEE80211_BAND_5GHZ) + rate_idx += IL_FIRST_OFDM_RATE; + /* Get PLCP rate for tx_cmd->rate_n_flags */ + rate_plcp = il_rates[rate_idx].plcp; + /* Zero out flags for this packet */ + rate_flags = 0; + + /* Set CCK flag as needed */ + if (rate_idx >= IL_FIRST_CCK_RATE && rate_idx <= IL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_CCK_MSK; + + /* Set up antennas */ + il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant); + rate_flags |= BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS; + + /* Set the rate in the TX cmd */ + tx_cmd->rate_n_flags = cpu_to_le32(rate_plcp | rate_flags); +} + +static void +il4965_tx_cmd_build_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info, + struct il_tx_cmd *tx_cmd, struct sk_buff *skb_frag, + int sta_id) +{ + struct ieee80211_key_conf *keyconf = info->control.hw_key; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + tx_cmd->sec_ctl = TX_CMD_SEC_CCM; + memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); + if (info->flags & IEEE80211_TX_CTL_AMPDU) + tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; + D_TX("tx_cmd with AES hwcrypto\n"); + break; + + case WLAN_CIPHER_SUITE_TKIP: + tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; + ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); + D_TX("tx_cmd with tkip hwcrypto\n"); + break; + + case WLAN_CIPHER_SUITE_WEP104: + tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; + /* fall through */ + case WLAN_CIPHER_SUITE_WEP40: + tx_cmd->sec_ctl |= + (TX_CMD_SEC_WEP | (keyconf->keyidx & TX_CMD_SEC_MSK) << + TX_CMD_SEC_SHIFT); + + memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); + + D_TX("Configuring packet for WEP encryption " "with key %d\n", + keyconf->keyidx); + break; + + default: + IL_ERR("Unknown encode cipher %x\n", keyconf->cipher); + break; + } +} + +/* + * start C_TX command process + */ +int +il4965_tx_skb(struct il_priv *il, + struct ieee80211_sta *sta, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct il_station_priv *sta_priv = NULL; + struct il_tx_queue *txq; + struct il_queue *q; + struct il_device_cmd *out_cmd; + struct il_cmd_meta *out_meta; + struct il_tx_cmd *tx_cmd; + int txq_id; + dma_addr_t phys_addr; + dma_addr_t txcmd_phys; + dma_addr_t scratch_phys; + u16 len, firstlen, secondlen; + u16 seq_number = 0; + __le16 fc; + u8 hdr_len; + u8 sta_id; + u8 wait_write_ptr = 0; + u8 tid = 0; + u8 *qc = NULL; + unsigned long flags; + bool is_agg = false; + + spin_lock_irqsave(&il->lock, flags); + if (il_is_rfkill(il)) { + D_DROP("Dropping - RF KILL\n"); + goto drop_unlock; + } + + fc = hdr->frame_control; + +#ifdef CONFIG_IWLEGACY_DEBUG + if (ieee80211_is_auth(fc)) + D_TX("Sending AUTH frame\n"); + else if (ieee80211_is_assoc_req(fc)) + D_TX("Sending ASSOC frame\n"); + else if (ieee80211_is_reassoc_req(fc)) + D_TX("Sending REASSOC frame\n"); +#endif + + hdr_len = ieee80211_hdrlen(fc); + + /* For management frames use broadcast id to do not break aggregation */ + if (!ieee80211_is_data(fc)) + sta_id = il->hw_params.bcast_id; + else { + /* Find idx into station table for destination station */ + sta_id = il_sta_id_or_broadcast(il, sta); + + if (sta_id == IL_INVALID_STATION) { + D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1); + goto drop_unlock; + } + } + + D_TX("station Id %d\n", sta_id); + + if (sta) + sta_priv = (void *)sta->drv_priv; + + if (sta_priv && sta_priv->asleep && + (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { + /* + * This sends an asynchronous command to the device, + * but we can rely on it being processed before the + * next frame is processed -- and the next frame to + * this station is the one that will consume this + * counter. + * For now set the counter to just 1 since we do not + * support uAPSD yet. + */ + il4965_sta_modify_sleep_tx_count(il, sta_id, 1); + } + + /* FIXME: remove me ? */ + WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); + + /* Access category (AC) is also the queue number */ + txq_id = skb_get_queue_mapping(skb); + + /* irqs already disabled/saved above when locking il->lock */ + spin_lock(&il->sta_lock); + + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) { + spin_unlock(&il->sta_lock); + goto drop_unlock; + } + seq_number = il->stations[sta_id].tid[tid].seq_number; + seq_number &= IEEE80211_SCTL_SEQ; + hdr->seq_ctrl = + hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(seq_number); + seq_number += 0x10; + /* aggregation is on for this */ + if (info->flags & IEEE80211_TX_CTL_AMPDU && + il->stations[sta_id].tid[tid].agg.state == IL_AGG_ON) { + txq_id = il->stations[sta_id].tid[tid].agg.txq_id; + is_agg = true; + } + } + + txq = &il->txq[txq_id]; + q = &txq->q; + + if (unlikely(il_queue_space(q) < q->high_mark)) { + spin_unlock(&il->sta_lock); + goto drop_unlock; + } + + if (ieee80211_is_data_qos(fc)) { + il->stations[sta_id].tid[tid].tfds_in_queue++; + if (!ieee80211_has_morefrags(fc)) + il->stations[sta_id].tid[tid].seq_number = seq_number; + } + + spin_unlock(&il->sta_lock); + + txq->skbs[q->write_ptr] = skb; + + /* Set up first empty entry in queue's array of Tx/cmd buffers */ + out_cmd = txq->cmd[q->write_ptr]; + out_meta = &txq->meta[q->write_ptr]; + tx_cmd = &out_cmd->cmd.tx; + memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); + memset(tx_cmd, 0, sizeof(struct il_tx_cmd)); + + /* + * Set up the Tx-command (not MAC!) header. + * Store the chosen Tx queue and TFD idx within the sequence field; + * after Tx, uCode's Tx response will return this value so driver can + * locate the frame within the tx queue and do post-tx processing. + */ + out_cmd->hdr.cmd = C_TX; + out_cmd->hdr.sequence = + cpu_to_le16((u16) + (QUEUE_TO_SEQ(txq_id) | IDX_TO_SEQ(q->write_ptr))); + + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, hdr_len); + + /* Total # bytes to be transmitted */ + tx_cmd->len = cpu_to_le16((u16) skb->len); + + if (info->control.hw_key) + il4965_tx_cmd_build_hwcrypto(il, info, tx_cmd, skb, sta_id); + + /* TODO need this for burst mode later on */ + il4965_tx_cmd_build_basic(il, skb, tx_cmd, info, hdr, sta_id); + + il4965_tx_cmd_build_rate(il, tx_cmd, info, sta, fc); + + /* + * Use the first empty entry in this queue's command buffer array + * to contain the Tx command and MAC header concatenated together + * (payload data will be in another buffer). + * Size of this varies, due to varying MAC header length. + * If end is not dword aligned, we'll have 2 extra bytes at the end + * of the MAC header (device reads on dword boundaries). + * We'll tell device about this padding later. + */ + len = sizeof(struct il_tx_cmd) + sizeof(struct il_cmd_header) + hdr_len; + firstlen = (len + 3) & ~3; + + /* Tell NIC about any 2-byte padding after MAC header */ + if (firstlen != len) + tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; + + /* Physical address of this Tx command's header (not MAC header!), + * within command buffer array. */ + txcmd_phys = + pci_map_single(il->pci_dev, &out_cmd->hdr, firstlen, + PCI_DMA_BIDIRECTIONAL); + if (unlikely(pci_dma_mapping_error(il->pci_dev, txcmd_phys))) + goto drop_unlock; + + /* Set up TFD's 2nd entry to point directly to remainder of skb, + * if any (802.11 null frames have no payload). */ + secondlen = skb->len - hdr_len; + if (secondlen > 0) { + phys_addr = + pci_map_single(il->pci_dev, skb->data + hdr_len, secondlen, + PCI_DMA_TODEVICE); + if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr))) + goto drop_unlock; + } + + /* Add buffer containing Tx command and MAC(!) header to TFD's + * first entry */ + il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, firstlen, 1, 0); + dma_unmap_addr_set(out_meta, mapping, txcmd_phys); + dma_unmap_len_set(out_meta, len, firstlen); + if (secondlen) + il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, secondlen, + 0, 0); + + if (!ieee80211_has_morefrags(hdr->frame_control)) { + txq->need_update = 1; + } else { + wait_write_ptr = 1; + txq->need_update = 0; + } + + scratch_phys = + txcmd_phys + sizeof(struct il_cmd_header) + + offsetof(struct il_tx_cmd, scratch); + + /* take back ownership of DMA buffer to enable update */ + pci_dma_sync_single_for_cpu(il->pci_dev, txcmd_phys, firstlen, + PCI_DMA_BIDIRECTIONAL); + tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); + tx_cmd->dram_msb_ptr = il_get_dma_hi_addr(scratch_phys); + + il_update_stats(il, true, fc, skb->len); + + D_TX("sequence nr = 0X%x\n", le16_to_cpu(out_cmd->hdr.sequence)); + D_TX("tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); + il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd, sizeof(*tx_cmd)); + il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd->hdr, hdr_len); + + /* Set up entry for this TFD in Tx byte-count array */ + if (info->flags & IEEE80211_TX_CTL_AMPDU) + il->ops->txq_update_byte_cnt_tbl(il, txq, le16_to_cpu(tx_cmd->len)); + + pci_dma_sync_single_for_device(il->pci_dev, txcmd_phys, firstlen, + PCI_DMA_BIDIRECTIONAL); + + /* Tell device the write idx *just past* this latest filled TFD */ + q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); + il_txq_update_write_ptr(il, txq); + spin_unlock_irqrestore(&il->lock, flags); + + /* + * At this point the frame is "transmitted" successfully + * and we will get a TX status notification eventually, + * regardless of the value of ret. "ret" only indicates + * whether or not we should update the write pointer. + */ + + /* + * Avoid atomic ops if it isn't an associated client. + * Also, if this is a packet for aggregation, don't + * increase the counter because the ucode will stop + * aggregation queues when their respective station + * goes to sleep. + */ + if (sta_priv && sta_priv->client && !is_agg) + atomic_inc(&sta_priv->pending_frames); + + if (il_queue_space(q) < q->high_mark && il->mac80211_registered) { + if (wait_write_ptr) { + spin_lock_irqsave(&il->lock, flags); + txq->need_update = 1; + il_txq_update_write_ptr(il, txq); + spin_unlock_irqrestore(&il->lock, flags); + } else { + il_stop_queue(il, txq); + } + } + + return 0; + +drop_unlock: + spin_unlock_irqrestore(&il->lock, flags); + return -1; +} + +static inline int +il4965_alloc_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr, size_t size) +{ + ptr->addr = dma_alloc_coherent(&il->pci_dev->dev, size, &ptr->dma, + GFP_KERNEL); + if (!ptr->addr) + return -ENOMEM; + ptr->size = size; + return 0; +} + +static inline void +il4965_free_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr) +{ + if (unlikely(!ptr->addr)) + return; + + dma_free_coherent(&il->pci_dev->dev, ptr->size, ptr->addr, ptr->dma); + memset(ptr, 0, sizeof(*ptr)); +} + +/** + * il4965_hw_txq_ctx_free - Free TXQ Context + * + * Destroy all TX DMA queues and structures + */ +void +il4965_hw_txq_ctx_free(struct il_priv *il) +{ + int txq_id; + + /* Tx queues */ + if (il->txq) { + for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) + if (txq_id == il->cmd_queue) + il_cmd_queue_free(il); + else + il_tx_queue_free(il, txq_id); + } + il4965_free_dma_ptr(il, &il->kw); + + il4965_free_dma_ptr(il, &il->scd_bc_tbls); + + /* free tx queue structure */ + il_free_txq_mem(il); +} + +/** + * il4965_txq_ctx_alloc - allocate TX queue context + * Allocate all Tx DMA structures and initialize them + * + * @param il + * @return error code + */ +int +il4965_txq_ctx_alloc(struct il_priv *il) +{ + int ret, txq_id; + unsigned long flags; + + /* Free all tx/cmd queues and keep-warm buffer */ + il4965_hw_txq_ctx_free(il); + + ret = + il4965_alloc_dma_ptr(il, &il->scd_bc_tbls, + il->hw_params.scd_bc_tbls_size); + if (ret) { + IL_ERR("Scheduler BC Table allocation failed\n"); + goto error_bc_tbls; + } + /* Alloc keep-warm buffer */ + ret = il4965_alloc_dma_ptr(il, &il->kw, IL_KW_SIZE); + if (ret) { + IL_ERR("Keep Warm allocation failed\n"); + goto error_kw; + } + + /* allocate tx queue structure */ + ret = il_alloc_txq_mem(il); + if (ret) + goto error; + + spin_lock_irqsave(&il->lock, flags); + + /* Turn off all Tx DMA fifos */ + il4965_txq_set_sched(il, 0); + + /* Tell NIC where to find the "keep warm" buffer */ + il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4); + + spin_unlock_irqrestore(&il->lock, flags); + + /* Alloc and init all Tx queues, including the command queue (#4/#9) */ + for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) { + ret = il_tx_queue_init(il, txq_id); + if (ret) { + IL_ERR("Tx %d queue init failed\n", txq_id); + goto error; + } + } + + return ret; + +error: + il4965_hw_txq_ctx_free(il); + il4965_free_dma_ptr(il, &il->kw); +error_kw: + il4965_free_dma_ptr(il, &il->scd_bc_tbls); +error_bc_tbls: + return ret; +} + +void +il4965_txq_ctx_reset(struct il_priv *il) +{ + int txq_id; + unsigned long flags; + + spin_lock_irqsave(&il->lock, flags); + + /* Turn off all Tx DMA fifos */ + il4965_txq_set_sched(il, 0); + /* Tell NIC where to find the "keep warm" buffer */ + il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4); + + spin_unlock_irqrestore(&il->lock, flags); + + /* Alloc and init all Tx queues, including the command queue (#4) */ + for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) + il_tx_queue_reset(il, txq_id); +} + +static void +il4965_txq_ctx_unmap(struct il_priv *il) +{ + int txq_id; + + if (!il->txq) + return; + + /* Unmap DMA from host system and free skb's */ + for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) + if (txq_id == il->cmd_queue) + il_cmd_queue_unmap(il); + else + il_tx_queue_unmap(il, txq_id); +} + +/** + * il4965_txq_ctx_stop - Stop all Tx DMA channels + */ +void +il4965_txq_ctx_stop(struct il_priv *il) +{ + int ch, ret; + + _il_wr_prph(il, IL49_SCD_TXFACT, 0); + + /* Stop each Tx DMA channel, and wait for it to be idle */ + for (ch = 0; ch < il->hw_params.dma_chnl_num; ch++) { + _il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); + ret = + _il_poll_bit(il, FH49_TSSR_TX_STATUS_REG, + FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), + FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), + 1000); + if (ret < 0) + IL_ERR("Timeout stopping DMA channel %d [0x%08x]", + ch, _il_rd(il, FH49_TSSR_TX_STATUS_REG)); + } +} + +/* + * Find first available (lowest unused) Tx Queue, mark it "active". + * Called only when finding queue for aggregation. + * Should never return anything < 7, because they should already + * be in use as EDCA AC (0-3), Command (4), reserved (5, 6) + */ +static int +il4965_txq_ctx_activate_free(struct il_priv *il) +{ + int txq_id; + + for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) + if (!test_and_set_bit(txq_id, &il->txq_ctx_active_msk)) + return txq_id; + return -1; +} + +/** + * il4965_tx_queue_stop_scheduler - Stop queue, but keep configuration + */ +static void +il4965_tx_queue_stop_scheduler(struct il_priv *il, u16 txq_id) +{ + /* Simply stop the queue, but don't change any configuration; + * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ + il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id), + (0 << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) | + (1 << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); +} + +/** + * il4965_tx_queue_set_q2ratid - Map unique receiver/tid combination to a queue + */ +static int +il4965_tx_queue_set_q2ratid(struct il_priv *il, u16 ra_tid, u16 txq_id) +{ + u32 tbl_dw_addr; + u32 tbl_dw; + u16 scd_q2ratid; + + scd_q2ratid = ra_tid & IL_SCD_QUEUE_RA_TID_MAP_RATID_MSK; + + tbl_dw_addr = + il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id); + + tbl_dw = il_read_targ_mem(il, tbl_dw_addr); + + if (txq_id & 0x1) + tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); + else + tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); + + il_write_targ_mem(il, tbl_dw_addr, tbl_dw); + + return 0; +} + +/** + * il4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue + * + * NOTE: txq_id must be greater than IL49_FIRST_AMPDU_QUEUE, + * i.e. it must be one of the higher queues used for aggregation + */ +static int +il4965_txq_agg_enable(struct il_priv *il, int txq_id, int tx_fifo, int sta_id, + int tid, u16 ssn_idx) +{ + unsigned long flags; + u16 ra_tid; + int ret; + + if ((IL49_FIRST_AMPDU_QUEUE > txq_id) || + (IL49_FIRST_AMPDU_QUEUE + + il->cfg->num_of_ampdu_queues <= txq_id)) { + IL_WARN("queue number out of range: %d, must be %d to %d\n", + txq_id, IL49_FIRST_AMPDU_QUEUE, + IL49_FIRST_AMPDU_QUEUE + + il->cfg->num_of_ampdu_queues - 1); + return -EINVAL; + } + + ra_tid = BUILD_RAxTID(sta_id, tid); + + /* Modify device's station table to Tx this TID */ + ret = il4965_sta_tx_modify_enable_tid(il, sta_id, tid); + if (ret) + return ret; + + spin_lock_irqsave(&il->lock, flags); + + /* Stop this Tx queue before configuring it */ + il4965_tx_queue_stop_scheduler(il, txq_id); + + /* Map receiver-address / traffic-ID to this queue */ + il4965_tx_queue_set_q2ratid(il, ra_tid, txq_id); + + /* Set this queue as a chain-building queue */ + il_set_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); + + /* Place first TFD at idx corresponding to start sequence number. + * Assumes that ssn_idx is valid (!= 0xFFF) */ + il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); + il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); + il4965_set_wr_ptrs(il, txq_id, ssn_idx); + + /* Set up Tx win size and frame limit for this queue */ + il_write_targ_mem(il, + il->scd_base_addr + + IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id), + (SCD_WIN_SIZE << IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) + & IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); + + il_write_targ_mem(il, + il->scd_base_addr + + IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), + (SCD_FRAME_LIMIT << + IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & + IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); + + il_set_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id)); + + /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ + il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 1); + + spin_unlock_irqrestore(&il->lock, flags); + + return 0; +} + +int +il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u16 * ssn) +{ + int sta_id; + int tx_fifo; + int txq_id; + int ret; + unsigned long flags; + struct il_tid_data *tid_data; + + /* FIXME: warning if tx fifo not found ? */ + tx_fifo = il4965_get_fifo_from_tid(tid); + if (unlikely(tx_fifo < 0)) + return tx_fifo; + + D_HT("%s on ra = %pM tid = %d\n", __func__, sta->addr, tid); + + sta_id = il_sta_id(sta); + if (sta_id == IL_INVALID_STATION) { + IL_ERR("Start AGG on invalid station\n"); + return -ENXIO; + } + if (unlikely(tid >= MAX_TID_COUNT)) + return -EINVAL; + + if (il->stations[sta_id].tid[tid].agg.state != IL_AGG_OFF) { + IL_ERR("Start AGG when state is not IL_AGG_OFF !\n"); + return -ENXIO; + } + + txq_id = il4965_txq_ctx_activate_free(il); + if (txq_id == -1) { + IL_ERR("No free aggregation queue available\n"); + return -ENXIO; + } + + spin_lock_irqsave(&il->sta_lock, flags); + tid_data = &il->stations[sta_id].tid[tid]; + *ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); + tid_data->agg.txq_id = txq_id; + il_set_swq_id(&il->txq[txq_id], il4965_get_ac_from_tid(tid), txq_id); + spin_unlock_irqrestore(&il->sta_lock, flags); + + ret = il4965_txq_agg_enable(il, txq_id, tx_fifo, sta_id, tid, *ssn); + if (ret) + return ret; + + spin_lock_irqsave(&il->sta_lock, flags); + tid_data = &il->stations[sta_id].tid[tid]; + if (tid_data->tfds_in_queue == 0) { + D_HT("HW queue is empty\n"); + tid_data->agg.state = IL_AGG_ON; + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + } else { + D_HT("HW queue is NOT empty: %d packets in HW queue\n", + tid_data->tfds_in_queue); + tid_data->agg.state = IL_EMPTYING_HW_QUEUE_ADDBA; + } + spin_unlock_irqrestore(&il->sta_lock, flags); + return ret; +} + +/** + * txq_id must be greater than IL49_FIRST_AMPDU_QUEUE + * il->lock must be held by the caller + */ +static int +il4965_txq_agg_disable(struct il_priv *il, u16 txq_id, u16 ssn_idx, u8 tx_fifo) +{ + if ((IL49_FIRST_AMPDU_QUEUE > txq_id) || + (IL49_FIRST_AMPDU_QUEUE + + il->cfg->num_of_ampdu_queues <= txq_id)) { + IL_WARN("queue number out of range: %d, must be %d to %d\n", + txq_id, IL49_FIRST_AMPDU_QUEUE, + IL49_FIRST_AMPDU_QUEUE + + il->cfg->num_of_ampdu_queues - 1); + return -EINVAL; + } + + il4965_tx_queue_stop_scheduler(il, txq_id); + + il_clear_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); + + il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); + il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); + /* supposes that ssn_idx is valid (!= 0xFFF) */ + il4965_set_wr_ptrs(il, txq_id, ssn_idx); + + il_clear_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id)); + il_txq_ctx_deactivate(il, txq_id); + il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 0); + + return 0; +} + +int +il4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid) +{ + int tx_fifo_id, txq_id, sta_id, ssn; + struct il_tid_data *tid_data; + int write_ptr, read_ptr; + unsigned long flags; + + /* FIXME: warning if tx_fifo_id not found ? */ + tx_fifo_id = il4965_get_fifo_from_tid(tid); + if (unlikely(tx_fifo_id < 0)) + return tx_fifo_id; + + sta_id = il_sta_id(sta); + + if (sta_id == IL_INVALID_STATION) { + IL_ERR("Invalid station for AGG tid %d\n", tid); + return -ENXIO; + } + + spin_lock_irqsave(&il->sta_lock, flags); + + tid_data = &il->stations[sta_id].tid[tid]; + ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4; + txq_id = tid_data->agg.txq_id; + + switch (il->stations[sta_id].tid[tid].agg.state) { + case IL_EMPTYING_HW_QUEUE_ADDBA: + /* + * This can happen if the peer stops aggregation + * again before we've had a chance to drain the + * queue we selected previously, i.e. before the + * session was really started completely. + */ + D_HT("AGG stop before setup done\n"); + goto turn_off; + case IL_AGG_ON: + break; + default: + IL_WARN("Stopping AGG while state not ON or starting\n"); + } + + write_ptr = il->txq[txq_id].q.write_ptr; + read_ptr = il->txq[txq_id].q.read_ptr; + + /* The queue is not empty */ + if (write_ptr != read_ptr) { + D_HT("Stopping a non empty AGG HW QUEUE\n"); + il->stations[sta_id].tid[tid].agg.state = + IL_EMPTYING_HW_QUEUE_DELBA; + spin_unlock_irqrestore(&il->sta_lock, flags); + return 0; + } + + D_HT("HW queue is empty\n"); +turn_off: + il->stations[sta_id].tid[tid].agg.state = IL_AGG_OFF; + + /* do not restore/save irqs */ + spin_unlock(&il->sta_lock); + spin_lock(&il->lock); + + /* + * the only reason this call can fail is queue number out of range, + * which can happen if uCode is reloaded and all the station + * information are lost. if it is outside the range, there is no need + * to deactivate the uCode queue, just return "success" to allow + * mac80211 to clean up it own data. + */ + il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo_id); + spin_unlock_irqrestore(&il->lock, flags); + + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + + return 0; +} + +int +il4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id) +{ + struct il_queue *q = &il->txq[txq_id].q; + u8 *addr = il->stations[sta_id].sta.sta.addr; + struct il_tid_data *tid_data = &il->stations[sta_id].tid[tid]; + + lockdep_assert_held(&il->sta_lock); + + switch (il->stations[sta_id].tid[tid].agg.state) { + case IL_EMPTYING_HW_QUEUE_DELBA: + /* We are reclaiming the last packet of the */ + /* aggregated HW queue */ + if (txq_id == tid_data->agg.txq_id && + q->read_ptr == q->write_ptr) { + u16 ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); + int tx_fifo = il4965_get_fifo_from_tid(tid); + D_HT("HW queue empty: continue DELBA flow\n"); + il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo); + tid_data->agg.state = IL_AGG_OFF; + ieee80211_stop_tx_ba_cb_irqsafe(il->vif, addr, tid); + } + break; + case IL_EMPTYING_HW_QUEUE_ADDBA: + /* We are reclaiming the last packet of the queue */ + if (tid_data->tfds_in_queue == 0) { + D_HT("HW queue empty: continue ADDBA flow\n"); + tid_data->agg.state = IL_AGG_ON; + ieee80211_start_tx_ba_cb_irqsafe(il->vif, addr, tid); + } + break; + } + + return 0; +} + +static void +il4965_non_agg_tx_status(struct il_priv *il, const u8 *addr1) +{ + struct ieee80211_sta *sta; + struct il_station_priv *sta_priv; + + rcu_read_lock(); + sta = ieee80211_find_sta(il->vif, addr1); + if (sta) { + sta_priv = (void *)sta->drv_priv; + /* avoid atomic ops if this isn't a client */ + if (sta_priv->client && + atomic_dec_return(&sta_priv->pending_frames) == 0) + ieee80211_sta_block_awake(il->hw, sta, false); + } + rcu_read_unlock(); +} + +static void +il4965_tx_status(struct il_priv *il, struct sk_buff *skb, bool is_agg) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (!is_agg) + il4965_non_agg_tx_status(il, hdr->addr1); + + ieee80211_tx_status_irqsafe(il->hw, skb); +} + +int +il4965_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx) +{ + struct il_tx_queue *txq = &il->txq[txq_id]; + struct il_queue *q = &txq->q; + int nfreed = 0; + struct ieee80211_hdr *hdr; + struct sk_buff *skb; + + if (idx >= q->n_bd || il_queue_used(q, idx) == 0) { + IL_ERR("Read idx for DMA queue txq id (%d), idx %d, " + "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd, + q->write_ptr, q->read_ptr); + return 0; + } + + for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; + q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { + + skb = txq->skbs[txq->q.read_ptr]; + + if (WARN_ON_ONCE(skb == NULL)) + continue; + + hdr = (struct ieee80211_hdr *) skb->data; + if (ieee80211_is_data_qos(hdr->frame_control)) + nfreed++; + + il4965_tx_status(il, skb, txq_id >= IL4965_FIRST_AMPDU_QUEUE); + + txq->skbs[txq->q.read_ptr] = NULL; + il->ops->txq_free_tfd(il, txq); + } + return nfreed; +} + +/** + * il4965_tx_status_reply_compressed_ba - Update tx status from block-ack + * + * Go through block-ack's bitmap of ACK'd frames, update driver's record of + * ACK vs. not. This gets sent to mac80211, then to rate scaling algo. + */ +static int +il4965_tx_status_reply_compressed_ba(struct il_priv *il, struct il_ht_agg *agg, + struct il_compressed_ba_resp *ba_resp) +{ + int i, sh, ack; + u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl); + u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); + int successes = 0; + struct ieee80211_tx_info *info; + u64 bitmap, sent_bitmap; + + if (unlikely(!agg->wait_for_ba)) { + if (unlikely(ba_resp->bitmap)) + IL_ERR("Received BA when not expected\n"); + return -EINVAL; + } + + /* Mark that the expected block-ack response arrived */ + agg->wait_for_ba = 0; + D_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->seq_ctl); + + /* Calculate shift to align block-ack bits with our Tx win bits */ + sh = agg->start_idx - SEQ_TO_IDX(seq_ctl >> 4); + if (sh < 0) /* tbw something is wrong with indices */ + sh += 0x100; + + if (agg->frame_count > (64 - sh)) { + D_TX_REPLY("more frames than bitmap size"); + return -1; + } + + /* don't use 64-bit values for now */ + bitmap = le64_to_cpu(ba_resp->bitmap) >> sh; + + /* check for success or failure according to the + * transmitted bitmap and block-ack bitmap */ + sent_bitmap = bitmap & agg->bitmap; + + /* For each frame attempted in aggregation, + * update driver's record of tx frame's status. */ + i = 0; + while (sent_bitmap) { + ack = sent_bitmap & 1ULL; + successes += ack; + D_TX_REPLY("%s ON i=%d idx=%d raw=%d\n", ack ? "ACK" : "NACK", + i, (agg->start_idx + i) & 0xff, agg->start_idx + i); + sent_bitmap >>= 1; + ++i; + } + + D_TX_REPLY("Bitmap %llx\n", (unsigned long long)bitmap); + + info = IEEE80211_SKB_CB(il->txq[scd_flow].skbs[agg->start_idx]); + memset(&info->status, 0, sizeof(info->status)); + info->flags |= IEEE80211_TX_STAT_ACK; + info->flags |= IEEE80211_TX_STAT_AMPDU; + info->status.ampdu_ack_len = successes; + info->status.ampdu_len = agg->frame_count; + il4965_hwrate_to_tx_control(il, agg->rate_n_flags, info); + + return 0; +} + +static inline bool +il4965_is_tx_success(u32 status) +{ + status &= TX_STATUS_MSK; + return (status == TX_STATUS_SUCCESS || status == TX_STATUS_DIRECT_DONE); +} + +static u8 +il4965_find_station(struct il_priv *il, const u8 *addr) +{ + int i; + int start = 0; + int ret = IL_INVALID_STATION; + unsigned long flags; + + if (il->iw_mode == NL80211_IFTYPE_ADHOC) + start = IL_STA_ID; + + if (is_broadcast_ether_addr(addr)) + return il->hw_params.bcast_id; + + spin_lock_irqsave(&il->sta_lock, flags); + for (i = start; i < il->hw_params.max_stations; i++) + if (il->stations[i].used && + ether_addr_equal(il->stations[i].sta.sta.addr, addr)) { + ret = i; + goto out; + } + + D_ASSOC("can not find STA %pM total %d\n", addr, il->num_stations); + +out: + /* + * It may be possible that more commands interacting with stations + * arrive before we completed processing the adding of + * station + */ + if (ret != IL_INVALID_STATION && + (!(il->stations[ret].used & IL_STA_UCODE_ACTIVE) || + ((il->stations[ret].used & IL_STA_UCODE_ACTIVE) && + (il->stations[ret].used & IL_STA_UCODE_INPROGRESS)))) { + IL_ERR("Requested station info for sta %d before ready.\n", + ret); + ret = IL_INVALID_STATION; + } + spin_unlock_irqrestore(&il->sta_lock, flags); + return ret; +} + +static int +il4965_get_ra_sta_id(struct il_priv *il, struct ieee80211_hdr *hdr) +{ + if (il->iw_mode == NL80211_IFTYPE_STATION) + return IL_AP_ID; + else { + u8 *da = ieee80211_get_DA(hdr); + + return il4965_find_station(il, da); + } +} + +static inline u32 +il4965_get_scd_ssn(struct il4965_tx_resp *tx_resp) +{ + return le32_to_cpup(&tx_resp->u.status + + tx_resp->frame_count) & IEEE80211_MAX_SN; +} + +static inline u32 +il4965_tx_status_to_mac80211(u32 status) +{ + status &= TX_STATUS_MSK; + + switch (status) { + case TX_STATUS_SUCCESS: + case TX_STATUS_DIRECT_DONE: + return IEEE80211_TX_STAT_ACK; + case TX_STATUS_FAIL_DEST_PS: + return IEEE80211_TX_STAT_TX_FILTERED; + default: + return 0; + } +} + +/** + * il4965_tx_status_reply_tx - Handle Tx response for frames in aggregation queue + */ +static int +il4965_tx_status_reply_tx(struct il_priv *il, struct il_ht_agg *agg, + struct il4965_tx_resp *tx_resp, int txq_id, + u16 start_idx) +{ + u16 status; + struct agg_tx_status *frame_status = tx_resp->u.agg_status; + struct ieee80211_tx_info *info = NULL; + struct ieee80211_hdr *hdr = NULL; + u32 rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); + int i, sh, idx; + u16 seq; + if (agg->wait_for_ba) + D_TX_REPLY("got tx response w/o block-ack\n"); + + agg->frame_count = tx_resp->frame_count; + agg->start_idx = start_idx; + agg->rate_n_flags = rate_n_flags; + agg->bitmap = 0; + + /* num frames attempted by Tx command */ + if (agg->frame_count == 1) { + /* Only one frame was attempted; no block-ack will arrive */ + status = le16_to_cpu(frame_status[0].status); + idx = start_idx; + + D_TX_REPLY("FrameCnt = %d, StartIdx=%d idx=%d\n", + agg->frame_count, agg->start_idx, idx); + + info = IEEE80211_SKB_CB(il->txq[txq_id].skbs[idx]); + info->status.rates[0].count = tx_resp->failure_frame + 1; + info->flags &= ~IEEE80211_TX_CTL_AMPDU; + info->flags |= il4965_tx_status_to_mac80211(status); + il4965_hwrate_to_tx_control(il, rate_n_flags, info); + + D_TX_REPLY("1 Frame 0x%x failure :%d\n", status & 0xff, + tx_resp->failure_frame); + D_TX_REPLY("Rate Info rate_n_flags=%x\n", rate_n_flags); + + agg->wait_for_ba = 0; + } else { + /* Two or more frames were attempted; expect block-ack */ + u64 bitmap = 0; + int start = agg->start_idx; + struct sk_buff *skb; + + /* Construct bit-map of pending frames within Tx win */ + for (i = 0; i < agg->frame_count; i++) { + u16 sc; + status = le16_to_cpu(frame_status[i].status); + seq = le16_to_cpu(frame_status[i].sequence); + idx = SEQ_TO_IDX(seq); + txq_id = SEQ_TO_QUEUE(seq); + + if (status & + (AGG_TX_STATE_FEW_BYTES_MSK | + AGG_TX_STATE_ABORT_MSK)) + continue; + + D_TX_REPLY("FrameCnt = %d, txq_id=%d idx=%d\n", + agg->frame_count, txq_id, idx); + + skb = il->txq[txq_id].skbs[idx]; + if (WARN_ON_ONCE(skb == NULL)) + return -1; + hdr = (struct ieee80211_hdr *) skb->data; + + sc = le16_to_cpu(hdr->seq_ctrl); + if (idx != (IEEE80211_SEQ_TO_SN(sc) & 0xff)) { + IL_ERR("BUG_ON idx doesn't match seq control" + " idx=%d, seq_idx=%d, seq=%d\n", idx, + IEEE80211_SEQ_TO_SN(sc), hdr->seq_ctrl); + return -1; + } + + D_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n", i, idx, + IEEE80211_SEQ_TO_SN(sc)); + + sh = idx - start; + if (sh > 64) { + sh = (start - idx) + 0xff; + bitmap = bitmap << sh; + sh = 0; + start = idx; + } else if (sh < -64) + sh = 0xff - (start - idx); + else if (sh < 0) { + sh = start - idx; + start = idx; + bitmap = bitmap << sh; + sh = 0; + } + bitmap |= 1ULL << sh; + D_TX_REPLY("start=%d bitmap=0x%llx\n", start, + (unsigned long long)bitmap); + } + + agg->bitmap = bitmap; + agg->start_idx = start; + D_TX_REPLY("Frames %d start_idx=%d bitmap=0x%llx\n", + agg->frame_count, agg->start_idx, + (unsigned long long)agg->bitmap); + + if (bitmap) + agg->wait_for_ba = 1; + } + return 0; +} + +/** + * il4965_hdl_tx - Handle standard (non-aggregation) Tx response + */ +static void +il4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + u16 sequence = le16_to_cpu(pkt->hdr.sequence); + int txq_id = SEQ_TO_QUEUE(sequence); + int idx = SEQ_TO_IDX(sequence); + struct il_tx_queue *txq = &il->txq[txq_id]; + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *info; + struct il4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; + u32 status = le32_to_cpu(tx_resp->u.status); + int uninitialized_var(tid); + int sta_id; + int freed; + u8 *qc = NULL; + unsigned long flags; + + if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) { + IL_ERR("Read idx for DMA queue txq_id (%d) idx %d " + "is out of range [0-%d] %d %d\n", txq_id, idx, + txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr); + return; + } + + txq->time_stamp = jiffies; + + skb = txq->skbs[txq->q.read_ptr]; + info = IEEE80211_SKB_CB(skb); + memset(&info->status, 0, sizeof(info->status)); + + hdr = (struct ieee80211_hdr *) skb->data; + if (ieee80211_is_data_qos(hdr->frame_control)) { + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + } + + sta_id = il4965_get_ra_sta_id(il, hdr); + if (txq->sched_retry && unlikely(sta_id == IL_INVALID_STATION)) { + IL_ERR("Station not known\n"); + return; + } + + /* + * Firmware will not transmit frame on passive channel, if it not yet + * received some valid frame on that channel. When this error happen + * we have to wait until firmware will unblock itself i.e. when we + * note received beacon or other frame. We unblock queues in + * il4965_pass_packet_to_mac80211 or in il_mac_bss_info_changed. + */ + if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) && + il->iw_mode == NL80211_IFTYPE_STATION) { + il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Stopped queues - RX waiting on passive channel\n"); + } + + spin_lock_irqsave(&il->sta_lock, flags); + if (txq->sched_retry) { + const u32 scd_ssn = il4965_get_scd_ssn(tx_resp); + struct il_ht_agg *agg = NULL; + WARN_ON(!qc); + + agg = &il->stations[sta_id].tid[tid].agg; + + il4965_tx_status_reply_tx(il, agg, tx_resp, txq_id, idx); + + /* check if BAR is needed */ + if (tx_resp->frame_count == 1 && + !il4965_is_tx_success(status)) + info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; + + if (txq->q.read_ptr != (scd_ssn & 0xff)) { + idx = il_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd); + D_TX_REPLY("Retry scheduler reclaim scd_ssn " + "%d idx %d\n", scd_ssn, idx); + freed = il4965_tx_queue_reclaim(il, txq_id, idx); + if (qc) + il4965_free_tfds_in_queue(il, sta_id, tid, + freed); + + if (il->mac80211_registered && + il_queue_space(&txq->q) > txq->q.low_mark && + agg->state != IL_EMPTYING_HW_QUEUE_DELBA) + il_wake_queue(il, txq); + } + } else { + info->status.rates[0].count = tx_resp->failure_frame + 1; + info->flags |= il4965_tx_status_to_mac80211(status); + il4965_hwrate_to_tx_control(il, + le32_to_cpu(tx_resp->rate_n_flags), + info); + + D_TX_REPLY("TXQ %d status %s (0x%08x) " + "rate_n_flags 0x%x retries %d\n", txq_id, + il4965_get_tx_fail_reason(status), status, + le32_to_cpu(tx_resp->rate_n_flags), + tx_resp->failure_frame); + + freed = il4965_tx_queue_reclaim(il, txq_id, idx); + if (qc && likely(sta_id != IL_INVALID_STATION)) + il4965_free_tfds_in_queue(il, sta_id, tid, freed); + else if (sta_id == IL_INVALID_STATION) + D_TX_REPLY("Station not known\n"); + + if (il->mac80211_registered && + il_queue_space(&txq->q) > txq->q.low_mark) + il_wake_queue(il, txq); + } + if (qc && likely(sta_id != IL_INVALID_STATION)) + il4965_txq_check_empty(il, sta_id, tid, txq_id); + + il4965_check_abort_status(il, tx_resp->frame_count, status); + + spin_unlock_irqrestore(&il->sta_lock, flags); +} + +/** + * translate ucode response to mac80211 tx status control values + */ +void +il4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags, + struct ieee80211_tx_info *info) +{ + struct ieee80211_tx_rate *r = &info->status.rates[0]; + + info->status.antenna = + ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); + if (rate_n_flags & RATE_MCS_HT_MSK) + r->flags |= IEEE80211_TX_RC_MCS; + if (rate_n_flags & RATE_MCS_GF_MSK) + r->flags |= IEEE80211_TX_RC_GREEN_FIELD; + if (rate_n_flags & RATE_MCS_HT40_MSK) + r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + if (rate_n_flags & RATE_MCS_DUP_MSK) + r->flags |= IEEE80211_TX_RC_DUP_DATA; + if (rate_n_flags & RATE_MCS_SGI_MSK) + r->flags |= IEEE80211_TX_RC_SHORT_GI; + r->idx = il4965_hwrate_to_mac80211_idx(rate_n_flags, info->band); +} + +/** + * il4965_hdl_compressed_ba - Handler for N_COMPRESSED_BA + * + * Handles block-acknowledge notification from device, which reports success + * of frames sent via aggregation. + */ +static void +il4965_hdl_compressed_ba(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; + struct il_tx_queue *txq = NULL; + struct il_ht_agg *agg; + int idx; + int sta_id; + int tid; + unsigned long flags; + + /* "flow" corresponds to Tx queue */ + u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); + + /* "ssn" is start of block-ack Tx win, corresponds to idx + * (in Tx queue's circular buffer) of first TFD/frame in win */ + u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); + + if (scd_flow >= il->hw_params.max_txq_num) { + IL_ERR("BUG_ON scd_flow is bigger than number of queues\n"); + return; + } + + txq = &il->txq[scd_flow]; + sta_id = ba_resp->sta_id; + tid = ba_resp->tid; + agg = &il->stations[sta_id].tid[tid].agg; + if (unlikely(agg->txq_id != scd_flow)) { + /* + * FIXME: this is a uCode bug which need to be addressed, + * log the information and return for now! + * since it is possible happen very often and in order + * not to fill the syslog, don't enable the logging by default + */ + D_TX_REPLY("BA scd_flow %d does not match txq_id %d\n", + scd_flow, agg->txq_id); + return; + } + + /* Find idx just before block-ack win */ + idx = il_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd); + + spin_lock_irqsave(&il->sta_lock, flags); + + D_TX_REPLY("N_COMPRESSED_BA [%d] Received from %pM, " "sta_id = %d\n", + agg->wait_for_ba, (u8 *) &ba_resp->sta_addr_lo32, + ba_resp->sta_id); + D_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%llx," "scd_flow = " + "%d, scd_ssn = %d\n", ba_resp->tid, ba_resp->seq_ctl, + (unsigned long long)le64_to_cpu(ba_resp->bitmap), + ba_resp->scd_flow, ba_resp->scd_ssn); + D_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx\n", agg->start_idx, + (unsigned long long)agg->bitmap); + + /* Update driver's record of ACK vs. not for each frame in win */ + il4965_tx_status_reply_compressed_ba(il, agg, ba_resp); + + /* Release all TFDs before the SSN, i.e. all TFDs in front of + * block-ack win (we assume that they've been successfully + * transmitted ... if not, it's too late anyway). */ + if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) { + /* calculate mac80211 ampdu sw queue to wake */ + int freed = il4965_tx_queue_reclaim(il, scd_flow, idx); + il4965_free_tfds_in_queue(il, sta_id, tid, freed); + + if (il_queue_space(&txq->q) > txq->q.low_mark && + il->mac80211_registered && + agg->state != IL_EMPTYING_HW_QUEUE_DELBA) + il_wake_queue(il, txq); + + il4965_txq_check_empty(il, sta_id, tid, scd_flow); + } + + spin_unlock_irqrestore(&il->sta_lock, flags); +} + +#ifdef CONFIG_IWLEGACY_DEBUG +const char * +il4965_get_tx_fail_reason(u32 status) +{ +#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x +#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x + + switch (status & TX_STATUS_MSK) { + case TX_STATUS_SUCCESS: + return "SUCCESS"; + TX_STATUS_POSTPONE(DELAY); + TX_STATUS_POSTPONE(FEW_BYTES); + TX_STATUS_POSTPONE(QUIET_PERIOD); + TX_STATUS_POSTPONE(CALC_TTAK); + TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); + TX_STATUS_FAIL(SHORT_LIMIT); + TX_STATUS_FAIL(LONG_LIMIT); + TX_STATUS_FAIL(FIFO_UNDERRUN); + TX_STATUS_FAIL(DRAIN_FLOW); + TX_STATUS_FAIL(RFKILL_FLUSH); + TX_STATUS_FAIL(LIFE_EXPIRE); + TX_STATUS_FAIL(DEST_PS); + TX_STATUS_FAIL(HOST_ABORTED); + TX_STATUS_FAIL(BT_RETRY); + TX_STATUS_FAIL(STA_INVALID); + TX_STATUS_FAIL(FRAG_DROPPED); + TX_STATUS_FAIL(TID_DISABLE); + TX_STATUS_FAIL(FIFO_FLUSHED); + TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); + TX_STATUS_FAIL(PASSIVE_NO_RX); + TX_STATUS_FAIL(NO_BEACON_ON_RADAR); + } + + return "UNKNOWN"; + +#undef TX_STATUS_FAIL +#undef TX_STATUS_POSTPONE +} +#endif /* CONFIG_IWLEGACY_DEBUG */ + +static struct il_link_quality_cmd * +il4965_sta_alloc_lq(struct il_priv *il, u8 sta_id) +{ + int i, r; + struct il_link_quality_cmd *link_cmd; + u32 rate_flags = 0; + __le32 rate_n_flags; + + link_cmd = kzalloc(sizeof(struct il_link_quality_cmd), GFP_KERNEL); + if (!link_cmd) { + IL_ERR("Unable to allocate memory for LQ cmd.\n"); + return NULL; + } + /* Set up the rate scaling to start at selected rate, fall back + * all the way down to 1M in IEEE order, and then spin on 1M */ + if (il->band == IEEE80211_BAND_5GHZ) + r = RATE_6M_IDX; + else + r = RATE_1M_IDX; + + if (r >= IL_FIRST_CCK_RATE && r <= IL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_CCK_MSK; + + rate_flags |= + il4965_first_antenna(il->hw_params. + valid_tx_ant) << RATE_MCS_ANT_POS; + rate_n_flags = cpu_to_le32(il_rates[r].plcp | rate_flags); + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) + link_cmd->rs_table[i].rate_n_flags = rate_n_flags; + + link_cmd->general_params.single_stream_ant_msk = + il4965_first_antenna(il->hw_params.valid_tx_ant); + + link_cmd->general_params.dual_stream_ant_msk = + il->hw_params.valid_tx_ant & ~il4965_first_antenna(il->hw_params. + valid_tx_ant); + if (!link_cmd->general_params.dual_stream_ant_msk) { + link_cmd->general_params.dual_stream_ant_msk = ANT_AB; + } else if (il4965_num_of_ant(il->hw_params.valid_tx_ant) == 2) { + link_cmd->general_params.dual_stream_ant_msk = + il->hw_params.valid_tx_ant; + } + + link_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + link_cmd->agg_params.agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); + + link_cmd->sta_id = sta_id; + + return link_cmd; +} + +/* + * il4965_add_bssid_station - Add the special IBSS BSSID station + * + * Function sleeps. + */ +int +il4965_add_bssid_station(struct il_priv *il, const u8 *addr, u8 *sta_id_r) +{ + int ret; + u8 sta_id; + struct il_link_quality_cmd *link_cmd; + unsigned long flags; + + if (sta_id_r) + *sta_id_r = IL_INVALID_STATION; + + ret = il_add_station_common(il, addr, 0, NULL, &sta_id); + if (ret) { + IL_ERR("Unable to add station %pM\n", addr); + return ret; + } + + if (sta_id_r) + *sta_id_r = sta_id; + + spin_lock_irqsave(&il->sta_lock, flags); + il->stations[sta_id].used |= IL_STA_LOCAL; + spin_unlock_irqrestore(&il->sta_lock, flags); + + /* Set up default rate scaling table in device's station table */ + link_cmd = il4965_sta_alloc_lq(il, sta_id); + if (!link_cmd) { + IL_ERR("Unable to initialize rate scaling for station %pM.\n", + addr); + return -ENOMEM; + } + + ret = il_send_lq_cmd(il, link_cmd, CMD_SYNC, true); + if (ret) + IL_ERR("Link quality command failed (%d)\n", ret); + + spin_lock_irqsave(&il->sta_lock, flags); + il->stations[sta_id].lq = link_cmd; + spin_unlock_irqrestore(&il->sta_lock, flags); + + return 0; +} + +static int +il4965_static_wepkey_cmd(struct il_priv *il, bool send_if_empty) +{ + int i; + u8 buff[sizeof(struct il_wep_cmd) + + sizeof(struct il_wep_key) * WEP_KEYS_MAX]; + struct il_wep_cmd *wep_cmd = (struct il_wep_cmd *)buff; + size_t cmd_size = sizeof(struct il_wep_cmd); + struct il_host_cmd cmd = { + .id = C_WEPKEY, + .data = wep_cmd, + .flags = CMD_SYNC, + }; + bool not_empty = false; + + might_sleep(); + + memset(wep_cmd, 0, + cmd_size + (sizeof(struct il_wep_key) * WEP_KEYS_MAX)); + + for (i = 0; i < WEP_KEYS_MAX; i++) { + u8 key_size = il->_4965.wep_keys[i].key_size; + + wep_cmd->key[i].key_idx = i; + if (key_size) { + wep_cmd->key[i].key_offset = i; + not_empty = true; + } else + wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; + + wep_cmd->key[i].key_size = key_size; + memcpy(&wep_cmd->key[i].key[3], il->_4965.wep_keys[i].key, key_size); + } + + wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; + wep_cmd->num_keys = WEP_KEYS_MAX; + + cmd_size += sizeof(struct il_wep_key) * WEP_KEYS_MAX; + cmd.len = cmd_size; + + if (not_empty || send_if_empty) + return il_send_cmd(il, &cmd); + else + return 0; +} + +int +il4965_restore_default_wep_keys(struct il_priv *il) +{ + lockdep_assert_held(&il->mutex); + + return il4965_static_wepkey_cmd(il, false); +} + +int +il4965_remove_default_wep_key(struct il_priv *il, + struct ieee80211_key_conf *keyconf) +{ + int ret; + int idx = keyconf->keyidx; + + lockdep_assert_held(&il->mutex); + + D_WEP("Removing default WEP key: idx=%d\n", idx); + + memset(&il->_4965.wep_keys[idx], 0, sizeof(struct il_wep_key)); + if (il_is_rfkill(il)) { + D_WEP("Not sending C_WEPKEY command due to RFKILL.\n"); + /* but keys in device are clear anyway so return success */ + return 0; + } + ret = il4965_static_wepkey_cmd(il, 1); + D_WEP("Remove default WEP key: idx=%d ret=%d\n", idx, ret); + + return ret; +} + +int +il4965_set_default_wep_key(struct il_priv *il, + struct ieee80211_key_conf *keyconf) +{ + int ret; + int len = keyconf->keylen; + int idx = keyconf->keyidx; + + lockdep_assert_held(&il->mutex); + + if (len != WEP_KEY_LEN_128 && len != WEP_KEY_LEN_64) { + D_WEP("Bad WEP key length %d\n", keyconf->keylen); + return -EINVAL; + } + + keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; + keyconf->hw_key_idx = HW_KEY_DEFAULT; + il->stations[IL_AP_ID].keyinfo.cipher = keyconf->cipher; + + il->_4965.wep_keys[idx].key_size = len; + memcpy(&il->_4965.wep_keys[idx].key, &keyconf->key, len); + + ret = il4965_static_wepkey_cmd(il, false); + + D_WEP("Set default WEP key: len=%d idx=%d ret=%d\n", len, idx, ret); + return ret; +} + +static int +il4965_set_wep_dynamic_key_info(struct il_priv *il, + struct ieee80211_key_conf *keyconf, u8 sta_id) +{ + unsigned long flags; + __le16 key_flags = 0; + struct il_addsta_cmd sta_cmd; + + lockdep_assert_held(&il->mutex); + + keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; + + key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK); + key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + key_flags &= ~STA_KEY_FLG_INVALID; + + if (keyconf->keylen == WEP_KEY_LEN_128) + key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; + + if (sta_id == il->hw_params.bcast_id) + key_flags |= STA_KEY_MULTICAST_MSK; + + spin_lock_irqsave(&il->sta_lock, flags); + + il->stations[sta_id].keyinfo.cipher = keyconf->cipher; + il->stations[sta_id].keyinfo.keylen = keyconf->keylen; + il->stations[sta_id].keyinfo.keyidx = keyconf->keyidx; + + memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); + + memcpy(&il->stations[sta_id].sta.key.key[3], keyconf->key, + keyconf->keylen); + + if ((il->stations[sta_id].sta.key. + key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) + il->stations[sta_id].sta.key.key_offset = + il_get_free_ucode_key_idx(il); + /* else, we are overriding an existing key => no need to allocated room + * in uCode. */ + + WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, + "no space for a new key"); + + il->stations[sta_id].sta.key.key_flags = key_flags; + il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; + il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + + memcpy(&sta_cmd, &il->stations[sta_id].sta, + sizeof(struct il_addsta_cmd)); + spin_unlock_irqrestore(&il->sta_lock, flags); + + return il_send_add_sta(il, &sta_cmd, CMD_SYNC); +} + +static int +il4965_set_ccmp_dynamic_key_info(struct il_priv *il, + struct ieee80211_key_conf *keyconf, u8 sta_id) +{ + unsigned long flags; + __le16 key_flags = 0; + struct il_addsta_cmd sta_cmd; + + lockdep_assert_held(&il->mutex); + + key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK); + key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + key_flags &= ~STA_KEY_FLG_INVALID; + + if (sta_id == il->hw_params.bcast_id) + key_flags |= STA_KEY_MULTICAST_MSK; + + keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + + spin_lock_irqsave(&il->sta_lock, flags); + il->stations[sta_id].keyinfo.cipher = keyconf->cipher; + il->stations[sta_id].keyinfo.keylen = keyconf->keylen; + + memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); + + memcpy(il->stations[sta_id].sta.key.key, keyconf->key, keyconf->keylen); + + if ((il->stations[sta_id].sta.key. + key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) + il->stations[sta_id].sta.key.key_offset = + il_get_free_ucode_key_idx(il); + /* else, we are overriding an existing key => no need to allocated room + * in uCode. */ + + WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, + "no space for a new key"); + + il->stations[sta_id].sta.key.key_flags = key_flags; + il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; + il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + + memcpy(&sta_cmd, &il->stations[sta_id].sta, + sizeof(struct il_addsta_cmd)); + spin_unlock_irqrestore(&il->sta_lock, flags); + + return il_send_add_sta(il, &sta_cmd, CMD_SYNC); +} + +static int +il4965_set_tkip_dynamic_key_info(struct il_priv *il, + struct ieee80211_key_conf *keyconf, u8 sta_id) +{ + unsigned long flags; + int ret = 0; + __le16 key_flags = 0; + + key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK); + key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + key_flags &= ~STA_KEY_FLG_INVALID; + + if (sta_id == il->hw_params.bcast_id) + key_flags |= STA_KEY_MULTICAST_MSK; + + keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + + spin_lock_irqsave(&il->sta_lock, flags); + + il->stations[sta_id].keyinfo.cipher = keyconf->cipher; + il->stations[sta_id].keyinfo.keylen = 16; + + if ((il->stations[sta_id].sta.key. + key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) + il->stations[sta_id].sta.key.key_offset = + il_get_free_ucode_key_idx(il); + /* else, we are overriding an existing key => no need to allocated room + * in uCode. */ + + WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, + "no space for a new key"); + + il->stations[sta_id].sta.key.key_flags = key_flags; + + /* This copy is acutally not needed: we get the key with each TX */ + memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, 16); + + memcpy(il->stations[sta_id].sta.key.key, keyconf->key, 16); + + spin_unlock_irqrestore(&il->sta_lock, flags); + + return ret; +} + +void +il4965_update_tkip_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) +{ + u8 sta_id; + unsigned long flags; + int i; + + if (il_scan_cancel(il)) { + /* cancel scan failed, just live w/ bad key and rely + briefly on SW decryption */ + return; + } + + sta_id = il_sta_id_or_broadcast(il, sta); + if (sta_id == IL_INVALID_STATION) + return; + + spin_lock_irqsave(&il->sta_lock, flags); + + il->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32; + + for (i = 0; i < 5; i++) + il->stations[sta_id].sta.key.tkip_rx_ttak[i] = + cpu_to_le16(phase1key[i]); + + il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; + il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + + il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC); + + spin_unlock_irqrestore(&il->sta_lock, flags); +} + +int +il4965_remove_dynamic_key(struct il_priv *il, + struct ieee80211_key_conf *keyconf, u8 sta_id) +{ + unsigned long flags; + u16 key_flags; + u8 keyidx; + struct il_addsta_cmd sta_cmd; + + lockdep_assert_held(&il->mutex); + + il->_4965.key_mapping_keys--; + + spin_lock_irqsave(&il->sta_lock, flags); + key_flags = le16_to_cpu(il->stations[sta_id].sta.key.key_flags); + keyidx = (key_flags >> STA_KEY_FLG_KEYID_POS) & 0x3; + + D_WEP("Remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id); + + if (keyconf->keyidx != keyidx) { + /* We need to remove a key with idx different that the one + * in the uCode. This means that the key we need to remove has + * been replaced by another one with different idx. + * Don't do anything and return ok + */ + spin_unlock_irqrestore(&il->sta_lock, flags); + return 0; + } + + if (il->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_INVALID) { + IL_WARN("Removing wrong key %d 0x%x\n", keyconf->keyidx, + key_flags); + spin_unlock_irqrestore(&il->sta_lock, flags); + return 0; + } + + if (!test_and_clear_bit + (il->stations[sta_id].sta.key.key_offset, &il->ucode_key_table)) + IL_ERR("idx %d not used in uCode key table.\n", + il->stations[sta_id].sta.key.key_offset); + memset(&il->stations[sta_id].keyinfo, 0, sizeof(struct il_hw_key)); + memset(&il->stations[sta_id].sta.key, 0, sizeof(struct il4965_keyinfo)); + il->stations[sta_id].sta.key.key_flags = + STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID; + il->stations[sta_id].sta.key.key_offset = keyconf->hw_key_idx; + il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; + il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + + if (il_is_rfkill(il)) { + D_WEP + ("Not sending C_ADD_STA command because RFKILL enabled.\n"); + spin_unlock_irqrestore(&il->sta_lock, flags); + return 0; + } + memcpy(&sta_cmd, &il->stations[sta_id].sta, + sizeof(struct il_addsta_cmd)); + spin_unlock_irqrestore(&il->sta_lock, flags); + + return il_send_add_sta(il, &sta_cmd, CMD_SYNC); +} + +int +il4965_set_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, + u8 sta_id) +{ + int ret; + + lockdep_assert_held(&il->mutex); + + il->_4965.key_mapping_keys++; + keyconf->hw_key_idx = HW_KEY_DYNAMIC; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + ret = + il4965_set_ccmp_dynamic_key_info(il, keyconf, sta_id); + break; + case WLAN_CIPHER_SUITE_TKIP: + ret = + il4965_set_tkip_dynamic_key_info(il, keyconf, sta_id); + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + ret = il4965_set_wep_dynamic_key_info(il, keyconf, sta_id); + break; + default: + IL_ERR("Unknown alg: %s cipher = %x\n", __func__, + keyconf->cipher); + ret = -EINVAL; + } + + D_WEP("Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n", + keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret); + + return ret; +} + +/** + * il4965_alloc_bcast_station - add broadcast station into driver's station table. + * + * This adds the broadcast station into the driver's station table + * and marks it driver active, so that it will be restored to the + * device at the next best time. + */ +int +il4965_alloc_bcast_station(struct il_priv *il) +{ + struct il_link_quality_cmd *link_cmd; + unsigned long flags; + u8 sta_id; + + spin_lock_irqsave(&il->sta_lock, flags); + sta_id = il_prep_station(il, il_bcast_addr, false, NULL); + if (sta_id == IL_INVALID_STATION) { + IL_ERR("Unable to prepare broadcast station\n"); + spin_unlock_irqrestore(&il->sta_lock, flags); + + return -EINVAL; + } + + il->stations[sta_id].used |= IL_STA_DRIVER_ACTIVE; + il->stations[sta_id].used |= IL_STA_BCAST; + spin_unlock_irqrestore(&il->sta_lock, flags); + + link_cmd = il4965_sta_alloc_lq(il, sta_id); + if (!link_cmd) { + IL_ERR + ("Unable to initialize rate scaling for bcast station.\n"); + return -ENOMEM; + } + + spin_lock_irqsave(&il->sta_lock, flags); + il->stations[sta_id].lq = link_cmd; + spin_unlock_irqrestore(&il->sta_lock, flags); + + return 0; +} + +/** + * il4965_update_bcast_station - update broadcast station's LQ command + * + * Only used by iwl4965. Placed here to have all bcast station management + * code together. + */ +static int +il4965_update_bcast_station(struct il_priv *il) +{ + unsigned long flags; + struct il_link_quality_cmd *link_cmd; + u8 sta_id = il->hw_params.bcast_id; + + link_cmd = il4965_sta_alloc_lq(il, sta_id); + if (!link_cmd) { + IL_ERR("Unable to initialize rate scaling for bcast sta.\n"); + return -ENOMEM; + } + + spin_lock_irqsave(&il->sta_lock, flags); + if (il->stations[sta_id].lq) + kfree(il->stations[sta_id].lq); + else + D_INFO("Bcast sta rate scaling has not been initialized.\n"); + il->stations[sta_id].lq = link_cmd; + spin_unlock_irqrestore(&il->sta_lock, flags); + + return 0; +} + +int +il4965_update_bcast_stations(struct il_priv *il) +{ + return il4965_update_bcast_station(il); +} + +/** + * il4965_sta_tx_modify_enable_tid - Enable Tx for this TID in station table + */ +int +il4965_sta_tx_modify_enable_tid(struct il_priv *il, int sta_id, int tid) +{ + unsigned long flags; + struct il_addsta_cmd sta_cmd; + + lockdep_assert_held(&il->mutex); + + /* Remove "disable" flag, to enable Tx for this TID */ + spin_lock_irqsave(&il->sta_lock, flags); + il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; + il->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); + il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &il->stations[sta_id].sta, + sizeof(struct il_addsta_cmd)); + spin_unlock_irqrestore(&il->sta_lock, flags); + + return il_send_add_sta(il, &sta_cmd, CMD_SYNC); +} + +int +il4965_sta_rx_agg_start(struct il_priv *il, struct ieee80211_sta *sta, int tid, + u16 ssn) +{ + unsigned long flags; + int sta_id; + struct il_addsta_cmd sta_cmd; + + lockdep_assert_held(&il->mutex); + + sta_id = il_sta_id(sta); + if (sta_id == IL_INVALID_STATION) + return -ENXIO; + + spin_lock_irqsave(&il->sta_lock, flags); + il->stations[sta_id].sta.station_flags_msk = 0; + il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; + il->stations[sta_id].sta.add_immediate_ba_tid = (u8) tid; + il->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); + il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &il->stations[sta_id].sta, + sizeof(struct il_addsta_cmd)); + spin_unlock_irqrestore(&il->sta_lock, flags); + + return il_send_add_sta(il, &sta_cmd, CMD_SYNC); +} + +int +il4965_sta_rx_agg_stop(struct il_priv *il, struct ieee80211_sta *sta, int tid) +{ + unsigned long flags; + int sta_id; + struct il_addsta_cmd sta_cmd; + + lockdep_assert_held(&il->mutex); + + sta_id = il_sta_id(sta); + if (sta_id == IL_INVALID_STATION) { + IL_ERR("Invalid station for AGG tid %d\n", tid); + return -ENXIO; + } + + spin_lock_irqsave(&il->sta_lock, flags); + il->stations[sta_id].sta.station_flags_msk = 0; + il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; + il->stations[sta_id].sta.remove_immediate_ba_tid = (u8) tid; + il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &il->stations[sta_id].sta, + sizeof(struct il_addsta_cmd)); + spin_unlock_irqrestore(&il->sta_lock, flags); + + return il_send_add_sta(il, &sta_cmd, CMD_SYNC); +} + +void +il4965_sta_modify_sleep_tx_count(struct il_priv *il, int sta_id, int cnt) +{ + unsigned long flags; + + spin_lock_irqsave(&il->sta_lock, flags); + il->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK; + il->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; + il->stations[sta_id].sta.sta.modify_mask = + STA_MODIFY_SLEEP_TX_COUNT_MSK; + il->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt); + il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC); + spin_unlock_irqrestore(&il->sta_lock, flags); + +} + +void +il4965_update_chain_flags(struct il_priv *il) +{ + if (il->ops->set_rxon_chain) { + il->ops->set_rxon_chain(il); + if (il->active.rx_chain != il->staging.rx_chain) + il_commit_rxon(il); + } +} + +static void +il4965_clear_free_frames(struct il_priv *il) +{ + struct list_head *element; + + D_INFO("%d frames on pre-allocated heap on clear.\n", il->frames_count); + + while (!list_empty(&il->free_frames)) { + element = il->free_frames.next; + list_del(element); + kfree(list_entry(element, struct il_frame, list)); + il->frames_count--; + } + + if (il->frames_count) { + IL_WARN("%d frames still in use. Did we lose one?\n", + il->frames_count); + il->frames_count = 0; + } +} + +static struct il_frame * +il4965_get_free_frame(struct il_priv *il) +{ + struct il_frame *frame; + struct list_head *element; + if (list_empty(&il->free_frames)) { + frame = kzalloc(sizeof(*frame), GFP_KERNEL); + if (!frame) { + IL_ERR("Could not allocate frame!\n"); + return NULL; + } + + il->frames_count++; + return frame; + } + + element = il->free_frames.next; + list_del(element); + return list_entry(element, struct il_frame, list); +} + +static void +il4965_free_frame(struct il_priv *il, struct il_frame *frame) +{ + memset(frame, 0, sizeof(*frame)); + list_add(&frame->list, &il->free_frames); +} + +static u32 +il4965_fill_beacon_frame(struct il_priv *il, struct ieee80211_hdr *hdr, + int left) +{ + lockdep_assert_held(&il->mutex); + + if (!il->beacon_skb) + return 0; + + if (il->beacon_skb->len > left) + return 0; + + memcpy(hdr, il->beacon_skb->data, il->beacon_skb->len); + + return il->beacon_skb->len; +} + +/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ +static void +il4965_set_beacon_tim(struct il_priv *il, + struct il_tx_beacon_cmd *tx_beacon_cmd, u8 * beacon, + u32 frame_size) +{ + u16 tim_idx; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; + + /* + * The idx is relative to frame start but we start looking at the + * variable-length part of the beacon. + */ + tim_idx = mgmt->u.beacon.variable - beacon; + + /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ + while ((tim_idx < (frame_size - 2)) && + (beacon[tim_idx] != WLAN_EID_TIM)) + tim_idx += beacon[tim_idx + 1] + 2; + + /* If TIM field was found, set variables */ + if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { + tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); + tx_beacon_cmd->tim_size = beacon[tim_idx + 1]; + } else + IL_WARN("Unable to find TIM Element in beacon\n"); +} + +static unsigned int +il4965_hw_get_beacon_cmd(struct il_priv *il, struct il_frame *frame) +{ + struct il_tx_beacon_cmd *tx_beacon_cmd; + u32 frame_size; + u32 rate_flags; + u32 rate; + /* + * We have to set up the TX command, the TX Beacon command, and the + * beacon contents. + */ + + lockdep_assert_held(&il->mutex); + + if (!il->beacon_enabled) { + IL_ERR("Trying to build beacon without beaconing enabled\n"); + return 0; + } + + /* Initialize memory */ + tx_beacon_cmd = &frame->u.beacon; + memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); + + /* Set up TX beacon contents */ + frame_size = + il4965_fill_beacon_frame(il, tx_beacon_cmd->frame, + sizeof(frame->u) - sizeof(*tx_beacon_cmd)); + if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE)) + return 0; + if (!frame_size) + return 0; + + /* Set up TX command fields */ + tx_beacon_cmd->tx.len = cpu_to_le16((u16) frame_size); + tx_beacon_cmd->tx.sta_id = il->hw_params.bcast_id; + tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + tx_beacon_cmd->tx.tx_flags = + TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK | + TX_CMD_FLG_STA_RATE_MSK; + + /* Set up TX beacon command fields */ + il4965_set_beacon_tim(il, tx_beacon_cmd, (u8 *) tx_beacon_cmd->frame, + frame_size); + + /* Set up packet rate and flags */ + rate = il_get_lowest_plcp(il); + il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant); + rate_flags = BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS; + if ((rate >= IL_FIRST_CCK_RATE) && (rate <= IL_LAST_CCK_RATE)) + rate_flags |= RATE_MCS_CCK_MSK; + tx_beacon_cmd->tx.rate_n_flags = cpu_to_le32(rate | rate_flags); + + return sizeof(*tx_beacon_cmd) + frame_size; +} + +int +il4965_send_beacon_cmd(struct il_priv *il) +{ + struct il_frame *frame; + unsigned int frame_size; + int rc; + + frame = il4965_get_free_frame(il); + if (!frame) { + IL_ERR("Could not obtain free frame buffer for beacon " + "command.\n"); + return -ENOMEM; + } + + frame_size = il4965_hw_get_beacon_cmd(il, frame); + if (!frame_size) { + IL_ERR("Error configuring the beacon command\n"); + il4965_free_frame(il, frame); + return -EINVAL; + } + + rc = il_send_cmd_pdu(il, C_TX_BEACON, frame_size, &frame->u.cmd[0]); + + il4965_free_frame(il, frame); + + return rc; +} + +static inline dma_addr_t +il4965_tfd_tb_get_addr(struct il_tfd *tfd, u8 idx) +{ + struct il_tfd_tb *tb = &tfd->tbs[idx]; + + dma_addr_t addr = get_unaligned_le32(&tb->lo); + if (sizeof(dma_addr_t) > sizeof(u32)) + addr |= + ((dma_addr_t) (le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << + 16; + + return addr; +} + +static inline u16 +il4965_tfd_tb_get_len(struct il_tfd *tfd, u8 idx) +{ + struct il_tfd_tb *tb = &tfd->tbs[idx]; + + return le16_to_cpu(tb->hi_n_len) >> 4; +} + +static inline void +il4965_tfd_set_tb(struct il_tfd *tfd, u8 idx, dma_addr_t addr, u16 len) +{ + struct il_tfd_tb *tb = &tfd->tbs[idx]; + u16 hi_n_len = len << 4; + + put_unaligned_le32(addr, &tb->lo); + if (sizeof(dma_addr_t) > sizeof(u32)) + hi_n_len |= ((addr >> 16) >> 16) & 0xF; + + tb->hi_n_len = cpu_to_le16(hi_n_len); + + tfd->num_tbs = idx + 1; +} + +static inline u8 +il4965_tfd_get_num_tbs(struct il_tfd *tfd) +{ + return tfd->num_tbs & 0x1f; +} + +/** + * il4965_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] + * @il - driver ilate data + * @txq - tx queue + * + * Does NOT advance any TFD circular buffer read/write idxes + * Does NOT free the TFD itself (which is within circular buffer) + */ +void +il4965_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq) +{ + struct il_tfd *tfd_tmp = (struct il_tfd *)txq->tfds; + struct il_tfd *tfd; + struct pci_dev *dev = il->pci_dev; + int idx = txq->q.read_ptr; + int i; + int num_tbs; + + tfd = &tfd_tmp[idx]; + + /* Sanity check on number of chunks */ + num_tbs = il4965_tfd_get_num_tbs(tfd); + + if (num_tbs >= IL_NUM_OF_TBS) { + IL_ERR("Too many chunks: %i\n", num_tbs); + /* @todo issue fatal error, it is quite serious situation */ + return; + } + + /* Unmap tx_cmd */ + if (num_tbs) + pci_unmap_single(dev, dma_unmap_addr(&txq->meta[idx], mapping), + dma_unmap_len(&txq->meta[idx], len), + PCI_DMA_BIDIRECTIONAL); + + /* Unmap chunks, if any. */ + for (i = 1; i < num_tbs; i++) + pci_unmap_single(dev, il4965_tfd_tb_get_addr(tfd, i), + il4965_tfd_tb_get_len(tfd, i), + PCI_DMA_TODEVICE); + + /* free SKB */ + if (txq->skbs) { + struct sk_buff *skb = txq->skbs[txq->q.read_ptr]; + + /* can be called from irqs-disabled context */ + if (skb) { + dev_kfree_skb_any(skb); + txq->skbs[txq->q.read_ptr] = NULL; + } + } +} + +int +il4965_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, + dma_addr_t addr, u16 len, u8 reset, u8 pad) +{ + struct il_queue *q; + struct il_tfd *tfd, *tfd_tmp; + u32 num_tbs; + + q = &txq->q; + tfd_tmp = (struct il_tfd *)txq->tfds; + tfd = &tfd_tmp[q->write_ptr]; + + if (reset) + memset(tfd, 0, sizeof(*tfd)); + + num_tbs = il4965_tfd_get_num_tbs(tfd); + + /* Each TFD can point to a maximum 20 Tx buffers */ + if (num_tbs >= IL_NUM_OF_TBS) { + IL_ERR("Error can not send more than %d chunks\n", + IL_NUM_OF_TBS); + return -EINVAL; + } + + BUG_ON(addr & ~DMA_BIT_MASK(36)); + if (unlikely(addr & ~IL_TX_DMA_MASK)) + IL_ERR("Unaligned address = %llx\n", (unsigned long long)addr); + + il4965_tfd_set_tb(tfd, num_tbs, addr, len); + + return 0; +} + +/* + * Tell nic where to find circular buffer of Tx Frame Descriptors for + * given Tx queue, and enable the DMA channel used for that queue. + * + * 4965 supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA + * channels supported in hardware. + */ +int +il4965_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq) +{ + int txq_id = txq->q.id; + + /* Circular buffer (TFD queue in DRAM) physical base address */ + il_wr(il, FH49_MEM_CBBC_QUEUE(txq_id), txq->q.dma_addr >> 8); + + return 0; +} + +/****************************************************************************** + * + * Generic RX handler implementations + * + ******************************************************************************/ +static void +il4965_hdl_alive(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_alive_resp *palive; + struct delayed_work *pwork; + + palive = &pkt->u.alive_frame; + + D_INFO("Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n", + palive->is_valid, palive->ver_type, palive->ver_subtype); + + if (palive->ver_subtype == INITIALIZE_SUBTYPE) { + D_INFO("Initialization Alive received.\n"); + memcpy(&il->card_alive_init, &pkt->u.alive_frame, + sizeof(struct il_init_alive_resp)); + pwork = &il->init_alive_start; + } else { + D_INFO("Runtime Alive received.\n"); + memcpy(&il->card_alive, &pkt->u.alive_frame, + sizeof(struct il_alive_resp)); + pwork = &il->alive_start; + } + + /* We delay the ALIVE response by 5ms to + * give the HW RF Kill time to activate... */ + if (palive->is_valid == UCODE_VALID_OK) + queue_delayed_work(il->workqueue, pwork, msecs_to_jiffies(5)); + else + IL_WARN("uCode did not respond OK.\n"); +} + +/** + * il4965_bg_stats_periodic - Timer callback to queue stats + * + * This callback is provided in order to send a stats request. + * + * This timer function is continually reset to execute within + * 60 seconds since the last N_STATS was received. We need to + * ensure we receive the stats in order to update the temperature + * used for calibrating the TXPOWER. + */ +static void +il4965_bg_stats_periodic(unsigned long data) +{ + struct il_priv *il = (struct il_priv *)data; + + if (test_bit(S_EXIT_PENDING, &il->status)) + return; + + /* dont send host command if rf-kill is on */ + if (!il_is_ready_rf(il)) + return; + + il_send_stats_request(il, CMD_ASYNC, false); +} + +static void +il4965_hdl_beacon(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il4965_beacon_notif *beacon = + (struct il4965_beacon_notif *)pkt->u.raw; +#ifdef CONFIG_IWLEGACY_DEBUG + u8 rate = il4965_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); + + D_RX("beacon status %x retries %d iss %d tsf:0x%.8x%.8x rate %d\n", + le32_to_cpu(beacon->beacon_notify_hdr.u.status) & TX_STATUS_MSK, + beacon->beacon_notify_hdr.failure_frame, + le32_to_cpu(beacon->ibss_mgr_status), + le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate); +#endif + il->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); +} + +static void +il4965_perform_ct_kill_task(struct il_priv *il) +{ + unsigned long flags; + + D_POWER("Stop all queues\n"); + + if (il->mac80211_registered) + ieee80211_stop_queues(il->hw); + + _il_wr(il, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + _il_rd(il, CSR_UCODE_DRV_GP1); + + spin_lock_irqsave(&il->reg_lock, flags); + if (likely(_il_grab_nic_access(il))) + _il_release_nic_access(il); + spin_unlock_irqrestore(&il->reg_lock, flags); +} + +/* Handle notification from uCode that card's power state is changing + * due to software, hardware, or critical temperature RFKILL */ +static void +il4965_hdl_card_state(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); + unsigned long status = il->status; + + D_RF_KILL("Card state received: HW:%s SW:%s CT:%s\n", + (flags & HW_CARD_DISABLED) ? "Kill" : "On", + (flags & SW_CARD_DISABLED) ? "Kill" : "On", + (flags & CT_CARD_DISABLED) ? "Reached" : "Not reached"); + + if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | CT_CARD_DISABLED)) { + + _il_wr(il, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + il_wr(il, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + + if (!(flags & RXON_CARD_DISABLED)) { + _il_wr(il, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + il_wr(il, HBUS_TARG_MBX_C, + HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + } + } + + if (flags & CT_CARD_DISABLED) + il4965_perform_ct_kill_task(il); + + if (flags & HW_CARD_DISABLED) + set_bit(S_RFKILL, &il->status); + else + clear_bit(S_RFKILL, &il->status); + + if (!(flags & RXON_CARD_DISABLED)) + il_scan_cancel(il); + + if ((test_bit(S_RFKILL, &status) != + test_bit(S_RFKILL, &il->status))) + wiphy_rfkill_set_hw_state(il->hw->wiphy, + test_bit(S_RFKILL, &il->status)); + else + wake_up(&il->wait_command_queue); +} + +/** + * il4965_setup_handlers - Initialize Rx handler callbacks + * + * Setup the RX handlers for each of the reply types sent from the uCode + * to the host. + * + * This function chains into the hardware specific files for them to setup + * any hardware specific handlers as well. + */ +static void +il4965_setup_handlers(struct il_priv *il) +{ + il->handlers[N_ALIVE] = il4965_hdl_alive; + il->handlers[N_ERROR] = il_hdl_error; + il->handlers[N_CHANNEL_SWITCH] = il_hdl_csa; + il->handlers[N_SPECTRUM_MEASUREMENT] = il_hdl_spectrum_measurement; + il->handlers[N_PM_SLEEP] = il_hdl_pm_sleep; + il->handlers[N_PM_DEBUG_STATS] = il_hdl_pm_debug_stats; + il->handlers[N_BEACON] = il4965_hdl_beacon; + + /* + * The same handler is used for both the REPLY to a discrete + * stats request from the host as well as for the periodic + * stats notifications (after received beacons) from the uCode. + */ + il->handlers[C_STATS] = il4965_hdl_c_stats; + il->handlers[N_STATS] = il4965_hdl_stats; + + il_setup_rx_scan_handlers(il); + + /* status change handler */ + il->handlers[N_CARD_STATE] = il4965_hdl_card_state; + + il->handlers[N_MISSED_BEACONS] = il4965_hdl_missed_beacon; + /* Rx handlers */ + il->handlers[N_RX_PHY] = il4965_hdl_rx_phy; + il->handlers[N_RX_MPDU] = il4965_hdl_rx; + il->handlers[N_RX] = il4965_hdl_rx; + /* block ack */ + il->handlers[N_COMPRESSED_BA] = il4965_hdl_compressed_ba; + /* Tx response */ + il->handlers[C_TX] = il4965_hdl_tx; +} + +/** + * il4965_rx_handle - Main entry function for receiving responses from uCode + * + * Uses the il->handlers callback function array to invoke + * the appropriate handlers, including command responses, + * frame-received notifications, and other notifications. + */ +void +il4965_rx_handle(struct il_priv *il) +{ + struct il_rx_buf *rxb; + struct il_rx_pkt *pkt; + struct il_rx_queue *rxq = &il->rxq; + u32 r, i; + int reclaim; + unsigned long flags; + u8 fill_rx = 0; + u32 count = 8; + int total_empty; + + /* uCode's read idx (stored in shared DRAM) indicates the last Rx + * buffer that the driver may process (last buffer filled by ucode). */ + r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF; + i = rxq->read; + + /* Rx interrupt, but nothing sent from uCode */ + if (i == r) + D_RX("r = %d, i = %d\n", r, i); + + /* calculate total frames need to be restock after handling RX */ + total_empty = r - rxq->write_actual; + if (total_empty < 0) + total_empty += RX_QUEUE_SIZE; + + if (total_empty > (RX_QUEUE_SIZE / 2)) + fill_rx = 1; + + while (i != r) { + int len; + + rxb = rxq->queue[i]; + + /* If an RXB doesn't have a Rx queue slot associated with it, + * then a bug has been introduced in the queue refilling + * routines -- catch it here */ + BUG_ON(rxb == NULL); + + rxq->queue[i] = NULL; + + pci_unmap_page(il->pci_dev, rxb->page_dma, + PAGE_SIZE << il->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + pkt = rxb_addr(rxb); + + len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; + len += sizeof(u32); /* account for status word */ + + reclaim = il_need_reclaim(il, pkt); + + /* Based on type of command response or notification, + * handle those that need handling via function in + * handlers table. See il4965_setup_handlers() */ + if (il->handlers[pkt->hdr.cmd]) { + D_RX("r = %d, i = %d, %s, 0x%02x\n", r, i, + il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); + il->isr_stats.handlers[pkt->hdr.cmd]++; + il->handlers[pkt->hdr.cmd] (il, rxb); + } else { + /* No handling needed */ + D_RX("r %d i %d No handler needed for %s, 0x%02x\n", r, + i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); + } + + /* + * XXX: After here, we should always check rxb->page + * against NULL before touching it or its virtual + * memory (pkt). Because some handler might have + * already taken or freed the pages. + */ + + if (reclaim) { + /* Invoke any callbacks, transfer the buffer to caller, + * and fire off the (possibly) blocking il_send_cmd() + * as we reclaim the driver command queue */ + if (rxb->page) + il_tx_cmd_complete(il, rxb); + else + IL_WARN("Claim null rxb?\n"); + } + + /* Reuse the page if possible. For notification packets and + * SKBs that fail to Rx correctly, add them back into the + * rx_free list for reuse later. */ + spin_lock_irqsave(&rxq->lock, flags); + if (rxb->page != NULL) { + rxb->page_dma = + pci_map_page(il->pci_dev, rxb->page, 0, + PAGE_SIZE << il->hw_params. + rx_page_order, PCI_DMA_FROMDEVICE); + + if (unlikely(pci_dma_mapping_error(il->pci_dev, + rxb->page_dma))) { + __il_free_pages(il, rxb->page); + rxb->page = NULL; + list_add_tail(&rxb->list, &rxq->rx_used); + } else { + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + } + } else + list_add_tail(&rxb->list, &rxq->rx_used); + + spin_unlock_irqrestore(&rxq->lock, flags); + + i = (i + 1) & RX_QUEUE_MASK; + /* If there are a lot of unused frames, + * restock the Rx queue so ucode wont assert. */ + if (fill_rx) { + count++; + if (count >= 8) { + rxq->read = i; + il4965_rx_replenish_now(il); + count = 0; + } + } + } + + /* Backtrack one entry */ + rxq->read = i; + if (fill_rx) + il4965_rx_replenish_now(il); + else + il4965_rx_queue_restock(il); +} + +/* call this function to flush any scheduled tasklet */ +static inline void +il4965_synchronize_irq(struct il_priv *il) +{ + /* wait to make sure we flush pending tasklet */ + synchronize_irq(il->pci_dev->irq); + tasklet_kill(&il->irq_tasklet); +} + +static void +il4965_irq_tasklet(struct il_priv *il) +{ + u32 inta, handled = 0; + u32 inta_fh; + unsigned long flags; + u32 i; +#ifdef CONFIG_IWLEGACY_DEBUG + u32 inta_mask; +#endif + + spin_lock_irqsave(&il->lock, flags); + + /* Ack/clear/reset pending uCode interrupts. + * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, + * and will clear only when CSR_FH_INT_STATUS gets cleared. */ + inta = _il_rd(il, CSR_INT); + _il_wr(il, CSR_INT, inta); + + /* Ack/clear/reset pending flow-handler (DMA) interrupts. + * Any new interrupts that happen after this, either while we're + * in this tasklet, or later, will show up in next ISR/tasklet. */ + inta_fh = _il_rd(il, CSR_FH_INT_STATUS); + _il_wr(il, CSR_FH_INT_STATUS, inta_fh); + +#ifdef CONFIG_IWLEGACY_DEBUG + if (il_get_debug_level(il) & IL_DL_ISR) { + /* just for debug */ + inta_mask = _il_rd(il, CSR_INT_MASK); + D_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, + inta_mask, inta_fh); + } +#endif + + spin_unlock_irqrestore(&il->lock, flags); + + /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not + * atomic, make sure that inta covers all the interrupts that + * we've discovered, even if FH interrupt came in just after + * reading CSR_INT. */ + if (inta_fh & CSR49_FH_INT_RX_MASK) + inta |= CSR_INT_BIT_FH_RX; + if (inta_fh & CSR49_FH_INT_TX_MASK) + inta |= CSR_INT_BIT_FH_TX; + + /* Now service all interrupt bits discovered above. */ + if (inta & CSR_INT_BIT_HW_ERR) { + IL_ERR("Hardware error detected. Restarting.\n"); + + /* Tell the device to stop sending interrupts */ + il_disable_interrupts(il); + + il->isr_stats.hw++; + il_irq_handle_error(il); + + handled |= CSR_INT_BIT_HW_ERR; + + return; + } +#ifdef CONFIG_IWLEGACY_DEBUG + if (il_get_debug_level(il) & (IL_DL_ISR)) { + /* NIC fires this, but we don't use it, redundant with WAKEUP */ + if (inta & CSR_INT_BIT_SCD) { + D_ISR("Scheduler finished to transmit " + "the frame/frames.\n"); + il->isr_stats.sch++; + } + + /* Alive notification via Rx interrupt will do the real work */ + if (inta & CSR_INT_BIT_ALIVE) { + D_ISR("Alive interrupt\n"); + il->isr_stats.alive++; + } + } +#endif + /* Safely ignore these bits for debug checks below */ + inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); + + /* HW RF KILL switch toggled */ + if (inta & CSR_INT_BIT_RF_KILL) { + int hw_rf_kill = 0; + + if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) + hw_rf_kill = 1; + + IL_WARN("RF_KILL bit toggled to %s.\n", + hw_rf_kill ? "disable radio" : "enable radio"); + + il->isr_stats.rfkill++; + + /* driver only loads ucode once setting the interface up. + * the driver allows loading the ucode even if the radio + * is killed. Hence update the killswitch state here. The + * rfkill handler will care about restarting if needed. + */ + if (hw_rf_kill) { + set_bit(S_RFKILL, &il->status); + } else { + clear_bit(S_RFKILL, &il->status); + il_force_reset(il, true); + } + wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill); + + handled |= CSR_INT_BIT_RF_KILL; + } + + /* Chip got too hot and stopped itself */ + if (inta & CSR_INT_BIT_CT_KILL) { + IL_ERR("Microcode CT kill error detected.\n"); + il->isr_stats.ctkill++; + handled |= CSR_INT_BIT_CT_KILL; + } + + /* Error detected by uCode */ + if (inta & CSR_INT_BIT_SW_ERR) { + IL_ERR("Microcode SW error detected. " " Restarting 0x%X.\n", + inta); + il->isr_stats.sw++; + il_irq_handle_error(il); + handled |= CSR_INT_BIT_SW_ERR; + } + + /* + * uCode wakes up after power-down sleep. + * Tell device about any new tx or host commands enqueued, + * and about any Rx buffers made available while asleep. + */ + if (inta & CSR_INT_BIT_WAKEUP) { + D_ISR("Wakeup interrupt\n"); + il_rx_queue_update_write_ptr(il, &il->rxq); + for (i = 0; i < il->hw_params.max_txq_num; i++) + il_txq_update_write_ptr(il, &il->txq[i]); + il->isr_stats.wakeup++; + handled |= CSR_INT_BIT_WAKEUP; + } + + /* All uCode command responses, including Tx command responses, + * Rx "responses" (frame-received notification), and other + * notifications from uCode come through here*/ + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { + il4965_rx_handle(il); + il->isr_stats.rx++; + handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); + } + + /* This "Tx" DMA channel is used only for loading uCode */ + if (inta & CSR_INT_BIT_FH_TX) { + D_ISR("uCode load interrupt\n"); + il->isr_stats.tx++; + handled |= CSR_INT_BIT_FH_TX; + /* Wake up uCode load routine, now that load is complete */ + il->ucode_write_complete = 1; + wake_up(&il->wait_command_queue); + } + + if (inta & ~handled) { + IL_ERR("Unhandled INTA bits 0x%08x\n", inta & ~handled); + il->isr_stats.unhandled++; + } + + if (inta & ~(il->inta_mask)) { + IL_WARN("Disabled INTA bits 0x%08x were pending\n", + inta & ~il->inta_mask); + IL_WARN(" with FH49_INT = 0x%08x\n", inta_fh); + } + + /* Re-enable all interrupts */ + /* only Re-enable if disabled by irq */ + if (test_bit(S_INT_ENABLED, &il->status)) + il_enable_interrupts(il); + /* Re-enable RF_KILL if it occurred */ + else if (handled & CSR_INT_BIT_RF_KILL) + il_enable_rfkill_int(il); + +#ifdef CONFIG_IWLEGACY_DEBUG + if (il_get_debug_level(il) & (IL_DL_ISR)) { + inta = _il_rd(il, CSR_INT); + inta_mask = _il_rd(il, CSR_INT_MASK); + inta_fh = _il_rd(il, CSR_FH_INT_STATUS); + D_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, " + "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); + } +#endif +} + +/***************************************************************************** + * + * sysfs attributes + * + *****************************************************************************/ + +#ifdef CONFIG_IWLEGACY_DEBUG + +/* + * The following adds a new attribute to the sysfs representation + * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/) + * used for controlling the debug level. + * + * See the level definitions in iwl for details. + * + * The debug_level being managed using sysfs below is a per device debug + * level that is used instead of the global debug level if it (the per + * device debug level) is set. + */ +static ssize_t +il4965_show_debug_level(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + return sprintf(buf, "0x%08X\n", il_get_debug_level(il)); +} + +static ssize_t +il4965_store_debug_level(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct il_priv *il = dev_get_drvdata(d); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 0, &val); + if (ret) + IL_ERR("%s is not in hex or decimal form.\n", buf); + else + il->debug_level = val; + + return strnlen(buf, count); +} + +static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, il4965_show_debug_level, + il4965_store_debug_level); + +#endif /* CONFIG_IWLEGACY_DEBUG */ + +static ssize_t +il4965_show_temperature(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + + if (!il_is_alive(il)) + return -EAGAIN; + + return sprintf(buf, "%d\n", il->temperature); +} + +static DEVICE_ATTR(temperature, S_IRUGO, il4965_show_temperature, NULL); + +static ssize_t +il4965_show_tx_power(struct device *d, struct device_attribute *attr, char *buf) +{ + struct il_priv *il = dev_get_drvdata(d); + + if (!il_is_ready_rf(il)) + return sprintf(buf, "off\n"); + else + return sprintf(buf, "%d\n", il->tx_power_user_lmt); +} + +static ssize_t +il4965_store_tx_power(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct il_priv *il = dev_get_drvdata(d); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + IL_INFO("%s is not in decimal form.\n", buf); + else { + ret = il_set_tx_power(il, val, false); + if (ret) + IL_ERR("failed setting tx power (0x%08x).\n", ret); + else + ret = count; + } + return ret; +} + +static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, il4965_show_tx_power, + il4965_store_tx_power); + +static struct attribute *il_sysfs_entries[] = { + &dev_attr_temperature.attr, + &dev_attr_tx_power.attr, +#ifdef CONFIG_IWLEGACY_DEBUG + &dev_attr_debug_level.attr, +#endif + NULL +}; + +static struct attribute_group il_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = il_sysfs_entries, +}; + +/****************************************************************************** + * + * uCode download functions + * + ******************************************************************************/ + +static void +il4965_dealloc_ucode_pci(struct il_priv *il) +{ + il_free_fw_desc(il->pci_dev, &il->ucode_code); + il_free_fw_desc(il->pci_dev, &il->ucode_data); + il_free_fw_desc(il->pci_dev, &il->ucode_data_backup); + il_free_fw_desc(il->pci_dev, &il->ucode_init); + il_free_fw_desc(il->pci_dev, &il->ucode_init_data); + il_free_fw_desc(il->pci_dev, &il->ucode_boot); +} + +static void +il4965_nic_start(struct il_priv *il) +{ + /* Remove all resets to allow NIC to operate */ + _il_wr(il, CSR_RESET, 0); +} + +static void il4965_ucode_callback(const struct firmware *ucode_raw, + void *context); +static int il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length); + +static int __must_check +il4965_request_firmware(struct il_priv *il, bool first) +{ + const char *name_pre = il->cfg->fw_name_pre; + char tag[8]; + + if (first) { + il->fw_idx = il->cfg->ucode_api_max; + sprintf(tag, "%d", il->fw_idx); + } else { + il->fw_idx--; + sprintf(tag, "%d", il->fw_idx); + } + + if (il->fw_idx < il->cfg->ucode_api_min) { + IL_ERR("no suitable firmware found!\n"); + return -ENOENT; + } + + sprintf(il->firmware_name, "%s%s%s", name_pre, tag, ".ucode"); + + D_INFO("attempting to load firmware '%s'\n", il->firmware_name); + + return request_firmware_nowait(THIS_MODULE, 1, il->firmware_name, + &il->pci_dev->dev, GFP_KERNEL, il, + il4965_ucode_callback); +} + +struct il4965_firmware_pieces { + const void *inst, *data, *init, *init_data, *boot; + size_t inst_size, data_size, init_size, init_data_size, boot_size; +}; + +static int +il4965_load_firmware(struct il_priv *il, const struct firmware *ucode_raw, + struct il4965_firmware_pieces *pieces) +{ + struct il_ucode_header *ucode = (void *)ucode_raw->data; + u32 api_ver, hdr_size; + const u8 *src; + + il->ucode_ver = le32_to_cpu(ucode->ver); + api_ver = IL_UCODE_API(il->ucode_ver); + + switch (api_ver) { + default: + case 0: + case 1: + case 2: + hdr_size = 24; + if (ucode_raw->size < hdr_size) { + IL_ERR("File size too small!\n"); + return -EINVAL; + } + pieces->inst_size = le32_to_cpu(ucode->v1.inst_size); + pieces->data_size = le32_to_cpu(ucode->v1.data_size); + pieces->init_size = le32_to_cpu(ucode->v1.init_size); + pieces->init_data_size = le32_to_cpu(ucode->v1.init_data_size); + pieces->boot_size = le32_to_cpu(ucode->v1.boot_size); + src = ucode->v1.data; + break; + } + + /* Verify size of file vs. image size info in file's header */ + if (ucode_raw->size != + hdr_size + pieces->inst_size + pieces->data_size + + pieces->init_size + pieces->init_data_size + pieces->boot_size) { + + IL_ERR("uCode file size %d does not match expected size\n", + (int)ucode_raw->size); + return -EINVAL; + } + + pieces->inst = src; + src += pieces->inst_size; + pieces->data = src; + src += pieces->data_size; + pieces->init = src; + src += pieces->init_size; + pieces->init_data = src; + src += pieces->init_data_size; + pieces->boot = src; + src += pieces->boot_size; + + return 0; +} + +/** + * il4965_ucode_callback - callback when firmware was loaded + * + * If loaded successfully, copies the firmware into buffers + * for the card to fetch (via DMA). + */ +static void +il4965_ucode_callback(const struct firmware *ucode_raw, void *context) +{ + struct il_priv *il = context; + struct il_ucode_header *ucode; + int err; + struct il4965_firmware_pieces pieces; + const unsigned int api_max = il->cfg->ucode_api_max; + const unsigned int api_min = il->cfg->ucode_api_min; + u32 api_ver; + + u32 max_probe_length = 200; + u32 standard_phy_calibration_size = + IL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; + + memset(&pieces, 0, sizeof(pieces)); + + if (!ucode_raw) { + if (il->fw_idx <= il->cfg->ucode_api_max) + IL_ERR("request for firmware file '%s' failed.\n", + il->firmware_name); + goto try_again; + } + + D_INFO("Loaded firmware file '%s' (%zd bytes).\n", il->firmware_name, + ucode_raw->size); + + /* Make sure that we got at least the API version number */ + if (ucode_raw->size < 4) { + IL_ERR("File size way too small!\n"); + goto try_again; + } + + /* Data from ucode file: header followed by uCode images */ + ucode = (struct il_ucode_header *)ucode_raw->data; + + err = il4965_load_firmware(il, ucode_raw, &pieces); + + if (err) + goto try_again; + + api_ver = IL_UCODE_API(il->ucode_ver); + + /* + * api_ver should match the api version forming part of the + * firmware filename ... but we don't check for that and only rely + * on the API version read from firmware header from here on forward + */ + if (api_ver < api_min || api_ver > api_max) { + IL_ERR("Driver unable to support your firmware API. " + "Driver supports v%u, firmware is v%u.\n", api_max, + api_ver); + goto try_again; + } + + if (api_ver != api_max) + IL_ERR("Firmware has old API version. Expected v%u, " + "got v%u. New firmware can be obtained " + "from http://www.intellinuxwireless.org.\n", api_max, + api_ver); + + IL_INFO("loaded firmware version %u.%u.%u.%u\n", + IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver), + IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver)); + + snprintf(il->hw->wiphy->fw_version, sizeof(il->hw->wiphy->fw_version), + "%u.%u.%u.%u", IL_UCODE_MAJOR(il->ucode_ver), + IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver), + IL_UCODE_SERIAL(il->ucode_ver)); + + /* + * For any of the failures below (before allocating pci memory) + * we will try to load a version with a smaller API -- maybe the + * user just got a corrupted version of the latest API. + */ + + D_INFO("f/w package hdr ucode version raw = 0x%x\n", il->ucode_ver); + D_INFO("f/w package hdr runtime inst size = %Zd\n", pieces.inst_size); + D_INFO("f/w package hdr runtime data size = %Zd\n", pieces.data_size); + D_INFO("f/w package hdr init inst size = %Zd\n", pieces.init_size); + D_INFO("f/w package hdr init data size = %Zd\n", pieces.init_data_size); + D_INFO("f/w package hdr boot inst size = %Zd\n", pieces.boot_size); + + /* Verify that uCode images will fit in card's SRAM */ + if (pieces.inst_size > il->hw_params.max_inst_size) { + IL_ERR("uCode instr len %Zd too large to fit in\n", + pieces.inst_size); + goto try_again; + } + + if (pieces.data_size > il->hw_params.max_data_size) { + IL_ERR("uCode data len %Zd too large to fit in\n", + pieces.data_size); + goto try_again; + } + + if (pieces.init_size > il->hw_params.max_inst_size) { + IL_ERR("uCode init instr len %Zd too large to fit in\n", + pieces.init_size); + goto try_again; + } + + if (pieces.init_data_size > il->hw_params.max_data_size) { + IL_ERR("uCode init data len %Zd too large to fit in\n", + pieces.init_data_size); + goto try_again; + } + + if (pieces.boot_size > il->hw_params.max_bsm_size) { + IL_ERR("uCode boot instr len %Zd too large to fit in\n", + pieces.boot_size); + goto try_again; + } + + /* Allocate ucode buffers for card's bus-master loading ... */ + + /* Runtime instructions and 2 copies of data: + * 1) unmodified from disk + * 2) backup cache for save/restore during power-downs */ + il->ucode_code.len = pieces.inst_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_code); + + il->ucode_data.len = pieces.data_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_data); + + il->ucode_data_backup.len = pieces.data_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_data_backup); + + if (!il->ucode_code.v_addr || !il->ucode_data.v_addr || + !il->ucode_data_backup.v_addr) + goto err_pci_alloc; + + /* Initialization instructions and data */ + if (pieces.init_size && pieces.init_data_size) { + il->ucode_init.len = pieces.init_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_init); + + il->ucode_init_data.len = pieces.init_data_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_init_data); + + if (!il->ucode_init.v_addr || !il->ucode_init_data.v_addr) + goto err_pci_alloc; + } + + /* Bootstrap (instructions only, no data) */ + if (pieces.boot_size) { + il->ucode_boot.len = pieces.boot_size; + il_alloc_fw_desc(il->pci_dev, &il->ucode_boot); + + if (!il->ucode_boot.v_addr) + goto err_pci_alloc; + } + + /* Now that we can no longer fail, copy information */ + + il->sta_key_max_num = STA_KEY_MAX_NUM; + + /* Copy images into buffers for card's bus-master reads ... */ + + /* Runtime instructions (first block of data in file) */ + D_INFO("Copying (but not loading) uCode instr len %Zd\n", + pieces.inst_size); + memcpy(il->ucode_code.v_addr, pieces.inst, pieces.inst_size); + + D_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", + il->ucode_code.v_addr, (u32) il->ucode_code.p_addr); + + /* + * Runtime data + * NOTE: Copy into backup buffer will be done in il_up() + */ + D_INFO("Copying (but not loading) uCode data len %Zd\n", + pieces.data_size); + memcpy(il->ucode_data.v_addr, pieces.data, pieces.data_size); + memcpy(il->ucode_data_backup.v_addr, pieces.data, pieces.data_size); + + /* Initialization instructions */ + if (pieces.init_size) { + D_INFO("Copying (but not loading) init instr len %Zd\n", + pieces.init_size); + memcpy(il->ucode_init.v_addr, pieces.init, pieces.init_size); + } + + /* Initialization data */ + if (pieces.init_data_size) { + D_INFO("Copying (but not loading) init data len %Zd\n", + pieces.init_data_size); + memcpy(il->ucode_init_data.v_addr, pieces.init_data, + pieces.init_data_size); + } + + /* Bootstrap instructions */ + D_INFO("Copying (but not loading) boot instr len %Zd\n", + pieces.boot_size); + memcpy(il->ucode_boot.v_addr, pieces.boot, pieces.boot_size); + + /* + * figure out the offset of chain noise reset and gain commands + * base on the size of standard phy calibration commands table size + */ + il->_4965.phy_calib_chain_noise_reset_cmd = + standard_phy_calibration_size; + il->_4965.phy_calib_chain_noise_gain_cmd = + standard_phy_calibration_size + 1; + + /************************************************** + * This is still part of probe() in a sense... + * + * 9. Setup and register with mac80211 and debugfs + **************************************************/ + err = il4965_mac_setup_register(il, max_probe_length); + if (err) + goto out_unbind; + + err = il_dbgfs_register(il, DRV_NAME); + if (err) + IL_ERR("failed to create debugfs files. Ignoring error: %d\n", + err); + + err = sysfs_create_group(&il->pci_dev->dev.kobj, &il_attribute_group); + if (err) { + IL_ERR("failed to create sysfs device attributes\n"); + goto out_unbind; + } + + /* We have our copies now, allow OS release its copies */ + release_firmware(ucode_raw); + complete(&il->_4965.firmware_loading_complete); + return; + +try_again: + /* try next, if any */ + if (il4965_request_firmware(il, false)) + goto out_unbind; + release_firmware(ucode_raw); + return; + +err_pci_alloc: + IL_ERR("failed to allocate pci memory\n"); + il4965_dealloc_ucode_pci(il); +out_unbind: + complete(&il->_4965.firmware_loading_complete); + device_release_driver(&il->pci_dev->dev); + release_firmware(ucode_raw); +} + +static const char *const desc_lookup_text[] = { + "OK", + "FAIL", + "BAD_PARAM", + "BAD_CHECKSUM", + "NMI_INTERRUPT_WDG", + "SYSASSERT", + "FATAL_ERROR", + "BAD_COMMAND", + "HW_ERROR_TUNE_LOCK", + "HW_ERROR_TEMPERATURE", + "ILLEGAL_CHAN_FREQ", + "VCC_NOT_STBL", + "FH49_ERROR", + "NMI_INTERRUPT_HOST", + "NMI_INTERRUPT_ACTION_PT", + "NMI_INTERRUPT_UNKNOWN", + "UCODE_VERSION_MISMATCH", + "HW_ERROR_ABS_LOCK", + "HW_ERROR_CAL_LOCK_FAIL", + "NMI_INTERRUPT_INST_ACTION_PT", + "NMI_INTERRUPT_DATA_ACTION_PT", + "NMI_TRM_HW_ER", + "NMI_INTERRUPT_TRM", + "NMI_INTERRUPT_BREAK_POINT", + "DEBUG_0", + "DEBUG_1", + "DEBUG_2", + "DEBUG_3", +}; + +static struct { + char *name; + u8 num; +} advanced_lookup[] = { + { + "NMI_INTERRUPT_WDG", 0x34}, { + "SYSASSERT", 0x35}, { + "UCODE_VERSION_MISMATCH", 0x37}, { + "BAD_COMMAND", 0x38}, { + "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C}, { + "FATAL_ERROR", 0x3D}, { + "NMI_TRM_HW_ERR", 0x46}, { + "NMI_INTERRUPT_TRM", 0x4C}, { + "NMI_INTERRUPT_BREAK_POINT", 0x54}, { + "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C}, { + "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64}, { + "NMI_INTERRUPT_HOST", 0x66}, { + "NMI_INTERRUPT_ACTION_PT", 0x7C}, { + "NMI_INTERRUPT_UNKNOWN", 0x84}, { + "NMI_INTERRUPT_INST_ACTION_PT", 0x86}, { +"ADVANCED_SYSASSERT", 0},}; + +static const char * +il4965_desc_lookup(u32 num) +{ + int i; + int max = ARRAY_SIZE(desc_lookup_text); + + if (num < max) + return desc_lookup_text[num]; + + max = ARRAY_SIZE(advanced_lookup) - 1; + for (i = 0; i < max; i++) { + if (advanced_lookup[i].num == num) + break; + } + return advanced_lookup[i].name; +} + +#define ERROR_START_OFFSET (1 * sizeof(u32)) +#define ERROR_ELEM_SIZE (7 * sizeof(u32)) + +void +il4965_dump_nic_error_log(struct il_priv *il) +{ + u32 data2, line; + u32 desc, time, count, base, data1; + u32 blink1, blink2, ilink1, ilink2; + u32 pc, hcmd; + + if (il->ucode_type == UCODE_INIT) + base = le32_to_cpu(il->card_alive_init.error_event_table_ptr); + else + base = le32_to_cpu(il->card_alive.error_event_table_ptr); + + if (!il->ops->is_valid_rtc_data_addr(base)) { + IL_ERR("Not valid error log pointer 0x%08X for %s uCode\n", + base, (il->ucode_type == UCODE_INIT) ? "Init" : "RT"); + return; + } + + count = il_read_targ_mem(il, base); + + if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { + IL_ERR("Start IWL Error Log Dump:\n"); + IL_ERR("Status: 0x%08lX, count: %d\n", il->status, count); + } + + desc = il_read_targ_mem(il, base + 1 * sizeof(u32)); + il->isr_stats.err_code = desc; + pc = il_read_targ_mem(il, base + 2 * sizeof(u32)); + blink1 = il_read_targ_mem(il, base + 3 * sizeof(u32)); + blink2 = il_read_targ_mem(il, base + 4 * sizeof(u32)); + ilink1 = il_read_targ_mem(il, base + 5 * sizeof(u32)); + ilink2 = il_read_targ_mem(il, base + 6 * sizeof(u32)); + data1 = il_read_targ_mem(il, base + 7 * sizeof(u32)); + data2 = il_read_targ_mem(il, base + 8 * sizeof(u32)); + line = il_read_targ_mem(il, base + 9 * sizeof(u32)); + time = il_read_targ_mem(il, base + 11 * sizeof(u32)); + hcmd = il_read_targ_mem(il, base + 22 * sizeof(u32)); + + IL_ERR("Desc Time " + "data1 data2 line\n"); + IL_ERR("%-28s (0x%04X) %010u 0x%08X 0x%08X %u\n", + il4965_desc_lookup(desc), desc, time, data1, data2, line); + IL_ERR("pc blink1 blink2 ilink1 ilink2 hcmd\n"); + IL_ERR("0x%05X 0x%05X 0x%05X 0x%05X 0x%05X 0x%05X\n", pc, blink1, + blink2, ilink1, ilink2, hcmd); +} + +static void +il4965_rf_kill_ct_config(struct il_priv *il) +{ + struct il_ct_kill_config cmd; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&il->lock, flags); + _il_wr(il, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + spin_unlock_irqrestore(&il->lock, flags); + + cmd.critical_temperature_R = + cpu_to_le32(il->hw_params.ct_kill_threshold); + + ret = il_send_cmd_pdu(il, C_CT_KILL_CONFIG, sizeof(cmd), &cmd); + if (ret) + IL_ERR("C_CT_KILL_CONFIG failed\n"); + else + D_INFO("C_CT_KILL_CONFIG " "succeeded, " + "critical temperature is %d\n", + il->hw_params.ct_kill_threshold); +} + +static const s8 default_queue_to_tx_fifo[] = { + IL_TX_FIFO_VO, + IL_TX_FIFO_VI, + IL_TX_FIFO_BE, + IL_TX_FIFO_BK, + IL49_CMD_FIFO_NUM, + IL_TX_FIFO_UNUSED, + IL_TX_FIFO_UNUSED, +}; + +#define IL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) + +static int +il4965_alive_notify(struct il_priv *il) +{ + u32 a; + unsigned long flags; + int i, chan; + u32 reg_val; + + spin_lock_irqsave(&il->lock, flags); + + /* Clear 4965's internal Tx Scheduler data base */ + il->scd_base_addr = il_rd_prph(il, IL49_SCD_SRAM_BASE_ADDR); + a = il->scd_base_addr + IL49_SCD_CONTEXT_DATA_OFFSET; + for (; a < il->scd_base_addr + IL49_SCD_TX_STTS_BITMAP_OFFSET; a += 4) + il_write_targ_mem(il, a, 0); + for (; a < il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET; a += 4) + il_write_targ_mem(il, a, 0); + for (; + a < + il->scd_base_addr + + IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(il->hw_params.max_txq_num); + a += 4) + il_write_targ_mem(il, a, 0); + + /* Tel 4965 where to find Tx byte count tables */ + il_wr_prph(il, IL49_SCD_DRAM_BASE_ADDR, il->scd_bc_tbls.dma >> 10); + + /* Enable DMA channel */ + for (chan = 0; chan < FH49_TCSR_CHNL_NUM; chan++) + il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(chan), + FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); + + /* Update FH chicken bits */ + reg_val = il_rd(il, FH49_TX_CHICKEN_BITS_REG); + il_wr(il, FH49_TX_CHICKEN_BITS_REG, + reg_val | FH49_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); + + /* Disable chain mode for all queues */ + il_wr_prph(il, IL49_SCD_QUEUECHAIN_SEL, 0); + + /* Initialize each Tx queue (including the command queue) */ + for (i = 0; i < il->hw_params.max_txq_num; i++) { + + /* TFD circular buffer read/write idxes */ + il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(i), 0); + il_wr(il, HBUS_TARG_WRPTR, 0 | (i << 8)); + + /* Max Tx Window size for Scheduler-ACK mode */ + il_write_targ_mem(il, + il->scd_base_addr + + IL49_SCD_CONTEXT_QUEUE_OFFSET(i), + (SCD_WIN_SIZE << + IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) & + IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); + + /* Frame limit */ + il_write_targ_mem(il, + il->scd_base_addr + + IL49_SCD_CONTEXT_QUEUE_OFFSET(i) + + sizeof(u32), + (SCD_FRAME_LIMIT << + IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & + IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); + + } + il_wr_prph(il, IL49_SCD_INTERRUPT_MASK, + (1 << il->hw_params.max_txq_num) - 1); + + /* Activate all Tx DMA/FIFO channels */ + il4965_txq_set_sched(il, IL_MASK(0, 6)); + + il4965_set_wr_ptrs(il, IL_DEFAULT_CMD_QUEUE_NUM, 0); + + /* make sure all queue are not stopped */ + memset(&il->queue_stopped[0], 0, sizeof(il->queue_stopped)); + for (i = 0; i < 4; i++) + atomic_set(&il->queue_stop_count[i], 0); + + /* reset to 0 to enable all the queue first */ + il->txq_ctx_active_msk = 0; + /* Map each Tx/cmd queue to its corresponding fifo */ + BUILD_BUG_ON(ARRAY_SIZE(default_queue_to_tx_fifo) != 7); + + for (i = 0; i < ARRAY_SIZE(default_queue_to_tx_fifo); i++) { + int ac = default_queue_to_tx_fifo[i]; + + il_txq_ctx_activate(il, i); + + if (ac == IL_TX_FIFO_UNUSED) + continue; + + il4965_tx_queue_set_status(il, &il->txq[i], ac, 0); + } + + spin_unlock_irqrestore(&il->lock, flags); + + return 0; +} + +/** + * il4965_alive_start - called after N_ALIVE notification received + * from protocol/runtime uCode (initialization uCode's + * Alive gets handled by il_init_alive_start()). + */ +static void +il4965_alive_start(struct il_priv *il) +{ + int ret = 0; + + D_INFO("Runtime Alive received.\n"); + + if (il->card_alive.is_valid != UCODE_VALID_OK) { + /* We had an error bringing up the hardware, so take it + * all the way back down so we can try again */ + D_INFO("Alive failed.\n"); + goto restart; + } + + /* Initialize uCode has loaded Runtime uCode ... verify inst image. + * This is a paranoid check, because we would not have gotten the + * "runtime" alive if code weren't properly loaded. */ + if (il4965_verify_ucode(il)) { + /* Runtime instruction load was bad; + * take it all the way back down so we can try again */ + D_INFO("Bad runtime uCode load.\n"); + goto restart; + } + + ret = il4965_alive_notify(il); + if (ret) { + IL_WARN("Could not complete ALIVE transition [ntf]: %d\n", ret); + goto restart; + } + + /* After the ALIVE response, we can send host commands to the uCode */ + set_bit(S_ALIVE, &il->status); + + /* Enable watchdog to monitor the driver tx queues */ + il_setup_watchdog(il); + + if (il_is_rfkill(il)) + return; + + ieee80211_wake_queues(il->hw); + + il->active_rate = RATES_MASK; + + il_power_update_mode(il, true); + D_INFO("Updated power mode\n"); + + if (il_is_associated(il)) { + struct il_rxon_cmd *active_rxon = + (struct il_rxon_cmd *)&il->active; + /* apply any changes in staging */ + il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + } else { + /* Initialize our rx_config data */ + il_connection_init_rx_config(il); + + if (il->ops->set_rxon_chain) + il->ops->set_rxon_chain(il); + } + + /* Configure bluetooth coexistence if enabled */ + il_send_bt_config(il); + + il4965_reset_run_time_calib(il); + + set_bit(S_READY, &il->status); + + /* Configure the adapter for unassociated operation */ + il_commit_rxon(il); + + /* At this point, the NIC is initialized and operational */ + il4965_rf_kill_ct_config(il); + + D_INFO("ALIVE processing complete.\n"); + wake_up(&il->wait_command_queue); + + return; + +restart: + queue_work(il->workqueue, &il->restart); +} + +static void il4965_cancel_deferred_work(struct il_priv *il); + +static void +__il4965_down(struct il_priv *il) +{ + unsigned long flags; + int exit_pending; + + D_INFO(DRV_NAME " is going down\n"); + + il_scan_cancel_timeout(il, 200); + + exit_pending = test_and_set_bit(S_EXIT_PENDING, &il->status); + + /* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set + * to prevent rearm timer */ + del_timer_sync(&il->watchdog); + + il_clear_ucode_stations(il); + + /* FIXME: race conditions ? */ + spin_lock_irq(&il->sta_lock); + /* + * Remove all key information that is not stored as part + * of station information since mac80211 may not have had + * a chance to remove all the keys. When device is + * reconfigured by mac80211 after an error all keys will + * be reconfigured. + */ + memset(il->_4965.wep_keys, 0, sizeof(il->_4965.wep_keys)); + il->_4965.key_mapping_keys = 0; + spin_unlock_irq(&il->sta_lock); + + il_dealloc_bcast_stations(il); + il_clear_driver_stations(il); + + /* Unblock any waiting calls */ + wake_up_all(&il->wait_command_queue); + + /* Wipe out the EXIT_PENDING status bit if we are not actually + * exiting the module */ + if (!exit_pending) + clear_bit(S_EXIT_PENDING, &il->status); + + /* stop and reset the on-board processor */ + _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + + /* tell the device to stop sending interrupts */ + spin_lock_irqsave(&il->lock, flags); + il_disable_interrupts(il); + spin_unlock_irqrestore(&il->lock, flags); + il4965_synchronize_irq(il); + + if (il->mac80211_registered) + ieee80211_stop_queues(il->hw); + + /* If we have not previously called il_init() then + * clear all bits but the RF Kill bit and return */ + if (!il_is_init(il)) { + il->status = + test_bit(S_RFKILL, &il->status) << S_RFKILL | + test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | + test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; + goto exit; + } + + /* ...otherwise clear out all the status bits but the RF Kill + * bit and continue taking the NIC down. */ + il->status &= + test_bit(S_RFKILL, &il->status) << S_RFKILL | + test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | + test_bit(S_FW_ERROR, &il->status) << S_FW_ERROR | + test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; + + /* + * We disabled and synchronized interrupt, and priv->mutex is taken, so + * here is the only thread which will program device registers, but + * still have lockdep assertions, so we are taking reg_lock. + */ + spin_lock_irq(&il->reg_lock); + /* FIXME: il_grab_nic_access if rfkill is off ? */ + + il4965_txq_ctx_stop(il); + il4965_rxq_stop(il); + /* Power-down device's busmaster DMA clocks */ + _il_wr_prph(il, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); + udelay(5); + /* Make sure (redundant) we've released our request to stay awake */ + _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + /* Stop the device, and put it in low power state */ + _il_apm_stop(il); + + spin_unlock_irq(&il->reg_lock); + + il4965_txq_ctx_unmap(il); +exit: + memset(&il->card_alive, 0, sizeof(struct il_alive_resp)); + + dev_kfree_skb(il->beacon_skb); + il->beacon_skb = NULL; + + /* clear out any free frames */ + il4965_clear_free_frames(il); +} + +static void +il4965_down(struct il_priv *il) +{ + mutex_lock(&il->mutex); + __il4965_down(il); + mutex_unlock(&il->mutex); + + il4965_cancel_deferred_work(il); +} + + +static void +il4965_set_hw_ready(struct il_priv *il) +{ + int ret; + + il_set_bit(il, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); + + /* See if we got it */ + ret = _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + 100); + if (ret >= 0) + il->hw_ready = true; + + D_INFO("hardware %s ready\n", (il->hw_ready) ? "" : "not"); +} + +static void +il4965_prepare_card_hw(struct il_priv *il) +{ + int ret; + + il->hw_ready = false; + + il4965_set_hw_ready(il); + if (il->hw_ready) + return; + + /* If HW is not ready, prepare the conditions to check again */ + il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE); + + ret = + _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, + ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, + CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); + + /* HW should be ready by now, check again. */ + if (ret != -ETIMEDOUT) + il4965_set_hw_ready(il); +} + +#define MAX_HW_RESTARTS 5 + +static int +__il4965_up(struct il_priv *il) +{ + int i; + int ret; + + if (test_bit(S_EXIT_PENDING, &il->status)) { + IL_WARN("Exit pending; will not bring the NIC up\n"); + return -EIO; + } + + if (!il->ucode_data_backup.v_addr || !il->ucode_data.v_addr) { + IL_ERR("ucode not available for device bringup\n"); + return -EIO; + } + + ret = il4965_alloc_bcast_station(il); + if (ret) { + il_dealloc_bcast_stations(il); + return ret; + } + + il4965_prepare_card_hw(il); + if (!il->hw_ready) { + IL_ERR("HW not ready\n"); + return -EIO; + } + + /* If platform's RF_KILL switch is NOT set to KILL */ + if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) + clear_bit(S_RFKILL, &il->status); + else { + set_bit(S_RFKILL, &il->status); + wiphy_rfkill_set_hw_state(il->hw->wiphy, true); + + il_enable_rfkill_int(il); + IL_WARN("Radio disabled by HW RF Kill switch\n"); + return 0; + } + + _il_wr(il, CSR_INT, 0xFFFFFFFF); + + /* must be initialised before il_hw_nic_init */ + il->cmd_queue = IL_DEFAULT_CMD_QUEUE_NUM; + + ret = il4965_hw_nic_init(il); + if (ret) { + IL_ERR("Unable to init nic\n"); + return ret; + } + + /* make sure rfkill handshake bits are cleared */ + _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + /* clear (again), then enable host interrupts */ + _il_wr(il, CSR_INT, 0xFFFFFFFF); + il_enable_interrupts(il); + + /* really make sure rfkill handshake bits are cleared */ + _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + + /* Copy original ucode data image from disk into backup cache. + * This will be used to initialize the on-board processor's + * data SRAM for a clean start when the runtime program first loads. */ + memcpy(il->ucode_data_backup.v_addr, il->ucode_data.v_addr, + il->ucode_data.len); + + for (i = 0; i < MAX_HW_RESTARTS; i++) { + + /* load bootstrap state machine, + * load bootstrap program into processor's memory, + * prepare to load the "initialize" uCode */ + ret = il->ops->load_ucode(il); + + if (ret) { + IL_ERR("Unable to set up bootstrap uCode: %d\n", ret); + continue; + } + + /* start card; "initialize" will load runtime ucode */ + il4965_nic_start(il); + + D_INFO(DRV_NAME " is coming up\n"); + + return 0; + } + + set_bit(S_EXIT_PENDING, &il->status); + __il4965_down(il); + clear_bit(S_EXIT_PENDING, &il->status); + + /* tried to restart and config the device for as long as our + * patience could withstand */ + IL_ERR("Unable to initialize device after %d attempts.\n", i); + return -EIO; +} + +/***************************************************************************** + * + * Workqueue callbacks + * + *****************************************************************************/ + +static void +il4965_bg_init_alive_start(struct work_struct *data) +{ + struct il_priv *il = + container_of(data, struct il_priv, init_alive_start.work); + + mutex_lock(&il->mutex); + if (test_bit(S_EXIT_PENDING, &il->status)) + goto out; + + il->ops->init_alive_start(il); +out: + mutex_unlock(&il->mutex); +} + +static void +il4965_bg_alive_start(struct work_struct *data) +{ + struct il_priv *il = + container_of(data, struct il_priv, alive_start.work); + + mutex_lock(&il->mutex); + if (test_bit(S_EXIT_PENDING, &il->status)) + goto out; + + il4965_alive_start(il); +out: + mutex_unlock(&il->mutex); +} + +static void +il4965_bg_run_time_calib_work(struct work_struct *work) +{ + struct il_priv *il = container_of(work, struct il_priv, + run_time_calib_work); + + mutex_lock(&il->mutex); + + if (test_bit(S_EXIT_PENDING, &il->status) || + test_bit(S_SCANNING, &il->status)) { + mutex_unlock(&il->mutex); + return; + } + + if (il->start_calib) { + il4965_chain_noise_calibration(il, (void *)&il->_4965.stats); + il4965_sensitivity_calibration(il, (void *)&il->_4965.stats); + } + + mutex_unlock(&il->mutex); +} + +static void +il4965_bg_restart(struct work_struct *data) +{ + struct il_priv *il = container_of(data, struct il_priv, restart); + + if (test_bit(S_EXIT_PENDING, &il->status)) + return; + + if (test_and_clear_bit(S_FW_ERROR, &il->status)) { + mutex_lock(&il->mutex); + il->is_open = 0; + + __il4965_down(il); + + mutex_unlock(&il->mutex); + il4965_cancel_deferred_work(il); + ieee80211_restart_hw(il->hw); + } else { + il4965_down(il); + + mutex_lock(&il->mutex); + if (test_bit(S_EXIT_PENDING, &il->status)) { + mutex_unlock(&il->mutex); + return; + } + + __il4965_up(il); + mutex_unlock(&il->mutex); + } +} + +static void +il4965_bg_rx_replenish(struct work_struct *data) +{ + struct il_priv *il = container_of(data, struct il_priv, rx_replenish); + + if (test_bit(S_EXIT_PENDING, &il->status)) + return; + + mutex_lock(&il->mutex); + il4965_rx_replenish(il); + mutex_unlock(&il->mutex); +} + +/***************************************************************************** + * + * mac80211 entry point functions + * + *****************************************************************************/ + +#define UCODE_READY_TIMEOUT (4 * HZ) + +/* + * Not a mac80211 entry point function, but it fits in with all the + * other mac80211 functions grouped here. + */ +static int +il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) +{ + int ret; + struct ieee80211_hw *hw = il->hw; + + hw->rate_control_algorithm = "iwl-4965-rs"; + + /* Tell mac80211 our characteristics */ + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(hw, SPECTRUM_MGMT); + ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + if (il->cfg->sku & IL_SKU_N) + hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS | + NL80211_FEATURE_STATIC_SMPS; + + hw->sta_data_size = sizeof(struct il_station_priv); + hw->vif_data_size = sizeof(struct il_vif_priv); + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); + + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; + + /* + * For now, disable PS by default because it affects + * RX performance significantly. + */ + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; + /* we create the 802.11 header and a zero-length SSID element */ + hw->wiphy->max_scan_ie_len = max_probe_length - 24 - 2; + + /* Default value; 4 EDCA QOS priorities */ + hw->queues = 4; + + hw->max_listen_interval = IL_CONN_MAX_LISTEN_INTERVAL; + + if (il->bands[IEEE80211_BAND_2GHZ].n_channels) + il->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &il->bands[IEEE80211_BAND_2GHZ]; + if (il->bands[IEEE80211_BAND_5GHZ].n_channels) + il->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &il->bands[IEEE80211_BAND_5GHZ]; + + il_leds_init(il); + + ret = ieee80211_register_hw(il->hw); + if (ret) { + IL_ERR("Failed to register hw (error %d)\n", ret); + return ret; + } + il->mac80211_registered = 1; + + return 0; +} + +int +il4965_mac_start(struct ieee80211_hw *hw) +{ + struct il_priv *il = hw->priv; + int ret; + + D_MAC80211("enter\n"); + + /* we should be verifying the device is ready to be opened */ + mutex_lock(&il->mutex); + ret = __il4965_up(il); + mutex_unlock(&il->mutex); + + if (ret) + return ret; + + if (il_is_rfkill(il)) + goto out; + + D_INFO("Start UP work done.\n"); + + /* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from + * mac80211 will not be run successfully. */ + ret = wait_event_timeout(il->wait_command_queue, + test_bit(S_READY, &il->status), + UCODE_READY_TIMEOUT); + if (!ret) { + if (!test_bit(S_READY, &il->status)) { + IL_ERR("START_ALIVE timeout after %dms.\n", + jiffies_to_msecs(UCODE_READY_TIMEOUT)); + return -ETIMEDOUT; + } + } + + il4965_led_enable(il); + +out: + il->is_open = 1; + D_MAC80211("leave\n"); + return 0; +} + +void +il4965_mac_stop(struct ieee80211_hw *hw) +{ + struct il_priv *il = hw->priv; + + D_MAC80211("enter\n"); + + if (!il->is_open) + return; + + il->is_open = 0; + + il4965_down(il); + + flush_workqueue(il->workqueue); + + /* User space software may expect getting rfkill changes + * even if interface is down */ + _il_wr(il, CSR_INT, 0xFFFFFFFF); + il_enable_rfkill_int(il); + + D_MAC80211("leave\n"); +} + +void +il4965_mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct il_priv *il = hw->priv; + + D_MACDUMP("enter\n"); + + D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, + ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); + + if (il4965_tx_skb(il, control->sta, skb)) + dev_kfree_skb_any(skb); + + D_MACDUMP("leave\n"); +} + +void +il4965_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, u16 * phase1key) +{ + struct il_priv *il = hw->priv; + + D_MAC80211("enter\n"); + + il4965_update_tkip_key(il, keyconf, sta, iv32, phase1key); + + D_MAC80211("leave\n"); +} + +int +il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct il_priv *il = hw->priv; + int ret; + u8 sta_id; + bool is_default_wep_key = false; + + D_MAC80211("enter\n"); + + if (il->cfg->mod_params->sw_crypto) { + D_MAC80211("leave - hwcrypto disabled\n"); + return -EOPNOTSUPP; + } + + /* + * To support IBSS RSN, don't program group keys in IBSS, the + * hardware will then not attempt to decrypt the frames. + */ + if (vif->type == NL80211_IFTYPE_ADHOC && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + D_MAC80211("leave - ad-hoc group key\n"); + return -EOPNOTSUPP; + } + + sta_id = il_sta_id_or_broadcast(il, sta); + if (sta_id == IL_INVALID_STATION) + return -EINVAL; + + mutex_lock(&il->mutex); + il_scan_cancel_timeout(il, 100); + + /* + * If we are getting WEP group key and we didn't receive any key mapping + * so far, we are in legacy wep mode (group key only), otherwise we are + * in 1X mode. + * In legacy wep mode, we use another host command to the uCode. + */ + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { + if (cmd == SET_KEY) + is_default_wep_key = !il->_4965.key_mapping_keys; + else + is_default_wep_key = + (key->hw_key_idx == HW_KEY_DEFAULT); + } + + switch (cmd) { + case SET_KEY: + if (is_default_wep_key) + ret = il4965_set_default_wep_key(il, key); + else + ret = il4965_set_dynamic_key(il, key, sta_id); + + D_MAC80211("enable hwcrypto key\n"); + break; + case DISABLE_KEY: + if (is_default_wep_key) + ret = il4965_remove_default_wep_key(il, key); + else + ret = il4965_remove_dynamic_key(il, key, sta_id); + + D_MAC80211("disable hwcrypto key\n"); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&il->mutex); + D_MAC80211("leave\n"); + + return ret; +} + +int +il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 * ssn, + u8 buf_size, bool amsdu) +{ + struct il_priv *il = hw->priv; + int ret = -EINVAL; + + D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid); + + if (!(il->cfg->sku & IL_SKU_N)) + return -EACCES; + + mutex_lock(&il->mutex); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + D_HT("start Rx\n"); + ret = il4965_sta_rx_agg_start(il, sta, tid, *ssn); + break; + case IEEE80211_AMPDU_RX_STOP: + D_HT("stop Rx\n"); + ret = il4965_sta_rx_agg_stop(il, sta, tid); + if (test_bit(S_EXIT_PENDING, &il->status)) + ret = 0; + break; + case IEEE80211_AMPDU_TX_START: + D_HT("start Tx\n"); + ret = il4965_tx_agg_start(il, vif, sta, tid, ssn); + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + D_HT("stop Tx\n"); + ret = il4965_tx_agg_stop(il, vif, sta, tid); + if (test_bit(S_EXIT_PENDING, &il->status)) + ret = 0; + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + ret = 0; + break; + } + mutex_unlock(&il->mutex); + + return ret; +} + +int +il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct il_priv *il = hw->priv; + struct il_station_priv *sta_priv = (void *)sta->drv_priv; + bool is_ap = vif->type == NL80211_IFTYPE_STATION; + int ret; + u8 sta_id; + + D_INFO("received request to add station %pM\n", sta->addr); + mutex_lock(&il->mutex); + D_INFO("proceeding to add station %pM\n", sta->addr); + sta_priv->common.sta_id = IL_INVALID_STATION; + + atomic_set(&sta_priv->pending_frames, 0); + + ret = + il_add_station_common(il, sta->addr, is_ap, sta, &sta_id); + if (ret) { + IL_ERR("Unable to add station %pM (%d)\n", sta->addr, ret); + /* Should we return success if return code is EEXIST ? */ + mutex_unlock(&il->mutex); + return ret; + } + + sta_priv->common.sta_id = sta_id; + + /* Initialize rate scaling */ + D_INFO("Initializing rate scaling for station %pM\n", sta->addr); + il4965_rs_rate_init(il, sta, sta_id); + mutex_unlock(&il->mutex); + + return 0; +} + +void +il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_channel_switch *ch_switch) +{ + struct il_priv *il = hw->priv; + const struct il_channel_info *ch_info; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_channel *channel = ch_switch->chandef.chan; + struct il_ht_config *ht_conf = &il->current_ht_config; + u16 ch; + + D_MAC80211("enter\n"); + + mutex_lock(&il->mutex); + + if (il_is_rfkill(il)) + goto out; + + if (test_bit(S_EXIT_PENDING, &il->status) || + test_bit(S_SCANNING, &il->status) || + test_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) + goto out; + + if (!il_is_associated(il)) + goto out; + + if (!il->ops->set_channel_switch) + goto out; + + ch = channel->hw_value; + if (le16_to_cpu(il->active.channel) == ch) + goto out; + + ch_info = il_get_channel_info(il, channel->band, ch); + if (!il_is_channel_valid(ch_info)) { + D_MAC80211("invalid channel\n"); + goto out; + } + + spin_lock_irq(&il->lock); + + il->current_ht_config.smps = conf->smps_mode; + + /* Configure HT40 channels */ + switch (cfg80211_get_chandef_type(&ch_switch->chandef)) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + il->ht.is_40mhz = false; + il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + break; + case NL80211_CHAN_HT40MINUS: + il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + il->ht.is_40mhz = true; + break; + case NL80211_CHAN_HT40PLUS: + il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + il->ht.is_40mhz = true; + break; + } + + if ((le16_to_cpu(il->staging.channel) != ch)) + il->staging.flags = 0; + + il_set_rxon_channel(il, channel); + il_set_rxon_ht(il, ht_conf); + il_set_flags_for_band(il, channel->band, il->vif); + + spin_unlock_irq(&il->lock); + + il_set_rate(il); + /* + * at this point, staging_rxon has the + * configuration for channel switch + */ + set_bit(S_CHANNEL_SWITCH_PENDING, &il->status); + il->switch_channel = cpu_to_le16(ch); + if (il->ops->set_channel_switch(il, ch_switch)) { + clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status); + il->switch_channel = 0; + ieee80211_chswitch_done(il->vif, false); + } + +out: + mutex_unlock(&il->mutex); + D_MAC80211("leave\n"); +} + +void +il4965_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct il_priv *il = hw->priv; + __le32 filter_or = 0, filter_nand = 0; + +#define CHK(test, flag) do { \ + if (*total_flags & (test)) \ + filter_or |= (flag); \ + else \ + filter_nand |= (flag); \ + } while (0) + + D_MAC80211("Enter: changed: 0x%x, total: 0x%x\n", changed_flags, + *total_flags); + + CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK); + /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ + CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); + CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); + +#undef CHK + + mutex_lock(&il->mutex); + + il->staging.filter_flags &= ~filter_nand; + il->staging.filter_flags |= filter_or; + + /* + * Not committing directly because hardware can perform a scan, + * but we'll eventually commit the filter flags change anyway. + */ + + mutex_unlock(&il->mutex); + + /* + * Receiving all multicast frames is always enabled by the + * default flags setup in il_connection_init_rx_config() + * since we currently do not support programming multicast + * filters into the device. + */ + *total_flags &= + FIF_OTHER_BSS | FIF_ALLMULTI | + FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; +} + +/***************************************************************************** + * + * driver setup and teardown + * + *****************************************************************************/ + +static void +il4965_bg_txpower_work(struct work_struct *work) +{ + struct il_priv *il = container_of(work, struct il_priv, + txpower_work); + + mutex_lock(&il->mutex); + + /* If a scan happened to start before we got here + * then just return; the stats notification will + * kick off another scheduled work to compensate for + * any temperature delta we missed here. */ + if (test_bit(S_EXIT_PENDING, &il->status) || + test_bit(S_SCANNING, &il->status)) + goto out; + + /* Regardless of if we are associated, we must reconfigure the + * TX power since frames can be sent on non-radar channels while + * not associated */ + il->ops->send_tx_power(il); + + /* Update last_temperature to keep is_calib_needed from running + * when it isn't needed... */ + il->last_temperature = il->temperature; +out: + mutex_unlock(&il->mutex); +} + +static void +il4965_setup_deferred_work(struct il_priv *il) +{ + il->workqueue = create_singlethread_workqueue(DRV_NAME); + + init_waitqueue_head(&il->wait_command_queue); + + INIT_WORK(&il->restart, il4965_bg_restart); + INIT_WORK(&il->rx_replenish, il4965_bg_rx_replenish); + INIT_WORK(&il->run_time_calib_work, il4965_bg_run_time_calib_work); + INIT_DELAYED_WORK(&il->init_alive_start, il4965_bg_init_alive_start); + INIT_DELAYED_WORK(&il->alive_start, il4965_bg_alive_start); + + il_setup_scan_deferred_work(il); + + INIT_WORK(&il->txpower_work, il4965_bg_txpower_work); + + setup_timer(&il->stats_periodic, il4965_bg_stats_periodic, + (unsigned long)il); + + setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il); + + tasklet_init(&il->irq_tasklet, + (void (*)(unsigned long))il4965_irq_tasklet, + (unsigned long)il); +} + +static void +il4965_cancel_deferred_work(struct il_priv *il) +{ + cancel_work_sync(&il->txpower_work); + cancel_delayed_work_sync(&il->init_alive_start); + cancel_delayed_work(&il->alive_start); + cancel_work_sync(&il->run_time_calib_work); + + il_cancel_scan_deferred_work(il); + + del_timer_sync(&il->stats_periodic); +} + +static void +il4965_init_hw_rates(struct il_priv *il, struct ieee80211_rate *rates) +{ + int i; + + for (i = 0; i < RATE_COUNT_LEGACY; i++) { + rates[i].bitrate = il_rates[i].ieee * 5; + rates[i].hw_value = i; /* Rate scaling will work on idxes */ + rates[i].hw_value_short = i; + rates[i].flags = 0; + if ((i >= IL_FIRST_CCK_RATE) && (i <= IL_LAST_CCK_RATE)) { + /* + * If CCK != 1M then set short preamble rate flag. + */ + rates[i].flags |= + (il_rates[i].plcp == + RATE_1M_PLCP) ? 0 : IEEE80211_RATE_SHORT_PREAMBLE; + } + } +} + +/* + * Acquire il->lock before calling this function ! + */ +void +il4965_set_wr_ptrs(struct il_priv *il, int txq_id, u32 idx) +{ + il_wr(il, HBUS_TARG_WRPTR, (idx & 0xff) | (txq_id << 8)); + il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(txq_id), idx); +} + +void +il4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq, + int tx_fifo_id, int scd_retry) +{ + int txq_id = txq->q.id; + + /* Find out whether to activate Tx queue */ + int active = test_bit(txq_id, &il->txq_ctx_active_msk) ? 1 : 0; + + /* Set up and activate */ + il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id), + (active << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) | + (tx_fifo_id << IL49_SCD_QUEUE_STTS_REG_POS_TXF) | + (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_WSL) | + (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK) | + IL49_SCD_QUEUE_STTS_REG_MSK); + + txq->sched_retry = scd_retry; + + D_INFO("%s %s Queue %d on AC %d\n", active ? "Activate" : "Deactivate", + scd_retry ? "BA" : "AC", txq_id, tx_fifo_id); +} + +static const struct ieee80211_ops il4965_mac_ops = { + .tx = il4965_mac_tx, + .start = il4965_mac_start, + .stop = il4965_mac_stop, + .add_interface = il_mac_add_interface, + .remove_interface = il_mac_remove_interface, + .change_interface = il_mac_change_interface, + .config = il_mac_config, + .configure_filter = il4965_configure_filter, + .set_key = il4965_mac_set_key, + .update_tkip_key = il4965_mac_update_tkip_key, + .conf_tx = il_mac_conf_tx, + .reset_tsf = il_mac_reset_tsf, + .bss_info_changed = il_mac_bss_info_changed, + .ampdu_action = il4965_mac_ampdu_action, + .hw_scan = il_mac_hw_scan, + .sta_add = il4965_mac_sta_add, + .sta_remove = il_mac_sta_remove, + .channel_switch = il4965_mac_channel_switch, + .tx_last_beacon = il_mac_tx_last_beacon, + .flush = il_mac_flush, +}; + +static int +il4965_init_drv(struct il_priv *il) +{ + int ret; + + spin_lock_init(&il->sta_lock); + spin_lock_init(&il->hcmd_lock); + + INIT_LIST_HEAD(&il->free_frames); + + mutex_init(&il->mutex); + + il->ieee_channels = NULL; + il->ieee_rates = NULL; + il->band = IEEE80211_BAND_2GHZ; + + il->iw_mode = NL80211_IFTYPE_STATION; + il->current_ht_config.smps = IEEE80211_SMPS_STATIC; + il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF; + + /* initialize force reset */ + il->force_reset.reset_duration = IL_DELAY_NEXT_FORCE_FW_RELOAD; + + /* Choose which receivers/antennas to use */ + if (il->ops->set_rxon_chain) + il->ops->set_rxon_chain(il); + + il_init_scan_params(il); + + ret = il_init_channel_map(il); + if (ret) { + IL_ERR("initializing regulatory failed: %d\n", ret); + goto err; + } + + ret = il_init_geos(il); + if (ret) { + IL_ERR("initializing geos failed: %d\n", ret); + goto err_free_channel_map; + } + il4965_init_hw_rates(il, il->ieee_rates); + + return 0; + +err_free_channel_map: + il_free_channel_map(il); +err: + return ret; +} + +static void +il4965_uninit_drv(struct il_priv *il) +{ + il_free_geos(il); + il_free_channel_map(il); + kfree(il->scan_cmd); +} + +static void +il4965_hw_detect(struct il_priv *il) +{ + il->hw_rev = _il_rd(il, CSR_HW_REV); + il->hw_wa_rev = _il_rd(il, CSR_HW_REV_WA_REG); + il->rev_id = il->pci_dev->revision; + D_INFO("HW Revision ID = 0x%X\n", il->rev_id); +} + +static struct il_sensitivity_ranges il4965_sensitivity = { + .min_nrg_cck = 97, + .max_nrg_cck = 0, /* not used, set to 0 */ + + .auto_corr_min_ofdm = 85, + .auto_corr_min_ofdm_mrc = 170, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 220, + + .auto_corr_max_ofdm = 120, + .auto_corr_max_ofdm_mrc = 210, + .auto_corr_max_ofdm_x1 = 140, + .auto_corr_max_ofdm_mrc_x1 = 270, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 200, + .auto_corr_min_cck_mrc = 200, + .auto_corr_max_cck_mrc = 400, + + .nrg_th_cck = 100, + .nrg_th_ofdm = 100, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, +}; + +static void +il4965_set_hw_params(struct il_priv *il) +{ + il->hw_params.bcast_id = IL4965_BROADCAST_ID; + il->hw_params.max_rxq_size = RX_QUEUE_SIZE; + il->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; + if (il->cfg->mod_params->amsdu_size_8K) + il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_8K); + else + il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_4K); + + il->hw_params.max_beacon_itrvl = IL_MAX_UCODE_BEACON_INTERVAL; + + if (il->cfg->mod_params->disable_11n) + il->cfg->sku &= ~IL_SKU_N; + + if (il->cfg->mod_params->num_of_queues >= IL_MIN_NUM_QUEUES && + il->cfg->mod_params->num_of_queues <= IL49_NUM_QUEUES) + il->cfg->num_of_queues = + il->cfg->mod_params->num_of_queues; + + il->hw_params.max_txq_num = il->cfg->num_of_queues; + il->hw_params.dma_chnl_num = FH49_TCSR_CHNL_NUM; + il->hw_params.scd_bc_tbls_size = + il->cfg->num_of_queues * + sizeof(struct il4965_scd_bc_tbl); + + il->hw_params.tfd_size = sizeof(struct il_tfd); + il->hw_params.max_stations = IL4965_STATION_COUNT; + il->hw_params.max_data_size = IL49_RTC_DATA_SIZE; + il->hw_params.max_inst_size = IL49_RTC_INST_SIZE; + il->hw_params.max_bsm_size = BSM_SRAM_SIZE; + il->hw_params.ht40_channel = BIT(IEEE80211_BAND_5GHZ); + + il->hw_params.rx_wrt_ptr_reg = FH49_RSCSR_CHNL0_WPTR; + + il->hw_params.tx_chains_num = il4965_num_of_ant(il->cfg->valid_tx_ant); + il->hw_params.rx_chains_num = il4965_num_of_ant(il->cfg->valid_rx_ant); + il->hw_params.valid_tx_ant = il->cfg->valid_tx_ant; + il->hw_params.valid_rx_ant = il->cfg->valid_rx_ant; + + il->hw_params.ct_kill_threshold = + CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY); + + il->hw_params.sens = &il4965_sensitivity; + il->hw_params.beacon_time_tsf_bits = IL4965_EXT_BEACON_TIME_POS; +} + +static int +il4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = 0; + struct il_priv *il; + struct ieee80211_hw *hw; + struct il_cfg *cfg = (struct il_cfg *)(ent->driver_data); + unsigned long flags; + u16 pci_cmd; + + /************************ + * 1. Allocating HW data + ************************/ + + hw = ieee80211_alloc_hw(sizeof(struct il_priv), &il4965_mac_ops); + if (!hw) { + err = -ENOMEM; + goto out; + } + il = hw->priv; + il->hw = hw; + SET_IEEE80211_DEV(hw, &pdev->dev); + + D_INFO("*** LOAD DRIVER ***\n"); + il->cfg = cfg; + il->ops = &il4965_ops; +#ifdef CONFIG_IWLEGACY_DEBUGFS + il->debugfs_ops = &il4965_debugfs_ops; +#endif + il->pci_dev = pdev; + il->inta_mask = CSR_INI_SET_MASK; + + /************************** + * 2. Initializing PCI bus + **************************/ + pci_disable_link_state(pdev, + PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM); + + if (pci_enable_device(pdev)) { + err = -ENODEV; + goto out_ieee80211_free_hw; + } + + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); + if (!err) + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36)); + if (err) { + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (!err) + err = + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + /* both attempts failed: */ + if (err) { + IL_WARN("No suitable DMA available.\n"); + goto out_pci_disable_device; + } + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) + goto out_pci_disable_device; + + pci_set_drvdata(pdev, il); + + /*********************** + * 3. Read REV register + ***********************/ + il->hw_base = pci_ioremap_bar(pdev, 0); + if (!il->hw_base) { + err = -ENODEV; + goto out_pci_release_regions; + } + + D_INFO("pci_resource_len = 0x%08llx\n", + (unsigned long long)pci_resource_len(pdev, 0)); + D_INFO("pci_resource_base = %p\n", il->hw_base); + + /* these spin locks will be used in apm_ops.init and EEPROM access + * we should init now + */ + spin_lock_init(&il->reg_lock); + spin_lock_init(&il->lock); + + /* + * stop and reset the on-board processor just in case it is in a + * strange state ... like being left stranded by a primary kernel + * and this is now the kdump kernel trying to start up + */ + _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + + il4965_hw_detect(il); + IL_INFO("Detected %s, REV=0x%X\n", il->cfg->name, il->hw_rev); + + /* We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state */ + pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); + + il4965_prepare_card_hw(il); + if (!il->hw_ready) { + IL_WARN("Failed, HW not ready\n"); + err = -EIO; + goto out_iounmap; + } + + /***************** + * 4. Read EEPROM + *****************/ + /* Read the EEPROM */ + err = il_eeprom_init(il); + if (err) { + IL_ERR("Unable to init EEPROM\n"); + goto out_iounmap; + } + err = il4965_eeprom_check_version(il); + if (err) + goto out_free_eeprom; + + /* extract MAC Address */ + il4965_eeprom_get_mac(il, il->addresses[0].addr); + D_INFO("MAC address: %pM\n", il->addresses[0].addr); + il->hw->wiphy->addresses = il->addresses; + il->hw->wiphy->n_addresses = 1; + + /************************ + * 5. Setup HW constants + ************************/ + il4965_set_hw_params(il); + + /******************* + * 6. Setup il + *******************/ + + err = il4965_init_drv(il); + if (err) + goto out_free_eeprom; + /* At this point both hw and il are initialized. */ + + /******************** + * 7. Setup services + ********************/ + spin_lock_irqsave(&il->lock, flags); + il_disable_interrupts(il); + spin_unlock_irqrestore(&il->lock, flags); + + pci_enable_msi(il->pci_dev); + + err = request_irq(il->pci_dev->irq, il_isr, IRQF_SHARED, DRV_NAME, il); + if (err) { + IL_ERR("Error allocating IRQ %d\n", il->pci_dev->irq); + goto out_disable_msi; + } + + il4965_setup_deferred_work(il); + il4965_setup_handlers(il); + + /********************************************* + * 8. Enable interrupts and read RFKILL state + *********************************************/ + + /* enable rfkill interrupt: hw bug w/a */ + pci_read_config_word(il->pci_dev, PCI_COMMAND, &pci_cmd); + if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { + pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(il->pci_dev, PCI_COMMAND, pci_cmd); + } + + il_enable_rfkill_int(il); + + /* If platform's RF_KILL switch is NOT set to KILL */ + if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) + clear_bit(S_RFKILL, &il->status); + else + set_bit(S_RFKILL, &il->status); + + wiphy_rfkill_set_hw_state(il->hw->wiphy, + test_bit(S_RFKILL, &il->status)); + + il_power_initialize(il); + + init_completion(&il->_4965.firmware_loading_complete); + + err = il4965_request_firmware(il, true); + if (err) + goto out_destroy_workqueue; + + return 0; + +out_destroy_workqueue: + destroy_workqueue(il->workqueue); + il->workqueue = NULL; + free_irq(il->pci_dev->irq, il); +out_disable_msi: + pci_disable_msi(il->pci_dev); + il4965_uninit_drv(il); +out_free_eeprom: + il_eeprom_free(il); +out_iounmap: + iounmap(il->hw_base); +out_pci_release_regions: + pci_release_regions(pdev); +out_pci_disable_device: + pci_disable_device(pdev); +out_ieee80211_free_hw: + ieee80211_free_hw(il->hw); +out: + return err; +} + +static void +il4965_pci_remove(struct pci_dev *pdev) +{ + struct il_priv *il = pci_get_drvdata(pdev); + unsigned long flags; + + if (!il) + return; + + wait_for_completion(&il->_4965.firmware_loading_complete); + + D_INFO("*** UNLOAD DRIVER ***\n"); + + il_dbgfs_unregister(il); + sysfs_remove_group(&pdev->dev.kobj, &il_attribute_group); + + /* ieee80211_unregister_hw call wil cause il_mac_stop to + * to be called and il4965_down since we are removing the device + * we need to set S_EXIT_PENDING bit. + */ + set_bit(S_EXIT_PENDING, &il->status); + + il_leds_exit(il); + + if (il->mac80211_registered) { + ieee80211_unregister_hw(il->hw); + il->mac80211_registered = 0; + } else { + il4965_down(il); + } + + /* + * Make sure device is reset to low power before unloading driver. + * This may be redundant with il4965_down(), but there are paths to + * run il4965_down() without calling apm_ops.stop(), and there are + * paths to avoid running il4965_down() at all before leaving driver. + * This (inexpensive) call *makes sure* device is reset. + */ + il_apm_stop(il); + + /* make sure we flush any pending irq or + * tasklet for the driver + */ + spin_lock_irqsave(&il->lock, flags); + il_disable_interrupts(il); + spin_unlock_irqrestore(&il->lock, flags); + + il4965_synchronize_irq(il); + + il4965_dealloc_ucode_pci(il); + + if (il->rxq.bd) + il4965_rx_queue_free(il, &il->rxq); + il4965_hw_txq_ctx_free(il); + + il_eeprom_free(il); + + /*netif_stop_queue(dev); */ + flush_workqueue(il->workqueue); + + /* ieee80211_unregister_hw calls il_mac_stop, which flushes + * il->workqueue... so we can't take down the workqueue + * until now... */ + destroy_workqueue(il->workqueue); + il->workqueue = NULL; + + free_irq(il->pci_dev->irq, il); + pci_disable_msi(il->pci_dev); + iounmap(il->hw_base); + pci_release_regions(pdev); + pci_disable_device(pdev); + + il4965_uninit_drv(il); + + dev_kfree_skb(il->beacon_skb); + + ieee80211_free_hw(il->hw); +} + +/* + * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask + * must be called under il->lock and mac access + */ +void +il4965_txq_set_sched(struct il_priv *il, u32 mask) +{ + il_wr_prph(il, IL49_SCD_TXFACT, mask); +} + +/***************************************************************************** + * + * driver and module entry point + * + *****************************************************************************/ + +/* Hardware specific file defines the PCI IDs table for that hardware module */ +static const struct pci_device_id il4965_hw_card_ids[] = { + {IL_PCI_DEVICE(0x4229, PCI_ANY_ID, il4965_cfg)}, + {IL_PCI_DEVICE(0x4230, PCI_ANY_ID, il4965_cfg)}, + {0} +}; +MODULE_DEVICE_TABLE(pci, il4965_hw_card_ids); + +static struct pci_driver il4965_driver = { + .name = DRV_NAME, + .id_table = il4965_hw_card_ids, + .probe = il4965_pci_probe, + .remove = il4965_pci_remove, + .driver.pm = IL_LEGACY_PM_OPS, +}; + +static int __init +il4965_init(void) +{ + + int ret; + pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); + pr_info(DRV_COPYRIGHT "\n"); + + ret = il4965_rate_control_register(); + if (ret) { + pr_err("Unable to register rate control algorithm: %d\n", ret); + return ret; + } + + ret = pci_register_driver(&il4965_driver); + if (ret) { + pr_err("Unable to initialize PCI module\n"); + goto error_register; + } + + return ret; + +error_register: + il4965_rate_control_unregister(); + return ret; +} + +static void __exit +il4965_exit(void) +{ + pci_unregister_driver(&il4965_driver); + il4965_rate_control_unregister(); +} + +module_exit(il4965_exit); +module_init(il4965_init); + +#ifdef CONFIG_IWLEGACY_DEBUG +module_param_named(debug, il_debug_level, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "debug output mask"); +#endif + +module_param_named(swcrypto, il4965_mod_params.sw_crypto, int, S_IRUGO); +MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); +module_param_named(queues_num, il4965_mod_params.num_of_queues, int, S_IRUGO); +MODULE_PARM_DESC(queues_num, "number of hw queues."); +module_param_named(11n_disable, il4965_mod_params.disable_11n, int, S_IRUGO); +MODULE_PARM_DESC(11n_disable, "disable 11n functionality"); +module_param_named(amsdu_size_8K, il4965_mod_params.amsdu_size_8K, int, + S_IRUGO); +MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0 [disabled])"); +module_param_named(fw_restart, il4965_mod_params.restart_fw, int, S_IRUGO); +MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); diff --git a/drivers/net/wireless/intel/iwlegacy/4965-rs.c b/drivers/net/wireless/intel/iwlegacy/4965-rs.c new file mode 100644 index 000000000000..bac60b2bc3f0 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/4965-rs.c @@ -0,0 +1,2835 @@ +/****************************************************************************** + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "common.h" +#include "4965.h" + +#define IL4965_RS_NAME "iwl-4965-rs" + +#define NUM_TRY_BEFORE_ANT_TOGGLE 1 +#define IL_NUMBER_TRY 1 +#define IL_HT_NUMBER_TRY 3 + +#define RATE_MAX_WINDOW 62 /* # tx in history win */ +#define RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ +#define RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ + +/* max allowed rate miss before sync LQ cmd */ +#define IL_MISSED_RATE_MAX 15 +/* max time to accum history 2 seconds */ +#define RATE_SCALE_FLUSH_INTVL (3*HZ) + +static u8 rs_ht_to_legacy[] = { + RATE_6M_IDX, RATE_6M_IDX, + RATE_6M_IDX, RATE_6M_IDX, + RATE_6M_IDX, + RATE_6M_IDX, RATE_9M_IDX, + RATE_12M_IDX, RATE_18M_IDX, + RATE_24M_IDX, RATE_36M_IDX, + RATE_48M_IDX, RATE_54M_IDX +}; + +static const u8 ant_toggle_lookup[] = { + /*ANT_NONE -> */ ANT_NONE, + /*ANT_A -> */ ANT_B, + /*ANT_B -> */ ANT_C, + /*ANT_AB -> */ ANT_BC, + /*ANT_C -> */ ANT_A, + /*ANT_AC -> */ ANT_AB, + /*ANT_BC -> */ ANT_AC, + /*ANT_ABC -> */ ANT_ABC, +}; + +#define IL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ + [RATE_##r##M_IDX] = { RATE_##r##M_PLCP, \ + RATE_SISO_##s##M_PLCP, \ + RATE_MIMO2_##s##M_PLCP,\ + RATE_##r##M_IEEE, \ + RATE_##ip##M_IDX, \ + RATE_##in##M_IDX, \ + RATE_##rp##M_IDX, \ + RATE_##rn##M_IDX, \ + RATE_##pp##M_IDX, \ + RATE_##np##M_IDX } + +/* + * Parameter order: + * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate + * + * If there isn't a valid next or previous rate then INV is used which + * maps to RATE_INVALID + * + */ +const struct il_rate_info il_rates[RATE_COUNT] = { + IL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ + IL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ + IL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ + IL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ + IL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ + IL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ + IL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ + IL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ + IL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ + IL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ + IL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ + IL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ + IL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ +}; + +static int +il4965_hwrate_to_plcp_idx(u32 rate_n_flags) +{ + int idx = 0; + + /* HT rate format */ + if (rate_n_flags & RATE_MCS_HT_MSK) { + idx = (rate_n_flags & 0xff); + + if (idx >= RATE_MIMO2_6M_PLCP) + idx = idx - RATE_MIMO2_6M_PLCP; + + idx += IL_FIRST_OFDM_RATE; + /* skip 9M not supported in ht */ + if (idx >= RATE_9M_IDX) + idx += 1; + if (idx >= IL_FIRST_OFDM_RATE && idx <= IL_LAST_OFDM_RATE) + return idx; + + /* legacy rate format, search for match in table */ + } else { + for (idx = 0; idx < ARRAY_SIZE(il_rates); idx++) + if (il_rates[idx].plcp == (rate_n_flags & 0xFF)) + return idx; + } + + return -1; +} + +static void il4965_rs_rate_scale_perform(struct il_priv *il, + struct sk_buff *skb, + struct ieee80211_sta *sta, + struct il_lq_sta *lq_sta); +static void il4965_rs_fill_link_cmd(struct il_priv *il, + struct il_lq_sta *lq_sta, u32 rate_n_flags); +static void il4965_rs_stay_in_table(struct il_lq_sta *lq_sta, + bool force_search); + +#ifdef CONFIG_MAC80211_DEBUGFS +static void il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, + u32 *rate_n_flags, int idx); +#else +static void +il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx) +{ +} +#endif + +/** + * The following tables contain the expected throughput metrics for all rates + * + * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits + * + * where invalid entries are zeros. + * + * CCK rates are only valid in legacy table and will only be used in G + * (2.4 GHz) band. + */ + +static s32 expected_tpt_legacy[RATE_COUNT] = { + 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 +}; + +static s32 expected_tpt_siso20MHz[4][RATE_COUNT] = { + {0, 0, 0, 0, 42, 0, 76, 102, 124, 158, 183, 193, 202}, /* Norm */ + {0, 0, 0, 0, 46, 0, 82, 110, 132, 167, 192, 202, 210}, /* SGI */ + {0, 0, 0, 0, 48, 0, 93, 135, 176, 251, 319, 351, 381}, /* AGG */ + {0, 0, 0, 0, 53, 0, 102, 149, 193, 275, 348, 381, 413}, /* AGG+SGI */ +}; + +static s32 expected_tpt_siso40MHz[4][RATE_COUNT] = { + {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ + {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ + {0, 0, 0, 0, 96, 0, 182, 259, 328, 451, 553, 598, 640}, /* AGG */ + {0, 0, 0, 0, 106, 0, 199, 282, 357, 487, 593, 640, 683}, /* AGG+SGI */ +}; + +static s32 expected_tpt_mimo2_20MHz[4][RATE_COUNT] = { + {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250}, /* Norm */ + {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256}, /* SGI */ + {0, 0, 0, 0, 92, 0, 175, 250, 317, 436, 534, 578, 619}, /* AGG */ + {0, 0, 0, 0, 102, 0, 192, 273, 344, 470, 573, 619, 660}, /* AGG+SGI */ +}; + +static s32 expected_tpt_mimo2_40MHz[4][RATE_COUNT] = { + {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ + {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ + {0, 0, 0, 0, 180, 0, 327, 446, 545, 708, 828, 878, 922}, /* AGG */ + {0, 0, 0, 0, 197, 0, 355, 481, 584, 752, 872, 922, 966}, /* AGG+SGI */ +}; + +/* mbps, mcs */ +static const struct il_rate_mcs_info il_rate_mcs[RATE_COUNT] = { + {"1", "BPSK DSSS"}, + {"2", "QPSK DSSS"}, + {"5.5", "BPSK CCK"}, + {"11", "QPSK CCK"}, + {"6", "BPSK 1/2"}, + {"9", "BPSK 1/2"}, + {"12", "QPSK 1/2"}, + {"18", "QPSK 3/4"}, + {"24", "16QAM 1/2"}, + {"36", "16QAM 3/4"}, + {"48", "64QAM 2/3"}, + {"54", "64QAM 3/4"}, + {"60", "64QAM 5/6"}, +}; + +#define MCS_IDX_PER_STREAM (8) + +static inline u8 +il4965_rs_extract_rate(u32 rate_n_flags) +{ + return (u8) (rate_n_flags & 0xFF); +} + +static void +il4965_rs_rate_scale_clear_win(struct il_rate_scale_data *win) +{ + win->data = 0; + win->success_counter = 0; + win->success_ratio = IL_INVALID_VALUE; + win->counter = 0; + win->average_tpt = IL_INVALID_VALUE; + win->stamp = 0; +} + +static inline u8 +il4965_rs_is_valid_ant(u8 valid_antenna, u8 ant_type) +{ + return (ant_type & valid_antenna) == ant_type; +} + +/* + * removes the old data from the stats. All data that is older than + * TID_MAX_TIME_DIFF, will be deleted. + */ +static void +il4965_rs_tl_rm_old_stats(struct il_traffic_load *tl, u32 curr_time) +{ + /* The oldest age we want to keep */ + u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; + + while (tl->queue_count && tl->time_stamp < oldest_time) { + tl->total -= tl->packet_count[tl->head]; + tl->packet_count[tl->head] = 0; + tl->time_stamp += TID_QUEUE_CELL_SPACING; + tl->queue_count--; + tl->head++; + if (tl->head >= TID_QUEUE_MAX_SIZE) + tl->head = 0; + } +} + +/* + * increment traffic load value for tid and also remove + * any old values if passed the certain time period + */ +static u8 +il4965_rs_tl_add_packet(struct il_lq_sta *lq_data, struct ieee80211_hdr *hdr) +{ + u32 curr_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 idx; + struct il_traffic_load *tl = NULL; + u8 tid; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + } else + return MAX_TID_COUNT; + + if (unlikely(tid >= TID_MAX_LOAD_COUNT)) + return MAX_TID_COUNT; + + tl = &lq_data->load[tid]; + + curr_time -= curr_time % TID_ROUND_VALUE; + + /* Happens only for the first packet. Initialize the data */ + if (!(tl->queue_count)) { + tl->total = 1; + tl->time_stamp = curr_time; + tl->queue_count = 1; + tl->head = 0; + tl->packet_count[0] = 1; + return MAX_TID_COUNT; + } + + time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); + idx = time_diff / TID_QUEUE_CELL_SPACING; + + /* The history is too long: remove data that is older than */ + /* TID_MAX_TIME_DIFF */ + if (idx >= TID_QUEUE_MAX_SIZE) + il4965_rs_tl_rm_old_stats(tl, curr_time); + + idx = (tl->head + idx) % TID_QUEUE_MAX_SIZE; + tl->packet_count[idx] = tl->packet_count[idx] + 1; + tl->total = tl->total + 1; + + if ((idx + 1) > tl->queue_count) + tl->queue_count = idx + 1; + + return tid; +} + +/* + get the traffic load value for tid +*/ +static u32 +il4965_rs_tl_get_load(struct il_lq_sta *lq_data, u8 tid) +{ + u32 curr_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 idx; + struct il_traffic_load *tl = NULL; + + if (tid >= TID_MAX_LOAD_COUNT) + return 0; + + tl = &(lq_data->load[tid]); + + curr_time -= curr_time % TID_ROUND_VALUE; + + if (!(tl->queue_count)) + return 0; + + time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); + idx = time_diff / TID_QUEUE_CELL_SPACING; + + /* The history is too long: remove data that is older than */ + /* TID_MAX_TIME_DIFF */ + if (idx >= TID_QUEUE_MAX_SIZE) + il4965_rs_tl_rm_old_stats(tl, curr_time); + + return tl->total; +} + +static int +il4965_rs_tl_turn_on_agg_for_tid(struct il_priv *il, struct il_lq_sta *lq_data, + u8 tid, struct ieee80211_sta *sta) +{ + int ret = -EAGAIN; + u32 load; + + load = il4965_rs_tl_get_load(lq_data, tid); + + if (load > IL_AGG_LOAD_THRESHOLD) { + D_HT("Starting Tx agg: STA: %pM tid: %d\n", sta->addr, tid); + ret = ieee80211_start_tx_ba_session(sta, tid, 5000); + if (ret == -EAGAIN) { + /* + * driver and mac80211 is out of sync + * this might be cause by reloading firmware + * stop the tx ba session here + */ + IL_ERR("Fail start Tx agg on tid: %d\n", tid); + ieee80211_stop_tx_ba_session(sta, tid); + } + } else + D_HT("Aggregation not enabled for tid %d because load = %u\n", + tid, load); + + return ret; +} + +static void +il4965_rs_tl_turn_on_agg(struct il_priv *il, u8 tid, struct il_lq_sta *lq_data, + struct ieee80211_sta *sta) +{ + if (tid < TID_MAX_LOAD_COUNT) + il4965_rs_tl_turn_on_agg_for_tid(il, lq_data, tid, sta); + else + IL_ERR("tid exceeds max load count: %d/%d\n", tid, + TID_MAX_LOAD_COUNT); +} + +static inline int +il4965_get_il4965_num_of_ant_from_rate(u32 rate_n_flags) +{ + return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_C_MSK); +} + +/* + * Static function to get the expected throughput from an il_scale_tbl_info + * that wraps a NULL pointer check + */ +static s32 +il4965_get_expected_tpt(struct il_scale_tbl_info *tbl, int rs_idx) +{ + if (tbl->expected_tpt) + return tbl->expected_tpt[rs_idx]; + return 0; +} + +/** + * il4965_rs_collect_tx_data - Update the success/failure sliding win + * + * We keep a sliding win of the last 62 packets transmitted + * at this rate. win->data contains the bitmask of successful + * packets. + */ +static int +il4965_rs_collect_tx_data(struct il_scale_tbl_info *tbl, int scale_idx, + int attempts, int successes) +{ + struct il_rate_scale_data *win = NULL; + static const u64 mask = (((u64) 1) << (RATE_MAX_WINDOW - 1)); + s32 fail_count, tpt; + + if (scale_idx < 0 || scale_idx >= RATE_COUNT) + return -EINVAL; + + /* Select win for current tx bit rate */ + win = &(tbl->win[scale_idx]); + + /* Get expected throughput */ + tpt = il4965_get_expected_tpt(tbl, scale_idx); + + /* + * Keep track of only the latest 62 tx frame attempts in this rate's + * history win; anything older isn't really relevant any more. + * If we have filled up the sliding win, drop the oldest attempt; + * if the oldest attempt (highest bit in bitmap) shows "success", + * subtract "1" from the success counter (this is the main reason + * we keep these bitmaps!). + */ + while (attempts > 0) { + if (win->counter >= RATE_MAX_WINDOW) { + + /* remove earliest */ + win->counter = RATE_MAX_WINDOW - 1; + + if (win->data & mask) { + win->data &= ~mask; + win->success_counter--; + } + } + + /* Increment frames-attempted counter */ + win->counter++; + + /* Shift bitmap by one frame to throw away oldest history */ + win->data <<= 1; + + /* Mark the most recent #successes attempts as successful */ + if (successes > 0) { + win->success_counter++; + win->data |= 0x1; + successes--; + } + + attempts--; + } + + /* Calculate current success ratio, avoid divide-by-0! */ + if (win->counter > 0) + win->success_ratio = + 128 * (100 * win->success_counter) / win->counter; + else + win->success_ratio = IL_INVALID_VALUE; + + fail_count = win->counter - win->success_counter; + + /* Calculate average throughput, if we have enough history. */ + if (fail_count >= RATE_MIN_FAILURE_TH || + win->success_counter >= RATE_MIN_SUCCESS_TH) + win->average_tpt = (win->success_ratio * tpt + 64) / 128; + else + win->average_tpt = IL_INVALID_VALUE; + + /* Tag this win as having been updated */ + win->stamp = jiffies; + + return 0; +} + +/* + * Fill uCode API rate_n_flags field, based on "search" or "active" table. + */ +static u32 +il4965_rate_n_flags_from_tbl(struct il_priv *il, struct il_scale_tbl_info *tbl, + int idx, u8 use_green) +{ + u32 rate_n_flags = 0; + + if (is_legacy(tbl->lq_type)) { + rate_n_flags = il_rates[idx].plcp; + if (idx >= IL_FIRST_CCK_RATE && idx <= IL_LAST_CCK_RATE) + rate_n_flags |= RATE_MCS_CCK_MSK; + + } else if (is_Ht(tbl->lq_type)) { + if (idx > IL_LAST_OFDM_RATE) { + IL_ERR("Invalid HT rate idx %d\n", idx); + idx = IL_LAST_OFDM_RATE; + } + rate_n_flags = RATE_MCS_HT_MSK; + + if (is_siso(tbl->lq_type)) + rate_n_flags |= il_rates[idx].plcp_siso; + else + rate_n_flags |= il_rates[idx].plcp_mimo2; + } else { + IL_ERR("Invalid tbl->lq_type %d\n", tbl->lq_type); + } + + rate_n_flags |= + ((tbl->ant_type << RATE_MCS_ANT_POS) & RATE_MCS_ANT_ABC_MSK); + + if (is_Ht(tbl->lq_type)) { + if (tbl->is_ht40) { + if (tbl->is_dup) + rate_n_flags |= RATE_MCS_DUP_MSK; + else + rate_n_flags |= RATE_MCS_HT40_MSK; + } + if (tbl->is_SGI) + rate_n_flags |= RATE_MCS_SGI_MSK; + + if (use_green) { + rate_n_flags |= RATE_MCS_GF_MSK; + if (is_siso(tbl->lq_type) && tbl->is_SGI) { + rate_n_flags &= ~RATE_MCS_SGI_MSK; + IL_ERR("GF was set with SGI:SISO\n"); + } + } + } + return rate_n_flags; +} + +/* + * Interpret uCode API's rate_n_flags format, + * fill "search" or "active" tx mode table. + */ +static int +il4965_rs_get_tbl_info_from_mcs(const u32 rate_n_flags, + enum ieee80211_band band, + struct il_scale_tbl_info *tbl, int *rate_idx) +{ + u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); + u8 il4965_num_of_ant = + il4965_get_il4965_num_of_ant_from_rate(rate_n_flags); + u8 mcs; + + memset(tbl, 0, sizeof(struct il_scale_tbl_info)); + *rate_idx = il4965_hwrate_to_plcp_idx(rate_n_flags); + + if (*rate_idx == RATE_INVALID) { + *rate_idx = -1; + return -EINVAL; + } + tbl->is_SGI = 0; /* default legacy setup */ + tbl->is_ht40 = 0; + tbl->is_dup = 0; + tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); + tbl->lq_type = LQ_NONE; + tbl->max_search = IL_MAX_SEARCH; + + /* legacy rate format */ + if (!(rate_n_flags & RATE_MCS_HT_MSK)) { + if (il4965_num_of_ant == 1) { + if (band == IEEE80211_BAND_5GHZ) + tbl->lq_type = LQ_A; + else + tbl->lq_type = LQ_G; + } + /* HT rate format */ + } else { + if (rate_n_flags & RATE_MCS_SGI_MSK) + tbl->is_SGI = 1; + + if ((rate_n_flags & RATE_MCS_HT40_MSK) || + (rate_n_flags & RATE_MCS_DUP_MSK)) + tbl->is_ht40 = 1; + + if (rate_n_flags & RATE_MCS_DUP_MSK) + tbl->is_dup = 1; + + mcs = il4965_rs_extract_rate(rate_n_flags); + + /* SISO */ + if (mcs <= RATE_SISO_60M_PLCP) { + if (il4965_num_of_ant == 1) + tbl->lq_type = LQ_SISO; /*else NONE */ + /* MIMO2 */ + } else { + if (il4965_num_of_ant == 2) + tbl->lq_type = LQ_MIMO2; + } + } + return 0; +} + +/* switch to another antenna/antennas and return 1 */ +/* if no other valid antenna found, return 0 */ +static int +il4965_rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, + struct il_scale_tbl_info *tbl) +{ + u8 new_ant_type; + + if (!tbl->ant_type || tbl->ant_type > ANT_ABC) + return 0; + + if (!il4965_rs_is_valid_ant(valid_ant, tbl->ant_type)) + return 0; + + new_ant_type = ant_toggle_lookup[tbl->ant_type]; + + while (new_ant_type != tbl->ant_type && + !il4965_rs_is_valid_ant(valid_ant, new_ant_type)) + new_ant_type = ant_toggle_lookup[new_ant_type]; + + if (new_ant_type == tbl->ant_type) + return 0; + + tbl->ant_type = new_ant_type; + *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; + *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; + return 1; +} + +/** + * Green-field mode is valid if the station supports it and + * there are no non-GF stations present in the BSS. + */ +static bool +il4965_rs_use_green(struct il_priv *il, struct ieee80211_sta *sta) +{ + return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !il->ht.non_gf_sta_present; +} + +/** + * il4965_rs_get_supported_rates - get the available rates + * + * if management frame or broadcast frame only return + * basic available rates. + * + */ +static u16 +il4965_rs_get_supported_rates(struct il_lq_sta *lq_sta, + struct ieee80211_hdr *hdr, + enum il_table_type rate_type) +{ + if (is_legacy(rate_type)) { + return lq_sta->active_legacy_rate; + } else { + if (is_siso(rate_type)) + return lq_sta->active_siso_rate; + else + return lq_sta->active_mimo2_rate; + } +} + +static u16 +il4965_rs_get_adjacent_rate(struct il_priv *il, u8 idx, u16 rate_mask, + int rate_type) +{ + u8 high = RATE_INVALID; + u8 low = RATE_INVALID; + + /* 802.11A or ht walks to the next literal adjacent rate in + * the rate table */ + if (is_a_band(rate_type) || !is_legacy(rate_type)) { + int i; + u32 mask; + + /* Find the previous rate that is in the rate mask */ + i = idx - 1; + for (mask = (1 << i); i >= 0; i--, mask >>= 1) { + if (rate_mask & mask) { + low = i; + break; + } + } + + /* Find the next rate that is in the rate mask */ + i = idx + 1; + for (mask = (1 << i); i < RATE_COUNT; i++, mask <<= 1) { + if (rate_mask & mask) { + high = i; + break; + } + } + + return (high << 8) | low; + } + + low = idx; + while (low != RATE_INVALID) { + low = il_rates[low].prev_rs; + if (low == RATE_INVALID) + break; + if (rate_mask & (1 << low)) + break; + D_RATE("Skipping masked lower rate: %d\n", low); + } + + high = idx; + while (high != RATE_INVALID) { + high = il_rates[high].next_rs; + if (high == RATE_INVALID) + break; + if (rate_mask & (1 << high)) + break; + D_RATE("Skipping masked higher rate: %d\n", high); + } + + return (high << 8) | low; +} + +static u32 +il4965_rs_get_lower_rate(struct il_lq_sta *lq_sta, + struct il_scale_tbl_info *tbl, u8 scale_idx, + u8 ht_possible) +{ + s32 low; + u16 rate_mask; + u16 high_low; + u8 switch_to_legacy = 0; + u8 is_green = lq_sta->is_green; + struct il_priv *il = lq_sta->drv; + + /* check if we need to switch from HT to legacy rates. + * assumption is that mandatory rates (1Mbps or 6Mbps) + * are always supported (spec demand) */ + if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_idx)) { + switch_to_legacy = 1; + scale_idx = rs_ht_to_legacy[scale_idx]; + if (lq_sta->band == IEEE80211_BAND_5GHZ) + tbl->lq_type = LQ_A; + else + tbl->lq_type = LQ_G; + + if (il4965_num_of_ant(tbl->ant_type) > 1) + tbl->ant_type = + il4965_first_antenna(il->hw_params.valid_tx_ant); + + tbl->is_ht40 = 0; + tbl->is_SGI = 0; + tbl->max_search = IL_MAX_SEARCH; + } + + rate_mask = il4965_rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); + + /* Mask with station rate restriction */ + if (is_legacy(tbl->lq_type)) { + /* supp_rates has no CCK bits in A mode */ + if (lq_sta->band == IEEE80211_BAND_5GHZ) + rate_mask = + (u16) (rate_mask & + (lq_sta->supp_rates << IL_FIRST_OFDM_RATE)); + else + rate_mask = (u16) (rate_mask & lq_sta->supp_rates); + } + + /* If we switched from HT to legacy, check current rate */ + if (switch_to_legacy && (rate_mask & (1 << scale_idx))) { + low = scale_idx; + goto out; + } + + high_low = + il4965_rs_get_adjacent_rate(lq_sta->drv, scale_idx, rate_mask, + tbl->lq_type); + low = high_low & 0xff; + + if (low == RATE_INVALID) + low = scale_idx; + +out: + return il4965_rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); +} + +/* + * Simple function to compare two rate scale table types + */ +static bool +il4965_table_type_matches(struct il_scale_tbl_info *a, + struct il_scale_tbl_info *b) +{ + return (a->lq_type == b->lq_type && a->ant_type == b->ant_type && + a->is_SGI == b->is_SGI); +} + +/* + * mac80211 sends us Tx status + */ +static void +il4965_rs_tx_status(void *il_r, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *il_sta, + struct sk_buff *skb) +{ + int legacy_success; + int retries; + int rs_idx, mac_idx, i; + struct il_lq_sta *lq_sta = il_sta; + struct il_link_quality_cmd *table; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct il_priv *il = (struct il_priv *)il_r; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + enum mac80211_rate_control_flags mac_flags; + u32 tx_rate; + struct il_scale_tbl_info tbl_type; + struct il_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; + + D_RATE("get frame ack response, update rate scale win\n"); + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (!lq_sta) { + D_RATE("Station rate scaling not created yet.\n"); + return; + } else if (!lq_sta->drv) { + D_RATE("Rate scaling not initialized yet.\n"); + return; + } + + if (!ieee80211_is_data(hdr->frame_control) || + (info->flags & IEEE80211_TX_CTL_NO_ACK)) + return; + + /* This packet was aggregated but doesn't carry status info */ + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && + !(info->flags & IEEE80211_TX_STAT_AMPDU)) + return; + + /* + * Ignore this Tx frame response if its initial rate doesn't match + * that of latest Link Quality command. There may be stragglers + * from a previous Link Quality command, but we're no longer interested + * in those; they're either from the "active" mode while we're trying + * to check "search" mode, or a prior "search" mode after we've moved + * to a new "search" mode (which might become the new "active" mode). + */ + table = &lq_sta->lq; + tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); + il4965_rs_get_tbl_info_from_mcs(tx_rate, il->band, &tbl_type, &rs_idx); + if (il->band == IEEE80211_BAND_5GHZ) + rs_idx -= IL_FIRST_OFDM_RATE; + mac_flags = info->status.rates[0].flags; + mac_idx = info->status.rates[0].idx; + /* For HT packets, map MCS to PLCP */ + if (mac_flags & IEEE80211_TX_RC_MCS) { + mac_idx &= RATE_MCS_CODE_MSK; /* Remove # of streams */ + if (mac_idx >= (RATE_9M_IDX - IL_FIRST_OFDM_RATE)) + mac_idx++; + /* + * mac80211 HT idx is always zero-idxed; we need to move + * HT OFDM rates after CCK rates in 2.4 GHz band + */ + if (il->band == IEEE80211_BAND_2GHZ) + mac_idx += IL_FIRST_OFDM_RATE; + } + /* Here we actually compare this rate to the latest LQ command */ + if (mac_idx < 0 || + tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI) || + tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH) || + tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA) || + tbl_type.ant_type != info->status.antenna || + !!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS) + || !!(tx_rate & RATE_MCS_GF_MSK) != + !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD) || rs_idx != mac_idx) { + D_RATE("initial rate %d does not match %d (0x%x)\n", mac_idx, + rs_idx, tx_rate); + /* + * Since rates mis-match, the last LQ command may have failed. + * After IL_MISSED_RATE_MAX mis-matches, resync the uCode with + * ... driver. + */ + lq_sta->missed_rate_counter++; + if (lq_sta->missed_rate_counter > IL_MISSED_RATE_MAX) { + lq_sta->missed_rate_counter = 0; + il_send_lq_cmd(il, &lq_sta->lq, CMD_ASYNC, false); + } + /* Regardless, ignore this status info for outdated rate */ + return; + } else + /* Rate did match, so reset the missed_rate_counter */ + lq_sta->missed_rate_counter = 0; + + /* Figure out if rate scale algorithm is in active or search table */ + if (il4965_table_type_matches + (&tbl_type, &(lq_sta->lq_info[lq_sta->active_tbl]))) { + curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + } else + if (il4965_table_type_matches + (&tbl_type, &lq_sta->lq_info[1 - lq_sta->active_tbl])) { + curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + } else { + D_RATE("Neither active nor search matches tx rate\n"); + tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + D_RATE("active- lq:%x, ant:%x, SGI:%d\n", tmp_tbl->lq_type, + tmp_tbl->ant_type, tmp_tbl->is_SGI); + tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + D_RATE("search- lq:%x, ant:%x, SGI:%d\n", tmp_tbl->lq_type, + tmp_tbl->ant_type, tmp_tbl->is_SGI); + D_RATE("actual- lq:%x, ant:%x, SGI:%d\n", tbl_type.lq_type, + tbl_type.ant_type, tbl_type.is_SGI); + /* + * no matching table found, let's by-pass the data collection + * and continue to perform rate scale to find the rate table + */ + il4965_rs_stay_in_table(lq_sta, true); + goto done; + } + + /* + * Updating the frame history depends on whether packets were + * aggregated. + * + * For aggregation, all packets were transmitted at the same rate, the + * first idx into rate scale table. + */ + if (info->flags & IEEE80211_TX_STAT_AMPDU) { + tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); + il4965_rs_get_tbl_info_from_mcs(tx_rate, il->band, &tbl_type, + &rs_idx); + il4965_rs_collect_tx_data(curr_tbl, rs_idx, + info->status.ampdu_len, + info->status.ampdu_ack_len); + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->stay_in_tbl) { + lq_sta->total_success += info->status.ampdu_ack_len; + lq_sta->total_failed += + (info->status.ampdu_len - + info->status.ampdu_ack_len); + } + } else { + /* + * For legacy, update frame history with for each Tx retry. + */ + retries = info->status.rates[0].count - 1; + /* HW doesn't send more than 15 retries */ + retries = min(retries, 15); + + /* The last transmission may have been successful */ + legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); + /* Collect data for each rate used during failed TX attempts */ + for (i = 0; i <= retries; ++i) { + tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags); + il4965_rs_get_tbl_info_from_mcs(tx_rate, il->band, + &tbl_type, &rs_idx); + /* + * Only collect stats if retried rate is in the same RS + * table as active/search. + */ + if (il4965_table_type_matches(&tbl_type, curr_tbl)) + tmp_tbl = curr_tbl; + else if (il4965_table_type_matches + (&tbl_type, other_tbl)) + tmp_tbl = other_tbl; + else + continue; + il4965_rs_collect_tx_data(tmp_tbl, rs_idx, 1, + i < + retries ? 0 : legacy_success); + } + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->stay_in_tbl) { + lq_sta->total_success += legacy_success; + lq_sta->total_failed += retries + (1 - legacy_success); + } + } + /* The last TX rate is cached in lq_sta; it's set in if/else above */ + lq_sta->last_rate_n_flags = tx_rate; +done: + /* See if there's a better rate or modulation mode to try. */ + if (sta->supp_rates[sband->band]) + il4965_rs_rate_scale_perform(il, skb, sta, lq_sta); +} + +/* + * Begin a period of staying with a selected modulation mode. + * Set "stay_in_tbl" flag to prevent any mode switches. + * Set frame tx success limits according to legacy vs. high-throughput, + * and reset overall (spanning all rates) tx success history stats. + * These control how long we stay using same modulation mode before + * searching for a new mode. + */ +static void +il4965_rs_set_stay_in_table(struct il_priv *il, u8 is_legacy, + struct il_lq_sta *lq_sta) +{ + D_RATE("we are staying in the same table\n"); + lq_sta->stay_in_tbl = 1; /* only place this gets set */ + if (is_legacy) { + lq_sta->table_count_limit = IL_LEGACY_TBL_COUNT; + lq_sta->max_failure_limit = IL_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IL_LEGACY_SUCCESS_LIMIT; + } else { + lq_sta->table_count_limit = IL_NONE_LEGACY_TBL_COUNT; + lq_sta->max_failure_limit = IL_NONE_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IL_NONE_LEGACY_SUCCESS_LIMIT; + } + lq_sta->table_count = 0; + lq_sta->total_failed = 0; + lq_sta->total_success = 0; + lq_sta->flush_timer = jiffies; + lq_sta->action_counter = 0; +} + +/* + * Find correct throughput table for given mode of modulation + */ +static void +il4965_rs_set_expected_tpt_table(struct il_lq_sta *lq_sta, + struct il_scale_tbl_info *tbl) +{ + /* Used to choose among HT tables */ + s32(*ht_tbl_pointer)[RATE_COUNT]; + + /* Check for invalid LQ type */ + if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { + tbl->expected_tpt = expected_tpt_legacy; + return; + } + + /* Legacy rates have only one table */ + if (is_legacy(tbl->lq_type)) { + tbl->expected_tpt = expected_tpt_legacy; + return; + } + + /* Choose among many HT tables depending on number of streams + * (SISO/MIMO2), channel width (20/40), SGI, and aggregation + * status */ + if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_siso20MHz; + else if (is_siso(tbl->lq_type)) + ht_tbl_pointer = expected_tpt_siso40MHz; + else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_mimo2_20MHz; + else /* if (is_mimo2(tbl->lq_type)) <-- must be true */ + ht_tbl_pointer = expected_tpt_mimo2_40MHz; + + if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ + tbl->expected_tpt = ht_tbl_pointer[0]; + else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ + tbl->expected_tpt = ht_tbl_pointer[1]; + else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ + tbl->expected_tpt = ht_tbl_pointer[2]; + else /* AGG+SGI */ + tbl->expected_tpt = ht_tbl_pointer[3]; +} + +/* + * Find starting rate for new "search" high-throughput mode of modulation. + * Goal is to find lowest expected rate (under perfect conditions) that is + * above the current measured throughput of "active" mode, to give new mode + * a fair chance to prove itself without too many challenges. + * + * This gets called when transitioning to more aggressive modulation + * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive + * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need + * to decrease to match "active" throughput. When moving from MIMO to SISO, + * bit rate will typically need to increase, but not if performance was bad. + */ +static s32 +il4965_rs_get_best_rate(struct il_priv *il, struct il_lq_sta *lq_sta, + struct il_scale_tbl_info *tbl, /* "search" */ + u16 rate_mask, s8 idx) +{ + /* "active" values */ + struct il_scale_tbl_info *active_tbl = + &(lq_sta->lq_info[lq_sta->active_tbl]); + s32 active_sr = active_tbl->win[idx].success_ratio; + s32 active_tpt = active_tbl->expected_tpt[idx]; + + /* expected "search" throughput */ + s32 *tpt_tbl = tbl->expected_tpt; + + s32 new_rate, high, low, start_hi; + u16 high_low; + s8 rate = idx; + + new_rate = high = low = start_hi = RATE_INVALID; + + for (;;) { + high_low = + il4965_rs_get_adjacent_rate(il, rate, rate_mask, + tbl->lq_type); + + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + /* + * Lower the "search" bit rate, to give new "search" mode + * approximately the same throughput as "active" if: + * + * 1) "Active" mode has been working modestly well (but not + * great), and expected "search" throughput (under perfect + * conditions) at candidate rate is above the actual + * measured "active" throughput (but less than expected + * "active" throughput under perfect conditions). + * OR + * 2) "Active" mode has been working perfectly or very well + * and expected "search" throughput (under perfect + * conditions) at candidate rate is above expected + * "active" throughput (under perfect conditions). + */ + if ((100 * tpt_tbl[rate] > lq_sta->last_tpt && + (active_sr > RATE_DECREASE_TH && active_sr <= RATE_HIGH_TH + && tpt_tbl[rate] <= active_tpt)) || + (active_sr >= RATE_SCALE_SWITCH && + tpt_tbl[rate] > active_tpt)) { + + /* (2nd or later pass) + * If we've already tried to raise the rate, and are + * now trying to lower it, use the higher rate. */ + if (start_hi != RATE_INVALID) { + new_rate = start_hi; + break; + } + + new_rate = rate; + + /* Loop again with lower rate */ + if (low != RATE_INVALID) + rate = low; + + /* Lower rate not available, use the original */ + else + break; + + /* Else try to raise the "search" rate to match "active" */ + } else { + /* (2nd or later pass) + * If we've already tried to lower the rate, and are + * now trying to raise it, use the lower rate. */ + if (new_rate != RATE_INVALID) + break; + + /* Loop again with higher rate */ + else if (high != RATE_INVALID) { + start_hi = high; + rate = high; + + /* Higher rate not available, use the original */ + } else { + new_rate = rate; + break; + } + } + } + + return new_rate; +} + +/* + * Set up search table for MIMO2 + */ +static int +il4965_rs_switch_to_mimo2(struct il_priv *il, struct il_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + struct il_scale_tbl_info *tbl, int idx) +{ + u16 rate_mask; + s32 rate; + s8 is_green = lq_sta->is_green; + + if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) + return -1; + + if (sta->smps_mode == IEEE80211_SMPS_STATIC) + return -1; + + /* Need both Tx chains/antennas to support MIMO */ + if (il->hw_params.tx_chains_num < 2) + return -1; + + D_RATE("LQ: try to switch to MIMO2\n"); + + tbl->lq_type = LQ_MIMO2; + tbl->is_dup = lq_sta->is_dup; + tbl->action = 0; + tbl->max_search = IL_MAX_SEARCH; + rate_mask = lq_sta->active_mimo2_rate; + + if (il_is_ht40_tx_allowed(il, &sta->ht_cap)) + tbl->is_ht40 = 1; + else + tbl->is_ht40 = 0; + + il4965_rs_set_expected_tpt_table(lq_sta, tbl); + + rate = il4965_rs_get_best_rate(il, lq_sta, tbl, rate_mask, idx); + + D_RATE("LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask); + if (rate == RATE_INVALID || !((1 << rate) & rate_mask)) { + D_RATE("Can't switch with idx %d rate mask %x\n", rate, + rate_mask); + return -1; + } + tbl->current_rate = + il4965_rate_n_flags_from_tbl(il, tbl, rate, is_green); + + D_RATE("LQ: Switch to new mcs %X idx is green %X\n", tbl->current_rate, + is_green); + return 0; +} + +/* + * Set up search table for SISO + */ +static int +il4965_rs_switch_to_siso(struct il_priv *il, struct il_lq_sta *lq_sta, + struct ieee80211_conf *conf, struct ieee80211_sta *sta, + struct il_scale_tbl_info *tbl, int idx) +{ + u16 rate_mask; + u8 is_green = lq_sta->is_green; + s32 rate; + + if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) + return -1; + + D_RATE("LQ: try to switch to SISO\n"); + + tbl->is_dup = lq_sta->is_dup; + tbl->lq_type = LQ_SISO; + tbl->action = 0; + tbl->max_search = IL_MAX_SEARCH; + rate_mask = lq_sta->active_siso_rate; + + if (il_is_ht40_tx_allowed(il, &sta->ht_cap)) + tbl->is_ht40 = 1; + else + tbl->is_ht40 = 0; + + if (is_green) + tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield */ + + il4965_rs_set_expected_tpt_table(lq_sta, tbl); + rate = il4965_rs_get_best_rate(il, lq_sta, tbl, rate_mask, idx); + + D_RATE("LQ: get best rate %d mask %X\n", rate, rate_mask); + if (rate == RATE_INVALID || !((1 << rate) & rate_mask)) { + D_RATE("can not switch with idx %d rate mask %x\n", rate, + rate_mask); + return -1; + } + tbl->current_rate = + il4965_rate_n_flags_from_tbl(il, tbl, rate, is_green); + D_RATE("LQ: Switch to new mcs %X idx is green %X\n", tbl->current_rate, + is_green); + return 0; +} + +/* + * Try to switch to new modulation mode from legacy + */ +static int +il4965_rs_move_legacy_other(struct il_priv *il, struct il_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, int idx) +{ + struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct il_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct il_rate_scale_data *win = &(tbl->win[idx]); + u32 sz = + (sizeof(struct il_scale_tbl_info) - + (sizeof(struct il_rate_scale_data) * RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = il->hw_params.valid_tx_ant; + u8 tx_chains_num = il->hw_params.tx_chains_num; + int ret = 0; + u8 update_search_tbl_counter = 0; + + tbl->action = IL_LEGACY_SWITCH_SISO; + + start_action = tbl->action; + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IL_LEGACY_SWITCH_ANTENNA1: + case IL_LEGACY_SWITCH_ANTENNA2: + D_RATE("LQ: Legacy toggle Antenna\n"); + + if ((tbl->action == IL_LEGACY_SWITCH_ANTENNA1 && + tx_chains_num <= 1) || + (tbl->action == IL_LEGACY_SWITCH_ANTENNA2 && + tx_chains_num <= 2)) + break; + + /* Don't change antenna if success has been great */ + if (win->success_ratio >= IL_RS_GOOD_RATIO) + break; + + /* Set up search table to try other antenna */ + memcpy(search_tbl, tbl, sz); + + if (il4965_rs_toggle_antenna + (valid_tx_ant, &search_tbl->current_rate, + search_tbl)) { + update_search_tbl_counter = 1; + il4965_rs_set_expected_tpt_table(lq_sta, + search_tbl); + goto out; + } + break; + case IL_LEGACY_SWITCH_SISO: + D_RATE("LQ: Legacy switch to SISO\n"); + + /* Set up search table to try SISO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + ret = + il4965_rs_switch_to_siso(il, lq_sta, conf, sta, + search_tbl, idx); + if (!ret) { + lq_sta->action_counter = 0; + goto out; + } + + break; + case IL_LEGACY_SWITCH_MIMO2_AB: + case IL_LEGACY_SWITCH_MIMO2_AC: + case IL_LEGACY_SWITCH_MIMO2_BC: + D_RATE("LQ: Legacy switch to MIMO2\n"); + + /* Set up search table to try MIMO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + + if (tbl->action == IL_LEGACY_SWITCH_MIMO2_AB) + search_tbl->ant_type = ANT_AB; + else if (tbl->action == IL_LEGACY_SWITCH_MIMO2_AC) + search_tbl->ant_type = ANT_AC; + else + search_tbl->ant_type = ANT_BC; + + if (!il4965_rs_is_valid_ant + (valid_tx_ant, search_tbl->ant_type)) + break; + + ret = + il4965_rs_switch_to_mimo2(il, lq_sta, conf, sta, + search_tbl, idx); + if (!ret) { + lq_sta->action_counter = 0; + goto out; + } + break; + } + tbl->action++; + if (tbl->action > IL_LEGACY_SWITCH_MIMO2_BC) + tbl->action = IL_LEGACY_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + + } + search_tbl->lq_type = LQ_NONE; + return 0; + +out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IL_LEGACY_SWITCH_MIMO2_BC) + tbl->action = IL_LEGACY_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; + return 0; + +} + +/* + * Try to switch to new modulation mode from SISO + */ +static int +il4965_rs_move_siso_to_other(struct il_priv *il, struct il_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, int idx) +{ + u8 is_green = lq_sta->is_green; + struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct il_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct il_rate_scale_data *win = &(tbl->win[idx]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + u32 sz = + (sizeof(struct il_scale_tbl_info) - + (sizeof(struct il_rate_scale_data) * RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = il->hw_params.valid_tx_ant; + u8 tx_chains_num = il->hw_params.tx_chains_num; + u8 update_search_tbl_counter = 0; + int ret; + + start_action = tbl->action; + + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IL_SISO_SWITCH_ANTENNA1: + case IL_SISO_SWITCH_ANTENNA2: + D_RATE("LQ: SISO toggle Antenna\n"); + if ((tbl->action == IL_SISO_SWITCH_ANTENNA1 && + tx_chains_num <= 1) || + (tbl->action == IL_SISO_SWITCH_ANTENNA2 && + tx_chains_num <= 2)) + break; + + if (win->success_ratio >= IL_RS_GOOD_RATIO) + break; + + memcpy(search_tbl, tbl, sz); + if (il4965_rs_toggle_antenna + (valid_tx_ant, &search_tbl->current_rate, + search_tbl)) { + update_search_tbl_counter = 1; + goto out; + } + break; + case IL_SISO_SWITCH_MIMO2_AB: + case IL_SISO_SWITCH_MIMO2_AC: + case IL_SISO_SWITCH_MIMO2_BC: + D_RATE("LQ: SISO switch to MIMO2\n"); + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + + if (tbl->action == IL_SISO_SWITCH_MIMO2_AB) + search_tbl->ant_type = ANT_AB; + else if (tbl->action == IL_SISO_SWITCH_MIMO2_AC) + search_tbl->ant_type = ANT_AC; + else + search_tbl->ant_type = ANT_BC; + + if (!il4965_rs_is_valid_ant + (valid_tx_ant, search_tbl->ant_type)) + break; + + ret = + il4965_rs_switch_to_mimo2(il, lq_sta, conf, sta, + search_tbl, idx); + if (!ret) + goto out; + break; + case IL_SISO_SWITCH_GI: + if (!tbl->is_ht40 && + !(ht_cap->cap & IEEE80211_HT_CAP_SGI_20)) + break; + if (tbl->is_ht40 && + !(ht_cap->cap & IEEE80211_HT_CAP_SGI_40)) + break; + + D_RATE("LQ: SISO toggle SGI/NGI\n"); + + memcpy(search_tbl, tbl, sz); + if (is_green) { + if (!tbl->is_SGI) + break; + else + IL_ERR("SGI was set in GF+SISO\n"); + } + search_tbl->is_SGI = !tbl->is_SGI; + il4965_rs_set_expected_tpt_table(lq_sta, search_tbl); + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[idx]) + break; + } + search_tbl->current_rate = + il4965_rate_n_flags_from_tbl(il, search_tbl, idx, + is_green); + update_search_tbl_counter = 1; + goto out; + } + tbl->action++; + if (tbl->action > IL_SISO_SWITCH_GI) + tbl->action = IL_SISO_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + } + search_tbl->lq_type = LQ_NONE; + return 0; + +out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IL_SISO_SWITCH_GI) + tbl->action = IL_SISO_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; + + return 0; +} + +/* + * Try to switch to new modulation mode from MIMO2 + */ +static int +il4965_rs_move_mimo2_to_other(struct il_priv *il, struct il_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, int idx) +{ + s8 is_green = lq_sta->is_green; + struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct il_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct il_rate_scale_data *win = &(tbl->win[idx]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + u32 sz = + (sizeof(struct il_scale_tbl_info) - + (sizeof(struct il_rate_scale_data) * RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = il->hw_params.valid_tx_ant; + u8 tx_chains_num = il->hw_params.tx_chains_num; + u8 update_search_tbl_counter = 0; + int ret; + + start_action = tbl->action; + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IL_MIMO2_SWITCH_ANTENNA1: + case IL_MIMO2_SWITCH_ANTENNA2: + D_RATE("LQ: MIMO2 toggle Antennas\n"); + + if (tx_chains_num <= 2) + break; + + if (win->success_ratio >= IL_RS_GOOD_RATIO) + break; + + memcpy(search_tbl, tbl, sz); + if (il4965_rs_toggle_antenna + (valid_tx_ant, &search_tbl->current_rate, + search_tbl)) { + update_search_tbl_counter = 1; + goto out; + } + break; + case IL_MIMO2_SWITCH_SISO_A: + case IL_MIMO2_SWITCH_SISO_B: + case IL_MIMO2_SWITCH_SISO_C: + D_RATE("LQ: MIMO2 switch to SISO\n"); + + /* Set up new search table for SISO */ + memcpy(search_tbl, tbl, sz); + + if (tbl->action == IL_MIMO2_SWITCH_SISO_A) + search_tbl->ant_type = ANT_A; + else if (tbl->action == IL_MIMO2_SWITCH_SISO_B) + search_tbl->ant_type = ANT_B; + else + search_tbl->ant_type = ANT_C; + + if (!il4965_rs_is_valid_ant + (valid_tx_ant, search_tbl->ant_type)) + break; + + ret = + il4965_rs_switch_to_siso(il, lq_sta, conf, sta, + search_tbl, idx); + if (!ret) + goto out; + + break; + + case IL_MIMO2_SWITCH_GI: + if (!tbl->is_ht40 && + !(ht_cap->cap & IEEE80211_HT_CAP_SGI_20)) + break; + if (tbl->is_ht40 && + !(ht_cap->cap & IEEE80211_HT_CAP_SGI_40)) + break; + + D_RATE("LQ: MIMO2 toggle SGI/NGI\n"); + + /* Set up new search table for MIMO2 */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = !tbl->is_SGI; + il4965_rs_set_expected_tpt_table(lq_sta, search_tbl); + /* + * If active table already uses the fastest possible + * modulation (dual stream with short guard interval), + * and it's working well, there's no need to look + * for a better type of modulation! + */ + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[idx]) + break; + } + search_tbl->current_rate = + il4965_rate_n_flags_from_tbl(il, search_tbl, idx, + is_green); + update_search_tbl_counter = 1; + goto out; + + } + tbl->action++; + if (tbl->action > IL_MIMO2_SWITCH_GI) + tbl->action = IL_MIMO2_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + } + search_tbl->lq_type = LQ_NONE; + return 0; +out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IL_MIMO2_SWITCH_GI) + tbl->action = IL_MIMO2_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; + + return 0; + +} + +/* + * Check whether we should continue using same modulation mode, or + * begin search for a new mode, based on: + * 1) # tx successes or failures while using this mode + * 2) # times calling this function + * 3) elapsed time in this mode (not used, for now) + */ +static void +il4965_rs_stay_in_table(struct il_lq_sta *lq_sta, bool force_search) +{ + struct il_scale_tbl_info *tbl; + int i; + int active_tbl; + int flush_interval_passed = 0; + struct il_priv *il; + + il = lq_sta->drv; + active_tbl = lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + + /* If we've been disallowing search, see if we should now allow it */ + if (lq_sta->stay_in_tbl) { + + /* Elapsed time using current modulation mode */ + if (lq_sta->flush_timer) + flush_interval_passed = + time_after(jiffies, + (unsigned long)(lq_sta->flush_timer + + RATE_SCALE_FLUSH_INTVL)); + + /* + * Check if we should allow search for new modulation mode. + * If many frames have failed or succeeded, or we've used + * this same modulation for a long time, allow search, and + * reset history stats that keep track of whether we should + * allow a new search. Also (below) reset all bitmaps and + * stats in active history. + */ + if (force_search || + lq_sta->total_failed > lq_sta->max_failure_limit || + lq_sta->total_success > lq_sta->max_success_limit || + (!lq_sta->search_better_tbl && lq_sta->flush_timer && + flush_interval_passed)) { + D_RATE("LQ: stay is expired %d %d %d\n", + lq_sta->total_failed, lq_sta->total_success, + flush_interval_passed); + + /* Allow search for new mode */ + lq_sta->stay_in_tbl = 0; /* only place reset */ + lq_sta->total_failed = 0; + lq_sta->total_success = 0; + lq_sta->flush_timer = 0; + + /* + * Else if we've used this modulation mode enough repetitions + * (regardless of elapsed time or success/failure), reset + * history bitmaps and rate-specific stats for all rates in + * active table. + */ + } else { + lq_sta->table_count++; + if (lq_sta->table_count >= lq_sta->table_count_limit) { + lq_sta->table_count = 0; + + D_RATE("LQ: stay in table clear win\n"); + for (i = 0; i < RATE_COUNT; i++) + il4965_rs_rate_scale_clear_win(& + (tbl-> + win + [i])); + } + } + + /* If transitioning to allow "search", reset all history + * bitmaps and stats in active table (this will become the new + * "search" table). */ + if (!lq_sta->stay_in_tbl) { + for (i = 0; i < RATE_COUNT; i++) + il4965_rs_rate_scale_clear_win(&(tbl->win[i])); + } + } +} + +/* + * setup rate table in uCode + */ +static void +il4965_rs_update_rate_tbl(struct il_priv *il, struct il_lq_sta *lq_sta, + struct il_scale_tbl_info *tbl, int idx, u8 is_green) +{ + u32 rate; + + /* Update uCode's rate table. */ + rate = il4965_rate_n_flags_from_tbl(il, tbl, idx, is_green); + il4965_rs_fill_link_cmd(il, lq_sta, rate); + il_send_lq_cmd(il, &lq_sta->lq, CMD_ASYNC, false); +} + +/* + * Do rate scaling and search for new modulation mode. + */ +static void +il4965_rs_rate_scale_perform(struct il_priv *il, struct sk_buff *skb, + struct ieee80211_sta *sta, + struct il_lq_sta *lq_sta) +{ + struct ieee80211_hw *hw = il->hw; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + int low = RATE_INVALID; + int high = RATE_INVALID; + int idx; + int i; + struct il_rate_scale_data *win = NULL; + int current_tpt = IL_INVALID_VALUE; + int low_tpt = IL_INVALID_VALUE; + int high_tpt = IL_INVALID_VALUE; + u32 fail_count; + s8 scale_action = 0; + u16 rate_mask; + u8 update_lq = 0; + struct il_scale_tbl_info *tbl, *tbl1; + u16 rate_scale_idx_msk = 0; + u8 is_green = 0; + u8 active_tbl = 0; + u8 done_search = 0; + u16 high_low; + s32 sr; + u8 tid = MAX_TID_COUNT; + struct il_tid_data *tid_data; + + D_RATE("rate scale calculate new rate for skb\n"); + + /* Send management frames and NO_ACK data using lowest rate. */ + /* TODO: this could probably be improved.. */ + if (!ieee80211_is_data(hdr->frame_control) || + (info->flags & IEEE80211_TX_CTL_NO_ACK)) + return; + + lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; + + tid = il4965_rs_tl_add_packet(lq_sta, hdr); + if (tid != MAX_TID_COUNT && (lq_sta->tx_agg_tid_en & (1 << tid))) { + tid_data = &il->stations[lq_sta->lq.sta_id].tid[tid]; + if (tid_data->agg.state == IL_AGG_OFF) + lq_sta->is_agg = 0; + else + lq_sta->is_agg = 1; + } else + lq_sta->is_agg = 0; + + /* + * Select rate-scale / modulation-mode table to work with in + * the rest of this function: "search" if searching for better + * modulation mode, or "active" if doing rate scaling within a mode. + */ + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + if (is_legacy(tbl->lq_type)) + lq_sta->is_green = 0; + else + lq_sta->is_green = il4965_rs_use_green(il, sta); + is_green = lq_sta->is_green; + + /* current tx rate */ + idx = lq_sta->last_txrate_idx; + + D_RATE("Rate scale idx %d for type %d\n", idx, tbl->lq_type); + + /* rates available for this association, and for modulation mode */ + rate_mask = il4965_rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); + + D_RATE("mask 0x%04X\n", rate_mask); + + /* mask with station rate restriction */ + if (is_legacy(tbl->lq_type)) { + if (lq_sta->band == IEEE80211_BAND_5GHZ) + /* supp_rates has no CCK bits in A mode */ + rate_scale_idx_msk = + (u16) (rate_mask & + (lq_sta->supp_rates << IL_FIRST_OFDM_RATE)); + else + rate_scale_idx_msk = + (u16) (rate_mask & lq_sta->supp_rates); + + } else + rate_scale_idx_msk = rate_mask; + + if (!rate_scale_idx_msk) + rate_scale_idx_msk = rate_mask; + + if (!((1 << idx) & rate_scale_idx_msk)) { + IL_ERR("Current Rate is not valid\n"); + if (lq_sta->search_better_tbl) { + /* revert to active table if search table is not valid */ + tbl->lq_type = LQ_NONE; + lq_sta->search_better_tbl = 0; + tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + /* get "active" rate info */ + idx = il4965_hwrate_to_plcp_idx(tbl->current_rate); + il4965_rs_update_rate_tbl(il, lq_sta, tbl, idx, + is_green); + } + return; + } + + /* Get expected throughput table and history win for current rate */ + if (!tbl->expected_tpt) { + IL_ERR("tbl->expected_tpt is NULL\n"); + return; + } + + /* force user max rate if set by user */ + if (lq_sta->max_rate_idx != -1 && lq_sta->max_rate_idx < idx) { + idx = lq_sta->max_rate_idx; + update_lq = 1; + win = &(tbl->win[idx]); + goto lq_update; + } + + win = &(tbl->win[idx]); + + /* + * If there is not enough history to calculate actual average + * throughput, keep analyzing results of more tx frames, without + * changing rate or mode (bypass most of the rest of this function). + * Set up new rate table in uCode only if old rate is not supported + * in current association (use new rate found above). + */ + fail_count = win->counter - win->success_counter; + if (fail_count < RATE_MIN_FAILURE_TH && + win->success_counter < RATE_MIN_SUCCESS_TH) { + D_RATE("LQ: still below TH. succ=%d total=%d " "for idx %d\n", + win->success_counter, win->counter, idx); + + /* Can't calculate this yet; not enough history */ + win->average_tpt = IL_INVALID_VALUE; + + /* Should we stay with this modulation mode, + * or search for a new one? */ + il4965_rs_stay_in_table(lq_sta, false); + + goto out; + } + /* Else we have enough samples; calculate estimate of + * actual average throughput */ + if (win->average_tpt != + ((win->success_ratio * tbl->expected_tpt[idx] + 64) / 128)) { + IL_ERR("expected_tpt should have been calculated by now\n"); + win->average_tpt = + ((win->success_ratio * tbl->expected_tpt[idx] + 64) / 128); + } + + /* If we are searching for better modulation mode, check success. */ + if (lq_sta->search_better_tbl) { + /* If good success, continue using the "search" mode; + * no need to send new link quality command, since we're + * continuing to use the setup that we've been trying. */ + if (win->average_tpt > lq_sta->last_tpt) { + + D_RATE("LQ: SWITCHING TO NEW TBL " + "suc=%d cur-tpt=%d old-tpt=%d\n", + win->success_ratio, win->average_tpt, + lq_sta->last_tpt); + + if (!is_legacy(tbl->lq_type)) + lq_sta->enable_counter = 1; + + /* Swap tables; "search" becomes "active" */ + lq_sta->active_tbl = active_tbl; + current_tpt = win->average_tpt; + + /* Else poor success; go back to mode in "active" table */ + } else { + + D_RATE("LQ: GOING BACK TO THE OLD TBL " + "suc=%d cur-tpt=%d old-tpt=%d\n", + win->success_ratio, win->average_tpt, + lq_sta->last_tpt); + + /* Nullify "search" table */ + tbl->lq_type = LQ_NONE; + + /* Revert to "active" table */ + active_tbl = lq_sta->active_tbl; + tbl = &(lq_sta->lq_info[active_tbl]); + + /* Revert to "active" rate and throughput info */ + idx = il4965_hwrate_to_plcp_idx(tbl->current_rate); + current_tpt = lq_sta->last_tpt; + + /* Need to set up a new rate table in uCode */ + update_lq = 1; + } + + /* Either way, we've made a decision; modulation mode + * search is done, allow rate adjustment next time. */ + lq_sta->search_better_tbl = 0; + done_search = 1; /* Don't switch modes below! */ + goto lq_update; + } + + /* (Else) not in search of better modulation mode, try for better + * starting rate, while staying in this mode. */ + high_low = + il4965_rs_get_adjacent_rate(il, idx, rate_scale_idx_msk, + tbl->lq_type); + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + /* If user set max rate, dont allow higher than user constrain */ + if (lq_sta->max_rate_idx != -1 && lq_sta->max_rate_idx < high) + high = RATE_INVALID; + + sr = win->success_ratio; + + /* Collect measured throughputs for current and adjacent rates */ + current_tpt = win->average_tpt; + if (low != RATE_INVALID) + low_tpt = tbl->win[low].average_tpt; + if (high != RATE_INVALID) + high_tpt = tbl->win[high].average_tpt; + + scale_action = 0; + + /* Too many failures, decrease rate */ + if (sr <= RATE_DECREASE_TH || current_tpt == 0) { + D_RATE("decrease rate because of low success_ratio\n"); + scale_action = -1; + + /* No throughput measured yet for adjacent rates; try increase. */ + } else if (low_tpt == IL_INVALID_VALUE && high_tpt == IL_INVALID_VALUE) { + + if (high != RATE_INVALID && sr >= RATE_INCREASE_TH) + scale_action = 1; + else if (low != RATE_INVALID) + scale_action = 0; + } + + /* Both adjacent throughputs are measured, but neither one has better + * throughput; we're using the best rate, don't change it! */ + else if (low_tpt != IL_INVALID_VALUE && high_tpt != IL_INVALID_VALUE && + low_tpt < current_tpt && high_tpt < current_tpt) + scale_action = 0; + + /* At least one adjacent rate's throughput is measured, + * and may have better performance. */ + else { + /* Higher adjacent rate's throughput is measured */ + if (high_tpt != IL_INVALID_VALUE) { + /* Higher rate has better throughput */ + if (high_tpt > current_tpt && sr >= RATE_INCREASE_TH) + scale_action = 1; + else + scale_action = 0; + + /* Lower adjacent rate's throughput is measured */ + } else if (low_tpt != IL_INVALID_VALUE) { + /* Lower rate has better throughput */ + if (low_tpt > current_tpt) { + D_RATE("decrease rate because of low tpt\n"); + scale_action = -1; + } else if (sr >= RATE_INCREASE_TH) { + scale_action = 1; + } + } + } + + /* Sanity check; asked for decrease, but success rate or throughput + * has been good at old rate. Don't change it. */ + if (scale_action == -1 && low != RATE_INVALID && + (sr > RATE_HIGH_TH || current_tpt > 100 * tbl->expected_tpt[low])) + scale_action = 0; + + switch (scale_action) { + case -1: + /* Decrease starting rate, update uCode's rate table */ + if (low != RATE_INVALID) { + update_lq = 1; + idx = low; + } + + break; + case 1: + /* Increase starting rate, update uCode's rate table */ + if (high != RATE_INVALID) { + update_lq = 1; + idx = high; + } + + break; + case 0: + /* No change */ + default: + break; + } + + D_RATE("choose rate scale idx %d action %d low %d " "high %d type %d\n", + idx, scale_action, low, high, tbl->lq_type); + +lq_update: + /* Replace uCode's rate table for the destination station. */ + if (update_lq) + il4965_rs_update_rate_tbl(il, lq_sta, tbl, idx, is_green); + + /* Should we stay with this modulation mode, + * or search for a new one? */ + il4965_rs_stay_in_table(lq_sta, false); + + /* + * Search for new modulation mode if we're: + * 1) Not changing rates right now + * 2) Not just finishing up a search + * 3) Allowing a new search + */ + if (!update_lq && !done_search && !lq_sta->stay_in_tbl && win->counter) { + /* Save current throughput to compare with "search" throughput */ + lq_sta->last_tpt = current_tpt; + + /* Select a new "search" modulation mode to try. + * If one is found, set up the new "search" table. */ + if (is_legacy(tbl->lq_type)) + il4965_rs_move_legacy_other(il, lq_sta, conf, sta, idx); + else if (is_siso(tbl->lq_type)) + il4965_rs_move_siso_to_other(il, lq_sta, conf, sta, + idx); + else /* (is_mimo2(tbl->lq_type)) */ + il4965_rs_move_mimo2_to_other(il, lq_sta, conf, sta, + idx); + + /* If new "search" mode was selected, set up in uCode table */ + if (lq_sta->search_better_tbl) { + /* Access the "search" table, clear its history. */ + tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + for (i = 0; i < RATE_COUNT; i++) + il4965_rs_rate_scale_clear_win(&(tbl->win[i])); + + /* Use new "search" start rate */ + idx = il4965_hwrate_to_plcp_idx(tbl->current_rate); + + D_RATE("Switch current mcs: %X idx: %d\n", + tbl->current_rate, idx); + il4965_rs_fill_link_cmd(il, lq_sta, tbl->current_rate); + il_send_lq_cmd(il, &lq_sta->lq, CMD_ASYNC, false); + } else + done_search = 1; + } + + if (done_search && !lq_sta->stay_in_tbl) { + /* If the "active" (non-search) mode was legacy, + * and we've tried switching antennas, + * but we haven't been able to try HT modes (not available), + * stay with best antenna legacy modulation for a while + * before next round of mode comparisons. */ + tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); + if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) && + lq_sta->action_counter > tbl1->max_search) { + D_RATE("LQ: STAY in legacy table\n"); + il4965_rs_set_stay_in_table(il, 1, lq_sta); + } + + /* If we're in an HT mode, and all 3 mode switch actions + * have been tried and compared, stay in this best modulation + * mode for a while before next round of mode comparisons. */ + if (lq_sta->enable_counter && + lq_sta->action_counter >= tbl1->max_search) { + if (lq_sta->last_tpt > IL_AGG_TPT_THREHOLD && + (lq_sta->tx_agg_tid_en & (1 << tid)) && + tid != MAX_TID_COUNT) { + tid_data = + &il->stations[lq_sta->lq.sta_id].tid[tid]; + if (tid_data->agg.state == IL_AGG_OFF) { + D_RATE("try to aggregate tid %d\n", + tid); + il4965_rs_tl_turn_on_agg(il, tid, + lq_sta, sta); + } + } + il4965_rs_set_stay_in_table(il, 0, lq_sta); + } + } + +out: + tbl->current_rate = + il4965_rate_n_flags_from_tbl(il, tbl, idx, is_green); + i = idx; + lq_sta->last_txrate_idx = i; +} + +/** + * il4965_rs_initialize_lq - Initialize a station's hardware rate table + * + * The uCode's station table contains a table of fallback rates + * for automatic fallback during transmission. + * + * NOTE: This sets up a default set of values. These will be replaced later + * if the driver's iwl-4965-rs rate scaling algorithm is used, instead of + * rc80211_simple. + * + * NOTE: Run C_ADD_STA command to set up station table entry, before + * calling this function (which runs C_TX_LINK_QUALITY_CMD, + * which requires station table entry to exist). + */ +static void +il4965_rs_initialize_lq(struct il_priv *il, struct ieee80211_conf *conf, + struct ieee80211_sta *sta, struct il_lq_sta *lq_sta) +{ + struct il_scale_tbl_info *tbl; + int rate_idx; + int i; + u32 rate; + u8 use_green; + u8 active_tbl = 0; + u8 valid_tx_ant; + struct il_station_priv *sta_priv; + + if (!sta || !lq_sta) + return; + + use_green = il4965_rs_use_green(il, sta); + sta_priv = (void *)sta->drv_priv; + + i = lq_sta->last_txrate_idx; + + valid_tx_ant = il->hw_params.valid_tx_ant; + + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + + if (i < 0 || i >= RATE_COUNT) + i = 0; + + rate = il_rates[i].plcp; + tbl->ant_type = il4965_first_antenna(valid_tx_ant); + rate |= tbl->ant_type << RATE_MCS_ANT_POS; + + if (i >= IL_FIRST_CCK_RATE && i <= IL_LAST_CCK_RATE) + rate |= RATE_MCS_CCK_MSK; + + il4965_rs_get_tbl_info_from_mcs(rate, il->band, tbl, &rate_idx); + if (!il4965_rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) + il4965_rs_toggle_antenna(valid_tx_ant, &rate, tbl); + + rate = il4965_rate_n_flags_from_tbl(il, tbl, rate_idx, use_green); + tbl->current_rate = rate; + il4965_rs_set_expected_tpt_table(lq_sta, tbl); + il4965_rs_fill_link_cmd(NULL, lq_sta, rate); + il->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; + il_send_lq_cmd(il, &lq_sta->lq, CMD_SYNC, true); +} + +static void +il4965_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta, + struct ieee80211_tx_rate_control *txrc) +{ + + struct sk_buff *skb = txrc->skb; + struct ieee80211_supported_band *sband = txrc->sband; + struct il_priv *il __maybe_unused = (struct il_priv *)il_r; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct il_lq_sta *lq_sta = il_sta; + int rate_idx; + + D_RATE("rate scale calculate new rate for skb\n"); + + /* Get max rate if user set max rate */ + if (lq_sta) { + lq_sta->max_rate_idx = txrc->max_rate_idx; + if (sband->band == IEEE80211_BAND_5GHZ && + lq_sta->max_rate_idx != -1) + lq_sta->max_rate_idx += IL_FIRST_OFDM_RATE; + if (lq_sta->max_rate_idx < 0 || + lq_sta->max_rate_idx >= RATE_COUNT) + lq_sta->max_rate_idx = -1; + } + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (lq_sta && !lq_sta->drv) { + D_RATE("Rate scaling not initialized yet.\n"); + il_sta = NULL; + } + + /* Send management frames and NO_ACK data using lowest rate. */ + if (rate_control_send_low(sta, il_sta, txrc)) + return; + + if (!lq_sta) + return; + + rate_idx = lq_sta->last_txrate_idx; + + if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { + rate_idx -= IL_FIRST_OFDM_RATE; + /* 6M and 9M shared same MCS idx */ + rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; + if (il4965_rs_extract_rate(lq_sta->last_rate_n_flags) >= + RATE_MIMO2_6M_PLCP) + rate_idx = rate_idx + MCS_IDX_PER_STREAM; + info->control.rates[0].flags = IEEE80211_TX_RC_MCS; + if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) + info->control.rates[0].flags |= + IEEE80211_TX_RC_SHORT_GI; + if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) + info->control.rates[0].flags |= + IEEE80211_TX_RC_DUP_DATA; + if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) + info->control.rates[0].flags |= + IEEE80211_TX_RC_40_MHZ_WIDTH; + if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) + info->control.rates[0].flags |= + IEEE80211_TX_RC_GREEN_FIELD; + } else { + /* Check for invalid rates */ + if (rate_idx < 0 || rate_idx >= RATE_COUNT_LEGACY || + (sband->band == IEEE80211_BAND_5GHZ && + rate_idx < IL_FIRST_OFDM_RATE)) + rate_idx = rate_lowest_index(sband, sta); + /* On valid 5 GHz rate, adjust idx */ + else if (sband->band == IEEE80211_BAND_5GHZ) + rate_idx -= IL_FIRST_OFDM_RATE; + info->control.rates[0].flags = 0; + } + info->control.rates[0].idx = rate_idx; + info->control.rates[0].count = 1; +} + +static void * +il4965_rs_alloc_sta(void *il_rate, struct ieee80211_sta *sta, gfp_t gfp) +{ + struct il_station_priv *sta_priv = + (struct il_station_priv *)sta->drv_priv; + struct il_priv *il; + + il = (struct il_priv *)il_rate; + D_RATE("create station rate scale win\n"); + + return &sta_priv->lq_sta; +} + +/* + * Called after adding a new station to initialize rate scaling + */ +void +il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, u8 sta_id) +{ + int i, j; + struct ieee80211_hw *hw = il->hw; + struct ieee80211_conf *conf = &il->hw->conf; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct il_station_priv *sta_priv; + struct il_lq_sta *lq_sta; + struct ieee80211_supported_band *sband; + + sta_priv = (struct il_station_priv *)sta->drv_priv; + lq_sta = &sta_priv->lq_sta; + sband = hw->wiphy->bands[conf->chandef.chan->band]; + + lq_sta->lq.sta_id = sta_id; + + for (j = 0; j < LQ_SIZE; j++) + for (i = 0; i < RATE_COUNT; i++) + il4965_rs_rate_scale_clear_win(&lq_sta->lq_info[j]. + win[i]); + + lq_sta->flush_timer = 0; + lq_sta->supp_rates = sta->supp_rates[sband->band]; + for (j = 0; j < LQ_SIZE; j++) + for (i = 0; i < RATE_COUNT; i++) + il4965_rs_rate_scale_clear_win(&lq_sta->lq_info[j]. + win[i]); + + D_RATE("LQ:" "*** rate scale station global init for station %d ***\n", + sta_id); + /* TODO: what is a good starting rate for STA? About middle? Maybe not + * the lowest or the highest rate.. Could consider using RSSI from + * previous packets? Need to have IEEE 802.1X auth succeed immediately + * after assoc.. */ + + lq_sta->is_dup = 0; + lq_sta->max_rate_idx = -1; + lq_sta->missed_rate_counter = IL_MISSED_RATE_MAX; + lq_sta->is_green = il4965_rs_use_green(il, sta); + lq_sta->active_legacy_rate = il->active_rate & ~(0x1000); + lq_sta->band = il->band; + /* + * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), + * supp_rates[] does not; shift to convert format, force 9 MBits off. + */ + lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; + lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; + lq_sta->active_siso_rate &= ~((u16) 0x2); + lq_sta->active_siso_rate <<= IL_FIRST_OFDM_RATE; + + /* Same here */ + lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; + lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; + lq_sta->active_mimo2_rate &= ~((u16) 0x2); + lq_sta->active_mimo2_rate <<= IL_FIRST_OFDM_RATE; + + /* These values will be overridden later */ + lq_sta->lq.general_params.single_stream_ant_msk = + il4965_first_antenna(il->hw_params.valid_tx_ant); + lq_sta->lq.general_params.dual_stream_ant_msk = + il->hw_params.valid_tx_ant & ~il4965_first_antenna(il->hw_params. + valid_tx_ant); + if (!lq_sta->lq.general_params.dual_stream_ant_msk) { + lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; + } else if (il4965_num_of_ant(il->hw_params.valid_tx_ant) == 2) { + lq_sta->lq.general_params.dual_stream_ant_msk = + il->hw_params.valid_tx_ant; + } + + /* as default allow aggregation for all tids */ + lq_sta->tx_agg_tid_en = IL_AGG_ALL_TID; + lq_sta->drv = il; + + /* Set last_txrate_idx to lowest rate */ + lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); + if (sband->band == IEEE80211_BAND_5GHZ) + lq_sta->last_txrate_idx += IL_FIRST_OFDM_RATE; + lq_sta->is_agg = 0; + +#ifdef CONFIG_MAC80211_DEBUGFS + lq_sta->dbg_fixed_rate = 0; +#endif + + il4965_rs_initialize_lq(il, conf, sta, lq_sta); +} + +static void +il4965_rs_fill_link_cmd(struct il_priv *il, struct il_lq_sta *lq_sta, + u32 new_rate) +{ + struct il_scale_tbl_info tbl_type; + int idx = 0; + int rate_idx; + int repeat_rate = 0; + u8 ant_toggle_cnt = 0; + u8 use_ht_possible = 1; + u8 valid_tx_ant = 0; + struct il_link_quality_cmd *lq_cmd = &lq_sta->lq; + + /* Override starting rate (idx 0) if needed for debug purposes */ + il4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, idx); + + /* Interpret new_rate (rate_n_flags) */ + il4965_rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, + &rate_idx); + + /* How many times should we repeat the initial rate? */ + if (is_legacy(tbl_type.lq_type)) { + ant_toggle_cnt = 1; + repeat_rate = IL_NUMBER_TRY; + } else { + repeat_rate = IL_HT_NUMBER_TRY; + } + + lq_cmd->general_params.mimo_delimiter = + is_mimo(tbl_type.lq_type) ? 1 : 0; + + /* Fill 1st table entry (idx 0) */ + lq_cmd->rs_table[idx].rate_n_flags = cpu_to_le32(new_rate); + + if (il4965_num_of_ant(tbl_type.ant_type) == 1) { + lq_cmd->general_params.single_stream_ant_msk = + tbl_type.ant_type; + } else if (il4965_num_of_ant(tbl_type.ant_type) == 2) { + lq_cmd->general_params.dual_stream_ant_msk = tbl_type.ant_type; + } + /* otherwise we don't modify the existing value */ + idx++; + repeat_rate--; + if (il) + valid_tx_ant = il->hw_params.valid_tx_ant; + + /* Fill rest of rate table */ + while (idx < LINK_QUAL_MAX_RETRY_NUM) { + /* Repeat initial/next rate. + * For legacy IL_NUMBER_TRY == 1, this loop will not execute. + * For HT IL_HT_NUMBER_TRY == 3, this executes twice. */ + while (repeat_rate > 0 && idx < LINK_QUAL_MAX_RETRY_NUM) { + if (is_legacy(tbl_type.lq_type)) { + if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) + ant_toggle_cnt++; + else if (il && + il4965_rs_toggle_antenna(valid_tx_ant, + &new_rate, + &tbl_type)) + ant_toggle_cnt = 1; + } + + /* Override next rate if needed for debug purposes */ + il4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, idx); + + /* Fill next table entry */ + lq_cmd->rs_table[idx].rate_n_flags = + cpu_to_le32(new_rate); + repeat_rate--; + idx++; + } + + il4965_rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, + &tbl_type, &rate_idx); + + /* Indicate to uCode which entries might be MIMO. + * If initial rate was MIMO, this will finally end up + * as (IL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ + if (is_mimo(tbl_type.lq_type)) + lq_cmd->general_params.mimo_delimiter = idx; + + /* Get next rate */ + new_rate = + il4965_rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, + use_ht_possible); + + /* How many times should we repeat the next rate? */ + if (is_legacy(tbl_type.lq_type)) { + if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) + ant_toggle_cnt++; + else if (il && + il4965_rs_toggle_antenna(valid_tx_ant, + &new_rate, &tbl_type)) + ant_toggle_cnt = 1; + + repeat_rate = IL_NUMBER_TRY; + } else { + repeat_rate = IL_HT_NUMBER_TRY; + } + + /* Don't allow HT rates after next pass. + * il4965_rs_get_lower_rate() will change type to LQ_A or LQ_G. */ + use_ht_possible = 0; + + /* Override next rate if needed for debug purposes */ + il4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, idx); + + /* Fill next table entry */ + lq_cmd->rs_table[idx].rate_n_flags = cpu_to_le32(new_rate); + + idx++; + repeat_rate--; + } + + lq_cmd->agg_params.agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; + lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + + lq_cmd->agg_params.agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); +} + +static void * +il4965_rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) +{ + return hw->priv; +} + +/* rate scale requires free function to be implemented */ +static void +il4965_rs_free(void *il_rate) +{ + return; +} + +static void +il4965_rs_free_sta(void *il_r, struct ieee80211_sta *sta, void *il_sta) +{ + struct il_priv *il __maybe_unused = il_r; + + D_RATE("enter\n"); + D_RATE("leave\n"); +} + +#ifdef CONFIG_MAC80211_DEBUGFS + +static void +il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx) +{ + struct il_priv *il; + u8 valid_tx_ant; + u8 ant_sel_tx; + + il = lq_sta->drv; + valid_tx_ant = il->hw_params.valid_tx_ant; + if (lq_sta->dbg_fixed_rate) { + ant_sel_tx = + ((lq_sta-> + dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >> + RATE_MCS_ANT_POS); + if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { + *rate_n_flags = lq_sta->dbg_fixed_rate; + D_RATE("Fixed rate ON\n"); + } else { + lq_sta->dbg_fixed_rate = 0; + IL_ERR + ("Invalid antenna selection 0x%X, Valid is 0x%X\n", + ant_sel_tx, valid_tx_ant); + D_RATE("Fixed rate OFF\n"); + } + } else { + D_RATE("Fixed rate OFF\n"); + } +} + +static ssize_t +il4965_rs_sta_dbgfs_scale_table_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_lq_sta *lq_sta = file->private_data; + struct il_priv *il; + char buf[64]; + size_t buf_size; + u32 parsed_rate; + + il = lq_sta->drv; + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%x", &parsed_rate) == 1) + lq_sta->dbg_fixed_rate = parsed_rate; + else + lq_sta->dbg_fixed_rate = 0; + + lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ + lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + + D_RATE("sta_id %d rate 0x%X\n", lq_sta->lq.sta_id, + lq_sta->dbg_fixed_rate); + + if (lq_sta->dbg_fixed_rate) { + il4965_rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); + il_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false); + } + + return count; +} + +static ssize_t +il4965_rs_sta_dbgfs_scale_table_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *buff; + int desc = 0; + int i = 0; + int idx = 0; + ssize_t ret; + + struct il_lq_sta *lq_sta = file->private_data; + struct il_priv *il; + struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + + il = lq_sta->drv; + buff = kmalloc(1024, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + desc += sprintf(buff + desc, "sta_id %d\n", lq_sta->lq.sta_id); + desc += + sprintf(buff + desc, "failed=%d success=%d rate=0%X\n", + lq_sta->total_failed, lq_sta->total_success, + lq_sta->active_legacy_rate); + desc += + sprintf(buff + desc, "fixed rate 0x%X\n", lq_sta->dbg_fixed_rate); + desc += + sprintf(buff + desc, "valid_tx_ant %s%s%s\n", + (il->hw_params.valid_tx_ant & ANT_A) ? "ANT_A," : "", + (il->hw_params.valid_tx_ant & ANT_B) ? "ANT_B," : "", + (il->hw_params.valid_tx_ant & ANT_C) ? "ANT_C" : ""); + desc += + sprintf(buff + desc, "lq type %s\n", + (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); + if (is_Ht(tbl->lq_type)) { + desc += + sprintf(buff + desc, " %s", + (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2"); + desc += + sprintf(buff + desc, " %s", + (tbl->is_ht40) ? "40MHz" : "20MHz"); + desc += + sprintf(buff + desc, " %s %s %s\n", + (tbl->is_SGI) ? "SGI" : "", + (lq_sta->is_green) ? "GF enabled" : "", + (lq_sta->is_agg) ? "AGG on" : ""); + } + desc += + sprintf(buff + desc, "last tx rate=0x%X\n", + lq_sta->last_rate_n_flags); + desc += + sprintf(buff + desc, + "general:" "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", + lq_sta->lq.general_params.flags, + lq_sta->lq.general_params.mimo_delimiter, + lq_sta->lq.general_params.single_stream_ant_msk, + lq_sta->lq.general_params.dual_stream_ant_msk); + + desc += + sprintf(buff + desc, + "agg:" + "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", + le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), + lq_sta->lq.agg_params.agg_dis_start_th, + lq_sta->lq.agg_params.agg_frame_cnt_limit); + + desc += + sprintf(buff + desc, + "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", + lq_sta->lq.general_params.start_rate_idx[0], + lq_sta->lq.general_params.start_rate_idx[1], + lq_sta->lq.general_params.start_rate_idx[2], + lq_sta->lq.general_params.start_rate_idx[3]); + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + idx = + il4965_hwrate_to_plcp_idx(le32_to_cpu + (lq_sta->lq.rs_table[i]. + rate_n_flags)); + if (is_legacy(tbl->lq_type)) { + desc += + sprintf(buff + desc, " rate[%d] 0x%X %smbps\n", i, + le32_to_cpu(lq_sta->lq.rs_table[i]. + rate_n_flags), + il_rate_mcs[idx].mbps); + } else { + desc += + sprintf(buff + desc, " rate[%d] 0x%X %smbps (%s)\n", + i, + le32_to_cpu(lq_sta->lq.rs_table[i]. + rate_n_flags), + il_rate_mcs[idx].mbps, + il_rate_mcs[idx].mcs); + } + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_scale_table_ops = { + .write = il4965_rs_sta_dbgfs_scale_table_write, + .read = il4965_rs_sta_dbgfs_scale_table_read, + .open = simple_open, + .llseek = default_llseek, +}; + +static ssize_t +il4965_rs_sta_dbgfs_stats_table_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *buff; + int desc = 0; + int i, j; + ssize_t ret; + + struct il_lq_sta *lq_sta = file->private_data; + + buff = kmalloc(1024, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + for (i = 0; i < LQ_SIZE; i++) { + desc += + sprintf(buff + desc, + "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n" + "rate=0x%X\n", lq_sta->active_tbl == i ? "*" : "x", + lq_sta->lq_info[i].lq_type, + lq_sta->lq_info[i].is_SGI, + lq_sta->lq_info[i].is_ht40, + lq_sta->lq_info[i].is_dup, lq_sta->is_green, + lq_sta->lq_info[i].current_rate); + for (j = 0; j < RATE_COUNT; j++) { + desc += + sprintf(buff + desc, + "counter=%d success=%d %%=%d\n", + lq_sta->lq_info[i].win[j].counter, + lq_sta->lq_info[i].win[j].success_counter, + lq_sta->lq_info[i].win[j].success_ratio); + } + } + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_stats_table_ops = { + .read = il4965_rs_sta_dbgfs_stats_table_read, + .open = simple_open, + .llseek = default_llseek, +}; + +static ssize_t +il4965_rs_sta_dbgfs_rate_scale_data_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + char buff[120]; + int desc = 0; + struct il_lq_sta *lq_sta = file->private_data; + struct il_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; + + if (is_Ht(tbl->lq_type)) + desc += + sprintf(buff + desc, "Bit Rate= %d Mb/s\n", + tbl->expected_tpt[lq_sta->last_txrate_idx]); + else + desc += + sprintf(buff + desc, "Bit Rate= %d Mb/s\n", + il_rates[lq_sta->last_txrate_idx].ieee >> 1); + + return simple_read_from_buffer(user_buf, count, ppos, buff, desc); +} + +static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { + .read = il4965_rs_sta_dbgfs_rate_scale_data_read, + .open = simple_open, + .llseek = default_llseek, +}; + +static void +il4965_rs_add_debugfs(void *il, void *il_sta, struct dentry *dir) +{ + struct il_lq_sta *lq_sta = il_sta; + lq_sta->rs_sta_dbgfs_scale_table_file = + debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, + lq_sta, &rs_sta_dbgfs_scale_table_ops); + lq_sta->rs_sta_dbgfs_stats_table_file = + debugfs_create_file("rate_stats_table", S_IRUSR, dir, lq_sta, + &rs_sta_dbgfs_stats_table_ops); + lq_sta->rs_sta_dbgfs_rate_scale_data_file = + debugfs_create_file("rate_scale_data", S_IRUSR, dir, lq_sta, + &rs_sta_dbgfs_rate_scale_data_ops); + lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = + debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, + &lq_sta->tx_agg_tid_en); + +} + +static void +il4965_rs_remove_debugfs(void *il, void *il_sta) +{ + struct il_lq_sta *lq_sta = il_sta; + debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); +} +#endif + +/* + * Initialization of rate scaling information is done by driver after + * the station is added. Since mac80211 calls this function before a + * station is added we ignore it. + */ +static void +il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *il_sta) +{ +} + +static const struct rate_control_ops rs_4965_ops = { + .name = IL4965_RS_NAME, + .tx_status = il4965_rs_tx_status, + .get_rate = il4965_rs_get_rate, + .rate_init = il4965_rs_rate_init_stub, + .alloc = il4965_rs_alloc, + .free = il4965_rs_free, + .alloc_sta = il4965_rs_alloc_sta, + .free_sta = il4965_rs_free_sta, +#ifdef CONFIG_MAC80211_DEBUGFS + .add_sta_debugfs = il4965_rs_add_debugfs, + .remove_sta_debugfs = il4965_rs_remove_debugfs, +#endif +}; + +int +il4965_rate_control_register(void) +{ + return ieee80211_rate_control_register(&rs_4965_ops); +} + +void +il4965_rate_control_unregister(void) +{ + ieee80211_rate_control_unregister(&rs_4965_ops); +} diff --git a/drivers/net/wireless/intel/iwlegacy/4965.c b/drivers/net/wireless/intel/iwlegacy/4965.c new file mode 100644 index 000000000000..fe47db9c20cd --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/4965.c @@ -0,0 +1,1950 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "4965.h" + +/** + * il_verify_inst_sparse - verify runtime uCode image in card vs. host, + * using sample data 100 bytes apart. If these sample points are good, + * it's a pretty good bet that everything between them is good, too. + */ +static int +il4965_verify_inst_sparse(struct il_priv *il, __le32 * image, u32 len) +{ + u32 val; + int ret = 0; + u32 errcnt = 0; + u32 i; + + D_INFO("ucode inst image size is %u\n", len); + + for (i = 0; i < len; i += 100, image += 100 / sizeof(u32)) { + /* read data comes through single port, auto-incr addr */ + /* NOTE: Use the debugless read so we don't flood kernel log + * if IL_DL_IO is set */ + il_wr(il, HBUS_TARG_MEM_RADDR, i + IL4965_RTC_INST_LOWER_BOUND); + val = _il_rd(il, HBUS_TARG_MEM_RDAT); + if (val != le32_to_cpu(*image)) { + ret = -EIO; + errcnt++; + if (errcnt >= 3) + break; + } + } + + return ret; +} + +/** + * il4965_verify_inst_full - verify runtime uCode image in card vs. host, + * looking at all data. + */ +static int +il4965_verify_inst_full(struct il_priv *il, __le32 * image, u32 len) +{ + u32 val; + u32 save_len = len; + int ret = 0; + u32 errcnt; + + D_INFO("ucode inst image size is %u\n", len); + + il_wr(il, HBUS_TARG_MEM_RADDR, IL4965_RTC_INST_LOWER_BOUND); + + errcnt = 0; + for (; len > 0; len -= sizeof(u32), image++) { + /* read data comes through single port, auto-incr addr */ + /* NOTE: Use the debugless read so we don't flood kernel log + * if IL_DL_IO is set */ + val = _il_rd(il, HBUS_TARG_MEM_RDAT); + if (val != le32_to_cpu(*image)) { + IL_ERR("uCode INST section is invalid at " + "offset 0x%x, is 0x%x, s/b 0x%x\n", + save_len - len, val, le32_to_cpu(*image)); + ret = -EIO; + errcnt++; + if (errcnt >= 20) + break; + } + } + + if (!errcnt) + D_INFO("ucode image in INSTRUCTION memory is good\n"); + + return ret; +} + +/** + * il4965_verify_ucode - determine which instruction image is in SRAM, + * and verify its contents + */ +int +il4965_verify_ucode(struct il_priv *il) +{ + __le32 *image; + u32 len; + int ret; + + /* Try bootstrap */ + image = (__le32 *) il->ucode_boot.v_addr; + len = il->ucode_boot.len; + ret = il4965_verify_inst_sparse(il, image, len); + if (!ret) { + D_INFO("Bootstrap uCode is good in inst SRAM\n"); + return 0; + } + + /* Try initialize */ + image = (__le32 *) il->ucode_init.v_addr; + len = il->ucode_init.len; + ret = il4965_verify_inst_sparse(il, image, len); + if (!ret) { + D_INFO("Initialize uCode is good in inst SRAM\n"); + return 0; + } + + /* Try runtime/protocol */ + image = (__le32 *) il->ucode_code.v_addr; + len = il->ucode_code.len; + ret = il4965_verify_inst_sparse(il, image, len); + if (!ret) { + D_INFO("Runtime uCode is good in inst SRAM\n"); + return 0; + } + + IL_ERR("NO VALID UCODE IMAGE IN INSTRUCTION SRAM!!\n"); + + /* Since nothing seems to match, show first several data entries in + * instruction SRAM, so maybe visual inspection will give a clue. + * Selection of bootstrap image (vs. other images) is arbitrary. */ + image = (__le32 *) il->ucode_boot.v_addr; + len = il->ucode_boot.len; + ret = il4965_verify_inst_full(il, image, len); + + return ret; +} + +/****************************************************************************** + * + * EEPROM related functions + * +******************************************************************************/ + +/* + * The device's EEPROM semaphore prevents conflicts between driver and uCode + * when accessing the EEPROM; each access is a series of pulses to/from the + * EEPROM chip, not a single event, so even reads could conflict if they + * weren't arbitrated by the semaphore. + */ +int +il4965_eeprom_acquire_semaphore(struct il_priv *il) +{ + u16 count; + int ret; + + for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { + /* Request semaphore */ + il_set_bit(il, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); + + /* See if we got it */ + ret = + _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, + EEPROM_SEM_TIMEOUT); + if (ret >= 0) + return ret; + } + + return ret; +} + +void +il4965_eeprom_release_semaphore(struct il_priv *il) +{ + il_clear_bit(il, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); + +} + +int +il4965_eeprom_check_version(struct il_priv *il) +{ + u16 eeprom_ver; + u16 calib_ver; + + eeprom_ver = il_eeprom_query16(il, EEPROM_VERSION); + calib_ver = il_eeprom_query16(il, EEPROM_4965_CALIB_VERSION_OFFSET); + + if (eeprom_ver < il->cfg->eeprom_ver || + calib_ver < il->cfg->eeprom_calib_ver) + goto err; + + IL_INFO("device EEPROM VER=0x%x, CALIB=0x%x\n", eeprom_ver, calib_ver); + + return 0; +err: + IL_ERR("Unsupported (too old) EEPROM VER=0x%x < 0x%x " + "CALIB=0x%x < 0x%x\n", eeprom_ver, il->cfg->eeprom_ver, + calib_ver, il->cfg->eeprom_calib_ver); + return -EINVAL; + +} + +void +il4965_eeprom_get_mac(const struct il_priv *il, u8 * mac) +{ + const u8 *addr = il_eeprom_query_addr(il, + EEPROM_MAC_ADDRESS); + memcpy(mac, addr, ETH_ALEN); +} + +/* Send led command */ +static int +il4965_send_led_cmd(struct il_priv *il, struct il_led_cmd *led_cmd) +{ + struct il_host_cmd cmd = { + .id = C_LEDS, + .len = sizeof(struct il_led_cmd), + .data = led_cmd, + .flags = CMD_ASYNC, + .callback = NULL, + }; + u32 reg; + + reg = _il_rd(il, CSR_LED_REG); + if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) + _il_wr(il, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK); + + return il_send_cmd(il, &cmd); +} + +/* Set led register off */ +void +il4965_led_enable(struct il_priv *il) +{ + _il_wr(il, CSR_LED_REG, CSR_LED_REG_TRUN_ON); +} + +static int il4965_send_tx_power(struct il_priv *il); +static int il4965_hw_get_temperature(struct il_priv *il); + +/* Highest firmware API version supported */ +#define IL4965_UCODE_API_MAX 2 + +/* Lowest firmware API version supported */ +#define IL4965_UCODE_API_MIN 2 + +#define IL4965_FW_PRE "iwlwifi-4965-" +#define _IL4965_MODULE_FIRMWARE(api) IL4965_FW_PRE #api ".ucode" +#define IL4965_MODULE_FIRMWARE(api) _IL4965_MODULE_FIRMWARE(api) + +/* check contents of special bootstrap uCode SRAM */ +static int +il4965_verify_bsm(struct il_priv *il) +{ + __le32 *image = il->ucode_boot.v_addr; + u32 len = il->ucode_boot.len; + u32 reg; + u32 val; + + D_INFO("Begin verify bsm\n"); + + /* verify BSM SRAM contents */ + val = il_rd_prph(il, BSM_WR_DWCOUNT_REG); + for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; + reg += sizeof(u32), image++) { + val = il_rd_prph(il, reg); + if (val != le32_to_cpu(*image)) { + IL_ERR("BSM uCode verification failed at " + "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n", + BSM_SRAM_LOWER_BOUND, reg - BSM_SRAM_LOWER_BOUND, + len, val, le32_to_cpu(*image)); + return -EIO; + } + } + + D_INFO("BSM bootstrap uCode image OK\n"); + + return 0; +} + +/** + * il4965_load_bsm - Load bootstrap instructions + * + * BSM operation: + * + * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program + * in special SRAM that does not power down during RFKILL. When powering back + * up after power-saving sleeps (or during initial uCode load), the BSM loads + * the bootstrap program into the on-board processor, and starts it. + * + * The bootstrap program loads (via DMA) instructions and data for a new + * program from host DRAM locations indicated by the host driver in the + * BSM_DRAM_* registers. Once the new program is loaded, it starts + * automatically. + * + * When initializing the NIC, the host driver points the BSM to the + * "initialize" uCode image. This uCode sets up some internal data, then + * notifies host via "initialize alive" that it is complete. + * + * The host then replaces the BSM_DRAM_* pointer values to point to the + * normal runtime uCode instructions and a backup uCode data cache buffer + * (filled initially with starting data values for the on-board processor), + * then triggers the "initialize" uCode to load and launch the runtime uCode, + * which begins normal operation. + * + * When doing a power-save shutdown, runtime uCode saves data SRAM into + * the backup data cache in DRAM before SRAM is powered down. + * + * When powering back up, the BSM loads the bootstrap program. This reloads + * the runtime uCode instructions and the backup data cache into SRAM, + * and re-launches the runtime uCode from where it left off. + */ +static int +il4965_load_bsm(struct il_priv *il) +{ + __le32 *image = il->ucode_boot.v_addr; + u32 len = il->ucode_boot.len; + dma_addr_t pinst; + dma_addr_t pdata; + u32 inst_len; + u32 data_len; + int i; + u32 done; + u32 reg_offset; + int ret; + + D_INFO("Begin load bsm\n"); + + il->ucode_type = UCODE_RT; + + /* make sure bootstrap program is no larger than BSM's SRAM size */ + if (len > IL49_MAX_BSM_SIZE) + return -EINVAL; + + /* Tell bootstrap uCode where to find the "Initialize" uCode + * in host DRAM ... host DRAM physical address bits 35:4 for 4965. + * NOTE: il_init_alive_start() will replace these values, + * after the "initialize" uCode has run, to point to + * runtime/protocol instructions and backup data cache. + */ + pinst = il->ucode_init.p_addr >> 4; + pdata = il->ucode_init_data.p_addr >> 4; + inst_len = il->ucode_init.len; + data_len = il->ucode_init_data.len; + + il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); + il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); + il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); + il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); + + /* Fill BSM memory with bootstrap instructions */ + for (reg_offset = BSM_SRAM_LOWER_BOUND; + reg_offset < BSM_SRAM_LOWER_BOUND + len; + reg_offset += sizeof(u32), image++) + _il_wr_prph(il, reg_offset, le32_to_cpu(*image)); + + ret = il4965_verify_bsm(il); + if (ret) + return ret; + + /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ + il_wr_prph(il, BSM_WR_MEM_SRC_REG, 0x0); + il_wr_prph(il, BSM_WR_MEM_DST_REG, IL49_RTC_INST_LOWER_BOUND); + il_wr_prph(il, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); + + /* Load bootstrap code into instruction SRAM now, + * to prepare to load "initialize" uCode */ + il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); + + /* Wait for load of bootstrap uCode to finish */ + for (i = 0; i < 100; i++) { + done = il_rd_prph(il, BSM_WR_CTRL_REG); + if (!(done & BSM_WR_CTRL_REG_BIT_START)) + break; + udelay(10); + } + if (i < 100) + D_INFO("BSM write complete, poll %d iterations\n", i); + else { + IL_ERR("BSM write did not complete!\n"); + return -EIO; + } + + /* Enable future boot loads whenever power management unit triggers it + * (e.g. when powering back up after power-save shutdown) */ + il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); + + return 0; +} + +/** + * il4965_set_ucode_ptrs - Set uCode address location + * + * Tell initialization uCode where to find runtime uCode. + * + * BSM registers initially contain pointers to initialization uCode. + * We need to replace them to load runtime uCode inst and data, + * and to save runtime data when powering down. + */ +static int +il4965_set_ucode_ptrs(struct il_priv *il) +{ + dma_addr_t pinst; + dma_addr_t pdata; + int ret = 0; + + /* bits 35:4 for 4965 */ + pinst = il->ucode_code.p_addr >> 4; + pdata = il->ucode_data_backup.p_addr >> 4; + + /* Tell bootstrap uCode where to find image to load */ + il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); + il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); + il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, il->ucode_data.len); + + /* Inst byte count must be last to set up, bit 31 signals uCode + * that all new ptr/size info is in place */ + il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, + il->ucode_code.len | BSM_DRAM_INST_LOAD); + D_INFO("Runtime uCode pointers are set.\n"); + + return ret; +} + +/** + * il4965_init_alive_start - Called after N_ALIVE notification received + * + * Called after N_ALIVE notification received from "initialize" uCode. + * + * The 4965 "initialize" ALIVE reply contains calibration data for: + * Voltage, temperature, and MIMO tx gain correction, now stored in il + * (3945 does not contain this data). + * + * Tell "initialize" uCode to go ahead and load the runtime uCode. +*/ +static void +il4965_init_alive_start(struct il_priv *il) +{ + /* Bootstrap uCode has loaded initialize uCode ... verify inst image. + * This is a paranoid check, because we would not have gotten the + * "initialize" alive if code weren't properly loaded. */ + if (il4965_verify_ucode(il)) { + /* Runtime instruction load was bad; + * take it all the way back down so we can try again */ + D_INFO("Bad \"initialize\" uCode load.\n"); + goto restart; + } + + /* Calculate temperature */ + il->temperature = il4965_hw_get_temperature(il); + + /* Send pointers to protocol/runtime uCode image ... init code will + * load and launch runtime uCode, which will send us another "Alive" + * notification. */ + D_INFO("Initialization Alive received.\n"); + if (il4965_set_ucode_ptrs(il)) { + /* Runtime instruction load won't happen; + * take it all the way back down so we can try again */ + D_INFO("Couldn't set up uCode pointers.\n"); + goto restart; + } + return; + +restart: + queue_work(il->workqueue, &il->restart); +} + +static bool +iw4965_is_ht40_channel(__le32 rxon_flags) +{ + int chan_mod = + le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK) >> + RXON_FLG_CHANNEL_MODE_POS; + return (chan_mod == CHANNEL_MODE_PURE_40 || + chan_mod == CHANNEL_MODE_MIXED); +} + +void +il4965_nic_config(struct il_priv *il) +{ + unsigned long flags; + u16 radio_cfg; + + spin_lock_irqsave(&il->lock, flags); + + radio_cfg = il_eeprom_query16(il, EEPROM_RADIO_CONFIG); + + /* write radio config values to register */ + if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) == EEPROM_4965_RF_CFG_TYPE_MAX) + il_set_bit(il, CSR_HW_IF_CONFIG_REG, + EEPROM_RF_CFG_TYPE_MSK(radio_cfg) | + EEPROM_RF_CFG_STEP_MSK(radio_cfg) | + EEPROM_RF_CFG_DASH_MSK(radio_cfg)); + + /* set CSR_HW_CONFIG_REG for uCode use */ + il_set_bit(il, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | + CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); + + il->calib_info = + (struct il_eeprom_calib_info *) + il_eeprom_query_addr(il, EEPROM_4965_CALIB_TXPOWER_OFFSET); + + spin_unlock_irqrestore(&il->lock, flags); +} + +/* Reset differential Rx gains in NIC to prepare for chain noise calibration. + * Called after every association, but this runs only once! + * ... once chain noise is calibrated the first time, it's good forever. */ +static void +il4965_chain_noise_reset(struct il_priv *il) +{ + struct il_chain_noise_data *data = &(il->chain_noise_data); + + if (data->state == IL_CHAIN_NOISE_ALIVE && il_is_any_associated(il)) { + struct il_calib_diff_gain_cmd cmd; + + /* clear data for chain noise calibration algorithm */ + data->chain_noise_a = 0; + data->chain_noise_b = 0; + data->chain_noise_c = 0; + data->chain_signal_a = 0; + data->chain_signal_b = 0; + data->chain_signal_c = 0; + data->beacon_count = 0; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD; + cmd.diff_gain_a = 0; + cmd.diff_gain_b = 0; + cmd.diff_gain_c = 0; + if (il_send_cmd_pdu(il, C_PHY_CALIBRATION, sizeof(cmd), &cmd)) + IL_ERR("Could not send C_PHY_CALIBRATION\n"); + data->state = IL_CHAIN_NOISE_ACCUMULATE; + D_CALIB("Run chain_noise_calibrate\n"); + } +} + +static s32 +il4965_math_div_round(s32 num, s32 denom, s32 * res) +{ + s32 sign = 1; + + if (num < 0) { + sign = -sign; + num = -num; + } + if (denom < 0) { + sign = -sign; + denom = -denom; + } + *res = 1; + *res = ((num * 2 + denom) / (denom * 2)) * sign; + + return 1; +} + +/** + * il4965_get_voltage_compensation - Power supply voltage comp for txpower + * + * Determines power supply voltage compensation for txpower calculations. + * Returns number of 1/2-dB steps to subtract from gain table idx, + * to compensate for difference between power supply voltage during + * factory measurements, vs. current power supply voltage. + * + * Voltage indication is higher for lower voltage. + * Lower voltage requires more gain (lower gain table idx). + */ +static s32 +il4965_get_voltage_compensation(s32 eeprom_voltage, s32 current_voltage) +{ + s32 comp = 0; + + if (TX_POWER_IL_ILLEGAL_VOLTAGE == eeprom_voltage || + TX_POWER_IL_ILLEGAL_VOLTAGE == current_voltage) + return 0; + + il4965_math_div_round(current_voltage - eeprom_voltage, + TX_POWER_IL_VOLTAGE_CODES_PER_03V, &comp); + + if (current_voltage > eeprom_voltage) + comp *= 2; + if ((comp < -2) || (comp > 2)) + comp = 0; + + return comp; +} + +static s32 +il4965_get_tx_atten_grp(u16 channel) +{ + if (channel >= CALIB_IL_TX_ATTEN_GR5_FCH && + channel <= CALIB_IL_TX_ATTEN_GR5_LCH) + return CALIB_CH_GROUP_5; + + if (channel >= CALIB_IL_TX_ATTEN_GR1_FCH && + channel <= CALIB_IL_TX_ATTEN_GR1_LCH) + return CALIB_CH_GROUP_1; + + if (channel >= CALIB_IL_TX_ATTEN_GR2_FCH && + channel <= CALIB_IL_TX_ATTEN_GR2_LCH) + return CALIB_CH_GROUP_2; + + if (channel >= CALIB_IL_TX_ATTEN_GR3_FCH && + channel <= CALIB_IL_TX_ATTEN_GR3_LCH) + return CALIB_CH_GROUP_3; + + if (channel >= CALIB_IL_TX_ATTEN_GR4_FCH && + channel <= CALIB_IL_TX_ATTEN_GR4_LCH) + return CALIB_CH_GROUP_4; + + return -EINVAL; +} + +static u32 +il4965_get_sub_band(const struct il_priv *il, u32 channel) +{ + s32 b = -1; + + for (b = 0; b < EEPROM_TX_POWER_BANDS; b++) { + if (il->calib_info->band_info[b].ch_from == 0) + continue; + + if (channel >= il->calib_info->band_info[b].ch_from && + channel <= il->calib_info->band_info[b].ch_to) + break; + } + + return b; +} + +static s32 +il4965_interpolate_value(s32 x, s32 x1, s32 y1, s32 x2, s32 y2) +{ + s32 val; + + if (x2 == x1) + return y1; + else { + il4965_math_div_round((x2 - x) * (y1 - y2), (x2 - x1), &val); + return val + y2; + } +} + +/** + * il4965_interpolate_chan - Interpolate factory measurements for one channel + * + * Interpolates factory measurements from the two sample channels within a + * sub-band, to apply to channel of interest. Interpolation is proportional to + * differences in channel frequencies, which is proportional to differences + * in channel number. + */ +static int +il4965_interpolate_chan(struct il_priv *il, u32 channel, + struct il_eeprom_calib_ch_info *chan_info) +{ + s32 s = -1; + u32 c; + u32 m; + const struct il_eeprom_calib_measure *m1; + const struct il_eeprom_calib_measure *m2; + struct il_eeprom_calib_measure *omeas; + u32 ch_i1; + u32 ch_i2; + + s = il4965_get_sub_band(il, channel); + if (s >= EEPROM_TX_POWER_BANDS) { + IL_ERR("Tx Power can not find channel %d\n", channel); + return -1; + } + + ch_i1 = il->calib_info->band_info[s].ch1.ch_num; + ch_i2 = il->calib_info->band_info[s].ch2.ch_num; + chan_info->ch_num = (u8) channel; + + D_TXPOWER("channel %d subband %d factory cal ch %d & %d\n", channel, s, + ch_i1, ch_i2); + + for (c = 0; c < EEPROM_TX_POWER_TX_CHAINS; c++) { + for (m = 0; m < EEPROM_TX_POWER_MEASUREMENTS; m++) { + m1 = &(il->calib_info->band_info[s].ch1. + measurements[c][m]); + m2 = &(il->calib_info->band_info[s].ch2. + measurements[c][m]); + omeas = &(chan_info->measurements[c][m]); + + omeas->actual_pow = + (u8) il4965_interpolate_value(channel, ch_i1, + m1->actual_pow, ch_i2, + m2->actual_pow); + omeas->gain_idx = + (u8) il4965_interpolate_value(channel, ch_i1, + m1->gain_idx, ch_i2, + m2->gain_idx); + omeas->temperature = + (u8) il4965_interpolate_value(channel, ch_i1, + m1->temperature, + ch_i2, + m2->temperature); + omeas->pa_det = + (s8) il4965_interpolate_value(channel, ch_i1, + m1->pa_det, ch_i2, + m2->pa_det); + + D_TXPOWER("chain %d meas %d AP1=%d AP2=%d AP=%d\n", c, + m, m1->actual_pow, m2->actual_pow, + omeas->actual_pow); + D_TXPOWER("chain %d meas %d NI1=%d NI2=%d NI=%d\n", c, + m, m1->gain_idx, m2->gain_idx, + omeas->gain_idx); + D_TXPOWER("chain %d meas %d PA1=%d PA2=%d PA=%d\n", c, + m, m1->pa_det, m2->pa_det, omeas->pa_det); + D_TXPOWER("chain %d meas %d T1=%d T2=%d T=%d\n", c, + m, m1->temperature, m2->temperature, + omeas->temperature); + } + } + + return 0; +} + +/* bit-rate-dependent table to prevent Tx distortion, in half-dB units, + * for OFDM 6, 12, 18, 24, 36, 48, 54, 60 MBit, and CCK all rates. */ +static s32 back_off_table[] = { + 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 20 MHz */ + 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 20 MHz */ + 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 40 MHz */ + 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 40 MHz */ + 10 /* CCK */ +}; + +/* Thermal compensation values for txpower for various frequency ranges ... + * ratios from 3:1 to 4.5:1 of degrees (Celsius) per half-dB gain adjust */ +static struct il4965_txpower_comp_entry { + s32 degrees_per_05db_a; + s32 degrees_per_05db_a_denom; +} tx_power_cmp_tble[CALIB_CH_GROUP_MAX] = { + { + 9, 2}, /* group 0 5.2, ch 34-43 */ + { + 4, 1}, /* group 1 5.2, ch 44-70 */ + { + 4, 1}, /* group 2 5.2, ch 71-124 */ + { + 4, 1}, /* group 3 5.2, ch 125-200 */ + { + 3, 1} /* group 4 2.4, ch all */ +}; + +static s32 +get_min_power_idx(s32 rate_power_idx, u32 band) +{ + if (!band) { + if ((rate_power_idx & 7) <= 4) + return MIN_TX_GAIN_IDX_52GHZ_EXT; + } + return MIN_TX_GAIN_IDX; +} + +struct gain_entry { + u8 dsp; + u8 radio; +}; + +static const struct gain_entry gain_table[2][108] = { + /* 5.2GHz power gain idx table */ + { + {123, 0x3F}, /* highest txpower */ + {117, 0x3F}, + {110, 0x3F}, + {104, 0x3F}, + {98, 0x3F}, + {110, 0x3E}, + {104, 0x3E}, + {98, 0x3E}, + {110, 0x3D}, + {104, 0x3D}, + {98, 0x3D}, + {110, 0x3C}, + {104, 0x3C}, + {98, 0x3C}, + {110, 0x3B}, + {104, 0x3B}, + {98, 0x3B}, + {110, 0x3A}, + {104, 0x3A}, + {98, 0x3A}, + {110, 0x39}, + {104, 0x39}, + {98, 0x39}, + {110, 0x38}, + {104, 0x38}, + {98, 0x38}, + {110, 0x37}, + {104, 0x37}, + {98, 0x37}, + {110, 0x36}, + {104, 0x36}, + {98, 0x36}, + {110, 0x35}, + {104, 0x35}, + {98, 0x35}, + {110, 0x34}, + {104, 0x34}, + {98, 0x34}, + {110, 0x33}, + {104, 0x33}, + {98, 0x33}, + {110, 0x32}, + {104, 0x32}, + {98, 0x32}, + {110, 0x31}, + {104, 0x31}, + {98, 0x31}, + {110, 0x30}, + {104, 0x30}, + {98, 0x30}, + {110, 0x25}, + {104, 0x25}, + {98, 0x25}, + {110, 0x24}, + {104, 0x24}, + {98, 0x24}, + {110, 0x23}, + {104, 0x23}, + {98, 0x23}, + {110, 0x22}, + {104, 0x18}, + {98, 0x18}, + {110, 0x17}, + {104, 0x17}, + {98, 0x17}, + {110, 0x16}, + {104, 0x16}, + {98, 0x16}, + {110, 0x15}, + {104, 0x15}, + {98, 0x15}, + {110, 0x14}, + {104, 0x14}, + {98, 0x14}, + {110, 0x13}, + {104, 0x13}, + {98, 0x13}, + {110, 0x12}, + {104, 0x08}, + {98, 0x08}, + {110, 0x07}, + {104, 0x07}, + {98, 0x07}, + {110, 0x06}, + {104, 0x06}, + {98, 0x06}, + {110, 0x05}, + {104, 0x05}, + {98, 0x05}, + {110, 0x04}, + {104, 0x04}, + {98, 0x04}, + {110, 0x03}, + {104, 0x03}, + {98, 0x03}, + {110, 0x02}, + {104, 0x02}, + {98, 0x02}, + {110, 0x01}, + {104, 0x01}, + {98, 0x01}, + {110, 0x00}, + {104, 0x00}, + {98, 0x00}, + {93, 0x00}, + {88, 0x00}, + {83, 0x00}, + {78, 0x00}, + }, + /* 2.4GHz power gain idx table */ + { + {110, 0x3f}, /* highest txpower */ + {104, 0x3f}, + {98, 0x3f}, + {110, 0x3e}, + {104, 0x3e}, + {98, 0x3e}, + {110, 0x3d}, + {104, 0x3d}, + {98, 0x3d}, + {110, 0x3c}, + {104, 0x3c}, + {98, 0x3c}, + {110, 0x3b}, + {104, 0x3b}, + {98, 0x3b}, + {110, 0x3a}, + {104, 0x3a}, + {98, 0x3a}, + {110, 0x39}, + {104, 0x39}, + {98, 0x39}, + {110, 0x38}, + {104, 0x38}, + {98, 0x38}, + {110, 0x37}, + {104, 0x37}, + {98, 0x37}, + {110, 0x36}, + {104, 0x36}, + {98, 0x36}, + {110, 0x35}, + {104, 0x35}, + {98, 0x35}, + {110, 0x34}, + {104, 0x34}, + {98, 0x34}, + {110, 0x33}, + {104, 0x33}, + {98, 0x33}, + {110, 0x32}, + {104, 0x32}, + {98, 0x32}, + {110, 0x31}, + {104, 0x31}, + {98, 0x31}, + {110, 0x30}, + {104, 0x30}, + {98, 0x30}, + {110, 0x6}, + {104, 0x6}, + {98, 0x6}, + {110, 0x5}, + {104, 0x5}, + {98, 0x5}, + {110, 0x4}, + {104, 0x4}, + {98, 0x4}, + {110, 0x3}, + {104, 0x3}, + {98, 0x3}, + {110, 0x2}, + {104, 0x2}, + {98, 0x2}, + {110, 0x1}, + {104, 0x1}, + {98, 0x1}, + {110, 0x0}, + {104, 0x0}, + {98, 0x0}, + {97, 0}, + {96, 0}, + {95, 0}, + {94, 0}, + {93, 0}, + {92, 0}, + {91, 0}, + {90, 0}, + {89, 0}, + {88, 0}, + {87, 0}, + {86, 0}, + {85, 0}, + {84, 0}, + {83, 0}, + {82, 0}, + {81, 0}, + {80, 0}, + {79, 0}, + {78, 0}, + {77, 0}, + {76, 0}, + {75, 0}, + {74, 0}, + {73, 0}, + {72, 0}, + {71, 0}, + {70, 0}, + {69, 0}, + {68, 0}, + {67, 0}, + {66, 0}, + {65, 0}, + {64, 0}, + {63, 0}, + {62, 0}, + {61, 0}, + {60, 0}, + {59, 0}, + } +}; + +static int +il4965_fill_txpower_tbl(struct il_priv *il, u8 band, u16 channel, u8 is_ht40, + u8 ctrl_chan_high, + struct il4965_tx_power_db *tx_power_tbl) +{ + u8 saturation_power; + s32 target_power; + s32 user_target_power; + s32 power_limit; + s32 current_temp; + s32 reg_limit; + s32 current_regulatory; + s32 txatten_grp = CALIB_CH_GROUP_MAX; + int i; + int c; + const struct il_channel_info *ch_info = NULL; + struct il_eeprom_calib_ch_info ch_eeprom_info; + const struct il_eeprom_calib_measure *measurement; + s16 voltage; + s32 init_voltage; + s32 voltage_compensation; + s32 degrees_per_05db_num; + s32 degrees_per_05db_denom; + s32 factory_temp; + s32 temperature_comp[2]; + s32 factory_gain_idx[2]; + s32 factory_actual_pwr[2]; + s32 power_idx; + + /* tx_power_user_lmt is in dBm, convert to half-dBm (half-dB units + * are used for idxing into txpower table) */ + user_target_power = 2 * il->tx_power_user_lmt; + + /* Get current (RXON) channel, band, width */ + D_TXPOWER("chan %d band %d is_ht40 %d\n", channel, band, is_ht40); + + ch_info = il_get_channel_info(il, il->band, channel); + + if (!il_is_channel_valid(ch_info)) + return -EINVAL; + + /* get txatten group, used to select 1) thermal txpower adjustment + * and 2) mimo txpower balance between Tx chains. */ + txatten_grp = il4965_get_tx_atten_grp(channel); + if (txatten_grp < 0) { + IL_ERR("Can't find txatten group for channel %d.\n", channel); + return txatten_grp; + } + + D_TXPOWER("channel %d belongs to txatten group %d\n", channel, + txatten_grp); + + if (is_ht40) { + if (ctrl_chan_high) + channel -= 2; + else + channel += 2; + } + + /* hardware txpower limits ... + * saturation (clipping distortion) txpowers are in half-dBm */ + if (band) + saturation_power = il->calib_info->saturation_power24; + else + saturation_power = il->calib_info->saturation_power52; + + if (saturation_power < IL_TX_POWER_SATURATION_MIN || + saturation_power > IL_TX_POWER_SATURATION_MAX) { + if (band) + saturation_power = IL_TX_POWER_DEFAULT_SATURATION_24; + else + saturation_power = IL_TX_POWER_DEFAULT_SATURATION_52; + } + + /* regulatory txpower limits ... reg_limit values are in half-dBm, + * max_power_avg values are in dBm, convert * 2 */ + if (is_ht40) + reg_limit = ch_info->ht40_max_power_avg * 2; + else + reg_limit = ch_info->max_power_avg * 2; + + if ((reg_limit < IL_TX_POWER_REGULATORY_MIN) || + (reg_limit > IL_TX_POWER_REGULATORY_MAX)) { + if (band) + reg_limit = IL_TX_POWER_DEFAULT_REGULATORY_24; + else + reg_limit = IL_TX_POWER_DEFAULT_REGULATORY_52; + } + + /* Interpolate txpower calibration values for this channel, + * based on factory calibration tests on spaced channels. */ + il4965_interpolate_chan(il, channel, &ch_eeprom_info); + + /* calculate tx gain adjustment based on power supply voltage */ + voltage = le16_to_cpu(il->calib_info->voltage); + init_voltage = (s32) le32_to_cpu(il->card_alive_init.voltage); + voltage_compensation = + il4965_get_voltage_compensation(voltage, init_voltage); + + D_TXPOWER("curr volt %d eeprom volt %d volt comp %d\n", init_voltage, + voltage, voltage_compensation); + + /* get current temperature (Celsius) */ + current_temp = max(il->temperature, IL_TX_POWER_TEMPERATURE_MIN); + current_temp = min(il->temperature, IL_TX_POWER_TEMPERATURE_MAX); + current_temp = KELVIN_TO_CELSIUS(current_temp); + + /* select thermal txpower adjustment params, based on channel group + * (same frequency group used for mimo txatten adjustment) */ + degrees_per_05db_num = + tx_power_cmp_tble[txatten_grp].degrees_per_05db_a; + degrees_per_05db_denom = + tx_power_cmp_tble[txatten_grp].degrees_per_05db_a_denom; + + /* get per-chain txpower values from factory measurements */ + for (c = 0; c < 2; c++) { + measurement = &ch_eeprom_info.measurements[c][1]; + + /* txgain adjustment (in half-dB steps) based on difference + * between factory and current temperature */ + factory_temp = measurement->temperature; + il4965_math_div_round((current_temp - + factory_temp) * degrees_per_05db_denom, + degrees_per_05db_num, + &temperature_comp[c]); + + factory_gain_idx[c] = measurement->gain_idx; + factory_actual_pwr[c] = measurement->actual_pow; + + D_TXPOWER("chain = %d\n", c); + D_TXPOWER("fctry tmp %d, " "curr tmp %d, comp %d steps\n", + factory_temp, current_temp, temperature_comp[c]); + + D_TXPOWER("fctry idx %d, fctry pwr %d\n", factory_gain_idx[c], + factory_actual_pwr[c]); + } + + /* for each of 33 bit-rates (including 1 for CCK) */ + for (i = 0; i < POWER_TBL_NUM_ENTRIES; i++) { + u8 is_mimo_rate; + union il4965_tx_power_dual_stream tx_power; + + /* for mimo, reduce each chain's txpower by half + * (3dB, 6 steps), so total output power is regulatory + * compliant. */ + if (i & 0x8) { + current_regulatory = + reg_limit - + IL_TX_POWER_MIMO_REGULATORY_COMPENSATION; + is_mimo_rate = 1; + } else { + current_regulatory = reg_limit; + is_mimo_rate = 0; + } + + /* find txpower limit, either hardware or regulatory */ + power_limit = saturation_power - back_off_table[i]; + if (power_limit > current_regulatory) + power_limit = current_regulatory; + + /* reduce user's txpower request if necessary + * for this rate on this channel */ + target_power = user_target_power; + if (target_power > power_limit) + target_power = power_limit; + + D_TXPOWER("rate %d sat %d reg %d usr %d tgt %d\n", i, + saturation_power - back_off_table[i], + current_regulatory, user_target_power, target_power); + + /* for each of 2 Tx chains (radio transmitters) */ + for (c = 0; c < 2; c++) { + s32 atten_value; + + if (is_mimo_rate) + atten_value = + (s32) le32_to_cpu(il->card_alive_init. + tx_atten[txatten_grp][c]); + else + atten_value = 0; + + /* calculate idx; higher idx means lower txpower */ + power_idx = + (u8) (factory_gain_idx[c] - + (target_power - factory_actual_pwr[c]) - + temperature_comp[c] - voltage_compensation + + atten_value); + +/* D_TXPOWER("calculated txpower idx %d\n", + power_idx); */ + + if (power_idx < get_min_power_idx(i, band)) + power_idx = get_min_power_idx(i, band); + + /* adjust 5 GHz idx to support negative idxes */ + if (!band) + power_idx += 9; + + /* CCK, rate 32, reduce txpower for CCK */ + if (i == POWER_TBL_CCK_ENTRY) + power_idx += + IL_TX_POWER_CCK_COMPENSATION_C_STEP; + + /* stay within the table! */ + if (power_idx > 107) { + IL_WARN("txpower idx %d > 107\n", power_idx); + power_idx = 107; + } + if (power_idx < 0) { + IL_WARN("txpower idx %d < 0\n", power_idx); + power_idx = 0; + } + + /* fill txpower command for this rate/chain */ + tx_power.s.radio_tx_gain[c] = + gain_table[band][power_idx].radio; + tx_power.s.dsp_predis_atten[c] = + gain_table[band][power_idx].dsp; + + D_TXPOWER("chain %d mimo %d idx %d " + "gain 0x%02x dsp %d\n", c, atten_value, + power_idx, tx_power.s.radio_tx_gain[c], + tx_power.s.dsp_predis_atten[c]); + } /* for each chain */ + + tx_power_tbl->power_tbl[i].dw = cpu_to_le32(tx_power.dw); + + } /* for each rate */ + + return 0; +} + +/** + * il4965_send_tx_power - Configure the TXPOWER level user limit + * + * Uses the active RXON for channel, band, and characteristics (ht40, high) + * The power limit is taken from il->tx_power_user_lmt. + */ +static int +il4965_send_tx_power(struct il_priv *il) +{ + struct il4965_txpowertable_cmd cmd = { 0 }; + int ret; + u8 band = 0; + bool is_ht40 = false; + u8 ctrl_chan_high = 0; + + if (WARN_ONCE + (test_bit(S_SCAN_HW, &il->status), + "TX Power requested while scanning!\n")) + return -EAGAIN; + + band = il->band == IEEE80211_BAND_2GHZ; + + is_ht40 = iw4965_is_ht40_channel(il->active.flags); + + if (is_ht40 && (il->active.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) + ctrl_chan_high = 1; + + cmd.band = band; + cmd.channel = il->active.channel; + + ret = + il4965_fill_txpower_tbl(il, band, le16_to_cpu(il->active.channel), + is_ht40, ctrl_chan_high, &cmd.tx_power); + if (ret) + goto out; + + ret = il_send_cmd_pdu(il, C_TX_PWR_TBL, sizeof(cmd), &cmd); + +out: + return ret; +} + +static int +il4965_send_rxon_assoc(struct il_priv *il) +{ + int ret = 0; + struct il4965_rxon_assoc_cmd rxon_assoc; + const struct il_rxon_cmd *rxon1 = &il->staging; + const struct il_rxon_cmd *rxon2 = &il->active; + + if (rxon1->flags == rxon2->flags && + rxon1->filter_flags == rxon2->filter_flags && + rxon1->cck_basic_rates == rxon2->cck_basic_rates && + rxon1->ofdm_ht_single_stream_basic_rates == + rxon2->ofdm_ht_single_stream_basic_rates && + rxon1->ofdm_ht_dual_stream_basic_rates == + rxon2->ofdm_ht_dual_stream_basic_rates && + rxon1->rx_chain == rxon2->rx_chain && + rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates) { + D_INFO("Using current RXON_ASSOC. Not resending.\n"); + return 0; + } + + rxon_assoc.flags = il->staging.flags; + rxon_assoc.filter_flags = il->staging.filter_flags; + rxon_assoc.ofdm_basic_rates = il->staging.ofdm_basic_rates; + rxon_assoc.cck_basic_rates = il->staging.cck_basic_rates; + rxon_assoc.reserved = 0; + rxon_assoc.ofdm_ht_single_stream_basic_rates = + il->staging.ofdm_ht_single_stream_basic_rates; + rxon_assoc.ofdm_ht_dual_stream_basic_rates = + il->staging.ofdm_ht_dual_stream_basic_rates; + rxon_assoc.rx_chain_select_flags = il->staging.rx_chain; + + ret = + il_send_cmd_pdu_async(il, C_RXON_ASSOC, sizeof(rxon_assoc), + &rxon_assoc, NULL); + + return ret; +} + +static int +il4965_commit_rxon(struct il_priv *il) +{ + /* cast away the const for active_rxon in this function */ + struct il_rxon_cmd *active_rxon = (void *)&il->active; + int ret; + bool new_assoc = !!(il->staging.filter_flags & RXON_FILTER_ASSOC_MSK); + + if (!il_is_alive(il)) + return -EBUSY; + + /* always get timestamp with Rx frame */ + il->staging.flags |= RXON_FLG_TSF2HOST_MSK; + + ret = il_check_rxon_cmd(il); + if (ret) { + IL_ERR("Invalid RXON configuration. Not committing.\n"); + return -EINVAL; + } + + /* + * receive commit_rxon request + * abort any previous channel switch if still in process + */ + if (test_bit(S_CHANNEL_SWITCH_PENDING, &il->status) && + il->switch_channel != il->staging.channel) { + D_11H("abort channel switch on %d\n", + le16_to_cpu(il->switch_channel)); + il_chswitch_done(il, false); + } + + /* If we don't need to send a full RXON, we can use + * il_rxon_assoc_cmd which is used to reconfigure filter + * and other flags for the current radio configuration. */ + if (!il_full_rxon_required(il)) { + ret = il_send_rxon_assoc(il); + if (ret) { + IL_ERR("Error setting RXON_ASSOC (%d)\n", ret); + return ret; + } + + memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); + il_print_rx_config_cmd(il); + /* + * We do not commit tx power settings while channel changing, + * do it now if tx power changed. + */ + il_set_tx_power(il, il->tx_power_next, false); + return 0; + } + + /* If we are currently associated and the new config requires + * an RXON_ASSOC and the new config wants the associated mask enabled, + * we must clear the associated from the active configuration + * before we apply the new config */ + if (il_is_associated(il) && new_assoc) { + D_INFO("Toggling associated bit on current RXON\n"); + active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + + ret = + il_send_cmd_pdu(il, C_RXON, + sizeof(struct il_rxon_cmd), active_rxon); + + /* If the mask clearing failed then we set + * active_rxon back to what it was previously */ + if (ret) { + active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK; + IL_ERR("Error clearing ASSOC_MSK (%d)\n", ret); + return ret; + } + il_clear_ucode_stations(il); + il_restore_stations(il); + ret = il4965_restore_default_wep_keys(il); + if (ret) { + IL_ERR("Failed to restore WEP keys (%d)\n", ret); + return ret; + } + } + + D_INFO("Sending RXON\n" "* with%s RXON_FILTER_ASSOC_MSK\n" + "* channel = %d\n" "* bssid = %pM\n", (new_assoc ? "" : "out"), + le16_to_cpu(il->staging.channel), il->staging.bssid_addr); + + il_set_rxon_hwcrypto(il, !il->cfg->mod_params->sw_crypto); + + /* Apply the new configuration + * RXON unassoc clears the station table in uCode so restoration of + * stations is needed after it (the RXON command) completes + */ + if (!new_assoc) { + ret = + il_send_cmd_pdu(il, C_RXON, + sizeof(struct il_rxon_cmd), &il->staging); + if (ret) { + IL_ERR("Error setting new RXON (%d)\n", ret); + return ret; + } + D_INFO("Return from !new_assoc RXON.\n"); + memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); + il_clear_ucode_stations(il); + il_restore_stations(il); + ret = il4965_restore_default_wep_keys(il); + if (ret) { + IL_ERR("Failed to restore WEP keys (%d)\n", ret); + return ret; + } + } + if (new_assoc) { + il->start_calib = 0; + /* Apply the new configuration + * RXON assoc doesn't clear the station table in uCode, + */ + ret = + il_send_cmd_pdu(il, C_RXON, + sizeof(struct il_rxon_cmd), &il->staging); + if (ret) { + IL_ERR("Error setting new RXON (%d)\n", ret); + return ret; + } + memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); + } + il_print_rx_config_cmd(il); + + il4965_init_sensitivity(il); + + /* If we issue a new RXON command which required a tune then we must + * send a new TXPOWER command or we won't be able to Tx any frames */ + ret = il_set_tx_power(il, il->tx_power_next, true); + if (ret) { + IL_ERR("Error sending TX power (%d)\n", ret); + return ret; + } + + return 0; +} + +static int +il4965_hw_channel_switch(struct il_priv *il, + struct ieee80211_channel_switch *ch_switch) +{ + int rc; + u8 band = 0; + bool is_ht40 = false; + u8 ctrl_chan_high = 0; + struct il4965_channel_switch_cmd cmd; + const struct il_channel_info *ch_info; + u32 switch_time_in_usec, ucode_switch_time; + u16 ch; + u32 tsf_low; + u8 switch_count; + u16 beacon_interval = le16_to_cpu(il->timing.beacon_interval); + struct ieee80211_vif *vif = il->vif; + band = (il->band == IEEE80211_BAND_2GHZ); + + if (WARN_ON_ONCE(vif == NULL)) + return -EIO; + + is_ht40 = iw4965_is_ht40_channel(il->staging.flags); + + if (is_ht40 && (il->staging.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) + ctrl_chan_high = 1; + + cmd.band = band; + cmd.expect_beacon = 0; + ch = ch_switch->chandef.chan->hw_value; + cmd.channel = cpu_to_le16(ch); + cmd.rxon_flags = il->staging.flags; + cmd.rxon_filter_flags = il->staging.filter_flags; + switch_count = ch_switch->count; + tsf_low = ch_switch->timestamp & 0x0ffffffff; + /* + * calculate the ucode channel switch time + * adding TSF as one of the factor for when to switch + */ + if (il->ucode_beacon_time > tsf_low && beacon_interval) { + if (switch_count > + ((il->ucode_beacon_time - tsf_low) / beacon_interval)) { + switch_count -= + (il->ucode_beacon_time - tsf_low) / beacon_interval; + } else + switch_count = 0; + } + if (switch_count <= 1) + cmd.switch_time = cpu_to_le32(il->ucode_beacon_time); + else { + switch_time_in_usec = + vif->bss_conf.beacon_int * switch_count * TIME_UNIT; + ucode_switch_time = + il_usecs_to_beacons(il, switch_time_in_usec, + beacon_interval); + cmd.switch_time = + il_add_beacon_time(il, il->ucode_beacon_time, + ucode_switch_time, beacon_interval); + } + D_11H("uCode time for the switch is 0x%x\n", cmd.switch_time); + ch_info = il_get_channel_info(il, il->band, ch); + if (ch_info) + cmd.expect_beacon = il_is_channel_radar(ch_info); + else { + IL_ERR("invalid channel switch from %u to %u\n", + il->active.channel, ch); + return -EFAULT; + } + + rc = il4965_fill_txpower_tbl(il, band, ch, is_ht40, ctrl_chan_high, + &cmd.tx_power); + if (rc) { + D_11H("error:%d fill txpower_tbl\n", rc); + return rc; + } + + return il_send_cmd_pdu(il, C_CHANNEL_SWITCH, sizeof(cmd), &cmd); +} + +/** + * il4965_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array + */ +static void +il4965_txq_update_byte_cnt_tbl(struct il_priv *il, struct il_tx_queue *txq, + u16 byte_cnt) +{ + struct il4965_scd_bc_tbl *scd_bc_tbl = il->scd_bc_tbls.addr; + int txq_id = txq->q.id; + int write_ptr = txq->q.write_ptr; + int len = byte_cnt + IL_TX_CRC_SIZE + IL_TX_DELIMITER_SIZE; + __le16 bc_ent; + + WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX); + + bc_ent = cpu_to_le16(len & 0xFFF); + /* Set up byte count within first 256 entries */ + scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; + + /* If within first 64 entries, duplicate at end */ + if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) + scd_bc_tbl[txq_id].tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = + bc_ent; +} + +/** + * il4965_hw_get_temperature - return the calibrated temperature (in Kelvin) + * @stats: Provides the temperature reading from the uCode + * + * A return of <0 indicates bogus data in the stats + */ +static int +il4965_hw_get_temperature(struct il_priv *il) +{ + s32 temperature; + s32 vt; + s32 R1, R2, R3; + u32 R4; + + if (test_bit(S_TEMPERATURE, &il->status) && + (il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK)) { + D_TEMP("Running HT40 temperature calibration\n"); + R1 = (s32) le32_to_cpu(il->card_alive_init.therm_r1[1]); + R2 = (s32) le32_to_cpu(il->card_alive_init.therm_r2[1]); + R3 = (s32) le32_to_cpu(il->card_alive_init.therm_r3[1]); + R4 = le32_to_cpu(il->card_alive_init.therm_r4[1]); + } else { + D_TEMP("Running temperature calibration\n"); + R1 = (s32) le32_to_cpu(il->card_alive_init.therm_r1[0]); + R2 = (s32) le32_to_cpu(il->card_alive_init.therm_r2[0]); + R3 = (s32) le32_to_cpu(il->card_alive_init.therm_r3[0]); + R4 = le32_to_cpu(il->card_alive_init.therm_r4[0]); + } + + /* + * Temperature is only 23 bits, so sign extend out to 32. + * + * NOTE If we haven't received a stats notification yet + * with an updated temperature, use R4 provided to us in the + * "initialize" ALIVE response. + */ + if (!test_bit(S_TEMPERATURE, &il->status)) + vt = sign_extend32(R4, 23); + else + vt = sign_extend32(le32_to_cpu + (il->_4965.stats.general.common.temperature), + 23); + + D_TEMP("Calib values R[1-3]: %d %d %d R4: %d\n", R1, R2, R3, vt); + + if (R3 == R1) { + IL_ERR("Calibration conflict R1 == R3\n"); + return -1; + } + + /* Calculate temperature in degrees Kelvin, adjust by 97%. + * Add offset to center the adjustment around 0 degrees Centigrade. */ + temperature = TEMPERATURE_CALIB_A_VAL * (vt - R2); + temperature /= (R3 - R1); + temperature = + (temperature * 97) / 100 + TEMPERATURE_CALIB_KELVIN_OFFSET; + + D_TEMP("Calibrated temperature: %dK, %dC\n", temperature, + KELVIN_TO_CELSIUS(temperature)); + + return temperature; +} + +/* Adjust Txpower only if temperature variance is greater than threshold. */ +#define IL_TEMPERATURE_THRESHOLD 3 + +/** + * il4965_is_temp_calib_needed - determines if new calibration is needed + * + * If the temperature changed has changed sufficiently, then a recalibration + * is needed. + * + * Assumes caller will replace il->last_temperature once calibration + * executed. + */ +static int +il4965_is_temp_calib_needed(struct il_priv *il) +{ + int temp_diff; + + if (!test_bit(S_STATS, &il->status)) { + D_TEMP("Temperature not updated -- no stats.\n"); + return 0; + } + + temp_diff = il->temperature - il->last_temperature; + + /* get absolute value */ + if (temp_diff < 0) { + D_POWER("Getting cooler, delta %d\n", temp_diff); + temp_diff = -temp_diff; + } else if (temp_diff == 0) + D_POWER("Temperature unchanged\n"); + else + D_POWER("Getting warmer, delta %d\n", temp_diff); + + if (temp_diff < IL_TEMPERATURE_THRESHOLD) { + D_POWER(" => thermal txpower calib not needed\n"); + return 0; + } + + D_POWER(" => thermal txpower calib needed\n"); + + return 1; +} + +void +il4965_temperature_calib(struct il_priv *il) +{ + s32 temp; + + temp = il4965_hw_get_temperature(il); + if (IL_TX_POWER_TEMPERATURE_OUT_OF_RANGE(temp)) + return; + + if (il->temperature != temp) { + if (il->temperature) + D_TEMP("Temperature changed " "from %dC to %dC\n", + KELVIN_TO_CELSIUS(il->temperature), + KELVIN_TO_CELSIUS(temp)); + else + D_TEMP("Temperature " "initialized to %dC\n", + KELVIN_TO_CELSIUS(temp)); + } + + il->temperature = temp; + set_bit(S_TEMPERATURE, &il->status); + + if (!il->disable_tx_power_cal && + unlikely(!test_bit(S_SCANNING, &il->status)) && + il4965_is_temp_calib_needed(il)) + queue_work(il->workqueue, &il->txpower_work); +} + +static u16 +il4965_get_hcmd_size(u8 cmd_id, u16 len) +{ + switch (cmd_id) { + case C_RXON: + return (u16) sizeof(struct il4965_rxon_cmd); + default: + return len; + } +} + +static u16 +il4965_build_addsta_hcmd(const struct il_addsta_cmd *cmd, u8 * data) +{ + struct il4965_addsta_cmd *addsta = (struct il4965_addsta_cmd *)data; + addsta->mode = cmd->mode; + memcpy(&addsta->sta, &cmd->sta, sizeof(struct sta_id_modify)); + memcpy(&addsta->key, &cmd->key, sizeof(struct il4965_keyinfo)); + addsta->station_flags = cmd->station_flags; + addsta->station_flags_msk = cmd->station_flags_msk; + addsta->tid_disable_tx = cmd->tid_disable_tx; + addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid; + addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid; + addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn; + addsta->sleep_tx_count = cmd->sleep_tx_count; + addsta->reserved1 = cpu_to_le16(0); + addsta->reserved2 = cpu_to_le16(0); + + return (u16) sizeof(struct il4965_addsta_cmd); +} + +static void +il4965_post_scan(struct il_priv *il) +{ + /* + * Since setting the RXON may have been deferred while + * performing the scan, fire one off if needed + */ + if (memcmp(&il->staging, &il->active, sizeof(il->staging))) + il_commit_rxon(il); +} + +static void +il4965_post_associate(struct il_priv *il) +{ + struct ieee80211_vif *vif = il->vif; + int ret = 0; + + if (!vif || !il->is_open) + return; + + if (test_bit(S_EXIT_PENDING, &il->status)) + return; + + il_scan_cancel_timeout(il, 200); + + il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + il_commit_rxon(il); + + ret = il_send_rxon_timing(il); + if (ret) + IL_WARN("RXON timing - " "Attempting to continue.\n"); + + il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + + il_set_rxon_ht(il, &il->current_ht_config); + + if (il->ops->set_rxon_chain) + il->ops->set_rxon_chain(il); + + il->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid); + + D_ASSOC("assoc id %d beacon interval %d\n", vif->bss_conf.aid, + vif->bss_conf.beacon_int); + + if (vif->bss_conf.use_short_preamble) + il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + else + il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + + if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { + if (vif->bss_conf.use_short_slot) + il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + } + + il_commit_rxon(il); + + D_ASSOC("Associated as %d to: %pM\n", vif->bss_conf.aid, + il->active.bssid_addr); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + break; + case NL80211_IFTYPE_ADHOC: + il4965_send_beacon_cmd(il); + break; + default: + IL_ERR("%s Should not be called in %d mode\n", __func__, + vif->type); + break; + } + + /* the chain noise calibration will enabled PM upon completion + * If chain noise has already been run, then we need to enable + * power management here */ + if (il->chain_noise_data.state == IL_CHAIN_NOISE_DONE) + il_power_update_mode(il, false); + + /* Enable Rx differential gain and sensitivity calibrations */ + il4965_chain_noise_reset(il); + il->start_calib = 1; +} + +static void +il4965_config_ap(struct il_priv *il) +{ + struct ieee80211_vif *vif = il->vif; + int ret = 0; + + lockdep_assert_held(&il->mutex); + + if (test_bit(S_EXIT_PENDING, &il->status)) + return; + + /* The following should be done only at AP bring up */ + if (!il_is_associated(il)) { + + /* RXON - unassoc (to set timing command) */ + il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + il_commit_rxon(il); + + /* RXON Timing */ + ret = il_send_rxon_timing(il); + if (ret) + IL_WARN("RXON timing failed - " + "Attempting to continue.\n"); + + /* AP has all antennas */ + il->chain_noise_data.active_chains = il->hw_params.valid_rx_ant; + il_set_rxon_ht(il, &il->current_ht_config); + if (il->ops->set_rxon_chain) + il->ops->set_rxon_chain(il); + + il->staging.assoc_id = 0; + + if (vif->bss_conf.use_short_preamble) + il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + else + il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + + if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { + if (vif->bss_conf.use_short_slot) + il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + } + /* need to send beacon cmd before committing assoc RXON! */ + il4965_send_beacon_cmd(il); + /* restore RXON assoc */ + il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + il_commit_rxon(il); + } + il4965_send_beacon_cmd(il); +} + +const struct il_ops il4965_ops = { + .txq_update_byte_cnt_tbl = il4965_txq_update_byte_cnt_tbl, + .txq_attach_buf_to_tfd = il4965_hw_txq_attach_buf_to_tfd, + .txq_free_tfd = il4965_hw_txq_free_tfd, + .txq_init = il4965_hw_tx_queue_init, + .is_valid_rtc_data_addr = il4965_hw_valid_rtc_data_addr, + .init_alive_start = il4965_init_alive_start, + .load_ucode = il4965_load_bsm, + .dump_nic_error_log = il4965_dump_nic_error_log, + .dump_fh = il4965_dump_fh, + .set_channel_switch = il4965_hw_channel_switch, + .apm_init = il_apm_init, + .send_tx_power = il4965_send_tx_power, + .update_chain_flags = il4965_update_chain_flags, + .eeprom_acquire_semaphore = il4965_eeprom_acquire_semaphore, + .eeprom_release_semaphore = il4965_eeprom_release_semaphore, + + .rxon_assoc = il4965_send_rxon_assoc, + .commit_rxon = il4965_commit_rxon, + .set_rxon_chain = il4965_set_rxon_chain, + + .get_hcmd_size = il4965_get_hcmd_size, + .build_addsta_hcmd = il4965_build_addsta_hcmd, + .request_scan = il4965_request_scan, + .post_scan = il4965_post_scan, + + .post_associate = il4965_post_associate, + .config_ap = il4965_config_ap, + .manage_ibss_station = il4965_manage_ibss_station, + .update_bcast_stations = il4965_update_bcast_stations, + + .send_led_cmd = il4965_send_led_cmd, +}; + +struct il_cfg il4965_cfg = { + .name = "Intel(R) Wireless WiFi Link 4965AGN", + .fw_name_pre = IL4965_FW_PRE, + .ucode_api_max = IL4965_UCODE_API_MAX, + .ucode_api_min = IL4965_UCODE_API_MIN, + .sku = IL_SKU_A | IL_SKU_G | IL_SKU_N, + .valid_tx_ant = ANT_AB, + .valid_rx_ant = ANT_ABC, + .eeprom_ver = EEPROM_4965_EEPROM_VERSION, + .eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION, + .mod_params = &il4965_mod_params, + .led_mode = IL_LED_BLINK, + /* + * Force use of chains B and C for scan RX on 5 GHz band + * because the device has off-channel reception on chain A. + */ + .scan_rx_antennas[IEEE80211_BAND_5GHZ] = ANT_BC, + + .eeprom_size = IL4965_EEPROM_IMG_SIZE, + .num_of_queues = IL49_NUM_QUEUES, + .num_of_ampdu_queues = IL49_NUM_AMPDU_QUEUES, + .pll_cfg_val = 0, + .set_l0s = true, + .use_bsm = true, + .led_compensation = 61, + .chain_noise_num_beacons = IL4965_CAL_NUM_BEACONS, + .wd_timeout = IL_DEF_WD_TIMEOUT, + .temperature_kelvin = true, + .ucode_tracing = true, + .sensitivity_calib_by_driver = true, + .chain_noise_calib_by_driver = true, + + .regulatory_bands = { + EEPROM_REGULATORY_BAND_1_CHANNELS, + EEPROM_REGULATORY_BAND_2_CHANNELS, + EEPROM_REGULATORY_BAND_3_CHANNELS, + EEPROM_REGULATORY_BAND_4_CHANNELS, + EEPROM_REGULATORY_BAND_5_CHANNELS, + EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS, + EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS + }, + +}; + +/* Module firmware */ +MODULE_FIRMWARE(IL4965_MODULE_FIRMWARE(IL4965_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlegacy/4965.h b/drivers/net/wireless/intel/iwlegacy/4965.h new file mode 100644 index 000000000000..8ab8706f9422 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/4965.h @@ -0,0 +1,1285 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __il_4965_h__ +#define __il_4965_h__ + +struct il_rx_queue; +struct il_rx_buf; +struct il_rx_pkt; +struct il_tx_queue; +struct il_rxon_context; + +/* configuration for the _4965 devices */ +extern struct il_cfg il4965_cfg; +extern const struct il_ops il4965_ops; + +extern struct il_mod_params il4965_mod_params; + +/* tx queue */ +void il4965_free_tfds_in_queue(struct il_priv *il, int sta_id, int tid, + int freed); + +/* RXON */ +void il4965_set_rxon_chain(struct il_priv *il); + +/* uCode */ +int il4965_verify_ucode(struct il_priv *il); + +/* lib */ +void il4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status); + +void il4965_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq); +int il4965_rx_init(struct il_priv *il, struct il_rx_queue *rxq); +int il4965_hw_nic_init(struct il_priv *il); +int il4965_dump_fh(struct il_priv *il, char **buf, bool display); + +void il4965_nic_config(struct il_priv *il); + +/* rx */ +void il4965_rx_queue_restock(struct il_priv *il); +void il4965_rx_replenish(struct il_priv *il); +void il4965_rx_replenish_now(struct il_priv *il); +void il4965_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq); +int il4965_rxq_stop(struct il_priv *il); +int il4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); +void il4965_rx_handle(struct il_priv *il); + +/* tx */ +void il4965_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq); +int il4965_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, + dma_addr_t addr, u16 len, u8 reset, u8 pad); +int il4965_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq); +void il4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags, + struct ieee80211_tx_info *info); +int il4965_tx_skb(struct il_priv *il, + struct ieee80211_sta *sta, + struct sk_buff *skb); +int il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u16 * ssn); +int il4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid); +int il4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id); +int il4965_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx); +void il4965_hw_txq_ctx_free(struct il_priv *il); +int il4965_txq_ctx_alloc(struct il_priv *il); +void il4965_txq_ctx_reset(struct il_priv *il); +void il4965_txq_ctx_stop(struct il_priv *il); +void il4965_txq_set_sched(struct il_priv *il, u32 mask); + +/* + * Acquire il->lock before calling this function ! + */ +void il4965_set_wr_ptrs(struct il_priv *il, int txq_id, u32 idx); +/** + * il4965_tx_queue_set_status - (optionally) start Tx/Cmd queue + * @tx_fifo_id: Tx DMA/FIFO channel (range 0-7) that the queue will feed + * @scd_retry: (1) Indicates queue will be used in aggregation mode + * + * NOTE: Acquire il->lock before calling this function ! + */ +void il4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq, + int tx_fifo_id, int scd_retry); + +/* scan */ +int il4965_request_scan(struct il_priv *il, struct ieee80211_vif *vif); + +/* station mgmt */ +int il4965_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif, + bool add); + +/* hcmd */ +int il4965_send_beacon_cmd(struct il_priv *il); + +#ifdef CONFIG_IWLEGACY_DEBUG +const char *il4965_get_tx_fail_reason(u32 status); +#else +static inline const char * +il4965_get_tx_fail_reason(u32 status) +{ + return ""; +} +#endif + +/* station management */ +int il4965_alloc_bcast_station(struct il_priv *il); +int il4965_add_bssid_station(struct il_priv *il, const u8 *addr, u8 *sta_id_r); +int il4965_remove_default_wep_key(struct il_priv *il, + struct ieee80211_key_conf *key); +int il4965_set_default_wep_key(struct il_priv *il, + struct ieee80211_key_conf *key); +int il4965_restore_default_wep_keys(struct il_priv *il); +int il4965_set_dynamic_key(struct il_priv *il, + struct ieee80211_key_conf *key, u8 sta_id); +int il4965_remove_dynamic_key(struct il_priv *il, + struct ieee80211_key_conf *key, u8 sta_id); +void il4965_update_tkip_key(struct il_priv *il, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, + u16 *phase1key); +int il4965_sta_tx_modify_enable_tid(struct il_priv *il, int sta_id, int tid); +int il4965_sta_rx_agg_start(struct il_priv *il, struct ieee80211_sta *sta, + int tid, u16 ssn); +int il4965_sta_rx_agg_stop(struct il_priv *il, struct ieee80211_sta *sta, + int tid); +void il4965_sta_modify_sleep_tx_count(struct il_priv *il, int sta_id, int cnt); +int il4965_update_bcast_stations(struct il_priv *il); + +/* rate */ +static inline u8 +il4965_hw_get_rate(__le32 rate_n_flags) +{ + return le32_to_cpu(rate_n_flags) & 0xFF; +} + +/* eeprom */ +void il4965_eeprom_get_mac(const struct il_priv *il, u8 * mac); +int il4965_eeprom_acquire_semaphore(struct il_priv *il); +void il4965_eeprom_release_semaphore(struct il_priv *il); +int il4965_eeprom_check_version(struct il_priv *il); + +/* mac80211 handlers (for 4965) */ +void il4965_mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb); +int il4965_mac_start(struct ieee80211_hw *hw); +void il4965_mac_stop(struct ieee80211_hw *hw); +void il4965_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, u64 multicast); +int il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +void il4965_mac_update_tkip_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, + u16 *phase1key); +int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 * ssn, + u8 buf_size, bool amsdu); +int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void +il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_channel_switch *ch_switch); + +void il4965_led_enable(struct il_priv *il); + +/* EEPROM */ +#define IL4965_EEPROM_IMG_SIZE 1024 + +/* + * uCode queue management definitions ... + * The first queue used for block-ack aggregation is #7 (4965 only). + * All block-ack aggregation queues should map to Tx DMA/FIFO channel 7. + */ +#define IL49_FIRST_AMPDU_QUEUE 7 + +/* Sizes and addresses for instruction and data memory (SRAM) in + * 4965's embedded processor. Driver access is via HBUS_TARG_MEM_* regs. */ +#define IL49_RTC_INST_LOWER_BOUND (0x000000) +#define IL49_RTC_INST_UPPER_BOUND (0x018000) + +#define IL49_RTC_DATA_LOWER_BOUND (0x800000) +#define IL49_RTC_DATA_UPPER_BOUND (0x80A000) + +#define IL49_RTC_INST_SIZE (IL49_RTC_INST_UPPER_BOUND - \ + IL49_RTC_INST_LOWER_BOUND) +#define IL49_RTC_DATA_SIZE (IL49_RTC_DATA_UPPER_BOUND - \ + IL49_RTC_DATA_LOWER_BOUND) + +#define IL49_MAX_INST_SIZE IL49_RTC_INST_SIZE +#define IL49_MAX_DATA_SIZE IL49_RTC_DATA_SIZE + +/* Size of uCode instruction memory in bootstrap state machine */ +#define IL49_MAX_BSM_SIZE BSM_SRAM_SIZE + +static inline int +il4965_hw_valid_rtc_data_addr(u32 addr) +{ + return (addr >= IL49_RTC_DATA_LOWER_BOUND && + addr < IL49_RTC_DATA_UPPER_BOUND); +} + +/********************* START TEMPERATURE *************************************/ + +/** + * 4965 temperature calculation. + * + * The driver must calculate the device temperature before calculating + * a txpower setting (amplifier gain is temperature dependent). The + * calculation uses 4 measurements, 3 of which (R1, R2, R3) are calibration + * values used for the life of the driver, and one of which (R4) is the + * real-time temperature indicator. + * + * uCode provides all 4 values to the driver via the "initialize alive" + * notification (see struct il4965_init_alive_resp). After the runtime uCode + * image loads, uCode updates the R4 value via stats notifications + * (see N_STATS), which occur after each received beacon + * when associated, or can be requested via C_STATS. + * + * NOTE: uCode provides the R4 value as a 23-bit signed value. Driver + * must sign-extend to 32 bits before applying formula below. + * + * Formula: + * + * degrees Kelvin = ((97 * 259 * (R4 - R2) / (R3 - R1)) / 100) + 8 + * + * NOTE: The basic formula is 259 * (R4-R2) / (R3-R1). The 97/100 is + * an additional correction, which should be centered around 0 degrees + * Celsius (273 degrees Kelvin). The 8 (3 percent of 273) compensates for + * centering the 97/100 correction around 0 degrees K. + * + * Add 273 to Kelvin value to find degrees Celsius, for comparing current + * temperature with factory-measured temperatures when calculating txpower + * settings. + */ +#define TEMPERATURE_CALIB_KELVIN_OFFSET 8 +#define TEMPERATURE_CALIB_A_VAL 259 + +/* Limit range of calculated temperature to be between these Kelvin values */ +#define IL_TX_POWER_TEMPERATURE_MIN (263) +#define IL_TX_POWER_TEMPERATURE_MAX (410) + +#define IL_TX_POWER_TEMPERATURE_OUT_OF_RANGE(t) \ + ((t) < IL_TX_POWER_TEMPERATURE_MIN || \ + (t) > IL_TX_POWER_TEMPERATURE_MAX) + +void il4965_temperature_calib(struct il_priv *il); +/********************* END TEMPERATURE ***************************************/ + +/********************* START TXPOWER *****************************************/ + +/** + * 4965 txpower calculations rely on information from three sources: + * + * 1) EEPROM + * 2) "initialize" alive notification + * 3) stats notifications + * + * EEPROM data consists of: + * + * 1) Regulatory information (max txpower and channel usage flags) is provided + * separately for each channel that can possibly supported by 4965. + * 40 MHz wide (.11n HT40) channels are listed separately from 20 MHz + * (legacy) channels. + * + * See struct il4965_eeprom_channel for format, and struct il4965_eeprom + * for locations in EEPROM. + * + * 2) Factory txpower calibration information is provided separately for + * sub-bands of contiguous channels. 2.4GHz has just one sub-band, + * but 5 GHz has several sub-bands. + * + * In addition, per-band (2.4 and 5 Ghz) saturation txpowers are provided. + * + * See struct il4965_eeprom_calib_info (and the tree of structures + * contained within it) for format, and struct il4965_eeprom for + * locations in EEPROM. + * + * "Initialization alive" notification (see struct il4965_init_alive_resp) + * consists of: + * + * 1) Temperature calculation parameters. + * + * 2) Power supply voltage measurement. + * + * 3) Tx gain compensation to balance 2 transmitters for MIMO use. + * + * Statistics notifications deliver: + * + * 1) Current values for temperature param R4. + */ + +/** + * To calculate a txpower setting for a given desired target txpower, channel, + * modulation bit rate, and transmitter chain (4965 has 2 transmitters to + * support MIMO and transmit diversity), driver must do the following: + * + * 1) Compare desired txpower vs. (EEPROM) regulatory limit for this channel. + * Do not exceed regulatory limit; reduce target txpower if necessary. + * + * If setting up txpowers for MIMO rates (rate idxes 8-15, 24-31), + * 2 transmitters will be used simultaneously; driver must reduce the + * regulatory limit by 3 dB (half-power) for each transmitter, so the + * combined total output of the 2 transmitters is within regulatory limits. + * + * + * 2) Compare target txpower vs. (EEPROM) saturation txpower *reduced by + * backoff for this bit rate*. Do not exceed (saturation - backoff[rate]); + * reduce target txpower if necessary. + * + * Backoff values below are in 1/2 dB units (equivalent to steps in + * txpower gain tables): + * + * OFDM 6 - 36 MBit: 10 steps (5 dB) + * OFDM 48 MBit: 15 steps (7.5 dB) + * OFDM 54 MBit: 17 steps (8.5 dB) + * OFDM 60 MBit: 20 steps (10 dB) + * CCK all rates: 10 steps (5 dB) + * + * Backoff values apply to saturation txpower on a per-transmitter basis; + * when using MIMO (2 transmitters), each transmitter uses the same + * saturation level provided in EEPROM, and the same backoff values; + * no reduction (such as with regulatory txpower limits) is required. + * + * Saturation and Backoff values apply equally to 20 Mhz (legacy) channel + * widths and 40 Mhz (.11n HT40) channel widths; there is no separate + * factory measurement for ht40 channels. + * + * The result of this step is the final target txpower. The rest of + * the steps figure out the proper settings for the device to achieve + * that target txpower. + * + * + * 3) Determine (EEPROM) calibration sub band for the target channel, by + * comparing against first and last channels in each sub band + * (see struct il4965_eeprom_calib_subband_info). + * + * + * 4) Linearly interpolate (EEPROM) factory calibration measurement sets, + * referencing the 2 factory-measured (sample) channels within the sub band. + * + * Interpolation is based on difference between target channel's frequency + * and the sample channels' frequencies. Since channel numbers are based + * on frequency (5 MHz between each channel number), this is equivalent + * to interpolating based on channel number differences. + * + * Note that the sample channels may or may not be the channels at the + * edges of the sub band. The target channel may be "outside" of the + * span of the sampled channels. + * + * Driver may choose the pair (for 2 Tx chains) of measurements (see + * struct il4965_eeprom_calib_ch_info) for which the actual measured + * txpower comes closest to the desired txpower. Usually, though, + * the middle set of measurements is closest to the regulatory limits, + * and is therefore a good choice for all txpower calculations (this + * assumes that high accuracy is needed for maximizing legal txpower, + * while lower txpower configurations do not need as much accuracy). + * + * Driver should interpolate both members of the chosen measurement pair, + * i.e. for both Tx chains (radio transmitters), unless the driver knows + * that only one of the chains will be used (e.g. only one tx antenna + * connected, but this should be unusual). The rate scaling algorithm + * switches antennas to find best performance, so both Tx chains will + * be used (although only one at a time) even for non-MIMO transmissions. + * + * Driver should interpolate factory values for temperature, gain table + * idx, and actual power. The power amplifier detector values are + * not used by the driver. + * + * Sanity check: If the target channel happens to be one of the sample + * channels, the results should agree with the sample channel's + * measurements! + * + * + * 5) Find difference between desired txpower and (interpolated) + * factory-measured txpower. Using (interpolated) factory gain table idx + * (shown elsewhere) as a starting point, adjust this idx lower to + * increase txpower, or higher to decrease txpower, until the target + * txpower is reached. Each step in the gain table is 1/2 dB. + * + * For example, if factory measured txpower is 16 dBm, and target txpower + * is 13 dBm, add 6 steps to the factory gain idx to reduce txpower + * by 3 dB. + * + * + * 6) Find difference between current device temperature and (interpolated) + * factory-measured temperature for sub-band. Factory values are in + * degrees Celsius. To calculate current temperature, see comments for + * "4965 temperature calculation". + * + * If current temperature is higher than factory temperature, driver must + * increase gain (lower gain table idx), and vice verse. + * + * Temperature affects gain differently for different channels: + * + * 2.4 GHz all channels: 3.5 degrees per half-dB step + * 5 GHz channels 34-43: 4.5 degrees per half-dB step + * 5 GHz channels >= 44: 4.0 degrees per half-dB step + * + * NOTE: Temperature can increase rapidly when transmitting, especially + * with heavy traffic at high txpowers. Driver should update + * temperature calculations often under these conditions to + * maintain strong txpower in the face of rising temperature. + * + * + * 7) Find difference between current power supply voltage indicator + * (from "initialize alive") and factory-measured power supply voltage + * indicator (EEPROM). + * + * If the current voltage is higher (indicator is lower) than factory + * voltage, gain should be reduced (gain table idx increased) by: + * + * (eeprom - current) / 7 + * + * If the current voltage is lower (indicator is higher) than factory + * voltage, gain should be increased (gain table idx decreased) by: + * + * 2 * (current - eeprom) / 7 + * + * If number of idx steps in either direction turns out to be > 2, + * something is wrong ... just use 0. + * + * NOTE: Voltage compensation is independent of band/channel. + * + * NOTE: "Initialize" uCode measures current voltage, which is assumed + * to be constant after this initial measurement. Voltage + * compensation for txpower (number of steps in gain table) + * may be calculated once and used until the next uCode bootload. + * + * + * 8) If setting up txpowers for MIMO rates (rate idxes 8-15, 24-31), + * adjust txpower for each transmitter chain, so txpower is balanced + * between the two chains. There are 5 pairs of tx_atten[group][chain] + * values in "initialize alive", one pair for each of 5 channel ranges: + * + * Group 0: 5 GHz channel 34-43 + * Group 1: 5 GHz channel 44-70 + * Group 2: 5 GHz channel 71-124 + * Group 3: 5 GHz channel 125-200 + * Group 4: 2.4 GHz all channels + * + * Add the tx_atten[group][chain] value to the idx for the target chain. + * The values are signed, but are in pairs of 0 and a non-negative number, + * so as to reduce gain (if necessary) of the "hotter" channel. This + * avoids any need to double-check for regulatory compliance after + * this step. + * + * + * 9) If setting up for a CCK rate, lower the gain by adding a CCK compensation + * value to the idx: + * + * Hardware rev B: 9 steps (4.5 dB) + * Hardware rev C: 5 steps (2.5 dB) + * + * Hardware rev for 4965 can be determined by reading CSR_HW_REV_WA_REG, + * bits [3:2], 1 = B, 2 = C. + * + * NOTE: This compensation is in addition to any saturation backoff that + * might have been applied in an earlier step. + * + * + * 10) Select the gain table, based on band (2.4 vs 5 GHz). + * + * Limit the adjusted idx to stay within the table! + * + * + * 11) Read gain table entries for DSP and radio gain, place into appropriate + * location(s) in command (struct il4965_txpowertable_cmd). + */ + +/** + * When MIMO is used (2 transmitters operating simultaneously), driver should + * limit each transmitter to deliver a max of 3 dB below the regulatory limit + * for the device. That is, use half power for each transmitter, so total + * txpower is within regulatory limits. + * + * The value "6" represents number of steps in gain table to reduce power 3 dB. + * Each step is 1/2 dB. + */ +#define IL_TX_POWER_MIMO_REGULATORY_COMPENSATION (6) + +/** + * CCK gain compensation. + * + * When calculating txpowers for CCK, after making sure that the target power + * is within regulatory and saturation limits, driver must additionally + * back off gain by adding these values to the gain table idx. + * + * Hardware rev for 4965 can be determined by reading CSR_HW_REV_WA_REG, + * bits [3:2], 1 = B, 2 = C. + */ +#define IL_TX_POWER_CCK_COMPENSATION_B_STEP (9) +#define IL_TX_POWER_CCK_COMPENSATION_C_STEP (5) + +/* + * 4965 power supply voltage compensation for txpower + */ +#define TX_POWER_IL_VOLTAGE_CODES_PER_03V (7) + +/** + * Gain tables. + * + * The following tables contain pair of values for setting txpower, i.e. + * gain settings for the output of the device's digital signal processor (DSP), + * and for the analog gain structure of the transmitter. + * + * Each entry in the gain tables represents a step of 1/2 dB. Note that these + * are *relative* steps, not indications of absolute output power. Output + * power varies with temperature, voltage, and channel frequency, and also + * requires consideration of average power (to satisfy regulatory constraints), + * and peak power (to avoid distortion of the output signal). + * + * Each entry contains two values: + * 1) DSP gain (or sometimes called DSP attenuation). This is a fine-grained + * linear value that multiplies the output of the digital signal processor, + * before being sent to the analog radio. + * 2) Radio gain. This sets the analog gain of the radio Tx path. + * It is a coarser setting, and behaves in a logarithmic (dB) fashion. + * + * EEPROM contains factory calibration data for txpower. This maps actual + * measured txpower levels to gain settings in the "well known" tables + * below ("well-known" means here that both factory calibration *and* the + * driver work with the same table). + * + * There are separate tables for 2.4 GHz and 5 GHz bands. The 5 GHz table + * has an extension (into negative idxes), in case the driver needs to + * boost power setting for high device temperatures (higher than would be + * present during factory calibration). A 5 Ghz EEPROM idx of "40" + * corresponds to the 49th entry in the table used by the driver. + */ +#define MIN_TX_GAIN_IDX (0) /* highest gain, lowest idx, 2.4 */ +#define MIN_TX_GAIN_IDX_52GHZ_EXT (-9) /* highest gain, lowest idx, 5 */ + +/** + * 2.4 GHz gain table + * + * Index Dsp gain Radio gain + * 0 110 0x3f (highest gain) + * 1 104 0x3f + * 2 98 0x3f + * 3 110 0x3e + * 4 104 0x3e + * 5 98 0x3e + * 6 110 0x3d + * 7 104 0x3d + * 8 98 0x3d + * 9 110 0x3c + * 10 104 0x3c + * 11 98 0x3c + * 12 110 0x3b + * 13 104 0x3b + * 14 98 0x3b + * 15 110 0x3a + * 16 104 0x3a + * 17 98 0x3a + * 18 110 0x39 + * 19 104 0x39 + * 20 98 0x39 + * 21 110 0x38 + * 22 104 0x38 + * 23 98 0x38 + * 24 110 0x37 + * 25 104 0x37 + * 26 98 0x37 + * 27 110 0x36 + * 28 104 0x36 + * 29 98 0x36 + * 30 110 0x35 + * 31 104 0x35 + * 32 98 0x35 + * 33 110 0x34 + * 34 104 0x34 + * 35 98 0x34 + * 36 110 0x33 + * 37 104 0x33 + * 38 98 0x33 + * 39 110 0x32 + * 40 104 0x32 + * 41 98 0x32 + * 42 110 0x31 + * 43 104 0x31 + * 44 98 0x31 + * 45 110 0x30 + * 46 104 0x30 + * 47 98 0x30 + * 48 110 0x6 + * 49 104 0x6 + * 50 98 0x6 + * 51 110 0x5 + * 52 104 0x5 + * 53 98 0x5 + * 54 110 0x4 + * 55 104 0x4 + * 56 98 0x4 + * 57 110 0x3 + * 58 104 0x3 + * 59 98 0x3 + * 60 110 0x2 + * 61 104 0x2 + * 62 98 0x2 + * 63 110 0x1 + * 64 104 0x1 + * 65 98 0x1 + * 66 110 0x0 + * 67 104 0x0 + * 68 98 0x0 + * 69 97 0 + * 70 96 0 + * 71 95 0 + * 72 94 0 + * 73 93 0 + * 74 92 0 + * 75 91 0 + * 76 90 0 + * 77 89 0 + * 78 88 0 + * 79 87 0 + * 80 86 0 + * 81 85 0 + * 82 84 0 + * 83 83 0 + * 84 82 0 + * 85 81 0 + * 86 80 0 + * 87 79 0 + * 88 78 0 + * 89 77 0 + * 90 76 0 + * 91 75 0 + * 92 74 0 + * 93 73 0 + * 94 72 0 + * 95 71 0 + * 96 70 0 + * 97 69 0 + * 98 68 0 + */ + +/** + * 5 GHz gain table + * + * Index Dsp gain Radio gain + * -9 123 0x3F (highest gain) + * -8 117 0x3F + * -7 110 0x3F + * -6 104 0x3F + * -5 98 0x3F + * -4 110 0x3E + * -3 104 0x3E + * -2 98 0x3E + * -1 110 0x3D + * 0 104 0x3D + * 1 98 0x3D + * 2 110 0x3C + * 3 104 0x3C + * 4 98 0x3C + * 5 110 0x3B + * 6 104 0x3B + * 7 98 0x3B + * 8 110 0x3A + * 9 104 0x3A + * 10 98 0x3A + * 11 110 0x39 + * 12 104 0x39 + * 13 98 0x39 + * 14 110 0x38 + * 15 104 0x38 + * 16 98 0x38 + * 17 110 0x37 + * 18 104 0x37 + * 19 98 0x37 + * 20 110 0x36 + * 21 104 0x36 + * 22 98 0x36 + * 23 110 0x35 + * 24 104 0x35 + * 25 98 0x35 + * 26 110 0x34 + * 27 104 0x34 + * 28 98 0x34 + * 29 110 0x33 + * 30 104 0x33 + * 31 98 0x33 + * 32 110 0x32 + * 33 104 0x32 + * 34 98 0x32 + * 35 110 0x31 + * 36 104 0x31 + * 37 98 0x31 + * 38 110 0x30 + * 39 104 0x30 + * 40 98 0x30 + * 41 110 0x25 + * 42 104 0x25 + * 43 98 0x25 + * 44 110 0x24 + * 45 104 0x24 + * 46 98 0x24 + * 47 110 0x23 + * 48 104 0x23 + * 49 98 0x23 + * 50 110 0x22 + * 51 104 0x18 + * 52 98 0x18 + * 53 110 0x17 + * 54 104 0x17 + * 55 98 0x17 + * 56 110 0x16 + * 57 104 0x16 + * 58 98 0x16 + * 59 110 0x15 + * 60 104 0x15 + * 61 98 0x15 + * 62 110 0x14 + * 63 104 0x14 + * 64 98 0x14 + * 65 110 0x13 + * 66 104 0x13 + * 67 98 0x13 + * 68 110 0x12 + * 69 104 0x08 + * 70 98 0x08 + * 71 110 0x07 + * 72 104 0x07 + * 73 98 0x07 + * 74 110 0x06 + * 75 104 0x06 + * 76 98 0x06 + * 77 110 0x05 + * 78 104 0x05 + * 79 98 0x05 + * 80 110 0x04 + * 81 104 0x04 + * 82 98 0x04 + * 83 110 0x03 + * 84 104 0x03 + * 85 98 0x03 + * 86 110 0x02 + * 87 104 0x02 + * 88 98 0x02 + * 89 110 0x01 + * 90 104 0x01 + * 91 98 0x01 + * 92 110 0x00 + * 93 104 0x00 + * 94 98 0x00 + * 95 93 0x00 + * 96 88 0x00 + * 97 83 0x00 + * 98 78 0x00 + */ + +/** + * Sanity checks and default values for EEPROM regulatory levels. + * If EEPROM values fall outside MIN/MAX range, use default values. + * + * Regulatory limits refer to the maximum average txpower allowed by + * regulatory agencies in the geographies in which the device is meant + * to be operated. These limits are SKU-specific (i.e. geography-specific), + * and channel-specific; each channel has an individual regulatory limit + * listed in the EEPROM. + * + * Units are in half-dBm (i.e. "34" means 17 dBm). + */ +#define IL_TX_POWER_DEFAULT_REGULATORY_24 (34) +#define IL_TX_POWER_DEFAULT_REGULATORY_52 (34) +#define IL_TX_POWER_REGULATORY_MIN (0) +#define IL_TX_POWER_REGULATORY_MAX (34) + +/** + * Sanity checks and default values for EEPROM saturation levels. + * If EEPROM values fall outside MIN/MAX range, use default values. + * + * Saturation is the highest level that the output power amplifier can produce + * without significant clipping distortion. This is a "peak" power level. + * Different types of modulation (i.e. various "rates", and OFDM vs. CCK) + * require differing amounts of backoff, relative to their average power output, + * in order to avoid clipping distortion. + * + * Driver must make sure that it is violating neither the saturation limit, + * nor the regulatory limit, when calculating Tx power settings for various + * rates. + * + * Units are in half-dBm (i.e. "38" means 19 dBm). + */ +#define IL_TX_POWER_DEFAULT_SATURATION_24 (38) +#define IL_TX_POWER_DEFAULT_SATURATION_52 (38) +#define IL_TX_POWER_SATURATION_MIN (20) +#define IL_TX_POWER_SATURATION_MAX (50) + +/** + * Channel groups used for Tx Attenuation calibration (MIMO tx channel balance) + * and thermal Txpower calibration. + * + * When calculating txpower, driver must compensate for current device + * temperature; higher temperature requires higher gain. Driver must calculate + * current temperature (see "4965 temperature calculation"), then compare vs. + * factory calibration temperature in EEPROM; if current temperature is higher + * than factory temperature, driver must *increase* gain by proportions shown + * in table below. If current temperature is lower than factory, driver must + * *decrease* gain. + * + * Different frequency ranges require different compensation, as shown below. + */ +/* Group 0, 5.2 GHz ch 34-43: 4.5 degrees per 1/2 dB. */ +#define CALIB_IL_TX_ATTEN_GR1_FCH 34 +#define CALIB_IL_TX_ATTEN_GR1_LCH 43 + +/* Group 1, 5.3 GHz ch 44-70: 4.0 degrees per 1/2 dB. */ +#define CALIB_IL_TX_ATTEN_GR2_FCH 44 +#define CALIB_IL_TX_ATTEN_GR2_LCH 70 + +/* Group 2, 5.5 GHz ch 71-124: 4.0 degrees per 1/2 dB. */ +#define CALIB_IL_TX_ATTEN_GR3_FCH 71 +#define CALIB_IL_TX_ATTEN_GR3_LCH 124 + +/* Group 3, 5.7 GHz ch 125-200: 4.0 degrees per 1/2 dB. */ +#define CALIB_IL_TX_ATTEN_GR4_FCH 125 +#define CALIB_IL_TX_ATTEN_GR4_LCH 200 + +/* Group 4, 2.4 GHz all channels: 3.5 degrees per 1/2 dB. */ +#define CALIB_IL_TX_ATTEN_GR5_FCH 1 +#define CALIB_IL_TX_ATTEN_GR5_LCH 20 + +enum { + CALIB_CH_GROUP_1 = 0, + CALIB_CH_GROUP_2 = 1, + CALIB_CH_GROUP_3 = 2, + CALIB_CH_GROUP_4 = 3, + CALIB_CH_GROUP_5 = 4, + CALIB_CH_GROUP_MAX +}; + +/********************* END TXPOWER *****************************************/ + +/** + * Tx/Rx Queues + * + * Most communication between driver and 4965 is via queues of data buffers. + * For example, all commands that the driver issues to device's embedded + * controller (uCode) are via the command queue (one of the Tx queues). All + * uCode command responses/replies/notifications, including Rx frames, are + * conveyed from uCode to driver via the Rx queue. + * + * Most support for these queues, including handshake support, resides in + * structures in host DRAM, shared between the driver and the device. When + * allocating this memory, the driver must make sure that data written by + * the host CPU updates DRAM immediately (and does not get "stuck" in CPU's + * cache memory), so DRAM and cache are consistent, and the device can + * immediately see changes made by the driver. + * + * 4965 supports up to 16 DRAM-based Tx queues, and services these queues via + * up to 7 DMA channels (FIFOs). Each Tx queue is supported by a circular array + * in DRAM containing 256 Transmit Frame Descriptors (TFDs). + */ +#define IL49_NUM_FIFOS 7 +#define IL49_CMD_FIFO_NUM 4 +#define IL49_NUM_QUEUES 16 +#define IL49_NUM_AMPDU_QUEUES 8 + +/** + * struct il4965_schedq_bc_tbl + * + * Byte Count table + * + * Each Tx queue uses a byte-count table containing 320 entries: + * one 16-bit entry for each of 256 TFDs, plus an additional 64 entries that + * duplicate the first 64 entries (to avoid wrap-around within a Tx win; + * max Tx win is 64 TFDs). + * + * When driver sets up a new TFD, it must also enter the total byte count + * of the frame to be transmitted into the corresponding entry in the byte + * count table for the chosen Tx queue. If the TFD idx is 0-63, the driver + * must duplicate the byte count entry in corresponding idx 256-319. + * + * padding puts each byte count table on a 1024-byte boundary; + * 4965 assumes tables are separated by 1024 bytes. + */ +struct il4965_scd_bc_tbl { + __le16 tfd_offset[TFD_QUEUE_BC_SIZE]; + u8 pad[1024 - (TFD_QUEUE_BC_SIZE) * sizeof(__le16)]; +} __packed; + +#define IL4965_RTC_INST_LOWER_BOUND (0x000000) + +/* RSSI to dBm */ +#define IL4965_RSSI_OFFSET 44 + +/* PCI registers */ +#define PCI_CFG_RETRY_TIMEOUT 0x041 + +#define IL4965_DEFAULT_TX_RETRY 15 + +/* EEPROM */ +#define IL4965_FIRST_AMPDU_QUEUE 10 + +/* Calibration */ +void il4965_chain_noise_calibration(struct il_priv *il, void *stat_resp); +void il4965_sensitivity_calibration(struct il_priv *il, void *resp); +void il4965_init_sensitivity(struct il_priv *il); +void il4965_reset_run_time_calib(struct il_priv *il); + +/* Debug */ +#ifdef CONFIG_IWLEGACY_DEBUGFS +extern const struct il_debugfs_ops il4965_debugfs_ops; +#endif + +/****************************/ +/* Flow Handler Definitions */ +/****************************/ + +/** + * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) + * Addresses are offsets from device's PCI hardware base address. + */ +#define FH49_MEM_LOWER_BOUND (0x1000) +#define FH49_MEM_UPPER_BOUND (0x2000) + +/** + * Keep-Warm (KW) buffer base address. + * + * Driver must allocate a 4KByte buffer that is used by 4965 for keeping the + * host DRAM powered on (via dummy accesses to DRAM) to maintain low-latency + * DRAM access when 4965 is Txing or Rxing. The dummy accesses prevent host + * from going into a power-savings mode that would cause higher DRAM latency, + * and possible data over/under-runs, before all Tx/Rx is complete. + * + * Driver loads FH49_KW_MEM_ADDR_REG with the physical address (bits 35:4) + * of the buffer, which must be 4K aligned. Once this is set up, the 4965 + * automatically invokes keep-warm accesses when normal accesses might not + * be sufficient to maintain fast DRAM response. + * + * Bit fields: + * 31-0: Keep-warm buffer physical base address [35:4], must be 4K aligned + */ +#define FH49_KW_MEM_ADDR_REG (FH49_MEM_LOWER_BOUND + 0x97C) + +/** + * TFD Circular Buffers Base (CBBC) addresses + * + * 4965 has 16 base pointer registers, one for each of 16 host-DRAM-resident + * circular buffers (CBs/queues) containing Transmit Frame Descriptors (TFDs) + * (see struct il_tfd_frame). These 16 pointer registers are offset by 0x04 + * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte + * aligned (address bits 0-7 must be 0). + * + * Bit fields in each pointer register: + * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned + */ +#define FH49_MEM_CBBC_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0x9D0) +#define FH49_MEM_CBBC_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xA10) + +/* Find TFD CB base pointer for given queue (range 0-15). */ +#define FH49_MEM_CBBC_QUEUE(x) (FH49_MEM_CBBC_LOWER_BOUND + (x) * 0x4) + +/** + * Rx SRAM Control and Status Registers (RSCSR) + * + * These registers provide handshake between driver and 4965 for the Rx queue + * (this queue handles *all* command responses, notifications, Rx data, etc. + * sent from 4965 uCode to host driver). Unlike Tx, there is only one Rx + * queue, and only one Rx DMA/FIFO channel. Also unlike Tx, which can + * concatenate up to 20 DRAM buffers to form a Tx frame, each Receive Buffer + * Descriptor (RBD) points to only one Rx Buffer (RB); there is a 1:1 + * mapping between RBDs and RBs. + * + * Driver must allocate host DRAM memory for the following, and set the + * physical address of each into 4965 registers: + * + * 1) Receive Buffer Descriptor (RBD) circular buffer (CB), typically with 256 + * entries (although any power of 2, up to 4096, is selectable by driver). + * Each entry (1 dword) points to a receive buffer (RB) of consistent size + * (typically 4K, although 8K or 16K are also selectable by driver). + * Driver sets up RB size and number of RBDs in the CB via Rx config + * register FH49_MEM_RCSR_CHNL0_CONFIG_REG. + * + * Bit fields within one RBD: + * 27-0: Receive Buffer physical address bits [35:8], 256-byte aligned + * + * Driver sets physical address [35:8] of base of RBD circular buffer + * into FH49_RSCSR_CHNL0_RBDCB_BASE_REG [27:0]. + * + * 2) Rx status buffer, 8 bytes, in which 4965 indicates which Rx Buffers + * (RBs) have been filled, via a "write pointer", actually the idx of + * the RB's corresponding RBD within the circular buffer. Driver sets + * physical address [35:4] into FH49_RSCSR_CHNL0_STTS_WPTR_REG [31:0]. + * + * Bit fields in lower dword of Rx status buffer (upper dword not used + * by driver; see struct il4965_shared, val0): + * 31-12: Not used by driver + * 11- 0: Index of last filled Rx buffer descriptor + * (4965 writes, driver reads this value) + * + * As the driver prepares Receive Buffers (RBs) for 4965 to fill, driver must + * enter pointers to these RBs into contiguous RBD circular buffer entries, + * and update the 4965's "write" idx register, + * FH49_RSCSR_CHNL0_RBDCB_WPTR_REG. + * + * This "write" idx corresponds to the *next* RBD that the driver will make + * available, i.e. one RBD past the tail of the ready-to-fill RBDs within + * the circular buffer. This value should initially be 0 (before preparing any + * RBs), should be 8 after preparing the first 8 RBs (for example), and must + * wrap back to 0 at the end of the circular buffer (but don't wrap before + * "read" idx has advanced past 1! See below). + * NOTE: 4965 EXPECTS THE WRITE IDX TO BE INCREMENTED IN MULTIPLES OF 8. + * + * As the 4965 fills RBs (referenced from contiguous RBDs within the circular + * buffer), it updates the Rx status buffer in host DRAM, 2) described above, + * to tell the driver the idx of the latest filled RBD. The driver must + * read this "read" idx from DRAM after receiving an Rx interrupt from 4965. + * + * The driver must also internally keep track of a third idx, which is the + * next RBD to process. When receiving an Rx interrupt, driver should process + * all filled but unprocessed RBs up to, but not including, the RB + * corresponding to the "read" idx. For example, if "read" idx becomes "1", + * driver may process the RB pointed to by RBD 0. Depending on volume of + * traffic, there may be many RBs to process. + * + * If read idx == write idx, 4965 thinks there is no room to put new data. + * Due to this, the maximum number of filled RBs is 255, instead of 256. To + * be safe, make sure that there is a gap of at least 2 RBDs between "write" + * and "read" idxes; that is, make sure that there are no more than 254 + * buffers waiting to be filled. + */ +#define FH49_MEM_RSCSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xBC0) +#define FH49_MEM_RSCSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xC00) +#define FH49_MEM_RSCSR_CHNL0 (FH49_MEM_RSCSR_LOWER_BOUND) + +/** + * Physical base address of 8-byte Rx Status buffer. + * Bit fields: + * 31-0: Rx status buffer physical base address [35:4], must 16-byte aligned. + */ +#define FH49_RSCSR_CHNL0_STTS_WPTR_REG (FH49_MEM_RSCSR_CHNL0) + +/** + * Physical base address of Rx Buffer Descriptor Circular Buffer. + * Bit fields: + * 27-0: RBD CD physical base address [35:8], must be 256-byte aligned. + */ +#define FH49_RSCSR_CHNL0_RBDCB_BASE_REG (FH49_MEM_RSCSR_CHNL0 + 0x004) + +/** + * Rx write pointer (idx, really!). + * Bit fields: + * 11-0: Index of driver's most recent prepared-to-be-filled RBD, + 1. + * NOTE: For 256-entry circular buffer, use only bits [7:0]. + */ +#define FH49_RSCSR_CHNL0_RBDCB_WPTR_REG (FH49_MEM_RSCSR_CHNL0 + 0x008) +#define FH49_RSCSR_CHNL0_WPTR (FH49_RSCSR_CHNL0_RBDCB_WPTR_REG) + +/** + * Rx Config/Status Registers (RCSR) + * Rx Config Reg for channel 0 (only channel used) + * + * Driver must initialize FH49_MEM_RCSR_CHNL0_CONFIG_REG as follows for + * normal operation (see bit fields). + * + * Clearing FH49_MEM_RCSR_CHNL0_CONFIG_REG to 0 turns off Rx DMA. + * Driver should poll FH49_MEM_RSSR_RX_STATUS_REG for + * FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (bit 24) before continuing. + * + * Bit fields: + * 31-30: Rx DMA channel enable: '00' off/pause, '01' pause at end of frame, + * '10' operate normally + * 29-24: reserved + * 23-20: # RBDs in circular buffer = 2^value; use "8" for 256 RBDs (normal), + * min "5" for 32 RBDs, max "12" for 4096 RBDs. + * 19-18: reserved + * 17-16: size of each receive buffer; '00' 4K (normal), '01' 8K, + * '10' 12K, '11' 16K. + * 15-14: reserved + * 13-12: IRQ destination; '00' none, '01' host driver (normal operation) + * 11- 4: timeout for closing Rx buffer and interrupting host (units 32 usec) + * typical value 0x10 (about 1/2 msec) + * 3- 0: reserved + */ +#define FH49_MEM_RCSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xC00) +#define FH49_MEM_RCSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xCC0) +#define FH49_MEM_RCSR_CHNL0 (FH49_MEM_RCSR_LOWER_BOUND) + +#define FH49_MEM_RCSR_CHNL0_CONFIG_REG (FH49_MEM_RCSR_CHNL0) + +#define FH49_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */ +#define FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */ +#define FH49_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK (0x00008000) /* bit 15 */ +#define FH49_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MSK (0x00030000) /* bits 16-17 */ +#define FH49_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MSK (0x00F00000) /* bits 20-23 */ +#define FH49_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MSK (0xC0000000) /* bits 30-31 */ + +#define FH49_RCSR_RX_CONFIG_RBDCB_SIZE_POS (20) +#define FH49_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS (4) +#define RX_RB_TIMEOUT (0x10) + +#define FH49_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000) +#define FH49_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000) +#define FH49_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL (0x80000000) + +#define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000) +#define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000) +#define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000) +#define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000) + +#define FH49_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY (0x00000004) +#define FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000) +#define FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000) + +/** + * Rx Shared Status Registers (RSSR) + * + * After stopping Rx DMA channel (writing 0 to + * FH49_MEM_RCSR_CHNL0_CONFIG_REG), driver must poll + * FH49_MEM_RSSR_RX_STATUS_REG until Rx channel is idle. + * + * Bit fields: + * 24: 1 = Channel 0 is idle + * + * FH49_MEM_RSSR_SHARED_CTRL_REG and FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV + * contain default values that should not be altered by the driver. + */ +#define FH49_MEM_RSSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xC40) +#define FH49_MEM_RSSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xD00) + +#define FH49_MEM_RSSR_SHARED_CTRL_REG (FH49_MEM_RSSR_LOWER_BOUND) +#define FH49_MEM_RSSR_RX_STATUS_REG (FH49_MEM_RSSR_LOWER_BOUND + 0x004) +#define FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV\ + (FH49_MEM_RSSR_LOWER_BOUND + 0x008) + +#define FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) + +#define FH49_MEM_TFDIB_REG1_ADDR_BITSHIFT 28 + +/* TFDB Area - TFDs buffer table */ +#define FH49_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF) +#define FH49_TFDIB_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0x900) +#define FH49_TFDIB_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0x958) +#define FH49_TFDIB_CTRL0_REG(_chnl) (FH49_TFDIB_LOWER_BOUND + 0x8 * (_chnl)) +#define FH49_TFDIB_CTRL1_REG(_chnl) (FH49_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4) + +/** + * Transmit DMA Channel Control/Status Registers (TCSR) + * + * 4965 has one configuration register for each of 8 Tx DMA/FIFO channels + * supported in hardware (don't confuse these with the 16 Tx queues in DRAM, + * which feed the DMA/FIFO channels); config regs are separated by 0x20 bytes. + * + * To use a Tx DMA channel, driver must initialize its + * FH49_TCSR_CHNL_TX_CONFIG_REG(chnl) with: + * + * FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + * FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL + * + * All other bits should be 0. + * + * Bit fields: + * 31-30: Tx DMA channel enable: '00' off/pause, '01' pause at end of frame, + * '10' operate normally + * 29- 4: Reserved, set to "0" + * 3: Enable internal DMA requests (1, normal operation), disable (0) + * 2- 0: Reserved, set to "0" + */ +#define FH49_TCSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xD00) +#define FH49_TCSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xE60) + +/* Find Control/Status reg for given Tx DMA/FIFO channel */ +#define FH49_TCSR_CHNL_NUM (7) +#define FH50_TCSR_CHNL_NUM (8) + +/* TCSR: tx_config register values */ +#define FH49_TCSR_CHNL_TX_CONFIG_REG(_chnl) \ + (FH49_TCSR_LOWER_BOUND + 0x20 * (_chnl)) +#define FH49_TCSR_CHNL_TX_CREDIT_REG(_chnl) \ + (FH49_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x4) +#define FH49_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \ + (FH49_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x8) + +#define FH49_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000) +#define FH49_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRV (0x00000001) + +#define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE (0x00000000) +#define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE (0x00000008) + +#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000) +#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000) +#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000) + +#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000) +#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000) +#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000) + +#define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000) +#define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000) +#define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000) + +#define FH49_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000) +#define FH49_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000) +#define FH49_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003) + +#define FH49_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20) +#define FH49_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12) + +/** + * Tx Shared Status Registers (TSSR) + * + * After stopping Tx DMA channel (writing 0 to + * FH49_TCSR_CHNL_TX_CONFIG_REG(chnl)), driver must poll + * FH49_TSSR_TX_STATUS_REG until selected Tx channel is idle + * (channel's buffers empty | no pending requests). + * + * Bit fields: + * 31-24: 1 = Channel buffers empty (channel 7:0) + * 23-16: 1 = No pending requests (channel 7:0) + */ +#define FH49_TSSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xEA0) +#define FH49_TSSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xEC0) + +#define FH49_TSSR_TX_STATUS_REG (FH49_TSSR_LOWER_BOUND + 0x010) + +/** + * Bit fields for TSSR(Tx Shared Status & Control) error status register: + * 31: Indicates an address error when accessed to internal memory + * uCode/driver must write "1" in order to clear this flag + * 30: Indicates that Host did not send the expected number of dwords to FH + * uCode/driver must write "1" in order to clear this flag + * 16-9:Each status bit is for one channel. Indicates that an (Error) ActDMA + * command was received from the scheduler while the TRB was already full + * with previous command + * uCode/driver must write "1" in order to clear this flag + * 7-0: Each status bit indicates a channel's TxCredit error. When an error + * bit is set, it indicates that the FH has received a full indication + * from the RTC TxFIFO and the current value of the TxCredit counter was + * not equal to zero. This mean that the credit mechanism was not + * synchronized to the TxFIFO status + * uCode/driver must write "1" in order to clear this flag + */ +#define FH49_TSSR_TX_ERROR_REG (FH49_TSSR_LOWER_BOUND + 0x018) + +#define FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16) + +/* Tx service channels */ +#define FH49_SRVC_CHNL (9) +#define FH49_SRVC_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0x9C8) +#define FH49_SRVC_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0x9D0) +#define FH49_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \ + (FH49_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4) + +#define FH49_TX_CHICKEN_BITS_REG (FH49_MEM_LOWER_BOUND + 0xE98) +/* Instruct FH to increment the retry count of a packet when + * it is brought from the memory to TX-FIFO + */ +#define FH49_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002) + +/* Keep Warm Size */ +#define IL_KW_SIZE 0x1000 /* 4k */ + +#endif /* __il_4965_h__ */ diff --git a/drivers/net/wireless/intel/iwlegacy/Kconfig b/drivers/net/wireless/intel/iwlegacy/Kconfig new file mode 100644 index 000000000000..fb919727b8bb --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/Kconfig @@ -0,0 +1,100 @@ +config IWLEGACY + tristate + select FW_LOADER + select NEW_LEDS + select LEDS_CLASS + select LEDS_TRIGGERS + select MAC80211_LEDS + +config IWL4965 + tristate "Intel Wireless WiFi 4965AGN (iwl4965)" + depends on PCI && MAC80211 + select IWLEGACY + ---help--- + This option enables support for + + Select to build the driver supporting the: + + Intel Wireless WiFi Link 4965AGN + + This driver uses the kernel's mac80211 subsystem. + + In order to use this driver, you will need a microcode (uCode) + image for it. You can obtain the microcode from: + + . + + The microcode is typically installed in /lib/firmware. You can + look in the hotplug script /etc/hotplug/firmware.agent to + determine which directory FIRMWARE_DIR is set to when the script + runs. + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The + module will be called iwl4965. + +config IWL3945 + tristate "Intel PRO/Wireless 3945ABG/BG Network Connection (iwl3945)" + depends on PCI && MAC80211 + select IWLEGACY + ---help--- + Select to build the driver supporting the: + + Intel PRO/Wireless 3945ABG/BG Network Connection + + This driver uses the kernel's mac80211 subsystem. + + In order to use this driver, you will need a microcode (uCode) + image for it. You can obtain the microcode from: + + . + + The microcode is typically installed in /lib/firmware. You can + look in the hotplug script /etc/hotplug/firmware.agent to + determine which directory FIRMWARE_DIR is set to when the script + runs. + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The + module will be called iwl3945. + +menu "iwl3945 / iwl4965 Debugging Options" + depends on IWLEGACY + +config IWLEGACY_DEBUG + bool "Enable full debugging output in iwlegacy (iwl 3945/4965) drivers" + depends on IWLEGACY + ---help--- + This option will enable debug tracing output for the iwlegacy + drivers. + + This will result in the kernel module being ~100k larger. You can + control which debug output is sent to the kernel log by setting the + value in + + /sys/class/net/wlan0/device/debug_level + + This entry will only exist if this option is enabled. + + To set a value, simply echo an 8-byte hex value to the same file: + + % echo 0x43fff > /sys/class/net/wlan0/device/debug_level + + You can find the list of debug mask values in: + drivers/net/wireless/iwlegacy/common.h + + If this is your first time using this driver, you should say Y here + as the debug information can assist others in helping you resolve + any problems you may encounter. + +config IWLEGACY_DEBUGFS + bool "iwlegacy (iwl 3945/4965) debugfs support" + depends on IWLEGACY && MAC80211_DEBUGFS + ---help--- + Enable creation of debugfs files for the iwlegacy drivers. This + is a low-impact option that allows getting insight into the + driver's state at runtime. + +endmenu diff --git a/drivers/net/wireless/intel/iwlegacy/Makefile b/drivers/net/wireless/intel/iwlegacy/Makefile new file mode 100644 index 000000000000..c985a01a0731 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/Makefile @@ -0,0 +1,17 @@ +obj-$(CONFIG_IWLEGACY) += iwlegacy.o +iwlegacy-objs := common.o +iwlegacy-$(CONFIG_IWLEGACY_DEBUGFS) += debug.o + +iwlegacy-objs += $(iwlegacy-m) + +# 4965 +obj-$(CONFIG_IWL4965) += iwl4965.o +iwl4965-objs := 4965.o 4965-mac.o 4965-rs.o 4965-calib.o +iwl4965-$(CONFIG_IWLEGACY_DEBUGFS) += 4965-debug.o + +# 3945 +obj-$(CONFIG_IWL3945) += iwl3945.o +iwl3945-objs := 3945-mac.o 3945.o 3945-rs.o +iwl3945-$(CONFIG_IWLEGACY_DEBUGFS) += 3945-debug.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/intel/iwlegacy/commands.h b/drivers/net/wireless/intel/iwlegacy/commands.h new file mode 100644 index 000000000000..dd744135c956 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/commands.h @@ -0,0 +1,3370 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __il_commands_h__ +#define __il_commands_h__ + +#include + +struct il_priv; + +/* uCode version contains 4 values: Major/Minor/API/Serial */ +#define IL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24) +#define IL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16) +#define IL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8) +#define IL_UCODE_SERIAL(ver) ((ver) & 0x000000FF) + +/* Tx rates */ +#define IL_CCK_RATES 4 +#define IL_OFDM_RATES 8 +#define IL_MAX_RATES (IL_CCK_RATES + IL_OFDM_RATES) + +enum { + N_ALIVE = 0x1, + N_ERROR = 0x2, + + /* RXON and QOS commands */ + C_RXON = 0x10, + C_RXON_ASSOC = 0x11, + C_QOS_PARAM = 0x13, + C_RXON_TIMING = 0x14, + + /* Multi-Station support */ + C_ADD_STA = 0x18, + C_REM_STA = 0x19, + + /* Security */ + C_WEPKEY = 0x20, + + /* RX, TX, LEDs */ + N_3945_RX = 0x1b, /* 3945 only */ + C_TX = 0x1c, + C_RATE_SCALE = 0x47, /* 3945 only */ + C_LEDS = 0x48, + C_TX_LINK_QUALITY_CMD = 0x4e, /* for 4965 */ + + /* 802.11h related */ + C_CHANNEL_SWITCH = 0x72, + N_CHANNEL_SWITCH = 0x73, + C_SPECTRUM_MEASUREMENT = 0x74, + N_SPECTRUM_MEASUREMENT = 0x75, + + /* Power Management */ + C_POWER_TBL = 0x77, + N_PM_SLEEP = 0x7A, + N_PM_DEBUG_STATS = 0x7B, + + /* Scan commands and notifications */ + C_SCAN = 0x80, + C_SCAN_ABORT = 0x81, + N_SCAN_START = 0x82, + N_SCAN_RESULTS = 0x83, + N_SCAN_COMPLETE = 0x84, + + /* IBSS/AP commands */ + N_BEACON = 0x90, + C_TX_BEACON = 0x91, + + /* Miscellaneous commands */ + C_TX_PWR_TBL = 0x97, + + /* Bluetooth device coexistence config command */ + C_BT_CONFIG = 0x9b, + + /* Statistics */ + C_STATS = 0x9c, + N_STATS = 0x9d, + + /* RF-KILL commands and notifications */ + N_CARD_STATE = 0xa1, + + /* Missed beacons notification */ + N_MISSED_BEACONS = 0xa2, + + C_CT_KILL_CONFIG = 0xa4, + C_SENSITIVITY = 0xa8, + C_PHY_CALIBRATION = 0xb0, + N_RX_PHY = 0xc0, + N_RX_MPDU = 0xc1, + N_RX = 0xc3, + N_COMPRESSED_BA = 0xc5, + + IL_CN_MAX = 0xff +}; + +/****************************************************************************** + * (0) + * Commonly used structures and definitions: + * Command header, rate_n_flags, txpower + * + *****************************************************************************/ + +/* il_cmd_header flags value */ +#define IL_CMD_FAILED_MSK 0x40 + +#define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f) +#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8) +#define SEQ_TO_IDX(s) ((s) & 0xff) +#define IDX_TO_SEQ(i) ((i) & 0xff) +#define SEQ_HUGE_FRAME cpu_to_le16(0x4000) +#define SEQ_RX_FRAME cpu_to_le16(0x8000) + +/** + * struct il_cmd_header + * + * This header format appears in the beginning of each command sent from the + * driver, and each response/notification received from uCode. + */ +struct il_cmd_header { + u8 cmd; /* Command ID: C_RXON, etc. */ + u8 flags; /* 0:5 reserved, 6 abort, 7 internal */ + /* + * The driver sets up the sequence number to values of its choosing. + * uCode does not use this value, but passes it back to the driver + * when sending the response to each driver-originated command, so + * the driver can match the response to the command. Since the values + * don't get used by uCode, the driver may set up an arbitrary format. + * + * There is one exception: uCode sets bit 15 when it originates + * the response/notification, i.e. when the response/notification + * is not a direct response to a command sent by the driver. For + * example, uCode issues N_3945_RX when it sends a received frame + * to the driver; it is not a direct response to any driver command. + * + * The Linux driver uses the following format: + * + * 0:7 tfd idx - position within TX queue + * 8:12 TX queue id + * 13 reserved + * 14 huge - driver sets this to indicate command is in the + * 'huge' storage at the end of the command buffers + * 15 unsolicited RX or uCode-originated notification + */ + __le16 sequence; + + /* command or response/notification data follows immediately */ + u8 data[0]; +} __packed; + +/** + * struct il3945_tx_power + * + * Used in C_TX_PWR_TBL, C_SCAN, C_CHANNEL_SWITCH + * + * Each entry contains two values: + * 1) DSP gain (or sometimes called DSP attenuation). This is a fine-grained + * linear value that multiplies the output of the digital signal processor, + * before being sent to the analog radio. + * 2) Radio gain. This sets the analog gain of the radio Tx path. + * It is a coarser setting, and behaves in a logarithmic (dB) fashion. + * + * Driver obtains values from struct il3945_tx_power power_gain_table[][]. + */ +struct il3945_tx_power { + u8 tx_gain; /* gain for analog radio */ + u8 dsp_atten; /* gain for DSP */ +} __packed; + +/** + * struct il3945_power_per_rate + * + * Used in C_TX_PWR_TBL, C_CHANNEL_SWITCH + */ +struct il3945_power_per_rate { + u8 rate; /* plcp */ + struct il3945_tx_power tpc; + u8 reserved; +} __packed; + +/** + * iwl4965 rate_n_flags bit fields + * + * rate_n_flags format is used in following iwl4965 commands: + * N_RX (response only) + * N_RX_MPDU (response only) + * C_TX (both command and response) + * C_TX_LINK_QUALITY_CMD + * + * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"): + * 2-0: 0) 6 Mbps + * 1) 12 Mbps + * 2) 18 Mbps + * 3) 24 Mbps + * 4) 36 Mbps + * 5) 48 Mbps + * 6) 54 Mbps + * 7) 60 Mbps + * + * 4-3: 0) Single stream (SISO) + * 1) Dual stream (MIMO) + * 2) Triple stream (MIMO) + * + * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data + * + * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): + * 3-0: 0xD) 6 Mbps + * 0xF) 9 Mbps + * 0x5) 12 Mbps + * 0x7) 18 Mbps + * 0x9) 24 Mbps + * 0xB) 36 Mbps + * 0x1) 48 Mbps + * 0x3) 54 Mbps + * + * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"): + * 6-0: 10) 1 Mbps + * 20) 2 Mbps + * 55) 5.5 Mbps + * 110) 11 Mbps + */ +#define RATE_MCS_CODE_MSK 0x7 +#define RATE_MCS_SPATIAL_POS 3 +#define RATE_MCS_SPATIAL_MSK 0x18 +#define RATE_MCS_HT_DUP_POS 5 +#define RATE_MCS_HT_DUP_MSK 0x20 + +/* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */ +#define RATE_MCS_FLAGS_POS 8 +#define RATE_MCS_HT_POS 8 +#define RATE_MCS_HT_MSK 0x100 + +/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ +#define RATE_MCS_CCK_POS 9 +#define RATE_MCS_CCK_MSK 0x200 + +/* Bit 10: (1) Use Green Field preamble */ +#define RATE_MCS_GF_POS 10 +#define RATE_MCS_GF_MSK 0x400 + +/* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */ +#define RATE_MCS_HT40_POS 11 +#define RATE_MCS_HT40_MSK 0x800 + +/* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */ +#define RATE_MCS_DUP_POS 12 +#define RATE_MCS_DUP_MSK 0x1000 + +/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ +#define RATE_MCS_SGI_POS 13 +#define RATE_MCS_SGI_MSK 0x2000 + +/** + * rate_n_flags Tx antenna masks + * 4965 has 2 transmitters + * bit14:16 + */ +#define RATE_MCS_ANT_POS 14 +#define RATE_MCS_ANT_A_MSK 0x04000 +#define RATE_MCS_ANT_B_MSK 0x08000 +#define RATE_MCS_ANT_C_MSK 0x10000 +#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | RATE_MCS_ANT_B_MSK) +#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | RATE_MCS_ANT_C_MSK) +#define RATE_ANT_NUM 3 + +#define POWER_TBL_NUM_ENTRIES 33 +#define POWER_TBL_NUM_HT_OFDM_ENTRIES 32 +#define POWER_TBL_CCK_ENTRY 32 + +#define IL_PWR_NUM_HT_OFDM_ENTRIES 24 +#define IL_PWR_CCK_ENTRIES 2 + +/** + * union il4965_tx_power_dual_stream + * + * Host format used for C_TX_PWR_TBL, C_CHANNEL_SWITCH + * Use __le32 version (struct tx_power_dual_stream) when building command. + * + * Driver provides radio gain and DSP attenuation settings to device in pairs, + * one value for each transmitter chain. The first value is for transmitter A, + * second for transmitter B. + * + * For SISO bit rates, both values in a pair should be identical. + * For MIMO rates, one value may be different from the other, + * in order to balance the Tx output between the two transmitters. + * + * See more details in doc for TXPOWER in 4965.h. + */ +union il4965_tx_power_dual_stream { + struct { + u8 radio_tx_gain[2]; + u8 dsp_predis_atten[2]; + } s; + u32 dw; +}; + +/** + * struct tx_power_dual_stream + * + * Table entries in C_TX_PWR_TBL, C_CHANNEL_SWITCH + * + * Same format as il_tx_power_dual_stream, but __le32 + */ +struct tx_power_dual_stream { + __le32 dw; +} __packed; + +/** + * struct il4965_tx_power_db + * + * Entire table within C_TX_PWR_TBL, C_CHANNEL_SWITCH + */ +struct il4965_tx_power_db { + struct tx_power_dual_stream power_tbl[POWER_TBL_NUM_ENTRIES]; +} __packed; + +/****************************************************************************** + * (0a) + * Alive and Error Commands & Responses: + * + *****************************************************************************/ + +#define UCODE_VALID_OK cpu_to_le32(0x1) +#define INITIALIZE_SUBTYPE (9) + +/* + * ("Initialize") N_ALIVE = 0x1 (response only, not a command) + * + * uCode issues this "initialize alive" notification once the initialization + * uCode image has completed its work, and is ready to load the runtime image. + * This is the *first* "alive" notification that the driver will receive after + * rebooting uCode; the "initialize" alive is indicated by subtype field == 9. + * + * See comments documenting "BSM" (bootstrap state machine). + * + * For 4965, this notification contains important calibration data for + * calculating txpower settings: + * + * 1) Power supply voltage indication. The voltage sensor outputs higher + * values for lower voltage, and vice verse. + * + * 2) Temperature measurement parameters, for each of two channel widths + * (20 MHz and 40 MHz) supported by the radios. Temperature sensing + * is done via one of the receiver chains, and channel width influences + * the results. + * + * 3) Tx gain compensation to balance 4965's 2 Tx chains for MIMO operation, + * for each of 5 frequency ranges. + */ +struct il_init_alive_resp { + u8 ucode_minor; + u8 ucode_major; + __le16 reserved1; + u8 sw_rev[8]; + u8 ver_type; + u8 ver_subtype; /* "9" for initialize alive */ + __le16 reserved2; + __le32 log_event_table_ptr; + __le32 error_event_table_ptr; + __le32 timestamp; + __le32 is_valid; + + /* calibration values from "initialize" uCode */ + __le32 voltage; /* signed, higher value is lower voltage */ + __le32 therm_r1[2]; /* signed, 1st for normal, 2nd for HT40 */ + __le32 therm_r2[2]; /* signed */ + __le32 therm_r3[2]; /* signed */ + __le32 therm_r4[2]; /* signed */ + __le32 tx_atten[5][2]; /* signed MIMO gain comp, 5 freq groups, + * 2 Tx chains */ +} __packed; + +/** + * N_ALIVE = 0x1 (response only, not a command) + * + * uCode issues this "alive" notification once the runtime image is ready + * to receive commands from the driver. This is the *second* "alive" + * notification that the driver will receive after rebooting uCode; + * this "alive" is indicated by subtype field != 9. + * + * See comments documenting "BSM" (bootstrap state machine). + * + * This response includes two pointers to structures within the device's + * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging: + * + * 1) log_event_table_ptr indicates base of the event log. This traces + * a 256-entry history of uCode execution within a circular buffer. + * Its header format is: + * + * __le32 log_size; log capacity (in number of entries) + * __le32 type; (1) timestamp with each entry, (0) no timestamp + * __le32 wraps; # times uCode has wrapped to top of circular buffer + * __le32 write_idx; next circular buffer entry that uCode would fill + * + * The header is followed by the circular buffer of log entries. Entries + * with timestamps have the following format: + * + * __le32 event_id; range 0 - 1500 + * __le32 timestamp; low 32 bits of TSF (of network, if associated) + * __le32 data; event_id-specific data value + * + * Entries without timestamps contain only event_id and data. + * + * + * 2) error_event_table_ptr indicates base of the error log. This contains + * information about any uCode error that occurs. For 4965, the format + * of the error log is: + * + * __le32 valid; (nonzero) valid, (0) log is empty + * __le32 error_id; type of error + * __le32 pc; program counter + * __le32 blink1; branch link + * __le32 blink2; branch link + * __le32 ilink1; interrupt link + * __le32 ilink2; interrupt link + * __le32 data1; error-specific data + * __le32 data2; error-specific data + * __le32 line; source code line of error + * __le32 bcon_time; beacon timer + * __le32 tsf_low; network timestamp function timer + * __le32 tsf_hi; network timestamp function timer + * __le32 gp1; GP1 timer register + * __le32 gp2; GP2 timer register + * __le32 gp3; GP3 timer register + * __le32 ucode_ver; uCode version + * __le32 hw_ver; HW Silicon version + * __le32 brd_ver; HW board version + * __le32 log_pc; log program counter + * __le32 frame_ptr; frame pointer + * __le32 stack_ptr; stack pointer + * __le32 hcmd; last host command + * __le32 isr0; isr status register LMPM_NIC_ISR0: rxtx_flag + * __le32 isr1; isr status register LMPM_NIC_ISR1: host_flag + * __le32 isr2; isr status register LMPM_NIC_ISR2: enc_flag + * __le32 isr3; isr status register LMPM_NIC_ISR3: time_flag + * __le32 isr4; isr status register LMPM_NIC_ISR4: wico interrupt + * __le32 isr_pref; isr status register LMPM_NIC_PREF_STAT + * __le32 wait_event; wait event() caller address + * __le32 l2p_control; L2pControlField + * __le32 l2p_duration; L2pDurationField + * __le32 l2p_mhvalid; L2pMhValidBits + * __le32 l2p_addr_match; L2pAddrMatchStat + * __le32 lmpm_pmg_sel; indicate which clocks are turned on (LMPM_PMG_SEL) + * __le32 u_timestamp; indicate when the date and time of the compilation + * __le32 reserved; + * + * The Linux driver can print both logs to the system log when a uCode error + * occurs. + */ +struct il_alive_resp { + u8 ucode_minor; + u8 ucode_major; + __le16 reserved1; + u8 sw_rev[8]; + u8 ver_type; + u8 ver_subtype; /* not "9" for runtime alive */ + __le16 reserved2; + __le32 log_event_table_ptr; /* SRAM address for event log */ + __le32 error_event_table_ptr; /* SRAM address for error log */ + __le32 timestamp; + __le32 is_valid; +} __packed; + +/* + * N_ERROR = 0x2 (response only, not a command) + */ +struct il_error_resp { + __le32 error_type; + u8 cmd_id; + u8 reserved1; + __le16 bad_cmd_seq_num; + __le32 error_info; + __le64 timestamp; +} __packed; + +/****************************************************************************** + * (1) + * RXON Commands & Responses: + * + *****************************************************************************/ + +/* + * Rx config defines & structure + */ +/* rx_config device types */ +enum { + RXON_DEV_TYPE_AP = 1, + RXON_DEV_TYPE_ESS = 3, + RXON_DEV_TYPE_IBSS = 4, + RXON_DEV_TYPE_SNIFFER = 6, +}; + +#define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0) +#define RXON_RX_CHAIN_DRIVER_FORCE_POS (0) +#define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1) +#define RXON_RX_CHAIN_VALID_POS (1) +#define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4) +#define RXON_RX_CHAIN_FORCE_SEL_POS (4) +#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7) +#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7) +#define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10) +#define RXON_RX_CHAIN_CNT_POS (10) +#define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12) +#define RXON_RX_CHAIN_MIMO_CNT_POS (12) +#define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14) +#define RXON_RX_CHAIN_MIMO_FORCE_POS (14) + +/* rx_config flags */ +/* band & modulation selection */ +#define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0) +#define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1) +/* auto detection enable */ +#define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2) +/* TGg protection when tx */ +#define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3) +/* cck short slot & preamble */ +#define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4) +#define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5) +/* antenna selection */ +#define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7) +#define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00) +#define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8) +#define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9) +/* radar detection enable */ +#define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12) +#define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13) +/* rx response to host with 8-byte TSF +* (according to ON_AIR deassertion) */ +#define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15) + +/* HT flags */ +#define RXON_FLG_CTRL_CHANNEL_LOC_POS (22) +#define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22) + +#define RXON_FLG_HT_OPERATING_MODE_POS (23) + +#define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23) +#define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23) + +#define RXON_FLG_CHANNEL_MODE_POS (25) +#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) + +/* channel mode */ +enum { + CHANNEL_MODE_LEGACY = 0, + CHANNEL_MODE_PURE_40 = 1, + CHANNEL_MODE_MIXED = 2, + CHANNEL_MODE_RESERVED = 3, +}; +#define RXON_FLG_CHANNEL_MODE_LEGACY \ + cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS) +#define RXON_FLG_CHANNEL_MODE_PURE_40 \ + cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS) +#define RXON_FLG_CHANNEL_MODE_MIXED \ + cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS) + +/* CTS to self (if spec allows) flag */ +#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30) + +/* rx_config filter flags */ +/* accept all data frames */ +#define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0) +/* pass control & management to host */ +#define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1) +/* accept multi-cast */ +#define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2) +/* don't decrypt uni-cast frames */ +#define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3) +/* don't decrypt multi-cast frames */ +#define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4) +/* STA is associated */ +#define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5) +/* transfer to host non bssid beacons in associated state */ +#define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6) + +/** + * C_RXON = 0x10 (command, has simple generic response) + * + * RXON tunes the radio tuner to a service channel, and sets up a number + * of parameters that are used primarily for Rx, but also for Tx operations. + * + * NOTE: When tuning to a new channel, driver must set the + * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent + * info within the device, including the station tables, tx retry + * rate tables, and txpower tables. Driver must build a new station + * table and txpower table before transmitting anything on the RXON + * channel. + * + * NOTE: All RXONs wipe clean the internal txpower table. Driver must + * issue a new C_TX_PWR_TBL after each C_RXON (0x10), + * regardless of whether RXON_FILTER_ASSOC_MSK is set. + */ + +struct il3945_rxon_cmd { + u8 node_addr[6]; + __le16 reserved1; + u8 bssid_addr[6]; + __le16 reserved2; + u8 wlap_bssid_addr[6]; + __le16 reserved3; + u8 dev_type; + u8 air_propagation; + __le16 reserved4; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 assoc_id; + __le32 flags; + __le32 filter_flags; + __le16 channel; + __le16 reserved5; +} __packed; + +struct il4965_rxon_cmd { + u8 node_addr[6]; + __le16 reserved1; + u8 bssid_addr[6]; + __le16 reserved2; + u8 wlap_bssid_addr[6]; + __le16 reserved3; + u8 dev_type; + u8 air_propagation; + __le16 rx_chain; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 assoc_id; + __le32 flags; + __le32 filter_flags; + __le16 channel; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; +} __packed; + +/* Create a common rxon cmd which will be typecast into the 3945 or 4965 + * specific rxon cmd, depending on where it is called from. + */ +struct il_rxon_cmd { + u8 node_addr[6]; + __le16 reserved1; + u8 bssid_addr[6]; + __le16 reserved2; + u8 wlap_bssid_addr[6]; + __le16 reserved3; + u8 dev_type; + u8 air_propagation; + __le16 rx_chain; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 assoc_id; + __le32 flags; + __le32 filter_flags; + __le16 channel; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; + u8 reserved4; + u8 reserved5; +} __packed; + +/* + * C_RXON_ASSOC = 0x11 (command, has simple generic response) + */ +struct il3945_rxon_assoc_cmd { + __le32 flags; + __le32 filter_flags; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 reserved; +} __packed; + +struct il4965_rxon_assoc_cmd { + __le32 flags; + __le32 filter_flags; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; + __le16 rx_chain_select_flags; + __le16 reserved; +} __packed; + +#define IL_CONN_MAX_LISTEN_INTERVAL 10 +#define IL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */ +#define IL39_MAX_UCODE_BEACON_INTERVAL 1 /* 1024 */ + +/* + * C_RXON_TIMING = 0x14 (command, has simple generic response) + */ +struct il_rxon_time_cmd { + __le64 timestamp; + __le16 beacon_interval; + __le16 atim_win; + __le32 beacon_init_val; + __le16 listen_interval; + u8 dtim_period; + u8 delta_cp_bss_tbtts; +} __packed; + +/* + * C_CHANNEL_SWITCH = 0x72 (command, has simple generic response) + */ +struct il3945_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + struct il3945_power_per_rate power[IL_MAX_RATES]; +} __packed; + +struct il4965_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + struct il4965_tx_power_db tx_power; +} __packed; + +/* + * N_CHANNEL_SWITCH = 0x73 (notification only, not a command) + */ +struct il_csa_notification { + __le16 band; + __le16 channel; + __le32 status; /* 0 - OK, 1 - fail */ +} __packed; + +/****************************************************************************** + * (2) + * Quality-of-Service (QOS) Commands & Responses: + * + *****************************************************************************/ + +/** + * struct il_ac_qos -- QOS timing params for C_QOS_PARAM + * One for each of 4 EDCA access categories in struct il_qosparam_cmd + * + * @cw_min: Contention win, start value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x0f. + * @cw_max: Contention win, max value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x3f. + * @aifsn: Number of slots in Arbitration Interframe Space (before + * performing random backoff timing prior to Tx). Device default 1. + * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. + * + * Device will automatically increase contention win by (2*CW) + 1 for each + * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW + * value, to cap the CW value. + */ +struct il_ac_qos { + __le16 cw_min; + __le16 cw_max; + u8 aifsn; + u8 reserved1; + __le16 edca_txop; +} __packed; + +/* QoS flags defines */ +#define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01) +#define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02) +#define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10) + +/* Number of Access Categories (AC) (EDCA), queues 0..3 */ +#define AC_NUM 4 + +/* + * C_QOS_PARAM = 0x13 (command, has simple generic response) + * + * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs + * 0: Background, 1: Best Effort, 2: Video, 3: Voice. + */ +struct il_qosparam_cmd { + __le32 qos_flags; + struct il_ac_qos ac[AC_NUM]; +} __packed; + +/****************************************************************************** + * (3) + * Add/Modify Stations Commands & Responses: + * + *****************************************************************************/ +/* + * Multi station support + */ + +/* Special, dedicated locations within device's station table */ +#define IL_AP_ID 0 +#define IL_STA_ID 2 +#define IL3945_BROADCAST_ID 24 +#define IL3945_STATION_COUNT 25 +#define IL4965_BROADCAST_ID 31 +#define IL4965_STATION_COUNT 32 + +#define IL_STATION_COUNT 32 /* MAX(3945,4965) */ +#define IL_INVALID_STATION 255 + +#define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) +#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) +#define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17) +#define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18) +#define STA_FLG_MAX_AGG_SIZE_POS (19) +#define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19) +#define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21) +#define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22) +#define STA_FLG_AGG_MPDU_DENSITY_POS (23) +#define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23) + +/* Use in mode field. 1: modify existing entry, 0: add new station entry */ +#define STA_CONTROL_MODIFY_MSK 0x01 + +/* key flags __le16*/ +#define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007) +#define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000) +#define STA_KEY_FLG_WEP cpu_to_le16(0x0001) +#define STA_KEY_FLG_CCMP cpu_to_le16(0x0002) +#define STA_KEY_FLG_TKIP cpu_to_le16(0x0003) + +#define STA_KEY_FLG_KEYID_POS 8 +#define STA_KEY_FLG_INVALID cpu_to_le16(0x0800) +/* wep key is either from global key (0) or from station info array (1) */ +#define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008) + +/* wep key in STA: 5-bytes (0) or 13-bytes (1) */ +#define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000) +#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000) +#define STA_KEY_MAX_NUM 8 + +/* Flags indicate whether to modify vs. don't change various station params */ +#define STA_MODIFY_KEY_MASK 0x01 +#define STA_MODIFY_TID_DISABLE_TX 0x02 +#define STA_MODIFY_TX_RATE_MSK 0x04 +#define STA_MODIFY_ADDBA_TID_MSK 0x08 +#define STA_MODIFY_DELBA_TID_MSK 0x10 +#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 + +/* Receiver address (actually, Rx station's idx into station table), + * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ +#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) + +struct il4965_keyinfo { + __le16 key_flags; + u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */ + u8 reserved1; + __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */ + u8 key_offset; + u8 reserved2; + u8 key[16]; /* 16-byte unicast decryption key */ +} __packed; + +/** + * struct sta_id_modify + * @addr[ETH_ALEN]: station's MAC address + * @sta_id: idx of station in uCode's station table + * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change + * + * Driver selects unused table idx when adding new station, + * or the idx to a pre-existing station entry when modifying that station. + * Some idxes have special purposes (IL_AP_ID, idx 0, is for AP). + * + * modify_mask flags select which parameters to modify vs. leave alone. + */ +struct sta_id_modify { + u8 addr[ETH_ALEN]; + __le16 reserved1; + u8 sta_id; + u8 modify_mask; + __le16 reserved2; +} __packed; + +/* + * C_ADD_STA = 0x18 (command) + * + * The device contains an internal table of per-station information, + * with info on security keys, aggregation parameters, and Tx rates for + * initial Tx attempt and any retries (4965 devices uses + * C_TX_LINK_QUALITY_CMD, + * 3945 uses C_RATE_SCALE to set up rate tables). + * + * C_ADD_STA sets up the table entry for one station, either creating + * a new entry, or modifying a pre-existing one. + * + * NOTE: RXON command (without "associated" bit set) wipes the station table + * clean. Moving into RF_KILL state does this also. Driver must set up + * new station table before transmitting anything on the RXON channel + * (except active scans or active measurements; those commands carry + * their own txpower/rate setup data). + * + * When getting started on a new channel, driver must set up the + * IL_BROADCAST_ID entry (last entry in the table). For a client + * station in a BSS, once an AP is selected, driver sets up the AP STA + * in the IL_AP_ID entry (1st entry in the table). BROADCAST and AP + * are all that are needed for a BSS client station. If the device is + * used as AP, or in an IBSS network, driver must set up station table + * entries for all STAs in network, starting with idx IL_STA_ID. + */ + +struct il3945_addsta_cmd { + u8 mode; /* 1: modify existing, 0: add new station */ + u8 reserved[3]; + struct sta_id_modify sta; + struct il4965_keyinfo key; + __le32 station_flags; /* STA_FLG_* */ + __le32 station_flags_msk; /* STA_FLG_* */ + + /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) + * corresponding to bit (e.g. bit 5 controls TID 5). + * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ + __le16 tid_disable_tx; + + __le16 rate_n_flags; + + /* TID for which to add block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + u8 add_immediate_ba_tid; + + /* TID for which to remove block-ack support. + * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ + u8 remove_immediate_ba_tid; + + /* Starting Sequence Number for added block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + __le16 add_immediate_ba_ssn; +} __packed; + +struct il4965_addsta_cmd { + u8 mode; /* 1: modify existing, 0: add new station */ + u8 reserved[3]; + struct sta_id_modify sta; + struct il4965_keyinfo key; + __le32 station_flags; /* STA_FLG_* */ + __le32 station_flags_msk; /* STA_FLG_* */ + + /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) + * corresponding to bit (e.g. bit 5 controls TID 5). + * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ + __le16 tid_disable_tx; + + __le16 reserved1; + + /* TID for which to add block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + u8 add_immediate_ba_tid; + + /* TID for which to remove block-ack support. + * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ + u8 remove_immediate_ba_tid; + + /* Starting Sequence Number for added block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + __le16 add_immediate_ba_ssn; + + /* + * Number of packets OK to transmit to station even though + * it is asleep -- used to synchronise PS-poll and u-APSD + * responses while ucode keeps track of STA sleep state. + */ + __le16 sleep_tx_count; + + __le16 reserved2; +} __packed; + +/* Wrapper struct for 3945 and 4965 addsta_cmd structures */ +struct il_addsta_cmd { + u8 mode; /* 1: modify existing, 0: add new station */ + u8 reserved[3]; + struct sta_id_modify sta; + struct il4965_keyinfo key; + __le32 station_flags; /* STA_FLG_* */ + __le32 station_flags_msk; /* STA_FLG_* */ + + /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) + * corresponding to bit (e.g. bit 5 controls TID 5). + * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ + __le16 tid_disable_tx; + + __le16 rate_n_flags; /* 3945 only */ + + /* TID for which to add block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + u8 add_immediate_ba_tid; + + /* TID for which to remove block-ack support. + * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ + u8 remove_immediate_ba_tid; + + /* Starting Sequence Number for added block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + __le16 add_immediate_ba_ssn; + + /* + * Number of packets OK to transmit to station even though + * it is asleep -- used to synchronise PS-poll and u-APSD + * responses while ucode keeps track of STA sleep state. + */ + __le16 sleep_tx_count; + + __le16 reserved2; +} __packed; + +#define ADD_STA_SUCCESS_MSK 0x1 +#define ADD_STA_NO_ROOM_IN_TBL 0x2 +#define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4 +#define ADD_STA_MODIFY_NON_EXIST_STA 0x8 +/* + * C_ADD_STA = 0x18 (response) + */ +struct il_add_sta_resp { + u8 status; /* ADD_STA_* */ +} __packed; + +#define REM_STA_SUCCESS_MSK 0x1 +/* + * C_REM_STA = 0x19 (response) + */ +struct il_rem_sta_resp { + u8 status; +} __packed; + +/* + * C_REM_STA = 0x19 (command) + */ +struct il_rem_sta_cmd { + u8 num_sta; /* number of removed stations */ + u8 reserved[3]; + u8 addr[ETH_ALEN]; /* MAC addr of the first station */ + u8 reserved2[2]; +} __packed; + +#define IL_TX_FIFO_BK_MSK cpu_to_le32(BIT(0)) +#define IL_TX_FIFO_BE_MSK cpu_to_le32(BIT(1)) +#define IL_TX_FIFO_VI_MSK cpu_to_le32(BIT(2)) +#define IL_TX_FIFO_VO_MSK cpu_to_le32(BIT(3)) +#define IL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00) + +#define IL_DROP_SINGLE 0 +#define IL_DROP_SELECTED 1 +#define IL_DROP_ALL 2 + +/* + * REPLY_WEP_KEY = 0x20 + */ +struct il_wep_key { + u8 key_idx; + u8 key_offset; + u8 reserved1[2]; + u8 key_size; + u8 reserved2[3]; + u8 key[16]; +} __packed; + +struct il_wep_cmd { + u8 num_keys; + u8 global_key_type; + u8 flags; + u8 reserved; + struct il_wep_key key[0]; +} __packed; + +#define WEP_KEY_WEP_TYPE 1 +#define WEP_KEYS_MAX 4 +#define WEP_INVALID_OFFSET 0xff +#define WEP_KEY_LEN_64 5 +#define WEP_KEY_LEN_128 13 + +/****************************************************************************** + * (4) + * Rx Responses: + * + *****************************************************************************/ + +#define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0) +#define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1) + +#define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0) +#define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) +#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) +#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) +#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0x70 +#define RX_RES_PHY_FLAGS_ANTENNA_POS 4 +#define RX_RES_PHY_FLAGS_AGG_MSK cpu_to_le16(1 << 7) + +#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) +#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) +#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8) +#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8) +#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8) +#define RX_RES_STATUS_SEC_TYPE_ERR (0x7 << 8) + +#define RX_RES_STATUS_STATION_FOUND (1<<6) +#define RX_RES_STATUS_NO_STATION_INFO_MISMATCH (1<<7) + +#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11) +#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11) +#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11) +#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11) +#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11) + +#define RX_MPDU_RES_STATUS_ICV_OK (0x20) +#define RX_MPDU_RES_STATUS_MIC_OK (0x40) +#define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7) +#define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800) + +struct il3945_rx_frame_stats { + u8 phy_count; + u8 id; + u8 rssi; + u8 agc; + __le16 sig_avg; + __le16 noise_diff; + u8 payload[0]; +} __packed; + +struct il3945_rx_frame_hdr { + __le16 channel; + __le16 phy_flags; + u8 reserved1; + u8 rate; + __le16 len; + u8 payload[0]; +} __packed; + +struct il3945_rx_frame_end { + __le32 status; + __le64 timestamp; + __le32 beacon_timestamp; +} __packed; + +/* + * N_3945_RX = 0x1b (response only, not a command) + * + * NOTE: DO NOT dereference from casts to this structure + * It is provided only for calculating minimum data set size. + * The actual offsets of the hdr and end are dynamic based on + * stats.phy_count + */ +struct il3945_rx_frame { + struct il3945_rx_frame_stats stats; + struct il3945_rx_frame_hdr hdr; + struct il3945_rx_frame_end end; +} __packed; + +#define IL39_RX_FRAME_SIZE (4 + sizeof(struct il3945_rx_frame)) + +/* Fixed (non-configurable) rx data from phy */ + +#define IL49_RX_RES_PHY_CNT 14 +#define IL49_RX_PHY_FLAGS_ANTENNAE_OFFSET (4) +#define IL49_RX_PHY_FLAGS_ANTENNAE_MASK (0x70) +#define IL49_AGC_DB_MASK (0x3f80) /* MASK(7,13) */ +#define IL49_AGC_DB_POS (7) +struct il4965_rx_non_cfg_phy { + __le16 ant_selection; /* ant A bit 4, ant B bit 5, ant C bit 6 */ + __le16 agc_info; /* agc code 0:6, agc dB 7:13, reserved 14:15 */ + u8 rssi_info[6]; /* we use even entries, 0/2/4 for A/B/C rssi */ + u8 pad[0]; +} __packed; + +/* + * N_RX = 0xc3 (response only, not a command) + * Used only for legacy (non 11n) frames. + */ +struct il_rx_phy_res { + u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */ + u8 cfg_phy_cnt; /* configurable DSP phy data byte count */ + u8 stat_id; /* configurable DSP phy data set ID */ + u8 reserved1; + __le64 timestamp; /* TSF at on air rise */ + __le32 beacon_time_stamp; /* beacon at on-air rise */ + __le16 phy_flags; /* general phy flags: band, modulation, ... */ + __le16 channel; /* channel number */ + u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */ + __le32 rate_n_flags; /* RATE_MCS_* */ + __le16 byte_count; /* frame's byte-count */ + __le16 frame_time; /* frame's time on the air */ +} __packed; + +struct il_rx_mpdu_res_start { + __le16 byte_count; + __le16 reserved; +} __packed; + +/****************************************************************************** + * (5) + * Tx Commands & Responses: + * + * Driver must place each C_TX command into one of the prioritized Tx + * queues in host DRAM, shared between driver and device (see comments for + * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode + * are preparing to transmit, the device pulls the Tx command over the PCI + * bus via one of the device's Tx DMA channels, to fill an internal FIFO + * from which data will be transmitted. + * + * uCode handles all timing and protocol related to control frames + * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler + * handle reception of block-acks; uCode updates the host driver via + * N_COMPRESSED_BA. + * + * uCode handles retrying Tx when an ACK is expected but not received. + * This includes trying lower data rates than the one requested in the Tx + * command, as set up by the C_RATE_SCALE (for 3945) or + * C_TX_LINK_QUALITY_CMD (4965). + * + * Driver sets up transmit power for various rates via C_TX_PWR_TBL. + * This command must be executed after every RXON command, before Tx can occur. + *****************************************************************************/ + +/* C_TX Tx flags field */ + +/* + * 1: Use Request-To-Send protocol before this frame. + * Mutually exclusive vs. TX_CMD_FLG_CTS_MSK. + */ +#define TX_CMD_FLG_RTS_MSK cpu_to_le32(1 << 1) + +/* + * 1: Transmit Clear-To-Send to self before this frame. + * Driver should set this for AUTH/DEAUTH/ASSOC-REQ/REASSOC mgmnt frames. + * Mutually exclusive vs. TX_CMD_FLG_RTS_MSK. + */ +#define TX_CMD_FLG_CTS_MSK cpu_to_le32(1 << 2) + +/* 1: Expect ACK from receiving station + * 0: Don't expect ACK (MAC header's duration field s/b 0) + * Set this for unicast frames, but not broadcast/multicast. */ +#define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3) + +/* For 4965 devices: + * 1: Use rate scale table (see C_TX_LINK_QUALITY_CMD). + * Tx command's initial_rate_idx indicates first rate to try; + * uCode walks through table for additional Tx attempts. + * 0: Use Tx rate/MCS from Tx command's rate_n_flags field. + * This rate will be used for all Tx attempts; it will not be scaled. */ +#define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4) + +/* 1: Expect immediate block-ack. + * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ +#define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6) + +/* + * 1: Frame requires full Tx-Op protection. + * Set this if either RTS or CTS Tx Flag gets set. + */ +#define TX_CMD_FLG_FULL_TXOP_PROT_MSK cpu_to_le32(1 << 7) + +/* Tx antenna selection field; used only for 3945, reserved (0) for 4965 devices. + * Set field to "0" to allow 3945 uCode to select antenna (normal usage). */ +#define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00) +#define TX_CMD_FLG_ANT_A_MSK cpu_to_le32(1 << 8) +#define TX_CMD_FLG_ANT_B_MSK cpu_to_le32(1 << 9) + +/* 1: uCode overrides sequence control field in MAC header. + * 0: Driver provides sequence control field in MAC header. + * Set this for management frames, non-QOS data frames, non-unicast frames, + * and also in Tx command embedded in C_SCAN for active scans. */ +#define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13) + +/* 1: This frame is non-last MPDU; more fragments are coming. + * 0: Last fragment, or not using fragmentation. */ +#define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14) + +/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame. + * 0: No TSF required in outgoing frame. + * Set this for transmitting beacons and probe responses. */ +#define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16) + +/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword + * alignment of frame's payload data field. + * 0: No pad + * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4 + * field (but not both). Driver must align frame data (i.e. data following + * MAC header) to DWORD boundary. */ +#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20) + +/* accelerate aggregation support + * 0 - no CCMP encryption; 1 - CCMP encryption */ +#define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22) + +/* HCCA-AP - disable duration overwriting. */ +#define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25) + +/* + * TX command security control + */ +#define TX_CMD_SEC_WEP 0x01 +#define TX_CMD_SEC_CCM 0x02 +#define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_MSK 0x03 +#define TX_CMD_SEC_SHIFT 6 +#define TX_CMD_SEC_KEY128 0x08 + +/* + * C_TX = 0x1c (command) + */ + +struct il3945_tx_cmd { + /* + * MPDU byte count: + * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * NOTE: Does not include Tx command bytes, post-MAC pad bytes, + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i + * Range: 14-2342 bytes. + */ + __le16 len; + + /* + * MPDU or MSDU byte count for next frame. + * Used for fragmentation and bursting, but not 11n aggregation. + * Same as "len", but for next frame. Set to 0 if not applicable. + */ + __le16 next_frame_len; + + __le32 tx_flags; /* TX_CMD_FLG_* */ + + u8 rate; + + /* Index of recipient station in uCode's station table */ + u8 sta_id; + u8 tid_tspec; + u8 sec_ctl; + u8 key[16]; + union { + u8 byte[8]; + __le16 word[4]; + __le32 dw[2]; + } tkip_mic; + __le32 next_frame_info; + union { + __le32 life_time; + __le32 attempt; + } stop_time; + u8 supp_rates[2]; + u8 rts_retry_limit; /*byte 50 */ + u8 data_retry_limit; /*byte 51 */ + union { + __le16 pm_frame_timeout; + __le16 attempt_duration; + } timeout; + + /* + * Duration of EDCA burst Tx Opportunity, in 32-usec units. + * Set this if txop time is not specified by HCCA protocol (e.g. by AP). + */ + __le16 driver_txop; + + /* + * MAC header goes here, followed by 2 bytes padding if MAC header + * length is 26 or 30 bytes, followed by payload data + */ + u8 payload[0]; + struct ieee80211_hdr hdr[0]; +} __packed; + +/* + * C_TX = 0x1c (response) + */ +struct il3945_tx_resp { + u8 failure_rts; + u8 failure_frame; + u8 bt_kill_count; + u8 rate; + __le32 wireless_media_time; + __le32 status; /* TX status */ +} __packed; + +/* + * 4965 uCode updates these Tx attempt count values in host DRAM. + * Used for managing Tx retries when expecting block-acks. + * Driver should set these fields to 0. + */ +struct il_dram_scratch { + u8 try_cnt; /* Tx attempts */ + u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */ + __le16 reserved; +} __packed; + +struct il_tx_cmd { + /* + * MPDU byte count: + * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * NOTE: Does not include Tx command bytes, post-MAC pad bytes, + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i + * Range: 14-2342 bytes. + */ + __le16 len; + + /* + * MPDU or MSDU byte count for next frame. + * Used for fragmentation and bursting, but not 11n aggregation. + * Same as "len", but for next frame. Set to 0 if not applicable. + */ + __le16 next_frame_len; + + __le32 tx_flags; /* TX_CMD_FLG_* */ + + /* uCode may modify this field of the Tx command (in host DRAM!). + * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ + struct il_dram_scratch scratch; + + /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ + __le32 rate_n_flags; /* RATE_MCS_* */ + + /* Index of destination station in uCode's station table */ + u8 sta_id; + + /* Type of security encryption: CCM or TKIP */ + u8 sec_ctl; /* TX_CMD_SEC_* */ + + /* + * Index into rate table (see C_TX_LINK_QUALITY_CMD) for initial + * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for + * data frames, this field may be used to selectively reduce initial + * rate (via non-0 value) for special frames (e.g. management), while + * still supporting rate scaling for all frames. + */ + u8 initial_rate_idx; + u8 reserved; + u8 key[16]; + __le16 next_frame_flags; + __le16 reserved2; + union { + __le32 life_time; + __le32 attempt; + } stop_time; + + /* Host DRAM physical address pointer to "scratch" in this command. + * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ + __le32 dram_lsb_ptr; + u8 dram_msb_ptr; + + u8 rts_retry_limit; /*byte 50 */ + u8 data_retry_limit; /*byte 51 */ + u8 tid_tspec; + union { + __le16 pm_frame_timeout; + __le16 attempt_duration; + } timeout; + + /* + * Duration of EDCA burst Tx Opportunity, in 32-usec units. + * Set this if txop time is not specified by HCCA protocol (e.g. by AP). + */ + __le16 driver_txop; + + /* + * MAC header goes here, followed by 2 bytes padding if MAC header + * length is 26 or 30 bytes, followed by payload data + */ + u8 payload[0]; + struct ieee80211_hdr hdr[0]; +} __packed; + +/* TX command response is sent after *3945* transmission attempts. + * + * NOTES: + * + * TX_STATUS_FAIL_NEXT_FRAG + * + * If the fragment flag in the MAC header for the frame being transmitted + * is set and there is insufficient time to transmit the next frame, the + * TX status will be returned with 'TX_STATUS_FAIL_NEXT_FRAG'. + * + * TX_STATUS_FIFO_UNDERRUN + * + * Indicates the host did not provide bytes to the FIFO fast enough while + * a TX was in progress. + * + * TX_STATUS_FAIL_MGMNT_ABORT + * + * This status is only possible if the ABORT ON MGMT RX parameter was + * set to true with the TX command. + * + * If the MSB of the status parameter is set then an abort sequence is + * required. This sequence consists of the host activating the TX Abort + * control line, and then waiting for the TX Abort command response. This + * indicates that a the device is no longer in a transmit state, and that the + * command FIFO has been cleared. The host must then deactivate the TX Abort + * control line. Receiving is still allowed in this case. + */ +enum { + TX_3945_STATUS_SUCCESS = 0x01, + TX_3945_STATUS_DIRECT_DONE = 0x02, + TX_3945_STATUS_FAIL_SHORT_LIMIT = 0x82, + TX_3945_STATUS_FAIL_LONG_LIMIT = 0x83, + TX_3945_STATUS_FAIL_FIFO_UNDERRUN = 0x84, + TX_3945_STATUS_FAIL_MGMNT_ABORT = 0x85, + TX_3945_STATUS_FAIL_NEXT_FRAG = 0x86, + TX_3945_STATUS_FAIL_LIFE_EXPIRE = 0x87, + TX_3945_STATUS_FAIL_DEST_PS = 0x88, + TX_3945_STATUS_FAIL_ABORTED = 0x89, + TX_3945_STATUS_FAIL_BT_RETRY = 0x8a, + TX_3945_STATUS_FAIL_STA_INVALID = 0x8b, + TX_3945_STATUS_FAIL_FRAG_DROPPED = 0x8c, + TX_3945_STATUS_FAIL_TID_DISABLE = 0x8d, + TX_3945_STATUS_FAIL_FRAME_FLUSHED = 0x8e, + TX_3945_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, + TX_3945_STATUS_FAIL_TX_LOCKED = 0x90, + TX_3945_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, +}; + +/* + * TX command response is sent after *4965* transmission attempts. + * + * both postpone and abort status are expected behavior from uCode. there is + * no special operation required from driver; except for RFKILL_FLUSH, + * which required tx flush host command to flush all the tx frames in queues + */ +enum { + TX_STATUS_SUCCESS = 0x01, + TX_STATUS_DIRECT_DONE = 0x02, + /* postpone TX */ + TX_STATUS_POSTPONE_DELAY = 0x40, + TX_STATUS_POSTPONE_FEW_BYTES = 0x41, + TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, + TX_STATUS_POSTPONE_CALC_TTAK = 0x44, + /* abort TX */ + TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, + TX_STATUS_FAIL_SHORT_LIMIT = 0x82, + TX_STATUS_FAIL_LONG_LIMIT = 0x83, + TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84, + TX_STATUS_FAIL_DRAIN_FLOW = 0x85, + TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, + TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, + TX_STATUS_FAIL_DEST_PS = 0x88, + TX_STATUS_FAIL_HOST_ABORTED = 0x89, + TX_STATUS_FAIL_BT_RETRY = 0x8a, + TX_STATUS_FAIL_STA_INVALID = 0x8b, + TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, + TX_STATUS_FAIL_TID_DISABLE = 0x8d, + TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, + TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, + TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90, + TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, +}; + +#define TX_PACKET_MODE_REGULAR 0x0000 +#define TX_PACKET_MODE_BURST_SEQ 0x0100 +#define TX_PACKET_MODE_BURST_FIRST 0x0200 + +enum { + TX_POWER_PA_NOT_ACTIVE = 0x0, +}; + +enum { + TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */ + TX_STATUS_DELAY_MSK = 0x00000040, + TX_STATUS_ABORT_MSK = 0x00000080, + TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */ + TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */ + TX_RESERVED = 0x00780000, /* bits 19:22 */ + TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */ + TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */ +}; + +/* ******************************* + * TX aggregation status + ******************************* */ + +enum { + AGG_TX_STATE_TRANSMITTED = 0x00, + AGG_TX_STATE_UNDERRUN_MSK = 0x01, + AGG_TX_STATE_FEW_BYTES_MSK = 0x04, + AGG_TX_STATE_ABORT_MSK = 0x08, + AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10, + AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20, + AGG_TX_STATE_SCD_QUERY_MSK = 0x80, + AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100, + AGG_TX_STATE_RESPONSE_MSK = 0x1ff, + AGG_TX_STATE_DUMP_TX_MSK = 0x200, + AGG_TX_STATE_DELAY_TX_MSK = 0x400 +}; + +#define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */ +#define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */ + +#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \ + AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK) + +/* # tx attempts for first frame in aggregation */ +#define AGG_TX_STATE_TRY_CNT_POS 12 +#define AGG_TX_STATE_TRY_CNT_MSK 0xf000 + +/* Command ID and sequence number of Tx command for this frame */ +#define AGG_TX_STATE_SEQ_NUM_POS 16 +#define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 + +/* + * C_TX = 0x1c (response) + * + * This response may be in one of two slightly different formats, indicated + * by the frame_count field: + * + * 1) No aggregation (frame_count == 1). This reports Tx results for + * a single frame. Multiple attempts, at various bit rates, may have + * been made for this frame. + * + * 2) Aggregation (frame_count > 1). This reports Tx results for + * 2 or more frames that used block-acknowledge. All frames were + * transmitted at same rate. Rate scaling may have been used if first + * frame in this new agg block failed in previous agg block(s). + * + * Note that, for aggregation, ACK (block-ack) status is not delivered here; + * block-ack has not been received by the time the 4965 device records + * this status. + * This status relates to reasons the tx might have been blocked or aborted + * within the sending station (this 4965 device), rather than whether it was + * received successfully by the destination station. + */ +struct agg_tx_status { + __le16 status; + __le16 sequence; +} __packed; + +struct il4965_tx_resp { + u8 frame_count; /* 1 no aggregation, >1 aggregation */ + u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */ + u8 failure_rts; /* # failures due to unsuccessful RTS */ + u8 failure_frame; /* # failures due to no ACK (unused for agg) */ + + /* For non-agg: Rate at which frame was successful. + * For agg: Rate at which all frames were transmitted. */ + __le32 rate_n_flags; /* RATE_MCS_* */ + + /* For non-agg: RTS + CTS + frame tx attempts time + ACK. + * For agg: RTS + CTS + aggregation tx time + block-ack time. */ + __le16 wireless_media_time; /* uSecs */ + + __le16 reserved; + __le32 pa_power1; /* RF power amplifier measurement (not used) */ + __le32 pa_power2; + + /* + * For non-agg: frame status TX_STATUS_* + * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status + * fields follow this one, up to frame_count. + * Bit fields: + * 11- 0: AGG_TX_STATE_* status code + * 15-12: Retry count for 1st frame in aggregation (retries + * occur if tx failed for this frame when it was a + * member of a previous aggregation block). If rate + * scaling is used, retry count indicates the rate + * table entry used for all frames in the new agg. + * 31-16: Sequence # for this frame's Tx cmd (not SSN!) + */ + union { + __le32 status; + struct agg_tx_status agg_status[0]; /* for each agg frame */ + } u; +} __packed; + +/* + * N_COMPRESSED_BA = 0xc5 (response only, not a command) + * + * Reports Block-Acknowledge from recipient station + */ +struct il_compressed_ba_resp { + __le32 sta_addr_lo32; + __le16 sta_addr_hi16; + __le16 reserved; + + /* Index of recipient (BA-sending) station in uCode's station table */ + u8 sta_id; + u8 tid; + __le16 seq_ctl; + __le64 bitmap; + __le16 scd_flow; + __le16 scd_ssn; +} __packed; + +/* + * C_TX_PWR_TBL = 0x97 (command, has simple generic response) + * + * See details under "TXPOWER" in 4965.h. + */ + +struct il3945_txpowertable_cmd { + u8 band; /* 0: 5 GHz, 1: 2.4 GHz */ + u8 reserved; + __le16 channel; + struct il3945_power_per_rate power[IL_MAX_RATES]; +} __packed; + +struct il4965_txpowertable_cmd { + u8 band; /* 0: 5 GHz, 1: 2.4 GHz */ + u8 reserved; + __le16 channel; + struct il4965_tx_power_db tx_power; +} __packed; + +/** + * struct il3945_rate_scaling_cmd - Rate Scaling Command & Response + * + * C_RATE_SCALE = 0x47 (command, has simple generic response) + * + * NOTE: The table of rates passed to the uCode via the + * RATE_SCALE command sets up the corresponding order of + * rates used for all related commands, including rate + * masks, etc. + * + * For example, if you set 9MB (PLCP 0x0f) as the first + * rate in the rate table, the bit mask for that rate + * when passed through ofdm_basic_rates on the C_RXON + * command would be bit 0 (1 << 0) + */ +struct il3945_rate_scaling_info { + __le16 rate_n_flags; + u8 try_cnt; + u8 next_rate_idx; +} __packed; + +struct il3945_rate_scaling_cmd { + u8 table_id; + u8 reserved[3]; + struct il3945_rate_scaling_info table[IL_MAX_RATES]; +} __packed; + +/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */ +#define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1 << 0) + +/* # of EDCA prioritized tx fifos */ +#define LINK_QUAL_AC_NUM AC_NUM + +/* # entries in rate scale table to support Tx retries */ +#define LINK_QUAL_MAX_RETRY_NUM 16 + +/* Tx antenna selection values */ +#define LINK_QUAL_ANT_A_MSK (1 << 0) +#define LINK_QUAL_ANT_B_MSK (1 << 1) +#define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK) + +/** + * struct il_link_qual_general_params + * + * Used in C_TX_LINK_QUALITY_CMD + */ +struct il_link_qual_general_params { + u8 flags; + + /* No entries at or above this (driver chosen) idx contain MIMO */ + u8 mimo_delimiter; + + /* Best single antenna to use for single stream (legacy, SISO). */ + u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */ + + /* Best antennas to use for MIMO (unused for 4965, assumes both). */ + u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */ + + /* + * If driver needs to use different initial rates for different + * EDCA QOS access categories (as implemented by tx fifos 0-3), + * this table will set that up, by indicating the idxes in the + * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start. + * Otherwise, driver should set all entries to 0. + * + * Entry usage: + * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice + * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3. + */ + u8 start_rate_idx[LINK_QUAL_AC_NUM]; +} __packed; + +#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ +#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) +#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) + +#define LINK_QUAL_AGG_DISABLE_START_DEF (3) +#define LINK_QUAL_AGG_DISABLE_START_MAX (255) +#define LINK_QUAL_AGG_DISABLE_START_MIN (0) + +#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (31) +#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) +#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) + +/** + * struct il_link_qual_agg_params + * + * Used in C_TX_LINK_QUALITY_CMD + */ +struct il_link_qual_agg_params { + + /* + *Maximum number of uSec in aggregation. + * default set to 4000 (4 milliseconds) if not configured in .cfg + */ + __le16 agg_time_limit; + + /* + * Number of Tx retries allowed for a frame, before that frame will + * no longer be considered for the start of an aggregation sequence + * (scheduler will then try to tx it as single frame). + * Driver should set this to 3. + */ + u8 agg_dis_start_th; + + /* + * Maximum number of frames in aggregation. + * 0 = no limit (default). 1 = no aggregation. + * Other values = max # frames in aggregation. + */ + u8 agg_frame_cnt_limit; + + __le32 reserved; +} __packed; + +/* + * C_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response) + * + * For 4965 devices only; 3945 uses C_RATE_SCALE. + * + * Each station in the 4965 device's internal station table has its own table + * of 16 + * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when + * an ACK is not received. This command replaces the entire table for + * one station. + * + * NOTE: Station must already be in 4965 device's station table. + * Use C_ADD_STA. + * + * The rate scaling procedures described below work well. Of course, other + * procedures are possible, and may work better for particular environments. + * + * + * FILLING THE RATE TBL + * + * Given a particular initial rate and mode, as determined by the rate + * scaling algorithm described below, the Linux driver uses the following + * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the + * Link Quality command: + * + * + * 1) If using High-throughput (HT) (SISO or MIMO) initial rate: + * a) Use this same initial rate for first 3 entries. + * b) Find next lower available rate using same mode (SISO or MIMO), + * use for next 3 entries. If no lower rate available, switch to + * legacy mode (no HT40 channel, no MIMO, no short guard interval). + * c) If using MIMO, set command's mimo_delimiter to number of entries + * using MIMO (3 or 6). + * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel, + * no MIMO, no short guard interval), at the next lower bit rate + * (e.g. if second HT bit rate was 54, try 48 legacy), and follow + * legacy procedure for remaining table entries. + * + * 2) If using legacy initial rate: + * a) Use the initial rate for only one entry. + * b) For each following entry, reduce the rate to next lower available + * rate, until reaching the lowest available rate. + * c) When reducing rate, also switch antenna selection. + * d) Once lowest available rate is reached, repeat this rate until + * rate table is filled (16 entries), switching antenna each entry. + * + * + * ACCUMULATING HISTORY + * + * The rate scaling algorithm for 4965 devices, as implemented in Linux driver, + * uses two sets of frame Tx success history: One for the current/active + * modulation mode, and one for a speculative/search mode that is being + * attempted. If the speculative mode turns out to be more effective (i.e. + * actual transfer rate is better), then the driver continues to use the + * speculative mode as the new current active mode. + * + * Each history set contains, separately for each possible rate, data for a + * sliding win of the 62 most recent tx attempts at that rate. The data + * includes a shifting bitmap of success(1)/failure(0), and sums of successful + * and attempted frames, from which the driver can additionally calculate a + * success ratio (success / attempted) and number of failures + * (attempted - success), and control the size of the win (attempted). + * The driver uses the bit map to remove successes from the success sum, as + * the oldest tx attempts fall out of the win. + * + * When the 4965 device makes multiple tx attempts for a given frame, each + * attempt might be at a different rate, and have different modulation + * characteristics (e.g. antenna, fat channel, short guard interval), as set + * up in the rate scaling table in the Link Quality command. The driver must + * determine which rate table entry was used for each tx attempt, to determine + * which rate-specific history to update, and record only those attempts that + * match the modulation characteristics of the history set. + * + * When using block-ack (aggregation), all frames are transmitted at the same + * rate, since there is no per-attempt acknowledgment from the destination + * station. The Tx response struct il_tx_resp indicates the Tx rate in + * rate_n_flags field. After receiving a block-ack, the driver can update + * history for the entire block all at once. + * + * + * FINDING BEST STARTING RATE: + * + * When working with a selected initial modulation mode (see below), the + * driver attempts to find a best initial rate. The initial rate is the + * first entry in the Link Quality command's rate table. + * + * 1) Calculate actual throughput (success ratio * expected throughput, see + * table below) for current initial rate. Do this only if enough frames + * have been attempted to make the value meaningful: at least 6 failed + * tx attempts, or at least 8 successes. If not enough, don't try rate + * scaling yet. + * + * 2) Find available rates adjacent to current initial rate. Available means: + * a) supported by hardware && + * b) supported by association && + * c) within any constraints selected by user + * + * 3) Gather measured throughputs for adjacent rates. These might not have + * enough history to calculate a throughput. That's okay, we might try + * using one of them anyway! + * + * 4) Try decreasing rate if, for current rate: + * a) success ratio is < 15% || + * b) lower adjacent rate has better measured throughput || + * c) higher adjacent rate has worse throughput, and lower is unmeasured + * + * As a sanity check, if decrease was determined above, leave rate + * unchanged if: + * a) lower rate unavailable + * b) success ratio at current rate > 85% (very good) + * c) current measured throughput is better than expected throughput + * of lower rate (under perfect 100% tx conditions, see table below) + * + * 5) Try increasing rate if, for current rate: + * a) success ratio is < 15% || + * b) both adjacent rates' throughputs are unmeasured (try it!) || + * b) higher adjacent rate has better measured throughput || + * c) lower adjacent rate has worse throughput, and higher is unmeasured + * + * As a sanity check, if increase was determined above, leave rate + * unchanged if: + * a) success ratio at current rate < 70%. This is not particularly + * good performance; higher rate is sure to have poorer success. + * + * 6) Re-evaluate the rate after each tx frame. If working with block- + * acknowledge, history and stats may be calculated for the entire + * block (including prior history that fits within the history wins), + * before re-evaluation. + * + * FINDING BEST STARTING MODULATION MODE: + * + * After working with a modulation mode for a "while" (and doing rate scaling), + * the driver searches for a new initial mode in an attempt to improve + * throughput. The "while" is measured by numbers of attempted frames: + * + * For legacy mode, search for new mode after: + * 480 successful frames, or 160 failed frames + * For high-throughput modes (SISO or MIMO), search for new mode after: + * 4500 successful frames, or 400 failed frames + * + * Mode switch possibilities are (3 for each mode): + * + * For legacy: + * Change antenna, try SISO (if HT association), try MIMO (if HT association) + * For SISO: + * Change antenna, try MIMO, try shortened guard interval (SGI) + * For MIMO: + * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI) + * + * When trying a new mode, use the same bit rate as the old/current mode when + * trying antenna switches and shortened guard interval. When switching to + * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate + * for which the expected throughput (under perfect conditions) is about the + * same or slightly better than the actual measured throughput delivered by + * the old/current mode. + * + * Actual throughput can be estimated by multiplying the expected throughput + * by the success ratio (successful / attempted tx frames). Frame size is + * not considered in this calculation; it assumes that frame size will average + * out to be fairly consistent over several samples. The following are + * metric values for expected throughput assuming 100% success ratio. + * Only G band has support for CCK rates: + * + * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60 + * + * G: 7 13 35 58 40 57 72 98 121 154 177 186 186 + * A: 0 0 0 0 40 57 72 98 121 154 177 186 186 + * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202 + * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211 + * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251 + * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257 + * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257 + * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264 + * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289 + * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293 + * + * After the new mode has been tried for a short while (minimum of 6 failed + * frames or 8 successful frames), compare success ratio and actual throughput + * estimate of the new mode with the old. If either is better with the new + * mode, continue to use the new mode. + * + * Continue comparing modes until all 3 possibilities have been tried. + * If moving from legacy to HT, try all 3 possibilities from the new HT + * mode. After trying all 3, a best mode is found. Continue to use this mode + * for the longer "while" described above (e.g. 480 successful frames for + * legacy), and then repeat the search process. + * + */ +struct il_link_quality_cmd { + + /* Index of destination/recipient station in uCode's station table */ + u8 sta_id; + u8 reserved1; + __le16 control; /* not used */ + struct il_link_qual_general_params general_params; + struct il_link_qual_agg_params agg_params; + + /* + * Rate info; when using rate-scaling, Tx command's initial_rate_idx + * specifies 1st Tx rate attempted, via idx into this table. + * 4965 devices works its way through table when retrying Tx. + */ + struct { + __le32 rate_n_flags; /* RATE_MCS_*, RATE_* */ + } rs_table[LINK_QUAL_MAX_RETRY_NUM]; + __le32 reserved2; +} __packed; + +/* + * BT configuration enable flags: + * bit 0 - 1: BT channel announcement enabled + * 0: disable + * bit 1 - 1: priority of BT device enabled + * 0: disable + */ +#define BT_COEX_DISABLE (0x0) +#define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0) +#define BT_ENABLE_PRIORITY BIT(1) + +#define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY) + +#define BT_LEAD_TIME_DEF (0x1E) + +#define BT_MAX_KILL_DEF (0x5) + +/* + * C_BT_CONFIG = 0x9b (command, has simple generic response) + * + * 3945 and 4965 devices support hardware handshake with Bluetooth device on + * same platform. Bluetooth device alerts wireless device when it will Tx; + * wireless device can delay or kill its own Tx to accommodate. + */ +struct il_bt_cmd { + u8 flags; + u8 lead_time; + u8 max_kill; + u8 reserved; + __le32 kill_ack_mask; + __le32 kill_cts_mask; +} __packed; + +/****************************************************************************** + * (6) + * Spectrum Management (802.11h) Commands, Responses, Notifications: + * + *****************************************************************************/ + +/* + * Spectrum Management + */ +#define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \ + RXON_FILTER_CTL2HOST_MSK | \ + RXON_FILTER_ACCEPT_GRP_MSK | \ + RXON_FILTER_DIS_DECRYPT_MSK | \ + RXON_FILTER_DIS_GRP_DECRYPT_MSK | \ + RXON_FILTER_ASSOC_MSK | \ + RXON_FILTER_BCON_AWARE_MSK) + +struct il_measure_channel { + __le32 duration; /* measurement duration in extended beacon + * format */ + u8 channel; /* channel to measure */ + u8 type; /* see enum il_measure_type */ + __le16 reserved; +} __packed; + +/* + * C_SPECTRUM_MEASUREMENT = 0x74 (command) + */ +struct il_spectrum_cmd { + __le16 len; /* number of bytes starting from token */ + u8 token; /* token id */ + u8 id; /* measurement id -- 0 or 1 */ + u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */ + u8 periodic; /* 1 = periodic */ + __le16 path_loss_timeout; + __le32 start_time; /* start time in extended beacon format */ + __le32 reserved2; + __le32 flags; /* rxon flags */ + __le32 filter_flags; /* rxon filter flags */ + __le16 channel_count; /* minimum 1, maximum 10 */ + __le16 reserved3; + struct il_measure_channel channels[10]; +} __packed; + +/* + * C_SPECTRUM_MEASUREMENT = 0x74 (response) + */ +struct il_spectrum_resp { + u8 token; + u8 id; /* id of the prior command replaced, or 0xff */ + __le16 status; /* 0 - command will be handled + * 1 - cannot handle (conflicts with another + * measurement) */ +} __packed; + +enum il_measurement_state { + IL_MEASUREMENT_START = 0, + IL_MEASUREMENT_STOP = 1, +}; + +enum il_measurement_status { + IL_MEASUREMENT_OK = 0, + IL_MEASUREMENT_CONCURRENT = 1, + IL_MEASUREMENT_CSA_CONFLICT = 2, + IL_MEASUREMENT_TGH_CONFLICT = 3, + /* 4-5 reserved */ + IL_MEASUREMENT_STOPPED = 6, + IL_MEASUREMENT_TIMEOUT = 7, + IL_MEASUREMENT_PERIODIC_FAILED = 8, +}; + +#define NUM_ELEMENTS_IN_HISTOGRAM 8 + +struct il_measurement_histogram { + __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */ + __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */ +} __packed; + +/* clear channel availability counters */ +struct il_measurement_cca_counters { + __le32 ofdm; + __le32 cck; +} __packed; + +enum il_measure_type { + IL_MEASURE_BASIC = (1 << 0), + IL_MEASURE_CHANNEL_LOAD = (1 << 1), + IL_MEASURE_HISTOGRAM_RPI = (1 << 2), + IL_MEASURE_HISTOGRAM_NOISE = (1 << 3), + IL_MEASURE_FRAME = (1 << 4), + /* bits 5:6 are reserved */ + IL_MEASURE_IDLE = (1 << 7), +}; + +/* + * N_SPECTRUM_MEASUREMENT = 0x75 (notification only, not a command) + */ +struct il_spectrum_notification { + u8 id; /* measurement id -- 0 or 1 */ + u8 token; + u8 channel_idx; /* idx in measurement channel list */ + u8 state; /* 0 - start, 1 - stop */ + __le32 start_time; /* lower 32-bits of TSF */ + u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */ + u8 channel; + u8 type; /* see enum il_measurement_type */ + u8 reserved1; + /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only + * valid if applicable for measurement type requested. */ + __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */ + __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */ + __le32 cca_time; /* channel load time in usecs */ + u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 - + * unidentified */ + u8 reserved2[3]; + struct il_measurement_histogram histogram; + __le32 stop_time; /* lower 32-bits of TSF */ + __le32 status; /* see il_measurement_status */ +} __packed; + +/****************************************************************************** + * (7) + * Power Management Commands, Responses, Notifications: + * + *****************************************************************************/ + +/** + * struct il_powertable_cmd - Power Table Command + * @flags: See below: + * + * C_POWER_TBL = 0x77 (command, has simple generic response) + * + * PM allow: + * bit 0 - '0' Driver not allow power management + * '1' Driver allow PM (use rest of parameters) + * + * uCode send sleep notifications: + * bit 1 - '0' Don't send sleep notification + * '1' send sleep notification (SEND_PM_NOTIFICATION) + * + * Sleep over DTIM + * bit 2 - '0' PM have to walk up every DTIM + * '1' PM could sleep over DTIM till listen Interval. + * + * PCI power managed + * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1) + * '1' !(PCI_CFG_LINK_CTRL & 0x1) + * + * Fast PD + * bit 4 - '1' Put radio to sleep when receiving frame for others + * + * Force sleep Modes + * bit 31/30- '00' use both mac/xtal sleeps + * '01' force Mac sleep + * '10' force xtal sleep + * '11' Illegal set + * + * NOTE: if sleep_interval[SLEEP_INTRVL_TBL_SIZE-1] > DTIM period then + * ucode assume sleep over DTIM is allowed and we don't need to wake up + * for every DTIM. + */ +#define IL_POWER_VEC_SIZE 5 + +#define IL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) +#define IL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2)) +#define IL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) + +struct il3945_powertable_cmd { + __le16 flags; + u8 reserved[2]; + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 sleep_interval[IL_POWER_VEC_SIZE]; +} __packed; + +struct il_powertable_cmd { + __le16 flags; + u8 keep_alive_seconds; /* 3945 reserved */ + u8 debug_flags; /* 3945 reserved */ + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 sleep_interval[IL_POWER_VEC_SIZE]; + __le32 keep_alive_beacons; +} __packed; + +/* + * N_PM_SLEEP = 0x7A (notification only, not a command) + * all devices identical. + */ +struct il_sleep_notification { + u8 pm_sleep_mode; + u8 pm_wakeup_src; + __le16 reserved; + __le32 sleep_time; + __le32 tsf_low; + __le32 bcon_timer; +} __packed; + +/* Sleep states. all devices identical. */ +enum { + IL_PM_NO_SLEEP = 0, + IL_PM_SLP_MAC = 1, + IL_PM_SLP_FULL_MAC_UNASSOCIATE = 2, + IL_PM_SLP_FULL_MAC_CARD_STATE = 3, + IL_PM_SLP_PHY = 4, + IL_PM_SLP_REPENT = 5, + IL_PM_WAKEUP_BY_TIMER = 6, + IL_PM_WAKEUP_BY_DRIVER = 7, + IL_PM_WAKEUP_BY_RFKILL = 8, + /* 3 reserved */ + IL_PM_NUM_OF_MODES = 12, +}; + +/* + * N_CARD_STATE = 0xa1 (notification only, not a command) + */ +struct il_card_state_notif { + __le32 flags; +} __packed; + +#define HW_CARD_DISABLED 0x01 +#define SW_CARD_DISABLED 0x02 +#define CT_CARD_DISABLED 0x04 +#define RXON_CARD_DISABLED 0x10 + +struct il_ct_kill_config { + __le32 reserved; + __le32 critical_temperature_M; + __le32 critical_temperature_R; +} __packed; + +/****************************************************************************** + * (8) + * Scan Commands, Responses, Notifications: + * + *****************************************************************************/ + +#define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0) +#define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1) + +/** + * struct il_scan_channel - entry in C_SCAN channel table + * + * One for each channel in the scan list. + * Each channel can independently select: + * 1) SSID for directed active scans + * 2) Txpower setting (for rate specified within Tx command) + * 3) How long to stay on-channel (behavior may be modified by quiet_time, + * quiet_plcp_th, good_CRC_th) + * + * To avoid uCode errors, make sure the following are true (see comments + * under struct il_scan_cmd about max_out_time and quiet_time): + * 1) If using passive_dwell (i.e. passive_dwell != 0): + * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0) + * 2) quiet_time <= active_dwell + * 3) If restricting off-channel time (i.e. max_out_time !=0): + * passive_dwell < max_out_time + * active_dwell < max_out_time + */ +struct il3945_scan_channel { + /* + * type is defined as: + * 0:0 1 = active, 0 = passive + * 1:4 SSID direct bit map; if a bit is set, then corresponding + * SSID IE is transmitted in probe request. + * 5:7 reserved + */ + u8 type; + u8 channel; /* band is selected by il3945_scan_cmd "flags" field */ + struct il3945_tx_power tpc; + __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ + __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ +} __packed; + +/* set number of direct probes u8 type */ +#define IL39_SCAN_PROBE_MASK(n) ((BIT(n) | (BIT(n) - BIT(1)))) + +struct il_scan_channel { + /* + * type is defined as: + * 0:0 1 = active, 0 = passive + * 1:20 SSID direct bit map; if a bit is set, then corresponding + * SSID IE is transmitted in probe request. + * 21:31 reserved + */ + __le32 type; + __le16 channel; /* band is selected by il_scan_cmd "flags" field */ + u8 tx_gain; /* gain for analog radio */ + u8 dsp_atten; /* gain for DSP */ + __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ + __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ +} __packed; + +/* set number of direct probes __le32 type */ +#define IL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1)))) + +/** + * struct il_ssid_ie - directed scan network information element + * + * Up to 20 of these may appear in C_SCAN (Note: Only 4 are in + * 3945 SCAN api), selected by "type" bit field in struct il_scan_channel; + * each channel may select different ssids from among the 20 (4) entries. + * SSID IEs get transmitted in reverse order of entry. + */ +struct il_ssid_ie { + u8 id; + u8 len; + u8 ssid[32]; +} __packed; + +#define PROBE_OPTION_MAX_3945 4 +#define PROBE_OPTION_MAX 20 +#define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF) +#define IL_GOOD_CRC_TH_DISABLED 0 +#define IL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) +#define IL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff) +#define IL_MAX_SCAN_SIZE 1024 +#define IL_MAX_CMD_SIZE 4096 + +/* + * C_SCAN = 0x80 (command) + * + * The hardware scan command is very powerful; the driver can set it up to + * maintain (relatively) normal network traffic while doing a scan in the + * background. The max_out_time and suspend_time control the ratio of how + * long the device stays on an associated network channel ("service channel") + * vs. how long it's away from the service channel, i.e. tuned to other channels + * for scanning. + * + * max_out_time is the max time off-channel (in usec), and suspend_time + * is how long (in "extended beacon" format) that the scan is "suspended" + * after returning to the service channel. That is, suspend_time is the + * time that we stay on the service channel, doing normal work, between + * scan segments. The driver may set these parameters differently to support + * scanning when associated vs. not associated, and light vs. heavy traffic + * loads when associated. + * + * After receiving this command, the device's scan engine does the following; + * + * 1) Sends SCAN_START notification to driver + * 2) Checks to see if it has time to do scan for one channel + * 3) Sends NULL packet, with power-save (PS) bit set to 1, + * to tell AP that we're going off-channel + * 4) Tunes to first channel in scan list, does active or passive scan + * 5) Sends SCAN_RESULT notification to driver + * 6) Checks to see if it has time to do scan on *next* channel in list + * 7) Repeats 4-6 until it no longer has time to scan the next channel + * before max_out_time expires + * 8) Returns to service channel + * 9) Sends NULL packet with PS=0 to tell AP that we're back + * 10) Stays on service channel until suspend_time expires + * 11) Repeats entire process 2-10 until list is complete + * 12) Sends SCAN_COMPLETE notification + * + * For fast, efficient scans, the scan command also has support for staying on + * a channel for just a short time, if doing active scanning and getting no + * responses to the transmitted probe request. This time is controlled by + * quiet_time, and the number of received packets below which a channel is + * considered "quiet" is controlled by quiet_plcp_threshold. + * + * For active scanning on channels that have regulatory restrictions against + * blindly transmitting, the scan can listen before transmitting, to make sure + * that there is already legitimate activity on the channel. If enough + * packets are cleanly received on the channel (controlled by good_CRC_th, + * typical value 1), the scan engine starts transmitting probe requests. + * + * Driver must use separate scan commands for 2.4 vs. 5 GHz bands. + * + * To avoid uCode errors, see timing restrictions described under + * struct il_scan_channel. + */ + +struct il3945_scan_cmd { + __le16 len; + u8 reserved0; + u8 channel_count; /* # channels in channel list */ + __le16 quiet_time; /* dwell only this # millisecs on quiet channel + * (only for active scan) */ + __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ + __le16 good_CRC_th; /* passive -> active promotion threshold */ + __le16 reserved1; + __le32 max_out_time; /* max usec to be away from associated (service) + * channel */ + __le32 suspend_time; /* pause scan this long (in "extended beacon + * format") when returning to service channel: + * 3945; 31:24 # beacons, 19:0 additional usec, + * 4965; 31:22 # beacons, 21:0 additional usec. + */ + __le32 flags; /* RXON_FLG_* */ + __le32 filter_flags; /* RXON_FILTER_* */ + + /* For active scans (set to all-0s for passive scans). + * Does not include payload. Must specify Tx rate; no rate scaling. */ + struct il3945_tx_cmd tx_cmd; + + /* For directed active scans (set to all-0s otherwise) */ + struct il_ssid_ie direct_scan[PROBE_OPTION_MAX_3945]; + + /* + * Probe request frame, followed by channel list. + * + * Size of probe request frame is specified by byte count in tx_cmd. + * Channel list follows immediately after probe request frame. + * Number of channels in list is specified by channel_count. + * Each channel in list is of type: + * + * struct il3945_scan_channel channels[0]; + * + * NOTE: Only one band of channels can be scanned per pass. You + * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait + * for one scan to complete (i.e. receive N_SCAN_COMPLETE) + * before requesting another scan. + */ + u8 data[0]; +} __packed; + +struct il_scan_cmd { + __le16 len; + u8 reserved0; + u8 channel_count; /* # channels in channel list */ + __le16 quiet_time; /* dwell only this # millisecs on quiet channel + * (only for active scan) */ + __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ + __le16 good_CRC_th; /* passive -> active promotion threshold */ + __le16 rx_chain; /* RXON_RX_CHAIN_* */ + __le32 max_out_time; /* max usec to be away from associated (service) + * channel */ + __le32 suspend_time; /* pause scan this long (in "extended beacon + * format") when returning to service chnl: + * 3945; 31:24 # beacons, 19:0 additional usec, + * 4965; 31:22 # beacons, 21:0 additional usec. + */ + __le32 flags; /* RXON_FLG_* */ + __le32 filter_flags; /* RXON_FILTER_* */ + + /* For active scans (set to all-0s for passive scans). + * Does not include payload. Must specify Tx rate; no rate scaling. */ + struct il_tx_cmd tx_cmd; + + /* For directed active scans (set to all-0s otherwise) */ + struct il_ssid_ie direct_scan[PROBE_OPTION_MAX]; + + /* + * Probe request frame, followed by channel list. + * + * Size of probe request frame is specified by byte count in tx_cmd. + * Channel list follows immediately after probe request frame. + * Number of channels in list is specified by channel_count. + * Each channel in list is of type: + * + * struct il_scan_channel channels[0]; + * + * NOTE: Only one band of channels can be scanned per pass. You + * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait + * for one scan to complete (i.e. receive N_SCAN_COMPLETE) + * before requesting another scan. + */ + u8 data[0]; +} __packed; + +/* Can abort will notify by complete notification with abort status. */ +#define CAN_ABORT_STATUS cpu_to_le32(0x1) +/* complete notification statuses */ +#define ABORT_STATUS 0x2 + +/* + * C_SCAN = 0x80 (response) + */ +struct il_scanreq_notification { + __le32 status; /* 1: okay, 2: cannot fulfill request */ +} __packed; + +/* + * N_SCAN_START = 0x82 (notification only, not a command) + */ +struct il_scanstart_notification { + __le32 tsf_low; + __le32 tsf_high; + __le32 beacon_timer; + u8 channel; + u8 band; + u8 reserved[2]; + __le32 status; +} __packed; + +#define SCAN_OWNER_STATUS 0x1 +#define MEASURE_OWNER_STATUS 0x2 + +#define IL_PROBE_STATUS_OK 0 +#define IL_PROBE_STATUS_TX_FAILED BIT(0) +/* error statuses combined with TX_FAILED */ +#define IL_PROBE_STATUS_FAIL_TTL BIT(1) +#define IL_PROBE_STATUS_FAIL_BT BIT(2) + +#define NUMBER_OF_STATS 1 /* first __le32 is good CRC */ +/* + * N_SCAN_RESULTS = 0x83 (notification only, not a command) + */ +struct il_scanresults_notification { + u8 channel; + u8 band; + u8 probe_status; + u8 num_probe_not_sent; /* not enough time to send */ + __le32 tsf_low; + __le32 tsf_high; + __le32 stats[NUMBER_OF_STATS]; +} __packed; + +/* + * N_SCAN_COMPLETE = 0x84 (notification only, not a command) + */ +struct il_scancomplete_notification { + u8 scanned_channels; + u8 status; + u8 last_channel; + __le32 tsf_low; + __le32 tsf_high; +} __packed; + +/****************************************************************************** + * (9) + * IBSS/AP Commands and Notifications: + * + *****************************************************************************/ + +enum il_ibss_manager { + IL_NOT_IBSS_MANAGER = 0, + IL_IBSS_MANAGER = 1, +}; + +/* + * N_BEACON = 0x90 (notification only, not a command) + */ + +struct il3945_beacon_notif { + struct il3945_tx_resp beacon_notify_hdr; + __le32 low_tsf; + __le32 high_tsf; + __le32 ibss_mgr_status; +} __packed; + +struct il4965_beacon_notif { + struct il4965_tx_resp beacon_notify_hdr; + __le32 low_tsf; + __le32 high_tsf; + __le32 ibss_mgr_status; +} __packed; + +/* + * C_TX_BEACON= 0x91 (command, has simple generic response) + */ + +struct il3945_tx_beacon_cmd { + struct il3945_tx_cmd tx; + __le16 tim_idx; + u8 tim_size; + u8 reserved1; + struct ieee80211_hdr frame[0]; /* beacon frame */ +} __packed; + +struct il_tx_beacon_cmd { + struct il_tx_cmd tx; + __le16 tim_idx; + u8 tim_size; + u8 reserved1; + struct ieee80211_hdr frame[0]; /* beacon frame */ +} __packed; + +/****************************************************************************** + * (10) + * Statistics Commands and Notifications: + * + *****************************************************************************/ + +#define IL_TEMP_CONVERT 260 + +#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 +#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 +#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 + +/* Used for passing to driver number of successes and failures per rate */ +struct rate_histogram { + union { + __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } success; + union { + __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } failed; +} __packed; + +/* stats command response */ + +struct iwl39_stats_rx_phy { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_limit_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; +} __packed; + +struct iwl39_stats_rx_non_phy { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ +} __packed; + +struct iwl39_stats_rx { + struct iwl39_stats_rx_phy ofdm; + struct iwl39_stats_rx_phy cck; + struct iwl39_stats_rx_non_phy general; +} __packed; + +struct iwl39_stats_tx { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; +} __packed; + +struct stats_dbg { + __le32 burst_check; + __le32 burst_count; + __le32 wait_for_silence_timeout_cnt; + __le32 reserved[3]; +} __packed; + +struct iwl39_stats_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; +} __packed; + +struct iwl39_stats_general { + __le32 temperature; + struct stats_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct iwl39_stats_div div; +} __packed; + +struct stats_rx_phy { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_limit_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 mh_format_err; + __le32 re_acq_main_rssi_sum; + __le32 reserved3; +} __packed; + +struct stats_rx_ht_phy { + __le32 plcp_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 crc32_err; + __le32 mh_format_err; + __le32 agg_crc32_good; + __le32 agg_mpdu_cnt; + __le32 agg_cnt; + __le32 unsupport_mcs; +} __packed; + +#define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1) + +struct stats_rx_non_phy { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ + __le32 channel_beacons; /* beacons with our bss id and in our + * serving channel */ + __le32 num_missed_bcon; /* number of missed beacons */ + __le32 adc_rx_saturation_time; /* count in 0.8us units the time the + * ADC was in saturation */ + __le32 ina_detection_search_time; /* total time (in 0.8us) searched + * for INA */ + __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ + __le32 interference_data_flag; /* flag for interference data + * availability. 1 when data is + * available. */ + __le32 channel_load; /* counts RX Enable time in uSec */ + __le32 dsp_false_alarms; /* DSP false alarm (both OFDM + * and CCK) counter */ + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 beacon_rssi_c; + __le32 beacon_energy_a; + __le32 beacon_energy_b; + __le32 beacon_energy_c; +} __packed; + +struct stats_rx { + struct stats_rx_phy ofdm; + struct stats_rx_phy cck; + struct stats_rx_non_phy general; + struct stats_rx_ht_phy ofdm_ht; +} __packed; + +/** + * struct stats_tx_power - current tx power + * + * @ant_a: current tx power on chain a in 1/2 dB step + * @ant_b: current tx power on chain b in 1/2 dB step + * @ant_c: current tx power on chain c in 1/2 dB step + */ +struct stats_tx_power { + u8 ant_a; + u8 ant_b; + u8 ant_c; + u8 reserved; +} __packed; + +struct stats_tx_non_phy_agg { + __le32 ba_timeout; + __le32 ba_reschedule_frames; + __le32 scd_query_agg_frame_cnt; + __le32 scd_query_no_agg; + __le32 scd_query_agg; + __le32 scd_query_mismatch; + __le32 frame_not_ready; + __le32 underrun; + __le32 bt_prio_kill; + __le32 rx_ba_rsp_cnt; +} __packed; + +struct stats_tx { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; + __le32 dump_msdu_cnt; + __le32 burst_abort_next_frame_mismatch_cnt; + __le32 burst_abort_missing_next_frame_cnt; + __le32 cts_timeout_collision; + __le32 ack_or_ba_timeout_collision; + struct stats_tx_non_phy_agg agg; + + __le32 reserved1; +} __packed; + +struct stats_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; + __le32 reserved1; + __le32 reserved2; +} __packed; + +struct stats_general_common { + __le32 temperature; /* radio temperature */ + struct stats_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct stats_div div; + __le32 rx_enable_counter; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; +} __packed; + +struct stats_general { + struct stats_general_common common; + __le32 reserved2; + __le32 reserved3; +} __packed; + +#define UCODE_STATS_CLEAR_MSK (0x1 << 0) +#define UCODE_STATS_FREQUENCY_MSK (0x1 << 1) +#define UCODE_STATS_NARROW_BAND_MSK (0x1 << 2) + +/* + * C_STATS = 0x9c, + * all devices identical. + * + * This command triggers an immediate response containing uCode stats. + * The response is in the same format as N_STATS 0x9d, below. + * + * If the CLEAR_STATS configuration flag is set, uCode will clear its + * internal copy of the stats (counters) after issuing the response. + * This flag does not affect N_STATSs after beacons (see below). + * + * If the DISABLE_NOTIF configuration flag is set, uCode will not issue + * N_STATSs after received beacons (see below). This flag + * does not affect the response to the C_STATS 0x9c itself. + */ +#define IL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */ +#define IL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2) /* see above */ +struct il_stats_cmd { + __le32 configuration_flags; /* IL_STATS_CONF_* */ +} __packed; + +/* + * N_STATS = 0x9d (notification only, not a command) + * + * By default, uCode issues this notification after receiving a beacon + * while associated. To disable this behavior, set DISABLE_NOTIF flag in the + * C_STATS 0x9c, above. + * + * Statistics counters continue to increment beacon after beacon, but are + * cleared when changing channels or when driver issues C_STATS + * 0x9c with CLEAR_STATS bit set (see above). + * + * uCode also issues this notification during scans. uCode clears stats + * appropriately so that each notification contains stats for only the + * one channel that has just been scanned. + */ +#define STATS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2) +#define STATS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8) + +struct il3945_notif_stats { + __le32 flag; + struct iwl39_stats_rx rx; + struct iwl39_stats_tx tx; + struct iwl39_stats_general general; +} __packed; + +struct il_notif_stats { + __le32 flag; + struct stats_rx rx; + struct stats_tx tx; + struct stats_general general; +} __packed; + +/* + * N_MISSED_BEACONS = 0xa2 (notification only, not a command) + * + * uCode send N_MISSED_BEACONS to driver when detect beacon missed + * in regardless of how many missed beacons, which mean when driver receive the + * notification, inside the command, it can find all the beacons information + * which include number of total missed beacons, number of consecutive missed + * beacons, number of beacons received and number of beacons expected to + * receive. + * + * If uCode detected consecutive_missed_beacons > 5, it will reset the radio + * in order to bring the radio/PHY back to working state; which has no relation + * to when driver will perform sensitivity calibration. + * + * Driver should set it own missed_beacon_threshold to decide when to perform + * sensitivity calibration based on number of consecutive missed beacons in + * order to improve overall performance, especially in noisy environment. + * + */ + +#define IL_MISSED_BEACON_THRESHOLD_MIN (1) +#define IL_MISSED_BEACON_THRESHOLD_DEF (5) +#define IL_MISSED_BEACON_THRESHOLD_MAX IL_MISSED_BEACON_THRESHOLD_DEF + +struct il_missed_beacon_notif { + __le32 consecutive_missed_beacons; + __le32 total_missed_becons; + __le32 num_expected_beacons; + __le32 num_recvd_beacons; +} __packed; + +/****************************************************************************** + * (11) + * Rx Calibration Commands: + * + * With the uCode used for open source drivers, most Tx calibration (except + * for Tx Power) and most Rx calibration is done by uCode during the + * "initialize" phase of uCode boot. Driver must calibrate only: + * + * 1) Tx power (depends on temperature), described elsewhere + * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas) + * 3) Receiver sensitivity (to optimize signal detection) + * + *****************************************************************************/ + +/** + * C_SENSITIVITY = 0xa8 (command, has simple generic response) + * + * This command sets up the Rx signal detector for a sensitivity level that + * is high enough to lock onto all signals within the associated network, + * but low enough to ignore signals that are below a certain threshold, so as + * not to have too many "false alarms". False alarms are signals that the + * Rx DSP tries to lock onto, but then discards after determining that they + * are noise. + * + * The optimum number of false alarms is between 5 and 50 per 200 TUs + * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e. + * time listening, not transmitting). Driver must adjust sensitivity so that + * the ratio of actual false alarms to actual Rx time falls within this range. + * + * While associated, uCode delivers N_STATSs after each + * received beacon. These provide information to the driver to analyze the + * sensitivity. Don't analyze stats that come in from scanning, or any + * other non-associated-network source. Pertinent stats include: + * + * From "general" stats (struct stats_rx_non_phy): + * + * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level) + * Measure of energy of desired signal. Used for establishing a level + * below which the device does not detect signals. + * + * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB) + * Measure of background noise in silent period after beacon. + * + * channel_load + * uSecs of actual Rx time during beacon period (varies according to + * how much time was spent transmitting). + * + * From "cck" and "ofdm" stats (struct stats_rx_phy), separately: + * + * false_alarm_cnt + * Signal locks abandoned early (before phy-level header). + * + * plcp_err + * Signal locks abandoned late (during phy-level header). + * + * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from + * beacon to beacon, i.e. each value is an accumulation of all errors + * before and including the latest beacon. Values will wrap around to 0 + * after counting up to 2^32 - 1. Driver must differentiate vs. + * previous beacon's values to determine # false alarms in the current + * beacon period. + * + * Total number of false alarms = false_alarms + plcp_errs + * + * For OFDM, adjust the following table entries in struct il_sensitivity_cmd + * (notice that the start points for OFDM are at or close to settings for + * maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX 90 / 85 / 120 + * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX 170 / 170 / 210 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX 105 / 105 / 140 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX 220 / 220 / 270 + * + * If actual rate of OFDM false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), reduce sensitivity + * by *adding* 1 to all 4 of the table entries above, up to the max for + * each entry. Conversely, if false alarm rate is too low (less than 5 + * for each 204.8 msecs listening), *subtract* 1 from each entry to + * increase sensitivity. + * + * For CCK sensitivity, keep track of the following: + * + * 1). 20-beacon history of maximum background noise, indicated by + * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the + * 3 receivers. For any given beacon, the "silence reference" is + * the maximum of last 60 samples (20 beacons * 3 receivers). + * + * 2). 10-beacon history of strongest signal level, as indicated + * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers, + * i.e. the strength of the signal through the best receiver at the + * moment. These measurements are "upside down", with lower values + * for stronger signals, so max energy will be *minimum* value. + * + * Then for any given beacon, the driver must determine the *weakest* + * of the strongest signals; this is the minimum level that needs to be + * successfully detected, when using the best receiver at the moment. + * "Max cck energy" is the maximum (higher value means lower energy!) + * of the last 10 minima. Once this is determined, driver must add + * a little margin by adding "6" to it. + * + * 3). Number of consecutive beacon periods with too few false alarms. + * Reset this to 0 at the first beacon period that falls within the + * "good" range (5 to 50 false alarms per 204.8 milliseconds rx). + * + * Then, adjust the following CCK table entries in struct il_sensitivity_cmd + * (notice that the start points for CCK are at maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX 125 / 125 / 200 + * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX 200 / 200 / 400 + * HD_MIN_ENERGY_CCK_DET_IDX 100 / 0 / 100 + * + * If actual rate of CCK false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), method for reducing + * sensitivity is: + * + * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX, + * up to max 400. + * + * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX is < 160, + * sensitivity has been reduced a significant amount; bring it up to + * a moderate 161. Otherwise, *add* 3, up to max 200. + * + * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX is > 160, + * sensitivity has been reduced only a moderate or small amount; + * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_IDX, + * down to min 0. Otherwise (if gain has been significantly reduced), + * don't change the HD_MIN_ENERGY_CCK_DET_IDX value. + * + * b) Save a snapshot of the "silence reference". + * + * If actual rate of CCK false alarms (+ plcp_errors) is too low + * (less than 5 for each 204.8 msecs listening), method for increasing + * sensitivity is used only if: + * + * 1a) Previous beacon did not have too many false alarms + * 1b) AND difference between previous "silence reference" and current + * "silence reference" (prev - current) is 2 or more, + * OR 2) 100 or more consecutive beacon periods have had rate of + * less than 5 false alarms per 204.8 milliseconds rx time. + * + * Method for increasing sensitivity: + * + * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX, + * down to min 125. + * + * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX, + * down to min 200. + * + * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_IDX, up to max 100. + * + * If actual rate of CCK false alarms (+ plcp_errors) is within good range + * (between 5 and 50 for each 204.8 msecs listening): + * + * 1) Save a snapshot of the silence reference. + * + * 2) If previous beacon had too many CCK false alarms (+ plcp_errors), + * give some extra margin to energy threshold by *subtracting* 8 + * from value in HD_MIN_ENERGY_CCK_DET_IDX. + * + * For all cases (too few, too many, good range), make sure that the CCK + * detection threshold (energy) is below the energy level for robust + * detection over the past 10 beacon periods, the "Max cck energy". + * Lower values mean higher energy; this means making sure that the value + * in HD_MIN_ENERGY_CCK_DET_IDX is at or *above* "Max cck energy". + * + */ + +/* + * Table entries in C_SENSITIVITY (struct il_sensitivity_cmd) + */ +#define HD_TBL_SIZE (11) /* number of entries */ +#define HD_MIN_ENERGY_CCK_DET_IDX (0) /* table idxes */ +#define HD_MIN_ENERGY_OFDM_DET_IDX (1) +#define HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX (2) +#define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX (3) +#define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX (4) +#define HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX (5) +#define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX (6) +#define HD_BARKER_CORR_TH_ADD_MIN_IDX (7) +#define HD_BARKER_CORR_TH_ADD_MIN_MRC_IDX (8) +#define HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX (9) +#define HD_OFDM_ENERGY_TH_IN_IDX (10) + +/* Control field in struct il_sensitivity_cmd */ +#define C_SENSITIVITY_CONTROL_DEFAULT_TBL cpu_to_le16(0) +#define C_SENSITIVITY_CONTROL_WORK_TBL cpu_to_le16(1) + +/** + * struct il_sensitivity_cmd + * @control: (1) updates working table, (0) updates default table + * @table: energy threshold values, use HD_* as idx into table + * + * Always use "1" in "control" to update uCode's working table and DSP. + */ +struct il_sensitivity_cmd { + __le16 control; /* always use "1" */ + __le16 table[HD_TBL_SIZE]; /* use HD_* as idx */ +} __packed; + +/** + * C_PHY_CALIBRATION = 0xb0 (command, has simple generic response) + * + * This command sets the relative gains of 4965 device's 3 radio receiver chains. + * + * After the first association, driver should accumulate signal and noise + * stats from the N_STATSs that follow the first 20 + * beacons from the associated network (don't collect stats that come + * in from scanning, or any other non-network source). + * + * DISCONNECTED ANTENNA: + * + * Driver should determine which antennas are actually connected, by comparing + * average beacon signal levels for the 3 Rx chains. Accumulate (add) the + * following values over 20 beacons, one accumulator for each of the chains + * a/b/c, from struct stats_rx_non_phy: + * + * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the strongest signal from among a/b/c. Compare the other two to the + * strongest. If any signal is more than 15 dB (times 20, unless you + * divide the accumulated values by 20) below the strongest, the driver + * considers that antenna to be disconnected, and should not try to use that + * antenna/chain for Rx or Tx. If both A and B seem to be disconnected, + * driver should declare the stronger one as connected, and attempt to use it + * (A and B are the only 2 Tx chains!). + * + * + * RX BALANCE: + * + * Driver should balance the 3 receivers (but just the ones that are connected + * to antennas, see above) for gain, by comparing the average signal levels + * detected during the silence after each beacon (background noise). + * Accumulate (add) the following values over 20 beacons, one accumulator for + * each of the chains a/b/c, from struct stats_rx_non_phy: + * + * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the weakest background noise level from among a/b/c. This Rx chain + * will be the reference, with 0 gain adjustment. Attenuate other channels by + * finding noise difference: + * + * (accum_noise[i] - accum_noise[reference]) / 30 + * + * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB. + * For use in diff_gain_[abc] fields of struct il_calibration_cmd, the + * driver should limit the difference results to a range of 0-3 (0-4.5 dB), + * and set bit 2 to indicate "reduce gain". The value for the reference + * (weakest) chain should be "0". + * + * diff_gain_[abc] bit fields: + * 2: (1) reduce gain, (0) increase gain + * 1-0: amount of gain, units of 1.5 dB + */ + +/* Phy calibration command for series */ +/* The default calibrate table size if not specified by firmware */ +#define IL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18 +enum { + IL_PHY_CALIBRATE_DIFF_GAIN_CMD = 7, + IL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE = 19, +}; + +#define IL_MAX_PHY_CALIBRATE_TBL_SIZE (253) + +struct il_calib_hdr { + u8 op_code; + u8 first_group; + u8 groups_num; + u8 data_valid; +} __packed; + +/* IL_PHY_CALIBRATE_DIFF_GAIN_CMD (7) */ +struct il_calib_diff_gain_cmd { + struct il_calib_hdr hdr; + s8 diff_gain_a; /* see above */ + s8 diff_gain_b; + s8 diff_gain_c; + u8 reserved1; +} __packed; + +/****************************************************************************** + * (12) + * Miscellaneous Commands: + * + *****************************************************************************/ + +/* + * LEDs Command & Response + * C_LEDS = 0x48 (command, has simple generic response) + * + * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field), + * this command turns it on or off, or sets up a periodic blinking cycle. + */ +struct il_led_cmd { + __le32 interval; /* "interval" in uSec */ + u8 id; /* 1: Activity, 2: Link, 3: Tech */ + u8 off; /* # intervals off while blinking; + * "0", with >0 "on" value, turns LED on */ + u8 on; /* # intervals on while blinking; + * "0", regardless of "off", turns LED off */ + u8 reserved; +} __packed; + +/****************************************************************************** + * (13) + * Union of all expected notifications/responses: + * + *****************************************************************************/ + +#define IL_RX_FRAME_SIZE_MSK 0x00003fff + +struct il_rx_pkt { + /* + * The first 4 bytes of the RX frame header contain both the RX frame + * size and some flags. + * Bit fields: + * 31: flag flush RB request + * 30: flag ignore TC (terminal counter) request + * 29: flag fast IRQ request + * 28-14: Reserved + * 13-00: RX frame size + */ + __le32 len_n_flags; + struct il_cmd_header hdr; + union { + struct il3945_rx_frame rx_frame; + struct il3945_tx_resp tx_resp; + struct il3945_beacon_notif beacon_status; + + struct il_alive_resp alive_frame; + struct il_spectrum_notification spectrum_notif; + struct il_csa_notification csa_notif; + struct il_error_resp err_resp; + struct il_card_state_notif card_state_notif; + struct il_add_sta_resp add_sta; + struct il_rem_sta_resp rem_sta; + struct il_sleep_notification sleep_notif; + struct il_spectrum_resp spectrum; + struct il_notif_stats stats; + struct il_compressed_ba_resp compressed_ba; + struct il_missed_beacon_notif missed_beacon; + __le32 status; + u8 raw[0]; + } u; +} __packed; + +#endif /* __il_commands_h__ */ diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c new file mode 100644 index 000000000000..887114582583 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/common.c @@ -0,0 +1,5586 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +int +_il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout) +{ + const int interval = 10; /* microseconds */ + int t = 0; + + do { + if ((_il_rd(il, addr) & mask) == (bits & mask)) + return t; + udelay(interval); + t += interval; + } while (t < timeout); + + return -ETIMEDOUT; +} +EXPORT_SYMBOL(_il_poll_bit); + +void +il_set_bit(struct il_priv *p, u32 r, u32 m) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&p->reg_lock, reg_flags); + _il_set_bit(p, r, m); + spin_unlock_irqrestore(&p->reg_lock, reg_flags); +} +EXPORT_SYMBOL(il_set_bit); + +void +il_clear_bit(struct il_priv *p, u32 r, u32 m) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&p->reg_lock, reg_flags); + _il_clear_bit(p, r, m); + spin_unlock_irqrestore(&p->reg_lock, reg_flags); +} +EXPORT_SYMBOL(il_clear_bit); + +bool +_il_grab_nic_access(struct il_priv *il) +{ + int ret; + u32 val; + + /* this bit wakes up the NIC */ + _il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + /* + * These bits say the device is running, and should keep running for + * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), + * but they do not indicate that embedded SRAM is restored yet; + * 3945 and 4965 have volatile SRAM, and must save/restore contents + * to/from host DRAM when sleeping/waking for power-saving. + * Each direction takes approximately 1/4 millisecond; with this + * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a + * series of register accesses are expected (e.g. reading Event Log), + * to keep device from sleeping. + * + * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that + * SRAM is okay/restored. We don't check that here because this call + * is just for hardware register access; but GP1 MAC_SLEEP check is a + * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). + * + */ + ret = + _il_poll_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, + (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | + CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); + if (unlikely(ret < 0)) { + val = _il_rd(il, CSR_GP_CNTRL); + WARN_ONCE(1, "Timeout waiting for ucode processor access " + "(CSR_GP_CNTRL 0x%08x)\n", val); + _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); + return false; + } + + return true; +} +EXPORT_SYMBOL_GPL(_il_grab_nic_access); + +int +il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout) +{ + const int interval = 10; /* microseconds */ + int t = 0; + + do { + if ((il_rd(il, addr) & mask) == mask) + return t; + udelay(interval); + t += interval; + } while (t < timeout); + + return -ETIMEDOUT; +} +EXPORT_SYMBOL(il_poll_bit); + +u32 +il_rd_prph(struct il_priv *il, u32 reg) +{ + unsigned long reg_flags; + u32 val; + + spin_lock_irqsave(&il->reg_lock, reg_flags); + _il_grab_nic_access(il); + val = _il_rd_prph(il, reg); + _il_release_nic_access(il); + spin_unlock_irqrestore(&il->reg_lock, reg_flags); + return val; +} +EXPORT_SYMBOL(il_rd_prph); + +void +il_wr_prph(struct il_priv *il, u32 addr, u32 val) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&il->reg_lock, reg_flags); + if (likely(_il_grab_nic_access(il))) { + _il_wr_prph(il, addr, val); + _il_release_nic_access(il); + } + spin_unlock_irqrestore(&il->reg_lock, reg_flags); +} +EXPORT_SYMBOL(il_wr_prph); + +u32 +il_read_targ_mem(struct il_priv *il, u32 addr) +{ + unsigned long reg_flags; + u32 value; + + spin_lock_irqsave(&il->reg_lock, reg_flags); + _il_grab_nic_access(il); + + _il_wr(il, HBUS_TARG_MEM_RADDR, addr); + value = _il_rd(il, HBUS_TARG_MEM_RDAT); + + _il_release_nic_access(il); + spin_unlock_irqrestore(&il->reg_lock, reg_flags); + return value; +} +EXPORT_SYMBOL(il_read_targ_mem); + +void +il_write_targ_mem(struct il_priv *il, u32 addr, u32 val) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&il->reg_lock, reg_flags); + if (likely(_il_grab_nic_access(il))) { + _il_wr(il, HBUS_TARG_MEM_WADDR, addr); + _il_wr(il, HBUS_TARG_MEM_WDAT, val); + _il_release_nic_access(il); + } + spin_unlock_irqrestore(&il->reg_lock, reg_flags); +} +EXPORT_SYMBOL(il_write_targ_mem); + +const char * +il_get_cmd_string(u8 cmd) +{ + switch (cmd) { + IL_CMD(N_ALIVE); + IL_CMD(N_ERROR); + IL_CMD(C_RXON); + IL_CMD(C_RXON_ASSOC); + IL_CMD(C_QOS_PARAM); + IL_CMD(C_RXON_TIMING); + IL_CMD(C_ADD_STA); + IL_CMD(C_REM_STA); + IL_CMD(C_WEPKEY); + IL_CMD(N_3945_RX); + IL_CMD(C_TX); + IL_CMD(C_RATE_SCALE); + IL_CMD(C_LEDS); + IL_CMD(C_TX_LINK_QUALITY_CMD); + IL_CMD(C_CHANNEL_SWITCH); + IL_CMD(N_CHANNEL_SWITCH); + IL_CMD(C_SPECTRUM_MEASUREMENT); + IL_CMD(N_SPECTRUM_MEASUREMENT); + IL_CMD(C_POWER_TBL); + IL_CMD(N_PM_SLEEP); + IL_CMD(N_PM_DEBUG_STATS); + IL_CMD(C_SCAN); + IL_CMD(C_SCAN_ABORT); + IL_CMD(N_SCAN_START); + IL_CMD(N_SCAN_RESULTS); + IL_CMD(N_SCAN_COMPLETE); + IL_CMD(N_BEACON); + IL_CMD(C_TX_BEACON); + IL_CMD(C_TX_PWR_TBL); + IL_CMD(C_BT_CONFIG); + IL_CMD(C_STATS); + IL_CMD(N_STATS); + IL_CMD(N_CARD_STATE); + IL_CMD(N_MISSED_BEACONS); + IL_CMD(C_CT_KILL_CONFIG); + IL_CMD(C_SENSITIVITY); + IL_CMD(C_PHY_CALIBRATION); + IL_CMD(N_RX_PHY); + IL_CMD(N_RX_MPDU); + IL_CMD(N_RX); + IL_CMD(N_COMPRESSED_BA); + default: + return "UNKNOWN"; + + } +} +EXPORT_SYMBOL(il_get_cmd_string); + +#define HOST_COMPLETE_TIMEOUT (HZ / 2) + +static void +il_generic_cmd_callback(struct il_priv *il, struct il_device_cmd *cmd, + struct il_rx_pkt *pkt) +{ + if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { + IL_ERR("Bad return from %s (0x%08X)\n", + il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); + return; + } +#ifdef CONFIG_IWLEGACY_DEBUG + switch (cmd->hdr.cmd) { + case C_TX_LINK_QUALITY_CMD: + case C_SENSITIVITY: + D_HC_DUMP("back from %s (0x%08X)\n", + il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); + break; + default: + D_HC("back from %s (0x%08X)\n", il_get_cmd_string(cmd->hdr.cmd), + pkt->hdr.flags); + } +#endif +} + +static int +il_send_cmd_async(struct il_priv *il, struct il_host_cmd *cmd) +{ + int ret; + + BUG_ON(!(cmd->flags & CMD_ASYNC)); + + /* An asynchronous command can not expect an SKB to be set. */ + BUG_ON(cmd->flags & CMD_WANT_SKB); + + /* Assign a generic callback if one is not provided */ + if (!cmd->callback) + cmd->callback = il_generic_cmd_callback; + + if (test_bit(S_EXIT_PENDING, &il->status)) + return -EBUSY; + + ret = il_enqueue_hcmd(il, cmd); + if (ret < 0) { + IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n", + il_get_cmd_string(cmd->id), ret); + return ret; + } + return 0; +} + +int +il_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd) +{ + int cmd_idx; + int ret; + + lockdep_assert_held(&il->mutex); + + BUG_ON(cmd->flags & CMD_ASYNC); + + /* A synchronous command can not have a callback set. */ + BUG_ON(cmd->callback); + + D_INFO("Attempting to send sync command %s\n", + il_get_cmd_string(cmd->id)); + + set_bit(S_HCMD_ACTIVE, &il->status); + D_INFO("Setting HCMD_ACTIVE for command %s\n", + il_get_cmd_string(cmd->id)); + + cmd_idx = il_enqueue_hcmd(il, cmd); + if (cmd_idx < 0) { + ret = cmd_idx; + IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n", + il_get_cmd_string(cmd->id), ret); + goto out; + } + + ret = wait_event_timeout(il->wait_command_queue, + !test_bit(S_HCMD_ACTIVE, &il->status), + HOST_COMPLETE_TIMEOUT); + if (!ret) { + if (test_bit(S_HCMD_ACTIVE, &il->status)) { + IL_ERR("Error sending %s: time out after %dms.\n", + il_get_cmd_string(cmd->id), + jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); + + clear_bit(S_HCMD_ACTIVE, &il->status); + D_INFO("Clearing HCMD_ACTIVE for command %s\n", + il_get_cmd_string(cmd->id)); + ret = -ETIMEDOUT; + goto cancel; + } + } + + if (test_bit(S_RFKILL, &il->status)) { + IL_ERR("Command %s aborted: RF KILL Switch\n", + il_get_cmd_string(cmd->id)); + ret = -ECANCELED; + goto fail; + } + if (test_bit(S_FW_ERROR, &il->status)) { + IL_ERR("Command %s failed: FW Error\n", + il_get_cmd_string(cmd->id)); + ret = -EIO; + goto fail; + } + if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) { + IL_ERR("Error: Response NULL in '%s'\n", + il_get_cmd_string(cmd->id)); + ret = -EIO; + goto cancel; + } + + ret = 0; + goto out; + +cancel: + if (cmd->flags & CMD_WANT_SKB) { + /* + * Cancel the CMD_WANT_SKB flag for the cmd in the + * TX cmd queue. Otherwise in case the cmd comes + * in later, it will possibly set an invalid + * address (cmd->meta.source). + */ + il->txq[il->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB; + } +fail: + if (cmd->reply_page) { + il_free_pages(il, cmd->reply_page); + cmd->reply_page = 0; + } +out: + return ret; +} +EXPORT_SYMBOL(il_send_cmd_sync); + +int +il_send_cmd(struct il_priv *il, struct il_host_cmd *cmd) +{ + if (cmd->flags & CMD_ASYNC) + return il_send_cmd_async(il, cmd); + + return il_send_cmd_sync(il, cmd); +} +EXPORT_SYMBOL(il_send_cmd); + +int +il_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, const void *data) +{ + struct il_host_cmd cmd = { + .id = id, + .len = len, + .data = data, + }; + + return il_send_cmd_sync(il, &cmd); +} +EXPORT_SYMBOL(il_send_cmd_pdu); + +int +il_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data, + void (*callback) (struct il_priv *il, + struct il_device_cmd *cmd, + struct il_rx_pkt *pkt)) +{ + struct il_host_cmd cmd = { + .id = id, + .len = len, + .data = data, + }; + + cmd.flags |= CMD_ASYNC; + cmd.callback = callback; + + return il_send_cmd_async(il, &cmd); +} +EXPORT_SYMBOL(il_send_cmd_pdu_async); + +/* default: IL_LED_BLINK(0) using blinking idx table */ +static int led_mode; +module_param(led_mode, int, S_IRUGO); +MODULE_PARM_DESC(led_mode, + "0=system default, " "1=On(RF On)/Off(RF Off), 2=blinking"); + +/* Throughput OFF time(ms) ON time (ms) + * >300 25 25 + * >200 to 300 40 40 + * >100 to 200 55 55 + * >70 to 100 65 65 + * >50 to 70 75 75 + * >20 to 50 85 85 + * >10 to 20 95 95 + * >5 to 10 110 110 + * >1 to 5 130 130 + * >0 to 1 167 167 + * <=0 SOLID ON + */ +static const struct ieee80211_tpt_blink il_blink[] = { + {.throughput = 0, .blink_time = 334}, + {.throughput = 1 * 1024 - 1, .blink_time = 260}, + {.throughput = 5 * 1024 - 1, .blink_time = 220}, + {.throughput = 10 * 1024 - 1, .blink_time = 190}, + {.throughput = 20 * 1024 - 1, .blink_time = 170}, + {.throughput = 50 * 1024 - 1, .blink_time = 150}, + {.throughput = 70 * 1024 - 1, .blink_time = 130}, + {.throughput = 100 * 1024 - 1, .blink_time = 110}, + {.throughput = 200 * 1024 - 1, .blink_time = 80}, + {.throughput = 300 * 1024 - 1, .blink_time = 50}, +}; + +/* + * Adjust led blink rate to compensate on a MAC Clock difference on every HW + * Led blink rate analysis showed an average deviation of 0% on 3945, + * 5% on 4965 HW. + * Need to compensate on the led on/off time per HW according to the deviation + * to achieve the desired led frequency + * The calculation is: (100-averageDeviation)/100 * blinkTime + * For code efficiency the calculation will be: + * compensation = (100 - averageDeviation) * 64 / 100 + * NewBlinkTime = (compensation * BlinkTime) / 64 + */ +static inline u8 +il_blink_compensation(struct il_priv *il, u8 time, u16 compensation) +{ + if (!compensation) { + IL_ERR("undefined blink compensation: " + "use pre-defined blinking time\n"); + return time; + } + + return (u8) ((time * compensation) >> 6); +} + +/* Set led pattern command */ +static int +il_led_cmd(struct il_priv *il, unsigned long on, unsigned long off) +{ + struct il_led_cmd led_cmd = { + .id = IL_LED_LINK, + .interval = IL_DEF_LED_INTRVL + }; + int ret; + + if (!test_bit(S_READY, &il->status)) + return -EBUSY; + + if (il->blink_on == on && il->blink_off == off) + return 0; + + if (off == 0) { + /* led is SOLID_ON */ + on = IL_LED_SOLID; + } + + D_LED("Led blink time compensation=%u\n", + il->cfg->led_compensation); + led_cmd.on = + il_blink_compensation(il, on, + il->cfg->led_compensation); + led_cmd.off = + il_blink_compensation(il, off, + il->cfg->led_compensation); + + ret = il->ops->send_led_cmd(il, &led_cmd); + if (!ret) { + il->blink_on = on; + il->blink_off = off; + } + return ret; +} + +static void +il_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct il_priv *il = container_of(led_cdev, struct il_priv, led); + unsigned long on = 0; + + if (brightness > 0) + on = IL_LED_SOLID; + + il_led_cmd(il, on, 0); +} + +static int +il_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, + unsigned long *delay_off) +{ + struct il_priv *il = container_of(led_cdev, struct il_priv, led); + + return il_led_cmd(il, *delay_on, *delay_off); +} + +void +il_leds_init(struct il_priv *il) +{ + int mode = led_mode; + int ret; + + if (mode == IL_LED_DEFAULT) + mode = il->cfg->led_mode; + + il->led.name = + kasprintf(GFP_KERNEL, "%s-led", wiphy_name(il->hw->wiphy)); + il->led.brightness_set = il_led_brightness_set; + il->led.blink_set = il_led_blink_set; + il->led.max_brightness = 1; + + switch (mode) { + case IL_LED_DEFAULT: + WARN_ON(1); + break; + case IL_LED_BLINK: + il->led.default_trigger = + ieee80211_create_tpt_led_trigger(il->hw, + IEEE80211_TPT_LEDTRIG_FL_CONNECTED, + il_blink, + ARRAY_SIZE(il_blink)); + break; + case IL_LED_RF_STATE: + il->led.default_trigger = ieee80211_get_radio_led_name(il->hw); + break; + } + + ret = led_classdev_register(&il->pci_dev->dev, &il->led); + if (ret) { + kfree(il->led.name); + return; + } + + il->led_registered = true; +} +EXPORT_SYMBOL(il_leds_init); + +void +il_leds_exit(struct il_priv *il) +{ + if (!il->led_registered) + return; + + led_classdev_unregister(&il->led); + kfree(il->led.name); +} +EXPORT_SYMBOL(il_leds_exit); + +/************************** EEPROM BANDS **************************** + * + * The il_eeprom_band definitions below provide the mapping from the + * EEPROM contents to the specific channel number supported for each + * band. + * + * For example, il_priv->eeprom.band_3_channels[4] from the band_3 + * definition below maps to physical channel 42 in the 5.2GHz spectrum. + * The specific geography and calibration information for that channel + * is contained in the eeprom map itself. + * + * During init, we copy the eeprom information and channel map + * information into il->channel_info_24/52 and il->channel_map_24/52 + * + * channel_map_24/52 provides the idx in the channel_info array for a + * given channel. We have to have two separate maps as there is channel + * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and + * band_2 + * + * A value of 0xff stored in the channel_map indicates that the channel + * is not supported by the hardware at all. + * + * A value of 0xfe in the channel_map indicates that the channel is not + * valid for Tx with the current hardware. This means that + * while the system can tune and receive on a given channel, it may not + * be able to associate or transmit any frames on that + * channel. There is no corresponding channel information for that + * entry. + * + *********************************************************************/ + +/* 2.4 GHz */ +const u8 il_eeprom_band_1[14] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 +}; + +/* 5.2 GHz bands */ +static const u8 il_eeprom_band_2[] = { /* 4915-5080MHz */ + 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 +}; + +static const u8 il_eeprom_band_3[] = { /* 5170-5320MHz */ + 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 +}; + +static const u8 il_eeprom_band_4[] = { /* 5500-5700MHz */ + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 +}; + +static const u8 il_eeprom_band_5[] = { /* 5725-5825MHz */ + 145, 149, 153, 157, 161, 165 +}; + +static const u8 il_eeprom_band_6[] = { /* 2.4 ht40 channel */ + 1, 2, 3, 4, 5, 6, 7 +}; + +static const u8 il_eeprom_band_7[] = { /* 5.2 ht40 channel */ + 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 +}; + +/****************************************************************************** + * + * EEPROM related functions + * +******************************************************************************/ + +static int +il_eeprom_verify_signature(struct il_priv *il) +{ + u32 gp = _il_rd(il, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; + int ret = 0; + + D_EEPROM("EEPROM signature=0x%08x\n", gp); + switch (gp) { + case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: + case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: + break; + default: + IL_ERR("bad EEPROM signature," "EEPROM_GP=0x%08x\n", gp); + ret = -ENOENT; + break; + } + return ret; +} + +const u8 * +il_eeprom_query_addr(const struct il_priv *il, size_t offset) +{ + BUG_ON(offset >= il->cfg->eeprom_size); + return &il->eeprom[offset]; +} +EXPORT_SYMBOL(il_eeprom_query_addr); + +u16 +il_eeprom_query16(const struct il_priv *il, size_t offset) +{ + if (!il->eeprom) + return 0; + return (u16) il->eeprom[offset] | ((u16) il->eeprom[offset + 1] << 8); +} +EXPORT_SYMBOL(il_eeprom_query16); + +/** + * il_eeprom_init - read EEPROM contents + * + * Load the EEPROM contents from adapter into il->eeprom + * + * NOTE: This routine uses the non-debug IO access functions. + */ +int +il_eeprom_init(struct il_priv *il) +{ + __le16 *e; + u32 gp = _il_rd(il, CSR_EEPROM_GP); + int sz; + int ret; + u16 addr; + + /* allocate eeprom */ + sz = il->cfg->eeprom_size; + D_EEPROM("NVM size = %d\n", sz); + il->eeprom = kzalloc(sz, GFP_KERNEL); + if (!il->eeprom) { + ret = -ENOMEM; + goto alloc_err; + } + e = (__le16 *) il->eeprom; + + il->ops->apm_init(il); + + ret = il_eeprom_verify_signature(il); + if (ret < 0) { + IL_ERR("EEPROM not found, EEPROM_GP=0x%08x\n", gp); + ret = -ENOENT; + goto err; + } + + /* Make sure driver (instead of uCode) is allowed to read EEPROM */ + ret = il->ops->eeprom_acquire_semaphore(il); + if (ret < 0) { + IL_ERR("Failed to acquire EEPROM semaphore.\n"); + ret = -ENOENT; + goto err; + } + + /* eeprom is an array of 16bit values */ + for (addr = 0; addr < sz; addr += sizeof(u16)) { + u32 r; + + _il_wr(il, CSR_EEPROM_REG, + CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); + + ret = + _il_poll_bit(il, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, + CSR_EEPROM_REG_READ_VALID_MSK, + IL_EEPROM_ACCESS_TIMEOUT); + if (ret < 0) { + IL_ERR("Time out reading EEPROM[%d]\n", addr); + goto done; + } + r = _il_rd(il, CSR_EEPROM_REG); + e[addr / 2] = cpu_to_le16(r >> 16); + } + + D_EEPROM("NVM Type: %s, version: 0x%x\n", "EEPROM", + il_eeprom_query16(il, EEPROM_VERSION)); + + ret = 0; +done: + il->ops->eeprom_release_semaphore(il); + +err: + if (ret) + il_eeprom_free(il); + /* Reset chip to save power until we load uCode during "up". */ + il_apm_stop(il); +alloc_err: + return ret; +} +EXPORT_SYMBOL(il_eeprom_init); + +void +il_eeprom_free(struct il_priv *il) +{ + kfree(il->eeprom); + il->eeprom = NULL; +} +EXPORT_SYMBOL(il_eeprom_free); + +static void +il_init_band_reference(const struct il_priv *il, int eep_band, + int *eeprom_ch_count, + const struct il_eeprom_channel **eeprom_ch_info, + const u8 **eeprom_ch_idx) +{ + u32 offset = il->cfg->regulatory_bands[eep_band - 1]; + + switch (eep_band) { + case 1: /* 2.4GHz band */ + *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_1); + *eeprom_ch_info = + (struct il_eeprom_channel *)il_eeprom_query_addr(il, + offset); + *eeprom_ch_idx = il_eeprom_band_1; + break; + case 2: /* 4.9GHz band */ + *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_2); + *eeprom_ch_info = + (struct il_eeprom_channel *)il_eeprom_query_addr(il, + offset); + *eeprom_ch_idx = il_eeprom_band_2; + break; + case 3: /* 5.2GHz band */ + *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_3); + *eeprom_ch_info = + (struct il_eeprom_channel *)il_eeprom_query_addr(il, + offset); + *eeprom_ch_idx = il_eeprom_band_3; + break; + case 4: /* 5.5GHz band */ + *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_4); + *eeprom_ch_info = + (struct il_eeprom_channel *)il_eeprom_query_addr(il, + offset); + *eeprom_ch_idx = il_eeprom_band_4; + break; + case 5: /* 5.7GHz band */ + *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_5); + *eeprom_ch_info = + (struct il_eeprom_channel *)il_eeprom_query_addr(il, + offset); + *eeprom_ch_idx = il_eeprom_band_5; + break; + case 6: /* 2.4GHz ht40 channels */ + *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_6); + *eeprom_ch_info = + (struct il_eeprom_channel *)il_eeprom_query_addr(il, + offset); + *eeprom_ch_idx = il_eeprom_band_6; + break; + case 7: /* 5 GHz ht40 channels */ + *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_7); + *eeprom_ch_info = + (struct il_eeprom_channel *)il_eeprom_query_addr(il, + offset); + *eeprom_ch_idx = il_eeprom_band_7; + break; + default: + BUG(); + } +} + +#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \ + ? # x " " : "") +/** + * il_mod_ht40_chan_info - Copy ht40 channel info into driver's il. + * + * Does not set up a command, or touch hardware. + */ +static int +il_mod_ht40_chan_info(struct il_priv *il, enum ieee80211_band band, u16 channel, + const struct il_eeprom_channel *eeprom_ch, + u8 clear_ht40_extension_channel) +{ + struct il_channel_info *ch_info; + + ch_info = + (struct il_channel_info *)il_get_channel_info(il, band, channel); + + if (!il_is_channel_valid(ch_info)) + return -1; + + D_EEPROM("HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" + " Ad-Hoc %ssupported\n", ch_info->channel, + il_is_channel_a_band(ch_info) ? "5.2" : "2.4", + CHECK_AND_PRINT(IBSS), CHECK_AND_PRINT(ACTIVE), + CHECK_AND_PRINT(RADAR), CHECK_AND_PRINT(WIDE), + CHECK_AND_PRINT(DFS), eeprom_ch->flags, + eeprom_ch->max_power_avg, + ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) && + !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); + + ch_info->ht40_eeprom = *eeprom_ch; + ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; + ch_info->ht40_flags = eeprom_ch->flags; + if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) + ch_info->ht40_extension_channel &= + ~clear_ht40_extension_channel; + + return 0; +} + +#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \ + ? # x " " : "") + +/** + * il_init_channel_map - Set up driver's info for all possible channels + */ +int +il_init_channel_map(struct il_priv *il) +{ + int eeprom_ch_count = 0; + const u8 *eeprom_ch_idx = NULL; + const struct il_eeprom_channel *eeprom_ch_info = NULL; + int band, ch; + struct il_channel_info *ch_info; + + if (il->channel_count) { + D_EEPROM("Channel map already initialized.\n"); + return 0; + } + + D_EEPROM("Initializing regulatory info from EEPROM\n"); + + il->channel_count = + ARRAY_SIZE(il_eeprom_band_1) + ARRAY_SIZE(il_eeprom_band_2) + + ARRAY_SIZE(il_eeprom_band_3) + ARRAY_SIZE(il_eeprom_band_4) + + ARRAY_SIZE(il_eeprom_band_5); + + D_EEPROM("Parsing data for %d channels.\n", il->channel_count); + + il->channel_info = + kzalloc(sizeof(struct il_channel_info) * il->channel_count, + GFP_KERNEL); + if (!il->channel_info) { + IL_ERR("Could not allocate channel_info\n"); + il->channel_count = 0; + return -ENOMEM; + } + + ch_info = il->channel_info; + + /* Loop through the 5 EEPROM bands adding them in order to the + * channel map we maintain (that contains additional information than + * what just in the EEPROM) */ + for (band = 1; band <= 5; band++) { + + il_init_band_reference(il, band, &eeprom_ch_count, + &eeprom_ch_info, &eeprom_ch_idx); + + /* Loop through each band adding each of the channels */ + for (ch = 0; ch < eeprom_ch_count; ch++) { + ch_info->channel = eeprom_ch_idx[ch]; + ch_info->band = + (band == + 1) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + + /* permanently store EEPROM's channel regulatory flags + * and max power in channel info database. */ + ch_info->eeprom = eeprom_ch_info[ch]; + + /* Copy the run-time flags so they are there even on + * invalid channels */ + ch_info->flags = eeprom_ch_info[ch].flags; + /* First write that ht40 is not enabled, and then enable + * one by one */ + ch_info->ht40_extension_channel = + IEEE80211_CHAN_NO_HT40; + + if (!(il_is_channel_valid(ch_info))) { + D_EEPROM("Ch. %d Flags %x [%sGHz] - " + "No traffic\n", ch_info->channel, + ch_info->flags, + il_is_channel_a_band(ch_info) ? "5.2" : + "2.4"); + ch_info++; + continue; + } + + /* Initialize regulatory-based run-time data */ + ch_info->max_power_avg = ch_info->curr_txpow = + eeprom_ch_info[ch].max_power_avg; + ch_info->scan_power = eeprom_ch_info[ch].max_power_avg; + ch_info->min_power = 0; + + D_EEPROM("Ch. %d [%sGHz] " "%s%s%s%s%s%s(0x%02x %ddBm):" + " Ad-Hoc %ssupported\n", ch_info->channel, + il_is_channel_a_band(ch_info) ? "5.2" : "2.4", + CHECK_AND_PRINT_I(VALID), + CHECK_AND_PRINT_I(IBSS), + CHECK_AND_PRINT_I(ACTIVE), + CHECK_AND_PRINT_I(RADAR), + CHECK_AND_PRINT_I(WIDE), + CHECK_AND_PRINT_I(DFS), + eeprom_ch_info[ch].flags, + eeprom_ch_info[ch].max_power_avg, + ((eeprom_ch_info[ch]. + flags & EEPROM_CHANNEL_IBSS) && + !(eeprom_ch_info[ch]. + flags & EEPROM_CHANNEL_RADAR)) ? "" : + "not "); + + ch_info++; + } + } + + /* Check if we do have HT40 channels */ + if (il->cfg->regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 && + il->cfg->regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40) + return 0; + + /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ + for (band = 6; band <= 7; band++) { + enum ieee80211_band ieeeband; + + il_init_band_reference(il, band, &eeprom_ch_count, + &eeprom_ch_info, &eeprom_ch_idx); + + /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ + ieeeband = + (band == 6) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + + /* Loop through each band adding each of the channels */ + for (ch = 0; ch < eeprom_ch_count; ch++) { + /* Set up driver's info for lower half */ + il_mod_ht40_chan_info(il, ieeeband, eeprom_ch_idx[ch], + &eeprom_ch_info[ch], + IEEE80211_CHAN_NO_HT40PLUS); + + /* Set up driver's info for upper half */ + il_mod_ht40_chan_info(il, ieeeband, + eeprom_ch_idx[ch] + 4, + &eeprom_ch_info[ch], + IEEE80211_CHAN_NO_HT40MINUS); + } + } + + return 0; +} +EXPORT_SYMBOL(il_init_channel_map); + +/* + * il_free_channel_map - undo allocations in il_init_channel_map + */ +void +il_free_channel_map(struct il_priv *il) +{ + kfree(il->channel_info); + il->channel_count = 0; +} +EXPORT_SYMBOL(il_free_channel_map); + +/** + * il_get_channel_info - Find driver's ilate channel info + * + * Based on band and channel number. + */ +const struct il_channel_info * +il_get_channel_info(const struct il_priv *il, enum ieee80211_band band, + u16 channel) +{ + int i; + + switch (band) { + case IEEE80211_BAND_5GHZ: + for (i = 14; i < il->channel_count; i++) { + if (il->channel_info[i].channel == channel) + return &il->channel_info[i]; + } + break; + case IEEE80211_BAND_2GHZ: + if (channel >= 1 && channel <= 14) + return &il->channel_info[channel - 1]; + break; + default: + BUG(); + } + + return NULL; +} +EXPORT_SYMBOL(il_get_channel_info); + +/* + * Setting power level allows the card to go to sleep when not busy. + * + * We calculate a sleep command based on the required latency, which + * we get from mac80211. + */ + +#define SLP_VEC(X0, X1, X2, X3, X4) { \ + cpu_to_le32(X0), \ + cpu_to_le32(X1), \ + cpu_to_le32(X2), \ + cpu_to_le32(X3), \ + cpu_to_le32(X4) \ +} + +static void +il_build_powertable_cmd(struct il_priv *il, struct il_powertable_cmd *cmd) +{ + const __le32 interval[3][IL_POWER_VEC_SIZE] = { + SLP_VEC(2, 2, 4, 6, 0xFF), + SLP_VEC(2, 4, 7, 10, 10), + SLP_VEC(4, 7, 10, 10, 0xFF) + }; + int i, dtim_period, no_dtim; + u32 max_sleep; + bool skip; + + memset(cmd, 0, sizeof(*cmd)); + + if (il->power_data.pci_pm) + cmd->flags |= IL_POWER_PCI_PM_MSK; + + /* if no Power Save, we are done */ + if (il->power_data.ps_disabled) + return; + + cmd->flags = IL_POWER_DRIVER_ALLOW_SLEEP_MSK; + cmd->keep_alive_seconds = 0; + cmd->debug_flags = 0; + cmd->rx_data_timeout = cpu_to_le32(25 * 1024); + cmd->tx_data_timeout = cpu_to_le32(25 * 1024); + cmd->keep_alive_beacons = 0; + + dtim_period = il->vif ? il->vif->bss_conf.dtim_period : 0; + + if (dtim_period <= 2) { + memcpy(cmd->sleep_interval, interval[0], sizeof(interval[0])); + no_dtim = 2; + } else if (dtim_period <= 10) { + memcpy(cmd->sleep_interval, interval[1], sizeof(interval[1])); + no_dtim = 2; + } else { + memcpy(cmd->sleep_interval, interval[2], sizeof(interval[2])); + no_dtim = 0; + } + + if (dtim_period == 0) { + dtim_period = 1; + skip = false; + } else { + skip = !!no_dtim; + } + + if (skip) { + __le32 tmp = cmd->sleep_interval[IL_POWER_VEC_SIZE - 1]; + + max_sleep = le32_to_cpu(tmp); + if (max_sleep == 0xFF) + max_sleep = dtim_period * (skip + 1); + else if (max_sleep > dtim_period) + max_sleep = (max_sleep / dtim_period) * dtim_period; + cmd->flags |= IL_POWER_SLEEP_OVER_DTIM_MSK; + } else { + max_sleep = dtim_period; + cmd->flags &= ~IL_POWER_SLEEP_OVER_DTIM_MSK; + } + + for (i = 0; i < IL_POWER_VEC_SIZE; i++) + if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) + cmd->sleep_interval[i] = cpu_to_le32(max_sleep); +} + +static int +il_set_power(struct il_priv *il, struct il_powertable_cmd *cmd) +{ + D_POWER("Sending power/sleep command\n"); + D_POWER("Flags value = 0x%08X\n", cmd->flags); + D_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); + D_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); + D_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n", + le32_to_cpu(cmd->sleep_interval[0]), + le32_to_cpu(cmd->sleep_interval[1]), + le32_to_cpu(cmd->sleep_interval[2]), + le32_to_cpu(cmd->sleep_interval[3]), + le32_to_cpu(cmd->sleep_interval[4])); + + return il_send_cmd_pdu(il, C_POWER_TBL, + sizeof(struct il_powertable_cmd), cmd); +} + +static int +il_power_set_mode(struct il_priv *il, struct il_powertable_cmd *cmd, bool force) +{ + int ret; + bool update_chains; + + lockdep_assert_held(&il->mutex); + + /* Don't update the RX chain when chain noise calibration is running */ + update_chains = il->chain_noise_data.state == IL_CHAIN_NOISE_DONE || + il->chain_noise_data.state == IL_CHAIN_NOISE_ALIVE; + + if (!memcmp(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) + return 0; + + if (!il_is_ready_rf(il)) + return -EIO; + + /* scan complete use sleep_power_next, need to be updated */ + memcpy(&il->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); + if (test_bit(S_SCANNING, &il->status) && !force) { + D_INFO("Defer power set mode while scanning\n"); + return 0; + } + + if (cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK) + set_bit(S_POWER_PMI, &il->status); + + ret = il_set_power(il, cmd); + if (!ret) { + if (!(cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK)) + clear_bit(S_POWER_PMI, &il->status); + + if (il->ops->update_chain_flags && update_chains) + il->ops->update_chain_flags(il); + else if (il->ops->update_chain_flags) + D_POWER("Cannot update the power, chain noise " + "calibration running: %d\n", + il->chain_noise_data.state); + + memcpy(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)); + } else + IL_ERR("set power fail, ret = %d", ret); + + return ret; +} + +int +il_power_update_mode(struct il_priv *il, bool force) +{ + struct il_powertable_cmd cmd; + + il_build_powertable_cmd(il, &cmd); + + return il_power_set_mode(il, &cmd, force); +} +EXPORT_SYMBOL(il_power_update_mode); + +/* initialize to default */ +void +il_power_initialize(struct il_priv *il) +{ + u16 lctl; + + pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl); + il->power_data.pci_pm = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S); + + il->power_data.debug_sleep_level_override = -1; + + memset(&il->power_data.sleep_cmd, 0, sizeof(il->power_data.sleep_cmd)); +} +EXPORT_SYMBOL(il_power_initialize); + +/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after + * sending probe req. This should be set long enough to hear probe responses + * from more than one AP. */ +#define IL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ +#define IL_ACTIVE_DWELL_TIME_52 (20) + +#define IL_ACTIVE_DWELL_FACTOR_24GHZ (3) +#define IL_ACTIVE_DWELL_FACTOR_52GHZ (2) + +/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. + * Must be set longer than active dwell time. + * For the most reliable scan, set > AP beacon interval (typically 100msec). */ +#define IL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ +#define IL_PASSIVE_DWELL_TIME_52 (10) +#define IL_PASSIVE_DWELL_BASE (100) +#define IL_CHANNEL_TUNE_TIME 5 + +static int +il_send_scan_abort(struct il_priv *il) +{ + int ret; + struct il_rx_pkt *pkt; + struct il_host_cmd cmd = { + .id = C_SCAN_ABORT, + .flags = CMD_WANT_SKB, + }; + + /* Exit instantly with error when device is not ready + * to receive scan abort command or it does not perform + * hardware scan currently */ + if (!test_bit(S_READY, &il->status) || + !test_bit(S_GEO_CONFIGURED, &il->status) || + !test_bit(S_SCAN_HW, &il->status) || + test_bit(S_FW_ERROR, &il->status) || + test_bit(S_EXIT_PENDING, &il->status)) + return -EIO; + + ret = il_send_cmd_sync(il, &cmd); + if (ret) + return ret; + + pkt = (struct il_rx_pkt *)cmd.reply_page; + if (pkt->u.status != CAN_ABORT_STATUS) { + /* The scan abort will return 1 for success or + * 2 for "failure". A failure condition can be + * due to simply not being in an active scan which + * can occur if we send the scan abort before we + * the microcode has notified us that a scan is + * completed. */ + D_SCAN("SCAN_ABORT ret %d.\n", pkt->u.status); + ret = -EIO; + } + + il_free_pages(il, cmd.reply_page); + return ret; +} + +static void +il_complete_scan(struct il_priv *il, bool aborted) +{ + /* check if scan was requested from mac80211 */ + if (il->scan_request) { + D_SCAN("Complete scan in mac80211\n"); + ieee80211_scan_completed(il->hw, aborted); + } + + il->scan_vif = NULL; + il->scan_request = NULL; +} + +void +il_force_scan_end(struct il_priv *il) +{ + lockdep_assert_held(&il->mutex); + + if (!test_bit(S_SCANNING, &il->status)) { + D_SCAN("Forcing scan end while not scanning\n"); + return; + } + + D_SCAN("Forcing scan end\n"); + clear_bit(S_SCANNING, &il->status); + clear_bit(S_SCAN_HW, &il->status); + clear_bit(S_SCAN_ABORTING, &il->status); + il_complete_scan(il, true); +} + +static void +il_do_scan_abort(struct il_priv *il) +{ + int ret; + + lockdep_assert_held(&il->mutex); + + if (!test_bit(S_SCANNING, &il->status)) { + D_SCAN("Not performing scan to abort\n"); + return; + } + + if (test_and_set_bit(S_SCAN_ABORTING, &il->status)) { + D_SCAN("Scan abort in progress\n"); + return; + } + + ret = il_send_scan_abort(il); + if (ret) { + D_SCAN("Send scan abort failed %d\n", ret); + il_force_scan_end(il); + } else + D_SCAN("Successfully send scan abort\n"); +} + +/** + * il_scan_cancel - Cancel any currently executing HW scan + */ +int +il_scan_cancel(struct il_priv *il) +{ + D_SCAN("Queuing abort scan\n"); + queue_work(il->workqueue, &il->abort_scan); + return 0; +} +EXPORT_SYMBOL(il_scan_cancel); + +/** + * il_scan_cancel_timeout - Cancel any currently executing HW scan + * @ms: amount of time to wait (in milliseconds) for scan to abort + * + */ +int +il_scan_cancel_timeout(struct il_priv *il, unsigned long ms) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(ms); + + lockdep_assert_held(&il->mutex); + + D_SCAN("Scan cancel timeout\n"); + + il_do_scan_abort(il); + + while (time_before_eq(jiffies, timeout)) { + if (!test_bit(S_SCAN_HW, &il->status)) + break; + msleep(20); + } + + return test_bit(S_SCAN_HW, &il->status); +} +EXPORT_SYMBOL(il_scan_cancel_timeout); + +/* Service response to C_SCAN (0x80) */ +static void +il_hdl_scan(struct il_priv *il, struct il_rx_buf *rxb) +{ +#ifdef CONFIG_IWLEGACY_DEBUG + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_scanreq_notification *notif = + (struct il_scanreq_notification *)pkt->u.raw; + + D_SCAN("Scan request status = 0x%x\n", notif->status); +#endif +} + +/* Service N_SCAN_START (0x82) */ +static void +il_hdl_scan_start(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_scanstart_notification *notif = + (struct il_scanstart_notification *)pkt->u.raw; + il->scan_start_tsf = le32_to_cpu(notif->tsf_low); + D_SCAN("Scan start: " "%d [802.11%s] " + "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", notif->channel, + notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high), + le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer); +} + +/* Service N_SCAN_RESULTS (0x83) */ +static void +il_hdl_scan_results(struct il_priv *il, struct il_rx_buf *rxb) +{ +#ifdef CONFIG_IWLEGACY_DEBUG + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_scanresults_notification *notif = + (struct il_scanresults_notification *)pkt->u.raw; + + D_SCAN("Scan ch.res: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d " + "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a", + le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low), + le32_to_cpu(notif->stats[0]), + le32_to_cpu(notif->tsf_low) - il->scan_start_tsf); +#endif +} + +/* Service N_SCAN_COMPLETE (0x84) */ +static void +il_hdl_scan_complete(struct il_priv *il, struct il_rx_buf *rxb) +{ + +#ifdef CONFIG_IWLEGACY_DEBUG + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_scancomplete_notification *scan_notif = (void *)pkt->u.raw; +#endif + + D_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", + scan_notif->scanned_channels, scan_notif->tsf_low, + scan_notif->tsf_high, scan_notif->status); + + /* The HW is no longer scanning */ + clear_bit(S_SCAN_HW, &il->status); + + D_SCAN("Scan on %sGHz took %dms\n", + (il->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", + jiffies_to_msecs(jiffies - il->scan_start)); + + queue_work(il->workqueue, &il->scan_completed); +} + +void +il_setup_rx_scan_handlers(struct il_priv *il) +{ + /* scan handlers */ + il->handlers[C_SCAN] = il_hdl_scan; + il->handlers[N_SCAN_START] = il_hdl_scan_start; + il->handlers[N_SCAN_RESULTS] = il_hdl_scan_results; + il->handlers[N_SCAN_COMPLETE] = il_hdl_scan_complete; +} +EXPORT_SYMBOL(il_setup_rx_scan_handlers); + +u16 +il_get_active_dwell_time(struct il_priv *il, enum ieee80211_band band, + u8 n_probes) +{ + if (band == IEEE80211_BAND_5GHZ) + return IL_ACTIVE_DWELL_TIME_52 + + IL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); + else + return IL_ACTIVE_DWELL_TIME_24 + + IL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); +} +EXPORT_SYMBOL(il_get_active_dwell_time); + +u16 +il_get_passive_dwell_time(struct il_priv *il, enum ieee80211_band band, + struct ieee80211_vif *vif) +{ + u16 value; + + u16 passive = + (band == + IEEE80211_BAND_2GHZ) ? IL_PASSIVE_DWELL_BASE + + IL_PASSIVE_DWELL_TIME_24 : IL_PASSIVE_DWELL_BASE + + IL_PASSIVE_DWELL_TIME_52; + + if (il_is_any_associated(il)) { + /* + * If we're associated, we clamp the maximum passive + * dwell time to be 98% of the smallest beacon interval + * (minus 2 * channel tune time) + */ + value = il->vif ? il->vif->bss_conf.beacon_int : 0; + if (value > IL_PASSIVE_DWELL_BASE || !value) + value = IL_PASSIVE_DWELL_BASE; + value = (value * 98) / 100 - IL_CHANNEL_TUNE_TIME * 2; + passive = min(value, passive); + } + + return passive; +} +EXPORT_SYMBOL(il_get_passive_dwell_time); + +void +il_init_scan_params(struct il_priv *il) +{ + u8 ant_idx = fls(il->hw_params.valid_tx_ant) - 1; + if (!il->scan_tx_ant[IEEE80211_BAND_5GHZ]) + il->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx; + if (!il->scan_tx_ant[IEEE80211_BAND_2GHZ]) + il->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; +} +EXPORT_SYMBOL(il_init_scan_params); + +static int +il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif) +{ + int ret; + + lockdep_assert_held(&il->mutex); + + cancel_delayed_work(&il->scan_check); + + if (!il_is_ready_rf(il)) { + IL_WARN("Request scan called when driver not ready.\n"); + return -EIO; + } + + if (test_bit(S_SCAN_HW, &il->status)) { + D_SCAN("Multiple concurrent scan requests in parallel.\n"); + return -EBUSY; + } + + if (test_bit(S_SCAN_ABORTING, &il->status)) { + D_SCAN("Scan request while abort pending.\n"); + return -EBUSY; + } + + D_SCAN("Starting scan...\n"); + + set_bit(S_SCANNING, &il->status); + il->scan_start = jiffies; + + ret = il->ops->request_scan(il, vif); + if (ret) { + clear_bit(S_SCANNING, &il->status); + return ret; + } + + queue_delayed_work(il->workqueue, &il->scan_check, + IL_SCAN_CHECK_WATCHDOG); + + return 0; +} + +int +il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct cfg80211_scan_request *req = &hw_req->req; + struct il_priv *il = hw->priv; + int ret; + + if (req->n_channels == 0) { + IL_ERR("Can not scan on no channels.\n"); + return -EINVAL; + } + + mutex_lock(&il->mutex); + D_MAC80211("enter\n"); + + if (test_bit(S_SCANNING, &il->status)) { + D_SCAN("Scan already in progress.\n"); + ret = -EAGAIN; + goto out_unlock; + } + + /* mac80211 will only ask for one band at a time */ + il->scan_request = req; + il->scan_vif = vif; + il->scan_band = req->channels[0]->band; + + ret = il_scan_initiate(il, vif); + +out_unlock: + D_MAC80211("leave ret %d\n", ret); + mutex_unlock(&il->mutex); + + return ret; +} +EXPORT_SYMBOL(il_mac_hw_scan); + +static void +il_bg_scan_check(struct work_struct *data) +{ + struct il_priv *il = + container_of(data, struct il_priv, scan_check.work); + + D_SCAN("Scan check work\n"); + + /* Since we are here firmware does not finish scan and + * most likely is in bad shape, so we don't bother to + * send abort command, just force scan complete to mac80211 */ + mutex_lock(&il->mutex); + il_force_scan_end(il); + mutex_unlock(&il->mutex); +} + +/** + * il_fill_probe_req - fill in all required fields and IE for probe request + */ + +u16 +il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, + const u8 *ta, const u8 *ies, int ie_len, int left) +{ + int len = 0; + u8 *pos = NULL; + + /* Make sure there is enough space for the probe request, + * two mandatory IEs and the data */ + left -= 24; + if (left < 0) + return 0; + + frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); + eth_broadcast_addr(frame->da); + memcpy(frame->sa, ta, ETH_ALEN); + eth_broadcast_addr(frame->bssid); + frame->seq_ctrl = 0; + + len += 24; + + /* ...next IE... */ + pos = &frame->u.probe_req.variable[0]; + + /* fill in our indirect SSID IE */ + left -= 2; + if (left < 0) + return 0; + *pos++ = WLAN_EID_SSID; + *pos++ = 0; + + len += 2; + + if (WARN_ON(left < ie_len)) + return len; + + if (ies && ie_len) { + memcpy(pos, ies, ie_len); + len += ie_len; + } + + return (u16) len; +} +EXPORT_SYMBOL(il_fill_probe_req); + +static void +il_bg_abort_scan(struct work_struct *work) +{ + struct il_priv *il = container_of(work, struct il_priv, abort_scan); + + D_SCAN("Abort scan work\n"); + + /* We keep scan_check work queued in case when firmware will not + * report back scan completed notification */ + mutex_lock(&il->mutex); + il_scan_cancel_timeout(il, 200); + mutex_unlock(&il->mutex); +} + +static void +il_bg_scan_completed(struct work_struct *work) +{ + struct il_priv *il = container_of(work, struct il_priv, scan_completed); + bool aborted; + + D_SCAN("Completed scan.\n"); + + cancel_delayed_work(&il->scan_check); + + mutex_lock(&il->mutex); + + aborted = test_and_clear_bit(S_SCAN_ABORTING, &il->status); + if (aborted) + D_SCAN("Aborted scan completed.\n"); + + if (!test_and_clear_bit(S_SCANNING, &il->status)) { + D_SCAN("Scan already completed.\n"); + goto out_settings; + } + + il_complete_scan(il, aborted); + +out_settings: + /* Can we still talk to firmware ? */ + if (!il_is_ready_rf(il)) + goto out; + + /* + * We do not commit power settings while scan is pending, + * do it now if the settings changed. + */ + il_power_set_mode(il, &il->power_data.sleep_cmd_next, false); + il_set_tx_power(il, il->tx_power_next, false); + + il->ops->post_scan(il); + +out: + mutex_unlock(&il->mutex); +} + +void +il_setup_scan_deferred_work(struct il_priv *il) +{ + INIT_WORK(&il->scan_completed, il_bg_scan_completed); + INIT_WORK(&il->abort_scan, il_bg_abort_scan); + INIT_DELAYED_WORK(&il->scan_check, il_bg_scan_check); +} +EXPORT_SYMBOL(il_setup_scan_deferred_work); + +void +il_cancel_scan_deferred_work(struct il_priv *il) +{ + cancel_work_sync(&il->abort_scan); + cancel_work_sync(&il->scan_completed); + + if (cancel_delayed_work_sync(&il->scan_check)) { + mutex_lock(&il->mutex); + il_force_scan_end(il); + mutex_unlock(&il->mutex); + } +} +EXPORT_SYMBOL(il_cancel_scan_deferred_work); + +/* il->sta_lock must be held */ +static void +il_sta_ucode_activate(struct il_priv *il, u8 sta_id) +{ + + if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) + IL_ERR("ACTIVATE a non DRIVER active station id %u addr %pM\n", + sta_id, il->stations[sta_id].sta.sta.addr); + + if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) { + D_ASSOC("STA id %u addr %pM already present" + " in uCode (according to driver)\n", sta_id, + il->stations[sta_id].sta.sta.addr); + } else { + il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE; + D_ASSOC("Added STA id %u addr %pM to uCode\n", sta_id, + il->stations[sta_id].sta.sta.addr); + } +} + +static int +il_process_add_sta_resp(struct il_priv *il, struct il_addsta_cmd *addsta, + struct il_rx_pkt *pkt, bool sync) +{ + u8 sta_id = addsta->sta.sta_id; + unsigned long flags; + int ret = -EIO; + + if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { + IL_ERR("Bad return from C_ADD_STA (0x%08X)\n", pkt->hdr.flags); + return ret; + } + + D_INFO("Processing response for adding station %u\n", sta_id); + + spin_lock_irqsave(&il->sta_lock, flags); + + switch (pkt->u.add_sta.status) { + case ADD_STA_SUCCESS_MSK: + D_INFO("C_ADD_STA PASSED\n"); + il_sta_ucode_activate(il, sta_id); + ret = 0; + break; + case ADD_STA_NO_ROOM_IN_TBL: + IL_ERR("Adding station %d failed, no room in table.\n", sta_id); + break; + case ADD_STA_NO_BLOCK_ACK_RESOURCE: + IL_ERR("Adding station %d failed, no block ack resource.\n", + sta_id); + break; + case ADD_STA_MODIFY_NON_EXIST_STA: + IL_ERR("Attempting to modify non-existing station %d\n", + sta_id); + break; + default: + D_ASSOC("Received C_ADD_STA:(0x%08X)\n", pkt->u.add_sta.status); + break; + } + + D_INFO("%s station id %u addr %pM\n", + il->stations[sta_id].sta.mode == + STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", sta_id, + il->stations[sta_id].sta.sta.addr); + + /* + * XXX: The MAC address in the command buffer is often changed from + * the original sent to the device. That is, the MAC address + * written to the command buffer often is not the same MAC address + * read from the command buffer when the command returns. This + * issue has not yet been resolved and this debugging is left to + * observe the problem. + */ + D_INFO("%s station according to cmd buffer %pM\n", + il->stations[sta_id].sta.mode == + STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr); + spin_unlock_irqrestore(&il->sta_lock, flags); + + return ret; +} + +static void +il_add_sta_callback(struct il_priv *il, struct il_device_cmd *cmd, + struct il_rx_pkt *pkt) +{ + struct il_addsta_cmd *addsta = (struct il_addsta_cmd *)cmd->cmd.payload; + + il_process_add_sta_resp(il, addsta, pkt, false); + +} + +int +il_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags) +{ + struct il_rx_pkt *pkt = NULL; + int ret = 0; + u8 data[sizeof(*sta)]; + struct il_host_cmd cmd = { + .id = C_ADD_STA, + .flags = flags, + .data = data, + }; + u8 sta_id __maybe_unused = sta->sta.sta_id; + + D_INFO("Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr, + flags & CMD_ASYNC ? "a" : ""); + + if (flags & CMD_ASYNC) + cmd.callback = il_add_sta_callback; + else { + cmd.flags |= CMD_WANT_SKB; + might_sleep(); + } + + cmd.len = il->ops->build_addsta_hcmd(sta, data); + ret = il_send_cmd(il, &cmd); + + if (ret || (flags & CMD_ASYNC)) + return ret; + + if (ret == 0) { + pkt = (struct il_rx_pkt *)cmd.reply_page; + ret = il_process_add_sta_resp(il, sta, pkt, true); + } + il_free_pages(il, cmd.reply_page); + + return ret; +} +EXPORT_SYMBOL(il_send_add_sta); + +static void +il_set_ht_add_station(struct il_priv *il, u8 idx, struct ieee80211_sta *sta) +{ + struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; + __le32 sta_flags; + + if (!sta || !sta_ht_inf->ht_supported) + goto done; + + D_ASSOC("spatial multiplexing power save mode: %s\n", + (sta->smps_mode == IEEE80211_SMPS_STATIC) ? "static" : + (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ? "dynamic" : + "disabled"); + + sta_flags = il->stations[idx].sta.station_flags; + + sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK); + + switch (sta->smps_mode) { + case IEEE80211_SMPS_STATIC: + sta_flags |= STA_FLG_MIMO_DIS_MSK; + break; + case IEEE80211_SMPS_DYNAMIC: + sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; + break; + case IEEE80211_SMPS_OFF: + break; + default: + IL_WARN("Invalid MIMO PS mode %d\n", sta->smps_mode); + break; + } + + sta_flags |= + cpu_to_le32((u32) sta_ht_inf-> + ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); + + sta_flags |= + cpu_to_le32((u32) sta_ht_inf-> + ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); + + if (il_is_ht40_tx_allowed(il, &sta->ht_cap)) + sta_flags |= STA_FLG_HT40_EN_MSK; + else + sta_flags &= ~STA_FLG_HT40_EN_MSK; + + il->stations[idx].sta.station_flags = sta_flags; +done: + return; +} + +/** + * il_prep_station - Prepare station information for addition + * + * should be called with sta_lock held + */ +u8 +il_prep_station(struct il_priv *il, const u8 *addr, bool is_ap, + struct ieee80211_sta *sta) +{ + struct il_station_entry *station; + int i; + u8 sta_id = IL_INVALID_STATION; + u16 rate; + + if (is_ap) + sta_id = IL_AP_ID; + else if (is_broadcast_ether_addr(addr)) + sta_id = il->hw_params.bcast_id; + else + for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) { + if (ether_addr_equal(il->stations[i].sta.sta.addr, + addr)) { + sta_id = i; + break; + } + + if (!il->stations[i].used && + sta_id == IL_INVALID_STATION) + sta_id = i; + } + + /* + * These two conditions have the same outcome, but keep them + * separate + */ + if (unlikely(sta_id == IL_INVALID_STATION)) + return sta_id; + + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { + D_INFO("STA %d already in process of being added.\n", sta_id); + return sta_id; + } + + if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && + (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) && + ether_addr_equal(il->stations[sta_id].sta.sta.addr, addr)) { + D_ASSOC("STA %d (%pM) already added, not adding again.\n", + sta_id, addr); + return sta_id; + } + + station = &il->stations[sta_id]; + station->used = IL_STA_DRIVER_ACTIVE; + D_ASSOC("Add STA to driver ID %d: %pM\n", sta_id, addr); + il->num_stations++; + + /* Set up the C_ADD_STA command to send to device */ + memset(&station->sta, 0, sizeof(struct il_addsta_cmd)); + memcpy(station->sta.sta.addr, addr, ETH_ALEN); + station->sta.mode = 0; + station->sta.sta.sta_id = sta_id; + station->sta.station_flags = 0; + + /* + * OK to call unconditionally, since local stations (IBSS BSSID + * STA and broadcast STA) pass in a NULL sta, and mac80211 + * doesn't allow HT IBSS. + */ + il_set_ht_add_station(il, sta_id, sta); + + /* 3945 only */ + rate = (il->band == IEEE80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP; + /* Turn on both antennas for the station... */ + station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK); + + return sta_id; + +} +EXPORT_SYMBOL_GPL(il_prep_station); + +#define STA_WAIT_TIMEOUT (HZ/2) + +/** + * il_add_station_common - + */ +int +il_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap, + struct ieee80211_sta *sta, u8 *sta_id_r) +{ + unsigned long flags_spin; + int ret = 0; + u8 sta_id; + struct il_addsta_cmd sta_cmd; + + *sta_id_r = 0; + spin_lock_irqsave(&il->sta_lock, flags_spin); + sta_id = il_prep_station(il, addr, is_ap, sta); + if (sta_id == IL_INVALID_STATION) { + IL_ERR("Unable to prepare station %pM for addition\n", addr); + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + return -EINVAL; + } + + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { + D_INFO("STA %d already in process of being added.\n", sta_id); + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + return -EEXIST; + } + + if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && + (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { + D_ASSOC("STA %d (%pM) already added, not adding again.\n", + sta_id, addr); + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + return -EEXIST; + } + + il->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS; + memcpy(&sta_cmd, &il->stations[sta_id].sta, + sizeof(struct il_addsta_cmd)); + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + + /* Add station to device's station table */ + ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); + if (ret) { + spin_lock_irqsave(&il->sta_lock, flags_spin); + IL_ERR("Adding station %pM failed.\n", + il->stations[sta_id].sta.sta.addr); + il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; + il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + } + *sta_id_r = sta_id; + return ret; +} +EXPORT_SYMBOL(il_add_station_common); + +/** + * il_sta_ucode_deactivate - deactivate ucode status for a station + * + * il->sta_lock must be held + */ +static void +il_sta_ucode_deactivate(struct il_priv *il, u8 sta_id) +{ + /* Ucode must be active and driver must be non active */ + if ((il->stations[sta_id]. + used & (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) != + IL_STA_UCODE_ACTIVE) + IL_ERR("removed non active STA %u\n", sta_id); + + il->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE; + + memset(&il->stations[sta_id], 0, sizeof(struct il_station_entry)); + D_ASSOC("Removed STA %u\n", sta_id); +} + +static int +il_send_remove_station(struct il_priv *il, const u8 * addr, int sta_id, + bool temporary) +{ + struct il_rx_pkt *pkt; + int ret; + + unsigned long flags_spin; + struct il_rem_sta_cmd rm_sta_cmd; + + struct il_host_cmd cmd = { + .id = C_REM_STA, + .len = sizeof(struct il_rem_sta_cmd), + .flags = CMD_SYNC, + .data = &rm_sta_cmd, + }; + + memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); + rm_sta_cmd.num_sta = 1; + memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); + + cmd.flags |= CMD_WANT_SKB; + + ret = il_send_cmd(il, &cmd); + + if (ret) + return ret; + + pkt = (struct il_rx_pkt *)cmd.reply_page; + if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { + IL_ERR("Bad return from C_REM_STA (0x%08X)\n", pkt->hdr.flags); + ret = -EIO; + } + + if (!ret) { + switch (pkt->u.rem_sta.status) { + case REM_STA_SUCCESS_MSK: + if (!temporary) { + spin_lock_irqsave(&il->sta_lock, flags_spin); + il_sta_ucode_deactivate(il, sta_id); + spin_unlock_irqrestore(&il->sta_lock, + flags_spin); + } + D_ASSOC("C_REM_STA PASSED\n"); + break; + default: + ret = -EIO; + IL_ERR("C_REM_STA failed\n"); + break; + } + } + il_free_pages(il, cmd.reply_page); + + return ret; +} + +/** + * il_remove_station - Remove driver's knowledge of station. + */ +int +il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr) +{ + unsigned long flags; + + if (!il_is_ready(il)) { + D_INFO("Unable to remove station %pM, device not ready.\n", + addr); + /* + * It is typical for stations to be removed when we are + * going down. Return success since device will be down + * soon anyway + */ + return 0; + } + + D_ASSOC("Removing STA from driver:%d %pM\n", sta_id, addr); + + if (WARN_ON(sta_id == IL_INVALID_STATION)) + return -EINVAL; + + spin_lock_irqsave(&il->sta_lock, flags); + + if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) { + D_INFO("Removing %pM but non DRIVER active\n", addr); + goto out_err; + } + + if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { + D_INFO("Removing %pM but non UCODE active\n", addr); + goto out_err; + } + + if (il->stations[sta_id].used & IL_STA_LOCAL) { + kfree(il->stations[sta_id].lq); + il->stations[sta_id].lq = NULL; + } + + il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; + + il->num_stations--; + + BUG_ON(il->num_stations < 0); + + spin_unlock_irqrestore(&il->sta_lock, flags); + + return il_send_remove_station(il, addr, sta_id, false); +out_err: + spin_unlock_irqrestore(&il->sta_lock, flags); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(il_remove_station); + +/** + * il_clear_ucode_stations - clear ucode station table bits + * + * This function clears all the bits in the driver indicating + * which stations are active in the ucode. Call when something + * other than explicit station management would cause this in + * the ucode, e.g. unassociated RXON. + */ +void +il_clear_ucode_stations(struct il_priv *il) +{ + int i; + unsigned long flags_spin; + bool cleared = false; + + D_INFO("Clearing ucode stations in driver\n"); + + spin_lock_irqsave(&il->sta_lock, flags_spin); + for (i = 0; i < il->hw_params.max_stations; i++) { + if (il->stations[i].used & IL_STA_UCODE_ACTIVE) { + D_INFO("Clearing ucode active for station %d\n", i); + il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; + cleared = true; + } + } + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + + if (!cleared) + D_INFO("No active stations found to be cleared\n"); +} +EXPORT_SYMBOL(il_clear_ucode_stations); + +/** + * il_restore_stations() - Restore driver known stations to device + * + * All stations considered active by driver, but not present in ucode, is + * restored. + * + * Function sleeps. + */ +void +il_restore_stations(struct il_priv *il) +{ + struct il_addsta_cmd sta_cmd; + struct il_link_quality_cmd lq; + unsigned long flags_spin; + int i; + bool found = false; + int ret; + bool send_lq; + + if (!il_is_ready(il)) { + D_INFO("Not ready yet, not restoring any stations.\n"); + return; + } + + D_ASSOC("Restoring all known stations ... start.\n"); + spin_lock_irqsave(&il->sta_lock, flags_spin); + for (i = 0; i < il->hw_params.max_stations; i++) { + if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) && + !(il->stations[i].used & IL_STA_UCODE_ACTIVE)) { + D_ASSOC("Restoring sta %pM\n", + il->stations[i].sta.sta.addr); + il->stations[i].sta.mode = 0; + il->stations[i].used |= IL_STA_UCODE_INPROGRESS; + found = true; + } + } + + for (i = 0; i < il->hw_params.max_stations; i++) { + if ((il->stations[i].used & IL_STA_UCODE_INPROGRESS)) { + memcpy(&sta_cmd, &il->stations[i].sta, + sizeof(struct il_addsta_cmd)); + send_lq = false; + if (il->stations[i].lq) { + memcpy(&lq, il->stations[i].lq, + sizeof(struct il_link_quality_cmd)); + send_lq = true; + } + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); + if (ret) { + spin_lock_irqsave(&il->sta_lock, flags_spin); + IL_ERR("Adding station %pM failed.\n", + il->stations[i].sta.sta.addr); + il->stations[i].used &= ~IL_STA_DRIVER_ACTIVE; + il->stations[i].used &= + ~IL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&il->sta_lock, + flags_spin); + } + /* + * Rate scaling has already been initialized, send + * current LQ command + */ + if (send_lq) + il_send_lq_cmd(il, &lq, CMD_SYNC, true); + spin_lock_irqsave(&il->sta_lock, flags_spin); + il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS; + } + } + + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + if (!found) + D_INFO("Restoring all known stations" + " .... no stations to be restored.\n"); + else + D_INFO("Restoring all known stations" " .... complete.\n"); +} +EXPORT_SYMBOL(il_restore_stations); + +int +il_get_free_ucode_key_idx(struct il_priv *il) +{ + int i; + + for (i = 0; i < il->sta_key_max_num; i++) + if (!test_and_set_bit(i, &il->ucode_key_table)) + return i; + + return WEP_INVALID_OFFSET; +} +EXPORT_SYMBOL(il_get_free_ucode_key_idx); + +void +il_dealloc_bcast_stations(struct il_priv *il) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&il->sta_lock, flags); + for (i = 0; i < il->hw_params.max_stations; i++) { + if (!(il->stations[i].used & IL_STA_BCAST)) + continue; + + il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; + il->num_stations--; + BUG_ON(il->num_stations < 0); + kfree(il->stations[i].lq); + il->stations[i].lq = NULL; + } + spin_unlock_irqrestore(&il->sta_lock, flags); +} +EXPORT_SYMBOL_GPL(il_dealloc_bcast_stations); + +#ifdef CONFIG_IWLEGACY_DEBUG +static void +il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq) +{ + int i; + D_RATE("lq station id 0x%x\n", lq->sta_id); + D_RATE("lq ant 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk, + lq->general_params.dual_stream_ant_msk); + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) + D_RATE("lq idx %d 0x%X\n", i, lq->rs_table[i].rate_n_flags); +} +#else +static inline void +il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq) +{ +} +#endif + +/** + * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity + * + * It sometimes happens when a HT rate has been in use and we + * loose connectivity with AP then mac80211 will first tell us that the + * current channel is not HT anymore before removing the station. In such a + * scenario the RXON flags will be updated to indicate we are not + * communicating HT anymore, but the LQ command may still contain HT rates. + * Test for this to prevent driver from sending LQ command between the time + * RXON flags are updated and when LQ command is updated. + */ +static bool +il_is_lq_table_valid(struct il_priv *il, struct il_link_quality_cmd *lq) +{ + int i; + + if (il->ht.enabled) + return true; + + D_INFO("Channel %u is not an HT channel\n", il->active.channel); + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) { + D_INFO("idx %d of LQ expects HT channel\n", i); + return false; + } + } + return true; +} + +/** + * il_send_lq_cmd() - Send link quality command + * @init: This command is sent as part of station initialization right + * after station has been added. + * + * The link quality command is sent as the last step of station creation. + * This is the special case in which init is set and we call a callback in + * this case to clear the state indicating that station creation is in + * progress. + */ +int +il_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq, + u8 flags, bool init) +{ + int ret = 0; + unsigned long flags_spin; + + struct il_host_cmd cmd = { + .id = C_TX_LINK_QUALITY_CMD, + .len = sizeof(struct il_link_quality_cmd), + .flags = flags, + .data = lq, + }; + + if (WARN_ON(lq->sta_id == IL_INVALID_STATION)) + return -EINVAL; + + spin_lock_irqsave(&il->sta_lock, flags_spin); + if (!(il->stations[lq->sta_id].used & IL_STA_DRIVER_ACTIVE)) { + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + return -EINVAL; + } + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + + il_dump_lq_cmd(il, lq); + BUG_ON(init && (cmd.flags & CMD_ASYNC)); + + if (il_is_lq_table_valid(il, lq)) + ret = il_send_cmd(il, &cmd); + else + ret = -EINVAL; + + if (cmd.flags & CMD_ASYNC) + return ret; + + if (init) { + D_INFO("init LQ command complete," + " clearing sta addition status for sta %d\n", + lq->sta_id); + spin_lock_irqsave(&il->sta_lock, flags_spin); + il->stations[lq->sta_id].used &= ~IL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&il->sta_lock, flags_spin); + } + return ret; +} +EXPORT_SYMBOL(il_send_lq_cmd); + +int +il_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct il_priv *il = hw->priv; + struct il_station_priv_common *sta_common = (void *)sta->drv_priv; + int ret; + + mutex_lock(&il->mutex); + D_MAC80211("enter station %pM\n", sta->addr); + + ret = il_remove_station(il, sta_common->sta_id, sta->addr); + if (ret) + IL_ERR("Error removing station %pM\n", sta->addr); + + D_MAC80211("leave ret %d\n", ret); + mutex_unlock(&il->mutex); + + return ret; +} +EXPORT_SYMBOL(il_mac_sta_remove); + +/************************** RX-FUNCTIONS ****************************/ +/* + * Rx theory of operation + * + * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), + * each of which point to Receive Buffers to be filled by the NIC. These get + * used not only for Rx frames, but for any command response or notification + * from the NIC. The driver and NIC manage the Rx buffers by means + * of idxes into the circular buffer. + * + * Rx Queue Indexes + * The host/firmware share two idx registers for managing the Rx buffers. + * + * The READ idx maps to the first position that the firmware may be writing + * to -- the driver can read up to (but not including) this position and get + * good data. + * The READ idx is managed by the firmware once the card is enabled. + * + * The WRITE idx maps to the last position the driver has read from -- the + * position preceding WRITE is the last slot the firmware can place a packet. + * + * The queue is empty (no good data) if WRITE = READ - 1, and is full if + * WRITE = READ. + * + * During initialization, the host sets up the READ queue position to the first + * IDX position, and WRITE to the last (READ - 1 wrapped) + * + * When the firmware places a packet in a buffer, it will advance the READ idx + * and fire the RX interrupt. The driver can then query the READ idx and + * process as many packets as possible, moving the WRITE idx forward as it + * resets the Rx queue buffers with new memory. + * + * The management in the driver is as follows: + * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When + * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled + * to replenish the iwl->rxq->rx_free. + * + In il_rx_replenish (scheduled) if 'processed' != 'read' then the + * iwl->rxq is replenished and the READ IDX is updated (updating the + * 'processed' and 'read' driver idxes as well) + * + A received packet is processed and handed to the kernel network stack, + * detached from the iwl->rxq. The driver 'processed' idx is updated. + * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free + * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ + * IDX is not incremented and iwl->status(RX_STALLED) is set. If there + * were enough free buffers and RX_STALLED is set it is cleared. + * + * + * Driver sequence: + * + * il_rx_queue_alloc() Allocates rx_free + * il_rx_replenish() Replenishes rx_free list from rx_used, and calls + * il_rx_queue_restock + * il_rx_queue_restock() Moves available buffers from rx_free into Rx + * queue, updates firmware pointers, and updates + * the WRITE idx. If insufficient rx_free buffers + * are available, schedules il_rx_replenish + * + * -- enable interrupts -- + * ISR - il_rx() Detach il_rx_bufs from pool up to the + * READ IDX, detaching the SKB from the pool. + * Moves the packet buffer from queue to rx_used. + * Calls il_rx_queue_restock to refill any empty + * slots. + * ... + * + */ + +/** + * il_rx_queue_space - Return number of free slots available in queue. + */ +int +il_rx_queue_space(const struct il_rx_queue *q) +{ + int s = q->read - q->write; + if (s <= 0) + s += RX_QUEUE_SIZE; + /* keep some buffer to not confuse full and empty queue */ + s -= 2; + if (s < 0) + s = 0; + return s; +} +EXPORT_SYMBOL(il_rx_queue_space); + +/** + * il_rx_queue_update_write_ptr - Update the write pointer for the RX queue + */ +void +il_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q) +{ + unsigned long flags; + u32 rx_wrt_ptr_reg = il->hw_params.rx_wrt_ptr_reg; + u32 reg; + + spin_lock_irqsave(&q->lock, flags); + + if (q->need_update == 0) + goto exit_unlock; + + /* If power-saving is in use, make sure device is awake */ + if (test_bit(S_POWER_PMI, &il->status)) { + reg = _il_rd(il, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + D_INFO("Rx queue requesting wakeup," " GP1 = 0x%x\n", + reg); + il_set_bit(il, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + goto exit_unlock; + } + + q->write_actual = (q->write & ~0x7); + il_wr(il, rx_wrt_ptr_reg, q->write_actual); + + /* Else device is assumed to be awake */ + } else { + /* Device expects a multiple of 8 */ + q->write_actual = (q->write & ~0x7); + il_wr(il, rx_wrt_ptr_reg, q->write_actual); + } + + q->need_update = 0; + +exit_unlock: + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(il_rx_queue_update_write_ptr); + +int +il_rx_queue_alloc(struct il_priv *il) +{ + struct il_rx_queue *rxq = &il->rxq; + struct device *dev = &il->pci_dev->dev; + int i; + + spin_lock_init(&rxq->lock); + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + + /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */ + rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma, + GFP_KERNEL); + if (!rxq->bd) + goto err_bd; + + rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct il_rb_status), + &rxq->rb_stts_dma, GFP_KERNEL); + if (!rxq->rb_stts) + goto err_rb; + + /* Fill the rx_used queue with _all_ of the Rx buffers */ + for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) + list_add_tail(&rxq->pool[i].list, &rxq->rx_used); + + /* Set us so that we have processed and used all buffers, but have + * not restocked the Rx queue with fresh buffers */ + rxq->read = rxq->write = 0; + rxq->write_actual = 0; + rxq->free_count = 0; + rxq->need_update = 0; + return 0; + +err_rb: + dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, + rxq->bd_dma); +err_bd: + return -ENOMEM; +} +EXPORT_SYMBOL(il_rx_queue_alloc); + +void +il_hdl_spectrum_measurement(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_spectrum_notification *report = &(pkt->u.spectrum_notif); + + if (!report->state) { + D_11H("Spectrum Measure Notification: Start\n"); + return; + } + + memcpy(&il->measure_report, report, sizeof(*report)); + il->measurement_status |= MEASUREMENT_READY; +} +EXPORT_SYMBOL(il_hdl_spectrum_measurement); + +/* + * returns non-zero if packet should be dropped + */ +int +il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr, + u32 decrypt_res, struct ieee80211_rx_status *stats) +{ + u16 fc = le16_to_cpu(hdr->frame_control); + + /* + * All contexts have the same setting here due to it being + * a module parameter, so OK to check any context. + */ + if (il->active.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK) + return 0; + + if (!(fc & IEEE80211_FCTL_PROTECTED)) + return 0; + + D_RX("decrypt_res:0x%x\n", decrypt_res); + switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { + case RX_RES_STATUS_SEC_TYPE_TKIP: + /* The uCode has got a bad phase 1 Key, pushes the packet. + * Decryption will be done in SW. */ + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_BAD_KEY_TTAK) + break; + + case RX_RES_STATUS_SEC_TYPE_WEP: + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_BAD_ICV_MIC) { + /* bad ICV, the packet is destroyed since the + * decryption is inplace, drop it */ + D_RX("Packet destroyed\n"); + return -1; + } + case RX_RES_STATUS_SEC_TYPE_CCMP: + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_DECRYPT_OK) { + D_RX("hw decrypt successfully!!!\n"); + stats->flag |= RX_FLAG_DECRYPTED; + } + break; + + default: + break; + } + return 0; +} +EXPORT_SYMBOL(il_set_decrypted_flag); + +/** + * il_txq_update_write_ptr - Send new write idx to hardware + */ +void +il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq) +{ + u32 reg = 0; + int txq_id = txq->q.id; + + if (txq->need_update == 0) + return; + + /* if we're trying to save power */ + if (test_bit(S_POWER_PMI, &il->status)) { + /* wake up nic if it's powered down ... + * uCode will wake up, and interrupt us again, so next + * time we'll skip this part. */ + reg = _il_rd(il, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + D_INFO("Tx queue %d requesting wakeup," " GP1 = 0x%x\n", + txq_id, reg); + il_set_bit(il, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + return; + } + + il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); + + /* + * else not in power-save mode, + * uCode will never sleep when we're + * trying to tx (during RFKILL, we're not trying to tx). + */ + } else + _il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); + txq->need_update = 0; +} +EXPORT_SYMBOL(il_txq_update_write_ptr); + +/** + * il_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's + */ +void +il_tx_queue_unmap(struct il_priv *il, int txq_id) +{ + struct il_tx_queue *txq = &il->txq[txq_id]; + struct il_queue *q = &txq->q; + + if (q->n_bd == 0) + return; + + while (q->write_ptr != q->read_ptr) { + il->ops->txq_free_tfd(il, txq); + q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd); + } +} +EXPORT_SYMBOL(il_tx_queue_unmap); + +/** + * il_tx_queue_free - Deallocate DMA queue. + * @txq: Transmit queue to deallocate. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * 0-fill, but do not free "txq" descriptor structure. + */ +void +il_tx_queue_free(struct il_priv *il, int txq_id) +{ + struct il_tx_queue *txq = &il->txq[txq_id]; + struct device *dev = &il->pci_dev->dev; + int i; + + il_tx_queue_unmap(il, txq_id); + + /* De-alloc array of command/tx buffers */ + for (i = 0; i < TFD_TX_CMD_SLOTS; i++) + kfree(txq->cmd[i]); + + /* De-alloc circular buffer of TFDs */ + if (txq->q.n_bd) + dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd, + txq->tfds, txq->q.dma_addr); + + /* De-alloc array of per-TFD driver data */ + kfree(txq->skbs); + txq->skbs = NULL; + + /* deallocate arrays */ + kfree(txq->cmd); + kfree(txq->meta); + txq->cmd = NULL; + txq->meta = NULL; + + /* 0-fill queue descriptor structure */ + memset(txq, 0, sizeof(*txq)); +} +EXPORT_SYMBOL(il_tx_queue_free); + +/** + * il_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue + */ +void +il_cmd_queue_unmap(struct il_priv *il) +{ + struct il_tx_queue *txq = &il->txq[il->cmd_queue]; + struct il_queue *q = &txq->q; + int i; + + if (q->n_bd == 0) + return; + + while (q->read_ptr != q->write_ptr) { + i = il_get_cmd_idx(q, q->read_ptr, 0); + + if (txq->meta[i].flags & CMD_MAPPED) { + pci_unmap_single(il->pci_dev, + dma_unmap_addr(&txq->meta[i], mapping), + dma_unmap_len(&txq->meta[i], len), + PCI_DMA_BIDIRECTIONAL); + txq->meta[i].flags = 0; + } + + q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd); + } + + i = q->n_win; + if (txq->meta[i].flags & CMD_MAPPED) { + pci_unmap_single(il->pci_dev, + dma_unmap_addr(&txq->meta[i], mapping), + dma_unmap_len(&txq->meta[i], len), + PCI_DMA_BIDIRECTIONAL); + txq->meta[i].flags = 0; + } +} +EXPORT_SYMBOL(il_cmd_queue_unmap); + +/** + * il_cmd_queue_free - Deallocate DMA queue. + * @txq: Transmit queue to deallocate. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * 0-fill, but do not free "txq" descriptor structure. + */ +void +il_cmd_queue_free(struct il_priv *il) +{ + struct il_tx_queue *txq = &il->txq[il->cmd_queue]; + struct device *dev = &il->pci_dev->dev; + int i; + + il_cmd_queue_unmap(il); + + /* De-alloc array of command/tx buffers */ + for (i = 0; i <= TFD_CMD_SLOTS; i++) + kfree(txq->cmd[i]); + + /* De-alloc circular buffer of TFDs */ + if (txq->q.n_bd) + dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd, + txq->tfds, txq->q.dma_addr); + + /* deallocate arrays */ + kfree(txq->cmd); + kfree(txq->meta); + txq->cmd = NULL; + txq->meta = NULL; + + /* 0-fill queue descriptor structure */ + memset(txq, 0, sizeof(*txq)); +} +EXPORT_SYMBOL(il_cmd_queue_free); + +/*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** + * DMA services + * + * Theory of operation + * + * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer + * of buffer descriptors, each of which points to one or more data buffers for + * the device to read from or fill. Driver and device exchange status of each + * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty + * entries in each circular buffer, to protect against confusing empty and full + * queue states. + * + * The device reads or writes the data in the queues via the device's several + * DMA/FIFO channels. Each queue is mapped to a single DMA channel. + * + * For Tx queue, there are low mark and high mark limits. If, after queuing + * the packet for Tx, free space become < low mark, Tx queue stopped. When + * reclaiming packets (on 'tx done IRQ), if free space become > high mark, + * Tx queue resumed. + * + * See more detailed info in 4965.h. + ***************************************************/ + +int +il_queue_space(const struct il_queue *q) +{ + int s = q->read_ptr - q->write_ptr; + + if (q->read_ptr > q->write_ptr) + s -= q->n_bd; + + if (s <= 0) + s += q->n_win; + /* keep some reserve to not confuse empty and full situations */ + s -= 2; + if (s < 0) + s = 0; + return s; +} +EXPORT_SYMBOL(il_queue_space); + + +/** + * il_queue_init - Initialize queue's high/low-water and read/write idxes + */ +static int +il_queue_init(struct il_priv *il, struct il_queue *q, int slots, u32 id) +{ + /* + * TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise + * il_queue_inc_wrap and il_queue_dec_wrap are broken. + */ + BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); + /* FIXME: remove q->n_bd */ + q->n_bd = TFD_QUEUE_SIZE_MAX; + + q->n_win = slots; + q->id = id; + + /* slots_must be power-of-two size, otherwise + * il_get_cmd_idx is broken. */ + BUG_ON(!is_power_of_2(slots)); + + q->low_mark = q->n_win / 4; + if (q->low_mark < 4) + q->low_mark = 4; + + q->high_mark = q->n_win / 8; + if (q->high_mark < 2) + q->high_mark = 2; + + q->write_ptr = q->read_ptr = 0; + + return 0; +} + +/** + * il_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue + */ +static int +il_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id) +{ + struct device *dev = &il->pci_dev->dev; + size_t tfd_sz = il->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX; + + /* Driver ilate data, only for Tx (not command) queues, + * not shared with device. */ + if (id != il->cmd_queue) { + txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX, + sizeof(struct sk_buff *), + GFP_KERNEL); + if (!txq->skbs) { + IL_ERR("Fail to alloc skbs\n"); + goto error; + } + } else + txq->skbs = NULL; + + /* Circular buffer of transmit frame descriptors (TFDs), + * shared with device */ + txq->tfds = + dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, GFP_KERNEL); + if (!txq->tfds) + goto error; + + txq->q.id = id; + + return 0; + +error: + kfree(txq->skbs); + txq->skbs = NULL; + + return -ENOMEM; +} + +/** + * il_tx_queue_init - Allocate and initialize one tx/cmd queue + */ +int +il_tx_queue_init(struct il_priv *il, u32 txq_id) +{ + int i, len, ret; + int slots, actual_slots; + struct il_tx_queue *txq = &il->txq[txq_id]; + + /* + * Alloc buffer array for commands (Tx or other types of commands). + * For the command queue (#4/#9), allocate command space + one big + * command for scan, since scan command is very huge; the system will + * not have two scans at the same time, so only one is needed. + * For normal Tx queues (all other queues), no super-size command + * space is needed. + */ + if (txq_id == il->cmd_queue) { + slots = TFD_CMD_SLOTS; + actual_slots = slots + 1; + } else { + slots = TFD_TX_CMD_SLOTS; + actual_slots = slots; + } + + txq->meta = + kzalloc(sizeof(struct il_cmd_meta) * actual_slots, GFP_KERNEL); + txq->cmd = + kzalloc(sizeof(struct il_device_cmd *) * actual_slots, GFP_KERNEL); + + if (!txq->meta || !txq->cmd) + goto out_free_arrays; + + len = sizeof(struct il_device_cmd); + for (i = 0; i < actual_slots; i++) { + /* only happens for cmd queue */ + if (i == slots) + len = IL_MAX_CMD_SIZE; + + txq->cmd[i] = kmalloc(len, GFP_KERNEL); + if (!txq->cmd[i]) + goto err; + } + + /* Alloc driver data array and TFD circular buffer */ + ret = il_tx_queue_alloc(il, txq, txq_id); + if (ret) + goto err; + + txq->need_update = 0; + + /* + * For the default queues 0-3, set up the swq_id + * already -- all others need to get one later + * (if they need one at all). + */ + if (txq_id < 4) + il_set_swq_id(txq, txq_id, txq_id); + + /* Initialize queue's high/low-water marks, and head/tail idxes */ + il_queue_init(il, &txq->q, slots, txq_id); + + /* Tell device where to find queue */ + il->ops->txq_init(il, txq); + + return 0; +err: + for (i = 0; i < actual_slots; i++) + kfree(txq->cmd[i]); +out_free_arrays: + kfree(txq->meta); + kfree(txq->cmd); + + return -ENOMEM; +} +EXPORT_SYMBOL(il_tx_queue_init); + +void +il_tx_queue_reset(struct il_priv *il, u32 txq_id) +{ + int slots, actual_slots; + struct il_tx_queue *txq = &il->txq[txq_id]; + + if (txq_id == il->cmd_queue) { + slots = TFD_CMD_SLOTS; + actual_slots = TFD_CMD_SLOTS + 1; + } else { + slots = TFD_TX_CMD_SLOTS; + actual_slots = TFD_TX_CMD_SLOTS; + } + + memset(txq->meta, 0, sizeof(struct il_cmd_meta) * actual_slots); + txq->need_update = 0; + + /* Initialize queue's high/low-water marks, and head/tail idxes */ + il_queue_init(il, &txq->q, slots, txq_id); + + /* Tell device where to find queue */ + il->ops->txq_init(il, txq); +} +EXPORT_SYMBOL(il_tx_queue_reset); + +/*************** HOST COMMAND QUEUE FUNCTIONS *****/ + +/** + * il_enqueue_hcmd - enqueue a uCode command + * @il: device ilate data point + * @cmd: a point to the ucode command structure + * + * The function returns < 0 values to indicate the operation is + * failed. On success, it turns the idx (> 0) of command in the + * command queue. + */ +int +il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd) +{ + struct il_tx_queue *txq = &il->txq[il->cmd_queue]; + struct il_queue *q = &txq->q; + struct il_device_cmd *out_cmd; + struct il_cmd_meta *out_meta; + dma_addr_t phys_addr; + unsigned long flags; + int len; + u32 idx; + u16 fix_size; + + cmd->len = il->ops->get_hcmd_size(cmd->id, cmd->len); + fix_size = (u16) (cmd->len + sizeof(out_cmd->hdr)); + + /* If any of the command structures end up being larger than + * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then + * we will need to increase the size of the TFD entries + * Also, check to see if command buffer should not exceed the size + * of device_cmd and max_cmd_size. */ + BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && + !(cmd->flags & CMD_SIZE_HUGE)); + BUG_ON(fix_size > IL_MAX_CMD_SIZE); + + if (il_is_rfkill(il) || il_is_ctkill(il)) { + IL_WARN("Not sending command - %s KILL\n", + il_is_rfkill(il) ? "RF" : "CT"); + return -EIO; + } + + spin_lock_irqsave(&il->hcmd_lock, flags); + + if (il_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { + spin_unlock_irqrestore(&il->hcmd_lock, flags); + + IL_ERR("Restarting adapter due to command queue full\n"); + queue_work(il->workqueue, &il->restart); + return -ENOSPC; + } + + idx = il_get_cmd_idx(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); + out_cmd = txq->cmd[idx]; + out_meta = &txq->meta[idx]; + + if (WARN_ON(out_meta->flags & CMD_MAPPED)) { + spin_unlock_irqrestore(&il->hcmd_lock, flags); + return -ENOSPC; + } + + memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ + out_meta->flags = cmd->flags | CMD_MAPPED; + if (cmd->flags & CMD_WANT_SKB) + out_meta->source = cmd; + if (cmd->flags & CMD_ASYNC) + out_meta->callback = cmd->callback; + + out_cmd->hdr.cmd = cmd->id; + memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len); + + /* At this point, the out_cmd now has all of the incoming cmd + * information */ + + out_cmd->hdr.flags = 0; + out_cmd->hdr.sequence = + cpu_to_le16(QUEUE_TO_SEQ(il->cmd_queue) | IDX_TO_SEQ(q->write_ptr)); + if (cmd->flags & CMD_SIZE_HUGE) + out_cmd->hdr.sequence |= SEQ_HUGE_FRAME; + len = sizeof(struct il_device_cmd); + if (idx == TFD_CMD_SLOTS) + len = IL_MAX_CMD_SIZE; + +#ifdef CONFIG_IWLEGACY_DEBUG + switch (out_cmd->hdr.cmd) { + case C_TX_LINK_QUALITY_CMD: + case C_SENSITIVITY: + D_HC_DUMP("Sending command %s (#%x), seq: 0x%04X, " + "%d bytes at %d[%d]:%d\n", + il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, + le16_to_cpu(out_cmd->hdr.sequence), fix_size, + q->write_ptr, idx, il->cmd_queue); + break; + default: + D_HC("Sending command %s (#%x), seq: 0x%04X, " + "%d bytes at %d[%d]:%d\n", + il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, + le16_to_cpu(out_cmd->hdr.sequence), fix_size, q->write_ptr, + idx, il->cmd_queue); + } +#endif + + phys_addr = + pci_map_single(il->pci_dev, &out_cmd->hdr, fix_size, + PCI_DMA_BIDIRECTIONAL); + if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr))) { + idx = -ENOMEM; + goto out; + } + dma_unmap_addr_set(out_meta, mapping, phys_addr); + dma_unmap_len_set(out_meta, len, fix_size); + + txq->need_update = 1; + + if (il->ops->txq_update_byte_cnt_tbl) + /* Set up entry in queue's byte count circular buffer */ + il->ops->txq_update_byte_cnt_tbl(il, txq, 0); + + il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, fix_size, 1, + U32_PAD(cmd->len)); + + /* Increment and update queue's write idx */ + q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); + il_txq_update_write_ptr(il, txq); + +out: + spin_unlock_irqrestore(&il->hcmd_lock, flags); + return idx; +} + +/** + * il_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd + * + * When FW advances 'R' idx, all entries between old and new 'R' idx + * need to be reclaimed. As result, some free space forms. If there is + * enough free space (> low mark), wake the stack that feeds us. + */ +static void +il_hcmd_queue_reclaim(struct il_priv *il, int txq_id, int idx, int cmd_idx) +{ + struct il_tx_queue *txq = &il->txq[txq_id]; + struct il_queue *q = &txq->q; + int nfreed = 0; + + if (idx >= q->n_bd || il_queue_used(q, idx) == 0) { + IL_ERR("Read idx for DMA queue txq id (%d), idx %d, " + "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd, + q->write_ptr, q->read_ptr); + return; + } + + for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; + q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { + + if (nfreed++ > 0) { + IL_ERR("HCMD skipped: idx (%d) %d %d\n", idx, + q->write_ptr, q->read_ptr); + queue_work(il->workqueue, &il->restart); + } + + } +} + +/** + * il_tx_cmd_complete - Pull unused buffers off the queue and reclaim them + * @rxb: Rx buffer to reclaim + * + * If an Rx buffer has an async callback associated with it the callback + * will be executed. The attached skb (if present) will only be freed + * if the callback returns 1 + */ +void +il_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + u16 sequence = le16_to_cpu(pkt->hdr.sequence); + int txq_id = SEQ_TO_QUEUE(sequence); + int idx = SEQ_TO_IDX(sequence); + int cmd_idx; + bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); + struct il_device_cmd *cmd; + struct il_cmd_meta *meta; + struct il_tx_queue *txq = &il->txq[il->cmd_queue]; + unsigned long flags; + + /* If a Tx command is being handled and it isn't in the actual + * command queue then there a command routing bug has been introduced + * in the queue management code. */ + if (WARN + (txq_id != il->cmd_queue, + "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", + txq_id, il->cmd_queue, sequence, il->txq[il->cmd_queue].q.read_ptr, + il->txq[il->cmd_queue].q.write_ptr)) { + il_print_hex_error(il, pkt, 32); + return; + } + + cmd_idx = il_get_cmd_idx(&txq->q, idx, huge); + cmd = txq->cmd[cmd_idx]; + meta = &txq->meta[cmd_idx]; + + txq->time_stamp = jiffies; + + pci_unmap_single(il->pci_dev, dma_unmap_addr(meta, mapping), + dma_unmap_len(meta, len), PCI_DMA_BIDIRECTIONAL); + + /* Input error checking is done when commands are added to queue. */ + if (meta->flags & CMD_WANT_SKB) { + meta->source->reply_page = (unsigned long)rxb_addr(rxb); + rxb->page = NULL; + } else if (meta->callback) + meta->callback(il, cmd, pkt); + + spin_lock_irqsave(&il->hcmd_lock, flags); + + il_hcmd_queue_reclaim(il, txq_id, idx, cmd_idx); + + if (!(meta->flags & CMD_ASYNC)) { + clear_bit(S_HCMD_ACTIVE, &il->status); + D_INFO("Clearing HCMD_ACTIVE for command %s\n", + il_get_cmd_string(cmd->hdr.cmd)); + wake_up(&il->wait_command_queue); + } + + /* Mark as unmapped */ + meta->flags = 0; + + spin_unlock_irqrestore(&il->hcmd_lock, flags); +} +EXPORT_SYMBOL(il_tx_cmd_complete); + +MODULE_DESCRIPTION("iwl-legacy: common functions for 3945 and 4965"); +MODULE_VERSION(IWLWIFI_VERSION); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_LICENSE("GPL"); + +/* + * set bt_coex_active to true, uCode will do kill/defer + * every time the priority line is asserted (BT is sending signals on the + * priority line in the PCIx). + * set bt_coex_active to false, uCode will ignore the BT activity and + * perform the normal operation + * + * User might experience transmit issue on some platform due to WiFi/BT + * co-exist problem. The possible behaviors are: + * Able to scan and finding all the available AP + * Not able to associate with any AP + * On those platforms, WiFi communication can be restored by set + * "bt_coex_active" module parameter to "false" + * + * default: bt_coex_active = true (BT_COEX_ENABLE) + */ +static bool bt_coex_active = true; +module_param(bt_coex_active, bool, S_IRUGO); +MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist"); + +u32 il_debug_level; +EXPORT_SYMBOL(il_debug_level); + +const u8 il_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +EXPORT_SYMBOL(il_bcast_addr); + +#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ +#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ +static void +il_init_ht_hw_capab(const struct il_priv *il, + struct ieee80211_sta_ht_cap *ht_info, + enum ieee80211_band band) +{ + u16 max_bit_rate = 0; + u8 rx_chains_num = il->hw_params.rx_chains_num; + u8 tx_chains_num = il->hw_params.tx_chains_num; + + ht_info->cap = 0; + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + + ht_info->ht_supported = true; + + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + max_bit_rate = MAX_BIT_RATE_20_MHZ; + if (il->hw_params.ht40_channel & BIT(band)) { + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + ht_info->mcs.rx_mask[4] = 0x01; + max_bit_rate = MAX_BIT_RATE_40_MHZ; + } + + if (il->cfg->mod_params->amsdu_size_8K) + ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; + + ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; + ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF; + + ht_info->mcs.rx_mask[0] = 0xFF; + if (rx_chains_num >= 2) + ht_info->mcs.rx_mask[1] = 0xFF; + if (rx_chains_num >= 3) + ht_info->mcs.rx_mask[2] = 0xFF; + + /* Highest supported Rx data rate */ + max_bit_rate *= rx_chains_num; + WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); + ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); + + /* Tx MCS capabilities */ + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + if (tx_chains_num != rx_chains_num) { + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + ht_info->mcs.tx_params |= + ((tx_chains_num - + 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + } +} + +/** + * il_init_geos - Initialize mac80211's geo/channel info based from eeprom + */ +int +il_init_geos(struct il_priv *il) +{ + struct il_channel_info *ch; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *channels; + struct ieee80211_channel *geo_ch; + struct ieee80211_rate *rates; + int i = 0; + s8 max_tx_power = 0; + + if (il->bands[IEEE80211_BAND_2GHZ].n_bitrates || + il->bands[IEEE80211_BAND_5GHZ].n_bitrates) { + D_INFO("Geography modes already initialized.\n"); + set_bit(S_GEO_CONFIGURED, &il->status); + return 0; + } + + channels = + kzalloc(sizeof(struct ieee80211_channel) * il->channel_count, + GFP_KERNEL); + if (!channels) + return -ENOMEM; + + rates = + kzalloc((sizeof(struct ieee80211_rate) * RATE_COUNT_LEGACY), + GFP_KERNEL); + if (!rates) { + kfree(channels); + return -ENOMEM; + } + + /* 5.2GHz channels start after the 2.4GHz channels */ + sband = &il->bands[IEEE80211_BAND_5GHZ]; + sband->channels = &channels[ARRAY_SIZE(il_eeprom_band_1)]; + /* just OFDM */ + sband->bitrates = &rates[IL_FIRST_OFDM_RATE]; + sband->n_bitrates = RATE_COUNT_LEGACY - IL_FIRST_OFDM_RATE; + + if (il->cfg->sku & IL_SKU_N) + il_init_ht_hw_capab(il, &sband->ht_cap, IEEE80211_BAND_5GHZ); + + sband = &il->bands[IEEE80211_BAND_2GHZ]; + sband->channels = channels; + /* OFDM & CCK */ + sband->bitrates = rates; + sband->n_bitrates = RATE_COUNT_LEGACY; + + if (il->cfg->sku & IL_SKU_N) + il_init_ht_hw_capab(il, &sband->ht_cap, IEEE80211_BAND_2GHZ); + + il->ieee_channels = channels; + il->ieee_rates = rates; + + for (i = 0; i < il->channel_count; i++) { + ch = &il->channel_info[i]; + + if (!il_is_channel_valid(ch)) + continue; + + sband = &il->bands[ch->band]; + + geo_ch = &sband->channels[sband->n_channels++]; + + geo_ch->center_freq = + ieee80211_channel_to_frequency(ch->channel, ch->band); + geo_ch->max_power = ch->max_power_avg; + geo_ch->max_antenna_gain = 0xff; + geo_ch->hw_value = ch->channel; + + if (il_is_channel_valid(ch)) { + if (!(ch->flags & EEPROM_CHANNEL_IBSS)) + geo_ch->flags |= IEEE80211_CHAN_NO_IR; + + if (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) + geo_ch->flags |= IEEE80211_CHAN_NO_IR; + + if (ch->flags & EEPROM_CHANNEL_RADAR) + geo_ch->flags |= IEEE80211_CHAN_RADAR; + + geo_ch->flags |= ch->ht40_extension_channel; + + if (ch->max_power_avg > max_tx_power) + max_tx_power = ch->max_power_avg; + } else { + geo_ch->flags |= IEEE80211_CHAN_DISABLED; + } + + D_INFO("Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel, + geo_ch->center_freq, + il_is_channel_a_band(ch) ? "5.2" : "2.4", + geo_ch-> + flags & IEEE80211_CHAN_DISABLED ? "restricted" : "valid", + geo_ch->flags); + } + + il->tx_power_device_lmt = max_tx_power; + il->tx_power_user_lmt = max_tx_power; + il->tx_power_next = max_tx_power; + + if (il->bands[IEEE80211_BAND_5GHZ].n_channels == 0 && + (il->cfg->sku & IL_SKU_A)) { + IL_INFO("Incorrectly detected BG card as ABG. " + "Please send your PCI ID 0x%04X:0x%04X to maintainer.\n", + il->pci_dev->device, il->pci_dev->subsystem_device); + il->cfg->sku &= ~IL_SKU_A; + } + + IL_INFO("Tunable channels: %d 802.11bg, %d 802.11a channels\n", + il->bands[IEEE80211_BAND_2GHZ].n_channels, + il->bands[IEEE80211_BAND_5GHZ].n_channels); + + set_bit(S_GEO_CONFIGURED, &il->status); + + return 0; +} +EXPORT_SYMBOL(il_init_geos); + +/* + * il_free_geos - undo allocations in il_init_geos + */ +void +il_free_geos(struct il_priv *il) +{ + kfree(il->ieee_channels); + kfree(il->ieee_rates); + clear_bit(S_GEO_CONFIGURED, &il->status); +} +EXPORT_SYMBOL(il_free_geos); + +static bool +il_is_channel_extension(struct il_priv *il, enum ieee80211_band band, + u16 channel, u8 extension_chan_offset) +{ + const struct il_channel_info *ch_info; + + ch_info = il_get_channel_info(il, band, channel); + if (!il_is_channel_valid(ch_info)) + return false; + + if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) + return !(ch_info-> + ht40_extension_channel & IEEE80211_CHAN_NO_HT40PLUS); + else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW) + return !(ch_info-> + ht40_extension_channel & IEEE80211_CHAN_NO_HT40MINUS); + + return false; +} + +bool +il_is_ht40_tx_allowed(struct il_priv *il, struct ieee80211_sta_ht_cap *ht_cap) +{ + if (!il->ht.enabled || !il->ht.is_40mhz) + return false; + + /* + * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 + * the bit will not set if it is pure 40MHz case + */ + if (ht_cap && !ht_cap->ht_supported) + return false; + +#ifdef CONFIG_IWLEGACY_DEBUGFS + if (il->disable_ht40) + return false; +#endif + + return il_is_channel_extension(il, il->band, + le16_to_cpu(il->staging.channel), + il->ht.extension_chan_offset); +} +EXPORT_SYMBOL(il_is_ht40_tx_allowed); + +static u16 +il_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) +{ + u16 new_val; + u16 beacon_factor; + + /* + * If mac80211 hasn't given us a beacon interval, program + * the default into the device. + */ + if (!beacon_val) + return DEFAULT_BEACON_INTERVAL; + + /* + * If the beacon interval we obtained from the peer + * is too large, we'll have to wake up more often + * (and in IBSS case, we'll beacon too much) + * + * For example, if max_beacon_val is 4096, and the + * requested beacon interval is 7000, we'll have to + * use 3500 to be able to wake up on the beacons. + * + * This could badly influence beacon detection stats. + */ + + beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; + new_val = beacon_val / beacon_factor; + + if (!new_val) + new_val = max_beacon_val; + + return new_val; +} + +int +il_send_rxon_timing(struct il_priv *il) +{ + u64 tsf; + s32 interval_tm, rem; + struct ieee80211_conf *conf = NULL; + u16 beacon_int; + struct ieee80211_vif *vif = il->vif; + + conf = &il->hw->conf; + + lockdep_assert_held(&il->mutex); + + memset(&il->timing, 0, sizeof(struct il_rxon_time_cmd)); + + il->timing.timestamp = cpu_to_le64(il->timestamp); + il->timing.listen_interval = cpu_to_le16(conf->listen_interval); + + beacon_int = vif ? vif->bss_conf.beacon_int : 0; + + /* + * TODO: For IBSS we need to get atim_win from mac80211, + * for now just always use 0 + */ + il->timing.atim_win = 0; + + beacon_int = + il_adjust_beacon_interval(beacon_int, + il->hw_params.max_beacon_itrvl * + TIME_UNIT); + il->timing.beacon_interval = cpu_to_le16(beacon_int); + + tsf = il->timestamp; /* tsf is modifed by do_div: copy it */ + interval_tm = beacon_int * TIME_UNIT; + rem = do_div(tsf, interval_tm); + il->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); + + il->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ? : 1) : 1; + + D_ASSOC("beacon interval %d beacon timer %d beacon tim %d\n", + le16_to_cpu(il->timing.beacon_interval), + le32_to_cpu(il->timing.beacon_init_val), + le16_to_cpu(il->timing.atim_win)); + + return il_send_cmd_pdu(il, C_RXON_TIMING, sizeof(il->timing), + &il->timing); +} +EXPORT_SYMBOL(il_send_rxon_timing); + +void +il_set_rxon_hwcrypto(struct il_priv *il, int hw_decrypt) +{ + struct il_rxon_cmd *rxon = &il->staging; + + if (hw_decrypt) + rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; + else + rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; + +} +EXPORT_SYMBOL(il_set_rxon_hwcrypto); + +/* validate RXON structure is valid */ +int +il_check_rxon_cmd(struct il_priv *il) +{ + struct il_rxon_cmd *rxon = &il->staging; + bool error = false; + + if (rxon->flags & RXON_FLG_BAND_24G_MSK) { + if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) { + IL_WARN("check 2.4G: wrong narrow\n"); + error = true; + } + if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) { + IL_WARN("check 2.4G: wrong radar\n"); + error = true; + } + } else { + if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) { + IL_WARN("check 5.2G: not short slot!\n"); + error = true; + } + if (rxon->flags & RXON_FLG_CCK_MSK) { + IL_WARN("check 5.2G: CCK!\n"); + error = true; + } + } + if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) { + IL_WARN("mac/bssid mcast!\n"); + error = true; + } + + /* make sure basic rates 6Mbps and 1Mbps are supported */ + if ((rxon->ofdm_basic_rates & RATE_6M_MASK) == 0 && + (rxon->cck_basic_rates & RATE_1M_MASK) == 0) { + IL_WARN("neither 1 nor 6 are basic\n"); + error = true; + } + + if (le16_to_cpu(rxon->assoc_id) > 2007) { + IL_WARN("aid > 2007\n"); + error = true; + } + + if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) == + (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) { + IL_WARN("CCK and short slot\n"); + error = true; + } + + if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) == + (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { + IL_WARN("CCK and auto detect"); + error = true; + } + + if ((rxon-> + flags & (RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK)) == + RXON_FLG_TGG_PROTECT_MSK) { + IL_WARN("TGg but no auto-detect\n"); + error = true; + } + + if (error) + IL_WARN("Tuning to channel %d\n", le16_to_cpu(rxon->channel)); + + if (error) { + IL_ERR("Invalid RXON\n"); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(il_check_rxon_cmd); + +/** + * il_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed + * @il: staging_rxon is compared to active_rxon + * + * If the RXON structure is changing enough to require a new tune, + * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that + * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. + */ +int +il_full_rxon_required(struct il_priv *il) +{ + const struct il_rxon_cmd *staging = &il->staging; + const struct il_rxon_cmd *active = &il->active; + +#define CHK(cond) \ + if ((cond)) { \ + D_INFO("need full RXON - " #cond "\n"); \ + return 1; \ + } + +#define CHK_NEQ(c1, c2) \ + if ((c1) != (c2)) { \ + D_INFO("need full RXON - " \ + #c1 " != " #c2 " - %d != %d\n", \ + (c1), (c2)); \ + return 1; \ + } + + /* These items are only settable from the full RXON command */ + CHK(!il_is_associated(il)); + CHK(!ether_addr_equal_64bits(staging->bssid_addr, active->bssid_addr)); + CHK(!ether_addr_equal_64bits(staging->node_addr, active->node_addr)); + CHK(!ether_addr_equal_64bits(staging->wlap_bssid_addr, + active->wlap_bssid_addr)); + CHK_NEQ(staging->dev_type, active->dev_type); + CHK_NEQ(staging->channel, active->channel); + CHK_NEQ(staging->air_propagation, active->air_propagation); + CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, + active->ofdm_ht_single_stream_basic_rates); + CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, + active->ofdm_ht_dual_stream_basic_rates); + CHK_NEQ(staging->assoc_id, active->assoc_id); + + /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can + * be updated with the RXON_ASSOC command -- however only some + * flag transitions are allowed using RXON_ASSOC */ + + /* Check if we are not switching bands */ + CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, + active->flags & RXON_FLG_BAND_24G_MSK); + + /* Check if we are switching association toggle */ + CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, + active->filter_flags & RXON_FILTER_ASSOC_MSK); + +#undef CHK +#undef CHK_NEQ + + return 0; +} +EXPORT_SYMBOL(il_full_rxon_required); + +u8 +il_get_lowest_plcp(struct il_priv *il) +{ + /* + * Assign the lowest rate -- should really get this from + * the beacon skb from mac80211. + */ + if (il->staging.flags & RXON_FLG_BAND_24G_MSK) + return RATE_1M_PLCP; + else + return RATE_6M_PLCP; +} +EXPORT_SYMBOL(il_get_lowest_plcp); + +static void +_il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf) +{ + struct il_rxon_cmd *rxon = &il->staging; + + if (!il->ht.enabled) { + rxon->flags &= + ~(RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK + | RXON_FLG_HT_PROT_MSK); + return; + } + + rxon->flags |= + cpu_to_le32(il->ht.protection << RXON_FLG_HT_OPERATING_MODE_POS); + + /* Set up channel bandwidth: + * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ + /* clear the HT channel mode before set the mode */ + rxon->flags &= + ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); + if (il_is_ht40_tx_allowed(il, NULL)) { + /* pure ht40 */ + if (il->ht.protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { + rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; + /* Note: control channel is opposite of extension channel */ + switch (il->ht.extension_chan_offset) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + rxon->flags &= + ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + break; + } + } else { + /* Note: control channel is opposite of extension channel */ + switch (il->ht.extension_chan_offset) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + rxon->flags &= + ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); + rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; + break; + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + default: + /* channel location only valid if in Mixed mode */ + IL_ERR("invalid extension channel offset\n"); + break; + } + } + } else { + rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY; + } + + if (il->ops->set_rxon_chain) + il->ops->set_rxon_chain(il); + + D_ASSOC("rxon flags 0x%X operation mode :0x%X " + "extension channel offset 0x%x\n", le32_to_cpu(rxon->flags), + il->ht.protection, il->ht.extension_chan_offset); +} + +void +il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf) +{ + _il_set_rxon_ht(il, ht_conf); +} +EXPORT_SYMBOL(il_set_rxon_ht); + +/* Return valid, unused, channel for a passive scan to reset the RF */ +u8 +il_get_single_channel_number(struct il_priv *il, enum ieee80211_band band) +{ + const struct il_channel_info *ch_info; + int i; + u8 channel = 0; + u8 min, max; + + if (band == IEEE80211_BAND_5GHZ) { + min = 14; + max = il->channel_count; + } else { + min = 0; + max = 14; + } + + for (i = min; i < max; i++) { + channel = il->channel_info[i].channel; + if (channel == le16_to_cpu(il->staging.channel)) + continue; + + ch_info = il_get_channel_info(il, band, channel); + if (il_is_channel_valid(ch_info)) + break; + } + + return channel; +} +EXPORT_SYMBOL(il_get_single_channel_number); + +/** + * il_set_rxon_channel - Set the band and channel values in staging RXON + * @ch: requested channel as a pointer to struct ieee80211_channel + + * NOTE: Does not commit to the hardware; it sets appropriate bit fields + * in the staging RXON flag structure based on the ch->band + */ +int +il_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch) +{ + enum ieee80211_band band = ch->band; + u16 channel = ch->hw_value; + + if (le16_to_cpu(il->staging.channel) == channel && il->band == band) + return 0; + + il->staging.channel = cpu_to_le16(channel); + if (band == IEEE80211_BAND_5GHZ) + il->staging.flags &= ~RXON_FLG_BAND_24G_MSK; + else + il->staging.flags |= RXON_FLG_BAND_24G_MSK; + + il->band = band; + + D_INFO("Staging channel set to %d [%d]\n", channel, band); + + return 0; +} +EXPORT_SYMBOL(il_set_rxon_channel); + +void +il_set_flags_for_band(struct il_priv *il, enum ieee80211_band band, + struct ieee80211_vif *vif) +{ + if (band == IEEE80211_BAND_5GHZ) { + il->staging.flags &= + ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK | + RXON_FLG_CCK_MSK); + il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + } else { + /* Copied from il_post_associate() */ + if (vif && vif->bss_conf.use_short_slot) + il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + + il->staging.flags |= RXON_FLG_BAND_24G_MSK; + il->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; + il->staging.flags &= ~RXON_FLG_CCK_MSK; + } +} +EXPORT_SYMBOL(il_set_flags_for_band); + +/* + * initialize rxon structure with default values from eeprom + */ +void +il_connection_init_rx_config(struct il_priv *il) +{ + const struct il_channel_info *ch_info; + + memset(&il->staging, 0, sizeof(il->staging)); + + switch (il->iw_mode) { + case NL80211_IFTYPE_UNSPECIFIED: + il->staging.dev_type = RXON_DEV_TYPE_ESS; + break; + case NL80211_IFTYPE_STATION: + il->staging.dev_type = RXON_DEV_TYPE_ESS; + il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; + break; + case NL80211_IFTYPE_ADHOC: + il->staging.dev_type = RXON_DEV_TYPE_IBSS; + il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; + il->staging.filter_flags = + RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK; + break; + default: + IL_ERR("Unsupported interface type %d\n", il->vif->type); + return; + } + +#if 0 + /* TODO: Figure out when short_preamble would be set and cache from + * that */ + if (!hw_to_local(il->hw)->short_preamble) + il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + else + il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; +#endif + + ch_info = + il_get_channel_info(il, il->band, le16_to_cpu(il->active.channel)); + + if (!ch_info) + ch_info = &il->channel_info[0]; + + il->staging.channel = cpu_to_le16(ch_info->channel); + il->band = ch_info->band; + + il_set_flags_for_band(il, il->band, il->vif); + + il->staging.ofdm_basic_rates = + (IL_OFDM_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; + il->staging.cck_basic_rates = + (IL_CCK_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF; + + /* clear both MIX and PURE40 mode flag */ + il->staging.flags &= + ~(RXON_FLG_CHANNEL_MODE_MIXED | RXON_FLG_CHANNEL_MODE_PURE_40); + if (il->vif) + memcpy(il->staging.node_addr, il->vif->addr, ETH_ALEN); + + il->staging.ofdm_ht_single_stream_basic_rates = 0xff; + il->staging.ofdm_ht_dual_stream_basic_rates = 0xff; +} +EXPORT_SYMBOL(il_connection_init_rx_config); + +void +il_set_rate(struct il_priv *il) +{ + const struct ieee80211_supported_band *hw = NULL; + struct ieee80211_rate *rate; + int i; + + hw = il_get_hw_mode(il, il->band); + if (!hw) { + IL_ERR("Failed to set rate: unable to get hw mode\n"); + return; + } + + il->active_rate = 0; + + for (i = 0; i < hw->n_bitrates; i++) { + rate = &(hw->bitrates[i]); + if (rate->hw_value < RATE_COUNT_LEGACY) + il->active_rate |= (1 << rate->hw_value); + } + + D_RATE("Set active_rate = %0x\n", il->active_rate); + + il->staging.cck_basic_rates = + (IL_CCK_BASIC_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF; + + il->staging.ofdm_basic_rates = + (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; +} +EXPORT_SYMBOL(il_set_rate); + +void +il_chswitch_done(struct il_priv *il, bool is_success) +{ + if (test_bit(S_EXIT_PENDING, &il->status)) + return; + + if (test_and_clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) + ieee80211_chswitch_done(il->vif, is_success); +} +EXPORT_SYMBOL(il_chswitch_done); + +void +il_hdl_csa(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_csa_notification *csa = &(pkt->u.csa_notif); + struct il_rxon_cmd *rxon = (void *)&il->active; + + if (!test_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) + return; + + if (!le32_to_cpu(csa->status) && csa->channel == il->switch_channel) { + rxon->channel = csa->channel; + il->staging.channel = csa->channel; + D_11H("CSA notif: channel %d\n", le16_to_cpu(csa->channel)); + il_chswitch_done(il, true); + } else { + IL_ERR("CSA notif (fail) : channel %d\n", + le16_to_cpu(csa->channel)); + il_chswitch_done(il, false); + } +} +EXPORT_SYMBOL(il_hdl_csa); + +#ifdef CONFIG_IWLEGACY_DEBUG +void +il_print_rx_config_cmd(struct il_priv *il) +{ + struct il_rxon_cmd *rxon = &il->staging; + + D_RADIO("RX CONFIG:\n"); + il_print_hex_dump(il, IL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); + D_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel)); + D_RADIO("u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags)); + D_RADIO("u32 filter_flags: 0x%08x\n", le32_to_cpu(rxon->filter_flags)); + D_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type); + D_RADIO("u8 ofdm_basic_rates: 0x%02x\n", rxon->ofdm_basic_rates); + D_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates); + D_RADIO("u8[6] node_addr: %pM\n", rxon->node_addr); + D_RADIO("u8[6] bssid_addr: %pM\n", rxon->bssid_addr); + D_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); +} +EXPORT_SYMBOL(il_print_rx_config_cmd); +#endif +/** + * il_irq_handle_error - called for HW or SW error interrupt from card + */ +void +il_irq_handle_error(struct il_priv *il) +{ + /* Set the FW error flag -- cleared on il_down */ + set_bit(S_FW_ERROR, &il->status); + + /* Cancel currently queued command. */ + clear_bit(S_HCMD_ACTIVE, &il->status); + + IL_ERR("Loaded firmware version: %s\n", il->hw->wiphy->fw_version); + + il->ops->dump_nic_error_log(il); + if (il->ops->dump_fh) + il->ops->dump_fh(il, NULL, false); +#ifdef CONFIG_IWLEGACY_DEBUG + if (il_get_debug_level(il) & IL_DL_FW_ERRORS) + il_print_rx_config_cmd(il); +#endif + + wake_up(&il->wait_command_queue); + + /* Keep the restart process from trying to send host + * commands by clearing the INIT status bit */ + clear_bit(S_READY, &il->status); + + if (!test_bit(S_EXIT_PENDING, &il->status)) { + IL_DBG(IL_DL_FW_ERRORS, + "Restarting adapter due to uCode error.\n"); + + if (il->cfg->mod_params->restart_fw) + queue_work(il->workqueue, &il->restart); + } +} +EXPORT_SYMBOL(il_irq_handle_error); + +static int +_il_apm_stop_master(struct il_priv *il) +{ + int ret = 0; + + /* stop device's busmaster DMA activity */ + _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); + + ret = + _il_poll_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED, + CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); + if (ret < 0) + IL_WARN("Master Disable Timed Out, 100 usec\n"); + + D_INFO("stop master\n"); + + return ret; +} + +void +_il_apm_stop(struct il_priv *il) +{ + lockdep_assert_held(&il->reg_lock); + + D_INFO("Stop card, put in low power state\n"); + + /* Stop device's DMA activity */ + _il_apm_stop_master(il); + + /* Reset the entire device */ + _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + udelay(10); + + /* + * Clear "initialization complete" bit to move adapter from + * D0A* (powered-up Active) --> D0U* (Uninitialized) state. + */ + _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); +} +EXPORT_SYMBOL(_il_apm_stop); + +void +il_apm_stop(struct il_priv *il) +{ + unsigned long flags; + + spin_lock_irqsave(&il->reg_lock, flags); + _il_apm_stop(il); + spin_unlock_irqrestore(&il->reg_lock, flags); +} +EXPORT_SYMBOL(il_apm_stop); + +/* + * Start up NIC's basic functionality after it has been reset + * (e.g. after platform boot, or shutdown via il_apm_stop()) + * NOTE: This does not load uCode nor start the embedded processor + */ +int +il_apm_init(struct il_priv *il) +{ + int ret = 0; + u16 lctl; + + D_INFO("Init card's basic functions\n"); + + /* + * Use "set_bit" below rather than "write", to preserve any hardware + * bits already set by default after reset. + */ + + /* Disable L0S exit timer (platform NMI Work/Around) */ + il_set_bit(il, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + + /* + * Disable L0s without affecting L1; + * don't wait for ICH L0s (ICH bug W/A) + */ + il_set_bit(il, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); + + /* Set FH wait threshold to maximum (HW error during stress W/A) */ + il_set_bit(il, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); + + /* + * Enable HAP INTA (interrupt from management bus) to + * wake device's PCI Express link L1a -> L0s + * NOTE: This is no-op for 3945 (non-existent bit) + */ + il_set_bit(il, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); + + /* + * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition. + * Check if BIOS (or OS) enabled L1-ASPM on this device. + * If so (likely), disable L0S, so device moves directly L0->L1; + * costs negligible amount of power savings. + * If not (unlikely), enable L0S, so there is at least some + * power savings, even without L1. + */ + if (il->cfg->set_l0s) { + pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl); + if (lctl & PCI_EXP_LNKCTL_ASPM_L1) { + /* L1-ASPM enabled; disable(!) L0S */ + il_set_bit(il, CSR_GIO_REG, + CSR_GIO_REG_VAL_L0S_ENABLED); + D_POWER("L1 Enabled; Disabling L0S\n"); + } else { + /* L1-ASPM disabled; enable(!) L0S */ + il_clear_bit(il, CSR_GIO_REG, + CSR_GIO_REG_VAL_L0S_ENABLED); + D_POWER("L1 Disabled; Enabling L0S\n"); + } + } + + /* Configure analog phase-lock-loop before activating to D0A */ + if (il->cfg->pll_cfg_val) + il_set_bit(il, CSR_ANA_PLL_CFG, + il->cfg->pll_cfg_val); + + /* + * Set "initialization complete" bit to move adapter from + * D0U* --> D0A* (powered-up active) state. + */ + il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* + * Wait for clock stabilization; once stabilized, access to + * device-internal resources is supported, e.g. il_wr_prph() + * and accesses to uCode SRAM. + */ + ret = + _il_poll_bit(il, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); + if (ret < 0) { + D_INFO("Failed to init the card\n"); + goto out; + } + + /* + * Enable DMA and BSM (if used) clocks, wait for them to stabilize. + * BSM (Boostrap State Machine) is only in 3945 and 4965. + * + * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits + * do not disable clocks. This preserves any hardware bits already + * set by default in "CLK_CTRL_REG" after reset. + */ + if (il->cfg->use_bsm) + il_wr_prph(il, APMG_CLK_EN_REG, + APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT); + else + il_wr_prph(il, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); + udelay(20); + + /* Disable L1-Active */ + il_set_bits_prph(il, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + +out: + return ret; +} +EXPORT_SYMBOL(il_apm_init); + +int +il_set_tx_power(struct il_priv *il, s8 tx_power, bool force) +{ + int ret; + s8 prev_tx_power; + bool defer; + + lockdep_assert_held(&il->mutex); + + if (il->tx_power_user_lmt == tx_power && !force) + return 0; + + if (!il->ops->send_tx_power) + return -EOPNOTSUPP; + + /* 0 dBm mean 1 milliwatt */ + if (tx_power < 0) { + IL_WARN("Requested user TXPOWER %d below 1 mW.\n", tx_power); + return -EINVAL; + } + + if (tx_power > il->tx_power_device_lmt) { + IL_WARN("Requested user TXPOWER %d above upper limit %d.\n", + tx_power, il->tx_power_device_lmt); + return -EINVAL; + } + + if (!il_is_ready_rf(il)) + return -EIO; + + /* scan complete and commit_rxon use tx_power_next value, + * it always need to be updated for newest request */ + il->tx_power_next = tx_power; + + /* do not set tx power when scanning or channel changing */ + defer = test_bit(S_SCANNING, &il->status) || + memcmp(&il->active, &il->staging, sizeof(il->staging)); + if (defer && !force) { + D_INFO("Deferring tx power set\n"); + return 0; + } + + prev_tx_power = il->tx_power_user_lmt; + il->tx_power_user_lmt = tx_power; + + ret = il->ops->send_tx_power(il); + + /* if fail to set tx_power, restore the orig. tx power */ + if (ret) { + il->tx_power_user_lmt = prev_tx_power; + il->tx_power_next = prev_tx_power; + } + return ret; +} +EXPORT_SYMBOL(il_set_tx_power); + +void +il_send_bt_config(struct il_priv *il) +{ + struct il_bt_cmd bt_cmd = { + .lead_time = BT_LEAD_TIME_DEF, + .max_kill = BT_MAX_KILL_DEF, + .kill_ack_mask = 0, + .kill_cts_mask = 0, + }; + + if (!bt_coex_active) + bt_cmd.flags = BT_COEX_DISABLE; + else + bt_cmd.flags = BT_COEX_ENABLE; + + D_INFO("BT coex %s\n", + (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); + + if (il_send_cmd_pdu(il, C_BT_CONFIG, sizeof(struct il_bt_cmd), &bt_cmd)) + IL_ERR("failed to send BT Coex Config\n"); +} +EXPORT_SYMBOL(il_send_bt_config); + +int +il_send_stats_request(struct il_priv *il, u8 flags, bool clear) +{ + struct il_stats_cmd stats_cmd = { + .configuration_flags = clear ? IL_STATS_CONF_CLEAR_STATS : 0, + }; + + if (flags & CMD_ASYNC) + return il_send_cmd_pdu_async(il, C_STATS, sizeof(struct il_stats_cmd), + &stats_cmd, NULL); + else + return il_send_cmd_pdu(il, C_STATS, sizeof(struct il_stats_cmd), + &stats_cmd); +} +EXPORT_SYMBOL(il_send_stats_request); + +void +il_hdl_pm_sleep(struct il_priv *il, struct il_rx_buf *rxb) +{ +#ifdef CONFIG_IWLEGACY_DEBUG + struct il_rx_pkt *pkt = rxb_addr(rxb); + struct il_sleep_notification *sleep = &(pkt->u.sleep_notif); + D_RX("sleep mode: %d, src: %d\n", + sleep->pm_sleep_mode, sleep->pm_wakeup_src); +#endif +} +EXPORT_SYMBOL(il_hdl_pm_sleep); + +void +il_hdl_pm_debug_stats(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + u32 len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; + D_RADIO("Dumping %d bytes of unhandled notification for %s:\n", len, + il_get_cmd_string(pkt->hdr.cmd)); + il_print_hex_dump(il, IL_DL_RADIO, pkt->u.raw, len); +} +EXPORT_SYMBOL(il_hdl_pm_debug_stats); + +void +il_hdl_error(struct il_priv *il, struct il_rx_buf *rxb) +{ + struct il_rx_pkt *pkt = rxb_addr(rxb); + + IL_ERR("Error Reply type 0x%08X cmd %s (0x%02X) " + "seq 0x%04X ser 0x%08X\n", + le32_to_cpu(pkt->u.err_resp.error_type), + il_get_cmd_string(pkt->u.err_resp.cmd_id), + pkt->u.err_resp.cmd_id, + le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num), + le32_to_cpu(pkt->u.err_resp.error_info)); +} +EXPORT_SYMBOL(il_hdl_error); + +void +il_clear_isr_stats(struct il_priv *il) +{ + memset(&il->isr_stats, 0, sizeof(il->isr_stats)); +} + +int +il_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct il_priv *il = hw->priv; + unsigned long flags; + int q; + + D_MAC80211("enter\n"); + + if (!il_is_ready_rf(il)) { + D_MAC80211("leave - RF not ready\n"); + return -EIO; + } + + if (queue >= AC_NUM) { + D_MAC80211("leave - queue >= AC_NUM %d\n", queue); + return 0; + } + + q = AC_NUM - 1 - queue; + + spin_lock_irqsave(&il->lock, flags); + + il->qos_data.def_qos_parm.ac[q].cw_min = + cpu_to_le16(params->cw_min); + il->qos_data.def_qos_parm.ac[q].cw_max = + cpu_to_le16(params->cw_max); + il->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; + il->qos_data.def_qos_parm.ac[q].edca_txop = + cpu_to_le16((params->txop * 32)); + + il->qos_data.def_qos_parm.ac[q].reserved1 = 0; + + spin_unlock_irqrestore(&il->lock, flags); + + D_MAC80211("leave\n"); + return 0; +} +EXPORT_SYMBOL(il_mac_conf_tx); + +int +il_mac_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct il_priv *il = hw->priv; + int ret; + + D_MAC80211("enter\n"); + + ret = (il->ibss_manager == IL_IBSS_MANAGER); + + D_MAC80211("leave ret %d\n", ret); + return ret; +} +EXPORT_SYMBOL_GPL(il_mac_tx_last_beacon); + +static int +il_set_mode(struct il_priv *il) +{ + il_connection_init_rx_config(il); + + if (il->ops->set_rxon_chain) + il->ops->set_rxon_chain(il); + + return il_commit_rxon(il); +} + +int +il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct il_priv *il = hw->priv; + int err; + bool reset; + + mutex_lock(&il->mutex); + D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); + + if (!il_is_ready_rf(il)) { + IL_WARN("Try to add interface when device not ready\n"); + err = -EINVAL; + goto out; + } + + /* + * We do not support multiple virtual interfaces, but on hardware reset + * we have to add the same interface again. + */ + reset = (il->vif == vif); + if (il->vif && !reset) { + err = -EOPNOTSUPP; + goto out; + } + + il->vif = vif; + il->iw_mode = vif->type; + + err = il_set_mode(il); + if (err) { + IL_WARN("Fail to set mode %d\n", vif->type); + if (!reset) { + il->vif = NULL; + il->iw_mode = NL80211_IFTYPE_STATION; + } + } + +out: + D_MAC80211("leave err %d\n", err); + mutex_unlock(&il->mutex); + + return err; +} +EXPORT_SYMBOL(il_mac_add_interface); + +static void +il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif) +{ + lockdep_assert_held(&il->mutex); + + if (il->scan_vif == vif) { + il_scan_cancel_timeout(il, 200); + il_force_scan_end(il); + } + + il_set_mode(il); +} + +void +il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct il_priv *il = hw->priv; + + mutex_lock(&il->mutex); + D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); + + WARN_ON(il->vif != vif); + il->vif = NULL; + il->iw_mode = NL80211_IFTYPE_UNSPECIFIED; + il_teardown_interface(il, vif); + eth_zero_addr(il->bssid); + + D_MAC80211("leave\n"); + mutex_unlock(&il->mutex); +} +EXPORT_SYMBOL(il_mac_remove_interface); + +int +il_alloc_txq_mem(struct il_priv *il) +{ + if (!il->txq) + il->txq = + kzalloc(sizeof(struct il_tx_queue) * + il->cfg->num_of_queues, GFP_KERNEL); + if (!il->txq) { + IL_ERR("Not enough memory for txq\n"); + return -ENOMEM; + } + return 0; +} +EXPORT_SYMBOL(il_alloc_txq_mem); + +void +il_free_txq_mem(struct il_priv *il) +{ + kfree(il->txq); + il->txq = NULL; +} +EXPORT_SYMBOL(il_free_txq_mem); + +int +il_force_reset(struct il_priv *il, bool external) +{ + struct il_force_reset *force_reset; + + if (test_bit(S_EXIT_PENDING, &il->status)) + return -EINVAL; + + force_reset = &il->force_reset; + force_reset->reset_request_count++; + if (!external) { + if (force_reset->last_force_reset_jiffies && + time_after(force_reset->last_force_reset_jiffies + + force_reset->reset_duration, jiffies)) { + D_INFO("force reset rejected\n"); + force_reset->reset_reject_count++; + return -EAGAIN; + } + } + force_reset->reset_success_count++; + force_reset->last_force_reset_jiffies = jiffies; + + /* + * if the request is from external(ex: debugfs), + * then always perform the request in regardless the module + * parameter setting + * if the request is from internal (uCode error or driver + * detect failure), then fw_restart module parameter + * need to be check before performing firmware reload + */ + + if (!external && !il->cfg->mod_params->restart_fw) { + D_INFO("Cancel firmware reload based on " + "module parameter setting\n"); + return 0; + } + + IL_ERR("On demand firmware reload\n"); + + /* Set the FW error flag -- cleared on il_down */ + set_bit(S_FW_ERROR, &il->status); + wake_up(&il->wait_command_queue); + /* + * Keep the restart process from trying to send host + * commands by clearing the INIT status bit + */ + clear_bit(S_READY, &il->status); + queue_work(il->workqueue, &il->restart); + + return 0; +} +EXPORT_SYMBOL(il_force_reset); + +int +il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum nl80211_iftype newtype, bool newp2p) +{ + struct il_priv *il = hw->priv; + int err; + + mutex_lock(&il->mutex); + D_MAC80211("enter: type %d, addr %pM newtype %d newp2p %d\n", + vif->type, vif->addr, newtype, newp2p); + + if (newp2p) { + err = -EOPNOTSUPP; + goto out; + } + + if (!il->vif || !il_is_ready_rf(il)) { + /* + * Huh? But wait ... this can maybe happen when + * we're in the middle of a firmware restart! + */ + err = -EBUSY; + goto out; + } + + /* success */ + vif->type = newtype; + vif->p2p = false; + il->iw_mode = newtype; + il_teardown_interface(il, vif); + err = 0; + +out: + D_MAC80211("leave err %d\n", err); + mutex_unlock(&il->mutex); + + return err; +} +EXPORT_SYMBOL(il_mac_change_interface); + +void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct il_priv *il = hw->priv; + unsigned long timeout = jiffies + msecs_to_jiffies(500); + int i; + + mutex_lock(&il->mutex); + D_MAC80211("enter\n"); + + if (il->txq == NULL) + goto out; + + for (i = 0; i < il->hw_params.max_txq_num; i++) { + struct il_queue *q; + + if (i == il->cmd_queue) + continue; + + q = &il->txq[i].q; + if (q->read_ptr == q->write_ptr) + continue; + + if (time_after(jiffies, timeout)) { + IL_ERR("Failed to flush queue %d\n", q->id); + break; + } + + msleep(20); + } +out: + D_MAC80211("leave\n"); + mutex_unlock(&il->mutex); +} +EXPORT_SYMBOL(il_mac_flush); + +/* + * On every watchdog tick we check (latest) time stamp. If it does not + * change during timeout period and queue is not empty we reset firmware. + */ +static int +il_check_stuck_queue(struct il_priv *il, int cnt) +{ + struct il_tx_queue *txq = &il->txq[cnt]; + struct il_queue *q = &txq->q; + unsigned long timeout; + unsigned long now = jiffies; + int ret; + + if (q->read_ptr == q->write_ptr) { + txq->time_stamp = now; + return 0; + } + + timeout = + txq->time_stamp + + msecs_to_jiffies(il->cfg->wd_timeout); + + if (time_after(now, timeout)) { + IL_ERR("Queue %d stuck for %u ms.\n", q->id, + jiffies_to_msecs(now - txq->time_stamp)); + ret = il_force_reset(il, false); + return (ret == -EAGAIN) ? 0 : 1; + } + + return 0; +} + +/* + * Making watchdog tick be a quarter of timeout assure we will + * discover the queue hung between timeout and 1.25*timeout + */ +#define IL_WD_TICK(timeout) ((timeout) / 4) + +/* + * Watchdog timer callback, we check each tx queue for stuck, if if hung + * we reset the firmware. If everything is fine just rearm the timer. + */ +void +il_bg_watchdog(unsigned long data) +{ + struct il_priv *il = (struct il_priv *)data; + int cnt; + unsigned long timeout; + + if (test_bit(S_EXIT_PENDING, &il->status)) + return; + + timeout = il->cfg->wd_timeout; + if (timeout == 0) + return; + + /* monitor and check for stuck cmd queue */ + if (il_check_stuck_queue(il, il->cmd_queue)) + return; + + /* monitor and check for other stuck queues */ + for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) { + /* skip as we already checked the command queue */ + if (cnt == il->cmd_queue) + continue; + if (il_check_stuck_queue(il, cnt)) + return; + } + + mod_timer(&il->watchdog, + jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); +} +EXPORT_SYMBOL(il_bg_watchdog); + +void +il_setup_watchdog(struct il_priv *il) +{ + unsigned int timeout = il->cfg->wd_timeout; + + if (timeout) + mod_timer(&il->watchdog, + jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); + else + del_timer(&il->watchdog); +} +EXPORT_SYMBOL(il_setup_watchdog); + +/* + * extended beacon time format + * time in usec will be changed into a 32-bit value in extended:internal format + * the extended part is the beacon counts + * the internal part is the time in usec within one beacon interval + */ +u32 +il_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval) +{ + u32 quot; + u32 rem; + u32 interval = beacon_interval * TIME_UNIT; + + if (!interval || !usec) + return 0; + + quot = + (usec / + interval) & (il_beacon_time_mask_high(il, + il->hw_params. + beacon_time_tsf_bits) >> il-> + hw_params.beacon_time_tsf_bits); + rem = + (usec % interval) & il_beacon_time_mask_low(il, + il->hw_params. + beacon_time_tsf_bits); + + return (quot << il->hw_params.beacon_time_tsf_bits) + rem; +} +EXPORT_SYMBOL(il_usecs_to_beacons); + +/* base is usually what we get from ucode with each received frame, + * the same as HW timer counter counting down + */ +__le32 +il_add_beacon_time(struct il_priv *il, u32 base, u32 addon, + u32 beacon_interval) +{ + u32 base_low = base & il_beacon_time_mask_low(il, + il->hw_params. + beacon_time_tsf_bits); + u32 addon_low = addon & il_beacon_time_mask_low(il, + il->hw_params. + beacon_time_tsf_bits); + u32 interval = beacon_interval * TIME_UNIT; + u32 res = (base & il_beacon_time_mask_high(il, + il->hw_params. + beacon_time_tsf_bits)) + + (addon & il_beacon_time_mask_high(il, + il->hw_params. + beacon_time_tsf_bits)); + + if (base_low > addon_low) + res += base_low - addon_low; + else if (base_low < addon_low) { + res += interval + base_low - addon_low; + res += (1 << il->hw_params.beacon_time_tsf_bits); + } else + res += (1 << il->hw_params.beacon_time_tsf_bits); + + return cpu_to_le32(res); +} +EXPORT_SYMBOL(il_add_beacon_time); + +#ifdef CONFIG_PM_SLEEP + +static int +il_pci_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct il_priv *il = pci_get_drvdata(pdev); + + /* + * This function is called when system goes into suspend state + * mac80211 will call il_mac_stop() from the mac80211 suspend function + * first but since il_mac_stop() has no knowledge of who the caller is, + * it will not call apm_ops.stop() to stop the DMA operation. + * Calling apm_ops.stop here to make sure we stop the DMA. + */ + il_apm_stop(il); + + return 0; +} + +static int +il_pci_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct il_priv *il = pci_get_drvdata(pdev); + bool hw_rfkill = false; + + /* + * We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); + + il_enable_interrupts(il); + + if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) + hw_rfkill = true; + + if (hw_rfkill) + set_bit(S_RFKILL, &il->status); + else + clear_bit(S_RFKILL, &il->status); + + wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rfkill); + + return 0; +} + +SIMPLE_DEV_PM_OPS(il_pm_ops, il_pci_suspend, il_pci_resume); +EXPORT_SYMBOL(il_pm_ops); + +#endif /* CONFIG_PM_SLEEP */ + +static void +il_update_qos(struct il_priv *il) +{ + if (test_bit(S_EXIT_PENDING, &il->status)) + return; + + il->qos_data.def_qos_parm.qos_flags = 0; + + if (il->qos_data.qos_active) + il->qos_data.def_qos_parm.qos_flags |= + QOS_PARAM_FLG_UPDATE_EDCA_MSK; + + if (il->ht.enabled) + il->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; + + D_QOS("send QoS cmd with Qos active=%d FLAGS=0x%X\n", + il->qos_data.qos_active, il->qos_data.def_qos_parm.qos_flags); + + il_send_cmd_pdu_async(il, C_QOS_PARAM, sizeof(struct il_qosparam_cmd), + &il->qos_data.def_qos_parm, NULL); +} + +/** + * il_mac_config - mac80211 config callback + */ +int +il_mac_config(struct ieee80211_hw *hw, u32 changed) +{ + struct il_priv *il = hw->priv; + const struct il_channel_info *ch_info; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_channel *channel = conf->chandef.chan; + struct il_ht_config *ht_conf = &il->current_ht_config; + unsigned long flags = 0; + int ret = 0; + u16 ch; + int scan_active = 0; + bool ht_changed = false; + + mutex_lock(&il->mutex); + D_MAC80211("enter: channel %d changed 0x%X\n", channel->hw_value, + changed); + + if (unlikely(test_bit(S_SCANNING, &il->status))) { + scan_active = 1; + D_MAC80211("scan active\n"); + } + + if (changed & + (IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) { + /* mac80211 uses static for non-HT which is what we want */ + il->current_ht_config.smps = conf->smps_mode; + + /* + * Recalculate chain counts. + * + * If monitor mode is enabled then mac80211 will + * set up the SM PS mode to OFF if an HT channel is + * configured. + */ + if (il->ops->set_rxon_chain) + il->ops->set_rxon_chain(il); + } + + /* during scanning mac80211 will delay channel setting until + * scan finish with changed = 0 + */ + if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { + + if (scan_active) + goto set_ch_out; + + ch = channel->hw_value; + ch_info = il_get_channel_info(il, channel->band, ch); + if (!il_is_channel_valid(ch_info)) { + D_MAC80211("leave - invalid channel\n"); + ret = -EINVAL; + goto set_ch_out; + } + + if (il->iw_mode == NL80211_IFTYPE_ADHOC && + !il_is_channel_ibss(ch_info)) { + D_MAC80211("leave - not IBSS channel\n"); + ret = -EINVAL; + goto set_ch_out; + } + + spin_lock_irqsave(&il->lock, flags); + + /* Configure HT40 channels */ + if (il->ht.enabled != conf_is_ht(conf)) { + il->ht.enabled = conf_is_ht(conf); + ht_changed = true; + } + if (il->ht.enabled) { + if (conf_is_ht40_minus(conf)) { + il->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_BELOW; + il->ht.is_40mhz = true; + } else if (conf_is_ht40_plus(conf)) { + il->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + il->ht.is_40mhz = true; + } else { + il->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_NONE; + il->ht.is_40mhz = false; + } + } else + il->ht.is_40mhz = false; + + /* + * Default to no protection. Protection mode will + * later be set from BSS config in il_ht_conf + */ + il->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; + + /* if we are switching from ht to 2.4 clear flags + * from any ht related info since 2.4 does not + * support ht */ + if ((le16_to_cpu(il->staging.channel) != ch)) + il->staging.flags = 0; + + il_set_rxon_channel(il, channel); + il_set_rxon_ht(il, ht_conf); + + il_set_flags_for_band(il, channel->band, il->vif); + + spin_unlock_irqrestore(&il->lock, flags); + + if (il->ops->update_bcast_stations) + ret = il->ops->update_bcast_stations(il); + +set_ch_out: + /* The list of supported rates and rate mask can be different + * for each band; since the band may have changed, reset + * the rate mask to what mac80211 lists */ + il_set_rate(il); + } + + if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) { + il->power_data.ps_disabled = !(conf->flags & IEEE80211_CONF_PS); + ret = il_power_update_mode(il, false); + if (ret) + D_MAC80211("Error setting sleep level\n"); + } + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + D_MAC80211("TX Power old=%d new=%d\n", il->tx_power_user_lmt, + conf->power_level); + + il_set_tx_power(il, conf->power_level, false); + } + + if (!il_is_ready(il)) { + D_MAC80211("leave - not ready\n"); + goto out; + } + + if (scan_active) + goto out; + + if (memcmp(&il->active, &il->staging, sizeof(il->staging))) + il_commit_rxon(il); + else + D_INFO("Not re-sending same RXON configuration.\n"); + if (ht_changed) + il_update_qos(il); + +out: + D_MAC80211("leave ret %d\n", ret); + mutex_unlock(&il->mutex); + + return ret; +} +EXPORT_SYMBOL(il_mac_config); + +void +il_mac_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct il_priv *il = hw->priv; + unsigned long flags; + + mutex_lock(&il->mutex); + D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); + + spin_lock_irqsave(&il->lock, flags); + + memset(&il->current_ht_config, 0, sizeof(struct il_ht_config)); + + /* new association get rid of ibss beacon skb */ + if (il->beacon_skb) + dev_kfree_skb(il->beacon_skb); + il->beacon_skb = NULL; + il->timestamp = 0; + + spin_unlock_irqrestore(&il->lock, flags); + + il_scan_cancel_timeout(il, 100); + if (!il_is_ready_rf(il)) { + D_MAC80211("leave - not ready\n"); + mutex_unlock(&il->mutex); + return; + } + + /* we are restarting association process */ + il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + il_commit_rxon(il); + + il_set_rate(il); + + D_MAC80211("leave\n"); + mutex_unlock(&il->mutex); +} +EXPORT_SYMBOL(il_mac_reset_tsf); + +static void +il_ht_conf(struct il_priv *il, struct ieee80211_vif *vif) +{ + struct il_ht_config *ht_conf = &il->current_ht_config; + struct ieee80211_sta *sta; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + + D_ASSOC("enter:\n"); + + if (!il->ht.enabled) + return; + + il->ht.protection = + bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; + il->ht.non_gf_sta_present = + !!(bss_conf-> + ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + + ht_conf->single_chain_sufficient = false; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + rcu_read_lock(); + sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (sta) { + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + int maxstreams; + + maxstreams = + (ht_cap->mcs. + tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) + >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + maxstreams += 1; + + if (ht_cap->mcs.rx_mask[1] == 0 && + ht_cap->mcs.rx_mask[2] == 0) + ht_conf->single_chain_sufficient = true; + if (maxstreams <= 1) + ht_conf->single_chain_sufficient = true; + } else { + /* + * If at all, this can only happen through a race + * when the AP disconnects us while we're still + * setting up the connection, in that case mac80211 + * will soon tell us about that. + */ + ht_conf->single_chain_sufficient = true; + } + rcu_read_unlock(); + break; + case NL80211_IFTYPE_ADHOC: + ht_conf->single_chain_sufficient = true; + break; + default: + break; + } + + D_ASSOC("leave\n"); +} + +static inline void +il_set_no_assoc(struct il_priv *il, struct ieee80211_vif *vif) +{ + /* + * inform the ucode that there is no longer an + * association and that no more packets should be + * sent + */ + il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + il->staging.assoc_id = 0; + il_commit_rxon(il); +} + +static void +il_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct il_priv *il = hw->priv; + unsigned long flags; + __le64 timestamp; + struct sk_buff *skb = ieee80211_beacon_get(hw, vif); + + if (!skb) + return; + + D_MAC80211("enter\n"); + + lockdep_assert_held(&il->mutex); + + if (!il->beacon_enabled) { + IL_ERR("update beacon with no beaconing enabled\n"); + dev_kfree_skb(skb); + return; + } + + spin_lock_irqsave(&il->lock, flags); + + if (il->beacon_skb) + dev_kfree_skb(il->beacon_skb); + + il->beacon_skb = skb; + + timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; + il->timestamp = le64_to_cpu(timestamp); + + D_MAC80211("leave\n"); + spin_unlock_irqrestore(&il->lock, flags); + + if (!il_is_ready_rf(il)) { + D_MAC80211("leave - RF not ready\n"); + return; + } + + il->ops->post_associate(il); +} + +void +il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, u32 changes) +{ + struct il_priv *il = hw->priv; + int ret; + + mutex_lock(&il->mutex); + D_MAC80211("enter: changes 0x%x\n", changes); + + if (!il_is_alive(il)) { + D_MAC80211("leave - not alive\n"); + mutex_unlock(&il->mutex); + return; + } + + if (changes & BSS_CHANGED_QOS) { + unsigned long flags; + + spin_lock_irqsave(&il->lock, flags); + il->qos_data.qos_active = bss_conf->qos; + il_update_qos(il); + spin_unlock_irqrestore(&il->lock, flags); + } + + if (changes & BSS_CHANGED_BEACON_ENABLED) { + /* FIXME: can we remove beacon_enabled ? */ + if (vif->bss_conf.enable_beacon) + il->beacon_enabled = true; + else + il->beacon_enabled = false; + } + + if (changes & BSS_CHANGED_BSSID) { + D_MAC80211("BSSID %pM\n", bss_conf->bssid); + + /* + * On passive channel we wait with blocked queues to see if + * there is traffic on that channel. If no frame will be + * received (what is very unlikely since scan detects AP on + * that channel, but theoretically possible), mac80211 associate + * procedure will time out and mac80211 will call us with NULL + * bssid. We have to unblock queues on such condition. + */ + if (is_zero_ether_addr(bss_conf->bssid)) + il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + + /* + * If there is currently a HW scan going on in the background, + * then we need to cancel it, otherwise sometimes we are not + * able to authenticate (FIXME: why ?) + */ + if (il_scan_cancel_timeout(il, 100)) { + D_MAC80211("leave - scan abort failed\n"); + mutex_unlock(&il->mutex); + return; + } + + /* mac80211 only sets assoc when in STATION mode */ + memcpy(il->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); + + /* FIXME: currently needed in a few places */ + memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); + } + + /* + * This needs to be after setting the BSSID in case + * mac80211 decides to do both changes at once because + * it will invoke post_associate. + */ + if (vif->type == NL80211_IFTYPE_ADHOC && (changes & BSS_CHANGED_BEACON)) + il_beacon_update(hw, vif); + + if (changes & BSS_CHANGED_ERP_PREAMBLE) { + D_MAC80211("ERP_PREAMBLE %d\n", bss_conf->use_short_preamble); + if (bss_conf->use_short_preamble) + il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + else + il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + } + + if (changes & BSS_CHANGED_ERP_CTS_PROT) { + D_MAC80211("ERP_CTS %d\n", bss_conf->use_cts_prot); + if (bss_conf->use_cts_prot && il->band != IEEE80211_BAND_5GHZ) + il->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; + else + il->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; + if (bss_conf->use_cts_prot) + il->staging.flags |= RXON_FLG_SELF_CTS_EN; + else + il->staging.flags &= ~RXON_FLG_SELF_CTS_EN; + } + + if (changes & BSS_CHANGED_BASIC_RATES) { + /* XXX use this information + * + * To do that, remove code from il_set_rate() and put something + * like this here: + * + if (A-band) + il->staging.ofdm_basic_rates = + bss_conf->basic_rates; + else + il->staging.ofdm_basic_rates = + bss_conf->basic_rates >> 4; + il->staging.cck_basic_rates = + bss_conf->basic_rates & 0xF; + */ + } + + if (changes & BSS_CHANGED_HT) { + il_ht_conf(il, vif); + + if (il->ops->set_rxon_chain) + il->ops->set_rxon_chain(il); + } + + if (changes & BSS_CHANGED_ASSOC) { + D_MAC80211("ASSOC %d\n", bss_conf->assoc); + if (bss_conf->assoc) { + il->timestamp = bss_conf->sync_tsf; + + if (!il_is_rfkill(il)) + il->ops->post_associate(il); + } else + il_set_no_assoc(il, vif); + } + + if (changes && il_is_associated(il) && bss_conf->aid) { + D_MAC80211("Changes (%#x) while associated\n", changes); + ret = il_send_rxon_assoc(il); + if (!ret) { + /* Sync active_rxon with latest change. */ + memcpy((void *)&il->active, &il->staging, + sizeof(struct il_rxon_cmd)); + } + } + + if (changes & BSS_CHANGED_BEACON_ENABLED) { + if (vif->bss_conf.enable_beacon) { + memcpy(il->staging.bssid_addr, bss_conf->bssid, + ETH_ALEN); + memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); + il->ops->config_ap(il); + } else + il_set_no_assoc(il, vif); + } + + if (changes & BSS_CHANGED_IBSS) { + ret = il->ops->manage_ibss_station(il, vif, + bss_conf->ibss_joined); + if (ret) + IL_ERR("failed to %s IBSS station %pM\n", + bss_conf->ibss_joined ? "add" : "remove", + bss_conf->bssid); + } + + D_MAC80211("leave\n"); + mutex_unlock(&il->mutex); +} +EXPORT_SYMBOL(il_mac_bss_info_changed); + +irqreturn_t +il_isr(int irq, void *data) +{ + struct il_priv *il = data; + u32 inta, inta_mask; + u32 inta_fh; + unsigned long flags; + if (!il) + return IRQ_NONE; + + spin_lock_irqsave(&il->lock, flags); + + /* Disable (but don't clear!) interrupts here to avoid + * back-to-back ISRs and sporadic interrupts from our NIC. + * If we have something to service, the tasklet will re-enable ints. + * If we *don't* have something, we'll re-enable before leaving here. */ + inta_mask = _il_rd(il, CSR_INT_MASK); /* just for debug */ + _il_wr(il, CSR_INT_MASK, 0x00000000); + + /* Discover which interrupts are active/pending */ + inta = _il_rd(il, CSR_INT); + inta_fh = _il_rd(il, CSR_FH_INT_STATUS); + + /* Ignore interrupt if there's nothing in NIC to service. + * This may be due to IRQ shared with another device, + * or due to sporadic interrupts thrown from our NIC. */ + if (!inta && !inta_fh) { + D_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n"); + goto none; + } + + if (inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0) { + /* Hardware disappeared. It might have already raised + * an interrupt */ + IL_WARN("HARDWARE GONE?? INTA == 0x%08x\n", inta); + goto unplugged; + } + + D_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, inta_mask, + inta_fh); + + inta &= ~CSR_INT_BIT_SCD; + + /* il_irq_tasklet() will service interrupts and re-enable them */ + if (likely(inta || inta_fh)) + tasklet_schedule(&il->irq_tasklet); + +unplugged: + spin_unlock_irqrestore(&il->lock, flags); + return IRQ_HANDLED; + +none: + /* re-enable interrupts here since we don't have anything to service. */ + /* only Re-enable if disabled by irq */ + if (test_bit(S_INT_ENABLED, &il->status)) + il_enable_interrupts(il); + spin_unlock_irqrestore(&il->lock, flags); + return IRQ_NONE; +} +EXPORT_SYMBOL(il_isr); + +/* + * il_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this + * function. + */ +void +il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info, + __le16 fc, __le32 *tx_flags) +{ + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { + *tx_flags |= TX_CMD_FLG_RTS_MSK; + *tx_flags &= ~TX_CMD_FLG_CTS_MSK; + *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; + + if (!ieee80211_is_mgmt(fc)) + return; + + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_AUTH): + case cpu_to_le16(IEEE80211_STYPE_DEAUTH): + case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): + case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): + *tx_flags &= ~TX_CMD_FLG_RTS_MSK; + *tx_flags |= TX_CMD_FLG_CTS_MSK; + break; + } + } else if (info->control.rates[0]. + flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + *tx_flags &= ~TX_CMD_FLG_RTS_MSK; + *tx_flags |= TX_CMD_FLG_CTS_MSK; + *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; + } +} +EXPORT_SYMBOL(il_tx_cmd_protection); diff --git a/drivers/net/wireless/intel/iwlegacy/common.h b/drivers/net/wireless/intel/iwlegacy/common.h new file mode 100644 index 000000000000..ce52cf114fde --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/common.h @@ -0,0 +1,3084 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#ifndef __il_core_h__ +#define __il_core_h__ + +#include +#include /* for struct pci_device_id */ +#include +#include +#include +#include +#include +#include + +#include "commands.h" +#include "csr.h" +#include "prph.h" + +struct il_host_cmd; +struct il_cmd; +struct il_tx_queue; + +#define IL_ERR(f, a...) dev_err(&il->pci_dev->dev, f, ## a) +#define IL_WARN(f, a...) dev_warn(&il->pci_dev->dev, f, ## a) +#define IL_INFO(f, a...) dev_info(&il->pci_dev->dev, f, ## a) + +#define RX_QUEUE_SIZE 256 +#define RX_QUEUE_MASK 255 +#define RX_QUEUE_SIZE_LOG 8 + +/* + * RX related structures and functions + */ +#define RX_FREE_BUFFERS 64 +#define RX_LOW_WATERMARK 8 + +#define U32_PAD(n) ((4-(n))&0x3) + +/* CT-KILL constants */ +#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ + +/* Default noise level to report when noise measurement is not available. + * This may be because we're: + * 1) Not associated (4965, no beacon stats being sent to driver) + * 2) Scanning (noise measurement does not apply to associated channel) + * 3) Receiving CCK (3945 delivers noise info only for OFDM frames) + * Use default noise value of -127 ... this is below the range of measurable + * Rx dBm for either 3945 or 4965, so it can indicate "unmeasurable" to user. + * Also, -127 works better than 0 when averaging frames with/without + * noise info (e.g. averaging might be done in app); measured dBm values are + * always negative ... using a negative value as the default keeps all + * averages within an s8's (used in some apps) range of negative values. */ +#define IL_NOISE_MEAS_NOT_AVAILABLE (-127) + +/* + * RTS threshold here is total size [2347] minus 4 FCS bytes + * Per spec: + * a value of 0 means RTS on all data/management packets + * a value > max MSDU size means no RTS + * else RTS for data/management frames where MPDU is larger + * than RTS value. + */ +#define DEFAULT_RTS_THRESHOLD 2347U +#define MIN_RTS_THRESHOLD 0U +#define MAX_RTS_THRESHOLD 2347U +#define MAX_MSDU_SIZE 2304U +#define MAX_MPDU_SIZE 2346U +#define DEFAULT_BEACON_INTERVAL 100U +#define DEFAULT_SHORT_RETRY_LIMIT 7U +#define DEFAULT_LONG_RETRY_LIMIT 4U + +struct il_rx_buf { + dma_addr_t page_dma; + struct page *page; + struct list_head list; +}; + +#define rxb_addr(r) page_address(r->page) + +/* defined below */ +struct il_device_cmd; + +struct il_cmd_meta { + /* only for SYNC commands, iff the reply skb is wanted */ + struct il_host_cmd *source; + /* + * only for ASYNC commands + * (which is somewhat stupid -- look at common.c for instance + * which duplicates a bunch of code because the callback isn't + * invoked for SYNC commands, if it were and its result passed + * through it would be simpler...) + */ + void (*callback) (struct il_priv *il, struct il_device_cmd *cmd, + struct il_rx_pkt *pkt); + + /* The CMD_SIZE_HUGE flag bit indicates that the command + * structure is stored at the end of the shared queue memory. */ + u32 flags; + + DEFINE_DMA_UNMAP_ADDR(mapping); + DEFINE_DMA_UNMAP_LEN(len); +}; + +/* + * Generic queue structure + * + * Contains common data for Rx and Tx queues + */ +struct il_queue { + int n_bd; /* number of BDs in this queue */ + int write_ptr; /* 1-st empty entry (idx) host_w */ + int read_ptr; /* last used entry (idx) host_r */ + /* use for monitoring and recovering the stuck queue */ + dma_addr_t dma_addr; /* physical addr for BD's */ + int n_win; /* safe queue win */ + u32 id; + int low_mark; /* low watermark, resume queue if free + * space more than this */ + int high_mark; /* high watermark, stop queue if free + * space less than this */ +}; + +/** + * struct il_tx_queue - Tx Queue for DMA + * @q: generic Rx/Tx queue descriptor + * @bd: base of circular buffer of TFDs + * @cmd: array of command/TX buffer pointers + * @meta: array of meta data for each command/tx buffer + * @dma_addr_cmd: physical address of cmd/tx buffer array + * @skbs: array of per-TFD socket buffer pointers + * @time_stamp: time (in jiffies) of last read_ptr change + * @need_update: indicates need to update read/write idx + * @sched_retry: indicates queue is high-throughput aggregation (HT AGG) enabled + * + * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame + * descriptors) and required locking structures. + */ +#define TFD_TX_CMD_SLOTS 256 +#define TFD_CMD_SLOTS 32 + +struct il_tx_queue { + struct il_queue q; + void *tfds; + struct il_device_cmd **cmd; + struct il_cmd_meta *meta; + struct sk_buff **skbs; + unsigned long time_stamp; + u8 need_update; + u8 sched_retry; + u8 active; + u8 swq_id; +}; + +/* + * EEPROM access time values: + * + * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG. + * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). + * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. + * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. + */ +#define IL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ + +#define IL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ +#define IL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ + +/* + * Regulatory channel usage flags in EEPROM struct il4965_eeprom_channel.flags. + * + * IBSS and/or AP operation is allowed *only* on those channels with + * (VALID && IBSS && ACTIVE && !RADAR). This restriction is in place because + * RADAR detection is not supported by the 4965 driver, but is a + * requirement for establishing a new network for legal operation on channels + * requiring RADAR detection or restricting ACTIVE scanning. + * + * NOTE: "WIDE" flag does not indicate anything about "HT40" 40 MHz channels. + * It only indicates that 20 MHz channel use is supported; HT40 channel + * usage is indicated by a separate set of regulatory flags for each + * HT40 channel pair. + * + * NOTE: Using a channel inappropriately will result in a uCode error! + */ +#define IL_NUM_TX_CALIB_GROUPS 5 +enum { + EEPROM_CHANNEL_VALID = (1 << 0), /* usable for this SKU/geo */ + EEPROM_CHANNEL_IBSS = (1 << 1), /* usable as an IBSS channel */ + /* Bit 2 Reserved */ + EEPROM_CHANNEL_ACTIVE = (1 << 3), /* active scanning allowed */ + EEPROM_CHANNEL_RADAR = (1 << 4), /* radar detection required */ + EEPROM_CHANNEL_WIDE = (1 << 5), /* 20 MHz channel okay */ + /* Bit 6 Reserved (was Narrow Channel) */ + EEPROM_CHANNEL_DFS = (1 << 7), /* dynamic freq selection candidate */ +}; + +/* SKU Capabilities */ +/* 3945 only */ +#define EEPROM_SKU_CAP_SW_RF_KILL_ENABLE (1 << 0) +#define EEPROM_SKU_CAP_HW_RF_KILL_ENABLE (1 << 1) + +/* *regulatory* channel data format in eeprom, one for each channel. + * There are separate entries for HT40 (40 MHz) vs. normal (20 MHz) channels. */ +struct il_eeprom_channel { + u8 flags; /* EEPROM_CHANNEL_* flags copied from EEPROM */ + s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */ +} __packed; + +/* 3945 Specific */ +#define EEPROM_3945_EEPROM_VERSION (0x2f) + +/* 4965 has two radio transmitters (and 3 radio receivers) */ +#define EEPROM_TX_POWER_TX_CHAINS (2) + +/* 4965 has room for up to 8 sets of txpower calibration data */ +#define EEPROM_TX_POWER_BANDS (8) + +/* 4965 factory calibration measures txpower gain settings for + * each of 3 target output levels */ +#define EEPROM_TX_POWER_MEASUREMENTS (3) + +/* 4965 Specific */ +/* 4965 driver does not work with txpower calibration version < 5 */ +#define EEPROM_4965_TX_POWER_VERSION (5) +#define EEPROM_4965_EEPROM_VERSION (0x2f) +#define EEPROM_4965_CALIB_VERSION_OFFSET (2*0xB6) /* 2 bytes */ +#define EEPROM_4965_CALIB_TXPOWER_OFFSET (2*0xE8) /* 48 bytes */ +#define EEPROM_4965_BOARD_REVISION (2*0x4F) /* 2 bytes */ +#define EEPROM_4965_BOARD_PBA (2*0x56+1) /* 9 bytes */ + +/* 2.4 GHz */ +extern const u8 il_eeprom_band_1[14]; + +/* + * factory calibration data for one txpower level, on one channel, + * measured on one of the 2 tx chains (radio transmitter and associated + * antenna). EEPROM contains: + * + * 1) Temperature (degrees Celsius) of device when measurement was made. + * + * 2) Gain table idx used to achieve the target measurement power. + * This refers to the "well-known" gain tables (see 4965.h). + * + * 3) Actual measured output power, in half-dBm ("34" = 17 dBm). + * + * 4) RF power amplifier detector level measurement (not used). + */ +struct il_eeprom_calib_measure { + u8 temperature; /* Device temperature (Celsius) */ + u8 gain_idx; /* Index into gain table */ + u8 actual_pow; /* Measured RF output power, half-dBm */ + s8 pa_det; /* Power amp detector level (not used) */ +} __packed; + +/* + * measurement set for one channel. EEPROM contains: + * + * 1) Channel number measured + * + * 2) Measurements for each of 3 power levels for each of 2 radio transmitters + * (a.k.a. "tx chains") (6 measurements altogether) + */ +struct il_eeprom_calib_ch_info { + u8 ch_num; + struct il_eeprom_calib_measure + measurements[EEPROM_TX_POWER_TX_CHAINS] + [EEPROM_TX_POWER_MEASUREMENTS]; +} __packed; + +/* + * txpower subband info. + * + * For each frequency subband, EEPROM contains the following: + * + * 1) First and last channels within range of the subband. "0" values + * indicate that this sample set is not being used. + * + * 2) Sample measurement sets for 2 channels close to the range endpoints. + */ +struct il_eeprom_calib_subband_info { + u8 ch_from; /* channel number of lowest channel in subband */ + u8 ch_to; /* channel number of highest channel in subband */ + struct il_eeprom_calib_ch_info ch1; + struct il_eeprom_calib_ch_info ch2; +} __packed; + +/* + * txpower calibration info. EEPROM contains: + * + * 1) Factory-measured saturation power levels (maximum levels at which + * tx power amplifier can output a signal without too much distortion). + * There is one level for 2.4 GHz band and one for 5 GHz band. These + * values apply to all channels within each of the bands. + * + * 2) Factory-measured power supply voltage level. This is assumed to be + * constant (i.e. same value applies to all channels/bands) while the + * factory measurements are being made. + * + * 3) Up to 8 sets of factory-measured txpower calibration values. + * These are for different frequency ranges, since txpower gain + * characteristics of the analog radio circuitry vary with frequency. + * + * Not all sets need to be filled with data; + * struct il_eeprom_calib_subband_info contains range of channels + * (0 if unused) for each set of data. + */ +struct il_eeprom_calib_info { + u8 saturation_power24; /* half-dBm (e.g. "34" = 17 dBm) */ + u8 saturation_power52; /* half-dBm */ + __le16 voltage; /* signed */ + struct il_eeprom_calib_subband_info band_info[EEPROM_TX_POWER_BANDS]; +} __packed; + +/* General */ +#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ +#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ +#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ +#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ +#define EEPROM_VERSION (2*0x44) /* 2 bytes */ +#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ +#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ +#define EEPROM_WOWLAN_MODE (2*0x47) /* 2 bytes */ +#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ +#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ + +/* The following masks are to be applied on EEPROM_RADIO_CONFIG */ +#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ +#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ +#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ +#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ +#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ +#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ + +#define EEPROM_3945_RF_CFG_TYPE_MAX 0x0 +#define EEPROM_4965_RF_CFG_TYPE_MAX 0x1 + +/* + * Per-channel regulatory data. + * + * Each channel that *might* be supported by iwl has a fixed location + * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory + * txpower (MSB). + * + * Entries immediately below are for 20 MHz channel width. HT40 (40 MHz) + * channels (only for 4965, not supported by 3945) appear later in the EEPROM. + * + * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 + */ +#define EEPROM_REGULATORY_SKU_ID (2*0x60) /* 4 bytes */ +#define EEPROM_REGULATORY_BAND_1 (2*0x62) /* 2 bytes */ +#define EEPROM_REGULATORY_BAND_1_CHANNELS (2*0x63) /* 28 bytes */ + +/* + * 4.9 GHz channels 183, 184, 185, 187, 188, 189, 192, 196, + * 5.0 GHz channels 7, 8, 11, 12, 16 + * (4915-5080MHz) (none of these is ever supported) + */ +#define EEPROM_REGULATORY_BAND_2 (2*0x71) /* 2 bytes */ +#define EEPROM_REGULATORY_BAND_2_CHANNELS (2*0x72) /* 26 bytes */ + +/* + * 5.2 GHz channels 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 + * (5170-5320MHz) + */ +#define EEPROM_REGULATORY_BAND_3 (2*0x7F) /* 2 bytes */ +#define EEPROM_REGULATORY_BAND_3_CHANNELS (2*0x80) /* 24 bytes */ + +/* + * 5.5 GHz channels 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 + * (5500-5700MHz) + */ +#define EEPROM_REGULATORY_BAND_4 (2*0x8C) /* 2 bytes */ +#define EEPROM_REGULATORY_BAND_4_CHANNELS (2*0x8D) /* 22 bytes */ + +/* + * 5.7 GHz channels 145, 149, 153, 157, 161, 165 + * (5725-5825MHz) + */ +#define EEPROM_REGULATORY_BAND_5 (2*0x98) /* 2 bytes */ +#define EEPROM_REGULATORY_BAND_5_CHANNELS (2*0x99) /* 12 bytes */ + +/* + * 2.4 GHz HT40 channels 1 (5), 2 (6), 3 (7), 4 (8), 5 (9), 6 (10), 7 (11) + * + * The channel listed is the center of the lower 20 MHz half of the channel. + * The overall center frequency is actually 2 channels (10 MHz) above that, + * and the upper half of each HT40 channel is centered 4 channels (20 MHz) away + * from the lower half; e.g. the upper half of HT40 channel 1 is channel 5, + * and the overall HT40 channel width centers on channel 3. + * + * NOTE: The RXON command uses 20 MHz channel numbers to specify the + * control channel to which to tune. RXON also specifies whether the + * control channel is the upper or lower half of a HT40 channel. + * + * NOTE: 4965 does not support HT40 channels on 2.4 GHz. + */ +#define EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS (2*0xA0) /* 14 bytes */ + +/* + * 5.2 GHz HT40 channels 36 (40), 44 (48), 52 (56), 60 (64), + * 100 (104), 108 (112), 116 (120), 124 (128), 132 (136), 149 (153), 157 (161) + */ +#define EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS (2*0xA8) /* 22 bytes */ + +#define EEPROM_REGULATORY_BAND_NO_HT40 (0) + +int il_eeprom_init(struct il_priv *il); +void il_eeprom_free(struct il_priv *il); +const u8 *il_eeprom_query_addr(const struct il_priv *il, size_t offset); +u16 il_eeprom_query16(const struct il_priv *il, size_t offset); +int il_init_channel_map(struct il_priv *il); +void il_free_channel_map(struct il_priv *il); +const struct il_channel_info *il_get_channel_info(const struct il_priv *il, + enum ieee80211_band band, + u16 channel); + +#define IL_NUM_SCAN_RATES (2) + +struct il4965_channel_tgd_info { + u8 type; + s8 max_power; +}; + +struct il4965_channel_tgh_info { + s64 last_radar_time; +}; + +#define IL4965_MAX_RATE (33) + +struct il3945_clip_group { + /* maximum power level to prevent clipping for each rate, derived by + * us from this band's saturation power in EEPROM */ + const s8 clip_powers[IL_MAX_RATES]; +}; + +/* current Tx power values to use, one for each rate for each channel. + * requested power is limited by: + * -- regulatory EEPROM limits for this channel + * -- hardware capabilities (clip-powers) + * -- spectrum management + * -- user preference (e.g. iwconfig) + * when requested power is set, base power idx must also be set. */ +struct il3945_channel_power_info { + struct il3945_tx_power tpc; /* actual radio and DSP gain settings */ + s8 power_table_idx; /* actual (compenst'd) idx into gain table */ + s8 base_power_idx; /* gain idx for power at factory temp. */ + s8 requested_power; /* power (dBm) requested for this chnl/rate */ +}; + +/* current scan Tx power values to use, one for each scan rate for each + * channel. */ +struct il3945_scan_power_info { + struct il3945_tx_power tpc; /* actual radio and DSP gain settings */ + s8 power_table_idx; /* actual (compenst'd) idx into gain table */ + s8 requested_power; /* scan pwr (dBm) requested for chnl/rate */ +}; + +/* + * One for each channel, holds all channel setup data + * Some of the fields (e.g. eeprom and flags/max_power_avg) are redundant + * with one another! + */ +struct il_channel_info { + struct il4965_channel_tgd_info tgd; + struct il4965_channel_tgh_info tgh; + struct il_eeprom_channel eeprom; /* EEPROM regulatory limit */ + struct il_eeprom_channel ht40_eeprom; /* EEPROM regulatory limit for + * HT40 channel */ + + u8 channel; /* channel number */ + u8 flags; /* flags copied from EEPROM */ + s8 max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ + s8 curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) limit */ + s8 min_power; /* always 0 */ + s8 scan_power; /* (dBm) regul. eeprom, direct scans, any rate */ + + u8 group_idx; /* 0-4, maps channel to group1/2/3/4/5 */ + u8 band_idx; /* 0-4, maps channel to band1/2/3/4/5 */ + enum ieee80211_band band; + + /* HT40 channel info */ + s8 ht40_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ + u8 ht40_flags; /* flags copied from EEPROM */ + u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */ + + /* Radio/DSP gain settings for each "normal" data Tx rate. + * These include, in addition to RF and DSP gain, a few fields for + * remembering/modifying gain settings (idxes). */ + struct il3945_channel_power_info power_info[IL4965_MAX_RATE]; + + /* Radio/DSP gain settings for each scan rate, for directed scans. */ + struct il3945_scan_power_info scan_pwr_info[IL_NUM_SCAN_RATES]; +}; + +#define IL_TX_FIFO_BK 0 /* shared */ +#define IL_TX_FIFO_BE 1 +#define IL_TX_FIFO_VI 2 /* shared */ +#define IL_TX_FIFO_VO 3 +#define IL_TX_FIFO_UNUSED -1 + +/* Minimum number of queues. MAX_NUM is defined in hw specific files. + * Set the minimum to accommodate the 4 standard TX queues, 1 command + * queue, 2 (unused) HCCA queues, and 4 HT queues (one for each AC) */ +#define IL_MIN_NUM_QUEUES 10 + +#define IL_DEFAULT_CMD_QUEUE_NUM 4 + +#define IEEE80211_DATA_LEN 2304 +#define IEEE80211_4ADDR_LEN 30 +#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) +#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) + +struct il_frame { + union { + struct ieee80211_hdr frame; + struct il_tx_beacon_cmd beacon; + u8 raw[IEEE80211_FRAME_LEN]; + u8 cmd[360]; + } u; + struct list_head list; +}; + +enum { + CMD_SYNC = 0, + CMD_SIZE_NORMAL = 0, + CMD_NO_SKB = 0, + CMD_SIZE_HUGE = (1 << 0), + CMD_ASYNC = (1 << 1), + CMD_WANT_SKB = (1 << 2), + CMD_MAPPED = (1 << 3), +}; + +#define DEF_CMD_PAYLOAD_SIZE 320 + +/** + * struct il_device_cmd + * + * For allocation of the command and tx queues, this establishes the overall + * size of the largest command we send to uCode, except for a scan command + * (which is relatively huge; space is allocated separately). + */ +struct il_device_cmd { + struct il_cmd_header hdr; /* uCode API */ + union { + u32 flags; + u8 val8; + u16 val16; + u32 val32; + struct il_tx_cmd tx; + u8 payload[DEF_CMD_PAYLOAD_SIZE]; + } __packed cmd; +} __packed; + +#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct il_device_cmd)) + +struct il_host_cmd { + const void *data; + unsigned long reply_page; + void (*callback) (struct il_priv *il, struct il_device_cmd *cmd, + struct il_rx_pkt *pkt); + u32 flags; + u16 len; + u8 id; +}; + +#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 +#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 +#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 + +/** + * struct il_rx_queue - Rx queue + * @bd: driver's pointer to buffer of receive buffer descriptors (rbd) + * @bd_dma: bus address of buffer of receive buffer descriptors (rbd) + * @read: Shared idx to newest available Rx buffer + * @write: Shared idx to oldest written Rx packet + * @free_count: Number of pre-allocated buffers in rx_free + * @rx_free: list of free SKBs for use + * @rx_used: List of Rx buffers with no SKB + * @need_update: flag to indicate we need to update read/write idx + * @rb_stts: driver's pointer to receive buffer status + * @rb_stts_dma: bus address of receive buffer status + * + * NOTE: rx_free and rx_used are used as a FIFO for il_rx_bufs + */ +struct il_rx_queue { + __le32 *bd; + dma_addr_t bd_dma; + struct il_rx_buf pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; + struct il_rx_buf *queue[RX_QUEUE_SIZE]; + u32 read; + u32 write; + u32 free_count; + u32 write_actual; + struct list_head rx_free; + struct list_head rx_used; + int need_update; + struct il_rb_status *rb_stts; + dma_addr_t rb_stts_dma; + spinlock_t lock; +}; + +#define IL_SUPPORTED_RATES_IE_LEN 8 + +#define MAX_TID_COUNT 9 + +#define IL_INVALID_RATE 0xFF +#define IL_INVALID_VALUE -1 + +/** + * struct il_ht_agg -- aggregation status while waiting for block-ack + * @txq_id: Tx queue used for Tx attempt + * @frame_count: # frames attempted by Tx command + * @wait_for_ba: Expect block-ack before next Tx reply + * @start_idx: Index of 1st Transmit Frame Descriptor (TFD) in Tx win + * @bitmap0: Low order bitmap, one bit for each frame pending ACK in Tx win + * @bitmap1: High order, one bit for each frame pending ACK in Tx win + * @rate_n_flags: Rate at which Tx was attempted + * + * If C_TX indicates that aggregation was attempted, driver must wait + * for block ack (N_COMPRESSED_BA). This struct stores tx reply info + * until block ack arrives. + */ +struct il_ht_agg { + u16 txq_id; + u16 frame_count; + u16 wait_for_ba; + u16 start_idx; + u64 bitmap; + u32 rate_n_flags; +#define IL_AGG_OFF 0 +#define IL_AGG_ON 1 +#define IL_EMPTYING_HW_QUEUE_ADDBA 2 +#define IL_EMPTYING_HW_QUEUE_DELBA 3 + u8 state; +}; + +struct il_tid_data { + u16 seq_number; /* 4965 only */ + u16 tfds_in_queue; + struct il_ht_agg agg; +}; + +struct il_hw_key { + u32 cipher; + int keylen; + u8 keyidx; + u8 key[32]; +}; + +union il_ht_rate_supp { + u16 rates; + struct { + u8 siso_rate; + u8 mimo_rate; + }; +}; + +#define CFG_HT_RX_AMPDU_FACTOR_8K (0x0) +#define CFG_HT_RX_AMPDU_FACTOR_16K (0x1) +#define CFG_HT_RX_AMPDU_FACTOR_32K (0x2) +#define CFG_HT_RX_AMPDU_FACTOR_64K (0x3) +#define CFG_HT_RX_AMPDU_FACTOR_DEF CFG_HT_RX_AMPDU_FACTOR_64K +#define CFG_HT_RX_AMPDU_FACTOR_MAX CFG_HT_RX_AMPDU_FACTOR_64K +#define CFG_HT_RX_AMPDU_FACTOR_MIN CFG_HT_RX_AMPDU_FACTOR_8K + +/* + * Maximal MPDU density for TX aggregation + * 4 - 2us density + * 5 - 4us density + * 6 - 8us density + * 7 - 16us density + */ +#define CFG_HT_MPDU_DENSITY_2USEC (0x4) +#define CFG_HT_MPDU_DENSITY_4USEC (0x5) +#define CFG_HT_MPDU_DENSITY_8USEC (0x6) +#define CFG_HT_MPDU_DENSITY_16USEC (0x7) +#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC +#define CFG_HT_MPDU_DENSITY_MAX CFG_HT_MPDU_DENSITY_16USEC +#define CFG_HT_MPDU_DENSITY_MIN (0x1) + +struct il_ht_config { + bool single_chain_sufficient; + enum ieee80211_smps_mode smps; /* current smps mode */ +}; + +/* QoS structures */ +struct il_qos_info { + int qos_active; + struct il_qosparam_cmd def_qos_parm; +}; + +/* + * Structure should be accessed with sta_lock held. When station addition + * is in progress (IL_STA_UCODE_INPROGRESS) it is possible to access only + * the commands (il_addsta_cmd and il_link_quality_cmd) without + * sta_lock held. + */ +struct il_station_entry { + struct il_addsta_cmd sta; + struct il_tid_data tid[MAX_TID_COUNT]; + u8 used; + struct il_hw_key keyinfo; + struct il_link_quality_cmd *lq; +}; + +struct il_station_priv_common { + u8 sta_id; +}; + +/** + * struct il_vif_priv - driver's ilate per-interface information + * + * When mac80211 allocates a virtual interface, it can allocate + * space for us to put data into. + */ +struct il_vif_priv { + u8 ibss_bssid_sta_id; +}; + +/* one for each uCode image (inst/data, boot/init/runtime) */ +struct fw_desc { + void *v_addr; /* access by driver */ + dma_addr_t p_addr; /* access by card's busmaster DMA */ + u32 len; /* bytes */ +}; + +/* uCode file layout */ +struct il_ucode_header { + __le32 ver; /* major/minor/API/serial */ + struct { + __le32 inst_size; /* bytes of runtime code */ + __le32 data_size; /* bytes of runtime data */ + __le32 init_size; /* bytes of init code */ + __le32 init_data_size; /* bytes of init data */ + __le32 boot_size; /* bytes of bootstrap code */ + u8 data[0]; /* in same order as sizes */ + } v1; +}; + +struct il4965_ibss_seq { + u8 mac[ETH_ALEN]; + u16 seq_num; + u16 frag_num; + unsigned long packet_time; + struct list_head list; +}; + +struct il_sensitivity_ranges { + u16 min_nrg_cck; + u16 max_nrg_cck; + + u16 nrg_th_cck; + u16 nrg_th_ofdm; + + u16 auto_corr_min_ofdm; + u16 auto_corr_min_ofdm_mrc; + u16 auto_corr_min_ofdm_x1; + u16 auto_corr_min_ofdm_mrc_x1; + + u16 auto_corr_max_ofdm; + u16 auto_corr_max_ofdm_mrc; + u16 auto_corr_max_ofdm_x1; + u16 auto_corr_max_ofdm_mrc_x1; + + u16 auto_corr_max_cck; + u16 auto_corr_max_cck_mrc; + u16 auto_corr_min_cck; + u16 auto_corr_min_cck_mrc; + + u16 barker_corr_th_min; + u16 barker_corr_th_min_mrc; + u16 nrg_th_cca; +}; + +#define KELVIN_TO_CELSIUS(x) ((x)-273) +#define CELSIUS_TO_KELVIN(x) ((x)+273) + +/** + * struct il_hw_params + * @bcast_id: f/w broadcast station ID + * @max_txq_num: Max # Tx queues supported + * @dma_chnl_num: Number of Tx DMA/FIFO channels + * @scd_bc_tbls_size: size of scheduler byte count tables + * @tfd_size: TFD size + * @tx/rx_chains_num: Number of TX/RX chains + * @valid_tx/rx_ant: usable antennas + * @max_rxq_size: Max # Rx frames in Rx queue (must be power-of-2) + * @max_rxq_log: Log-base-2 of max_rxq_size + * @rx_page_order: Rx buffer page order + * @rx_wrt_ptr_reg: FH{39}_RSCSR_CHNL0_WPTR + * @max_stations: + * @ht40_channel: is 40MHz width possible in band 2.4 + * BIT(IEEE80211_BAND_5GHZ) BIT(IEEE80211_BAND_5GHZ) + * @sw_crypto: 0 for hw, 1 for sw + * @max_xxx_size: for ucode uses + * @ct_kill_threshold: temperature threshold + * @beacon_time_tsf_bits: number of valid tsf bits for beacon time + * @struct il_sensitivity_ranges: range of sensitivity values + */ +struct il_hw_params { + u8 bcast_id; + u8 max_txq_num; + u8 dma_chnl_num; + u16 scd_bc_tbls_size; + u32 tfd_size; + u8 tx_chains_num; + u8 rx_chains_num; + u8 valid_tx_ant; + u8 valid_rx_ant; + u16 max_rxq_size; + u16 max_rxq_log; + u32 rx_page_order; + u32 rx_wrt_ptr_reg; + u8 max_stations; + u8 ht40_channel; + u8 max_beacon_itrvl; /* in 1024 ms */ + u32 max_inst_size; + u32 max_data_size; + u32 max_bsm_size; + u32 ct_kill_threshold; /* value in hw-dependent units */ + u16 beacon_time_tsf_bits; + const struct il_sensitivity_ranges *sens; +}; + +/****************************************************************************** + * + * Functions implemented in core module which are forward declared here + * for use by iwl-[4-5].c + * + * NOTE: The implementation of these functions are not hardware specific + * which is why they are in the core module files. + * + * Naming convention -- + * il_ <-- Is part of iwlwifi + * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) + * il4965_bg_ <-- Called from work queue context + * il4965_mac_ <-- mac80211 callback + * + ****************************************************************************/ +void il4965_update_chain_flags(struct il_priv *il); +extern const u8 il_bcast_addr[ETH_ALEN]; +int il_queue_space(const struct il_queue *q); +static inline int +il_queue_used(const struct il_queue *q, int i) +{ + return q->write_ptr >= q->read_ptr ? (i >= q->read_ptr && + i < q->write_ptr) : !(i < + q->read_ptr + && i >= + q-> + write_ptr); +} + +static inline u8 +il_get_cmd_idx(struct il_queue *q, u32 idx, int is_huge) +{ + /* + * This is for init calibration result and scan command which + * required buffer > TFD_MAX_PAYLOAD_SIZE, + * the big buffer at end of command array + */ + if (is_huge) + return q->n_win; /* must be power of 2 */ + + /* Otherwise, use normal size buffers */ + return idx & (q->n_win - 1); +} + +struct il_dma_ptr { + dma_addr_t dma; + void *addr; + size_t size; +}; + +#define IL_OPERATION_MODE_AUTO 0 +#define IL_OPERATION_MODE_HT_ONLY 1 +#define IL_OPERATION_MODE_MIXED 2 +#define IL_OPERATION_MODE_20MHZ 3 + +#define IL_TX_CRC_SIZE 4 +#define IL_TX_DELIMITER_SIZE 4 + +#define TX_POWER_IL_ILLEGAL_VOLTAGE -10000 + +/* Sensitivity and chain noise calibration */ +#define INITIALIZATION_VALUE 0xFFFF +#define IL4965_CAL_NUM_BEACONS 20 +#define IL_CAL_NUM_BEACONS 16 +#define MAXIMUM_ALLOWED_PATHLOSS 15 + +#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3 + +#define MAX_FA_OFDM 50 +#define MIN_FA_OFDM 5 +#define MAX_FA_CCK 50 +#define MIN_FA_CCK 5 + +#define AUTO_CORR_STEP_OFDM 1 + +#define AUTO_CORR_STEP_CCK 3 +#define AUTO_CORR_MAX_TH_CCK 160 + +#define NRG_DIFF 2 +#define NRG_STEP_CCK 2 +#define NRG_MARGIN 8 +#define MAX_NUMBER_CCK_NO_FA 100 + +#define AUTO_CORR_CCK_MIN_VAL_DEF (125) + +#define CHAIN_A 0 +#define CHAIN_B 1 +#define CHAIN_C 2 +#define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4 +#define ALL_BAND_FILTER 0xFF00 +#define IN_BAND_FILTER 0xFF +#define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF + +#define NRG_NUM_PREV_STAT_L 20 +#define NUM_RX_CHAINS 3 + +enum il4965_false_alarm_state { + IL_FA_TOO_MANY = 0, + IL_FA_TOO_FEW = 1, + IL_FA_GOOD_RANGE = 2, +}; + +enum il4965_chain_noise_state { + IL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */ + IL_CHAIN_NOISE_ACCUMULATE, + IL_CHAIN_NOISE_CALIBRATED, + IL_CHAIN_NOISE_DONE, +}; + +enum ucode_type { + UCODE_NONE = 0, + UCODE_INIT, + UCODE_RT +}; + +/* Sensitivity calib data */ +struct il_sensitivity_data { + u32 auto_corr_ofdm; + u32 auto_corr_ofdm_mrc; + u32 auto_corr_ofdm_x1; + u32 auto_corr_ofdm_mrc_x1; + u32 auto_corr_cck; + u32 auto_corr_cck_mrc; + + u32 last_bad_plcp_cnt_ofdm; + u32 last_fa_cnt_ofdm; + u32 last_bad_plcp_cnt_cck; + u32 last_fa_cnt_cck; + + u32 nrg_curr_state; + u32 nrg_prev_state; + u32 nrg_value[10]; + u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L]; + u32 nrg_silence_ref; + u32 nrg_energy_idx; + u32 nrg_silence_idx; + u32 nrg_th_cck; + s32 nrg_auto_corr_silence_diff; + u32 num_in_cck_no_fa; + u32 nrg_th_ofdm; + + u16 barker_corr_th_min; + u16 barker_corr_th_min_mrc; + u16 nrg_th_cca; +}; + +/* Chain noise (differential Rx gain) calib data */ +struct il_chain_noise_data { + u32 active_chains; + u32 chain_noise_a; + u32 chain_noise_b; + u32 chain_noise_c; + u32 chain_signal_a; + u32 chain_signal_b; + u32 chain_signal_c; + u16 beacon_count; + u8 disconn_array[NUM_RX_CHAINS]; + u8 delta_gain_code[NUM_RX_CHAINS]; + u8 radio_write; + u8 state; +}; + +#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ +#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ + +#define IL_TRAFFIC_ENTRIES (256) +#define IL_TRAFFIC_ENTRY_SIZE (64) + +enum { + MEASUREMENT_READY = (1 << 0), + MEASUREMENT_ACTIVE = (1 << 1), +}; + +/* interrupt stats */ +struct isr_stats { + u32 hw; + u32 sw; + u32 err_code; + u32 sch; + u32 alive; + u32 rfkill; + u32 ctkill; + u32 wakeup; + u32 rx; + u32 handlers[IL_CN_MAX]; + u32 tx; + u32 unhandled; +}; + +/* management stats */ +enum il_mgmt_stats { + MANAGEMENT_ASSOC_REQ = 0, + MANAGEMENT_ASSOC_RESP, + MANAGEMENT_REASSOC_REQ, + MANAGEMENT_REASSOC_RESP, + MANAGEMENT_PROBE_REQ, + MANAGEMENT_PROBE_RESP, + MANAGEMENT_BEACON, + MANAGEMENT_ATIM, + MANAGEMENT_DISASSOC, + MANAGEMENT_AUTH, + MANAGEMENT_DEAUTH, + MANAGEMENT_ACTION, + MANAGEMENT_MAX, +}; +/* control stats */ +enum il_ctrl_stats { + CONTROL_BACK_REQ = 0, + CONTROL_BACK, + CONTROL_PSPOLL, + CONTROL_RTS, + CONTROL_CTS, + CONTROL_ACK, + CONTROL_CFEND, + CONTROL_CFENDACK, + CONTROL_MAX, +}; + +struct traffic_stats { +#ifdef CONFIG_IWLEGACY_DEBUGFS + u32 mgmt[MANAGEMENT_MAX]; + u32 ctrl[CONTROL_MAX]; + u32 data_cnt; + u64 data_bytes; +#endif +}; + +/* + * host interrupt timeout value + * used with setting interrupt coalescing timer + * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit + * + * default interrupt coalescing timer is 64 x 32 = 2048 usecs + * default interrupt coalescing calibration timer is 16 x 32 = 512 usecs + */ +#define IL_HOST_INT_TIMEOUT_MAX (0xFF) +#define IL_HOST_INT_TIMEOUT_DEF (0x40) +#define IL_HOST_INT_TIMEOUT_MIN (0x0) +#define IL_HOST_INT_CALIB_TIMEOUT_MAX (0xFF) +#define IL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) +#define IL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) + +#define IL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5) + +/* TX queue watchdog timeouts in mSecs */ +#define IL_DEF_WD_TIMEOUT (2000) +#define IL_LONG_WD_TIMEOUT (10000) +#define IL_MAX_WD_TIMEOUT (120000) + +struct il_force_reset { + int reset_request_count; + int reset_success_count; + int reset_reject_count; + unsigned long reset_duration; + unsigned long last_force_reset_jiffies; +}; + +/* extend beacon time format bit shifting */ +/* + * for _3945 devices + * bits 31:24 - extended + * bits 23:0 - interval + */ +#define IL3945_EXT_BEACON_TIME_POS 24 +/* + * for _4965 devices + * bits 31:22 - extended + * bits 21:0 - interval + */ +#define IL4965_EXT_BEACON_TIME_POS 22 + +struct il_rxon_context { + struct ieee80211_vif *vif; +}; + +struct il_power_mgr { + struct il_powertable_cmd sleep_cmd; + struct il_powertable_cmd sleep_cmd_next; + int debug_sleep_level_override; + bool pci_pm; + bool ps_disabled; +}; + +struct il_priv { + struct ieee80211_hw *hw; + struct ieee80211_channel *ieee_channels; + struct ieee80211_rate *ieee_rates; + + struct il_cfg *cfg; + const struct il_ops *ops; +#ifdef CONFIG_IWLEGACY_DEBUGFS + const struct il_debugfs_ops *debugfs_ops; +#endif + + /* temporary frame storage list */ + struct list_head free_frames; + int frames_count; + + enum ieee80211_band band; + int alloc_rxb_page; + + void (*handlers[IL_CN_MAX]) (struct il_priv *il, + struct il_rx_buf *rxb); + + struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; + + /* spectrum measurement report caching */ + struct il_spectrum_notification measure_report; + u8 measurement_status; + + /* ucode beacon time */ + u32 ucode_beacon_time; + int missed_beacon_threshold; + + /* track IBSS manager (last beacon) status */ + u32 ibss_manager; + + /* force reset */ + struct il_force_reset force_reset; + + /* we allocate array of il_channel_info for NIC's valid channels. + * Access via channel # using indirect idx array */ + struct il_channel_info *channel_info; /* channel info array */ + u8 channel_count; /* # of channels */ + + /* thermal calibration */ + s32 temperature; /* degrees Kelvin */ + s32 last_temperature; + + /* Scan related variables */ + unsigned long scan_start; + unsigned long scan_start_tsf; + void *scan_cmd; + enum ieee80211_band scan_band; + struct cfg80211_scan_request *scan_request; + struct ieee80211_vif *scan_vif; + u8 scan_tx_ant[IEEE80211_NUM_BANDS]; + u8 mgmt_tx_ant; + + /* spinlock */ + spinlock_t lock; /* protect general shared data */ + spinlock_t hcmd_lock; /* protect hcmd */ + spinlock_t reg_lock; /* protect hw register access */ + struct mutex mutex; + + /* basic pci-network driver stuff */ + struct pci_dev *pci_dev; + + /* pci hardware address support */ + void __iomem *hw_base; + u32 hw_rev; + u32 hw_wa_rev; + u8 rev_id; + + /* command queue number */ + u8 cmd_queue; + + /* max number of station keys */ + u8 sta_key_max_num; + + /* EEPROM MAC addresses */ + struct mac_address addresses[1]; + + /* uCode images, save to reload in case of failure */ + int fw_idx; /* firmware we're trying to load */ + u32 ucode_ver; /* version of ucode, copy of + il_ucode.ver */ + struct fw_desc ucode_code; /* runtime inst */ + struct fw_desc ucode_data; /* runtime data original */ + struct fw_desc ucode_data_backup; /* runtime data save/restore */ + struct fw_desc ucode_init; /* initialization inst */ + struct fw_desc ucode_init_data; /* initialization data */ + struct fw_desc ucode_boot; /* bootstrap inst */ + enum ucode_type ucode_type; + u8 ucode_write_complete; /* the image write is complete */ + char firmware_name[25]; + + struct ieee80211_vif *vif; + + struct il_qos_info qos_data; + + struct { + bool enabled; + bool is_40mhz; + bool non_gf_sta_present; + u8 protection; + u8 extension_chan_offset; + } ht; + + /* + * We declare this const so it can only be + * changed via explicit cast within the + * routines that actually update the physical + * hardware. + */ + const struct il_rxon_cmd active; + struct il_rxon_cmd staging; + + struct il_rxon_time_cmd timing; + + __le16 switch_channel; + + /* 1st responses from initialize and runtime uCode images. + * _4965's initialize alive response contains some calibration data. */ + struct il_init_alive_resp card_alive_init; + struct il_alive_resp card_alive; + + u16 active_rate; + + u8 start_calib; + struct il_sensitivity_data sensitivity_data; + struct il_chain_noise_data chain_noise_data; + __le16 sensitivity_tbl[HD_TBL_SIZE]; + + struct il_ht_config current_ht_config; + + /* Rate scaling data */ + u8 retry_rate; + + wait_queue_head_t wait_command_queue; + + int activity_timer_active; + + /* Rx and Tx DMA processing queues */ + struct il_rx_queue rxq; + struct il_tx_queue *txq; + unsigned long txq_ctx_active_msk; + struct il_dma_ptr kw; /* keep warm address */ + struct il_dma_ptr scd_bc_tbls; + + u32 scd_base_addr; /* scheduler sram base address */ + + unsigned long status; + + /* counts mgmt, ctl, and data packets */ + struct traffic_stats tx_stats; + struct traffic_stats rx_stats; + + /* counts interrupts */ + struct isr_stats isr_stats; + + struct il_power_mgr power_data; + + /* context information */ + u8 bssid[ETH_ALEN]; /* used only on 3945 but filled by core */ + + /* station table variables */ + + /* Note: if lock and sta_lock are needed, lock must be acquired first */ + spinlock_t sta_lock; + int num_stations; + struct il_station_entry stations[IL_STATION_COUNT]; + unsigned long ucode_key_table; + + /* queue refcounts */ +#define IL_MAX_HW_QUEUES 32 + unsigned long queue_stopped[BITS_TO_LONGS(IL_MAX_HW_QUEUES)]; +#define IL_STOP_REASON_PASSIVE 0 + unsigned long stop_reason; + /* for each AC */ + atomic_t queue_stop_count[4]; + + /* Indication if ieee80211_ops->open has been called */ + u8 is_open; + + u8 mac80211_registered; + + /* eeprom -- this is in the card's little endian byte order */ + u8 *eeprom; + struct il_eeprom_calib_info *calib_info; + + enum nl80211_iftype iw_mode; + + /* Last Rx'd beacon timestamp */ + u64 timestamp; + + union { +#if defined(CONFIG_IWL3945) || defined(CONFIG_IWL3945_MODULE) + struct { + void *shared_virt; + dma_addr_t shared_phys; + + struct delayed_work thermal_periodic; + struct delayed_work rfkill_poll; + + struct il3945_notif_stats stats; +#ifdef CONFIG_IWLEGACY_DEBUGFS + struct il3945_notif_stats accum_stats; + struct il3945_notif_stats delta_stats; + struct il3945_notif_stats max_delta; +#endif + + u32 sta_supp_rates; + int last_rx_rssi; /* From Rx packet stats */ + + /* Rx'd packet timing information */ + u32 last_beacon_time; + u64 last_tsf; + + /* + * each calibration channel group in the + * EEPROM has a derived clip setting for + * each rate. + */ + const struct il3945_clip_group clip_groups[5]; + + } _3945; +#endif +#if defined(CONFIG_IWL4965) || defined(CONFIG_IWL4965_MODULE) + struct { + struct il_rx_phy_res last_phy_res; + bool last_phy_res_valid; + u32 ampdu_ref; + + struct completion firmware_loading_complete; + + /* + * chain noise reset and gain commands are the + * two extra calibration commands follows the standard + * phy calibration commands + */ + u8 phy_calib_chain_noise_reset_cmd; + u8 phy_calib_chain_noise_gain_cmd; + + u8 key_mapping_keys; + struct il_wep_key wep_keys[WEP_KEYS_MAX]; + + struct il_notif_stats stats; +#ifdef CONFIG_IWLEGACY_DEBUGFS + struct il_notif_stats accum_stats; + struct il_notif_stats delta_stats; + struct il_notif_stats max_delta; +#endif + + } _4965; +#endif + }; + + struct il_hw_params hw_params; + + u32 inta_mask; + + struct workqueue_struct *workqueue; + + struct work_struct restart; + struct work_struct scan_completed; + struct work_struct rx_replenish; + struct work_struct abort_scan; + + bool beacon_enabled; + struct sk_buff *beacon_skb; + + struct work_struct tx_flush; + + struct tasklet_struct irq_tasklet; + + struct delayed_work init_alive_start; + struct delayed_work alive_start; + struct delayed_work scan_check; + + /* TX Power */ + s8 tx_power_user_lmt; + s8 tx_power_device_lmt; + s8 tx_power_next; + +#ifdef CONFIG_IWLEGACY_DEBUG + /* debugging info */ + u32 debug_level; /* per device debugging will override global + il_debug_level if set */ +#endif /* CONFIG_IWLEGACY_DEBUG */ +#ifdef CONFIG_IWLEGACY_DEBUGFS + /* debugfs */ + u16 tx_traffic_idx; + u16 rx_traffic_idx; + u8 *tx_traffic; + u8 *rx_traffic; + struct dentry *debugfs_dir; + u32 dbgfs_sram_offset, dbgfs_sram_len; + bool disable_ht40; +#endif /* CONFIG_IWLEGACY_DEBUGFS */ + + struct work_struct txpower_work; + bool disable_sens_cal; + bool disable_chain_noise_cal; + bool disable_tx_power_cal; + struct work_struct run_time_calib_work; + struct timer_list stats_periodic; + struct timer_list watchdog; + bool hw_ready; + + struct led_classdev led; + unsigned long blink_on, blink_off; + bool led_registered; +}; /*il_priv */ + +static inline void +il_txq_ctx_activate(struct il_priv *il, int txq_id) +{ + set_bit(txq_id, &il->txq_ctx_active_msk); +} + +static inline void +il_txq_ctx_deactivate(struct il_priv *il, int txq_id) +{ + clear_bit(txq_id, &il->txq_ctx_active_msk); +} + +static inline int +il_is_associated(struct il_priv *il) +{ + return (il->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; +} + +static inline int +il_is_any_associated(struct il_priv *il) +{ + return il_is_associated(il); +} + +static inline int +il_is_channel_valid(const struct il_channel_info *ch_info) +{ + if (ch_info == NULL) + return 0; + return (ch_info->flags & EEPROM_CHANNEL_VALID) ? 1 : 0; +} + +static inline int +il_is_channel_radar(const struct il_channel_info *ch_info) +{ + return (ch_info->flags & EEPROM_CHANNEL_RADAR) ? 1 : 0; +} + +static inline u8 +il_is_channel_a_band(const struct il_channel_info *ch_info) +{ + return ch_info->band == IEEE80211_BAND_5GHZ; +} + +static inline int +il_is_channel_passive(const struct il_channel_info *ch) +{ + return (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) ? 1 : 0; +} + +static inline int +il_is_channel_ibss(const struct il_channel_info *ch) +{ + return (ch->flags & EEPROM_CHANNEL_IBSS) ? 1 : 0; +} + +static inline void +__il_free_pages(struct il_priv *il, struct page *page) +{ + __free_pages(page, il->hw_params.rx_page_order); + il->alloc_rxb_page--; +} + +static inline void +il_free_pages(struct il_priv *il, unsigned long page) +{ + free_pages(page, il->hw_params.rx_page_order); + il->alloc_rxb_page--; +} + +#define IWLWIFI_VERSION "in-tree:" +#define DRV_COPYRIGHT "Copyright(c) 2003-2011 Intel Corporation" +#define DRV_AUTHOR "" + +#define IL_PCI_DEVICE(dev, subdev, cfg) \ + .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = (subdev), \ + .driver_data = (kernel_ulong_t)&(cfg) + +#define TIME_UNIT 1024 + +#define IL_SKU_G 0x1 +#define IL_SKU_A 0x2 +#define IL_SKU_N 0x8 + +#define IL_CMD(x) case x: return #x + +/* Size of one Rx buffer in host DRAM */ +#define IL_RX_BUF_SIZE_3K (3 * 1000) /* 3945 only */ +#define IL_RX_BUF_SIZE_4K (4 * 1024) +#define IL_RX_BUF_SIZE_8K (8 * 1024) + +#ifdef CONFIG_IWLEGACY_DEBUGFS +struct il_debugfs_ops { + ssize_t(*rx_stats_read) (struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); + ssize_t(*tx_stats_read) (struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); + ssize_t(*general_stats_read) (struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos); +}; +#endif + +struct il_ops { + /* Handling TX */ + void (*txq_update_byte_cnt_tbl) (struct il_priv *il, + struct il_tx_queue *txq, + u16 byte_cnt); + int (*txq_attach_buf_to_tfd) (struct il_priv *il, + struct il_tx_queue *txq, dma_addr_t addr, + u16 len, u8 reset, u8 pad); + void (*txq_free_tfd) (struct il_priv *il, struct il_tx_queue *txq); + int (*txq_init) (struct il_priv *il, struct il_tx_queue *txq); + /* alive notification after init uCode load */ + void (*init_alive_start) (struct il_priv *il); + /* check validity of rtc data address */ + int (*is_valid_rtc_data_addr) (u32 addr); + /* 1st ucode load */ + int (*load_ucode) (struct il_priv *il); + + void (*dump_nic_error_log) (struct il_priv *il); + int (*dump_fh) (struct il_priv *il, char **buf, bool display); + int (*set_channel_switch) (struct il_priv *il, + struct ieee80211_channel_switch *ch_switch); + /* power management */ + int (*apm_init) (struct il_priv *il); + + /* tx power */ + int (*send_tx_power) (struct il_priv *il); + void (*update_chain_flags) (struct il_priv *il); + + /* eeprom operations */ + int (*eeprom_acquire_semaphore) (struct il_priv *il); + void (*eeprom_release_semaphore) (struct il_priv *il); + + int (*rxon_assoc) (struct il_priv *il); + int (*commit_rxon) (struct il_priv *il); + void (*set_rxon_chain) (struct il_priv *il); + + u16(*get_hcmd_size) (u8 cmd_id, u16 len); + u16(*build_addsta_hcmd) (const struct il_addsta_cmd *cmd, u8 *data); + + int (*request_scan) (struct il_priv *il, struct ieee80211_vif *vif); + void (*post_scan) (struct il_priv *il); + void (*post_associate) (struct il_priv *il); + void (*config_ap) (struct il_priv *il); + /* station management */ + int (*update_bcast_stations) (struct il_priv *il); + int (*manage_ibss_station) (struct il_priv *il, + struct ieee80211_vif *vif, bool add); + + int (*send_led_cmd) (struct il_priv *il, struct il_led_cmd *led_cmd); +}; + +struct il_mod_params { + int sw_crypto; /* def: 0 = using hardware encryption */ + int disable_hw_scan; /* def: 0 = use h/w scan */ + int num_of_queues; /* def: HW dependent */ + int disable_11n; /* def: 0 = 11n capabilities enabled */ + int amsdu_size_8K; /* def: 0 = disable 8K amsdu size */ + int antenna; /* def: 0 = both antennas (use diversity) */ + int restart_fw; /* def: 1 = restart firmware */ +}; + +#define IL_LED_SOLID 11 +#define IL_DEF_LED_INTRVL cpu_to_le32(1000) + +#define IL_LED_ACTIVITY (0<<1) +#define IL_LED_LINK (1<<1) + +/* + * LED mode + * IL_LED_DEFAULT: use device default + * IL_LED_RF_STATE: turn LED on/off based on RF state + * LED ON = RF ON + * LED OFF = RF OFF + * IL_LED_BLINK: adjust led blink rate based on blink table + */ +enum il_led_mode { + IL_LED_DEFAULT, + IL_LED_RF_STATE, + IL_LED_BLINK, +}; + +void il_leds_init(struct il_priv *il); +void il_leds_exit(struct il_priv *il); + +/** + * struct il_cfg + * @fw_name_pre: Firmware filename prefix. The api version and extension + * (.ucode) will be added to filename before loading from disk. The + * filename is constructed as fw_name_pre.ucode. + * @ucode_api_max: Highest version of uCode API supported by driver. + * @ucode_api_min: Lowest version of uCode API supported by driver. + * @scan_antennas: available antenna for scan operation + * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) + * + * We enable the driver to be backward compatible wrt API version. The + * driver specifies which APIs it supports (with @ucode_api_max being the + * highest and @ucode_api_min the lowest). Firmware will only be loaded if + * it has a supported API version. The firmware's API version will be + * stored in @il_priv, enabling the driver to make runtime changes based + * on firmware version used. + * + * For example, + * if (IL_UCODE_API(il->ucode_ver) >= 2) { + * Driver interacts with Firmware API version >= 2. + * } else { + * Driver interacts with Firmware API version 1. + * } + * + * The ideal usage of this infrastructure is to treat a new ucode API + * release as a new hardware revision. That is, through utilizing the + * il_hcmd_utils_ops etc. we accommodate different command structures + * and flows between hardware versions as well as their API + * versions. + * + */ +struct il_cfg { + /* params specific to an individual device within a device family */ + const char *name; + const char *fw_name_pre; + const unsigned int ucode_api_max; + const unsigned int ucode_api_min; + u8 valid_tx_ant; + u8 valid_rx_ant; + unsigned int sku; + u16 eeprom_ver; + u16 eeprom_calib_ver; + /* module based parameters which can be set from modprobe cmd */ + const struct il_mod_params *mod_params; + /* params not likely to change within a device family */ + struct il_base_params *base_params; + /* params likely to change within a device family */ + u8 scan_rx_antennas[IEEE80211_NUM_BANDS]; + enum il_led_mode led_mode; + + int eeprom_size; + int num_of_queues; /* def: HW dependent */ + int num_of_ampdu_queues; /* def: HW dependent */ + /* for il_apm_init() */ + u32 pll_cfg_val; + bool set_l0s; + bool use_bsm; + + u16 led_compensation; + int chain_noise_num_beacons; + unsigned int wd_timeout; + bool temperature_kelvin; + const bool ucode_tracing; + const bool sensitivity_calib_by_driver; + const bool chain_noise_calib_by_driver; + + const u32 regulatory_bands[7]; +}; + +/*************************** + * L i b * + ***************************/ + +int il_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params); +int il_mac_tx_last_beacon(struct ieee80211_hw *hw); + +void il_set_rxon_hwcrypto(struct il_priv *il, int hw_decrypt); +int il_check_rxon_cmd(struct il_priv *il); +int il_full_rxon_required(struct il_priv *il); +int il_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch); +void il_set_flags_for_band(struct il_priv *il, enum ieee80211_band band, + struct ieee80211_vif *vif); +u8 il_get_single_channel_number(struct il_priv *il, enum ieee80211_band band); +void il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf); +bool il_is_ht40_tx_allowed(struct il_priv *il, + struct ieee80211_sta_ht_cap *ht_cap); +void il_connection_init_rx_config(struct il_priv *il); +void il_set_rate(struct il_priv *il); +int il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr, + u32 decrypt_res, struct ieee80211_rx_status *stats); +void il_irq_handle_error(struct il_priv *il); +int il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void il_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum nl80211_iftype newtype, bool newp2p); +void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); +int il_alloc_txq_mem(struct il_priv *il); +void il_free_txq_mem(struct il_priv *il); + +#ifdef CONFIG_IWLEGACY_DEBUGFS +void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len); +#else +static inline void +il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len) +{ +} +#endif + +/***************************************************** + * Handlers + ***************************************************/ +void il_hdl_pm_sleep(struct il_priv *il, struct il_rx_buf *rxb); +void il_hdl_pm_debug_stats(struct il_priv *il, struct il_rx_buf *rxb); +void il_hdl_error(struct il_priv *il, struct il_rx_buf *rxb); +void il_hdl_csa(struct il_priv *il, struct il_rx_buf *rxb); + +/***************************************************** +* RX +******************************************************/ +void il_cmd_queue_unmap(struct il_priv *il); +void il_cmd_queue_free(struct il_priv *il); +int il_rx_queue_alloc(struct il_priv *il); +void il_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q); +int il_rx_queue_space(const struct il_rx_queue *q); +void il_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb); + +void il_hdl_spectrum_measurement(struct il_priv *il, struct il_rx_buf *rxb); +void il_recover_from_stats(struct il_priv *il, struct il_rx_pkt *pkt); +void il_chswitch_done(struct il_priv *il, bool is_success); + +/***************************************************** +* TX +******************************************************/ +void il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq); +int il_tx_queue_init(struct il_priv *il, u32 txq_id); +void il_tx_queue_reset(struct il_priv *il, u32 txq_id); +void il_tx_queue_unmap(struct il_priv *il, int txq_id); +void il_tx_queue_free(struct il_priv *il, int txq_id); +void il_setup_watchdog(struct il_priv *il); +/***************************************************** + * TX power + ****************************************************/ +int il_set_tx_power(struct il_priv *il, s8 tx_power, bool force); + +/******************************************************************************* + * Rate + ******************************************************************************/ + +u8 il_get_lowest_plcp(struct il_priv *il); + +/******************************************************************************* + * Scanning + ******************************************************************************/ +void il_init_scan_params(struct il_priv *il); +int il_scan_cancel(struct il_priv *il); +int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms); +void il_force_scan_end(struct il_priv *il); +int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req); +void il_internal_short_hw_scan(struct il_priv *il); +int il_force_reset(struct il_priv *il, bool external); +u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, + const u8 *ta, const u8 *ie, int ie_len, int left); +void il_setup_rx_scan_handlers(struct il_priv *il); +u16 il_get_active_dwell_time(struct il_priv *il, enum ieee80211_band band, + u8 n_probes); +u16 il_get_passive_dwell_time(struct il_priv *il, enum ieee80211_band band, + struct ieee80211_vif *vif); +void il_setup_scan_deferred_work(struct il_priv *il); +void il_cancel_scan_deferred_work(struct il_priv *il); + +/* For faster active scanning, scan will move to the next channel if fewer than + * PLCP_QUIET_THRESH packets are heard on this channel within + * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell + * time if it's a quiet channel (nothing responded to our probe, and there's + * no other traffic). + * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */ +#define IL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */ +#define IL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */ + +#define IL_SCAN_CHECK_WATCHDOG (HZ * 7) + +/***************************************************** + * S e n d i n g H o s t C o m m a n d s * + *****************************************************/ + +const char *il_get_cmd_string(u8 cmd); +int __must_check il_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd); +int il_send_cmd(struct il_priv *il, struct il_host_cmd *cmd); +int __must_check il_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, + const void *data); +int il_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data, + void (*callback) (struct il_priv *il, + struct il_device_cmd *cmd, + struct il_rx_pkt *pkt)); + +int il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd); + +/***************************************************** + * PCI * + *****************************************************/ + +void il_bg_watchdog(unsigned long data); +u32 il_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval); +__le32 il_add_beacon_time(struct il_priv *il, u32 base, u32 addon, + u32 beacon_interval); + +#ifdef CONFIG_PM_SLEEP +extern const struct dev_pm_ops il_pm_ops; + +#define IL_LEGACY_PM_OPS (&il_pm_ops) + +#else /* !CONFIG_PM_SLEEP */ + +#define IL_LEGACY_PM_OPS NULL + +#endif /* !CONFIG_PM_SLEEP */ + +/***************************************************** +* Error Handling Debugging +******************************************************/ +void il4965_dump_nic_error_log(struct il_priv *il); +#ifdef CONFIG_IWLEGACY_DEBUG +void il_print_rx_config_cmd(struct il_priv *il); +#else +static inline void +il_print_rx_config_cmd(struct il_priv *il) +{ +} +#endif + +void il_clear_isr_stats(struct il_priv *il); + +/***************************************************** +* GEOS +******************************************************/ +int il_init_geos(struct il_priv *il); +void il_free_geos(struct il_priv *il); + +/*************** DRIVER STATUS FUNCTIONS *****/ + +#define S_HCMD_ACTIVE 0 /* host command in progress */ +/* 1 is unused (used to be S_HCMD_SYNC_ACTIVE) */ +#define S_INT_ENABLED 2 +#define S_RFKILL 3 +#define S_CT_KILL 4 +#define S_INIT 5 +#define S_ALIVE 6 +#define S_READY 7 +#define S_TEMPERATURE 8 +#define S_GEO_CONFIGURED 9 +#define S_EXIT_PENDING 10 +#define S_STATS 12 +#define S_SCANNING 13 +#define S_SCAN_ABORTING 14 +#define S_SCAN_HW 15 +#define S_POWER_PMI 16 +#define S_FW_ERROR 17 +#define S_CHANNEL_SWITCH_PENDING 18 + +static inline int +il_is_ready(struct il_priv *il) +{ + /* The adapter is 'ready' if READY and GEO_CONFIGURED bits are + * set but EXIT_PENDING is not */ + return test_bit(S_READY, &il->status) && + test_bit(S_GEO_CONFIGURED, &il->status) && + !test_bit(S_EXIT_PENDING, &il->status); +} + +static inline int +il_is_alive(struct il_priv *il) +{ + return test_bit(S_ALIVE, &il->status); +} + +static inline int +il_is_init(struct il_priv *il) +{ + return test_bit(S_INIT, &il->status); +} + +static inline int +il_is_rfkill(struct il_priv *il) +{ + return test_bit(S_RFKILL, &il->status); +} + +static inline int +il_is_ctkill(struct il_priv *il) +{ + return test_bit(S_CT_KILL, &il->status); +} + +static inline int +il_is_ready_rf(struct il_priv *il) +{ + + if (il_is_rfkill(il)) + return 0; + + return il_is_ready(il); +} + +void il_send_bt_config(struct il_priv *il); +int il_send_stats_request(struct il_priv *il, u8 flags, bool clear); +void il_apm_stop(struct il_priv *il); +void _il_apm_stop(struct il_priv *il); + +int il_apm_init(struct il_priv *il); + +int il_send_rxon_timing(struct il_priv *il); + +static inline int +il_send_rxon_assoc(struct il_priv *il) +{ + return il->ops->rxon_assoc(il); +} + +static inline int +il_commit_rxon(struct il_priv *il) +{ + return il->ops->commit_rxon(il); +} + +static inline const struct ieee80211_supported_band * +il_get_hw_mode(struct il_priv *il, enum ieee80211_band band) +{ + return il->hw->wiphy->bands[band]; +} + +/* mac80211 handlers */ +int il_mac_config(struct ieee80211_hw *hw, u32 changed); +void il_mac_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, u32 changes); +void il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info, + __le16 fc, __le32 *tx_flags); + +irqreturn_t il_isr(int irq, void *data); + +void il_set_bit(struct il_priv *p, u32 r, u32 m); +void il_clear_bit(struct il_priv *p, u32 r, u32 m); +bool _il_grab_nic_access(struct il_priv *il); +int _il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout); +int il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout); +u32 il_rd_prph(struct il_priv *il, u32 reg); +void il_wr_prph(struct il_priv *il, u32 addr, u32 val); +u32 il_read_targ_mem(struct il_priv *il, u32 addr); +void il_write_targ_mem(struct il_priv *il, u32 addr, u32 val); + +static inline bool il_need_reclaim(struct il_priv *il, struct il_rx_pkt *pkt) +{ + /* Reclaim a command buffer only if this packet is a response + * to a (driver-originated) command. If the packet (e.g. Rx frame) + * originated from uCode, there is no command buffer to reclaim. + * Ucode should set SEQ_RX_FRAME bit if ucode-originated, but + * apparently a few don't get set; catch them here. + */ + return !(pkt->hdr.sequence & SEQ_RX_FRAME) && + pkt->hdr.cmd != N_STATS && pkt->hdr.cmd != C_TX && + pkt->hdr.cmd != N_RX_PHY && pkt->hdr.cmd != N_RX && + pkt->hdr.cmd != N_RX_MPDU && pkt->hdr.cmd != N_COMPRESSED_BA; +} + +static inline void +_il_write8(struct il_priv *il, u32 ofs, u8 val) +{ + writeb(val, il->hw_base + ofs); +} +#define il_write8(il, ofs, val) _il_write8(il, ofs, val) + +static inline void +_il_wr(struct il_priv *il, u32 ofs, u32 val) +{ + writel(val, il->hw_base + ofs); +} + +static inline u32 +_il_rd(struct il_priv *il, u32 ofs) +{ + return readl(il->hw_base + ofs); +} + +static inline void +_il_clear_bit(struct il_priv *il, u32 reg, u32 mask) +{ + _il_wr(il, reg, _il_rd(il, reg) & ~mask); +} + +static inline void +_il_set_bit(struct il_priv *il, u32 reg, u32 mask) +{ + _il_wr(il, reg, _il_rd(il, reg) | mask); +} + +static inline void +_il_release_nic_access(struct il_priv *il) +{ + _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + /* + * In above we are reading CSR_GP_CNTRL register, what will flush any + * previous writes, but still want write, which clear MAC_ACCESS_REQ + * bit, be performed on PCI bus before any other writes scheduled on + * different CPUs (after we drop reg_lock). + */ + mmiowb(); +} + +static inline u32 +il_rd(struct il_priv *il, u32 reg) +{ + u32 value; + unsigned long reg_flags; + + spin_lock_irqsave(&il->reg_lock, reg_flags); + _il_grab_nic_access(il); + value = _il_rd(il, reg); + _il_release_nic_access(il); + spin_unlock_irqrestore(&il->reg_lock, reg_flags); + return value; +} + +static inline void +il_wr(struct il_priv *il, u32 reg, u32 value) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&il->reg_lock, reg_flags); + if (likely(_il_grab_nic_access(il))) { + _il_wr(il, reg, value); + _il_release_nic_access(il); + } + spin_unlock_irqrestore(&il->reg_lock, reg_flags); +} + +static inline u32 +_il_rd_prph(struct il_priv *il, u32 reg) +{ + _il_wr(il, HBUS_TARG_PRPH_RADDR, reg | (3 << 24)); + return _il_rd(il, HBUS_TARG_PRPH_RDAT); +} + +static inline void +_il_wr_prph(struct il_priv *il, u32 addr, u32 val) +{ + _il_wr(il, HBUS_TARG_PRPH_WADDR, ((addr & 0x0000FFFF) | (3 << 24))); + _il_wr(il, HBUS_TARG_PRPH_WDAT, val); +} + +static inline void +il_set_bits_prph(struct il_priv *il, u32 reg, u32 mask) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&il->reg_lock, reg_flags); + if (likely(_il_grab_nic_access(il))) { + _il_wr_prph(il, reg, (_il_rd_prph(il, reg) | mask)); + _il_release_nic_access(il); + } + spin_unlock_irqrestore(&il->reg_lock, reg_flags); +} + +static inline void +il_set_bits_mask_prph(struct il_priv *il, u32 reg, u32 bits, u32 mask) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&il->reg_lock, reg_flags); + if (likely(_il_grab_nic_access(il))) { + _il_wr_prph(il, reg, ((_il_rd_prph(il, reg) & mask) | bits)); + _il_release_nic_access(il); + } + spin_unlock_irqrestore(&il->reg_lock, reg_flags); +} + +static inline void +il_clear_bits_prph(struct il_priv *il, u32 reg, u32 mask) +{ + unsigned long reg_flags; + u32 val; + + spin_lock_irqsave(&il->reg_lock, reg_flags); + if (likely(_il_grab_nic_access(il))) { + val = _il_rd_prph(il, reg); + _il_wr_prph(il, reg, (val & ~mask)); + _il_release_nic_access(il); + } + spin_unlock_irqrestore(&il->reg_lock, reg_flags); +} + +#define HW_KEY_DYNAMIC 0 +#define HW_KEY_DEFAULT 1 + +#define IL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ +#define IL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ +#define IL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of + being activated */ +#define IL_STA_LOCAL BIT(3) /* station state not directed by mac80211; + (this is for the IBSS BSSID stations) */ +#define IL_STA_BCAST BIT(4) /* this station is the special bcast station */ + +void il_restore_stations(struct il_priv *il); +void il_clear_ucode_stations(struct il_priv *il); +void il_dealloc_bcast_stations(struct il_priv *il); +int il_get_free_ucode_key_idx(struct il_priv *il); +int il_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags); +int il_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap, + struct ieee80211_sta *sta, u8 *sta_id_r); +int il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr); +int il_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + +u8 il_prep_station(struct il_priv *il, const u8 *addr, bool is_ap, + struct ieee80211_sta *sta); + +int il_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq, + u8 flags, bool init); + +/** + * il_clear_driver_stations - clear knowledge of all stations from driver + * @il: iwl il struct + * + * This is called during il_down() to make sure that in the case + * we're coming there from a hardware restart mac80211 will be + * able to reconfigure stations -- if we're getting there in the + * normal down flow then the stations will already be cleared. + */ +static inline void +il_clear_driver_stations(struct il_priv *il) +{ + unsigned long flags; + + spin_lock_irqsave(&il->sta_lock, flags); + memset(il->stations, 0, sizeof(il->stations)); + il->num_stations = 0; + il->ucode_key_table = 0; + spin_unlock_irqrestore(&il->sta_lock, flags); +} + +static inline int +il_sta_id(struct ieee80211_sta *sta) +{ + if (WARN_ON(!sta)) + return IL_INVALID_STATION; + + return ((struct il_station_priv_common *)sta->drv_priv)->sta_id; +} + +/** + * il_sta_id_or_broadcast - return sta_id or broadcast sta + * @il: iwl il + * @context: the current context + * @sta: mac80211 station + * + * In certain circumstances mac80211 passes a station pointer + * that may be %NULL, for example during TX or key setup. In + * that case, we need to use the broadcast station, so this + * inline wraps that pattern. + */ +static inline int +il_sta_id_or_broadcast(struct il_priv *il, struct ieee80211_sta *sta) +{ + int sta_id; + + if (!sta) + return il->hw_params.bcast_id; + + sta_id = il_sta_id(sta); + + /* + * mac80211 should not be passing a partially + * initialised station! + */ + WARN_ON(sta_id == IL_INVALID_STATION); + + return sta_id; +} + +/** + * il_queue_inc_wrap - increment queue idx, wrap back to beginning + * @idx -- current idx + * @n_bd -- total number of entries in queue (must be power of 2) + */ +static inline int +il_queue_inc_wrap(int idx, int n_bd) +{ + return ++idx & (n_bd - 1); +} + +/** + * il_queue_dec_wrap - decrement queue idx, wrap back to end + * @idx -- current idx + * @n_bd -- total number of entries in queue (must be power of 2) + */ +static inline int +il_queue_dec_wrap(int idx, int n_bd) +{ + return --idx & (n_bd - 1); +} + +/* TODO: Move fw_desc functions to iwl-pci.ko */ +static inline void +il_free_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc) +{ + if (desc->v_addr) + dma_free_coherent(&pci_dev->dev, desc->len, desc->v_addr, + desc->p_addr); + desc->v_addr = NULL; + desc->len = 0; +} + +static inline int +il_alloc_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc) +{ + if (!desc->len) { + desc->v_addr = NULL; + return -EINVAL; + } + + desc->v_addr = dma_alloc_coherent(&pci_dev->dev, desc->len, + &desc->p_addr, GFP_KERNEL); + return (desc->v_addr != NULL) ? 0 : -ENOMEM; +} + +/* + * we have 8 bits used like this: + * + * 7 6 5 4 3 2 1 0 + * | | | | | | | | + * | | | | | | +-+-------- AC queue (0-3) + * | | | | | | + * | +-+-+-+-+------------ HW queue ID + * | + * +---------------------- unused + */ +static inline void +il_set_swq_id(struct il_tx_queue *txq, u8 ac, u8 hwq) +{ + BUG_ON(ac > 3); /* only have 2 bits */ + BUG_ON(hwq > 31); /* only use 5 bits */ + + txq->swq_id = (hwq << 2) | ac; +} + +static inline void +_il_wake_queue(struct il_priv *il, u8 ac) +{ + if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0) + ieee80211_wake_queue(il->hw, ac); +} + +static inline void +_il_stop_queue(struct il_priv *il, u8 ac) +{ + if (atomic_inc_return(&il->queue_stop_count[ac]) > 0) + ieee80211_stop_queue(il->hw, ac); +} +static inline void +il_wake_queue(struct il_priv *il, struct il_tx_queue *txq) +{ + u8 queue = txq->swq_id; + u8 ac = queue & 3; + u8 hwq = (queue >> 2) & 0x1f; + + if (test_and_clear_bit(hwq, il->queue_stopped)) + _il_wake_queue(il, ac); +} + +static inline void +il_stop_queue(struct il_priv *il, struct il_tx_queue *txq) +{ + u8 queue = txq->swq_id; + u8 ac = queue & 3; + u8 hwq = (queue >> 2) & 0x1f; + + if (!test_and_set_bit(hwq, il->queue_stopped)) + _il_stop_queue(il, ac); +} + +static inline void +il_wake_queues_by_reason(struct il_priv *il, int reason) +{ + u8 ac; + + if (test_and_clear_bit(reason, &il->stop_reason)) + for (ac = 0; ac < 4; ac++) + _il_wake_queue(il, ac); +} + +static inline void +il_stop_queues_by_reason(struct il_priv *il, int reason) +{ + u8 ac; + + if (!test_and_set_bit(reason, &il->stop_reason)) + for (ac = 0; ac < 4; ac++) + _il_stop_queue(il, ac); +} + +#ifdef ieee80211_stop_queue +#undef ieee80211_stop_queue +#endif + +#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue + +#ifdef ieee80211_wake_queue +#undef ieee80211_wake_queue +#endif + +#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue + +static inline void +il_disable_interrupts(struct il_priv *il) +{ + clear_bit(S_INT_ENABLED, &il->status); + + /* disable interrupts from uCode/NIC to host */ + _il_wr(il, CSR_INT_MASK, 0x00000000); + + /* acknowledge/clear/reset any interrupts still pending + * from uCode or flow handler (Rx/Tx DMA) */ + _il_wr(il, CSR_INT, 0xffffffff); + _il_wr(il, CSR_FH_INT_STATUS, 0xffffffff); +} + +static inline void +il_enable_rfkill_int(struct il_priv *il) +{ + _il_wr(il, CSR_INT_MASK, CSR_INT_BIT_RF_KILL); +} + +static inline void +il_enable_interrupts(struct il_priv *il) +{ + set_bit(S_INT_ENABLED, &il->status); + _il_wr(il, CSR_INT_MASK, il->inta_mask); +} + +/** + * il_beacon_time_mask_low - mask of lower 32 bit of beacon time + * @il -- pointer to il_priv data structure + * @tsf_bits -- number of bits need to shift for masking) + */ +static inline u32 +il_beacon_time_mask_low(struct il_priv *il, u16 tsf_bits) +{ + return (1 << tsf_bits) - 1; +} + +/** + * il_beacon_time_mask_high - mask of higher 32 bit of beacon time + * @il -- pointer to il_priv data structure + * @tsf_bits -- number of bits need to shift for masking) + */ +static inline u32 +il_beacon_time_mask_high(struct il_priv *il, u16 tsf_bits) +{ + return ((1 << (32 - tsf_bits)) - 1) << tsf_bits; +} + +/** + * struct il_rb_status - reseve buffer status host memory mapped FH registers + * + * @closed_rb_num [0:11] - Indicates the idx of the RB which was closed + * @closed_fr_num [0:11] - Indicates the idx of the RX Frame which was closed + * @finished_rb_num [0:11] - Indicates the idx of the current RB + * in which the last frame was written to + * @finished_fr_num [0:11] - Indicates the idx of the RX Frame + * which was transferred + */ +struct il_rb_status { + __le16 closed_rb_num; + __le16 closed_fr_num; + __le16 finished_rb_num; + __le16 finished_fr_nam; + __le32 __unused; /* 3945 only */ +} __packed; + +#define TFD_QUEUE_SIZE_MAX 256 +#define TFD_QUEUE_SIZE_BC_DUP 64 +#define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP) +#define IL_TX_DMA_MASK DMA_BIT_MASK(36) +#define IL_NUM_OF_TBS 20 + +static inline u8 +il_get_dma_hi_addr(dma_addr_t addr) +{ + return (sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0) & 0xF; +} + +/** + * struct il_tfd_tb transmit buffer descriptor within transmit frame descriptor + * + * This structure contains dma address and length of transmission address + * + * @lo: low [31:0] portion of the dma address of TX buffer every even is + * unaligned on 16 bit boundary + * @hi_n_len: 0-3 [35:32] portion of dma + * 4-15 length of the tx buffer + */ +struct il_tfd_tb { + __le32 lo; + __le16 hi_n_len; +} __packed; + +/** + * struct il_tfd + * + * Transmit Frame Descriptor (TFD) + * + * @ __reserved1[3] reserved + * @ num_tbs 0-4 number of active tbs + * 5 reserved + * 6-7 padding (not used) + * @ tbs[20] transmit frame buffer descriptors + * @ __pad padding + * + * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM. + * Both driver and device share these circular buffers, each of which must be + * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes + * + * Driver must indicate the physical address of the base of each + * circular buffer via the FH49_MEM_CBBC_QUEUE registers. + * + * Each TFD contains pointer/size information for up to 20 data buffers + * in host DRAM. These buffers collectively contain the (one) frame described + * by the TFD. Each buffer must be a single contiguous block of memory within + * itself, but buffers may be scattered in host DRAM. Each buffer has max size + * of (4K - 4). The concatenates all of a TFD's buffers into a single + * Tx frame, up to 8 KBytes in size. + * + * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx. + */ +struct il_tfd { + u8 __reserved1[3]; + u8 num_tbs; + struct il_tfd_tb tbs[IL_NUM_OF_TBS]; + __le32 __pad; +} __packed; +/* PCI registers */ +#define PCI_CFG_RETRY_TIMEOUT 0x041 + +struct il_rate_info { + u8 plcp; /* uCode API: RATE_6M_PLCP, etc. */ + u8 plcp_siso; /* uCode API: RATE_SISO_6M_PLCP, etc. */ + u8 plcp_mimo2; /* uCode API: RATE_MIMO2_6M_PLCP, etc. */ + u8 ieee; /* MAC header: RATE_6M_IEEE, etc. */ + u8 prev_ieee; /* previous rate in IEEE speeds */ + u8 next_ieee; /* next rate in IEEE speeds */ + u8 prev_rs; /* previous rate used in rs algo */ + u8 next_rs; /* next rate used in rs algo */ + u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ + u8 next_rs_tgg; /* next rate used in TGG rs algo */ +}; + +struct il3945_rate_info { + u8 plcp; /* uCode API: RATE_6M_PLCP, etc. */ + u8 ieee; /* MAC header: RATE_6M_IEEE, etc. */ + u8 prev_ieee; /* previous rate in IEEE speeds */ + u8 next_ieee; /* next rate in IEEE speeds */ + u8 prev_rs; /* previous rate used in rs algo */ + u8 next_rs; /* next rate used in rs algo */ + u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ + u8 next_rs_tgg; /* next rate used in TGG rs algo */ + u8 table_rs_idx; /* idx in rate scale table cmd */ + u8 prev_table_rs; /* prev in rate table cmd */ +}; + +/* + * These serve as idxes into + * struct il_rate_info il_rates[RATE_COUNT]; + */ +enum { + RATE_1M_IDX = 0, + RATE_2M_IDX, + RATE_5M_IDX, + RATE_11M_IDX, + RATE_6M_IDX, + RATE_9M_IDX, + RATE_12M_IDX, + RATE_18M_IDX, + RATE_24M_IDX, + RATE_36M_IDX, + RATE_48M_IDX, + RATE_54M_IDX, + RATE_60M_IDX, + RATE_COUNT, + RATE_COUNT_LEGACY = RATE_COUNT - 1, /* Excluding 60M */ + RATE_COUNT_3945 = RATE_COUNT - 1, + RATE_INVM_IDX = RATE_COUNT, + RATE_INVALID = RATE_COUNT, +}; + +enum { + RATE_6M_IDX_TBL = 0, + RATE_9M_IDX_TBL, + RATE_12M_IDX_TBL, + RATE_18M_IDX_TBL, + RATE_24M_IDX_TBL, + RATE_36M_IDX_TBL, + RATE_48M_IDX_TBL, + RATE_54M_IDX_TBL, + RATE_1M_IDX_TBL, + RATE_2M_IDX_TBL, + RATE_5M_IDX_TBL, + RATE_11M_IDX_TBL, + RATE_INVM_IDX_TBL = RATE_INVM_IDX - 1, +}; + +enum { + IL_FIRST_OFDM_RATE = RATE_6M_IDX, + IL39_LAST_OFDM_RATE = RATE_54M_IDX, + IL_LAST_OFDM_RATE = RATE_60M_IDX, + IL_FIRST_CCK_RATE = RATE_1M_IDX, + IL_LAST_CCK_RATE = RATE_11M_IDX, +}; + +/* #define vs. enum to keep from defaulting to 'large integer' */ +#define RATE_6M_MASK (1 << RATE_6M_IDX) +#define RATE_9M_MASK (1 << RATE_9M_IDX) +#define RATE_12M_MASK (1 << RATE_12M_IDX) +#define RATE_18M_MASK (1 << RATE_18M_IDX) +#define RATE_24M_MASK (1 << RATE_24M_IDX) +#define RATE_36M_MASK (1 << RATE_36M_IDX) +#define RATE_48M_MASK (1 << RATE_48M_IDX) +#define RATE_54M_MASK (1 << RATE_54M_IDX) +#define RATE_60M_MASK (1 << RATE_60M_IDX) +#define RATE_1M_MASK (1 << RATE_1M_IDX) +#define RATE_2M_MASK (1 << RATE_2M_IDX) +#define RATE_5M_MASK (1 << RATE_5M_IDX) +#define RATE_11M_MASK (1 << RATE_11M_IDX) + +/* uCode API values for legacy bit rates, both OFDM and CCK */ +enum { + RATE_6M_PLCP = 13, + RATE_9M_PLCP = 15, + RATE_12M_PLCP = 5, + RATE_18M_PLCP = 7, + RATE_24M_PLCP = 9, + RATE_36M_PLCP = 11, + RATE_48M_PLCP = 1, + RATE_54M_PLCP = 3, + RATE_60M_PLCP = 3, /*FIXME:RS:should be removed */ + RATE_1M_PLCP = 10, + RATE_2M_PLCP = 20, + RATE_5M_PLCP = 55, + RATE_11M_PLCP = 110, + /*FIXME:RS:add RATE_LEGACY_INVM_PLCP = 0, */ +}; + +/* uCode API values for OFDM high-throughput (HT) bit rates */ +enum { + RATE_SISO_6M_PLCP = 0, + RATE_SISO_12M_PLCP = 1, + RATE_SISO_18M_PLCP = 2, + RATE_SISO_24M_PLCP = 3, + RATE_SISO_36M_PLCP = 4, + RATE_SISO_48M_PLCP = 5, + RATE_SISO_54M_PLCP = 6, + RATE_SISO_60M_PLCP = 7, + RATE_MIMO2_6M_PLCP = 0x8, + RATE_MIMO2_12M_PLCP = 0x9, + RATE_MIMO2_18M_PLCP = 0xa, + RATE_MIMO2_24M_PLCP = 0xb, + RATE_MIMO2_36M_PLCP = 0xc, + RATE_MIMO2_48M_PLCP = 0xd, + RATE_MIMO2_54M_PLCP = 0xe, + RATE_MIMO2_60M_PLCP = 0xf, + RATE_SISO_INVM_PLCP, + RATE_MIMO2_INVM_PLCP = RATE_SISO_INVM_PLCP, +}; + +/* MAC header values for bit rates */ +enum { + RATE_6M_IEEE = 12, + RATE_9M_IEEE = 18, + RATE_12M_IEEE = 24, + RATE_18M_IEEE = 36, + RATE_24M_IEEE = 48, + RATE_36M_IEEE = 72, + RATE_48M_IEEE = 96, + RATE_54M_IEEE = 108, + RATE_60M_IEEE = 120, + RATE_1M_IEEE = 2, + RATE_2M_IEEE = 4, + RATE_5M_IEEE = 11, + RATE_11M_IEEE = 22, +}; + +#define IL_CCK_BASIC_RATES_MASK \ + (RATE_1M_MASK | \ + RATE_2M_MASK) + +#define IL_CCK_RATES_MASK \ + (IL_CCK_BASIC_RATES_MASK | \ + RATE_5M_MASK | \ + RATE_11M_MASK) + +#define IL_OFDM_BASIC_RATES_MASK \ + (RATE_6M_MASK | \ + RATE_12M_MASK | \ + RATE_24M_MASK) + +#define IL_OFDM_RATES_MASK \ + (IL_OFDM_BASIC_RATES_MASK | \ + RATE_9M_MASK | \ + RATE_18M_MASK | \ + RATE_36M_MASK | \ + RATE_48M_MASK | \ + RATE_54M_MASK) + +#define IL_BASIC_RATES_MASK \ + (IL_OFDM_BASIC_RATES_MASK | \ + IL_CCK_BASIC_RATES_MASK) + +#define RATES_MASK ((1 << RATE_COUNT) - 1) +#define RATES_MASK_3945 ((1 << RATE_COUNT_3945) - 1) + +#define IL_INVALID_VALUE -1 + +#define IL_MIN_RSSI_VAL -100 +#define IL_MAX_RSSI_VAL 0 + +/* These values specify how many Tx frame attempts before + * searching for a new modulation mode */ +#define IL_LEGACY_FAILURE_LIMIT 160 +#define IL_LEGACY_SUCCESS_LIMIT 480 +#define IL_LEGACY_TBL_COUNT 160 + +#define IL_NONE_LEGACY_FAILURE_LIMIT 400 +#define IL_NONE_LEGACY_SUCCESS_LIMIT 4500 +#define IL_NONE_LEGACY_TBL_COUNT 1500 + +/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ +#define IL_RS_GOOD_RATIO 12800 /* 100% */ +#define RATE_SCALE_SWITCH 10880 /* 85% */ +#define RATE_HIGH_TH 10880 /* 85% */ +#define RATE_INCREASE_TH 6400 /* 50% */ +#define RATE_DECREASE_TH 1920 /* 15% */ + +/* possible actions when in legacy mode */ +#define IL_LEGACY_SWITCH_ANTENNA1 0 +#define IL_LEGACY_SWITCH_ANTENNA2 1 +#define IL_LEGACY_SWITCH_SISO 2 +#define IL_LEGACY_SWITCH_MIMO2_AB 3 +#define IL_LEGACY_SWITCH_MIMO2_AC 4 +#define IL_LEGACY_SWITCH_MIMO2_BC 5 + +/* possible actions when in siso mode */ +#define IL_SISO_SWITCH_ANTENNA1 0 +#define IL_SISO_SWITCH_ANTENNA2 1 +#define IL_SISO_SWITCH_MIMO2_AB 2 +#define IL_SISO_SWITCH_MIMO2_AC 3 +#define IL_SISO_SWITCH_MIMO2_BC 4 +#define IL_SISO_SWITCH_GI 5 + +/* possible actions when in mimo mode */ +#define IL_MIMO2_SWITCH_ANTENNA1 0 +#define IL_MIMO2_SWITCH_ANTENNA2 1 +#define IL_MIMO2_SWITCH_SISO_A 2 +#define IL_MIMO2_SWITCH_SISO_B 3 +#define IL_MIMO2_SWITCH_SISO_C 4 +#define IL_MIMO2_SWITCH_GI 5 + +#define IL_MAX_SEARCH IL_MIMO2_SWITCH_GI + +#define IL_ACTION_LIMIT 3 /* # possible actions */ + +#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ + +/* load per tid defines for A-MPDU activation */ +#define IL_AGG_TPT_THREHOLD 0 +#define IL_AGG_LOAD_THRESHOLD 10 +#define IL_AGG_ALL_TID 0xff +#define TID_QUEUE_CELL_SPACING 50 /*mS */ +#define TID_QUEUE_MAX_SIZE 20 +#define TID_ROUND_VALUE 5 /* mS */ +#define TID_MAX_LOAD_COUNT 8 + +#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) +#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) + +extern const struct il_rate_info il_rates[RATE_COUNT]; + +enum il_table_type { + LQ_NONE, + LQ_G, /* legacy types */ + LQ_A, + LQ_SISO, /* high-throughput types */ + LQ_MIMO2, + LQ_MAX, +}; + +#define is_legacy(tbl) ((tbl) == LQ_G || (tbl) == LQ_A) +#define is_siso(tbl) ((tbl) == LQ_SISO) +#define is_mimo2(tbl) ((tbl) == LQ_MIMO2) +#define is_mimo(tbl) (is_mimo2(tbl)) +#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) +#define is_a_band(tbl) ((tbl) == LQ_A) +#define is_g_and(tbl) ((tbl) == LQ_G) + +#define ANT_NONE 0x0 +#define ANT_A BIT(0) +#define ANT_B BIT(1) +#define ANT_AB (ANT_A | ANT_B) +#define ANT_C BIT(2) +#define ANT_AC (ANT_A | ANT_C) +#define ANT_BC (ANT_B | ANT_C) +#define ANT_ABC (ANT_AB | ANT_C) + +#define IL_MAX_MCS_DISPLAY_SIZE 12 + +struct il_rate_mcs_info { + char mbps[IL_MAX_MCS_DISPLAY_SIZE]; + char mcs[IL_MAX_MCS_DISPLAY_SIZE]; +}; + +/** + * struct il_rate_scale_data -- tx success history for one rate + */ +struct il_rate_scale_data { + u64 data; /* bitmap of successful frames */ + s32 success_counter; /* number of frames successful */ + s32 success_ratio; /* per-cent * 128 */ + s32 counter; /* number of frames attempted */ + s32 average_tpt; /* success ratio * expected throughput */ + unsigned long stamp; +}; + +/** + * struct il_scale_tbl_info -- tx params and success history for all rates + * + * There are two of these in struct il_lq_sta, + * one for "active", and one for "search". + */ +struct il_scale_tbl_info { + enum il_table_type lq_type; + u8 ant_type; + u8 is_SGI; /* 1 = short guard interval */ + u8 is_ht40; /* 1 = 40 MHz channel width */ + u8 is_dup; /* 1 = duplicated data streams */ + u8 action; /* change modulation; IL_[LEGACY/SISO/MIMO]_SWITCH_* */ + u8 max_search; /* maximun number of tables we can search */ + s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ + u32 current_rate; /* rate_n_flags, uCode API format */ + struct il_rate_scale_data win[RATE_COUNT]; /* rate histories */ +}; + +struct il_traffic_load { + unsigned long time_stamp; /* age of the oldest stats */ + u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time + * slice */ + u32 total; /* total num of packets during the + * last TID_MAX_TIME_DIFF */ + u8 queue_count; /* number of queues that has + * been used since the last cleanup */ + u8 head; /* start of the circular buffer */ +}; + +/** + * struct il_lq_sta -- driver's rate scaling ilate structure + * + * Pointer to this gets passed back and forth between driver and mac80211. + */ +struct il_lq_sta { + u8 active_tbl; /* idx of active table, range 0-1 */ + u8 enable_counter; /* indicates HT mode */ + u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ + u8 search_better_tbl; /* 1: currently trying alternate mode */ + s32 last_tpt; + + /* The following determine when to search for a new mode */ + u32 table_count_limit; + u32 max_failure_limit; /* # failed frames before new search */ + u32 max_success_limit; /* # successful frames before new search */ + u32 table_count; + u32 total_failed; /* total failed frames, any/all rates */ + u32 total_success; /* total successful frames, any/all rates */ + u64 flush_timer; /* time staying in mode before new search */ + + u8 action_counter; /* # mode-switch actions tried */ + u8 is_green; + u8 is_dup; + enum ieee80211_band band; + + /* The following are bitmaps of rates; RATE_6M_MASK, etc. */ + u32 supp_rates; + u16 active_legacy_rate; + u16 active_siso_rate; + u16 active_mimo2_rate; + s8 max_rate_idx; /* Max rate set by user */ + u8 missed_rate_counter; + + struct il_link_quality_cmd lq; + struct il_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ + struct il_traffic_load load[TID_MAX_LOAD_COUNT]; + u8 tx_agg_tid_en; +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *rs_sta_dbgfs_scale_table_file; + struct dentry *rs_sta_dbgfs_stats_table_file; + struct dentry *rs_sta_dbgfs_rate_scale_data_file; + struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; + u32 dbg_fixed_rate; +#endif + struct il_priv *drv; + + /* used to be in sta_info */ + int last_txrate_idx; + /* last tx rate_n_flags */ + u32 last_rate_n_flags; + /* packets destined for this STA are aggregated */ + u8 is_agg; +}; + +/* + * il_station_priv: Driver's ilate station information + * + * When mac80211 creates a station it reserves some space (hw->sta_data_size) + * in the structure for use by driver. This structure is places in that + * space. + * + * The common struct MUST be first because it is shared between + * 3945 and 4965! + */ +struct il_station_priv { + struct il_station_priv_common common; + struct il_lq_sta lq_sta; + atomic_t pending_frames; + bool client; + bool asleep; +}; + +static inline u8 +il4965_num_of_ant(u8 m) +{ + return !!(m & ANT_A) + !!(m & ANT_B) + !!(m & ANT_C); +} + +static inline u8 +il4965_first_antenna(u8 mask) +{ + if (mask & ANT_A) + return ANT_A; + if (mask & ANT_B) + return ANT_B; + return ANT_C; +} + +/** + * il3945_rate_scale_init - Initialize the rate scale table based on assoc info + * + * The specific throughput table used is based on the type of network + * the associated with, including A, B, G, and G w/ TGG protection + */ +void il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); + +/* Initialize station's rate scaling information after adding station */ +void il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, + u8 sta_id); +void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, + u8 sta_id); + +/** + * il_rate_control_register - Register the rate control algorithm callbacks + * + * Since the rate control algorithm is hardware specific, there is no need + * or reason to place it as a stand alone module. The driver can call + * il_rate_control_register in order to register the rate control callbacks + * with the mac80211 subsystem. This should be performed prior to calling + * ieee80211_register_hw + * + */ +int il4965_rate_control_register(void); +int il3945_rate_control_register(void); + +/** + * il_rate_control_unregister - Unregister the rate control callbacks + * + * This should be called after calling ieee80211_unregister_hw, but before + * the driver is unloaded. + */ +void il4965_rate_control_unregister(void); +void il3945_rate_control_unregister(void); + +int il_power_update_mode(struct il_priv *il, bool force); +void il_power_initialize(struct il_priv *il); + +extern u32 il_debug_level; + +#ifdef CONFIG_IWLEGACY_DEBUG +/* + * il_get_debug_level: Return active debug level for device + * + * Using sysfs it is possible to set per device debug level. This debug + * level will be used if set, otherwise the global debug level which can be + * set via module parameter is used. + */ +static inline u32 +il_get_debug_level(struct il_priv *il) +{ + if (il->debug_level) + return il->debug_level; + else + return il_debug_level; +} +#else +static inline u32 +il_get_debug_level(struct il_priv *il) +{ + return il_debug_level; +} +#endif + +#define il_print_hex_error(il, p, len) \ +do { \ + print_hex_dump(KERN_ERR, "iwl data: ", \ + DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ +} while (0) + +#ifdef CONFIG_IWLEGACY_DEBUG +#define IL_DBG(level, fmt, args...) \ +do { \ + if (il_get_debug_level(il) & level) \ + dev_err(&il->hw->wiphy->dev, "%c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __func__ , ##args); \ +} while (0) + +#define il_print_hex_dump(il, level, p, len) \ +do { \ + if (il_get_debug_level(il) & level) \ + print_hex_dump(KERN_DEBUG, "iwl data: ", \ + DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ +} while (0) + +#else +#define IL_DBG(level, fmt, args...) +static inline void +il_print_hex_dump(struct il_priv *il, int level, const void *p, u32 len) +{ +} +#endif /* CONFIG_IWLEGACY_DEBUG */ + +#ifdef CONFIG_IWLEGACY_DEBUGFS +int il_dbgfs_register(struct il_priv *il, const char *name); +void il_dbgfs_unregister(struct il_priv *il); +#else +static inline int +il_dbgfs_register(struct il_priv *il, const char *name) +{ + return 0; +} + +static inline void +il_dbgfs_unregister(struct il_priv *il) +{ +} +#endif /* CONFIG_IWLEGACY_DEBUGFS */ + +/* + * To use the debug system: + * + * If you are defining a new debug classification, simply add it to the #define + * list here in the form of + * + * #define IL_DL_xxxx VALUE + * + * where xxxx should be the name of the classification (for example, WEP). + * + * You then need to either add a IL_xxxx_DEBUG() macro definition for your + * classification, or use IL_DBG(IL_DL_xxxx, ...) whenever you want + * to send output to that classification. + * + * The active debug levels can be accessed via files + * + * /sys/module/iwl4965/parameters/debug + * /sys/module/iwl3945/parameters/debug + * /sys/class/net/wlan0/device/debug_level + * + * when CONFIG_IWLEGACY_DEBUG=y. + */ + +/* 0x0000000F - 0x00000001 */ +#define IL_DL_INFO (1 << 0) +#define IL_DL_MAC80211 (1 << 1) +#define IL_DL_HCMD (1 << 2) +#define IL_DL_STATE (1 << 3) +/* 0x000000F0 - 0x00000010 */ +#define IL_DL_MACDUMP (1 << 4) +#define IL_DL_HCMD_DUMP (1 << 5) +#define IL_DL_EEPROM (1 << 6) +#define IL_DL_RADIO (1 << 7) +/* 0x00000F00 - 0x00000100 */ +#define IL_DL_POWER (1 << 8) +#define IL_DL_TEMP (1 << 9) +#define IL_DL_NOTIF (1 << 10) +#define IL_DL_SCAN (1 << 11) +/* 0x0000F000 - 0x00001000 */ +#define IL_DL_ASSOC (1 << 12) +#define IL_DL_DROP (1 << 13) +#define IL_DL_TXPOWER (1 << 14) +#define IL_DL_AP (1 << 15) +/* 0x000F0000 - 0x00010000 */ +#define IL_DL_FW (1 << 16) +#define IL_DL_RF_KILL (1 << 17) +#define IL_DL_FW_ERRORS (1 << 18) +#define IL_DL_LED (1 << 19) +/* 0x00F00000 - 0x00100000 */ +#define IL_DL_RATE (1 << 20) +#define IL_DL_CALIB (1 << 21) +#define IL_DL_WEP (1 << 22) +#define IL_DL_TX (1 << 23) +/* 0x0F000000 - 0x01000000 */ +#define IL_DL_RX (1 << 24) +#define IL_DL_ISR (1 << 25) +#define IL_DL_HT (1 << 26) +/* 0xF0000000 - 0x10000000 */ +#define IL_DL_11H (1 << 28) +#define IL_DL_STATS (1 << 29) +#define IL_DL_TX_REPLY (1 << 30) +#define IL_DL_QOS (1 << 31) + +#define D_INFO(f, a...) IL_DBG(IL_DL_INFO, f, ## a) +#define D_MAC80211(f, a...) IL_DBG(IL_DL_MAC80211, f, ## a) +#define D_MACDUMP(f, a...) IL_DBG(IL_DL_MACDUMP, f, ## a) +#define D_TEMP(f, a...) IL_DBG(IL_DL_TEMP, f, ## a) +#define D_SCAN(f, a...) IL_DBG(IL_DL_SCAN, f, ## a) +#define D_RX(f, a...) IL_DBG(IL_DL_RX, f, ## a) +#define D_TX(f, a...) IL_DBG(IL_DL_TX, f, ## a) +#define D_ISR(f, a...) IL_DBG(IL_DL_ISR, f, ## a) +#define D_LED(f, a...) IL_DBG(IL_DL_LED, f, ## a) +#define D_WEP(f, a...) IL_DBG(IL_DL_WEP, f, ## a) +#define D_HC(f, a...) IL_DBG(IL_DL_HCMD, f, ## a) +#define D_HC_DUMP(f, a...) IL_DBG(IL_DL_HCMD_DUMP, f, ## a) +#define D_EEPROM(f, a...) IL_DBG(IL_DL_EEPROM, f, ## a) +#define D_CALIB(f, a...) IL_DBG(IL_DL_CALIB, f, ## a) +#define D_FW(f, a...) IL_DBG(IL_DL_FW, f, ## a) +#define D_RF_KILL(f, a...) IL_DBG(IL_DL_RF_KILL, f, ## a) +#define D_DROP(f, a...) IL_DBG(IL_DL_DROP, f, ## a) +#define D_AP(f, a...) IL_DBG(IL_DL_AP, f, ## a) +#define D_TXPOWER(f, a...) IL_DBG(IL_DL_TXPOWER, f, ## a) +#define D_RATE(f, a...) IL_DBG(IL_DL_RATE, f, ## a) +#define D_NOTIF(f, a...) IL_DBG(IL_DL_NOTIF, f, ## a) +#define D_ASSOC(f, a...) IL_DBG(IL_DL_ASSOC, f, ## a) +#define D_HT(f, a...) IL_DBG(IL_DL_HT, f, ## a) +#define D_STATS(f, a...) IL_DBG(IL_DL_STATS, f, ## a) +#define D_TX_REPLY(f, a...) IL_DBG(IL_DL_TX_REPLY, f, ## a) +#define D_QOS(f, a...) IL_DBG(IL_DL_QOS, f, ## a) +#define D_RADIO(f, a...) IL_DBG(IL_DL_RADIO, f, ## a) +#define D_POWER(f, a...) IL_DBG(IL_DL_POWER, f, ## a) +#define D_11H(f, a...) IL_DBG(IL_DL_11H, f, ## a) + +#endif /* __il_core_h__ */ diff --git a/drivers/net/wireless/intel/iwlegacy/csr.h b/drivers/net/wireless/intel/iwlegacy/csr.h new file mode 100644 index 000000000000..9138e15004fa --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/csr.h @@ -0,0 +1,419 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __il_csr_h__ +#define __il_csr_h__ +/* + * CSR (control and status registers) + * + * CSR registers are mapped directly into PCI bus space, and are accessible + * whenever platform supplies power to device, even when device is in + * low power states due to driver-invoked device resets + * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes. + * + * Use _il_wr() and _il_rd() family to access these registers; + * these provide simple PCI bus access, without waking up the MAC. + * Do not use il_wr() family for these registers; + * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ. + * The MAC (uCode processor, etc.) does not need to be powered up for accessing + * the CSR registers. + * + * NOTE: Device does need to be awake in order to read this memory + * via CSR_EEPROM register + */ +#define CSR_BASE (0x000) + +#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */ +#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */ +#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */ +#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */ +#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack */ +#define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */ +#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc */ +#define CSR_GP_CNTRL (CSR_BASE+0x024) + +/* 2nd byte of CSR_INT_COALESCING, not accessible via _il_wr()! */ +#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005) + +/* + * Hardware revision info + * Bit fields: + * 31-8: Reserved + * 7-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions + * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D + * 1-0: "Dash" (-) value, as in A-1, etc. + * + * NOTE: Revision step affects calculation of CCK txpower for 4965. + * NOTE: See also CSR_HW_REV_WA_REG (work-around for bug in 4965). + */ +#define CSR_HW_REV (CSR_BASE+0x028) + +/* + * EEPROM memory reads + * + * NOTE: Device must be awake, initialized via apm_ops.init(), + * in order to read. + */ +#define CSR_EEPROM_REG (CSR_BASE+0x02c) +#define CSR_EEPROM_GP (CSR_BASE+0x030) + +#define CSR_GIO_REG (CSR_BASE+0x03C) +#define CSR_GP_UCODE_REG (CSR_BASE+0x048) +#define CSR_GP_DRIVER_REG (CSR_BASE+0x050) + +/* + * UCODE-DRIVER GP (general purpose) mailbox registers. + * SET/CLR registers set/clear bit(s) if "1" is written. + */ +#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054) +#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058) +#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c) +#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060) + +#define CSR_LED_REG (CSR_BASE+0x094) +#define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0) + +/* GIO Chicken Bits (PCI Express bus link power management) */ +#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100) + +/* Analog phase-lock-loop configuration */ +#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c) + +/* + * CSR Hardware Revision Workaround Register. Indicates hardware rev; + * "step" determines CCK backoff for txpower calculation. Used for 4965 only. + * See also CSR_HW_REV register. + * Bit fields: + * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step + * 1-0: "Dash" (-) value, as in C-1, etc. + */ +#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C) + +#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240) +#define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250) + +/* Bits for CSR_HW_IF_CONFIG_REG */ +#define CSR49_HW_IF_CONFIG_REG_BIT_4965_R (0x00000010) +#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x00000C00) +#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100) +#define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200) + +#define CSR39_HW_IF_CONFIG_REG_BIT_3945_MB (0x00000100) +#define CSR39_HW_IF_CONFIG_REG_BIT_3945_MM (0x00000200) +#define CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC (0x00000400) +#define CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE (0x00000800) +#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A (0x00000000) +#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B (0x00001000) + +#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) +#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) +#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ +#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ +#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ + +#define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int */ +#define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec */ + +/* interrupt flags in INTA, set by uCode or hardware (e.g. dma), + * acknowledged (reset) by host writing "1" to flagged bits. */ +#define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */ +#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */ +#define CSR_INT_BIT_RX_PERIODIC (1 << 28) /* Rx periodic */ +#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */ +#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */ +#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */ +#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */ +#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */ +#define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses, 3945 */ +#define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */ +#define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */ + +#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ + CSR_INT_BIT_HW_ERR | \ + CSR_INT_BIT_FH_TX | \ + CSR_INT_BIT_SW_ERR | \ + CSR_INT_BIT_RF_KILL | \ + CSR_INT_BIT_SW_RX | \ + CSR_INT_BIT_WAKEUP | \ + CSR_INT_BIT_ALIVE) + +/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ +#define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */ +#define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */ +#define CSR39_FH_INT_BIT_RX_CHNL2 (1 << 18) /* Rx channel 2 (3945 only) */ +#define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */ +#define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */ +#define CSR39_FH_INT_BIT_TX_CHNL6 (1 << 6) /* Tx channel 6 (3945 only) */ +#define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */ +#define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */ + +#define CSR39_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ + CSR39_FH_INT_BIT_RX_CHNL2 | \ + CSR_FH_INT_BIT_RX_CHNL1 | \ + CSR_FH_INT_BIT_RX_CHNL0) + +#define CSR39_FH_INT_TX_MASK (CSR39_FH_INT_BIT_TX_CHNL6 | \ + CSR_FH_INT_BIT_TX_CHNL1 | \ + CSR_FH_INT_BIT_TX_CHNL0) + +#define CSR49_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ + CSR_FH_INT_BIT_RX_CHNL1 | \ + CSR_FH_INT_BIT_RX_CHNL0) + +#define CSR49_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \ + CSR_FH_INT_BIT_TX_CHNL0) + +/* GPIO */ +#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200) +#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000) +#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC (0x00000200) + +/* RESET */ +#define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001) +#define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002) +#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080) +#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100) +#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200) +#define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000) + +/* + * GP (general purpose) CONTROL REGISTER + * Bit fields: + * 27: HW_RF_KILL_SW + * Indicates state of (platform's) hardware RF-Kill switch + * 26-24: POWER_SAVE_TYPE + * Indicates current power-saving mode: + * 000 -- No power saving + * 001 -- MAC power-down + * 010 -- PHY (radio) power-down + * 011 -- Error + * 9-6: SYS_CONFIG + * Indicates current system configuration, reflecting pins on chip + * as forced high/low by device circuit board. + * 4: GOING_TO_SLEEP + * Indicates MAC is entering a power-saving sleep power-down. + * Not a good time to access device-internal resources. + * 3: MAC_ACCESS_REQ + * Host sets this to request and maintain MAC wakeup, to allow host + * access to device-internal resources. Host must wait for + * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR + * device registers. + * 2: INIT_DONE + * Host sets this to put device into fully operational D0 power mode. + * Host resets this after SW_RESET to put device into low power mode. + * 0: MAC_CLOCK_READY + * Indicates MAC (ucode processor, etc.) is powered up and can run. + * Internal resources are accessible. + * NOTE: This does not indicate that the processor is actually running. + * NOTE: This does not indicate that 4965 or 3945 has completed + * init or post-power-down restore of internal SRAM memory. + * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that + * SRAM is restored and uCode is in normal operation mode. + * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and + * do not need to save/restore it. + * NOTE: After device reset, this bit remains "0" until host sets + * INIT_DONE + */ +#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001) +#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004) +#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008) +#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010) + +#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001) + +#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000) +#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000) +#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000) + +/* EEPROM REG */ +#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) +#define CSR_EEPROM_REG_BIT_CMD (0x00000002) +#define CSR_EEPROM_REG_MSK_ADDR (0x0000FFFC) +#define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000) + +/* EEPROM GP */ +#define CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */ +#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) +#define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002) +#define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004) + +/* GP REG */ +#define CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */ +#define CSR_GP_REG_NO_POWER_SAVE (0x00000000) +#define CSR_GP_REG_MAC_POWER_SAVE (0x01000000) +#define CSR_GP_REG_PHY_POWER_SAVE (0x02000000) +#define CSR_GP_REG_POWER_SAVE_ERROR (0x03000000) + +/* CSR GIO */ +#define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002) + +/* + * UCODE-DRIVER GP (general purpose) mailbox register 1 + * Host driver and uCode write and/or read this register to communicate with + * each other. + * Bit fields: + * 4: UCODE_DISABLE + * Host sets this to request permanent halt of uCode, same as + * sending CARD_STATE command with "halt" bit set. + * 3: CT_KILL_EXIT + * Host sets this to request exit from CT_KILL state, i.e. host thinks + * device temperature is low enough to continue normal operation. + * 2: CMD_BLOCKED + * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL) + * to release uCode to clear all Tx and command queues, enter + * unassociated mode, and power down. + * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit. + * 1: SW_BIT_RFKILL + * Host sets this when issuing CARD_STATE command to request + * device sleep. + * 0: MAC_SLEEP + * uCode sets this when preparing a power-saving power-down. + * uCode resets this when power-up is complete and SRAM is sane. + * NOTE: 3945/4965 saves internal SRAM data to host when powering down, + * and must restore this data after powering back up. + * MAC_SLEEP is the best indication that restore is complete. + * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and + * do not need to save/restore it. + */ +#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001) +#define CSR_UCODE_SW_BIT_RFKILL (0x00000002) +#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) +#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008) + +/* GIO Chicken Bits (PCI Express bus link power management) */ +#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) +#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) + +/* LED */ +#define CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF) +#define CSR_LED_REG_TRUN_ON (0x78) +#define CSR_LED_REG_TRUN_OFF (0x38) + +/* ANA_PLL */ +#define CSR39_ANA_PLL_CFG_VAL (0x01000000) + +/* HPET MEM debug */ +#define CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000) + +/* DRAM INT TBL */ +#define CSR_DRAM_INT_TBL_ENABLE (1 << 31) +#define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) + +/* + * HBUS (Host-side Bus) + * + * HBUS registers are mapped directly into PCI bus space, but are used + * to indirectly access device's internal memory or registers that + * may be powered-down. + * + * Use il_wr()/il_rd() family + * for these registers; + * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ + * to make sure the MAC (uCode processor, etc.) is powered up for accessing + * internal resources. + * + * Do not use _il_wr()/_il_rd() family to access these registers; + * these provide only simple PCI bus access, without waking up the MAC. + */ +#define HBUS_BASE (0x400) + +/* + * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM + * structures, error log, event log, verifying uCode load). + * First write to address register, then read from or write to data register + * to complete the job. Once the address register is set up, accesses to + * data registers auto-increment the address by one dword. + * Bit usage for address registers (read or write): + * 0-31: memory address within device + */ +#define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c) +#define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010) +#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018) +#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c) + +/* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */ +#define HBUS_TARG_MBX_C (HBUS_BASE+0x030) +#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004) + +/* + * Registers for accessing device's internal peripheral registers + * (e.g. SCD, BSM, etc.). First write to address register, + * then read from or write to data register to complete the job. + * Bit usage for address registers (read or write): + * 0-15: register address (offset) within device + * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword) + */ +#define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044) +#define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048) +#define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c) +#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050) + +/* + * Per-Tx-queue write pointer (idx, really!) + * Indicates idx to next TFD that driver will fill (1 past latest filled). + * Bit usage: + * 0-7: queue write idx + * 11-8: queue selector + */ +#define HBUS_TARG_WRPTR (HBUS_BASE+0x060) + +#endif /* !__il_csr_h__ */ diff --git a/drivers/net/wireless/intel/iwlegacy/debug.c b/drivers/net/wireless/intel/iwlegacy/debug.c new file mode 100644 index 000000000000..908b9f4fef6f --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/debug.c @@ -0,0 +1,1426 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ +#include +#include +#include + +#include "common.h" + +static void +il_clear_traffic_stats(struct il_priv *il) +{ + memset(&il->tx_stats, 0, sizeof(struct traffic_stats)); + memset(&il->rx_stats, 0, sizeof(struct traffic_stats)); +} + +/* + * il_update_stats function record all the MGMT, CTRL and DATA pkt for + * both TX and Rx . Use debugfs to display the rx/rx_stats + */ +void +il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len) +{ + struct traffic_stats *stats; + + if (is_tx) + stats = &il->tx_stats; + else + stats = &il->rx_stats; + + if (ieee80211_is_mgmt(fc)) { + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): + stats->mgmt[MANAGEMENT_ASSOC_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): + stats->mgmt[MANAGEMENT_ASSOC_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): + stats->mgmt[MANAGEMENT_REASSOC_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): + stats->mgmt[MANAGEMENT_REASSOC_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): + stats->mgmt[MANAGEMENT_PROBE_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): + stats->mgmt[MANAGEMENT_PROBE_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_BEACON): + stats->mgmt[MANAGEMENT_BEACON]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ATIM): + stats->mgmt[MANAGEMENT_ATIM]++; + break; + case cpu_to_le16(IEEE80211_STYPE_DISASSOC): + stats->mgmt[MANAGEMENT_DISASSOC]++; + break; + case cpu_to_le16(IEEE80211_STYPE_AUTH): + stats->mgmt[MANAGEMENT_AUTH]++; + break; + case cpu_to_le16(IEEE80211_STYPE_DEAUTH): + stats->mgmt[MANAGEMENT_DEAUTH]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ACTION): + stats->mgmt[MANAGEMENT_ACTION]++; + break; + } + } else if (ieee80211_is_ctl(fc)) { + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_BACK_REQ): + stats->ctrl[CONTROL_BACK_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_BACK): + stats->ctrl[CONTROL_BACK]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PSPOLL): + stats->ctrl[CONTROL_PSPOLL]++; + break; + case cpu_to_le16(IEEE80211_STYPE_RTS): + stats->ctrl[CONTROL_RTS]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CTS): + stats->ctrl[CONTROL_CTS]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ACK): + stats->ctrl[CONTROL_ACK]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CFEND): + stats->ctrl[CONTROL_CFEND]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CFENDACK): + stats->ctrl[CONTROL_CFENDACK]++; + break; + } + } else { + /* data */ + stats->data_cnt++; + stats->data_bytes += len; + } +} +EXPORT_SYMBOL(il_update_stats); + +/* create and remove of files */ +#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, il, \ + &il_dbgfs_##name##_ops)) \ + goto err; \ +} while (0) + +#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ +} while (0) + +#define DEBUGFS_ADD_X32(name, parent, ptr) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ +} while (0) + +/* file operation */ +#define DEBUGFS_READ_FUNC(name) \ +static ssize_t il_dbgfs_##name##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos); + +#define DEBUGFS_WRITE_FUNC(name) \ +static ssize_t il_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos); + + +#define DEBUGFS_READ_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name); \ +static const struct file_operations il_dbgfs_##name##_ops = { \ + .read = il_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_WRITE_FILE_OPS(name) \ + DEBUGFS_WRITE_FUNC(name); \ +static const struct file_operations il_dbgfs_##name##_ops = { \ + .write = il_dbgfs_##name##_write, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name); \ + DEBUGFS_WRITE_FUNC(name); \ +static const struct file_operations il_dbgfs_##name##_ops = { \ + .write = il_dbgfs_##name##_write, \ + .read = il_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +static const char * +il_get_mgmt_string(int cmd) +{ + switch (cmd) { + IL_CMD(MANAGEMENT_ASSOC_REQ); + IL_CMD(MANAGEMENT_ASSOC_RESP); + IL_CMD(MANAGEMENT_REASSOC_REQ); + IL_CMD(MANAGEMENT_REASSOC_RESP); + IL_CMD(MANAGEMENT_PROBE_REQ); + IL_CMD(MANAGEMENT_PROBE_RESP); + IL_CMD(MANAGEMENT_BEACON); + IL_CMD(MANAGEMENT_ATIM); + IL_CMD(MANAGEMENT_DISASSOC); + IL_CMD(MANAGEMENT_AUTH); + IL_CMD(MANAGEMENT_DEAUTH); + IL_CMD(MANAGEMENT_ACTION); + default: + return "UNKNOWN"; + + } +} + +static const char * +il_get_ctrl_string(int cmd) +{ + switch (cmd) { + IL_CMD(CONTROL_BACK_REQ); + IL_CMD(CONTROL_BACK); + IL_CMD(CONTROL_PSPOLL); + IL_CMD(CONTROL_RTS); + IL_CMD(CONTROL_CTS); + IL_CMD(CONTROL_ACK); + IL_CMD(CONTROL_CFEND); + IL_CMD(CONTROL_CFENDACK); + default: + return "UNKNOWN"; + + } +} + +static ssize_t +il_dbgfs_tx_stats_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + char *buf; + int pos = 0; + + int cnt; + ssize_t ret; + const size_t bufsz = + 100 + sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX); + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); + for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { + pos += + scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", + il_get_mgmt_string(cnt), il->tx_stats.mgmt[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Control\n"); + for (cnt = 0; cnt < CONTROL_MAX; cnt++) { + pos += + scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", + il_get_ctrl_string(cnt), il->tx_stats.ctrl[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); + pos += + scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", + il->tx_stats.data_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", + il->tx_stats.data_bytes); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il_dbgfs_clear_traffic_stats_write(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct il_priv *il = file->private_data; + u32 clear_flag; + char buf[8]; + int buf_size; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &clear_flag) != 1) + return -EFAULT; + il_clear_traffic_stats(il); + + return count; +} + +static ssize_t +il_dbgfs_rx_stats_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + char *buf; + int pos = 0; + int cnt; + ssize_t ret; + const size_t bufsz = + 100 + sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX); + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); + for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { + pos += + scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", + il_get_mgmt_string(cnt), il->rx_stats.mgmt[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Control:\n"); + for (cnt = 0; cnt < CONTROL_MAX; cnt++) { + pos += + scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", + il_get_ctrl_string(cnt), il->rx_stats.ctrl[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); + pos += + scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", + il->rx_stats.data_cnt); + pos += + scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", + il->rx_stats.data_bytes); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +#define BYTE1_MASK 0x000000ff; +#define BYTE2_MASK 0x0000ffff; +#define BYTE3_MASK 0x00ffffff; +static ssize_t +il_dbgfs_sram_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + u32 val; + char *buf; + ssize_t ret; + int i; + int pos = 0; + struct il_priv *il = file->private_data; + size_t bufsz; + + /* default is to dump the entire data segment */ + if (!il->dbgfs_sram_offset && !il->dbgfs_sram_len) { + il->dbgfs_sram_offset = 0x800000; + if (il->ucode_type == UCODE_INIT) + il->dbgfs_sram_len = il->ucode_init_data.len; + else + il->dbgfs_sram_len = il->ucode_data.len; + } + bufsz = 30 + il->dbgfs_sram_len * sizeof(char) * 10; + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + pos += + scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", + il->dbgfs_sram_len); + pos += + scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", + il->dbgfs_sram_offset); + for (i = il->dbgfs_sram_len; i > 0; i -= 4) { + val = + il_read_targ_mem(il, + il->dbgfs_sram_offset + + il->dbgfs_sram_len - i); + if (i < 4) { + switch (i) { + case 1: + val &= BYTE1_MASK; + break; + case 2: + val &= BYTE2_MASK; + break; + case 3: + val &= BYTE3_MASK; + break; + } + } + if (!(i % 16)) + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "0x%08x ", val); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il_dbgfs_sram_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + char buf[64]; + int buf_size; + u32 offset, len; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%x,%x", &offset, &len) == 2) { + il->dbgfs_sram_offset = offset; + il->dbgfs_sram_len = len; + } else { + il->dbgfs_sram_offset = 0; + il->dbgfs_sram_len = 0; + } + + return count; +} + +static ssize_t +il_dbgfs_stations_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct il_priv *il = file->private_data; + struct il_station_entry *station; + int max_sta = il->hw_params.max_stations; + char *buf; + int i, j, pos = 0; + ssize_t ret; + /* Add 30 for initial string */ + const size_t bufsz = 30 + sizeof(char) * 500 * (il->num_stations); + + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += + scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n", + il->num_stations); + + for (i = 0; i < max_sta; i++) { + station = &il->stations[i]; + if (!station->used) + continue; + pos += + scnprintf(buf + pos, bufsz - pos, + "station %d - addr: %pM, flags: %#x\n", i, + station->sta.sta.addr, + station->sta.station_flags_msk); + pos += + scnprintf(buf + pos, bufsz - pos, + "TID\tseq_num\ttxq_id\tframes\ttfds\t"); + pos += + scnprintf(buf + pos, bufsz - pos, + "start_idx\tbitmap\t\t\trate_n_flags\n"); + + for (j = 0; j < MAX_TID_COUNT; j++) { + pos += + scnprintf(buf + pos, bufsz - pos, + "%d:\t%#x\t%#x\t%u\t%u\t%u\t\t%#.16llx\t%#x", + j, station->tid[j].seq_number, + station->tid[j].agg.txq_id, + station->tid[j].agg.frame_count, + station->tid[j].tfds_in_queue, + station->tid[j].agg.start_idx, + station->tid[j].agg.bitmap, + station->tid[j].agg.rate_n_flags); + + if (station->tid[j].agg.wait_for_ba) + pos += + scnprintf(buf + pos, bufsz - pos, + " - waitforba"); + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } + + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il_dbgfs_nvm_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + ssize_t ret; + struct il_priv *il = file->private_data; + int pos = 0, ofs = 0, buf_size = 0; + const u8 *ptr; + char *buf; + u16 eeprom_ver; + size_t eeprom_len = il->cfg->eeprom_size; + buf_size = 4 * eeprom_len + 256; + + if (eeprom_len % 16) { + IL_ERR("NVM size is not multiple of 16.\n"); + return -ENODATA; + } + + ptr = il->eeprom; + if (!ptr) { + IL_ERR("Invalid EEPROM memory\n"); + return -ENOMEM; + } + + /* 4 characters for byte 0xYY */ + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) { + IL_ERR("Can not allocate Buffer\n"); + return -ENOMEM; + } + eeprom_ver = il_eeprom_query16(il, EEPROM_VERSION); + pos += + scnprintf(buf + pos, buf_size - pos, "EEPROM " "version: 0x%x\n", + eeprom_ver); + for (ofs = 0; ofs < eeprom_len; ofs += 16) { + pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x %16ph\n", + ofs, ptr + ofs); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il_dbgfs_channels_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct il_priv *il = file->private_data; + struct ieee80211_channel *channels = NULL; + const struct ieee80211_supported_band *supp_band = NULL; + int pos = 0, i, bufsz = PAGE_SIZE; + char *buf; + ssize_t ret; + + if (!test_bit(S_GEO_CONFIGURED, &il->status)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IL_ERR("Can not allocate Buffer\n"); + return -ENOMEM; + } + + supp_band = il_get_hw_mode(il, IEEE80211_BAND_2GHZ); + if (supp_band) { + channels = supp_band->channels; + + pos += + scnprintf(buf + pos, bufsz - pos, + "Displaying %d channels in 2.4GHz band 802.11bg):\n", + supp_band->n_channels); + + for (i = 0; i < supp_band->n_channels; i++) + pos += + scnprintf(buf + pos, bufsz - pos, + "%d: %ddBm: BSS%s%s, %s.\n", + channels[i].hw_value, + channels[i].max_power, + channels[i]. + flags & IEEE80211_CHAN_RADAR ? + " (IEEE 802.11h required)" : "", + ((channels[i]. + flags & IEEE80211_CHAN_NO_IR) || + (channels[i]. + flags & IEEE80211_CHAN_RADAR)) ? "" : + ", IBSS", + channels[i]. + flags & IEEE80211_CHAN_NO_IR ? + "passive only" : "active/passive"); + } + supp_band = il_get_hw_mode(il, IEEE80211_BAND_5GHZ); + if (supp_band) { + channels = supp_band->channels; + + pos += + scnprintf(buf + pos, bufsz - pos, + "Displaying %d channels in 5.2GHz band (802.11a)\n", + supp_band->n_channels); + + for (i = 0; i < supp_band->n_channels; i++) + pos += + scnprintf(buf + pos, bufsz - pos, + "%d: %ddBm: BSS%s%s, %s.\n", + channels[i].hw_value, + channels[i].max_power, + channels[i]. + flags & IEEE80211_CHAN_RADAR ? + " (IEEE 802.11h required)" : "", + ((channels[i]. + flags & IEEE80211_CHAN_NO_IR) || + (channels[i]. + flags & IEEE80211_CHAN_RADAR)) ? "" : + ", IBSS", + channels[i]. + flags & IEEE80211_CHAN_NO_IR ? + "passive only" : "active/passive"); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il_dbgfs_status_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + char buf[512]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += + scnprintf(buf + pos, bufsz - pos, "S_HCMD_ACTIVE:\t %d\n", + test_bit(S_HCMD_ACTIVE, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_INT_ENABLED:\t %d\n", + test_bit(S_INT_ENABLED, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_RFKILL:\t %d\n", + test_bit(S_RFKILL, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_CT_KILL:\t\t %d\n", + test_bit(S_CT_KILL, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_INIT:\t\t %d\n", + test_bit(S_INIT, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_ALIVE:\t\t %d\n", + test_bit(S_ALIVE, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_READY:\t\t %d\n", + test_bit(S_READY, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_TEMPERATURE:\t %d\n", + test_bit(S_TEMPERATURE, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_GEO_CONFIGURED:\t %d\n", + test_bit(S_GEO_CONFIGURED, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_EXIT_PENDING:\t %d\n", + test_bit(S_EXIT_PENDING, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_STATS:\t %d\n", + test_bit(S_STATS, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_SCANNING:\t %d\n", + test_bit(S_SCANNING, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_SCAN_ABORTING:\t %d\n", + test_bit(S_SCAN_ABORTING, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_SCAN_HW:\t\t %d\n", + test_bit(S_SCAN_HW, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_POWER_PMI:\t %d\n", + test_bit(S_POWER_PMI, &il->status)); + pos += + scnprintf(buf + pos, bufsz - pos, "S_FW_ERROR:\t %d\n", + test_bit(S_FW_ERROR, &il->status)); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +il_dbgfs_interrupt_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = 24 * 64; /* 24 items * 64 char per item */ + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IL_ERR("Can not allocate Buffer\n"); + return -ENOMEM; + } + + pos += + scnprintf(buf + pos, bufsz - pos, "Interrupt Statistics Report:\n"); + + pos += + scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", + il->isr_stats.hw); + pos += + scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", + il->isr_stats.sw); + if (il->isr_stats.sw || il->isr_stats.hw) { + pos += + scnprintf(buf + pos, bufsz - pos, + "\tLast Restarting Code: 0x%X\n", + il->isr_stats.err_code); + } +#ifdef CONFIG_IWLEGACY_DEBUG + pos += + scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", + il->isr_stats.sch); + pos += + scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", + il->isr_stats.alive); +#endif + pos += + scnprintf(buf + pos, bufsz - pos, + "HW RF KILL switch toggled:\t %u\n", + il->isr_stats.rfkill); + + pos += + scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", + il->isr_stats.ctkill); + + pos += + scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", + il->isr_stats.wakeup); + + pos += + scnprintf(buf + pos, bufsz - pos, "Rx command responses:\t\t %u\n", + il->isr_stats.rx); + for (cnt = 0; cnt < IL_CN_MAX; cnt++) { + if (il->isr_stats.handlers[cnt] > 0) + pos += + scnprintf(buf + pos, bufsz - pos, + "\tRx handler[%36s]:\t\t %u\n", + il_get_cmd_string(cnt), + il->isr_stats.handlers[cnt]); + } + + pos += + scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", + il->isr_stats.tx); + + pos += + scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", + il->isr_stats.unhandled); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il_dbgfs_interrupt_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + char buf[8]; + int buf_size; + u32 reset_flag; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &reset_flag) != 1) + return -EFAULT; + if (reset_flag == 0) + il_clear_isr_stats(il); + + return count; +} + +static ssize_t +il_dbgfs_qos_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct il_priv *il = file->private_data; + int pos = 0, i; + char buf[256]; + const size_t bufsz = sizeof(buf); + + for (i = 0; i < AC_NUM; i++) { + pos += + scnprintf(buf + pos, bufsz - pos, + "\tcw_min\tcw_max\taifsn\ttxop\n"); + pos += + scnprintf(buf + pos, bufsz - pos, + "AC[%d]\t%u\t%u\t%u\t%u\n", i, + il->qos_data.def_qos_parm.ac[i].cw_min, + il->qos_data.def_qos_parm.ac[i].cw_max, + il->qos_data.def_qos_parm.ac[i].aifsn, + il->qos_data.def_qos_parm.ac[i].edca_txop); + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +il_dbgfs_disable_ht40_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + char buf[8]; + int buf_size; + int ht40; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &ht40) != 1) + return -EFAULT; + if (!il_is_any_associated(il)) + il->disable_ht40 = ht40 ? true : false; + else { + IL_ERR("Sta associated with AP - " + "Change to 40MHz channel support is not allowed\n"); + return -EINVAL; + } + + return count; +} + +static ssize_t +il_dbgfs_disable_ht40_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + char buf[100]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += + scnprintf(buf + pos, bufsz - pos, "11n 40MHz Mode: %s\n", + il->disable_ht40 ? "Disabled" : "Enabled"); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +DEBUGFS_READ_WRITE_FILE_OPS(sram); +DEBUGFS_READ_FILE_OPS(nvm); +DEBUGFS_READ_FILE_OPS(stations); +DEBUGFS_READ_FILE_OPS(channels); +DEBUGFS_READ_FILE_OPS(status); +DEBUGFS_READ_WRITE_FILE_OPS(interrupt); +DEBUGFS_READ_FILE_OPS(qos); +DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); + +static ssize_t +il_dbgfs_tx_queue_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + struct il_tx_queue *txq; + struct il_queue *q; + char *buf; + int pos = 0; + int cnt; + int ret; + const size_t bufsz = + sizeof(char) * 64 * il->cfg->num_of_queues; + + if (!il->txq) { + IL_ERR("txq not ready\n"); + return -EAGAIN; + } + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) { + txq = &il->txq[cnt]; + q = &txq->q; + pos += + scnprintf(buf + pos, bufsz - pos, + "hwq %.2d: read=%u write=%u stop=%d" + " swq_id=%#.2x (ac %d/hwq %d)\n", cnt, + q->read_ptr, q->write_ptr, + !!test_bit(cnt, il->queue_stopped), + txq->swq_id, txq->swq_id & 3, + (txq->swq_id >> 2) & 0x1f); + if (cnt >= 4) + continue; + /* for the ACs, display the stop count too */ + pos += + scnprintf(buf + pos, bufsz - pos, + " stop-count: %d\n", + atomic_read(&il->queue_stop_count[cnt])); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il_dbgfs_rx_queue_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + struct il_rx_queue *rxq = &il->rxq; + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", rxq->read); + pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", rxq->write); + pos += + scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", + rxq->free_count); + if (rxq->rb_stts) { + pos += + scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", + le16_to_cpu(rxq->rb_stts-> + closed_rb_num) & 0x0FFF); + } else { + pos += + scnprintf(buf + pos, bufsz - pos, + "closed_rb_num: Not Allocated\n"); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +il_dbgfs_ucode_rx_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + + return il->debugfs_ops->rx_stats_read(file, user_buf, count, ppos); +} + +static ssize_t +il_dbgfs_ucode_tx_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + + return il->debugfs_ops->tx_stats_read(file, user_buf, count, ppos); +} + +static ssize_t +il_dbgfs_ucode_general_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + + return il->debugfs_ops->general_stats_read(file, user_buf, count, ppos); +} + +static ssize_t +il_dbgfs_sensitivity_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct il_sensitivity_data) * 4 + 100; + ssize_t ret; + struct il_sensitivity_data *data; + + data = &il->sensitivity_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IL_ERR("Can not allocate Buffer\n"); + return -ENOMEM; + } + + pos += + scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n", + data->auto_corr_ofdm); + pos += + scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_mrc:\t\t %u\n", + data->auto_corr_ofdm_mrc); + pos += + scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n", + data->auto_corr_ofdm_x1); + pos += + scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_mrc_x1:\t\t %u\n", + data->auto_corr_ofdm_mrc_x1); + pos += + scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n", + data->auto_corr_cck); + pos += + scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n", + data->auto_corr_cck_mrc); + pos += + scnprintf(buf + pos, bufsz - pos, + "last_bad_plcp_cnt_ofdm:\t\t %u\n", + data->last_bad_plcp_cnt_ofdm); + pos += + scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n", + data->last_fa_cnt_ofdm); + pos += + scnprintf(buf + pos, bufsz - pos, "last_bad_plcp_cnt_cck:\t\t %u\n", + data->last_bad_plcp_cnt_cck); + pos += + scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n", + data->last_fa_cnt_cck); + pos += + scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n", + data->nrg_curr_state); + pos += + scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n", + data->nrg_prev_state); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t"); + for (cnt = 0; cnt < 10; cnt++) { + pos += + scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_value[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t"); + for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) { + pos += + scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_silence_rssi[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += + scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n", + data->nrg_silence_ref); + pos += + scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n", + data->nrg_energy_idx); + pos += + scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n", + data->nrg_silence_idx); + pos += + scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n", + data->nrg_th_cck); + pos += + scnprintf(buf + pos, bufsz - pos, + "nrg_auto_corr_silence_diff:\t %u\n", + data->nrg_auto_corr_silence_diff); + pos += + scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n", + data->num_in_cck_no_fa); + pos += + scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n", + data->nrg_th_ofdm); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il_dbgfs_chain_noise_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct il_chain_noise_data) * 4 + 100; + ssize_t ret; + struct il_chain_noise_data *data; + + data = &il->chain_noise_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IL_ERR("Can not allocate Buffer\n"); + return -ENOMEM; + } + + pos += + scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n", + data->active_chains); + pos += + scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n", + data->chain_noise_a); + pos += + scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n", + data->chain_noise_b); + pos += + scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n", + data->chain_noise_c); + pos += + scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n", + data->chain_signal_a); + pos += + scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n", + data->chain_signal_b); + pos += + scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n", + data->chain_signal_c); + pos += + scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n", + data->beacon_count); + + pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += + scnprintf(buf + pos, bufsz - pos, " %u", + data->disconn_array[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += + scnprintf(buf + pos, bufsz - pos, " %u", + data->delta_gain_code[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += + scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n", + data->radio_write); + pos += + scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n", + data->state); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +il_dbgfs_power_save_status_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + char buf[60]; + int pos = 0; + const size_t bufsz = sizeof(buf); + u32 pwrsave_status; + + pwrsave_status = + _il_rd(il, CSR_GP_CNTRL) & CSR_GP_REG_POWER_SAVE_STATUS_MSK; + + pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: "); + pos += + scnprintf(buf + pos, bufsz - pos, "%s\n", + (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" : + (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" : + (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" : + "error"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +il_dbgfs_clear_ucode_stats_write(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct il_priv *il = file->private_data; + char buf[8]; + int buf_size; + int clear; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &clear) != 1) + return -EFAULT; + + /* make request to uCode to retrieve stats information */ + mutex_lock(&il->mutex); + il_send_stats_request(il, CMD_SYNC, true); + mutex_unlock(&il->mutex); + + return count; +} + +static ssize_t +il_dbgfs_rxon_flags_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + int len = 0; + char buf[20]; + + len = sprintf(buf, "0x%04X\n", le32_to_cpu(il->active.flags)); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t +il_dbgfs_rxon_filter_flags_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + int len = 0; + char buf[20]; + + len = + sprintf(buf, "0x%04X\n", le32_to_cpu(il->active.filter_flags)); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t +il_dbgfs_fh_reg_read(struct file *file, char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct il_priv *il = file->private_data; + char *buf; + int pos = 0; + ssize_t ret = -EFAULT; + + if (il->ops->dump_fh) { + ret = pos = il->ops->dump_fh(il, &buf, true); + if (buf) { + ret = + simple_read_from_buffer(user_buf, count, ppos, buf, + pos); + kfree(buf); + } + } + + return ret; +} + +static ssize_t +il_dbgfs_missed_beacon_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + int pos = 0; + char buf[12]; + const size_t bufsz = sizeof(buf); + + pos += + scnprintf(buf + pos, bufsz - pos, "%d\n", + il->missed_beacon_threshold); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +il_dbgfs_missed_beacon_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct il_priv *il = file->private_data; + char buf[8]; + int buf_size; + int missed; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &missed) != 1) + return -EINVAL; + + if (missed < IL_MISSED_BEACON_THRESHOLD_MIN || + missed > IL_MISSED_BEACON_THRESHOLD_MAX) + il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF; + else + il->missed_beacon_threshold = missed; + + return count; +} + +static ssize_t +il_dbgfs_force_reset_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + int pos = 0; + char buf[300]; + const size_t bufsz = sizeof(buf); + struct il_force_reset *force_reset; + + force_reset = &il->force_reset; + + pos += + scnprintf(buf + pos, bufsz - pos, "\tnumber of reset request: %d\n", + force_reset->reset_request_count); + pos += + scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request success: %d\n", + force_reset->reset_success_count); + pos += + scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request reject: %d\n", + force_reset->reset_reject_count); + pos += + scnprintf(buf + pos, bufsz - pos, "\treset duration: %lu\n", + force_reset->reset_duration); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +il_dbgfs_force_reset_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + + int ret; + struct il_priv *il = file->private_data; + + ret = il_force_reset(il, true); + + return ret ? ret : count; +} + +static ssize_t +il_dbgfs_wd_timeout_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + + struct il_priv *il = file->private_data; + char buf[8]; + int buf_size; + int timeout; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &timeout) != 1) + return -EINVAL; + if (timeout < 0 || timeout > IL_MAX_WD_TIMEOUT) + timeout = IL_DEF_WD_TIMEOUT; + + il->cfg->wd_timeout = timeout; + il_setup_watchdog(il); + return count; +} + +DEBUGFS_READ_FILE_OPS(rx_stats); +DEBUGFS_READ_FILE_OPS(tx_stats); +DEBUGFS_READ_FILE_OPS(rx_queue); +DEBUGFS_READ_FILE_OPS(tx_queue); +DEBUGFS_READ_FILE_OPS(ucode_rx_stats); +DEBUGFS_READ_FILE_OPS(ucode_tx_stats); +DEBUGFS_READ_FILE_OPS(ucode_general_stats); +DEBUGFS_READ_FILE_OPS(sensitivity); +DEBUGFS_READ_FILE_OPS(chain_noise); +DEBUGFS_READ_FILE_OPS(power_save_status); +DEBUGFS_WRITE_FILE_OPS(clear_ucode_stats); +DEBUGFS_WRITE_FILE_OPS(clear_traffic_stats); +DEBUGFS_READ_FILE_OPS(fh_reg); +DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon); +DEBUGFS_READ_WRITE_FILE_OPS(force_reset); +DEBUGFS_READ_FILE_OPS(rxon_flags); +DEBUGFS_READ_FILE_OPS(rxon_filter_flags); +DEBUGFS_WRITE_FILE_OPS(wd_timeout); + +/* + * Create the debugfs files and directories + * + */ +int +il_dbgfs_register(struct il_priv *il, const char *name) +{ + struct dentry *phyd = il->hw->wiphy->debugfsdir; + struct dentry *dir_drv, *dir_data, *dir_rf, *dir_debug; + + dir_drv = debugfs_create_dir(name, phyd); + if (!dir_drv) + return -ENOMEM; + + il->debugfs_dir = dir_drv; + + dir_data = debugfs_create_dir("data", dir_drv); + if (!dir_data) + goto err; + dir_rf = debugfs_create_dir("rf", dir_drv); + if (!dir_rf) + goto err; + dir_debug = debugfs_create_dir("debug", dir_drv); + if (!dir_debug) + goto err; + + DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(interrupt, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(rx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(tx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(rx_queue, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(tx_queue, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(clear_ucode_stats, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(clear_traffic_stats, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(fh_reg, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(force_reset, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); + + if (il->cfg->sensitivity_calib_by_driver) + DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); + if (il->cfg->chain_noise_calib_by_driver) + DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(wd_timeout, dir_debug, S_IWUSR); + if (il->cfg->sensitivity_calib_by_driver) + DEBUGFS_ADD_BOOL(disable_sensitivity, dir_rf, + &il->disable_sens_cal); + if (il->cfg->chain_noise_calib_by_driver) + DEBUGFS_ADD_BOOL(disable_chain_noise, dir_rf, + &il->disable_chain_noise_cal); + DEBUGFS_ADD_BOOL(disable_tx_power, dir_rf, &il->disable_tx_power_cal); + return 0; + +err: + IL_ERR("Can't create the debugfs directory\n"); + il_dbgfs_unregister(il); + return -ENOMEM; +} +EXPORT_SYMBOL(il_dbgfs_register); + +/** + * Remove the debugfs files and directories + * + */ +void +il_dbgfs_unregister(struct il_priv *il) +{ + if (!il->debugfs_dir) + return; + + debugfs_remove_recursive(il->debugfs_dir); + il->debugfs_dir = NULL; +} +EXPORT_SYMBOL(il_dbgfs_unregister); diff --git a/drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h b/drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h new file mode 100644 index 000000000000..85fe48e520f9 --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h @@ -0,0 +1,92 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __il_spectrum_h__ +#define __il_spectrum_h__ +enum { /* ieee80211_basic_report.map */ + IEEE80211_BASIC_MAP_BSS = (1 << 0), + IEEE80211_BASIC_MAP_OFDM = (1 << 1), + IEEE80211_BASIC_MAP_UNIDENTIFIED = (1 << 2), + IEEE80211_BASIC_MAP_RADAR = (1 << 3), + IEEE80211_BASIC_MAP_UNMEASURED = (1 << 4), + /* Bits 5-7 are reserved */ + +}; +struct ieee80211_basic_report { + u8 channel; + __le64 start_time; + __le16 duration; + u8 map; +} __packed; + +enum { /* ieee80211_measurement_request.mode */ + /* Bit 0 is reserved */ + IEEE80211_MEASUREMENT_ENABLE = (1 << 1), + IEEE80211_MEASUREMENT_REQUEST = (1 << 2), + IEEE80211_MEASUREMENT_REPORT = (1 << 3), + /* Bits 4-7 are reserved */ +}; + +enum { + IEEE80211_REPORT_BASIC = 0, /* required */ + IEEE80211_REPORT_CCA = 1, /* optional */ + IEEE80211_REPORT_RPI = 2, /* optional */ + /* 3-255 reserved */ +}; + +struct ieee80211_measurement_params { + u8 channel; + __le64 start_time; + __le16 duration; +} __packed; + +struct ieee80211_info_element { + u8 id; + u8 len; + u8 data[0]; +} __packed; + +struct ieee80211_measurement_request { + struct ieee80211_info_element ie; + u8 token; + u8 mode; + u8 type; + struct ieee80211_measurement_params params[0]; +} __packed; + +struct ieee80211_measurement_report { + struct ieee80211_info_element ie; + u8 token; + u8 mode; + u8 type; + union { + struct ieee80211_basic_report basic[0]; + } u; +} __packed; + +#endif diff --git a/drivers/net/wireless/intel/iwlegacy/prph.h b/drivers/net/wireless/intel/iwlegacy/prph.h new file mode 100644 index 000000000000..ffec4b4a248a --- /dev/null +++ b/drivers/net/wireless/intel/iwlegacy/prph.h @@ -0,0 +1,522 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __il_prph_h__ +#define __il_prph_h__ + +/* + * Registers in this file are internal, not PCI bus memory mapped. + * Driver accesses these via HBUS_TARG_PRPH_* registers. + */ +#define PRPH_BASE (0x00000) +#define PRPH_END (0xFFFFF) + +/* APMG (power management) constants */ +#define APMG_BASE (PRPH_BASE + 0x3000) +#define APMG_CLK_CTRL_REG (APMG_BASE + 0x0000) +#define APMG_CLK_EN_REG (APMG_BASE + 0x0004) +#define APMG_CLK_DIS_REG (APMG_BASE + 0x0008) +#define APMG_PS_CTRL_REG (APMG_BASE + 0x000c) +#define APMG_PCIDEV_STT_REG (APMG_BASE + 0x0010) +#define APMG_RFKILL_REG (APMG_BASE + 0x0014) +#define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c) +#define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020) +#define APMG_DIGITAL_SVR_REG (APMG_BASE + 0x0058) +#define APMG_ANALOG_SVR_REG (APMG_BASE + 0x006C) + +#define APMS_CLK_VAL_MRB_FUNC_MODE (0x00000001) +#define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) +#define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) + +#define APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS (0x00400000) +#define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000) +#define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000) +#define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) +#define APMG_PS_CTRL_VAL_PWR_SRC_MAX (0x01000000) /* 3945 only */ +#define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000) +#define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */ +#define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060) + +#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) + +/** + * BSM (Bootstrap State Machine) + * + * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program + * in special SRAM that does not power down when the embedded control + * processor is sleeping (e.g. for periodic power-saving shutdowns of radio). + * + * When powering back up after sleeps (or during initial uCode load), the BSM + * internally loads the short bootstrap program from the special SRAM into the + * embedded processor's instruction SRAM, and starts the processor so it runs + * the bootstrap program. + * + * This bootstrap program loads (via PCI busmaster DMA) instructions and data + * images for a uCode program from host DRAM locations. The host driver + * indicates DRAM locations and sizes for instruction and data images via the + * four BSM_DRAM_* registers. Once the bootstrap program loads the new program, + * the new program starts automatically. + * + * The uCode used for open-source drivers includes two programs: + * + * 1) Initialization -- performs hardware calibration and sets up some + * internal data, then notifies host via "initialize alive" notification + * (struct il_init_alive_resp) that it has completed all of its work. + * After signal from host, it then loads and starts the runtime program. + * The initialization program must be used when initially setting up the + * NIC after loading the driver. + * + * 2) Runtime/Protocol -- performs all normal runtime operations. This + * notifies host via "alive" notification (struct il_alive_resp) that it + * is ready to be used. + * + * When initializing the NIC, the host driver does the following procedure: + * + * 1) Load bootstrap program (instructions only, no data image for bootstrap) + * into bootstrap memory. Use dword writes starting at BSM_SRAM_LOWER_BOUND + * + * 2) Point (via BSM_DRAM_*) to the "initialize" uCode data and instruction + * images in host DRAM. + * + * 3) Set up BSM to copy from BSM SRAM into uCode instruction SRAM when asked: + * BSM_WR_MEM_SRC_REG = 0 + * BSM_WR_MEM_DST_REG = RTC_INST_LOWER_BOUND + * BSM_WR_MEM_DWCOUNT_REG = # dwords in bootstrap instruction image + * + * 4) Load bootstrap into instruction SRAM: + * BSM_WR_CTRL_REG = BSM_WR_CTRL_REG_BIT_START + * + * 5) Wait for load completion: + * Poll BSM_WR_CTRL_REG for BSM_WR_CTRL_REG_BIT_START = 0 + * + * 6) Enable future boot loads whenever NIC's power management triggers it: + * BSM_WR_CTRL_REG = BSM_WR_CTRL_REG_BIT_START_EN + * + * 7) Start the NIC by removing all reset bits: + * CSR_RESET = 0 + * + * The bootstrap uCode (already in instruction SRAM) loads initialization + * uCode. Initialization uCode performs data initialization, sends + * "initialize alive" notification to host, and waits for a signal from + * host to load runtime code. + * + * 4) Point (via BSM_DRAM_*) to the "runtime" uCode data and instruction + * images in host DRAM. The last register loaded must be the instruction + * byte count register ("1" in MSbit tells initialization uCode to load + * the runtime uCode): + * BSM_DRAM_INST_BYTECOUNT_REG = byte count | BSM_DRAM_INST_LOAD + * + * 5) Wait for "alive" notification, then issue normal runtime commands. + * + * Data caching during power-downs: + * + * Just before the embedded controller powers down (e.g for automatic + * power-saving modes, or for RFKILL), uCode stores (via PCI busmaster DMA) + * a current snapshot of the embedded processor's data SRAM into host DRAM. + * This caches the data while the embedded processor's memory is powered down. + * Location and size are controlled by BSM_DRAM_DATA_* registers. + * + * NOTE: Instruction SRAM does not need to be saved, since that doesn't + * change during operation; the original image (from uCode distribution + * file) can be used for reload. + * + * When powering back up, the BSM loads the bootstrap program. Bootstrap looks + * at the BSM_DRAM_* registers, which now point to the runtime instruction + * image and the cached (modified) runtime data (*not* the initialization + * uCode). Bootstrap reloads these runtime images into SRAM, and restarts the + * uCode from where it left off before the power-down. + * + * NOTE: Initialization uCode does *not* run as part of the save/restore + * procedure. + * + * This save/restore method is mostly for autonomous power management during + * normal operation (result of C_POWER_TBL). Platform suspend/resume and + * RFKILL should use complete restarts (with total re-initialization) of uCode, + * allowing total shutdown (including BSM memory). + * + * Note that, during normal operation, the host DRAM that held the initial + * startup data for the runtime code is now being used as a backup data cache + * for modified data! If you need to completely re-initialize the NIC, make + * sure that you use the runtime data image from the uCode distribution file, + * not the modified/saved runtime data. You may want to store a separate + * "clean" runtime data image in DRAM to avoid disk reads of distribution file. + */ + +/* BSM bit fields */ +#define BSM_WR_CTRL_REG_BIT_START (0x80000000) /* start boot load now */ +#define BSM_WR_CTRL_REG_BIT_START_EN (0x40000000) /* enable boot after pwrup */ +#define BSM_DRAM_INST_LOAD (0x80000000) /* start program load now */ + +/* BSM addresses */ +#define BSM_BASE (PRPH_BASE + 0x3400) +#define BSM_END (PRPH_BASE + 0x3800) + +#define BSM_WR_CTRL_REG (BSM_BASE + 0x000) /* ctl and status */ +#define BSM_WR_MEM_SRC_REG (BSM_BASE + 0x004) /* source in BSM mem */ +#define BSM_WR_MEM_DST_REG (BSM_BASE + 0x008) /* dest in SRAM mem */ +#define BSM_WR_DWCOUNT_REG (BSM_BASE + 0x00C) /* bytes */ +#define BSM_WR_STATUS_REG (BSM_BASE + 0x010) /* bit 0: 1 == done */ + +/* + * Pointers and size regs for bootstrap load and data SRAM save/restore. + * NOTE: 3945 pointers use bits 31:0 of DRAM address. + * 4965 pointers use bits 35:4 of DRAM address. + */ +#define BSM_DRAM_INST_PTR_REG (BSM_BASE + 0x090) +#define BSM_DRAM_INST_BYTECOUNT_REG (BSM_BASE + 0x094) +#define BSM_DRAM_DATA_PTR_REG (BSM_BASE + 0x098) +#define BSM_DRAM_DATA_BYTECOUNT_REG (BSM_BASE + 0x09C) + +/* + * BSM special memory, stays powered on during power-save sleeps. + * Read/write, address range from LOWER_BOUND to (LOWER_BOUND + SIZE -1) + */ +#define BSM_SRAM_LOWER_BOUND (PRPH_BASE + 0x3800) +#define BSM_SRAM_SIZE (1024) /* bytes */ + +/* 3945 Tx scheduler registers */ +#define ALM_SCD_BASE (PRPH_BASE + 0x2E00) +#define ALM_SCD_MODE_REG (ALM_SCD_BASE + 0x000) +#define ALM_SCD_ARASTAT_REG (ALM_SCD_BASE + 0x004) +#define ALM_SCD_TXFACT_REG (ALM_SCD_BASE + 0x010) +#define ALM_SCD_TXF4MF_REG (ALM_SCD_BASE + 0x014) +#define ALM_SCD_TXF5MF_REG (ALM_SCD_BASE + 0x020) +#define ALM_SCD_SBYP_MODE_1_REG (ALM_SCD_BASE + 0x02C) +#define ALM_SCD_SBYP_MODE_2_REG (ALM_SCD_BASE + 0x030) + +/** + * Tx Scheduler + * + * The Tx Scheduler selects the next frame to be transmitted, choosing TFDs + * (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in + * host DRAM. It steers each frame's Tx command (which contains the frame + * data) into one of up to 7 prioritized Tx DMA FIFO channels within the + * device. A queue maps to only one (selectable by driver) Tx DMA channel, + * but one DMA channel may take input from several queues. + * + * Tx DMA FIFOs have dedicated purposes. For 4965, they are used as follows + * (cf. default_queue_to_tx_fifo in 4965.c): + * + * 0 -- EDCA BK (background) frames, lowest priority + * 1 -- EDCA BE (best effort) frames, normal priority + * 2 -- EDCA VI (video) frames, higher priority + * 3 -- EDCA VO (voice) and management frames, highest priority + * 4 -- Commands (e.g. RXON, etc.) + * 5 -- unused (HCCA) + * 6 -- unused (HCCA) + * 7 -- not used by driver (device-internal only) + * + * + * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6. + * In addition, driver can map the remaining queues to Tx DMA/FIFO + * channels 0-3 to support 11n aggregation via EDCA DMA channels. + * + * The driver sets up each queue to work in one of two modes: + * + * 1) Scheduler-Ack, in which the scheduler automatically supports a + * block-ack (BA) win of up to 64 TFDs. In this mode, each queue + * contains TFDs for a unique combination of Recipient Address (RA) + * and Traffic Identifier (TID), that is, traffic of a given + * Quality-Of-Service (QOS) priority, destined for a single station. + * + * In scheduler-ack mode, the scheduler keeps track of the Tx status of + * each frame within the BA win, including whether it's been transmitted, + * and whether it's been acknowledged by the receiving station. The device + * automatically processes block-acks received from the receiving STA, + * and reschedules un-acked frames to be retransmitted (successful + * Tx completion may end up being out-of-order). + * + * The driver must maintain the queue's Byte Count table in host DRAM + * (struct il4965_sched_queue_byte_cnt_tbl) for this mode. + * This mode does not support fragmentation. + * + * 2) FIFO (a.k.a. non-Scheduler-ACK), in which each TFD is processed in order. + * The device may automatically retry Tx, but will retry only one frame + * at a time, until receiving ACK from receiving station, or reaching + * retry limit and giving up. + * + * The command queue (#4/#9) must use this mode! + * This mode does not require use of the Byte Count table in host DRAM. + * + * Driver controls scheduler operation via 3 means: + * 1) Scheduler registers + * 2) Shared scheduler data base in internal 4956 SRAM + * 3) Shared data in host DRAM + * + * Initialization: + * + * When loading, driver should allocate memory for: + * 1) 16 TFD circular buffers, each with space for (typically) 256 TFDs. + * 2) 16 Byte Count circular buffers in 16 KBytes contiguous memory + * (1024 bytes for each queue). + * + * After receiving "Alive" response from uCode, driver must initialize + * the scheduler (especially for queue #4/#9, the command queue, otherwise + * the driver can't issue commands!): + */ + +/** + * Max Tx win size is the max number of contiguous TFDs that the scheduler + * can keep track of at one time when creating block-ack chains of frames. + * Note that "64" matches the number of ack bits in a block-ack packet. + * Driver should use SCD_WIN_SIZE and SCD_FRAME_LIMIT values to initialize + * IL49_SCD_CONTEXT_QUEUE_OFFSET(x) values. + */ +#define SCD_WIN_SIZE 64 +#define SCD_FRAME_LIMIT 64 + +/* SCD registers are internal, must be accessed via HBUS_TARG_PRPH regs */ +#define IL49_SCD_START_OFFSET 0xa02c00 + +/* + * 4965 tells driver SRAM address for internal scheduler structs via this reg. + * Value is valid only after "Alive" response from uCode. + */ +#define IL49_SCD_SRAM_BASE_ADDR (IL49_SCD_START_OFFSET + 0x0) + +/* + * Driver may need to update queue-empty bits after changing queue's + * write and read pointers (idxes) during (re-)initialization (i.e. when + * scheduler is not tracking what's happening). + * Bit fields: + * 31-16: Write mask -- 1: update empty bit, 0: don't change empty bit + * 15-00: Empty state, one for each queue -- 1: empty, 0: non-empty + * NOTE: This register is not used by Linux driver. + */ +#define IL49_SCD_EMPTY_BITS (IL49_SCD_START_OFFSET + 0x4) + +/* + * Physical base address of array of byte count (BC) circular buffers (CBs). + * Each Tx queue has a BC CB in host DRAM to support Scheduler-ACK mode. + * This register points to BC CB for queue 0, must be on 1024-byte boundary. + * Others are spaced by 1024 bytes. + * Each BC CB is 2 bytes * (256 + 64) = 740 bytes, followed by 384 bytes pad. + * (Index into a queue's BC CB) = (idx into queue's TFD CB) = (SSN & 0xff). + * Bit fields: + * 25-00: Byte Count CB physical address [35:10], must be 1024-byte aligned. + */ +#define IL49_SCD_DRAM_BASE_ADDR (IL49_SCD_START_OFFSET + 0x10) + +/* + * Enables any/all Tx DMA/FIFO channels. + * Scheduler generates requests for only the active channels. + * Set this to 0xff to enable all 8 channels (normal usage). + * Bit fields: + * 7- 0: Enable (1), disable (0), one bit for each channel 0-7 + */ +#define IL49_SCD_TXFACT (IL49_SCD_START_OFFSET + 0x1c) +/* + * Queue (x) Write Pointers (idxes, really!), one for each Tx queue. + * Initialized and updated by driver as new TFDs are added to queue. + * NOTE: If using Block Ack, idx must correspond to frame's + * Start Sequence Number; idx = (SSN & 0xff) + * NOTE: Alternative to HBUS_TARG_WRPTR, which is what Linux driver uses? + */ +#define IL49_SCD_QUEUE_WRPTR(x) (IL49_SCD_START_OFFSET + 0x24 + (x) * 4) + +/* + * Queue (x) Read Pointers (idxes, really!), one for each Tx queue. + * For FIFO mode, idx indicates next frame to transmit. + * For Scheduler-ACK mode, idx indicates first frame in Tx win. + * Initialized by driver, updated by scheduler. + */ +#define IL49_SCD_QUEUE_RDPTR(x) (IL49_SCD_START_OFFSET + 0x64 + (x) * 4) + +/* + * Select which queues work in chain mode (1) vs. not (0). + * Use chain mode to build chains of aggregated frames. + * Bit fields: + * 31-16: Reserved + * 15-00: Mode, one bit for each queue -- 1: Chain mode, 0: one-at-a-time + * NOTE: If driver sets up queue for chain mode, it should be also set up + * Scheduler-ACK mode as well, via SCD_QUEUE_STATUS_BITS(x). + */ +#define IL49_SCD_QUEUECHAIN_SEL (IL49_SCD_START_OFFSET + 0xd0) + +/* + * Select which queues interrupt driver when scheduler increments + * a queue's read pointer (idx). + * Bit fields: + * 31-16: Reserved + * 15-00: Interrupt enable, one bit for each queue -- 1: enabled, 0: disabled + * NOTE: This functionality is apparently a no-op; driver relies on interrupts + * from Rx queue to read Tx command responses and update Tx queues. + */ +#define IL49_SCD_INTERRUPT_MASK (IL49_SCD_START_OFFSET + 0xe4) + +/* + * Queue search status registers. One for each queue. + * Sets up queue mode and assigns queue to Tx DMA channel. + * Bit fields: + * 19-10: Write mask/enable bits for bits 0-9 + * 9: Driver should init to "0" + * 8: Scheduler-ACK mode (1), non-Scheduler-ACK (i.e. FIFO) mode (0). + * Driver should init to "1" for aggregation mode, or "0" otherwise. + * 7-6: Driver should init to "0" + * 5: Window Size Left; indicates whether scheduler can request + * another TFD, based on win size, etc. Driver should init + * this bit to "1" for aggregation mode, or "0" for non-agg. + * 4-1: Tx FIFO to use (range 0-7). + * 0: Queue is active (1), not active (0). + * Other bits should be written as "0" + * + * NOTE: If enabling Scheduler-ACK mode, chain mode should also be enabled + * via SCD_QUEUECHAIN_SEL. + */ +#define IL49_SCD_QUEUE_STATUS_BITS(x)\ + (IL49_SCD_START_OFFSET + 0x104 + (x) * 4) + +/* Bit field positions */ +#define IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE (0) +#define IL49_SCD_QUEUE_STTS_REG_POS_TXF (1) +#define IL49_SCD_QUEUE_STTS_REG_POS_WSL (5) +#define IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK (8) + +/* Write masks */ +#define IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (10) +#define IL49_SCD_QUEUE_STTS_REG_MSK (0x0007FC00) + +/** + * 4965 internal SRAM structures for scheduler, shared with driver ... + * + * Driver should clear and initialize the following areas after receiving + * "Alive" response from 4965 uCode, i.e. after initial + * uCode load, or after a uCode load done for error recovery: + * + * SCD_CONTEXT_DATA_OFFSET (size 128 bytes) + * SCD_TX_STTS_BITMAP_OFFSET (size 256 bytes) + * SCD_TRANSLATE_TBL_OFFSET (size 32 bytes) + * + * Driver accesses SRAM via HBUS_TARG_MEM_* registers. + * Driver reads base address of this scheduler area from SCD_SRAM_BASE_ADDR. + * All OFFSET values must be added to this base address. + */ + +/* + * Queue context. One 8-byte entry for each of 16 queues. + * + * Driver should clear this entire area (size 0x80) to 0 after receiving + * "Alive" notification from uCode. Additionally, driver should init + * each queue's entry as follows: + * + * LS Dword bit fields: + * 0-06: Max Tx win size for Scheduler-ACK. Driver should init to 64. + * + * MS Dword bit fields: + * 16-22: Frame limit. Driver should init to 10 (0xa). + * + * Driver should init all other bits to 0. + * + * Init must be done after driver receives "Alive" response from 4965 uCode, + * and when setting up queue for aggregation. + */ +#define IL49_SCD_CONTEXT_DATA_OFFSET 0x380 +#define IL49_SCD_CONTEXT_QUEUE_OFFSET(x) \ + (IL49_SCD_CONTEXT_DATA_OFFSET + ((x) * 8)) + +#define IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS (0) +#define IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK (0x0000007F) +#define IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16) +#define IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000) + +/* + * Tx Status Bitmap + * + * Driver should clear this entire area (size 0x100) to 0 after receiving + * "Alive" notification from uCode. Area is used only by device itself; + * no other support (besides clearing) is required from driver. + */ +#define IL49_SCD_TX_STTS_BITMAP_OFFSET 0x400 + +/* + * RAxTID to queue translation mapping. + * + * When queue is in Scheduler-ACK mode, frames placed in a that queue must be + * for only one combination of receiver address (RA) and traffic ID (TID), i.e. + * one QOS priority level destined for one station (for this wireless link, + * not final destination). The SCD_TRANSLATE_TBL area provides 16 16-bit + * mappings, one for each of the 16 queues. If queue is not in Scheduler-ACK + * mode, the device ignores the mapping value. + * + * Bit fields, for each 16-bit map: + * 15-9: Reserved, set to 0 + * 8-4: Index into device's station table for recipient station + * 3-0: Traffic ID (tid), range 0-15 + * + * Driver should clear this entire area (size 32 bytes) to 0 after receiving + * "Alive" notification from uCode. To update a 16-bit map value, driver + * must read a dword-aligned value from device SRAM, replace the 16-bit map + * value of interest, and write the dword value back into device SRAM. + */ +#define IL49_SCD_TRANSLATE_TBL_OFFSET 0x500 + +/* Find translation table dword to read/write for given queue */ +#define IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(x) \ + ((IL49_SCD_TRANSLATE_TBL_OFFSET + ((x) * 2)) & 0xfffffffc) + +#define IL_SCD_TXFIFO_POS_TID (0) +#define IL_SCD_TXFIFO_POS_RA (4) +#define IL_SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF) + +/*********************** END TX SCHEDULER *************************************/ + +#endif /* __il_prph_h__ */ diff --git a/drivers/net/wireless/iwlegacy/3945-debug.c b/drivers/net/wireless/iwlegacy/3945-debug.c deleted file mode 100644 index c1b4441fb8b2..000000000000 --- a/drivers/net/wireless/iwlegacy/3945-debug.c +++ /dev/null @@ -1,511 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ - -#include "common.h" -#include "3945.h" - -static int -il3945_stats_flag(struct il_priv *il, char *buf, int bufsz) -{ - int p = 0; - - p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", - le32_to_cpu(il->_3945.stats.flag)); - if (le32_to_cpu(il->_3945.stats.flag) & UCODE_STATS_CLEAR_MSK) - p += scnprintf(buf + p, bufsz - p, - "\tStatistics have been cleared\n"); - p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", - (le32_to_cpu(il->_3945.stats.flag) & - UCODE_STATS_FREQUENCY_MSK) ? "2.4 GHz" : "5.2 GHz"); - p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", - (le32_to_cpu(il->_3945.stats.flag) & - UCODE_STATS_NARROW_BAND_MSK) ? "enabled" : "disabled"); - return p; -} - -static ssize_t -il3945_ucode_rx_stats_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - int pos = 0; - char *buf; - int bufsz = - sizeof(struct iwl39_stats_rx_phy) * 40 + - sizeof(struct iwl39_stats_rx_non_phy) * 40 + 400; - ssize_t ret; - struct iwl39_stats_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; - struct iwl39_stats_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; - struct iwl39_stats_rx_non_phy *general, *accum_general; - struct iwl39_stats_rx_non_phy *delta_general, *max_general; - - if (!il_is_alive(il)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - IL_ERR("Can not allocate Buffer\n"); - return -ENOMEM; - } - - /* - * The statistic information display here is based on - * the last stats notification from uCode - * might not reflect the current uCode activity - */ - ofdm = &il->_3945.stats.rx.ofdm; - cck = &il->_3945.stats.rx.cck; - general = &il->_3945.stats.rx.general; - accum_ofdm = &il->_3945.accum_stats.rx.ofdm; - accum_cck = &il->_3945.accum_stats.rx.cck; - accum_general = &il->_3945.accum_stats.rx.general; - delta_ofdm = &il->_3945.delta_stats.rx.ofdm; - delta_cck = &il->_3945.delta_stats.rx.cck; - delta_general = &il->_3945.delta_stats.rx.general; - max_ofdm = &il->_3945.max_delta.rx.ofdm; - max_cck = &il->_3945.max_delta.rx.cck; - max_general = &il->_3945.max_delta.rx.general; - - pos += il3945_stats_flag(il, buf, bufsz); - pos += - scnprintf(buf + pos, bufsz - pos, - "%-32s current" - "acumulative delta max\n", - "Statistics_Rx - OFDM:"); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "ina_cnt:", - le32_to_cpu(ofdm->ina_cnt), accum_ofdm->ina_cnt, - delta_ofdm->ina_cnt, max_ofdm->ina_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "fina_cnt:", - le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, - delta_ofdm->fina_cnt, max_ofdm->fina_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "plcp_err:", - le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, - delta_ofdm->plcp_err, max_ofdm->plcp_err); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "crc32_err:", - le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, - delta_ofdm->crc32_err, max_ofdm->crc32_err); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "overrun_err:", - le32_to_cpu(ofdm->overrun_err), accum_ofdm->overrun_err, - delta_ofdm->overrun_err, max_ofdm->overrun_err); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "early_overrun_err:", - le32_to_cpu(ofdm->early_overrun_err), - accum_ofdm->early_overrun_err, - delta_ofdm->early_overrun_err, - max_ofdm->early_overrun_err); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "crc32_good:", - le32_to_cpu(ofdm->crc32_good), accum_ofdm->crc32_good, - delta_ofdm->crc32_good, max_ofdm->crc32_good); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "false_alarm_cnt:", - le32_to_cpu(ofdm->false_alarm_cnt), - accum_ofdm->false_alarm_cnt, delta_ofdm->false_alarm_cnt, - max_ofdm->false_alarm_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "fina_sync_err_cnt:", - le32_to_cpu(ofdm->fina_sync_err_cnt), - accum_ofdm->fina_sync_err_cnt, - delta_ofdm->fina_sync_err_cnt, - max_ofdm->fina_sync_err_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "sfd_timeout:", - le32_to_cpu(ofdm->sfd_timeout), accum_ofdm->sfd_timeout, - delta_ofdm->sfd_timeout, max_ofdm->sfd_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "fina_timeout:", - le32_to_cpu(ofdm->fina_timeout), accum_ofdm->fina_timeout, - delta_ofdm->fina_timeout, max_ofdm->fina_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "unresponded_rts:", - le32_to_cpu(ofdm->unresponded_rts), - accum_ofdm->unresponded_rts, delta_ofdm->unresponded_rts, - max_ofdm->unresponded_rts); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", - "rxe_frame_lmt_ovrun:", - le32_to_cpu(ofdm->rxe_frame_limit_overrun), - accum_ofdm->rxe_frame_limit_overrun, - delta_ofdm->rxe_frame_limit_overrun, - max_ofdm->rxe_frame_limit_overrun); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "sent_ack_cnt:", - le32_to_cpu(ofdm->sent_ack_cnt), accum_ofdm->sent_ack_cnt, - delta_ofdm->sent_ack_cnt, max_ofdm->sent_ack_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "sent_cts_cnt:", - le32_to_cpu(ofdm->sent_cts_cnt), accum_ofdm->sent_cts_cnt, - delta_ofdm->sent_cts_cnt, max_ofdm->sent_cts_cnt); - - pos += - scnprintf(buf + pos, bufsz - pos, - "%-32s current" - "acumulative delta max\n", - "Statistics_Rx - CCK:"); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "ina_cnt:", - le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, - delta_cck->ina_cnt, max_cck->ina_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "fina_cnt:", - le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, - delta_cck->fina_cnt, max_cck->fina_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "plcp_err:", - le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, - delta_cck->plcp_err, max_cck->plcp_err); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "crc32_err:", - le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, - delta_cck->crc32_err, max_cck->crc32_err); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "overrun_err:", - le32_to_cpu(cck->overrun_err), accum_cck->overrun_err, - delta_cck->overrun_err, max_cck->overrun_err); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "early_overrun_err:", - le32_to_cpu(cck->early_overrun_err), - accum_cck->early_overrun_err, - delta_cck->early_overrun_err, max_cck->early_overrun_err); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "crc32_good:", - le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, - delta_cck->crc32_good, max_cck->crc32_good); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "false_alarm_cnt:", - le32_to_cpu(cck->false_alarm_cnt), - accum_cck->false_alarm_cnt, delta_cck->false_alarm_cnt, - max_cck->false_alarm_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "fina_sync_err_cnt:", - le32_to_cpu(cck->fina_sync_err_cnt), - accum_cck->fina_sync_err_cnt, - delta_cck->fina_sync_err_cnt, max_cck->fina_sync_err_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "sfd_timeout:", - le32_to_cpu(cck->sfd_timeout), accum_cck->sfd_timeout, - delta_cck->sfd_timeout, max_cck->sfd_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "fina_timeout:", - le32_to_cpu(cck->fina_timeout), accum_cck->fina_timeout, - delta_cck->fina_timeout, max_cck->fina_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "unresponded_rts:", - le32_to_cpu(cck->unresponded_rts), - accum_cck->unresponded_rts, delta_cck->unresponded_rts, - max_cck->unresponded_rts); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", - "rxe_frame_lmt_ovrun:", - le32_to_cpu(cck->rxe_frame_limit_overrun), - accum_cck->rxe_frame_limit_overrun, - delta_cck->rxe_frame_limit_overrun, - max_cck->rxe_frame_limit_overrun); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "sent_ack_cnt:", - le32_to_cpu(cck->sent_ack_cnt), accum_cck->sent_ack_cnt, - delta_cck->sent_ack_cnt, max_cck->sent_ack_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "sent_cts_cnt:", - le32_to_cpu(cck->sent_cts_cnt), accum_cck->sent_cts_cnt, - delta_cck->sent_cts_cnt, max_cck->sent_cts_cnt); - - pos += - scnprintf(buf + pos, bufsz - pos, - "%-32s current" - "acumulative delta max\n", - "Statistics_Rx - GENERAL:"); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "bogus_cts:", - le32_to_cpu(general->bogus_cts), accum_general->bogus_cts, - delta_general->bogus_cts, max_general->bogus_cts); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "bogus_ack:", - le32_to_cpu(general->bogus_ack), accum_general->bogus_ack, - delta_general->bogus_ack, max_general->bogus_ack); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "non_bssid_frames:", - le32_to_cpu(general->non_bssid_frames), - accum_general->non_bssid_frames, - delta_general->non_bssid_frames, - max_general->non_bssid_frames); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "filtered_frames:", - le32_to_cpu(general->filtered_frames), - accum_general->filtered_frames, - delta_general->filtered_frames, - max_general->filtered_frames); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", - "non_channel_beacons:", - le32_to_cpu(general->non_channel_beacons), - accum_general->non_channel_beacons, - delta_general->non_channel_beacons, - max_general->non_channel_beacons); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il3945_ucode_tx_stats_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - int pos = 0; - char *buf; - int bufsz = (sizeof(struct iwl39_stats_tx) * 48) + 250; - ssize_t ret; - struct iwl39_stats_tx *tx, *accum_tx, *delta_tx, *max_tx; - - if (!il_is_alive(il)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - IL_ERR("Can not allocate Buffer\n"); - return -ENOMEM; - } - - /* - * The statistic information display here is based on - * the last stats notification from uCode - * might not reflect the current uCode activity - */ - tx = &il->_3945.stats.tx; - accum_tx = &il->_3945.accum_stats.tx; - delta_tx = &il->_3945.delta_stats.tx; - max_tx = &il->_3945.max_delta.tx; - pos += il3945_stats_flag(il, buf, bufsz); - pos += - scnprintf(buf + pos, bufsz - pos, - "%-32s current" - "acumulative delta max\n", - "Statistics_Tx:"); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "preamble:", - le32_to_cpu(tx->preamble_cnt), accum_tx->preamble_cnt, - delta_tx->preamble_cnt, max_tx->preamble_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "rx_detected_cnt:", - le32_to_cpu(tx->rx_detected_cnt), - accum_tx->rx_detected_cnt, delta_tx->rx_detected_cnt, - max_tx->rx_detected_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "bt_prio_defer_cnt:", - le32_to_cpu(tx->bt_prio_defer_cnt), - accum_tx->bt_prio_defer_cnt, delta_tx->bt_prio_defer_cnt, - max_tx->bt_prio_defer_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "bt_prio_kill_cnt:", - le32_to_cpu(tx->bt_prio_kill_cnt), - accum_tx->bt_prio_kill_cnt, delta_tx->bt_prio_kill_cnt, - max_tx->bt_prio_kill_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "few_bytes_cnt:", - le32_to_cpu(tx->few_bytes_cnt), accum_tx->few_bytes_cnt, - delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "cts_timeout:", - le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, - delta_tx->cts_timeout, max_tx->cts_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "ack_timeout:", - le32_to_cpu(tx->ack_timeout), accum_tx->ack_timeout, - delta_tx->ack_timeout, max_tx->ack_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "expected_ack_cnt:", - le32_to_cpu(tx->expected_ack_cnt), - accum_tx->expected_ack_cnt, delta_tx->expected_ack_cnt, - max_tx->expected_ack_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "actual_ack_cnt:", - le32_to_cpu(tx->actual_ack_cnt), accum_tx->actual_ack_cnt, - delta_tx->actual_ack_cnt, max_tx->actual_ack_cnt); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il3945_ucode_general_stats_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - int pos = 0; - char *buf; - int bufsz = sizeof(struct iwl39_stats_general) * 10 + 300; - ssize_t ret; - struct iwl39_stats_general *general, *accum_general; - struct iwl39_stats_general *delta_general, *max_general; - struct stats_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; - struct iwl39_stats_div *div, *accum_div, *delta_div, *max_div; - - if (!il_is_alive(il)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - IL_ERR("Can not allocate Buffer\n"); - return -ENOMEM; - } - - /* - * The statistic information display here is based on - * the last stats notification from uCode - * might not reflect the current uCode activity - */ - general = &il->_3945.stats.general; - dbg = &il->_3945.stats.general.dbg; - div = &il->_3945.stats.general.div; - accum_general = &il->_3945.accum_stats.general; - delta_general = &il->_3945.delta_stats.general; - max_general = &il->_3945.max_delta.general; - accum_dbg = &il->_3945.accum_stats.general.dbg; - delta_dbg = &il->_3945.delta_stats.general.dbg; - max_dbg = &il->_3945.max_delta.general.dbg; - accum_div = &il->_3945.accum_stats.general.div; - delta_div = &il->_3945.delta_stats.general.div; - max_div = &il->_3945.max_delta.general.div; - pos += il3945_stats_flag(il, buf, bufsz); - pos += - scnprintf(buf + pos, bufsz - pos, - "%-32s current" - "acumulative delta max\n", - "Statistics_General:"); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "burst_check:", - le32_to_cpu(dbg->burst_check), accum_dbg->burst_check, - delta_dbg->burst_check, max_dbg->burst_check); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "burst_count:", - le32_to_cpu(dbg->burst_count), accum_dbg->burst_count, - delta_dbg->burst_count, max_dbg->burst_count); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "sleep_time:", - le32_to_cpu(general->sleep_time), - accum_general->sleep_time, delta_general->sleep_time, - max_general->sleep_time); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "slots_out:", - le32_to_cpu(general->slots_out), accum_general->slots_out, - delta_general->slots_out, max_general->slots_out); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "slots_idle:", - le32_to_cpu(general->slots_idle), - accum_general->slots_idle, delta_general->slots_idle, - max_general->slots_idle); - pos += - scnprintf(buf + pos, bufsz - pos, "ttl_timestamp:\t\t\t%u\n", - le32_to_cpu(general->ttl_timestamp)); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "tx_on_a:", - le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, - delta_div->tx_on_a, max_div->tx_on_a); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "tx_on_b:", - le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, - delta_div->tx_on_b, max_div->tx_on_b); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "exec_time:", - le32_to_cpu(div->exec_time), accum_div->exec_time, - delta_div->exec_time, max_div->exec_time); - pos += - scnprintf(buf + pos, bufsz - pos, - " %-30s %10u %10u %10u %10u\n", "probe_time:", - le32_to_cpu(div->probe_time), accum_div->probe_time, - delta_div->probe_time, max_div->probe_time); - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -const struct il_debugfs_ops il3945_debugfs_ops = { - .rx_stats_read = il3945_ucode_rx_stats_read, - .tx_stats_read = il3945_ucode_tx_stats_read, - .general_stats_read = il3945_ucode_general_stats_read, -}; diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c deleted file mode 100644 index af1b3e6839fa..000000000000 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ /dev/null @@ -1,3959 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#define DRV_NAME "iwl3945" - -#include "commands.h" -#include "common.h" -#include "3945.h" -#include "iwl-spectrum.h" - -/* - * module name, copyright, version, etc. - */ - -#define DRV_DESCRIPTION \ -"Intel(R) PRO/Wireless 3945ABG/BG Network Connection driver for Linux" - -#ifdef CONFIG_IWLEGACY_DEBUG -#define VD "d" -#else -#define VD -#endif - -/* - * add "s" to indicate spectrum measurement included. - * we add it here to be consistent with previous releases in which - * this was configurable. - */ -#define DRV_VERSION IWLWIFI_VERSION VD "s" -#define DRV_COPYRIGHT "Copyright(c) 2003-2011 Intel Corporation" -#define DRV_AUTHOR "" - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); -MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); -MODULE_LICENSE("GPL"); - - /* module parameters */ -struct il_mod_params il3945_mod_params = { - .sw_crypto = 1, - .restart_fw = 1, - .disable_hw_scan = 1, - /* the rest are 0 by default */ -}; - -/** - * il3945_get_antenna_flags - Get antenna flags for RXON command - * @il: eeprom and antenna fields are used to determine antenna flags - * - * il->eeprom39 is used to determine if antenna AUX/MAIN are reversed - * il3945_mod_params.antenna specifies the antenna diversity mode: - * - * IL_ANTENNA_DIVERSITY - NIC selects best antenna by itself - * IL_ANTENNA_MAIN - Force MAIN antenna - * IL_ANTENNA_AUX - Force AUX antenna - */ -__le32 -il3945_get_antenna_flags(const struct il_priv *il) -{ - struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; - - switch (il3945_mod_params.antenna) { - case IL_ANTENNA_DIVERSITY: - return 0; - - case IL_ANTENNA_MAIN: - if (eeprom->antenna_switch_type) - return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK; - return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK; - - case IL_ANTENNA_AUX: - if (eeprom->antenna_switch_type) - return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_A_MSK; - return RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_B_MSK; - } - - /* bad antenna selector value */ - IL_ERR("Bad antenna selector value (0x%x)\n", - il3945_mod_params.antenna); - - return 0; /* "diversity" is default if error */ -} - -static int -il3945_set_ccmp_dynamic_key_info(struct il_priv *il, - struct ieee80211_key_conf *keyconf, u8 sta_id) -{ - unsigned long flags; - __le16 key_flags = 0; - int ret; - - key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK); - key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); - - if (sta_id == il->hw_params.bcast_id) - key_flags |= STA_KEY_MULTICAST_MSK; - - keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - keyconf->hw_key_idx = keyconf->keyidx; - key_flags &= ~STA_KEY_FLG_INVALID; - - spin_lock_irqsave(&il->sta_lock, flags); - il->stations[sta_id].keyinfo.cipher = keyconf->cipher; - il->stations[sta_id].keyinfo.keylen = keyconf->keylen; - memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); - - memcpy(il->stations[sta_id].sta.key.key, keyconf->key, keyconf->keylen); - - if ((il->stations[sta_id].sta.key. - key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) - il->stations[sta_id].sta.key.key_offset = - il_get_free_ucode_key_idx(il); - /* else, we are overriding an existing key => no need to allocated room - * in uCode. */ - - WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, - "no space for a new key"); - - il->stations[sta_id].sta.key.key_flags = key_flags; - il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; - il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - - D_INFO("hwcrypto: modify ucode station key info\n"); - - ret = il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC); - - spin_unlock_irqrestore(&il->sta_lock, flags); - - return ret; -} - -static int -il3945_set_tkip_dynamic_key_info(struct il_priv *il, - struct ieee80211_key_conf *keyconf, u8 sta_id) -{ - return -EOPNOTSUPP; -} - -static int -il3945_set_wep_dynamic_key_info(struct il_priv *il, - struct ieee80211_key_conf *keyconf, u8 sta_id) -{ - return -EOPNOTSUPP; -} - -static int -il3945_clear_sta_key_info(struct il_priv *il, u8 sta_id) -{ - unsigned long flags; - struct il_addsta_cmd sta_cmd; - - spin_lock_irqsave(&il->sta_lock, flags); - memset(&il->stations[sta_id].keyinfo, 0, sizeof(struct il_hw_key)); - memset(&il->stations[sta_id].sta.key, 0, sizeof(struct il4965_keyinfo)); - il->stations[sta_id].sta.key.key_flags = STA_KEY_FLG_NO_ENC; - il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; - il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - memcpy(&sta_cmd, &il->stations[sta_id].sta, - sizeof(struct il_addsta_cmd)); - spin_unlock_irqrestore(&il->sta_lock, flags); - - D_INFO("hwcrypto: clear ucode station key info\n"); - return il_send_add_sta(il, &sta_cmd, CMD_SYNC); -} - -static int -il3945_set_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, - u8 sta_id) -{ - int ret = 0; - - keyconf->hw_key_idx = HW_KEY_DYNAMIC; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - ret = il3945_set_ccmp_dynamic_key_info(il, keyconf, sta_id); - break; - case WLAN_CIPHER_SUITE_TKIP: - ret = il3945_set_tkip_dynamic_key_info(il, keyconf, sta_id); - break; - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - ret = il3945_set_wep_dynamic_key_info(il, keyconf, sta_id); - break; - default: - IL_ERR("Unknown alg: %s alg=%x\n", __func__, keyconf->cipher); - ret = -EINVAL; - } - - D_WEP("Set dynamic key: alg=%x len=%d idx=%d sta=%d ret=%d\n", - keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret); - - return ret; -} - -static int -il3945_remove_static_key(struct il_priv *il) -{ - int ret = -EOPNOTSUPP; - - return ret; -} - -static int -il3945_set_static_key(struct il_priv *il, struct ieee80211_key_conf *key) -{ - if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || - key->cipher == WLAN_CIPHER_SUITE_WEP104) - return -EOPNOTSUPP; - - IL_ERR("Static key invalid: cipher %x\n", key->cipher); - return -EINVAL; -} - -static void -il3945_clear_free_frames(struct il_priv *il) -{ - struct list_head *element; - - D_INFO("%d frames on pre-allocated heap on clear.\n", il->frames_count); - - while (!list_empty(&il->free_frames)) { - element = il->free_frames.next; - list_del(element); - kfree(list_entry(element, struct il3945_frame, list)); - il->frames_count--; - } - - if (il->frames_count) { - IL_WARN("%d frames still in use. Did we lose one?\n", - il->frames_count); - il->frames_count = 0; - } -} - -static struct il3945_frame * -il3945_get_free_frame(struct il_priv *il) -{ - struct il3945_frame *frame; - struct list_head *element; - if (list_empty(&il->free_frames)) { - frame = kzalloc(sizeof(*frame), GFP_KERNEL); - if (!frame) { - IL_ERR("Could not allocate frame!\n"); - return NULL; - } - - il->frames_count++; - return frame; - } - - element = il->free_frames.next; - list_del(element); - return list_entry(element, struct il3945_frame, list); -} - -static void -il3945_free_frame(struct il_priv *il, struct il3945_frame *frame) -{ - memset(frame, 0, sizeof(*frame)); - list_add(&frame->list, &il->free_frames); -} - -unsigned int -il3945_fill_beacon_frame(struct il_priv *il, struct ieee80211_hdr *hdr, - int left) -{ - - if (!il_is_associated(il) || !il->beacon_skb) - return 0; - - if (il->beacon_skb->len > left) - return 0; - - memcpy(hdr, il->beacon_skb->data, il->beacon_skb->len); - - return il->beacon_skb->len; -} - -static int -il3945_send_beacon_cmd(struct il_priv *il) -{ - struct il3945_frame *frame; - unsigned int frame_size; - int rc; - u8 rate; - - frame = il3945_get_free_frame(il); - - if (!frame) { - IL_ERR("Could not obtain free frame buffer for beacon " - "command.\n"); - return -ENOMEM; - } - - rate = il_get_lowest_plcp(il); - - frame_size = il3945_hw_get_beacon_cmd(il, frame, rate); - - rc = il_send_cmd_pdu(il, C_TX_BEACON, frame_size, &frame->u.cmd[0]); - - il3945_free_frame(il, frame); - - return rc; -} - -static void -il3945_unset_hw_params(struct il_priv *il) -{ - if (il->_3945.shared_virt) - dma_free_coherent(&il->pci_dev->dev, - sizeof(struct il3945_shared), - il->_3945.shared_virt, il->_3945.shared_phys); -} - -static void -il3945_build_tx_cmd_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info, - struct il_device_cmd *cmd, - struct sk_buff *skb_frag, int sta_id) -{ - struct il3945_tx_cmd *tx_cmd = (struct il3945_tx_cmd *)cmd->cmd.payload; - struct il_hw_key *keyinfo = &il->stations[sta_id].keyinfo; - - tx_cmd->sec_ctl = 0; - - switch (keyinfo->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - tx_cmd->sec_ctl = TX_CMD_SEC_CCM; - memcpy(tx_cmd->key, keyinfo->key, keyinfo->keylen); - D_TX("tx_cmd with AES hwcrypto\n"); - break; - - case WLAN_CIPHER_SUITE_TKIP: - break; - - case WLAN_CIPHER_SUITE_WEP104: - tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; - /* fall through */ - case WLAN_CIPHER_SUITE_WEP40: - tx_cmd->sec_ctl |= - TX_CMD_SEC_WEP | (info->control.hw_key-> - hw_key_idx & TX_CMD_SEC_MSK) << - TX_CMD_SEC_SHIFT; - - memcpy(&tx_cmd->key[3], keyinfo->key, keyinfo->keylen); - - D_TX("Configuring packet for WEP encryption " "with key %d\n", - info->control.hw_key->hw_key_idx); - break; - - default: - IL_ERR("Unknown encode cipher %x\n", keyinfo->cipher); - break; - } -} - -/* - * handle build C_TX command notification. - */ -static void -il3945_build_tx_cmd_basic(struct il_priv *il, struct il_device_cmd *cmd, - struct ieee80211_tx_info *info, - struct ieee80211_hdr *hdr, u8 std_id) -{ - struct il3945_tx_cmd *tx_cmd = (struct il3945_tx_cmd *)cmd->cmd.payload; - __le32 tx_flags = tx_cmd->tx_flags; - __le16 fc = hdr->frame_control; - - tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { - tx_flags |= TX_CMD_FLG_ACK_MSK; - if (ieee80211_is_mgmt(fc)) - tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - if (ieee80211_is_probe_resp(fc) && - !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) - tx_flags |= TX_CMD_FLG_TSF_MSK; - } else { - tx_flags &= (~TX_CMD_FLG_ACK_MSK); - tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - } - - tx_cmd->sta_id = std_id; - if (ieee80211_has_morefrags(fc)) - tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; - - if (ieee80211_is_data_qos(fc)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tx_cmd->tid_tspec = qc[0] & 0xf; - tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; - } else { - tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - } - - il_tx_cmd_protection(il, info, fc, &tx_flags); - - tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); - if (ieee80211_is_mgmt(fc)) { - if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) - tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); - else - tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); - } else { - tx_cmd->timeout.pm_frame_timeout = 0; - } - - tx_cmd->driver_txop = 0; - tx_cmd->tx_flags = tx_flags; - tx_cmd->next_frame_len = 0; -} - -/* - * start C_TX command process - */ -static int -il3945_tx_skb(struct il_priv *il, - struct ieee80211_sta *sta, - struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct il3945_tx_cmd *tx_cmd; - struct il_tx_queue *txq = NULL; - struct il_queue *q = NULL; - struct il_device_cmd *out_cmd; - struct il_cmd_meta *out_meta; - dma_addr_t phys_addr; - dma_addr_t txcmd_phys; - int txq_id = skb_get_queue_mapping(skb); - u16 len, idx, hdr_len; - u16 firstlen, secondlen; - u8 id; - u8 unicast; - u8 sta_id; - u8 tid = 0; - __le16 fc; - u8 wait_write_ptr = 0; - unsigned long flags; - - spin_lock_irqsave(&il->lock, flags); - if (il_is_rfkill(il)) { - D_DROP("Dropping - RF KILL\n"); - goto drop_unlock; - } - - if ((ieee80211_get_tx_rate(il->hw, info)->hw_value & 0xFF) == - IL_INVALID_RATE) { - IL_ERR("ERROR: No TX rate available.\n"); - goto drop_unlock; - } - - unicast = !is_multicast_ether_addr(hdr->addr1); - id = 0; - - fc = hdr->frame_control; - -#ifdef CONFIG_IWLEGACY_DEBUG - if (ieee80211_is_auth(fc)) - D_TX("Sending AUTH frame\n"); - else if (ieee80211_is_assoc_req(fc)) - D_TX("Sending ASSOC frame\n"); - else if (ieee80211_is_reassoc_req(fc)) - D_TX("Sending REASSOC frame\n"); -#endif - - spin_unlock_irqrestore(&il->lock, flags); - - hdr_len = ieee80211_hdrlen(fc); - - /* Find idx into station table for destination station */ - sta_id = il_sta_id_or_broadcast(il, sta); - if (sta_id == IL_INVALID_STATION) { - D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1); - goto drop; - } - - D_RATE("station Id %d\n", sta_id); - - if (ieee80211_is_data_qos(fc)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - if (unlikely(tid >= MAX_TID_COUNT)) - goto drop; - } - - /* Descriptor for chosen Tx queue */ - txq = &il->txq[txq_id]; - q = &txq->q; - - if ((il_queue_space(q) < q->high_mark)) - goto drop; - - spin_lock_irqsave(&il->lock, flags); - - idx = il_get_cmd_idx(q, q->write_ptr, 0); - - txq->skbs[q->write_ptr] = skb; - - /* Init first empty entry in queue's array of Tx/cmd buffers */ - out_cmd = txq->cmd[idx]; - out_meta = &txq->meta[idx]; - tx_cmd = (struct il3945_tx_cmd *)out_cmd->cmd.payload; - memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); - memset(tx_cmd, 0, sizeof(*tx_cmd)); - - /* - * Set up the Tx-command (not MAC!) header. - * Store the chosen Tx queue and TFD idx within the sequence field; - * after Tx, uCode's Tx response will return this value so driver can - * locate the frame within the tx queue and do post-tx processing. - */ - out_cmd->hdr.cmd = C_TX; - out_cmd->hdr.sequence = - cpu_to_le16((u16) - (QUEUE_TO_SEQ(txq_id) | IDX_TO_SEQ(q->write_ptr))); - - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdr_len); - - if (info->control.hw_key) - il3945_build_tx_cmd_hwcrypto(il, info, out_cmd, skb, sta_id); - - /* TODO need this for burst mode later on */ - il3945_build_tx_cmd_basic(il, out_cmd, info, hdr, sta_id); - - il3945_hw_build_tx_cmd_rate(il, out_cmd, info, hdr, sta_id); - - /* Total # bytes to be transmitted */ - tx_cmd->len = cpu_to_le16((u16) skb->len); - - tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_A_MSK; - tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_B_MSK; - - /* - * Use the first empty entry in this queue's command buffer array - * to contain the Tx command and MAC header concatenated together - * (payload data will be in another buffer). - * Size of this varies, due to varying MAC header length. - * If end is not dword aligned, we'll have 2 extra bytes at the end - * of the MAC header (device reads on dword boundaries). - * We'll tell device about this padding later. - */ - len = - sizeof(struct il3945_tx_cmd) + sizeof(struct il_cmd_header) + - hdr_len; - firstlen = (len + 3) & ~3; - - /* Physical address of this Tx command's header (not MAC header!), - * within command buffer array. */ - txcmd_phys = - pci_map_single(il->pci_dev, &out_cmd->hdr, firstlen, - PCI_DMA_TODEVICE); - if (unlikely(pci_dma_mapping_error(il->pci_dev, txcmd_phys))) - goto drop_unlock; - - /* Set up TFD's 2nd entry to point directly to remainder of skb, - * if any (802.11 null frames have no payload). */ - secondlen = skb->len - hdr_len; - if (secondlen > 0) { - phys_addr = - pci_map_single(il->pci_dev, skb->data + hdr_len, secondlen, - PCI_DMA_TODEVICE); - if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr))) - goto drop_unlock; - } - - /* Add buffer containing Tx command and MAC(!) header to TFD's - * first entry */ - il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, firstlen, 1, 0); - dma_unmap_addr_set(out_meta, mapping, txcmd_phys); - dma_unmap_len_set(out_meta, len, firstlen); - if (secondlen > 0) - il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, secondlen, 0, - U32_PAD(secondlen)); - - if (!ieee80211_has_morefrags(hdr->frame_control)) { - txq->need_update = 1; - } else { - wait_write_ptr = 1; - txq->need_update = 0; - } - - il_update_stats(il, true, fc, skb->len); - - D_TX("sequence nr = 0X%x\n", le16_to_cpu(out_cmd->hdr.sequence)); - D_TX("tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); - il_print_hex_dump(il, IL_DL_TX, tx_cmd, sizeof(*tx_cmd)); - il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd->hdr, - ieee80211_hdrlen(fc)); - - /* Tell device the write idx *just past* this latest filled TFD */ - q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); - il_txq_update_write_ptr(il, txq); - spin_unlock_irqrestore(&il->lock, flags); - - if (il_queue_space(q) < q->high_mark && il->mac80211_registered) { - if (wait_write_ptr) { - spin_lock_irqsave(&il->lock, flags); - txq->need_update = 1; - il_txq_update_write_ptr(il, txq); - spin_unlock_irqrestore(&il->lock, flags); - } - - il_stop_queue(il, txq); - } - - return 0; - -drop_unlock: - spin_unlock_irqrestore(&il->lock, flags); -drop: - return -1; -} - -static int -il3945_get_measurement(struct il_priv *il, - struct ieee80211_measurement_params *params, u8 type) -{ - struct il_spectrum_cmd spectrum; - struct il_rx_pkt *pkt; - struct il_host_cmd cmd = { - .id = C_SPECTRUM_MEASUREMENT, - .data = (void *)&spectrum, - .flags = CMD_WANT_SKB, - }; - u32 add_time = le64_to_cpu(params->start_time); - int rc; - int spectrum_resp_status; - int duration = le16_to_cpu(params->duration); - - if (il_is_associated(il)) - add_time = - il_usecs_to_beacons(il, - le64_to_cpu(params->start_time) - - il->_3945.last_tsf, - le16_to_cpu(il->timing.beacon_interval)); - - memset(&spectrum, 0, sizeof(spectrum)); - - spectrum.channel_count = cpu_to_le16(1); - spectrum.flags = - RXON_FLG_TSF2HOST_MSK | RXON_FLG_ANT_A_MSK | RXON_FLG_DIS_DIV_MSK; - spectrum.filter_flags = MEASUREMENT_FILTER_FLAG; - cmd.len = sizeof(spectrum); - spectrum.len = cpu_to_le16(cmd.len - sizeof(spectrum.len)); - - if (il_is_associated(il)) - spectrum.start_time = - il_add_beacon_time(il, il->_3945.last_beacon_time, add_time, - le16_to_cpu(il->timing.beacon_interval)); - else - spectrum.start_time = 0; - - spectrum.channels[0].duration = cpu_to_le32(duration * TIME_UNIT); - spectrum.channels[0].channel = params->channel; - spectrum.channels[0].type = type; - if (il->active.flags & RXON_FLG_BAND_24G_MSK) - spectrum.flags |= - RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK | - RXON_FLG_TGG_PROTECT_MSK; - - rc = il_send_cmd_sync(il, &cmd); - if (rc) - return rc; - - pkt = (struct il_rx_pkt *)cmd.reply_page; - if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { - IL_ERR("Bad return from N_RX_ON_ASSOC command\n"); - rc = -EIO; - } - - spectrum_resp_status = le16_to_cpu(pkt->u.spectrum.status); - switch (spectrum_resp_status) { - case 0: /* Command will be handled */ - if (pkt->u.spectrum.id != 0xff) { - D_INFO("Replaced existing measurement: %d\n", - pkt->u.spectrum.id); - il->measurement_status &= ~MEASUREMENT_READY; - } - il->measurement_status |= MEASUREMENT_ACTIVE; - rc = 0; - break; - - case 1: /* Command will not be handled */ - rc = -EAGAIN; - break; - } - - il_free_pages(il, cmd.reply_page); - - return rc; -} - -static void -il3945_hdl_alive(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_alive_resp *palive; - struct delayed_work *pwork; - - palive = &pkt->u.alive_frame; - - D_INFO("Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n", - palive->is_valid, palive->ver_type, palive->ver_subtype); - - if (palive->ver_subtype == INITIALIZE_SUBTYPE) { - D_INFO("Initialization Alive received.\n"); - memcpy(&il->card_alive_init, &pkt->u.alive_frame, - sizeof(struct il_alive_resp)); - pwork = &il->init_alive_start; - } else { - D_INFO("Runtime Alive received.\n"); - memcpy(&il->card_alive, &pkt->u.alive_frame, - sizeof(struct il_alive_resp)); - pwork = &il->alive_start; - il3945_disable_events(il); - } - - /* We delay the ALIVE response by 5ms to - * give the HW RF Kill time to activate... */ - if (palive->is_valid == UCODE_VALID_OK) - queue_delayed_work(il->workqueue, pwork, msecs_to_jiffies(5)); - else - IL_WARN("uCode did not respond OK.\n"); -} - -static void -il3945_hdl_add_sta(struct il_priv *il, struct il_rx_buf *rxb) -{ -#ifdef CONFIG_IWLEGACY_DEBUG - struct il_rx_pkt *pkt = rxb_addr(rxb); -#endif - - D_RX("Received C_ADD_STA: 0x%02X\n", pkt->u.status); -} - -static void -il3945_hdl_beacon(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il3945_beacon_notif *beacon = &(pkt->u.beacon_status); -#ifdef CONFIG_IWLEGACY_DEBUG - u8 rate = beacon->beacon_notify_hdr.rate; - - D_RX("beacon status %x retries %d iss %d " "tsf %d %d rate %d\n", - le32_to_cpu(beacon->beacon_notify_hdr.status) & TX_STATUS_MSK, - beacon->beacon_notify_hdr.failure_frame, - le32_to_cpu(beacon->ibss_mgr_status), - le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate); -#endif - - il->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); - -} - -/* Handle notification from uCode that card's power state is changing - * due to software, hardware, or critical temperature RFKILL */ -static void -il3945_hdl_card_state(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); - unsigned long status = il->status; - - IL_WARN("Card state received: HW:%s SW:%s\n", - (flags & HW_CARD_DISABLED) ? "Kill" : "On", - (flags & SW_CARD_DISABLED) ? "Kill" : "On"); - - _il_wr(il, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - if (flags & HW_CARD_DISABLED) - set_bit(S_RFKILL, &il->status); - else - clear_bit(S_RFKILL, &il->status); - - il_scan_cancel(il); - - if ((test_bit(S_RFKILL, &status) != - test_bit(S_RFKILL, &il->status))) - wiphy_rfkill_set_hw_state(il->hw->wiphy, - test_bit(S_RFKILL, &il->status)); - else - wake_up(&il->wait_command_queue); -} - -/** - * il3945_setup_handlers - Initialize Rx handler callbacks - * - * Setup the RX handlers for each of the reply types sent from the uCode - * to the host. - * - * This function chains into the hardware specific files for them to setup - * any hardware specific handlers as well. - */ -static void -il3945_setup_handlers(struct il_priv *il) -{ - il->handlers[N_ALIVE] = il3945_hdl_alive; - il->handlers[C_ADD_STA] = il3945_hdl_add_sta; - il->handlers[N_ERROR] = il_hdl_error; - il->handlers[N_CHANNEL_SWITCH] = il_hdl_csa; - il->handlers[N_SPECTRUM_MEASUREMENT] = il_hdl_spectrum_measurement; - il->handlers[N_PM_SLEEP] = il_hdl_pm_sleep; - il->handlers[N_PM_DEBUG_STATS] = il_hdl_pm_debug_stats; - il->handlers[N_BEACON] = il3945_hdl_beacon; - - /* - * The same handler is used for both the REPLY to a discrete - * stats request from the host as well as for the periodic - * stats notifications (after received beacons) from the uCode. - */ - il->handlers[C_STATS] = il3945_hdl_c_stats; - il->handlers[N_STATS] = il3945_hdl_stats; - - il_setup_rx_scan_handlers(il); - il->handlers[N_CARD_STATE] = il3945_hdl_card_state; - - /* Set up hardware specific Rx handlers */ - il3945_hw_handler_setup(il); -} - -/************************** RX-FUNCTIONS ****************************/ -/* - * Rx theory of operation - * - * The host allocates 32 DMA target addresses and passes the host address - * to the firmware at register IL_RFDS_TBL_LOWER + N * RFD_SIZE where N is - * 0 to 31 - * - * Rx Queue Indexes - * The host/firmware share two idx registers for managing the Rx buffers. - * - * The READ idx maps to the first position that the firmware may be writing - * to -- the driver can read up to (but not including) this position and get - * good data. - * The READ idx is managed by the firmware once the card is enabled. - * - * The WRITE idx maps to the last position the driver has read from -- the - * position preceding WRITE is the last slot the firmware can place a packet. - * - * The queue is empty (no good data) if WRITE = READ - 1, and is full if - * WRITE = READ. - * - * During initialization, the host sets up the READ queue position to the first - * IDX position, and WRITE to the last (READ - 1 wrapped) - * - * When the firmware places a packet in a buffer, it will advance the READ idx - * and fire the RX interrupt. The driver can then query the READ idx and - * process as many packets as possible, moving the WRITE idx forward as it - * resets the Rx queue buffers with new memory. - * - * The management in the driver is as follows: - * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When - * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled - * to replenish the iwl->rxq->rx_free. - * + In il3945_rx_replenish (scheduled) if 'processed' != 'read' then the - * iwl->rxq is replenished and the READ IDX is updated (updating the - * 'processed' and 'read' driver idxes as well) - * + A received packet is processed and handed to the kernel network stack, - * detached from the iwl->rxq. The driver 'processed' idx is updated. - * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free - * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ - * IDX is not incremented and iwl->status(RX_STALLED) is set. If there - * were enough free buffers and RX_STALLED is set it is cleared. - * - * - * Driver sequence: - * - * il3945_rx_replenish() Replenishes rx_free list from rx_used, and calls - * il3945_rx_queue_restock - * il3945_rx_queue_restock() Moves available buffers from rx_free into Rx - * queue, updates firmware pointers, and updates - * the WRITE idx. If insufficient rx_free buffers - * are available, schedules il3945_rx_replenish - * - * -- enable interrupts -- - * ISR - il3945_rx() Detach il_rx_bufs from pool up to the - * READ IDX, detaching the SKB from the pool. - * Moves the packet buffer from queue to rx_used. - * Calls il3945_rx_queue_restock to refill any empty - * slots. - * ... - * - */ - -/** - * il3945_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr - */ -static inline __le32 -il3945_dma_addr2rbd_ptr(struct il_priv *il, dma_addr_t dma_addr) -{ - return cpu_to_le32((u32) dma_addr); -} - -/** - * il3945_rx_queue_restock - refill RX queue from pre-allocated pool - * - * If there are slots in the RX queue that need to be restocked, - * and we have free pre-allocated buffers, fill the ranks as much - * as we can, pulling from rx_free. - * - * This moves the 'write' idx forward to catch up with 'processed', and - * also updates the memory address in the firmware to reference the new - * target buffer. - */ -static void -il3945_rx_queue_restock(struct il_priv *il) -{ - struct il_rx_queue *rxq = &il->rxq; - struct list_head *element; - struct il_rx_buf *rxb; - unsigned long flags; - int write; - - spin_lock_irqsave(&rxq->lock, flags); - write = rxq->write & ~0x7; - while (il_rx_queue_space(rxq) > 0 && rxq->free_count) { - /* Get next free Rx buffer, remove from free list */ - element = rxq->rx_free.next; - rxb = list_entry(element, struct il_rx_buf, list); - list_del(element); - - /* Point to Rx buffer via next RBD in circular buffer */ - rxq->bd[rxq->write] = - il3945_dma_addr2rbd_ptr(il, rxb->page_dma); - rxq->queue[rxq->write] = rxb; - rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; - rxq->free_count--; - } - spin_unlock_irqrestore(&rxq->lock, flags); - /* If the pre-allocated buffer pool is dropping low, schedule to - * refill it */ - if (rxq->free_count <= RX_LOW_WATERMARK) - queue_work(il->workqueue, &il->rx_replenish); - - /* If we've added more space for the firmware to place data, tell it. - * Increment device's write pointer in multiples of 8. */ - if (rxq->write_actual != (rxq->write & ~0x7) || - abs(rxq->write - rxq->read) > 7) { - spin_lock_irqsave(&rxq->lock, flags); - rxq->need_update = 1; - spin_unlock_irqrestore(&rxq->lock, flags); - il_rx_queue_update_write_ptr(il, rxq); - } -} - -/** - * il3945_rx_replenish - Move all used packet from rx_used to rx_free - * - * When moving to rx_free an SKB is allocated for the slot. - * - * Also restock the Rx queue via il3945_rx_queue_restock. - * This is called as a scheduled work item (except for during initialization) - */ -static void -il3945_rx_allocate(struct il_priv *il, gfp_t priority) -{ - struct il_rx_queue *rxq = &il->rxq; - struct list_head *element; - struct il_rx_buf *rxb; - struct page *page; - dma_addr_t page_dma; - unsigned long flags; - gfp_t gfp_mask = priority; - - while (1) { - spin_lock_irqsave(&rxq->lock, flags); - if (list_empty(&rxq->rx_used)) { - spin_unlock_irqrestore(&rxq->lock, flags); - return; - } - spin_unlock_irqrestore(&rxq->lock, flags); - - if (rxq->free_count > RX_LOW_WATERMARK) - gfp_mask |= __GFP_NOWARN; - - if (il->hw_params.rx_page_order > 0) - gfp_mask |= __GFP_COMP; - - /* Alloc a new receive buffer */ - page = alloc_pages(gfp_mask, il->hw_params.rx_page_order); - if (!page) { - if (net_ratelimit()) - D_INFO("Failed to allocate SKB buffer.\n"); - if (rxq->free_count <= RX_LOW_WATERMARK && - net_ratelimit()) - IL_ERR("Failed to allocate SKB buffer with %0x." - "Only %u free buffers remaining.\n", - priority, rxq->free_count); - /* We don't reschedule replenish work here -- we will - * call the restock method and if it still needs - * more buffers it will schedule replenish */ - break; - } - - /* Get physical address of RB/SKB */ - page_dma = - pci_map_page(il->pci_dev, page, 0, - PAGE_SIZE << il->hw_params.rx_page_order, - PCI_DMA_FROMDEVICE); - - if (unlikely(pci_dma_mapping_error(il->pci_dev, page_dma))) { - __free_pages(page, il->hw_params.rx_page_order); - break; - } - - spin_lock_irqsave(&rxq->lock, flags); - - if (list_empty(&rxq->rx_used)) { - spin_unlock_irqrestore(&rxq->lock, flags); - pci_unmap_page(il->pci_dev, page_dma, - PAGE_SIZE << il->hw_params.rx_page_order, - PCI_DMA_FROMDEVICE); - __free_pages(page, il->hw_params.rx_page_order); - return; - } - - element = rxq->rx_used.next; - rxb = list_entry(element, struct il_rx_buf, list); - list_del(element); - - rxb->page = page; - rxb->page_dma = page_dma; - list_add_tail(&rxb->list, &rxq->rx_free); - rxq->free_count++; - il->alloc_rxb_page++; - - spin_unlock_irqrestore(&rxq->lock, flags); - } -} - -void -il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq) -{ - unsigned long flags; - int i; - spin_lock_irqsave(&rxq->lock, flags); - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - /* Fill the rx_used queue with _all_ of the Rx buffers */ - for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { - /* In the reset function, these buffers may have been allocated - * to an SKB, so we need to unmap and free potential storage */ - if (rxq->pool[i].page != NULL) { - pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, - PAGE_SIZE << il->hw_params.rx_page_order, - PCI_DMA_FROMDEVICE); - __il_free_pages(il, rxq->pool[i].page); - rxq->pool[i].page = NULL; - } - list_add_tail(&rxq->pool[i].list, &rxq->rx_used); - } - - /* Set us so that we have processed and used all buffers, but have - * not restocked the Rx queue with fresh buffers */ - rxq->read = rxq->write = 0; - rxq->write_actual = 0; - rxq->free_count = 0; - spin_unlock_irqrestore(&rxq->lock, flags); -} - -void -il3945_rx_replenish(void *data) -{ - struct il_priv *il = data; - unsigned long flags; - - il3945_rx_allocate(il, GFP_KERNEL); - - spin_lock_irqsave(&il->lock, flags); - il3945_rx_queue_restock(il); - spin_unlock_irqrestore(&il->lock, flags); -} - -static void -il3945_rx_replenish_now(struct il_priv *il) -{ - il3945_rx_allocate(il, GFP_ATOMIC); - - il3945_rx_queue_restock(il); -} - -/* Assumes that the skb field of the buffers in 'pool' is kept accurate. - * If an SKB has been detached, the POOL needs to have its SKB set to NULL - * This free routine walks the list of POOL entries and if SKB is set to - * non NULL it is unmapped and freed - */ -static void -il3945_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq) -{ - int i; - for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { - if (rxq->pool[i].page != NULL) { - pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, - PAGE_SIZE << il->hw_params.rx_page_order, - PCI_DMA_FROMDEVICE); - __il_free_pages(il, rxq->pool[i].page); - rxq->pool[i].page = NULL; - } - } - - dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, - rxq->bd_dma); - dma_free_coherent(&il->pci_dev->dev, sizeof(struct il_rb_status), - rxq->rb_stts, rxq->rb_stts_dma); - rxq->bd = NULL; - rxq->rb_stts = NULL; -} - -/* Convert linear signal-to-noise ratio into dB */ -static u8 ratio2dB[100] = { -/* 0 1 2 3 4 5 6 7 8 9 */ - 0, 0, 6, 10, 12, 14, 16, 17, 18, 19, /* 00 - 09 */ - 20, 21, 22, 22, 23, 23, 24, 25, 26, 26, /* 10 - 19 */ - 26, 26, 26, 27, 27, 28, 28, 28, 29, 29, /* 20 - 29 */ - 29, 30, 30, 30, 31, 31, 31, 31, 32, 32, /* 30 - 39 */ - 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, /* 40 - 49 */ - 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, /* 50 - 59 */ - 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, /* 60 - 69 */ - 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, /* 70 - 79 */ - 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, /* 80 - 89 */ - 39, 39, 39, 39, 39, 40, 40, 40, 40, 40 /* 90 - 99 */ -}; - -/* Calculates a relative dB value from a ratio of linear - * (i.e. not dB) signal levels. - * Conversion assumes that levels are voltages (20*log), not powers (10*log). */ -int -il3945_calc_db_from_ratio(int sig_ratio) -{ - /* 1000:1 or higher just report as 60 dB */ - if (sig_ratio >= 1000) - return 60; - - /* 100:1 or higher, divide by 10 and use table, - * add 20 dB to make up for divide by 10 */ - if (sig_ratio >= 100) - return 20 + (int)ratio2dB[sig_ratio / 10]; - - /* We shouldn't see this */ - if (sig_ratio < 1) - return 0; - - /* Use table for ratios 1:1 - 99:1 */ - return (int)ratio2dB[sig_ratio]; -} - -/** - * il3945_rx_handle - Main entry function for receiving responses from uCode - * - * Uses the il->handlers callback function array to invoke - * the appropriate handlers, including command responses, - * frame-received notifications, and other notifications. - */ -static void -il3945_rx_handle(struct il_priv *il) -{ - struct il_rx_buf *rxb; - struct il_rx_pkt *pkt; - struct il_rx_queue *rxq = &il->rxq; - u32 r, i; - int reclaim; - unsigned long flags; - u8 fill_rx = 0; - u32 count = 8; - int total_empty = 0; - - /* uCode's read idx (stored in shared DRAM) indicates the last Rx - * buffer that the driver may process (last buffer filled by ucode). */ - r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF; - i = rxq->read; - - /* calculate total frames need to be restock after handling RX */ - total_empty = r - rxq->write_actual; - if (total_empty < 0) - total_empty += RX_QUEUE_SIZE; - - if (total_empty > (RX_QUEUE_SIZE / 2)) - fill_rx = 1; - /* Rx interrupt, but nothing sent from uCode */ - if (i == r) - D_RX("r = %d, i = %d\n", r, i); - - while (i != r) { - int len; - - rxb = rxq->queue[i]; - - /* If an RXB doesn't have a Rx queue slot associated with it, - * then a bug has been introduced in the queue refilling - * routines -- catch it here */ - BUG_ON(rxb == NULL); - - rxq->queue[i] = NULL; - - pci_unmap_page(il->pci_dev, rxb->page_dma, - PAGE_SIZE << il->hw_params.rx_page_order, - PCI_DMA_FROMDEVICE); - pkt = rxb_addr(rxb); - - len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; - len += sizeof(u32); /* account for status word */ - - reclaim = il_need_reclaim(il, pkt); - - /* Based on type of command response or notification, - * handle those that need handling via function in - * handlers table. See il3945_setup_handlers() */ - if (il->handlers[pkt->hdr.cmd]) { - D_RX("r = %d, i = %d, %s, 0x%02x\n", r, i, - il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); - il->isr_stats.handlers[pkt->hdr.cmd]++; - il->handlers[pkt->hdr.cmd] (il, rxb); - } else { - /* No handling needed */ - D_RX("r %d i %d No handler needed for %s, 0x%02x\n", r, - i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); - } - - /* - * XXX: After here, we should always check rxb->page - * against NULL before touching it or its virtual - * memory (pkt). Because some handler might have - * already taken or freed the pages. - */ - - if (reclaim) { - /* Invoke any callbacks, transfer the buffer to caller, - * and fire off the (possibly) blocking il_send_cmd() - * as we reclaim the driver command queue */ - if (rxb->page) - il_tx_cmd_complete(il, rxb); - else - IL_WARN("Claim null rxb?\n"); - } - - /* Reuse the page if possible. For notification packets and - * SKBs that fail to Rx correctly, add them back into the - * rx_free list for reuse later. */ - spin_lock_irqsave(&rxq->lock, flags); - if (rxb->page != NULL) { - rxb->page_dma = - pci_map_page(il->pci_dev, rxb->page, 0, - PAGE_SIZE << il->hw_params. - rx_page_order, PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(il->pci_dev, - rxb->page_dma))) { - __il_free_pages(il, rxb->page); - rxb->page = NULL; - list_add_tail(&rxb->list, &rxq->rx_used); - } else { - list_add_tail(&rxb->list, &rxq->rx_free); - rxq->free_count++; - } - } else - list_add_tail(&rxb->list, &rxq->rx_used); - - spin_unlock_irqrestore(&rxq->lock, flags); - - i = (i + 1) & RX_QUEUE_MASK; - /* If there are a lot of unused frames, - * restock the Rx queue so ucode won't assert. */ - if (fill_rx) { - count++; - if (count >= 8) { - rxq->read = i; - il3945_rx_replenish_now(il); - count = 0; - } - } - } - - /* Backtrack one entry */ - rxq->read = i; - if (fill_rx) - il3945_rx_replenish_now(il); - else - il3945_rx_queue_restock(il); -} - -/* call this function to flush any scheduled tasklet */ -static inline void -il3945_synchronize_irq(struct il_priv *il) -{ - /* wait to make sure we flush pending tasklet */ - synchronize_irq(il->pci_dev->irq); - tasklet_kill(&il->irq_tasklet); -} - -static const char * -il3945_desc_lookup(int i) -{ - switch (i) { - case 1: - return "FAIL"; - case 2: - return "BAD_PARAM"; - case 3: - return "BAD_CHECKSUM"; - case 4: - return "NMI_INTERRUPT"; - case 5: - return "SYSASSERT"; - case 6: - return "FATAL_ERROR"; - } - - return "UNKNOWN"; -} - -#define ERROR_START_OFFSET (1 * sizeof(u32)) -#define ERROR_ELEM_SIZE (7 * sizeof(u32)) - -void -il3945_dump_nic_error_log(struct il_priv *il) -{ - u32 i; - u32 desc, time, count, base, data1; - u32 blink1, blink2, ilink1, ilink2; - - base = le32_to_cpu(il->card_alive.error_event_table_ptr); - - if (!il3945_hw_valid_rtc_data_addr(base)) { - IL_ERR("Not valid error log pointer 0x%08X\n", base); - return; - } - - count = il_read_targ_mem(il, base); - - if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { - IL_ERR("Start IWL Error Log Dump:\n"); - IL_ERR("Status: 0x%08lX, count: %d\n", il->status, count); - } - - IL_ERR("Desc Time asrtPC blink2 " - "ilink1 nmiPC Line\n"); - for (i = ERROR_START_OFFSET; - i < (count * ERROR_ELEM_SIZE) + ERROR_START_OFFSET; - i += ERROR_ELEM_SIZE) { - desc = il_read_targ_mem(il, base + i); - time = il_read_targ_mem(il, base + i + 1 * sizeof(u32)); - blink1 = il_read_targ_mem(il, base + i + 2 * sizeof(u32)); - blink2 = il_read_targ_mem(il, base + i + 3 * sizeof(u32)); - ilink1 = il_read_targ_mem(il, base + i + 4 * sizeof(u32)); - ilink2 = il_read_targ_mem(il, base + i + 5 * sizeof(u32)); - data1 = il_read_targ_mem(il, base + i + 6 * sizeof(u32)); - - IL_ERR("%-13s (0x%X) %010u 0x%05X 0x%05X 0x%05X 0x%05X %u\n\n", - il3945_desc_lookup(desc), desc, time, blink1, blink2, - ilink1, ilink2, data1); - } -} - -static void -il3945_irq_tasklet(struct il_priv *il) -{ - u32 inta, handled = 0; - u32 inta_fh; - unsigned long flags; -#ifdef CONFIG_IWLEGACY_DEBUG - u32 inta_mask; -#endif - - spin_lock_irqsave(&il->lock, flags); - - /* Ack/clear/reset pending uCode interrupts. - * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, - * and will clear only when CSR_FH_INT_STATUS gets cleared. */ - inta = _il_rd(il, CSR_INT); - _il_wr(il, CSR_INT, inta); - - /* Ack/clear/reset pending flow-handler (DMA) interrupts. - * Any new interrupts that happen after this, either while we're - * in this tasklet, or later, will show up in next ISR/tasklet. */ - inta_fh = _il_rd(il, CSR_FH_INT_STATUS); - _il_wr(il, CSR_FH_INT_STATUS, inta_fh); - -#ifdef CONFIG_IWLEGACY_DEBUG - if (il_get_debug_level(il) & IL_DL_ISR) { - /* just for debug */ - inta_mask = _il_rd(il, CSR_INT_MASK); - D_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, - inta_mask, inta_fh); - } -#endif - - spin_unlock_irqrestore(&il->lock, flags); - - /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not - * atomic, make sure that inta covers all the interrupts that - * we've discovered, even if FH interrupt came in just after - * reading CSR_INT. */ - if (inta_fh & CSR39_FH_INT_RX_MASK) - inta |= CSR_INT_BIT_FH_RX; - if (inta_fh & CSR39_FH_INT_TX_MASK) - inta |= CSR_INT_BIT_FH_TX; - - /* Now service all interrupt bits discovered above. */ - if (inta & CSR_INT_BIT_HW_ERR) { - IL_ERR("Hardware error detected. Restarting.\n"); - - /* Tell the device to stop sending interrupts */ - il_disable_interrupts(il); - - il->isr_stats.hw++; - il_irq_handle_error(il); - - handled |= CSR_INT_BIT_HW_ERR; - - return; - } -#ifdef CONFIG_IWLEGACY_DEBUG - if (il_get_debug_level(il) & (IL_DL_ISR)) { - /* NIC fires this, but we don't use it, redundant with WAKEUP */ - if (inta & CSR_INT_BIT_SCD) { - D_ISR("Scheduler finished to transmit " - "the frame/frames.\n"); - il->isr_stats.sch++; - } - - /* Alive notification via Rx interrupt will do the real work */ - if (inta & CSR_INT_BIT_ALIVE) { - D_ISR("Alive interrupt\n"); - il->isr_stats.alive++; - } - } -#endif - /* Safely ignore these bits for debug checks below */ - inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); - - /* Error detected by uCode */ - if (inta & CSR_INT_BIT_SW_ERR) { - IL_ERR("Microcode SW error detected. " "Restarting 0x%X.\n", - inta); - il->isr_stats.sw++; - il_irq_handle_error(il); - handled |= CSR_INT_BIT_SW_ERR; - } - - /* uCode wakes up after power-down sleep */ - if (inta & CSR_INT_BIT_WAKEUP) { - D_ISR("Wakeup interrupt\n"); - il_rx_queue_update_write_ptr(il, &il->rxq); - - spin_lock_irqsave(&il->lock, flags); - il_txq_update_write_ptr(il, &il->txq[0]); - il_txq_update_write_ptr(il, &il->txq[1]); - il_txq_update_write_ptr(il, &il->txq[2]); - il_txq_update_write_ptr(il, &il->txq[3]); - il_txq_update_write_ptr(il, &il->txq[4]); - spin_unlock_irqrestore(&il->lock, flags); - - il->isr_stats.wakeup++; - handled |= CSR_INT_BIT_WAKEUP; - } - - /* All uCode command responses, including Tx command responses, - * Rx "responses" (frame-received notification), and other - * notifications from uCode come through here*/ - if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { - il3945_rx_handle(il); - il->isr_stats.rx++; - handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); - } - - if (inta & CSR_INT_BIT_FH_TX) { - D_ISR("Tx interrupt\n"); - il->isr_stats.tx++; - - _il_wr(il, CSR_FH_INT_STATUS, (1 << 6)); - il_wr(il, FH39_TCSR_CREDIT(FH39_SRVC_CHNL), 0x0); - handled |= CSR_INT_BIT_FH_TX; - } - - if (inta & ~handled) { - IL_ERR("Unhandled INTA bits 0x%08x\n", inta & ~handled); - il->isr_stats.unhandled++; - } - - if (inta & ~il->inta_mask) { - IL_WARN("Disabled INTA bits 0x%08x were pending\n", - inta & ~il->inta_mask); - IL_WARN(" with inta_fh = 0x%08x\n", inta_fh); - } - - /* Re-enable all interrupts */ - /* only Re-enable if disabled by irq */ - if (test_bit(S_INT_ENABLED, &il->status)) - il_enable_interrupts(il); - -#ifdef CONFIG_IWLEGACY_DEBUG - if (il_get_debug_level(il) & (IL_DL_ISR)) { - inta = _il_rd(il, CSR_INT); - inta_mask = _il_rd(il, CSR_INT_MASK); - inta_fh = _il_rd(il, CSR_FH_INT_STATUS); - D_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, " - "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); - } -#endif -} - -static int -il3945_get_channels_for_scan(struct il_priv *il, enum ieee80211_band band, - u8 is_active, u8 n_probes, - struct il3945_scan_channel *scan_ch, - struct ieee80211_vif *vif) -{ - struct ieee80211_channel *chan; - const struct ieee80211_supported_band *sband; - const struct il_channel_info *ch_info; - u16 passive_dwell = 0; - u16 active_dwell = 0; - int added, i; - - sband = il_get_hw_mode(il, band); - if (!sband) - return 0; - - active_dwell = il_get_active_dwell_time(il, band, n_probes); - passive_dwell = il_get_passive_dwell_time(il, band, vif); - - if (passive_dwell <= active_dwell) - passive_dwell = active_dwell + 1; - - for (i = 0, added = 0; i < il->scan_request->n_channels; i++) { - chan = il->scan_request->channels[i]; - - if (chan->band != band) - continue; - - scan_ch->channel = chan->hw_value; - - ch_info = il_get_channel_info(il, band, scan_ch->channel); - if (!il_is_channel_valid(ch_info)) { - D_SCAN("Channel %d is INVALID for this band.\n", - scan_ch->channel); - continue; - } - - scan_ch->active_dwell = cpu_to_le16(active_dwell); - scan_ch->passive_dwell = cpu_to_le16(passive_dwell); - /* If passive , set up for auto-switch - * and use long active_dwell time. - */ - if (!is_active || il_is_channel_passive(ch_info) || - (chan->flags & IEEE80211_CHAN_NO_IR)) { - scan_ch->type = 0; /* passive */ - if (IL_UCODE_API(il->ucode_ver) == 1) - scan_ch->active_dwell = - cpu_to_le16(passive_dwell - 1); - } else { - scan_ch->type = 1; /* active */ - } - - /* Set direct probe bits. These may be used both for active - * scan channels (probes gets sent right away), - * or for passive channels (probes get se sent only after - * hearing clear Rx packet).*/ - if (IL_UCODE_API(il->ucode_ver) >= 2) { - if (n_probes) - scan_ch->type |= IL39_SCAN_PROBE_MASK(n_probes); - } else { - /* uCode v1 does not allow setting direct probe bits on - * passive channel. */ - if ((scan_ch->type & 1) && n_probes) - scan_ch->type |= IL39_SCAN_PROBE_MASK(n_probes); - } - - /* Set txpower levels to defaults */ - scan_ch->tpc.dsp_atten = 110; - /* scan_pwr_info->tpc.dsp_atten; */ - - /*scan_pwr_info->tpc.tx_gain; */ - if (band == IEEE80211_BAND_5GHZ) - scan_ch->tpc.tx_gain = ((1 << 5) | (3 << 3)) | 3; - else { - scan_ch->tpc.tx_gain = ((1 << 5) | (5 << 3)); - /* NOTE: if we were doing 6Mb OFDM for scans we'd use - * power level: - * scan_ch->tpc.tx_gain = ((1 << 5) | (2 << 3)) | 3; - */ - } - - D_SCAN("Scanning %d [%s %d]\n", scan_ch->channel, - (scan_ch->type & 1) ? "ACTIVE" : "PASSIVE", - (scan_ch->type & 1) ? active_dwell : passive_dwell); - - scan_ch++; - added++; - } - - D_SCAN("total channels to scan %d\n", added); - return added; -} - -static void -il3945_init_hw_rates(struct il_priv *il, struct ieee80211_rate *rates) -{ - int i; - - for (i = 0; i < RATE_COUNT_LEGACY; i++) { - rates[i].bitrate = il3945_rates[i].ieee * 5; - rates[i].hw_value = i; /* Rate scaling will work on idxes */ - rates[i].hw_value_short = i; - rates[i].flags = 0; - if (i > IL39_LAST_OFDM_RATE || i < IL_FIRST_OFDM_RATE) { - /* - * If CCK != 1M then set short preamble rate flag. - */ - rates[i].flags |= - (il3945_rates[i].plcp == - 10) ? 0 : IEEE80211_RATE_SHORT_PREAMBLE; - } - } -} - -/****************************************************************************** - * - * uCode download functions - * - ******************************************************************************/ - -static void -il3945_dealloc_ucode_pci(struct il_priv *il) -{ - il_free_fw_desc(il->pci_dev, &il->ucode_code); - il_free_fw_desc(il->pci_dev, &il->ucode_data); - il_free_fw_desc(il->pci_dev, &il->ucode_data_backup); - il_free_fw_desc(il->pci_dev, &il->ucode_init); - il_free_fw_desc(il->pci_dev, &il->ucode_init_data); - il_free_fw_desc(il->pci_dev, &il->ucode_boot); -} - -/** - * il3945_verify_inst_full - verify runtime uCode image in card vs. host, - * looking at all data. - */ -static int -il3945_verify_inst_full(struct il_priv *il, __le32 * image, u32 len) -{ - u32 val; - u32 save_len = len; - int rc = 0; - u32 errcnt; - - D_INFO("ucode inst image size is %u\n", len); - - il_wr(il, HBUS_TARG_MEM_RADDR, IL39_RTC_INST_LOWER_BOUND); - - errcnt = 0; - for (; len > 0; len -= sizeof(u32), image++) { - /* read data comes through single port, auto-incr addr */ - /* NOTE: Use the debugless read so we don't flood kernel log - * if IL_DL_IO is set */ - val = _il_rd(il, HBUS_TARG_MEM_RDAT); - if (val != le32_to_cpu(*image)) { - IL_ERR("uCode INST section is invalid at " - "offset 0x%x, is 0x%x, s/b 0x%x\n", - save_len - len, val, le32_to_cpu(*image)); - rc = -EIO; - errcnt++; - if (errcnt >= 20) - break; - } - } - - if (!errcnt) - D_INFO("ucode image in INSTRUCTION memory is good\n"); - - return rc; -} - -/** - * il3945_verify_inst_sparse - verify runtime uCode image in card vs. host, - * using sample data 100 bytes apart. If these sample points are good, - * it's a pretty good bet that everything between them is good, too. - */ -static int -il3945_verify_inst_sparse(struct il_priv *il, __le32 * image, u32 len) -{ - u32 val; - int rc = 0; - u32 errcnt = 0; - u32 i; - - D_INFO("ucode inst image size is %u\n", len); - - for (i = 0; i < len; i += 100, image += 100 / sizeof(u32)) { - /* read data comes through single port, auto-incr addr */ - /* NOTE: Use the debugless read so we don't flood kernel log - * if IL_DL_IO is set */ - il_wr(il, HBUS_TARG_MEM_RADDR, i + IL39_RTC_INST_LOWER_BOUND); - val = _il_rd(il, HBUS_TARG_MEM_RDAT); - if (val != le32_to_cpu(*image)) { -#if 0 /* Enable this if you want to see details */ - IL_ERR("uCode INST section is invalid at " - "offset 0x%x, is 0x%x, s/b 0x%x\n", i, val, - *image); -#endif - rc = -EIO; - errcnt++; - if (errcnt >= 3) - break; - } - } - - return rc; -} - -/** - * il3945_verify_ucode - determine which instruction image is in SRAM, - * and verify its contents - */ -static int -il3945_verify_ucode(struct il_priv *il) -{ - __le32 *image; - u32 len; - int rc = 0; - - /* Try bootstrap */ - image = (__le32 *) il->ucode_boot.v_addr; - len = il->ucode_boot.len; - rc = il3945_verify_inst_sparse(il, image, len); - if (rc == 0) { - D_INFO("Bootstrap uCode is good in inst SRAM\n"); - return 0; - } - - /* Try initialize */ - image = (__le32 *) il->ucode_init.v_addr; - len = il->ucode_init.len; - rc = il3945_verify_inst_sparse(il, image, len); - if (rc == 0) { - D_INFO("Initialize uCode is good in inst SRAM\n"); - return 0; - } - - /* Try runtime/protocol */ - image = (__le32 *) il->ucode_code.v_addr; - len = il->ucode_code.len; - rc = il3945_verify_inst_sparse(il, image, len); - if (rc == 0) { - D_INFO("Runtime uCode is good in inst SRAM\n"); - return 0; - } - - IL_ERR("NO VALID UCODE IMAGE IN INSTRUCTION SRAM!!\n"); - - /* Since nothing seems to match, show first several data entries in - * instruction SRAM, so maybe visual inspection will give a clue. - * Selection of bootstrap image (vs. other images) is arbitrary. */ - image = (__le32 *) il->ucode_boot.v_addr; - len = il->ucode_boot.len; - rc = il3945_verify_inst_full(il, image, len); - - return rc; -} - -static void -il3945_nic_start(struct il_priv *il) -{ - /* Remove all resets to allow NIC to operate */ - _il_wr(il, CSR_RESET, 0); -} - -#define IL3945_UCODE_GET(item) \ -static u32 il3945_ucode_get_##item(const struct il_ucode_header *ucode)\ -{ \ - return le32_to_cpu(ucode->v1.item); \ -} - -static u32 -il3945_ucode_get_header_size(u32 api_ver) -{ - return 24; -} - -static u8 * -il3945_ucode_get_data(const struct il_ucode_header *ucode) -{ - return (u8 *) ucode->v1.data; -} - -IL3945_UCODE_GET(inst_size); -IL3945_UCODE_GET(data_size); -IL3945_UCODE_GET(init_size); -IL3945_UCODE_GET(init_data_size); -IL3945_UCODE_GET(boot_size); - -/** - * il3945_read_ucode - Read uCode images from disk file. - * - * Copy into buffers for card to fetch via bus-mastering - */ -static int -il3945_read_ucode(struct il_priv *il) -{ - const struct il_ucode_header *ucode; - int ret = -EINVAL, idx; - const struct firmware *ucode_raw; - /* firmware file name contains uCode/driver compatibility version */ - const char *name_pre = il->cfg->fw_name_pre; - const unsigned int api_max = il->cfg->ucode_api_max; - const unsigned int api_min = il->cfg->ucode_api_min; - char buf[25]; - u8 *src; - size_t len; - u32 api_ver, inst_size, data_size, init_size, init_data_size, boot_size; - - /* Ask kernel firmware_class module to get the boot firmware off disk. - * request_firmware() is synchronous, file is in memory on return. */ - for (idx = api_max; idx >= api_min; idx--) { - sprintf(buf, "%s%u%s", name_pre, idx, ".ucode"); - ret = request_firmware(&ucode_raw, buf, &il->pci_dev->dev); - if (ret < 0) { - IL_ERR("%s firmware file req failed: %d\n", buf, ret); - if (ret == -ENOENT) - continue; - else - goto error; - } else { - if (idx < api_max) - IL_ERR("Loaded firmware %s, " - "which is deprecated. " - " Please use API v%u instead.\n", buf, - api_max); - D_INFO("Got firmware '%s' file " - "(%zd bytes) from disk\n", buf, ucode_raw->size); - break; - } - } - - if (ret < 0) - goto error; - - /* Make sure that we got at least our header! */ - if (ucode_raw->size < il3945_ucode_get_header_size(1)) { - IL_ERR("File size way too small!\n"); - ret = -EINVAL; - goto err_release; - } - - /* Data from ucode file: header followed by uCode images */ - ucode = (struct il_ucode_header *)ucode_raw->data; - - il->ucode_ver = le32_to_cpu(ucode->ver); - api_ver = IL_UCODE_API(il->ucode_ver); - inst_size = il3945_ucode_get_inst_size(ucode); - data_size = il3945_ucode_get_data_size(ucode); - init_size = il3945_ucode_get_init_size(ucode); - init_data_size = il3945_ucode_get_init_data_size(ucode); - boot_size = il3945_ucode_get_boot_size(ucode); - src = il3945_ucode_get_data(ucode); - - /* api_ver should match the api version forming part of the - * firmware filename ... but we don't check for that and only rely - * on the API version read from firmware header from here on forward */ - - if (api_ver < api_min || api_ver > api_max) { - IL_ERR("Driver unable to support your firmware API. " - "Driver supports v%u, firmware is v%u.\n", api_max, - api_ver); - il->ucode_ver = 0; - ret = -EINVAL; - goto err_release; - } - if (api_ver != api_max) - IL_ERR("Firmware has old API version. Expected %u, " - "got %u. New firmware can be obtained " - "from http://www.intellinuxwireless.org.\n", api_max, - api_ver); - - IL_INFO("loaded firmware version %u.%u.%u.%u\n", - IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver), - IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver)); - - snprintf(il->hw->wiphy->fw_version, sizeof(il->hw->wiphy->fw_version), - "%u.%u.%u.%u", IL_UCODE_MAJOR(il->ucode_ver), - IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver), - IL_UCODE_SERIAL(il->ucode_ver)); - - D_INFO("f/w package hdr ucode version raw = 0x%x\n", il->ucode_ver); - D_INFO("f/w package hdr runtime inst size = %u\n", inst_size); - D_INFO("f/w package hdr runtime data size = %u\n", data_size); - D_INFO("f/w package hdr init inst size = %u\n", init_size); - D_INFO("f/w package hdr init data size = %u\n", init_data_size); - D_INFO("f/w package hdr boot inst size = %u\n", boot_size); - - /* Verify size of file vs. image size info in file's header */ - if (ucode_raw->size != - il3945_ucode_get_header_size(api_ver) + inst_size + data_size + - init_size + init_data_size + boot_size) { - - D_INFO("uCode file size %zd does not match expected size\n", - ucode_raw->size); - ret = -EINVAL; - goto err_release; - } - - /* Verify that uCode images will fit in card's SRAM */ - if (inst_size > IL39_MAX_INST_SIZE) { - D_INFO("uCode instr len %d too large to fit in\n", inst_size); - ret = -EINVAL; - goto err_release; - } - - if (data_size > IL39_MAX_DATA_SIZE) { - D_INFO("uCode data len %d too large to fit in\n", data_size); - ret = -EINVAL; - goto err_release; - } - if (init_size > IL39_MAX_INST_SIZE) { - D_INFO("uCode init instr len %d too large to fit in\n", - init_size); - ret = -EINVAL; - goto err_release; - } - if (init_data_size > IL39_MAX_DATA_SIZE) { - D_INFO("uCode init data len %d too large to fit in\n", - init_data_size); - ret = -EINVAL; - goto err_release; - } - if (boot_size > IL39_MAX_BSM_SIZE) { - D_INFO("uCode boot instr len %d too large to fit in\n", - boot_size); - ret = -EINVAL; - goto err_release; - } - - /* Allocate ucode buffers for card's bus-master loading ... */ - - /* Runtime instructions and 2 copies of data: - * 1) unmodified from disk - * 2) backup cache for save/restore during power-downs */ - il->ucode_code.len = inst_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_code); - - il->ucode_data.len = data_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_data); - - il->ucode_data_backup.len = data_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_data_backup); - - if (!il->ucode_code.v_addr || !il->ucode_data.v_addr || - !il->ucode_data_backup.v_addr) - goto err_pci_alloc; - - /* Initialization instructions and data */ - if (init_size && init_data_size) { - il->ucode_init.len = init_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_init); - - il->ucode_init_data.len = init_data_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_init_data); - - if (!il->ucode_init.v_addr || !il->ucode_init_data.v_addr) - goto err_pci_alloc; - } - - /* Bootstrap (instructions only, no data) */ - if (boot_size) { - il->ucode_boot.len = boot_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_boot); - - if (!il->ucode_boot.v_addr) - goto err_pci_alloc; - } - - /* Copy images into buffers for card's bus-master reads ... */ - - /* Runtime instructions (first block of data in file) */ - len = inst_size; - D_INFO("Copying (but not loading) uCode instr len %zd\n", len); - memcpy(il->ucode_code.v_addr, src, len); - src += len; - - D_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", - il->ucode_code.v_addr, (u32) il->ucode_code.p_addr); - - /* Runtime data (2nd block) - * NOTE: Copy into backup buffer will be done in il3945_up() */ - len = data_size; - D_INFO("Copying (but not loading) uCode data len %zd\n", len); - memcpy(il->ucode_data.v_addr, src, len); - memcpy(il->ucode_data_backup.v_addr, src, len); - src += len; - - /* Initialization instructions (3rd block) */ - if (init_size) { - len = init_size; - D_INFO("Copying (but not loading) init instr len %zd\n", len); - memcpy(il->ucode_init.v_addr, src, len); - src += len; - } - - /* Initialization data (4th block) */ - if (init_data_size) { - len = init_data_size; - D_INFO("Copying (but not loading) init data len %zd\n", len); - memcpy(il->ucode_init_data.v_addr, src, len); - src += len; - } - - /* Bootstrap instructions (5th block) */ - len = boot_size; - D_INFO("Copying (but not loading) boot instr len %zd\n", len); - memcpy(il->ucode_boot.v_addr, src, len); - - /* We have our copies now, allow OS release its copies */ - release_firmware(ucode_raw); - return 0; - -err_pci_alloc: - IL_ERR("failed to allocate pci memory\n"); - ret = -ENOMEM; - il3945_dealloc_ucode_pci(il); - -err_release: - release_firmware(ucode_raw); - -error: - return ret; -} - -/** - * il3945_set_ucode_ptrs - Set uCode address location - * - * Tell initialization uCode where to find runtime uCode. - * - * BSM registers initially contain pointers to initialization uCode. - * We need to replace them to load runtime uCode inst and data, - * and to save runtime data when powering down. - */ -static int -il3945_set_ucode_ptrs(struct il_priv *il) -{ - dma_addr_t pinst; - dma_addr_t pdata; - - /* bits 31:0 for 3945 */ - pinst = il->ucode_code.p_addr; - pdata = il->ucode_data_backup.p_addr; - - /* Tell bootstrap uCode where to find image to load */ - il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); - il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); - il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, il->ucode_data.len); - - /* Inst byte count must be last to set up, bit 31 signals uCode - * that all new ptr/size info is in place */ - il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, - il->ucode_code.len | BSM_DRAM_INST_LOAD); - - D_INFO("Runtime uCode pointers are set.\n"); - - return 0; -} - -/** - * il3945_init_alive_start - Called after N_ALIVE notification received - * - * Called after N_ALIVE notification received from "initialize" uCode. - * - * Tell "initialize" uCode to go ahead and load the runtime uCode. - */ -static void -il3945_init_alive_start(struct il_priv *il) -{ - /* Check alive response for "valid" sign from uCode */ - if (il->card_alive_init.is_valid != UCODE_VALID_OK) { - /* We had an error bringing up the hardware, so take it - * all the way back down so we can try again */ - D_INFO("Initialize Alive failed.\n"); - goto restart; - } - - /* Bootstrap uCode has loaded initialize uCode ... verify inst image. - * This is a paranoid check, because we would not have gotten the - * "initialize" alive if code weren't properly loaded. */ - if (il3945_verify_ucode(il)) { - /* Runtime instruction load was bad; - * take it all the way back down so we can try again */ - D_INFO("Bad \"initialize\" uCode load.\n"); - goto restart; - } - - /* Send pointers to protocol/runtime uCode image ... init code will - * load and launch runtime uCode, which will send us another "Alive" - * notification. */ - D_INFO("Initialization Alive received.\n"); - if (il3945_set_ucode_ptrs(il)) { - /* Runtime instruction load won't happen; - * take it all the way back down so we can try again */ - D_INFO("Couldn't set up uCode pointers.\n"); - goto restart; - } - return; - -restart: - queue_work(il->workqueue, &il->restart); -} - -/** - * il3945_alive_start - called after N_ALIVE notification received - * from protocol/runtime uCode (initialization uCode's - * Alive gets handled by il3945_init_alive_start()). - */ -static void -il3945_alive_start(struct il_priv *il) -{ - int thermal_spin = 0; - u32 rfkill; - - D_INFO("Runtime Alive received.\n"); - - if (il->card_alive.is_valid != UCODE_VALID_OK) { - /* We had an error bringing up the hardware, so take it - * all the way back down so we can try again */ - D_INFO("Alive failed.\n"); - goto restart; - } - - /* Initialize uCode has loaded Runtime uCode ... verify inst image. - * This is a paranoid check, because we would not have gotten the - * "runtime" alive if code weren't properly loaded. */ - if (il3945_verify_ucode(il)) { - /* Runtime instruction load was bad; - * take it all the way back down so we can try again */ - D_INFO("Bad runtime uCode load.\n"); - goto restart; - } - - rfkill = il_rd_prph(il, APMG_RFKILL_REG); - D_INFO("RFKILL status: 0x%x\n", rfkill); - - if (rfkill & 0x1) { - clear_bit(S_RFKILL, &il->status); - /* if RFKILL is not on, then wait for thermal - * sensor in adapter to kick in */ - while (il3945_hw_get_temperature(il) == 0) { - thermal_spin++; - udelay(10); - } - - if (thermal_spin) - D_INFO("Thermal calibration took %dus\n", - thermal_spin * 10); - } else - set_bit(S_RFKILL, &il->status); - - /* After the ALIVE response, we can send commands to 3945 uCode */ - set_bit(S_ALIVE, &il->status); - - /* Enable watchdog to monitor the driver tx queues */ - il_setup_watchdog(il); - - if (il_is_rfkill(il)) - return; - - ieee80211_wake_queues(il->hw); - - il->active_rate = RATES_MASK_3945; - - il_power_update_mode(il, true); - - if (il_is_associated(il)) { - struct il3945_rxon_cmd *active_rxon = - (struct il3945_rxon_cmd *)(&il->active); - - il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - } else { - /* Initialize our rx_config data */ - il_connection_init_rx_config(il); - } - - /* Configure Bluetooth device coexistence support */ - il_send_bt_config(il); - - set_bit(S_READY, &il->status); - - /* Configure the adapter for unassociated operation */ - il3945_commit_rxon(il); - - il3945_reg_txpower_periodic(il); - - D_INFO("ALIVE processing complete.\n"); - wake_up(&il->wait_command_queue); - - return; - -restart: - queue_work(il->workqueue, &il->restart); -} - -static void il3945_cancel_deferred_work(struct il_priv *il); - -static void -__il3945_down(struct il_priv *il) -{ - unsigned long flags; - int exit_pending; - - D_INFO(DRV_NAME " is going down\n"); - - il_scan_cancel_timeout(il, 200); - - exit_pending = test_and_set_bit(S_EXIT_PENDING, &il->status); - - /* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set - * to prevent rearm timer */ - del_timer_sync(&il->watchdog); - - /* Station information will now be cleared in device */ - il_clear_ucode_stations(il); - il_dealloc_bcast_stations(il); - il_clear_driver_stations(il); - - /* Unblock any waiting calls */ - wake_up_all(&il->wait_command_queue); - - /* Wipe out the EXIT_PENDING status bit if we are not actually - * exiting the module */ - if (!exit_pending) - clear_bit(S_EXIT_PENDING, &il->status); - - /* stop and reset the on-board processor */ - _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); - - /* tell the device to stop sending interrupts */ - spin_lock_irqsave(&il->lock, flags); - il_disable_interrupts(il); - spin_unlock_irqrestore(&il->lock, flags); - il3945_synchronize_irq(il); - - if (il->mac80211_registered) - ieee80211_stop_queues(il->hw); - - /* If we have not previously called il3945_init() then - * clear all bits but the RF Kill bits and return */ - if (!il_is_init(il)) { - il->status = - test_bit(S_RFKILL, &il->status) << S_RFKILL | - test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | - test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; - goto exit; - } - - /* ...otherwise clear out all the status bits but the RF Kill - * bit and continue taking the NIC down. */ - il->status &= - test_bit(S_RFKILL, &il->status) << S_RFKILL | - test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | - test_bit(S_FW_ERROR, &il->status) << S_FW_ERROR | - test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; - - /* - * We disabled and synchronized interrupt, and priv->mutex is taken, so - * here is the only thread which will program device registers, but - * still have lockdep assertions, so we are taking reg_lock. - */ - spin_lock_irq(&il->reg_lock); - /* FIXME: il_grab_nic_access if rfkill is off ? */ - - il3945_hw_txq_ctx_stop(il); - il3945_hw_rxq_stop(il); - /* Power-down device's busmaster DMA clocks */ - _il_wr_prph(il, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); - udelay(5); - /* Stop the device, and put it in low power state */ - _il_apm_stop(il); - - spin_unlock_irq(&il->reg_lock); - - il3945_hw_txq_ctx_free(il); -exit: - memset(&il->card_alive, 0, sizeof(struct il_alive_resp)); - - if (il->beacon_skb) - dev_kfree_skb(il->beacon_skb); - il->beacon_skb = NULL; - - /* clear out any free frames */ - il3945_clear_free_frames(il); -} - -static void -il3945_down(struct il_priv *il) -{ - mutex_lock(&il->mutex); - __il3945_down(il); - mutex_unlock(&il->mutex); - - il3945_cancel_deferred_work(il); -} - -#define MAX_HW_RESTARTS 5 - -static int -il3945_alloc_bcast_station(struct il_priv *il) -{ - unsigned long flags; - u8 sta_id; - - spin_lock_irqsave(&il->sta_lock, flags); - sta_id = il_prep_station(il, il_bcast_addr, false, NULL); - if (sta_id == IL_INVALID_STATION) { - IL_ERR("Unable to prepare broadcast station\n"); - spin_unlock_irqrestore(&il->sta_lock, flags); - - return -EINVAL; - } - - il->stations[sta_id].used |= IL_STA_DRIVER_ACTIVE; - il->stations[sta_id].used |= IL_STA_BCAST; - spin_unlock_irqrestore(&il->sta_lock, flags); - - return 0; -} - -static int -__il3945_up(struct il_priv *il) -{ - int rc, i; - - rc = il3945_alloc_bcast_station(il); - if (rc) - return rc; - - if (test_bit(S_EXIT_PENDING, &il->status)) { - IL_WARN("Exit pending; will not bring the NIC up\n"); - return -EIO; - } - - if (!il->ucode_data_backup.v_addr || !il->ucode_data.v_addr) { - IL_ERR("ucode not available for device bring up\n"); - return -EIO; - } - - /* If platform's RF_KILL switch is NOT set to KILL */ - if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) - clear_bit(S_RFKILL, &il->status); - else { - set_bit(S_RFKILL, &il->status); - return -ERFKILL; - } - - _il_wr(il, CSR_INT, 0xFFFFFFFF); - - rc = il3945_hw_nic_init(il); - if (rc) { - IL_ERR("Unable to int nic\n"); - return rc; - } - - /* make sure rfkill handshake bits are cleared */ - _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - /* clear (again), then enable host interrupts */ - _il_wr(il, CSR_INT, 0xFFFFFFFF); - il_enable_interrupts(il); - - /* really make sure rfkill handshake bits are cleared */ - _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - - /* Copy original ucode data image from disk into backup cache. - * This will be used to initialize the on-board processor's - * data SRAM for a clean start when the runtime program first loads. */ - memcpy(il->ucode_data_backup.v_addr, il->ucode_data.v_addr, - il->ucode_data.len); - - /* We return success when we resume from suspend and rf_kill is on. */ - if (test_bit(S_RFKILL, &il->status)) - return 0; - - for (i = 0; i < MAX_HW_RESTARTS; i++) { - - /* load bootstrap state machine, - * load bootstrap program into processor's memory, - * prepare to load the "initialize" uCode */ - rc = il->ops->load_ucode(il); - - if (rc) { - IL_ERR("Unable to set up bootstrap uCode: %d\n", rc); - continue; - } - - /* start card; "initialize" will load runtime ucode */ - il3945_nic_start(il); - - D_INFO(DRV_NAME " is coming up\n"); - - return 0; - } - - set_bit(S_EXIT_PENDING, &il->status); - __il3945_down(il); - clear_bit(S_EXIT_PENDING, &il->status); - - /* tried to restart and config the device for as long as our - * patience could withstand */ - IL_ERR("Unable to initialize device after %d attempts.\n", i); - return -EIO; -} - -/***************************************************************************** - * - * Workqueue callbacks - * - *****************************************************************************/ - -static void -il3945_bg_init_alive_start(struct work_struct *data) -{ - struct il_priv *il = - container_of(data, struct il_priv, init_alive_start.work); - - mutex_lock(&il->mutex); - if (test_bit(S_EXIT_PENDING, &il->status)) - goto out; - - il3945_init_alive_start(il); -out: - mutex_unlock(&il->mutex); -} - -static void -il3945_bg_alive_start(struct work_struct *data) -{ - struct il_priv *il = - container_of(data, struct il_priv, alive_start.work); - - mutex_lock(&il->mutex); - if (test_bit(S_EXIT_PENDING, &il->status) || il->txq == NULL) - goto out; - - il3945_alive_start(il); -out: - mutex_unlock(&il->mutex); -} - -/* - * 3945 cannot interrupt driver when hardware rf kill switch toggles; - * driver must poll CSR_GP_CNTRL_REG register for change. This register - * *is* readable even when device has been SW_RESET into low power mode - * (e.g. during RF KILL). - */ -static void -il3945_rfkill_poll(struct work_struct *data) -{ - struct il_priv *il = - container_of(data, struct il_priv, _3945.rfkill_poll.work); - bool old_rfkill = test_bit(S_RFKILL, &il->status); - bool new_rfkill = - !(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); - - if (new_rfkill != old_rfkill) { - if (new_rfkill) - set_bit(S_RFKILL, &il->status); - else - clear_bit(S_RFKILL, &il->status); - - wiphy_rfkill_set_hw_state(il->hw->wiphy, new_rfkill); - - D_RF_KILL("RF_KILL bit toggled to %s.\n", - new_rfkill ? "disable radio" : "enable radio"); - } - - /* Keep this running, even if radio now enabled. This will be - * cancelled in mac_start() if system decides to start again */ - queue_delayed_work(il->workqueue, &il->_3945.rfkill_poll, - round_jiffies_relative(2 * HZ)); - -} - -int -il3945_request_scan(struct il_priv *il, struct ieee80211_vif *vif) -{ - struct il_host_cmd cmd = { - .id = C_SCAN, - .len = sizeof(struct il3945_scan_cmd), - .flags = CMD_SIZE_HUGE, - }; - struct il3945_scan_cmd *scan; - u8 n_probes = 0; - enum ieee80211_band band; - bool is_active = false; - int ret; - u16 len; - - lockdep_assert_held(&il->mutex); - - if (!il->scan_cmd) { - il->scan_cmd = - kmalloc(sizeof(struct il3945_scan_cmd) + IL_MAX_SCAN_SIZE, - GFP_KERNEL); - if (!il->scan_cmd) { - D_SCAN("Fail to allocate scan memory\n"); - return -ENOMEM; - } - } - scan = il->scan_cmd; - memset(scan, 0, sizeof(struct il3945_scan_cmd) + IL_MAX_SCAN_SIZE); - - scan->quiet_plcp_th = IL_PLCP_QUIET_THRESH; - scan->quiet_time = IL_ACTIVE_QUIET_TIME; - - if (il_is_associated(il)) { - u16 interval; - u32 extra; - u32 suspend_time = 100; - u32 scan_suspend_time = 100; - - D_INFO("Scanning while associated...\n"); - - interval = vif->bss_conf.beacon_int; - - scan->suspend_time = 0; - scan->max_out_time = cpu_to_le32(200 * 1024); - if (!interval) - interval = suspend_time; - /* - * suspend time format: - * 0-19: beacon interval in usec (time before exec.) - * 20-23: 0 - * 24-31: number of beacons (suspend between channels) - */ - - extra = (suspend_time / interval) << 24; - scan_suspend_time = - 0xFF0FFFFF & (extra | ((suspend_time % interval) * 1024)); - - scan->suspend_time = cpu_to_le32(scan_suspend_time); - D_SCAN("suspend_time 0x%X beacon interval %d\n", - scan_suspend_time, interval); - } - - if (il->scan_request->n_ssids) { - int i, p = 0; - D_SCAN("Kicking off active scan\n"); - for (i = 0; i < il->scan_request->n_ssids; i++) { - /* always does wildcard anyway */ - if (!il->scan_request->ssids[i].ssid_len) - continue; - scan->direct_scan[p].id = WLAN_EID_SSID; - scan->direct_scan[p].len = - il->scan_request->ssids[i].ssid_len; - memcpy(scan->direct_scan[p].ssid, - il->scan_request->ssids[i].ssid, - il->scan_request->ssids[i].ssid_len); - n_probes++; - p++; - } - is_active = true; - } else - D_SCAN("Kicking off passive scan.\n"); - - /* We don't build a direct scan probe request; the uCode will do - * that based on the direct_mask added to each channel entry */ - scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; - scan->tx_cmd.sta_id = il->hw_params.bcast_id; - scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - - /* flags + rate selection */ - - switch (il->scan_band) { - case IEEE80211_BAND_2GHZ: - scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; - scan->tx_cmd.rate = RATE_1M_PLCP; - band = IEEE80211_BAND_2GHZ; - break; - case IEEE80211_BAND_5GHZ: - scan->tx_cmd.rate = RATE_6M_PLCP; - band = IEEE80211_BAND_5GHZ; - break; - default: - IL_WARN("Invalid scan band\n"); - return -EIO; - } - - /* - * If active scaning is requested but a certain channel is marked - * passive, we can do active scanning if we detect transmissions. For - * passive only scanning disable switching to active on any channel. - */ - scan->good_CRC_th = - is_active ? IL_GOOD_CRC_TH_DEFAULT : IL_GOOD_CRC_TH_NEVER; - - len = - il_fill_probe_req(il, (struct ieee80211_mgmt *)scan->data, - vif->addr, il->scan_request->ie, - il->scan_request->ie_len, - IL_MAX_SCAN_SIZE - sizeof(*scan)); - scan->tx_cmd.len = cpu_to_le16(len); - - /* select Rx antennas */ - scan->flags |= il3945_get_antenna_flags(il); - - scan->channel_count = - il3945_get_channels_for_scan(il, band, is_active, n_probes, - (void *)&scan->data[len], vif); - if (scan->channel_count == 0) { - D_SCAN("channel count %d\n", scan->channel_count); - return -EIO; - } - - cmd.len += - le16_to_cpu(scan->tx_cmd.len) + - scan->channel_count * sizeof(struct il3945_scan_channel); - cmd.data = scan; - scan->len = cpu_to_le16(cmd.len); - - set_bit(S_SCAN_HW, &il->status); - ret = il_send_cmd_sync(il, &cmd); - if (ret) - clear_bit(S_SCAN_HW, &il->status); - return ret; -} - -void -il3945_post_scan(struct il_priv *il) -{ - /* - * Since setting the RXON may have been deferred while - * performing the scan, fire one off if needed - */ - if (memcmp(&il->staging, &il->active, sizeof(il->staging))) - il3945_commit_rxon(il); -} - -static void -il3945_bg_restart(struct work_struct *data) -{ - struct il_priv *il = container_of(data, struct il_priv, restart); - - if (test_bit(S_EXIT_PENDING, &il->status)) - return; - - if (test_and_clear_bit(S_FW_ERROR, &il->status)) { - mutex_lock(&il->mutex); - il->is_open = 0; - mutex_unlock(&il->mutex); - il3945_down(il); - ieee80211_restart_hw(il->hw); - } else { - il3945_down(il); - - mutex_lock(&il->mutex); - if (test_bit(S_EXIT_PENDING, &il->status)) { - mutex_unlock(&il->mutex); - return; - } - - __il3945_up(il); - mutex_unlock(&il->mutex); - } -} - -static void -il3945_bg_rx_replenish(struct work_struct *data) -{ - struct il_priv *il = container_of(data, struct il_priv, rx_replenish); - - mutex_lock(&il->mutex); - if (test_bit(S_EXIT_PENDING, &il->status)) - goto out; - - il3945_rx_replenish(il); -out: - mutex_unlock(&il->mutex); -} - -void -il3945_post_associate(struct il_priv *il) -{ - int rc = 0; - struct ieee80211_conf *conf = NULL; - - if (!il->vif || !il->is_open) - return; - - D_ASSOC("Associated as %d to: %pM\n", il->vif->bss_conf.aid, - il->active.bssid_addr); - - if (test_bit(S_EXIT_PENDING, &il->status)) - return; - - il_scan_cancel_timeout(il, 200); - - conf = &il->hw->conf; - - il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - il3945_commit_rxon(il); - - rc = il_send_rxon_timing(il); - if (rc) - IL_WARN("C_RXON_TIMING failed - " "Attempting to continue.\n"); - - il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - - il->staging.assoc_id = cpu_to_le16(il->vif->bss_conf.aid); - - D_ASSOC("assoc id %d beacon interval %d\n", il->vif->bss_conf.aid, - il->vif->bss_conf.beacon_int); - - if (il->vif->bss_conf.use_short_preamble) - il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; - else - il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - - if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { - if (il->vif->bss_conf.use_short_slot) - il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - else - il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - } - - il3945_commit_rxon(il); - - switch (il->vif->type) { - case NL80211_IFTYPE_STATION: - il3945_rate_scale_init(il->hw, IL_AP_ID); - break; - case NL80211_IFTYPE_ADHOC: - il3945_send_beacon_cmd(il); - break; - default: - IL_ERR("%s Should not be called in %d mode\n", __func__, - il->vif->type); - break; - } -} - -/***************************************************************************** - * - * mac80211 entry point functions - * - *****************************************************************************/ - -#define UCODE_READY_TIMEOUT (2 * HZ) - -static int -il3945_mac_start(struct ieee80211_hw *hw) -{ - struct il_priv *il = hw->priv; - int ret; - - /* we should be verifying the device is ready to be opened */ - mutex_lock(&il->mutex); - D_MAC80211("enter\n"); - - /* fetch ucode file from disk, alloc and copy to bus-master buffers ... - * ucode filename and max sizes are card-specific. */ - - if (!il->ucode_code.len) { - ret = il3945_read_ucode(il); - if (ret) { - IL_ERR("Could not read microcode: %d\n", ret); - mutex_unlock(&il->mutex); - goto out_release_irq; - } - } - - ret = __il3945_up(il); - - mutex_unlock(&il->mutex); - - if (ret) - goto out_release_irq; - - D_INFO("Start UP work.\n"); - - /* Wait for START_ALIVE from ucode. Otherwise callbacks from - * mac80211 will not be run successfully. */ - ret = wait_event_timeout(il->wait_command_queue, - test_bit(S_READY, &il->status), - UCODE_READY_TIMEOUT); - if (!ret) { - if (!test_bit(S_READY, &il->status)) { - IL_ERR("Wait for START_ALIVE timeout after %dms.\n", - jiffies_to_msecs(UCODE_READY_TIMEOUT)); - ret = -ETIMEDOUT; - goto out_release_irq; - } - } - - /* ucode is running and will send rfkill notifications, - * no need to poll the killswitch state anymore */ - cancel_delayed_work(&il->_3945.rfkill_poll); - - il->is_open = 1; - D_MAC80211("leave\n"); - return 0; - -out_release_irq: - il->is_open = 0; - D_MAC80211("leave - failed\n"); - return ret; -} - -static void -il3945_mac_stop(struct ieee80211_hw *hw) -{ - struct il_priv *il = hw->priv; - - D_MAC80211("enter\n"); - - if (!il->is_open) { - D_MAC80211("leave - skip\n"); - return; - } - - il->is_open = 0; - - il3945_down(il); - - flush_workqueue(il->workqueue); - - /* start polling the killswitch state again */ - queue_delayed_work(il->workqueue, &il->_3945.rfkill_poll, - round_jiffies_relative(2 * HZ)); - - D_MAC80211("leave\n"); -} - -static void -il3945_mac_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct il_priv *il = hw->priv; - - D_MAC80211("enter\n"); - - D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, - ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); - - if (il3945_tx_skb(il, control->sta, skb)) - dev_kfree_skb_any(skb); - - D_MAC80211("leave\n"); -} - -void -il3945_config_ap(struct il_priv *il) -{ - struct ieee80211_vif *vif = il->vif; - int rc = 0; - - if (test_bit(S_EXIT_PENDING, &il->status)) - return; - - /* The following should be done only at AP bring up */ - if (!(il_is_associated(il))) { - - /* RXON - unassoc (to set timing command) */ - il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - il3945_commit_rxon(il); - - /* RXON Timing */ - rc = il_send_rxon_timing(il); - if (rc) - IL_WARN("C_RXON_TIMING failed - " - "Attempting to continue.\n"); - - il->staging.assoc_id = 0; - - if (vif->bss_conf.use_short_preamble) - il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; - else - il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - - if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { - if (vif->bss_conf.use_short_slot) - il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - else - il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - } - /* restore RXON assoc */ - il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - il3945_commit_rxon(il); - } - il3945_send_beacon_cmd(il); -} - -static int -il3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct il_priv *il = hw->priv; - int ret = 0; - u8 sta_id = IL_INVALID_STATION; - u8 static_key; - - D_MAC80211("enter\n"); - - if (il3945_mod_params.sw_crypto) { - D_MAC80211("leave - hwcrypto disabled\n"); - return -EOPNOTSUPP; - } - - /* - * To support IBSS RSN, don't program group keys in IBSS, the - * hardware will then not attempt to decrypt the frames. - */ - if (vif->type == NL80211_IFTYPE_ADHOC && - !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { - D_MAC80211("leave - IBSS RSN\n"); - return -EOPNOTSUPP; - } - - static_key = !il_is_associated(il); - - if (!static_key) { - sta_id = il_sta_id_or_broadcast(il, sta); - if (sta_id == IL_INVALID_STATION) { - D_MAC80211("leave - station not found\n"); - return -EINVAL; - } - } - - mutex_lock(&il->mutex); - il_scan_cancel_timeout(il, 100); - - switch (cmd) { - case SET_KEY: - if (static_key) - ret = il3945_set_static_key(il, key); - else - ret = il3945_set_dynamic_key(il, key, sta_id); - D_MAC80211("enable hwcrypto key\n"); - break; - case DISABLE_KEY: - if (static_key) - ret = il3945_remove_static_key(il); - else - ret = il3945_clear_sta_key_info(il, sta_id); - D_MAC80211("disable hwcrypto key\n"); - break; - default: - ret = -EINVAL; - } - - D_MAC80211("leave ret %d\n", ret); - mutex_unlock(&il->mutex); - - return ret; -} - -static int -il3945_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct il_priv *il = hw->priv; - struct il3945_sta_priv *sta_priv = (void *)sta->drv_priv; - int ret; - bool is_ap = vif->type == NL80211_IFTYPE_STATION; - u8 sta_id; - - mutex_lock(&il->mutex); - D_INFO("station %pM\n", sta->addr); - sta_priv->common.sta_id = IL_INVALID_STATION; - - ret = il_add_station_common(il, sta->addr, is_ap, sta, &sta_id); - if (ret) { - IL_ERR("Unable to add station %pM (%d)\n", sta->addr, ret); - /* Should we return success if return code is EEXIST ? */ - mutex_unlock(&il->mutex); - return ret; - } - - sta_priv->common.sta_id = sta_id; - - /* Initialize rate scaling */ - D_INFO("Initializing rate scaling for station %pM\n", sta->addr); - il3945_rs_rate_init(il, sta, sta_id); - mutex_unlock(&il->mutex); - - return 0; -} - -static void -il3945_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, u64 multicast) -{ - struct il_priv *il = hw->priv; - __le32 filter_or = 0, filter_nand = 0; - -#define CHK(test, flag) do { \ - if (*total_flags & (test)) \ - filter_or |= (flag); \ - else \ - filter_nand |= (flag); \ - } while (0) - - D_MAC80211("Enter: changed: 0x%x, total: 0x%x\n", changed_flags, - *total_flags); - - CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK); - CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK); - CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); - -#undef CHK - - mutex_lock(&il->mutex); - - il->staging.filter_flags &= ~filter_nand; - il->staging.filter_flags |= filter_or; - - /* - * Not committing directly because hardware can perform a scan, - * but even if hw is ready, committing here breaks for some reason, - * we'll eventually commit the filter flags change anyway. - */ - - mutex_unlock(&il->mutex); - - /* - * Receiving all multicast frames is always enabled by the - * default flags setup in il_connection_init_rx_config() - * since we currently do not support programming multicast - * filters into the device. - */ - *total_flags &= - FIF_OTHER_BSS | FIF_ALLMULTI | - FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; -} - -/***************************************************************************** - * - * sysfs attributes - * - *****************************************************************************/ - -#ifdef CONFIG_IWLEGACY_DEBUG - -/* - * The following adds a new attribute to the sysfs representation - * of this device driver (i.e. a new file in /sys/bus/pci/drivers/iwl/) - * used for controlling the debug level. - * - * See the level definitions in iwl for details. - * - * The debug_level being managed using sysfs below is a per device debug - * level that is used instead of the global debug level if it (the per - * device debug level) is set. - */ -static ssize_t -il3945_show_debug_level(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - return sprintf(buf, "0x%08X\n", il_get_debug_level(il)); -} - -static ssize_t -il3945_store_debug_level(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct il_priv *il = dev_get_drvdata(d); - unsigned long val; - int ret; - - ret = kstrtoul(buf, 0, &val); - if (ret) - IL_INFO("%s is not in hex or decimal form.\n", buf); - else - il->debug_level = val; - - return strnlen(buf, count); -} - -static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, il3945_show_debug_level, - il3945_store_debug_level); - -#endif /* CONFIG_IWLEGACY_DEBUG */ - -static ssize_t -il3945_show_temperature(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - - if (!il_is_alive(il)) - return -EAGAIN; - - return sprintf(buf, "%d\n", il3945_hw_get_temperature(il)); -} - -static DEVICE_ATTR(temperature, S_IRUGO, il3945_show_temperature, NULL); - -static ssize_t -il3945_show_tx_power(struct device *d, struct device_attribute *attr, char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - return sprintf(buf, "%d\n", il->tx_power_user_lmt); -} - -static ssize_t -il3945_store_tx_power(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct il_priv *il = dev_get_drvdata(d); - char *p = (char *)buf; - u32 val; - - val = simple_strtoul(p, &p, 10); - if (p == buf) - IL_INFO(": %s is not in decimal form.\n", buf); - else - il3945_hw_reg_set_txpower(il, val); - - return count; -} - -static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, il3945_show_tx_power, - il3945_store_tx_power); - -static ssize_t -il3945_show_flags(struct device *d, struct device_attribute *attr, char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - - return sprintf(buf, "0x%04X\n", il->active.flags); -} - -static ssize_t -il3945_store_flags(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct il_priv *il = dev_get_drvdata(d); - u32 flags = simple_strtoul(buf, NULL, 0); - - mutex_lock(&il->mutex); - if (le32_to_cpu(il->staging.flags) != flags) { - /* Cancel any currently running scans... */ - if (il_scan_cancel_timeout(il, 100)) - IL_WARN("Could not cancel scan.\n"); - else { - D_INFO("Committing rxon.flags = 0x%04X\n", flags); - il->staging.flags = cpu_to_le32(flags); - il3945_commit_rxon(il); - } - } - mutex_unlock(&il->mutex); - - return count; -} - -static DEVICE_ATTR(flags, S_IWUSR | S_IRUGO, il3945_show_flags, - il3945_store_flags); - -static ssize_t -il3945_show_filter_flags(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - - return sprintf(buf, "0x%04X\n", le32_to_cpu(il->active.filter_flags)); -} - -static ssize_t -il3945_store_filter_flags(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct il_priv *il = dev_get_drvdata(d); - u32 filter_flags = simple_strtoul(buf, NULL, 0); - - mutex_lock(&il->mutex); - if (le32_to_cpu(il->staging.filter_flags) != filter_flags) { - /* Cancel any currently running scans... */ - if (il_scan_cancel_timeout(il, 100)) - IL_WARN("Could not cancel scan.\n"); - else { - D_INFO("Committing rxon.filter_flags = " "0x%04X\n", - filter_flags); - il->staging.filter_flags = cpu_to_le32(filter_flags); - il3945_commit_rxon(il); - } - } - mutex_unlock(&il->mutex); - - return count; -} - -static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, il3945_show_filter_flags, - il3945_store_filter_flags); - -static ssize_t -il3945_show_measurement(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - struct il_spectrum_notification measure_report; - u32 size = sizeof(measure_report), len = 0, ofs = 0; - u8 *data = (u8 *) &measure_report; - unsigned long flags; - - spin_lock_irqsave(&il->lock, flags); - if (!(il->measurement_status & MEASUREMENT_READY)) { - spin_unlock_irqrestore(&il->lock, flags); - return 0; - } - memcpy(&measure_report, &il->measure_report, size); - il->measurement_status = 0; - spin_unlock_irqrestore(&il->lock, flags); - - while (size && PAGE_SIZE - len) { - hex_dump_to_buffer(data + ofs, size, 16, 1, buf + len, - PAGE_SIZE - len, true); - len = strlen(buf); - if (PAGE_SIZE - len) - buf[len++] = '\n'; - - ofs += 16; - size -= min(size, 16U); - } - - return len; -} - -static ssize_t -il3945_store_measurement(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct il_priv *il = dev_get_drvdata(d); - struct ieee80211_measurement_params params = { - .channel = le16_to_cpu(il->active.channel), - .start_time = cpu_to_le64(il->_3945.last_tsf), - .duration = cpu_to_le16(1), - }; - u8 type = IL_MEASURE_BASIC; - u8 buffer[32]; - u8 channel; - - if (count) { - char *p = buffer; - strlcpy(buffer, buf, sizeof(buffer)); - channel = simple_strtoul(p, NULL, 0); - if (channel) - params.channel = channel; - - p = buffer; - while (*p && *p != ' ') - p++; - if (*p) - type = simple_strtoul(p + 1, NULL, 0); - } - - D_INFO("Invoking measurement of type %d on " "channel %d (for '%s')\n", - type, params.channel, buf); - il3945_get_measurement(il, ¶ms, type); - - return count; -} - -static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR, il3945_show_measurement, - il3945_store_measurement); - -static ssize_t -il3945_store_retry_rate(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct il_priv *il = dev_get_drvdata(d); - - il->retry_rate = simple_strtoul(buf, NULL, 0); - if (il->retry_rate <= 0) - il->retry_rate = 1; - - return count; -} - -static ssize_t -il3945_show_retry_rate(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - return sprintf(buf, "%d", il->retry_rate); -} - -static DEVICE_ATTR(retry_rate, S_IWUSR | S_IRUSR, il3945_show_retry_rate, - il3945_store_retry_rate); - -static ssize_t -il3945_show_channels(struct device *d, struct device_attribute *attr, char *buf) -{ - /* all this shit doesn't belong into sysfs anyway */ - return 0; -} - -static DEVICE_ATTR(channels, S_IRUSR, il3945_show_channels, NULL); - -static ssize_t -il3945_show_antenna(struct device *d, struct device_attribute *attr, char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - - if (!il_is_alive(il)) - return -EAGAIN; - - return sprintf(buf, "%d\n", il3945_mod_params.antenna); -} - -static ssize_t -il3945_store_antenna(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct il_priv *il __maybe_unused = dev_get_drvdata(d); - int ant; - - if (count == 0) - return 0; - - if (sscanf(buf, "%1i", &ant) != 1) { - D_INFO("not in hex or decimal form.\n"); - return count; - } - - if (ant >= 0 && ant <= 2) { - D_INFO("Setting antenna select to %d.\n", ant); - il3945_mod_params.antenna = (enum il3945_antenna)ant; - } else - D_INFO("Bad antenna select value %d.\n", ant); - - return count; -} - -static DEVICE_ATTR(antenna, S_IWUSR | S_IRUGO, il3945_show_antenna, - il3945_store_antenna); - -static ssize_t -il3945_show_status(struct device *d, struct device_attribute *attr, char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - if (!il_is_alive(il)) - return -EAGAIN; - return sprintf(buf, "0x%08x\n", (int)il->status); -} - -static DEVICE_ATTR(status, S_IRUGO, il3945_show_status, NULL); - -static ssize_t -il3945_dump_error_log(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct il_priv *il = dev_get_drvdata(d); - char *p = (char *)buf; - - if (p[0] == '1') - il3945_dump_nic_error_log(il); - - return strnlen(buf, count); -} - -static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, il3945_dump_error_log); - -/***************************************************************************** - * - * driver setup and tear down - * - *****************************************************************************/ - -static void -il3945_setup_deferred_work(struct il_priv *il) -{ - il->workqueue = create_singlethread_workqueue(DRV_NAME); - - init_waitqueue_head(&il->wait_command_queue); - - INIT_WORK(&il->restart, il3945_bg_restart); - INIT_WORK(&il->rx_replenish, il3945_bg_rx_replenish); - INIT_DELAYED_WORK(&il->init_alive_start, il3945_bg_init_alive_start); - INIT_DELAYED_WORK(&il->alive_start, il3945_bg_alive_start); - INIT_DELAYED_WORK(&il->_3945.rfkill_poll, il3945_rfkill_poll); - - il_setup_scan_deferred_work(il); - - il3945_hw_setup_deferred_work(il); - - setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il); - - tasklet_init(&il->irq_tasklet, - (void (*)(unsigned long))il3945_irq_tasklet, - (unsigned long)il); -} - -static void -il3945_cancel_deferred_work(struct il_priv *il) -{ - il3945_hw_cancel_deferred_work(il); - - cancel_delayed_work_sync(&il->init_alive_start); - cancel_delayed_work(&il->alive_start); - - il_cancel_scan_deferred_work(il); -} - -static struct attribute *il3945_sysfs_entries[] = { - &dev_attr_antenna.attr, - &dev_attr_channels.attr, - &dev_attr_dump_errors.attr, - &dev_attr_flags.attr, - &dev_attr_filter_flags.attr, - &dev_attr_measurement.attr, - &dev_attr_retry_rate.attr, - &dev_attr_status.attr, - &dev_attr_temperature.attr, - &dev_attr_tx_power.attr, -#ifdef CONFIG_IWLEGACY_DEBUG - &dev_attr_debug_level.attr, -#endif - NULL -}; - -static struct attribute_group il3945_attribute_group = { - .name = NULL, /* put in device directory */ - .attrs = il3945_sysfs_entries, -}; - -static struct ieee80211_ops il3945_mac_ops __read_mostly = { - .tx = il3945_mac_tx, - .start = il3945_mac_start, - .stop = il3945_mac_stop, - .add_interface = il_mac_add_interface, - .remove_interface = il_mac_remove_interface, - .change_interface = il_mac_change_interface, - .config = il_mac_config, - .configure_filter = il3945_configure_filter, - .set_key = il3945_mac_set_key, - .conf_tx = il_mac_conf_tx, - .reset_tsf = il_mac_reset_tsf, - .bss_info_changed = il_mac_bss_info_changed, - .hw_scan = il_mac_hw_scan, - .sta_add = il3945_mac_sta_add, - .sta_remove = il_mac_sta_remove, - .tx_last_beacon = il_mac_tx_last_beacon, - .flush = il_mac_flush, -}; - -static int -il3945_init_drv(struct il_priv *il) -{ - int ret; - struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; - - il->retry_rate = 1; - il->beacon_skb = NULL; - - spin_lock_init(&il->sta_lock); - spin_lock_init(&il->hcmd_lock); - - INIT_LIST_HEAD(&il->free_frames); - - mutex_init(&il->mutex); - - il->ieee_channels = NULL; - il->ieee_rates = NULL; - il->band = IEEE80211_BAND_2GHZ; - - il->iw_mode = NL80211_IFTYPE_STATION; - il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF; - - /* initialize force reset */ - il->force_reset.reset_duration = IL_DELAY_NEXT_FORCE_FW_RELOAD; - - if (eeprom->version < EEPROM_3945_EEPROM_VERSION) { - IL_WARN("Unsupported EEPROM version: 0x%04X\n", - eeprom->version); - ret = -EINVAL; - goto err; - } - ret = il_init_channel_map(il); - if (ret) { - IL_ERR("initializing regulatory failed: %d\n", ret); - goto err; - } - - /* Set up txpower settings in driver for all channels */ - if (il3945_txpower_set_from_eeprom(il)) { - ret = -EIO; - goto err_free_channel_map; - } - - ret = il_init_geos(il); - if (ret) { - IL_ERR("initializing geos failed: %d\n", ret); - goto err_free_channel_map; - } - il3945_init_hw_rates(il, il->ieee_rates); - - return 0; - -err_free_channel_map: - il_free_channel_map(il); -err: - return ret; -} - -#define IL3945_MAX_PROBE_REQUEST 200 - -static int -il3945_setup_mac(struct il_priv *il) -{ - int ret; - struct ieee80211_hw *hw = il->hw; - - hw->rate_control_algorithm = "iwl-3945-rs"; - hw->sta_data_size = sizeof(struct il3945_sta_priv); - hw->vif_data_size = sizeof(struct il_vif_priv); - - /* Tell mac80211 our characteristics */ - ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); - ieee80211_hw_set(hw, SUPPORTS_PS); - ieee80211_hw_set(hw, SIGNAL_DBM); - ieee80211_hw_set(hw, SPECTRUM_MGMT); - - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); - - hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | - REGULATORY_DISABLE_BEACON_HINTS; - - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX_3945; - /* we create the 802.11 header and a zero-length SSID element */ - hw->wiphy->max_scan_ie_len = IL3945_MAX_PROBE_REQUEST - 24 - 2; - - /* Default value; 4 EDCA QOS priorities */ - hw->queues = 4; - - if (il->bands[IEEE80211_BAND_2GHZ].n_channels) - il->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &il->bands[IEEE80211_BAND_2GHZ]; - - if (il->bands[IEEE80211_BAND_5GHZ].n_channels) - il->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &il->bands[IEEE80211_BAND_5GHZ]; - - il_leds_init(il); - - ret = ieee80211_register_hw(il->hw); - if (ret) { - IL_ERR("Failed to register hw (error %d)\n", ret); - return ret; - } - il->mac80211_registered = 1; - - return 0; -} - -static int -il3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = 0; - struct il_priv *il; - struct ieee80211_hw *hw; - struct il_cfg *cfg = (struct il_cfg *)(ent->driver_data); - struct il3945_eeprom *eeprom; - unsigned long flags; - - /*********************** - * 1. Allocating HW data - * ********************/ - - hw = ieee80211_alloc_hw(sizeof(struct il_priv), &il3945_mac_ops); - if (!hw) { - err = -ENOMEM; - goto out; - } - il = hw->priv; - il->hw = hw; - SET_IEEE80211_DEV(hw, &pdev->dev); - - il->cmd_queue = IL39_CMD_QUEUE_NUM; - - /* - * Disabling hardware scan means that mac80211 will perform scans - * "the hard way", rather than using device's scan. - */ - if (il3945_mod_params.disable_hw_scan) { - D_INFO("Disabling hw_scan\n"); - il3945_mac_ops.hw_scan = NULL; - } - - D_INFO("*** LOAD DRIVER ***\n"); - il->cfg = cfg; - il->ops = &il3945_ops; -#ifdef CONFIG_IWLEGACY_DEBUGFS - il->debugfs_ops = &il3945_debugfs_ops; -#endif - il->pci_dev = pdev; - il->inta_mask = CSR_INI_SET_MASK; - - /*************************** - * 2. Initializing PCI bus - * *************************/ - pci_disable_link_state(pdev, - PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | - PCIE_LINK_STATE_CLKPM); - - if (pci_enable_device(pdev)) { - err = -ENODEV; - goto out_ieee80211_free_hw; - } - - pci_set_master(pdev); - - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (!err) - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (err) { - IL_WARN("No suitable DMA available.\n"); - goto out_pci_disable_device; - } - - pci_set_drvdata(pdev, il); - err = pci_request_regions(pdev, DRV_NAME); - if (err) - goto out_pci_disable_device; - - /*********************** - * 3. Read REV Register - * ********************/ - il->hw_base = pci_ioremap_bar(pdev, 0); - if (!il->hw_base) { - err = -ENODEV; - goto out_pci_release_regions; - } - - D_INFO("pci_resource_len = 0x%08llx\n", - (unsigned long long)pci_resource_len(pdev, 0)); - D_INFO("pci_resource_base = %p\n", il->hw_base); - - /* We disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state */ - pci_write_config_byte(pdev, 0x41, 0x00); - - /* these spin locks will be used in apm_init and EEPROM access - * we should init now - */ - spin_lock_init(&il->reg_lock); - spin_lock_init(&il->lock); - - /* - * stop and reset the on-board processor just in case it is in a - * strange state ... like being left stranded by a primary kernel - * and this is now the kdump kernel trying to start up - */ - _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); - - /*********************** - * 4. Read EEPROM - * ********************/ - - /* Read the EEPROM */ - err = il_eeprom_init(il); - if (err) { - IL_ERR("Unable to init EEPROM\n"); - goto out_iounmap; - } - /* MAC Address location in EEPROM same for 3945/4965 */ - eeprom = (struct il3945_eeprom *)il->eeprom; - D_INFO("MAC address: %pM\n", eeprom->mac_address); - SET_IEEE80211_PERM_ADDR(il->hw, eeprom->mac_address); - - /*********************** - * 5. Setup HW Constants - * ********************/ - /* Device-specific setup */ - err = il3945_hw_set_hw_params(il); - if (err) { - IL_ERR("failed to set hw settings\n"); - goto out_eeprom_free; - } - - /*********************** - * 6. Setup il - * ********************/ - - err = il3945_init_drv(il); - if (err) { - IL_ERR("initializing driver failed\n"); - goto out_unset_hw_params; - } - - IL_INFO("Detected Intel Wireless WiFi Link %s\n", il->cfg->name); - - /*********************** - * 7. Setup Services - * ********************/ - - spin_lock_irqsave(&il->lock, flags); - il_disable_interrupts(il); - spin_unlock_irqrestore(&il->lock, flags); - - pci_enable_msi(il->pci_dev); - - err = request_irq(il->pci_dev->irq, il_isr, IRQF_SHARED, DRV_NAME, il); - if (err) { - IL_ERR("Error allocating IRQ %d\n", il->pci_dev->irq); - goto out_disable_msi; - } - - err = sysfs_create_group(&pdev->dev.kobj, &il3945_attribute_group); - if (err) { - IL_ERR("failed to create sysfs device attributes\n"); - goto out_release_irq; - } - - il_set_rxon_channel(il, &il->bands[IEEE80211_BAND_2GHZ].channels[5]); - il3945_setup_deferred_work(il); - il3945_setup_handlers(il); - il_power_initialize(il); - - /********************************* - * 8. Setup and Register mac80211 - * *******************************/ - - il_enable_interrupts(il); - - err = il3945_setup_mac(il); - if (err) - goto out_remove_sysfs; - - err = il_dbgfs_register(il, DRV_NAME); - if (err) - IL_ERR("failed to create debugfs files. Ignoring error: %d\n", - err); - - /* Start monitoring the killswitch */ - queue_delayed_work(il->workqueue, &il->_3945.rfkill_poll, 2 * HZ); - - return 0; - -out_remove_sysfs: - destroy_workqueue(il->workqueue); - il->workqueue = NULL; - sysfs_remove_group(&pdev->dev.kobj, &il3945_attribute_group); -out_release_irq: - free_irq(il->pci_dev->irq, il); -out_disable_msi: - pci_disable_msi(il->pci_dev); - il_free_geos(il); - il_free_channel_map(il); -out_unset_hw_params: - il3945_unset_hw_params(il); -out_eeprom_free: - il_eeprom_free(il); -out_iounmap: - iounmap(il->hw_base); -out_pci_release_regions: - pci_release_regions(pdev); -out_pci_disable_device: - pci_disable_device(pdev); -out_ieee80211_free_hw: - ieee80211_free_hw(il->hw); -out: - return err; -} - -static void -il3945_pci_remove(struct pci_dev *pdev) -{ - struct il_priv *il = pci_get_drvdata(pdev); - unsigned long flags; - - if (!il) - return; - - D_INFO("*** UNLOAD DRIVER ***\n"); - - il_dbgfs_unregister(il); - - set_bit(S_EXIT_PENDING, &il->status); - - il_leds_exit(il); - - if (il->mac80211_registered) { - ieee80211_unregister_hw(il->hw); - il->mac80211_registered = 0; - } else { - il3945_down(il); - } - - /* - * Make sure device is reset to low power before unloading driver. - * This may be redundant with il_down(), but there are paths to - * run il_down() without calling apm_ops.stop(), and there are - * paths to avoid running il_down() at all before leaving driver. - * This (inexpensive) call *makes sure* device is reset. - */ - il_apm_stop(il); - - /* make sure we flush any pending irq or - * tasklet for the driver - */ - spin_lock_irqsave(&il->lock, flags); - il_disable_interrupts(il); - spin_unlock_irqrestore(&il->lock, flags); - - il3945_synchronize_irq(il); - - sysfs_remove_group(&pdev->dev.kobj, &il3945_attribute_group); - - cancel_delayed_work_sync(&il->_3945.rfkill_poll); - - il3945_dealloc_ucode_pci(il); - - if (il->rxq.bd) - il3945_rx_queue_free(il, &il->rxq); - il3945_hw_txq_ctx_free(il); - - il3945_unset_hw_params(il); - - /*netif_stop_queue(dev); */ - flush_workqueue(il->workqueue); - - /* ieee80211_unregister_hw calls il3945_mac_stop, which flushes - * il->workqueue... so we can't take down the workqueue - * until now... */ - destroy_workqueue(il->workqueue); - il->workqueue = NULL; - - free_irq(pdev->irq, il); - pci_disable_msi(pdev); - - iounmap(il->hw_base); - pci_release_regions(pdev); - pci_disable_device(pdev); - - il_free_channel_map(il); - il_free_geos(il); - kfree(il->scan_cmd); - if (il->beacon_skb) - dev_kfree_skb(il->beacon_skb); - - ieee80211_free_hw(il->hw); -} - -/***************************************************************************** - * - * driver and module entry point - * - *****************************************************************************/ - -static struct pci_driver il3945_driver = { - .name = DRV_NAME, - .id_table = il3945_hw_card_ids, - .probe = il3945_pci_probe, - .remove = il3945_pci_remove, - .driver.pm = IL_LEGACY_PM_OPS, -}; - -static int __init -il3945_init(void) -{ - - int ret; - pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); - pr_info(DRV_COPYRIGHT "\n"); - - ret = il3945_rate_control_register(); - if (ret) { - pr_err("Unable to register rate control algorithm: %d\n", ret); - return ret; - } - - ret = pci_register_driver(&il3945_driver); - if (ret) { - pr_err("Unable to initialize PCI module\n"); - goto error_register; - } - - return ret; - -error_register: - il3945_rate_control_unregister(); - return ret; -} - -static void __exit -il3945_exit(void) -{ - pci_unregister_driver(&il3945_driver); - il3945_rate_control_unregister(); -} - -MODULE_FIRMWARE(IL3945_MODULE_FIRMWARE(IL3945_UCODE_API_MAX)); - -module_param_named(antenna, il3945_mod_params.antenna, int, S_IRUGO); -MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); -module_param_named(swcrypto, il3945_mod_params.sw_crypto, int, S_IRUGO); -MODULE_PARM_DESC(swcrypto, "using software crypto (default 1 [software])"); -module_param_named(disable_hw_scan, il3945_mod_params.disable_hw_scan, int, - S_IRUGO); -MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 1)"); -#ifdef CONFIG_IWLEGACY_DEBUG -module_param_named(debug, il_debug_level, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "debug output mask"); -#endif -module_param_named(fw_restart, il3945_mod_params.restart_fw, int, S_IRUGO); -MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); - -module_exit(il3945_exit); -module_init(il3945_init); diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/iwlegacy/3945-rs.c deleted file mode 100644 index 76b0729ade17..000000000000 --- a/drivers/net/wireless/iwlegacy/3945-rs.c +++ /dev/null @@ -1,979 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "commands.h" -#include "3945.h" - -#define RS_NAME "iwl-3945-rs" - -static s32 il3945_expected_tpt_g[RATE_COUNT_3945] = { - 7, 13, 35, 58, 0, 0, 76, 104, 130, 168, 191, 202 -}; - -static s32 il3945_expected_tpt_g_prot[RATE_COUNT_3945] = { - 7, 13, 35, 58, 0, 0, 0, 80, 93, 113, 123, 125 -}; - -static s32 il3945_expected_tpt_a[RATE_COUNT_3945] = { - 0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186 -}; - -static s32 il3945_expected_tpt_b[RATE_COUNT_3945] = { - 7, 13, 35, 58, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -struct il3945_tpt_entry { - s8 min_rssi; - u8 idx; -}; - -static struct il3945_tpt_entry il3945_tpt_table_a[] = { - {-60, RATE_54M_IDX}, - {-64, RATE_48M_IDX}, - {-72, RATE_36M_IDX}, - {-80, RATE_24M_IDX}, - {-84, RATE_18M_IDX}, - {-85, RATE_12M_IDX}, - {-87, RATE_9M_IDX}, - {-89, RATE_6M_IDX} -}; - -static struct il3945_tpt_entry il3945_tpt_table_g[] = { - {-60, RATE_54M_IDX}, - {-64, RATE_48M_IDX}, - {-68, RATE_36M_IDX}, - {-80, RATE_24M_IDX}, - {-84, RATE_18M_IDX}, - {-85, RATE_12M_IDX}, - {-86, RATE_11M_IDX}, - {-88, RATE_5M_IDX}, - {-90, RATE_2M_IDX}, - {-92, RATE_1M_IDX} -}; - -#define RATE_MAX_WINDOW 62 -#define RATE_FLUSH (3*HZ) -#define RATE_WIN_FLUSH (HZ/2) -#define IL39_RATE_HIGH_TH 11520 -#define IL_SUCCESS_UP_TH 8960 -#define IL_SUCCESS_DOWN_TH 10880 -#define RATE_MIN_FAILURE_TH 6 -#define RATE_MIN_SUCCESS_TH 8 -#define RATE_DECREASE_TH 1920 -#define RATE_RETRY_TH 15 - -static u8 -il3945_get_rate_idx_by_rssi(s32 rssi, enum ieee80211_band band) -{ - u32 idx = 0; - u32 table_size = 0; - struct il3945_tpt_entry *tpt_table = NULL; - - if (rssi < IL_MIN_RSSI_VAL || rssi > IL_MAX_RSSI_VAL) - rssi = IL_MIN_RSSI_VAL; - - switch (band) { - case IEEE80211_BAND_2GHZ: - tpt_table = il3945_tpt_table_g; - table_size = ARRAY_SIZE(il3945_tpt_table_g); - break; - case IEEE80211_BAND_5GHZ: - tpt_table = il3945_tpt_table_a; - table_size = ARRAY_SIZE(il3945_tpt_table_a); - break; - default: - BUG(); - break; - } - - while (idx < table_size && rssi < tpt_table[idx].min_rssi) - idx++; - - idx = min(idx, table_size - 1); - - return tpt_table[idx].idx; -} - -static void -il3945_clear_win(struct il3945_rate_scale_data *win) -{ - win->data = 0; - win->success_counter = 0; - win->success_ratio = -1; - win->counter = 0; - win->average_tpt = IL_INVALID_VALUE; - win->stamp = 0; -} - -/** - * il3945_rate_scale_flush_wins - flush out the rate scale wins - * - * Returns the number of wins that have gathered data but were - * not flushed. If there were any that were not flushed, then - * reschedule the rate flushing routine. - */ -static int -il3945_rate_scale_flush_wins(struct il3945_rs_sta *rs_sta) -{ - int unflushed = 0; - int i; - unsigned long flags; - struct il_priv *il __maybe_unused = rs_sta->il; - - /* - * For each rate, if we have collected data on that rate - * and it has been more than RATE_WIN_FLUSH - * since we flushed, clear out the gathered stats - */ - for (i = 0; i < RATE_COUNT_3945; i++) { - if (!rs_sta->win[i].counter) - continue; - - spin_lock_irqsave(&rs_sta->lock, flags); - if (time_after(jiffies, rs_sta->win[i].stamp + RATE_WIN_FLUSH)) { - D_RATE("flushing %d samples of rate " "idx %d\n", - rs_sta->win[i].counter, i); - il3945_clear_win(&rs_sta->win[i]); - } else - unflushed++; - spin_unlock_irqrestore(&rs_sta->lock, flags); - } - - return unflushed; -} - -#define RATE_FLUSH_MAX 5000 /* msec */ -#define RATE_FLUSH_MIN 50 /* msec */ -#define IL_AVERAGE_PACKETS 1500 - -static void -il3945_bg_rate_scale_flush(unsigned long data) -{ - struct il3945_rs_sta *rs_sta = (void *)data; - struct il_priv *il __maybe_unused = rs_sta->il; - int unflushed = 0; - unsigned long flags; - u32 packet_count, duration, pps; - - D_RATE("enter\n"); - - unflushed = il3945_rate_scale_flush_wins(rs_sta); - - spin_lock_irqsave(&rs_sta->lock, flags); - - /* Number of packets Rx'd since last time this timer ran */ - packet_count = (rs_sta->tx_packets - rs_sta->last_tx_packets) + 1; - - rs_sta->last_tx_packets = rs_sta->tx_packets + 1; - - if (unflushed) { - duration = - jiffies_to_msecs(jiffies - rs_sta->last_partial_flush); - - D_RATE("Tx'd %d packets in %dms\n", packet_count, duration); - - /* Determine packets per second */ - if (duration) - pps = (packet_count * 1000) / duration; - else - pps = 0; - - if (pps) { - duration = (IL_AVERAGE_PACKETS * 1000) / pps; - if (duration < RATE_FLUSH_MIN) - duration = RATE_FLUSH_MIN; - else if (duration > RATE_FLUSH_MAX) - duration = RATE_FLUSH_MAX; - } else - duration = RATE_FLUSH_MAX; - - rs_sta->flush_time = msecs_to_jiffies(duration); - - D_RATE("new flush period: %d msec ave %d\n", duration, - packet_count); - - mod_timer(&rs_sta->rate_scale_flush, - jiffies + rs_sta->flush_time); - - rs_sta->last_partial_flush = jiffies; - } else { - rs_sta->flush_time = RATE_FLUSH; - rs_sta->flush_pending = 0; - } - /* If there weren't any unflushed entries, we don't schedule the timer - * to run again */ - - rs_sta->last_flush = jiffies; - - spin_unlock_irqrestore(&rs_sta->lock, flags); - - D_RATE("leave\n"); -} - -/** - * il3945_collect_tx_data - Update the success/failure sliding win - * - * We keep a sliding win of the last 64 packets transmitted - * at this rate. win->data contains the bitmask of successful - * packets. - */ -static void -il3945_collect_tx_data(struct il3945_rs_sta *rs_sta, - struct il3945_rate_scale_data *win, int success, - int retries, int idx) -{ - unsigned long flags; - s32 fail_count; - struct il_priv *il __maybe_unused = rs_sta->il; - - if (!retries) { - D_RATE("leave: retries == 0 -- should be at least 1\n"); - return; - } - - spin_lock_irqsave(&rs_sta->lock, flags); - - /* - * Keep track of only the latest 62 tx frame attempts in this rate's - * history win; anything older isn't really relevant any more. - * If we have filled up the sliding win, drop the oldest attempt; - * if the oldest attempt (highest bit in bitmap) shows "success", - * subtract "1" from the success counter (this is the main reason - * we keep these bitmaps!). - * */ - while (retries > 0) { - if (win->counter >= RATE_MAX_WINDOW) { - - /* remove earliest */ - win->counter = RATE_MAX_WINDOW - 1; - - if (win->data & (1ULL << (RATE_MAX_WINDOW - 1))) { - win->data &= ~(1ULL << (RATE_MAX_WINDOW - 1)); - win->success_counter--; - } - } - - /* Increment frames-attempted counter */ - win->counter++; - - /* Shift bitmap by one frame (throw away oldest history), - * OR in "1", and increment "success" if this - * frame was successful. */ - win->data <<= 1; - if (success > 0) { - win->success_counter++; - win->data |= 0x1; - success--; - } - - retries--; - } - - /* Calculate current success ratio, avoid divide-by-0! */ - if (win->counter > 0) - win->success_ratio = - 128 * (100 * win->success_counter) / win->counter; - else - win->success_ratio = IL_INVALID_VALUE; - - fail_count = win->counter - win->success_counter; - - /* Calculate average throughput, if we have enough history. */ - if (fail_count >= RATE_MIN_FAILURE_TH || - win->success_counter >= RATE_MIN_SUCCESS_TH) - win->average_tpt = - ((win->success_ratio * rs_sta->expected_tpt[idx] + - 64) / 128); - else - win->average_tpt = IL_INVALID_VALUE; - - /* Tag this win as having been updated */ - win->stamp = jiffies; - - spin_unlock_irqrestore(&rs_sta->lock, flags); -} - -/* - * Called after adding a new station to initialize rate scaling - */ -void -il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, u8 sta_id) -{ - struct ieee80211_hw *hw = il->hw; - struct ieee80211_conf *conf = &il->hw->conf; - struct il3945_sta_priv *psta; - struct il3945_rs_sta *rs_sta; - struct ieee80211_supported_band *sband; - int i; - - D_INFO("enter\n"); - if (sta_id == il->hw_params.bcast_id) - goto out; - - psta = (struct il3945_sta_priv *)sta->drv_priv; - rs_sta = &psta->rs_sta; - sband = hw->wiphy->bands[conf->chandef.chan->band]; - - rs_sta->il = il; - - rs_sta->start_rate = RATE_INVALID; - - /* default to just 802.11b */ - rs_sta->expected_tpt = il3945_expected_tpt_b; - - rs_sta->last_partial_flush = jiffies; - rs_sta->last_flush = jiffies; - rs_sta->flush_time = RATE_FLUSH; - rs_sta->last_tx_packets = 0; - - rs_sta->rate_scale_flush.data = (unsigned long)rs_sta; - rs_sta->rate_scale_flush.function = il3945_bg_rate_scale_flush; - - for (i = 0; i < RATE_COUNT_3945; i++) - il3945_clear_win(&rs_sta->win[i]); - - /* TODO: what is a good starting rate for STA? About middle? Maybe not - * the lowest or the highest rate.. Could consider using RSSI from - * previous packets? Need to have IEEE 802.1X auth succeed immediately - * after assoc.. */ - - for (i = sband->n_bitrates - 1; i >= 0; i--) { - if (sta->supp_rates[sband->band] & (1 << i)) { - rs_sta->last_txrate_idx = i; - break; - } - } - - il->_3945.sta_supp_rates = sta->supp_rates[sband->band]; - /* For 5 GHz band it start at IL_FIRST_OFDM_RATE */ - if (sband->band == IEEE80211_BAND_5GHZ) { - rs_sta->last_txrate_idx += IL_FIRST_OFDM_RATE; - il->_3945.sta_supp_rates <<= IL_FIRST_OFDM_RATE; - } - -out: - il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS; - - D_INFO("leave\n"); -} - -static void * -il3945_rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) -{ - return hw->priv; -} - -/* rate scale requires free function to be implemented */ -static void -il3945_rs_free(void *il) -{ -} - -static void * -il3945_rs_alloc_sta(void *il_priv, struct ieee80211_sta *sta, gfp_t gfp) -{ - struct il3945_rs_sta *rs_sta; - struct il3945_sta_priv *psta = (void *)sta->drv_priv; - struct il_priv *il __maybe_unused = il_priv; - - D_RATE("enter\n"); - - rs_sta = &psta->rs_sta; - - spin_lock_init(&rs_sta->lock); - init_timer(&rs_sta->rate_scale_flush); - - D_RATE("leave\n"); - - return rs_sta; -} - -static void -il3945_rs_free_sta(void *il_priv, struct ieee80211_sta *sta, void *il_sta) -{ - struct il3945_rs_sta *rs_sta = il_sta; - - /* - * Be careful not to use any members of il3945_rs_sta (like trying - * to use il_priv to print out debugging) since it may not be fully - * initialized at this point. - */ - del_timer_sync(&rs_sta->rate_scale_flush); -} - -/** - * il3945_rs_tx_status - Update rate control values based on Tx results - * - * NOTE: Uses il_priv->retry_rate for the # of retries attempted by - * the hardware for each rate. - */ -static void -il3945_rs_tx_status(void *il_rate, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *il_sta, - struct sk_buff *skb) -{ - s8 retries = 0, current_count; - int scale_rate_idx, first_idx, last_idx; - unsigned long flags; - struct il_priv *il = (struct il_priv *)il_rate; - struct il3945_rs_sta *rs_sta = il_sta; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - D_RATE("enter\n"); - - retries = info->status.rates[0].count; - /* Sanity Check for retries */ - if (retries > RATE_RETRY_TH) - retries = RATE_RETRY_TH; - - first_idx = sband->bitrates[info->status.rates[0].idx].hw_value; - if (first_idx < 0 || first_idx >= RATE_COUNT_3945) { - D_RATE("leave: Rate out of bounds: %d\n", first_idx); - return; - } - - if (!il_sta) { - D_RATE("leave: No STA il data to update!\n"); - return; - } - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (!rs_sta->il) { - D_RATE("leave: STA il data uninitialized!\n"); - return; - } - - rs_sta->tx_packets++; - - scale_rate_idx = first_idx; - last_idx = first_idx; - - /* - * Update the win for each rate. We determine which rates - * were Tx'd based on the total number of retries vs. the number - * of retries configured for each rate -- currently set to the - * il value 'retry_rate' vs. rate specific - * - * On exit from this while loop last_idx indicates the rate - * at which the frame was finally transmitted (or failed if no - * ACK) - */ - while (retries > 1) { - if ((retries - 1) < il->retry_rate) { - current_count = (retries - 1); - last_idx = scale_rate_idx; - } else { - current_count = il->retry_rate; - last_idx = il3945_rs_next_rate(il, scale_rate_idx); - } - - /* Update this rate accounting for as many retries - * as was used for it (per current_count) */ - il3945_collect_tx_data(rs_sta, &rs_sta->win[scale_rate_idx], 0, - current_count, scale_rate_idx); - D_RATE("Update rate %d for %d retries.\n", scale_rate_idx, - current_count); - - retries -= current_count; - - scale_rate_idx = last_idx; - } - - /* Update the last idx win with success/failure based on ACK */ - D_RATE("Update rate %d with %s.\n", last_idx, - (info->flags & IEEE80211_TX_STAT_ACK) ? "success" : "failure"); - il3945_collect_tx_data(rs_sta, &rs_sta->win[last_idx], - info->flags & IEEE80211_TX_STAT_ACK, 1, - last_idx); - - /* We updated the rate scale win -- if its been more than - * flush_time since the last run, schedule the flush - * again */ - spin_lock_irqsave(&rs_sta->lock, flags); - - if (!rs_sta->flush_pending && - time_after(jiffies, rs_sta->last_flush + rs_sta->flush_time)) { - - rs_sta->last_partial_flush = jiffies; - rs_sta->flush_pending = 1; - mod_timer(&rs_sta->rate_scale_flush, - jiffies + rs_sta->flush_time); - } - - spin_unlock_irqrestore(&rs_sta->lock, flags); - - D_RATE("leave\n"); -} - -static u16 -il3945_get_adjacent_rate(struct il3945_rs_sta *rs_sta, u8 idx, u16 rate_mask, - enum ieee80211_band band) -{ - u8 high = RATE_INVALID; - u8 low = RATE_INVALID; - struct il_priv *il __maybe_unused = rs_sta->il; - - /* 802.11A walks to the next literal adjacent rate in - * the rate table */ - if (unlikely(band == IEEE80211_BAND_5GHZ)) { - int i; - u32 mask; - - /* Find the previous rate that is in the rate mask */ - i = idx - 1; - for (mask = (1 << i); i >= 0; i--, mask >>= 1) { - if (rate_mask & mask) { - low = i; - break; - } - } - - /* Find the next rate that is in the rate mask */ - i = idx + 1; - for (mask = (1 << i); i < RATE_COUNT_3945; i++, mask <<= 1) { - if (rate_mask & mask) { - high = i; - break; - } - } - - return (high << 8) | low; - } - - low = idx; - while (low != RATE_INVALID) { - if (rs_sta->tgg) - low = il3945_rates[low].prev_rs_tgg; - else - low = il3945_rates[low].prev_rs; - if (low == RATE_INVALID) - break; - if (rate_mask & (1 << low)) - break; - D_RATE("Skipping masked lower rate: %d\n", low); - } - - high = idx; - while (high != RATE_INVALID) { - if (rs_sta->tgg) - high = il3945_rates[high].next_rs_tgg; - else - high = il3945_rates[high].next_rs; - if (high == RATE_INVALID) - break; - if (rate_mask & (1 << high)) - break; - D_RATE("Skipping masked higher rate: %d\n", high); - } - - return (high << 8) | low; -} - -/** - * il3945_rs_get_rate - find the rate for the requested packet - * - * Returns the ieee80211_rate structure allocated by the driver. - * - * The rate control algorithm has no internal mapping between hw_mode's - * rate ordering and the rate ordering used by the rate control algorithm. - * - * The rate control algorithm uses a single table of rates that goes across - * the entire A/B/G spectrum vs. being limited to just one particular - * hw_mode. - * - * As such, we can't convert the idx obtained below into the hw_mode's - * rate table and must reference the driver allocated rate table - * - */ -static void -il3945_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta, - struct ieee80211_tx_rate_control *txrc) -{ - struct ieee80211_supported_band *sband = txrc->sband; - struct sk_buff *skb = txrc->skb; - u8 low = RATE_INVALID; - u8 high = RATE_INVALID; - u16 high_low; - int idx; - struct il3945_rs_sta *rs_sta = il_sta; - struct il3945_rate_scale_data *win = NULL; - int current_tpt = IL_INVALID_VALUE; - int low_tpt = IL_INVALID_VALUE; - int high_tpt = IL_INVALID_VALUE; - u32 fail_count; - s8 scale_action = 0; - unsigned long flags; - u16 rate_mask; - s8 max_rate_idx = -1; - struct il_priv *il __maybe_unused = (struct il_priv *)il_r; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - D_RATE("enter\n"); - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (rs_sta && !rs_sta->il) { - D_RATE("Rate scaling information not initialized yet.\n"); - il_sta = NULL; - } - - if (rate_control_send_low(sta, il_sta, txrc)) - return; - - rate_mask = sta->supp_rates[sband->band]; - - /* get user max rate if set */ - max_rate_idx = txrc->max_rate_idx; - if (sband->band == IEEE80211_BAND_5GHZ && max_rate_idx != -1) - max_rate_idx += IL_FIRST_OFDM_RATE; - if (max_rate_idx < 0 || max_rate_idx >= RATE_COUNT) - max_rate_idx = -1; - - idx = min(rs_sta->last_txrate_idx & 0xffff, RATE_COUNT_3945 - 1); - - if (sband->band == IEEE80211_BAND_5GHZ) - rate_mask = rate_mask << IL_FIRST_OFDM_RATE; - - spin_lock_irqsave(&rs_sta->lock, flags); - - /* for recent assoc, choose best rate regarding - * to rssi value - */ - if (rs_sta->start_rate != RATE_INVALID) { - if (rs_sta->start_rate < idx && - (rate_mask & (1 << rs_sta->start_rate))) - idx = rs_sta->start_rate; - rs_sta->start_rate = RATE_INVALID; - } - - /* force user max rate if set by user */ - if (max_rate_idx != -1 && max_rate_idx < idx) { - if (rate_mask & (1 << max_rate_idx)) - idx = max_rate_idx; - } - - win = &(rs_sta->win[idx]); - - fail_count = win->counter - win->success_counter; - - if (fail_count < RATE_MIN_FAILURE_TH && - win->success_counter < RATE_MIN_SUCCESS_TH) { - spin_unlock_irqrestore(&rs_sta->lock, flags); - - D_RATE("Invalid average_tpt on rate %d: " - "counter: %d, success_counter: %d, " - "expected_tpt is %sNULL\n", idx, win->counter, - win->success_counter, - rs_sta->expected_tpt ? "not " : ""); - - /* Can't calculate this yet; not enough history */ - win->average_tpt = IL_INVALID_VALUE; - goto out; - - } - - current_tpt = win->average_tpt; - - high_low = - il3945_get_adjacent_rate(rs_sta, idx, rate_mask, sband->band); - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - /* If user set max rate, dont allow higher than user constrain */ - if (max_rate_idx != -1 && max_rate_idx < high) - high = RATE_INVALID; - - /* Collect Measured throughputs of adjacent rates */ - if (low != RATE_INVALID) - low_tpt = rs_sta->win[low].average_tpt; - - if (high != RATE_INVALID) - high_tpt = rs_sta->win[high].average_tpt; - - spin_unlock_irqrestore(&rs_sta->lock, flags); - - scale_action = 0; - - /* Low success ratio , need to drop the rate */ - if (win->success_ratio < RATE_DECREASE_TH || !current_tpt) { - D_RATE("decrease rate because of low success_ratio\n"); - scale_action = -1; - /* No throughput measured yet for adjacent rates, - * try increase */ - } else if (low_tpt == IL_INVALID_VALUE && high_tpt == IL_INVALID_VALUE) { - - if (high != RATE_INVALID && - win->success_ratio >= RATE_INCREASE_TH) - scale_action = 1; - else if (low != RATE_INVALID) - scale_action = 0; - - /* Both adjacent throughputs are measured, but neither one has - * better throughput; we're using the best rate, don't change - * it! */ - } else if (low_tpt != IL_INVALID_VALUE && high_tpt != IL_INVALID_VALUE - && low_tpt < current_tpt && high_tpt < current_tpt) { - - D_RATE("No action -- low [%d] & high [%d] < " - "current_tpt [%d]\n", low_tpt, high_tpt, current_tpt); - scale_action = 0; - - /* At least one of the rates has better throughput */ - } else { - if (high_tpt != IL_INVALID_VALUE) { - - /* High rate has better throughput, Increase - * rate */ - if (high_tpt > current_tpt && - win->success_ratio >= RATE_INCREASE_TH) - scale_action = 1; - else { - D_RATE("decrease rate because of high tpt\n"); - scale_action = 0; - } - } else if (low_tpt != IL_INVALID_VALUE) { - if (low_tpt > current_tpt) { - D_RATE("decrease rate because of low tpt\n"); - scale_action = -1; - } else if (win->success_ratio >= RATE_INCREASE_TH) { - /* Lower rate has better - * throughput,decrease rate */ - scale_action = 1; - } - } - } - - /* Sanity check; asked for decrease, but success rate or throughput - * has been good at old rate. Don't change it. */ - if (scale_action == -1 && low != RATE_INVALID && - (win->success_ratio > RATE_HIGH_TH || - current_tpt > 100 * rs_sta->expected_tpt[low])) - scale_action = 0; - - switch (scale_action) { - case -1: - /* Decrese rate */ - if (low != RATE_INVALID) - idx = low; - break; - case 1: - /* Increase rate */ - if (high != RATE_INVALID) - idx = high; - - break; - case 0: - default: - /* No change */ - break; - } - - D_RATE("Selected %d (action %d) - low %d high %d\n", idx, scale_action, - low, high); - -out: - - if (sband->band == IEEE80211_BAND_5GHZ) { - if (WARN_ON_ONCE(idx < IL_FIRST_OFDM_RATE)) - idx = IL_FIRST_OFDM_RATE; - rs_sta->last_txrate_idx = idx; - info->control.rates[0].idx = idx - IL_FIRST_OFDM_RATE; - } else { - rs_sta->last_txrate_idx = idx; - info->control.rates[0].idx = rs_sta->last_txrate_idx; - } - info->control.rates[0].count = 1; - - D_RATE("leave: %d\n", idx); -} - -#ifdef CONFIG_MAC80211_DEBUGFS - -static ssize_t -il3945_sta_dbgfs_stats_table_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - char *buff; - int desc = 0; - int j; - ssize_t ret; - struct il3945_rs_sta *lq_sta = file->private_data; - - buff = kmalloc(1024, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - desc += - sprintf(buff + desc, - "tx packets=%d last rate idx=%d\n" - "rate=0x%X flush time %d\n", lq_sta->tx_packets, - lq_sta->last_txrate_idx, lq_sta->start_rate, - jiffies_to_msecs(lq_sta->flush_time)); - for (j = 0; j < RATE_COUNT_3945; j++) { - desc += - sprintf(buff + desc, "counter=%d success=%d %%=%d\n", - lq_sta->win[j].counter, - lq_sta->win[j].success_counter, - lq_sta->win[j].success_ratio); - } - ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); - kfree(buff); - return ret; -} - -static const struct file_operations rs_sta_dbgfs_stats_table_ops = { - .read = il3945_sta_dbgfs_stats_table_read, - .open = simple_open, - .llseek = default_llseek, -}; - -static void -il3945_add_debugfs(void *il, void *il_sta, struct dentry *dir) -{ - struct il3945_rs_sta *lq_sta = il_sta; - - lq_sta->rs_sta_dbgfs_stats_table_file = - debugfs_create_file("rate_stats_table", 0600, dir, lq_sta, - &rs_sta_dbgfs_stats_table_ops); - -} - -static void -il3945_remove_debugfs(void *il, void *il_sta) -{ - struct il3945_rs_sta *lq_sta = il_sta; - debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); -} -#endif - -/* - * Initialization of rate scaling information is done by driver after - * the station is added. Since mac80211 calls this function before a - * station is added we ignore it. - */ -static void -il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, - struct cfg80211_chan_def *chandef, - struct ieee80211_sta *sta, void *il_sta) -{ -} - -static const struct rate_control_ops rs_ops = { - .name = RS_NAME, - .tx_status = il3945_rs_tx_status, - .get_rate = il3945_rs_get_rate, - .rate_init = il3945_rs_rate_init_stub, - .alloc = il3945_rs_alloc, - .free = il3945_rs_free, - .alloc_sta = il3945_rs_alloc_sta, - .free_sta = il3945_rs_free_sta, -#ifdef CONFIG_MAC80211_DEBUGFS - .add_sta_debugfs = il3945_add_debugfs, - .remove_sta_debugfs = il3945_remove_debugfs, -#endif - -}; - -void -il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) -{ - struct il_priv *il = hw->priv; - s32 rssi = 0; - unsigned long flags; - struct il3945_rs_sta *rs_sta; - struct ieee80211_sta *sta; - struct il3945_sta_priv *psta; - - D_RATE("enter\n"); - - rcu_read_lock(); - - sta = ieee80211_find_sta(il->vif, il->stations[sta_id].sta.sta.addr); - if (!sta) { - D_RATE("Unable to find station to initialize rate scaling.\n"); - rcu_read_unlock(); - return; - } - - psta = (void *)sta->drv_priv; - rs_sta = &psta->rs_sta; - - spin_lock_irqsave(&rs_sta->lock, flags); - - rs_sta->tgg = 0; - switch (il->band) { - case IEEE80211_BAND_2GHZ: - /* TODO: this always does G, not a regression */ - if (il->active.flags & RXON_FLG_TGG_PROTECT_MSK) { - rs_sta->tgg = 1; - rs_sta->expected_tpt = il3945_expected_tpt_g_prot; - } else - rs_sta->expected_tpt = il3945_expected_tpt_g; - break; - case IEEE80211_BAND_5GHZ: - rs_sta->expected_tpt = il3945_expected_tpt_a; - break; - default: - BUG(); - break; - } - - spin_unlock_irqrestore(&rs_sta->lock, flags); - - rssi = il->_3945.last_rx_rssi; - if (rssi == 0) - rssi = IL_MIN_RSSI_VAL; - - D_RATE("Network RSSI: %d\n", rssi); - - rs_sta->start_rate = il3945_get_rate_idx_by_rssi(rssi, il->band); - - D_RATE("leave: rssi %d assign rate idx: " "%d (plcp 0x%x)\n", rssi, - rs_sta->start_rate, il3945_rates[rs_sta->start_rate].plcp); - rcu_read_unlock(); -} - -int -il3945_rate_control_register(void) -{ - return ieee80211_rate_control_register(&rs_ops); -} - -void -il3945_rate_control_unregister(void) -{ - ieee80211_rate_control_unregister(&rs_ops); -} diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c deleted file mode 100644 index 93bdf684babe..000000000000 --- a/drivers/net/wireless/iwlegacy/3945.c +++ /dev/null @@ -1,2741 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "3945.h" - -/* Send led command */ -static int -il3945_send_led_cmd(struct il_priv *il, struct il_led_cmd *led_cmd) -{ - struct il_host_cmd cmd = { - .id = C_LEDS, - .len = sizeof(struct il_led_cmd), - .data = led_cmd, - .flags = CMD_ASYNC, - .callback = NULL, - }; - - return il_send_cmd(il, &cmd); -} - -#define IL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np) \ - [RATE_##r##M_IDX] = { RATE_##r##M_PLCP, \ - RATE_##r##M_IEEE, \ - RATE_##ip##M_IDX, \ - RATE_##in##M_IDX, \ - RATE_##rp##M_IDX, \ - RATE_##rn##M_IDX, \ - RATE_##pp##M_IDX, \ - RATE_##np##M_IDX, \ - RATE_##r##M_IDX_TBL, \ - RATE_##ip##M_IDX_TBL } - -/* - * Parameter order: - * rate, prev rate, next rate, prev tgg rate, next tgg rate - * - * If there isn't a valid next or previous rate then INV is used which - * maps to RATE_INVALID - * - */ -const struct il3945_rate_info il3945_rates[RATE_COUNT_3945] = { - IL_DECLARE_RATE_INFO(1, INV, 2, INV, 2, INV, 2), /* 1mbps */ - IL_DECLARE_RATE_INFO(2, 1, 5, 1, 5, 1, 5), /* 2mbps */ - IL_DECLARE_RATE_INFO(5, 2, 6, 2, 11, 2, 11), /*5.5mbps */ - IL_DECLARE_RATE_INFO(11, 9, 12, 5, 12, 5, 18), /* 11mbps */ - IL_DECLARE_RATE_INFO(6, 5, 9, 5, 11, 5, 11), /* 6mbps */ - IL_DECLARE_RATE_INFO(9, 6, 11, 5, 11, 5, 11), /* 9mbps */ - IL_DECLARE_RATE_INFO(12, 11, 18, 11, 18, 11, 18), /* 12mbps */ - IL_DECLARE_RATE_INFO(18, 12, 24, 12, 24, 11, 24), /* 18mbps */ - IL_DECLARE_RATE_INFO(24, 18, 36, 18, 36, 18, 36), /* 24mbps */ - IL_DECLARE_RATE_INFO(36, 24, 48, 24, 48, 24, 48), /* 36mbps */ - IL_DECLARE_RATE_INFO(48, 36, 54, 36, 54, 36, 54), /* 48mbps */ - IL_DECLARE_RATE_INFO(54, 48, INV, 48, INV, 48, INV), /* 54mbps */ -}; - -static inline u8 -il3945_get_prev_ieee_rate(u8 rate_idx) -{ - u8 rate = il3945_rates[rate_idx].prev_ieee; - - if (rate == RATE_INVALID) - rate = rate_idx; - return rate; -} - -/* 1 = enable the il3945_disable_events() function */ -#define IL_EVT_DISABLE (0) -#define IL_EVT_DISABLE_SIZE (1532/32) - -/** - * il3945_disable_events - Disable selected events in uCode event log - * - * Disable an event by writing "1"s into "disable" - * bitmap in SRAM. Bit position corresponds to Event # (id/type). - * Default values of 0 enable uCode events to be logged. - * Use for only special debugging. This function is just a placeholder as-is, - * you'll need to provide the special bits! ... - * ... and set IL_EVT_DISABLE to 1. */ -void -il3945_disable_events(struct il_priv *il) -{ - int i; - u32 base; /* SRAM address of event log header */ - u32 disable_ptr; /* SRAM address of event-disable bitmap array */ - u32 array_size; /* # of u32 entries in array */ - static const u32 evt_disable[IL_EVT_DISABLE_SIZE] = { - 0x00000000, /* 31 - 0 Event id numbers */ - 0x00000000, /* 63 - 32 */ - 0x00000000, /* 95 - 64 */ - 0x00000000, /* 127 - 96 */ - 0x00000000, /* 159 - 128 */ - 0x00000000, /* 191 - 160 */ - 0x00000000, /* 223 - 192 */ - 0x00000000, /* 255 - 224 */ - 0x00000000, /* 287 - 256 */ - 0x00000000, /* 319 - 288 */ - 0x00000000, /* 351 - 320 */ - 0x00000000, /* 383 - 352 */ - 0x00000000, /* 415 - 384 */ - 0x00000000, /* 447 - 416 */ - 0x00000000, /* 479 - 448 */ - 0x00000000, /* 511 - 480 */ - 0x00000000, /* 543 - 512 */ - 0x00000000, /* 575 - 544 */ - 0x00000000, /* 607 - 576 */ - 0x00000000, /* 639 - 608 */ - 0x00000000, /* 671 - 640 */ - 0x00000000, /* 703 - 672 */ - 0x00000000, /* 735 - 704 */ - 0x00000000, /* 767 - 736 */ - 0x00000000, /* 799 - 768 */ - 0x00000000, /* 831 - 800 */ - 0x00000000, /* 863 - 832 */ - 0x00000000, /* 895 - 864 */ - 0x00000000, /* 927 - 896 */ - 0x00000000, /* 959 - 928 */ - 0x00000000, /* 991 - 960 */ - 0x00000000, /* 1023 - 992 */ - 0x00000000, /* 1055 - 1024 */ - 0x00000000, /* 1087 - 1056 */ - 0x00000000, /* 1119 - 1088 */ - 0x00000000, /* 1151 - 1120 */ - 0x00000000, /* 1183 - 1152 */ - 0x00000000, /* 1215 - 1184 */ - 0x00000000, /* 1247 - 1216 */ - 0x00000000, /* 1279 - 1248 */ - 0x00000000, /* 1311 - 1280 */ - 0x00000000, /* 1343 - 1312 */ - 0x00000000, /* 1375 - 1344 */ - 0x00000000, /* 1407 - 1376 */ - 0x00000000, /* 1439 - 1408 */ - 0x00000000, /* 1471 - 1440 */ - 0x00000000, /* 1503 - 1472 */ - }; - - base = le32_to_cpu(il->card_alive.log_event_table_ptr); - if (!il3945_hw_valid_rtc_data_addr(base)) { - IL_ERR("Invalid event log pointer 0x%08X\n", base); - return; - } - - disable_ptr = il_read_targ_mem(il, base + (4 * sizeof(u32))); - array_size = il_read_targ_mem(il, base + (5 * sizeof(u32))); - - if (IL_EVT_DISABLE && array_size == IL_EVT_DISABLE_SIZE) { - D_INFO("Disabling selected uCode log events at 0x%x\n", - disable_ptr); - for (i = 0; i < IL_EVT_DISABLE_SIZE; i++) - il_write_targ_mem(il, disable_ptr + (i * sizeof(u32)), - evt_disable[i]); - - } else { - D_INFO("Selected uCode log events may be disabled\n"); - D_INFO(" by writing \"1\"s into disable bitmap\n"); - D_INFO(" in SRAM at 0x%x, size %d u32s\n", disable_ptr, - array_size); - } - -} - -static int -il3945_hwrate_to_plcp_idx(u8 plcp) -{ - int idx; - - for (idx = 0; idx < RATE_COUNT_3945; idx++) - if (il3945_rates[idx].plcp == plcp) - return idx; - return -1; -} - -#ifdef CONFIG_IWLEGACY_DEBUG -#define TX_STATUS_ENTRY(x) case TX_3945_STATUS_FAIL_ ## x: return #x - -static const char * -il3945_get_tx_fail_reason(u32 status) -{ - switch (status & TX_STATUS_MSK) { - case TX_3945_STATUS_SUCCESS: - return "SUCCESS"; - TX_STATUS_ENTRY(SHORT_LIMIT); - TX_STATUS_ENTRY(LONG_LIMIT); - TX_STATUS_ENTRY(FIFO_UNDERRUN); - TX_STATUS_ENTRY(MGMNT_ABORT); - TX_STATUS_ENTRY(NEXT_FRAG); - TX_STATUS_ENTRY(LIFE_EXPIRE); - TX_STATUS_ENTRY(DEST_PS); - TX_STATUS_ENTRY(ABORTED); - TX_STATUS_ENTRY(BT_RETRY); - TX_STATUS_ENTRY(STA_INVALID); - TX_STATUS_ENTRY(FRAG_DROPPED); - TX_STATUS_ENTRY(TID_DISABLE); - TX_STATUS_ENTRY(FRAME_FLUSHED); - TX_STATUS_ENTRY(INSUFFICIENT_CF_POLL); - TX_STATUS_ENTRY(TX_LOCKED); - TX_STATUS_ENTRY(NO_BEACON_ON_RADAR); - } - - return "UNKNOWN"; -} -#else -static inline const char * -il3945_get_tx_fail_reason(u32 status) -{ - return ""; -} -#endif - -/* - * get ieee prev rate from rate scale table. - * for A and B mode we need to overright prev - * value - */ -int -il3945_rs_next_rate(struct il_priv *il, int rate) -{ - int next_rate = il3945_get_prev_ieee_rate(rate); - - switch (il->band) { - case IEEE80211_BAND_5GHZ: - if (rate == RATE_12M_IDX) - next_rate = RATE_9M_IDX; - else if (rate == RATE_6M_IDX) - next_rate = RATE_6M_IDX; - break; - case IEEE80211_BAND_2GHZ: - if (!(il->_3945.sta_supp_rates & IL_OFDM_RATES_MASK) && - il_is_associated(il)) { - if (rate == RATE_11M_IDX) - next_rate = RATE_5M_IDX; - } - break; - - default: - break; - } - - return next_rate; -} - -/** - * il3945_tx_queue_reclaim - Reclaim Tx queue entries already Tx'd - * - * When FW advances 'R' idx, all entries between old and new 'R' idx - * need to be reclaimed. As result, some free space forms. If there is - * enough free space (> low mark), wake the stack that feeds us. - */ -static void -il3945_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx) -{ - struct il_tx_queue *txq = &il->txq[txq_id]; - struct il_queue *q = &txq->q; - struct sk_buff *skb; - - BUG_ON(txq_id == IL39_CMD_QUEUE_NUM); - - for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; - q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { - - skb = txq->skbs[txq->q.read_ptr]; - ieee80211_tx_status_irqsafe(il->hw, skb); - txq->skbs[txq->q.read_ptr] = NULL; - il->ops->txq_free_tfd(il, txq); - } - - if (il_queue_space(q) > q->low_mark && txq_id >= 0 && - txq_id != IL39_CMD_QUEUE_NUM && il->mac80211_registered) - il_wake_queue(il, txq); -} - -/** - * il3945_hdl_tx - Handle Tx response - */ -static void -il3945_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - u16 sequence = le16_to_cpu(pkt->hdr.sequence); - int txq_id = SEQ_TO_QUEUE(sequence); - int idx = SEQ_TO_IDX(sequence); - struct il_tx_queue *txq = &il->txq[txq_id]; - struct ieee80211_tx_info *info; - struct il3945_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; - u32 status = le32_to_cpu(tx_resp->status); - int rate_idx; - int fail; - - if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) { - IL_ERR("Read idx for DMA queue txq_id (%d) idx %d " - "is out of range [0-%d] %d %d\n", txq_id, idx, - txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr); - return; - } - - /* - * Firmware will not transmit frame on passive channel, if it not yet - * received some valid frame on that channel. When this error happen - * we have to wait until firmware will unblock itself i.e. when we - * note received beacon or other frame. We unblock queues in - * il3945_pass_packet_to_mac80211 or in il_mac_bss_info_changed. - */ - if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) && - il->iw_mode == NL80211_IFTYPE_STATION) { - il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE); - D_INFO("Stopped queues - RX waiting on passive channel\n"); - } - - txq->time_stamp = jiffies; - info = IEEE80211_SKB_CB(txq->skbs[txq->q.read_ptr]); - ieee80211_tx_info_clear_status(info); - - /* Fill the MRR chain with some info about on-chip retransmissions */ - rate_idx = il3945_hwrate_to_plcp_idx(tx_resp->rate); - if (info->band == IEEE80211_BAND_5GHZ) - rate_idx -= IL_FIRST_OFDM_RATE; - - fail = tx_resp->failure_frame; - - info->status.rates[0].idx = rate_idx; - info->status.rates[0].count = fail + 1; /* add final attempt */ - - /* tx_status->rts_retry_count = tx_resp->failure_rts; */ - info->flags |= - ((status & TX_STATUS_MSK) == - TX_STATUS_SUCCESS) ? IEEE80211_TX_STAT_ACK : 0; - - D_TX("Tx queue %d Status %s (0x%08x) plcp rate %d retries %d\n", txq_id, - il3945_get_tx_fail_reason(status), status, tx_resp->rate, - tx_resp->failure_frame); - - D_TX_REPLY("Tx queue reclaim %d\n", idx); - il3945_tx_queue_reclaim(il, txq_id, idx); - - if (status & TX_ABORT_REQUIRED_MSK) - IL_ERR("TODO: Implement Tx ABORT REQUIRED!!!\n"); -} - -/***************************************************************************** - * - * Intel PRO/Wireless 3945ABG/BG Network Connection - * - * RX handler implementations - * - *****************************************************************************/ -#ifdef CONFIG_IWLEGACY_DEBUGFS -static void -il3945_accumulative_stats(struct il_priv *il, __le32 * stats) -{ - int i; - __le32 *prev_stats; - u32 *accum_stats; - u32 *delta, *max_delta; - - prev_stats = (__le32 *) &il->_3945.stats; - accum_stats = (u32 *) &il->_3945.accum_stats; - delta = (u32 *) &il->_3945.delta_stats; - max_delta = (u32 *) &il->_3945.max_delta; - - for (i = sizeof(__le32); i < sizeof(struct il3945_notif_stats); - i += - sizeof(__le32), stats++, prev_stats++, delta++, max_delta++, - accum_stats++) { - if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) { - *delta = - (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats)); - *accum_stats += *delta; - if (*delta > *max_delta) - *max_delta = *delta; - } - } - - /* reset accumulative stats for "no-counter" type stats */ - il->_3945.accum_stats.general.temperature = - il->_3945.stats.general.temperature; - il->_3945.accum_stats.general.ttl_timestamp = - il->_3945.stats.general.ttl_timestamp; -} -#endif - -void -il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - - D_RX("Statistics notification received (%d vs %d).\n", - (int)sizeof(struct il3945_notif_stats), - le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK); -#ifdef CONFIG_IWLEGACY_DEBUGFS - il3945_accumulative_stats(il, (__le32 *) &pkt->u.raw); -#endif - - memcpy(&il->_3945.stats, pkt->u.raw, sizeof(il->_3945.stats)); -} - -void -il3945_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - __le32 *flag = (__le32 *) &pkt->u.raw; - - if (le32_to_cpu(*flag) & UCODE_STATS_CLEAR_MSK) { -#ifdef CONFIG_IWLEGACY_DEBUGFS - memset(&il->_3945.accum_stats, 0, - sizeof(struct il3945_notif_stats)); - memset(&il->_3945.delta_stats, 0, - sizeof(struct il3945_notif_stats)); - memset(&il->_3945.max_delta, 0, - sizeof(struct il3945_notif_stats)); -#endif - D_RX("Statistics have been cleared\n"); - } - il3945_hdl_stats(il, rxb); -} - -/****************************************************************************** - * - * Misc. internal state and helper functions - * - ******************************************************************************/ - -/* This is necessary only for a number of stats, see the caller. */ -static int -il3945_is_network_packet(struct il_priv *il, struct ieee80211_hdr *header) -{ - /* Filter incoming packets to determine if they are targeted toward - * this network, discarding packets coming from ourselves */ - switch (il->iw_mode) { - case NL80211_IFTYPE_ADHOC: /* Header: Dest. | Source | BSSID */ - /* packets to our IBSS update information */ - return ether_addr_equal_64bits(header->addr3, il->bssid); - case NL80211_IFTYPE_STATION: /* Header: Dest. | AP{BSSID} | Source */ - /* packets to our IBSS update information */ - return ether_addr_equal_64bits(header->addr2, il->bssid); - default: - return 1; - } -} - -#define SMALL_PACKET_SIZE 256 - -static void -il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb, - struct ieee80211_rx_status *stats) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IL_RX_DATA(pkt); - struct il3945_rx_frame_hdr *rx_hdr = IL_RX_HDR(pkt); - struct il3945_rx_frame_end *rx_end = IL_RX_END(pkt); - u32 len = le16_to_cpu(rx_hdr->len); - struct sk_buff *skb; - __le16 fc = hdr->frame_control; - u32 fraglen = PAGE_SIZE << il->hw_params.rx_page_order; - - /* We received data from the HW, so stop the watchdog */ - if (unlikely(len + IL39_RX_FRAME_SIZE > fraglen)) { - D_DROP("Corruption detected!\n"); - return; - } - - /* We only process data packets if the interface is open */ - if (unlikely(!il->is_open)) { - D_DROP("Dropping packet while interface is not open.\n"); - return; - } - - if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) { - il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); - D_INFO("Woke queues - frame received on passive channel\n"); - } - - skb = dev_alloc_skb(SMALL_PACKET_SIZE); - if (!skb) { - IL_ERR("dev_alloc_skb failed\n"); - return; - } - - if (!il3945_mod_params.sw_crypto) - il_set_decrypted_flag(il, (struct ieee80211_hdr *)pkt, - le32_to_cpu(rx_end->status), stats); - - /* If frame is small enough to fit into skb->head, copy it - * and do not consume a full page - */ - if (len <= SMALL_PACKET_SIZE) { - memcpy(skb_put(skb, len), rx_hdr->payload, len); - } else { - skb_add_rx_frag(skb, 0, rxb->page, - (void *)rx_hdr->payload - (void *)pkt, len, - fraglen); - il->alloc_rxb_page--; - rxb->page = NULL; - } - il_update_stats(il, false, fc, len); - memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); - - ieee80211_rx(il->hw, skb); -} - -#define IL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6) - -static void -il3945_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct ieee80211_hdr *header; - struct ieee80211_rx_status rx_status = {}; - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il3945_rx_frame_stats *rx_stats = IL_RX_STATS(pkt); - struct il3945_rx_frame_hdr *rx_hdr = IL_RX_HDR(pkt); - struct il3945_rx_frame_end *rx_end = IL_RX_END(pkt); - u16 rx_stats_sig_avg __maybe_unused = le16_to_cpu(rx_stats->sig_avg); - u16 rx_stats_noise_diff __maybe_unused = - le16_to_cpu(rx_stats->noise_diff); - u8 network_packet; - - rx_status.flag = 0; - rx_status.mactime = le64_to_cpu(rx_end->timestamp); - rx_status.band = - (rx_hdr-> - phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ : - IEEE80211_BAND_5GHZ; - rx_status.freq = - ieee80211_channel_to_frequency(le16_to_cpu(rx_hdr->channel), - rx_status.band); - - rx_status.rate_idx = il3945_hwrate_to_plcp_idx(rx_hdr->rate); - if (rx_status.band == IEEE80211_BAND_5GHZ) - rx_status.rate_idx -= IL_FIRST_OFDM_RATE; - - rx_status.antenna = - (le16_to_cpu(rx_hdr->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> - 4; - - /* set the preamble flag if appropriate */ - if (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) - rx_status.flag |= RX_FLAG_SHORTPRE; - - if ((unlikely(rx_stats->phy_count > 20))) { - D_DROP("dsp size out of range [0,20]: %d\n", - rx_stats->phy_count); - return; - } - - if (!(rx_end->status & RX_RES_STATUS_NO_CRC32_ERROR) || - !(rx_end->status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { - D_RX("Bad CRC or FIFO: 0x%08X.\n", rx_end->status); - return; - } - - /* Convert 3945's rssi indicator to dBm */ - rx_status.signal = rx_stats->rssi - IL39_RSSI_OFFSET; - - D_STATS("Rssi %d sig_avg %d noise_diff %d\n", rx_status.signal, - rx_stats_sig_avg, rx_stats_noise_diff); - - header = (struct ieee80211_hdr *)IL_RX_DATA(pkt); - - network_packet = il3945_is_network_packet(il, header); - - D_STATS("[%c] %d RSSI:%d Signal:%u, Rate:%u\n", - network_packet ? '*' : ' ', le16_to_cpu(rx_hdr->channel), - rx_status.signal, rx_status.signal, rx_status.rate_idx); - - if (network_packet) { - il->_3945.last_beacon_time = - le32_to_cpu(rx_end->beacon_timestamp); - il->_3945.last_tsf = le64_to_cpu(rx_end->timestamp); - il->_3945.last_rx_rssi = rx_status.signal; - } - - il3945_pass_packet_to_mac80211(il, rxb, &rx_status); -} - -int -il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, - dma_addr_t addr, u16 len, u8 reset, u8 pad) -{ - int count; - struct il_queue *q; - struct il3945_tfd *tfd, *tfd_tmp; - - q = &txq->q; - tfd_tmp = (struct il3945_tfd *)txq->tfds; - tfd = &tfd_tmp[q->write_ptr]; - - if (reset) - memset(tfd, 0, sizeof(*tfd)); - - count = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags)); - - if (count >= NUM_TFD_CHUNKS || count < 0) { - IL_ERR("Error can not send more than %d chunks\n", - NUM_TFD_CHUNKS); - return -EINVAL; - } - - tfd->tbs[count].addr = cpu_to_le32(addr); - tfd->tbs[count].len = cpu_to_le32(len); - - count++; - - tfd->control_flags = - cpu_to_le32(TFD_CTL_COUNT_SET(count) | TFD_CTL_PAD_SET(pad)); - - return 0; -} - -/** - * il3945_hw_txq_free_tfd - Free one TFD, those at idx [txq->q.read_ptr] - * - * Does NOT advance any idxes - */ -void -il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq) -{ - struct il3945_tfd *tfd_tmp = (struct il3945_tfd *)txq->tfds; - int idx = txq->q.read_ptr; - struct il3945_tfd *tfd = &tfd_tmp[idx]; - struct pci_dev *dev = il->pci_dev; - int i; - int counter; - - /* sanity check */ - counter = TFD_CTL_COUNT_GET(le32_to_cpu(tfd->control_flags)); - if (counter > NUM_TFD_CHUNKS) { - IL_ERR("Too many chunks: %i\n", counter); - /* @todo issue fatal error, it is quite serious situation */ - return; - } - - /* Unmap tx_cmd */ - if (counter) - pci_unmap_single(dev, dma_unmap_addr(&txq->meta[idx], mapping), - dma_unmap_len(&txq->meta[idx], len), - PCI_DMA_TODEVICE); - - /* unmap chunks if any */ - - for (i = 1; i < counter; i++) - pci_unmap_single(dev, le32_to_cpu(tfd->tbs[i].addr), - le32_to_cpu(tfd->tbs[i].len), - PCI_DMA_TODEVICE); - - /* free SKB */ - if (txq->skbs) { - struct sk_buff *skb = txq->skbs[txq->q.read_ptr]; - - /* can be called from irqs-disabled context */ - if (skb) { - dev_kfree_skb_any(skb); - txq->skbs[txq->q.read_ptr] = NULL; - } - } -} - -/** - * il3945_hw_build_tx_cmd_rate - Add rate portion to TX_CMD: - * -*/ -void -il3945_hw_build_tx_cmd_rate(struct il_priv *il, struct il_device_cmd *cmd, - struct ieee80211_tx_info *info, - struct ieee80211_hdr *hdr, int sta_id) -{ - u16 hw_value = ieee80211_get_tx_rate(il->hw, info)->hw_value; - u16 rate_idx = min(hw_value & 0xffff, RATE_COUNT_3945 - 1); - u16 rate_mask; - int rate; - const u8 rts_retry_limit = 7; - u8 data_retry_limit; - __le32 tx_flags; - __le16 fc = hdr->frame_control; - struct il3945_tx_cmd *tx_cmd = (struct il3945_tx_cmd *)cmd->cmd.payload; - - rate = il3945_rates[rate_idx].plcp; - tx_flags = tx_cmd->tx_flags; - - /* We need to figure out how to get the sta->supp_rates while - * in this running context */ - rate_mask = RATES_MASK_3945; - - /* Set retry limit on DATA packets and Probe Responses */ - if (ieee80211_is_probe_resp(fc)) - data_retry_limit = 3; - else - data_retry_limit = IL_DEFAULT_TX_RETRY; - tx_cmd->data_retry_limit = data_retry_limit; - /* Set retry limit on RTS packets */ - tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit); - - tx_cmd->rate = rate; - tx_cmd->tx_flags = tx_flags; - - /* OFDM */ - tx_cmd->supp_rates[0] = - ((rate_mask & IL_OFDM_RATES_MASK) >> IL_FIRST_OFDM_RATE) & 0xFF; - - /* CCK */ - tx_cmd->supp_rates[1] = (rate_mask & 0xF); - - D_RATE("Tx sta id: %d, rate: %d (plcp), flags: 0x%4X " - "cck/ofdm mask: 0x%x/0x%x\n", sta_id, tx_cmd->rate, - le32_to_cpu(tx_cmd->tx_flags), tx_cmd->supp_rates[1], - tx_cmd->supp_rates[0]); -} - -static u8 -il3945_sync_sta(struct il_priv *il, int sta_id, u16 tx_rate) -{ - unsigned long flags_spin; - struct il_station_entry *station; - - if (sta_id == IL_INVALID_STATION) - return IL_INVALID_STATION; - - spin_lock_irqsave(&il->sta_lock, flags_spin); - station = &il->stations[sta_id]; - - station->sta.sta.modify_mask = STA_MODIFY_TX_RATE_MSK; - station->sta.rate_n_flags = cpu_to_le16(tx_rate); - station->sta.mode = STA_CONTROL_MODIFY_MSK; - il_send_add_sta(il, &station->sta, CMD_ASYNC); - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - - D_RATE("SCALE sync station %d to rate %d\n", sta_id, tx_rate); - return sta_id; -} - -static void -il3945_set_pwr_vmain(struct il_priv *il) -{ -/* - * (for documentation purposes) - * to set power to V_AUX, do - - if (pci_pme_capable(il->pci_dev, PCI_D3cold)) { - il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VAUX, - ~APMG_PS_CTRL_MSK_PWR_SRC); - - _il_poll_bit(il, CSR_GPIO_IN, - CSR_GPIO_IN_VAL_VAUX_PWR_SRC, - CSR_GPIO_IN_BIT_AUX_POWER, 5000); - } - */ - - il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, - ~APMG_PS_CTRL_MSK_PWR_SRC); - - _il_poll_bit(il, CSR_GPIO_IN, CSR_GPIO_IN_VAL_VMAIN_PWR_SRC, - CSR_GPIO_IN_BIT_AUX_POWER, 5000); -} - -static int -il3945_rx_init(struct il_priv *il, struct il_rx_queue *rxq) -{ - il_wr(il, FH39_RCSR_RBD_BASE(0), rxq->bd_dma); - il_wr(il, FH39_RCSR_RPTR_ADDR(0), rxq->rb_stts_dma); - il_wr(il, FH39_RCSR_WPTR(0), 0); - il_wr(il, FH39_RCSR_CONFIG(0), - FH39_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN_ENABLE | - FH39_RCSR_RX_CONFIG_REG_VAL_RDRBD_EN_ENABLE | - FH39_RCSR_RX_CONFIG_REG_BIT_WR_STTS_EN | - FH39_RCSR_RX_CONFIG_REG_VAL_MAX_FRAG_SIZE_128 | (RX_QUEUE_SIZE_LOG - << - FH39_RCSR_RX_CONFIG_REG_POS_RBDC_SIZE) - | FH39_RCSR_RX_CONFIG_REG_VAL_IRQ_DEST_INT_HOST | (1 << - FH39_RCSR_RX_CONFIG_REG_POS_IRQ_RBTH) - | FH39_RCSR_RX_CONFIG_REG_VAL_MSG_MODE_FH); - - /* fake read to flush all prev I/O */ - il_rd(il, FH39_RSSR_CTRL); - - return 0; -} - -static int -il3945_tx_reset(struct il_priv *il) -{ - /* bypass mode */ - il_wr_prph(il, ALM_SCD_MODE_REG, 0x2); - - /* RA 0 is active */ - il_wr_prph(il, ALM_SCD_ARASTAT_REG, 0x01); - - /* all 6 fifo are active */ - il_wr_prph(il, ALM_SCD_TXFACT_REG, 0x3f); - - il_wr_prph(il, ALM_SCD_SBYP_MODE_1_REG, 0x010000); - il_wr_prph(il, ALM_SCD_SBYP_MODE_2_REG, 0x030002); - il_wr_prph(il, ALM_SCD_TXF4MF_REG, 0x000004); - il_wr_prph(il, ALM_SCD_TXF5MF_REG, 0x000005); - - il_wr(il, FH39_TSSR_CBB_BASE, il->_3945.shared_phys); - - il_wr(il, FH39_TSSR_MSG_CONFIG, - FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON | - FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON | - FH39_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B | - FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON | - FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON | - FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH | - FH39_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH); - - return 0; -} - -/** - * il3945_txq_ctx_reset - Reset TX queue context - * - * Destroys all DMA structures and initialize them again - */ -static int -il3945_txq_ctx_reset(struct il_priv *il) -{ - int rc, txq_id; - - il3945_hw_txq_ctx_free(il); - - /* allocate tx queue structure */ - rc = il_alloc_txq_mem(il); - if (rc) - return rc; - - /* Tx CMD queue */ - rc = il3945_tx_reset(il); - if (rc) - goto error; - - /* Tx queue(s) */ - for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) { - rc = il_tx_queue_init(il, txq_id); - if (rc) { - IL_ERR("Tx %d queue init failed\n", txq_id); - goto error; - } - } - - return rc; - -error: - il3945_hw_txq_ctx_free(il); - return rc; -} - -/* - * Start up 3945's basic functionality after it has been reset - * (e.g. after platform boot, or shutdown via il_apm_stop()) - * NOTE: This does not load uCode nor start the embedded processor - */ -static int -il3945_apm_init(struct il_priv *il) -{ - int ret = il_apm_init(il); - - /* Clear APMG (NIC's internal power management) interrupts */ - il_wr_prph(il, APMG_RTC_INT_MSK_REG, 0x0); - il_wr_prph(il, APMG_RTC_INT_STT_REG, 0xFFFFFFFF); - - /* Reset radio chip */ - il_set_bits_prph(il, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); - udelay(5); - il_clear_bits_prph(il, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); - - return ret; -} - -static void -il3945_nic_config(struct il_priv *il) -{ - struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; - unsigned long flags; - u8 rev_id = il->pci_dev->revision; - - spin_lock_irqsave(&il->lock, flags); - - /* Determine HW type */ - D_INFO("HW Revision ID = 0x%X\n", rev_id); - - if (rev_id & PCI_CFG_REV_ID_BIT_RTP) - D_INFO("RTP type\n"); - else if (rev_id & PCI_CFG_REV_ID_BIT_BASIC_SKU) { - D_INFO("3945 RADIO-MB type\n"); - il_set_bit(il, CSR_HW_IF_CONFIG_REG, - CSR39_HW_IF_CONFIG_REG_BIT_3945_MB); - } else { - D_INFO("3945 RADIO-MM type\n"); - il_set_bit(il, CSR_HW_IF_CONFIG_REG, - CSR39_HW_IF_CONFIG_REG_BIT_3945_MM); - } - - if (EEPROM_SKU_CAP_OP_MODE_MRC == eeprom->sku_cap) { - D_INFO("SKU OP mode is mrc\n"); - il_set_bit(il, CSR_HW_IF_CONFIG_REG, - CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC); - } else - D_INFO("SKU OP mode is basic\n"); - - if ((eeprom->board_revision & 0xF0) == 0xD0) { - D_INFO("3945ABG revision is 0x%X\n", eeprom->board_revision); - il_set_bit(il, CSR_HW_IF_CONFIG_REG, - CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE); - } else { - D_INFO("3945ABG revision is 0x%X\n", eeprom->board_revision); - il_clear_bit(il, CSR_HW_IF_CONFIG_REG, - CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE); - } - - if (eeprom->almgor_m_version <= 1) { - il_set_bit(il, CSR_HW_IF_CONFIG_REG, - CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A); - D_INFO("Card M type A version is 0x%X\n", - eeprom->almgor_m_version); - } else { - D_INFO("Card M type B version is 0x%X\n", - eeprom->almgor_m_version); - il_set_bit(il, CSR_HW_IF_CONFIG_REG, - CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B); - } - spin_unlock_irqrestore(&il->lock, flags); - - if (eeprom->sku_cap & EEPROM_SKU_CAP_SW_RF_KILL_ENABLE) - D_RF_KILL("SW RF KILL supported in EEPROM.\n"); - - if (eeprom->sku_cap & EEPROM_SKU_CAP_HW_RF_KILL_ENABLE) - D_RF_KILL("HW RF KILL supported in EEPROM.\n"); -} - -int -il3945_hw_nic_init(struct il_priv *il) -{ - int rc; - unsigned long flags; - struct il_rx_queue *rxq = &il->rxq; - - spin_lock_irqsave(&il->lock, flags); - il3945_apm_init(il); - spin_unlock_irqrestore(&il->lock, flags); - - il3945_set_pwr_vmain(il); - il3945_nic_config(il); - - /* Allocate the RX queue, or reset if it is already allocated */ - if (!rxq->bd) { - rc = il_rx_queue_alloc(il); - if (rc) { - IL_ERR("Unable to initialize Rx queue\n"); - return -ENOMEM; - } - } else - il3945_rx_queue_reset(il, rxq); - - il3945_rx_replenish(il); - - il3945_rx_init(il, rxq); - - /* Look at using this instead: - rxq->need_update = 1; - il_rx_queue_update_write_ptr(il, rxq); - */ - - il_wr(il, FH39_RCSR_WPTR(0), rxq->write & ~7); - - rc = il3945_txq_ctx_reset(il); - if (rc) - return rc; - - set_bit(S_INIT, &il->status); - - return 0; -} - -/** - * il3945_hw_txq_ctx_free - Free TXQ Context - * - * Destroy all TX DMA queues and structures - */ -void -il3945_hw_txq_ctx_free(struct il_priv *il) -{ - int txq_id; - - /* Tx queues */ - if (il->txq) - for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) - if (txq_id == IL39_CMD_QUEUE_NUM) - il_cmd_queue_free(il); - else - il_tx_queue_free(il, txq_id); - - /* free tx queue structure */ - il_free_txq_mem(il); -} - -void -il3945_hw_txq_ctx_stop(struct il_priv *il) -{ - int txq_id; - - /* stop SCD */ - _il_wr_prph(il, ALM_SCD_MODE_REG, 0); - _il_wr_prph(il, ALM_SCD_TXFACT_REG, 0); - - /* reset TFD queues */ - for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) { - _il_wr(il, FH39_TCSR_CONFIG(txq_id), 0x0); - _il_poll_bit(il, FH39_TSSR_TX_STATUS, - FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id), - FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id), - 1000); - } -} - -/** - * il3945_hw_reg_adjust_power_by_temp - * return idx delta into power gain settings table -*/ -static int -il3945_hw_reg_adjust_power_by_temp(int new_reading, int old_reading) -{ - return (new_reading - old_reading) * (-11) / 100; -} - -/** - * il3945_hw_reg_temp_out_of_range - Keep temperature in sane range - */ -static inline int -il3945_hw_reg_temp_out_of_range(int temperature) -{ - return (temperature < -260 || temperature > 25) ? 1 : 0; -} - -int -il3945_hw_get_temperature(struct il_priv *il) -{ - return _il_rd(il, CSR_UCODE_DRV_GP2); -} - -/** - * il3945_hw_reg_txpower_get_temperature - * get the current temperature by reading from NIC -*/ -static int -il3945_hw_reg_txpower_get_temperature(struct il_priv *il) -{ - struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; - int temperature; - - temperature = il3945_hw_get_temperature(il); - - /* driver's okay range is -260 to +25. - * human readable okay range is 0 to +285 */ - D_INFO("Temperature: %d\n", temperature + IL_TEMP_CONVERT); - - /* handle insane temp reading */ - if (il3945_hw_reg_temp_out_of_range(temperature)) { - IL_ERR("Error bad temperature value %d\n", temperature); - - /* if really really hot(?), - * substitute the 3rd band/group's temp measured at factory */ - if (il->last_temperature > 100) - temperature = eeprom->groups[2].temperature; - else /* else use most recent "sane" value from driver */ - temperature = il->last_temperature; - } - - return temperature; /* raw, not "human readable" */ -} - -/* Adjust Txpower only if temperature variance is greater than threshold. - * - * Both are lower than older versions' 9 degrees */ -#define IL_TEMPERATURE_LIMIT_TIMER 6 - -/** - * il3945_is_temp_calib_needed - determines if new calibration is needed - * - * records new temperature in tx_mgr->temperature. - * replaces tx_mgr->last_temperature *only* if calib needed - * (assumes caller will actually do the calibration!). */ -static int -il3945_is_temp_calib_needed(struct il_priv *il) -{ - int temp_diff; - - il->temperature = il3945_hw_reg_txpower_get_temperature(il); - temp_diff = il->temperature - il->last_temperature; - - /* get absolute value */ - if (temp_diff < 0) { - D_POWER("Getting cooler, delta %d,\n", temp_diff); - temp_diff = -temp_diff; - } else if (temp_diff == 0) - D_POWER("Same temp,\n"); - else - D_POWER("Getting warmer, delta %d,\n", temp_diff); - - /* if we don't need calibration, *don't* update last_temperature */ - if (temp_diff < IL_TEMPERATURE_LIMIT_TIMER) { - D_POWER("Timed thermal calib not needed\n"); - return 0; - } - - D_POWER("Timed thermal calib needed\n"); - - /* assume that caller will actually do calib ... - * update the "last temperature" value */ - il->last_temperature = il->temperature; - return 1; -} - -#define IL_MAX_GAIN_ENTRIES 78 -#define IL_CCK_FROM_OFDM_POWER_DIFF -5 -#define IL_CCK_FROM_OFDM_IDX_DIFF (10) - -/* radio and DSP power table, each step is 1/2 dB. - * 1st number is for RF analog gain, 2nd number is for DSP pre-DAC gain. */ -static struct il3945_tx_power power_gain_table[2][IL_MAX_GAIN_ENTRIES] = { - { - {251, 127}, /* 2.4 GHz, highest power */ - {251, 127}, - {251, 127}, - {251, 127}, - {251, 125}, - {251, 110}, - {251, 105}, - {251, 98}, - {187, 125}, - {187, 115}, - {187, 108}, - {187, 99}, - {243, 119}, - {243, 111}, - {243, 105}, - {243, 97}, - {243, 92}, - {211, 106}, - {211, 100}, - {179, 120}, - {179, 113}, - {179, 107}, - {147, 125}, - {147, 119}, - {147, 112}, - {147, 106}, - {147, 101}, - {147, 97}, - {147, 91}, - {115, 107}, - {235, 121}, - {235, 115}, - {235, 109}, - {203, 127}, - {203, 121}, - {203, 115}, - {203, 108}, - {203, 102}, - {203, 96}, - {203, 92}, - {171, 110}, - {171, 104}, - {171, 98}, - {139, 116}, - {227, 125}, - {227, 119}, - {227, 113}, - {227, 107}, - {227, 101}, - {227, 96}, - {195, 113}, - {195, 106}, - {195, 102}, - {195, 95}, - {163, 113}, - {163, 106}, - {163, 102}, - {163, 95}, - {131, 113}, - {131, 106}, - {131, 102}, - {131, 95}, - {99, 113}, - {99, 106}, - {99, 102}, - {99, 95}, - {67, 113}, - {67, 106}, - {67, 102}, - {67, 95}, - {35, 113}, - {35, 106}, - {35, 102}, - {35, 95}, - {3, 113}, - {3, 106}, - {3, 102}, - {3, 95} /* 2.4 GHz, lowest power */ - }, - { - {251, 127}, /* 5.x GHz, highest power */ - {251, 120}, - {251, 114}, - {219, 119}, - {219, 101}, - {187, 113}, - {187, 102}, - {155, 114}, - {155, 103}, - {123, 117}, - {123, 107}, - {123, 99}, - {123, 92}, - {91, 108}, - {59, 125}, - {59, 118}, - {59, 109}, - {59, 102}, - {59, 96}, - {59, 90}, - {27, 104}, - {27, 98}, - {27, 92}, - {115, 118}, - {115, 111}, - {115, 104}, - {83, 126}, - {83, 121}, - {83, 113}, - {83, 105}, - {83, 99}, - {51, 118}, - {51, 111}, - {51, 104}, - {51, 98}, - {19, 116}, - {19, 109}, - {19, 102}, - {19, 98}, - {19, 93}, - {171, 113}, - {171, 107}, - {171, 99}, - {139, 120}, - {139, 113}, - {139, 107}, - {139, 99}, - {107, 120}, - {107, 113}, - {107, 107}, - {107, 99}, - {75, 120}, - {75, 113}, - {75, 107}, - {75, 99}, - {43, 120}, - {43, 113}, - {43, 107}, - {43, 99}, - {11, 120}, - {11, 113}, - {11, 107}, - {11, 99}, - {131, 107}, - {131, 99}, - {99, 120}, - {99, 113}, - {99, 107}, - {99, 99}, - {67, 120}, - {67, 113}, - {67, 107}, - {67, 99}, - {35, 120}, - {35, 113}, - {35, 107}, - {35, 99}, - {3, 120} /* 5.x GHz, lowest power */ - } -}; - -static inline u8 -il3945_hw_reg_fix_power_idx(int idx) -{ - if (idx < 0) - return 0; - if (idx >= IL_MAX_GAIN_ENTRIES) - return IL_MAX_GAIN_ENTRIES - 1; - return (u8) idx; -} - -/* Kick off thermal recalibration check every 60 seconds */ -#define REG_RECALIB_PERIOD (60) - -/** - * il3945_hw_reg_set_scan_power - Set Tx power for scan probe requests - * - * Set (in our channel info database) the direct scan Tx power for 1 Mbit (CCK) - * or 6 Mbit (OFDM) rates. - */ -static void -il3945_hw_reg_set_scan_power(struct il_priv *il, u32 scan_tbl_idx, s32 rate_idx, - const s8 *clip_pwrs, - struct il_channel_info *ch_info, int band_idx) -{ - struct il3945_scan_power_info *scan_power_info; - s8 power; - u8 power_idx; - - scan_power_info = &ch_info->scan_pwr_info[scan_tbl_idx]; - - /* use this channel group's 6Mbit clipping/saturation pwr, - * but cap at regulatory scan power restriction (set during init - * based on eeprom channel data) for this channel. */ - power = min(ch_info->scan_power, clip_pwrs[RATE_6M_IDX_TBL]); - - power = min(power, il->tx_power_user_lmt); - scan_power_info->requested_power = power; - - /* find difference between new scan *power* and current "normal" - * Tx *power* for 6Mb. Use this difference (x2) to adjust the - * current "normal" temperature-compensated Tx power *idx* for - * this rate (1Mb or 6Mb) to yield new temp-compensated scan power - * *idx*. */ - power_idx = - ch_info->power_info[rate_idx].power_table_idx - (power - - ch_info-> - power_info - [RATE_6M_IDX_TBL]. - requested_power) * - 2; - - /* store reference idx that we use when adjusting *all* scan - * powers. So we can accommodate user (all channel) or spectrum - * management (single channel) power changes "between" temperature - * feedback compensation procedures. - * don't force fit this reference idx into gain table; it may be a - * negative number. This will help avoid errors when we're at - * the lower bounds (highest gains, for warmest temperatures) - * of the table. */ - - /* don't exceed table bounds for "real" setting */ - power_idx = il3945_hw_reg_fix_power_idx(power_idx); - - scan_power_info->power_table_idx = power_idx; - scan_power_info->tpc.tx_gain = - power_gain_table[band_idx][power_idx].tx_gain; - scan_power_info->tpc.dsp_atten = - power_gain_table[band_idx][power_idx].dsp_atten; -} - -/** - * il3945_send_tx_power - fill in Tx Power command with gain settings - * - * Configures power settings for all rates for the current channel, - * using values from channel info struct, and send to NIC - */ -static int -il3945_send_tx_power(struct il_priv *il) -{ - int rate_idx, i; - const struct il_channel_info *ch_info = NULL; - struct il3945_txpowertable_cmd txpower = { - .channel = il->active.channel, - }; - u16 chan; - - if (WARN_ONCE - (test_bit(S_SCAN_HW, &il->status), - "TX Power requested while scanning!\n")) - return -EAGAIN; - - chan = le16_to_cpu(il->active.channel); - - txpower.band = (il->band == IEEE80211_BAND_5GHZ) ? 0 : 1; - ch_info = il_get_channel_info(il, il->band, chan); - if (!ch_info) { - IL_ERR("Failed to get channel info for channel %d [%d]\n", chan, - il->band); - return -EINVAL; - } - - if (!il_is_channel_valid(ch_info)) { - D_POWER("Not calling TX_PWR_TBL_CMD on " "non-Tx channel.\n"); - return 0; - } - - /* fill cmd with power settings for all rates for current channel */ - /* Fill OFDM rate */ - for (rate_idx = IL_FIRST_OFDM_RATE, i = 0; - rate_idx <= IL39_LAST_OFDM_RATE; rate_idx++, i++) { - - txpower.power[i].tpc = ch_info->power_info[i].tpc; - txpower.power[i].rate = il3945_rates[rate_idx].plcp; - - D_POWER("ch %d:%d rf %d dsp %3d rate code 0x%02x\n", - le16_to_cpu(txpower.channel), txpower.band, - txpower.power[i].tpc.tx_gain, - txpower.power[i].tpc.dsp_atten, txpower.power[i].rate); - } - /* Fill CCK rates */ - for (rate_idx = IL_FIRST_CCK_RATE; rate_idx <= IL_LAST_CCK_RATE; - rate_idx++, i++) { - txpower.power[i].tpc = ch_info->power_info[i].tpc; - txpower.power[i].rate = il3945_rates[rate_idx].plcp; - - D_POWER("ch %d:%d rf %d dsp %3d rate code 0x%02x\n", - le16_to_cpu(txpower.channel), txpower.band, - txpower.power[i].tpc.tx_gain, - txpower.power[i].tpc.dsp_atten, txpower.power[i].rate); - } - - return il_send_cmd_pdu(il, C_TX_PWR_TBL, - sizeof(struct il3945_txpowertable_cmd), - &txpower); - -} - -/** - * il3945_hw_reg_set_new_power - Configures power tables at new levels - * @ch_info: Channel to update. Uses power_info.requested_power. - * - * Replace requested_power and base_power_idx ch_info fields for - * one channel. - * - * Called if user or spectrum management changes power preferences. - * Takes into account h/w and modulation limitations (clip power). - * - * This does *not* send anything to NIC, just sets up ch_info for one channel. - * - * NOTE: reg_compensate_for_temperature_dif() *must* be run after this to - * properly fill out the scan powers, and actual h/w gain settings, - * and send changes to NIC - */ -static int -il3945_hw_reg_set_new_power(struct il_priv *il, struct il_channel_info *ch_info) -{ - struct il3945_channel_power_info *power_info; - int power_changed = 0; - int i; - const s8 *clip_pwrs; - int power; - - /* Get this chnlgrp's rate-to-max/clip-powers table */ - clip_pwrs = il->_3945.clip_groups[ch_info->group_idx].clip_powers; - - /* Get this channel's rate-to-current-power settings table */ - power_info = ch_info->power_info; - - /* update OFDM Txpower settings */ - for (i = RATE_6M_IDX_TBL; i <= RATE_54M_IDX_TBL; i++, ++power_info) { - int delta_idx; - - /* limit new power to be no more than h/w capability */ - power = min(ch_info->curr_txpow, clip_pwrs[i]); - if (power == power_info->requested_power) - continue; - - /* find difference between old and new requested powers, - * update base (non-temp-compensated) power idx */ - delta_idx = (power - power_info->requested_power) * 2; - power_info->base_power_idx -= delta_idx; - - /* save new requested power value */ - power_info->requested_power = power; - - power_changed = 1; - } - - /* update CCK Txpower settings, based on OFDM 12M setting ... - * ... all CCK power settings for a given channel are the *same*. */ - if (power_changed) { - power = - ch_info->power_info[RATE_12M_IDX_TBL].requested_power + - IL_CCK_FROM_OFDM_POWER_DIFF; - - /* do all CCK rates' il3945_channel_power_info structures */ - for (i = RATE_1M_IDX_TBL; i <= RATE_11M_IDX_TBL; i++) { - power_info->requested_power = power; - power_info->base_power_idx = - ch_info->power_info[RATE_12M_IDX_TBL]. - base_power_idx + IL_CCK_FROM_OFDM_IDX_DIFF; - ++power_info; - } - } - - return 0; -} - -/** - * il3945_hw_reg_get_ch_txpower_limit - returns new power limit for channel - * - * NOTE: Returned power limit may be less (but not more) than requested, - * based strictly on regulatory (eeprom and spectrum mgt) limitations - * (no consideration for h/w clipping limitations). - */ -static int -il3945_hw_reg_get_ch_txpower_limit(struct il_channel_info *ch_info) -{ - s8 max_power; - -#if 0 - /* if we're using TGd limits, use lower of TGd or EEPROM */ - if (ch_info->tgd_data.max_power != 0) - max_power = - min(ch_info->tgd_data.max_power, - ch_info->eeprom.max_power_avg); - - /* else just use EEPROM limits */ - else -#endif - max_power = ch_info->eeprom.max_power_avg; - - return min(max_power, ch_info->max_power_avg); -} - -/** - * il3945_hw_reg_comp_txpower_temp - Compensate for temperature - * - * Compensate txpower settings of *all* channels for temperature. - * This only accounts for the difference between current temperature - * and the factory calibration temperatures, and bases the new settings - * on the channel's base_power_idx. - * - * If RxOn is "associated", this sends the new Txpower to NIC! - */ -static int -il3945_hw_reg_comp_txpower_temp(struct il_priv *il) -{ - struct il_channel_info *ch_info = NULL; - struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; - int delta_idx; - const s8 *clip_pwrs; /* array of h/w max power levels for each rate */ - u8 a_band; - u8 rate_idx; - u8 scan_tbl_idx; - u8 i; - int ref_temp; - int temperature = il->temperature; - - if (il->disable_tx_power_cal || test_bit(S_SCANNING, &il->status)) { - /* do not perform tx power calibration */ - return 0; - } - /* set up new Tx power info for each and every channel, 2.4 and 5.x */ - for (i = 0; i < il->channel_count; i++) { - ch_info = &il->channel_info[i]; - a_band = il_is_channel_a_band(ch_info); - - /* Get this chnlgrp's factory calibration temperature */ - ref_temp = (s16) eeprom->groups[ch_info->group_idx].temperature; - - /* get power idx adjustment based on current and factory - * temps */ - delta_idx = - il3945_hw_reg_adjust_power_by_temp(temperature, ref_temp); - - /* set tx power value for all rates, OFDM and CCK */ - for (rate_idx = 0; rate_idx < RATE_COUNT_3945; rate_idx++) { - int power_idx = - ch_info->power_info[rate_idx].base_power_idx; - - /* temperature compensate */ - power_idx += delta_idx; - - /* stay within table range */ - power_idx = il3945_hw_reg_fix_power_idx(power_idx); - ch_info->power_info[rate_idx].power_table_idx = - (u8) power_idx; - ch_info->power_info[rate_idx].tpc = - power_gain_table[a_band][power_idx]; - } - - /* Get this chnlgrp's rate-to-max/clip-powers table */ - clip_pwrs = - il->_3945.clip_groups[ch_info->group_idx].clip_powers; - - /* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */ - for (scan_tbl_idx = 0; scan_tbl_idx < IL_NUM_SCAN_RATES; - scan_tbl_idx++) { - s32 actual_idx = - (scan_tbl_idx == - 0) ? RATE_1M_IDX_TBL : RATE_6M_IDX_TBL; - il3945_hw_reg_set_scan_power(il, scan_tbl_idx, - actual_idx, clip_pwrs, - ch_info, a_band); - } - } - - /* send Txpower command for current channel to ucode */ - return il->ops->send_tx_power(il); -} - -int -il3945_hw_reg_set_txpower(struct il_priv *il, s8 power) -{ - struct il_channel_info *ch_info; - s8 max_power; - u8 a_band; - u8 i; - - if (il->tx_power_user_lmt == power) { - D_POWER("Requested Tx power same as current " "limit: %ddBm.\n", - power); - return 0; - } - - D_POWER("Setting upper limit clamp to %ddBm.\n", power); - il->tx_power_user_lmt = power; - - /* set up new Tx powers for each and every channel, 2.4 and 5.x */ - - for (i = 0; i < il->channel_count; i++) { - ch_info = &il->channel_info[i]; - a_band = il_is_channel_a_band(ch_info); - - /* find minimum power of all user and regulatory constraints - * (does not consider h/w clipping limitations) */ - max_power = il3945_hw_reg_get_ch_txpower_limit(ch_info); - max_power = min(power, max_power); - if (max_power != ch_info->curr_txpow) { - ch_info->curr_txpow = max_power; - - /* this considers the h/w clipping limitations */ - il3945_hw_reg_set_new_power(il, ch_info); - } - } - - /* update txpower settings for all channels, - * send to NIC if associated. */ - il3945_is_temp_calib_needed(il); - il3945_hw_reg_comp_txpower_temp(il); - - return 0; -} - -static int -il3945_send_rxon_assoc(struct il_priv *il) -{ - int rc = 0; - struct il_rx_pkt *pkt; - struct il3945_rxon_assoc_cmd rxon_assoc; - struct il_host_cmd cmd = { - .id = C_RXON_ASSOC, - .len = sizeof(rxon_assoc), - .flags = CMD_WANT_SKB, - .data = &rxon_assoc, - }; - const struct il_rxon_cmd *rxon1 = &il->staging; - const struct il_rxon_cmd *rxon2 = &il->active; - - if (rxon1->flags == rxon2->flags && - rxon1->filter_flags == rxon2->filter_flags && - rxon1->cck_basic_rates == rxon2->cck_basic_rates && - rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates) { - D_INFO("Using current RXON_ASSOC. Not resending.\n"); - return 0; - } - - rxon_assoc.flags = il->staging.flags; - rxon_assoc.filter_flags = il->staging.filter_flags; - rxon_assoc.ofdm_basic_rates = il->staging.ofdm_basic_rates; - rxon_assoc.cck_basic_rates = il->staging.cck_basic_rates; - rxon_assoc.reserved = 0; - - rc = il_send_cmd_sync(il, &cmd); - if (rc) - return rc; - - pkt = (struct il_rx_pkt *)cmd.reply_page; - if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { - IL_ERR("Bad return from C_RXON_ASSOC command\n"); - rc = -EIO; - } - - il_free_pages(il, cmd.reply_page); - - return rc; -} - -/** - * il3945_commit_rxon - commit staging_rxon to hardware - * - * The RXON command in staging_rxon is committed to the hardware and - * the active_rxon structure is updated with the new data. This - * function correctly transitions out of the RXON_ASSOC_MSK state if - * a HW tune is required based on the RXON structure changes. - */ -int -il3945_commit_rxon(struct il_priv *il) -{ - /* cast away the const for active_rxon in this function */ - struct il3945_rxon_cmd *active_rxon = (void *)&il->active; - struct il3945_rxon_cmd *staging_rxon = (void *)&il->staging; - int rc = 0; - bool new_assoc = !!(staging_rxon->filter_flags & RXON_FILTER_ASSOC_MSK); - - if (test_bit(S_EXIT_PENDING, &il->status)) - return -EINVAL; - - if (!il_is_alive(il)) - return -1; - - /* always get timestamp with Rx frame */ - staging_rxon->flags |= RXON_FLG_TSF2HOST_MSK; - - /* select antenna */ - staging_rxon->flags &= ~(RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_SEL_MSK); - staging_rxon->flags |= il3945_get_antenna_flags(il); - - rc = il_check_rxon_cmd(il); - if (rc) { - IL_ERR("Invalid RXON configuration. Not committing.\n"); - return -EINVAL; - } - - /* If we don't need to send a full RXON, we can use - * il3945_rxon_assoc_cmd which is used to reconfigure filter - * and other flags for the current radio configuration. */ - if (!il_full_rxon_required(il)) { - rc = il_send_rxon_assoc(il); - if (rc) { - IL_ERR("Error setting RXON_ASSOC " - "configuration (%d).\n", rc); - return rc; - } - - memcpy(active_rxon, staging_rxon, sizeof(*active_rxon)); - /* - * We do not commit tx power settings while channel changing, - * do it now if tx power changed. - */ - il_set_tx_power(il, il->tx_power_next, false); - return 0; - } - - /* If we are currently associated and the new config requires - * an RXON_ASSOC and the new config wants the associated mask enabled, - * we must clear the associated from the active configuration - * before we apply the new config */ - if (il_is_associated(il) && new_assoc) { - D_INFO("Toggling associated bit on current RXON\n"); - active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - - /* - * reserved4 and 5 could have been filled by the iwlcore code. - * Let's clear them before pushing to the 3945. - */ - active_rxon->reserved4 = 0; - active_rxon->reserved5 = 0; - rc = il_send_cmd_pdu(il, C_RXON, sizeof(struct il3945_rxon_cmd), - &il->active); - - /* If the mask clearing failed then we set - * active_rxon back to what it was previously */ - if (rc) { - active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK; - IL_ERR("Error clearing ASSOC_MSK on current " - "configuration (%d).\n", rc); - return rc; - } - il_clear_ucode_stations(il); - il_restore_stations(il); - } - - D_INFO("Sending RXON\n" "* with%s RXON_FILTER_ASSOC_MSK\n" - "* channel = %d\n" "* bssid = %pM\n", (new_assoc ? "" : "out"), - le16_to_cpu(staging_rxon->channel), staging_rxon->bssid_addr); - - /* - * reserved4 and 5 could have been filled by the iwlcore code. - * Let's clear them before pushing to the 3945. - */ - staging_rxon->reserved4 = 0; - staging_rxon->reserved5 = 0; - - il_set_rxon_hwcrypto(il, !il3945_mod_params.sw_crypto); - - /* Apply the new configuration */ - rc = il_send_cmd_pdu(il, C_RXON, sizeof(struct il3945_rxon_cmd), - staging_rxon); - if (rc) { - IL_ERR("Error setting new configuration (%d).\n", rc); - return rc; - } - - memcpy(active_rxon, staging_rxon, sizeof(*active_rxon)); - - if (!new_assoc) { - il_clear_ucode_stations(il); - il_restore_stations(il); - } - - /* If we issue a new RXON command which required a tune then we must - * send a new TXPOWER command or we won't be able to Tx any frames */ - rc = il_set_tx_power(il, il->tx_power_next, true); - if (rc) { - IL_ERR("Error setting Tx power (%d).\n", rc); - return rc; - } - - /* Init the hardware's rate fallback order based on the band */ - rc = il3945_init_hw_rate_table(il); - if (rc) { - IL_ERR("Error setting HW rate table: %02X\n", rc); - return -EIO; - } - - return 0; -} - -/** - * il3945_reg_txpower_periodic - called when time to check our temperature. - * - * -- reset periodic timer - * -- see if temp has changed enough to warrant re-calibration ... if so: - * -- correct coeffs for temp (can reset temp timer) - * -- save this temp as "last", - * -- send new set of gain settings to NIC - * NOTE: This should continue working, even when we're not associated, - * so we can keep our internal table of scan powers current. */ -void -il3945_reg_txpower_periodic(struct il_priv *il) -{ - /* This will kick in the "brute force" - * il3945_hw_reg_comp_txpower_temp() below */ - if (!il3945_is_temp_calib_needed(il)) - goto reschedule; - - /* Set up a new set of temp-adjusted TxPowers, send to NIC. - * This is based *only* on current temperature, - * ignoring any previous power measurements */ - il3945_hw_reg_comp_txpower_temp(il); - -reschedule: - queue_delayed_work(il->workqueue, &il->_3945.thermal_periodic, - REG_RECALIB_PERIOD * HZ); -} - -static void -il3945_bg_reg_txpower_periodic(struct work_struct *work) -{ - struct il_priv *il = container_of(work, struct il_priv, - _3945.thermal_periodic.work); - - mutex_lock(&il->mutex); - if (test_bit(S_EXIT_PENDING, &il->status) || il->txq == NULL) - goto out; - - il3945_reg_txpower_periodic(il); -out: - mutex_unlock(&il->mutex); -} - -/** - * il3945_hw_reg_get_ch_grp_idx - find the channel-group idx (0-4) for channel. - * - * This function is used when initializing channel-info structs. - * - * NOTE: These channel groups do *NOT* match the bands above! - * These channel groups are based on factory-tested channels; - * on A-band, EEPROM's "group frequency" entries represent the top - * channel in each group 1-4. Group 5 All B/G channels are in group 0. - */ -static u16 -il3945_hw_reg_get_ch_grp_idx(struct il_priv *il, - const struct il_channel_info *ch_info) -{ - struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; - struct il3945_eeprom_txpower_group *ch_grp = &eeprom->groups[0]; - u8 group; - u16 group_idx = 0; /* based on factory calib frequencies */ - u8 grp_channel; - - /* Find the group idx for the channel ... don't use idx 1(?) */ - if (il_is_channel_a_band(ch_info)) { - for (group = 1; group < 5; group++) { - grp_channel = ch_grp[group].group_channel; - if (ch_info->channel <= grp_channel) { - group_idx = group; - break; - } - } - /* group 4 has a few channels *above* its factory cal freq */ - if (group == 5) - group_idx = 4; - } else - group_idx = 0; /* 2.4 GHz, group 0 */ - - D_POWER("Chnl %d mapped to grp %d\n", ch_info->channel, group_idx); - return group_idx; -} - -/** - * il3945_hw_reg_get_matched_power_idx - Interpolate to get nominal idx - * - * Interpolate to get nominal (i.e. at factory calibration temperature) idx - * into radio/DSP gain settings table for requested power. - */ -static int -il3945_hw_reg_get_matched_power_idx(struct il_priv *il, s8 requested_power, - s32 setting_idx, s32 *new_idx) -{ - const struct il3945_eeprom_txpower_group *chnl_grp = NULL; - struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; - s32 idx0, idx1; - s32 power = 2 * requested_power; - s32 i; - const struct il3945_eeprom_txpower_sample *samples; - s32 gains0, gains1; - s32 res; - s32 denominator; - - chnl_grp = &eeprom->groups[setting_idx]; - samples = chnl_grp->samples; - for (i = 0; i < 5; i++) { - if (power == samples[i].power) { - *new_idx = samples[i].gain_idx; - return 0; - } - } - - if (power > samples[1].power) { - idx0 = 0; - idx1 = 1; - } else if (power > samples[2].power) { - idx0 = 1; - idx1 = 2; - } else if (power > samples[3].power) { - idx0 = 2; - idx1 = 3; - } else { - idx0 = 3; - idx1 = 4; - } - - denominator = (s32) samples[idx1].power - (s32) samples[idx0].power; - if (denominator == 0) - return -EINVAL; - gains0 = (s32) samples[idx0].gain_idx * (1 << 19); - gains1 = (s32) samples[idx1].gain_idx * (1 << 19); - res = - gains0 + (gains1 - gains0) * ((s32) power - - (s32) samples[idx0].power) / - denominator + (1 << 18); - *new_idx = res >> 19; - return 0; -} - -static void -il3945_hw_reg_init_channel_groups(struct il_priv *il) -{ - u32 i; - s32 rate_idx; - struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; - const struct il3945_eeprom_txpower_group *group; - - D_POWER("Initializing factory calib info from EEPROM\n"); - - for (i = 0; i < IL_NUM_TX_CALIB_GROUPS; i++) { - s8 *clip_pwrs; /* table of power levels for each rate */ - s8 satur_pwr; /* saturation power for each chnl group */ - group = &eeprom->groups[i]; - - /* sanity check on factory saturation power value */ - if (group->saturation_power < 40) { - IL_WARN("Error: saturation power is %d, " - "less than minimum expected 40\n", - group->saturation_power); - return; - } - - /* - * Derive requested power levels for each rate, based on - * hardware capabilities (saturation power for band). - * Basic value is 3dB down from saturation, with further - * power reductions for highest 3 data rates. These - * backoffs provide headroom for high rate modulation - * power peaks, without too much distortion (clipping). - */ - /* we'll fill in this array with h/w max power levels */ - clip_pwrs = (s8 *) il->_3945.clip_groups[i].clip_powers; - - /* divide factory saturation power by 2 to find -3dB level */ - satur_pwr = (s8) (group->saturation_power >> 1); - - /* fill in channel group's nominal powers for each rate */ - for (rate_idx = 0; rate_idx < RATE_COUNT_3945; - rate_idx++, clip_pwrs++) { - switch (rate_idx) { - case RATE_36M_IDX_TBL: - if (i == 0) /* B/G */ - *clip_pwrs = satur_pwr; - else /* A */ - *clip_pwrs = satur_pwr - 5; - break; - case RATE_48M_IDX_TBL: - if (i == 0) - *clip_pwrs = satur_pwr - 7; - else - *clip_pwrs = satur_pwr - 10; - break; - case RATE_54M_IDX_TBL: - if (i == 0) - *clip_pwrs = satur_pwr - 9; - else - *clip_pwrs = satur_pwr - 12; - break; - default: - *clip_pwrs = satur_pwr; - break; - } - } - } -} - -/** - * il3945_txpower_set_from_eeprom - Set channel power info based on EEPROM - * - * Second pass (during init) to set up il->channel_info - * - * Set up Tx-power settings in our channel info database for each VALID - * (for this geo/SKU) channel, at all Tx data rates, based on eeprom values - * and current temperature. - * - * Since this is based on current temperature (at init time), these values may - * not be valid for very long, but it gives us a starting/default point, - * and allows us to active (i.e. using Tx) scan. - * - * This does *not* write values to NIC, just sets up our internal table. - */ -int -il3945_txpower_set_from_eeprom(struct il_priv *il) -{ - struct il_channel_info *ch_info = NULL; - struct il3945_channel_power_info *pwr_info; - struct il3945_eeprom *eeprom = (struct il3945_eeprom *)il->eeprom; - int delta_idx; - u8 rate_idx; - u8 scan_tbl_idx; - const s8 *clip_pwrs; /* array of power levels for each rate */ - u8 gain, dsp_atten; - s8 power; - u8 pwr_idx, base_pwr_idx, a_band; - u8 i; - int temperature; - - /* save temperature reference, - * so we can determine next time to calibrate */ - temperature = il3945_hw_reg_txpower_get_temperature(il); - il->last_temperature = temperature; - - il3945_hw_reg_init_channel_groups(il); - - /* initialize Tx power info for each and every channel, 2.4 and 5.x */ - for (i = 0, ch_info = il->channel_info; i < il->channel_count; - i++, ch_info++) { - a_band = il_is_channel_a_band(ch_info); - if (!il_is_channel_valid(ch_info)) - continue; - - /* find this channel's channel group (*not* "band") idx */ - ch_info->group_idx = il3945_hw_reg_get_ch_grp_idx(il, ch_info); - - /* Get this chnlgrp's rate->max/clip-powers table */ - clip_pwrs = - il->_3945.clip_groups[ch_info->group_idx].clip_powers; - - /* calculate power idx *adjustment* value according to - * diff between current temperature and factory temperature */ - delta_idx = - il3945_hw_reg_adjust_power_by_temp(temperature, - eeprom->groups[ch_info-> - group_idx]. - temperature); - - D_POWER("Delta idx for channel %d: %d [%d]\n", ch_info->channel, - delta_idx, temperature + IL_TEMP_CONVERT); - - /* set tx power value for all OFDM rates */ - for (rate_idx = 0; rate_idx < IL_OFDM_RATES; rate_idx++) { - s32 uninitialized_var(power_idx); - int rc; - - /* use channel group's clip-power table, - * but don't exceed channel's max power */ - s8 pwr = min(ch_info->max_power_avg, - clip_pwrs[rate_idx]); - - pwr_info = &ch_info->power_info[rate_idx]; - - /* get base (i.e. at factory-measured temperature) - * power table idx for this rate's power */ - rc = il3945_hw_reg_get_matched_power_idx(il, pwr, - ch_info-> - group_idx, - &power_idx); - if (rc) { - IL_ERR("Invalid power idx\n"); - return rc; - } - pwr_info->base_power_idx = (u8) power_idx; - - /* temperature compensate */ - power_idx += delta_idx; - - /* stay within range of gain table */ - power_idx = il3945_hw_reg_fix_power_idx(power_idx); - - /* fill 1 OFDM rate's il3945_channel_power_info struct */ - pwr_info->requested_power = pwr; - pwr_info->power_table_idx = (u8) power_idx; - pwr_info->tpc.tx_gain = - power_gain_table[a_band][power_idx].tx_gain; - pwr_info->tpc.dsp_atten = - power_gain_table[a_band][power_idx].dsp_atten; - } - - /* set tx power for CCK rates, based on OFDM 12 Mbit settings */ - pwr_info = &ch_info->power_info[RATE_12M_IDX_TBL]; - power = pwr_info->requested_power + IL_CCK_FROM_OFDM_POWER_DIFF; - pwr_idx = pwr_info->power_table_idx + IL_CCK_FROM_OFDM_IDX_DIFF; - base_pwr_idx = - pwr_info->base_power_idx + IL_CCK_FROM_OFDM_IDX_DIFF; - - /* stay within table range */ - pwr_idx = il3945_hw_reg_fix_power_idx(pwr_idx); - gain = power_gain_table[a_band][pwr_idx].tx_gain; - dsp_atten = power_gain_table[a_band][pwr_idx].dsp_atten; - - /* fill each CCK rate's il3945_channel_power_info structure - * NOTE: All CCK-rate Txpwrs are the same for a given chnl! - * NOTE: CCK rates start at end of OFDM rates! */ - for (rate_idx = 0; rate_idx < IL_CCK_RATES; rate_idx++) { - pwr_info = - &ch_info->power_info[rate_idx + IL_OFDM_RATES]; - pwr_info->requested_power = power; - pwr_info->power_table_idx = pwr_idx; - pwr_info->base_power_idx = base_pwr_idx; - pwr_info->tpc.tx_gain = gain; - pwr_info->tpc.dsp_atten = dsp_atten; - } - - /* set scan tx power, 1Mbit for CCK, 6Mbit for OFDM */ - for (scan_tbl_idx = 0; scan_tbl_idx < IL_NUM_SCAN_RATES; - scan_tbl_idx++) { - s32 actual_idx = - (scan_tbl_idx == - 0) ? RATE_1M_IDX_TBL : RATE_6M_IDX_TBL; - il3945_hw_reg_set_scan_power(il, scan_tbl_idx, - actual_idx, clip_pwrs, - ch_info, a_band); - } - } - - return 0; -} - -int -il3945_hw_rxq_stop(struct il_priv *il) -{ - int ret; - - _il_wr(il, FH39_RCSR_CONFIG(0), 0); - ret = _il_poll_bit(il, FH39_RSSR_STATUS, - FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, - FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, - 1000); - if (ret < 0) - IL_ERR("Can't stop Rx DMA.\n"); - - return 0; -} - -int -il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq) -{ - int txq_id = txq->q.id; - - struct il3945_shared *shared_data = il->_3945.shared_virt; - - shared_data->tx_base_ptr[txq_id] = cpu_to_le32((u32) txq->q.dma_addr); - - il_wr(il, FH39_CBCC_CTRL(txq_id), 0); - il_wr(il, FH39_CBCC_BASE(txq_id), 0); - - il_wr(il, FH39_TCSR_CONFIG(txq_id), - FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT | - FH39_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF | - FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD | - FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL | - FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE); - - /* fake read to flush all prev. writes */ - _il_rd(il, FH39_TSSR_CBB_BASE); - - return 0; -} - -/* - * HCMD utils - */ -static u16 -il3945_get_hcmd_size(u8 cmd_id, u16 len) -{ - switch (cmd_id) { - case C_RXON: - return sizeof(struct il3945_rxon_cmd); - case C_POWER_TBL: - return sizeof(struct il3945_powertable_cmd); - default: - return len; - } -} - -static u16 -il3945_build_addsta_hcmd(const struct il_addsta_cmd *cmd, u8 * data) -{ - struct il3945_addsta_cmd *addsta = (struct il3945_addsta_cmd *)data; - addsta->mode = cmd->mode; - memcpy(&addsta->sta, &cmd->sta, sizeof(struct sta_id_modify)); - memcpy(&addsta->key, &cmd->key, sizeof(struct il4965_keyinfo)); - addsta->station_flags = cmd->station_flags; - addsta->station_flags_msk = cmd->station_flags_msk; - addsta->tid_disable_tx = cpu_to_le16(0); - addsta->rate_n_flags = cmd->rate_n_flags; - addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid; - addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid; - addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn; - - return (u16) sizeof(struct il3945_addsta_cmd); -} - -static int -il3945_add_bssid_station(struct il_priv *il, const u8 * addr, u8 * sta_id_r) -{ - int ret; - u8 sta_id; - unsigned long flags; - - if (sta_id_r) - *sta_id_r = IL_INVALID_STATION; - - ret = il_add_station_common(il, addr, 0, NULL, &sta_id); - if (ret) { - IL_ERR("Unable to add station %pM\n", addr); - return ret; - } - - if (sta_id_r) - *sta_id_r = sta_id; - - spin_lock_irqsave(&il->sta_lock, flags); - il->stations[sta_id].used |= IL_STA_LOCAL; - spin_unlock_irqrestore(&il->sta_lock, flags); - - return 0; -} - -static int -il3945_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif, - bool add) -{ - struct il_vif_priv *vif_priv = (void *)vif->drv_priv; - int ret; - - if (add) { - ret = - il3945_add_bssid_station(il, vif->bss_conf.bssid, - &vif_priv->ibss_bssid_sta_id); - if (ret) - return ret; - - il3945_sync_sta(il, vif_priv->ibss_bssid_sta_id, - (il->band == - IEEE80211_BAND_5GHZ) ? RATE_6M_PLCP : - RATE_1M_PLCP); - il3945_rate_scale_init(il->hw, vif_priv->ibss_bssid_sta_id); - - return 0; - } - - return il_remove_station(il, vif_priv->ibss_bssid_sta_id, - vif->bss_conf.bssid); -} - -/** - * il3945_init_hw_rate_table - Initialize the hardware rate fallback table - */ -int -il3945_init_hw_rate_table(struct il_priv *il) -{ - int rc, i, idx, prev_idx; - struct il3945_rate_scaling_cmd rate_cmd = { - .reserved = {0, 0, 0}, - }; - struct il3945_rate_scaling_info *table = rate_cmd.table; - - for (i = 0; i < ARRAY_SIZE(il3945_rates); i++) { - idx = il3945_rates[i].table_rs_idx; - - table[idx].rate_n_flags = cpu_to_le16(il3945_rates[i].plcp); - table[idx].try_cnt = il->retry_rate; - prev_idx = il3945_get_prev_ieee_rate(i); - table[idx].next_rate_idx = il3945_rates[prev_idx].table_rs_idx; - } - - switch (il->band) { - case IEEE80211_BAND_5GHZ: - D_RATE("Select A mode rate scale\n"); - /* If one of the following CCK rates is used, - * have it fall back to the 6M OFDM rate */ - for (i = RATE_1M_IDX_TBL; i <= RATE_11M_IDX_TBL; i++) - table[i].next_rate_idx = - il3945_rates[IL_FIRST_OFDM_RATE].table_rs_idx; - - /* Don't fall back to CCK rates */ - table[RATE_12M_IDX_TBL].next_rate_idx = RATE_9M_IDX_TBL; - - /* Don't drop out of OFDM rates */ - table[RATE_6M_IDX_TBL].next_rate_idx = - il3945_rates[IL_FIRST_OFDM_RATE].table_rs_idx; - break; - - case IEEE80211_BAND_2GHZ: - D_RATE("Select B/G mode rate scale\n"); - /* If an OFDM rate is used, have it fall back to the - * 1M CCK rates */ - - if (!(il->_3945.sta_supp_rates & IL_OFDM_RATES_MASK) && - il_is_associated(il)) { - - idx = IL_FIRST_CCK_RATE; - for (i = RATE_6M_IDX_TBL; i <= RATE_54M_IDX_TBL; i++) - table[i].next_rate_idx = - il3945_rates[idx].table_rs_idx; - - idx = RATE_11M_IDX_TBL; - /* CCK shouldn't fall back to OFDM... */ - table[idx].next_rate_idx = RATE_5M_IDX_TBL; - } - break; - - default: - WARN_ON(1); - break; - } - - /* Update the rate scaling for control frame Tx */ - rate_cmd.table_id = 0; - rc = il_send_cmd_pdu(il, C_RATE_SCALE, sizeof(rate_cmd), &rate_cmd); - if (rc) - return rc; - - /* Update the rate scaling for data frame Tx */ - rate_cmd.table_id = 1; - return il_send_cmd_pdu(il, C_RATE_SCALE, sizeof(rate_cmd), &rate_cmd); -} - -/* Called when initializing driver */ -int -il3945_hw_set_hw_params(struct il_priv *il) -{ - memset((void *)&il->hw_params, 0, sizeof(struct il_hw_params)); - - il->_3945.shared_virt = - dma_alloc_coherent(&il->pci_dev->dev, sizeof(struct il3945_shared), - &il->_3945.shared_phys, GFP_KERNEL); - if (!il->_3945.shared_virt) - return -ENOMEM; - - il->hw_params.bcast_id = IL3945_BROADCAST_ID; - - /* Assign number of Usable TX queues */ - il->hw_params.max_txq_num = il->cfg->num_of_queues; - - il->hw_params.tfd_size = sizeof(struct il3945_tfd); - il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_3K); - il->hw_params.max_rxq_size = RX_QUEUE_SIZE; - il->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; - il->hw_params.max_stations = IL3945_STATION_COUNT; - - il->sta_key_max_num = STA_KEY_MAX_NUM; - - il->hw_params.rx_wrt_ptr_reg = FH39_RSCSR_CHNL0_WPTR; - il->hw_params.max_beacon_itrvl = IL39_MAX_UCODE_BEACON_INTERVAL; - il->hw_params.beacon_time_tsf_bits = IL3945_EXT_BEACON_TIME_POS; - - return 0; -} - -unsigned int -il3945_hw_get_beacon_cmd(struct il_priv *il, struct il3945_frame *frame, - u8 rate) -{ - struct il3945_tx_beacon_cmd *tx_beacon_cmd; - unsigned int frame_size; - - tx_beacon_cmd = (struct il3945_tx_beacon_cmd *)&frame->u; - memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); - - tx_beacon_cmd->tx.sta_id = il->hw_params.bcast_id; - tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - - frame_size = - il3945_fill_beacon_frame(il, tx_beacon_cmd->frame, - sizeof(frame->u) - sizeof(*tx_beacon_cmd)); - - BUG_ON(frame_size > MAX_MPDU_SIZE); - tx_beacon_cmd->tx.len = cpu_to_le16((u16) frame_size); - - tx_beacon_cmd->tx.rate = rate; - tx_beacon_cmd->tx.tx_flags = - (TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK); - - /* supp_rates[0] == OFDM start at IL_FIRST_OFDM_RATE */ - tx_beacon_cmd->tx.supp_rates[0] = - (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; - - tx_beacon_cmd->tx.supp_rates[1] = (IL_CCK_BASIC_RATES_MASK & 0xF); - - return sizeof(struct il3945_tx_beacon_cmd) + frame_size; -} - -void -il3945_hw_handler_setup(struct il_priv *il) -{ - il->handlers[C_TX] = il3945_hdl_tx; - il->handlers[N_3945_RX] = il3945_hdl_rx; -} - -void -il3945_hw_setup_deferred_work(struct il_priv *il) -{ - INIT_DELAYED_WORK(&il->_3945.thermal_periodic, - il3945_bg_reg_txpower_periodic); -} - -void -il3945_hw_cancel_deferred_work(struct il_priv *il) -{ - cancel_delayed_work(&il->_3945.thermal_periodic); -} - -/* check contents of special bootstrap uCode SRAM */ -static int -il3945_verify_bsm(struct il_priv *il) -{ - __le32 *image = il->ucode_boot.v_addr; - u32 len = il->ucode_boot.len; - u32 reg; - u32 val; - - D_INFO("Begin verify bsm\n"); - - /* verify BSM SRAM contents */ - val = il_rd_prph(il, BSM_WR_DWCOUNT_REG); - for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; - reg += sizeof(u32), image++) { - val = il_rd_prph(il, reg); - if (val != le32_to_cpu(*image)) { - IL_ERR("BSM uCode verification failed at " - "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n", - BSM_SRAM_LOWER_BOUND, reg - BSM_SRAM_LOWER_BOUND, - len, val, le32_to_cpu(*image)); - return -EIO; - } - } - - D_INFO("BSM bootstrap uCode image OK\n"); - - return 0; -} - -/****************************************************************************** - * - * EEPROM related functions - * - ******************************************************************************/ - -/* - * Clear the OWNER_MSK, to establish driver (instead of uCode running on - * embedded controller) as EEPROM reader; each read is a series of pulses - * to/from the EEPROM chip, not a single event, so even reads could conflict - * if they weren't arbitrated by some ownership mechanism. Here, the driver - * simply claims ownership, which should be safe when this function is called - * (i.e. before loading uCode!). - */ -static int -il3945_eeprom_acquire_semaphore(struct il_priv *il) -{ - _il_clear_bit(il, CSR_EEPROM_GP, CSR_EEPROM_GP_IF_OWNER_MSK); - return 0; -} - -static void -il3945_eeprom_release_semaphore(struct il_priv *il) -{ - return; -} - - /** - * il3945_load_bsm - Load bootstrap instructions - * - * BSM operation: - * - * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program - * in special SRAM that does not power down during RFKILL. When powering back - * up after power-saving sleeps (or during initial uCode load), the BSM loads - * the bootstrap program into the on-board processor, and starts it. - * - * The bootstrap program loads (via DMA) instructions and data for a new - * program from host DRAM locations indicated by the host driver in the - * BSM_DRAM_* registers. Once the new program is loaded, it starts - * automatically. - * - * When initializing the NIC, the host driver points the BSM to the - * "initialize" uCode image. This uCode sets up some internal data, then - * notifies host via "initialize alive" that it is complete. - * - * The host then replaces the BSM_DRAM_* pointer values to point to the - * normal runtime uCode instructions and a backup uCode data cache buffer - * (filled initially with starting data values for the on-board processor), - * then triggers the "initialize" uCode to load and launch the runtime uCode, - * which begins normal operation. - * - * When doing a power-save shutdown, runtime uCode saves data SRAM into - * the backup data cache in DRAM before SRAM is powered down. - * - * When powering back up, the BSM loads the bootstrap program. This reloads - * the runtime uCode instructions and the backup data cache into SRAM, - * and re-launches the runtime uCode from where it left off. - */ -static int -il3945_load_bsm(struct il_priv *il) -{ - __le32 *image = il->ucode_boot.v_addr; - u32 len = il->ucode_boot.len; - dma_addr_t pinst; - dma_addr_t pdata; - u32 inst_len; - u32 data_len; - int rc; - int i; - u32 done; - u32 reg_offset; - - D_INFO("Begin load bsm\n"); - - /* make sure bootstrap program is no larger than BSM's SRAM size */ - if (len > IL39_MAX_BSM_SIZE) - return -EINVAL; - - /* Tell bootstrap uCode where to find the "Initialize" uCode - * in host DRAM ... host DRAM physical address bits 31:0 for 3945. - * NOTE: il3945_initialize_alive_start() will replace these values, - * after the "initialize" uCode has run, to point to - * runtime/protocol instructions and backup data cache. */ - pinst = il->ucode_init.p_addr; - pdata = il->ucode_init_data.p_addr; - inst_len = il->ucode_init.len; - data_len = il->ucode_init_data.len; - - il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); - il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); - il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); - il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); - - /* Fill BSM memory with bootstrap instructions */ - for (reg_offset = BSM_SRAM_LOWER_BOUND; - reg_offset < BSM_SRAM_LOWER_BOUND + len; - reg_offset += sizeof(u32), image++) - _il_wr_prph(il, reg_offset, le32_to_cpu(*image)); - - rc = il3945_verify_bsm(il); - if (rc) - return rc; - - /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ - il_wr_prph(il, BSM_WR_MEM_SRC_REG, 0x0); - il_wr_prph(il, BSM_WR_MEM_DST_REG, IL39_RTC_INST_LOWER_BOUND); - il_wr_prph(il, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); - - /* Load bootstrap code into instruction SRAM now, - * to prepare to load "initialize" uCode */ - il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); - - /* Wait for load of bootstrap uCode to finish */ - for (i = 0; i < 100; i++) { - done = il_rd_prph(il, BSM_WR_CTRL_REG); - if (!(done & BSM_WR_CTRL_REG_BIT_START)) - break; - udelay(10); - } - if (i < 100) - D_INFO("BSM write complete, poll %d iterations\n", i); - else { - IL_ERR("BSM write did not complete!\n"); - return -EIO; - } - - /* Enable future boot loads whenever power management unit triggers it - * (e.g. when powering back up after power-save shutdown) */ - il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); - - return 0; -} - -const struct il_ops il3945_ops = { - .txq_attach_buf_to_tfd = il3945_hw_txq_attach_buf_to_tfd, - .txq_free_tfd = il3945_hw_txq_free_tfd, - .txq_init = il3945_hw_tx_queue_init, - .load_ucode = il3945_load_bsm, - .dump_nic_error_log = il3945_dump_nic_error_log, - .apm_init = il3945_apm_init, - .send_tx_power = il3945_send_tx_power, - .is_valid_rtc_data_addr = il3945_hw_valid_rtc_data_addr, - .eeprom_acquire_semaphore = il3945_eeprom_acquire_semaphore, - .eeprom_release_semaphore = il3945_eeprom_release_semaphore, - - .rxon_assoc = il3945_send_rxon_assoc, - .commit_rxon = il3945_commit_rxon, - - .get_hcmd_size = il3945_get_hcmd_size, - .build_addsta_hcmd = il3945_build_addsta_hcmd, - .request_scan = il3945_request_scan, - .post_scan = il3945_post_scan, - - .post_associate = il3945_post_associate, - .config_ap = il3945_config_ap, - .manage_ibss_station = il3945_manage_ibss_station, - - .send_led_cmd = il3945_send_led_cmd, -}; - -static struct il_cfg il3945_bg_cfg = { - .name = "3945BG", - .fw_name_pre = IL3945_FW_PRE, - .ucode_api_max = IL3945_UCODE_API_MAX, - .ucode_api_min = IL3945_UCODE_API_MIN, - .sku = IL_SKU_G, - .eeprom_ver = EEPROM_3945_EEPROM_VERSION, - .mod_params = &il3945_mod_params, - .led_mode = IL_LED_BLINK, - - .eeprom_size = IL3945_EEPROM_IMG_SIZE, - .num_of_queues = IL39_NUM_QUEUES, - .pll_cfg_val = CSR39_ANA_PLL_CFG_VAL, - .set_l0s = false, - .use_bsm = true, - .led_compensation = 64, - .wd_timeout = IL_DEF_WD_TIMEOUT, - - .regulatory_bands = { - EEPROM_REGULATORY_BAND_1_CHANNELS, - EEPROM_REGULATORY_BAND_2_CHANNELS, - EEPROM_REGULATORY_BAND_3_CHANNELS, - EEPROM_REGULATORY_BAND_4_CHANNELS, - EEPROM_REGULATORY_BAND_5_CHANNELS, - EEPROM_REGULATORY_BAND_NO_HT40, - EEPROM_REGULATORY_BAND_NO_HT40, - }, -}; - -static struct il_cfg il3945_abg_cfg = { - .name = "3945ABG", - .fw_name_pre = IL3945_FW_PRE, - .ucode_api_max = IL3945_UCODE_API_MAX, - .ucode_api_min = IL3945_UCODE_API_MIN, - .sku = IL_SKU_A | IL_SKU_G, - .eeprom_ver = EEPROM_3945_EEPROM_VERSION, - .mod_params = &il3945_mod_params, - .led_mode = IL_LED_BLINK, - - .eeprom_size = IL3945_EEPROM_IMG_SIZE, - .num_of_queues = IL39_NUM_QUEUES, - .pll_cfg_val = CSR39_ANA_PLL_CFG_VAL, - .set_l0s = false, - .use_bsm = true, - .led_compensation = 64, - .wd_timeout = IL_DEF_WD_TIMEOUT, - - .regulatory_bands = { - EEPROM_REGULATORY_BAND_1_CHANNELS, - EEPROM_REGULATORY_BAND_2_CHANNELS, - EEPROM_REGULATORY_BAND_3_CHANNELS, - EEPROM_REGULATORY_BAND_4_CHANNELS, - EEPROM_REGULATORY_BAND_5_CHANNELS, - EEPROM_REGULATORY_BAND_NO_HT40, - EEPROM_REGULATORY_BAND_NO_HT40, - }, -}; - -const struct pci_device_id il3945_hw_card_ids[] = { - {IL_PCI_DEVICE(0x4222, 0x1005, il3945_bg_cfg)}, - {IL_PCI_DEVICE(0x4222, 0x1034, il3945_bg_cfg)}, - {IL_PCI_DEVICE(0x4222, 0x1044, il3945_bg_cfg)}, - {IL_PCI_DEVICE(0x4227, 0x1014, il3945_bg_cfg)}, - {IL_PCI_DEVICE(0x4222, PCI_ANY_ID, il3945_abg_cfg)}, - {IL_PCI_DEVICE(0x4227, PCI_ANY_ID, il3945_abg_cfg)}, - {0} -}; - -MODULE_DEVICE_TABLE(pci, il3945_hw_card_ids); diff --git a/drivers/net/wireless/iwlegacy/3945.h b/drivers/net/wireless/iwlegacy/3945.h deleted file mode 100644 index 00030d43a194..000000000000 --- a/drivers/net/wireless/iwlegacy/3945.h +++ /dev/null @@ -1,593 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __il_3945_h__ -#define __il_3945_h__ - -#include /* for struct pci_device_id */ -#include -#include - -/* Hardware specific file defines the PCI IDs table for that hardware module */ -extern const struct pci_device_id il3945_hw_card_ids[]; - -#include "common.h" - -extern const struct il_ops il3945_ops; - -/* Highest firmware API version supported */ -#define IL3945_UCODE_API_MAX 2 - -/* Lowest firmware API version supported */ -#define IL3945_UCODE_API_MIN 1 - -#define IL3945_FW_PRE "iwlwifi-3945-" -#define _IL3945_MODULE_FIRMWARE(api) IL3945_FW_PRE #api ".ucode" -#define IL3945_MODULE_FIRMWARE(api) _IL3945_MODULE_FIRMWARE(api) - -/* Default noise level to report when noise measurement is not available. - * This may be because we're: - * 1) Not associated (4965, no beacon stats being sent to driver) - * 2) Scanning (noise measurement does not apply to associated channel) - * 3) Receiving CCK (3945 delivers noise info only for OFDM frames) - * Use default noise value of -127 ... this is below the range of measurable - * Rx dBm for either 3945 or 4965, so it can indicate "unmeasurable" to user. - * Also, -127 works better than 0 when averaging frames with/without - * noise info (e.g. averaging might be done in app); measured dBm values are - * always negative ... using a negative value as the default keeps all - * averages within an s8's (used in some apps) range of negative values. */ -#define IL_NOISE_MEAS_NOT_AVAILABLE (-127) - -/* Module parameters accessible from iwl-*.c */ -extern struct il_mod_params il3945_mod_params; - -struct il3945_rate_scale_data { - u64 data; - s32 success_counter; - s32 success_ratio; - s32 counter; - s32 average_tpt; - unsigned long stamp; -}; - -struct il3945_rs_sta { - spinlock_t lock; - struct il_priv *il; - s32 *expected_tpt; - unsigned long last_partial_flush; - unsigned long last_flush; - u32 flush_time; - u32 last_tx_packets; - u32 tx_packets; - u8 tgg; - u8 flush_pending; - u8 start_rate; - struct timer_list rate_scale_flush; - struct il3945_rate_scale_data win[RATE_COUNT_3945]; -#ifdef CONFIG_MAC80211_DEBUGFS - struct dentry *rs_sta_dbgfs_stats_table_file; -#endif - - /* used to be in sta_info */ - int last_txrate_idx; -}; - -/* - * The common struct MUST be first because it is shared between - * 3945 and 4965! - */ -struct il3945_sta_priv { - struct il_station_priv_common common; - struct il3945_rs_sta rs_sta; -}; - -enum il3945_antenna { - IL_ANTENNA_DIVERSITY, - IL_ANTENNA_MAIN, - IL_ANTENNA_AUX -}; - -/* - * RTS threshold here is total size [2347] minus 4 FCS bytes - * Per spec: - * a value of 0 means RTS on all data/management packets - * a value > max MSDU size means no RTS - * else RTS for data/management frames where MPDU is larger - * than RTS value. - */ -#define DEFAULT_RTS_THRESHOLD 2347U -#define MIN_RTS_THRESHOLD 0U -#define MAX_RTS_THRESHOLD 2347U -#define MAX_MSDU_SIZE 2304U -#define MAX_MPDU_SIZE 2346U -#define DEFAULT_BEACON_INTERVAL 100U -#define DEFAULT_SHORT_RETRY_LIMIT 7U -#define DEFAULT_LONG_RETRY_LIMIT 4U - -#define IL_TX_FIFO_AC0 0 -#define IL_TX_FIFO_AC1 1 -#define IL_TX_FIFO_AC2 2 -#define IL_TX_FIFO_AC3 3 -#define IL_TX_FIFO_HCCA_1 5 -#define IL_TX_FIFO_HCCA_2 6 -#define IL_TX_FIFO_NONE 7 - -#define IEEE80211_DATA_LEN 2304 -#define IEEE80211_4ADDR_LEN 30 -#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) -#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) - -struct il3945_frame { - union { - struct ieee80211_hdr frame; - struct il3945_tx_beacon_cmd beacon; - u8 raw[IEEE80211_FRAME_LEN]; - u8 cmd[360]; - } u; - struct list_head list; -}; - -#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 -#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 -#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 - -#define IL_SUPPORTED_RATES_IE_LEN 8 - -#define SCAN_INTERVAL 100 - -#define MAX_TID_COUNT 9 - -#define IL_INVALID_RATE 0xFF -#define IL_INVALID_VALUE -1 - -#define STA_PS_STATUS_WAKE 0 -#define STA_PS_STATUS_SLEEP 1 - -struct il3945_ibss_seq { - u8 mac[ETH_ALEN]; - u16 seq_num; - u16 frag_num; - unsigned long packet_time; - struct list_head list; -}; - -#define IL_RX_HDR(x) ((struct il3945_rx_frame_hdr *)(\ - x->u.rx_frame.stats.payload + \ - x->u.rx_frame.stats.phy_count)) -#define IL_RX_END(x) ((struct il3945_rx_frame_end *)(\ - IL_RX_HDR(x)->payload + \ - le16_to_cpu(IL_RX_HDR(x)->len))) -#define IL_RX_STATS(x) (&x->u.rx_frame.stats) -#define IL_RX_DATA(x) (IL_RX_HDR(x)->payload) - -/****************************************************************************** - * - * Functions implemented in iwl3945-base.c which are forward declared here - * for use by iwl-*.c - * - *****************************************************************************/ -int il3945_calc_db_from_ratio(int sig_ratio); -void il3945_rx_replenish(void *data); -void il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq); -unsigned int il3945_fill_beacon_frame(struct il_priv *il, - struct ieee80211_hdr *hdr, int left); -int il3945_dump_nic_event_log(struct il_priv *il, bool full_log, char **buf, - bool display); -void il3945_dump_nic_error_log(struct il_priv *il); - -/****************************************************************************** - * - * Functions implemented in iwl-[34]*.c which are forward declared here - * for use by iwl3945-base.c - * - * NOTE: The implementation of these functions are hardware specific - * which is why they are in the hardware specific files (vs. iwl-base.c) - * - * Naming convention -- - * il3945_ <-- Its part of iwlwifi (should be changed to il3945_) - * il3945_hw_ <-- Hardware specific (implemented in iwl-XXXX.c by all HW) - * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) - * il3945_bg_ <-- Called from work queue context - * il3945_mac_ <-- mac80211 callback - * - ****************************************************************************/ -void il3945_hw_handler_setup(struct il_priv *il); -void il3945_hw_setup_deferred_work(struct il_priv *il); -void il3945_hw_cancel_deferred_work(struct il_priv *il); -int il3945_hw_rxq_stop(struct il_priv *il); -int il3945_hw_set_hw_params(struct il_priv *il); -int il3945_hw_nic_init(struct il_priv *il); -int il3945_hw_nic_stop_master(struct il_priv *il); -void il3945_hw_txq_ctx_free(struct il_priv *il); -void il3945_hw_txq_ctx_stop(struct il_priv *il); -int il3945_hw_nic_reset(struct il_priv *il); -int il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, - dma_addr_t addr, u16 len, u8 reset, u8 pad); -void il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq); -int il3945_hw_get_temperature(struct il_priv *il); -int il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq); -unsigned int il3945_hw_get_beacon_cmd(struct il_priv *il, - struct il3945_frame *frame, u8 rate); -void il3945_hw_build_tx_cmd_rate(struct il_priv *il, struct il_device_cmd *cmd, - struct ieee80211_tx_info *info, - struct ieee80211_hdr *hdr, int sta_id); -int il3945_hw_reg_send_txpower(struct il_priv *il); -int il3945_hw_reg_set_txpower(struct il_priv *il, s8 power); -void il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb); -void il3945_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb); -void il3945_disable_events(struct il_priv *il); -int il4965_get_temperature(const struct il_priv *il); -void il3945_post_associate(struct il_priv *il); -void il3945_config_ap(struct il_priv *il); - -int il3945_commit_rxon(struct il_priv *il); - -/** - * il3945_hw_find_station - Find station id for a given BSSID - * @bssid: MAC address of station ID to find - * - * NOTE: This should not be hardware specific but the code has - * not yet been merged into a single common layer for managing the - * station tables. - */ -u8 il3945_hw_find_station(struct il_priv *il, const u8 *bssid); - -__le32 il3945_get_antenna_flags(const struct il_priv *il); -int il3945_init_hw_rate_table(struct il_priv *il); -void il3945_reg_txpower_periodic(struct il_priv *il); -int il3945_txpower_set_from_eeprom(struct il_priv *il); - -int il3945_rs_next_rate(struct il_priv *il, int rate); - -/* scanning */ -int il3945_request_scan(struct il_priv *il, struct ieee80211_vif *vif); -void il3945_post_scan(struct il_priv *il); - -/* rates */ -extern const struct il3945_rate_info il3945_rates[RATE_COUNT_3945]; - -/* RSSI to dBm */ -#define IL39_RSSI_OFFSET 95 - -/* - * EEPROM related constants, enums, and structures. - */ -#define EEPROM_SKU_CAP_OP_MODE_MRC (1 << 7) - -/* - * Mapping of a Tx power level, at factory calibration temperature, - * to a radio/DSP gain table idx. - * One for each of 5 "sample" power levels in each band. - * v_det is measured at the factory, using the 3945's built-in power amplifier - * (PA) output voltage detector. This same detector is used during Tx of - * long packets in normal operation to provide feedback as to proper output - * level. - * Data copied from EEPROM. - * DO NOT ALTER THIS STRUCTURE!!! - */ -struct il3945_eeprom_txpower_sample { - u8 gain_idx; /* idx into power (gain) setup table ... */ - s8 power; /* ... for this pwr level for this chnl group */ - u16 v_det; /* PA output voltage */ -} __packed; - -/* - * Mappings of Tx power levels -> nominal radio/DSP gain table idxes. - * One for each channel group (a.k.a. "band") (1 for BG, 4 for A). - * Tx power setup code interpolates between the 5 "sample" power levels - * to determine the nominal setup for a requested power level. - * Data copied from EEPROM. - * DO NOT ALTER THIS STRUCTURE!!! - */ -struct il3945_eeprom_txpower_group { - struct il3945_eeprom_txpower_sample samples[5]; /* 5 power levels */ - s32 a, b, c, d, e; /* coefficients for voltage->power - * formula (signed) */ - s32 Fa, Fb, Fc, Fd, Fe; /* these modify coeffs based on - * frequency (signed) */ - s8 saturation_power; /* highest power possible by h/w in this - * band */ - u8 group_channel; /* "representative" channel # in this band */ - s16 temperature; /* h/w temperature at factory calib this band - * (signed) */ -} __packed; - -/* - * Temperature-based Tx-power compensation data, not band-specific. - * These coefficients are use to modify a/b/c/d/e coeffs based on - * difference between current temperature and factory calib temperature. - * Data copied from EEPROM. - */ -struct il3945_eeprom_temperature_corr { - u32 Ta; - u32 Tb; - u32 Tc; - u32 Td; - u32 Te; -} __packed; - -/* - * EEPROM map - */ -struct il3945_eeprom { - u8 reserved0[16]; - u16 device_id; /* abs.ofs: 16 */ - u8 reserved1[2]; - u16 pmc; /* abs.ofs: 20 */ - u8 reserved2[20]; - u8 mac_address[6]; /* abs.ofs: 42 */ - u8 reserved3[58]; - u16 board_revision; /* abs.ofs: 106 */ - u8 reserved4[11]; - u8 board_pba_number[9]; /* abs.ofs: 119 */ - u8 reserved5[8]; - u16 version; /* abs.ofs: 136 */ - u8 sku_cap; /* abs.ofs: 138 */ - u8 leds_mode; /* abs.ofs: 139 */ - u16 oem_mode; - u16 wowlan_mode; /* abs.ofs: 142 */ - u16 leds_time_interval; /* abs.ofs: 144 */ - u8 leds_off_time; /* abs.ofs: 146 */ - u8 leds_on_time; /* abs.ofs: 147 */ - u8 almgor_m_version; /* abs.ofs: 148 */ - u8 antenna_switch_type; /* abs.ofs: 149 */ - u8 reserved6[42]; - u8 sku_id[4]; /* abs.ofs: 192 */ - -/* - * Per-channel regulatory data. - * - * Each channel that *might* be supported by 3945 has a fixed location - * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory - * txpower (MSB). - * - * Entries immediately below are for 20 MHz channel width. - * - * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 - */ - u16 band_1_count; /* abs.ofs: 196 */ - struct il_eeprom_channel band_1_channels[14]; /* abs.ofs: 198 */ - -/* - * 4.9 GHz channels 183, 184, 185, 187, 188, 189, 192, 196, - * 5.0 GHz channels 7, 8, 11, 12, 16 - * (4915-5080MHz) (none of these is ever supported) - */ - u16 band_2_count; /* abs.ofs: 226 */ - struct il_eeprom_channel band_2_channels[13]; /* abs.ofs: 228 */ - -/* - * 5.2 GHz channels 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 - * (5170-5320MHz) - */ - u16 band_3_count; /* abs.ofs: 254 */ - struct il_eeprom_channel band_3_channels[12]; /* abs.ofs: 256 */ - -/* - * 5.5 GHz channels 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 - * (5500-5700MHz) - */ - u16 band_4_count; /* abs.ofs: 280 */ - struct il_eeprom_channel band_4_channels[11]; /* abs.ofs: 282 */ - -/* - * 5.7 GHz channels 145, 149, 153, 157, 161, 165 - * (5725-5825MHz) - */ - u16 band_5_count; /* abs.ofs: 304 */ - struct il_eeprom_channel band_5_channels[6]; /* abs.ofs: 306 */ - - u8 reserved9[194]; - -/* - * 3945 Txpower calibration data. - */ -#define IL_NUM_TX_CALIB_GROUPS 5 - struct il3945_eeprom_txpower_group groups[IL_NUM_TX_CALIB_GROUPS]; -/* abs.ofs: 512 */ - struct il3945_eeprom_temperature_corr corrections; /* abs.ofs: 832 */ - u8 reserved16[172]; /* fill out to full 1024 byte block */ -} __packed; - -#define IL3945_EEPROM_IMG_SIZE 1024 - -/* End of EEPROM */ - -#define PCI_CFG_REV_ID_BIT_BASIC_SKU (0x40) /* bit 6 */ -#define PCI_CFG_REV_ID_BIT_RTP (0x80) /* bit 7 */ - -/* 4 DATA + 1 CMD. There are 2 HCCA queues that are not used. */ -#define IL39_NUM_QUEUES 5 -#define IL39_CMD_QUEUE_NUM 4 - -#define IL_DEFAULT_TX_RETRY 15 - -/*********************************************/ - -#define RFD_SIZE 4 -#define NUM_TFD_CHUNKS 4 - -#define TFD_CTL_COUNT_SET(n) (n << 24) -#define TFD_CTL_COUNT_GET(ctl) ((ctl >> 24) & 7) -#define TFD_CTL_PAD_SET(n) (n << 28) -#define TFD_CTL_PAD_GET(ctl) (ctl >> 28) - -/* Sizes and addresses for instruction and data memory (SRAM) in - * 3945's embedded processor. Driver access is via HBUS_TARG_MEM_* regs. */ -#define IL39_RTC_INST_LOWER_BOUND (0x000000) -#define IL39_RTC_INST_UPPER_BOUND (0x014000) - -#define IL39_RTC_DATA_LOWER_BOUND (0x800000) -#define IL39_RTC_DATA_UPPER_BOUND (0x808000) - -#define IL39_RTC_INST_SIZE (IL39_RTC_INST_UPPER_BOUND - \ - IL39_RTC_INST_LOWER_BOUND) -#define IL39_RTC_DATA_SIZE (IL39_RTC_DATA_UPPER_BOUND - \ - IL39_RTC_DATA_LOWER_BOUND) - -#define IL39_MAX_INST_SIZE IL39_RTC_INST_SIZE -#define IL39_MAX_DATA_SIZE IL39_RTC_DATA_SIZE - -/* Size of uCode instruction memory in bootstrap state machine */ -#define IL39_MAX_BSM_SIZE IL39_RTC_INST_SIZE - -static inline int -il3945_hw_valid_rtc_data_addr(u32 addr) -{ - return (addr >= IL39_RTC_DATA_LOWER_BOUND && - addr < IL39_RTC_DATA_UPPER_BOUND); -} - -/* Base physical address of il3945_shared is provided to FH39_TSSR_CBB_BASE - * and &il3945_shared.rx_read_ptr[0] is provided to FH39_RCSR_RPTR_ADDR(0) */ -struct il3945_shared { - __le32 tx_base_ptr[8]; -} __packed; - -/************************************/ -/* iwl3945 Flow Handler Definitions */ -/************************************/ - -/** - * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) - * Addresses are offsets from device's PCI hardware base address. - */ -#define FH39_MEM_LOWER_BOUND (0x0800) -#define FH39_MEM_UPPER_BOUND (0x1000) - -#define FH39_CBCC_TBL (FH39_MEM_LOWER_BOUND + 0x140) -#define FH39_TFDB_TBL (FH39_MEM_LOWER_BOUND + 0x180) -#define FH39_RCSR_TBL (FH39_MEM_LOWER_BOUND + 0x400) -#define FH39_RSSR_TBL (FH39_MEM_LOWER_BOUND + 0x4c0) -#define FH39_TCSR_TBL (FH39_MEM_LOWER_BOUND + 0x500) -#define FH39_TSSR_TBL (FH39_MEM_LOWER_BOUND + 0x680) - -/* TFDB (Transmit Frame Buffer Descriptor) */ -#define FH39_TFDB(_ch, buf) (FH39_TFDB_TBL + \ - ((_ch) * 2 + (buf)) * 0x28) -#define FH39_TFDB_CHNL_BUF_CTRL_REG(_ch) (FH39_TFDB_TBL + 0x50 * (_ch)) - -/* CBCC channel is [0,2] */ -#define FH39_CBCC(_ch) (FH39_CBCC_TBL + (_ch) * 0x8) -#define FH39_CBCC_CTRL(_ch) (FH39_CBCC(_ch) + 0x00) -#define FH39_CBCC_BASE(_ch) (FH39_CBCC(_ch) + 0x04) - -/* RCSR channel is [0,2] */ -#define FH39_RCSR(_ch) (FH39_RCSR_TBL + (_ch) * 0x40) -#define FH39_RCSR_CONFIG(_ch) (FH39_RCSR(_ch) + 0x00) -#define FH39_RCSR_RBD_BASE(_ch) (FH39_RCSR(_ch) + 0x04) -#define FH39_RCSR_WPTR(_ch) (FH39_RCSR(_ch) + 0x20) -#define FH39_RCSR_RPTR_ADDR(_ch) (FH39_RCSR(_ch) + 0x24) - -#define FH39_RSCSR_CHNL0_WPTR (FH39_RCSR_WPTR(0)) - -/* RSSR */ -#define FH39_RSSR_CTRL (FH39_RSSR_TBL + 0x000) -#define FH39_RSSR_STATUS (FH39_RSSR_TBL + 0x004) - -/* TCSR */ -#define FH39_TCSR(_ch) (FH39_TCSR_TBL + (_ch) * 0x20) -#define FH39_TCSR_CONFIG(_ch) (FH39_TCSR(_ch) + 0x00) -#define FH39_TCSR_CREDIT(_ch) (FH39_TCSR(_ch) + 0x04) -#define FH39_TCSR_BUFF_STTS(_ch) (FH39_TCSR(_ch) + 0x08) - -/* TSSR */ -#define FH39_TSSR_CBB_BASE (FH39_TSSR_TBL + 0x000) -#define FH39_TSSR_MSG_CONFIG (FH39_TSSR_TBL + 0x008) -#define FH39_TSSR_TX_STATUS (FH39_TSSR_TBL + 0x010) - -/* DBM */ - -#define FH39_SRVC_CHNL (6) - -#define FH39_RCSR_RX_CONFIG_REG_POS_RBDC_SIZE (20) -#define FH39_RCSR_RX_CONFIG_REG_POS_IRQ_RBTH (4) - -#define FH39_RCSR_RX_CONFIG_REG_BIT_WR_STTS_EN (0x08000000) - -#define FH39_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN_ENABLE (0x80000000) - -#define FH39_RCSR_RX_CONFIG_REG_VAL_RDRBD_EN_ENABLE (0x20000000) - -#define FH39_RCSR_RX_CONFIG_REG_VAL_MAX_FRAG_SIZE_128 (0x01000000) - -#define FH39_RCSR_RX_CONFIG_REG_VAL_IRQ_DEST_INT_HOST (0x00001000) - -#define FH39_RCSR_RX_CONFIG_REG_VAL_MSG_MODE_FH (0x00000000) - -#define FH39_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000) -#define FH39_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRIVER (0x00000001) - -#define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE_VAL (0x00000000) -#define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL (0x00000008) - -#define FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000) - -#define FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000) - -#define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000) -#define FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000) - -#define FH39_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00004000) - -#define FH39_TCSR_CHNL_TX_BUF_STS_REG_BIT_TFDB_WPTR (0x00000001) - -#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON (0xFF000000) -#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON (0x00FF0000) - -#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B (0x00000400) - -#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TFD_ON (0x00000100) -#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_CBB_ON (0x00000080) - -#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH (0x00000020) -#define FH39_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH (0x00000005) - -#define FH39_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_ch) (BIT(_ch) << 24) -#define FH39_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_ch) (BIT(_ch) << 16) - -#define FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_ch) \ - (FH39_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_ch) | \ - FH39_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_ch)) - -#define FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) - -struct il3945_tfd_tb { - __le32 addr; - __le32 len; -} __packed; - -struct il3945_tfd { - __le32 control_flags; - struct il3945_tfd_tb tbs[4]; - u8 __pad[28]; -} __packed; - -#ifdef CONFIG_IWLEGACY_DEBUGFS -extern const struct il_debugfs_ops il3945_debugfs_ops; -#endif - -#endif diff --git a/drivers/net/wireless/iwlegacy/4965-calib.c b/drivers/net/wireless/iwlegacy/4965-calib.c deleted file mode 100644 index e78bdefb8952..000000000000 --- a/drivers/net/wireless/iwlegacy/4965-calib.c +++ /dev/null @@ -1,934 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#include -#include - -#include "common.h" -#include "4965.h" - -/***************************************************************************** - * INIT calibrations framework - *****************************************************************************/ - -struct stats_general_data { - u32 beacon_silence_rssi_a; - u32 beacon_silence_rssi_b; - u32 beacon_silence_rssi_c; - u32 beacon_energy_a; - u32 beacon_energy_b; - u32 beacon_energy_c; -}; - -/***************************************************************************** - * RUNTIME calibrations framework - *****************************************************************************/ - -/* "false alarms" are signals that our DSP tries to lock onto, - * but then determines that they are either noise, or transmissions - * from a distant wireless network (also "noise", really) that get - * "stepped on" by stronger transmissions within our own network. - * This algorithm attempts to set a sensitivity level that is high - * enough to receive all of our own network traffic, but not so - * high that our DSP gets too busy trying to lock onto non-network - * activity/noise. */ -static int -il4965_sens_energy_cck(struct il_priv *il, u32 norm_fa, u32 rx_enable_time, - struct stats_general_data *rx_info) -{ - u32 max_nrg_cck = 0; - int i = 0; - u8 max_silence_rssi = 0; - u32 silence_ref = 0; - u8 silence_rssi_a = 0; - u8 silence_rssi_b = 0; - u8 silence_rssi_c = 0; - u32 val; - - /* "false_alarms" values below are cross-multiplications to assess the - * numbers of false alarms within the measured period of actual Rx - * (Rx is off when we're txing), vs the min/max expected false alarms - * (some should be expected if rx is sensitive enough) in a - * hypothetical listening period of 200 time units (TU), 204.8 msec: - * - * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time - * - * */ - u32 false_alarms = norm_fa * 200 * 1024; - u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; - u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; - struct il_sensitivity_data *data = NULL; - const struct il_sensitivity_ranges *ranges = il->hw_params.sens; - - data = &(il->sensitivity_data); - - data->nrg_auto_corr_silence_diff = 0; - - /* Find max silence rssi among all 3 receivers. - * This is background noise, which may include transmissions from other - * networks, measured during silence before our network's beacon */ - silence_rssi_a = - (u8) ((rx_info->beacon_silence_rssi_a & ALL_BAND_FILTER) >> 8); - silence_rssi_b = - (u8) ((rx_info->beacon_silence_rssi_b & ALL_BAND_FILTER) >> 8); - silence_rssi_c = - (u8) ((rx_info->beacon_silence_rssi_c & ALL_BAND_FILTER) >> 8); - - val = max(silence_rssi_b, silence_rssi_c); - max_silence_rssi = max(silence_rssi_a, (u8) val); - - /* Store silence rssi in 20-beacon history table */ - data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; - data->nrg_silence_idx++; - if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) - data->nrg_silence_idx = 0; - - /* Find max silence rssi across 20 beacon history */ - for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { - val = data->nrg_silence_rssi[i]; - silence_ref = max(silence_ref, val); - } - D_CALIB("silence a %u, b %u, c %u, 20-bcn max %u\n", silence_rssi_a, - silence_rssi_b, silence_rssi_c, silence_ref); - - /* Find max rx energy (min value!) among all 3 receivers, - * measured during beacon frame. - * Save it in 10-beacon history table. */ - i = data->nrg_energy_idx; - val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); - data->nrg_value[i] = min(rx_info->beacon_energy_a, val); - - data->nrg_energy_idx++; - if (data->nrg_energy_idx >= 10) - data->nrg_energy_idx = 0; - - /* Find min rx energy (max value) across 10 beacon history. - * This is the minimum signal level that we want to receive well. - * Add backoff (margin so we don't miss slightly lower energy frames). - * This establishes an upper bound (min value) for energy threshold. */ - max_nrg_cck = data->nrg_value[0]; - for (i = 1; i < 10; i++) - max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); - max_nrg_cck += 6; - - D_CALIB("rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", - rx_info->beacon_energy_a, rx_info->beacon_energy_b, - rx_info->beacon_energy_c, max_nrg_cck - 6); - - /* Count number of consecutive beacons with fewer-than-desired - * false alarms. */ - if (false_alarms < min_false_alarms) - data->num_in_cck_no_fa++; - else - data->num_in_cck_no_fa = 0; - D_CALIB("consecutive bcns with few false alarms = %u\n", - data->num_in_cck_no_fa); - - /* If we got too many false alarms this time, reduce sensitivity */ - if (false_alarms > max_false_alarms && - data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK) { - D_CALIB("norm FA %u > max FA %u\n", false_alarms, - max_false_alarms); - D_CALIB("... reducing sensitivity\n"); - data->nrg_curr_state = IL_FA_TOO_MANY; - /* Store for "fewer than desired" on later beacon */ - data->nrg_silence_ref = silence_ref; - - /* increase energy threshold (reduce nrg value) - * to decrease sensitivity */ - data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; - /* Else if we got fewer than desired, increase sensitivity */ - } else if (false_alarms < min_false_alarms) { - data->nrg_curr_state = IL_FA_TOO_FEW; - - /* Compare silence level with silence level for most recent - * healthy number or too many false alarms */ - data->nrg_auto_corr_silence_diff = - (s32) data->nrg_silence_ref - (s32) silence_ref; - - D_CALIB("norm FA %u < min FA %u, silence diff %d\n", - false_alarms, min_false_alarms, - data->nrg_auto_corr_silence_diff); - - /* Increase value to increase sensitivity, but only if: - * 1a) previous beacon did *not* have *too many* false alarms - * 1b) AND there's a significant difference in Rx levels - * from a previous beacon with too many, or healthy # FAs - * OR 2) We've seen a lot of beacons (100) with too few - * false alarms */ - if (data->nrg_prev_state != IL_FA_TOO_MANY && - (data->nrg_auto_corr_silence_diff > NRG_DIFF || - data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) { - - D_CALIB("... increasing sensitivity\n"); - /* Increase nrg value to increase sensitivity */ - val = data->nrg_th_cck + NRG_STEP_CCK; - data->nrg_th_cck = min((u32) ranges->min_nrg_cck, val); - } else { - D_CALIB("... but not changing sensitivity\n"); - } - - /* Else we got a healthy number of false alarms, keep status quo */ - } else { - D_CALIB(" FA in safe zone\n"); - data->nrg_curr_state = IL_FA_GOOD_RANGE; - - /* Store for use in "fewer than desired" with later beacon */ - data->nrg_silence_ref = silence_ref; - - /* If previous beacon had too many false alarms, - * give it some extra margin by reducing sensitivity again - * (but don't go below measured energy of desired Rx) */ - if (IL_FA_TOO_MANY == data->nrg_prev_state) { - D_CALIB("... increasing margin\n"); - if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) - data->nrg_th_cck -= NRG_MARGIN; - else - data->nrg_th_cck = max_nrg_cck; - } - } - - /* Make sure the energy threshold does not go above the measured - * energy of the desired Rx signals (reduced by backoff margin), - * or else we might start missing Rx frames. - * Lower value is higher energy, so we use max()! - */ - data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); - D_CALIB("new nrg_th_cck %u\n", data->nrg_th_cck); - - data->nrg_prev_state = data->nrg_curr_state; - - /* Auto-correlation CCK algorithm */ - if (false_alarms > min_false_alarms) { - - /* increase auto_corr values to decrease sensitivity - * so the DSP won't be disturbed by the noise - */ - if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) - data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; - else { - val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; - data->auto_corr_cck = - min((u32) ranges->auto_corr_max_cck, val); - } - val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; - data->auto_corr_cck_mrc = - min((u32) ranges->auto_corr_max_cck_mrc, val); - } else if (false_alarms < min_false_alarms && - (data->nrg_auto_corr_silence_diff > NRG_DIFF || - data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA)) { - - /* Decrease auto_corr values to increase sensitivity */ - val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; - data->auto_corr_cck = max((u32) ranges->auto_corr_min_cck, val); - val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; - data->auto_corr_cck_mrc = - max((u32) ranges->auto_corr_min_cck_mrc, val); - } - - return 0; -} - -static int -il4965_sens_auto_corr_ofdm(struct il_priv *il, u32 norm_fa, u32 rx_enable_time) -{ - u32 val; - u32 false_alarms = norm_fa * 200 * 1024; - u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; - u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; - struct il_sensitivity_data *data = NULL; - const struct il_sensitivity_ranges *ranges = il->hw_params.sens; - - data = &(il->sensitivity_data); - - /* If we got too many false alarms this time, reduce sensitivity */ - if (false_alarms > max_false_alarms) { - - D_CALIB("norm FA %u > max FA %u)\n", false_alarms, - max_false_alarms); - - val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm = - min((u32) ranges->auto_corr_max_ofdm, val); - - val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc = - min((u32) ranges->auto_corr_max_ofdm_mrc, val); - - val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_x1 = - min((u32) ranges->auto_corr_max_ofdm_x1, val); - - val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc_x1 = - min((u32) ranges->auto_corr_max_ofdm_mrc_x1, val); - } - - /* Else if we got fewer than desired, increase sensitivity */ - else if (false_alarms < min_false_alarms) { - - D_CALIB("norm FA %u < min FA %u\n", false_alarms, - min_false_alarms); - - val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm = - max((u32) ranges->auto_corr_min_ofdm, val); - - val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc = - max((u32) ranges->auto_corr_min_ofdm_mrc, val); - - val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_x1 = - max((u32) ranges->auto_corr_min_ofdm_x1, val); - - val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc_x1 = - max((u32) ranges->auto_corr_min_ofdm_mrc_x1, val); - } else { - D_CALIB("min FA %u < norm FA %u < max FA %u OK\n", - min_false_alarms, false_alarms, max_false_alarms); - } - return 0; -} - -static void -il4965_prepare_legacy_sensitivity_tbl(struct il_priv *il, - struct il_sensitivity_data *data, - __le16 *tbl) -{ - tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX] = - cpu_to_le16((u16) data->auto_corr_ofdm); - tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX] = - cpu_to_le16((u16) data->auto_corr_ofdm_mrc); - tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX] = - cpu_to_le16((u16) data->auto_corr_ofdm_x1); - tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX] = - cpu_to_le16((u16) data->auto_corr_ofdm_mrc_x1); - - tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX] = - cpu_to_le16((u16) data->auto_corr_cck); - tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX] = - cpu_to_le16((u16) data->auto_corr_cck_mrc); - - tbl[HD_MIN_ENERGY_CCK_DET_IDX] = cpu_to_le16((u16) data->nrg_th_cck); - tbl[HD_MIN_ENERGY_OFDM_DET_IDX] = cpu_to_le16((u16) data->nrg_th_ofdm); - - tbl[HD_BARKER_CORR_TH_ADD_MIN_IDX] = - cpu_to_le16(data->barker_corr_th_min); - tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_IDX] = - cpu_to_le16(data->barker_corr_th_min_mrc); - tbl[HD_OFDM_ENERGY_TH_IN_IDX] = cpu_to_le16(data->nrg_th_cca); - - D_CALIB("ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", - data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, - data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, - data->nrg_th_ofdm); - - D_CALIB("cck: ac %u mrc %u thresh %u\n", data->auto_corr_cck, - data->auto_corr_cck_mrc, data->nrg_th_cck); -} - -/* Prepare a C_SENSITIVITY, send to uCode if values have changed */ -static int -il4965_sensitivity_write(struct il_priv *il) -{ - struct il_sensitivity_cmd cmd; - struct il_sensitivity_data *data = NULL; - struct il_host_cmd cmd_out = { - .id = C_SENSITIVITY, - .len = sizeof(struct il_sensitivity_cmd), - .flags = CMD_ASYNC, - .data = &cmd, - }; - - data = &(il->sensitivity_data); - - memset(&cmd, 0, sizeof(cmd)); - - il4965_prepare_legacy_sensitivity_tbl(il, data, &cmd.table[0]); - - /* Update uCode's "work" table, and copy it to DSP */ - cmd.control = C_SENSITIVITY_CONTROL_WORK_TBL; - - /* Don't send command to uCode if nothing has changed */ - if (!memcmp - (&cmd.table[0], &(il->sensitivity_tbl[0]), - sizeof(u16) * HD_TBL_SIZE)) { - D_CALIB("No change in C_SENSITIVITY\n"); - return 0; - } - - /* Copy table for comparison next time */ - memcpy(&(il->sensitivity_tbl[0]), &(cmd.table[0]), - sizeof(u16) * HD_TBL_SIZE); - - return il_send_cmd(il, &cmd_out); -} - -void -il4965_init_sensitivity(struct il_priv *il) -{ - int ret = 0; - int i; - struct il_sensitivity_data *data = NULL; - const struct il_sensitivity_ranges *ranges = il->hw_params.sens; - - if (il->disable_sens_cal) - return; - - D_CALIB("Start il4965_init_sensitivity\n"); - - /* Clear driver's sensitivity algo data */ - data = &(il->sensitivity_data); - - if (ranges == NULL) - return; - - memset(data, 0, sizeof(struct il_sensitivity_data)); - - data->num_in_cck_no_fa = 0; - data->nrg_curr_state = IL_FA_TOO_MANY; - data->nrg_prev_state = IL_FA_TOO_MANY; - data->nrg_silence_ref = 0; - data->nrg_silence_idx = 0; - data->nrg_energy_idx = 0; - - for (i = 0; i < 10; i++) - data->nrg_value[i] = 0; - - for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) - data->nrg_silence_rssi[i] = 0; - - data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; - data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; - data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; - data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; - data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; - data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; - data->nrg_th_cck = ranges->nrg_th_cck; - data->nrg_th_ofdm = ranges->nrg_th_ofdm; - data->barker_corr_th_min = ranges->barker_corr_th_min; - data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; - data->nrg_th_cca = ranges->nrg_th_cca; - - data->last_bad_plcp_cnt_ofdm = 0; - data->last_fa_cnt_ofdm = 0; - data->last_bad_plcp_cnt_cck = 0; - data->last_fa_cnt_cck = 0; - - ret |= il4965_sensitivity_write(il); - D_CALIB("<disable_sens_cal) - return; - - data = &(il->sensitivity_data); - - if (!il_is_any_associated(il)) { - D_CALIB("<< - not associated\n"); - return; - } - - spin_lock_irqsave(&il->lock, flags); - - rx_info = &(((struct il_notif_stats *)resp)->rx.general); - ofdm = &(((struct il_notif_stats *)resp)->rx.ofdm); - cck = &(((struct il_notif_stats *)resp)->rx.cck); - - if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { - D_CALIB("<< invalid data.\n"); - spin_unlock_irqrestore(&il->lock, flags); - return; - } - - /* Extract Statistics: */ - rx_enable_time = le32_to_cpu(rx_info->channel_load); - fa_cck = le32_to_cpu(cck->false_alarm_cnt); - fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); - bad_plcp_cck = le32_to_cpu(cck->plcp_err); - bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); - - statis.beacon_silence_rssi_a = - le32_to_cpu(rx_info->beacon_silence_rssi_a); - statis.beacon_silence_rssi_b = - le32_to_cpu(rx_info->beacon_silence_rssi_b); - statis.beacon_silence_rssi_c = - le32_to_cpu(rx_info->beacon_silence_rssi_c); - statis.beacon_energy_a = le32_to_cpu(rx_info->beacon_energy_a); - statis.beacon_energy_b = le32_to_cpu(rx_info->beacon_energy_b); - statis.beacon_energy_c = le32_to_cpu(rx_info->beacon_energy_c); - - spin_unlock_irqrestore(&il->lock, flags); - - D_CALIB("rx_enable_time = %u usecs\n", rx_enable_time); - - if (!rx_enable_time) { - D_CALIB("<< RX Enable Time == 0!\n"); - return; - } - - /* These stats increase monotonically, and do not reset - * at each beacon. Calculate difference from last value, or just - * use the new stats value if it has reset or wrapped around. */ - if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) - data->last_bad_plcp_cnt_cck = bad_plcp_cck; - else { - bad_plcp_cck -= data->last_bad_plcp_cnt_cck; - data->last_bad_plcp_cnt_cck += bad_plcp_cck; - } - - if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) - data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; - else { - bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; - data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; - } - - if (data->last_fa_cnt_ofdm > fa_ofdm) - data->last_fa_cnt_ofdm = fa_ofdm; - else { - fa_ofdm -= data->last_fa_cnt_ofdm; - data->last_fa_cnt_ofdm += fa_ofdm; - } - - if (data->last_fa_cnt_cck > fa_cck) - data->last_fa_cnt_cck = fa_cck; - else { - fa_cck -= data->last_fa_cnt_cck; - data->last_fa_cnt_cck += fa_cck; - } - - /* Total aborted signal locks */ - norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; - norm_fa_cck = fa_cck + bad_plcp_cck; - - D_CALIB("cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, - bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); - - il4965_sens_auto_corr_ofdm(il, norm_fa_ofdm, rx_enable_time); - il4965_sens_energy_cck(il, norm_fa_cck, rx_enable_time, &statis); - - il4965_sensitivity_write(il); -} - -static inline u8 -il4965_find_first_chain(u8 mask) -{ - if (mask & ANT_A) - return CHAIN_A; - if (mask & ANT_B) - return CHAIN_B; - return CHAIN_C; -} - -/** - * Run disconnected antenna algorithm to find out which antennas are - * disconnected. - */ -static void -il4965_find_disconn_antenna(struct il_priv *il, u32 * average_sig, - struct il_chain_noise_data *data) -{ - u32 active_chains = 0; - u32 max_average_sig; - u16 max_average_sig_antenna_i; - u8 num_tx_chains; - u8 first_chain; - u16 i = 0; - - average_sig[0] = - data->chain_signal_a / - il->cfg->chain_noise_num_beacons; - average_sig[1] = - data->chain_signal_b / - il->cfg->chain_noise_num_beacons; - average_sig[2] = - data->chain_signal_c / - il->cfg->chain_noise_num_beacons; - - if (average_sig[0] >= average_sig[1]) { - max_average_sig = average_sig[0]; - max_average_sig_antenna_i = 0; - active_chains = (1 << max_average_sig_antenna_i); - } else { - max_average_sig = average_sig[1]; - max_average_sig_antenna_i = 1; - active_chains = (1 << max_average_sig_antenna_i); - } - - if (average_sig[2] >= max_average_sig) { - max_average_sig = average_sig[2]; - max_average_sig_antenna_i = 2; - active_chains = (1 << max_average_sig_antenna_i); - } - - D_CALIB("average_sig: a %d b %d c %d\n", average_sig[0], average_sig[1], - average_sig[2]); - D_CALIB("max_average_sig = %d, antenna %d\n", max_average_sig, - max_average_sig_antenna_i); - - /* Compare signal strengths for all 3 receivers. */ - for (i = 0; i < NUM_RX_CHAINS; i++) { - if (i != max_average_sig_antenna_i) { - s32 rssi_delta = (max_average_sig - average_sig[i]); - - /* If signal is very weak, compared with - * strongest, mark it as disconnected. */ - if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) - data->disconn_array[i] = 1; - else - active_chains |= (1 << i); - D_CALIB("i = %d rssiDelta = %d " - "disconn_array[i] = %d\n", i, rssi_delta, - data->disconn_array[i]); - } - } - - /* - * The above algorithm sometimes fails when the ucode - * reports 0 for all chains. It's not clear why that - * happens to start with, but it is then causing trouble - * because this can make us enable more chains than the - * hardware really has. - * - * To be safe, simply mask out any chains that we know - * are not on the device. - */ - active_chains &= il->hw_params.valid_rx_ant; - - num_tx_chains = 0; - for (i = 0; i < NUM_RX_CHAINS; i++) { - /* loops on all the bits of - * il->hw_setting.valid_tx_ant */ - u8 ant_msk = (1 << i); - if (!(il->hw_params.valid_tx_ant & ant_msk)) - continue; - - num_tx_chains++; - if (data->disconn_array[i] == 0) - /* there is a Tx antenna connected */ - break; - if (num_tx_chains == il->hw_params.tx_chains_num && - data->disconn_array[i]) { - /* - * If all chains are disconnected - * connect the first valid tx chain - */ - first_chain = - il4965_find_first_chain(il->cfg->valid_tx_ant); - data->disconn_array[first_chain] = 0; - active_chains |= BIT(first_chain); - D_CALIB("All Tx chains are disconnected" - "- declare %d as connected\n", first_chain); - break; - } - } - - if (active_chains != il->hw_params.valid_rx_ant && - active_chains != il->chain_noise_data.active_chains) - D_CALIB("Detected that not all antennas are connected! " - "Connected: %#x, valid: %#x.\n", active_chains, - il->hw_params.valid_rx_ant); - - /* Save for use within RXON, TX, SCAN commands, etc. */ - data->active_chains = active_chains; - D_CALIB("active_chains (bitwise) = 0x%x\n", active_chains); -} - -static void -il4965_gain_computation(struct il_priv *il, u32 * average_noise, - u16 min_average_noise_antenna_i, u32 min_average_noise, - u8 default_chain) -{ - int i, ret; - struct il_chain_noise_data *data = &il->chain_noise_data; - - data->delta_gain_code[min_average_noise_antenna_i] = 0; - - for (i = default_chain; i < NUM_RX_CHAINS; i++) { - s32 delta_g = 0; - - if (!data->disconn_array[i] && - data->delta_gain_code[i] == - CHAIN_NOISE_DELTA_GAIN_INIT_VAL) { - delta_g = average_noise[i] - min_average_noise; - data->delta_gain_code[i] = (u8) ((delta_g * 10) / 15); - data->delta_gain_code[i] = - min(data->delta_gain_code[i], - (u8) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); - - data->delta_gain_code[i] = - (data->delta_gain_code[i] | (1 << 2)); - } else { - data->delta_gain_code[i] = 0; - } - } - D_CALIB("delta_gain_codes: a %d b %d c %d\n", data->delta_gain_code[0], - data->delta_gain_code[1], data->delta_gain_code[2]); - - /* Differential gain gets sent to uCode only once */ - if (!data->radio_write) { - struct il_calib_diff_gain_cmd cmd; - data->radio_write = 1; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD; - cmd.diff_gain_a = data->delta_gain_code[0]; - cmd.diff_gain_b = data->delta_gain_code[1]; - cmd.diff_gain_c = data->delta_gain_code[2]; - ret = il_send_cmd_pdu(il, C_PHY_CALIBRATION, sizeof(cmd), &cmd); - if (ret) - D_CALIB("fail sending cmd " "C_PHY_CALIBRATION\n"); - - /* TODO we might want recalculate - * rx_chain in rxon cmd */ - - /* Mark so we run this algo only once! */ - data->state = IL_CHAIN_NOISE_CALIBRATED; - } -} - -/* - * Accumulate 16 beacons of signal and noise stats for each of - * 3 receivers/antennas/rx-chains, then figure out: - * 1) Which antennas are connected. - * 2) Differential rx gain settings to balance the 3 receivers. - */ -void -il4965_chain_noise_calibration(struct il_priv *il, void *stat_resp) -{ - struct il_chain_noise_data *data = NULL; - - u32 chain_noise_a; - u32 chain_noise_b; - u32 chain_noise_c; - u32 chain_sig_a; - u32 chain_sig_b; - u32 chain_sig_c; - u32 average_sig[NUM_RX_CHAINS] = { INITIALIZATION_VALUE }; - u32 average_noise[NUM_RX_CHAINS] = { INITIALIZATION_VALUE }; - u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; - u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; - u16 i = 0; - u16 rxon_chnum = INITIALIZATION_VALUE; - u16 stat_chnum = INITIALIZATION_VALUE; - u8 rxon_band24; - u8 stat_band24; - unsigned long flags; - struct stats_rx_non_phy *rx_info; - - if (il->disable_chain_noise_cal) - return; - - data = &(il->chain_noise_data); - - /* - * Accumulate just the first "chain_noise_num_beacons" after - * the first association, then we're done forever. - */ - if (data->state != IL_CHAIN_NOISE_ACCUMULATE) { - if (data->state == IL_CHAIN_NOISE_ALIVE) - D_CALIB("Wait for noise calib reset\n"); - return; - } - - spin_lock_irqsave(&il->lock, flags); - - rx_info = &(((struct il_notif_stats *)stat_resp)->rx.general); - - if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { - D_CALIB(" << Interference data unavailable\n"); - spin_unlock_irqrestore(&il->lock, flags); - return; - } - - rxon_band24 = !!(il->staging.flags & RXON_FLG_BAND_24G_MSK); - rxon_chnum = le16_to_cpu(il->staging.channel); - - stat_band24 = - !!(((struct il_notif_stats *)stat_resp)-> - flag & STATS_REPLY_FLG_BAND_24G_MSK); - stat_chnum = - le32_to_cpu(((struct il_notif_stats *)stat_resp)->flag) >> 16; - - /* Make sure we accumulate data for just the associated channel - * (even if scanning). */ - if (rxon_chnum != stat_chnum || rxon_band24 != stat_band24) { - D_CALIB("Stats not from chan=%d, band24=%d\n", rxon_chnum, - rxon_band24); - spin_unlock_irqrestore(&il->lock, flags); - return; - } - - /* - * Accumulate beacon stats values across - * "chain_noise_num_beacons" - */ - chain_noise_a = - le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; - chain_noise_b = - le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; - chain_noise_c = - le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; - - chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; - chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; - chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; - - spin_unlock_irqrestore(&il->lock, flags); - - data->beacon_count++; - - data->chain_noise_a = (chain_noise_a + data->chain_noise_a); - data->chain_noise_b = (chain_noise_b + data->chain_noise_b); - data->chain_noise_c = (chain_noise_c + data->chain_noise_c); - - data->chain_signal_a = (chain_sig_a + data->chain_signal_a); - data->chain_signal_b = (chain_sig_b + data->chain_signal_b); - data->chain_signal_c = (chain_sig_c + data->chain_signal_c); - - D_CALIB("chan=%d, band24=%d, beacon=%d\n", rxon_chnum, rxon_band24, - data->beacon_count); - D_CALIB("chain_sig: a %d b %d c %d\n", chain_sig_a, chain_sig_b, - chain_sig_c); - D_CALIB("chain_noise: a %d b %d c %d\n", chain_noise_a, chain_noise_b, - chain_noise_c); - - /* If this is the "chain_noise_num_beacons", determine: - * 1) Disconnected antennas (using signal strengths) - * 2) Differential gain (using silence noise) to balance receivers */ - if (data->beacon_count != il->cfg->chain_noise_num_beacons) - return; - - /* Analyze signal for disconnected antenna */ - il4965_find_disconn_antenna(il, average_sig, data); - - /* Analyze noise for rx balance */ - average_noise[0] = - data->chain_noise_a / il->cfg->chain_noise_num_beacons; - average_noise[1] = - data->chain_noise_b / il->cfg->chain_noise_num_beacons; - average_noise[2] = - data->chain_noise_c / il->cfg->chain_noise_num_beacons; - - for (i = 0; i < NUM_RX_CHAINS; i++) { - if (!data->disconn_array[i] && - average_noise[i] <= min_average_noise) { - /* This means that chain i is active and has - * lower noise values so far: */ - min_average_noise = average_noise[i]; - min_average_noise_antenna_i = i; - } - } - - D_CALIB("average_noise: a %d b %d c %d\n", average_noise[0], - average_noise[1], average_noise[2]); - - D_CALIB("min_average_noise = %d, antenna %d\n", min_average_noise, - min_average_noise_antenna_i); - - il4965_gain_computation(il, average_noise, min_average_noise_antenna_i, - min_average_noise, - il4965_find_first_chain(il->cfg->valid_rx_ant)); - - /* Some power changes may have been made during the calibration. - * Update and commit the RXON - */ - if (il->ops->update_chain_flags) - il->ops->update_chain_flags(il); - - data->state = IL_CHAIN_NOISE_DONE; - il_power_update_mode(il, false); -} - -void -il4965_reset_run_time_calib(struct il_priv *il) -{ - int i; - memset(&(il->sensitivity_data), 0, sizeof(struct il_sensitivity_data)); - memset(&(il->chain_noise_data), 0, sizeof(struct il_chain_noise_data)); - for (i = 0; i < NUM_RX_CHAINS; i++) - il->chain_noise_data.delta_gain_code[i] = - CHAIN_NOISE_DELTA_GAIN_INIT_VAL; - - /* Ask for stats now, the uCode will send notification - * periodically after association */ - il_send_stats_request(il, CMD_ASYNC, true); -} diff --git a/drivers/net/wireless/iwlegacy/4965-debug.c b/drivers/net/wireless/iwlegacy/4965-debug.c deleted file mode 100644 index e0597bfdddb8..000000000000 --- a/drivers/net/wireless/iwlegacy/4965-debug.c +++ /dev/null @@ -1,752 +0,0 @@ -/****************************************************************************** -* -* GPL LICENSE SUMMARY -* -* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of version 2 of the GNU General Public License as -* published by the Free Software Foundation. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, -* USA -* -* The full GNU General Public License is included in this distribution -* in the file called LICENSE.GPL. -* -* Contact Information: -* Intel Linux Wireless -* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 -*****************************************************************************/ -#include "common.h" -#include "4965.h" - -static const char *fmt_value = " %-30s %10u\n"; -static const char *fmt_table = " %-30s %10u %10u %10u %10u\n"; -static const char *fmt_header = - "%-32s current cumulative delta max\n"; - -static int -il4965_stats_flag(struct il_priv *il, char *buf, int bufsz) -{ - int p = 0; - u32 flag; - - flag = le32_to_cpu(il->_4965.stats.flag); - - p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", flag); - if (flag & UCODE_STATS_CLEAR_MSK) - p += scnprintf(buf + p, bufsz - p, - "\tStatistics have been cleared\n"); - p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", - (flag & UCODE_STATS_FREQUENCY_MSK) ? "2.4 GHz" : - "5.2 GHz"); - p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", - (flag & UCODE_STATS_NARROW_BAND_MSK) ? "enabled" : - "disabled"); - - return p; -} - -static ssize_t -il4965_ucode_rx_stats_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - int pos = 0; - char *buf; - int bufsz = - sizeof(struct stats_rx_phy) * 40 + - sizeof(struct stats_rx_non_phy) * 40 + - sizeof(struct stats_rx_ht_phy) * 40 + 400; - ssize_t ret; - struct stats_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; - struct stats_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; - struct stats_rx_non_phy *general, *accum_general; - struct stats_rx_non_phy *delta_general, *max_general; - struct stats_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht; - - if (!il_is_alive(il)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - IL_ERR("Can not allocate Buffer\n"); - return -ENOMEM; - } - - /* - * the statistic information display here is based on - * the last stats notification from uCode - * might not reflect the current uCode activity - */ - ofdm = &il->_4965.stats.rx.ofdm; - cck = &il->_4965.stats.rx.cck; - general = &il->_4965.stats.rx.general; - ht = &il->_4965.stats.rx.ofdm_ht; - accum_ofdm = &il->_4965.accum_stats.rx.ofdm; - accum_cck = &il->_4965.accum_stats.rx.cck; - accum_general = &il->_4965.accum_stats.rx.general; - accum_ht = &il->_4965.accum_stats.rx.ofdm_ht; - delta_ofdm = &il->_4965.delta_stats.rx.ofdm; - delta_cck = &il->_4965.delta_stats.rx.cck; - delta_general = &il->_4965.delta_stats.rx.general; - delta_ht = &il->_4965.delta_stats.rx.ofdm_ht; - max_ofdm = &il->_4965.max_delta.rx.ofdm; - max_cck = &il->_4965.max_delta.rx.cck; - max_general = &il->_4965.max_delta.rx.general; - max_ht = &il->_4965.max_delta.rx.ofdm_ht; - - pos += il4965_stats_flag(il, buf, bufsz); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - OFDM:"); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "ina_cnt:", - le32_to_cpu(ofdm->ina_cnt), accum_ofdm->ina_cnt, - delta_ofdm->ina_cnt, max_ofdm->ina_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_cnt:", - le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, - delta_ofdm->fina_cnt, max_ofdm->fina_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", - le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, - delta_ofdm->plcp_err, max_ofdm->plcp_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", - le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, - delta_ofdm->crc32_err, max_ofdm->crc32_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", - le32_to_cpu(ofdm->overrun_err), accum_ofdm->overrun_err, - delta_ofdm->overrun_err, max_ofdm->overrun_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", - le32_to_cpu(ofdm->early_overrun_err), - accum_ofdm->early_overrun_err, - delta_ofdm->early_overrun_err, - max_ofdm->early_overrun_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", - le32_to_cpu(ofdm->crc32_good), accum_ofdm->crc32_good, - delta_ofdm->crc32_good, max_ofdm->crc32_good); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "false_alarm_cnt:", - le32_to_cpu(ofdm->false_alarm_cnt), - accum_ofdm->false_alarm_cnt, delta_ofdm->false_alarm_cnt, - max_ofdm->false_alarm_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_sync_err_cnt:", - le32_to_cpu(ofdm->fina_sync_err_cnt), - accum_ofdm->fina_sync_err_cnt, - delta_ofdm->fina_sync_err_cnt, - max_ofdm->fina_sync_err_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "sfd_timeout:", - le32_to_cpu(ofdm->sfd_timeout), accum_ofdm->sfd_timeout, - delta_ofdm->sfd_timeout, max_ofdm->sfd_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_timeout:", - le32_to_cpu(ofdm->fina_timeout), accum_ofdm->fina_timeout, - delta_ofdm->fina_timeout, max_ofdm->fina_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "unresponded_rts:", - le32_to_cpu(ofdm->unresponded_rts), - accum_ofdm->unresponded_rts, delta_ofdm->unresponded_rts, - max_ofdm->unresponded_rts); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "rxe_frame_lmt_ovrun:", - le32_to_cpu(ofdm->rxe_frame_limit_overrun), - accum_ofdm->rxe_frame_limit_overrun, - delta_ofdm->rxe_frame_limit_overrun, - max_ofdm->rxe_frame_limit_overrun); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ack_cnt:", - le32_to_cpu(ofdm->sent_ack_cnt), accum_ofdm->sent_ack_cnt, - delta_ofdm->sent_ack_cnt, max_ofdm->sent_ack_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_cts_cnt:", - le32_to_cpu(ofdm->sent_cts_cnt), accum_ofdm->sent_cts_cnt, - delta_ofdm->sent_cts_cnt, max_ofdm->sent_cts_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ba_rsp_cnt:", - le32_to_cpu(ofdm->sent_ba_rsp_cnt), - accum_ofdm->sent_ba_rsp_cnt, delta_ofdm->sent_ba_rsp_cnt, - max_ofdm->sent_ba_rsp_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_self_kill:", - le32_to_cpu(ofdm->dsp_self_kill), - accum_ofdm->dsp_self_kill, delta_ofdm->dsp_self_kill, - max_ofdm->dsp_self_kill); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", - le32_to_cpu(ofdm->mh_format_err), - accum_ofdm->mh_format_err, delta_ofdm->mh_format_err, - max_ofdm->mh_format_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "re_acq_main_rssi_sum:", - le32_to_cpu(ofdm->re_acq_main_rssi_sum), - accum_ofdm->re_acq_main_rssi_sum, - delta_ofdm->re_acq_main_rssi_sum, - max_ofdm->re_acq_main_rssi_sum); - - pos += - scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - CCK:"); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "ina_cnt:", - le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, - delta_cck->ina_cnt, max_cck->ina_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_cnt:", - le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, - delta_cck->fina_cnt, max_cck->fina_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", - le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, - delta_cck->plcp_err, max_cck->plcp_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", - le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, - delta_cck->crc32_err, max_cck->crc32_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", - le32_to_cpu(cck->overrun_err), accum_cck->overrun_err, - delta_cck->overrun_err, max_cck->overrun_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", - le32_to_cpu(cck->early_overrun_err), - accum_cck->early_overrun_err, - delta_cck->early_overrun_err, max_cck->early_overrun_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", - le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, - delta_cck->crc32_good, max_cck->crc32_good); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "false_alarm_cnt:", - le32_to_cpu(cck->false_alarm_cnt), - accum_cck->false_alarm_cnt, delta_cck->false_alarm_cnt, - max_cck->false_alarm_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_sync_err_cnt:", - le32_to_cpu(cck->fina_sync_err_cnt), - accum_cck->fina_sync_err_cnt, - delta_cck->fina_sync_err_cnt, max_cck->fina_sync_err_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "sfd_timeout:", - le32_to_cpu(cck->sfd_timeout), accum_cck->sfd_timeout, - delta_cck->sfd_timeout, max_cck->sfd_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "fina_timeout:", - le32_to_cpu(cck->fina_timeout), accum_cck->fina_timeout, - delta_cck->fina_timeout, max_cck->fina_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "unresponded_rts:", - le32_to_cpu(cck->unresponded_rts), - accum_cck->unresponded_rts, delta_cck->unresponded_rts, - max_cck->unresponded_rts); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "rxe_frame_lmt_ovrun:", - le32_to_cpu(cck->rxe_frame_limit_overrun), - accum_cck->rxe_frame_limit_overrun, - delta_cck->rxe_frame_limit_overrun, - max_cck->rxe_frame_limit_overrun); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ack_cnt:", - le32_to_cpu(cck->sent_ack_cnt), accum_cck->sent_ack_cnt, - delta_cck->sent_ack_cnt, max_cck->sent_ack_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_cts_cnt:", - le32_to_cpu(cck->sent_cts_cnt), accum_cck->sent_cts_cnt, - delta_cck->sent_cts_cnt, max_cck->sent_cts_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "sent_ba_rsp_cnt:", - le32_to_cpu(cck->sent_ba_rsp_cnt), - accum_cck->sent_ba_rsp_cnt, delta_cck->sent_ba_rsp_cnt, - max_cck->sent_ba_rsp_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_self_kill:", - le32_to_cpu(cck->dsp_self_kill), accum_cck->dsp_self_kill, - delta_cck->dsp_self_kill, max_cck->dsp_self_kill); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", - le32_to_cpu(cck->mh_format_err), accum_cck->mh_format_err, - delta_cck->mh_format_err, max_cck->mh_format_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "re_acq_main_rssi_sum:", - le32_to_cpu(cck->re_acq_main_rssi_sum), - accum_cck->re_acq_main_rssi_sum, - delta_cck->re_acq_main_rssi_sum, - max_cck->re_acq_main_rssi_sum); - - pos += - scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - GENERAL:"); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "bogus_cts:", - le32_to_cpu(general->bogus_cts), accum_general->bogus_cts, - delta_general->bogus_cts, max_general->bogus_cts); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "bogus_ack:", - le32_to_cpu(general->bogus_ack), accum_general->bogus_ack, - delta_general->bogus_ack, max_general->bogus_ack); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "non_bssid_frames:", - le32_to_cpu(general->non_bssid_frames), - accum_general->non_bssid_frames, - delta_general->non_bssid_frames, - max_general->non_bssid_frames); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "filtered_frames:", - le32_to_cpu(general->filtered_frames), - accum_general->filtered_frames, - delta_general->filtered_frames, - max_general->filtered_frames); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "non_channel_beacons:", - le32_to_cpu(general->non_channel_beacons), - accum_general->non_channel_beacons, - delta_general->non_channel_beacons, - max_general->non_channel_beacons); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "channel_beacons:", - le32_to_cpu(general->channel_beacons), - accum_general->channel_beacons, - delta_general->channel_beacons, - max_general->channel_beacons); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "num_missed_bcon:", - le32_to_cpu(general->num_missed_bcon), - accum_general->num_missed_bcon, - delta_general->num_missed_bcon, - max_general->num_missed_bcon); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "adc_rx_saturation_time:", - le32_to_cpu(general->adc_rx_saturation_time), - accum_general->adc_rx_saturation_time, - delta_general->adc_rx_saturation_time, - max_general->adc_rx_saturation_time); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "ina_detect_search_tm:", - le32_to_cpu(general->ina_detection_search_time), - accum_general->ina_detection_search_time, - delta_general->ina_detection_search_time, - max_general->ina_detection_search_time); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "beacon_silence_rssi_a:", - le32_to_cpu(general->beacon_silence_rssi_a), - accum_general->beacon_silence_rssi_a, - delta_general->beacon_silence_rssi_a, - max_general->beacon_silence_rssi_a); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "beacon_silence_rssi_b:", - le32_to_cpu(general->beacon_silence_rssi_b), - accum_general->beacon_silence_rssi_b, - delta_general->beacon_silence_rssi_b, - max_general->beacon_silence_rssi_b); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "beacon_silence_rssi_c:", - le32_to_cpu(general->beacon_silence_rssi_c), - accum_general->beacon_silence_rssi_c, - delta_general->beacon_silence_rssi_c, - max_general->beacon_silence_rssi_c); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "interference_data_flag:", - le32_to_cpu(general->interference_data_flag), - accum_general->interference_data_flag, - delta_general->interference_data_flag, - max_general->interference_data_flag); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "channel_load:", - le32_to_cpu(general->channel_load), - accum_general->channel_load, delta_general->channel_load, - max_general->channel_load); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "dsp_false_alarms:", - le32_to_cpu(general->dsp_false_alarms), - accum_general->dsp_false_alarms, - delta_general->dsp_false_alarms, - max_general->dsp_false_alarms); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_a:", - le32_to_cpu(general->beacon_rssi_a), - accum_general->beacon_rssi_a, - delta_general->beacon_rssi_a, max_general->beacon_rssi_a); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_b:", - le32_to_cpu(general->beacon_rssi_b), - accum_general->beacon_rssi_b, - delta_general->beacon_rssi_b, max_general->beacon_rssi_b); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_rssi_c:", - le32_to_cpu(general->beacon_rssi_c), - accum_general->beacon_rssi_c, - delta_general->beacon_rssi_c, max_general->beacon_rssi_c); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_a:", - le32_to_cpu(general->beacon_energy_a), - accum_general->beacon_energy_a, - delta_general->beacon_energy_a, - max_general->beacon_energy_a); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_b:", - le32_to_cpu(general->beacon_energy_b), - accum_general->beacon_energy_b, - delta_general->beacon_energy_b, - max_general->beacon_energy_b); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "beacon_energy_c:", - le32_to_cpu(general->beacon_energy_c), - accum_general->beacon_energy_c, - delta_general->beacon_energy_c, - max_general->beacon_energy_c); - - pos += - scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - OFDM_HT:"); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "plcp_err:", - le32_to_cpu(ht->plcp_err), accum_ht->plcp_err, - delta_ht->plcp_err, max_ht->plcp_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "overrun_err:", - le32_to_cpu(ht->overrun_err), accum_ht->overrun_err, - delta_ht->overrun_err, max_ht->overrun_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "early_overrun_err:", - le32_to_cpu(ht->early_overrun_err), - accum_ht->early_overrun_err, delta_ht->early_overrun_err, - max_ht->early_overrun_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_good:", - le32_to_cpu(ht->crc32_good), accum_ht->crc32_good, - delta_ht->crc32_good, max_ht->crc32_good); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "crc32_err:", - le32_to_cpu(ht->crc32_err), accum_ht->crc32_err, - delta_ht->crc32_err, max_ht->crc32_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "mh_format_err:", - le32_to_cpu(ht->mh_format_err), accum_ht->mh_format_err, - delta_ht->mh_format_err, max_ht->mh_format_err); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_crc32_good:", - le32_to_cpu(ht->agg_crc32_good), accum_ht->agg_crc32_good, - delta_ht->agg_crc32_good, max_ht->agg_crc32_good); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_mpdu_cnt:", - le32_to_cpu(ht->agg_mpdu_cnt), accum_ht->agg_mpdu_cnt, - delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "agg_cnt:", - le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt, - delta_ht->agg_cnt, max_ht->agg_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "unsupport_mcs:", - le32_to_cpu(ht->unsupport_mcs), accum_ht->unsupport_mcs, - delta_ht->unsupport_mcs, max_ht->unsupport_mcs); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il4965_ucode_tx_stats_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - int pos = 0; - char *buf; - int bufsz = (sizeof(struct stats_tx) * 48) + 250; - ssize_t ret; - struct stats_tx *tx, *accum_tx, *delta_tx, *max_tx; - - if (!il_is_alive(il)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - IL_ERR("Can not allocate Buffer\n"); - return -ENOMEM; - } - - /* the statistic information display here is based on - * the last stats notification from uCode - * might not reflect the current uCode activity - */ - tx = &il->_4965.stats.tx; - accum_tx = &il->_4965.accum_stats.tx; - delta_tx = &il->_4965.delta_stats.tx; - max_tx = &il->_4965.max_delta.tx; - - pos += il4965_stats_flag(il, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Tx:"); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "preamble:", - le32_to_cpu(tx->preamble_cnt), accum_tx->preamble_cnt, - delta_tx->preamble_cnt, max_tx->preamble_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "rx_detected_cnt:", - le32_to_cpu(tx->rx_detected_cnt), - accum_tx->rx_detected_cnt, delta_tx->rx_detected_cnt, - max_tx->rx_detected_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "bt_prio_defer_cnt:", - le32_to_cpu(tx->bt_prio_defer_cnt), - accum_tx->bt_prio_defer_cnt, delta_tx->bt_prio_defer_cnt, - max_tx->bt_prio_defer_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "bt_prio_kill_cnt:", - le32_to_cpu(tx->bt_prio_kill_cnt), - accum_tx->bt_prio_kill_cnt, delta_tx->bt_prio_kill_cnt, - max_tx->bt_prio_kill_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "few_bytes_cnt:", - le32_to_cpu(tx->few_bytes_cnt), accum_tx->few_bytes_cnt, - delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "cts_timeout:", - le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, - delta_tx->cts_timeout, max_tx->cts_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "ack_timeout:", - le32_to_cpu(tx->ack_timeout), accum_tx->ack_timeout, - delta_tx->ack_timeout, max_tx->ack_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "expected_ack_cnt:", - le32_to_cpu(tx->expected_ack_cnt), - accum_tx->expected_ack_cnt, delta_tx->expected_ack_cnt, - max_tx->expected_ack_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "actual_ack_cnt:", - le32_to_cpu(tx->actual_ack_cnt), accum_tx->actual_ack_cnt, - delta_tx->actual_ack_cnt, max_tx->actual_ack_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "dump_msdu_cnt:", - le32_to_cpu(tx->dump_msdu_cnt), accum_tx->dump_msdu_cnt, - delta_tx->dump_msdu_cnt, max_tx->dump_msdu_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "abort_nxt_frame_mismatch:", - le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt), - accum_tx->burst_abort_next_frame_mismatch_cnt, - delta_tx->burst_abort_next_frame_mismatch_cnt, - max_tx->burst_abort_next_frame_mismatch_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "abort_missing_nxt_frame:", - le32_to_cpu(tx->burst_abort_missing_next_frame_cnt), - accum_tx->burst_abort_missing_next_frame_cnt, - delta_tx->burst_abort_missing_next_frame_cnt, - max_tx->burst_abort_missing_next_frame_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "cts_timeout_collision:", - le32_to_cpu(tx->cts_timeout_collision), - accum_tx->cts_timeout_collision, - delta_tx->cts_timeout_collision, - max_tx->cts_timeout_collision); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "ack_ba_timeout_collision:", - le32_to_cpu(tx->ack_or_ba_timeout_collision), - accum_tx->ack_or_ba_timeout_collision, - delta_tx->ack_or_ba_timeout_collision, - max_tx->ack_or_ba_timeout_collision); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "agg ba_timeout:", - le32_to_cpu(tx->agg.ba_timeout), accum_tx->agg.ba_timeout, - delta_tx->agg.ba_timeout, max_tx->agg.ba_timeout); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "agg ba_resched_frames:", - le32_to_cpu(tx->agg.ba_reschedule_frames), - accum_tx->agg.ba_reschedule_frames, - delta_tx->agg.ba_reschedule_frames, - max_tx->agg.ba_reschedule_frames); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "agg scd_query_agg_frame:", - le32_to_cpu(tx->agg.scd_query_agg_frame_cnt), - accum_tx->agg.scd_query_agg_frame_cnt, - delta_tx->agg.scd_query_agg_frame_cnt, - max_tx->agg.scd_query_agg_frame_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "agg scd_query_no_agg:", - le32_to_cpu(tx->agg.scd_query_no_agg), - accum_tx->agg.scd_query_no_agg, - delta_tx->agg.scd_query_no_agg, - max_tx->agg.scd_query_no_agg); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "agg scd_query_agg:", - le32_to_cpu(tx->agg.scd_query_agg), - accum_tx->agg.scd_query_agg, delta_tx->agg.scd_query_agg, - max_tx->agg.scd_query_agg); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "agg scd_query_mismatch:", - le32_to_cpu(tx->agg.scd_query_mismatch), - accum_tx->agg.scd_query_mismatch, - delta_tx->agg.scd_query_mismatch, - max_tx->agg.scd_query_mismatch); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "agg frame_not_ready:", - le32_to_cpu(tx->agg.frame_not_ready), - accum_tx->agg.frame_not_ready, - delta_tx->agg.frame_not_ready, - max_tx->agg.frame_not_ready); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "agg underrun:", - le32_to_cpu(tx->agg.underrun), accum_tx->agg.underrun, - delta_tx->agg.underrun, max_tx->agg.underrun); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "agg bt_prio_kill:", - le32_to_cpu(tx->agg.bt_prio_kill), - accum_tx->agg.bt_prio_kill, delta_tx->agg.bt_prio_kill, - max_tx->agg.bt_prio_kill); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "agg rx_ba_rsp_cnt:", - le32_to_cpu(tx->agg.rx_ba_rsp_cnt), - accum_tx->agg.rx_ba_rsp_cnt, delta_tx->agg.rx_ba_rsp_cnt, - max_tx->agg.rx_ba_rsp_cnt); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il4965_ucode_general_stats_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - int pos = 0; - char *buf; - int bufsz = sizeof(struct stats_general) * 10 + 300; - ssize_t ret; - struct stats_general_common *general, *accum_general; - struct stats_general_common *delta_general, *max_general; - struct stats_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; - struct stats_div *div, *accum_div, *delta_div, *max_div; - - if (!il_is_alive(il)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - IL_ERR("Can not allocate Buffer\n"); - return -ENOMEM; - } - - /* the statistic information display here is based on - * the last stats notification from uCode - * might not reflect the current uCode activity - */ - general = &il->_4965.stats.general.common; - dbg = &il->_4965.stats.general.common.dbg; - div = &il->_4965.stats.general.common.div; - accum_general = &il->_4965.accum_stats.general.common; - accum_dbg = &il->_4965.accum_stats.general.common.dbg; - accum_div = &il->_4965.accum_stats.general.common.div; - delta_general = &il->_4965.delta_stats.general.common; - max_general = &il->_4965.max_delta.general.common; - delta_dbg = &il->_4965.delta_stats.general.common.dbg; - max_dbg = &il->_4965.max_delta.general.common.dbg; - delta_div = &il->_4965.delta_stats.general.common.div; - max_div = &il->_4965.max_delta.general.common.div; - - pos += il4965_stats_flag(il, buf, bufsz); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_General:"); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_value, "temperature:", - le32_to_cpu(general->temperature)); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_value, "ttl_timestamp:", - le32_to_cpu(general->ttl_timestamp)); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "burst_check:", - le32_to_cpu(dbg->burst_check), accum_dbg->burst_check, - delta_dbg->burst_check, max_dbg->burst_check); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "burst_count:", - le32_to_cpu(dbg->burst_count), accum_dbg->burst_count, - delta_dbg->burst_count, max_dbg->burst_count); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, - "wait_for_silence_timeout_count:", - le32_to_cpu(dbg->wait_for_silence_timeout_cnt), - accum_dbg->wait_for_silence_timeout_cnt, - delta_dbg->wait_for_silence_timeout_cnt, - max_dbg->wait_for_silence_timeout_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "sleep_time:", - le32_to_cpu(general->sleep_time), - accum_general->sleep_time, delta_general->sleep_time, - max_general->sleep_time); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "slots_out:", - le32_to_cpu(general->slots_out), accum_general->slots_out, - delta_general->slots_out, max_general->slots_out); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "slots_idle:", - le32_to_cpu(general->slots_idle), - accum_general->slots_idle, delta_general->slots_idle, - max_general->slots_idle); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "tx_on_a:", - le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, - delta_div->tx_on_a, max_div->tx_on_a); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "tx_on_b:", - le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, - delta_div->tx_on_b, max_div->tx_on_b); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "exec_time:", - le32_to_cpu(div->exec_time), accum_div->exec_time, - delta_div->exec_time, max_div->exec_time); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "probe_time:", - le32_to_cpu(div->probe_time), accum_div->probe_time, - delta_div->probe_time, max_div->probe_time); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "rx_enable_counter:", - le32_to_cpu(general->rx_enable_counter), - accum_general->rx_enable_counter, - delta_general->rx_enable_counter, - max_general->rx_enable_counter); - pos += - scnprintf(buf + pos, bufsz - pos, fmt_table, "num_of_sos_states:", - le32_to_cpu(general->num_of_sos_states), - accum_general->num_of_sos_states, - delta_general->num_of_sos_states, - max_general->num_of_sos_states); - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -const struct il_debugfs_ops il4965_debugfs_ops = { - .rx_stats_read = il4965_ucode_rx_stats_read, - .tx_stats_read = il4965_ucode_tx_stats_read, - .general_stats_read = il4965_ucode_general_stats_read, -}; diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c deleted file mode 100644 index 6656215a13a9..000000000000 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ /dev/null @@ -1,6868 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#define DRV_NAME "iwl4965" - -#include "common.h" -#include "4965.h" - -/****************************************************************************** - * - * module boiler plate - * - ******************************************************************************/ - -/* - * module name, copyright, version, etc. - */ -#define DRV_DESCRIPTION "Intel(R) Wireless WiFi 4965 driver for Linux" - -#ifdef CONFIG_IWLEGACY_DEBUG -#define VD "d" -#else -#define VD -#endif - -#define DRV_VERSION IWLWIFI_VERSION VD - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); -MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("iwl4965"); - -void -il4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status) -{ - if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { - IL_ERR("Tx flush command to flush out all frames\n"); - if (!test_bit(S_EXIT_PENDING, &il->status)) - queue_work(il->workqueue, &il->tx_flush); - } -} - -/* - * EEPROM - */ -struct il_mod_params il4965_mod_params = { - .restart_fw = 1, - /* the rest are 0 by default */ -}; - -void -il4965_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq) -{ - unsigned long flags; - int i; - spin_lock_irqsave(&rxq->lock, flags); - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - /* Fill the rx_used queue with _all_ of the Rx buffers */ - for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { - /* In the reset function, these buffers may have been allocated - * to an SKB, so we need to unmap and free potential storage */ - if (rxq->pool[i].page != NULL) { - pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, - PAGE_SIZE << il->hw_params.rx_page_order, - PCI_DMA_FROMDEVICE); - __il_free_pages(il, rxq->pool[i].page); - rxq->pool[i].page = NULL; - } - list_add_tail(&rxq->pool[i].list, &rxq->rx_used); - } - - for (i = 0; i < RX_QUEUE_SIZE; i++) - rxq->queue[i] = NULL; - - /* Set us so that we have processed and used all buffers, but have - * not restocked the Rx queue with fresh buffers */ - rxq->read = rxq->write = 0; - rxq->write_actual = 0; - rxq->free_count = 0; - spin_unlock_irqrestore(&rxq->lock, flags); -} - -int -il4965_rx_init(struct il_priv *il, struct il_rx_queue *rxq) -{ - u32 rb_size; - const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ - u32 rb_timeout = 0; - - if (il->cfg->mod_params->amsdu_size_8K) - rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; - else - rb_size = FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; - - /* Stop Rx DMA */ - il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0); - - /* Reset driver's Rx queue write idx */ - il_wr(il, FH49_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); - - /* Tell device where to find RBD circular buffer in DRAM */ - il_wr(il, FH49_RSCSR_CHNL0_RBDCB_BASE_REG, (u32) (rxq->bd_dma >> 8)); - - /* Tell device where in DRAM to update its Rx status */ - il_wr(il, FH49_RSCSR_CHNL0_STTS_WPTR_REG, rxq->rb_stts_dma >> 4); - - /* Enable Rx DMA - * Direct rx interrupts to hosts - * Rx buffer size 4 or 8k - * RB timeout 0x10 - * 256 RBDs - */ - il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, - FH49_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | - FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | - FH49_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | - rb_size | - (rb_timeout << FH49_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | - (rfdnlog << FH49_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); - - /* Set interrupt coalescing timer to default (2048 usecs) */ - il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_TIMEOUT_DEF); - - return 0; -} - -static void -il4965_set_pwr_vmain(struct il_priv *il) -{ -/* - * (for documentation purposes) - * to set power to V_AUX, do: - - if (pci_pme_capable(il->pci_dev, PCI_D3cold)) - il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VAUX, - ~APMG_PS_CTRL_MSK_PWR_SRC); - */ - - il_set_bits_mask_prph(il, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, - ~APMG_PS_CTRL_MSK_PWR_SRC); -} - -int -il4965_hw_nic_init(struct il_priv *il) -{ - unsigned long flags; - struct il_rx_queue *rxq = &il->rxq; - int ret; - - spin_lock_irqsave(&il->lock, flags); - il_apm_init(il); - /* Set interrupt coalescing calibration timer to default (512 usecs) */ - il_write8(il, CSR_INT_COALESCING, IL_HOST_INT_CALIB_TIMEOUT_DEF); - spin_unlock_irqrestore(&il->lock, flags); - - il4965_set_pwr_vmain(il); - il4965_nic_config(il); - - /* Allocate the RX queue, or reset if it is already allocated */ - if (!rxq->bd) { - ret = il_rx_queue_alloc(il); - if (ret) { - IL_ERR("Unable to initialize Rx queue\n"); - return -ENOMEM; - } - } else - il4965_rx_queue_reset(il, rxq); - - il4965_rx_replenish(il); - - il4965_rx_init(il, rxq); - - spin_lock_irqsave(&il->lock, flags); - - rxq->need_update = 1; - il_rx_queue_update_write_ptr(il, rxq); - - spin_unlock_irqrestore(&il->lock, flags); - - /* Allocate or reset and init all Tx and Command queues */ - if (!il->txq) { - ret = il4965_txq_ctx_alloc(il); - if (ret) - return ret; - } else - il4965_txq_ctx_reset(il); - - set_bit(S_INIT, &il->status); - - return 0; -} - -/** - * il4965_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr - */ -static inline __le32 -il4965_dma_addr2rbd_ptr(struct il_priv *il, dma_addr_t dma_addr) -{ - return cpu_to_le32((u32) (dma_addr >> 8)); -} - -/** - * il4965_rx_queue_restock - refill RX queue from pre-allocated pool - * - * If there are slots in the RX queue that need to be restocked, - * and we have free pre-allocated buffers, fill the ranks as much - * as we can, pulling from rx_free. - * - * This moves the 'write' idx forward to catch up with 'processed', and - * also updates the memory address in the firmware to reference the new - * target buffer. - */ -void -il4965_rx_queue_restock(struct il_priv *il) -{ - struct il_rx_queue *rxq = &il->rxq; - struct list_head *element; - struct il_rx_buf *rxb; - unsigned long flags; - - spin_lock_irqsave(&rxq->lock, flags); - while (il_rx_queue_space(rxq) > 0 && rxq->free_count) { - /* The overwritten rxb must be a used one */ - rxb = rxq->queue[rxq->write]; - BUG_ON(rxb && rxb->page); - - /* Get next free Rx buffer, remove from free list */ - element = rxq->rx_free.next; - rxb = list_entry(element, struct il_rx_buf, list); - list_del(element); - - /* Point to Rx buffer via next RBD in circular buffer */ - rxq->bd[rxq->write] = - il4965_dma_addr2rbd_ptr(il, rxb->page_dma); - rxq->queue[rxq->write] = rxb; - rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; - rxq->free_count--; - } - spin_unlock_irqrestore(&rxq->lock, flags); - /* If the pre-allocated buffer pool is dropping low, schedule to - * refill it */ - if (rxq->free_count <= RX_LOW_WATERMARK) - queue_work(il->workqueue, &il->rx_replenish); - - /* If we've added more space for the firmware to place data, tell it. - * Increment device's write pointer in multiples of 8. */ - if (rxq->write_actual != (rxq->write & ~0x7)) { - spin_lock_irqsave(&rxq->lock, flags); - rxq->need_update = 1; - spin_unlock_irqrestore(&rxq->lock, flags); - il_rx_queue_update_write_ptr(il, rxq); - } -} - -/** - * il4965_rx_replenish - Move all used packet from rx_used to rx_free - * - * When moving to rx_free an SKB is allocated for the slot. - * - * Also restock the Rx queue via il_rx_queue_restock. - * This is called as a scheduled work item (except for during initialization) - */ -static void -il4965_rx_allocate(struct il_priv *il, gfp_t priority) -{ - struct il_rx_queue *rxq = &il->rxq; - struct list_head *element; - struct il_rx_buf *rxb; - struct page *page; - dma_addr_t page_dma; - unsigned long flags; - gfp_t gfp_mask = priority; - - while (1) { - spin_lock_irqsave(&rxq->lock, flags); - if (list_empty(&rxq->rx_used)) { - spin_unlock_irqrestore(&rxq->lock, flags); - return; - } - spin_unlock_irqrestore(&rxq->lock, flags); - - if (rxq->free_count > RX_LOW_WATERMARK) - gfp_mask |= __GFP_NOWARN; - - if (il->hw_params.rx_page_order > 0) - gfp_mask |= __GFP_COMP; - - /* Alloc a new receive buffer */ - page = alloc_pages(gfp_mask, il->hw_params.rx_page_order); - if (!page) { - if (net_ratelimit()) - D_INFO("alloc_pages failed, " "order: %d\n", - il->hw_params.rx_page_order); - - if (rxq->free_count <= RX_LOW_WATERMARK && - net_ratelimit()) - IL_ERR("Failed to alloc_pages with %s. " - "Only %u free buffers remaining.\n", - priority == - GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL", - rxq->free_count); - /* We don't reschedule replenish work here -- we will - * call the restock method and if it still needs - * more buffers it will schedule replenish */ - return; - } - - /* Get physical address of the RB */ - page_dma = - pci_map_page(il->pci_dev, page, 0, - PAGE_SIZE << il->hw_params.rx_page_order, - PCI_DMA_FROMDEVICE); - if (unlikely(pci_dma_mapping_error(il->pci_dev, page_dma))) { - __free_pages(page, il->hw_params.rx_page_order); - break; - } - - spin_lock_irqsave(&rxq->lock, flags); - - if (list_empty(&rxq->rx_used)) { - spin_unlock_irqrestore(&rxq->lock, flags); - pci_unmap_page(il->pci_dev, page_dma, - PAGE_SIZE << il->hw_params.rx_page_order, - PCI_DMA_FROMDEVICE); - __free_pages(page, il->hw_params.rx_page_order); - return; - } - - element = rxq->rx_used.next; - rxb = list_entry(element, struct il_rx_buf, list); - list_del(element); - - BUG_ON(rxb->page); - - rxb->page = page; - rxb->page_dma = page_dma; - list_add_tail(&rxb->list, &rxq->rx_free); - rxq->free_count++; - il->alloc_rxb_page++; - - spin_unlock_irqrestore(&rxq->lock, flags); - } -} - -void -il4965_rx_replenish(struct il_priv *il) -{ - unsigned long flags; - - il4965_rx_allocate(il, GFP_KERNEL); - - spin_lock_irqsave(&il->lock, flags); - il4965_rx_queue_restock(il); - spin_unlock_irqrestore(&il->lock, flags); -} - -void -il4965_rx_replenish_now(struct il_priv *il) -{ - il4965_rx_allocate(il, GFP_ATOMIC); - - il4965_rx_queue_restock(il); -} - -/* Assumes that the skb field of the buffers in 'pool' is kept accurate. - * If an SKB has been detached, the POOL needs to have its SKB set to NULL - * This free routine walks the list of POOL entries and if SKB is set to - * non NULL it is unmapped and freed - */ -void -il4965_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq) -{ - int i; - for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { - if (rxq->pool[i].page != NULL) { - pci_unmap_page(il->pci_dev, rxq->pool[i].page_dma, - PAGE_SIZE << il->hw_params.rx_page_order, - PCI_DMA_FROMDEVICE); - __il_free_pages(il, rxq->pool[i].page); - rxq->pool[i].page = NULL; - } - } - - dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, - rxq->bd_dma); - dma_free_coherent(&il->pci_dev->dev, sizeof(struct il_rb_status), - rxq->rb_stts, rxq->rb_stts_dma); - rxq->bd = NULL; - rxq->rb_stts = NULL; -} - -int -il4965_rxq_stop(struct il_priv *il) -{ - int ret; - - _il_wr(il, FH49_MEM_RCSR_CHNL0_CONFIG_REG, 0); - ret = _il_poll_bit(il, FH49_MEM_RSSR_RX_STATUS_REG, - FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, - FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, - 1000); - if (ret < 0) - IL_ERR("Can't stop Rx DMA.\n"); - - return 0; -} - -int -il4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band) -{ - int idx = 0; - int band_offset = 0; - - /* HT rate format: mac80211 wants an MCS number, which is just LSB */ - if (rate_n_flags & RATE_MCS_HT_MSK) { - idx = (rate_n_flags & 0xff); - return idx; - /* Legacy rate format, search for match in table */ - } else { - if (band == IEEE80211_BAND_5GHZ) - band_offset = IL_FIRST_OFDM_RATE; - for (idx = band_offset; idx < RATE_COUNT_LEGACY; idx++) - if (il_rates[idx].plcp == (rate_n_flags & 0xFF)) - return idx - band_offset; - } - - return -1; -} - -static int -il4965_calc_rssi(struct il_priv *il, struct il_rx_phy_res *rx_resp) -{ - /* data from PHY/DSP regarding signal strength, etc., - * contents are always there, not configurable by host. */ - struct il4965_rx_non_cfg_phy *ncphy = - (struct il4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy_buf; - u32 agc = - (le16_to_cpu(ncphy->agc_info) & IL49_AGC_DB_MASK) >> - IL49_AGC_DB_POS; - - u32 valid_antennae = - (le16_to_cpu(rx_resp->phy_flags) & IL49_RX_PHY_FLAGS_ANTENNAE_MASK) - >> IL49_RX_PHY_FLAGS_ANTENNAE_OFFSET; - u8 max_rssi = 0; - u32 i; - - /* Find max rssi among 3 possible receivers. - * These values are measured by the digital signal processor (DSP). - * They should stay fairly constant even as the signal strength varies, - * if the radio's automatic gain control (AGC) is working right. - * AGC value (see below) will provide the "interesting" info. */ - for (i = 0; i < 3; i++) - if (valid_antennae & (1 << i)) - max_rssi = max(ncphy->rssi_info[i << 1], max_rssi); - - D_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n", - ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4], - max_rssi, agc); - - /* dBm = max_rssi dB - agc dB - constant. - * Higher AGC (higher radio gain) means lower signal. */ - return max_rssi - agc - IL4965_RSSI_OFFSET; -} - -static u32 -il4965_translate_rx_status(struct il_priv *il, u32 decrypt_in) -{ - u32 decrypt_out = 0; - - if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) == - RX_RES_STATUS_STATION_FOUND) - decrypt_out |= - (RX_RES_STATUS_STATION_FOUND | - RX_RES_STATUS_NO_STATION_INFO_MISMATCH); - - decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK); - - /* packet was not encrypted */ - if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == - RX_RES_STATUS_SEC_TYPE_NONE) - return decrypt_out; - - /* packet was encrypted with unknown alg */ - if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == - RX_RES_STATUS_SEC_TYPE_ERR) - return decrypt_out; - - /* decryption was not done in HW */ - if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) != - RX_MPDU_RES_STATUS_DEC_DONE_MSK) - return decrypt_out; - - switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) { - - case RX_RES_STATUS_SEC_TYPE_CCMP: - /* alg is CCM: check MIC only */ - if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK)) - /* Bad MIC */ - decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; - else - decrypt_out |= RX_RES_STATUS_DECRYPT_OK; - - break; - - case RX_RES_STATUS_SEC_TYPE_TKIP: - if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) { - /* Bad TTAK */ - decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK; - break; - } - /* fall through if TTAK OK */ - default: - if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK)) - decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; - else - decrypt_out |= RX_RES_STATUS_DECRYPT_OK; - break; - } - - D_RX("decrypt_in:0x%x decrypt_out = 0x%x\n", decrypt_in, decrypt_out); - - return decrypt_out; -} - -#define SMALL_PACKET_SIZE 256 - -static void -il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr, - u32 len, u32 ampdu_status, struct il_rx_buf *rxb, - struct ieee80211_rx_status *stats) -{ - struct sk_buff *skb; - __le16 fc = hdr->frame_control; - - /* We only process data packets if the interface is open */ - if (unlikely(!il->is_open)) { - D_DROP("Dropping packet while interface is not open.\n"); - return; - } - - if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) { - il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); - D_INFO("Woke queues - frame received on passive channel\n"); - } - - /* In case of HW accelerated crypto and bad decryption, drop */ - if (!il->cfg->mod_params->sw_crypto && - il_set_decrypted_flag(il, hdr, ampdu_status, stats)) - return; - - skb = dev_alloc_skb(SMALL_PACKET_SIZE); - if (!skb) { - IL_ERR("dev_alloc_skb failed\n"); - return; - } - - if (len <= SMALL_PACKET_SIZE) { - memcpy(skb_put(skb, len), hdr, len); - } else { - skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), - len, PAGE_SIZE << il->hw_params.rx_page_order); - il->alloc_rxb_page--; - rxb->page = NULL; - } - - il_update_stats(il, false, fc, len); - memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); - - ieee80211_rx(il->hw, skb); -} - -/* Called for N_RX (legacy ABG frames), or - * N_RX_MPDU (HT high-throughput N frames). */ -static void -il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct ieee80211_hdr *header; - struct ieee80211_rx_status rx_status = {}; - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_rx_phy_res *phy_res; - __le32 rx_pkt_status; - struct il_rx_mpdu_res_start *amsdu; - u32 len; - u32 ampdu_status; - u32 rate_n_flags; - - /** - * N_RX and N_RX_MPDU are handled differently. - * N_RX: physical layer info is in this buffer - * N_RX_MPDU: physical layer info was sent in separate - * command and cached in il->last_phy_res - * - * Here we set up local variables depending on which command is - * received. - */ - if (pkt->hdr.cmd == N_RX) { - phy_res = (struct il_rx_phy_res *)pkt->u.raw; - header = - (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*phy_res) + - phy_res->cfg_phy_cnt); - - len = le16_to_cpu(phy_res->byte_count); - rx_pkt_status = - *(__le32 *) (pkt->u.raw + sizeof(*phy_res) + - phy_res->cfg_phy_cnt + len); - ampdu_status = le32_to_cpu(rx_pkt_status); - } else { - if (!il->_4965.last_phy_res_valid) { - IL_ERR("MPDU frame without cached PHY data\n"); - return; - } - phy_res = &il->_4965.last_phy_res; - amsdu = (struct il_rx_mpdu_res_start *)pkt->u.raw; - header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu)); - len = le16_to_cpu(amsdu->byte_count); - rx_pkt_status = *(__le32 *) (pkt->u.raw + sizeof(*amsdu) + len); - ampdu_status = - il4965_translate_rx_status(il, le32_to_cpu(rx_pkt_status)); - } - - if ((unlikely(phy_res->cfg_phy_cnt > 20))) { - D_DROP("dsp size out of range [0,20]: %d\n", - phy_res->cfg_phy_cnt); - return; - } - - if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || - !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { - D_RX("Bad CRC or FIFO: 0x%08X.\n", le32_to_cpu(rx_pkt_status)); - return; - } - - /* This will be used in several places later */ - rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); - - /* rx_status carries information about the packet to mac80211 */ - rx_status.mactime = le64_to_cpu(phy_res->timestamp); - rx_status.band = - (phy_res-> - phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ : - IEEE80211_BAND_5GHZ; - rx_status.freq = - ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel), - rx_status.band); - rx_status.rate_idx = - il4965_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band); - rx_status.flag = 0; - - /* TSF isn't reliable. In order to allow smooth user experience, - * this W/A doesn't propagate it to the mac80211 */ - /*rx_status.flag |= RX_FLAG_MACTIME_START; */ - - il->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); - - /* Find max signal strength (dBm) among 3 antenna/receiver chains */ - rx_status.signal = il4965_calc_rssi(il, phy_res); - - D_STATS("Rssi %d, TSF %llu\n", rx_status.signal, - (unsigned long long)rx_status.mactime); - - /* - * "antenna number" - * - * It seems that the antenna field in the phy flags value - * is actually a bit field. This is undefined by radiotap, - * it wants an actual antenna number but I always get "7" - * for most legacy frames I receive indicating that the - * same frame was received on all three RX chains. - * - * I think this field should be removed in favor of a - * new 802.11n radiotap field "RX chains" that is defined - * as a bitmask. - */ - rx_status.antenna = - (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) >> - RX_RES_PHY_FLAGS_ANTENNA_POS; - - /* set the preamble flag if appropriate */ - if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) - rx_status.flag |= RX_FLAG_SHORTPRE; - - /* Set up the HT phy flags */ - if (rate_n_flags & RATE_MCS_HT_MSK) - rx_status.flag |= RX_FLAG_HT; - if (rate_n_flags & RATE_MCS_HT40_MSK) - rx_status.flag |= RX_FLAG_40MHZ; - if (rate_n_flags & RATE_MCS_SGI_MSK) - rx_status.flag |= RX_FLAG_SHORT_GI; - - if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) { - /* We know which subframes of an A-MPDU belong - * together since we get a single PHY response - * from the firmware for all of them. - */ - - rx_status.flag |= RX_FLAG_AMPDU_DETAILS; - rx_status.ampdu_reference = il->_4965.ampdu_ref; - } - - il4965_pass_packet_to_mac80211(il, header, len, ampdu_status, rxb, - &rx_status); -} - -/* Cache phy data (Rx signal strength, etc) for HT frame (N_RX_PHY). - * This will be used later in il_hdl_rx() for N_RX_MPDU. */ -static void -il4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - il->_4965.last_phy_res_valid = true; - il->_4965.ampdu_ref++; - memcpy(&il->_4965.last_phy_res, pkt->u.raw, - sizeof(struct il_rx_phy_res)); -} - -static int -il4965_get_channels_for_scan(struct il_priv *il, struct ieee80211_vif *vif, - enum ieee80211_band band, u8 is_active, - u8 n_probes, struct il_scan_channel *scan_ch) -{ - struct ieee80211_channel *chan; - const struct ieee80211_supported_band *sband; - const struct il_channel_info *ch_info; - u16 passive_dwell = 0; - u16 active_dwell = 0; - int added, i; - u16 channel; - - sband = il_get_hw_mode(il, band); - if (!sband) - return 0; - - active_dwell = il_get_active_dwell_time(il, band, n_probes); - passive_dwell = il_get_passive_dwell_time(il, band, vif); - - if (passive_dwell <= active_dwell) - passive_dwell = active_dwell + 1; - - for (i = 0, added = 0; i < il->scan_request->n_channels; i++) { - chan = il->scan_request->channels[i]; - - if (chan->band != band) - continue; - - channel = chan->hw_value; - scan_ch->channel = cpu_to_le16(channel); - - ch_info = il_get_channel_info(il, band, channel); - if (!il_is_channel_valid(ch_info)) { - D_SCAN("Channel %d is INVALID for this band.\n", - channel); - continue; - } - - if (!is_active || il_is_channel_passive(ch_info) || - (chan->flags & IEEE80211_CHAN_NO_IR)) - scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; - else - scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; - - if (n_probes) - scan_ch->type |= IL_SCAN_PROBE_MASK(n_probes); - - scan_ch->active_dwell = cpu_to_le16(active_dwell); - scan_ch->passive_dwell = cpu_to_le16(passive_dwell); - - /* Set txpower levels to defaults */ - scan_ch->dsp_atten = 110; - - /* NOTE: if we were doing 6Mb OFDM for scans we'd use - * power level: - * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; - */ - if (band == IEEE80211_BAND_5GHZ) - scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; - else - scan_ch->tx_gain = ((1 << 5) | (5 << 3)); - - D_SCAN("Scanning ch=%d prob=0x%X [%s %d]\n", channel, - le32_to_cpu(scan_ch->type), - (scan_ch-> - type & SCAN_CHANNEL_TYPE_ACTIVE) ? "ACTIVE" : "PASSIVE", - (scan_ch-> - type & SCAN_CHANNEL_TYPE_ACTIVE) ? active_dwell : - passive_dwell); - - scan_ch++; - added++; - } - - D_SCAN("total channels to scan %d\n", added); - return added; -} - -static void -il4965_toggle_tx_ant(struct il_priv *il, u8 *ant, u8 valid) -{ - int i; - u8 ind = *ant; - - for (i = 0; i < RATE_ANT_NUM - 1; i++) { - ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; - if (valid & BIT(ind)) { - *ant = ind; - return; - } - } -} - -int -il4965_request_scan(struct il_priv *il, struct ieee80211_vif *vif) -{ - struct il_host_cmd cmd = { - .id = C_SCAN, - .len = sizeof(struct il_scan_cmd), - .flags = CMD_SIZE_HUGE, - }; - struct il_scan_cmd *scan; - u32 rate_flags = 0; - u16 cmd_len; - u16 rx_chain = 0; - enum ieee80211_band band; - u8 n_probes = 0; - u8 rx_ant = il->hw_params.valid_rx_ant; - u8 rate; - bool is_active = false; - int chan_mod; - u8 active_chains; - u8 scan_tx_antennas = il->hw_params.valid_tx_ant; - int ret; - - lockdep_assert_held(&il->mutex); - - if (!il->scan_cmd) { - il->scan_cmd = - kmalloc(sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE, - GFP_KERNEL); - if (!il->scan_cmd) { - D_SCAN("fail to allocate memory for scan\n"); - return -ENOMEM; - } - } - scan = il->scan_cmd; - memset(scan, 0, sizeof(struct il_scan_cmd) + IL_MAX_SCAN_SIZE); - - scan->quiet_plcp_th = IL_PLCP_QUIET_THRESH; - scan->quiet_time = IL_ACTIVE_QUIET_TIME; - - if (il_is_any_associated(il)) { - u16 interval; - u32 extra; - u32 suspend_time = 100; - u32 scan_suspend_time = 100; - - D_INFO("Scanning while associated...\n"); - interval = vif->bss_conf.beacon_int; - - scan->suspend_time = 0; - scan->max_out_time = cpu_to_le32(200 * 1024); - if (!interval) - interval = suspend_time; - - extra = (suspend_time / interval) << 22; - scan_suspend_time = - (extra | ((suspend_time % interval) * 1024)); - scan->suspend_time = cpu_to_le32(scan_suspend_time); - D_SCAN("suspend_time 0x%X beacon interval %d\n", - scan_suspend_time, interval); - } - - if (il->scan_request->n_ssids) { - int i, p = 0; - D_SCAN("Kicking off active scan\n"); - for (i = 0; i < il->scan_request->n_ssids; i++) { - /* always does wildcard anyway */ - if (!il->scan_request->ssids[i].ssid_len) - continue; - scan->direct_scan[p].id = WLAN_EID_SSID; - scan->direct_scan[p].len = - il->scan_request->ssids[i].ssid_len; - memcpy(scan->direct_scan[p].ssid, - il->scan_request->ssids[i].ssid, - il->scan_request->ssids[i].ssid_len); - n_probes++; - p++; - } - is_active = true; - } else - D_SCAN("Start passive scan.\n"); - - scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; - scan->tx_cmd.sta_id = il->hw_params.bcast_id; - scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - - switch (il->scan_band) { - case IEEE80211_BAND_2GHZ: - scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; - chan_mod = - le32_to_cpu(il->active.flags & RXON_FLG_CHANNEL_MODE_MSK) >> - RXON_FLG_CHANNEL_MODE_POS; - if (chan_mod == CHANNEL_MODE_PURE_40) { - rate = RATE_6M_PLCP; - } else { - rate = RATE_1M_PLCP; - rate_flags = RATE_MCS_CCK_MSK; - } - break; - case IEEE80211_BAND_5GHZ: - rate = RATE_6M_PLCP; - break; - default: - IL_WARN("Invalid scan band\n"); - return -EIO; - } - - /* - * If active scanning is requested but a certain channel is - * marked passive, we can do active scanning if we detect - * transmissions. - * - * There is an issue with some firmware versions that triggers - * a sysassert on a "good CRC threshold" of zero (== disabled), - * on a radar channel even though this means that we should NOT - * send probes. - * - * The "good CRC threshold" is the number of frames that we - * need to receive during our dwell time on a channel before - * sending out probes -- setting this to a huge value will - * mean we never reach it, but at the same time work around - * the aforementioned issue. Thus use IL_GOOD_CRC_TH_NEVER - * here instead of IL_GOOD_CRC_TH_DISABLED. - */ - scan->good_CRC_th = - is_active ? IL_GOOD_CRC_TH_DEFAULT : IL_GOOD_CRC_TH_NEVER; - - band = il->scan_band; - - if (il->cfg->scan_rx_antennas[band]) - rx_ant = il->cfg->scan_rx_antennas[band]; - - il4965_toggle_tx_ant(il, &il->scan_tx_ant[band], scan_tx_antennas); - rate_flags |= BIT(il->scan_tx_ant[band]) << RATE_MCS_ANT_POS; - scan->tx_cmd.rate_n_flags = cpu_to_le32(rate | rate_flags); - - /* In power save mode use one chain, otherwise use all chains */ - if (test_bit(S_POWER_PMI, &il->status)) { - /* rx_ant has been set to all valid chains previously */ - active_chains = - rx_ant & ((u8) (il->chain_noise_data.active_chains)); - if (!active_chains) - active_chains = rx_ant; - - D_SCAN("chain_noise_data.active_chains: %u\n", - il->chain_noise_data.active_chains); - - rx_ant = il4965_first_antenna(active_chains); - } - - /* MIMO is not used here, but value is required */ - rx_chain |= il->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS; - rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; - rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; - rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; - scan->rx_chain = cpu_to_le16(rx_chain); - - cmd_len = - il_fill_probe_req(il, (struct ieee80211_mgmt *)scan->data, - vif->addr, il->scan_request->ie, - il->scan_request->ie_len, - IL_MAX_SCAN_SIZE - sizeof(*scan)); - scan->tx_cmd.len = cpu_to_le16(cmd_len); - - scan->filter_flags |= - (RXON_FILTER_ACCEPT_GRP_MSK | RXON_FILTER_BCON_AWARE_MSK); - - scan->channel_count = - il4965_get_channels_for_scan(il, vif, band, is_active, n_probes, - (void *)&scan->data[cmd_len]); - if (scan->channel_count == 0) { - D_SCAN("channel count %d\n", scan->channel_count); - return -EIO; - } - - cmd.len += - le16_to_cpu(scan->tx_cmd.len) + - scan->channel_count * sizeof(struct il_scan_channel); - cmd.data = scan; - scan->len = cpu_to_le16(cmd.len); - - set_bit(S_SCAN_HW, &il->status); - - ret = il_send_cmd_sync(il, &cmd); - if (ret) - clear_bit(S_SCAN_HW, &il->status); - - return ret; -} - -int -il4965_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif, - bool add) -{ - struct il_vif_priv *vif_priv = (void *)vif->drv_priv; - - if (add) - return il4965_add_bssid_station(il, vif->bss_conf.bssid, - &vif_priv->ibss_bssid_sta_id); - return il_remove_station(il, vif_priv->ibss_bssid_sta_id, - vif->bss_conf.bssid); -} - -void -il4965_free_tfds_in_queue(struct il_priv *il, int sta_id, int tid, int freed) -{ - lockdep_assert_held(&il->sta_lock); - - if (il->stations[sta_id].tid[tid].tfds_in_queue >= freed) - il->stations[sta_id].tid[tid].tfds_in_queue -= freed; - else { - D_TX("free more than tfds_in_queue (%u:%d)\n", - il->stations[sta_id].tid[tid].tfds_in_queue, freed); - il->stations[sta_id].tid[tid].tfds_in_queue = 0; - } -} - -#define IL_TX_QUEUE_MSK 0xfffff - -static bool -il4965_is_single_rx_stream(struct il_priv *il) -{ - return il->current_ht_config.smps == IEEE80211_SMPS_STATIC || - il->current_ht_config.single_chain_sufficient; -} - -#define IL_NUM_RX_CHAINS_MULTIPLE 3 -#define IL_NUM_RX_CHAINS_SINGLE 2 -#define IL_NUM_IDLE_CHAINS_DUAL 2 -#define IL_NUM_IDLE_CHAINS_SINGLE 1 - -/* - * Determine how many receiver/antenna chains to use. - * - * More provides better reception via diversity. Fewer saves power - * at the expense of throughput, but only when not in powersave to - * start with. - * - * MIMO (dual stream) requires at least 2, but works better with 3. - * This does not determine *which* chains to use, just how many. - */ -static int -il4965_get_active_rx_chain_count(struct il_priv *il) -{ - /* # of Rx chains to use when expecting MIMO. */ - if (il4965_is_single_rx_stream(il)) - return IL_NUM_RX_CHAINS_SINGLE; - else - return IL_NUM_RX_CHAINS_MULTIPLE; -} - -/* - * When we are in power saving mode, unless device support spatial - * multiplexing power save, use the active count for rx chain count. - */ -static int -il4965_get_idle_rx_chain_count(struct il_priv *il, int active_cnt) -{ - /* # Rx chains when idling, depending on SMPS mode */ - switch (il->current_ht_config.smps) { - case IEEE80211_SMPS_STATIC: - case IEEE80211_SMPS_DYNAMIC: - return IL_NUM_IDLE_CHAINS_SINGLE; - case IEEE80211_SMPS_OFF: - return active_cnt; - default: - WARN(1, "invalid SMPS mode %d", il->current_ht_config.smps); - return active_cnt; - } -} - -/* up to 4 chains */ -static u8 -il4965_count_chain_bitmap(u32 chain_bitmap) -{ - u8 res; - res = (chain_bitmap & BIT(0)) >> 0; - res += (chain_bitmap & BIT(1)) >> 1; - res += (chain_bitmap & BIT(2)) >> 2; - res += (chain_bitmap & BIT(3)) >> 3; - return res; -} - -/** - * il4965_set_rxon_chain - Set up Rx chain usage in "staging" RXON image - * - * Selects how many and which Rx receivers/antennas/chains to use. - * This should not be used for scan command ... it puts data in wrong place. - */ -void -il4965_set_rxon_chain(struct il_priv *il) -{ - bool is_single = il4965_is_single_rx_stream(il); - bool is_cam = !test_bit(S_POWER_PMI, &il->status); - u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; - u32 active_chains; - u16 rx_chain; - - /* Tell uCode which antennas are actually connected. - * Before first association, we assume all antennas are connected. - * Just after first association, il4965_chain_noise_calibration() - * checks which antennas actually *are* connected. */ - if (il->chain_noise_data.active_chains) - active_chains = il->chain_noise_data.active_chains; - else - active_chains = il->hw_params.valid_rx_ant; - - rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; - - /* How many receivers should we use? */ - active_rx_cnt = il4965_get_active_rx_chain_count(il); - idle_rx_cnt = il4965_get_idle_rx_chain_count(il, active_rx_cnt); - - /* correct rx chain count according hw settings - * and chain noise calibration - */ - valid_rx_cnt = il4965_count_chain_bitmap(active_chains); - if (valid_rx_cnt < active_rx_cnt) - active_rx_cnt = valid_rx_cnt; - - if (valid_rx_cnt < idle_rx_cnt) - idle_rx_cnt = valid_rx_cnt; - - rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; - rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; - - il->staging.rx_chain = cpu_to_le16(rx_chain); - - if (!is_single && active_rx_cnt >= IL_NUM_RX_CHAINS_SINGLE && is_cam) - il->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; - else - il->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; - - D_ASSOC("rx_chain=0x%X active=%d idle=%d\n", il->staging.rx_chain, - active_rx_cnt, idle_rx_cnt); - - WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || - active_rx_cnt < idle_rx_cnt); -} - -static const char * -il4965_get_fh_string(int cmd) -{ - switch (cmd) { - IL_CMD(FH49_RSCSR_CHNL0_STTS_WPTR_REG); - IL_CMD(FH49_RSCSR_CHNL0_RBDCB_BASE_REG); - IL_CMD(FH49_RSCSR_CHNL0_WPTR); - IL_CMD(FH49_MEM_RCSR_CHNL0_CONFIG_REG); - IL_CMD(FH49_MEM_RSSR_SHARED_CTRL_REG); - IL_CMD(FH49_MEM_RSSR_RX_STATUS_REG); - IL_CMD(FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); - IL_CMD(FH49_TSSR_TX_STATUS_REG); - IL_CMD(FH49_TSSR_TX_ERROR_REG); - default: - return "UNKNOWN"; - } -} - -int -il4965_dump_fh(struct il_priv *il, char **buf, bool display) -{ - int i; -#ifdef CONFIG_IWLEGACY_DEBUG - int pos = 0; - size_t bufsz = 0; -#endif - static const u32 fh_tbl[] = { - FH49_RSCSR_CHNL0_STTS_WPTR_REG, - FH49_RSCSR_CHNL0_RBDCB_BASE_REG, - FH49_RSCSR_CHNL0_WPTR, - FH49_MEM_RCSR_CHNL0_CONFIG_REG, - FH49_MEM_RSSR_SHARED_CTRL_REG, - FH49_MEM_RSSR_RX_STATUS_REG, - FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, - FH49_TSSR_TX_STATUS_REG, - FH49_TSSR_TX_ERROR_REG - }; -#ifdef CONFIG_IWLEGACY_DEBUG - if (display) { - bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; - *buf = kmalloc(bufsz, GFP_KERNEL); - if (!*buf) - return -ENOMEM; - pos += - scnprintf(*buf + pos, bufsz - pos, "FH register values:\n"); - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { - pos += - scnprintf(*buf + pos, bufsz - pos, - " %34s: 0X%08x\n", - il4965_get_fh_string(fh_tbl[i]), - il_rd(il, fh_tbl[i])); - } - return pos; - } -#endif - IL_ERR("FH register values:\n"); - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { - IL_ERR(" %34s: 0X%08x\n", il4965_get_fh_string(fh_tbl[i]), - il_rd(il, fh_tbl[i])); - } - return 0; -} - -static void -il4965_hdl_missed_beacon(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_missed_beacon_notif *missed_beacon; - - missed_beacon = &pkt->u.missed_beacon; - if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > - il->missed_beacon_threshold) { - D_CALIB("missed bcn cnsq %d totl %d rcd %d expctd %d\n", - le32_to_cpu(missed_beacon->consecutive_missed_beacons), - le32_to_cpu(missed_beacon->total_missed_becons), - le32_to_cpu(missed_beacon->num_recvd_beacons), - le32_to_cpu(missed_beacon->num_expected_beacons)); - if (!test_bit(S_SCANNING, &il->status)) - il4965_init_sensitivity(il); - } -} - -/* Calculate noise level, based on measurements during network silence just - * before arriving beacon. This measurement can be done only if we know - * exactly when to expect beacons, therefore only when we're associated. */ -static void -il4965_rx_calc_noise(struct il_priv *il) -{ - struct stats_rx_non_phy *rx_info; - int num_active_rx = 0; - int total_silence = 0; - int bcn_silence_a, bcn_silence_b, bcn_silence_c; - int last_rx_noise; - - rx_info = &(il->_4965.stats.rx.general); - bcn_silence_a = - le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; - bcn_silence_b = - le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; - bcn_silence_c = - le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; - - if (bcn_silence_a) { - total_silence += bcn_silence_a; - num_active_rx++; - } - if (bcn_silence_b) { - total_silence += bcn_silence_b; - num_active_rx++; - } - if (bcn_silence_c) { - total_silence += bcn_silence_c; - num_active_rx++; - } - - /* Average among active antennas */ - if (num_active_rx) - last_rx_noise = (total_silence / num_active_rx) - 107; - else - last_rx_noise = IL_NOISE_MEAS_NOT_AVAILABLE; - - D_CALIB("inband silence a %u, b %u, c %u, dBm %d\n", bcn_silence_a, - bcn_silence_b, bcn_silence_c, last_rx_noise); -} - -#ifdef CONFIG_IWLEGACY_DEBUGFS -/* - * based on the assumption of all stats counter are in DWORD - * FIXME: This function is for debugging, do not deal with - * the case of counters roll-over. - */ -static void -il4965_accumulative_stats(struct il_priv *il, __le32 * stats) -{ - int i, size; - __le32 *prev_stats; - u32 *accum_stats; - u32 *delta, *max_delta; - struct stats_general_common *general, *accum_general; - struct stats_tx *tx, *accum_tx; - - prev_stats = (__le32 *) &il->_4965.stats; - accum_stats = (u32 *) &il->_4965.accum_stats; - size = sizeof(struct il_notif_stats); - general = &il->_4965.stats.general.common; - accum_general = &il->_4965.accum_stats.general.common; - tx = &il->_4965.stats.tx; - accum_tx = &il->_4965.accum_stats.tx; - delta = (u32 *) &il->_4965.delta_stats; - max_delta = (u32 *) &il->_4965.max_delta; - - for (i = sizeof(__le32); i < size; - i += - sizeof(__le32), stats++, prev_stats++, delta++, max_delta++, - accum_stats++) { - if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) { - *delta = - (le32_to_cpu(*stats) - le32_to_cpu(*prev_stats)); - *accum_stats += *delta; - if (*delta > *max_delta) - *max_delta = *delta; - } - } - - /* reset accumulative stats for "no-counter" type stats */ - accum_general->temperature = general->temperature; - accum_general->ttl_timestamp = general->ttl_timestamp; -} -#endif - -static void -il4965_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb) -{ - const int recalib_seconds = 60; - bool change; - struct il_rx_pkt *pkt = rxb_addr(rxb); - - D_RX("Statistics notification received (%d vs %d).\n", - (int)sizeof(struct il_notif_stats), - le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK); - - change = - ((il->_4965.stats.general.common.temperature != - pkt->u.stats.general.common.temperature) || - ((il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK) != - (pkt->u.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK))); -#ifdef CONFIG_IWLEGACY_DEBUGFS - il4965_accumulative_stats(il, (__le32 *) &pkt->u.stats); -#endif - - /* TODO: reading some of stats is unneeded */ - memcpy(&il->_4965.stats, &pkt->u.stats, sizeof(il->_4965.stats)); - - set_bit(S_STATS, &il->status); - - /* - * Reschedule the stats timer to occur in recalib_seconds to ensure - * we get a thermal update even if the uCode doesn't give us one - */ - mod_timer(&il->stats_periodic, - jiffies + msecs_to_jiffies(recalib_seconds * 1000)); - - if (unlikely(!test_bit(S_SCANNING, &il->status)) && - (pkt->hdr.cmd == N_STATS)) { - il4965_rx_calc_noise(il); - queue_work(il->workqueue, &il->run_time_calib_work); - } - - if (change) - il4965_temperature_calib(il); -} - -static void -il4965_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - - if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATS_CLEAR_MSK) { -#ifdef CONFIG_IWLEGACY_DEBUGFS - memset(&il->_4965.accum_stats, 0, - sizeof(struct il_notif_stats)); - memset(&il->_4965.delta_stats, 0, - sizeof(struct il_notif_stats)); - memset(&il->_4965.max_delta, 0, sizeof(struct il_notif_stats)); -#endif - D_RX("Statistics have been cleared\n"); - } - il4965_hdl_stats(il, rxb); -} - - -/* - * mac80211 queues, ACs, hardware queues, FIFOs. - * - * Cf. http://wireless.kernel.org/en/developers/Documentation/mac80211/queues - * - * Mac80211 uses the following numbers, which we get as from it - * by way of skb_get_queue_mapping(skb): - * - * VO 0 - * VI 1 - * BE 2 - * BK 3 - * - * - * Regular (not A-MPDU) frames are put into hardware queues corresponding - * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their - * own queue per aggregation session (RA/TID combination), such queues are - * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In - * order to map frames to the right queue, we also need an AC->hw queue - * mapping. This is implemented here. - * - * Due to the way hw queues are set up (by the hw specific modules like - * 4965.c), the AC->hw queue mapping is the identity - * mapping. - */ - -static const u8 tid_to_ac[] = { - IEEE80211_AC_BE, - IEEE80211_AC_BK, - IEEE80211_AC_BK, - IEEE80211_AC_BE, - IEEE80211_AC_VI, - IEEE80211_AC_VI, - IEEE80211_AC_VO, - IEEE80211_AC_VO -}; - -static inline int -il4965_get_ac_from_tid(u16 tid) -{ - if (likely(tid < ARRAY_SIZE(tid_to_ac))) - return tid_to_ac[tid]; - - /* no support for TIDs 8-15 yet */ - return -EINVAL; -} - -static inline int -il4965_get_fifo_from_tid(u16 tid) -{ - const u8 ac_to_fifo[] = { - IL_TX_FIFO_VO, - IL_TX_FIFO_VI, - IL_TX_FIFO_BE, - IL_TX_FIFO_BK, - }; - - if (likely(tid < ARRAY_SIZE(tid_to_ac))) - return ac_to_fifo[tid_to_ac[tid]]; - - /* no support for TIDs 8-15 yet */ - return -EINVAL; -} - -/* - * handle build C_TX command notification. - */ -static void -il4965_tx_cmd_build_basic(struct il_priv *il, struct sk_buff *skb, - struct il_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, - struct ieee80211_hdr *hdr, u8 std_id) -{ - __le16 fc = hdr->frame_control; - __le32 tx_flags = tx_cmd->tx_flags; - - tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { - tx_flags |= TX_CMD_FLG_ACK_MSK; - if (ieee80211_is_mgmt(fc)) - tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - if (ieee80211_is_probe_resp(fc) && - !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) - tx_flags |= TX_CMD_FLG_TSF_MSK; - } else { - tx_flags &= (~TX_CMD_FLG_ACK_MSK); - tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - } - - if (ieee80211_is_back_req(fc)) - tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; - - tx_cmd->sta_id = std_id; - if (ieee80211_has_morefrags(fc)) - tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; - - if (ieee80211_is_data_qos(fc)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tx_cmd->tid_tspec = qc[0] & 0xf; - tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; - } else { - tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - } - - il_tx_cmd_protection(il, info, fc, &tx_flags); - - tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); - if (ieee80211_is_mgmt(fc)) { - if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) - tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); - else - tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); - } else { - tx_cmd->timeout.pm_frame_timeout = 0; - } - - tx_cmd->driver_txop = 0; - tx_cmd->tx_flags = tx_flags; - tx_cmd->next_frame_len = 0; -} - -static void -il4965_tx_cmd_build_rate(struct il_priv *il, - struct il_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, - __le16 fc) -{ - const u8 rts_retry_limit = 60; - u32 rate_flags; - int rate_idx; - u8 data_retry_limit; - u8 rate_plcp; - - /* Set retry limit on DATA packets and Probe Responses */ - if (ieee80211_is_probe_resp(fc)) - data_retry_limit = 3; - else - data_retry_limit = IL4965_DEFAULT_TX_RETRY; - tx_cmd->data_retry_limit = data_retry_limit; - /* Set retry limit on RTS packets */ - tx_cmd->rts_retry_limit = min(data_retry_limit, rts_retry_limit); - - /* DATA packets will use the uCode station table for rate/antenna - * selection */ - if (ieee80211_is_data(fc)) { - tx_cmd->initial_rate_idx = 0; - tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; - return; - } - - /** - * If the current TX rate stored in mac80211 has the MCS bit set, it's - * not really a TX rate. Thus, we use the lowest supported rate for - * this band. Also use the lowest supported rate if the stored rate - * idx is invalid. - */ - rate_idx = info->control.rates[0].idx; - if ((info->control.rates[0].flags & IEEE80211_TX_RC_MCS) || rate_idx < 0 - || rate_idx > RATE_COUNT_LEGACY) - rate_idx = rate_lowest_index(&il->bands[info->band], sta); - /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ - if (info->band == IEEE80211_BAND_5GHZ) - rate_idx += IL_FIRST_OFDM_RATE; - /* Get PLCP rate for tx_cmd->rate_n_flags */ - rate_plcp = il_rates[rate_idx].plcp; - /* Zero out flags for this packet */ - rate_flags = 0; - - /* Set CCK flag as needed */ - if (rate_idx >= IL_FIRST_CCK_RATE && rate_idx <= IL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; - - /* Set up antennas */ - il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant); - rate_flags |= BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS; - - /* Set the rate in the TX cmd */ - tx_cmd->rate_n_flags = cpu_to_le32(rate_plcp | rate_flags); -} - -static void -il4965_tx_cmd_build_hwcrypto(struct il_priv *il, struct ieee80211_tx_info *info, - struct il_tx_cmd *tx_cmd, struct sk_buff *skb_frag, - int sta_id) -{ - struct ieee80211_key_conf *keyconf = info->control.hw_key; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - tx_cmd->sec_ctl = TX_CMD_SEC_CCM; - memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); - if (info->flags & IEEE80211_TX_CTL_AMPDU) - tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; - D_TX("tx_cmd with AES hwcrypto\n"); - break; - - case WLAN_CIPHER_SUITE_TKIP: - tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; - ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); - D_TX("tx_cmd with tkip hwcrypto\n"); - break; - - case WLAN_CIPHER_SUITE_WEP104: - tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; - /* fall through */ - case WLAN_CIPHER_SUITE_WEP40: - tx_cmd->sec_ctl |= - (TX_CMD_SEC_WEP | (keyconf->keyidx & TX_CMD_SEC_MSK) << - TX_CMD_SEC_SHIFT); - - memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); - - D_TX("Configuring packet for WEP encryption " "with key %d\n", - keyconf->keyidx); - break; - - default: - IL_ERR("Unknown encode cipher %x\n", keyconf->cipher); - break; - } -} - -/* - * start C_TX command process - */ -int -il4965_tx_skb(struct il_priv *il, - struct ieee80211_sta *sta, - struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct il_station_priv *sta_priv = NULL; - struct il_tx_queue *txq; - struct il_queue *q; - struct il_device_cmd *out_cmd; - struct il_cmd_meta *out_meta; - struct il_tx_cmd *tx_cmd; - int txq_id; - dma_addr_t phys_addr; - dma_addr_t txcmd_phys; - dma_addr_t scratch_phys; - u16 len, firstlen, secondlen; - u16 seq_number = 0; - __le16 fc; - u8 hdr_len; - u8 sta_id; - u8 wait_write_ptr = 0; - u8 tid = 0; - u8 *qc = NULL; - unsigned long flags; - bool is_agg = false; - - spin_lock_irqsave(&il->lock, flags); - if (il_is_rfkill(il)) { - D_DROP("Dropping - RF KILL\n"); - goto drop_unlock; - } - - fc = hdr->frame_control; - -#ifdef CONFIG_IWLEGACY_DEBUG - if (ieee80211_is_auth(fc)) - D_TX("Sending AUTH frame\n"); - else if (ieee80211_is_assoc_req(fc)) - D_TX("Sending ASSOC frame\n"); - else if (ieee80211_is_reassoc_req(fc)) - D_TX("Sending REASSOC frame\n"); -#endif - - hdr_len = ieee80211_hdrlen(fc); - - /* For management frames use broadcast id to do not break aggregation */ - if (!ieee80211_is_data(fc)) - sta_id = il->hw_params.bcast_id; - else { - /* Find idx into station table for destination station */ - sta_id = il_sta_id_or_broadcast(il, sta); - - if (sta_id == IL_INVALID_STATION) { - D_DROP("Dropping - INVALID STATION: %pM\n", hdr->addr1); - goto drop_unlock; - } - } - - D_TX("station Id %d\n", sta_id); - - if (sta) - sta_priv = (void *)sta->drv_priv; - - if (sta_priv && sta_priv->asleep && - (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { - /* - * This sends an asynchronous command to the device, - * but we can rely on it being processed before the - * next frame is processed -- and the next frame to - * this station is the one that will consume this - * counter. - * For now set the counter to just 1 since we do not - * support uAPSD yet. - */ - il4965_sta_modify_sleep_tx_count(il, sta_id, 1); - } - - /* FIXME: remove me ? */ - WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); - - /* Access category (AC) is also the queue number */ - txq_id = skb_get_queue_mapping(skb); - - /* irqs already disabled/saved above when locking il->lock */ - spin_lock(&il->sta_lock); - - if (ieee80211_is_data_qos(fc)) { - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) { - spin_unlock(&il->sta_lock); - goto drop_unlock; - } - seq_number = il->stations[sta_id].tid[tid].seq_number; - seq_number &= IEEE80211_SCTL_SEQ; - hdr->seq_ctrl = - hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(seq_number); - seq_number += 0x10; - /* aggregation is on for this */ - if (info->flags & IEEE80211_TX_CTL_AMPDU && - il->stations[sta_id].tid[tid].agg.state == IL_AGG_ON) { - txq_id = il->stations[sta_id].tid[tid].agg.txq_id; - is_agg = true; - } - } - - txq = &il->txq[txq_id]; - q = &txq->q; - - if (unlikely(il_queue_space(q) < q->high_mark)) { - spin_unlock(&il->sta_lock); - goto drop_unlock; - } - - if (ieee80211_is_data_qos(fc)) { - il->stations[sta_id].tid[tid].tfds_in_queue++; - if (!ieee80211_has_morefrags(fc)) - il->stations[sta_id].tid[tid].seq_number = seq_number; - } - - spin_unlock(&il->sta_lock); - - txq->skbs[q->write_ptr] = skb; - - /* Set up first empty entry in queue's array of Tx/cmd buffers */ - out_cmd = txq->cmd[q->write_ptr]; - out_meta = &txq->meta[q->write_ptr]; - tx_cmd = &out_cmd->cmd.tx; - memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); - memset(tx_cmd, 0, sizeof(struct il_tx_cmd)); - - /* - * Set up the Tx-command (not MAC!) header. - * Store the chosen Tx queue and TFD idx within the sequence field; - * after Tx, uCode's Tx response will return this value so driver can - * locate the frame within the tx queue and do post-tx processing. - */ - out_cmd->hdr.cmd = C_TX; - out_cmd->hdr.sequence = - cpu_to_le16((u16) - (QUEUE_TO_SEQ(txq_id) | IDX_TO_SEQ(q->write_ptr))); - - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdr_len); - - /* Total # bytes to be transmitted */ - tx_cmd->len = cpu_to_le16((u16) skb->len); - - if (info->control.hw_key) - il4965_tx_cmd_build_hwcrypto(il, info, tx_cmd, skb, sta_id); - - /* TODO need this for burst mode later on */ - il4965_tx_cmd_build_basic(il, skb, tx_cmd, info, hdr, sta_id); - - il4965_tx_cmd_build_rate(il, tx_cmd, info, sta, fc); - - /* - * Use the first empty entry in this queue's command buffer array - * to contain the Tx command and MAC header concatenated together - * (payload data will be in another buffer). - * Size of this varies, due to varying MAC header length. - * If end is not dword aligned, we'll have 2 extra bytes at the end - * of the MAC header (device reads on dword boundaries). - * We'll tell device about this padding later. - */ - len = sizeof(struct il_tx_cmd) + sizeof(struct il_cmd_header) + hdr_len; - firstlen = (len + 3) & ~3; - - /* Tell NIC about any 2-byte padding after MAC header */ - if (firstlen != len) - tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; - - /* Physical address of this Tx command's header (not MAC header!), - * within command buffer array. */ - txcmd_phys = - pci_map_single(il->pci_dev, &out_cmd->hdr, firstlen, - PCI_DMA_BIDIRECTIONAL); - if (unlikely(pci_dma_mapping_error(il->pci_dev, txcmd_phys))) - goto drop_unlock; - - /* Set up TFD's 2nd entry to point directly to remainder of skb, - * if any (802.11 null frames have no payload). */ - secondlen = skb->len - hdr_len; - if (secondlen > 0) { - phys_addr = - pci_map_single(il->pci_dev, skb->data + hdr_len, secondlen, - PCI_DMA_TODEVICE); - if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr))) - goto drop_unlock; - } - - /* Add buffer containing Tx command and MAC(!) header to TFD's - * first entry */ - il->ops->txq_attach_buf_to_tfd(il, txq, txcmd_phys, firstlen, 1, 0); - dma_unmap_addr_set(out_meta, mapping, txcmd_phys); - dma_unmap_len_set(out_meta, len, firstlen); - if (secondlen) - il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, secondlen, - 0, 0); - - if (!ieee80211_has_morefrags(hdr->frame_control)) { - txq->need_update = 1; - } else { - wait_write_ptr = 1; - txq->need_update = 0; - } - - scratch_phys = - txcmd_phys + sizeof(struct il_cmd_header) + - offsetof(struct il_tx_cmd, scratch); - - /* take back ownership of DMA buffer to enable update */ - pci_dma_sync_single_for_cpu(il->pci_dev, txcmd_phys, firstlen, - PCI_DMA_BIDIRECTIONAL); - tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); - tx_cmd->dram_msb_ptr = il_get_dma_hi_addr(scratch_phys); - - il_update_stats(il, true, fc, skb->len); - - D_TX("sequence nr = 0X%x\n", le16_to_cpu(out_cmd->hdr.sequence)); - D_TX("tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); - il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd, sizeof(*tx_cmd)); - il_print_hex_dump(il, IL_DL_TX, (u8 *) tx_cmd->hdr, hdr_len); - - /* Set up entry for this TFD in Tx byte-count array */ - if (info->flags & IEEE80211_TX_CTL_AMPDU) - il->ops->txq_update_byte_cnt_tbl(il, txq, le16_to_cpu(tx_cmd->len)); - - pci_dma_sync_single_for_device(il->pci_dev, txcmd_phys, firstlen, - PCI_DMA_BIDIRECTIONAL); - - /* Tell device the write idx *just past* this latest filled TFD */ - q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); - il_txq_update_write_ptr(il, txq); - spin_unlock_irqrestore(&il->lock, flags); - - /* - * At this point the frame is "transmitted" successfully - * and we will get a TX status notification eventually, - * regardless of the value of ret. "ret" only indicates - * whether or not we should update the write pointer. - */ - - /* - * Avoid atomic ops if it isn't an associated client. - * Also, if this is a packet for aggregation, don't - * increase the counter because the ucode will stop - * aggregation queues when their respective station - * goes to sleep. - */ - if (sta_priv && sta_priv->client && !is_agg) - atomic_inc(&sta_priv->pending_frames); - - if (il_queue_space(q) < q->high_mark && il->mac80211_registered) { - if (wait_write_ptr) { - spin_lock_irqsave(&il->lock, flags); - txq->need_update = 1; - il_txq_update_write_ptr(il, txq); - spin_unlock_irqrestore(&il->lock, flags); - } else { - il_stop_queue(il, txq); - } - } - - return 0; - -drop_unlock: - spin_unlock_irqrestore(&il->lock, flags); - return -1; -} - -static inline int -il4965_alloc_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr, size_t size) -{ - ptr->addr = dma_alloc_coherent(&il->pci_dev->dev, size, &ptr->dma, - GFP_KERNEL); - if (!ptr->addr) - return -ENOMEM; - ptr->size = size; - return 0; -} - -static inline void -il4965_free_dma_ptr(struct il_priv *il, struct il_dma_ptr *ptr) -{ - if (unlikely(!ptr->addr)) - return; - - dma_free_coherent(&il->pci_dev->dev, ptr->size, ptr->addr, ptr->dma); - memset(ptr, 0, sizeof(*ptr)); -} - -/** - * il4965_hw_txq_ctx_free - Free TXQ Context - * - * Destroy all TX DMA queues and structures - */ -void -il4965_hw_txq_ctx_free(struct il_priv *il) -{ - int txq_id; - - /* Tx queues */ - if (il->txq) { - for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) - if (txq_id == il->cmd_queue) - il_cmd_queue_free(il); - else - il_tx_queue_free(il, txq_id); - } - il4965_free_dma_ptr(il, &il->kw); - - il4965_free_dma_ptr(il, &il->scd_bc_tbls); - - /* free tx queue structure */ - il_free_txq_mem(il); -} - -/** - * il4965_txq_ctx_alloc - allocate TX queue context - * Allocate all Tx DMA structures and initialize them - * - * @param il - * @return error code - */ -int -il4965_txq_ctx_alloc(struct il_priv *il) -{ - int ret, txq_id; - unsigned long flags; - - /* Free all tx/cmd queues and keep-warm buffer */ - il4965_hw_txq_ctx_free(il); - - ret = - il4965_alloc_dma_ptr(il, &il->scd_bc_tbls, - il->hw_params.scd_bc_tbls_size); - if (ret) { - IL_ERR("Scheduler BC Table allocation failed\n"); - goto error_bc_tbls; - } - /* Alloc keep-warm buffer */ - ret = il4965_alloc_dma_ptr(il, &il->kw, IL_KW_SIZE); - if (ret) { - IL_ERR("Keep Warm allocation failed\n"); - goto error_kw; - } - - /* allocate tx queue structure */ - ret = il_alloc_txq_mem(il); - if (ret) - goto error; - - spin_lock_irqsave(&il->lock, flags); - - /* Turn off all Tx DMA fifos */ - il4965_txq_set_sched(il, 0); - - /* Tell NIC where to find the "keep warm" buffer */ - il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4); - - spin_unlock_irqrestore(&il->lock, flags); - - /* Alloc and init all Tx queues, including the command queue (#4/#9) */ - for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) { - ret = il_tx_queue_init(il, txq_id); - if (ret) { - IL_ERR("Tx %d queue init failed\n", txq_id); - goto error; - } - } - - return ret; - -error: - il4965_hw_txq_ctx_free(il); - il4965_free_dma_ptr(il, &il->kw); -error_kw: - il4965_free_dma_ptr(il, &il->scd_bc_tbls); -error_bc_tbls: - return ret; -} - -void -il4965_txq_ctx_reset(struct il_priv *il) -{ - int txq_id; - unsigned long flags; - - spin_lock_irqsave(&il->lock, flags); - - /* Turn off all Tx DMA fifos */ - il4965_txq_set_sched(il, 0); - /* Tell NIC where to find the "keep warm" buffer */ - il_wr(il, FH49_KW_MEM_ADDR_REG, il->kw.dma >> 4); - - spin_unlock_irqrestore(&il->lock, flags); - - /* Alloc and init all Tx queues, including the command queue (#4) */ - for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) - il_tx_queue_reset(il, txq_id); -} - -static void -il4965_txq_ctx_unmap(struct il_priv *il) -{ - int txq_id; - - if (!il->txq) - return; - - /* Unmap DMA from host system and free skb's */ - for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) - if (txq_id == il->cmd_queue) - il_cmd_queue_unmap(il); - else - il_tx_queue_unmap(il, txq_id); -} - -/** - * il4965_txq_ctx_stop - Stop all Tx DMA channels - */ -void -il4965_txq_ctx_stop(struct il_priv *il) -{ - int ch, ret; - - _il_wr_prph(il, IL49_SCD_TXFACT, 0); - - /* Stop each Tx DMA channel, and wait for it to be idle */ - for (ch = 0; ch < il->hw_params.dma_chnl_num; ch++) { - _il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); - ret = - _il_poll_bit(il, FH49_TSSR_TX_STATUS_REG, - FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), - FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), - 1000); - if (ret < 0) - IL_ERR("Timeout stopping DMA channel %d [0x%08x]", - ch, _il_rd(il, FH49_TSSR_TX_STATUS_REG)); - } -} - -/* - * Find first available (lowest unused) Tx Queue, mark it "active". - * Called only when finding queue for aggregation. - * Should never return anything < 7, because they should already - * be in use as EDCA AC (0-3), Command (4), reserved (5, 6) - */ -static int -il4965_txq_ctx_activate_free(struct il_priv *il) -{ - int txq_id; - - for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++) - if (!test_and_set_bit(txq_id, &il->txq_ctx_active_msk)) - return txq_id; - return -1; -} - -/** - * il4965_tx_queue_stop_scheduler - Stop queue, but keep configuration - */ -static void -il4965_tx_queue_stop_scheduler(struct il_priv *il, u16 txq_id) -{ - /* Simply stop the queue, but don't change any configuration; - * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ - il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id), - (0 << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) | - (1 << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); -} - -/** - * il4965_tx_queue_set_q2ratid - Map unique receiver/tid combination to a queue - */ -static int -il4965_tx_queue_set_q2ratid(struct il_priv *il, u16 ra_tid, u16 txq_id) -{ - u32 tbl_dw_addr; - u32 tbl_dw; - u16 scd_q2ratid; - - scd_q2ratid = ra_tid & IL_SCD_QUEUE_RA_TID_MAP_RATID_MSK; - - tbl_dw_addr = - il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id); - - tbl_dw = il_read_targ_mem(il, tbl_dw_addr); - - if (txq_id & 0x1) - tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); - else - tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); - - il_write_targ_mem(il, tbl_dw_addr, tbl_dw); - - return 0; -} - -/** - * il4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue - * - * NOTE: txq_id must be greater than IL49_FIRST_AMPDU_QUEUE, - * i.e. it must be one of the higher queues used for aggregation - */ -static int -il4965_txq_agg_enable(struct il_priv *il, int txq_id, int tx_fifo, int sta_id, - int tid, u16 ssn_idx) -{ - unsigned long flags; - u16 ra_tid; - int ret; - - if ((IL49_FIRST_AMPDU_QUEUE > txq_id) || - (IL49_FIRST_AMPDU_QUEUE + - il->cfg->num_of_ampdu_queues <= txq_id)) { - IL_WARN("queue number out of range: %d, must be %d to %d\n", - txq_id, IL49_FIRST_AMPDU_QUEUE, - IL49_FIRST_AMPDU_QUEUE + - il->cfg->num_of_ampdu_queues - 1); - return -EINVAL; - } - - ra_tid = BUILD_RAxTID(sta_id, tid); - - /* Modify device's station table to Tx this TID */ - ret = il4965_sta_tx_modify_enable_tid(il, sta_id, tid); - if (ret) - return ret; - - spin_lock_irqsave(&il->lock, flags); - - /* Stop this Tx queue before configuring it */ - il4965_tx_queue_stop_scheduler(il, txq_id); - - /* Map receiver-address / traffic-ID to this queue */ - il4965_tx_queue_set_q2ratid(il, ra_tid, txq_id); - - /* Set this queue as a chain-building queue */ - il_set_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); - - /* Place first TFD at idx corresponding to start sequence number. - * Assumes that ssn_idx is valid (!= 0xFFF) */ - il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); - il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); - il4965_set_wr_ptrs(il, txq_id, ssn_idx); - - /* Set up Tx win size and frame limit for this queue */ - il_write_targ_mem(il, - il->scd_base_addr + - IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id), - (SCD_WIN_SIZE << IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) - & IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); - - il_write_targ_mem(il, - il->scd_base_addr + - IL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), - (SCD_FRAME_LIMIT << - IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); - - il_set_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id)); - - /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ - il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 1); - - spin_unlock_irqrestore(&il->lock, flags); - - return 0; -} - -int -il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 * ssn) -{ - int sta_id; - int tx_fifo; - int txq_id; - int ret; - unsigned long flags; - struct il_tid_data *tid_data; - - /* FIXME: warning if tx fifo not found ? */ - tx_fifo = il4965_get_fifo_from_tid(tid); - if (unlikely(tx_fifo < 0)) - return tx_fifo; - - D_HT("%s on ra = %pM tid = %d\n", __func__, sta->addr, tid); - - sta_id = il_sta_id(sta); - if (sta_id == IL_INVALID_STATION) { - IL_ERR("Start AGG on invalid station\n"); - return -ENXIO; - } - if (unlikely(tid >= MAX_TID_COUNT)) - return -EINVAL; - - if (il->stations[sta_id].tid[tid].agg.state != IL_AGG_OFF) { - IL_ERR("Start AGG when state is not IL_AGG_OFF !\n"); - return -ENXIO; - } - - txq_id = il4965_txq_ctx_activate_free(il); - if (txq_id == -1) { - IL_ERR("No free aggregation queue available\n"); - return -ENXIO; - } - - spin_lock_irqsave(&il->sta_lock, flags); - tid_data = &il->stations[sta_id].tid[tid]; - *ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); - tid_data->agg.txq_id = txq_id; - il_set_swq_id(&il->txq[txq_id], il4965_get_ac_from_tid(tid), txq_id); - spin_unlock_irqrestore(&il->sta_lock, flags); - - ret = il4965_txq_agg_enable(il, txq_id, tx_fifo, sta_id, tid, *ssn); - if (ret) - return ret; - - spin_lock_irqsave(&il->sta_lock, flags); - tid_data = &il->stations[sta_id].tid[tid]; - if (tid_data->tfds_in_queue == 0) { - D_HT("HW queue is empty\n"); - tid_data->agg.state = IL_AGG_ON; - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - } else { - D_HT("HW queue is NOT empty: %d packets in HW queue\n", - tid_data->tfds_in_queue); - tid_data->agg.state = IL_EMPTYING_HW_QUEUE_ADDBA; - } - spin_unlock_irqrestore(&il->sta_lock, flags); - return ret; -} - -/** - * txq_id must be greater than IL49_FIRST_AMPDU_QUEUE - * il->lock must be held by the caller - */ -static int -il4965_txq_agg_disable(struct il_priv *il, u16 txq_id, u16 ssn_idx, u8 tx_fifo) -{ - if ((IL49_FIRST_AMPDU_QUEUE > txq_id) || - (IL49_FIRST_AMPDU_QUEUE + - il->cfg->num_of_ampdu_queues <= txq_id)) { - IL_WARN("queue number out of range: %d, must be %d to %d\n", - txq_id, IL49_FIRST_AMPDU_QUEUE, - IL49_FIRST_AMPDU_QUEUE + - il->cfg->num_of_ampdu_queues - 1); - return -EINVAL; - } - - il4965_tx_queue_stop_scheduler(il, txq_id); - - il_clear_bits_prph(il, IL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); - - il->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); - il->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); - /* supposes that ssn_idx is valid (!= 0xFFF) */ - il4965_set_wr_ptrs(il, txq_id, ssn_idx); - - il_clear_bits_prph(il, IL49_SCD_INTERRUPT_MASK, (1 << txq_id)); - il_txq_ctx_deactivate(il, txq_id); - il4965_tx_queue_set_status(il, &il->txq[txq_id], tx_fifo, 0); - - return 0; -} - -int -il4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid) -{ - int tx_fifo_id, txq_id, sta_id, ssn; - struct il_tid_data *tid_data; - int write_ptr, read_ptr; - unsigned long flags; - - /* FIXME: warning if tx_fifo_id not found ? */ - tx_fifo_id = il4965_get_fifo_from_tid(tid); - if (unlikely(tx_fifo_id < 0)) - return tx_fifo_id; - - sta_id = il_sta_id(sta); - - if (sta_id == IL_INVALID_STATION) { - IL_ERR("Invalid station for AGG tid %d\n", tid); - return -ENXIO; - } - - spin_lock_irqsave(&il->sta_lock, flags); - - tid_data = &il->stations[sta_id].tid[tid]; - ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4; - txq_id = tid_data->agg.txq_id; - - switch (il->stations[sta_id].tid[tid].agg.state) { - case IL_EMPTYING_HW_QUEUE_ADDBA: - /* - * This can happen if the peer stops aggregation - * again before we've had a chance to drain the - * queue we selected previously, i.e. before the - * session was really started completely. - */ - D_HT("AGG stop before setup done\n"); - goto turn_off; - case IL_AGG_ON: - break; - default: - IL_WARN("Stopping AGG while state not ON or starting\n"); - } - - write_ptr = il->txq[txq_id].q.write_ptr; - read_ptr = il->txq[txq_id].q.read_ptr; - - /* The queue is not empty */ - if (write_ptr != read_ptr) { - D_HT("Stopping a non empty AGG HW QUEUE\n"); - il->stations[sta_id].tid[tid].agg.state = - IL_EMPTYING_HW_QUEUE_DELBA; - spin_unlock_irqrestore(&il->sta_lock, flags); - return 0; - } - - D_HT("HW queue is empty\n"); -turn_off: - il->stations[sta_id].tid[tid].agg.state = IL_AGG_OFF; - - /* do not restore/save irqs */ - spin_unlock(&il->sta_lock); - spin_lock(&il->lock); - - /* - * the only reason this call can fail is queue number out of range, - * which can happen if uCode is reloaded and all the station - * information are lost. if it is outside the range, there is no need - * to deactivate the uCode queue, just return "success" to allow - * mac80211 to clean up it own data. - */ - il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo_id); - spin_unlock_irqrestore(&il->lock, flags); - - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - - return 0; -} - -int -il4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id) -{ - struct il_queue *q = &il->txq[txq_id].q; - u8 *addr = il->stations[sta_id].sta.sta.addr; - struct il_tid_data *tid_data = &il->stations[sta_id].tid[tid]; - - lockdep_assert_held(&il->sta_lock); - - switch (il->stations[sta_id].tid[tid].agg.state) { - case IL_EMPTYING_HW_QUEUE_DELBA: - /* We are reclaiming the last packet of the */ - /* aggregated HW queue */ - if (txq_id == tid_data->agg.txq_id && - q->read_ptr == q->write_ptr) { - u16 ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); - int tx_fifo = il4965_get_fifo_from_tid(tid); - D_HT("HW queue empty: continue DELBA flow\n"); - il4965_txq_agg_disable(il, txq_id, ssn, tx_fifo); - tid_data->agg.state = IL_AGG_OFF; - ieee80211_stop_tx_ba_cb_irqsafe(il->vif, addr, tid); - } - break; - case IL_EMPTYING_HW_QUEUE_ADDBA: - /* We are reclaiming the last packet of the queue */ - if (tid_data->tfds_in_queue == 0) { - D_HT("HW queue empty: continue ADDBA flow\n"); - tid_data->agg.state = IL_AGG_ON; - ieee80211_start_tx_ba_cb_irqsafe(il->vif, addr, tid); - } - break; - } - - return 0; -} - -static void -il4965_non_agg_tx_status(struct il_priv *il, const u8 *addr1) -{ - struct ieee80211_sta *sta; - struct il_station_priv *sta_priv; - - rcu_read_lock(); - sta = ieee80211_find_sta(il->vif, addr1); - if (sta) { - sta_priv = (void *)sta->drv_priv; - /* avoid atomic ops if this isn't a client */ - if (sta_priv->client && - atomic_dec_return(&sta_priv->pending_frames) == 0) - ieee80211_sta_block_awake(il->hw, sta, false); - } - rcu_read_unlock(); -} - -static void -il4965_tx_status(struct il_priv *il, struct sk_buff *skb, bool is_agg) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - - if (!is_agg) - il4965_non_agg_tx_status(il, hdr->addr1); - - ieee80211_tx_status_irqsafe(il->hw, skb); -} - -int -il4965_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx) -{ - struct il_tx_queue *txq = &il->txq[txq_id]; - struct il_queue *q = &txq->q; - int nfreed = 0; - struct ieee80211_hdr *hdr; - struct sk_buff *skb; - - if (idx >= q->n_bd || il_queue_used(q, idx) == 0) { - IL_ERR("Read idx for DMA queue txq id (%d), idx %d, " - "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd, - q->write_ptr, q->read_ptr); - return 0; - } - - for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; - q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { - - skb = txq->skbs[txq->q.read_ptr]; - - if (WARN_ON_ONCE(skb == NULL)) - continue; - - hdr = (struct ieee80211_hdr *) skb->data; - if (ieee80211_is_data_qos(hdr->frame_control)) - nfreed++; - - il4965_tx_status(il, skb, txq_id >= IL4965_FIRST_AMPDU_QUEUE); - - txq->skbs[txq->q.read_ptr] = NULL; - il->ops->txq_free_tfd(il, txq); - } - return nfreed; -} - -/** - * il4965_tx_status_reply_compressed_ba - Update tx status from block-ack - * - * Go through block-ack's bitmap of ACK'd frames, update driver's record of - * ACK vs. not. This gets sent to mac80211, then to rate scaling algo. - */ -static int -il4965_tx_status_reply_compressed_ba(struct il_priv *il, struct il_ht_agg *agg, - struct il_compressed_ba_resp *ba_resp) -{ - int i, sh, ack; - u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl); - u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); - int successes = 0; - struct ieee80211_tx_info *info; - u64 bitmap, sent_bitmap; - - if (unlikely(!agg->wait_for_ba)) { - if (unlikely(ba_resp->bitmap)) - IL_ERR("Received BA when not expected\n"); - return -EINVAL; - } - - /* Mark that the expected block-ack response arrived */ - agg->wait_for_ba = 0; - D_TX_REPLY("BA %d %d\n", agg->start_idx, ba_resp->seq_ctl); - - /* Calculate shift to align block-ack bits with our Tx win bits */ - sh = agg->start_idx - SEQ_TO_IDX(seq_ctl >> 4); - if (sh < 0) /* tbw something is wrong with indices */ - sh += 0x100; - - if (agg->frame_count > (64 - sh)) { - D_TX_REPLY("more frames than bitmap size"); - return -1; - } - - /* don't use 64-bit values for now */ - bitmap = le64_to_cpu(ba_resp->bitmap) >> sh; - - /* check for success or failure according to the - * transmitted bitmap and block-ack bitmap */ - sent_bitmap = bitmap & agg->bitmap; - - /* For each frame attempted in aggregation, - * update driver's record of tx frame's status. */ - i = 0; - while (sent_bitmap) { - ack = sent_bitmap & 1ULL; - successes += ack; - D_TX_REPLY("%s ON i=%d idx=%d raw=%d\n", ack ? "ACK" : "NACK", - i, (agg->start_idx + i) & 0xff, agg->start_idx + i); - sent_bitmap >>= 1; - ++i; - } - - D_TX_REPLY("Bitmap %llx\n", (unsigned long long)bitmap); - - info = IEEE80211_SKB_CB(il->txq[scd_flow].skbs[agg->start_idx]); - memset(&info->status, 0, sizeof(info->status)); - info->flags |= IEEE80211_TX_STAT_ACK; - info->flags |= IEEE80211_TX_STAT_AMPDU; - info->status.ampdu_ack_len = successes; - info->status.ampdu_len = agg->frame_count; - il4965_hwrate_to_tx_control(il, agg->rate_n_flags, info); - - return 0; -} - -static inline bool -il4965_is_tx_success(u32 status) -{ - status &= TX_STATUS_MSK; - return (status == TX_STATUS_SUCCESS || status == TX_STATUS_DIRECT_DONE); -} - -static u8 -il4965_find_station(struct il_priv *il, const u8 *addr) -{ - int i; - int start = 0; - int ret = IL_INVALID_STATION; - unsigned long flags; - - if (il->iw_mode == NL80211_IFTYPE_ADHOC) - start = IL_STA_ID; - - if (is_broadcast_ether_addr(addr)) - return il->hw_params.bcast_id; - - spin_lock_irqsave(&il->sta_lock, flags); - for (i = start; i < il->hw_params.max_stations; i++) - if (il->stations[i].used && - ether_addr_equal(il->stations[i].sta.sta.addr, addr)) { - ret = i; - goto out; - } - - D_ASSOC("can not find STA %pM total %d\n", addr, il->num_stations); - -out: - /* - * It may be possible that more commands interacting with stations - * arrive before we completed processing the adding of - * station - */ - if (ret != IL_INVALID_STATION && - (!(il->stations[ret].used & IL_STA_UCODE_ACTIVE) || - ((il->stations[ret].used & IL_STA_UCODE_ACTIVE) && - (il->stations[ret].used & IL_STA_UCODE_INPROGRESS)))) { - IL_ERR("Requested station info for sta %d before ready.\n", - ret); - ret = IL_INVALID_STATION; - } - spin_unlock_irqrestore(&il->sta_lock, flags); - return ret; -} - -static int -il4965_get_ra_sta_id(struct il_priv *il, struct ieee80211_hdr *hdr) -{ - if (il->iw_mode == NL80211_IFTYPE_STATION) - return IL_AP_ID; - else { - u8 *da = ieee80211_get_DA(hdr); - - return il4965_find_station(il, da); - } -} - -static inline u32 -il4965_get_scd_ssn(struct il4965_tx_resp *tx_resp) -{ - return le32_to_cpup(&tx_resp->u.status + - tx_resp->frame_count) & IEEE80211_MAX_SN; -} - -static inline u32 -il4965_tx_status_to_mac80211(u32 status) -{ - status &= TX_STATUS_MSK; - - switch (status) { - case TX_STATUS_SUCCESS: - case TX_STATUS_DIRECT_DONE: - return IEEE80211_TX_STAT_ACK; - case TX_STATUS_FAIL_DEST_PS: - return IEEE80211_TX_STAT_TX_FILTERED; - default: - return 0; - } -} - -/** - * il4965_tx_status_reply_tx - Handle Tx response for frames in aggregation queue - */ -static int -il4965_tx_status_reply_tx(struct il_priv *il, struct il_ht_agg *agg, - struct il4965_tx_resp *tx_resp, int txq_id, - u16 start_idx) -{ - u16 status; - struct agg_tx_status *frame_status = tx_resp->u.agg_status; - struct ieee80211_tx_info *info = NULL; - struct ieee80211_hdr *hdr = NULL; - u32 rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); - int i, sh, idx; - u16 seq; - if (agg->wait_for_ba) - D_TX_REPLY("got tx response w/o block-ack\n"); - - agg->frame_count = tx_resp->frame_count; - agg->start_idx = start_idx; - agg->rate_n_flags = rate_n_flags; - agg->bitmap = 0; - - /* num frames attempted by Tx command */ - if (agg->frame_count == 1) { - /* Only one frame was attempted; no block-ack will arrive */ - status = le16_to_cpu(frame_status[0].status); - idx = start_idx; - - D_TX_REPLY("FrameCnt = %d, StartIdx=%d idx=%d\n", - agg->frame_count, agg->start_idx, idx); - - info = IEEE80211_SKB_CB(il->txq[txq_id].skbs[idx]); - info->status.rates[0].count = tx_resp->failure_frame + 1; - info->flags &= ~IEEE80211_TX_CTL_AMPDU; - info->flags |= il4965_tx_status_to_mac80211(status); - il4965_hwrate_to_tx_control(il, rate_n_flags, info); - - D_TX_REPLY("1 Frame 0x%x failure :%d\n", status & 0xff, - tx_resp->failure_frame); - D_TX_REPLY("Rate Info rate_n_flags=%x\n", rate_n_flags); - - agg->wait_for_ba = 0; - } else { - /* Two or more frames were attempted; expect block-ack */ - u64 bitmap = 0; - int start = agg->start_idx; - struct sk_buff *skb; - - /* Construct bit-map of pending frames within Tx win */ - for (i = 0; i < agg->frame_count; i++) { - u16 sc; - status = le16_to_cpu(frame_status[i].status); - seq = le16_to_cpu(frame_status[i].sequence); - idx = SEQ_TO_IDX(seq); - txq_id = SEQ_TO_QUEUE(seq); - - if (status & - (AGG_TX_STATE_FEW_BYTES_MSK | - AGG_TX_STATE_ABORT_MSK)) - continue; - - D_TX_REPLY("FrameCnt = %d, txq_id=%d idx=%d\n", - agg->frame_count, txq_id, idx); - - skb = il->txq[txq_id].skbs[idx]; - if (WARN_ON_ONCE(skb == NULL)) - return -1; - hdr = (struct ieee80211_hdr *) skb->data; - - sc = le16_to_cpu(hdr->seq_ctrl); - if (idx != (IEEE80211_SEQ_TO_SN(sc) & 0xff)) { - IL_ERR("BUG_ON idx doesn't match seq control" - " idx=%d, seq_idx=%d, seq=%d\n", idx, - IEEE80211_SEQ_TO_SN(sc), hdr->seq_ctrl); - return -1; - } - - D_TX_REPLY("AGG Frame i=%d idx %d seq=%d\n", i, idx, - IEEE80211_SEQ_TO_SN(sc)); - - sh = idx - start; - if (sh > 64) { - sh = (start - idx) + 0xff; - bitmap = bitmap << sh; - sh = 0; - start = idx; - } else if (sh < -64) - sh = 0xff - (start - idx); - else if (sh < 0) { - sh = start - idx; - start = idx; - bitmap = bitmap << sh; - sh = 0; - } - bitmap |= 1ULL << sh; - D_TX_REPLY("start=%d bitmap=0x%llx\n", start, - (unsigned long long)bitmap); - } - - agg->bitmap = bitmap; - agg->start_idx = start; - D_TX_REPLY("Frames %d start_idx=%d bitmap=0x%llx\n", - agg->frame_count, agg->start_idx, - (unsigned long long)agg->bitmap); - - if (bitmap) - agg->wait_for_ba = 1; - } - return 0; -} - -/** - * il4965_hdl_tx - Handle standard (non-aggregation) Tx response - */ -static void -il4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - u16 sequence = le16_to_cpu(pkt->hdr.sequence); - int txq_id = SEQ_TO_QUEUE(sequence); - int idx = SEQ_TO_IDX(sequence); - struct il_tx_queue *txq = &il->txq[txq_id]; - struct sk_buff *skb; - struct ieee80211_hdr *hdr; - struct ieee80211_tx_info *info; - struct il4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; - u32 status = le32_to_cpu(tx_resp->u.status); - int uninitialized_var(tid); - int sta_id; - int freed; - u8 *qc = NULL; - unsigned long flags; - - if (idx >= txq->q.n_bd || il_queue_used(&txq->q, idx) == 0) { - IL_ERR("Read idx for DMA queue txq_id (%d) idx %d " - "is out of range [0-%d] %d %d\n", txq_id, idx, - txq->q.n_bd, txq->q.write_ptr, txq->q.read_ptr); - return; - } - - txq->time_stamp = jiffies; - - skb = txq->skbs[txq->q.read_ptr]; - info = IEEE80211_SKB_CB(skb); - memset(&info->status, 0, sizeof(info->status)); - - hdr = (struct ieee80211_hdr *) skb->data; - if (ieee80211_is_data_qos(hdr->frame_control)) { - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & 0xf; - } - - sta_id = il4965_get_ra_sta_id(il, hdr); - if (txq->sched_retry && unlikely(sta_id == IL_INVALID_STATION)) { - IL_ERR("Station not known\n"); - return; - } - - /* - * Firmware will not transmit frame on passive channel, if it not yet - * received some valid frame on that channel. When this error happen - * we have to wait until firmware will unblock itself i.e. when we - * note received beacon or other frame. We unblock queues in - * il4965_pass_packet_to_mac80211 or in il_mac_bss_info_changed. - */ - if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) && - il->iw_mode == NL80211_IFTYPE_STATION) { - il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE); - D_INFO("Stopped queues - RX waiting on passive channel\n"); - } - - spin_lock_irqsave(&il->sta_lock, flags); - if (txq->sched_retry) { - const u32 scd_ssn = il4965_get_scd_ssn(tx_resp); - struct il_ht_agg *agg = NULL; - WARN_ON(!qc); - - agg = &il->stations[sta_id].tid[tid].agg; - - il4965_tx_status_reply_tx(il, agg, tx_resp, txq_id, idx); - - /* check if BAR is needed */ - if (tx_resp->frame_count == 1 && - !il4965_is_tx_success(status)) - info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; - - if (txq->q.read_ptr != (scd_ssn & 0xff)) { - idx = il_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd); - D_TX_REPLY("Retry scheduler reclaim scd_ssn " - "%d idx %d\n", scd_ssn, idx); - freed = il4965_tx_queue_reclaim(il, txq_id, idx); - if (qc) - il4965_free_tfds_in_queue(il, sta_id, tid, - freed); - - if (il->mac80211_registered && - il_queue_space(&txq->q) > txq->q.low_mark && - agg->state != IL_EMPTYING_HW_QUEUE_DELBA) - il_wake_queue(il, txq); - } - } else { - info->status.rates[0].count = tx_resp->failure_frame + 1; - info->flags |= il4965_tx_status_to_mac80211(status); - il4965_hwrate_to_tx_control(il, - le32_to_cpu(tx_resp->rate_n_flags), - info); - - D_TX_REPLY("TXQ %d status %s (0x%08x) " - "rate_n_flags 0x%x retries %d\n", txq_id, - il4965_get_tx_fail_reason(status), status, - le32_to_cpu(tx_resp->rate_n_flags), - tx_resp->failure_frame); - - freed = il4965_tx_queue_reclaim(il, txq_id, idx); - if (qc && likely(sta_id != IL_INVALID_STATION)) - il4965_free_tfds_in_queue(il, sta_id, tid, freed); - else if (sta_id == IL_INVALID_STATION) - D_TX_REPLY("Station not known\n"); - - if (il->mac80211_registered && - il_queue_space(&txq->q) > txq->q.low_mark) - il_wake_queue(il, txq); - } - if (qc && likely(sta_id != IL_INVALID_STATION)) - il4965_txq_check_empty(il, sta_id, tid, txq_id); - - il4965_check_abort_status(il, tx_resp->frame_count, status); - - spin_unlock_irqrestore(&il->sta_lock, flags); -} - -/** - * translate ucode response to mac80211 tx status control values - */ -void -il4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags, - struct ieee80211_tx_info *info) -{ - struct ieee80211_tx_rate *r = &info->status.rates[0]; - - info->status.antenna = - ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); - if (rate_n_flags & RATE_MCS_HT_MSK) - r->flags |= IEEE80211_TX_RC_MCS; - if (rate_n_flags & RATE_MCS_GF_MSK) - r->flags |= IEEE80211_TX_RC_GREEN_FIELD; - if (rate_n_flags & RATE_MCS_HT40_MSK) - r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - if (rate_n_flags & RATE_MCS_DUP_MSK) - r->flags |= IEEE80211_TX_RC_DUP_DATA; - if (rate_n_flags & RATE_MCS_SGI_MSK) - r->flags |= IEEE80211_TX_RC_SHORT_GI; - r->idx = il4965_hwrate_to_mac80211_idx(rate_n_flags, info->band); -} - -/** - * il4965_hdl_compressed_ba - Handler for N_COMPRESSED_BA - * - * Handles block-acknowledge notification from device, which reports success - * of frames sent via aggregation. - */ -static void -il4965_hdl_compressed_ba(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; - struct il_tx_queue *txq = NULL; - struct il_ht_agg *agg; - int idx; - int sta_id; - int tid; - unsigned long flags; - - /* "flow" corresponds to Tx queue */ - u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); - - /* "ssn" is start of block-ack Tx win, corresponds to idx - * (in Tx queue's circular buffer) of first TFD/frame in win */ - u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); - - if (scd_flow >= il->hw_params.max_txq_num) { - IL_ERR("BUG_ON scd_flow is bigger than number of queues\n"); - return; - } - - txq = &il->txq[scd_flow]; - sta_id = ba_resp->sta_id; - tid = ba_resp->tid; - agg = &il->stations[sta_id].tid[tid].agg; - if (unlikely(agg->txq_id != scd_flow)) { - /* - * FIXME: this is a uCode bug which need to be addressed, - * log the information and return for now! - * since it is possible happen very often and in order - * not to fill the syslog, don't enable the logging by default - */ - D_TX_REPLY("BA scd_flow %d does not match txq_id %d\n", - scd_flow, agg->txq_id); - return; - } - - /* Find idx just before block-ack win */ - idx = il_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd); - - spin_lock_irqsave(&il->sta_lock, flags); - - D_TX_REPLY("N_COMPRESSED_BA [%d] Received from %pM, " "sta_id = %d\n", - agg->wait_for_ba, (u8 *) &ba_resp->sta_addr_lo32, - ba_resp->sta_id); - D_TX_REPLY("TID = %d, SeqCtl = %d, bitmap = 0x%llx," "scd_flow = " - "%d, scd_ssn = %d\n", ba_resp->tid, ba_resp->seq_ctl, - (unsigned long long)le64_to_cpu(ba_resp->bitmap), - ba_resp->scd_flow, ba_resp->scd_ssn); - D_TX_REPLY("DAT start_idx = %d, bitmap = 0x%llx\n", agg->start_idx, - (unsigned long long)agg->bitmap); - - /* Update driver's record of ACK vs. not for each frame in win */ - il4965_tx_status_reply_compressed_ba(il, agg, ba_resp); - - /* Release all TFDs before the SSN, i.e. all TFDs in front of - * block-ack win (we assume that they've been successfully - * transmitted ... if not, it's too late anyway). */ - if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) { - /* calculate mac80211 ampdu sw queue to wake */ - int freed = il4965_tx_queue_reclaim(il, scd_flow, idx); - il4965_free_tfds_in_queue(il, sta_id, tid, freed); - - if (il_queue_space(&txq->q) > txq->q.low_mark && - il->mac80211_registered && - agg->state != IL_EMPTYING_HW_QUEUE_DELBA) - il_wake_queue(il, txq); - - il4965_txq_check_empty(il, sta_id, tid, scd_flow); - } - - spin_unlock_irqrestore(&il->sta_lock, flags); -} - -#ifdef CONFIG_IWLEGACY_DEBUG -const char * -il4965_get_tx_fail_reason(u32 status) -{ -#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x -#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x - - switch (status & TX_STATUS_MSK) { - case TX_STATUS_SUCCESS: - return "SUCCESS"; - TX_STATUS_POSTPONE(DELAY); - TX_STATUS_POSTPONE(FEW_BYTES); - TX_STATUS_POSTPONE(QUIET_PERIOD); - TX_STATUS_POSTPONE(CALC_TTAK); - TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); - TX_STATUS_FAIL(SHORT_LIMIT); - TX_STATUS_FAIL(LONG_LIMIT); - TX_STATUS_FAIL(FIFO_UNDERRUN); - TX_STATUS_FAIL(DRAIN_FLOW); - TX_STATUS_FAIL(RFKILL_FLUSH); - TX_STATUS_FAIL(LIFE_EXPIRE); - TX_STATUS_FAIL(DEST_PS); - TX_STATUS_FAIL(HOST_ABORTED); - TX_STATUS_FAIL(BT_RETRY); - TX_STATUS_FAIL(STA_INVALID); - TX_STATUS_FAIL(FRAG_DROPPED); - TX_STATUS_FAIL(TID_DISABLE); - TX_STATUS_FAIL(FIFO_FLUSHED); - TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); - TX_STATUS_FAIL(PASSIVE_NO_RX); - TX_STATUS_FAIL(NO_BEACON_ON_RADAR); - } - - return "UNKNOWN"; - -#undef TX_STATUS_FAIL -#undef TX_STATUS_POSTPONE -} -#endif /* CONFIG_IWLEGACY_DEBUG */ - -static struct il_link_quality_cmd * -il4965_sta_alloc_lq(struct il_priv *il, u8 sta_id) -{ - int i, r; - struct il_link_quality_cmd *link_cmd; - u32 rate_flags = 0; - __le32 rate_n_flags; - - link_cmd = kzalloc(sizeof(struct il_link_quality_cmd), GFP_KERNEL); - if (!link_cmd) { - IL_ERR("Unable to allocate memory for LQ cmd.\n"); - return NULL; - } - /* Set up the rate scaling to start at selected rate, fall back - * all the way down to 1M in IEEE order, and then spin on 1M */ - if (il->band == IEEE80211_BAND_5GHZ) - r = RATE_6M_IDX; - else - r = RATE_1M_IDX; - - if (r >= IL_FIRST_CCK_RATE && r <= IL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; - - rate_flags |= - il4965_first_antenna(il->hw_params. - valid_tx_ant) << RATE_MCS_ANT_POS; - rate_n_flags = cpu_to_le32(il_rates[r].plcp | rate_flags); - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) - link_cmd->rs_table[i].rate_n_flags = rate_n_flags; - - link_cmd->general_params.single_stream_ant_msk = - il4965_first_antenna(il->hw_params.valid_tx_ant); - - link_cmd->general_params.dual_stream_ant_msk = - il->hw_params.valid_tx_ant & ~il4965_first_antenna(il->hw_params. - valid_tx_ant); - if (!link_cmd->general_params.dual_stream_ant_msk) { - link_cmd->general_params.dual_stream_ant_msk = ANT_AB; - } else if (il4965_num_of_ant(il->hw_params.valid_tx_ant) == 2) { - link_cmd->general_params.dual_stream_ant_msk = - il->hw_params.valid_tx_ant; - } - - link_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; - link_cmd->agg_params.agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - - link_cmd->sta_id = sta_id; - - return link_cmd; -} - -/* - * il4965_add_bssid_station - Add the special IBSS BSSID station - * - * Function sleeps. - */ -int -il4965_add_bssid_station(struct il_priv *il, const u8 *addr, u8 *sta_id_r) -{ - int ret; - u8 sta_id; - struct il_link_quality_cmd *link_cmd; - unsigned long flags; - - if (sta_id_r) - *sta_id_r = IL_INVALID_STATION; - - ret = il_add_station_common(il, addr, 0, NULL, &sta_id); - if (ret) { - IL_ERR("Unable to add station %pM\n", addr); - return ret; - } - - if (sta_id_r) - *sta_id_r = sta_id; - - spin_lock_irqsave(&il->sta_lock, flags); - il->stations[sta_id].used |= IL_STA_LOCAL; - spin_unlock_irqrestore(&il->sta_lock, flags); - - /* Set up default rate scaling table in device's station table */ - link_cmd = il4965_sta_alloc_lq(il, sta_id); - if (!link_cmd) { - IL_ERR("Unable to initialize rate scaling for station %pM.\n", - addr); - return -ENOMEM; - } - - ret = il_send_lq_cmd(il, link_cmd, CMD_SYNC, true); - if (ret) - IL_ERR("Link quality command failed (%d)\n", ret); - - spin_lock_irqsave(&il->sta_lock, flags); - il->stations[sta_id].lq = link_cmd; - spin_unlock_irqrestore(&il->sta_lock, flags); - - return 0; -} - -static int -il4965_static_wepkey_cmd(struct il_priv *il, bool send_if_empty) -{ - int i; - u8 buff[sizeof(struct il_wep_cmd) + - sizeof(struct il_wep_key) * WEP_KEYS_MAX]; - struct il_wep_cmd *wep_cmd = (struct il_wep_cmd *)buff; - size_t cmd_size = sizeof(struct il_wep_cmd); - struct il_host_cmd cmd = { - .id = C_WEPKEY, - .data = wep_cmd, - .flags = CMD_SYNC, - }; - bool not_empty = false; - - might_sleep(); - - memset(wep_cmd, 0, - cmd_size + (sizeof(struct il_wep_key) * WEP_KEYS_MAX)); - - for (i = 0; i < WEP_KEYS_MAX; i++) { - u8 key_size = il->_4965.wep_keys[i].key_size; - - wep_cmd->key[i].key_idx = i; - if (key_size) { - wep_cmd->key[i].key_offset = i; - not_empty = true; - } else - wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; - - wep_cmd->key[i].key_size = key_size; - memcpy(&wep_cmd->key[i].key[3], il->_4965.wep_keys[i].key, key_size); - } - - wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; - wep_cmd->num_keys = WEP_KEYS_MAX; - - cmd_size += sizeof(struct il_wep_key) * WEP_KEYS_MAX; - cmd.len = cmd_size; - - if (not_empty || send_if_empty) - return il_send_cmd(il, &cmd); - else - return 0; -} - -int -il4965_restore_default_wep_keys(struct il_priv *il) -{ - lockdep_assert_held(&il->mutex); - - return il4965_static_wepkey_cmd(il, false); -} - -int -il4965_remove_default_wep_key(struct il_priv *il, - struct ieee80211_key_conf *keyconf) -{ - int ret; - int idx = keyconf->keyidx; - - lockdep_assert_held(&il->mutex); - - D_WEP("Removing default WEP key: idx=%d\n", idx); - - memset(&il->_4965.wep_keys[idx], 0, sizeof(struct il_wep_key)); - if (il_is_rfkill(il)) { - D_WEP("Not sending C_WEPKEY command due to RFKILL.\n"); - /* but keys in device are clear anyway so return success */ - return 0; - } - ret = il4965_static_wepkey_cmd(il, 1); - D_WEP("Remove default WEP key: idx=%d ret=%d\n", idx, ret); - - return ret; -} - -int -il4965_set_default_wep_key(struct il_priv *il, - struct ieee80211_key_conf *keyconf) -{ - int ret; - int len = keyconf->keylen; - int idx = keyconf->keyidx; - - lockdep_assert_held(&il->mutex); - - if (len != WEP_KEY_LEN_128 && len != WEP_KEY_LEN_64) { - D_WEP("Bad WEP key length %d\n", keyconf->keylen); - return -EINVAL; - } - - keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; - keyconf->hw_key_idx = HW_KEY_DEFAULT; - il->stations[IL_AP_ID].keyinfo.cipher = keyconf->cipher; - - il->_4965.wep_keys[idx].key_size = len; - memcpy(&il->_4965.wep_keys[idx].key, &keyconf->key, len); - - ret = il4965_static_wepkey_cmd(il, false); - - D_WEP("Set default WEP key: len=%d idx=%d ret=%d\n", len, idx, ret); - return ret; -} - -static int -il4965_set_wep_dynamic_key_info(struct il_priv *il, - struct ieee80211_key_conf *keyconf, u8 sta_id) -{ - unsigned long flags; - __le16 key_flags = 0; - struct il_addsta_cmd sta_cmd; - - lockdep_assert_held(&il->mutex); - - keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; - - key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK); - key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); - key_flags &= ~STA_KEY_FLG_INVALID; - - if (keyconf->keylen == WEP_KEY_LEN_128) - key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; - - if (sta_id == il->hw_params.bcast_id) - key_flags |= STA_KEY_MULTICAST_MSK; - - spin_lock_irqsave(&il->sta_lock, flags); - - il->stations[sta_id].keyinfo.cipher = keyconf->cipher; - il->stations[sta_id].keyinfo.keylen = keyconf->keylen; - il->stations[sta_id].keyinfo.keyidx = keyconf->keyidx; - - memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); - - memcpy(&il->stations[sta_id].sta.key.key[3], keyconf->key, - keyconf->keylen); - - if ((il->stations[sta_id].sta.key. - key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) - il->stations[sta_id].sta.key.key_offset = - il_get_free_ucode_key_idx(il); - /* else, we are overriding an existing key => no need to allocated room - * in uCode. */ - - WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, - "no space for a new key"); - - il->stations[sta_id].sta.key.key_flags = key_flags; - il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; - il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - - memcpy(&sta_cmd, &il->stations[sta_id].sta, - sizeof(struct il_addsta_cmd)); - spin_unlock_irqrestore(&il->sta_lock, flags); - - return il_send_add_sta(il, &sta_cmd, CMD_SYNC); -} - -static int -il4965_set_ccmp_dynamic_key_info(struct il_priv *il, - struct ieee80211_key_conf *keyconf, u8 sta_id) -{ - unsigned long flags; - __le16 key_flags = 0; - struct il_addsta_cmd sta_cmd; - - lockdep_assert_held(&il->mutex); - - key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK); - key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); - key_flags &= ~STA_KEY_FLG_INVALID; - - if (sta_id == il->hw_params.bcast_id) - key_flags |= STA_KEY_MULTICAST_MSK; - - keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - - spin_lock_irqsave(&il->sta_lock, flags); - il->stations[sta_id].keyinfo.cipher = keyconf->cipher; - il->stations[sta_id].keyinfo.keylen = keyconf->keylen; - - memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, keyconf->keylen); - - memcpy(il->stations[sta_id].sta.key.key, keyconf->key, keyconf->keylen); - - if ((il->stations[sta_id].sta.key. - key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) - il->stations[sta_id].sta.key.key_offset = - il_get_free_ucode_key_idx(il); - /* else, we are overriding an existing key => no need to allocated room - * in uCode. */ - - WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, - "no space for a new key"); - - il->stations[sta_id].sta.key.key_flags = key_flags; - il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; - il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - - memcpy(&sta_cmd, &il->stations[sta_id].sta, - sizeof(struct il_addsta_cmd)); - spin_unlock_irqrestore(&il->sta_lock, flags); - - return il_send_add_sta(il, &sta_cmd, CMD_SYNC); -} - -static int -il4965_set_tkip_dynamic_key_info(struct il_priv *il, - struct ieee80211_key_conf *keyconf, u8 sta_id) -{ - unsigned long flags; - int ret = 0; - __le16 key_flags = 0; - - key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK); - key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); - key_flags &= ~STA_KEY_FLG_INVALID; - - if (sta_id == il->hw_params.bcast_id) - key_flags |= STA_KEY_MULTICAST_MSK; - - keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - - spin_lock_irqsave(&il->sta_lock, flags); - - il->stations[sta_id].keyinfo.cipher = keyconf->cipher; - il->stations[sta_id].keyinfo.keylen = 16; - - if ((il->stations[sta_id].sta.key. - key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) - il->stations[sta_id].sta.key.key_offset = - il_get_free_ucode_key_idx(il); - /* else, we are overriding an existing key => no need to allocated room - * in uCode. */ - - WARN(il->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, - "no space for a new key"); - - il->stations[sta_id].sta.key.key_flags = key_flags; - - /* This copy is acutally not needed: we get the key with each TX */ - memcpy(il->stations[sta_id].keyinfo.key, keyconf->key, 16); - - memcpy(il->stations[sta_id].sta.key.key, keyconf->key, 16); - - spin_unlock_irqrestore(&il->sta_lock, flags); - - return ret; -} - -void -il4965_update_tkip_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) -{ - u8 sta_id; - unsigned long flags; - int i; - - if (il_scan_cancel(il)) { - /* cancel scan failed, just live w/ bad key and rely - briefly on SW decryption */ - return; - } - - sta_id = il_sta_id_or_broadcast(il, sta); - if (sta_id == IL_INVALID_STATION) - return; - - spin_lock_irqsave(&il->sta_lock, flags); - - il->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32; - - for (i = 0; i < 5; i++) - il->stations[sta_id].sta.key.tkip_rx_ttak[i] = - cpu_to_le16(phase1key[i]); - - il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; - il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - - il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC); - - spin_unlock_irqrestore(&il->sta_lock, flags); -} - -int -il4965_remove_dynamic_key(struct il_priv *il, - struct ieee80211_key_conf *keyconf, u8 sta_id) -{ - unsigned long flags; - u16 key_flags; - u8 keyidx; - struct il_addsta_cmd sta_cmd; - - lockdep_assert_held(&il->mutex); - - il->_4965.key_mapping_keys--; - - spin_lock_irqsave(&il->sta_lock, flags); - key_flags = le16_to_cpu(il->stations[sta_id].sta.key.key_flags); - keyidx = (key_flags >> STA_KEY_FLG_KEYID_POS) & 0x3; - - D_WEP("Remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id); - - if (keyconf->keyidx != keyidx) { - /* We need to remove a key with idx different that the one - * in the uCode. This means that the key we need to remove has - * been replaced by another one with different idx. - * Don't do anything and return ok - */ - spin_unlock_irqrestore(&il->sta_lock, flags); - return 0; - } - - if (il->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_INVALID) { - IL_WARN("Removing wrong key %d 0x%x\n", keyconf->keyidx, - key_flags); - spin_unlock_irqrestore(&il->sta_lock, flags); - return 0; - } - - if (!test_and_clear_bit - (il->stations[sta_id].sta.key.key_offset, &il->ucode_key_table)) - IL_ERR("idx %d not used in uCode key table.\n", - il->stations[sta_id].sta.key.key_offset); - memset(&il->stations[sta_id].keyinfo, 0, sizeof(struct il_hw_key)); - memset(&il->stations[sta_id].sta.key, 0, sizeof(struct il4965_keyinfo)); - il->stations[sta_id].sta.key.key_flags = - STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID; - il->stations[sta_id].sta.key.key_offset = keyconf->hw_key_idx; - il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; - il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - - if (il_is_rfkill(il)) { - D_WEP - ("Not sending C_ADD_STA command because RFKILL enabled.\n"); - spin_unlock_irqrestore(&il->sta_lock, flags); - return 0; - } - memcpy(&sta_cmd, &il->stations[sta_id].sta, - sizeof(struct il_addsta_cmd)); - spin_unlock_irqrestore(&il->sta_lock, flags); - - return il_send_add_sta(il, &sta_cmd, CMD_SYNC); -} - -int -il4965_set_dynamic_key(struct il_priv *il, struct ieee80211_key_conf *keyconf, - u8 sta_id) -{ - int ret; - - lockdep_assert_held(&il->mutex); - - il->_4965.key_mapping_keys++; - keyconf->hw_key_idx = HW_KEY_DYNAMIC; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - ret = - il4965_set_ccmp_dynamic_key_info(il, keyconf, sta_id); - break; - case WLAN_CIPHER_SUITE_TKIP: - ret = - il4965_set_tkip_dynamic_key_info(il, keyconf, sta_id); - break; - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - ret = il4965_set_wep_dynamic_key_info(il, keyconf, sta_id); - break; - default: - IL_ERR("Unknown alg: %s cipher = %x\n", __func__, - keyconf->cipher); - ret = -EINVAL; - } - - D_WEP("Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n", - keyconf->cipher, keyconf->keylen, keyconf->keyidx, sta_id, ret); - - return ret; -} - -/** - * il4965_alloc_bcast_station - add broadcast station into driver's station table. - * - * This adds the broadcast station into the driver's station table - * and marks it driver active, so that it will be restored to the - * device at the next best time. - */ -int -il4965_alloc_bcast_station(struct il_priv *il) -{ - struct il_link_quality_cmd *link_cmd; - unsigned long flags; - u8 sta_id; - - spin_lock_irqsave(&il->sta_lock, flags); - sta_id = il_prep_station(il, il_bcast_addr, false, NULL); - if (sta_id == IL_INVALID_STATION) { - IL_ERR("Unable to prepare broadcast station\n"); - spin_unlock_irqrestore(&il->sta_lock, flags); - - return -EINVAL; - } - - il->stations[sta_id].used |= IL_STA_DRIVER_ACTIVE; - il->stations[sta_id].used |= IL_STA_BCAST; - spin_unlock_irqrestore(&il->sta_lock, flags); - - link_cmd = il4965_sta_alloc_lq(il, sta_id); - if (!link_cmd) { - IL_ERR - ("Unable to initialize rate scaling for bcast station.\n"); - return -ENOMEM; - } - - spin_lock_irqsave(&il->sta_lock, flags); - il->stations[sta_id].lq = link_cmd; - spin_unlock_irqrestore(&il->sta_lock, flags); - - return 0; -} - -/** - * il4965_update_bcast_station - update broadcast station's LQ command - * - * Only used by iwl4965. Placed here to have all bcast station management - * code together. - */ -static int -il4965_update_bcast_station(struct il_priv *il) -{ - unsigned long flags; - struct il_link_quality_cmd *link_cmd; - u8 sta_id = il->hw_params.bcast_id; - - link_cmd = il4965_sta_alloc_lq(il, sta_id); - if (!link_cmd) { - IL_ERR("Unable to initialize rate scaling for bcast sta.\n"); - return -ENOMEM; - } - - spin_lock_irqsave(&il->sta_lock, flags); - if (il->stations[sta_id].lq) - kfree(il->stations[sta_id].lq); - else - D_INFO("Bcast sta rate scaling has not been initialized.\n"); - il->stations[sta_id].lq = link_cmd; - spin_unlock_irqrestore(&il->sta_lock, flags); - - return 0; -} - -int -il4965_update_bcast_stations(struct il_priv *il) -{ - return il4965_update_bcast_station(il); -} - -/** - * il4965_sta_tx_modify_enable_tid - Enable Tx for this TID in station table - */ -int -il4965_sta_tx_modify_enable_tid(struct il_priv *il, int sta_id, int tid) -{ - unsigned long flags; - struct il_addsta_cmd sta_cmd; - - lockdep_assert_held(&il->mutex); - - /* Remove "disable" flag, to enable Tx for this TID */ - spin_lock_irqsave(&il->sta_lock, flags); - il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; - il->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); - il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - memcpy(&sta_cmd, &il->stations[sta_id].sta, - sizeof(struct il_addsta_cmd)); - spin_unlock_irqrestore(&il->sta_lock, flags); - - return il_send_add_sta(il, &sta_cmd, CMD_SYNC); -} - -int -il4965_sta_rx_agg_start(struct il_priv *il, struct ieee80211_sta *sta, int tid, - u16 ssn) -{ - unsigned long flags; - int sta_id; - struct il_addsta_cmd sta_cmd; - - lockdep_assert_held(&il->mutex); - - sta_id = il_sta_id(sta); - if (sta_id == IL_INVALID_STATION) - return -ENXIO; - - spin_lock_irqsave(&il->sta_lock, flags); - il->stations[sta_id].sta.station_flags_msk = 0; - il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; - il->stations[sta_id].sta.add_immediate_ba_tid = (u8) tid; - il->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); - il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - memcpy(&sta_cmd, &il->stations[sta_id].sta, - sizeof(struct il_addsta_cmd)); - spin_unlock_irqrestore(&il->sta_lock, flags); - - return il_send_add_sta(il, &sta_cmd, CMD_SYNC); -} - -int -il4965_sta_rx_agg_stop(struct il_priv *il, struct ieee80211_sta *sta, int tid) -{ - unsigned long flags; - int sta_id; - struct il_addsta_cmd sta_cmd; - - lockdep_assert_held(&il->mutex); - - sta_id = il_sta_id(sta); - if (sta_id == IL_INVALID_STATION) { - IL_ERR("Invalid station for AGG tid %d\n", tid); - return -ENXIO; - } - - spin_lock_irqsave(&il->sta_lock, flags); - il->stations[sta_id].sta.station_flags_msk = 0; - il->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; - il->stations[sta_id].sta.remove_immediate_ba_tid = (u8) tid; - il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - memcpy(&sta_cmd, &il->stations[sta_id].sta, - sizeof(struct il_addsta_cmd)); - spin_unlock_irqrestore(&il->sta_lock, flags); - - return il_send_add_sta(il, &sta_cmd, CMD_SYNC); -} - -void -il4965_sta_modify_sleep_tx_count(struct il_priv *il, int sta_id, int cnt) -{ - unsigned long flags; - - spin_lock_irqsave(&il->sta_lock, flags); - il->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK; - il->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; - il->stations[sta_id].sta.sta.modify_mask = - STA_MODIFY_SLEEP_TX_COUNT_MSK; - il->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt); - il->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - il_send_add_sta(il, &il->stations[sta_id].sta, CMD_ASYNC); - spin_unlock_irqrestore(&il->sta_lock, flags); - -} - -void -il4965_update_chain_flags(struct il_priv *il) -{ - if (il->ops->set_rxon_chain) { - il->ops->set_rxon_chain(il); - if (il->active.rx_chain != il->staging.rx_chain) - il_commit_rxon(il); - } -} - -static void -il4965_clear_free_frames(struct il_priv *il) -{ - struct list_head *element; - - D_INFO("%d frames on pre-allocated heap on clear.\n", il->frames_count); - - while (!list_empty(&il->free_frames)) { - element = il->free_frames.next; - list_del(element); - kfree(list_entry(element, struct il_frame, list)); - il->frames_count--; - } - - if (il->frames_count) { - IL_WARN("%d frames still in use. Did we lose one?\n", - il->frames_count); - il->frames_count = 0; - } -} - -static struct il_frame * -il4965_get_free_frame(struct il_priv *il) -{ - struct il_frame *frame; - struct list_head *element; - if (list_empty(&il->free_frames)) { - frame = kzalloc(sizeof(*frame), GFP_KERNEL); - if (!frame) { - IL_ERR("Could not allocate frame!\n"); - return NULL; - } - - il->frames_count++; - return frame; - } - - element = il->free_frames.next; - list_del(element); - return list_entry(element, struct il_frame, list); -} - -static void -il4965_free_frame(struct il_priv *il, struct il_frame *frame) -{ - memset(frame, 0, sizeof(*frame)); - list_add(&frame->list, &il->free_frames); -} - -static u32 -il4965_fill_beacon_frame(struct il_priv *il, struct ieee80211_hdr *hdr, - int left) -{ - lockdep_assert_held(&il->mutex); - - if (!il->beacon_skb) - return 0; - - if (il->beacon_skb->len > left) - return 0; - - memcpy(hdr, il->beacon_skb->data, il->beacon_skb->len); - - return il->beacon_skb->len; -} - -/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ -static void -il4965_set_beacon_tim(struct il_priv *il, - struct il_tx_beacon_cmd *tx_beacon_cmd, u8 * beacon, - u32 frame_size) -{ - u16 tim_idx; - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; - - /* - * The idx is relative to frame start but we start looking at the - * variable-length part of the beacon. - */ - tim_idx = mgmt->u.beacon.variable - beacon; - - /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ - while ((tim_idx < (frame_size - 2)) && - (beacon[tim_idx] != WLAN_EID_TIM)) - tim_idx += beacon[tim_idx + 1] + 2; - - /* If TIM field was found, set variables */ - if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { - tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); - tx_beacon_cmd->tim_size = beacon[tim_idx + 1]; - } else - IL_WARN("Unable to find TIM Element in beacon\n"); -} - -static unsigned int -il4965_hw_get_beacon_cmd(struct il_priv *il, struct il_frame *frame) -{ - struct il_tx_beacon_cmd *tx_beacon_cmd; - u32 frame_size; - u32 rate_flags; - u32 rate; - /* - * We have to set up the TX command, the TX Beacon command, and the - * beacon contents. - */ - - lockdep_assert_held(&il->mutex); - - if (!il->beacon_enabled) { - IL_ERR("Trying to build beacon without beaconing enabled\n"); - return 0; - } - - /* Initialize memory */ - tx_beacon_cmd = &frame->u.beacon; - memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); - - /* Set up TX beacon contents */ - frame_size = - il4965_fill_beacon_frame(il, tx_beacon_cmd->frame, - sizeof(frame->u) - sizeof(*tx_beacon_cmd)); - if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE)) - return 0; - if (!frame_size) - return 0; - - /* Set up TX command fields */ - tx_beacon_cmd->tx.len = cpu_to_le16((u16) frame_size); - tx_beacon_cmd->tx.sta_id = il->hw_params.bcast_id; - tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - tx_beacon_cmd->tx.tx_flags = - TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK | - TX_CMD_FLG_STA_RATE_MSK; - - /* Set up TX beacon command fields */ - il4965_set_beacon_tim(il, tx_beacon_cmd, (u8 *) tx_beacon_cmd->frame, - frame_size); - - /* Set up packet rate and flags */ - rate = il_get_lowest_plcp(il); - il4965_toggle_tx_ant(il, &il->mgmt_tx_ant, il->hw_params.valid_tx_ant); - rate_flags = BIT(il->mgmt_tx_ant) << RATE_MCS_ANT_POS; - if ((rate >= IL_FIRST_CCK_RATE) && (rate <= IL_LAST_CCK_RATE)) - rate_flags |= RATE_MCS_CCK_MSK; - tx_beacon_cmd->tx.rate_n_flags = cpu_to_le32(rate | rate_flags); - - return sizeof(*tx_beacon_cmd) + frame_size; -} - -int -il4965_send_beacon_cmd(struct il_priv *il) -{ - struct il_frame *frame; - unsigned int frame_size; - int rc; - - frame = il4965_get_free_frame(il); - if (!frame) { - IL_ERR("Could not obtain free frame buffer for beacon " - "command.\n"); - return -ENOMEM; - } - - frame_size = il4965_hw_get_beacon_cmd(il, frame); - if (!frame_size) { - IL_ERR("Error configuring the beacon command\n"); - il4965_free_frame(il, frame); - return -EINVAL; - } - - rc = il_send_cmd_pdu(il, C_TX_BEACON, frame_size, &frame->u.cmd[0]); - - il4965_free_frame(il, frame); - - return rc; -} - -static inline dma_addr_t -il4965_tfd_tb_get_addr(struct il_tfd *tfd, u8 idx) -{ - struct il_tfd_tb *tb = &tfd->tbs[idx]; - - dma_addr_t addr = get_unaligned_le32(&tb->lo); - if (sizeof(dma_addr_t) > sizeof(u32)) - addr |= - ((dma_addr_t) (le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << - 16; - - return addr; -} - -static inline u16 -il4965_tfd_tb_get_len(struct il_tfd *tfd, u8 idx) -{ - struct il_tfd_tb *tb = &tfd->tbs[idx]; - - return le16_to_cpu(tb->hi_n_len) >> 4; -} - -static inline void -il4965_tfd_set_tb(struct il_tfd *tfd, u8 idx, dma_addr_t addr, u16 len) -{ - struct il_tfd_tb *tb = &tfd->tbs[idx]; - u16 hi_n_len = len << 4; - - put_unaligned_le32(addr, &tb->lo); - if (sizeof(dma_addr_t) > sizeof(u32)) - hi_n_len |= ((addr >> 16) >> 16) & 0xF; - - tb->hi_n_len = cpu_to_le16(hi_n_len); - - tfd->num_tbs = idx + 1; -} - -static inline u8 -il4965_tfd_get_num_tbs(struct il_tfd *tfd) -{ - return tfd->num_tbs & 0x1f; -} - -/** - * il4965_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] - * @il - driver ilate data - * @txq - tx queue - * - * Does NOT advance any TFD circular buffer read/write idxes - * Does NOT free the TFD itself (which is within circular buffer) - */ -void -il4965_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq) -{ - struct il_tfd *tfd_tmp = (struct il_tfd *)txq->tfds; - struct il_tfd *tfd; - struct pci_dev *dev = il->pci_dev; - int idx = txq->q.read_ptr; - int i; - int num_tbs; - - tfd = &tfd_tmp[idx]; - - /* Sanity check on number of chunks */ - num_tbs = il4965_tfd_get_num_tbs(tfd); - - if (num_tbs >= IL_NUM_OF_TBS) { - IL_ERR("Too many chunks: %i\n", num_tbs); - /* @todo issue fatal error, it is quite serious situation */ - return; - } - - /* Unmap tx_cmd */ - if (num_tbs) - pci_unmap_single(dev, dma_unmap_addr(&txq->meta[idx], mapping), - dma_unmap_len(&txq->meta[idx], len), - PCI_DMA_BIDIRECTIONAL); - - /* Unmap chunks, if any. */ - for (i = 1; i < num_tbs; i++) - pci_unmap_single(dev, il4965_tfd_tb_get_addr(tfd, i), - il4965_tfd_tb_get_len(tfd, i), - PCI_DMA_TODEVICE); - - /* free SKB */ - if (txq->skbs) { - struct sk_buff *skb = txq->skbs[txq->q.read_ptr]; - - /* can be called from irqs-disabled context */ - if (skb) { - dev_kfree_skb_any(skb); - txq->skbs[txq->q.read_ptr] = NULL; - } - } -} - -int -il4965_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, - dma_addr_t addr, u16 len, u8 reset, u8 pad) -{ - struct il_queue *q; - struct il_tfd *tfd, *tfd_tmp; - u32 num_tbs; - - q = &txq->q; - tfd_tmp = (struct il_tfd *)txq->tfds; - tfd = &tfd_tmp[q->write_ptr]; - - if (reset) - memset(tfd, 0, sizeof(*tfd)); - - num_tbs = il4965_tfd_get_num_tbs(tfd); - - /* Each TFD can point to a maximum 20 Tx buffers */ - if (num_tbs >= IL_NUM_OF_TBS) { - IL_ERR("Error can not send more than %d chunks\n", - IL_NUM_OF_TBS); - return -EINVAL; - } - - BUG_ON(addr & ~DMA_BIT_MASK(36)); - if (unlikely(addr & ~IL_TX_DMA_MASK)) - IL_ERR("Unaligned address = %llx\n", (unsigned long long)addr); - - il4965_tfd_set_tb(tfd, num_tbs, addr, len); - - return 0; -} - -/* - * Tell nic where to find circular buffer of Tx Frame Descriptors for - * given Tx queue, and enable the DMA channel used for that queue. - * - * 4965 supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA - * channels supported in hardware. - */ -int -il4965_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq) -{ - int txq_id = txq->q.id; - - /* Circular buffer (TFD queue in DRAM) physical base address */ - il_wr(il, FH49_MEM_CBBC_QUEUE(txq_id), txq->q.dma_addr >> 8); - - return 0; -} - -/****************************************************************************** - * - * Generic RX handler implementations - * - ******************************************************************************/ -static void -il4965_hdl_alive(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_alive_resp *palive; - struct delayed_work *pwork; - - palive = &pkt->u.alive_frame; - - D_INFO("Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n", - palive->is_valid, palive->ver_type, palive->ver_subtype); - - if (palive->ver_subtype == INITIALIZE_SUBTYPE) { - D_INFO("Initialization Alive received.\n"); - memcpy(&il->card_alive_init, &pkt->u.alive_frame, - sizeof(struct il_init_alive_resp)); - pwork = &il->init_alive_start; - } else { - D_INFO("Runtime Alive received.\n"); - memcpy(&il->card_alive, &pkt->u.alive_frame, - sizeof(struct il_alive_resp)); - pwork = &il->alive_start; - } - - /* We delay the ALIVE response by 5ms to - * give the HW RF Kill time to activate... */ - if (palive->is_valid == UCODE_VALID_OK) - queue_delayed_work(il->workqueue, pwork, msecs_to_jiffies(5)); - else - IL_WARN("uCode did not respond OK.\n"); -} - -/** - * il4965_bg_stats_periodic - Timer callback to queue stats - * - * This callback is provided in order to send a stats request. - * - * This timer function is continually reset to execute within - * 60 seconds since the last N_STATS was received. We need to - * ensure we receive the stats in order to update the temperature - * used for calibrating the TXPOWER. - */ -static void -il4965_bg_stats_periodic(unsigned long data) -{ - struct il_priv *il = (struct il_priv *)data; - - if (test_bit(S_EXIT_PENDING, &il->status)) - return; - - /* dont send host command if rf-kill is on */ - if (!il_is_ready_rf(il)) - return; - - il_send_stats_request(il, CMD_ASYNC, false); -} - -static void -il4965_hdl_beacon(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il4965_beacon_notif *beacon = - (struct il4965_beacon_notif *)pkt->u.raw; -#ifdef CONFIG_IWLEGACY_DEBUG - u8 rate = il4965_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); - - D_RX("beacon status %x retries %d iss %d tsf:0x%.8x%.8x rate %d\n", - le32_to_cpu(beacon->beacon_notify_hdr.u.status) & TX_STATUS_MSK, - beacon->beacon_notify_hdr.failure_frame, - le32_to_cpu(beacon->ibss_mgr_status), - le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate); -#endif - il->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); -} - -static void -il4965_perform_ct_kill_task(struct il_priv *il) -{ - unsigned long flags; - - D_POWER("Stop all queues\n"); - - if (il->mac80211_registered) - ieee80211_stop_queues(il->hw); - - _il_wr(il, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); - _il_rd(il, CSR_UCODE_DRV_GP1); - - spin_lock_irqsave(&il->reg_lock, flags); - if (likely(_il_grab_nic_access(il))) - _il_release_nic_access(il); - spin_unlock_irqrestore(&il->reg_lock, flags); -} - -/* Handle notification from uCode that card's power state is changing - * due to software, hardware, or critical temperature RFKILL */ -static void -il4965_hdl_card_state(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); - unsigned long status = il->status; - - D_RF_KILL("Card state received: HW:%s SW:%s CT:%s\n", - (flags & HW_CARD_DISABLED) ? "Kill" : "On", - (flags & SW_CARD_DISABLED) ? "Kill" : "On", - (flags & CT_CARD_DISABLED) ? "Reached" : "Not reached"); - - if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | CT_CARD_DISABLED)) { - - _il_wr(il, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - il_wr(il, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); - - if (!(flags & RXON_CARD_DISABLED)) { - _il_wr(il, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - il_wr(il, HBUS_TARG_MBX_C, - HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); - } - } - - if (flags & CT_CARD_DISABLED) - il4965_perform_ct_kill_task(il); - - if (flags & HW_CARD_DISABLED) - set_bit(S_RFKILL, &il->status); - else - clear_bit(S_RFKILL, &il->status); - - if (!(flags & RXON_CARD_DISABLED)) - il_scan_cancel(il); - - if ((test_bit(S_RFKILL, &status) != - test_bit(S_RFKILL, &il->status))) - wiphy_rfkill_set_hw_state(il->hw->wiphy, - test_bit(S_RFKILL, &il->status)); - else - wake_up(&il->wait_command_queue); -} - -/** - * il4965_setup_handlers - Initialize Rx handler callbacks - * - * Setup the RX handlers for each of the reply types sent from the uCode - * to the host. - * - * This function chains into the hardware specific files for them to setup - * any hardware specific handlers as well. - */ -static void -il4965_setup_handlers(struct il_priv *il) -{ - il->handlers[N_ALIVE] = il4965_hdl_alive; - il->handlers[N_ERROR] = il_hdl_error; - il->handlers[N_CHANNEL_SWITCH] = il_hdl_csa; - il->handlers[N_SPECTRUM_MEASUREMENT] = il_hdl_spectrum_measurement; - il->handlers[N_PM_SLEEP] = il_hdl_pm_sleep; - il->handlers[N_PM_DEBUG_STATS] = il_hdl_pm_debug_stats; - il->handlers[N_BEACON] = il4965_hdl_beacon; - - /* - * The same handler is used for both the REPLY to a discrete - * stats request from the host as well as for the periodic - * stats notifications (after received beacons) from the uCode. - */ - il->handlers[C_STATS] = il4965_hdl_c_stats; - il->handlers[N_STATS] = il4965_hdl_stats; - - il_setup_rx_scan_handlers(il); - - /* status change handler */ - il->handlers[N_CARD_STATE] = il4965_hdl_card_state; - - il->handlers[N_MISSED_BEACONS] = il4965_hdl_missed_beacon; - /* Rx handlers */ - il->handlers[N_RX_PHY] = il4965_hdl_rx_phy; - il->handlers[N_RX_MPDU] = il4965_hdl_rx; - il->handlers[N_RX] = il4965_hdl_rx; - /* block ack */ - il->handlers[N_COMPRESSED_BA] = il4965_hdl_compressed_ba; - /* Tx response */ - il->handlers[C_TX] = il4965_hdl_tx; -} - -/** - * il4965_rx_handle - Main entry function for receiving responses from uCode - * - * Uses the il->handlers callback function array to invoke - * the appropriate handlers, including command responses, - * frame-received notifications, and other notifications. - */ -void -il4965_rx_handle(struct il_priv *il) -{ - struct il_rx_buf *rxb; - struct il_rx_pkt *pkt; - struct il_rx_queue *rxq = &il->rxq; - u32 r, i; - int reclaim; - unsigned long flags; - u8 fill_rx = 0; - u32 count = 8; - int total_empty; - - /* uCode's read idx (stored in shared DRAM) indicates the last Rx - * buffer that the driver may process (last buffer filled by ucode). */ - r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF; - i = rxq->read; - - /* Rx interrupt, but nothing sent from uCode */ - if (i == r) - D_RX("r = %d, i = %d\n", r, i); - - /* calculate total frames need to be restock after handling RX */ - total_empty = r - rxq->write_actual; - if (total_empty < 0) - total_empty += RX_QUEUE_SIZE; - - if (total_empty > (RX_QUEUE_SIZE / 2)) - fill_rx = 1; - - while (i != r) { - int len; - - rxb = rxq->queue[i]; - - /* If an RXB doesn't have a Rx queue slot associated with it, - * then a bug has been introduced in the queue refilling - * routines -- catch it here */ - BUG_ON(rxb == NULL); - - rxq->queue[i] = NULL; - - pci_unmap_page(il->pci_dev, rxb->page_dma, - PAGE_SIZE << il->hw_params.rx_page_order, - PCI_DMA_FROMDEVICE); - pkt = rxb_addr(rxb); - - len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; - len += sizeof(u32); /* account for status word */ - - reclaim = il_need_reclaim(il, pkt); - - /* Based on type of command response or notification, - * handle those that need handling via function in - * handlers table. See il4965_setup_handlers() */ - if (il->handlers[pkt->hdr.cmd]) { - D_RX("r = %d, i = %d, %s, 0x%02x\n", r, i, - il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); - il->isr_stats.handlers[pkt->hdr.cmd]++; - il->handlers[pkt->hdr.cmd] (il, rxb); - } else { - /* No handling needed */ - D_RX("r %d i %d No handler needed for %s, 0x%02x\n", r, - i, il_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); - } - - /* - * XXX: After here, we should always check rxb->page - * against NULL before touching it or its virtual - * memory (pkt). Because some handler might have - * already taken or freed the pages. - */ - - if (reclaim) { - /* Invoke any callbacks, transfer the buffer to caller, - * and fire off the (possibly) blocking il_send_cmd() - * as we reclaim the driver command queue */ - if (rxb->page) - il_tx_cmd_complete(il, rxb); - else - IL_WARN("Claim null rxb?\n"); - } - - /* Reuse the page if possible. For notification packets and - * SKBs that fail to Rx correctly, add them back into the - * rx_free list for reuse later. */ - spin_lock_irqsave(&rxq->lock, flags); - if (rxb->page != NULL) { - rxb->page_dma = - pci_map_page(il->pci_dev, rxb->page, 0, - PAGE_SIZE << il->hw_params. - rx_page_order, PCI_DMA_FROMDEVICE); - - if (unlikely(pci_dma_mapping_error(il->pci_dev, - rxb->page_dma))) { - __il_free_pages(il, rxb->page); - rxb->page = NULL; - list_add_tail(&rxb->list, &rxq->rx_used); - } else { - list_add_tail(&rxb->list, &rxq->rx_free); - rxq->free_count++; - } - } else - list_add_tail(&rxb->list, &rxq->rx_used); - - spin_unlock_irqrestore(&rxq->lock, flags); - - i = (i + 1) & RX_QUEUE_MASK; - /* If there are a lot of unused frames, - * restock the Rx queue so ucode wont assert. */ - if (fill_rx) { - count++; - if (count >= 8) { - rxq->read = i; - il4965_rx_replenish_now(il); - count = 0; - } - } - } - - /* Backtrack one entry */ - rxq->read = i; - if (fill_rx) - il4965_rx_replenish_now(il); - else - il4965_rx_queue_restock(il); -} - -/* call this function to flush any scheduled tasklet */ -static inline void -il4965_synchronize_irq(struct il_priv *il) -{ - /* wait to make sure we flush pending tasklet */ - synchronize_irq(il->pci_dev->irq); - tasklet_kill(&il->irq_tasklet); -} - -static void -il4965_irq_tasklet(struct il_priv *il) -{ - u32 inta, handled = 0; - u32 inta_fh; - unsigned long flags; - u32 i; -#ifdef CONFIG_IWLEGACY_DEBUG - u32 inta_mask; -#endif - - spin_lock_irqsave(&il->lock, flags); - - /* Ack/clear/reset pending uCode interrupts. - * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, - * and will clear only when CSR_FH_INT_STATUS gets cleared. */ - inta = _il_rd(il, CSR_INT); - _il_wr(il, CSR_INT, inta); - - /* Ack/clear/reset pending flow-handler (DMA) interrupts. - * Any new interrupts that happen after this, either while we're - * in this tasklet, or later, will show up in next ISR/tasklet. */ - inta_fh = _il_rd(il, CSR_FH_INT_STATUS); - _il_wr(il, CSR_FH_INT_STATUS, inta_fh); - -#ifdef CONFIG_IWLEGACY_DEBUG - if (il_get_debug_level(il) & IL_DL_ISR) { - /* just for debug */ - inta_mask = _il_rd(il, CSR_INT_MASK); - D_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, - inta_mask, inta_fh); - } -#endif - - spin_unlock_irqrestore(&il->lock, flags); - - /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not - * atomic, make sure that inta covers all the interrupts that - * we've discovered, even if FH interrupt came in just after - * reading CSR_INT. */ - if (inta_fh & CSR49_FH_INT_RX_MASK) - inta |= CSR_INT_BIT_FH_RX; - if (inta_fh & CSR49_FH_INT_TX_MASK) - inta |= CSR_INT_BIT_FH_TX; - - /* Now service all interrupt bits discovered above. */ - if (inta & CSR_INT_BIT_HW_ERR) { - IL_ERR("Hardware error detected. Restarting.\n"); - - /* Tell the device to stop sending interrupts */ - il_disable_interrupts(il); - - il->isr_stats.hw++; - il_irq_handle_error(il); - - handled |= CSR_INT_BIT_HW_ERR; - - return; - } -#ifdef CONFIG_IWLEGACY_DEBUG - if (il_get_debug_level(il) & (IL_DL_ISR)) { - /* NIC fires this, but we don't use it, redundant with WAKEUP */ - if (inta & CSR_INT_BIT_SCD) { - D_ISR("Scheduler finished to transmit " - "the frame/frames.\n"); - il->isr_stats.sch++; - } - - /* Alive notification via Rx interrupt will do the real work */ - if (inta & CSR_INT_BIT_ALIVE) { - D_ISR("Alive interrupt\n"); - il->isr_stats.alive++; - } - } -#endif - /* Safely ignore these bits for debug checks below */ - inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); - - /* HW RF KILL switch toggled */ - if (inta & CSR_INT_BIT_RF_KILL) { - int hw_rf_kill = 0; - - if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) - hw_rf_kill = 1; - - IL_WARN("RF_KILL bit toggled to %s.\n", - hw_rf_kill ? "disable radio" : "enable radio"); - - il->isr_stats.rfkill++; - - /* driver only loads ucode once setting the interface up. - * the driver allows loading the ucode even if the radio - * is killed. Hence update the killswitch state here. The - * rfkill handler will care about restarting if needed. - */ - if (hw_rf_kill) { - set_bit(S_RFKILL, &il->status); - } else { - clear_bit(S_RFKILL, &il->status); - il_force_reset(il, true); - } - wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill); - - handled |= CSR_INT_BIT_RF_KILL; - } - - /* Chip got too hot and stopped itself */ - if (inta & CSR_INT_BIT_CT_KILL) { - IL_ERR("Microcode CT kill error detected.\n"); - il->isr_stats.ctkill++; - handled |= CSR_INT_BIT_CT_KILL; - } - - /* Error detected by uCode */ - if (inta & CSR_INT_BIT_SW_ERR) { - IL_ERR("Microcode SW error detected. " " Restarting 0x%X.\n", - inta); - il->isr_stats.sw++; - il_irq_handle_error(il); - handled |= CSR_INT_BIT_SW_ERR; - } - - /* - * uCode wakes up after power-down sleep. - * Tell device about any new tx or host commands enqueued, - * and about any Rx buffers made available while asleep. - */ - if (inta & CSR_INT_BIT_WAKEUP) { - D_ISR("Wakeup interrupt\n"); - il_rx_queue_update_write_ptr(il, &il->rxq); - for (i = 0; i < il->hw_params.max_txq_num; i++) - il_txq_update_write_ptr(il, &il->txq[i]); - il->isr_stats.wakeup++; - handled |= CSR_INT_BIT_WAKEUP; - } - - /* All uCode command responses, including Tx command responses, - * Rx "responses" (frame-received notification), and other - * notifications from uCode come through here*/ - if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { - il4965_rx_handle(il); - il->isr_stats.rx++; - handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); - } - - /* This "Tx" DMA channel is used only for loading uCode */ - if (inta & CSR_INT_BIT_FH_TX) { - D_ISR("uCode load interrupt\n"); - il->isr_stats.tx++; - handled |= CSR_INT_BIT_FH_TX; - /* Wake up uCode load routine, now that load is complete */ - il->ucode_write_complete = 1; - wake_up(&il->wait_command_queue); - } - - if (inta & ~handled) { - IL_ERR("Unhandled INTA bits 0x%08x\n", inta & ~handled); - il->isr_stats.unhandled++; - } - - if (inta & ~(il->inta_mask)) { - IL_WARN("Disabled INTA bits 0x%08x were pending\n", - inta & ~il->inta_mask); - IL_WARN(" with FH49_INT = 0x%08x\n", inta_fh); - } - - /* Re-enable all interrupts */ - /* only Re-enable if disabled by irq */ - if (test_bit(S_INT_ENABLED, &il->status)) - il_enable_interrupts(il); - /* Re-enable RF_KILL if it occurred */ - else if (handled & CSR_INT_BIT_RF_KILL) - il_enable_rfkill_int(il); - -#ifdef CONFIG_IWLEGACY_DEBUG - if (il_get_debug_level(il) & (IL_DL_ISR)) { - inta = _il_rd(il, CSR_INT); - inta_mask = _il_rd(il, CSR_INT_MASK); - inta_fh = _il_rd(il, CSR_FH_INT_STATUS); - D_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, " - "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); - } -#endif -} - -/***************************************************************************** - * - * sysfs attributes - * - *****************************************************************************/ - -#ifdef CONFIG_IWLEGACY_DEBUG - -/* - * The following adds a new attribute to the sysfs representation - * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/) - * used for controlling the debug level. - * - * See the level definitions in iwl for details. - * - * The debug_level being managed using sysfs below is a per device debug - * level that is used instead of the global debug level if it (the per - * device debug level) is set. - */ -static ssize_t -il4965_show_debug_level(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - return sprintf(buf, "0x%08X\n", il_get_debug_level(il)); -} - -static ssize_t -il4965_store_debug_level(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct il_priv *il = dev_get_drvdata(d); - unsigned long val; - int ret; - - ret = kstrtoul(buf, 0, &val); - if (ret) - IL_ERR("%s is not in hex or decimal form.\n", buf); - else - il->debug_level = val; - - return strnlen(buf, count); -} - -static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, il4965_show_debug_level, - il4965_store_debug_level); - -#endif /* CONFIG_IWLEGACY_DEBUG */ - -static ssize_t -il4965_show_temperature(struct device *d, struct device_attribute *attr, - char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - - if (!il_is_alive(il)) - return -EAGAIN; - - return sprintf(buf, "%d\n", il->temperature); -} - -static DEVICE_ATTR(temperature, S_IRUGO, il4965_show_temperature, NULL); - -static ssize_t -il4965_show_tx_power(struct device *d, struct device_attribute *attr, char *buf) -{ - struct il_priv *il = dev_get_drvdata(d); - - if (!il_is_ready_rf(il)) - return sprintf(buf, "off\n"); - else - return sprintf(buf, "%d\n", il->tx_power_user_lmt); -} - -static ssize_t -il4965_store_tx_power(struct device *d, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct il_priv *il = dev_get_drvdata(d); - unsigned long val; - int ret; - - ret = kstrtoul(buf, 10, &val); - if (ret) - IL_INFO("%s is not in decimal form.\n", buf); - else { - ret = il_set_tx_power(il, val, false); - if (ret) - IL_ERR("failed setting tx power (0x%08x).\n", ret); - else - ret = count; - } - return ret; -} - -static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, il4965_show_tx_power, - il4965_store_tx_power); - -static struct attribute *il_sysfs_entries[] = { - &dev_attr_temperature.attr, - &dev_attr_tx_power.attr, -#ifdef CONFIG_IWLEGACY_DEBUG - &dev_attr_debug_level.attr, -#endif - NULL -}; - -static struct attribute_group il_attribute_group = { - .name = NULL, /* put in device directory */ - .attrs = il_sysfs_entries, -}; - -/****************************************************************************** - * - * uCode download functions - * - ******************************************************************************/ - -static void -il4965_dealloc_ucode_pci(struct il_priv *il) -{ - il_free_fw_desc(il->pci_dev, &il->ucode_code); - il_free_fw_desc(il->pci_dev, &il->ucode_data); - il_free_fw_desc(il->pci_dev, &il->ucode_data_backup); - il_free_fw_desc(il->pci_dev, &il->ucode_init); - il_free_fw_desc(il->pci_dev, &il->ucode_init_data); - il_free_fw_desc(il->pci_dev, &il->ucode_boot); -} - -static void -il4965_nic_start(struct il_priv *il) -{ - /* Remove all resets to allow NIC to operate */ - _il_wr(il, CSR_RESET, 0); -} - -static void il4965_ucode_callback(const struct firmware *ucode_raw, - void *context); -static int il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length); - -static int __must_check -il4965_request_firmware(struct il_priv *il, bool first) -{ - const char *name_pre = il->cfg->fw_name_pre; - char tag[8]; - - if (first) { - il->fw_idx = il->cfg->ucode_api_max; - sprintf(tag, "%d", il->fw_idx); - } else { - il->fw_idx--; - sprintf(tag, "%d", il->fw_idx); - } - - if (il->fw_idx < il->cfg->ucode_api_min) { - IL_ERR("no suitable firmware found!\n"); - return -ENOENT; - } - - sprintf(il->firmware_name, "%s%s%s", name_pre, tag, ".ucode"); - - D_INFO("attempting to load firmware '%s'\n", il->firmware_name); - - return request_firmware_nowait(THIS_MODULE, 1, il->firmware_name, - &il->pci_dev->dev, GFP_KERNEL, il, - il4965_ucode_callback); -} - -struct il4965_firmware_pieces { - const void *inst, *data, *init, *init_data, *boot; - size_t inst_size, data_size, init_size, init_data_size, boot_size; -}; - -static int -il4965_load_firmware(struct il_priv *il, const struct firmware *ucode_raw, - struct il4965_firmware_pieces *pieces) -{ - struct il_ucode_header *ucode = (void *)ucode_raw->data; - u32 api_ver, hdr_size; - const u8 *src; - - il->ucode_ver = le32_to_cpu(ucode->ver); - api_ver = IL_UCODE_API(il->ucode_ver); - - switch (api_ver) { - default: - case 0: - case 1: - case 2: - hdr_size = 24; - if (ucode_raw->size < hdr_size) { - IL_ERR("File size too small!\n"); - return -EINVAL; - } - pieces->inst_size = le32_to_cpu(ucode->v1.inst_size); - pieces->data_size = le32_to_cpu(ucode->v1.data_size); - pieces->init_size = le32_to_cpu(ucode->v1.init_size); - pieces->init_data_size = le32_to_cpu(ucode->v1.init_data_size); - pieces->boot_size = le32_to_cpu(ucode->v1.boot_size); - src = ucode->v1.data; - break; - } - - /* Verify size of file vs. image size info in file's header */ - if (ucode_raw->size != - hdr_size + pieces->inst_size + pieces->data_size + - pieces->init_size + pieces->init_data_size + pieces->boot_size) { - - IL_ERR("uCode file size %d does not match expected size\n", - (int)ucode_raw->size); - return -EINVAL; - } - - pieces->inst = src; - src += pieces->inst_size; - pieces->data = src; - src += pieces->data_size; - pieces->init = src; - src += pieces->init_size; - pieces->init_data = src; - src += pieces->init_data_size; - pieces->boot = src; - src += pieces->boot_size; - - return 0; -} - -/** - * il4965_ucode_callback - callback when firmware was loaded - * - * If loaded successfully, copies the firmware into buffers - * for the card to fetch (via DMA). - */ -static void -il4965_ucode_callback(const struct firmware *ucode_raw, void *context) -{ - struct il_priv *il = context; - struct il_ucode_header *ucode; - int err; - struct il4965_firmware_pieces pieces; - const unsigned int api_max = il->cfg->ucode_api_max; - const unsigned int api_min = il->cfg->ucode_api_min; - u32 api_ver; - - u32 max_probe_length = 200; - u32 standard_phy_calibration_size = - IL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; - - memset(&pieces, 0, sizeof(pieces)); - - if (!ucode_raw) { - if (il->fw_idx <= il->cfg->ucode_api_max) - IL_ERR("request for firmware file '%s' failed.\n", - il->firmware_name); - goto try_again; - } - - D_INFO("Loaded firmware file '%s' (%zd bytes).\n", il->firmware_name, - ucode_raw->size); - - /* Make sure that we got at least the API version number */ - if (ucode_raw->size < 4) { - IL_ERR("File size way too small!\n"); - goto try_again; - } - - /* Data from ucode file: header followed by uCode images */ - ucode = (struct il_ucode_header *)ucode_raw->data; - - err = il4965_load_firmware(il, ucode_raw, &pieces); - - if (err) - goto try_again; - - api_ver = IL_UCODE_API(il->ucode_ver); - - /* - * api_ver should match the api version forming part of the - * firmware filename ... but we don't check for that and only rely - * on the API version read from firmware header from here on forward - */ - if (api_ver < api_min || api_ver > api_max) { - IL_ERR("Driver unable to support your firmware API. " - "Driver supports v%u, firmware is v%u.\n", api_max, - api_ver); - goto try_again; - } - - if (api_ver != api_max) - IL_ERR("Firmware has old API version. Expected v%u, " - "got v%u. New firmware can be obtained " - "from http://www.intellinuxwireless.org.\n", api_max, - api_ver); - - IL_INFO("loaded firmware version %u.%u.%u.%u\n", - IL_UCODE_MAJOR(il->ucode_ver), IL_UCODE_MINOR(il->ucode_ver), - IL_UCODE_API(il->ucode_ver), IL_UCODE_SERIAL(il->ucode_ver)); - - snprintf(il->hw->wiphy->fw_version, sizeof(il->hw->wiphy->fw_version), - "%u.%u.%u.%u", IL_UCODE_MAJOR(il->ucode_ver), - IL_UCODE_MINOR(il->ucode_ver), IL_UCODE_API(il->ucode_ver), - IL_UCODE_SERIAL(il->ucode_ver)); - - /* - * For any of the failures below (before allocating pci memory) - * we will try to load a version with a smaller API -- maybe the - * user just got a corrupted version of the latest API. - */ - - D_INFO("f/w package hdr ucode version raw = 0x%x\n", il->ucode_ver); - D_INFO("f/w package hdr runtime inst size = %Zd\n", pieces.inst_size); - D_INFO("f/w package hdr runtime data size = %Zd\n", pieces.data_size); - D_INFO("f/w package hdr init inst size = %Zd\n", pieces.init_size); - D_INFO("f/w package hdr init data size = %Zd\n", pieces.init_data_size); - D_INFO("f/w package hdr boot inst size = %Zd\n", pieces.boot_size); - - /* Verify that uCode images will fit in card's SRAM */ - if (pieces.inst_size > il->hw_params.max_inst_size) { - IL_ERR("uCode instr len %Zd too large to fit in\n", - pieces.inst_size); - goto try_again; - } - - if (pieces.data_size > il->hw_params.max_data_size) { - IL_ERR("uCode data len %Zd too large to fit in\n", - pieces.data_size); - goto try_again; - } - - if (pieces.init_size > il->hw_params.max_inst_size) { - IL_ERR("uCode init instr len %Zd too large to fit in\n", - pieces.init_size); - goto try_again; - } - - if (pieces.init_data_size > il->hw_params.max_data_size) { - IL_ERR("uCode init data len %Zd too large to fit in\n", - pieces.init_data_size); - goto try_again; - } - - if (pieces.boot_size > il->hw_params.max_bsm_size) { - IL_ERR("uCode boot instr len %Zd too large to fit in\n", - pieces.boot_size); - goto try_again; - } - - /* Allocate ucode buffers for card's bus-master loading ... */ - - /* Runtime instructions and 2 copies of data: - * 1) unmodified from disk - * 2) backup cache for save/restore during power-downs */ - il->ucode_code.len = pieces.inst_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_code); - - il->ucode_data.len = pieces.data_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_data); - - il->ucode_data_backup.len = pieces.data_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_data_backup); - - if (!il->ucode_code.v_addr || !il->ucode_data.v_addr || - !il->ucode_data_backup.v_addr) - goto err_pci_alloc; - - /* Initialization instructions and data */ - if (pieces.init_size && pieces.init_data_size) { - il->ucode_init.len = pieces.init_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_init); - - il->ucode_init_data.len = pieces.init_data_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_init_data); - - if (!il->ucode_init.v_addr || !il->ucode_init_data.v_addr) - goto err_pci_alloc; - } - - /* Bootstrap (instructions only, no data) */ - if (pieces.boot_size) { - il->ucode_boot.len = pieces.boot_size; - il_alloc_fw_desc(il->pci_dev, &il->ucode_boot); - - if (!il->ucode_boot.v_addr) - goto err_pci_alloc; - } - - /* Now that we can no longer fail, copy information */ - - il->sta_key_max_num = STA_KEY_MAX_NUM; - - /* Copy images into buffers for card's bus-master reads ... */ - - /* Runtime instructions (first block of data in file) */ - D_INFO("Copying (but not loading) uCode instr len %Zd\n", - pieces.inst_size); - memcpy(il->ucode_code.v_addr, pieces.inst, pieces.inst_size); - - D_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", - il->ucode_code.v_addr, (u32) il->ucode_code.p_addr); - - /* - * Runtime data - * NOTE: Copy into backup buffer will be done in il_up() - */ - D_INFO("Copying (but not loading) uCode data len %Zd\n", - pieces.data_size); - memcpy(il->ucode_data.v_addr, pieces.data, pieces.data_size); - memcpy(il->ucode_data_backup.v_addr, pieces.data, pieces.data_size); - - /* Initialization instructions */ - if (pieces.init_size) { - D_INFO("Copying (but not loading) init instr len %Zd\n", - pieces.init_size); - memcpy(il->ucode_init.v_addr, pieces.init, pieces.init_size); - } - - /* Initialization data */ - if (pieces.init_data_size) { - D_INFO("Copying (but not loading) init data len %Zd\n", - pieces.init_data_size); - memcpy(il->ucode_init_data.v_addr, pieces.init_data, - pieces.init_data_size); - } - - /* Bootstrap instructions */ - D_INFO("Copying (but not loading) boot instr len %Zd\n", - pieces.boot_size); - memcpy(il->ucode_boot.v_addr, pieces.boot, pieces.boot_size); - - /* - * figure out the offset of chain noise reset and gain commands - * base on the size of standard phy calibration commands table size - */ - il->_4965.phy_calib_chain_noise_reset_cmd = - standard_phy_calibration_size; - il->_4965.phy_calib_chain_noise_gain_cmd = - standard_phy_calibration_size + 1; - - /************************************************** - * This is still part of probe() in a sense... - * - * 9. Setup and register with mac80211 and debugfs - **************************************************/ - err = il4965_mac_setup_register(il, max_probe_length); - if (err) - goto out_unbind; - - err = il_dbgfs_register(il, DRV_NAME); - if (err) - IL_ERR("failed to create debugfs files. Ignoring error: %d\n", - err); - - err = sysfs_create_group(&il->pci_dev->dev.kobj, &il_attribute_group); - if (err) { - IL_ERR("failed to create sysfs device attributes\n"); - goto out_unbind; - } - - /* We have our copies now, allow OS release its copies */ - release_firmware(ucode_raw); - complete(&il->_4965.firmware_loading_complete); - return; - -try_again: - /* try next, if any */ - if (il4965_request_firmware(il, false)) - goto out_unbind; - release_firmware(ucode_raw); - return; - -err_pci_alloc: - IL_ERR("failed to allocate pci memory\n"); - il4965_dealloc_ucode_pci(il); -out_unbind: - complete(&il->_4965.firmware_loading_complete); - device_release_driver(&il->pci_dev->dev); - release_firmware(ucode_raw); -} - -static const char *const desc_lookup_text[] = { - "OK", - "FAIL", - "BAD_PARAM", - "BAD_CHECKSUM", - "NMI_INTERRUPT_WDG", - "SYSASSERT", - "FATAL_ERROR", - "BAD_COMMAND", - "HW_ERROR_TUNE_LOCK", - "HW_ERROR_TEMPERATURE", - "ILLEGAL_CHAN_FREQ", - "VCC_NOT_STBL", - "FH49_ERROR", - "NMI_INTERRUPT_HOST", - "NMI_INTERRUPT_ACTION_PT", - "NMI_INTERRUPT_UNKNOWN", - "UCODE_VERSION_MISMATCH", - "HW_ERROR_ABS_LOCK", - "HW_ERROR_CAL_LOCK_FAIL", - "NMI_INTERRUPT_INST_ACTION_PT", - "NMI_INTERRUPT_DATA_ACTION_PT", - "NMI_TRM_HW_ER", - "NMI_INTERRUPT_TRM", - "NMI_INTERRUPT_BREAK_POINT", - "DEBUG_0", - "DEBUG_1", - "DEBUG_2", - "DEBUG_3", -}; - -static struct { - char *name; - u8 num; -} advanced_lookup[] = { - { - "NMI_INTERRUPT_WDG", 0x34}, { - "SYSASSERT", 0x35}, { - "UCODE_VERSION_MISMATCH", 0x37}, { - "BAD_COMMAND", 0x38}, { - "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C}, { - "FATAL_ERROR", 0x3D}, { - "NMI_TRM_HW_ERR", 0x46}, { - "NMI_INTERRUPT_TRM", 0x4C}, { - "NMI_INTERRUPT_BREAK_POINT", 0x54}, { - "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C}, { - "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64}, { - "NMI_INTERRUPT_HOST", 0x66}, { - "NMI_INTERRUPT_ACTION_PT", 0x7C}, { - "NMI_INTERRUPT_UNKNOWN", 0x84}, { - "NMI_INTERRUPT_INST_ACTION_PT", 0x86}, { -"ADVANCED_SYSASSERT", 0},}; - -static const char * -il4965_desc_lookup(u32 num) -{ - int i; - int max = ARRAY_SIZE(desc_lookup_text); - - if (num < max) - return desc_lookup_text[num]; - - max = ARRAY_SIZE(advanced_lookup) - 1; - for (i = 0; i < max; i++) { - if (advanced_lookup[i].num == num) - break; - } - return advanced_lookup[i].name; -} - -#define ERROR_START_OFFSET (1 * sizeof(u32)) -#define ERROR_ELEM_SIZE (7 * sizeof(u32)) - -void -il4965_dump_nic_error_log(struct il_priv *il) -{ - u32 data2, line; - u32 desc, time, count, base, data1; - u32 blink1, blink2, ilink1, ilink2; - u32 pc, hcmd; - - if (il->ucode_type == UCODE_INIT) - base = le32_to_cpu(il->card_alive_init.error_event_table_ptr); - else - base = le32_to_cpu(il->card_alive.error_event_table_ptr); - - if (!il->ops->is_valid_rtc_data_addr(base)) { - IL_ERR("Not valid error log pointer 0x%08X for %s uCode\n", - base, (il->ucode_type == UCODE_INIT) ? "Init" : "RT"); - return; - } - - count = il_read_targ_mem(il, base); - - if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { - IL_ERR("Start IWL Error Log Dump:\n"); - IL_ERR("Status: 0x%08lX, count: %d\n", il->status, count); - } - - desc = il_read_targ_mem(il, base + 1 * sizeof(u32)); - il->isr_stats.err_code = desc; - pc = il_read_targ_mem(il, base + 2 * sizeof(u32)); - blink1 = il_read_targ_mem(il, base + 3 * sizeof(u32)); - blink2 = il_read_targ_mem(il, base + 4 * sizeof(u32)); - ilink1 = il_read_targ_mem(il, base + 5 * sizeof(u32)); - ilink2 = il_read_targ_mem(il, base + 6 * sizeof(u32)); - data1 = il_read_targ_mem(il, base + 7 * sizeof(u32)); - data2 = il_read_targ_mem(il, base + 8 * sizeof(u32)); - line = il_read_targ_mem(il, base + 9 * sizeof(u32)); - time = il_read_targ_mem(il, base + 11 * sizeof(u32)); - hcmd = il_read_targ_mem(il, base + 22 * sizeof(u32)); - - IL_ERR("Desc Time " - "data1 data2 line\n"); - IL_ERR("%-28s (0x%04X) %010u 0x%08X 0x%08X %u\n", - il4965_desc_lookup(desc), desc, time, data1, data2, line); - IL_ERR("pc blink1 blink2 ilink1 ilink2 hcmd\n"); - IL_ERR("0x%05X 0x%05X 0x%05X 0x%05X 0x%05X 0x%05X\n", pc, blink1, - blink2, ilink1, ilink2, hcmd); -} - -static void -il4965_rf_kill_ct_config(struct il_priv *il) -{ - struct il_ct_kill_config cmd; - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&il->lock, flags); - _il_wr(il, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); - spin_unlock_irqrestore(&il->lock, flags); - - cmd.critical_temperature_R = - cpu_to_le32(il->hw_params.ct_kill_threshold); - - ret = il_send_cmd_pdu(il, C_CT_KILL_CONFIG, sizeof(cmd), &cmd); - if (ret) - IL_ERR("C_CT_KILL_CONFIG failed\n"); - else - D_INFO("C_CT_KILL_CONFIG " "succeeded, " - "critical temperature is %d\n", - il->hw_params.ct_kill_threshold); -} - -static const s8 default_queue_to_tx_fifo[] = { - IL_TX_FIFO_VO, - IL_TX_FIFO_VI, - IL_TX_FIFO_BE, - IL_TX_FIFO_BK, - IL49_CMD_FIFO_NUM, - IL_TX_FIFO_UNUSED, - IL_TX_FIFO_UNUSED, -}; - -#define IL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) - -static int -il4965_alive_notify(struct il_priv *il) -{ - u32 a; - unsigned long flags; - int i, chan; - u32 reg_val; - - spin_lock_irqsave(&il->lock, flags); - - /* Clear 4965's internal Tx Scheduler data base */ - il->scd_base_addr = il_rd_prph(il, IL49_SCD_SRAM_BASE_ADDR); - a = il->scd_base_addr + IL49_SCD_CONTEXT_DATA_OFFSET; - for (; a < il->scd_base_addr + IL49_SCD_TX_STTS_BITMAP_OFFSET; a += 4) - il_write_targ_mem(il, a, 0); - for (; a < il->scd_base_addr + IL49_SCD_TRANSLATE_TBL_OFFSET; a += 4) - il_write_targ_mem(il, a, 0); - for (; - a < - il->scd_base_addr + - IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(il->hw_params.max_txq_num); - a += 4) - il_write_targ_mem(il, a, 0); - - /* Tel 4965 where to find Tx byte count tables */ - il_wr_prph(il, IL49_SCD_DRAM_BASE_ADDR, il->scd_bc_tbls.dma >> 10); - - /* Enable DMA channel */ - for (chan = 0; chan < FH49_TCSR_CHNL_NUM; chan++) - il_wr(il, FH49_TCSR_CHNL_TX_CONFIG_REG(chan), - FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); - - /* Update FH chicken bits */ - reg_val = il_rd(il, FH49_TX_CHICKEN_BITS_REG); - il_wr(il, FH49_TX_CHICKEN_BITS_REG, - reg_val | FH49_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); - - /* Disable chain mode for all queues */ - il_wr_prph(il, IL49_SCD_QUEUECHAIN_SEL, 0); - - /* Initialize each Tx queue (including the command queue) */ - for (i = 0; i < il->hw_params.max_txq_num; i++) { - - /* TFD circular buffer read/write idxes */ - il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(i), 0); - il_wr(il, HBUS_TARG_WRPTR, 0 | (i << 8)); - - /* Max Tx Window size for Scheduler-ACK mode */ - il_write_targ_mem(il, - il->scd_base_addr + - IL49_SCD_CONTEXT_QUEUE_OFFSET(i), - (SCD_WIN_SIZE << - IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) & - IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); - - /* Frame limit */ - il_write_targ_mem(il, - il->scd_base_addr + - IL49_SCD_CONTEXT_QUEUE_OFFSET(i) + - sizeof(u32), - (SCD_FRAME_LIMIT << - IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); - - } - il_wr_prph(il, IL49_SCD_INTERRUPT_MASK, - (1 << il->hw_params.max_txq_num) - 1); - - /* Activate all Tx DMA/FIFO channels */ - il4965_txq_set_sched(il, IL_MASK(0, 6)); - - il4965_set_wr_ptrs(il, IL_DEFAULT_CMD_QUEUE_NUM, 0); - - /* make sure all queue are not stopped */ - memset(&il->queue_stopped[0], 0, sizeof(il->queue_stopped)); - for (i = 0; i < 4; i++) - atomic_set(&il->queue_stop_count[i], 0); - - /* reset to 0 to enable all the queue first */ - il->txq_ctx_active_msk = 0; - /* Map each Tx/cmd queue to its corresponding fifo */ - BUILD_BUG_ON(ARRAY_SIZE(default_queue_to_tx_fifo) != 7); - - for (i = 0; i < ARRAY_SIZE(default_queue_to_tx_fifo); i++) { - int ac = default_queue_to_tx_fifo[i]; - - il_txq_ctx_activate(il, i); - - if (ac == IL_TX_FIFO_UNUSED) - continue; - - il4965_tx_queue_set_status(il, &il->txq[i], ac, 0); - } - - spin_unlock_irqrestore(&il->lock, flags); - - return 0; -} - -/** - * il4965_alive_start - called after N_ALIVE notification received - * from protocol/runtime uCode (initialization uCode's - * Alive gets handled by il_init_alive_start()). - */ -static void -il4965_alive_start(struct il_priv *il) -{ - int ret = 0; - - D_INFO("Runtime Alive received.\n"); - - if (il->card_alive.is_valid != UCODE_VALID_OK) { - /* We had an error bringing up the hardware, so take it - * all the way back down so we can try again */ - D_INFO("Alive failed.\n"); - goto restart; - } - - /* Initialize uCode has loaded Runtime uCode ... verify inst image. - * This is a paranoid check, because we would not have gotten the - * "runtime" alive if code weren't properly loaded. */ - if (il4965_verify_ucode(il)) { - /* Runtime instruction load was bad; - * take it all the way back down so we can try again */ - D_INFO("Bad runtime uCode load.\n"); - goto restart; - } - - ret = il4965_alive_notify(il); - if (ret) { - IL_WARN("Could not complete ALIVE transition [ntf]: %d\n", ret); - goto restart; - } - - /* After the ALIVE response, we can send host commands to the uCode */ - set_bit(S_ALIVE, &il->status); - - /* Enable watchdog to monitor the driver tx queues */ - il_setup_watchdog(il); - - if (il_is_rfkill(il)) - return; - - ieee80211_wake_queues(il->hw); - - il->active_rate = RATES_MASK; - - il_power_update_mode(il, true); - D_INFO("Updated power mode\n"); - - if (il_is_associated(il)) { - struct il_rxon_cmd *active_rxon = - (struct il_rxon_cmd *)&il->active; - /* apply any changes in staging */ - il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - } else { - /* Initialize our rx_config data */ - il_connection_init_rx_config(il); - - if (il->ops->set_rxon_chain) - il->ops->set_rxon_chain(il); - } - - /* Configure bluetooth coexistence if enabled */ - il_send_bt_config(il); - - il4965_reset_run_time_calib(il); - - set_bit(S_READY, &il->status); - - /* Configure the adapter for unassociated operation */ - il_commit_rxon(il); - - /* At this point, the NIC is initialized and operational */ - il4965_rf_kill_ct_config(il); - - D_INFO("ALIVE processing complete.\n"); - wake_up(&il->wait_command_queue); - - return; - -restart: - queue_work(il->workqueue, &il->restart); -} - -static void il4965_cancel_deferred_work(struct il_priv *il); - -static void -__il4965_down(struct il_priv *il) -{ - unsigned long flags; - int exit_pending; - - D_INFO(DRV_NAME " is going down\n"); - - il_scan_cancel_timeout(il, 200); - - exit_pending = test_and_set_bit(S_EXIT_PENDING, &il->status); - - /* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set - * to prevent rearm timer */ - del_timer_sync(&il->watchdog); - - il_clear_ucode_stations(il); - - /* FIXME: race conditions ? */ - spin_lock_irq(&il->sta_lock); - /* - * Remove all key information that is not stored as part - * of station information since mac80211 may not have had - * a chance to remove all the keys. When device is - * reconfigured by mac80211 after an error all keys will - * be reconfigured. - */ - memset(il->_4965.wep_keys, 0, sizeof(il->_4965.wep_keys)); - il->_4965.key_mapping_keys = 0; - spin_unlock_irq(&il->sta_lock); - - il_dealloc_bcast_stations(il); - il_clear_driver_stations(il); - - /* Unblock any waiting calls */ - wake_up_all(&il->wait_command_queue); - - /* Wipe out the EXIT_PENDING status bit if we are not actually - * exiting the module */ - if (!exit_pending) - clear_bit(S_EXIT_PENDING, &il->status); - - /* stop and reset the on-board processor */ - _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); - - /* tell the device to stop sending interrupts */ - spin_lock_irqsave(&il->lock, flags); - il_disable_interrupts(il); - spin_unlock_irqrestore(&il->lock, flags); - il4965_synchronize_irq(il); - - if (il->mac80211_registered) - ieee80211_stop_queues(il->hw); - - /* If we have not previously called il_init() then - * clear all bits but the RF Kill bit and return */ - if (!il_is_init(il)) { - il->status = - test_bit(S_RFKILL, &il->status) << S_RFKILL | - test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | - test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; - goto exit; - } - - /* ...otherwise clear out all the status bits but the RF Kill - * bit and continue taking the NIC down. */ - il->status &= - test_bit(S_RFKILL, &il->status) << S_RFKILL | - test_bit(S_GEO_CONFIGURED, &il->status) << S_GEO_CONFIGURED | - test_bit(S_FW_ERROR, &il->status) << S_FW_ERROR | - test_bit(S_EXIT_PENDING, &il->status) << S_EXIT_PENDING; - - /* - * We disabled and synchronized interrupt, and priv->mutex is taken, so - * here is the only thread which will program device registers, but - * still have lockdep assertions, so we are taking reg_lock. - */ - spin_lock_irq(&il->reg_lock); - /* FIXME: il_grab_nic_access if rfkill is off ? */ - - il4965_txq_ctx_stop(il); - il4965_rxq_stop(il); - /* Power-down device's busmaster DMA clocks */ - _il_wr_prph(il, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); - udelay(5); - /* Make sure (redundant) we've released our request to stay awake */ - _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - /* Stop the device, and put it in low power state */ - _il_apm_stop(il); - - spin_unlock_irq(&il->reg_lock); - - il4965_txq_ctx_unmap(il); -exit: - memset(&il->card_alive, 0, sizeof(struct il_alive_resp)); - - dev_kfree_skb(il->beacon_skb); - il->beacon_skb = NULL; - - /* clear out any free frames */ - il4965_clear_free_frames(il); -} - -static void -il4965_down(struct il_priv *il) -{ - mutex_lock(&il->mutex); - __il4965_down(il); - mutex_unlock(&il->mutex); - - il4965_cancel_deferred_work(il); -} - - -static void -il4965_set_hw_ready(struct il_priv *il) -{ - int ret; - - il_set_bit(il, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); - - /* See if we got it */ - ret = _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, - 100); - if (ret >= 0) - il->hw_ready = true; - - D_INFO("hardware %s ready\n", (il->hw_ready) ? "" : "not"); -} - -static void -il4965_prepare_card_hw(struct il_priv *il) -{ - int ret; - - il->hw_ready = false; - - il4965_set_hw_ready(il); - if (il->hw_ready) - return; - - /* If HW is not ready, prepare the conditions to check again */ - il_set_bit(il, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE); - - ret = - _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, - ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, - CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); - - /* HW should be ready by now, check again. */ - if (ret != -ETIMEDOUT) - il4965_set_hw_ready(il); -} - -#define MAX_HW_RESTARTS 5 - -static int -__il4965_up(struct il_priv *il) -{ - int i; - int ret; - - if (test_bit(S_EXIT_PENDING, &il->status)) { - IL_WARN("Exit pending; will not bring the NIC up\n"); - return -EIO; - } - - if (!il->ucode_data_backup.v_addr || !il->ucode_data.v_addr) { - IL_ERR("ucode not available for device bringup\n"); - return -EIO; - } - - ret = il4965_alloc_bcast_station(il); - if (ret) { - il_dealloc_bcast_stations(il); - return ret; - } - - il4965_prepare_card_hw(il); - if (!il->hw_ready) { - IL_ERR("HW not ready\n"); - return -EIO; - } - - /* If platform's RF_KILL switch is NOT set to KILL */ - if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) - clear_bit(S_RFKILL, &il->status); - else { - set_bit(S_RFKILL, &il->status); - wiphy_rfkill_set_hw_state(il->hw->wiphy, true); - - il_enable_rfkill_int(il); - IL_WARN("Radio disabled by HW RF Kill switch\n"); - return 0; - } - - _il_wr(il, CSR_INT, 0xFFFFFFFF); - - /* must be initialised before il_hw_nic_init */ - il->cmd_queue = IL_DEFAULT_CMD_QUEUE_NUM; - - ret = il4965_hw_nic_init(il); - if (ret) { - IL_ERR("Unable to init nic\n"); - return ret; - } - - /* make sure rfkill handshake bits are cleared */ - _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - /* clear (again), then enable host interrupts */ - _il_wr(il, CSR_INT, 0xFFFFFFFF); - il_enable_interrupts(il); - - /* really make sure rfkill handshake bits are cleared */ - _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - _il_wr(il, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - - /* Copy original ucode data image from disk into backup cache. - * This will be used to initialize the on-board processor's - * data SRAM for a clean start when the runtime program first loads. */ - memcpy(il->ucode_data_backup.v_addr, il->ucode_data.v_addr, - il->ucode_data.len); - - for (i = 0; i < MAX_HW_RESTARTS; i++) { - - /* load bootstrap state machine, - * load bootstrap program into processor's memory, - * prepare to load the "initialize" uCode */ - ret = il->ops->load_ucode(il); - - if (ret) { - IL_ERR("Unable to set up bootstrap uCode: %d\n", ret); - continue; - } - - /* start card; "initialize" will load runtime ucode */ - il4965_nic_start(il); - - D_INFO(DRV_NAME " is coming up\n"); - - return 0; - } - - set_bit(S_EXIT_PENDING, &il->status); - __il4965_down(il); - clear_bit(S_EXIT_PENDING, &il->status); - - /* tried to restart and config the device for as long as our - * patience could withstand */ - IL_ERR("Unable to initialize device after %d attempts.\n", i); - return -EIO; -} - -/***************************************************************************** - * - * Workqueue callbacks - * - *****************************************************************************/ - -static void -il4965_bg_init_alive_start(struct work_struct *data) -{ - struct il_priv *il = - container_of(data, struct il_priv, init_alive_start.work); - - mutex_lock(&il->mutex); - if (test_bit(S_EXIT_PENDING, &il->status)) - goto out; - - il->ops->init_alive_start(il); -out: - mutex_unlock(&il->mutex); -} - -static void -il4965_bg_alive_start(struct work_struct *data) -{ - struct il_priv *il = - container_of(data, struct il_priv, alive_start.work); - - mutex_lock(&il->mutex); - if (test_bit(S_EXIT_PENDING, &il->status)) - goto out; - - il4965_alive_start(il); -out: - mutex_unlock(&il->mutex); -} - -static void -il4965_bg_run_time_calib_work(struct work_struct *work) -{ - struct il_priv *il = container_of(work, struct il_priv, - run_time_calib_work); - - mutex_lock(&il->mutex); - - if (test_bit(S_EXIT_PENDING, &il->status) || - test_bit(S_SCANNING, &il->status)) { - mutex_unlock(&il->mutex); - return; - } - - if (il->start_calib) { - il4965_chain_noise_calibration(il, (void *)&il->_4965.stats); - il4965_sensitivity_calibration(il, (void *)&il->_4965.stats); - } - - mutex_unlock(&il->mutex); -} - -static void -il4965_bg_restart(struct work_struct *data) -{ - struct il_priv *il = container_of(data, struct il_priv, restart); - - if (test_bit(S_EXIT_PENDING, &il->status)) - return; - - if (test_and_clear_bit(S_FW_ERROR, &il->status)) { - mutex_lock(&il->mutex); - il->is_open = 0; - - __il4965_down(il); - - mutex_unlock(&il->mutex); - il4965_cancel_deferred_work(il); - ieee80211_restart_hw(il->hw); - } else { - il4965_down(il); - - mutex_lock(&il->mutex); - if (test_bit(S_EXIT_PENDING, &il->status)) { - mutex_unlock(&il->mutex); - return; - } - - __il4965_up(il); - mutex_unlock(&il->mutex); - } -} - -static void -il4965_bg_rx_replenish(struct work_struct *data) -{ - struct il_priv *il = container_of(data, struct il_priv, rx_replenish); - - if (test_bit(S_EXIT_PENDING, &il->status)) - return; - - mutex_lock(&il->mutex); - il4965_rx_replenish(il); - mutex_unlock(&il->mutex); -} - -/***************************************************************************** - * - * mac80211 entry point functions - * - *****************************************************************************/ - -#define UCODE_READY_TIMEOUT (4 * HZ) - -/* - * Not a mac80211 entry point function, but it fits in with all the - * other mac80211 functions grouped here. - */ -static int -il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) -{ - int ret; - struct ieee80211_hw *hw = il->hw; - - hw->rate_control_algorithm = "iwl-4965-rs"; - - /* Tell mac80211 our characteristics */ - ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); - ieee80211_hw_set(hw, SUPPORTS_PS); - ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); - ieee80211_hw_set(hw, SPECTRUM_MGMT); - ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); - ieee80211_hw_set(hw, SIGNAL_DBM); - ieee80211_hw_set(hw, AMPDU_AGGREGATION); - if (il->cfg->sku & IL_SKU_N) - hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS | - NL80211_FEATURE_STATIC_SMPS; - - hw->sta_data_size = sizeof(struct il_station_priv); - hw->vif_data_size = sizeof(struct il_vif_priv); - - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); - - hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | - REGULATORY_DISABLE_BEACON_HINTS; - - /* - * For now, disable PS by default because it affects - * RX performance significantly. - */ - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; - /* we create the 802.11 header and a zero-length SSID element */ - hw->wiphy->max_scan_ie_len = max_probe_length - 24 - 2; - - /* Default value; 4 EDCA QOS priorities */ - hw->queues = 4; - - hw->max_listen_interval = IL_CONN_MAX_LISTEN_INTERVAL; - - if (il->bands[IEEE80211_BAND_2GHZ].n_channels) - il->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &il->bands[IEEE80211_BAND_2GHZ]; - if (il->bands[IEEE80211_BAND_5GHZ].n_channels) - il->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &il->bands[IEEE80211_BAND_5GHZ]; - - il_leds_init(il); - - ret = ieee80211_register_hw(il->hw); - if (ret) { - IL_ERR("Failed to register hw (error %d)\n", ret); - return ret; - } - il->mac80211_registered = 1; - - return 0; -} - -int -il4965_mac_start(struct ieee80211_hw *hw) -{ - struct il_priv *il = hw->priv; - int ret; - - D_MAC80211("enter\n"); - - /* we should be verifying the device is ready to be opened */ - mutex_lock(&il->mutex); - ret = __il4965_up(il); - mutex_unlock(&il->mutex); - - if (ret) - return ret; - - if (il_is_rfkill(il)) - goto out; - - D_INFO("Start UP work done.\n"); - - /* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from - * mac80211 will not be run successfully. */ - ret = wait_event_timeout(il->wait_command_queue, - test_bit(S_READY, &il->status), - UCODE_READY_TIMEOUT); - if (!ret) { - if (!test_bit(S_READY, &il->status)) { - IL_ERR("START_ALIVE timeout after %dms.\n", - jiffies_to_msecs(UCODE_READY_TIMEOUT)); - return -ETIMEDOUT; - } - } - - il4965_led_enable(il); - -out: - il->is_open = 1; - D_MAC80211("leave\n"); - return 0; -} - -void -il4965_mac_stop(struct ieee80211_hw *hw) -{ - struct il_priv *il = hw->priv; - - D_MAC80211("enter\n"); - - if (!il->is_open) - return; - - il->is_open = 0; - - il4965_down(il); - - flush_workqueue(il->workqueue); - - /* User space software may expect getting rfkill changes - * even if interface is down */ - _il_wr(il, CSR_INT, 0xFFFFFFFF); - il_enable_rfkill_int(il); - - D_MAC80211("leave\n"); -} - -void -il4965_mac_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct il_priv *il = hw->priv; - - D_MACDUMP("enter\n"); - - D_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, - ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); - - if (il4965_tx_skb(il, control->sta, skb)) - dev_kfree_skb_any(skb); - - D_MACDUMP("leave\n"); -} - -void -il4965_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, u16 * phase1key) -{ - struct il_priv *il = hw->priv; - - D_MAC80211("enter\n"); - - il4965_update_tkip_key(il, keyconf, sta, iv32, phase1key); - - D_MAC80211("leave\n"); -} - -int -il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct il_priv *il = hw->priv; - int ret; - u8 sta_id; - bool is_default_wep_key = false; - - D_MAC80211("enter\n"); - - if (il->cfg->mod_params->sw_crypto) { - D_MAC80211("leave - hwcrypto disabled\n"); - return -EOPNOTSUPP; - } - - /* - * To support IBSS RSN, don't program group keys in IBSS, the - * hardware will then not attempt to decrypt the frames. - */ - if (vif->type == NL80211_IFTYPE_ADHOC && - !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { - D_MAC80211("leave - ad-hoc group key\n"); - return -EOPNOTSUPP; - } - - sta_id = il_sta_id_or_broadcast(il, sta); - if (sta_id == IL_INVALID_STATION) - return -EINVAL; - - mutex_lock(&il->mutex); - il_scan_cancel_timeout(il, 100); - - /* - * If we are getting WEP group key and we didn't receive any key mapping - * so far, we are in legacy wep mode (group key only), otherwise we are - * in 1X mode. - * In legacy wep mode, we use another host command to the uCode. - */ - if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || - key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { - if (cmd == SET_KEY) - is_default_wep_key = !il->_4965.key_mapping_keys; - else - is_default_wep_key = - (key->hw_key_idx == HW_KEY_DEFAULT); - } - - switch (cmd) { - case SET_KEY: - if (is_default_wep_key) - ret = il4965_set_default_wep_key(il, key); - else - ret = il4965_set_dynamic_key(il, key, sta_id); - - D_MAC80211("enable hwcrypto key\n"); - break; - case DISABLE_KEY: - if (is_default_wep_key) - ret = il4965_remove_default_wep_key(il, key); - else - ret = il4965_remove_dynamic_key(il, key, sta_id); - - D_MAC80211("disable hwcrypto key\n"); - break; - default: - ret = -EINVAL; - } - - mutex_unlock(&il->mutex); - D_MAC80211("leave\n"); - - return ret; -} - -int -il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size, bool amsdu) -{ - struct il_priv *il = hw->priv; - int ret = -EINVAL; - - D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid); - - if (!(il->cfg->sku & IL_SKU_N)) - return -EACCES; - - mutex_lock(&il->mutex); - - switch (action) { - case IEEE80211_AMPDU_RX_START: - D_HT("start Rx\n"); - ret = il4965_sta_rx_agg_start(il, sta, tid, *ssn); - break; - case IEEE80211_AMPDU_RX_STOP: - D_HT("stop Rx\n"); - ret = il4965_sta_rx_agg_stop(il, sta, tid); - if (test_bit(S_EXIT_PENDING, &il->status)) - ret = 0; - break; - case IEEE80211_AMPDU_TX_START: - D_HT("start Tx\n"); - ret = il4965_tx_agg_start(il, vif, sta, tid, ssn); - break; - case IEEE80211_AMPDU_TX_STOP_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - D_HT("stop Tx\n"); - ret = il4965_tx_agg_stop(il, vif, sta, tid); - if (test_bit(S_EXIT_PENDING, &il->status)) - ret = 0; - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - ret = 0; - break; - } - mutex_unlock(&il->mutex); - - return ret; -} - -int -il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct il_priv *il = hw->priv; - struct il_station_priv *sta_priv = (void *)sta->drv_priv; - bool is_ap = vif->type == NL80211_IFTYPE_STATION; - int ret; - u8 sta_id; - - D_INFO("received request to add station %pM\n", sta->addr); - mutex_lock(&il->mutex); - D_INFO("proceeding to add station %pM\n", sta->addr); - sta_priv->common.sta_id = IL_INVALID_STATION; - - atomic_set(&sta_priv->pending_frames, 0); - - ret = - il_add_station_common(il, sta->addr, is_ap, sta, &sta_id); - if (ret) { - IL_ERR("Unable to add station %pM (%d)\n", sta->addr, ret); - /* Should we return success if return code is EEXIST ? */ - mutex_unlock(&il->mutex); - return ret; - } - - sta_priv->common.sta_id = sta_id; - - /* Initialize rate scaling */ - D_INFO("Initializing rate scaling for station %pM\n", sta->addr); - il4965_rs_rate_init(il, sta, sta_id); - mutex_unlock(&il->mutex); - - return 0; -} - -void -il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_channel_switch *ch_switch) -{ - struct il_priv *il = hw->priv; - const struct il_channel_info *ch_info; - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_channel *channel = ch_switch->chandef.chan; - struct il_ht_config *ht_conf = &il->current_ht_config; - u16 ch; - - D_MAC80211("enter\n"); - - mutex_lock(&il->mutex); - - if (il_is_rfkill(il)) - goto out; - - if (test_bit(S_EXIT_PENDING, &il->status) || - test_bit(S_SCANNING, &il->status) || - test_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) - goto out; - - if (!il_is_associated(il)) - goto out; - - if (!il->ops->set_channel_switch) - goto out; - - ch = channel->hw_value; - if (le16_to_cpu(il->active.channel) == ch) - goto out; - - ch_info = il_get_channel_info(il, channel->band, ch); - if (!il_is_channel_valid(ch_info)) { - D_MAC80211("invalid channel\n"); - goto out; - } - - spin_lock_irq(&il->lock); - - il->current_ht_config.smps = conf->smps_mode; - - /* Configure HT40 channels */ - switch (cfg80211_get_chandef_type(&ch_switch->chandef)) { - case NL80211_CHAN_NO_HT: - case NL80211_CHAN_HT20: - il->ht.is_40mhz = false; - il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; - break; - case NL80211_CHAN_HT40MINUS: - il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; - il->ht.is_40mhz = true; - break; - case NL80211_CHAN_HT40PLUS: - il->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - il->ht.is_40mhz = true; - break; - } - - if ((le16_to_cpu(il->staging.channel) != ch)) - il->staging.flags = 0; - - il_set_rxon_channel(il, channel); - il_set_rxon_ht(il, ht_conf); - il_set_flags_for_band(il, channel->band, il->vif); - - spin_unlock_irq(&il->lock); - - il_set_rate(il); - /* - * at this point, staging_rxon has the - * configuration for channel switch - */ - set_bit(S_CHANNEL_SWITCH_PENDING, &il->status); - il->switch_channel = cpu_to_le16(ch); - if (il->ops->set_channel_switch(il, ch_switch)) { - clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status); - il->switch_channel = 0; - ieee80211_chswitch_done(il->vif, false); - } - -out: - mutex_unlock(&il->mutex); - D_MAC80211("leave\n"); -} - -void -il4965_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, u64 multicast) -{ - struct il_priv *il = hw->priv; - __le32 filter_or = 0, filter_nand = 0; - -#define CHK(test, flag) do { \ - if (*total_flags & (test)) \ - filter_or |= (flag); \ - else \ - filter_nand |= (flag); \ - } while (0) - - D_MAC80211("Enter: changed: 0x%x, total: 0x%x\n", changed_flags, - *total_flags); - - CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK); - /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ - CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); - CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); - -#undef CHK - - mutex_lock(&il->mutex); - - il->staging.filter_flags &= ~filter_nand; - il->staging.filter_flags |= filter_or; - - /* - * Not committing directly because hardware can perform a scan, - * but we'll eventually commit the filter flags change anyway. - */ - - mutex_unlock(&il->mutex); - - /* - * Receiving all multicast frames is always enabled by the - * default flags setup in il_connection_init_rx_config() - * since we currently do not support programming multicast - * filters into the device. - */ - *total_flags &= - FIF_OTHER_BSS | FIF_ALLMULTI | - FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; -} - -/***************************************************************************** - * - * driver setup and teardown - * - *****************************************************************************/ - -static void -il4965_bg_txpower_work(struct work_struct *work) -{ - struct il_priv *il = container_of(work, struct il_priv, - txpower_work); - - mutex_lock(&il->mutex); - - /* If a scan happened to start before we got here - * then just return; the stats notification will - * kick off another scheduled work to compensate for - * any temperature delta we missed here. */ - if (test_bit(S_EXIT_PENDING, &il->status) || - test_bit(S_SCANNING, &il->status)) - goto out; - - /* Regardless of if we are associated, we must reconfigure the - * TX power since frames can be sent on non-radar channels while - * not associated */ - il->ops->send_tx_power(il); - - /* Update last_temperature to keep is_calib_needed from running - * when it isn't needed... */ - il->last_temperature = il->temperature; -out: - mutex_unlock(&il->mutex); -} - -static void -il4965_setup_deferred_work(struct il_priv *il) -{ - il->workqueue = create_singlethread_workqueue(DRV_NAME); - - init_waitqueue_head(&il->wait_command_queue); - - INIT_WORK(&il->restart, il4965_bg_restart); - INIT_WORK(&il->rx_replenish, il4965_bg_rx_replenish); - INIT_WORK(&il->run_time_calib_work, il4965_bg_run_time_calib_work); - INIT_DELAYED_WORK(&il->init_alive_start, il4965_bg_init_alive_start); - INIT_DELAYED_WORK(&il->alive_start, il4965_bg_alive_start); - - il_setup_scan_deferred_work(il); - - INIT_WORK(&il->txpower_work, il4965_bg_txpower_work); - - setup_timer(&il->stats_periodic, il4965_bg_stats_periodic, - (unsigned long)il); - - setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il); - - tasklet_init(&il->irq_tasklet, - (void (*)(unsigned long))il4965_irq_tasklet, - (unsigned long)il); -} - -static void -il4965_cancel_deferred_work(struct il_priv *il) -{ - cancel_work_sync(&il->txpower_work); - cancel_delayed_work_sync(&il->init_alive_start); - cancel_delayed_work(&il->alive_start); - cancel_work_sync(&il->run_time_calib_work); - - il_cancel_scan_deferred_work(il); - - del_timer_sync(&il->stats_periodic); -} - -static void -il4965_init_hw_rates(struct il_priv *il, struct ieee80211_rate *rates) -{ - int i; - - for (i = 0; i < RATE_COUNT_LEGACY; i++) { - rates[i].bitrate = il_rates[i].ieee * 5; - rates[i].hw_value = i; /* Rate scaling will work on idxes */ - rates[i].hw_value_short = i; - rates[i].flags = 0; - if ((i >= IL_FIRST_CCK_RATE) && (i <= IL_LAST_CCK_RATE)) { - /* - * If CCK != 1M then set short preamble rate flag. - */ - rates[i].flags |= - (il_rates[i].plcp == - RATE_1M_PLCP) ? 0 : IEEE80211_RATE_SHORT_PREAMBLE; - } - } -} - -/* - * Acquire il->lock before calling this function ! - */ -void -il4965_set_wr_ptrs(struct il_priv *il, int txq_id, u32 idx) -{ - il_wr(il, HBUS_TARG_WRPTR, (idx & 0xff) | (txq_id << 8)); - il_wr_prph(il, IL49_SCD_QUEUE_RDPTR(txq_id), idx); -} - -void -il4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq, - int tx_fifo_id, int scd_retry) -{ - int txq_id = txq->q.id; - - /* Find out whether to activate Tx queue */ - int active = test_bit(txq_id, &il->txq_ctx_active_msk) ? 1 : 0; - - /* Set up and activate */ - il_wr_prph(il, IL49_SCD_QUEUE_STATUS_BITS(txq_id), - (active << IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) | - (tx_fifo_id << IL49_SCD_QUEUE_STTS_REG_POS_TXF) | - (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_WSL) | - (scd_retry << IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK) | - IL49_SCD_QUEUE_STTS_REG_MSK); - - txq->sched_retry = scd_retry; - - D_INFO("%s %s Queue %d on AC %d\n", active ? "Activate" : "Deactivate", - scd_retry ? "BA" : "AC", txq_id, tx_fifo_id); -} - -static const struct ieee80211_ops il4965_mac_ops = { - .tx = il4965_mac_tx, - .start = il4965_mac_start, - .stop = il4965_mac_stop, - .add_interface = il_mac_add_interface, - .remove_interface = il_mac_remove_interface, - .change_interface = il_mac_change_interface, - .config = il_mac_config, - .configure_filter = il4965_configure_filter, - .set_key = il4965_mac_set_key, - .update_tkip_key = il4965_mac_update_tkip_key, - .conf_tx = il_mac_conf_tx, - .reset_tsf = il_mac_reset_tsf, - .bss_info_changed = il_mac_bss_info_changed, - .ampdu_action = il4965_mac_ampdu_action, - .hw_scan = il_mac_hw_scan, - .sta_add = il4965_mac_sta_add, - .sta_remove = il_mac_sta_remove, - .channel_switch = il4965_mac_channel_switch, - .tx_last_beacon = il_mac_tx_last_beacon, - .flush = il_mac_flush, -}; - -static int -il4965_init_drv(struct il_priv *il) -{ - int ret; - - spin_lock_init(&il->sta_lock); - spin_lock_init(&il->hcmd_lock); - - INIT_LIST_HEAD(&il->free_frames); - - mutex_init(&il->mutex); - - il->ieee_channels = NULL; - il->ieee_rates = NULL; - il->band = IEEE80211_BAND_2GHZ; - - il->iw_mode = NL80211_IFTYPE_STATION; - il->current_ht_config.smps = IEEE80211_SMPS_STATIC; - il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF; - - /* initialize force reset */ - il->force_reset.reset_duration = IL_DELAY_NEXT_FORCE_FW_RELOAD; - - /* Choose which receivers/antennas to use */ - if (il->ops->set_rxon_chain) - il->ops->set_rxon_chain(il); - - il_init_scan_params(il); - - ret = il_init_channel_map(il); - if (ret) { - IL_ERR("initializing regulatory failed: %d\n", ret); - goto err; - } - - ret = il_init_geos(il); - if (ret) { - IL_ERR("initializing geos failed: %d\n", ret); - goto err_free_channel_map; - } - il4965_init_hw_rates(il, il->ieee_rates); - - return 0; - -err_free_channel_map: - il_free_channel_map(il); -err: - return ret; -} - -static void -il4965_uninit_drv(struct il_priv *il) -{ - il_free_geos(il); - il_free_channel_map(il); - kfree(il->scan_cmd); -} - -static void -il4965_hw_detect(struct il_priv *il) -{ - il->hw_rev = _il_rd(il, CSR_HW_REV); - il->hw_wa_rev = _il_rd(il, CSR_HW_REV_WA_REG); - il->rev_id = il->pci_dev->revision; - D_INFO("HW Revision ID = 0x%X\n", il->rev_id); -} - -static struct il_sensitivity_ranges il4965_sensitivity = { - .min_nrg_cck = 97, - .max_nrg_cck = 0, /* not used, set to 0 */ - - .auto_corr_min_ofdm = 85, - .auto_corr_min_ofdm_mrc = 170, - .auto_corr_min_ofdm_x1 = 105, - .auto_corr_min_ofdm_mrc_x1 = 220, - - .auto_corr_max_ofdm = 120, - .auto_corr_max_ofdm_mrc = 210, - .auto_corr_max_ofdm_x1 = 140, - .auto_corr_max_ofdm_mrc_x1 = 270, - - .auto_corr_min_cck = 125, - .auto_corr_max_cck = 200, - .auto_corr_min_cck_mrc = 200, - .auto_corr_max_cck_mrc = 400, - - .nrg_th_cck = 100, - .nrg_th_ofdm = 100, - - .barker_corr_th_min = 190, - .barker_corr_th_min_mrc = 390, - .nrg_th_cca = 62, -}; - -static void -il4965_set_hw_params(struct il_priv *il) -{ - il->hw_params.bcast_id = IL4965_BROADCAST_ID; - il->hw_params.max_rxq_size = RX_QUEUE_SIZE; - il->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; - if (il->cfg->mod_params->amsdu_size_8K) - il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_8K); - else - il->hw_params.rx_page_order = get_order(IL_RX_BUF_SIZE_4K); - - il->hw_params.max_beacon_itrvl = IL_MAX_UCODE_BEACON_INTERVAL; - - if (il->cfg->mod_params->disable_11n) - il->cfg->sku &= ~IL_SKU_N; - - if (il->cfg->mod_params->num_of_queues >= IL_MIN_NUM_QUEUES && - il->cfg->mod_params->num_of_queues <= IL49_NUM_QUEUES) - il->cfg->num_of_queues = - il->cfg->mod_params->num_of_queues; - - il->hw_params.max_txq_num = il->cfg->num_of_queues; - il->hw_params.dma_chnl_num = FH49_TCSR_CHNL_NUM; - il->hw_params.scd_bc_tbls_size = - il->cfg->num_of_queues * - sizeof(struct il4965_scd_bc_tbl); - - il->hw_params.tfd_size = sizeof(struct il_tfd); - il->hw_params.max_stations = IL4965_STATION_COUNT; - il->hw_params.max_data_size = IL49_RTC_DATA_SIZE; - il->hw_params.max_inst_size = IL49_RTC_INST_SIZE; - il->hw_params.max_bsm_size = BSM_SRAM_SIZE; - il->hw_params.ht40_channel = BIT(IEEE80211_BAND_5GHZ); - - il->hw_params.rx_wrt_ptr_reg = FH49_RSCSR_CHNL0_WPTR; - - il->hw_params.tx_chains_num = il4965_num_of_ant(il->cfg->valid_tx_ant); - il->hw_params.rx_chains_num = il4965_num_of_ant(il->cfg->valid_rx_ant); - il->hw_params.valid_tx_ant = il->cfg->valid_tx_ant; - il->hw_params.valid_rx_ant = il->cfg->valid_rx_ant; - - il->hw_params.ct_kill_threshold = - CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY); - - il->hw_params.sens = &il4965_sensitivity; - il->hw_params.beacon_time_tsf_bits = IL4965_EXT_BEACON_TIME_POS; -} - -static int -il4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = 0; - struct il_priv *il; - struct ieee80211_hw *hw; - struct il_cfg *cfg = (struct il_cfg *)(ent->driver_data); - unsigned long flags; - u16 pci_cmd; - - /************************ - * 1. Allocating HW data - ************************/ - - hw = ieee80211_alloc_hw(sizeof(struct il_priv), &il4965_mac_ops); - if (!hw) { - err = -ENOMEM; - goto out; - } - il = hw->priv; - il->hw = hw; - SET_IEEE80211_DEV(hw, &pdev->dev); - - D_INFO("*** LOAD DRIVER ***\n"); - il->cfg = cfg; - il->ops = &il4965_ops; -#ifdef CONFIG_IWLEGACY_DEBUGFS - il->debugfs_ops = &il4965_debugfs_ops; -#endif - il->pci_dev = pdev; - il->inta_mask = CSR_INI_SET_MASK; - - /************************** - * 2. Initializing PCI bus - **************************/ - pci_disable_link_state(pdev, - PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | - PCIE_LINK_STATE_CLKPM); - - if (pci_enable_device(pdev)) { - err = -ENODEV; - goto out_ieee80211_free_hw; - } - - pci_set_master(pdev); - - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); - if (!err) - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36)); - if (err) { - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (!err) - err = - pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - /* both attempts failed: */ - if (err) { - IL_WARN("No suitable DMA available.\n"); - goto out_pci_disable_device; - } - } - - err = pci_request_regions(pdev, DRV_NAME); - if (err) - goto out_pci_disable_device; - - pci_set_drvdata(pdev, il); - - /*********************** - * 3. Read REV register - ***********************/ - il->hw_base = pci_ioremap_bar(pdev, 0); - if (!il->hw_base) { - err = -ENODEV; - goto out_pci_release_regions; - } - - D_INFO("pci_resource_len = 0x%08llx\n", - (unsigned long long)pci_resource_len(pdev, 0)); - D_INFO("pci_resource_base = %p\n", il->hw_base); - - /* these spin locks will be used in apm_ops.init and EEPROM access - * we should init now - */ - spin_lock_init(&il->reg_lock); - spin_lock_init(&il->lock); - - /* - * stop and reset the on-board processor just in case it is in a - * strange state ... like being left stranded by a primary kernel - * and this is now the kdump kernel trying to start up - */ - _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); - - il4965_hw_detect(il); - IL_INFO("Detected %s, REV=0x%X\n", il->cfg->name, il->hw_rev); - - /* We disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state */ - pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); - - il4965_prepare_card_hw(il); - if (!il->hw_ready) { - IL_WARN("Failed, HW not ready\n"); - err = -EIO; - goto out_iounmap; - } - - /***************** - * 4. Read EEPROM - *****************/ - /* Read the EEPROM */ - err = il_eeprom_init(il); - if (err) { - IL_ERR("Unable to init EEPROM\n"); - goto out_iounmap; - } - err = il4965_eeprom_check_version(il); - if (err) - goto out_free_eeprom; - - /* extract MAC Address */ - il4965_eeprom_get_mac(il, il->addresses[0].addr); - D_INFO("MAC address: %pM\n", il->addresses[0].addr); - il->hw->wiphy->addresses = il->addresses; - il->hw->wiphy->n_addresses = 1; - - /************************ - * 5. Setup HW constants - ************************/ - il4965_set_hw_params(il); - - /******************* - * 6. Setup il - *******************/ - - err = il4965_init_drv(il); - if (err) - goto out_free_eeprom; - /* At this point both hw and il are initialized. */ - - /******************** - * 7. Setup services - ********************/ - spin_lock_irqsave(&il->lock, flags); - il_disable_interrupts(il); - spin_unlock_irqrestore(&il->lock, flags); - - pci_enable_msi(il->pci_dev); - - err = request_irq(il->pci_dev->irq, il_isr, IRQF_SHARED, DRV_NAME, il); - if (err) { - IL_ERR("Error allocating IRQ %d\n", il->pci_dev->irq); - goto out_disable_msi; - } - - il4965_setup_deferred_work(il); - il4965_setup_handlers(il); - - /********************************************* - * 8. Enable interrupts and read RFKILL state - *********************************************/ - - /* enable rfkill interrupt: hw bug w/a */ - pci_read_config_word(il->pci_dev, PCI_COMMAND, &pci_cmd); - if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { - pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; - pci_write_config_word(il->pci_dev, PCI_COMMAND, pci_cmd); - } - - il_enable_rfkill_int(il); - - /* If platform's RF_KILL switch is NOT set to KILL */ - if (_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) - clear_bit(S_RFKILL, &il->status); - else - set_bit(S_RFKILL, &il->status); - - wiphy_rfkill_set_hw_state(il->hw->wiphy, - test_bit(S_RFKILL, &il->status)); - - il_power_initialize(il); - - init_completion(&il->_4965.firmware_loading_complete); - - err = il4965_request_firmware(il, true); - if (err) - goto out_destroy_workqueue; - - return 0; - -out_destroy_workqueue: - destroy_workqueue(il->workqueue); - il->workqueue = NULL; - free_irq(il->pci_dev->irq, il); -out_disable_msi: - pci_disable_msi(il->pci_dev); - il4965_uninit_drv(il); -out_free_eeprom: - il_eeprom_free(il); -out_iounmap: - iounmap(il->hw_base); -out_pci_release_regions: - pci_release_regions(pdev); -out_pci_disable_device: - pci_disable_device(pdev); -out_ieee80211_free_hw: - ieee80211_free_hw(il->hw); -out: - return err; -} - -static void -il4965_pci_remove(struct pci_dev *pdev) -{ - struct il_priv *il = pci_get_drvdata(pdev); - unsigned long flags; - - if (!il) - return; - - wait_for_completion(&il->_4965.firmware_loading_complete); - - D_INFO("*** UNLOAD DRIVER ***\n"); - - il_dbgfs_unregister(il); - sysfs_remove_group(&pdev->dev.kobj, &il_attribute_group); - - /* ieee80211_unregister_hw call wil cause il_mac_stop to - * to be called and il4965_down since we are removing the device - * we need to set S_EXIT_PENDING bit. - */ - set_bit(S_EXIT_PENDING, &il->status); - - il_leds_exit(il); - - if (il->mac80211_registered) { - ieee80211_unregister_hw(il->hw); - il->mac80211_registered = 0; - } else { - il4965_down(il); - } - - /* - * Make sure device is reset to low power before unloading driver. - * This may be redundant with il4965_down(), but there are paths to - * run il4965_down() without calling apm_ops.stop(), and there are - * paths to avoid running il4965_down() at all before leaving driver. - * This (inexpensive) call *makes sure* device is reset. - */ - il_apm_stop(il); - - /* make sure we flush any pending irq or - * tasklet for the driver - */ - spin_lock_irqsave(&il->lock, flags); - il_disable_interrupts(il); - spin_unlock_irqrestore(&il->lock, flags); - - il4965_synchronize_irq(il); - - il4965_dealloc_ucode_pci(il); - - if (il->rxq.bd) - il4965_rx_queue_free(il, &il->rxq); - il4965_hw_txq_ctx_free(il); - - il_eeprom_free(il); - - /*netif_stop_queue(dev); */ - flush_workqueue(il->workqueue); - - /* ieee80211_unregister_hw calls il_mac_stop, which flushes - * il->workqueue... so we can't take down the workqueue - * until now... */ - destroy_workqueue(il->workqueue); - il->workqueue = NULL; - - free_irq(il->pci_dev->irq, il); - pci_disable_msi(il->pci_dev); - iounmap(il->hw_base); - pci_release_regions(pdev); - pci_disable_device(pdev); - - il4965_uninit_drv(il); - - dev_kfree_skb(il->beacon_skb); - - ieee80211_free_hw(il->hw); -} - -/* - * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask - * must be called under il->lock and mac access - */ -void -il4965_txq_set_sched(struct il_priv *il, u32 mask) -{ - il_wr_prph(il, IL49_SCD_TXFACT, mask); -} - -/***************************************************************************** - * - * driver and module entry point - * - *****************************************************************************/ - -/* Hardware specific file defines the PCI IDs table for that hardware module */ -static const struct pci_device_id il4965_hw_card_ids[] = { - {IL_PCI_DEVICE(0x4229, PCI_ANY_ID, il4965_cfg)}, - {IL_PCI_DEVICE(0x4230, PCI_ANY_ID, il4965_cfg)}, - {0} -}; -MODULE_DEVICE_TABLE(pci, il4965_hw_card_ids); - -static struct pci_driver il4965_driver = { - .name = DRV_NAME, - .id_table = il4965_hw_card_ids, - .probe = il4965_pci_probe, - .remove = il4965_pci_remove, - .driver.pm = IL_LEGACY_PM_OPS, -}; - -static int __init -il4965_init(void) -{ - - int ret; - pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); - pr_info(DRV_COPYRIGHT "\n"); - - ret = il4965_rate_control_register(); - if (ret) { - pr_err("Unable to register rate control algorithm: %d\n", ret); - return ret; - } - - ret = pci_register_driver(&il4965_driver); - if (ret) { - pr_err("Unable to initialize PCI module\n"); - goto error_register; - } - - return ret; - -error_register: - il4965_rate_control_unregister(); - return ret; -} - -static void __exit -il4965_exit(void) -{ - pci_unregister_driver(&il4965_driver); - il4965_rate_control_unregister(); -} - -module_exit(il4965_exit); -module_init(il4965_init); - -#ifdef CONFIG_IWLEGACY_DEBUG -module_param_named(debug, il_debug_level, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "debug output mask"); -#endif - -module_param_named(swcrypto, il4965_mod_params.sw_crypto, int, S_IRUGO); -MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); -module_param_named(queues_num, il4965_mod_params.num_of_queues, int, S_IRUGO); -MODULE_PARM_DESC(queues_num, "number of hw queues."); -module_param_named(11n_disable, il4965_mod_params.disable_11n, int, S_IRUGO); -MODULE_PARM_DESC(11n_disable, "disable 11n functionality"); -module_param_named(amsdu_size_8K, il4965_mod_params.amsdu_size_8K, int, - S_IRUGO); -MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0 [disabled])"); -module_param_named(fw_restart, il4965_mod_params.restart_fw, int, S_IRUGO); -MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/iwlegacy/4965-rs.c deleted file mode 100644 index bac60b2bc3f0..000000000000 --- a/drivers/net/wireless/iwlegacy/4965-rs.c +++ /dev/null @@ -1,2835 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "common.h" -#include "4965.h" - -#define IL4965_RS_NAME "iwl-4965-rs" - -#define NUM_TRY_BEFORE_ANT_TOGGLE 1 -#define IL_NUMBER_TRY 1 -#define IL_HT_NUMBER_TRY 3 - -#define RATE_MAX_WINDOW 62 /* # tx in history win */ -#define RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ -#define RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ - -/* max allowed rate miss before sync LQ cmd */ -#define IL_MISSED_RATE_MAX 15 -/* max time to accum history 2 seconds */ -#define RATE_SCALE_FLUSH_INTVL (3*HZ) - -static u8 rs_ht_to_legacy[] = { - RATE_6M_IDX, RATE_6M_IDX, - RATE_6M_IDX, RATE_6M_IDX, - RATE_6M_IDX, - RATE_6M_IDX, RATE_9M_IDX, - RATE_12M_IDX, RATE_18M_IDX, - RATE_24M_IDX, RATE_36M_IDX, - RATE_48M_IDX, RATE_54M_IDX -}; - -static const u8 ant_toggle_lookup[] = { - /*ANT_NONE -> */ ANT_NONE, - /*ANT_A -> */ ANT_B, - /*ANT_B -> */ ANT_C, - /*ANT_AB -> */ ANT_BC, - /*ANT_C -> */ ANT_A, - /*ANT_AC -> */ ANT_AB, - /*ANT_BC -> */ ANT_AC, - /*ANT_ABC -> */ ANT_ABC, -}; - -#define IL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ - [RATE_##r##M_IDX] = { RATE_##r##M_PLCP, \ - RATE_SISO_##s##M_PLCP, \ - RATE_MIMO2_##s##M_PLCP,\ - RATE_##r##M_IEEE, \ - RATE_##ip##M_IDX, \ - RATE_##in##M_IDX, \ - RATE_##rp##M_IDX, \ - RATE_##rn##M_IDX, \ - RATE_##pp##M_IDX, \ - RATE_##np##M_IDX } - -/* - * Parameter order: - * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate - * - * If there isn't a valid next or previous rate then INV is used which - * maps to RATE_INVALID - * - */ -const struct il_rate_info il_rates[RATE_COUNT] = { - IL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ - IL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ - IL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ - IL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ - IL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ - IL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ - IL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ - IL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ - IL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ - IL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ - IL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ - IL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ - IL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ -}; - -static int -il4965_hwrate_to_plcp_idx(u32 rate_n_flags) -{ - int idx = 0; - - /* HT rate format */ - if (rate_n_flags & RATE_MCS_HT_MSK) { - idx = (rate_n_flags & 0xff); - - if (idx >= RATE_MIMO2_6M_PLCP) - idx = idx - RATE_MIMO2_6M_PLCP; - - idx += IL_FIRST_OFDM_RATE; - /* skip 9M not supported in ht */ - if (idx >= RATE_9M_IDX) - idx += 1; - if (idx >= IL_FIRST_OFDM_RATE && idx <= IL_LAST_OFDM_RATE) - return idx; - - /* legacy rate format, search for match in table */ - } else { - for (idx = 0; idx < ARRAY_SIZE(il_rates); idx++) - if (il_rates[idx].plcp == (rate_n_flags & 0xFF)) - return idx; - } - - return -1; -} - -static void il4965_rs_rate_scale_perform(struct il_priv *il, - struct sk_buff *skb, - struct ieee80211_sta *sta, - struct il_lq_sta *lq_sta); -static void il4965_rs_fill_link_cmd(struct il_priv *il, - struct il_lq_sta *lq_sta, u32 rate_n_flags); -static void il4965_rs_stay_in_table(struct il_lq_sta *lq_sta, - bool force_search); - -#ifdef CONFIG_MAC80211_DEBUGFS -static void il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, - u32 *rate_n_flags, int idx); -#else -static void -il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx) -{ -} -#endif - -/** - * The following tables contain the expected throughput metrics for all rates - * - * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits - * - * where invalid entries are zeros. - * - * CCK rates are only valid in legacy table and will only be used in G - * (2.4 GHz) band. - */ - -static s32 expected_tpt_legacy[RATE_COUNT] = { - 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 -}; - -static s32 expected_tpt_siso20MHz[4][RATE_COUNT] = { - {0, 0, 0, 0, 42, 0, 76, 102, 124, 158, 183, 193, 202}, /* Norm */ - {0, 0, 0, 0, 46, 0, 82, 110, 132, 167, 192, 202, 210}, /* SGI */ - {0, 0, 0, 0, 48, 0, 93, 135, 176, 251, 319, 351, 381}, /* AGG */ - {0, 0, 0, 0, 53, 0, 102, 149, 193, 275, 348, 381, 413}, /* AGG+SGI */ -}; - -static s32 expected_tpt_siso40MHz[4][RATE_COUNT] = { - {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ - {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ - {0, 0, 0, 0, 96, 0, 182, 259, 328, 451, 553, 598, 640}, /* AGG */ - {0, 0, 0, 0, 106, 0, 199, 282, 357, 487, 593, 640, 683}, /* AGG+SGI */ -}; - -static s32 expected_tpt_mimo2_20MHz[4][RATE_COUNT] = { - {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250}, /* Norm */ - {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256}, /* SGI */ - {0, 0, 0, 0, 92, 0, 175, 250, 317, 436, 534, 578, 619}, /* AGG */ - {0, 0, 0, 0, 102, 0, 192, 273, 344, 470, 573, 619, 660}, /* AGG+SGI */ -}; - -static s32 expected_tpt_mimo2_40MHz[4][RATE_COUNT] = { - {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ - {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ - {0, 0, 0, 0, 180, 0, 327, 446, 545, 708, 828, 878, 922}, /* AGG */ - {0, 0, 0, 0, 197, 0, 355, 481, 584, 752, 872, 922, 966}, /* AGG+SGI */ -}; - -/* mbps, mcs */ -static const struct il_rate_mcs_info il_rate_mcs[RATE_COUNT] = { - {"1", "BPSK DSSS"}, - {"2", "QPSK DSSS"}, - {"5.5", "BPSK CCK"}, - {"11", "QPSK CCK"}, - {"6", "BPSK 1/2"}, - {"9", "BPSK 1/2"}, - {"12", "QPSK 1/2"}, - {"18", "QPSK 3/4"}, - {"24", "16QAM 1/2"}, - {"36", "16QAM 3/4"}, - {"48", "64QAM 2/3"}, - {"54", "64QAM 3/4"}, - {"60", "64QAM 5/6"}, -}; - -#define MCS_IDX_PER_STREAM (8) - -static inline u8 -il4965_rs_extract_rate(u32 rate_n_flags) -{ - return (u8) (rate_n_flags & 0xFF); -} - -static void -il4965_rs_rate_scale_clear_win(struct il_rate_scale_data *win) -{ - win->data = 0; - win->success_counter = 0; - win->success_ratio = IL_INVALID_VALUE; - win->counter = 0; - win->average_tpt = IL_INVALID_VALUE; - win->stamp = 0; -} - -static inline u8 -il4965_rs_is_valid_ant(u8 valid_antenna, u8 ant_type) -{ - return (ant_type & valid_antenna) == ant_type; -} - -/* - * removes the old data from the stats. All data that is older than - * TID_MAX_TIME_DIFF, will be deleted. - */ -static void -il4965_rs_tl_rm_old_stats(struct il_traffic_load *tl, u32 curr_time) -{ - /* The oldest age we want to keep */ - u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; - - while (tl->queue_count && tl->time_stamp < oldest_time) { - tl->total -= tl->packet_count[tl->head]; - tl->packet_count[tl->head] = 0; - tl->time_stamp += TID_QUEUE_CELL_SPACING; - tl->queue_count--; - tl->head++; - if (tl->head >= TID_QUEUE_MAX_SIZE) - tl->head = 0; - } -} - -/* - * increment traffic load value for tid and also remove - * any old values if passed the certain time period - */ -static u8 -il4965_rs_tl_add_packet(struct il_lq_sta *lq_data, struct ieee80211_hdr *hdr) -{ - u32 curr_time = jiffies_to_msecs(jiffies); - u32 time_diff; - s32 idx; - struct il_traffic_load *tl = NULL; - u8 tid; - - if (ieee80211_is_data_qos(hdr->frame_control)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & 0xf; - } else - return MAX_TID_COUNT; - - if (unlikely(tid >= TID_MAX_LOAD_COUNT)) - return MAX_TID_COUNT; - - tl = &lq_data->load[tid]; - - curr_time -= curr_time % TID_ROUND_VALUE; - - /* Happens only for the first packet. Initialize the data */ - if (!(tl->queue_count)) { - tl->total = 1; - tl->time_stamp = curr_time; - tl->queue_count = 1; - tl->head = 0; - tl->packet_count[0] = 1; - return MAX_TID_COUNT; - } - - time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); - idx = time_diff / TID_QUEUE_CELL_SPACING; - - /* The history is too long: remove data that is older than */ - /* TID_MAX_TIME_DIFF */ - if (idx >= TID_QUEUE_MAX_SIZE) - il4965_rs_tl_rm_old_stats(tl, curr_time); - - idx = (tl->head + idx) % TID_QUEUE_MAX_SIZE; - tl->packet_count[idx] = tl->packet_count[idx] + 1; - tl->total = tl->total + 1; - - if ((idx + 1) > tl->queue_count) - tl->queue_count = idx + 1; - - return tid; -} - -/* - get the traffic load value for tid -*/ -static u32 -il4965_rs_tl_get_load(struct il_lq_sta *lq_data, u8 tid) -{ - u32 curr_time = jiffies_to_msecs(jiffies); - u32 time_diff; - s32 idx; - struct il_traffic_load *tl = NULL; - - if (tid >= TID_MAX_LOAD_COUNT) - return 0; - - tl = &(lq_data->load[tid]); - - curr_time -= curr_time % TID_ROUND_VALUE; - - if (!(tl->queue_count)) - return 0; - - time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); - idx = time_diff / TID_QUEUE_CELL_SPACING; - - /* The history is too long: remove data that is older than */ - /* TID_MAX_TIME_DIFF */ - if (idx >= TID_QUEUE_MAX_SIZE) - il4965_rs_tl_rm_old_stats(tl, curr_time); - - return tl->total; -} - -static int -il4965_rs_tl_turn_on_agg_for_tid(struct il_priv *il, struct il_lq_sta *lq_data, - u8 tid, struct ieee80211_sta *sta) -{ - int ret = -EAGAIN; - u32 load; - - load = il4965_rs_tl_get_load(lq_data, tid); - - if (load > IL_AGG_LOAD_THRESHOLD) { - D_HT("Starting Tx agg: STA: %pM tid: %d\n", sta->addr, tid); - ret = ieee80211_start_tx_ba_session(sta, tid, 5000); - if (ret == -EAGAIN) { - /* - * driver and mac80211 is out of sync - * this might be cause by reloading firmware - * stop the tx ba session here - */ - IL_ERR("Fail start Tx agg on tid: %d\n", tid); - ieee80211_stop_tx_ba_session(sta, tid); - } - } else - D_HT("Aggregation not enabled for tid %d because load = %u\n", - tid, load); - - return ret; -} - -static void -il4965_rs_tl_turn_on_agg(struct il_priv *il, u8 tid, struct il_lq_sta *lq_data, - struct ieee80211_sta *sta) -{ - if (tid < TID_MAX_LOAD_COUNT) - il4965_rs_tl_turn_on_agg_for_tid(il, lq_data, tid, sta); - else - IL_ERR("tid exceeds max load count: %d/%d\n", tid, - TID_MAX_LOAD_COUNT); -} - -static inline int -il4965_get_il4965_num_of_ant_from_rate(u32 rate_n_flags) -{ - return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_C_MSK); -} - -/* - * Static function to get the expected throughput from an il_scale_tbl_info - * that wraps a NULL pointer check - */ -static s32 -il4965_get_expected_tpt(struct il_scale_tbl_info *tbl, int rs_idx) -{ - if (tbl->expected_tpt) - return tbl->expected_tpt[rs_idx]; - return 0; -} - -/** - * il4965_rs_collect_tx_data - Update the success/failure sliding win - * - * We keep a sliding win of the last 62 packets transmitted - * at this rate. win->data contains the bitmask of successful - * packets. - */ -static int -il4965_rs_collect_tx_data(struct il_scale_tbl_info *tbl, int scale_idx, - int attempts, int successes) -{ - struct il_rate_scale_data *win = NULL; - static const u64 mask = (((u64) 1) << (RATE_MAX_WINDOW - 1)); - s32 fail_count, tpt; - - if (scale_idx < 0 || scale_idx >= RATE_COUNT) - return -EINVAL; - - /* Select win for current tx bit rate */ - win = &(tbl->win[scale_idx]); - - /* Get expected throughput */ - tpt = il4965_get_expected_tpt(tbl, scale_idx); - - /* - * Keep track of only the latest 62 tx frame attempts in this rate's - * history win; anything older isn't really relevant any more. - * If we have filled up the sliding win, drop the oldest attempt; - * if the oldest attempt (highest bit in bitmap) shows "success", - * subtract "1" from the success counter (this is the main reason - * we keep these bitmaps!). - */ - while (attempts > 0) { - if (win->counter >= RATE_MAX_WINDOW) { - - /* remove earliest */ - win->counter = RATE_MAX_WINDOW - 1; - - if (win->data & mask) { - win->data &= ~mask; - win->success_counter--; - } - } - - /* Increment frames-attempted counter */ - win->counter++; - - /* Shift bitmap by one frame to throw away oldest history */ - win->data <<= 1; - - /* Mark the most recent #successes attempts as successful */ - if (successes > 0) { - win->success_counter++; - win->data |= 0x1; - successes--; - } - - attempts--; - } - - /* Calculate current success ratio, avoid divide-by-0! */ - if (win->counter > 0) - win->success_ratio = - 128 * (100 * win->success_counter) / win->counter; - else - win->success_ratio = IL_INVALID_VALUE; - - fail_count = win->counter - win->success_counter; - - /* Calculate average throughput, if we have enough history. */ - if (fail_count >= RATE_MIN_FAILURE_TH || - win->success_counter >= RATE_MIN_SUCCESS_TH) - win->average_tpt = (win->success_ratio * tpt + 64) / 128; - else - win->average_tpt = IL_INVALID_VALUE; - - /* Tag this win as having been updated */ - win->stamp = jiffies; - - return 0; -} - -/* - * Fill uCode API rate_n_flags field, based on "search" or "active" table. - */ -static u32 -il4965_rate_n_flags_from_tbl(struct il_priv *il, struct il_scale_tbl_info *tbl, - int idx, u8 use_green) -{ - u32 rate_n_flags = 0; - - if (is_legacy(tbl->lq_type)) { - rate_n_flags = il_rates[idx].plcp; - if (idx >= IL_FIRST_CCK_RATE && idx <= IL_LAST_CCK_RATE) - rate_n_flags |= RATE_MCS_CCK_MSK; - - } else if (is_Ht(tbl->lq_type)) { - if (idx > IL_LAST_OFDM_RATE) { - IL_ERR("Invalid HT rate idx %d\n", idx); - idx = IL_LAST_OFDM_RATE; - } - rate_n_flags = RATE_MCS_HT_MSK; - - if (is_siso(tbl->lq_type)) - rate_n_flags |= il_rates[idx].plcp_siso; - else - rate_n_flags |= il_rates[idx].plcp_mimo2; - } else { - IL_ERR("Invalid tbl->lq_type %d\n", tbl->lq_type); - } - - rate_n_flags |= - ((tbl->ant_type << RATE_MCS_ANT_POS) & RATE_MCS_ANT_ABC_MSK); - - if (is_Ht(tbl->lq_type)) { - if (tbl->is_ht40) { - if (tbl->is_dup) - rate_n_flags |= RATE_MCS_DUP_MSK; - else - rate_n_flags |= RATE_MCS_HT40_MSK; - } - if (tbl->is_SGI) - rate_n_flags |= RATE_MCS_SGI_MSK; - - if (use_green) { - rate_n_flags |= RATE_MCS_GF_MSK; - if (is_siso(tbl->lq_type) && tbl->is_SGI) { - rate_n_flags &= ~RATE_MCS_SGI_MSK; - IL_ERR("GF was set with SGI:SISO\n"); - } - } - } - return rate_n_flags; -} - -/* - * Interpret uCode API's rate_n_flags format, - * fill "search" or "active" tx mode table. - */ -static int -il4965_rs_get_tbl_info_from_mcs(const u32 rate_n_flags, - enum ieee80211_band band, - struct il_scale_tbl_info *tbl, int *rate_idx) -{ - u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); - u8 il4965_num_of_ant = - il4965_get_il4965_num_of_ant_from_rate(rate_n_flags); - u8 mcs; - - memset(tbl, 0, sizeof(struct il_scale_tbl_info)); - *rate_idx = il4965_hwrate_to_plcp_idx(rate_n_flags); - - if (*rate_idx == RATE_INVALID) { - *rate_idx = -1; - return -EINVAL; - } - tbl->is_SGI = 0; /* default legacy setup */ - tbl->is_ht40 = 0; - tbl->is_dup = 0; - tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); - tbl->lq_type = LQ_NONE; - tbl->max_search = IL_MAX_SEARCH; - - /* legacy rate format */ - if (!(rate_n_flags & RATE_MCS_HT_MSK)) { - if (il4965_num_of_ant == 1) { - if (band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; - else - tbl->lq_type = LQ_G; - } - /* HT rate format */ - } else { - if (rate_n_flags & RATE_MCS_SGI_MSK) - tbl->is_SGI = 1; - - if ((rate_n_flags & RATE_MCS_HT40_MSK) || - (rate_n_flags & RATE_MCS_DUP_MSK)) - tbl->is_ht40 = 1; - - if (rate_n_flags & RATE_MCS_DUP_MSK) - tbl->is_dup = 1; - - mcs = il4965_rs_extract_rate(rate_n_flags); - - /* SISO */ - if (mcs <= RATE_SISO_60M_PLCP) { - if (il4965_num_of_ant == 1) - tbl->lq_type = LQ_SISO; /*else NONE */ - /* MIMO2 */ - } else { - if (il4965_num_of_ant == 2) - tbl->lq_type = LQ_MIMO2; - } - } - return 0; -} - -/* switch to another antenna/antennas and return 1 */ -/* if no other valid antenna found, return 0 */ -static int -il4965_rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, - struct il_scale_tbl_info *tbl) -{ - u8 new_ant_type; - - if (!tbl->ant_type || tbl->ant_type > ANT_ABC) - return 0; - - if (!il4965_rs_is_valid_ant(valid_ant, tbl->ant_type)) - return 0; - - new_ant_type = ant_toggle_lookup[tbl->ant_type]; - - while (new_ant_type != tbl->ant_type && - !il4965_rs_is_valid_ant(valid_ant, new_ant_type)) - new_ant_type = ant_toggle_lookup[new_ant_type]; - - if (new_ant_type == tbl->ant_type) - return 0; - - tbl->ant_type = new_ant_type; - *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; - *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; - return 1; -} - -/** - * Green-field mode is valid if the station supports it and - * there are no non-GF stations present in the BSS. - */ -static bool -il4965_rs_use_green(struct il_priv *il, struct ieee80211_sta *sta) -{ - return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && - !il->ht.non_gf_sta_present; -} - -/** - * il4965_rs_get_supported_rates - get the available rates - * - * if management frame or broadcast frame only return - * basic available rates. - * - */ -static u16 -il4965_rs_get_supported_rates(struct il_lq_sta *lq_sta, - struct ieee80211_hdr *hdr, - enum il_table_type rate_type) -{ - if (is_legacy(rate_type)) { - return lq_sta->active_legacy_rate; - } else { - if (is_siso(rate_type)) - return lq_sta->active_siso_rate; - else - return lq_sta->active_mimo2_rate; - } -} - -static u16 -il4965_rs_get_adjacent_rate(struct il_priv *il, u8 idx, u16 rate_mask, - int rate_type) -{ - u8 high = RATE_INVALID; - u8 low = RATE_INVALID; - - /* 802.11A or ht walks to the next literal adjacent rate in - * the rate table */ - if (is_a_band(rate_type) || !is_legacy(rate_type)) { - int i; - u32 mask; - - /* Find the previous rate that is in the rate mask */ - i = idx - 1; - for (mask = (1 << i); i >= 0; i--, mask >>= 1) { - if (rate_mask & mask) { - low = i; - break; - } - } - - /* Find the next rate that is in the rate mask */ - i = idx + 1; - for (mask = (1 << i); i < RATE_COUNT; i++, mask <<= 1) { - if (rate_mask & mask) { - high = i; - break; - } - } - - return (high << 8) | low; - } - - low = idx; - while (low != RATE_INVALID) { - low = il_rates[low].prev_rs; - if (low == RATE_INVALID) - break; - if (rate_mask & (1 << low)) - break; - D_RATE("Skipping masked lower rate: %d\n", low); - } - - high = idx; - while (high != RATE_INVALID) { - high = il_rates[high].next_rs; - if (high == RATE_INVALID) - break; - if (rate_mask & (1 << high)) - break; - D_RATE("Skipping masked higher rate: %d\n", high); - } - - return (high << 8) | low; -} - -static u32 -il4965_rs_get_lower_rate(struct il_lq_sta *lq_sta, - struct il_scale_tbl_info *tbl, u8 scale_idx, - u8 ht_possible) -{ - s32 low; - u16 rate_mask; - u16 high_low; - u8 switch_to_legacy = 0; - u8 is_green = lq_sta->is_green; - struct il_priv *il = lq_sta->drv; - - /* check if we need to switch from HT to legacy rates. - * assumption is that mandatory rates (1Mbps or 6Mbps) - * are always supported (spec demand) */ - if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_idx)) { - switch_to_legacy = 1; - scale_idx = rs_ht_to_legacy[scale_idx]; - if (lq_sta->band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; - else - tbl->lq_type = LQ_G; - - if (il4965_num_of_ant(tbl->ant_type) > 1) - tbl->ant_type = - il4965_first_antenna(il->hw_params.valid_tx_ant); - - tbl->is_ht40 = 0; - tbl->is_SGI = 0; - tbl->max_search = IL_MAX_SEARCH; - } - - rate_mask = il4965_rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); - - /* Mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { - /* supp_rates has no CCK bits in A mode */ - if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate_mask = - (u16) (rate_mask & - (lq_sta->supp_rates << IL_FIRST_OFDM_RATE)); - else - rate_mask = (u16) (rate_mask & lq_sta->supp_rates); - } - - /* If we switched from HT to legacy, check current rate */ - if (switch_to_legacy && (rate_mask & (1 << scale_idx))) { - low = scale_idx; - goto out; - } - - high_low = - il4965_rs_get_adjacent_rate(lq_sta->drv, scale_idx, rate_mask, - tbl->lq_type); - low = high_low & 0xff; - - if (low == RATE_INVALID) - low = scale_idx; - -out: - return il4965_rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); -} - -/* - * Simple function to compare two rate scale table types - */ -static bool -il4965_table_type_matches(struct il_scale_tbl_info *a, - struct il_scale_tbl_info *b) -{ - return (a->lq_type == b->lq_type && a->ant_type == b->ant_type && - a->is_SGI == b->is_SGI); -} - -/* - * mac80211 sends us Tx status - */ -static void -il4965_rs_tx_status(void *il_r, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *il_sta, - struct sk_buff *skb) -{ - int legacy_success; - int retries; - int rs_idx, mac_idx, i; - struct il_lq_sta *lq_sta = il_sta; - struct il_link_quality_cmd *table; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct il_priv *il = (struct il_priv *)il_r; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - enum mac80211_rate_control_flags mac_flags; - u32 tx_rate; - struct il_scale_tbl_info tbl_type; - struct il_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; - - D_RATE("get frame ack response, update rate scale win\n"); - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (!lq_sta) { - D_RATE("Station rate scaling not created yet.\n"); - return; - } else if (!lq_sta->drv) { - D_RATE("Rate scaling not initialized yet.\n"); - return; - } - - if (!ieee80211_is_data(hdr->frame_control) || - (info->flags & IEEE80211_TX_CTL_NO_ACK)) - return; - - /* This packet was aggregated but doesn't carry status info */ - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && - !(info->flags & IEEE80211_TX_STAT_AMPDU)) - return; - - /* - * Ignore this Tx frame response if its initial rate doesn't match - * that of latest Link Quality command. There may be stragglers - * from a previous Link Quality command, but we're no longer interested - * in those; they're either from the "active" mode while we're trying - * to check "search" mode, or a prior "search" mode after we've moved - * to a new "search" mode (which might become the new "active" mode). - */ - table = &lq_sta->lq; - tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); - il4965_rs_get_tbl_info_from_mcs(tx_rate, il->band, &tbl_type, &rs_idx); - if (il->band == IEEE80211_BAND_5GHZ) - rs_idx -= IL_FIRST_OFDM_RATE; - mac_flags = info->status.rates[0].flags; - mac_idx = info->status.rates[0].idx; - /* For HT packets, map MCS to PLCP */ - if (mac_flags & IEEE80211_TX_RC_MCS) { - mac_idx &= RATE_MCS_CODE_MSK; /* Remove # of streams */ - if (mac_idx >= (RATE_9M_IDX - IL_FIRST_OFDM_RATE)) - mac_idx++; - /* - * mac80211 HT idx is always zero-idxed; we need to move - * HT OFDM rates after CCK rates in 2.4 GHz band - */ - if (il->band == IEEE80211_BAND_2GHZ) - mac_idx += IL_FIRST_OFDM_RATE; - } - /* Here we actually compare this rate to the latest LQ command */ - if (mac_idx < 0 || - tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI) || - tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH) || - tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA) || - tbl_type.ant_type != info->status.antenna || - !!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS) - || !!(tx_rate & RATE_MCS_GF_MSK) != - !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD) || rs_idx != mac_idx) { - D_RATE("initial rate %d does not match %d (0x%x)\n", mac_idx, - rs_idx, tx_rate); - /* - * Since rates mis-match, the last LQ command may have failed. - * After IL_MISSED_RATE_MAX mis-matches, resync the uCode with - * ... driver. - */ - lq_sta->missed_rate_counter++; - if (lq_sta->missed_rate_counter > IL_MISSED_RATE_MAX) { - lq_sta->missed_rate_counter = 0; - il_send_lq_cmd(il, &lq_sta->lq, CMD_ASYNC, false); - } - /* Regardless, ignore this status info for outdated rate */ - return; - } else - /* Rate did match, so reset the missed_rate_counter */ - lq_sta->missed_rate_counter = 0; - - /* Figure out if rate scale algorithm is in active or search table */ - if (il4965_table_type_matches - (&tbl_type, &(lq_sta->lq_info[lq_sta->active_tbl]))) { - curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - } else - if (il4965_table_type_matches - (&tbl_type, &lq_sta->lq_info[1 - lq_sta->active_tbl])) { - curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - } else { - D_RATE("Neither active nor search matches tx rate\n"); - tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - D_RATE("active- lq:%x, ant:%x, SGI:%d\n", tmp_tbl->lq_type, - tmp_tbl->ant_type, tmp_tbl->is_SGI); - tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - D_RATE("search- lq:%x, ant:%x, SGI:%d\n", tmp_tbl->lq_type, - tmp_tbl->ant_type, tmp_tbl->is_SGI); - D_RATE("actual- lq:%x, ant:%x, SGI:%d\n", tbl_type.lq_type, - tbl_type.ant_type, tbl_type.is_SGI); - /* - * no matching table found, let's by-pass the data collection - * and continue to perform rate scale to find the rate table - */ - il4965_rs_stay_in_table(lq_sta, true); - goto done; - } - - /* - * Updating the frame history depends on whether packets were - * aggregated. - * - * For aggregation, all packets were transmitted at the same rate, the - * first idx into rate scale table. - */ - if (info->flags & IEEE80211_TX_STAT_AMPDU) { - tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); - il4965_rs_get_tbl_info_from_mcs(tx_rate, il->band, &tbl_type, - &rs_idx); - il4965_rs_collect_tx_data(curr_tbl, rs_idx, - info->status.ampdu_len, - info->status.ampdu_ack_len); - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->stay_in_tbl) { - lq_sta->total_success += info->status.ampdu_ack_len; - lq_sta->total_failed += - (info->status.ampdu_len - - info->status.ampdu_ack_len); - } - } else { - /* - * For legacy, update frame history with for each Tx retry. - */ - retries = info->status.rates[0].count - 1; - /* HW doesn't send more than 15 retries */ - retries = min(retries, 15); - - /* The last transmission may have been successful */ - legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); - /* Collect data for each rate used during failed TX attempts */ - for (i = 0; i <= retries; ++i) { - tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags); - il4965_rs_get_tbl_info_from_mcs(tx_rate, il->band, - &tbl_type, &rs_idx); - /* - * Only collect stats if retried rate is in the same RS - * table as active/search. - */ - if (il4965_table_type_matches(&tbl_type, curr_tbl)) - tmp_tbl = curr_tbl; - else if (il4965_table_type_matches - (&tbl_type, other_tbl)) - tmp_tbl = other_tbl; - else - continue; - il4965_rs_collect_tx_data(tmp_tbl, rs_idx, 1, - i < - retries ? 0 : legacy_success); - } - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->stay_in_tbl) { - lq_sta->total_success += legacy_success; - lq_sta->total_failed += retries + (1 - legacy_success); - } - } - /* The last TX rate is cached in lq_sta; it's set in if/else above */ - lq_sta->last_rate_n_flags = tx_rate; -done: - /* See if there's a better rate or modulation mode to try. */ - if (sta->supp_rates[sband->band]) - il4965_rs_rate_scale_perform(il, skb, sta, lq_sta); -} - -/* - * Begin a period of staying with a selected modulation mode. - * Set "stay_in_tbl" flag to prevent any mode switches. - * Set frame tx success limits according to legacy vs. high-throughput, - * and reset overall (spanning all rates) tx success history stats. - * These control how long we stay using same modulation mode before - * searching for a new mode. - */ -static void -il4965_rs_set_stay_in_table(struct il_priv *il, u8 is_legacy, - struct il_lq_sta *lq_sta) -{ - D_RATE("we are staying in the same table\n"); - lq_sta->stay_in_tbl = 1; /* only place this gets set */ - if (is_legacy) { - lq_sta->table_count_limit = IL_LEGACY_TBL_COUNT; - lq_sta->max_failure_limit = IL_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IL_LEGACY_SUCCESS_LIMIT; - } else { - lq_sta->table_count_limit = IL_NONE_LEGACY_TBL_COUNT; - lq_sta->max_failure_limit = IL_NONE_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IL_NONE_LEGACY_SUCCESS_LIMIT; - } - lq_sta->table_count = 0; - lq_sta->total_failed = 0; - lq_sta->total_success = 0; - lq_sta->flush_timer = jiffies; - lq_sta->action_counter = 0; -} - -/* - * Find correct throughput table for given mode of modulation - */ -static void -il4965_rs_set_expected_tpt_table(struct il_lq_sta *lq_sta, - struct il_scale_tbl_info *tbl) -{ - /* Used to choose among HT tables */ - s32(*ht_tbl_pointer)[RATE_COUNT]; - - /* Check for invalid LQ type */ - if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { - tbl->expected_tpt = expected_tpt_legacy; - return; - } - - /* Legacy rates have only one table */ - if (is_legacy(tbl->lq_type)) { - tbl->expected_tpt = expected_tpt_legacy; - return; - } - - /* Choose among many HT tables depending on number of streams - * (SISO/MIMO2), channel width (20/40), SGI, and aggregation - * status */ - if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) - ht_tbl_pointer = expected_tpt_siso20MHz; - else if (is_siso(tbl->lq_type)) - ht_tbl_pointer = expected_tpt_siso40MHz; - else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) - ht_tbl_pointer = expected_tpt_mimo2_20MHz; - else /* if (is_mimo2(tbl->lq_type)) <-- must be true */ - ht_tbl_pointer = expected_tpt_mimo2_40MHz; - - if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ - tbl->expected_tpt = ht_tbl_pointer[0]; - else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ - tbl->expected_tpt = ht_tbl_pointer[1]; - else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ - tbl->expected_tpt = ht_tbl_pointer[2]; - else /* AGG+SGI */ - tbl->expected_tpt = ht_tbl_pointer[3]; -} - -/* - * Find starting rate for new "search" high-throughput mode of modulation. - * Goal is to find lowest expected rate (under perfect conditions) that is - * above the current measured throughput of "active" mode, to give new mode - * a fair chance to prove itself without too many challenges. - * - * This gets called when transitioning to more aggressive modulation - * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive - * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need - * to decrease to match "active" throughput. When moving from MIMO to SISO, - * bit rate will typically need to increase, but not if performance was bad. - */ -static s32 -il4965_rs_get_best_rate(struct il_priv *il, struct il_lq_sta *lq_sta, - struct il_scale_tbl_info *tbl, /* "search" */ - u16 rate_mask, s8 idx) -{ - /* "active" values */ - struct il_scale_tbl_info *active_tbl = - &(lq_sta->lq_info[lq_sta->active_tbl]); - s32 active_sr = active_tbl->win[idx].success_ratio; - s32 active_tpt = active_tbl->expected_tpt[idx]; - - /* expected "search" throughput */ - s32 *tpt_tbl = tbl->expected_tpt; - - s32 new_rate, high, low, start_hi; - u16 high_low; - s8 rate = idx; - - new_rate = high = low = start_hi = RATE_INVALID; - - for (;;) { - high_low = - il4965_rs_get_adjacent_rate(il, rate, rate_mask, - tbl->lq_type); - - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - /* - * Lower the "search" bit rate, to give new "search" mode - * approximately the same throughput as "active" if: - * - * 1) "Active" mode has been working modestly well (but not - * great), and expected "search" throughput (under perfect - * conditions) at candidate rate is above the actual - * measured "active" throughput (but less than expected - * "active" throughput under perfect conditions). - * OR - * 2) "Active" mode has been working perfectly or very well - * and expected "search" throughput (under perfect - * conditions) at candidate rate is above expected - * "active" throughput (under perfect conditions). - */ - if ((100 * tpt_tbl[rate] > lq_sta->last_tpt && - (active_sr > RATE_DECREASE_TH && active_sr <= RATE_HIGH_TH - && tpt_tbl[rate] <= active_tpt)) || - (active_sr >= RATE_SCALE_SWITCH && - tpt_tbl[rate] > active_tpt)) { - - /* (2nd or later pass) - * If we've already tried to raise the rate, and are - * now trying to lower it, use the higher rate. */ - if (start_hi != RATE_INVALID) { - new_rate = start_hi; - break; - } - - new_rate = rate; - - /* Loop again with lower rate */ - if (low != RATE_INVALID) - rate = low; - - /* Lower rate not available, use the original */ - else - break; - - /* Else try to raise the "search" rate to match "active" */ - } else { - /* (2nd or later pass) - * If we've already tried to lower the rate, and are - * now trying to raise it, use the lower rate. */ - if (new_rate != RATE_INVALID) - break; - - /* Loop again with higher rate */ - else if (high != RATE_INVALID) { - start_hi = high; - rate = high; - - /* Higher rate not available, use the original */ - } else { - new_rate = rate; - break; - } - } - } - - return new_rate; -} - -/* - * Set up search table for MIMO2 - */ -static int -il4965_rs_switch_to_mimo2(struct il_priv *il, struct il_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, - struct il_scale_tbl_info *tbl, int idx) -{ - u16 rate_mask; - s32 rate; - s8 is_green = lq_sta->is_green; - - if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) - return -1; - - if (sta->smps_mode == IEEE80211_SMPS_STATIC) - return -1; - - /* Need both Tx chains/antennas to support MIMO */ - if (il->hw_params.tx_chains_num < 2) - return -1; - - D_RATE("LQ: try to switch to MIMO2\n"); - - tbl->lq_type = LQ_MIMO2; - tbl->is_dup = lq_sta->is_dup; - tbl->action = 0; - tbl->max_search = IL_MAX_SEARCH; - rate_mask = lq_sta->active_mimo2_rate; - - if (il_is_ht40_tx_allowed(il, &sta->ht_cap)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - il4965_rs_set_expected_tpt_table(lq_sta, tbl); - - rate = il4965_rs_get_best_rate(il, lq_sta, tbl, rate_mask, idx); - - D_RATE("LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask); - if (rate == RATE_INVALID || !((1 << rate) & rate_mask)) { - D_RATE("Can't switch with idx %d rate mask %x\n", rate, - rate_mask); - return -1; - } - tbl->current_rate = - il4965_rate_n_flags_from_tbl(il, tbl, rate, is_green); - - D_RATE("LQ: Switch to new mcs %X idx is green %X\n", tbl->current_rate, - is_green); - return 0; -} - -/* - * Set up search table for SISO - */ -static int -il4965_rs_switch_to_siso(struct il_priv *il, struct il_lq_sta *lq_sta, - struct ieee80211_conf *conf, struct ieee80211_sta *sta, - struct il_scale_tbl_info *tbl, int idx) -{ - u16 rate_mask; - u8 is_green = lq_sta->is_green; - s32 rate; - - if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) - return -1; - - D_RATE("LQ: try to switch to SISO\n"); - - tbl->is_dup = lq_sta->is_dup; - tbl->lq_type = LQ_SISO; - tbl->action = 0; - tbl->max_search = IL_MAX_SEARCH; - rate_mask = lq_sta->active_siso_rate; - - if (il_is_ht40_tx_allowed(il, &sta->ht_cap)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - if (is_green) - tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield */ - - il4965_rs_set_expected_tpt_table(lq_sta, tbl); - rate = il4965_rs_get_best_rate(il, lq_sta, tbl, rate_mask, idx); - - D_RATE("LQ: get best rate %d mask %X\n", rate, rate_mask); - if (rate == RATE_INVALID || !((1 << rate) & rate_mask)) { - D_RATE("can not switch with idx %d rate mask %x\n", rate, - rate_mask); - return -1; - } - tbl->current_rate = - il4965_rate_n_flags_from_tbl(il, tbl, rate, is_green); - D_RATE("LQ: Switch to new mcs %X idx is green %X\n", tbl->current_rate, - is_green); - return 0; -} - -/* - * Try to switch to new modulation mode from legacy - */ -static int -il4965_rs_move_legacy_other(struct il_priv *il, struct il_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, int idx) -{ - struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct il_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct il_rate_scale_data *win = &(tbl->win[idx]); - u32 sz = - (sizeof(struct il_scale_tbl_info) - - (sizeof(struct il_rate_scale_data) * RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = il->hw_params.valid_tx_ant; - u8 tx_chains_num = il->hw_params.tx_chains_num; - int ret = 0; - u8 update_search_tbl_counter = 0; - - tbl->action = IL_LEGACY_SWITCH_SISO; - - start_action = tbl->action; - for (;;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IL_LEGACY_SWITCH_ANTENNA1: - case IL_LEGACY_SWITCH_ANTENNA2: - D_RATE("LQ: Legacy toggle Antenna\n"); - - if ((tbl->action == IL_LEGACY_SWITCH_ANTENNA1 && - tx_chains_num <= 1) || - (tbl->action == IL_LEGACY_SWITCH_ANTENNA2 && - tx_chains_num <= 2)) - break; - - /* Don't change antenna if success has been great */ - if (win->success_ratio >= IL_RS_GOOD_RATIO) - break; - - /* Set up search table to try other antenna */ - memcpy(search_tbl, tbl, sz); - - if (il4965_rs_toggle_antenna - (valid_tx_ant, &search_tbl->current_rate, - search_tbl)) { - update_search_tbl_counter = 1; - il4965_rs_set_expected_tpt_table(lq_sta, - search_tbl); - goto out; - } - break; - case IL_LEGACY_SWITCH_SISO: - D_RATE("LQ: Legacy switch to SISO\n"); - - /* Set up search table to try SISO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - ret = - il4965_rs_switch_to_siso(il, lq_sta, conf, sta, - search_tbl, idx); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - - break; - case IL_LEGACY_SWITCH_MIMO2_AB: - case IL_LEGACY_SWITCH_MIMO2_AC: - case IL_LEGACY_SWITCH_MIMO2_BC: - D_RATE("LQ: Legacy switch to MIMO2\n"); - - /* Set up search table to try MIMO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - if (tbl->action == IL_LEGACY_SWITCH_MIMO2_AB) - search_tbl->ant_type = ANT_AB; - else if (tbl->action == IL_LEGACY_SWITCH_MIMO2_AC) - search_tbl->ant_type = ANT_AC; - else - search_tbl->ant_type = ANT_BC; - - if (!il4965_rs_is_valid_ant - (valid_tx_ant, search_tbl->ant_type)) - break; - - ret = - il4965_rs_switch_to_mimo2(il, lq_sta, conf, sta, - search_tbl, idx); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - break; - } - tbl->action++; - if (tbl->action > IL_LEGACY_SWITCH_MIMO2_BC) - tbl->action = IL_LEGACY_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - - } - search_tbl->lq_type = LQ_NONE; - return 0; - -out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IL_LEGACY_SWITCH_MIMO2_BC) - tbl->action = IL_LEGACY_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - return 0; - -} - -/* - * Try to switch to new modulation mode from SISO - */ -static int -il4965_rs_move_siso_to_other(struct il_priv *il, struct il_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, int idx) -{ - u8 is_green = lq_sta->is_green; - struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct il_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct il_rate_scale_data *win = &(tbl->win[idx]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - u32 sz = - (sizeof(struct il_scale_tbl_info) - - (sizeof(struct il_rate_scale_data) * RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = il->hw_params.valid_tx_ant; - u8 tx_chains_num = il->hw_params.tx_chains_num; - u8 update_search_tbl_counter = 0; - int ret; - - start_action = tbl->action; - - for (;;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IL_SISO_SWITCH_ANTENNA1: - case IL_SISO_SWITCH_ANTENNA2: - D_RATE("LQ: SISO toggle Antenna\n"); - if ((tbl->action == IL_SISO_SWITCH_ANTENNA1 && - tx_chains_num <= 1) || - (tbl->action == IL_SISO_SWITCH_ANTENNA2 && - tx_chains_num <= 2)) - break; - - if (win->success_ratio >= IL_RS_GOOD_RATIO) - break; - - memcpy(search_tbl, tbl, sz); - if (il4965_rs_toggle_antenna - (valid_tx_ant, &search_tbl->current_rate, - search_tbl)) { - update_search_tbl_counter = 1; - goto out; - } - break; - case IL_SISO_SWITCH_MIMO2_AB: - case IL_SISO_SWITCH_MIMO2_AC: - case IL_SISO_SWITCH_MIMO2_BC: - D_RATE("LQ: SISO switch to MIMO2\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - if (tbl->action == IL_SISO_SWITCH_MIMO2_AB) - search_tbl->ant_type = ANT_AB; - else if (tbl->action == IL_SISO_SWITCH_MIMO2_AC) - search_tbl->ant_type = ANT_AC; - else - search_tbl->ant_type = ANT_BC; - - if (!il4965_rs_is_valid_ant - (valid_tx_ant, search_tbl->ant_type)) - break; - - ret = - il4965_rs_switch_to_mimo2(il, lq_sta, conf, sta, - search_tbl, idx); - if (!ret) - goto out; - break; - case IL_SISO_SWITCH_GI: - if (!tbl->is_ht40 && - !(ht_cap->cap & IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && - !(ht_cap->cap & IEEE80211_HT_CAP_SGI_40)) - break; - - D_RATE("LQ: SISO toggle SGI/NGI\n"); - - memcpy(search_tbl, tbl, sz); - if (is_green) { - if (!tbl->is_SGI) - break; - else - IL_ERR("SGI was set in GF+SISO\n"); - } - search_tbl->is_SGI = !tbl->is_SGI; - il4965_rs_set_expected_tpt_table(lq_sta, search_tbl); - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[idx]) - break; - } - search_tbl->current_rate = - il4965_rate_n_flags_from_tbl(il, search_tbl, idx, - is_green); - update_search_tbl_counter = 1; - goto out; - } - tbl->action++; - if (tbl->action > IL_SISO_SWITCH_GI) - tbl->action = IL_SISO_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return 0; - -out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IL_SISO_SWITCH_GI) - tbl->action = IL_SISO_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - - return 0; -} - -/* - * Try to switch to new modulation mode from MIMO2 - */ -static int -il4965_rs_move_mimo2_to_other(struct il_priv *il, struct il_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, int idx) -{ - s8 is_green = lq_sta->is_green; - struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct il_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct il_rate_scale_data *win = &(tbl->win[idx]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - u32 sz = - (sizeof(struct il_scale_tbl_info) - - (sizeof(struct il_rate_scale_data) * RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = il->hw_params.valid_tx_ant; - u8 tx_chains_num = il->hw_params.tx_chains_num; - u8 update_search_tbl_counter = 0; - int ret; - - start_action = tbl->action; - for (;;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IL_MIMO2_SWITCH_ANTENNA1: - case IL_MIMO2_SWITCH_ANTENNA2: - D_RATE("LQ: MIMO2 toggle Antennas\n"); - - if (tx_chains_num <= 2) - break; - - if (win->success_ratio >= IL_RS_GOOD_RATIO) - break; - - memcpy(search_tbl, tbl, sz); - if (il4965_rs_toggle_antenna - (valid_tx_ant, &search_tbl->current_rate, - search_tbl)) { - update_search_tbl_counter = 1; - goto out; - } - break; - case IL_MIMO2_SWITCH_SISO_A: - case IL_MIMO2_SWITCH_SISO_B: - case IL_MIMO2_SWITCH_SISO_C: - D_RATE("LQ: MIMO2 switch to SISO\n"); - - /* Set up new search table for SISO */ - memcpy(search_tbl, tbl, sz); - - if (tbl->action == IL_MIMO2_SWITCH_SISO_A) - search_tbl->ant_type = ANT_A; - else if (tbl->action == IL_MIMO2_SWITCH_SISO_B) - search_tbl->ant_type = ANT_B; - else - search_tbl->ant_type = ANT_C; - - if (!il4965_rs_is_valid_ant - (valid_tx_ant, search_tbl->ant_type)) - break; - - ret = - il4965_rs_switch_to_siso(il, lq_sta, conf, sta, - search_tbl, idx); - if (!ret) - goto out; - - break; - - case IL_MIMO2_SWITCH_GI: - if (!tbl->is_ht40 && - !(ht_cap->cap & IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && - !(ht_cap->cap & IEEE80211_HT_CAP_SGI_40)) - break; - - D_RATE("LQ: MIMO2 toggle SGI/NGI\n"); - - /* Set up new search table for MIMO2 */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = !tbl->is_SGI; - il4965_rs_set_expected_tpt_table(lq_sta, search_tbl); - /* - * If active table already uses the fastest possible - * modulation (dual stream with short guard interval), - * and it's working well, there's no need to look - * for a better type of modulation! - */ - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[idx]) - break; - } - search_tbl->current_rate = - il4965_rate_n_flags_from_tbl(il, search_tbl, idx, - is_green); - update_search_tbl_counter = 1; - goto out; - - } - tbl->action++; - if (tbl->action > IL_MIMO2_SWITCH_GI) - tbl->action = IL_MIMO2_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return 0; -out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IL_MIMO2_SWITCH_GI) - tbl->action = IL_MIMO2_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - - return 0; - -} - -/* - * Check whether we should continue using same modulation mode, or - * begin search for a new mode, based on: - * 1) # tx successes or failures while using this mode - * 2) # times calling this function - * 3) elapsed time in this mode (not used, for now) - */ -static void -il4965_rs_stay_in_table(struct il_lq_sta *lq_sta, bool force_search) -{ - struct il_scale_tbl_info *tbl; - int i; - int active_tbl; - int flush_interval_passed = 0; - struct il_priv *il; - - il = lq_sta->drv; - active_tbl = lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - - /* If we've been disallowing search, see if we should now allow it */ - if (lq_sta->stay_in_tbl) { - - /* Elapsed time using current modulation mode */ - if (lq_sta->flush_timer) - flush_interval_passed = - time_after(jiffies, - (unsigned long)(lq_sta->flush_timer + - RATE_SCALE_FLUSH_INTVL)); - - /* - * Check if we should allow search for new modulation mode. - * If many frames have failed or succeeded, or we've used - * this same modulation for a long time, allow search, and - * reset history stats that keep track of whether we should - * allow a new search. Also (below) reset all bitmaps and - * stats in active history. - */ - if (force_search || - lq_sta->total_failed > lq_sta->max_failure_limit || - lq_sta->total_success > lq_sta->max_success_limit || - (!lq_sta->search_better_tbl && lq_sta->flush_timer && - flush_interval_passed)) { - D_RATE("LQ: stay is expired %d %d %d\n", - lq_sta->total_failed, lq_sta->total_success, - flush_interval_passed); - - /* Allow search for new mode */ - lq_sta->stay_in_tbl = 0; /* only place reset */ - lq_sta->total_failed = 0; - lq_sta->total_success = 0; - lq_sta->flush_timer = 0; - - /* - * Else if we've used this modulation mode enough repetitions - * (regardless of elapsed time or success/failure), reset - * history bitmaps and rate-specific stats for all rates in - * active table. - */ - } else { - lq_sta->table_count++; - if (lq_sta->table_count >= lq_sta->table_count_limit) { - lq_sta->table_count = 0; - - D_RATE("LQ: stay in table clear win\n"); - for (i = 0; i < RATE_COUNT; i++) - il4965_rs_rate_scale_clear_win(& - (tbl-> - win - [i])); - } - } - - /* If transitioning to allow "search", reset all history - * bitmaps and stats in active table (this will become the new - * "search" table). */ - if (!lq_sta->stay_in_tbl) { - for (i = 0; i < RATE_COUNT; i++) - il4965_rs_rate_scale_clear_win(&(tbl->win[i])); - } - } -} - -/* - * setup rate table in uCode - */ -static void -il4965_rs_update_rate_tbl(struct il_priv *il, struct il_lq_sta *lq_sta, - struct il_scale_tbl_info *tbl, int idx, u8 is_green) -{ - u32 rate; - - /* Update uCode's rate table. */ - rate = il4965_rate_n_flags_from_tbl(il, tbl, idx, is_green); - il4965_rs_fill_link_cmd(il, lq_sta, rate); - il_send_lq_cmd(il, &lq_sta->lq, CMD_ASYNC, false); -} - -/* - * Do rate scaling and search for new modulation mode. - */ -static void -il4965_rs_rate_scale_perform(struct il_priv *il, struct sk_buff *skb, - struct ieee80211_sta *sta, - struct il_lq_sta *lq_sta) -{ - struct ieee80211_hw *hw = il->hw; - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - int low = RATE_INVALID; - int high = RATE_INVALID; - int idx; - int i; - struct il_rate_scale_data *win = NULL; - int current_tpt = IL_INVALID_VALUE; - int low_tpt = IL_INVALID_VALUE; - int high_tpt = IL_INVALID_VALUE; - u32 fail_count; - s8 scale_action = 0; - u16 rate_mask; - u8 update_lq = 0; - struct il_scale_tbl_info *tbl, *tbl1; - u16 rate_scale_idx_msk = 0; - u8 is_green = 0; - u8 active_tbl = 0; - u8 done_search = 0; - u16 high_low; - s32 sr; - u8 tid = MAX_TID_COUNT; - struct il_tid_data *tid_data; - - D_RATE("rate scale calculate new rate for skb\n"); - - /* Send management frames and NO_ACK data using lowest rate. */ - /* TODO: this could probably be improved.. */ - if (!ieee80211_is_data(hdr->frame_control) || - (info->flags & IEEE80211_TX_CTL_NO_ACK)) - return; - - lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; - - tid = il4965_rs_tl_add_packet(lq_sta, hdr); - if (tid != MAX_TID_COUNT && (lq_sta->tx_agg_tid_en & (1 << tid))) { - tid_data = &il->stations[lq_sta->lq.sta_id].tid[tid]; - if (tid_data->agg.state == IL_AGG_OFF) - lq_sta->is_agg = 0; - else - lq_sta->is_agg = 1; - } else - lq_sta->is_agg = 0; - - /* - * Select rate-scale / modulation-mode table to work with in - * the rest of this function: "search" if searching for better - * modulation mode, or "active" if doing rate scaling within a mode. - */ - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - if (is_legacy(tbl->lq_type)) - lq_sta->is_green = 0; - else - lq_sta->is_green = il4965_rs_use_green(il, sta); - is_green = lq_sta->is_green; - - /* current tx rate */ - idx = lq_sta->last_txrate_idx; - - D_RATE("Rate scale idx %d for type %d\n", idx, tbl->lq_type); - - /* rates available for this association, and for modulation mode */ - rate_mask = il4965_rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); - - D_RATE("mask 0x%04X\n", rate_mask); - - /* mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { - if (lq_sta->band == IEEE80211_BAND_5GHZ) - /* supp_rates has no CCK bits in A mode */ - rate_scale_idx_msk = - (u16) (rate_mask & - (lq_sta->supp_rates << IL_FIRST_OFDM_RATE)); - else - rate_scale_idx_msk = - (u16) (rate_mask & lq_sta->supp_rates); - - } else - rate_scale_idx_msk = rate_mask; - - if (!rate_scale_idx_msk) - rate_scale_idx_msk = rate_mask; - - if (!((1 << idx) & rate_scale_idx_msk)) { - IL_ERR("Current Rate is not valid\n"); - if (lq_sta->search_better_tbl) { - /* revert to active table if search table is not valid */ - tbl->lq_type = LQ_NONE; - lq_sta->search_better_tbl = 0; - tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - /* get "active" rate info */ - idx = il4965_hwrate_to_plcp_idx(tbl->current_rate); - il4965_rs_update_rate_tbl(il, lq_sta, tbl, idx, - is_green); - } - return; - } - - /* Get expected throughput table and history win for current rate */ - if (!tbl->expected_tpt) { - IL_ERR("tbl->expected_tpt is NULL\n"); - return; - } - - /* force user max rate if set by user */ - if (lq_sta->max_rate_idx != -1 && lq_sta->max_rate_idx < idx) { - idx = lq_sta->max_rate_idx; - update_lq = 1; - win = &(tbl->win[idx]); - goto lq_update; - } - - win = &(tbl->win[idx]); - - /* - * If there is not enough history to calculate actual average - * throughput, keep analyzing results of more tx frames, without - * changing rate or mode (bypass most of the rest of this function). - * Set up new rate table in uCode only if old rate is not supported - * in current association (use new rate found above). - */ - fail_count = win->counter - win->success_counter; - if (fail_count < RATE_MIN_FAILURE_TH && - win->success_counter < RATE_MIN_SUCCESS_TH) { - D_RATE("LQ: still below TH. succ=%d total=%d " "for idx %d\n", - win->success_counter, win->counter, idx); - - /* Can't calculate this yet; not enough history */ - win->average_tpt = IL_INVALID_VALUE; - - /* Should we stay with this modulation mode, - * or search for a new one? */ - il4965_rs_stay_in_table(lq_sta, false); - - goto out; - } - /* Else we have enough samples; calculate estimate of - * actual average throughput */ - if (win->average_tpt != - ((win->success_ratio * tbl->expected_tpt[idx] + 64) / 128)) { - IL_ERR("expected_tpt should have been calculated by now\n"); - win->average_tpt = - ((win->success_ratio * tbl->expected_tpt[idx] + 64) / 128); - } - - /* If we are searching for better modulation mode, check success. */ - if (lq_sta->search_better_tbl) { - /* If good success, continue using the "search" mode; - * no need to send new link quality command, since we're - * continuing to use the setup that we've been trying. */ - if (win->average_tpt > lq_sta->last_tpt) { - - D_RATE("LQ: SWITCHING TO NEW TBL " - "suc=%d cur-tpt=%d old-tpt=%d\n", - win->success_ratio, win->average_tpt, - lq_sta->last_tpt); - - if (!is_legacy(tbl->lq_type)) - lq_sta->enable_counter = 1; - - /* Swap tables; "search" becomes "active" */ - lq_sta->active_tbl = active_tbl; - current_tpt = win->average_tpt; - - /* Else poor success; go back to mode in "active" table */ - } else { - - D_RATE("LQ: GOING BACK TO THE OLD TBL " - "suc=%d cur-tpt=%d old-tpt=%d\n", - win->success_ratio, win->average_tpt, - lq_sta->last_tpt); - - /* Nullify "search" table */ - tbl->lq_type = LQ_NONE; - - /* Revert to "active" table */ - active_tbl = lq_sta->active_tbl; - tbl = &(lq_sta->lq_info[active_tbl]); - - /* Revert to "active" rate and throughput info */ - idx = il4965_hwrate_to_plcp_idx(tbl->current_rate); - current_tpt = lq_sta->last_tpt; - - /* Need to set up a new rate table in uCode */ - update_lq = 1; - } - - /* Either way, we've made a decision; modulation mode - * search is done, allow rate adjustment next time. */ - lq_sta->search_better_tbl = 0; - done_search = 1; /* Don't switch modes below! */ - goto lq_update; - } - - /* (Else) not in search of better modulation mode, try for better - * starting rate, while staying in this mode. */ - high_low = - il4965_rs_get_adjacent_rate(il, idx, rate_scale_idx_msk, - tbl->lq_type); - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - /* If user set max rate, dont allow higher than user constrain */ - if (lq_sta->max_rate_idx != -1 && lq_sta->max_rate_idx < high) - high = RATE_INVALID; - - sr = win->success_ratio; - - /* Collect measured throughputs for current and adjacent rates */ - current_tpt = win->average_tpt; - if (low != RATE_INVALID) - low_tpt = tbl->win[low].average_tpt; - if (high != RATE_INVALID) - high_tpt = tbl->win[high].average_tpt; - - scale_action = 0; - - /* Too many failures, decrease rate */ - if (sr <= RATE_DECREASE_TH || current_tpt == 0) { - D_RATE("decrease rate because of low success_ratio\n"); - scale_action = -1; - - /* No throughput measured yet for adjacent rates; try increase. */ - } else if (low_tpt == IL_INVALID_VALUE && high_tpt == IL_INVALID_VALUE) { - - if (high != RATE_INVALID && sr >= RATE_INCREASE_TH) - scale_action = 1; - else if (low != RATE_INVALID) - scale_action = 0; - } - - /* Both adjacent throughputs are measured, but neither one has better - * throughput; we're using the best rate, don't change it! */ - else if (low_tpt != IL_INVALID_VALUE && high_tpt != IL_INVALID_VALUE && - low_tpt < current_tpt && high_tpt < current_tpt) - scale_action = 0; - - /* At least one adjacent rate's throughput is measured, - * and may have better performance. */ - else { - /* Higher adjacent rate's throughput is measured */ - if (high_tpt != IL_INVALID_VALUE) { - /* Higher rate has better throughput */ - if (high_tpt > current_tpt && sr >= RATE_INCREASE_TH) - scale_action = 1; - else - scale_action = 0; - - /* Lower adjacent rate's throughput is measured */ - } else if (low_tpt != IL_INVALID_VALUE) { - /* Lower rate has better throughput */ - if (low_tpt > current_tpt) { - D_RATE("decrease rate because of low tpt\n"); - scale_action = -1; - } else if (sr >= RATE_INCREASE_TH) { - scale_action = 1; - } - } - } - - /* Sanity check; asked for decrease, but success rate or throughput - * has been good at old rate. Don't change it. */ - if (scale_action == -1 && low != RATE_INVALID && - (sr > RATE_HIGH_TH || current_tpt > 100 * tbl->expected_tpt[low])) - scale_action = 0; - - switch (scale_action) { - case -1: - /* Decrease starting rate, update uCode's rate table */ - if (low != RATE_INVALID) { - update_lq = 1; - idx = low; - } - - break; - case 1: - /* Increase starting rate, update uCode's rate table */ - if (high != RATE_INVALID) { - update_lq = 1; - idx = high; - } - - break; - case 0: - /* No change */ - default: - break; - } - - D_RATE("choose rate scale idx %d action %d low %d " "high %d type %d\n", - idx, scale_action, low, high, tbl->lq_type); - -lq_update: - /* Replace uCode's rate table for the destination station. */ - if (update_lq) - il4965_rs_update_rate_tbl(il, lq_sta, tbl, idx, is_green); - - /* Should we stay with this modulation mode, - * or search for a new one? */ - il4965_rs_stay_in_table(lq_sta, false); - - /* - * Search for new modulation mode if we're: - * 1) Not changing rates right now - * 2) Not just finishing up a search - * 3) Allowing a new search - */ - if (!update_lq && !done_search && !lq_sta->stay_in_tbl && win->counter) { - /* Save current throughput to compare with "search" throughput */ - lq_sta->last_tpt = current_tpt; - - /* Select a new "search" modulation mode to try. - * If one is found, set up the new "search" table. */ - if (is_legacy(tbl->lq_type)) - il4965_rs_move_legacy_other(il, lq_sta, conf, sta, idx); - else if (is_siso(tbl->lq_type)) - il4965_rs_move_siso_to_other(il, lq_sta, conf, sta, - idx); - else /* (is_mimo2(tbl->lq_type)) */ - il4965_rs_move_mimo2_to_other(il, lq_sta, conf, sta, - idx); - - /* If new "search" mode was selected, set up in uCode table */ - if (lq_sta->search_better_tbl) { - /* Access the "search" table, clear its history. */ - tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - for (i = 0; i < RATE_COUNT; i++) - il4965_rs_rate_scale_clear_win(&(tbl->win[i])); - - /* Use new "search" start rate */ - idx = il4965_hwrate_to_plcp_idx(tbl->current_rate); - - D_RATE("Switch current mcs: %X idx: %d\n", - tbl->current_rate, idx); - il4965_rs_fill_link_cmd(il, lq_sta, tbl->current_rate); - il_send_lq_cmd(il, &lq_sta->lq, CMD_ASYNC, false); - } else - done_search = 1; - } - - if (done_search && !lq_sta->stay_in_tbl) { - /* If the "active" (non-search) mode was legacy, - * and we've tried switching antennas, - * but we haven't been able to try HT modes (not available), - * stay with best antenna legacy modulation for a while - * before next round of mode comparisons. */ - tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); - if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) && - lq_sta->action_counter > tbl1->max_search) { - D_RATE("LQ: STAY in legacy table\n"); - il4965_rs_set_stay_in_table(il, 1, lq_sta); - } - - /* If we're in an HT mode, and all 3 mode switch actions - * have been tried and compared, stay in this best modulation - * mode for a while before next round of mode comparisons. */ - if (lq_sta->enable_counter && - lq_sta->action_counter >= tbl1->max_search) { - if (lq_sta->last_tpt > IL_AGG_TPT_THREHOLD && - (lq_sta->tx_agg_tid_en & (1 << tid)) && - tid != MAX_TID_COUNT) { - tid_data = - &il->stations[lq_sta->lq.sta_id].tid[tid]; - if (tid_data->agg.state == IL_AGG_OFF) { - D_RATE("try to aggregate tid %d\n", - tid); - il4965_rs_tl_turn_on_agg(il, tid, - lq_sta, sta); - } - } - il4965_rs_set_stay_in_table(il, 0, lq_sta); - } - } - -out: - tbl->current_rate = - il4965_rate_n_flags_from_tbl(il, tbl, idx, is_green); - i = idx; - lq_sta->last_txrate_idx = i; -} - -/** - * il4965_rs_initialize_lq - Initialize a station's hardware rate table - * - * The uCode's station table contains a table of fallback rates - * for automatic fallback during transmission. - * - * NOTE: This sets up a default set of values. These will be replaced later - * if the driver's iwl-4965-rs rate scaling algorithm is used, instead of - * rc80211_simple. - * - * NOTE: Run C_ADD_STA command to set up station table entry, before - * calling this function (which runs C_TX_LINK_QUALITY_CMD, - * which requires station table entry to exist). - */ -static void -il4965_rs_initialize_lq(struct il_priv *il, struct ieee80211_conf *conf, - struct ieee80211_sta *sta, struct il_lq_sta *lq_sta) -{ - struct il_scale_tbl_info *tbl; - int rate_idx; - int i; - u32 rate; - u8 use_green; - u8 active_tbl = 0; - u8 valid_tx_ant; - struct il_station_priv *sta_priv; - - if (!sta || !lq_sta) - return; - - use_green = il4965_rs_use_green(il, sta); - sta_priv = (void *)sta->drv_priv; - - i = lq_sta->last_txrate_idx; - - valid_tx_ant = il->hw_params.valid_tx_ant; - - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - - if (i < 0 || i >= RATE_COUNT) - i = 0; - - rate = il_rates[i].plcp; - tbl->ant_type = il4965_first_antenna(valid_tx_ant); - rate |= tbl->ant_type << RATE_MCS_ANT_POS; - - if (i >= IL_FIRST_CCK_RATE && i <= IL_LAST_CCK_RATE) - rate |= RATE_MCS_CCK_MSK; - - il4965_rs_get_tbl_info_from_mcs(rate, il->band, tbl, &rate_idx); - if (!il4965_rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) - il4965_rs_toggle_antenna(valid_tx_ant, &rate, tbl); - - rate = il4965_rate_n_flags_from_tbl(il, tbl, rate_idx, use_green); - tbl->current_rate = rate; - il4965_rs_set_expected_tpt_table(lq_sta, tbl); - il4965_rs_fill_link_cmd(NULL, lq_sta, rate); - il->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; - il_send_lq_cmd(il, &lq_sta->lq, CMD_SYNC, true); -} - -static void -il4965_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta, - struct ieee80211_tx_rate_control *txrc) -{ - - struct sk_buff *skb = txrc->skb; - struct ieee80211_supported_band *sband = txrc->sband; - struct il_priv *il __maybe_unused = (struct il_priv *)il_r; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct il_lq_sta *lq_sta = il_sta; - int rate_idx; - - D_RATE("rate scale calculate new rate for skb\n"); - - /* Get max rate if user set max rate */ - if (lq_sta) { - lq_sta->max_rate_idx = txrc->max_rate_idx; - if (sband->band == IEEE80211_BAND_5GHZ && - lq_sta->max_rate_idx != -1) - lq_sta->max_rate_idx += IL_FIRST_OFDM_RATE; - if (lq_sta->max_rate_idx < 0 || - lq_sta->max_rate_idx >= RATE_COUNT) - lq_sta->max_rate_idx = -1; - } - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (lq_sta && !lq_sta->drv) { - D_RATE("Rate scaling not initialized yet.\n"); - il_sta = NULL; - } - - /* Send management frames and NO_ACK data using lowest rate. */ - if (rate_control_send_low(sta, il_sta, txrc)) - return; - - if (!lq_sta) - return; - - rate_idx = lq_sta->last_txrate_idx; - - if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { - rate_idx -= IL_FIRST_OFDM_RATE; - /* 6M and 9M shared same MCS idx */ - rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; - if (il4965_rs_extract_rate(lq_sta->last_rate_n_flags) >= - RATE_MIMO2_6M_PLCP) - rate_idx = rate_idx + MCS_IDX_PER_STREAM; - info->control.rates[0].flags = IEEE80211_TX_RC_MCS; - if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) - info->control.rates[0].flags |= - IEEE80211_TX_RC_SHORT_GI; - if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) - info->control.rates[0].flags |= - IEEE80211_TX_RC_DUP_DATA; - if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) - info->control.rates[0].flags |= - IEEE80211_TX_RC_40_MHZ_WIDTH; - if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) - info->control.rates[0].flags |= - IEEE80211_TX_RC_GREEN_FIELD; - } else { - /* Check for invalid rates */ - if (rate_idx < 0 || rate_idx >= RATE_COUNT_LEGACY || - (sband->band == IEEE80211_BAND_5GHZ && - rate_idx < IL_FIRST_OFDM_RATE)) - rate_idx = rate_lowest_index(sband, sta); - /* On valid 5 GHz rate, adjust idx */ - else if (sband->band == IEEE80211_BAND_5GHZ) - rate_idx -= IL_FIRST_OFDM_RATE; - info->control.rates[0].flags = 0; - } - info->control.rates[0].idx = rate_idx; - info->control.rates[0].count = 1; -} - -static void * -il4965_rs_alloc_sta(void *il_rate, struct ieee80211_sta *sta, gfp_t gfp) -{ - struct il_station_priv *sta_priv = - (struct il_station_priv *)sta->drv_priv; - struct il_priv *il; - - il = (struct il_priv *)il_rate; - D_RATE("create station rate scale win\n"); - - return &sta_priv->lq_sta; -} - -/* - * Called after adding a new station to initialize rate scaling - */ -void -il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, u8 sta_id) -{ - int i, j; - struct ieee80211_hw *hw = il->hw; - struct ieee80211_conf *conf = &il->hw->conf; - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct il_station_priv *sta_priv; - struct il_lq_sta *lq_sta; - struct ieee80211_supported_band *sband; - - sta_priv = (struct il_station_priv *)sta->drv_priv; - lq_sta = &sta_priv->lq_sta; - sband = hw->wiphy->bands[conf->chandef.chan->band]; - - lq_sta->lq.sta_id = sta_id; - - for (j = 0; j < LQ_SIZE; j++) - for (i = 0; i < RATE_COUNT; i++) - il4965_rs_rate_scale_clear_win(&lq_sta->lq_info[j]. - win[i]); - - lq_sta->flush_timer = 0; - lq_sta->supp_rates = sta->supp_rates[sband->band]; - for (j = 0; j < LQ_SIZE; j++) - for (i = 0; i < RATE_COUNT; i++) - il4965_rs_rate_scale_clear_win(&lq_sta->lq_info[j]. - win[i]); - - D_RATE("LQ:" "*** rate scale station global init for station %d ***\n", - sta_id); - /* TODO: what is a good starting rate for STA? About middle? Maybe not - * the lowest or the highest rate.. Could consider using RSSI from - * previous packets? Need to have IEEE 802.1X auth succeed immediately - * after assoc.. */ - - lq_sta->is_dup = 0; - lq_sta->max_rate_idx = -1; - lq_sta->missed_rate_counter = IL_MISSED_RATE_MAX; - lq_sta->is_green = il4965_rs_use_green(il, sta); - lq_sta->active_legacy_rate = il->active_rate & ~(0x1000); - lq_sta->band = il->band; - /* - * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), - * supp_rates[] does not; shift to convert format, force 9 MBits off. - */ - lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; - lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; - lq_sta->active_siso_rate &= ~((u16) 0x2); - lq_sta->active_siso_rate <<= IL_FIRST_OFDM_RATE; - - /* Same here */ - lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; - lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; - lq_sta->active_mimo2_rate &= ~((u16) 0x2); - lq_sta->active_mimo2_rate <<= IL_FIRST_OFDM_RATE; - - /* These values will be overridden later */ - lq_sta->lq.general_params.single_stream_ant_msk = - il4965_first_antenna(il->hw_params.valid_tx_ant); - lq_sta->lq.general_params.dual_stream_ant_msk = - il->hw_params.valid_tx_ant & ~il4965_first_antenna(il->hw_params. - valid_tx_ant); - if (!lq_sta->lq.general_params.dual_stream_ant_msk) { - lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; - } else if (il4965_num_of_ant(il->hw_params.valid_tx_ant) == 2) { - lq_sta->lq.general_params.dual_stream_ant_msk = - il->hw_params.valid_tx_ant; - } - - /* as default allow aggregation for all tids */ - lq_sta->tx_agg_tid_en = IL_AGG_ALL_TID; - lq_sta->drv = il; - - /* Set last_txrate_idx to lowest rate */ - lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); - if (sband->band == IEEE80211_BAND_5GHZ) - lq_sta->last_txrate_idx += IL_FIRST_OFDM_RATE; - lq_sta->is_agg = 0; - -#ifdef CONFIG_MAC80211_DEBUGFS - lq_sta->dbg_fixed_rate = 0; -#endif - - il4965_rs_initialize_lq(il, conf, sta, lq_sta); -} - -static void -il4965_rs_fill_link_cmd(struct il_priv *il, struct il_lq_sta *lq_sta, - u32 new_rate) -{ - struct il_scale_tbl_info tbl_type; - int idx = 0; - int rate_idx; - int repeat_rate = 0; - u8 ant_toggle_cnt = 0; - u8 use_ht_possible = 1; - u8 valid_tx_ant = 0; - struct il_link_quality_cmd *lq_cmd = &lq_sta->lq; - - /* Override starting rate (idx 0) if needed for debug purposes */ - il4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, idx); - - /* Interpret new_rate (rate_n_flags) */ - il4965_rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, - &rate_idx); - - /* How many times should we repeat the initial rate? */ - if (is_legacy(tbl_type.lq_type)) { - ant_toggle_cnt = 1; - repeat_rate = IL_NUMBER_TRY; - } else { - repeat_rate = IL_HT_NUMBER_TRY; - } - - lq_cmd->general_params.mimo_delimiter = - is_mimo(tbl_type.lq_type) ? 1 : 0; - - /* Fill 1st table entry (idx 0) */ - lq_cmd->rs_table[idx].rate_n_flags = cpu_to_le32(new_rate); - - if (il4965_num_of_ant(tbl_type.ant_type) == 1) { - lq_cmd->general_params.single_stream_ant_msk = - tbl_type.ant_type; - } else if (il4965_num_of_ant(tbl_type.ant_type) == 2) { - lq_cmd->general_params.dual_stream_ant_msk = tbl_type.ant_type; - } - /* otherwise we don't modify the existing value */ - idx++; - repeat_rate--; - if (il) - valid_tx_ant = il->hw_params.valid_tx_ant; - - /* Fill rest of rate table */ - while (idx < LINK_QUAL_MAX_RETRY_NUM) { - /* Repeat initial/next rate. - * For legacy IL_NUMBER_TRY == 1, this loop will not execute. - * For HT IL_HT_NUMBER_TRY == 3, this executes twice. */ - while (repeat_rate > 0 && idx < LINK_QUAL_MAX_RETRY_NUM) { - if (is_legacy(tbl_type.lq_type)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (il && - il4965_rs_toggle_antenna(valid_tx_ant, - &new_rate, - &tbl_type)) - ant_toggle_cnt = 1; - } - - /* Override next rate if needed for debug purposes */ - il4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, idx); - - /* Fill next table entry */ - lq_cmd->rs_table[idx].rate_n_flags = - cpu_to_le32(new_rate); - repeat_rate--; - idx++; - } - - il4965_rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, - &tbl_type, &rate_idx); - - /* Indicate to uCode which entries might be MIMO. - * If initial rate was MIMO, this will finally end up - * as (IL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ - if (is_mimo(tbl_type.lq_type)) - lq_cmd->general_params.mimo_delimiter = idx; - - /* Get next rate */ - new_rate = - il4965_rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, - use_ht_possible); - - /* How many times should we repeat the next rate? */ - if (is_legacy(tbl_type.lq_type)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (il && - il4965_rs_toggle_antenna(valid_tx_ant, - &new_rate, &tbl_type)) - ant_toggle_cnt = 1; - - repeat_rate = IL_NUMBER_TRY; - } else { - repeat_rate = IL_HT_NUMBER_TRY; - } - - /* Don't allow HT rates after next pass. - * il4965_rs_get_lower_rate() will change type to LQ_A or LQ_G. */ - use_ht_possible = 0; - - /* Override next rate if needed for debug purposes */ - il4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, idx); - - /* Fill next table entry */ - lq_cmd->rs_table[idx].rate_n_flags = cpu_to_le32(new_rate); - - idx++; - repeat_rate--; - } - - lq_cmd->agg_params.agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; - lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; - - lq_cmd->agg_params.agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); -} - -static void * -il4965_rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) -{ - return hw->priv; -} - -/* rate scale requires free function to be implemented */ -static void -il4965_rs_free(void *il_rate) -{ - return; -} - -static void -il4965_rs_free_sta(void *il_r, struct ieee80211_sta *sta, void *il_sta) -{ - struct il_priv *il __maybe_unused = il_r; - - D_RATE("enter\n"); - D_RATE("leave\n"); -} - -#ifdef CONFIG_MAC80211_DEBUGFS - -static void -il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx) -{ - struct il_priv *il; - u8 valid_tx_ant; - u8 ant_sel_tx; - - il = lq_sta->drv; - valid_tx_ant = il->hw_params.valid_tx_ant; - if (lq_sta->dbg_fixed_rate) { - ant_sel_tx = - ((lq_sta-> - dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >> - RATE_MCS_ANT_POS); - if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { - *rate_n_flags = lq_sta->dbg_fixed_rate; - D_RATE("Fixed rate ON\n"); - } else { - lq_sta->dbg_fixed_rate = 0; - IL_ERR - ("Invalid antenna selection 0x%X, Valid is 0x%X\n", - ant_sel_tx, valid_tx_ant); - D_RATE("Fixed rate OFF\n"); - } - } else { - D_RATE("Fixed rate OFF\n"); - } -} - -static ssize_t -il4965_rs_sta_dbgfs_scale_table_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_lq_sta *lq_sta = file->private_data; - struct il_priv *il; - char buf[64]; - size_t buf_size; - u32 parsed_rate; - - il = lq_sta->drv; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (sscanf(buf, "%x", &parsed_rate) == 1) - lq_sta->dbg_fixed_rate = parsed_rate; - else - lq_sta->dbg_fixed_rate = 0; - - lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ - lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - - D_RATE("sta_id %d rate 0x%X\n", lq_sta->lq.sta_id, - lq_sta->dbg_fixed_rate); - - if (lq_sta->dbg_fixed_rate) { - il4965_rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); - il_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false); - } - - return count; -} - -static ssize_t -il4965_rs_sta_dbgfs_scale_table_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - char *buff; - int desc = 0; - int i = 0; - int idx = 0; - ssize_t ret; - - struct il_lq_sta *lq_sta = file->private_data; - struct il_priv *il; - struct il_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - - il = lq_sta->drv; - buff = kmalloc(1024, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - desc += sprintf(buff + desc, "sta_id %d\n", lq_sta->lq.sta_id); - desc += - sprintf(buff + desc, "failed=%d success=%d rate=0%X\n", - lq_sta->total_failed, lq_sta->total_success, - lq_sta->active_legacy_rate); - desc += - sprintf(buff + desc, "fixed rate 0x%X\n", lq_sta->dbg_fixed_rate); - desc += - sprintf(buff + desc, "valid_tx_ant %s%s%s\n", - (il->hw_params.valid_tx_ant & ANT_A) ? "ANT_A," : "", - (il->hw_params.valid_tx_ant & ANT_B) ? "ANT_B," : "", - (il->hw_params.valid_tx_ant & ANT_C) ? "ANT_C" : ""); - desc += - sprintf(buff + desc, "lq type %s\n", - (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); - if (is_Ht(tbl->lq_type)) { - desc += - sprintf(buff + desc, " %s", - (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2"); - desc += - sprintf(buff + desc, " %s", - (tbl->is_ht40) ? "40MHz" : "20MHz"); - desc += - sprintf(buff + desc, " %s %s %s\n", - (tbl->is_SGI) ? "SGI" : "", - (lq_sta->is_green) ? "GF enabled" : "", - (lq_sta->is_agg) ? "AGG on" : ""); - } - desc += - sprintf(buff + desc, "last tx rate=0x%X\n", - lq_sta->last_rate_n_flags); - desc += - sprintf(buff + desc, - "general:" "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", - lq_sta->lq.general_params.flags, - lq_sta->lq.general_params.mimo_delimiter, - lq_sta->lq.general_params.single_stream_ant_msk, - lq_sta->lq.general_params.dual_stream_ant_msk); - - desc += - sprintf(buff + desc, - "agg:" - "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", - le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), - lq_sta->lq.agg_params.agg_dis_start_th, - lq_sta->lq.agg_params.agg_frame_cnt_limit); - - desc += - sprintf(buff + desc, - "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", - lq_sta->lq.general_params.start_rate_idx[0], - lq_sta->lq.general_params.start_rate_idx[1], - lq_sta->lq.general_params.start_rate_idx[2], - lq_sta->lq.general_params.start_rate_idx[3]); - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - idx = - il4965_hwrate_to_plcp_idx(le32_to_cpu - (lq_sta->lq.rs_table[i]. - rate_n_flags)); - if (is_legacy(tbl->lq_type)) { - desc += - sprintf(buff + desc, " rate[%d] 0x%X %smbps\n", i, - le32_to_cpu(lq_sta->lq.rs_table[i]. - rate_n_flags), - il_rate_mcs[idx].mbps); - } else { - desc += - sprintf(buff + desc, " rate[%d] 0x%X %smbps (%s)\n", - i, - le32_to_cpu(lq_sta->lq.rs_table[i]. - rate_n_flags), - il_rate_mcs[idx].mbps, - il_rate_mcs[idx].mcs); - } - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); - kfree(buff); - return ret; -} - -static const struct file_operations rs_sta_dbgfs_scale_table_ops = { - .write = il4965_rs_sta_dbgfs_scale_table_write, - .read = il4965_rs_sta_dbgfs_scale_table_read, - .open = simple_open, - .llseek = default_llseek, -}; - -static ssize_t -il4965_rs_sta_dbgfs_stats_table_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - char *buff; - int desc = 0; - int i, j; - ssize_t ret; - - struct il_lq_sta *lq_sta = file->private_data; - - buff = kmalloc(1024, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - for (i = 0; i < LQ_SIZE; i++) { - desc += - sprintf(buff + desc, - "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n" - "rate=0x%X\n", lq_sta->active_tbl == i ? "*" : "x", - lq_sta->lq_info[i].lq_type, - lq_sta->lq_info[i].is_SGI, - lq_sta->lq_info[i].is_ht40, - lq_sta->lq_info[i].is_dup, lq_sta->is_green, - lq_sta->lq_info[i].current_rate); - for (j = 0; j < RATE_COUNT; j++) { - desc += - sprintf(buff + desc, - "counter=%d success=%d %%=%d\n", - lq_sta->lq_info[i].win[j].counter, - lq_sta->lq_info[i].win[j].success_counter, - lq_sta->lq_info[i].win[j].success_ratio); - } - } - ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); - kfree(buff); - return ret; -} - -static const struct file_operations rs_sta_dbgfs_stats_table_ops = { - .read = il4965_rs_sta_dbgfs_stats_table_read, - .open = simple_open, - .llseek = default_llseek, -}; - -static ssize_t -il4965_rs_sta_dbgfs_rate_scale_data_read(struct file *file, - char __user *user_buf, size_t count, - loff_t *ppos) -{ - char buff[120]; - int desc = 0; - struct il_lq_sta *lq_sta = file->private_data; - struct il_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; - - if (is_Ht(tbl->lq_type)) - desc += - sprintf(buff + desc, "Bit Rate= %d Mb/s\n", - tbl->expected_tpt[lq_sta->last_txrate_idx]); - else - desc += - sprintf(buff + desc, "Bit Rate= %d Mb/s\n", - il_rates[lq_sta->last_txrate_idx].ieee >> 1); - - return simple_read_from_buffer(user_buf, count, ppos, buff, desc); -} - -static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { - .read = il4965_rs_sta_dbgfs_rate_scale_data_read, - .open = simple_open, - .llseek = default_llseek, -}; - -static void -il4965_rs_add_debugfs(void *il, void *il_sta, struct dentry *dir) -{ - struct il_lq_sta *lq_sta = il_sta; - lq_sta->rs_sta_dbgfs_scale_table_file = - debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, - lq_sta, &rs_sta_dbgfs_scale_table_ops); - lq_sta->rs_sta_dbgfs_stats_table_file = - debugfs_create_file("rate_stats_table", S_IRUSR, dir, lq_sta, - &rs_sta_dbgfs_stats_table_ops); - lq_sta->rs_sta_dbgfs_rate_scale_data_file = - debugfs_create_file("rate_scale_data", S_IRUSR, dir, lq_sta, - &rs_sta_dbgfs_rate_scale_data_ops); - lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = - debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, - &lq_sta->tx_agg_tid_en); - -} - -static void -il4965_rs_remove_debugfs(void *il, void *il_sta) -{ - struct il_lq_sta *lq_sta = il_sta; - debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); -} -#endif - -/* - * Initialization of rate scaling information is done by driver after - * the station is added. Since mac80211 calls this function before a - * station is added we ignore it. - */ -static void -il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, - struct cfg80211_chan_def *chandef, - struct ieee80211_sta *sta, void *il_sta) -{ -} - -static const struct rate_control_ops rs_4965_ops = { - .name = IL4965_RS_NAME, - .tx_status = il4965_rs_tx_status, - .get_rate = il4965_rs_get_rate, - .rate_init = il4965_rs_rate_init_stub, - .alloc = il4965_rs_alloc, - .free = il4965_rs_free, - .alloc_sta = il4965_rs_alloc_sta, - .free_sta = il4965_rs_free_sta, -#ifdef CONFIG_MAC80211_DEBUGFS - .add_sta_debugfs = il4965_rs_add_debugfs, - .remove_sta_debugfs = il4965_rs_remove_debugfs, -#endif -}; - -int -il4965_rate_control_register(void) -{ - return ieee80211_rate_control_register(&rs_4965_ops); -} - -void -il4965_rate_control_unregister(void) -{ - ieee80211_rate_control_unregister(&rs_4965_ops); -} diff --git a/drivers/net/wireless/iwlegacy/4965.c b/drivers/net/wireless/iwlegacy/4965.c deleted file mode 100644 index fe47db9c20cd..000000000000 --- a/drivers/net/wireless/iwlegacy/4965.c +++ /dev/null @@ -1,1950 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "4965.h" - -/** - * il_verify_inst_sparse - verify runtime uCode image in card vs. host, - * using sample data 100 bytes apart. If these sample points are good, - * it's a pretty good bet that everything between them is good, too. - */ -static int -il4965_verify_inst_sparse(struct il_priv *il, __le32 * image, u32 len) -{ - u32 val; - int ret = 0; - u32 errcnt = 0; - u32 i; - - D_INFO("ucode inst image size is %u\n", len); - - for (i = 0; i < len; i += 100, image += 100 / sizeof(u32)) { - /* read data comes through single port, auto-incr addr */ - /* NOTE: Use the debugless read so we don't flood kernel log - * if IL_DL_IO is set */ - il_wr(il, HBUS_TARG_MEM_RADDR, i + IL4965_RTC_INST_LOWER_BOUND); - val = _il_rd(il, HBUS_TARG_MEM_RDAT); - if (val != le32_to_cpu(*image)) { - ret = -EIO; - errcnt++; - if (errcnt >= 3) - break; - } - } - - return ret; -} - -/** - * il4965_verify_inst_full - verify runtime uCode image in card vs. host, - * looking at all data. - */ -static int -il4965_verify_inst_full(struct il_priv *il, __le32 * image, u32 len) -{ - u32 val; - u32 save_len = len; - int ret = 0; - u32 errcnt; - - D_INFO("ucode inst image size is %u\n", len); - - il_wr(il, HBUS_TARG_MEM_RADDR, IL4965_RTC_INST_LOWER_BOUND); - - errcnt = 0; - for (; len > 0; len -= sizeof(u32), image++) { - /* read data comes through single port, auto-incr addr */ - /* NOTE: Use the debugless read so we don't flood kernel log - * if IL_DL_IO is set */ - val = _il_rd(il, HBUS_TARG_MEM_RDAT); - if (val != le32_to_cpu(*image)) { - IL_ERR("uCode INST section is invalid at " - "offset 0x%x, is 0x%x, s/b 0x%x\n", - save_len - len, val, le32_to_cpu(*image)); - ret = -EIO; - errcnt++; - if (errcnt >= 20) - break; - } - } - - if (!errcnt) - D_INFO("ucode image in INSTRUCTION memory is good\n"); - - return ret; -} - -/** - * il4965_verify_ucode - determine which instruction image is in SRAM, - * and verify its contents - */ -int -il4965_verify_ucode(struct il_priv *il) -{ - __le32 *image; - u32 len; - int ret; - - /* Try bootstrap */ - image = (__le32 *) il->ucode_boot.v_addr; - len = il->ucode_boot.len; - ret = il4965_verify_inst_sparse(il, image, len); - if (!ret) { - D_INFO("Bootstrap uCode is good in inst SRAM\n"); - return 0; - } - - /* Try initialize */ - image = (__le32 *) il->ucode_init.v_addr; - len = il->ucode_init.len; - ret = il4965_verify_inst_sparse(il, image, len); - if (!ret) { - D_INFO("Initialize uCode is good in inst SRAM\n"); - return 0; - } - - /* Try runtime/protocol */ - image = (__le32 *) il->ucode_code.v_addr; - len = il->ucode_code.len; - ret = il4965_verify_inst_sparse(il, image, len); - if (!ret) { - D_INFO("Runtime uCode is good in inst SRAM\n"); - return 0; - } - - IL_ERR("NO VALID UCODE IMAGE IN INSTRUCTION SRAM!!\n"); - - /* Since nothing seems to match, show first several data entries in - * instruction SRAM, so maybe visual inspection will give a clue. - * Selection of bootstrap image (vs. other images) is arbitrary. */ - image = (__le32 *) il->ucode_boot.v_addr; - len = il->ucode_boot.len; - ret = il4965_verify_inst_full(il, image, len); - - return ret; -} - -/****************************************************************************** - * - * EEPROM related functions - * -******************************************************************************/ - -/* - * The device's EEPROM semaphore prevents conflicts between driver and uCode - * when accessing the EEPROM; each access is a series of pulses to/from the - * EEPROM chip, not a single event, so even reads could conflict if they - * weren't arbitrated by the semaphore. - */ -int -il4965_eeprom_acquire_semaphore(struct il_priv *il) -{ - u16 count; - int ret; - - for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { - /* Request semaphore */ - il_set_bit(il, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); - - /* See if we got it */ - ret = - _il_poll_bit(il, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, - EEPROM_SEM_TIMEOUT); - if (ret >= 0) - return ret; - } - - return ret; -} - -void -il4965_eeprom_release_semaphore(struct il_priv *il) -{ - il_clear_bit(il, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); - -} - -int -il4965_eeprom_check_version(struct il_priv *il) -{ - u16 eeprom_ver; - u16 calib_ver; - - eeprom_ver = il_eeprom_query16(il, EEPROM_VERSION); - calib_ver = il_eeprom_query16(il, EEPROM_4965_CALIB_VERSION_OFFSET); - - if (eeprom_ver < il->cfg->eeprom_ver || - calib_ver < il->cfg->eeprom_calib_ver) - goto err; - - IL_INFO("device EEPROM VER=0x%x, CALIB=0x%x\n", eeprom_ver, calib_ver); - - return 0; -err: - IL_ERR("Unsupported (too old) EEPROM VER=0x%x < 0x%x " - "CALIB=0x%x < 0x%x\n", eeprom_ver, il->cfg->eeprom_ver, - calib_ver, il->cfg->eeprom_calib_ver); - return -EINVAL; - -} - -void -il4965_eeprom_get_mac(const struct il_priv *il, u8 * mac) -{ - const u8 *addr = il_eeprom_query_addr(il, - EEPROM_MAC_ADDRESS); - memcpy(mac, addr, ETH_ALEN); -} - -/* Send led command */ -static int -il4965_send_led_cmd(struct il_priv *il, struct il_led_cmd *led_cmd) -{ - struct il_host_cmd cmd = { - .id = C_LEDS, - .len = sizeof(struct il_led_cmd), - .data = led_cmd, - .flags = CMD_ASYNC, - .callback = NULL, - }; - u32 reg; - - reg = _il_rd(il, CSR_LED_REG); - if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) - _il_wr(il, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK); - - return il_send_cmd(il, &cmd); -} - -/* Set led register off */ -void -il4965_led_enable(struct il_priv *il) -{ - _il_wr(il, CSR_LED_REG, CSR_LED_REG_TRUN_ON); -} - -static int il4965_send_tx_power(struct il_priv *il); -static int il4965_hw_get_temperature(struct il_priv *il); - -/* Highest firmware API version supported */ -#define IL4965_UCODE_API_MAX 2 - -/* Lowest firmware API version supported */ -#define IL4965_UCODE_API_MIN 2 - -#define IL4965_FW_PRE "iwlwifi-4965-" -#define _IL4965_MODULE_FIRMWARE(api) IL4965_FW_PRE #api ".ucode" -#define IL4965_MODULE_FIRMWARE(api) _IL4965_MODULE_FIRMWARE(api) - -/* check contents of special bootstrap uCode SRAM */ -static int -il4965_verify_bsm(struct il_priv *il) -{ - __le32 *image = il->ucode_boot.v_addr; - u32 len = il->ucode_boot.len; - u32 reg; - u32 val; - - D_INFO("Begin verify bsm\n"); - - /* verify BSM SRAM contents */ - val = il_rd_prph(il, BSM_WR_DWCOUNT_REG); - for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; - reg += sizeof(u32), image++) { - val = il_rd_prph(il, reg); - if (val != le32_to_cpu(*image)) { - IL_ERR("BSM uCode verification failed at " - "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n", - BSM_SRAM_LOWER_BOUND, reg - BSM_SRAM_LOWER_BOUND, - len, val, le32_to_cpu(*image)); - return -EIO; - } - } - - D_INFO("BSM bootstrap uCode image OK\n"); - - return 0; -} - -/** - * il4965_load_bsm - Load bootstrap instructions - * - * BSM operation: - * - * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program - * in special SRAM that does not power down during RFKILL. When powering back - * up after power-saving sleeps (or during initial uCode load), the BSM loads - * the bootstrap program into the on-board processor, and starts it. - * - * The bootstrap program loads (via DMA) instructions and data for a new - * program from host DRAM locations indicated by the host driver in the - * BSM_DRAM_* registers. Once the new program is loaded, it starts - * automatically. - * - * When initializing the NIC, the host driver points the BSM to the - * "initialize" uCode image. This uCode sets up some internal data, then - * notifies host via "initialize alive" that it is complete. - * - * The host then replaces the BSM_DRAM_* pointer values to point to the - * normal runtime uCode instructions and a backup uCode data cache buffer - * (filled initially with starting data values for the on-board processor), - * then triggers the "initialize" uCode to load and launch the runtime uCode, - * which begins normal operation. - * - * When doing a power-save shutdown, runtime uCode saves data SRAM into - * the backup data cache in DRAM before SRAM is powered down. - * - * When powering back up, the BSM loads the bootstrap program. This reloads - * the runtime uCode instructions and the backup data cache into SRAM, - * and re-launches the runtime uCode from where it left off. - */ -static int -il4965_load_bsm(struct il_priv *il) -{ - __le32 *image = il->ucode_boot.v_addr; - u32 len = il->ucode_boot.len; - dma_addr_t pinst; - dma_addr_t pdata; - u32 inst_len; - u32 data_len; - int i; - u32 done; - u32 reg_offset; - int ret; - - D_INFO("Begin load bsm\n"); - - il->ucode_type = UCODE_RT; - - /* make sure bootstrap program is no larger than BSM's SRAM size */ - if (len > IL49_MAX_BSM_SIZE) - return -EINVAL; - - /* Tell bootstrap uCode where to find the "Initialize" uCode - * in host DRAM ... host DRAM physical address bits 35:4 for 4965. - * NOTE: il_init_alive_start() will replace these values, - * after the "initialize" uCode has run, to point to - * runtime/protocol instructions and backup data cache. - */ - pinst = il->ucode_init.p_addr >> 4; - pdata = il->ucode_init_data.p_addr >> 4; - inst_len = il->ucode_init.len; - data_len = il->ucode_init_data.len; - - il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); - il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); - il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); - il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); - - /* Fill BSM memory with bootstrap instructions */ - for (reg_offset = BSM_SRAM_LOWER_BOUND; - reg_offset < BSM_SRAM_LOWER_BOUND + len; - reg_offset += sizeof(u32), image++) - _il_wr_prph(il, reg_offset, le32_to_cpu(*image)); - - ret = il4965_verify_bsm(il); - if (ret) - return ret; - - /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ - il_wr_prph(il, BSM_WR_MEM_SRC_REG, 0x0); - il_wr_prph(il, BSM_WR_MEM_DST_REG, IL49_RTC_INST_LOWER_BOUND); - il_wr_prph(il, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); - - /* Load bootstrap code into instruction SRAM now, - * to prepare to load "initialize" uCode */ - il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); - - /* Wait for load of bootstrap uCode to finish */ - for (i = 0; i < 100; i++) { - done = il_rd_prph(il, BSM_WR_CTRL_REG); - if (!(done & BSM_WR_CTRL_REG_BIT_START)) - break; - udelay(10); - } - if (i < 100) - D_INFO("BSM write complete, poll %d iterations\n", i); - else { - IL_ERR("BSM write did not complete!\n"); - return -EIO; - } - - /* Enable future boot loads whenever power management unit triggers it - * (e.g. when powering back up after power-save shutdown) */ - il_wr_prph(il, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); - - return 0; -} - -/** - * il4965_set_ucode_ptrs - Set uCode address location - * - * Tell initialization uCode where to find runtime uCode. - * - * BSM registers initially contain pointers to initialization uCode. - * We need to replace them to load runtime uCode inst and data, - * and to save runtime data when powering down. - */ -static int -il4965_set_ucode_ptrs(struct il_priv *il) -{ - dma_addr_t pinst; - dma_addr_t pdata; - int ret = 0; - - /* bits 35:4 for 4965 */ - pinst = il->ucode_code.p_addr >> 4; - pdata = il->ucode_data_backup.p_addr >> 4; - - /* Tell bootstrap uCode where to find image to load */ - il_wr_prph(il, BSM_DRAM_INST_PTR_REG, pinst); - il_wr_prph(il, BSM_DRAM_DATA_PTR_REG, pdata); - il_wr_prph(il, BSM_DRAM_DATA_BYTECOUNT_REG, il->ucode_data.len); - - /* Inst byte count must be last to set up, bit 31 signals uCode - * that all new ptr/size info is in place */ - il_wr_prph(il, BSM_DRAM_INST_BYTECOUNT_REG, - il->ucode_code.len | BSM_DRAM_INST_LOAD); - D_INFO("Runtime uCode pointers are set.\n"); - - return ret; -} - -/** - * il4965_init_alive_start - Called after N_ALIVE notification received - * - * Called after N_ALIVE notification received from "initialize" uCode. - * - * The 4965 "initialize" ALIVE reply contains calibration data for: - * Voltage, temperature, and MIMO tx gain correction, now stored in il - * (3945 does not contain this data). - * - * Tell "initialize" uCode to go ahead and load the runtime uCode. -*/ -static void -il4965_init_alive_start(struct il_priv *il) -{ - /* Bootstrap uCode has loaded initialize uCode ... verify inst image. - * This is a paranoid check, because we would not have gotten the - * "initialize" alive if code weren't properly loaded. */ - if (il4965_verify_ucode(il)) { - /* Runtime instruction load was bad; - * take it all the way back down so we can try again */ - D_INFO("Bad \"initialize\" uCode load.\n"); - goto restart; - } - - /* Calculate temperature */ - il->temperature = il4965_hw_get_temperature(il); - - /* Send pointers to protocol/runtime uCode image ... init code will - * load and launch runtime uCode, which will send us another "Alive" - * notification. */ - D_INFO("Initialization Alive received.\n"); - if (il4965_set_ucode_ptrs(il)) { - /* Runtime instruction load won't happen; - * take it all the way back down so we can try again */ - D_INFO("Couldn't set up uCode pointers.\n"); - goto restart; - } - return; - -restart: - queue_work(il->workqueue, &il->restart); -} - -static bool -iw4965_is_ht40_channel(__le32 rxon_flags) -{ - int chan_mod = - le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK) >> - RXON_FLG_CHANNEL_MODE_POS; - return (chan_mod == CHANNEL_MODE_PURE_40 || - chan_mod == CHANNEL_MODE_MIXED); -} - -void -il4965_nic_config(struct il_priv *il) -{ - unsigned long flags; - u16 radio_cfg; - - spin_lock_irqsave(&il->lock, flags); - - radio_cfg = il_eeprom_query16(il, EEPROM_RADIO_CONFIG); - - /* write radio config values to register */ - if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) == EEPROM_4965_RF_CFG_TYPE_MAX) - il_set_bit(il, CSR_HW_IF_CONFIG_REG, - EEPROM_RF_CFG_TYPE_MSK(radio_cfg) | - EEPROM_RF_CFG_STEP_MSK(radio_cfg) | - EEPROM_RF_CFG_DASH_MSK(radio_cfg)); - - /* set CSR_HW_CONFIG_REG for uCode use */ - il_set_bit(il, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | - CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); - - il->calib_info = - (struct il_eeprom_calib_info *) - il_eeprom_query_addr(il, EEPROM_4965_CALIB_TXPOWER_OFFSET); - - spin_unlock_irqrestore(&il->lock, flags); -} - -/* Reset differential Rx gains in NIC to prepare for chain noise calibration. - * Called after every association, but this runs only once! - * ... once chain noise is calibrated the first time, it's good forever. */ -static void -il4965_chain_noise_reset(struct il_priv *il) -{ - struct il_chain_noise_data *data = &(il->chain_noise_data); - - if (data->state == IL_CHAIN_NOISE_ALIVE && il_is_any_associated(il)) { - struct il_calib_diff_gain_cmd cmd; - - /* clear data for chain noise calibration algorithm */ - data->chain_noise_a = 0; - data->chain_noise_b = 0; - data->chain_noise_c = 0; - data->chain_signal_a = 0; - data->chain_signal_b = 0; - data->chain_signal_c = 0; - data->beacon_count = 0; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.op_code = IL_PHY_CALIBRATE_DIFF_GAIN_CMD; - cmd.diff_gain_a = 0; - cmd.diff_gain_b = 0; - cmd.diff_gain_c = 0; - if (il_send_cmd_pdu(il, C_PHY_CALIBRATION, sizeof(cmd), &cmd)) - IL_ERR("Could not send C_PHY_CALIBRATION\n"); - data->state = IL_CHAIN_NOISE_ACCUMULATE; - D_CALIB("Run chain_noise_calibrate\n"); - } -} - -static s32 -il4965_math_div_round(s32 num, s32 denom, s32 * res) -{ - s32 sign = 1; - - if (num < 0) { - sign = -sign; - num = -num; - } - if (denom < 0) { - sign = -sign; - denom = -denom; - } - *res = 1; - *res = ((num * 2 + denom) / (denom * 2)) * sign; - - return 1; -} - -/** - * il4965_get_voltage_compensation - Power supply voltage comp for txpower - * - * Determines power supply voltage compensation for txpower calculations. - * Returns number of 1/2-dB steps to subtract from gain table idx, - * to compensate for difference between power supply voltage during - * factory measurements, vs. current power supply voltage. - * - * Voltage indication is higher for lower voltage. - * Lower voltage requires more gain (lower gain table idx). - */ -static s32 -il4965_get_voltage_compensation(s32 eeprom_voltage, s32 current_voltage) -{ - s32 comp = 0; - - if (TX_POWER_IL_ILLEGAL_VOLTAGE == eeprom_voltage || - TX_POWER_IL_ILLEGAL_VOLTAGE == current_voltage) - return 0; - - il4965_math_div_round(current_voltage - eeprom_voltage, - TX_POWER_IL_VOLTAGE_CODES_PER_03V, &comp); - - if (current_voltage > eeprom_voltage) - comp *= 2; - if ((comp < -2) || (comp > 2)) - comp = 0; - - return comp; -} - -static s32 -il4965_get_tx_atten_grp(u16 channel) -{ - if (channel >= CALIB_IL_TX_ATTEN_GR5_FCH && - channel <= CALIB_IL_TX_ATTEN_GR5_LCH) - return CALIB_CH_GROUP_5; - - if (channel >= CALIB_IL_TX_ATTEN_GR1_FCH && - channel <= CALIB_IL_TX_ATTEN_GR1_LCH) - return CALIB_CH_GROUP_1; - - if (channel >= CALIB_IL_TX_ATTEN_GR2_FCH && - channel <= CALIB_IL_TX_ATTEN_GR2_LCH) - return CALIB_CH_GROUP_2; - - if (channel >= CALIB_IL_TX_ATTEN_GR3_FCH && - channel <= CALIB_IL_TX_ATTEN_GR3_LCH) - return CALIB_CH_GROUP_3; - - if (channel >= CALIB_IL_TX_ATTEN_GR4_FCH && - channel <= CALIB_IL_TX_ATTEN_GR4_LCH) - return CALIB_CH_GROUP_4; - - return -EINVAL; -} - -static u32 -il4965_get_sub_band(const struct il_priv *il, u32 channel) -{ - s32 b = -1; - - for (b = 0; b < EEPROM_TX_POWER_BANDS; b++) { - if (il->calib_info->band_info[b].ch_from == 0) - continue; - - if (channel >= il->calib_info->band_info[b].ch_from && - channel <= il->calib_info->band_info[b].ch_to) - break; - } - - return b; -} - -static s32 -il4965_interpolate_value(s32 x, s32 x1, s32 y1, s32 x2, s32 y2) -{ - s32 val; - - if (x2 == x1) - return y1; - else { - il4965_math_div_round((x2 - x) * (y1 - y2), (x2 - x1), &val); - return val + y2; - } -} - -/** - * il4965_interpolate_chan - Interpolate factory measurements for one channel - * - * Interpolates factory measurements from the two sample channels within a - * sub-band, to apply to channel of interest. Interpolation is proportional to - * differences in channel frequencies, which is proportional to differences - * in channel number. - */ -static int -il4965_interpolate_chan(struct il_priv *il, u32 channel, - struct il_eeprom_calib_ch_info *chan_info) -{ - s32 s = -1; - u32 c; - u32 m; - const struct il_eeprom_calib_measure *m1; - const struct il_eeprom_calib_measure *m2; - struct il_eeprom_calib_measure *omeas; - u32 ch_i1; - u32 ch_i2; - - s = il4965_get_sub_band(il, channel); - if (s >= EEPROM_TX_POWER_BANDS) { - IL_ERR("Tx Power can not find channel %d\n", channel); - return -1; - } - - ch_i1 = il->calib_info->band_info[s].ch1.ch_num; - ch_i2 = il->calib_info->band_info[s].ch2.ch_num; - chan_info->ch_num = (u8) channel; - - D_TXPOWER("channel %d subband %d factory cal ch %d & %d\n", channel, s, - ch_i1, ch_i2); - - for (c = 0; c < EEPROM_TX_POWER_TX_CHAINS; c++) { - for (m = 0; m < EEPROM_TX_POWER_MEASUREMENTS; m++) { - m1 = &(il->calib_info->band_info[s].ch1. - measurements[c][m]); - m2 = &(il->calib_info->band_info[s].ch2. - measurements[c][m]); - omeas = &(chan_info->measurements[c][m]); - - omeas->actual_pow = - (u8) il4965_interpolate_value(channel, ch_i1, - m1->actual_pow, ch_i2, - m2->actual_pow); - omeas->gain_idx = - (u8) il4965_interpolate_value(channel, ch_i1, - m1->gain_idx, ch_i2, - m2->gain_idx); - omeas->temperature = - (u8) il4965_interpolate_value(channel, ch_i1, - m1->temperature, - ch_i2, - m2->temperature); - omeas->pa_det = - (s8) il4965_interpolate_value(channel, ch_i1, - m1->pa_det, ch_i2, - m2->pa_det); - - D_TXPOWER("chain %d meas %d AP1=%d AP2=%d AP=%d\n", c, - m, m1->actual_pow, m2->actual_pow, - omeas->actual_pow); - D_TXPOWER("chain %d meas %d NI1=%d NI2=%d NI=%d\n", c, - m, m1->gain_idx, m2->gain_idx, - omeas->gain_idx); - D_TXPOWER("chain %d meas %d PA1=%d PA2=%d PA=%d\n", c, - m, m1->pa_det, m2->pa_det, omeas->pa_det); - D_TXPOWER("chain %d meas %d T1=%d T2=%d T=%d\n", c, - m, m1->temperature, m2->temperature, - omeas->temperature); - } - } - - return 0; -} - -/* bit-rate-dependent table to prevent Tx distortion, in half-dB units, - * for OFDM 6, 12, 18, 24, 36, 48, 54, 60 MBit, and CCK all rates. */ -static s32 back_off_table[] = { - 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 20 MHz */ - 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 20 MHz */ - 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM SISO 40 MHz */ - 10, 10, 10, 10, 10, 15, 17, 20, /* OFDM MIMO 40 MHz */ - 10 /* CCK */ -}; - -/* Thermal compensation values for txpower for various frequency ranges ... - * ratios from 3:1 to 4.5:1 of degrees (Celsius) per half-dB gain adjust */ -static struct il4965_txpower_comp_entry { - s32 degrees_per_05db_a; - s32 degrees_per_05db_a_denom; -} tx_power_cmp_tble[CALIB_CH_GROUP_MAX] = { - { - 9, 2}, /* group 0 5.2, ch 34-43 */ - { - 4, 1}, /* group 1 5.2, ch 44-70 */ - { - 4, 1}, /* group 2 5.2, ch 71-124 */ - { - 4, 1}, /* group 3 5.2, ch 125-200 */ - { - 3, 1} /* group 4 2.4, ch all */ -}; - -static s32 -get_min_power_idx(s32 rate_power_idx, u32 band) -{ - if (!band) { - if ((rate_power_idx & 7) <= 4) - return MIN_TX_GAIN_IDX_52GHZ_EXT; - } - return MIN_TX_GAIN_IDX; -} - -struct gain_entry { - u8 dsp; - u8 radio; -}; - -static const struct gain_entry gain_table[2][108] = { - /* 5.2GHz power gain idx table */ - { - {123, 0x3F}, /* highest txpower */ - {117, 0x3F}, - {110, 0x3F}, - {104, 0x3F}, - {98, 0x3F}, - {110, 0x3E}, - {104, 0x3E}, - {98, 0x3E}, - {110, 0x3D}, - {104, 0x3D}, - {98, 0x3D}, - {110, 0x3C}, - {104, 0x3C}, - {98, 0x3C}, - {110, 0x3B}, - {104, 0x3B}, - {98, 0x3B}, - {110, 0x3A}, - {104, 0x3A}, - {98, 0x3A}, - {110, 0x39}, - {104, 0x39}, - {98, 0x39}, - {110, 0x38}, - {104, 0x38}, - {98, 0x38}, - {110, 0x37}, - {104, 0x37}, - {98, 0x37}, - {110, 0x36}, - {104, 0x36}, - {98, 0x36}, - {110, 0x35}, - {104, 0x35}, - {98, 0x35}, - {110, 0x34}, - {104, 0x34}, - {98, 0x34}, - {110, 0x33}, - {104, 0x33}, - {98, 0x33}, - {110, 0x32}, - {104, 0x32}, - {98, 0x32}, - {110, 0x31}, - {104, 0x31}, - {98, 0x31}, - {110, 0x30}, - {104, 0x30}, - {98, 0x30}, - {110, 0x25}, - {104, 0x25}, - {98, 0x25}, - {110, 0x24}, - {104, 0x24}, - {98, 0x24}, - {110, 0x23}, - {104, 0x23}, - {98, 0x23}, - {110, 0x22}, - {104, 0x18}, - {98, 0x18}, - {110, 0x17}, - {104, 0x17}, - {98, 0x17}, - {110, 0x16}, - {104, 0x16}, - {98, 0x16}, - {110, 0x15}, - {104, 0x15}, - {98, 0x15}, - {110, 0x14}, - {104, 0x14}, - {98, 0x14}, - {110, 0x13}, - {104, 0x13}, - {98, 0x13}, - {110, 0x12}, - {104, 0x08}, - {98, 0x08}, - {110, 0x07}, - {104, 0x07}, - {98, 0x07}, - {110, 0x06}, - {104, 0x06}, - {98, 0x06}, - {110, 0x05}, - {104, 0x05}, - {98, 0x05}, - {110, 0x04}, - {104, 0x04}, - {98, 0x04}, - {110, 0x03}, - {104, 0x03}, - {98, 0x03}, - {110, 0x02}, - {104, 0x02}, - {98, 0x02}, - {110, 0x01}, - {104, 0x01}, - {98, 0x01}, - {110, 0x00}, - {104, 0x00}, - {98, 0x00}, - {93, 0x00}, - {88, 0x00}, - {83, 0x00}, - {78, 0x00}, - }, - /* 2.4GHz power gain idx table */ - { - {110, 0x3f}, /* highest txpower */ - {104, 0x3f}, - {98, 0x3f}, - {110, 0x3e}, - {104, 0x3e}, - {98, 0x3e}, - {110, 0x3d}, - {104, 0x3d}, - {98, 0x3d}, - {110, 0x3c}, - {104, 0x3c}, - {98, 0x3c}, - {110, 0x3b}, - {104, 0x3b}, - {98, 0x3b}, - {110, 0x3a}, - {104, 0x3a}, - {98, 0x3a}, - {110, 0x39}, - {104, 0x39}, - {98, 0x39}, - {110, 0x38}, - {104, 0x38}, - {98, 0x38}, - {110, 0x37}, - {104, 0x37}, - {98, 0x37}, - {110, 0x36}, - {104, 0x36}, - {98, 0x36}, - {110, 0x35}, - {104, 0x35}, - {98, 0x35}, - {110, 0x34}, - {104, 0x34}, - {98, 0x34}, - {110, 0x33}, - {104, 0x33}, - {98, 0x33}, - {110, 0x32}, - {104, 0x32}, - {98, 0x32}, - {110, 0x31}, - {104, 0x31}, - {98, 0x31}, - {110, 0x30}, - {104, 0x30}, - {98, 0x30}, - {110, 0x6}, - {104, 0x6}, - {98, 0x6}, - {110, 0x5}, - {104, 0x5}, - {98, 0x5}, - {110, 0x4}, - {104, 0x4}, - {98, 0x4}, - {110, 0x3}, - {104, 0x3}, - {98, 0x3}, - {110, 0x2}, - {104, 0x2}, - {98, 0x2}, - {110, 0x1}, - {104, 0x1}, - {98, 0x1}, - {110, 0x0}, - {104, 0x0}, - {98, 0x0}, - {97, 0}, - {96, 0}, - {95, 0}, - {94, 0}, - {93, 0}, - {92, 0}, - {91, 0}, - {90, 0}, - {89, 0}, - {88, 0}, - {87, 0}, - {86, 0}, - {85, 0}, - {84, 0}, - {83, 0}, - {82, 0}, - {81, 0}, - {80, 0}, - {79, 0}, - {78, 0}, - {77, 0}, - {76, 0}, - {75, 0}, - {74, 0}, - {73, 0}, - {72, 0}, - {71, 0}, - {70, 0}, - {69, 0}, - {68, 0}, - {67, 0}, - {66, 0}, - {65, 0}, - {64, 0}, - {63, 0}, - {62, 0}, - {61, 0}, - {60, 0}, - {59, 0}, - } -}; - -static int -il4965_fill_txpower_tbl(struct il_priv *il, u8 band, u16 channel, u8 is_ht40, - u8 ctrl_chan_high, - struct il4965_tx_power_db *tx_power_tbl) -{ - u8 saturation_power; - s32 target_power; - s32 user_target_power; - s32 power_limit; - s32 current_temp; - s32 reg_limit; - s32 current_regulatory; - s32 txatten_grp = CALIB_CH_GROUP_MAX; - int i; - int c; - const struct il_channel_info *ch_info = NULL; - struct il_eeprom_calib_ch_info ch_eeprom_info; - const struct il_eeprom_calib_measure *measurement; - s16 voltage; - s32 init_voltage; - s32 voltage_compensation; - s32 degrees_per_05db_num; - s32 degrees_per_05db_denom; - s32 factory_temp; - s32 temperature_comp[2]; - s32 factory_gain_idx[2]; - s32 factory_actual_pwr[2]; - s32 power_idx; - - /* tx_power_user_lmt is in dBm, convert to half-dBm (half-dB units - * are used for idxing into txpower table) */ - user_target_power = 2 * il->tx_power_user_lmt; - - /* Get current (RXON) channel, band, width */ - D_TXPOWER("chan %d band %d is_ht40 %d\n", channel, band, is_ht40); - - ch_info = il_get_channel_info(il, il->band, channel); - - if (!il_is_channel_valid(ch_info)) - return -EINVAL; - - /* get txatten group, used to select 1) thermal txpower adjustment - * and 2) mimo txpower balance between Tx chains. */ - txatten_grp = il4965_get_tx_atten_grp(channel); - if (txatten_grp < 0) { - IL_ERR("Can't find txatten group for channel %d.\n", channel); - return txatten_grp; - } - - D_TXPOWER("channel %d belongs to txatten group %d\n", channel, - txatten_grp); - - if (is_ht40) { - if (ctrl_chan_high) - channel -= 2; - else - channel += 2; - } - - /* hardware txpower limits ... - * saturation (clipping distortion) txpowers are in half-dBm */ - if (band) - saturation_power = il->calib_info->saturation_power24; - else - saturation_power = il->calib_info->saturation_power52; - - if (saturation_power < IL_TX_POWER_SATURATION_MIN || - saturation_power > IL_TX_POWER_SATURATION_MAX) { - if (band) - saturation_power = IL_TX_POWER_DEFAULT_SATURATION_24; - else - saturation_power = IL_TX_POWER_DEFAULT_SATURATION_52; - } - - /* regulatory txpower limits ... reg_limit values are in half-dBm, - * max_power_avg values are in dBm, convert * 2 */ - if (is_ht40) - reg_limit = ch_info->ht40_max_power_avg * 2; - else - reg_limit = ch_info->max_power_avg * 2; - - if ((reg_limit < IL_TX_POWER_REGULATORY_MIN) || - (reg_limit > IL_TX_POWER_REGULATORY_MAX)) { - if (band) - reg_limit = IL_TX_POWER_DEFAULT_REGULATORY_24; - else - reg_limit = IL_TX_POWER_DEFAULT_REGULATORY_52; - } - - /* Interpolate txpower calibration values for this channel, - * based on factory calibration tests on spaced channels. */ - il4965_interpolate_chan(il, channel, &ch_eeprom_info); - - /* calculate tx gain adjustment based on power supply voltage */ - voltage = le16_to_cpu(il->calib_info->voltage); - init_voltage = (s32) le32_to_cpu(il->card_alive_init.voltage); - voltage_compensation = - il4965_get_voltage_compensation(voltage, init_voltage); - - D_TXPOWER("curr volt %d eeprom volt %d volt comp %d\n", init_voltage, - voltage, voltage_compensation); - - /* get current temperature (Celsius) */ - current_temp = max(il->temperature, IL_TX_POWER_TEMPERATURE_MIN); - current_temp = min(il->temperature, IL_TX_POWER_TEMPERATURE_MAX); - current_temp = KELVIN_TO_CELSIUS(current_temp); - - /* select thermal txpower adjustment params, based on channel group - * (same frequency group used for mimo txatten adjustment) */ - degrees_per_05db_num = - tx_power_cmp_tble[txatten_grp].degrees_per_05db_a; - degrees_per_05db_denom = - tx_power_cmp_tble[txatten_grp].degrees_per_05db_a_denom; - - /* get per-chain txpower values from factory measurements */ - for (c = 0; c < 2; c++) { - measurement = &ch_eeprom_info.measurements[c][1]; - - /* txgain adjustment (in half-dB steps) based on difference - * between factory and current temperature */ - factory_temp = measurement->temperature; - il4965_math_div_round((current_temp - - factory_temp) * degrees_per_05db_denom, - degrees_per_05db_num, - &temperature_comp[c]); - - factory_gain_idx[c] = measurement->gain_idx; - factory_actual_pwr[c] = measurement->actual_pow; - - D_TXPOWER("chain = %d\n", c); - D_TXPOWER("fctry tmp %d, " "curr tmp %d, comp %d steps\n", - factory_temp, current_temp, temperature_comp[c]); - - D_TXPOWER("fctry idx %d, fctry pwr %d\n", factory_gain_idx[c], - factory_actual_pwr[c]); - } - - /* for each of 33 bit-rates (including 1 for CCK) */ - for (i = 0; i < POWER_TBL_NUM_ENTRIES; i++) { - u8 is_mimo_rate; - union il4965_tx_power_dual_stream tx_power; - - /* for mimo, reduce each chain's txpower by half - * (3dB, 6 steps), so total output power is regulatory - * compliant. */ - if (i & 0x8) { - current_regulatory = - reg_limit - - IL_TX_POWER_MIMO_REGULATORY_COMPENSATION; - is_mimo_rate = 1; - } else { - current_regulatory = reg_limit; - is_mimo_rate = 0; - } - - /* find txpower limit, either hardware or regulatory */ - power_limit = saturation_power - back_off_table[i]; - if (power_limit > current_regulatory) - power_limit = current_regulatory; - - /* reduce user's txpower request if necessary - * for this rate on this channel */ - target_power = user_target_power; - if (target_power > power_limit) - target_power = power_limit; - - D_TXPOWER("rate %d sat %d reg %d usr %d tgt %d\n", i, - saturation_power - back_off_table[i], - current_regulatory, user_target_power, target_power); - - /* for each of 2 Tx chains (radio transmitters) */ - for (c = 0; c < 2; c++) { - s32 atten_value; - - if (is_mimo_rate) - atten_value = - (s32) le32_to_cpu(il->card_alive_init. - tx_atten[txatten_grp][c]); - else - atten_value = 0; - - /* calculate idx; higher idx means lower txpower */ - power_idx = - (u8) (factory_gain_idx[c] - - (target_power - factory_actual_pwr[c]) - - temperature_comp[c] - voltage_compensation + - atten_value); - -/* D_TXPOWER("calculated txpower idx %d\n", - power_idx); */ - - if (power_idx < get_min_power_idx(i, band)) - power_idx = get_min_power_idx(i, band); - - /* adjust 5 GHz idx to support negative idxes */ - if (!band) - power_idx += 9; - - /* CCK, rate 32, reduce txpower for CCK */ - if (i == POWER_TBL_CCK_ENTRY) - power_idx += - IL_TX_POWER_CCK_COMPENSATION_C_STEP; - - /* stay within the table! */ - if (power_idx > 107) { - IL_WARN("txpower idx %d > 107\n", power_idx); - power_idx = 107; - } - if (power_idx < 0) { - IL_WARN("txpower idx %d < 0\n", power_idx); - power_idx = 0; - } - - /* fill txpower command for this rate/chain */ - tx_power.s.radio_tx_gain[c] = - gain_table[band][power_idx].radio; - tx_power.s.dsp_predis_atten[c] = - gain_table[band][power_idx].dsp; - - D_TXPOWER("chain %d mimo %d idx %d " - "gain 0x%02x dsp %d\n", c, atten_value, - power_idx, tx_power.s.radio_tx_gain[c], - tx_power.s.dsp_predis_atten[c]); - } /* for each chain */ - - tx_power_tbl->power_tbl[i].dw = cpu_to_le32(tx_power.dw); - - } /* for each rate */ - - return 0; -} - -/** - * il4965_send_tx_power - Configure the TXPOWER level user limit - * - * Uses the active RXON for channel, band, and characteristics (ht40, high) - * The power limit is taken from il->tx_power_user_lmt. - */ -static int -il4965_send_tx_power(struct il_priv *il) -{ - struct il4965_txpowertable_cmd cmd = { 0 }; - int ret; - u8 band = 0; - bool is_ht40 = false; - u8 ctrl_chan_high = 0; - - if (WARN_ONCE - (test_bit(S_SCAN_HW, &il->status), - "TX Power requested while scanning!\n")) - return -EAGAIN; - - band = il->band == IEEE80211_BAND_2GHZ; - - is_ht40 = iw4965_is_ht40_channel(il->active.flags); - - if (is_ht40 && (il->active.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) - ctrl_chan_high = 1; - - cmd.band = band; - cmd.channel = il->active.channel; - - ret = - il4965_fill_txpower_tbl(il, band, le16_to_cpu(il->active.channel), - is_ht40, ctrl_chan_high, &cmd.tx_power); - if (ret) - goto out; - - ret = il_send_cmd_pdu(il, C_TX_PWR_TBL, sizeof(cmd), &cmd); - -out: - return ret; -} - -static int -il4965_send_rxon_assoc(struct il_priv *il) -{ - int ret = 0; - struct il4965_rxon_assoc_cmd rxon_assoc; - const struct il_rxon_cmd *rxon1 = &il->staging; - const struct il_rxon_cmd *rxon2 = &il->active; - - if (rxon1->flags == rxon2->flags && - rxon1->filter_flags == rxon2->filter_flags && - rxon1->cck_basic_rates == rxon2->cck_basic_rates && - rxon1->ofdm_ht_single_stream_basic_rates == - rxon2->ofdm_ht_single_stream_basic_rates && - rxon1->ofdm_ht_dual_stream_basic_rates == - rxon2->ofdm_ht_dual_stream_basic_rates && - rxon1->rx_chain == rxon2->rx_chain && - rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates) { - D_INFO("Using current RXON_ASSOC. Not resending.\n"); - return 0; - } - - rxon_assoc.flags = il->staging.flags; - rxon_assoc.filter_flags = il->staging.filter_flags; - rxon_assoc.ofdm_basic_rates = il->staging.ofdm_basic_rates; - rxon_assoc.cck_basic_rates = il->staging.cck_basic_rates; - rxon_assoc.reserved = 0; - rxon_assoc.ofdm_ht_single_stream_basic_rates = - il->staging.ofdm_ht_single_stream_basic_rates; - rxon_assoc.ofdm_ht_dual_stream_basic_rates = - il->staging.ofdm_ht_dual_stream_basic_rates; - rxon_assoc.rx_chain_select_flags = il->staging.rx_chain; - - ret = - il_send_cmd_pdu_async(il, C_RXON_ASSOC, sizeof(rxon_assoc), - &rxon_assoc, NULL); - - return ret; -} - -static int -il4965_commit_rxon(struct il_priv *il) -{ - /* cast away the const for active_rxon in this function */ - struct il_rxon_cmd *active_rxon = (void *)&il->active; - int ret; - bool new_assoc = !!(il->staging.filter_flags & RXON_FILTER_ASSOC_MSK); - - if (!il_is_alive(il)) - return -EBUSY; - - /* always get timestamp with Rx frame */ - il->staging.flags |= RXON_FLG_TSF2HOST_MSK; - - ret = il_check_rxon_cmd(il); - if (ret) { - IL_ERR("Invalid RXON configuration. Not committing.\n"); - return -EINVAL; - } - - /* - * receive commit_rxon request - * abort any previous channel switch if still in process - */ - if (test_bit(S_CHANNEL_SWITCH_PENDING, &il->status) && - il->switch_channel != il->staging.channel) { - D_11H("abort channel switch on %d\n", - le16_to_cpu(il->switch_channel)); - il_chswitch_done(il, false); - } - - /* If we don't need to send a full RXON, we can use - * il_rxon_assoc_cmd which is used to reconfigure filter - * and other flags for the current radio configuration. */ - if (!il_full_rxon_required(il)) { - ret = il_send_rxon_assoc(il); - if (ret) { - IL_ERR("Error setting RXON_ASSOC (%d)\n", ret); - return ret; - } - - memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); - il_print_rx_config_cmd(il); - /* - * We do not commit tx power settings while channel changing, - * do it now if tx power changed. - */ - il_set_tx_power(il, il->tx_power_next, false); - return 0; - } - - /* If we are currently associated and the new config requires - * an RXON_ASSOC and the new config wants the associated mask enabled, - * we must clear the associated from the active configuration - * before we apply the new config */ - if (il_is_associated(il) && new_assoc) { - D_INFO("Toggling associated bit on current RXON\n"); - active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - - ret = - il_send_cmd_pdu(il, C_RXON, - sizeof(struct il_rxon_cmd), active_rxon); - - /* If the mask clearing failed then we set - * active_rxon back to what it was previously */ - if (ret) { - active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK; - IL_ERR("Error clearing ASSOC_MSK (%d)\n", ret); - return ret; - } - il_clear_ucode_stations(il); - il_restore_stations(il); - ret = il4965_restore_default_wep_keys(il); - if (ret) { - IL_ERR("Failed to restore WEP keys (%d)\n", ret); - return ret; - } - } - - D_INFO("Sending RXON\n" "* with%s RXON_FILTER_ASSOC_MSK\n" - "* channel = %d\n" "* bssid = %pM\n", (new_assoc ? "" : "out"), - le16_to_cpu(il->staging.channel), il->staging.bssid_addr); - - il_set_rxon_hwcrypto(il, !il->cfg->mod_params->sw_crypto); - - /* Apply the new configuration - * RXON unassoc clears the station table in uCode so restoration of - * stations is needed after it (the RXON command) completes - */ - if (!new_assoc) { - ret = - il_send_cmd_pdu(il, C_RXON, - sizeof(struct il_rxon_cmd), &il->staging); - if (ret) { - IL_ERR("Error setting new RXON (%d)\n", ret); - return ret; - } - D_INFO("Return from !new_assoc RXON.\n"); - memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); - il_clear_ucode_stations(il); - il_restore_stations(il); - ret = il4965_restore_default_wep_keys(il); - if (ret) { - IL_ERR("Failed to restore WEP keys (%d)\n", ret); - return ret; - } - } - if (new_assoc) { - il->start_calib = 0; - /* Apply the new configuration - * RXON assoc doesn't clear the station table in uCode, - */ - ret = - il_send_cmd_pdu(il, C_RXON, - sizeof(struct il_rxon_cmd), &il->staging); - if (ret) { - IL_ERR("Error setting new RXON (%d)\n", ret); - return ret; - } - memcpy(active_rxon, &il->staging, sizeof(*active_rxon)); - } - il_print_rx_config_cmd(il); - - il4965_init_sensitivity(il); - - /* If we issue a new RXON command which required a tune then we must - * send a new TXPOWER command or we won't be able to Tx any frames */ - ret = il_set_tx_power(il, il->tx_power_next, true); - if (ret) { - IL_ERR("Error sending TX power (%d)\n", ret); - return ret; - } - - return 0; -} - -static int -il4965_hw_channel_switch(struct il_priv *il, - struct ieee80211_channel_switch *ch_switch) -{ - int rc; - u8 band = 0; - bool is_ht40 = false; - u8 ctrl_chan_high = 0; - struct il4965_channel_switch_cmd cmd; - const struct il_channel_info *ch_info; - u32 switch_time_in_usec, ucode_switch_time; - u16 ch; - u32 tsf_low; - u8 switch_count; - u16 beacon_interval = le16_to_cpu(il->timing.beacon_interval); - struct ieee80211_vif *vif = il->vif; - band = (il->band == IEEE80211_BAND_2GHZ); - - if (WARN_ON_ONCE(vif == NULL)) - return -EIO; - - is_ht40 = iw4965_is_ht40_channel(il->staging.flags); - - if (is_ht40 && (il->staging.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) - ctrl_chan_high = 1; - - cmd.band = band; - cmd.expect_beacon = 0; - ch = ch_switch->chandef.chan->hw_value; - cmd.channel = cpu_to_le16(ch); - cmd.rxon_flags = il->staging.flags; - cmd.rxon_filter_flags = il->staging.filter_flags; - switch_count = ch_switch->count; - tsf_low = ch_switch->timestamp & 0x0ffffffff; - /* - * calculate the ucode channel switch time - * adding TSF as one of the factor for when to switch - */ - if (il->ucode_beacon_time > tsf_low && beacon_interval) { - if (switch_count > - ((il->ucode_beacon_time - tsf_low) / beacon_interval)) { - switch_count -= - (il->ucode_beacon_time - tsf_low) / beacon_interval; - } else - switch_count = 0; - } - if (switch_count <= 1) - cmd.switch_time = cpu_to_le32(il->ucode_beacon_time); - else { - switch_time_in_usec = - vif->bss_conf.beacon_int * switch_count * TIME_UNIT; - ucode_switch_time = - il_usecs_to_beacons(il, switch_time_in_usec, - beacon_interval); - cmd.switch_time = - il_add_beacon_time(il, il->ucode_beacon_time, - ucode_switch_time, beacon_interval); - } - D_11H("uCode time for the switch is 0x%x\n", cmd.switch_time); - ch_info = il_get_channel_info(il, il->band, ch); - if (ch_info) - cmd.expect_beacon = il_is_channel_radar(ch_info); - else { - IL_ERR("invalid channel switch from %u to %u\n", - il->active.channel, ch); - return -EFAULT; - } - - rc = il4965_fill_txpower_tbl(il, band, ch, is_ht40, ctrl_chan_high, - &cmd.tx_power); - if (rc) { - D_11H("error:%d fill txpower_tbl\n", rc); - return rc; - } - - return il_send_cmd_pdu(il, C_CHANNEL_SWITCH, sizeof(cmd), &cmd); -} - -/** - * il4965_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array - */ -static void -il4965_txq_update_byte_cnt_tbl(struct il_priv *il, struct il_tx_queue *txq, - u16 byte_cnt) -{ - struct il4965_scd_bc_tbl *scd_bc_tbl = il->scd_bc_tbls.addr; - int txq_id = txq->q.id; - int write_ptr = txq->q.write_ptr; - int len = byte_cnt + IL_TX_CRC_SIZE + IL_TX_DELIMITER_SIZE; - __le16 bc_ent; - - WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX); - - bc_ent = cpu_to_le16(len & 0xFFF); - /* Set up byte count within first 256 entries */ - scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; - - /* If within first 64 entries, duplicate at end */ - if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) - scd_bc_tbl[txq_id].tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = - bc_ent; -} - -/** - * il4965_hw_get_temperature - return the calibrated temperature (in Kelvin) - * @stats: Provides the temperature reading from the uCode - * - * A return of <0 indicates bogus data in the stats - */ -static int -il4965_hw_get_temperature(struct il_priv *il) -{ - s32 temperature; - s32 vt; - s32 R1, R2, R3; - u32 R4; - - if (test_bit(S_TEMPERATURE, &il->status) && - (il->_4965.stats.flag & STATS_REPLY_FLG_HT40_MODE_MSK)) { - D_TEMP("Running HT40 temperature calibration\n"); - R1 = (s32) le32_to_cpu(il->card_alive_init.therm_r1[1]); - R2 = (s32) le32_to_cpu(il->card_alive_init.therm_r2[1]); - R3 = (s32) le32_to_cpu(il->card_alive_init.therm_r3[1]); - R4 = le32_to_cpu(il->card_alive_init.therm_r4[1]); - } else { - D_TEMP("Running temperature calibration\n"); - R1 = (s32) le32_to_cpu(il->card_alive_init.therm_r1[0]); - R2 = (s32) le32_to_cpu(il->card_alive_init.therm_r2[0]); - R3 = (s32) le32_to_cpu(il->card_alive_init.therm_r3[0]); - R4 = le32_to_cpu(il->card_alive_init.therm_r4[0]); - } - - /* - * Temperature is only 23 bits, so sign extend out to 32. - * - * NOTE If we haven't received a stats notification yet - * with an updated temperature, use R4 provided to us in the - * "initialize" ALIVE response. - */ - if (!test_bit(S_TEMPERATURE, &il->status)) - vt = sign_extend32(R4, 23); - else - vt = sign_extend32(le32_to_cpu - (il->_4965.stats.general.common.temperature), - 23); - - D_TEMP("Calib values R[1-3]: %d %d %d R4: %d\n", R1, R2, R3, vt); - - if (R3 == R1) { - IL_ERR("Calibration conflict R1 == R3\n"); - return -1; - } - - /* Calculate temperature in degrees Kelvin, adjust by 97%. - * Add offset to center the adjustment around 0 degrees Centigrade. */ - temperature = TEMPERATURE_CALIB_A_VAL * (vt - R2); - temperature /= (R3 - R1); - temperature = - (temperature * 97) / 100 + TEMPERATURE_CALIB_KELVIN_OFFSET; - - D_TEMP("Calibrated temperature: %dK, %dC\n", temperature, - KELVIN_TO_CELSIUS(temperature)); - - return temperature; -} - -/* Adjust Txpower only if temperature variance is greater than threshold. */ -#define IL_TEMPERATURE_THRESHOLD 3 - -/** - * il4965_is_temp_calib_needed - determines if new calibration is needed - * - * If the temperature changed has changed sufficiently, then a recalibration - * is needed. - * - * Assumes caller will replace il->last_temperature once calibration - * executed. - */ -static int -il4965_is_temp_calib_needed(struct il_priv *il) -{ - int temp_diff; - - if (!test_bit(S_STATS, &il->status)) { - D_TEMP("Temperature not updated -- no stats.\n"); - return 0; - } - - temp_diff = il->temperature - il->last_temperature; - - /* get absolute value */ - if (temp_diff < 0) { - D_POWER("Getting cooler, delta %d\n", temp_diff); - temp_diff = -temp_diff; - } else if (temp_diff == 0) - D_POWER("Temperature unchanged\n"); - else - D_POWER("Getting warmer, delta %d\n", temp_diff); - - if (temp_diff < IL_TEMPERATURE_THRESHOLD) { - D_POWER(" => thermal txpower calib not needed\n"); - return 0; - } - - D_POWER(" => thermal txpower calib needed\n"); - - return 1; -} - -void -il4965_temperature_calib(struct il_priv *il) -{ - s32 temp; - - temp = il4965_hw_get_temperature(il); - if (IL_TX_POWER_TEMPERATURE_OUT_OF_RANGE(temp)) - return; - - if (il->temperature != temp) { - if (il->temperature) - D_TEMP("Temperature changed " "from %dC to %dC\n", - KELVIN_TO_CELSIUS(il->temperature), - KELVIN_TO_CELSIUS(temp)); - else - D_TEMP("Temperature " "initialized to %dC\n", - KELVIN_TO_CELSIUS(temp)); - } - - il->temperature = temp; - set_bit(S_TEMPERATURE, &il->status); - - if (!il->disable_tx_power_cal && - unlikely(!test_bit(S_SCANNING, &il->status)) && - il4965_is_temp_calib_needed(il)) - queue_work(il->workqueue, &il->txpower_work); -} - -static u16 -il4965_get_hcmd_size(u8 cmd_id, u16 len) -{ - switch (cmd_id) { - case C_RXON: - return (u16) sizeof(struct il4965_rxon_cmd); - default: - return len; - } -} - -static u16 -il4965_build_addsta_hcmd(const struct il_addsta_cmd *cmd, u8 * data) -{ - struct il4965_addsta_cmd *addsta = (struct il4965_addsta_cmd *)data; - addsta->mode = cmd->mode; - memcpy(&addsta->sta, &cmd->sta, sizeof(struct sta_id_modify)); - memcpy(&addsta->key, &cmd->key, sizeof(struct il4965_keyinfo)); - addsta->station_flags = cmd->station_flags; - addsta->station_flags_msk = cmd->station_flags_msk; - addsta->tid_disable_tx = cmd->tid_disable_tx; - addsta->add_immediate_ba_tid = cmd->add_immediate_ba_tid; - addsta->remove_immediate_ba_tid = cmd->remove_immediate_ba_tid; - addsta->add_immediate_ba_ssn = cmd->add_immediate_ba_ssn; - addsta->sleep_tx_count = cmd->sleep_tx_count; - addsta->reserved1 = cpu_to_le16(0); - addsta->reserved2 = cpu_to_le16(0); - - return (u16) sizeof(struct il4965_addsta_cmd); -} - -static void -il4965_post_scan(struct il_priv *il) -{ - /* - * Since setting the RXON may have been deferred while - * performing the scan, fire one off if needed - */ - if (memcmp(&il->staging, &il->active, sizeof(il->staging))) - il_commit_rxon(il); -} - -static void -il4965_post_associate(struct il_priv *il) -{ - struct ieee80211_vif *vif = il->vif; - int ret = 0; - - if (!vif || !il->is_open) - return; - - if (test_bit(S_EXIT_PENDING, &il->status)) - return; - - il_scan_cancel_timeout(il, 200); - - il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - il_commit_rxon(il); - - ret = il_send_rxon_timing(il); - if (ret) - IL_WARN("RXON timing - " "Attempting to continue.\n"); - - il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - - il_set_rxon_ht(il, &il->current_ht_config); - - if (il->ops->set_rxon_chain) - il->ops->set_rxon_chain(il); - - il->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid); - - D_ASSOC("assoc id %d beacon interval %d\n", vif->bss_conf.aid, - vif->bss_conf.beacon_int); - - if (vif->bss_conf.use_short_preamble) - il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; - else - il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - - if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { - if (vif->bss_conf.use_short_slot) - il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - else - il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - } - - il_commit_rxon(il); - - D_ASSOC("Associated as %d to: %pM\n", vif->bss_conf.aid, - il->active.bssid_addr); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - break; - case NL80211_IFTYPE_ADHOC: - il4965_send_beacon_cmd(il); - break; - default: - IL_ERR("%s Should not be called in %d mode\n", __func__, - vif->type); - break; - } - - /* the chain noise calibration will enabled PM upon completion - * If chain noise has already been run, then we need to enable - * power management here */ - if (il->chain_noise_data.state == IL_CHAIN_NOISE_DONE) - il_power_update_mode(il, false); - - /* Enable Rx differential gain and sensitivity calibrations */ - il4965_chain_noise_reset(il); - il->start_calib = 1; -} - -static void -il4965_config_ap(struct il_priv *il) -{ - struct ieee80211_vif *vif = il->vif; - int ret = 0; - - lockdep_assert_held(&il->mutex); - - if (test_bit(S_EXIT_PENDING, &il->status)) - return; - - /* The following should be done only at AP bring up */ - if (!il_is_associated(il)) { - - /* RXON - unassoc (to set timing command) */ - il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - il_commit_rxon(il); - - /* RXON Timing */ - ret = il_send_rxon_timing(il); - if (ret) - IL_WARN("RXON timing failed - " - "Attempting to continue.\n"); - - /* AP has all antennas */ - il->chain_noise_data.active_chains = il->hw_params.valid_rx_ant; - il_set_rxon_ht(il, &il->current_ht_config); - if (il->ops->set_rxon_chain) - il->ops->set_rxon_chain(il); - - il->staging.assoc_id = 0; - - if (vif->bss_conf.use_short_preamble) - il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; - else - il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - - if (il->staging.flags & RXON_FLG_BAND_24G_MSK) { - if (vif->bss_conf.use_short_slot) - il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - else - il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - } - /* need to send beacon cmd before committing assoc RXON! */ - il4965_send_beacon_cmd(il); - /* restore RXON assoc */ - il->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - il_commit_rxon(il); - } - il4965_send_beacon_cmd(il); -} - -const struct il_ops il4965_ops = { - .txq_update_byte_cnt_tbl = il4965_txq_update_byte_cnt_tbl, - .txq_attach_buf_to_tfd = il4965_hw_txq_attach_buf_to_tfd, - .txq_free_tfd = il4965_hw_txq_free_tfd, - .txq_init = il4965_hw_tx_queue_init, - .is_valid_rtc_data_addr = il4965_hw_valid_rtc_data_addr, - .init_alive_start = il4965_init_alive_start, - .load_ucode = il4965_load_bsm, - .dump_nic_error_log = il4965_dump_nic_error_log, - .dump_fh = il4965_dump_fh, - .set_channel_switch = il4965_hw_channel_switch, - .apm_init = il_apm_init, - .send_tx_power = il4965_send_tx_power, - .update_chain_flags = il4965_update_chain_flags, - .eeprom_acquire_semaphore = il4965_eeprom_acquire_semaphore, - .eeprom_release_semaphore = il4965_eeprom_release_semaphore, - - .rxon_assoc = il4965_send_rxon_assoc, - .commit_rxon = il4965_commit_rxon, - .set_rxon_chain = il4965_set_rxon_chain, - - .get_hcmd_size = il4965_get_hcmd_size, - .build_addsta_hcmd = il4965_build_addsta_hcmd, - .request_scan = il4965_request_scan, - .post_scan = il4965_post_scan, - - .post_associate = il4965_post_associate, - .config_ap = il4965_config_ap, - .manage_ibss_station = il4965_manage_ibss_station, - .update_bcast_stations = il4965_update_bcast_stations, - - .send_led_cmd = il4965_send_led_cmd, -}; - -struct il_cfg il4965_cfg = { - .name = "Intel(R) Wireless WiFi Link 4965AGN", - .fw_name_pre = IL4965_FW_PRE, - .ucode_api_max = IL4965_UCODE_API_MAX, - .ucode_api_min = IL4965_UCODE_API_MIN, - .sku = IL_SKU_A | IL_SKU_G | IL_SKU_N, - .valid_tx_ant = ANT_AB, - .valid_rx_ant = ANT_ABC, - .eeprom_ver = EEPROM_4965_EEPROM_VERSION, - .eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION, - .mod_params = &il4965_mod_params, - .led_mode = IL_LED_BLINK, - /* - * Force use of chains B and C for scan RX on 5 GHz band - * because the device has off-channel reception on chain A. - */ - .scan_rx_antennas[IEEE80211_BAND_5GHZ] = ANT_BC, - - .eeprom_size = IL4965_EEPROM_IMG_SIZE, - .num_of_queues = IL49_NUM_QUEUES, - .num_of_ampdu_queues = IL49_NUM_AMPDU_QUEUES, - .pll_cfg_val = 0, - .set_l0s = true, - .use_bsm = true, - .led_compensation = 61, - .chain_noise_num_beacons = IL4965_CAL_NUM_BEACONS, - .wd_timeout = IL_DEF_WD_TIMEOUT, - .temperature_kelvin = true, - .ucode_tracing = true, - .sensitivity_calib_by_driver = true, - .chain_noise_calib_by_driver = true, - - .regulatory_bands = { - EEPROM_REGULATORY_BAND_1_CHANNELS, - EEPROM_REGULATORY_BAND_2_CHANNELS, - EEPROM_REGULATORY_BAND_3_CHANNELS, - EEPROM_REGULATORY_BAND_4_CHANNELS, - EEPROM_REGULATORY_BAND_5_CHANNELS, - EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS, - EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS - }, - -}; - -/* Module firmware */ -MODULE_FIRMWARE(IL4965_MODULE_FIRMWARE(IL4965_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlegacy/4965.h b/drivers/net/wireless/iwlegacy/4965.h deleted file mode 100644 index 8ab8706f9422..000000000000 --- a/drivers/net/wireless/iwlegacy/4965.h +++ /dev/null @@ -1,1285 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __il_4965_h__ -#define __il_4965_h__ - -struct il_rx_queue; -struct il_rx_buf; -struct il_rx_pkt; -struct il_tx_queue; -struct il_rxon_context; - -/* configuration for the _4965 devices */ -extern struct il_cfg il4965_cfg; -extern const struct il_ops il4965_ops; - -extern struct il_mod_params il4965_mod_params; - -/* tx queue */ -void il4965_free_tfds_in_queue(struct il_priv *il, int sta_id, int tid, - int freed); - -/* RXON */ -void il4965_set_rxon_chain(struct il_priv *il); - -/* uCode */ -int il4965_verify_ucode(struct il_priv *il); - -/* lib */ -void il4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status); - -void il4965_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq); -int il4965_rx_init(struct il_priv *il, struct il_rx_queue *rxq); -int il4965_hw_nic_init(struct il_priv *il); -int il4965_dump_fh(struct il_priv *il, char **buf, bool display); - -void il4965_nic_config(struct il_priv *il); - -/* rx */ -void il4965_rx_queue_restock(struct il_priv *il); -void il4965_rx_replenish(struct il_priv *il); -void il4965_rx_replenish_now(struct il_priv *il); -void il4965_rx_queue_free(struct il_priv *il, struct il_rx_queue *rxq); -int il4965_rxq_stop(struct il_priv *il); -int il4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); -void il4965_rx_handle(struct il_priv *il); - -/* tx */ -void il4965_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq); -int il4965_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, - dma_addr_t addr, u16 len, u8 reset, u8 pad); -int il4965_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq); -void il4965_hwrate_to_tx_control(struct il_priv *il, u32 rate_n_flags, - struct ieee80211_tx_info *info); -int il4965_tx_skb(struct il_priv *il, - struct ieee80211_sta *sta, - struct sk_buff *skb); -int il4965_tx_agg_start(struct il_priv *il, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 * ssn); -int il4965_tx_agg_stop(struct il_priv *il, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid); -int il4965_txq_check_empty(struct il_priv *il, int sta_id, u8 tid, int txq_id); -int il4965_tx_queue_reclaim(struct il_priv *il, int txq_id, int idx); -void il4965_hw_txq_ctx_free(struct il_priv *il); -int il4965_txq_ctx_alloc(struct il_priv *il); -void il4965_txq_ctx_reset(struct il_priv *il); -void il4965_txq_ctx_stop(struct il_priv *il); -void il4965_txq_set_sched(struct il_priv *il, u32 mask); - -/* - * Acquire il->lock before calling this function ! - */ -void il4965_set_wr_ptrs(struct il_priv *il, int txq_id, u32 idx); -/** - * il4965_tx_queue_set_status - (optionally) start Tx/Cmd queue - * @tx_fifo_id: Tx DMA/FIFO channel (range 0-7) that the queue will feed - * @scd_retry: (1) Indicates queue will be used in aggregation mode - * - * NOTE: Acquire il->lock before calling this function ! - */ -void il4965_tx_queue_set_status(struct il_priv *il, struct il_tx_queue *txq, - int tx_fifo_id, int scd_retry); - -/* scan */ -int il4965_request_scan(struct il_priv *il, struct ieee80211_vif *vif); - -/* station mgmt */ -int il4965_manage_ibss_station(struct il_priv *il, struct ieee80211_vif *vif, - bool add); - -/* hcmd */ -int il4965_send_beacon_cmd(struct il_priv *il); - -#ifdef CONFIG_IWLEGACY_DEBUG -const char *il4965_get_tx_fail_reason(u32 status); -#else -static inline const char * -il4965_get_tx_fail_reason(u32 status) -{ - return ""; -} -#endif - -/* station management */ -int il4965_alloc_bcast_station(struct il_priv *il); -int il4965_add_bssid_station(struct il_priv *il, const u8 *addr, u8 *sta_id_r); -int il4965_remove_default_wep_key(struct il_priv *il, - struct ieee80211_key_conf *key); -int il4965_set_default_wep_key(struct il_priv *il, - struct ieee80211_key_conf *key); -int il4965_restore_default_wep_keys(struct il_priv *il); -int il4965_set_dynamic_key(struct il_priv *il, - struct ieee80211_key_conf *key, u8 sta_id); -int il4965_remove_dynamic_key(struct il_priv *il, - struct ieee80211_key_conf *key, u8 sta_id); -void il4965_update_tkip_key(struct il_priv *il, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, - u16 *phase1key); -int il4965_sta_tx_modify_enable_tid(struct il_priv *il, int sta_id, int tid); -int il4965_sta_rx_agg_start(struct il_priv *il, struct ieee80211_sta *sta, - int tid, u16 ssn); -int il4965_sta_rx_agg_stop(struct il_priv *il, struct ieee80211_sta *sta, - int tid); -void il4965_sta_modify_sleep_tx_count(struct il_priv *il, int sta_id, int cnt); -int il4965_update_bcast_stations(struct il_priv *il); - -/* rate */ -static inline u8 -il4965_hw_get_rate(__le32 rate_n_flags) -{ - return le32_to_cpu(rate_n_flags) & 0xFF; -} - -/* eeprom */ -void il4965_eeprom_get_mac(const struct il_priv *il, u8 * mac); -int il4965_eeprom_acquire_semaphore(struct il_priv *il); -void il4965_eeprom_release_semaphore(struct il_priv *il); -int il4965_eeprom_check_version(struct il_priv *il); - -/* mac80211 handlers (for 4965) */ -void il4965_mac_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb); -int il4965_mac_start(struct ieee80211_hw *hw); -void il4965_mac_stop(struct ieee80211_hw *hw); -void il4965_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, u64 multicast); -int il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key); -void il4965_mac_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, - u16 *phase1key); -int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size, bool amsdu); -int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -void -il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_channel_switch *ch_switch); - -void il4965_led_enable(struct il_priv *il); - -/* EEPROM */ -#define IL4965_EEPROM_IMG_SIZE 1024 - -/* - * uCode queue management definitions ... - * The first queue used for block-ack aggregation is #7 (4965 only). - * All block-ack aggregation queues should map to Tx DMA/FIFO channel 7. - */ -#define IL49_FIRST_AMPDU_QUEUE 7 - -/* Sizes and addresses for instruction and data memory (SRAM) in - * 4965's embedded processor. Driver access is via HBUS_TARG_MEM_* regs. */ -#define IL49_RTC_INST_LOWER_BOUND (0x000000) -#define IL49_RTC_INST_UPPER_BOUND (0x018000) - -#define IL49_RTC_DATA_LOWER_BOUND (0x800000) -#define IL49_RTC_DATA_UPPER_BOUND (0x80A000) - -#define IL49_RTC_INST_SIZE (IL49_RTC_INST_UPPER_BOUND - \ - IL49_RTC_INST_LOWER_BOUND) -#define IL49_RTC_DATA_SIZE (IL49_RTC_DATA_UPPER_BOUND - \ - IL49_RTC_DATA_LOWER_BOUND) - -#define IL49_MAX_INST_SIZE IL49_RTC_INST_SIZE -#define IL49_MAX_DATA_SIZE IL49_RTC_DATA_SIZE - -/* Size of uCode instruction memory in bootstrap state machine */ -#define IL49_MAX_BSM_SIZE BSM_SRAM_SIZE - -static inline int -il4965_hw_valid_rtc_data_addr(u32 addr) -{ - return (addr >= IL49_RTC_DATA_LOWER_BOUND && - addr < IL49_RTC_DATA_UPPER_BOUND); -} - -/********************* START TEMPERATURE *************************************/ - -/** - * 4965 temperature calculation. - * - * The driver must calculate the device temperature before calculating - * a txpower setting (amplifier gain is temperature dependent). The - * calculation uses 4 measurements, 3 of which (R1, R2, R3) are calibration - * values used for the life of the driver, and one of which (R4) is the - * real-time temperature indicator. - * - * uCode provides all 4 values to the driver via the "initialize alive" - * notification (see struct il4965_init_alive_resp). After the runtime uCode - * image loads, uCode updates the R4 value via stats notifications - * (see N_STATS), which occur after each received beacon - * when associated, or can be requested via C_STATS. - * - * NOTE: uCode provides the R4 value as a 23-bit signed value. Driver - * must sign-extend to 32 bits before applying formula below. - * - * Formula: - * - * degrees Kelvin = ((97 * 259 * (R4 - R2) / (R3 - R1)) / 100) + 8 - * - * NOTE: The basic formula is 259 * (R4-R2) / (R3-R1). The 97/100 is - * an additional correction, which should be centered around 0 degrees - * Celsius (273 degrees Kelvin). The 8 (3 percent of 273) compensates for - * centering the 97/100 correction around 0 degrees K. - * - * Add 273 to Kelvin value to find degrees Celsius, for comparing current - * temperature with factory-measured temperatures when calculating txpower - * settings. - */ -#define TEMPERATURE_CALIB_KELVIN_OFFSET 8 -#define TEMPERATURE_CALIB_A_VAL 259 - -/* Limit range of calculated temperature to be between these Kelvin values */ -#define IL_TX_POWER_TEMPERATURE_MIN (263) -#define IL_TX_POWER_TEMPERATURE_MAX (410) - -#define IL_TX_POWER_TEMPERATURE_OUT_OF_RANGE(t) \ - ((t) < IL_TX_POWER_TEMPERATURE_MIN || \ - (t) > IL_TX_POWER_TEMPERATURE_MAX) - -void il4965_temperature_calib(struct il_priv *il); -/********************* END TEMPERATURE ***************************************/ - -/********************* START TXPOWER *****************************************/ - -/** - * 4965 txpower calculations rely on information from three sources: - * - * 1) EEPROM - * 2) "initialize" alive notification - * 3) stats notifications - * - * EEPROM data consists of: - * - * 1) Regulatory information (max txpower and channel usage flags) is provided - * separately for each channel that can possibly supported by 4965. - * 40 MHz wide (.11n HT40) channels are listed separately from 20 MHz - * (legacy) channels. - * - * See struct il4965_eeprom_channel for format, and struct il4965_eeprom - * for locations in EEPROM. - * - * 2) Factory txpower calibration information is provided separately for - * sub-bands of contiguous channels. 2.4GHz has just one sub-band, - * but 5 GHz has several sub-bands. - * - * In addition, per-band (2.4 and 5 Ghz) saturation txpowers are provided. - * - * See struct il4965_eeprom_calib_info (and the tree of structures - * contained within it) for format, and struct il4965_eeprom for - * locations in EEPROM. - * - * "Initialization alive" notification (see struct il4965_init_alive_resp) - * consists of: - * - * 1) Temperature calculation parameters. - * - * 2) Power supply voltage measurement. - * - * 3) Tx gain compensation to balance 2 transmitters for MIMO use. - * - * Statistics notifications deliver: - * - * 1) Current values for temperature param R4. - */ - -/** - * To calculate a txpower setting for a given desired target txpower, channel, - * modulation bit rate, and transmitter chain (4965 has 2 transmitters to - * support MIMO and transmit diversity), driver must do the following: - * - * 1) Compare desired txpower vs. (EEPROM) regulatory limit for this channel. - * Do not exceed regulatory limit; reduce target txpower if necessary. - * - * If setting up txpowers for MIMO rates (rate idxes 8-15, 24-31), - * 2 transmitters will be used simultaneously; driver must reduce the - * regulatory limit by 3 dB (half-power) for each transmitter, so the - * combined total output of the 2 transmitters is within regulatory limits. - * - * - * 2) Compare target txpower vs. (EEPROM) saturation txpower *reduced by - * backoff for this bit rate*. Do not exceed (saturation - backoff[rate]); - * reduce target txpower if necessary. - * - * Backoff values below are in 1/2 dB units (equivalent to steps in - * txpower gain tables): - * - * OFDM 6 - 36 MBit: 10 steps (5 dB) - * OFDM 48 MBit: 15 steps (7.5 dB) - * OFDM 54 MBit: 17 steps (8.5 dB) - * OFDM 60 MBit: 20 steps (10 dB) - * CCK all rates: 10 steps (5 dB) - * - * Backoff values apply to saturation txpower on a per-transmitter basis; - * when using MIMO (2 transmitters), each transmitter uses the same - * saturation level provided in EEPROM, and the same backoff values; - * no reduction (such as with regulatory txpower limits) is required. - * - * Saturation and Backoff values apply equally to 20 Mhz (legacy) channel - * widths and 40 Mhz (.11n HT40) channel widths; there is no separate - * factory measurement for ht40 channels. - * - * The result of this step is the final target txpower. The rest of - * the steps figure out the proper settings for the device to achieve - * that target txpower. - * - * - * 3) Determine (EEPROM) calibration sub band for the target channel, by - * comparing against first and last channels in each sub band - * (see struct il4965_eeprom_calib_subband_info). - * - * - * 4) Linearly interpolate (EEPROM) factory calibration measurement sets, - * referencing the 2 factory-measured (sample) channels within the sub band. - * - * Interpolation is based on difference between target channel's frequency - * and the sample channels' frequencies. Since channel numbers are based - * on frequency (5 MHz between each channel number), this is equivalent - * to interpolating based on channel number differences. - * - * Note that the sample channels may or may not be the channels at the - * edges of the sub band. The target channel may be "outside" of the - * span of the sampled channels. - * - * Driver may choose the pair (for 2 Tx chains) of measurements (see - * struct il4965_eeprom_calib_ch_info) for which the actual measured - * txpower comes closest to the desired txpower. Usually, though, - * the middle set of measurements is closest to the regulatory limits, - * and is therefore a good choice for all txpower calculations (this - * assumes that high accuracy is needed for maximizing legal txpower, - * while lower txpower configurations do not need as much accuracy). - * - * Driver should interpolate both members of the chosen measurement pair, - * i.e. for both Tx chains (radio transmitters), unless the driver knows - * that only one of the chains will be used (e.g. only one tx antenna - * connected, but this should be unusual). The rate scaling algorithm - * switches antennas to find best performance, so both Tx chains will - * be used (although only one at a time) even for non-MIMO transmissions. - * - * Driver should interpolate factory values for temperature, gain table - * idx, and actual power. The power amplifier detector values are - * not used by the driver. - * - * Sanity check: If the target channel happens to be one of the sample - * channels, the results should agree with the sample channel's - * measurements! - * - * - * 5) Find difference between desired txpower and (interpolated) - * factory-measured txpower. Using (interpolated) factory gain table idx - * (shown elsewhere) as a starting point, adjust this idx lower to - * increase txpower, or higher to decrease txpower, until the target - * txpower is reached. Each step in the gain table is 1/2 dB. - * - * For example, if factory measured txpower is 16 dBm, and target txpower - * is 13 dBm, add 6 steps to the factory gain idx to reduce txpower - * by 3 dB. - * - * - * 6) Find difference between current device temperature and (interpolated) - * factory-measured temperature for sub-band. Factory values are in - * degrees Celsius. To calculate current temperature, see comments for - * "4965 temperature calculation". - * - * If current temperature is higher than factory temperature, driver must - * increase gain (lower gain table idx), and vice verse. - * - * Temperature affects gain differently for different channels: - * - * 2.4 GHz all channels: 3.5 degrees per half-dB step - * 5 GHz channels 34-43: 4.5 degrees per half-dB step - * 5 GHz channels >= 44: 4.0 degrees per half-dB step - * - * NOTE: Temperature can increase rapidly when transmitting, especially - * with heavy traffic at high txpowers. Driver should update - * temperature calculations often under these conditions to - * maintain strong txpower in the face of rising temperature. - * - * - * 7) Find difference between current power supply voltage indicator - * (from "initialize alive") and factory-measured power supply voltage - * indicator (EEPROM). - * - * If the current voltage is higher (indicator is lower) than factory - * voltage, gain should be reduced (gain table idx increased) by: - * - * (eeprom - current) / 7 - * - * If the current voltage is lower (indicator is higher) than factory - * voltage, gain should be increased (gain table idx decreased) by: - * - * 2 * (current - eeprom) / 7 - * - * If number of idx steps in either direction turns out to be > 2, - * something is wrong ... just use 0. - * - * NOTE: Voltage compensation is independent of band/channel. - * - * NOTE: "Initialize" uCode measures current voltage, which is assumed - * to be constant after this initial measurement. Voltage - * compensation for txpower (number of steps in gain table) - * may be calculated once and used until the next uCode bootload. - * - * - * 8) If setting up txpowers for MIMO rates (rate idxes 8-15, 24-31), - * adjust txpower for each transmitter chain, so txpower is balanced - * between the two chains. There are 5 pairs of tx_atten[group][chain] - * values in "initialize alive", one pair for each of 5 channel ranges: - * - * Group 0: 5 GHz channel 34-43 - * Group 1: 5 GHz channel 44-70 - * Group 2: 5 GHz channel 71-124 - * Group 3: 5 GHz channel 125-200 - * Group 4: 2.4 GHz all channels - * - * Add the tx_atten[group][chain] value to the idx for the target chain. - * The values are signed, but are in pairs of 0 and a non-negative number, - * so as to reduce gain (if necessary) of the "hotter" channel. This - * avoids any need to double-check for regulatory compliance after - * this step. - * - * - * 9) If setting up for a CCK rate, lower the gain by adding a CCK compensation - * value to the idx: - * - * Hardware rev B: 9 steps (4.5 dB) - * Hardware rev C: 5 steps (2.5 dB) - * - * Hardware rev for 4965 can be determined by reading CSR_HW_REV_WA_REG, - * bits [3:2], 1 = B, 2 = C. - * - * NOTE: This compensation is in addition to any saturation backoff that - * might have been applied in an earlier step. - * - * - * 10) Select the gain table, based on band (2.4 vs 5 GHz). - * - * Limit the adjusted idx to stay within the table! - * - * - * 11) Read gain table entries for DSP and radio gain, place into appropriate - * location(s) in command (struct il4965_txpowertable_cmd). - */ - -/** - * When MIMO is used (2 transmitters operating simultaneously), driver should - * limit each transmitter to deliver a max of 3 dB below the regulatory limit - * for the device. That is, use half power for each transmitter, so total - * txpower is within regulatory limits. - * - * The value "6" represents number of steps in gain table to reduce power 3 dB. - * Each step is 1/2 dB. - */ -#define IL_TX_POWER_MIMO_REGULATORY_COMPENSATION (6) - -/** - * CCK gain compensation. - * - * When calculating txpowers for CCK, after making sure that the target power - * is within regulatory and saturation limits, driver must additionally - * back off gain by adding these values to the gain table idx. - * - * Hardware rev for 4965 can be determined by reading CSR_HW_REV_WA_REG, - * bits [3:2], 1 = B, 2 = C. - */ -#define IL_TX_POWER_CCK_COMPENSATION_B_STEP (9) -#define IL_TX_POWER_CCK_COMPENSATION_C_STEP (5) - -/* - * 4965 power supply voltage compensation for txpower - */ -#define TX_POWER_IL_VOLTAGE_CODES_PER_03V (7) - -/** - * Gain tables. - * - * The following tables contain pair of values for setting txpower, i.e. - * gain settings for the output of the device's digital signal processor (DSP), - * and for the analog gain structure of the transmitter. - * - * Each entry in the gain tables represents a step of 1/2 dB. Note that these - * are *relative* steps, not indications of absolute output power. Output - * power varies with temperature, voltage, and channel frequency, and also - * requires consideration of average power (to satisfy regulatory constraints), - * and peak power (to avoid distortion of the output signal). - * - * Each entry contains two values: - * 1) DSP gain (or sometimes called DSP attenuation). This is a fine-grained - * linear value that multiplies the output of the digital signal processor, - * before being sent to the analog radio. - * 2) Radio gain. This sets the analog gain of the radio Tx path. - * It is a coarser setting, and behaves in a logarithmic (dB) fashion. - * - * EEPROM contains factory calibration data for txpower. This maps actual - * measured txpower levels to gain settings in the "well known" tables - * below ("well-known" means here that both factory calibration *and* the - * driver work with the same table). - * - * There are separate tables for 2.4 GHz and 5 GHz bands. The 5 GHz table - * has an extension (into negative idxes), in case the driver needs to - * boost power setting for high device temperatures (higher than would be - * present during factory calibration). A 5 Ghz EEPROM idx of "40" - * corresponds to the 49th entry in the table used by the driver. - */ -#define MIN_TX_GAIN_IDX (0) /* highest gain, lowest idx, 2.4 */ -#define MIN_TX_GAIN_IDX_52GHZ_EXT (-9) /* highest gain, lowest idx, 5 */ - -/** - * 2.4 GHz gain table - * - * Index Dsp gain Radio gain - * 0 110 0x3f (highest gain) - * 1 104 0x3f - * 2 98 0x3f - * 3 110 0x3e - * 4 104 0x3e - * 5 98 0x3e - * 6 110 0x3d - * 7 104 0x3d - * 8 98 0x3d - * 9 110 0x3c - * 10 104 0x3c - * 11 98 0x3c - * 12 110 0x3b - * 13 104 0x3b - * 14 98 0x3b - * 15 110 0x3a - * 16 104 0x3a - * 17 98 0x3a - * 18 110 0x39 - * 19 104 0x39 - * 20 98 0x39 - * 21 110 0x38 - * 22 104 0x38 - * 23 98 0x38 - * 24 110 0x37 - * 25 104 0x37 - * 26 98 0x37 - * 27 110 0x36 - * 28 104 0x36 - * 29 98 0x36 - * 30 110 0x35 - * 31 104 0x35 - * 32 98 0x35 - * 33 110 0x34 - * 34 104 0x34 - * 35 98 0x34 - * 36 110 0x33 - * 37 104 0x33 - * 38 98 0x33 - * 39 110 0x32 - * 40 104 0x32 - * 41 98 0x32 - * 42 110 0x31 - * 43 104 0x31 - * 44 98 0x31 - * 45 110 0x30 - * 46 104 0x30 - * 47 98 0x30 - * 48 110 0x6 - * 49 104 0x6 - * 50 98 0x6 - * 51 110 0x5 - * 52 104 0x5 - * 53 98 0x5 - * 54 110 0x4 - * 55 104 0x4 - * 56 98 0x4 - * 57 110 0x3 - * 58 104 0x3 - * 59 98 0x3 - * 60 110 0x2 - * 61 104 0x2 - * 62 98 0x2 - * 63 110 0x1 - * 64 104 0x1 - * 65 98 0x1 - * 66 110 0x0 - * 67 104 0x0 - * 68 98 0x0 - * 69 97 0 - * 70 96 0 - * 71 95 0 - * 72 94 0 - * 73 93 0 - * 74 92 0 - * 75 91 0 - * 76 90 0 - * 77 89 0 - * 78 88 0 - * 79 87 0 - * 80 86 0 - * 81 85 0 - * 82 84 0 - * 83 83 0 - * 84 82 0 - * 85 81 0 - * 86 80 0 - * 87 79 0 - * 88 78 0 - * 89 77 0 - * 90 76 0 - * 91 75 0 - * 92 74 0 - * 93 73 0 - * 94 72 0 - * 95 71 0 - * 96 70 0 - * 97 69 0 - * 98 68 0 - */ - -/** - * 5 GHz gain table - * - * Index Dsp gain Radio gain - * -9 123 0x3F (highest gain) - * -8 117 0x3F - * -7 110 0x3F - * -6 104 0x3F - * -5 98 0x3F - * -4 110 0x3E - * -3 104 0x3E - * -2 98 0x3E - * -1 110 0x3D - * 0 104 0x3D - * 1 98 0x3D - * 2 110 0x3C - * 3 104 0x3C - * 4 98 0x3C - * 5 110 0x3B - * 6 104 0x3B - * 7 98 0x3B - * 8 110 0x3A - * 9 104 0x3A - * 10 98 0x3A - * 11 110 0x39 - * 12 104 0x39 - * 13 98 0x39 - * 14 110 0x38 - * 15 104 0x38 - * 16 98 0x38 - * 17 110 0x37 - * 18 104 0x37 - * 19 98 0x37 - * 20 110 0x36 - * 21 104 0x36 - * 22 98 0x36 - * 23 110 0x35 - * 24 104 0x35 - * 25 98 0x35 - * 26 110 0x34 - * 27 104 0x34 - * 28 98 0x34 - * 29 110 0x33 - * 30 104 0x33 - * 31 98 0x33 - * 32 110 0x32 - * 33 104 0x32 - * 34 98 0x32 - * 35 110 0x31 - * 36 104 0x31 - * 37 98 0x31 - * 38 110 0x30 - * 39 104 0x30 - * 40 98 0x30 - * 41 110 0x25 - * 42 104 0x25 - * 43 98 0x25 - * 44 110 0x24 - * 45 104 0x24 - * 46 98 0x24 - * 47 110 0x23 - * 48 104 0x23 - * 49 98 0x23 - * 50 110 0x22 - * 51 104 0x18 - * 52 98 0x18 - * 53 110 0x17 - * 54 104 0x17 - * 55 98 0x17 - * 56 110 0x16 - * 57 104 0x16 - * 58 98 0x16 - * 59 110 0x15 - * 60 104 0x15 - * 61 98 0x15 - * 62 110 0x14 - * 63 104 0x14 - * 64 98 0x14 - * 65 110 0x13 - * 66 104 0x13 - * 67 98 0x13 - * 68 110 0x12 - * 69 104 0x08 - * 70 98 0x08 - * 71 110 0x07 - * 72 104 0x07 - * 73 98 0x07 - * 74 110 0x06 - * 75 104 0x06 - * 76 98 0x06 - * 77 110 0x05 - * 78 104 0x05 - * 79 98 0x05 - * 80 110 0x04 - * 81 104 0x04 - * 82 98 0x04 - * 83 110 0x03 - * 84 104 0x03 - * 85 98 0x03 - * 86 110 0x02 - * 87 104 0x02 - * 88 98 0x02 - * 89 110 0x01 - * 90 104 0x01 - * 91 98 0x01 - * 92 110 0x00 - * 93 104 0x00 - * 94 98 0x00 - * 95 93 0x00 - * 96 88 0x00 - * 97 83 0x00 - * 98 78 0x00 - */ - -/** - * Sanity checks and default values for EEPROM regulatory levels. - * If EEPROM values fall outside MIN/MAX range, use default values. - * - * Regulatory limits refer to the maximum average txpower allowed by - * regulatory agencies in the geographies in which the device is meant - * to be operated. These limits are SKU-specific (i.e. geography-specific), - * and channel-specific; each channel has an individual regulatory limit - * listed in the EEPROM. - * - * Units are in half-dBm (i.e. "34" means 17 dBm). - */ -#define IL_TX_POWER_DEFAULT_REGULATORY_24 (34) -#define IL_TX_POWER_DEFAULT_REGULATORY_52 (34) -#define IL_TX_POWER_REGULATORY_MIN (0) -#define IL_TX_POWER_REGULATORY_MAX (34) - -/** - * Sanity checks and default values for EEPROM saturation levels. - * If EEPROM values fall outside MIN/MAX range, use default values. - * - * Saturation is the highest level that the output power amplifier can produce - * without significant clipping distortion. This is a "peak" power level. - * Different types of modulation (i.e. various "rates", and OFDM vs. CCK) - * require differing amounts of backoff, relative to their average power output, - * in order to avoid clipping distortion. - * - * Driver must make sure that it is violating neither the saturation limit, - * nor the regulatory limit, when calculating Tx power settings for various - * rates. - * - * Units are in half-dBm (i.e. "38" means 19 dBm). - */ -#define IL_TX_POWER_DEFAULT_SATURATION_24 (38) -#define IL_TX_POWER_DEFAULT_SATURATION_52 (38) -#define IL_TX_POWER_SATURATION_MIN (20) -#define IL_TX_POWER_SATURATION_MAX (50) - -/** - * Channel groups used for Tx Attenuation calibration (MIMO tx channel balance) - * and thermal Txpower calibration. - * - * When calculating txpower, driver must compensate for current device - * temperature; higher temperature requires higher gain. Driver must calculate - * current temperature (see "4965 temperature calculation"), then compare vs. - * factory calibration temperature in EEPROM; if current temperature is higher - * than factory temperature, driver must *increase* gain by proportions shown - * in table below. If current temperature is lower than factory, driver must - * *decrease* gain. - * - * Different frequency ranges require different compensation, as shown below. - */ -/* Group 0, 5.2 GHz ch 34-43: 4.5 degrees per 1/2 dB. */ -#define CALIB_IL_TX_ATTEN_GR1_FCH 34 -#define CALIB_IL_TX_ATTEN_GR1_LCH 43 - -/* Group 1, 5.3 GHz ch 44-70: 4.0 degrees per 1/2 dB. */ -#define CALIB_IL_TX_ATTEN_GR2_FCH 44 -#define CALIB_IL_TX_ATTEN_GR2_LCH 70 - -/* Group 2, 5.5 GHz ch 71-124: 4.0 degrees per 1/2 dB. */ -#define CALIB_IL_TX_ATTEN_GR3_FCH 71 -#define CALIB_IL_TX_ATTEN_GR3_LCH 124 - -/* Group 3, 5.7 GHz ch 125-200: 4.0 degrees per 1/2 dB. */ -#define CALIB_IL_TX_ATTEN_GR4_FCH 125 -#define CALIB_IL_TX_ATTEN_GR4_LCH 200 - -/* Group 4, 2.4 GHz all channels: 3.5 degrees per 1/2 dB. */ -#define CALIB_IL_TX_ATTEN_GR5_FCH 1 -#define CALIB_IL_TX_ATTEN_GR5_LCH 20 - -enum { - CALIB_CH_GROUP_1 = 0, - CALIB_CH_GROUP_2 = 1, - CALIB_CH_GROUP_3 = 2, - CALIB_CH_GROUP_4 = 3, - CALIB_CH_GROUP_5 = 4, - CALIB_CH_GROUP_MAX -}; - -/********************* END TXPOWER *****************************************/ - -/** - * Tx/Rx Queues - * - * Most communication between driver and 4965 is via queues of data buffers. - * For example, all commands that the driver issues to device's embedded - * controller (uCode) are via the command queue (one of the Tx queues). All - * uCode command responses/replies/notifications, including Rx frames, are - * conveyed from uCode to driver via the Rx queue. - * - * Most support for these queues, including handshake support, resides in - * structures in host DRAM, shared between the driver and the device. When - * allocating this memory, the driver must make sure that data written by - * the host CPU updates DRAM immediately (and does not get "stuck" in CPU's - * cache memory), so DRAM and cache are consistent, and the device can - * immediately see changes made by the driver. - * - * 4965 supports up to 16 DRAM-based Tx queues, and services these queues via - * up to 7 DMA channels (FIFOs). Each Tx queue is supported by a circular array - * in DRAM containing 256 Transmit Frame Descriptors (TFDs). - */ -#define IL49_NUM_FIFOS 7 -#define IL49_CMD_FIFO_NUM 4 -#define IL49_NUM_QUEUES 16 -#define IL49_NUM_AMPDU_QUEUES 8 - -/** - * struct il4965_schedq_bc_tbl - * - * Byte Count table - * - * Each Tx queue uses a byte-count table containing 320 entries: - * one 16-bit entry for each of 256 TFDs, plus an additional 64 entries that - * duplicate the first 64 entries (to avoid wrap-around within a Tx win; - * max Tx win is 64 TFDs). - * - * When driver sets up a new TFD, it must also enter the total byte count - * of the frame to be transmitted into the corresponding entry in the byte - * count table for the chosen Tx queue. If the TFD idx is 0-63, the driver - * must duplicate the byte count entry in corresponding idx 256-319. - * - * padding puts each byte count table on a 1024-byte boundary; - * 4965 assumes tables are separated by 1024 bytes. - */ -struct il4965_scd_bc_tbl { - __le16 tfd_offset[TFD_QUEUE_BC_SIZE]; - u8 pad[1024 - (TFD_QUEUE_BC_SIZE) * sizeof(__le16)]; -} __packed; - -#define IL4965_RTC_INST_LOWER_BOUND (0x000000) - -/* RSSI to dBm */ -#define IL4965_RSSI_OFFSET 44 - -/* PCI registers */ -#define PCI_CFG_RETRY_TIMEOUT 0x041 - -#define IL4965_DEFAULT_TX_RETRY 15 - -/* EEPROM */ -#define IL4965_FIRST_AMPDU_QUEUE 10 - -/* Calibration */ -void il4965_chain_noise_calibration(struct il_priv *il, void *stat_resp); -void il4965_sensitivity_calibration(struct il_priv *il, void *resp); -void il4965_init_sensitivity(struct il_priv *il); -void il4965_reset_run_time_calib(struct il_priv *il); - -/* Debug */ -#ifdef CONFIG_IWLEGACY_DEBUGFS -extern const struct il_debugfs_ops il4965_debugfs_ops; -#endif - -/****************************/ -/* Flow Handler Definitions */ -/****************************/ - -/** - * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) - * Addresses are offsets from device's PCI hardware base address. - */ -#define FH49_MEM_LOWER_BOUND (0x1000) -#define FH49_MEM_UPPER_BOUND (0x2000) - -/** - * Keep-Warm (KW) buffer base address. - * - * Driver must allocate a 4KByte buffer that is used by 4965 for keeping the - * host DRAM powered on (via dummy accesses to DRAM) to maintain low-latency - * DRAM access when 4965 is Txing or Rxing. The dummy accesses prevent host - * from going into a power-savings mode that would cause higher DRAM latency, - * and possible data over/under-runs, before all Tx/Rx is complete. - * - * Driver loads FH49_KW_MEM_ADDR_REG with the physical address (bits 35:4) - * of the buffer, which must be 4K aligned. Once this is set up, the 4965 - * automatically invokes keep-warm accesses when normal accesses might not - * be sufficient to maintain fast DRAM response. - * - * Bit fields: - * 31-0: Keep-warm buffer physical base address [35:4], must be 4K aligned - */ -#define FH49_KW_MEM_ADDR_REG (FH49_MEM_LOWER_BOUND + 0x97C) - -/** - * TFD Circular Buffers Base (CBBC) addresses - * - * 4965 has 16 base pointer registers, one for each of 16 host-DRAM-resident - * circular buffers (CBs/queues) containing Transmit Frame Descriptors (TFDs) - * (see struct il_tfd_frame). These 16 pointer registers are offset by 0x04 - * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte - * aligned (address bits 0-7 must be 0). - * - * Bit fields in each pointer register: - * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned - */ -#define FH49_MEM_CBBC_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0x9D0) -#define FH49_MEM_CBBC_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xA10) - -/* Find TFD CB base pointer for given queue (range 0-15). */ -#define FH49_MEM_CBBC_QUEUE(x) (FH49_MEM_CBBC_LOWER_BOUND + (x) * 0x4) - -/** - * Rx SRAM Control and Status Registers (RSCSR) - * - * These registers provide handshake between driver and 4965 for the Rx queue - * (this queue handles *all* command responses, notifications, Rx data, etc. - * sent from 4965 uCode to host driver). Unlike Tx, there is only one Rx - * queue, and only one Rx DMA/FIFO channel. Also unlike Tx, which can - * concatenate up to 20 DRAM buffers to form a Tx frame, each Receive Buffer - * Descriptor (RBD) points to only one Rx Buffer (RB); there is a 1:1 - * mapping between RBDs and RBs. - * - * Driver must allocate host DRAM memory for the following, and set the - * physical address of each into 4965 registers: - * - * 1) Receive Buffer Descriptor (RBD) circular buffer (CB), typically with 256 - * entries (although any power of 2, up to 4096, is selectable by driver). - * Each entry (1 dword) points to a receive buffer (RB) of consistent size - * (typically 4K, although 8K or 16K are also selectable by driver). - * Driver sets up RB size and number of RBDs in the CB via Rx config - * register FH49_MEM_RCSR_CHNL0_CONFIG_REG. - * - * Bit fields within one RBD: - * 27-0: Receive Buffer physical address bits [35:8], 256-byte aligned - * - * Driver sets physical address [35:8] of base of RBD circular buffer - * into FH49_RSCSR_CHNL0_RBDCB_BASE_REG [27:0]. - * - * 2) Rx status buffer, 8 bytes, in which 4965 indicates which Rx Buffers - * (RBs) have been filled, via a "write pointer", actually the idx of - * the RB's corresponding RBD within the circular buffer. Driver sets - * physical address [35:4] into FH49_RSCSR_CHNL0_STTS_WPTR_REG [31:0]. - * - * Bit fields in lower dword of Rx status buffer (upper dword not used - * by driver; see struct il4965_shared, val0): - * 31-12: Not used by driver - * 11- 0: Index of last filled Rx buffer descriptor - * (4965 writes, driver reads this value) - * - * As the driver prepares Receive Buffers (RBs) for 4965 to fill, driver must - * enter pointers to these RBs into contiguous RBD circular buffer entries, - * and update the 4965's "write" idx register, - * FH49_RSCSR_CHNL0_RBDCB_WPTR_REG. - * - * This "write" idx corresponds to the *next* RBD that the driver will make - * available, i.e. one RBD past the tail of the ready-to-fill RBDs within - * the circular buffer. This value should initially be 0 (before preparing any - * RBs), should be 8 after preparing the first 8 RBs (for example), and must - * wrap back to 0 at the end of the circular buffer (but don't wrap before - * "read" idx has advanced past 1! See below). - * NOTE: 4965 EXPECTS THE WRITE IDX TO BE INCREMENTED IN MULTIPLES OF 8. - * - * As the 4965 fills RBs (referenced from contiguous RBDs within the circular - * buffer), it updates the Rx status buffer in host DRAM, 2) described above, - * to tell the driver the idx of the latest filled RBD. The driver must - * read this "read" idx from DRAM after receiving an Rx interrupt from 4965. - * - * The driver must also internally keep track of a third idx, which is the - * next RBD to process. When receiving an Rx interrupt, driver should process - * all filled but unprocessed RBs up to, but not including, the RB - * corresponding to the "read" idx. For example, if "read" idx becomes "1", - * driver may process the RB pointed to by RBD 0. Depending on volume of - * traffic, there may be many RBs to process. - * - * If read idx == write idx, 4965 thinks there is no room to put new data. - * Due to this, the maximum number of filled RBs is 255, instead of 256. To - * be safe, make sure that there is a gap of at least 2 RBDs between "write" - * and "read" idxes; that is, make sure that there are no more than 254 - * buffers waiting to be filled. - */ -#define FH49_MEM_RSCSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xBC0) -#define FH49_MEM_RSCSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xC00) -#define FH49_MEM_RSCSR_CHNL0 (FH49_MEM_RSCSR_LOWER_BOUND) - -/** - * Physical base address of 8-byte Rx Status buffer. - * Bit fields: - * 31-0: Rx status buffer physical base address [35:4], must 16-byte aligned. - */ -#define FH49_RSCSR_CHNL0_STTS_WPTR_REG (FH49_MEM_RSCSR_CHNL0) - -/** - * Physical base address of Rx Buffer Descriptor Circular Buffer. - * Bit fields: - * 27-0: RBD CD physical base address [35:8], must be 256-byte aligned. - */ -#define FH49_RSCSR_CHNL0_RBDCB_BASE_REG (FH49_MEM_RSCSR_CHNL0 + 0x004) - -/** - * Rx write pointer (idx, really!). - * Bit fields: - * 11-0: Index of driver's most recent prepared-to-be-filled RBD, + 1. - * NOTE: For 256-entry circular buffer, use only bits [7:0]. - */ -#define FH49_RSCSR_CHNL0_RBDCB_WPTR_REG (FH49_MEM_RSCSR_CHNL0 + 0x008) -#define FH49_RSCSR_CHNL0_WPTR (FH49_RSCSR_CHNL0_RBDCB_WPTR_REG) - -/** - * Rx Config/Status Registers (RCSR) - * Rx Config Reg for channel 0 (only channel used) - * - * Driver must initialize FH49_MEM_RCSR_CHNL0_CONFIG_REG as follows for - * normal operation (see bit fields). - * - * Clearing FH49_MEM_RCSR_CHNL0_CONFIG_REG to 0 turns off Rx DMA. - * Driver should poll FH49_MEM_RSSR_RX_STATUS_REG for - * FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (bit 24) before continuing. - * - * Bit fields: - * 31-30: Rx DMA channel enable: '00' off/pause, '01' pause at end of frame, - * '10' operate normally - * 29-24: reserved - * 23-20: # RBDs in circular buffer = 2^value; use "8" for 256 RBDs (normal), - * min "5" for 32 RBDs, max "12" for 4096 RBDs. - * 19-18: reserved - * 17-16: size of each receive buffer; '00' 4K (normal), '01' 8K, - * '10' 12K, '11' 16K. - * 15-14: reserved - * 13-12: IRQ destination; '00' none, '01' host driver (normal operation) - * 11- 4: timeout for closing Rx buffer and interrupting host (units 32 usec) - * typical value 0x10 (about 1/2 msec) - * 3- 0: reserved - */ -#define FH49_MEM_RCSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xC00) -#define FH49_MEM_RCSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xCC0) -#define FH49_MEM_RCSR_CHNL0 (FH49_MEM_RCSR_LOWER_BOUND) - -#define FH49_MEM_RCSR_CHNL0_CONFIG_REG (FH49_MEM_RCSR_CHNL0) - -#define FH49_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */ -#define FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */ -#define FH49_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK (0x00008000) /* bit 15 */ -#define FH49_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MSK (0x00030000) /* bits 16-17 */ -#define FH49_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MSK (0x00F00000) /* bits 20-23 */ -#define FH49_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MSK (0xC0000000) /* bits 30-31 */ - -#define FH49_RCSR_RX_CONFIG_RBDCB_SIZE_POS (20) -#define FH49_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS (4) -#define RX_RB_TIMEOUT (0x10) - -#define FH49_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000) -#define FH49_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000) -#define FH49_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL (0x80000000) - -#define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000) -#define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000) -#define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000) -#define FH49_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000) - -#define FH49_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY (0x00000004) -#define FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000) -#define FH49_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000) - -/** - * Rx Shared Status Registers (RSSR) - * - * After stopping Rx DMA channel (writing 0 to - * FH49_MEM_RCSR_CHNL0_CONFIG_REG), driver must poll - * FH49_MEM_RSSR_RX_STATUS_REG until Rx channel is idle. - * - * Bit fields: - * 24: 1 = Channel 0 is idle - * - * FH49_MEM_RSSR_SHARED_CTRL_REG and FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV - * contain default values that should not be altered by the driver. - */ -#define FH49_MEM_RSSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xC40) -#define FH49_MEM_RSSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xD00) - -#define FH49_MEM_RSSR_SHARED_CTRL_REG (FH49_MEM_RSSR_LOWER_BOUND) -#define FH49_MEM_RSSR_RX_STATUS_REG (FH49_MEM_RSSR_LOWER_BOUND + 0x004) -#define FH49_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV\ - (FH49_MEM_RSSR_LOWER_BOUND + 0x008) - -#define FH49_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) - -#define FH49_MEM_TFDIB_REG1_ADDR_BITSHIFT 28 - -/* TFDB Area - TFDs buffer table */ -#define FH49_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF) -#define FH49_TFDIB_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0x900) -#define FH49_TFDIB_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0x958) -#define FH49_TFDIB_CTRL0_REG(_chnl) (FH49_TFDIB_LOWER_BOUND + 0x8 * (_chnl)) -#define FH49_TFDIB_CTRL1_REG(_chnl) (FH49_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4) - -/** - * Transmit DMA Channel Control/Status Registers (TCSR) - * - * 4965 has one configuration register for each of 8 Tx DMA/FIFO channels - * supported in hardware (don't confuse these with the 16 Tx queues in DRAM, - * which feed the DMA/FIFO channels); config regs are separated by 0x20 bytes. - * - * To use a Tx DMA channel, driver must initialize its - * FH49_TCSR_CHNL_TX_CONFIG_REG(chnl) with: - * - * FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - * FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL - * - * All other bits should be 0. - * - * Bit fields: - * 31-30: Tx DMA channel enable: '00' off/pause, '01' pause at end of frame, - * '10' operate normally - * 29- 4: Reserved, set to "0" - * 3: Enable internal DMA requests (1, normal operation), disable (0) - * 2- 0: Reserved, set to "0" - */ -#define FH49_TCSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xD00) -#define FH49_TCSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xE60) - -/* Find Control/Status reg for given Tx DMA/FIFO channel */ -#define FH49_TCSR_CHNL_NUM (7) -#define FH50_TCSR_CHNL_NUM (8) - -/* TCSR: tx_config register values */ -#define FH49_TCSR_CHNL_TX_CONFIG_REG(_chnl) \ - (FH49_TCSR_LOWER_BOUND + 0x20 * (_chnl)) -#define FH49_TCSR_CHNL_TX_CREDIT_REG(_chnl) \ - (FH49_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x4) -#define FH49_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \ - (FH49_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x8) - -#define FH49_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000) -#define FH49_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRV (0x00000001) - -#define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE (0x00000000) -#define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE (0x00000008) - -#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000) -#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000) -#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000) - -#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000) -#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000) -#define FH49_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000) - -#define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000) -#define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000) -#define FH49_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000) - -#define FH49_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000) -#define FH49_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000) -#define FH49_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003) - -#define FH49_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20) -#define FH49_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12) - -/** - * Tx Shared Status Registers (TSSR) - * - * After stopping Tx DMA channel (writing 0 to - * FH49_TCSR_CHNL_TX_CONFIG_REG(chnl)), driver must poll - * FH49_TSSR_TX_STATUS_REG until selected Tx channel is idle - * (channel's buffers empty | no pending requests). - * - * Bit fields: - * 31-24: 1 = Channel buffers empty (channel 7:0) - * 23-16: 1 = No pending requests (channel 7:0) - */ -#define FH49_TSSR_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0xEA0) -#define FH49_TSSR_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0xEC0) - -#define FH49_TSSR_TX_STATUS_REG (FH49_TSSR_LOWER_BOUND + 0x010) - -/** - * Bit fields for TSSR(Tx Shared Status & Control) error status register: - * 31: Indicates an address error when accessed to internal memory - * uCode/driver must write "1" in order to clear this flag - * 30: Indicates that Host did not send the expected number of dwords to FH - * uCode/driver must write "1" in order to clear this flag - * 16-9:Each status bit is for one channel. Indicates that an (Error) ActDMA - * command was received from the scheduler while the TRB was already full - * with previous command - * uCode/driver must write "1" in order to clear this flag - * 7-0: Each status bit indicates a channel's TxCredit error. When an error - * bit is set, it indicates that the FH has received a full indication - * from the RTC TxFIFO and the current value of the TxCredit counter was - * not equal to zero. This mean that the credit mechanism was not - * synchronized to the TxFIFO status - * uCode/driver must write "1" in order to clear this flag - */ -#define FH49_TSSR_TX_ERROR_REG (FH49_TSSR_LOWER_BOUND + 0x018) - -#define FH49_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16) - -/* Tx service channels */ -#define FH49_SRVC_CHNL (9) -#define FH49_SRVC_LOWER_BOUND (FH49_MEM_LOWER_BOUND + 0x9C8) -#define FH49_SRVC_UPPER_BOUND (FH49_MEM_LOWER_BOUND + 0x9D0) -#define FH49_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \ - (FH49_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4) - -#define FH49_TX_CHICKEN_BITS_REG (FH49_MEM_LOWER_BOUND + 0xE98) -/* Instruct FH to increment the retry count of a packet when - * it is brought from the memory to TX-FIFO - */ -#define FH49_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002) - -/* Keep Warm Size */ -#define IL_KW_SIZE 0x1000 /* 4k */ - -#endif /* __il_4965_h__ */ diff --git a/drivers/net/wireless/iwlegacy/Kconfig b/drivers/net/wireless/iwlegacy/Kconfig deleted file mode 100644 index fb919727b8bb..000000000000 --- a/drivers/net/wireless/iwlegacy/Kconfig +++ /dev/null @@ -1,100 +0,0 @@ -config IWLEGACY - tristate - select FW_LOADER - select NEW_LEDS - select LEDS_CLASS - select LEDS_TRIGGERS - select MAC80211_LEDS - -config IWL4965 - tristate "Intel Wireless WiFi 4965AGN (iwl4965)" - depends on PCI && MAC80211 - select IWLEGACY - ---help--- - This option enables support for - - Select to build the driver supporting the: - - Intel Wireless WiFi Link 4965AGN - - This driver uses the kernel's mac80211 subsystem. - - In order to use this driver, you will need a microcode (uCode) - image for it. You can obtain the microcode from: - - . - - The microcode is typically installed in /lib/firmware. You can - look in the hotplug script /etc/hotplug/firmware.agent to - determine which directory FIRMWARE_DIR is set to when the script - runs. - - If you want to compile the driver as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read . The - module will be called iwl4965. - -config IWL3945 - tristate "Intel PRO/Wireless 3945ABG/BG Network Connection (iwl3945)" - depends on PCI && MAC80211 - select IWLEGACY - ---help--- - Select to build the driver supporting the: - - Intel PRO/Wireless 3945ABG/BG Network Connection - - This driver uses the kernel's mac80211 subsystem. - - In order to use this driver, you will need a microcode (uCode) - image for it. You can obtain the microcode from: - - . - - The microcode is typically installed in /lib/firmware. You can - look in the hotplug script /etc/hotplug/firmware.agent to - determine which directory FIRMWARE_DIR is set to when the script - runs. - - If you want to compile the driver as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read . The - module will be called iwl3945. - -menu "iwl3945 / iwl4965 Debugging Options" - depends on IWLEGACY - -config IWLEGACY_DEBUG - bool "Enable full debugging output in iwlegacy (iwl 3945/4965) drivers" - depends on IWLEGACY - ---help--- - This option will enable debug tracing output for the iwlegacy - drivers. - - This will result in the kernel module being ~100k larger. You can - control which debug output is sent to the kernel log by setting the - value in - - /sys/class/net/wlan0/device/debug_level - - This entry will only exist if this option is enabled. - - To set a value, simply echo an 8-byte hex value to the same file: - - % echo 0x43fff > /sys/class/net/wlan0/device/debug_level - - You can find the list of debug mask values in: - drivers/net/wireless/iwlegacy/common.h - - If this is your first time using this driver, you should say Y here - as the debug information can assist others in helping you resolve - any problems you may encounter. - -config IWLEGACY_DEBUGFS - bool "iwlegacy (iwl 3945/4965) debugfs support" - depends on IWLEGACY && MAC80211_DEBUGFS - ---help--- - Enable creation of debugfs files for the iwlegacy drivers. This - is a low-impact option that allows getting insight into the - driver's state at runtime. - -endmenu diff --git a/drivers/net/wireless/iwlegacy/Makefile b/drivers/net/wireless/iwlegacy/Makefile deleted file mode 100644 index c985a01a0731..000000000000 --- a/drivers/net/wireless/iwlegacy/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -obj-$(CONFIG_IWLEGACY) += iwlegacy.o -iwlegacy-objs := common.o -iwlegacy-$(CONFIG_IWLEGACY_DEBUGFS) += debug.o - -iwlegacy-objs += $(iwlegacy-m) - -# 4965 -obj-$(CONFIG_IWL4965) += iwl4965.o -iwl4965-objs := 4965.o 4965-mac.o 4965-rs.o 4965-calib.o -iwl4965-$(CONFIG_IWLEGACY_DEBUGFS) += 4965-debug.o - -# 3945 -obj-$(CONFIG_IWL3945) += iwl3945.o -iwl3945-objs := 3945-mac.o 3945.o 3945-rs.o -iwl3945-$(CONFIG_IWLEGACY_DEBUGFS) += 3945-debug.o - -ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/iwlegacy/commands.h b/drivers/net/wireless/iwlegacy/commands.h deleted file mode 100644 index dd744135c956..000000000000 --- a/drivers/net/wireless/iwlegacy/commands.h +++ /dev/null @@ -1,3370 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __il_commands_h__ -#define __il_commands_h__ - -#include - -struct il_priv; - -/* uCode version contains 4 values: Major/Minor/API/Serial */ -#define IL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24) -#define IL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16) -#define IL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8) -#define IL_UCODE_SERIAL(ver) ((ver) & 0x000000FF) - -/* Tx rates */ -#define IL_CCK_RATES 4 -#define IL_OFDM_RATES 8 -#define IL_MAX_RATES (IL_CCK_RATES + IL_OFDM_RATES) - -enum { - N_ALIVE = 0x1, - N_ERROR = 0x2, - - /* RXON and QOS commands */ - C_RXON = 0x10, - C_RXON_ASSOC = 0x11, - C_QOS_PARAM = 0x13, - C_RXON_TIMING = 0x14, - - /* Multi-Station support */ - C_ADD_STA = 0x18, - C_REM_STA = 0x19, - - /* Security */ - C_WEPKEY = 0x20, - - /* RX, TX, LEDs */ - N_3945_RX = 0x1b, /* 3945 only */ - C_TX = 0x1c, - C_RATE_SCALE = 0x47, /* 3945 only */ - C_LEDS = 0x48, - C_TX_LINK_QUALITY_CMD = 0x4e, /* for 4965 */ - - /* 802.11h related */ - C_CHANNEL_SWITCH = 0x72, - N_CHANNEL_SWITCH = 0x73, - C_SPECTRUM_MEASUREMENT = 0x74, - N_SPECTRUM_MEASUREMENT = 0x75, - - /* Power Management */ - C_POWER_TBL = 0x77, - N_PM_SLEEP = 0x7A, - N_PM_DEBUG_STATS = 0x7B, - - /* Scan commands and notifications */ - C_SCAN = 0x80, - C_SCAN_ABORT = 0x81, - N_SCAN_START = 0x82, - N_SCAN_RESULTS = 0x83, - N_SCAN_COMPLETE = 0x84, - - /* IBSS/AP commands */ - N_BEACON = 0x90, - C_TX_BEACON = 0x91, - - /* Miscellaneous commands */ - C_TX_PWR_TBL = 0x97, - - /* Bluetooth device coexistence config command */ - C_BT_CONFIG = 0x9b, - - /* Statistics */ - C_STATS = 0x9c, - N_STATS = 0x9d, - - /* RF-KILL commands and notifications */ - N_CARD_STATE = 0xa1, - - /* Missed beacons notification */ - N_MISSED_BEACONS = 0xa2, - - C_CT_KILL_CONFIG = 0xa4, - C_SENSITIVITY = 0xa8, - C_PHY_CALIBRATION = 0xb0, - N_RX_PHY = 0xc0, - N_RX_MPDU = 0xc1, - N_RX = 0xc3, - N_COMPRESSED_BA = 0xc5, - - IL_CN_MAX = 0xff -}; - -/****************************************************************************** - * (0) - * Commonly used structures and definitions: - * Command header, rate_n_flags, txpower - * - *****************************************************************************/ - -/* il_cmd_header flags value */ -#define IL_CMD_FAILED_MSK 0x40 - -#define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f) -#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8) -#define SEQ_TO_IDX(s) ((s) & 0xff) -#define IDX_TO_SEQ(i) ((i) & 0xff) -#define SEQ_HUGE_FRAME cpu_to_le16(0x4000) -#define SEQ_RX_FRAME cpu_to_le16(0x8000) - -/** - * struct il_cmd_header - * - * This header format appears in the beginning of each command sent from the - * driver, and each response/notification received from uCode. - */ -struct il_cmd_header { - u8 cmd; /* Command ID: C_RXON, etc. */ - u8 flags; /* 0:5 reserved, 6 abort, 7 internal */ - /* - * The driver sets up the sequence number to values of its choosing. - * uCode does not use this value, but passes it back to the driver - * when sending the response to each driver-originated command, so - * the driver can match the response to the command. Since the values - * don't get used by uCode, the driver may set up an arbitrary format. - * - * There is one exception: uCode sets bit 15 when it originates - * the response/notification, i.e. when the response/notification - * is not a direct response to a command sent by the driver. For - * example, uCode issues N_3945_RX when it sends a received frame - * to the driver; it is not a direct response to any driver command. - * - * The Linux driver uses the following format: - * - * 0:7 tfd idx - position within TX queue - * 8:12 TX queue id - * 13 reserved - * 14 huge - driver sets this to indicate command is in the - * 'huge' storage at the end of the command buffers - * 15 unsolicited RX or uCode-originated notification - */ - __le16 sequence; - - /* command or response/notification data follows immediately */ - u8 data[0]; -} __packed; - -/** - * struct il3945_tx_power - * - * Used in C_TX_PWR_TBL, C_SCAN, C_CHANNEL_SWITCH - * - * Each entry contains two values: - * 1) DSP gain (or sometimes called DSP attenuation). This is a fine-grained - * linear value that multiplies the output of the digital signal processor, - * before being sent to the analog radio. - * 2) Radio gain. This sets the analog gain of the radio Tx path. - * It is a coarser setting, and behaves in a logarithmic (dB) fashion. - * - * Driver obtains values from struct il3945_tx_power power_gain_table[][]. - */ -struct il3945_tx_power { - u8 tx_gain; /* gain for analog radio */ - u8 dsp_atten; /* gain for DSP */ -} __packed; - -/** - * struct il3945_power_per_rate - * - * Used in C_TX_PWR_TBL, C_CHANNEL_SWITCH - */ -struct il3945_power_per_rate { - u8 rate; /* plcp */ - struct il3945_tx_power tpc; - u8 reserved; -} __packed; - -/** - * iwl4965 rate_n_flags bit fields - * - * rate_n_flags format is used in following iwl4965 commands: - * N_RX (response only) - * N_RX_MPDU (response only) - * C_TX (both command and response) - * C_TX_LINK_QUALITY_CMD - * - * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"): - * 2-0: 0) 6 Mbps - * 1) 12 Mbps - * 2) 18 Mbps - * 3) 24 Mbps - * 4) 36 Mbps - * 5) 48 Mbps - * 6) 54 Mbps - * 7) 60 Mbps - * - * 4-3: 0) Single stream (SISO) - * 1) Dual stream (MIMO) - * 2) Triple stream (MIMO) - * - * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data - * - * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): - * 3-0: 0xD) 6 Mbps - * 0xF) 9 Mbps - * 0x5) 12 Mbps - * 0x7) 18 Mbps - * 0x9) 24 Mbps - * 0xB) 36 Mbps - * 0x1) 48 Mbps - * 0x3) 54 Mbps - * - * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"): - * 6-0: 10) 1 Mbps - * 20) 2 Mbps - * 55) 5.5 Mbps - * 110) 11 Mbps - */ -#define RATE_MCS_CODE_MSK 0x7 -#define RATE_MCS_SPATIAL_POS 3 -#define RATE_MCS_SPATIAL_MSK 0x18 -#define RATE_MCS_HT_DUP_POS 5 -#define RATE_MCS_HT_DUP_MSK 0x20 - -/* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */ -#define RATE_MCS_FLAGS_POS 8 -#define RATE_MCS_HT_POS 8 -#define RATE_MCS_HT_MSK 0x100 - -/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ -#define RATE_MCS_CCK_POS 9 -#define RATE_MCS_CCK_MSK 0x200 - -/* Bit 10: (1) Use Green Field preamble */ -#define RATE_MCS_GF_POS 10 -#define RATE_MCS_GF_MSK 0x400 - -/* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */ -#define RATE_MCS_HT40_POS 11 -#define RATE_MCS_HT40_MSK 0x800 - -/* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */ -#define RATE_MCS_DUP_POS 12 -#define RATE_MCS_DUP_MSK 0x1000 - -/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ -#define RATE_MCS_SGI_POS 13 -#define RATE_MCS_SGI_MSK 0x2000 - -/** - * rate_n_flags Tx antenna masks - * 4965 has 2 transmitters - * bit14:16 - */ -#define RATE_MCS_ANT_POS 14 -#define RATE_MCS_ANT_A_MSK 0x04000 -#define RATE_MCS_ANT_B_MSK 0x08000 -#define RATE_MCS_ANT_C_MSK 0x10000 -#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | RATE_MCS_ANT_B_MSK) -#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | RATE_MCS_ANT_C_MSK) -#define RATE_ANT_NUM 3 - -#define POWER_TBL_NUM_ENTRIES 33 -#define POWER_TBL_NUM_HT_OFDM_ENTRIES 32 -#define POWER_TBL_CCK_ENTRY 32 - -#define IL_PWR_NUM_HT_OFDM_ENTRIES 24 -#define IL_PWR_CCK_ENTRIES 2 - -/** - * union il4965_tx_power_dual_stream - * - * Host format used for C_TX_PWR_TBL, C_CHANNEL_SWITCH - * Use __le32 version (struct tx_power_dual_stream) when building command. - * - * Driver provides radio gain and DSP attenuation settings to device in pairs, - * one value for each transmitter chain. The first value is for transmitter A, - * second for transmitter B. - * - * For SISO bit rates, both values in a pair should be identical. - * For MIMO rates, one value may be different from the other, - * in order to balance the Tx output between the two transmitters. - * - * See more details in doc for TXPOWER in 4965.h. - */ -union il4965_tx_power_dual_stream { - struct { - u8 radio_tx_gain[2]; - u8 dsp_predis_atten[2]; - } s; - u32 dw; -}; - -/** - * struct tx_power_dual_stream - * - * Table entries in C_TX_PWR_TBL, C_CHANNEL_SWITCH - * - * Same format as il_tx_power_dual_stream, but __le32 - */ -struct tx_power_dual_stream { - __le32 dw; -} __packed; - -/** - * struct il4965_tx_power_db - * - * Entire table within C_TX_PWR_TBL, C_CHANNEL_SWITCH - */ -struct il4965_tx_power_db { - struct tx_power_dual_stream power_tbl[POWER_TBL_NUM_ENTRIES]; -} __packed; - -/****************************************************************************** - * (0a) - * Alive and Error Commands & Responses: - * - *****************************************************************************/ - -#define UCODE_VALID_OK cpu_to_le32(0x1) -#define INITIALIZE_SUBTYPE (9) - -/* - * ("Initialize") N_ALIVE = 0x1 (response only, not a command) - * - * uCode issues this "initialize alive" notification once the initialization - * uCode image has completed its work, and is ready to load the runtime image. - * This is the *first* "alive" notification that the driver will receive after - * rebooting uCode; the "initialize" alive is indicated by subtype field == 9. - * - * See comments documenting "BSM" (bootstrap state machine). - * - * For 4965, this notification contains important calibration data for - * calculating txpower settings: - * - * 1) Power supply voltage indication. The voltage sensor outputs higher - * values for lower voltage, and vice verse. - * - * 2) Temperature measurement parameters, for each of two channel widths - * (20 MHz and 40 MHz) supported by the radios. Temperature sensing - * is done via one of the receiver chains, and channel width influences - * the results. - * - * 3) Tx gain compensation to balance 4965's 2 Tx chains for MIMO operation, - * for each of 5 frequency ranges. - */ -struct il_init_alive_resp { - u8 ucode_minor; - u8 ucode_major; - __le16 reserved1; - u8 sw_rev[8]; - u8 ver_type; - u8 ver_subtype; /* "9" for initialize alive */ - __le16 reserved2; - __le32 log_event_table_ptr; - __le32 error_event_table_ptr; - __le32 timestamp; - __le32 is_valid; - - /* calibration values from "initialize" uCode */ - __le32 voltage; /* signed, higher value is lower voltage */ - __le32 therm_r1[2]; /* signed, 1st for normal, 2nd for HT40 */ - __le32 therm_r2[2]; /* signed */ - __le32 therm_r3[2]; /* signed */ - __le32 therm_r4[2]; /* signed */ - __le32 tx_atten[5][2]; /* signed MIMO gain comp, 5 freq groups, - * 2 Tx chains */ -} __packed; - -/** - * N_ALIVE = 0x1 (response only, not a command) - * - * uCode issues this "alive" notification once the runtime image is ready - * to receive commands from the driver. This is the *second* "alive" - * notification that the driver will receive after rebooting uCode; - * this "alive" is indicated by subtype field != 9. - * - * See comments documenting "BSM" (bootstrap state machine). - * - * This response includes two pointers to structures within the device's - * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging: - * - * 1) log_event_table_ptr indicates base of the event log. This traces - * a 256-entry history of uCode execution within a circular buffer. - * Its header format is: - * - * __le32 log_size; log capacity (in number of entries) - * __le32 type; (1) timestamp with each entry, (0) no timestamp - * __le32 wraps; # times uCode has wrapped to top of circular buffer - * __le32 write_idx; next circular buffer entry that uCode would fill - * - * The header is followed by the circular buffer of log entries. Entries - * with timestamps have the following format: - * - * __le32 event_id; range 0 - 1500 - * __le32 timestamp; low 32 bits of TSF (of network, if associated) - * __le32 data; event_id-specific data value - * - * Entries without timestamps contain only event_id and data. - * - * - * 2) error_event_table_ptr indicates base of the error log. This contains - * information about any uCode error that occurs. For 4965, the format - * of the error log is: - * - * __le32 valid; (nonzero) valid, (0) log is empty - * __le32 error_id; type of error - * __le32 pc; program counter - * __le32 blink1; branch link - * __le32 blink2; branch link - * __le32 ilink1; interrupt link - * __le32 ilink2; interrupt link - * __le32 data1; error-specific data - * __le32 data2; error-specific data - * __le32 line; source code line of error - * __le32 bcon_time; beacon timer - * __le32 tsf_low; network timestamp function timer - * __le32 tsf_hi; network timestamp function timer - * __le32 gp1; GP1 timer register - * __le32 gp2; GP2 timer register - * __le32 gp3; GP3 timer register - * __le32 ucode_ver; uCode version - * __le32 hw_ver; HW Silicon version - * __le32 brd_ver; HW board version - * __le32 log_pc; log program counter - * __le32 frame_ptr; frame pointer - * __le32 stack_ptr; stack pointer - * __le32 hcmd; last host command - * __le32 isr0; isr status register LMPM_NIC_ISR0: rxtx_flag - * __le32 isr1; isr status register LMPM_NIC_ISR1: host_flag - * __le32 isr2; isr status register LMPM_NIC_ISR2: enc_flag - * __le32 isr3; isr status register LMPM_NIC_ISR3: time_flag - * __le32 isr4; isr status register LMPM_NIC_ISR4: wico interrupt - * __le32 isr_pref; isr status register LMPM_NIC_PREF_STAT - * __le32 wait_event; wait event() caller address - * __le32 l2p_control; L2pControlField - * __le32 l2p_duration; L2pDurationField - * __le32 l2p_mhvalid; L2pMhValidBits - * __le32 l2p_addr_match; L2pAddrMatchStat - * __le32 lmpm_pmg_sel; indicate which clocks are turned on (LMPM_PMG_SEL) - * __le32 u_timestamp; indicate when the date and time of the compilation - * __le32 reserved; - * - * The Linux driver can print both logs to the system log when a uCode error - * occurs. - */ -struct il_alive_resp { - u8 ucode_minor; - u8 ucode_major; - __le16 reserved1; - u8 sw_rev[8]; - u8 ver_type; - u8 ver_subtype; /* not "9" for runtime alive */ - __le16 reserved2; - __le32 log_event_table_ptr; /* SRAM address for event log */ - __le32 error_event_table_ptr; /* SRAM address for error log */ - __le32 timestamp; - __le32 is_valid; -} __packed; - -/* - * N_ERROR = 0x2 (response only, not a command) - */ -struct il_error_resp { - __le32 error_type; - u8 cmd_id; - u8 reserved1; - __le16 bad_cmd_seq_num; - __le32 error_info; - __le64 timestamp; -} __packed; - -/****************************************************************************** - * (1) - * RXON Commands & Responses: - * - *****************************************************************************/ - -/* - * Rx config defines & structure - */ -/* rx_config device types */ -enum { - RXON_DEV_TYPE_AP = 1, - RXON_DEV_TYPE_ESS = 3, - RXON_DEV_TYPE_IBSS = 4, - RXON_DEV_TYPE_SNIFFER = 6, -}; - -#define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0) -#define RXON_RX_CHAIN_DRIVER_FORCE_POS (0) -#define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1) -#define RXON_RX_CHAIN_VALID_POS (1) -#define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4) -#define RXON_RX_CHAIN_FORCE_SEL_POS (4) -#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7) -#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7) -#define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10) -#define RXON_RX_CHAIN_CNT_POS (10) -#define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12) -#define RXON_RX_CHAIN_MIMO_CNT_POS (12) -#define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14) -#define RXON_RX_CHAIN_MIMO_FORCE_POS (14) - -/* rx_config flags */ -/* band & modulation selection */ -#define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0) -#define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1) -/* auto detection enable */ -#define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2) -/* TGg protection when tx */ -#define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3) -/* cck short slot & preamble */ -#define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4) -#define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5) -/* antenna selection */ -#define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7) -#define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00) -#define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8) -#define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9) -/* radar detection enable */ -#define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12) -#define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13) -/* rx response to host with 8-byte TSF -* (according to ON_AIR deassertion) */ -#define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15) - -/* HT flags */ -#define RXON_FLG_CTRL_CHANNEL_LOC_POS (22) -#define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22) - -#define RXON_FLG_HT_OPERATING_MODE_POS (23) - -#define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23) -#define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23) - -#define RXON_FLG_CHANNEL_MODE_POS (25) -#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) - -/* channel mode */ -enum { - CHANNEL_MODE_LEGACY = 0, - CHANNEL_MODE_PURE_40 = 1, - CHANNEL_MODE_MIXED = 2, - CHANNEL_MODE_RESERVED = 3, -}; -#define RXON_FLG_CHANNEL_MODE_LEGACY \ - cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS) -#define RXON_FLG_CHANNEL_MODE_PURE_40 \ - cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS) -#define RXON_FLG_CHANNEL_MODE_MIXED \ - cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS) - -/* CTS to self (if spec allows) flag */ -#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30) - -/* rx_config filter flags */ -/* accept all data frames */ -#define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0) -/* pass control & management to host */ -#define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1) -/* accept multi-cast */ -#define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2) -/* don't decrypt uni-cast frames */ -#define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3) -/* don't decrypt multi-cast frames */ -#define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4) -/* STA is associated */ -#define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5) -/* transfer to host non bssid beacons in associated state */ -#define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6) - -/** - * C_RXON = 0x10 (command, has simple generic response) - * - * RXON tunes the radio tuner to a service channel, and sets up a number - * of parameters that are used primarily for Rx, but also for Tx operations. - * - * NOTE: When tuning to a new channel, driver must set the - * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent - * info within the device, including the station tables, tx retry - * rate tables, and txpower tables. Driver must build a new station - * table and txpower table before transmitting anything on the RXON - * channel. - * - * NOTE: All RXONs wipe clean the internal txpower table. Driver must - * issue a new C_TX_PWR_TBL after each C_RXON (0x10), - * regardless of whether RXON_FILTER_ASSOC_MSK is set. - */ - -struct il3945_rxon_cmd { - u8 node_addr[6]; - __le16 reserved1; - u8 bssid_addr[6]; - __le16 reserved2; - u8 wlap_bssid_addr[6]; - __le16 reserved3; - u8 dev_type; - u8 air_propagation; - __le16 reserved4; - u8 ofdm_basic_rates; - u8 cck_basic_rates; - __le16 assoc_id; - __le32 flags; - __le32 filter_flags; - __le16 channel; - __le16 reserved5; -} __packed; - -struct il4965_rxon_cmd { - u8 node_addr[6]; - __le16 reserved1; - u8 bssid_addr[6]; - __le16 reserved2; - u8 wlap_bssid_addr[6]; - __le16 reserved3; - u8 dev_type; - u8 air_propagation; - __le16 rx_chain; - u8 ofdm_basic_rates; - u8 cck_basic_rates; - __le16 assoc_id; - __le32 flags; - __le32 filter_flags; - __le16 channel; - u8 ofdm_ht_single_stream_basic_rates; - u8 ofdm_ht_dual_stream_basic_rates; -} __packed; - -/* Create a common rxon cmd which will be typecast into the 3945 or 4965 - * specific rxon cmd, depending on where it is called from. - */ -struct il_rxon_cmd { - u8 node_addr[6]; - __le16 reserved1; - u8 bssid_addr[6]; - __le16 reserved2; - u8 wlap_bssid_addr[6]; - __le16 reserved3; - u8 dev_type; - u8 air_propagation; - __le16 rx_chain; - u8 ofdm_basic_rates; - u8 cck_basic_rates; - __le16 assoc_id; - __le32 flags; - __le32 filter_flags; - __le16 channel; - u8 ofdm_ht_single_stream_basic_rates; - u8 ofdm_ht_dual_stream_basic_rates; - u8 reserved4; - u8 reserved5; -} __packed; - -/* - * C_RXON_ASSOC = 0x11 (command, has simple generic response) - */ -struct il3945_rxon_assoc_cmd { - __le32 flags; - __le32 filter_flags; - u8 ofdm_basic_rates; - u8 cck_basic_rates; - __le16 reserved; -} __packed; - -struct il4965_rxon_assoc_cmd { - __le32 flags; - __le32 filter_flags; - u8 ofdm_basic_rates; - u8 cck_basic_rates; - u8 ofdm_ht_single_stream_basic_rates; - u8 ofdm_ht_dual_stream_basic_rates; - __le16 rx_chain_select_flags; - __le16 reserved; -} __packed; - -#define IL_CONN_MAX_LISTEN_INTERVAL 10 -#define IL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */ -#define IL39_MAX_UCODE_BEACON_INTERVAL 1 /* 1024 */ - -/* - * C_RXON_TIMING = 0x14 (command, has simple generic response) - */ -struct il_rxon_time_cmd { - __le64 timestamp; - __le16 beacon_interval; - __le16 atim_win; - __le32 beacon_init_val; - __le16 listen_interval; - u8 dtim_period; - u8 delta_cp_bss_tbtts; -} __packed; - -/* - * C_CHANNEL_SWITCH = 0x72 (command, has simple generic response) - */ -struct il3945_channel_switch_cmd { - u8 band; - u8 expect_beacon; - __le16 channel; - __le32 rxon_flags; - __le32 rxon_filter_flags; - __le32 switch_time; - struct il3945_power_per_rate power[IL_MAX_RATES]; -} __packed; - -struct il4965_channel_switch_cmd { - u8 band; - u8 expect_beacon; - __le16 channel; - __le32 rxon_flags; - __le32 rxon_filter_flags; - __le32 switch_time; - struct il4965_tx_power_db tx_power; -} __packed; - -/* - * N_CHANNEL_SWITCH = 0x73 (notification only, not a command) - */ -struct il_csa_notification { - __le16 band; - __le16 channel; - __le32 status; /* 0 - OK, 1 - fail */ -} __packed; - -/****************************************************************************** - * (2) - * Quality-of-Service (QOS) Commands & Responses: - * - *****************************************************************************/ - -/** - * struct il_ac_qos -- QOS timing params for C_QOS_PARAM - * One for each of 4 EDCA access categories in struct il_qosparam_cmd - * - * @cw_min: Contention win, start value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x0f. - * @cw_max: Contention win, max value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x3f. - * @aifsn: Number of slots in Arbitration Interframe Space (before - * performing random backoff timing prior to Tx). Device default 1. - * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. - * - * Device will automatically increase contention win by (2*CW) + 1 for each - * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW - * value, to cap the CW value. - */ -struct il_ac_qos { - __le16 cw_min; - __le16 cw_max; - u8 aifsn; - u8 reserved1; - __le16 edca_txop; -} __packed; - -/* QoS flags defines */ -#define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01) -#define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02) -#define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10) - -/* Number of Access Categories (AC) (EDCA), queues 0..3 */ -#define AC_NUM 4 - -/* - * C_QOS_PARAM = 0x13 (command, has simple generic response) - * - * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs - * 0: Background, 1: Best Effort, 2: Video, 3: Voice. - */ -struct il_qosparam_cmd { - __le32 qos_flags; - struct il_ac_qos ac[AC_NUM]; -} __packed; - -/****************************************************************************** - * (3) - * Add/Modify Stations Commands & Responses: - * - *****************************************************************************/ -/* - * Multi station support - */ - -/* Special, dedicated locations within device's station table */ -#define IL_AP_ID 0 -#define IL_STA_ID 2 -#define IL3945_BROADCAST_ID 24 -#define IL3945_STATION_COUNT 25 -#define IL4965_BROADCAST_ID 31 -#define IL4965_STATION_COUNT 32 - -#define IL_STATION_COUNT 32 /* MAX(3945,4965) */ -#define IL_INVALID_STATION 255 - -#define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) -#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) -#define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17) -#define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18) -#define STA_FLG_MAX_AGG_SIZE_POS (19) -#define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19) -#define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21) -#define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22) -#define STA_FLG_AGG_MPDU_DENSITY_POS (23) -#define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23) - -/* Use in mode field. 1: modify existing entry, 0: add new station entry */ -#define STA_CONTROL_MODIFY_MSK 0x01 - -/* key flags __le16*/ -#define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007) -#define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000) -#define STA_KEY_FLG_WEP cpu_to_le16(0x0001) -#define STA_KEY_FLG_CCMP cpu_to_le16(0x0002) -#define STA_KEY_FLG_TKIP cpu_to_le16(0x0003) - -#define STA_KEY_FLG_KEYID_POS 8 -#define STA_KEY_FLG_INVALID cpu_to_le16(0x0800) -/* wep key is either from global key (0) or from station info array (1) */ -#define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008) - -/* wep key in STA: 5-bytes (0) or 13-bytes (1) */ -#define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000) -#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000) -#define STA_KEY_MAX_NUM 8 - -/* Flags indicate whether to modify vs. don't change various station params */ -#define STA_MODIFY_KEY_MASK 0x01 -#define STA_MODIFY_TID_DISABLE_TX 0x02 -#define STA_MODIFY_TX_RATE_MSK 0x04 -#define STA_MODIFY_ADDBA_TID_MSK 0x08 -#define STA_MODIFY_DELBA_TID_MSK 0x10 -#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 - -/* Receiver address (actually, Rx station's idx into station table), - * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ -#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) - -struct il4965_keyinfo { - __le16 key_flags; - u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */ - u8 reserved1; - __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */ - u8 key_offset; - u8 reserved2; - u8 key[16]; /* 16-byte unicast decryption key */ -} __packed; - -/** - * struct sta_id_modify - * @addr[ETH_ALEN]: station's MAC address - * @sta_id: idx of station in uCode's station table - * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change - * - * Driver selects unused table idx when adding new station, - * or the idx to a pre-existing station entry when modifying that station. - * Some idxes have special purposes (IL_AP_ID, idx 0, is for AP). - * - * modify_mask flags select which parameters to modify vs. leave alone. - */ -struct sta_id_modify { - u8 addr[ETH_ALEN]; - __le16 reserved1; - u8 sta_id; - u8 modify_mask; - __le16 reserved2; -} __packed; - -/* - * C_ADD_STA = 0x18 (command) - * - * The device contains an internal table of per-station information, - * with info on security keys, aggregation parameters, and Tx rates for - * initial Tx attempt and any retries (4965 devices uses - * C_TX_LINK_QUALITY_CMD, - * 3945 uses C_RATE_SCALE to set up rate tables). - * - * C_ADD_STA sets up the table entry for one station, either creating - * a new entry, or modifying a pre-existing one. - * - * NOTE: RXON command (without "associated" bit set) wipes the station table - * clean. Moving into RF_KILL state does this also. Driver must set up - * new station table before transmitting anything on the RXON channel - * (except active scans or active measurements; those commands carry - * their own txpower/rate setup data). - * - * When getting started on a new channel, driver must set up the - * IL_BROADCAST_ID entry (last entry in the table). For a client - * station in a BSS, once an AP is selected, driver sets up the AP STA - * in the IL_AP_ID entry (1st entry in the table). BROADCAST and AP - * are all that are needed for a BSS client station. If the device is - * used as AP, or in an IBSS network, driver must set up station table - * entries for all STAs in network, starting with idx IL_STA_ID. - */ - -struct il3945_addsta_cmd { - u8 mode; /* 1: modify existing, 0: add new station */ - u8 reserved[3]; - struct sta_id_modify sta; - struct il4965_keyinfo key; - __le32 station_flags; /* STA_FLG_* */ - __le32 station_flags_msk; /* STA_FLG_* */ - - /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) - * corresponding to bit (e.g. bit 5 controls TID 5). - * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ - __le16 tid_disable_tx; - - __le16 rate_n_flags; - - /* TID for which to add block-ack support. - * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ - u8 add_immediate_ba_tid; - - /* TID for which to remove block-ack support. - * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ - u8 remove_immediate_ba_tid; - - /* Starting Sequence Number for added block-ack support. - * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ - __le16 add_immediate_ba_ssn; -} __packed; - -struct il4965_addsta_cmd { - u8 mode; /* 1: modify existing, 0: add new station */ - u8 reserved[3]; - struct sta_id_modify sta; - struct il4965_keyinfo key; - __le32 station_flags; /* STA_FLG_* */ - __le32 station_flags_msk; /* STA_FLG_* */ - - /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) - * corresponding to bit (e.g. bit 5 controls TID 5). - * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ - __le16 tid_disable_tx; - - __le16 reserved1; - - /* TID for which to add block-ack support. - * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ - u8 add_immediate_ba_tid; - - /* TID for which to remove block-ack support. - * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ - u8 remove_immediate_ba_tid; - - /* Starting Sequence Number for added block-ack support. - * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ - __le16 add_immediate_ba_ssn; - - /* - * Number of packets OK to transmit to station even though - * it is asleep -- used to synchronise PS-poll and u-APSD - * responses while ucode keeps track of STA sleep state. - */ - __le16 sleep_tx_count; - - __le16 reserved2; -} __packed; - -/* Wrapper struct for 3945 and 4965 addsta_cmd structures */ -struct il_addsta_cmd { - u8 mode; /* 1: modify existing, 0: add new station */ - u8 reserved[3]; - struct sta_id_modify sta; - struct il4965_keyinfo key; - __le32 station_flags; /* STA_FLG_* */ - __le32 station_flags_msk; /* STA_FLG_* */ - - /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) - * corresponding to bit (e.g. bit 5 controls TID 5). - * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ - __le16 tid_disable_tx; - - __le16 rate_n_flags; /* 3945 only */ - - /* TID for which to add block-ack support. - * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ - u8 add_immediate_ba_tid; - - /* TID for which to remove block-ack support. - * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ - u8 remove_immediate_ba_tid; - - /* Starting Sequence Number for added block-ack support. - * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ - __le16 add_immediate_ba_ssn; - - /* - * Number of packets OK to transmit to station even though - * it is asleep -- used to synchronise PS-poll and u-APSD - * responses while ucode keeps track of STA sleep state. - */ - __le16 sleep_tx_count; - - __le16 reserved2; -} __packed; - -#define ADD_STA_SUCCESS_MSK 0x1 -#define ADD_STA_NO_ROOM_IN_TBL 0x2 -#define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4 -#define ADD_STA_MODIFY_NON_EXIST_STA 0x8 -/* - * C_ADD_STA = 0x18 (response) - */ -struct il_add_sta_resp { - u8 status; /* ADD_STA_* */ -} __packed; - -#define REM_STA_SUCCESS_MSK 0x1 -/* - * C_REM_STA = 0x19 (response) - */ -struct il_rem_sta_resp { - u8 status; -} __packed; - -/* - * C_REM_STA = 0x19 (command) - */ -struct il_rem_sta_cmd { - u8 num_sta; /* number of removed stations */ - u8 reserved[3]; - u8 addr[ETH_ALEN]; /* MAC addr of the first station */ - u8 reserved2[2]; -} __packed; - -#define IL_TX_FIFO_BK_MSK cpu_to_le32(BIT(0)) -#define IL_TX_FIFO_BE_MSK cpu_to_le32(BIT(1)) -#define IL_TX_FIFO_VI_MSK cpu_to_le32(BIT(2)) -#define IL_TX_FIFO_VO_MSK cpu_to_le32(BIT(3)) -#define IL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00) - -#define IL_DROP_SINGLE 0 -#define IL_DROP_SELECTED 1 -#define IL_DROP_ALL 2 - -/* - * REPLY_WEP_KEY = 0x20 - */ -struct il_wep_key { - u8 key_idx; - u8 key_offset; - u8 reserved1[2]; - u8 key_size; - u8 reserved2[3]; - u8 key[16]; -} __packed; - -struct il_wep_cmd { - u8 num_keys; - u8 global_key_type; - u8 flags; - u8 reserved; - struct il_wep_key key[0]; -} __packed; - -#define WEP_KEY_WEP_TYPE 1 -#define WEP_KEYS_MAX 4 -#define WEP_INVALID_OFFSET 0xff -#define WEP_KEY_LEN_64 5 -#define WEP_KEY_LEN_128 13 - -/****************************************************************************** - * (4) - * Rx Responses: - * - *****************************************************************************/ - -#define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0) -#define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1) - -#define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0) -#define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) -#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) -#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) -#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0x70 -#define RX_RES_PHY_FLAGS_ANTENNA_POS 4 -#define RX_RES_PHY_FLAGS_AGG_MSK cpu_to_le16(1 << 7) - -#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) -#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) -#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8) -#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8) -#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8) -#define RX_RES_STATUS_SEC_TYPE_ERR (0x7 << 8) - -#define RX_RES_STATUS_STATION_FOUND (1<<6) -#define RX_RES_STATUS_NO_STATION_INFO_MISMATCH (1<<7) - -#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11) -#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11) -#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11) -#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11) -#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11) - -#define RX_MPDU_RES_STATUS_ICV_OK (0x20) -#define RX_MPDU_RES_STATUS_MIC_OK (0x40) -#define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7) -#define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800) - -struct il3945_rx_frame_stats { - u8 phy_count; - u8 id; - u8 rssi; - u8 agc; - __le16 sig_avg; - __le16 noise_diff; - u8 payload[0]; -} __packed; - -struct il3945_rx_frame_hdr { - __le16 channel; - __le16 phy_flags; - u8 reserved1; - u8 rate; - __le16 len; - u8 payload[0]; -} __packed; - -struct il3945_rx_frame_end { - __le32 status; - __le64 timestamp; - __le32 beacon_timestamp; -} __packed; - -/* - * N_3945_RX = 0x1b (response only, not a command) - * - * NOTE: DO NOT dereference from casts to this structure - * It is provided only for calculating minimum data set size. - * The actual offsets of the hdr and end are dynamic based on - * stats.phy_count - */ -struct il3945_rx_frame { - struct il3945_rx_frame_stats stats; - struct il3945_rx_frame_hdr hdr; - struct il3945_rx_frame_end end; -} __packed; - -#define IL39_RX_FRAME_SIZE (4 + sizeof(struct il3945_rx_frame)) - -/* Fixed (non-configurable) rx data from phy */ - -#define IL49_RX_RES_PHY_CNT 14 -#define IL49_RX_PHY_FLAGS_ANTENNAE_OFFSET (4) -#define IL49_RX_PHY_FLAGS_ANTENNAE_MASK (0x70) -#define IL49_AGC_DB_MASK (0x3f80) /* MASK(7,13) */ -#define IL49_AGC_DB_POS (7) -struct il4965_rx_non_cfg_phy { - __le16 ant_selection; /* ant A bit 4, ant B bit 5, ant C bit 6 */ - __le16 agc_info; /* agc code 0:6, agc dB 7:13, reserved 14:15 */ - u8 rssi_info[6]; /* we use even entries, 0/2/4 for A/B/C rssi */ - u8 pad[0]; -} __packed; - -/* - * N_RX = 0xc3 (response only, not a command) - * Used only for legacy (non 11n) frames. - */ -struct il_rx_phy_res { - u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */ - u8 cfg_phy_cnt; /* configurable DSP phy data byte count */ - u8 stat_id; /* configurable DSP phy data set ID */ - u8 reserved1; - __le64 timestamp; /* TSF at on air rise */ - __le32 beacon_time_stamp; /* beacon at on-air rise */ - __le16 phy_flags; /* general phy flags: band, modulation, ... */ - __le16 channel; /* channel number */ - u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */ - __le32 rate_n_flags; /* RATE_MCS_* */ - __le16 byte_count; /* frame's byte-count */ - __le16 frame_time; /* frame's time on the air */ -} __packed; - -struct il_rx_mpdu_res_start { - __le16 byte_count; - __le16 reserved; -} __packed; - -/****************************************************************************** - * (5) - * Tx Commands & Responses: - * - * Driver must place each C_TX command into one of the prioritized Tx - * queues in host DRAM, shared between driver and device (see comments for - * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode - * are preparing to transmit, the device pulls the Tx command over the PCI - * bus via one of the device's Tx DMA channels, to fill an internal FIFO - * from which data will be transmitted. - * - * uCode handles all timing and protocol related to control frames - * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler - * handle reception of block-acks; uCode updates the host driver via - * N_COMPRESSED_BA. - * - * uCode handles retrying Tx when an ACK is expected but not received. - * This includes trying lower data rates than the one requested in the Tx - * command, as set up by the C_RATE_SCALE (for 3945) or - * C_TX_LINK_QUALITY_CMD (4965). - * - * Driver sets up transmit power for various rates via C_TX_PWR_TBL. - * This command must be executed after every RXON command, before Tx can occur. - *****************************************************************************/ - -/* C_TX Tx flags field */ - -/* - * 1: Use Request-To-Send protocol before this frame. - * Mutually exclusive vs. TX_CMD_FLG_CTS_MSK. - */ -#define TX_CMD_FLG_RTS_MSK cpu_to_le32(1 << 1) - -/* - * 1: Transmit Clear-To-Send to self before this frame. - * Driver should set this for AUTH/DEAUTH/ASSOC-REQ/REASSOC mgmnt frames. - * Mutually exclusive vs. TX_CMD_FLG_RTS_MSK. - */ -#define TX_CMD_FLG_CTS_MSK cpu_to_le32(1 << 2) - -/* 1: Expect ACK from receiving station - * 0: Don't expect ACK (MAC header's duration field s/b 0) - * Set this for unicast frames, but not broadcast/multicast. */ -#define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3) - -/* For 4965 devices: - * 1: Use rate scale table (see C_TX_LINK_QUALITY_CMD). - * Tx command's initial_rate_idx indicates first rate to try; - * uCode walks through table for additional Tx attempts. - * 0: Use Tx rate/MCS from Tx command's rate_n_flags field. - * This rate will be used for all Tx attempts; it will not be scaled. */ -#define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4) - -/* 1: Expect immediate block-ack. - * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ -#define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6) - -/* - * 1: Frame requires full Tx-Op protection. - * Set this if either RTS or CTS Tx Flag gets set. - */ -#define TX_CMD_FLG_FULL_TXOP_PROT_MSK cpu_to_le32(1 << 7) - -/* Tx antenna selection field; used only for 3945, reserved (0) for 4965 devices. - * Set field to "0" to allow 3945 uCode to select antenna (normal usage). */ -#define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00) -#define TX_CMD_FLG_ANT_A_MSK cpu_to_le32(1 << 8) -#define TX_CMD_FLG_ANT_B_MSK cpu_to_le32(1 << 9) - -/* 1: uCode overrides sequence control field in MAC header. - * 0: Driver provides sequence control field in MAC header. - * Set this for management frames, non-QOS data frames, non-unicast frames, - * and also in Tx command embedded in C_SCAN for active scans. */ -#define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13) - -/* 1: This frame is non-last MPDU; more fragments are coming. - * 0: Last fragment, or not using fragmentation. */ -#define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14) - -/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame. - * 0: No TSF required in outgoing frame. - * Set this for transmitting beacons and probe responses. */ -#define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16) - -/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword - * alignment of frame's payload data field. - * 0: No pad - * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4 - * field (but not both). Driver must align frame data (i.e. data following - * MAC header) to DWORD boundary. */ -#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20) - -/* accelerate aggregation support - * 0 - no CCMP encryption; 1 - CCMP encryption */ -#define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22) - -/* HCCA-AP - disable duration overwriting. */ -#define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25) - -/* - * TX command security control - */ -#define TX_CMD_SEC_WEP 0x01 -#define TX_CMD_SEC_CCM 0x02 -#define TX_CMD_SEC_TKIP 0x03 -#define TX_CMD_SEC_MSK 0x03 -#define TX_CMD_SEC_SHIFT 6 -#define TX_CMD_SEC_KEY128 0x08 - -/* - * C_TX = 0x1c (command) - */ - -struct il3945_tx_cmd { - /* - * MPDU byte count: - * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, - * + 8 byte IV for CCM or TKIP (not used for WEP) - * + Data payload - * + 8-byte MIC (not used for CCM/WEP) - * NOTE: Does not include Tx command bytes, post-MAC pad bytes, - * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i - * Range: 14-2342 bytes. - */ - __le16 len; - - /* - * MPDU or MSDU byte count for next frame. - * Used for fragmentation and bursting, but not 11n aggregation. - * Same as "len", but for next frame. Set to 0 if not applicable. - */ - __le16 next_frame_len; - - __le32 tx_flags; /* TX_CMD_FLG_* */ - - u8 rate; - - /* Index of recipient station in uCode's station table */ - u8 sta_id; - u8 tid_tspec; - u8 sec_ctl; - u8 key[16]; - union { - u8 byte[8]; - __le16 word[4]; - __le32 dw[2]; - } tkip_mic; - __le32 next_frame_info; - union { - __le32 life_time; - __le32 attempt; - } stop_time; - u8 supp_rates[2]; - u8 rts_retry_limit; /*byte 50 */ - u8 data_retry_limit; /*byte 51 */ - union { - __le16 pm_frame_timeout; - __le16 attempt_duration; - } timeout; - - /* - * Duration of EDCA burst Tx Opportunity, in 32-usec units. - * Set this if txop time is not specified by HCCA protocol (e.g. by AP). - */ - __le16 driver_txop; - - /* - * MAC header goes here, followed by 2 bytes padding if MAC header - * length is 26 or 30 bytes, followed by payload data - */ - u8 payload[0]; - struct ieee80211_hdr hdr[0]; -} __packed; - -/* - * C_TX = 0x1c (response) - */ -struct il3945_tx_resp { - u8 failure_rts; - u8 failure_frame; - u8 bt_kill_count; - u8 rate; - __le32 wireless_media_time; - __le32 status; /* TX status */ -} __packed; - -/* - * 4965 uCode updates these Tx attempt count values in host DRAM. - * Used for managing Tx retries when expecting block-acks. - * Driver should set these fields to 0. - */ -struct il_dram_scratch { - u8 try_cnt; /* Tx attempts */ - u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */ - __le16 reserved; -} __packed; - -struct il_tx_cmd { - /* - * MPDU byte count: - * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, - * + 8 byte IV for CCM or TKIP (not used for WEP) - * + Data payload - * + 8-byte MIC (not used for CCM/WEP) - * NOTE: Does not include Tx command bytes, post-MAC pad bytes, - * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i - * Range: 14-2342 bytes. - */ - __le16 len; - - /* - * MPDU or MSDU byte count for next frame. - * Used for fragmentation and bursting, but not 11n aggregation. - * Same as "len", but for next frame. Set to 0 if not applicable. - */ - __le16 next_frame_len; - - __le32 tx_flags; /* TX_CMD_FLG_* */ - - /* uCode may modify this field of the Tx command (in host DRAM!). - * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ - struct il_dram_scratch scratch; - - /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ - __le32 rate_n_flags; /* RATE_MCS_* */ - - /* Index of destination station in uCode's station table */ - u8 sta_id; - - /* Type of security encryption: CCM or TKIP */ - u8 sec_ctl; /* TX_CMD_SEC_* */ - - /* - * Index into rate table (see C_TX_LINK_QUALITY_CMD) for initial - * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for - * data frames, this field may be used to selectively reduce initial - * rate (via non-0 value) for special frames (e.g. management), while - * still supporting rate scaling for all frames. - */ - u8 initial_rate_idx; - u8 reserved; - u8 key[16]; - __le16 next_frame_flags; - __le16 reserved2; - union { - __le32 life_time; - __le32 attempt; - } stop_time; - - /* Host DRAM physical address pointer to "scratch" in this command. - * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ - __le32 dram_lsb_ptr; - u8 dram_msb_ptr; - - u8 rts_retry_limit; /*byte 50 */ - u8 data_retry_limit; /*byte 51 */ - u8 tid_tspec; - union { - __le16 pm_frame_timeout; - __le16 attempt_duration; - } timeout; - - /* - * Duration of EDCA burst Tx Opportunity, in 32-usec units. - * Set this if txop time is not specified by HCCA protocol (e.g. by AP). - */ - __le16 driver_txop; - - /* - * MAC header goes here, followed by 2 bytes padding if MAC header - * length is 26 or 30 bytes, followed by payload data - */ - u8 payload[0]; - struct ieee80211_hdr hdr[0]; -} __packed; - -/* TX command response is sent after *3945* transmission attempts. - * - * NOTES: - * - * TX_STATUS_FAIL_NEXT_FRAG - * - * If the fragment flag in the MAC header for the frame being transmitted - * is set and there is insufficient time to transmit the next frame, the - * TX status will be returned with 'TX_STATUS_FAIL_NEXT_FRAG'. - * - * TX_STATUS_FIFO_UNDERRUN - * - * Indicates the host did not provide bytes to the FIFO fast enough while - * a TX was in progress. - * - * TX_STATUS_FAIL_MGMNT_ABORT - * - * This status is only possible if the ABORT ON MGMT RX parameter was - * set to true with the TX command. - * - * If the MSB of the status parameter is set then an abort sequence is - * required. This sequence consists of the host activating the TX Abort - * control line, and then waiting for the TX Abort command response. This - * indicates that a the device is no longer in a transmit state, and that the - * command FIFO has been cleared. The host must then deactivate the TX Abort - * control line. Receiving is still allowed in this case. - */ -enum { - TX_3945_STATUS_SUCCESS = 0x01, - TX_3945_STATUS_DIRECT_DONE = 0x02, - TX_3945_STATUS_FAIL_SHORT_LIMIT = 0x82, - TX_3945_STATUS_FAIL_LONG_LIMIT = 0x83, - TX_3945_STATUS_FAIL_FIFO_UNDERRUN = 0x84, - TX_3945_STATUS_FAIL_MGMNT_ABORT = 0x85, - TX_3945_STATUS_FAIL_NEXT_FRAG = 0x86, - TX_3945_STATUS_FAIL_LIFE_EXPIRE = 0x87, - TX_3945_STATUS_FAIL_DEST_PS = 0x88, - TX_3945_STATUS_FAIL_ABORTED = 0x89, - TX_3945_STATUS_FAIL_BT_RETRY = 0x8a, - TX_3945_STATUS_FAIL_STA_INVALID = 0x8b, - TX_3945_STATUS_FAIL_FRAG_DROPPED = 0x8c, - TX_3945_STATUS_FAIL_TID_DISABLE = 0x8d, - TX_3945_STATUS_FAIL_FRAME_FLUSHED = 0x8e, - TX_3945_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, - TX_3945_STATUS_FAIL_TX_LOCKED = 0x90, - TX_3945_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, -}; - -/* - * TX command response is sent after *4965* transmission attempts. - * - * both postpone and abort status are expected behavior from uCode. there is - * no special operation required from driver; except for RFKILL_FLUSH, - * which required tx flush host command to flush all the tx frames in queues - */ -enum { - TX_STATUS_SUCCESS = 0x01, - TX_STATUS_DIRECT_DONE = 0x02, - /* postpone TX */ - TX_STATUS_POSTPONE_DELAY = 0x40, - TX_STATUS_POSTPONE_FEW_BYTES = 0x41, - TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, - TX_STATUS_POSTPONE_CALC_TTAK = 0x44, - /* abort TX */ - TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, - TX_STATUS_FAIL_SHORT_LIMIT = 0x82, - TX_STATUS_FAIL_LONG_LIMIT = 0x83, - TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84, - TX_STATUS_FAIL_DRAIN_FLOW = 0x85, - TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, - TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, - TX_STATUS_FAIL_DEST_PS = 0x88, - TX_STATUS_FAIL_HOST_ABORTED = 0x89, - TX_STATUS_FAIL_BT_RETRY = 0x8a, - TX_STATUS_FAIL_STA_INVALID = 0x8b, - TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, - TX_STATUS_FAIL_TID_DISABLE = 0x8d, - TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, - TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, - TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90, - TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, -}; - -#define TX_PACKET_MODE_REGULAR 0x0000 -#define TX_PACKET_MODE_BURST_SEQ 0x0100 -#define TX_PACKET_MODE_BURST_FIRST 0x0200 - -enum { - TX_POWER_PA_NOT_ACTIVE = 0x0, -}; - -enum { - TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */ - TX_STATUS_DELAY_MSK = 0x00000040, - TX_STATUS_ABORT_MSK = 0x00000080, - TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */ - TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */ - TX_RESERVED = 0x00780000, /* bits 19:22 */ - TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */ - TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */ -}; - -/* ******************************* - * TX aggregation status - ******************************* */ - -enum { - AGG_TX_STATE_TRANSMITTED = 0x00, - AGG_TX_STATE_UNDERRUN_MSK = 0x01, - AGG_TX_STATE_FEW_BYTES_MSK = 0x04, - AGG_TX_STATE_ABORT_MSK = 0x08, - AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10, - AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20, - AGG_TX_STATE_SCD_QUERY_MSK = 0x80, - AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100, - AGG_TX_STATE_RESPONSE_MSK = 0x1ff, - AGG_TX_STATE_DUMP_TX_MSK = 0x200, - AGG_TX_STATE_DELAY_TX_MSK = 0x400 -}; - -#define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */ -#define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */ - -#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \ - AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK) - -/* # tx attempts for first frame in aggregation */ -#define AGG_TX_STATE_TRY_CNT_POS 12 -#define AGG_TX_STATE_TRY_CNT_MSK 0xf000 - -/* Command ID and sequence number of Tx command for this frame */ -#define AGG_TX_STATE_SEQ_NUM_POS 16 -#define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 - -/* - * C_TX = 0x1c (response) - * - * This response may be in one of two slightly different formats, indicated - * by the frame_count field: - * - * 1) No aggregation (frame_count == 1). This reports Tx results for - * a single frame. Multiple attempts, at various bit rates, may have - * been made for this frame. - * - * 2) Aggregation (frame_count > 1). This reports Tx results for - * 2 or more frames that used block-acknowledge. All frames were - * transmitted at same rate. Rate scaling may have been used if first - * frame in this new agg block failed in previous agg block(s). - * - * Note that, for aggregation, ACK (block-ack) status is not delivered here; - * block-ack has not been received by the time the 4965 device records - * this status. - * This status relates to reasons the tx might have been blocked or aborted - * within the sending station (this 4965 device), rather than whether it was - * received successfully by the destination station. - */ -struct agg_tx_status { - __le16 status; - __le16 sequence; -} __packed; - -struct il4965_tx_resp { - u8 frame_count; /* 1 no aggregation, >1 aggregation */ - u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */ - u8 failure_rts; /* # failures due to unsuccessful RTS */ - u8 failure_frame; /* # failures due to no ACK (unused for agg) */ - - /* For non-agg: Rate at which frame was successful. - * For agg: Rate at which all frames were transmitted. */ - __le32 rate_n_flags; /* RATE_MCS_* */ - - /* For non-agg: RTS + CTS + frame tx attempts time + ACK. - * For agg: RTS + CTS + aggregation tx time + block-ack time. */ - __le16 wireless_media_time; /* uSecs */ - - __le16 reserved; - __le32 pa_power1; /* RF power amplifier measurement (not used) */ - __le32 pa_power2; - - /* - * For non-agg: frame status TX_STATUS_* - * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status - * fields follow this one, up to frame_count. - * Bit fields: - * 11- 0: AGG_TX_STATE_* status code - * 15-12: Retry count for 1st frame in aggregation (retries - * occur if tx failed for this frame when it was a - * member of a previous aggregation block). If rate - * scaling is used, retry count indicates the rate - * table entry used for all frames in the new agg. - * 31-16: Sequence # for this frame's Tx cmd (not SSN!) - */ - union { - __le32 status; - struct agg_tx_status agg_status[0]; /* for each agg frame */ - } u; -} __packed; - -/* - * N_COMPRESSED_BA = 0xc5 (response only, not a command) - * - * Reports Block-Acknowledge from recipient station - */ -struct il_compressed_ba_resp { - __le32 sta_addr_lo32; - __le16 sta_addr_hi16; - __le16 reserved; - - /* Index of recipient (BA-sending) station in uCode's station table */ - u8 sta_id; - u8 tid; - __le16 seq_ctl; - __le64 bitmap; - __le16 scd_flow; - __le16 scd_ssn; -} __packed; - -/* - * C_TX_PWR_TBL = 0x97 (command, has simple generic response) - * - * See details under "TXPOWER" in 4965.h. - */ - -struct il3945_txpowertable_cmd { - u8 band; /* 0: 5 GHz, 1: 2.4 GHz */ - u8 reserved; - __le16 channel; - struct il3945_power_per_rate power[IL_MAX_RATES]; -} __packed; - -struct il4965_txpowertable_cmd { - u8 band; /* 0: 5 GHz, 1: 2.4 GHz */ - u8 reserved; - __le16 channel; - struct il4965_tx_power_db tx_power; -} __packed; - -/** - * struct il3945_rate_scaling_cmd - Rate Scaling Command & Response - * - * C_RATE_SCALE = 0x47 (command, has simple generic response) - * - * NOTE: The table of rates passed to the uCode via the - * RATE_SCALE command sets up the corresponding order of - * rates used for all related commands, including rate - * masks, etc. - * - * For example, if you set 9MB (PLCP 0x0f) as the first - * rate in the rate table, the bit mask for that rate - * when passed through ofdm_basic_rates on the C_RXON - * command would be bit 0 (1 << 0) - */ -struct il3945_rate_scaling_info { - __le16 rate_n_flags; - u8 try_cnt; - u8 next_rate_idx; -} __packed; - -struct il3945_rate_scaling_cmd { - u8 table_id; - u8 reserved[3]; - struct il3945_rate_scaling_info table[IL_MAX_RATES]; -} __packed; - -/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */ -#define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1 << 0) - -/* # of EDCA prioritized tx fifos */ -#define LINK_QUAL_AC_NUM AC_NUM - -/* # entries in rate scale table to support Tx retries */ -#define LINK_QUAL_MAX_RETRY_NUM 16 - -/* Tx antenna selection values */ -#define LINK_QUAL_ANT_A_MSK (1 << 0) -#define LINK_QUAL_ANT_B_MSK (1 << 1) -#define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK) - -/** - * struct il_link_qual_general_params - * - * Used in C_TX_LINK_QUALITY_CMD - */ -struct il_link_qual_general_params { - u8 flags; - - /* No entries at or above this (driver chosen) idx contain MIMO */ - u8 mimo_delimiter; - - /* Best single antenna to use for single stream (legacy, SISO). */ - u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */ - - /* Best antennas to use for MIMO (unused for 4965, assumes both). */ - u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */ - - /* - * If driver needs to use different initial rates for different - * EDCA QOS access categories (as implemented by tx fifos 0-3), - * this table will set that up, by indicating the idxes in the - * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start. - * Otherwise, driver should set all entries to 0. - * - * Entry usage: - * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice - * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3. - */ - u8 start_rate_idx[LINK_QUAL_AC_NUM]; -} __packed; - -#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ -#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) -#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) - -#define LINK_QUAL_AGG_DISABLE_START_DEF (3) -#define LINK_QUAL_AGG_DISABLE_START_MAX (255) -#define LINK_QUAL_AGG_DISABLE_START_MIN (0) - -#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (31) -#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) -#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) - -/** - * struct il_link_qual_agg_params - * - * Used in C_TX_LINK_QUALITY_CMD - */ -struct il_link_qual_agg_params { - - /* - *Maximum number of uSec in aggregation. - * default set to 4000 (4 milliseconds) if not configured in .cfg - */ - __le16 agg_time_limit; - - /* - * Number of Tx retries allowed for a frame, before that frame will - * no longer be considered for the start of an aggregation sequence - * (scheduler will then try to tx it as single frame). - * Driver should set this to 3. - */ - u8 agg_dis_start_th; - - /* - * Maximum number of frames in aggregation. - * 0 = no limit (default). 1 = no aggregation. - * Other values = max # frames in aggregation. - */ - u8 agg_frame_cnt_limit; - - __le32 reserved; -} __packed; - -/* - * C_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response) - * - * For 4965 devices only; 3945 uses C_RATE_SCALE. - * - * Each station in the 4965 device's internal station table has its own table - * of 16 - * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when - * an ACK is not received. This command replaces the entire table for - * one station. - * - * NOTE: Station must already be in 4965 device's station table. - * Use C_ADD_STA. - * - * The rate scaling procedures described below work well. Of course, other - * procedures are possible, and may work better for particular environments. - * - * - * FILLING THE RATE TBL - * - * Given a particular initial rate and mode, as determined by the rate - * scaling algorithm described below, the Linux driver uses the following - * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the - * Link Quality command: - * - * - * 1) If using High-throughput (HT) (SISO or MIMO) initial rate: - * a) Use this same initial rate for first 3 entries. - * b) Find next lower available rate using same mode (SISO or MIMO), - * use for next 3 entries. If no lower rate available, switch to - * legacy mode (no HT40 channel, no MIMO, no short guard interval). - * c) If using MIMO, set command's mimo_delimiter to number of entries - * using MIMO (3 or 6). - * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel, - * no MIMO, no short guard interval), at the next lower bit rate - * (e.g. if second HT bit rate was 54, try 48 legacy), and follow - * legacy procedure for remaining table entries. - * - * 2) If using legacy initial rate: - * a) Use the initial rate for only one entry. - * b) For each following entry, reduce the rate to next lower available - * rate, until reaching the lowest available rate. - * c) When reducing rate, also switch antenna selection. - * d) Once lowest available rate is reached, repeat this rate until - * rate table is filled (16 entries), switching antenna each entry. - * - * - * ACCUMULATING HISTORY - * - * The rate scaling algorithm for 4965 devices, as implemented in Linux driver, - * uses two sets of frame Tx success history: One for the current/active - * modulation mode, and one for a speculative/search mode that is being - * attempted. If the speculative mode turns out to be more effective (i.e. - * actual transfer rate is better), then the driver continues to use the - * speculative mode as the new current active mode. - * - * Each history set contains, separately for each possible rate, data for a - * sliding win of the 62 most recent tx attempts at that rate. The data - * includes a shifting bitmap of success(1)/failure(0), and sums of successful - * and attempted frames, from which the driver can additionally calculate a - * success ratio (success / attempted) and number of failures - * (attempted - success), and control the size of the win (attempted). - * The driver uses the bit map to remove successes from the success sum, as - * the oldest tx attempts fall out of the win. - * - * When the 4965 device makes multiple tx attempts for a given frame, each - * attempt might be at a different rate, and have different modulation - * characteristics (e.g. antenna, fat channel, short guard interval), as set - * up in the rate scaling table in the Link Quality command. The driver must - * determine which rate table entry was used for each tx attempt, to determine - * which rate-specific history to update, and record only those attempts that - * match the modulation characteristics of the history set. - * - * When using block-ack (aggregation), all frames are transmitted at the same - * rate, since there is no per-attempt acknowledgment from the destination - * station. The Tx response struct il_tx_resp indicates the Tx rate in - * rate_n_flags field. After receiving a block-ack, the driver can update - * history for the entire block all at once. - * - * - * FINDING BEST STARTING RATE: - * - * When working with a selected initial modulation mode (see below), the - * driver attempts to find a best initial rate. The initial rate is the - * first entry in the Link Quality command's rate table. - * - * 1) Calculate actual throughput (success ratio * expected throughput, see - * table below) for current initial rate. Do this only if enough frames - * have been attempted to make the value meaningful: at least 6 failed - * tx attempts, or at least 8 successes. If not enough, don't try rate - * scaling yet. - * - * 2) Find available rates adjacent to current initial rate. Available means: - * a) supported by hardware && - * b) supported by association && - * c) within any constraints selected by user - * - * 3) Gather measured throughputs for adjacent rates. These might not have - * enough history to calculate a throughput. That's okay, we might try - * using one of them anyway! - * - * 4) Try decreasing rate if, for current rate: - * a) success ratio is < 15% || - * b) lower adjacent rate has better measured throughput || - * c) higher adjacent rate has worse throughput, and lower is unmeasured - * - * As a sanity check, if decrease was determined above, leave rate - * unchanged if: - * a) lower rate unavailable - * b) success ratio at current rate > 85% (very good) - * c) current measured throughput is better than expected throughput - * of lower rate (under perfect 100% tx conditions, see table below) - * - * 5) Try increasing rate if, for current rate: - * a) success ratio is < 15% || - * b) both adjacent rates' throughputs are unmeasured (try it!) || - * b) higher adjacent rate has better measured throughput || - * c) lower adjacent rate has worse throughput, and higher is unmeasured - * - * As a sanity check, if increase was determined above, leave rate - * unchanged if: - * a) success ratio at current rate < 70%. This is not particularly - * good performance; higher rate is sure to have poorer success. - * - * 6) Re-evaluate the rate after each tx frame. If working with block- - * acknowledge, history and stats may be calculated for the entire - * block (including prior history that fits within the history wins), - * before re-evaluation. - * - * FINDING BEST STARTING MODULATION MODE: - * - * After working with a modulation mode for a "while" (and doing rate scaling), - * the driver searches for a new initial mode in an attempt to improve - * throughput. The "while" is measured by numbers of attempted frames: - * - * For legacy mode, search for new mode after: - * 480 successful frames, or 160 failed frames - * For high-throughput modes (SISO or MIMO), search for new mode after: - * 4500 successful frames, or 400 failed frames - * - * Mode switch possibilities are (3 for each mode): - * - * For legacy: - * Change antenna, try SISO (if HT association), try MIMO (if HT association) - * For SISO: - * Change antenna, try MIMO, try shortened guard interval (SGI) - * For MIMO: - * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI) - * - * When trying a new mode, use the same bit rate as the old/current mode when - * trying antenna switches and shortened guard interval. When switching to - * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate - * for which the expected throughput (under perfect conditions) is about the - * same or slightly better than the actual measured throughput delivered by - * the old/current mode. - * - * Actual throughput can be estimated by multiplying the expected throughput - * by the success ratio (successful / attempted tx frames). Frame size is - * not considered in this calculation; it assumes that frame size will average - * out to be fairly consistent over several samples. The following are - * metric values for expected throughput assuming 100% success ratio. - * Only G band has support for CCK rates: - * - * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60 - * - * G: 7 13 35 58 40 57 72 98 121 154 177 186 186 - * A: 0 0 0 0 40 57 72 98 121 154 177 186 186 - * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202 - * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211 - * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251 - * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257 - * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257 - * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264 - * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289 - * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293 - * - * After the new mode has been tried for a short while (minimum of 6 failed - * frames or 8 successful frames), compare success ratio and actual throughput - * estimate of the new mode with the old. If either is better with the new - * mode, continue to use the new mode. - * - * Continue comparing modes until all 3 possibilities have been tried. - * If moving from legacy to HT, try all 3 possibilities from the new HT - * mode. After trying all 3, a best mode is found. Continue to use this mode - * for the longer "while" described above (e.g. 480 successful frames for - * legacy), and then repeat the search process. - * - */ -struct il_link_quality_cmd { - - /* Index of destination/recipient station in uCode's station table */ - u8 sta_id; - u8 reserved1; - __le16 control; /* not used */ - struct il_link_qual_general_params general_params; - struct il_link_qual_agg_params agg_params; - - /* - * Rate info; when using rate-scaling, Tx command's initial_rate_idx - * specifies 1st Tx rate attempted, via idx into this table. - * 4965 devices works its way through table when retrying Tx. - */ - struct { - __le32 rate_n_flags; /* RATE_MCS_*, RATE_* */ - } rs_table[LINK_QUAL_MAX_RETRY_NUM]; - __le32 reserved2; -} __packed; - -/* - * BT configuration enable flags: - * bit 0 - 1: BT channel announcement enabled - * 0: disable - * bit 1 - 1: priority of BT device enabled - * 0: disable - */ -#define BT_COEX_DISABLE (0x0) -#define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0) -#define BT_ENABLE_PRIORITY BIT(1) - -#define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY) - -#define BT_LEAD_TIME_DEF (0x1E) - -#define BT_MAX_KILL_DEF (0x5) - -/* - * C_BT_CONFIG = 0x9b (command, has simple generic response) - * - * 3945 and 4965 devices support hardware handshake with Bluetooth device on - * same platform. Bluetooth device alerts wireless device when it will Tx; - * wireless device can delay or kill its own Tx to accommodate. - */ -struct il_bt_cmd { - u8 flags; - u8 lead_time; - u8 max_kill; - u8 reserved; - __le32 kill_ack_mask; - __le32 kill_cts_mask; -} __packed; - -/****************************************************************************** - * (6) - * Spectrum Management (802.11h) Commands, Responses, Notifications: - * - *****************************************************************************/ - -/* - * Spectrum Management - */ -#define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \ - RXON_FILTER_CTL2HOST_MSK | \ - RXON_FILTER_ACCEPT_GRP_MSK | \ - RXON_FILTER_DIS_DECRYPT_MSK | \ - RXON_FILTER_DIS_GRP_DECRYPT_MSK | \ - RXON_FILTER_ASSOC_MSK | \ - RXON_FILTER_BCON_AWARE_MSK) - -struct il_measure_channel { - __le32 duration; /* measurement duration in extended beacon - * format */ - u8 channel; /* channel to measure */ - u8 type; /* see enum il_measure_type */ - __le16 reserved; -} __packed; - -/* - * C_SPECTRUM_MEASUREMENT = 0x74 (command) - */ -struct il_spectrum_cmd { - __le16 len; /* number of bytes starting from token */ - u8 token; /* token id */ - u8 id; /* measurement id -- 0 or 1 */ - u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */ - u8 periodic; /* 1 = periodic */ - __le16 path_loss_timeout; - __le32 start_time; /* start time in extended beacon format */ - __le32 reserved2; - __le32 flags; /* rxon flags */ - __le32 filter_flags; /* rxon filter flags */ - __le16 channel_count; /* minimum 1, maximum 10 */ - __le16 reserved3; - struct il_measure_channel channels[10]; -} __packed; - -/* - * C_SPECTRUM_MEASUREMENT = 0x74 (response) - */ -struct il_spectrum_resp { - u8 token; - u8 id; /* id of the prior command replaced, or 0xff */ - __le16 status; /* 0 - command will be handled - * 1 - cannot handle (conflicts with another - * measurement) */ -} __packed; - -enum il_measurement_state { - IL_MEASUREMENT_START = 0, - IL_MEASUREMENT_STOP = 1, -}; - -enum il_measurement_status { - IL_MEASUREMENT_OK = 0, - IL_MEASUREMENT_CONCURRENT = 1, - IL_MEASUREMENT_CSA_CONFLICT = 2, - IL_MEASUREMENT_TGH_CONFLICT = 3, - /* 4-5 reserved */ - IL_MEASUREMENT_STOPPED = 6, - IL_MEASUREMENT_TIMEOUT = 7, - IL_MEASUREMENT_PERIODIC_FAILED = 8, -}; - -#define NUM_ELEMENTS_IN_HISTOGRAM 8 - -struct il_measurement_histogram { - __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */ - __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */ -} __packed; - -/* clear channel availability counters */ -struct il_measurement_cca_counters { - __le32 ofdm; - __le32 cck; -} __packed; - -enum il_measure_type { - IL_MEASURE_BASIC = (1 << 0), - IL_MEASURE_CHANNEL_LOAD = (1 << 1), - IL_MEASURE_HISTOGRAM_RPI = (1 << 2), - IL_MEASURE_HISTOGRAM_NOISE = (1 << 3), - IL_MEASURE_FRAME = (1 << 4), - /* bits 5:6 are reserved */ - IL_MEASURE_IDLE = (1 << 7), -}; - -/* - * N_SPECTRUM_MEASUREMENT = 0x75 (notification only, not a command) - */ -struct il_spectrum_notification { - u8 id; /* measurement id -- 0 or 1 */ - u8 token; - u8 channel_idx; /* idx in measurement channel list */ - u8 state; /* 0 - start, 1 - stop */ - __le32 start_time; /* lower 32-bits of TSF */ - u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */ - u8 channel; - u8 type; /* see enum il_measurement_type */ - u8 reserved1; - /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only - * valid if applicable for measurement type requested. */ - __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */ - __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */ - __le32 cca_time; /* channel load time in usecs */ - u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 - - * unidentified */ - u8 reserved2[3]; - struct il_measurement_histogram histogram; - __le32 stop_time; /* lower 32-bits of TSF */ - __le32 status; /* see il_measurement_status */ -} __packed; - -/****************************************************************************** - * (7) - * Power Management Commands, Responses, Notifications: - * - *****************************************************************************/ - -/** - * struct il_powertable_cmd - Power Table Command - * @flags: See below: - * - * C_POWER_TBL = 0x77 (command, has simple generic response) - * - * PM allow: - * bit 0 - '0' Driver not allow power management - * '1' Driver allow PM (use rest of parameters) - * - * uCode send sleep notifications: - * bit 1 - '0' Don't send sleep notification - * '1' send sleep notification (SEND_PM_NOTIFICATION) - * - * Sleep over DTIM - * bit 2 - '0' PM have to walk up every DTIM - * '1' PM could sleep over DTIM till listen Interval. - * - * PCI power managed - * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1) - * '1' !(PCI_CFG_LINK_CTRL & 0x1) - * - * Fast PD - * bit 4 - '1' Put radio to sleep when receiving frame for others - * - * Force sleep Modes - * bit 31/30- '00' use both mac/xtal sleeps - * '01' force Mac sleep - * '10' force xtal sleep - * '11' Illegal set - * - * NOTE: if sleep_interval[SLEEP_INTRVL_TBL_SIZE-1] > DTIM period then - * ucode assume sleep over DTIM is allowed and we don't need to wake up - * for every DTIM. - */ -#define IL_POWER_VEC_SIZE 5 - -#define IL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) -#define IL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2)) -#define IL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) - -struct il3945_powertable_cmd { - __le16 flags; - u8 reserved[2]; - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 sleep_interval[IL_POWER_VEC_SIZE]; -} __packed; - -struct il_powertable_cmd { - __le16 flags; - u8 keep_alive_seconds; /* 3945 reserved */ - u8 debug_flags; /* 3945 reserved */ - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 sleep_interval[IL_POWER_VEC_SIZE]; - __le32 keep_alive_beacons; -} __packed; - -/* - * N_PM_SLEEP = 0x7A (notification only, not a command) - * all devices identical. - */ -struct il_sleep_notification { - u8 pm_sleep_mode; - u8 pm_wakeup_src; - __le16 reserved; - __le32 sleep_time; - __le32 tsf_low; - __le32 bcon_timer; -} __packed; - -/* Sleep states. all devices identical. */ -enum { - IL_PM_NO_SLEEP = 0, - IL_PM_SLP_MAC = 1, - IL_PM_SLP_FULL_MAC_UNASSOCIATE = 2, - IL_PM_SLP_FULL_MAC_CARD_STATE = 3, - IL_PM_SLP_PHY = 4, - IL_PM_SLP_REPENT = 5, - IL_PM_WAKEUP_BY_TIMER = 6, - IL_PM_WAKEUP_BY_DRIVER = 7, - IL_PM_WAKEUP_BY_RFKILL = 8, - /* 3 reserved */ - IL_PM_NUM_OF_MODES = 12, -}; - -/* - * N_CARD_STATE = 0xa1 (notification only, not a command) - */ -struct il_card_state_notif { - __le32 flags; -} __packed; - -#define HW_CARD_DISABLED 0x01 -#define SW_CARD_DISABLED 0x02 -#define CT_CARD_DISABLED 0x04 -#define RXON_CARD_DISABLED 0x10 - -struct il_ct_kill_config { - __le32 reserved; - __le32 critical_temperature_M; - __le32 critical_temperature_R; -} __packed; - -/****************************************************************************** - * (8) - * Scan Commands, Responses, Notifications: - * - *****************************************************************************/ - -#define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0) -#define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1) - -/** - * struct il_scan_channel - entry in C_SCAN channel table - * - * One for each channel in the scan list. - * Each channel can independently select: - * 1) SSID for directed active scans - * 2) Txpower setting (for rate specified within Tx command) - * 3) How long to stay on-channel (behavior may be modified by quiet_time, - * quiet_plcp_th, good_CRC_th) - * - * To avoid uCode errors, make sure the following are true (see comments - * under struct il_scan_cmd about max_out_time and quiet_time): - * 1) If using passive_dwell (i.e. passive_dwell != 0): - * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0) - * 2) quiet_time <= active_dwell - * 3) If restricting off-channel time (i.e. max_out_time !=0): - * passive_dwell < max_out_time - * active_dwell < max_out_time - */ -struct il3945_scan_channel { - /* - * type is defined as: - * 0:0 1 = active, 0 = passive - * 1:4 SSID direct bit map; if a bit is set, then corresponding - * SSID IE is transmitted in probe request. - * 5:7 reserved - */ - u8 type; - u8 channel; /* band is selected by il3945_scan_cmd "flags" field */ - struct il3945_tx_power tpc; - __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ - __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ -} __packed; - -/* set number of direct probes u8 type */ -#define IL39_SCAN_PROBE_MASK(n) ((BIT(n) | (BIT(n) - BIT(1)))) - -struct il_scan_channel { - /* - * type is defined as: - * 0:0 1 = active, 0 = passive - * 1:20 SSID direct bit map; if a bit is set, then corresponding - * SSID IE is transmitted in probe request. - * 21:31 reserved - */ - __le32 type; - __le16 channel; /* band is selected by il_scan_cmd "flags" field */ - u8 tx_gain; /* gain for analog radio */ - u8 dsp_atten; /* gain for DSP */ - __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ - __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ -} __packed; - -/* set number of direct probes __le32 type */ -#define IL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1)))) - -/** - * struct il_ssid_ie - directed scan network information element - * - * Up to 20 of these may appear in C_SCAN (Note: Only 4 are in - * 3945 SCAN api), selected by "type" bit field in struct il_scan_channel; - * each channel may select different ssids from among the 20 (4) entries. - * SSID IEs get transmitted in reverse order of entry. - */ -struct il_ssid_ie { - u8 id; - u8 len; - u8 ssid[32]; -} __packed; - -#define PROBE_OPTION_MAX_3945 4 -#define PROBE_OPTION_MAX 20 -#define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF) -#define IL_GOOD_CRC_TH_DISABLED 0 -#define IL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) -#define IL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff) -#define IL_MAX_SCAN_SIZE 1024 -#define IL_MAX_CMD_SIZE 4096 - -/* - * C_SCAN = 0x80 (command) - * - * The hardware scan command is very powerful; the driver can set it up to - * maintain (relatively) normal network traffic while doing a scan in the - * background. The max_out_time and suspend_time control the ratio of how - * long the device stays on an associated network channel ("service channel") - * vs. how long it's away from the service channel, i.e. tuned to other channels - * for scanning. - * - * max_out_time is the max time off-channel (in usec), and suspend_time - * is how long (in "extended beacon" format) that the scan is "suspended" - * after returning to the service channel. That is, suspend_time is the - * time that we stay on the service channel, doing normal work, between - * scan segments. The driver may set these parameters differently to support - * scanning when associated vs. not associated, and light vs. heavy traffic - * loads when associated. - * - * After receiving this command, the device's scan engine does the following; - * - * 1) Sends SCAN_START notification to driver - * 2) Checks to see if it has time to do scan for one channel - * 3) Sends NULL packet, with power-save (PS) bit set to 1, - * to tell AP that we're going off-channel - * 4) Tunes to first channel in scan list, does active or passive scan - * 5) Sends SCAN_RESULT notification to driver - * 6) Checks to see if it has time to do scan on *next* channel in list - * 7) Repeats 4-6 until it no longer has time to scan the next channel - * before max_out_time expires - * 8) Returns to service channel - * 9) Sends NULL packet with PS=0 to tell AP that we're back - * 10) Stays on service channel until suspend_time expires - * 11) Repeats entire process 2-10 until list is complete - * 12) Sends SCAN_COMPLETE notification - * - * For fast, efficient scans, the scan command also has support for staying on - * a channel for just a short time, if doing active scanning and getting no - * responses to the transmitted probe request. This time is controlled by - * quiet_time, and the number of received packets below which a channel is - * considered "quiet" is controlled by quiet_plcp_threshold. - * - * For active scanning on channels that have regulatory restrictions against - * blindly transmitting, the scan can listen before transmitting, to make sure - * that there is already legitimate activity on the channel. If enough - * packets are cleanly received on the channel (controlled by good_CRC_th, - * typical value 1), the scan engine starts transmitting probe requests. - * - * Driver must use separate scan commands for 2.4 vs. 5 GHz bands. - * - * To avoid uCode errors, see timing restrictions described under - * struct il_scan_channel. - */ - -struct il3945_scan_cmd { - __le16 len; - u8 reserved0; - u8 channel_count; /* # channels in channel list */ - __le16 quiet_time; /* dwell only this # millisecs on quiet channel - * (only for active scan) */ - __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ - __le16 good_CRC_th; /* passive -> active promotion threshold */ - __le16 reserved1; - __le32 max_out_time; /* max usec to be away from associated (service) - * channel */ - __le32 suspend_time; /* pause scan this long (in "extended beacon - * format") when returning to service channel: - * 3945; 31:24 # beacons, 19:0 additional usec, - * 4965; 31:22 # beacons, 21:0 additional usec. - */ - __le32 flags; /* RXON_FLG_* */ - __le32 filter_flags; /* RXON_FILTER_* */ - - /* For active scans (set to all-0s for passive scans). - * Does not include payload. Must specify Tx rate; no rate scaling. */ - struct il3945_tx_cmd tx_cmd; - - /* For directed active scans (set to all-0s otherwise) */ - struct il_ssid_ie direct_scan[PROBE_OPTION_MAX_3945]; - - /* - * Probe request frame, followed by channel list. - * - * Size of probe request frame is specified by byte count in tx_cmd. - * Channel list follows immediately after probe request frame. - * Number of channels in list is specified by channel_count. - * Each channel in list is of type: - * - * struct il3945_scan_channel channels[0]; - * - * NOTE: Only one band of channels can be scanned per pass. You - * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait - * for one scan to complete (i.e. receive N_SCAN_COMPLETE) - * before requesting another scan. - */ - u8 data[0]; -} __packed; - -struct il_scan_cmd { - __le16 len; - u8 reserved0; - u8 channel_count; /* # channels in channel list */ - __le16 quiet_time; /* dwell only this # millisecs on quiet channel - * (only for active scan) */ - __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ - __le16 good_CRC_th; /* passive -> active promotion threshold */ - __le16 rx_chain; /* RXON_RX_CHAIN_* */ - __le32 max_out_time; /* max usec to be away from associated (service) - * channel */ - __le32 suspend_time; /* pause scan this long (in "extended beacon - * format") when returning to service chnl: - * 3945; 31:24 # beacons, 19:0 additional usec, - * 4965; 31:22 # beacons, 21:0 additional usec. - */ - __le32 flags; /* RXON_FLG_* */ - __le32 filter_flags; /* RXON_FILTER_* */ - - /* For active scans (set to all-0s for passive scans). - * Does not include payload. Must specify Tx rate; no rate scaling. */ - struct il_tx_cmd tx_cmd; - - /* For directed active scans (set to all-0s otherwise) */ - struct il_ssid_ie direct_scan[PROBE_OPTION_MAX]; - - /* - * Probe request frame, followed by channel list. - * - * Size of probe request frame is specified by byte count in tx_cmd. - * Channel list follows immediately after probe request frame. - * Number of channels in list is specified by channel_count. - * Each channel in list is of type: - * - * struct il_scan_channel channels[0]; - * - * NOTE: Only one band of channels can be scanned per pass. You - * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait - * for one scan to complete (i.e. receive N_SCAN_COMPLETE) - * before requesting another scan. - */ - u8 data[0]; -} __packed; - -/* Can abort will notify by complete notification with abort status. */ -#define CAN_ABORT_STATUS cpu_to_le32(0x1) -/* complete notification statuses */ -#define ABORT_STATUS 0x2 - -/* - * C_SCAN = 0x80 (response) - */ -struct il_scanreq_notification { - __le32 status; /* 1: okay, 2: cannot fulfill request */ -} __packed; - -/* - * N_SCAN_START = 0x82 (notification only, not a command) - */ -struct il_scanstart_notification { - __le32 tsf_low; - __le32 tsf_high; - __le32 beacon_timer; - u8 channel; - u8 band; - u8 reserved[2]; - __le32 status; -} __packed; - -#define SCAN_OWNER_STATUS 0x1 -#define MEASURE_OWNER_STATUS 0x2 - -#define IL_PROBE_STATUS_OK 0 -#define IL_PROBE_STATUS_TX_FAILED BIT(0) -/* error statuses combined with TX_FAILED */ -#define IL_PROBE_STATUS_FAIL_TTL BIT(1) -#define IL_PROBE_STATUS_FAIL_BT BIT(2) - -#define NUMBER_OF_STATS 1 /* first __le32 is good CRC */ -/* - * N_SCAN_RESULTS = 0x83 (notification only, not a command) - */ -struct il_scanresults_notification { - u8 channel; - u8 band; - u8 probe_status; - u8 num_probe_not_sent; /* not enough time to send */ - __le32 tsf_low; - __le32 tsf_high; - __le32 stats[NUMBER_OF_STATS]; -} __packed; - -/* - * N_SCAN_COMPLETE = 0x84 (notification only, not a command) - */ -struct il_scancomplete_notification { - u8 scanned_channels; - u8 status; - u8 last_channel; - __le32 tsf_low; - __le32 tsf_high; -} __packed; - -/****************************************************************************** - * (9) - * IBSS/AP Commands and Notifications: - * - *****************************************************************************/ - -enum il_ibss_manager { - IL_NOT_IBSS_MANAGER = 0, - IL_IBSS_MANAGER = 1, -}; - -/* - * N_BEACON = 0x90 (notification only, not a command) - */ - -struct il3945_beacon_notif { - struct il3945_tx_resp beacon_notify_hdr; - __le32 low_tsf; - __le32 high_tsf; - __le32 ibss_mgr_status; -} __packed; - -struct il4965_beacon_notif { - struct il4965_tx_resp beacon_notify_hdr; - __le32 low_tsf; - __le32 high_tsf; - __le32 ibss_mgr_status; -} __packed; - -/* - * C_TX_BEACON= 0x91 (command, has simple generic response) - */ - -struct il3945_tx_beacon_cmd { - struct il3945_tx_cmd tx; - __le16 tim_idx; - u8 tim_size; - u8 reserved1; - struct ieee80211_hdr frame[0]; /* beacon frame */ -} __packed; - -struct il_tx_beacon_cmd { - struct il_tx_cmd tx; - __le16 tim_idx; - u8 tim_size; - u8 reserved1; - struct ieee80211_hdr frame[0]; /* beacon frame */ -} __packed; - -/****************************************************************************** - * (10) - * Statistics Commands and Notifications: - * - *****************************************************************************/ - -#define IL_TEMP_CONVERT 260 - -#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 -#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 -#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 - -/* Used for passing to driver number of successes and failures per rate */ -struct rate_histogram { - union { - __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; - __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; - __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; - } success; - union { - __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; - __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; - __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; - } failed; -} __packed; - -/* stats command response */ - -struct iwl39_stats_rx_phy { - __le32 ina_cnt; - __le32 fina_cnt; - __le32 plcp_err; - __le32 crc32_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 false_alarm_cnt; - __le32 fina_sync_err_cnt; - __le32 sfd_timeout; - __le32 fina_timeout; - __le32 unresponded_rts; - __le32 rxe_frame_limit_overrun; - __le32 sent_ack_cnt; - __le32 sent_cts_cnt; -} __packed; - -struct iwl39_stats_rx_non_phy { - __le32 bogus_cts; /* CTS received when not expecting CTS */ - __le32 bogus_ack; /* ACK received when not expecting ACK */ - __le32 non_bssid_frames; /* number of frames with BSSID that - * doesn't belong to the STA BSSID */ - __le32 filtered_frames; /* count frames that were dumped in the - * filtering process */ - __le32 non_channel_beacons; /* beacons with our bss id but not on - * our serving channel */ -} __packed; - -struct iwl39_stats_rx { - struct iwl39_stats_rx_phy ofdm; - struct iwl39_stats_rx_phy cck; - struct iwl39_stats_rx_non_phy general; -} __packed; - -struct iwl39_stats_tx { - __le32 preamble_cnt; - __le32 rx_detected_cnt; - __le32 bt_prio_defer_cnt; - __le32 bt_prio_kill_cnt; - __le32 few_bytes_cnt; - __le32 cts_timeout; - __le32 ack_timeout; - __le32 expected_ack_cnt; - __le32 actual_ack_cnt; -} __packed; - -struct stats_dbg { - __le32 burst_check; - __le32 burst_count; - __le32 wait_for_silence_timeout_cnt; - __le32 reserved[3]; -} __packed; - -struct iwl39_stats_div { - __le32 tx_on_a; - __le32 tx_on_b; - __le32 exec_time; - __le32 probe_time; -} __packed; - -struct iwl39_stats_general { - __le32 temperature; - struct stats_dbg dbg; - __le32 sleep_time; - __le32 slots_out; - __le32 slots_idle; - __le32 ttl_timestamp; - struct iwl39_stats_div div; -} __packed; - -struct stats_rx_phy { - __le32 ina_cnt; - __le32 fina_cnt; - __le32 plcp_err; - __le32 crc32_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 false_alarm_cnt; - __le32 fina_sync_err_cnt; - __le32 sfd_timeout; - __le32 fina_timeout; - __le32 unresponded_rts; - __le32 rxe_frame_limit_overrun; - __le32 sent_ack_cnt; - __le32 sent_cts_cnt; - __le32 sent_ba_rsp_cnt; - __le32 dsp_self_kill; - __le32 mh_format_err; - __le32 re_acq_main_rssi_sum; - __le32 reserved3; -} __packed; - -struct stats_rx_ht_phy { - __le32 plcp_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 crc32_err; - __le32 mh_format_err; - __le32 agg_crc32_good; - __le32 agg_mpdu_cnt; - __le32 agg_cnt; - __le32 unsupport_mcs; -} __packed; - -#define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1) - -struct stats_rx_non_phy { - __le32 bogus_cts; /* CTS received when not expecting CTS */ - __le32 bogus_ack; /* ACK received when not expecting ACK */ - __le32 non_bssid_frames; /* number of frames with BSSID that - * doesn't belong to the STA BSSID */ - __le32 filtered_frames; /* count frames that were dumped in the - * filtering process */ - __le32 non_channel_beacons; /* beacons with our bss id but not on - * our serving channel */ - __le32 channel_beacons; /* beacons with our bss id and in our - * serving channel */ - __le32 num_missed_bcon; /* number of missed beacons */ - __le32 adc_rx_saturation_time; /* count in 0.8us units the time the - * ADC was in saturation */ - __le32 ina_detection_search_time; /* total time (in 0.8us) searched - * for INA */ - __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ - __le32 interference_data_flag; /* flag for interference data - * availability. 1 when data is - * available. */ - __le32 channel_load; /* counts RX Enable time in uSec */ - __le32 dsp_false_alarms; /* DSP false alarm (both OFDM - * and CCK) counter */ - __le32 beacon_rssi_a; - __le32 beacon_rssi_b; - __le32 beacon_rssi_c; - __le32 beacon_energy_a; - __le32 beacon_energy_b; - __le32 beacon_energy_c; -} __packed; - -struct stats_rx { - struct stats_rx_phy ofdm; - struct stats_rx_phy cck; - struct stats_rx_non_phy general; - struct stats_rx_ht_phy ofdm_ht; -} __packed; - -/** - * struct stats_tx_power - current tx power - * - * @ant_a: current tx power on chain a in 1/2 dB step - * @ant_b: current tx power on chain b in 1/2 dB step - * @ant_c: current tx power on chain c in 1/2 dB step - */ -struct stats_tx_power { - u8 ant_a; - u8 ant_b; - u8 ant_c; - u8 reserved; -} __packed; - -struct stats_tx_non_phy_agg { - __le32 ba_timeout; - __le32 ba_reschedule_frames; - __le32 scd_query_agg_frame_cnt; - __le32 scd_query_no_agg; - __le32 scd_query_agg; - __le32 scd_query_mismatch; - __le32 frame_not_ready; - __le32 underrun; - __le32 bt_prio_kill; - __le32 rx_ba_rsp_cnt; -} __packed; - -struct stats_tx { - __le32 preamble_cnt; - __le32 rx_detected_cnt; - __le32 bt_prio_defer_cnt; - __le32 bt_prio_kill_cnt; - __le32 few_bytes_cnt; - __le32 cts_timeout; - __le32 ack_timeout; - __le32 expected_ack_cnt; - __le32 actual_ack_cnt; - __le32 dump_msdu_cnt; - __le32 burst_abort_next_frame_mismatch_cnt; - __le32 burst_abort_missing_next_frame_cnt; - __le32 cts_timeout_collision; - __le32 ack_or_ba_timeout_collision; - struct stats_tx_non_phy_agg agg; - - __le32 reserved1; -} __packed; - -struct stats_div { - __le32 tx_on_a; - __le32 tx_on_b; - __le32 exec_time; - __le32 probe_time; - __le32 reserved1; - __le32 reserved2; -} __packed; - -struct stats_general_common { - __le32 temperature; /* radio temperature */ - struct stats_dbg dbg; - __le32 sleep_time; - __le32 slots_out; - __le32 slots_idle; - __le32 ttl_timestamp; - struct stats_div div; - __le32 rx_enable_counter; - /* - * num_of_sos_states: - * count the number of times we have to re-tune - * in order to get out of bad PHY status - */ - __le32 num_of_sos_states; -} __packed; - -struct stats_general { - struct stats_general_common common; - __le32 reserved2; - __le32 reserved3; -} __packed; - -#define UCODE_STATS_CLEAR_MSK (0x1 << 0) -#define UCODE_STATS_FREQUENCY_MSK (0x1 << 1) -#define UCODE_STATS_NARROW_BAND_MSK (0x1 << 2) - -/* - * C_STATS = 0x9c, - * all devices identical. - * - * This command triggers an immediate response containing uCode stats. - * The response is in the same format as N_STATS 0x9d, below. - * - * If the CLEAR_STATS configuration flag is set, uCode will clear its - * internal copy of the stats (counters) after issuing the response. - * This flag does not affect N_STATSs after beacons (see below). - * - * If the DISABLE_NOTIF configuration flag is set, uCode will not issue - * N_STATSs after received beacons (see below). This flag - * does not affect the response to the C_STATS 0x9c itself. - */ -#define IL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */ -#define IL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2) /* see above */ -struct il_stats_cmd { - __le32 configuration_flags; /* IL_STATS_CONF_* */ -} __packed; - -/* - * N_STATS = 0x9d (notification only, not a command) - * - * By default, uCode issues this notification after receiving a beacon - * while associated. To disable this behavior, set DISABLE_NOTIF flag in the - * C_STATS 0x9c, above. - * - * Statistics counters continue to increment beacon after beacon, but are - * cleared when changing channels or when driver issues C_STATS - * 0x9c with CLEAR_STATS bit set (see above). - * - * uCode also issues this notification during scans. uCode clears stats - * appropriately so that each notification contains stats for only the - * one channel that has just been scanned. - */ -#define STATS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2) -#define STATS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8) - -struct il3945_notif_stats { - __le32 flag; - struct iwl39_stats_rx rx; - struct iwl39_stats_tx tx; - struct iwl39_stats_general general; -} __packed; - -struct il_notif_stats { - __le32 flag; - struct stats_rx rx; - struct stats_tx tx; - struct stats_general general; -} __packed; - -/* - * N_MISSED_BEACONS = 0xa2 (notification only, not a command) - * - * uCode send N_MISSED_BEACONS to driver when detect beacon missed - * in regardless of how many missed beacons, which mean when driver receive the - * notification, inside the command, it can find all the beacons information - * which include number of total missed beacons, number of consecutive missed - * beacons, number of beacons received and number of beacons expected to - * receive. - * - * If uCode detected consecutive_missed_beacons > 5, it will reset the radio - * in order to bring the radio/PHY back to working state; which has no relation - * to when driver will perform sensitivity calibration. - * - * Driver should set it own missed_beacon_threshold to decide when to perform - * sensitivity calibration based on number of consecutive missed beacons in - * order to improve overall performance, especially in noisy environment. - * - */ - -#define IL_MISSED_BEACON_THRESHOLD_MIN (1) -#define IL_MISSED_BEACON_THRESHOLD_DEF (5) -#define IL_MISSED_BEACON_THRESHOLD_MAX IL_MISSED_BEACON_THRESHOLD_DEF - -struct il_missed_beacon_notif { - __le32 consecutive_missed_beacons; - __le32 total_missed_becons; - __le32 num_expected_beacons; - __le32 num_recvd_beacons; -} __packed; - -/****************************************************************************** - * (11) - * Rx Calibration Commands: - * - * With the uCode used for open source drivers, most Tx calibration (except - * for Tx Power) and most Rx calibration is done by uCode during the - * "initialize" phase of uCode boot. Driver must calibrate only: - * - * 1) Tx power (depends on temperature), described elsewhere - * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas) - * 3) Receiver sensitivity (to optimize signal detection) - * - *****************************************************************************/ - -/** - * C_SENSITIVITY = 0xa8 (command, has simple generic response) - * - * This command sets up the Rx signal detector for a sensitivity level that - * is high enough to lock onto all signals within the associated network, - * but low enough to ignore signals that are below a certain threshold, so as - * not to have too many "false alarms". False alarms are signals that the - * Rx DSP tries to lock onto, but then discards after determining that they - * are noise. - * - * The optimum number of false alarms is between 5 and 50 per 200 TUs - * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e. - * time listening, not transmitting). Driver must adjust sensitivity so that - * the ratio of actual false alarms to actual Rx time falls within this range. - * - * While associated, uCode delivers N_STATSs after each - * received beacon. These provide information to the driver to analyze the - * sensitivity. Don't analyze stats that come in from scanning, or any - * other non-associated-network source. Pertinent stats include: - * - * From "general" stats (struct stats_rx_non_phy): - * - * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level) - * Measure of energy of desired signal. Used for establishing a level - * below which the device does not detect signals. - * - * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB) - * Measure of background noise in silent period after beacon. - * - * channel_load - * uSecs of actual Rx time during beacon period (varies according to - * how much time was spent transmitting). - * - * From "cck" and "ofdm" stats (struct stats_rx_phy), separately: - * - * false_alarm_cnt - * Signal locks abandoned early (before phy-level header). - * - * plcp_err - * Signal locks abandoned late (during phy-level header). - * - * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from - * beacon to beacon, i.e. each value is an accumulation of all errors - * before and including the latest beacon. Values will wrap around to 0 - * after counting up to 2^32 - 1. Driver must differentiate vs. - * previous beacon's values to determine # false alarms in the current - * beacon period. - * - * Total number of false alarms = false_alarms + plcp_errs - * - * For OFDM, adjust the following table entries in struct il_sensitivity_cmd - * (notice that the start points for OFDM are at or close to settings for - * maximum sensitivity): - * - * START / MIN / MAX - * HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX 90 / 85 / 120 - * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX 170 / 170 / 210 - * HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX 105 / 105 / 140 - * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX 220 / 220 / 270 - * - * If actual rate of OFDM false alarms (+ plcp_errors) is too high - * (greater than 50 for each 204.8 msecs listening), reduce sensitivity - * by *adding* 1 to all 4 of the table entries above, up to the max for - * each entry. Conversely, if false alarm rate is too low (less than 5 - * for each 204.8 msecs listening), *subtract* 1 from each entry to - * increase sensitivity. - * - * For CCK sensitivity, keep track of the following: - * - * 1). 20-beacon history of maximum background noise, indicated by - * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the - * 3 receivers. For any given beacon, the "silence reference" is - * the maximum of last 60 samples (20 beacons * 3 receivers). - * - * 2). 10-beacon history of strongest signal level, as indicated - * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers, - * i.e. the strength of the signal through the best receiver at the - * moment. These measurements are "upside down", with lower values - * for stronger signals, so max energy will be *minimum* value. - * - * Then for any given beacon, the driver must determine the *weakest* - * of the strongest signals; this is the minimum level that needs to be - * successfully detected, when using the best receiver at the moment. - * "Max cck energy" is the maximum (higher value means lower energy!) - * of the last 10 minima. Once this is determined, driver must add - * a little margin by adding "6" to it. - * - * 3). Number of consecutive beacon periods with too few false alarms. - * Reset this to 0 at the first beacon period that falls within the - * "good" range (5 to 50 false alarms per 204.8 milliseconds rx). - * - * Then, adjust the following CCK table entries in struct il_sensitivity_cmd - * (notice that the start points for CCK are at maximum sensitivity): - * - * START / MIN / MAX - * HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX 125 / 125 / 200 - * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX 200 / 200 / 400 - * HD_MIN_ENERGY_CCK_DET_IDX 100 / 0 / 100 - * - * If actual rate of CCK false alarms (+ plcp_errors) is too high - * (greater than 50 for each 204.8 msecs listening), method for reducing - * sensitivity is: - * - * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX, - * up to max 400. - * - * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX is < 160, - * sensitivity has been reduced a significant amount; bring it up to - * a moderate 161. Otherwise, *add* 3, up to max 200. - * - * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX is > 160, - * sensitivity has been reduced only a moderate or small amount; - * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_IDX, - * down to min 0. Otherwise (if gain has been significantly reduced), - * don't change the HD_MIN_ENERGY_CCK_DET_IDX value. - * - * b) Save a snapshot of the "silence reference". - * - * If actual rate of CCK false alarms (+ plcp_errors) is too low - * (less than 5 for each 204.8 msecs listening), method for increasing - * sensitivity is used only if: - * - * 1a) Previous beacon did not have too many false alarms - * 1b) AND difference between previous "silence reference" and current - * "silence reference" (prev - current) is 2 or more, - * OR 2) 100 or more consecutive beacon periods have had rate of - * less than 5 false alarms per 204.8 milliseconds rx time. - * - * Method for increasing sensitivity: - * - * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX, - * down to min 125. - * - * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX, - * down to min 200. - * - * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_IDX, up to max 100. - * - * If actual rate of CCK false alarms (+ plcp_errors) is within good range - * (between 5 and 50 for each 204.8 msecs listening): - * - * 1) Save a snapshot of the silence reference. - * - * 2) If previous beacon had too many CCK false alarms (+ plcp_errors), - * give some extra margin to energy threshold by *subtracting* 8 - * from value in HD_MIN_ENERGY_CCK_DET_IDX. - * - * For all cases (too few, too many, good range), make sure that the CCK - * detection threshold (energy) is below the energy level for robust - * detection over the past 10 beacon periods, the "Max cck energy". - * Lower values mean higher energy; this means making sure that the value - * in HD_MIN_ENERGY_CCK_DET_IDX is at or *above* "Max cck energy". - * - */ - -/* - * Table entries in C_SENSITIVITY (struct il_sensitivity_cmd) - */ -#define HD_TBL_SIZE (11) /* number of entries */ -#define HD_MIN_ENERGY_CCK_DET_IDX (0) /* table idxes */ -#define HD_MIN_ENERGY_OFDM_DET_IDX (1) -#define HD_AUTO_CORR32_X1_TH_ADD_MIN_IDX (2) -#define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_IDX (3) -#define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_IDX (4) -#define HD_AUTO_CORR32_X4_TH_ADD_MIN_IDX (5) -#define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_IDX (6) -#define HD_BARKER_CORR_TH_ADD_MIN_IDX (7) -#define HD_BARKER_CORR_TH_ADD_MIN_MRC_IDX (8) -#define HD_AUTO_CORR40_X4_TH_ADD_MIN_IDX (9) -#define HD_OFDM_ENERGY_TH_IN_IDX (10) - -/* Control field in struct il_sensitivity_cmd */ -#define C_SENSITIVITY_CONTROL_DEFAULT_TBL cpu_to_le16(0) -#define C_SENSITIVITY_CONTROL_WORK_TBL cpu_to_le16(1) - -/** - * struct il_sensitivity_cmd - * @control: (1) updates working table, (0) updates default table - * @table: energy threshold values, use HD_* as idx into table - * - * Always use "1" in "control" to update uCode's working table and DSP. - */ -struct il_sensitivity_cmd { - __le16 control; /* always use "1" */ - __le16 table[HD_TBL_SIZE]; /* use HD_* as idx */ -} __packed; - -/** - * C_PHY_CALIBRATION = 0xb0 (command, has simple generic response) - * - * This command sets the relative gains of 4965 device's 3 radio receiver chains. - * - * After the first association, driver should accumulate signal and noise - * stats from the N_STATSs that follow the first 20 - * beacons from the associated network (don't collect stats that come - * in from scanning, or any other non-network source). - * - * DISCONNECTED ANTENNA: - * - * Driver should determine which antennas are actually connected, by comparing - * average beacon signal levels for the 3 Rx chains. Accumulate (add) the - * following values over 20 beacons, one accumulator for each of the chains - * a/b/c, from struct stats_rx_non_phy: - * - * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB) - * - * Find the strongest signal from among a/b/c. Compare the other two to the - * strongest. If any signal is more than 15 dB (times 20, unless you - * divide the accumulated values by 20) below the strongest, the driver - * considers that antenna to be disconnected, and should not try to use that - * antenna/chain for Rx or Tx. If both A and B seem to be disconnected, - * driver should declare the stronger one as connected, and attempt to use it - * (A and B are the only 2 Tx chains!). - * - * - * RX BALANCE: - * - * Driver should balance the 3 receivers (but just the ones that are connected - * to antennas, see above) for gain, by comparing the average signal levels - * detected during the silence after each beacon (background noise). - * Accumulate (add) the following values over 20 beacons, one accumulator for - * each of the chains a/b/c, from struct stats_rx_non_phy: - * - * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB) - * - * Find the weakest background noise level from among a/b/c. This Rx chain - * will be the reference, with 0 gain adjustment. Attenuate other channels by - * finding noise difference: - * - * (accum_noise[i] - accum_noise[reference]) / 30 - * - * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB. - * For use in diff_gain_[abc] fields of struct il_calibration_cmd, the - * driver should limit the difference results to a range of 0-3 (0-4.5 dB), - * and set bit 2 to indicate "reduce gain". The value for the reference - * (weakest) chain should be "0". - * - * diff_gain_[abc] bit fields: - * 2: (1) reduce gain, (0) increase gain - * 1-0: amount of gain, units of 1.5 dB - */ - -/* Phy calibration command for series */ -/* The default calibrate table size if not specified by firmware */ -#define IL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18 -enum { - IL_PHY_CALIBRATE_DIFF_GAIN_CMD = 7, - IL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE = 19, -}; - -#define IL_MAX_PHY_CALIBRATE_TBL_SIZE (253) - -struct il_calib_hdr { - u8 op_code; - u8 first_group; - u8 groups_num; - u8 data_valid; -} __packed; - -/* IL_PHY_CALIBRATE_DIFF_GAIN_CMD (7) */ -struct il_calib_diff_gain_cmd { - struct il_calib_hdr hdr; - s8 diff_gain_a; /* see above */ - s8 diff_gain_b; - s8 diff_gain_c; - u8 reserved1; -} __packed; - -/****************************************************************************** - * (12) - * Miscellaneous Commands: - * - *****************************************************************************/ - -/* - * LEDs Command & Response - * C_LEDS = 0x48 (command, has simple generic response) - * - * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field), - * this command turns it on or off, or sets up a periodic blinking cycle. - */ -struct il_led_cmd { - __le32 interval; /* "interval" in uSec */ - u8 id; /* 1: Activity, 2: Link, 3: Tech */ - u8 off; /* # intervals off while blinking; - * "0", with >0 "on" value, turns LED on */ - u8 on; /* # intervals on while blinking; - * "0", regardless of "off", turns LED off */ - u8 reserved; -} __packed; - -/****************************************************************************** - * (13) - * Union of all expected notifications/responses: - * - *****************************************************************************/ - -#define IL_RX_FRAME_SIZE_MSK 0x00003fff - -struct il_rx_pkt { - /* - * The first 4 bytes of the RX frame header contain both the RX frame - * size and some flags. - * Bit fields: - * 31: flag flush RB request - * 30: flag ignore TC (terminal counter) request - * 29: flag fast IRQ request - * 28-14: Reserved - * 13-00: RX frame size - */ - __le32 len_n_flags; - struct il_cmd_header hdr; - union { - struct il3945_rx_frame rx_frame; - struct il3945_tx_resp tx_resp; - struct il3945_beacon_notif beacon_status; - - struct il_alive_resp alive_frame; - struct il_spectrum_notification spectrum_notif; - struct il_csa_notification csa_notif; - struct il_error_resp err_resp; - struct il_card_state_notif card_state_notif; - struct il_add_sta_resp add_sta; - struct il_rem_sta_resp rem_sta; - struct il_sleep_notification sleep_notif; - struct il_spectrum_resp spectrum; - struct il_notif_stats stats; - struct il_compressed_ba_resp compressed_ba; - struct il_missed_beacon_notif missed_beacon; - __le32 status; - u8 raw[0]; - } u; -} __packed; - -#endif /* __il_commands_h__ */ diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c deleted file mode 100644 index 887114582583..000000000000 --- a/drivers/net/wireless/iwlegacy/common.c +++ /dev/null @@ -1,5586 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" - -int -_il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout) -{ - const int interval = 10; /* microseconds */ - int t = 0; - - do { - if ((_il_rd(il, addr) & mask) == (bits & mask)) - return t; - udelay(interval); - t += interval; - } while (t < timeout); - - return -ETIMEDOUT; -} -EXPORT_SYMBOL(_il_poll_bit); - -void -il_set_bit(struct il_priv *p, u32 r, u32 m) -{ - unsigned long reg_flags; - - spin_lock_irqsave(&p->reg_lock, reg_flags); - _il_set_bit(p, r, m); - spin_unlock_irqrestore(&p->reg_lock, reg_flags); -} -EXPORT_SYMBOL(il_set_bit); - -void -il_clear_bit(struct il_priv *p, u32 r, u32 m) -{ - unsigned long reg_flags; - - spin_lock_irqsave(&p->reg_lock, reg_flags); - _il_clear_bit(p, r, m); - spin_unlock_irqrestore(&p->reg_lock, reg_flags); -} -EXPORT_SYMBOL(il_clear_bit); - -bool -_il_grab_nic_access(struct il_priv *il) -{ - int ret; - u32 val; - - /* this bit wakes up the NIC */ - _il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - - /* - * These bits say the device is running, and should keep running for - * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), - * but they do not indicate that embedded SRAM is restored yet; - * 3945 and 4965 have volatile SRAM, and must save/restore contents - * to/from host DRAM when sleeping/waking for power-saving. - * Each direction takes approximately 1/4 millisecond; with this - * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a - * series of register accesses are expected (e.g. reading Event Log), - * to keep device from sleeping. - * - * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that - * SRAM is okay/restored. We don't check that here because this call - * is just for hardware register access; but GP1 MAC_SLEEP check is a - * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). - * - */ - ret = - _il_poll_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, - (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | - CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); - if (unlikely(ret < 0)) { - val = _il_rd(il, CSR_GP_CNTRL); - WARN_ONCE(1, "Timeout waiting for ucode processor access " - "(CSR_GP_CNTRL 0x%08x)\n", val); - _il_wr(il, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); - return false; - } - - return true; -} -EXPORT_SYMBOL_GPL(_il_grab_nic_access); - -int -il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout) -{ - const int interval = 10; /* microseconds */ - int t = 0; - - do { - if ((il_rd(il, addr) & mask) == mask) - return t; - udelay(interval); - t += interval; - } while (t < timeout); - - return -ETIMEDOUT; -} -EXPORT_SYMBOL(il_poll_bit); - -u32 -il_rd_prph(struct il_priv *il, u32 reg) -{ - unsigned long reg_flags; - u32 val; - - spin_lock_irqsave(&il->reg_lock, reg_flags); - _il_grab_nic_access(il); - val = _il_rd_prph(il, reg); - _il_release_nic_access(il); - spin_unlock_irqrestore(&il->reg_lock, reg_flags); - return val; -} -EXPORT_SYMBOL(il_rd_prph); - -void -il_wr_prph(struct il_priv *il, u32 addr, u32 val) -{ - unsigned long reg_flags; - - spin_lock_irqsave(&il->reg_lock, reg_flags); - if (likely(_il_grab_nic_access(il))) { - _il_wr_prph(il, addr, val); - _il_release_nic_access(il); - } - spin_unlock_irqrestore(&il->reg_lock, reg_flags); -} -EXPORT_SYMBOL(il_wr_prph); - -u32 -il_read_targ_mem(struct il_priv *il, u32 addr) -{ - unsigned long reg_flags; - u32 value; - - spin_lock_irqsave(&il->reg_lock, reg_flags); - _il_grab_nic_access(il); - - _il_wr(il, HBUS_TARG_MEM_RADDR, addr); - value = _il_rd(il, HBUS_TARG_MEM_RDAT); - - _il_release_nic_access(il); - spin_unlock_irqrestore(&il->reg_lock, reg_flags); - return value; -} -EXPORT_SYMBOL(il_read_targ_mem); - -void -il_write_targ_mem(struct il_priv *il, u32 addr, u32 val) -{ - unsigned long reg_flags; - - spin_lock_irqsave(&il->reg_lock, reg_flags); - if (likely(_il_grab_nic_access(il))) { - _il_wr(il, HBUS_TARG_MEM_WADDR, addr); - _il_wr(il, HBUS_TARG_MEM_WDAT, val); - _il_release_nic_access(il); - } - spin_unlock_irqrestore(&il->reg_lock, reg_flags); -} -EXPORT_SYMBOL(il_write_targ_mem); - -const char * -il_get_cmd_string(u8 cmd) -{ - switch (cmd) { - IL_CMD(N_ALIVE); - IL_CMD(N_ERROR); - IL_CMD(C_RXON); - IL_CMD(C_RXON_ASSOC); - IL_CMD(C_QOS_PARAM); - IL_CMD(C_RXON_TIMING); - IL_CMD(C_ADD_STA); - IL_CMD(C_REM_STA); - IL_CMD(C_WEPKEY); - IL_CMD(N_3945_RX); - IL_CMD(C_TX); - IL_CMD(C_RATE_SCALE); - IL_CMD(C_LEDS); - IL_CMD(C_TX_LINK_QUALITY_CMD); - IL_CMD(C_CHANNEL_SWITCH); - IL_CMD(N_CHANNEL_SWITCH); - IL_CMD(C_SPECTRUM_MEASUREMENT); - IL_CMD(N_SPECTRUM_MEASUREMENT); - IL_CMD(C_POWER_TBL); - IL_CMD(N_PM_SLEEP); - IL_CMD(N_PM_DEBUG_STATS); - IL_CMD(C_SCAN); - IL_CMD(C_SCAN_ABORT); - IL_CMD(N_SCAN_START); - IL_CMD(N_SCAN_RESULTS); - IL_CMD(N_SCAN_COMPLETE); - IL_CMD(N_BEACON); - IL_CMD(C_TX_BEACON); - IL_CMD(C_TX_PWR_TBL); - IL_CMD(C_BT_CONFIG); - IL_CMD(C_STATS); - IL_CMD(N_STATS); - IL_CMD(N_CARD_STATE); - IL_CMD(N_MISSED_BEACONS); - IL_CMD(C_CT_KILL_CONFIG); - IL_CMD(C_SENSITIVITY); - IL_CMD(C_PHY_CALIBRATION); - IL_CMD(N_RX_PHY); - IL_CMD(N_RX_MPDU); - IL_CMD(N_RX); - IL_CMD(N_COMPRESSED_BA); - default: - return "UNKNOWN"; - - } -} -EXPORT_SYMBOL(il_get_cmd_string); - -#define HOST_COMPLETE_TIMEOUT (HZ / 2) - -static void -il_generic_cmd_callback(struct il_priv *il, struct il_device_cmd *cmd, - struct il_rx_pkt *pkt) -{ - if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { - IL_ERR("Bad return from %s (0x%08X)\n", - il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); - return; - } -#ifdef CONFIG_IWLEGACY_DEBUG - switch (cmd->hdr.cmd) { - case C_TX_LINK_QUALITY_CMD: - case C_SENSITIVITY: - D_HC_DUMP("back from %s (0x%08X)\n", - il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); - break; - default: - D_HC("back from %s (0x%08X)\n", il_get_cmd_string(cmd->hdr.cmd), - pkt->hdr.flags); - } -#endif -} - -static int -il_send_cmd_async(struct il_priv *il, struct il_host_cmd *cmd) -{ - int ret; - - BUG_ON(!(cmd->flags & CMD_ASYNC)); - - /* An asynchronous command can not expect an SKB to be set. */ - BUG_ON(cmd->flags & CMD_WANT_SKB); - - /* Assign a generic callback if one is not provided */ - if (!cmd->callback) - cmd->callback = il_generic_cmd_callback; - - if (test_bit(S_EXIT_PENDING, &il->status)) - return -EBUSY; - - ret = il_enqueue_hcmd(il, cmd); - if (ret < 0) { - IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n", - il_get_cmd_string(cmd->id), ret); - return ret; - } - return 0; -} - -int -il_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd) -{ - int cmd_idx; - int ret; - - lockdep_assert_held(&il->mutex); - - BUG_ON(cmd->flags & CMD_ASYNC); - - /* A synchronous command can not have a callback set. */ - BUG_ON(cmd->callback); - - D_INFO("Attempting to send sync command %s\n", - il_get_cmd_string(cmd->id)); - - set_bit(S_HCMD_ACTIVE, &il->status); - D_INFO("Setting HCMD_ACTIVE for command %s\n", - il_get_cmd_string(cmd->id)); - - cmd_idx = il_enqueue_hcmd(il, cmd); - if (cmd_idx < 0) { - ret = cmd_idx; - IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n", - il_get_cmd_string(cmd->id), ret); - goto out; - } - - ret = wait_event_timeout(il->wait_command_queue, - !test_bit(S_HCMD_ACTIVE, &il->status), - HOST_COMPLETE_TIMEOUT); - if (!ret) { - if (test_bit(S_HCMD_ACTIVE, &il->status)) { - IL_ERR("Error sending %s: time out after %dms.\n", - il_get_cmd_string(cmd->id), - jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); - - clear_bit(S_HCMD_ACTIVE, &il->status); - D_INFO("Clearing HCMD_ACTIVE for command %s\n", - il_get_cmd_string(cmd->id)); - ret = -ETIMEDOUT; - goto cancel; - } - } - - if (test_bit(S_RFKILL, &il->status)) { - IL_ERR("Command %s aborted: RF KILL Switch\n", - il_get_cmd_string(cmd->id)); - ret = -ECANCELED; - goto fail; - } - if (test_bit(S_FW_ERROR, &il->status)) { - IL_ERR("Command %s failed: FW Error\n", - il_get_cmd_string(cmd->id)); - ret = -EIO; - goto fail; - } - if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) { - IL_ERR("Error: Response NULL in '%s'\n", - il_get_cmd_string(cmd->id)); - ret = -EIO; - goto cancel; - } - - ret = 0; - goto out; - -cancel: - if (cmd->flags & CMD_WANT_SKB) { - /* - * Cancel the CMD_WANT_SKB flag for the cmd in the - * TX cmd queue. Otherwise in case the cmd comes - * in later, it will possibly set an invalid - * address (cmd->meta.source). - */ - il->txq[il->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB; - } -fail: - if (cmd->reply_page) { - il_free_pages(il, cmd->reply_page); - cmd->reply_page = 0; - } -out: - return ret; -} -EXPORT_SYMBOL(il_send_cmd_sync); - -int -il_send_cmd(struct il_priv *il, struct il_host_cmd *cmd) -{ - if (cmd->flags & CMD_ASYNC) - return il_send_cmd_async(il, cmd); - - return il_send_cmd_sync(il, cmd); -} -EXPORT_SYMBOL(il_send_cmd); - -int -il_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, const void *data) -{ - struct il_host_cmd cmd = { - .id = id, - .len = len, - .data = data, - }; - - return il_send_cmd_sync(il, &cmd); -} -EXPORT_SYMBOL(il_send_cmd_pdu); - -int -il_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data, - void (*callback) (struct il_priv *il, - struct il_device_cmd *cmd, - struct il_rx_pkt *pkt)) -{ - struct il_host_cmd cmd = { - .id = id, - .len = len, - .data = data, - }; - - cmd.flags |= CMD_ASYNC; - cmd.callback = callback; - - return il_send_cmd_async(il, &cmd); -} -EXPORT_SYMBOL(il_send_cmd_pdu_async); - -/* default: IL_LED_BLINK(0) using blinking idx table */ -static int led_mode; -module_param(led_mode, int, S_IRUGO); -MODULE_PARM_DESC(led_mode, - "0=system default, " "1=On(RF On)/Off(RF Off), 2=blinking"); - -/* Throughput OFF time(ms) ON time (ms) - * >300 25 25 - * >200 to 300 40 40 - * >100 to 200 55 55 - * >70 to 100 65 65 - * >50 to 70 75 75 - * >20 to 50 85 85 - * >10 to 20 95 95 - * >5 to 10 110 110 - * >1 to 5 130 130 - * >0 to 1 167 167 - * <=0 SOLID ON - */ -static const struct ieee80211_tpt_blink il_blink[] = { - {.throughput = 0, .blink_time = 334}, - {.throughput = 1 * 1024 - 1, .blink_time = 260}, - {.throughput = 5 * 1024 - 1, .blink_time = 220}, - {.throughput = 10 * 1024 - 1, .blink_time = 190}, - {.throughput = 20 * 1024 - 1, .blink_time = 170}, - {.throughput = 50 * 1024 - 1, .blink_time = 150}, - {.throughput = 70 * 1024 - 1, .blink_time = 130}, - {.throughput = 100 * 1024 - 1, .blink_time = 110}, - {.throughput = 200 * 1024 - 1, .blink_time = 80}, - {.throughput = 300 * 1024 - 1, .blink_time = 50}, -}; - -/* - * Adjust led blink rate to compensate on a MAC Clock difference on every HW - * Led blink rate analysis showed an average deviation of 0% on 3945, - * 5% on 4965 HW. - * Need to compensate on the led on/off time per HW according to the deviation - * to achieve the desired led frequency - * The calculation is: (100-averageDeviation)/100 * blinkTime - * For code efficiency the calculation will be: - * compensation = (100 - averageDeviation) * 64 / 100 - * NewBlinkTime = (compensation * BlinkTime) / 64 - */ -static inline u8 -il_blink_compensation(struct il_priv *il, u8 time, u16 compensation) -{ - if (!compensation) { - IL_ERR("undefined blink compensation: " - "use pre-defined blinking time\n"); - return time; - } - - return (u8) ((time * compensation) >> 6); -} - -/* Set led pattern command */ -static int -il_led_cmd(struct il_priv *il, unsigned long on, unsigned long off) -{ - struct il_led_cmd led_cmd = { - .id = IL_LED_LINK, - .interval = IL_DEF_LED_INTRVL - }; - int ret; - - if (!test_bit(S_READY, &il->status)) - return -EBUSY; - - if (il->blink_on == on && il->blink_off == off) - return 0; - - if (off == 0) { - /* led is SOLID_ON */ - on = IL_LED_SOLID; - } - - D_LED("Led blink time compensation=%u\n", - il->cfg->led_compensation); - led_cmd.on = - il_blink_compensation(il, on, - il->cfg->led_compensation); - led_cmd.off = - il_blink_compensation(il, off, - il->cfg->led_compensation); - - ret = il->ops->send_led_cmd(il, &led_cmd); - if (!ret) { - il->blink_on = on; - il->blink_off = off; - } - return ret; -} - -static void -il_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct il_priv *il = container_of(led_cdev, struct il_priv, led); - unsigned long on = 0; - - if (brightness > 0) - on = IL_LED_SOLID; - - il_led_cmd(il, on, 0); -} - -static int -il_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, - unsigned long *delay_off) -{ - struct il_priv *il = container_of(led_cdev, struct il_priv, led); - - return il_led_cmd(il, *delay_on, *delay_off); -} - -void -il_leds_init(struct il_priv *il) -{ - int mode = led_mode; - int ret; - - if (mode == IL_LED_DEFAULT) - mode = il->cfg->led_mode; - - il->led.name = - kasprintf(GFP_KERNEL, "%s-led", wiphy_name(il->hw->wiphy)); - il->led.brightness_set = il_led_brightness_set; - il->led.blink_set = il_led_blink_set; - il->led.max_brightness = 1; - - switch (mode) { - case IL_LED_DEFAULT: - WARN_ON(1); - break; - case IL_LED_BLINK: - il->led.default_trigger = - ieee80211_create_tpt_led_trigger(il->hw, - IEEE80211_TPT_LEDTRIG_FL_CONNECTED, - il_blink, - ARRAY_SIZE(il_blink)); - break; - case IL_LED_RF_STATE: - il->led.default_trigger = ieee80211_get_radio_led_name(il->hw); - break; - } - - ret = led_classdev_register(&il->pci_dev->dev, &il->led); - if (ret) { - kfree(il->led.name); - return; - } - - il->led_registered = true; -} -EXPORT_SYMBOL(il_leds_init); - -void -il_leds_exit(struct il_priv *il) -{ - if (!il->led_registered) - return; - - led_classdev_unregister(&il->led); - kfree(il->led.name); -} -EXPORT_SYMBOL(il_leds_exit); - -/************************** EEPROM BANDS **************************** - * - * The il_eeprom_band definitions below provide the mapping from the - * EEPROM contents to the specific channel number supported for each - * band. - * - * For example, il_priv->eeprom.band_3_channels[4] from the band_3 - * definition below maps to physical channel 42 in the 5.2GHz spectrum. - * The specific geography and calibration information for that channel - * is contained in the eeprom map itself. - * - * During init, we copy the eeprom information and channel map - * information into il->channel_info_24/52 and il->channel_map_24/52 - * - * channel_map_24/52 provides the idx in the channel_info array for a - * given channel. We have to have two separate maps as there is channel - * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and - * band_2 - * - * A value of 0xff stored in the channel_map indicates that the channel - * is not supported by the hardware at all. - * - * A value of 0xfe in the channel_map indicates that the channel is not - * valid for Tx with the current hardware. This means that - * while the system can tune and receive on a given channel, it may not - * be able to associate or transmit any frames on that - * channel. There is no corresponding channel information for that - * entry. - * - *********************************************************************/ - -/* 2.4 GHz */ -const u8 il_eeprom_band_1[14] = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 -}; - -/* 5.2 GHz bands */ -static const u8 il_eeprom_band_2[] = { /* 4915-5080MHz */ - 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 -}; - -static const u8 il_eeprom_band_3[] = { /* 5170-5320MHz */ - 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 -}; - -static const u8 il_eeprom_band_4[] = { /* 5500-5700MHz */ - 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 -}; - -static const u8 il_eeprom_band_5[] = { /* 5725-5825MHz */ - 145, 149, 153, 157, 161, 165 -}; - -static const u8 il_eeprom_band_6[] = { /* 2.4 ht40 channel */ - 1, 2, 3, 4, 5, 6, 7 -}; - -static const u8 il_eeprom_band_7[] = { /* 5.2 ht40 channel */ - 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 -}; - -/****************************************************************************** - * - * EEPROM related functions - * -******************************************************************************/ - -static int -il_eeprom_verify_signature(struct il_priv *il) -{ - u32 gp = _il_rd(il, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; - int ret = 0; - - D_EEPROM("EEPROM signature=0x%08x\n", gp); - switch (gp) { - case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: - case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: - break; - default: - IL_ERR("bad EEPROM signature," "EEPROM_GP=0x%08x\n", gp); - ret = -ENOENT; - break; - } - return ret; -} - -const u8 * -il_eeprom_query_addr(const struct il_priv *il, size_t offset) -{ - BUG_ON(offset >= il->cfg->eeprom_size); - return &il->eeprom[offset]; -} -EXPORT_SYMBOL(il_eeprom_query_addr); - -u16 -il_eeprom_query16(const struct il_priv *il, size_t offset) -{ - if (!il->eeprom) - return 0; - return (u16) il->eeprom[offset] | ((u16) il->eeprom[offset + 1] << 8); -} -EXPORT_SYMBOL(il_eeprom_query16); - -/** - * il_eeprom_init - read EEPROM contents - * - * Load the EEPROM contents from adapter into il->eeprom - * - * NOTE: This routine uses the non-debug IO access functions. - */ -int -il_eeprom_init(struct il_priv *il) -{ - __le16 *e; - u32 gp = _il_rd(il, CSR_EEPROM_GP); - int sz; - int ret; - u16 addr; - - /* allocate eeprom */ - sz = il->cfg->eeprom_size; - D_EEPROM("NVM size = %d\n", sz); - il->eeprom = kzalloc(sz, GFP_KERNEL); - if (!il->eeprom) { - ret = -ENOMEM; - goto alloc_err; - } - e = (__le16 *) il->eeprom; - - il->ops->apm_init(il); - - ret = il_eeprom_verify_signature(il); - if (ret < 0) { - IL_ERR("EEPROM not found, EEPROM_GP=0x%08x\n", gp); - ret = -ENOENT; - goto err; - } - - /* Make sure driver (instead of uCode) is allowed to read EEPROM */ - ret = il->ops->eeprom_acquire_semaphore(il); - if (ret < 0) { - IL_ERR("Failed to acquire EEPROM semaphore.\n"); - ret = -ENOENT; - goto err; - } - - /* eeprom is an array of 16bit values */ - for (addr = 0; addr < sz; addr += sizeof(u16)) { - u32 r; - - _il_wr(il, CSR_EEPROM_REG, - CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - - ret = - _il_poll_bit(il, CSR_EEPROM_REG, - CSR_EEPROM_REG_READ_VALID_MSK, - CSR_EEPROM_REG_READ_VALID_MSK, - IL_EEPROM_ACCESS_TIMEOUT); - if (ret < 0) { - IL_ERR("Time out reading EEPROM[%d]\n", addr); - goto done; - } - r = _il_rd(il, CSR_EEPROM_REG); - e[addr / 2] = cpu_to_le16(r >> 16); - } - - D_EEPROM("NVM Type: %s, version: 0x%x\n", "EEPROM", - il_eeprom_query16(il, EEPROM_VERSION)); - - ret = 0; -done: - il->ops->eeprom_release_semaphore(il); - -err: - if (ret) - il_eeprom_free(il); - /* Reset chip to save power until we load uCode during "up". */ - il_apm_stop(il); -alloc_err: - return ret; -} -EXPORT_SYMBOL(il_eeprom_init); - -void -il_eeprom_free(struct il_priv *il) -{ - kfree(il->eeprom); - il->eeprom = NULL; -} -EXPORT_SYMBOL(il_eeprom_free); - -static void -il_init_band_reference(const struct il_priv *il, int eep_band, - int *eeprom_ch_count, - const struct il_eeprom_channel **eeprom_ch_info, - const u8 **eeprom_ch_idx) -{ - u32 offset = il->cfg->regulatory_bands[eep_band - 1]; - - switch (eep_band) { - case 1: /* 2.4GHz band */ - *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_1); - *eeprom_ch_info = - (struct il_eeprom_channel *)il_eeprom_query_addr(il, - offset); - *eeprom_ch_idx = il_eeprom_band_1; - break; - case 2: /* 4.9GHz band */ - *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_2); - *eeprom_ch_info = - (struct il_eeprom_channel *)il_eeprom_query_addr(il, - offset); - *eeprom_ch_idx = il_eeprom_band_2; - break; - case 3: /* 5.2GHz band */ - *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_3); - *eeprom_ch_info = - (struct il_eeprom_channel *)il_eeprom_query_addr(il, - offset); - *eeprom_ch_idx = il_eeprom_band_3; - break; - case 4: /* 5.5GHz band */ - *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_4); - *eeprom_ch_info = - (struct il_eeprom_channel *)il_eeprom_query_addr(il, - offset); - *eeprom_ch_idx = il_eeprom_band_4; - break; - case 5: /* 5.7GHz band */ - *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_5); - *eeprom_ch_info = - (struct il_eeprom_channel *)il_eeprom_query_addr(il, - offset); - *eeprom_ch_idx = il_eeprom_band_5; - break; - case 6: /* 2.4GHz ht40 channels */ - *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_6); - *eeprom_ch_info = - (struct il_eeprom_channel *)il_eeprom_query_addr(il, - offset); - *eeprom_ch_idx = il_eeprom_band_6; - break; - case 7: /* 5 GHz ht40 channels */ - *eeprom_ch_count = ARRAY_SIZE(il_eeprom_band_7); - *eeprom_ch_info = - (struct il_eeprom_channel *)il_eeprom_query_addr(il, - offset); - *eeprom_ch_idx = il_eeprom_band_7; - break; - default: - BUG(); - } -} - -#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \ - ? # x " " : "") -/** - * il_mod_ht40_chan_info - Copy ht40 channel info into driver's il. - * - * Does not set up a command, or touch hardware. - */ -static int -il_mod_ht40_chan_info(struct il_priv *il, enum ieee80211_band band, u16 channel, - const struct il_eeprom_channel *eeprom_ch, - u8 clear_ht40_extension_channel) -{ - struct il_channel_info *ch_info; - - ch_info = - (struct il_channel_info *)il_get_channel_info(il, band, channel); - - if (!il_is_channel_valid(ch_info)) - return -1; - - D_EEPROM("HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" - " Ad-Hoc %ssupported\n", ch_info->channel, - il_is_channel_a_band(ch_info) ? "5.2" : "2.4", - CHECK_AND_PRINT(IBSS), CHECK_AND_PRINT(ACTIVE), - CHECK_AND_PRINT(RADAR), CHECK_AND_PRINT(WIDE), - CHECK_AND_PRINT(DFS), eeprom_ch->flags, - eeprom_ch->max_power_avg, - ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) && - !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); - - ch_info->ht40_eeprom = *eeprom_ch; - ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; - ch_info->ht40_flags = eeprom_ch->flags; - if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) - ch_info->ht40_extension_channel &= - ~clear_ht40_extension_channel; - - return 0; -} - -#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \ - ? # x " " : "") - -/** - * il_init_channel_map - Set up driver's info for all possible channels - */ -int -il_init_channel_map(struct il_priv *il) -{ - int eeprom_ch_count = 0; - const u8 *eeprom_ch_idx = NULL; - const struct il_eeprom_channel *eeprom_ch_info = NULL; - int band, ch; - struct il_channel_info *ch_info; - - if (il->channel_count) { - D_EEPROM("Channel map already initialized.\n"); - return 0; - } - - D_EEPROM("Initializing regulatory info from EEPROM\n"); - - il->channel_count = - ARRAY_SIZE(il_eeprom_band_1) + ARRAY_SIZE(il_eeprom_band_2) + - ARRAY_SIZE(il_eeprom_band_3) + ARRAY_SIZE(il_eeprom_band_4) + - ARRAY_SIZE(il_eeprom_band_5); - - D_EEPROM("Parsing data for %d channels.\n", il->channel_count); - - il->channel_info = - kzalloc(sizeof(struct il_channel_info) * il->channel_count, - GFP_KERNEL); - if (!il->channel_info) { - IL_ERR("Could not allocate channel_info\n"); - il->channel_count = 0; - return -ENOMEM; - } - - ch_info = il->channel_info; - - /* Loop through the 5 EEPROM bands adding them in order to the - * channel map we maintain (that contains additional information than - * what just in the EEPROM) */ - for (band = 1; band <= 5; band++) { - - il_init_band_reference(il, band, &eeprom_ch_count, - &eeprom_ch_info, &eeprom_ch_idx); - - /* Loop through each band adding each of the channels */ - for (ch = 0; ch < eeprom_ch_count; ch++) { - ch_info->channel = eeprom_ch_idx[ch]; - ch_info->band = - (band == - 1) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - - /* permanently store EEPROM's channel regulatory flags - * and max power in channel info database. */ - ch_info->eeprom = eeprom_ch_info[ch]; - - /* Copy the run-time flags so they are there even on - * invalid channels */ - ch_info->flags = eeprom_ch_info[ch].flags; - /* First write that ht40 is not enabled, and then enable - * one by one */ - ch_info->ht40_extension_channel = - IEEE80211_CHAN_NO_HT40; - - if (!(il_is_channel_valid(ch_info))) { - D_EEPROM("Ch. %d Flags %x [%sGHz] - " - "No traffic\n", ch_info->channel, - ch_info->flags, - il_is_channel_a_band(ch_info) ? "5.2" : - "2.4"); - ch_info++; - continue; - } - - /* Initialize regulatory-based run-time data */ - ch_info->max_power_avg = ch_info->curr_txpow = - eeprom_ch_info[ch].max_power_avg; - ch_info->scan_power = eeprom_ch_info[ch].max_power_avg; - ch_info->min_power = 0; - - D_EEPROM("Ch. %d [%sGHz] " "%s%s%s%s%s%s(0x%02x %ddBm):" - " Ad-Hoc %ssupported\n", ch_info->channel, - il_is_channel_a_band(ch_info) ? "5.2" : "2.4", - CHECK_AND_PRINT_I(VALID), - CHECK_AND_PRINT_I(IBSS), - CHECK_AND_PRINT_I(ACTIVE), - CHECK_AND_PRINT_I(RADAR), - CHECK_AND_PRINT_I(WIDE), - CHECK_AND_PRINT_I(DFS), - eeprom_ch_info[ch].flags, - eeprom_ch_info[ch].max_power_avg, - ((eeprom_ch_info[ch]. - flags & EEPROM_CHANNEL_IBSS) && - !(eeprom_ch_info[ch]. - flags & EEPROM_CHANNEL_RADAR)) ? "" : - "not "); - - ch_info++; - } - } - - /* Check if we do have HT40 channels */ - if (il->cfg->regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 && - il->cfg->regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40) - return 0; - - /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ - for (band = 6; band <= 7; band++) { - enum ieee80211_band ieeeband; - - il_init_band_reference(il, band, &eeprom_ch_count, - &eeprom_ch_info, &eeprom_ch_idx); - - /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ - ieeeband = - (band == 6) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - - /* Loop through each band adding each of the channels */ - for (ch = 0; ch < eeprom_ch_count; ch++) { - /* Set up driver's info for lower half */ - il_mod_ht40_chan_info(il, ieeeband, eeprom_ch_idx[ch], - &eeprom_ch_info[ch], - IEEE80211_CHAN_NO_HT40PLUS); - - /* Set up driver's info for upper half */ - il_mod_ht40_chan_info(il, ieeeband, - eeprom_ch_idx[ch] + 4, - &eeprom_ch_info[ch], - IEEE80211_CHAN_NO_HT40MINUS); - } - } - - return 0; -} -EXPORT_SYMBOL(il_init_channel_map); - -/* - * il_free_channel_map - undo allocations in il_init_channel_map - */ -void -il_free_channel_map(struct il_priv *il) -{ - kfree(il->channel_info); - il->channel_count = 0; -} -EXPORT_SYMBOL(il_free_channel_map); - -/** - * il_get_channel_info - Find driver's ilate channel info - * - * Based on band and channel number. - */ -const struct il_channel_info * -il_get_channel_info(const struct il_priv *il, enum ieee80211_band band, - u16 channel) -{ - int i; - - switch (band) { - case IEEE80211_BAND_5GHZ: - for (i = 14; i < il->channel_count; i++) { - if (il->channel_info[i].channel == channel) - return &il->channel_info[i]; - } - break; - case IEEE80211_BAND_2GHZ: - if (channel >= 1 && channel <= 14) - return &il->channel_info[channel - 1]; - break; - default: - BUG(); - } - - return NULL; -} -EXPORT_SYMBOL(il_get_channel_info); - -/* - * Setting power level allows the card to go to sleep when not busy. - * - * We calculate a sleep command based on the required latency, which - * we get from mac80211. - */ - -#define SLP_VEC(X0, X1, X2, X3, X4) { \ - cpu_to_le32(X0), \ - cpu_to_le32(X1), \ - cpu_to_le32(X2), \ - cpu_to_le32(X3), \ - cpu_to_le32(X4) \ -} - -static void -il_build_powertable_cmd(struct il_priv *il, struct il_powertable_cmd *cmd) -{ - const __le32 interval[3][IL_POWER_VEC_SIZE] = { - SLP_VEC(2, 2, 4, 6, 0xFF), - SLP_VEC(2, 4, 7, 10, 10), - SLP_VEC(4, 7, 10, 10, 0xFF) - }; - int i, dtim_period, no_dtim; - u32 max_sleep; - bool skip; - - memset(cmd, 0, sizeof(*cmd)); - - if (il->power_data.pci_pm) - cmd->flags |= IL_POWER_PCI_PM_MSK; - - /* if no Power Save, we are done */ - if (il->power_data.ps_disabled) - return; - - cmd->flags = IL_POWER_DRIVER_ALLOW_SLEEP_MSK; - cmd->keep_alive_seconds = 0; - cmd->debug_flags = 0; - cmd->rx_data_timeout = cpu_to_le32(25 * 1024); - cmd->tx_data_timeout = cpu_to_le32(25 * 1024); - cmd->keep_alive_beacons = 0; - - dtim_period = il->vif ? il->vif->bss_conf.dtim_period : 0; - - if (dtim_period <= 2) { - memcpy(cmd->sleep_interval, interval[0], sizeof(interval[0])); - no_dtim = 2; - } else if (dtim_period <= 10) { - memcpy(cmd->sleep_interval, interval[1], sizeof(interval[1])); - no_dtim = 2; - } else { - memcpy(cmd->sleep_interval, interval[2], sizeof(interval[2])); - no_dtim = 0; - } - - if (dtim_period == 0) { - dtim_period = 1; - skip = false; - } else { - skip = !!no_dtim; - } - - if (skip) { - __le32 tmp = cmd->sleep_interval[IL_POWER_VEC_SIZE - 1]; - - max_sleep = le32_to_cpu(tmp); - if (max_sleep == 0xFF) - max_sleep = dtim_period * (skip + 1); - else if (max_sleep > dtim_period) - max_sleep = (max_sleep / dtim_period) * dtim_period; - cmd->flags |= IL_POWER_SLEEP_OVER_DTIM_MSK; - } else { - max_sleep = dtim_period; - cmd->flags &= ~IL_POWER_SLEEP_OVER_DTIM_MSK; - } - - for (i = 0; i < IL_POWER_VEC_SIZE; i++) - if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) - cmd->sleep_interval[i] = cpu_to_le32(max_sleep); -} - -static int -il_set_power(struct il_priv *il, struct il_powertable_cmd *cmd) -{ - D_POWER("Sending power/sleep command\n"); - D_POWER("Flags value = 0x%08X\n", cmd->flags); - D_POWER("Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); - D_POWER("Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); - D_POWER("Sleep interval vector = { %d , %d , %d , %d , %d }\n", - le32_to_cpu(cmd->sleep_interval[0]), - le32_to_cpu(cmd->sleep_interval[1]), - le32_to_cpu(cmd->sleep_interval[2]), - le32_to_cpu(cmd->sleep_interval[3]), - le32_to_cpu(cmd->sleep_interval[4])); - - return il_send_cmd_pdu(il, C_POWER_TBL, - sizeof(struct il_powertable_cmd), cmd); -} - -static int -il_power_set_mode(struct il_priv *il, struct il_powertable_cmd *cmd, bool force) -{ - int ret; - bool update_chains; - - lockdep_assert_held(&il->mutex); - - /* Don't update the RX chain when chain noise calibration is running */ - update_chains = il->chain_noise_data.state == IL_CHAIN_NOISE_DONE || - il->chain_noise_data.state == IL_CHAIN_NOISE_ALIVE; - - if (!memcmp(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) - return 0; - - if (!il_is_ready_rf(il)) - return -EIO; - - /* scan complete use sleep_power_next, need to be updated */ - memcpy(&il->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); - if (test_bit(S_SCANNING, &il->status) && !force) { - D_INFO("Defer power set mode while scanning\n"); - return 0; - } - - if (cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK) - set_bit(S_POWER_PMI, &il->status); - - ret = il_set_power(il, cmd); - if (!ret) { - if (!(cmd->flags & IL_POWER_DRIVER_ALLOW_SLEEP_MSK)) - clear_bit(S_POWER_PMI, &il->status); - - if (il->ops->update_chain_flags && update_chains) - il->ops->update_chain_flags(il); - else if (il->ops->update_chain_flags) - D_POWER("Cannot update the power, chain noise " - "calibration running: %d\n", - il->chain_noise_data.state); - - memcpy(&il->power_data.sleep_cmd, cmd, sizeof(*cmd)); - } else - IL_ERR("set power fail, ret = %d", ret); - - return ret; -} - -int -il_power_update_mode(struct il_priv *il, bool force) -{ - struct il_powertable_cmd cmd; - - il_build_powertable_cmd(il, &cmd); - - return il_power_set_mode(il, &cmd, force); -} -EXPORT_SYMBOL(il_power_update_mode); - -/* initialize to default */ -void -il_power_initialize(struct il_priv *il) -{ - u16 lctl; - - pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl); - il->power_data.pci_pm = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S); - - il->power_data.debug_sleep_level_override = -1; - - memset(&il->power_data.sleep_cmd, 0, sizeof(il->power_data.sleep_cmd)); -} -EXPORT_SYMBOL(il_power_initialize); - -/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after - * sending probe req. This should be set long enough to hear probe responses - * from more than one AP. */ -#define IL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ -#define IL_ACTIVE_DWELL_TIME_52 (20) - -#define IL_ACTIVE_DWELL_FACTOR_24GHZ (3) -#define IL_ACTIVE_DWELL_FACTOR_52GHZ (2) - -/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. - * Must be set longer than active dwell time. - * For the most reliable scan, set > AP beacon interval (typically 100msec). */ -#define IL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ -#define IL_PASSIVE_DWELL_TIME_52 (10) -#define IL_PASSIVE_DWELL_BASE (100) -#define IL_CHANNEL_TUNE_TIME 5 - -static int -il_send_scan_abort(struct il_priv *il) -{ - int ret; - struct il_rx_pkt *pkt; - struct il_host_cmd cmd = { - .id = C_SCAN_ABORT, - .flags = CMD_WANT_SKB, - }; - - /* Exit instantly with error when device is not ready - * to receive scan abort command or it does not perform - * hardware scan currently */ - if (!test_bit(S_READY, &il->status) || - !test_bit(S_GEO_CONFIGURED, &il->status) || - !test_bit(S_SCAN_HW, &il->status) || - test_bit(S_FW_ERROR, &il->status) || - test_bit(S_EXIT_PENDING, &il->status)) - return -EIO; - - ret = il_send_cmd_sync(il, &cmd); - if (ret) - return ret; - - pkt = (struct il_rx_pkt *)cmd.reply_page; - if (pkt->u.status != CAN_ABORT_STATUS) { - /* The scan abort will return 1 for success or - * 2 for "failure". A failure condition can be - * due to simply not being in an active scan which - * can occur if we send the scan abort before we - * the microcode has notified us that a scan is - * completed. */ - D_SCAN("SCAN_ABORT ret %d.\n", pkt->u.status); - ret = -EIO; - } - - il_free_pages(il, cmd.reply_page); - return ret; -} - -static void -il_complete_scan(struct il_priv *il, bool aborted) -{ - /* check if scan was requested from mac80211 */ - if (il->scan_request) { - D_SCAN("Complete scan in mac80211\n"); - ieee80211_scan_completed(il->hw, aborted); - } - - il->scan_vif = NULL; - il->scan_request = NULL; -} - -void -il_force_scan_end(struct il_priv *il) -{ - lockdep_assert_held(&il->mutex); - - if (!test_bit(S_SCANNING, &il->status)) { - D_SCAN("Forcing scan end while not scanning\n"); - return; - } - - D_SCAN("Forcing scan end\n"); - clear_bit(S_SCANNING, &il->status); - clear_bit(S_SCAN_HW, &il->status); - clear_bit(S_SCAN_ABORTING, &il->status); - il_complete_scan(il, true); -} - -static void -il_do_scan_abort(struct il_priv *il) -{ - int ret; - - lockdep_assert_held(&il->mutex); - - if (!test_bit(S_SCANNING, &il->status)) { - D_SCAN("Not performing scan to abort\n"); - return; - } - - if (test_and_set_bit(S_SCAN_ABORTING, &il->status)) { - D_SCAN("Scan abort in progress\n"); - return; - } - - ret = il_send_scan_abort(il); - if (ret) { - D_SCAN("Send scan abort failed %d\n", ret); - il_force_scan_end(il); - } else - D_SCAN("Successfully send scan abort\n"); -} - -/** - * il_scan_cancel - Cancel any currently executing HW scan - */ -int -il_scan_cancel(struct il_priv *il) -{ - D_SCAN("Queuing abort scan\n"); - queue_work(il->workqueue, &il->abort_scan); - return 0; -} -EXPORT_SYMBOL(il_scan_cancel); - -/** - * il_scan_cancel_timeout - Cancel any currently executing HW scan - * @ms: amount of time to wait (in milliseconds) for scan to abort - * - */ -int -il_scan_cancel_timeout(struct il_priv *il, unsigned long ms) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(ms); - - lockdep_assert_held(&il->mutex); - - D_SCAN("Scan cancel timeout\n"); - - il_do_scan_abort(il); - - while (time_before_eq(jiffies, timeout)) { - if (!test_bit(S_SCAN_HW, &il->status)) - break; - msleep(20); - } - - return test_bit(S_SCAN_HW, &il->status); -} -EXPORT_SYMBOL(il_scan_cancel_timeout); - -/* Service response to C_SCAN (0x80) */ -static void -il_hdl_scan(struct il_priv *il, struct il_rx_buf *rxb) -{ -#ifdef CONFIG_IWLEGACY_DEBUG - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_scanreq_notification *notif = - (struct il_scanreq_notification *)pkt->u.raw; - - D_SCAN("Scan request status = 0x%x\n", notif->status); -#endif -} - -/* Service N_SCAN_START (0x82) */ -static void -il_hdl_scan_start(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_scanstart_notification *notif = - (struct il_scanstart_notification *)pkt->u.raw; - il->scan_start_tsf = le32_to_cpu(notif->tsf_low); - D_SCAN("Scan start: " "%d [802.11%s] " - "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", notif->channel, - notif->band ? "bg" : "a", le32_to_cpu(notif->tsf_high), - le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer); -} - -/* Service N_SCAN_RESULTS (0x83) */ -static void -il_hdl_scan_results(struct il_priv *il, struct il_rx_buf *rxb) -{ -#ifdef CONFIG_IWLEGACY_DEBUG - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_scanresults_notification *notif = - (struct il_scanresults_notification *)pkt->u.raw; - - D_SCAN("Scan ch.res: " "%d [802.11%s] " "(TSF: 0x%08X:%08X) - %d " - "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a", - le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low), - le32_to_cpu(notif->stats[0]), - le32_to_cpu(notif->tsf_low) - il->scan_start_tsf); -#endif -} - -/* Service N_SCAN_COMPLETE (0x84) */ -static void -il_hdl_scan_complete(struct il_priv *il, struct il_rx_buf *rxb) -{ - -#ifdef CONFIG_IWLEGACY_DEBUG - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_scancomplete_notification *scan_notif = (void *)pkt->u.raw; -#endif - - D_SCAN("Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", - scan_notif->scanned_channels, scan_notif->tsf_low, - scan_notif->tsf_high, scan_notif->status); - - /* The HW is no longer scanning */ - clear_bit(S_SCAN_HW, &il->status); - - D_SCAN("Scan on %sGHz took %dms\n", - (il->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", - jiffies_to_msecs(jiffies - il->scan_start)); - - queue_work(il->workqueue, &il->scan_completed); -} - -void -il_setup_rx_scan_handlers(struct il_priv *il) -{ - /* scan handlers */ - il->handlers[C_SCAN] = il_hdl_scan; - il->handlers[N_SCAN_START] = il_hdl_scan_start; - il->handlers[N_SCAN_RESULTS] = il_hdl_scan_results; - il->handlers[N_SCAN_COMPLETE] = il_hdl_scan_complete; -} -EXPORT_SYMBOL(il_setup_rx_scan_handlers); - -u16 -il_get_active_dwell_time(struct il_priv *il, enum ieee80211_band band, - u8 n_probes) -{ - if (band == IEEE80211_BAND_5GHZ) - return IL_ACTIVE_DWELL_TIME_52 + - IL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); - else - return IL_ACTIVE_DWELL_TIME_24 + - IL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); -} -EXPORT_SYMBOL(il_get_active_dwell_time); - -u16 -il_get_passive_dwell_time(struct il_priv *il, enum ieee80211_band band, - struct ieee80211_vif *vif) -{ - u16 value; - - u16 passive = - (band == - IEEE80211_BAND_2GHZ) ? IL_PASSIVE_DWELL_BASE + - IL_PASSIVE_DWELL_TIME_24 : IL_PASSIVE_DWELL_BASE + - IL_PASSIVE_DWELL_TIME_52; - - if (il_is_any_associated(il)) { - /* - * If we're associated, we clamp the maximum passive - * dwell time to be 98% of the smallest beacon interval - * (minus 2 * channel tune time) - */ - value = il->vif ? il->vif->bss_conf.beacon_int : 0; - if (value > IL_PASSIVE_DWELL_BASE || !value) - value = IL_PASSIVE_DWELL_BASE; - value = (value * 98) / 100 - IL_CHANNEL_TUNE_TIME * 2; - passive = min(value, passive); - } - - return passive; -} -EXPORT_SYMBOL(il_get_passive_dwell_time); - -void -il_init_scan_params(struct il_priv *il) -{ - u8 ant_idx = fls(il->hw_params.valid_tx_ant) - 1; - if (!il->scan_tx_ant[IEEE80211_BAND_5GHZ]) - il->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx; - if (!il->scan_tx_ant[IEEE80211_BAND_2GHZ]) - il->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; -} -EXPORT_SYMBOL(il_init_scan_params); - -static int -il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif) -{ - int ret; - - lockdep_assert_held(&il->mutex); - - cancel_delayed_work(&il->scan_check); - - if (!il_is_ready_rf(il)) { - IL_WARN("Request scan called when driver not ready.\n"); - return -EIO; - } - - if (test_bit(S_SCAN_HW, &il->status)) { - D_SCAN("Multiple concurrent scan requests in parallel.\n"); - return -EBUSY; - } - - if (test_bit(S_SCAN_ABORTING, &il->status)) { - D_SCAN("Scan request while abort pending.\n"); - return -EBUSY; - } - - D_SCAN("Starting scan...\n"); - - set_bit(S_SCANNING, &il->status); - il->scan_start = jiffies; - - ret = il->ops->request_scan(il, vif); - if (ret) { - clear_bit(S_SCANNING, &il->status); - return ret; - } - - queue_delayed_work(il->workqueue, &il->scan_check, - IL_SCAN_CHECK_WATCHDOG); - - return 0; -} - -int -il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req) -{ - struct cfg80211_scan_request *req = &hw_req->req; - struct il_priv *il = hw->priv; - int ret; - - if (req->n_channels == 0) { - IL_ERR("Can not scan on no channels.\n"); - return -EINVAL; - } - - mutex_lock(&il->mutex); - D_MAC80211("enter\n"); - - if (test_bit(S_SCANNING, &il->status)) { - D_SCAN("Scan already in progress.\n"); - ret = -EAGAIN; - goto out_unlock; - } - - /* mac80211 will only ask for one band at a time */ - il->scan_request = req; - il->scan_vif = vif; - il->scan_band = req->channels[0]->band; - - ret = il_scan_initiate(il, vif); - -out_unlock: - D_MAC80211("leave ret %d\n", ret); - mutex_unlock(&il->mutex); - - return ret; -} -EXPORT_SYMBOL(il_mac_hw_scan); - -static void -il_bg_scan_check(struct work_struct *data) -{ - struct il_priv *il = - container_of(data, struct il_priv, scan_check.work); - - D_SCAN("Scan check work\n"); - - /* Since we are here firmware does not finish scan and - * most likely is in bad shape, so we don't bother to - * send abort command, just force scan complete to mac80211 */ - mutex_lock(&il->mutex); - il_force_scan_end(il); - mutex_unlock(&il->mutex); -} - -/** - * il_fill_probe_req - fill in all required fields and IE for probe request - */ - -u16 -il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, - const u8 *ta, const u8 *ies, int ie_len, int left) -{ - int len = 0; - u8 *pos = NULL; - - /* Make sure there is enough space for the probe request, - * two mandatory IEs and the data */ - left -= 24; - if (left < 0) - return 0; - - frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - eth_broadcast_addr(frame->da); - memcpy(frame->sa, ta, ETH_ALEN); - eth_broadcast_addr(frame->bssid); - frame->seq_ctrl = 0; - - len += 24; - - /* ...next IE... */ - pos = &frame->u.probe_req.variable[0]; - - /* fill in our indirect SSID IE */ - left -= 2; - if (left < 0) - return 0; - *pos++ = WLAN_EID_SSID; - *pos++ = 0; - - len += 2; - - if (WARN_ON(left < ie_len)) - return len; - - if (ies && ie_len) { - memcpy(pos, ies, ie_len); - len += ie_len; - } - - return (u16) len; -} -EXPORT_SYMBOL(il_fill_probe_req); - -static void -il_bg_abort_scan(struct work_struct *work) -{ - struct il_priv *il = container_of(work, struct il_priv, abort_scan); - - D_SCAN("Abort scan work\n"); - - /* We keep scan_check work queued in case when firmware will not - * report back scan completed notification */ - mutex_lock(&il->mutex); - il_scan_cancel_timeout(il, 200); - mutex_unlock(&il->mutex); -} - -static void -il_bg_scan_completed(struct work_struct *work) -{ - struct il_priv *il = container_of(work, struct il_priv, scan_completed); - bool aborted; - - D_SCAN("Completed scan.\n"); - - cancel_delayed_work(&il->scan_check); - - mutex_lock(&il->mutex); - - aborted = test_and_clear_bit(S_SCAN_ABORTING, &il->status); - if (aborted) - D_SCAN("Aborted scan completed.\n"); - - if (!test_and_clear_bit(S_SCANNING, &il->status)) { - D_SCAN("Scan already completed.\n"); - goto out_settings; - } - - il_complete_scan(il, aborted); - -out_settings: - /* Can we still talk to firmware ? */ - if (!il_is_ready_rf(il)) - goto out; - - /* - * We do not commit power settings while scan is pending, - * do it now if the settings changed. - */ - il_power_set_mode(il, &il->power_data.sleep_cmd_next, false); - il_set_tx_power(il, il->tx_power_next, false); - - il->ops->post_scan(il); - -out: - mutex_unlock(&il->mutex); -} - -void -il_setup_scan_deferred_work(struct il_priv *il) -{ - INIT_WORK(&il->scan_completed, il_bg_scan_completed); - INIT_WORK(&il->abort_scan, il_bg_abort_scan); - INIT_DELAYED_WORK(&il->scan_check, il_bg_scan_check); -} -EXPORT_SYMBOL(il_setup_scan_deferred_work); - -void -il_cancel_scan_deferred_work(struct il_priv *il) -{ - cancel_work_sync(&il->abort_scan); - cancel_work_sync(&il->scan_completed); - - if (cancel_delayed_work_sync(&il->scan_check)) { - mutex_lock(&il->mutex); - il_force_scan_end(il); - mutex_unlock(&il->mutex); - } -} -EXPORT_SYMBOL(il_cancel_scan_deferred_work); - -/* il->sta_lock must be held */ -static void -il_sta_ucode_activate(struct il_priv *il, u8 sta_id) -{ - - if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) - IL_ERR("ACTIVATE a non DRIVER active station id %u addr %pM\n", - sta_id, il->stations[sta_id].sta.sta.addr); - - if (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) { - D_ASSOC("STA id %u addr %pM already present" - " in uCode (according to driver)\n", sta_id, - il->stations[sta_id].sta.sta.addr); - } else { - il->stations[sta_id].used |= IL_STA_UCODE_ACTIVE; - D_ASSOC("Added STA id %u addr %pM to uCode\n", sta_id, - il->stations[sta_id].sta.sta.addr); - } -} - -static int -il_process_add_sta_resp(struct il_priv *il, struct il_addsta_cmd *addsta, - struct il_rx_pkt *pkt, bool sync) -{ - u8 sta_id = addsta->sta.sta_id; - unsigned long flags; - int ret = -EIO; - - if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { - IL_ERR("Bad return from C_ADD_STA (0x%08X)\n", pkt->hdr.flags); - return ret; - } - - D_INFO("Processing response for adding station %u\n", sta_id); - - spin_lock_irqsave(&il->sta_lock, flags); - - switch (pkt->u.add_sta.status) { - case ADD_STA_SUCCESS_MSK: - D_INFO("C_ADD_STA PASSED\n"); - il_sta_ucode_activate(il, sta_id); - ret = 0; - break; - case ADD_STA_NO_ROOM_IN_TBL: - IL_ERR("Adding station %d failed, no room in table.\n", sta_id); - break; - case ADD_STA_NO_BLOCK_ACK_RESOURCE: - IL_ERR("Adding station %d failed, no block ack resource.\n", - sta_id); - break; - case ADD_STA_MODIFY_NON_EXIST_STA: - IL_ERR("Attempting to modify non-existing station %d\n", - sta_id); - break; - default: - D_ASSOC("Received C_ADD_STA:(0x%08X)\n", pkt->u.add_sta.status); - break; - } - - D_INFO("%s station id %u addr %pM\n", - il->stations[sta_id].sta.mode == - STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", sta_id, - il->stations[sta_id].sta.sta.addr); - - /* - * XXX: The MAC address in the command buffer is often changed from - * the original sent to the device. That is, the MAC address - * written to the command buffer often is not the same MAC address - * read from the command buffer when the command returns. This - * issue has not yet been resolved and this debugging is left to - * observe the problem. - */ - D_INFO("%s station according to cmd buffer %pM\n", - il->stations[sta_id].sta.mode == - STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr); - spin_unlock_irqrestore(&il->sta_lock, flags); - - return ret; -} - -static void -il_add_sta_callback(struct il_priv *il, struct il_device_cmd *cmd, - struct il_rx_pkt *pkt) -{ - struct il_addsta_cmd *addsta = (struct il_addsta_cmd *)cmd->cmd.payload; - - il_process_add_sta_resp(il, addsta, pkt, false); - -} - -int -il_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags) -{ - struct il_rx_pkt *pkt = NULL; - int ret = 0; - u8 data[sizeof(*sta)]; - struct il_host_cmd cmd = { - .id = C_ADD_STA, - .flags = flags, - .data = data, - }; - u8 sta_id __maybe_unused = sta->sta.sta_id; - - D_INFO("Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr, - flags & CMD_ASYNC ? "a" : ""); - - if (flags & CMD_ASYNC) - cmd.callback = il_add_sta_callback; - else { - cmd.flags |= CMD_WANT_SKB; - might_sleep(); - } - - cmd.len = il->ops->build_addsta_hcmd(sta, data); - ret = il_send_cmd(il, &cmd); - - if (ret || (flags & CMD_ASYNC)) - return ret; - - if (ret == 0) { - pkt = (struct il_rx_pkt *)cmd.reply_page; - ret = il_process_add_sta_resp(il, sta, pkt, true); - } - il_free_pages(il, cmd.reply_page); - - return ret; -} -EXPORT_SYMBOL(il_send_add_sta); - -static void -il_set_ht_add_station(struct il_priv *il, u8 idx, struct ieee80211_sta *sta) -{ - struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; - __le32 sta_flags; - - if (!sta || !sta_ht_inf->ht_supported) - goto done; - - D_ASSOC("spatial multiplexing power save mode: %s\n", - (sta->smps_mode == IEEE80211_SMPS_STATIC) ? "static" : - (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ? "dynamic" : - "disabled"); - - sta_flags = il->stations[idx].sta.station_flags; - - sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK); - - switch (sta->smps_mode) { - case IEEE80211_SMPS_STATIC: - sta_flags |= STA_FLG_MIMO_DIS_MSK; - break; - case IEEE80211_SMPS_DYNAMIC: - sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; - break; - case IEEE80211_SMPS_OFF: - break; - default: - IL_WARN("Invalid MIMO PS mode %d\n", sta->smps_mode); - break; - } - - sta_flags |= - cpu_to_le32((u32) sta_ht_inf-> - ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); - - sta_flags |= - cpu_to_le32((u32) sta_ht_inf-> - ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); - - if (il_is_ht40_tx_allowed(il, &sta->ht_cap)) - sta_flags |= STA_FLG_HT40_EN_MSK; - else - sta_flags &= ~STA_FLG_HT40_EN_MSK; - - il->stations[idx].sta.station_flags = sta_flags; -done: - return; -} - -/** - * il_prep_station - Prepare station information for addition - * - * should be called with sta_lock held - */ -u8 -il_prep_station(struct il_priv *il, const u8 *addr, bool is_ap, - struct ieee80211_sta *sta) -{ - struct il_station_entry *station; - int i; - u8 sta_id = IL_INVALID_STATION; - u16 rate; - - if (is_ap) - sta_id = IL_AP_ID; - else if (is_broadcast_ether_addr(addr)) - sta_id = il->hw_params.bcast_id; - else - for (i = IL_STA_ID; i < il->hw_params.max_stations; i++) { - if (ether_addr_equal(il->stations[i].sta.sta.addr, - addr)) { - sta_id = i; - break; - } - - if (!il->stations[i].used && - sta_id == IL_INVALID_STATION) - sta_id = i; - } - - /* - * These two conditions have the same outcome, but keep them - * separate - */ - if (unlikely(sta_id == IL_INVALID_STATION)) - return sta_id; - - /* - * uCode is not able to deal with multiple requests to add a - * station. Keep track if one is in progress so that we do not send - * another. - */ - if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { - D_INFO("STA %d already in process of being added.\n", sta_id); - return sta_id; - } - - if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && - (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE) && - ether_addr_equal(il->stations[sta_id].sta.sta.addr, addr)) { - D_ASSOC("STA %d (%pM) already added, not adding again.\n", - sta_id, addr); - return sta_id; - } - - station = &il->stations[sta_id]; - station->used = IL_STA_DRIVER_ACTIVE; - D_ASSOC("Add STA to driver ID %d: %pM\n", sta_id, addr); - il->num_stations++; - - /* Set up the C_ADD_STA command to send to device */ - memset(&station->sta, 0, sizeof(struct il_addsta_cmd)); - memcpy(station->sta.sta.addr, addr, ETH_ALEN); - station->sta.mode = 0; - station->sta.sta.sta_id = sta_id; - station->sta.station_flags = 0; - - /* - * OK to call unconditionally, since local stations (IBSS BSSID - * STA and broadcast STA) pass in a NULL sta, and mac80211 - * doesn't allow HT IBSS. - */ - il_set_ht_add_station(il, sta_id, sta); - - /* 3945 only */ - rate = (il->band == IEEE80211_BAND_5GHZ) ? RATE_6M_PLCP : RATE_1M_PLCP; - /* Turn on both antennas for the station... */ - station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK); - - return sta_id; - -} -EXPORT_SYMBOL_GPL(il_prep_station); - -#define STA_WAIT_TIMEOUT (HZ/2) - -/** - * il_add_station_common - - */ -int -il_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap, - struct ieee80211_sta *sta, u8 *sta_id_r) -{ - unsigned long flags_spin; - int ret = 0; - u8 sta_id; - struct il_addsta_cmd sta_cmd; - - *sta_id_r = 0; - spin_lock_irqsave(&il->sta_lock, flags_spin); - sta_id = il_prep_station(il, addr, is_ap, sta); - if (sta_id == IL_INVALID_STATION) { - IL_ERR("Unable to prepare station %pM for addition\n", addr); - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - return -EINVAL; - } - - /* - * uCode is not able to deal with multiple requests to add a - * station. Keep track if one is in progress so that we do not send - * another. - */ - if (il->stations[sta_id].used & IL_STA_UCODE_INPROGRESS) { - D_INFO("STA %d already in process of being added.\n", sta_id); - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - return -EEXIST; - } - - if ((il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE) && - (il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { - D_ASSOC("STA %d (%pM) already added, not adding again.\n", - sta_id, addr); - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - return -EEXIST; - } - - il->stations[sta_id].used |= IL_STA_UCODE_INPROGRESS; - memcpy(&sta_cmd, &il->stations[sta_id].sta, - sizeof(struct il_addsta_cmd)); - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - - /* Add station to device's station table */ - ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); - if (ret) { - spin_lock_irqsave(&il->sta_lock, flags_spin); - IL_ERR("Adding station %pM failed.\n", - il->stations[sta_id].sta.sta.addr); - il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; - il->stations[sta_id].used &= ~IL_STA_UCODE_INPROGRESS; - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - } - *sta_id_r = sta_id; - return ret; -} -EXPORT_SYMBOL(il_add_station_common); - -/** - * il_sta_ucode_deactivate - deactivate ucode status for a station - * - * il->sta_lock must be held - */ -static void -il_sta_ucode_deactivate(struct il_priv *il, u8 sta_id) -{ - /* Ucode must be active and driver must be non active */ - if ((il->stations[sta_id]. - used & (IL_STA_UCODE_ACTIVE | IL_STA_DRIVER_ACTIVE)) != - IL_STA_UCODE_ACTIVE) - IL_ERR("removed non active STA %u\n", sta_id); - - il->stations[sta_id].used &= ~IL_STA_UCODE_ACTIVE; - - memset(&il->stations[sta_id], 0, sizeof(struct il_station_entry)); - D_ASSOC("Removed STA %u\n", sta_id); -} - -static int -il_send_remove_station(struct il_priv *il, const u8 * addr, int sta_id, - bool temporary) -{ - struct il_rx_pkt *pkt; - int ret; - - unsigned long flags_spin; - struct il_rem_sta_cmd rm_sta_cmd; - - struct il_host_cmd cmd = { - .id = C_REM_STA, - .len = sizeof(struct il_rem_sta_cmd), - .flags = CMD_SYNC, - .data = &rm_sta_cmd, - }; - - memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); - rm_sta_cmd.num_sta = 1; - memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); - - cmd.flags |= CMD_WANT_SKB; - - ret = il_send_cmd(il, &cmd); - - if (ret) - return ret; - - pkt = (struct il_rx_pkt *)cmd.reply_page; - if (pkt->hdr.flags & IL_CMD_FAILED_MSK) { - IL_ERR("Bad return from C_REM_STA (0x%08X)\n", pkt->hdr.flags); - ret = -EIO; - } - - if (!ret) { - switch (pkt->u.rem_sta.status) { - case REM_STA_SUCCESS_MSK: - if (!temporary) { - spin_lock_irqsave(&il->sta_lock, flags_spin); - il_sta_ucode_deactivate(il, sta_id); - spin_unlock_irqrestore(&il->sta_lock, - flags_spin); - } - D_ASSOC("C_REM_STA PASSED\n"); - break; - default: - ret = -EIO; - IL_ERR("C_REM_STA failed\n"); - break; - } - } - il_free_pages(il, cmd.reply_page); - - return ret; -} - -/** - * il_remove_station - Remove driver's knowledge of station. - */ -int -il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr) -{ - unsigned long flags; - - if (!il_is_ready(il)) { - D_INFO("Unable to remove station %pM, device not ready.\n", - addr); - /* - * It is typical for stations to be removed when we are - * going down. Return success since device will be down - * soon anyway - */ - return 0; - } - - D_ASSOC("Removing STA from driver:%d %pM\n", sta_id, addr); - - if (WARN_ON(sta_id == IL_INVALID_STATION)) - return -EINVAL; - - spin_lock_irqsave(&il->sta_lock, flags); - - if (!(il->stations[sta_id].used & IL_STA_DRIVER_ACTIVE)) { - D_INFO("Removing %pM but non DRIVER active\n", addr); - goto out_err; - } - - if (!(il->stations[sta_id].used & IL_STA_UCODE_ACTIVE)) { - D_INFO("Removing %pM but non UCODE active\n", addr); - goto out_err; - } - - if (il->stations[sta_id].used & IL_STA_LOCAL) { - kfree(il->stations[sta_id].lq); - il->stations[sta_id].lq = NULL; - } - - il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE; - - il->num_stations--; - - BUG_ON(il->num_stations < 0); - - spin_unlock_irqrestore(&il->sta_lock, flags); - - return il_send_remove_station(il, addr, sta_id, false); -out_err: - spin_unlock_irqrestore(&il->sta_lock, flags); - return -EINVAL; -} -EXPORT_SYMBOL_GPL(il_remove_station); - -/** - * il_clear_ucode_stations - clear ucode station table bits - * - * This function clears all the bits in the driver indicating - * which stations are active in the ucode. Call when something - * other than explicit station management would cause this in - * the ucode, e.g. unassociated RXON. - */ -void -il_clear_ucode_stations(struct il_priv *il) -{ - int i; - unsigned long flags_spin; - bool cleared = false; - - D_INFO("Clearing ucode stations in driver\n"); - - spin_lock_irqsave(&il->sta_lock, flags_spin); - for (i = 0; i < il->hw_params.max_stations; i++) { - if (il->stations[i].used & IL_STA_UCODE_ACTIVE) { - D_INFO("Clearing ucode active for station %d\n", i); - il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; - cleared = true; - } - } - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - - if (!cleared) - D_INFO("No active stations found to be cleared\n"); -} -EXPORT_SYMBOL(il_clear_ucode_stations); - -/** - * il_restore_stations() - Restore driver known stations to device - * - * All stations considered active by driver, but not present in ucode, is - * restored. - * - * Function sleeps. - */ -void -il_restore_stations(struct il_priv *il) -{ - struct il_addsta_cmd sta_cmd; - struct il_link_quality_cmd lq; - unsigned long flags_spin; - int i; - bool found = false; - int ret; - bool send_lq; - - if (!il_is_ready(il)) { - D_INFO("Not ready yet, not restoring any stations.\n"); - return; - } - - D_ASSOC("Restoring all known stations ... start.\n"); - spin_lock_irqsave(&il->sta_lock, flags_spin); - for (i = 0; i < il->hw_params.max_stations; i++) { - if ((il->stations[i].used & IL_STA_DRIVER_ACTIVE) && - !(il->stations[i].used & IL_STA_UCODE_ACTIVE)) { - D_ASSOC("Restoring sta %pM\n", - il->stations[i].sta.sta.addr); - il->stations[i].sta.mode = 0; - il->stations[i].used |= IL_STA_UCODE_INPROGRESS; - found = true; - } - } - - for (i = 0; i < il->hw_params.max_stations; i++) { - if ((il->stations[i].used & IL_STA_UCODE_INPROGRESS)) { - memcpy(&sta_cmd, &il->stations[i].sta, - sizeof(struct il_addsta_cmd)); - send_lq = false; - if (il->stations[i].lq) { - memcpy(&lq, il->stations[i].lq, - sizeof(struct il_link_quality_cmd)); - send_lq = true; - } - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - ret = il_send_add_sta(il, &sta_cmd, CMD_SYNC); - if (ret) { - spin_lock_irqsave(&il->sta_lock, flags_spin); - IL_ERR("Adding station %pM failed.\n", - il->stations[i].sta.sta.addr); - il->stations[i].used &= ~IL_STA_DRIVER_ACTIVE; - il->stations[i].used &= - ~IL_STA_UCODE_INPROGRESS; - spin_unlock_irqrestore(&il->sta_lock, - flags_spin); - } - /* - * Rate scaling has already been initialized, send - * current LQ command - */ - if (send_lq) - il_send_lq_cmd(il, &lq, CMD_SYNC, true); - spin_lock_irqsave(&il->sta_lock, flags_spin); - il->stations[i].used &= ~IL_STA_UCODE_INPROGRESS; - } - } - - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - if (!found) - D_INFO("Restoring all known stations" - " .... no stations to be restored.\n"); - else - D_INFO("Restoring all known stations" " .... complete.\n"); -} -EXPORT_SYMBOL(il_restore_stations); - -int -il_get_free_ucode_key_idx(struct il_priv *il) -{ - int i; - - for (i = 0; i < il->sta_key_max_num; i++) - if (!test_and_set_bit(i, &il->ucode_key_table)) - return i; - - return WEP_INVALID_OFFSET; -} -EXPORT_SYMBOL(il_get_free_ucode_key_idx); - -void -il_dealloc_bcast_stations(struct il_priv *il) -{ - unsigned long flags; - int i; - - spin_lock_irqsave(&il->sta_lock, flags); - for (i = 0; i < il->hw_params.max_stations; i++) { - if (!(il->stations[i].used & IL_STA_BCAST)) - continue; - - il->stations[i].used &= ~IL_STA_UCODE_ACTIVE; - il->num_stations--; - BUG_ON(il->num_stations < 0); - kfree(il->stations[i].lq); - il->stations[i].lq = NULL; - } - spin_unlock_irqrestore(&il->sta_lock, flags); -} -EXPORT_SYMBOL_GPL(il_dealloc_bcast_stations); - -#ifdef CONFIG_IWLEGACY_DEBUG -static void -il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq) -{ - int i; - D_RATE("lq station id 0x%x\n", lq->sta_id); - D_RATE("lq ant 0x%X 0x%X\n", lq->general_params.single_stream_ant_msk, - lq->general_params.dual_stream_ant_msk); - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) - D_RATE("lq idx %d 0x%X\n", i, lq->rs_table[i].rate_n_flags); -} -#else -static inline void -il_dump_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq) -{ -} -#endif - -/** - * il_is_lq_table_valid() - Test one aspect of LQ cmd for validity - * - * It sometimes happens when a HT rate has been in use and we - * loose connectivity with AP then mac80211 will first tell us that the - * current channel is not HT anymore before removing the station. In such a - * scenario the RXON flags will be updated to indicate we are not - * communicating HT anymore, but the LQ command may still contain HT rates. - * Test for this to prevent driver from sending LQ command between the time - * RXON flags are updated and when LQ command is updated. - */ -static bool -il_is_lq_table_valid(struct il_priv *il, struct il_link_quality_cmd *lq) -{ - int i; - - if (il->ht.enabled) - return true; - - D_INFO("Channel %u is not an HT channel\n", il->active.channel); - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & RATE_MCS_HT_MSK) { - D_INFO("idx %d of LQ expects HT channel\n", i); - return false; - } - } - return true; -} - -/** - * il_send_lq_cmd() - Send link quality command - * @init: This command is sent as part of station initialization right - * after station has been added. - * - * The link quality command is sent as the last step of station creation. - * This is the special case in which init is set and we call a callback in - * this case to clear the state indicating that station creation is in - * progress. - */ -int -il_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq, - u8 flags, bool init) -{ - int ret = 0; - unsigned long flags_spin; - - struct il_host_cmd cmd = { - .id = C_TX_LINK_QUALITY_CMD, - .len = sizeof(struct il_link_quality_cmd), - .flags = flags, - .data = lq, - }; - - if (WARN_ON(lq->sta_id == IL_INVALID_STATION)) - return -EINVAL; - - spin_lock_irqsave(&il->sta_lock, flags_spin); - if (!(il->stations[lq->sta_id].used & IL_STA_DRIVER_ACTIVE)) { - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - return -EINVAL; - } - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - - il_dump_lq_cmd(il, lq); - BUG_ON(init && (cmd.flags & CMD_ASYNC)); - - if (il_is_lq_table_valid(il, lq)) - ret = il_send_cmd(il, &cmd); - else - ret = -EINVAL; - - if (cmd.flags & CMD_ASYNC) - return ret; - - if (init) { - D_INFO("init LQ command complete," - " clearing sta addition status for sta %d\n", - lq->sta_id); - spin_lock_irqsave(&il->sta_lock, flags_spin); - il->stations[lq->sta_id].used &= ~IL_STA_UCODE_INPROGRESS; - spin_unlock_irqrestore(&il->sta_lock, flags_spin); - } - return ret; -} -EXPORT_SYMBOL(il_send_lq_cmd); - -int -il_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct il_priv *il = hw->priv; - struct il_station_priv_common *sta_common = (void *)sta->drv_priv; - int ret; - - mutex_lock(&il->mutex); - D_MAC80211("enter station %pM\n", sta->addr); - - ret = il_remove_station(il, sta_common->sta_id, sta->addr); - if (ret) - IL_ERR("Error removing station %pM\n", sta->addr); - - D_MAC80211("leave ret %d\n", ret); - mutex_unlock(&il->mutex); - - return ret; -} -EXPORT_SYMBOL(il_mac_sta_remove); - -/************************** RX-FUNCTIONS ****************************/ -/* - * Rx theory of operation - * - * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), - * each of which point to Receive Buffers to be filled by the NIC. These get - * used not only for Rx frames, but for any command response or notification - * from the NIC. The driver and NIC manage the Rx buffers by means - * of idxes into the circular buffer. - * - * Rx Queue Indexes - * The host/firmware share two idx registers for managing the Rx buffers. - * - * The READ idx maps to the first position that the firmware may be writing - * to -- the driver can read up to (but not including) this position and get - * good data. - * The READ idx is managed by the firmware once the card is enabled. - * - * The WRITE idx maps to the last position the driver has read from -- the - * position preceding WRITE is the last slot the firmware can place a packet. - * - * The queue is empty (no good data) if WRITE = READ - 1, and is full if - * WRITE = READ. - * - * During initialization, the host sets up the READ queue position to the first - * IDX position, and WRITE to the last (READ - 1 wrapped) - * - * When the firmware places a packet in a buffer, it will advance the READ idx - * and fire the RX interrupt. The driver can then query the READ idx and - * process as many packets as possible, moving the WRITE idx forward as it - * resets the Rx queue buffers with new memory. - * - * The management in the driver is as follows: - * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When - * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled - * to replenish the iwl->rxq->rx_free. - * + In il_rx_replenish (scheduled) if 'processed' != 'read' then the - * iwl->rxq is replenished and the READ IDX is updated (updating the - * 'processed' and 'read' driver idxes as well) - * + A received packet is processed and handed to the kernel network stack, - * detached from the iwl->rxq. The driver 'processed' idx is updated. - * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free - * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ - * IDX is not incremented and iwl->status(RX_STALLED) is set. If there - * were enough free buffers and RX_STALLED is set it is cleared. - * - * - * Driver sequence: - * - * il_rx_queue_alloc() Allocates rx_free - * il_rx_replenish() Replenishes rx_free list from rx_used, and calls - * il_rx_queue_restock - * il_rx_queue_restock() Moves available buffers from rx_free into Rx - * queue, updates firmware pointers, and updates - * the WRITE idx. If insufficient rx_free buffers - * are available, schedules il_rx_replenish - * - * -- enable interrupts -- - * ISR - il_rx() Detach il_rx_bufs from pool up to the - * READ IDX, detaching the SKB from the pool. - * Moves the packet buffer from queue to rx_used. - * Calls il_rx_queue_restock to refill any empty - * slots. - * ... - * - */ - -/** - * il_rx_queue_space - Return number of free slots available in queue. - */ -int -il_rx_queue_space(const struct il_rx_queue *q) -{ - int s = q->read - q->write; - if (s <= 0) - s += RX_QUEUE_SIZE; - /* keep some buffer to not confuse full and empty queue */ - s -= 2; - if (s < 0) - s = 0; - return s; -} -EXPORT_SYMBOL(il_rx_queue_space); - -/** - * il_rx_queue_update_write_ptr - Update the write pointer for the RX queue - */ -void -il_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q) -{ - unsigned long flags; - u32 rx_wrt_ptr_reg = il->hw_params.rx_wrt_ptr_reg; - u32 reg; - - spin_lock_irqsave(&q->lock, flags); - - if (q->need_update == 0) - goto exit_unlock; - - /* If power-saving is in use, make sure device is awake */ - if (test_bit(S_POWER_PMI, &il->status)) { - reg = _il_rd(il, CSR_UCODE_DRV_GP1); - - if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { - D_INFO("Rx queue requesting wakeup," " GP1 = 0x%x\n", - reg); - il_set_bit(il, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - goto exit_unlock; - } - - q->write_actual = (q->write & ~0x7); - il_wr(il, rx_wrt_ptr_reg, q->write_actual); - - /* Else device is assumed to be awake */ - } else { - /* Device expects a multiple of 8 */ - q->write_actual = (q->write & ~0x7); - il_wr(il, rx_wrt_ptr_reg, q->write_actual); - } - - q->need_update = 0; - -exit_unlock: - spin_unlock_irqrestore(&q->lock, flags); -} -EXPORT_SYMBOL(il_rx_queue_update_write_ptr); - -int -il_rx_queue_alloc(struct il_priv *il) -{ - struct il_rx_queue *rxq = &il->rxq; - struct device *dev = &il->pci_dev->dev; - int i; - - spin_lock_init(&rxq->lock); - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - - /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */ - rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma, - GFP_KERNEL); - if (!rxq->bd) - goto err_bd; - - rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct il_rb_status), - &rxq->rb_stts_dma, GFP_KERNEL); - if (!rxq->rb_stts) - goto err_rb; - - /* Fill the rx_used queue with _all_ of the Rx buffers */ - for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) - list_add_tail(&rxq->pool[i].list, &rxq->rx_used); - - /* Set us so that we have processed and used all buffers, but have - * not restocked the Rx queue with fresh buffers */ - rxq->read = rxq->write = 0; - rxq->write_actual = 0; - rxq->free_count = 0; - rxq->need_update = 0; - return 0; - -err_rb: - dma_free_coherent(&il->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, - rxq->bd_dma); -err_bd: - return -ENOMEM; -} -EXPORT_SYMBOL(il_rx_queue_alloc); - -void -il_hdl_spectrum_measurement(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_spectrum_notification *report = &(pkt->u.spectrum_notif); - - if (!report->state) { - D_11H("Spectrum Measure Notification: Start\n"); - return; - } - - memcpy(&il->measure_report, report, sizeof(*report)); - il->measurement_status |= MEASUREMENT_READY; -} -EXPORT_SYMBOL(il_hdl_spectrum_measurement); - -/* - * returns non-zero if packet should be dropped - */ -int -il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr, - u32 decrypt_res, struct ieee80211_rx_status *stats) -{ - u16 fc = le16_to_cpu(hdr->frame_control); - - /* - * All contexts have the same setting here due to it being - * a module parameter, so OK to check any context. - */ - if (il->active.filter_flags & RXON_FILTER_DIS_DECRYPT_MSK) - return 0; - - if (!(fc & IEEE80211_FCTL_PROTECTED)) - return 0; - - D_RX("decrypt_res:0x%x\n", decrypt_res); - switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { - case RX_RES_STATUS_SEC_TYPE_TKIP: - /* The uCode has got a bad phase 1 Key, pushes the packet. - * Decryption will be done in SW. */ - if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == - RX_RES_STATUS_BAD_KEY_TTAK) - break; - - case RX_RES_STATUS_SEC_TYPE_WEP: - if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == - RX_RES_STATUS_BAD_ICV_MIC) { - /* bad ICV, the packet is destroyed since the - * decryption is inplace, drop it */ - D_RX("Packet destroyed\n"); - return -1; - } - case RX_RES_STATUS_SEC_TYPE_CCMP: - if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == - RX_RES_STATUS_DECRYPT_OK) { - D_RX("hw decrypt successfully!!!\n"); - stats->flag |= RX_FLAG_DECRYPTED; - } - break; - - default: - break; - } - return 0; -} -EXPORT_SYMBOL(il_set_decrypted_flag); - -/** - * il_txq_update_write_ptr - Send new write idx to hardware - */ -void -il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq) -{ - u32 reg = 0; - int txq_id = txq->q.id; - - if (txq->need_update == 0) - return; - - /* if we're trying to save power */ - if (test_bit(S_POWER_PMI, &il->status)) { - /* wake up nic if it's powered down ... - * uCode will wake up, and interrupt us again, so next - * time we'll skip this part. */ - reg = _il_rd(il, CSR_UCODE_DRV_GP1); - - if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { - D_INFO("Tx queue %d requesting wakeup," " GP1 = 0x%x\n", - txq_id, reg); - il_set_bit(il, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - return; - } - - il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); - - /* - * else not in power-save mode, - * uCode will never sleep when we're - * trying to tx (during RFKILL, we're not trying to tx). - */ - } else - _il_wr(il, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); - txq->need_update = 0; -} -EXPORT_SYMBOL(il_txq_update_write_ptr); - -/** - * il_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's - */ -void -il_tx_queue_unmap(struct il_priv *il, int txq_id) -{ - struct il_tx_queue *txq = &il->txq[txq_id]; - struct il_queue *q = &txq->q; - - if (q->n_bd == 0) - return; - - while (q->write_ptr != q->read_ptr) { - il->ops->txq_free_tfd(il, txq); - q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd); - } -} -EXPORT_SYMBOL(il_tx_queue_unmap); - -/** - * il_tx_queue_free - Deallocate DMA queue. - * @txq: Transmit queue to deallocate. - * - * Empty queue by removing and destroying all BD's. - * Free all buffers. - * 0-fill, but do not free "txq" descriptor structure. - */ -void -il_tx_queue_free(struct il_priv *il, int txq_id) -{ - struct il_tx_queue *txq = &il->txq[txq_id]; - struct device *dev = &il->pci_dev->dev; - int i; - - il_tx_queue_unmap(il, txq_id); - - /* De-alloc array of command/tx buffers */ - for (i = 0; i < TFD_TX_CMD_SLOTS; i++) - kfree(txq->cmd[i]); - - /* De-alloc circular buffer of TFDs */ - if (txq->q.n_bd) - dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd, - txq->tfds, txq->q.dma_addr); - - /* De-alloc array of per-TFD driver data */ - kfree(txq->skbs); - txq->skbs = NULL; - - /* deallocate arrays */ - kfree(txq->cmd); - kfree(txq->meta); - txq->cmd = NULL; - txq->meta = NULL; - - /* 0-fill queue descriptor structure */ - memset(txq, 0, sizeof(*txq)); -} -EXPORT_SYMBOL(il_tx_queue_free); - -/** - * il_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue - */ -void -il_cmd_queue_unmap(struct il_priv *il) -{ - struct il_tx_queue *txq = &il->txq[il->cmd_queue]; - struct il_queue *q = &txq->q; - int i; - - if (q->n_bd == 0) - return; - - while (q->read_ptr != q->write_ptr) { - i = il_get_cmd_idx(q, q->read_ptr, 0); - - if (txq->meta[i].flags & CMD_MAPPED) { - pci_unmap_single(il->pci_dev, - dma_unmap_addr(&txq->meta[i], mapping), - dma_unmap_len(&txq->meta[i], len), - PCI_DMA_BIDIRECTIONAL); - txq->meta[i].flags = 0; - } - - q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd); - } - - i = q->n_win; - if (txq->meta[i].flags & CMD_MAPPED) { - pci_unmap_single(il->pci_dev, - dma_unmap_addr(&txq->meta[i], mapping), - dma_unmap_len(&txq->meta[i], len), - PCI_DMA_BIDIRECTIONAL); - txq->meta[i].flags = 0; - } -} -EXPORT_SYMBOL(il_cmd_queue_unmap); - -/** - * il_cmd_queue_free - Deallocate DMA queue. - * @txq: Transmit queue to deallocate. - * - * Empty queue by removing and destroying all BD's. - * Free all buffers. - * 0-fill, but do not free "txq" descriptor structure. - */ -void -il_cmd_queue_free(struct il_priv *il) -{ - struct il_tx_queue *txq = &il->txq[il->cmd_queue]; - struct device *dev = &il->pci_dev->dev; - int i; - - il_cmd_queue_unmap(il); - - /* De-alloc array of command/tx buffers */ - for (i = 0; i <= TFD_CMD_SLOTS; i++) - kfree(txq->cmd[i]); - - /* De-alloc circular buffer of TFDs */ - if (txq->q.n_bd) - dma_free_coherent(dev, il->hw_params.tfd_size * txq->q.n_bd, - txq->tfds, txq->q.dma_addr); - - /* deallocate arrays */ - kfree(txq->cmd); - kfree(txq->meta); - txq->cmd = NULL; - txq->meta = NULL; - - /* 0-fill queue descriptor structure */ - memset(txq, 0, sizeof(*txq)); -} -EXPORT_SYMBOL(il_cmd_queue_free); - -/*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** - * DMA services - * - * Theory of operation - * - * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer - * of buffer descriptors, each of which points to one or more data buffers for - * the device to read from or fill. Driver and device exchange status of each - * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty - * entries in each circular buffer, to protect against confusing empty and full - * queue states. - * - * The device reads or writes the data in the queues via the device's several - * DMA/FIFO channels. Each queue is mapped to a single DMA channel. - * - * For Tx queue, there are low mark and high mark limits. If, after queuing - * the packet for Tx, free space become < low mark, Tx queue stopped. When - * reclaiming packets (on 'tx done IRQ), if free space become > high mark, - * Tx queue resumed. - * - * See more detailed info in 4965.h. - ***************************************************/ - -int -il_queue_space(const struct il_queue *q) -{ - int s = q->read_ptr - q->write_ptr; - - if (q->read_ptr > q->write_ptr) - s -= q->n_bd; - - if (s <= 0) - s += q->n_win; - /* keep some reserve to not confuse empty and full situations */ - s -= 2; - if (s < 0) - s = 0; - return s; -} -EXPORT_SYMBOL(il_queue_space); - - -/** - * il_queue_init - Initialize queue's high/low-water and read/write idxes - */ -static int -il_queue_init(struct il_priv *il, struct il_queue *q, int slots, u32 id) -{ - /* - * TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise - * il_queue_inc_wrap and il_queue_dec_wrap are broken. - */ - BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); - /* FIXME: remove q->n_bd */ - q->n_bd = TFD_QUEUE_SIZE_MAX; - - q->n_win = slots; - q->id = id; - - /* slots_must be power-of-two size, otherwise - * il_get_cmd_idx is broken. */ - BUG_ON(!is_power_of_2(slots)); - - q->low_mark = q->n_win / 4; - if (q->low_mark < 4) - q->low_mark = 4; - - q->high_mark = q->n_win / 8; - if (q->high_mark < 2) - q->high_mark = 2; - - q->write_ptr = q->read_ptr = 0; - - return 0; -} - -/** - * il_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue - */ -static int -il_tx_queue_alloc(struct il_priv *il, struct il_tx_queue *txq, u32 id) -{ - struct device *dev = &il->pci_dev->dev; - size_t tfd_sz = il->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX; - - /* Driver ilate data, only for Tx (not command) queues, - * not shared with device. */ - if (id != il->cmd_queue) { - txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX, - sizeof(struct sk_buff *), - GFP_KERNEL); - if (!txq->skbs) { - IL_ERR("Fail to alloc skbs\n"); - goto error; - } - } else - txq->skbs = NULL; - - /* Circular buffer of transmit frame descriptors (TFDs), - * shared with device */ - txq->tfds = - dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, GFP_KERNEL); - if (!txq->tfds) - goto error; - - txq->q.id = id; - - return 0; - -error: - kfree(txq->skbs); - txq->skbs = NULL; - - return -ENOMEM; -} - -/** - * il_tx_queue_init - Allocate and initialize one tx/cmd queue - */ -int -il_tx_queue_init(struct il_priv *il, u32 txq_id) -{ - int i, len, ret; - int slots, actual_slots; - struct il_tx_queue *txq = &il->txq[txq_id]; - - /* - * Alloc buffer array for commands (Tx or other types of commands). - * For the command queue (#4/#9), allocate command space + one big - * command for scan, since scan command is very huge; the system will - * not have two scans at the same time, so only one is needed. - * For normal Tx queues (all other queues), no super-size command - * space is needed. - */ - if (txq_id == il->cmd_queue) { - slots = TFD_CMD_SLOTS; - actual_slots = slots + 1; - } else { - slots = TFD_TX_CMD_SLOTS; - actual_slots = slots; - } - - txq->meta = - kzalloc(sizeof(struct il_cmd_meta) * actual_slots, GFP_KERNEL); - txq->cmd = - kzalloc(sizeof(struct il_device_cmd *) * actual_slots, GFP_KERNEL); - - if (!txq->meta || !txq->cmd) - goto out_free_arrays; - - len = sizeof(struct il_device_cmd); - for (i = 0; i < actual_slots; i++) { - /* only happens for cmd queue */ - if (i == slots) - len = IL_MAX_CMD_SIZE; - - txq->cmd[i] = kmalloc(len, GFP_KERNEL); - if (!txq->cmd[i]) - goto err; - } - - /* Alloc driver data array and TFD circular buffer */ - ret = il_tx_queue_alloc(il, txq, txq_id); - if (ret) - goto err; - - txq->need_update = 0; - - /* - * For the default queues 0-3, set up the swq_id - * already -- all others need to get one later - * (if they need one at all). - */ - if (txq_id < 4) - il_set_swq_id(txq, txq_id, txq_id); - - /* Initialize queue's high/low-water marks, and head/tail idxes */ - il_queue_init(il, &txq->q, slots, txq_id); - - /* Tell device where to find queue */ - il->ops->txq_init(il, txq); - - return 0; -err: - for (i = 0; i < actual_slots; i++) - kfree(txq->cmd[i]); -out_free_arrays: - kfree(txq->meta); - kfree(txq->cmd); - - return -ENOMEM; -} -EXPORT_SYMBOL(il_tx_queue_init); - -void -il_tx_queue_reset(struct il_priv *il, u32 txq_id) -{ - int slots, actual_slots; - struct il_tx_queue *txq = &il->txq[txq_id]; - - if (txq_id == il->cmd_queue) { - slots = TFD_CMD_SLOTS; - actual_slots = TFD_CMD_SLOTS + 1; - } else { - slots = TFD_TX_CMD_SLOTS; - actual_slots = TFD_TX_CMD_SLOTS; - } - - memset(txq->meta, 0, sizeof(struct il_cmd_meta) * actual_slots); - txq->need_update = 0; - - /* Initialize queue's high/low-water marks, and head/tail idxes */ - il_queue_init(il, &txq->q, slots, txq_id); - - /* Tell device where to find queue */ - il->ops->txq_init(il, txq); -} -EXPORT_SYMBOL(il_tx_queue_reset); - -/*************** HOST COMMAND QUEUE FUNCTIONS *****/ - -/** - * il_enqueue_hcmd - enqueue a uCode command - * @il: device ilate data point - * @cmd: a point to the ucode command structure - * - * The function returns < 0 values to indicate the operation is - * failed. On success, it turns the idx (> 0) of command in the - * command queue. - */ -int -il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd) -{ - struct il_tx_queue *txq = &il->txq[il->cmd_queue]; - struct il_queue *q = &txq->q; - struct il_device_cmd *out_cmd; - struct il_cmd_meta *out_meta; - dma_addr_t phys_addr; - unsigned long flags; - int len; - u32 idx; - u16 fix_size; - - cmd->len = il->ops->get_hcmd_size(cmd->id, cmd->len); - fix_size = (u16) (cmd->len + sizeof(out_cmd->hdr)); - - /* If any of the command structures end up being larger than - * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then - * we will need to increase the size of the TFD entries - * Also, check to see if command buffer should not exceed the size - * of device_cmd and max_cmd_size. */ - BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && - !(cmd->flags & CMD_SIZE_HUGE)); - BUG_ON(fix_size > IL_MAX_CMD_SIZE); - - if (il_is_rfkill(il) || il_is_ctkill(il)) { - IL_WARN("Not sending command - %s KILL\n", - il_is_rfkill(il) ? "RF" : "CT"); - return -EIO; - } - - spin_lock_irqsave(&il->hcmd_lock, flags); - - if (il_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { - spin_unlock_irqrestore(&il->hcmd_lock, flags); - - IL_ERR("Restarting adapter due to command queue full\n"); - queue_work(il->workqueue, &il->restart); - return -ENOSPC; - } - - idx = il_get_cmd_idx(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); - out_cmd = txq->cmd[idx]; - out_meta = &txq->meta[idx]; - - if (WARN_ON(out_meta->flags & CMD_MAPPED)) { - spin_unlock_irqrestore(&il->hcmd_lock, flags); - return -ENOSPC; - } - - memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ - out_meta->flags = cmd->flags | CMD_MAPPED; - if (cmd->flags & CMD_WANT_SKB) - out_meta->source = cmd; - if (cmd->flags & CMD_ASYNC) - out_meta->callback = cmd->callback; - - out_cmd->hdr.cmd = cmd->id; - memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len); - - /* At this point, the out_cmd now has all of the incoming cmd - * information */ - - out_cmd->hdr.flags = 0; - out_cmd->hdr.sequence = - cpu_to_le16(QUEUE_TO_SEQ(il->cmd_queue) | IDX_TO_SEQ(q->write_ptr)); - if (cmd->flags & CMD_SIZE_HUGE) - out_cmd->hdr.sequence |= SEQ_HUGE_FRAME; - len = sizeof(struct il_device_cmd); - if (idx == TFD_CMD_SLOTS) - len = IL_MAX_CMD_SIZE; - -#ifdef CONFIG_IWLEGACY_DEBUG - switch (out_cmd->hdr.cmd) { - case C_TX_LINK_QUALITY_CMD: - case C_SENSITIVITY: - D_HC_DUMP("Sending command %s (#%x), seq: 0x%04X, " - "%d bytes at %d[%d]:%d\n", - il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, - le16_to_cpu(out_cmd->hdr.sequence), fix_size, - q->write_ptr, idx, il->cmd_queue); - break; - default: - D_HC("Sending command %s (#%x), seq: 0x%04X, " - "%d bytes at %d[%d]:%d\n", - il_get_cmd_string(out_cmd->hdr.cmd), out_cmd->hdr.cmd, - le16_to_cpu(out_cmd->hdr.sequence), fix_size, q->write_ptr, - idx, il->cmd_queue); - } -#endif - - phys_addr = - pci_map_single(il->pci_dev, &out_cmd->hdr, fix_size, - PCI_DMA_BIDIRECTIONAL); - if (unlikely(pci_dma_mapping_error(il->pci_dev, phys_addr))) { - idx = -ENOMEM; - goto out; - } - dma_unmap_addr_set(out_meta, mapping, phys_addr); - dma_unmap_len_set(out_meta, len, fix_size); - - txq->need_update = 1; - - if (il->ops->txq_update_byte_cnt_tbl) - /* Set up entry in queue's byte count circular buffer */ - il->ops->txq_update_byte_cnt_tbl(il, txq, 0); - - il->ops->txq_attach_buf_to_tfd(il, txq, phys_addr, fix_size, 1, - U32_PAD(cmd->len)); - - /* Increment and update queue's write idx */ - q->write_ptr = il_queue_inc_wrap(q->write_ptr, q->n_bd); - il_txq_update_write_ptr(il, txq); - -out: - spin_unlock_irqrestore(&il->hcmd_lock, flags); - return idx; -} - -/** - * il_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd - * - * When FW advances 'R' idx, all entries between old and new 'R' idx - * need to be reclaimed. As result, some free space forms. If there is - * enough free space (> low mark), wake the stack that feeds us. - */ -static void -il_hcmd_queue_reclaim(struct il_priv *il, int txq_id, int idx, int cmd_idx) -{ - struct il_tx_queue *txq = &il->txq[txq_id]; - struct il_queue *q = &txq->q; - int nfreed = 0; - - if (idx >= q->n_bd || il_queue_used(q, idx) == 0) { - IL_ERR("Read idx for DMA queue txq id (%d), idx %d, " - "is out of range [0-%d] %d %d.\n", txq_id, idx, q->n_bd, - q->write_ptr, q->read_ptr); - return; - } - - for (idx = il_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; - q->read_ptr = il_queue_inc_wrap(q->read_ptr, q->n_bd)) { - - if (nfreed++ > 0) { - IL_ERR("HCMD skipped: idx (%d) %d %d\n", idx, - q->write_ptr, q->read_ptr); - queue_work(il->workqueue, &il->restart); - } - - } -} - -/** - * il_tx_cmd_complete - Pull unused buffers off the queue and reclaim them - * @rxb: Rx buffer to reclaim - * - * If an Rx buffer has an async callback associated with it the callback - * will be executed. The attached skb (if present) will only be freed - * if the callback returns 1 - */ -void -il_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - u16 sequence = le16_to_cpu(pkt->hdr.sequence); - int txq_id = SEQ_TO_QUEUE(sequence); - int idx = SEQ_TO_IDX(sequence); - int cmd_idx; - bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); - struct il_device_cmd *cmd; - struct il_cmd_meta *meta; - struct il_tx_queue *txq = &il->txq[il->cmd_queue]; - unsigned long flags; - - /* If a Tx command is being handled and it isn't in the actual - * command queue then there a command routing bug has been introduced - * in the queue management code. */ - if (WARN - (txq_id != il->cmd_queue, - "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", - txq_id, il->cmd_queue, sequence, il->txq[il->cmd_queue].q.read_ptr, - il->txq[il->cmd_queue].q.write_ptr)) { - il_print_hex_error(il, pkt, 32); - return; - } - - cmd_idx = il_get_cmd_idx(&txq->q, idx, huge); - cmd = txq->cmd[cmd_idx]; - meta = &txq->meta[cmd_idx]; - - txq->time_stamp = jiffies; - - pci_unmap_single(il->pci_dev, dma_unmap_addr(meta, mapping), - dma_unmap_len(meta, len), PCI_DMA_BIDIRECTIONAL); - - /* Input error checking is done when commands are added to queue. */ - if (meta->flags & CMD_WANT_SKB) { - meta->source->reply_page = (unsigned long)rxb_addr(rxb); - rxb->page = NULL; - } else if (meta->callback) - meta->callback(il, cmd, pkt); - - spin_lock_irqsave(&il->hcmd_lock, flags); - - il_hcmd_queue_reclaim(il, txq_id, idx, cmd_idx); - - if (!(meta->flags & CMD_ASYNC)) { - clear_bit(S_HCMD_ACTIVE, &il->status); - D_INFO("Clearing HCMD_ACTIVE for command %s\n", - il_get_cmd_string(cmd->hdr.cmd)); - wake_up(&il->wait_command_queue); - } - - /* Mark as unmapped */ - meta->flags = 0; - - spin_unlock_irqrestore(&il->hcmd_lock, flags); -} -EXPORT_SYMBOL(il_tx_cmd_complete); - -MODULE_DESCRIPTION("iwl-legacy: common functions for 3945 and 4965"); -MODULE_VERSION(IWLWIFI_VERSION); -MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); -MODULE_LICENSE("GPL"); - -/* - * set bt_coex_active to true, uCode will do kill/defer - * every time the priority line is asserted (BT is sending signals on the - * priority line in the PCIx). - * set bt_coex_active to false, uCode will ignore the BT activity and - * perform the normal operation - * - * User might experience transmit issue on some platform due to WiFi/BT - * co-exist problem. The possible behaviors are: - * Able to scan and finding all the available AP - * Not able to associate with any AP - * On those platforms, WiFi communication can be restored by set - * "bt_coex_active" module parameter to "false" - * - * default: bt_coex_active = true (BT_COEX_ENABLE) - */ -static bool bt_coex_active = true; -module_param(bt_coex_active, bool, S_IRUGO); -MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist"); - -u32 il_debug_level; -EXPORT_SYMBOL(il_debug_level); - -const u8 il_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; -EXPORT_SYMBOL(il_bcast_addr); - -#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ -#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ -static void -il_init_ht_hw_capab(const struct il_priv *il, - struct ieee80211_sta_ht_cap *ht_info, - enum ieee80211_band band) -{ - u16 max_bit_rate = 0; - u8 rx_chains_num = il->hw_params.rx_chains_num; - u8 tx_chains_num = il->hw_params.tx_chains_num; - - ht_info->cap = 0; - memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); - - ht_info->ht_supported = true; - - ht_info->cap |= IEEE80211_HT_CAP_SGI_20; - max_bit_rate = MAX_BIT_RATE_20_MHZ; - if (il->hw_params.ht40_channel & BIT(band)) { - ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - ht_info->cap |= IEEE80211_HT_CAP_SGI_40; - ht_info->mcs.rx_mask[4] = 0x01; - max_bit_rate = MAX_BIT_RATE_40_MHZ; - } - - if (il->cfg->mod_params->amsdu_size_8K) - ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; - - ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; - ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF; - - ht_info->mcs.rx_mask[0] = 0xFF; - if (rx_chains_num >= 2) - ht_info->mcs.rx_mask[1] = 0xFF; - if (rx_chains_num >= 3) - ht_info->mcs.rx_mask[2] = 0xFF; - - /* Highest supported Rx data rate */ - max_bit_rate *= rx_chains_num; - WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); - ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); - - /* Tx MCS capabilities */ - ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - if (tx_chains_num != rx_chains_num) { - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; - ht_info->mcs.tx_params |= - ((tx_chains_num - - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - } -} - -/** - * il_init_geos - Initialize mac80211's geo/channel info based from eeprom - */ -int -il_init_geos(struct il_priv *il) -{ - struct il_channel_info *ch; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *channels; - struct ieee80211_channel *geo_ch; - struct ieee80211_rate *rates; - int i = 0; - s8 max_tx_power = 0; - - if (il->bands[IEEE80211_BAND_2GHZ].n_bitrates || - il->bands[IEEE80211_BAND_5GHZ].n_bitrates) { - D_INFO("Geography modes already initialized.\n"); - set_bit(S_GEO_CONFIGURED, &il->status); - return 0; - } - - channels = - kzalloc(sizeof(struct ieee80211_channel) * il->channel_count, - GFP_KERNEL); - if (!channels) - return -ENOMEM; - - rates = - kzalloc((sizeof(struct ieee80211_rate) * RATE_COUNT_LEGACY), - GFP_KERNEL); - if (!rates) { - kfree(channels); - return -ENOMEM; - } - - /* 5.2GHz channels start after the 2.4GHz channels */ - sband = &il->bands[IEEE80211_BAND_5GHZ]; - sband->channels = &channels[ARRAY_SIZE(il_eeprom_band_1)]; - /* just OFDM */ - sband->bitrates = &rates[IL_FIRST_OFDM_RATE]; - sband->n_bitrates = RATE_COUNT_LEGACY - IL_FIRST_OFDM_RATE; - - if (il->cfg->sku & IL_SKU_N) - il_init_ht_hw_capab(il, &sband->ht_cap, IEEE80211_BAND_5GHZ); - - sband = &il->bands[IEEE80211_BAND_2GHZ]; - sband->channels = channels; - /* OFDM & CCK */ - sband->bitrates = rates; - sband->n_bitrates = RATE_COUNT_LEGACY; - - if (il->cfg->sku & IL_SKU_N) - il_init_ht_hw_capab(il, &sband->ht_cap, IEEE80211_BAND_2GHZ); - - il->ieee_channels = channels; - il->ieee_rates = rates; - - for (i = 0; i < il->channel_count; i++) { - ch = &il->channel_info[i]; - - if (!il_is_channel_valid(ch)) - continue; - - sband = &il->bands[ch->band]; - - geo_ch = &sband->channels[sband->n_channels++]; - - geo_ch->center_freq = - ieee80211_channel_to_frequency(ch->channel, ch->band); - geo_ch->max_power = ch->max_power_avg; - geo_ch->max_antenna_gain = 0xff; - geo_ch->hw_value = ch->channel; - - if (il_is_channel_valid(ch)) { - if (!(ch->flags & EEPROM_CHANNEL_IBSS)) - geo_ch->flags |= IEEE80211_CHAN_NO_IR; - - if (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) - geo_ch->flags |= IEEE80211_CHAN_NO_IR; - - if (ch->flags & EEPROM_CHANNEL_RADAR) - geo_ch->flags |= IEEE80211_CHAN_RADAR; - - geo_ch->flags |= ch->ht40_extension_channel; - - if (ch->max_power_avg > max_tx_power) - max_tx_power = ch->max_power_avg; - } else { - geo_ch->flags |= IEEE80211_CHAN_DISABLED; - } - - D_INFO("Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel, - geo_ch->center_freq, - il_is_channel_a_band(ch) ? "5.2" : "2.4", - geo_ch-> - flags & IEEE80211_CHAN_DISABLED ? "restricted" : "valid", - geo_ch->flags); - } - - il->tx_power_device_lmt = max_tx_power; - il->tx_power_user_lmt = max_tx_power; - il->tx_power_next = max_tx_power; - - if (il->bands[IEEE80211_BAND_5GHZ].n_channels == 0 && - (il->cfg->sku & IL_SKU_A)) { - IL_INFO("Incorrectly detected BG card as ABG. " - "Please send your PCI ID 0x%04X:0x%04X to maintainer.\n", - il->pci_dev->device, il->pci_dev->subsystem_device); - il->cfg->sku &= ~IL_SKU_A; - } - - IL_INFO("Tunable channels: %d 802.11bg, %d 802.11a channels\n", - il->bands[IEEE80211_BAND_2GHZ].n_channels, - il->bands[IEEE80211_BAND_5GHZ].n_channels); - - set_bit(S_GEO_CONFIGURED, &il->status); - - return 0; -} -EXPORT_SYMBOL(il_init_geos); - -/* - * il_free_geos - undo allocations in il_init_geos - */ -void -il_free_geos(struct il_priv *il) -{ - kfree(il->ieee_channels); - kfree(il->ieee_rates); - clear_bit(S_GEO_CONFIGURED, &il->status); -} -EXPORT_SYMBOL(il_free_geos); - -static bool -il_is_channel_extension(struct il_priv *il, enum ieee80211_band band, - u16 channel, u8 extension_chan_offset) -{ - const struct il_channel_info *ch_info; - - ch_info = il_get_channel_info(il, band, channel); - if (!il_is_channel_valid(ch_info)) - return false; - - if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) - return !(ch_info-> - ht40_extension_channel & IEEE80211_CHAN_NO_HT40PLUS); - else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW) - return !(ch_info-> - ht40_extension_channel & IEEE80211_CHAN_NO_HT40MINUS); - - return false; -} - -bool -il_is_ht40_tx_allowed(struct il_priv *il, struct ieee80211_sta_ht_cap *ht_cap) -{ - if (!il->ht.enabled || !il->ht.is_40mhz) - return false; - - /* - * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 - * the bit will not set if it is pure 40MHz case - */ - if (ht_cap && !ht_cap->ht_supported) - return false; - -#ifdef CONFIG_IWLEGACY_DEBUGFS - if (il->disable_ht40) - return false; -#endif - - return il_is_channel_extension(il, il->band, - le16_to_cpu(il->staging.channel), - il->ht.extension_chan_offset); -} -EXPORT_SYMBOL(il_is_ht40_tx_allowed); - -static u16 -il_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) -{ - u16 new_val; - u16 beacon_factor; - - /* - * If mac80211 hasn't given us a beacon interval, program - * the default into the device. - */ - if (!beacon_val) - return DEFAULT_BEACON_INTERVAL; - - /* - * If the beacon interval we obtained from the peer - * is too large, we'll have to wake up more often - * (and in IBSS case, we'll beacon too much) - * - * For example, if max_beacon_val is 4096, and the - * requested beacon interval is 7000, we'll have to - * use 3500 to be able to wake up on the beacons. - * - * This could badly influence beacon detection stats. - */ - - beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; - new_val = beacon_val / beacon_factor; - - if (!new_val) - new_val = max_beacon_val; - - return new_val; -} - -int -il_send_rxon_timing(struct il_priv *il) -{ - u64 tsf; - s32 interval_tm, rem; - struct ieee80211_conf *conf = NULL; - u16 beacon_int; - struct ieee80211_vif *vif = il->vif; - - conf = &il->hw->conf; - - lockdep_assert_held(&il->mutex); - - memset(&il->timing, 0, sizeof(struct il_rxon_time_cmd)); - - il->timing.timestamp = cpu_to_le64(il->timestamp); - il->timing.listen_interval = cpu_to_le16(conf->listen_interval); - - beacon_int = vif ? vif->bss_conf.beacon_int : 0; - - /* - * TODO: For IBSS we need to get atim_win from mac80211, - * for now just always use 0 - */ - il->timing.atim_win = 0; - - beacon_int = - il_adjust_beacon_interval(beacon_int, - il->hw_params.max_beacon_itrvl * - TIME_UNIT); - il->timing.beacon_interval = cpu_to_le16(beacon_int); - - tsf = il->timestamp; /* tsf is modifed by do_div: copy it */ - interval_tm = beacon_int * TIME_UNIT; - rem = do_div(tsf, interval_tm); - il->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); - - il->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ? : 1) : 1; - - D_ASSOC("beacon interval %d beacon timer %d beacon tim %d\n", - le16_to_cpu(il->timing.beacon_interval), - le32_to_cpu(il->timing.beacon_init_val), - le16_to_cpu(il->timing.atim_win)); - - return il_send_cmd_pdu(il, C_RXON_TIMING, sizeof(il->timing), - &il->timing); -} -EXPORT_SYMBOL(il_send_rxon_timing); - -void -il_set_rxon_hwcrypto(struct il_priv *il, int hw_decrypt) -{ - struct il_rxon_cmd *rxon = &il->staging; - - if (hw_decrypt) - rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; - else - rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; - -} -EXPORT_SYMBOL(il_set_rxon_hwcrypto); - -/* validate RXON structure is valid */ -int -il_check_rxon_cmd(struct il_priv *il) -{ - struct il_rxon_cmd *rxon = &il->staging; - bool error = false; - - if (rxon->flags & RXON_FLG_BAND_24G_MSK) { - if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) { - IL_WARN("check 2.4G: wrong narrow\n"); - error = true; - } - if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) { - IL_WARN("check 2.4G: wrong radar\n"); - error = true; - } - } else { - if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) { - IL_WARN("check 5.2G: not short slot!\n"); - error = true; - } - if (rxon->flags & RXON_FLG_CCK_MSK) { - IL_WARN("check 5.2G: CCK!\n"); - error = true; - } - } - if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) { - IL_WARN("mac/bssid mcast!\n"); - error = true; - } - - /* make sure basic rates 6Mbps and 1Mbps are supported */ - if ((rxon->ofdm_basic_rates & RATE_6M_MASK) == 0 && - (rxon->cck_basic_rates & RATE_1M_MASK) == 0) { - IL_WARN("neither 1 nor 6 are basic\n"); - error = true; - } - - if (le16_to_cpu(rxon->assoc_id) > 2007) { - IL_WARN("aid > 2007\n"); - error = true; - } - - if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) == - (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) { - IL_WARN("CCK and short slot\n"); - error = true; - } - - if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) == - (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { - IL_WARN("CCK and auto detect"); - error = true; - } - - if ((rxon-> - flags & (RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK)) == - RXON_FLG_TGG_PROTECT_MSK) { - IL_WARN("TGg but no auto-detect\n"); - error = true; - } - - if (error) - IL_WARN("Tuning to channel %d\n", le16_to_cpu(rxon->channel)); - - if (error) { - IL_ERR("Invalid RXON\n"); - return -EINVAL; - } - return 0; -} -EXPORT_SYMBOL(il_check_rxon_cmd); - -/** - * il_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed - * @il: staging_rxon is compared to active_rxon - * - * If the RXON structure is changing enough to require a new tune, - * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that - * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. - */ -int -il_full_rxon_required(struct il_priv *il) -{ - const struct il_rxon_cmd *staging = &il->staging; - const struct il_rxon_cmd *active = &il->active; - -#define CHK(cond) \ - if ((cond)) { \ - D_INFO("need full RXON - " #cond "\n"); \ - return 1; \ - } - -#define CHK_NEQ(c1, c2) \ - if ((c1) != (c2)) { \ - D_INFO("need full RXON - " \ - #c1 " != " #c2 " - %d != %d\n", \ - (c1), (c2)); \ - return 1; \ - } - - /* These items are only settable from the full RXON command */ - CHK(!il_is_associated(il)); - CHK(!ether_addr_equal_64bits(staging->bssid_addr, active->bssid_addr)); - CHK(!ether_addr_equal_64bits(staging->node_addr, active->node_addr)); - CHK(!ether_addr_equal_64bits(staging->wlap_bssid_addr, - active->wlap_bssid_addr)); - CHK_NEQ(staging->dev_type, active->dev_type); - CHK_NEQ(staging->channel, active->channel); - CHK_NEQ(staging->air_propagation, active->air_propagation); - CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, - active->ofdm_ht_single_stream_basic_rates); - CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, - active->ofdm_ht_dual_stream_basic_rates); - CHK_NEQ(staging->assoc_id, active->assoc_id); - - /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can - * be updated with the RXON_ASSOC command -- however only some - * flag transitions are allowed using RXON_ASSOC */ - - /* Check if we are not switching bands */ - CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, - active->flags & RXON_FLG_BAND_24G_MSK); - - /* Check if we are switching association toggle */ - CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, - active->filter_flags & RXON_FILTER_ASSOC_MSK); - -#undef CHK -#undef CHK_NEQ - - return 0; -} -EXPORT_SYMBOL(il_full_rxon_required); - -u8 -il_get_lowest_plcp(struct il_priv *il) -{ - /* - * Assign the lowest rate -- should really get this from - * the beacon skb from mac80211. - */ - if (il->staging.flags & RXON_FLG_BAND_24G_MSK) - return RATE_1M_PLCP; - else - return RATE_6M_PLCP; -} -EXPORT_SYMBOL(il_get_lowest_plcp); - -static void -_il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf) -{ - struct il_rxon_cmd *rxon = &il->staging; - - if (!il->ht.enabled) { - rxon->flags &= - ~(RXON_FLG_CHANNEL_MODE_MSK | - RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK - | RXON_FLG_HT_PROT_MSK); - return; - } - - rxon->flags |= - cpu_to_le32(il->ht.protection << RXON_FLG_HT_OPERATING_MODE_POS); - - /* Set up channel bandwidth: - * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ - /* clear the HT channel mode before set the mode */ - rxon->flags &= - ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); - if (il_is_ht40_tx_allowed(il, NULL)) { - /* pure ht40 */ - if (il->ht.protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { - rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; - /* Note: control channel is opposite of extension channel */ - switch (il->ht.extension_chan_offset) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - rxon->flags &= - ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; - break; - } - } else { - /* Note: control channel is opposite of extension channel */ - switch (il->ht.extension_chan_offset) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - rxon->flags &= - ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); - rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; - rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; - break; - case IEEE80211_HT_PARAM_CHA_SEC_NONE: - default: - /* channel location only valid if in Mixed mode */ - IL_ERR("invalid extension channel offset\n"); - break; - } - } - } else { - rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY; - } - - if (il->ops->set_rxon_chain) - il->ops->set_rxon_chain(il); - - D_ASSOC("rxon flags 0x%X operation mode :0x%X " - "extension channel offset 0x%x\n", le32_to_cpu(rxon->flags), - il->ht.protection, il->ht.extension_chan_offset); -} - -void -il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf) -{ - _il_set_rxon_ht(il, ht_conf); -} -EXPORT_SYMBOL(il_set_rxon_ht); - -/* Return valid, unused, channel for a passive scan to reset the RF */ -u8 -il_get_single_channel_number(struct il_priv *il, enum ieee80211_band band) -{ - const struct il_channel_info *ch_info; - int i; - u8 channel = 0; - u8 min, max; - - if (band == IEEE80211_BAND_5GHZ) { - min = 14; - max = il->channel_count; - } else { - min = 0; - max = 14; - } - - for (i = min; i < max; i++) { - channel = il->channel_info[i].channel; - if (channel == le16_to_cpu(il->staging.channel)) - continue; - - ch_info = il_get_channel_info(il, band, channel); - if (il_is_channel_valid(ch_info)) - break; - } - - return channel; -} -EXPORT_SYMBOL(il_get_single_channel_number); - -/** - * il_set_rxon_channel - Set the band and channel values in staging RXON - * @ch: requested channel as a pointer to struct ieee80211_channel - - * NOTE: Does not commit to the hardware; it sets appropriate bit fields - * in the staging RXON flag structure based on the ch->band - */ -int -il_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch) -{ - enum ieee80211_band band = ch->band; - u16 channel = ch->hw_value; - - if (le16_to_cpu(il->staging.channel) == channel && il->band == band) - return 0; - - il->staging.channel = cpu_to_le16(channel); - if (band == IEEE80211_BAND_5GHZ) - il->staging.flags &= ~RXON_FLG_BAND_24G_MSK; - else - il->staging.flags |= RXON_FLG_BAND_24G_MSK; - - il->band = band; - - D_INFO("Staging channel set to %d [%d]\n", channel, band); - - return 0; -} -EXPORT_SYMBOL(il_set_rxon_channel); - -void -il_set_flags_for_band(struct il_priv *il, enum ieee80211_band band, - struct ieee80211_vif *vif) -{ - if (band == IEEE80211_BAND_5GHZ) { - il->staging.flags &= - ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK | - RXON_FLG_CCK_MSK); - il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - } else { - /* Copied from il_post_associate() */ - if (vif && vif->bss_conf.use_short_slot) - il->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - else - il->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - - il->staging.flags |= RXON_FLG_BAND_24G_MSK; - il->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; - il->staging.flags &= ~RXON_FLG_CCK_MSK; - } -} -EXPORT_SYMBOL(il_set_flags_for_band); - -/* - * initialize rxon structure with default values from eeprom - */ -void -il_connection_init_rx_config(struct il_priv *il) -{ - const struct il_channel_info *ch_info; - - memset(&il->staging, 0, sizeof(il->staging)); - - switch (il->iw_mode) { - case NL80211_IFTYPE_UNSPECIFIED: - il->staging.dev_type = RXON_DEV_TYPE_ESS; - break; - case NL80211_IFTYPE_STATION: - il->staging.dev_type = RXON_DEV_TYPE_ESS; - il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; - break; - case NL80211_IFTYPE_ADHOC: - il->staging.dev_type = RXON_DEV_TYPE_IBSS; - il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; - il->staging.filter_flags = - RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK; - break; - default: - IL_ERR("Unsupported interface type %d\n", il->vif->type); - return; - } - -#if 0 - /* TODO: Figure out when short_preamble would be set and cache from - * that */ - if (!hw_to_local(il->hw)->short_preamble) - il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - else - il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; -#endif - - ch_info = - il_get_channel_info(il, il->band, le16_to_cpu(il->active.channel)); - - if (!ch_info) - ch_info = &il->channel_info[0]; - - il->staging.channel = cpu_to_le16(ch_info->channel); - il->band = ch_info->band; - - il_set_flags_for_band(il, il->band, il->vif); - - il->staging.ofdm_basic_rates = - (IL_OFDM_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; - il->staging.cck_basic_rates = - (IL_CCK_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF; - - /* clear both MIX and PURE40 mode flag */ - il->staging.flags &= - ~(RXON_FLG_CHANNEL_MODE_MIXED | RXON_FLG_CHANNEL_MODE_PURE_40); - if (il->vif) - memcpy(il->staging.node_addr, il->vif->addr, ETH_ALEN); - - il->staging.ofdm_ht_single_stream_basic_rates = 0xff; - il->staging.ofdm_ht_dual_stream_basic_rates = 0xff; -} -EXPORT_SYMBOL(il_connection_init_rx_config); - -void -il_set_rate(struct il_priv *il) -{ - const struct ieee80211_supported_band *hw = NULL; - struct ieee80211_rate *rate; - int i; - - hw = il_get_hw_mode(il, il->band); - if (!hw) { - IL_ERR("Failed to set rate: unable to get hw mode\n"); - return; - } - - il->active_rate = 0; - - for (i = 0; i < hw->n_bitrates; i++) { - rate = &(hw->bitrates[i]); - if (rate->hw_value < RATE_COUNT_LEGACY) - il->active_rate |= (1 << rate->hw_value); - } - - D_RATE("Set active_rate = %0x\n", il->active_rate); - - il->staging.cck_basic_rates = - (IL_CCK_BASIC_RATES_MASK >> IL_FIRST_CCK_RATE) & 0xF; - - il->staging.ofdm_basic_rates = - (IL_OFDM_BASIC_RATES_MASK >> IL_FIRST_OFDM_RATE) & 0xFF; -} -EXPORT_SYMBOL(il_set_rate); - -void -il_chswitch_done(struct il_priv *il, bool is_success) -{ - if (test_bit(S_EXIT_PENDING, &il->status)) - return; - - if (test_and_clear_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) - ieee80211_chswitch_done(il->vif, is_success); -} -EXPORT_SYMBOL(il_chswitch_done); - -void -il_hdl_csa(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_csa_notification *csa = &(pkt->u.csa_notif); - struct il_rxon_cmd *rxon = (void *)&il->active; - - if (!test_bit(S_CHANNEL_SWITCH_PENDING, &il->status)) - return; - - if (!le32_to_cpu(csa->status) && csa->channel == il->switch_channel) { - rxon->channel = csa->channel; - il->staging.channel = csa->channel; - D_11H("CSA notif: channel %d\n", le16_to_cpu(csa->channel)); - il_chswitch_done(il, true); - } else { - IL_ERR("CSA notif (fail) : channel %d\n", - le16_to_cpu(csa->channel)); - il_chswitch_done(il, false); - } -} -EXPORT_SYMBOL(il_hdl_csa); - -#ifdef CONFIG_IWLEGACY_DEBUG -void -il_print_rx_config_cmd(struct il_priv *il) -{ - struct il_rxon_cmd *rxon = &il->staging; - - D_RADIO("RX CONFIG:\n"); - il_print_hex_dump(il, IL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); - D_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel)); - D_RADIO("u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags)); - D_RADIO("u32 filter_flags: 0x%08x\n", le32_to_cpu(rxon->filter_flags)); - D_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type); - D_RADIO("u8 ofdm_basic_rates: 0x%02x\n", rxon->ofdm_basic_rates); - D_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates); - D_RADIO("u8[6] node_addr: %pM\n", rxon->node_addr); - D_RADIO("u8[6] bssid_addr: %pM\n", rxon->bssid_addr); - D_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); -} -EXPORT_SYMBOL(il_print_rx_config_cmd); -#endif -/** - * il_irq_handle_error - called for HW or SW error interrupt from card - */ -void -il_irq_handle_error(struct il_priv *il) -{ - /* Set the FW error flag -- cleared on il_down */ - set_bit(S_FW_ERROR, &il->status); - - /* Cancel currently queued command. */ - clear_bit(S_HCMD_ACTIVE, &il->status); - - IL_ERR("Loaded firmware version: %s\n", il->hw->wiphy->fw_version); - - il->ops->dump_nic_error_log(il); - if (il->ops->dump_fh) - il->ops->dump_fh(il, NULL, false); -#ifdef CONFIG_IWLEGACY_DEBUG - if (il_get_debug_level(il) & IL_DL_FW_ERRORS) - il_print_rx_config_cmd(il); -#endif - - wake_up(&il->wait_command_queue); - - /* Keep the restart process from trying to send host - * commands by clearing the INIT status bit */ - clear_bit(S_READY, &il->status); - - if (!test_bit(S_EXIT_PENDING, &il->status)) { - IL_DBG(IL_DL_FW_ERRORS, - "Restarting adapter due to uCode error.\n"); - - if (il->cfg->mod_params->restart_fw) - queue_work(il->workqueue, &il->restart); - } -} -EXPORT_SYMBOL(il_irq_handle_error); - -static int -_il_apm_stop_master(struct il_priv *il) -{ - int ret = 0; - - /* stop device's busmaster DMA activity */ - _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); - - ret = - _il_poll_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED, - CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); - if (ret < 0) - IL_WARN("Master Disable Timed Out, 100 usec\n"); - - D_INFO("stop master\n"); - - return ret; -} - -void -_il_apm_stop(struct il_priv *il) -{ - lockdep_assert_held(&il->reg_lock); - - D_INFO("Stop card, put in low power state\n"); - - /* Stop device's DMA activity */ - _il_apm_stop_master(il); - - /* Reset the entire device */ - _il_set_bit(il, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - - udelay(10); - - /* - * Clear "initialization complete" bit to move adapter from - * D0A* (powered-up Active) --> D0U* (Uninitialized) state. - */ - _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); -} -EXPORT_SYMBOL(_il_apm_stop); - -void -il_apm_stop(struct il_priv *il) -{ - unsigned long flags; - - spin_lock_irqsave(&il->reg_lock, flags); - _il_apm_stop(il); - spin_unlock_irqrestore(&il->reg_lock, flags); -} -EXPORT_SYMBOL(il_apm_stop); - -/* - * Start up NIC's basic functionality after it has been reset - * (e.g. after platform boot, or shutdown via il_apm_stop()) - * NOTE: This does not load uCode nor start the embedded processor - */ -int -il_apm_init(struct il_priv *il) -{ - int ret = 0; - u16 lctl; - - D_INFO("Init card's basic functions\n"); - - /* - * Use "set_bit" below rather than "write", to preserve any hardware - * bits already set by default after reset. - */ - - /* Disable L0S exit timer (platform NMI Work/Around) */ - il_set_bit(il, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); - - /* - * Disable L0s without affecting L1; - * don't wait for ICH L0s (ICH bug W/A) - */ - il_set_bit(il, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); - - /* Set FH wait threshold to maximum (HW error during stress W/A) */ - il_set_bit(il, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); - - /* - * Enable HAP INTA (interrupt from management bus) to - * wake device's PCI Express link L1a -> L0s - * NOTE: This is no-op for 3945 (non-existent bit) - */ - il_set_bit(il, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); - - /* - * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition. - * Check if BIOS (or OS) enabled L1-ASPM on this device. - * If so (likely), disable L0S, so device moves directly L0->L1; - * costs negligible amount of power savings. - * If not (unlikely), enable L0S, so there is at least some - * power savings, even without L1. - */ - if (il->cfg->set_l0s) { - pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &lctl); - if (lctl & PCI_EXP_LNKCTL_ASPM_L1) { - /* L1-ASPM enabled; disable(!) L0S */ - il_set_bit(il, CSR_GIO_REG, - CSR_GIO_REG_VAL_L0S_ENABLED); - D_POWER("L1 Enabled; Disabling L0S\n"); - } else { - /* L1-ASPM disabled; enable(!) L0S */ - il_clear_bit(il, CSR_GIO_REG, - CSR_GIO_REG_VAL_L0S_ENABLED); - D_POWER("L1 Disabled; Enabling L0S\n"); - } - } - - /* Configure analog phase-lock-loop before activating to D0A */ - if (il->cfg->pll_cfg_val) - il_set_bit(il, CSR_ANA_PLL_CFG, - il->cfg->pll_cfg_val); - - /* - * Set "initialization complete" bit to move adapter from - * D0U* --> D0A* (powered-up active) state. - */ - il_set_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - /* - * Wait for clock stabilization; once stabilized, access to - * device-internal resources is supported, e.g. il_wr_prph() - * and accesses to uCode SRAM. - */ - ret = - _il_poll_bit(il, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); - if (ret < 0) { - D_INFO("Failed to init the card\n"); - goto out; - } - - /* - * Enable DMA and BSM (if used) clocks, wait for them to stabilize. - * BSM (Boostrap State Machine) is only in 3945 and 4965. - * - * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits - * do not disable clocks. This preserves any hardware bits already - * set by default in "CLK_CTRL_REG" after reset. - */ - if (il->cfg->use_bsm) - il_wr_prph(il, APMG_CLK_EN_REG, - APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT); - else - il_wr_prph(il, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); - udelay(20); - - /* Disable L1-Active */ - il_set_bits_prph(il, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - -out: - return ret; -} -EXPORT_SYMBOL(il_apm_init); - -int -il_set_tx_power(struct il_priv *il, s8 tx_power, bool force) -{ - int ret; - s8 prev_tx_power; - bool defer; - - lockdep_assert_held(&il->mutex); - - if (il->tx_power_user_lmt == tx_power && !force) - return 0; - - if (!il->ops->send_tx_power) - return -EOPNOTSUPP; - - /* 0 dBm mean 1 milliwatt */ - if (tx_power < 0) { - IL_WARN("Requested user TXPOWER %d below 1 mW.\n", tx_power); - return -EINVAL; - } - - if (tx_power > il->tx_power_device_lmt) { - IL_WARN("Requested user TXPOWER %d above upper limit %d.\n", - tx_power, il->tx_power_device_lmt); - return -EINVAL; - } - - if (!il_is_ready_rf(il)) - return -EIO; - - /* scan complete and commit_rxon use tx_power_next value, - * it always need to be updated for newest request */ - il->tx_power_next = tx_power; - - /* do not set tx power when scanning or channel changing */ - defer = test_bit(S_SCANNING, &il->status) || - memcmp(&il->active, &il->staging, sizeof(il->staging)); - if (defer && !force) { - D_INFO("Deferring tx power set\n"); - return 0; - } - - prev_tx_power = il->tx_power_user_lmt; - il->tx_power_user_lmt = tx_power; - - ret = il->ops->send_tx_power(il); - - /* if fail to set tx_power, restore the orig. tx power */ - if (ret) { - il->tx_power_user_lmt = prev_tx_power; - il->tx_power_next = prev_tx_power; - } - return ret; -} -EXPORT_SYMBOL(il_set_tx_power); - -void -il_send_bt_config(struct il_priv *il) -{ - struct il_bt_cmd bt_cmd = { - .lead_time = BT_LEAD_TIME_DEF, - .max_kill = BT_MAX_KILL_DEF, - .kill_ack_mask = 0, - .kill_cts_mask = 0, - }; - - if (!bt_coex_active) - bt_cmd.flags = BT_COEX_DISABLE; - else - bt_cmd.flags = BT_COEX_ENABLE; - - D_INFO("BT coex %s\n", - (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); - - if (il_send_cmd_pdu(il, C_BT_CONFIG, sizeof(struct il_bt_cmd), &bt_cmd)) - IL_ERR("failed to send BT Coex Config\n"); -} -EXPORT_SYMBOL(il_send_bt_config); - -int -il_send_stats_request(struct il_priv *il, u8 flags, bool clear) -{ - struct il_stats_cmd stats_cmd = { - .configuration_flags = clear ? IL_STATS_CONF_CLEAR_STATS : 0, - }; - - if (flags & CMD_ASYNC) - return il_send_cmd_pdu_async(il, C_STATS, sizeof(struct il_stats_cmd), - &stats_cmd, NULL); - else - return il_send_cmd_pdu(il, C_STATS, sizeof(struct il_stats_cmd), - &stats_cmd); -} -EXPORT_SYMBOL(il_send_stats_request); - -void -il_hdl_pm_sleep(struct il_priv *il, struct il_rx_buf *rxb) -{ -#ifdef CONFIG_IWLEGACY_DEBUG - struct il_rx_pkt *pkt = rxb_addr(rxb); - struct il_sleep_notification *sleep = &(pkt->u.sleep_notif); - D_RX("sleep mode: %d, src: %d\n", - sleep->pm_sleep_mode, sleep->pm_wakeup_src); -#endif -} -EXPORT_SYMBOL(il_hdl_pm_sleep); - -void -il_hdl_pm_debug_stats(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - u32 len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; - D_RADIO("Dumping %d bytes of unhandled notification for %s:\n", len, - il_get_cmd_string(pkt->hdr.cmd)); - il_print_hex_dump(il, IL_DL_RADIO, pkt->u.raw, len); -} -EXPORT_SYMBOL(il_hdl_pm_debug_stats); - -void -il_hdl_error(struct il_priv *il, struct il_rx_buf *rxb) -{ - struct il_rx_pkt *pkt = rxb_addr(rxb); - - IL_ERR("Error Reply type 0x%08X cmd %s (0x%02X) " - "seq 0x%04X ser 0x%08X\n", - le32_to_cpu(pkt->u.err_resp.error_type), - il_get_cmd_string(pkt->u.err_resp.cmd_id), - pkt->u.err_resp.cmd_id, - le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num), - le32_to_cpu(pkt->u.err_resp.error_info)); -} -EXPORT_SYMBOL(il_hdl_error); - -void -il_clear_isr_stats(struct il_priv *il) -{ - memset(&il->isr_stats, 0, sizeof(il->isr_stats)); -} - -int -il_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct il_priv *il = hw->priv; - unsigned long flags; - int q; - - D_MAC80211("enter\n"); - - if (!il_is_ready_rf(il)) { - D_MAC80211("leave - RF not ready\n"); - return -EIO; - } - - if (queue >= AC_NUM) { - D_MAC80211("leave - queue >= AC_NUM %d\n", queue); - return 0; - } - - q = AC_NUM - 1 - queue; - - spin_lock_irqsave(&il->lock, flags); - - il->qos_data.def_qos_parm.ac[q].cw_min = - cpu_to_le16(params->cw_min); - il->qos_data.def_qos_parm.ac[q].cw_max = - cpu_to_le16(params->cw_max); - il->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; - il->qos_data.def_qos_parm.ac[q].edca_txop = - cpu_to_le16((params->txop * 32)); - - il->qos_data.def_qos_parm.ac[q].reserved1 = 0; - - spin_unlock_irqrestore(&il->lock, flags); - - D_MAC80211("leave\n"); - return 0; -} -EXPORT_SYMBOL(il_mac_conf_tx); - -int -il_mac_tx_last_beacon(struct ieee80211_hw *hw) -{ - struct il_priv *il = hw->priv; - int ret; - - D_MAC80211("enter\n"); - - ret = (il->ibss_manager == IL_IBSS_MANAGER); - - D_MAC80211("leave ret %d\n", ret); - return ret; -} -EXPORT_SYMBOL_GPL(il_mac_tx_last_beacon); - -static int -il_set_mode(struct il_priv *il) -{ - il_connection_init_rx_config(il); - - if (il->ops->set_rxon_chain) - il->ops->set_rxon_chain(il); - - return il_commit_rxon(il); -} - -int -il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct il_priv *il = hw->priv; - int err; - bool reset; - - mutex_lock(&il->mutex); - D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); - - if (!il_is_ready_rf(il)) { - IL_WARN("Try to add interface when device not ready\n"); - err = -EINVAL; - goto out; - } - - /* - * We do not support multiple virtual interfaces, but on hardware reset - * we have to add the same interface again. - */ - reset = (il->vif == vif); - if (il->vif && !reset) { - err = -EOPNOTSUPP; - goto out; - } - - il->vif = vif; - il->iw_mode = vif->type; - - err = il_set_mode(il); - if (err) { - IL_WARN("Fail to set mode %d\n", vif->type); - if (!reset) { - il->vif = NULL; - il->iw_mode = NL80211_IFTYPE_STATION; - } - } - -out: - D_MAC80211("leave err %d\n", err); - mutex_unlock(&il->mutex); - - return err; -} -EXPORT_SYMBOL(il_mac_add_interface); - -static void -il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif) -{ - lockdep_assert_held(&il->mutex); - - if (il->scan_vif == vif) { - il_scan_cancel_timeout(il, 200); - il_force_scan_end(il); - } - - il_set_mode(il); -} - -void -il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct il_priv *il = hw->priv; - - mutex_lock(&il->mutex); - D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); - - WARN_ON(il->vif != vif); - il->vif = NULL; - il->iw_mode = NL80211_IFTYPE_UNSPECIFIED; - il_teardown_interface(il, vif); - eth_zero_addr(il->bssid); - - D_MAC80211("leave\n"); - mutex_unlock(&il->mutex); -} -EXPORT_SYMBOL(il_mac_remove_interface); - -int -il_alloc_txq_mem(struct il_priv *il) -{ - if (!il->txq) - il->txq = - kzalloc(sizeof(struct il_tx_queue) * - il->cfg->num_of_queues, GFP_KERNEL); - if (!il->txq) { - IL_ERR("Not enough memory for txq\n"); - return -ENOMEM; - } - return 0; -} -EXPORT_SYMBOL(il_alloc_txq_mem); - -void -il_free_txq_mem(struct il_priv *il) -{ - kfree(il->txq); - il->txq = NULL; -} -EXPORT_SYMBOL(il_free_txq_mem); - -int -il_force_reset(struct il_priv *il, bool external) -{ - struct il_force_reset *force_reset; - - if (test_bit(S_EXIT_PENDING, &il->status)) - return -EINVAL; - - force_reset = &il->force_reset; - force_reset->reset_request_count++; - if (!external) { - if (force_reset->last_force_reset_jiffies && - time_after(force_reset->last_force_reset_jiffies + - force_reset->reset_duration, jiffies)) { - D_INFO("force reset rejected\n"); - force_reset->reset_reject_count++; - return -EAGAIN; - } - } - force_reset->reset_success_count++; - force_reset->last_force_reset_jiffies = jiffies; - - /* - * if the request is from external(ex: debugfs), - * then always perform the request in regardless the module - * parameter setting - * if the request is from internal (uCode error or driver - * detect failure), then fw_restart module parameter - * need to be check before performing firmware reload - */ - - if (!external && !il->cfg->mod_params->restart_fw) { - D_INFO("Cancel firmware reload based on " - "module parameter setting\n"); - return 0; - } - - IL_ERR("On demand firmware reload\n"); - - /* Set the FW error flag -- cleared on il_down */ - set_bit(S_FW_ERROR, &il->status); - wake_up(&il->wait_command_queue); - /* - * Keep the restart process from trying to send host - * commands by clearing the INIT status bit - */ - clear_bit(S_READY, &il->status); - queue_work(il->workqueue, &il->restart); - - return 0; -} -EXPORT_SYMBOL(il_force_reset); - -int -il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum nl80211_iftype newtype, bool newp2p) -{ - struct il_priv *il = hw->priv; - int err; - - mutex_lock(&il->mutex); - D_MAC80211("enter: type %d, addr %pM newtype %d newp2p %d\n", - vif->type, vif->addr, newtype, newp2p); - - if (newp2p) { - err = -EOPNOTSUPP; - goto out; - } - - if (!il->vif || !il_is_ready_rf(il)) { - /* - * Huh? But wait ... this can maybe happen when - * we're in the middle of a firmware restart! - */ - err = -EBUSY; - goto out; - } - - /* success */ - vif->type = newtype; - vif->p2p = false; - il->iw_mode = newtype; - il_teardown_interface(il, vif); - err = 0; - -out: - D_MAC80211("leave err %d\n", err); - mutex_unlock(&il->mutex); - - return err; -} -EXPORT_SYMBOL(il_mac_change_interface); - -void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 queues, bool drop) -{ - struct il_priv *il = hw->priv; - unsigned long timeout = jiffies + msecs_to_jiffies(500); - int i; - - mutex_lock(&il->mutex); - D_MAC80211("enter\n"); - - if (il->txq == NULL) - goto out; - - for (i = 0; i < il->hw_params.max_txq_num; i++) { - struct il_queue *q; - - if (i == il->cmd_queue) - continue; - - q = &il->txq[i].q; - if (q->read_ptr == q->write_ptr) - continue; - - if (time_after(jiffies, timeout)) { - IL_ERR("Failed to flush queue %d\n", q->id); - break; - } - - msleep(20); - } -out: - D_MAC80211("leave\n"); - mutex_unlock(&il->mutex); -} -EXPORT_SYMBOL(il_mac_flush); - -/* - * On every watchdog tick we check (latest) time stamp. If it does not - * change during timeout period and queue is not empty we reset firmware. - */ -static int -il_check_stuck_queue(struct il_priv *il, int cnt) -{ - struct il_tx_queue *txq = &il->txq[cnt]; - struct il_queue *q = &txq->q; - unsigned long timeout; - unsigned long now = jiffies; - int ret; - - if (q->read_ptr == q->write_ptr) { - txq->time_stamp = now; - return 0; - } - - timeout = - txq->time_stamp + - msecs_to_jiffies(il->cfg->wd_timeout); - - if (time_after(now, timeout)) { - IL_ERR("Queue %d stuck for %u ms.\n", q->id, - jiffies_to_msecs(now - txq->time_stamp)); - ret = il_force_reset(il, false); - return (ret == -EAGAIN) ? 0 : 1; - } - - return 0; -} - -/* - * Making watchdog tick be a quarter of timeout assure we will - * discover the queue hung between timeout and 1.25*timeout - */ -#define IL_WD_TICK(timeout) ((timeout) / 4) - -/* - * Watchdog timer callback, we check each tx queue for stuck, if if hung - * we reset the firmware. If everything is fine just rearm the timer. - */ -void -il_bg_watchdog(unsigned long data) -{ - struct il_priv *il = (struct il_priv *)data; - int cnt; - unsigned long timeout; - - if (test_bit(S_EXIT_PENDING, &il->status)) - return; - - timeout = il->cfg->wd_timeout; - if (timeout == 0) - return; - - /* monitor and check for stuck cmd queue */ - if (il_check_stuck_queue(il, il->cmd_queue)) - return; - - /* monitor and check for other stuck queues */ - for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) { - /* skip as we already checked the command queue */ - if (cnt == il->cmd_queue) - continue; - if (il_check_stuck_queue(il, cnt)) - return; - } - - mod_timer(&il->watchdog, - jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); -} -EXPORT_SYMBOL(il_bg_watchdog); - -void -il_setup_watchdog(struct il_priv *il) -{ - unsigned int timeout = il->cfg->wd_timeout; - - if (timeout) - mod_timer(&il->watchdog, - jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); - else - del_timer(&il->watchdog); -} -EXPORT_SYMBOL(il_setup_watchdog); - -/* - * extended beacon time format - * time in usec will be changed into a 32-bit value in extended:internal format - * the extended part is the beacon counts - * the internal part is the time in usec within one beacon interval - */ -u32 -il_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval) -{ - u32 quot; - u32 rem; - u32 interval = beacon_interval * TIME_UNIT; - - if (!interval || !usec) - return 0; - - quot = - (usec / - interval) & (il_beacon_time_mask_high(il, - il->hw_params. - beacon_time_tsf_bits) >> il-> - hw_params.beacon_time_tsf_bits); - rem = - (usec % interval) & il_beacon_time_mask_low(il, - il->hw_params. - beacon_time_tsf_bits); - - return (quot << il->hw_params.beacon_time_tsf_bits) + rem; -} -EXPORT_SYMBOL(il_usecs_to_beacons); - -/* base is usually what we get from ucode with each received frame, - * the same as HW timer counter counting down - */ -__le32 -il_add_beacon_time(struct il_priv *il, u32 base, u32 addon, - u32 beacon_interval) -{ - u32 base_low = base & il_beacon_time_mask_low(il, - il->hw_params. - beacon_time_tsf_bits); - u32 addon_low = addon & il_beacon_time_mask_low(il, - il->hw_params. - beacon_time_tsf_bits); - u32 interval = beacon_interval * TIME_UNIT; - u32 res = (base & il_beacon_time_mask_high(il, - il->hw_params. - beacon_time_tsf_bits)) + - (addon & il_beacon_time_mask_high(il, - il->hw_params. - beacon_time_tsf_bits)); - - if (base_low > addon_low) - res += base_low - addon_low; - else if (base_low < addon_low) { - res += interval + base_low - addon_low; - res += (1 << il->hw_params.beacon_time_tsf_bits); - } else - res += (1 << il->hw_params.beacon_time_tsf_bits); - - return cpu_to_le32(res); -} -EXPORT_SYMBOL(il_add_beacon_time); - -#ifdef CONFIG_PM_SLEEP - -static int -il_pci_suspend(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - struct il_priv *il = pci_get_drvdata(pdev); - - /* - * This function is called when system goes into suspend state - * mac80211 will call il_mac_stop() from the mac80211 suspend function - * first but since il_mac_stop() has no knowledge of who the caller is, - * it will not call apm_ops.stop() to stop the DMA operation. - * Calling apm_ops.stop here to make sure we stop the DMA. - */ - il_apm_stop(il); - - return 0; -} - -static int -il_pci_resume(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - struct il_priv *il = pci_get_drvdata(pdev); - bool hw_rfkill = false; - - /* - * We disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state. - */ - pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); - - il_enable_interrupts(il); - - if (!(_il_rd(il, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) - hw_rfkill = true; - - if (hw_rfkill) - set_bit(S_RFKILL, &il->status); - else - clear_bit(S_RFKILL, &il->status); - - wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rfkill); - - return 0; -} - -SIMPLE_DEV_PM_OPS(il_pm_ops, il_pci_suspend, il_pci_resume); -EXPORT_SYMBOL(il_pm_ops); - -#endif /* CONFIG_PM_SLEEP */ - -static void -il_update_qos(struct il_priv *il) -{ - if (test_bit(S_EXIT_PENDING, &il->status)) - return; - - il->qos_data.def_qos_parm.qos_flags = 0; - - if (il->qos_data.qos_active) - il->qos_data.def_qos_parm.qos_flags |= - QOS_PARAM_FLG_UPDATE_EDCA_MSK; - - if (il->ht.enabled) - il->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; - - D_QOS("send QoS cmd with Qos active=%d FLAGS=0x%X\n", - il->qos_data.qos_active, il->qos_data.def_qos_parm.qos_flags); - - il_send_cmd_pdu_async(il, C_QOS_PARAM, sizeof(struct il_qosparam_cmd), - &il->qos_data.def_qos_parm, NULL); -} - -/** - * il_mac_config - mac80211 config callback - */ -int -il_mac_config(struct ieee80211_hw *hw, u32 changed) -{ - struct il_priv *il = hw->priv; - const struct il_channel_info *ch_info; - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_channel *channel = conf->chandef.chan; - struct il_ht_config *ht_conf = &il->current_ht_config; - unsigned long flags = 0; - int ret = 0; - u16 ch; - int scan_active = 0; - bool ht_changed = false; - - mutex_lock(&il->mutex); - D_MAC80211("enter: channel %d changed 0x%X\n", channel->hw_value, - changed); - - if (unlikely(test_bit(S_SCANNING, &il->status))) { - scan_active = 1; - D_MAC80211("scan active\n"); - } - - if (changed & - (IEEE80211_CONF_CHANGE_SMPS | IEEE80211_CONF_CHANGE_CHANNEL)) { - /* mac80211 uses static for non-HT which is what we want */ - il->current_ht_config.smps = conf->smps_mode; - - /* - * Recalculate chain counts. - * - * If monitor mode is enabled then mac80211 will - * set up the SM PS mode to OFF if an HT channel is - * configured. - */ - if (il->ops->set_rxon_chain) - il->ops->set_rxon_chain(il); - } - - /* during scanning mac80211 will delay channel setting until - * scan finish with changed = 0 - */ - if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { - - if (scan_active) - goto set_ch_out; - - ch = channel->hw_value; - ch_info = il_get_channel_info(il, channel->band, ch); - if (!il_is_channel_valid(ch_info)) { - D_MAC80211("leave - invalid channel\n"); - ret = -EINVAL; - goto set_ch_out; - } - - if (il->iw_mode == NL80211_IFTYPE_ADHOC && - !il_is_channel_ibss(ch_info)) { - D_MAC80211("leave - not IBSS channel\n"); - ret = -EINVAL; - goto set_ch_out; - } - - spin_lock_irqsave(&il->lock, flags); - - /* Configure HT40 channels */ - if (il->ht.enabled != conf_is_ht(conf)) { - il->ht.enabled = conf_is_ht(conf); - ht_changed = true; - } - if (il->ht.enabled) { - if (conf_is_ht40_minus(conf)) { - il->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_BELOW; - il->ht.is_40mhz = true; - } else if (conf_is_ht40_plus(conf)) { - il->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - il->ht.is_40mhz = true; - } else { - il->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_NONE; - il->ht.is_40mhz = false; - } - } else - il->ht.is_40mhz = false; - - /* - * Default to no protection. Protection mode will - * later be set from BSS config in il_ht_conf - */ - il->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; - - /* if we are switching from ht to 2.4 clear flags - * from any ht related info since 2.4 does not - * support ht */ - if ((le16_to_cpu(il->staging.channel) != ch)) - il->staging.flags = 0; - - il_set_rxon_channel(il, channel); - il_set_rxon_ht(il, ht_conf); - - il_set_flags_for_band(il, channel->band, il->vif); - - spin_unlock_irqrestore(&il->lock, flags); - - if (il->ops->update_bcast_stations) - ret = il->ops->update_bcast_stations(il); - -set_ch_out: - /* The list of supported rates and rate mask can be different - * for each band; since the band may have changed, reset - * the rate mask to what mac80211 lists */ - il_set_rate(il); - } - - if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) { - il->power_data.ps_disabled = !(conf->flags & IEEE80211_CONF_PS); - ret = il_power_update_mode(il, false); - if (ret) - D_MAC80211("Error setting sleep level\n"); - } - - if (changed & IEEE80211_CONF_CHANGE_POWER) { - D_MAC80211("TX Power old=%d new=%d\n", il->tx_power_user_lmt, - conf->power_level); - - il_set_tx_power(il, conf->power_level, false); - } - - if (!il_is_ready(il)) { - D_MAC80211("leave - not ready\n"); - goto out; - } - - if (scan_active) - goto out; - - if (memcmp(&il->active, &il->staging, sizeof(il->staging))) - il_commit_rxon(il); - else - D_INFO("Not re-sending same RXON configuration.\n"); - if (ht_changed) - il_update_qos(il); - -out: - D_MAC80211("leave ret %d\n", ret); - mutex_unlock(&il->mutex); - - return ret; -} -EXPORT_SYMBOL(il_mac_config); - -void -il_mac_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct il_priv *il = hw->priv; - unsigned long flags; - - mutex_lock(&il->mutex); - D_MAC80211("enter: type %d, addr %pM\n", vif->type, vif->addr); - - spin_lock_irqsave(&il->lock, flags); - - memset(&il->current_ht_config, 0, sizeof(struct il_ht_config)); - - /* new association get rid of ibss beacon skb */ - if (il->beacon_skb) - dev_kfree_skb(il->beacon_skb); - il->beacon_skb = NULL; - il->timestamp = 0; - - spin_unlock_irqrestore(&il->lock, flags); - - il_scan_cancel_timeout(il, 100); - if (!il_is_ready_rf(il)) { - D_MAC80211("leave - not ready\n"); - mutex_unlock(&il->mutex); - return; - } - - /* we are restarting association process */ - il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - il_commit_rxon(il); - - il_set_rate(il); - - D_MAC80211("leave\n"); - mutex_unlock(&il->mutex); -} -EXPORT_SYMBOL(il_mac_reset_tsf); - -static void -il_ht_conf(struct il_priv *il, struct ieee80211_vif *vif) -{ - struct il_ht_config *ht_conf = &il->current_ht_config; - struct ieee80211_sta *sta; - struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - - D_ASSOC("enter:\n"); - - if (!il->ht.enabled) - return; - - il->ht.protection = - bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; - il->ht.non_gf_sta_present = - !!(bss_conf-> - ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - - ht_conf->single_chain_sufficient = false; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - rcu_read_lock(); - sta = ieee80211_find_sta(vif, bss_conf->bssid); - if (sta) { - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - int maxstreams; - - maxstreams = - (ht_cap->mcs. - tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) - >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; - maxstreams += 1; - - if (ht_cap->mcs.rx_mask[1] == 0 && - ht_cap->mcs.rx_mask[2] == 0) - ht_conf->single_chain_sufficient = true; - if (maxstreams <= 1) - ht_conf->single_chain_sufficient = true; - } else { - /* - * If at all, this can only happen through a race - * when the AP disconnects us while we're still - * setting up the connection, in that case mac80211 - * will soon tell us about that. - */ - ht_conf->single_chain_sufficient = true; - } - rcu_read_unlock(); - break; - case NL80211_IFTYPE_ADHOC: - ht_conf->single_chain_sufficient = true; - break; - default: - break; - } - - D_ASSOC("leave\n"); -} - -static inline void -il_set_no_assoc(struct il_priv *il, struct ieee80211_vif *vif) -{ - /* - * inform the ucode that there is no longer an - * association and that no more packets should be - * sent - */ - il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - il->staging.assoc_id = 0; - il_commit_rxon(il); -} - -static void -il_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct il_priv *il = hw->priv; - unsigned long flags; - __le64 timestamp; - struct sk_buff *skb = ieee80211_beacon_get(hw, vif); - - if (!skb) - return; - - D_MAC80211("enter\n"); - - lockdep_assert_held(&il->mutex); - - if (!il->beacon_enabled) { - IL_ERR("update beacon with no beaconing enabled\n"); - dev_kfree_skb(skb); - return; - } - - spin_lock_irqsave(&il->lock, flags); - - if (il->beacon_skb) - dev_kfree_skb(il->beacon_skb); - - il->beacon_skb = skb; - - timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; - il->timestamp = le64_to_cpu(timestamp); - - D_MAC80211("leave\n"); - spin_unlock_irqrestore(&il->lock, flags); - - if (!il_is_ready_rf(il)) { - D_MAC80211("leave - RF not ready\n"); - return; - } - - il->ops->post_associate(il); -} - -void -il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, u32 changes) -{ - struct il_priv *il = hw->priv; - int ret; - - mutex_lock(&il->mutex); - D_MAC80211("enter: changes 0x%x\n", changes); - - if (!il_is_alive(il)) { - D_MAC80211("leave - not alive\n"); - mutex_unlock(&il->mutex); - return; - } - - if (changes & BSS_CHANGED_QOS) { - unsigned long flags; - - spin_lock_irqsave(&il->lock, flags); - il->qos_data.qos_active = bss_conf->qos; - il_update_qos(il); - spin_unlock_irqrestore(&il->lock, flags); - } - - if (changes & BSS_CHANGED_BEACON_ENABLED) { - /* FIXME: can we remove beacon_enabled ? */ - if (vif->bss_conf.enable_beacon) - il->beacon_enabled = true; - else - il->beacon_enabled = false; - } - - if (changes & BSS_CHANGED_BSSID) { - D_MAC80211("BSSID %pM\n", bss_conf->bssid); - - /* - * On passive channel we wait with blocked queues to see if - * there is traffic on that channel. If no frame will be - * received (what is very unlikely since scan detects AP on - * that channel, but theoretically possible), mac80211 associate - * procedure will time out and mac80211 will call us with NULL - * bssid. We have to unblock queues on such condition. - */ - if (is_zero_ether_addr(bss_conf->bssid)) - il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); - - /* - * If there is currently a HW scan going on in the background, - * then we need to cancel it, otherwise sometimes we are not - * able to authenticate (FIXME: why ?) - */ - if (il_scan_cancel_timeout(il, 100)) { - D_MAC80211("leave - scan abort failed\n"); - mutex_unlock(&il->mutex); - return; - } - - /* mac80211 only sets assoc when in STATION mode */ - memcpy(il->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); - - /* FIXME: currently needed in a few places */ - memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); - } - - /* - * This needs to be after setting the BSSID in case - * mac80211 decides to do both changes at once because - * it will invoke post_associate. - */ - if (vif->type == NL80211_IFTYPE_ADHOC && (changes & BSS_CHANGED_BEACON)) - il_beacon_update(hw, vif); - - if (changes & BSS_CHANGED_ERP_PREAMBLE) { - D_MAC80211("ERP_PREAMBLE %d\n", bss_conf->use_short_preamble); - if (bss_conf->use_short_preamble) - il->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; - else - il->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - } - - if (changes & BSS_CHANGED_ERP_CTS_PROT) { - D_MAC80211("ERP_CTS %d\n", bss_conf->use_cts_prot); - if (bss_conf->use_cts_prot && il->band != IEEE80211_BAND_5GHZ) - il->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; - else - il->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; - if (bss_conf->use_cts_prot) - il->staging.flags |= RXON_FLG_SELF_CTS_EN; - else - il->staging.flags &= ~RXON_FLG_SELF_CTS_EN; - } - - if (changes & BSS_CHANGED_BASIC_RATES) { - /* XXX use this information - * - * To do that, remove code from il_set_rate() and put something - * like this here: - * - if (A-band) - il->staging.ofdm_basic_rates = - bss_conf->basic_rates; - else - il->staging.ofdm_basic_rates = - bss_conf->basic_rates >> 4; - il->staging.cck_basic_rates = - bss_conf->basic_rates & 0xF; - */ - } - - if (changes & BSS_CHANGED_HT) { - il_ht_conf(il, vif); - - if (il->ops->set_rxon_chain) - il->ops->set_rxon_chain(il); - } - - if (changes & BSS_CHANGED_ASSOC) { - D_MAC80211("ASSOC %d\n", bss_conf->assoc); - if (bss_conf->assoc) { - il->timestamp = bss_conf->sync_tsf; - - if (!il_is_rfkill(il)) - il->ops->post_associate(il); - } else - il_set_no_assoc(il, vif); - } - - if (changes && il_is_associated(il) && bss_conf->aid) { - D_MAC80211("Changes (%#x) while associated\n", changes); - ret = il_send_rxon_assoc(il); - if (!ret) { - /* Sync active_rxon with latest change. */ - memcpy((void *)&il->active, &il->staging, - sizeof(struct il_rxon_cmd)); - } - } - - if (changes & BSS_CHANGED_BEACON_ENABLED) { - if (vif->bss_conf.enable_beacon) { - memcpy(il->staging.bssid_addr, bss_conf->bssid, - ETH_ALEN); - memcpy(il->bssid, bss_conf->bssid, ETH_ALEN); - il->ops->config_ap(il); - } else - il_set_no_assoc(il, vif); - } - - if (changes & BSS_CHANGED_IBSS) { - ret = il->ops->manage_ibss_station(il, vif, - bss_conf->ibss_joined); - if (ret) - IL_ERR("failed to %s IBSS station %pM\n", - bss_conf->ibss_joined ? "add" : "remove", - bss_conf->bssid); - } - - D_MAC80211("leave\n"); - mutex_unlock(&il->mutex); -} -EXPORT_SYMBOL(il_mac_bss_info_changed); - -irqreturn_t -il_isr(int irq, void *data) -{ - struct il_priv *il = data; - u32 inta, inta_mask; - u32 inta_fh; - unsigned long flags; - if (!il) - return IRQ_NONE; - - spin_lock_irqsave(&il->lock, flags); - - /* Disable (but don't clear!) interrupts here to avoid - * back-to-back ISRs and sporadic interrupts from our NIC. - * If we have something to service, the tasklet will re-enable ints. - * If we *don't* have something, we'll re-enable before leaving here. */ - inta_mask = _il_rd(il, CSR_INT_MASK); /* just for debug */ - _il_wr(il, CSR_INT_MASK, 0x00000000); - - /* Discover which interrupts are active/pending */ - inta = _il_rd(il, CSR_INT); - inta_fh = _il_rd(il, CSR_FH_INT_STATUS); - - /* Ignore interrupt if there's nothing in NIC to service. - * This may be due to IRQ shared with another device, - * or due to sporadic interrupts thrown from our NIC. */ - if (!inta && !inta_fh) { - D_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n"); - goto none; - } - - if (inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0) { - /* Hardware disappeared. It might have already raised - * an interrupt */ - IL_WARN("HARDWARE GONE?? INTA == 0x%08x\n", inta); - goto unplugged; - } - - D_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", inta, inta_mask, - inta_fh); - - inta &= ~CSR_INT_BIT_SCD; - - /* il_irq_tasklet() will service interrupts and re-enable them */ - if (likely(inta || inta_fh)) - tasklet_schedule(&il->irq_tasklet); - -unplugged: - spin_unlock_irqrestore(&il->lock, flags); - return IRQ_HANDLED; - -none: - /* re-enable interrupts here since we don't have anything to service. */ - /* only Re-enable if disabled by irq */ - if (test_bit(S_INT_ENABLED, &il->status)) - il_enable_interrupts(il); - spin_unlock_irqrestore(&il->lock, flags); - return IRQ_NONE; -} -EXPORT_SYMBOL(il_isr); - -/* - * il_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this - * function. - */ -void -il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info, - __le16 fc, __le32 *tx_flags) -{ - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { - *tx_flags |= TX_CMD_FLG_RTS_MSK; - *tx_flags &= ~TX_CMD_FLG_CTS_MSK; - *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; - - if (!ieee80211_is_mgmt(fc)) - return; - - switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { - case cpu_to_le16(IEEE80211_STYPE_AUTH): - case cpu_to_le16(IEEE80211_STYPE_DEAUTH): - case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): - case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): - *tx_flags &= ~TX_CMD_FLG_RTS_MSK; - *tx_flags |= TX_CMD_FLG_CTS_MSK; - break; - } - } else if (info->control.rates[0]. - flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - *tx_flags &= ~TX_CMD_FLG_RTS_MSK; - *tx_flags |= TX_CMD_FLG_CTS_MSK; - *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; - } -} -EXPORT_SYMBOL(il_tx_cmd_protection); diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h deleted file mode 100644 index ce52cf114fde..000000000000 --- a/drivers/net/wireless/iwlegacy/common.h +++ /dev/null @@ -1,3084 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#ifndef __il_core_h__ -#define __il_core_h__ - -#include -#include /* for struct pci_device_id */ -#include -#include -#include -#include -#include -#include - -#include "commands.h" -#include "csr.h" -#include "prph.h" - -struct il_host_cmd; -struct il_cmd; -struct il_tx_queue; - -#define IL_ERR(f, a...) dev_err(&il->pci_dev->dev, f, ## a) -#define IL_WARN(f, a...) dev_warn(&il->pci_dev->dev, f, ## a) -#define IL_INFO(f, a...) dev_info(&il->pci_dev->dev, f, ## a) - -#define RX_QUEUE_SIZE 256 -#define RX_QUEUE_MASK 255 -#define RX_QUEUE_SIZE_LOG 8 - -/* - * RX related structures and functions - */ -#define RX_FREE_BUFFERS 64 -#define RX_LOW_WATERMARK 8 - -#define U32_PAD(n) ((4-(n))&0x3) - -/* CT-KILL constants */ -#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ - -/* Default noise level to report when noise measurement is not available. - * This may be because we're: - * 1) Not associated (4965, no beacon stats being sent to driver) - * 2) Scanning (noise measurement does not apply to associated channel) - * 3) Receiving CCK (3945 delivers noise info only for OFDM frames) - * Use default noise value of -127 ... this is below the range of measurable - * Rx dBm for either 3945 or 4965, so it can indicate "unmeasurable" to user. - * Also, -127 works better than 0 when averaging frames with/without - * noise info (e.g. averaging might be done in app); measured dBm values are - * always negative ... using a negative value as the default keeps all - * averages within an s8's (used in some apps) range of negative values. */ -#define IL_NOISE_MEAS_NOT_AVAILABLE (-127) - -/* - * RTS threshold here is total size [2347] minus 4 FCS bytes - * Per spec: - * a value of 0 means RTS on all data/management packets - * a value > max MSDU size means no RTS - * else RTS for data/management frames where MPDU is larger - * than RTS value. - */ -#define DEFAULT_RTS_THRESHOLD 2347U -#define MIN_RTS_THRESHOLD 0U -#define MAX_RTS_THRESHOLD 2347U -#define MAX_MSDU_SIZE 2304U -#define MAX_MPDU_SIZE 2346U -#define DEFAULT_BEACON_INTERVAL 100U -#define DEFAULT_SHORT_RETRY_LIMIT 7U -#define DEFAULT_LONG_RETRY_LIMIT 4U - -struct il_rx_buf { - dma_addr_t page_dma; - struct page *page; - struct list_head list; -}; - -#define rxb_addr(r) page_address(r->page) - -/* defined below */ -struct il_device_cmd; - -struct il_cmd_meta { - /* only for SYNC commands, iff the reply skb is wanted */ - struct il_host_cmd *source; - /* - * only for ASYNC commands - * (which is somewhat stupid -- look at common.c for instance - * which duplicates a bunch of code because the callback isn't - * invoked for SYNC commands, if it were and its result passed - * through it would be simpler...) - */ - void (*callback) (struct il_priv *il, struct il_device_cmd *cmd, - struct il_rx_pkt *pkt); - - /* The CMD_SIZE_HUGE flag bit indicates that the command - * structure is stored at the end of the shared queue memory. */ - u32 flags; - - DEFINE_DMA_UNMAP_ADDR(mapping); - DEFINE_DMA_UNMAP_LEN(len); -}; - -/* - * Generic queue structure - * - * Contains common data for Rx and Tx queues - */ -struct il_queue { - int n_bd; /* number of BDs in this queue */ - int write_ptr; /* 1-st empty entry (idx) host_w */ - int read_ptr; /* last used entry (idx) host_r */ - /* use for monitoring and recovering the stuck queue */ - dma_addr_t dma_addr; /* physical addr for BD's */ - int n_win; /* safe queue win */ - u32 id; - int low_mark; /* low watermark, resume queue if free - * space more than this */ - int high_mark; /* high watermark, stop queue if free - * space less than this */ -}; - -/** - * struct il_tx_queue - Tx Queue for DMA - * @q: generic Rx/Tx queue descriptor - * @bd: base of circular buffer of TFDs - * @cmd: array of command/TX buffer pointers - * @meta: array of meta data for each command/tx buffer - * @dma_addr_cmd: physical address of cmd/tx buffer array - * @skbs: array of per-TFD socket buffer pointers - * @time_stamp: time (in jiffies) of last read_ptr change - * @need_update: indicates need to update read/write idx - * @sched_retry: indicates queue is high-throughput aggregation (HT AGG) enabled - * - * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame - * descriptors) and required locking structures. - */ -#define TFD_TX_CMD_SLOTS 256 -#define TFD_CMD_SLOTS 32 - -struct il_tx_queue { - struct il_queue q; - void *tfds; - struct il_device_cmd **cmd; - struct il_cmd_meta *meta; - struct sk_buff **skbs; - unsigned long time_stamp; - u8 need_update; - u8 sched_retry; - u8 active; - u8 swq_id; -}; - -/* - * EEPROM access time values: - * - * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG. - * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). - * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. - * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. - */ -#define IL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ - -#define IL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ -#define IL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ - -/* - * Regulatory channel usage flags in EEPROM struct il4965_eeprom_channel.flags. - * - * IBSS and/or AP operation is allowed *only* on those channels with - * (VALID && IBSS && ACTIVE && !RADAR). This restriction is in place because - * RADAR detection is not supported by the 4965 driver, but is a - * requirement for establishing a new network for legal operation on channels - * requiring RADAR detection or restricting ACTIVE scanning. - * - * NOTE: "WIDE" flag does not indicate anything about "HT40" 40 MHz channels. - * It only indicates that 20 MHz channel use is supported; HT40 channel - * usage is indicated by a separate set of regulatory flags for each - * HT40 channel pair. - * - * NOTE: Using a channel inappropriately will result in a uCode error! - */ -#define IL_NUM_TX_CALIB_GROUPS 5 -enum { - EEPROM_CHANNEL_VALID = (1 << 0), /* usable for this SKU/geo */ - EEPROM_CHANNEL_IBSS = (1 << 1), /* usable as an IBSS channel */ - /* Bit 2 Reserved */ - EEPROM_CHANNEL_ACTIVE = (1 << 3), /* active scanning allowed */ - EEPROM_CHANNEL_RADAR = (1 << 4), /* radar detection required */ - EEPROM_CHANNEL_WIDE = (1 << 5), /* 20 MHz channel okay */ - /* Bit 6 Reserved (was Narrow Channel) */ - EEPROM_CHANNEL_DFS = (1 << 7), /* dynamic freq selection candidate */ -}; - -/* SKU Capabilities */ -/* 3945 only */ -#define EEPROM_SKU_CAP_SW_RF_KILL_ENABLE (1 << 0) -#define EEPROM_SKU_CAP_HW_RF_KILL_ENABLE (1 << 1) - -/* *regulatory* channel data format in eeprom, one for each channel. - * There are separate entries for HT40 (40 MHz) vs. normal (20 MHz) channels. */ -struct il_eeprom_channel { - u8 flags; /* EEPROM_CHANNEL_* flags copied from EEPROM */ - s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */ -} __packed; - -/* 3945 Specific */ -#define EEPROM_3945_EEPROM_VERSION (0x2f) - -/* 4965 has two radio transmitters (and 3 radio receivers) */ -#define EEPROM_TX_POWER_TX_CHAINS (2) - -/* 4965 has room for up to 8 sets of txpower calibration data */ -#define EEPROM_TX_POWER_BANDS (8) - -/* 4965 factory calibration measures txpower gain settings for - * each of 3 target output levels */ -#define EEPROM_TX_POWER_MEASUREMENTS (3) - -/* 4965 Specific */ -/* 4965 driver does not work with txpower calibration version < 5 */ -#define EEPROM_4965_TX_POWER_VERSION (5) -#define EEPROM_4965_EEPROM_VERSION (0x2f) -#define EEPROM_4965_CALIB_VERSION_OFFSET (2*0xB6) /* 2 bytes */ -#define EEPROM_4965_CALIB_TXPOWER_OFFSET (2*0xE8) /* 48 bytes */ -#define EEPROM_4965_BOARD_REVISION (2*0x4F) /* 2 bytes */ -#define EEPROM_4965_BOARD_PBA (2*0x56+1) /* 9 bytes */ - -/* 2.4 GHz */ -extern const u8 il_eeprom_band_1[14]; - -/* - * factory calibration data for one txpower level, on one channel, - * measured on one of the 2 tx chains (radio transmitter and associated - * antenna). EEPROM contains: - * - * 1) Temperature (degrees Celsius) of device when measurement was made. - * - * 2) Gain table idx used to achieve the target measurement power. - * This refers to the "well-known" gain tables (see 4965.h). - * - * 3) Actual measured output power, in half-dBm ("34" = 17 dBm). - * - * 4) RF power amplifier detector level measurement (not used). - */ -struct il_eeprom_calib_measure { - u8 temperature; /* Device temperature (Celsius) */ - u8 gain_idx; /* Index into gain table */ - u8 actual_pow; /* Measured RF output power, half-dBm */ - s8 pa_det; /* Power amp detector level (not used) */ -} __packed; - -/* - * measurement set for one channel. EEPROM contains: - * - * 1) Channel number measured - * - * 2) Measurements for each of 3 power levels for each of 2 radio transmitters - * (a.k.a. "tx chains") (6 measurements altogether) - */ -struct il_eeprom_calib_ch_info { - u8 ch_num; - struct il_eeprom_calib_measure - measurements[EEPROM_TX_POWER_TX_CHAINS] - [EEPROM_TX_POWER_MEASUREMENTS]; -} __packed; - -/* - * txpower subband info. - * - * For each frequency subband, EEPROM contains the following: - * - * 1) First and last channels within range of the subband. "0" values - * indicate that this sample set is not being used. - * - * 2) Sample measurement sets for 2 channels close to the range endpoints. - */ -struct il_eeprom_calib_subband_info { - u8 ch_from; /* channel number of lowest channel in subband */ - u8 ch_to; /* channel number of highest channel in subband */ - struct il_eeprom_calib_ch_info ch1; - struct il_eeprom_calib_ch_info ch2; -} __packed; - -/* - * txpower calibration info. EEPROM contains: - * - * 1) Factory-measured saturation power levels (maximum levels at which - * tx power amplifier can output a signal without too much distortion). - * There is one level for 2.4 GHz band and one for 5 GHz band. These - * values apply to all channels within each of the bands. - * - * 2) Factory-measured power supply voltage level. This is assumed to be - * constant (i.e. same value applies to all channels/bands) while the - * factory measurements are being made. - * - * 3) Up to 8 sets of factory-measured txpower calibration values. - * These are for different frequency ranges, since txpower gain - * characteristics of the analog radio circuitry vary with frequency. - * - * Not all sets need to be filled with data; - * struct il_eeprom_calib_subband_info contains range of channels - * (0 if unused) for each set of data. - */ -struct il_eeprom_calib_info { - u8 saturation_power24; /* half-dBm (e.g. "34" = 17 dBm) */ - u8 saturation_power52; /* half-dBm */ - __le16 voltage; /* signed */ - struct il_eeprom_calib_subband_info band_info[EEPROM_TX_POWER_BANDS]; -} __packed; - -/* General */ -#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ -#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ -#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ -#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ -#define EEPROM_VERSION (2*0x44) /* 2 bytes */ -#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ -#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ -#define EEPROM_WOWLAN_MODE (2*0x47) /* 2 bytes */ -#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ -#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ - -/* The following masks are to be applied on EEPROM_RADIO_CONFIG */ -#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ -#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ -#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ -#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ -#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ -#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ - -#define EEPROM_3945_RF_CFG_TYPE_MAX 0x0 -#define EEPROM_4965_RF_CFG_TYPE_MAX 0x1 - -/* - * Per-channel regulatory data. - * - * Each channel that *might* be supported by iwl has a fixed location - * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory - * txpower (MSB). - * - * Entries immediately below are for 20 MHz channel width. HT40 (40 MHz) - * channels (only for 4965, not supported by 3945) appear later in the EEPROM. - * - * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 - */ -#define EEPROM_REGULATORY_SKU_ID (2*0x60) /* 4 bytes */ -#define EEPROM_REGULATORY_BAND_1 (2*0x62) /* 2 bytes */ -#define EEPROM_REGULATORY_BAND_1_CHANNELS (2*0x63) /* 28 bytes */ - -/* - * 4.9 GHz channels 183, 184, 185, 187, 188, 189, 192, 196, - * 5.0 GHz channels 7, 8, 11, 12, 16 - * (4915-5080MHz) (none of these is ever supported) - */ -#define EEPROM_REGULATORY_BAND_2 (2*0x71) /* 2 bytes */ -#define EEPROM_REGULATORY_BAND_2_CHANNELS (2*0x72) /* 26 bytes */ - -/* - * 5.2 GHz channels 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 - * (5170-5320MHz) - */ -#define EEPROM_REGULATORY_BAND_3 (2*0x7F) /* 2 bytes */ -#define EEPROM_REGULATORY_BAND_3_CHANNELS (2*0x80) /* 24 bytes */ - -/* - * 5.5 GHz channels 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 - * (5500-5700MHz) - */ -#define EEPROM_REGULATORY_BAND_4 (2*0x8C) /* 2 bytes */ -#define EEPROM_REGULATORY_BAND_4_CHANNELS (2*0x8D) /* 22 bytes */ - -/* - * 5.7 GHz channels 145, 149, 153, 157, 161, 165 - * (5725-5825MHz) - */ -#define EEPROM_REGULATORY_BAND_5 (2*0x98) /* 2 bytes */ -#define EEPROM_REGULATORY_BAND_5_CHANNELS (2*0x99) /* 12 bytes */ - -/* - * 2.4 GHz HT40 channels 1 (5), 2 (6), 3 (7), 4 (8), 5 (9), 6 (10), 7 (11) - * - * The channel listed is the center of the lower 20 MHz half of the channel. - * The overall center frequency is actually 2 channels (10 MHz) above that, - * and the upper half of each HT40 channel is centered 4 channels (20 MHz) away - * from the lower half; e.g. the upper half of HT40 channel 1 is channel 5, - * and the overall HT40 channel width centers on channel 3. - * - * NOTE: The RXON command uses 20 MHz channel numbers to specify the - * control channel to which to tune. RXON also specifies whether the - * control channel is the upper or lower half of a HT40 channel. - * - * NOTE: 4965 does not support HT40 channels on 2.4 GHz. - */ -#define EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS (2*0xA0) /* 14 bytes */ - -/* - * 5.2 GHz HT40 channels 36 (40), 44 (48), 52 (56), 60 (64), - * 100 (104), 108 (112), 116 (120), 124 (128), 132 (136), 149 (153), 157 (161) - */ -#define EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS (2*0xA8) /* 22 bytes */ - -#define EEPROM_REGULATORY_BAND_NO_HT40 (0) - -int il_eeprom_init(struct il_priv *il); -void il_eeprom_free(struct il_priv *il); -const u8 *il_eeprom_query_addr(const struct il_priv *il, size_t offset); -u16 il_eeprom_query16(const struct il_priv *il, size_t offset); -int il_init_channel_map(struct il_priv *il); -void il_free_channel_map(struct il_priv *il); -const struct il_channel_info *il_get_channel_info(const struct il_priv *il, - enum ieee80211_band band, - u16 channel); - -#define IL_NUM_SCAN_RATES (2) - -struct il4965_channel_tgd_info { - u8 type; - s8 max_power; -}; - -struct il4965_channel_tgh_info { - s64 last_radar_time; -}; - -#define IL4965_MAX_RATE (33) - -struct il3945_clip_group { - /* maximum power level to prevent clipping for each rate, derived by - * us from this band's saturation power in EEPROM */ - const s8 clip_powers[IL_MAX_RATES]; -}; - -/* current Tx power values to use, one for each rate for each channel. - * requested power is limited by: - * -- regulatory EEPROM limits for this channel - * -- hardware capabilities (clip-powers) - * -- spectrum management - * -- user preference (e.g. iwconfig) - * when requested power is set, base power idx must also be set. */ -struct il3945_channel_power_info { - struct il3945_tx_power tpc; /* actual radio and DSP gain settings */ - s8 power_table_idx; /* actual (compenst'd) idx into gain table */ - s8 base_power_idx; /* gain idx for power at factory temp. */ - s8 requested_power; /* power (dBm) requested for this chnl/rate */ -}; - -/* current scan Tx power values to use, one for each scan rate for each - * channel. */ -struct il3945_scan_power_info { - struct il3945_tx_power tpc; /* actual radio and DSP gain settings */ - s8 power_table_idx; /* actual (compenst'd) idx into gain table */ - s8 requested_power; /* scan pwr (dBm) requested for chnl/rate */ -}; - -/* - * One for each channel, holds all channel setup data - * Some of the fields (e.g. eeprom and flags/max_power_avg) are redundant - * with one another! - */ -struct il_channel_info { - struct il4965_channel_tgd_info tgd; - struct il4965_channel_tgh_info tgh; - struct il_eeprom_channel eeprom; /* EEPROM regulatory limit */ - struct il_eeprom_channel ht40_eeprom; /* EEPROM regulatory limit for - * HT40 channel */ - - u8 channel; /* channel number */ - u8 flags; /* flags copied from EEPROM */ - s8 max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ - s8 curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) limit */ - s8 min_power; /* always 0 */ - s8 scan_power; /* (dBm) regul. eeprom, direct scans, any rate */ - - u8 group_idx; /* 0-4, maps channel to group1/2/3/4/5 */ - u8 band_idx; /* 0-4, maps channel to band1/2/3/4/5 */ - enum ieee80211_band band; - - /* HT40 channel info */ - s8 ht40_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ - u8 ht40_flags; /* flags copied from EEPROM */ - u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */ - - /* Radio/DSP gain settings for each "normal" data Tx rate. - * These include, in addition to RF and DSP gain, a few fields for - * remembering/modifying gain settings (idxes). */ - struct il3945_channel_power_info power_info[IL4965_MAX_RATE]; - - /* Radio/DSP gain settings for each scan rate, for directed scans. */ - struct il3945_scan_power_info scan_pwr_info[IL_NUM_SCAN_RATES]; -}; - -#define IL_TX_FIFO_BK 0 /* shared */ -#define IL_TX_FIFO_BE 1 -#define IL_TX_FIFO_VI 2 /* shared */ -#define IL_TX_FIFO_VO 3 -#define IL_TX_FIFO_UNUSED -1 - -/* Minimum number of queues. MAX_NUM is defined in hw specific files. - * Set the minimum to accommodate the 4 standard TX queues, 1 command - * queue, 2 (unused) HCCA queues, and 4 HT queues (one for each AC) */ -#define IL_MIN_NUM_QUEUES 10 - -#define IL_DEFAULT_CMD_QUEUE_NUM 4 - -#define IEEE80211_DATA_LEN 2304 -#define IEEE80211_4ADDR_LEN 30 -#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) -#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) - -struct il_frame { - union { - struct ieee80211_hdr frame; - struct il_tx_beacon_cmd beacon; - u8 raw[IEEE80211_FRAME_LEN]; - u8 cmd[360]; - } u; - struct list_head list; -}; - -enum { - CMD_SYNC = 0, - CMD_SIZE_NORMAL = 0, - CMD_NO_SKB = 0, - CMD_SIZE_HUGE = (1 << 0), - CMD_ASYNC = (1 << 1), - CMD_WANT_SKB = (1 << 2), - CMD_MAPPED = (1 << 3), -}; - -#define DEF_CMD_PAYLOAD_SIZE 320 - -/** - * struct il_device_cmd - * - * For allocation of the command and tx queues, this establishes the overall - * size of the largest command we send to uCode, except for a scan command - * (which is relatively huge; space is allocated separately). - */ -struct il_device_cmd { - struct il_cmd_header hdr; /* uCode API */ - union { - u32 flags; - u8 val8; - u16 val16; - u32 val32; - struct il_tx_cmd tx; - u8 payload[DEF_CMD_PAYLOAD_SIZE]; - } __packed cmd; -} __packed; - -#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct il_device_cmd)) - -struct il_host_cmd { - const void *data; - unsigned long reply_page; - void (*callback) (struct il_priv *il, struct il_device_cmd *cmd, - struct il_rx_pkt *pkt); - u32 flags; - u16 len; - u8 id; -}; - -#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 -#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 -#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 - -/** - * struct il_rx_queue - Rx queue - * @bd: driver's pointer to buffer of receive buffer descriptors (rbd) - * @bd_dma: bus address of buffer of receive buffer descriptors (rbd) - * @read: Shared idx to newest available Rx buffer - * @write: Shared idx to oldest written Rx packet - * @free_count: Number of pre-allocated buffers in rx_free - * @rx_free: list of free SKBs for use - * @rx_used: List of Rx buffers with no SKB - * @need_update: flag to indicate we need to update read/write idx - * @rb_stts: driver's pointer to receive buffer status - * @rb_stts_dma: bus address of receive buffer status - * - * NOTE: rx_free and rx_used are used as a FIFO for il_rx_bufs - */ -struct il_rx_queue { - __le32 *bd; - dma_addr_t bd_dma; - struct il_rx_buf pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; - struct il_rx_buf *queue[RX_QUEUE_SIZE]; - u32 read; - u32 write; - u32 free_count; - u32 write_actual; - struct list_head rx_free; - struct list_head rx_used; - int need_update; - struct il_rb_status *rb_stts; - dma_addr_t rb_stts_dma; - spinlock_t lock; -}; - -#define IL_SUPPORTED_RATES_IE_LEN 8 - -#define MAX_TID_COUNT 9 - -#define IL_INVALID_RATE 0xFF -#define IL_INVALID_VALUE -1 - -/** - * struct il_ht_agg -- aggregation status while waiting for block-ack - * @txq_id: Tx queue used for Tx attempt - * @frame_count: # frames attempted by Tx command - * @wait_for_ba: Expect block-ack before next Tx reply - * @start_idx: Index of 1st Transmit Frame Descriptor (TFD) in Tx win - * @bitmap0: Low order bitmap, one bit for each frame pending ACK in Tx win - * @bitmap1: High order, one bit for each frame pending ACK in Tx win - * @rate_n_flags: Rate at which Tx was attempted - * - * If C_TX indicates that aggregation was attempted, driver must wait - * for block ack (N_COMPRESSED_BA). This struct stores tx reply info - * until block ack arrives. - */ -struct il_ht_agg { - u16 txq_id; - u16 frame_count; - u16 wait_for_ba; - u16 start_idx; - u64 bitmap; - u32 rate_n_flags; -#define IL_AGG_OFF 0 -#define IL_AGG_ON 1 -#define IL_EMPTYING_HW_QUEUE_ADDBA 2 -#define IL_EMPTYING_HW_QUEUE_DELBA 3 - u8 state; -}; - -struct il_tid_data { - u16 seq_number; /* 4965 only */ - u16 tfds_in_queue; - struct il_ht_agg agg; -}; - -struct il_hw_key { - u32 cipher; - int keylen; - u8 keyidx; - u8 key[32]; -}; - -union il_ht_rate_supp { - u16 rates; - struct { - u8 siso_rate; - u8 mimo_rate; - }; -}; - -#define CFG_HT_RX_AMPDU_FACTOR_8K (0x0) -#define CFG_HT_RX_AMPDU_FACTOR_16K (0x1) -#define CFG_HT_RX_AMPDU_FACTOR_32K (0x2) -#define CFG_HT_RX_AMPDU_FACTOR_64K (0x3) -#define CFG_HT_RX_AMPDU_FACTOR_DEF CFG_HT_RX_AMPDU_FACTOR_64K -#define CFG_HT_RX_AMPDU_FACTOR_MAX CFG_HT_RX_AMPDU_FACTOR_64K -#define CFG_HT_RX_AMPDU_FACTOR_MIN CFG_HT_RX_AMPDU_FACTOR_8K - -/* - * Maximal MPDU density for TX aggregation - * 4 - 2us density - * 5 - 4us density - * 6 - 8us density - * 7 - 16us density - */ -#define CFG_HT_MPDU_DENSITY_2USEC (0x4) -#define CFG_HT_MPDU_DENSITY_4USEC (0x5) -#define CFG_HT_MPDU_DENSITY_8USEC (0x6) -#define CFG_HT_MPDU_DENSITY_16USEC (0x7) -#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC -#define CFG_HT_MPDU_DENSITY_MAX CFG_HT_MPDU_DENSITY_16USEC -#define CFG_HT_MPDU_DENSITY_MIN (0x1) - -struct il_ht_config { - bool single_chain_sufficient; - enum ieee80211_smps_mode smps; /* current smps mode */ -}; - -/* QoS structures */ -struct il_qos_info { - int qos_active; - struct il_qosparam_cmd def_qos_parm; -}; - -/* - * Structure should be accessed with sta_lock held. When station addition - * is in progress (IL_STA_UCODE_INPROGRESS) it is possible to access only - * the commands (il_addsta_cmd and il_link_quality_cmd) without - * sta_lock held. - */ -struct il_station_entry { - struct il_addsta_cmd sta; - struct il_tid_data tid[MAX_TID_COUNT]; - u8 used; - struct il_hw_key keyinfo; - struct il_link_quality_cmd *lq; -}; - -struct il_station_priv_common { - u8 sta_id; -}; - -/** - * struct il_vif_priv - driver's ilate per-interface information - * - * When mac80211 allocates a virtual interface, it can allocate - * space for us to put data into. - */ -struct il_vif_priv { - u8 ibss_bssid_sta_id; -}; - -/* one for each uCode image (inst/data, boot/init/runtime) */ -struct fw_desc { - void *v_addr; /* access by driver */ - dma_addr_t p_addr; /* access by card's busmaster DMA */ - u32 len; /* bytes */ -}; - -/* uCode file layout */ -struct il_ucode_header { - __le32 ver; /* major/minor/API/serial */ - struct { - __le32 inst_size; /* bytes of runtime code */ - __le32 data_size; /* bytes of runtime data */ - __le32 init_size; /* bytes of init code */ - __le32 init_data_size; /* bytes of init data */ - __le32 boot_size; /* bytes of bootstrap code */ - u8 data[0]; /* in same order as sizes */ - } v1; -}; - -struct il4965_ibss_seq { - u8 mac[ETH_ALEN]; - u16 seq_num; - u16 frag_num; - unsigned long packet_time; - struct list_head list; -}; - -struct il_sensitivity_ranges { - u16 min_nrg_cck; - u16 max_nrg_cck; - - u16 nrg_th_cck; - u16 nrg_th_ofdm; - - u16 auto_corr_min_ofdm; - u16 auto_corr_min_ofdm_mrc; - u16 auto_corr_min_ofdm_x1; - u16 auto_corr_min_ofdm_mrc_x1; - - u16 auto_corr_max_ofdm; - u16 auto_corr_max_ofdm_mrc; - u16 auto_corr_max_ofdm_x1; - u16 auto_corr_max_ofdm_mrc_x1; - - u16 auto_corr_max_cck; - u16 auto_corr_max_cck_mrc; - u16 auto_corr_min_cck; - u16 auto_corr_min_cck_mrc; - - u16 barker_corr_th_min; - u16 barker_corr_th_min_mrc; - u16 nrg_th_cca; -}; - -#define KELVIN_TO_CELSIUS(x) ((x)-273) -#define CELSIUS_TO_KELVIN(x) ((x)+273) - -/** - * struct il_hw_params - * @bcast_id: f/w broadcast station ID - * @max_txq_num: Max # Tx queues supported - * @dma_chnl_num: Number of Tx DMA/FIFO channels - * @scd_bc_tbls_size: size of scheduler byte count tables - * @tfd_size: TFD size - * @tx/rx_chains_num: Number of TX/RX chains - * @valid_tx/rx_ant: usable antennas - * @max_rxq_size: Max # Rx frames in Rx queue (must be power-of-2) - * @max_rxq_log: Log-base-2 of max_rxq_size - * @rx_page_order: Rx buffer page order - * @rx_wrt_ptr_reg: FH{39}_RSCSR_CHNL0_WPTR - * @max_stations: - * @ht40_channel: is 40MHz width possible in band 2.4 - * BIT(IEEE80211_BAND_5GHZ) BIT(IEEE80211_BAND_5GHZ) - * @sw_crypto: 0 for hw, 1 for sw - * @max_xxx_size: for ucode uses - * @ct_kill_threshold: temperature threshold - * @beacon_time_tsf_bits: number of valid tsf bits for beacon time - * @struct il_sensitivity_ranges: range of sensitivity values - */ -struct il_hw_params { - u8 bcast_id; - u8 max_txq_num; - u8 dma_chnl_num; - u16 scd_bc_tbls_size; - u32 tfd_size; - u8 tx_chains_num; - u8 rx_chains_num; - u8 valid_tx_ant; - u8 valid_rx_ant; - u16 max_rxq_size; - u16 max_rxq_log; - u32 rx_page_order; - u32 rx_wrt_ptr_reg; - u8 max_stations; - u8 ht40_channel; - u8 max_beacon_itrvl; /* in 1024 ms */ - u32 max_inst_size; - u32 max_data_size; - u32 max_bsm_size; - u32 ct_kill_threshold; /* value in hw-dependent units */ - u16 beacon_time_tsf_bits; - const struct il_sensitivity_ranges *sens; -}; - -/****************************************************************************** - * - * Functions implemented in core module which are forward declared here - * for use by iwl-[4-5].c - * - * NOTE: The implementation of these functions are not hardware specific - * which is why they are in the core module files. - * - * Naming convention -- - * il_ <-- Is part of iwlwifi - * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) - * il4965_bg_ <-- Called from work queue context - * il4965_mac_ <-- mac80211 callback - * - ****************************************************************************/ -void il4965_update_chain_flags(struct il_priv *il); -extern const u8 il_bcast_addr[ETH_ALEN]; -int il_queue_space(const struct il_queue *q); -static inline int -il_queue_used(const struct il_queue *q, int i) -{ - return q->write_ptr >= q->read_ptr ? (i >= q->read_ptr && - i < q->write_ptr) : !(i < - q->read_ptr - && i >= - q-> - write_ptr); -} - -static inline u8 -il_get_cmd_idx(struct il_queue *q, u32 idx, int is_huge) -{ - /* - * This is for init calibration result and scan command which - * required buffer > TFD_MAX_PAYLOAD_SIZE, - * the big buffer at end of command array - */ - if (is_huge) - return q->n_win; /* must be power of 2 */ - - /* Otherwise, use normal size buffers */ - return idx & (q->n_win - 1); -} - -struct il_dma_ptr { - dma_addr_t dma; - void *addr; - size_t size; -}; - -#define IL_OPERATION_MODE_AUTO 0 -#define IL_OPERATION_MODE_HT_ONLY 1 -#define IL_OPERATION_MODE_MIXED 2 -#define IL_OPERATION_MODE_20MHZ 3 - -#define IL_TX_CRC_SIZE 4 -#define IL_TX_DELIMITER_SIZE 4 - -#define TX_POWER_IL_ILLEGAL_VOLTAGE -10000 - -/* Sensitivity and chain noise calibration */ -#define INITIALIZATION_VALUE 0xFFFF -#define IL4965_CAL_NUM_BEACONS 20 -#define IL_CAL_NUM_BEACONS 16 -#define MAXIMUM_ALLOWED_PATHLOSS 15 - -#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3 - -#define MAX_FA_OFDM 50 -#define MIN_FA_OFDM 5 -#define MAX_FA_CCK 50 -#define MIN_FA_CCK 5 - -#define AUTO_CORR_STEP_OFDM 1 - -#define AUTO_CORR_STEP_CCK 3 -#define AUTO_CORR_MAX_TH_CCK 160 - -#define NRG_DIFF 2 -#define NRG_STEP_CCK 2 -#define NRG_MARGIN 8 -#define MAX_NUMBER_CCK_NO_FA 100 - -#define AUTO_CORR_CCK_MIN_VAL_DEF (125) - -#define CHAIN_A 0 -#define CHAIN_B 1 -#define CHAIN_C 2 -#define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4 -#define ALL_BAND_FILTER 0xFF00 -#define IN_BAND_FILTER 0xFF -#define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF - -#define NRG_NUM_PREV_STAT_L 20 -#define NUM_RX_CHAINS 3 - -enum il4965_false_alarm_state { - IL_FA_TOO_MANY = 0, - IL_FA_TOO_FEW = 1, - IL_FA_GOOD_RANGE = 2, -}; - -enum il4965_chain_noise_state { - IL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */ - IL_CHAIN_NOISE_ACCUMULATE, - IL_CHAIN_NOISE_CALIBRATED, - IL_CHAIN_NOISE_DONE, -}; - -enum ucode_type { - UCODE_NONE = 0, - UCODE_INIT, - UCODE_RT -}; - -/* Sensitivity calib data */ -struct il_sensitivity_data { - u32 auto_corr_ofdm; - u32 auto_corr_ofdm_mrc; - u32 auto_corr_ofdm_x1; - u32 auto_corr_ofdm_mrc_x1; - u32 auto_corr_cck; - u32 auto_corr_cck_mrc; - - u32 last_bad_plcp_cnt_ofdm; - u32 last_fa_cnt_ofdm; - u32 last_bad_plcp_cnt_cck; - u32 last_fa_cnt_cck; - - u32 nrg_curr_state; - u32 nrg_prev_state; - u32 nrg_value[10]; - u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L]; - u32 nrg_silence_ref; - u32 nrg_energy_idx; - u32 nrg_silence_idx; - u32 nrg_th_cck; - s32 nrg_auto_corr_silence_diff; - u32 num_in_cck_no_fa; - u32 nrg_th_ofdm; - - u16 barker_corr_th_min; - u16 barker_corr_th_min_mrc; - u16 nrg_th_cca; -}; - -/* Chain noise (differential Rx gain) calib data */ -struct il_chain_noise_data { - u32 active_chains; - u32 chain_noise_a; - u32 chain_noise_b; - u32 chain_noise_c; - u32 chain_signal_a; - u32 chain_signal_b; - u32 chain_signal_c; - u16 beacon_count; - u8 disconn_array[NUM_RX_CHAINS]; - u8 delta_gain_code[NUM_RX_CHAINS]; - u8 radio_write; - u8 state; -}; - -#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ -#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ - -#define IL_TRAFFIC_ENTRIES (256) -#define IL_TRAFFIC_ENTRY_SIZE (64) - -enum { - MEASUREMENT_READY = (1 << 0), - MEASUREMENT_ACTIVE = (1 << 1), -}; - -/* interrupt stats */ -struct isr_stats { - u32 hw; - u32 sw; - u32 err_code; - u32 sch; - u32 alive; - u32 rfkill; - u32 ctkill; - u32 wakeup; - u32 rx; - u32 handlers[IL_CN_MAX]; - u32 tx; - u32 unhandled; -}; - -/* management stats */ -enum il_mgmt_stats { - MANAGEMENT_ASSOC_REQ = 0, - MANAGEMENT_ASSOC_RESP, - MANAGEMENT_REASSOC_REQ, - MANAGEMENT_REASSOC_RESP, - MANAGEMENT_PROBE_REQ, - MANAGEMENT_PROBE_RESP, - MANAGEMENT_BEACON, - MANAGEMENT_ATIM, - MANAGEMENT_DISASSOC, - MANAGEMENT_AUTH, - MANAGEMENT_DEAUTH, - MANAGEMENT_ACTION, - MANAGEMENT_MAX, -}; -/* control stats */ -enum il_ctrl_stats { - CONTROL_BACK_REQ = 0, - CONTROL_BACK, - CONTROL_PSPOLL, - CONTROL_RTS, - CONTROL_CTS, - CONTROL_ACK, - CONTROL_CFEND, - CONTROL_CFENDACK, - CONTROL_MAX, -}; - -struct traffic_stats { -#ifdef CONFIG_IWLEGACY_DEBUGFS - u32 mgmt[MANAGEMENT_MAX]; - u32 ctrl[CONTROL_MAX]; - u32 data_cnt; - u64 data_bytes; -#endif -}; - -/* - * host interrupt timeout value - * used with setting interrupt coalescing timer - * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit - * - * default interrupt coalescing timer is 64 x 32 = 2048 usecs - * default interrupt coalescing calibration timer is 16 x 32 = 512 usecs - */ -#define IL_HOST_INT_TIMEOUT_MAX (0xFF) -#define IL_HOST_INT_TIMEOUT_DEF (0x40) -#define IL_HOST_INT_TIMEOUT_MIN (0x0) -#define IL_HOST_INT_CALIB_TIMEOUT_MAX (0xFF) -#define IL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) -#define IL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) - -#define IL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5) - -/* TX queue watchdog timeouts in mSecs */ -#define IL_DEF_WD_TIMEOUT (2000) -#define IL_LONG_WD_TIMEOUT (10000) -#define IL_MAX_WD_TIMEOUT (120000) - -struct il_force_reset { - int reset_request_count; - int reset_success_count; - int reset_reject_count; - unsigned long reset_duration; - unsigned long last_force_reset_jiffies; -}; - -/* extend beacon time format bit shifting */ -/* - * for _3945 devices - * bits 31:24 - extended - * bits 23:0 - interval - */ -#define IL3945_EXT_BEACON_TIME_POS 24 -/* - * for _4965 devices - * bits 31:22 - extended - * bits 21:0 - interval - */ -#define IL4965_EXT_BEACON_TIME_POS 22 - -struct il_rxon_context { - struct ieee80211_vif *vif; -}; - -struct il_power_mgr { - struct il_powertable_cmd sleep_cmd; - struct il_powertable_cmd sleep_cmd_next; - int debug_sleep_level_override; - bool pci_pm; - bool ps_disabled; -}; - -struct il_priv { - struct ieee80211_hw *hw; - struct ieee80211_channel *ieee_channels; - struct ieee80211_rate *ieee_rates; - - struct il_cfg *cfg; - const struct il_ops *ops; -#ifdef CONFIG_IWLEGACY_DEBUGFS - const struct il_debugfs_ops *debugfs_ops; -#endif - - /* temporary frame storage list */ - struct list_head free_frames; - int frames_count; - - enum ieee80211_band band; - int alloc_rxb_page; - - void (*handlers[IL_CN_MAX]) (struct il_priv *il, - struct il_rx_buf *rxb); - - struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; - - /* spectrum measurement report caching */ - struct il_spectrum_notification measure_report; - u8 measurement_status; - - /* ucode beacon time */ - u32 ucode_beacon_time; - int missed_beacon_threshold; - - /* track IBSS manager (last beacon) status */ - u32 ibss_manager; - - /* force reset */ - struct il_force_reset force_reset; - - /* we allocate array of il_channel_info for NIC's valid channels. - * Access via channel # using indirect idx array */ - struct il_channel_info *channel_info; /* channel info array */ - u8 channel_count; /* # of channels */ - - /* thermal calibration */ - s32 temperature; /* degrees Kelvin */ - s32 last_temperature; - - /* Scan related variables */ - unsigned long scan_start; - unsigned long scan_start_tsf; - void *scan_cmd; - enum ieee80211_band scan_band; - struct cfg80211_scan_request *scan_request; - struct ieee80211_vif *scan_vif; - u8 scan_tx_ant[IEEE80211_NUM_BANDS]; - u8 mgmt_tx_ant; - - /* spinlock */ - spinlock_t lock; /* protect general shared data */ - spinlock_t hcmd_lock; /* protect hcmd */ - spinlock_t reg_lock; /* protect hw register access */ - struct mutex mutex; - - /* basic pci-network driver stuff */ - struct pci_dev *pci_dev; - - /* pci hardware address support */ - void __iomem *hw_base; - u32 hw_rev; - u32 hw_wa_rev; - u8 rev_id; - - /* command queue number */ - u8 cmd_queue; - - /* max number of station keys */ - u8 sta_key_max_num; - - /* EEPROM MAC addresses */ - struct mac_address addresses[1]; - - /* uCode images, save to reload in case of failure */ - int fw_idx; /* firmware we're trying to load */ - u32 ucode_ver; /* version of ucode, copy of - il_ucode.ver */ - struct fw_desc ucode_code; /* runtime inst */ - struct fw_desc ucode_data; /* runtime data original */ - struct fw_desc ucode_data_backup; /* runtime data save/restore */ - struct fw_desc ucode_init; /* initialization inst */ - struct fw_desc ucode_init_data; /* initialization data */ - struct fw_desc ucode_boot; /* bootstrap inst */ - enum ucode_type ucode_type; - u8 ucode_write_complete; /* the image write is complete */ - char firmware_name[25]; - - struct ieee80211_vif *vif; - - struct il_qos_info qos_data; - - struct { - bool enabled; - bool is_40mhz; - bool non_gf_sta_present; - u8 protection; - u8 extension_chan_offset; - } ht; - - /* - * We declare this const so it can only be - * changed via explicit cast within the - * routines that actually update the physical - * hardware. - */ - const struct il_rxon_cmd active; - struct il_rxon_cmd staging; - - struct il_rxon_time_cmd timing; - - __le16 switch_channel; - - /* 1st responses from initialize and runtime uCode images. - * _4965's initialize alive response contains some calibration data. */ - struct il_init_alive_resp card_alive_init; - struct il_alive_resp card_alive; - - u16 active_rate; - - u8 start_calib; - struct il_sensitivity_data sensitivity_data; - struct il_chain_noise_data chain_noise_data; - __le16 sensitivity_tbl[HD_TBL_SIZE]; - - struct il_ht_config current_ht_config; - - /* Rate scaling data */ - u8 retry_rate; - - wait_queue_head_t wait_command_queue; - - int activity_timer_active; - - /* Rx and Tx DMA processing queues */ - struct il_rx_queue rxq; - struct il_tx_queue *txq; - unsigned long txq_ctx_active_msk; - struct il_dma_ptr kw; /* keep warm address */ - struct il_dma_ptr scd_bc_tbls; - - u32 scd_base_addr; /* scheduler sram base address */ - - unsigned long status; - - /* counts mgmt, ctl, and data packets */ - struct traffic_stats tx_stats; - struct traffic_stats rx_stats; - - /* counts interrupts */ - struct isr_stats isr_stats; - - struct il_power_mgr power_data; - - /* context information */ - u8 bssid[ETH_ALEN]; /* used only on 3945 but filled by core */ - - /* station table variables */ - - /* Note: if lock and sta_lock are needed, lock must be acquired first */ - spinlock_t sta_lock; - int num_stations; - struct il_station_entry stations[IL_STATION_COUNT]; - unsigned long ucode_key_table; - - /* queue refcounts */ -#define IL_MAX_HW_QUEUES 32 - unsigned long queue_stopped[BITS_TO_LONGS(IL_MAX_HW_QUEUES)]; -#define IL_STOP_REASON_PASSIVE 0 - unsigned long stop_reason; - /* for each AC */ - atomic_t queue_stop_count[4]; - - /* Indication if ieee80211_ops->open has been called */ - u8 is_open; - - u8 mac80211_registered; - - /* eeprom -- this is in the card's little endian byte order */ - u8 *eeprom; - struct il_eeprom_calib_info *calib_info; - - enum nl80211_iftype iw_mode; - - /* Last Rx'd beacon timestamp */ - u64 timestamp; - - union { -#if defined(CONFIG_IWL3945) || defined(CONFIG_IWL3945_MODULE) - struct { - void *shared_virt; - dma_addr_t shared_phys; - - struct delayed_work thermal_periodic; - struct delayed_work rfkill_poll; - - struct il3945_notif_stats stats; -#ifdef CONFIG_IWLEGACY_DEBUGFS - struct il3945_notif_stats accum_stats; - struct il3945_notif_stats delta_stats; - struct il3945_notif_stats max_delta; -#endif - - u32 sta_supp_rates; - int last_rx_rssi; /* From Rx packet stats */ - - /* Rx'd packet timing information */ - u32 last_beacon_time; - u64 last_tsf; - - /* - * each calibration channel group in the - * EEPROM has a derived clip setting for - * each rate. - */ - const struct il3945_clip_group clip_groups[5]; - - } _3945; -#endif -#if defined(CONFIG_IWL4965) || defined(CONFIG_IWL4965_MODULE) - struct { - struct il_rx_phy_res last_phy_res; - bool last_phy_res_valid; - u32 ampdu_ref; - - struct completion firmware_loading_complete; - - /* - * chain noise reset and gain commands are the - * two extra calibration commands follows the standard - * phy calibration commands - */ - u8 phy_calib_chain_noise_reset_cmd; - u8 phy_calib_chain_noise_gain_cmd; - - u8 key_mapping_keys; - struct il_wep_key wep_keys[WEP_KEYS_MAX]; - - struct il_notif_stats stats; -#ifdef CONFIG_IWLEGACY_DEBUGFS - struct il_notif_stats accum_stats; - struct il_notif_stats delta_stats; - struct il_notif_stats max_delta; -#endif - - } _4965; -#endif - }; - - struct il_hw_params hw_params; - - u32 inta_mask; - - struct workqueue_struct *workqueue; - - struct work_struct restart; - struct work_struct scan_completed; - struct work_struct rx_replenish; - struct work_struct abort_scan; - - bool beacon_enabled; - struct sk_buff *beacon_skb; - - struct work_struct tx_flush; - - struct tasklet_struct irq_tasklet; - - struct delayed_work init_alive_start; - struct delayed_work alive_start; - struct delayed_work scan_check; - - /* TX Power */ - s8 tx_power_user_lmt; - s8 tx_power_device_lmt; - s8 tx_power_next; - -#ifdef CONFIG_IWLEGACY_DEBUG - /* debugging info */ - u32 debug_level; /* per device debugging will override global - il_debug_level if set */ -#endif /* CONFIG_IWLEGACY_DEBUG */ -#ifdef CONFIG_IWLEGACY_DEBUGFS - /* debugfs */ - u16 tx_traffic_idx; - u16 rx_traffic_idx; - u8 *tx_traffic; - u8 *rx_traffic; - struct dentry *debugfs_dir; - u32 dbgfs_sram_offset, dbgfs_sram_len; - bool disable_ht40; -#endif /* CONFIG_IWLEGACY_DEBUGFS */ - - struct work_struct txpower_work; - bool disable_sens_cal; - bool disable_chain_noise_cal; - bool disable_tx_power_cal; - struct work_struct run_time_calib_work; - struct timer_list stats_periodic; - struct timer_list watchdog; - bool hw_ready; - - struct led_classdev led; - unsigned long blink_on, blink_off; - bool led_registered; -}; /*il_priv */ - -static inline void -il_txq_ctx_activate(struct il_priv *il, int txq_id) -{ - set_bit(txq_id, &il->txq_ctx_active_msk); -} - -static inline void -il_txq_ctx_deactivate(struct il_priv *il, int txq_id) -{ - clear_bit(txq_id, &il->txq_ctx_active_msk); -} - -static inline int -il_is_associated(struct il_priv *il) -{ - return (il->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; -} - -static inline int -il_is_any_associated(struct il_priv *il) -{ - return il_is_associated(il); -} - -static inline int -il_is_channel_valid(const struct il_channel_info *ch_info) -{ - if (ch_info == NULL) - return 0; - return (ch_info->flags & EEPROM_CHANNEL_VALID) ? 1 : 0; -} - -static inline int -il_is_channel_radar(const struct il_channel_info *ch_info) -{ - return (ch_info->flags & EEPROM_CHANNEL_RADAR) ? 1 : 0; -} - -static inline u8 -il_is_channel_a_band(const struct il_channel_info *ch_info) -{ - return ch_info->band == IEEE80211_BAND_5GHZ; -} - -static inline int -il_is_channel_passive(const struct il_channel_info *ch) -{ - return (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) ? 1 : 0; -} - -static inline int -il_is_channel_ibss(const struct il_channel_info *ch) -{ - return (ch->flags & EEPROM_CHANNEL_IBSS) ? 1 : 0; -} - -static inline void -__il_free_pages(struct il_priv *il, struct page *page) -{ - __free_pages(page, il->hw_params.rx_page_order); - il->alloc_rxb_page--; -} - -static inline void -il_free_pages(struct il_priv *il, unsigned long page) -{ - free_pages(page, il->hw_params.rx_page_order); - il->alloc_rxb_page--; -} - -#define IWLWIFI_VERSION "in-tree:" -#define DRV_COPYRIGHT "Copyright(c) 2003-2011 Intel Corporation" -#define DRV_AUTHOR "" - -#define IL_PCI_DEVICE(dev, subdev, cfg) \ - .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ - .subvendor = PCI_ANY_ID, .subdevice = (subdev), \ - .driver_data = (kernel_ulong_t)&(cfg) - -#define TIME_UNIT 1024 - -#define IL_SKU_G 0x1 -#define IL_SKU_A 0x2 -#define IL_SKU_N 0x8 - -#define IL_CMD(x) case x: return #x - -/* Size of one Rx buffer in host DRAM */ -#define IL_RX_BUF_SIZE_3K (3 * 1000) /* 3945 only */ -#define IL_RX_BUF_SIZE_4K (4 * 1024) -#define IL_RX_BUF_SIZE_8K (8 * 1024) - -#ifdef CONFIG_IWLEGACY_DEBUGFS -struct il_debugfs_ops { - ssize_t(*rx_stats_read) (struct file *file, char __user *user_buf, - size_t count, loff_t *ppos); - ssize_t(*tx_stats_read) (struct file *file, char __user *user_buf, - size_t count, loff_t *ppos); - ssize_t(*general_stats_read) (struct file *file, - char __user *user_buf, size_t count, - loff_t *ppos); -}; -#endif - -struct il_ops { - /* Handling TX */ - void (*txq_update_byte_cnt_tbl) (struct il_priv *il, - struct il_tx_queue *txq, - u16 byte_cnt); - int (*txq_attach_buf_to_tfd) (struct il_priv *il, - struct il_tx_queue *txq, dma_addr_t addr, - u16 len, u8 reset, u8 pad); - void (*txq_free_tfd) (struct il_priv *il, struct il_tx_queue *txq); - int (*txq_init) (struct il_priv *il, struct il_tx_queue *txq); - /* alive notification after init uCode load */ - void (*init_alive_start) (struct il_priv *il); - /* check validity of rtc data address */ - int (*is_valid_rtc_data_addr) (u32 addr); - /* 1st ucode load */ - int (*load_ucode) (struct il_priv *il); - - void (*dump_nic_error_log) (struct il_priv *il); - int (*dump_fh) (struct il_priv *il, char **buf, bool display); - int (*set_channel_switch) (struct il_priv *il, - struct ieee80211_channel_switch *ch_switch); - /* power management */ - int (*apm_init) (struct il_priv *il); - - /* tx power */ - int (*send_tx_power) (struct il_priv *il); - void (*update_chain_flags) (struct il_priv *il); - - /* eeprom operations */ - int (*eeprom_acquire_semaphore) (struct il_priv *il); - void (*eeprom_release_semaphore) (struct il_priv *il); - - int (*rxon_assoc) (struct il_priv *il); - int (*commit_rxon) (struct il_priv *il); - void (*set_rxon_chain) (struct il_priv *il); - - u16(*get_hcmd_size) (u8 cmd_id, u16 len); - u16(*build_addsta_hcmd) (const struct il_addsta_cmd *cmd, u8 *data); - - int (*request_scan) (struct il_priv *il, struct ieee80211_vif *vif); - void (*post_scan) (struct il_priv *il); - void (*post_associate) (struct il_priv *il); - void (*config_ap) (struct il_priv *il); - /* station management */ - int (*update_bcast_stations) (struct il_priv *il); - int (*manage_ibss_station) (struct il_priv *il, - struct ieee80211_vif *vif, bool add); - - int (*send_led_cmd) (struct il_priv *il, struct il_led_cmd *led_cmd); -}; - -struct il_mod_params { - int sw_crypto; /* def: 0 = using hardware encryption */ - int disable_hw_scan; /* def: 0 = use h/w scan */ - int num_of_queues; /* def: HW dependent */ - int disable_11n; /* def: 0 = 11n capabilities enabled */ - int amsdu_size_8K; /* def: 0 = disable 8K amsdu size */ - int antenna; /* def: 0 = both antennas (use diversity) */ - int restart_fw; /* def: 1 = restart firmware */ -}; - -#define IL_LED_SOLID 11 -#define IL_DEF_LED_INTRVL cpu_to_le32(1000) - -#define IL_LED_ACTIVITY (0<<1) -#define IL_LED_LINK (1<<1) - -/* - * LED mode - * IL_LED_DEFAULT: use device default - * IL_LED_RF_STATE: turn LED on/off based on RF state - * LED ON = RF ON - * LED OFF = RF OFF - * IL_LED_BLINK: adjust led blink rate based on blink table - */ -enum il_led_mode { - IL_LED_DEFAULT, - IL_LED_RF_STATE, - IL_LED_BLINK, -}; - -void il_leds_init(struct il_priv *il); -void il_leds_exit(struct il_priv *il); - -/** - * struct il_cfg - * @fw_name_pre: Firmware filename prefix. The api version and extension - * (.ucode) will be added to filename before loading from disk. The - * filename is constructed as fw_name_pre.ucode. - * @ucode_api_max: Highest version of uCode API supported by driver. - * @ucode_api_min: Lowest version of uCode API supported by driver. - * @scan_antennas: available antenna for scan operation - * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) - * - * We enable the driver to be backward compatible wrt API version. The - * driver specifies which APIs it supports (with @ucode_api_max being the - * highest and @ucode_api_min the lowest). Firmware will only be loaded if - * it has a supported API version. The firmware's API version will be - * stored in @il_priv, enabling the driver to make runtime changes based - * on firmware version used. - * - * For example, - * if (IL_UCODE_API(il->ucode_ver) >= 2) { - * Driver interacts with Firmware API version >= 2. - * } else { - * Driver interacts with Firmware API version 1. - * } - * - * The ideal usage of this infrastructure is to treat a new ucode API - * release as a new hardware revision. That is, through utilizing the - * il_hcmd_utils_ops etc. we accommodate different command structures - * and flows between hardware versions as well as their API - * versions. - * - */ -struct il_cfg { - /* params specific to an individual device within a device family */ - const char *name; - const char *fw_name_pre; - const unsigned int ucode_api_max; - const unsigned int ucode_api_min; - u8 valid_tx_ant; - u8 valid_rx_ant; - unsigned int sku; - u16 eeprom_ver; - u16 eeprom_calib_ver; - /* module based parameters which can be set from modprobe cmd */ - const struct il_mod_params *mod_params; - /* params not likely to change within a device family */ - struct il_base_params *base_params; - /* params likely to change within a device family */ - u8 scan_rx_antennas[IEEE80211_NUM_BANDS]; - enum il_led_mode led_mode; - - int eeprom_size; - int num_of_queues; /* def: HW dependent */ - int num_of_ampdu_queues; /* def: HW dependent */ - /* for il_apm_init() */ - u32 pll_cfg_val; - bool set_l0s; - bool use_bsm; - - u16 led_compensation; - int chain_noise_num_beacons; - unsigned int wd_timeout; - bool temperature_kelvin; - const bool ucode_tracing; - const bool sensitivity_calib_by_driver; - const bool chain_noise_calib_by_driver; - - const u32 regulatory_bands[7]; -}; - -/*************************** - * L i b * - ***************************/ - -int il_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u16 queue, const struct ieee80211_tx_queue_params *params); -int il_mac_tx_last_beacon(struct ieee80211_hw *hw); - -void il_set_rxon_hwcrypto(struct il_priv *il, int hw_decrypt); -int il_check_rxon_cmd(struct il_priv *il); -int il_full_rxon_required(struct il_priv *il); -int il_set_rxon_channel(struct il_priv *il, struct ieee80211_channel *ch); -void il_set_flags_for_band(struct il_priv *il, enum ieee80211_band band, - struct ieee80211_vif *vif); -u8 il_get_single_channel_number(struct il_priv *il, enum ieee80211_band band); -void il_set_rxon_ht(struct il_priv *il, struct il_ht_config *ht_conf); -bool il_is_ht40_tx_allowed(struct il_priv *il, - struct ieee80211_sta_ht_cap *ht_cap); -void il_connection_init_rx_config(struct il_priv *il); -void il_set_rate(struct il_priv *il); -int il_set_decrypted_flag(struct il_priv *il, struct ieee80211_hdr *hdr, - u32 decrypt_res, struct ieee80211_rx_status *stats); -void il_irq_handle_error(struct il_priv *il); -int il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); -void il_mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); -int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum nl80211_iftype newtype, bool newp2p); -void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 queues, bool drop); -int il_alloc_txq_mem(struct il_priv *il); -void il_free_txq_mem(struct il_priv *il); - -#ifdef CONFIG_IWLEGACY_DEBUGFS -void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len); -#else -static inline void -il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len) -{ -} -#endif - -/***************************************************** - * Handlers - ***************************************************/ -void il_hdl_pm_sleep(struct il_priv *il, struct il_rx_buf *rxb); -void il_hdl_pm_debug_stats(struct il_priv *il, struct il_rx_buf *rxb); -void il_hdl_error(struct il_priv *il, struct il_rx_buf *rxb); -void il_hdl_csa(struct il_priv *il, struct il_rx_buf *rxb); - -/***************************************************** -* RX -******************************************************/ -void il_cmd_queue_unmap(struct il_priv *il); -void il_cmd_queue_free(struct il_priv *il); -int il_rx_queue_alloc(struct il_priv *il); -void il_rx_queue_update_write_ptr(struct il_priv *il, struct il_rx_queue *q); -int il_rx_queue_space(const struct il_rx_queue *q); -void il_tx_cmd_complete(struct il_priv *il, struct il_rx_buf *rxb); - -void il_hdl_spectrum_measurement(struct il_priv *il, struct il_rx_buf *rxb); -void il_recover_from_stats(struct il_priv *il, struct il_rx_pkt *pkt); -void il_chswitch_done(struct il_priv *il, bool is_success); - -/***************************************************** -* TX -******************************************************/ -void il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq); -int il_tx_queue_init(struct il_priv *il, u32 txq_id); -void il_tx_queue_reset(struct il_priv *il, u32 txq_id); -void il_tx_queue_unmap(struct il_priv *il, int txq_id); -void il_tx_queue_free(struct il_priv *il, int txq_id); -void il_setup_watchdog(struct il_priv *il); -/***************************************************** - * TX power - ****************************************************/ -int il_set_tx_power(struct il_priv *il, s8 tx_power, bool force); - -/******************************************************************************* - * Rate - ******************************************************************************/ - -u8 il_get_lowest_plcp(struct il_priv *il); - -/******************************************************************************* - * Scanning - ******************************************************************************/ -void il_init_scan_params(struct il_priv *il); -int il_scan_cancel(struct il_priv *il); -int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms); -void il_force_scan_end(struct il_priv *il); -int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req); -void il_internal_short_hw_scan(struct il_priv *il); -int il_force_reset(struct il_priv *il, bool external); -u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, - const u8 *ta, const u8 *ie, int ie_len, int left); -void il_setup_rx_scan_handlers(struct il_priv *il); -u16 il_get_active_dwell_time(struct il_priv *il, enum ieee80211_band band, - u8 n_probes); -u16 il_get_passive_dwell_time(struct il_priv *il, enum ieee80211_band band, - struct ieee80211_vif *vif); -void il_setup_scan_deferred_work(struct il_priv *il); -void il_cancel_scan_deferred_work(struct il_priv *il); - -/* For faster active scanning, scan will move to the next channel if fewer than - * PLCP_QUIET_THRESH packets are heard on this channel within - * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell - * time if it's a quiet channel (nothing responded to our probe, and there's - * no other traffic). - * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */ -#define IL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */ -#define IL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */ - -#define IL_SCAN_CHECK_WATCHDOG (HZ * 7) - -/***************************************************** - * S e n d i n g H o s t C o m m a n d s * - *****************************************************/ - -const char *il_get_cmd_string(u8 cmd); -int __must_check il_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd); -int il_send_cmd(struct il_priv *il, struct il_host_cmd *cmd); -int __must_check il_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, - const void *data); -int il_send_cmd_pdu_async(struct il_priv *il, u8 id, u16 len, const void *data, - void (*callback) (struct il_priv *il, - struct il_device_cmd *cmd, - struct il_rx_pkt *pkt)); - -int il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd); - -/***************************************************** - * PCI * - *****************************************************/ - -void il_bg_watchdog(unsigned long data); -u32 il_usecs_to_beacons(struct il_priv *il, u32 usec, u32 beacon_interval); -__le32 il_add_beacon_time(struct il_priv *il, u32 base, u32 addon, - u32 beacon_interval); - -#ifdef CONFIG_PM_SLEEP -extern const struct dev_pm_ops il_pm_ops; - -#define IL_LEGACY_PM_OPS (&il_pm_ops) - -#else /* !CONFIG_PM_SLEEP */ - -#define IL_LEGACY_PM_OPS NULL - -#endif /* !CONFIG_PM_SLEEP */ - -/***************************************************** -* Error Handling Debugging -******************************************************/ -void il4965_dump_nic_error_log(struct il_priv *il); -#ifdef CONFIG_IWLEGACY_DEBUG -void il_print_rx_config_cmd(struct il_priv *il); -#else -static inline void -il_print_rx_config_cmd(struct il_priv *il) -{ -} -#endif - -void il_clear_isr_stats(struct il_priv *il); - -/***************************************************** -* GEOS -******************************************************/ -int il_init_geos(struct il_priv *il); -void il_free_geos(struct il_priv *il); - -/*************** DRIVER STATUS FUNCTIONS *****/ - -#define S_HCMD_ACTIVE 0 /* host command in progress */ -/* 1 is unused (used to be S_HCMD_SYNC_ACTIVE) */ -#define S_INT_ENABLED 2 -#define S_RFKILL 3 -#define S_CT_KILL 4 -#define S_INIT 5 -#define S_ALIVE 6 -#define S_READY 7 -#define S_TEMPERATURE 8 -#define S_GEO_CONFIGURED 9 -#define S_EXIT_PENDING 10 -#define S_STATS 12 -#define S_SCANNING 13 -#define S_SCAN_ABORTING 14 -#define S_SCAN_HW 15 -#define S_POWER_PMI 16 -#define S_FW_ERROR 17 -#define S_CHANNEL_SWITCH_PENDING 18 - -static inline int -il_is_ready(struct il_priv *il) -{ - /* The adapter is 'ready' if READY and GEO_CONFIGURED bits are - * set but EXIT_PENDING is not */ - return test_bit(S_READY, &il->status) && - test_bit(S_GEO_CONFIGURED, &il->status) && - !test_bit(S_EXIT_PENDING, &il->status); -} - -static inline int -il_is_alive(struct il_priv *il) -{ - return test_bit(S_ALIVE, &il->status); -} - -static inline int -il_is_init(struct il_priv *il) -{ - return test_bit(S_INIT, &il->status); -} - -static inline int -il_is_rfkill(struct il_priv *il) -{ - return test_bit(S_RFKILL, &il->status); -} - -static inline int -il_is_ctkill(struct il_priv *il) -{ - return test_bit(S_CT_KILL, &il->status); -} - -static inline int -il_is_ready_rf(struct il_priv *il) -{ - - if (il_is_rfkill(il)) - return 0; - - return il_is_ready(il); -} - -void il_send_bt_config(struct il_priv *il); -int il_send_stats_request(struct il_priv *il, u8 flags, bool clear); -void il_apm_stop(struct il_priv *il); -void _il_apm_stop(struct il_priv *il); - -int il_apm_init(struct il_priv *il); - -int il_send_rxon_timing(struct il_priv *il); - -static inline int -il_send_rxon_assoc(struct il_priv *il) -{ - return il->ops->rxon_assoc(il); -} - -static inline int -il_commit_rxon(struct il_priv *il) -{ - return il->ops->commit_rxon(il); -} - -static inline const struct ieee80211_supported_band * -il_get_hw_mode(struct il_priv *il, enum ieee80211_band band) -{ - return il->hw->wiphy->bands[band]; -} - -/* mac80211 handlers */ -int il_mac_config(struct ieee80211_hw *hw, u32 changed); -void il_mac_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); -void il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, u32 changes); -void il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info, - __le16 fc, __le32 *tx_flags); - -irqreturn_t il_isr(int irq, void *data); - -void il_set_bit(struct il_priv *p, u32 r, u32 m); -void il_clear_bit(struct il_priv *p, u32 r, u32 m); -bool _il_grab_nic_access(struct il_priv *il); -int _il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout); -int il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout); -u32 il_rd_prph(struct il_priv *il, u32 reg); -void il_wr_prph(struct il_priv *il, u32 addr, u32 val); -u32 il_read_targ_mem(struct il_priv *il, u32 addr); -void il_write_targ_mem(struct il_priv *il, u32 addr, u32 val); - -static inline bool il_need_reclaim(struct il_priv *il, struct il_rx_pkt *pkt) -{ - /* Reclaim a command buffer only if this packet is a response - * to a (driver-originated) command. If the packet (e.g. Rx frame) - * originated from uCode, there is no command buffer to reclaim. - * Ucode should set SEQ_RX_FRAME bit if ucode-originated, but - * apparently a few don't get set; catch them here. - */ - return !(pkt->hdr.sequence & SEQ_RX_FRAME) && - pkt->hdr.cmd != N_STATS && pkt->hdr.cmd != C_TX && - pkt->hdr.cmd != N_RX_PHY && pkt->hdr.cmd != N_RX && - pkt->hdr.cmd != N_RX_MPDU && pkt->hdr.cmd != N_COMPRESSED_BA; -} - -static inline void -_il_write8(struct il_priv *il, u32 ofs, u8 val) -{ - writeb(val, il->hw_base + ofs); -} -#define il_write8(il, ofs, val) _il_write8(il, ofs, val) - -static inline void -_il_wr(struct il_priv *il, u32 ofs, u32 val) -{ - writel(val, il->hw_base + ofs); -} - -static inline u32 -_il_rd(struct il_priv *il, u32 ofs) -{ - return readl(il->hw_base + ofs); -} - -static inline void -_il_clear_bit(struct il_priv *il, u32 reg, u32 mask) -{ - _il_wr(il, reg, _il_rd(il, reg) & ~mask); -} - -static inline void -_il_set_bit(struct il_priv *il, u32 reg, u32 mask) -{ - _il_wr(il, reg, _il_rd(il, reg) | mask); -} - -static inline void -_il_release_nic_access(struct il_priv *il) -{ - _il_clear_bit(il, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - /* - * In above we are reading CSR_GP_CNTRL register, what will flush any - * previous writes, but still want write, which clear MAC_ACCESS_REQ - * bit, be performed on PCI bus before any other writes scheduled on - * different CPUs (after we drop reg_lock). - */ - mmiowb(); -} - -static inline u32 -il_rd(struct il_priv *il, u32 reg) -{ - u32 value; - unsigned long reg_flags; - - spin_lock_irqsave(&il->reg_lock, reg_flags); - _il_grab_nic_access(il); - value = _il_rd(il, reg); - _il_release_nic_access(il); - spin_unlock_irqrestore(&il->reg_lock, reg_flags); - return value; -} - -static inline void -il_wr(struct il_priv *il, u32 reg, u32 value) -{ - unsigned long reg_flags; - - spin_lock_irqsave(&il->reg_lock, reg_flags); - if (likely(_il_grab_nic_access(il))) { - _il_wr(il, reg, value); - _il_release_nic_access(il); - } - spin_unlock_irqrestore(&il->reg_lock, reg_flags); -} - -static inline u32 -_il_rd_prph(struct il_priv *il, u32 reg) -{ - _il_wr(il, HBUS_TARG_PRPH_RADDR, reg | (3 << 24)); - return _il_rd(il, HBUS_TARG_PRPH_RDAT); -} - -static inline void -_il_wr_prph(struct il_priv *il, u32 addr, u32 val) -{ - _il_wr(il, HBUS_TARG_PRPH_WADDR, ((addr & 0x0000FFFF) | (3 << 24))); - _il_wr(il, HBUS_TARG_PRPH_WDAT, val); -} - -static inline void -il_set_bits_prph(struct il_priv *il, u32 reg, u32 mask) -{ - unsigned long reg_flags; - - spin_lock_irqsave(&il->reg_lock, reg_flags); - if (likely(_il_grab_nic_access(il))) { - _il_wr_prph(il, reg, (_il_rd_prph(il, reg) | mask)); - _il_release_nic_access(il); - } - spin_unlock_irqrestore(&il->reg_lock, reg_flags); -} - -static inline void -il_set_bits_mask_prph(struct il_priv *il, u32 reg, u32 bits, u32 mask) -{ - unsigned long reg_flags; - - spin_lock_irqsave(&il->reg_lock, reg_flags); - if (likely(_il_grab_nic_access(il))) { - _il_wr_prph(il, reg, ((_il_rd_prph(il, reg) & mask) | bits)); - _il_release_nic_access(il); - } - spin_unlock_irqrestore(&il->reg_lock, reg_flags); -} - -static inline void -il_clear_bits_prph(struct il_priv *il, u32 reg, u32 mask) -{ - unsigned long reg_flags; - u32 val; - - spin_lock_irqsave(&il->reg_lock, reg_flags); - if (likely(_il_grab_nic_access(il))) { - val = _il_rd_prph(il, reg); - _il_wr_prph(il, reg, (val & ~mask)); - _il_release_nic_access(il); - } - spin_unlock_irqrestore(&il->reg_lock, reg_flags); -} - -#define HW_KEY_DYNAMIC 0 -#define HW_KEY_DEFAULT 1 - -#define IL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ -#define IL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ -#define IL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of - being activated */ -#define IL_STA_LOCAL BIT(3) /* station state not directed by mac80211; - (this is for the IBSS BSSID stations) */ -#define IL_STA_BCAST BIT(4) /* this station is the special bcast station */ - -void il_restore_stations(struct il_priv *il); -void il_clear_ucode_stations(struct il_priv *il); -void il_dealloc_bcast_stations(struct il_priv *il); -int il_get_free_ucode_key_idx(struct il_priv *il); -int il_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags); -int il_add_station_common(struct il_priv *il, const u8 *addr, bool is_ap, - struct ieee80211_sta *sta, u8 *sta_id_r); -int il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr); -int il_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); - -u8 il_prep_station(struct il_priv *il, const u8 *addr, bool is_ap, - struct ieee80211_sta *sta); - -int il_send_lq_cmd(struct il_priv *il, struct il_link_quality_cmd *lq, - u8 flags, bool init); - -/** - * il_clear_driver_stations - clear knowledge of all stations from driver - * @il: iwl il struct - * - * This is called during il_down() to make sure that in the case - * we're coming there from a hardware restart mac80211 will be - * able to reconfigure stations -- if we're getting there in the - * normal down flow then the stations will already be cleared. - */ -static inline void -il_clear_driver_stations(struct il_priv *il) -{ - unsigned long flags; - - spin_lock_irqsave(&il->sta_lock, flags); - memset(il->stations, 0, sizeof(il->stations)); - il->num_stations = 0; - il->ucode_key_table = 0; - spin_unlock_irqrestore(&il->sta_lock, flags); -} - -static inline int -il_sta_id(struct ieee80211_sta *sta) -{ - if (WARN_ON(!sta)) - return IL_INVALID_STATION; - - return ((struct il_station_priv_common *)sta->drv_priv)->sta_id; -} - -/** - * il_sta_id_or_broadcast - return sta_id or broadcast sta - * @il: iwl il - * @context: the current context - * @sta: mac80211 station - * - * In certain circumstances mac80211 passes a station pointer - * that may be %NULL, for example during TX or key setup. In - * that case, we need to use the broadcast station, so this - * inline wraps that pattern. - */ -static inline int -il_sta_id_or_broadcast(struct il_priv *il, struct ieee80211_sta *sta) -{ - int sta_id; - - if (!sta) - return il->hw_params.bcast_id; - - sta_id = il_sta_id(sta); - - /* - * mac80211 should not be passing a partially - * initialised station! - */ - WARN_ON(sta_id == IL_INVALID_STATION); - - return sta_id; -} - -/** - * il_queue_inc_wrap - increment queue idx, wrap back to beginning - * @idx -- current idx - * @n_bd -- total number of entries in queue (must be power of 2) - */ -static inline int -il_queue_inc_wrap(int idx, int n_bd) -{ - return ++idx & (n_bd - 1); -} - -/** - * il_queue_dec_wrap - decrement queue idx, wrap back to end - * @idx -- current idx - * @n_bd -- total number of entries in queue (must be power of 2) - */ -static inline int -il_queue_dec_wrap(int idx, int n_bd) -{ - return --idx & (n_bd - 1); -} - -/* TODO: Move fw_desc functions to iwl-pci.ko */ -static inline void -il_free_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc) -{ - if (desc->v_addr) - dma_free_coherent(&pci_dev->dev, desc->len, desc->v_addr, - desc->p_addr); - desc->v_addr = NULL; - desc->len = 0; -} - -static inline int -il_alloc_fw_desc(struct pci_dev *pci_dev, struct fw_desc *desc) -{ - if (!desc->len) { - desc->v_addr = NULL; - return -EINVAL; - } - - desc->v_addr = dma_alloc_coherent(&pci_dev->dev, desc->len, - &desc->p_addr, GFP_KERNEL); - return (desc->v_addr != NULL) ? 0 : -ENOMEM; -} - -/* - * we have 8 bits used like this: - * - * 7 6 5 4 3 2 1 0 - * | | | | | | | | - * | | | | | | +-+-------- AC queue (0-3) - * | | | | | | - * | +-+-+-+-+------------ HW queue ID - * | - * +---------------------- unused - */ -static inline void -il_set_swq_id(struct il_tx_queue *txq, u8 ac, u8 hwq) -{ - BUG_ON(ac > 3); /* only have 2 bits */ - BUG_ON(hwq > 31); /* only use 5 bits */ - - txq->swq_id = (hwq << 2) | ac; -} - -static inline void -_il_wake_queue(struct il_priv *il, u8 ac) -{ - if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0) - ieee80211_wake_queue(il->hw, ac); -} - -static inline void -_il_stop_queue(struct il_priv *il, u8 ac) -{ - if (atomic_inc_return(&il->queue_stop_count[ac]) > 0) - ieee80211_stop_queue(il->hw, ac); -} -static inline void -il_wake_queue(struct il_priv *il, struct il_tx_queue *txq) -{ - u8 queue = txq->swq_id; - u8 ac = queue & 3; - u8 hwq = (queue >> 2) & 0x1f; - - if (test_and_clear_bit(hwq, il->queue_stopped)) - _il_wake_queue(il, ac); -} - -static inline void -il_stop_queue(struct il_priv *il, struct il_tx_queue *txq) -{ - u8 queue = txq->swq_id; - u8 ac = queue & 3; - u8 hwq = (queue >> 2) & 0x1f; - - if (!test_and_set_bit(hwq, il->queue_stopped)) - _il_stop_queue(il, ac); -} - -static inline void -il_wake_queues_by_reason(struct il_priv *il, int reason) -{ - u8 ac; - - if (test_and_clear_bit(reason, &il->stop_reason)) - for (ac = 0; ac < 4; ac++) - _il_wake_queue(il, ac); -} - -static inline void -il_stop_queues_by_reason(struct il_priv *il, int reason) -{ - u8 ac; - - if (!test_and_set_bit(reason, &il->stop_reason)) - for (ac = 0; ac < 4; ac++) - _il_stop_queue(il, ac); -} - -#ifdef ieee80211_stop_queue -#undef ieee80211_stop_queue -#endif - -#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue - -#ifdef ieee80211_wake_queue -#undef ieee80211_wake_queue -#endif - -#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue - -static inline void -il_disable_interrupts(struct il_priv *il) -{ - clear_bit(S_INT_ENABLED, &il->status); - - /* disable interrupts from uCode/NIC to host */ - _il_wr(il, CSR_INT_MASK, 0x00000000); - - /* acknowledge/clear/reset any interrupts still pending - * from uCode or flow handler (Rx/Tx DMA) */ - _il_wr(il, CSR_INT, 0xffffffff); - _il_wr(il, CSR_FH_INT_STATUS, 0xffffffff); -} - -static inline void -il_enable_rfkill_int(struct il_priv *il) -{ - _il_wr(il, CSR_INT_MASK, CSR_INT_BIT_RF_KILL); -} - -static inline void -il_enable_interrupts(struct il_priv *il) -{ - set_bit(S_INT_ENABLED, &il->status); - _il_wr(il, CSR_INT_MASK, il->inta_mask); -} - -/** - * il_beacon_time_mask_low - mask of lower 32 bit of beacon time - * @il -- pointer to il_priv data structure - * @tsf_bits -- number of bits need to shift for masking) - */ -static inline u32 -il_beacon_time_mask_low(struct il_priv *il, u16 tsf_bits) -{ - return (1 << tsf_bits) - 1; -} - -/** - * il_beacon_time_mask_high - mask of higher 32 bit of beacon time - * @il -- pointer to il_priv data structure - * @tsf_bits -- number of bits need to shift for masking) - */ -static inline u32 -il_beacon_time_mask_high(struct il_priv *il, u16 tsf_bits) -{ - return ((1 << (32 - tsf_bits)) - 1) << tsf_bits; -} - -/** - * struct il_rb_status - reseve buffer status host memory mapped FH registers - * - * @closed_rb_num [0:11] - Indicates the idx of the RB which was closed - * @closed_fr_num [0:11] - Indicates the idx of the RX Frame which was closed - * @finished_rb_num [0:11] - Indicates the idx of the current RB - * in which the last frame was written to - * @finished_fr_num [0:11] - Indicates the idx of the RX Frame - * which was transferred - */ -struct il_rb_status { - __le16 closed_rb_num; - __le16 closed_fr_num; - __le16 finished_rb_num; - __le16 finished_fr_nam; - __le32 __unused; /* 3945 only */ -} __packed; - -#define TFD_QUEUE_SIZE_MAX 256 -#define TFD_QUEUE_SIZE_BC_DUP 64 -#define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP) -#define IL_TX_DMA_MASK DMA_BIT_MASK(36) -#define IL_NUM_OF_TBS 20 - -static inline u8 -il_get_dma_hi_addr(dma_addr_t addr) -{ - return (sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0) & 0xF; -} - -/** - * struct il_tfd_tb transmit buffer descriptor within transmit frame descriptor - * - * This structure contains dma address and length of transmission address - * - * @lo: low [31:0] portion of the dma address of TX buffer every even is - * unaligned on 16 bit boundary - * @hi_n_len: 0-3 [35:32] portion of dma - * 4-15 length of the tx buffer - */ -struct il_tfd_tb { - __le32 lo; - __le16 hi_n_len; -} __packed; - -/** - * struct il_tfd - * - * Transmit Frame Descriptor (TFD) - * - * @ __reserved1[3] reserved - * @ num_tbs 0-4 number of active tbs - * 5 reserved - * 6-7 padding (not used) - * @ tbs[20] transmit frame buffer descriptors - * @ __pad padding - * - * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM. - * Both driver and device share these circular buffers, each of which must be - * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes - * - * Driver must indicate the physical address of the base of each - * circular buffer via the FH49_MEM_CBBC_QUEUE registers. - * - * Each TFD contains pointer/size information for up to 20 data buffers - * in host DRAM. These buffers collectively contain the (one) frame described - * by the TFD. Each buffer must be a single contiguous block of memory within - * itself, but buffers may be scattered in host DRAM. Each buffer has max size - * of (4K - 4). The concatenates all of a TFD's buffers into a single - * Tx frame, up to 8 KBytes in size. - * - * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx. - */ -struct il_tfd { - u8 __reserved1[3]; - u8 num_tbs; - struct il_tfd_tb tbs[IL_NUM_OF_TBS]; - __le32 __pad; -} __packed; -/* PCI registers */ -#define PCI_CFG_RETRY_TIMEOUT 0x041 - -struct il_rate_info { - u8 plcp; /* uCode API: RATE_6M_PLCP, etc. */ - u8 plcp_siso; /* uCode API: RATE_SISO_6M_PLCP, etc. */ - u8 plcp_mimo2; /* uCode API: RATE_MIMO2_6M_PLCP, etc. */ - u8 ieee; /* MAC header: RATE_6M_IEEE, etc. */ - u8 prev_ieee; /* previous rate in IEEE speeds */ - u8 next_ieee; /* next rate in IEEE speeds */ - u8 prev_rs; /* previous rate used in rs algo */ - u8 next_rs; /* next rate used in rs algo */ - u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ - u8 next_rs_tgg; /* next rate used in TGG rs algo */ -}; - -struct il3945_rate_info { - u8 plcp; /* uCode API: RATE_6M_PLCP, etc. */ - u8 ieee; /* MAC header: RATE_6M_IEEE, etc. */ - u8 prev_ieee; /* previous rate in IEEE speeds */ - u8 next_ieee; /* next rate in IEEE speeds */ - u8 prev_rs; /* previous rate used in rs algo */ - u8 next_rs; /* next rate used in rs algo */ - u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ - u8 next_rs_tgg; /* next rate used in TGG rs algo */ - u8 table_rs_idx; /* idx in rate scale table cmd */ - u8 prev_table_rs; /* prev in rate table cmd */ -}; - -/* - * These serve as idxes into - * struct il_rate_info il_rates[RATE_COUNT]; - */ -enum { - RATE_1M_IDX = 0, - RATE_2M_IDX, - RATE_5M_IDX, - RATE_11M_IDX, - RATE_6M_IDX, - RATE_9M_IDX, - RATE_12M_IDX, - RATE_18M_IDX, - RATE_24M_IDX, - RATE_36M_IDX, - RATE_48M_IDX, - RATE_54M_IDX, - RATE_60M_IDX, - RATE_COUNT, - RATE_COUNT_LEGACY = RATE_COUNT - 1, /* Excluding 60M */ - RATE_COUNT_3945 = RATE_COUNT - 1, - RATE_INVM_IDX = RATE_COUNT, - RATE_INVALID = RATE_COUNT, -}; - -enum { - RATE_6M_IDX_TBL = 0, - RATE_9M_IDX_TBL, - RATE_12M_IDX_TBL, - RATE_18M_IDX_TBL, - RATE_24M_IDX_TBL, - RATE_36M_IDX_TBL, - RATE_48M_IDX_TBL, - RATE_54M_IDX_TBL, - RATE_1M_IDX_TBL, - RATE_2M_IDX_TBL, - RATE_5M_IDX_TBL, - RATE_11M_IDX_TBL, - RATE_INVM_IDX_TBL = RATE_INVM_IDX - 1, -}; - -enum { - IL_FIRST_OFDM_RATE = RATE_6M_IDX, - IL39_LAST_OFDM_RATE = RATE_54M_IDX, - IL_LAST_OFDM_RATE = RATE_60M_IDX, - IL_FIRST_CCK_RATE = RATE_1M_IDX, - IL_LAST_CCK_RATE = RATE_11M_IDX, -}; - -/* #define vs. enum to keep from defaulting to 'large integer' */ -#define RATE_6M_MASK (1 << RATE_6M_IDX) -#define RATE_9M_MASK (1 << RATE_9M_IDX) -#define RATE_12M_MASK (1 << RATE_12M_IDX) -#define RATE_18M_MASK (1 << RATE_18M_IDX) -#define RATE_24M_MASK (1 << RATE_24M_IDX) -#define RATE_36M_MASK (1 << RATE_36M_IDX) -#define RATE_48M_MASK (1 << RATE_48M_IDX) -#define RATE_54M_MASK (1 << RATE_54M_IDX) -#define RATE_60M_MASK (1 << RATE_60M_IDX) -#define RATE_1M_MASK (1 << RATE_1M_IDX) -#define RATE_2M_MASK (1 << RATE_2M_IDX) -#define RATE_5M_MASK (1 << RATE_5M_IDX) -#define RATE_11M_MASK (1 << RATE_11M_IDX) - -/* uCode API values for legacy bit rates, both OFDM and CCK */ -enum { - RATE_6M_PLCP = 13, - RATE_9M_PLCP = 15, - RATE_12M_PLCP = 5, - RATE_18M_PLCP = 7, - RATE_24M_PLCP = 9, - RATE_36M_PLCP = 11, - RATE_48M_PLCP = 1, - RATE_54M_PLCP = 3, - RATE_60M_PLCP = 3, /*FIXME:RS:should be removed */ - RATE_1M_PLCP = 10, - RATE_2M_PLCP = 20, - RATE_5M_PLCP = 55, - RATE_11M_PLCP = 110, - /*FIXME:RS:add RATE_LEGACY_INVM_PLCP = 0, */ -}; - -/* uCode API values for OFDM high-throughput (HT) bit rates */ -enum { - RATE_SISO_6M_PLCP = 0, - RATE_SISO_12M_PLCP = 1, - RATE_SISO_18M_PLCP = 2, - RATE_SISO_24M_PLCP = 3, - RATE_SISO_36M_PLCP = 4, - RATE_SISO_48M_PLCP = 5, - RATE_SISO_54M_PLCP = 6, - RATE_SISO_60M_PLCP = 7, - RATE_MIMO2_6M_PLCP = 0x8, - RATE_MIMO2_12M_PLCP = 0x9, - RATE_MIMO2_18M_PLCP = 0xa, - RATE_MIMO2_24M_PLCP = 0xb, - RATE_MIMO2_36M_PLCP = 0xc, - RATE_MIMO2_48M_PLCP = 0xd, - RATE_MIMO2_54M_PLCP = 0xe, - RATE_MIMO2_60M_PLCP = 0xf, - RATE_SISO_INVM_PLCP, - RATE_MIMO2_INVM_PLCP = RATE_SISO_INVM_PLCP, -}; - -/* MAC header values for bit rates */ -enum { - RATE_6M_IEEE = 12, - RATE_9M_IEEE = 18, - RATE_12M_IEEE = 24, - RATE_18M_IEEE = 36, - RATE_24M_IEEE = 48, - RATE_36M_IEEE = 72, - RATE_48M_IEEE = 96, - RATE_54M_IEEE = 108, - RATE_60M_IEEE = 120, - RATE_1M_IEEE = 2, - RATE_2M_IEEE = 4, - RATE_5M_IEEE = 11, - RATE_11M_IEEE = 22, -}; - -#define IL_CCK_BASIC_RATES_MASK \ - (RATE_1M_MASK | \ - RATE_2M_MASK) - -#define IL_CCK_RATES_MASK \ - (IL_CCK_BASIC_RATES_MASK | \ - RATE_5M_MASK | \ - RATE_11M_MASK) - -#define IL_OFDM_BASIC_RATES_MASK \ - (RATE_6M_MASK | \ - RATE_12M_MASK | \ - RATE_24M_MASK) - -#define IL_OFDM_RATES_MASK \ - (IL_OFDM_BASIC_RATES_MASK | \ - RATE_9M_MASK | \ - RATE_18M_MASK | \ - RATE_36M_MASK | \ - RATE_48M_MASK | \ - RATE_54M_MASK) - -#define IL_BASIC_RATES_MASK \ - (IL_OFDM_BASIC_RATES_MASK | \ - IL_CCK_BASIC_RATES_MASK) - -#define RATES_MASK ((1 << RATE_COUNT) - 1) -#define RATES_MASK_3945 ((1 << RATE_COUNT_3945) - 1) - -#define IL_INVALID_VALUE -1 - -#define IL_MIN_RSSI_VAL -100 -#define IL_MAX_RSSI_VAL 0 - -/* These values specify how many Tx frame attempts before - * searching for a new modulation mode */ -#define IL_LEGACY_FAILURE_LIMIT 160 -#define IL_LEGACY_SUCCESS_LIMIT 480 -#define IL_LEGACY_TBL_COUNT 160 - -#define IL_NONE_LEGACY_FAILURE_LIMIT 400 -#define IL_NONE_LEGACY_SUCCESS_LIMIT 4500 -#define IL_NONE_LEGACY_TBL_COUNT 1500 - -/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ -#define IL_RS_GOOD_RATIO 12800 /* 100% */ -#define RATE_SCALE_SWITCH 10880 /* 85% */ -#define RATE_HIGH_TH 10880 /* 85% */ -#define RATE_INCREASE_TH 6400 /* 50% */ -#define RATE_DECREASE_TH 1920 /* 15% */ - -/* possible actions when in legacy mode */ -#define IL_LEGACY_SWITCH_ANTENNA1 0 -#define IL_LEGACY_SWITCH_ANTENNA2 1 -#define IL_LEGACY_SWITCH_SISO 2 -#define IL_LEGACY_SWITCH_MIMO2_AB 3 -#define IL_LEGACY_SWITCH_MIMO2_AC 4 -#define IL_LEGACY_SWITCH_MIMO2_BC 5 - -/* possible actions when in siso mode */ -#define IL_SISO_SWITCH_ANTENNA1 0 -#define IL_SISO_SWITCH_ANTENNA2 1 -#define IL_SISO_SWITCH_MIMO2_AB 2 -#define IL_SISO_SWITCH_MIMO2_AC 3 -#define IL_SISO_SWITCH_MIMO2_BC 4 -#define IL_SISO_SWITCH_GI 5 - -/* possible actions when in mimo mode */ -#define IL_MIMO2_SWITCH_ANTENNA1 0 -#define IL_MIMO2_SWITCH_ANTENNA2 1 -#define IL_MIMO2_SWITCH_SISO_A 2 -#define IL_MIMO2_SWITCH_SISO_B 3 -#define IL_MIMO2_SWITCH_SISO_C 4 -#define IL_MIMO2_SWITCH_GI 5 - -#define IL_MAX_SEARCH IL_MIMO2_SWITCH_GI - -#define IL_ACTION_LIMIT 3 /* # possible actions */ - -#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ - -/* load per tid defines for A-MPDU activation */ -#define IL_AGG_TPT_THREHOLD 0 -#define IL_AGG_LOAD_THRESHOLD 10 -#define IL_AGG_ALL_TID 0xff -#define TID_QUEUE_CELL_SPACING 50 /*mS */ -#define TID_QUEUE_MAX_SIZE 20 -#define TID_ROUND_VALUE 5 /* mS */ -#define TID_MAX_LOAD_COUNT 8 - -#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) -#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) - -extern const struct il_rate_info il_rates[RATE_COUNT]; - -enum il_table_type { - LQ_NONE, - LQ_G, /* legacy types */ - LQ_A, - LQ_SISO, /* high-throughput types */ - LQ_MIMO2, - LQ_MAX, -}; - -#define is_legacy(tbl) ((tbl) == LQ_G || (tbl) == LQ_A) -#define is_siso(tbl) ((tbl) == LQ_SISO) -#define is_mimo2(tbl) ((tbl) == LQ_MIMO2) -#define is_mimo(tbl) (is_mimo2(tbl)) -#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) -#define is_a_band(tbl) ((tbl) == LQ_A) -#define is_g_and(tbl) ((tbl) == LQ_G) - -#define ANT_NONE 0x0 -#define ANT_A BIT(0) -#define ANT_B BIT(1) -#define ANT_AB (ANT_A | ANT_B) -#define ANT_C BIT(2) -#define ANT_AC (ANT_A | ANT_C) -#define ANT_BC (ANT_B | ANT_C) -#define ANT_ABC (ANT_AB | ANT_C) - -#define IL_MAX_MCS_DISPLAY_SIZE 12 - -struct il_rate_mcs_info { - char mbps[IL_MAX_MCS_DISPLAY_SIZE]; - char mcs[IL_MAX_MCS_DISPLAY_SIZE]; -}; - -/** - * struct il_rate_scale_data -- tx success history for one rate - */ -struct il_rate_scale_data { - u64 data; /* bitmap of successful frames */ - s32 success_counter; /* number of frames successful */ - s32 success_ratio; /* per-cent * 128 */ - s32 counter; /* number of frames attempted */ - s32 average_tpt; /* success ratio * expected throughput */ - unsigned long stamp; -}; - -/** - * struct il_scale_tbl_info -- tx params and success history for all rates - * - * There are two of these in struct il_lq_sta, - * one for "active", and one for "search". - */ -struct il_scale_tbl_info { - enum il_table_type lq_type; - u8 ant_type; - u8 is_SGI; /* 1 = short guard interval */ - u8 is_ht40; /* 1 = 40 MHz channel width */ - u8 is_dup; /* 1 = duplicated data streams */ - u8 action; /* change modulation; IL_[LEGACY/SISO/MIMO]_SWITCH_* */ - u8 max_search; /* maximun number of tables we can search */ - s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ - u32 current_rate; /* rate_n_flags, uCode API format */ - struct il_rate_scale_data win[RATE_COUNT]; /* rate histories */ -}; - -struct il_traffic_load { - unsigned long time_stamp; /* age of the oldest stats */ - u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time - * slice */ - u32 total; /* total num of packets during the - * last TID_MAX_TIME_DIFF */ - u8 queue_count; /* number of queues that has - * been used since the last cleanup */ - u8 head; /* start of the circular buffer */ -}; - -/** - * struct il_lq_sta -- driver's rate scaling ilate structure - * - * Pointer to this gets passed back and forth between driver and mac80211. - */ -struct il_lq_sta { - u8 active_tbl; /* idx of active table, range 0-1 */ - u8 enable_counter; /* indicates HT mode */ - u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ - u8 search_better_tbl; /* 1: currently trying alternate mode */ - s32 last_tpt; - - /* The following determine when to search for a new mode */ - u32 table_count_limit; - u32 max_failure_limit; /* # failed frames before new search */ - u32 max_success_limit; /* # successful frames before new search */ - u32 table_count; - u32 total_failed; /* total failed frames, any/all rates */ - u32 total_success; /* total successful frames, any/all rates */ - u64 flush_timer; /* time staying in mode before new search */ - - u8 action_counter; /* # mode-switch actions tried */ - u8 is_green; - u8 is_dup; - enum ieee80211_band band; - - /* The following are bitmaps of rates; RATE_6M_MASK, etc. */ - u32 supp_rates; - u16 active_legacy_rate; - u16 active_siso_rate; - u16 active_mimo2_rate; - s8 max_rate_idx; /* Max rate set by user */ - u8 missed_rate_counter; - - struct il_link_quality_cmd lq; - struct il_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ - struct il_traffic_load load[TID_MAX_LOAD_COUNT]; - u8 tx_agg_tid_en; -#ifdef CONFIG_MAC80211_DEBUGFS - struct dentry *rs_sta_dbgfs_scale_table_file; - struct dentry *rs_sta_dbgfs_stats_table_file; - struct dentry *rs_sta_dbgfs_rate_scale_data_file; - struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; - u32 dbg_fixed_rate; -#endif - struct il_priv *drv; - - /* used to be in sta_info */ - int last_txrate_idx; - /* last tx rate_n_flags */ - u32 last_rate_n_flags; - /* packets destined for this STA are aggregated */ - u8 is_agg; -}; - -/* - * il_station_priv: Driver's ilate station information - * - * When mac80211 creates a station it reserves some space (hw->sta_data_size) - * in the structure for use by driver. This structure is places in that - * space. - * - * The common struct MUST be first because it is shared between - * 3945 and 4965! - */ -struct il_station_priv { - struct il_station_priv_common common; - struct il_lq_sta lq_sta; - atomic_t pending_frames; - bool client; - bool asleep; -}; - -static inline u8 -il4965_num_of_ant(u8 m) -{ - return !!(m & ANT_A) + !!(m & ANT_B) + !!(m & ANT_C); -} - -static inline u8 -il4965_first_antenna(u8 mask) -{ - if (mask & ANT_A) - return ANT_A; - if (mask & ANT_B) - return ANT_B; - return ANT_C; -} - -/** - * il3945_rate_scale_init - Initialize the rate scale table based on assoc info - * - * The specific throughput table used is based on the type of network - * the associated with, including A, B, G, and G w/ TGG protection - */ -void il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); - -/* Initialize station's rate scaling information after adding station */ -void il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, - u8 sta_id); -void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, - u8 sta_id); - -/** - * il_rate_control_register - Register the rate control algorithm callbacks - * - * Since the rate control algorithm is hardware specific, there is no need - * or reason to place it as a stand alone module. The driver can call - * il_rate_control_register in order to register the rate control callbacks - * with the mac80211 subsystem. This should be performed prior to calling - * ieee80211_register_hw - * - */ -int il4965_rate_control_register(void); -int il3945_rate_control_register(void); - -/** - * il_rate_control_unregister - Unregister the rate control callbacks - * - * This should be called after calling ieee80211_unregister_hw, but before - * the driver is unloaded. - */ -void il4965_rate_control_unregister(void); -void il3945_rate_control_unregister(void); - -int il_power_update_mode(struct il_priv *il, bool force); -void il_power_initialize(struct il_priv *il); - -extern u32 il_debug_level; - -#ifdef CONFIG_IWLEGACY_DEBUG -/* - * il_get_debug_level: Return active debug level for device - * - * Using sysfs it is possible to set per device debug level. This debug - * level will be used if set, otherwise the global debug level which can be - * set via module parameter is used. - */ -static inline u32 -il_get_debug_level(struct il_priv *il) -{ - if (il->debug_level) - return il->debug_level; - else - return il_debug_level; -} -#else -static inline u32 -il_get_debug_level(struct il_priv *il) -{ - return il_debug_level; -} -#endif - -#define il_print_hex_error(il, p, len) \ -do { \ - print_hex_dump(KERN_ERR, "iwl data: ", \ - DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ -} while (0) - -#ifdef CONFIG_IWLEGACY_DEBUG -#define IL_DBG(level, fmt, args...) \ -do { \ - if (il_get_debug_level(il) & level) \ - dev_err(&il->hw->wiphy->dev, "%c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __func__ , ##args); \ -} while (0) - -#define il_print_hex_dump(il, level, p, len) \ -do { \ - if (il_get_debug_level(il) & level) \ - print_hex_dump(KERN_DEBUG, "iwl data: ", \ - DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ -} while (0) - -#else -#define IL_DBG(level, fmt, args...) -static inline void -il_print_hex_dump(struct il_priv *il, int level, const void *p, u32 len) -{ -} -#endif /* CONFIG_IWLEGACY_DEBUG */ - -#ifdef CONFIG_IWLEGACY_DEBUGFS -int il_dbgfs_register(struct il_priv *il, const char *name); -void il_dbgfs_unregister(struct il_priv *il); -#else -static inline int -il_dbgfs_register(struct il_priv *il, const char *name) -{ - return 0; -} - -static inline void -il_dbgfs_unregister(struct il_priv *il) -{ -} -#endif /* CONFIG_IWLEGACY_DEBUGFS */ - -/* - * To use the debug system: - * - * If you are defining a new debug classification, simply add it to the #define - * list here in the form of - * - * #define IL_DL_xxxx VALUE - * - * where xxxx should be the name of the classification (for example, WEP). - * - * You then need to either add a IL_xxxx_DEBUG() macro definition for your - * classification, or use IL_DBG(IL_DL_xxxx, ...) whenever you want - * to send output to that classification. - * - * The active debug levels can be accessed via files - * - * /sys/module/iwl4965/parameters/debug - * /sys/module/iwl3945/parameters/debug - * /sys/class/net/wlan0/device/debug_level - * - * when CONFIG_IWLEGACY_DEBUG=y. - */ - -/* 0x0000000F - 0x00000001 */ -#define IL_DL_INFO (1 << 0) -#define IL_DL_MAC80211 (1 << 1) -#define IL_DL_HCMD (1 << 2) -#define IL_DL_STATE (1 << 3) -/* 0x000000F0 - 0x00000010 */ -#define IL_DL_MACDUMP (1 << 4) -#define IL_DL_HCMD_DUMP (1 << 5) -#define IL_DL_EEPROM (1 << 6) -#define IL_DL_RADIO (1 << 7) -/* 0x00000F00 - 0x00000100 */ -#define IL_DL_POWER (1 << 8) -#define IL_DL_TEMP (1 << 9) -#define IL_DL_NOTIF (1 << 10) -#define IL_DL_SCAN (1 << 11) -/* 0x0000F000 - 0x00001000 */ -#define IL_DL_ASSOC (1 << 12) -#define IL_DL_DROP (1 << 13) -#define IL_DL_TXPOWER (1 << 14) -#define IL_DL_AP (1 << 15) -/* 0x000F0000 - 0x00010000 */ -#define IL_DL_FW (1 << 16) -#define IL_DL_RF_KILL (1 << 17) -#define IL_DL_FW_ERRORS (1 << 18) -#define IL_DL_LED (1 << 19) -/* 0x00F00000 - 0x00100000 */ -#define IL_DL_RATE (1 << 20) -#define IL_DL_CALIB (1 << 21) -#define IL_DL_WEP (1 << 22) -#define IL_DL_TX (1 << 23) -/* 0x0F000000 - 0x01000000 */ -#define IL_DL_RX (1 << 24) -#define IL_DL_ISR (1 << 25) -#define IL_DL_HT (1 << 26) -/* 0xF0000000 - 0x10000000 */ -#define IL_DL_11H (1 << 28) -#define IL_DL_STATS (1 << 29) -#define IL_DL_TX_REPLY (1 << 30) -#define IL_DL_QOS (1 << 31) - -#define D_INFO(f, a...) IL_DBG(IL_DL_INFO, f, ## a) -#define D_MAC80211(f, a...) IL_DBG(IL_DL_MAC80211, f, ## a) -#define D_MACDUMP(f, a...) IL_DBG(IL_DL_MACDUMP, f, ## a) -#define D_TEMP(f, a...) IL_DBG(IL_DL_TEMP, f, ## a) -#define D_SCAN(f, a...) IL_DBG(IL_DL_SCAN, f, ## a) -#define D_RX(f, a...) IL_DBG(IL_DL_RX, f, ## a) -#define D_TX(f, a...) IL_DBG(IL_DL_TX, f, ## a) -#define D_ISR(f, a...) IL_DBG(IL_DL_ISR, f, ## a) -#define D_LED(f, a...) IL_DBG(IL_DL_LED, f, ## a) -#define D_WEP(f, a...) IL_DBG(IL_DL_WEP, f, ## a) -#define D_HC(f, a...) IL_DBG(IL_DL_HCMD, f, ## a) -#define D_HC_DUMP(f, a...) IL_DBG(IL_DL_HCMD_DUMP, f, ## a) -#define D_EEPROM(f, a...) IL_DBG(IL_DL_EEPROM, f, ## a) -#define D_CALIB(f, a...) IL_DBG(IL_DL_CALIB, f, ## a) -#define D_FW(f, a...) IL_DBG(IL_DL_FW, f, ## a) -#define D_RF_KILL(f, a...) IL_DBG(IL_DL_RF_KILL, f, ## a) -#define D_DROP(f, a...) IL_DBG(IL_DL_DROP, f, ## a) -#define D_AP(f, a...) IL_DBG(IL_DL_AP, f, ## a) -#define D_TXPOWER(f, a...) IL_DBG(IL_DL_TXPOWER, f, ## a) -#define D_RATE(f, a...) IL_DBG(IL_DL_RATE, f, ## a) -#define D_NOTIF(f, a...) IL_DBG(IL_DL_NOTIF, f, ## a) -#define D_ASSOC(f, a...) IL_DBG(IL_DL_ASSOC, f, ## a) -#define D_HT(f, a...) IL_DBG(IL_DL_HT, f, ## a) -#define D_STATS(f, a...) IL_DBG(IL_DL_STATS, f, ## a) -#define D_TX_REPLY(f, a...) IL_DBG(IL_DL_TX_REPLY, f, ## a) -#define D_QOS(f, a...) IL_DBG(IL_DL_QOS, f, ## a) -#define D_RADIO(f, a...) IL_DBG(IL_DL_RADIO, f, ## a) -#define D_POWER(f, a...) IL_DBG(IL_DL_POWER, f, ## a) -#define D_11H(f, a...) IL_DBG(IL_DL_11H, f, ## a) - -#endif /* __il_core_h__ */ diff --git a/drivers/net/wireless/iwlegacy/csr.h b/drivers/net/wireless/iwlegacy/csr.h deleted file mode 100644 index 9138e15004fa..000000000000 --- a/drivers/net/wireless/iwlegacy/csr.h +++ /dev/null @@ -1,419 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __il_csr_h__ -#define __il_csr_h__ -/* - * CSR (control and status registers) - * - * CSR registers are mapped directly into PCI bus space, and are accessible - * whenever platform supplies power to device, even when device is in - * low power states due to driver-invoked device resets - * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes. - * - * Use _il_wr() and _il_rd() family to access these registers; - * these provide simple PCI bus access, without waking up the MAC. - * Do not use il_wr() family for these registers; - * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ. - * The MAC (uCode processor, etc.) does not need to be powered up for accessing - * the CSR registers. - * - * NOTE: Device does need to be awake in order to read this memory - * via CSR_EEPROM register - */ -#define CSR_BASE (0x000) - -#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */ -#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */ -#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */ -#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */ -#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack */ -#define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */ -#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc */ -#define CSR_GP_CNTRL (CSR_BASE+0x024) - -/* 2nd byte of CSR_INT_COALESCING, not accessible via _il_wr()! */ -#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005) - -/* - * Hardware revision info - * Bit fields: - * 31-8: Reserved - * 7-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions - * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D - * 1-0: "Dash" (-) value, as in A-1, etc. - * - * NOTE: Revision step affects calculation of CCK txpower for 4965. - * NOTE: See also CSR_HW_REV_WA_REG (work-around for bug in 4965). - */ -#define CSR_HW_REV (CSR_BASE+0x028) - -/* - * EEPROM memory reads - * - * NOTE: Device must be awake, initialized via apm_ops.init(), - * in order to read. - */ -#define CSR_EEPROM_REG (CSR_BASE+0x02c) -#define CSR_EEPROM_GP (CSR_BASE+0x030) - -#define CSR_GIO_REG (CSR_BASE+0x03C) -#define CSR_GP_UCODE_REG (CSR_BASE+0x048) -#define CSR_GP_DRIVER_REG (CSR_BASE+0x050) - -/* - * UCODE-DRIVER GP (general purpose) mailbox registers. - * SET/CLR registers set/clear bit(s) if "1" is written. - */ -#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054) -#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058) -#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c) -#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060) - -#define CSR_LED_REG (CSR_BASE+0x094) -#define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0) - -/* GIO Chicken Bits (PCI Express bus link power management) */ -#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100) - -/* Analog phase-lock-loop configuration */ -#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c) - -/* - * CSR Hardware Revision Workaround Register. Indicates hardware rev; - * "step" determines CCK backoff for txpower calculation. Used for 4965 only. - * See also CSR_HW_REV register. - * Bit fields: - * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step - * 1-0: "Dash" (-) value, as in C-1, etc. - */ -#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C) - -#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240) -#define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250) - -/* Bits for CSR_HW_IF_CONFIG_REG */ -#define CSR49_HW_IF_CONFIG_REG_BIT_4965_R (0x00000010) -#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x00000C00) -#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100) -#define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200) - -#define CSR39_HW_IF_CONFIG_REG_BIT_3945_MB (0x00000100) -#define CSR39_HW_IF_CONFIG_REG_BIT_3945_MM (0x00000200) -#define CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC (0x00000400) -#define CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE (0x00000800) -#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A (0x00000000) -#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B (0x00001000) - -#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) -#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) -#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ -#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ -#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ - -#define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int */ -#define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec */ - -/* interrupt flags in INTA, set by uCode or hardware (e.g. dma), - * acknowledged (reset) by host writing "1" to flagged bits. */ -#define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */ -#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */ -#define CSR_INT_BIT_RX_PERIODIC (1 << 28) /* Rx periodic */ -#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */ -#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */ -#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */ -#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */ -#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */ -#define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses, 3945 */ -#define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */ -#define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */ - -#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ - CSR_INT_BIT_HW_ERR | \ - CSR_INT_BIT_FH_TX | \ - CSR_INT_BIT_SW_ERR | \ - CSR_INT_BIT_RF_KILL | \ - CSR_INT_BIT_SW_RX | \ - CSR_INT_BIT_WAKEUP | \ - CSR_INT_BIT_ALIVE) - -/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ -#define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */ -#define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */ -#define CSR39_FH_INT_BIT_RX_CHNL2 (1 << 18) /* Rx channel 2 (3945 only) */ -#define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */ -#define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */ -#define CSR39_FH_INT_BIT_TX_CHNL6 (1 << 6) /* Tx channel 6 (3945 only) */ -#define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */ -#define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */ - -#define CSR39_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ - CSR39_FH_INT_BIT_RX_CHNL2 | \ - CSR_FH_INT_BIT_RX_CHNL1 | \ - CSR_FH_INT_BIT_RX_CHNL0) - -#define CSR39_FH_INT_TX_MASK (CSR39_FH_INT_BIT_TX_CHNL6 | \ - CSR_FH_INT_BIT_TX_CHNL1 | \ - CSR_FH_INT_BIT_TX_CHNL0) - -#define CSR49_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ - CSR_FH_INT_BIT_RX_CHNL1 | \ - CSR_FH_INT_BIT_RX_CHNL0) - -#define CSR49_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \ - CSR_FH_INT_BIT_TX_CHNL0) - -/* GPIO */ -#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200) -#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000) -#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC (0x00000200) - -/* RESET */ -#define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001) -#define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002) -#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080) -#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100) -#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200) -#define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000) - -/* - * GP (general purpose) CONTROL REGISTER - * Bit fields: - * 27: HW_RF_KILL_SW - * Indicates state of (platform's) hardware RF-Kill switch - * 26-24: POWER_SAVE_TYPE - * Indicates current power-saving mode: - * 000 -- No power saving - * 001 -- MAC power-down - * 010 -- PHY (radio) power-down - * 011 -- Error - * 9-6: SYS_CONFIG - * Indicates current system configuration, reflecting pins on chip - * as forced high/low by device circuit board. - * 4: GOING_TO_SLEEP - * Indicates MAC is entering a power-saving sleep power-down. - * Not a good time to access device-internal resources. - * 3: MAC_ACCESS_REQ - * Host sets this to request and maintain MAC wakeup, to allow host - * access to device-internal resources. Host must wait for - * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR - * device registers. - * 2: INIT_DONE - * Host sets this to put device into fully operational D0 power mode. - * Host resets this after SW_RESET to put device into low power mode. - * 0: MAC_CLOCK_READY - * Indicates MAC (ucode processor, etc.) is powered up and can run. - * Internal resources are accessible. - * NOTE: This does not indicate that the processor is actually running. - * NOTE: This does not indicate that 4965 or 3945 has completed - * init or post-power-down restore of internal SRAM memory. - * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that - * SRAM is restored and uCode is in normal operation mode. - * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and - * do not need to save/restore it. - * NOTE: After device reset, this bit remains "0" until host sets - * INIT_DONE - */ -#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001) -#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004) -#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008) -#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010) - -#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001) - -#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000) -#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000) -#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000) - -/* EEPROM REG */ -#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) -#define CSR_EEPROM_REG_BIT_CMD (0x00000002) -#define CSR_EEPROM_REG_MSK_ADDR (0x0000FFFC) -#define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000) - -/* EEPROM GP */ -#define CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */ -#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) -#define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002) -#define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004) - -/* GP REG */ -#define CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */ -#define CSR_GP_REG_NO_POWER_SAVE (0x00000000) -#define CSR_GP_REG_MAC_POWER_SAVE (0x01000000) -#define CSR_GP_REG_PHY_POWER_SAVE (0x02000000) -#define CSR_GP_REG_POWER_SAVE_ERROR (0x03000000) - -/* CSR GIO */ -#define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002) - -/* - * UCODE-DRIVER GP (general purpose) mailbox register 1 - * Host driver and uCode write and/or read this register to communicate with - * each other. - * Bit fields: - * 4: UCODE_DISABLE - * Host sets this to request permanent halt of uCode, same as - * sending CARD_STATE command with "halt" bit set. - * 3: CT_KILL_EXIT - * Host sets this to request exit from CT_KILL state, i.e. host thinks - * device temperature is low enough to continue normal operation. - * 2: CMD_BLOCKED - * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL) - * to release uCode to clear all Tx and command queues, enter - * unassociated mode, and power down. - * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit. - * 1: SW_BIT_RFKILL - * Host sets this when issuing CARD_STATE command to request - * device sleep. - * 0: MAC_SLEEP - * uCode sets this when preparing a power-saving power-down. - * uCode resets this when power-up is complete and SRAM is sane. - * NOTE: 3945/4965 saves internal SRAM data to host when powering down, - * and must restore this data after powering back up. - * MAC_SLEEP is the best indication that restore is complete. - * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and - * do not need to save/restore it. - */ -#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001) -#define CSR_UCODE_SW_BIT_RFKILL (0x00000002) -#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) -#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008) - -/* GIO Chicken Bits (PCI Express bus link power management) */ -#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) -#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) - -/* LED */ -#define CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF) -#define CSR_LED_REG_TRUN_ON (0x78) -#define CSR_LED_REG_TRUN_OFF (0x38) - -/* ANA_PLL */ -#define CSR39_ANA_PLL_CFG_VAL (0x01000000) - -/* HPET MEM debug */ -#define CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000) - -/* DRAM INT TBL */ -#define CSR_DRAM_INT_TBL_ENABLE (1 << 31) -#define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) - -/* - * HBUS (Host-side Bus) - * - * HBUS registers are mapped directly into PCI bus space, but are used - * to indirectly access device's internal memory or registers that - * may be powered-down. - * - * Use il_wr()/il_rd() family - * for these registers; - * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ - * to make sure the MAC (uCode processor, etc.) is powered up for accessing - * internal resources. - * - * Do not use _il_wr()/_il_rd() family to access these registers; - * these provide only simple PCI bus access, without waking up the MAC. - */ -#define HBUS_BASE (0x400) - -/* - * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM - * structures, error log, event log, verifying uCode load). - * First write to address register, then read from or write to data register - * to complete the job. Once the address register is set up, accesses to - * data registers auto-increment the address by one dword. - * Bit usage for address registers (read or write): - * 0-31: memory address within device - */ -#define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c) -#define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010) -#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018) -#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c) - -/* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */ -#define HBUS_TARG_MBX_C (HBUS_BASE+0x030) -#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004) - -/* - * Registers for accessing device's internal peripheral registers - * (e.g. SCD, BSM, etc.). First write to address register, - * then read from or write to data register to complete the job. - * Bit usage for address registers (read or write): - * 0-15: register address (offset) within device - * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword) - */ -#define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044) -#define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048) -#define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c) -#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050) - -/* - * Per-Tx-queue write pointer (idx, really!) - * Indicates idx to next TFD that driver will fill (1 past latest filled). - * Bit usage: - * 0-7: queue write idx - * 11-8: queue selector - */ -#define HBUS_TARG_WRPTR (HBUS_BASE+0x060) - -#endif /* !__il_csr_h__ */ diff --git a/drivers/net/wireless/iwlegacy/debug.c b/drivers/net/wireless/iwlegacy/debug.c deleted file mode 100644 index 908b9f4fef6f..000000000000 --- a/drivers/net/wireless/iwlegacy/debug.c +++ /dev/null @@ -1,1426 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ -#include -#include -#include - -#include "common.h" - -static void -il_clear_traffic_stats(struct il_priv *il) -{ - memset(&il->tx_stats, 0, sizeof(struct traffic_stats)); - memset(&il->rx_stats, 0, sizeof(struct traffic_stats)); -} - -/* - * il_update_stats function record all the MGMT, CTRL and DATA pkt for - * both TX and Rx . Use debugfs to display the rx/rx_stats - */ -void -il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len) -{ - struct traffic_stats *stats; - - if (is_tx) - stats = &il->tx_stats; - else - stats = &il->rx_stats; - - if (ieee80211_is_mgmt(fc)) { - switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { - case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): - stats->mgmt[MANAGEMENT_ASSOC_REQ]++; - break; - case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): - stats->mgmt[MANAGEMENT_ASSOC_RESP]++; - break; - case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): - stats->mgmt[MANAGEMENT_REASSOC_REQ]++; - break; - case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): - stats->mgmt[MANAGEMENT_REASSOC_RESP]++; - break; - case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): - stats->mgmt[MANAGEMENT_PROBE_REQ]++; - break; - case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): - stats->mgmt[MANAGEMENT_PROBE_RESP]++; - break; - case cpu_to_le16(IEEE80211_STYPE_BEACON): - stats->mgmt[MANAGEMENT_BEACON]++; - break; - case cpu_to_le16(IEEE80211_STYPE_ATIM): - stats->mgmt[MANAGEMENT_ATIM]++; - break; - case cpu_to_le16(IEEE80211_STYPE_DISASSOC): - stats->mgmt[MANAGEMENT_DISASSOC]++; - break; - case cpu_to_le16(IEEE80211_STYPE_AUTH): - stats->mgmt[MANAGEMENT_AUTH]++; - break; - case cpu_to_le16(IEEE80211_STYPE_DEAUTH): - stats->mgmt[MANAGEMENT_DEAUTH]++; - break; - case cpu_to_le16(IEEE80211_STYPE_ACTION): - stats->mgmt[MANAGEMENT_ACTION]++; - break; - } - } else if (ieee80211_is_ctl(fc)) { - switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { - case cpu_to_le16(IEEE80211_STYPE_BACK_REQ): - stats->ctrl[CONTROL_BACK_REQ]++; - break; - case cpu_to_le16(IEEE80211_STYPE_BACK): - stats->ctrl[CONTROL_BACK]++; - break; - case cpu_to_le16(IEEE80211_STYPE_PSPOLL): - stats->ctrl[CONTROL_PSPOLL]++; - break; - case cpu_to_le16(IEEE80211_STYPE_RTS): - stats->ctrl[CONTROL_RTS]++; - break; - case cpu_to_le16(IEEE80211_STYPE_CTS): - stats->ctrl[CONTROL_CTS]++; - break; - case cpu_to_le16(IEEE80211_STYPE_ACK): - stats->ctrl[CONTROL_ACK]++; - break; - case cpu_to_le16(IEEE80211_STYPE_CFEND): - stats->ctrl[CONTROL_CFEND]++; - break; - case cpu_to_le16(IEEE80211_STYPE_CFENDACK): - stats->ctrl[CONTROL_CFENDACK]++; - break; - } - } else { - /* data */ - stats->data_cnt++; - stats->data_bytes += len; - } -} -EXPORT_SYMBOL(il_update_stats); - -/* create and remove of files */ -#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, il, \ - &il_dbgfs_##name##_ops)) \ - goto err; \ -} while (0) - -#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ - struct dentry *__tmp; \ - __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ - parent, ptr); \ - if (IS_ERR(__tmp) || !__tmp) \ - goto err; \ -} while (0) - -#define DEBUGFS_ADD_X32(name, parent, ptr) do { \ - struct dentry *__tmp; \ - __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \ - parent, ptr); \ - if (IS_ERR(__tmp) || !__tmp) \ - goto err; \ -} while (0) - -/* file operation */ -#define DEBUGFS_READ_FUNC(name) \ -static ssize_t il_dbgfs_##name##_read(struct file *file, \ - char __user *user_buf, \ - size_t count, loff_t *ppos); - -#define DEBUGFS_WRITE_FUNC(name) \ -static ssize_t il_dbgfs_##name##_write(struct file *file, \ - const char __user *user_buf, \ - size_t count, loff_t *ppos); - - -#define DEBUGFS_READ_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ -static const struct file_operations il_dbgfs_##name##_ops = { \ - .read = il_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define DEBUGFS_WRITE_FILE_OPS(name) \ - DEBUGFS_WRITE_FUNC(name); \ -static const struct file_operations il_dbgfs_##name##_ops = { \ - .write = il_dbgfs_##name##_write, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ - DEBUGFS_WRITE_FUNC(name); \ -static const struct file_operations il_dbgfs_##name##_ops = { \ - .write = il_dbgfs_##name##_write, \ - .read = il_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -static const char * -il_get_mgmt_string(int cmd) -{ - switch (cmd) { - IL_CMD(MANAGEMENT_ASSOC_REQ); - IL_CMD(MANAGEMENT_ASSOC_RESP); - IL_CMD(MANAGEMENT_REASSOC_REQ); - IL_CMD(MANAGEMENT_REASSOC_RESP); - IL_CMD(MANAGEMENT_PROBE_REQ); - IL_CMD(MANAGEMENT_PROBE_RESP); - IL_CMD(MANAGEMENT_BEACON); - IL_CMD(MANAGEMENT_ATIM); - IL_CMD(MANAGEMENT_DISASSOC); - IL_CMD(MANAGEMENT_AUTH); - IL_CMD(MANAGEMENT_DEAUTH); - IL_CMD(MANAGEMENT_ACTION); - default: - return "UNKNOWN"; - - } -} - -static const char * -il_get_ctrl_string(int cmd) -{ - switch (cmd) { - IL_CMD(CONTROL_BACK_REQ); - IL_CMD(CONTROL_BACK); - IL_CMD(CONTROL_PSPOLL); - IL_CMD(CONTROL_RTS); - IL_CMD(CONTROL_CTS); - IL_CMD(CONTROL_ACK); - IL_CMD(CONTROL_CFEND); - IL_CMD(CONTROL_CFENDACK); - default: - return "UNKNOWN"; - - } -} - -static ssize_t -il_dbgfs_tx_stats_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - char *buf; - int pos = 0; - - int cnt; - ssize_t ret; - const size_t bufsz = - 100 + sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX); - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); - for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { - pos += - scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", - il_get_mgmt_string(cnt), il->tx_stats.mgmt[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "Control\n"); - for (cnt = 0; cnt < CONTROL_MAX; cnt++) { - pos += - scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", - il_get_ctrl_string(cnt), il->tx_stats.ctrl[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); - pos += - scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", - il->tx_stats.data_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", - il->tx_stats.data_bytes); - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il_dbgfs_clear_traffic_stats_write(struct file *file, - const char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct il_priv *il = file->private_data; - u32 clear_flag; - char buf[8]; - int buf_size; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x", &clear_flag) != 1) - return -EFAULT; - il_clear_traffic_stats(il); - - return count; -} - -static ssize_t -il_dbgfs_rx_stats_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - char *buf; - int pos = 0; - int cnt; - ssize_t ret; - const size_t bufsz = - 100 + sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX); - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); - for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { - pos += - scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", - il_get_mgmt_string(cnt), il->rx_stats.mgmt[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "Control:\n"); - for (cnt = 0; cnt < CONTROL_MAX; cnt++) { - pos += - scnprintf(buf + pos, bufsz - pos, "\t%25s\t\t: %u\n", - il_get_ctrl_string(cnt), il->rx_stats.ctrl[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); - pos += - scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", - il->rx_stats.data_cnt); - pos += - scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", - il->rx_stats.data_bytes); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -#define BYTE1_MASK 0x000000ff; -#define BYTE2_MASK 0x0000ffff; -#define BYTE3_MASK 0x00ffffff; -static ssize_t -il_dbgfs_sram_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - u32 val; - char *buf; - ssize_t ret; - int i; - int pos = 0; - struct il_priv *il = file->private_data; - size_t bufsz; - - /* default is to dump the entire data segment */ - if (!il->dbgfs_sram_offset && !il->dbgfs_sram_len) { - il->dbgfs_sram_offset = 0x800000; - if (il->ucode_type == UCODE_INIT) - il->dbgfs_sram_len = il->ucode_init_data.len; - else - il->dbgfs_sram_len = il->ucode_data.len; - } - bufsz = 30 + il->dbgfs_sram_len * sizeof(char) * 10; - buf = kmalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - pos += - scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", - il->dbgfs_sram_len); - pos += - scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", - il->dbgfs_sram_offset); - for (i = il->dbgfs_sram_len; i > 0; i -= 4) { - val = - il_read_targ_mem(il, - il->dbgfs_sram_offset + - il->dbgfs_sram_len - i); - if (i < 4) { - switch (i) { - case 1: - val &= BYTE1_MASK; - break; - case 2: - val &= BYTE2_MASK; - break; - case 3: - val &= BYTE3_MASK; - break; - } - } - if (!(i % 16)) - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += scnprintf(buf + pos, bufsz - pos, "0x%08x ", val); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il_dbgfs_sram_write(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - char buf[64]; - int buf_size; - u32 offset, len; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (sscanf(buf, "%x,%x", &offset, &len) == 2) { - il->dbgfs_sram_offset = offset; - il->dbgfs_sram_len = len; - } else { - il->dbgfs_sram_offset = 0; - il->dbgfs_sram_len = 0; - } - - return count; -} - -static ssize_t -il_dbgfs_stations_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct il_priv *il = file->private_data; - struct il_station_entry *station; - int max_sta = il->hw_params.max_stations; - char *buf; - int i, j, pos = 0; - ssize_t ret; - /* Add 30 for initial string */ - const size_t bufsz = 30 + sizeof(char) * 500 * (il->num_stations); - - buf = kmalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += - scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n", - il->num_stations); - - for (i = 0; i < max_sta; i++) { - station = &il->stations[i]; - if (!station->used) - continue; - pos += - scnprintf(buf + pos, bufsz - pos, - "station %d - addr: %pM, flags: %#x\n", i, - station->sta.sta.addr, - station->sta.station_flags_msk); - pos += - scnprintf(buf + pos, bufsz - pos, - "TID\tseq_num\ttxq_id\tframes\ttfds\t"); - pos += - scnprintf(buf + pos, bufsz - pos, - "start_idx\tbitmap\t\t\trate_n_flags\n"); - - for (j = 0; j < MAX_TID_COUNT; j++) { - pos += - scnprintf(buf + pos, bufsz - pos, - "%d:\t%#x\t%#x\t%u\t%u\t%u\t\t%#.16llx\t%#x", - j, station->tid[j].seq_number, - station->tid[j].agg.txq_id, - station->tid[j].agg.frame_count, - station->tid[j].tfds_in_queue, - station->tid[j].agg.start_idx, - station->tid[j].agg.bitmap, - station->tid[j].agg.rate_n_flags); - - if (station->tid[j].agg.wait_for_ba) - pos += - scnprintf(buf + pos, bufsz - pos, - " - waitforba"); - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - } - - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il_dbgfs_nvm_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - ssize_t ret; - struct il_priv *il = file->private_data; - int pos = 0, ofs = 0, buf_size = 0; - const u8 *ptr; - char *buf; - u16 eeprom_ver; - size_t eeprom_len = il->cfg->eeprom_size; - buf_size = 4 * eeprom_len + 256; - - if (eeprom_len % 16) { - IL_ERR("NVM size is not multiple of 16.\n"); - return -ENODATA; - } - - ptr = il->eeprom; - if (!ptr) { - IL_ERR("Invalid EEPROM memory\n"); - return -ENOMEM; - } - - /* 4 characters for byte 0xYY */ - buf = kzalloc(buf_size, GFP_KERNEL); - if (!buf) { - IL_ERR("Can not allocate Buffer\n"); - return -ENOMEM; - } - eeprom_ver = il_eeprom_query16(il, EEPROM_VERSION); - pos += - scnprintf(buf + pos, buf_size - pos, "EEPROM " "version: 0x%x\n", - eeprom_ver); - for (ofs = 0; ofs < eeprom_len; ofs += 16) { - pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x %16ph\n", - ofs, ptr + ofs); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il_dbgfs_channels_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct il_priv *il = file->private_data; - struct ieee80211_channel *channels = NULL; - const struct ieee80211_supported_band *supp_band = NULL; - int pos = 0, i, bufsz = PAGE_SIZE; - char *buf; - ssize_t ret; - - if (!test_bit(S_GEO_CONFIGURED, &il->status)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - IL_ERR("Can not allocate Buffer\n"); - return -ENOMEM; - } - - supp_band = il_get_hw_mode(il, IEEE80211_BAND_2GHZ); - if (supp_band) { - channels = supp_band->channels; - - pos += - scnprintf(buf + pos, bufsz - pos, - "Displaying %d channels in 2.4GHz band 802.11bg):\n", - supp_band->n_channels); - - for (i = 0; i < supp_band->n_channels; i++) - pos += - scnprintf(buf + pos, bufsz - pos, - "%d: %ddBm: BSS%s%s, %s.\n", - channels[i].hw_value, - channels[i].max_power, - channels[i]. - flags & IEEE80211_CHAN_RADAR ? - " (IEEE 802.11h required)" : "", - ((channels[i]. - flags & IEEE80211_CHAN_NO_IR) || - (channels[i]. - flags & IEEE80211_CHAN_RADAR)) ? "" : - ", IBSS", - channels[i]. - flags & IEEE80211_CHAN_NO_IR ? - "passive only" : "active/passive"); - } - supp_band = il_get_hw_mode(il, IEEE80211_BAND_5GHZ); - if (supp_band) { - channels = supp_band->channels; - - pos += - scnprintf(buf + pos, bufsz - pos, - "Displaying %d channels in 5.2GHz band (802.11a)\n", - supp_band->n_channels); - - for (i = 0; i < supp_band->n_channels; i++) - pos += - scnprintf(buf + pos, bufsz - pos, - "%d: %ddBm: BSS%s%s, %s.\n", - channels[i].hw_value, - channels[i].max_power, - channels[i]. - flags & IEEE80211_CHAN_RADAR ? - " (IEEE 802.11h required)" : "", - ((channels[i]. - flags & IEEE80211_CHAN_NO_IR) || - (channels[i]. - flags & IEEE80211_CHAN_RADAR)) ? "" : - ", IBSS", - channels[i]. - flags & IEEE80211_CHAN_NO_IR ? - "passive only" : "active/passive"); - } - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il_dbgfs_status_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - char buf[512]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += - scnprintf(buf + pos, bufsz - pos, "S_HCMD_ACTIVE:\t %d\n", - test_bit(S_HCMD_ACTIVE, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_INT_ENABLED:\t %d\n", - test_bit(S_INT_ENABLED, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_RFKILL:\t %d\n", - test_bit(S_RFKILL, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_CT_KILL:\t\t %d\n", - test_bit(S_CT_KILL, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_INIT:\t\t %d\n", - test_bit(S_INIT, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_ALIVE:\t\t %d\n", - test_bit(S_ALIVE, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_READY:\t\t %d\n", - test_bit(S_READY, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_TEMPERATURE:\t %d\n", - test_bit(S_TEMPERATURE, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_GEO_CONFIGURED:\t %d\n", - test_bit(S_GEO_CONFIGURED, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_EXIT_PENDING:\t %d\n", - test_bit(S_EXIT_PENDING, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_STATS:\t %d\n", - test_bit(S_STATS, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_SCANNING:\t %d\n", - test_bit(S_SCANNING, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_SCAN_ABORTING:\t %d\n", - test_bit(S_SCAN_ABORTING, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_SCAN_HW:\t\t %d\n", - test_bit(S_SCAN_HW, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_POWER_PMI:\t %d\n", - test_bit(S_POWER_PMI, &il->status)); - pos += - scnprintf(buf + pos, bufsz - pos, "S_FW_ERROR:\t %d\n", - test_bit(S_FW_ERROR, &il->status)); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -il_dbgfs_interrupt_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - int pos = 0; - int cnt = 0; - char *buf; - int bufsz = 24 * 64; /* 24 items * 64 char per item */ - ssize_t ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - IL_ERR("Can not allocate Buffer\n"); - return -ENOMEM; - } - - pos += - scnprintf(buf + pos, bufsz - pos, "Interrupt Statistics Report:\n"); - - pos += - scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", - il->isr_stats.hw); - pos += - scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", - il->isr_stats.sw); - if (il->isr_stats.sw || il->isr_stats.hw) { - pos += - scnprintf(buf + pos, bufsz - pos, - "\tLast Restarting Code: 0x%X\n", - il->isr_stats.err_code); - } -#ifdef CONFIG_IWLEGACY_DEBUG - pos += - scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", - il->isr_stats.sch); - pos += - scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", - il->isr_stats.alive); -#endif - pos += - scnprintf(buf + pos, bufsz - pos, - "HW RF KILL switch toggled:\t %u\n", - il->isr_stats.rfkill); - - pos += - scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", - il->isr_stats.ctkill); - - pos += - scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", - il->isr_stats.wakeup); - - pos += - scnprintf(buf + pos, bufsz - pos, "Rx command responses:\t\t %u\n", - il->isr_stats.rx); - for (cnt = 0; cnt < IL_CN_MAX; cnt++) { - if (il->isr_stats.handlers[cnt] > 0) - pos += - scnprintf(buf + pos, bufsz - pos, - "\tRx handler[%36s]:\t\t %u\n", - il_get_cmd_string(cnt), - il->isr_stats.handlers[cnt]); - } - - pos += - scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", - il->isr_stats.tx); - - pos += - scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", - il->isr_stats.unhandled); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il_dbgfs_interrupt_write(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - char buf[8]; - int buf_size; - u32 reset_flag; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x", &reset_flag) != 1) - return -EFAULT; - if (reset_flag == 0) - il_clear_isr_stats(il); - - return count; -} - -static ssize_t -il_dbgfs_qos_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct il_priv *il = file->private_data; - int pos = 0, i; - char buf[256]; - const size_t bufsz = sizeof(buf); - - for (i = 0; i < AC_NUM; i++) { - pos += - scnprintf(buf + pos, bufsz - pos, - "\tcw_min\tcw_max\taifsn\ttxop\n"); - pos += - scnprintf(buf + pos, bufsz - pos, - "AC[%d]\t%u\t%u\t%u\t%u\n", i, - il->qos_data.def_qos_parm.ac[i].cw_min, - il->qos_data.def_qos_parm.ac[i].cw_max, - il->qos_data.def_qos_parm.ac[i].aifsn, - il->qos_data.def_qos_parm.ac[i].edca_txop); - } - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -il_dbgfs_disable_ht40_write(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - char buf[8]; - int buf_size; - int ht40; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &ht40) != 1) - return -EFAULT; - if (!il_is_any_associated(il)) - il->disable_ht40 = ht40 ? true : false; - else { - IL_ERR("Sta associated with AP - " - "Change to 40MHz channel support is not allowed\n"); - return -EINVAL; - } - - return count; -} - -static ssize_t -il_dbgfs_disable_ht40_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - char buf[100]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += - scnprintf(buf + pos, bufsz - pos, "11n 40MHz Mode: %s\n", - il->disable_ht40 ? "Disabled" : "Enabled"); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -DEBUGFS_READ_WRITE_FILE_OPS(sram); -DEBUGFS_READ_FILE_OPS(nvm); -DEBUGFS_READ_FILE_OPS(stations); -DEBUGFS_READ_FILE_OPS(channels); -DEBUGFS_READ_FILE_OPS(status); -DEBUGFS_READ_WRITE_FILE_OPS(interrupt); -DEBUGFS_READ_FILE_OPS(qos); -DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); - -static ssize_t -il_dbgfs_tx_queue_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - struct il_tx_queue *txq; - struct il_queue *q; - char *buf; - int pos = 0; - int cnt; - int ret; - const size_t bufsz = - sizeof(char) * 64 * il->cfg->num_of_queues; - - if (!il->txq) { - IL_ERR("txq not ready\n"); - return -EAGAIN; - } - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (cnt = 0; cnt < il->hw_params.max_txq_num; cnt++) { - txq = &il->txq[cnt]; - q = &txq->q; - pos += - scnprintf(buf + pos, bufsz - pos, - "hwq %.2d: read=%u write=%u stop=%d" - " swq_id=%#.2x (ac %d/hwq %d)\n", cnt, - q->read_ptr, q->write_ptr, - !!test_bit(cnt, il->queue_stopped), - txq->swq_id, txq->swq_id & 3, - (txq->swq_id >> 2) & 0x1f); - if (cnt >= 4) - continue; - /* for the ACs, display the stop count too */ - pos += - scnprintf(buf + pos, bufsz - pos, - " stop-count: %d\n", - atomic_read(&il->queue_stop_count[cnt])); - } - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il_dbgfs_rx_queue_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - struct il_rx_queue *rxq = &il->rxq; - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", rxq->read); - pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", rxq->write); - pos += - scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", - rxq->free_count); - if (rxq->rb_stts) { - pos += - scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", - le16_to_cpu(rxq->rb_stts-> - closed_rb_num) & 0x0FFF); - } else { - pos += - scnprintf(buf + pos, bufsz - pos, - "closed_rb_num: Not Allocated\n"); - } - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -il_dbgfs_ucode_rx_stats_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - - return il->debugfs_ops->rx_stats_read(file, user_buf, count, ppos); -} - -static ssize_t -il_dbgfs_ucode_tx_stats_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - - return il->debugfs_ops->tx_stats_read(file, user_buf, count, ppos); -} - -static ssize_t -il_dbgfs_ucode_general_stats_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - - return il->debugfs_ops->general_stats_read(file, user_buf, count, ppos); -} - -static ssize_t -il_dbgfs_sensitivity_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - int pos = 0; - int cnt = 0; - char *buf; - int bufsz = sizeof(struct il_sensitivity_data) * 4 + 100; - ssize_t ret; - struct il_sensitivity_data *data; - - data = &il->sensitivity_data; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - IL_ERR("Can not allocate Buffer\n"); - return -ENOMEM; - } - - pos += - scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n", - data->auto_corr_ofdm); - pos += - scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_mrc:\t\t %u\n", - data->auto_corr_ofdm_mrc); - pos += - scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n", - data->auto_corr_ofdm_x1); - pos += - scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_mrc_x1:\t\t %u\n", - data->auto_corr_ofdm_mrc_x1); - pos += - scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n", - data->auto_corr_cck); - pos += - scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n", - data->auto_corr_cck_mrc); - pos += - scnprintf(buf + pos, bufsz - pos, - "last_bad_plcp_cnt_ofdm:\t\t %u\n", - data->last_bad_plcp_cnt_ofdm); - pos += - scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n", - data->last_fa_cnt_ofdm); - pos += - scnprintf(buf + pos, bufsz - pos, "last_bad_plcp_cnt_cck:\t\t %u\n", - data->last_bad_plcp_cnt_cck); - pos += - scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n", - data->last_fa_cnt_cck); - pos += - scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n", - data->nrg_curr_state); - pos += - scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n", - data->nrg_prev_state); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t"); - for (cnt = 0; cnt < 10; cnt++) { - pos += - scnprintf(buf + pos, bufsz - pos, " %u", - data->nrg_value[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t"); - for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) { - pos += - scnprintf(buf + pos, bufsz - pos, " %u", - data->nrg_silence_rssi[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += - scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n", - data->nrg_silence_ref); - pos += - scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n", - data->nrg_energy_idx); - pos += - scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n", - data->nrg_silence_idx); - pos += - scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n", - data->nrg_th_cck); - pos += - scnprintf(buf + pos, bufsz - pos, - "nrg_auto_corr_silence_diff:\t %u\n", - data->nrg_auto_corr_silence_diff); - pos += - scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n", - data->num_in_cck_no_fa); - pos += - scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n", - data->nrg_th_ofdm); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il_dbgfs_chain_noise_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - int pos = 0; - int cnt = 0; - char *buf; - int bufsz = sizeof(struct il_chain_noise_data) * 4 + 100; - ssize_t ret; - struct il_chain_noise_data *data; - - data = &il->chain_noise_data; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - IL_ERR("Can not allocate Buffer\n"); - return -ENOMEM; - } - - pos += - scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n", - data->active_chains); - pos += - scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n", - data->chain_noise_a); - pos += - scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n", - data->chain_noise_b); - pos += - scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n", - data->chain_noise_c); - pos += - scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n", - data->chain_signal_a); - pos += - scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n", - data->chain_signal_b); - pos += - scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n", - data->chain_signal_c); - pos += - scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n", - data->beacon_count); - - pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t"); - for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { - pos += - scnprintf(buf + pos, bufsz - pos, " %u", - data->disconn_array[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t"); - for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { - pos += - scnprintf(buf + pos, bufsz - pos, " %u", - data->delta_gain_code[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += - scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n", - data->radio_write); - pos += - scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n", - data->state); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t -il_dbgfs_power_save_status_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - char buf[60]; - int pos = 0; - const size_t bufsz = sizeof(buf); - u32 pwrsave_status; - - pwrsave_status = - _il_rd(il, CSR_GP_CNTRL) & CSR_GP_REG_POWER_SAVE_STATUS_MSK; - - pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: "); - pos += - scnprintf(buf + pos, bufsz - pos, "%s\n", - (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" : - (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" : - (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" : - "error"); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -il_dbgfs_clear_ucode_stats_write(struct file *file, - const char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct il_priv *il = file->private_data; - char buf[8]; - int buf_size; - int clear; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &clear) != 1) - return -EFAULT; - - /* make request to uCode to retrieve stats information */ - mutex_lock(&il->mutex); - il_send_stats_request(il, CMD_SYNC, true); - mutex_unlock(&il->mutex); - - return count; -} - -static ssize_t -il_dbgfs_rxon_flags_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - int len = 0; - char buf[20]; - - len = sprintf(buf, "0x%04X\n", le32_to_cpu(il->active.flags)); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t -il_dbgfs_rxon_filter_flags_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - int len = 0; - char buf[20]; - - len = - sprintf(buf, "0x%04X\n", le32_to_cpu(il->active.filter_flags)); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t -il_dbgfs_fh_reg_read(struct file *file, char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct il_priv *il = file->private_data; - char *buf; - int pos = 0; - ssize_t ret = -EFAULT; - - if (il->ops->dump_fh) { - ret = pos = il->ops->dump_fh(il, &buf, true); - if (buf) { - ret = - simple_read_from_buffer(user_buf, count, ppos, buf, - pos); - kfree(buf); - } - } - - return ret; -} - -static ssize_t -il_dbgfs_missed_beacon_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - int pos = 0; - char buf[12]; - const size_t bufsz = sizeof(buf); - - pos += - scnprintf(buf + pos, bufsz - pos, "%d\n", - il->missed_beacon_threshold); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -il_dbgfs_missed_beacon_write(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct il_priv *il = file->private_data; - char buf[8]; - int buf_size; - int missed; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &missed) != 1) - return -EINVAL; - - if (missed < IL_MISSED_BEACON_THRESHOLD_MIN || - missed > IL_MISSED_BEACON_THRESHOLD_MAX) - il->missed_beacon_threshold = IL_MISSED_BEACON_THRESHOLD_DEF; - else - il->missed_beacon_threshold = missed; - - return count; -} - -static ssize_t -il_dbgfs_force_reset_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - int pos = 0; - char buf[300]; - const size_t bufsz = sizeof(buf); - struct il_force_reset *force_reset; - - force_reset = &il->force_reset; - - pos += - scnprintf(buf + pos, bufsz - pos, "\tnumber of reset request: %d\n", - force_reset->reset_request_count); - pos += - scnprintf(buf + pos, bufsz - pos, - "\tnumber of reset request success: %d\n", - force_reset->reset_success_count); - pos += - scnprintf(buf + pos, bufsz - pos, - "\tnumber of reset request reject: %d\n", - force_reset->reset_reject_count); - pos += - scnprintf(buf + pos, bufsz - pos, "\treset duration: %lu\n", - force_reset->reset_duration); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -il_dbgfs_force_reset_write(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) -{ - - int ret; - struct il_priv *il = file->private_data; - - ret = il_force_reset(il, true); - - return ret ? ret : count; -} - -static ssize_t -il_dbgfs_wd_timeout_write(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) -{ - - struct il_priv *il = file->private_data; - char buf[8]; - int buf_size; - int timeout; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &timeout) != 1) - return -EINVAL; - if (timeout < 0 || timeout > IL_MAX_WD_TIMEOUT) - timeout = IL_DEF_WD_TIMEOUT; - - il->cfg->wd_timeout = timeout; - il_setup_watchdog(il); - return count; -} - -DEBUGFS_READ_FILE_OPS(rx_stats); -DEBUGFS_READ_FILE_OPS(tx_stats); -DEBUGFS_READ_FILE_OPS(rx_queue); -DEBUGFS_READ_FILE_OPS(tx_queue); -DEBUGFS_READ_FILE_OPS(ucode_rx_stats); -DEBUGFS_READ_FILE_OPS(ucode_tx_stats); -DEBUGFS_READ_FILE_OPS(ucode_general_stats); -DEBUGFS_READ_FILE_OPS(sensitivity); -DEBUGFS_READ_FILE_OPS(chain_noise); -DEBUGFS_READ_FILE_OPS(power_save_status); -DEBUGFS_WRITE_FILE_OPS(clear_ucode_stats); -DEBUGFS_WRITE_FILE_OPS(clear_traffic_stats); -DEBUGFS_READ_FILE_OPS(fh_reg); -DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon); -DEBUGFS_READ_WRITE_FILE_OPS(force_reset); -DEBUGFS_READ_FILE_OPS(rxon_flags); -DEBUGFS_READ_FILE_OPS(rxon_filter_flags); -DEBUGFS_WRITE_FILE_OPS(wd_timeout); - -/* - * Create the debugfs files and directories - * - */ -int -il_dbgfs_register(struct il_priv *il, const char *name) -{ - struct dentry *phyd = il->hw->wiphy->debugfsdir; - struct dentry *dir_drv, *dir_data, *dir_rf, *dir_debug; - - dir_drv = debugfs_create_dir(name, phyd); - if (!dir_drv) - return -ENOMEM; - - il->debugfs_dir = dir_drv; - - dir_data = debugfs_create_dir("data", dir_drv); - if (!dir_data) - goto err; - dir_rf = debugfs_create_dir("rf", dir_drv); - if (!dir_rf) - goto err; - dir_debug = debugfs_create_dir("debug", dir_drv); - if (!dir_debug) - goto err; - - DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(interrupt, dir_data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(rx_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(tx_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(rx_queue, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(tx_queue, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(clear_ucode_stats, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(clear_traffic_stats, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(fh_reg, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(force_reset, dir_debug, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); - - if (il->cfg->sensitivity_calib_by_driver) - DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); - if (il->cfg->chain_noise_calib_by_driver) - DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(wd_timeout, dir_debug, S_IWUSR); - if (il->cfg->sensitivity_calib_by_driver) - DEBUGFS_ADD_BOOL(disable_sensitivity, dir_rf, - &il->disable_sens_cal); - if (il->cfg->chain_noise_calib_by_driver) - DEBUGFS_ADD_BOOL(disable_chain_noise, dir_rf, - &il->disable_chain_noise_cal); - DEBUGFS_ADD_BOOL(disable_tx_power, dir_rf, &il->disable_tx_power_cal); - return 0; - -err: - IL_ERR("Can't create the debugfs directory\n"); - il_dbgfs_unregister(il); - return -ENOMEM; -} -EXPORT_SYMBOL(il_dbgfs_register); - -/** - * Remove the debugfs files and directories - * - */ -void -il_dbgfs_unregister(struct il_priv *il) -{ - if (!il->debugfs_dir) - return; - - debugfs_remove_recursive(il->debugfs_dir); - il->debugfs_dir = NULL; -} -EXPORT_SYMBOL(il_dbgfs_unregister); diff --git a/drivers/net/wireless/iwlegacy/iwl-spectrum.h b/drivers/net/wireless/iwlegacy/iwl-spectrum.h deleted file mode 100644 index 85fe48e520f9..000000000000 --- a/drivers/net/wireless/iwlegacy/iwl-spectrum.h +++ /dev/null @@ -1,92 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __il_spectrum_h__ -#define __il_spectrum_h__ -enum { /* ieee80211_basic_report.map */ - IEEE80211_BASIC_MAP_BSS = (1 << 0), - IEEE80211_BASIC_MAP_OFDM = (1 << 1), - IEEE80211_BASIC_MAP_UNIDENTIFIED = (1 << 2), - IEEE80211_BASIC_MAP_RADAR = (1 << 3), - IEEE80211_BASIC_MAP_UNMEASURED = (1 << 4), - /* Bits 5-7 are reserved */ - -}; -struct ieee80211_basic_report { - u8 channel; - __le64 start_time; - __le16 duration; - u8 map; -} __packed; - -enum { /* ieee80211_measurement_request.mode */ - /* Bit 0 is reserved */ - IEEE80211_MEASUREMENT_ENABLE = (1 << 1), - IEEE80211_MEASUREMENT_REQUEST = (1 << 2), - IEEE80211_MEASUREMENT_REPORT = (1 << 3), - /* Bits 4-7 are reserved */ -}; - -enum { - IEEE80211_REPORT_BASIC = 0, /* required */ - IEEE80211_REPORT_CCA = 1, /* optional */ - IEEE80211_REPORT_RPI = 2, /* optional */ - /* 3-255 reserved */ -}; - -struct ieee80211_measurement_params { - u8 channel; - __le64 start_time; - __le16 duration; -} __packed; - -struct ieee80211_info_element { - u8 id; - u8 len; - u8 data[0]; -} __packed; - -struct ieee80211_measurement_request { - struct ieee80211_info_element ie; - u8 token; - u8 mode; - u8 type; - struct ieee80211_measurement_params params[0]; -} __packed; - -struct ieee80211_measurement_report { - struct ieee80211_info_element ie; - u8 token; - u8 mode; - u8 type; - union { - struct ieee80211_basic_report basic[0]; - } u; -} __packed; - -#endif diff --git a/drivers/net/wireless/iwlegacy/prph.h b/drivers/net/wireless/iwlegacy/prph.h deleted file mode 100644 index ffec4b4a248a..000000000000 --- a/drivers/net/wireless/iwlegacy/prph.h +++ /dev/null @@ -1,522 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __il_prph_h__ -#define __il_prph_h__ - -/* - * Registers in this file are internal, not PCI bus memory mapped. - * Driver accesses these via HBUS_TARG_PRPH_* registers. - */ -#define PRPH_BASE (0x00000) -#define PRPH_END (0xFFFFF) - -/* APMG (power management) constants */ -#define APMG_BASE (PRPH_BASE + 0x3000) -#define APMG_CLK_CTRL_REG (APMG_BASE + 0x0000) -#define APMG_CLK_EN_REG (APMG_BASE + 0x0004) -#define APMG_CLK_DIS_REG (APMG_BASE + 0x0008) -#define APMG_PS_CTRL_REG (APMG_BASE + 0x000c) -#define APMG_PCIDEV_STT_REG (APMG_BASE + 0x0010) -#define APMG_RFKILL_REG (APMG_BASE + 0x0014) -#define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c) -#define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020) -#define APMG_DIGITAL_SVR_REG (APMG_BASE + 0x0058) -#define APMG_ANALOG_SVR_REG (APMG_BASE + 0x006C) - -#define APMS_CLK_VAL_MRB_FUNC_MODE (0x00000001) -#define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) -#define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) - -#define APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS (0x00400000) -#define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000) -#define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000) -#define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) -#define APMG_PS_CTRL_VAL_PWR_SRC_MAX (0x01000000) /* 3945 only */ -#define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000) -#define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */ -#define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060) - -#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) - -/** - * BSM (Bootstrap State Machine) - * - * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program - * in special SRAM that does not power down when the embedded control - * processor is sleeping (e.g. for periodic power-saving shutdowns of radio). - * - * When powering back up after sleeps (or during initial uCode load), the BSM - * internally loads the short bootstrap program from the special SRAM into the - * embedded processor's instruction SRAM, and starts the processor so it runs - * the bootstrap program. - * - * This bootstrap program loads (via PCI busmaster DMA) instructions and data - * images for a uCode program from host DRAM locations. The host driver - * indicates DRAM locations and sizes for instruction and data images via the - * four BSM_DRAM_* registers. Once the bootstrap program loads the new program, - * the new program starts automatically. - * - * The uCode used for open-source drivers includes two programs: - * - * 1) Initialization -- performs hardware calibration and sets up some - * internal data, then notifies host via "initialize alive" notification - * (struct il_init_alive_resp) that it has completed all of its work. - * After signal from host, it then loads and starts the runtime program. - * The initialization program must be used when initially setting up the - * NIC after loading the driver. - * - * 2) Runtime/Protocol -- performs all normal runtime operations. This - * notifies host via "alive" notification (struct il_alive_resp) that it - * is ready to be used. - * - * When initializing the NIC, the host driver does the following procedure: - * - * 1) Load bootstrap program (instructions only, no data image for bootstrap) - * into bootstrap memory. Use dword writes starting at BSM_SRAM_LOWER_BOUND - * - * 2) Point (via BSM_DRAM_*) to the "initialize" uCode data and instruction - * images in host DRAM. - * - * 3) Set up BSM to copy from BSM SRAM into uCode instruction SRAM when asked: - * BSM_WR_MEM_SRC_REG = 0 - * BSM_WR_MEM_DST_REG = RTC_INST_LOWER_BOUND - * BSM_WR_MEM_DWCOUNT_REG = # dwords in bootstrap instruction image - * - * 4) Load bootstrap into instruction SRAM: - * BSM_WR_CTRL_REG = BSM_WR_CTRL_REG_BIT_START - * - * 5) Wait for load completion: - * Poll BSM_WR_CTRL_REG for BSM_WR_CTRL_REG_BIT_START = 0 - * - * 6) Enable future boot loads whenever NIC's power management triggers it: - * BSM_WR_CTRL_REG = BSM_WR_CTRL_REG_BIT_START_EN - * - * 7) Start the NIC by removing all reset bits: - * CSR_RESET = 0 - * - * The bootstrap uCode (already in instruction SRAM) loads initialization - * uCode. Initialization uCode performs data initialization, sends - * "initialize alive" notification to host, and waits for a signal from - * host to load runtime code. - * - * 4) Point (via BSM_DRAM_*) to the "runtime" uCode data and instruction - * images in host DRAM. The last register loaded must be the instruction - * byte count register ("1" in MSbit tells initialization uCode to load - * the runtime uCode): - * BSM_DRAM_INST_BYTECOUNT_REG = byte count | BSM_DRAM_INST_LOAD - * - * 5) Wait for "alive" notification, then issue normal runtime commands. - * - * Data caching during power-downs: - * - * Just before the embedded controller powers down (e.g for automatic - * power-saving modes, or for RFKILL), uCode stores (via PCI busmaster DMA) - * a current snapshot of the embedded processor's data SRAM into host DRAM. - * This caches the data while the embedded processor's memory is powered down. - * Location and size are controlled by BSM_DRAM_DATA_* registers. - * - * NOTE: Instruction SRAM does not need to be saved, since that doesn't - * change during operation; the original image (from uCode distribution - * file) can be used for reload. - * - * When powering back up, the BSM loads the bootstrap program. Bootstrap looks - * at the BSM_DRAM_* registers, which now point to the runtime instruction - * image and the cached (modified) runtime data (*not* the initialization - * uCode). Bootstrap reloads these runtime images into SRAM, and restarts the - * uCode from where it left off before the power-down. - * - * NOTE: Initialization uCode does *not* run as part of the save/restore - * procedure. - * - * This save/restore method is mostly for autonomous power management during - * normal operation (result of C_POWER_TBL). Platform suspend/resume and - * RFKILL should use complete restarts (with total re-initialization) of uCode, - * allowing total shutdown (including BSM memory). - * - * Note that, during normal operation, the host DRAM that held the initial - * startup data for the runtime code is now being used as a backup data cache - * for modified data! If you need to completely re-initialize the NIC, make - * sure that you use the runtime data image from the uCode distribution file, - * not the modified/saved runtime data. You may want to store a separate - * "clean" runtime data image in DRAM to avoid disk reads of distribution file. - */ - -/* BSM bit fields */ -#define BSM_WR_CTRL_REG_BIT_START (0x80000000) /* start boot load now */ -#define BSM_WR_CTRL_REG_BIT_START_EN (0x40000000) /* enable boot after pwrup */ -#define BSM_DRAM_INST_LOAD (0x80000000) /* start program load now */ - -/* BSM addresses */ -#define BSM_BASE (PRPH_BASE + 0x3400) -#define BSM_END (PRPH_BASE + 0x3800) - -#define BSM_WR_CTRL_REG (BSM_BASE + 0x000) /* ctl and status */ -#define BSM_WR_MEM_SRC_REG (BSM_BASE + 0x004) /* source in BSM mem */ -#define BSM_WR_MEM_DST_REG (BSM_BASE + 0x008) /* dest in SRAM mem */ -#define BSM_WR_DWCOUNT_REG (BSM_BASE + 0x00C) /* bytes */ -#define BSM_WR_STATUS_REG (BSM_BASE + 0x010) /* bit 0: 1 == done */ - -/* - * Pointers and size regs for bootstrap load and data SRAM save/restore. - * NOTE: 3945 pointers use bits 31:0 of DRAM address. - * 4965 pointers use bits 35:4 of DRAM address. - */ -#define BSM_DRAM_INST_PTR_REG (BSM_BASE + 0x090) -#define BSM_DRAM_INST_BYTECOUNT_REG (BSM_BASE + 0x094) -#define BSM_DRAM_DATA_PTR_REG (BSM_BASE + 0x098) -#define BSM_DRAM_DATA_BYTECOUNT_REG (BSM_BASE + 0x09C) - -/* - * BSM special memory, stays powered on during power-save sleeps. - * Read/write, address range from LOWER_BOUND to (LOWER_BOUND + SIZE -1) - */ -#define BSM_SRAM_LOWER_BOUND (PRPH_BASE + 0x3800) -#define BSM_SRAM_SIZE (1024) /* bytes */ - -/* 3945 Tx scheduler registers */ -#define ALM_SCD_BASE (PRPH_BASE + 0x2E00) -#define ALM_SCD_MODE_REG (ALM_SCD_BASE + 0x000) -#define ALM_SCD_ARASTAT_REG (ALM_SCD_BASE + 0x004) -#define ALM_SCD_TXFACT_REG (ALM_SCD_BASE + 0x010) -#define ALM_SCD_TXF4MF_REG (ALM_SCD_BASE + 0x014) -#define ALM_SCD_TXF5MF_REG (ALM_SCD_BASE + 0x020) -#define ALM_SCD_SBYP_MODE_1_REG (ALM_SCD_BASE + 0x02C) -#define ALM_SCD_SBYP_MODE_2_REG (ALM_SCD_BASE + 0x030) - -/** - * Tx Scheduler - * - * The Tx Scheduler selects the next frame to be transmitted, choosing TFDs - * (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in - * host DRAM. It steers each frame's Tx command (which contains the frame - * data) into one of up to 7 prioritized Tx DMA FIFO channels within the - * device. A queue maps to only one (selectable by driver) Tx DMA channel, - * but one DMA channel may take input from several queues. - * - * Tx DMA FIFOs have dedicated purposes. For 4965, they are used as follows - * (cf. default_queue_to_tx_fifo in 4965.c): - * - * 0 -- EDCA BK (background) frames, lowest priority - * 1 -- EDCA BE (best effort) frames, normal priority - * 2 -- EDCA VI (video) frames, higher priority - * 3 -- EDCA VO (voice) and management frames, highest priority - * 4 -- Commands (e.g. RXON, etc.) - * 5 -- unused (HCCA) - * 6 -- unused (HCCA) - * 7 -- not used by driver (device-internal only) - * - * - * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6. - * In addition, driver can map the remaining queues to Tx DMA/FIFO - * channels 0-3 to support 11n aggregation via EDCA DMA channels. - * - * The driver sets up each queue to work in one of two modes: - * - * 1) Scheduler-Ack, in which the scheduler automatically supports a - * block-ack (BA) win of up to 64 TFDs. In this mode, each queue - * contains TFDs for a unique combination of Recipient Address (RA) - * and Traffic Identifier (TID), that is, traffic of a given - * Quality-Of-Service (QOS) priority, destined for a single station. - * - * In scheduler-ack mode, the scheduler keeps track of the Tx status of - * each frame within the BA win, including whether it's been transmitted, - * and whether it's been acknowledged by the receiving station. The device - * automatically processes block-acks received from the receiving STA, - * and reschedules un-acked frames to be retransmitted (successful - * Tx completion may end up being out-of-order). - * - * The driver must maintain the queue's Byte Count table in host DRAM - * (struct il4965_sched_queue_byte_cnt_tbl) for this mode. - * This mode does not support fragmentation. - * - * 2) FIFO (a.k.a. non-Scheduler-ACK), in which each TFD is processed in order. - * The device may automatically retry Tx, but will retry only one frame - * at a time, until receiving ACK from receiving station, or reaching - * retry limit and giving up. - * - * The command queue (#4/#9) must use this mode! - * This mode does not require use of the Byte Count table in host DRAM. - * - * Driver controls scheduler operation via 3 means: - * 1) Scheduler registers - * 2) Shared scheduler data base in internal 4956 SRAM - * 3) Shared data in host DRAM - * - * Initialization: - * - * When loading, driver should allocate memory for: - * 1) 16 TFD circular buffers, each with space for (typically) 256 TFDs. - * 2) 16 Byte Count circular buffers in 16 KBytes contiguous memory - * (1024 bytes for each queue). - * - * After receiving "Alive" response from uCode, driver must initialize - * the scheduler (especially for queue #4/#9, the command queue, otherwise - * the driver can't issue commands!): - */ - -/** - * Max Tx win size is the max number of contiguous TFDs that the scheduler - * can keep track of at one time when creating block-ack chains of frames. - * Note that "64" matches the number of ack bits in a block-ack packet. - * Driver should use SCD_WIN_SIZE and SCD_FRAME_LIMIT values to initialize - * IL49_SCD_CONTEXT_QUEUE_OFFSET(x) values. - */ -#define SCD_WIN_SIZE 64 -#define SCD_FRAME_LIMIT 64 - -/* SCD registers are internal, must be accessed via HBUS_TARG_PRPH regs */ -#define IL49_SCD_START_OFFSET 0xa02c00 - -/* - * 4965 tells driver SRAM address for internal scheduler structs via this reg. - * Value is valid only after "Alive" response from uCode. - */ -#define IL49_SCD_SRAM_BASE_ADDR (IL49_SCD_START_OFFSET + 0x0) - -/* - * Driver may need to update queue-empty bits after changing queue's - * write and read pointers (idxes) during (re-)initialization (i.e. when - * scheduler is not tracking what's happening). - * Bit fields: - * 31-16: Write mask -- 1: update empty bit, 0: don't change empty bit - * 15-00: Empty state, one for each queue -- 1: empty, 0: non-empty - * NOTE: This register is not used by Linux driver. - */ -#define IL49_SCD_EMPTY_BITS (IL49_SCD_START_OFFSET + 0x4) - -/* - * Physical base address of array of byte count (BC) circular buffers (CBs). - * Each Tx queue has a BC CB in host DRAM to support Scheduler-ACK mode. - * This register points to BC CB for queue 0, must be on 1024-byte boundary. - * Others are spaced by 1024 bytes. - * Each BC CB is 2 bytes * (256 + 64) = 740 bytes, followed by 384 bytes pad. - * (Index into a queue's BC CB) = (idx into queue's TFD CB) = (SSN & 0xff). - * Bit fields: - * 25-00: Byte Count CB physical address [35:10], must be 1024-byte aligned. - */ -#define IL49_SCD_DRAM_BASE_ADDR (IL49_SCD_START_OFFSET + 0x10) - -/* - * Enables any/all Tx DMA/FIFO channels. - * Scheduler generates requests for only the active channels. - * Set this to 0xff to enable all 8 channels (normal usage). - * Bit fields: - * 7- 0: Enable (1), disable (0), one bit for each channel 0-7 - */ -#define IL49_SCD_TXFACT (IL49_SCD_START_OFFSET + 0x1c) -/* - * Queue (x) Write Pointers (idxes, really!), one for each Tx queue. - * Initialized and updated by driver as new TFDs are added to queue. - * NOTE: If using Block Ack, idx must correspond to frame's - * Start Sequence Number; idx = (SSN & 0xff) - * NOTE: Alternative to HBUS_TARG_WRPTR, which is what Linux driver uses? - */ -#define IL49_SCD_QUEUE_WRPTR(x) (IL49_SCD_START_OFFSET + 0x24 + (x) * 4) - -/* - * Queue (x) Read Pointers (idxes, really!), one for each Tx queue. - * For FIFO mode, idx indicates next frame to transmit. - * For Scheduler-ACK mode, idx indicates first frame in Tx win. - * Initialized by driver, updated by scheduler. - */ -#define IL49_SCD_QUEUE_RDPTR(x) (IL49_SCD_START_OFFSET + 0x64 + (x) * 4) - -/* - * Select which queues work in chain mode (1) vs. not (0). - * Use chain mode to build chains of aggregated frames. - * Bit fields: - * 31-16: Reserved - * 15-00: Mode, one bit for each queue -- 1: Chain mode, 0: one-at-a-time - * NOTE: If driver sets up queue for chain mode, it should be also set up - * Scheduler-ACK mode as well, via SCD_QUEUE_STATUS_BITS(x). - */ -#define IL49_SCD_QUEUECHAIN_SEL (IL49_SCD_START_OFFSET + 0xd0) - -/* - * Select which queues interrupt driver when scheduler increments - * a queue's read pointer (idx). - * Bit fields: - * 31-16: Reserved - * 15-00: Interrupt enable, one bit for each queue -- 1: enabled, 0: disabled - * NOTE: This functionality is apparently a no-op; driver relies on interrupts - * from Rx queue to read Tx command responses and update Tx queues. - */ -#define IL49_SCD_INTERRUPT_MASK (IL49_SCD_START_OFFSET + 0xe4) - -/* - * Queue search status registers. One for each queue. - * Sets up queue mode and assigns queue to Tx DMA channel. - * Bit fields: - * 19-10: Write mask/enable bits for bits 0-9 - * 9: Driver should init to "0" - * 8: Scheduler-ACK mode (1), non-Scheduler-ACK (i.e. FIFO) mode (0). - * Driver should init to "1" for aggregation mode, or "0" otherwise. - * 7-6: Driver should init to "0" - * 5: Window Size Left; indicates whether scheduler can request - * another TFD, based on win size, etc. Driver should init - * this bit to "1" for aggregation mode, or "0" for non-agg. - * 4-1: Tx FIFO to use (range 0-7). - * 0: Queue is active (1), not active (0). - * Other bits should be written as "0" - * - * NOTE: If enabling Scheduler-ACK mode, chain mode should also be enabled - * via SCD_QUEUECHAIN_SEL. - */ -#define IL49_SCD_QUEUE_STATUS_BITS(x)\ - (IL49_SCD_START_OFFSET + 0x104 + (x) * 4) - -/* Bit field positions */ -#define IL49_SCD_QUEUE_STTS_REG_POS_ACTIVE (0) -#define IL49_SCD_QUEUE_STTS_REG_POS_TXF (1) -#define IL49_SCD_QUEUE_STTS_REG_POS_WSL (5) -#define IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK (8) - -/* Write masks */ -#define IL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (10) -#define IL49_SCD_QUEUE_STTS_REG_MSK (0x0007FC00) - -/** - * 4965 internal SRAM structures for scheduler, shared with driver ... - * - * Driver should clear and initialize the following areas after receiving - * "Alive" response from 4965 uCode, i.e. after initial - * uCode load, or after a uCode load done for error recovery: - * - * SCD_CONTEXT_DATA_OFFSET (size 128 bytes) - * SCD_TX_STTS_BITMAP_OFFSET (size 256 bytes) - * SCD_TRANSLATE_TBL_OFFSET (size 32 bytes) - * - * Driver accesses SRAM via HBUS_TARG_MEM_* registers. - * Driver reads base address of this scheduler area from SCD_SRAM_BASE_ADDR. - * All OFFSET values must be added to this base address. - */ - -/* - * Queue context. One 8-byte entry for each of 16 queues. - * - * Driver should clear this entire area (size 0x80) to 0 after receiving - * "Alive" notification from uCode. Additionally, driver should init - * each queue's entry as follows: - * - * LS Dword bit fields: - * 0-06: Max Tx win size for Scheduler-ACK. Driver should init to 64. - * - * MS Dword bit fields: - * 16-22: Frame limit. Driver should init to 10 (0xa). - * - * Driver should init all other bits to 0. - * - * Init must be done after driver receives "Alive" response from 4965 uCode, - * and when setting up queue for aggregation. - */ -#define IL49_SCD_CONTEXT_DATA_OFFSET 0x380 -#define IL49_SCD_CONTEXT_QUEUE_OFFSET(x) \ - (IL49_SCD_CONTEXT_DATA_OFFSET + ((x) * 8)) - -#define IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS (0) -#define IL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK (0x0000007F) -#define IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16) -#define IL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000) - -/* - * Tx Status Bitmap - * - * Driver should clear this entire area (size 0x100) to 0 after receiving - * "Alive" notification from uCode. Area is used only by device itself; - * no other support (besides clearing) is required from driver. - */ -#define IL49_SCD_TX_STTS_BITMAP_OFFSET 0x400 - -/* - * RAxTID to queue translation mapping. - * - * When queue is in Scheduler-ACK mode, frames placed in a that queue must be - * for only one combination of receiver address (RA) and traffic ID (TID), i.e. - * one QOS priority level destined for one station (for this wireless link, - * not final destination). The SCD_TRANSLATE_TBL area provides 16 16-bit - * mappings, one for each of the 16 queues. If queue is not in Scheduler-ACK - * mode, the device ignores the mapping value. - * - * Bit fields, for each 16-bit map: - * 15-9: Reserved, set to 0 - * 8-4: Index into device's station table for recipient station - * 3-0: Traffic ID (tid), range 0-15 - * - * Driver should clear this entire area (size 32 bytes) to 0 after receiving - * "Alive" notification from uCode. To update a 16-bit map value, driver - * must read a dword-aligned value from device SRAM, replace the 16-bit map - * value of interest, and write the dword value back into device SRAM. - */ -#define IL49_SCD_TRANSLATE_TBL_OFFSET 0x500 - -/* Find translation table dword to read/write for given queue */ -#define IL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(x) \ - ((IL49_SCD_TRANSLATE_TBL_OFFSET + ((x) * 2)) & 0xfffffffc) - -#define IL_SCD_TXFIFO_POS_TID (0) -#define IL_SCD_TXFIFO_POS_RA (4) -#define IL_SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF) - -/*********************** END TX SCHEDULER *************************************/ - -#endif /* __il_prph_h__ */ -- cgit v1.2.3-59-g8ed1b From e705c12146aa9c69ca498d4ebb83ba7138f9b41f Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 20:57:38 +0200 Subject: iwlwifi: move under intel vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 1 - drivers/net/wireless/Makefile | 1 - drivers/net/wireless/intel/Kconfig | 1 + drivers/net/wireless/intel/Makefile | 2 + drivers/net/wireless/intel/iwlwifi/Kconfig | 161 + drivers/net/wireless/intel/iwlwifi/Makefile | 23 + drivers/net/wireless/intel/iwlwifi/dvm/Makefile | 13 + drivers/net/wireless/intel/iwlwifi/dvm/agn.h | 485 +++ drivers/net/wireless/intel/iwlwifi/dvm/calib.c | 1113 +++++ drivers/net/wireless/intel/iwlwifi/dvm/calib.h | 74 + drivers/net/wireless/intel/iwlwifi/dvm/commands.h | 4008 ++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c | 2441 +++++++++++ drivers/net/wireless/intel/iwlwifi/dvm/dev.h | 949 +++++ drivers/net/wireless/intel/iwlwifi/dvm/devices.c | 690 ++++ drivers/net/wireless/intel/iwlwifi/dvm/led.c | 223 + drivers/net/wireless/intel/iwlwifi/dvm/led.h | 55 + drivers/net/wireless/intel/iwlwifi/dvm/lib.c | 1300 ++++++ drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c | 1655 ++++++++ drivers/net/wireless/intel/iwlwifi/dvm/main.c | 2077 ++++++++++ drivers/net/wireless/intel/iwlwifi/dvm/power.c | 395 ++ drivers/net/wireless/intel/iwlwifi/dvm/power.h | 47 + drivers/net/wireless/intel/iwlwifi/dvm/rs.c | 3338 +++++++++++++++ drivers/net/wireless/intel/iwlwifi/dvm/rs.h | 426 ++ drivers/net/wireless/intel/iwlwifi/dvm/rx.c | 1101 +++++ drivers/net/wireless/intel/iwlwifi/dvm/rxon.c | 1572 ++++++++ drivers/net/wireless/intel/iwlwifi/dvm/scan.c | 1075 +++++ drivers/net/wireless/intel/iwlwifi/dvm/sta.c | 1442 +++++++ drivers/net/wireless/intel/iwlwifi/dvm/tt.c | 685 ++++ drivers/net/wireless/intel/iwlwifi/dvm/tt.h | 128 + drivers/net/wireless/intel/iwlwifi/dvm/tx.c | 1412 +++++++ drivers/net/wireless/intel/iwlwifi/dvm/ucode.c | 452 +++ drivers/net/wireless/intel/iwlwifi/iwl-1000.c | 140 + drivers/net/wireless/intel/iwlwifi/iwl-2000.c | 216 + drivers/net/wireless/intel/iwlwifi/iwl-5000.c | 178 + drivers/net/wireless/intel/iwlwifi/iwl-6000.c | 389 ++ drivers/net/wireless/intel/iwlwifi/iwl-7000.c | 346 ++ drivers/net/wireless/intel/iwlwifi/iwl-8000.c | 229 ++ drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h | 117 + drivers/net/wireless/intel/iwlwifi/iwl-config.h | 437 ++ drivers/net/wireless/intel/iwlwifi/iwl-csr.h | 552 +++ drivers/net/wireless/intel/iwlwifi/iwl-debug.c | 136 + drivers/net/wireless/intel/iwlwifi/iwl-debug.h | 225 ++ .../net/wireless/intel/iwlwifi/iwl-devtrace-data.h | 80 + .../net/wireless/intel/iwlwifi/iwl-devtrace-io.h | 155 + .../wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h | 209 + .../net/wireless/intel/iwlwifi/iwl-devtrace-msg.h | 97 + .../wireless/intel/iwlwifi/iwl-devtrace-ucode.h | 81 + drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c | 43 + drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h | 89 + drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 1706 ++++++++ drivers/net/wireless/intel/iwlwifi/iwl-drv.h | 155 + .../net/wireless/intel/iwlwifi/iwl-eeprom-parse.c | 947 +++++ .../net/wireless/intel/iwlwifi/iwl-eeprom-parse.h | 144 + .../net/wireless/intel/iwlwifi/iwl-eeprom-read.c | 464 +++ .../net/wireless/intel/iwlwifi/iwl-eeprom-read.h | 70 + drivers/net/wireless/intel/iwlwifi/iwl-fh.h | 535 +++ .../net/wireless/intel/iwlwifi/iwl-fw-error-dump.h | 320 ++ drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 768 ++++ drivers/net/wireless/intel/iwlwifi/iwl-fw.h | 322 ++ drivers/net/wireless/intel/iwlwifi/iwl-io.c | 289 ++ drivers/net/wireless/intel/iwlwifi/iwl-io.h | 73 + drivers/net/wireless/intel/iwlwifi/iwl-modparams.h | 129 + .../net/wireless/intel/iwlwifi/iwl-notif-wait.c | 193 + .../net/wireless/intel/iwlwifi/iwl-notif-wait.h | 139 + drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 844 ++++ drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h | 97 + drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h | 271 ++ drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c | 471 +++ drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h | 82 + drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 401 ++ drivers/net/wireless/intel/iwlwifi/iwl-scd.h | 143 + drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 114 + drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 1125 ++++++ drivers/net/wireless/intel/iwlwifi/mvm/Makefile | 12 + drivers/net/wireless/intel/iwlwifi/mvm/binding.c | 211 + drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 1005 +++++ .../net/wireless/intel/iwlwifi/mvm/coex_legacy.c | 1315 ++++++ drivers/net/wireless/intel/iwlwifi/mvm/constants.h | 139 + drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2104 ++++++++++ .../net/wireless/intel/iwlwifi/mvm/debugfs-vif.c | 1483 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 1516 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h | 103 + .../net/wireless/intel/iwlwifi/mvm/fw-api-coex.h | 476 +++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h | 425 ++ .../net/wireless/intel/iwlwifi/mvm/fw-api-mac.h | 387 ++ .../net/wireless/intel/iwlwifi/mvm/fw-api-power.h | 467 +++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h | 389 ++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h | 238 ++ .../net/wireless/intel/iwlwifi/mvm/fw-api-scan.h | 730 ++++ .../net/wireless/intel/iwlwifi/mvm/fw-api-sta.h | 414 ++ .../net/wireless/intel/iwlwifi/mvm/fw-api-stats.h | 284 ++ .../net/wireless/intel/iwlwifi/mvm/fw-api-tof.h | 386 ++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h | 646 +++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 1773 ++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 1166 ++++++ drivers/net/wireless/intel/iwlwifi/mvm/led.c | 136 + drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 1452 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 4260 ++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1535 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 864 ++++ .../net/wireless/intel/iwlwifi/mvm/offloading.c | 217 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1434 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c | 295 ++ drivers/net/wireless/intel/iwlwifi/mvm/power.c | 1040 +++++ drivers/net/wireless/intel/iwlwifi/mvm/quota.c | 328 ++ drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 3983 ++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/rs.h | 392 ++ drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 612 +++ drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 1552 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/sf.c | 340 ++ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 1810 +++++++++ drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 426 ++ drivers/net/wireless/intel/iwlwifi/mvm/tdls.c | 732 ++++ drivers/net/wireless/intel/iwlwifi/mvm/testmode.h | 97 + .../net/wireless/intel/iwlwifi/mvm/time-event.c | 872 ++++ .../net/wireless/intel/iwlwifi/mvm/time-event.h | 249 ++ drivers/net/wireless/intel/iwlwifi/mvm/tof.c | 306 ++ drivers/net/wireless/intel/iwlwifi/mvm/tof.h | 94 + drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 460 +++ drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 1115 +++++ drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 1083 +++++ drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 685 ++++ drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 569 +++ drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 1548 +++++++ drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2825 +++++++++++++ drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 1988 +++++++++ drivers/net/wireless/iwlwifi/Kconfig | 161 - drivers/net/wireless/iwlwifi/Makefile | 23 - drivers/net/wireless/iwlwifi/dvm/Makefile | 13 - drivers/net/wireless/iwlwifi/dvm/agn.h | 485 --- drivers/net/wireless/iwlwifi/dvm/calib.c | 1113 ----- drivers/net/wireless/iwlwifi/dvm/calib.h | 74 - drivers/net/wireless/iwlwifi/dvm/commands.h | 4008 ------------------ drivers/net/wireless/iwlwifi/dvm/debugfs.c | 2441 ----------- drivers/net/wireless/iwlwifi/dvm/dev.h | 949 ----- drivers/net/wireless/iwlwifi/dvm/devices.c | 690 ---- drivers/net/wireless/iwlwifi/dvm/led.c | 223 - drivers/net/wireless/iwlwifi/dvm/led.h | 55 - drivers/net/wireless/iwlwifi/dvm/lib.c | 1300 ------ drivers/net/wireless/iwlwifi/dvm/mac80211.c | 1655 -------- drivers/net/wireless/iwlwifi/dvm/main.c | 2077 ---------- drivers/net/wireless/iwlwifi/dvm/power.c | 395 -- drivers/net/wireless/iwlwifi/dvm/power.h | 47 - drivers/net/wireless/iwlwifi/dvm/rs.c | 3338 --------------- drivers/net/wireless/iwlwifi/dvm/rs.h | 426 -- drivers/net/wireless/iwlwifi/dvm/rx.c | 1101 ----- drivers/net/wireless/iwlwifi/dvm/rxon.c | 1572 -------- drivers/net/wireless/iwlwifi/dvm/scan.c | 1075 ----- drivers/net/wireless/iwlwifi/dvm/sta.c | 1442 ------- drivers/net/wireless/iwlwifi/dvm/tt.c | 685 ---- drivers/net/wireless/iwlwifi/dvm/tt.h | 128 - drivers/net/wireless/iwlwifi/dvm/tx.c | 1412 ------- drivers/net/wireless/iwlwifi/dvm/ucode.c | 452 --- drivers/net/wireless/iwlwifi/iwl-1000.c | 140 - drivers/net/wireless/iwlwifi/iwl-2000.c | 216 - drivers/net/wireless/iwlwifi/iwl-5000.c | 178 - drivers/net/wireless/iwlwifi/iwl-6000.c | 389 -- drivers/net/wireless/iwlwifi/iwl-7000.c | 346 -- drivers/net/wireless/iwlwifi/iwl-8000.c | 229 -- drivers/net/wireless/iwlwifi/iwl-agn-hw.h | 117 - drivers/net/wireless/iwlwifi/iwl-config.h | 437 -- drivers/net/wireless/iwlwifi/iwl-csr.h | 552 --- drivers/net/wireless/iwlwifi/iwl-debug.c | 136 - drivers/net/wireless/iwlwifi/iwl-debug.h | 225 -- drivers/net/wireless/iwlwifi/iwl-devtrace-data.h | 80 - drivers/net/wireless/iwlwifi/iwl-devtrace-io.h | 155 - .../net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h | 209 - drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h | 97 - drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h | 81 - drivers/net/wireless/iwlwifi/iwl-devtrace.c | 43 - drivers/net/wireless/iwlwifi/iwl-devtrace.h | 89 - drivers/net/wireless/iwlwifi/iwl-drv.c | 1706 -------- drivers/net/wireless/iwlwifi/iwl-drv.h | 155 - drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c | 947 ----- drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h | 144 - drivers/net/wireless/iwlwifi/iwl-eeprom-read.c | 464 --- drivers/net/wireless/iwlwifi/iwl-eeprom-read.h | 70 - drivers/net/wireless/iwlwifi/iwl-fh.h | 535 --- drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h | 320 -- drivers/net/wireless/iwlwifi/iwl-fw-file.h | 768 ---- drivers/net/wireless/iwlwifi/iwl-fw.h | 322 -- drivers/net/wireless/iwlwifi/iwl-io.c | 289 -- drivers/net/wireless/iwlwifi/iwl-io.h | 73 - drivers/net/wireless/iwlwifi/iwl-modparams.h | 129 - drivers/net/wireless/iwlwifi/iwl-notif-wait.c | 193 - drivers/net/wireless/iwlwifi/iwl-notif-wait.h | 139 - drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 844 ---- drivers/net/wireless/iwlwifi/iwl-nvm-parse.h | 97 - drivers/net/wireless/iwlwifi/iwl-op-mode.h | 271 -- drivers/net/wireless/iwlwifi/iwl-phy-db.c | 471 --- drivers/net/wireless/iwlwifi/iwl-phy-db.h | 82 - drivers/net/wireless/iwlwifi/iwl-prph.h | 401 -- drivers/net/wireless/iwlwifi/iwl-scd.h | 143 - drivers/net/wireless/iwlwifi/iwl-trans.c | 114 - drivers/net/wireless/iwlwifi/iwl-trans.h | 1125 ------ drivers/net/wireless/iwlwifi/mvm/Makefile | 12 - drivers/net/wireless/iwlwifi/mvm/binding.c | 211 - drivers/net/wireless/iwlwifi/mvm/coex.c | 1005 ----- drivers/net/wireless/iwlwifi/mvm/coex_legacy.c | 1315 ------ drivers/net/wireless/iwlwifi/mvm/constants.h | 139 - drivers/net/wireless/iwlwifi/mvm/d3.c | 2104 ---------- drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c | 1483 ------- drivers/net/wireless/iwlwifi/mvm/debugfs.c | 1516 ------- drivers/net/wireless/iwlwifi/mvm/debugfs.h | 103 - drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h | 476 --- drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h | 425 -- drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h | 387 -- drivers/net/wireless/iwlwifi/mvm/fw-api-power.h | 467 --- drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h | 389 -- drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h | 238 -- drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h | 730 ---- drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h | 414 -- drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h | 284 -- drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h | 386 -- drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h | 646 --- drivers/net/wireless/iwlwifi/mvm/fw-api.h | 1773 -------- drivers/net/wireless/iwlwifi/mvm/fw.c | 1166 ------ drivers/net/wireless/iwlwifi/mvm/led.c | 136 - drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 1452 ------- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 4260 -------------------- drivers/net/wireless/iwlwifi/mvm/mvm.h | 1535 ------- drivers/net/wireless/iwlwifi/mvm/nvm.c | 864 ---- drivers/net/wireless/iwlwifi/mvm/offloading.c | 217 - drivers/net/wireless/iwlwifi/mvm/ops.c | 1434 ------- drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c | 295 -- drivers/net/wireless/iwlwifi/mvm/power.c | 1040 ----- drivers/net/wireless/iwlwifi/mvm/quota.c | 328 -- drivers/net/wireless/iwlwifi/mvm/rs.c | 3983 ------------------ drivers/net/wireless/iwlwifi/mvm/rs.h | 392 -- drivers/net/wireless/iwlwifi/mvm/rx.c | 612 --- drivers/net/wireless/iwlwifi/mvm/scan.c | 1552 ------- drivers/net/wireless/iwlwifi/mvm/sf.c | 340 -- drivers/net/wireless/iwlwifi/mvm/sta.c | 1810 --------- drivers/net/wireless/iwlwifi/mvm/sta.h | 426 -- drivers/net/wireless/iwlwifi/mvm/tdls.c | 732 ---- drivers/net/wireless/iwlwifi/mvm/testmode.h | 97 - drivers/net/wireless/iwlwifi/mvm/time-event.c | 872 ---- drivers/net/wireless/iwlwifi/mvm/time-event.h | 249 -- drivers/net/wireless/iwlwifi/mvm/tof.c | 306 -- drivers/net/wireless/iwlwifi/mvm/tof.h | 94 - drivers/net/wireless/iwlwifi/mvm/tt.c | 460 --- drivers/net/wireless/iwlwifi/mvm/tx.c | 1115 ----- drivers/net/wireless/iwlwifi/mvm/utils.c | 1083 ----- drivers/net/wireless/iwlwifi/pcie/drv.c | 685 ---- drivers/net/wireless/iwlwifi/pcie/internal.h | 569 --- drivers/net/wireless/iwlwifi/pcie/rx.c | 1548 ------- drivers/net/wireless/iwlwifi/pcie/trans.c | 2825 ------------- drivers/net/wireless/iwlwifi/pcie/tx.c | 1988 --------- 249 files changed, 89833 insertions(+), 89832 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/Kconfig create mode 100644 drivers/net/wireless/intel/iwlwifi/Makefile create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/Makefile create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/agn.h create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/calib.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/calib.h create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/commands.h create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/dev.h create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/devices.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/led.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/led.h create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/lib.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/main.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/power.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/power.h create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/rs.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/rs.h create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/rx.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/rxon.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/scan.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/sta.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/tt.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/tt.h create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/tx.c create mode 100644 drivers/net/wireless/intel/iwlwifi/dvm/ucode.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-1000.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-2000.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-5000.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-6000.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-7000.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-8000.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-config.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-csr.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-debug.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-debug.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-drv.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-drv.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-fh.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-fw.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-io.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-io.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-modparams.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-prph.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-scd.h create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-trans.c create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-trans.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/Makefile create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/binding.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/coex.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/constants.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/d3.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/led.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/mvm.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/nvm.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/offloading.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/ops.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/power.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/quota.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/rs.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/rs.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/rx.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/scan.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/sf.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/sta.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/sta.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/tdls.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/testmode.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/time-event.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/time-event.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/tof.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/tof.h create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/tt.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/tx.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/utils.c create mode 100644 drivers/net/wireless/intel/iwlwifi/pcie/drv.c create mode 100644 drivers/net/wireless/intel/iwlwifi/pcie/internal.h create mode 100644 drivers/net/wireless/intel/iwlwifi/pcie/rx.c create mode 100644 drivers/net/wireless/intel/iwlwifi/pcie/trans.c create mode 100644 drivers/net/wireless/intel/iwlwifi/pcie/tx.c delete mode 100644 drivers/net/wireless/iwlwifi/Kconfig delete mode 100644 drivers/net/wireless/iwlwifi/Makefile delete mode 100644 drivers/net/wireless/iwlwifi/dvm/Makefile delete mode 100644 drivers/net/wireless/iwlwifi/dvm/agn.h delete mode 100644 drivers/net/wireless/iwlwifi/dvm/calib.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/calib.h delete mode 100644 drivers/net/wireless/iwlwifi/dvm/commands.h delete mode 100644 drivers/net/wireless/iwlwifi/dvm/debugfs.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/dev.h delete mode 100644 drivers/net/wireless/iwlwifi/dvm/devices.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/led.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/led.h delete mode 100644 drivers/net/wireless/iwlwifi/dvm/lib.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/mac80211.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/main.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/power.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/power.h delete mode 100644 drivers/net/wireless/iwlwifi/dvm/rs.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/rs.h delete mode 100644 drivers/net/wireless/iwlwifi/dvm/rx.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/rxon.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/scan.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/sta.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/tt.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/tt.h delete mode 100644 drivers/net/wireless/iwlwifi/dvm/tx.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/ucode.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-1000.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-2000.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-5000.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-6000.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-7000.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-8000.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-hw.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-config.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-csr.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-debug.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-debug.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-devtrace-data.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-devtrace-io.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-devtrace.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-devtrace.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-drv.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-drv.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-eeprom-read.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-eeprom-read.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-fh.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-fw-file.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-fw.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-io.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-io.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-modparams.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-notif-wait.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-notif-wait.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-nvm-parse.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-nvm-parse.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-op-mode.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-phy-db.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-phy-db.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-prph.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-scd.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-trans.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-trans.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/Makefile delete mode 100644 drivers/net/wireless/iwlwifi/mvm/binding.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/coex.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/coex_legacy.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/constants.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/d3.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/debugfs.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/debugfs.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api-power.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw-api.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/fw.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/led.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/mac80211.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/mvm.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/nvm.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/offloading.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/ops.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/power.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/quota.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/rs.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/rs.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/rx.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/scan.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/sf.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/sta.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/sta.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/tdls.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/testmode.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/time-event.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/time-event.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/tof.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/tof.h delete mode 100644 drivers/net/wireless/iwlwifi/mvm/tt.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/tx.c delete mode 100644 drivers/net/wireless/iwlwifi/mvm/utils.c delete mode 100644 drivers/net/wireless/iwlwifi/pcie/drv.c delete mode 100644 drivers/net/wireless/iwlwifi/pcie/internal.h delete mode 100644 drivers/net/wireless/iwlwifi/pcie/rx.c delete mode 100644 drivers/net/wireless/iwlwifi/pcie/trans.c delete mode 100644 drivers/net/wireless/iwlwifi/pcie/tx.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 83f9c2ba6962..d72be8c88138 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5651,7 +5651,7 @@ L: linux-wireless@vger.kernel.org W: http://intellinuxwireless.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git S: Supported -F: drivers/net/wireless/iwlwifi/ +F: drivers/net/wireless/intel/iwlwifi/ INTEL MANAGEMENT ENGINE (mei) M: Tomas Winkler diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index f293831e2878..d71efe89970b 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -162,7 +162,6 @@ config MWL8K source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/hostap/Kconfig" -source "drivers/net/wireless/iwlwifi/Kconfig" source "drivers/net/wireless/libertas/Kconfig" source "drivers/net/wireless/orinoco/Kconfig" source "drivers/net/wireless/p54/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 135aa8753f69..6bcb2925a6ce 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ obj-$(CONFIG_MWL8K) += mwl8k.o -obj-$(CONFIG_IWLWIFI) += iwlwifi/ obj-$(CONFIG_RT2X00) += rt2x00/ obj-$(CONFIG_WL_MEDIATEK) += mediatek/ diff --git a/drivers/net/wireless/intel/Kconfig b/drivers/net/wireless/intel/Kconfig index 0a7cd61e528c..5b14f2f64a8a 100644 --- a/drivers/net/wireless/intel/Kconfig +++ b/drivers/net/wireless/intel/Kconfig @@ -13,5 +13,6 @@ if WLAN_VENDOR_INTEL source "drivers/net/wireless/intel/ipw2x00/Kconfig" source "drivers/net/wireless/intel/iwlegacy/Kconfig" +source "drivers/net/wireless/intel/iwlwifi/Kconfig" endif # WLAN_VENDOR_INTEL diff --git a/drivers/net/wireless/intel/Makefile b/drivers/net/wireless/intel/Makefile index cec507d3c6bf..c9cbcc85b569 100644 --- a/drivers/net/wireless/intel/Makefile +++ b/drivers/net/wireless/intel/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_IPW2100) += ipw2x00/ obj-$(CONFIG_IPW2200) += ipw2x00/ obj-$(CONFIG_IWLEGACY) += iwlegacy/ + +obj-$(CONFIG_IWLWIFI) += iwlwifi/ diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig new file mode 100644 index 000000000000..6e949df399d6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/Kconfig @@ -0,0 +1,161 @@ +config IWLWIFI + tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) " + depends on PCI && MAC80211 && HAS_IOMEM + select FW_LOADER + ---help--- + Select to build the driver supporting the: + + Intel Wireless WiFi Link Next-Gen AGN + + This option enables support for use with the following hardware: + Intel Wireless WiFi Link 6250AGN Adapter + Intel 6000 Series Wi-Fi Adapters (6200AGN and 6300AGN) + Intel WiFi Link 1000BGN + Intel Wireless WiFi 5150AGN + Intel Wireless WiFi 5100AGN, 5300AGN, and 5350AGN + Intel 6005 Series Wi-Fi Adapters + Intel 6030 Series Wi-Fi Adapters + Intel Wireless WiFi Link 6150BGN 2 Adapter + Intel 100 Series Wi-Fi Adapters (100BGN and 130BGN) + Intel 2000 Series Wi-Fi Adapters + Intel 7260 Wi-Fi Adapter + Intel 3160 Wi-Fi Adapter + Intel 7265 Wi-Fi Adapter + Intel 8260 Wi-Fi Adapter + Intel 3165 Wi-Fi Adapter + + + This driver uses the kernel's mac80211 subsystem. + + In order to use this driver, you will need a firmware + image for it. You can obtain the microcode from: + + . + + The firmware is typically installed in /lib/firmware. You can + look in the hotplug script /etc/hotplug/firmware.agent to + determine which directory FIRMWARE_DIR is set to when the script + runs. + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The + module will be called iwlwifi. + +if IWLWIFI + +config IWLWIFI_LEDS + bool + depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI + select LEDS_TRIGGERS + select MAC80211_LEDS + default y + +config IWLDVM + tristate "Intel Wireless WiFi DVM Firmware support" + default IWLWIFI + help + This is the driver that supports the DVM firmware. The list + of the devices that use this firmware is available here: + https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware + +config IWLMVM + tristate "Intel Wireless WiFi MVM Firmware support" + select WANT_DEV_COREDUMP + help + This is the driver that supports the MVM firmware. The list + of the devices that use this firmware is available here: + https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware + +# don't call it _MODULE -- will confuse Kconfig/fixdep/... +config IWLWIFI_OPMODE_MODULAR + bool + default y if IWLDVM=m + default y if IWLMVM=m + +comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM" + depends on IWLDVM=n && IWLMVM=n + +config IWLWIFI_BCAST_FILTERING + bool "Enable broadcast filtering" + depends on IWLMVM + help + Say Y here to enable default bcast filtering configuration. + + Enabling broadcast filtering will drop any incoming wireless + broadcast frames, except some very specific predefined + patterns (e.g. incoming arp requests). + + If unsure, don't enable this option, as some programs might + expect incoming broadcasts for their normal operations. + +config IWLWIFI_UAPSD + bool "enable U-APSD by default" + depends on IWLMVM + help + Say Y here to enable U-APSD by default. This may cause + interoperability problems with some APs, manifesting in lower than + expected throughput due to those APs not enabling aggregation + + If unsure, say N. + +menu "Debugging Options" + +config IWLWIFI_DEBUG + bool "Enable full debugging output in the iwlwifi driver" + ---help--- + This option will enable debug tracing output for the iwlwifi drivers + + This will result in the kernel module being ~100k larger. You can + control which debug output is sent to the kernel log by setting the + value in + + /sys/module/iwlwifi/parameters/debug + + This entry will only exist if this option is enabled. + + To set a value, simply echo an 8-byte hex value to the same file: + + % echo 0x43fff > /sys/module/iwlwifi/parameters/debug + + You can find the list of debug mask values in: + drivers/net/wireless/iwlwifi/iwl-debug.h + + If this is your first time using this driver, you should say Y here + as the debug information can assist others in helping you resolve + any problems you may encounter. + +config IWLWIFI_DEBUGFS + bool "iwlwifi debugfs support" + depends on MAC80211_DEBUGFS + ---help--- + Enable creation of debugfs files for the iwlwifi drivers. This + is a low-impact option that allows getting insight into the + driver's state at runtime. + +config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE + bool "Experimental uCode support" + depends on IWLWIFI_DEBUG + ---help--- + Enable use of experimental ucode for testing and debugging. + +config IWLWIFI_DEVICE_TRACING + bool "iwlwifi device access tracing" + depends on EVENT_TRACING + default y + help + Say Y here to trace all commands, including TX frames and IO + accesses, sent to the device. If you say yes, iwlwifi will + register with the ftrace framework for event tracing and dump + all this information to the ringbuffer, you may need to + increase the ringbuffer size. See the ftrace documentation + for more information. + + When tracing is not enabled, this option still has some + (though rather small) overhead. + + If unsure, say Y so we can help you better when problems + occur. +endmenu + +endif diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile new file mode 100644 index 000000000000..dbfc5b18bcb7 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -0,0 +1,23 @@ +# common +obj-$(CONFIG_IWLWIFI) += iwlwifi.o +iwlwifi-objs += iwl-io.o +iwlwifi-objs += iwl-drv.o +iwlwifi-objs += iwl-debug.o +iwlwifi-objs += iwl-notif-wait.o +iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o +iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o +iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o +iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o +iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o +iwlwifi-objs += iwl-trans.o + +iwlwifi-objs += $(iwlwifi-m) + +iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o + +ccflags-y += -D__CHECK_ENDIAN__ -I$(src) + +obj-$(CONFIG_IWLDVM) += dvm/ +obj-$(CONFIG_IWLMVM) += mvm/ + +CFLAGS_iwl-devtrace.o := -I$(src) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/Makefile b/drivers/net/wireless/intel/iwlwifi/dvm/Makefile new file mode 100644 index 000000000000..4d19685f31c3 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/Makefile @@ -0,0 +1,13 @@ +# DVM +obj-$(CONFIG_IWLDVM) += iwldvm.o +iwldvm-objs += main.o rs.o mac80211.o ucode.o tx.o +iwldvm-objs += lib.o calib.o tt.o sta.o rx.o + +iwldvm-objs += power.o +iwldvm-objs += scan.o +iwldvm-objs += rxon.o devices.o + +iwldvm-$(CONFIG_IWLWIFI_LEDS) += led.o +iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o + +ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h new file mode 100644 index 000000000000..991def878881 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h @@ -0,0 +1,485 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_agn_h__ +#define __iwl_agn_h__ + +#include "iwl-config.h" + +#include "dev.h" + +/* The first 11 queues (0-10) are used otherwise */ +#define IWLAGN_FIRST_AMPDU_QUEUE 11 + +/* AUX (TX during scan dwell) queue */ +#define IWL_AUX_QUEUE 10 + +#define IWL_INVALID_STATION 255 + +/* device operations */ +extern const struct iwl_dvm_cfg iwl_dvm_1000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_2000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_105_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_2030_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_5000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_5150_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6005_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6050_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg; + + +#define TIME_UNIT 1024 + +/***************************************************** +* DRIVER STATUS FUNCTIONS +******************************************************/ +#define STATUS_RF_KILL_HW 0 +#define STATUS_CT_KILL 1 +#define STATUS_ALIVE 2 +#define STATUS_READY 3 +#define STATUS_EXIT_PENDING 5 +#define STATUS_STATISTICS 6 +#define STATUS_SCANNING 7 +#define STATUS_SCAN_ABORTING 8 +#define STATUS_SCAN_HW 9 +#define STATUS_FW_ERROR 10 +#define STATUS_CHANNEL_SWITCH_PENDING 11 +#define STATUS_SCAN_COMPLETE 12 +#define STATUS_POWER_PMI 13 + +struct iwl_ucode_capabilities; + +extern const struct ieee80211_ops iwlagn_hw_ops; + +static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd) +{ + hdr->op_code = cmd; + hdr->first_group = 0; + hdr->groups_num = 1; + hdr->data_valid = 1; +} + +void iwl_down(struct iwl_priv *priv); +void iwl_cancel_deferred_work(struct iwl_priv *priv); +void iwlagn_prepare_restart(struct iwl_priv *priv); +void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb); + +bool iwl_check_for_ct_kill(struct iwl_priv *priv); + +void iwlagn_lift_passive_no_rx(struct iwl_priv *priv); + +/* MAC80211 */ +struct ieee80211_hw *iwl_alloc_all(void); +int iwlagn_mac_setup_register(struct iwl_priv *priv, + const struct iwl_ucode_capabilities *capa); +void iwlagn_mac_unregister(struct iwl_priv *priv); + +/* commands */ +int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd); +int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, + u32 flags, u16 len, const void *data); + +/* RXON */ +void iwl_connection_init_rx_config(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwlagn_set_pan_params(struct iwl_priv *priv); +int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx); +void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx); +int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed); +void iwlagn_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes); +void iwlagn_config_ht40(struct ieee80211_conf *conf, + struct iwl_rxon_context *ctx); +void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf); +void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, + struct iwl_rxon_context *ctx); +void iwl_set_flags_for_band(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + enum ieee80211_band band, + struct ieee80211_vif *vif); + +/* uCode */ +int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type); +void iwl_send_prio_tbl(struct iwl_priv *priv); +int iwl_init_alive_start(struct iwl_priv *priv); +int iwl_run_init_ucode(struct iwl_priv *priv); +int iwl_load_ucode_wait_alive(struct iwl_priv *priv, + enum iwl_ucode_type ucode_type); +int iwl_send_calib_results(struct iwl_priv *priv); +int iwl_calib_set(struct iwl_priv *priv, + const struct iwl_calib_hdr *cmd, int len); +void iwl_calib_free_results(struct iwl_priv *priv); +int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, + char **buf); +int iwlagn_hw_valid_rtc_data_addr(u32 addr); + +/* lib */ +int iwlagn_send_tx_power(struct iwl_priv *priv); +void iwlagn_temperature(struct iwl_priv *priv); +int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk); +void iwlagn_dev_txfifo_flush(struct iwl_priv *priv); +int iwlagn_send_beacon_cmd(struct iwl_priv *priv); +int iwl_send_statistics_request(struct iwl_priv *priv, + u8 flags, bool clear); + +static inline const struct ieee80211_supported_band *iwl_get_hw_mode( + struct iwl_priv *priv, enum ieee80211_band band) +{ + return priv->hw->wiphy->bands[band]; +} + +#ifdef CONFIG_PM_SLEEP +int iwlagn_send_patterns(struct iwl_priv *priv, + struct cfg80211_wowlan *wowlan); +int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan); +#endif + +/* rx */ +int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); +void iwl_setup_rx_handlers(struct iwl_priv *priv); +void iwl_chswitch_done(struct iwl_priv *priv, bool is_success); + + +/* tx */ +int iwlagn_tx_skb(struct iwl_priv *priv, + struct ieee80211_sta *sta, + struct sk_buff *skb); +int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u16 *ssn); +int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u8 buf_size); +int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid); +int iwlagn_tx_agg_flush(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid); +void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb); +void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb); + +static inline u32 iwl_tx_status_to_mac80211(u32 status) +{ + status &= TX_STATUS_MSK; + + switch (status) { + case TX_STATUS_SUCCESS: + case TX_STATUS_DIRECT_DONE: + return IEEE80211_TX_STAT_ACK; + case TX_STATUS_FAIL_DEST_PS: + case TX_STATUS_FAIL_PASSIVE_NO_RX: + return IEEE80211_TX_STAT_TX_FILTERED; + default: + return 0; + } +} + +static inline bool iwl_is_tx_success(u32 status) +{ + status &= TX_STATUS_MSK; + return (status == TX_STATUS_SUCCESS) || + (status == TX_STATUS_DIRECT_DONE); +} + +u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid); + +/* scan */ +void iwlagn_post_scan(struct iwl_priv *priv); +int iwl_force_rf_reset(struct iwl_priv *priv, bool external); +void iwl_init_scan_params(struct iwl_priv *priv); +int iwl_scan_cancel(struct iwl_priv *priv); +void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); +void iwl_force_scan_end(struct iwl_priv *priv); +void iwl_internal_short_hw_scan(struct iwl_priv *priv); +void iwl_setup_rx_scan_handlers(struct iwl_priv *priv); +void iwl_setup_scan_deferred_work(struct iwl_priv *priv); +void iwl_cancel_scan_deferred_work(struct iwl_priv *priv); +int __must_check iwl_scan_initiate(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum iwl_scan_type scan_type, + enum ieee80211_band band); + +/* For faster active scanning, scan will move to the next channel if fewer than + * PLCP_QUIET_THRESH packets are heard on this channel within + * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell + * time if it's a quiet channel (nothing responded to our probe, and there's + * no other traffic). + * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */ +#define IWL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */ +#define IWL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */ + +#define IWL_SCAN_CHECK_WATCHDOG (HZ * 15) + + +/* bt coex */ +void iwlagn_send_advance_bt_config(struct iwl_priv *priv); +void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv); +void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv); +void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv); +void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv); +void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena); + +static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv) +{ + return priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist; +} + +#ifdef CONFIG_IWLWIFI_DEBUG +const char *iwl_get_tx_fail_reason(u32 status); +const char *iwl_get_agg_tx_fail_reason(u16 status); +#else +static inline const char *iwl_get_tx_fail_reason(u32 status) { return ""; } +static inline const char *iwl_get_agg_tx_fail_reason(u16 status) { return ""; } +#endif + + +/* station management */ +int iwlagn_manage_ibss_station(struct iwl_priv *priv, + struct ieee80211_vif *vif, bool add); +#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ +#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ +#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of + being activated */ +#define IWL_STA_LOCAL BIT(3) /* station state not directed by mac80211; + (this is for the IBSS BSSID stations) */ +#define IWL_STA_BCAST BIT(4) /* this station is the special bcast station */ + + +void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx); +void iwl_clear_ucode_stations(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +void iwl_dealloc_bcast_stations(struct iwl_priv *priv); +int iwl_get_free_ucode_key_offset(struct iwl_priv *priv); +int iwl_send_add_sta(struct iwl_priv *priv, + struct iwl_addsta_cmd *sta, u8 flags); +int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, + struct ieee80211_sta *sta, u8 *sta_id_r); +int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, + const u8 *addr); +void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, + const u8 *addr); +u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, struct ieee80211_sta *sta); + +int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct iwl_link_quality_cmd *lq, u8 flags, bool init); +void iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb); +int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct ieee80211_sta *sta); + +bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_sta *sta); + +static inline int iwl_sta_id(struct ieee80211_sta *sta) +{ + if (WARN_ON(!sta)) + return IWL_INVALID_STATION; + + return ((struct iwl_station_priv *)sta->drv_priv)->sta_id; +} + +int iwlagn_alloc_bcast_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, u8 *sta_id_r); +int iwl_remove_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key); +int iwl_set_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key); +int iwl_restore_default_wep_keys(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwl_set_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key, + struct ieee80211_sta *sta); +int iwl_remove_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key, + struct ieee80211_sta *sta); +void iwl_update_tkip_key(struct iwl_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); +int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid); +int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid, u16 ssn); +int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid); +void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt); +int iwl_update_bcast_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwl_update_bcast_stations(struct iwl_priv *priv); + +/* rate */ +static inline u32 iwl_ant_idx_to_flags(u8 ant_idx) +{ + return BIT(ant_idx) << RATE_MCS_ANT_POS; +} + +static inline u8 iwl_hw_get_rate(__le32 rate_n_flags) +{ + return le32_to_cpu(rate_n_flags) & RATE_MCS_RATE_MSK; +} + +static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) +{ + return cpu_to_le32(flags|(u32)rate); +} + +int iwl_alive_start(struct iwl_priv *priv); + +#ifdef CONFIG_IWLWIFI_DEBUG +void iwl_print_rx_config_cmd(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid); +#else +static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid) +{ +} +#endif + +/* status checks */ + +static inline int iwl_is_ready(struct iwl_priv *priv) +{ + /* The adapter is 'ready' if READY EXIT_PENDING is not set */ + return test_bit(STATUS_READY, &priv->status) && + !test_bit(STATUS_EXIT_PENDING, &priv->status); +} + +static inline int iwl_is_alive(struct iwl_priv *priv) +{ + return test_bit(STATUS_ALIVE, &priv->status); +} + +static inline int iwl_is_rfkill(struct iwl_priv *priv) +{ + return test_bit(STATUS_RF_KILL_HW, &priv->status); +} + +static inline int iwl_is_ctkill(struct iwl_priv *priv) +{ + return test_bit(STATUS_CT_KILL, &priv->status); +} + +static inline int iwl_is_ready_rf(struct iwl_priv *priv) +{ + if (iwl_is_rfkill(priv)) + return 0; + + return iwl_is_ready(priv); +} + +static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state) +{ + if (state) + set_bit(STATUS_POWER_PMI, &priv->status); + else + clear_bit(STATUS_POWER_PMI, &priv->status); + iwl_trans_set_pmi(priv->trans, state); +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir); +#else +static inline int iwl_dbgfs_register(struct iwl_priv *priv, + struct dentry *dbgfs_dir) +{ + return 0; +} +#endif /* CONFIG_IWLWIFI_DEBUGFS */ + +#ifdef CONFIG_IWLWIFI_DEBUG +#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \ +do { \ + if (!iwl_is_rfkill((m))) \ + IWL_ERR(m, fmt, ##args); \ + else \ + __iwl_err((m)->dev, true, \ + !iwl_have_debug_level(IWL_DL_RADIO), \ + fmt, ##args); \ +} while (0) +#else +#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \ +do { \ + if (!iwl_is_rfkill((m))) \ + IWL_ERR(m, fmt, ##args); \ + else \ + __iwl_err((m)->dev, true, true, fmt, ##args); \ +} while (0) +#endif /* CONFIG_IWLWIFI_DEBUG */ + +extern const char *const iwl_dvm_cmd_strings[REPLY_MAX + 1]; + +static inline const char *iwl_dvm_get_cmd_string(u8 cmd) +{ + const char *s = iwl_dvm_cmd_strings[cmd]; + if (s) + return s; + return "UNKNOWN"; +} +#endif /* __iwl_agn_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/calib.c b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c new file mode 100644 index 000000000000..20e6aa910700 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c @@ -0,0 +1,1113 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include +#include + +#include "iwl-trans.h" + +#include "dev.h" +#include "calib.h" +#include "agn.h" + +/***************************************************************************** + * INIT calibrations framework + *****************************************************************************/ + +/* Opaque calibration results */ +struct iwl_calib_result { + struct list_head list; + size_t cmd_len; + struct iwl_calib_hdr hdr; + /* data follows */ +}; + +struct statistics_general_data { + u32 beacon_silence_rssi_a; + u32 beacon_silence_rssi_b; + u32 beacon_silence_rssi_c; + u32 beacon_energy_a; + u32 beacon_energy_b; + u32 beacon_energy_c; +}; + +int iwl_send_calib_results(struct iwl_priv *priv) +{ + struct iwl_host_cmd hcmd = { + .id = REPLY_PHY_CALIBRATION_CMD, + }; + struct iwl_calib_result *res; + + list_for_each_entry(res, &priv->calib_results, list) { + int ret; + + hcmd.len[0] = res->cmd_len; + hcmd.data[0] = &res->hdr; + hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + ret = iwl_dvm_send_cmd(priv, &hcmd); + if (ret) { + IWL_ERR(priv, "Error %d on calib cmd %d\n", + ret, res->hdr.op_code); + return ret; + } + } + + return 0; +} + +int iwl_calib_set(struct iwl_priv *priv, + const struct iwl_calib_hdr *cmd, int len) +{ + struct iwl_calib_result *res, *tmp; + + res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr), + GFP_ATOMIC); + if (!res) + return -ENOMEM; + memcpy(&res->hdr, cmd, len); + res->cmd_len = len; + + list_for_each_entry(tmp, &priv->calib_results, list) { + if (tmp->hdr.op_code == res->hdr.op_code) { + list_replace(&tmp->list, &res->list); + kfree(tmp); + return 0; + } + } + + /* wasn't in list already */ + list_add_tail(&res->list, &priv->calib_results); + + return 0; +} + +void iwl_calib_free_results(struct iwl_priv *priv) +{ + struct iwl_calib_result *res, *tmp; + + list_for_each_entry_safe(res, tmp, &priv->calib_results, list) { + list_del(&res->list); + kfree(res); + } +} + +/***************************************************************************** + * RUNTIME calibrations framework + *****************************************************************************/ + +/* "false alarms" are signals that our DSP tries to lock onto, + * but then determines that they are either noise, or transmissions + * from a distant wireless network (also "noise", really) that get + * "stepped on" by stronger transmissions within our own network. + * This algorithm attempts to set a sensitivity level that is high + * enough to receive all of our own network traffic, but not so + * high that our DSP gets too busy trying to lock onto non-network + * activity/noise. */ +static int iwl_sens_energy_cck(struct iwl_priv *priv, + u32 norm_fa, + u32 rx_enable_time, + struct statistics_general_data *rx_info) +{ + u32 max_nrg_cck = 0; + int i = 0; + u8 max_silence_rssi = 0; + u32 silence_ref = 0; + u8 silence_rssi_a = 0; + u8 silence_rssi_b = 0; + u8 silence_rssi_c = 0; + u32 val; + + /* "false_alarms" values below are cross-multiplications to assess the + * numbers of false alarms within the measured period of actual Rx + * (Rx is off when we're txing), vs the min/max expected false alarms + * (some should be expected if rx is sensitive enough) in a + * hypothetical listening period of 200 time units (TU), 204.8 msec: + * + * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time + * + * */ + u32 false_alarms = norm_fa * 200 * 1024; + u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; + u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; + struct iwl_sensitivity_data *data = NULL; + const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; + + data = &(priv->sensitivity_data); + + data->nrg_auto_corr_silence_diff = 0; + + /* Find max silence rssi among all 3 receivers. + * This is background noise, which may include transmissions from other + * networks, measured during silence before our network's beacon */ + silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a & + ALL_BAND_FILTER) >> 8); + silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b & + ALL_BAND_FILTER) >> 8); + silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c & + ALL_BAND_FILTER) >> 8); + + val = max(silence_rssi_b, silence_rssi_c); + max_silence_rssi = max(silence_rssi_a, (u8) val); + + /* Store silence rssi in 20-beacon history table */ + data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; + data->nrg_silence_idx++; + if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) + data->nrg_silence_idx = 0; + + /* Find max silence rssi across 20 beacon history */ + for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { + val = data->nrg_silence_rssi[i]; + silence_ref = max(silence_ref, val); + } + IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n", + silence_rssi_a, silence_rssi_b, silence_rssi_c, + silence_ref); + + /* Find max rx energy (min value!) among all 3 receivers, + * measured during beacon frame. + * Save it in 10-beacon history table. */ + i = data->nrg_energy_idx; + val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); + data->nrg_value[i] = min(rx_info->beacon_energy_a, val); + + data->nrg_energy_idx++; + if (data->nrg_energy_idx >= 10) + data->nrg_energy_idx = 0; + + /* Find min rx energy (max value) across 10 beacon history. + * This is the minimum signal level that we want to receive well. + * Add backoff (margin so we don't miss slightly lower energy frames). + * This establishes an upper bound (min value) for energy threshold. */ + max_nrg_cck = data->nrg_value[0]; + for (i = 1; i < 10; i++) + max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); + max_nrg_cck += 6; + + IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", + rx_info->beacon_energy_a, rx_info->beacon_energy_b, + rx_info->beacon_energy_c, max_nrg_cck - 6); + + /* Count number of consecutive beacons with fewer-than-desired + * false alarms. */ + if (false_alarms < min_false_alarms) + data->num_in_cck_no_fa++; + else + data->num_in_cck_no_fa = 0; + IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n", + data->num_in_cck_no_fa); + + /* If we got too many false alarms this time, reduce sensitivity */ + if ((false_alarms > max_false_alarms) && + (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) { + IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n", + false_alarms, max_false_alarms); + IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n"); + data->nrg_curr_state = IWL_FA_TOO_MANY; + /* Store for "fewer than desired" on later beacon */ + data->nrg_silence_ref = silence_ref; + + /* increase energy threshold (reduce nrg value) + * to decrease sensitivity */ + data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; + /* Else if we got fewer than desired, increase sensitivity */ + } else if (false_alarms < min_false_alarms) { + data->nrg_curr_state = IWL_FA_TOO_FEW; + + /* Compare silence level with silence level for most recent + * healthy number or too many false alarms */ + data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref - + (s32)silence_ref; + + IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u, silence diff %d\n", + false_alarms, min_false_alarms, + data->nrg_auto_corr_silence_diff); + + /* Increase value to increase sensitivity, but only if: + * 1a) previous beacon did *not* have *too many* false alarms + * 1b) AND there's a significant difference in Rx levels + * from a previous beacon with too many, or healthy # FAs + * OR 2) We've seen a lot of beacons (100) with too few + * false alarms */ + if ((data->nrg_prev_state != IWL_FA_TOO_MANY) && + ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || + (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { + + IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n"); + /* Increase nrg value to increase sensitivity */ + val = data->nrg_th_cck + NRG_STEP_CCK; + data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val); + } else { + IWL_DEBUG_CALIB(priv, "... but not changing sensitivity\n"); + } + + /* Else we got a healthy number of false alarms, keep status quo */ + } else { + IWL_DEBUG_CALIB(priv, " FA in safe zone\n"); + data->nrg_curr_state = IWL_FA_GOOD_RANGE; + + /* Store for use in "fewer than desired" with later beacon */ + data->nrg_silence_ref = silence_ref; + + /* If previous beacon had too many false alarms, + * give it some extra margin by reducing sensitivity again + * (but don't go below measured energy of desired Rx) */ + if (IWL_FA_TOO_MANY == data->nrg_prev_state) { + IWL_DEBUG_CALIB(priv, "... increasing margin\n"); + if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) + data->nrg_th_cck -= NRG_MARGIN; + else + data->nrg_th_cck = max_nrg_cck; + } + } + + /* Make sure the energy threshold does not go above the measured + * energy of the desired Rx signals (reduced by backoff margin), + * or else we might start missing Rx frames. + * Lower value is higher energy, so we use max()! + */ + data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); + IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck); + + data->nrg_prev_state = data->nrg_curr_state; + + /* Auto-correlation CCK algorithm */ + if (false_alarms > min_false_alarms) { + + /* increase auto_corr values to decrease sensitivity + * so the DSP won't be disturbed by the noise + */ + if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) + data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; + else { + val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; + data->auto_corr_cck = + min((u32)ranges->auto_corr_max_cck, val); + } + val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; + data->auto_corr_cck_mrc = + min((u32)ranges->auto_corr_max_cck_mrc, val); + } else if ((false_alarms < min_false_alarms) && + ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || + (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { + + /* Decrease auto_corr values to increase sensitivity */ + val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; + data->auto_corr_cck = + max((u32)ranges->auto_corr_min_cck, val); + val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; + data->auto_corr_cck_mrc = + max((u32)ranges->auto_corr_min_cck_mrc, val); + } + + return 0; +} + + +static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv, + u32 norm_fa, + u32 rx_enable_time) +{ + u32 val; + u32 false_alarms = norm_fa * 200 * 1024; + u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; + u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; + struct iwl_sensitivity_data *data = NULL; + const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; + + data = &(priv->sensitivity_data); + + /* If we got too many false alarms this time, reduce sensitivity */ + if (false_alarms > max_false_alarms) { + + IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n", + false_alarms, max_false_alarms); + + val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm = + min((u32)ranges->auto_corr_max_ofdm, val); + + val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc = + min((u32)ranges->auto_corr_max_ofdm_mrc, val); + + val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_x1 = + min((u32)ranges->auto_corr_max_ofdm_x1, val); + + val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc_x1 = + min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val); + } + + /* Else if we got fewer than desired, increase sensitivity */ + else if (false_alarms < min_false_alarms) { + + IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n", + false_alarms, min_false_alarms); + + val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm = + max((u32)ranges->auto_corr_min_ofdm, val); + + val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc = + max((u32)ranges->auto_corr_min_ofdm_mrc, val); + + val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_x1 = + max((u32)ranges->auto_corr_min_ofdm_x1, val); + + val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc_x1 = + max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val); + } else { + IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n", + min_false_alarms, false_alarms, max_false_alarms); + } + return 0; +} + +static void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv, + struct iwl_sensitivity_data *data, + __le16 *tbl) +{ + tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm); + tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm_mrc); + tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm_x1); + tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1); + + tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] = + cpu_to_le16((u16)data->auto_corr_cck); + tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16((u16)data->auto_corr_cck_mrc); + + tbl[HD_MIN_ENERGY_CCK_DET_INDEX] = + cpu_to_le16((u16)data->nrg_th_cck); + tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] = + cpu_to_le16((u16)data->nrg_th_ofdm); + + tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] = + cpu_to_le16(data->barker_corr_th_min); + tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16(data->barker_corr_th_min_mrc); + tbl[HD_OFDM_ENERGY_TH_IN_INDEX] = + cpu_to_le16(data->nrg_th_cca); + + IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", + data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, + data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, + data->nrg_th_ofdm); + + IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n", + data->auto_corr_cck, data->auto_corr_cck_mrc, + data->nrg_th_cck); +} + +/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ +static int iwl_sensitivity_write(struct iwl_priv *priv) +{ + struct iwl_sensitivity_cmd cmd; + struct iwl_sensitivity_data *data = NULL; + struct iwl_host_cmd cmd_out = { + .id = SENSITIVITY_CMD, + .len = { sizeof(struct iwl_sensitivity_cmd), }, + .flags = CMD_ASYNC, + .data = { &cmd, }, + }; + + data = &(priv->sensitivity_data); + + memset(&cmd, 0, sizeof(cmd)); + + iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]); + + /* Update uCode's "work" table, and copy it to DSP */ + cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; + + /* Don't send command to uCode if nothing has changed */ + if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]), + sizeof(u16)*HD_TABLE_SIZE)) { + IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); + return 0; + } + + /* Copy table for comparison next time */ + memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]), + sizeof(u16)*HD_TABLE_SIZE); + + return iwl_dvm_send_cmd(priv, &cmd_out); +} + +/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ +static int iwl_enhance_sensitivity_write(struct iwl_priv *priv) +{ + struct iwl_enhance_sensitivity_cmd cmd; + struct iwl_sensitivity_data *data = NULL; + struct iwl_host_cmd cmd_out = { + .id = SENSITIVITY_CMD, + .len = { sizeof(struct iwl_enhance_sensitivity_cmd), }, + .flags = CMD_ASYNC, + .data = { &cmd, }, + }; + + data = &(priv->sensitivity_data); + + memset(&cmd, 0, sizeof(cmd)); + + iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]); + + if (priv->lib->hd_v2) { + cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = + HD_INA_NON_SQUARE_DET_OFDM_DATA_V2; + cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = + HD_INA_NON_SQUARE_DET_CCK_DATA_V2; + cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = + HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = + HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = + HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = + HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = + HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = + HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = + HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = + HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = + HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2; + } else { + cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = + HD_INA_NON_SQUARE_DET_OFDM_DATA_V1; + cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = + HD_INA_NON_SQUARE_DET_CCK_DATA_V1; + cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = + HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = + HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = + HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = + HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = + HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = + HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = + HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = + HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = + HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1; + } + + /* Update uCode's "work" table, and copy it to DSP */ + cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; + + /* Don't send command to uCode if nothing has changed */ + if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]), + sizeof(u16)*HD_TABLE_SIZE) && + !memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX], + &(priv->enhance_sensitivity_tbl[0]), + sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) { + IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); + return 0; + } + + /* Copy table for comparison next time */ + memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]), + sizeof(u16)*HD_TABLE_SIZE); + memcpy(&(priv->enhance_sensitivity_tbl[0]), + &(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]), + sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES); + + return iwl_dvm_send_cmd(priv, &cmd_out); +} + +void iwl_init_sensitivity(struct iwl_priv *priv) +{ + int ret = 0; + int i; + struct iwl_sensitivity_data *data = NULL; + const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; + + if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) + return; + + IWL_DEBUG_CALIB(priv, "Start iwl_init_sensitivity\n"); + + /* Clear driver's sensitivity algo data */ + data = &(priv->sensitivity_data); + + if (ranges == NULL) + return; + + memset(data, 0, sizeof(struct iwl_sensitivity_data)); + + data->num_in_cck_no_fa = 0; + data->nrg_curr_state = IWL_FA_TOO_MANY; + data->nrg_prev_state = IWL_FA_TOO_MANY; + data->nrg_silence_ref = 0; + data->nrg_silence_idx = 0; + data->nrg_energy_idx = 0; + + for (i = 0; i < 10; i++) + data->nrg_value[i] = 0; + + for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) + data->nrg_silence_rssi[i] = 0; + + data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; + data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; + data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; + data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; + data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; + data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; + data->nrg_th_cck = ranges->nrg_th_cck; + data->nrg_th_ofdm = ranges->nrg_th_ofdm; + data->barker_corr_th_min = ranges->barker_corr_th_min; + data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; + data->nrg_th_cca = ranges->nrg_th_cca; + + data->last_bad_plcp_cnt_ofdm = 0; + data->last_fa_cnt_ofdm = 0; + data->last_bad_plcp_cnt_cck = 0; + data->last_fa_cnt_cck = 0; + + if (priv->fw->enhance_sensitivity_table) + ret |= iwl_enhance_sensitivity_write(priv); + else + ret |= iwl_sensitivity_write(priv); + IWL_DEBUG_CALIB(priv, "<calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) + return; + + data = &(priv->sensitivity_data); + + if (!iwl_is_any_associated(priv)) { + IWL_DEBUG_CALIB(priv, "<< - not associated\n"); + return; + } + + spin_lock_bh(&priv->statistics.lock); + rx_info = &priv->statistics.rx_non_phy; + ofdm = &priv->statistics.rx_ofdm; + cck = &priv->statistics.rx_cck; + if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { + IWL_DEBUG_CALIB(priv, "<< invalid data.\n"); + spin_unlock_bh(&priv->statistics.lock); + return; + } + + /* Extract Statistics: */ + rx_enable_time = le32_to_cpu(rx_info->channel_load); + fa_cck = le32_to_cpu(cck->false_alarm_cnt); + fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); + bad_plcp_cck = le32_to_cpu(cck->plcp_err); + bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); + + statis.beacon_silence_rssi_a = + le32_to_cpu(rx_info->beacon_silence_rssi_a); + statis.beacon_silence_rssi_b = + le32_to_cpu(rx_info->beacon_silence_rssi_b); + statis.beacon_silence_rssi_c = + le32_to_cpu(rx_info->beacon_silence_rssi_c); + statis.beacon_energy_a = + le32_to_cpu(rx_info->beacon_energy_a); + statis.beacon_energy_b = + le32_to_cpu(rx_info->beacon_energy_b); + statis.beacon_energy_c = + le32_to_cpu(rx_info->beacon_energy_c); + + spin_unlock_bh(&priv->statistics.lock); + + IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time); + + if (!rx_enable_time) { + IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n"); + return; + } + + /* These statistics increase monotonically, and do not reset + * at each beacon. Calculate difference from last value, or just + * use the new statistics value if it has reset or wrapped around. */ + if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) + data->last_bad_plcp_cnt_cck = bad_plcp_cck; + else { + bad_plcp_cck -= data->last_bad_plcp_cnt_cck; + data->last_bad_plcp_cnt_cck += bad_plcp_cck; + } + + if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) + data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; + else { + bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; + data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; + } + + if (data->last_fa_cnt_ofdm > fa_ofdm) + data->last_fa_cnt_ofdm = fa_ofdm; + else { + fa_ofdm -= data->last_fa_cnt_ofdm; + data->last_fa_cnt_ofdm += fa_ofdm; + } + + if (data->last_fa_cnt_cck > fa_cck) + data->last_fa_cnt_cck = fa_cck; + else { + fa_cck -= data->last_fa_cnt_cck; + data->last_fa_cnt_cck += fa_cck; + } + + /* Total aborted signal locks */ + norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; + norm_fa_cck = fa_cck + bad_plcp_cck; + + IWL_DEBUG_CALIB(priv, "cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, + bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); + + iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time); + iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis); + if (priv->fw->enhance_sensitivity_table) + iwl_enhance_sensitivity_write(priv); + else + iwl_sensitivity_write(priv); +} + +static inline u8 find_first_chain(u8 mask) +{ + if (mask & ANT_A) + return CHAIN_A; + if (mask & ANT_B) + return CHAIN_B; + return CHAIN_C; +} + +/** + * Run disconnected antenna algorithm to find out which antennas are + * disconnected. + */ +static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, + struct iwl_chain_noise_data *data) +{ + u32 active_chains = 0; + u32 max_average_sig; + u16 max_average_sig_antenna_i; + u8 num_tx_chains; + u8 first_chain; + u16 i = 0; + + average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS; + average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS; + average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS; + + if (average_sig[0] >= average_sig[1]) { + max_average_sig = average_sig[0]; + max_average_sig_antenna_i = 0; + active_chains = (1 << max_average_sig_antenna_i); + } else { + max_average_sig = average_sig[1]; + max_average_sig_antenna_i = 1; + active_chains = (1 << max_average_sig_antenna_i); + } + + if (average_sig[2] >= max_average_sig) { + max_average_sig = average_sig[2]; + max_average_sig_antenna_i = 2; + active_chains = (1 << max_average_sig_antenna_i); + } + + IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n", + average_sig[0], average_sig[1], average_sig[2]); + IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n", + max_average_sig, max_average_sig_antenna_i); + + /* Compare signal strengths for all 3 receivers. */ + for (i = 0; i < NUM_RX_CHAINS; i++) { + if (i != max_average_sig_antenna_i) { + s32 rssi_delta = (max_average_sig - average_sig[i]); + + /* If signal is very weak, compared with + * strongest, mark it as disconnected. */ + if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) + data->disconn_array[i] = 1; + else + active_chains |= (1 << i); + IWL_DEBUG_CALIB(priv, "i = %d rssiDelta = %d " + "disconn_array[i] = %d\n", + i, rssi_delta, data->disconn_array[i]); + } + } + + /* + * The above algorithm sometimes fails when the ucode + * reports 0 for all chains. It's not clear why that + * happens to start with, but it is then causing trouble + * because this can make us enable more chains than the + * hardware really has. + * + * To be safe, simply mask out any chains that we know + * are not on the device. + */ + active_chains &= priv->nvm_data->valid_rx_ant; + + num_tx_chains = 0; + for (i = 0; i < NUM_RX_CHAINS; i++) { + /* loops on all the bits of + * priv->hw_setting.valid_tx_ant */ + u8 ant_msk = (1 << i); + if (!(priv->nvm_data->valid_tx_ant & ant_msk)) + continue; + + num_tx_chains++; + if (data->disconn_array[i] == 0) + /* there is a Tx antenna connected */ + break; + if (num_tx_chains == priv->hw_params.tx_chains_num && + data->disconn_array[i]) { + /* + * If all chains are disconnected + * connect the first valid tx chain + */ + first_chain = + find_first_chain(priv->nvm_data->valid_tx_ant); + data->disconn_array[first_chain] = 0; + active_chains |= BIT(first_chain); + IWL_DEBUG_CALIB(priv, + "All Tx chains are disconnected W/A - declare %d as connected\n", + first_chain); + break; + } + } + + if (active_chains != priv->nvm_data->valid_rx_ant && + active_chains != priv->chain_noise_data.active_chains) + IWL_DEBUG_CALIB(priv, + "Detected that not all antennas are connected! " + "Connected: %#x, valid: %#x.\n", + active_chains, + priv->nvm_data->valid_rx_ant); + + /* Save for use within RXON, TX, SCAN commands, etc. */ + data->active_chains = active_chains; + IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n", + active_chains); +} + +static void iwlagn_gain_computation(struct iwl_priv *priv, + u32 average_noise[NUM_RX_CHAINS], + u8 default_chain) +{ + int i; + s32 delta_g; + struct iwl_chain_noise_data *data = &priv->chain_noise_data; + + /* + * Find Gain Code for the chains based on "default chain" + */ + for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) { + if ((data->disconn_array[i])) { + data->delta_gain_code[i] = 0; + continue; + } + + delta_g = (priv->lib->chain_noise_scale * + ((s32)average_noise[default_chain] - + (s32)average_noise[i])) / 1500; + + /* bound gain by 2 bits value max, 3rd bit is sign */ + data->delta_gain_code[i] = + min(abs(delta_g), + (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); + + if (delta_g < 0) + /* + * set negative sign ... + * note to Intel developers: This is uCode API format, + * not the format of any internal device registers. + * Do not change this format for e.g. 6050 or similar + * devices. Change format only if more resolution + * (i.e. more than 2 bits magnitude) is needed. + */ + data->delta_gain_code[i] |= (1 << 2); + } + + IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d ANT_C = %d\n", + data->delta_gain_code[1], data->delta_gain_code[2]); + + if (!data->radio_write) { + struct iwl_calib_chain_noise_gain_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + + iwl_set_calib_hdr(&cmd.hdr, + priv->phy_calib_chain_noise_gain_cmd); + cmd.delta_gain_1 = data->delta_gain_code[1]; + cmd.delta_gain_2 = data->delta_gain_code[2]; + iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, + CMD_ASYNC, sizeof(cmd), &cmd); + + data->radio_write = 1; + data->state = IWL_CHAIN_NOISE_CALIBRATED; + } +} + +/* + * Accumulate 16 beacons of signal and noise statistics for each of + * 3 receivers/antennas/rx-chains, then figure out: + * 1) Which antennas are connected. + * 2) Differential rx gain settings to balance the 3 receivers. + */ +void iwl_chain_noise_calibration(struct iwl_priv *priv) +{ + struct iwl_chain_noise_data *data = NULL; + + u32 chain_noise_a; + u32 chain_noise_b; + u32 chain_noise_c; + u32 chain_sig_a; + u32 chain_sig_b; + u32 chain_sig_c; + u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; + u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; + u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; + u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; + u16 i = 0; + u16 rxon_chnum = INITIALIZATION_VALUE; + u16 stat_chnum = INITIALIZATION_VALUE; + u8 rxon_band24; + u8 stat_band24; + struct statistics_rx_non_phy *rx_info; + + /* + * MULTI-FIXME: + * When we support multiple interfaces on different channels, + * this must be modified/fixed. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED) + return; + + data = &(priv->chain_noise_data); + + /* + * Accumulate just the first "chain_noise_num_beacons" after + * the first association, then we're done forever. + */ + if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) { + if (data->state == IWL_CHAIN_NOISE_ALIVE) + IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n"); + return; + } + + spin_lock_bh(&priv->statistics.lock); + + rx_info = &priv->statistics.rx_non_phy; + + if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { + IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n"); + spin_unlock_bh(&priv->statistics.lock); + return; + } + + rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK); + rxon_chnum = le16_to_cpu(ctx->staging.channel); + stat_band24 = + !!(priv->statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK); + stat_chnum = le32_to_cpu(priv->statistics.flag) >> 16; + + /* Make sure we accumulate data for just the associated channel + * (even if scanning). */ + if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) { + IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n", + rxon_chnum, rxon_band24); + spin_unlock_bh(&priv->statistics.lock); + return; + } + + /* + * Accumulate beacon statistics values across + * "chain_noise_num_beacons" + */ + chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & + IN_BAND_FILTER; + chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & + IN_BAND_FILTER; + chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & + IN_BAND_FILTER; + + chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; + chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; + chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; + + spin_unlock_bh(&priv->statistics.lock); + + data->beacon_count++; + + data->chain_noise_a = (chain_noise_a + data->chain_noise_a); + data->chain_noise_b = (chain_noise_b + data->chain_noise_b); + data->chain_noise_c = (chain_noise_c + data->chain_noise_c); + + data->chain_signal_a = (chain_sig_a + data->chain_signal_a); + data->chain_signal_b = (chain_sig_b + data->chain_signal_b); + data->chain_signal_c = (chain_sig_c + data->chain_signal_c); + + IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n", + rxon_chnum, rxon_band24, data->beacon_count); + IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n", + chain_sig_a, chain_sig_b, chain_sig_c); + IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n", + chain_noise_a, chain_noise_b, chain_noise_c); + + /* If this is the "chain_noise_num_beacons", determine: + * 1) Disconnected antennas (using signal strengths) + * 2) Differential gain (using silence noise) to balance receivers */ + if (data->beacon_count != IWL_CAL_NUM_BEACONS) + return; + + /* Analyze signal for disconnected antenna */ + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { + /* Disable disconnected antenna algorithm for advanced + bt coex, assuming valid antennas are connected */ + data->active_chains = priv->nvm_data->valid_rx_ant; + for (i = 0; i < NUM_RX_CHAINS; i++) + if (!(data->active_chains & (1<disconn_array[i] = 1; + } else + iwl_find_disconn_antenna(priv, average_sig, data); + + /* Analyze noise for rx balance */ + average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS; + average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS; + average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS; + + for (i = 0; i < NUM_RX_CHAINS; i++) { + if (!(data->disconn_array[i]) && + (average_noise[i] <= min_average_noise)) { + /* This means that chain i is active and has + * lower noise values so far: */ + min_average_noise = average_noise[i]; + min_average_noise_antenna_i = i; + } + } + + IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n", + average_noise[0], average_noise[1], + average_noise[2]); + + IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n", + min_average_noise, min_average_noise_antenna_i); + + iwlagn_gain_computation( + priv, average_noise, + find_first_chain(priv->nvm_data->valid_rx_ant)); + + /* Some power changes may have been made during the calibration. + * Update and commit the RXON + */ + iwl_update_chain_flags(priv); + + data->state = IWL_CHAIN_NOISE_DONE; + iwl_power_update_mode(priv, false); +} + +void iwl_reset_run_time_calib(struct iwl_priv *priv) +{ + int i; + memset(&(priv->sensitivity_data), 0, + sizeof(struct iwl_sensitivity_data)); + memset(&(priv->chain_noise_data), 0, + sizeof(struct iwl_chain_noise_data)); + for (i = 0; i < NUM_RX_CHAINS; i++) + priv->chain_noise_data.delta_gain_code[i] = + CHAIN_NOISE_DELTA_GAIN_INIT_VAL; + + /* Ask for statistics now, the uCode will send notification + * periodically after association */ + iwl_send_statistics_request(priv, CMD_ASYNC, true); +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/calib.h b/drivers/net/wireless/intel/iwlwifi/dvm/calib.h new file mode 100644 index 000000000000..aeae4e80ea40 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/calib.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifndef __iwl_calib_h__ +#define __iwl_calib_h__ + +#include "dev.h" +#include "commands.h" + +void iwl_chain_noise_calibration(struct iwl_priv *priv); +void iwl_sensitivity_calibration(struct iwl_priv *priv); + +void iwl_init_sensitivity(struct iwl_priv *priv); +void iwl_reset_run_time_calib(struct iwl_priv *priv); + +#endif /* __iwl_calib_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h new file mode 100644 index 000000000000..7a34e4d158d1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h @@ -0,0 +1,4008 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +/* + * Please use this file (commands.h) only for uCode API definitions. + * Please use iwl-xxxx-hw.h for hardware-related definitions. + * Please use dev.h for driver implementation definitions. + */ + +#ifndef __iwl_commands_h__ +#define __iwl_commands_h__ + +#include +#include + + +enum { + REPLY_ALIVE = 0x1, + REPLY_ERROR = 0x2, + REPLY_ECHO = 0x3, /* test command */ + + /* RXON and QOS commands */ + REPLY_RXON = 0x10, + REPLY_RXON_ASSOC = 0x11, + REPLY_QOS_PARAM = 0x13, + REPLY_RXON_TIMING = 0x14, + + /* Multi-Station support */ + REPLY_ADD_STA = 0x18, + REPLY_REMOVE_STA = 0x19, + REPLY_REMOVE_ALL_STA = 0x1a, /* not used */ + REPLY_TXFIFO_FLUSH = 0x1e, + + /* Security */ + REPLY_WEPKEY = 0x20, + + /* RX, TX, LEDs */ + REPLY_TX = 0x1c, + REPLY_LEDS_CMD = 0x48, + REPLY_TX_LINK_QUALITY_CMD = 0x4e, + + /* WiMAX coexistence */ + COEX_PRIORITY_TABLE_CMD = 0x5a, + COEX_MEDIUM_NOTIFICATION = 0x5b, + COEX_EVENT_CMD = 0x5c, + + /* Calibration */ + TEMPERATURE_NOTIFICATION = 0x62, + CALIBRATION_CFG_CMD = 0x65, + CALIBRATION_RES_NOTIFICATION = 0x66, + CALIBRATION_COMPLETE_NOTIFICATION = 0x67, + + /* 802.11h related */ + REPLY_QUIET_CMD = 0x71, /* not used */ + REPLY_CHANNEL_SWITCH = 0x72, + CHANNEL_SWITCH_NOTIFICATION = 0x73, + REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74, + SPECTRUM_MEASURE_NOTIFICATION = 0x75, + + /* Power Management */ + POWER_TABLE_CMD = 0x77, + PM_SLEEP_NOTIFICATION = 0x7A, + PM_DEBUG_STATISTIC_NOTIFIC = 0x7B, + + /* Scan commands and notifications */ + REPLY_SCAN_CMD = 0x80, + REPLY_SCAN_ABORT_CMD = 0x81, + SCAN_START_NOTIFICATION = 0x82, + SCAN_RESULTS_NOTIFICATION = 0x83, + SCAN_COMPLETE_NOTIFICATION = 0x84, + + /* IBSS/AP commands */ + BEACON_NOTIFICATION = 0x90, + REPLY_TX_BEACON = 0x91, + WHO_IS_AWAKE_NOTIFICATION = 0x94, /* not used */ + + /* Miscellaneous commands */ + REPLY_TX_POWER_DBM_CMD = 0x95, + QUIET_NOTIFICATION = 0x96, /* not used */ + REPLY_TX_PWR_TABLE_CMD = 0x97, + REPLY_TX_POWER_DBM_CMD_V1 = 0x98, /* old version of API */ + TX_ANT_CONFIGURATION_CMD = 0x98, + MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */ + + /* Bluetooth device coexistence config command */ + REPLY_BT_CONFIG = 0x9b, + + /* Statistics */ + REPLY_STATISTICS_CMD = 0x9c, + STATISTICS_NOTIFICATION = 0x9d, + + /* RF-KILL commands and notifications */ + REPLY_CARD_STATE_CMD = 0xa0, + CARD_STATE_NOTIFICATION = 0xa1, + + /* Missed beacons notification */ + MISSED_BEACONS_NOTIFICATION = 0xa2, + + REPLY_CT_KILL_CONFIG_CMD = 0xa4, + SENSITIVITY_CMD = 0xa8, + REPLY_PHY_CALIBRATION_CMD = 0xb0, + REPLY_RX_PHY_CMD = 0xc0, + REPLY_RX_MPDU_CMD = 0xc1, + REPLY_RX = 0xc3, + REPLY_COMPRESSED_BA = 0xc5, + + /* BT Coex */ + REPLY_BT_COEX_PRIO_TABLE = 0xcc, + REPLY_BT_COEX_PROT_ENV = 0xcd, + REPLY_BT_COEX_PROFILE_NOTIF = 0xce, + + /* PAN commands */ + REPLY_WIPAN_PARAMS = 0xb2, + REPLY_WIPAN_RXON = 0xb3, /* use REPLY_RXON structure */ + REPLY_WIPAN_RXON_TIMING = 0xb4, /* use REPLY_RXON_TIMING structure */ + REPLY_WIPAN_RXON_ASSOC = 0xb6, /* use REPLY_RXON_ASSOC structure */ + REPLY_WIPAN_QOS_PARAM = 0xb7, /* use REPLY_QOS_PARAM structure */ + REPLY_WIPAN_WEPKEY = 0xb8, /* use REPLY_WEPKEY structure */ + REPLY_WIPAN_P2P_CHANNEL_SWITCH = 0xb9, + REPLY_WIPAN_NOA_NOTIFICATION = 0xbc, + REPLY_WIPAN_DEACTIVATION_COMPLETE = 0xbd, + + REPLY_WOWLAN_PATTERNS = 0xe0, + REPLY_WOWLAN_WAKEUP_FILTER = 0xe1, + REPLY_WOWLAN_TSC_RSC_PARAMS = 0xe2, + REPLY_WOWLAN_TKIP_PARAMS = 0xe3, + REPLY_WOWLAN_KEK_KCK_MATERIAL = 0xe4, + REPLY_WOWLAN_GET_STATUS = 0xe5, + REPLY_D3_CONFIG = 0xd3, + + REPLY_MAX = 0xff +}; + +/* + * Minimum number of queues. MAX_NUM is defined in hw specific files. + * Set the minimum to accommodate + * - 4 standard TX queues + * - the command queue + * - 4 PAN TX queues + * - the PAN multicast queue, and + * - the AUX (TX during scan dwell) queue. + */ +#define IWL_MIN_NUM_QUEUES 11 + +/* + * Command queue depends on iPAN support. + */ +#define IWL_DEFAULT_CMD_QUEUE_NUM 4 +#define IWL_IPAN_CMD_QUEUE_NUM 9 + +#define IWL_TX_FIFO_BK 0 /* shared */ +#define IWL_TX_FIFO_BE 1 +#define IWL_TX_FIFO_VI 2 /* shared */ +#define IWL_TX_FIFO_VO 3 +#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK +#define IWL_TX_FIFO_BE_IPAN 4 +#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI +#define IWL_TX_FIFO_VO_IPAN 5 +/* re-uses the VO FIFO, uCode will properly flush/schedule */ +#define IWL_TX_FIFO_AUX 5 +#define IWL_TX_FIFO_UNUSED 255 + +#define IWLAGN_CMD_FIFO_NUM 7 + +/* + * This queue number is required for proper operation + * because the ucode will stop/start the scheduler as + * required. + */ +#define IWL_IPAN_MCAST_QUEUE 8 + +/****************************************************************************** + * (0) + * Commonly used structures and definitions: + * Command header, rate_n_flags, txpower + * + *****************************************************************************/ + +/** + * iwlagn rate_n_flags bit fields + * + * rate_n_flags format is used in following iwlagn commands: + * REPLY_RX (response only) + * REPLY_RX_MPDU (response only) + * REPLY_TX (both command and response) + * REPLY_TX_LINK_QUALITY_CMD + * + * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"): + * 2-0: 0) 6 Mbps + * 1) 12 Mbps + * 2) 18 Mbps + * 3) 24 Mbps + * 4) 36 Mbps + * 5) 48 Mbps + * 6) 54 Mbps + * 7) 60 Mbps + * + * 4-3: 0) Single stream (SISO) + * 1) Dual stream (MIMO) + * 2) Triple stream (MIMO) + * + * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data + * + * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): + * 3-0: 0xD) 6 Mbps + * 0xF) 9 Mbps + * 0x5) 12 Mbps + * 0x7) 18 Mbps + * 0x9) 24 Mbps + * 0xB) 36 Mbps + * 0x1) 48 Mbps + * 0x3) 54 Mbps + * + * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"): + * 6-0: 10) 1 Mbps + * 20) 2 Mbps + * 55) 5.5 Mbps + * 110) 11 Mbps + */ +#define RATE_MCS_CODE_MSK 0x7 +#define RATE_MCS_SPATIAL_POS 3 +#define RATE_MCS_SPATIAL_MSK 0x18 +#define RATE_MCS_HT_DUP_POS 5 +#define RATE_MCS_HT_DUP_MSK 0x20 +/* Both legacy and HT use bits 7:0 as the CCK/OFDM rate or HT MCS */ +#define RATE_MCS_RATE_MSK 0xff + +/* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */ +#define RATE_MCS_FLAGS_POS 8 +#define RATE_MCS_HT_POS 8 +#define RATE_MCS_HT_MSK 0x100 + +/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ +#define RATE_MCS_CCK_POS 9 +#define RATE_MCS_CCK_MSK 0x200 + +/* Bit 10: (1) Use Green Field preamble */ +#define RATE_MCS_GF_POS 10 +#define RATE_MCS_GF_MSK 0x400 + +/* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */ +#define RATE_MCS_HT40_POS 11 +#define RATE_MCS_HT40_MSK 0x800 + +/* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */ +#define RATE_MCS_DUP_POS 12 +#define RATE_MCS_DUP_MSK 0x1000 + +/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ +#define RATE_MCS_SGI_POS 13 +#define RATE_MCS_SGI_MSK 0x2000 + +/** + * rate_n_flags Tx antenna masks + * 4965 has 2 transmitters + * 5100 has 1 transmitter B + * 5150 has 1 transmitter A + * 5300 has 3 transmitters + * 5350 has 3 transmitters + * bit14:16 + */ +#define RATE_MCS_ANT_POS 14 +#define RATE_MCS_ANT_A_MSK 0x04000 +#define RATE_MCS_ANT_B_MSK 0x08000 +#define RATE_MCS_ANT_C_MSK 0x10000 +#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | RATE_MCS_ANT_B_MSK) +#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | RATE_MCS_ANT_C_MSK) +#define RATE_ANT_NUM 3 + +#define POWER_TABLE_NUM_ENTRIES 33 +#define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32 +#define POWER_TABLE_CCK_ENTRY 32 + +#define IWL_PWR_NUM_HT_OFDM_ENTRIES 24 +#define IWL_PWR_CCK_ENTRIES 2 + +/** + * struct tx_power_dual_stream + * + * Table entries in REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH + * + * Same format as iwl_tx_power_dual_stream, but __le32 + */ +struct tx_power_dual_stream { + __le32 dw; +} __packed; + +/** + * Command REPLY_TX_POWER_DBM_CMD = 0x98 + * struct iwlagn_tx_power_dbm_cmd + */ +#define IWLAGN_TX_POWER_AUTO 0x7f +#define IWLAGN_TX_POWER_NO_CLOSED (0x1 << 6) + +struct iwlagn_tx_power_dbm_cmd { + s8 global_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */ + u8 flags; + s8 srv_chan_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */ + u8 reserved; +} __packed; + +/** + * Command TX_ANT_CONFIGURATION_CMD = 0x98 + * This command is used to configure valid Tx antenna. + * By default uCode concludes the valid antenna according to the radio flavor. + * This command enables the driver to override/modify this conclusion. + */ +struct iwl_tx_ant_config_cmd { + __le32 valid; +} __packed; + +/****************************************************************************** + * (0a) + * Alive and Error Commands & Responses: + * + *****************************************************************************/ + +#define UCODE_VALID_OK cpu_to_le32(0x1) + +/** + * REPLY_ALIVE = 0x1 (response only, not a command) + * + * uCode issues this "alive" notification once the runtime image is ready + * to receive commands from the driver. This is the *second* "alive" + * notification that the driver will receive after rebooting uCode; + * this "alive" is indicated by subtype field != 9. + * + * See comments documenting "BSM" (bootstrap state machine). + * + * This response includes two pointers to structures within the device's + * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging: + * + * 1) log_event_table_ptr indicates base of the event log. This traces + * a 256-entry history of uCode execution within a circular buffer. + * Its header format is: + * + * __le32 log_size; log capacity (in number of entries) + * __le32 type; (1) timestamp with each entry, (0) no timestamp + * __le32 wraps; # times uCode has wrapped to top of circular buffer + * __le32 write_index; next circular buffer entry that uCode would fill + * + * The header is followed by the circular buffer of log entries. Entries + * with timestamps have the following format: + * + * __le32 event_id; range 0 - 1500 + * __le32 timestamp; low 32 bits of TSF (of network, if associated) + * __le32 data; event_id-specific data value + * + * Entries without timestamps contain only event_id and data. + * + * + * 2) error_event_table_ptr indicates base of the error log. This contains + * information about any uCode error that occurs. For agn, the format + * of the error log is defined by struct iwl_error_event_table. + * + * The Linux driver can print both logs to the system log when a uCode error + * occurs. + */ + +/* + * Note: This structure is read from the device with IO accesses, + * and the reading already does the endian conversion. As it is + * read with u32-sized accesses, any members with a different size + * need to be ordered correctly though! + */ +struct iwl_error_event_table { + u32 valid; /* (nonzero) valid, (0) log is empty */ + u32 error_id; /* type of error */ + u32 pc; /* program counter */ + u32 blink1; /* branch link */ + u32 blink2; /* branch link */ + u32 ilink1; /* interrupt link */ + u32 ilink2; /* interrupt link */ + u32 data1; /* error-specific data */ + u32 data2; /* error-specific data */ + u32 line; /* source code line of error */ + u32 bcon_time; /* beacon timer */ + u32 tsf_low; /* network timestamp function timer */ + u32 tsf_hi; /* network timestamp function timer */ + u32 gp1; /* GP1 timer register */ + u32 gp2; /* GP2 timer register */ + u32 gp3; /* GP3 timer register */ + u32 ucode_ver; /* uCode version */ + u32 hw_ver; /* HW Silicon version */ + u32 brd_ver; /* HW board version */ + u32 log_pc; /* log program counter */ + u32 frame_ptr; /* frame pointer */ + u32 stack_ptr; /* stack pointer */ + u32 hcmd; /* last host command header */ + u32 isr0; /* isr status register LMPM_NIC_ISR0: + * rxtx_flag */ + u32 isr1; /* isr status register LMPM_NIC_ISR1: + * host_flag */ + u32 isr2; /* isr status register LMPM_NIC_ISR2: + * enc_flag */ + u32 isr3; /* isr status register LMPM_NIC_ISR3: + * time_flag */ + u32 isr4; /* isr status register LMPM_NIC_ISR4: + * wico interrupt */ + u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ + u32 wait_event; /* wait event() caller address */ + u32 l2p_control; /* L2pControlField */ + u32 l2p_duration; /* L2pDurationField */ + u32 l2p_mhvalid; /* L2pMhValidBits */ + u32 l2p_addr_match; /* L2pAddrMatchStat */ + u32 lmpm_pmg_sel; /* indicate which clocks are turned on + * (LMPM_PMG_SEL) */ + u32 u_timestamp; /* indicate when the date and time of the + * compilation */ + u32 flow_handler; /* FH read/write pointers, RX credit */ +} __packed; + +struct iwl_alive_resp { + u8 ucode_minor; + u8 ucode_major; + __le16 reserved1; + u8 sw_rev[8]; + u8 ver_type; + u8 ver_subtype; /* not "9" for runtime alive */ + __le16 reserved2; + __le32 log_event_table_ptr; /* SRAM address for event log */ + __le32 error_event_table_ptr; /* SRAM address for error log */ + __le32 timestamp; + __le32 is_valid; +} __packed; + +/* + * REPLY_ERROR = 0x2 (response only, not a command) + */ +struct iwl_error_resp { + __le32 error_type; + u8 cmd_id; + u8 reserved1; + __le16 bad_cmd_seq_num; + __le32 error_info; + __le64 timestamp; +} __packed; + +/****************************************************************************** + * (1) + * RXON Commands & Responses: + * + *****************************************************************************/ + +/* + * Rx config defines & structure + */ +/* rx_config device types */ +enum { + RXON_DEV_TYPE_AP = 1, + RXON_DEV_TYPE_ESS = 3, + RXON_DEV_TYPE_IBSS = 4, + RXON_DEV_TYPE_SNIFFER = 6, + RXON_DEV_TYPE_CP = 7, + RXON_DEV_TYPE_2STA = 8, + RXON_DEV_TYPE_P2P = 9, +}; + + +#define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0) +#define RXON_RX_CHAIN_DRIVER_FORCE_POS (0) +#define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1) +#define RXON_RX_CHAIN_VALID_POS (1) +#define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4) +#define RXON_RX_CHAIN_FORCE_SEL_POS (4) +#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7) +#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7) +#define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10) +#define RXON_RX_CHAIN_CNT_POS (10) +#define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12) +#define RXON_RX_CHAIN_MIMO_CNT_POS (12) +#define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14) +#define RXON_RX_CHAIN_MIMO_FORCE_POS (14) + +/* rx_config flags */ +/* band & modulation selection */ +#define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0) +#define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1) +/* auto detection enable */ +#define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2) +/* TGg protection when tx */ +#define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3) +/* cck short slot & preamble */ +#define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4) +#define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5) +/* antenna selection */ +#define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7) +#define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00) +#define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8) +#define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9) +/* radar detection enable */ +#define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12) +#define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13) +/* rx response to host with 8-byte TSF +* (according to ON_AIR deassertion) */ +#define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15) + + +/* HT flags */ +#define RXON_FLG_CTRL_CHANNEL_LOC_POS (22) +#define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22) + +#define RXON_FLG_HT_OPERATING_MODE_POS (23) + +#define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23) +#define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23) + +#define RXON_FLG_CHANNEL_MODE_POS (25) +#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) + +/* channel mode */ +enum { + CHANNEL_MODE_LEGACY = 0, + CHANNEL_MODE_PURE_40 = 1, + CHANNEL_MODE_MIXED = 2, + CHANNEL_MODE_RESERVED = 3, +}; +#define RXON_FLG_CHANNEL_MODE_LEGACY cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS) +#define RXON_FLG_CHANNEL_MODE_PURE_40 cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS) +#define RXON_FLG_CHANNEL_MODE_MIXED cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS) + +/* CTS to self (if spec allows) flag */ +#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30) + +/* rx_config filter flags */ +/* accept all data frames */ +#define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0) +/* pass control & management to host */ +#define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1) +/* accept multi-cast */ +#define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2) +/* don't decrypt uni-cast frames */ +#define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3) +/* don't decrypt multi-cast frames */ +#define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4) +/* STA is associated */ +#define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5) +/* transfer to host non bssid beacons in associated state */ +#define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6) + +/** + * REPLY_RXON = 0x10 (command, has simple generic response) + * + * RXON tunes the radio tuner to a service channel, and sets up a number + * of parameters that are used primarily for Rx, but also for Tx operations. + * + * NOTE: When tuning to a new channel, driver must set the + * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent + * info within the device, including the station tables, tx retry + * rate tables, and txpower tables. Driver must build a new station + * table and txpower table before transmitting anything on the RXON + * channel. + * + * NOTE: All RXONs wipe clean the internal txpower table. Driver must + * issue a new REPLY_TX_PWR_TABLE_CMD after each REPLY_RXON (0x10), + * regardless of whether RXON_FILTER_ASSOC_MSK is set. + */ + +struct iwl_rxon_cmd { + u8 node_addr[6]; + __le16 reserved1; + u8 bssid_addr[6]; + __le16 reserved2; + u8 wlap_bssid_addr[6]; + __le16 reserved3; + u8 dev_type; + u8 air_propagation; + __le16 rx_chain; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 assoc_id; + __le32 flags; + __le32 filter_flags; + __le16 channel; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; + u8 ofdm_ht_triple_stream_basic_rates; + u8 reserved5; + __le16 acquisition_data; + __le16 reserved6; +} __packed; + +/* + * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response) + */ +struct iwl_rxon_assoc_cmd { + __le32 flags; + __le32 filter_flags; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 reserved1; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; + u8 ofdm_ht_triple_stream_basic_rates; + u8 reserved2; + __le16 rx_chain_select_flags; + __le16 acquisition_data; + __le32 reserved3; +} __packed; + +#define IWL_CONN_MAX_LISTEN_INTERVAL 10 +#define IWL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */ + +/* + * REPLY_RXON_TIMING = 0x14 (command, has simple generic response) + */ +struct iwl_rxon_time_cmd { + __le64 timestamp; + __le16 beacon_interval; + __le16 atim_window; + __le32 beacon_init_val; + __le16 listen_interval; + u8 dtim_period; + u8 delta_cp_bss_tbtts; +} __packed; + +/* + * REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response) + */ +/** + * struct iwl5000_channel_switch_cmd + * @band: 0- 5.2GHz, 1- 2.4GHz + * @expect_beacon: 0- resume transmits after channel switch + * 1- wait for beacon to resume transmits + * @channel: new channel number + * @rxon_flags: Rx on flags + * @rxon_filter_flags: filtering parameters + * @switch_time: switch time in extended beacon format + * @reserved: reserved bytes + */ +struct iwl5000_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + __le32 reserved[2][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; +} __packed; + +/** + * struct iwl6000_channel_switch_cmd + * @band: 0- 5.2GHz, 1- 2.4GHz + * @expect_beacon: 0- resume transmits after channel switch + * 1- wait for beacon to resume transmits + * @channel: new channel number + * @rxon_flags: Rx on flags + * @rxon_filter_flags: filtering parameters + * @switch_time: switch time in extended beacon format + * @reserved: reserved bytes + */ +struct iwl6000_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + __le32 reserved[3][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; +} __packed; + +/* + * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command) + */ +struct iwl_csa_notification { + __le16 band; + __le16 channel; + __le32 status; /* 0 - OK, 1 - fail */ +} __packed; + +/****************************************************************************** + * (2) + * Quality-of-Service (QOS) Commands & Responses: + * + *****************************************************************************/ + +/** + * struct iwl_ac_qos -- QOS timing params for REPLY_QOS_PARAM + * One for each of 4 EDCA access categories in struct iwl_qosparam_cmd + * + * @cw_min: Contention window, start value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x0f. + * @cw_max: Contention window, max value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x3f. + * @aifsn: Number of slots in Arbitration Interframe Space (before + * performing random backoff timing prior to Tx). Device default 1. + * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. + * + * Device will automatically increase contention window by (2*CW) + 1 for each + * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW + * value, to cap the CW value. + */ +struct iwl_ac_qos { + __le16 cw_min; + __le16 cw_max; + u8 aifsn; + u8 reserved1; + __le16 edca_txop; +} __packed; + +/* QoS flags defines */ +#define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01) +#define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02) +#define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10) + +/* Number of Access Categories (AC) (EDCA), queues 0..3 */ +#define AC_NUM 4 + +/* + * REPLY_QOS_PARAM = 0x13 (command, has simple generic response) + * + * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs + * 0: Background, 1: Best Effort, 2: Video, 3: Voice. + */ +struct iwl_qosparam_cmd { + __le32 qos_flags; + struct iwl_ac_qos ac[AC_NUM]; +} __packed; + +/****************************************************************************** + * (3) + * Add/Modify Stations Commands & Responses: + * + *****************************************************************************/ +/* + * Multi station support + */ + +/* Special, dedicated locations within device's station table */ +#define IWL_AP_ID 0 +#define IWL_AP_ID_PAN 1 +#define IWL_STA_ID 2 +#define IWLAGN_PAN_BCAST_ID 14 +#define IWLAGN_BROADCAST_ID 15 +#define IWLAGN_STATION_COUNT 16 + +#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT + +#define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) +#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) +#define STA_FLG_PAN_STATION cpu_to_le32(1 << 13) +#define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17) +#define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18) +#define STA_FLG_MAX_AGG_SIZE_POS (19) +#define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19) +#define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21) +#define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22) +#define STA_FLG_AGG_MPDU_DENSITY_POS (23) +#define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23) + +/* Use in mode field. 1: modify existing entry, 0: add new station entry */ +#define STA_CONTROL_MODIFY_MSK 0x01 + +/* key flags __le16*/ +#define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007) +#define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000) +#define STA_KEY_FLG_WEP cpu_to_le16(0x0001) +#define STA_KEY_FLG_CCMP cpu_to_le16(0x0002) +#define STA_KEY_FLG_TKIP cpu_to_le16(0x0003) + +#define STA_KEY_FLG_KEYID_POS 8 +#define STA_KEY_FLG_INVALID cpu_to_le16(0x0800) +/* wep key is either from global key (0) or from station info array (1) */ +#define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008) + +/* wep key in STA: 5-bytes (0) or 13-bytes (1) */ +#define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000) +#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000) +#define STA_KEY_MAX_NUM 8 +#define STA_KEY_MAX_NUM_PAN 16 +/* must not match WEP_INVALID_OFFSET */ +#define IWLAGN_HW_KEY_DEFAULT 0xfe + +/* Flags indicate whether to modify vs. don't change various station params */ +#define STA_MODIFY_KEY_MASK 0x01 +#define STA_MODIFY_TID_DISABLE_TX 0x02 +#define STA_MODIFY_TX_RATE_MSK 0x04 +#define STA_MODIFY_ADDBA_TID_MSK 0x08 +#define STA_MODIFY_DELBA_TID_MSK 0x10 +#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 + +/* agn */ +struct iwl_keyinfo { + __le16 key_flags; + u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */ + u8 reserved1; + __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */ + u8 key_offset; + u8 reserved2; + u8 key[16]; /* 16-byte unicast decryption key */ + __le64 tx_secur_seq_cnt; + __le64 hw_tkip_mic_rx_key; + __le64 hw_tkip_mic_tx_key; +} __packed; + +/** + * struct sta_id_modify + * @addr[ETH_ALEN]: station's MAC address + * @sta_id: index of station in uCode's station table + * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change + * + * Driver selects unused table index when adding new station, + * or the index to a pre-existing station entry when modifying that station. + * Some indexes have special purposes (IWL_AP_ID, index 0, is for AP). + * + * modify_mask flags select which parameters to modify vs. leave alone. + */ +struct sta_id_modify { + u8 addr[ETH_ALEN]; + __le16 reserved1; + u8 sta_id; + u8 modify_mask; + __le16 reserved2; +} __packed; + +/* + * REPLY_ADD_STA = 0x18 (command) + * + * The device contains an internal table of per-station information, + * with info on security keys, aggregation parameters, and Tx rates for + * initial Tx attempt and any retries (agn devices uses + * REPLY_TX_LINK_QUALITY_CMD, + * + * REPLY_ADD_STA sets up the table entry for one station, either creating + * a new entry, or modifying a pre-existing one. + * + * NOTE: RXON command (without "associated" bit set) wipes the station table + * clean. Moving into RF_KILL state does this also. Driver must set up + * new station table before transmitting anything on the RXON channel + * (except active scans or active measurements; those commands carry + * their own txpower/rate setup data). + * + * When getting started on a new channel, driver must set up the + * IWL_BROADCAST_ID entry (last entry in the table). For a client + * station in a BSS, once an AP is selected, driver sets up the AP STA + * in the IWL_AP_ID entry (1st entry in the table). BROADCAST and AP + * are all that are needed for a BSS client station. If the device is + * used as AP, or in an IBSS network, driver must set up station table + * entries for all STAs in network, starting with index IWL_STA_ID. + */ + +struct iwl_addsta_cmd { + u8 mode; /* 1: modify existing, 0: add new station */ + u8 reserved[3]; + struct sta_id_modify sta; + struct iwl_keyinfo key; + __le32 station_flags; /* STA_FLG_* */ + __le32 station_flags_msk; /* STA_FLG_* */ + + /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) + * corresponding to bit (e.g. bit 5 controls TID 5). + * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ + __le16 tid_disable_tx; + __le16 legacy_reserved; + + /* TID for which to add block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + u8 add_immediate_ba_tid; + + /* TID for which to remove block-ack support. + * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ + u8 remove_immediate_ba_tid; + + /* Starting Sequence Number for added block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + __le16 add_immediate_ba_ssn; + + /* + * Number of packets OK to transmit to station even though + * it is asleep -- used to synchronise PS-poll and u-APSD + * responses while ucode keeps track of STA sleep state. + */ + __le16 sleep_tx_count; + + __le16 reserved2; +} __packed; + + +#define ADD_STA_SUCCESS_MSK 0x1 +#define ADD_STA_NO_ROOM_IN_TABLE 0x2 +#define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4 +#define ADD_STA_MODIFY_NON_EXIST_STA 0x8 +/* + * REPLY_ADD_STA = 0x18 (response) + */ +struct iwl_add_sta_resp { + u8 status; /* ADD_STA_* */ +} __packed; + +#define REM_STA_SUCCESS_MSK 0x1 +/* + * REPLY_REM_STA = 0x19 (response) + */ +struct iwl_rem_sta_resp { + u8 status; +} __packed; + +/* + * REPLY_REM_STA = 0x19 (command) + */ +struct iwl_rem_sta_cmd { + u8 num_sta; /* number of removed stations */ + u8 reserved[3]; + u8 addr[ETH_ALEN]; /* MAC addr of the first station */ + u8 reserved2[2]; +} __packed; + + +/* WiFi queues mask */ +#define IWL_SCD_BK_MSK BIT(0) +#define IWL_SCD_BE_MSK BIT(1) +#define IWL_SCD_VI_MSK BIT(2) +#define IWL_SCD_VO_MSK BIT(3) +#define IWL_SCD_MGMT_MSK BIT(3) + +/* PAN queues mask */ +#define IWL_PAN_SCD_BK_MSK BIT(4) +#define IWL_PAN_SCD_BE_MSK BIT(5) +#define IWL_PAN_SCD_VI_MSK BIT(6) +#define IWL_PAN_SCD_VO_MSK BIT(7) +#define IWL_PAN_SCD_MGMT_MSK BIT(7) +#define IWL_PAN_SCD_MULTICAST_MSK BIT(8) + +#define IWL_AGG_TX_QUEUE_MSK 0xffc00 + +#define IWL_DROP_ALL BIT(1) + +/* + * REPLY_TXFIFO_FLUSH = 0x1e(command and response) + * + * When using full FIFO flush this command checks the scheduler HW block WR/RD + * pointers to check if all the frames were transferred by DMA into the + * relevant TX FIFO queue. Only when the DMA is finished and the queue is + * empty the command can finish. + * This command is used to flush the TXFIFO from transmit commands, it may + * operate on single or multiple queues, the command queue can't be flushed by + * this command. The command response is returned when all the queue flush + * operations are done. Each TX command flushed return response with the FLUSH + * status set in the TX response status. When FIFO flush operation is used, + * the flush operation ends when both the scheduler DMA done and TXFIFO empty + * are set. + * + * @queue_control: bit mask for which queues to flush + * @flush_control: flush controls + * 0: Dump single MSDU + * 1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable. + * 2: Dump all FIFO + */ +struct iwl_txfifo_flush_cmd_v3 { + __le32 queue_control; + __le16 flush_control; + __le16 reserved; +} __packed; + +struct iwl_txfifo_flush_cmd_v2 { + __le16 queue_control; + __le16 flush_control; +} __packed; + +/* + * REPLY_WEP_KEY = 0x20 + */ +struct iwl_wep_key { + u8 key_index; + u8 key_offset; + u8 reserved1[2]; + u8 key_size; + u8 reserved2[3]; + u8 key[16]; +} __packed; + +struct iwl_wep_cmd { + u8 num_keys; + u8 global_key_type; + u8 flags; + u8 reserved; + struct iwl_wep_key key[0]; +} __packed; + +#define WEP_KEY_WEP_TYPE 1 +#define WEP_KEYS_MAX 4 +#define WEP_INVALID_OFFSET 0xff +#define WEP_KEY_LEN_64 5 +#define WEP_KEY_LEN_128 13 + +/****************************************************************************** + * (4) + * Rx Responses: + * + *****************************************************************************/ + +#define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0) +#define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1) + +#define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0) +#define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) +#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) +#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) +#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0x70 +#define RX_RES_PHY_FLAGS_ANTENNA_POS 4 +#define RX_RES_PHY_FLAGS_AGG_MSK cpu_to_le16(1 << 7) + +#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) +#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) +#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8) +#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8) +#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8) +#define RX_RES_STATUS_SEC_TYPE_ERR (0x7 << 8) + +#define RX_RES_STATUS_STATION_FOUND (1<<6) +#define RX_RES_STATUS_NO_STATION_INFO_MISMATCH (1<<7) + +#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11) +#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11) +#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11) +#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11) +#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11) + +#define RX_MPDU_RES_STATUS_ICV_OK (0x20) +#define RX_MPDU_RES_STATUS_MIC_OK (0x40) +#define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7) +#define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800) + + +#define IWLAGN_RX_RES_PHY_CNT 8 +#define IWLAGN_RX_RES_AGC_IDX 1 +#define IWLAGN_RX_RES_RSSI_AB_IDX 2 +#define IWLAGN_RX_RES_RSSI_C_IDX 3 +#define IWLAGN_OFDM_AGC_MSK 0xfe00 +#define IWLAGN_OFDM_AGC_BIT_POS 9 +#define IWLAGN_OFDM_RSSI_INBAND_A_BITMSK 0x00ff +#define IWLAGN_OFDM_RSSI_ALLBAND_A_BITMSK 0xff00 +#define IWLAGN_OFDM_RSSI_A_BIT_POS 0 +#define IWLAGN_OFDM_RSSI_INBAND_B_BITMSK 0xff0000 +#define IWLAGN_OFDM_RSSI_ALLBAND_B_BITMSK 0xff000000 +#define IWLAGN_OFDM_RSSI_B_BIT_POS 16 +#define IWLAGN_OFDM_RSSI_INBAND_C_BITMSK 0x00ff +#define IWLAGN_OFDM_RSSI_ALLBAND_C_BITMSK 0xff00 +#define IWLAGN_OFDM_RSSI_C_BIT_POS 0 + +struct iwlagn_non_cfg_phy { + __le32 non_cfg_phy[IWLAGN_RX_RES_PHY_CNT]; /* up to 8 phy entries */ +} __packed; + + +/* + * REPLY_RX = 0xc3 (response only, not a command) + * Used only for legacy (non 11n) frames. + */ +struct iwl_rx_phy_res { + u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */ + u8 cfg_phy_cnt; /* configurable DSP phy data byte count */ + u8 stat_id; /* configurable DSP phy data set ID */ + u8 reserved1; + __le64 timestamp; /* TSF at on air rise */ + __le32 beacon_time_stamp; /* beacon at on-air rise */ + __le16 phy_flags; /* general phy flags: band, modulation, ... */ + __le16 channel; /* channel number */ + u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */ + __le32 rate_n_flags; /* RATE_MCS_* */ + __le16 byte_count; /* frame's byte-count */ + __le16 frame_time; /* frame's time on the air */ +} __packed; + +struct iwl_rx_mpdu_res_start { + __le16 byte_count; + __le16 reserved; +} __packed; + + +/****************************************************************************** + * (5) + * Tx Commands & Responses: + * + * Driver must place each REPLY_TX command into one of the prioritized Tx + * queues in host DRAM, shared between driver and device (see comments for + * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode + * are preparing to transmit, the device pulls the Tx command over the PCI + * bus via one of the device's Tx DMA channels, to fill an internal FIFO + * from which data will be transmitted. + * + * uCode handles all timing and protocol related to control frames + * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler + * handle reception of block-acks; uCode updates the host driver via + * REPLY_COMPRESSED_BA. + * + * uCode handles retrying Tx when an ACK is expected but not received. + * This includes trying lower data rates than the one requested in the Tx + * command, as set up by the REPLY_TX_LINK_QUALITY_CMD (agn). + * + * Driver sets up transmit power for various rates via REPLY_TX_PWR_TABLE_CMD. + * This command must be executed after every RXON command, before Tx can occur. + *****************************************************************************/ + +/* REPLY_TX Tx flags field */ + +/* + * 1: Use RTS/CTS protocol or CTS-to-self if spec allows it + * before this frame. if CTS-to-self required check + * RXON_FLG_SELF_CTS_EN status. + */ +#define TX_CMD_FLG_PROT_REQUIRE_MSK cpu_to_le32(1 << 0) + +/* 1: Expect ACK from receiving station + * 0: Don't expect ACK (MAC header's duration field s/b 0) + * Set this for unicast frames, but not broadcast/multicast. */ +#define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3) + +/* For agn devices: + * 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD). + * Tx command's initial_rate_index indicates first rate to try; + * uCode walks through table for additional Tx attempts. + * 0: Use Tx rate/MCS from Tx command's rate_n_flags field. + * This rate will be used for all Tx attempts; it will not be scaled. */ +#define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4) + +/* 1: Expect immediate block-ack. + * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ +#define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6) + +/* Tx antenna selection field; reserved (0) for agn devices. */ +#define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00) + +/* 1: Ignore Bluetooth priority for this frame. + * 0: Delay Tx until Bluetooth device is done (normal usage). */ +#define TX_CMD_FLG_IGNORE_BT cpu_to_le32(1 << 12) + +/* 1: uCode overrides sequence control field in MAC header. + * 0: Driver provides sequence control field in MAC header. + * Set this for management frames, non-QOS data frames, non-unicast frames, + * and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */ +#define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13) + +/* 1: This frame is non-last MPDU; more fragments are coming. + * 0: Last fragment, or not using fragmentation. */ +#define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14) + +/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame. + * 0: No TSF required in outgoing frame. + * Set this for transmitting beacons and probe responses. */ +#define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16) + +/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword + * alignment of frame's payload data field. + * 0: No pad + * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4 + * field (but not both). Driver must align frame data (i.e. data following + * MAC header) to DWORD boundary. */ +#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20) + +/* accelerate aggregation support + * 0 - no CCMP encryption; 1 - CCMP encryption */ +#define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22) + +/* HCCA-AP - disable duration overwriting. */ +#define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25) + + +/* + * TX command security control + */ +#define TX_CMD_SEC_WEP 0x01 +#define TX_CMD_SEC_CCM 0x02 +#define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_MSK 0x03 +#define TX_CMD_SEC_SHIFT 6 +#define TX_CMD_SEC_KEY128 0x08 + +/* + * REPLY_TX = 0x1c (command) + */ + +/* + * 4965 uCode updates these Tx attempt count values in host DRAM. + * Used for managing Tx retries when expecting block-acks. + * Driver should set these fields to 0. + */ +struct iwl_dram_scratch { + u8 try_cnt; /* Tx attempts */ + u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */ + __le16 reserved; +} __packed; + +struct iwl_tx_cmd { + /* + * MPDU byte count: + * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * NOTE: Does not include Tx command bytes, post-MAC pad bytes, + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i + * Range: 14-2342 bytes. + */ + __le16 len; + + /* + * MPDU or MSDU byte count for next frame. + * Used for fragmentation and bursting, but not 11n aggregation. + * Same as "len", but for next frame. Set to 0 if not applicable. + */ + __le16 next_frame_len; + + __le32 tx_flags; /* TX_CMD_FLG_* */ + + /* uCode may modify this field of the Tx command (in host DRAM!). + * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ + struct iwl_dram_scratch scratch; + + /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ + __le32 rate_n_flags; /* RATE_MCS_* */ + + /* Index of destination station in uCode's station table */ + u8 sta_id; + + /* Type of security encryption: CCM or TKIP */ + u8 sec_ctl; /* TX_CMD_SEC_* */ + + /* + * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial + * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for + * data frames, this field may be used to selectively reduce initial + * rate (via non-0 value) for special frames (e.g. management), while + * still supporting rate scaling for all frames. + */ + u8 initial_rate_index; + u8 reserved; + u8 key[16]; + __le16 next_frame_flags; + __le16 reserved2; + union { + __le32 life_time; + __le32 attempt; + } stop_time; + + /* Host DRAM physical address pointer to "scratch" in this command. + * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ + __le32 dram_lsb_ptr; + u8 dram_msb_ptr; + + u8 rts_retry_limit; /*byte 50 */ + u8 data_retry_limit; /*byte 51 */ + u8 tid_tspec; + union { + __le16 pm_frame_timeout; + __le16 attempt_duration; + } timeout; + + /* + * Duration of EDCA burst Tx Opportunity, in 32-usec units. + * Set this if txop time is not specified by HCCA protocol (e.g. by AP). + */ + __le16 driver_txop; + + /* + * MAC header goes here, followed by 2 bytes padding if MAC header + * length is 26 or 30 bytes, followed by payload data + */ + u8 payload[0]; + struct ieee80211_hdr hdr[0]; +} __packed; + +/* + * TX command response is sent after *agn* transmission attempts. + * + * both postpone and abort status are expected behavior from uCode. there is + * no special operation required from driver; except for RFKILL_FLUSH, + * which required tx flush host command to flush all the tx frames in queues + */ +enum { + TX_STATUS_SUCCESS = 0x01, + TX_STATUS_DIRECT_DONE = 0x02, + /* postpone TX */ + TX_STATUS_POSTPONE_DELAY = 0x40, + TX_STATUS_POSTPONE_FEW_BYTES = 0x41, + TX_STATUS_POSTPONE_BT_PRIO = 0x42, + TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, + TX_STATUS_POSTPONE_CALC_TTAK = 0x44, + /* abort TX */ + TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, + TX_STATUS_FAIL_SHORT_LIMIT = 0x82, + TX_STATUS_FAIL_LONG_LIMIT = 0x83, + TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84, + TX_STATUS_FAIL_DRAIN_FLOW = 0x85, + TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, + TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, + TX_STATUS_FAIL_DEST_PS = 0x88, + TX_STATUS_FAIL_HOST_ABORTED = 0x89, + TX_STATUS_FAIL_BT_RETRY = 0x8a, + TX_STATUS_FAIL_STA_INVALID = 0x8b, + TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, + TX_STATUS_FAIL_TID_DISABLE = 0x8d, + TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, + TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, + TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90, + TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, +}; + +#define TX_PACKET_MODE_REGULAR 0x0000 +#define TX_PACKET_MODE_BURST_SEQ 0x0100 +#define TX_PACKET_MODE_BURST_FIRST 0x0200 + +enum { + TX_POWER_PA_NOT_ACTIVE = 0x0, +}; + +enum { + TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */ + TX_STATUS_DELAY_MSK = 0x00000040, + TX_STATUS_ABORT_MSK = 0x00000080, + TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */ + TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */ + TX_RESERVED = 0x00780000, /* bits 19:22 */ + TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */ + TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */ +}; + +/* ******************************* + * TX aggregation status + ******************************* */ + +enum { + AGG_TX_STATE_TRANSMITTED = 0x00, + AGG_TX_STATE_UNDERRUN_MSK = 0x01, + AGG_TX_STATE_BT_PRIO_MSK = 0x02, + AGG_TX_STATE_FEW_BYTES_MSK = 0x04, + AGG_TX_STATE_ABORT_MSK = 0x08, + AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10, + AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20, + AGG_TX_STATE_LAST_SENT_BT_KILL_MSK = 0x40, + AGG_TX_STATE_SCD_QUERY_MSK = 0x80, + AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100, + AGG_TX_STATE_RESPONSE_MSK = 0x1ff, + AGG_TX_STATE_DUMP_TX_MSK = 0x200, + AGG_TX_STATE_DELAY_TX_MSK = 0x400 +}; + +#define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */ +#define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */ +#define AGG_TX_TRY_POS 12 + +#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \ + AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \ + AGG_TX_STATE_LAST_SENT_BT_KILL_MSK) + +/* # tx attempts for first frame in aggregation */ +#define AGG_TX_STATE_TRY_CNT_POS 12 +#define AGG_TX_STATE_TRY_CNT_MSK 0xf000 + +/* Command ID and sequence number of Tx command for this frame */ +#define AGG_TX_STATE_SEQ_NUM_POS 16 +#define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 + +/* + * REPLY_TX = 0x1c (response) + * + * This response may be in one of two slightly different formats, indicated + * by the frame_count field: + * + * 1) No aggregation (frame_count == 1). This reports Tx results for + * a single frame. Multiple attempts, at various bit rates, may have + * been made for this frame. + * + * 2) Aggregation (frame_count > 1). This reports Tx results for + * 2 or more frames that used block-acknowledge. All frames were + * transmitted at same rate. Rate scaling may have been used if first + * frame in this new agg block failed in previous agg block(s). + * + * Note that, for aggregation, ACK (block-ack) status is not delivered here; + * block-ack has not been received by the time the agn device records + * this status. + * This status relates to reasons the tx might have been blocked or aborted + * within the sending station (this agn device), rather than whether it was + * received successfully by the destination station. + */ +struct agg_tx_status { + __le16 status; + __le16 sequence; +} __packed; + +/* + * definitions for initial rate index field + * bits [3:0] initial rate index + * bits [6:4] rate table color, used for the initial rate + * bit-7 invalid rate indication + * i.e. rate was not chosen from rate table + * or rate table color was changed during frame retries + * refer tlc rate info + */ + +#define IWL50_TX_RES_INIT_RATE_INDEX_POS 0 +#define IWL50_TX_RES_INIT_RATE_INDEX_MSK 0x0f +#define IWL50_TX_RES_RATE_TABLE_COLOR_POS 4 +#define IWL50_TX_RES_RATE_TABLE_COLOR_MSK 0x70 +#define IWL50_TX_RES_INV_RATE_INDEX_MSK 0x80 + +/* refer to ra_tid */ +#define IWLAGN_TX_RES_TID_POS 0 +#define IWLAGN_TX_RES_TID_MSK 0x0f +#define IWLAGN_TX_RES_RA_POS 4 +#define IWLAGN_TX_RES_RA_MSK 0xf0 + +struct iwlagn_tx_resp { + u8 frame_count; /* 1 no aggregation, >1 aggregation */ + u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */ + u8 failure_rts; /* # failures due to unsuccessful RTS */ + u8 failure_frame; /* # failures due to no ACK (unused for agg) */ + + /* For non-agg: Rate at which frame was successful. + * For agg: Rate at which all frames were transmitted. */ + __le32 rate_n_flags; /* RATE_MCS_* */ + + /* For non-agg: RTS + CTS + frame tx attempts time + ACK. + * For agg: RTS + CTS + aggregation tx time + block-ack time. */ + __le16 wireless_media_time; /* uSecs */ + + u8 pa_status; /* RF power amplifier measurement (not used) */ + u8 pa_integ_res_a[3]; + u8 pa_integ_res_b[3]; + u8 pa_integ_res_C[3]; + + __le32 tfd_info; + __le16 seq_ctl; + __le16 byte_cnt; + u8 tlc_info; + u8 ra_tid; /* tid (0:3), sta_id (4:7) */ + __le16 frame_ctrl; + /* + * For non-agg: frame status TX_STATUS_* + * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status + * fields follow this one, up to frame_count. + * Bit fields: + * 11- 0: AGG_TX_STATE_* status code + * 15-12: Retry count for 1st frame in aggregation (retries + * occur if tx failed for this frame when it was a + * member of a previous aggregation block). If rate + * scaling is used, retry count indicates the rate + * table entry used for all frames in the new agg. + * 31-16: Sequence # for this frame's Tx cmd (not SSN!) + */ + struct agg_tx_status status; /* TX status (in aggregation - + * status of 1st frame) */ +} __packed; +/* + * REPLY_COMPRESSED_BA = 0xc5 (response only, not a command) + * + * Reports Block-Acknowledge from recipient station + */ +struct iwl_compressed_ba_resp { + __le32 sta_addr_lo32; + __le16 sta_addr_hi16; + __le16 reserved; + + /* Index of recipient (BA-sending) station in uCode's station table */ + u8 sta_id; + u8 tid; + __le16 seq_ctl; + __le64 bitmap; + __le16 scd_flow; + __le16 scd_ssn; + u8 txed; /* number of frames sent */ + u8 txed_2_done; /* number of frames acked */ + __le16 reserved1; +} __packed; + +/* + * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response) + * + */ + +/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */ +#define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1 << 0) + +/* # of EDCA prioritized tx fifos */ +#define LINK_QUAL_AC_NUM AC_NUM + +/* # entries in rate scale table to support Tx retries */ +#define LINK_QUAL_MAX_RETRY_NUM 16 + +/* Tx antenna selection values */ +#define LINK_QUAL_ANT_A_MSK (1 << 0) +#define LINK_QUAL_ANT_B_MSK (1 << 1) +#define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK) + + +/** + * struct iwl_link_qual_general_params + * + * Used in REPLY_TX_LINK_QUALITY_CMD + */ +struct iwl_link_qual_general_params { + u8 flags; + + /* No entries at or above this (driver chosen) index contain MIMO */ + u8 mimo_delimiter; + + /* Best single antenna to use for single stream (legacy, SISO). */ + u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */ + + /* Best antennas to use for MIMO (unused for 4965, assumes both). */ + u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */ + + /* + * If driver needs to use different initial rates for different + * EDCA QOS access categories (as implemented by tx fifos 0-3), + * this table will set that up, by indicating the indexes in the + * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start. + * Otherwise, driver should set all entries to 0. + * + * Entry usage: + * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice + * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3. + */ + u8 start_rate_index[LINK_QUAL_AC_NUM]; +} __packed; + +#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ +#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) +#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) + +#define LINK_QUAL_AGG_DISABLE_START_DEF (3) +#define LINK_QUAL_AGG_DISABLE_START_MAX (255) +#define LINK_QUAL_AGG_DISABLE_START_MIN (0) + +#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) +#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) +#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) + +/** + * struct iwl_link_qual_agg_params + * + * Used in REPLY_TX_LINK_QUALITY_CMD + */ +struct iwl_link_qual_agg_params { + + /* + *Maximum number of uSec in aggregation. + * default set to 4000 (4 milliseconds) if not configured in .cfg + */ + __le16 agg_time_limit; + + /* + * Number of Tx retries allowed for a frame, before that frame will + * no longer be considered for the start of an aggregation sequence + * (scheduler will then try to tx it as single frame). + * Driver should set this to 3. + */ + u8 agg_dis_start_th; + + /* + * Maximum number of frames in aggregation. + * 0 = no limit (default). 1 = no aggregation. + * Other values = max # frames in aggregation. + */ + u8 agg_frame_cnt_limit; + + __le32 reserved; +} __packed; + +/* + * REPLY_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response) + * + * For agn devices + * + * Each station in the agn device's internal station table has its own table + * of 16 + * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when + * an ACK is not received. This command replaces the entire table for + * one station. + * + * NOTE: Station must already be in agn device's station table. + * Use REPLY_ADD_STA. + * + * The rate scaling procedures described below work well. Of course, other + * procedures are possible, and may work better for particular environments. + * + * + * FILLING THE RATE TABLE + * + * Given a particular initial rate and mode, as determined by the rate + * scaling algorithm described below, the Linux driver uses the following + * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the + * Link Quality command: + * + * + * 1) If using High-throughput (HT) (SISO or MIMO) initial rate: + * a) Use this same initial rate for first 3 entries. + * b) Find next lower available rate using same mode (SISO or MIMO), + * use for next 3 entries. If no lower rate available, switch to + * legacy mode (no HT40 channel, no MIMO, no short guard interval). + * c) If using MIMO, set command's mimo_delimiter to number of entries + * using MIMO (3 or 6). + * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel, + * no MIMO, no short guard interval), at the next lower bit rate + * (e.g. if second HT bit rate was 54, try 48 legacy), and follow + * legacy procedure for remaining table entries. + * + * 2) If using legacy initial rate: + * a) Use the initial rate for only one entry. + * b) For each following entry, reduce the rate to next lower available + * rate, until reaching the lowest available rate. + * c) When reducing rate, also switch antenna selection. + * d) Once lowest available rate is reached, repeat this rate until + * rate table is filled (16 entries), switching antenna each entry. + * + * + * ACCUMULATING HISTORY + * + * The rate scaling algorithm for agn devices, as implemented in Linux driver, + * uses two sets of frame Tx success history: One for the current/active + * modulation mode, and one for a speculative/search mode that is being + * attempted. If the speculative mode turns out to be more effective (i.e. + * actual transfer rate is better), then the driver continues to use the + * speculative mode as the new current active mode. + * + * Each history set contains, separately for each possible rate, data for a + * sliding window of the 62 most recent tx attempts at that rate. The data + * includes a shifting bitmap of success(1)/failure(0), and sums of successful + * and attempted frames, from which the driver can additionally calculate a + * success ratio (success / attempted) and number of failures + * (attempted - success), and control the size of the window (attempted). + * The driver uses the bit map to remove successes from the success sum, as + * the oldest tx attempts fall out of the window. + * + * When the agn device makes multiple tx attempts for a given frame, each + * attempt might be at a different rate, and have different modulation + * characteristics (e.g. antenna, fat channel, short guard interval), as set + * up in the rate scaling table in the Link Quality command. The driver must + * determine which rate table entry was used for each tx attempt, to determine + * which rate-specific history to update, and record only those attempts that + * match the modulation characteristics of the history set. + * + * When using block-ack (aggregation), all frames are transmitted at the same + * rate, since there is no per-attempt acknowledgment from the destination + * station. The Tx response struct iwl_tx_resp indicates the Tx rate in + * rate_n_flags field. After receiving a block-ack, the driver can update + * history for the entire block all at once. + * + * + * FINDING BEST STARTING RATE: + * + * When working with a selected initial modulation mode (see below), the + * driver attempts to find a best initial rate. The initial rate is the + * first entry in the Link Quality command's rate table. + * + * 1) Calculate actual throughput (success ratio * expected throughput, see + * table below) for current initial rate. Do this only if enough frames + * have been attempted to make the value meaningful: at least 6 failed + * tx attempts, or at least 8 successes. If not enough, don't try rate + * scaling yet. + * + * 2) Find available rates adjacent to current initial rate. Available means: + * a) supported by hardware && + * b) supported by association && + * c) within any constraints selected by user + * + * 3) Gather measured throughputs for adjacent rates. These might not have + * enough history to calculate a throughput. That's okay, we might try + * using one of them anyway! + * + * 4) Try decreasing rate if, for current rate: + * a) success ratio is < 15% || + * b) lower adjacent rate has better measured throughput || + * c) higher adjacent rate has worse throughput, and lower is unmeasured + * + * As a sanity check, if decrease was determined above, leave rate + * unchanged if: + * a) lower rate unavailable + * b) success ratio at current rate > 85% (very good) + * c) current measured throughput is better than expected throughput + * of lower rate (under perfect 100% tx conditions, see table below) + * + * 5) Try increasing rate if, for current rate: + * a) success ratio is < 15% || + * b) both adjacent rates' throughputs are unmeasured (try it!) || + * b) higher adjacent rate has better measured throughput || + * c) lower adjacent rate has worse throughput, and higher is unmeasured + * + * As a sanity check, if increase was determined above, leave rate + * unchanged if: + * a) success ratio at current rate < 70%. This is not particularly + * good performance; higher rate is sure to have poorer success. + * + * 6) Re-evaluate the rate after each tx frame. If working with block- + * acknowledge, history and statistics may be calculated for the entire + * block (including prior history that fits within the history windows), + * before re-evaluation. + * + * FINDING BEST STARTING MODULATION MODE: + * + * After working with a modulation mode for a "while" (and doing rate scaling), + * the driver searches for a new initial mode in an attempt to improve + * throughput. The "while" is measured by numbers of attempted frames: + * + * For legacy mode, search for new mode after: + * 480 successful frames, or 160 failed frames + * For high-throughput modes (SISO or MIMO), search for new mode after: + * 4500 successful frames, or 400 failed frames + * + * Mode switch possibilities are (3 for each mode): + * + * For legacy: + * Change antenna, try SISO (if HT association), try MIMO (if HT association) + * For SISO: + * Change antenna, try MIMO, try shortened guard interval (SGI) + * For MIMO: + * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI) + * + * When trying a new mode, use the same bit rate as the old/current mode when + * trying antenna switches and shortened guard interval. When switching to + * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate + * for which the expected throughput (under perfect conditions) is about the + * same or slightly better than the actual measured throughput delivered by + * the old/current mode. + * + * Actual throughput can be estimated by multiplying the expected throughput + * by the success ratio (successful / attempted tx frames). Frame size is + * not considered in this calculation; it assumes that frame size will average + * out to be fairly consistent over several samples. The following are + * metric values for expected throughput assuming 100% success ratio. + * Only G band has support for CCK rates: + * + * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60 + * + * G: 7 13 35 58 40 57 72 98 121 154 177 186 186 + * A: 0 0 0 0 40 57 72 98 121 154 177 186 186 + * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202 + * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211 + * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251 + * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257 + * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257 + * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264 + * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289 + * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293 + * + * After the new mode has been tried for a short while (minimum of 6 failed + * frames or 8 successful frames), compare success ratio and actual throughput + * estimate of the new mode with the old. If either is better with the new + * mode, continue to use the new mode. + * + * Continue comparing modes until all 3 possibilities have been tried. + * If moving from legacy to HT, try all 3 possibilities from the new HT + * mode. After trying all 3, a best mode is found. Continue to use this mode + * for the longer "while" described above (e.g. 480 successful frames for + * legacy), and then repeat the search process. + * + */ +struct iwl_link_quality_cmd { + + /* Index of destination/recipient station in uCode's station table */ + u8 sta_id; + u8 reserved1; + __le16 control; /* not used */ + struct iwl_link_qual_general_params general_params; + struct iwl_link_qual_agg_params agg_params; + + /* + * Rate info; when using rate-scaling, Tx command's initial_rate_index + * specifies 1st Tx rate attempted, via index into this table. + * agn devices works its way through table when retrying Tx. + */ + struct { + __le32 rate_n_flags; /* RATE_MCS_*, IWL_RATE_* */ + } rs_table[LINK_QUAL_MAX_RETRY_NUM]; + __le32 reserved2; +} __packed; + +/* + * BT configuration enable flags: + * bit 0 - 1: BT channel announcement enabled + * 0: disable + * bit 1 - 1: priority of BT device enabled + * 0: disable + * bit 2 - 1: BT 2 wire support enabled + * 0: disable + */ +#define BT_COEX_DISABLE (0x0) +#define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0) +#define BT_ENABLE_PRIORITY BIT(1) +#define BT_ENABLE_2_WIRE BIT(2) + +#define BT_COEX_DISABLE (0x0) +#define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY) + +#define BT_LEAD_TIME_MIN (0x0) +#define BT_LEAD_TIME_DEF (0x1E) +#define BT_LEAD_TIME_MAX (0xFF) + +#define BT_MAX_KILL_MIN (0x1) +#define BT_MAX_KILL_DEF (0x5) +#define BT_MAX_KILL_MAX (0xFF) + +#define BT_DURATION_LIMIT_DEF 625 +#define BT_DURATION_LIMIT_MAX 1250 +#define BT_DURATION_LIMIT_MIN 625 + +#define BT_ON_THRESHOLD_DEF 4 +#define BT_ON_THRESHOLD_MAX 1000 +#define BT_ON_THRESHOLD_MIN 1 + +#define BT_FRAG_THRESHOLD_DEF 0 +#define BT_FRAG_THRESHOLD_MAX 0 +#define BT_FRAG_THRESHOLD_MIN 0 + +#define BT_AGG_THRESHOLD_DEF 1200 +#define BT_AGG_THRESHOLD_MAX 8000 +#define BT_AGG_THRESHOLD_MIN 400 + +/* + * REPLY_BT_CONFIG = 0x9b (command, has simple generic response) + * + * agn devices support hardware handshake with Bluetooth device on + * same platform. Bluetooth device alerts wireless device when it will Tx; + * wireless device can delay or kill its own Tx to accommodate. + */ +struct iwl_bt_cmd { + u8 flags; + u8 lead_time; + u8 max_kill; + u8 reserved; + __le32 kill_ack_mask; + __le32 kill_cts_mask; +} __packed; + +#define IWLAGN_BT_FLAG_CHANNEL_INHIBITION BIT(0) + +#define IWLAGN_BT_FLAG_COEX_MODE_MASK (BIT(3)|BIT(4)|BIT(5)) +#define IWLAGN_BT_FLAG_COEX_MODE_SHIFT 3 +#define IWLAGN_BT_FLAG_COEX_MODE_DISABLED 0 +#define IWLAGN_BT_FLAG_COEX_MODE_LEGACY_2W 1 +#define IWLAGN_BT_FLAG_COEX_MODE_3W 2 +#define IWLAGN_BT_FLAG_COEX_MODE_4W 3 + +#define IWLAGN_BT_FLAG_UCODE_DEFAULT BIT(6) +/* Disable Sync PSPoll on SCO/eSCO */ +#define IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE BIT(7) + +#define IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD -75 /* dBm */ +#define IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD -65 /* dBm */ + +#define IWLAGN_BT_PRIO_BOOST_MAX 0xFF +#define IWLAGN_BT_PRIO_BOOST_MIN 0x00 +#define IWLAGN_BT_PRIO_BOOST_DEFAULT 0xF0 +#define IWLAGN_BT_PRIO_BOOST_DEFAULT32 0xF0F0F0F0 + +#define IWLAGN_BT_MAX_KILL_DEFAULT 5 + +#define IWLAGN_BT3_T7_DEFAULT 1 + +enum iwl_bt_kill_idx { + IWL_BT_KILL_DEFAULT = 0, + IWL_BT_KILL_OVERRIDE = 1, + IWL_BT_KILL_REDUCE = 2, +}; + +#define IWLAGN_BT_KILL_ACK_MASK_DEFAULT cpu_to_le32(0xffff0000) +#define IWLAGN_BT_KILL_CTS_MASK_DEFAULT cpu_to_le32(0xffff0000) +#define IWLAGN_BT_KILL_ACK_CTS_MASK_SCO cpu_to_le32(0xffffffff) +#define IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE cpu_to_le32(0) + +#define IWLAGN_BT3_PRIO_SAMPLE_DEFAULT 2 + +#define IWLAGN_BT3_T2_DEFAULT 0xc + +#define IWLAGN_BT_VALID_ENABLE_FLAGS cpu_to_le16(BIT(0)) +#define IWLAGN_BT_VALID_BOOST cpu_to_le16(BIT(1)) +#define IWLAGN_BT_VALID_MAX_KILL cpu_to_le16(BIT(2)) +#define IWLAGN_BT_VALID_3W_TIMERS cpu_to_le16(BIT(3)) +#define IWLAGN_BT_VALID_KILL_ACK_MASK cpu_to_le16(BIT(4)) +#define IWLAGN_BT_VALID_KILL_CTS_MASK cpu_to_le16(BIT(5)) +#define IWLAGN_BT_VALID_REDUCED_TX_PWR cpu_to_le16(BIT(6)) +#define IWLAGN_BT_VALID_3W_LUT cpu_to_le16(BIT(7)) + +#define IWLAGN_BT_ALL_VALID_MSK (IWLAGN_BT_VALID_ENABLE_FLAGS | \ + IWLAGN_BT_VALID_BOOST | \ + IWLAGN_BT_VALID_MAX_KILL | \ + IWLAGN_BT_VALID_3W_TIMERS | \ + IWLAGN_BT_VALID_KILL_ACK_MASK | \ + IWLAGN_BT_VALID_KILL_CTS_MASK | \ + IWLAGN_BT_VALID_REDUCED_TX_PWR | \ + IWLAGN_BT_VALID_3W_LUT) + +#define IWLAGN_BT_REDUCED_TX_PWR BIT(0) + +#define IWLAGN_BT_DECISION_LUT_SIZE 12 + +struct iwl_basic_bt_cmd { + u8 flags; + u8 ledtime; /* unused */ + u8 max_kill; + u8 bt3_timer_t7_value; + __le32 kill_ack_mask; + __le32 kill_cts_mask; + u8 bt3_prio_sample_time; + u8 bt3_timer_t2_value; + __le16 bt4_reaction_time; /* unused */ + __le32 bt3_lookup_table[IWLAGN_BT_DECISION_LUT_SIZE]; + /* + * bit 0: use reduced tx power for control frame + * bit 1 - 7: reserved + */ + u8 reduce_txpower; + u8 reserved; + __le16 valid; +}; + +struct iwl_bt_cmd_v1 { + struct iwl_basic_bt_cmd basic; + u8 prio_boost; + /* + * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask + * if configure the following patterns + */ + u8 tx_prio_boost; /* SW boost of WiFi tx priority */ + __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ +}; + +struct iwl_bt_cmd_v2 { + struct iwl_basic_bt_cmd basic; + __le32 prio_boost; + /* + * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask + * if configure the following patterns + */ + u8 reserved; + u8 tx_prio_boost; /* SW boost of WiFi tx priority */ + __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ +}; + +#define IWLAGN_BT_SCO_ACTIVE cpu_to_le32(BIT(0)) + +struct iwlagn_bt_sco_cmd { + __le32 flags; +}; + +/****************************************************************************** + * (6) + * Spectrum Management (802.11h) Commands, Responses, Notifications: + * + *****************************************************************************/ + +/* + * Spectrum Management + */ +#define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \ + RXON_FILTER_CTL2HOST_MSK | \ + RXON_FILTER_ACCEPT_GRP_MSK | \ + RXON_FILTER_DIS_DECRYPT_MSK | \ + RXON_FILTER_DIS_GRP_DECRYPT_MSK | \ + RXON_FILTER_ASSOC_MSK | \ + RXON_FILTER_BCON_AWARE_MSK) + +struct iwl_measure_channel { + __le32 duration; /* measurement duration in extended beacon + * format */ + u8 channel; /* channel to measure */ + u8 type; /* see enum iwl_measure_type */ + __le16 reserved; +} __packed; + +/* + * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command) + */ +struct iwl_spectrum_cmd { + __le16 len; /* number of bytes starting from token */ + u8 token; /* token id */ + u8 id; /* measurement id -- 0 or 1 */ + u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */ + u8 periodic; /* 1 = periodic */ + __le16 path_loss_timeout; + __le32 start_time; /* start time in extended beacon format */ + __le32 reserved2; + __le32 flags; /* rxon flags */ + __le32 filter_flags; /* rxon filter flags */ + __le16 channel_count; /* minimum 1, maximum 10 */ + __le16 reserved3; + struct iwl_measure_channel channels[10]; +} __packed; + +/* + * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response) + */ +struct iwl_spectrum_resp { + u8 token; + u8 id; /* id of the prior command replaced, or 0xff */ + __le16 status; /* 0 - command will be handled + * 1 - cannot handle (conflicts with another + * measurement) */ +} __packed; + +enum iwl_measurement_state { + IWL_MEASUREMENT_START = 0, + IWL_MEASUREMENT_STOP = 1, +}; + +enum iwl_measurement_status { + IWL_MEASUREMENT_OK = 0, + IWL_MEASUREMENT_CONCURRENT = 1, + IWL_MEASUREMENT_CSA_CONFLICT = 2, + IWL_MEASUREMENT_TGH_CONFLICT = 3, + /* 4-5 reserved */ + IWL_MEASUREMENT_STOPPED = 6, + IWL_MEASUREMENT_TIMEOUT = 7, + IWL_MEASUREMENT_PERIODIC_FAILED = 8, +}; + +#define NUM_ELEMENTS_IN_HISTOGRAM 8 + +struct iwl_measurement_histogram { + __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */ + __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */ +} __packed; + +/* clear channel availability counters */ +struct iwl_measurement_cca_counters { + __le32 ofdm; + __le32 cck; +} __packed; + +enum iwl_measure_type { + IWL_MEASURE_BASIC = (1 << 0), + IWL_MEASURE_CHANNEL_LOAD = (1 << 1), + IWL_MEASURE_HISTOGRAM_RPI = (1 << 2), + IWL_MEASURE_HISTOGRAM_NOISE = (1 << 3), + IWL_MEASURE_FRAME = (1 << 4), + /* bits 5:6 are reserved */ + IWL_MEASURE_IDLE = (1 << 7), +}; + +/* + * SPECTRUM_MEASURE_NOTIFICATION = 0x75 (notification only, not a command) + */ +struct iwl_spectrum_notification { + u8 id; /* measurement id -- 0 or 1 */ + u8 token; + u8 channel_index; /* index in measurement channel list */ + u8 state; /* 0 - start, 1 - stop */ + __le32 start_time; /* lower 32-bits of TSF */ + u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */ + u8 channel; + u8 type; /* see enum iwl_measurement_type */ + u8 reserved1; + /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only + * valid if applicable for measurement type requested. */ + __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */ + __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */ + __le32 cca_time; /* channel load time in usecs */ + u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 - + * unidentified */ + u8 reserved2[3]; + struct iwl_measurement_histogram histogram; + __le32 stop_time; /* lower 32-bits of TSF */ + __le32 status; /* see iwl_measurement_status */ +} __packed; + +/****************************************************************************** + * (7) + * Power Management Commands, Responses, Notifications: + * + *****************************************************************************/ + +/** + * struct iwl_powertable_cmd - Power Table Command + * @flags: See below: + * + * POWER_TABLE_CMD = 0x77 (command, has simple generic response) + * + * PM allow: + * bit 0 - '0' Driver not allow power management + * '1' Driver allow PM (use rest of parameters) + * + * uCode send sleep notifications: + * bit 1 - '0' Don't send sleep notification + * '1' send sleep notification (SEND_PM_NOTIFICATION) + * + * Sleep over DTIM + * bit 2 - '0' PM have to walk up every DTIM + * '1' PM could sleep over DTIM till listen Interval. + * + * PCI power managed + * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1) + * '1' !(PCI_CFG_LINK_CTRL & 0x1) + * + * Fast PD + * bit 4 - '1' Put radio to sleep when receiving frame for others + * + * Force sleep Modes + * bit 31/30- '00' use both mac/xtal sleeps + * '01' force Mac sleep + * '10' force xtal sleep + * '11' Illegal set + * + * NOTE: if sleep_interval[SLEEP_INTRVL_TABLE_SIZE-1] > DTIM period then + * ucode assume sleep over DTIM is allowed and we don't need to wake up + * for every DTIM. + */ +#define IWL_POWER_VEC_SIZE 5 + +#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) +#define IWL_POWER_POWER_SAVE_ENA_MSK cpu_to_le16(BIT(0)) +#define IWL_POWER_POWER_MANAGEMENT_ENA_MSK cpu_to_le16(BIT(1)) +#define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2)) +#define IWL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) +#define IWL_POWER_FAST_PD cpu_to_le16(BIT(4)) +#define IWL_POWER_BEACON_FILTERING cpu_to_le16(BIT(5)) +#define IWL_POWER_SHADOW_REG_ENA cpu_to_le16(BIT(6)) +#define IWL_POWER_CT_KILL_SET cpu_to_le16(BIT(7)) +#define IWL_POWER_BT_SCO_ENA cpu_to_le16(BIT(8)) +#define IWL_POWER_ADVANCE_PM_ENA_MSK cpu_to_le16(BIT(9)) + +struct iwl_powertable_cmd { + __le16 flags; + u8 keep_alive_seconds; + u8 debug_flags; + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 sleep_interval[IWL_POWER_VEC_SIZE]; + __le32 keep_alive_beacons; +} __packed; + +/* + * PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command) + * all devices identical. + */ +struct iwl_sleep_notification { + u8 pm_sleep_mode; + u8 pm_wakeup_src; + __le16 reserved; + __le32 sleep_time; + __le32 tsf_low; + __le32 bcon_timer; +} __packed; + +/* Sleep states. all devices identical. */ +enum { + IWL_PM_NO_SLEEP = 0, + IWL_PM_SLP_MAC = 1, + IWL_PM_SLP_FULL_MAC_UNASSOCIATE = 2, + IWL_PM_SLP_FULL_MAC_CARD_STATE = 3, + IWL_PM_SLP_PHY = 4, + IWL_PM_SLP_REPENT = 5, + IWL_PM_WAKEUP_BY_TIMER = 6, + IWL_PM_WAKEUP_BY_DRIVER = 7, + IWL_PM_WAKEUP_BY_RFKILL = 8, + /* 3 reserved */ + IWL_PM_NUM_OF_MODES = 12, +}; + +/* + * REPLY_CARD_STATE_CMD = 0xa0 (command, has simple generic response) + */ +#define CARD_STATE_CMD_DISABLE 0x00 /* Put card to sleep */ +#define CARD_STATE_CMD_ENABLE 0x01 /* Wake up card */ +#define CARD_STATE_CMD_HALT 0x02 /* Power down permanently */ +struct iwl_card_state_cmd { + __le32 status; /* CARD_STATE_CMD_* request new power state */ +} __packed; + +/* + * CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command) + */ +struct iwl_card_state_notif { + __le32 flags; +} __packed; + +#define HW_CARD_DISABLED 0x01 +#define SW_CARD_DISABLED 0x02 +#define CT_CARD_DISABLED 0x04 +#define RXON_CARD_DISABLED 0x10 + +struct iwl_ct_kill_config { + __le32 reserved; + __le32 critical_temperature_M; + __le32 critical_temperature_R; +} __packed; + +/* 1000, and 6x00 */ +struct iwl_ct_kill_throttling_config { + __le32 critical_temperature_exit; + __le32 reserved; + __le32 critical_temperature_enter; +} __packed; + +/****************************************************************************** + * (8) + * Scan Commands, Responses, Notifications: + * + *****************************************************************************/ + +#define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0) +#define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1) + +/** + * struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table + * + * One for each channel in the scan list. + * Each channel can independently select: + * 1) SSID for directed active scans + * 2) Txpower setting (for rate specified within Tx command) + * 3) How long to stay on-channel (behavior may be modified by quiet_time, + * quiet_plcp_th, good_CRC_th) + * + * To avoid uCode errors, make sure the following are true (see comments + * under struct iwl_scan_cmd about max_out_time and quiet_time): + * 1) If using passive_dwell (i.e. passive_dwell != 0): + * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0) + * 2) quiet_time <= active_dwell + * 3) If restricting off-channel time (i.e. max_out_time !=0): + * passive_dwell < max_out_time + * active_dwell < max_out_time + */ + +struct iwl_scan_channel { + /* + * type is defined as: + * 0:0 1 = active, 0 = passive + * 1:20 SSID direct bit map; if a bit is set, then corresponding + * SSID IE is transmitted in probe request. + * 21:31 reserved + */ + __le32 type; + __le16 channel; /* band is selected by iwl_scan_cmd "flags" field */ + u8 tx_gain; /* gain for analog radio */ + u8 dsp_atten; /* gain for DSP */ + __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ + __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ +} __packed; + +/* set number of direct probes __le32 type */ +#define IWL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1)))) + +/** + * struct iwl_ssid_ie - directed scan network information element + * + * Up to 20 of these may appear in REPLY_SCAN_CMD, + * selected by "type" bit field in struct iwl_scan_channel; + * each channel may select different ssids from among the 20 entries. + * SSID IEs get transmitted in reverse order of entry. + */ +struct iwl_ssid_ie { + u8 id; + u8 len; + u8 ssid[32]; +} __packed; + +#define PROBE_OPTION_MAX 20 +#define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF) +#define IWL_GOOD_CRC_TH_DISABLED 0 +#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) +#define IWL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff) +#define IWL_MAX_CMD_SIZE 4096 + +/* + * REPLY_SCAN_CMD = 0x80 (command) + * + * The hardware scan command is very powerful; the driver can set it up to + * maintain (relatively) normal network traffic while doing a scan in the + * background. The max_out_time and suspend_time control the ratio of how + * long the device stays on an associated network channel ("service channel") + * vs. how long it's away from the service channel, i.e. tuned to other channels + * for scanning. + * + * max_out_time is the max time off-channel (in usec), and suspend_time + * is how long (in "extended beacon" format) that the scan is "suspended" + * after returning to the service channel. That is, suspend_time is the + * time that we stay on the service channel, doing normal work, between + * scan segments. The driver may set these parameters differently to support + * scanning when associated vs. not associated, and light vs. heavy traffic + * loads when associated. + * + * After receiving this command, the device's scan engine does the following; + * + * 1) Sends SCAN_START notification to driver + * 2) Checks to see if it has time to do scan for one channel + * 3) Sends NULL packet, with power-save (PS) bit set to 1, + * to tell AP that we're going off-channel + * 4) Tunes to first channel in scan list, does active or passive scan + * 5) Sends SCAN_RESULT notification to driver + * 6) Checks to see if it has time to do scan on *next* channel in list + * 7) Repeats 4-6 until it no longer has time to scan the next channel + * before max_out_time expires + * 8) Returns to service channel + * 9) Sends NULL packet with PS=0 to tell AP that we're back + * 10) Stays on service channel until suspend_time expires + * 11) Repeats entire process 2-10 until list is complete + * 12) Sends SCAN_COMPLETE notification + * + * For fast, efficient scans, the scan command also has support for staying on + * a channel for just a short time, if doing active scanning and getting no + * responses to the transmitted probe request. This time is controlled by + * quiet_time, and the number of received packets below which a channel is + * considered "quiet" is controlled by quiet_plcp_threshold. + * + * For active scanning on channels that have regulatory restrictions against + * blindly transmitting, the scan can listen before transmitting, to make sure + * that there is already legitimate activity on the channel. If enough + * packets are cleanly received on the channel (controlled by good_CRC_th, + * typical value 1), the scan engine starts transmitting probe requests. + * + * Driver must use separate scan commands for 2.4 vs. 5 GHz bands. + * + * To avoid uCode errors, see timing restrictions described under + * struct iwl_scan_channel. + */ + +enum iwl_scan_flags { + /* BIT(0) currently unused */ + IWL_SCAN_FLAGS_ACTION_FRAME_TX = BIT(1), + /* bits 2-7 reserved */ +}; + +struct iwl_scan_cmd { + __le16 len; + u8 scan_flags; /* scan flags: see enum iwl_scan_flags */ + u8 channel_count; /* # channels in channel list */ + __le16 quiet_time; /* dwell only this # millisecs on quiet channel + * (only for active scan) */ + __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ + __le16 good_CRC_th; /* passive -> active promotion threshold */ + __le16 rx_chain; /* RXON_RX_CHAIN_* */ + __le32 max_out_time; /* max usec to be away from associated (service) + * channel */ + __le32 suspend_time; /* pause scan this long (in "extended beacon + * format") when returning to service chnl: + */ + __le32 flags; /* RXON_FLG_* */ + __le32 filter_flags; /* RXON_FILTER_* */ + + /* For active scans (set to all-0s for passive scans). + * Does not include payload. Must specify Tx rate; no rate scaling. */ + struct iwl_tx_cmd tx_cmd; + + /* For directed active scans (set to all-0s otherwise) */ + struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; + + /* + * Probe request frame, followed by channel list. + * + * Size of probe request frame is specified by byte count in tx_cmd. + * Channel list follows immediately after probe request frame. + * Number of channels in list is specified by channel_count. + * Each channel in list is of type: + * + * struct iwl_scan_channel channels[0]; + * + * NOTE: Only one band of channels can be scanned per pass. You + * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait + * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION) + * before requesting another scan. + */ + u8 data[0]; +} __packed; + +/* Can abort will notify by complete notification with abort status. */ +#define CAN_ABORT_STATUS cpu_to_le32(0x1) +/* complete notification statuses */ +#define ABORT_STATUS 0x2 + +/* + * REPLY_SCAN_CMD = 0x80 (response) + */ +struct iwl_scanreq_notification { + __le32 status; /* 1: okay, 2: cannot fulfill request */ +} __packed; + +/* + * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command) + */ +struct iwl_scanstart_notification { + __le32 tsf_low; + __le32 tsf_high; + __le32 beacon_timer; + u8 channel; + u8 band; + u8 reserved[2]; + __le32 status; +} __packed; + +#define SCAN_OWNER_STATUS 0x1 +#define MEASURE_OWNER_STATUS 0x2 + +#define IWL_PROBE_STATUS_OK 0 +#define IWL_PROBE_STATUS_TX_FAILED BIT(0) +/* error statuses combined with TX_FAILED */ +#define IWL_PROBE_STATUS_FAIL_TTL BIT(1) +#define IWL_PROBE_STATUS_FAIL_BT BIT(2) + +#define NUMBER_OF_STATISTICS 1 /* first __le32 is good CRC */ +/* + * SCAN_RESULTS_NOTIFICATION = 0x83 (notification only, not a command) + */ +struct iwl_scanresults_notification { + u8 channel; + u8 band; + u8 probe_status; + u8 num_probe_not_sent; /* not enough time to send */ + __le32 tsf_low; + __le32 tsf_high; + __le32 statistics[NUMBER_OF_STATISTICS]; +} __packed; + +/* + * SCAN_COMPLETE_NOTIFICATION = 0x84 (notification only, not a command) + */ +struct iwl_scancomplete_notification { + u8 scanned_channels; + u8 status; + u8 bt_status; /* BT On/Off status */ + u8 last_channel; + __le32 tsf_low; + __le32 tsf_high; +} __packed; + + +/****************************************************************************** + * (9) + * IBSS/AP Commands and Notifications: + * + *****************************************************************************/ + +enum iwl_ibss_manager { + IWL_NOT_IBSS_MANAGER = 0, + IWL_IBSS_MANAGER = 1, +}; + +/* + * BEACON_NOTIFICATION = 0x90 (notification only, not a command) + */ + +struct iwlagn_beacon_notif { + struct iwlagn_tx_resp beacon_notify_hdr; + __le32 low_tsf; + __le32 high_tsf; + __le32 ibss_mgr_status; +} __packed; + +/* + * REPLY_TX_BEACON = 0x91 (command, has simple generic response) + */ + +struct iwl_tx_beacon_cmd { + struct iwl_tx_cmd tx; + __le16 tim_idx; + u8 tim_size; + u8 reserved1; + struct ieee80211_hdr frame[0]; /* beacon frame */ +} __packed; + +/****************************************************************************** + * (10) + * Statistics Commands and Notifications: + * + *****************************************************************************/ + +#define IWL_TEMP_CONVERT 260 + +#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 +#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 +#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 + +/* Used for passing to driver number of successes and failures per rate */ +struct rate_histogram { + union { + __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } success; + union { + __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } failed; +} __packed; + +/* statistics command response */ + +struct statistics_dbg { + __le32 burst_check; + __le32 burst_count; + __le32 wait_for_silence_timeout_cnt; + __le32 reserved[3]; +} __packed; + +struct statistics_rx_phy { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_limit_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 mh_format_err; + __le32 re_acq_main_rssi_sum; + __le32 reserved3; +} __packed; + +struct statistics_rx_ht_phy { + __le32 plcp_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 crc32_err; + __le32 mh_format_err; + __le32 agg_crc32_good; + __le32 agg_mpdu_cnt; + __le32 agg_cnt; + __le32 unsupport_mcs; +} __packed; + +#define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1) + +struct statistics_rx_non_phy { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ + __le32 channel_beacons; /* beacons with our bss id and in our + * serving channel */ + __le32 num_missed_bcon; /* number of missed beacons */ + __le32 adc_rx_saturation_time; /* count in 0.8us units the time the + * ADC was in saturation */ + __le32 ina_detection_search_time;/* total time (in 0.8us) searched + * for INA */ + __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ + __le32 interference_data_flag; /* flag for interference data + * availability. 1 when data is + * available. */ + __le32 channel_load; /* counts RX Enable time in uSec */ + __le32 dsp_false_alarms; /* DSP false alarm (both OFDM + * and CCK) counter */ + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 beacon_rssi_c; + __le32 beacon_energy_a; + __le32 beacon_energy_b; + __le32 beacon_energy_c; +} __packed; + +struct statistics_rx_non_phy_bt { + struct statistics_rx_non_phy common; + /* additional stats for bt */ + __le32 num_bt_kills; + __le32 reserved[2]; +} __packed; + +struct statistics_rx { + struct statistics_rx_phy ofdm; + struct statistics_rx_phy cck; + struct statistics_rx_non_phy general; + struct statistics_rx_ht_phy ofdm_ht; +} __packed; + +struct statistics_rx_bt { + struct statistics_rx_phy ofdm; + struct statistics_rx_phy cck; + struct statistics_rx_non_phy_bt general; + struct statistics_rx_ht_phy ofdm_ht; +} __packed; + +/** + * struct statistics_tx_power - current tx power + * + * @ant_a: current tx power on chain a in 1/2 dB step + * @ant_b: current tx power on chain b in 1/2 dB step + * @ant_c: current tx power on chain c in 1/2 dB step + */ +struct statistics_tx_power { + u8 ant_a; + u8 ant_b; + u8 ant_c; + u8 reserved; +} __packed; + +struct statistics_tx_non_phy_agg { + __le32 ba_timeout; + __le32 ba_reschedule_frames; + __le32 scd_query_agg_frame_cnt; + __le32 scd_query_no_agg; + __le32 scd_query_agg; + __le32 scd_query_mismatch; + __le32 frame_not_ready; + __le32 underrun; + __le32 bt_prio_kill; + __le32 rx_ba_rsp_cnt; +} __packed; + +struct statistics_tx { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; + __le32 dump_msdu_cnt; + __le32 burst_abort_next_frame_mismatch_cnt; + __le32 burst_abort_missing_next_frame_cnt; + __le32 cts_timeout_collision; + __le32 ack_or_ba_timeout_collision; + struct statistics_tx_non_phy_agg agg; + /* + * "tx_power" are optional parameters provided by uCode, + * 6000 series is the only device provide the information, + * Those are reserved fields for all the other devices + */ + struct statistics_tx_power tx_power; + __le32 reserved1; +} __packed; + + +struct statistics_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; + __le32 reserved1; + __le32 reserved2; +} __packed; + +struct statistics_general_common { + __le32 temperature; /* radio temperature */ + __le32 temperature_m; /* radio voltage */ + struct statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct statistics_div div; + __le32 rx_enable_counter; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; +} __packed; + +struct statistics_bt_activity { + /* Tx statistics */ + __le32 hi_priority_tx_req_cnt; + __le32 hi_priority_tx_denied_cnt; + __le32 lo_priority_tx_req_cnt; + __le32 lo_priority_tx_denied_cnt; + /* Rx statistics */ + __le32 hi_priority_rx_req_cnt; + __le32 hi_priority_rx_denied_cnt; + __le32 lo_priority_rx_req_cnt; + __le32 lo_priority_rx_denied_cnt; +} __packed; + +struct statistics_general { + struct statistics_general_common common; + __le32 reserved2; + __le32 reserved3; +} __packed; + +struct statistics_general_bt { + struct statistics_general_common common; + struct statistics_bt_activity activity; + __le32 reserved2; + __le32 reserved3; +} __packed; + +#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0) +#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1) +#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2) + +/* + * REPLY_STATISTICS_CMD = 0x9c, + * all devices identical. + * + * This command triggers an immediate response containing uCode statistics. + * The response is in the same format as STATISTICS_NOTIFICATION 0x9d, below. + * + * If the CLEAR_STATS configuration flag is set, uCode will clear its + * internal copy of the statistics (counters) after issuing the response. + * This flag does not affect STATISTICS_NOTIFICATIONs after beacons (see below). + * + * If the DISABLE_NOTIF configuration flag is set, uCode will not issue + * STATISTICS_NOTIFICATIONs after received beacons (see below). This flag + * does not affect the response to the REPLY_STATISTICS_CMD 0x9c itself. + */ +#define IWL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */ +#define IWL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2)/* see above */ +struct iwl_statistics_cmd { + __le32 configuration_flags; /* IWL_STATS_CONF_* */ +} __packed; + +/* + * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) + * + * By default, uCode issues this notification after receiving a beacon + * while associated. To disable this behavior, set DISABLE_NOTIF flag in the + * REPLY_STATISTICS_CMD 0x9c, above. + * + * Statistics counters continue to increment beacon after beacon, but are + * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD + * 0x9c with CLEAR_STATS bit set (see above). + * + * uCode also issues this notification during scans. uCode clears statistics + * appropriately so that each notification contains statistics for only the + * one channel that has just been scanned. + */ +#define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2) +#define STATISTICS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8) + +struct iwl_notif_statistics { + __le32 flag; + struct statistics_rx rx; + struct statistics_tx tx; + struct statistics_general general; +} __packed; + +struct iwl_bt_notif_statistics { + __le32 flag; + struct statistics_rx_bt rx; + struct statistics_tx tx; + struct statistics_general_bt general; +} __packed; + +/* + * MISSED_BEACONS_NOTIFICATION = 0xa2 (notification only, not a command) + * + * uCode send MISSED_BEACONS_NOTIFICATION to driver when detect beacon missed + * in regardless of how many missed beacons, which mean when driver receive the + * notification, inside the command, it can find all the beacons information + * which include number of total missed beacons, number of consecutive missed + * beacons, number of beacons received and number of beacons expected to + * receive. + * + * If uCode detected consecutive_missed_beacons > 5, it will reset the radio + * in order to bring the radio/PHY back to working state; which has no relation + * to when driver will perform sensitivity calibration. + * + * Driver should set it own missed_beacon_threshold to decide when to perform + * sensitivity calibration based on number of consecutive missed beacons in + * order to improve overall performance, especially in noisy environment. + * + */ + +#define IWL_MISSED_BEACON_THRESHOLD_MIN (1) +#define IWL_MISSED_BEACON_THRESHOLD_DEF (5) +#define IWL_MISSED_BEACON_THRESHOLD_MAX IWL_MISSED_BEACON_THRESHOLD_DEF + +struct iwl_missed_beacon_notif { + __le32 consecutive_missed_beacons; + __le32 total_missed_becons; + __le32 num_expected_beacons; + __le32 num_recvd_beacons; +} __packed; + + +/****************************************************************************** + * (11) + * Rx Calibration Commands: + * + * With the uCode used for open source drivers, most Tx calibration (except + * for Tx Power) and most Rx calibration is done by uCode during the + * "initialize" phase of uCode boot. Driver must calibrate only: + * + * 1) Tx power (depends on temperature), described elsewhere + * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas) + * 3) Receiver sensitivity (to optimize signal detection) + * + *****************************************************************************/ + +/** + * SENSITIVITY_CMD = 0xa8 (command, has simple generic response) + * + * This command sets up the Rx signal detector for a sensitivity level that + * is high enough to lock onto all signals within the associated network, + * but low enough to ignore signals that are below a certain threshold, so as + * not to have too many "false alarms". False alarms are signals that the + * Rx DSP tries to lock onto, but then discards after determining that they + * are noise. + * + * The optimum number of false alarms is between 5 and 50 per 200 TUs + * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e. + * time listening, not transmitting). Driver must adjust sensitivity so that + * the ratio of actual false alarms to actual Rx time falls within this range. + * + * While associated, uCode delivers STATISTICS_NOTIFICATIONs after each + * received beacon. These provide information to the driver to analyze the + * sensitivity. Don't analyze statistics that come in from scanning, or any + * other non-associated-network source. Pertinent statistics include: + * + * From "general" statistics (struct statistics_rx_non_phy): + * + * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level) + * Measure of energy of desired signal. Used for establishing a level + * below which the device does not detect signals. + * + * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB) + * Measure of background noise in silent period after beacon. + * + * channel_load + * uSecs of actual Rx time during beacon period (varies according to + * how much time was spent transmitting). + * + * From "cck" and "ofdm" statistics (struct statistics_rx_phy), separately: + * + * false_alarm_cnt + * Signal locks abandoned early (before phy-level header). + * + * plcp_err + * Signal locks abandoned late (during phy-level header). + * + * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from + * beacon to beacon, i.e. each value is an accumulation of all errors + * before and including the latest beacon. Values will wrap around to 0 + * after counting up to 2^32 - 1. Driver must differentiate vs. + * previous beacon's values to determine # false alarms in the current + * beacon period. + * + * Total number of false alarms = false_alarms + plcp_errs + * + * For OFDM, adjust the following table entries in struct iwl_sensitivity_cmd + * (notice that the start points for OFDM are at or close to settings for + * maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX 90 / 85 / 120 + * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX 170 / 170 / 210 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX 105 / 105 / 140 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX 220 / 220 / 270 + * + * If actual rate of OFDM false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), reduce sensitivity + * by *adding* 1 to all 4 of the table entries above, up to the max for + * each entry. Conversely, if false alarm rate is too low (less than 5 + * for each 204.8 msecs listening), *subtract* 1 from each entry to + * increase sensitivity. + * + * For CCK sensitivity, keep track of the following: + * + * 1). 20-beacon history of maximum background noise, indicated by + * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the + * 3 receivers. For any given beacon, the "silence reference" is + * the maximum of last 60 samples (20 beacons * 3 receivers). + * + * 2). 10-beacon history of strongest signal level, as indicated + * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers, + * i.e. the strength of the signal through the best receiver at the + * moment. These measurements are "upside down", with lower values + * for stronger signals, so max energy will be *minimum* value. + * + * Then for any given beacon, the driver must determine the *weakest* + * of the strongest signals; this is the minimum level that needs to be + * successfully detected, when using the best receiver at the moment. + * "Max cck energy" is the maximum (higher value means lower energy!) + * of the last 10 minima. Once this is determined, driver must add + * a little margin by adding "6" to it. + * + * 3). Number of consecutive beacon periods with too few false alarms. + * Reset this to 0 at the first beacon period that falls within the + * "good" range (5 to 50 false alarms per 204.8 milliseconds rx). + * + * Then, adjust the following CCK table entries in struct iwl_sensitivity_cmd + * (notice that the start points for CCK are at maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX 125 / 125 / 200 + * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX 200 / 200 / 400 + * HD_MIN_ENERGY_CCK_DET_INDEX 100 / 0 / 100 + * + * If actual rate of CCK false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), method for reducing + * sensitivity is: + * + * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, + * up to max 400. + * + * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is < 160, + * sensitivity has been reduced a significant amount; bring it up to + * a moderate 161. Otherwise, *add* 3, up to max 200. + * + * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is > 160, + * sensitivity has been reduced only a moderate or small amount; + * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_INDEX, + * down to min 0. Otherwise (if gain has been significantly reduced), + * don't change the HD_MIN_ENERGY_CCK_DET_INDEX value. + * + * b) Save a snapshot of the "silence reference". + * + * If actual rate of CCK false alarms (+ plcp_errors) is too low + * (less than 5 for each 204.8 msecs listening), method for increasing + * sensitivity is used only if: + * + * 1a) Previous beacon did not have too many false alarms + * 1b) AND difference between previous "silence reference" and current + * "silence reference" (prev - current) is 2 or more, + * OR 2) 100 or more consecutive beacon periods have had rate of + * less than 5 false alarms per 204.8 milliseconds rx time. + * + * Method for increasing sensitivity: + * + * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX, + * down to min 125. + * + * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, + * down to min 200. + * + * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_INDEX, up to max 100. + * + * If actual rate of CCK false alarms (+ plcp_errors) is within good range + * (between 5 and 50 for each 204.8 msecs listening): + * + * 1) Save a snapshot of the silence reference. + * + * 2) If previous beacon had too many CCK false alarms (+ plcp_errors), + * give some extra margin to energy threshold by *subtracting* 8 + * from value in HD_MIN_ENERGY_CCK_DET_INDEX. + * + * For all cases (too few, too many, good range), make sure that the CCK + * detection threshold (energy) is below the energy level for robust + * detection over the past 10 beacon periods, the "Max cck energy". + * Lower values mean higher energy; this means making sure that the value + * in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy". + * + */ + +/* + * Table entries in SENSITIVITY_CMD (struct iwl_sensitivity_cmd) + */ +#define HD_TABLE_SIZE (11) /* number of entries */ +#define HD_MIN_ENERGY_CCK_DET_INDEX (0) /* table indexes */ +#define HD_MIN_ENERGY_OFDM_DET_INDEX (1) +#define HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX (2) +#define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX (3) +#define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX (4) +#define HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX (5) +#define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX (6) +#define HD_BARKER_CORR_TH_ADD_MIN_INDEX (7) +#define HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX (8) +#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9) +#define HD_OFDM_ENERGY_TH_IN_INDEX (10) + +/* + * Additional table entries in enhance SENSITIVITY_CMD + */ +#define HD_INA_NON_SQUARE_DET_OFDM_INDEX (11) +#define HD_INA_NON_SQUARE_DET_CCK_INDEX (12) +#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX (13) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX (14) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (15) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX (16) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX (17) +#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX (18) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (19) +#define HD_CCK_NON_SQUARE_DET_SLOPE_INDEX (20) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX (21) +#define HD_RESERVED (22) + +/* number of entries for enhanced tbl */ +#define ENHANCE_HD_TABLE_SIZE (23) + +/* number of additional entries for enhanced tbl */ +#define ENHANCE_HD_TABLE_ENTRIES (ENHANCE_HD_TABLE_SIZE - HD_TABLE_SIZE) + +#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V1 cpu_to_le16(0) +#define HD_INA_NON_SQUARE_DET_CCK_DATA_V1 cpu_to_le16(0) +#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1 cpu_to_le16(0) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(668) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(486) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(37) +#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(853) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4) +#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(476) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(99) + +#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V2 cpu_to_le16(1) +#define HD_INA_NON_SQUARE_DET_CCK_DATA_V2 cpu_to_le16(1) +#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2 cpu_to_le16(1) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(600) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(40) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(486) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(45) +#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(853) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(60) +#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(476) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(99) + + +/* Control field in struct iwl_sensitivity_cmd */ +#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE cpu_to_le16(0) +#define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1) + +/** + * struct iwl_sensitivity_cmd + * @control: (1) updates working table, (0) updates default table + * @table: energy threshold values, use HD_* as index into table + * + * Always use "1" in "control" to update uCode's working table and DSP. + */ +struct iwl_sensitivity_cmd { + __le16 control; /* always use "1" */ + __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */ +} __packed; + +/* + * + */ +struct iwl_enhance_sensitivity_cmd { + __le16 control; /* always use "1" */ + __le16 enhance_table[ENHANCE_HD_TABLE_SIZE]; /* use HD_* as index */ +} __packed; + + +/** + * REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response) + * + * This command sets the relative gains of agn device's 3 radio receiver chains. + * + * After the first association, driver should accumulate signal and noise + * statistics from the STATISTICS_NOTIFICATIONs that follow the first 20 + * beacons from the associated network (don't collect statistics that come + * in from scanning, or any other non-network source). + * + * DISCONNECTED ANTENNA: + * + * Driver should determine which antennas are actually connected, by comparing + * average beacon signal levels for the 3 Rx chains. Accumulate (add) the + * following values over 20 beacons, one accumulator for each of the chains + * a/b/c, from struct statistics_rx_non_phy: + * + * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the strongest signal from among a/b/c. Compare the other two to the + * strongest. If any signal is more than 15 dB (times 20, unless you + * divide the accumulated values by 20) below the strongest, the driver + * considers that antenna to be disconnected, and should not try to use that + * antenna/chain for Rx or Tx. If both A and B seem to be disconnected, + * driver should declare the stronger one as connected, and attempt to use it + * (A and B are the only 2 Tx chains!). + * + * + * RX BALANCE: + * + * Driver should balance the 3 receivers (but just the ones that are connected + * to antennas, see above) for gain, by comparing the average signal levels + * detected during the silence after each beacon (background noise). + * Accumulate (add) the following values over 20 beacons, one accumulator for + * each of the chains a/b/c, from struct statistics_rx_non_phy: + * + * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the weakest background noise level from among a/b/c. This Rx chain + * will be the reference, with 0 gain adjustment. Attenuate other channels by + * finding noise difference: + * + * (accum_noise[i] - accum_noise[reference]) / 30 + * + * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB. + * For use in diff_gain_[abc] fields of struct iwl_calibration_cmd, the + * driver should limit the difference results to a range of 0-3 (0-4.5 dB), + * and set bit 2 to indicate "reduce gain". The value for the reference + * (weakest) chain should be "0". + * + * diff_gain_[abc] bit fields: + * 2: (1) reduce gain, (0) increase gain + * 1-0: amount of gain, units of 1.5 dB + */ + +/* Phy calibration command for series */ +enum { + IWL_PHY_CALIBRATE_DC_CMD = 8, + IWL_PHY_CALIBRATE_LO_CMD = 9, + IWL_PHY_CALIBRATE_TX_IQ_CMD = 11, + IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15, + IWL_PHY_CALIBRATE_BASE_BAND_CMD = 16, + IWL_PHY_CALIBRATE_TX_IQ_PERD_CMD = 17, + IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD = 18, +}; + +/* This enum defines the bitmap of various calibrations to enable in both + * init ucode and runtime ucode through CALIBRATION_CFG_CMD. + */ +enum iwl_ucode_calib_cfg { + IWL_CALIB_CFG_RX_BB_IDX = BIT(0), + IWL_CALIB_CFG_DC_IDX = BIT(1), + IWL_CALIB_CFG_LO_IDX = BIT(2), + IWL_CALIB_CFG_TX_IQ_IDX = BIT(3), + IWL_CALIB_CFG_RX_IQ_IDX = BIT(4), + IWL_CALIB_CFG_NOISE_IDX = BIT(5), + IWL_CALIB_CFG_CRYSTAL_IDX = BIT(6), + IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(7), + IWL_CALIB_CFG_PAPD_IDX = BIT(8), + IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(9), + IWL_CALIB_CFG_TX_PWR_IDX = BIT(10), +}; + +#define IWL_CALIB_INIT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \ + IWL_CALIB_CFG_DC_IDX | \ + IWL_CALIB_CFG_LO_IDX | \ + IWL_CALIB_CFG_TX_IQ_IDX | \ + IWL_CALIB_CFG_RX_IQ_IDX | \ + IWL_CALIB_CFG_CRYSTAL_IDX) + +#define IWL_CALIB_RT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \ + IWL_CALIB_CFG_DC_IDX | \ + IWL_CALIB_CFG_LO_IDX | \ + IWL_CALIB_CFG_TX_IQ_IDX | \ + IWL_CALIB_CFG_RX_IQ_IDX | \ + IWL_CALIB_CFG_TEMPERATURE_IDX | \ + IWL_CALIB_CFG_PAPD_IDX | \ + IWL_CALIB_CFG_TX_PWR_IDX | \ + IWL_CALIB_CFG_CRYSTAL_IDX) + +#define IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK cpu_to_le32(BIT(0)) + +struct iwl_calib_cfg_elmnt_s { + __le32 is_enable; + __le32 start; + __le32 send_res; + __le32 apply_res; + __le32 reserved; +} __packed; + +struct iwl_calib_cfg_status_s { + struct iwl_calib_cfg_elmnt_s once; + struct iwl_calib_cfg_elmnt_s perd; + __le32 flags; +} __packed; + +struct iwl_calib_cfg_cmd { + struct iwl_calib_cfg_status_s ucd_calib_cfg; + struct iwl_calib_cfg_status_s drv_calib_cfg; + __le32 reserved1; +} __packed; + +struct iwl_calib_hdr { + u8 op_code; + u8 first_group; + u8 groups_num; + u8 data_valid; +} __packed; + +struct iwl_calib_cmd { + struct iwl_calib_hdr hdr; + u8 data[0]; +} __packed; + +struct iwl_calib_xtal_freq_cmd { + struct iwl_calib_hdr hdr; + u8 cap_pin1; + u8 cap_pin2; + u8 pad[2]; +} __packed; + +#define DEFAULT_RADIO_SENSOR_OFFSET cpu_to_le16(2700) +struct iwl_calib_temperature_offset_cmd { + struct iwl_calib_hdr hdr; + __le16 radio_sensor_offset; + __le16 reserved; +} __packed; + +struct iwl_calib_temperature_offset_v2_cmd { + struct iwl_calib_hdr hdr; + __le16 radio_sensor_offset_high; + __le16 radio_sensor_offset_low; + __le16 burntVoltageRef; + __le16 reserved; +} __packed; + +/* IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */ +struct iwl_calib_chain_noise_reset_cmd { + struct iwl_calib_hdr hdr; + u8 data[0]; +}; + +/* IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD */ +struct iwl_calib_chain_noise_gain_cmd { + struct iwl_calib_hdr hdr; + u8 delta_gain_1; + u8 delta_gain_2; + u8 pad[2]; +} __packed; + +/****************************************************************************** + * (12) + * Miscellaneous Commands: + * + *****************************************************************************/ + +/* + * LEDs Command & Response + * REPLY_LEDS_CMD = 0x48 (command, has simple generic response) + * + * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field), + * this command turns it on or off, or sets up a periodic blinking cycle. + */ +struct iwl_led_cmd { + __le32 interval; /* "interval" in uSec */ + u8 id; /* 1: Activity, 2: Link, 3: Tech */ + u8 off; /* # intervals off while blinking; + * "0", with >0 "on" value, turns LED on */ + u8 on; /* # intervals on while blinking; + * "0", regardless of "off", turns LED off */ + u8 reserved; +} __packed; + +/* + * station priority table entries + * also used as potential "events" value for both + * COEX_MEDIUM_NOTIFICATION and COEX_EVENT_CMD + */ + +/* + * COEX events entry flag masks + * RP - Requested Priority + * WP - Win Medium Priority: priority assigned when the contention has been won + */ +#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG (0x1) +#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG (0x2) +#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG (0x4) + +#define COEX_CU_UNASSOC_IDLE_RP 4 +#define COEX_CU_UNASSOC_MANUAL_SCAN_RP 4 +#define COEX_CU_UNASSOC_AUTO_SCAN_RP 4 +#define COEX_CU_CALIBRATION_RP 4 +#define COEX_CU_PERIODIC_CALIBRATION_RP 4 +#define COEX_CU_CONNECTION_ESTAB_RP 4 +#define COEX_CU_ASSOCIATED_IDLE_RP 4 +#define COEX_CU_ASSOC_MANUAL_SCAN_RP 4 +#define COEX_CU_ASSOC_AUTO_SCAN_RP 4 +#define COEX_CU_ASSOC_ACTIVE_LEVEL_RP 4 +#define COEX_CU_RF_ON_RP 6 +#define COEX_CU_RF_OFF_RP 4 +#define COEX_CU_STAND_ALONE_DEBUG_RP 6 +#define COEX_CU_IPAN_ASSOC_LEVEL_RP 4 +#define COEX_CU_RSRVD1_RP 4 +#define COEX_CU_RSRVD2_RP 4 + +#define COEX_CU_UNASSOC_IDLE_WP 3 +#define COEX_CU_UNASSOC_MANUAL_SCAN_WP 3 +#define COEX_CU_UNASSOC_AUTO_SCAN_WP 3 +#define COEX_CU_CALIBRATION_WP 3 +#define COEX_CU_PERIODIC_CALIBRATION_WP 3 +#define COEX_CU_CONNECTION_ESTAB_WP 3 +#define COEX_CU_ASSOCIATED_IDLE_WP 3 +#define COEX_CU_ASSOC_MANUAL_SCAN_WP 3 +#define COEX_CU_ASSOC_AUTO_SCAN_WP 3 +#define COEX_CU_ASSOC_ACTIVE_LEVEL_WP 3 +#define COEX_CU_RF_ON_WP 3 +#define COEX_CU_RF_OFF_WP 3 +#define COEX_CU_STAND_ALONE_DEBUG_WP 6 +#define COEX_CU_IPAN_ASSOC_LEVEL_WP 3 +#define COEX_CU_RSRVD1_WP 3 +#define COEX_CU_RSRVD2_WP 3 + +#define COEX_UNASSOC_IDLE_FLAGS 0 +#define COEX_UNASSOC_MANUAL_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_UNASSOC_AUTO_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_CALIBRATION_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_PERIODIC_CALIBRATION_FLAGS 0 +/* + * COEX_CONNECTION_ESTAB: + * we need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. + */ +#define COEX_CONNECTION_ESTAB_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ + COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) +#define COEX_ASSOCIATED_IDLE_FLAGS 0 +#define COEX_ASSOC_MANUAL_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_ASSOC_AUTO_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0 +#define COEX_RF_ON_FLAGS 0 +#define COEX_RF_OFF_FLAGS 0 +#define COEX_STAND_ALONE_DEBUG_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_IPAN_ASSOC_LEVEL_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ + COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) +#define COEX_RSRVD1_FLAGS 0 +#define COEX_RSRVD2_FLAGS 0 +/* + * COEX_CU_RF_ON is the event wrapping all radio ownership. + * We need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. + */ +#define COEX_CU_RF_ON_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ + COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) + + +enum { + /* un-association part */ + COEX_UNASSOC_IDLE = 0, + COEX_UNASSOC_MANUAL_SCAN = 1, + COEX_UNASSOC_AUTO_SCAN = 2, + /* calibration */ + COEX_CALIBRATION = 3, + COEX_PERIODIC_CALIBRATION = 4, + /* connection */ + COEX_CONNECTION_ESTAB = 5, + /* association part */ + COEX_ASSOCIATED_IDLE = 6, + COEX_ASSOC_MANUAL_SCAN = 7, + COEX_ASSOC_AUTO_SCAN = 8, + COEX_ASSOC_ACTIVE_LEVEL = 9, + /* RF ON/OFF */ + COEX_RF_ON = 10, + COEX_RF_OFF = 11, + COEX_STAND_ALONE_DEBUG = 12, + /* IPAN */ + COEX_IPAN_ASSOC_LEVEL = 13, + /* reserved */ + COEX_RSRVD1 = 14, + COEX_RSRVD2 = 15, + COEX_NUM_OF_EVENTS = 16 +}; + +/* + * Coexistence WIFI/WIMAX Command + * COEX_PRIORITY_TABLE_CMD = 0x5a + * + */ +struct iwl_wimax_coex_event_entry { + u8 request_prio; + u8 win_medium_prio; + u8 reserved; + u8 flags; +} __packed; + +/* COEX flag masks */ + +/* Station table is valid */ +#define COEX_FLAGS_STA_TABLE_VALID_MSK (0x1) +/* UnMask wake up src at unassociated sleep */ +#define COEX_FLAGS_UNASSOC_WA_UNMASK_MSK (0x4) +/* UnMask wake up src at associated sleep */ +#define COEX_FLAGS_ASSOC_WA_UNMASK_MSK (0x8) +/* Enable CoEx feature. */ +#define COEX_FLAGS_COEX_ENABLE_MSK (0x80) + +struct iwl_wimax_coex_cmd { + u8 flags; + u8 reserved[3]; + struct iwl_wimax_coex_event_entry sta_prio[COEX_NUM_OF_EVENTS]; +} __packed; + +/* + * Coexistence MEDIUM NOTIFICATION + * COEX_MEDIUM_NOTIFICATION = 0x5b + * + * notification from uCode to host to indicate medium changes + * + */ +/* + * status field + * bit 0 - 2: medium status + * bit 3: medium change indication + * bit 4 - 31: reserved + */ +/* status option values, (0 - 2 bits) */ +#define COEX_MEDIUM_BUSY (0x0) /* radio belongs to WiMAX */ +#define COEX_MEDIUM_ACTIVE (0x1) /* radio belongs to WiFi */ +#define COEX_MEDIUM_PRE_RELEASE (0x2) /* received radio release */ +#define COEX_MEDIUM_MSK (0x7) + +/* send notification status (1 bit) */ +#define COEX_MEDIUM_CHANGED (0x8) +#define COEX_MEDIUM_CHANGED_MSK (0x8) +#define COEX_MEDIUM_SHIFT (3) + +struct iwl_coex_medium_notification { + __le32 status; + __le32 events; +} __packed; + +/* + * Coexistence EVENT Command + * COEX_EVENT_CMD = 0x5c + * + * send from host to uCode for coex event request. + */ +/* flags options */ +#define COEX_EVENT_REQUEST_MSK (0x1) + +struct iwl_coex_event_cmd { + u8 flags; + u8 event; + __le16 reserved; +} __packed; + +struct iwl_coex_event_resp { + __le32 status; +} __packed; + + +/****************************************************************************** + * Bluetooth Coexistence commands + * + *****************************************************************************/ + +/* + * BT Status notification + * REPLY_BT_COEX_PROFILE_NOTIF = 0xce + */ +enum iwl_bt_coex_profile_traffic_load { + IWL_BT_COEX_TRAFFIC_LOAD_NONE = 0, + IWL_BT_COEX_TRAFFIC_LOAD_LOW = 1, + IWL_BT_COEX_TRAFFIC_LOAD_HIGH = 2, + IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS = 3, +/* + * There are no more even though below is a u8, the + * indication from the BT device only has two bits. + */ +}; + +#define BT_SESSION_ACTIVITY_1_UART_MSG 0x1 +#define BT_SESSION_ACTIVITY_2_UART_MSG 0x2 + +/* BT UART message - Share Part (BT -> WiFi) */ +#define BT_UART_MSG_FRAME1MSGTYPE_POS (0) +#define BT_UART_MSG_FRAME1MSGTYPE_MSK \ + (0x7 << BT_UART_MSG_FRAME1MSGTYPE_POS) +#define BT_UART_MSG_FRAME1SSN_POS (3) +#define BT_UART_MSG_FRAME1SSN_MSK \ + (0x3 << BT_UART_MSG_FRAME1SSN_POS) +#define BT_UART_MSG_FRAME1UPDATEREQ_POS (5) +#define BT_UART_MSG_FRAME1UPDATEREQ_MSK \ + (0x1 << BT_UART_MSG_FRAME1UPDATEREQ_POS) +#define BT_UART_MSG_FRAME1RESERVED_POS (6) +#define BT_UART_MSG_FRAME1RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME1RESERVED_POS) + +#define BT_UART_MSG_FRAME2OPENCONNECTIONS_POS (0) +#define BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK \ + (0x3 << BT_UART_MSG_FRAME2OPENCONNECTIONS_POS) +#define BT_UART_MSG_FRAME2TRAFFICLOAD_POS (2) +#define BT_UART_MSG_FRAME2TRAFFICLOAD_MSK \ + (0x3 << BT_UART_MSG_FRAME2TRAFFICLOAD_POS) +#define BT_UART_MSG_FRAME2CHLSEQN_POS (4) +#define BT_UART_MSG_FRAME2CHLSEQN_MSK \ + (0x1 << BT_UART_MSG_FRAME2CHLSEQN_POS) +#define BT_UART_MSG_FRAME2INBAND_POS (5) +#define BT_UART_MSG_FRAME2INBAND_MSK \ + (0x1 << BT_UART_MSG_FRAME2INBAND_POS) +#define BT_UART_MSG_FRAME2RESERVED_POS (6) +#define BT_UART_MSG_FRAME2RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME2RESERVED_POS) + +#define BT_UART_MSG_FRAME3SCOESCO_POS (0) +#define BT_UART_MSG_FRAME3SCOESCO_MSK \ + (0x1 << BT_UART_MSG_FRAME3SCOESCO_POS) +#define BT_UART_MSG_FRAME3SNIFF_POS (1) +#define BT_UART_MSG_FRAME3SNIFF_MSK \ + (0x1 << BT_UART_MSG_FRAME3SNIFF_POS) +#define BT_UART_MSG_FRAME3A2DP_POS (2) +#define BT_UART_MSG_FRAME3A2DP_MSK \ + (0x1 << BT_UART_MSG_FRAME3A2DP_POS) +#define BT_UART_MSG_FRAME3ACL_POS (3) +#define BT_UART_MSG_FRAME3ACL_MSK \ + (0x1 << BT_UART_MSG_FRAME3ACL_POS) +#define BT_UART_MSG_FRAME3MASTER_POS (4) +#define BT_UART_MSG_FRAME3MASTER_MSK \ + (0x1 << BT_UART_MSG_FRAME3MASTER_POS) +#define BT_UART_MSG_FRAME3OBEX_POS (5) +#define BT_UART_MSG_FRAME3OBEX_MSK \ + (0x1 << BT_UART_MSG_FRAME3OBEX_POS) +#define BT_UART_MSG_FRAME3RESERVED_POS (6) +#define BT_UART_MSG_FRAME3RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME3RESERVED_POS) + +#define BT_UART_MSG_FRAME4IDLEDURATION_POS (0) +#define BT_UART_MSG_FRAME4IDLEDURATION_MSK \ + (0x3F << BT_UART_MSG_FRAME4IDLEDURATION_POS) +#define BT_UART_MSG_FRAME4RESERVED_POS (6) +#define BT_UART_MSG_FRAME4RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME4RESERVED_POS) + +#define BT_UART_MSG_FRAME5TXACTIVITY_POS (0) +#define BT_UART_MSG_FRAME5TXACTIVITY_MSK \ + (0x3 << BT_UART_MSG_FRAME5TXACTIVITY_POS) +#define BT_UART_MSG_FRAME5RXACTIVITY_POS (2) +#define BT_UART_MSG_FRAME5RXACTIVITY_MSK \ + (0x3 << BT_UART_MSG_FRAME5RXACTIVITY_POS) +#define BT_UART_MSG_FRAME5ESCORETRANSMIT_POS (4) +#define BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK \ + (0x3 << BT_UART_MSG_FRAME5ESCORETRANSMIT_POS) +#define BT_UART_MSG_FRAME5RESERVED_POS (6) +#define BT_UART_MSG_FRAME5RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME5RESERVED_POS) + +#define BT_UART_MSG_FRAME6SNIFFINTERVAL_POS (0) +#define BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK \ + (0x1F << BT_UART_MSG_FRAME6SNIFFINTERVAL_POS) +#define BT_UART_MSG_FRAME6DISCOVERABLE_POS (5) +#define BT_UART_MSG_FRAME6DISCOVERABLE_MSK \ + (0x1 << BT_UART_MSG_FRAME6DISCOVERABLE_POS) +#define BT_UART_MSG_FRAME6RESERVED_POS (6) +#define BT_UART_MSG_FRAME6RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME6RESERVED_POS) + +#define BT_UART_MSG_FRAME7SNIFFACTIVITY_POS (0) +#define BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK \ + (0x7 << BT_UART_MSG_FRAME7SNIFFACTIVITY_POS) +#define BT_UART_MSG_FRAME7PAGE_POS (3) +#define BT_UART_MSG_FRAME7PAGE_MSK \ + (0x1 << BT_UART_MSG_FRAME7PAGE_POS) +#define BT_UART_MSG_FRAME7INQUIRY_POS (4) +#define BT_UART_MSG_FRAME7INQUIRY_MSK \ + (0x1 << BT_UART_MSG_FRAME7INQUIRY_POS) +#define BT_UART_MSG_FRAME7CONNECTABLE_POS (5) +#define BT_UART_MSG_FRAME7CONNECTABLE_MSK \ + (0x1 << BT_UART_MSG_FRAME7CONNECTABLE_POS) +#define BT_UART_MSG_FRAME7RESERVED_POS (6) +#define BT_UART_MSG_FRAME7RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME7RESERVED_POS) + +/* BT Session Activity 2 UART message (BT -> WiFi) */ +#define BT_UART_MSG_2_FRAME1RESERVED1_POS (5) +#define BT_UART_MSG_2_FRAME1RESERVED1_MSK \ + (0x1< + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "iwl-debug.h" +#include "iwl-io.h" +#include "dev.h" +#include "agn.h" + +/* create and remove of files */ +#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, priv, \ + &iwl_dbgfs_##name##_ops)) \ + goto err; \ +} while (0) + +#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ +} while (0) + +#define DEBUGFS_ADD_X32(name, parent, ptr) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ +} while (0) + +#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_u32(#name, mode, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ +} while (0) + +/* file operation */ +#define DEBUGFS_READ_FILE_OPS(name) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_WRITE_FILE_OPS(name) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = iwl_dbgfs_##name##_write, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + + +#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = iwl_dbgfs_##name##_write, \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +static ssize_t iwl_dbgfs_sram_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + u32 val = 0; + char *buf; + ssize_t ret; + int i = 0; + bool device_format = false; + int offset = 0; + int len = 0; + int pos = 0; + int sram; + struct iwl_priv *priv = file->private_data; + const struct fw_img *img; + size_t bufsz; + + if (!iwl_is_ready_rf(priv)) + return -EAGAIN; + + /* default is to dump the entire data segment */ + if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) { + priv->dbgfs_sram_offset = 0x800000; + if (!priv->ucode_loaded) + return -EINVAL; + img = &priv->fw->img[priv->cur_ucode]; + priv->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + } + len = priv->dbgfs_sram_len; + + if (len == -4) { + device_format = true; + len = 4; + } + + bufsz = 50 + len * 4; + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", + len); + pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", + priv->dbgfs_sram_offset); + + /* adjust sram address since reads are only on even u32 boundaries */ + offset = priv->dbgfs_sram_offset & 0x3; + sram = priv->dbgfs_sram_offset & ~0x3; + + /* read the first u32 from sram */ + val = iwl_trans_read_mem32(priv->trans, sram); + + for (; len; len--) { + /* put the address at the start of every line */ + if (i == 0) + pos += scnprintf(buf + pos, bufsz - pos, + "%08X: ", sram + offset); + + if (device_format) + pos += scnprintf(buf + pos, bufsz - pos, + "%02x", (val >> (8 * (3 - offset))) & 0xff); + else + pos += scnprintf(buf + pos, bufsz - pos, + "%02x ", (val >> (8 * offset)) & 0xff); + + /* if all bytes processed, read the next u32 from sram */ + if (++offset == 4) { + sram += 4; + offset = 0; + val = iwl_trans_read_mem32(priv->trans, sram); + } + + /* put in extra spaces and split lines for human readability */ + if (++i == 16) { + i = 0; + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } else if (!(i & 7)) { + pos += scnprintf(buf + pos, bufsz - pos, " "); + } else if (!(i & 3)) { + pos += scnprintf(buf + pos, bufsz - pos, " "); + } + } + if (i) + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_sram_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[64]; + int buf_size; + u32 offset, len; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%x,%x", &offset, &len) == 2) { + priv->dbgfs_sram_offset = offset; + priv->dbgfs_sram_len = len; + } else if (sscanf(buf, "%x", &offset) == 1) { + priv->dbgfs_sram_offset = offset; + priv->dbgfs_sram_len = -4; + } else { + priv->dbgfs_sram_offset = 0; + priv->dbgfs_sram_len = 0; + } + + return count; +} + +static ssize_t iwl_dbgfs_wowlan_sram_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + const struct fw_img *img = &priv->fw->img[IWL_UCODE_WOWLAN]; + + if (!priv->wowlan_sram) + return -ENODATA; + + return simple_read_from_buffer(user_buf, count, ppos, + priv->wowlan_sram, + img->sec[IWL_UCODE_SECTION_DATA].len); +} +static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct iwl_station_entry *station; + struct iwl_tid_data *tid_data; + char *buf; + int i, j, pos = 0; + ssize_t ret; + /* Add 30 for initial string */ + const size_t bufsz = 30 + sizeof(char) * 500 * (priv->num_stations); + + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n", + priv->num_stations); + + for (i = 0; i < IWLAGN_STATION_COUNT; i++) { + station = &priv->stations[i]; + if (!station->used) + continue; + pos += scnprintf(buf + pos, bufsz - pos, + "station %d - addr: %pM, flags: %#x\n", + i, station->sta.sta.addr, + station->sta.station_flags_msk); + pos += scnprintf(buf + pos, bufsz - pos, + "TID seqno next_rclmd " + "rate_n_flags state txq\n"); + + for (j = 0; j < IWL_MAX_TID_COUNT; j++) { + tid_data = &priv->tid_data[i][j]; + pos += scnprintf(buf + pos, bufsz - pos, + "%d: 0x%.4x 0x%.4x 0x%.8x " + "%d %.2d", + j, tid_data->seq_number, + tid_data->next_reclaimed, + tid_data->agg.rate_n_flags, + tid_data->agg.state, + tid_data->agg.txq_id); + + if (tid_data->agg.wait_for_ba) + pos += scnprintf(buf + pos, bufsz - pos, + " - waitforba"); + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } + + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_nvm_read(struct file *file, + char __user *user_buf, + size_t count, + loff_t *ppos) +{ + ssize_t ret; + struct iwl_priv *priv = file->private_data; + int pos = 0, ofs = 0, buf_size = 0; + const u8 *ptr; + char *buf; + u16 nvm_ver; + size_t eeprom_len = priv->eeprom_blob_size; + buf_size = 4 * eeprom_len + 256; + + if (eeprom_len % 16) + return -ENODATA; + + ptr = priv->eeprom_blob; + if (!ptr) + return -ENOMEM; + + /* 4 characters for byte 0xYY */ + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + nvm_ver = priv->nvm_data->nvm_version; + pos += scnprintf(buf + pos, buf_size - pos, + "NVM version: 0x%x\n", nvm_ver); + for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) { + pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x %16ph\n", + ofs, ptr + ofs); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct ieee80211_channel *channels = NULL; + const struct ieee80211_supported_band *supp_band = NULL; + int pos = 0, i, bufsz = PAGE_SIZE; + char *buf; + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ); + if (supp_band) { + channels = supp_band->channels; + + pos += scnprintf(buf + pos, bufsz - pos, + "Displaying %d channels in 2.4GHz band 802.11bg):\n", + supp_band->n_channels); + + for (i = 0; i < supp_band->n_channels; i++) + pos += scnprintf(buf + pos, bufsz - pos, + "%d: %ddBm: BSS%s%s, %s.\n", + channels[i].hw_value, + channels[i].max_power, + channels[i].flags & IEEE80211_CHAN_RADAR ? + " (IEEE 802.11h required)" : "", + ((channels[i].flags & IEEE80211_CHAN_NO_IR) + || (channels[i].flags & + IEEE80211_CHAN_RADAR)) ? "" : + ", IBSS", + channels[i].flags & + IEEE80211_CHAN_NO_IR ? + "passive only" : "active/passive"); + } + supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ); + if (supp_band) { + channels = supp_band->channels; + + pos += scnprintf(buf + pos, bufsz - pos, + "Displaying %d channels in 5.2GHz band (802.11a)\n", + supp_band->n_channels); + + for (i = 0; i < supp_band->n_channels; i++) + pos += scnprintf(buf + pos, bufsz - pos, + "%d: %ddBm: BSS%s%s, %s.\n", + channels[i].hw_value, + channels[i].max_power, + channels[i].flags & IEEE80211_CHAN_RADAR ? + " (IEEE 802.11h required)" : "", + ((channels[i].flags & IEEE80211_CHAN_NO_IR) + || (channels[i].flags & + IEEE80211_CHAN_RADAR)) ? "" : + ", IBSS", + channels[i].flags & + IEEE80211_CHAN_NO_IR ? + "passive only" : "active/passive"); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_status_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[512]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n", + test_bit(STATUS_RF_KILL_HW, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n", + test_bit(STATUS_CT_KILL, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n", + test_bit(STATUS_ALIVE, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n", + test_bit(STATUS_READY, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n", + test_bit(STATUS_EXIT_PENDING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n", + test_bit(STATUS_STATISTICS, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n", + test_bit(STATUS_SCANNING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n", + test_bit(STATUS_SCAN_ABORTING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n", + test_bit(STATUS_SCAN_HW, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n", + test_bit(STATUS_POWER_PMI, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n", + test_bit(STATUS_FW_ERROR, &priv->status)); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_rx_handlers_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = 24 * 64; /* 24 items * 64 char per item */ + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (cnt = 0; cnt < REPLY_MAX; cnt++) { + if (priv->rx_handlers_stats[cnt] > 0) + pos += scnprintf(buf + pos, bufsz - pos, + "\tRx handler[%36s]:\t\t %u\n", + iwl_dvm_get_cmd_string(cnt), + priv->rx_handlers_stats[cnt]); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_rx_handlers_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + + char buf[8]; + int buf_size; + u32 reset_flag; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &reset_flag) != 1) + return -EFAULT; + if (reset_flag == 0) + memset(&priv->rx_handlers_stats[0], 0, + sizeof(priv->rx_handlers_stats)); + + return count; +} + +static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct iwl_rxon_context *ctx; + int pos = 0, i; + char buf[256 * NUM_IWL_RXON_CTX]; + const size_t bufsz = sizeof(buf); + + for_each_context(priv, ctx) { + pos += scnprintf(buf + pos, bufsz - pos, "context %d:\n", + ctx->ctxid); + for (i = 0; i < AC_NUM; i++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\tcw_min\tcw_max\taifsn\ttxop\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "AC[%d]\t%u\t%u\t%u\t%u\n", i, + ctx->qos_data.def_qos_parm.ac[i].cw_min, + ctx->qos_data.def_qos_parm.ac[i].cw_max, + ctx->qos_data.def_qos_parm.ac[i].aifsn, + ctx->qos_data.def_qos_parm.ac[i].edca_txop); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + char buf[100]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, + "Thermal Throttling Mode: %s\n", + tt->advanced_tt ? "Advance" : "Legacy"); + pos += scnprintf(buf + pos, bufsz - pos, + "Thermal Throttling State: %d\n", + tt->state); + if (tt->advanced_tt) { + restriction = tt->restriction + tt->state; + pos += scnprintf(buf + pos, bufsz - pos, + "Tx mode: %d\n", + restriction->tx_stream); + pos += scnprintf(buf + pos, bufsz - pos, + "Rx mode: %d\n", + restriction->rx_stream); + pos += scnprintf(buf + pos, bufsz - pos, + "HT mode: %d\n", + restriction->is_ht); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_disable_ht40_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int ht40; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &ht40) != 1) + return -EFAULT; + if (!iwl_is_any_associated(priv)) + priv->disable_ht40 = ht40 ? true : false; + else + return -EINVAL; + + return count; +} + +static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[100]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, + "11n 40MHz Mode: %s\n", + priv->disable_ht40 ? "Disabled" : "Enabled"); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_temperature_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "%d\n", priv->temperature); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + + +static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int value; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%d", &value) != 1) + return -EINVAL; + + /* + * Our users expect 0 to be "CAM", but 0 isn't actually + * valid here. However, let's not confuse them and present + * IWL_POWER_INDEX_1 as "1", not "0". + */ + if (value == 0) + return -EINVAL; + else if (value > 0) + value -= 1; + + if (value != -1 && (value < 0 || value >= IWL_POWER_NUM)) + return -EINVAL; + + if (!iwl_is_ready_rf(priv)) + return -EAGAIN; + + priv->power_data.debug_sleep_level_override = value; + + mutex_lock(&priv->mutex); + iwl_power_update_mode(priv, true); + mutex_unlock(&priv->mutex); + + return count; +} + +static ssize_t iwl_dbgfs_sleep_level_override_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[10]; + int pos, value; + const size_t bufsz = sizeof(buf); + + /* see the write function */ + value = priv->power_data.debug_sleep_level_override; + if (value >= 0) + value += 1; + + pos = scnprintf(buf, bufsz, "%d\n", value); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[200]; + int pos = 0, i; + const size_t bufsz = sizeof(buf); + struct iwl_powertable_cmd *cmd = &priv->power_data.sleep_cmd; + + pos += scnprintf(buf + pos, bufsz - pos, + "flags: %#.2x\n", le16_to_cpu(cmd->flags)); + pos += scnprintf(buf + pos, bufsz - pos, + "RX/TX timeout: %d/%d usec\n", + le32_to_cpu(cmd->rx_data_timeout), + le32_to_cpu(cmd->tx_data_timeout)); + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + pos += scnprintf(buf + pos, bufsz - pos, + "sleep_interval[%d]: %d\n", i, + le32_to_cpu(cmd->sleep_interval[i])); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +DEBUGFS_READ_WRITE_FILE_OPS(sram); +DEBUGFS_READ_FILE_OPS(wowlan_sram); +DEBUGFS_READ_FILE_OPS(nvm); +DEBUGFS_READ_FILE_OPS(stations); +DEBUGFS_READ_FILE_OPS(channels); +DEBUGFS_READ_FILE_OPS(status); +DEBUGFS_READ_WRITE_FILE_OPS(rx_handlers); +DEBUGFS_READ_FILE_OPS(qos); +DEBUGFS_READ_FILE_OPS(thermal_throttling); +DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); +DEBUGFS_READ_FILE_OPS(temperature); +DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override); +DEBUGFS_READ_FILE_OPS(current_sleep_command); + +static const char *fmt_value = " %-30s %10u\n"; +static const char *fmt_hex = " %-30s 0x%02X\n"; +static const char *fmt_table = " %-30s %10u %10u %10u %10u\n"; +static const char *fmt_header = + "%-32s current cumulative delta max\n"; + +static int iwl_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz) +{ + int p = 0; + u32 flag; + + lockdep_assert_held(&priv->statistics.lock); + + flag = le32_to_cpu(priv->statistics.flag); + + p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", flag); + if (flag & UCODE_STATISTICS_CLEAR_MSK) + p += scnprintf(buf + p, bufsz - p, + "\tStatistics have been cleared\n"); + p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", + (flag & UCODE_STATISTICS_FREQUENCY_MSK) + ? "2.4 GHz" : "5.2 GHz"); + p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", + (flag & UCODE_STATISTICS_NARROW_BAND_MSK) + ? "enabled" : "disabled"); + + return p; +} + +static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct statistics_rx_phy) * 40 + + sizeof(struct statistics_rx_non_phy) * 40 + + sizeof(struct statistics_rx_ht_phy) * 40 + 400; + ssize_t ret; + struct statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; + struct statistics_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; + struct statistics_rx_non_phy *general, *accum_general; + struct statistics_rx_non_phy *delta_general, *max_general; + struct statistics_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* + * the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + spin_lock_bh(&priv->statistics.lock); + ofdm = &priv->statistics.rx_ofdm; + cck = &priv->statistics.rx_cck; + general = &priv->statistics.rx_non_phy; + ht = &priv->statistics.rx_ofdm_ht; + accum_ofdm = &priv->accum_stats.rx_ofdm; + accum_cck = &priv->accum_stats.rx_cck; + accum_general = &priv->accum_stats.rx_non_phy; + accum_ht = &priv->accum_stats.rx_ofdm_ht; + delta_ofdm = &priv->delta_stats.rx_ofdm; + delta_cck = &priv->delta_stats.rx_cck; + delta_general = &priv->delta_stats.rx_non_phy; + delta_ht = &priv->delta_stats.rx_ofdm_ht; + max_ofdm = &priv->max_delta_stats.rx_ofdm; + max_cck = &priv->max_delta_stats.rx_cck; + max_general = &priv->max_delta_stats.rx_non_phy; + max_ht = &priv->max_delta_stats.rx_ofdm_ht; + + pos += iwl_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - OFDM:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ina_cnt:", + le32_to_cpu(ofdm->ina_cnt), + accum_ofdm->ina_cnt, + delta_ofdm->ina_cnt, max_ofdm->ina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_cnt:", + le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, + delta_ofdm->fina_cnt, max_ofdm->fina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "plcp_err:", + le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, + delta_ofdm->plcp_err, max_ofdm->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_err:", + le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, + delta_ofdm->crc32_err, max_ofdm->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "overrun_err:", + le32_to_cpu(ofdm->overrun_err), + accum_ofdm->overrun_err, delta_ofdm->overrun_err, + max_ofdm->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "early_overrun_err:", + le32_to_cpu(ofdm->early_overrun_err), + accum_ofdm->early_overrun_err, + delta_ofdm->early_overrun_err, + max_ofdm->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_good:", + le32_to_cpu(ofdm->crc32_good), + accum_ofdm->crc32_good, delta_ofdm->crc32_good, + max_ofdm->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "false_alarm_cnt:", + le32_to_cpu(ofdm->false_alarm_cnt), + accum_ofdm->false_alarm_cnt, + delta_ofdm->false_alarm_cnt, + max_ofdm->false_alarm_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_sync_err_cnt:", + le32_to_cpu(ofdm->fina_sync_err_cnt), + accum_ofdm->fina_sync_err_cnt, + delta_ofdm->fina_sync_err_cnt, + max_ofdm->fina_sync_err_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sfd_timeout:", + le32_to_cpu(ofdm->sfd_timeout), + accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout, + max_ofdm->sfd_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_timeout:", + le32_to_cpu(ofdm->fina_timeout), + accum_ofdm->fina_timeout, delta_ofdm->fina_timeout, + max_ofdm->fina_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "unresponded_rts:", + le32_to_cpu(ofdm->unresponded_rts), + accum_ofdm->unresponded_rts, + delta_ofdm->unresponded_rts, + max_ofdm->unresponded_rts); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rxe_frame_lmt_ovrun:", + le32_to_cpu(ofdm->rxe_frame_limit_overrun), + accum_ofdm->rxe_frame_limit_overrun, + delta_ofdm->rxe_frame_limit_overrun, + max_ofdm->rxe_frame_limit_overrun); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ack_cnt:", + le32_to_cpu(ofdm->sent_ack_cnt), + accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt, + max_ofdm->sent_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_cts_cnt:", + le32_to_cpu(ofdm->sent_cts_cnt), + accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt, + max_ofdm->sent_cts_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ba_rsp_cnt:", + le32_to_cpu(ofdm->sent_ba_rsp_cnt), + accum_ofdm->sent_ba_rsp_cnt, + delta_ofdm->sent_ba_rsp_cnt, + max_ofdm->sent_ba_rsp_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dsp_self_kill:", + le32_to_cpu(ofdm->dsp_self_kill), + accum_ofdm->dsp_self_kill, + delta_ofdm->dsp_self_kill, + max_ofdm->dsp_self_kill); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "mh_format_err:", + le32_to_cpu(ofdm->mh_format_err), + accum_ofdm->mh_format_err, + delta_ofdm->mh_format_err, + max_ofdm->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "re_acq_main_rssi_sum:", + le32_to_cpu(ofdm->re_acq_main_rssi_sum), + accum_ofdm->re_acq_main_rssi_sum, + delta_ofdm->re_acq_main_rssi_sum, + max_ofdm->re_acq_main_rssi_sum); + + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - CCK:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ina_cnt:", + le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, + delta_cck->ina_cnt, max_cck->ina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_cnt:", + le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, + delta_cck->fina_cnt, max_cck->fina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "plcp_err:", + le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, + delta_cck->plcp_err, max_cck->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_err:", + le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, + delta_cck->crc32_err, max_cck->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "overrun_err:", + le32_to_cpu(cck->overrun_err), + accum_cck->overrun_err, delta_cck->overrun_err, + max_cck->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "early_overrun_err:", + le32_to_cpu(cck->early_overrun_err), + accum_cck->early_overrun_err, + delta_cck->early_overrun_err, + max_cck->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_good:", + le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, + delta_cck->crc32_good, max_cck->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "false_alarm_cnt:", + le32_to_cpu(cck->false_alarm_cnt), + accum_cck->false_alarm_cnt, + delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_sync_err_cnt:", + le32_to_cpu(cck->fina_sync_err_cnt), + accum_cck->fina_sync_err_cnt, + delta_cck->fina_sync_err_cnt, + max_cck->fina_sync_err_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sfd_timeout:", + le32_to_cpu(cck->sfd_timeout), + accum_cck->sfd_timeout, delta_cck->sfd_timeout, + max_cck->sfd_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_timeout:", + le32_to_cpu(cck->fina_timeout), + accum_cck->fina_timeout, delta_cck->fina_timeout, + max_cck->fina_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "unresponded_rts:", + le32_to_cpu(cck->unresponded_rts), + accum_cck->unresponded_rts, delta_cck->unresponded_rts, + max_cck->unresponded_rts); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rxe_frame_lmt_ovrun:", + le32_to_cpu(cck->rxe_frame_limit_overrun), + accum_cck->rxe_frame_limit_overrun, + delta_cck->rxe_frame_limit_overrun, + max_cck->rxe_frame_limit_overrun); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ack_cnt:", + le32_to_cpu(cck->sent_ack_cnt), + accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt, + max_cck->sent_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_cts_cnt:", + le32_to_cpu(cck->sent_cts_cnt), + accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt, + max_cck->sent_cts_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ba_rsp_cnt:", + le32_to_cpu(cck->sent_ba_rsp_cnt), + accum_cck->sent_ba_rsp_cnt, + delta_cck->sent_ba_rsp_cnt, + max_cck->sent_ba_rsp_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dsp_self_kill:", + le32_to_cpu(cck->dsp_self_kill), + accum_cck->dsp_self_kill, delta_cck->dsp_self_kill, + max_cck->dsp_self_kill); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "mh_format_err:", + le32_to_cpu(cck->mh_format_err), + accum_cck->mh_format_err, delta_cck->mh_format_err, + max_cck->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "re_acq_main_rssi_sum:", + le32_to_cpu(cck->re_acq_main_rssi_sum), + accum_cck->re_acq_main_rssi_sum, + delta_cck->re_acq_main_rssi_sum, + max_cck->re_acq_main_rssi_sum); + + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - GENERAL:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bogus_cts:", + le32_to_cpu(general->bogus_cts), + accum_general->bogus_cts, delta_general->bogus_cts, + max_general->bogus_cts); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bogus_ack:", + le32_to_cpu(general->bogus_ack), + accum_general->bogus_ack, delta_general->bogus_ack, + max_general->bogus_ack); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "non_bssid_frames:", + le32_to_cpu(general->non_bssid_frames), + accum_general->non_bssid_frames, + delta_general->non_bssid_frames, + max_general->non_bssid_frames); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "filtered_frames:", + le32_to_cpu(general->filtered_frames), + accum_general->filtered_frames, + delta_general->filtered_frames, + max_general->filtered_frames); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "non_channel_beacons:", + le32_to_cpu(general->non_channel_beacons), + accum_general->non_channel_beacons, + delta_general->non_channel_beacons, + max_general->non_channel_beacons); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "channel_beacons:", + le32_to_cpu(general->channel_beacons), + accum_general->channel_beacons, + delta_general->channel_beacons, + max_general->channel_beacons); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "num_missed_bcon:", + le32_to_cpu(general->num_missed_bcon), + accum_general->num_missed_bcon, + delta_general->num_missed_bcon, + max_general->num_missed_bcon); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "adc_rx_saturation_time:", + le32_to_cpu(general->adc_rx_saturation_time), + accum_general->adc_rx_saturation_time, + delta_general->adc_rx_saturation_time, + max_general->adc_rx_saturation_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ina_detect_search_tm:", + le32_to_cpu(general->ina_detection_search_time), + accum_general->ina_detection_search_time, + delta_general->ina_detection_search_time, + max_general->ina_detection_search_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_silence_rssi_a:", + le32_to_cpu(general->beacon_silence_rssi_a), + accum_general->beacon_silence_rssi_a, + delta_general->beacon_silence_rssi_a, + max_general->beacon_silence_rssi_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_silence_rssi_b:", + le32_to_cpu(general->beacon_silence_rssi_b), + accum_general->beacon_silence_rssi_b, + delta_general->beacon_silence_rssi_b, + max_general->beacon_silence_rssi_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_silence_rssi_c:", + le32_to_cpu(general->beacon_silence_rssi_c), + accum_general->beacon_silence_rssi_c, + delta_general->beacon_silence_rssi_c, + max_general->beacon_silence_rssi_c); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "interference_data_flag:", + le32_to_cpu(general->interference_data_flag), + accum_general->interference_data_flag, + delta_general->interference_data_flag, + max_general->interference_data_flag); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "channel_load:", + le32_to_cpu(general->channel_load), + accum_general->channel_load, + delta_general->channel_load, + max_general->channel_load); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dsp_false_alarms:", + le32_to_cpu(general->dsp_false_alarms), + accum_general->dsp_false_alarms, + delta_general->dsp_false_alarms, + max_general->dsp_false_alarms); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_rssi_a:", + le32_to_cpu(general->beacon_rssi_a), + accum_general->beacon_rssi_a, + delta_general->beacon_rssi_a, + max_general->beacon_rssi_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_rssi_b:", + le32_to_cpu(general->beacon_rssi_b), + accum_general->beacon_rssi_b, + delta_general->beacon_rssi_b, + max_general->beacon_rssi_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_rssi_c:", + le32_to_cpu(general->beacon_rssi_c), + accum_general->beacon_rssi_c, + delta_general->beacon_rssi_c, + max_general->beacon_rssi_c); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_energy_a:", + le32_to_cpu(general->beacon_energy_a), + accum_general->beacon_energy_a, + delta_general->beacon_energy_a, + max_general->beacon_energy_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_energy_b:", + le32_to_cpu(general->beacon_energy_b), + accum_general->beacon_energy_b, + delta_general->beacon_energy_b, + max_general->beacon_energy_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_energy_c:", + le32_to_cpu(general->beacon_energy_c), + accum_general->beacon_energy_c, + delta_general->beacon_energy_c, + max_general->beacon_energy_c); + + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - OFDM_HT:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "plcp_err:", + le32_to_cpu(ht->plcp_err), accum_ht->plcp_err, + delta_ht->plcp_err, max_ht->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "overrun_err:", + le32_to_cpu(ht->overrun_err), accum_ht->overrun_err, + delta_ht->overrun_err, max_ht->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "early_overrun_err:", + le32_to_cpu(ht->early_overrun_err), + accum_ht->early_overrun_err, + delta_ht->early_overrun_err, + max_ht->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_good:", + le32_to_cpu(ht->crc32_good), accum_ht->crc32_good, + delta_ht->crc32_good, max_ht->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_err:", + le32_to_cpu(ht->crc32_err), accum_ht->crc32_err, + delta_ht->crc32_err, max_ht->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "mh_format_err:", + le32_to_cpu(ht->mh_format_err), + accum_ht->mh_format_err, + delta_ht->mh_format_err, max_ht->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg_crc32_good:", + le32_to_cpu(ht->agg_crc32_good), + accum_ht->agg_crc32_good, + delta_ht->agg_crc32_good, max_ht->agg_crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg_mpdu_cnt:", + le32_to_cpu(ht->agg_mpdu_cnt), + accum_ht->agg_mpdu_cnt, + delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg_cnt:", + le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt, + delta_ht->agg_cnt, max_ht->agg_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "unsupport_mcs:", + le32_to_cpu(ht->unsupport_mcs), + accum_ht->unsupport_mcs, + delta_ht->unsupport_mcs, max_ht->unsupport_mcs); + + spin_unlock_bh(&priv->statistics.lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0; + char *buf; + int bufsz = (sizeof(struct statistics_tx) * 48) + 250; + ssize_t ret; + struct statistics_tx *tx, *accum_tx, *delta_tx, *max_tx; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + spin_lock_bh(&priv->statistics.lock); + + tx = &priv->statistics.tx; + accum_tx = &priv->accum_stats.tx; + delta_tx = &priv->delta_stats.tx; + max_tx = &priv->max_delta_stats.tx; + + pos += iwl_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Tx:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "preamble:", + le32_to_cpu(tx->preamble_cnt), + accum_tx->preamble_cnt, + delta_tx->preamble_cnt, max_tx->preamble_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rx_detected_cnt:", + le32_to_cpu(tx->rx_detected_cnt), + accum_tx->rx_detected_cnt, + delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bt_prio_defer_cnt:", + le32_to_cpu(tx->bt_prio_defer_cnt), + accum_tx->bt_prio_defer_cnt, + delta_tx->bt_prio_defer_cnt, + max_tx->bt_prio_defer_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bt_prio_kill_cnt:", + le32_to_cpu(tx->bt_prio_kill_cnt), + accum_tx->bt_prio_kill_cnt, + delta_tx->bt_prio_kill_cnt, + max_tx->bt_prio_kill_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "few_bytes_cnt:", + le32_to_cpu(tx->few_bytes_cnt), + accum_tx->few_bytes_cnt, + delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "cts_timeout:", + le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, + delta_tx->cts_timeout, max_tx->cts_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ack_timeout:", + le32_to_cpu(tx->ack_timeout), + accum_tx->ack_timeout, + delta_tx->ack_timeout, max_tx->ack_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "expected_ack_cnt:", + le32_to_cpu(tx->expected_ack_cnt), + accum_tx->expected_ack_cnt, + delta_tx->expected_ack_cnt, + max_tx->expected_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "actual_ack_cnt:", + le32_to_cpu(tx->actual_ack_cnt), + accum_tx->actual_ack_cnt, + delta_tx->actual_ack_cnt, + max_tx->actual_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dump_msdu_cnt:", + le32_to_cpu(tx->dump_msdu_cnt), + accum_tx->dump_msdu_cnt, + delta_tx->dump_msdu_cnt, + max_tx->dump_msdu_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "abort_nxt_frame_mismatch:", + le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt), + accum_tx->burst_abort_next_frame_mismatch_cnt, + delta_tx->burst_abort_next_frame_mismatch_cnt, + max_tx->burst_abort_next_frame_mismatch_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "abort_missing_nxt_frame:", + le32_to_cpu(tx->burst_abort_missing_next_frame_cnt), + accum_tx->burst_abort_missing_next_frame_cnt, + delta_tx->burst_abort_missing_next_frame_cnt, + max_tx->burst_abort_missing_next_frame_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "cts_timeout_collision:", + le32_to_cpu(tx->cts_timeout_collision), + accum_tx->cts_timeout_collision, + delta_tx->cts_timeout_collision, + max_tx->cts_timeout_collision); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ack_ba_timeout_collision:", + le32_to_cpu(tx->ack_or_ba_timeout_collision), + accum_tx->ack_or_ba_timeout_collision, + delta_tx->ack_or_ba_timeout_collision, + max_tx->ack_or_ba_timeout_collision); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg ba_timeout:", + le32_to_cpu(tx->agg.ba_timeout), + accum_tx->agg.ba_timeout, + delta_tx->agg.ba_timeout, + max_tx->agg.ba_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg ba_resched_frames:", + le32_to_cpu(tx->agg.ba_reschedule_frames), + accum_tx->agg.ba_reschedule_frames, + delta_tx->agg.ba_reschedule_frames, + max_tx->agg.ba_reschedule_frames); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_agg_frame:", + le32_to_cpu(tx->agg.scd_query_agg_frame_cnt), + accum_tx->agg.scd_query_agg_frame_cnt, + delta_tx->agg.scd_query_agg_frame_cnt, + max_tx->agg.scd_query_agg_frame_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_no_agg:", + le32_to_cpu(tx->agg.scd_query_no_agg), + accum_tx->agg.scd_query_no_agg, + delta_tx->agg.scd_query_no_agg, + max_tx->agg.scd_query_no_agg); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_agg:", + le32_to_cpu(tx->agg.scd_query_agg), + accum_tx->agg.scd_query_agg, + delta_tx->agg.scd_query_agg, + max_tx->agg.scd_query_agg); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_mismatch:", + le32_to_cpu(tx->agg.scd_query_mismatch), + accum_tx->agg.scd_query_mismatch, + delta_tx->agg.scd_query_mismatch, + max_tx->agg.scd_query_mismatch); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg frame_not_ready:", + le32_to_cpu(tx->agg.frame_not_ready), + accum_tx->agg.frame_not_ready, + delta_tx->agg.frame_not_ready, + max_tx->agg.frame_not_ready); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg underrun:", + le32_to_cpu(tx->agg.underrun), + accum_tx->agg.underrun, + delta_tx->agg.underrun, max_tx->agg.underrun); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg bt_prio_kill:", + le32_to_cpu(tx->agg.bt_prio_kill), + accum_tx->agg.bt_prio_kill, + delta_tx->agg.bt_prio_kill, + max_tx->agg.bt_prio_kill); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg rx_ba_rsp_cnt:", + le32_to_cpu(tx->agg.rx_ba_rsp_cnt), + accum_tx->agg.rx_ba_rsp_cnt, + delta_tx->agg.rx_ba_rsp_cnt, + max_tx->agg.rx_ba_rsp_cnt); + + if (tx->tx_power.ant_a || tx->tx_power.ant_b || tx->tx_power.ant_c) { + pos += scnprintf(buf + pos, bufsz - pos, + "tx power: (1/2 dB step)\n"); + if ((priv->nvm_data->valid_tx_ant & ANT_A) && + tx->tx_power.ant_a) + pos += scnprintf(buf + pos, bufsz - pos, + fmt_hex, "antenna A:", + tx->tx_power.ant_a); + if ((priv->nvm_data->valid_tx_ant & ANT_B) && + tx->tx_power.ant_b) + pos += scnprintf(buf + pos, bufsz - pos, + fmt_hex, "antenna B:", + tx->tx_power.ant_b); + if ((priv->nvm_data->valid_tx_ant & ANT_C) && + tx->tx_power.ant_c) + pos += scnprintf(buf + pos, bufsz - pos, + fmt_hex, "antenna C:", + tx->tx_power.ant_c); + } + + spin_unlock_bh(&priv->statistics.lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct statistics_general) * 10 + 300; + ssize_t ret; + struct statistics_general_common *general, *accum_general; + struct statistics_general_common *delta_general, *max_general; + struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; + struct statistics_div *div, *accum_div, *delta_div, *max_div; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + + spin_lock_bh(&priv->statistics.lock); + + general = &priv->statistics.common; + dbg = &priv->statistics.common.dbg; + div = &priv->statistics.common.div; + accum_general = &priv->accum_stats.common; + accum_dbg = &priv->accum_stats.common.dbg; + accum_div = &priv->accum_stats.common.div; + delta_general = &priv->delta_stats.common; + max_general = &priv->max_delta_stats.common; + delta_dbg = &priv->delta_stats.common.dbg; + max_dbg = &priv->max_delta_stats.common.dbg; + delta_div = &priv->delta_stats.common.div; + max_div = &priv->max_delta_stats.common.div; + + pos += iwl_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_General:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_value, "temperature:", + le32_to_cpu(general->temperature)); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_value, "temperature_m:", + le32_to_cpu(general->temperature_m)); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_value, "ttl_timestamp:", + le32_to_cpu(general->ttl_timestamp)); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "burst_check:", + le32_to_cpu(dbg->burst_check), + accum_dbg->burst_check, + delta_dbg->burst_check, max_dbg->burst_check); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "burst_count:", + le32_to_cpu(dbg->burst_count), + accum_dbg->burst_count, + delta_dbg->burst_count, max_dbg->burst_count); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "wait_for_silence_timeout_count:", + le32_to_cpu(dbg->wait_for_silence_timeout_cnt), + accum_dbg->wait_for_silence_timeout_cnt, + delta_dbg->wait_for_silence_timeout_cnt, + max_dbg->wait_for_silence_timeout_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sleep_time:", + le32_to_cpu(general->sleep_time), + accum_general->sleep_time, + delta_general->sleep_time, max_general->sleep_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "slots_out:", + le32_to_cpu(general->slots_out), + accum_general->slots_out, + delta_general->slots_out, max_general->slots_out); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "slots_idle:", + le32_to_cpu(general->slots_idle), + accum_general->slots_idle, + delta_general->slots_idle, max_general->slots_idle); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "tx_on_a:", + le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, + delta_div->tx_on_a, max_div->tx_on_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "tx_on_b:", + le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, + delta_div->tx_on_b, max_div->tx_on_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "exec_time:", + le32_to_cpu(div->exec_time), accum_div->exec_time, + delta_div->exec_time, max_div->exec_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "probe_time:", + le32_to_cpu(div->probe_time), accum_div->probe_time, + delta_div->probe_time, max_div->probe_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rx_enable_counter:", + le32_to_cpu(general->rx_enable_counter), + accum_general->rx_enable_counter, + delta_general->rx_enable_counter, + max_general->rx_enable_counter); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "num_of_sos_states:", + le32_to_cpu(general->num_of_sos_states), + accum_general->num_of_sos_states, + delta_general->num_of_sos_states, + max_general->num_of_sos_states); + + spin_unlock_bh(&priv->statistics.lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char *buf; + int bufsz = (sizeof(struct statistics_bt_activity) * 24) + 200; + ssize_t ret; + struct statistics_bt_activity *bt, *accum_bt; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + if (!priv->bt_enable_flag) + return -EINVAL; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + ret = iwl_send_statistics_request(priv, 0, false); + mutex_unlock(&priv->mutex); + + if (ret) + return -EAGAIN; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* + * the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + + spin_lock_bh(&priv->statistics.lock); + + bt = &priv->statistics.bt_activity; + accum_bt = &priv->accum_stats.bt_activity; + + pos += iwl_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_BT:\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "\t\t\tcurrent\t\t\taccumulative\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "hi_priority_tx_req_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(bt->hi_priority_tx_req_cnt), + accum_bt->hi_priority_tx_req_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "hi_priority_tx_denied_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(bt->hi_priority_tx_denied_cnt), + accum_bt->hi_priority_tx_denied_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "lo_priority_tx_req_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(bt->lo_priority_tx_req_cnt), + accum_bt->lo_priority_tx_req_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "lo_priority_tx_denied_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(bt->lo_priority_tx_denied_cnt), + accum_bt->lo_priority_tx_denied_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "hi_priority_rx_req_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(bt->hi_priority_rx_req_cnt), + accum_bt->hi_priority_rx_req_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "hi_priority_rx_denied_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(bt->hi_priority_rx_denied_cnt), + accum_bt->hi_priority_rx_denied_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "lo_priority_rx_req_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(bt->lo_priority_rx_req_cnt), + accum_bt->lo_priority_rx_req_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "lo_priority_rx_denied_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(bt->lo_priority_rx_denied_cnt), + accum_bt->lo_priority_rx_denied_cnt); + + pos += scnprintf(buf + pos, bufsz - pos, + "(rx)num_bt_kills:\t\t%u\t\t\t%u\n", + le32_to_cpu(priv->statistics.num_bt_kills), + priv->statistics.accum_num_bt_kills); + + spin_unlock_bh(&priv->statistics.lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_reply_tx_error_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char *buf; + int bufsz = (sizeof(struct reply_tx_error_statistics) * 24) + + (sizeof(struct reply_agg_tx_error_statistics) * 24) + 200; + ssize_t ret; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_TX_Error:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_DELAY), + priv->reply_tx_stats.pp_delay); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_FEW_BYTES), + priv->reply_tx_stats.pp_few_bytes); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_BT_PRIO), + priv->reply_tx_stats.pp_bt_prio); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_QUIET_PERIOD), + priv->reply_tx_stats.pp_quiet_period); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_CALC_TTAK), + priv->reply_tx_stats.pp_calc_ttak); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_tx_fail_reason( + TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY), + priv->reply_tx_stats.int_crossed_retry); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_SHORT_LIMIT), + priv->reply_tx_stats.short_limit); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_LONG_LIMIT), + priv->reply_tx_stats.long_limit); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_UNDERRUN), + priv->reply_tx_stats.fifo_underrun); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_DRAIN_FLOW), + priv->reply_tx_stats.drain_flow); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_RFKILL_FLUSH), + priv->reply_tx_stats.rfkill_flush); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_LIFE_EXPIRE), + priv->reply_tx_stats.life_expire); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_DEST_PS), + priv->reply_tx_stats.dest_ps); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_HOST_ABORTED), + priv->reply_tx_stats.host_abort); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_BT_RETRY), + priv->reply_tx_stats.pp_delay); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_STA_INVALID), + priv->reply_tx_stats.sta_invalid); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_FRAG_DROPPED), + priv->reply_tx_stats.frag_drop); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_TID_DISABLE), + priv->reply_tx_stats.tid_disable); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_FLUSHED), + priv->reply_tx_stats.fifo_flush); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_tx_fail_reason( + TX_STATUS_FAIL_INSUFFICIENT_CF_POLL), + priv->reply_tx_stats.insuff_cf_poll); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_PASSIVE_NO_RX), + priv->reply_tx_stats.fail_hw_drop); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_tx_fail_reason( + TX_STATUS_FAIL_NO_BEACON_ON_RADAR), + priv->reply_tx_stats.sta_color_mismatch); + pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n", + priv->reply_tx_stats.unknown); + + pos += scnprintf(buf + pos, bufsz - pos, + "\nStatistics_Agg_TX_Error:\n"); + + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_UNDERRUN_MSK), + priv->reply_agg_tx_stats.underrun); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_BT_PRIO_MSK), + priv->reply_agg_tx_stats.bt_prio); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_FEW_BYTES_MSK), + priv->reply_agg_tx_stats.few_bytes); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_ABORT_MSK), + priv->reply_agg_tx_stats.abort); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_agg_tx_fail_reason( + AGG_TX_STATE_LAST_SENT_TTL_MSK), + priv->reply_agg_tx_stats.last_sent_ttl); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_agg_tx_fail_reason( + AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK), + priv->reply_agg_tx_stats.last_sent_try); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_agg_tx_fail_reason( + AGG_TX_STATE_LAST_SENT_BT_KILL_MSK), + priv->reply_agg_tx_stats.last_sent_bt_kill); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_SCD_QUERY_MSK), + priv->reply_agg_tx_stats.scd_query); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_agg_tx_fail_reason( + AGG_TX_STATE_TEST_BAD_CRC32_MSK), + priv->reply_agg_tx_stats.bad_crc32); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_RESPONSE_MSK), + priv->reply_agg_tx_stats.response); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DUMP_TX_MSK), + priv->reply_agg_tx_stats.dump_tx); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DELAY_TX_MSK), + priv->reply_agg_tx_stats.delay_tx); + pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n", + priv->reply_agg_tx_stats.unknown); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_sensitivity_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct iwl_sensitivity_data) * 4 + 100; + ssize_t ret; + struct iwl_sensitivity_data *data; + + data = &priv->sensitivity_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n", + data->auto_corr_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, + "auto_corr_ofdm_mrc:\t\t %u\n", + data->auto_corr_ofdm_mrc); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n", + data->auto_corr_ofdm_x1); + pos += scnprintf(buf + pos, bufsz - pos, + "auto_corr_ofdm_mrc_x1:\t\t %u\n", + data->auto_corr_ofdm_mrc_x1); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n", + data->auto_corr_cck); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n", + data->auto_corr_cck_mrc); + pos += scnprintf(buf + pos, bufsz - pos, + "last_bad_plcp_cnt_ofdm:\t\t %u\n", + data->last_bad_plcp_cnt_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n", + data->last_fa_cnt_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, + "last_bad_plcp_cnt_cck:\t\t %u\n", + data->last_bad_plcp_cnt_cck); + pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n", + data->last_fa_cnt_cck); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n", + data->nrg_curr_state); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n", + data->nrg_prev_state); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t"); + for (cnt = 0; cnt < 10; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_value[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t"); + for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_silence_rssi[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n", + data->nrg_silence_ref); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n", + data->nrg_energy_idx); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n", + data->nrg_silence_idx); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n", + data->nrg_th_cck); + pos += scnprintf(buf + pos, bufsz - pos, + "nrg_auto_corr_silence_diff:\t %u\n", + data->nrg_auto_corr_silence_diff); + pos += scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n", + data->num_in_cck_no_fa); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n", + data->nrg_th_ofdm); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + + +static ssize_t iwl_dbgfs_chain_noise_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct iwl_chain_noise_data) * 4 + 100; + ssize_t ret; + struct iwl_chain_noise_data *data; + + data = &priv->chain_noise_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n", + data->active_chains); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n", + data->chain_noise_a); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n", + data->chain_noise_b); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n", + data->chain_noise_c); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n", + data->chain_signal_a); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n", + data->chain_signal_b); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n", + data->chain_signal_c); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n", + data->beacon_count); + + pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->disconn_array[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->delta_gain_code[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n", + data->radio_write); + pos += scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n", + data->state); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_power_save_status_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[60]; + int pos = 0; + const size_t bufsz = sizeof(buf); + u32 pwrsave_status; + + pwrsave_status = iwl_read32(priv->trans, CSR_GP_CNTRL) & + CSR_GP_REG_POWER_SAVE_STATUS_MSK; + + pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: "); + pos += scnprintf(buf + pos, bufsz - pos, "%s\n", + (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" : + (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" : + (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" : + "error"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int clear; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &clear) != 1) + return -EFAULT; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + iwl_send_statistics_request(priv, 0, true); + mutex_unlock(&priv->mutex); + + return count; +} + +static ssize_t iwl_dbgfs_ucode_tracing_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[128]; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "ucode trace timer is %s\n", + priv->event_log.ucode_trace ? "On" : "Off"); + pos += scnprintf(buf + pos, bufsz - pos, "non_wraps_count:\t\t %u\n", + priv->event_log.non_wraps_count); + pos += scnprintf(buf + pos, bufsz - pos, "wraps_once_count:\t\t %u\n", + priv->event_log.wraps_once_count); + pos += scnprintf(buf + pos, bufsz - pos, "wraps_more_count:\t\t %u\n", + priv->event_log.wraps_more_count); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int trace; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &trace) != 1) + return -EFAULT; + + if (trace) { + priv->event_log.ucode_trace = true; + if (iwl_is_alive(priv)) { + /* start collecting data now */ + mod_timer(&priv->ucode_trace, jiffies); + } + } else { + priv->event_log.ucode_trace = false; + del_timer_sync(&priv->ucode_trace); + } + + return count; +} + +static ssize_t iwl_dbgfs_rxon_flags_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int len = 0; + char buf[20]; + + len = sprintf(buf, "0x%04X\n", + le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.flags)); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t iwl_dbgfs_rxon_filter_flags_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int len = 0; + char buf[20]; + + len = sprintf(buf, "0x%04X\n", + le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags)); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t iwl_dbgfs_missed_beacon_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[12]; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "%d\n", + priv->missed_beacon_threshold); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_missed_beacon_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int missed; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &missed) != 1) + return -EINVAL; + + if (missed < IWL_MISSED_BEACON_THRESHOLD_MIN || + missed > IWL_MISSED_BEACON_THRESHOLD_MAX) + priv->missed_beacon_threshold = + IWL_MISSED_BEACON_THRESHOLD_DEF; + else + priv->missed_beacon_threshold = missed; + + return count; +} + +static ssize_t iwl_dbgfs_plcp_delta_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[12]; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "%u\n", + priv->plcp_delta_threshold); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int plcp; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &plcp) != 1) + return -EINVAL; + if ((plcp < IWL_MAX_PLCP_ERR_THRESHOLD_MIN) || + (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX)) + priv->plcp_delta_threshold = + IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE; + else + priv->plcp_delta_threshold = plcp; + return count; +} + +static ssize_t iwl_dbgfs_rf_reset_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[300]; + const size_t bufsz = sizeof(buf); + struct iwl_rf_reset *rf_reset = &priv->rf_reset; + + pos += scnprintf(buf + pos, bufsz - pos, + "RF reset statistics\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request: %d\n", + rf_reset->reset_request_count); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request success: %d\n", + rf_reset->reset_success_count); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request reject: %d\n", + rf_reset->reset_reject_count); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_rf_reset_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int ret; + + ret = iwl_force_rf_reset(priv, true); + return ret ? ret : count; +} + +static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int flush; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &flush) != 1) + return -EINVAL; + + if (iwl_is_rfkill(priv)) + return -EFAULT; + + iwlagn_dev_txfifo_flush(priv); + + return count; +} + +static ssize_t iwl_dbgfs_bt_traffic_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char buf[200]; + const size_t bufsz = sizeof(buf); + + if (!priv->bt_enable_flag) { + pos += scnprintf(buf + pos, bufsz - pos, "BT coex disabled\n"); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); + } + pos += scnprintf(buf + pos, bufsz - pos, "BT enable flag: 0x%x\n", + priv->bt_enable_flag); + pos += scnprintf(buf + pos, bufsz - pos, "BT in %s mode\n", + priv->bt_full_concurrent ? "full concurrency" : "3-wire"); + pos += scnprintf(buf + pos, bufsz - pos, "BT status: %s, " + "last traffic notif: %d\n", + priv->bt_status ? "On" : "Off", priv->last_bt_traffic_load); + pos += scnprintf(buf + pos, bufsz - pos, "ch_announcement: %d, " + "kill_ack_mask: %x, kill_cts_mask: %x\n", + priv->bt_ch_announce, priv->kill_ack_mask, + priv->kill_cts_mask); + + pos += scnprintf(buf + pos, bufsz - pos, "bluetooth traffic load: "); + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + pos += scnprintf(buf + pos, bufsz - pos, "Continuous\n"); + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + pos += scnprintf(buf + pos, bufsz - pos, "High\n"); + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + pos += scnprintf(buf + pos, bufsz - pos, "Low\n"); + break; + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + default: + pos += scnprintf(buf + pos, bufsz - pos, "None\n"); + break; + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_protection_mode_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + + int pos = 0; + char buf[40]; + const size_t bufsz = sizeof(buf); + + if (priv->cfg->ht_params) + pos += scnprintf(buf + pos, bufsz - pos, + "use %s for aggregation\n", + (priv->hw_params.use_rts_for_aggregation) ? + "rts/cts" : "cts-to-self"); + else + pos += scnprintf(buf + pos, bufsz - pos, "N/A"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_protection_mode_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int rts; + + if (!priv->cfg->ht_params) + return -EINVAL; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &rts) != 1) + return -EINVAL; + if (rts) + priv->hw_params.use_rts_for_aggregation = true; + else + priv->hw_params.use_rts_for_aggregation = false; + return count; +} + +static int iwl_cmd_echo_test(struct iwl_priv *priv) +{ + int ret; + struct iwl_host_cmd cmd = { + .id = REPLY_ECHO, + .len = { 0 }, + }; + + ret = iwl_dvm_send_cmd(priv, &cmd); + if (ret) + IWL_ERR(priv, "echo testing fail: 0X%x\n", ret); + else + IWL_DEBUG_INFO(priv, "echo testing pass\n"); + return ret; +} + +static ssize_t iwl_dbgfs_echo_test_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + iwl_cmd_echo_test(priv); + return count; +} + +#ifdef CONFIG_IWLWIFI_DEBUG +static ssize_t iwl_dbgfs_log_event_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char *buf = NULL; + ssize_t ret; + + ret = iwl_dump_nic_event_log(priv, true, &buf); + if (ret > 0) + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_log_event_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + u32 event_log_flag; + char buf[8]; + int buf_size; + + /* check that the interface is up */ + if (!iwl_is_ready(priv)) + return -EAGAIN; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &event_log_flag) != 1) + return -EFAULT; + if (event_log_flag == 1) + iwl_dump_nic_event_log(priv, true, NULL); + + return count; +} +#endif + +static ssize_t iwl_dbgfs_calib_disabled_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[120]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, + "Sensitivity calibrations %s\n", + (priv->calib_disabled & + IWL_SENSITIVITY_CALIB_DISABLED) ? + "DISABLED" : "ENABLED"); + pos += scnprintf(buf + pos, bufsz - pos, + "Chain noise calibrations %s\n", + (priv->calib_disabled & + IWL_CHAIN_NOISE_CALIB_DISABLED) ? + "DISABLED" : "ENABLED"); + pos += scnprintf(buf + pos, bufsz - pos, + "Tx power calibrations %s\n", + (priv->calib_disabled & + IWL_TX_POWER_CALIB_DISABLED) ? + "DISABLED" : "ENABLED"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_calib_disabled_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + u32 calib_disabled; + int buf_size; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &calib_disabled) != 1) + return -EFAULT; + + priv->calib_disabled = calib_disabled; + + return count; +} + +static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + bool restart_fw = iwlwifi_mod_params.restart_fw; + int ret; + + iwlwifi_mod_params.restart_fw = true; + + mutex_lock(&priv->mutex); + + /* take the return value to make compiler happy - it will fail anyway */ + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, 0, 0, NULL); + + mutex_unlock(&priv->mutex); + + iwlwifi_mod_params.restart_fw = restart_fw; + + return count; +} + +DEBUGFS_READ_FILE_OPS(ucode_rx_stats); +DEBUGFS_READ_FILE_OPS(ucode_tx_stats); +DEBUGFS_READ_FILE_OPS(ucode_general_stats); +DEBUGFS_READ_FILE_OPS(sensitivity); +DEBUGFS_READ_FILE_OPS(chain_noise); +DEBUGFS_READ_FILE_OPS(power_save_status); +DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics); +DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing); +DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon); +DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta); +DEBUGFS_READ_WRITE_FILE_OPS(rf_reset); +DEBUGFS_READ_FILE_OPS(rxon_flags); +DEBUGFS_READ_FILE_OPS(rxon_filter_flags); +DEBUGFS_WRITE_FILE_OPS(txfifo_flush); +DEBUGFS_READ_FILE_OPS(ucode_bt_stats); +DEBUGFS_READ_FILE_OPS(bt_traffic); +DEBUGFS_READ_WRITE_FILE_OPS(protection_mode); +DEBUGFS_READ_FILE_OPS(reply_tx_error); +DEBUGFS_WRITE_FILE_OPS(echo_test); +DEBUGFS_WRITE_FILE_OPS(fw_restart); +#ifdef CONFIG_IWLWIFI_DEBUG +DEBUGFS_READ_WRITE_FILE_OPS(log_event); +#endif +DEBUGFS_READ_WRITE_FILE_OPS(calib_disabled); + +/* + * Create the debugfs files and directories + * + */ +int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir) +{ + struct dentry *dir_data, *dir_rf, *dir_debug; + + priv->debugfs_dir = dbgfs_dir; + + dir_data = debugfs_create_dir("data", dbgfs_dir); + if (!dir_data) + goto err; + dir_rf = debugfs_create_dir("rf", dbgfs_dir); + if (!dir_rf) + goto err; + dir_debug = debugfs_create_dir("debug", dbgfs_dir); + if (!dir_debug) + goto err; + + DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(wowlan_sram, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(rx_handlers, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(sleep_level_override, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(current_sleep_command, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(thermal_throttling, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(temperature, dir_data, S_IRUSR); + + DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(rf_reset, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(ucode_bt_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(reply_tx_error, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(fw_restart, dir_debug, S_IWUSR); +#ifdef CONFIG_IWLWIFI_DEBUG + DEBUGFS_ADD_FILE(log_event, dir_debug, S_IWUSR | S_IRUSR); +#endif + + if (iwl_advanced_bt_coexist(priv)) + DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR); + + /* Calibrations disabled/enabled status*/ + DEBUGFS_ADD_FILE(calib_disabled, dir_rf, S_IWUSR | S_IRUSR); + + /* + * Create a symlink with mac80211. This is not very robust, as it does + * not remove the symlink created. The implicit assumption is that + * when the opmode exits, mac80211 will also exit, and will remove + * this symlink as part of its cleanup. + */ + if (priv->mac80211_registered) { + char buf[100]; + struct dentry *mac80211_dir, *dev_dir, *root_dir; + + dev_dir = dbgfs_dir->d_parent; + root_dir = dev_dir->d_parent; + mac80211_dir = priv->hw->wiphy->debugfsdir; + + snprintf(buf, 100, "../../%s/%s", root_dir->d_name.name, + dev_dir->d_name.name); + + if (!debugfs_create_symlink("iwlwifi", mac80211_dir, buf)) + goto err; + } + + return 0; + +err: + IWL_ERR(priv, "failed to create the dvm debugfs entries\n"); + return -ENOMEM; +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/dev.h b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h new file mode 100644 index 000000000000..0ba3e56d6015 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h @@ -0,0 +1,949 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +/* + * Please use this file (dev.h) for driver implementation definitions. + * Please use commands.h for uCode API definitions. + */ + +#ifndef __iwl_dev_h__ +#define __iwl_dev_h__ + +#include +#include +#include +#include +#include +#include + +#include "iwl-fw.h" +#include "iwl-eeprom-parse.h" +#include "iwl-csr.h" +#include "iwl-debug.h" +#include "iwl-agn-hw.h" +#include "iwl-op-mode.h" +#include "iwl-notif-wait.h" +#include "iwl-trans.h" + +#include "led.h" +#include "power.h" +#include "rs.h" +#include "tt.h" + +/* CT-KILL constants */ +#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ +#define CT_KILL_THRESHOLD 114 /* in Celsius */ +#define CT_KILL_EXIT_THRESHOLD 95 /* in Celsius */ + +/* Default noise level to report when noise measurement is not available. + * This may be because we're: + * 1) Not associated no beacon statistics being sent to driver) + * 2) Scanning (noise measurement does not apply to associated channel) + * Use default noise value of -127 ... this is below the range of measurable + * Rx dBm for all agn devices, so it can indicate "unmeasurable" to user. + * Also, -127 works better than 0 when averaging frames with/without + * noise info (e.g. averaging might be done in app); measured dBm values are + * always negative ... using a negative value as the default keeps all + * averages within an s8's (used in some apps) range of negative values. */ +#define IWL_NOISE_MEAS_NOT_AVAILABLE (-127) + +/* + * RTS threshold here is total size [2347] minus 4 FCS bytes + * Per spec: + * a value of 0 means RTS on all data/management packets + * a value > max MSDU size means no RTS + * else RTS for data/management frames where MPDU is larger + * than RTS value. + */ +#define DEFAULT_RTS_THRESHOLD 2347U +#define MIN_RTS_THRESHOLD 0U +#define MAX_RTS_THRESHOLD 2347U +#define MAX_MSDU_SIZE 2304U +#define MAX_MPDU_SIZE 2346U +#define DEFAULT_BEACON_INTERVAL 200U +#define DEFAULT_SHORT_RETRY_LIMIT 7U +#define DEFAULT_LONG_RETRY_LIMIT 4U + +#define IWL_NUM_SCAN_RATES (2) + + +#define IEEE80211_DATA_LEN 2304 +#define IEEE80211_4ADDR_LEN 30 +#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) +#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) + +#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 +#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 +#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 + +#define IWL_SUPPORTED_RATES_IE_LEN 8 + +#define IWL_INVALID_RATE 0xFF +#define IWL_INVALID_VALUE -1 + +union iwl_ht_rate_supp { + u16 rates; + struct { + u8 siso_rate; + u8 mimo_rate; + }; +}; + +struct iwl_ht_config { + bool single_chain_sufficient; + enum ieee80211_smps_mode smps; /* current smps mode */ +}; + +/* QoS structures */ +struct iwl_qos_info { + int qos_active; + struct iwl_qosparam_cmd def_qos_parm; +}; + +/** + * enum iwl_agg_state + * + * The state machine of the BA agreement establishment / tear down. + * These states relate to a specific RA / TID. + * + * @IWL_AGG_OFF: aggregation is not used + * @IWL_AGG_STARTING: aggregation are starting (between start and oper) + * @IWL_AGG_ON: aggregation session is up + * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the + * HW queue to be empty from packets for this RA /TID. + * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the + * HW queue to be empty from packets for this RA /TID. + */ +enum iwl_agg_state { + IWL_AGG_OFF = 0, + IWL_AGG_STARTING, + IWL_AGG_ON, + IWL_EMPTYING_HW_QUEUE_ADDBA, + IWL_EMPTYING_HW_QUEUE_DELBA, +}; + +/** + * struct iwl_ht_agg - aggregation state machine + + * This structs holds the states for the BA agreement establishment and tear + * down. It also holds the state during the BA session itself. This struct is + * duplicated for each RA / TID. + + * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the + * Tx response (REPLY_TX), and the block ack notification + * (REPLY_COMPRESSED_BA). + * @state: state of the BA agreement establishment / tear down. + * @txq_id: Tx queue used by the BA session + * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or + * the first packet to be sent in legacy HW queue in Tx AGG stop flow. + * Basically when next_reclaimed reaches ssn, we can tell mac80211 that + * we are ready to finish the Tx AGG stop / start flow. + * @wait_for_ba: Expect block-ack before next Tx reply + */ +struct iwl_ht_agg { + u32 rate_n_flags; + enum iwl_agg_state state; + u16 txq_id; + u16 ssn; + bool wait_for_ba; +}; + +/** + * struct iwl_tid_data - one for each RA / TID + + * This structs holds the states for each RA / TID. + + * @seq_number: the next WiFi sequence number to use + * @next_reclaimed: the WiFi sequence number of the next packet to be acked. + * This is basically (last acked packet++). + * @agg: aggregation state machine + */ +struct iwl_tid_data { + u16 seq_number; + u16 next_reclaimed; + struct iwl_ht_agg agg; +}; + +/* + * Structure should be accessed with sta_lock held. When station addition + * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only + * the commands (iwl_addsta_cmd and iwl_link_quality_cmd) without sta_lock + * held. + */ +struct iwl_station_entry { + struct iwl_addsta_cmd sta; + u8 used, ctxid; + struct iwl_link_quality_cmd *lq; +}; + +/* + * iwl_station_priv: Driver's private station information + * + * When mac80211 creates a station it reserves some space (hw->sta_data_size) + * in the structure for use by driver. This structure is places in that + * space. + */ +struct iwl_station_priv { + struct iwl_rxon_context *ctx; + struct iwl_lq_sta lq_sta; + atomic_t pending_frames; + bool client; + bool asleep; + u8 max_agg_bufsize; + u8 sta_id; +}; + +/** + * struct iwl_vif_priv - driver's private per-interface information + * + * When mac80211 allocates a virtual interface, it can allocate + * space for us to put data into. + */ +struct iwl_vif_priv { + struct iwl_rxon_context *ctx; + u8 ibss_bssid_sta_id; +}; + +struct iwl_sensitivity_ranges { + u16 min_nrg_cck; + + u16 nrg_th_cck; + u16 nrg_th_ofdm; + + u16 auto_corr_min_ofdm; + u16 auto_corr_min_ofdm_mrc; + u16 auto_corr_min_ofdm_x1; + u16 auto_corr_min_ofdm_mrc_x1; + + u16 auto_corr_max_ofdm; + u16 auto_corr_max_ofdm_mrc; + u16 auto_corr_max_ofdm_x1; + u16 auto_corr_max_ofdm_mrc_x1; + + u16 auto_corr_max_cck; + u16 auto_corr_max_cck_mrc; + u16 auto_corr_min_cck; + u16 auto_corr_min_cck_mrc; + + u16 barker_corr_th_min; + u16 barker_corr_th_min_mrc; + u16 nrg_th_cca; +}; + + +#define KELVIN_TO_CELSIUS(x) ((x)-273) +#define CELSIUS_TO_KELVIN(x) ((x)+273) + + +/****************************************************************************** + * + * Functions implemented in core module which are forward declared here + * for use by iwl-[4-5].c + * + * NOTE: The implementation of these functions are not hardware specific + * which is why they are in the core module files. + * + * Naming convention -- + * iwl_ <-- Is part of iwlwifi + * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) + * + ****************************************************************************/ +void iwl_update_chain_flags(struct iwl_priv *priv); +extern const u8 iwl_bcast_addr[ETH_ALEN]; + +#define IWL_OPERATION_MODE_AUTO 0 +#define IWL_OPERATION_MODE_HT_ONLY 1 +#define IWL_OPERATION_MODE_MIXED 2 +#define IWL_OPERATION_MODE_20MHZ 3 + +#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000 + +/* Sensitivity and chain noise calibration */ +#define INITIALIZATION_VALUE 0xFFFF +#define IWL_CAL_NUM_BEACONS 16 +#define MAXIMUM_ALLOWED_PATHLOSS 15 + +#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3 + +#define MAX_FA_OFDM 50 +#define MIN_FA_OFDM 5 +#define MAX_FA_CCK 50 +#define MIN_FA_CCK 5 + +#define AUTO_CORR_STEP_OFDM 1 + +#define AUTO_CORR_STEP_CCK 3 +#define AUTO_CORR_MAX_TH_CCK 160 + +#define NRG_DIFF 2 +#define NRG_STEP_CCK 2 +#define NRG_MARGIN 8 +#define MAX_NUMBER_CCK_NO_FA 100 + +#define AUTO_CORR_CCK_MIN_VAL_DEF (125) + +#define CHAIN_A 0 +#define CHAIN_B 1 +#define CHAIN_C 2 +#define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4 +#define ALL_BAND_FILTER 0xFF00 +#define IN_BAND_FILTER 0xFF +#define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF + +#define NRG_NUM_PREV_STAT_L 20 +#define NUM_RX_CHAINS 3 + +enum iwlagn_false_alarm_state { + IWL_FA_TOO_MANY = 0, + IWL_FA_TOO_FEW = 1, + IWL_FA_GOOD_RANGE = 2, +}; + +enum iwlagn_chain_noise_state { + IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */ + IWL_CHAIN_NOISE_ACCUMULATE, + IWL_CHAIN_NOISE_CALIBRATED, + IWL_CHAIN_NOISE_DONE, +}; + +/* Sensitivity calib data */ +struct iwl_sensitivity_data { + u32 auto_corr_ofdm; + u32 auto_corr_ofdm_mrc; + u32 auto_corr_ofdm_x1; + u32 auto_corr_ofdm_mrc_x1; + u32 auto_corr_cck; + u32 auto_corr_cck_mrc; + + u32 last_bad_plcp_cnt_ofdm; + u32 last_fa_cnt_ofdm; + u32 last_bad_plcp_cnt_cck; + u32 last_fa_cnt_cck; + + u32 nrg_curr_state; + u32 nrg_prev_state; + u32 nrg_value[10]; + u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L]; + u32 nrg_silence_ref; + u32 nrg_energy_idx; + u32 nrg_silence_idx; + u32 nrg_th_cck; + s32 nrg_auto_corr_silence_diff; + u32 num_in_cck_no_fa; + u32 nrg_th_ofdm; + + u16 barker_corr_th_min; + u16 barker_corr_th_min_mrc; + u16 nrg_th_cca; +}; + +/* Chain noise (differential Rx gain) calib data */ +struct iwl_chain_noise_data { + u32 active_chains; + u32 chain_noise_a; + u32 chain_noise_b; + u32 chain_noise_c; + u32 chain_signal_a; + u32 chain_signal_b; + u32 chain_signal_c; + u16 beacon_count; + u8 disconn_array[NUM_RX_CHAINS]; + u8 delta_gain_code[NUM_RX_CHAINS]; + u8 radio_write; + u8 state; +}; + +enum { + MEASUREMENT_READY = (1 << 0), + MEASUREMENT_ACTIVE = (1 << 1), +}; + +/* reply_tx_statistics (for _agn devices) */ +struct reply_tx_error_statistics { + u32 pp_delay; + u32 pp_few_bytes; + u32 pp_bt_prio; + u32 pp_quiet_period; + u32 pp_calc_ttak; + u32 int_crossed_retry; + u32 short_limit; + u32 long_limit; + u32 fifo_underrun; + u32 drain_flow; + u32 rfkill_flush; + u32 life_expire; + u32 dest_ps; + u32 host_abort; + u32 bt_retry; + u32 sta_invalid; + u32 frag_drop; + u32 tid_disable; + u32 fifo_flush; + u32 insuff_cf_poll; + u32 fail_hw_drop; + u32 sta_color_mismatch; + u32 unknown; +}; + +/* reply_agg_tx_statistics (for _agn devices) */ +struct reply_agg_tx_error_statistics { + u32 underrun; + u32 bt_prio; + u32 few_bytes; + u32 abort; + u32 last_sent_ttl; + u32 last_sent_try; + u32 last_sent_bt_kill; + u32 scd_query; + u32 bad_crc32; + u32 response; + u32 dump_tx; + u32 delay_tx; + u32 unknown; +}; + +/* + * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds + * to perform continuous uCode event logging operation if enabled + */ +#define UCODE_TRACE_PERIOD (10) + +/* + * iwl_event_log: current uCode event log position + * + * @ucode_trace: enable/disable ucode continuous trace timer + * @num_wraps: how many times the event buffer wraps + * @next_entry: the entry just before the next one that uCode would fill + * @non_wraps_count: counter for no wrap detected when dump ucode events + * @wraps_once_count: counter for wrap once detected when dump ucode events + * @wraps_more_count: counter for wrap more than once detected + * when dump ucode events + */ +struct iwl_event_log { + bool ucode_trace; + u32 num_wraps; + u32 next_entry; + int non_wraps_count; + int wraps_once_count; + int wraps_more_count; +}; + +#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3) + +/* BT Antenna Coupling Threshold (dB) */ +#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35) + +/* Firmware reload counter and Timestamp */ +#define IWL_MIN_RELOAD_DURATION 1000 /* 1000 ms */ +#define IWL_MAX_CONTINUE_RELOAD_CNT 4 + + +struct iwl_rf_reset { + int reset_request_count; + int reset_success_count; + int reset_reject_count; + unsigned long last_reset_jiffies; +}; + +enum iwl_rxon_context_id { + IWL_RXON_CTX_BSS, + IWL_RXON_CTX_PAN, + + NUM_IWL_RXON_CTX +}; + +/* extend beacon time format bit shifting */ +/* + * for _agn devices + * bits 31:22 - extended + * bits 21:0 - interval + */ +#define IWLAGN_EXT_BEACON_TIME_POS 22 + +struct iwl_rxon_context { + struct ieee80211_vif *vif; + + u8 mcast_queue; + u8 ac_to_queue[IEEE80211_NUM_ACS]; + u8 ac_to_fifo[IEEE80211_NUM_ACS]; + + /* + * We could use the vif to indicate active, but we + * also need it to be active during disabling when + * we already removed the vif for type setting. + */ + bool always_active, is_active; + + bool ht_need_multiple_chains; + + enum iwl_rxon_context_id ctxid; + + u32 interface_modes, exclusive_interface_modes; + u8 unused_devtype, ap_devtype, ibss_devtype, station_devtype; + + /* + * We declare this const so it can only be + * changed via explicit cast within the + * routines that actually update the physical + * hardware. + */ + const struct iwl_rxon_cmd active; + struct iwl_rxon_cmd staging; + + struct iwl_rxon_time_cmd timing; + + struct iwl_qos_info qos_data; + + u8 bcast_sta_id, ap_sta_id; + + u8 rxon_cmd, rxon_assoc_cmd, rxon_timing_cmd; + u8 qos_cmd; + u8 wep_key_cmd; + + struct iwl_wep_key wep_keys[WEP_KEYS_MAX]; + u8 key_mapping_keys; + + __le32 station_flags; + + int beacon_int; + + struct { + bool non_gf_sta_present; + u8 protection; + bool enabled, is_40mhz; + u8 extension_chan_offset; + } ht; +}; + +enum iwl_scan_type { + IWL_SCAN_NORMAL, + IWL_SCAN_RADIO_RESET, +}; + +/** + * struct iwl_hw_params + * + * Holds the module parameters + * + * @tx_chains_num: Number of TX chains + * @rx_chains_num: Number of RX chains + * @ct_kill_threshold: temperature threshold - in hw dependent unit + * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit + * relevant for 1000, 6000 and up + * @struct iwl_sensitivity_ranges: range of sensitivity values + * @use_rts_for_aggregation: use rts/cts protection for HT traffic + */ +struct iwl_hw_params { + u8 tx_chains_num; + u8 rx_chains_num; + bool use_rts_for_aggregation; + u32 ct_kill_threshold; + u32 ct_kill_exit_threshold; + + const struct iwl_sensitivity_ranges *sens; +}; + +/** + * struct iwl_dvm_bt_params - DVM specific BT (coex) parameters + * @advanced_bt_coexist: support advanced bt coexist + * @bt_init_traffic_load: specify initial bt traffic load + * @bt_prio_boost: default bt priority boost value + * @agg_time_limit: maximum number of uSec in aggregation + * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode + */ +struct iwl_dvm_bt_params { + bool advanced_bt_coexist; + u8 bt_init_traffic_load; + u32 bt_prio_boost; + u16 agg_time_limit; + bool bt_sco_disable; + bool bt_session_2; +}; + +/** + * struct iwl_dvm_cfg - DVM firmware specific device configuration + * @set_hw_params: set hardware parameters + * @set_channel_switch: send channel switch command + * @nic_config: apply device specific configuration + * @temperature: read temperature + * @adv_thermal_throttle: support advance thermal throttle + * @support_ct_kill_exit: support ct kill exit condition + * @plcp_delta_threshold: plcp error rate threshold used to trigger + * radio tuning when there is a high receiving plcp error rate + * @chain_noise_scale: default chain noise scale used for gain computation + * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up + * @no_idle_support: do not support idle mode + * @bt_params: pointer to BT parameters + * @need_temp_offset_calib: need to perform temperature offset calibration + * @no_xtal_calib: some devices do not need crystal calibration data, + * don't send it to those + * @temp_offset_v2: support v2 of temperature offset calibration + * @adv_pm: advanced power management + */ +struct iwl_dvm_cfg { + void (*set_hw_params)(struct iwl_priv *priv); + int (*set_channel_switch)(struct iwl_priv *priv, + struct ieee80211_channel_switch *ch_switch); + void (*nic_config)(struct iwl_priv *priv); + void (*temperature)(struct iwl_priv *priv); + + const struct iwl_dvm_bt_params *bt_params; + s32 chain_noise_scale; + u8 plcp_delta_threshold; + bool adv_thermal_throttle; + bool support_ct_kill_exit; + bool hd_v2; + bool no_idle_support; + bool need_temp_offset_calib; + bool no_xtal_calib; + bool temp_offset_v2; + bool adv_pm; +}; + +struct iwl_wipan_noa_data { + struct rcu_head rcu_head; + u32 length; + u8 data[]; +}; + +/* Calibration disabling bit mask */ +enum { + IWL_CALIB_ENABLE_ALL = 0, + + IWL_SENSITIVITY_CALIB_DISABLED = BIT(0), + IWL_CHAIN_NOISE_CALIB_DISABLED = BIT(1), + IWL_TX_POWER_CALIB_DISABLED = BIT(2), + + IWL_CALIB_DISABLE_ALL = 0xFFFFFFFF, +}; + +#define IWL_OP_MODE_GET_DVM(_iwl_op_mode) \ + ((struct iwl_priv *) ((_iwl_op_mode)->op_mode_specific)) + +#define IWL_MAC80211_GET_DVM(_hw) \ + ((struct iwl_priv *) ((struct iwl_op_mode *) \ + (_hw)->priv)->op_mode_specific) + +struct iwl_priv { + + struct iwl_trans *trans; + struct device *dev; /* for debug prints only */ + const struct iwl_cfg *cfg; + const struct iwl_fw *fw; + const struct iwl_dvm_cfg *lib; + unsigned long status; + + spinlock_t sta_lock; + struct mutex mutex; + + unsigned long transport_queue_stop; + bool passive_no_rx; +#define IWL_INVALID_MAC80211_QUEUE 0xff + u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; + atomic_t queue_stop_count[IWL_MAX_HW_QUEUES]; + + unsigned long agg_q_alloc[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; + + /* ieee device used by generic ieee processing code */ + struct ieee80211_hw *hw; + + struct napi_struct *napi; + + struct list_head calib_results; + + struct workqueue_struct *workqueue; + + struct iwl_hw_params hw_params; + + enum ieee80211_band band; + u8 valid_contexts; + + void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb); + + struct iwl_notif_wait_data notif_wait; + + /* spectrum measurement report caching */ + struct iwl_spectrum_notification measure_report; + u8 measurement_status; + + /* ucode beacon time */ + u32 ucode_beacon_time; + int missed_beacon_threshold; + + /* track IBSS manager (last beacon) status */ + u32 ibss_manager; + + /* jiffies when last recovery from statistics was performed */ + unsigned long rx_statistics_jiffies; + + /*counters */ + u32 rx_handlers_stats[REPLY_MAX]; + + /* rf reset */ + struct iwl_rf_reset rf_reset; + + /* firmware reload counter and timestamp */ + unsigned long reload_jiffies; + int reload_count; + bool ucode_loaded; + + u8 plcp_delta_threshold; + + /* thermal calibration */ + s32 temperature; /* Celsius */ + s32 last_temperature; + + struct iwl_wipan_noa_data __rcu *noa_data; + + /* Scan related variables */ + unsigned long scan_start; + unsigned long scan_start_tsf; + void *scan_cmd; + enum ieee80211_band scan_band; + struct cfg80211_scan_request *scan_request; + struct ieee80211_vif *scan_vif; + enum iwl_scan_type scan_type; + u8 scan_tx_ant[IEEE80211_NUM_BANDS]; + u8 mgmt_tx_ant; + + /* max number of station keys */ + u8 sta_key_max_num; + + bool new_scan_threshold_behaviour; + + bool wowlan; + + /* EEPROM MAC addresses */ + struct mac_address addresses[2]; + + struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX]; + + __le16 switch_channel; + + u8 start_calib; + struct iwl_sensitivity_data sensitivity_data; + struct iwl_chain_noise_data chain_noise_data; + __le16 sensitivity_tbl[HD_TABLE_SIZE]; + __le16 enhance_sensitivity_tbl[ENHANCE_HD_TABLE_ENTRIES]; + + struct iwl_ht_config current_ht_config; + + /* Rate scaling data */ + u8 retry_rate; + + int activity_timer_active; + + struct iwl_power_mgr power_data; + struct iwl_tt_mgmt thermal_throttle; + + /* station table variables */ + int num_stations; + struct iwl_station_entry stations[IWLAGN_STATION_COUNT]; + unsigned long ucode_key_table; + struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT]; + atomic_t num_aux_in_flight; + + u8 mac80211_registered; + + /* Indication if ieee80211_ops->open has been called */ + u8 is_open; + + enum nl80211_iftype iw_mode; + + /* Last Rx'd beacon timestamp */ + u64 timestamp; + + struct { + __le32 flag; + struct statistics_general_common common; + struct statistics_rx_non_phy rx_non_phy; + struct statistics_rx_phy rx_ofdm; + struct statistics_rx_ht_phy rx_ofdm_ht; + struct statistics_rx_phy rx_cck; + struct statistics_tx tx; +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct statistics_bt_activity bt_activity; + __le32 num_bt_kills, accum_num_bt_kills; +#endif + spinlock_t lock; + } statistics; +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct { + struct statistics_general_common common; + struct statistics_rx_non_phy rx_non_phy; + struct statistics_rx_phy rx_ofdm; + struct statistics_rx_ht_phy rx_ofdm_ht; + struct statistics_rx_phy rx_cck; + struct statistics_tx tx; + struct statistics_bt_activity bt_activity; + } accum_stats, delta_stats, max_delta_stats; +#endif + + /* + * reporting the number of tids has AGG on. 0 means + * no AGGREGATION + */ + u8 agg_tids_count; + + struct iwl_rx_phy_res last_phy_res; + u32 ampdu_ref; + bool last_phy_res_valid; + + /* + * chain noise reset and gain commands are the + * two extra calibration commands follows the standard + * phy calibration commands + */ + u8 phy_calib_chain_noise_reset_cmd; + u8 phy_calib_chain_noise_gain_cmd; + + /* counts reply_tx error */ + struct reply_tx_error_statistics reply_tx_stats; + struct reply_agg_tx_error_statistics reply_agg_tx_stats; + + /* bt coex */ + u8 bt_enable_flag; + u8 bt_status; + u8 bt_traffic_load, last_bt_traffic_load; + bool bt_ch_announce; + bool bt_full_concurrent; + bool bt_ant_couple_ok; + __le32 kill_ack_mask; + __le32 kill_cts_mask; + __le16 bt_valid; + bool reduced_txpower; + u16 bt_on_thresh; + u16 bt_duration; + u16 dynamic_frag_thresh; + u8 bt_ci_compliance; + struct work_struct bt_traffic_change_work; + bool bt_enable_pspoll; + struct iwl_rxon_context *cur_rssi_ctx; + bool bt_is_sco; + + struct work_struct restart; + struct work_struct scan_completed; + struct work_struct abort_scan; + + struct work_struct beacon_update; + struct iwl_rxon_context *beacon_ctx; + struct sk_buff *beacon_skb; + void *beacon_cmd; + + struct work_struct tt_work; + struct work_struct ct_enter; + struct work_struct ct_exit; + struct work_struct start_internal_scan; + struct work_struct tx_flush; + struct work_struct bt_full_concurrency; + struct work_struct bt_runtime_config; + + struct delayed_work scan_check; + + /* TX Power settings */ + s8 tx_power_user_lmt; + s8 tx_power_next; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* debugfs */ + struct dentry *debugfs_dir; + u32 dbgfs_sram_offset, dbgfs_sram_len; + bool disable_ht40; + void *wowlan_sram; +#endif /* CONFIG_IWLWIFI_DEBUGFS */ + + struct iwl_nvm_data *nvm_data; + /* eeprom blob for debugfs */ + u8 *eeprom_blob; + size_t eeprom_blob_size; + + struct work_struct txpower_work; + u32 calib_disabled; + struct work_struct run_time_calib_work; + struct timer_list statistics_periodic; + struct timer_list ucode_trace; + + struct iwl_event_log event_log; + +#ifdef CONFIG_IWLWIFI_LEDS + struct led_classdev led; + unsigned long blink_on, blink_off; + bool led_registered; +#endif + + /* WoWLAN GTK rekey data */ + u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; + __le64 replay_ctr; + __le16 last_seq_ctl; + bool have_rekey_data; +#ifdef CONFIG_PM_SLEEP + struct wiphy_wowlan_support wowlan_support; +#endif + + /* device_pointers: pointers to ucode event tables */ + struct { + u32 error_event_table; + u32 log_event_table; + } device_pointers; + + /* indicator of loaded ucode image */ + enum iwl_ucode_type cur_ucode; +}; /*iwl_priv */ + +static inline struct iwl_rxon_context * +iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif) +{ + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + + return vif_priv->ctx; +} + +#define for_each_context(priv, ctx) \ + for (ctx = &priv->contexts[IWL_RXON_CTX_BSS]; \ + ctx < &priv->contexts[NUM_IWL_RXON_CTX]; ctx++) \ + if (priv->valid_contexts & BIT(ctx->ctxid)) + +static inline int iwl_is_associated_ctx(struct iwl_rxon_context *ctx) +{ + return (ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; +} + +static inline int iwl_is_associated(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid) +{ + return iwl_is_associated_ctx(&priv->contexts[ctxid]); +} + +static inline int iwl_is_any_associated(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + for_each_context(priv, ctx) + if (iwl_is_associated_ctx(ctx)) + return true; + return false; +} + +#endif /* __iwl_dev_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/devices.c b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c new file mode 100644 index 000000000000..34b41e5f7cfc --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c @@ -0,0 +1,690 @@ +/****************************************************************************** + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +/* + * DVM device-specific data & functions + */ +#include "iwl-io.h" +#include "iwl-prph.h" +#include "iwl-eeprom-parse.h" + +#include "agn.h" +#include "dev.h" +#include "commands.h" + + +/* + * 1000 series + * =========== + */ + +/* + * For 1000, use advance thermal throttling critical temperature threshold, + * but legacy thermal management implementation for now. + * This is for the reason of 1000 uCode using advance thermal throttling API + * but not implement ct_kill_exit based on ct_kill exit temperature + * so the thermal throttling will still based on legacy thermal throttling + * management. + * The code here need to be modified once 1000 uCode has the advanced thermal + * throttling algorithm in place + */ +static void iwl1000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; + priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; +} + +/* NIC configuration for 1000 series */ +static void iwl1000_nic_config(struct iwl_priv *priv) +{ + /* Setting digital SVR for 1000 card to 1.32V */ + /* locking is acquired in iwl_set_bits_mask_prph() function */ + iwl_set_bits_mask_prph(priv->trans, APMG_DIGITAL_SVR_REG, + APMG_SVR_DIGITAL_VOLTAGE_1_32, + ~APMG_SVR_VOLTAGE_CONFIG_BIT_MSK); +} + +/** + * iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time + * @priv -- pointer to iwl_priv data structure + * @tsf_bits -- number of bits need to shift for masking) + */ +static inline u32 iwl_beacon_time_mask_low(struct iwl_priv *priv, + u16 tsf_bits) +{ + return (1 << tsf_bits) - 1; +} + +/** + * iwl_beacon_time_mask_high - mask of higher 32 bit of beacon time + * @priv -- pointer to iwl_priv data structure + * @tsf_bits -- number of bits need to shift for masking) + */ +static inline u32 iwl_beacon_time_mask_high(struct iwl_priv *priv, + u16 tsf_bits) +{ + return ((1 << (32 - tsf_bits)) - 1) << tsf_bits; +} + +/* + * extended beacon time format + * time in usec will be changed into a 32-bit value in extended:internal format + * the extended part is the beacon counts + * the internal part is the time in usec within one beacon interval + */ +static u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, + u32 beacon_interval) +{ + u32 quot; + u32 rem; + u32 interval = beacon_interval * TIME_UNIT; + + if (!interval || !usec) + return 0; + + quot = (usec / interval) & + (iwl_beacon_time_mask_high(priv, IWLAGN_EXT_BEACON_TIME_POS) >> + IWLAGN_EXT_BEACON_TIME_POS); + rem = (usec % interval) & iwl_beacon_time_mask_low(priv, + IWLAGN_EXT_BEACON_TIME_POS); + + return (quot << IWLAGN_EXT_BEACON_TIME_POS) + rem; +} + +/* base is usually what we get from ucode with each received frame, + * the same as HW timer counter counting down + */ +static __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base, + u32 addon, u32 beacon_interval) +{ + u32 base_low = base & iwl_beacon_time_mask_low(priv, + IWLAGN_EXT_BEACON_TIME_POS); + u32 addon_low = addon & iwl_beacon_time_mask_low(priv, + IWLAGN_EXT_BEACON_TIME_POS); + u32 interval = beacon_interval * TIME_UNIT; + u32 res = (base & iwl_beacon_time_mask_high(priv, + IWLAGN_EXT_BEACON_TIME_POS)) + + (addon & iwl_beacon_time_mask_high(priv, + IWLAGN_EXT_BEACON_TIME_POS)); + + if (base_low > addon_low) + res += base_low - addon_low; + else if (base_low < addon_low) { + res += interval + base_low - addon_low; + res += (1 << IWLAGN_EXT_BEACON_TIME_POS); + } else + res += (1 << IWLAGN_EXT_BEACON_TIME_POS); + + return cpu_to_le32(res); +} + +static const struct iwl_sensitivity_ranges iwl1000_sensitivity = { + .min_nrg_cck = 95, + .auto_corr_min_ofdm = 90, + .auto_corr_min_ofdm_mrc = 170, + .auto_corr_min_ofdm_x1 = 120, + .auto_corr_min_ofdm_mrc_x1 = 240, + + .auto_corr_max_ofdm = 120, + .auto_corr_max_ofdm_mrc = 210, + .auto_corr_max_ofdm_x1 = 155, + .auto_corr_max_ofdm_mrc_x1 = 290, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 200, + .auto_corr_min_cck_mrc = 170, + .auto_corr_max_cck_mrc = 400, + .nrg_th_cck = 95, + .nrg_th_ofdm = 95, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, +}; + +static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) +{ + iwl1000_set_ct_threshold(priv); + + /* Set initial sensitivity parameters */ + priv->hw_params.sens = &iwl1000_sensitivity; +} + +const struct iwl_dvm_cfg iwl_dvm_1000_cfg = { + .set_hw_params = iwl1000_hw_set_hw_params, + .nic_config = iwl1000_nic_config, + .temperature = iwlagn_temperature, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, +}; + + +/* + * 2000 series + * =========== + */ + +static void iwl2000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; + priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; +} + +/* NIC configuration for 2000 series */ +static void iwl2000_nic_config(struct iwl_priv *priv) +{ + iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER); +} + +static const struct iwl_sensitivity_ranges iwl2000_sensitivity = { + .min_nrg_cck = 97, + .auto_corr_min_ofdm = 80, + .auto_corr_min_ofdm_mrc = 128, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 192, + + .auto_corr_max_ofdm = 145, + .auto_corr_max_ofdm_mrc = 232, + .auto_corr_max_ofdm_x1 = 110, + .auto_corr_max_ofdm_mrc_x1 = 232, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 175, + .auto_corr_min_cck_mrc = 160, + .auto_corr_max_cck_mrc = 310, + .nrg_th_cck = 97, + .nrg_th_ofdm = 100, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, +}; + +static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) +{ + iwl2000_set_ct_threshold(priv); + + /* Set initial sensitivity parameters */ + priv->hw_params.sens = &iwl2000_sensitivity; +} + +const struct iwl_dvm_cfg iwl_dvm_2000_cfg = { + .set_hw_params = iwl2000_hw_set_hw_params, + .nic_config = iwl2000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, +}; + +const struct iwl_dvm_cfg iwl_dvm_105_cfg = { + .set_hw_params = iwl2000_hw_set_hw_params, + .nic_config = iwl2000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, + .adv_pm = true, +}; + +static const struct iwl_dvm_bt_params iwl2030_bt_params = { + /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ + .advanced_bt_coexist = true, + .agg_time_limit = BT_AGG_THRESHOLD_DEF, + .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, + .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32, + .bt_sco_disable = true, + .bt_session_2 = true, +}; + +const struct iwl_dvm_cfg iwl_dvm_2030_cfg = { + .set_hw_params = iwl2000_hw_set_hw_params, + .nic_config = iwl2000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .bt_params = &iwl2030_bt_params, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, + .adv_pm = true, +}; + +/* + * 5000 series + * =========== + */ + +/* NIC configuration for 5000 series */ +static const struct iwl_sensitivity_ranges iwl5000_sensitivity = { + .min_nrg_cck = 100, + .auto_corr_min_ofdm = 90, + .auto_corr_min_ofdm_mrc = 170, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 220, + + .auto_corr_max_ofdm = 120, + .auto_corr_max_ofdm_mrc = 210, + .auto_corr_max_ofdm_x1 = 120, + .auto_corr_max_ofdm_mrc_x1 = 240, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 200, + .auto_corr_min_cck_mrc = 200, + .auto_corr_max_cck_mrc = 400, + .nrg_th_cck = 100, + .nrg_th_ofdm = 100, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, +}; + +static const struct iwl_sensitivity_ranges iwl5150_sensitivity = { + .min_nrg_cck = 95, + .auto_corr_min_ofdm = 90, + .auto_corr_min_ofdm_mrc = 170, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 220, + + .auto_corr_max_ofdm = 120, + .auto_corr_max_ofdm_mrc = 210, + /* max = min for performance bug in 5150 DSP */ + .auto_corr_max_ofdm_x1 = 105, + .auto_corr_max_ofdm_mrc_x1 = 220, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 200, + .auto_corr_min_cck_mrc = 170, + .auto_corr_max_cck_mrc = 400, + .nrg_th_cck = 95, + .nrg_th_ofdm = 95, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, +}; + +#define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF (-5) + +static s32 iwl_temp_calib_to_offset(struct iwl_priv *priv) +{ + u16 temperature, voltage; + + temperature = le16_to_cpu(priv->nvm_data->kelvin_temperature); + voltage = le16_to_cpu(priv->nvm_data->kelvin_voltage); + + /* offset = temp - volt / coeff */ + return (s32)(temperature - + voltage / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF); +} + +static void iwl5150_set_ct_threshold(struct iwl_priv *priv) +{ + const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF; + s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY) - + iwl_temp_calib_to_offset(priv); + + priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef; +} + +static void iwl5000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; +} + +static void iwl5000_hw_set_hw_params(struct iwl_priv *priv) +{ + iwl5000_set_ct_threshold(priv); + + /* Set initial sensitivity parameters */ + priv->hw_params.sens = &iwl5000_sensitivity; +} + +static void iwl5150_hw_set_hw_params(struct iwl_priv *priv) +{ + iwl5150_set_ct_threshold(priv); + + /* Set initial sensitivity parameters */ + priv->hw_params.sens = &iwl5150_sensitivity; +} + +static void iwl5150_temperature(struct iwl_priv *priv) +{ + u32 vt = 0; + s32 offset = iwl_temp_calib_to_offset(priv); + + vt = le32_to_cpu(priv->statistics.common.temperature); + vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset; + /* now vt hold the temperature in Kelvin */ + priv->temperature = KELVIN_TO_CELSIUS(vt); + iwl_tt_handler(priv); +} + +static int iwl5000_hw_channel_switch(struct iwl_priv *priv, + struct ieee80211_channel_switch *ch_switch) +{ + /* + * MULTI-FIXME + * See iwlagn_mac_channel_switch. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl5000_channel_switch_cmd cmd; + u32 switch_time_in_usec, ucode_switch_time; + u16 ch; + u32 tsf_low; + u8 switch_count; + u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval); + struct ieee80211_vif *vif = ctx->vif; + struct iwl_host_cmd hcmd = { + .id = REPLY_CHANNEL_SWITCH, + .len = { sizeof(cmd), }, + .data = { &cmd, }, + }; + + cmd.band = priv->band == IEEE80211_BAND_2GHZ; + ch = ch_switch->chandef.chan->hw_value; + IWL_DEBUG_11H(priv, "channel switch from %d to %d\n", + ctx->active.channel, ch); + cmd.channel = cpu_to_le16(ch); + cmd.rxon_flags = ctx->staging.flags; + cmd.rxon_filter_flags = ctx->staging.filter_flags; + switch_count = ch_switch->count; + tsf_low = ch_switch->timestamp & 0x0ffffffff; + /* + * calculate the ucode channel switch time + * adding TSF as one of the factor for when to switch + */ + if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) { + if (switch_count > ((priv->ucode_beacon_time - tsf_low) / + beacon_interval)) { + switch_count -= (priv->ucode_beacon_time - + tsf_low) / beacon_interval; + } else + switch_count = 0; + } + if (switch_count <= 1) + cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time); + else { + switch_time_in_usec = + vif->bss_conf.beacon_int * switch_count * TIME_UNIT; + ucode_switch_time = iwl_usecs_to_beacons(priv, + switch_time_in_usec, + beacon_interval); + cmd.switch_time = iwl_add_beacon_time(priv, + priv->ucode_beacon_time, + ucode_switch_time, + beacon_interval); + } + IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", + cmd.switch_time); + cmd.expect_beacon = + ch_switch->chandef.chan->flags & IEEE80211_CHAN_RADAR; + + return iwl_dvm_send_cmd(priv, &hcmd); +} + +const struct iwl_dvm_cfg iwl_dvm_5000_cfg = { + .set_hw_params = iwl5000_hw_set_hw_params, + .set_channel_switch = iwl5000_hw_channel_switch, + .temperature = iwlagn_temperature, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .no_idle_support = true, +}; + +const struct iwl_dvm_cfg iwl_dvm_5150_cfg = { + .set_hw_params = iwl5150_hw_set_hw_params, + .set_channel_switch = iwl5000_hw_channel_switch, + .temperature = iwl5150_temperature, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .no_idle_support = true, + .no_xtal_calib = true, +}; + + + +/* + * 6000 series + * =========== + */ + +static void iwl6000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; + priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; +} + +/* NIC configuration for 6000 series */ +static void iwl6000_nic_config(struct iwl_priv *priv) +{ + switch (priv->cfg->device_family) { + case IWL_DEVICE_FAMILY_6005: + case IWL_DEVICE_FAMILY_6030: + case IWL_DEVICE_FAMILY_6000: + break; + case IWL_DEVICE_FAMILY_6000i: + /* 2x2 IPA phy type */ + iwl_write32(priv->trans, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA); + break; + case IWL_DEVICE_FAMILY_6050: + /* Indicate calibration version to uCode. */ + if (priv->nvm_data->calib_version >= 6) + iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); + break; + case IWL_DEVICE_FAMILY_6150: + /* Indicate calibration version to uCode. */ + if (priv->nvm_data->calib_version >= 6) + iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); + iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_6050_1x2); + break; + default: + WARN_ON(1); + } +} + +static const struct iwl_sensitivity_ranges iwl6000_sensitivity = { + .min_nrg_cck = 110, + .auto_corr_min_ofdm = 80, + .auto_corr_min_ofdm_mrc = 128, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 192, + + .auto_corr_max_ofdm = 145, + .auto_corr_max_ofdm_mrc = 232, + .auto_corr_max_ofdm_x1 = 110, + .auto_corr_max_ofdm_mrc_x1 = 232, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 175, + .auto_corr_min_cck_mrc = 160, + .auto_corr_max_cck_mrc = 310, + .nrg_th_cck = 110, + .nrg_th_ofdm = 110, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 336, + .nrg_th_cca = 62, +}; + +static void iwl6000_hw_set_hw_params(struct iwl_priv *priv) +{ + iwl6000_set_ct_threshold(priv); + + /* Set initial sensitivity parameters */ + priv->hw_params.sens = &iwl6000_sensitivity; + +} + +static int iwl6000_hw_channel_switch(struct iwl_priv *priv, + struct ieee80211_channel_switch *ch_switch) +{ + /* + * MULTI-FIXME + * See iwlagn_mac_channel_switch. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl6000_channel_switch_cmd *cmd; + u32 switch_time_in_usec, ucode_switch_time; + u16 ch; + u32 tsf_low; + u8 switch_count; + u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval); + struct ieee80211_vif *vif = ctx->vif; + struct iwl_host_cmd hcmd = { + .id = REPLY_CHANNEL_SWITCH, + .len = { sizeof(*cmd), }, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + int err; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + hcmd.data[0] = cmd; + + cmd->band = priv->band == IEEE80211_BAND_2GHZ; + ch = ch_switch->chandef.chan->hw_value; + IWL_DEBUG_11H(priv, "channel switch from %u to %u\n", + ctx->active.channel, ch); + cmd->channel = cpu_to_le16(ch); + cmd->rxon_flags = ctx->staging.flags; + cmd->rxon_filter_flags = ctx->staging.filter_flags; + switch_count = ch_switch->count; + tsf_low = ch_switch->timestamp & 0x0ffffffff; + /* + * calculate the ucode channel switch time + * adding TSF as one of the factor for when to switch + */ + if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) { + if (switch_count > ((priv->ucode_beacon_time - tsf_low) / + beacon_interval)) { + switch_count -= (priv->ucode_beacon_time - + tsf_low) / beacon_interval; + } else + switch_count = 0; + } + if (switch_count <= 1) + cmd->switch_time = cpu_to_le32(priv->ucode_beacon_time); + else { + switch_time_in_usec = + vif->bss_conf.beacon_int * switch_count * TIME_UNIT; + ucode_switch_time = iwl_usecs_to_beacons(priv, + switch_time_in_usec, + beacon_interval); + cmd->switch_time = iwl_add_beacon_time(priv, + priv->ucode_beacon_time, + ucode_switch_time, + beacon_interval); + } + IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", + cmd->switch_time); + cmd->expect_beacon = + ch_switch->chandef.chan->flags & IEEE80211_CHAN_RADAR; + + err = iwl_dvm_send_cmd(priv, &hcmd); + kfree(cmd); + return err; +} + +const struct iwl_dvm_cfg iwl_dvm_6000_cfg = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, +}; + +const struct iwl_dvm_cfg iwl_dvm_6005_cfg = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .need_temp_offset_calib = true, +}; + +const struct iwl_dvm_cfg iwl_dvm_6050_cfg = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1500, +}; + +static const struct iwl_dvm_bt_params iwl6000_bt_params = { + /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ + .advanced_bt_coexist = true, + .agg_time_limit = BT_AGG_THRESHOLD_DEF, + .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, + .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, + .bt_sco_disable = true, +}; + +const struct iwl_dvm_cfg iwl_dvm_6030_cfg = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .bt_params = &iwl6000_bt_params, + .need_temp_offset_calib = true, + .adv_pm = true, +}; diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.c b/drivers/net/wireless/intel/iwlwifi/dvm/led.c new file mode 100644 index 000000000000..ca4d6692cc4e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.c @@ -0,0 +1,223 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "iwl-io.h" +#include "iwl-trans.h" +#include "iwl-modparams.h" +#include "dev.h" +#include "agn.h" + +/* Throughput OFF time(ms) ON time (ms) + * >300 25 25 + * >200 to 300 40 40 + * >100 to 200 55 55 + * >70 to 100 65 65 + * >50 to 70 75 75 + * >20 to 50 85 85 + * >10 to 20 95 95 + * >5 to 10 110 110 + * >1 to 5 130 130 + * >0 to 1 167 167 + * <=0 SOLID ON + */ +static const struct ieee80211_tpt_blink iwl_blink[] = { + { .throughput = 0, .blink_time = 334 }, + { .throughput = 1 * 1024 - 1, .blink_time = 260 }, + { .throughput = 5 * 1024 - 1, .blink_time = 220 }, + { .throughput = 10 * 1024 - 1, .blink_time = 190 }, + { .throughput = 20 * 1024 - 1, .blink_time = 170 }, + { .throughput = 50 * 1024 - 1, .blink_time = 150 }, + { .throughput = 70 * 1024 - 1, .blink_time = 130 }, + { .throughput = 100 * 1024 - 1, .blink_time = 110 }, + { .throughput = 200 * 1024 - 1, .blink_time = 80 }, + { .throughput = 300 * 1024 - 1, .blink_time = 50 }, +}; + +/* Set led register off */ +void iwlagn_led_enable(struct iwl_priv *priv) +{ + iwl_write32(priv->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON); +} + +/* + * Adjust led blink rate to compensate on a MAC Clock difference on every HW + * Led blink rate analysis showed an average deviation of 20% on 5000 series + * and up. + * Need to compensate on the led on/off time per HW according to the deviation + * to achieve the desired led frequency + * The calculation is: (100-averageDeviation)/100 * blinkTime + * For code efficiency the calculation will be: + * compensation = (100 - averageDeviation) * 64 / 100 + * NewBlinkTime = (compensation * BlinkTime) / 64 + */ +static inline u8 iwl_blink_compensation(struct iwl_priv *priv, + u8 time, u16 compensation) +{ + if (!compensation) { + IWL_ERR(priv, "undefined blink compensation: " + "use pre-defined blinking time\n"); + return time; + } + + return (u8)((time * compensation) >> 6); +} + +static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_LEDS_CMD, + .len = { sizeof(struct iwl_led_cmd), }, + .data = { led_cmd, }, + .flags = CMD_ASYNC, + }; + u32 reg; + + reg = iwl_read32(priv->trans, CSR_LED_REG); + if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) + iwl_write32(priv->trans, CSR_LED_REG, + reg & CSR_LED_BSM_CTRL_MSK); + + return iwl_dvm_send_cmd(priv, &cmd); +} + +/* Set led pattern command */ +static int iwl_led_cmd(struct iwl_priv *priv, + unsigned long on, + unsigned long off) +{ + struct iwl_led_cmd led_cmd = { + .id = IWL_LED_LINK, + .interval = IWL_DEF_LED_INTRVL + }; + int ret; + + if (!test_bit(STATUS_READY, &priv->status)) + return -EBUSY; + + if (priv->blink_on == on && priv->blink_off == off) + return 0; + + if (off == 0) { + /* led is SOLID_ON */ + on = IWL_LED_SOLID; + } + + IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n", + priv->cfg->base_params->led_compensation); + led_cmd.on = iwl_blink_compensation(priv, on, + priv->cfg->base_params->led_compensation); + led_cmd.off = iwl_blink_compensation(priv, off, + priv->cfg->base_params->led_compensation); + + ret = iwl_send_led_cmd(priv, &led_cmd); + if (!ret) { + priv->blink_on = on; + priv->blink_off = off; + } + return ret; +} + +static void iwl_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); + unsigned long on = 0; + + if (brightness > 0) + on = IWL_LED_SOLID; + + iwl_led_cmd(priv, on, 0); +} + +static int iwl_led_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); + + return iwl_led_cmd(priv, *delay_on, *delay_off); +} + +void iwl_leds_init(struct iwl_priv *priv) +{ + int mode = iwlwifi_mod_params.led_mode; + int ret; + + if (mode == IWL_LED_DISABLE) { + IWL_INFO(priv, "Led disabled\n"); + return; + } + if (mode == IWL_LED_DEFAULT) + mode = priv->cfg->led_mode; + + priv->led.name = kasprintf(GFP_KERNEL, "%s-led", + wiphy_name(priv->hw->wiphy)); + priv->led.brightness_set = iwl_led_brightness_set; + priv->led.blink_set = iwl_led_blink_set; + priv->led.max_brightness = 1; + + switch (mode) { + case IWL_LED_DEFAULT: + WARN_ON(1); + break; + case IWL_LED_BLINK: + priv->led.default_trigger = + ieee80211_create_tpt_led_trigger(priv->hw, + IEEE80211_TPT_LEDTRIG_FL_CONNECTED, + iwl_blink, ARRAY_SIZE(iwl_blink)); + break; + case IWL_LED_RF_STATE: + priv->led.default_trigger = + ieee80211_get_radio_led_name(priv->hw); + break; + } + + ret = led_classdev_register(priv->trans->dev, &priv->led); + if (ret) { + kfree(priv->led.name); + return; + } + + priv->led_registered = true; +} + +void iwl_leds_exit(struct iwl_priv *priv) +{ + if (!priv->led_registered) + return; + + led_classdev_unregister(&priv->led); + kfree(priv->led.name); +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.h b/drivers/net/wireless/intel/iwlwifi/dvm/led.h new file mode 100644 index 000000000000..1c6b2252d0f2 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_leds_h__ +#define __iwl_leds_h__ + + +struct iwl_priv; + +#define IWL_LED_SOLID 11 +#define IWL_DEF_LED_INTRVL cpu_to_le32(1000) + +#define IWL_LED_ACTIVITY (0<<1) +#define IWL_LED_LINK (1<<1) + +#ifdef CONFIG_IWLWIFI_LEDS +void iwlagn_led_enable(struct iwl_priv *priv); +void iwl_leds_init(struct iwl_priv *priv); +void iwl_leds_exit(struct iwl_priv *priv); +#else +static inline void iwlagn_led_enable(struct iwl_priv *priv) +{ +} +static inline void iwl_leds_init(struct iwl_priv *priv) +{ +} +static inline void iwl_leds_exit(struct iwl_priv *priv) +{ +} +#endif + +#endif /* __iwl_leds_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c new file mode 100644 index 000000000000..e18629a16fb0 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c @@ -0,0 +1,1300 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include "iwl-io.h" +#include "iwl-agn-hw.h" +#include "iwl-trans.h" +#include "iwl-modparams.h" + +#include "dev.h" +#include "agn.h" + +int iwlagn_hw_valid_rtc_data_addr(u32 addr) +{ + return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) && + (addr < IWLAGN_RTC_DATA_UPPER_BOUND); +} + +int iwlagn_send_tx_power(struct iwl_priv *priv) +{ + struct iwlagn_tx_power_dbm_cmd tx_power_cmd; + u8 tx_ant_cfg_cmd; + + if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->status), + "TX Power requested while scanning!\n")) + return -EAGAIN; + + /* half dBm need to multiply */ + tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt); + + if (tx_power_cmd.global_lmt > priv->nvm_data->max_tx_pwr_half_dbm) { + /* + * For the newer devices which using enhanced/extend tx power + * table in EEPROM, the format is in half dBm. driver need to + * convert to dBm format before report to mac80211. + * By doing so, there is a possibility of 1/2 dBm resolution + * lost. driver will perform "round-up" operation before + * reporting, but it will cause 1/2 dBm tx power over the + * regulatory limit. Perform the checking here, if the + * "tx_power_user_lmt" is higher than EEPROM value (in + * half-dBm format), lower the tx power based on EEPROM + */ + tx_power_cmd.global_lmt = + priv->nvm_data->max_tx_pwr_half_dbm; + } + tx_power_cmd.flags = IWLAGN_TX_POWER_NO_CLOSED; + tx_power_cmd.srv_chan_lmt = IWLAGN_TX_POWER_AUTO; + + if (IWL_UCODE_API(priv->fw->ucode_ver) == 1) + tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD_V1; + else + tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD; + + return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, 0, + sizeof(tx_power_cmd), &tx_power_cmd); +} + +void iwlagn_temperature(struct iwl_priv *priv) +{ + lockdep_assert_held(&priv->statistics.lock); + + /* store temperature from correct statistics (in Celsius) */ + priv->temperature = le32_to_cpu(priv->statistics.common.temperature); + iwl_tt_handler(priv); +} + +int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band) +{ + int idx = 0; + int band_offset = 0; + + /* HT rate format: mac80211 wants an MCS number, which is just LSB */ + if (rate_n_flags & RATE_MCS_HT_MSK) { + idx = (rate_n_flags & 0xff); + return idx; + /* Legacy rate format, search for match in table */ + } else { + if (band == IEEE80211_BAND_5GHZ) + band_offset = IWL_FIRST_OFDM_RATE; + for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) + if (iwl_rates[idx].plcp == (rate_n_flags & 0xFF)) + return idx - band_offset; + } + + return -1; +} + +int iwlagn_manage_ibss_station(struct iwl_priv *priv, + struct ieee80211_vif *vif, bool add) +{ + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + + if (add) + return iwlagn_add_bssid_station(priv, vif_priv->ctx, + vif->bss_conf.bssid, + &vif_priv->ibss_bssid_sta_id); + return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id, + vif->bss_conf.bssid); +} + +/** + * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode + * + * pre-requirements: + * 1. acquire mutex before calling + * 2. make sure rf is on and not in exit state + */ +int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk) +{ + struct iwl_txfifo_flush_cmd_v3 flush_cmd_v3 = { + .flush_control = cpu_to_le16(IWL_DROP_ALL), + }; + struct iwl_txfifo_flush_cmd_v2 flush_cmd_v2 = { + .flush_control = cpu_to_le16(IWL_DROP_ALL), + }; + + u32 queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | + IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK; + + if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) + queue_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK | + IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK | + IWL_PAN_SCD_MGMT_MSK | + IWL_PAN_SCD_MULTICAST_MSK; + + if (priv->nvm_data->sku_cap_11n_enable) + queue_control |= IWL_AGG_TX_QUEUE_MSK; + + if (scd_q_msk) + queue_control = scd_q_msk; + + IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", queue_control); + flush_cmd_v3.queue_control = cpu_to_le32(queue_control); + flush_cmd_v2.queue_control = cpu_to_le16((u16)queue_control); + + if (IWL_UCODE_API(priv->fw->ucode_ver) > 2) + return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0, + sizeof(flush_cmd_v3), + &flush_cmd_v3); + return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0, + sizeof(flush_cmd_v2), &flush_cmd_v2); +} + +void iwlagn_dev_txfifo_flush(struct iwl_priv *priv) +{ + mutex_lock(&priv->mutex); + ieee80211_stop_queues(priv->hw); + if (iwlagn_txfifo_flush(priv, 0)) { + IWL_ERR(priv, "flush request fail\n"); + goto done; + } + IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n"); + iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff); +done: + ieee80211_wake_queues(priv->hw); + mutex_unlock(&priv->mutex); +} + +/* + * BT coex + */ +/* Notmal TDM */ +static const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xc0004000), + cpu_to_le32(0x00004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), +}; + + +/* Loose Coex */ +static const __le32 iwlagn_loose_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), +}; + +/* Full concurrency */ +static const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), +}; + +void iwlagn_send_advance_bt_config(struct iwl_priv *priv) +{ + struct iwl_basic_bt_cmd basic = { + .max_kill = IWLAGN_BT_MAX_KILL_DEFAULT, + .bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT, + .bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT, + .bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT, + }; + struct iwl_bt_cmd_v1 bt_cmd_v1; + struct iwl_bt_cmd_v2 bt_cmd_v2; + int ret; + + BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != + sizeof(basic.bt3_lookup_table)); + + if (priv->lib->bt_params) { + /* + * newer generation of devices (2000 series and newer) + * use the version 2 of the bt command + * we need to make sure sending the host command + * with correct data structure to avoid uCode assert + */ + if (priv->lib->bt_params->bt_session_2) { + bt_cmd_v2.prio_boost = cpu_to_le32( + priv->lib->bt_params->bt_prio_boost); + bt_cmd_v2.tx_prio_boost = 0; + bt_cmd_v2.rx_prio_boost = 0; + } else { + /* older version only has 8 bits */ + WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF); + bt_cmd_v1.prio_boost = + priv->lib->bt_params->bt_prio_boost; + bt_cmd_v1.tx_prio_boost = 0; + bt_cmd_v1.rx_prio_boost = 0; + } + } else { + IWL_ERR(priv, "failed to construct BT Coex Config\n"); + return; + } + + /* + * Possible situations when BT needs to take over for receive, + * at the same time where STA needs to response to AP's frame(s), + * reduce the tx power of the required response frames, by that, + * allow the concurrent BT receive & WiFi transmit + * (BT - ANT A, WiFi -ANT B), without interference to one another + * + * Reduced tx power apply to control frames only (ACK/Back/CTS) + * when indicated by the BT config command + */ + basic.kill_ack_mask = priv->kill_ack_mask; + basic.kill_cts_mask = priv->kill_cts_mask; + if (priv->reduced_txpower) + basic.reduce_txpower = IWLAGN_BT_REDUCED_TX_PWR; + basic.valid = priv->bt_valid; + + /* + * Configure BT coex mode to "no coexistence" when the + * user disabled BT coexistence, we have no interface + * (might be in monitor mode), or the interface is in + * IBSS mode (no proper uCode support for coex then). + */ + if (!iwlwifi_mod_params.bt_coex_active || + priv->iw_mode == NL80211_IFTYPE_ADHOC) { + basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED; + } else { + basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W << + IWLAGN_BT_FLAG_COEX_MODE_SHIFT; + + if (!priv->bt_enable_pspoll) + basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; + else + basic.flags &= ~IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; + + if (priv->bt_ch_announce) + basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION; + IWL_DEBUG_COEX(priv, "BT coex flag: 0X%x\n", basic.flags); + } + priv->bt_enable_flag = basic.flags; + if (priv->bt_full_concurrent) + memcpy(basic.bt3_lookup_table, iwlagn_concurrent_lookup, + sizeof(iwlagn_concurrent_lookup)); + else + memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup, + sizeof(iwlagn_def_3w_lookup)); + + IWL_DEBUG_COEX(priv, "BT coex %s in %s mode\n", + basic.flags ? "active" : "disabled", + priv->bt_full_concurrent ? + "full concurrency" : "3-wire"); + + if (priv->lib->bt_params->bt_session_2) { + memcpy(&bt_cmd_v2.basic, &basic, + sizeof(basic)); + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, + 0, sizeof(bt_cmd_v2), &bt_cmd_v2); + } else { + memcpy(&bt_cmd_v1.basic, &basic, + sizeof(basic)); + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, + 0, sizeof(bt_cmd_v1), &bt_cmd_v1); + } + if (ret) + IWL_ERR(priv, "failed to send BT Coex Config\n"); + +} + +void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena) +{ + struct iwl_rxon_context *ctx, *found_ctx = NULL; + bool found_ap = false; + + lockdep_assert_held(&priv->mutex); + + /* Check whether AP or GO mode is active. */ + if (rssi_ena) { + for_each_context(priv, ctx) { + if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_AP && + iwl_is_associated_ctx(ctx)) { + found_ap = true; + break; + } + } + } + + /* + * If disable was received or If GO/AP mode, disable RSSI + * measurements. + */ + if (!rssi_ena || found_ap) { + if (priv->cur_rssi_ctx) { + ctx = priv->cur_rssi_ctx; + ieee80211_disable_rssi_reports(ctx->vif); + priv->cur_rssi_ctx = NULL; + } + return; + } + + /* + * If rssi measurements need to be enabled, consider all cases now. + * Figure out how many contexts are active. + */ + for_each_context(priv, ctx) { + if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && + iwl_is_associated_ctx(ctx)) { + found_ctx = ctx; + break; + } + } + + /* + * rssi monitor already enabled for the correct interface...nothing + * to do. + */ + if (found_ctx == priv->cur_rssi_ctx) + return; + + /* + * Figure out if rssi monitor is currently enabled, and needs + * to be changed. If rssi monitor is already enabled, disable + * it first else just enable rssi measurements on the + * interface found above. + */ + if (priv->cur_rssi_ctx) { + ctx = priv->cur_rssi_ctx; + if (ctx->vif) + ieee80211_disable_rssi_reports(ctx->vif); + } + + priv->cur_rssi_ctx = found_ctx; + + if (!found_ctx) + return; + + ieee80211_enable_rssi_reports(found_ctx->vif, + IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD, + IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD); +} + +static bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg) +{ + return (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3SCOESCO_POS; +} + +static void iwlagn_bt_traffic_change_work(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, bt_traffic_change_work); + struct iwl_rxon_context *ctx; + int smps_request = -1; + + if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { + /* bt coex disabled */ + return; + } + + /* + * Note: bt_traffic_load can be overridden by scan complete and + * coex profile notifications. Ignore that since only bad consequence + * can be not matching debug print with actual state. + */ + IWL_DEBUG_COEX(priv, "BT traffic load changes: %d\n", + priv->bt_traffic_load); + + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + if (priv->bt_status) + smps_request = IEEE80211_SMPS_DYNAMIC; + else + smps_request = IEEE80211_SMPS_AUTOMATIC; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + smps_request = IEEE80211_SMPS_DYNAMIC; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + smps_request = IEEE80211_SMPS_STATIC; + break; + default: + IWL_ERR(priv, "Invalid BT traffic load: %d\n", + priv->bt_traffic_load); + break; + } + + mutex_lock(&priv->mutex); + + /* + * We can not send command to firmware while scanning. When the scan + * complete we will schedule this work again. We do check with mutex + * locked to prevent new scan request to arrive. We do not check + * STATUS_SCANNING to avoid race when queue_work two times from + * different notifications, but quit and not perform any work at all. + */ + if (test_bit(STATUS_SCAN_HW, &priv->status)) + goto out; + + iwl_update_chain_flags(priv); + + if (smps_request != -1) { + priv->current_ht_config.smps = smps_request; + for_each_context(priv, ctx) { + if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION) + ieee80211_request_smps(ctx->vif, smps_request); + } + } + + /* + * Dynamic PS poll related functionality. Adjust RSSI measurements if + * necessary. + */ + iwlagn_bt_coex_rssi_monitor(priv); +out: + mutex_unlock(&priv->mutex); +} + +/* + * If BT sco traffic, and RSSI monitor is enabled, move measurements to the + * correct interface or disable it if this is the last interface to be + * removed. + */ +void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv) +{ + if (priv->bt_is_sco && + priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS) + iwlagn_bt_adjust_rssi_monitor(priv, true); + else + iwlagn_bt_adjust_rssi_monitor(priv, false); +} + +static void iwlagn_print_uartmsg(struct iwl_priv *priv, + struct iwl_bt_uart_msg *uart_msg) +{ + IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, " + "Update Req = 0x%X\n", + (BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >> + BT_UART_MSG_FRAME1MSGTYPE_POS, + (BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >> + BT_UART_MSG_FRAME1SSN_POS, + (BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >> + BT_UART_MSG_FRAME1UPDATEREQ_POS); + + IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, " + "Chl_SeqN = 0x%X, In band = 0x%X\n", + (BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >> + BT_UART_MSG_FRAME2OPENCONNECTIONS_POS, + (BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >> + BT_UART_MSG_FRAME2TRAFFICLOAD_POS, + (BT_UART_MSG_FRAME2CHLSEQN_MSK & uart_msg->frame2) >> + BT_UART_MSG_FRAME2CHLSEQN_POS, + (BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >> + BT_UART_MSG_FRAME2INBAND_POS); + + IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, " + "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X\n", + (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3SCOESCO_POS, + (BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3SNIFF_POS, + (BT_UART_MSG_FRAME3A2DP_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3A2DP_POS, + (BT_UART_MSG_FRAME3ACL_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3ACL_POS, + (BT_UART_MSG_FRAME3MASTER_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3MASTER_POS, + (BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3OBEX_POS); + + IWL_DEBUG_COEX(priv, "Idle duration = 0x%X\n", + (BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >> + BT_UART_MSG_FRAME4IDLEDURATION_POS); + + IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, " + "eSCO Retransmissions = 0x%X\n", + (BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >> + BT_UART_MSG_FRAME5TXACTIVITY_POS, + (BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >> + BT_UART_MSG_FRAME5RXACTIVITY_POS, + (BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >> + BT_UART_MSG_FRAME5ESCORETRANSMIT_POS); + + IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X\n", + (BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >> + BT_UART_MSG_FRAME6SNIFFINTERVAL_POS, + (BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >> + BT_UART_MSG_FRAME6DISCOVERABLE_POS); + + IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = " + "0x%X, Inquiry = 0x%X, Connectable = 0x%X\n", + (BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >> + BT_UART_MSG_FRAME7SNIFFACTIVITY_POS, + (BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >> + BT_UART_MSG_FRAME7PAGE_POS, + (BT_UART_MSG_FRAME7INQUIRY_MSK & uart_msg->frame7) >> + BT_UART_MSG_FRAME7INQUIRY_POS, + (BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >> + BT_UART_MSG_FRAME7CONNECTABLE_POS); +} + +static bool iwlagn_set_kill_msk(struct iwl_priv *priv, + struct iwl_bt_uart_msg *uart_msg) +{ + bool need_update = false; + u8 kill_msk = IWL_BT_KILL_REDUCE; + static const __le32 bt_kill_ack_msg[3] = { + IWLAGN_BT_KILL_ACK_MASK_DEFAULT, + IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, + IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; + static const __le32 bt_kill_cts_msg[3] = { + IWLAGN_BT_KILL_CTS_MASK_DEFAULT, + IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, + IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; + + if (!priv->reduced_txpower) + kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) + ? IWL_BT_KILL_OVERRIDE : IWL_BT_KILL_DEFAULT; + if (priv->kill_ack_mask != bt_kill_ack_msg[kill_msk] || + priv->kill_cts_mask != bt_kill_cts_msg[kill_msk]) { + priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK; + priv->kill_ack_mask = bt_kill_ack_msg[kill_msk]; + priv->bt_valid |= IWLAGN_BT_VALID_KILL_CTS_MASK; + priv->kill_cts_mask = bt_kill_cts_msg[kill_msk]; + need_update = true; + } + return need_update; +} + +/* + * Upon RSSI changes, sends a bt config command with following changes + * 1. enable/disable "reduced control frames tx power + * 2. update the "kill)ack_mask" and "kill_cts_mask" + * + * If "reduced tx power" is enabled, uCode shall + * 1. ACK/Back/CTS rate shall reduced to 6Mbps + * 2. not use duplciate 20/40MHz mode + */ +static bool iwlagn_fill_txpower_mode(struct iwl_priv *priv, + struct iwl_bt_uart_msg *uart_msg) +{ + bool need_update = false; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + int ave_rssi; + + if (!ctx->vif || (ctx->vif->type != NL80211_IFTYPE_STATION)) { + IWL_DEBUG_INFO(priv, "BSS ctx not active or not in sta mode\n"); + return false; + } + + ave_rssi = ieee80211_ave_rssi(ctx->vif); + if (!ave_rssi) { + /* no rssi data, no changes to reduce tx power */ + IWL_DEBUG_COEX(priv, "no rssi data available\n"); + return need_update; + } + if (!priv->reduced_txpower && + !iwl_is_associated(priv, IWL_RXON_CTX_PAN) && + (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) && + (uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | + BT_UART_MSG_FRAME3OBEX_MSK)) && + !(uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | + BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK))) { + /* enabling reduced tx power */ + priv->reduced_txpower = true; + priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; + need_update = true; + } else if (priv->reduced_txpower && + (iwl_is_associated(priv, IWL_RXON_CTX_PAN) || + (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) || + (uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | + BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK)) || + !(uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | + BT_UART_MSG_FRAME3OBEX_MSK)))) { + /* disable reduced tx power */ + priv->reduced_txpower = false; + priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; + need_update = true; + } + + return need_update; +} + +static void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_bt_coex_profile_notif *coex = (void *)pkt->data; + struct iwl_bt_uart_msg *uart_msg = &coex->last_bt_uart_msg; + + if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { + /* bt coex disabled */ + return; + } + + IWL_DEBUG_COEX(priv, "BT Coex notification:\n"); + IWL_DEBUG_COEX(priv, " status: %d\n", coex->bt_status); + IWL_DEBUG_COEX(priv, " traffic load: %d\n", coex->bt_traffic_load); + IWL_DEBUG_COEX(priv, " CI compliance: %d\n", + coex->bt_ci_compliance); + iwlagn_print_uartmsg(priv, uart_msg); + + priv->last_bt_traffic_load = priv->bt_traffic_load; + priv->bt_is_sco = iwlagn_bt_traffic_is_sco(uart_msg); + + if (priv->iw_mode != NL80211_IFTYPE_ADHOC) { + if (priv->bt_status != coex->bt_status || + priv->last_bt_traffic_load != coex->bt_traffic_load) { + if (coex->bt_status) { + /* BT on */ + if (!priv->bt_ch_announce) + priv->bt_traffic_load = + IWL_BT_COEX_TRAFFIC_LOAD_HIGH; + else + priv->bt_traffic_load = + coex->bt_traffic_load; + } else { + /* BT off */ + priv->bt_traffic_load = + IWL_BT_COEX_TRAFFIC_LOAD_NONE; + } + priv->bt_status = coex->bt_status; + queue_work(priv->workqueue, + &priv->bt_traffic_change_work); + } + } + + /* schedule to send runtime bt_config */ + /* check reduce power before change ack/cts kill mask */ + if (iwlagn_fill_txpower_mode(priv, uart_msg) || + iwlagn_set_kill_msk(priv, uart_msg)) + queue_work(priv->workqueue, &priv->bt_runtime_config); + + + /* FIXME: based on notification, adjust the prio_boost */ + + priv->bt_ci_compliance = coex->bt_ci_compliance; +} + +void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv) +{ + priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] = + iwlagn_bt_coex_profile_notif; +} + +void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv) +{ + INIT_WORK(&priv->bt_traffic_change_work, + iwlagn_bt_traffic_change_work); +} + +void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv) +{ + cancel_work_sync(&priv->bt_traffic_change_work); +} + +static bool is_single_rx_stream(struct iwl_priv *priv) +{ + return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC || + priv->current_ht_config.single_chain_sufficient; +} + +#define IWL_NUM_RX_CHAINS_MULTIPLE 3 +#define IWL_NUM_RX_CHAINS_SINGLE 2 +#define IWL_NUM_IDLE_CHAINS_DUAL 2 +#define IWL_NUM_IDLE_CHAINS_SINGLE 1 + +/* + * Determine how many receiver/antenna chains to use. + * + * More provides better reception via diversity. Fewer saves power + * at the expense of throughput, but only when not in powersave to + * start with. + * + * MIMO (dual stream) requires at least 2, but works better with 3. + * This does not determine *which* chains to use, just how many. + */ +static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) +{ + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && + (priv->bt_full_concurrent || + priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { + /* + * only use chain 'A' in bt high traffic load or + * full concurrency mode + */ + return IWL_NUM_RX_CHAINS_SINGLE; + } + /* # of Rx chains to use when expecting MIMO. */ + if (is_single_rx_stream(priv)) + return IWL_NUM_RX_CHAINS_SINGLE; + else + return IWL_NUM_RX_CHAINS_MULTIPLE; +} + +/* + * When we are in power saving mode, unless device support spatial + * multiplexing power save, use the active count for rx chain count. + */ +static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) +{ + /* # Rx chains when idling, depending on SMPS mode */ + switch (priv->current_ht_config.smps) { + case IEEE80211_SMPS_STATIC: + case IEEE80211_SMPS_DYNAMIC: + return IWL_NUM_IDLE_CHAINS_SINGLE; + case IEEE80211_SMPS_AUTOMATIC: + case IEEE80211_SMPS_OFF: + return active_cnt; + default: + WARN(1, "invalid SMPS mode %d", + priv->current_ht_config.smps); + return active_cnt; + } +} + +/* up to 4 chains */ +static u8 iwl_count_chain_bitmap(u32 chain_bitmap) +{ + u8 res; + res = (chain_bitmap & BIT(0)) >> 0; + res += (chain_bitmap & BIT(1)) >> 1; + res += (chain_bitmap & BIT(2)) >> 2; + res += (chain_bitmap & BIT(3)) >> 3; + return res; +} + +/** + * iwlagn_set_rxon_chain - Set up Rx chain usage in "staging" RXON image + * + * Selects how many and which Rx receivers/antennas/chains to use. + * This should not be used for scan command ... it puts data in wrong place. + */ +void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + bool is_single = is_single_rx_stream(priv); + bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); + u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; + u32 active_chains; + u16 rx_chain; + + /* Tell uCode which antennas are actually connected. + * Before first association, we assume all antennas are connected. + * Just after first association, iwl_chain_noise_calibration() + * checks which antennas actually *are* connected. */ + if (priv->chain_noise_data.active_chains) + active_chains = priv->chain_noise_data.active_chains; + else + active_chains = priv->nvm_data->valid_rx_ant; + + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && + (priv->bt_full_concurrent || + priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { + /* + * only use chain 'A' in bt high traffic load or + * full concurrency mode + */ + active_chains = first_antenna(active_chains); + } + + rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; + + /* How many receivers should we use? */ + active_rx_cnt = iwl_get_active_rx_chain_count(priv); + idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt); + + + /* correct rx chain count according hw settings + * and chain noise calibration + */ + valid_rx_cnt = iwl_count_chain_bitmap(active_chains); + if (valid_rx_cnt < active_rx_cnt) + active_rx_cnt = valid_rx_cnt; + + if (valid_rx_cnt < idle_rx_cnt) + idle_rx_cnt = valid_rx_cnt; + + rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; + rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; + + ctx->staging.rx_chain = cpu_to_le16(rx_chain); + + if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam) + ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; + else + ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; + + IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n", + ctx->staging.rx_chain, + active_rx_cnt, idle_rx_cnt); + + WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || + active_rx_cnt < idle_rx_cnt); +} + +u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid) +{ + int i; + u8 ind = ant; + + if (priv->band == IEEE80211_BAND_2GHZ && + priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) + return 0; + + for (i = 0; i < RATE_ANT_NUM - 1; i++) { + ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; + if (valid & BIT(ind)) + return ind; + } + return ant; +} + +#ifdef CONFIG_PM_SLEEP +static void iwlagn_convert_p1k(u16 *p1k, __le16 *out) +{ + int i; + + for (i = 0; i < IWLAGN_P1K_SIZE; i++) + out[i] = cpu_to_le16(p1k[i]); +} + +struct wowlan_key_data { + struct iwl_rxon_context *ctx; + struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc; + struct iwlagn_wowlan_tkip_params_cmd *tkip; + const u8 *bssid; + bool error, use_rsc_tsc, use_tkip; +}; + + +static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct wowlan_key_data *data = _data; + struct iwl_rxon_context *ctx = data->ctx; + struct aes_sc *aes_sc, *aes_tx_sc = NULL; + struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; + struct iwlagn_p1k_cache *rx_p1ks; + u8 *rx_mic_key; + struct ieee80211_key_seq seq; + u32 cur_rx_iv32 = 0; + u16 p1k[IWLAGN_P1K_SIZE]; + int ret, i; + + mutex_lock(&priv->mutex); + + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && + !sta && !ctx->key_mapping_keys) + ret = iwl_set_default_wep_key(priv, ctx, key); + else + ret = iwl_set_dynamic_key(priv, ctx, key, sta); + + if (ret) { + IWL_ERR(priv, "Error setting key during suspend!\n"); + data->error = true; + } + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + if (sta) { + tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; + tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; + + rx_p1ks = data->tkip->rx_uni; + + ieee80211_get_key_tx_seq(key, &seq); + tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); + tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); + + ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); + iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); + + memcpy(data->tkip->mic_keys.tx, + &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], + IWLAGN_MIC_KEY_SIZE); + + rx_mic_key = data->tkip->mic_keys.rx_unicast; + } else { + tkip_sc = + data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; + rx_p1ks = data->tkip->rx_multi; + rx_mic_key = data->tkip->mic_keys.rx_mcast; + } + + /* + * For non-QoS this relies on the fact that both the uCode and + * mac80211 use TID 0 (as they need to to avoid replay attacks) + * for checking the IV in the frames. + */ + for (i = 0; i < IWLAGN_NUM_RSC; i++) { + ieee80211_get_key_rx_seq(key, i, &seq); + tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); + tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); + /* wrapping isn't allowed, AP must rekey */ + if (seq.tkip.iv32 > cur_rx_iv32) + cur_rx_iv32 = seq.tkip.iv32; + } + + ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k); + iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k); + ieee80211_get_tkip_rx_p1k(key, data->bssid, + cur_rx_iv32 + 1, p1k); + iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k); + + memcpy(rx_mic_key, + &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], + IWLAGN_MIC_KEY_SIZE); + + data->use_tkip = true; + data->use_rsc_tsc = true; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (sta) { + u8 *pn = seq.ccmp.pn; + + aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; + aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; + + ieee80211_get_key_tx_seq(key, &seq); + aes_tx_sc->pn = cpu_to_le64( + (u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } else + aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; + + /* + * For non-QoS this relies on the fact that both the uCode and + * mac80211 use TID 0 for checking the IV in the frames. + */ + for (i = 0; i < IWLAGN_NUM_RSC; i++) { + u8 *pn = seq.ccmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); + aes_sc[i].pn = cpu_to_le64( + (u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } + data->use_rsc_tsc = true; + break; + } + + mutex_unlock(&priv->mutex); +} + +int iwlagn_send_patterns(struct iwl_priv *priv, + struct cfg80211_wowlan *wowlan) +{ + struct iwlagn_wowlan_patterns_cmd *pattern_cmd; + struct iwl_host_cmd cmd = { + .id = REPLY_WOWLAN_PATTERNS, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + int i, err; + + if (!wowlan->n_patterns) + return 0; + + cmd.len[0] = sizeof(*pattern_cmd) + + wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern); + + pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); + if (!pattern_cmd) + return -ENOMEM; + + pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); + + for (i = 0; i < wowlan->n_patterns; i++) { + int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); + + memcpy(&pattern_cmd->patterns[i].mask, + wowlan->patterns[i].mask, mask_len); + memcpy(&pattern_cmd->patterns[i].pattern, + wowlan->patterns[i].pattern, + wowlan->patterns[i].pattern_len); + pattern_cmd->patterns[i].mask_size = mask_len; + pattern_cmd->patterns[i].pattern_size = + wowlan->patterns[i].pattern_len; + } + + cmd.data[0] = pattern_cmd; + err = iwl_dvm_send_cmd(priv, &cmd); + kfree(pattern_cmd); + return err; +} + +int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) +{ + struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd; + struct iwl_rxon_cmd rxon; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd; + struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {}; + struct iwlagn_d3_config_cmd d3_cfg_cmd = { + /* + * Program the minimum sleep time to 10 seconds, as many + * platforms have issues processing a wakeup signal while + * still being in the process of suspending. + */ + .min_sleep_time = cpu_to_le32(10 * 1000 * 1000), + }; + struct wowlan_key_data key_data = { + .ctx = ctx, + .bssid = ctx->active.bssid_addr, + .use_rsc_tsc = false, + .tkip = &tkip_cmd, + .use_tkip = false, + }; + int ret, i; + u16 seq; + + key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); + if (!key_data.rsc_tsc) + return -ENOMEM; + + memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd)); + + /* + * We know the last used seqno, and the uCode expects to know that + * one, it will increment before TX. + */ + seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ; + wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq); + + /* + * For QoS counters, we store the one to use next, so subtract 0x10 + * since the uCode will add 0x10 before using the value. + */ + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + seq = priv->tid_data[IWL_AP_ID][i].seq_number; + seq -= 0x10; + wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq); + } + + if (wowlan->disconnect) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | + IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE); + if (wowlan->magic_pkt) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET); + if (wowlan->gtk_rekey_failure) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL); + if (wowlan->eap_identity_req) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ); + if (wowlan->four_way_handshake) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); + if (wowlan->n_patterns) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); + + if (wowlan->rfkill_release) + d3_cfg_cmd.wakeup_flags |= + cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL); + + iwl_scan_cancel_timeout(priv, 200); + + memcpy(&rxon, &ctx->active, sizeof(rxon)); + + priv->ucode_loaded = false; + iwl_trans_stop_device(priv->trans); + + priv->wowlan = true; + + ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); + if (ret) + goto out; + + /* now configure WoWLAN ucode */ + ret = iwl_alive_start(priv); + if (ret) + goto out; + + memcpy(&ctx->staging, &rxon, sizeof(rxon)); + ret = iwlagn_commit_rxon(priv, ctx); + if (ret) + goto out; + + ret = iwl_power_update_mode(priv, true); + if (ret) + goto out; + + if (!iwlwifi_mod_params.sw_crypto) { + /* mark all keys clear */ + priv->ucode_key_table = 0; + ctx->key_mapping_keys = 0; + + /* + * This needs to be unlocked due to lock ordering + * constraints. Since we're in the suspend path + * that isn't really a problem though. + */ + mutex_unlock(&priv->mutex); + ieee80211_iter_keys(priv->hw, ctx->vif, + iwlagn_wowlan_program_keys, + &key_data); + mutex_lock(&priv->mutex); + if (key_data.error) { + ret = -EIO; + goto out; + } + + if (key_data.use_rsc_tsc) { + struct iwl_host_cmd rsc_tsc_cmd = { + .id = REPLY_WOWLAN_TSC_RSC_PARAMS, + .data[0] = key_data.rsc_tsc, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + .len[0] = sizeof(*key_data.rsc_tsc), + }; + + ret = iwl_dvm_send_cmd(priv, &rsc_tsc_cmd); + if (ret) + goto out; + } + + if (key_data.use_tkip) { + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_WOWLAN_TKIP_PARAMS, + 0, sizeof(tkip_cmd), + &tkip_cmd); + if (ret) + goto out; + } + + if (priv->have_rekey_data) { + memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); + memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN); + kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); + memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN); + kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); + kek_kck_cmd.replay_ctr = priv->replay_ctr; + + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_WOWLAN_KEK_KCK_MATERIAL, + 0, sizeof(kek_kck_cmd), + &kek_kck_cmd); + if (ret) + goto out; + } + } + + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, 0, + sizeof(d3_cfg_cmd), &d3_cfg_cmd); + if (ret) + goto out; + + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER, + 0, sizeof(wakeup_filter_cmd), + &wakeup_filter_cmd); + if (ret) + goto out; + + ret = iwlagn_send_patterns(priv, wowlan); + out: + kfree(key_data.rsc_tsc); + return ret; +} +#endif + +int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) +{ + if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) { + IWL_WARN(priv, "Not sending command - %s KILL\n", + iwl_is_rfkill(priv) ? "RF" : "CT"); + return -EIO; + } + + if (test_bit(STATUS_FW_ERROR, &priv->status)) { + IWL_ERR(priv, "Command %s failed: FW Error\n", + iwl_dvm_get_cmd_string(cmd->id)); + return -EIO; + } + + /* + * This can happen upon FW ASSERT: we clear the STATUS_FW_ERROR flag + * in iwl_down but cancel the workers only later. + */ + if (!priv->ucode_loaded) { + IWL_ERR(priv, "Fw not loaded - dropping CMD: %x\n", cmd->id); + return -EIO; + } + + /* + * Synchronous commands from this op-mode must hold + * the mutex, this ensures we don't try to send two + * (or more) synchronous commands at a time. + */ + if (!(cmd->flags & CMD_ASYNC)) + lockdep_assert_held(&priv->mutex); + + return iwl_trans_send_cmd(priv->trans, cmd); +} + +int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, + u32 flags, u16 len, const void *data) +{ + struct iwl_host_cmd cmd = { + .id = id, + .len = { len, }, + .data = { data, }, + .flags = flags, + }; + + return iwl_dvm_send_cmd(priv, &cmd); +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c new file mode 100644 index 000000000000..b3ad34e8bf5a --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -0,0 +1,1655 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "iwl-io.h" +#include "iwl-trans.h" +#include "iwl-op-mode.h" +#include "iwl-modparams.h" + +#include "dev.h" +#include "calib.h" +#include "agn.h" + +/***************************************************************************** + * + * mac80211 entry point functions + * + *****************************************************************************/ + +static const struct ieee80211_iface_limit iwlagn_sta_ap_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, +}; + +static const struct ieee80211_iface_combination +iwlagn_iface_combinations_dualmode[] = { + { .num_different_channels = 1, + .max_interfaces = 2, + .beacon_int_infra_match = true, + .limits = iwlagn_sta_ap_limits, + .n_limits = ARRAY_SIZE(iwlagn_sta_ap_limits), + }, + { .num_different_channels = 1, + .max_interfaces = 2, + .limits = iwlagn_2sta_limits, + .n_limits = ARRAY_SIZE(iwlagn_2sta_limits), + }, +}; + +/* + * Not a mac80211 entry point function, but it fits in with all the + * other mac80211 functions grouped here. + */ +int iwlagn_mac_setup_register(struct iwl_priv *priv, + const struct iwl_ucode_capabilities *capa) +{ + int ret; + struct ieee80211_hw *hw = priv->hw; + struct iwl_rxon_context *ctx; + + hw->rate_control_algorithm = "iwl-agn-rs"; + + /* Tell mac80211 our characteristics */ + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); + ieee80211_hw_set(hw, SPECTRUM_MGMT); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(hw, QUEUE_CONTROL); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); + + hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE; + hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT; + + /* + * Including the following line will crash some AP's. This + * workaround removes the stimulus which causes the crash until + * the AP software can be fixed. + hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF; + */ + + if (priv->nvm_data->sku_cap_11n_enable) + hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS | + NL80211_FEATURE_STATIC_SMPS; + + /* + * Enable 11w if advertised by firmware and software crypto + * is not enabled (as the firmware will interpret some mgmt + * packets, so enabling it with software crypto isn't safe) + */ + if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP && + !iwlwifi_mod_params.sw_crypto) + ieee80211_hw_set(hw, MFP_CAPABLE); + + hw->sta_data_size = sizeof(struct iwl_station_priv); + hw->vif_data_size = sizeof(struct iwl_vif_priv); + + for_each_context(priv, ctx) { + hw->wiphy->interface_modes |= ctx->interface_modes; + hw->wiphy->interface_modes |= ctx->exclusive_interface_modes; + } + + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); + + if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { + hw->wiphy->iface_combinations = + iwlagn_iface_combinations_dualmode; + hw->wiphy->n_iface_combinations = + ARRAY_SIZE(iwlagn_iface_combinations_dualmode); + } + + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; + +#ifdef CONFIG_PM_SLEEP + if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len && + priv->trans->ops->d3_suspend && + priv->trans->ops->d3_resume && + device_can_wakeup(priv->trans->dev)) { + priv->wowlan_support.flags = WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_RFKILL_RELEASE; + if (!iwlwifi_mod_params.sw_crypto) + priv->wowlan_support.flags |= + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE; + + priv->wowlan_support.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS; + priv->wowlan_support.pattern_min_len = + IWLAGN_WOWLAN_MIN_PATTERN_LEN; + priv->wowlan_support.pattern_max_len = + IWLAGN_WOWLAN_MAX_PATTERN_LEN; + hw->wiphy->wowlan = &priv->wowlan_support; + } +#endif + + if (iwlwifi_mod_params.power_save) + hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; + else + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; + /* we create the 802.11 header and a max-length SSID element */ + hw->wiphy->max_scan_ie_len = capa->max_probe_length - 24 - 34; + + /* + * We don't use all queues: 4 and 9 are unused and any + * aggregation queue gets mapped down to the AC queue. + */ + hw->queues = IWLAGN_FIRST_AMPDU_QUEUE; + + hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; + + if (priv->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels) + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &priv->nvm_data->bands[IEEE80211_BAND_2GHZ]; + if (priv->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels) + priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &priv->nvm_data->bands[IEEE80211_BAND_5GHZ]; + + hw->wiphy->hw_version = priv->trans->hw_id; + + iwl_leds_init(priv); + + ret = ieee80211_register_hw(priv->hw); + if (ret) { + IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); + iwl_leds_exit(priv); + return ret; + } + priv->mac80211_registered = 1; + + return 0; +} + +void iwlagn_mac_unregister(struct iwl_priv *priv) +{ + if (!priv->mac80211_registered) + return; + iwl_leds_exit(priv); + ieee80211_unregister_hw(priv->hw); + priv->mac80211_registered = 0; +} + +static int __iwl_up(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + int ret; + + lockdep_assert_held(&priv->mutex); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { + IWL_WARN(priv, "Exit pending; will not bring the NIC up\n"); + return -EIO; + } + + for_each_context(priv, ctx) { + ret = iwlagn_alloc_bcast_station(priv, ctx); + if (ret) { + iwl_dealloc_bcast_stations(priv); + return ret; + } + } + + ret = iwl_trans_start_hw(priv->trans); + if (ret) { + IWL_ERR(priv, "Failed to start HW: %d\n", ret); + goto error; + } + + ret = iwl_run_init_ucode(priv); + if (ret) { + IWL_ERR(priv, "Failed to run INIT ucode: %d\n", ret); + goto error; + } + + ret = iwl_trans_start_hw(priv->trans); + if (ret) { + IWL_ERR(priv, "Failed to start HW: %d\n", ret); + goto error; + } + + ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR); + if (ret) { + IWL_ERR(priv, "Failed to start RT ucode: %d\n", ret); + goto error; + } + + ret = iwl_alive_start(priv); + if (ret) + goto error; + return 0; + + error: + set_bit(STATUS_EXIT_PENDING, &priv->status); + iwl_down(priv); + clear_bit(STATUS_EXIT_PENDING, &priv->status); + + IWL_ERR(priv, "Unable to initialize device.\n"); + return ret; +} + +static int iwlagn_mac_start(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + int ret; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + /* we should be verifying the device is ready to be opened */ + mutex_lock(&priv->mutex); + ret = __iwl_up(priv); + mutex_unlock(&priv->mutex); + if (ret) + return ret; + + IWL_DEBUG_INFO(priv, "Start UP work done.\n"); + + /* Now we should be done, and the READY bit should be set. */ + if (WARN_ON(!test_bit(STATUS_READY, &priv->status))) + ret = -EIO; + + iwlagn_led_enable(priv); + + priv->is_open = 1; + IWL_DEBUG_MAC80211(priv, "leave\n"); + return 0; +} + +static void iwlagn_mac_stop(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (!priv->is_open) + return; + + priv->is_open = 0; + + mutex_lock(&priv->mutex); + iwl_down(priv); + mutex_unlock(&priv->mutex); + + iwl_cancel_deferred_work(priv); + + flush_workqueue(priv->workqueue); + + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + if (iwlwifi_mod_params.sw_crypto) + return; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->mutex); + + if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif) + goto out; + + memcpy(priv->kek, data->kek, NL80211_KEK_LEN); + memcpy(priv->kck, data->kck, NL80211_KCK_LEN); + priv->replay_ctr = + cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr)); + priv->have_rekey_data = true; + + out: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +#ifdef CONFIG_PM_SLEEP + +static int iwlagn_mac_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + int ret; + + if (WARN_ON(!wowlan)) + return -EINVAL; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->mutex); + + /* Don't attempt WoWLAN when not associated, tear down instead. */ + if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION || + !iwl_is_associated_ctx(ctx)) { + ret = 1; + goto out; + } + + ret = iwlagn_suspend(priv, wowlan); + if (ret) + goto error; + + /* let the ucode operate on its own */ + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + + iwl_trans_d3_suspend(priv->trans, false); + + goto out; + + error: + priv->wowlan = false; + iwlagn_prepare_restart(priv); + ieee80211_restart_hw(priv->hw); + out: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +struct iwl_resume_data { + struct iwl_priv *priv; + struct iwlagn_wowlan_status *cmd; + bool valid; +}; + +static bool iwl_resume_status_fn(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_resume_data *resume_data = data; + struct iwl_priv *priv = resume_data->priv; + + if (iwl_rx_packet_payload_len(pkt) != sizeof(*resume_data->cmd)) { + IWL_ERR(priv, "rx wrong size data\n"); + return true; + } + memcpy(resume_data->cmd, pkt->data, sizeof(*resume_data->cmd)); + resume_data->valid = true; + + return true; +} + +static int iwlagn_mac_resume(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct ieee80211_vif *vif; + u32 base; + int ret; + enum iwl_d3_status d3_status; + struct error_table_start { + /* cf. struct iwl_error_event_table */ + u32 valid; + u32 error_id; + } err_info; + struct iwl_notification_wait status_wait; + static const u16 status_cmd[] = { + REPLY_WOWLAN_GET_STATUS, + }; + struct iwlagn_wowlan_status status_data = {}; + struct iwl_resume_data resume_data = { + .priv = priv, + .cmd = &status_data, + .valid = false, + }; + struct cfg80211_wowlan_wakeup wakeup = { + .pattern_idx = -1, + }; +#ifdef CONFIG_IWLWIFI_DEBUGFS + const struct fw_img *img; +#endif + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->mutex); + + /* we'll clear ctx->vif during iwlagn_prepare_restart() */ + vif = ctx->vif; + + ret = iwl_trans_d3_resume(priv->trans, &d3_status, false); + if (ret) + goto out_unlock; + + if (d3_status != IWL_D3_STATUS_ALIVE) { + IWL_INFO(priv, "Device was reset during suspend\n"); + goto out_unlock; + } + + /* uCode is no longer operating by itself */ + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + + base = priv->device_pointers.error_event_table; + if (!iwlagn_hw_valid_rtc_data_addr(base)) { + IWL_WARN(priv, "Invalid error table during resume!\n"); + goto out_unlock; + } + + iwl_trans_read_mem_bytes(priv->trans, base, + &err_info, sizeof(err_info)); + + if (err_info.valid) { + IWL_INFO(priv, "error table is valid (%d, 0x%x)\n", + err_info.valid, err_info.error_id); + if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) { + wakeup.rfkill_release = true; + ieee80211_report_wowlan_wakeup(vif, &wakeup, + GFP_KERNEL); + } + goto out_unlock; + } + +#ifdef CONFIG_IWLWIFI_DEBUGFS + img = &priv->fw->img[IWL_UCODE_WOWLAN]; + if (!priv->wowlan_sram) + priv->wowlan_sram = + kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len, + GFP_KERNEL); + + if (priv->wowlan_sram) + iwl_trans_read_mem(priv->trans, 0x800000, + priv->wowlan_sram, + img->sec[IWL_UCODE_SECTION_DATA].len / 4); +#endif + + /* + * This is very strange. The GET_STATUS command is sent but the device + * doesn't reply properly, it seems it doesn't close the RBD so one is + * always left open ... As a result, we need to send another command + * and have to reset the driver afterwards. As we need to switch to + * runtime firmware again that'll happen. + */ + + iwl_init_notification_wait(&priv->notif_wait, &status_wait, status_cmd, + ARRAY_SIZE(status_cmd), iwl_resume_status_fn, + &resume_data); + + iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_GET_STATUS, CMD_ASYNC, 0, NULL); + iwl_dvm_send_cmd_pdu(priv, REPLY_ECHO, CMD_ASYNC, 0, NULL); + /* an RBD is left open in the firmware now! */ + + ret = iwl_wait_notification(&priv->notif_wait, &status_wait, HZ/5); + if (ret) + goto out_unlock; + + if (resume_data.valid && priv->contexts[IWL_RXON_CTX_BSS].vif) { + u32 reasons = le32_to_cpu(status_data.wakeup_reason); + struct cfg80211_wowlan_wakeup *wakeup_report; + + IWL_INFO(priv, "WoWLAN wakeup reason(s): 0x%.8x\n", reasons); + + if (reasons) { + if (reasons & IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET) + wakeup.magic_pkt = true; + if (reasons & IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH) + wakeup.pattern_idx = status_data.pattern_number; + if (reasons & (IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | + IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE)) + wakeup.disconnect = true; + if (reasons & IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL) + wakeup.gtk_rekey_failure = true; + if (reasons & IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ) + wakeup.eap_identity_req = true; + if (reasons & IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE) + wakeup.four_way_handshake = true; + wakeup_report = &wakeup; + } else { + wakeup_report = NULL; + } + + ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); + } + + priv->wowlan = false; + + iwlagn_prepare_restart(priv); + + memset((void *)&ctx->active, 0, sizeof(ctx->active)); + iwl_connection_init_rx_config(priv, ctx); + iwlagn_set_rxon_chain(priv, ctx); + + out_unlock: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + ieee80211_resume_disconnect(vif); + + return 1; +} + +static void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + device_set_wakeup_enable(priv->trans->dev, enabled); +} +#endif + +static void iwlagn_mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + if (iwlagn_tx_skb(priv, control->sta, skb)) + ieee80211_free_txskb(hw, skb); +} + +static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, + u32 iv32, u16 *phase1key) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key); +} + +static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *ctx = vif_priv->ctx; + int ret; + bool is_default_wep_key = false; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (iwlwifi_mod_params.sw_crypto) { + IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n"); + return -EOPNOTSUPP; + } + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + /* fall through */ + case WLAN_CIPHER_SUITE_CCMP: + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + break; + default: + break; + } + + /* + * We could program these keys into the hardware as well, but we + * don't expect much multicast traffic in IBSS and having keys + * for more stations is probably more useful. + * + * Mark key TX-only and return 0. + */ + if (vif->type == NL80211_IFTYPE_ADHOC && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + key->hw_key_idx = WEP_INVALID_OFFSET; + return 0; + } + + /* If they key was TX-only, accept deletion */ + if (cmd == DISABLE_KEY && key->hw_key_idx == WEP_INVALID_OFFSET) + return 0; + + mutex_lock(&priv->mutex); + iwl_scan_cancel_timeout(priv, 100); + + BUILD_BUG_ON(WEP_INVALID_OFFSET == IWLAGN_HW_KEY_DEFAULT); + + /* + * If we are getting WEP group key and we didn't receive any key mapping + * so far, we are in legacy wep mode (group key only), otherwise we are + * in 1X mode. + * In legacy wep mode, we use another host command to the uCode. + */ + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { + if (cmd == SET_KEY) + is_default_wep_key = !ctx->key_mapping_keys; + else + is_default_wep_key = + key->hw_key_idx == IWLAGN_HW_KEY_DEFAULT; + } + + + switch (cmd) { + case SET_KEY: + if (is_default_wep_key) { + ret = iwl_set_default_wep_key(priv, vif_priv->ctx, key); + break; + } + ret = iwl_set_dynamic_key(priv, vif_priv->ctx, key, sta); + if (ret) { + /* + * can't add key for RX, but we don't need it + * in the device for TX so still return 0 + */ + ret = 0; + key->hw_key_idx = WEP_INVALID_OFFSET; + } + + IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n"); + break; + case DISABLE_KEY: + if (is_default_wep_key) + ret = iwl_remove_default_wep_key(priv, ctx, key); + else + ret = iwl_remove_dynamic_key(priv, ctx, key, sta); + + IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n"); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg) +{ + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) + return false; + return true; +} + +static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) +{ + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) + return false; + if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG) + return true; + + /* disabled by default */ + return false; +} + +static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size, bool amsdu) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + int ret = -EINVAL; + struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; + + IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", + sta->addr, tid); + + if (!(priv->nvm_data->sku_cap_11n_enable)) + return -EACCES; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->mutex); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + if (!iwl_enable_rx_ampdu(priv->cfg)) + break; + IWL_DEBUG_HT(priv, "start Rx\n"); + ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn); + break; + case IEEE80211_AMPDU_RX_STOP: + IWL_DEBUG_HT(priv, "stop Rx\n"); + ret = iwl_sta_rx_agg_stop(priv, sta, tid); + break; + case IEEE80211_AMPDU_TX_START: + if (!priv->trans->ops->txq_enable) + break; + if (!iwl_enable_tx_ampdu(priv->cfg)) + break; + IWL_DEBUG_HT(priv, "start Tx\n"); + ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + IWL_DEBUG_HT(priv, "Flush Tx\n"); + ret = iwlagn_tx_agg_flush(priv, vif, sta, tid); + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + IWL_DEBUG_HT(priv, "stop Tx\n"); + ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); + if ((ret == 0) && (priv->agg_tids_count > 0)) { + priv->agg_tids_count--; + IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", + priv->agg_tids_count); + } + if (!priv->agg_tids_count && + priv->hw_params.use_rts_for_aggregation) { + /* + * switch off RTS/CTS if it was previously enabled + */ + sta_priv->lq_sta.lq.general_params.flags &= + ~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; + iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif), + &sta_priv->lq_sta.lq, CMD_ASYNC, false); + } + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + ret = iwlagn_tx_agg_oper(priv, vif, sta, tid, buf_size); + break; + } + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + return ret; +} + +static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + bool is_ap = vif->type == NL80211_IFTYPE_STATION; + int ret; + u8 sta_id; + + IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n", + sta->addr); + sta_priv->sta_id = IWL_INVALID_STATION; + + atomic_set(&sta_priv->pending_frames, 0); + if (vif->type == NL80211_IFTYPE_AP) + sta_priv->client = true; + + ret = iwl_add_station_common(priv, vif_priv->ctx, sta->addr, + is_ap, sta, &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM (%d)\n", + sta->addr, ret); + /* Should we return success if return code is EEXIST ? */ + return ret; + } + + sta_priv->sta_id = sta_id; + + return 0; +} + +static int iwlagn_mac_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + int ret; + + IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", sta->addr); + + if (vif->type == NL80211_IFTYPE_STATION) { + /* + * Station will be removed from device when the RXON + * is set to unassociated -- just deactivate it here + * to avoid re-programming it. + */ + ret = 0; + iwl_deactivate_station(priv, sta_priv->sta_id, sta->addr); + } else { + ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr); + if (ret) + IWL_DEBUG_QUIET_RFKILL(priv, + "Error removing station %pM\n", sta->addr); + } + return ret; +} + +static int iwlagn_mac_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + enum { + NONE, ADD, REMOVE, HT_RATE_INIT, ADD_RATE_INIT, + } op = NONE; + int ret; + + IWL_DEBUG_MAC80211(priv, "station %pM state change %d->%d\n", + sta->addr, old_state, new_state); + + mutex_lock(&priv->mutex); + if (vif->type == NL80211_IFTYPE_STATION) { + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) + op = ADD; + else if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST) + op = REMOVE; + else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) + op = HT_RATE_INIT; + } else { + if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) + op = ADD_RATE_INIT; + else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH) + op = REMOVE; + } + + switch (op) { + case ADD: + ret = iwlagn_mac_sta_add(hw, vif, sta); + if (ret) + break; + /* + * Clear the in-progress flag, the AP station entry was added + * but we'll initialize LQ only when we've associated (which + * would also clear the in-progress flag). This is necessary + * in case we never initialize LQ because association fails. + */ + spin_lock_bh(&priv->sta_lock); + priv->stations[iwl_sta_id(sta)].used &= + ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_bh(&priv->sta_lock); + break; + case REMOVE: + ret = iwlagn_mac_sta_remove(hw, vif, sta); + break; + case ADD_RATE_INIT: + ret = iwlagn_mac_sta_add(hw, vif, sta); + if (ret) + break; + /* Initialize rate scaling */ + IWL_DEBUG_INFO(priv, + "Initializing rate scaling for station %pM\n", + sta->addr); + iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); + ret = 0; + break; + case HT_RATE_INIT: + /* Initialize rate scaling */ + ret = iwl_sta_update_ht(priv, vif_priv->ctx, sta); + if (ret) + break; + IWL_DEBUG_INFO(priv, + "Initializing rate scaling for station %pM\n", + sta->addr); + iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); + ret = 0; + break; + default: + ret = 0; + break; + } + + /* + * mac80211 might WARN if we fail, but due the way we + * (badly) handle hard rfkill, we might fail here + */ + if (iwl_is_rfkill(priv)) + ret = 0; + + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *ch_switch) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_channel *channel = ch_switch->chandef.chan; + struct iwl_ht_config *ht_conf = &priv->current_ht_config; + /* + * MULTI-FIXME + * When we add support for multiple interfaces, we need to + * revisit this. The channel switch command in the device + * only affects the BSS context, but what does that really + * mean? And what if we get a CSA on the second interface? + * This needs a lot of work. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + u16 ch; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + mutex_lock(&priv->mutex); + + if (iwl_is_rfkill(priv)) + goto out; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status) || + test_bit(STATUS_SCANNING, &priv->status) || + test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) + goto out; + + if (!iwl_is_associated_ctx(ctx)) + goto out; + + if (!priv->lib->set_channel_switch) + goto out; + + ch = channel->hw_value; + if (le16_to_cpu(ctx->active.channel) == ch) + goto out; + + priv->current_ht_config.smps = conf->smps_mode; + + /* Configure HT40 channels */ + switch (cfg80211_get_chandef_type(&ch_switch->chandef)) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + ctx->ht.is_40mhz = false; + ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + break; + case NL80211_CHAN_HT40MINUS: + ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + ctx->ht.is_40mhz = true; + break; + case NL80211_CHAN_HT40PLUS: + ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + ctx->ht.is_40mhz = true; + break; + } + + if ((le16_to_cpu(ctx->staging.channel) != ch)) + ctx->staging.flags = 0; + + iwl_set_rxon_channel(priv, channel, ctx); + iwl_set_rxon_ht(priv, ht_conf); + iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif); + + /* + * at this point, staging_rxon has the + * configuration for channel switch + */ + set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); + priv->switch_channel = cpu_to_le16(ch); + if (priv->lib->set_channel_switch(priv, ch_switch)) { + clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); + priv->switch_channel = 0; + ieee80211_chswitch_done(ctx->vif, false); + } + +out: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) +{ + /* + * MULTI-FIXME + * See iwlagn_mac_channel_switch. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) + return; + + if (ctx->vif) + ieee80211_chswitch_done(ctx->vif, is_success); +} + +static void iwlagn_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + __le32 filter_or = 0, filter_nand = 0; + struct iwl_rxon_context *ctx; + +#define CHK(test, flag) do { \ + if (*total_flags & (test)) \ + filter_or |= (flag); \ + else \ + filter_nand |= (flag); \ + } while (0) + + IWL_DEBUG_MAC80211(priv, "Enter: changed: 0x%x, total: 0x%x\n", + changed_flags, *total_flags); + + CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK); + /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ + CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); + CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); + +#undef CHK + + mutex_lock(&priv->mutex); + + for_each_context(priv, ctx) { + ctx->staging.filter_flags &= ~filter_nand; + ctx->staging.filter_flags |= filter_or; + + /* + * Not committing directly because hardware can perform a scan, + * but we'll eventually commit the filter flags change anyway. + */ + } + + mutex_unlock(&priv->mutex); + + /* + * Receiving all multicast frames is always enabled by the + * default flags setup in iwl_connection_init_rx_config() + * since we currently do not support programming multicast + * filters into the device. + */ + *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | + FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; +} + +static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + u32 scd_queues; + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { + IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n"); + goto done; + } + if (iwl_is_rfkill(priv)) { + IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n"); + goto done; + } + + scd_queues = BIT(priv->cfg->base_params->num_of_queues) - 1; + scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) | + BIT(IWL_DEFAULT_CMD_QUEUE_NUM)); + + if (drop) { + IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n", + scd_queues); + if (iwlagn_txfifo_flush(priv, scd_queues)) { + IWL_ERR(priv, "flush request fail\n"); + goto done; + } + } + + IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n"); + iwl_trans_wait_tx_queue_empty(priv->trans, scd_queues); +done: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +static void iwlagn_mac_event_callback(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct ieee80211_event *event) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + if (event->type != RSSI_EVENT) + return; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { + if (event->u.rssi.data == RSSI_EVENT_LOW) + priv->bt_enable_pspoll = true; + else if (event->u.rssi.data == RSSI_EVENT_HIGH) + priv->bt_enable_pspoll = false; + + queue_work(priv->workqueue, &priv->bt_runtime_config); + } else { + IWL_DEBUG_MAC80211(priv, "Advanced BT coex disabled," + "ignoring RSSI callback\n"); + } + + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +static int iwlagn_mac_set_tim(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, bool set) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + queue_work(priv->workqueue, &priv->beacon_update); + + return 0; +} + +static int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *ctx = vif_priv->ctx; + int q; + + if (WARN_ON(!ctx)) + return -EINVAL; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (!iwl_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); + return -EIO; + } + + if (queue >= AC_NUM) { + IWL_DEBUG_MAC80211(priv, "leave - queue >= AC_NUM %d\n", queue); + return 0; + } + + q = AC_NUM - 1 - queue; + + mutex_lock(&priv->mutex); + + ctx->qos_data.def_qos_parm.ac[q].cw_min = + cpu_to_le16(params->cw_min); + ctx->qos_data.def_qos_parm.ac[q].cw_max = + cpu_to_le16(params->cw_max); + ctx->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; + ctx->qos_data.def_qos_parm.ac[q].edca_txop = + cpu_to_le16((params->txop * 32)); + + ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0; + + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + return 0; +} + +static int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + return priv->ibss_manager == IWL_IBSS_MANAGER; +} + +static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + iwl_connection_init_rx_config(priv, ctx); + + iwlagn_set_rxon_chain(priv, ctx); + + return iwlagn_commit_rxon(priv, ctx); +} + +static int iwl_setup_interface(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct ieee80211_vif *vif = ctx->vif; + int err, ac; + + lockdep_assert_held(&priv->mutex); + + /* + * This variable will be correct only when there's just + * a single context, but all code using it is for hardware + * that supports only one context. + */ + priv->iw_mode = vif->type; + + ctx->is_active = true; + + err = iwl_set_mode(priv, ctx); + if (err) { + if (!ctx->always_active) + ctx->is_active = false; + return err; + } + + if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist && + vif->type == NL80211_IFTYPE_ADHOC) { + /* + * pretend to have high BT traffic as long as we + * are operating in IBSS mode, as this will cause + * the rate scaling etc. to behave as intended. + */ + priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; + } + + /* set up queue mappings */ + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + vif->hw_queue[ac] = ctx->ac_to_queue[ac]; + + if (vif->type == NL80211_IFTYPE_AP) + vif->cab_queue = ctx->mcast_queue; + else + vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; + + return 0; +} + +static int iwlagn_mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *tmp, *ctx = NULL; + int err; + enum nl80211_iftype viftype = ieee80211_vif_type_p2p(vif); + bool reset = false; + + IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", + viftype, vif->addr); + + mutex_lock(&priv->mutex); + + if (!iwl_is_ready_rf(priv)) { + IWL_WARN(priv, "Try to add interface when device not ready\n"); + err = -EINVAL; + goto out; + } + + for_each_context(priv, tmp) { + u32 possible_modes = + tmp->interface_modes | tmp->exclusive_interface_modes; + + if (tmp->vif) { + /* On reset we need to add the same interface again */ + if (tmp->vif == vif) { + reset = true; + ctx = tmp; + break; + } + + /* check if this busy context is exclusive */ + if (tmp->exclusive_interface_modes & + BIT(tmp->vif->type)) { + err = -EINVAL; + goto out; + } + continue; + } + + if (!(possible_modes & BIT(viftype))) + continue; + + /* have maybe usable context w/o interface */ + ctx = tmp; + break; + } + + if (!ctx) { + err = -EOPNOTSUPP; + goto out; + } + + vif_priv->ctx = ctx; + ctx->vif = vif; + + /* + * In SNIFFER device type, the firmware reports the FCS to + * the host, rather than snipping it off. Unfortunately, + * mac80211 doesn't (yet) provide a per-packet flag for + * this, so that we have to set the hardware flag based + * on the interfaces added. As the monitor interface can + * only be present by itself, and will be removed before + * other interfaces are added, this is safe. + */ + if (vif->type == NL80211_IFTYPE_MONITOR) + ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); + else + __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, priv->hw->flags); + + err = iwl_setup_interface(priv, ctx); + if (!err || reset) + goto out; + + ctx->vif = NULL; + priv->iw_mode = NL80211_IFTYPE_STATION; + out: + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + return err; +} + +static void iwl_teardown_interface(struct iwl_priv *priv, + struct ieee80211_vif *vif, + bool mode_change) +{ + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + + lockdep_assert_held(&priv->mutex); + + if (priv->scan_vif == vif) { + iwl_scan_cancel_timeout(priv, 200); + iwl_force_scan_end(priv); + } + + if (!mode_change) { + iwl_set_mode(priv, ctx); + if (!ctx->always_active) + ctx->is_active = false; + } + + /* + * When removing the IBSS interface, overwrite the + * BT traffic load with the stored one from the last + * notification, if any. If this is a device that + * doesn't implement this, this has no effect since + * both values are the same and zero. + */ + if (vif->type == NL80211_IFTYPE_ADHOC) + priv->bt_traffic_load = priv->last_bt_traffic_load; +} + +static void iwlagn_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + mutex_lock(&priv->mutex); + + if (WARN_ON(ctx->vif != vif)) { + struct iwl_rxon_context *tmp; + IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif); + for_each_context(priv, tmp) + IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n", + tmp->ctxid, tmp, tmp->vif); + } + ctx->vif = NULL; + + iwl_teardown_interface(priv, vif, false); + + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + +} + +static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum nl80211_iftype newtype, bool newp2p) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx, *tmp; + enum nl80211_iftype newviftype = newtype; + u32 interface_modes; + int err; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + newtype = ieee80211_iftype_p2p(newtype, newp2p); + + mutex_lock(&priv->mutex); + + ctx = iwl_rxon_ctx_from_vif(vif); + + /* + * To simplify this code, only support changes on the + * BSS context. The PAN context is usually reassigned + * by creating/removing P2P interfaces anyway. + */ + if (ctx->ctxid != IWL_RXON_CTX_BSS) { + err = -EBUSY; + goto out; + } + + if (!ctx->vif || !iwl_is_ready_rf(priv)) { + /* + * Huh? But wait ... this can maybe happen when + * we're in the middle of a firmware restart! + */ + err = -EBUSY; + goto out; + } + + /* Check if the switch is supported in the same context */ + interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes; + if (!(interface_modes & BIT(newtype))) { + err = -EBUSY; + goto out; + } + + if (ctx->exclusive_interface_modes & BIT(newtype)) { + for_each_context(priv, tmp) { + if (ctx == tmp) + continue; + + if (!tmp->is_active) + continue; + + /* + * The current mode switch would be exclusive, but + * another context is active ... refuse the switch. + */ + err = -EBUSY; + goto out; + } + } + + /* success */ + iwl_teardown_interface(priv, vif, true); + vif->type = newviftype; + vif->p2p = newp2p; + err = iwl_setup_interface(priv, ctx); + WARN_ON(err); + /* + * We've switched internally, but submitting to the + * device may have failed for some reason. Mask this + * error, because otherwise mac80211 will not switch + * (and set the interface type back) and we'll be + * out of sync with it. + */ + err = 0; + + out: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return err; +} + +static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct cfg80211_scan_request *req = &hw_req->req; + int ret; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (req->n_channels == 0) + return -EINVAL; + + mutex_lock(&priv->mutex); + + /* + * If an internal scan is in progress, just set + * up the scan_request as per above. + */ + if (priv->scan_type != IWL_SCAN_NORMAL) { + IWL_DEBUG_SCAN(priv, + "SCAN request during internal scan - defer\n"); + priv->scan_request = req; + priv->scan_vif = vif; + ret = 0; + } else { + priv->scan_request = req; + priv->scan_vif = vif; + /* + * mac80211 will only ask for one band at a time + * so using channels[0] here is ok + */ + ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL, + req->channels[0]->band); + if (ret) { + priv->scan_request = NULL; + priv->scan_vif = NULL; + } + } + + IWL_DEBUG_MAC80211(priv, "leave\n"); + + mutex_unlock(&priv->mutex); + + return ret; +} + +static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) +{ + struct iwl_addsta_cmd cmd = { + .mode = STA_CONTROL_MODIFY_MSK, + .station_flags_msk = STA_FLG_PWR_SAVE_MSK, + .sta.sta_id = sta_id, + }; + + iwl_send_add_sta(priv, &cmd, CMD_ASYNC); +} + +static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + int sta_id; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + switch (cmd) { + case STA_NOTIFY_SLEEP: + WARN_ON(!sta_priv->client); + sta_priv->asleep = true; + if (atomic_read(&sta_priv->pending_frames) > 0) + ieee80211_sta_block_awake(hw, sta, true); + break; + case STA_NOTIFY_AWAKE: + WARN_ON(!sta_priv->client); + if (!sta_priv->asleep) + break; + sta_priv->asleep = false; + sta_id = iwl_sta_id(sta); + if (sta_id != IWL_INVALID_STATION) + iwl_sta_modify_ps_wake(priv, sta_id); + break; + default: + break; + } + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +const struct ieee80211_ops iwlagn_hw_ops = { + .tx = iwlagn_mac_tx, + .start = iwlagn_mac_start, + .stop = iwlagn_mac_stop, +#ifdef CONFIG_PM_SLEEP + .suspend = iwlagn_mac_suspend, + .resume = iwlagn_mac_resume, + .set_wakeup = iwlagn_mac_set_wakeup, +#endif + .add_interface = iwlagn_mac_add_interface, + .remove_interface = iwlagn_mac_remove_interface, + .change_interface = iwlagn_mac_change_interface, + .config = iwlagn_mac_config, + .configure_filter = iwlagn_configure_filter, + .set_key = iwlagn_mac_set_key, + .update_tkip_key = iwlagn_mac_update_tkip_key, + .set_rekey_data = iwlagn_mac_set_rekey_data, + .conf_tx = iwlagn_mac_conf_tx, + .bss_info_changed = iwlagn_bss_info_changed, + .ampdu_action = iwlagn_mac_ampdu_action, + .hw_scan = iwlagn_mac_hw_scan, + .sta_notify = iwlagn_mac_sta_notify, + .sta_state = iwlagn_mac_sta_state, + .channel_switch = iwlagn_mac_channel_switch, + .flush = iwlagn_mac_flush, + .tx_last_beacon = iwlagn_mac_tx_last_beacon, + .event_callback = iwlagn_mac_event_callback, + .set_tim = iwlagn_mac_set_tim, +}; + +/* This function both allocates and initializes hw and priv. */ +struct ieee80211_hw *iwl_alloc_all(void) +{ + struct iwl_priv *priv; + struct iwl_op_mode *op_mode; + /* mac80211 allocates memory for this device instance, including + * space for this driver's private structure */ + struct ieee80211_hw *hw; + + hw = ieee80211_alloc_hw(sizeof(struct iwl_priv) + + sizeof(struct iwl_op_mode), &iwlagn_hw_ops); + if (!hw) + goto out; + + op_mode = hw->priv; + priv = IWL_OP_MODE_GET_DVM(op_mode); + priv->hw = hw; + +out: + return hw; +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c new file mode 100644 index 000000000000..e7616f0ee6e8 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -0,0 +1,2077 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "iwl-eeprom-read.h" +#include "iwl-eeprom-parse.h" +#include "iwl-io.h" +#include "iwl-trans.h" +#include "iwl-op-mode.h" +#include "iwl-drv.h" +#include "iwl-modparams.h" +#include "iwl-prph.h" + +#include "dev.h" +#include "calib.h" +#include "agn.h" + + +/****************************************************************************** + * + * module boiler plate + * + ******************************************************************************/ + +#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux" +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_LICENSE("GPL"); + +static const struct iwl_op_mode_ops iwl_dvm_ops; + +void iwl_update_chain_flags(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + + for_each_context(priv, ctx) { + iwlagn_set_rxon_chain(priv, ctx); + if (ctx->active.rx_chain != ctx->staging.rx_chain) + iwlagn_commit_rxon(priv, ctx); + } +} + +/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ +static void iwl_set_beacon_tim(struct iwl_priv *priv, + struct iwl_tx_beacon_cmd *tx_beacon_cmd, + u8 *beacon, u32 frame_size) +{ + u16 tim_idx; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; + + /* + * The index is relative to frame start but we start looking at the + * variable-length part of the beacon. + */ + tim_idx = mgmt->u.beacon.variable - beacon; + + /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ + while ((tim_idx < (frame_size - 2)) && + (beacon[tim_idx] != WLAN_EID_TIM)) + tim_idx += beacon[tim_idx+1] + 2; + + /* If TIM field was found, set variables */ + if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { + tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); + tx_beacon_cmd->tim_size = beacon[tim_idx+1]; + } else + IWL_WARN(priv, "Unable to find TIM Element in beacon\n"); +} + +int iwlagn_send_beacon_cmd(struct iwl_priv *priv) +{ + struct iwl_tx_beacon_cmd *tx_beacon_cmd; + struct iwl_host_cmd cmd = { + .id = REPLY_TX_BEACON, + }; + struct ieee80211_tx_info *info; + u32 frame_size; + u32 rate_flags; + u32 rate; + + /* + * We have to set up the TX command, the TX Beacon command, and the + * beacon contents. + */ + + lockdep_assert_held(&priv->mutex); + + if (!priv->beacon_ctx) { + IWL_ERR(priv, "trying to build beacon w/o beacon context!\n"); + return 0; + } + + if (WARN_ON(!priv->beacon_skb)) + return -EINVAL; + + /* Allocate beacon command */ + if (!priv->beacon_cmd) + priv->beacon_cmd = kzalloc(sizeof(*tx_beacon_cmd), GFP_KERNEL); + tx_beacon_cmd = priv->beacon_cmd; + if (!tx_beacon_cmd) + return -ENOMEM; + + frame_size = priv->beacon_skb->len; + + /* Set up TX command fields */ + tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); + tx_beacon_cmd->tx.sta_id = priv->beacon_ctx->bcast_sta_id; + tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | + TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK; + + /* Set up TX beacon command fields */ + iwl_set_beacon_tim(priv, tx_beacon_cmd, priv->beacon_skb->data, + frame_size); + + /* Set up packet rate and flags */ + info = IEEE80211_SKB_CB(priv->beacon_skb); + + /* + * Let's set up the rate at least somewhat correctly; + * it will currently not actually be used by the uCode, + * it uses the broadcast station's rate instead. + */ + if (info->control.rates[0].idx < 0 || + info->control.rates[0].flags & IEEE80211_TX_RC_MCS) + rate = 0; + else + rate = info->control.rates[0].idx; + + priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, + priv->nvm_data->valid_tx_ant); + rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant); + + /* In mac80211, rates for 5 GHz start at 0 */ + if (info->band == IEEE80211_BAND_5GHZ) + rate += IWL_FIRST_OFDM_RATE; + else if (rate >= IWL_FIRST_CCK_RATE && rate <= IWL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_CCK_MSK; + + tx_beacon_cmd->tx.rate_n_flags = + iwl_hw_set_rate_n_flags(rate, rate_flags); + + /* Submit command */ + cmd.len[0] = sizeof(*tx_beacon_cmd); + cmd.data[0] = tx_beacon_cmd; + cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + cmd.len[1] = frame_size; + cmd.data[1] = priv->beacon_skb->data; + cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY; + + return iwl_dvm_send_cmd(priv, &cmd); +} + +static void iwl_bg_beacon_update(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, beacon_update); + struct sk_buff *beacon; + + mutex_lock(&priv->mutex); + if (!priv->beacon_ctx) { + IWL_ERR(priv, "updating beacon w/o beacon context!\n"); + goto out; + } + + if (priv->beacon_ctx->vif->type != NL80211_IFTYPE_AP) { + /* + * The ucode will send beacon notifications even in + * IBSS mode, but we don't want to process them. But + * we need to defer the type check to here due to + * requiring locking around the beacon_ctx access. + */ + goto out; + } + + /* Pull updated AP beacon from mac80211. will fail if not in AP mode */ + beacon = ieee80211_beacon_get(priv->hw, priv->beacon_ctx->vif); + if (!beacon) { + IWL_ERR(priv, "update beacon failed -- keeping old\n"); + goto out; + } + + /* new beacon skb is allocated every time; dispose previous.*/ + dev_kfree_skb(priv->beacon_skb); + + priv->beacon_skb = beacon; + + iwlagn_send_beacon_cmd(priv); + out: + mutex_unlock(&priv->mutex); +} + +static void iwl_bg_bt_runtime_config(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, bt_runtime_config); + + mutex_lock(&priv->mutex); + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + goto out; + + /* dont send host command if rf-kill is on */ + if (!iwl_is_ready_rf(priv)) + goto out; + + iwlagn_send_advance_bt_config(priv); +out: + mutex_unlock(&priv->mutex); +} + +static void iwl_bg_bt_full_concurrency(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, bt_full_concurrency); + struct iwl_rxon_context *ctx; + + mutex_lock(&priv->mutex); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + goto out; + + /* dont send host command if rf-kill is on */ + if (!iwl_is_ready_rf(priv)) + goto out; + + IWL_DEBUG_INFO(priv, "BT coex in %s mode\n", + priv->bt_full_concurrent ? + "full concurrency" : "3-wire"); + + /* + * LQ & RXON updated cmds must be sent before BT Config cmd + * to avoid 3-wire collisions + */ + for_each_context(priv, ctx) { + iwlagn_set_rxon_chain(priv, ctx); + iwlagn_commit_rxon(priv, ctx); + } + + iwlagn_send_advance_bt_config(priv); +out: + mutex_unlock(&priv->mutex); +} + +int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) +{ + struct iwl_statistics_cmd statistics_cmd = { + .configuration_flags = + clear ? IWL_STATS_CONF_CLEAR_STATS : 0, + }; + + if (flags & CMD_ASYNC) + return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, + CMD_ASYNC, + sizeof(struct iwl_statistics_cmd), + &statistics_cmd); + else + return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, 0, + sizeof(struct iwl_statistics_cmd), + &statistics_cmd); +} + +/** + * iwl_bg_statistics_periodic - Timer callback to queue statistics + * + * This callback is provided in order to send a statistics request. + * + * This timer function is continually reset to execute within + * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION + * was received. We need to ensure we receive the statistics in order + * to update the temperature used for calibrating the TXPOWER. + */ +static void iwl_bg_statistics_periodic(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* dont send host command if rf-kill is on */ + if (!iwl_is_ready_rf(priv)) + return; + + iwl_send_statistics_request(priv, CMD_ASYNC, false); +} + + +static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base, + u32 start_idx, u32 num_events, + u32 capacity, u32 mode) +{ + u32 i; + u32 ptr; /* SRAM byte address of log data */ + u32 ev, time, data; /* event log data */ + unsigned long reg_flags; + + if (mode == 0) + ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32)); + else + ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32)); + + /* Make sure device is powered up for SRAM reads */ + if (!iwl_trans_grab_nic_access(priv->trans, false, ®_flags)) + return; + + /* Set starting address; reads will auto-increment */ + iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, ptr); + + /* + * Refuse to read more than would have fit into the log from + * the current start_idx. This used to happen due to the race + * described below, but now WARN because the code below should + * prevent it from happening here. + */ + if (WARN_ON(num_events > capacity - start_idx)) + num_events = capacity - start_idx; + + /* + * "time" is actually "data" for mode 0 (no timestamp). + * place event id # at far right for easier visual parsing. + */ + for (i = 0; i < num_events; i++) { + ev = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); + time = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); + if (mode == 0) { + trace_iwlwifi_dev_ucode_cont_event( + priv->trans->dev, 0, time, ev); + } else { + data = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); + trace_iwlwifi_dev_ucode_cont_event( + priv->trans->dev, time, data, ev); + } + } + /* Allow device to power down */ + iwl_trans_release_nic_access(priv->trans, ®_flags); +} + +static void iwl_continuous_event_trace(struct iwl_priv *priv) +{ + u32 capacity; /* event log capacity in # entries */ + struct { + u32 capacity; + u32 mode; + u32 wrap_counter; + u32 write_counter; + } __packed read; + u32 base; /* SRAM byte address of event log header */ + u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ + u32 num_wraps; /* # times uCode wrapped to top of log */ + u32 next_entry; /* index of next entry to be written by uCode */ + + base = priv->device_pointers.log_event_table; + if (iwlagn_hw_valid_rtc_data_addr(base)) { + iwl_trans_read_mem_bytes(priv->trans, base, + &read, sizeof(read)); + capacity = read.capacity; + mode = read.mode; + num_wraps = read.wrap_counter; + next_entry = read.write_counter; + } else + return; + + /* + * Unfortunately, the uCode doesn't use temporary variables. + * Therefore, it can happen that we read next_entry == capacity, + * which really means next_entry == 0. + */ + if (unlikely(next_entry == capacity)) + next_entry = 0; + /* + * Additionally, the uCode increases the write pointer before + * the wraps counter, so if the write pointer is smaller than + * the old write pointer (wrap occurred) but we read that no + * wrap occurred, we actually read between the next_entry and + * num_wraps update (this does happen in practice!!) -- take + * that into account by increasing num_wraps. + */ + if (unlikely(next_entry < priv->event_log.next_entry && + num_wraps == priv->event_log.num_wraps)) + num_wraps++; + + if (num_wraps == priv->event_log.num_wraps) { + iwl_print_cont_event_trace( + priv, base, priv->event_log.next_entry, + next_entry - priv->event_log.next_entry, + capacity, mode); + + priv->event_log.non_wraps_count++; + } else { + if (num_wraps - priv->event_log.num_wraps > 1) + priv->event_log.wraps_more_count++; + else + priv->event_log.wraps_once_count++; + + trace_iwlwifi_dev_ucode_wrap_event(priv->trans->dev, + num_wraps - priv->event_log.num_wraps, + next_entry, priv->event_log.next_entry); + + if (next_entry < priv->event_log.next_entry) { + iwl_print_cont_event_trace( + priv, base, priv->event_log.next_entry, + capacity - priv->event_log.next_entry, + capacity, mode); + + iwl_print_cont_event_trace( + priv, base, 0, next_entry, capacity, mode); + } else { + iwl_print_cont_event_trace( + priv, base, next_entry, + capacity - next_entry, + capacity, mode); + + iwl_print_cont_event_trace( + priv, base, 0, next_entry, capacity, mode); + } + } + + priv->event_log.num_wraps = num_wraps; + priv->event_log.next_entry = next_entry; +} + +/** + * iwl_bg_ucode_trace - Timer callback to log ucode event + * + * The timer is continually set to execute every + * UCODE_TRACE_PERIOD milliseconds after the last timer expired + * this function is to perform continuous uCode event logging operation + * if enabled + */ +static void iwl_bg_ucode_trace(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (priv->event_log.ucode_trace) { + iwl_continuous_event_trace(priv); + /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */ + mod_timer(&priv->ucode_trace, + jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD)); + } +} + +static void iwl_bg_tx_flush(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, tx_flush); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* do nothing if rf-kill is on */ + if (!iwl_is_ready_rf(priv)) + return; + + IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n"); + iwlagn_dev_txfifo_flush(priv); +} + +/* + * queue/FIFO/AC mapping definitions + */ + +static const u8 iwlagn_bss_ac_to_fifo[] = { + IWL_TX_FIFO_VO, + IWL_TX_FIFO_VI, + IWL_TX_FIFO_BE, + IWL_TX_FIFO_BK, +}; + +static const u8 iwlagn_bss_ac_to_queue[] = { + 0, 1, 2, 3, +}; + +static const u8 iwlagn_pan_ac_to_fifo[] = { + IWL_TX_FIFO_VO_IPAN, + IWL_TX_FIFO_VI_IPAN, + IWL_TX_FIFO_BE_IPAN, + IWL_TX_FIFO_BK_IPAN, +}; + +static const u8 iwlagn_pan_ac_to_queue[] = { + 7, 6, 5, 4, +}; + +static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags) +{ + int i; + + /* + * The default context is always valid, + * the PAN context depends on uCode. + */ + priv->valid_contexts = BIT(IWL_RXON_CTX_BSS); + if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) + priv->valid_contexts |= BIT(IWL_RXON_CTX_PAN); + + for (i = 0; i < NUM_IWL_RXON_CTX; i++) + priv->contexts[i].ctxid = i; + + priv->contexts[IWL_RXON_CTX_BSS].always_active = true; + priv->contexts[IWL_RXON_CTX_BSS].is_active = true; + priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON; + priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING; + priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC; + priv->contexts[IWL_RXON_CTX_BSS].qos_cmd = REPLY_QOS_PARAM; + priv->contexts[IWL_RXON_CTX_BSS].ap_sta_id = IWL_AP_ID; + priv->contexts[IWL_RXON_CTX_BSS].wep_key_cmd = REPLY_WEPKEY; + priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID; + priv->contexts[IWL_RXON_CTX_BSS].exclusive_interface_modes = + BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MONITOR); + priv->contexts[IWL_RXON_CTX_BSS].interface_modes = + BIT(NL80211_IFTYPE_STATION); + priv->contexts[IWL_RXON_CTX_BSS].ap_devtype = RXON_DEV_TYPE_AP; + priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS; + priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS; + priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS; + memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_queue, + iwlagn_bss_ac_to_queue, sizeof(iwlagn_bss_ac_to_queue)); + memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_fifo, + iwlagn_bss_ac_to_fifo, sizeof(iwlagn_bss_ac_to_fifo)); + + priv->contexts[IWL_RXON_CTX_PAN].rxon_cmd = REPLY_WIPAN_RXON; + priv->contexts[IWL_RXON_CTX_PAN].rxon_timing_cmd = + REPLY_WIPAN_RXON_TIMING; + priv->contexts[IWL_RXON_CTX_PAN].rxon_assoc_cmd = + REPLY_WIPAN_RXON_ASSOC; + priv->contexts[IWL_RXON_CTX_PAN].qos_cmd = REPLY_WIPAN_QOS_PARAM; + priv->contexts[IWL_RXON_CTX_PAN].ap_sta_id = IWL_AP_ID_PAN; + priv->contexts[IWL_RXON_CTX_PAN].wep_key_cmd = REPLY_WIPAN_WEPKEY; + priv->contexts[IWL_RXON_CTX_PAN].bcast_sta_id = IWLAGN_PAN_BCAST_ID; + priv->contexts[IWL_RXON_CTX_PAN].station_flags = STA_FLG_PAN_STATION; + priv->contexts[IWL_RXON_CTX_PAN].interface_modes = + BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); + + priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP; + priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA; + priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P; + memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_queue, + iwlagn_pan_ac_to_queue, sizeof(iwlagn_pan_ac_to_queue)); + memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_fifo, + iwlagn_pan_ac_to_fifo, sizeof(iwlagn_pan_ac_to_fifo)); + priv->contexts[IWL_RXON_CTX_PAN].mcast_queue = IWL_IPAN_MCAST_QUEUE; + + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); +} + +static void iwl_rf_kill_ct_config(struct iwl_priv *priv) +{ + struct iwl_ct_kill_config cmd; + struct iwl_ct_kill_throttling_config adv_cmd; + int ret = 0; + + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + + priv->thermal_throttle.ct_kill_toggle = false; + + if (priv->lib->support_ct_kill_exit) { + adv_cmd.critical_temperature_enter = + cpu_to_le32(priv->hw_params.ct_kill_threshold); + adv_cmd.critical_temperature_exit = + cpu_to_le32(priv->hw_params.ct_kill_exit_threshold); + + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_CT_KILL_CONFIG_CMD, + 0, sizeof(adv_cmd), &adv_cmd); + if (ret) + IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); + else + IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " + "succeeded, critical temperature enter is %d," + "exit is %d\n", + priv->hw_params.ct_kill_threshold, + priv->hw_params.ct_kill_exit_threshold); + } else { + cmd.critical_temperature_R = + cpu_to_le32(priv->hw_params.ct_kill_threshold); + + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_CT_KILL_CONFIG_CMD, + 0, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); + else + IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " + "succeeded, " + "critical temperature is %d\n", + priv->hw_params.ct_kill_threshold); + } +} + +static int iwlagn_send_calib_cfg_rt(struct iwl_priv *priv, u32 cfg) +{ + struct iwl_calib_cfg_cmd calib_cfg_cmd; + struct iwl_host_cmd cmd = { + .id = CALIBRATION_CFG_CMD, + .len = { sizeof(struct iwl_calib_cfg_cmd), }, + .data = { &calib_cfg_cmd, }, + }; + + memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd)); + calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_RT_CFG_ALL; + calib_cfg_cmd.ucd_calib_cfg.once.start = cpu_to_le32(cfg); + + return iwl_dvm_send_cmd(priv, &cmd); +} + + +static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant) +{ + struct iwl_tx_ant_config_cmd tx_ant_cmd = { + .valid = cpu_to_le32(valid_tx_ant), + }; + + if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) { + IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant); + return iwl_dvm_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, 0, + sizeof(struct iwl_tx_ant_config_cmd), + &tx_ant_cmd); + } else { + IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n"); + return -EOPNOTSUPP; + } +} + +static void iwl_send_bt_config(struct iwl_priv *priv) +{ + struct iwl_bt_cmd bt_cmd = { + .lead_time = BT_LEAD_TIME_DEF, + .max_kill = BT_MAX_KILL_DEF, + .kill_ack_mask = 0, + .kill_cts_mask = 0, + }; + + if (!iwlwifi_mod_params.bt_coex_active) + bt_cmd.flags = BT_COEX_DISABLE; + else + bt_cmd.flags = BT_COEX_ENABLE; + + priv->bt_enable_flag = bt_cmd.flags; + IWL_DEBUG_INFO(priv, "BT coex %s\n", + (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); + + if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, + 0, sizeof(struct iwl_bt_cmd), &bt_cmd)) + IWL_ERR(priv, "failed to send BT Coex Config\n"); +} + +/** + * iwl_alive_start - called after REPLY_ALIVE notification received + * from protocol/runtime uCode (initialization uCode's + * Alive gets handled by iwl_init_alive_start()). + */ +int iwl_alive_start(struct iwl_priv *priv) +{ + int ret = 0; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + IWL_DEBUG_INFO(priv, "Runtime Alive received.\n"); + + /* After the ALIVE response, we can send host commands to the uCode */ + set_bit(STATUS_ALIVE, &priv->status); + + if (iwl_is_rfkill(priv)) + return -ERFKILL; + + if (priv->event_log.ucode_trace) { + /* start collecting data now */ + mod_timer(&priv->ucode_trace, jiffies); + } + + /* download priority table before any calibration request */ + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { + /* Configure Bluetooth device coexistence support */ + if (priv->lib->bt_params->bt_sco_disable) + priv->bt_enable_pspoll = false; + else + priv->bt_enable_pspoll = true; + + priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; + priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; + priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; + iwlagn_send_advance_bt_config(priv); + priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS; + priv->cur_rssi_ctx = NULL; + + iwl_send_prio_tbl(priv); + + /* FIXME: w/a to force change uCode BT state machine */ + ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); + if (ret) + return ret; + ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); + if (ret) + return ret; + } else if (priv->lib->bt_params) { + /* + * default is 2-wire BT coexexistence support + */ + iwl_send_bt_config(priv); + } + + /* + * Perform runtime calibrations, including DC calibration. + */ + iwlagn_send_calib_cfg_rt(priv, IWL_CALIB_CFG_DC_IDX); + + ieee80211_wake_queues(priv->hw); + + /* Configure Tx antenna selection based on H/W config */ + iwlagn_send_tx_ant_config(priv, priv->nvm_data->valid_tx_ant); + + if (iwl_is_associated_ctx(ctx) && !priv->wowlan) { + struct iwl_rxon_cmd *active_rxon = + (struct iwl_rxon_cmd *)&ctx->active; + /* apply any changes in staging */ + ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + } else { + struct iwl_rxon_context *tmp; + /* Initialize our rx_config data */ + for_each_context(priv, tmp) + iwl_connection_init_rx_config(priv, tmp); + + iwlagn_set_rxon_chain(priv, ctx); + } + + if (!priv->wowlan) { + /* WoWLAN ucode will not reply in the same way, skip it */ + iwl_reset_run_time_calib(priv); + } + + set_bit(STATUS_READY, &priv->status); + + /* Configure the adapter for unassociated operation */ + ret = iwlagn_commit_rxon(priv, ctx); + if (ret) + return ret; + + /* At this point, the NIC is initialized and operational */ + iwl_rf_kill_ct_config(priv); + + IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n"); + + return iwl_power_update_mode(priv, true); +} + +/** + * iwl_clear_driver_stations - clear knowledge of all stations from driver + * @priv: iwl priv struct + * + * This is called during iwl_down() to make sure that in the case + * we're coming there from a hardware restart mac80211 will be + * able to reconfigure stations -- if we're getting there in the + * normal down flow then the stations will already be cleared. + */ +static void iwl_clear_driver_stations(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + + spin_lock_bh(&priv->sta_lock); + memset(priv->stations, 0, sizeof(priv->stations)); + priv->num_stations = 0; + + priv->ucode_key_table = 0; + + for_each_context(priv, ctx) { + /* + * Remove all key information that is not stored as part + * of station information since mac80211 may not have had + * a chance to remove all the keys. When device is + * reconfigured by mac80211 after an error all keys will + * be reconfigured. + */ + memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys)); + ctx->key_mapping_keys = 0; + } + + spin_unlock_bh(&priv->sta_lock); +} + +void iwl_down(struct iwl_priv *priv) +{ + int exit_pending; + + IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n"); + + lockdep_assert_held(&priv->mutex); + + iwl_scan_cancel_timeout(priv, 200); + + exit_pending = + test_and_set_bit(STATUS_EXIT_PENDING, &priv->status); + + iwl_clear_ucode_stations(priv, NULL); + iwl_dealloc_bcast_stations(priv); + iwl_clear_driver_stations(priv); + + /* reset BT coex data */ + priv->bt_status = 0; + priv->cur_rssi_ctx = NULL; + priv->bt_is_sco = 0; + if (priv->lib->bt_params) + priv->bt_traffic_load = + priv->lib->bt_params->bt_init_traffic_load; + else + priv->bt_traffic_load = 0; + priv->bt_full_concurrent = false; + priv->bt_ci_compliance = 0; + + /* Wipe out the EXIT_PENDING status bit if we are not actually + * exiting the module */ + if (!exit_pending) + clear_bit(STATUS_EXIT_PENDING, &priv->status); + + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + + priv->ucode_loaded = false; + iwl_trans_stop_device(priv->trans); + + /* Set num_aux_in_flight must be done after the transport is stopped */ + atomic_set(&priv->num_aux_in_flight, 0); + + /* Clear out all status bits but a few that are stable across reset */ + priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) << + STATUS_RF_KILL_HW | + test_bit(STATUS_FW_ERROR, &priv->status) << + STATUS_FW_ERROR | + test_bit(STATUS_EXIT_PENDING, &priv->status) << + STATUS_EXIT_PENDING; + + dev_kfree_skb(priv->beacon_skb); + priv->beacon_skb = NULL; +} + +/***************************************************************************** + * + * Workqueue callbacks + * + *****************************************************************************/ + +static void iwl_bg_run_time_calib_work(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, + run_time_calib_work); + + mutex_lock(&priv->mutex); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status) || + test_bit(STATUS_SCANNING, &priv->status)) { + mutex_unlock(&priv->mutex); + return; + } + + if (priv->start_calib) { + iwl_chain_noise_calibration(priv); + iwl_sensitivity_calibration(priv); + } + + mutex_unlock(&priv->mutex); +} + +void iwlagn_prepare_restart(struct iwl_priv *priv) +{ + bool bt_full_concurrent; + u8 bt_ci_compliance; + u8 bt_load; + u8 bt_status; + bool bt_is_sco; + int i; + + lockdep_assert_held(&priv->mutex); + + priv->is_open = 0; + + /* + * __iwl_down() will clear the BT status variables, + * which is correct, but when we restart we really + * want to keep them so restore them afterwards. + * + * The restart process will later pick them up and + * re-configure the hw when we reconfigure the BT + * command. + */ + bt_full_concurrent = priv->bt_full_concurrent; + bt_ci_compliance = priv->bt_ci_compliance; + bt_load = priv->bt_traffic_load; + bt_status = priv->bt_status; + bt_is_sco = priv->bt_is_sco; + + iwl_down(priv); + + priv->bt_full_concurrent = bt_full_concurrent; + priv->bt_ci_compliance = bt_ci_compliance; + priv->bt_traffic_load = bt_load; + priv->bt_status = bt_status; + priv->bt_is_sco = bt_is_sco; + + /* reset aggregation queues */ + for (i = IWLAGN_FIRST_AMPDU_QUEUE; i < IWL_MAX_HW_QUEUES; i++) + priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; + /* and stop counts */ + for (i = 0; i < IWL_MAX_HW_QUEUES; i++) + atomic_set(&priv->queue_stop_count[i], 0); + + memset(priv->agg_q_alloc, 0, sizeof(priv->agg_q_alloc)); +} + +static void iwl_bg_restart(struct work_struct *data) +{ + struct iwl_priv *priv = container_of(data, struct iwl_priv, restart); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) { + mutex_lock(&priv->mutex); + iwlagn_prepare_restart(priv); + mutex_unlock(&priv->mutex); + iwl_cancel_deferred_work(priv); + if (priv->mac80211_registered) + ieee80211_restart_hw(priv->hw); + else + IWL_ERR(priv, + "Cannot request restart before registrating with mac80211\n"); + } else { + WARN_ON(1); + } +} + +/***************************************************************************** + * + * driver setup and teardown + * + *****************************************************************************/ + +static void iwl_setup_deferred_work(struct iwl_priv *priv) +{ + priv->workqueue = create_singlethread_workqueue(DRV_NAME); + + INIT_WORK(&priv->restart, iwl_bg_restart); + INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update); + INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work); + INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush); + INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency); + INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config); + + iwl_setup_scan_deferred_work(priv); + + if (priv->lib->bt_params) + iwlagn_bt_setup_deferred_work(priv); + + setup_timer(&priv->statistics_periodic, iwl_bg_statistics_periodic, + (unsigned long)priv); + + setup_timer(&priv->ucode_trace, iwl_bg_ucode_trace, + (unsigned long)priv); +} + +void iwl_cancel_deferred_work(struct iwl_priv *priv) +{ + if (priv->lib->bt_params) + iwlagn_bt_cancel_deferred_work(priv); + + cancel_work_sync(&priv->run_time_calib_work); + cancel_work_sync(&priv->beacon_update); + + iwl_cancel_scan_deferred_work(priv); + + cancel_work_sync(&priv->bt_full_concurrency); + cancel_work_sync(&priv->bt_runtime_config); + + del_timer_sync(&priv->statistics_periodic); + del_timer_sync(&priv->ucode_trace); +} + +static int iwl_init_drv(struct iwl_priv *priv) +{ + spin_lock_init(&priv->sta_lock); + + mutex_init(&priv->mutex); + + INIT_LIST_HEAD(&priv->calib_results); + + priv->band = IEEE80211_BAND_2GHZ; + + priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold; + + priv->iw_mode = NL80211_IFTYPE_STATION; + priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; + priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; + priv->agg_tids_count = 0; + + priv->rx_statistics_jiffies = jiffies; + + /* Choose which receivers/antennas to use */ + iwlagn_set_rxon_chain(priv, &priv->contexts[IWL_RXON_CTX_BSS]); + + iwl_init_scan_params(priv); + + /* init bt coex */ + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { + priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; + priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; + priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; + priv->bt_on_thresh = BT_ON_THRESHOLD_DEF; + priv->bt_duration = BT_DURATION_LIMIT_DEF; + priv->dynamic_frag_thresh = BT_FRAG_THRESHOLD_DEF; + } + + return 0; +} + +static void iwl_uninit_drv(struct iwl_priv *priv) +{ + kfree(priv->scan_cmd); + kfree(priv->beacon_cmd); + kfree(rcu_dereference_raw(priv->noa_data)); + iwl_calib_free_results(priv); +#ifdef CONFIG_IWLWIFI_DEBUGFS + kfree(priv->wowlan_sram); +#endif +} + +static void iwl_set_hw_params(struct iwl_priv *priv) +{ + if (priv->cfg->ht_params) + priv->hw_params.use_rts_for_aggregation = + priv->cfg->ht_params->use_rts_for_aggregation; + + /* Device-specific setup */ + priv->lib->set_hw_params(priv); +} + + + +/* show what optional capabilities we have */ +static void iwl_option_config(struct iwl_priv *priv) +{ +#ifdef CONFIG_IWLWIFI_DEBUG + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG enabled\n"); +#else + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG disabled\n"); +#endif + +#ifdef CONFIG_IWLWIFI_DEBUGFS + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS enabled\n"); +#else + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS disabled\n"); +#endif + +#ifdef CONFIG_IWLWIFI_DEVICE_TRACING + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING enabled\n"); +#else + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n"); +#endif +} + +static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) +{ + struct iwl_nvm_data *data = priv->nvm_data; + + if (data->sku_cap_11n_enable && + !priv->cfg->ht_params) { + IWL_ERR(priv, "Invalid 11n configuration\n"); + return -EINVAL; + } + + if (!data->sku_cap_11n_enable && !data->sku_cap_band_24GHz_enable && + !data->sku_cap_band_52GHz_enable) { + IWL_ERR(priv, "Invalid device sku\n"); + return -EINVAL; + } + + IWL_DEBUG_INFO(priv, + "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n", + data->sku_cap_band_24GHz_enable ? "" : "NOT", "enabled", + data->sku_cap_band_52GHz_enable ? "" : "NOT", "enabled", + data->sku_cap_11n_enable ? "" : "NOT", "enabled"); + + priv->hw_params.tx_chains_num = + num_of_ant(data->valid_tx_ant); + if (priv->cfg->rx_with_siso_diversity) + priv->hw_params.rx_chains_num = 1; + else + priv->hw_params.rx_chains_num = + num_of_ant(data->valid_rx_ant); + + IWL_DEBUG_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", + data->valid_tx_ant, + data->valid_rx_ant); + + return 0; +} + +static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, + const struct iwl_cfg *cfg, + const struct iwl_fw *fw, + struct dentry *dbgfs_dir) +{ + struct iwl_priv *priv; + struct ieee80211_hw *hw; + struct iwl_op_mode *op_mode; + u16 num_mac; + u32 ucode_flags; + struct iwl_trans_config trans_cfg = {}; + static const u8 no_reclaim_cmds[] = { + REPLY_RX_PHY_CMD, + REPLY_RX_MPDU_CMD, + REPLY_COMPRESSED_BA, + STATISTICS_NOTIFICATION, + REPLY_TX, + }; + int i; + + /************************ + * 1. Allocating HW data + ************************/ + hw = iwl_alloc_all(); + if (!hw) { + pr_err("%s: Cannot allocate network device\n", cfg->name); + goto out; + } + + op_mode = hw->priv; + op_mode->ops = &iwl_dvm_ops; + priv = IWL_OP_MODE_GET_DVM(op_mode); + priv->trans = trans; + priv->dev = trans->dev; + priv->cfg = cfg; + priv->fw = fw; + + switch (priv->cfg->device_family) { + case IWL_DEVICE_FAMILY_1000: + case IWL_DEVICE_FAMILY_100: + priv->lib = &iwl_dvm_1000_cfg; + break; + case IWL_DEVICE_FAMILY_2000: + priv->lib = &iwl_dvm_2000_cfg; + break; + case IWL_DEVICE_FAMILY_105: + priv->lib = &iwl_dvm_105_cfg; + break; + case IWL_DEVICE_FAMILY_2030: + case IWL_DEVICE_FAMILY_135: + priv->lib = &iwl_dvm_2030_cfg; + break; + case IWL_DEVICE_FAMILY_5000: + priv->lib = &iwl_dvm_5000_cfg; + break; + case IWL_DEVICE_FAMILY_5150: + priv->lib = &iwl_dvm_5150_cfg; + break; + case IWL_DEVICE_FAMILY_6000: + case IWL_DEVICE_FAMILY_6000i: + priv->lib = &iwl_dvm_6000_cfg; + break; + case IWL_DEVICE_FAMILY_6005: + priv->lib = &iwl_dvm_6005_cfg; + break; + case IWL_DEVICE_FAMILY_6050: + case IWL_DEVICE_FAMILY_6150: + priv->lib = &iwl_dvm_6050_cfg; + break; + case IWL_DEVICE_FAMILY_6030: + priv->lib = &iwl_dvm_6030_cfg; + break; + default: + break; + } + + if (WARN_ON(!priv->lib)) + goto out_free_hw; + + /* + * Populate the state variables that the transport layer needs + * to know about. + */ + trans_cfg.op_mode = op_mode; + trans_cfg.no_reclaim_cmds = no_reclaim_cmds; + trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); + trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; + trans_cfg.cmd_q_wdg_timeout = IWL_WATCHDOG_DISABLED; + + trans_cfg.command_names = iwl_dvm_cmd_strings; + trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM; + + WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE < + priv->cfg->base_params->num_of_queues); + + ucode_flags = fw->ucode_capa.flags; + + if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) { + priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN; + trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM; + } else { + priv->sta_key_max_num = STA_KEY_MAX_NUM; + trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; + } + + /* Configure transport layer */ + iwl_trans_configure(priv->trans, &trans_cfg); + + trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; + trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); + + /* At this point both hw and priv are allocated. */ + + SET_IEEE80211_DEV(priv->hw, priv->trans->dev); + + iwl_option_config(priv); + + IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n"); + + /* is antenna coupling more than 35dB ? */ + priv->bt_ant_couple_ok = + (iwlwifi_mod_params.ant_coupling > + IWL_BT_ANTENNA_COUPLING_THRESHOLD) ? + true : false; + + /* bt channel inhibition enabled*/ + priv->bt_ch_announce = true; + IWL_DEBUG_INFO(priv, "BT channel inhibition is %s\n", + (priv->bt_ch_announce) ? "On" : "Off"); + + /* these spin locks will be used in apm_ops.init and EEPROM access + * we should init now + */ + spin_lock_init(&priv->statistics.lock); + + /*********************** + * 2. Read REV register + ***********************/ + IWL_INFO(priv, "Detected %s, REV=0x%X\n", + priv->cfg->name, priv->trans->hw_rev); + + if (iwl_trans_start_hw(priv->trans)) + goto out_free_hw; + + /* Read the EEPROM */ + if (iwl_read_eeprom(priv->trans, &priv->eeprom_blob, + &priv->eeprom_blob_size)) { + IWL_ERR(priv, "Unable to init EEPROM\n"); + goto out_free_hw; + } + + /* Reset chip to save power until we load uCode during "up". */ + iwl_trans_stop_device(priv->trans); + + priv->nvm_data = iwl_parse_eeprom_data(priv->trans->dev, priv->cfg, + priv->eeprom_blob, + priv->eeprom_blob_size); + if (!priv->nvm_data) + goto out_free_eeprom_blob; + + if (iwl_nvm_check_version(priv->nvm_data, priv->trans)) + goto out_free_eeprom; + + if (iwl_eeprom_init_hw_params(priv)) + goto out_free_eeprom; + + /* extract MAC Address */ + memcpy(priv->addresses[0].addr, priv->nvm_data->hw_addr, ETH_ALEN); + IWL_DEBUG_INFO(priv, "MAC address: %pM\n", priv->addresses[0].addr); + priv->hw->wiphy->addresses = priv->addresses; + priv->hw->wiphy->n_addresses = 1; + num_mac = priv->nvm_data->n_hw_addrs; + if (num_mac > 1) { + memcpy(priv->addresses[1].addr, priv->addresses[0].addr, + ETH_ALEN); + priv->addresses[1].addr[5]++; + priv->hw->wiphy->n_addresses++; + } + + /************************ + * 4. Setup HW constants + ************************/ + iwl_set_hw_params(priv); + + if (!(priv->nvm_data->sku_cap_ipan_enable)) { + IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN\n"); + ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN; + /* + * if not PAN, then don't support P2P -- might be a uCode + * packaging bug or due to the eeprom check above + */ + priv->sta_key_max_num = STA_KEY_MAX_NUM; + trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; + + /* Configure transport layer again*/ + iwl_trans_configure(priv->trans, &trans_cfg); + } + + /******************* + * 5. Setup priv + *******************/ + for (i = 0; i < IWL_MAX_HW_QUEUES; i++) { + priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; + if (i < IWLAGN_FIRST_AMPDU_QUEUE && + i != IWL_DEFAULT_CMD_QUEUE_NUM && + i != IWL_IPAN_CMD_QUEUE_NUM) + priv->queue_to_mac80211[i] = i; + atomic_set(&priv->queue_stop_count[i], 0); + } + + if (iwl_init_drv(priv)) + goto out_free_eeprom; + + /* At this point both hw and priv are initialized. */ + + /******************** + * 6. Setup services + ********************/ + iwl_setup_deferred_work(priv); + iwl_setup_rx_handlers(priv); + + iwl_power_initialize(priv); + iwl_tt_initialize(priv); + + snprintf(priv->hw->wiphy->fw_version, + sizeof(priv->hw->wiphy->fw_version), + "%s", fw->fw_version); + + priv->new_scan_threshold_behaviour = + !!(ucode_flags & IWL_UCODE_TLV_FLAGS_NEWSCAN); + + priv->phy_calib_chain_noise_reset_cmd = + fw->ucode_capa.standard_phy_calibration_size; + priv->phy_calib_chain_noise_gain_cmd = + fw->ucode_capa.standard_phy_calibration_size + 1; + + /* initialize all valid contexts */ + iwl_init_context(priv, ucode_flags); + + /************************************************** + * This is still part of probe() in a sense... + * + * 7. Setup and register with mac80211 and debugfs + **************************************************/ + if (iwlagn_mac_setup_register(priv, &fw->ucode_capa)) + goto out_destroy_workqueue; + + if (iwl_dbgfs_register(priv, dbgfs_dir)) + goto out_mac80211_unregister; + + return op_mode; + +out_mac80211_unregister: + iwlagn_mac_unregister(priv); +out_destroy_workqueue: + iwl_tt_exit(priv); + iwl_cancel_deferred_work(priv); + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + iwl_uninit_drv(priv); +out_free_eeprom_blob: + kfree(priv->eeprom_blob); +out_free_eeprom: + iwl_free_nvm_data(priv->nvm_data); +out_free_hw: + ieee80211_free_hw(priv->hw); +out: + op_mode = NULL; + return op_mode; +} + +static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n"); + + iwlagn_mac_unregister(priv); + + iwl_tt_exit(priv); + + kfree(priv->eeprom_blob); + iwl_free_nvm_data(priv->nvm_data); + + /*netif_stop_queue(dev); */ + flush_workqueue(priv->workqueue); + + /* ieee80211_unregister_hw calls iwlagn_mac_stop, which flushes + * priv->workqueue... so we can't take down the workqueue + * until now... */ + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + + iwl_uninit_drv(priv); + + dev_kfree_skb(priv->beacon_skb); + + iwl_trans_op_mode_leave(priv->trans); + ieee80211_free_hw(priv->hw); +} + +static const char * const desc_lookup_text[] = { + "OK", + "FAIL", + "BAD_PARAM", + "BAD_CHECKSUM", + "NMI_INTERRUPT_WDG", + "SYSASSERT", + "FATAL_ERROR", + "BAD_COMMAND", + "HW_ERROR_TUNE_LOCK", + "HW_ERROR_TEMPERATURE", + "ILLEGAL_CHAN_FREQ", + "VCC_NOT_STABLE", + "FH_ERROR", + "NMI_INTERRUPT_HOST", + "NMI_INTERRUPT_ACTION_PT", + "NMI_INTERRUPT_UNKNOWN", + "UCODE_VERSION_MISMATCH", + "HW_ERROR_ABS_LOCK", + "HW_ERROR_CAL_LOCK_FAIL", + "NMI_INTERRUPT_INST_ACTION_PT", + "NMI_INTERRUPT_DATA_ACTION_PT", + "NMI_TRM_HW_ER", + "NMI_INTERRUPT_TRM", + "NMI_INTERRUPT_BREAK_POINT", + "DEBUG_0", + "DEBUG_1", + "DEBUG_2", + "DEBUG_3", +}; + +static struct { char *name; u8 num; } advanced_lookup[] = { + { "NMI_INTERRUPT_WDG", 0x34 }, + { "SYSASSERT", 0x35 }, + { "UCODE_VERSION_MISMATCH", 0x37 }, + { "BAD_COMMAND", 0x38 }, + { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, + { "FATAL_ERROR", 0x3D }, + { "NMI_TRM_HW_ERR", 0x46 }, + { "NMI_INTERRUPT_TRM", 0x4C }, + { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, + { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, + { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, + { "NMI_INTERRUPT_HOST", 0x66 }, + { "NMI_INTERRUPT_ACTION_PT", 0x7C }, + { "NMI_INTERRUPT_UNKNOWN", 0x84 }, + { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, + { "ADVANCED_SYSASSERT", 0 }, +}; + +static const char *desc_lookup(u32 num) +{ + int i; + int max = ARRAY_SIZE(desc_lookup_text); + + if (num < max) + return desc_lookup_text[num]; + + max = ARRAY_SIZE(advanced_lookup) - 1; + for (i = 0; i < max; i++) { + if (advanced_lookup[i].num == num) + break; + } + return advanced_lookup[i].name; +} + +#define ERROR_START_OFFSET (1 * sizeof(u32)) +#define ERROR_ELEM_SIZE (7 * sizeof(u32)) + +static void iwl_dump_nic_error_log(struct iwl_priv *priv) +{ + struct iwl_trans *trans = priv->trans; + u32 base; + struct iwl_error_event_table table; + + base = priv->device_pointers.error_event_table; + if (priv->cur_ucode == IWL_UCODE_INIT) { + if (!base) + base = priv->fw->init_errlog_ptr; + } else { + if (!base) + base = priv->fw->inst_errlog_ptr; + } + + if (!iwlagn_hw_valid_rtc_data_addr(base)) { + IWL_ERR(priv, + "Not valid error log pointer 0x%08X for %s uCode\n", + base, + (priv->cur_ucode == IWL_UCODE_INIT) + ? "Init" : "RT"); + return; + } + + /*TODO: Update dbgfs with ISR error stats obtained below */ + iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); + + if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { + IWL_ERR(trans, "Start IWL Error Log Dump:\n"); + IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", + priv->status, table.valid); + } + + trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, + table.data1, table.data2, table.line, + table.blink1, table.blink2, table.ilink1, + table.ilink2, table.bcon_time, table.gp1, + table.gp2, table.gp3, table.ucode_ver, + table.hw_ver, 0, table.brd_ver); + IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id, + desc_lookup(table.error_id)); + IWL_ERR(priv, "0x%08X | uPc\n", table.pc); + IWL_ERR(priv, "0x%08X | branchlink1\n", table.blink1); + IWL_ERR(priv, "0x%08X | branchlink2\n", table.blink2); + IWL_ERR(priv, "0x%08X | interruptlink1\n", table.ilink1); + IWL_ERR(priv, "0x%08X | interruptlink2\n", table.ilink2); + IWL_ERR(priv, "0x%08X | data1\n", table.data1); + IWL_ERR(priv, "0x%08X | data2\n", table.data2); + IWL_ERR(priv, "0x%08X | line\n", table.line); + IWL_ERR(priv, "0x%08X | beacon time\n", table.bcon_time); + IWL_ERR(priv, "0x%08X | tsf low\n", table.tsf_low); + IWL_ERR(priv, "0x%08X | tsf hi\n", table.tsf_hi); + IWL_ERR(priv, "0x%08X | time gp1\n", table.gp1); + IWL_ERR(priv, "0x%08X | time gp2\n", table.gp2); + IWL_ERR(priv, "0x%08X | time gp3\n", table.gp3); + IWL_ERR(priv, "0x%08X | uCode version\n", table.ucode_ver); + IWL_ERR(priv, "0x%08X | hw version\n", table.hw_ver); + IWL_ERR(priv, "0x%08X | board version\n", table.brd_ver); + IWL_ERR(priv, "0x%08X | hcmd\n", table.hcmd); + IWL_ERR(priv, "0x%08X | isr0\n", table.isr0); + IWL_ERR(priv, "0x%08X | isr1\n", table.isr1); + IWL_ERR(priv, "0x%08X | isr2\n", table.isr2); + IWL_ERR(priv, "0x%08X | isr3\n", table.isr3); + IWL_ERR(priv, "0x%08X | isr4\n", table.isr4); + IWL_ERR(priv, "0x%08X | isr_pref\n", table.isr_pref); + IWL_ERR(priv, "0x%08X | wait_event\n", table.wait_event); + IWL_ERR(priv, "0x%08X | l2p_control\n", table.l2p_control); + IWL_ERR(priv, "0x%08X | l2p_duration\n", table.l2p_duration); + IWL_ERR(priv, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); + IWL_ERR(priv, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); + IWL_ERR(priv, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); + IWL_ERR(priv, "0x%08X | timestamp\n", table.u_timestamp); + IWL_ERR(priv, "0x%08X | flow_handler\n", table.flow_handler); +} + +#define EVENT_START_OFFSET (4 * sizeof(u32)) + +/** + * iwl_print_event_log - Dump error event log to syslog + * + */ +static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, + u32 num_events, u32 mode, + int pos, char **buf, size_t bufsz) +{ + u32 i; + u32 base; /* SRAM byte address of event log header */ + u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ + u32 ptr; /* SRAM byte address of log data */ + u32 ev, time, data; /* event log data */ + unsigned long reg_flags; + + struct iwl_trans *trans = priv->trans; + + if (num_events == 0) + return pos; + + base = priv->device_pointers.log_event_table; + if (priv->cur_ucode == IWL_UCODE_INIT) { + if (!base) + base = priv->fw->init_evtlog_ptr; + } else { + if (!base) + base = priv->fw->inst_evtlog_ptr; + } + + if (mode == 0) + event_size = 2 * sizeof(u32); + else + event_size = 3 * sizeof(u32); + + ptr = base + EVENT_START_OFFSET + (start_idx * event_size); + + /* Make sure device is powered up for SRAM reads */ + if (!iwl_trans_grab_nic_access(trans, false, ®_flags)) + return pos; + + /* Set starting address; reads will auto-increment */ + iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr); + + /* "time" is actually "data" for mode 0 (no timestamp). + * place event id # at far right for easier visual parsing. */ + for (i = 0; i < num_events; i++) { + ev = iwl_read32(trans, HBUS_TARG_MEM_RDAT); + time = iwl_read32(trans, HBUS_TARG_MEM_RDAT); + if (mode == 0) { + /* data, ev */ + if (bufsz) { + pos += scnprintf(*buf + pos, bufsz - pos, + "EVT_LOG:0x%08x:%04u\n", + time, ev); + } else { + trace_iwlwifi_dev_ucode_event(trans->dev, 0, + time, ev); + IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", + time, ev); + } + } else { + data = iwl_read32(trans, HBUS_TARG_MEM_RDAT); + if (bufsz) { + pos += scnprintf(*buf + pos, bufsz - pos, + "EVT_LOGT:%010u:0x%08x:%04u\n", + time, data, ev); + } else { + IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", + time, data, ev); + trace_iwlwifi_dev_ucode_event(trans->dev, time, + data, ev); + } + } + } + + /* Allow device to power down */ + iwl_trans_release_nic_access(trans, ®_flags); + return pos; +} + +/** + * iwl_print_last_event_logs - Dump the newest # of event log to syslog + */ +static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity, + u32 num_wraps, u32 next_entry, + u32 size, u32 mode, + int pos, char **buf, size_t bufsz) +{ + /* + * display the newest DEFAULT_LOG_ENTRIES entries + * i.e the entries just before the next ont that uCode would fill. + */ + if (num_wraps) { + if (next_entry < size) { + pos = iwl_print_event_log(priv, + capacity - (size - next_entry), + size - next_entry, mode, + pos, buf, bufsz); + pos = iwl_print_event_log(priv, 0, + next_entry, mode, + pos, buf, bufsz); + } else + pos = iwl_print_event_log(priv, next_entry - size, + size, mode, pos, buf, bufsz); + } else { + if (next_entry < size) { + pos = iwl_print_event_log(priv, 0, next_entry, + mode, pos, buf, bufsz); + } else { + pos = iwl_print_event_log(priv, next_entry - size, + size, mode, pos, buf, bufsz); + } + } + return pos; +} + +#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20) + +int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, + char **buf) +{ + u32 base; /* SRAM byte address of event log header */ + u32 capacity; /* event log capacity in # entries */ + u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ + u32 num_wraps; /* # times uCode wrapped to top of log */ + u32 next_entry; /* index of next entry to be written by uCode */ + u32 size; /* # entries that we'll print */ + u32 logsize; + int pos = 0; + size_t bufsz = 0; + struct iwl_trans *trans = priv->trans; + + base = priv->device_pointers.log_event_table; + if (priv->cur_ucode == IWL_UCODE_INIT) { + logsize = priv->fw->init_evtlog_size; + if (!base) + base = priv->fw->init_evtlog_ptr; + } else { + logsize = priv->fw->inst_evtlog_size; + if (!base) + base = priv->fw->inst_evtlog_ptr; + } + + if (!iwlagn_hw_valid_rtc_data_addr(base)) { + IWL_ERR(priv, + "Invalid event log pointer 0x%08X for %s uCode\n", + base, + (priv->cur_ucode == IWL_UCODE_INIT) + ? "Init" : "RT"); + return -EINVAL; + } + + /* event log header */ + capacity = iwl_trans_read_mem32(trans, base); + mode = iwl_trans_read_mem32(trans, base + (1 * sizeof(u32))); + num_wraps = iwl_trans_read_mem32(trans, base + (2 * sizeof(u32))); + next_entry = iwl_trans_read_mem32(trans, base + (3 * sizeof(u32))); + + if (capacity > logsize) { + IWL_ERR(priv, "Log capacity %d is bogus, limit to %d " + "entries\n", capacity, logsize); + capacity = logsize; + } + + if (next_entry > logsize) { + IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n", + next_entry, logsize); + next_entry = logsize; + } + + size = num_wraps ? capacity : next_entry; + + /* bail out if nothing in log */ + if (size == 0) { + IWL_ERR(trans, "Start IWL Event Log Dump: nothing in log\n"); + return pos; + } + + if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log) + size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) + ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; + IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n", + size); + +#ifdef CONFIG_IWLWIFI_DEBUG + if (buf) { + if (full_log) + bufsz = capacity * 48; + else + bufsz = size * 48; + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + } + if (iwl_have_debug_level(IWL_DL_FW_ERRORS) || full_log) { + /* + * if uCode has wrapped back to top of log, + * start at the oldest entry, + * i.e the next one that uCode would fill. + */ + if (num_wraps) + pos = iwl_print_event_log(priv, next_entry, + capacity - next_entry, mode, + pos, buf, bufsz); + /* (then/else) start at top of log */ + pos = iwl_print_event_log(priv, 0, + next_entry, mode, pos, buf, bufsz); + } else + pos = iwl_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode, + pos, buf, bufsz); +#else + pos = iwl_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode, + pos, buf, bufsz); +#endif + return pos; +} + +static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) +{ + unsigned int reload_msec; + unsigned long reload_jiffies; + + if (iwl_have_debug_level(IWL_DL_FW_ERRORS)) + iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS); + + /* uCode is no longer loaded. */ + priv->ucode_loaded = false; + + /* Set the FW error flag -- cleared on iwl_down */ + set_bit(STATUS_FW_ERROR, &priv->status); + + iwl_abort_notification_waits(&priv->notif_wait); + + /* Keep the restart process from trying to send host + * commands by clearing the ready bit */ + clear_bit(STATUS_READY, &priv->status); + + if (!ondemand) { + /* + * If firmware keep reloading, then it indicate something + * serious wrong and firmware having problem to recover + * from it. Instead of keep trying which will fill the syslog + * and hang the system, let's just stop it + */ + reload_jiffies = jiffies; + reload_msec = jiffies_to_msecs((long) reload_jiffies - + (long) priv->reload_jiffies); + priv->reload_jiffies = reload_jiffies; + if (reload_msec <= IWL_MIN_RELOAD_DURATION) { + priv->reload_count++; + if (priv->reload_count >= IWL_MAX_CONTINUE_RELOAD_CNT) { + IWL_ERR(priv, "BUG_ON, Stop restarting\n"); + return; + } + } else + priv->reload_count = 0; + } + + if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) { + if (iwlwifi_mod_params.restart_fw) { + IWL_DEBUG_FW_ERRORS(priv, + "Restarting adapter due to uCode error.\n"); + queue_work(priv->workqueue, &priv->restart); + } else + IWL_DEBUG_FW_ERRORS(priv, + "Detected FW error, but not restarting\n"); + } +} + +static void iwl_nic_error(struct iwl_op_mode *op_mode) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + IWL_ERR(priv, "Loaded firmware version: %s\n", + priv->fw->fw_version); + + iwl_dump_nic_error_log(priv); + iwl_dump_nic_event_log(priv, false, NULL); + + iwlagn_fw_error(priv, false); +} + +static void iwl_cmd_queue_full(struct iwl_op_mode *op_mode) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + if (!iwl_check_for_ct_kill(priv)) { + IWL_ERR(priv, "Restarting adapter queue is full\n"); + iwlagn_fw_error(priv, false); + } +} + +#define EEPROM_RF_CONFIG_TYPE_MAX 0x3 + +static void iwl_nic_config(struct iwl_op_mode *op_mode) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + /* SKU Control */ + iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | + CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP, + (CSR_HW_REV_STEP(priv->trans->hw_rev) << + CSR_HW_IF_CONFIG_REG_POS_MAC_STEP) | + (CSR_HW_REV_DASH(priv->trans->hw_rev) << + CSR_HW_IF_CONFIG_REG_POS_MAC_DASH)); + + /* write radio config values to register */ + if (priv->nvm_data->radio_cfg_type <= EEPROM_RF_CONFIG_TYPE_MAX) { + u32 reg_val = + priv->nvm_data->radio_cfg_type << + CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE | + priv->nvm_data->radio_cfg_step << + CSR_HW_IF_CONFIG_REG_POS_PHY_STEP | + priv->nvm_data->radio_cfg_dash << + CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; + + iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | + CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | + CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH, + reg_val); + + IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n", + priv->nvm_data->radio_cfg_type, + priv->nvm_data->radio_cfg_step, + priv->nvm_data->radio_cfg_dash); + } else { + WARN_ON(1); + } + + /* set CSR_HW_CONFIG_REG for uCode use */ + iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | + CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); + + /* W/A : NIC is stuck in a reset state after Early PCIe power off + * (PCIe power is lost before PERST# is asserted), + * causing ME FW to lose ownership and not being able to obtain it back. + */ + iwl_set_bits_mask_prph(priv->trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, + ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); + + if (priv->lib->nic_config) + priv->lib->nic_config(priv); +} + +static void iwl_wimax_active(struct iwl_op_mode *op_mode) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + clear_bit(STATUS_READY, &priv->status); + IWL_ERR(priv, "RF is used by WiMAX\n"); +} + +static void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + int mq = priv->queue_to_mac80211[queue]; + + if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) + return; + + if (atomic_inc_return(&priv->queue_stop_count[mq]) > 1) { + IWL_DEBUG_TX_QUEUES(priv, + "queue %d (mac80211 %d) already stopped\n", + queue, mq); + return; + } + + set_bit(mq, &priv->transport_queue_stop); + ieee80211_stop_queue(priv->hw, mq); +} + +static void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + int mq = priv->queue_to_mac80211[queue]; + + if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) + return; + + if (atomic_dec_return(&priv->queue_stop_count[mq]) > 0) { + IWL_DEBUG_TX_QUEUES(priv, + "queue %d (mac80211 %d) already awake\n", + queue, mq); + return; + } + + clear_bit(mq, &priv->transport_queue_stop); + + if (!priv->passive_no_rx) + ieee80211_wake_queue(priv->hw, mq); +} + +void iwlagn_lift_passive_no_rx(struct iwl_priv *priv) +{ + int mq; + + if (!priv->passive_no_rx) + return; + + for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) { + if (!test_bit(mq, &priv->transport_queue_stop)) { + IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d\n", mq); + ieee80211_wake_queue(priv->hw, mq); + } else { + IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d\n", mq); + } + } + + priv->passive_no_rx = false; +} + +static void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + struct ieee80211_tx_info *info; + + info = IEEE80211_SKB_CB(skb); + iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); + ieee80211_free_txskb(priv->hw, skb); +} + +static bool iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + if (state) + set_bit(STATUS_RF_KILL_HW, &priv->status); + else + clear_bit(STATUS_RF_KILL_HW, &priv->status); + + wiphy_rfkill_set_hw_state(priv->hw->wiphy, state); + + return false; +} + +static const struct iwl_op_mode_ops iwl_dvm_ops = { + .start = iwl_op_mode_dvm_start, + .stop = iwl_op_mode_dvm_stop, + .rx = iwl_rx_dispatch, + .queue_full = iwl_stop_sw_queue, + .queue_not_full = iwl_wake_sw_queue, + .hw_rf_kill = iwl_set_hw_rfkill_state, + .free_skb = iwl_free_skb, + .nic_error = iwl_nic_error, + .cmd_queue_full = iwl_cmd_queue_full, + .nic_config = iwl_nic_config, + .wimax_active = iwl_wimax_active, +}; + +/***************************************************************************** + * + * driver and module entry point + * + *****************************************************************************/ +static int __init iwl_init(void) +{ + + int ret; + + ret = iwlagn_rate_control_register(); + if (ret) { + pr_err("Unable to register rate control algorithm: %d\n", ret); + return ret; + } + + ret = iwl_opmode_register("iwldvm", &iwl_dvm_ops); + if (ret) { + pr_err("Unable to register op_mode: %d\n", ret); + iwlagn_rate_control_unregister(); + } + + return ret; +} +module_init(iwl_init); + +static void __exit iwl_exit(void) +{ + iwl_opmode_deregister("iwldvm"); + iwlagn_rate_control_unregister(); +} +module_exit(iwl_exit); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/power.c b/drivers/net/wireless/intel/iwlwifi/dvm/power.c new file mode 100644 index 000000000000..1513dbc79c14 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.c @@ -0,0 +1,395 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + + +#include +#include +#include +#include +#include "iwl-io.h" +#include "iwl-debug.h" +#include "iwl-trans.h" +#include "iwl-modparams.h" +#include "dev.h" +#include "agn.h" +#include "commands.h" +#include "power.h" + +static bool force_cam = true; +module_param(force_cam, bool, 0644); +MODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)"); + +/* + * Setting power level allows the card to go to sleep when not busy. + * + * We calculate a sleep command based on the required latency, which + * we get from mac80211. In order to handle thermal throttling, we can + * also use pre-defined power levels. + */ + +/* + * This defines the old power levels. They are still used by default + * (level 1) and for thermal throttle (levels 3 through 5) + */ + +struct iwl_power_vec_entry { + struct iwl_powertable_cmd cmd; + u8 no_dtim; /* number of skip dtim */ +}; + +#define IWL_DTIM_RANGE_0_MAX 2 +#define IWL_DTIM_RANGE_1_MAX 10 + +#define NOSLP cpu_to_le16(0), 0, 0 +#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 +#define ASLP (IWL_POWER_POWER_SAVE_ENA_MSK | \ + IWL_POWER_POWER_MANAGEMENT_ENA_MSK | \ + IWL_POWER_ADVANCE_PM_ENA_MSK) +#define ASLP_TOUT(T) cpu_to_le32(T) +#define TU_TO_USEC 1024 +#define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC) +#define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \ + cpu_to_le32(X1), \ + cpu_to_le32(X2), \ + cpu_to_le32(X3), \ + cpu_to_le32(X4)} +/* default power management (not Tx power) table values */ +/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */ +/* DTIM 0 - 2 */ +static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { + {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0}, + {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1}, + {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2} +}; + + +/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ +/* DTIM 3 - 10 */ +static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { + {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, + {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1}, + {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2} +}; + +/* for DTIM period > IWL_DTIM_RANGE_1_MAX */ +/* DTIM 11 - */ +static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { + {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, + {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, + {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} +}; + +/* advance power management */ +/* DTIM 0 - 2 */ +static const struct iwl_power_vec_entry apm_range_0[IWL_POWER_NUM] = { + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} +}; + + +/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ +/* DTIM 3 - 10 */ +static const struct iwl_power_vec_entry apm_range_1[IWL_POWER_NUM] = { + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 6, 8, 0xFF), 0}, 2} +}; + +/* for DTIM period > IWL_DTIM_RANGE_1_MAX */ +/* DTIM 11 - */ +static const struct iwl_power_vec_entry apm_range_2[IWL_POWER_NUM] = { + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} +}; + +static void iwl_static_sleep_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd, + enum iwl_power_level lvl, int period) +{ + const struct iwl_power_vec_entry *table; + int max_sleep[IWL_POWER_VEC_SIZE] = { 0 }; + int i; + u8 skip; + u32 slp_itrvl; + + if (priv->lib->adv_pm) { + table = apm_range_2; + if (period <= IWL_DTIM_RANGE_1_MAX) + table = apm_range_1; + if (period <= IWL_DTIM_RANGE_0_MAX) + table = apm_range_0; + } else { + table = range_2; + if (period <= IWL_DTIM_RANGE_1_MAX) + table = range_1; + if (period <= IWL_DTIM_RANGE_0_MAX) + table = range_0; + } + + if (WARN_ON(lvl < 0 || lvl >= IWL_POWER_NUM)) + memset(cmd, 0, sizeof(*cmd)); + else + *cmd = table[lvl].cmd; + + if (period == 0) { + skip = 0; + period = 1; + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + max_sleep[i] = 1; + + } else { + skip = table[lvl].no_dtim; + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]); + max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1; + } + + slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); + /* figure out the listen interval based on dtim period and skip */ + if (slp_itrvl == 0xFF) + cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = + cpu_to_le32(period * (skip + 1)); + + slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); + if (slp_itrvl > period) + cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = + cpu_to_le32((slp_itrvl / period) * period); + + if (skip) + cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; + else + cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; + + if (priv->cfg->base_params->shadow_reg_enable) + cmd->flags |= IWL_POWER_SHADOW_REG_ENA; + else + cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; + + if (iwl_advanced_bt_coexist(priv)) { + if (!priv->lib->bt_params->bt_sco_disable) + cmd->flags |= IWL_POWER_BT_SCO_ENA; + else + cmd->flags &= ~IWL_POWER_BT_SCO_ENA; + } + + + slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); + if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL) + cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = + cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL); + + /* enforce max sleep interval */ + for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) { + if (le32_to_cpu(cmd->sleep_interval[i]) > + (max_sleep[i] * period)) + cmd->sleep_interval[i] = + cpu_to_le32(max_sleep[i] * period); + if (i != (IWL_POWER_VEC_SIZE - 1)) { + if (le32_to_cpu(cmd->sleep_interval[i]) > + le32_to_cpu(cmd->sleep_interval[i+1])) + cmd->sleep_interval[i] = + cmd->sleep_interval[i+1]; + } + } + + if (priv->power_data.bus_pm) + cmd->flags |= IWL_POWER_PCI_PM_MSK; + else + cmd->flags &= ~IWL_POWER_PCI_PM_MSK; + + IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n", + skip, period); + /* The power level here is 0-4 (used as array index), but user expects + to see 1-5 (according to spec). */ + IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1); +} + +static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd) +{ + memset(cmd, 0, sizeof(*cmd)); + + if (priv->power_data.bus_pm) + cmd->flags |= IWL_POWER_PCI_PM_MSK; + + IWL_DEBUG_POWER(priv, "Sleep command for CAM\n"); +} + +static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) +{ + IWL_DEBUG_POWER(priv, "Sending power/sleep command\n"); + IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags); + IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); + IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); + IWL_DEBUG_POWER(priv, "Sleep interval vector = { %d , %d , %d , %d , %d }\n", + le32_to_cpu(cmd->sleep_interval[0]), + le32_to_cpu(cmd->sleep_interval[1]), + le32_to_cpu(cmd->sleep_interval[2]), + le32_to_cpu(cmd->sleep_interval[3]), + le32_to_cpu(cmd->sleep_interval[4])); + + return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, 0, + sizeof(struct iwl_powertable_cmd), cmd); +} + +static void iwl_power_build_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd) +{ + bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS; + int dtimper; + + if (force_cam) { + iwl_power_sleep_cam_cmd(priv, cmd); + return; + } + + dtimper = priv->hw->conf.ps_dtim_period ?: 1; + + if (priv->wowlan) + iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); + else if (!priv->lib->no_idle_support && + priv->hw->conf.flags & IEEE80211_CONF_IDLE) + iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); + else if (iwl_tt_is_low_power_state(priv)) { + /* in thermal throttling low power state */ + iwl_static_sleep_cmd(priv, cmd, + iwl_tt_current_power_mode(priv), dtimper); + } else if (!enabled) + iwl_power_sleep_cam_cmd(priv, cmd); + else if (priv->power_data.debug_sleep_level_override >= 0) + iwl_static_sleep_cmd(priv, cmd, + priv->power_data.debug_sleep_level_override, + dtimper); + else { + /* Note that the user parameter is 1-5 (according to spec), + but we pass 0-4 because it acts as an array index. */ + if (iwlwifi_mod_params.power_level > IWL_POWER_INDEX_1 && + iwlwifi_mod_params.power_level <= IWL_POWER_NUM) + iwl_static_sleep_cmd(priv, cmd, + iwlwifi_mod_params.power_level - 1, dtimper); + else + iwl_static_sleep_cmd(priv, cmd, + IWL_POWER_INDEX_1, dtimper); + } +} + +int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, + bool force) +{ + int ret; + bool update_chains; + + lockdep_assert_held(&priv->mutex); + + /* Don't update the RX chain when chain noise calibration is running */ + update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE || + priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; + + if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) + return 0; + + if (!iwl_is_ready_rf(priv)) + return -EIO; + + /* scan complete use sleep_power_next, need to be updated */ + memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); + if (test_bit(STATUS_SCANNING, &priv->status) && !force) { + IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n"); + return 0; + } + + if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK) + iwl_dvm_set_pmi(priv, true); + + ret = iwl_set_power(priv, cmd); + if (!ret) { + if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)) + iwl_dvm_set_pmi(priv, false); + + if (update_chains) + iwl_update_chain_flags(priv); + else + IWL_DEBUG_POWER(priv, + "Cannot update the power, chain noise " + "calibration running: %d\n", + priv->chain_noise_data.state); + + memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)); + } else + IWL_ERR(priv, "set power fail, ret = %d\n", ret); + + return ret; +} + +int iwl_power_update_mode(struct iwl_priv *priv, bool force) +{ + struct iwl_powertable_cmd cmd; + + iwl_power_build_cmd(priv, &cmd); + return iwl_power_set_mode(priv, &cmd, force); +} + +/* initialize to default */ +void iwl_power_initialize(struct iwl_priv *priv) +{ + priv->power_data.bus_pm = priv->trans->pm_support; + + priv->power_data.debug_sleep_level_override = -1; + + memset(&priv->power_data.sleep_cmd, 0, + sizeof(priv->power_data.sleep_cmd)); +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/power.h b/drivers/net/wireless/intel/iwlwifi/dvm/power.h new file mode 100644 index 000000000000..570d3a5e4670 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ +#ifndef __iwl_power_setting_h__ +#define __iwl_power_setting_h__ + +#include "commands.h" + +struct iwl_power_mgr { + struct iwl_powertable_cmd sleep_cmd; + struct iwl_powertable_cmd sleep_cmd_next; + int debug_sleep_level_override; + bool bus_pm; +}; + +int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, + bool force); +int iwl_power_update_mode(struct iwl_priv *priv, bool force); +void iwl_power_initialize(struct iwl_priv *priv); + +extern bool no_sleep_autoadjust; + +#endif /* __iwl_power_setting_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c new file mode 100644 index 000000000000..cef921c1a623 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c @@ -0,0 +1,3338 @@ +/****************************************************************************** + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "dev.h" +#include "agn.h" + +#define RS_NAME "iwl-agn-rs" + +#define NUM_TRY_BEFORE_ANT_TOGGLE 1 +#define IWL_NUMBER_TRY 1 +#define IWL_HT_NUMBER_TRY 3 + +#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ +#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ +#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ + +/* max allowed rate miss before sync LQ cmd */ +#define IWL_MISSED_RATE_MAX 15 +/* max time to accum history 2 seconds */ +#define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ) + +static u8 rs_ht_to_legacy[] = { + IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, + IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, + IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, + IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX +}; + +static const u8 ant_toggle_lookup[] = { + /*ANT_NONE -> */ ANT_NONE, + /*ANT_A -> */ ANT_B, + /*ANT_B -> */ ANT_C, + /*ANT_AB -> */ ANT_BC, + /*ANT_C -> */ ANT_A, + /*ANT_AC -> */ ANT_AB, + /*ANT_BC -> */ ANT_AC, + /*ANT_ABC -> */ ANT_ABC, +}; + +#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ + [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ + IWL_RATE_SISO_##s##M_PLCP, \ + IWL_RATE_MIMO2_##s##M_PLCP,\ + IWL_RATE_MIMO3_##s##M_PLCP,\ + IWL_RATE_##r##M_IEEE, \ + IWL_RATE_##ip##M_INDEX, \ + IWL_RATE_##in##M_INDEX, \ + IWL_RATE_##rp##M_INDEX, \ + IWL_RATE_##rn##M_INDEX, \ + IWL_RATE_##pp##M_INDEX, \ + IWL_RATE_##np##M_INDEX } + +/* + * Parameter order: + * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate + * + * If there isn't a valid next or previous rate then INV is used which + * maps to IWL_RATE_INVALID + * + */ +const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { + IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ + IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ + IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ + IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ + IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ + IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ + IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ + IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ + IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ + IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ + IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ + IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ + IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ + /* FIXME:RS: ^^ should be INV (legacy) */ +}; + +static inline u8 rs_extract_rate(u32 rate_n_flags) +{ + return (u8)(rate_n_flags & RATE_MCS_RATE_MSK); +} + +static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) +{ + int idx = 0; + + /* HT rate format */ + if (rate_n_flags & RATE_MCS_HT_MSK) { + idx = rs_extract_rate(rate_n_flags); + + if (idx >= IWL_RATE_MIMO3_6M_PLCP) + idx = idx - IWL_RATE_MIMO3_6M_PLCP; + else if (idx >= IWL_RATE_MIMO2_6M_PLCP) + idx = idx - IWL_RATE_MIMO2_6M_PLCP; + + idx += IWL_FIRST_OFDM_RATE; + /* skip 9M not supported in ht*/ + if (idx >= IWL_RATE_9M_INDEX) + idx += 1; + if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE)) + return idx; + + /* legacy rate format, search for match in table */ + } else { + for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) + if (iwl_rates[idx].plcp == + rs_extract_rate(rate_n_flags)) + return idx; + } + + return -1; +} + +static void rs_rate_scale_perform(struct iwl_priv *priv, + struct sk_buff *skb, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta); +static void rs_fill_link_cmd(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, u32 rate_n_flags); +static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); + + +#ifdef CONFIG_MAC80211_DEBUGFS +static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index); +#else +static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index) +{} +#endif + +/** + * The following tables contain the expected throughput metrics for all rates + * + * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits + * + * where invalid entries are zeros. + * + * CCK rates are only valid in legacy table and will only be used in G + * (2.4 GHz) band. + */ + +static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = { + 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 +}; + +static const u16 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */ + {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */ + {0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */ + {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */ +}; + +static const u16 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ + {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ + {0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */ + {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */ +}; + +static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */ + {0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */ + {0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */ + {0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/ +}; + +static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ + {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ + {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */ + {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */ +}; + +static const u16 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */ + {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */ + {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */ + {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */ +}; + +static const u16 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */ + {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */ + {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */ + {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */ +}; + +/* mbps, mcs */ +static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { + { "1", "BPSK DSSS"}, + { "2", "QPSK DSSS"}, + {"5.5", "BPSK CCK"}, + { "11", "QPSK CCK"}, + { "6", "BPSK 1/2"}, + { "9", "BPSK 1/2"}, + { "12", "QPSK 1/2"}, + { "18", "QPSK 3/4"}, + { "24", "16QAM 1/2"}, + { "36", "16QAM 3/4"}, + { "48", "64QAM 2/3"}, + { "54", "64QAM 3/4"}, + { "60", "64QAM 5/6"}, +}; + +#define MCS_INDEX_PER_STREAM (8) + +static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) +{ + window->data = 0; + window->success_counter = 0; + window->success_ratio = IWL_INVALID_VALUE; + window->counter = 0; + window->average_tpt = IWL_INVALID_VALUE; + window->stamp = 0; +} + +static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) +{ + return (ant_type & valid_antenna) == ant_type; +} + +/* + * removes the old data from the statistics. All data that is older than + * TID_MAX_TIME_DIFF, will be deleted. + */ +static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time) +{ + /* The oldest age we want to keep */ + u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; + + while (tl->queue_count && + (tl->time_stamp < oldest_time)) { + tl->total -= tl->packet_count[tl->head]; + tl->packet_count[tl->head] = 0; + tl->time_stamp += TID_QUEUE_CELL_SPACING; + tl->queue_count--; + tl->head++; + if (tl->head >= TID_QUEUE_MAX_SIZE) + tl->head = 0; + } +} + +/* + * increment traffic load value for tid and also remove + * any old values if passed the certain time period + */ +static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data, + struct ieee80211_hdr *hdr) +{ + u32 curr_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 index; + struct iwl_traffic_load *tl = NULL; + u8 tid; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + } else + return IWL_MAX_TID_COUNT; + + if (unlikely(tid >= IWL_MAX_TID_COUNT)) + return IWL_MAX_TID_COUNT; + + tl = &lq_data->load[tid]; + + curr_time -= curr_time % TID_ROUND_VALUE; + + /* Happens only for the first packet. Initialize the data */ + if (!(tl->queue_count)) { + tl->total = 1; + tl->time_stamp = curr_time; + tl->queue_count = 1; + tl->head = 0; + tl->packet_count[0] = 1; + return IWL_MAX_TID_COUNT; + } + + time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); + index = time_diff / TID_QUEUE_CELL_SPACING; + + /* The history is too long: remove data that is older than */ + /* TID_MAX_TIME_DIFF */ + if (index >= TID_QUEUE_MAX_SIZE) + rs_tl_rm_old_stats(tl, curr_time); + + index = (tl->head + index) % TID_QUEUE_MAX_SIZE; + tl->packet_count[index] = tl->packet_count[index] + 1; + tl->total = tl->total + 1; + + if ((index + 1) > tl->queue_count) + tl->queue_count = index + 1; + + return tid; +} + +#ifdef CONFIG_MAC80211_DEBUGFS +/** + * Program the device to use fixed rate for frame transmit + * This is for debugging/testing only + * once the device start use fixed rate, we need to reload the module + * to being back the normal operation. + */ +static void rs_program_fix_rate(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta) +{ + struct iwl_station_priv *sta_priv = + container_of(lq_sta, struct iwl_station_priv, lq_sta); + struct iwl_rxon_context *ctx = sta_priv->ctx; + + lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ + lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + + IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n", + lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); + + if (lq_sta->dbg_fixed_rate) { + rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); + iwl_send_lq_cmd(lq_sta->drv, ctx, &lq_sta->lq, CMD_ASYNC, + false); + } +} +#endif + +/* + get the traffic load value for tid +*/ +static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid) +{ + u32 curr_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 index; + struct iwl_traffic_load *tl = NULL; + + if (tid >= IWL_MAX_TID_COUNT) + return 0; + + tl = &(lq_data->load[tid]); + + curr_time -= curr_time % TID_ROUND_VALUE; + + if (!(tl->queue_count)) + return 0; + + time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); + index = time_diff / TID_QUEUE_CELL_SPACING; + + /* The history is too long: remove data that is older than */ + /* TID_MAX_TIME_DIFF */ + if (index >= TID_QUEUE_MAX_SIZE) + rs_tl_rm_old_stats(tl, curr_time); + + return tl->total; +} + +static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, + struct iwl_lq_sta *lq_data, u8 tid, + struct ieee80211_sta *sta) +{ + int ret = -EAGAIN; + u32 load; + + /* + * Don't create TX aggregation sessions when in high + * BT traffic, as they would just be disrupted by BT. + */ + if (priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) { + IWL_DEBUG_COEX(priv, + "BT traffic (%d), no aggregation allowed\n", + priv->bt_traffic_load); + return ret; + } + + load = rs_tl_get_load(lq_data, tid); + + IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", + sta->addr, tid); + ret = ieee80211_start_tx_ba_session(sta, tid, 5000); + if (ret == -EAGAIN) { + /* + * driver and mac80211 is out of sync + * this might be cause by reloading firmware + * stop the tx ba session here + */ + IWL_ERR(priv, "Fail start Tx agg on tid: %d\n", + tid); + ieee80211_stop_tx_ba_session(sta, tid); + } + return ret; +} + +static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, + struct iwl_lq_sta *lq_data, + struct ieee80211_sta *sta) +{ + if (tid < IWL_MAX_TID_COUNT) + rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); + else + IWL_ERR(priv, "tid exceeds max TID count: %d/%d\n", + tid, IWL_MAX_TID_COUNT); +} + +static inline int get_num_of_ant_from_rate(u32 rate_n_flags) +{ + return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_C_MSK); +} + +/* + * Static function to get the expected throughput from an iwl_scale_tbl_info + * that wraps a NULL pointer check + */ +static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) +{ + if (tbl->expected_tpt) + return tbl->expected_tpt[rs_index]; + return 0; +} + +/** + * rs_collect_tx_data - Update the success/failure sliding window + * + * We keep a sliding window of the last 62 packets transmitted + * at this rate. window->data contains the bitmask of successful + * packets. + */ +static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, + int scale_index, int attempts, int successes) +{ + struct iwl_rate_scale_data *window = NULL; + static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); + s32 fail_count, tpt; + + if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) + return -EINVAL; + + /* Select window for current tx bit rate */ + window = &(tbl->win[scale_index]); + + /* Get expected throughput */ + tpt = get_expected_tpt(tbl, scale_index); + + /* + * Keep track of only the latest 62 tx frame attempts in this rate's + * history window; anything older isn't really relevant any more. + * If we have filled up the sliding window, drop the oldest attempt; + * if the oldest attempt (highest bit in bitmap) shows "success", + * subtract "1" from the success counter (this is the main reason + * we keep these bitmaps!). + */ + while (attempts > 0) { + if (window->counter >= IWL_RATE_MAX_WINDOW) { + + /* remove earliest */ + window->counter = IWL_RATE_MAX_WINDOW - 1; + + if (window->data & mask) { + window->data &= ~mask; + window->success_counter--; + } + } + + /* Increment frames-attempted counter */ + window->counter++; + + /* Shift bitmap by one frame to throw away oldest history */ + window->data <<= 1; + + /* Mark the most recent #successes attempts as successful */ + if (successes > 0) { + window->success_counter++; + window->data |= 0x1; + successes--; + } + + attempts--; + } + + /* Calculate current success ratio, avoid divide-by-0! */ + if (window->counter > 0) + window->success_ratio = 128 * (100 * window->success_counter) + / window->counter; + else + window->success_ratio = IWL_INVALID_VALUE; + + fail_count = window->counter - window->success_counter; + + /* Calculate average throughput, if we have enough history. */ + if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || + (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) + window->average_tpt = (window->success_ratio * tpt + 64) / 128; + else + window->average_tpt = IWL_INVALID_VALUE; + + /* Tag this window as having been updated */ + window->stamp = jiffies; + + return 0; +} + +/* + * Fill uCode API rate_n_flags field, based on "search" or "active" table. + */ +/* FIXME:RS:remove this function and put the flags statically in the table */ +static u32 rate_n_flags_from_tbl(struct iwl_priv *priv, + struct iwl_scale_tbl_info *tbl, + int index, u8 use_green) +{ + u32 rate_n_flags = 0; + + if (is_legacy(tbl->lq_type)) { + rate_n_flags = iwl_rates[index].plcp; + if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) + rate_n_flags |= RATE_MCS_CCK_MSK; + + } else if (is_Ht(tbl->lq_type)) { + if (index > IWL_LAST_OFDM_RATE) { + IWL_ERR(priv, "Invalid HT rate index %d\n", index); + index = IWL_LAST_OFDM_RATE; + } + rate_n_flags = RATE_MCS_HT_MSK; + + if (is_siso(tbl->lq_type)) + rate_n_flags |= iwl_rates[index].plcp_siso; + else if (is_mimo2(tbl->lq_type)) + rate_n_flags |= iwl_rates[index].plcp_mimo2; + else + rate_n_flags |= iwl_rates[index].plcp_mimo3; + } else { + IWL_ERR(priv, "Invalid tbl->lq_type %d\n", tbl->lq_type); + } + + rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & + RATE_MCS_ANT_ABC_MSK); + + if (is_Ht(tbl->lq_type)) { + if (tbl->is_ht40) { + if (tbl->is_dup) + rate_n_flags |= RATE_MCS_DUP_MSK; + else + rate_n_flags |= RATE_MCS_HT40_MSK; + } + if (tbl->is_SGI) + rate_n_flags |= RATE_MCS_SGI_MSK; + + if (use_green) { + rate_n_flags |= RATE_MCS_GF_MSK; + if (is_siso(tbl->lq_type) && tbl->is_SGI) { + rate_n_flags &= ~RATE_MCS_SGI_MSK; + IWL_ERR(priv, "GF was set with SGI:SISO\n"); + } + } + } + return rate_n_flags; +} + +/* + * Interpret uCode API's rate_n_flags format, + * fill "search" or "active" tx mode table. + */ +static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, + enum ieee80211_band band, + struct iwl_scale_tbl_info *tbl, + int *rate_idx) +{ + u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); + u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); + u8 mcs; + + memset(tbl, 0, sizeof(struct iwl_scale_tbl_info)); + *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); + + if (*rate_idx == IWL_RATE_INVALID) { + *rate_idx = -1; + return -EINVAL; + } + tbl->is_SGI = 0; /* default legacy setup */ + tbl->is_ht40 = 0; + tbl->is_dup = 0; + tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); + tbl->lq_type = LQ_NONE; + tbl->max_search = IWL_MAX_SEARCH; + + /* legacy rate format */ + if (!(rate_n_flags & RATE_MCS_HT_MSK)) { + if (num_of_ant == 1) { + if (band == IEEE80211_BAND_5GHZ) + tbl->lq_type = LQ_A; + else + tbl->lq_type = LQ_G; + } + /* HT rate format */ + } else { + if (rate_n_flags & RATE_MCS_SGI_MSK) + tbl->is_SGI = 1; + + if ((rate_n_flags & RATE_MCS_HT40_MSK) || + (rate_n_flags & RATE_MCS_DUP_MSK)) + tbl->is_ht40 = 1; + + if (rate_n_flags & RATE_MCS_DUP_MSK) + tbl->is_dup = 1; + + mcs = rs_extract_rate(rate_n_flags); + + /* SISO */ + if (mcs <= IWL_RATE_SISO_60M_PLCP) { + if (num_of_ant == 1) + tbl->lq_type = LQ_SISO; /*else NONE*/ + /* MIMO2 */ + } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) { + if (num_of_ant == 2) + tbl->lq_type = LQ_MIMO2; + /* MIMO3 */ + } else { + if (num_of_ant == 3) { + tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; + tbl->lq_type = LQ_MIMO3; + } + } + } + return 0; +} + +/* switch to another antenna/antennas and return 1 */ +/* if no other valid antenna found, return 0 */ +static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, + struct iwl_scale_tbl_info *tbl) +{ + u8 new_ant_type; + + if (!tbl->ant_type || tbl->ant_type > ANT_ABC) + return 0; + + if (!rs_is_valid_ant(valid_ant, tbl->ant_type)) + return 0; + + new_ant_type = ant_toggle_lookup[tbl->ant_type]; + + while ((new_ant_type != tbl->ant_type) && + !rs_is_valid_ant(valid_ant, new_ant_type)) + new_ant_type = ant_toggle_lookup[new_ant_type]; + + if (new_ant_type == tbl->ant_type) + return 0; + + tbl->ant_type = new_ant_type; + *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; + *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; + return 1; +} + +/** + * Green-field mode is valid if the station supports it and + * there are no non-GF stations present in the BSS. + */ +static bool rs_use_green(struct ieee80211_sta *sta) +{ + /* + * There's a bug somewhere in this code that causes the + * scaling to get stuck because GF+SGI can't be combined + * in SISO rates. Until we find that bug, disable GF, it + * has only limited benefit and we still interoperate with + * GF APs since we can always receive GF transmissions. + */ + return false; +} + +/** + * rs_get_supported_rates - get the available rates + * + * if management frame or broadcast frame only return + * basic available rates. + * + */ +static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, + struct ieee80211_hdr *hdr, + enum iwl_table_type rate_type) +{ + if (is_legacy(rate_type)) { + return lq_sta->active_legacy_rate; + } else { + if (is_siso(rate_type)) + return lq_sta->active_siso_rate; + else if (is_mimo2(rate_type)) + return lq_sta->active_mimo2_rate; + else + return lq_sta->active_mimo3_rate; + } +} + +static u16 rs_get_adjacent_rate(struct iwl_priv *priv, u8 index, u16 rate_mask, + int rate_type) +{ + u8 high = IWL_RATE_INVALID; + u8 low = IWL_RATE_INVALID; + + /* 802.11A or ht walks to the next literal adjacent rate in + * the rate table */ + if (is_a_band(rate_type) || !is_legacy(rate_type)) { + int i; + u32 mask; + + /* Find the previous rate that is in the rate mask */ + i = index - 1; + for (mask = (1 << i); i >= 0; i--, mask >>= 1) { + if (rate_mask & mask) { + low = i; + break; + } + } + + /* Find the next rate that is in the rate mask */ + i = index + 1; + for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { + if (rate_mask & mask) { + high = i; + break; + } + } + + return (high << 8) | low; + } + + low = index; + while (low != IWL_RATE_INVALID) { + low = iwl_rates[low].prev_rs; + if (low == IWL_RATE_INVALID) + break; + if (rate_mask & (1 << low)) + break; + IWL_DEBUG_RATE(priv, "Skipping masked lower rate: %d\n", low); + } + + high = index; + while (high != IWL_RATE_INVALID) { + high = iwl_rates[high].next_rs; + if (high == IWL_RATE_INVALID) + break; + if (rate_mask & (1 << high)) + break; + IWL_DEBUG_RATE(priv, "Skipping masked higher rate: %d\n", high); + } + + return (high << 8) | low; +} + +static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + u8 scale_index, u8 ht_possible) +{ + s32 low; + u16 rate_mask; + u16 high_low; + u8 switch_to_legacy = 0; + u8 is_green = lq_sta->is_green; + struct iwl_priv *priv = lq_sta->drv; + + /* check if we need to switch from HT to legacy rates. + * assumption is that mandatory rates (1Mbps or 6Mbps) + * are always supported (spec demand) */ + if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { + switch_to_legacy = 1; + scale_index = rs_ht_to_legacy[scale_index]; + if (lq_sta->band == IEEE80211_BAND_5GHZ) + tbl->lq_type = LQ_A; + else + tbl->lq_type = LQ_G; + + if (num_of_ant(tbl->ant_type) > 1) + tbl->ant_type = + first_antenna(priv->nvm_data->valid_tx_ant); + + tbl->is_ht40 = 0; + tbl->is_SGI = 0; + tbl->max_search = IWL_MAX_SEARCH; + } + + rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); + + /* Mask with station rate restriction */ + if (is_legacy(tbl->lq_type)) { + /* supp_rates has no CCK bits in A mode */ + if (lq_sta->band == IEEE80211_BAND_5GHZ) + rate_mask = (u16)(rate_mask & + (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); + else + rate_mask = (u16)(rate_mask & lq_sta->supp_rates); + } + + /* If we switched from HT to legacy, check current rate */ + if (switch_to_legacy && (rate_mask & (1 << scale_index))) { + low = scale_index; + goto out; + } + + high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask, + tbl->lq_type); + low = high_low & 0xff; + + if (low == IWL_RATE_INVALID) + low = scale_index; + +out: + return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); +} + +/* + * Simple function to compare two rate scale table types + */ +static bool table_type_matches(struct iwl_scale_tbl_info *a, + struct iwl_scale_tbl_info *b) +{ + return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) && + (a->is_SGI == b->is_SGI); +} + +static void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct iwl_lq_sta *lq_sta) +{ + struct iwl_scale_tbl_info *tbl; + bool full_concurrent = priv->bt_full_concurrent; + + if (priv->bt_ant_couple_ok) { + /* + * Is there a need to switch between + * full concurrency and 3-wire? + */ + if (priv->bt_ci_compliance && priv->bt_ant_couple_ok) + full_concurrent = true; + else + full_concurrent = false; + } + if ((priv->bt_traffic_load != priv->last_bt_traffic_load) || + (priv->bt_full_concurrent != full_concurrent)) { + priv->bt_full_concurrent = full_concurrent; + priv->last_bt_traffic_load = priv->bt_traffic_load; + + /* Update uCode's rate table. */ + tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); + + queue_work(priv->workqueue, &priv->bt_full_concurrency); + } +} + +/* + * mac80211 sends us Tx status + */ +static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta, + struct sk_buff *skb) +{ + int legacy_success; + int retries; + int rs_index, mac_index, i; + struct iwl_lq_sta *lq_sta = priv_sta; + struct iwl_link_quality_cmd *table; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct iwl_op_mode *op_mode = (struct iwl_op_mode *)priv_r; + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + enum mac80211_rate_control_flags mac_flags; + u32 tx_rate; + struct iwl_scale_tbl_info tbl_type; + struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->ctx; + + IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n"); + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (!lq_sta) { + IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n"); + return; + } else if (!lq_sta->drv) { + IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); + return; + } + + if (!ieee80211_is_data(hdr->frame_control) || + info->flags & IEEE80211_TX_CTL_NO_ACK) + return; + + /* This packet was aggregated but doesn't carry status info */ + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && + !(info->flags & IEEE80211_TX_STAT_AMPDU)) + return; + + /* + * Ignore this Tx frame response if its initial rate doesn't match + * that of latest Link Quality command. There may be stragglers + * from a previous Link Quality command, but we're no longer interested + * in those; they're either from the "active" mode while we're trying + * to check "search" mode, or a prior "search" mode after we've moved + * to a new "search" mode (which might become the new "active" mode). + */ + table = &lq_sta->lq; + tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); + rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); + if (priv->band == IEEE80211_BAND_5GHZ) + rs_index -= IWL_FIRST_OFDM_RATE; + mac_flags = info->status.rates[0].flags; + mac_index = info->status.rates[0].idx; + /* For HT packets, map MCS to PLCP */ + if (mac_flags & IEEE80211_TX_RC_MCS) { + mac_index &= RATE_MCS_CODE_MSK; /* Remove # of streams */ + if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE)) + mac_index++; + /* + * mac80211 HT index is always zero-indexed; we need to move + * HT OFDM rates after CCK rates in 2.4 GHz band + */ + if (priv->band == IEEE80211_BAND_2GHZ) + mac_index += IWL_FIRST_OFDM_RATE; + } + /* Here we actually compare this rate to the latest LQ command */ + if ((mac_index < 0) || + (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || + (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || + (tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA)) || + (tbl_type.ant_type != info->status.antenna) || + (!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)) || + (!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || + (rs_index != mac_index)) { + IWL_DEBUG_RATE(priv, "initial rate %d does not match %d (0x%x)\n", mac_index, rs_index, tx_rate); + /* + * Since rates mis-match, the last LQ command may have failed. + * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with + * ... driver. + */ + lq_sta->missed_rate_counter++; + if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { + lq_sta->missed_rate_counter = 0; + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); + } + /* Regardless, ignore this status info for outdated rate */ + return; + } else + /* Rate did match, so reset the missed_rate_counter */ + lq_sta->missed_rate_counter = 0; + + /* Figure out if rate scale algorithm is in active or search table */ + if (table_type_matches(&tbl_type, + &(lq_sta->lq_info[lq_sta->active_tbl]))) { + curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + } else if (table_type_matches(&tbl_type, + &lq_sta->lq_info[1 - lq_sta->active_tbl])) { + curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + } else { + IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n"); + tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + IWL_DEBUG_RATE(priv, "active- lq:%x, ant:%x, SGI:%d\n", + tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); + tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + IWL_DEBUG_RATE(priv, "search- lq:%x, ant:%x, SGI:%d\n", + tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); + IWL_DEBUG_RATE(priv, "actual- lq:%x, ant:%x, SGI:%d\n", + tbl_type.lq_type, tbl_type.ant_type, tbl_type.is_SGI); + /* + * no matching table found, let's by-pass the data collection + * and continue to perform rate scale to find the rate table + */ + rs_stay_in_table(lq_sta, true); + goto done; + } + + /* + * Updating the frame history depends on whether packets were + * aggregated. + * + * For aggregation, all packets were transmitted at the same rate, the + * first index into rate scale table. + */ + if (info->flags & IEEE80211_TX_STAT_AMPDU) { + tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); + rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, + &rs_index); + rs_collect_tx_data(curr_tbl, rs_index, + info->status.ampdu_len, + info->status.ampdu_ack_len); + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->stay_in_tbl) { + lq_sta->total_success += info->status.ampdu_ack_len; + lq_sta->total_failed += (info->status.ampdu_len - + info->status.ampdu_ack_len); + } + } else { + /* + * For legacy, update frame history with for each Tx retry. + */ + retries = info->status.rates[0].count - 1; + /* HW doesn't send more than 15 retries */ + retries = min(retries, 15); + + /* The last transmission may have been successful */ + legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); + /* Collect data for each rate used during failed TX attempts */ + for (i = 0; i <= retries; ++i) { + tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags); + rs_get_tbl_info_from_mcs(tx_rate, priv->band, + &tbl_type, &rs_index); + /* + * Only collect stats if retried rate is in the same RS + * table as active/search. + */ + if (table_type_matches(&tbl_type, curr_tbl)) + tmp_tbl = curr_tbl; + else if (table_type_matches(&tbl_type, other_tbl)) + tmp_tbl = other_tbl; + else + continue; + rs_collect_tx_data(tmp_tbl, rs_index, 1, + i < retries ? 0 : legacy_success); + } + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->stay_in_tbl) { + lq_sta->total_success += legacy_success; + lq_sta->total_failed += retries + (1 - legacy_success); + } + } + /* The last TX rate is cached in lq_sta; it's set in if/else above */ + lq_sta->last_rate_n_flags = tx_rate; +done: + /* See if there's a better rate or modulation mode to try. */ + if (sta && sta->supp_rates[sband->band]) + rs_rate_scale_perform(priv, skb, sta, lq_sta); + + if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist) + rs_bt_update_lq(priv, ctx, lq_sta); +} + +/* + * Begin a period of staying with a selected modulation mode. + * Set "stay_in_tbl" flag to prevent any mode switches. + * Set frame tx success limits according to legacy vs. high-throughput, + * and reset overall (spanning all rates) tx success history statistics. + * These control how long we stay using same modulation mode before + * searching for a new mode. + */ +static void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy, + struct iwl_lq_sta *lq_sta) +{ + IWL_DEBUG_RATE(priv, "we are staying in the same table\n"); + lq_sta->stay_in_tbl = 1; /* only place this gets set */ + if (is_legacy) { + lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; + } else { + lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT; + } + lq_sta->table_count = 0; + lq_sta->total_failed = 0; + lq_sta->total_success = 0; + lq_sta->flush_timer = jiffies; + lq_sta->action_counter = 0; +} + +/* + * Find correct throughput table for given mode of modulation + */ +static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl) +{ + /* Used to choose among HT tables */ + const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT]; + + /* Check for invalid LQ type */ + if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { + tbl->expected_tpt = expected_tpt_legacy; + return; + } + + /* Legacy rates have only one table */ + if (is_legacy(tbl->lq_type)) { + tbl->expected_tpt = expected_tpt_legacy; + return; + } + + /* Choose among many HT tables depending on number of streams + * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation + * status */ + if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_siso20MHz; + else if (is_siso(tbl->lq_type)) + ht_tbl_pointer = expected_tpt_siso40MHz; + else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_mimo2_20MHz; + else if (is_mimo2(tbl->lq_type)) + ht_tbl_pointer = expected_tpt_mimo2_40MHz; + else if (is_mimo3(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_mimo3_20MHz; + else /* if (is_mimo3(tbl->lq_type)) <-- must be true */ + ht_tbl_pointer = expected_tpt_mimo3_40MHz; + + if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ + tbl->expected_tpt = ht_tbl_pointer[0]; + else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ + tbl->expected_tpt = ht_tbl_pointer[1]; + else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ + tbl->expected_tpt = ht_tbl_pointer[2]; + else /* AGG+SGI */ + tbl->expected_tpt = ht_tbl_pointer[3]; +} + +/* + * Find starting rate for new "search" high-throughput mode of modulation. + * Goal is to find lowest expected rate (under perfect conditions) that is + * above the current measured throughput of "active" mode, to give new mode + * a fair chance to prove itself without too many challenges. + * + * This gets called when transitioning to more aggressive modulation + * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive + * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need + * to decrease to match "active" throughput. When moving from MIMO to SISO, + * bit rate will typically need to increase, but not if performance was bad. + */ +static s32 rs_get_best_rate(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, /* "search" */ + u16 rate_mask, s8 index) +{ + /* "active" values */ + struct iwl_scale_tbl_info *active_tbl = + &(lq_sta->lq_info[lq_sta->active_tbl]); + s32 active_sr = active_tbl->win[index].success_ratio; + s32 active_tpt = active_tbl->expected_tpt[index]; + /* expected "search" throughput */ + const u16 *tpt_tbl = tbl->expected_tpt; + + s32 new_rate, high, low, start_hi; + u16 high_low; + s8 rate = index; + + new_rate = high = low = start_hi = IWL_RATE_INVALID; + + for (; ;) { + high_low = rs_get_adjacent_rate(priv, rate, rate_mask, + tbl->lq_type); + + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + /* + * Lower the "search" bit rate, to give new "search" mode + * approximately the same throughput as "active" if: + * + * 1) "Active" mode has been working modestly well (but not + * great), and expected "search" throughput (under perfect + * conditions) at candidate rate is above the actual + * measured "active" throughput (but less than expected + * "active" throughput under perfect conditions). + * OR + * 2) "Active" mode has been working perfectly or very well + * and expected "search" throughput (under perfect + * conditions) at candidate rate is above expected + * "active" throughput (under perfect conditions). + */ + if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && + ((active_sr > IWL_RATE_DECREASE_TH) && + (active_sr <= IWL_RATE_HIGH_TH) && + (tpt_tbl[rate] <= active_tpt))) || + ((active_sr >= IWL_RATE_SCALE_SWITCH) && + (tpt_tbl[rate] > active_tpt))) { + + /* (2nd or later pass) + * If we've already tried to raise the rate, and are + * now trying to lower it, use the higher rate. */ + if (start_hi != IWL_RATE_INVALID) { + new_rate = start_hi; + break; + } + + new_rate = rate; + + /* Loop again with lower rate */ + if (low != IWL_RATE_INVALID) + rate = low; + + /* Lower rate not available, use the original */ + else + break; + + /* Else try to raise the "search" rate to match "active" */ + } else { + /* (2nd or later pass) + * If we've already tried to lower the rate, and are + * now trying to raise it, use the lower rate. */ + if (new_rate != IWL_RATE_INVALID) + break; + + /* Loop again with higher rate */ + else if (high != IWL_RATE_INVALID) { + start_hi = high; + rate = high; + + /* Higher rate not available, use the original */ + } else { + new_rate = rate; + break; + } + } + } + + return new_rate; +} + +/* + * Set up search table for MIMO2 + */ +static int rs_switch_to_mimo2(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl, int index) +{ + u16 rate_mask; + s32 rate; + s8 is_green = lq_sta->is_green; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->ctx; + + if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) + return -1; + + if (sta->smps_mode == IEEE80211_SMPS_STATIC) + return -1; + + /* Need both Tx chains/antennas to support MIMO */ + if (priv->hw_params.tx_chains_num < 2) + return -1; + + IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO2\n"); + + tbl->lq_type = LQ_MIMO2; + tbl->is_dup = lq_sta->is_dup; + tbl->action = 0; + tbl->max_search = IWL_MAX_SEARCH; + rate_mask = lq_sta->active_mimo2_rate; + + if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) + tbl->is_ht40 = 1; + else + tbl->is_ht40 = 0; + + rs_set_expected_tpt_table(lq_sta, tbl); + + rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); + + IWL_DEBUG_RATE(priv, "LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask); + if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { + IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", + rate, rate_mask); + return -1; + } + tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); + + IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", + tbl->current_rate, is_green); + return 0; +} + +/* + * Set up search table for MIMO3 + */ +static int rs_switch_to_mimo3(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl, int index) +{ + u16 rate_mask; + s32 rate; + s8 is_green = lq_sta->is_green; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->ctx; + + if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) + return -1; + + if (sta->smps_mode == IEEE80211_SMPS_STATIC) + return -1; + + /* Need both Tx chains/antennas to support MIMO */ + if (priv->hw_params.tx_chains_num < 3) + return -1; + + IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO3\n"); + + tbl->lq_type = LQ_MIMO3; + tbl->is_dup = lq_sta->is_dup; + tbl->action = 0; + tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; + rate_mask = lq_sta->active_mimo3_rate; + + if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) + tbl->is_ht40 = 1; + else + tbl->is_ht40 = 0; + + rs_set_expected_tpt_table(lq_sta, tbl); + + rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); + + IWL_DEBUG_RATE(priv, "LQ: MIMO3 best rate %d mask %X\n", + rate, rate_mask); + if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { + IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", + rate, rate_mask); + return -1; + } + tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); + + IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", + tbl->current_rate, is_green); + return 0; +} + +/* + * Set up search table for SISO + */ +static int rs_switch_to_siso(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl, int index) +{ + u16 rate_mask; + u8 is_green = lq_sta->is_green; + s32 rate; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->ctx; + + if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) + return -1; + + IWL_DEBUG_RATE(priv, "LQ: try to switch to SISO\n"); + + tbl->is_dup = lq_sta->is_dup; + tbl->lq_type = LQ_SISO; + tbl->action = 0; + tbl->max_search = IWL_MAX_SEARCH; + rate_mask = lq_sta->active_siso_rate; + + if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) + tbl->is_ht40 = 1; + else + tbl->is_ht40 = 0; + + if (is_green) + tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ + + rs_set_expected_tpt_table(lq_sta, tbl); + rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); + + IWL_DEBUG_RATE(priv, "LQ: get best rate %d mask %X\n", rate, rate_mask); + if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { + IWL_DEBUG_RATE(priv, "can not switch with index %d rate mask %x\n", + rate, rate_mask); + return -1; + } + tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); + IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", + tbl->current_rate, is_green); + return 0; +} + +/* + * Try to switch to new modulation mode from legacy + */ +static void rs_move_legacy_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + int index) +{ + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + int ret = 0; + u8 update_search_tbl_counter = 0; + + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + /* nothing */ + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + /* avoid antenna B unless MIMO */ + if (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2) + tbl->action = IWL_LEGACY_SWITCH_SISO; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + /* avoid antenna B and MIMO */ + valid_tx_ant = + first_antenna(priv->nvm_data->valid_tx_ant); + if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2 && + tbl->action != IWL_LEGACY_SWITCH_SISO) + tbl->action = IWL_LEGACY_SWITCH_SISO; + break; + default: + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); + break; + } + + if (!iwl_ht_enabled(priv)) + /* stay in Legacy */ + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && + tbl->action > IWL_LEGACY_SWITCH_SISO) + tbl->action = IWL_LEGACY_SWITCH_SISO; + + /* configure as 1x1 if bt full concurrency */ + if (priv->bt_full_concurrent) { + if (!iwl_ht_enabled(priv)) + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + else if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) + tbl->action = IWL_LEGACY_SWITCH_SISO; + valid_tx_ant = + first_antenna(priv->nvm_data->valid_tx_ant); + } + + start_action = tbl->action; + for (; ;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_LEGACY_SWITCH_ANTENNA1: + case IWL_LEGACY_SWITCH_ANTENNA2: + IWL_DEBUG_RATE(priv, "LQ: Legacy toggle Antenna\n"); + + if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 && + tx_chains_num <= 1) || + (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 && + tx_chains_num <= 2)) + break; + + /* Don't change antenna if success has been great */ + if (window->success_ratio >= IWL_RS_GOOD_RATIO && + !priv->bt_full_concurrent && + priv->bt_traffic_load == + IWL_BT_COEX_TRAFFIC_LOAD_NONE) + break; + + /* Set up search table to try other antenna */ + memcpy(search_tbl, tbl, sz); + + if (rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) { + update_search_tbl_counter = 1; + rs_set_expected_tpt_table(lq_sta, search_tbl); + goto out; + } + break; + case IWL_LEGACY_SWITCH_SISO: + IWL_DEBUG_RATE(priv, "LQ: Legacy switch to SISO\n"); + + /* Set up search table to try SISO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + ret = rs_switch_to_siso(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->action_counter = 0; + goto out; + } + + break; + case IWL_LEGACY_SWITCH_MIMO2_AB: + case IWL_LEGACY_SWITCH_MIMO2_AC: + case IWL_LEGACY_SWITCH_MIMO2_BC: + IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO2\n"); + + /* Set up search table to try MIMO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + + if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB) + search_tbl->ant_type = ANT_AB; + else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC) + search_tbl->ant_type = ANT_AC; + else + search_tbl->ant_type = ANT_BC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->action_counter = 0; + goto out; + } + break; + + case IWL_LEGACY_SWITCH_MIMO3_ABC: + IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO3\n"); + + /* Set up search table to try MIMO3 */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + + search_tbl->ant_type = ANT_ABC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->action_counter = 0; + goto out; + } + break; + } + tbl->action++; + if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + + } + search_tbl->lq_type = LQ_NONE; + return; + +out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; +} + +/* + * Try to switch to new modulation mode from SISO + */ +static void rs_move_siso_to_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, int index) +{ + u8 is_green = lq_sta->is_green; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + u8 update_search_tbl_counter = 0; + int ret; + + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + /* nothing */ + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + /* avoid antenna B unless MIMO */ + if (tbl->action == IWL_SISO_SWITCH_ANTENNA2) + tbl->action = IWL_SISO_SWITCH_MIMO2_AB; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + /* avoid antenna B and MIMO */ + valid_tx_ant = + first_antenna(priv->nvm_data->valid_tx_ant); + if (tbl->action != IWL_SISO_SWITCH_ANTENNA1) + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + break; + default: + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); + break; + } + + if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && + tbl->action > IWL_SISO_SWITCH_ANTENNA2) { + /* stay in SISO */ + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + } + + /* configure as 1x1 if bt full concurrency */ + if (priv->bt_full_concurrent) { + valid_tx_ant = + first_antenna(priv->nvm_data->valid_tx_ant); + if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + } + + start_action = tbl->action; + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_SISO_SWITCH_ANTENNA1: + case IWL_SISO_SWITCH_ANTENNA2: + IWL_DEBUG_RATE(priv, "LQ: SISO toggle Antenna\n"); + if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 && + tx_chains_num <= 1) || + (tbl->action == IWL_SISO_SWITCH_ANTENNA2 && + tx_chains_num <= 2)) + break; + + if (window->success_ratio >= IWL_RS_GOOD_RATIO && + !priv->bt_full_concurrent && + priv->bt_traffic_load == + IWL_BT_COEX_TRAFFIC_LOAD_NONE) + break; + + memcpy(search_tbl, tbl, sz); + if (rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) { + update_search_tbl_counter = 1; + goto out; + } + break; + case IWL_SISO_SWITCH_MIMO2_AB: + case IWL_SISO_SWITCH_MIMO2_AC: + case IWL_SISO_SWITCH_MIMO2_BC: + IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO2\n"); + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + + if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB) + search_tbl->ant_type = ANT_AB; + else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC) + search_tbl->ant_type = ANT_AC; + else + search_tbl->ant_type = ANT_BC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + break; + case IWL_SISO_SWITCH_GI: + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + break; + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + break; + + IWL_DEBUG_RATE(priv, "LQ: SISO toggle SGI/NGI\n"); + + memcpy(search_tbl, tbl, sz); + if (is_green) { + if (!tbl->is_SGI) + break; + else + IWL_ERR(priv, + "SGI was set in GF+SISO\n"); + } + search_tbl->is_SGI = !tbl->is_SGI; + rs_set_expected_tpt_table(lq_sta, search_tbl); + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[index]) + break; + } + search_tbl->current_rate = + rate_n_flags_from_tbl(priv, search_tbl, + index, is_green); + update_search_tbl_counter = 1; + goto out; + case IWL_SISO_SWITCH_MIMO3_ABC: + IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO3\n"); + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + search_tbl->ant_type = ANT_ABC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + break; + } + tbl->action++; + if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + } + search_tbl->lq_type = LQ_NONE; + return; + + out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC) + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; +} + +/* + * Try to switch to new modulation mode from MIMO2 + */ +static void rs_move_mimo2_to_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, int index) +{ + s8 is_green = lq_sta->is_green; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + u8 update_search_tbl_counter = 0; + int ret; + + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + /* nothing */ + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + /* avoid antenna B and MIMO */ + if (tbl->action != IWL_MIMO2_SWITCH_SISO_A) + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + /* avoid antenna B unless MIMO */ + if (tbl->action == IWL_MIMO2_SWITCH_SISO_B || + tbl->action == IWL_MIMO2_SWITCH_SISO_C) + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + break; + default: + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); + break; + } + + if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && + (tbl->action < IWL_MIMO2_SWITCH_SISO_A || + tbl->action > IWL_MIMO2_SWITCH_SISO_C)) { + /* switch in SISO */ + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + } + + /* configure as 1x1 if bt full concurrency */ + if (priv->bt_full_concurrent && + (tbl->action < IWL_MIMO2_SWITCH_SISO_A || + tbl->action > IWL_MIMO2_SWITCH_SISO_C)) + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + + start_action = tbl->action; + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_MIMO2_SWITCH_ANTENNA1: + case IWL_MIMO2_SWITCH_ANTENNA2: + IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle Antennas\n"); + + if (tx_chains_num <= 2) + break; + + if (window->success_ratio >= IWL_RS_GOOD_RATIO) + break; + + memcpy(search_tbl, tbl, sz); + if (rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) { + update_search_tbl_counter = 1; + goto out; + } + break; + case IWL_MIMO2_SWITCH_SISO_A: + case IWL_MIMO2_SWITCH_SISO_B: + case IWL_MIMO2_SWITCH_SISO_C: + IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to SISO\n"); + + /* Set up new search table for SISO */ + memcpy(search_tbl, tbl, sz); + + if (tbl->action == IWL_MIMO2_SWITCH_SISO_A) + search_tbl->ant_type = ANT_A; + else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B) + search_tbl->ant_type = ANT_B; + else + search_tbl->ant_type = ANT_C; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_siso(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + + break; + + case IWL_MIMO2_SWITCH_GI: + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + break; + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + break; + + IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle SGI/NGI\n"); + + /* Set up new search table for MIMO2 */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = !tbl->is_SGI; + rs_set_expected_tpt_table(lq_sta, search_tbl); + /* + * If active table already uses the fastest possible + * modulation (dual stream with short guard interval), + * and it's working well, there's no need to look + * for a better type of modulation! + */ + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[index]) + break; + } + search_tbl->current_rate = + rate_n_flags_from_tbl(priv, search_tbl, + index, is_green); + update_search_tbl_counter = 1; + goto out; + + case IWL_MIMO2_SWITCH_MIMO3_ABC: + IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to MIMO3\n"); + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + search_tbl->ant_type = ANT_ABC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + + break; + } + tbl->action++; + if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) + tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + } + search_tbl->lq_type = LQ_NONE; + return; + out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) + tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; + +} + +/* + * Try to switch to new modulation mode from MIMO3 + */ +static void rs_move_mimo3_to_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, int index) +{ + s8 is_green = lq_sta->is_green; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + int ret; + u8 update_search_tbl_counter = 0; + + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + /* nothing */ + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + /* avoid antenna B and MIMO */ + if (tbl->action != IWL_MIMO3_SWITCH_SISO_A) + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + /* avoid antenna B unless MIMO */ + if (tbl->action == IWL_MIMO3_SWITCH_SISO_B || + tbl->action == IWL_MIMO3_SWITCH_SISO_C) + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + break; + default: + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); + break; + } + + if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && + (tbl->action < IWL_MIMO3_SWITCH_SISO_A || + tbl->action > IWL_MIMO3_SWITCH_SISO_C)) { + /* switch in SISO */ + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + } + + /* configure as 1x1 if bt full concurrency */ + if (priv->bt_full_concurrent && + (tbl->action < IWL_MIMO3_SWITCH_SISO_A || + tbl->action > IWL_MIMO3_SWITCH_SISO_C)) + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + + start_action = tbl->action; + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_MIMO3_SWITCH_ANTENNA1: + case IWL_MIMO3_SWITCH_ANTENNA2: + IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle Antennas\n"); + + if (tx_chains_num <= 3) + break; + + if (window->success_ratio >= IWL_RS_GOOD_RATIO) + break; + + memcpy(search_tbl, tbl, sz); + if (rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) + goto out; + break; + case IWL_MIMO3_SWITCH_SISO_A: + case IWL_MIMO3_SWITCH_SISO_B: + case IWL_MIMO3_SWITCH_SISO_C: + IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to SISO\n"); + + /* Set up new search table for SISO */ + memcpy(search_tbl, tbl, sz); + + if (tbl->action == IWL_MIMO3_SWITCH_SISO_A) + search_tbl->ant_type = ANT_A; + else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B) + search_tbl->ant_type = ANT_B; + else + search_tbl->ant_type = ANT_C; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_siso(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + + break; + + case IWL_MIMO3_SWITCH_MIMO2_AB: + case IWL_MIMO3_SWITCH_MIMO2_AC: + case IWL_MIMO3_SWITCH_MIMO2_BC: + IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to MIMO2\n"); + + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB) + search_tbl->ant_type = ANT_AB; + else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC) + search_tbl->ant_type = ANT_AC; + else + search_tbl->ant_type = ANT_BC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + + break; + + case IWL_MIMO3_SWITCH_GI: + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + break; + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + break; + + IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle SGI/NGI\n"); + + /* Set up new search table for MIMO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = !tbl->is_SGI; + rs_set_expected_tpt_table(lq_sta, search_tbl); + /* + * If active table already uses the fastest possible + * modulation (dual stream with short guard interval), + * and it's working well, there's no need to look + * for a better type of modulation! + */ + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[index]) + break; + } + search_tbl->current_rate = + rate_n_flags_from_tbl(priv, search_tbl, + index, is_green); + update_search_tbl_counter = 1; + goto out; + } + tbl->action++; + if (tbl->action > IWL_MIMO3_SWITCH_GI) + tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + } + search_tbl->lq_type = LQ_NONE; + return; + out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IWL_MIMO3_SWITCH_GI) + tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; +} + +/* + * Check whether we should continue using same modulation mode, or + * begin search for a new mode, based on: + * 1) # tx successes or failures while using this mode + * 2) # times calling this function + * 3) elapsed time in this mode (not used, for now) + */ +static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) +{ + struct iwl_scale_tbl_info *tbl; + int i; + int active_tbl; + int flush_interval_passed = 0; + struct iwl_priv *priv; + + priv = lq_sta->drv; + active_tbl = lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + + /* If we've been disallowing search, see if we should now allow it */ + if (lq_sta->stay_in_tbl) { + + /* Elapsed time using current modulation mode */ + if (lq_sta->flush_timer) + flush_interval_passed = + time_after(jiffies, + (unsigned long)(lq_sta->flush_timer + + IWL_RATE_SCALE_FLUSH_INTVL)); + + /* + * Check if we should allow search for new modulation mode. + * If many frames have failed or succeeded, or we've used + * this same modulation for a long time, allow search, and + * reset history stats that keep track of whether we should + * allow a new search. Also (below) reset all bitmaps and + * stats in active history. + */ + if (force_search || + (lq_sta->total_failed > lq_sta->max_failure_limit) || + (lq_sta->total_success > lq_sta->max_success_limit) || + ((!lq_sta->search_better_tbl) && (lq_sta->flush_timer) + && (flush_interval_passed))) { + IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n", + lq_sta->total_failed, + lq_sta->total_success, + flush_interval_passed); + + /* Allow search for new mode */ + lq_sta->stay_in_tbl = 0; /* only place reset */ + lq_sta->total_failed = 0; + lq_sta->total_success = 0; + lq_sta->flush_timer = 0; + + /* + * Else if we've used this modulation mode enough repetitions + * (regardless of elapsed time or success/failure), reset + * history bitmaps and rate-specific stats for all rates in + * active table. + */ + } else { + lq_sta->table_count++; + if (lq_sta->table_count >= + lq_sta->table_count_limit) { + lq_sta->table_count = 0; + + IWL_DEBUG_RATE(priv, "LQ: stay in table clear win\n"); + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window( + &(tbl->win[i])); + } + } + + /* If transitioning to allow "search", reset all history + * bitmaps and stats in active table (this will become the new + * "search" table). */ + if (!lq_sta->stay_in_tbl) { + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&(tbl->win[i])); + } + } +} + +/* + * setup rate table in uCode + */ +static void rs_update_rate_tbl(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + int index, u8 is_green) +{ + u32 rate; + + /* Update uCode's rate table. */ + rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); + rs_fill_link_cmd(priv, lq_sta, rate); + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); +} + +/* + * Do rate scaling and search for new modulation mode. + */ +static void rs_rate_scale_perform(struct iwl_priv *priv, + struct sk_buff *skb, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta) +{ + struct ieee80211_hw *hw = priv->hw; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + int low = IWL_RATE_INVALID; + int high = IWL_RATE_INVALID; + int index; + int i; + struct iwl_rate_scale_data *window = NULL; + int current_tpt = IWL_INVALID_VALUE; + int low_tpt = IWL_INVALID_VALUE; + int high_tpt = IWL_INVALID_VALUE; + u32 fail_count; + s8 scale_action = 0; + u16 rate_mask; + u8 update_lq = 0; + struct iwl_scale_tbl_info *tbl, *tbl1; + u16 rate_scale_index_msk = 0; + u8 is_green = 0; + u8 active_tbl = 0; + u8 done_search = 0; + u16 high_low; + s32 sr; + u8 tid = IWL_MAX_TID_COUNT; + struct iwl_tid_data *tid_data; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->ctx; + + IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n"); + + /* Send management frames and NO_ACK data using lowest rate. */ + /* TODO: this could probably be improved.. */ + if (!ieee80211_is_data(hdr->frame_control) || + info->flags & IEEE80211_TX_CTL_NO_ACK) + return; + + lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; + + tid = rs_tl_add_packet(lq_sta, hdr); + if ((tid != IWL_MAX_TID_COUNT) && + (lq_sta->tx_agg_tid_en & (1 << tid))) { + tid_data = &priv->tid_data[lq_sta->lq.sta_id][tid]; + if (tid_data->agg.state == IWL_AGG_OFF) + lq_sta->is_agg = 0; + else + lq_sta->is_agg = 1; + } else + lq_sta->is_agg = 0; + + /* + * Select rate-scale / modulation-mode table to work with in + * the rest of this function: "search" if searching for better + * modulation mode, or "active" if doing rate scaling within a mode. + */ + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + if (is_legacy(tbl->lq_type)) + lq_sta->is_green = 0; + else + lq_sta->is_green = rs_use_green(sta); + is_green = lq_sta->is_green; + + /* current tx rate */ + index = lq_sta->last_txrate_idx; + + IWL_DEBUG_RATE(priv, "Rate scale index %d for type %d\n", index, + tbl->lq_type); + + /* rates available for this association, and for modulation mode */ + rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); + + IWL_DEBUG_RATE(priv, "mask 0x%04X\n", rate_mask); + + /* mask with station rate restriction */ + if (is_legacy(tbl->lq_type)) { + if (lq_sta->band == IEEE80211_BAND_5GHZ) + /* supp_rates has no CCK bits in A mode */ + rate_scale_index_msk = (u16) (rate_mask & + (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); + else + rate_scale_index_msk = (u16) (rate_mask & + lq_sta->supp_rates); + + } else + rate_scale_index_msk = rate_mask; + + if (!rate_scale_index_msk) + rate_scale_index_msk = rate_mask; + + if (!((1 << index) & rate_scale_index_msk)) { + IWL_ERR(priv, "Current Rate is not valid\n"); + if (lq_sta->search_better_tbl) { + /* revert to active table if search table is not valid*/ + tbl->lq_type = LQ_NONE; + lq_sta->search_better_tbl = 0; + tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + /* get "active" rate info */ + index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + rs_update_rate_tbl(priv, ctx, lq_sta, tbl, + index, is_green); + } + return; + } + + /* Get expected throughput table and history window for current rate */ + if (!tbl->expected_tpt) { + IWL_ERR(priv, "tbl->expected_tpt is NULL\n"); + return; + } + + /* force user max rate if set by user */ + if ((lq_sta->max_rate_idx != -1) && + (lq_sta->max_rate_idx < index)) { + index = lq_sta->max_rate_idx; + update_lq = 1; + window = &(tbl->win[index]); + goto lq_update; + } + + window = &(tbl->win[index]); + + /* + * If there is not enough history to calculate actual average + * throughput, keep analyzing results of more tx frames, without + * changing rate or mode (bypass most of the rest of this function). + * Set up new rate table in uCode only if old rate is not supported + * in current association (use new rate found above). + */ + fail_count = window->counter - window->success_counter; + if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && + (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { + IWL_DEBUG_RATE(priv, "LQ: still below TH. succ=%d total=%d " + "for index %d\n", + window->success_counter, window->counter, index); + + /* Can't calculate this yet; not enough history */ + window->average_tpt = IWL_INVALID_VALUE; + + /* Should we stay with this modulation mode, + * or search for a new one? */ + rs_stay_in_table(lq_sta, false); + + goto out; + } + /* Else we have enough samples; calculate estimate of + * actual average throughput */ + if (window->average_tpt != ((window->success_ratio * + tbl->expected_tpt[index] + 64) / 128)) { + IWL_ERR(priv, "expected_tpt should have been calculated by now\n"); + window->average_tpt = ((window->success_ratio * + tbl->expected_tpt[index] + 64) / 128); + } + + /* If we are searching for better modulation mode, check success. */ + if (lq_sta->search_better_tbl && + (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI)) { + /* If good success, continue using the "search" mode; + * no need to send new link quality command, since we're + * continuing to use the setup that we've been trying. */ + if (window->average_tpt > lq_sta->last_tpt) { + + IWL_DEBUG_RATE(priv, "LQ: SWITCHING TO NEW TABLE " + "suc=%d cur-tpt=%d old-tpt=%d\n", + window->success_ratio, + window->average_tpt, + lq_sta->last_tpt); + + if (!is_legacy(tbl->lq_type)) + lq_sta->enable_counter = 1; + + /* Swap tables; "search" becomes "active" */ + lq_sta->active_tbl = active_tbl; + current_tpt = window->average_tpt; + + /* Else poor success; go back to mode in "active" table */ + } else { + + IWL_DEBUG_RATE(priv, "LQ: GOING BACK TO THE OLD TABLE " + "suc=%d cur-tpt=%d old-tpt=%d\n", + window->success_ratio, + window->average_tpt, + lq_sta->last_tpt); + + /* Nullify "search" table */ + tbl->lq_type = LQ_NONE; + + /* Revert to "active" table */ + active_tbl = lq_sta->active_tbl; + tbl = &(lq_sta->lq_info[active_tbl]); + + /* Revert to "active" rate and throughput info */ + index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + current_tpt = lq_sta->last_tpt; + + /* Need to set up a new rate table in uCode */ + update_lq = 1; + } + + /* Either way, we've made a decision; modulation mode + * search is done, allow rate adjustment next time. */ + lq_sta->search_better_tbl = 0; + done_search = 1; /* Don't switch modes below! */ + goto lq_update; + } + + /* (Else) not in search of better modulation mode, try for better + * starting rate, while staying in this mode. */ + high_low = rs_get_adjacent_rate(priv, index, rate_scale_index_msk, + tbl->lq_type); + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + /* If user set max rate, dont allow higher than user constrain */ + if ((lq_sta->max_rate_idx != -1) && + (lq_sta->max_rate_idx < high)) + high = IWL_RATE_INVALID; + + sr = window->success_ratio; + + /* Collect measured throughputs for current and adjacent rates */ + current_tpt = window->average_tpt; + if (low != IWL_RATE_INVALID) + low_tpt = tbl->win[low].average_tpt; + if (high != IWL_RATE_INVALID) + high_tpt = tbl->win[high].average_tpt; + + scale_action = 0; + + /* Too many failures, decrease rate */ + if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) { + IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n"); + scale_action = -1; + + /* No throughput measured yet for adjacent rates; try increase. */ + } else if ((low_tpt == IWL_INVALID_VALUE) && + (high_tpt == IWL_INVALID_VALUE)) { + + if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) + scale_action = 1; + else if (low != IWL_RATE_INVALID) + scale_action = 0; + } + + /* Both adjacent throughputs are measured, but neither one has better + * throughput; we're using the best rate, don't change it! */ + else if ((low_tpt != IWL_INVALID_VALUE) && + (high_tpt != IWL_INVALID_VALUE) && + (low_tpt < current_tpt) && + (high_tpt < current_tpt)) + scale_action = 0; + + /* At least one adjacent rate's throughput is measured, + * and may have better performance. */ + else { + /* Higher adjacent rate's throughput is measured */ + if (high_tpt != IWL_INVALID_VALUE) { + /* Higher rate has better throughput */ + if (high_tpt > current_tpt && + sr >= IWL_RATE_INCREASE_TH) { + scale_action = 1; + } else { + scale_action = 0; + } + + /* Lower adjacent rate's throughput is measured */ + } else if (low_tpt != IWL_INVALID_VALUE) { + /* Lower rate has better throughput */ + if (low_tpt > current_tpt) { + IWL_DEBUG_RATE(priv, + "decrease rate because of low tpt\n"); + scale_action = -1; + } else if (sr >= IWL_RATE_INCREASE_TH) { + scale_action = 1; + } + } + } + + /* Sanity check; asked for decrease, but success rate or throughput + * has been good at old rate. Don't change it. */ + if ((scale_action == -1) && (low != IWL_RATE_INVALID) && + ((sr > IWL_RATE_HIGH_TH) || + (current_tpt > (100 * tbl->expected_tpt[low])))) + scale_action = 0; + if (!iwl_ht_enabled(priv) && !is_legacy(tbl->lq_type)) + scale_action = -1; + if (iwl_tx_ant_restriction(priv) != IWL_ANT_OK_MULTI && + (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) + scale_action = -1; + + if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && + (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { + if (lq_sta->last_bt_traffic > priv->bt_traffic_load) { + /* + * don't set scale_action, don't want to scale up if + * the rate scale doesn't otherwise think that is a + * good idea. + */ + } else if (lq_sta->last_bt_traffic <= priv->bt_traffic_load) { + scale_action = -1; + } + } + lq_sta->last_bt_traffic = priv->bt_traffic_load; + + if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && + (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { + /* search for a new modulation */ + rs_stay_in_table(lq_sta, true); + goto lq_update; + } + + switch (scale_action) { + case -1: + /* Decrease starting rate, update uCode's rate table */ + if (low != IWL_RATE_INVALID) { + update_lq = 1; + index = low; + } + + break; + case 1: + /* Increase starting rate, update uCode's rate table */ + if (high != IWL_RATE_INVALID) { + update_lq = 1; + index = high; + } + + break; + case 0: + /* No change */ + default: + break; + } + + IWL_DEBUG_RATE(priv, "choose rate scale index %d action %d low %d " + "high %d type %d\n", + index, scale_action, low, high, tbl->lq_type); + +lq_update: + /* Replace uCode's rate table for the destination station. */ + if (update_lq) + rs_update_rate_tbl(priv, ctx, lq_sta, tbl, index, is_green); + + if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI) { + /* Should we stay with this modulation mode, + * or search for a new one? */ + rs_stay_in_table(lq_sta, false); + } + /* + * Search for new modulation mode if we're: + * 1) Not changing rates right now + * 2) Not just finishing up a search + * 3) Allowing a new search + */ + if (!update_lq && !done_search && !lq_sta->stay_in_tbl && window->counter) { + /* Save current throughput to compare with "search" throughput*/ + lq_sta->last_tpt = current_tpt; + + /* Select a new "search" modulation mode to try. + * If one is found, set up the new "search" table. */ + if (is_legacy(tbl->lq_type)) + rs_move_legacy_other(priv, lq_sta, conf, sta, index); + else if (is_siso(tbl->lq_type)) + rs_move_siso_to_other(priv, lq_sta, conf, sta, index); + else if (is_mimo2(tbl->lq_type)) + rs_move_mimo2_to_other(priv, lq_sta, conf, sta, index); + else + rs_move_mimo3_to_other(priv, lq_sta, conf, sta, index); + + /* If new "search" mode was selected, set up in uCode table */ + if (lq_sta->search_better_tbl) { + /* Access the "search" table, clear its history. */ + tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&(tbl->win[i])); + + /* Use new "search" start rate */ + index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + + IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n", + tbl->current_rate, index); + rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); + } else + done_search = 1; + } + + if (done_search && !lq_sta->stay_in_tbl) { + /* If the "active" (non-search) mode was legacy, + * and we've tried switching antennas, + * but we haven't been able to try HT modes (not available), + * stay with best antenna legacy modulation for a while + * before next round of mode comparisons. */ + tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); + if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) && + lq_sta->action_counter > tbl1->max_search) { + IWL_DEBUG_RATE(priv, "LQ: STAY in legacy table\n"); + rs_set_stay_in_table(priv, 1, lq_sta); + } + + /* If we're in an HT mode, and all 3 mode switch actions + * have been tried and compared, stay in this best modulation + * mode for a while before next round of mode comparisons. */ + if (lq_sta->enable_counter && + (lq_sta->action_counter >= tbl1->max_search) && + iwl_ht_enabled(priv)) { + if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && + (lq_sta->tx_agg_tid_en & (1 << tid)) && + (tid != IWL_MAX_TID_COUNT)) { + u8 sta_id = lq_sta->lq.sta_id; + tid_data = &priv->tid_data[sta_id][tid]; + if (tid_data->agg.state == IWL_AGG_OFF) { + IWL_DEBUG_RATE(priv, + "try to aggregate tid %d\n", + tid); + rs_tl_turn_on_agg(priv, tid, + lq_sta, sta); + } + } + rs_set_stay_in_table(priv, 0, lq_sta); + } + } + +out: + tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); + lq_sta->last_txrate_idx = index; +} + +/** + * rs_initialize_lq - Initialize a station's hardware rate table + * + * The uCode's station table contains a table of fallback rates + * for automatic fallback during transmission. + * + * NOTE: This sets up a default set of values. These will be replaced later + * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of + * rc80211_simple. + * + * NOTE: Run REPLY_ADD_STA command to set up station table entry, before + * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, + * which requires station table entry to exist). + */ +static void rs_initialize_lq(struct iwl_priv *priv, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta) +{ + struct iwl_scale_tbl_info *tbl; + int rate_idx; + int i; + u32 rate; + u8 use_green = rs_use_green(sta); + u8 active_tbl = 0; + u8 valid_tx_ant; + struct iwl_station_priv *sta_priv; + struct iwl_rxon_context *ctx; + + if (!sta || !lq_sta) + return; + + sta_priv = (void *)sta->drv_priv; + ctx = sta_priv->ctx; + + i = lq_sta->last_txrate_idx; + + valid_tx_ant = priv->nvm_data->valid_tx_ant; + + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + + if ((i < 0) || (i >= IWL_RATE_COUNT)) + i = 0; + + rate = iwl_rates[i].plcp; + tbl->ant_type = first_antenna(valid_tx_ant); + rate |= tbl->ant_type << RATE_MCS_ANT_POS; + + if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE) + rate |= RATE_MCS_CCK_MSK; + + rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx); + if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) + rs_toggle_antenna(valid_tx_ant, &rate, tbl); + + rate = rate_n_flags_from_tbl(priv, tbl, rate_idx, use_green); + tbl->current_rate = rate; + rs_set_expected_tpt_table(lq_sta, tbl); + rs_fill_link_cmd(NULL, lq_sta, rate); + priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, 0, true); +} + +static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, + struct ieee80211_tx_rate_control *txrc) +{ + + struct sk_buff *skb = txrc->skb; + struct ieee80211_supported_band *sband = txrc->sband; + struct iwl_op_mode *op_mode __maybe_unused = + (struct iwl_op_mode *)priv_r; + struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_lq_sta *lq_sta = priv_sta; + int rate_idx; + + IWL_DEBUG_RATE_LIMIT(priv, "rate scale calculate new rate for skb\n"); + + /* Get max rate if user set max rate */ + if (lq_sta) { + lq_sta->max_rate_idx = txrc->max_rate_idx; + if ((sband->band == IEEE80211_BAND_5GHZ) && + (lq_sta->max_rate_idx != -1)) + lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE; + if ((lq_sta->max_rate_idx < 0) || + (lq_sta->max_rate_idx >= IWL_RATE_COUNT)) + lq_sta->max_rate_idx = -1; + } + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (lq_sta && !lq_sta->drv) { + IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); + priv_sta = NULL; + } + + /* Send management frames and NO_ACK data using lowest rate. */ + if (rate_control_send_low(sta, priv_sta, txrc)) + return; + + rate_idx = lq_sta->last_txrate_idx; + + if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { + rate_idx -= IWL_FIRST_OFDM_RATE; + /* 6M and 9M shared same MCS index */ + rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; + if (rs_extract_rate(lq_sta->last_rate_n_flags) >= + IWL_RATE_MIMO3_6M_PLCP) + rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM); + else if (rs_extract_rate(lq_sta->last_rate_n_flags) >= + IWL_RATE_MIMO2_6M_PLCP) + rate_idx = rate_idx + MCS_INDEX_PER_STREAM; + info->control.rates[0].flags = IEEE80211_TX_RC_MCS; + if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) + info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI; + if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) + info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA; + if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) + info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) + info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD; + } else { + /* Check for invalid rates */ + if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) || + ((sband->band == IEEE80211_BAND_5GHZ) && + (rate_idx < IWL_FIRST_OFDM_RATE))) + rate_idx = rate_lowest_index(sband, sta); + /* On valid 5 GHz rate, adjust index */ + else if (sband->band == IEEE80211_BAND_5GHZ) + rate_idx -= IWL_FIRST_OFDM_RATE; + info->control.rates[0].flags = 0; + } + info->control.rates[0].idx = rate_idx; + info->control.rates[0].count = 1; +} + +static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta, + gfp_t gfp) +{ + struct iwl_station_priv *sta_priv = (struct iwl_station_priv *) sta->drv_priv; + struct iwl_op_mode *op_mode __maybe_unused = + (struct iwl_op_mode *)priv_rate; + struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); + + IWL_DEBUG_RATE(priv, "create station rate scale window\n"); + + return &sta_priv->lq_sta; +} + +/* + * Called after adding a new station to initialize rate scaling + */ +void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id) +{ + int i, j; + struct ieee80211_hw *hw = priv->hw; + struct ieee80211_conf *conf = &priv->hw->conf; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct iwl_station_priv *sta_priv; + struct iwl_lq_sta *lq_sta; + struct ieee80211_supported_band *sband; + unsigned long supp; /* must be unsigned long for for_each_set_bit */ + + sta_priv = (struct iwl_station_priv *) sta->drv_priv; + lq_sta = &sta_priv->lq_sta; + sband = hw->wiphy->bands[conf->chandef.chan->band]; + + + lq_sta->lq.sta_id = sta_id; + + for (j = 0; j < LQ_SIZE; j++) + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); + + lq_sta->flush_timer = 0; + lq_sta->supp_rates = sta->supp_rates[sband->band]; + + IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n", + sta_id); + /* TODO: what is a good starting rate for STA? About middle? Maybe not + * the lowest or the highest rate.. Could consider using RSSI from + * previous packets? Need to have IEEE 802.1X auth succeed immediately + * after assoc.. */ + + lq_sta->is_dup = 0; + lq_sta->max_rate_idx = -1; + lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; + lq_sta->is_green = rs_use_green(sta); + lq_sta->band = sband->band; + /* + * active legacy rates as per supported rates bitmap + */ + supp = sta->supp_rates[sband->band]; + lq_sta->active_legacy_rate = 0; + for_each_set_bit(i, &supp, BITS_PER_LONG) + lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); + + /* + * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), + * supp_rates[] does not; shift to convert format, force 9 MBits off. + */ + lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; + lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; + lq_sta->active_siso_rate &= ~((u16)0x2); + lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; + + /* Same here */ + lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; + lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; + lq_sta->active_mimo2_rate &= ~((u16)0x2); + lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; + + lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1; + lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1; + lq_sta->active_mimo3_rate &= ~((u16)0x2); + lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE; + + IWL_DEBUG_RATE(priv, "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n", + lq_sta->active_siso_rate, + lq_sta->active_mimo2_rate, + lq_sta->active_mimo3_rate); + + /* These values will be overridden later */ + lq_sta->lq.general_params.single_stream_ant_msk = + first_antenna(priv->nvm_data->valid_tx_ant); + lq_sta->lq.general_params.dual_stream_ant_msk = + priv->nvm_data->valid_tx_ant & + ~first_antenna(priv->nvm_data->valid_tx_ant); + if (!lq_sta->lq.general_params.dual_stream_ant_msk) { + lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; + } else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) { + lq_sta->lq.general_params.dual_stream_ant_msk = + priv->nvm_data->valid_tx_ant; + } + + /* as default allow aggregation for all tids */ + lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; + lq_sta->drv = priv; + + /* Set last_txrate_idx to lowest rate */ + lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); + if (sband->band == IEEE80211_BAND_5GHZ) + lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; + lq_sta->is_agg = 0; +#ifdef CONFIG_MAC80211_DEBUGFS + lq_sta->dbg_fixed_rate = 0; +#endif + + rs_initialize_lq(priv, sta, lq_sta); +} + +static void rs_fill_link_cmd(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, u32 new_rate) +{ + struct iwl_scale_tbl_info tbl_type; + int index = 0; + int rate_idx; + int repeat_rate = 0; + u8 ant_toggle_cnt = 0; + u8 use_ht_possible = 1; + u8 valid_tx_ant = 0; + struct iwl_station_priv *sta_priv = + container_of(lq_sta, struct iwl_station_priv, lq_sta); + struct iwl_link_quality_cmd *lq_cmd = &lq_sta->lq; + + /* Override starting rate (index 0) if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Interpret new_rate (rate_n_flags) */ + rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, + &tbl_type, &rate_idx); + + if (priv && priv->bt_full_concurrent) { + /* 1x1 only */ + tbl_type.ant_type = + first_antenna(priv->nvm_data->valid_tx_ant); + } + + /* How many times should we repeat the initial rate? */ + if (is_legacy(tbl_type.lq_type)) { + ant_toggle_cnt = 1; + repeat_rate = IWL_NUMBER_TRY; + } else { + repeat_rate = min(IWL_HT_NUMBER_TRY, + LINK_QUAL_AGG_DISABLE_START_DEF - 1); + } + + lq_cmd->general_params.mimo_delimiter = + is_mimo(tbl_type.lq_type) ? 1 : 0; + + /* Fill 1st table entry (index 0) */ + lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); + + if (num_of_ant(tbl_type.ant_type) == 1) { + lq_cmd->general_params.single_stream_ant_msk = + tbl_type.ant_type; + } else if (num_of_ant(tbl_type.ant_type) == 2) { + lq_cmd->general_params.dual_stream_ant_msk = + tbl_type.ant_type; + } /* otherwise we don't modify the existing value */ + + index++; + repeat_rate--; + if (priv) { + if (priv->bt_full_concurrent) + valid_tx_ant = ANT_A; + else + valid_tx_ant = priv->nvm_data->valid_tx_ant; + } + + /* Fill rest of rate table */ + while (index < LINK_QUAL_MAX_RETRY_NUM) { + /* Repeat initial/next rate. + * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. + * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ + while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { + if (is_legacy(tbl_type.lq_type)) { + if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) + ant_toggle_cnt++; + else if (priv && + rs_toggle_antenna(valid_tx_ant, + &new_rate, &tbl_type)) + ant_toggle_cnt = 1; + } + + /* Override next rate if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Fill next table entry */ + lq_cmd->rs_table[index].rate_n_flags = + cpu_to_le32(new_rate); + repeat_rate--; + index++; + } + + rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, + &rate_idx); + + if (priv && priv->bt_full_concurrent) { + /* 1x1 only */ + tbl_type.ant_type = + first_antenna(priv->nvm_data->valid_tx_ant); + } + + /* Indicate to uCode which entries might be MIMO. + * If initial rate was MIMO, this will finally end up + * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ + if (is_mimo(tbl_type.lq_type)) + lq_cmd->general_params.mimo_delimiter = index; + + /* Get next rate */ + new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, + use_ht_possible); + + /* How many times should we repeat the next rate? */ + if (is_legacy(tbl_type.lq_type)) { + if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) + ant_toggle_cnt++; + else if (priv && + rs_toggle_antenna(valid_tx_ant, + &new_rate, &tbl_type)) + ant_toggle_cnt = 1; + + repeat_rate = IWL_NUMBER_TRY; + } else { + repeat_rate = IWL_HT_NUMBER_TRY; + } + + /* Don't allow HT rates after next pass. + * rs_get_lower_rate() will change type to LQ_A or LQ_G. */ + use_ht_possible = 0; + + /* Override next rate if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Fill next table entry */ + lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); + + index++; + repeat_rate--; + } + + lq_cmd->agg_params.agg_frame_cnt_limit = + sta_priv->max_agg_bufsize ?: LINK_QUAL_AGG_FRAME_LIMIT_DEF; + lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + + lq_cmd->agg_params.agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); + /* + * overwrite if needed, pass aggregation time limit + * to uCode in uSec + */ + if (priv && priv->lib->bt_params && + priv->lib->bt_params->agg_time_limit && + priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) + lq_cmd->agg_params.agg_time_limit = + cpu_to_le16(priv->lib->bt_params->agg_time_limit); +} + +static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) +{ + return hw->priv; +} +/* rate scale requires free function to be implemented */ +static void rs_free(void *priv_rate) +{ + return; +} + +static void rs_free_sta(void *priv_r, struct ieee80211_sta *sta, + void *priv_sta) +{ + struct iwl_op_mode *op_mode __maybe_unused = priv_r; + struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); + + IWL_DEBUG_RATE(priv, "enter\n"); + IWL_DEBUG_RATE(priv, "leave\n"); +} + +#ifdef CONFIG_MAC80211_DEBUGFS +static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index) +{ + struct iwl_priv *priv; + u8 valid_tx_ant; + u8 ant_sel_tx; + + priv = lq_sta->drv; + valid_tx_ant = priv->nvm_data->valid_tx_ant; + if (lq_sta->dbg_fixed_rate) { + ant_sel_tx = + ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) + >> RATE_MCS_ANT_POS); + if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { + *rate_n_flags = lq_sta->dbg_fixed_rate; + IWL_DEBUG_RATE(priv, "Fixed rate ON\n"); + } else { + lq_sta->dbg_fixed_rate = 0; + IWL_ERR(priv, + "Invalid antenna selection 0x%X, Valid is 0x%X\n", + ant_sel_tx, valid_tx_ant); + IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); + } + } else { + IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); + } +} + +static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_priv *priv; + char buf[64]; + size_t buf_size; + u32 parsed_rate; + + + priv = lq_sta->drv; + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%x", &parsed_rate) == 1) + lq_sta->dbg_fixed_rate = parsed_rate; + else + lq_sta->dbg_fixed_rate = 0; + + rs_program_fix_rate(priv, lq_sta); + + return count; +} + +static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *buff; + int desc = 0; + int i = 0; + int index = 0; + ssize_t ret; + + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_priv *priv; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + + priv = lq_sta->drv; + buff = kmalloc(1024, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); + desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n", + lq_sta->total_failed, lq_sta->total_success, + lq_sta->active_legacy_rate); + desc += sprintf(buff+desc, "fixed rate 0x%X\n", + lq_sta->dbg_fixed_rate); + desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", + (priv->nvm_data->valid_tx_ant & ANT_A) ? "ANT_A," : "", + (priv->nvm_data->valid_tx_ant & ANT_B) ? "ANT_B," : "", + (priv->nvm_data->valid_tx_ant & ANT_C) ? "ANT_C" : ""); + desc += sprintf(buff+desc, "lq type %s\n", + (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); + if (is_Ht(tbl->lq_type)) { + desc += sprintf(buff + desc, " %s", + (is_siso(tbl->lq_type)) ? "SISO" : + ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3")); + desc += sprintf(buff + desc, " %s", + (tbl->is_ht40) ? "40MHz" : "20MHz"); + desc += sprintf(buff + desc, " %s %s %s\n", + (tbl->is_SGI) ? "SGI" : "", + (lq_sta->is_green) ? "GF enabled" : "", + (lq_sta->is_agg) ? "AGG on" : ""); + } + desc += sprintf(buff+desc, "last tx rate=0x%X\n", + lq_sta->last_rate_n_flags); + desc += sprintf(buff+desc, "general:" + "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", + lq_sta->lq.general_params.flags, + lq_sta->lq.general_params.mimo_delimiter, + lq_sta->lq.general_params.single_stream_ant_msk, + lq_sta->lq.general_params.dual_stream_ant_msk); + + desc += sprintf(buff+desc, "agg:" + "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", + le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), + lq_sta->lq.agg_params.agg_dis_start_th, + lq_sta->lq.agg_params.agg_frame_cnt_limit); + + desc += sprintf(buff+desc, + "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", + lq_sta->lq.general_params.start_rate_index[0], + lq_sta->lq.general_params.start_rate_index[1], + lq_sta->lq.general_params.start_rate_index[2], + lq_sta->lq.general_params.start_rate_index[3]); + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + index = iwl_hwrate_to_plcp_idx( + le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags)); + if (is_legacy(tbl->lq_type)) { + desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n", + i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), + iwl_rate_mcs[index].mbps); + } else { + desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps (%s)\n", + i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), + iwl_rate_mcs[index].mbps, iwl_rate_mcs[index].mcs); + } + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_scale_table_ops = { + .write = rs_sta_dbgfs_scale_table_write, + .read = rs_sta_dbgfs_scale_table_read, + .open = simple_open, + .llseek = default_llseek, +}; +static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *buff; + int desc = 0; + int i, j; + ssize_t ret; + + struct iwl_lq_sta *lq_sta = file->private_data; + + buff = kmalloc(1024, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + for (i = 0; i < LQ_SIZE; i++) { + desc += sprintf(buff+desc, + "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n" + "rate=0x%X\n", + lq_sta->active_tbl == i ? "*" : "x", + lq_sta->lq_info[i].lq_type, + lq_sta->lq_info[i].is_SGI, + lq_sta->lq_info[i].is_ht40, + lq_sta->lq_info[i].is_dup, + lq_sta->is_green, + lq_sta->lq_info[i].current_rate); + for (j = 0; j < IWL_RATE_COUNT; j++) { + desc += sprintf(buff+desc, + "counter=%d success=%d %%=%d\n", + lq_sta->lq_info[i].win[j].counter, + lq_sta->lq_info[i].win[j].success_counter, + lq_sta->lq_info[i].win[j].success_ratio); + } + } + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_stats_table_ops = { + .read = rs_sta_dbgfs_stats_table_read, + .open = simple_open, + .llseek = default_llseek, +}; + +static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; + char buff[120]; + int desc = 0; + + if (is_Ht(tbl->lq_type)) + desc += sprintf(buff+desc, + "Bit Rate= %d Mb/s\n", + tbl->expected_tpt[lq_sta->last_txrate_idx]); + else + desc += sprintf(buff+desc, + "Bit Rate= %d Mb/s\n", + iwl_rates[lq_sta->last_txrate_idx].ieee >> 1); + + return simple_read_from_buffer(user_buf, count, ppos, buff, desc); +} + +static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { + .read = rs_sta_dbgfs_rate_scale_data_read, + .open = simple_open, + .llseek = default_llseek, +}; + +static void rs_add_debugfs(void *priv, void *priv_sta, + struct dentry *dir) +{ + struct iwl_lq_sta *lq_sta = priv_sta; + lq_sta->rs_sta_dbgfs_scale_table_file = + debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, + lq_sta, &rs_sta_dbgfs_scale_table_ops); + lq_sta->rs_sta_dbgfs_stats_table_file = + debugfs_create_file("rate_stats_table", S_IRUSR, dir, + lq_sta, &rs_sta_dbgfs_stats_table_ops); + lq_sta->rs_sta_dbgfs_rate_scale_data_file = + debugfs_create_file("rate_scale_data", S_IRUSR, dir, + lq_sta, &rs_sta_dbgfs_rate_scale_data_ops); + lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = + debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, + &lq_sta->tx_agg_tid_en); + +} + +static void rs_remove_debugfs(void *priv, void *priv_sta) +{ + struct iwl_lq_sta *lq_sta = priv_sta; + debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); +} +#endif + +/* + * Initialization of rate scaling information is done by driver after + * the station is added. Since mac80211 calls this function before a + * station is added we ignore it. + */ +static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *priv_sta) +{ +} + +static const struct rate_control_ops rs_ops = { + .name = RS_NAME, + .tx_status = rs_tx_status, + .get_rate = rs_get_rate, + .rate_init = rs_rate_init_stub, + .alloc = rs_alloc, + .free = rs_free, + .alloc_sta = rs_alloc_sta, + .free_sta = rs_free_sta, +#ifdef CONFIG_MAC80211_DEBUGFS + .add_sta_debugfs = rs_add_debugfs, + .remove_sta_debugfs = rs_remove_debugfs, +#endif +}; + +int iwlagn_rate_control_register(void) +{ + return ieee80211_rate_control_register(&rs_ops); +} + +void iwlagn_rate_control_unregister(void) +{ + ieee80211_rate_control_unregister(&rs_ops); +} + diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rs.h b/drivers/net/wireless/intel/iwlwifi/dvm/rs.h new file mode 100644 index 000000000000..f6bd25cad203 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.h @@ -0,0 +1,426 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_agn_rs_h__ +#define __iwl_agn_rs_h__ + +#include + +#include "iwl-config.h" + +#include "commands.h" + +struct iwl_rate_info { + u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ + u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ + u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ + u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */ + u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ + u8 prev_ieee; /* previous rate in IEEE speeds */ + u8 next_ieee; /* next rate in IEEE speeds */ + u8 prev_rs; /* previous rate used in rs algo */ + u8 next_rs; /* next rate used in rs algo */ + u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ + u8 next_rs_tgg; /* next rate used in TGG rs algo */ +}; + +/* + * These serve as indexes into + * struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; + */ +enum { + IWL_RATE_1M_INDEX = 0, + IWL_RATE_2M_INDEX, + IWL_RATE_5M_INDEX, + IWL_RATE_11M_INDEX, + IWL_RATE_6M_INDEX, + IWL_RATE_9M_INDEX, + IWL_RATE_12M_INDEX, + IWL_RATE_18M_INDEX, + IWL_RATE_24M_INDEX, + IWL_RATE_36M_INDEX, + IWL_RATE_48M_INDEX, + IWL_RATE_54M_INDEX, + IWL_RATE_60M_INDEX, + IWL_RATE_COUNT, /*FIXME:RS:change to IWL_RATE_INDEX_COUNT,*/ + IWL_RATE_COUNT_LEGACY = IWL_RATE_COUNT - 1, /* Excluding 60M */ + IWL_RATE_INVM_INDEX = IWL_RATE_COUNT, + IWL_RATE_INVALID = IWL_RATE_COUNT, +}; + +enum { + IWL_RATE_6M_INDEX_TABLE = 0, + IWL_RATE_9M_INDEX_TABLE, + IWL_RATE_12M_INDEX_TABLE, + IWL_RATE_18M_INDEX_TABLE, + IWL_RATE_24M_INDEX_TABLE, + IWL_RATE_36M_INDEX_TABLE, + IWL_RATE_48M_INDEX_TABLE, + IWL_RATE_54M_INDEX_TABLE, + IWL_RATE_1M_INDEX_TABLE, + IWL_RATE_2M_INDEX_TABLE, + IWL_RATE_5M_INDEX_TABLE, + IWL_RATE_11M_INDEX_TABLE, + IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1, +}; + +enum { + IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, + IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX, + IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, + IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, +}; + +/* #define vs. enum to keep from defaulting to 'large integer' */ +#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX) +#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX) +#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX) +#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX) +#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX) +#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX) +#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX) +#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX) +#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX) +#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX) +#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX) +#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX) +#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) + +/* uCode API values for legacy bit rates, both OFDM and CCK */ +enum { + IWL_RATE_6M_PLCP = 13, + IWL_RATE_9M_PLCP = 15, + IWL_RATE_12M_PLCP = 5, + IWL_RATE_18M_PLCP = 7, + IWL_RATE_24M_PLCP = 9, + IWL_RATE_36M_PLCP = 11, + IWL_RATE_48M_PLCP = 1, + IWL_RATE_54M_PLCP = 3, + IWL_RATE_60M_PLCP = 3,/*FIXME:RS:should be removed*/ + IWL_RATE_1M_PLCP = 10, + IWL_RATE_2M_PLCP = 20, + IWL_RATE_5M_PLCP = 55, + IWL_RATE_11M_PLCP = 110, + /*FIXME:RS:change to IWL_RATE_LEGACY_??M_PLCP */ + /*FIXME:RS:add IWL_RATE_LEGACY_INVM_PLCP = 0,*/ +}; + +/* uCode API values for OFDM high-throughput (HT) bit rates */ +enum { + IWL_RATE_SISO_6M_PLCP = 0, + IWL_RATE_SISO_12M_PLCP = 1, + IWL_RATE_SISO_18M_PLCP = 2, + IWL_RATE_SISO_24M_PLCP = 3, + IWL_RATE_SISO_36M_PLCP = 4, + IWL_RATE_SISO_48M_PLCP = 5, + IWL_RATE_SISO_54M_PLCP = 6, + IWL_RATE_SISO_60M_PLCP = 7, + IWL_RATE_MIMO2_6M_PLCP = 0x8, + IWL_RATE_MIMO2_12M_PLCP = 0x9, + IWL_RATE_MIMO2_18M_PLCP = 0xa, + IWL_RATE_MIMO2_24M_PLCP = 0xb, + IWL_RATE_MIMO2_36M_PLCP = 0xc, + IWL_RATE_MIMO2_48M_PLCP = 0xd, + IWL_RATE_MIMO2_54M_PLCP = 0xe, + IWL_RATE_MIMO2_60M_PLCP = 0xf, + IWL_RATE_MIMO3_6M_PLCP = 0x10, + IWL_RATE_MIMO3_12M_PLCP = 0x11, + IWL_RATE_MIMO3_18M_PLCP = 0x12, + IWL_RATE_MIMO3_24M_PLCP = 0x13, + IWL_RATE_MIMO3_36M_PLCP = 0x14, + IWL_RATE_MIMO3_48M_PLCP = 0x15, + IWL_RATE_MIMO3_54M_PLCP = 0x16, + IWL_RATE_MIMO3_60M_PLCP = 0x17, + IWL_RATE_SISO_INVM_PLCP, + IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, + IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, +}; + +/* MAC header values for bit rates */ +enum { + IWL_RATE_6M_IEEE = 12, + IWL_RATE_9M_IEEE = 18, + IWL_RATE_12M_IEEE = 24, + IWL_RATE_18M_IEEE = 36, + IWL_RATE_24M_IEEE = 48, + IWL_RATE_36M_IEEE = 72, + IWL_RATE_48M_IEEE = 96, + IWL_RATE_54M_IEEE = 108, + IWL_RATE_60M_IEEE = 120, + IWL_RATE_1M_IEEE = 2, + IWL_RATE_2M_IEEE = 4, + IWL_RATE_5M_IEEE = 11, + IWL_RATE_11M_IEEE = 22, +}; + +#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) + +#define IWL_INVALID_VALUE -1 + +#define IWL_MIN_RSSI_VAL -100 +#define IWL_MAX_RSSI_VAL 0 + +/* These values specify how many Tx frame attempts before + * searching for a new modulation mode */ +#define IWL_LEGACY_FAILURE_LIMIT 160 +#define IWL_LEGACY_SUCCESS_LIMIT 480 +#define IWL_LEGACY_TABLE_COUNT 160 + +#define IWL_NONE_LEGACY_FAILURE_LIMIT 400 +#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500 +#define IWL_NONE_LEGACY_TABLE_COUNT 1500 + +/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ +#define IWL_RS_GOOD_RATIO 12800 /* 100% */ +#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ +#define IWL_RATE_HIGH_TH 10880 /* 85% */ +#define IWL_RATE_INCREASE_TH 6400 /* 50% */ +#define IWL_RATE_DECREASE_TH 1920 /* 15% */ + +/* possible actions when in legacy mode */ +#define IWL_LEGACY_SWITCH_ANTENNA1 0 +#define IWL_LEGACY_SWITCH_ANTENNA2 1 +#define IWL_LEGACY_SWITCH_SISO 2 +#define IWL_LEGACY_SWITCH_MIMO2_AB 3 +#define IWL_LEGACY_SWITCH_MIMO2_AC 4 +#define IWL_LEGACY_SWITCH_MIMO2_BC 5 +#define IWL_LEGACY_SWITCH_MIMO3_ABC 6 + +/* possible actions when in siso mode */ +#define IWL_SISO_SWITCH_ANTENNA1 0 +#define IWL_SISO_SWITCH_ANTENNA2 1 +#define IWL_SISO_SWITCH_MIMO2_AB 2 +#define IWL_SISO_SWITCH_MIMO2_AC 3 +#define IWL_SISO_SWITCH_MIMO2_BC 4 +#define IWL_SISO_SWITCH_GI 5 +#define IWL_SISO_SWITCH_MIMO3_ABC 6 + + +/* possible actions when in mimo mode */ +#define IWL_MIMO2_SWITCH_ANTENNA1 0 +#define IWL_MIMO2_SWITCH_ANTENNA2 1 +#define IWL_MIMO2_SWITCH_SISO_A 2 +#define IWL_MIMO2_SWITCH_SISO_B 3 +#define IWL_MIMO2_SWITCH_SISO_C 4 +#define IWL_MIMO2_SWITCH_GI 5 +#define IWL_MIMO2_SWITCH_MIMO3_ABC 6 + + +/* possible actions when in mimo3 mode */ +#define IWL_MIMO3_SWITCH_ANTENNA1 0 +#define IWL_MIMO3_SWITCH_ANTENNA2 1 +#define IWL_MIMO3_SWITCH_SISO_A 2 +#define IWL_MIMO3_SWITCH_SISO_B 3 +#define IWL_MIMO3_SWITCH_SISO_C 4 +#define IWL_MIMO3_SWITCH_MIMO2_AB 5 +#define IWL_MIMO3_SWITCH_MIMO2_AC 6 +#define IWL_MIMO3_SWITCH_MIMO2_BC 7 +#define IWL_MIMO3_SWITCH_GI 8 + + +#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI +#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC + +/*FIXME:RS:add possible actions for MIMO3*/ + +#define IWL_ACTION_LIMIT 3 /* # possible actions */ + +#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ + +/* load per tid defines for A-MPDU activation */ +#define IWL_AGG_TPT_THREHOLD 0 +#define IWL_AGG_LOAD_THRESHOLD 10 +#define IWL_AGG_ALL_TID 0xff +#define TID_QUEUE_CELL_SPACING 50 /*mS */ +#define TID_QUEUE_MAX_SIZE 20 +#define TID_ROUND_VALUE 5 /* mS */ + +#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) +#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) + +extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; + +enum iwl_table_type { + LQ_NONE, + LQ_G, /* legacy types */ + LQ_A, + LQ_SISO, /* high-throughput types */ + LQ_MIMO2, + LQ_MIMO3, + LQ_MAX, +}; + +#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A)) +#define is_siso(tbl) ((tbl) == LQ_SISO) +#define is_mimo2(tbl) ((tbl) == LQ_MIMO2) +#define is_mimo3(tbl) ((tbl) == LQ_MIMO3) +#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl)) +#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) +#define is_a_band(tbl) ((tbl) == LQ_A) +#define is_g_and(tbl) ((tbl) == LQ_G) + +#define IWL_MAX_MCS_DISPLAY_SIZE 12 + +struct iwl_rate_mcs_info { + char mbps[IWL_MAX_MCS_DISPLAY_SIZE]; + char mcs[IWL_MAX_MCS_DISPLAY_SIZE]; +}; + +/** + * struct iwl_rate_scale_data -- tx success history for one rate + */ +struct iwl_rate_scale_data { + u64 data; /* bitmap of successful frames */ + s32 success_counter; /* number of frames successful */ + s32 success_ratio; /* per-cent * 128 */ + s32 counter; /* number of frames attempted */ + s32 average_tpt; /* success ratio * expected throughput */ + unsigned long stamp; +}; + +/** + * struct iwl_scale_tbl_info -- tx params and success history for all rates + * + * There are two of these in struct iwl_lq_sta, + * one for "active", and one for "search". + */ +struct iwl_scale_tbl_info { + enum iwl_table_type lq_type; + u8 ant_type; + u8 is_SGI; /* 1 = short guard interval */ + u8 is_ht40; /* 1 = 40 MHz channel width */ + u8 is_dup; /* 1 = duplicated data streams */ + u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ + u8 max_search; /* maximun number of tables we can search */ + const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ + u32 current_rate; /* rate_n_flags, uCode API format */ + struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ +}; + +struct iwl_traffic_load { + unsigned long time_stamp; /* age of the oldest statistics */ + u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time + * slice */ + u32 total; /* total num of packets during the + * last TID_MAX_TIME_DIFF */ + u8 queue_count; /* number of queues that has + * been used since the last cleanup */ + u8 head; /* start of the circular buffer */ +}; + +/** + * struct iwl_lq_sta -- driver's rate scaling private structure + * + * Pointer to this gets passed back and forth between driver and mac80211. + */ +struct iwl_lq_sta { + u8 active_tbl; /* index of active table, range 0-1 */ + u8 enable_counter; /* indicates HT mode */ + u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ + u8 search_better_tbl; /* 1: currently trying alternate mode */ + s32 last_tpt; + + /* The following determine when to search for a new mode */ + u32 table_count_limit; + u32 max_failure_limit; /* # failed frames before new search */ + u32 max_success_limit; /* # successful frames before new search */ + u32 table_count; + u32 total_failed; /* total failed frames, any/all rates */ + u32 total_success; /* total successful frames, any/all rates */ + u64 flush_timer; /* time staying in mode before new search */ + + u8 action_counter; /* # mode-switch actions tried */ + u8 is_green; + u8 is_dup; + enum ieee80211_band band; + + /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ + u32 supp_rates; + u16 active_legacy_rate; + u16 active_siso_rate; + u16 active_mimo2_rate; + u16 active_mimo3_rate; + s8 max_rate_idx; /* Max rate set by user */ + u8 missed_rate_counter; + + struct iwl_link_quality_cmd lq; + struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ + struct iwl_traffic_load load[IWL_MAX_TID_COUNT]; + u8 tx_agg_tid_en; +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *rs_sta_dbgfs_scale_table_file; + struct dentry *rs_sta_dbgfs_stats_table_file; + struct dentry *rs_sta_dbgfs_rate_scale_data_file; + struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; + u32 dbg_fixed_rate; +#endif + struct iwl_priv *drv; + + /* used to be in sta_info */ + int last_txrate_idx; + /* last tx rate_n_flags */ + u32 last_rate_n_flags; + /* packets destined for this STA are aggregated */ + u8 is_agg; + /* BT traffic this sta was last updated in */ + u8 last_bt_traffic; +}; + +static inline u8 first_antenna(u8 mask) +{ + if (mask & ANT_A) + return ANT_A; + if (mask & ANT_B) + return ANT_B; + return ANT_C; +} + + +/* Initialize station's rate scaling information after adding station */ +void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, + u8 sta_id); + +/** + * iwl_rate_control_register - Register the rate control algorithm callbacks + * + * Since the rate control algorithm is hardware specific, there is no need + * or reason to place it as a stand alone module. The driver can call + * iwl_rate_control_register in order to register the rate control callbacks + * with the mac80211 subsystem. This should be performed prior to calling + * ieee80211_register_hw + * + */ +int iwlagn_rate_control_register(void); + +/** + * iwl_rate_control_unregister - Unregister the rate control callbacks + * + * This should be called after calling ieee80211_unregister_hw, but before + * the driver is unloaded. + */ +void iwlagn_rate_control_unregister(void); + +#endif /* __iwl_agn__rs__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c new file mode 100644 index 000000000000..4a45b0b594c7 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c @@ -0,0 +1,1101 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portionhelp of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include "iwl-io.h" +#include "dev.h" +#include "calib.h" +#include "agn.h" + +#define IWL_CMD_ENTRY(x) [x] = #x + +const char *const iwl_dvm_cmd_strings[REPLY_MAX + 1] = { + IWL_CMD_ENTRY(REPLY_ALIVE), + IWL_CMD_ENTRY(REPLY_ERROR), + IWL_CMD_ENTRY(REPLY_ECHO), + IWL_CMD_ENTRY(REPLY_RXON), + IWL_CMD_ENTRY(REPLY_RXON_ASSOC), + IWL_CMD_ENTRY(REPLY_QOS_PARAM), + IWL_CMD_ENTRY(REPLY_RXON_TIMING), + IWL_CMD_ENTRY(REPLY_ADD_STA), + IWL_CMD_ENTRY(REPLY_REMOVE_STA), + IWL_CMD_ENTRY(REPLY_REMOVE_ALL_STA), + IWL_CMD_ENTRY(REPLY_TXFIFO_FLUSH), + IWL_CMD_ENTRY(REPLY_WEPKEY), + IWL_CMD_ENTRY(REPLY_TX), + IWL_CMD_ENTRY(REPLY_LEDS_CMD), + IWL_CMD_ENTRY(REPLY_TX_LINK_QUALITY_CMD), + IWL_CMD_ENTRY(COEX_PRIORITY_TABLE_CMD), + IWL_CMD_ENTRY(COEX_MEDIUM_NOTIFICATION), + IWL_CMD_ENTRY(COEX_EVENT_CMD), + IWL_CMD_ENTRY(REPLY_QUIET_CMD), + IWL_CMD_ENTRY(REPLY_CHANNEL_SWITCH), + IWL_CMD_ENTRY(CHANNEL_SWITCH_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_SPECTRUM_MEASUREMENT_CMD), + IWL_CMD_ENTRY(SPECTRUM_MEASURE_NOTIFICATION), + IWL_CMD_ENTRY(POWER_TABLE_CMD), + IWL_CMD_ENTRY(PM_SLEEP_NOTIFICATION), + IWL_CMD_ENTRY(PM_DEBUG_STATISTIC_NOTIFIC), + IWL_CMD_ENTRY(REPLY_SCAN_CMD), + IWL_CMD_ENTRY(REPLY_SCAN_ABORT_CMD), + IWL_CMD_ENTRY(SCAN_START_NOTIFICATION), + IWL_CMD_ENTRY(SCAN_RESULTS_NOTIFICATION), + IWL_CMD_ENTRY(SCAN_COMPLETE_NOTIFICATION), + IWL_CMD_ENTRY(BEACON_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_TX_BEACON), + IWL_CMD_ENTRY(WHO_IS_AWAKE_NOTIFICATION), + IWL_CMD_ENTRY(QUIET_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_TX_PWR_TABLE_CMD), + IWL_CMD_ENTRY(MEASURE_ABORT_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_BT_CONFIG), + IWL_CMD_ENTRY(REPLY_STATISTICS_CMD), + IWL_CMD_ENTRY(STATISTICS_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_CARD_STATE_CMD), + IWL_CMD_ENTRY(CARD_STATE_NOTIFICATION), + IWL_CMD_ENTRY(MISSED_BEACONS_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_CT_KILL_CONFIG_CMD), + IWL_CMD_ENTRY(SENSITIVITY_CMD), + IWL_CMD_ENTRY(REPLY_PHY_CALIBRATION_CMD), + IWL_CMD_ENTRY(REPLY_RX_PHY_CMD), + IWL_CMD_ENTRY(REPLY_RX_MPDU_CMD), + IWL_CMD_ENTRY(REPLY_COMPRESSED_BA), + IWL_CMD_ENTRY(CALIBRATION_CFG_CMD), + IWL_CMD_ENTRY(CALIBRATION_RES_NOTIFICATION), + IWL_CMD_ENTRY(CALIBRATION_COMPLETE_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_TX_POWER_DBM_CMD), + IWL_CMD_ENTRY(TEMPERATURE_NOTIFICATION), + IWL_CMD_ENTRY(TX_ANT_CONFIGURATION_CMD), + IWL_CMD_ENTRY(REPLY_BT_COEX_PROFILE_NOTIF), + IWL_CMD_ENTRY(REPLY_BT_COEX_PRIO_TABLE), + IWL_CMD_ENTRY(REPLY_BT_COEX_PROT_ENV), + IWL_CMD_ENTRY(REPLY_WIPAN_PARAMS), + IWL_CMD_ENTRY(REPLY_WIPAN_RXON), + IWL_CMD_ENTRY(REPLY_WIPAN_RXON_TIMING), + IWL_CMD_ENTRY(REPLY_WIPAN_RXON_ASSOC), + IWL_CMD_ENTRY(REPLY_WIPAN_QOS_PARAM), + IWL_CMD_ENTRY(REPLY_WIPAN_WEPKEY), + IWL_CMD_ENTRY(REPLY_WIPAN_P2P_CHANNEL_SWITCH), + IWL_CMD_ENTRY(REPLY_WIPAN_NOA_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_WIPAN_DEACTIVATION_COMPLETE), + IWL_CMD_ENTRY(REPLY_WOWLAN_PATTERNS), + IWL_CMD_ENTRY(REPLY_WOWLAN_WAKEUP_FILTER), + IWL_CMD_ENTRY(REPLY_WOWLAN_TSC_RSC_PARAMS), + IWL_CMD_ENTRY(REPLY_WOWLAN_TKIP_PARAMS), + IWL_CMD_ENTRY(REPLY_WOWLAN_KEK_KCK_MATERIAL), + IWL_CMD_ENTRY(REPLY_WOWLAN_GET_STATUS), + IWL_CMD_ENTRY(REPLY_D3_CONFIG), +}; +#undef IWL_CMD_ENTRY + +/****************************************************************************** + * + * Generic RX handler implementations + * + ******************************************************************************/ + +static void iwlagn_rx_reply_error(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_error_resp *err_resp = (void *)pkt->data; + + IWL_ERR(priv, "Error Reply type 0x%08X cmd REPLY_ERROR (0x%02X) " + "seq 0x%04X ser 0x%08X\n", + le32_to_cpu(err_resp->error_type), + err_resp->cmd_id, + le16_to_cpu(err_resp->bad_cmd_seq_num), + le32_to_cpu(err_resp->error_info)); +} + +static void iwlagn_rx_csa(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_csa_notification *csa = (void *)pkt->data; + /* + * MULTI-FIXME + * See iwlagn_mac_channel_switch. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl_rxon_cmd *rxon = (void *)&ctx->active; + + if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) + return; + + if (!le32_to_cpu(csa->status) && csa->channel == priv->switch_channel) { + rxon->channel = csa->channel; + ctx->staging.channel = csa->channel; + IWL_DEBUG_11H(priv, "CSA notif: channel %d\n", + le16_to_cpu(csa->channel)); + iwl_chswitch_done(priv, true); + } else { + IWL_ERR(priv, "CSA notif (fail) : channel %d\n", + le16_to_cpu(csa->channel)); + iwl_chswitch_done(priv, false); + } +} + + +static void iwlagn_rx_spectrum_measure_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_spectrum_notification *report = (void *)pkt->data; + + if (!report->state) { + IWL_DEBUG_11H(priv, + "Spectrum Measure Notification: Start\n"); + return; + } + + memcpy(&priv->measure_report, report, sizeof(*report)); + priv->measurement_status |= MEASUREMENT_READY; +} + +static void iwlagn_rx_pm_sleep_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ +#ifdef CONFIG_IWLWIFI_DEBUG + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_sleep_notification *sleep = (void *)pkt->data; + IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n", + sleep->pm_sleep_mode, sleep->pm_wakeup_src); +#endif +} + +static void iwlagn_rx_pm_debug_statistics_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + 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); +} + +static void iwlagn_rx_beacon_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwlagn_beacon_notif *beacon = (void *)pkt->data; +#ifdef CONFIG_IWLWIFI_DEBUG + u16 status = le16_to_cpu(beacon->beacon_notify_hdr.status.status); + u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); + + IWL_DEBUG_RX(priv, "beacon status %#x, retries:%d ibssmgr:%d " + "tsf:0x%.8x%.8x rate:%d\n", + status & TX_STATUS_MSK, + beacon->beacon_notify_hdr.failure_frame, + le32_to_cpu(beacon->ibss_mgr_status), + le32_to_cpu(beacon->high_tsf), + le32_to_cpu(beacon->low_tsf), rate); +#endif + + priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); +} + +/** + * iwl_good_plcp_health - checks for plcp error. + * + * When the plcp error is exceeding the thresholds, reset the radio + * to improve the throughput. + */ +static bool iwlagn_good_plcp_health(struct iwl_priv *priv, + struct statistics_rx_phy *cur_ofdm, + struct statistics_rx_ht_phy *cur_ofdm_ht, + unsigned int msecs) +{ + int delta; + int threshold = priv->plcp_delta_threshold; + + if (threshold == IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) { + IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n"); + return true; + } + + delta = le32_to_cpu(cur_ofdm->plcp_err) - + le32_to_cpu(priv->statistics.rx_ofdm.plcp_err) + + le32_to_cpu(cur_ofdm_ht->plcp_err) - + le32_to_cpu(priv->statistics.rx_ofdm_ht.plcp_err); + + /* Can be negative if firmware reset statistics */ + if (delta <= 0) + return true; + + if ((delta * 100 / msecs) > threshold) { + IWL_DEBUG_RADIO(priv, + "plcp health threshold %u delta %d msecs %u\n", + threshold, delta, msecs); + return false; + } + + return true; +} + +int iwl_force_rf_reset(struct iwl_priv *priv, bool external) +{ + struct iwl_rf_reset *rf_reset; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return -EAGAIN; + + if (!iwl_is_any_associated(priv)) { + IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n"); + return -ENOLINK; + } + + rf_reset = &priv->rf_reset; + rf_reset->reset_request_count++; + if (!external && rf_reset->last_reset_jiffies && + time_after(rf_reset->last_reset_jiffies + + IWL_DELAY_NEXT_FORCE_RF_RESET, jiffies)) { + IWL_DEBUG_INFO(priv, "RF reset rejected\n"); + rf_reset->reset_reject_count++; + return -EAGAIN; + } + rf_reset->reset_success_count++; + rf_reset->last_reset_jiffies = jiffies; + + /* + * There is no easy and better way to force reset the radio, + * the only known method is switching channel which will force to + * reset and tune the radio. + * Use internal short scan (single channel) operation to should + * achieve this objective. + * Driver should reset the radio when number of consecutive missed + * beacon, or any other uCode error condition detected. + */ + IWL_DEBUG_INFO(priv, "perform radio reset.\n"); + iwl_internal_short_hw_scan(priv); + return 0; +} + + +static void iwlagn_recover_from_statistics(struct iwl_priv *priv, + struct statistics_rx_phy *cur_ofdm, + struct statistics_rx_ht_phy *cur_ofdm_ht, + struct statistics_tx *tx, + unsigned long stamp) +{ + unsigned int msecs; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + msecs = jiffies_to_msecs(stamp - priv->rx_statistics_jiffies); + + /* Only gather statistics and update time stamp when not associated */ + if (!iwl_is_any_associated(priv)) + return; + + /* Do not check/recover when do not have enough statistics data */ + if (msecs < 99) + return; + + if (!iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs)) + iwl_force_rf_reset(priv, false); +} + +/* Calculate noise level, based on measurements during network silence just + * before arriving beacon. This measurement can be done only if we know + * exactly when to expect beacons, therefore only when we're associated. */ +static void iwlagn_rx_calc_noise(struct iwl_priv *priv) +{ + struct statistics_rx_non_phy *rx_info; + int num_active_rx = 0; + int total_silence = 0; + int bcn_silence_a, bcn_silence_b, bcn_silence_c; + int last_rx_noise; + + rx_info = &priv->statistics.rx_non_phy; + + bcn_silence_a = + le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; + bcn_silence_b = + le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; + bcn_silence_c = + le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; + + if (bcn_silence_a) { + total_silence += bcn_silence_a; + num_active_rx++; + } + if (bcn_silence_b) { + total_silence += bcn_silence_b; + num_active_rx++; + } + if (bcn_silence_c) { + total_silence += bcn_silence_c; + num_active_rx++; + } + + /* Average among active antennas */ + if (num_active_rx) + last_rx_noise = (total_silence / num_active_rx) - 107; + else + last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; + + IWL_DEBUG_CALIB(priv, "inband silence a %u, b %u, c %u, dBm %d\n", + bcn_silence_a, bcn_silence_b, bcn_silence_c, + last_rx_noise); +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +/* + * based on the assumption of all statistics counter are in DWORD + * FIXME: This function is for debugging, do not deal with + * the case of counters roll-over. + */ +static void accum_stats(__le32 *prev, __le32 *cur, __le32 *delta, + __le32 *max_delta, __le32 *accum, int size) +{ + int i; + + for (i = 0; + i < size / sizeof(__le32); + i++, prev++, cur++, delta++, max_delta++, accum++) { + if (le32_to_cpu(*cur) > le32_to_cpu(*prev)) { + *delta = cpu_to_le32( + le32_to_cpu(*cur) - le32_to_cpu(*prev)); + le32_add_cpu(accum, le32_to_cpu(*delta)); + if (le32_to_cpu(*delta) > le32_to_cpu(*max_delta)) + *max_delta = *delta; + } + } +} + +static void +iwlagn_accumulative_statistics(struct iwl_priv *priv, + struct statistics_general_common *common, + struct statistics_rx_non_phy *rx_non_phy, + struct statistics_rx_phy *rx_ofdm, + struct statistics_rx_ht_phy *rx_ofdm_ht, + struct statistics_rx_phy *rx_cck, + struct statistics_tx *tx, + struct statistics_bt_activity *bt_activity) +{ +#define ACCUM(_name) \ + accum_stats((__le32 *)&priv->statistics._name, \ + (__le32 *)_name, \ + (__le32 *)&priv->delta_stats._name, \ + (__le32 *)&priv->max_delta_stats._name, \ + (__le32 *)&priv->accum_stats._name, \ + sizeof(*_name)); + + ACCUM(common); + ACCUM(rx_non_phy); + ACCUM(rx_ofdm); + ACCUM(rx_ofdm_ht); + ACCUM(rx_cck); + ACCUM(tx); + if (bt_activity) + ACCUM(bt_activity); +#undef ACCUM +} +#else +static inline void +iwlagn_accumulative_statistics(struct iwl_priv *priv, + struct statistics_general_common *common, + struct statistics_rx_non_phy *rx_non_phy, + struct statistics_rx_phy *rx_ofdm, + struct statistics_rx_ht_phy *rx_ofdm_ht, + struct statistics_rx_phy *rx_cck, + struct statistics_tx *tx, + struct statistics_bt_activity *bt_activity) +{ +} +#endif + +static void iwlagn_rx_statistics(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + unsigned long stamp = jiffies; + const int reg_recalib_period = 60; + int change; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u32 len = iwl_rx_packet_payload_len(pkt); + __le32 *flag; + struct statistics_general_common *common; + struct statistics_rx_non_phy *rx_non_phy; + struct statistics_rx_phy *rx_ofdm; + struct statistics_rx_ht_phy *rx_ofdm_ht; + struct statistics_rx_phy *rx_cck; + struct statistics_tx *tx; + struct statistics_bt_activity *bt_activity; + + IWL_DEBUG_RX(priv, "Statistics notification received (%d bytes).\n", + len); + + spin_lock(&priv->statistics.lock); + + if (len == sizeof(struct iwl_bt_notif_statistics)) { + struct iwl_bt_notif_statistics *stats; + stats = (void *)&pkt->data; + flag = &stats->flag; + common = &stats->general.common; + rx_non_phy = &stats->rx.general.common; + rx_ofdm = &stats->rx.ofdm; + rx_ofdm_ht = &stats->rx.ofdm_ht; + rx_cck = &stats->rx.cck; + tx = &stats->tx; + bt_activity = &stats->general.activity; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* handle this exception directly */ + priv->statistics.num_bt_kills = stats->rx.general.num_bt_kills; + le32_add_cpu(&priv->statistics.accum_num_bt_kills, + le32_to_cpu(stats->rx.general.num_bt_kills)); +#endif + } else if (len == sizeof(struct iwl_notif_statistics)) { + struct iwl_notif_statistics *stats; + stats = (void *)&pkt->data; + flag = &stats->flag; + common = &stats->general.common; + rx_non_phy = &stats->rx.general; + rx_ofdm = &stats->rx.ofdm; + rx_ofdm_ht = &stats->rx.ofdm_ht; + rx_cck = &stats->rx.cck; + tx = &stats->tx; + bt_activity = NULL; + } else { + WARN_ONCE(1, "len %d doesn't match BT (%zu) or normal (%zu)\n", + len, sizeof(struct iwl_bt_notif_statistics), + sizeof(struct iwl_notif_statistics)); + spin_unlock(&priv->statistics.lock); + return; + } + + change = common->temperature != priv->statistics.common.temperature || + (*flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK) != + (priv->statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK); + + iwlagn_accumulative_statistics(priv, common, rx_non_phy, rx_ofdm, + rx_ofdm_ht, rx_cck, tx, bt_activity); + + iwlagn_recover_from_statistics(priv, rx_ofdm, rx_ofdm_ht, tx, stamp); + + priv->statistics.flag = *flag; + memcpy(&priv->statistics.common, common, sizeof(*common)); + memcpy(&priv->statistics.rx_non_phy, rx_non_phy, sizeof(*rx_non_phy)); + memcpy(&priv->statistics.rx_ofdm, rx_ofdm, sizeof(*rx_ofdm)); + memcpy(&priv->statistics.rx_ofdm_ht, rx_ofdm_ht, sizeof(*rx_ofdm_ht)); + memcpy(&priv->statistics.rx_cck, rx_cck, sizeof(*rx_cck)); + memcpy(&priv->statistics.tx, tx, sizeof(*tx)); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (bt_activity) + memcpy(&priv->statistics.bt_activity, bt_activity, + sizeof(*bt_activity)); +#endif + + priv->rx_statistics_jiffies = stamp; + + set_bit(STATUS_STATISTICS, &priv->status); + + /* Reschedule the statistics timer to occur in + * reg_recalib_period seconds to ensure we get a + * thermal update even if the uCode doesn't give + * us one */ + mod_timer(&priv->statistics_periodic, jiffies + + msecs_to_jiffies(reg_recalib_period * 1000)); + + if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) && + (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) { + iwlagn_rx_calc_noise(priv); + queue_work(priv->workqueue, &priv->run_time_calib_work); + } + if (priv->lib->temperature && change) + priv->lib->temperature(priv); + + spin_unlock(&priv->statistics.lock); +} + +static void iwlagn_rx_reply_statistics(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_notif_statistics *stats = (void *)pkt->data; + + if (le32_to_cpu(stats->flag) & UCODE_STATISTICS_CLEAR_MSK) { +#ifdef CONFIG_IWLWIFI_DEBUGFS + memset(&priv->accum_stats, 0, + sizeof(priv->accum_stats)); + memset(&priv->delta_stats, 0, + sizeof(priv->delta_stats)); + memset(&priv->max_delta_stats, 0, + sizeof(priv->max_delta_stats)); +#endif + IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); + } + + iwlagn_rx_statistics(priv, rxb); +} + +/* Handle notification from uCode that card's power state is changing + * due to software, hardware, or critical temperature RFKILL */ +static void iwlagn_rx_card_state_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_card_state_notif *card_state_notif = (void *)pkt->data; + u32 flags = le32_to_cpu(card_state_notif->flags); + unsigned long status = priv->status; + + IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n", + (flags & HW_CARD_DISABLED) ? "Kill" : "On", + (flags & SW_CARD_DISABLED) ? "Kill" : "On", + (flags & CT_CARD_DISABLED) ? + "Reached" : "Not reached"); + + if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | + CT_CARD_DISABLED)) { + + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C, + HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + + if (!(flags & RXON_CARD_DISABLED)) { + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C, + HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + } + if (flags & CT_CARD_DISABLED) + iwl_tt_enter_ct_kill(priv); + } + if (!(flags & CT_CARD_DISABLED)) + iwl_tt_exit_ct_kill(priv); + + if (flags & HW_CARD_DISABLED) + set_bit(STATUS_RF_KILL_HW, &priv->status); + else + clear_bit(STATUS_RF_KILL_HW, &priv->status); + + + if (!(flags & RXON_CARD_DISABLED)) + iwl_scan_cancel(priv); + + if ((test_bit(STATUS_RF_KILL_HW, &status) != + test_bit(STATUS_RF_KILL_HW, &priv->status))) + wiphy_rfkill_set_hw_state(priv->hw->wiphy, + test_bit(STATUS_RF_KILL_HW, &priv->status)); +} + +static void iwlagn_rx_missed_beacon_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) + +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_missed_beacon_notif *missed_beacon = (void *)pkt->data; + + if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > + priv->missed_beacon_threshold) { + IWL_DEBUG_CALIB(priv, + "missed bcn cnsq %d totl %d rcd %d expctd %d\n", + le32_to_cpu(missed_beacon->consecutive_missed_beacons), + le32_to_cpu(missed_beacon->total_missed_becons), + le32_to_cpu(missed_beacon->num_recvd_beacons), + le32_to_cpu(missed_beacon->num_expected_beacons)); + if (!test_bit(STATUS_SCANNING, &priv->status)) + iwl_init_sensitivity(priv); + } +} + +/* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD). + * This will be used later in iwl_rx_reply_rx() for REPLY_RX_MPDU_CMD. */ +static void iwlagn_rx_reply_rx_phy(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + priv->last_phy_res_valid = true; + priv->ampdu_ref++; + memcpy(&priv->last_phy_res, pkt->data, + sizeof(struct iwl_rx_phy_res)); +} + +/* + * returns non-zero if packet should be dropped + */ +static int iwlagn_set_decrypted_flag(struct iwl_priv *priv, + struct ieee80211_hdr *hdr, + u32 decrypt_res, + struct ieee80211_rx_status *stats) +{ + u16 fc = le16_to_cpu(hdr->frame_control); + + /* + * All contexts have the same setting here due to it being + * a module parameter, so OK to check any context. + */ + if (priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags & + RXON_FILTER_DIS_DECRYPT_MSK) + return 0; + + if (!(fc & IEEE80211_FCTL_PROTECTED)) + return 0; + + IWL_DEBUG_RX(priv, "decrypt_res:0x%x\n", decrypt_res); + switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { + case RX_RES_STATUS_SEC_TYPE_TKIP: + /* The uCode has got a bad phase 1 Key, pushes the packet. + * Decryption will be done in SW. */ + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_BAD_KEY_TTAK) + break; + + case RX_RES_STATUS_SEC_TYPE_WEP: + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_BAD_ICV_MIC) { + /* bad ICV, the packet is destroyed since the + * decryption is inplace, drop it */ + IWL_DEBUG_RX(priv, "Packet destroyed\n"); + return -1; + } + case RX_RES_STATUS_SEC_TYPE_CCMP: + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_DECRYPT_OK) { + IWL_DEBUG_RX(priv, "hw decrypt successfully!!!\n"); + stats->flag |= RX_FLAG_DECRYPTED; + } + break; + + default: + break; + } + return 0; +} + +static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, + struct ieee80211_hdr *hdr, + u16 len, + u32 ampdu_status, + struct iwl_rx_cmd_buffer *rxb, + struct ieee80211_rx_status *stats) +{ + struct sk_buff *skb; + __le16 fc = hdr->frame_control; + struct iwl_rxon_context *ctx; + unsigned int hdrlen, fraglen; + + /* We only process data packets if the interface is open */ + if (unlikely(!priv->is_open)) { + IWL_DEBUG_DROP_LIMIT(priv, + "Dropping packet while interface is not open.\n"); + return; + } + + /* In case of HW accelerated crypto and bad decryption, drop */ + if (!iwlwifi_mod_params.sw_crypto && + iwlagn_set_decrypted_flag(priv, hdr, ampdu_status, stats)) + return; + + /* Dont use dev_alloc_skb(), we'll have enough headroom once + * ieee80211_hdr pulled. + */ + skb = alloc_skb(128, GFP_ATOMIC); + if (!skb) { + IWL_ERR(priv, "alloc_skb failed\n"); + return; + } + /* If frame is small enough to fit in skb->head, pull it completely. + * If not, only pull ieee80211_hdr so that splice() or TCP coalesce + * are more efficient. + */ + hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr); + + memcpy(skb_put(skb, hdrlen), hdr, hdrlen); + fraglen = len - hdrlen; + + if (fraglen) { + int offset = (void *)hdr + hdrlen - + rxb_addr(rxb) + rxb_offset(rxb); + + skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, + fraglen, rxb->truesize); + } + + /* + * Wake any queues that were stopped due to a passive channel tx + * failure. This can happen because the regulatory enforcement in + * the device waits for a beacon before allowing transmission, + * sometimes even after already having transmitted frames for the + * association because the new RXON may reset the information. + */ + if (unlikely(ieee80211_is_beacon(fc) && priv->passive_no_rx)) { + for_each_context(priv, ctx) { + if (!ether_addr_equal(hdr->addr3, + ctx->active.bssid_addr)) + continue; + iwlagn_lift_passive_no_rx(priv); + } + } + + memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); + + ieee80211_rx_napi(priv->hw, skb, priv->napi); +} + +static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) +{ + u32 decrypt_out = 0; + + if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) == + RX_RES_STATUS_STATION_FOUND) + decrypt_out |= (RX_RES_STATUS_STATION_FOUND | + RX_RES_STATUS_NO_STATION_INFO_MISMATCH); + + decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK); + + /* packet was not encrypted */ + if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == + RX_RES_STATUS_SEC_TYPE_NONE) + return decrypt_out; + + /* packet was encrypted with unknown alg */ + if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == + RX_RES_STATUS_SEC_TYPE_ERR) + return decrypt_out; + + /* decryption was not done in HW */ + if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) != + RX_MPDU_RES_STATUS_DEC_DONE_MSK) + return decrypt_out; + + switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) { + + case RX_RES_STATUS_SEC_TYPE_CCMP: + /* alg is CCM: check MIC only */ + if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK)) + /* Bad MIC */ + decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; + else + decrypt_out |= RX_RES_STATUS_DECRYPT_OK; + + break; + + case RX_RES_STATUS_SEC_TYPE_TKIP: + if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) { + /* Bad TTAK */ + decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK; + break; + } + /* fall through if TTAK OK */ + default: + if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK)) + decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; + else + decrypt_out |= RX_RES_STATUS_DECRYPT_OK; + break; + } + + IWL_DEBUG_RX(priv, "decrypt_in:0x%x decrypt_out = 0x%x\n", + decrypt_in, decrypt_out); + + return decrypt_out; +} + +/* Calc max signal level (dBm) among 3 possible receivers */ +static int iwlagn_calc_rssi(struct iwl_priv *priv, + struct iwl_rx_phy_res *rx_resp) +{ + /* data from PHY/DSP regarding signal strength, etc., + * contents are always there, not configurable by host + */ + struct iwlagn_non_cfg_phy *ncphy = + (struct iwlagn_non_cfg_phy *)rx_resp->non_cfg_phy_buf; + u32 val, rssi_a, rssi_b, rssi_c, max_rssi; + u8 agc; + + val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_AGC_IDX]); + agc = (val & IWLAGN_OFDM_AGC_MSK) >> IWLAGN_OFDM_AGC_BIT_POS; + + /* Find max rssi among 3 possible receivers. + * These values are measured by the digital signal processor (DSP). + * They should stay fairly constant even as the signal strength varies, + * if the radio's automatic gain control (AGC) is working right. + * AGC value (see below) will provide the "interesting" info. + */ + val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_AB_IDX]); + rssi_a = (val & IWLAGN_OFDM_RSSI_INBAND_A_BITMSK) >> + IWLAGN_OFDM_RSSI_A_BIT_POS; + rssi_b = (val & IWLAGN_OFDM_RSSI_INBAND_B_BITMSK) >> + IWLAGN_OFDM_RSSI_B_BIT_POS; + val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_C_IDX]); + rssi_c = (val & IWLAGN_OFDM_RSSI_INBAND_C_BITMSK) >> + IWLAGN_OFDM_RSSI_C_BIT_POS; + + max_rssi = max_t(u32, rssi_a, rssi_b); + max_rssi = max_t(u32, max_rssi, rssi_c); + + IWL_DEBUG_STATS(priv, "Rssi In A %d B %d C %d Max %d AGC dB %d\n", + rssi_a, rssi_b, rssi_c, max_rssi, agc); + + /* dBm = max_rssi dB - agc dB - constant. + * Higher AGC (higher radio gain) means lower signal. */ + return max_rssi - agc - IWLAGN_RSSI_OFFSET; +} + +/* Called for REPLY_RX_MPDU_CMD */ +static void iwlagn_rx_reply_rx(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct ieee80211_hdr *header; + struct ieee80211_rx_status rx_status = {}; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_rx_phy_res *phy_res; + __le32 rx_pkt_status; + struct iwl_rx_mpdu_res_start *amsdu; + u32 len; + u32 ampdu_status; + u32 rate_n_flags; + + if (!priv->last_phy_res_valid) { + IWL_ERR(priv, "MPDU frame without cached PHY data\n"); + return; + } + phy_res = &priv->last_phy_res; + amsdu = (struct iwl_rx_mpdu_res_start *)pkt->data; + header = (struct ieee80211_hdr *)(pkt->data + sizeof(*amsdu)); + len = le16_to_cpu(amsdu->byte_count); + rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*amsdu) + len); + ampdu_status = iwlagn_translate_rx_status(priv, + le32_to_cpu(rx_pkt_status)); + + if ((unlikely(phy_res->cfg_phy_cnt > 20))) { + IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d\n", + phy_res->cfg_phy_cnt); + return; + } + + if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || + !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { + IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n", + le32_to_cpu(rx_pkt_status)); + return; + } + + /* This will be used in several places later */ + rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); + + /* rx_status carries information about the packet to mac80211 */ + rx_status.mactime = le64_to_cpu(phy_res->timestamp); + rx_status.band = (phy_res->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + rx_status.freq = + ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel), + rx_status.band); + rx_status.rate_idx = + iwlagn_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band); + rx_status.flag = 0; + + /* TSF isn't reliable. In order to allow smooth user experience, + * this W/A doesn't propagate it to the mac80211 */ + /*rx_status.flag |= RX_FLAG_MACTIME_START;*/ + + priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); + + /* Find max signal strength (dBm) among 3 antenna/receiver chains */ + rx_status.signal = iwlagn_calc_rssi(priv, phy_res); + + IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, TSF %llu\n", + rx_status.signal, (unsigned long long)rx_status.mactime); + + /* + * "antenna number" + * + * It seems that the antenna field in the phy flags value + * is actually a bit field. This is undefined by radiotap, + * it wants an actual antenna number but I always get "7" + * for most legacy frames I receive indicating that the + * same frame was received on all three RX chains. + * + * I think this field should be removed in favor of a + * new 802.11n radiotap field "RX chains" that is defined + * as a bitmask. + */ + rx_status.antenna = + (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) + >> RX_RES_PHY_FLAGS_ANTENNA_POS; + + /* set the preamble flag if appropriate */ + if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) + rx_status.flag |= RX_FLAG_SHORTPRE; + + if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) { + /* + * We know which subframes of an A-MPDU belong + * together since we get a single PHY response + * from the firmware for all of them + */ + rx_status.flag |= RX_FLAG_AMPDU_DETAILS; + rx_status.ampdu_reference = priv->ampdu_ref; + } + + /* Set up the HT phy flags */ + if (rate_n_flags & RATE_MCS_HT_MSK) + rx_status.flag |= RX_FLAG_HT; + if (rate_n_flags & RATE_MCS_HT40_MSK) + rx_status.flag |= RX_FLAG_40MHZ; + if (rate_n_flags & RATE_MCS_SGI_MSK) + rx_status.flag |= RX_FLAG_SHORT_GI; + if (rate_n_flags & RATE_MCS_GF_MSK) + rx_status.flag |= RX_FLAG_HT_GF; + + iwlagn_pass_packet_to_mac80211(priv, header, len, ampdu_status, + rxb, &rx_status); +} + +static void iwlagn_rx_noa_notification(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_wipan_noa_data *new_data, *old_data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_wipan_noa_notification *noa_notif = (void *)pkt->data; + + /* no condition -- we're in softirq */ + old_data = rcu_dereference_protected(priv->noa_data, true); + + if (noa_notif->noa_active) { + u32 len = le16_to_cpu(noa_notif->noa_attribute.length); + u32 copylen = len; + + /* EID, len, OUI, subtype */ + len += 1 + 1 + 3 + 1; + /* P2P id, P2P length */ + len += 1 + 2; + copylen += 1 + 2; + + new_data = kmalloc(sizeof(*new_data) + len, GFP_ATOMIC); + if (new_data) { + new_data->length = len; + new_data->data[0] = WLAN_EID_VENDOR_SPECIFIC; + new_data->data[1] = len - 2; /* not counting EID, len */ + new_data->data[2] = (WLAN_OUI_WFA >> 16) & 0xff; + new_data->data[3] = (WLAN_OUI_WFA >> 8) & 0xff; + new_data->data[4] = (WLAN_OUI_WFA >> 0) & 0xff; + new_data->data[5] = WLAN_OUI_TYPE_WFA_P2P; + memcpy(&new_data->data[6], &noa_notif->noa_attribute, + copylen); + } + } else + new_data = NULL; + + rcu_assign_pointer(priv->noa_data, new_data); + + if (old_data) + kfree_rcu(old_data, rcu_head); +} + +/** + * iwl_setup_rx_handlers - Initialize Rx handler callbacks + * + * Setup the RX handlers for each of the reply types sent from the uCode + * to the host. + */ +void iwl_setup_rx_handlers(struct iwl_priv *priv) +{ + void (**handlers)(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb); + + handlers = priv->rx_handlers; + + handlers[REPLY_ERROR] = iwlagn_rx_reply_error; + handlers[CHANNEL_SWITCH_NOTIFICATION] = iwlagn_rx_csa; + handlers[SPECTRUM_MEASURE_NOTIFICATION] = + iwlagn_rx_spectrum_measure_notif; + handlers[PM_SLEEP_NOTIFICATION] = iwlagn_rx_pm_sleep_notif; + handlers[PM_DEBUG_STATISTIC_NOTIFIC] = + iwlagn_rx_pm_debug_statistics_notif; + handlers[BEACON_NOTIFICATION] = iwlagn_rx_beacon_notif; + handlers[REPLY_ADD_STA] = iwl_add_sta_callback; + + handlers[REPLY_WIPAN_NOA_NOTIFICATION] = iwlagn_rx_noa_notification; + + /* + * The same handler is used for both the REPLY to a discrete + * statistics request from the host as well as for the periodic + * statistics notifications (after received beacons) from the uCode. + */ + handlers[REPLY_STATISTICS_CMD] = iwlagn_rx_reply_statistics; + handlers[STATISTICS_NOTIFICATION] = iwlagn_rx_statistics; + + iwl_setup_rx_scan_handlers(priv); + + handlers[CARD_STATE_NOTIFICATION] = iwlagn_rx_card_state_notif; + handlers[MISSED_BEACONS_NOTIFICATION] = + iwlagn_rx_missed_beacon_notif; + + /* Rx handlers */ + handlers[REPLY_RX_PHY_CMD] = iwlagn_rx_reply_rx_phy; + handlers[REPLY_RX_MPDU_CMD] = iwlagn_rx_reply_rx; + + /* block ack */ + handlers[REPLY_COMPRESSED_BA] = + iwlagn_rx_reply_compressed_ba; + + priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx; + + /* set up notification wait support */ + iwl_notification_wait_init(&priv->notif_wait); + + /* Set up BT Rx handlers */ + if (priv->lib->bt_params) + iwlagn_bt_rx_handler_setup(priv); +} + +void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + /* + * Do the notification wait before RX handlers so + * even if the RX handler consumes the RXB we have + * access to it in the notification wait entry. + */ + iwl_notification_wait_notify(&priv->notif_wait, pkt); + + /* Based on type of command response or notification, + * handle those that need handling via function in + * rx_handlers table. See iwl_setup_rx_handlers() */ + if (priv->rx_handlers[pkt->hdr.cmd]) { + priv->rx_handlers_stats[pkt->hdr.cmd]++; + priv->rx_handlers[pkt->hdr.cmd](priv, rxb); + } else { + /* No handling needed */ + IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n", + iwl_dvm_get_cmd_string(pkt->hdr.cmd), + pkt->hdr.cmd); + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c new file mode 100644 index 000000000000..85ceceb34fcc --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c @@ -0,0 +1,1572 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include "iwl-trans.h" +#include "iwl-modparams.h" +#include "dev.h" +#include "agn.h" +#include "calib.h" + +/* + * initialize rxon structure with default values from eeprom + */ +void iwl_connection_init_rx_config(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + memset(&ctx->staging, 0, sizeof(ctx->staging)); + + if (!ctx->vif) { + ctx->staging.dev_type = ctx->unused_devtype; + } else + switch (ctx->vif->type) { + case NL80211_IFTYPE_AP: + ctx->staging.dev_type = ctx->ap_devtype; + break; + + case NL80211_IFTYPE_STATION: + ctx->staging.dev_type = ctx->station_devtype; + ctx->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; + break; + + case NL80211_IFTYPE_ADHOC: + ctx->staging.dev_type = ctx->ibss_devtype; + ctx->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; + ctx->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK | + RXON_FILTER_ACCEPT_GRP_MSK; + break; + + case NL80211_IFTYPE_MONITOR: + ctx->staging.dev_type = RXON_DEV_TYPE_SNIFFER; + break; + + default: + IWL_ERR(priv, "Unsupported interface type %d\n", + ctx->vif->type); + break; + } + +#if 0 + /* TODO: Figure out when short_preamble would be set and cache from + * that */ + if (!hw_to_local(priv->hw)->short_preamble) + ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + else + ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; +#endif + + ctx->staging.channel = + cpu_to_le16(priv->hw->conf.chandef.chan->hw_value); + priv->band = priv->hw->conf.chandef.chan->band; + + iwl_set_flags_for_band(priv, ctx, priv->band, ctx->vif); + + /* clear both MIX and PURE40 mode flag */ + ctx->staging.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED | + RXON_FLG_CHANNEL_MODE_PURE_40); + if (ctx->vif) + memcpy(ctx->staging.node_addr, ctx->vif->addr, ETH_ALEN); + + ctx->staging.ofdm_ht_single_stream_basic_rates = 0xff; + ctx->staging.ofdm_ht_dual_stream_basic_rates = 0xff; + ctx->staging.ofdm_ht_triple_stream_basic_rates = 0xff; +} + +static int iwlagn_disable_bss(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_rxon_cmd *send) +{ + __le32 old_filter = send->filter_flags; + int ret; + + send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, + 0, sizeof(*send), send); + + send->filter_flags = old_filter; + + if (ret) + IWL_DEBUG_QUIET_RFKILL(priv, + "Error clearing ASSOC_MSK on BSS (%d)\n", ret); + + return ret; +} + +static int iwlagn_disable_pan(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_rxon_cmd *send) +{ + struct iwl_notification_wait disable_wait; + __le32 old_filter = send->filter_flags; + u8 old_dev_type = send->dev_type; + int ret; + static const u16 deactivate_cmd[] = { + REPLY_WIPAN_DEACTIVATION_COMPLETE + }; + + iwl_init_notification_wait(&priv->notif_wait, &disable_wait, + deactivate_cmd, ARRAY_SIZE(deactivate_cmd), + NULL, NULL); + + send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + send->dev_type = RXON_DEV_TYPE_P2P; + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, + 0, sizeof(*send), send); + + send->filter_flags = old_filter; + send->dev_type = old_dev_type; + + if (ret) { + IWL_ERR(priv, "Error disabling PAN (%d)\n", ret); + iwl_remove_notification(&priv->notif_wait, &disable_wait); + } else { + ret = iwl_wait_notification(&priv->notif_wait, + &disable_wait, HZ); + if (ret) + IWL_ERR(priv, "Timed out waiting for PAN disable\n"); + } + + return ret; +} + +static int iwlagn_disconn_pan(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_rxon_cmd *send) +{ + __le32 old_filter = send->filter_flags; + int ret; + + send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0, + sizeof(*send), send); + + send->filter_flags = old_filter; + + return ret; +} + +static void iwlagn_update_qos(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int ret; + + if (!ctx->is_active) + return; + + ctx->qos_data.def_qos_parm.qos_flags = 0; + + if (ctx->qos_data.qos_active) + ctx->qos_data.def_qos_parm.qos_flags |= + QOS_PARAM_FLG_UPDATE_EDCA_MSK; + + if (ctx->ht.enabled) + ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; + + IWL_DEBUG_INFO(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n", + ctx->qos_data.qos_active, + ctx->qos_data.def_qos_parm.qos_flags); + + ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, 0, + sizeof(struct iwl_qosparam_cmd), + &ctx->qos_data.def_qos_parm); + if (ret) + IWL_DEBUG_QUIET_RFKILL(priv, "Failed to update QoS\n"); +} + +static int iwlagn_update_beacon(struct iwl_priv *priv, + struct ieee80211_vif *vif) +{ + lockdep_assert_held(&priv->mutex); + + dev_kfree_skb(priv->beacon_skb); + priv->beacon_skb = ieee80211_beacon_get(priv->hw, vif); + if (!priv->beacon_skb) + return -ENOMEM; + return iwlagn_send_beacon_cmd(priv); +} + +static int iwlagn_send_rxon_assoc(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int ret = 0; + struct iwl_rxon_assoc_cmd rxon_assoc; + const struct iwl_rxon_cmd *rxon1 = &ctx->staging; + const struct iwl_rxon_cmd *rxon2 = &ctx->active; + + if ((rxon1->flags == rxon2->flags) && + (rxon1->filter_flags == rxon2->filter_flags) && + (rxon1->cck_basic_rates == rxon2->cck_basic_rates) && + (rxon1->ofdm_ht_single_stream_basic_rates == + rxon2->ofdm_ht_single_stream_basic_rates) && + (rxon1->ofdm_ht_dual_stream_basic_rates == + rxon2->ofdm_ht_dual_stream_basic_rates) && + (rxon1->ofdm_ht_triple_stream_basic_rates == + rxon2->ofdm_ht_triple_stream_basic_rates) && + (rxon1->acquisition_data == rxon2->acquisition_data) && + (rxon1->rx_chain == rxon2->rx_chain) && + (rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates)) { + IWL_DEBUG_INFO(priv, "Using current RXON_ASSOC. Not resending.\n"); + return 0; + } + + rxon_assoc.flags = ctx->staging.flags; + rxon_assoc.filter_flags = ctx->staging.filter_flags; + rxon_assoc.ofdm_basic_rates = ctx->staging.ofdm_basic_rates; + rxon_assoc.cck_basic_rates = ctx->staging.cck_basic_rates; + rxon_assoc.reserved1 = 0; + rxon_assoc.reserved2 = 0; + rxon_assoc.reserved3 = 0; + rxon_assoc.ofdm_ht_single_stream_basic_rates = + ctx->staging.ofdm_ht_single_stream_basic_rates; + rxon_assoc.ofdm_ht_dual_stream_basic_rates = + ctx->staging.ofdm_ht_dual_stream_basic_rates; + rxon_assoc.rx_chain_select_flags = ctx->staging.rx_chain; + rxon_assoc.ofdm_ht_triple_stream_basic_rates = + ctx->staging.ofdm_ht_triple_stream_basic_rates; + rxon_assoc.acquisition_data = ctx->staging.acquisition_data; + + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_assoc_cmd, + CMD_ASYNC, sizeof(rxon_assoc), &rxon_assoc); + return ret; +} + +static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) +{ + u16 new_val; + u16 beacon_factor; + + /* + * If mac80211 hasn't given us a beacon interval, program + * the default into the device (not checking this here + * would cause the adjustment below to return the maximum + * value, which may break PAN.) + */ + if (!beacon_val) + return DEFAULT_BEACON_INTERVAL; + + /* + * If the beacon interval we obtained from the peer + * is too large, we'll have to wake up more often + * (and in IBSS case, we'll beacon too much) + * + * For example, if max_beacon_val is 4096, and the + * requested beacon interval is 7000, we'll have to + * use 3500 to be able to wake up on the beacons. + * + * This could badly influence beacon detection stats. + */ + + beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; + new_val = beacon_val / beacon_factor; + + if (!new_val) + new_val = max_beacon_val; + + return new_val; +} + +static int iwl_send_rxon_timing(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + u64 tsf; + s32 interval_tm, rem; + struct ieee80211_conf *conf = NULL; + u16 beacon_int; + struct ieee80211_vif *vif = ctx->vif; + + conf = &priv->hw->conf; + + lockdep_assert_held(&priv->mutex); + + memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd)); + + ctx->timing.timestamp = cpu_to_le64(priv->timestamp); + ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval); + + beacon_int = vif ? vif->bss_conf.beacon_int : 0; + + /* + * TODO: For IBSS we need to get atim_window from mac80211, + * for now just always use 0 + */ + ctx->timing.atim_window = 0; + + if (ctx->ctxid == IWL_RXON_CTX_PAN && + (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) && + iwl_is_associated(priv, IWL_RXON_CTX_BSS) && + priv->contexts[IWL_RXON_CTX_BSS].vif && + priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) { + ctx->timing.beacon_interval = + priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval; + beacon_int = le16_to_cpu(ctx->timing.beacon_interval); + } else if (ctx->ctxid == IWL_RXON_CTX_BSS && + iwl_is_associated(priv, IWL_RXON_CTX_PAN) && + priv->contexts[IWL_RXON_CTX_PAN].vif && + priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int && + (!iwl_is_associated_ctx(ctx) || !ctx->vif || + !ctx->vif->bss_conf.beacon_int)) { + ctx->timing.beacon_interval = + priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval; + beacon_int = le16_to_cpu(ctx->timing.beacon_interval); + } else { + beacon_int = iwl_adjust_beacon_interval(beacon_int, + IWL_MAX_UCODE_BEACON_INTERVAL * TIME_UNIT); + ctx->timing.beacon_interval = cpu_to_le16(beacon_int); + } + + ctx->beacon_int = beacon_int; + + tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */ + interval_tm = beacon_int * TIME_UNIT; + rem = do_div(tsf, interval_tm); + ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); + + ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1; + + IWL_DEBUG_ASSOC(priv, + "beacon interval %d beacon timer %d beacon tim %d\n", + le16_to_cpu(ctx->timing.beacon_interval), + le32_to_cpu(ctx->timing.beacon_init_val), + le16_to_cpu(ctx->timing.atim_window)); + + return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd, + 0, sizeof(ctx->timing), &ctx->timing); +} + +static int iwlagn_rxon_disconn(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int ret; + struct iwl_rxon_cmd *active = (void *)&ctx->active; + + if (ctx->ctxid == IWL_RXON_CTX_BSS) { + ret = iwlagn_disable_bss(priv, ctx, &ctx->staging); + } else { + ret = iwlagn_disable_pan(priv, ctx, &ctx->staging); + if (ret) + return ret; + if (ctx->vif) { + ret = iwl_send_rxon_timing(priv, ctx); + if (ret) { + IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); + return ret; + } + ret = iwlagn_disconn_pan(priv, ctx, &ctx->staging); + } + } + if (ret) + return ret; + + /* + * Un-assoc RXON clears the station table and WEP + * keys, so we have to restore those afterwards. + */ + iwl_clear_ucode_stations(priv, ctx); + /* update -- might need P2P now */ + iwl_update_bcast_station(priv, ctx); + iwl_restore_stations(priv, ctx); + ret = iwl_restore_default_wep_keys(priv, ctx); + if (ret) { + IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret); + return ret; + } + + memcpy(active, &ctx->staging, sizeof(*active)); + return 0; +} + +static int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) +{ + int ret; + s8 prev_tx_power; + bool defer; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + if (priv->calib_disabled & IWL_TX_POWER_CALIB_DISABLED) + return 0; + + lockdep_assert_held(&priv->mutex); + + if (priv->tx_power_user_lmt == tx_power && !force) + return 0; + + if (tx_power < IWLAGN_TX_POWER_TARGET_POWER_MIN) { + IWL_WARN(priv, + "Requested user TXPOWER %d below lower limit %d.\n", + tx_power, + IWLAGN_TX_POWER_TARGET_POWER_MIN); + return -EINVAL; + } + + if (tx_power > DIV_ROUND_UP(priv->nvm_data->max_tx_pwr_half_dbm, 2)) { + IWL_WARN(priv, + "Requested user TXPOWER %d above upper limit %d.\n", + tx_power, priv->nvm_data->max_tx_pwr_half_dbm); + return -EINVAL; + } + + if (!iwl_is_ready_rf(priv)) + return -EIO; + + /* scan complete and commit_rxon use tx_power_next value, + * it always need to be updated for newest request */ + priv->tx_power_next = tx_power; + + /* do not set tx power when scanning or channel changing */ + defer = test_bit(STATUS_SCANNING, &priv->status) || + memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging)); + if (defer && !force) { + IWL_DEBUG_INFO(priv, "Deferring tx power set\n"); + return 0; + } + + prev_tx_power = priv->tx_power_user_lmt; + priv->tx_power_user_lmt = tx_power; + + ret = iwlagn_send_tx_power(priv); + + /* if fail to set tx_power, restore the orig. tx power */ + if (ret) { + priv->tx_power_user_lmt = prev_tx_power; + priv->tx_power_next = prev_tx_power; + } + return ret; +} + +static int iwlagn_rxon_connect(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int ret; + struct iwl_rxon_cmd *active = (void *)&ctx->active; + + /* RXON timing must be before associated RXON */ + if (ctx->ctxid == IWL_RXON_CTX_BSS) { + ret = iwl_send_rxon_timing(priv, ctx); + if (ret) { + IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); + return ret; + } + } + /* QoS info may be cleared by previous un-assoc RXON */ + iwlagn_update_qos(priv, ctx); + + /* + * We'll run into this code path when beaconing is + * enabled, but then we also need to send the beacon + * to the device. + */ + if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_AP)) { + ret = iwlagn_update_beacon(priv, ctx->vif); + if (ret) { + IWL_ERR(priv, + "Error sending required beacon (%d)!\n", + ret); + return ret; + } + } + + priv->start_calib = 0; + /* + * Apply the new configuration. + * + * Associated RXON doesn't clear the station table in uCode, + * so we don't need to restore stations etc. after this. + */ + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0, + sizeof(struct iwl_rxon_cmd), &ctx->staging); + if (ret) { + IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); + return ret; + } + memcpy(active, &ctx->staging, sizeof(*active)); + + /* IBSS beacon needs to be sent after setting assoc */ + if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_ADHOC)) + if (iwlagn_update_beacon(priv, ctx->vif)) + IWL_ERR(priv, "Error sending IBSS beacon\n"); + iwl_init_sensitivity(priv); + + /* + * If we issue a new RXON command which required a tune then + * we must send a new TXPOWER command or we won't be able to + * Tx any frames. + * + * It's expected we set power here if channel is changing. + */ + ret = iwl_set_tx_power(priv, priv->tx_power_next, true); + if (ret) { + IWL_ERR(priv, "Error sending TX power (%d)\n", ret); + return ret; + } + + if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && + priv->cfg->ht_params && priv->cfg->ht_params->smps_mode) + ieee80211_request_smps(ctx->vif, + priv->cfg->ht_params->smps_mode); + + return 0; +} + +int iwlagn_set_pan_params(struct iwl_priv *priv) +{ + struct iwl_wipan_params_cmd cmd; + struct iwl_rxon_context *ctx_bss, *ctx_pan; + int slot0 = 300, slot1 = 0; + int ret; + + if (priv->valid_contexts == BIT(IWL_RXON_CTX_BSS)) + return 0; + + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); + + lockdep_assert_held(&priv->mutex); + + ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS]; + ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN]; + + /* + * If the PAN context is inactive, then we don't need + * to update the PAN parameters, the last thing we'll + * have done before it goes inactive is making the PAN + * parameters be WLAN-only. + */ + if (!ctx_pan->is_active) + return 0; + + memset(&cmd, 0, sizeof(cmd)); + + /* only 2 slots are currently allowed */ + cmd.num_slots = 2; + + cmd.slots[0].type = 0; /* BSS */ + cmd.slots[1].type = 1; /* PAN */ + + if (ctx_bss->vif && ctx_pan->vif) { + int bcnint = ctx_pan->beacon_int; + int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1; + + /* should be set, but seems unused?? */ + cmd.flags |= cpu_to_le16(IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE); + + if (ctx_pan->vif->type == NL80211_IFTYPE_AP && + bcnint && + bcnint != ctx_bss->beacon_int) { + IWL_ERR(priv, + "beacon intervals don't match (%d, %d)\n", + ctx_bss->beacon_int, ctx_pan->beacon_int); + } else + bcnint = max_t(int, bcnint, + ctx_bss->beacon_int); + if (!bcnint) + bcnint = DEFAULT_BEACON_INTERVAL; + slot0 = bcnint / 2; + slot1 = bcnint - slot0; + + if (test_bit(STATUS_SCAN_HW, &priv->status) || + (!ctx_bss->vif->bss_conf.idle && + !ctx_bss->vif->bss_conf.assoc)) { + slot0 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME; + slot1 = IWL_MIN_SLOT_TIME; + } else if (!ctx_pan->vif->bss_conf.idle && + !ctx_pan->vif->bss_conf.assoc) { + slot1 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME; + slot0 = IWL_MIN_SLOT_TIME; + } + } else if (ctx_pan->vif) { + slot0 = 0; + slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) * + ctx_pan->beacon_int; + slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1); + + if (test_bit(STATUS_SCAN_HW, &priv->status)) { + slot0 = slot1 * 3 - IWL_MIN_SLOT_TIME; + slot1 = IWL_MIN_SLOT_TIME; + } + } + + cmd.slots[0].width = cpu_to_le16(slot0); + cmd.slots[1].width = cpu_to_le16(slot1); + + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, 0, + sizeof(cmd), &cmd); + if (ret) + IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret); + + return ret; +} + +static void _iwl_set_rxon_ht(struct iwl_priv *priv, + struct iwl_ht_config *ht_conf, + struct iwl_rxon_context *ctx) +{ + struct iwl_rxon_cmd *rxon = &ctx->staging; + + if (!ctx->ht.enabled) { + rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | + RXON_FLG_HT40_PROT_MSK | + RXON_FLG_HT_PROT_MSK); + return; + } + + /* FIXME: if the definition of ht.protection changed, the "translation" + * will be needed for rxon->flags + */ + rxon->flags |= cpu_to_le32(ctx->ht.protection << + RXON_FLG_HT_OPERATING_MODE_POS); + + /* Set up channel bandwidth: + * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ + /* clear the HT channel mode before set the mode */ + rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); + if (iwl_is_ht40_tx_allowed(priv, ctx, NULL)) { + /* pure ht40 */ + if (ctx->ht.protection == + IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { + rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; + /* + * Note: control channel is opposite of extension + * channel + */ + switch (ctx->ht.extension_chan_offset) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + rxon->flags &= + ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + rxon->flags |= + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + break; + } + } else { + /* + * Note: control channel is opposite of extension + * channel + */ + switch (ctx->ht.extension_chan_offset) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + rxon->flags &= + ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); + rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; + break; + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + default: + /* + * channel location only valid if in Mixed + * mode + */ + IWL_ERR(priv, + "invalid extension channel offset\n"); + break; + } + } + } else { + rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY; + } + + iwlagn_set_rxon_chain(priv, ctx); + + IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X " + "extension channel offset 0x%x\n", + le32_to_cpu(rxon->flags), ctx->ht.protection, + ctx->ht.extension_chan_offset); +} + +void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) +{ + struct iwl_rxon_context *ctx; + + for_each_context(priv, ctx) + _iwl_set_rxon_ht(priv, ht_conf, ctx); +} + +/** + * iwl_set_rxon_channel - Set the band and channel values in staging RXON + * @ch: requested channel as a pointer to struct ieee80211_channel + + * NOTE: Does not commit to the hardware; it sets appropriate bit fields + * in the staging RXON flag structure based on the ch->band + */ +void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, + struct iwl_rxon_context *ctx) +{ + enum ieee80211_band band = ch->band; + u16 channel = ch->hw_value; + + if ((le16_to_cpu(ctx->staging.channel) == channel) && + (priv->band == band)) + return; + + ctx->staging.channel = cpu_to_le16(channel); + if (band == IEEE80211_BAND_5GHZ) + ctx->staging.flags &= ~RXON_FLG_BAND_24G_MSK; + else + ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; + + priv->band = band; + + IWL_DEBUG_INFO(priv, "Staging channel set to %d [%d]\n", channel, band); + +} + +void iwl_set_flags_for_band(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + enum ieee80211_band band, + struct ieee80211_vif *vif) +{ + if (band == IEEE80211_BAND_5GHZ) { + ctx->staging.flags &= + ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK + | RXON_FLG_CCK_MSK); + ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + } else { + /* Copied from iwl_post_associate() */ + if (vif && vif->bss_conf.use_short_slot) + ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + + ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; + ctx->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; + ctx->staging.flags &= ~RXON_FLG_CCK_MSK; + } +} + +static void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, int hw_decrypt) +{ + struct iwl_rxon_cmd *rxon = &ctx->staging; + + if (hw_decrypt) + rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; + else + rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; + +} + +/* validate RXON structure is valid */ +static int iwl_check_rxon_cmd(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct iwl_rxon_cmd *rxon = &ctx->staging; + u32 errors = 0; + + if (rxon->flags & RXON_FLG_BAND_24G_MSK) { + if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) { + IWL_WARN(priv, "check 2.4G: wrong narrow\n"); + errors |= BIT(0); + } + if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) { + IWL_WARN(priv, "check 2.4G: wrong radar\n"); + errors |= BIT(1); + } + } else { + if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) { + IWL_WARN(priv, "check 5.2G: not short slot!\n"); + errors |= BIT(2); + } + if (rxon->flags & RXON_FLG_CCK_MSK) { + IWL_WARN(priv, "check 5.2G: CCK!\n"); + errors |= BIT(3); + } + } + if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) { + IWL_WARN(priv, "mac/bssid mcast!\n"); + errors |= BIT(4); + } + + /* make sure basic rates 6Mbps and 1Mbps are supported */ + if ((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0 && + (rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0) { + IWL_WARN(priv, "neither 1 nor 6 are basic\n"); + errors |= BIT(5); + } + + if (le16_to_cpu(rxon->assoc_id) > 2007) { + IWL_WARN(priv, "aid > 2007\n"); + errors |= BIT(6); + } + + if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) + == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) { + IWL_WARN(priv, "CCK and short slot\n"); + errors |= BIT(7); + } + + if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) + == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { + IWL_WARN(priv, "CCK and auto detect\n"); + errors |= BIT(8); + } + + if ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK | + RXON_FLG_TGG_PROTECT_MSK)) == + RXON_FLG_TGG_PROTECT_MSK) { + IWL_WARN(priv, "TGg but no auto-detect\n"); + errors |= BIT(9); + } + + if (rxon->channel == 0) { + IWL_WARN(priv, "zero channel is invalid\n"); + errors |= BIT(10); + } + + WARN(errors, "Invalid RXON (%#x), channel %d", + errors, le16_to_cpu(rxon->channel)); + + return errors ? -EINVAL : 0; +} + +/** + * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed + * @priv: staging_rxon is compared to active_rxon + * + * If the RXON structure is changing enough to require a new tune, + * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that + * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. + */ +static int iwl_full_rxon_required(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + const struct iwl_rxon_cmd *staging = &ctx->staging; + const struct iwl_rxon_cmd *active = &ctx->active; + +#define CHK(cond) \ + if ((cond)) { \ + IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n"); \ + return 1; \ + } + +#define CHK_NEQ(c1, c2) \ + if ((c1) != (c2)) { \ + IWL_DEBUG_INFO(priv, "need full RXON - " \ + #c1 " != " #c2 " - %d != %d\n", \ + (c1), (c2)); \ + return 1; \ + } + + /* These items are only settable from the full RXON command */ + CHK(!iwl_is_associated_ctx(ctx)); + CHK(!ether_addr_equal(staging->bssid_addr, active->bssid_addr)); + CHK(!ether_addr_equal(staging->node_addr, active->node_addr)); + CHK(!ether_addr_equal(staging->wlap_bssid_addr, + active->wlap_bssid_addr)); + CHK_NEQ(staging->dev_type, active->dev_type); + CHK_NEQ(staging->channel, active->channel); + CHK_NEQ(staging->air_propagation, active->air_propagation); + CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, + active->ofdm_ht_single_stream_basic_rates); + CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, + active->ofdm_ht_dual_stream_basic_rates); + CHK_NEQ(staging->ofdm_ht_triple_stream_basic_rates, + active->ofdm_ht_triple_stream_basic_rates); + CHK_NEQ(staging->assoc_id, active->assoc_id); + + /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can + * be updated with the RXON_ASSOC command -- however only some + * flag transitions are allowed using RXON_ASSOC */ + + /* Check if we are not switching bands */ + CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, + active->flags & RXON_FLG_BAND_24G_MSK); + + /* Check if we are switching association toggle */ + CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, + active->filter_flags & RXON_FILTER_ASSOC_MSK); + +#undef CHK +#undef CHK_NEQ + + return 0; +} + +#ifdef CONFIG_IWLWIFI_DEBUG +void iwl_print_rx_config_cmd(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid) +{ + struct iwl_rxon_context *ctx = &priv->contexts[ctxid]; + struct iwl_rxon_cmd *rxon = &ctx->staging; + + IWL_DEBUG_RADIO(priv, "RX CONFIG:\n"); + iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); + IWL_DEBUG_RADIO(priv, "u16 channel: 0x%x\n", + le16_to_cpu(rxon->channel)); + IWL_DEBUG_RADIO(priv, "u32 flags: 0x%08X\n", + le32_to_cpu(rxon->flags)); + IWL_DEBUG_RADIO(priv, "u32 filter_flags: 0x%08x\n", + le32_to_cpu(rxon->filter_flags)); + IWL_DEBUG_RADIO(priv, "u8 dev_type: 0x%x\n", rxon->dev_type); + IWL_DEBUG_RADIO(priv, "u8 ofdm_basic_rates: 0x%02x\n", + rxon->ofdm_basic_rates); + IWL_DEBUG_RADIO(priv, "u8 cck_basic_rates: 0x%02x\n", + rxon->cck_basic_rates); + IWL_DEBUG_RADIO(priv, "u8[6] node_addr: %pM\n", rxon->node_addr); + IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr); + IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", + le16_to_cpu(rxon->assoc_id)); +} +#endif + +static void iwl_calc_basic_rates(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int lowest_present_ofdm = 100; + int lowest_present_cck = 100; + u8 cck = 0; + u8 ofdm = 0; + + if (ctx->vif) { + struct ieee80211_supported_band *sband; + unsigned long basic = ctx->vif->bss_conf.basic_rates; + int i; + + sband = priv->hw->wiphy->bands[priv->hw->conf.chandef.chan->band]; + + for_each_set_bit(i, &basic, BITS_PER_LONG) { + int hw = sband->bitrates[i].hw_value; + if (hw >= IWL_FIRST_OFDM_RATE) { + ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE); + if (lowest_present_ofdm > hw) + lowest_present_ofdm = hw; + } else { + BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); + + cck |= BIT(hw); + if (lowest_present_cck > hw) + lowest_present_cck = hw; + } + } + } + + /* + * Now we've got the basic rates as bitmaps in the ofdm and cck + * variables. This isn't sufficient though, as there might not + * be all the right rates in the bitmap. E.g. if the only basic + * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps + * and 6 Mbps because the 802.11-2007 standard says in 9.6: + * + * [...] a STA responding to a received frame shall transmit + * its Control Response frame [...] at the highest rate in the + * BSSBasicRateSet parameter that is less than or equal to the + * rate of the immediately previous frame in the frame exchange + * sequence ([...]) and that is of the same modulation class + * ([...]) as the received frame. If no rate contained in the + * BSSBasicRateSet parameter meets these conditions, then the + * control frame sent in response to a received frame shall be + * transmitted at the highest mandatory rate of the PHY that is + * less than or equal to the rate of the received frame, and + * that is of the same modulation class as the received frame. + * + * As a consequence, we need to add all mandatory rates that are + * lower than all of the basic rates to these bitmaps. + */ + + if (IWL_RATE_24M_INDEX < lowest_present_ofdm) + ofdm |= IWL_RATE_24M_MASK >> IWL_FIRST_OFDM_RATE; + if (IWL_RATE_12M_INDEX < lowest_present_ofdm) + ofdm |= IWL_RATE_12M_MASK >> IWL_FIRST_OFDM_RATE; + /* 6M already there or needed so always add */ + ofdm |= IWL_RATE_6M_MASK >> IWL_FIRST_OFDM_RATE; + + /* + * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. + * Note, however: + * - if no CCK rates are basic, it must be ERP since there must + * be some basic rates at all, so they're OFDM => ERP PHY + * (or we're in 5 GHz, and the cck bitmap will never be used) + * - if 11M is a basic rate, it must be ERP as well, so add 5.5M + * - if 5.5M is basic, 1M and 2M are mandatory + * - if 2M is basic, 1M is mandatory + * - if 1M is basic, that's the only valid ACK rate. + * As a consequence, it's not as complicated as it sounds, just add + * any lower rates to the ACK rate bitmap. + */ + if (IWL_RATE_11M_INDEX < lowest_present_cck) + cck |= IWL_RATE_11M_MASK >> IWL_FIRST_CCK_RATE; + if (IWL_RATE_5M_INDEX < lowest_present_cck) + cck |= IWL_RATE_5M_MASK >> IWL_FIRST_CCK_RATE; + if (IWL_RATE_2M_INDEX < lowest_present_cck) + cck |= IWL_RATE_2M_MASK >> IWL_FIRST_CCK_RATE; + /* 1M already there or needed so always add */ + cck |= IWL_RATE_1M_MASK >> IWL_FIRST_CCK_RATE; + + IWL_DEBUG_RATE(priv, "Set basic rates cck:0x%.2x ofdm:0x%.2x\n", + cck, ofdm); + + /* "basic_rates" is a misnomer here -- should be called ACK rates */ + ctx->staging.cck_basic_rates = cck; + ctx->staging.ofdm_basic_rates = ofdm; +} + +/** + * iwlagn_commit_rxon - commit staging_rxon to hardware + * + * The RXON command in staging_rxon is committed to the hardware and + * the active_rxon structure is updated with the new data. This + * function correctly transitions out of the RXON_ASSOC_MSK state if + * a HW tune is required based on the RXON structure changes. + * + * The connect/disconnect flow should be as the following: + * + * 1. make sure send RXON command with association bit unset if not connect + * this should include the channel and the band for the candidate + * to be connected to + * 2. Add Station before RXON association with the AP + * 3. RXON_timing has to send before RXON for connection + * 4. full RXON command - associated bit set + * 5. use RXON_ASSOC command to update any flags changes + */ +int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + /* cast away the const for active_rxon in this function */ + struct iwl_rxon_cmd *active = (void *)&ctx->active; + bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK); + int ret; + + lockdep_assert_held(&priv->mutex); + + if (!iwl_is_alive(priv)) + return -EBUSY; + + /* This function hardcodes a bunch of dual-mode assumptions */ + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); + + if (!ctx->is_active) + return 0; + + /* always get timestamp with Rx frame */ + ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK; + + /* recalculate basic rates */ + iwl_calc_basic_rates(priv, ctx); + + /* + * force CTS-to-self frames protection if RTS-CTS is not preferred + * one aggregation protection method + */ + if (!priv->hw_params.use_rts_for_aggregation) + ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; + + if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) || + !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK)) + ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + + iwl_print_rx_config_cmd(priv, ctx->ctxid); + ret = iwl_check_rxon_cmd(priv, ctx); + if (ret) { + IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n"); + return -EINVAL; + } + + /* + * receive commit_rxon request + * abort any previous channel switch if still in process + */ + if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) && + (priv->switch_channel != ctx->staging.channel)) { + IWL_DEBUG_11H(priv, "abort channel switch on %d\n", + le16_to_cpu(priv->switch_channel)); + iwl_chswitch_done(priv, false); + } + + /* + * If we don't need to send a full RXON, we can use + * iwl_rxon_assoc_cmd which is used to reconfigure filter + * and other flags for the current radio configuration. + */ + if (!iwl_full_rxon_required(priv, ctx)) { + ret = iwlagn_send_rxon_assoc(priv, ctx); + if (ret) { + IWL_ERR(priv, "Error setting RXON_ASSOC (%d)\n", ret); + return ret; + } + + memcpy(active, &ctx->staging, sizeof(*active)); + /* + * We do not commit tx power settings while channel changing, + * do it now if after settings changed. + */ + iwl_set_tx_power(priv, priv->tx_power_next, false); + + /* make sure we are in the right PS state */ + iwl_power_update_mode(priv, true); + + return 0; + } + + iwl_set_rxon_hwcrypto(priv, ctx, !iwlwifi_mod_params.sw_crypto); + + IWL_DEBUG_INFO(priv, + "Going to commit RXON\n" + " * with%s RXON_FILTER_ASSOC_MSK\n" + " * channel = %d\n" + " * bssid = %pM\n", + (new_assoc ? "" : "out"), + le16_to_cpu(ctx->staging.channel), + ctx->staging.bssid_addr); + + /* + * Always clear associated first, but with the correct config. + * This is required as for example station addition for the + * AP station must be done after the BSSID is set to correctly + * set up filters in the device. + */ + ret = iwlagn_rxon_disconn(priv, ctx); + if (ret) + return ret; + + ret = iwlagn_set_pan_params(priv); + if (ret) + return ret; + + if (new_assoc) + return iwlagn_rxon_connect(priv, ctx); + + return 0; +} + +void iwlagn_config_ht40(struct ieee80211_conf *conf, + struct iwl_rxon_context *ctx) +{ + if (conf_is_ht40_minus(conf)) { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_BELOW; + ctx->ht.is_40mhz = true; + } else if (conf_is_ht40_plus(conf)) { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + ctx->ht.is_40mhz = true; + } else { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_NONE; + ctx->ht.is_40mhz = false; + } +} + +int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_channel *channel = conf->chandef.chan; + int ret = 0; + + IWL_DEBUG_MAC80211(priv, "enter: changed %#x\n", changed); + + mutex_lock(&priv->mutex); + + if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) { + IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); + goto out; + } + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); + goto out; + } + + if (changed & (IEEE80211_CONF_CHANGE_SMPS | + IEEE80211_CONF_CHANGE_CHANNEL)) { + /* mac80211 uses static for non-HT which is what we want */ + priv->current_ht_config.smps = conf->smps_mode; + + /* + * Recalculate chain counts. + * + * If monitor mode is enabled then mac80211 will + * set up the SM PS mode to OFF if an HT channel is + * configured. + */ + for_each_context(priv, ctx) + iwlagn_set_rxon_chain(priv, ctx); + } + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + for_each_context(priv, ctx) { + /* Configure HT40 channels */ + if (ctx->ht.enabled != conf_is_ht(conf)) + ctx->ht.enabled = conf_is_ht(conf); + + if (ctx->ht.enabled) { + /* if HT40 is used, it should not change + * after associated except channel switch */ + if (!ctx->ht.is_40mhz || + !iwl_is_associated_ctx(ctx)) + iwlagn_config_ht40(conf, ctx); + } else + ctx->ht.is_40mhz = false; + + /* + * Default to no protection. Protection mode will + * later be set from BSS config in iwl_ht_conf + */ + ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; + + /* if we are switching from ht to 2.4 clear flags + * from any ht related info since 2.4 does not + * support ht */ + if (le16_to_cpu(ctx->staging.channel) != + channel->hw_value) + ctx->staging.flags = 0; + + iwl_set_rxon_channel(priv, channel, ctx); + iwl_set_rxon_ht(priv, &priv->current_ht_config); + + iwl_set_flags_for_band(priv, ctx, channel->band, + ctx->vif); + } + + iwl_update_bcast_stations(priv); + } + + if (changed & (IEEE80211_CONF_CHANGE_PS | + IEEE80211_CONF_CHANGE_IDLE)) { + ret = iwl_power_update_mode(priv, false); + if (ret) + IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n"); + } + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n", + priv->tx_power_user_lmt, conf->power_level); + + iwl_set_tx_power(priv, conf->power_level, false); + } + + for_each_context(priv, ctx) { + if (!memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) + continue; + iwlagn_commit_rxon(priv, ctx); + } + out: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static void iwlagn_check_needed_chains(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_bss_conf *bss_conf) +{ + struct ieee80211_vif *vif = ctx->vif; + struct iwl_rxon_context *tmp; + struct ieee80211_sta *sta; + struct iwl_ht_config *ht_conf = &priv->current_ht_config; + struct ieee80211_sta_ht_cap *ht_cap; + bool need_multiple; + + lockdep_assert_held(&priv->mutex); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + rcu_read_lock(); + sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (!sta) { + /* + * If at all, this can only happen through a race + * when the AP disconnects us while we're still + * setting up the connection, in that case mac80211 + * will soon tell us about that. + */ + need_multiple = false; + rcu_read_unlock(); + break; + } + + ht_cap = &sta->ht_cap; + + need_multiple = true; + + /* + * If the peer advertises no support for receiving 2 and 3 + * stream MCS rates, it can't be transmitting them either. + */ + if (ht_cap->mcs.rx_mask[1] == 0 && + ht_cap->mcs.rx_mask[2] == 0) { + need_multiple = false; + } else if (!(ht_cap->mcs.tx_params & + IEEE80211_HT_MCS_TX_DEFINED)) { + /* If it can't TX MCS at all ... */ + need_multiple = false; + } else if (ht_cap->mcs.tx_params & + IEEE80211_HT_MCS_TX_RX_DIFF) { + int maxstreams; + + /* + * But if it can receive them, it might still not + * be able to transmit them, which is what we need + * to check here -- so check the number of streams + * it advertises for TX (if different from RX). + */ + + maxstreams = (ht_cap->mcs.tx_params & + IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK); + maxstreams >>= + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + maxstreams += 1; + + if (maxstreams <= 1) + need_multiple = false; + } + + rcu_read_unlock(); + break; + case NL80211_IFTYPE_ADHOC: + /* currently */ + need_multiple = false; + break; + default: + /* only AP really */ + need_multiple = true; + break; + } + + ctx->ht_need_multiple_chains = need_multiple; + + if (!need_multiple) { + /* check all contexts */ + for_each_context(priv, tmp) { + if (!tmp->vif) + continue; + if (tmp->ht_need_multiple_chains) { + need_multiple = true; + break; + } + } + } + + ht_conf->single_chain_sufficient = !need_multiple; +} + +static void iwlagn_chain_noise_reset(struct iwl_priv *priv) +{ + struct iwl_chain_noise_data *data = &priv->chain_noise_data; + int ret; + + if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED) + return; + + if ((data->state == IWL_CHAIN_NOISE_ALIVE) && + iwl_is_any_associated(priv)) { + struct iwl_calib_chain_noise_reset_cmd cmd; + + /* clear data for chain noise calibration algorithm */ + data->chain_noise_a = 0; + data->chain_noise_b = 0; + data->chain_noise_c = 0; + data->chain_signal_a = 0; + data->chain_signal_b = 0; + data->chain_signal_c = 0; + data->beacon_count = 0; + + memset(&cmd, 0, sizeof(cmd)); + iwl_set_calib_hdr(&cmd.hdr, + priv->phy_calib_chain_noise_reset_cmd); + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_PHY_CALIBRATION_CMD, + 0, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(priv, + "Could not send REPLY_PHY_CALIBRATION_CMD\n"); + data->state = IWL_CHAIN_NOISE_ACCUMULATE; + IWL_DEBUG_CALIB(priv, "Run chain_noise_calibrate\n"); + } +} + +void iwlagn_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + int ret; + bool force = false; + + mutex_lock(&priv->mutex); + + if (changes & BSS_CHANGED_IDLE && bss_conf->idle) { + /* + * If we go idle, then clearly no "passive-no-rx" + * workaround is needed any more, this is a reset. + */ + iwlagn_lift_passive_no_rx(priv); + } + + if (unlikely(!iwl_is_ready(priv))) { + IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); + mutex_unlock(&priv->mutex); + return; + } + + if (unlikely(!ctx->vif)) { + IWL_DEBUG_MAC80211(priv, "leave - vif is NULL\n"); + mutex_unlock(&priv->mutex); + return; + } + + if (changes & BSS_CHANGED_BEACON_INT) + force = true; + + if (changes & BSS_CHANGED_QOS) { + ctx->qos_data.qos_active = bss_conf->qos; + iwlagn_update_qos(priv, ctx); + } + + ctx->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid); + if (vif->bss_conf.use_short_preamble) + ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + else + ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + + if (changes & BSS_CHANGED_ASSOC) { + if (bss_conf->assoc) { + priv->timestamp = bss_conf->sync_tsf; + ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + } else { + ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + + if (ctx->ctxid == IWL_RXON_CTX_BSS) + priv->have_rekey_data = false; + } + + iwlagn_bt_coex_rssi_monitor(priv); + } + + if (ctx->ht.enabled) { + ctx->ht.protection = bss_conf->ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION; + ctx->ht.non_gf_sta_present = !!(bss_conf->ht_operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + iwlagn_check_needed_chains(priv, ctx, bss_conf); + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + + iwlagn_set_rxon_chain(priv, ctx); + + if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ)) + ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; + else + ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; + + if (bss_conf->use_cts_prot) + ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; + else + ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN; + + memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); + + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) { + if (vif->bss_conf.enable_beacon) { + ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + priv->beacon_ctx = ctx; + } else { + ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + priv->beacon_ctx = NULL; + } + } + + /* + * If the ucode decides to do beacon filtering before + * association, it will lose beacons that are needed + * before sending frames out on passive channels. This + * causes association failures on those channels. Enable + * receiving beacons in such cases. + */ + + if (vif->type == NL80211_IFTYPE_STATION) { + if (!bss_conf->assoc) + ctx->staging.filter_flags |= RXON_FILTER_BCON_AWARE_MSK; + else + ctx->staging.filter_flags &= + ~RXON_FILTER_BCON_AWARE_MSK; + } + + if (force || memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) + iwlagn_commit_rxon(priv, ctx); + + if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) { + /* + * The chain noise calibration will enable PM upon + * completion. If calibration has already been run + * then we need to enable power management here. + */ + if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE) + iwl_power_update_mode(priv, false); + + /* Enable RX differential gain and sensitivity calibrations */ + iwlagn_chain_noise_reset(priv); + priv->start_calib = 1; + } + + if (changes & BSS_CHANGED_IBSS) { + ret = iwlagn_manage_ibss_station(priv, vif, + bss_conf->ibss_joined); + if (ret) + IWL_ERR(priv, "failed to %s IBSS station %pM\n", + bss_conf->ibss_joined ? "add" : "remove", + bss_conf->bssid); + } + + if (changes & BSS_CHANGED_BEACON && priv->beacon_ctx == ctx) { + if (iwlagn_update_beacon(priv, vif)) + IWL_ERR(priv, "Error updating beacon\n"); + } + + mutex_unlock(&priv->mutex); +} + +void iwlagn_post_scan(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + + /* + * We do not commit power settings while scan is pending, + * do it now if the settings changed. + */ + iwl_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false); + iwl_set_tx_power(priv, priv->tx_power_next, false); + + /* + * Since setting the RXON may have been deferred while + * performing the scan, fire one off if needed + */ + for_each_context(priv, ctx) + if (memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) + iwlagn_commit_rxon(priv, ctx); + + iwlagn_set_pan_params(priv); +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/scan.c b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c new file mode 100644 index 000000000000..648159495bbc --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c @@ -0,0 +1,1075 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ +#include +#include +#include +#include + +#include "dev.h" +#include "agn.h" + +/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after + * sending probe req. This should be set long enough to hear probe responses + * from more than one AP. */ +#define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ +#define IWL_ACTIVE_DWELL_TIME_52 (20) + +#define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3) +#define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2) + +/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. + * Must be set longer than active dwell time. + * For the most reliable scan, set > AP beacon interval (typically 100msec). */ +#define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ +#define IWL_PASSIVE_DWELL_TIME_52 (10) +#define IWL_PASSIVE_DWELL_BASE (100) +#define IWL_CHANNEL_TUNE_TIME 5 +#define MAX_SCAN_CHANNEL 50 + +/* For reset radio, need minimal dwell time only */ +#define IWL_RADIO_RESET_DWELL_TIME 5 + +static int iwl_send_scan_abort(struct iwl_priv *priv) +{ + int ret; + struct iwl_host_cmd cmd = { + .id = REPLY_SCAN_ABORT_CMD, + .flags = CMD_WANT_SKB, + }; + __le32 *status; + + /* Exit instantly with error when device is not ready + * to receive scan abort command or it does not perform + * hardware scan currently */ + if (!test_bit(STATUS_READY, &priv->status) || + !test_bit(STATUS_SCAN_HW, &priv->status) || + test_bit(STATUS_FW_ERROR, &priv->status)) + return -EIO; + + ret = iwl_dvm_send_cmd(priv, &cmd); + if (ret) + return ret; + + status = (void *)cmd.resp_pkt->data; + if (*status != CAN_ABORT_STATUS) { + /* The scan abort will return 1 for success or + * 2 for "failure". A failure condition can be + * due to simply not being in an active scan which + * can occur if we send the scan abort before we + * the microcode has notified us that a scan is + * completed. */ + IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n", + le32_to_cpu(*status)); + ret = -EIO; + } + + iwl_free_resp(&cmd); + return ret; +} + +static void iwl_complete_scan(struct iwl_priv *priv, bool aborted) +{ + /* check if scan was requested from mac80211 */ + if (priv->scan_request) { + IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n"); + ieee80211_scan_completed(priv->hw, aborted); + } + + priv->scan_type = IWL_SCAN_NORMAL; + priv->scan_vif = NULL; + priv->scan_request = NULL; +} + +static void iwl_process_scan_complete(struct iwl_priv *priv) +{ + bool aborted; + + lockdep_assert_held(&priv->mutex); + + if (!test_and_clear_bit(STATUS_SCAN_COMPLETE, &priv->status)) + return; + + IWL_DEBUG_SCAN(priv, "Completed scan.\n"); + + cancel_delayed_work(&priv->scan_check); + + aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status); + if (aborted) + IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n"); + + if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan already completed.\n"); + goto out_settings; + } + + if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) { + int err; + + /* Check if mac80211 requested scan during our internal scan */ + if (priv->scan_request == NULL) + goto out_complete; + + /* If so request a new scan */ + err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL, + priv->scan_request->channels[0]->band); + if (err) { + IWL_DEBUG_SCAN(priv, + "failed to initiate pending scan: %d\n", err); + aborted = true; + goto out_complete; + } + + return; + } + +out_complete: + iwl_complete_scan(priv, aborted); + +out_settings: + /* Can we still talk to firmware ? */ + if (!iwl_is_ready_rf(priv)) + return; + + iwlagn_post_scan(priv); +} + +void iwl_force_scan_end(struct iwl_priv *priv) +{ + lockdep_assert_held(&priv->mutex); + + if (!test_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n"); + return; + } + + IWL_DEBUG_SCAN(priv, "Forcing scan end\n"); + clear_bit(STATUS_SCANNING, &priv->status); + clear_bit(STATUS_SCAN_HW, &priv->status); + clear_bit(STATUS_SCAN_ABORTING, &priv->status); + clear_bit(STATUS_SCAN_COMPLETE, &priv->status); + iwl_complete_scan(priv, true); +} + +static void iwl_do_scan_abort(struct iwl_priv *priv) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + if (!test_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n"); + return; + } + + if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan abort in progress\n"); + return; + } + + ret = iwl_send_scan_abort(priv); + if (ret) { + IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret); + iwl_force_scan_end(priv); + } else + IWL_DEBUG_SCAN(priv, "Successfully send scan abort\n"); +} + +/** + * iwl_scan_cancel - Cancel any currently executing HW scan + */ +int iwl_scan_cancel(struct iwl_priv *priv) +{ + IWL_DEBUG_SCAN(priv, "Queuing abort scan\n"); + queue_work(priv->workqueue, &priv->abort_scan); + return 0; +} + +/** + * iwl_scan_cancel_timeout - Cancel any currently executing HW scan + * @ms: amount of time to wait (in milliseconds) for scan to abort + * + */ +void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(ms); + + lockdep_assert_held(&priv->mutex); + + IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n"); + + iwl_do_scan_abort(priv); + + while (time_before_eq(jiffies, timeout)) { + if (!test_bit(STATUS_SCAN_HW, &priv->status)) + goto finished; + msleep(20); + } + + return; + + finished: + /* + * Now STATUS_SCAN_HW is clear. This means that the + * device finished, but the background work is going + * to execute at best as soon as we release the mutex. + * Since we need to be able to issue a new scan right + * after this function returns, run the complete here. + * The STATUS_SCAN_COMPLETE bit will then be cleared + * and prevent the background work from "completing" + * a possible new scan. + */ + iwl_process_scan_complete(priv); +} + +/* Service response to REPLY_SCAN_CMD (0x80) */ +static void iwl_rx_reply_scan(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ +#ifdef CONFIG_IWLWIFI_DEBUG + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scanreq_notification *notif = (void *)pkt->data; + + IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status); +#endif +} + +/* Service SCAN_START_NOTIFICATION (0x82) */ +static void iwl_rx_scan_start_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scanstart_notification *notif = (void *)pkt->data; + + priv->scan_start_tsf = le32_to_cpu(notif->tsf_low); + IWL_DEBUG_SCAN(priv, "Scan start: " + "%d [802.11%s] " + "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", + notif->channel, + notif->band ? "bg" : "a", + le32_to_cpu(notif->tsf_high), + le32_to_cpu(notif->tsf_low), + notif->status, notif->beacon_timer); +} + +/* Service SCAN_RESULTS_NOTIFICATION (0x83) */ +static void iwl_rx_scan_results_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ +#ifdef CONFIG_IWLWIFI_DEBUG + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scanresults_notification *notif = (void *)pkt->data; + + IWL_DEBUG_SCAN(priv, "Scan ch.res: " + "%d [802.11%s] " + "probe status: %u:%u " + "(TSF: 0x%08X:%08X) - %d " + "elapsed=%lu usec\n", + notif->channel, + notif->band ? "bg" : "a", + notif->probe_status, notif->num_probe_not_sent, + le32_to_cpu(notif->tsf_high), + le32_to_cpu(notif->tsf_low), + le32_to_cpu(notif->statistics[0]), + le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf); +#endif +} + +/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ +static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scancomplete_notification *scan_notif = (void *)pkt->data; + + IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", + scan_notif->scanned_channels, + scan_notif->tsf_low, + scan_notif->tsf_high, scan_notif->status); + + IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", + (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", + jiffies_to_msecs(jiffies - priv->scan_start)); + + /* + * When aborting, we run the scan completed background work inline + * and the background work must then do nothing. The SCAN_COMPLETE + * bit helps implement that logic and thus needs to be set before + * queueing the work. Also, since the scan abort waits for SCAN_HW + * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW + * to avoid a race there. + */ + set_bit(STATUS_SCAN_COMPLETE, &priv->status); + clear_bit(STATUS_SCAN_HW, &priv->status); + queue_work(priv->workqueue, &priv->scan_completed); + + if (priv->iw_mode != NL80211_IFTYPE_ADHOC && + iwl_advanced_bt_coexist(priv) && + priv->bt_status != scan_notif->bt_status) { + if (scan_notif->bt_status) { + /* BT on */ + if (!priv->bt_ch_announce) + priv->bt_traffic_load = + IWL_BT_COEX_TRAFFIC_LOAD_HIGH; + /* + * otherwise, no traffic load information provided + * no changes made + */ + } else { + /* BT off */ + priv->bt_traffic_load = + IWL_BT_COEX_TRAFFIC_LOAD_NONE; + } + priv->bt_status = scan_notif->bt_status; + queue_work(priv->workqueue, + &priv->bt_traffic_change_work); + } +} + +void iwl_setup_rx_scan_handlers(struct iwl_priv *priv) +{ + /* scan handlers */ + priv->rx_handlers[REPLY_SCAN_CMD] = iwl_rx_reply_scan; + priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl_rx_scan_start_notif; + priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] = + iwl_rx_scan_results_notif; + priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] = + iwl_rx_scan_complete_notif; +} + +static u16 iwl_get_active_dwell_time(struct iwl_priv *priv, + enum ieee80211_band band, u8 n_probes) +{ + if (band == IEEE80211_BAND_5GHZ) + return IWL_ACTIVE_DWELL_TIME_52 + + IWL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); + else + return IWL_ACTIVE_DWELL_TIME_24 + + IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); +} + +static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time) +{ + struct iwl_rxon_context *ctx; + int limits[NUM_IWL_RXON_CTX] = {}; + int n_active = 0; + u16 limit; + + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); + + /* + * If we're associated, we clamp the dwell time 98% + * of the beacon interval (minus 2 * channel tune time) + * If both contexts are active, we have to restrict to + * 1/2 of the minimum of them, because they might be in + * lock-step with the time inbetween only half of what + * time we'd have in each of them. + */ + for_each_context(priv, ctx) { + switch (ctx->staging.dev_type) { + case RXON_DEV_TYPE_P2P: + /* no timing constraints */ + continue; + case RXON_DEV_TYPE_ESS: + default: + /* timing constraints if associated */ + if (!iwl_is_associated_ctx(ctx)) + continue; + break; + case RXON_DEV_TYPE_CP: + case RXON_DEV_TYPE_2STA: + /* + * These seem to always have timers for TBTT + * active in uCode even when not associated yet. + */ + break; + } + + limits[n_active++] = ctx->beacon_int ?: IWL_PASSIVE_DWELL_BASE; + } + + switch (n_active) { + case 0: + return dwell_time; + case 2: + limit = (limits[1] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; + limit /= 2; + dwell_time = min(limit, dwell_time); + /* fall through to limit further */ + case 1: + limit = (limits[0] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; + limit /= n_active; + return min(limit, dwell_time); + default: + WARN_ON_ONCE(1); + return dwell_time; + } +} + +static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, + enum ieee80211_band band) +{ + u16 passive = (band == IEEE80211_BAND_2GHZ) ? + IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 : + IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52; + + return iwl_limit_dwell(priv, passive); +} + +/* Return valid, unused, channel for a passive scan to reset the RF */ +static u8 iwl_get_single_channel_number(struct iwl_priv *priv, + enum ieee80211_band band) +{ + struct ieee80211_supported_band *sband = priv->hw->wiphy->bands[band]; + struct iwl_rxon_context *ctx; + int i; + + for (i = 0; i < sband->n_channels; i++) { + bool busy = false; + + for_each_context(priv, ctx) { + busy = sband->channels[i].hw_value == + le16_to_cpu(ctx->staging.channel); + if (busy) + break; + } + + if (busy) + continue; + + if (!(sband->channels[i].flags & IEEE80211_CHAN_DISABLED)) + return sband->channels[i].hw_value; + } + + return 0; +} + +static int iwl_get_channel_for_reset_scan(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum ieee80211_band band, + struct iwl_scan_channel *scan_ch) +{ + const struct ieee80211_supported_band *sband; + u16 channel; + + sband = iwl_get_hw_mode(priv, band); + if (!sband) { + IWL_ERR(priv, "invalid band\n"); + return 0; + } + + channel = iwl_get_single_channel_number(priv, band); + if (channel) { + scan_ch->channel = cpu_to_le16(channel); + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + scan_ch->active_dwell = + cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); + scan_ch->passive_dwell = + cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + if (band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + return 1; + } + + IWL_ERR(priv, "no valid channel found\n"); + return 0; +} + +static int iwl_get_channels_for_scan(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum ieee80211_band band, + u8 is_active, u8 n_probes, + struct iwl_scan_channel *scan_ch) +{ + struct ieee80211_channel *chan; + const struct ieee80211_supported_band *sband; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int added, i; + u16 channel; + + sband = iwl_get_hw_mode(priv, band); + if (!sband) + return 0; + + active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); + passive_dwell = iwl_get_passive_dwell_time(priv, band); + + if (passive_dwell <= active_dwell) + passive_dwell = active_dwell + 1; + + for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { + chan = priv->scan_request->channels[i]; + + if (chan->band != band) + continue; + + channel = chan->hw_value; + scan_ch->channel = cpu_to_le16(channel); + + if (!is_active || (chan->flags & IEEE80211_CHAN_NO_IR)) + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + else + scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; + + if (n_probes) + scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes); + + scan_ch->active_dwell = cpu_to_le16(active_dwell); + scan_ch->passive_dwell = cpu_to_le16(passive_dwell); + + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + + /* NOTE: if we were doing 6Mb OFDM for scans we'd use + * power level: + * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; + */ + if (band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + + IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n", + channel, le32_to_cpu(scan_ch->type), + (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? + "ACTIVE" : "PASSIVE", + (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? + active_dwell : passive_dwell); + + scan_ch++; + added++; + } + + IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added); + return added; +} + +/** + * iwl_fill_probe_req - fill in all required fields and IE for probe request + */ + +static u16 iwl_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, + const u8 *ies, int ie_len, const u8 *ssid, + u8 ssid_len, int left) +{ + int len = 0; + u8 *pos = NULL; + + /* Make sure there is enough space for the probe request, + * two mandatory IEs and the data */ + left -= 24; + if (left < 0) + return 0; + + frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); + eth_broadcast_addr(frame->da); + memcpy(frame->sa, ta, ETH_ALEN); + eth_broadcast_addr(frame->bssid); + frame->seq_ctrl = 0; + + len += 24; + + /* ...next IE... */ + pos = &frame->u.probe_req.variable[0]; + + /* fill in our SSID IE */ + left -= ssid_len + 2; + if (left < 0) + return 0; + *pos++ = WLAN_EID_SSID; + *pos++ = ssid_len; + if (ssid && ssid_len) { + memcpy(pos, ssid, ssid_len); + pos += ssid_len; + } + + len += ssid_len + 2; + + if (WARN_ON(left < ie_len)) + return len; + + if (ies && ie_len) { + memcpy(pos, ies, ie_len); + len += ie_len; + } + + return (u16)len; +} + +static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_SCAN_CMD, + .len = { sizeof(struct iwl_scan_cmd), }, + }; + struct iwl_scan_cmd *scan; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + u32 rate_flags = 0; + u16 cmd_len = 0; + u16 rx_chain = 0; + enum ieee80211_band band; + u8 n_probes = 0; + u8 rx_ant = priv->nvm_data->valid_rx_ant; + u8 rate; + bool is_active = false; + int chan_mod; + u8 active_chains; + u8 scan_tx_antennas = priv->nvm_data->valid_tx_ant; + int ret; + int scan_cmd_size = sizeof(struct iwl_scan_cmd) + + MAX_SCAN_CHANNEL * sizeof(struct iwl_scan_channel) + + priv->fw->ucode_capa.max_probe_length; + const u8 *ssid = NULL; + u8 ssid_len = 0; + + if (WARN_ON(priv->scan_type == IWL_SCAN_NORMAL && + (!priv->scan_request || + priv->scan_request->n_channels > MAX_SCAN_CHANNEL))) + return -EINVAL; + + lockdep_assert_held(&priv->mutex); + + if (vif) + ctx = iwl_rxon_ctx_from_vif(vif); + + if (!priv->scan_cmd) { + priv->scan_cmd = kmalloc(scan_cmd_size, GFP_KERNEL); + if (!priv->scan_cmd) { + IWL_DEBUG_SCAN(priv, + "fail to allocate memory for scan\n"); + return -ENOMEM; + } + } + scan = priv->scan_cmd; + memset(scan, 0, scan_cmd_size); + + scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; + scan->quiet_time = IWL_ACTIVE_QUIET_TIME; + + if (iwl_is_any_associated(priv)) { + u16 interval = 0; + u32 extra; + u32 suspend_time = 100; + u32 scan_suspend_time = 100; + + IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); + switch (priv->scan_type) { + case IWL_SCAN_RADIO_RESET: + interval = 0; + break; + case IWL_SCAN_NORMAL: + interval = vif->bss_conf.beacon_int; + break; + } + + scan->suspend_time = 0; + scan->max_out_time = cpu_to_le32(200 * 1024); + if (!interval) + interval = suspend_time; + + extra = (suspend_time / interval) << 22; + scan_suspend_time = (extra | + ((suspend_time % interval) * 1024)); + scan->suspend_time = cpu_to_le32(scan_suspend_time); + IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", + scan_suspend_time, interval); + } + + switch (priv->scan_type) { + case IWL_SCAN_RADIO_RESET: + IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); + /* + * Override quiet time as firmware checks that active + * dwell is >= quiet; since we use passive scan it'll + * not actually be used. + */ + scan->quiet_time = cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); + break; + case IWL_SCAN_NORMAL: + if (priv->scan_request->n_ssids) { + int i, p = 0; + IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); + /* + * The highest priority SSID is inserted to the + * probe request template. + */ + ssid_len = priv->scan_request->ssids[0].ssid_len; + ssid = priv->scan_request->ssids[0].ssid; + + /* + * Invert the order of ssids, the firmware will invert + * it back. + */ + for (i = priv->scan_request->n_ssids - 1; i >= 1; i--) { + scan->direct_scan[p].id = WLAN_EID_SSID; + scan->direct_scan[p].len = + priv->scan_request->ssids[i].ssid_len; + memcpy(scan->direct_scan[p].ssid, + priv->scan_request->ssids[i].ssid, + priv->scan_request->ssids[i].ssid_len); + n_probes++; + p++; + } + is_active = true; + } else + IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); + break; + } + + scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; + scan->tx_cmd.sta_id = ctx->bcast_sta_id; + scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + switch (priv->scan_band) { + case IEEE80211_BAND_2GHZ: + scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; + chan_mod = le32_to_cpu( + priv->contexts[IWL_RXON_CTX_BSS].active.flags & + RXON_FLG_CHANNEL_MODE_MSK) + >> RXON_FLG_CHANNEL_MODE_POS; + if ((priv->scan_request && priv->scan_request->no_cck) || + chan_mod == CHANNEL_MODE_PURE_40) { + rate = IWL_RATE_6M_PLCP; + } else { + rate = IWL_RATE_1M_PLCP; + rate_flags = RATE_MCS_CCK_MSK; + } + /* + * Internal scans are passive, so we can indiscriminately set + * the BT ignore flag on 2.4 GHz since it applies to TX only. + */ + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) + scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; + break; + case IEEE80211_BAND_5GHZ: + rate = IWL_RATE_6M_PLCP; + break; + default: + IWL_WARN(priv, "Invalid scan band\n"); + return -EIO; + } + + /* + * If active scanning is requested but a certain channel is + * marked passive, we can do active scanning if we detect + * transmissions. + * + * There is an issue with some firmware versions that triggers + * a sysassert on a "good CRC threshold" of zero (== disabled), + * on a radar channel even though this means that we should NOT + * send probes. + * + * The "good CRC threshold" is the number of frames that we + * need to receive during our dwell time on a channel before + * sending out probes -- setting this to a huge value will + * mean we never reach it, but at the same time work around + * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER + * here instead of IWL_GOOD_CRC_TH_DISABLED. + * + * This was fixed in later versions along with some other + * scan changes, and the threshold behaves as a flag in those + * versions. + */ + if (priv->new_scan_threshold_behaviour) + scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : + IWL_GOOD_CRC_TH_DISABLED; + else + scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : + IWL_GOOD_CRC_TH_NEVER; + + band = priv->scan_band; + + if (band == IEEE80211_BAND_2GHZ && + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { + /* transmit 2.4 GHz probes only on first antenna */ + scan_tx_antennas = first_antenna(scan_tx_antennas); + } + + priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, + priv->scan_tx_ant[band], + scan_tx_antennas); + rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]); + scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags); + + /* + * In power save mode while associated use one chain, + * otherwise use all chains + */ + if (test_bit(STATUS_POWER_PMI, &priv->status) && + !(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { + /* rx_ant has been set to all valid chains previously */ + active_chains = rx_ant & + ((u8)(priv->chain_noise_data.active_chains)); + if (!active_chains) + active_chains = rx_ant; + + IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n", + priv->chain_noise_data.active_chains); + + rx_ant = first_antenna(active_chains); + } + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && + priv->bt_full_concurrent) { + /* operated as 1x1 in full concurrency mode */ + rx_ant = first_antenna(rx_ant); + } + + /* MIMO is not used here, but value is required */ + rx_chain |= + priv->nvm_data->valid_rx_ant << RXON_RX_CHAIN_VALID_POS; + rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; + rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; + rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; + scan->rx_chain = cpu_to_le16(rx_chain); + switch (priv->scan_type) { + case IWL_SCAN_NORMAL: + cmd_len = iwl_fill_probe_req( + (struct ieee80211_mgmt *)scan->data, + vif->addr, + priv->scan_request->ie, + priv->scan_request->ie_len, + ssid, ssid_len, + scan_cmd_size - sizeof(*scan)); + break; + case IWL_SCAN_RADIO_RESET: + /* use bcast addr, will not be transmitted but must be valid */ + cmd_len = iwl_fill_probe_req( + (struct ieee80211_mgmt *)scan->data, + iwl_bcast_addr, NULL, 0, + NULL, 0, + scan_cmd_size - sizeof(*scan)); + break; + default: + BUG(); + } + scan->tx_cmd.len = cpu_to_le16(cmd_len); + + scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | + RXON_FILTER_BCON_AWARE_MSK); + + switch (priv->scan_type) { + case IWL_SCAN_RADIO_RESET: + scan->channel_count = + iwl_get_channel_for_reset_scan(priv, vif, band, + (void *)&scan->data[cmd_len]); + break; + case IWL_SCAN_NORMAL: + scan->channel_count = + iwl_get_channels_for_scan(priv, vif, band, + is_active, n_probes, + (void *)&scan->data[cmd_len]); + break; + } + + if (scan->channel_count == 0) { + IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); + return -EIO; + } + + cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) + + scan->channel_count * sizeof(struct iwl_scan_channel); + cmd.data[0] = scan; + cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + scan->len = cpu_to_le16(cmd.len[0]); + + /* set scan bit here for PAN params */ + set_bit(STATUS_SCAN_HW, &priv->status); + + ret = iwlagn_set_pan_params(priv); + if (ret) { + clear_bit(STATUS_SCAN_HW, &priv->status); + return ret; + } + + ret = iwl_dvm_send_cmd(priv, &cmd); + if (ret) { + clear_bit(STATUS_SCAN_HW, &priv->status); + iwlagn_set_pan_params(priv); + } + + return ret; +} + +void iwl_init_scan_params(struct iwl_priv *priv) +{ + u8 ant_idx = fls(priv->nvm_data->valid_tx_ant) - 1; + if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ]) + priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx; + if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ]) + priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; +} + +int __must_check iwl_scan_initiate(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum iwl_scan_type scan_type, + enum ieee80211_band band) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + cancel_delayed_work(&priv->scan_check); + + if (!iwl_is_ready_rf(priv)) { + IWL_WARN(priv, "Request scan called when driver not ready.\n"); + return -EIO; + } + + if (test_bit(STATUS_SCAN_HW, &priv->status)) { + IWL_DEBUG_SCAN(priv, + "Multiple concurrent scan requests in parallel.\n"); + return -EBUSY; + } + + if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n"); + return -EBUSY; + } + + IWL_DEBUG_SCAN(priv, "Starting %sscan...\n", + scan_type == IWL_SCAN_NORMAL ? "" : + "internal short "); + + set_bit(STATUS_SCANNING, &priv->status); + priv->scan_type = scan_type; + priv->scan_start = jiffies; + priv->scan_band = band; + + ret = iwlagn_request_scan(priv, vif); + if (ret) { + clear_bit(STATUS_SCANNING, &priv->status); + priv->scan_type = IWL_SCAN_NORMAL; + return ret; + } + + queue_delayed_work(priv->workqueue, &priv->scan_check, + IWL_SCAN_CHECK_WATCHDOG); + + return 0; +} + + +/* + * internal short scan, this function should only been called while associated. + * It will reset and tune the radio to prevent possible RF related problem + */ +void iwl_internal_short_hw_scan(struct iwl_priv *priv) +{ + queue_work(priv->workqueue, &priv->start_internal_scan); +} + +static void iwl_bg_start_internal_scan(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, start_internal_scan); + + IWL_DEBUG_SCAN(priv, "Start internal scan\n"); + + mutex_lock(&priv->mutex); + + if (priv->scan_type == IWL_SCAN_RADIO_RESET) { + IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n"); + goto unlock; + } + + if (test_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); + goto unlock; + } + + if (iwl_scan_initiate(priv, NULL, IWL_SCAN_RADIO_RESET, priv->band)) + IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n"); + unlock: + mutex_unlock(&priv->mutex); +} + +static void iwl_bg_scan_check(struct work_struct *data) +{ + struct iwl_priv *priv = + container_of(data, struct iwl_priv, scan_check.work); + + IWL_DEBUG_SCAN(priv, "Scan check work\n"); + + /* Since we are here firmware does not finish scan and + * most likely is in bad shape, so we don't bother to + * send abort command, just force scan complete to mac80211 */ + mutex_lock(&priv->mutex); + iwl_force_scan_end(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl_bg_abort_scan(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan); + + IWL_DEBUG_SCAN(priv, "Abort scan work\n"); + + /* We keep scan_check work queued in case when firmware will not + * report back scan completed notification */ + mutex_lock(&priv->mutex); + iwl_scan_cancel_timeout(priv, 200); + mutex_unlock(&priv->mutex); +} + +static void iwl_bg_scan_completed(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, scan_completed); + + mutex_lock(&priv->mutex); + iwl_process_scan_complete(priv); + mutex_unlock(&priv->mutex); +} + +void iwl_setup_scan_deferred_work(struct iwl_priv *priv) +{ + INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); + INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan); + INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan); + INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check); +} + +void iwl_cancel_scan_deferred_work(struct iwl_priv *priv) +{ + cancel_work_sync(&priv->start_internal_scan); + cancel_work_sync(&priv->abort_scan); + cancel_work_sync(&priv->scan_completed); + + if (cancel_delayed_work_sync(&priv->scan_check)) { + mutex_lock(&priv->mutex); + iwl_force_scan_end(priv); + mutex_unlock(&priv->mutex); + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/sta.c b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c new file mode 100644 index 000000000000..0fa67d3b7235 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c @@ -0,0 +1,1442 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include "iwl-trans.h" +#include "dev.h" +#include "agn.h" + +const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +static int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) +{ + lockdep_assert_held(&priv->sta_lock); + + if (sta_id >= IWLAGN_STATION_COUNT) { + IWL_ERR(priv, "invalid sta_id %u\n", sta_id); + return -EINVAL; + } + if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) + IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u " + "addr %pM\n", + sta_id, priv->stations[sta_id].sta.sta.addr); + + if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) { + IWL_DEBUG_ASSOC(priv, + "STA id %u addr %pM already present in uCode " + "(according to driver)\n", + sta_id, priv->stations[sta_id].sta.sta.addr); + } else { + priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE; + IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n", + sta_id, priv->stations[sta_id].sta.sta.addr); + } + return 0; +} + +static void iwl_process_add_sta_resp(struct iwl_priv *priv, + struct iwl_rx_packet *pkt) +{ + struct iwl_add_sta_resp *add_sta_resp = (void *)pkt->data; + + IWL_DEBUG_INFO(priv, "Processing response for adding station\n"); + + spin_lock_bh(&priv->sta_lock); + + switch (add_sta_resp->status) { + case ADD_STA_SUCCESS_MSK: + IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); + break; + case ADD_STA_NO_ROOM_IN_TABLE: + IWL_ERR(priv, "Adding station failed, no room in table.\n"); + break; + case ADD_STA_NO_BLOCK_ACK_RESOURCE: + IWL_ERR(priv, + "Adding station failed, no block ack resource.\n"); + break; + case ADD_STA_MODIFY_NON_EXIST_STA: + IWL_ERR(priv, "Attempting to modify non-existing station\n"); + break; + default: + IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", + add_sta_resp->status); + break; + } + + spin_unlock_bh(&priv->sta_lock); +} + +void iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + iwl_process_add_sta_resp(priv, pkt); +} + +int iwl_send_add_sta(struct iwl_priv *priv, + struct iwl_addsta_cmd *sta, u8 flags) +{ + int ret = 0; + struct iwl_host_cmd cmd = { + .id = REPLY_ADD_STA, + .flags = flags, + .data = { sta, }, + .len = { sizeof(*sta), }, + }; + u8 sta_id __maybe_unused = sta->sta.sta_id; + struct iwl_rx_packet *pkt; + struct iwl_add_sta_resp *add_sta_resp; + + IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", + sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); + + if (!(flags & CMD_ASYNC)) { + cmd.flags |= CMD_WANT_SKB; + might_sleep(); + } + + ret = iwl_dvm_send_cmd(priv, &cmd); + + if (ret || (flags & CMD_ASYNC)) + return ret; + + pkt = cmd.resp_pkt; + add_sta_resp = (void *)pkt->data; + + /* debug messages are printed in the handler */ + if (add_sta_resp->status == ADD_STA_SUCCESS_MSK) { + spin_lock_bh(&priv->sta_lock); + ret = iwl_sta_ucode_activate(priv, sta_id); + spin_unlock_bh(&priv->sta_lock); + } else { + ret = -EIO; + } + + iwl_free_resp(&cmd); + + return ret; +} + +bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_sta *sta) +{ + if (!ctx->ht.enabled || !ctx->ht.is_40mhz) + return false; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (priv->disable_ht40) + return false; +#endif + + /* special case for RXON */ + if (!sta) + return true; + + return sta->bandwidth >= IEEE80211_STA_RX_BW_40; +} + +static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, + struct ieee80211_sta *sta, + struct iwl_rxon_context *ctx, + __le32 *flags, __le32 *mask) +{ + struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; + + *mask = STA_FLG_RTS_MIMO_PROT_MSK | + STA_FLG_MIMO_DIS_MSK | + STA_FLG_HT40_EN_MSK | + STA_FLG_MAX_AGG_SIZE_MSK | + STA_FLG_AGG_MPDU_DENSITY_MSK; + *flags = 0; + + if (!sta || !sta_ht_inf->ht_supported) + return; + + IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n", + sta->addr, + (sta->smps_mode == IEEE80211_SMPS_STATIC) ? + "static" : + (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ? + "dynamic" : "disabled"); + + switch (sta->smps_mode) { + case IEEE80211_SMPS_STATIC: + *flags |= STA_FLG_MIMO_DIS_MSK; + break; + case IEEE80211_SMPS_DYNAMIC: + *flags |= STA_FLG_RTS_MIMO_PROT_MSK; + break; + case IEEE80211_SMPS_OFF: + break; + default: + IWL_WARN(priv, "Invalid MIMO PS mode %d\n", sta->smps_mode); + break; + } + + *flags |= cpu_to_le32( + (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); + + *flags |= cpu_to_le32( + (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); + + if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) + *flags |= STA_FLG_HT40_EN_MSK; +} + +int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct ieee80211_sta *sta) +{ + u8 sta_id = iwl_sta_id(sta); + __le32 flags, mask; + struct iwl_addsta_cmd cmd; + + if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) + return -EINVAL; + + iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].sta.station_flags &= ~mask; + priv->stations[sta_id].sta.station_flags |= flags; + spin_unlock_bh(&priv->sta_lock); + + memset(&cmd, 0, sizeof(cmd)); + cmd.mode = STA_CONTROL_MODIFY_MSK; + cmd.station_flags_msk = mask; + cmd.station_flags = flags; + cmd.sta.sta_id = sta_id; + + return iwl_send_add_sta(priv, &cmd, 0); +} + +static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, + struct ieee80211_sta *sta, + struct iwl_rxon_context *ctx) +{ + __le32 flags, mask; + + iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); + + lockdep_assert_held(&priv->sta_lock); + priv->stations[index].sta.station_flags &= ~mask; + priv->stations[index].sta.station_flags |= flags; +} + +/** + * iwl_prep_station - Prepare station information for addition + * + * should be called with sta_lock held + */ +u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, struct ieee80211_sta *sta) +{ + struct iwl_station_entry *station; + int i; + u8 sta_id = IWL_INVALID_STATION; + + if (is_ap) + sta_id = ctx->ap_sta_id; + else if (is_broadcast_ether_addr(addr)) + sta_id = ctx->bcast_sta_id; + else + for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) { + if (ether_addr_equal(priv->stations[i].sta.sta.addr, + addr)) { + sta_id = i; + break; + } + + if (!priv->stations[i].used && + sta_id == IWL_INVALID_STATION) + sta_id = i; + } + + /* + * These two conditions have the same outcome, but keep them + * separate + */ + if (unlikely(sta_id == IWL_INVALID_STATION)) + return sta_id; + + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { + IWL_DEBUG_INFO(priv, "STA %d already in process of being " + "added.\n", sta_id); + return sta_id; + } + + if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && + (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) && + ether_addr_equal(priv->stations[sta_id].sta.sta.addr, addr)) { + IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " + "adding again.\n", sta_id, addr); + return sta_id; + } + + station = &priv->stations[sta_id]; + station->used = IWL_STA_DRIVER_ACTIVE; + IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", + sta_id, addr); + priv->num_stations++; + + /* Set up the REPLY_ADD_STA command to send to device */ + memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd)); + memcpy(station->sta.sta.addr, addr, ETH_ALEN); + station->sta.mode = 0; + station->sta.sta.sta_id = sta_id; + station->sta.station_flags = ctx->station_flags; + station->ctxid = ctx->ctxid; + + if (sta) { + struct iwl_station_priv *sta_priv; + + sta_priv = (void *)sta->drv_priv; + sta_priv->ctx = ctx; + } + + /* + * OK to call unconditionally, since local stations (IBSS BSSID + * STA and broadcast STA) pass in a NULL sta, and mac80211 + * doesn't allow HT IBSS. + */ + iwl_set_ht_add_station(priv, sta_id, sta, ctx); + + return sta_id; + +} + +#define STA_WAIT_TIMEOUT (HZ/2) + +/** + * iwl_add_station_common - + */ +int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, + struct ieee80211_sta *sta, u8 *sta_id_r) +{ + int ret = 0; + u8 sta_id; + struct iwl_addsta_cmd sta_cmd; + + *sta_id_r = 0; + spin_lock_bh(&priv->sta_lock); + sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Unable to prepare station %pM for addition\n", + addr); + spin_unlock_bh(&priv->sta_lock); + return -EINVAL; + } + + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { + IWL_DEBUG_INFO(priv, "STA %d already in process of being " + "added.\n", sta_id); + spin_unlock_bh(&priv->sta_lock); + return -EEXIST; + } + + if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && + (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " + "adding again.\n", sta_id, addr); + spin_unlock_bh(&priv->sta_lock); + return -EEXIST; + } + + priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, + sizeof(struct iwl_addsta_cmd)); + spin_unlock_bh(&priv->sta_lock); + + /* Add station to device's station table */ + ret = iwl_send_add_sta(priv, &sta_cmd, 0); + if (ret) { + spin_lock_bh(&priv->sta_lock); + IWL_ERR(priv, "Adding station %pM failed.\n", + priv->stations[sta_id].sta.sta.addr); + priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_bh(&priv->sta_lock); + } + *sta_id_r = sta_id; + return ret; +} + +/** + * iwl_sta_ucode_deactivate - deactivate ucode status for a station + */ +static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id) +{ + lockdep_assert_held(&priv->sta_lock); + + /* Ucode must be active and driver must be non active */ + if ((priv->stations[sta_id].used & + (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) != + IWL_STA_UCODE_ACTIVE) + IWL_ERR(priv, "removed non active STA %u\n", sta_id); + + priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; + + memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); + IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); +} + +static int iwl_send_remove_station(struct iwl_priv *priv, + const u8 *addr, int sta_id, + bool temporary) +{ + struct iwl_rx_packet *pkt; + int ret; + struct iwl_rem_sta_cmd rm_sta_cmd; + struct iwl_rem_sta_resp *rem_sta_resp; + + struct iwl_host_cmd cmd = { + .id = REPLY_REMOVE_STA, + .len = { sizeof(struct iwl_rem_sta_cmd), }, + .data = { &rm_sta_cmd, }, + }; + + memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); + rm_sta_cmd.num_sta = 1; + memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); + + cmd.flags |= CMD_WANT_SKB; + + ret = iwl_dvm_send_cmd(priv, &cmd); + + if (ret) + return ret; + + pkt = cmd.resp_pkt; + rem_sta_resp = (void *)pkt->data; + + switch (rem_sta_resp->status) { + case REM_STA_SUCCESS_MSK: + if (!temporary) { + spin_lock_bh(&priv->sta_lock); + iwl_sta_ucode_deactivate(priv, sta_id); + spin_unlock_bh(&priv->sta_lock); + } + IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); + break; + default: + ret = -EIO; + IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); + break; + } + + iwl_free_resp(&cmd); + + return ret; +} + +/** + * iwl_remove_station - Remove driver's knowledge of station. + */ +int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, + const u8 *addr) +{ + u8 tid; + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_INFO(priv, + "Unable to remove station %pM, device not ready.\n", + addr); + /* + * It is typical for stations to be removed when we are + * going down. Return success since device will be down + * soon anyway + */ + return 0; + } + + IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", + sta_id, addr); + + if (WARN_ON(sta_id == IWL_INVALID_STATION)) + return -EINVAL; + + spin_lock_bh(&priv->sta_lock); + + if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { + IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", + addr); + goto out_err; + } + + if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", + addr); + goto out_err; + } + + if (priv->stations[sta_id].used & IWL_STA_LOCAL) { + kfree(priv->stations[sta_id].lq); + priv->stations[sta_id].lq = NULL; + } + + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) + memset(&priv->tid_data[sta_id][tid], 0, + sizeof(priv->tid_data[sta_id][tid])); + + priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + + priv->num_stations--; + + if (WARN_ON(priv->num_stations < 0)) + priv->num_stations = 0; + + spin_unlock_bh(&priv->sta_lock); + + return iwl_send_remove_station(priv, addr, sta_id, false); +out_err: + spin_unlock_bh(&priv->sta_lock); + return -EINVAL; +} + +void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, + const u8 *addr) +{ + u8 tid; + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_INFO(priv, + "Unable to remove station %pM, device not ready.\n", + addr); + return; + } + + IWL_DEBUG_ASSOC(priv, "Deactivating STA: %pM (%d)\n", addr, sta_id); + + if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) + return; + + spin_lock_bh(&priv->sta_lock); + + WARN_ON_ONCE(!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)); + + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) + memset(&priv->tid_data[sta_id][tid], 0, + sizeof(priv->tid_data[sta_id][tid])); + + priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + + priv->num_stations--; + + if (WARN_ON_ONCE(priv->num_stations < 0)) + priv->num_stations = 0; + + spin_unlock_bh(&priv->sta_lock); +} + +static void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + u8 sta_id, struct iwl_link_quality_cmd *link_cmd) +{ + int i, r; + u32 rate_flags = 0; + __le32 rate_n_flags; + + lockdep_assert_held(&priv->mutex); + + memset(link_cmd, 0, sizeof(*link_cmd)); + + /* Set up the rate scaling to start at selected rate, fall back + * all the way down to 1M in IEEE order, and then spin on 1M */ + if (priv->band == IEEE80211_BAND_5GHZ) + r = IWL_RATE_6M_INDEX; + else if (ctx && ctx->vif && ctx->vif->p2p) + r = IWL_RATE_6M_INDEX; + else + r = IWL_RATE_1M_INDEX; + + if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_CCK_MSK; + + rate_flags |= first_antenna(priv->nvm_data->valid_tx_ant) << + RATE_MCS_ANT_POS; + rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) + link_cmd->rs_table[i].rate_n_flags = rate_n_flags; + + link_cmd->general_params.single_stream_ant_msk = + first_antenna(priv->nvm_data->valid_tx_ant); + + link_cmd->general_params.dual_stream_ant_msk = + priv->nvm_data->valid_tx_ant & + ~first_antenna(priv->nvm_data->valid_tx_ant); + if (!link_cmd->general_params.dual_stream_ant_msk) { + link_cmd->general_params.dual_stream_ant_msk = ANT_AB; + } else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) { + link_cmd->general_params.dual_stream_ant_msk = + priv->nvm_data->valid_tx_ant; + } + + link_cmd->agg_params.agg_dis_start_th = + LINK_QUAL_AGG_DISABLE_START_DEF; + link_cmd->agg_params.agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); + + link_cmd->sta_id = sta_id; +} + +/** + * iwl_clear_ucode_stations - clear ucode station table bits + * + * This function clears all the bits in the driver indicating + * which stations are active in the ucode. Call when something + * other than explicit station management would cause this in + * the ucode, e.g. unassociated RXON. + */ +void iwl_clear_ucode_stations(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int i; + bool cleared = false; + + IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n"); + + spin_lock_bh(&priv->sta_lock); + for (i = 0; i < IWLAGN_STATION_COUNT; i++) { + if (ctx && ctx->ctxid != priv->stations[i].ctxid) + continue; + + if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) { + IWL_DEBUG_INFO(priv, + "Clearing ucode active for station %d\n", i); + priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; + cleared = true; + } + } + spin_unlock_bh(&priv->sta_lock); + + if (!cleared) + IWL_DEBUG_INFO(priv, + "No active stations found to be cleared\n"); +} + +/** + * iwl_restore_stations() - Restore driver known stations to device + * + * All stations considered active by driver, but not present in ucode, is + * restored. + * + * Function sleeps. + */ +void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + struct iwl_addsta_cmd sta_cmd; + static const struct iwl_link_quality_cmd zero_lq = {}; + struct iwl_link_quality_cmd lq; + int i; + bool found = false; + int ret; + bool send_lq; + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_INFO(priv, + "Not ready yet, not restoring any stations.\n"); + return; + } + + IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); + spin_lock_bh(&priv->sta_lock); + for (i = 0; i < IWLAGN_STATION_COUNT; i++) { + if (ctx->ctxid != priv->stations[i].ctxid) + continue; + if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && + !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", + priv->stations[i].sta.sta.addr); + priv->stations[i].sta.mode = 0; + priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; + found = true; + } + } + + for (i = 0; i < IWLAGN_STATION_COUNT; i++) { + if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { + memcpy(&sta_cmd, &priv->stations[i].sta, + sizeof(struct iwl_addsta_cmd)); + send_lq = false; + if (priv->stations[i].lq) { + if (priv->wowlan) + iwl_sta_fill_lq(priv, ctx, i, &lq); + else + memcpy(&lq, priv->stations[i].lq, + sizeof(struct iwl_link_quality_cmd)); + + if (memcmp(&lq, &zero_lq, sizeof(lq))) + send_lq = true; + } + spin_unlock_bh(&priv->sta_lock); + ret = iwl_send_add_sta(priv, &sta_cmd, 0); + if (ret) { + spin_lock_bh(&priv->sta_lock); + IWL_ERR(priv, "Adding station %pM failed.\n", + priv->stations[i].sta.sta.addr); + priv->stations[i].used &= + ~IWL_STA_DRIVER_ACTIVE; + priv->stations[i].used &= + ~IWL_STA_UCODE_INPROGRESS; + continue; + } + /* + * Rate scaling has already been initialized, send + * current LQ command + */ + if (send_lq) + iwl_send_lq_cmd(priv, ctx, &lq, 0, true); + spin_lock_bh(&priv->sta_lock); + priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; + } + } + + spin_unlock_bh(&priv->sta_lock); + if (!found) + IWL_DEBUG_INFO(priv, "Restoring all known stations .... " + "no stations to be restored.\n"); + else + IWL_DEBUG_INFO(priv, "Restoring all known stations .... " + "complete.\n"); +} + +int iwl_get_free_ucode_key_offset(struct iwl_priv *priv) +{ + int i; + + for (i = 0; i < priv->sta_key_max_num; i++) + if (!test_and_set_bit(i, &priv->ucode_key_table)) + return i; + + return WEP_INVALID_OFFSET; +} + +void iwl_dealloc_bcast_stations(struct iwl_priv *priv) +{ + int i; + + spin_lock_bh(&priv->sta_lock); + for (i = 0; i < IWLAGN_STATION_COUNT; i++) { + if (!(priv->stations[i].used & IWL_STA_BCAST)) + continue; + + priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; + priv->num_stations--; + if (WARN_ON(priv->num_stations < 0)) + priv->num_stations = 0; + kfree(priv->stations[i].lq); + priv->stations[i].lq = NULL; + } + spin_unlock_bh(&priv->sta_lock); +} + +#ifdef CONFIG_IWLWIFI_DEBUG +static void iwl_dump_lq_cmd(struct iwl_priv *priv, + struct iwl_link_quality_cmd *lq) +{ + int i; + IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id); + IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n", + lq->general_params.single_stream_ant_msk, + lq->general_params.dual_stream_ant_msk); + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) + IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n", + i, lq->rs_table[i].rate_n_flags); +} +#else +static inline void iwl_dump_lq_cmd(struct iwl_priv *priv, + struct iwl_link_quality_cmd *lq) +{ +} +#endif + +/** + * is_lq_table_valid() - Test one aspect of LQ cmd for validity + * + * It sometimes happens when a HT rate has been in use and we + * loose connectivity with AP then mac80211 will first tell us that the + * current channel is not HT anymore before removing the station. In such a + * scenario the RXON flags will be updated to indicate we are not + * communicating HT anymore, but the LQ command may still contain HT rates. + * Test for this to prevent driver from sending LQ command between the time + * RXON flags are updated and when LQ command is updated. + */ +static bool is_lq_table_valid(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_link_quality_cmd *lq) +{ + int i; + + if (ctx->ht.enabled) + return true; + + IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n", + ctx->active.channel); + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & + RATE_MCS_HT_MSK) { + IWL_DEBUG_INFO(priv, + "index %d of LQ expects HT channel\n", + i); + return false; + } + } + return true; +} + +/** + * iwl_send_lq_cmd() - Send link quality command + * @init: This command is sent as part of station initialization right + * after station has been added. + * + * The link quality command is sent as the last step of station creation. + * This is the special case in which init is set and we call a callback in + * this case to clear the state indicating that station creation is in + * progress. + */ +int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct iwl_link_quality_cmd *lq, u8 flags, bool init) +{ + int ret = 0; + struct iwl_host_cmd cmd = { + .id = REPLY_TX_LINK_QUALITY_CMD, + .len = { sizeof(struct iwl_link_quality_cmd), }, + .flags = flags, + .data = { lq, }, + }; + + if (WARN_ON(lq->sta_id == IWL_INVALID_STATION)) + return -EINVAL; + + + spin_lock_bh(&priv->sta_lock); + if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) { + spin_unlock_bh(&priv->sta_lock); + return -EINVAL; + } + spin_unlock_bh(&priv->sta_lock); + + iwl_dump_lq_cmd(priv, lq); + if (WARN_ON(init && (cmd.flags & CMD_ASYNC))) + return -EINVAL; + + if (is_lq_table_valid(priv, ctx, lq)) + ret = iwl_dvm_send_cmd(priv, &cmd); + else + ret = -EINVAL; + + if (cmd.flags & CMD_ASYNC) + return ret; + + if (init) { + IWL_DEBUG_INFO(priv, "init LQ command complete, " + "clearing sta addition status for sta %d\n", + lq->sta_id); + spin_lock_bh(&priv->sta_lock); + priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_bh(&priv->sta_lock); + } + return ret; +} + + +static struct iwl_link_quality_cmd * +iwl_sta_alloc_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + u8 sta_id) +{ + struct iwl_link_quality_cmd *link_cmd; + + link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL); + if (!link_cmd) { + IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n"); + return NULL; + } + + iwl_sta_fill_lq(priv, ctx, sta_id, link_cmd); + + return link_cmd; +} + +/* + * iwlagn_add_bssid_station - Add the special IBSS BSSID station + * + * Function sleeps. + */ +int iwlagn_add_bssid_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + const u8 *addr, u8 *sta_id_r) +{ + int ret; + u8 sta_id; + struct iwl_link_quality_cmd *link_cmd; + + if (sta_id_r) + *sta_id_r = IWL_INVALID_STATION; + + ret = iwl_add_station_common(priv, ctx, addr, 0, NULL, &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM\n", addr); + return ret; + } + + if (sta_id_r) + *sta_id_r = sta_id; + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].used |= IWL_STA_LOCAL; + spin_unlock_bh(&priv->sta_lock); + + /* Set up default rate scaling table in device's station table */ + link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); + if (!link_cmd) { + IWL_ERR(priv, + "Unable to initialize rate scaling for station %pM.\n", + addr); + return -ENOMEM; + } + + ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true); + if (ret) + IWL_ERR(priv, "Link quality command failed (%d)\n", ret); + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].lq = link_cmd; + spin_unlock_bh(&priv->sta_lock); + + return 0; +} + +/* + * static WEP keys + * + * For each context, the device has a table of 4 static WEP keys + * (one for each key index) that is updated with the following + * commands. + */ + +static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + bool send_if_empty) +{ + int i, not_empty = 0; + u8 buff[sizeof(struct iwl_wep_cmd) + + sizeof(struct iwl_wep_key) * WEP_KEYS_MAX]; + struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff; + size_t cmd_size = sizeof(struct iwl_wep_cmd); + struct iwl_host_cmd cmd = { + .id = ctx->wep_key_cmd, + .data = { wep_cmd, }, + }; + + might_sleep(); + + memset(wep_cmd, 0, cmd_size + + (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX)); + + for (i = 0; i < WEP_KEYS_MAX ; i++) { + wep_cmd->key[i].key_index = i; + if (ctx->wep_keys[i].key_size) { + wep_cmd->key[i].key_offset = i; + not_empty = 1; + } else { + wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; + } + + wep_cmd->key[i].key_size = ctx->wep_keys[i].key_size; + memcpy(&wep_cmd->key[i].key[3], ctx->wep_keys[i].key, + ctx->wep_keys[i].key_size); + } + + wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; + wep_cmd->num_keys = WEP_KEYS_MAX; + + cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX; + + cmd.len[0] = cmd_size; + + if (not_empty || send_if_empty) + return iwl_dvm_send_cmd(priv, &cmd); + else + return 0; +} + +int iwl_restore_default_wep_keys(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + lockdep_assert_held(&priv->mutex); + + return iwl_send_static_wepkey_cmd(priv, ctx, false); +} + +int iwl_remove_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n", + keyconf->keyidx); + + memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0])); + if (iwl_is_rfkill(priv)) { + IWL_DEBUG_WEP(priv, + "Not sending REPLY_WEPKEY command due to RFKILL.\n"); + /* but keys in device are clear anyway so return success */ + return 0; + } + ret = iwl_send_static_wepkey_cmd(priv, ctx, 1); + IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n", + keyconf->keyidx, ret); + + return ret; +} + +int iwl_set_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + if (keyconf->keylen != WEP_KEY_LEN_128 && + keyconf->keylen != WEP_KEY_LEN_64) { + IWL_DEBUG_WEP(priv, + "Bad WEP key length %d\n", keyconf->keylen); + return -EINVAL; + } + + keyconf->hw_key_idx = IWLAGN_HW_KEY_DEFAULT; + + ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; + memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key, + keyconf->keylen); + + ret = iwl_send_static_wepkey_cmd(priv, ctx, false); + IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n", + keyconf->keylen, keyconf->keyidx, ret); + + return ret; +} + +/* + * dynamic (per-station) keys + * + * The dynamic keys are a little more complicated. The device has + * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys. + * These are linked to stations by a table that contains an index + * into the key table for each station/key index/{mcast,unicast}, + * i.e. it's basically an array of pointers like this: + * key_offset_t key_mapping[NUM_STATIONS][4][2]; + * (it really works differently, but you can think of it as such) + * + * The key uploading and linking happens in the same command, the + * add station command with STA_MODIFY_KEY_MASK. + */ + +static u8 iwlagn_key_sta_id(struct iwl_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + + if (sta) + return iwl_sta_id(sta); + + /* + * The device expects GTKs for station interfaces to be + * installed as GTKs for the AP station. If we have no + * station ID, then use the ap_sta_id in that case. + */ + if (vif->type == NL80211_IFTYPE_STATION && vif_priv->ctx) + return vif_priv->ctx->ap_sta_id; + + return IWL_INVALID_STATION; +} + +static int iwlagn_send_sta_key(struct iwl_priv *priv, + struct ieee80211_key_conf *keyconf, + u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k, + u32 cmd_flags) +{ + __le16 key_flags; + struct iwl_addsta_cmd sta_cmd; + int i; + + spin_lock_bh(&priv->sta_lock); + memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); + spin_unlock_bh(&priv->sta_lock); + + key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + key_flags |= STA_KEY_FLG_MAP_KEY_MSK; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + key_flags |= STA_KEY_FLG_CCMP; + memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); + break; + case WLAN_CIPHER_SUITE_TKIP: + key_flags |= STA_KEY_FLG_TKIP; + sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; + for (i = 0; i < 5; i++) + sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); + memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); + break; + case WLAN_CIPHER_SUITE_WEP104: + key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; + /* fall through */ + case WLAN_CIPHER_SUITE_WEP40: + key_flags |= STA_KEY_FLG_WEP; + memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen); + break; + default: + WARN_ON(1); + return -EINVAL; + } + + if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + key_flags |= STA_KEY_MULTICAST_MSK; + + /* key pointer (offset) */ + sta_cmd.key.key_offset = keyconf->hw_key_idx; + + sta_cmd.key.key_flags = key_flags; + sta_cmd.mode = STA_CONTROL_MODIFY_MSK; + sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; + + return iwl_send_add_sta(priv, &sta_cmd, cmd_flags); +} + +void iwl_update_tkip_key(struct iwl_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) +{ + u8 sta_id = iwlagn_key_sta_id(priv, vif, sta); + + if (sta_id == IWL_INVALID_STATION) + return; + + if (iwl_scan_cancel(priv)) { + /* cancel scan failed, just live w/ bad key and rely + briefly on SW decryption */ + return; + } + + iwlagn_send_sta_key(priv, keyconf, sta_id, + iv32, phase1key, CMD_ASYNC); +} + +int iwl_remove_dynamic_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta) +{ + struct iwl_addsta_cmd sta_cmd; + u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); + __le16 key_flags; + + /* if station isn't there, neither is the key */ + if (sta_id == IWL_INVALID_STATION) + return -ENOENT; + + spin_lock_bh(&priv->sta_lock); + memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); + if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) + sta_id = IWL_INVALID_STATION; + spin_unlock_bh(&priv->sta_lock); + + if (sta_id == IWL_INVALID_STATION) + return 0; + + lockdep_assert_held(&priv->mutex); + + ctx->key_mapping_keys--; + + IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n", + keyconf->keyidx, sta_id); + + if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table)) + IWL_ERR(priv, "offset %d not used in uCode key table.\n", + keyconf->hw_key_idx); + + key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + key_flags |= STA_KEY_FLG_MAP_KEY_MSK | STA_KEY_FLG_NO_ENC | + STA_KEY_FLG_INVALID; + + if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + key_flags |= STA_KEY_MULTICAST_MSK; + + sta_cmd.key.key_flags = key_flags; + sta_cmd.key.key_offset = keyconf->hw_key_idx; + sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; + sta_cmd.mode = STA_CONTROL_MODIFY_MSK; + + return iwl_send_add_sta(priv, &sta_cmd, 0); +} + +int iwl_set_dynamic_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta) +{ + struct ieee80211_key_seq seq; + u16 p1k[5]; + int ret; + u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); + const u8 *addr; + + if (sta_id == IWL_INVALID_STATION) + return -EINVAL; + + lockdep_assert_held(&priv->mutex); + + keyconf->hw_key_idx = iwl_get_free_ucode_key_offset(priv); + if (keyconf->hw_key_idx == WEP_INVALID_OFFSET) + return -ENOSPC; + + ctx->key_mapping_keys++; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + if (sta) + addr = sta->addr; + else /* station mode case only */ + addr = ctx->active.bssid_addr; + + /* pre-fill phase 1 key into device cache */ + ieee80211_get_key_rx_seq(keyconf, 0, &seq); + ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); + ret = iwlagn_send_sta_key(priv, keyconf, sta_id, + seq.tkip.iv32, p1k, 0); + break; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + ret = iwlagn_send_sta_key(priv, keyconf, sta_id, + 0, NULL, 0); + break; + default: + IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher); + ret = -EINVAL; + } + + if (ret) { + ctx->key_mapping_keys--; + clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table); + } + + IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", + keyconf->cipher, keyconf->keylen, keyconf->keyidx, + sta ? sta->addr : NULL, ret); + + return ret; +} + +/** + * iwlagn_alloc_bcast_station - add broadcast station into driver's station table. + * + * This adds the broadcast station into the driver's station table + * and marks it driver active, so that it will be restored to the + * device at the next best time. + */ +int iwlagn_alloc_bcast_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct iwl_link_quality_cmd *link_cmd; + u8 sta_id; + + spin_lock_bh(&priv->sta_lock); + sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Unable to prepare broadcast station\n"); + spin_unlock_bh(&priv->sta_lock); + + return -EINVAL; + } + + priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE; + priv->stations[sta_id].used |= IWL_STA_BCAST; + spin_unlock_bh(&priv->sta_lock); + + link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); + if (!link_cmd) { + IWL_ERR(priv, + "Unable to initialize rate scaling for bcast station.\n"); + return -ENOMEM; + } + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].lq = link_cmd; + spin_unlock_bh(&priv->sta_lock); + + return 0; +} + +/** + * iwl_update_bcast_station - update broadcast station's LQ command + * + * Only used by iwlagn. Placed here to have all bcast station management + * code together. + */ +int iwl_update_bcast_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct iwl_link_quality_cmd *link_cmd; + u8 sta_id = ctx->bcast_sta_id; + + link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); + if (!link_cmd) { + IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n"); + return -ENOMEM; + } + + spin_lock_bh(&priv->sta_lock); + if (priv->stations[sta_id].lq) + kfree(priv->stations[sta_id].lq); + else + IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n"); + priv->stations[sta_id].lq = link_cmd; + spin_unlock_bh(&priv->sta_lock); + + return 0; +} + +int iwl_update_bcast_stations(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + int ret = 0; + + for_each_context(priv, ctx) { + ret = iwl_update_bcast_station(priv, ctx); + if (ret) + break; + } + + return ret; +} + +/** + * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table + */ +int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) +{ + struct iwl_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + /* Remove "disable" flag, to enable Tx for this TID */ + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; + priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); + spin_unlock_bh(&priv->sta_lock); + + return iwl_send_add_sta(priv, &sta_cmd, 0); +} + +int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid, u16 ssn) +{ + int sta_id; + struct iwl_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + sta_id = iwl_sta_id(sta); + if (sta_id == IWL_INVALID_STATION) + return -ENXIO; + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].sta.station_flags_msk = 0; + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; + priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid; + priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); + spin_unlock_bh(&priv->sta_lock); + + return iwl_send_add_sta(priv, &sta_cmd, 0); +} + +int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid) +{ + int sta_id; + struct iwl_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + sta_id = iwl_sta_id(sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); + return -ENXIO; + } + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].sta.station_flags_msk = 0; + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; + priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid; + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); + spin_unlock_bh(&priv->sta_lock); + + return iwl_send_add_sta(priv, &sta_cmd, 0); +} + + + +void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) +{ + struct iwl_addsta_cmd cmd = { + .mode = STA_CONTROL_MODIFY_MSK, + .station_flags = STA_FLG_PWR_SAVE_MSK, + .station_flags_msk = STA_FLG_PWR_SAVE_MSK, + .sta.sta_id = sta_id, + .sta.modify_mask = STA_MODIFY_SLEEP_TX_COUNT_MSK, + .sleep_tx_count = cpu_to_le16(cnt), + }; + + iwl_send_add_sta(priv, &cmd, CMD_ASYNC); +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c new file mode 100644 index 000000000000..c4736c8834c5 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c @@ -0,0 +1,685 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + + +#include +#include +#include +#include +#include "iwl-io.h" +#include "iwl-modparams.h" +#include "iwl-debug.h" +#include "agn.h" +#include "dev.h" +#include "commands.h" +#include "tt.h" + +/* default Thermal Throttling transaction table + * Current state | Throttling Down | Throttling Up + *============================================================================= + * Condition Nxt State Condition Nxt State Condition Nxt State + *----------------------------------------------------------------------------- + * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 + * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 + *============================================================================= + */ +static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, + {IWL_TI_1, 105, CT_KILL_THRESHOLD - 1}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, + {IWL_TI_2, 110, CT_KILL_THRESHOLD - 1}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, + {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, + {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; + +/* Advance Thermal Throttling default restriction table */ +static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = { + {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true }, + {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true }, + {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false }, + {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false } +}; + +bool iwl_tt_is_low_power_state(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (tt->state >= IWL_TI_1) + return true; + return false; +} + +u8 iwl_tt_current_power_mode(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + return tt->tt_power_mode; +} + +bool iwl_ht_enabled(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + + if (!priv->thermal_throttle.advanced_tt) + return true; + restriction = tt->restriction + tt->state; + return restriction->is_ht; +} + +static bool iwl_within_ct_kill_margin(struct iwl_priv *priv) +{ + s32 temp = priv->temperature; /* degrees CELSIUS except specified */ + bool within_margin = false; + + if (!priv->thermal_throttle.advanced_tt) + within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= + CT_KILL_THRESHOLD_LEGACY) ? true : false; + else + within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= + CT_KILL_THRESHOLD) ? true : false; + return within_margin; +} + +bool iwl_check_for_ct_kill(struct iwl_priv *priv) +{ + bool is_ct_kill = false; + + if (iwl_within_ct_kill_margin(priv)) { + iwl_tt_enter_ct_kill(priv); + is_ct_kill = true; + } + return is_ct_kill; +} + +enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + + if (!priv->thermal_throttle.advanced_tt) + return IWL_ANT_OK_MULTI; + restriction = tt->restriction + tt->state; + return restriction->tx_stream; +} + +enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + + if (!priv->thermal_throttle.advanced_tt) + return IWL_ANT_OK_MULTI; + restriction = tt->restriction + tt->state; + return restriction->rx_stream; +} + +#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ +#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */ + +/* + * toggle the bit to wake up uCode and check the temperature + * if the temperature is below CT, uCode will stay awake and send card + * state notification with CT_KILL bit clear to inform Thermal Throttling + * Management to change state. Otherwise, uCode will go back to sleep + * without doing anything, driver should continue the 5 seconds timer + * to wake up uCode for temperature check until temperature drop below CT + */ +static void iwl_tt_check_exit_ct_kill(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + unsigned long flags; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (tt->state == IWL_TI_CT_KILL) { + if (priv->thermal_throttle.ct_kill_toggle) { + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + priv->thermal_throttle.ct_kill_toggle = false; + } else { + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + priv->thermal_throttle.ct_kill_toggle = true; + } + iwl_read32(priv->trans, CSR_UCODE_DRV_GP1); + if (iwl_trans_grab_nic_access(priv->trans, false, &flags)) + iwl_trans_release_nic_access(priv->trans, &flags); + + /* Reschedule the ct_kill timer to occur in + * CT_KILL_EXIT_DURATION seconds to ensure we get a + * thermal update */ + IWL_DEBUG_TEMP(priv, "schedule ct_kill exit timer\n"); + mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, + jiffies + CT_KILL_EXIT_DURATION * HZ); + } +} + +static void iwl_perform_ct_kill_task(struct iwl_priv *priv, + bool stop) +{ + if (stop) { + IWL_DEBUG_TEMP(priv, "Stop all queues\n"); + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + IWL_DEBUG_TEMP(priv, + "Schedule 5 seconds CT_KILL Timer\n"); + mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, + jiffies + CT_KILL_EXIT_DURATION * HZ); + } else { + IWL_DEBUG_TEMP(priv, "Wake all queues\n"); + if (priv->mac80211_registered) + ieee80211_wake_queues(priv->hw); + } +} + +static void iwl_tt_ready_for_ct_kill(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* temperature timer expired, ready to go into CT_KILL state */ + if (tt->state != IWL_TI_CT_KILL) { + IWL_DEBUG_TEMP(priv, "entering CT_KILL state when " + "temperature timer expired\n"); + tt->state = IWL_TI_CT_KILL; + set_bit(STATUS_CT_KILL, &priv->status); + iwl_perform_ct_kill_task(priv, true); + } +} + +static void iwl_prepare_ct_kill_task(struct iwl_priv *priv) +{ + IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n"); + /* make request to retrieve statistics information */ + iwl_send_statistics_request(priv, 0, false); + /* Reschedule the ct_kill wait timer */ + mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm, + jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION)); +} + +#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY) +#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100) +#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90) + +/* + * Legacy thermal throttling + * 1) Avoid NIC destruction due to high temperatures + * Chip will identify dangerously high temperatures that can + * harm the device and will power down + * 2) Avoid the NIC power down due to high temperature + * Throttle early enough to lower the power consumption before + * drastic steps are needed + */ +static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + enum iwl_tt_state old_state; + +#ifdef CONFIG_IWLWIFI_DEBUG + if ((tt->tt_previous_temp) && + (temp > tt->tt_previous_temp) && + ((temp - tt->tt_previous_temp) > + IWL_TT_INCREASE_MARGIN)) { + IWL_DEBUG_TEMP(priv, + "Temperature increase %d degree Celsius\n", + (temp - tt->tt_previous_temp)); + } +#endif + old_state = tt->state; + /* in Celsius */ + if (temp >= IWL_MINIMAL_POWER_THRESHOLD) + tt->state = IWL_TI_CT_KILL; + else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2) + tt->state = IWL_TI_2; + else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1) + tt->state = IWL_TI_1; + else + tt->state = IWL_TI_0; + +#ifdef CONFIG_IWLWIFI_DEBUG + tt->tt_previous_temp = temp; +#endif + /* stop ct_kill_waiting_tm timer */ + del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); + if (tt->state != old_state) { + switch (tt->state) { + case IWL_TI_0: + /* + * When the system is ready to go back to IWL_TI_0 + * we only have to call iwl_power_update_mode() to + * do so. + */ + break; + case IWL_TI_1: + tt->tt_power_mode = IWL_POWER_INDEX_3; + break; + case IWL_TI_2: + tt->tt_power_mode = IWL_POWER_INDEX_4; + break; + default: + tt->tt_power_mode = IWL_POWER_INDEX_5; + break; + } + mutex_lock(&priv->mutex); + if (old_state == IWL_TI_CT_KILL) + clear_bit(STATUS_CT_KILL, &priv->status); + if (tt->state != IWL_TI_CT_KILL && + iwl_power_update_mode(priv, true)) { + /* TT state not updated + * try again during next temperature read + */ + if (old_state == IWL_TI_CT_KILL) + set_bit(STATUS_CT_KILL, &priv->status); + tt->state = old_state; + IWL_ERR(priv, "Cannot update power mode, " + "TT state not updated\n"); + } else { + if (tt->state == IWL_TI_CT_KILL) { + if (force) { + set_bit(STATUS_CT_KILL, &priv->status); + iwl_perform_ct_kill_task(priv, true); + } else { + iwl_prepare_ct_kill_task(priv); + tt->state = old_state; + } + } else if (old_state == IWL_TI_CT_KILL && + tt->state != IWL_TI_CT_KILL) + iwl_perform_ct_kill_task(priv, false); + IWL_DEBUG_TEMP(priv, "Temperature state changed %u\n", + tt->state); + IWL_DEBUG_TEMP(priv, "Power Index change to %u\n", + tt->tt_power_mode); + } + mutex_unlock(&priv->mutex); + } +} + +/* + * Advance thermal throttling + * 1) Avoid NIC destruction due to high temperatures + * Chip will identify dangerously high temperatures that can + * harm the device and will power down + * 2) Avoid the NIC power down due to high temperature + * Throttle early enough to lower the power consumption before + * drastic steps are needed + * Actions include relaxing the power down sleep thresholds and + * decreasing the number of TX streams + * 3) Avoid throughput performance impact as much as possible + * + *============================================================================= + * Condition Nxt State Condition Nxt State Condition Nxt State + *----------------------------------------------------------------------------- + * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 + * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 + *============================================================================= + */ +static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + int i; + bool changed = false; + enum iwl_tt_state old_state; + struct iwl_tt_trans *transaction; + + old_state = tt->state; + for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) { + /* based on the current TT state, + * find the curresponding transaction table + * each table has (IWL_TI_STATE_MAX - 1) entries + * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) + * will advance to the correct table. + * then based on the current temperature + * find the next state need to transaction to + * go through all the possible (IWL_TI_STATE_MAX - 1) entries + * in the current table to see if transaction is needed + */ + transaction = tt->transaction + + ((old_state * (IWL_TI_STATE_MAX - 1)) + i); + if (temp >= transaction->tt_low && + temp <= transaction->tt_high) { +#ifdef CONFIG_IWLWIFI_DEBUG + if ((tt->tt_previous_temp) && + (temp > tt->tt_previous_temp) && + ((temp - tt->tt_previous_temp) > + IWL_TT_INCREASE_MARGIN)) { + IWL_DEBUG_TEMP(priv, + "Temperature increase %d " + "degree Celsius\n", + (temp - tt->tt_previous_temp)); + } + tt->tt_previous_temp = temp; +#endif + if (old_state != + transaction->next_state) { + changed = true; + tt->state = + transaction->next_state; + } + break; + } + } + /* stop ct_kill_waiting_tm timer */ + del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); + if (changed) { + if (tt->state >= IWL_TI_1) { + /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ + tt->tt_power_mode = IWL_POWER_INDEX_5; + + if (!iwl_ht_enabled(priv)) { + struct iwl_rxon_context *ctx; + + for_each_context(priv, ctx) { + struct iwl_rxon_cmd *rxon; + + rxon = &ctx->staging; + + /* disable HT */ + rxon->flags &= ~( + RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | + RXON_FLG_HT40_PROT_MSK | + RXON_FLG_HT_PROT_MSK); + } + } else { + /* check HT capability and set + * according to the system HT capability + * in case get disabled before */ + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + + } else { + /* + * restore system power setting -- it will be + * recalculated automatically. + */ + + /* check HT capability and set + * according to the system HT capability + * in case get disabled before */ + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + mutex_lock(&priv->mutex); + if (old_state == IWL_TI_CT_KILL) + clear_bit(STATUS_CT_KILL, &priv->status); + if (tt->state != IWL_TI_CT_KILL && + iwl_power_update_mode(priv, true)) { + /* TT state not updated + * try again during next temperature read + */ + IWL_ERR(priv, "Cannot update power mode, " + "TT state not updated\n"); + if (old_state == IWL_TI_CT_KILL) + set_bit(STATUS_CT_KILL, &priv->status); + tt->state = old_state; + } else { + IWL_DEBUG_TEMP(priv, + "Thermal Throttling to new state: %u\n", + tt->state); + if (old_state != IWL_TI_CT_KILL && + tt->state == IWL_TI_CT_KILL) { + if (force) { + IWL_DEBUG_TEMP(priv, + "Enter IWL_TI_CT_KILL\n"); + set_bit(STATUS_CT_KILL, &priv->status); + iwl_perform_ct_kill_task(priv, true); + } else { + tt->state = old_state; + iwl_prepare_ct_kill_task(priv); + } + } else if (old_state == IWL_TI_CT_KILL && + tt->state != IWL_TI_CT_KILL) { + IWL_DEBUG_TEMP(priv, "Exit IWL_TI_CT_KILL\n"); + iwl_perform_ct_kill_task(priv, false); + } + } + mutex_unlock(&priv->mutex); + } +} + +/* Card State Notification indicated reach critical temperature + * if PSP not enable, no Thermal Throttling function will be performed + * just set the GP1 bit to acknowledge the event + * otherwise, go into IWL_TI_CT_KILL state + * since Card State Notification will not provide any temperature reading + * for Legacy mode + * so just pass the CT_KILL temperature to iwl_legacy_tt_handler() + * for advance mode + * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state + */ +static void iwl_bg_ct_enter(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter); + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!iwl_is_ready(priv)) + return; + + if (tt->state != IWL_TI_CT_KILL) { + IWL_ERR(priv, "Device reached critical temperature " + "- ucode going to sleep!\n"); + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, + IWL_MINIMAL_POWER_THRESHOLD, + true); + else + iwl_advance_tt_handler(priv, + CT_KILL_THRESHOLD + 1, true); + } +} + +/* Card State Notification indicated out of critical temperature + * since Card State Notification will not provide any temperature reading + * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature + * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state + */ +static void iwl_bg_ct_exit(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit); + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!iwl_is_ready(priv)) + return; + + /* stop ct_kill_exit_tm timer */ + del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); + + if (tt->state == IWL_TI_CT_KILL) { + IWL_ERR(priv, + "Device temperature below critical" + "- ucode awake!\n"); + /* + * exit from CT_KILL state + * reset the current temperature reading + */ + priv->temperature = 0; + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, + IWL_REDUCED_PERFORMANCE_THRESHOLD_2, + true); + else + iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD, + true); + } +} + +void iwl_tt_enter_ct_kill(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_TEMP(priv, "Queueing critical temperature enter.\n"); + queue_work(priv->workqueue, &priv->ct_enter); +} + +void iwl_tt_exit_ct_kill(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_TEMP(priv, "Queueing critical temperature exit.\n"); + queue_work(priv->workqueue, &priv->ct_exit); +} + +static void iwl_bg_tt_work(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work); + s32 temp = priv->temperature; /* degrees CELSIUS except specified */ + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, temp, false); + else + iwl_advance_tt_handler(priv, temp, false); +} + +void iwl_tt_handler(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_TEMP(priv, "Queueing thermal throttling work.\n"); + queue_work(priv->workqueue, &priv->tt_work); +} + +/* Thermal throttling initialization + * For advance thermal throttling: + * Initialize Thermal Index and temperature threshold table + * Initialize thermal throttling restriction table + */ +void iwl_tt_initialize(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1); + struct iwl_tt_trans *transaction; + + IWL_DEBUG_TEMP(priv, "Initialize Thermal Throttling\n"); + + memset(tt, 0, sizeof(struct iwl_tt_mgmt)); + + tt->state = IWL_TI_0; + setup_timer(&priv->thermal_throttle.ct_kill_exit_tm, + iwl_tt_check_exit_ct_kill, (unsigned long)priv); + setup_timer(&priv->thermal_throttle.ct_kill_waiting_tm, + iwl_tt_ready_for_ct_kill, (unsigned long)priv); + /* setup deferred ct kill work */ + INIT_WORK(&priv->tt_work, iwl_bg_tt_work); + INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); + INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); + + if (priv->lib->adv_thermal_throttle) { + IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); + tt->restriction = kcalloc(IWL_TI_STATE_MAX, + sizeof(struct iwl_tt_restriction), + GFP_KERNEL); + tt->transaction = kcalloc(IWL_TI_STATE_MAX * + (IWL_TI_STATE_MAX - 1), + sizeof(struct iwl_tt_trans), + GFP_KERNEL); + if (!tt->restriction || !tt->transaction) { + IWL_ERR(priv, "Fallback to Legacy Throttling\n"); + priv->thermal_throttle.advanced_tt = false; + kfree(tt->restriction); + tt->restriction = NULL; + kfree(tt->transaction); + tt->transaction = NULL; + } else { + transaction = tt->transaction + + (IWL_TI_0 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_0[0], size); + transaction = tt->transaction + + (IWL_TI_1 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_1[0], size); + transaction = tt->transaction + + (IWL_TI_2 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_2[0], size); + transaction = tt->transaction + + (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_3[0], size); + size = sizeof(struct iwl_tt_restriction) * + IWL_TI_STATE_MAX; + memcpy(tt->restriction, + &restriction_range[0], size); + priv->thermal_throttle.advanced_tt = true; + } + } else { + IWL_DEBUG_TEMP(priv, "Legacy Thermal Throttling\n"); + priv->thermal_throttle.advanced_tt = false; + } +} + +/* cleanup thermal throttling management related memory and timer */ +void iwl_tt_exit(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + /* stop ct_kill_exit_tm timer if activated */ + del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); + /* stop ct_kill_waiting_tm timer if activated */ + del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); + cancel_work_sync(&priv->tt_work); + cancel_work_sync(&priv->ct_enter); + cancel_work_sync(&priv->ct_exit); + + if (priv->thermal_throttle.advanced_tt) { + /* free advance thermal throttling memory */ + kfree(tt->restriction); + tt->restriction = NULL; + kfree(tt->transaction); + tt->transaction = NULL; + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h new file mode 100644 index 000000000000..507726534b84 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h @@ -0,0 +1,128 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ +#ifndef __iwl_tt_setting_h__ +#define __iwl_tt_setting_h__ + +#include "commands.h" + +#define IWL_ABSOLUTE_ZERO 0 +#define IWL_ABSOLUTE_MAX 0xFFFFFFFF +#define IWL_TT_INCREASE_MARGIN 5 +#define IWL_TT_CT_KILL_MARGIN 3 + +enum iwl_antenna_ok { + IWL_ANT_OK_NONE, + IWL_ANT_OK_SINGLE, + IWL_ANT_OK_MULTI, +}; + +/* Thermal Throttling State Machine states */ +enum iwl_tt_state { + IWL_TI_0, /* normal temperature, system power state */ + IWL_TI_1, /* high temperature detect, low power state */ + IWL_TI_2, /* higher temperature detected, lower power state */ + IWL_TI_CT_KILL, /* critical temperature detected, lowest power state */ + IWL_TI_STATE_MAX +}; + +/** + * struct iwl_tt_restriction - Thermal Throttling restriction table + * @tx_stream: number of tx stream allowed + * @is_ht: ht enable/disable + * @rx_stream: number of rx stream allowed + * + * This table is used by advance thermal throttling management + * based on the current thermal throttling state, and determines + * the number of tx/rx streams and the status of HT operation. + */ +struct iwl_tt_restriction { + enum iwl_antenna_ok tx_stream; + enum iwl_antenna_ok rx_stream; + bool is_ht; +}; + +/** + * struct iwl_tt_trans - Thermal Throttling transaction table + * @next_state: next thermal throttling mode + * @tt_low: low temperature threshold to change state + * @tt_high: high temperature threshold to change state + * + * This is used by the advanced thermal throttling algorithm + * to determine the next thermal state to go based on the + * current temperature. + */ +struct iwl_tt_trans { + enum iwl_tt_state next_state; + u32 tt_low; + u32 tt_high; +}; + +/** + * struct iwl_tt_mgnt - Thermal Throttling Management structure + * @advanced_tt: advanced thermal throttle required + * @state: current Thermal Throttling state + * @tt_power_mode: Thermal Throttling power mode index + * being used to set power level when + * when thermal throttling state != IWL_TI_0 + * the tt_power_mode should set to different + * power mode based on the current tt state + * @tt_previous_temperature: last measured temperature + * @iwl_tt_restriction: ptr to restriction tbl, used by advance + * thermal throttling to determine how many tx/rx streams + * should be used in tt state; and can HT be enabled or not + * @iwl_tt_trans: ptr to adv trans table, used by advance thermal throttling + * state transaction + * @ct_kill_toggle: used to toggle the CSR bit when checking uCode temperature + * @ct_kill_exit_tm: timer to exit thermal kill + */ +struct iwl_tt_mgmt { + enum iwl_tt_state state; + bool advanced_tt; + u8 tt_power_mode; + bool ct_kill_toggle; +#ifdef CONFIG_IWLWIFI_DEBUG + s32 tt_previous_temp; +#endif + struct iwl_tt_restriction *restriction; + struct iwl_tt_trans *transaction; + struct timer_list ct_kill_exit_tm; + struct timer_list ct_kill_waiting_tm; +}; + +u8 iwl_tt_current_power_mode(struct iwl_priv *priv); +bool iwl_tt_is_low_power_state(struct iwl_priv *priv); +bool iwl_ht_enabled(struct iwl_priv *priv); +enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv); +enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv); +void iwl_tt_enter_ct_kill(struct iwl_priv *priv); +void iwl_tt_exit_ct_kill(struct iwl_priv *priv); +void iwl_tt_handler(struct iwl_priv *priv); +void iwl_tt_initialize(struct iwl_priv *priv); +void iwl_tt_exit(struct iwl_priv *priv); + +#endif /* __iwl_tt_setting_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c new file mode 100644 index 000000000000..bddd19769035 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c @@ -0,0 +1,1412 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include +#include +#include "iwl-io.h" +#include "iwl-trans.h" +#include "iwl-agn-hw.h" +#include "dev.h" +#include "agn.h" + +static const u8 tid_to_ac[] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO, +}; + +static void iwlagn_tx_cmd_protection(struct iwl_priv *priv, + struct ieee80211_tx_info *info, + __le16 fc, __le32 *tx_flags) +{ + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS || + info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT || + info->flags & IEEE80211_TX_CTL_AMPDU) + *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK; +} + +/* + * handle build REPLY_TX command notification. + */ +static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, + struct sk_buff *skb, + struct iwl_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, u8 sta_id) +{ + __le16 fc = hdr->frame_control; + __le32 tx_flags = tx_cmd->tx_flags; + + tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + tx_flags |= TX_CMD_FLG_ACK_MSK; + else + tx_flags &= ~TX_CMD_FLG_ACK_MSK; + + if (ieee80211_is_probe_resp(fc)) + tx_flags |= TX_CMD_FLG_TSF_MSK; + else if (ieee80211_is_back_req(fc)) + tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; + else if (info->band == IEEE80211_BAND_2GHZ && + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && + (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || + ieee80211_is_reassoc_req(fc) || + info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) + tx_flags |= TX_CMD_FLG_IGNORE_BT; + + + tx_cmd->sta_id = sta_id; + if (ieee80211_has_morefrags(fc)) + tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; + + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tx_cmd->tid_tspec = qc[0] & 0xf; + tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; + } else { + tx_cmd->tid_tspec = IWL_TID_NON_QOS; + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + else + tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; + } + + iwlagn_tx_cmd_protection(priv, info, fc, &tx_flags); + + tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); + if (ieee80211_is_mgmt(fc)) { + if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); + else + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); + } else { + tx_cmd->timeout.pm_frame_timeout = 0; + } + + tx_cmd->driver_txop = 0; + tx_cmd->tx_flags = tx_flags; + tx_cmd->next_frame_len = 0; +} + +static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, + struct iwl_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + __le16 fc) +{ + u32 rate_flags; + int rate_idx; + u8 rts_retry_limit; + u8 data_retry_limit; + u8 rate_plcp; + + if (priv->wowlan) { + rts_retry_limit = IWLAGN_LOW_RETRY_LIMIT; + data_retry_limit = IWLAGN_LOW_RETRY_LIMIT; + } else { + /* Set retry limit on RTS packets */ + rts_retry_limit = IWLAGN_RTS_DFAULT_RETRY_LIMIT; + + /* Set retry limit on DATA packets and Probe Responses*/ + if (ieee80211_is_probe_resp(fc)) { + data_retry_limit = IWLAGN_MGMT_DFAULT_RETRY_LIMIT; + rts_retry_limit = + min(data_retry_limit, rts_retry_limit); + } else if (ieee80211_is_back_req(fc)) + data_retry_limit = IWLAGN_BAR_DFAULT_RETRY_LIMIT; + else + data_retry_limit = IWLAGN_DEFAULT_TX_RETRY; + } + + tx_cmd->data_retry_limit = data_retry_limit; + tx_cmd->rts_retry_limit = rts_retry_limit; + + /* DATA packets will use the uCode station table for rate/antenna + * selection */ + if (ieee80211_is_data(fc)) { + tx_cmd->initial_rate_index = 0; + tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; + return; + } else if (ieee80211_is_back_req(fc)) + tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; + + /** + * If the current TX rate stored in mac80211 has the MCS bit set, it's + * not really a TX rate. Thus, we use the lowest supported rate for + * this band. Also use the lowest supported rate if the stored rate + * index is invalid. + */ + rate_idx = info->control.rates[0].idx; + if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS || + (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY)) + rate_idx = rate_lowest_index( + &priv->nvm_data->bands[info->band], sta); + /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ + if (info->band == IEEE80211_BAND_5GHZ) + rate_idx += IWL_FIRST_OFDM_RATE; + /* Get PLCP rate for tx_cmd->rate_n_flags */ + rate_plcp = iwl_rates[rate_idx].plcp; + /* Zero out flags for this packet */ + rate_flags = 0; + + /* Set CCK flag as needed */ + if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) + rate_flags |= RATE_MCS_CCK_MSK; + + /* Set up antennas */ + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && + priv->bt_full_concurrent) { + /* operated as 1x1 in full concurrency mode */ + priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, + first_antenna(priv->nvm_data->valid_tx_ant)); + } else + priv->mgmt_tx_ant = iwl_toggle_tx_ant( + priv, priv->mgmt_tx_ant, + priv->nvm_data->valid_tx_ant); + rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant); + + /* Set the rate in the TX cmd */ + tx_cmd->rate_n_flags = iwl_hw_set_rate_n_flags(rate_plcp, rate_flags); +} + +static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv, + struct ieee80211_tx_info *info, + struct iwl_tx_cmd *tx_cmd, + struct sk_buff *skb_frag) +{ + struct ieee80211_key_conf *keyconf = info->control.hw_key; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + tx_cmd->sec_ctl = TX_CMD_SEC_CCM; + memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); + if (info->flags & IEEE80211_TX_CTL_AMPDU) + tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; + break; + + case WLAN_CIPHER_SUITE_TKIP: + tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; + ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); + break; + + case WLAN_CIPHER_SUITE_WEP104: + tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; + /* fall through */ + case WLAN_CIPHER_SUITE_WEP40: + tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP | + (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); + + memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); + + IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " + "with key %d\n", keyconf->keyidx); + break; + + default: + IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher); + break; + } +} + +/** + * iwl_sta_id_or_broadcast - return sta_id or broadcast sta + * @context: the current context + * @sta: mac80211 station + * + * In certain circumstances mac80211 passes a station pointer + * that may be %NULL, for example during TX or key setup. In + * that case, we need to use the broadcast station, so this + * inline wraps that pattern. + */ +static int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context, + struct ieee80211_sta *sta) +{ + int sta_id; + + if (!sta) + return context->bcast_sta_id; + + sta_id = iwl_sta_id(sta); + + /* + * mac80211 should not be passing a partially + * initialised station! + */ + WARN_ON(sta_id == IWL_INVALID_STATION); + + return sta_id; +} + +/* + * start REPLY_TX command process + */ +int iwlagn_tx_skb(struct iwl_priv *priv, + struct ieee80211_sta *sta, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_station_priv *sta_priv = NULL; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl_device_cmd *dev_cmd; + struct iwl_tx_cmd *tx_cmd; + __le16 fc; + u8 hdr_len; + u16 len, seq_number = 0; + u8 sta_id, tid = IWL_MAX_TID_COUNT; + bool is_agg = false, is_data_qos = false; + int txq_id; + + if (info->control.vif) + ctx = iwl_rxon_ctx_from_vif(info->control.vif); + + if (iwl_is_rfkill(priv)) { + IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n"); + goto drop_unlock_priv; + } + + fc = hdr->frame_control; + +#ifdef CONFIG_IWLWIFI_DEBUG + if (ieee80211_is_auth(fc)) + IWL_DEBUG_TX(priv, "Sending AUTH frame\n"); + else if (ieee80211_is_assoc_req(fc)) + IWL_DEBUG_TX(priv, "Sending ASSOC frame\n"); + else if (ieee80211_is_reassoc_req(fc)) + IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); +#endif + + if (unlikely(ieee80211_is_probe_resp(fc))) { + struct iwl_wipan_noa_data *noa_data = + rcu_dereference(priv->noa_data); + + if (noa_data && + pskb_expand_head(skb, 0, noa_data->length, + GFP_ATOMIC) == 0) { + memcpy(skb_put(skb, noa_data->length), + noa_data->data, noa_data->length); + hdr = (struct ieee80211_hdr *)skb->data; + } + } + + hdr_len = ieee80211_hdrlen(fc); + + /* For management frames use broadcast id to do not break aggregation */ + if (!ieee80211_is_data(fc)) + sta_id = ctx->bcast_sta_id; + else { + /* Find index into station table for destination station */ + sta_id = iwl_sta_id_or_broadcast(ctx, sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", + hdr->addr1); + goto drop_unlock_priv; + } + } + + if (sta) + sta_priv = (void *)sta->drv_priv; + + if (sta_priv && sta_priv->asleep && + (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { + /* + * This sends an asynchronous command to the device, + * but we can rely on it being processed before the + * next frame is processed -- and the next frame to + * this station is the one that will consume this + * counter. + * For now set the counter to just 1 since we do not + * support uAPSD yet. + * + * FIXME: If we get two non-bufferable frames one + * after the other, we might only send out one of + * them because this is racy. + */ + iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); + } + + dev_cmd = iwl_trans_alloc_tx_cmd(priv->trans); + + if (unlikely(!dev_cmd)) + goto drop_unlock_priv; + + memset(dev_cmd, 0, sizeof(*dev_cmd)); + dev_cmd->hdr.cmd = REPLY_TX; + tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; + + /* Total # bytes to be transmitted */ + len = (u16)skb->len; + tx_cmd->len = cpu_to_le16(len); + + if (info->control.hw_key) + iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb); + + /* TODO need this for burst mode later on */ + iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id); + + iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, sta, fc); + + memset(&info->status, 0, sizeof(info->status)); + + info->driver_data[0] = ctx; + info->driver_data[1] = dev_cmd; + /* From now on, we cannot access info->control */ + + spin_lock(&priv->sta_lock); + + if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { + u8 *qc = NULL; + struct iwl_tid_data *tid_data; + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) + goto drop_unlock_sta; + tid_data = &priv->tid_data[sta_id][tid]; + + /* aggregation is on for this */ + if (info->flags & IEEE80211_TX_CTL_AMPDU && + tid_data->agg.state != IWL_AGG_ON) { + IWL_ERR(priv, + "TX_CTL_AMPDU while not in AGG: Tx flags = 0x%08x, agg.state = %d\n", + info->flags, tid_data->agg.state); + IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d\n", + sta_id, tid, + IEEE80211_SEQ_TO_SN(tid_data->seq_number)); + goto drop_unlock_sta; + } + + /* We can receive packets from the stack in IWL_AGG_{ON,OFF} + * only. Check this here. + */ + if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON && + tid_data->agg.state != IWL_AGG_OFF, + "Tx while agg.state = %d\n", tid_data->agg.state)) + goto drop_unlock_sta; + + seq_number = tid_data->seq_number; + 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; + + if (info->flags & IEEE80211_TX_CTL_AMPDU) + is_agg = true; + is_data_qos = true; + } + + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, hdr_len); + + txq_id = info->hw_queue; + + if (is_agg) + txq_id = priv->tid_data[sta_id][tid].agg.txq_id; + else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { + /* + * The microcode will clear the more data + * bit in the last frame it transmits. + */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + + WARN_ON_ONCE(is_agg && + priv->queue_to_mac80211[txq_id] != info->hw_queue); + + IWL_DEBUG_TX(priv, "TX to [%d|%d] Q:%d - seq: 0x%x\n", sta_id, tid, + txq_id, seq_number); + + if (iwl_trans_tx(priv->trans, skb, dev_cmd, txq_id)) + goto drop_unlock_sta; + + if (is_data_qos && !ieee80211_has_morefrags(fc)) + priv->tid_data[sta_id][tid].seq_number = seq_number; + + spin_unlock(&priv->sta_lock); + + /* + * Avoid atomic ops if it isn't an associated client. + * Also, if this is a packet for aggregation, don't + * increase the counter because the ucode will stop + * aggregation queues when their respective station + * goes to sleep. + */ + if (sta_priv && sta_priv->client && !is_agg) + atomic_inc(&sta_priv->pending_frames); + + return 0; + +drop_unlock_sta: + if (dev_cmd) + iwl_trans_free_tx_cmd(priv->trans, dev_cmd); + spin_unlock(&priv->sta_lock); +drop_unlock_priv: + return -1; +} + +static int iwlagn_alloc_agg_txq(struct iwl_priv *priv, int mq) +{ + int q; + + for (q = IWLAGN_FIRST_AMPDU_QUEUE; + q < priv->cfg->base_params->num_of_queues; q++) { + if (!test_and_set_bit(q, priv->agg_q_alloc)) { + priv->queue_to_mac80211[q] = mq; + return q; + } + } + + return -ENOSPC; +} + +static void iwlagn_dealloc_agg_txq(struct iwl_priv *priv, int q) +{ + clear_bit(q, priv->agg_q_alloc); + priv->queue_to_mac80211[q] = IWL_INVALID_MAC80211_QUEUE; +} + +int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid) +{ + struct iwl_tid_data *tid_data; + int sta_id, txq_id; + enum iwl_agg_state agg_state; + + sta_id = iwl_sta_id(sta); + + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); + return -ENXIO; + } + + spin_lock_bh(&priv->sta_lock); + + tid_data = &priv->tid_data[sta_id][tid]; + txq_id = tid_data->agg.txq_id; + + switch (tid_data->agg.state) { + case IWL_EMPTYING_HW_QUEUE_ADDBA: + /* + * This can happen if the peer stops aggregation + * again before we've had a chance to drain the + * queue we selected previously, i.e. before the + * session was really started completely. + */ + IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); + goto turn_off; + case IWL_AGG_STARTING: + /* + * This can happen when the session is stopped before + * we receive ADDBA response + */ + IWL_DEBUG_HT(priv, "AGG stop before AGG became operational\n"); + goto turn_off; + case IWL_AGG_ON: + break; + default: + IWL_WARN(priv, + "Stopping AGG while state not ON or starting for %d on %d (%d)\n", + sta_id, tid, tid_data->agg.state); + spin_unlock_bh(&priv->sta_lock); + return 0; + } + + tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); + + /* There are still packets for this RA / TID in the HW */ + if (!test_bit(txq_id, priv->agg_q_alloc)) { + IWL_DEBUG_TX_QUEUES(priv, + "stopping AGG on STA/TID %d/%d but hwq %d not used\n", + sta_id, tid, txq_id); + } else if (tid_data->agg.ssn != tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, + "Can't proceed: ssn %d, next_recl = %d\n", + tid_data->agg.ssn, + tid_data->next_reclaimed); + tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_DELBA; + spin_unlock_bh(&priv->sta_lock); + return 0; + } + + IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", + tid_data->agg.ssn); +turn_off: + agg_state = tid_data->agg.state; + tid_data->agg.state = IWL_AGG_OFF; + + spin_unlock_bh(&priv->sta_lock); + + if (test_bit(txq_id, priv->agg_q_alloc)) { + /* + * If the transport didn't know that we wanted to start + * agreggation, don't tell it that we want to stop them. + * This can happen when we don't get the addBA response on + * time, or we hadn't time to drain the AC queues. + */ + if (agg_state == IWL_AGG_ON) + iwl_trans_txq_disable(priv->trans, txq_id, true); + else + IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", + agg_state); + iwlagn_dealloc_agg_txq(priv, txq_id); + } + + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + + return 0; +} + +int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) +{ + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + struct iwl_tid_data *tid_data; + int sta_id, txq_id, ret; + + IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n", + sta->addr, tid); + + sta_id = iwl_sta_id(sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Start AGG on invalid station\n"); + return -ENXIO; + } + if (unlikely(tid >= IWL_MAX_TID_COUNT)) + return -EINVAL; + + if (priv->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) { + IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n"); + return -ENXIO; + } + + txq_id = iwlagn_alloc_agg_txq(priv, ctx->ac_to_queue[tid_to_ac[tid]]); + if (txq_id < 0) { + IWL_DEBUG_TX_QUEUES(priv, + "No free aggregation queue for %pM/%d\n", + sta->addr, tid); + return txq_id; + } + + ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); + if (ret) + return ret; + + spin_lock_bh(&priv->sta_lock); + tid_data = &priv->tid_data[sta_id][tid]; + tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); + tid_data->agg.txq_id = txq_id; + + *ssn = tid_data->agg.ssn; + + if (*ssn == tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", + tid_data->agg.ssn); + tid_data->agg.state = IWL_AGG_STARTING; + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + } else { + IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " + "next_reclaimed = %d\n", + tid_data->agg.ssn, + tid_data->next_reclaimed); + tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; + } + spin_unlock_bh(&priv->sta_lock); + + return ret; +} + +int iwlagn_tx_agg_flush(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid) +{ + struct iwl_tid_data *tid_data; + enum iwl_agg_state agg_state; + int sta_id, txq_id; + sta_id = iwl_sta_id(sta); + + /* + * First set the agg state to OFF to avoid calling + * ieee80211_stop_tx_ba_cb in iwlagn_check_ratid_empty. + */ + spin_lock_bh(&priv->sta_lock); + + tid_data = &priv->tid_data[sta_id][tid]; + txq_id = tid_data->agg.txq_id; + agg_state = tid_data->agg.state; + IWL_DEBUG_TX_QUEUES(priv, "Flush AGG: sta %d tid %d q %d state %d\n", + sta_id, tid, txq_id, tid_data->agg.state); + + tid_data->agg.state = IWL_AGG_OFF; + + spin_unlock_bh(&priv->sta_lock); + + if (iwlagn_txfifo_flush(priv, BIT(txq_id))) + IWL_ERR(priv, "Couldn't flush the AGG queue\n"); + + if (test_bit(txq_id, priv->agg_q_alloc)) { + /* + * If the transport didn't know that we wanted to start + * agreggation, don't tell it that we want to stop them. + * This can happen when we don't get the addBA response on + * time, or we hadn't time to drain the AC queues. + */ + if (agg_state == IWL_AGG_ON) + iwl_trans_txq_disable(priv->trans, txq_id, true); + else + IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", + agg_state); + iwlagn_dealloc_agg_txq(priv, txq_id); + } + + return 0; +} + +int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u8 buf_size) +{ + struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + int q, fifo; + u16 ssn; + + buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); + + spin_lock_bh(&priv->sta_lock); + ssn = priv->tid_data[sta_priv->sta_id][tid].agg.ssn; + q = priv->tid_data[sta_priv->sta_id][tid].agg.txq_id; + priv->tid_data[sta_priv->sta_id][tid].agg.state = IWL_AGG_ON; + spin_unlock_bh(&priv->sta_lock); + + fifo = ctx->ac_to_fifo[tid_to_ac[tid]]; + + iwl_trans_txq_enable(priv->trans, q, fifo, sta_priv->sta_id, tid, + buf_size, ssn, 0); + + /* + * If the limit is 0, then it wasn't initialised yet, + * use the default. We can do that since we take the + * minimum below, and we don't want to go above our + * default due to hardware restrictions. + */ + if (sta_priv->max_agg_bufsize == 0) + sta_priv->max_agg_bufsize = + LINK_QUAL_AGG_FRAME_LIMIT_DEF; + + /* + * Even though in theory the peer could have different + * aggregation reorder buffer sizes for different sessions, + * our ucode doesn't allow for that and has a global limit + * for each station. Therefore, use the minimum of all the + * aggregation sessions and our default value. + */ + sta_priv->max_agg_bufsize = + min(sta_priv->max_agg_bufsize, buf_size); + + if (priv->hw_params.use_rts_for_aggregation) { + /* + * switch to RTS/CTS if it is the prefer protection + * method for HT traffic + */ + + sta_priv->lq_sta.lq.general_params.flags |= + LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; + } + priv->agg_tids_count++; + IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", + priv->agg_tids_count); + + sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit = + sta_priv->max_agg_bufsize; + + IWL_DEBUG_HT(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", + sta->addr, tid); + + return iwl_send_lq_cmd(priv, ctx, + &sta_priv->lq_sta.lq, CMD_ASYNC, false); +} + +static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) +{ + struct iwl_tid_data *tid_data = &priv->tid_data[sta_id][tid]; + enum iwl_rxon_context_id ctx; + struct ieee80211_vif *vif; + u8 *addr; + + lockdep_assert_held(&priv->sta_lock); + + addr = priv->stations[sta_id].sta.sta.addr; + ctx = priv->stations[sta_id].ctxid; + vif = priv->contexts[ctx].vif; + + switch (priv->tid_data[sta_id][tid].agg.state) { + case IWL_EMPTYING_HW_QUEUE_DELBA: + /* There are no packets for this RA / TID in the HW any more */ + if (tid_data->agg.ssn == tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, + "Can continue DELBA flow ssn = next_recl = %d\n", + tid_data->next_reclaimed); + iwl_trans_txq_disable(priv->trans, + tid_data->agg.txq_id, true); + iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id); + tid_data->agg.state = IWL_AGG_OFF; + ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); + } + break; + case IWL_EMPTYING_HW_QUEUE_ADDBA: + /* There are no packets for this RA / TID in the HW any more */ + if (tid_data->agg.ssn == tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, + "Can continue ADDBA flow ssn = next_recl = %d\n", + tid_data->next_reclaimed); + tid_data->agg.state = IWL_AGG_STARTING; + ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); + } + break; + default: + break; + } +} + +static void iwlagn_non_agg_tx_status(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + const u8 *addr1) +{ + struct ieee80211_sta *sta; + struct iwl_station_priv *sta_priv; + + rcu_read_lock(); + sta = ieee80211_find_sta(ctx->vif, addr1); + if (sta) { + sta_priv = (void *)sta->drv_priv; + /* avoid atomic ops if this isn't a client */ + if (sta_priv->client && + atomic_dec_return(&sta_priv->pending_frames) == 0) + ieee80211_sta_block_awake(priv->hw, sta, false); + } + rcu_read_unlock(); +} + +/** + * translate ucode response to mac80211 tx status control values + */ +static void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, + struct ieee80211_tx_info *info) +{ + struct ieee80211_tx_rate *r = &info->status.rates[0]; + + info->status.antenna = + ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); + if (rate_n_flags & RATE_MCS_HT_MSK) + r->flags |= IEEE80211_TX_RC_MCS; + if (rate_n_flags & RATE_MCS_GF_MSK) + r->flags |= IEEE80211_TX_RC_GREEN_FIELD; + if (rate_n_flags & RATE_MCS_HT40_MSK) + r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + if (rate_n_flags & RATE_MCS_DUP_MSK) + r->flags |= IEEE80211_TX_RC_DUP_DATA; + if (rate_n_flags & RATE_MCS_SGI_MSK) + r->flags |= IEEE80211_TX_RC_SHORT_GI; + r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band); +} + +#ifdef CONFIG_IWLWIFI_DEBUG +const char *iwl_get_tx_fail_reason(u32 status) +{ +#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x +#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x + + switch (status & TX_STATUS_MSK) { + case TX_STATUS_SUCCESS: + return "SUCCESS"; + TX_STATUS_POSTPONE(DELAY); + TX_STATUS_POSTPONE(FEW_BYTES); + TX_STATUS_POSTPONE(BT_PRIO); + TX_STATUS_POSTPONE(QUIET_PERIOD); + TX_STATUS_POSTPONE(CALC_TTAK); + TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); + TX_STATUS_FAIL(SHORT_LIMIT); + TX_STATUS_FAIL(LONG_LIMIT); + TX_STATUS_FAIL(FIFO_UNDERRUN); + TX_STATUS_FAIL(DRAIN_FLOW); + TX_STATUS_FAIL(RFKILL_FLUSH); + TX_STATUS_FAIL(LIFE_EXPIRE); + TX_STATUS_FAIL(DEST_PS); + TX_STATUS_FAIL(HOST_ABORTED); + TX_STATUS_FAIL(BT_RETRY); + TX_STATUS_FAIL(STA_INVALID); + TX_STATUS_FAIL(FRAG_DROPPED); + TX_STATUS_FAIL(TID_DISABLE); + TX_STATUS_FAIL(FIFO_FLUSHED); + TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); + TX_STATUS_FAIL(PASSIVE_NO_RX); + TX_STATUS_FAIL(NO_BEACON_ON_RADAR); + } + + return "UNKNOWN"; + +#undef TX_STATUS_FAIL +#undef TX_STATUS_POSTPONE +} +#endif /* CONFIG_IWLWIFI_DEBUG */ + +static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) +{ + status &= AGG_TX_STATUS_MSK; + + switch (status) { + case AGG_TX_STATE_UNDERRUN_MSK: + priv->reply_agg_tx_stats.underrun++; + break; + case AGG_TX_STATE_BT_PRIO_MSK: + priv->reply_agg_tx_stats.bt_prio++; + break; + case AGG_TX_STATE_FEW_BYTES_MSK: + priv->reply_agg_tx_stats.few_bytes++; + break; + case AGG_TX_STATE_ABORT_MSK: + priv->reply_agg_tx_stats.abort++; + break; + case AGG_TX_STATE_LAST_SENT_TTL_MSK: + priv->reply_agg_tx_stats.last_sent_ttl++; + break; + case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK: + priv->reply_agg_tx_stats.last_sent_try++; + break; + case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK: + priv->reply_agg_tx_stats.last_sent_bt_kill++; + break; + case AGG_TX_STATE_SCD_QUERY_MSK: + priv->reply_agg_tx_stats.scd_query++; + break; + case AGG_TX_STATE_TEST_BAD_CRC32_MSK: + priv->reply_agg_tx_stats.bad_crc32++; + break; + case AGG_TX_STATE_RESPONSE_MSK: + priv->reply_agg_tx_stats.response++; + break; + case AGG_TX_STATE_DUMP_TX_MSK: + priv->reply_agg_tx_stats.dump_tx++; + break; + case AGG_TX_STATE_DELAY_TX_MSK: + priv->reply_agg_tx_stats.delay_tx++; + break; + default: + priv->reply_agg_tx_stats.unknown++; + break; + } +} + +static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) +{ + return le32_to_cpup((__le32 *)&tx_resp->status + + tx_resp->frame_count) & IEEE80211_MAX_SN; +} + +static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, + struct iwlagn_tx_resp *tx_resp) +{ + struct agg_tx_status *frame_status = &tx_resp->status; + int tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> + IWLAGN_TX_RES_TID_POS; + int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> + IWLAGN_TX_RES_RA_POS; + struct iwl_ht_agg *agg = &priv->tid_data[sta_id][tid].agg; + u32 status = le16_to_cpu(tx_resp->status.status); + int i; + + WARN_ON(tid == IWL_TID_NON_QOS); + + if (agg->wait_for_ba) + IWL_DEBUG_TX_REPLY(priv, + "got tx response w/o block-ack\n"); + + agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); + agg->wait_for_ba = (tx_resp->frame_count > 1); + + /* + * If the BT kill count is non-zero, we'll get this + * notification again. + */ + if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { + IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); + } + + if (tx_resp->frame_count == 1) + return; + + IWL_DEBUG_TX_REPLY(priv, "TXQ %d initial_rate 0x%x ssn %d frm_cnt %d\n", + agg->txq_id, + le32_to_cpu(tx_resp->rate_n_flags), + iwlagn_get_scd_ssn(tx_resp), tx_resp->frame_count); + + /* Construct bit-map of pending frames within Tx window */ + for (i = 0; i < tx_resp->frame_count; i++) { + u16 fstatus = le16_to_cpu(frame_status[i].status); + u8 retry_cnt = (fstatus & AGG_TX_TRY_MSK) >> AGG_TX_TRY_POS; + + if (status & AGG_TX_STATUS_MSK) + iwlagn_count_agg_tx_err_status(priv, fstatus); + + if (status & (AGG_TX_STATE_FEW_BYTES_MSK | + AGG_TX_STATE_ABORT_MSK)) + continue; + + if (status & AGG_TX_STATUS_MSK || retry_cnt > 1) + IWL_DEBUG_TX_REPLY(priv, + "%d: status %s (0x%04x), try-count (0x%01x)\n", + i, + iwl_get_agg_tx_fail_reason(fstatus), + fstatus & AGG_TX_STATUS_MSK, + retry_cnt); + } +} + +#ifdef CONFIG_IWLWIFI_DEBUG +#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x + +const char *iwl_get_agg_tx_fail_reason(u16 status) +{ + status &= AGG_TX_STATUS_MSK; + switch (status) { + case AGG_TX_STATE_TRANSMITTED: + return "SUCCESS"; + AGG_TX_STATE_FAIL(UNDERRUN_MSK); + AGG_TX_STATE_FAIL(BT_PRIO_MSK); + AGG_TX_STATE_FAIL(FEW_BYTES_MSK); + AGG_TX_STATE_FAIL(ABORT_MSK); + AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK); + AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK); + AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK); + AGG_TX_STATE_FAIL(SCD_QUERY_MSK); + AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK); + AGG_TX_STATE_FAIL(RESPONSE_MSK); + AGG_TX_STATE_FAIL(DUMP_TX_MSK); + AGG_TX_STATE_FAIL(DELAY_TX_MSK); + } + + return "UNKNOWN"; +} +#endif /* CONFIG_IWLWIFI_DEBUG */ + +static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) +{ + status &= TX_STATUS_MSK; + + switch (status) { + case TX_STATUS_POSTPONE_DELAY: + priv->reply_tx_stats.pp_delay++; + break; + case TX_STATUS_POSTPONE_FEW_BYTES: + priv->reply_tx_stats.pp_few_bytes++; + break; + case TX_STATUS_POSTPONE_BT_PRIO: + priv->reply_tx_stats.pp_bt_prio++; + break; + case TX_STATUS_POSTPONE_QUIET_PERIOD: + priv->reply_tx_stats.pp_quiet_period++; + break; + case TX_STATUS_POSTPONE_CALC_TTAK: + priv->reply_tx_stats.pp_calc_ttak++; + break; + case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: + priv->reply_tx_stats.int_crossed_retry++; + break; + case TX_STATUS_FAIL_SHORT_LIMIT: + priv->reply_tx_stats.short_limit++; + break; + case TX_STATUS_FAIL_LONG_LIMIT: + priv->reply_tx_stats.long_limit++; + break; + case TX_STATUS_FAIL_FIFO_UNDERRUN: + priv->reply_tx_stats.fifo_underrun++; + break; + case TX_STATUS_FAIL_DRAIN_FLOW: + priv->reply_tx_stats.drain_flow++; + break; + case TX_STATUS_FAIL_RFKILL_FLUSH: + priv->reply_tx_stats.rfkill_flush++; + break; + case TX_STATUS_FAIL_LIFE_EXPIRE: + priv->reply_tx_stats.life_expire++; + break; + case TX_STATUS_FAIL_DEST_PS: + priv->reply_tx_stats.dest_ps++; + break; + case TX_STATUS_FAIL_HOST_ABORTED: + priv->reply_tx_stats.host_abort++; + break; + case TX_STATUS_FAIL_BT_RETRY: + priv->reply_tx_stats.bt_retry++; + break; + case TX_STATUS_FAIL_STA_INVALID: + priv->reply_tx_stats.sta_invalid++; + break; + case TX_STATUS_FAIL_FRAG_DROPPED: + priv->reply_tx_stats.frag_drop++; + break; + case TX_STATUS_FAIL_TID_DISABLE: + priv->reply_tx_stats.tid_disable++; + break; + case TX_STATUS_FAIL_FIFO_FLUSHED: + priv->reply_tx_stats.fifo_flush++; + break; + case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL: + priv->reply_tx_stats.insuff_cf_poll++; + break; + case TX_STATUS_FAIL_PASSIVE_NO_RX: + priv->reply_tx_stats.fail_hw_drop++; + break; + case TX_STATUS_FAIL_NO_BEACON_ON_RADAR: + priv->reply_tx_stats.sta_color_mismatch++; + break; + default: + priv->reply_tx_stats.unknown++; + break; + } +} + +static void iwlagn_set_tx_status(struct iwl_priv *priv, + struct ieee80211_tx_info *info, + struct iwlagn_tx_resp *tx_resp) +{ + u16 status = le16_to_cpu(tx_resp->status.status); + + info->flags &= ~IEEE80211_TX_CTL_AMPDU; + + info->status.rates[0].count = tx_resp->failure_frame + 1; + info->flags |= iwl_tx_status_to_mac80211(status); + iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), + info); + if (!iwl_is_tx_success(status)) + iwlagn_count_tx_err_status(priv, status); +} + +static void iwl_check_abort_status(struct iwl_priv *priv, + u8 frame_count, u32 status) +{ + if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { + IWL_ERR(priv, "Tx flush command to flush out all frames\n"); + if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) + queue_work(priv->workqueue, &priv->tx_flush); + } +} + +void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u16 sequence = le16_to_cpu(pkt->hdr.sequence); + int txq_id = SEQ_TO_QUEUE(sequence); + int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence); + struct iwlagn_tx_resp *tx_resp = (void *)pkt->data; + struct ieee80211_hdr *hdr; + u32 status = le16_to_cpu(tx_resp->status.status); + u16 ssn = iwlagn_get_scd_ssn(tx_resp); + int tid; + int sta_id; + int freed; + struct ieee80211_tx_info *info; + struct sk_buff_head skbs; + struct sk_buff *skb; + struct iwl_rxon_context *ctx; + bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); + + tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> + IWLAGN_TX_RES_TID_POS; + sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> + IWLAGN_TX_RES_RA_POS; + + spin_lock_bh(&priv->sta_lock); + + if (is_agg) { + WARN_ON_ONCE(sta_id >= IWLAGN_STATION_COUNT || + tid >= IWL_MAX_TID_COUNT); + if (txq_id != priv->tid_data[sta_id][tid].agg.txq_id) + IWL_ERR(priv, "txq_id mismatch: %d %d\n", txq_id, + priv->tid_data[sta_id][tid].agg.txq_id); + iwl_rx_reply_tx_agg(priv, tx_resp); + } + + __skb_queue_head_init(&skbs); + + if (tx_resp->frame_count == 1) { + u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); + next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10); + + if (is_agg) { + /* If this is an aggregation queue, we can rely on the + * ssn since the wifi sequence number corresponds to + * the index in the TFD ring (%256). + * The seq_ctl is the sequence control of the packet + * to which this Tx response relates. But if there is a + * hole in the bitmap of the BA we received, this Tx + * response may allow to reclaim the hole and all the + * subsequent packets that were already acked. + * In that case, seq_ctl != ssn, and the next packet + * to be reclaimed will be ssn and not seq_ctl. + */ + next_reclaimed = ssn; + } + + if (tid != IWL_TID_NON_QOS) { + priv->tid_data[sta_id][tid].next_reclaimed = + next_reclaimed; + IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", + next_reclaimed); + } + + iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs); + + iwlagn_check_ratid_empty(priv, sta_id, tid); + freed = 0; + + /* process frames */ + skb_queue_walk(&skbs, skb) { + hdr = (struct ieee80211_hdr *)skb->data; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + priv->last_seq_ctl = tx_resp->seq_ctl; + + info = IEEE80211_SKB_CB(skb); + ctx = info->driver_data[0]; + iwl_trans_free_tx_cmd(priv->trans, + info->driver_data[1]); + + memset(&info->status, 0, sizeof(info->status)); + + if (status == TX_STATUS_FAIL_PASSIVE_NO_RX && + ctx->vif && + ctx->vif->type == NL80211_IFTYPE_STATION) { + /* block and stop all queues */ + priv->passive_no_rx = true; + IWL_DEBUG_TX_QUEUES(priv, + "stop all queues: passive channel\n"); + ieee80211_stop_queues(priv->hw); + + IWL_DEBUG_TX_REPLY(priv, + "TXQ %d status %s (0x%08x) " + "rate_n_flags 0x%x retries %d\n", + txq_id, + iwl_get_tx_fail_reason(status), + status, + le32_to_cpu(tx_resp->rate_n_flags), + tx_resp->failure_frame); + + IWL_DEBUG_TX_REPLY(priv, + "FrameCnt = %d, idx=%d\n", + tx_resp->frame_count, cmd_index); + } + + /* check if BAR is needed */ + if (is_agg && !iwl_is_tx_success(status)) + info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; + iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb), + tx_resp); + if (!is_agg) + iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); + + freed++; + } + + if (tid != IWL_TID_NON_QOS) { + priv->tid_data[sta_id][tid].next_reclaimed = + next_reclaimed; + IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", + next_reclaimed); + } + + if (!is_agg && freed != 1) + IWL_ERR(priv, "Q: %d, freed %d\n", txq_id, freed); + + IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id, + iwl_get_tx_fail_reason(status), status); + + IWL_DEBUG_TX_REPLY(priv, + "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d seq_ctl=0x%x\n", + le32_to_cpu(tx_resp->rate_n_flags), + tx_resp->failure_frame, + SEQ_TO_INDEX(sequence), ssn, + le16_to_cpu(tx_resp->seq_ctl)); + } + + iwl_check_abort_status(priv, tx_resp->frame_count, status); + spin_unlock_bh(&priv->sta_lock); + + while (!skb_queue_empty(&skbs)) { + skb = __skb_dequeue(&skbs); + ieee80211_tx_status(priv->hw, skb); + } +} + +/** + * iwlagn_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA + * + * Handles block-acknowledge notification from device, which reports success + * of frames sent via aggregation. + */ +void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_compressed_ba_resp *ba_resp = (void *)pkt->data; + struct iwl_ht_agg *agg; + struct sk_buff_head reclaimed_skbs; + struct sk_buff *skb; + int sta_id; + int tid; + int freed; + + /* "flow" corresponds to Tx queue */ + u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); + + /* "ssn" is start of block-ack Tx window, corresponds to index + * (in Tx queue's circular buffer) of first TFD/frame in window */ + u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); + + if (scd_flow >= priv->cfg->base_params->num_of_queues) { + IWL_ERR(priv, + "BUG_ON scd_flow is bigger than number of queues\n"); + return; + } + + sta_id = ba_resp->sta_id; + tid = ba_resp->tid; + agg = &priv->tid_data[sta_id][tid].agg; + + spin_lock_bh(&priv->sta_lock); + + if (unlikely(!agg->wait_for_ba)) { + if (unlikely(ba_resp->bitmap)) + IWL_ERR(priv, "Received BA when not expected\n"); + spin_unlock_bh(&priv->sta_lock); + return; + } + + if (unlikely(scd_flow != agg->txq_id)) { + /* + * FIXME: this is a uCode bug which need to be addressed, + * log the information and return for now. + * Since it is can possibly happen very often and in order + * not to fill the syslog, don't use IWL_ERR or IWL_WARN + */ + IWL_DEBUG_TX_QUEUES(priv, + "Bad queue mapping txq_id=%d, agg_txq[sta:%d,tid:%d]=%d\n", + scd_flow, sta_id, tid, agg->txq_id); + spin_unlock_bh(&priv->sta_lock); + return; + } + + __skb_queue_head_init(&reclaimed_skbs); + + /* Release all TFDs before the SSN, i.e. all TFDs in front of + * block-ack window (we assume that they've been successfully + * transmitted ... if not, it's too late anyway). */ + iwl_trans_reclaim(priv->trans, scd_flow, ba_resp_scd_ssn, + &reclaimed_skbs); + + IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, " + "sta_id = %d\n", + agg->wait_for_ba, + (u8 *) &ba_resp->sta_addr_lo32, + ba_resp->sta_id); + IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, " + "scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n", + ba_resp->tid, le16_to_cpu(ba_resp->seq_ctl), + (unsigned long long)le64_to_cpu(ba_resp->bitmap), + scd_flow, ba_resp_scd_ssn, ba_resp->txed, + ba_resp->txed_2_done); + + /* Mark that the expected block-ack response arrived */ + agg->wait_for_ba = false; + + /* Sanity check values reported by uCode */ + if (ba_resp->txed_2_done > ba_resp->txed) { + IWL_DEBUG_TX_REPLY(priv, + "bogus sent(%d) and ack(%d) count\n", + ba_resp->txed, ba_resp->txed_2_done); + /* + * set txed_2_done = txed, + * so it won't impact rate scale + */ + ba_resp->txed = ba_resp->txed_2_done; + } + + priv->tid_data[sta_id][tid].next_reclaimed = ba_resp_scd_ssn; + + iwlagn_check_ratid_empty(priv, sta_id, tid); + freed = 0; + + skb_queue_walk(&reclaimed_skbs, skb) { + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (ieee80211_is_data_qos(hdr->frame_control)) + freed++; + else + WARN_ON_ONCE(1); + + iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); + + memset(&info->status, 0, sizeof(info->status)); + /* Packet was transmitted successfully, failures come as single + * frames because before failing a frame the firmware transmits + * it without aggregation at least once. + */ + info->flags |= IEEE80211_TX_STAT_ACK; + + if (freed == 1) { + /* this is the first skb we deliver in this batch */ + /* put the rate scaling data there */ + info = IEEE80211_SKB_CB(skb); + memset(&info->status, 0, sizeof(info->status)); + info->flags |= IEEE80211_TX_STAT_AMPDU; + info->status.ampdu_ack_len = ba_resp->txed_2_done; + info->status.ampdu_len = ba_resp->txed; + iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, + info); + } + } + + spin_unlock_bh(&priv->sta_lock); + + while (!skb_queue_empty(&reclaimed_skbs)) { + skb = __skb_dequeue(&reclaimed_skbs); + ieee80211_tx_status(priv->hw, skb); + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c new file mode 100644 index 000000000000..931a8e4269ef --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c @@ -0,0 +1,452 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include + +#include "iwl-io.h" +#include "iwl-agn-hw.h" +#include "iwl-trans.h" +#include "iwl-fh.h" +#include "iwl-op-mode.h" + +#include "dev.h" +#include "agn.h" +#include "calib.h" + +/****************************************************************************** + * + * uCode download functions + * + ******************************************************************************/ + +static inline const struct fw_img * +iwl_get_ucode_image(struct iwl_priv *priv, enum iwl_ucode_type ucode_type) +{ + if (ucode_type >= IWL_UCODE_TYPE_MAX) + return NULL; + + return &priv->fw->img[ucode_type]; +} + +/* + * Calibration + */ +static int iwl_set_Xtal_calib(struct iwl_priv *priv) +{ + struct iwl_calib_xtal_freq_cmd cmd; + __le16 *xtal_calib = priv->nvm_data->xtal_calib; + + iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD); + cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]); + cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]); + return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); +} + +static int iwl_set_temperature_offset_calib(struct iwl_priv *priv) +{ + struct iwl_calib_temperature_offset_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); + cmd.radio_sensor_offset = priv->nvm_data->raw_temperature; + if (!(cmd.radio_sensor_offset)) + cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET; + + IWL_DEBUG_CALIB(priv, "Radio sensor offset: %d\n", + le16_to_cpu(cmd.radio_sensor_offset)); + return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); +} + +static int iwl_set_temperature_offset_calib_v2(struct iwl_priv *priv) +{ + struct iwl_calib_temperature_offset_v2_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); + cmd.radio_sensor_offset_high = priv->nvm_data->kelvin_temperature; + cmd.radio_sensor_offset_low = priv->nvm_data->raw_temperature; + if (!cmd.radio_sensor_offset_low) { + IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n"); + cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET; + cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET; + } + cmd.burntVoltageRef = priv->nvm_data->calib_voltage; + + IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n", + le16_to_cpu(cmd.radio_sensor_offset_high)); + IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n", + le16_to_cpu(cmd.radio_sensor_offset_low)); + IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n", + le16_to_cpu(cmd.burntVoltageRef)); + + return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); +} + +static int iwl_send_calib_cfg(struct iwl_priv *priv) +{ + struct iwl_calib_cfg_cmd calib_cfg_cmd; + struct iwl_host_cmd cmd = { + .id = CALIBRATION_CFG_CMD, + .len = { sizeof(struct iwl_calib_cfg_cmd), }, + .data = { &calib_cfg_cmd, }, + }; + + memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd)); + calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL; + calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL; + calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL; + calib_cfg_cmd.ucd_calib_cfg.flags = + IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK; + + return iwl_dvm_send_cmd(priv, &cmd); +} + +int iwl_init_alive_start(struct iwl_priv *priv) +{ + int ret; + + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { + /* + * Tell uCode we are ready to perform calibration + * need to perform this before any calibration + * no need to close the envlope since we are going + * to load the runtime uCode later. + */ + ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); + if (ret) + return ret; + + } + + ret = iwl_send_calib_cfg(priv); + if (ret) + return ret; + + /** + * temperature offset calibration is only needed for runtime ucode, + * so prepare the value now. + */ + if (priv->lib->need_temp_offset_calib) { + if (priv->lib->temp_offset_v2) + return iwl_set_temperature_offset_calib_v2(priv); + else + return iwl_set_temperature_offset_calib(priv); + } + + return 0; +} + +static int iwl_send_wimax_coex(struct iwl_priv *priv) +{ + struct iwl_wimax_coex_cmd coex_cmd; + + /* coexistence is disabled */ + memset(&coex_cmd, 0, sizeof(coex_cmd)); + + return iwl_dvm_send_cmd_pdu(priv, + COEX_PRIORITY_TABLE_CMD, 0, + sizeof(coex_cmd), &coex_cmd); +} + +static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { + ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_COEX_OFF << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_COEX_ON << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + 0, 0, 0, 0, 0, 0, 0 +}; + +void iwl_send_prio_tbl(struct iwl_priv *priv) +{ + struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd; + + memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl, + sizeof(iwl_bt_prio_tbl)); + if (iwl_dvm_send_cmd_pdu(priv, + REPLY_BT_COEX_PRIO_TABLE, 0, + sizeof(prio_tbl_cmd), &prio_tbl_cmd)) + IWL_ERR(priv, "failed to send BT prio tbl command\n"); +} + +int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type) +{ + struct iwl_bt_coex_prot_env_cmd env_cmd; + int ret; + + env_cmd.action = action; + env_cmd.type = type; + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_BT_COEX_PROT_ENV, 0, + sizeof(env_cmd), &env_cmd); + if (ret) + IWL_ERR(priv, "failed to send BT env command\n"); + return ret; +} + +static const u8 iwlagn_default_queue_to_tx_fifo[] = { + IWL_TX_FIFO_VO, + IWL_TX_FIFO_VI, + IWL_TX_FIFO_BE, + IWL_TX_FIFO_BK, +}; + +static const u8 iwlagn_ipan_queue_to_tx_fifo[] = { + IWL_TX_FIFO_VO, + IWL_TX_FIFO_VI, + IWL_TX_FIFO_BE, + IWL_TX_FIFO_BK, + IWL_TX_FIFO_BK_IPAN, + IWL_TX_FIFO_BE_IPAN, + IWL_TX_FIFO_VI_IPAN, + IWL_TX_FIFO_VO_IPAN, + IWL_TX_FIFO_BE_IPAN, + IWL_TX_FIFO_UNUSED, + IWL_TX_FIFO_AUX, +}; + +static int iwl_alive_notify(struct iwl_priv *priv) +{ + const u8 *queue_to_txf; + u8 n_queues; + int ret; + int i; + + iwl_trans_fw_alive(priv->trans, 0); + + if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN && + priv->nvm_data->sku_cap_ipan_enable) { + n_queues = ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo); + queue_to_txf = iwlagn_ipan_queue_to_tx_fifo; + } else { + n_queues = ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo); + queue_to_txf = iwlagn_default_queue_to_tx_fifo; + } + + for (i = 0; i < n_queues; i++) + if (queue_to_txf[i] != IWL_TX_FIFO_UNUSED) + iwl_trans_ac_txq_enable(priv->trans, i, + queue_to_txf[i], 0); + + priv->passive_no_rx = false; + priv->transport_queue_stop = 0; + + ret = iwl_send_wimax_coex(priv); + if (ret) + return ret; + + if (!priv->lib->no_xtal_calib) { + ret = iwl_set_Xtal_calib(priv); + if (ret) + return ret; + } + + return iwl_send_calib_results(priv); +} + +struct iwl_alive_data { + bool valid; + u8 subtype; +}; + +static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_priv *priv = + container_of(notif_wait, struct iwl_priv, notif_wait); + struct iwl_alive_data *alive_data = data; + struct iwl_alive_resp *palive; + + palive = (void *)pkt->data; + + IWL_DEBUG_FW(priv, "Alive ucode status 0x%08X revision " + "0x%01X 0x%01X\n", + palive->is_valid, palive->ver_type, + palive->ver_subtype); + + priv->device_pointers.error_event_table = + le32_to_cpu(palive->error_event_table_ptr); + priv->device_pointers.log_event_table = + le32_to_cpu(palive->log_event_table_ptr); + + alive_data->subtype = palive->ver_subtype; + alive_data->valid = palive->is_valid == UCODE_VALID_OK; + + return true; +} + +#define UCODE_ALIVE_TIMEOUT HZ +#define UCODE_CALIB_TIMEOUT (2*HZ) + +int iwl_load_ucode_wait_alive(struct iwl_priv *priv, + enum iwl_ucode_type ucode_type) +{ + struct iwl_notification_wait alive_wait; + struct iwl_alive_data alive_data; + const struct fw_img *fw; + int ret; + enum iwl_ucode_type old_type; + static const u16 alive_cmd[] = { REPLY_ALIVE }; + + fw = iwl_get_ucode_image(priv, ucode_type); + if (WARN_ON(!fw)) + return -EINVAL; + + old_type = priv->cur_ucode; + priv->cur_ucode = ucode_type; + priv->ucode_loaded = false; + + iwl_init_notification_wait(&priv->notif_wait, &alive_wait, + alive_cmd, ARRAY_SIZE(alive_cmd), + iwl_alive_fn, &alive_data); + + ret = iwl_trans_start_fw(priv->trans, fw, false); + if (ret) { + priv->cur_ucode = old_type; + iwl_remove_notification(&priv->notif_wait, &alive_wait); + return ret; + } + + /* + * Some things may run in the background now, but we + * just wait for the ALIVE notification here. + */ + ret = iwl_wait_notification(&priv->notif_wait, &alive_wait, + UCODE_ALIVE_TIMEOUT); + if (ret) { + priv->cur_ucode = old_type; + return ret; + } + + if (!alive_data.valid) { + IWL_ERR(priv, "Loaded ucode is not valid!\n"); + priv->cur_ucode = old_type; + return -EIO; + } + + priv->ucode_loaded = true; + + if (ucode_type != IWL_UCODE_WOWLAN) { + /* delay a bit to give rfkill time to run */ + msleep(5); + } + + ret = iwl_alive_notify(priv); + if (ret) { + IWL_WARN(priv, + "Could not complete ALIVE transition: %d\n", ret); + priv->cur_ucode = old_type; + return ret; + } + + return 0; +} + +static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_priv *priv = data; + struct iwl_calib_hdr *hdr; + + if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) { + WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION); + return true; + } + + hdr = (struct iwl_calib_hdr *)pkt->data; + + 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); + + return false; +} + +int iwl_run_init_ucode(struct iwl_priv *priv) +{ + struct iwl_notification_wait calib_wait; + static const u16 calib_complete[] = { + CALIBRATION_RES_NOTIFICATION, + CALIBRATION_COMPLETE_NOTIFICATION + }; + int ret; + + lockdep_assert_held(&priv->mutex); + + /* No init ucode required? Curious, but maybe ok */ + if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len) + return 0; + + iwl_init_notification_wait(&priv->notif_wait, &calib_wait, + calib_complete, ARRAY_SIZE(calib_complete), + iwlagn_wait_calib, priv); + + /* Will also start the device */ + ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT); + if (ret) + goto error; + + ret = iwl_init_alive_start(priv); + if (ret) + goto error; + + /* + * Some things may run in the background now, but we + * just wait for the calibration complete notification. + */ + ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, + UCODE_CALIB_TIMEOUT); + + goto out; + + error: + iwl_remove_notification(&priv->notif_wait, &calib_wait); + out: + /* Whatever happened, stop the device */ + iwl_trans_stop_device(priv->trans); + priv->ucode_loaded = false; + + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-1000.c b/drivers/net/wireless/intel/iwlwifi/iwl-1000.c new file mode 100644 index 000000000000..06f6cc08f451 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-1000.c @@ -0,0 +1,140 @@ +/****************************************************************************** + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include "iwl-config.h" +#include "iwl-csr.h" +#include "iwl-agn-hw.h" + +/* Highest firmware API version supported */ +#define IWL1000_UCODE_API_MAX 5 +#define IWL100_UCODE_API_MAX 5 + +/* Oldest version we won't warn about */ +#define IWL1000_UCODE_API_OK 5 +#define IWL100_UCODE_API_OK 5 + +/* Lowest firmware API version supported */ +#define IWL1000_UCODE_API_MIN 1 +#define IWL100_UCODE_API_MIN 5 + +/* EEPROM version */ +#define EEPROM_1000_TX_POWER_VERSION (4) +#define EEPROM_1000_EEPROM_VERSION (0x15C) + +#define IWL1000_FW_PRE "iwlwifi-1000-" +#define IWL1000_MODULE_FIRMWARE(api) IWL1000_FW_PRE __stringify(api) ".ucode" + +#define IWL100_FW_PRE "iwlwifi-100-" +#define IWL100_MODULE_FIRMWARE(api) IWL100_FW_PRE __stringify(api) ".ucode" + + +static const struct iwl_base_params iwl1000_base_params = { + .num_of_queues = IWLAGN_NUM_QUEUES, + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .max_ll_items = OTP_MAX_LL_ITEMS_1000, + .shadow_ram_support = false, + .led_compensation = 51, + .wd_timeout = IWL_WATCHDOG_DISABLED, + .max_event_log_size = 128, + .scd_chain_ext_wa = true, +}; + +static const struct iwl_ht_params iwl1000_ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(IEEE80211_BAND_2GHZ), +}; + +static const struct iwl_eeprom_params iwl1000_eeprom_params = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_REG_BAND_24_HT40_CHANNELS, + EEPROM_REGULATORY_BAND_NO_HT40, + } +}; + +#define IWL_DEVICE_1000 \ + .fw_name_pre = IWL1000_FW_PRE, \ + .ucode_api_max = IWL1000_UCODE_API_MAX, \ + .ucode_api_ok = IWL1000_UCODE_API_OK, \ + .ucode_api_min = IWL1000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_1000, \ + .max_inst_size = IWLAGN_RTC_INST_SIZE, \ + .max_data_size = IWLAGN_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_1000_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ + .base_params = &iwl1000_base_params, \ + .eeprom_params = &iwl1000_eeprom_params, \ + .led_mode = IWL_LED_BLINK, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl1000_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN", + IWL_DEVICE_1000, + .ht_params = &iwl1000_ht_params, +}; + +const struct iwl_cfg iwl1000_bg_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 1000 BG", + IWL_DEVICE_1000, +}; + +#define IWL_DEVICE_100 \ + .fw_name_pre = IWL100_FW_PRE, \ + .ucode_api_max = IWL100_UCODE_API_MAX, \ + .ucode_api_ok = IWL100_UCODE_API_OK, \ + .ucode_api_min = IWL100_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_100, \ + .max_inst_size = IWLAGN_RTC_INST_SIZE, \ + .max_data_size = IWLAGN_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_1000_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ + .base_params = &iwl1000_base_params, \ + .eeprom_params = &iwl1000_eeprom_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .rx_with_siso_diversity = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl100_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 100 BGN", + IWL_DEVICE_100, + .ht_params = &iwl1000_ht_params, +}; + +const struct iwl_cfg iwl100_bg_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 100 BG", + IWL_DEVICE_100, +}; + +MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL100_MODULE_FIRMWARE(IWL100_UCODE_API_OK)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-2000.c b/drivers/net/wireless/intel/iwlwifi/iwl-2000.c new file mode 100644 index 000000000000..890b95f497d6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-2000.c @@ -0,0 +1,216 @@ +/****************************************************************************** + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include "iwl-config.h" +#include "iwl-agn-hw.h" +#include "dvm/commands.h" /* needed for BT for now */ + +/* Highest firmware API version supported */ +#define IWL2030_UCODE_API_MAX 6 +#define IWL2000_UCODE_API_MAX 6 +#define IWL105_UCODE_API_MAX 6 +#define IWL135_UCODE_API_MAX 6 + +/* Oldest version we won't warn about */ +#define IWL2030_UCODE_API_OK 6 +#define IWL2000_UCODE_API_OK 6 +#define IWL105_UCODE_API_OK 6 +#define IWL135_UCODE_API_OK 6 + +/* Lowest firmware API version supported */ +#define IWL2030_UCODE_API_MIN 5 +#define IWL2000_UCODE_API_MIN 5 +#define IWL105_UCODE_API_MIN 5 +#define IWL135_UCODE_API_MIN 5 + +/* EEPROM version */ +#define EEPROM_2000_TX_POWER_VERSION (6) +#define EEPROM_2000_EEPROM_VERSION (0x805) + + +#define IWL2030_FW_PRE "iwlwifi-2030-" +#define IWL2030_MODULE_FIRMWARE(api) IWL2030_FW_PRE __stringify(api) ".ucode" + +#define IWL2000_FW_PRE "iwlwifi-2000-" +#define IWL2000_MODULE_FIRMWARE(api) IWL2000_FW_PRE __stringify(api) ".ucode" + +#define IWL105_FW_PRE "iwlwifi-105-" +#define IWL105_MODULE_FIRMWARE(api) IWL105_FW_PRE __stringify(api) ".ucode" + +#define IWL135_FW_PRE "iwlwifi-135-" +#define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE __stringify(api) ".ucode" + +static const struct iwl_base_params iwl2000_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .max_ll_items = OTP_MAX_LL_ITEMS_2x00, + .shadow_ram_support = true, + .led_compensation = 51, + .wd_timeout = IWL_DEF_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .scd_chain_ext_wa = true, +}; + + +static const struct iwl_base_params iwl2030_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .max_ll_items = OTP_MAX_LL_ITEMS_2x00, + .shadow_ram_support = true, + .led_compensation = 57, + .wd_timeout = IWL_LONG_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .scd_chain_ext_wa = true, +}; + +static const struct iwl_ht_params iwl2000_ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(IEEE80211_BAND_2GHZ), +}; + +static const struct iwl_eeprom_params iwl20x0_eeprom_params = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_6000_REG_BAND_24_HT40_CHANNELS, + EEPROM_REGULATORY_BAND_NO_HT40, + }, + .enhanced_txpower = true, +}; + +#define IWL_DEVICE_2000 \ + .fw_name_pre = IWL2000_FW_PRE, \ + .ucode_api_max = IWL2000_UCODE_API_MAX, \ + .ucode_api_ok = IWL2000_UCODE_API_OK, \ + .ucode_api_min = IWL2000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_2000, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ + .base_params = &iwl2000_base_params, \ + .eeprom_params = &iwl20x0_eeprom_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + + +const struct iwl_cfg iwl2000_2bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN", + IWL_DEVICE_2000, + .ht_params = &iwl2000_ht_params, +}; + +const struct iwl_cfg iwl2000_2bgn_d_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 2200D BGN", + IWL_DEVICE_2000, + .ht_params = &iwl2000_ht_params, +}; + +#define IWL_DEVICE_2030 \ + .fw_name_pre = IWL2030_FW_PRE, \ + .ucode_api_max = IWL2030_UCODE_API_MAX, \ + .ucode_api_ok = IWL2030_UCODE_API_OK, \ + .ucode_api_min = IWL2030_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_2030, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ + .base_params = &iwl2030_base_params, \ + .eeprom_params = &iwl20x0_eeprom_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl2030_2bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", + IWL_DEVICE_2030, + .ht_params = &iwl2000_ht_params, +}; + +#define IWL_DEVICE_105 \ + .fw_name_pre = IWL105_FW_PRE, \ + .ucode_api_max = IWL105_UCODE_API_MAX, \ + .ucode_api_ok = IWL105_UCODE_API_OK, \ + .ucode_api_min = IWL105_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_105, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ + .base_params = &iwl2000_base_params, \ + .eeprom_params = &iwl20x0_eeprom_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .rx_with_siso_diversity = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl105_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 105 BGN", + IWL_DEVICE_105, + .ht_params = &iwl2000_ht_params, +}; + +const struct iwl_cfg iwl105_bgn_d_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 105D BGN", + IWL_DEVICE_105, + .ht_params = &iwl2000_ht_params, +}; + +#define IWL_DEVICE_135 \ + .fw_name_pre = IWL135_FW_PRE, \ + .ucode_api_max = IWL135_UCODE_API_MAX, \ + .ucode_api_ok = IWL135_UCODE_API_OK, \ + .ucode_api_min = IWL135_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_135, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ + .base_params = &iwl2030_base_params, \ + .eeprom_params = &iwl20x0_eeprom_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .rx_with_siso_diversity = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl135_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 135 BGN", + IWL_DEVICE_135, + .ht_params = &iwl2000_ht_params, +}; + +MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_OK)); +MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_OK)); +MODULE_FIRMWARE(IWL135_MODULE_FIRMWARE(IWL135_UCODE_API_OK)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-5000.c b/drivers/net/wireless/intel/iwlwifi/iwl-5000.c new file mode 100644 index 000000000000..724194e23414 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-5000.c @@ -0,0 +1,178 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include "iwl-config.h" +#include "iwl-agn-hw.h" +#include "iwl-csr.h" + +/* Highest firmware API version supported */ +#define IWL5000_UCODE_API_MAX 5 +#define IWL5150_UCODE_API_MAX 2 + +/* Oldest version we won't warn about */ +#define IWL5000_UCODE_API_OK 5 +#define IWL5150_UCODE_API_OK 2 + +/* Lowest firmware API version supported */ +#define IWL5000_UCODE_API_MIN 1 +#define IWL5150_UCODE_API_MIN 1 + +/* EEPROM versions */ +#define EEPROM_5000_TX_POWER_VERSION (4) +#define EEPROM_5000_EEPROM_VERSION (0x11A) +#define EEPROM_5050_TX_POWER_VERSION (4) +#define EEPROM_5050_EEPROM_VERSION (0x21E) + +#define IWL5000_FW_PRE "iwlwifi-5000-" +#define IWL5000_MODULE_FIRMWARE(api) IWL5000_FW_PRE __stringify(api) ".ucode" + +#define IWL5150_FW_PRE "iwlwifi-5150-" +#define IWL5150_MODULE_FIRMWARE(api) IWL5150_FW_PRE __stringify(api) ".ucode" + +static const struct iwl_base_params iwl5000_base_params = { + .eeprom_size = IWLAGN_EEPROM_IMG_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .led_compensation = 51, + .wd_timeout = IWL_WATCHDOG_DISABLED, + .max_event_log_size = 512, + .scd_chain_ext_wa = true, +}; + +static const struct iwl_ht_params iwl5000_ht_params = { + .ht_greenfield_support = true, + .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), +}; + +static const struct iwl_eeprom_params iwl5000_eeprom_params = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_REG_BAND_24_HT40_CHANNELS, + EEPROM_REG_BAND_52_HT40_CHANNELS + }, +}; + +#define IWL_DEVICE_5000 \ + .fw_name_pre = IWL5000_FW_PRE, \ + .ucode_api_max = IWL5000_UCODE_API_MAX, \ + .ucode_api_ok = IWL5000_UCODE_API_OK, \ + .ucode_api_min = IWL5000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_5000, \ + .max_inst_size = IWLAGN_RTC_INST_SIZE, \ + .max_data_size = IWLAGN_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_5000_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_5000_TX_POWER_VERSION, \ + .base_params = &iwl5000_base_params, \ + .eeprom_params = &iwl5000_eeprom_params, \ + .led_mode = IWL_LED_BLINK, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl5300_agn_cfg = { + .name = "Intel(R) Ultimate N WiFi Link 5300 AGN", + IWL_DEVICE_5000, + /* at least EEPROM 0x11A has wrong info */ + .valid_tx_ant = ANT_ABC, /* .cfg overwrite */ + .valid_rx_ant = ANT_ABC, /* .cfg overwrite */ + .ht_params = &iwl5000_ht_params, +}; + +const struct iwl_cfg iwl5100_bgn_cfg = { + .name = "Intel(R) WiFi Link 5100 BGN", + IWL_DEVICE_5000, + .valid_tx_ant = ANT_B, /* .cfg overwrite */ + .valid_rx_ant = ANT_AB, /* .cfg overwrite */ + .ht_params = &iwl5000_ht_params, +}; + +const struct iwl_cfg iwl5100_abg_cfg = { + .name = "Intel(R) WiFi Link 5100 ABG", + IWL_DEVICE_5000, + .valid_tx_ant = ANT_B, /* .cfg overwrite */ + .valid_rx_ant = ANT_AB, /* .cfg overwrite */ +}; + +const struct iwl_cfg iwl5100_agn_cfg = { + .name = "Intel(R) WiFi Link 5100 AGN", + IWL_DEVICE_5000, + .valid_tx_ant = ANT_B, /* .cfg overwrite */ + .valid_rx_ant = ANT_AB, /* .cfg overwrite */ + .ht_params = &iwl5000_ht_params, +}; + +const struct iwl_cfg iwl5350_agn_cfg = { + .name = "Intel(R) WiMAX/WiFi Link 5350 AGN", + .fw_name_pre = IWL5000_FW_PRE, + .ucode_api_max = IWL5000_UCODE_API_MAX, + .ucode_api_ok = IWL5000_UCODE_API_OK, + .ucode_api_min = IWL5000_UCODE_API_MIN, + .device_family = IWL_DEVICE_FAMILY_5000, + .max_inst_size = IWLAGN_RTC_INST_SIZE, + .max_data_size = IWLAGN_RTC_DATA_SIZE, + .nvm_ver = EEPROM_5050_EEPROM_VERSION, + .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, + .base_params = &iwl5000_base_params, + .eeprom_params = &iwl5000_eeprom_params, + .ht_params = &iwl5000_ht_params, + .led_mode = IWL_LED_BLINK, + .internal_wimax_coex = true, +}; + +#define IWL_DEVICE_5150 \ + .fw_name_pre = IWL5150_FW_PRE, \ + .ucode_api_max = IWL5150_UCODE_API_MAX, \ + .ucode_api_ok = IWL5150_UCODE_API_OK, \ + .ucode_api_min = IWL5150_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_5150, \ + .max_inst_size = IWLAGN_RTC_INST_SIZE, \ + .max_data_size = IWLAGN_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_5050_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ + .base_params = &iwl5000_base_params, \ + .eeprom_params = &iwl5000_eeprom_params, \ + .led_mode = IWL_LED_BLINK, \ + .internal_wimax_coex = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl5150_agn_cfg = { + .name = "Intel(R) WiMAX/WiFi Link 5150 AGN", + IWL_DEVICE_5150, + .ht_params = &iwl5000_ht_params, + +}; + +const struct iwl_cfg iwl5150_abg_cfg = { + .name = "Intel(R) WiMAX/WiFi Link 5150 ABG", + IWL_DEVICE_5150, +}; + +MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_OK)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-6000.c b/drivers/net/wireless/intel/iwlwifi/iwl-6000.c new file mode 100644 index 000000000000..21b2630763dc --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-6000.c @@ -0,0 +1,389 @@ +/****************************************************************************** + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include "iwl-config.h" +#include "iwl-agn-hw.h" +#include "dvm/commands.h" /* needed for BT for now */ + +/* Highest firmware API version supported */ +#define IWL6000_UCODE_API_MAX 6 +#define IWL6050_UCODE_API_MAX 5 +#define IWL6000G2_UCODE_API_MAX 6 +#define IWL6035_UCODE_API_MAX 6 + +/* Oldest version we won't warn about */ +#define IWL6000_UCODE_API_OK 4 +#define IWL6000G2_UCODE_API_OK 5 +#define IWL6050_UCODE_API_OK 5 +#define IWL6000G2B_UCODE_API_OK 6 +#define IWL6035_UCODE_API_OK 6 + +/* Lowest firmware API version supported */ +#define IWL6000_UCODE_API_MIN 4 +#define IWL6050_UCODE_API_MIN 4 +#define IWL6000G2_UCODE_API_MIN 5 +#define IWL6035_UCODE_API_MIN 6 + +/* EEPROM versions */ +#define EEPROM_6000_TX_POWER_VERSION (4) +#define EEPROM_6000_EEPROM_VERSION (0x423) +#define EEPROM_6050_TX_POWER_VERSION (4) +#define EEPROM_6050_EEPROM_VERSION (0x532) +#define EEPROM_6150_TX_POWER_VERSION (6) +#define EEPROM_6150_EEPROM_VERSION (0x553) +#define EEPROM_6005_TX_POWER_VERSION (6) +#define EEPROM_6005_EEPROM_VERSION (0x709) +#define EEPROM_6030_TX_POWER_VERSION (6) +#define EEPROM_6030_EEPROM_VERSION (0x709) +#define EEPROM_6035_TX_POWER_VERSION (6) +#define EEPROM_6035_EEPROM_VERSION (0x753) + +#define IWL6000_FW_PRE "iwlwifi-6000-" +#define IWL6000_MODULE_FIRMWARE(api) IWL6000_FW_PRE __stringify(api) ".ucode" + +#define IWL6050_FW_PRE "iwlwifi-6050-" +#define IWL6050_MODULE_FIRMWARE(api) IWL6050_FW_PRE __stringify(api) ".ucode" + +#define IWL6005_FW_PRE "iwlwifi-6000g2a-" +#define IWL6005_MODULE_FIRMWARE(api) IWL6005_FW_PRE __stringify(api) ".ucode" + +#define IWL6030_FW_PRE "iwlwifi-6000g2b-" +#define IWL6030_MODULE_FIRMWARE(api) IWL6030_FW_PRE __stringify(api) ".ucode" + +static const struct iwl_base_params iwl6000_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .led_compensation = 51, + .wd_timeout = IWL_DEF_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .scd_chain_ext_wa = true, +}; + +static const struct iwl_base_params iwl6050_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .max_ll_items = OTP_MAX_LL_ITEMS_6x50, + .shadow_ram_support = true, + .led_compensation = 51, + .wd_timeout = IWL_DEF_WD_TIMEOUT, + .max_event_log_size = 1024, + .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .scd_chain_ext_wa = true, +}; + +static const struct iwl_base_params iwl6000_g2_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .led_compensation = 57, + .wd_timeout = IWL_LONG_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .scd_chain_ext_wa = true, +}; + +static const struct iwl_ht_params iwl6000_ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), +}; + +static const struct iwl_eeprom_params iwl6000_eeprom_params = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_6000_REG_BAND_24_HT40_CHANNELS, + EEPROM_REG_BAND_52_HT40_CHANNELS + }, + .enhanced_txpower = true, +}; + +#define IWL_DEVICE_6005 \ + .fw_name_pre = IWL6005_FW_PRE, \ + .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ + .ucode_api_ok = IWL6000G2_UCODE_API_OK, \ + .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_6005, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_6005_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ + .base_params = &iwl6000_g2_base_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl6005_2agn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN", + IWL_DEVICE_6005, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6005_2abg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6205 ABG", + IWL_DEVICE_6005, +}; + +const struct iwl_cfg iwl6005_2bg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6205 BG", + IWL_DEVICE_6005, +}; + +const struct iwl_cfg iwl6005_2agn_sff_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6205S AGN", + IWL_DEVICE_6005, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6005_2agn_d_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6205D AGN", + IWL_DEVICE_6005, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6005_2agn_mow1_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6206 AGN", + IWL_DEVICE_6005, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6005_2agn_mow2_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6207 AGN", + IWL_DEVICE_6005, + .ht_params = &iwl6000_ht_params, +}; + +#define IWL_DEVICE_6030 \ + .fw_name_pre = IWL6030_FW_PRE, \ + .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ + .ucode_api_ok = IWL6000G2B_UCODE_API_OK, \ + .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_6030, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ + .base_params = &iwl6000_g2_base_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl6030_2agn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", + IWL_DEVICE_6030, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6030_2abg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6230 ABG", + IWL_DEVICE_6030, +}; + +const struct iwl_cfg iwl6030_2bgn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6230 BGN", + IWL_DEVICE_6030, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6030_2bg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6230 BG", + IWL_DEVICE_6030, +}; + +#define IWL_DEVICE_6035 \ + .fw_name_pre = IWL6030_FW_PRE, \ + .ucode_api_max = IWL6035_UCODE_API_MAX, \ + .ucode_api_ok = IWL6035_UCODE_API_OK, \ + .ucode_api_min = IWL6035_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_6030, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ + .base_params = &iwl6000_g2_base_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl6035_2agn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", + IWL_DEVICE_6035, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6035_2agn_sff_cfg = { + .name = "Intel(R) Centrino(R) Ultimate-N 6235 AGN", + IWL_DEVICE_6035, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl1030_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN", + IWL_DEVICE_6030, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl1030_bg_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 1030 BG", + IWL_DEVICE_6030, +}; + +const struct iwl_cfg iwl130_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 130 BGN", + IWL_DEVICE_6030, + .ht_params = &iwl6000_ht_params, + .rx_with_siso_diversity = true, +}; + +const struct iwl_cfg iwl130_bg_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 130 BG", + IWL_DEVICE_6030, + .rx_with_siso_diversity = true, +}; + +/* + * "i": Internal configuration, use internal Power Amplifier + */ +#define IWL_DEVICE_6000i \ + .fw_name_pre = IWL6000_FW_PRE, \ + .ucode_api_max = IWL6000_UCODE_API_MAX, \ + .ucode_api_ok = IWL6000_UCODE_API_OK, \ + .ucode_api_min = IWL6000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_6000i, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .valid_tx_ant = ANT_BC, /* .cfg overwrite */ \ + .valid_rx_ant = ANT_BC, /* .cfg overwrite */ \ + .nvm_ver = EEPROM_6000_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, \ + .base_params = &iwl6000_base_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ + .led_mode = IWL_LED_BLINK, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl6000i_2agn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN", + IWL_DEVICE_6000i, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6000i_2abg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG", + IWL_DEVICE_6000i, +}; + +const struct iwl_cfg iwl6000i_2bg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6200 BG", + IWL_DEVICE_6000i, +}; + +#define IWL_DEVICE_6050 \ + .fw_name_pre = IWL6050_FW_PRE, \ + .ucode_api_max = IWL6050_UCODE_API_MAX, \ + .ucode_api_min = IWL6050_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_6050, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .valid_tx_ant = ANT_AB, /* .cfg overwrite */ \ + .valid_rx_ant = ANT_AB, /* .cfg overwrite */ \ + .nvm_ver = EEPROM_6050_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_6050_TX_POWER_VERSION, \ + .base_params = &iwl6050_base_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ + .led_mode = IWL_LED_BLINK, \ + .internal_wimax_coex = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl6050_2agn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN", + IWL_DEVICE_6050, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6050_2abg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG", + IWL_DEVICE_6050, +}; + +#define IWL_DEVICE_6150 \ + .fw_name_pre = IWL6050_FW_PRE, \ + .ucode_api_max = IWL6050_UCODE_API_MAX, \ + .ucode_api_min = IWL6050_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_6150, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .nvm_ver = EEPROM_6150_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_6150_TX_POWER_VERSION, \ + .base_params = &iwl6050_base_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ + .led_mode = IWL_LED_BLINK, \ + .internal_wimax_coex = true, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + +const struct iwl_cfg iwl6150_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN", + IWL_DEVICE_6150, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6150_bg_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG", + IWL_DEVICE_6150, +}; + +const struct iwl_cfg iwl6000_3agn_cfg = { + .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN", + .fw_name_pre = IWL6000_FW_PRE, + .ucode_api_max = IWL6000_UCODE_API_MAX, + .ucode_api_ok = IWL6000_UCODE_API_OK, + .ucode_api_min = IWL6000_UCODE_API_MIN, + .device_family = IWL_DEVICE_FAMILY_6000, + .max_inst_size = IWL60_RTC_INST_SIZE, + .max_data_size = IWL60_RTC_DATA_SIZE, + .nvm_ver = EEPROM_6000_EEPROM_VERSION, + .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, + .base_params = &iwl6000_base_params, + .eeprom_params = &iwl6000_eeprom_params, + .ht_params = &iwl6000_ht_params, + .led_mode = IWL_LED_BLINK, +}; + +MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_OK)); +MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_OK)); +MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2B_UCODE_API_OK)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c new file mode 100644 index 000000000000..1a73c7a1da77 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c @@ -0,0 +1,346 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include "iwl-config.h" +#include "iwl-agn-hw.h" + +/* Highest firmware API version supported */ +#define IWL7260_UCODE_API_MAX 17 + +/* Oldest version we won't warn about */ +#define IWL7260_UCODE_API_OK 13 + +/* Lowest firmware API version supported */ +#define IWL7260_UCODE_API_MIN 13 + +/* NVM versions */ +#define IWL7260_NVM_VERSION 0x0a1d +#define IWL7260_TX_POWER_VERSION 0xffff /* meaningless */ +#define IWL3160_NVM_VERSION 0x709 +#define IWL3160_TX_POWER_VERSION 0xffff /* meaningless */ +#define IWL3165_NVM_VERSION 0x709 +#define IWL3165_TX_POWER_VERSION 0xffff /* meaningless */ +#define IWL7265_NVM_VERSION 0x0a1d +#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */ +#define IWL7265D_NVM_VERSION 0x0c11 +#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */ + +/* DCCM offsets and lengths */ +#define IWL7000_DCCM_OFFSET 0x800000 +#define IWL7260_DCCM_LEN 0x14000 +#define IWL3160_DCCM_LEN 0x10000 +#define IWL7265_DCCM_LEN 0x17A00 + +#define IWL7260_FW_PRE "iwlwifi-7260-" +#define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode" + +#define IWL3160_FW_PRE "iwlwifi-3160-" +#define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode" + +#define IWL7265_FW_PRE "iwlwifi-7265-" +#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode" + +#define IWL7265D_FW_PRE "iwlwifi-7265D-" +#define IWL7265D_MODULE_FIRMWARE(api) IWL7265D_FW_PRE __stringify(api) ".ucode" + +#define NVM_HW_SECTION_NUM_FAMILY_7000 0 + +static const struct iwl_base_params iwl7000_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000, + .num_of_queues = 31, + .pll_cfg_val = 0, + .shadow_ram_support = true, + .led_compensation = 57, + .wd_timeout = IWL_LONG_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = true, + .pcie_l1_allowed = true, + .apmg_wake_up_wa = true, +}; + +static const struct iwl_tt_params iwl7000_high_temp_tt_params = { + .ct_kill_entry = 118, + .ct_kill_exit = 96, + .ct_kill_duration = 5, + .dynamic_smps_entry = 114, + .dynamic_smps_exit = 110, + .tx_protection_entry = 114, + .tx_protection_exit = 108, + .tx_backoff = { + {.temperature = 112, .backoff = 300}, + {.temperature = 113, .backoff = 800}, + {.temperature = 114, .backoff = 1500}, + {.temperature = 115, .backoff = 3000}, + {.temperature = 116, .backoff = 5000}, + {.temperature = 117, .backoff = 10000}, + }, + .support_ct_kill = true, + .support_dynamic_smps = true, + .support_tx_protection = true, + .support_tx_backoff = true, +}; + +static const struct iwl_ht_params iwl7000_ht_params = { + .stbc = true, + .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), +}; + +#define IWL_DEVICE_7000 \ + .ucode_api_max = IWL7260_UCODE_API_MAX, \ + .ucode_api_ok = IWL7260_UCODE_API_OK, \ + .ucode_api_min = IWL7260_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_7000, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .base_params = &iwl7000_base_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000, \ + .non_shared_ant = ANT_A, \ + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .dccm_offset = IWL7000_DCCM_OFFSET + +const struct iwl_cfg iwl7260_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, + .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, +}; + +const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { + .name = "Intel(R) Dual Band Wireless AC 7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, + .high_temp = true, + .host_interrupt_operation_mode = true, + .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, + .thermal_params = &iwl7000_high_temp_tt_params, +}; + +const struct iwl_cfg iwl7260_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, + .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, +}; + +const struct iwl_cfg iwl7260_n_cfg = { + .name = "Intel(R) Wireless N 7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, + .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, +}; + +const struct iwl_cfg iwl3160_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 3160", + .fw_name_pre = IWL3160_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3160_NVM_VERSION, + .nvm_calib_ver = IWL3160_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, + .dccm_len = IWL3160_DCCM_LEN, +}; + +const struct iwl_cfg iwl3160_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 3160", + .fw_name_pre = IWL3160_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3160_NVM_VERSION, + .nvm_calib_ver = IWL3160_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, + .dccm_len = IWL3160_DCCM_LEN, +}; + +const struct iwl_cfg iwl3160_n_cfg = { + .name = "Intel(R) Wireless N 3160", + .fw_name_pre = IWL3160_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3160_NVM_VERSION, + .nvm_calib_ver = IWL3160_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, + .dccm_len = IWL3160_DCCM_LEN, +}; + +static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = { + {.pwr = 1600, .backoff = 0}, + {.pwr = 1300, .backoff = 467}, + {.pwr = 900, .backoff = 1900}, + {.pwr = 800, .backoff = 2630}, + {.pwr = 700, .backoff = 3720}, + {.pwr = 600, .backoff = 5550}, + {.pwr = 500, .backoff = 9350}, + {0}, +}; + +static const struct iwl_ht_params iwl7265_ht_params = { + .stbc = true, + .ldpc = true, + .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), +}; + +const struct iwl_cfg iwl3165_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 3165", + .fw_name_pre = IWL7265D_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3165_NVM_VERSION, + .nvm_calib_ver = IWL3165_TX_POWER_VERSION, + .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, +}; + +const struct iwl_cfg iwl7265_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 7265", + .fw_name_pre = IWL7265_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7265_ht_params, + .nvm_ver = IWL7265_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, + .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, +}; + +const struct iwl_cfg iwl7265_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 7265", + .fw_name_pre = IWL7265_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7265_ht_params, + .nvm_ver = IWL7265_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, + .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, +}; + +const struct iwl_cfg iwl7265_n_cfg = { + .name = "Intel(R) Wireless N 7265", + .fw_name_pre = IWL7265_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7265_ht_params, + .nvm_ver = IWL7265_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, + .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, +}; + +const struct iwl_cfg iwl7265d_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 7265", + .fw_name_pre = IWL7265D_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7265_ht_params, + .nvm_ver = IWL7265D_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, + .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, +}; + +const struct iwl_cfg iwl7265d_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 7265", + .fw_name_pre = IWL7265D_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7265_ht_params, + .nvm_ver = IWL7265D_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, + .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, +}; + +const struct iwl_cfg iwl7265d_n_cfg = { + .name = "Intel(R) Wireless N 7265", + .fw_name_pre = IWL7265D_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7265_ht_params, + .nvm_ver = IWL7265D_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, + .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, +}; + +MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); +MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); +MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); +MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c new file mode 100644 index 000000000000..0116e5a4c393 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c @@ -0,0 +1,229 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include "iwl-config.h" +#include "iwl-agn-hw.h" + +/* Highest firmware API version supported */ +#define IWL8000_UCODE_API_MAX 17 + +/* Oldest version we won't warn about */ +#define IWL8000_UCODE_API_OK 13 + +/* Lowest firmware API version supported */ +#define IWL8000_UCODE_API_MIN 13 + +/* NVM versions */ +#define IWL8000_NVM_VERSION 0x0a1d +#define IWL8000_TX_POWER_VERSION 0xffff /* meaningless */ + +/* Memory offsets and lengths */ +#define IWL8260_DCCM_OFFSET 0x800000 +#define IWL8260_DCCM_LEN 0x18000 +#define IWL8260_DCCM2_OFFSET 0x880000 +#define IWL8260_DCCM2_LEN 0x8000 +#define IWL8260_SMEM_OFFSET 0x400000 +#define IWL8260_SMEM_LEN 0x68000 + +#define IWL8000_FW_PRE "iwlwifi-8000" +#define IWL8000_MODULE_FIRMWARE(api) \ + IWL8000_FW_PRE "-" __stringify(api) ".ucode" + +#define NVM_HW_SECTION_NUM_FAMILY_8000 10 +#define DEFAULT_NVM_FILE_FAMILY_8000B "nvmData-8000B" +#define DEFAULT_NVM_FILE_FAMILY_8000C "nvmData-8000C" + +/* Max SDIO RX/TX aggregation sizes of the ADDBA request/response */ +#define MAX_RX_AGG_SIZE_8260_SDIO 21 +#define MAX_TX_AGG_SIZE_8260_SDIO 40 + +/* Max A-MPDU exponent for HT and VHT */ +#define MAX_HT_AMPDU_EXPONENT_8260_SDIO IEEE80211_HT_MAX_AMPDU_32K +#define MAX_VHT_AMPDU_EXPONENT_8260_SDIO IEEE80211_VHT_MAX_AMPDU_32K + +static const struct iwl_base_params iwl8000_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000, + .num_of_queues = 31, + .pll_cfg_val = 0, + .shadow_ram_support = true, + .led_compensation = 57, + .wd_timeout = IWL_LONG_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = true, + .pcie_l1_allowed = true, +}; + +static const struct iwl_ht_params iwl8000_ht_params = { + .stbc = true, + .ldpc = true, + .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), +}; + +static const struct iwl_tt_params iwl8000_tt_params = { + .ct_kill_entry = 115, + .ct_kill_exit = 93, + .ct_kill_duration = 5, + .dynamic_smps_entry = 111, + .dynamic_smps_exit = 107, + .tx_protection_entry = 112, + .tx_protection_exit = 105, + .tx_backoff = { + {.temperature = 110, .backoff = 200}, + {.temperature = 111, .backoff = 600}, + {.temperature = 112, .backoff = 1200}, + {.temperature = 113, .backoff = 2000}, + {.temperature = 114, .backoff = 4000}, + }, + .support_ct_kill = true, + .support_dynamic_smps = true, + .support_tx_protection = true, + .support_tx_backoff = true, +}; + +#define IWL_DEVICE_8000 \ + .ucode_api_max = IWL8000_UCODE_API_MAX, \ + .ucode_api_ok = IWL8000_UCODE_API_OK, \ + .ucode_api_min = IWL8000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_8000, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .base_params = &iwl8000_base_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000, \ + .d0i3 = true, \ + .features = NETIF_F_RXCSUM, \ + .non_shared_ant = ANT_A, \ + .dccm_offset = IWL8260_DCCM_OFFSET, \ + .dccm_len = IWL8260_DCCM_LEN, \ + .dccm2_offset = IWL8260_DCCM2_OFFSET, \ + .dccm2_len = IWL8260_DCCM2_LEN, \ + .smem_offset = IWL8260_SMEM_OFFSET, \ + .smem_len = IWL8260_SMEM_LEN, \ + .default_nvm_file_B_step = DEFAULT_NVM_FILE_FAMILY_8000B, \ + .default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C, \ + .thermal_params = &iwl8000_tt_params, \ + .apmg_not_supported = true + +const struct iwl_cfg iwl8260_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 8260", + .fw_name_pre = IWL8000_FW_PRE, + IWL_DEVICE_8000, + .ht_params = &iwl8000_ht_params, + .nvm_ver = IWL8000_NVM_VERSION, + .nvm_calib_ver = IWL8000_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl8260_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 8260", + .fw_name_pre = IWL8000_FW_PRE, + IWL_DEVICE_8000, + .ht_params = &iwl8000_ht_params, + .nvm_ver = IWL8000_NVM_VERSION, + .nvm_calib_ver = IWL8000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, +}; + +const struct iwl_cfg iwl4165_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 4165", + .fw_name_pre = IWL8000_FW_PRE, + IWL_DEVICE_8000, + .ht_params = &iwl8000_ht_params, + .nvm_ver = IWL8000_NVM_VERSION, + .nvm_calib_ver = IWL8000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, +}; + +const struct iwl_cfg iwl8260_2ac_sdio_cfg = { + .name = "Intel(R) Dual Band Wireless-AC 8260", + .fw_name_pre = IWL8000_FW_PRE, + IWL_DEVICE_8000, + .ht_params = &iwl8000_ht_params, + .nvm_ver = IWL8000_NVM_VERSION, + .nvm_calib_ver = IWL8000_TX_POWER_VERSION, + .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO, + .max_tx_agg_size = MAX_TX_AGG_SIZE_8260_SDIO, + .disable_dummy_notification = true, + .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO, + .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO, +}; + +const struct iwl_cfg iwl4165_2ac_sdio_cfg = { + .name = "Intel(R) Dual Band Wireless-AC 4165", + .fw_name_pre = IWL8000_FW_PRE, + IWL_DEVICE_8000, + .ht_params = &iwl8000_ht_params, + .nvm_ver = IWL8000_NVM_VERSION, + .nvm_calib_ver = IWL8000_TX_POWER_VERSION, + .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO, + .max_tx_agg_size = MAX_TX_AGG_SIZE_8260_SDIO, + .bt_shared_single_ant = true, + .disable_dummy_notification = true, + .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO, + .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO, +}; + +MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h new file mode 100644 index 000000000000..04a483d38659 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h @@ -0,0 +1,117 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +/* + * Please use this file (iwl-agn-hw.h) only for hardware-related definitions. + */ + +#ifndef __iwl_agn_hw_h__ +#define __iwl_agn_hw_h__ + +#define IWLAGN_RTC_INST_LOWER_BOUND (0x000000) +#define IWLAGN_RTC_INST_UPPER_BOUND (0x020000) + +#define IWLAGN_RTC_DATA_LOWER_BOUND (0x800000) +#define IWLAGN_RTC_DATA_UPPER_BOUND (0x80C000) + +#define IWLAGN_RTC_INST_SIZE (IWLAGN_RTC_INST_UPPER_BOUND - \ + IWLAGN_RTC_INST_LOWER_BOUND) +#define IWLAGN_RTC_DATA_SIZE (IWLAGN_RTC_DATA_UPPER_BOUND - \ + IWLAGN_RTC_DATA_LOWER_BOUND) + +#define IWL60_RTC_INST_LOWER_BOUND (0x000000) +#define IWL60_RTC_INST_UPPER_BOUND (0x040000) +#define IWL60_RTC_DATA_LOWER_BOUND (0x800000) +#define IWL60_RTC_DATA_UPPER_BOUND (0x814000) +#define IWL60_RTC_INST_SIZE \ + (IWL60_RTC_INST_UPPER_BOUND - IWL60_RTC_INST_LOWER_BOUND) +#define IWL60_RTC_DATA_SIZE \ + (IWL60_RTC_DATA_UPPER_BOUND - IWL60_RTC_DATA_LOWER_BOUND) + +/* RSSI to dBm */ +#define IWLAGN_RSSI_OFFSET 44 + +#define IWLAGN_DEFAULT_TX_RETRY 15 +#define IWLAGN_MGMT_DFAULT_RETRY_LIMIT 3 +#define IWLAGN_RTS_DFAULT_RETRY_LIMIT 60 +#define IWLAGN_BAR_DFAULT_RETRY_LIMIT 60 +#define IWLAGN_LOW_RETRY_LIMIT 7 + +/* Limit range of txpower output target to be between these values */ +#define IWLAGN_TX_POWER_TARGET_POWER_MIN (0) /* 0 dBm: 1 milliwatt */ +#define IWLAGN_TX_POWER_TARGET_POWER_MAX (16) /* 16 dBm */ + +/* EEPROM */ +#define IWLAGN_EEPROM_IMG_SIZE 2048 + +/* high blocks contain PAPD data */ +#define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */ +#define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */ +#define OTP_MAX_LL_ITEMS_1000 (3) /* OTP blocks for 1000 */ +#define OTP_MAX_LL_ITEMS_6x00 (4) /* OTP blocks for 6x00 */ +#define OTP_MAX_LL_ITEMS_6x50 (7) /* OTP blocks for 6x50 */ +#define OTP_MAX_LL_ITEMS_2x00 (4) /* OTP blocks for 2x00 */ + + +#define IWLAGN_NUM_QUEUES 20 + +#endif /* __iwl_agn_hw_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h new file mode 100644 index 000000000000..910970858f98 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -0,0 +1,437 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __IWL_CONFIG_H__ +#define __IWL_CONFIG_H__ + +#include +#include + + +enum iwl_device_family { + IWL_DEVICE_FAMILY_UNDEFINED, + IWL_DEVICE_FAMILY_1000, + IWL_DEVICE_FAMILY_100, + IWL_DEVICE_FAMILY_2000, + IWL_DEVICE_FAMILY_2030, + IWL_DEVICE_FAMILY_105, + IWL_DEVICE_FAMILY_135, + IWL_DEVICE_FAMILY_5000, + IWL_DEVICE_FAMILY_5150, + IWL_DEVICE_FAMILY_6000, + IWL_DEVICE_FAMILY_6000i, + IWL_DEVICE_FAMILY_6005, + IWL_DEVICE_FAMILY_6030, + IWL_DEVICE_FAMILY_6050, + IWL_DEVICE_FAMILY_6150, + IWL_DEVICE_FAMILY_7000, + IWL_DEVICE_FAMILY_8000, +}; + +static inline bool iwl_has_secure_boot(u32 hw_rev, + enum iwl_device_family family) +{ + /* return 1 only for family 8000 B0 */ + if ((family == IWL_DEVICE_FAMILY_8000) && (hw_rev & 0xC)) + return true; + + return false; +} + +/* + * LED mode + * IWL_LED_DEFAULT: use device default + * IWL_LED_RF_STATE: turn LED on/off based on RF state + * LED ON = RF ON + * LED OFF = RF OFF + * IWL_LED_BLINK: adjust led blink rate based on blink table + * IWL_LED_DISABLE: led disabled + */ +enum iwl_led_mode { + IWL_LED_DEFAULT, + IWL_LED_RF_STATE, + IWL_LED_BLINK, + IWL_LED_DISABLE, +}; + +/* + * This is the threshold value of plcp error rate per 100mSecs. It is + * used to set and check for the validity of plcp_delta. + */ +#define IWL_MAX_PLCP_ERR_THRESHOLD_MIN 1 +#define IWL_MAX_PLCP_ERR_THRESHOLD_DEF 50 +#define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF 100 +#define IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF 200 +#define IWL_MAX_PLCP_ERR_THRESHOLD_MAX 255 +#define IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE 0 + +/* TX queue watchdog timeouts in mSecs */ +#define IWL_WATCHDOG_DISABLED 0 +#define IWL_DEF_WD_TIMEOUT 2500 +#define IWL_LONG_WD_TIMEOUT 10000 +#define IWL_MAX_WD_TIMEOUT 120000 + +#define IWL_DEFAULT_MAX_TX_POWER 22 + +/* Antenna presence definitions */ +#define ANT_NONE 0x0 +#define ANT_A BIT(0) +#define ANT_B BIT(1) +#define ANT_C BIT(2) +#define ANT_AB (ANT_A | ANT_B) +#define ANT_AC (ANT_A | ANT_C) +#define ANT_BC (ANT_B | ANT_C) +#define ANT_ABC (ANT_A | ANT_B | ANT_C) + +static inline u8 num_of_ant(u8 mask) +{ + return !!((mask) & ANT_A) + + !!((mask) & ANT_B) + + !!((mask) & ANT_C); +} + +/* + * @max_ll_items: max number of OTP blocks + * @shadow_ram_support: shadow support for OTP memory + * @led_compensation: compensate on the led on/off time per HW according + * to the deviation to achieve the desired led frequency. + * The detail algorithm is described in iwl-led.c + * @wd_timeout: TX queues watchdog timeout + * @max_event_log_size: size of event log buffer size for ucode event logging + * @shadow_reg_enable: HW shadow register support + * @apmg_wake_up_wa: should the MAC access REQ be asserted when a command + * is in flight. This is due to a HW bug in 7260, 3160 and 7265. + * @scd_chain_ext_wa: should the chain extension feature in SCD be disabled. + */ +struct iwl_base_params { + int eeprom_size; + int num_of_queues; /* def: HW dependent */ + /* for iwl_pcie_apm_init() */ + u32 pll_cfg_val; + + const u16 max_ll_items; + const bool shadow_ram_support; + u16 led_compensation; + unsigned int wd_timeout; + u32 max_event_log_size; + const bool shadow_reg_enable; + const bool pcie_l1_allowed; + const bool apmg_wake_up_wa; + const bool scd_chain_ext_wa; +}; + +/* + * @stbc: support Tx STBC and 1*SS Rx STBC + * @ldpc: support Tx/Rx with LDPC + * @use_rts_for_aggregation: use rts/cts protection for HT traffic + * @ht40_bands: bitmap of bands (using %IEEE80211_BAND_*) that support HT40 + */ +struct iwl_ht_params { + enum ieee80211_smps_mode smps_mode; + const bool ht_greenfield_support; /* if used set to true */ + const bool stbc; + const bool ldpc; + bool use_rts_for_aggregation; + u8 ht40_bands; +}; + +/* + * Tx-backoff threshold + * @temperature: The threshold in Celsius + * @backoff: The tx-backoff in uSec + */ +struct iwl_tt_tx_backoff { + s32 temperature; + u32 backoff; +}; + +#define TT_TX_BACKOFF_SIZE 6 + +/** + * struct iwl_tt_params - thermal throttling parameters + * @ct_kill_entry: CT Kill entry threshold + * @ct_kill_exit: CT Kill exit threshold + * @ct_kill_duration: The time intervals (in uSec) in which the driver needs + * to checks whether to exit CT Kill. + * @dynamic_smps_entry: Dynamic SMPS entry threshold + * @dynamic_smps_exit: Dynamic SMPS exit threshold + * @tx_protection_entry: TX protection entry threshold + * @tx_protection_exit: TX protection exit threshold + * @tx_backoff: Array of thresholds for tx-backoff , in ascending order. + * @support_ct_kill: Support CT Kill? + * @support_dynamic_smps: Support dynamic SMPS? + * @support_tx_protection: Support tx protection? + * @support_tx_backoff: Support tx-backoff? + */ +struct iwl_tt_params { + u32 ct_kill_entry; + u32 ct_kill_exit; + u32 ct_kill_duration; + u32 dynamic_smps_entry; + u32 dynamic_smps_exit; + u32 tx_protection_entry; + u32 tx_protection_exit; + struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE]; + bool support_ct_kill; + bool support_dynamic_smps; + bool support_tx_protection; + bool support_tx_backoff; +}; + +/* + * information on how to parse the EEPROM + */ +#define EEPROM_REG_BAND_1_CHANNELS 0x08 +#define EEPROM_REG_BAND_2_CHANNELS 0x26 +#define EEPROM_REG_BAND_3_CHANNELS 0x42 +#define EEPROM_REG_BAND_4_CHANNELS 0x5C +#define EEPROM_REG_BAND_5_CHANNELS 0x74 +#define EEPROM_REG_BAND_24_HT40_CHANNELS 0x82 +#define EEPROM_REG_BAND_52_HT40_CHANNELS 0x92 +#define EEPROM_6000_REG_BAND_24_HT40_CHANNELS 0x80 +#define EEPROM_REGULATORY_BAND_NO_HT40 0 + +/* lower blocks contain EEPROM image and calibration data */ +#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */ +#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */ +#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */ + +struct iwl_eeprom_params { + const u8 regulatory_bands[7]; + bool enhanced_txpower; +}; + +/* Tx-backoff power threshold + * @pwr: The power limit in mw + * @backoff: The tx-backoff in uSec + */ +struct iwl_pwr_tx_backoff { + u32 pwr; + u32 backoff; +}; + +/** + * struct iwl_cfg + * @name: Official name of the device + * @fw_name_pre: Firmware filename prefix. The api version and extension + * (.ucode) will be added to filename before loading from disk. The + * filename is constructed as fw_name_pre.ucode. + * @ucode_api_max: Highest version of uCode API supported by driver. + * @ucode_api_ok: oldest version of the uCode API that is OK to load + * without a warning, for use in transitions + * @ucode_api_min: Lowest version of uCode API supported by driver. + * @max_inst_size: The maximal length of the fw inst section + * @max_data_size: The maximal length of the fw data section + * @valid_tx_ant: valid transmit antenna + * @valid_rx_ant: valid receive antenna + * @non_shared_ant: the antenna that is for WiFi only + * @nvm_ver: NVM version + * @nvm_calib_ver: NVM calibration version + * @lib: pointer to the lib ops + * @base_params: pointer to basic parameters + * @ht_params: point to ht parameters + * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) + * @rx_with_siso_diversity: 1x1 device with rx antenna diversity + * @internal_wimax_coex: internal wifi/wimax combo device + * @high_temp: Is this NIC is designated to be in high temperature. + * @host_interrupt_operation_mode: device needs host interrupt operation + * mode set + * @d0i3: device uses d0i3 instead of d3 + * @nvm_hw_section_num: the ID of the HW NVM section + * @features: hw features, any combination of feature_whitelist + * @pwr_tx_backoffs: translation table between power limits and backoffs + * @max_rx_agg_size: max RX aggregation size of the ADDBA request/response + * @max_tx_agg_size: max TX aggregation size of the ADDBA request/response + * @max_ht_ampdu_factor: the exponent of the max length of A-MPDU that the + * station can receive in HT + * @max_vht_ampdu_exponent: the exponent of the max length of A-MPDU that the + * station can receive in VHT + * @dccm_offset: offset from which DCCM begins + * @dccm_len: length of DCCM (including runtime stack CCM) + * @dccm2_offset: offset from which the second DCCM begins + * @dccm2_len: length of the second DCCM + * @smem_offset: offset from which the SMEM begins + * @smem_len: the length of SMEM + * + * We enable the driver to be backward compatible wrt. hardware features. + * API differences in uCode shouldn't be handled here but through TLVs + * and/or the uCode API version instead. + */ +struct iwl_cfg { + /* params specific to an individual device within a device family */ + const char *name; + const char *fw_name_pre; + const unsigned int ucode_api_max; + const unsigned int ucode_api_ok; + const unsigned int ucode_api_min; + const enum iwl_device_family device_family; + const u32 max_data_size; + const u32 max_inst_size; + u8 valid_tx_ant; + u8 valid_rx_ant; + u8 non_shared_ant; + bool bt_shared_single_ant; + u16 nvm_ver; + u16 nvm_calib_ver; + /* params not likely to change within a device family */ + const struct iwl_base_params *base_params; + /* params likely to change within a device family */ + const struct iwl_ht_params *ht_params; + const struct iwl_eeprom_params *eeprom_params; + enum iwl_led_mode led_mode; + const bool rx_with_siso_diversity; + const bool internal_wimax_coex; + const bool host_interrupt_operation_mode; + bool high_temp; + bool d0i3; + u8 nvm_hw_section_num; + bool lp_xtal_workaround; + const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; + bool no_power_up_nic_in_init; + const char *default_nvm_file_B_step; + const char *default_nvm_file_C_step; + netdev_features_t features; + unsigned int max_rx_agg_size; + bool disable_dummy_notification; + unsigned int max_tx_agg_size; + unsigned int max_ht_ampdu_exponent; + unsigned int max_vht_ampdu_exponent; + const u32 dccm_offset; + const u32 dccm_len; + const u32 dccm2_offset; + const u32 dccm2_len; + const u32 smem_offset; + const u32 smem_len; + const struct iwl_tt_params *thermal_params; + bool apmg_not_supported; +}; + +/* + * This list declares the config structures for all devices. + */ +#if IS_ENABLED(CONFIG_IWLDVM) +extern const struct iwl_cfg iwl5300_agn_cfg; +extern const struct iwl_cfg iwl5100_agn_cfg; +extern const struct iwl_cfg iwl5350_agn_cfg; +extern const struct iwl_cfg iwl5100_bgn_cfg; +extern const struct iwl_cfg iwl5100_abg_cfg; +extern const struct iwl_cfg iwl5150_agn_cfg; +extern const struct iwl_cfg iwl5150_abg_cfg; +extern const struct iwl_cfg iwl6005_2agn_cfg; +extern const struct iwl_cfg iwl6005_2abg_cfg; +extern const struct iwl_cfg iwl6005_2bg_cfg; +extern const struct iwl_cfg iwl6005_2agn_sff_cfg; +extern const struct iwl_cfg iwl6005_2agn_d_cfg; +extern const struct iwl_cfg iwl6005_2agn_mow1_cfg; +extern const struct iwl_cfg iwl6005_2agn_mow2_cfg; +extern const struct iwl_cfg iwl1030_bgn_cfg; +extern const struct iwl_cfg iwl1030_bg_cfg; +extern const struct iwl_cfg iwl6030_2agn_cfg; +extern const struct iwl_cfg iwl6030_2abg_cfg; +extern const struct iwl_cfg iwl6030_2bgn_cfg; +extern const struct iwl_cfg iwl6030_2bg_cfg; +extern const struct iwl_cfg iwl6000i_2agn_cfg; +extern const struct iwl_cfg iwl6000i_2abg_cfg; +extern const struct iwl_cfg iwl6000i_2bg_cfg; +extern const struct iwl_cfg iwl6000_3agn_cfg; +extern const struct iwl_cfg iwl6050_2agn_cfg; +extern const struct iwl_cfg iwl6050_2abg_cfg; +extern const struct iwl_cfg iwl6150_bgn_cfg; +extern const struct iwl_cfg iwl6150_bg_cfg; +extern const struct iwl_cfg iwl1000_bgn_cfg; +extern const struct iwl_cfg iwl1000_bg_cfg; +extern const struct iwl_cfg iwl100_bgn_cfg; +extern const struct iwl_cfg iwl100_bg_cfg; +extern const struct iwl_cfg iwl130_bgn_cfg; +extern const struct iwl_cfg iwl130_bg_cfg; +extern const struct iwl_cfg iwl2000_2bgn_cfg; +extern const struct iwl_cfg iwl2000_2bgn_d_cfg; +extern const struct iwl_cfg iwl2030_2bgn_cfg; +extern const struct iwl_cfg iwl6035_2agn_cfg; +extern const struct iwl_cfg iwl6035_2agn_sff_cfg; +extern const struct iwl_cfg iwl105_bgn_cfg; +extern const struct iwl_cfg iwl105_bgn_d_cfg; +extern const struct iwl_cfg iwl135_bgn_cfg; +#endif /* CONFIG_IWLDVM */ +#if IS_ENABLED(CONFIG_IWLMVM) +extern const struct iwl_cfg iwl7260_2ac_cfg; +extern const struct iwl_cfg iwl7260_2ac_cfg_high_temp; +extern const struct iwl_cfg iwl7260_2n_cfg; +extern const struct iwl_cfg iwl7260_n_cfg; +extern const struct iwl_cfg iwl3160_2ac_cfg; +extern const struct iwl_cfg iwl3160_2n_cfg; +extern const struct iwl_cfg iwl3160_n_cfg; +extern const struct iwl_cfg iwl3165_2ac_cfg; +extern const struct iwl_cfg iwl7265_2ac_cfg; +extern const struct iwl_cfg iwl7265_2n_cfg; +extern const struct iwl_cfg iwl7265_n_cfg; +extern const struct iwl_cfg iwl7265d_2ac_cfg; +extern const struct iwl_cfg iwl7265d_2n_cfg; +extern const struct iwl_cfg iwl7265d_n_cfg; +extern const struct iwl_cfg iwl8260_2n_cfg; +extern const struct iwl_cfg iwl8260_2ac_cfg; +extern const struct iwl_cfg iwl4165_2ac_cfg; +extern const struct iwl_cfg iwl8260_2ac_sdio_cfg; +extern const struct iwl_cfg iwl4165_2ac_sdio_cfg; +#endif /* CONFIG_IWLMVM */ + +#endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h new file mode 100644 index 000000000000..543abeaffcf0 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -0,0 +1,552 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_csr_h__ +#define __iwl_csr_h__ +/* + * CSR (control and status registers) + * + * CSR registers are mapped directly into PCI bus space, and are accessible + * whenever platform supplies power to device, even when device is in + * low power states due to driver-invoked device resets + * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes. + * + * Use iwl_write32() and iwl_read32() family to access these registers; + * these provide simple PCI bus access, without waking up the MAC. + * Do not use iwl_write_direct32() family for these registers; + * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ. + * The MAC (uCode processor, etc.) does not need to be powered up for accessing + * the CSR registers. + * + * NOTE: Device does need to be awake in order to read this memory + * via CSR_EEPROM and CSR_OTP registers + */ +#define CSR_BASE (0x000) + +#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */ +#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */ +#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */ +#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */ +#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/ +#define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */ +#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/ +#define CSR_GP_CNTRL (CSR_BASE+0x024) + +/* 2nd byte of CSR_INT_COALESCING, not accessible via iwl_write32()! */ +#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005) + +/* + * Hardware revision info + * Bit fields: + * 31-16: Reserved + * 15-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions + * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D + * 1-0: "Dash" (-) value, as in A-1, etc. + */ +#define CSR_HW_REV (CSR_BASE+0x028) + +/* + * EEPROM and OTP (one-time-programmable) memory reads + * + * NOTE: Device must be awake, initialized via apm_ops.init(), + * in order to read. + */ +#define CSR_EEPROM_REG (CSR_BASE+0x02c) +#define CSR_EEPROM_GP (CSR_BASE+0x030) +#define CSR_OTP_GP_REG (CSR_BASE+0x034) + +#define CSR_GIO_REG (CSR_BASE+0x03C) +#define CSR_GP_UCODE_REG (CSR_BASE+0x048) +#define CSR_GP_DRIVER_REG (CSR_BASE+0x050) + +/* + * UCODE-DRIVER GP (general purpose) mailbox registers. + * SET/CLR registers set/clear bit(s) if "1" is written. + */ +#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054) +#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058) +#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c) +#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060) + +#define CSR_MBOX_SET_REG (CSR_BASE + 0x88) + +#define CSR_LED_REG (CSR_BASE+0x094) +#define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0) +#define CSR_MAC_SHADOW_REG_CTRL (CSR_BASE+0x0A8) /* 6000 and up */ + + +/* GIO Chicken Bits (PCI Express bus link power management) */ +#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100) + +/* Analog phase-lock-loop configuration */ +#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c) + +/* + * CSR HW resources monitor registers + */ +#define CSR_MONITOR_CFG_REG (CSR_BASE+0x214) +#define CSR_MONITOR_STATUS_REG (CSR_BASE+0x228) +#define CSR_MONITOR_XTAL_RESOURCES (0x00000010) + +/* + * CSR Hardware Revision Workaround Register. Indicates hardware rev; + * "step" determines CCK backoff for txpower calculation. Used for 4965 only. + * See also CSR_HW_REV register. + * Bit fields: + * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step + * 1-0: "Dash" (-) value, as in C-1, etc. + */ +#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C) + +#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240) +#define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250) + +/* Bits for CSR_HW_IF_CONFIG_REG */ +#define CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH (0x00000003) +#define CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP (0x0000000C) +#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x000000C0) +#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100) +#define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200) +#define CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE (0x00000C00) +#define CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH (0x00003000) +#define CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP (0x0000C000) + +#define CSR_HW_IF_CONFIG_REG_POS_MAC_DASH (0) +#define CSR_HW_IF_CONFIG_REG_POS_MAC_STEP (2) +#define CSR_HW_IF_CONFIG_REG_POS_BOARD_VER (6) +#define CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE (10) +#define CSR_HW_IF_CONFIG_REG_POS_PHY_DASH (12) +#define CSR_HW_IF_CONFIG_REG_POS_PHY_STEP (14) + +#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) +#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) +#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ +#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ +#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ +#define CSR_HW_IF_CONFIG_REG_ENABLE_PME (0x10000000) +#define CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */ + +#define CSR_MBOX_SET_REG_OS_ALIVE BIT(5) + +#define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/ +#define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/ + +/* interrupt flags in INTA, set by uCode or hardware (e.g. dma), + * acknowledged (reset) by host writing "1" to flagged bits. */ +#define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */ +#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */ +#define CSR_INT_BIT_RX_PERIODIC (1 << 28) /* Rx periodic */ +#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */ +#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */ +#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */ +#define CSR_INT_BIT_PAGING (1 << 24) /* SDIO PAGING */ +#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */ +#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */ +#define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses */ +#define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */ +#define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */ + +#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ + CSR_INT_BIT_HW_ERR | \ + CSR_INT_BIT_FH_TX | \ + CSR_INT_BIT_SW_ERR | \ + CSR_INT_BIT_PAGING | \ + CSR_INT_BIT_RF_KILL | \ + CSR_INT_BIT_SW_RX | \ + CSR_INT_BIT_WAKEUP | \ + CSR_INT_BIT_ALIVE | \ + CSR_INT_BIT_RX_PERIODIC) + +/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ +#define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */ +#define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */ +#define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */ +#define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */ +#define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */ +#define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */ + +#define CSR_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ + CSR_FH_INT_BIT_RX_CHNL1 | \ + CSR_FH_INT_BIT_RX_CHNL0) + +#define CSR_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \ + CSR_FH_INT_BIT_TX_CHNL0) + +/* GPIO */ +#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200) +#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000) +#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC (0x00000200) + +/* RESET */ +#define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001) +#define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002) +#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080) +#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100) +#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200) +#define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000) + +/* + * GP (general purpose) CONTROL REGISTER + * Bit fields: + * 27: HW_RF_KILL_SW + * Indicates state of (platform's) hardware RF-Kill switch + * 26-24: POWER_SAVE_TYPE + * Indicates current power-saving mode: + * 000 -- No power saving + * 001 -- MAC power-down + * 010 -- PHY (radio) power-down + * 011 -- Error + * 10: XTAL ON request + * 9-6: SYS_CONFIG + * Indicates current system configuration, reflecting pins on chip + * as forced high/low by device circuit board. + * 4: GOING_TO_SLEEP + * Indicates MAC is entering a power-saving sleep power-down. + * Not a good time to access device-internal resources. + * 3: MAC_ACCESS_REQ + * Host sets this to request and maintain MAC wakeup, to allow host + * access to device-internal resources. Host must wait for + * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR + * device registers. + * 2: INIT_DONE + * Host sets this to put device into fully operational D0 power mode. + * Host resets this after SW_RESET to put device into low power mode. + * 0: MAC_CLOCK_READY + * Indicates MAC (ucode processor, etc.) is powered up and can run. + * Internal resources are accessible. + * NOTE: This does not indicate that the processor is actually running. + * NOTE: This does not indicate that device has completed + * init or post-power-down restore of internal SRAM memory. + * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that + * SRAM is restored and uCode is in normal operation mode. + * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and + * do not need to save/restore it. + * NOTE: After device reset, this bit remains "0" until host sets + * INIT_DONE + */ +#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001) +#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004) +#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008) +#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010) +#define CSR_GP_CNTRL_REG_FLAG_XTAL_ON (0x00000400) + +#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001) + +#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000) +#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000) +#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000) + + +/* HW REV */ +#define CSR_HW_REV_DASH(_val) (((_val) & 0x0000003) >> 0) +#define CSR_HW_REV_STEP(_val) (((_val) & 0x000000C) >> 2) + + +/** + * hw_rev values + */ +enum { + SILICON_A_STEP = 0, + SILICON_B_STEP, + SILICON_C_STEP, +}; + + +#define CSR_HW_REV_TYPE_MSK (0x000FFF0) +#define CSR_HW_REV_TYPE_5300 (0x0000020) +#define CSR_HW_REV_TYPE_5350 (0x0000030) +#define CSR_HW_REV_TYPE_5100 (0x0000050) +#define CSR_HW_REV_TYPE_5150 (0x0000040) +#define CSR_HW_REV_TYPE_1000 (0x0000060) +#define CSR_HW_REV_TYPE_6x00 (0x0000070) +#define CSR_HW_REV_TYPE_6x50 (0x0000080) +#define CSR_HW_REV_TYPE_6150 (0x0000084) +#define CSR_HW_REV_TYPE_6x05 (0x00000B0) +#define CSR_HW_REV_TYPE_6x30 CSR_HW_REV_TYPE_6x05 +#define CSR_HW_REV_TYPE_6x35 CSR_HW_REV_TYPE_6x05 +#define CSR_HW_REV_TYPE_2x30 (0x00000C0) +#define CSR_HW_REV_TYPE_2x00 (0x0000100) +#define CSR_HW_REV_TYPE_105 (0x0000110) +#define CSR_HW_REV_TYPE_135 (0x0000120) +#define CSR_HW_REV_TYPE_7265D (0x0000210) +#define CSR_HW_REV_TYPE_NONE (0x00001F0) + +/* EEPROM REG */ +#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) +#define CSR_EEPROM_REG_BIT_CMD (0x00000002) +#define CSR_EEPROM_REG_MSK_ADDR (0x0000FFFC) +#define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000) + +/* EEPROM GP */ +#define CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */ +#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) +#define CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP (0x00000000) +#define CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP (0x00000001) +#define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002) +#define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004) + +/* One-time-programmable memory general purpose reg */ +#define CSR_OTP_GP_REG_DEVICE_SELECT (0x00010000) /* 0 - EEPROM, 1 - OTP */ +#define CSR_OTP_GP_REG_OTP_ACCESS_MODE (0x00020000) /* 0 - absolute, 1 - relative */ +#define CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK (0x00100000) /* bit 20 */ +#define CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK (0x00200000) /* bit 21 */ + +/* GP REG */ +#define CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */ +#define CSR_GP_REG_NO_POWER_SAVE (0x00000000) +#define CSR_GP_REG_MAC_POWER_SAVE (0x01000000) +#define CSR_GP_REG_PHY_POWER_SAVE (0x02000000) +#define CSR_GP_REG_POWER_SAVE_ERROR (0x03000000) + + +/* CSR GIO */ +#define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002) + +/* + * UCODE-DRIVER GP (general purpose) mailbox register 1 + * Host driver and uCode write and/or read this register to communicate with + * each other. + * Bit fields: + * 4: UCODE_DISABLE + * Host sets this to request permanent halt of uCode, same as + * sending CARD_STATE command with "halt" bit set. + * 3: CT_KILL_EXIT + * Host sets this to request exit from CT_KILL state, i.e. host thinks + * device temperature is low enough to continue normal operation. + * 2: CMD_BLOCKED + * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL) + * to release uCode to clear all Tx and command queues, enter + * unassociated mode, and power down. + * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit. + * 1: SW_BIT_RFKILL + * Host sets this when issuing CARD_STATE command to request + * device sleep. + * 0: MAC_SLEEP + * uCode sets this when preparing a power-saving power-down. + * uCode resets this when power-up is complete and SRAM is sane. + * NOTE: device saves internal SRAM data to host when powering down, + * and must restore this data after powering back up. + * MAC_SLEEP is the best indication that restore is complete. + * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and + * do not need to save/restore it. + */ +#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001) +#define CSR_UCODE_SW_BIT_RFKILL (0x00000002) +#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) +#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008) +#define CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE (0x00000020) + +/* GP Driver */ +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_MSK (0x00000003) +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_3x3_HYB (0x00000000) +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB (0x00000001) +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA (0x00000002) +#define CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6 (0x00000004) +#define CSR_GP_DRIVER_REG_BIT_6050_1x2 (0x00000008) + +#define CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER (0x00000080) + +/* GIO Chicken Bits (PCI Express bus link power management) */ +#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) +#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) + +/* LED */ +#define CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF) +#define CSR_LED_REG_TURN_ON (0x60) +#define CSR_LED_REG_TURN_OFF (0x20) + +/* ANA_PLL */ +#define CSR50_ANA_PLL_CFG_VAL (0x00880300) + +/* HPET MEM debug */ +#define CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000) + +/* DRAM INT TABLE */ +#define CSR_DRAM_INT_TBL_ENABLE (1 << 31) +#define CSR_DRAM_INIT_TBL_WRITE_POINTER (1 << 28) +#define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) + +/* + * SHR target access (Shared block memory space) + * + * Shared internal registers can be accessed directly from PCI bus through SHR + * arbiter without need for the MAC HW to be powered up. This is possible due to + * indirect read/write via HEEP_CTRL_WRD_PCIEX_CTRL (0xEC) and + * HEEP_CTRL_WRD_PCIEX_DATA (0xF4) registers. + * + * Use iwl_write32()/iwl_read32() family to access these registers. The MAC HW + * need not be powered up so no "grab inc access" is required. + */ + +/* + * Registers for accessing shared registers (e.g. SHR_APMG_GP1, + * SHR_APMG_XTAL_CFG). For example, to read from SHR_APMG_GP1 register (0x1DC), + * first, write to the control register: + * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register) + * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 2 (read access) + * second, read from the data register HEEP_CTRL_WRD_PCIEX_DATA[31:0]. + * + * To write the register, first, write to the data register + * HEEP_CTRL_WRD_PCIEX_DATA[31:0] and then: + * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register) + * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 3 (write access) + */ +#define HEEP_CTRL_WRD_PCIEX_CTRL_REG (CSR_BASE+0x0ec) +#define HEEP_CTRL_WRD_PCIEX_DATA_REG (CSR_BASE+0x0f4) + +/* + * HBUS (Host-side Bus) + * + * HBUS registers are mapped directly into PCI bus space, but are used + * to indirectly access device's internal memory or registers that + * may be powered-down. + * + * Use iwl_write_direct32()/iwl_read_direct32() family for these registers; + * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ + * to make sure the MAC (uCode processor, etc.) is powered up for accessing + * internal resources. + * + * Do not use iwl_write32()/iwl_read32() family to access these registers; + * these provide only simple PCI bus access, without waking up the MAC. + */ +#define HBUS_BASE (0x400) + +/* + * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM + * structures, error log, event log, verifying uCode load). + * First write to address register, then read from or write to data register + * to complete the job. Once the address register is set up, accesses to + * data registers auto-increment the address by one dword. + * Bit usage for address registers (read or write): + * 0-31: memory address within device + */ +#define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c) +#define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010) +#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018) +#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c) + +/* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */ +#define HBUS_TARG_MBX_C (HBUS_BASE+0x030) +#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004) + +/* + * Registers for accessing device's internal peripheral registers + * (e.g. SCD, BSM, etc.). First write to address register, + * then read from or write to data register to complete the job. + * Bit usage for address registers (read or write): + * 0-15: register address (offset) within device + * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword) + */ +#define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044) +#define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048) +#define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c) +#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050) + +/* Used to enable DBGM */ +#define HBUS_TARG_TEST_REG (HBUS_BASE+0x05c) + +/* + * Per-Tx-queue write pointer (index, really!) + * Indicates index to next TFD that driver will fill (1 past latest filled). + * Bit usage: + * 0-7: queue write index + * 11-8: queue selector + */ +#define HBUS_TARG_WRPTR (HBUS_BASE+0x060) + +/********************************************************** + * CSR values + **********************************************************/ + /* + * host interrupt timeout value + * used with setting interrupt coalescing timer + * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit + * + * default interrupt coalescing timer is 64 x 32 = 2048 usecs + */ +#define IWL_HOST_INT_TIMEOUT_MAX (0xFF) +#define IWL_HOST_INT_TIMEOUT_DEF (0x40) +#define IWL_HOST_INT_TIMEOUT_MIN (0x0) +#define IWL_HOST_INT_OPER_MODE BIT(31) + +/***************************************************************************** + * 7000/3000 series SHR DTS addresses * + *****************************************************************************/ + +/* Diode Results Register Structure: */ +enum dtd_diode_reg { + DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */ + DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */ + DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */ + DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */ + DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */ + DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */ +/* Those are the masks INSIDE the flags bit-field: */ + DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0, + DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */ + DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7, + DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */ +}; + +#endif /* !__iwl_csr_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.c b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c new file mode 100644 index 000000000000..09feff4fa226 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c @@ -0,0 +1,136 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include +#include "iwl-drv.h" +#include "iwl-debug.h" +#include "iwl-devtrace.h" + +#define __iwl_fn(fn) \ +void __iwl_ ##fn(struct device *dev, const char *fmt, ...) \ +{ \ + struct va_format vaf = { \ + .fmt = fmt, \ + }; \ + va_list args; \ + \ + va_start(args, fmt); \ + vaf.va = &args; \ + dev_ ##fn(dev, "%pV", &vaf); \ + trace_iwlwifi_ ##fn(&vaf); \ + va_end(args); \ +} + +__iwl_fn(warn) +IWL_EXPORT_SYMBOL(__iwl_warn); +__iwl_fn(info) +IWL_EXPORT_SYMBOL(__iwl_info); +__iwl_fn(crit) +IWL_EXPORT_SYMBOL(__iwl_crit); + +void __iwl_err(struct device *dev, bool rfkill_prefix, bool trace_only, + const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + if (!trace_only) { + if (rfkill_prefix) + dev_err(dev, "(RFKILL) %pV", &vaf); + else + dev_err(dev, "%pV", &vaf); + } + trace_iwlwifi_err(&vaf); + va_end(args); +} +IWL_EXPORT_SYMBOL(__iwl_err); + +#if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING) +void __iwl_dbg(struct device *dev, + u32 level, bool limit, const char *function, + const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; +#ifdef CONFIG_IWLWIFI_DEBUG + if (iwl_have_debug_level(level) && + (!limit || net_ratelimit())) + dev_printk(KERN_DEBUG, dev, "%c %s %pV", + in_interrupt() ? 'I' : 'U', function, &vaf); +#endif + trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf); + va_end(args); +} +IWL_EXPORT_SYMBOL(__iwl_dbg); +#endif diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h new file mode 100644 index 000000000000..9bb36d79c2bd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h @@ -0,0 +1,225 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_debug_h__ +#define __iwl_debug_h__ + +#include "iwl-modparams.h" + + +static inline bool iwl_have_debug_level(u32 level) +{ +#ifdef CONFIG_IWLWIFI_DEBUG + return iwlwifi_mod_params.debug_level & level; +#else + return false; +#endif +} + +void __iwl_err(struct device *dev, bool rfkill_prefix, bool only_trace, + const char *fmt, ...) __printf(4, 5); +void __iwl_warn(struct device *dev, const char *fmt, ...) __printf(2, 3); +void __iwl_info(struct device *dev, const char *fmt, ...) __printf(2, 3); +void __iwl_crit(struct device *dev, const char *fmt, ...) __printf(2, 3); + +/* not all compilers can evaluate strlen() at compile time, so use sizeof() */ +#define CHECK_FOR_NEWLINE(f) BUILD_BUG_ON(f[sizeof(f) - 2] != '\n') + +/* No matter what is m (priv, bus, trans), this will work */ +#define IWL_ERR_DEV(d, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_err((d), false, false, f, ## a); \ + } while (0) +#define IWL_ERR(m, f, a...) \ + IWL_ERR_DEV((m)->dev, f, ## a) +#define IWL_WARN(m, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_warn((m)->dev, f, ## a); \ + } while (0) +#define IWL_INFO(m, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_info((m)->dev, f, ## a); \ + } while (0) +#define IWL_CRIT(m, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_crit((m)->dev, f, ## a); \ + } while (0) + +#if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING) +void __iwl_dbg(struct device *dev, + u32 level, bool limit, const char *function, + const char *fmt, ...) __printf(5, 6); +#else +__printf(5, 6) static inline void +__iwl_dbg(struct device *dev, + u32 level, bool limit, const char *function, + const char *fmt, ...) +{} +#endif + +#define iwl_print_hex_error(m, p, len) \ +do { \ + print_hex_dump(KERN_ERR, "iwl data: ", \ + DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ +} while (0) + +#define __IWL_DEBUG_DEV(dev, level, limit, fmt, args...) \ + do { \ + CHECK_FOR_NEWLINE(fmt); \ + __iwl_dbg(dev, level, limit, __func__, fmt, ##args); \ + } while (0) +#define IWL_DEBUG(m, level, fmt, args...) \ + __IWL_DEBUG_DEV((m)->dev, level, false, fmt, ##args) +#define IWL_DEBUG_DEV(dev, level, fmt, args...) \ + __IWL_DEBUG_DEV(dev, level, false, fmt, ##args) +#define IWL_DEBUG_LIMIT(m, level, fmt, args...) \ + __IWL_DEBUG_DEV((m)->dev, level, true, fmt, ##args) + +#ifdef CONFIG_IWLWIFI_DEBUG +#define iwl_print_hex_dump(m, level, p, len) \ +do { \ + if (iwl_have_debug_level(level)) \ + print_hex_dump(KERN_DEBUG, "iwl data: ", \ + DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ +} while (0) +#else +#define iwl_print_hex_dump(m, level, p, len) +#endif /* CONFIG_IWLWIFI_DEBUG */ + +/* + * To use the debug system: + * + * If you are defining a new debug classification, simply add it to the #define + * list here in the form of + * + * #define IWL_DL_xxxx VALUE + * + * where xxxx should be the name of the classification (for example, WEP). + * + * You then need to either add a IWL_xxxx_DEBUG() macro definition for your + * classification, or use IWL_DEBUG(IWL_DL_xxxx, ...) whenever you want + * to send output to that classification. + * + * The active debug levels can be accessed via files + * + * /sys/module/iwlwifi/parameters/debug + * when CONFIG_IWLWIFI_DEBUG=y. + * + * /sys/kernel/debug/phy0/iwlwifi/debug/debug_level + * when CONFIG_IWLWIFI_DEBUGFS=y. + * + */ + +/* 0x0000000F - 0x00000001 */ +#define IWL_DL_INFO 0x00000001 +#define IWL_DL_MAC80211 0x00000002 +#define IWL_DL_HCMD 0x00000004 +#define IWL_DL_TDLS 0x00000008 +/* 0x000000F0 - 0x00000010 */ +#define IWL_DL_QUOTA 0x00000010 +#define IWL_DL_TE 0x00000020 +#define IWL_DL_EEPROM 0x00000040 +#define IWL_DL_RADIO 0x00000080 +/* 0x00000F00 - 0x00000100 */ +#define IWL_DL_POWER 0x00000100 +#define IWL_DL_TEMP 0x00000200 +#define IWL_DL_RPM 0x00000400 +#define IWL_DL_SCAN 0x00000800 +/* 0x0000F000 - 0x00001000 */ +#define IWL_DL_ASSOC 0x00001000 +#define IWL_DL_DROP 0x00002000 +#define IWL_DL_LAR 0x00004000 +#define IWL_DL_COEX 0x00008000 +/* 0x000F0000 - 0x00010000 */ +#define IWL_DL_FW 0x00010000 +#define IWL_DL_RF_KILL 0x00020000 +#define IWL_DL_FW_ERRORS 0x00040000 +#define IWL_DL_LED 0x00080000 +/* 0x00F00000 - 0x00100000 */ +#define IWL_DL_RATE 0x00100000 +#define IWL_DL_CALIB 0x00200000 +#define IWL_DL_WEP 0x00400000 +#define IWL_DL_TX 0x00800000 +/* 0x0F000000 - 0x01000000 */ +#define IWL_DL_RX 0x01000000 +#define IWL_DL_ISR 0x02000000 +#define IWL_DL_HT 0x04000000 +#define IWL_DL_EXTERNAL 0x08000000 +/* 0xF0000000 - 0x10000000 */ +#define IWL_DL_11H 0x10000000 +#define IWL_DL_STATS 0x20000000 +#define IWL_DL_TX_REPLY 0x40000000 +#define IWL_DL_TX_QUEUES 0x80000000 + +#define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a) +#define IWL_DEBUG_TDLS(p, f, a...) IWL_DEBUG(p, IWL_DL_TDLS, f, ## a) +#define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a) +#define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a) +#define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a) +#define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a) +#define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a) +#define IWL_DEBUG_TX(p, f, a...) IWL_DEBUG(p, IWL_DL_TX, f, ## a) +#define IWL_DEBUG_ISR(p, f, a...) IWL_DEBUG(p, IWL_DL_ISR, f, ## a) +#define IWL_DEBUG_LED(p, f, a...) IWL_DEBUG(p, IWL_DL_LED, f, ## a) +#define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a) +#define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a) +#define IWL_DEBUG_QUOTA(p, f, a...) IWL_DEBUG(p, IWL_DL_QUOTA, f, ## a) +#define IWL_DEBUG_TE(p, f, a...) IWL_DEBUG(p, IWL_DL_TE, f, ## a) +#define IWL_DEBUG_EEPROM(d, f, a...) IWL_DEBUG_DEV(d, IWL_DL_EEPROM, f, ## a) +#define IWL_DEBUG_CALIB(p, f, a...) IWL_DEBUG(p, IWL_DL_CALIB, f, ## a) +#define IWL_DEBUG_FW(p, f, a...) IWL_DEBUG(p, IWL_DL_FW, f, ## a) +#define IWL_DEBUG_RF_KILL(p, f, a...) IWL_DEBUG(p, IWL_DL_RF_KILL, f, ## a) +#define IWL_DEBUG_FW_ERRORS(p, f, a...) IWL_DEBUG(p, IWL_DL_FW_ERRORS, f, ## a) +#define IWL_DEBUG_DROP(p, f, a...) IWL_DEBUG(p, IWL_DL_DROP, f, ## a) +#define IWL_DEBUG_DROP_LIMIT(p, f, a...) \ + IWL_DEBUG_LIMIT(p, IWL_DL_DROP, f, ## a) +#define IWL_DEBUG_COEX(p, f, a...) IWL_DEBUG(p, IWL_DL_COEX, f, ## a) +#define IWL_DEBUG_RATE(p, f, a...) IWL_DEBUG(p, IWL_DL_RATE, f, ## a) +#define IWL_DEBUG_RATE_LIMIT(p, f, a...) \ + IWL_DEBUG_LIMIT(p, IWL_DL_RATE, f, ## a) +#define IWL_DEBUG_ASSOC(p, f, a...) \ + IWL_DEBUG(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a) +#define IWL_DEBUG_ASSOC_LIMIT(p, f, a...) \ + IWL_DEBUG_LIMIT(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a) +#define IWL_DEBUG_HT(p, f, a...) IWL_DEBUG(p, IWL_DL_HT, f, ## a) +#define IWL_DEBUG_STATS(p, f, a...) IWL_DEBUG(p, IWL_DL_STATS, f, ## a) +#define IWL_DEBUG_STATS_LIMIT(p, f, a...) \ + IWL_DEBUG_LIMIT(p, IWL_DL_STATS, f, ## a) +#define IWL_DEBUG_TX_REPLY(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_REPLY, f, ## a) +#define IWL_DEBUG_TX_QUEUES(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_QUEUES, f, ## a) +#define IWL_DEBUG_RADIO(p, f, a...) IWL_DEBUG(p, IWL_DL_RADIO, f, ## a) +#define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a) +#define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a) +#define IWL_DEBUG_RPM(p, f, a...) IWL_DEBUG(p, IWL_DL_RPM, f, ## a) +#define IWL_DEBUG_LAR(p, f, a...) IWL_DEBUG(p, IWL_DL_LAR, f, ## a) + +#endif diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h new file mode 100644 index 000000000000..71a78cede9b0 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#if !defined(__IWLWIFI_DEVICE_TRACE_DATA) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_DEVICE_TRACE_DATA + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_data + +TRACE_EVENT(iwlwifi_dev_tx_data, + TP_PROTO(const struct device *dev, + struct sk_buff *skb, + u8 hdr_len, size_t data_len), + TP_ARGS(dev, skb, hdr_len, data_len), + TP_STRUCT__entry( + DEV_ENTRY + + __dynamic_array(u8, data, iwl_trace_data(skb) ? data_len : 0) + ), + TP_fast_assign( + DEV_ASSIGN; + if (iwl_trace_data(skb)) + skb_copy_bits(skb, hdr_len, + __get_dynamic_array(data), data_len); + ), + TP_printk("[%s] TX frame data", __get_str(dev)) +); + +TRACE_EVENT(iwlwifi_dev_rx_data, + TP_PROTO(const struct device *dev, + const struct iwl_trans *trans, + void *rxbuf, size_t len), + TP_ARGS(dev, trans, rxbuf, len), + TP_STRUCT__entry( + DEV_ENTRY + + __dynamic_array(u8, data, + len - iwl_rx_trace_len(trans, rxbuf, len)) + ), + TP_fast_assign( + size_t offs = iwl_rx_trace_len(trans, rxbuf, len); + DEV_ASSIGN; + if (offs < len) + memcpy(__get_dynamic_array(data), + ((u8 *)rxbuf) + offs, len - offs); + ), + TP_printk("[%s] RX frame data", __get_str(dev)) +); +#endif /* __IWLWIFI_DEVICE_TRACE_DATA */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace-data +#include diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h new file mode 100644 index 000000000000..f62c54485852 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h @@ -0,0 +1,155 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#if !defined(__IWLWIFI_DEVICE_TRACE_IO) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_DEVICE_TRACE_IO + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_io + +TRACE_EVENT(iwlwifi_dev_ioread32, + TP_PROTO(const struct device *dev, u32 offs, u32 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] read io[%#x] = %#x", + __get_str(dev), __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_iowrite8, + TP_PROTO(const struct device *dev, u32 offs, u8 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, offs) + __field(u8, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] write io[%#x] = %#x)", + __get_str(dev), __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_iowrite32, + TP_PROTO(const struct device *dev, u32 offs, u32 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] write io[%#x] = %#x)", + __get_str(dev), __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_iowrite_prph32, + TP_PROTO(const struct device *dev, u32 offs, u32 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] write PRPH[%#x] = %#x)", + __get_str(dev), __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_ioread_prph32, + TP_PROTO(const struct device *dev, u32 offs, u32 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] read PRPH[%#x] = %#x", + __get_str(dev), __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_irq, + TP_PROTO(const struct device *dev), + TP_ARGS(dev), + TP_STRUCT__entry( + DEV_ENTRY + ), + TP_fast_assign( + DEV_ASSIGN; + ), + /* TP_printk("") doesn't compile */ + TP_printk("%d", 0) +); + +TRACE_EVENT(iwlwifi_dev_ict_read, + TP_PROTO(const struct device *dev, u32 index, u32 value), + TP_ARGS(dev, index, value), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, index) + __field(u32, value) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->index = index; + __entry->value = value; + ), + TP_printk("[%s] read ict[%d] = %#.8x", + __get_str(dev), __entry->index, __entry->value) +); +#endif /* __IWLWIFI_DEVICE_TRACE_IO */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace-io +#include diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h new file mode 100644 index 000000000000..eb4b99a1c8cd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h @@ -0,0 +1,209 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#if !defined(__IWLWIFI_DEVICE_TRACE_IWLWIFI) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_DEVICE_TRACE_IWLWIFI + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi + +TRACE_EVENT(iwlwifi_dev_hcmd, + TP_PROTO(const struct device *dev, + struct iwl_host_cmd *cmd, u16 total_size, + struct iwl_cmd_header_wide *hdr), + TP_ARGS(dev, cmd, total_size, hdr), + TP_STRUCT__entry( + DEV_ENTRY + __dynamic_array(u8, hcmd, total_size) + __field(u32, flags) + ), + TP_fast_assign( + int i, offset = sizeof(struct iwl_cmd_header); + + if (hdr->group_id) + offset = sizeof(struct iwl_cmd_header_wide); + + DEV_ASSIGN; + __entry->flags = cmd->flags; + memcpy(__get_dynamic_array(hcmd), hdr, offset); + + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { + if (!cmd->len[i]) + continue; + memcpy((u8 *)__get_dynamic_array(hcmd) + offset, + cmd->data[i], cmd->len[i]); + offset += cmd->len[i]; + } + ), + TP_printk("[%s] hcmd %#.2x.%#.2x (%ssync)", + __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[1], + ((u8 *)__get_dynamic_array(hcmd))[0], + __entry->flags & CMD_ASYNC ? "a" : "") +); + +TRACE_EVENT(iwlwifi_dev_rx, + TP_PROTO(const struct device *dev, const struct iwl_trans *trans, + struct iwl_rx_packet *pkt, size_t len), + TP_ARGS(dev, trans, pkt, len), + TP_STRUCT__entry( + DEV_ENTRY + __field(u8, cmd) + __dynamic_array(u8, rxbuf, iwl_rx_trace_len(trans, pkt, len)) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->cmd = pkt->hdr.cmd; + memcpy(__get_dynamic_array(rxbuf), pkt, + iwl_rx_trace_len(trans, pkt, len)); + ), + TP_printk("[%s] RX cmd %#.2x", + __get_str(dev), __entry->cmd) +); + +TRACE_EVENT(iwlwifi_dev_tx, + TP_PROTO(const struct device *dev, struct sk_buff *skb, + void *tfd, size_t tfdlen, + void *buf0, size_t buf0_len, + void *buf1, size_t buf1_len), + TP_ARGS(dev, skb, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len), + TP_STRUCT__entry( + DEV_ENTRY + + __field(size_t, framelen) + __dynamic_array(u8, tfd, tfdlen) + + /* + * Do not insert between or below these items, + * we want to keep the frame together (except + * for the possible padding). + */ + __dynamic_array(u8, buf0, buf0_len) + __dynamic_array(u8, buf1, iwl_trace_data(skb) ? 0 : buf1_len) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->framelen = buf0_len + buf1_len; + memcpy(__get_dynamic_array(tfd), tfd, tfdlen); + memcpy(__get_dynamic_array(buf0), buf0, buf0_len); + if (!iwl_trace_data(skb)) + memcpy(__get_dynamic_array(buf1), buf1, buf1_len); + ), + TP_printk("[%s] TX %.2x (%zu bytes)", + __get_str(dev), ((u8 *)__get_dynamic_array(buf0))[0], + __entry->framelen) +); + +TRACE_EVENT(iwlwifi_dev_ucode_error, + TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low, + u32 data1, u32 data2, u32 line, u32 blink1, + u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time, + u32 gp1, u32 gp2, u32 gp3, u32 major, u32 minor, u32 hw_ver, + u32 brd_ver), + TP_ARGS(dev, desc, tsf_low, data1, data2, line, + blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2, + gp3, major, minor, hw_ver, brd_ver), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, desc) + __field(u32, tsf_low) + __field(u32, data1) + __field(u32, data2) + __field(u32, line) + __field(u32, blink1) + __field(u32, blink2) + __field(u32, ilink1) + __field(u32, ilink2) + __field(u32, bcon_time) + __field(u32, gp1) + __field(u32, gp2) + __field(u32, gp3) + __field(u32, major) + __field(u32, minor) + __field(u32, hw_ver) + __field(u32, brd_ver) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->desc = desc; + __entry->tsf_low = tsf_low; + __entry->data1 = data1; + __entry->data2 = data2; + __entry->line = line; + __entry->blink1 = blink1; + __entry->blink2 = blink2; + __entry->ilink1 = ilink1; + __entry->ilink2 = ilink2; + __entry->bcon_time = bcon_time; + __entry->gp1 = gp1; + __entry->gp2 = gp2; + __entry->gp3 = gp3; + __entry->major = major; + __entry->minor = minor; + __entry->hw_ver = hw_ver; + __entry->brd_ver = brd_ver; + ), + TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, " + "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X " + "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X major 0x%08X " + "minor 0x%08X hw 0x%08X brd 0x%08X", + __get_str(dev), __entry->desc, __entry->tsf_low, + __entry->data1, + __entry->data2, __entry->line, __entry->blink1, + __entry->blink2, __entry->ilink1, __entry->ilink2, + __entry->bcon_time, __entry->gp1, __entry->gp2, + __entry->gp3, __entry->major, __entry->minor, + __entry->hw_ver, __entry->brd_ver) +); + +TRACE_EVENT(iwlwifi_dev_ucode_event, + TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev), + TP_ARGS(dev, time, data, ev), + TP_STRUCT__entry( + DEV_ENTRY + + __field(u32, time) + __field(u32, data) + __field(u32, ev) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->time = time; + __entry->data = data; + __entry->ev = ev; + ), + TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u", + __get_str(dev), __entry->time, __entry->data, __entry->ev) +); +#endif /* __IWLWIFI_DEVICE_TRACE_IWLWIFI */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace-iwlwifi +#include diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h new file mode 100644 index 000000000000..a3b3c2465f89 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h @@ -0,0 +1,97 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#if !defined(__IWLWIFI_DEVICE_TRACE_MSG) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_DEVICE_TRACE_MSG + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_msg + +#define MAX_MSG_LEN 110 + +DECLARE_EVENT_CLASS(iwlwifi_msg_event, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry( + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_err, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_warn, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_info, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_crit, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +TRACE_EVENT(iwlwifi_dbg, + TP_PROTO(u32 level, bool in_interrupt, const char *function, + struct va_format *vaf), + TP_ARGS(level, in_interrupt, function, vaf), + TP_STRUCT__entry( + __field(u32, level) + __field(u8, in_interrupt) + __string(function, function) + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + __entry->level = level; + __entry->in_interrupt = in_interrupt; + __assign_str(function, function); + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s", __get_str(msg)) +); +#endif /* __IWLWIFI_DEVICE_TRACE_MSG */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace-msg +#include diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h new file mode 100644 index 000000000000..10839fae9cd9 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h @@ -0,0 +1,81 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#if !defined(__IWLWIFI_DEVICE_TRACE_UCODE) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_DEVICE_TRACE_UCODE + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_ucode + +TRACE_EVENT(iwlwifi_dev_ucode_cont_event, + TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev), + TP_ARGS(dev, time, data, ev), + TP_STRUCT__entry( + DEV_ENTRY + + __field(u32, time) + __field(u32, data) + __field(u32, ev) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->time = time; + __entry->data = data; + __entry->ev = ev; + ), + TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u", + __get_str(dev), __entry->time, __entry->data, __entry->ev) +); + +TRACE_EVENT(iwlwifi_dev_ucode_wrap_event, + TP_PROTO(const struct device *dev, u32 wraps, u32 n_entry, u32 p_entry), + TP_ARGS(dev, wraps, n_entry, p_entry), + TP_STRUCT__entry( + DEV_ENTRY + + __field(u32, wraps) + __field(u32, n_entry) + __field(u32, p_entry) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->wraps = wraps; + __entry->n_entry = n_entry; + __entry->p_entry = p_entry; + ), + TP_printk("[%s] wraps=#%02d n=0x%X p=0x%X", + __get_str(dev), __entry->wraps, __entry->n_entry, + __entry->p_entry) +); +#endif /* __IWLWIFI_DEVICE_TRACE_UCODE */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace-ucode +#include diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c new file mode 100644 index 000000000000..90987d6f348e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c @@ -0,0 +1,43 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include + +/* sparse doesn't like tracepoint macros */ +#ifndef __CHECKER__ +#include "iwl-trans.h" + +#define CREATE_TRACE_POINTS +#include "iwl-devtrace.h" + +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite8); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ioread32); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_cont_event); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_wrap_event); +#endif diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h new file mode 100644 index 000000000000..b87acd6a229b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h @@ -0,0 +1,89 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __IWLWIFI_DEVICE_TRACE +#include +#include +#include +#include "iwl-trans.h" +#if !defined(__IWLWIFI_DEVICE_TRACE) +static inline bool iwl_trace_data(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (!ieee80211_is_data(hdr->frame_control)) + return false; + return !(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO); +} + +static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans, + void *rxbuf, size_t len) +{ + struct iwl_cmd_header *cmd = (void *)((u8 *)rxbuf + sizeof(__le32)); + struct ieee80211_hdr *hdr; + + if (cmd->cmd != trans->rx_mpdu_cmd) + return len; + + hdr = (void *)((u8 *)cmd + sizeof(struct iwl_cmd_header) + + trans->rx_mpdu_cmd_hdr_size); + if (!ieee80211_is_data(hdr->frame_control)) + return len; + /* maybe try to identify EAPOL frames? */ + return sizeof(__le32) + sizeof(*cmd) + trans->rx_mpdu_cmd_hdr_size + + ieee80211_hdrlen(hdr->frame_control); +} +#endif + +#define __IWLWIFI_DEVICE_TRACE + +#include +#include +#include "iwl-trans.h" + + +#if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__) +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(...) +#undef DEFINE_EVENT +#define DEFINE_EVENT(evt_class, name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#endif + +#define DEV_ENTRY __string(dev, dev_name(dev)) +#define DEV_ASSIGN __assign_str(dev, dev_name(dev)) + +#include "iwl-devtrace-io.h" +#include "iwl-devtrace-ucode.h" +#include "iwl-devtrace-msg.h" +#include "iwl-devtrace-data.h" +#include "iwl-devtrace-iwlwifi.h" + +#endif /* __IWLWIFI_DEVICE_TRACE */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c new file mode 100644 index 000000000000..463cadfbfccb --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -0,0 +1,1706 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include "iwl-drv.h" +#include "iwl-csr.h" +#include "iwl-debug.h" +#include "iwl-trans.h" +#include "iwl-op-mode.h" +#include "iwl-agn-hw.h" +#include "iwl-fw.h" +#include "iwl-config.h" +#include "iwl-modparams.h" + +/****************************************************************************** + * + * module boiler plate + * + ******************************************************************************/ + +#define DRV_DESCRIPTION "Intel(R) Wireless WiFi driver for Linux" +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_IWLWIFI_DEBUGFS +static struct dentry *iwl_dbgfs_root; +#endif + +/** + * struct iwl_drv - drv common data + * @list: list of drv structures using this opmode + * @fw: the iwl_fw structure + * @op_mode: the running op_mode + * @trans: transport layer + * @dev: for debug prints only + * @cfg: configuration struct + * @fw_index: firmware revision to try loading + * @firmware_name: composite filename of ucode file to load + * @request_firmware_complete: the firmware has been obtained from user space + */ +struct iwl_drv { + struct list_head list; + struct iwl_fw fw; + + struct iwl_op_mode *op_mode; + struct iwl_trans *trans; + struct device *dev; + const struct iwl_cfg *cfg; + + int fw_index; /* firmware we're trying to load */ + char firmware_name[32]; /* name of firmware file to load */ + + struct completion request_firmware_complete; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct dentry *dbgfs_drv; + struct dentry *dbgfs_trans; + struct dentry *dbgfs_op_mode; +#endif +}; + +enum { + DVM_OP_MODE = 0, + MVM_OP_MODE = 1, +}; + +/* Protects the table contents, i.e. the ops pointer & drv list */ +static struct mutex iwlwifi_opmode_table_mtx; +static struct iwlwifi_opmode_table { + const char *name; /* name: iwldvm, iwlmvm, etc */ + const struct iwl_op_mode_ops *ops; /* pointer to op_mode ops */ + struct list_head drv; /* list of devices using this op_mode */ +} iwlwifi_opmode_table[] = { /* ops set when driver is initialized */ + [DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL }, + [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL }, +}; + +#define IWL_DEFAULT_SCAN_CHANNELS 40 + +/* + * struct fw_sec: Just for the image parsing process. + * For the fw storage we are using struct fw_desc. + */ +struct fw_sec { + const void *data; /* the sec data */ + size_t size; /* section size */ + u32 offset; /* offset of writing in the device */ +}; + +static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc) +{ + vfree(desc->data); + desc->data = NULL; + desc->len = 0; +} + +static void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img) +{ + int i; + for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) + iwl_free_fw_desc(drv, &img->sec[i]); +} + +static void iwl_dealloc_ucode(struct iwl_drv *drv) +{ + int i; + + kfree(drv->fw.dbg_dest_tlv); + for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) + kfree(drv->fw.dbg_conf_tlv[i]); + for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) + kfree(drv->fw.dbg_trigger_tlv[i]); + + for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) + iwl_free_fw_img(drv, drv->fw.img + i); +} + +static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc, + struct fw_sec *sec) +{ + void *data; + + desc->data = NULL; + + if (!sec || !sec->size) + return -EINVAL; + + data = vmalloc(sec->size); + if (!data) + return -ENOMEM; + + desc->len = sec->size; + desc->offset = sec->offset; + memcpy(data, sec->data, desc->len); + desc->data = data; + + return 0; +} + +static void iwl_req_fw_callback(const struct firmware *ucode_raw, + void *context); + +#define UCODE_EXPERIMENTAL_INDEX 100 +#define UCODE_EXPERIMENTAL_TAG "exp" + +static int iwl_request_firmware(struct iwl_drv *drv, bool first) +{ + const char *name_pre = drv->cfg->fw_name_pre; + char tag[8]; + + if (first) { +#ifdef CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE + drv->fw_index = UCODE_EXPERIMENTAL_INDEX; + strcpy(tag, UCODE_EXPERIMENTAL_TAG); + } else if (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) { +#endif + drv->fw_index = drv->cfg->ucode_api_max; + sprintf(tag, "%d", drv->fw_index); + } else { + drv->fw_index--; + sprintf(tag, "%d", drv->fw_index); + } + + if (drv->fw_index < drv->cfg->ucode_api_min) { + IWL_ERR(drv, "no suitable firmware found!\n"); + return -ENOENT; + } + + snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode", + name_pre, tag); + + /* + * Starting 8000B - FW name format has changed. This overwrites the + * previous name and uses the new format. + */ + if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { + char rev_step = 'A' + CSR_HW_REV_STEP(drv->trans->hw_rev); + + snprintf(drv->firmware_name, sizeof(drv->firmware_name), + "%s%c-%s.ucode", name_pre, rev_step, tag); + } + + IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n", + (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) + ? "EXPERIMENTAL " : "", + drv->firmware_name); + + return request_firmware_nowait(THIS_MODULE, 1, drv->firmware_name, + drv->trans->dev, + GFP_KERNEL, drv, iwl_req_fw_callback); +} + +struct fw_img_parsing { + struct fw_sec sec[IWL_UCODE_SECTION_MAX]; + int sec_counter; +}; + +/* + * struct fw_sec_parsing: to extract fw section and it's offset from tlv + */ +struct fw_sec_parsing { + __le32 offset; + const u8 data[]; +} __packed; + +/** + * struct iwl_tlv_calib_data - parse the default calib data from TLV + * + * @ucode_type: the uCode to which the following default calib relates. + * @calib: default calibrations. + */ +struct iwl_tlv_calib_data { + __le32 ucode_type; + struct iwl_tlv_calib_ctrl calib; +} __packed; + +struct iwl_firmware_pieces { + struct fw_img_parsing img[IWL_UCODE_TYPE_MAX]; + + u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr; + u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr; + + /* FW debug data parsed for driver usage */ + struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; + struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX]; + size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX]; + struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX]; + size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX]; +}; + +/* + * These functions are just to extract uCode section data from the pieces + * structure. + */ +static struct fw_sec *get_sec(struct iwl_firmware_pieces *pieces, + enum iwl_ucode_type type, + int sec) +{ + return &pieces->img[type].sec[sec]; +} + +static void set_sec_data(struct iwl_firmware_pieces *pieces, + enum iwl_ucode_type type, + int sec, + const void *data) +{ + pieces->img[type].sec[sec].data = data; +} + +static void set_sec_size(struct iwl_firmware_pieces *pieces, + enum iwl_ucode_type type, + int sec, + size_t size) +{ + pieces->img[type].sec[sec].size = size; +} + +static size_t get_sec_size(struct iwl_firmware_pieces *pieces, + enum iwl_ucode_type type, + int sec) +{ + return pieces->img[type].sec[sec].size; +} + +static void set_sec_offset(struct iwl_firmware_pieces *pieces, + enum iwl_ucode_type type, + int sec, + u32 offset) +{ + pieces->img[type].sec[sec].offset = offset; +} + +static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len) +{ + int i, j; + struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data; + struct iwl_fw_cipher_scheme *fwcs; + struct ieee80211_cipher_scheme *cs; + u32 cipher; + + if (len < sizeof(*l) || + len < sizeof(l->size) + l->size * sizeof(l->cs[0])) + return -EINVAL; + + for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) { + fwcs = &l->cs[j]; + cipher = le32_to_cpu(fwcs->cipher); + + /* we skip schemes with zero cipher suite selector */ + if (!cipher) + continue; + + cs = &fw->cs[j++]; + cs->cipher = cipher; + cs->iftype = BIT(NL80211_IFTYPE_STATION); + cs->hdr_len = fwcs->hdr_len; + cs->pn_len = fwcs->pn_len; + cs->pn_off = fwcs->pn_off; + cs->key_idx_off = fwcs->key_idx_off; + cs->key_idx_mask = fwcs->key_idx_mask; + cs->key_idx_shift = fwcs->key_idx_shift; + cs->mic_len = fwcs->mic_len; + } + + return 0; +} + +static int iwl_store_gscan_capa(struct iwl_fw *fw, const u8 *data, + const u32 len) +{ + struct iwl_fw_gscan_capabilities *fw_capa = (void *)data; + struct iwl_gscan_capabilities *capa = &fw->gscan_capa; + + if (len < sizeof(*fw_capa)) + return -EINVAL; + + capa->max_scan_cache_size = le32_to_cpu(fw_capa->max_scan_cache_size); + capa->max_scan_buckets = le32_to_cpu(fw_capa->max_scan_buckets); + capa->max_ap_cache_per_scan = + le32_to_cpu(fw_capa->max_ap_cache_per_scan); + capa->max_rssi_sample_size = le32_to_cpu(fw_capa->max_rssi_sample_size); + capa->max_scan_reporting_threshold = + le32_to_cpu(fw_capa->max_scan_reporting_threshold); + capa->max_hotlist_aps = le32_to_cpu(fw_capa->max_hotlist_aps); + capa->max_significant_change_aps = + le32_to_cpu(fw_capa->max_significant_change_aps); + capa->max_bssid_history_entries = + le32_to_cpu(fw_capa->max_bssid_history_entries); + return 0; +} + +/* + * Gets uCode section from tlv. + */ +static int iwl_store_ucode_sec(struct iwl_firmware_pieces *pieces, + const void *data, enum iwl_ucode_type type, + int size) +{ + struct fw_img_parsing *img; + struct fw_sec *sec; + struct fw_sec_parsing *sec_parse; + + if (WARN_ON(!pieces || !data || type >= IWL_UCODE_TYPE_MAX)) + return -1; + + sec_parse = (struct fw_sec_parsing *)data; + + img = &pieces->img[type]; + sec = &img->sec[img->sec_counter]; + + sec->offset = le32_to_cpu(sec_parse->offset); + sec->data = sec_parse->data; + sec->size = size - sizeof(sec_parse->offset); + + ++img->sec_counter; + + return 0; +} + +static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data) +{ + struct iwl_tlv_calib_data *def_calib = + (struct iwl_tlv_calib_data *)data; + u32 ucode_type = le32_to_cpu(def_calib->ucode_type); + if (ucode_type >= IWL_UCODE_TYPE_MAX) { + IWL_ERR(drv, "Wrong ucode_type %u for default calibration.\n", + ucode_type); + return -EINVAL; + } + drv->fw.default_calib[ucode_type].flow_trigger = + def_calib->calib.flow_trigger; + drv->fw.default_calib[ucode_type].event_trigger = + def_calib->calib.event_trigger; + + return 0; +} + +static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, + struct iwl_ucode_capabilities *capa) +{ + const struct iwl_ucode_api *ucode_api = (void *)data; + u32 api_index = le32_to_cpu(ucode_api->api_index); + u32 api_flags = le32_to_cpu(ucode_api->api_flags); + int i; + + if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) { + IWL_ERR(drv, "api_index larger than supported by driver\n"); + /* don't return an error so we can load FW that has more bits */ + return 0; + } + + for (i = 0; i < 32; i++) { + if (api_flags & BIT(i)) + __set_bit(i + 32 * api_index, capa->_api); + } + + return 0; +} + +static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data, + struct iwl_ucode_capabilities *capa) +{ + const struct iwl_ucode_capa *ucode_capa = (void *)data; + u32 api_index = le32_to_cpu(ucode_capa->api_index); + u32 api_flags = le32_to_cpu(ucode_capa->api_capa); + int i; + + if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) { + IWL_ERR(drv, "api_index larger than supported by driver\n"); + /* don't return an error so we can load FW that has more bits */ + return 0; + } + + for (i = 0; i < 32; i++) { + if (api_flags & BIT(i)) + __set_bit(i + 32 * api_index, capa->_capa); + } + + return 0; +} + +static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv, + const struct firmware *ucode_raw, + struct iwl_firmware_pieces *pieces) +{ + struct iwl_ucode_header *ucode = (void *)ucode_raw->data; + u32 api_ver, hdr_size, build; + char buildstr[25]; + const u8 *src; + + drv->fw.ucode_ver = le32_to_cpu(ucode->ver); + api_ver = IWL_UCODE_API(drv->fw.ucode_ver); + + switch (api_ver) { + default: + hdr_size = 28; + if (ucode_raw->size < hdr_size) { + IWL_ERR(drv, "File size too small!\n"); + return -EINVAL; + } + build = le32_to_cpu(ucode->u.v2.build); + set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, + le32_to_cpu(ucode->u.v2.inst_size)); + set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, + le32_to_cpu(ucode->u.v2.data_size)); + set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, + le32_to_cpu(ucode->u.v2.init_size)); + set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, + le32_to_cpu(ucode->u.v2.init_data_size)); + src = ucode->u.v2.data; + break; + case 0: + case 1: + case 2: + hdr_size = 24; + if (ucode_raw->size < hdr_size) { + IWL_ERR(drv, "File size too small!\n"); + return -EINVAL; + } + build = 0; + set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, + le32_to_cpu(ucode->u.v1.inst_size)); + set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, + le32_to_cpu(ucode->u.v1.data_size)); + set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, + le32_to_cpu(ucode->u.v1.init_size)); + set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, + le32_to_cpu(ucode->u.v1.init_data_size)); + src = ucode->u.v1.data; + break; + } + + if (build) + sprintf(buildstr, " build %u%s", build, + (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) + ? " (EXP)" : ""); + else + buildstr[0] = '\0'; + + snprintf(drv->fw.fw_version, + sizeof(drv->fw.fw_version), + "%u.%u.%u.%u%s", + IWL_UCODE_MAJOR(drv->fw.ucode_ver), + IWL_UCODE_MINOR(drv->fw.ucode_ver), + IWL_UCODE_API(drv->fw.ucode_ver), + IWL_UCODE_SERIAL(drv->fw.ucode_ver), + buildstr); + + /* Verify size of file vs. image size info in file's header */ + + if (ucode_raw->size != hdr_size + + get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) + + get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) + + get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) + + get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)) { + + IWL_ERR(drv, + "uCode file size %d does not match expected size\n", + (int)ucode_raw->size); + return -EINVAL; + } + + + set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, src); + src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST); + set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, + IWLAGN_RTC_INST_LOWER_BOUND); + set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, src); + src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA); + set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, + IWLAGN_RTC_DATA_LOWER_BOUND); + set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, src); + src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST); + set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, + IWLAGN_RTC_INST_LOWER_BOUND); + set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, src); + src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA); + set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, + IWLAGN_RTC_DATA_LOWER_BOUND); + return 0; +} + +static int iwl_parse_tlv_firmware(struct iwl_drv *drv, + const struct firmware *ucode_raw, + struct iwl_firmware_pieces *pieces, + struct iwl_ucode_capabilities *capa) +{ + struct iwl_tlv_ucode_header *ucode = (void *)ucode_raw->data; + struct iwl_ucode_tlv *tlv; + size_t len = ucode_raw->size; + const u8 *data; + u32 tlv_len; + u32 usniffer_img; + enum iwl_ucode_tlv_type tlv_type; + const u8 *tlv_data; + char buildstr[25]; + u32 build, paging_mem_size; + int num_of_cpus; + bool usniffer_images = false; + bool usniffer_req = false; + bool gscan_capa = false; + + if (len < sizeof(*ucode)) { + IWL_ERR(drv, "uCode has invalid length: %zd\n", len); + return -EINVAL; + } + + if (ucode->magic != cpu_to_le32(IWL_TLV_UCODE_MAGIC)) { + IWL_ERR(drv, "invalid uCode magic: 0X%x\n", + le32_to_cpu(ucode->magic)); + return -EINVAL; + } + + drv->fw.ucode_ver = le32_to_cpu(ucode->ver); + memcpy(drv->fw.human_readable, ucode->human_readable, + sizeof(drv->fw.human_readable)); + build = le32_to_cpu(ucode->build); + + if (build) + sprintf(buildstr, " build %u%s", build, + (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) + ? " (EXP)" : ""); + else + buildstr[0] = '\0'; + + snprintf(drv->fw.fw_version, + sizeof(drv->fw.fw_version), + "%u.%u.%u.%u%s", + IWL_UCODE_MAJOR(drv->fw.ucode_ver), + IWL_UCODE_MINOR(drv->fw.ucode_ver), + IWL_UCODE_API(drv->fw.ucode_ver), + IWL_UCODE_SERIAL(drv->fw.ucode_ver), + buildstr); + + data = ucode->data; + + len -= sizeof(*ucode); + + while (len >= sizeof(*tlv)) { + len -= sizeof(*tlv); + tlv = (void *)data; + + tlv_len = le32_to_cpu(tlv->length); + tlv_type = le32_to_cpu(tlv->type); + tlv_data = tlv->data; + + if (len < tlv_len) { + IWL_ERR(drv, "invalid TLV len: %zd/%u\n", + len, tlv_len); + return -EINVAL; + } + len -= ALIGN(tlv_len, 4); + data += sizeof(*tlv) + ALIGN(tlv_len, 4); + + switch (tlv_type) { + case IWL_UCODE_TLV_INST: + set_sec_data(pieces, IWL_UCODE_REGULAR, + IWL_UCODE_SECTION_INST, tlv_data); + set_sec_size(pieces, IWL_UCODE_REGULAR, + IWL_UCODE_SECTION_INST, tlv_len); + set_sec_offset(pieces, IWL_UCODE_REGULAR, + IWL_UCODE_SECTION_INST, + IWLAGN_RTC_INST_LOWER_BOUND); + break; + case IWL_UCODE_TLV_DATA: + set_sec_data(pieces, IWL_UCODE_REGULAR, + IWL_UCODE_SECTION_DATA, tlv_data); + set_sec_size(pieces, IWL_UCODE_REGULAR, + IWL_UCODE_SECTION_DATA, tlv_len); + set_sec_offset(pieces, IWL_UCODE_REGULAR, + IWL_UCODE_SECTION_DATA, + IWLAGN_RTC_DATA_LOWER_BOUND); + break; + case IWL_UCODE_TLV_INIT: + set_sec_data(pieces, IWL_UCODE_INIT, + IWL_UCODE_SECTION_INST, tlv_data); + set_sec_size(pieces, IWL_UCODE_INIT, + IWL_UCODE_SECTION_INST, tlv_len); + set_sec_offset(pieces, IWL_UCODE_INIT, + IWL_UCODE_SECTION_INST, + IWLAGN_RTC_INST_LOWER_BOUND); + break; + case IWL_UCODE_TLV_INIT_DATA: + set_sec_data(pieces, IWL_UCODE_INIT, + IWL_UCODE_SECTION_DATA, tlv_data); + set_sec_size(pieces, IWL_UCODE_INIT, + IWL_UCODE_SECTION_DATA, tlv_len); + set_sec_offset(pieces, IWL_UCODE_INIT, + IWL_UCODE_SECTION_DATA, + IWLAGN_RTC_DATA_LOWER_BOUND); + break; + case IWL_UCODE_TLV_BOOT: + IWL_ERR(drv, "Found unexpected BOOT ucode\n"); + break; + case IWL_UCODE_TLV_PROBE_MAX_LEN: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + capa->max_probe_length = + le32_to_cpup((__le32 *)tlv_data); + break; + case IWL_UCODE_TLV_PAN: + if (tlv_len) + goto invalid_tlv_len; + capa->flags |= IWL_UCODE_TLV_FLAGS_PAN; + break; + case IWL_UCODE_TLV_FLAGS: + /* must be at least one u32 */ + if (tlv_len < sizeof(u32)) + goto invalid_tlv_len; + /* and a proper number of u32s */ + if (tlv_len % sizeof(u32)) + goto invalid_tlv_len; + /* + * This driver only reads the first u32 as + * right now no more features are defined, + * if that changes then either the driver + * will not work with the new firmware, or + * it'll not take advantage of new features. + */ + capa->flags = le32_to_cpup((__le32 *)tlv_data); + break; + case IWL_UCODE_TLV_API_CHANGES_SET: + if (tlv_len != sizeof(struct iwl_ucode_api)) + goto invalid_tlv_len; + if (iwl_set_ucode_api_flags(drv, tlv_data, capa)) + goto tlv_error; + break; + case IWL_UCODE_TLV_ENABLED_CAPABILITIES: + if (tlv_len != sizeof(struct iwl_ucode_capa)) + goto invalid_tlv_len; + if (iwl_set_ucode_capabilities(drv, tlv_data, capa)) + goto tlv_error; + break; + case IWL_UCODE_TLV_INIT_EVTLOG_PTR: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + pieces->init_evtlog_ptr = + le32_to_cpup((__le32 *)tlv_data); + break; + case IWL_UCODE_TLV_INIT_EVTLOG_SIZE: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + pieces->init_evtlog_size = + le32_to_cpup((__le32 *)tlv_data); + break; + case IWL_UCODE_TLV_INIT_ERRLOG_PTR: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + pieces->init_errlog_ptr = + le32_to_cpup((__le32 *)tlv_data); + break; + case IWL_UCODE_TLV_RUNT_EVTLOG_PTR: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + pieces->inst_evtlog_ptr = + le32_to_cpup((__le32 *)tlv_data); + break; + case IWL_UCODE_TLV_RUNT_EVTLOG_SIZE: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + pieces->inst_evtlog_size = + le32_to_cpup((__le32 *)tlv_data); + break; + case IWL_UCODE_TLV_RUNT_ERRLOG_PTR: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + pieces->inst_errlog_ptr = + le32_to_cpup((__le32 *)tlv_data); + break; + case IWL_UCODE_TLV_ENHANCE_SENS_TBL: + if (tlv_len) + goto invalid_tlv_len; + drv->fw.enhance_sensitivity_table = true; + break; + case IWL_UCODE_TLV_WOWLAN_INST: + set_sec_data(pieces, IWL_UCODE_WOWLAN, + IWL_UCODE_SECTION_INST, tlv_data); + set_sec_size(pieces, IWL_UCODE_WOWLAN, + IWL_UCODE_SECTION_INST, tlv_len); + set_sec_offset(pieces, IWL_UCODE_WOWLAN, + IWL_UCODE_SECTION_INST, + IWLAGN_RTC_INST_LOWER_BOUND); + break; + case IWL_UCODE_TLV_WOWLAN_DATA: + set_sec_data(pieces, IWL_UCODE_WOWLAN, + IWL_UCODE_SECTION_DATA, tlv_data); + set_sec_size(pieces, IWL_UCODE_WOWLAN, + IWL_UCODE_SECTION_DATA, tlv_len); + set_sec_offset(pieces, IWL_UCODE_WOWLAN, + IWL_UCODE_SECTION_DATA, + IWLAGN_RTC_DATA_LOWER_BOUND); + break; + case IWL_UCODE_TLV_PHY_CALIBRATION_SIZE: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + capa->standard_phy_calibration_size = + le32_to_cpup((__le32 *)tlv_data); + break; + case IWL_UCODE_TLV_SEC_RT: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, + tlv_len); + drv->fw.mvm_fw = true; + break; + case IWL_UCODE_TLV_SEC_INIT: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, + tlv_len); + drv->fw.mvm_fw = true; + break; + case IWL_UCODE_TLV_SEC_WOWLAN: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, + tlv_len); + drv->fw.mvm_fw = true; + break; + case IWL_UCODE_TLV_DEF_CALIB: + if (tlv_len != sizeof(struct iwl_tlv_calib_data)) + goto invalid_tlv_len; + if (iwl_set_default_calib(drv, tlv_data)) + goto tlv_error; + break; + case IWL_UCODE_TLV_PHY_SKU: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data); + drv->fw.valid_tx_ant = (drv->fw.phy_config & + FW_PHY_CFG_TX_CHAIN) >> + FW_PHY_CFG_TX_CHAIN_POS; + drv->fw.valid_rx_ant = (drv->fw.phy_config & + FW_PHY_CFG_RX_CHAIN) >> + FW_PHY_CFG_RX_CHAIN_POS; + break; + case IWL_UCODE_TLV_SECURE_SEC_RT: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, + tlv_len); + drv->fw.mvm_fw = true; + break; + case IWL_UCODE_TLV_SECURE_SEC_INIT: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, + tlv_len); + drv->fw.mvm_fw = true; + break; + case IWL_UCODE_TLV_SECURE_SEC_WOWLAN: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, + tlv_len); + drv->fw.mvm_fw = true; + break; + case IWL_UCODE_TLV_NUM_OF_CPU: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + num_of_cpus = + le32_to_cpup((__le32 *)tlv_data); + + if (num_of_cpus == 2) { + drv->fw.img[IWL_UCODE_REGULAR].is_dual_cpus = + true; + drv->fw.img[IWL_UCODE_INIT].is_dual_cpus = + true; + drv->fw.img[IWL_UCODE_WOWLAN].is_dual_cpus = + true; + } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) { + IWL_ERR(drv, "Driver support upto 2 CPUs\n"); + return -EINVAL; + } + break; + case IWL_UCODE_TLV_CSCHEME: + if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len)) + goto invalid_tlv_len; + break; + case IWL_UCODE_TLV_N_SCAN_CHANNELS: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + capa->n_scan_channels = + le32_to_cpup((__le32 *)tlv_data); + break; + case IWL_UCODE_TLV_FW_VERSION: { + __le32 *ptr = (void *)tlv_data; + u32 major, minor; + u8 local_comp; + + if (tlv_len != sizeof(u32) * 3) + goto invalid_tlv_len; + + major = le32_to_cpup(ptr++); + minor = le32_to_cpup(ptr++); + local_comp = le32_to_cpup(ptr); + + snprintf(drv->fw.fw_version, + sizeof(drv->fw.fw_version), "%u.%u.%u", + major, minor, local_comp); + break; + } + case IWL_UCODE_TLV_FW_DBG_DEST: { + struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data; + + if (pieces->dbg_dest_tlv) { + IWL_ERR(drv, + "dbg destination ignored, already exists\n"); + break; + } + + pieces->dbg_dest_tlv = dest; + IWL_INFO(drv, "Found debug destination: %s\n", + get_fw_dbg_mode_string(dest->monitor_mode)); + + drv->fw.dbg_dest_reg_num = + tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv, + reg_ops); + drv->fw.dbg_dest_reg_num /= + sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]); + + break; + } + case IWL_UCODE_TLV_FW_DBG_CONF: { + struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data; + + if (!pieces->dbg_dest_tlv) { + IWL_ERR(drv, + "Ignore dbg config %d - no destination configured\n", + conf->id); + break; + } + + if (conf->id >= ARRAY_SIZE(drv->fw.dbg_conf_tlv)) { + IWL_ERR(drv, + "Skip unknown configuration: %d\n", + conf->id); + break; + } + + if (pieces->dbg_conf_tlv[conf->id]) { + IWL_ERR(drv, + "Ignore duplicate dbg config %d\n", + conf->id); + break; + } + + if (conf->usniffer) + usniffer_req = true; + + IWL_INFO(drv, "Found debug configuration: %d\n", + conf->id); + + pieces->dbg_conf_tlv[conf->id] = conf; + pieces->dbg_conf_tlv_len[conf->id] = tlv_len; + break; + } + case IWL_UCODE_TLV_FW_DBG_TRIGGER: { + struct iwl_fw_dbg_trigger_tlv *trigger = + (void *)tlv_data; + u32 trigger_id = le32_to_cpu(trigger->id); + + if (trigger_id >= ARRAY_SIZE(drv->fw.dbg_trigger_tlv)) { + IWL_ERR(drv, + "Skip unknown trigger: %u\n", + trigger->id); + break; + } + + if (pieces->dbg_trigger_tlv[trigger_id]) { + IWL_ERR(drv, + "Ignore duplicate dbg trigger %u\n", + trigger->id); + break; + } + + IWL_INFO(drv, "Found debug trigger: %u\n", trigger->id); + + pieces->dbg_trigger_tlv[trigger_id] = trigger; + pieces->dbg_trigger_tlv_len[trigger_id] = tlv_len; + break; + } + case IWL_UCODE_TLV_SEC_RT_USNIFFER: + usniffer_images = true; + iwl_store_ucode_sec(pieces, tlv_data, + IWL_UCODE_REGULAR_USNIFFER, + tlv_len); + break; + case IWL_UCODE_TLV_PAGING: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + paging_mem_size = le32_to_cpup((__le32 *)tlv_data); + + IWL_DEBUG_FW(drv, + "Paging: paging enabled (size = %u bytes)\n", + paging_mem_size); + + if (paging_mem_size > MAX_PAGING_IMAGE_SIZE) { + IWL_ERR(drv, + "Paging: driver supports up to %lu bytes for paging image\n", + MAX_PAGING_IMAGE_SIZE); + return -EINVAL; + } + + if (paging_mem_size & (FW_PAGING_SIZE - 1)) { + IWL_ERR(drv, + "Paging: image isn't multiple %lu\n", + FW_PAGING_SIZE); + return -EINVAL; + } + + drv->fw.img[IWL_UCODE_REGULAR].paging_mem_size = + paging_mem_size; + usniffer_img = IWL_UCODE_REGULAR_USNIFFER; + drv->fw.img[usniffer_img].paging_mem_size = + paging_mem_size; + break; + case IWL_UCODE_TLV_SDIO_ADMA_ADDR: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + drv->fw.sdio_adma_addr = + le32_to_cpup((__le32 *)tlv_data); + break; + case IWL_UCODE_TLV_FW_GSCAN_CAPA: + if (iwl_store_gscan_capa(&drv->fw, tlv_data, tlv_len)) + goto invalid_tlv_len; + gscan_capa = true; + break; + default: + IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); + break; + } + } + + if (usniffer_req && !usniffer_images) { + IWL_ERR(drv, + "user selected to work with usniffer but usniffer image isn't available in ucode package\n"); + return -EINVAL; + } + + if (len) { + IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len); + iwl_print_hex_dump(drv, IWL_DL_FW, (u8 *)data, len); + return -EINVAL; + } + + /* + * If ucode advertises that it supports GSCAN but GSCAN + * capabilities TLV is not present, warn and continue without GSCAN. + */ + if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT) && + WARN(!gscan_capa, + "GSCAN is supported but capabilities TLV is unavailable\n")) + __clear_bit((__force long)IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT, + capa->_capa); + + return 0; + + invalid_tlv_len: + IWL_ERR(drv, "TLV %d has invalid size: %u\n", tlv_type, tlv_len); + tlv_error: + iwl_print_hex_dump(drv, IWL_DL_FW, tlv_data, tlv_len); + + return -EINVAL; +} + +static int iwl_alloc_ucode(struct iwl_drv *drv, + struct iwl_firmware_pieces *pieces, + enum iwl_ucode_type type) +{ + int i; + for (i = 0; + i < IWL_UCODE_SECTION_MAX && get_sec_size(pieces, type, i); + i++) + if (iwl_alloc_fw_desc(drv, &(drv->fw.img[type].sec[i]), + get_sec(pieces, type, i))) + return -ENOMEM; + return 0; +} + +static int validate_sec_sizes(struct iwl_drv *drv, + struct iwl_firmware_pieces *pieces, + const struct iwl_cfg *cfg) +{ + IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %Zd\n", + get_sec_size(pieces, IWL_UCODE_REGULAR, + IWL_UCODE_SECTION_INST)); + IWL_DEBUG_INFO(drv, "f/w package hdr runtime data size = %Zd\n", + get_sec_size(pieces, IWL_UCODE_REGULAR, + IWL_UCODE_SECTION_DATA)); + IWL_DEBUG_INFO(drv, "f/w package hdr init inst size = %Zd\n", + get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST)); + IWL_DEBUG_INFO(drv, "f/w package hdr init data size = %Zd\n", + get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)); + + /* Verify that uCode images will fit in card's SRAM. */ + if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) > + cfg->max_inst_size) { + IWL_ERR(drv, "uCode instr len %Zd too large to fit in\n", + get_sec_size(pieces, IWL_UCODE_REGULAR, + IWL_UCODE_SECTION_INST)); + return -1; + } + + if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) > + cfg->max_data_size) { + IWL_ERR(drv, "uCode data len %Zd too large to fit in\n", + get_sec_size(pieces, IWL_UCODE_REGULAR, + IWL_UCODE_SECTION_DATA)); + return -1; + } + + if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) > + cfg->max_inst_size) { + IWL_ERR(drv, "uCode init instr len %Zd too large to fit in\n", + get_sec_size(pieces, IWL_UCODE_INIT, + IWL_UCODE_SECTION_INST)); + return -1; + } + + if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) > + cfg->max_data_size) { + IWL_ERR(drv, "uCode init data len %Zd too large to fit in\n", + get_sec_size(pieces, IWL_UCODE_REGULAR, + IWL_UCODE_SECTION_DATA)); + return -1; + } + return 0; +} + +static struct iwl_op_mode * +_iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op) +{ + const struct iwl_op_mode_ops *ops = op->ops; + struct dentry *dbgfs_dir = NULL; + struct iwl_op_mode *op_mode = NULL; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + drv->dbgfs_op_mode = debugfs_create_dir(op->name, + drv->dbgfs_drv); + if (!drv->dbgfs_op_mode) { + IWL_ERR(drv, + "failed to create opmode debugfs directory\n"); + return op_mode; + } + dbgfs_dir = drv->dbgfs_op_mode; +#endif + + op_mode = ops->start(drv->trans, drv->cfg, &drv->fw, dbgfs_dir); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (!op_mode) { + debugfs_remove_recursive(drv->dbgfs_op_mode); + drv->dbgfs_op_mode = NULL; + } +#endif + + return op_mode; +} + +static void _iwl_op_mode_stop(struct iwl_drv *drv) +{ + /* op_mode can be NULL if its start failed */ + if (drv->op_mode) { + iwl_op_mode_stop(drv->op_mode); + drv->op_mode = NULL; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + debugfs_remove_recursive(drv->dbgfs_op_mode); + drv->dbgfs_op_mode = NULL; +#endif + } +} + +/** + * iwl_req_fw_callback - callback when firmware was loaded + * + * If loaded successfully, copies the firmware into buffers + * for the card to fetch (via DMA). + */ +static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) +{ + struct iwl_drv *drv = context; + struct iwl_fw *fw = &drv->fw; + struct iwl_ucode_header *ucode; + struct iwlwifi_opmode_table *op; + int err; + struct iwl_firmware_pieces *pieces; + const unsigned int api_max = drv->cfg->ucode_api_max; + unsigned int api_ok = drv->cfg->ucode_api_ok; + const unsigned int api_min = drv->cfg->ucode_api_min; + size_t trigger_tlv_sz[FW_DBG_TRIGGER_MAX]; + u32 api_ver; + int i; + bool load_module = false; + + fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH; + fw->ucode_capa.standard_phy_calibration_size = + IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; + fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS; + + if (!api_ok) + api_ok = api_max; + + pieces = kzalloc(sizeof(*pieces), GFP_KERNEL); + if (!pieces) + return; + + if (!ucode_raw) { + if (drv->fw_index <= api_ok) + IWL_ERR(drv, + "request for firmware file '%s' failed.\n", + drv->firmware_name); + goto try_again; + } + + IWL_DEBUG_INFO(drv, "Loaded firmware file '%s' (%zd bytes).\n", + drv->firmware_name, ucode_raw->size); + + /* Make sure that we got at least the API version number */ + if (ucode_raw->size < 4) { + IWL_ERR(drv, "File size way too small!\n"); + goto try_again; + } + + /* Data from ucode file: header followed by uCode images */ + ucode = (struct iwl_ucode_header *)ucode_raw->data; + + if (ucode->ver) + err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces); + else + err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces, + &fw->ucode_capa); + + if (err) + goto try_again; + + if (fw_has_api(&drv->fw.ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION)) + api_ver = drv->fw.ucode_ver; + else + api_ver = IWL_UCODE_API(drv->fw.ucode_ver); + + /* + * api_ver should match the api version forming part of the + * firmware filename ... but we don't check for that and only rely + * on the API version read from firmware header from here on forward + */ + /* no api version check required for experimental uCode */ + if (drv->fw_index != UCODE_EXPERIMENTAL_INDEX) { + if (api_ver < api_min || api_ver > api_max) { + IWL_ERR(drv, + "Driver unable to support your firmware API. " + "Driver supports v%u, firmware is v%u.\n", + api_max, api_ver); + goto try_again; + } + + if (api_ver < api_ok) { + if (api_ok != api_max) + IWL_ERR(drv, "Firmware has old API version, " + "expected v%u through v%u, got v%u.\n", + api_ok, api_max, api_ver); + else + IWL_ERR(drv, "Firmware has old API version, " + "expected v%u, got v%u.\n", + api_max, api_ver); + IWL_ERR(drv, "New firmware can be obtained from " + "http://www.intellinuxwireless.org/.\n"); + } + } + + /* + * In mvm uCode there is no difference between data and instructions + * sections. + */ + if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg)) + goto try_again; + + /* Allocate ucode buffers for card's bus-master loading ... */ + + /* Runtime instructions and 2 copies of data: + * 1) unmodified from disk + * 2) backup cache for save/restore during power-downs */ + for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) + if (iwl_alloc_ucode(drv, pieces, i)) + goto out_free_fw; + + if (pieces->dbg_dest_tlv) { + drv->fw.dbg_dest_tlv = + kmemdup(pieces->dbg_dest_tlv, + sizeof(*pieces->dbg_dest_tlv) + + sizeof(pieces->dbg_dest_tlv->reg_ops[0]) * + drv->fw.dbg_dest_reg_num, GFP_KERNEL); + + if (!drv->fw.dbg_dest_tlv) + goto out_free_fw; + } + + for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) { + if (pieces->dbg_conf_tlv[i]) { + drv->fw.dbg_conf_tlv_len[i] = + pieces->dbg_conf_tlv_len[i]; + drv->fw.dbg_conf_tlv[i] = + kmemdup(pieces->dbg_conf_tlv[i], + drv->fw.dbg_conf_tlv_len[i], + GFP_KERNEL); + if (!drv->fw.dbg_conf_tlv[i]) + goto out_free_fw; + } + } + + memset(&trigger_tlv_sz, 0xff, sizeof(trigger_tlv_sz)); + + trigger_tlv_sz[FW_DBG_TRIGGER_MISSED_BEACONS] = + sizeof(struct iwl_fw_dbg_trigger_missed_bcon); + trigger_tlv_sz[FW_DBG_TRIGGER_CHANNEL_SWITCH] = 0; + trigger_tlv_sz[FW_DBG_TRIGGER_FW_NOTIF] = + sizeof(struct iwl_fw_dbg_trigger_cmd); + trigger_tlv_sz[FW_DBG_TRIGGER_MLME] = + sizeof(struct iwl_fw_dbg_trigger_mlme); + trigger_tlv_sz[FW_DBG_TRIGGER_STATS] = + sizeof(struct iwl_fw_dbg_trigger_stats); + trigger_tlv_sz[FW_DBG_TRIGGER_RSSI] = + sizeof(struct iwl_fw_dbg_trigger_low_rssi); + trigger_tlv_sz[FW_DBG_TRIGGER_TXQ_TIMERS] = + sizeof(struct iwl_fw_dbg_trigger_txq_timer); + trigger_tlv_sz[FW_DBG_TRIGGER_TIME_EVENT] = + sizeof(struct iwl_fw_dbg_trigger_time_event); + trigger_tlv_sz[FW_DBG_TRIGGER_BA] = + sizeof(struct iwl_fw_dbg_trigger_ba); + + for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) { + if (pieces->dbg_trigger_tlv[i]) { + /* + * If the trigger isn't long enough, WARN and exit. + * Someone is trying to debug something and he won't + * be able to catch the bug he is trying to chase. + * We'd better be noisy to be sure he knows what's + * going on. + */ + if (WARN_ON(pieces->dbg_trigger_tlv_len[i] < + (trigger_tlv_sz[i] + + sizeof(struct iwl_fw_dbg_trigger_tlv)))) + goto out_free_fw; + drv->fw.dbg_trigger_tlv_len[i] = + pieces->dbg_trigger_tlv_len[i]; + drv->fw.dbg_trigger_tlv[i] = + kmemdup(pieces->dbg_trigger_tlv[i], + drv->fw.dbg_trigger_tlv_len[i], + GFP_KERNEL); + if (!drv->fw.dbg_trigger_tlv[i]) + goto out_free_fw; + } + } + + /* Now that we can no longer fail, copy information */ + + /* + * The (size - 16) / 12 formula is based on the information recorded + * for each event, which is of mode 1 (including timestamp) for all + * new microcodes that include this information. + */ + fw->init_evtlog_ptr = pieces->init_evtlog_ptr; + if (pieces->init_evtlog_size) + fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12; + else + fw->init_evtlog_size = + drv->cfg->base_params->max_event_log_size; + fw->init_errlog_ptr = pieces->init_errlog_ptr; + fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr; + if (pieces->inst_evtlog_size) + fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12; + else + fw->inst_evtlog_size = + drv->cfg->base_params->max_event_log_size; + fw->inst_errlog_ptr = pieces->inst_errlog_ptr; + + /* + * figure out the offset of chain noise reset and gain commands + * base on the size of standard phy calibration commands table size + */ + if (fw->ucode_capa.standard_phy_calibration_size > + IWL_MAX_PHY_CALIBRATE_TBL_SIZE) + fw->ucode_capa.standard_phy_calibration_size = + IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE; + + /* We have our copies now, allow OS release its copies */ + release_firmware(ucode_raw); + + mutex_lock(&iwlwifi_opmode_table_mtx); + if (fw->mvm_fw) + op = &iwlwifi_opmode_table[MVM_OP_MODE]; + else + op = &iwlwifi_opmode_table[DVM_OP_MODE]; + + IWL_INFO(drv, "loaded firmware version %s op_mode %s\n", + drv->fw.fw_version, op->name); + + /* add this device to the list of devices using this op_mode */ + list_add_tail(&drv->list, &op->drv); + + if (op->ops) { + drv->op_mode = _iwl_op_mode_start(drv, op); + + if (!drv->op_mode) { + mutex_unlock(&iwlwifi_opmode_table_mtx); + goto out_unbind; + } + } else { + load_module = true; + } + mutex_unlock(&iwlwifi_opmode_table_mtx); + + /* + * Complete the firmware request last so that + * a driver unbind (stop) doesn't run while we + * are doing the start() above. + */ + complete(&drv->request_firmware_complete); + + /* + * Load the module last so we don't block anything + * else from proceeding if the module fails to load + * or hangs loading. + */ + if (load_module) { + err = request_module("%s", op->name); +#ifdef CONFIG_IWLWIFI_OPMODE_MODULAR + if (err) + IWL_ERR(drv, + "failed to load module %s (error %d), is dynamic loading enabled?\n", + op->name, err); +#endif + } + kfree(pieces); + return; + + try_again: + /* try next, if any */ + release_firmware(ucode_raw); + if (iwl_request_firmware(drv, false)) + goto out_unbind; + kfree(pieces); + return; + + out_free_fw: + IWL_ERR(drv, "failed to allocate pci memory\n"); + iwl_dealloc_ucode(drv); + release_firmware(ucode_raw); + out_unbind: + kfree(pieces); + complete(&drv->request_firmware_complete); + device_release_driver(drv->trans->dev); +} + +struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, + const struct iwl_cfg *cfg) +{ + struct iwl_drv *drv; + int ret; + + drv = kzalloc(sizeof(*drv), GFP_KERNEL); + if (!drv) { + ret = -ENOMEM; + goto err; + } + + drv->trans = trans; + drv->dev = trans->dev; + drv->cfg = cfg; + + init_completion(&drv->request_firmware_complete); + INIT_LIST_HEAD(&drv->list); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* Create the device debugfs entries. */ + drv->dbgfs_drv = debugfs_create_dir(dev_name(trans->dev), + iwl_dbgfs_root); + + if (!drv->dbgfs_drv) { + IWL_ERR(drv, "failed to create debugfs directory\n"); + ret = -ENOMEM; + goto err_free_drv; + } + + /* Create transport layer debugfs dir */ + drv->trans->dbgfs_dir = debugfs_create_dir("trans", drv->dbgfs_drv); + + if (!drv->trans->dbgfs_dir) { + IWL_ERR(drv, "failed to create transport debugfs directory\n"); + ret = -ENOMEM; + goto err_free_dbgfs; + } +#endif + + ret = iwl_request_firmware(drv, true); + if (ret) { + IWL_ERR(trans, "Couldn't request the fw\n"); + goto err_fw; + } + + return drv; + +err_fw: +#ifdef CONFIG_IWLWIFI_DEBUGFS +err_free_dbgfs: + debugfs_remove_recursive(drv->dbgfs_drv); +err_free_drv: +#endif + kfree(drv); +err: + return ERR_PTR(ret); +} + +void iwl_drv_stop(struct iwl_drv *drv) +{ + wait_for_completion(&drv->request_firmware_complete); + + _iwl_op_mode_stop(drv); + + iwl_dealloc_ucode(drv); + + mutex_lock(&iwlwifi_opmode_table_mtx); + /* + * List is empty (this item wasn't added) + * when firmware loading failed -- in that + * case we can't remove it from any list. + */ + if (!list_empty(&drv->list)) + list_del(&drv->list); + mutex_unlock(&iwlwifi_opmode_table_mtx); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + debugfs_remove_recursive(drv->dbgfs_drv); +#endif + + kfree(drv); +} + + +/* shared module parameters */ +struct iwl_mod_params iwlwifi_mod_params = { + .restart_fw = true, + .bt_coex_active = true, + .power_level = IWL_POWER_INDEX_1, + .d0i3_disable = true, +#ifndef CONFIG_IWLWIFI_UAPSD + .uapsd_disable = true, +#endif /* CONFIG_IWLWIFI_UAPSD */ + /* the rest are 0 by default */ +}; +IWL_EXPORT_SYMBOL(iwlwifi_mod_params); + +int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops) +{ + int i; + struct iwl_drv *drv; + struct iwlwifi_opmode_table *op; + + mutex_lock(&iwlwifi_opmode_table_mtx); + for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { + op = &iwlwifi_opmode_table[i]; + if (strcmp(op->name, name)) + continue; + op->ops = ops; + /* TODO: need to handle exceptional case */ + list_for_each_entry(drv, &op->drv, list) + drv->op_mode = _iwl_op_mode_start(drv, op); + + mutex_unlock(&iwlwifi_opmode_table_mtx); + return 0; + } + mutex_unlock(&iwlwifi_opmode_table_mtx); + return -EIO; +} +IWL_EXPORT_SYMBOL(iwl_opmode_register); + +void iwl_opmode_deregister(const char *name) +{ + int i; + struct iwl_drv *drv; + + mutex_lock(&iwlwifi_opmode_table_mtx); + for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { + if (strcmp(iwlwifi_opmode_table[i].name, name)) + continue; + iwlwifi_opmode_table[i].ops = NULL; + + /* call the stop routine for all devices */ + list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list) + _iwl_op_mode_stop(drv); + + mutex_unlock(&iwlwifi_opmode_table_mtx); + return; + } + mutex_unlock(&iwlwifi_opmode_table_mtx); +} +IWL_EXPORT_SYMBOL(iwl_opmode_deregister); + +static int __init iwl_drv_init(void) +{ + int i; + + mutex_init(&iwlwifi_opmode_table_mtx); + + for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) + INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv); + + pr_info(DRV_DESCRIPTION "\n"); + pr_info(DRV_COPYRIGHT "\n"); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* Create the root of iwlwifi debugfs subsystem. */ + iwl_dbgfs_root = debugfs_create_dir(DRV_NAME, NULL); + + if (!iwl_dbgfs_root) + return -EFAULT; +#endif + + return iwl_pci_register_driver(); +} +module_init(iwl_drv_init); + +static void __exit iwl_drv_exit(void) +{ + iwl_pci_unregister_driver(); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + debugfs_remove_recursive(iwl_dbgfs_root); +#endif +} +module_exit(iwl_drv_exit); + +#ifdef CONFIG_IWLWIFI_DEBUG +module_param_named(debug, iwlwifi_mod_params.debug_level, uint, + S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "debug output mask"); +#endif + +module_param_named(swcrypto, iwlwifi_mod_params.sw_crypto, int, S_IRUGO); +MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); +module_param_named(11n_disable, iwlwifi_mod_params.disable_11n, uint, S_IRUGO); +MODULE_PARM_DESC(11n_disable, + "disable 11n functionality, bitmap: 1: full, 2: disable agg TX, 4: disable agg RX, 8 enable agg TX"); +module_param_named(amsdu_size_8K, iwlwifi_mod_params.amsdu_size_8K, + int, S_IRUGO); +MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0)"); +module_param_named(fw_restart, iwlwifi_mod_params.restart_fw, bool, S_IRUGO); +MODULE_PARM_DESC(fw_restart, "restart firmware in case of error (default true)"); + +module_param_named(antenna_coupling, iwlwifi_mod_params.ant_coupling, + int, S_IRUGO); +MODULE_PARM_DESC(antenna_coupling, + "specify antenna coupling in dB (default: 0 dB)"); + +module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO); +MODULE_PARM_DESC(nvm_file, "NVM file name"); + +module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable, + bool, S_IRUGO); +MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)"); + +module_param_named(lar_disable, iwlwifi_mod_params.lar_disable, + bool, S_IRUGO); +MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)"); + +module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, + bool, S_IRUGO | S_IWUSR); +#ifdef CONFIG_IWLWIFI_UAPSD +MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)"); +#else +MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: Y)"); +#endif + +/* + * set bt_coex_active to true, uCode will do kill/defer + * every time the priority line is asserted (BT is sending signals on the + * priority line in the PCIx). + * set bt_coex_active to false, uCode will ignore the BT activity and + * perform the normal operation + * + * User might experience transmit issue on some platform due to WiFi/BT + * co-exist problem. The possible behaviors are: + * Able to scan and finding all the available AP + * Not able to associate with any AP + * On those platforms, WiFi communication can be restored by set + * "bt_coex_active" module parameter to "false" + * + * default: bt_coex_active = true (BT_COEX_ENABLE) + */ +module_param_named(bt_coex_active, iwlwifi_mod_params.bt_coex_active, + bool, S_IRUGO); +MODULE_PARM_DESC(bt_coex_active, "enable wifi/bt co-exist (default: enable)"); + +module_param_named(led_mode, iwlwifi_mod_params.led_mode, int, S_IRUGO); +MODULE_PARM_DESC(led_mode, "0=system default, " + "1=On(RF On)/Off(RF Off), 2=blinking, 3=Off (default: 0)"); + +module_param_named(power_save, iwlwifi_mod_params.power_save, + bool, S_IRUGO); +MODULE_PARM_DESC(power_save, + "enable WiFi power management (default: disable)"); + +module_param_named(power_level, iwlwifi_mod_params.power_level, + int, S_IRUGO); +MODULE_PARM_DESC(power_level, + "default power save level (range from 1 - 5, default: 1)"); + +module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO); +MODULE_PARM_DESC(fw_monitor, + "firmware monitor - to debug FW (default: false - needs lots of memory)"); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h new file mode 100644 index 000000000000..cda746b33db1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h @@ -0,0 +1,155 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_drv_h__ +#define __iwl_drv_h__ +#include + +/* for all modules */ +#define DRV_NAME "iwlwifi" +#define DRV_COPYRIGHT "Copyright(c) 2003- 2015 Intel Corporation" +#define DRV_AUTHOR "" + +/* radio config bits (actual values from NVM definition) */ +#define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ +#define NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ +#define NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ +#define NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ +#define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ +#define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ + +#define NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(x) (x & 0xF) +#define NVM_RF_CFG_DASH_MSK_FAMILY_8000(x) ((x >> 4) & 0xF) +#define NVM_RF_CFG_STEP_MSK_FAMILY_8000(x) ((x >> 8) & 0xF) +#define NVM_RF_CFG_TYPE_MSK_FAMILY_8000(x) ((x >> 12) & 0xFFF) +#define NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(x) ((x >> 24) & 0xF) +#define NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(x) ((x >> 28) & 0xF) + +/** + * DOC: Driver system flows - drv component + * + * This component implements the system flows such as bus enumeration, bus + * removal. Bus dependent parts of system flows (such as iwl_pci_probe) are in + * bus specific files (transport files). This is the code that is common among + * different buses. + * + * This component is also in charge of managing the several implementations of + * the wifi flows: it will allow to have several fw API implementation. These + * different implementations will differ in the way they implement mac80211's + * handlers too. + + * The init flow wrt to the drv component looks like this: + * 1) The bus specific component is called from module_init + * 2) The bus specific component registers the bus driver + * 3) The bus driver calls the probe function + * 4) The bus specific component configures the bus + * 5) The bus specific component calls to the drv bus agnostic part + * (iwl_drv_start) + * 6) iwl_drv_start fetches the fw ASYNC, iwl_req_fw_callback + * 7) iwl_req_fw_callback parses the fw file + * 8) iwl_req_fw_callback starts the wifi implementation to matches the fw + */ + +struct iwl_drv; +struct iwl_trans; +struct iwl_cfg; +/** + * iwl_drv_start - start the drv + * + * @trans_ops: the ops of the transport + * @cfg: device specific constants / virtual functions + * + * starts the driver: fetches the firmware. This should be called by bus + * specific system flows implementations. For example, the bus specific probe + * function should do bus related operations only, and then call to this + * function. It returns the driver object or %NULL if an error occurred. + */ +struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, + const struct iwl_cfg *cfg); + +/** + * iwl_drv_stop - stop the drv + * + * @drv: + * + * Stop the driver. This should be called by bus specific system flows + * implementations. For example, the bus specific remove function should first + * call this function and then do the bus related operations only. + */ +void iwl_drv_stop(struct iwl_drv *drv); + +/* + * exported symbol management + * + * The driver can be split into multiple modules, in which case some symbols + * must be exported for the sub-modules. However, if it's not split and + * everything is built-in, then we can avoid that. + */ +#ifdef CONFIG_IWLWIFI_OPMODE_MODULAR +#define IWL_EXPORT_SYMBOL(sym) EXPORT_SYMBOL_GPL(sym) +#else +#define IWL_EXPORT_SYMBOL(sym) +#endif + +#endif /* __iwl_drv_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c new file mode 100644 index 000000000000..acc3d186c5c1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c @@ -0,0 +1,947 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#include +#include +#include +#include "iwl-drv.h" +#include "iwl-modparams.h" +#include "iwl-eeprom-parse.h" + +/* EEPROM offset definitions */ + +/* indirect access definitions */ +#define ADDRESS_MSK 0x0000FFFF +#define INDIRECT_TYPE_MSK 0x000F0000 +#define INDIRECT_HOST 0x00010000 +#define INDIRECT_GENERAL 0x00020000 +#define INDIRECT_REGULATORY 0x00030000 +#define INDIRECT_CALIBRATION 0x00040000 +#define INDIRECT_PROCESS_ADJST 0x00050000 +#define INDIRECT_OTHERS 0x00060000 +#define INDIRECT_TXP_LIMIT 0x00070000 +#define INDIRECT_TXP_LIMIT_SIZE 0x00080000 +#define INDIRECT_ADDRESS 0x00100000 + +/* corresponding link offsets in EEPROM */ +#define EEPROM_LINK_HOST (2*0x64) +#define EEPROM_LINK_GENERAL (2*0x65) +#define EEPROM_LINK_REGULATORY (2*0x66) +#define EEPROM_LINK_CALIBRATION (2*0x67) +#define EEPROM_LINK_PROCESS_ADJST (2*0x68) +#define EEPROM_LINK_OTHERS (2*0x69) +#define EEPROM_LINK_TXP_LIMIT (2*0x6a) +#define EEPROM_LINK_TXP_LIMIT_SIZE (2*0x6b) + +/* General */ +#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ +#define EEPROM_SUBSYSTEM_ID (2*0x0A) /* 2 bytes */ +#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ +#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ +#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ +#define EEPROM_VERSION (2*0x44) /* 2 bytes */ +#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ +#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ +#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ +#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ + +/* calibration */ +struct iwl_eeprom_calib_hdr { + u8 version; + u8 pa_type; + __le16 voltage; +} __packed; + +#define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION) +#define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL) + +/* temperature */ +#define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL) +#define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL) + +/* SKU Capabilities (actual values from EEPROM definition) */ +enum eeprom_sku_bits { + EEPROM_SKU_CAP_BAND_24GHZ = BIT(4), + EEPROM_SKU_CAP_BAND_52GHZ = BIT(5), + EEPROM_SKU_CAP_11N_ENABLE = BIT(6), + EEPROM_SKU_CAP_AMT_ENABLE = BIT(7), + EEPROM_SKU_CAP_IPAN_ENABLE = BIT(8) +}; + +/* radio config bits (actual values from EEPROM definition) */ +#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ +#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ +#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ +#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ +#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ +#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ + + +/* + * EEPROM bands + * These are the channel numbers from each band in the order + * that they are stored in the EEPROM band information. Note + * that EEPROM bands aren't the same as mac80211 bands, and + * there are even special "ht40 bands" in the EEPROM. + */ +static const u8 iwl_eeprom_band_1[14] = { /* 2.4 GHz */ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 +}; + +static const u8 iwl_eeprom_band_2[] = { /* 4915-5080MHz */ + 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 +}; + +static const u8 iwl_eeprom_band_3[] = { /* 5170-5320MHz */ + 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 +}; + +static const u8 iwl_eeprom_band_4[] = { /* 5500-5700MHz */ + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 +}; + +static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */ + 145, 149, 153, 157, 161, 165 +}; + +static const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */ + 1, 2, 3, 4, 5, 6, 7 +}; + +static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ + 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 +}; + +#define IWL_NUM_CHANNELS (ARRAY_SIZE(iwl_eeprom_band_1) + \ + ARRAY_SIZE(iwl_eeprom_band_2) + \ + ARRAY_SIZE(iwl_eeprom_band_3) + \ + ARRAY_SIZE(iwl_eeprom_band_4) + \ + ARRAY_SIZE(iwl_eeprom_band_5)) + +/* rate data (static) */ +static struct ieee80211_rate iwl_cfg80211_rates[] = { + { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, }, + { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1, + .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, + { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2, + .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, + { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3, + .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, + { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, }, + { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, }, + { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, }, + { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, }, + { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, }, + { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, }, + { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, }, + { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, }, +}; +#define RATES_24_OFFS 0 +#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates) +#define RATES_52_OFFS 4 +#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS) + +/* EEPROM reading functions */ + +static u16 iwl_eeprom_query16(const u8 *eeprom, size_t eeprom_size, int offset) +{ + if (WARN_ON(offset + sizeof(u16) > eeprom_size)) + return 0; + return le16_to_cpup((__le16 *)(eeprom + offset)); +} + +static u32 eeprom_indirect_address(const u8 *eeprom, size_t eeprom_size, + u32 address) +{ + u16 offset = 0; + + if ((address & INDIRECT_ADDRESS) == 0) + return address; + + switch (address & INDIRECT_TYPE_MSK) { + case INDIRECT_HOST: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_HOST); + break; + case INDIRECT_GENERAL: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_GENERAL); + break; + case INDIRECT_REGULATORY: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_REGULATORY); + break; + case INDIRECT_TXP_LIMIT: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_TXP_LIMIT); + break; + case INDIRECT_TXP_LIMIT_SIZE: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_TXP_LIMIT_SIZE); + break; + case INDIRECT_CALIBRATION: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_CALIBRATION); + break; + case INDIRECT_PROCESS_ADJST: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_PROCESS_ADJST); + break; + case INDIRECT_OTHERS: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_OTHERS); + break; + default: + WARN_ON(1); + break; + } + + /* translate the offset from words to byte */ + return (address & ADDRESS_MSK) + (offset << 1); +} + +static const u8 *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size, + u32 offset) +{ + u32 address = eeprom_indirect_address(eeprom, eeprom_size, offset); + + if (WARN_ON(address >= eeprom_size)) + return NULL; + + return &eeprom[address]; +} + +static int iwl_eeprom_read_calib(const u8 *eeprom, size_t eeprom_size, + struct iwl_nvm_data *data) +{ + struct iwl_eeprom_calib_hdr *hdr; + + hdr = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, + EEPROM_CALIB_ALL); + if (!hdr) + return -ENODATA; + data->calib_version = hdr->version; + data->calib_voltage = hdr->voltage; + + return 0; +} + +/** + * enum iwl_eeprom_channel_flags - channel flags in EEPROM + * @EEPROM_CHANNEL_VALID: channel is usable for this SKU/geo + * @EEPROM_CHANNEL_IBSS: usable as an IBSS channel + * @EEPROM_CHANNEL_ACTIVE: active scanning allowed + * @EEPROM_CHANNEL_RADAR: radar detection required + * @EEPROM_CHANNEL_WIDE: 20 MHz channel okay (?) + * @EEPROM_CHANNEL_DFS: dynamic freq selection candidate + */ +enum iwl_eeprom_channel_flags { + EEPROM_CHANNEL_VALID = BIT(0), + EEPROM_CHANNEL_IBSS = BIT(1), + EEPROM_CHANNEL_ACTIVE = BIT(3), + EEPROM_CHANNEL_RADAR = BIT(4), + EEPROM_CHANNEL_WIDE = BIT(5), + EEPROM_CHANNEL_DFS = BIT(7), +}; + +/** + * struct iwl_eeprom_channel - EEPROM channel data + * @flags: %EEPROM_CHANNEL_* flags + * @max_power_avg: max power (in dBm) on this channel, at most 31 dBm + */ +struct iwl_eeprom_channel { + u8 flags; + s8 max_power_avg; +} __packed; + + +enum iwl_eeprom_enhanced_txpwr_flags { + IWL_EEPROM_ENH_TXP_FL_VALID = BIT(0), + IWL_EEPROM_ENH_TXP_FL_BAND_52G = BIT(1), + IWL_EEPROM_ENH_TXP_FL_OFDM = BIT(2), + IWL_EEPROM_ENH_TXP_FL_40MHZ = BIT(3), + IWL_EEPROM_ENH_TXP_FL_HT_AP = BIT(4), + IWL_EEPROM_ENH_TXP_FL_RES1 = BIT(5), + IWL_EEPROM_ENH_TXP_FL_RES2 = BIT(6), + IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE = BIT(7), +}; + +/** + * iwl_eeprom_enhanced_txpwr structure + * @flags: entry flags + * @channel: channel number + * @chain_a_max_pwr: chain a max power in 1/2 dBm + * @chain_b_max_pwr: chain b max power in 1/2 dBm + * @chain_c_max_pwr: chain c max power in 1/2 dBm + * @delta_20_in_40: 20-in-40 deltas (hi/lo) + * @mimo2_max_pwr: mimo2 max power in 1/2 dBm + * @mimo3_max_pwr: mimo3 max power in 1/2 dBm + * + * This structure presents the enhanced regulatory tx power limit layout + * in an EEPROM image. + */ +struct iwl_eeprom_enhanced_txpwr { + u8 flags; + u8 channel; + s8 chain_a_max; + s8 chain_b_max; + s8 chain_c_max; + u8 delta_20_in_40; + s8 mimo2_max; + s8 mimo3_max; +} __packed; + +static s8 iwl_get_max_txpwr_half_dbm(const struct iwl_nvm_data *data, + struct iwl_eeprom_enhanced_txpwr *txp) +{ + s8 result = 0; /* (.5 dBm) */ + + /* Take the highest tx power from any valid chains */ + if (data->valid_tx_ant & ANT_A && txp->chain_a_max > result) + result = txp->chain_a_max; + + if (data->valid_tx_ant & ANT_B && txp->chain_b_max > result) + result = txp->chain_b_max; + + if (data->valid_tx_ant & ANT_C && txp->chain_c_max > result) + result = txp->chain_c_max; + + if ((data->valid_tx_ant == ANT_AB || + data->valid_tx_ant == ANT_BC || + data->valid_tx_ant == ANT_AC) && txp->mimo2_max > result) + result = txp->mimo2_max; + + if (data->valid_tx_ant == ANT_ABC && txp->mimo3_max > result) + result = txp->mimo3_max; + + return result; +} + +#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT) +#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr) +#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE) + +#define TXP_CHECK_AND_PRINT(x) \ + ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) ? # x " " : "") + +static void +iwl_eeprom_enh_txp_read_element(struct iwl_nvm_data *data, + struct iwl_eeprom_enhanced_txpwr *txp, + int n_channels, s8 max_txpower_avg) +{ + int ch_idx; + enum ieee80211_band band; + + band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + + for (ch_idx = 0; ch_idx < n_channels; ch_idx++) { + struct ieee80211_channel *chan = &data->channels[ch_idx]; + + /* update matching channel or from common data only */ + if (txp->channel != 0 && chan->hw_value != txp->channel) + continue; + + /* update matching band only */ + if (band != chan->band) + continue; + + if (chan->max_power < max_txpower_avg && + !(txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ)) + chan->max_power = max_txpower_avg; + } +} + +static void iwl_eeprom_enhanced_txpower(struct device *dev, + struct iwl_nvm_data *data, + const u8 *eeprom, size_t eeprom_size, + int n_channels) +{ + struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; + int idx, entries; + __le16 *txp_len; + s8 max_txp_avg_halfdbm; + + BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); + + /* the length is in 16-bit words, but we want entries */ + txp_len = (__le16 *)iwl_eeprom_query_addr(eeprom, eeprom_size, + EEPROM_TXP_SZ_OFFS); + entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; + + txp_array = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, + EEPROM_TXP_OFFS); + + for (idx = 0; idx < entries; idx++) { + txp = &txp_array[idx]; + /* skip invalid entries */ + if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID)) + continue; + + IWL_DEBUG_EEPROM(dev, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n", + (txp->channel && (txp->flags & + IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ? + "Common " : (txp->channel) ? + "Channel" : "Common", + (txp->channel), + TXP_CHECK_AND_PRINT(VALID), + TXP_CHECK_AND_PRINT(BAND_52G), + TXP_CHECK_AND_PRINT(OFDM), + TXP_CHECK_AND_PRINT(40MHZ), + TXP_CHECK_AND_PRINT(HT_AP), + TXP_CHECK_AND_PRINT(RES1), + TXP_CHECK_AND_PRINT(RES2), + TXP_CHECK_AND_PRINT(COMMON_TYPE), + txp->flags); + IWL_DEBUG_EEPROM(dev, + "\t\t chain_A: 0x%02x chain_B: 0X%02x chain_C: 0X%02x\n", + txp->chain_a_max, txp->chain_b_max, + txp->chain_c_max); + IWL_DEBUG_EEPROM(dev, + "\t\t MIMO2: 0x%02x MIMO3: 0x%02x High 20_on_40: 0x%02x Low 20_on_40: 0x%02x\n", + txp->mimo2_max, txp->mimo3_max, + ((txp->delta_20_in_40 & 0xf0) >> 4), + (txp->delta_20_in_40 & 0x0f)); + + max_txp_avg_halfdbm = iwl_get_max_txpwr_half_dbm(data, txp); + + iwl_eeprom_enh_txp_read_element(data, txp, n_channels, + DIV_ROUND_UP(max_txp_avg_halfdbm, 2)); + + if (max_txp_avg_halfdbm > data->max_tx_pwr_half_dbm) + data->max_tx_pwr_half_dbm = max_txp_avg_halfdbm; + } +} + +static void iwl_init_band_reference(const struct iwl_cfg *cfg, + const u8 *eeprom, size_t eeprom_size, + int eeprom_band, int *eeprom_ch_count, + const struct iwl_eeprom_channel **ch_info, + const u8 **eeprom_ch_array) +{ + u32 offset = cfg->eeprom_params->regulatory_bands[eeprom_band - 1]; + + offset |= INDIRECT_ADDRESS | INDIRECT_REGULATORY; + + *ch_info = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, offset); + + switch (eeprom_band) { + case 1: /* 2.4GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1); + *eeprom_ch_array = iwl_eeprom_band_1; + break; + case 2: /* 4.9GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2); + *eeprom_ch_array = iwl_eeprom_band_2; + break; + case 3: /* 5.2GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3); + *eeprom_ch_array = iwl_eeprom_band_3; + break; + case 4: /* 5.5GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4); + *eeprom_ch_array = iwl_eeprom_band_4; + break; + case 5: /* 5.7GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5); + *eeprom_ch_array = iwl_eeprom_band_5; + break; + case 6: /* 2.4GHz ht40 channels */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6); + *eeprom_ch_array = iwl_eeprom_band_6; + break; + case 7: /* 5 GHz ht40 channels */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7); + *eeprom_ch_array = iwl_eeprom_band_7; + break; + default: + *eeprom_ch_count = 0; + *eeprom_ch_array = NULL; + WARN_ON(1); + } +} + +#define CHECK_AND_PRINT(x) \ + ((eeprom_ch->flags & EEPROM_CHANNEL_##x) ? # x " " : "") + +static void iwl_mod_ht40_chan_info(struct device *dev, + struct iwl_nvm_data *data, int n_channels, + enum ieee80211_band band, u16 channel, + const struct iwl_eeprom_channel *eeprom_ch, + u8 clear_ht40_extension_channel) +{ + struct ieee80211_channel *chan = NULL; + int i; + + for (i = 0; i < n_channels; i++) { + if (data->channels[i].band != band) + continue; + if (data->channels[i].hw_value != channel) + continue; + chan = &data->channels[i]; + break; + } + + if (!chan) + return; + + IWL_DEBUG_EEPROM(dev, + "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", + channel, + band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4", + CHECK_AND_PRINT(IBSS), + CHECK_AND_PRINT(ACTIVE), + CHECK_AND_PRINT(RADAR), + CHECK_AND_PRINT(WIDE), + CHECK_AND_PRINT(DFS), + eeprom_ch->flags, + eeprom_ch->max_power_avg, + ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) && + !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" + : "not "); + + if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) + chan->flags &= ~clear_ht40_extension_channel; +} + +#define CHECK_AND_PRINT_I(x) \ + ((eeprom_ch_info[ch_idx].flags & EEPROM_CHANNEL_##x) ? # x " " : "") + +static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + const u8 *eeprom, size_t eeprom_size) +{ + int band, ch_idx; + const struct iwl_eeprom_channel *eeprom_ch_info; + const u8 *eeprom_ch_array; + int eeprom_ch_count; + int n_channels = 0; + + /* + * Loop through the 5 EEPROM bands and add them to the parse list + */ + for (band = 1; band <= 5; band++) { + struct ieee80211_channel *channel; + + iwl_init_band_reference(cfg, eeprom, eeprom_size, band, + &eeprom_ch_count, &eeprom_ch_info, + &eeprom_ch_array); + + /* Loop through each band adding each of the channels */ + for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) { + const struct iwl_eeprom_channel *eeprom_ch; + + eeprom_ch = &eeprom_ch_info[ch_idx]; + + if (!(eeprom_ch->flags & EEPROM_CHANNEL_VALID)) { + IWL_DEBUG_EEPROM(dev, + "Ch. %d Flags %x [%sGHz] - No traffic\n", + eeprom_ch_array[ch_idx], + eeprom_ch_info[ch_idx].flags, + (band != 1) ? "5.2" : "2.4"); + continue; + } + + channel = &data->channels[n_channels]; + n_channels++; + + channel->hw_value = eeprom_ch_array[ch_idx]; + channel->band = (band == 1) ? IEEE80211_BAND_2GHZ + : IEEE80211_BAND_5GHZ; + channel->center_freq = + ieee80211_channel_to_frequency( + channel->hw_value, channel->band); + + /* set no-HT40, will enable as appropriate later */ + channel->flags = IEEE80211_CHAN_NO_HT40; + + if (!(eeprom_ch->flags & EEPROM_CHANNEL_IBSS)) + channel->flags |= IEEE80211_CHAN_NO_IR; + + if (!(eeprom_ch->flags & EEPROM_CHANNEL_ACTIVE)) + channel->flags |= IEEE80211_CHAN_NO_IR; + + if (eeprom_ch->flags & EEPROM_CHANNEL_RADAR) + channel->flags |= IEEE80211_CHAN_RADAR; + + /* Initialize regulatory-based run-time data */ + channel->max_power = + eeprom_ch_info[ch_idx].max_power_avg; + IWL_DEBUG_EEPROM(dev, + "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", + channel->hw_value, + (band != 1) ? "5.2" : "2.4", + CHECK_AND_PRINT_I(VALID), + CHECK_AND_PRINT_I(IBSS), + CHECK_AND_PRINT_I(ACTIVE), + CHECK_AND_PRINT_I(RADAR), + CHECK_AND_PRINT_I(WIDE), + CHECK_AND_PRINT_I(DFS), + eeprom_ch_info[ch_idx].flags, + eeprom_ch_info[ch_idx].max_power_avg, + ((eeprom_ch_info[ch_idx].flags & + EEPROM_CHANNEL_IBSS) && + !(eeprom_ch_info[ch_idx].flags & + EEPROM_CHANNEL_RADAR)) + ? "" : "not "); + } + } + + if (cfg->eeprom_params->enhanced_txpower) { + /* + * for newer device (6000 series and up) + * EEPROM contain enhanced tx power information + * driver need to process addition information + * to determine the max channel tx power limits + */ + iwl_eeprom_enhanced_txpower(dev, data, eeprom, eeprom_size, + n_channels); + } else { + /* All others use data from channel map */ + int i; + + data->max_tx_pwr_half_dbm = -128; + + for (i = 0; i < n_channels; i++) + data->max_tx_pwr_half_dbm = + max_t(s8, data->max_tx_pwr_half_dbm, + data->channels[i].max_power * 2); + } + + /* Check if we do have HT40 channels */ + if (cfg->eeprom_params->regulatory_bands[5] == + EEPROM_REGULATORY_BAND_NO_HT40 && + cfg->eeprom_params->regulatory_bands[6] == + EEPROM_REGULATORY_BAND_NO_HT40) + return n_channels; + + /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ + for (band = 6; band <= 7; band++) { + enum ieee80211_band ieeeband; + + iwl_init_band_reference(cfg, eeprom, eeprom_size, band, + &eeprom_ch_count, &eeprom_ch_info, + &eeprom_ch_array); + + /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ + ieeeband = (band == 6) ? IEEE80211_BAND_2GHZ + : IEEE80211_BAND_5GHZ; + + /* Loop through each band adding each of the channels */ + for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) { + /* Set up driver's info for lower half */ + iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband, + eeprom_ch_array[ch_idx], + &eeprom_ch_info[ch_idx], + IEEE80211_CHAN_NO_HT40PLUS); + + /* Set up driver's info for upper half */ + iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband, + eeprom_ch_array[ch_idx] + 4, + &eeprom_ch_info[ch_idx], + IEEE80211_CHAN_NO_HT40MINUS); + } + } + + return n_channels; +} + +int iwl_init_sband_channels(struct iwl_nvm_data *data, + struct ieee80211_supported_band *sband, + int n_channels, enum ieee80211_band band) +{ + struct ieee80211_channel *chan = &data->channels[0]; + int n = 0, idx = 0; + + while (idx < n_channels && chan->band != band) + chan = &data->channels[++idx]; + + sband->channels = &data->channels[idx]; + + while (idx < n_channels && chan->band == band) { + chan = &data->channels[++idx]; + n++; + } + + sband->n_channels = n; + + return n; +} + +#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ +#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ + +void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + struct ieee80211_sta_ht_cap *ht_info, + enum ieee80211_band band, + u8 tx_chains, u8 rx_chains) +{ + int max_bit_rate = 0; + + tx_chains = hweight8(tx_chains); + if (cfg->rx_with_siso_diversity) + rx_chains = 1; + else + rx_chains = hweight8(rx_chains); + + if (!(data->sku_cap_11n_enable) || !cfg->ht_params) { + ht_info->ht_supported = false; + return; + } + + if (data->sku_cap_mimo_disabled) + rx_chains = 1; + + ht_info->ht_supported = true; + ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40; + + if (cfg->ht_params->stbc) { + ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); + + if (tx_chains > 1) + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + } + + if (cfg->ht_params->ldpc) + ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; + + if (iwlwifi_mod_params.amsdu_size_8K) + ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; + + ht_info->ampdu_factor = cfg->max_ht_ampdu_exponent; + ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4; + + ht_info->mcs.rx_mask[0] = 0xFF; + if (rx_chains >= 2) + ht_info->mcs.rx_mask[1] = 0xFF; + if (rx_chains >= 3) + ht_info->mcs.rx_mask[2] = 0xFF; + + if (cfg->ht_params->ht_greenfield_support) + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + + max_bit_rate = MAX_BIT_RATE_20_MHZ; + + if (cfg->ht_params->ht40_bands & BIT(band)) { + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + max_bit_rate = MAX_BIT_RATE_40_MHZ; + } + + /* Highest supported Rx data rate */ + max_bit_rate *= rx_chains; + WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); + ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); + + /* Tx MCS capabilities */ + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + if (tx_chains != rx_chains) { + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + ht_info->mcs.tx_params |= ((tx_chains - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + } +} + +static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + const u8 *eeprom, size_t eeprom_size) +{ + int n_channels = iwl_init_channel_map(dev, cfg, data, + eeprom, eeprom_size); + int n_used = 0; + struct ieee80211_supported_band *sband; + + sband = &data->bands[IEEE80211_BAND_2GHZ]; + sband->band = IEEE80211_BAND_2GHZ; + sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; + sband->n_bitrates = N_RATES_24; + n_used += iwl_init_sband_channels(data, sband, n_channels, + IEEE80211_BAND_2GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ, + data->valid_tx_ant, data->valid_rx_ant); + + sband = &data->bands[IEEE80211_BAND_5GHZ]; + sband->band = IEEE80211_BAND_5GHZ; + sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; + sband->n_bitrates = N_RATES_52; + n_used += iwl_init_sband_channels(data, sband, n_channels, + IEEE80211_BAND_5GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, + data->valid_tx_ant, data->valid_rx_ant); + + if (n_channels != n_used) + IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n", + n_used, n_channels); +} + +/* EEPROM data functions */ + +struct iwl_nvm_data * +iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg, + const u8 *eeprom, size_t eeprom_size) +{ + struct iwl_nvm_data *data; + const void *tmp; + u16 radio_cfg, sku; + + if (WARN_ON(!cfg || !cfg->eeprom_params)) + return NULL; + + data = kzalloc(sizeof(*data) + + sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS, + GFP_KERNEL); + if (!data) + return NULL; + + /* get MAC address(es) */ + tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_MAC_ADDRESS); + if (!tmp) + goto err_free; + memcpy(data->hw_addr, tmp, ETH_ALEN); + data->n_hw_addrs = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_NUM_MAC_ADDRESS); + + if (iwl_eeprom_read_calib(eeprom, eeprom_size, data)) + goto err_free; + + tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_XTAL); + if (!tmp) + goto err_free; + memcpy(data->xtal_calib, tmp, sizeof(data->xtal_calib)); + + tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, + EEPROM_RAW_TEMPERATURE); + if (!tmp) + goto err_free; + data->raw_temperature = *(__le16 *)tmp; + + tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, + EEPROM_KELVIN_TEMPERATURE); + if (!tmp) + goto err_free; + data->kelvin_temperature = *(__le16 *)tmp; + data->kelvin_voltage = *((__le16 *)tmp + 1); + + radio_cfg = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_RADIO_CONFIG); + data->radio_cfg_dash = EEPROM_RF_CFG_DASH_MSK(radio_cfg); + data->radio_cfg_pnum = EEPROM_RF_CFG_PNUM_MSK(radio_cfg); + data->radio_cfg_step = EEPROM_RF_CFG_STEP_MSK(radio_cfg); + data->radio_cfg_type = EEPROM_RF_CFG_TYPE_MSK(radio_cfg); + data->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg); + data->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg); + + sku = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_SKU_CAP); + data->sku_cap_11n_enable = sku & EEPROM_SKU_CAP_11N_ENABLE; + data->sku_cap_amt_enable = sku & EEPROM_SKU_CAP_AMT_ENABLE; + data->sku_cap_band_24GHz_enable = sku & EEPROM_SKU_CAP_BAND_24GHZ; + data->sku_cap_band_52GHz_enable = sku & EEPROM_SKU_CAP_BAND_52GHZ; + data->sku_cap_ipan_enable = sku & EEPROM_SKU_CAP_IPAN_ENABLE; + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) + data->sku_cap_11n_enable = false; + + data->nvm_version = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_VERSION); + + /* check overrides (some devices have wrong EEPROM) */ + if (cfg->valid_tx_ant) + data->valid_tx_ant = cfg->valid_tx_ant; + if (cfg->valid_rx_ant) + data->valid_rx_ant = cfg->valid_rx_ant; + + if (!data->valid_tx_ant || !data->valid_rx_ant) { + IWL_ERR_DEV(dev, "invalid antennas (0x%x, 0x%x)\n", + data->valid_tx_ant, data->valid_rx_ant); + goto err_free; + } + + iwl_init_sbands(dev, cfg, data, eeprom, eeprom_size); + + return data; + err_free: + kfree(data); + return NULL; +} +IWL_EXPORT_SYMBOL(iwl_parse_eeprom_data); + +/* helper functions */ +int iwl_nvm_check_version(struct iwl_nvm_data *data, + struct iwl_trans *trans) +{ + if (data->nvm_version >= trans->cfg->nvm_ver || + data->calib_version >= trans->cfg->nvm_calib_ver) { + IWL_DEBUG_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n", + data->nvm_version, data->calib_version); + return 0; + } + + IWL_ERR(trans, + "Unsupported (too old) EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n", + data->nvm_version, trans->cfg->nvm_ver, + data->calib_version, trans->cfg->nvm_calib_ver); + return -EINVAL; +} +IWL_EXPORT_SYMBOL(iwl_nvm_check_version); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h new file mode 100644 index 000000000000..750c8c9ee70d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h @@ -0,0 +1,144 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifndef __iwl_eeprom_parse_h__ +#define __iwl_eeprom_parse_h__ + +#include +#include +#include "iwl-trans.h" + +struct iwl_nvm_data { + int n_hw_addrs; + u8 hw_addr[ETH_ALEN]; + + u8 calib_version; + __le16 calib_voltage; + + __le16 raw_temperature; + __le16 kelvin_temperature; + __le16 kelvin_voltage; + __le16 xtal_calib[2]; + + bool sku_cap_band_24GHz_enable; + bool sku_cap_band_52GHz_enable; + bool sku_cap_11n_enable; + bool sku_cap_11ac_enable; + bool sku_cap_amt_enable; + bool sku_cap_ipan_enable; + bool sku_cap_mimo_disabled; + + u16 radio_cfg_type; + u8 radio_cfg_step; + u8 radio_cfg_dash; + u8 radio_cfg_pnum; + u8 valid_tx_ant, valid_rx_ant; + + u32 nvm_version; + s8 max_tx_pwr_half_dbm; + + bool lar_enabled; + struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; + struct ieee80211_channel channels[]; +}; + +/** + * iwl_parse_eeprom_data - parse EEPROM data and return values + * + * @dev: device pointer we're parsing for, for debug only + * @cfg: device configuration for parsing and overrides + * @eeprom: the EEPROM data + * @eeprom_size: length of the EEPROM data + * + * This function parses all EEPROM values we need and then + * returns a (newly allocated) struct containing all the + * relevant values for driver use. The struct must be freed + * later with iwl_free_nvm_data(). + */ +struct iwl_nvm_data * +iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg, + const u8 *eeprom, size_t eeprom_size); + +/** + * iwl_free_nvm_data - free NVM data + * @data: the data to free + */ +static inline void iwl_free_nvm_data(struct iwl_nvm_data *data) +{ + kfree(data); +} + +int iwl_nvm_check_version(struct iwl_nvm_data *data, + struct iwl_trans *trans); + +int iwl_init_sband_channels(struct iwl_nvm_data *data, + struct ieee80211_supported_band *sband, + int n_channels, enum ieee80211_band band); + +void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + struct ieee80211_sta_ht_cap *ht_info, + enum ieee80211_band band, + u8 tx_chains, u8 rx_chains); + +#endif /* __iwl_eeprom_parse_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c new file mode 100644 index 000000000000..219ca8acca62 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c @@ -0,0 +1,464 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#include +#include +#include + +#include "iwl-drv.h" +#include "iwl-debug.h" +#include "iwl-eeprom-read.h" +#include "iwl-io.h" +#include "iwl-prph.h" +#include "iwl-csr.h" + +/* + * EEPROM access time values: + * + * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG. + * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). + * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. + * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. + */ +#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ + +#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ +#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ + + +/* + * The device's EEPROM semaphore prevents conflicts between driver and uCode + * when accessing the EEPROM; each access is a series of pulses to/from the + * EEPROM chip, not a single event, so even reads could conflict if they + * weren't arbitrated by the semaphore. + */ + +#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ +#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ + +static int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans) +{ + u16 count; + int ret; + + for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { + /* Request semaphore */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); + + /* See if we got it */ + ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, + EEPROM_SEM_TIMEOUT); + if (ret >= 0) { + IWL_DEBUG_EEPROM(trans->dev, + "Acquired semaphore after %d tries.\n", + count+1); + return ret; + } + } + + return ret; +} + +static void iwl_eeprom_release_semaphore(struct iwl_trans *trans) +{ + iwl_clear_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); +} + +static int iwl_eeprom_verify_signature(struct iwl_trans *trans, bool nvm_is_otp) +{ + u32 gp = iwl_read32(trans, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; + + IWL_DEBUG_EEPROM(trans->dev, "EEPROM signature=0x%08x\n", gp); + + switch (gp) { + case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP: + if (!nvm_is_otp) { + IWL_ERR(trans, "EEPROM with bad signature: 0x%08x\n", + gp); + return -ENOENT; + } + return 0; + case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: + case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: + if (nvm_is_otp) { + IWL_ERR(trans, "OTP with bad signature: 0x%08x\n", gp); + return -ENOENT; + } + return 0; + case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP: + default: + IWL_ERR(trans, + "bad EEPROM/OTP signature, type=%s, EEPROM_GP=0x%08x\n", + nvm_is_otp ? "OTP" : "EEPROM", gp); + return -ENOENT; + } +} + +/****************************************************************************** + * + * OTP related functions + * +******************************************************************************/ + +static void iwl_set_otp_access_absolute(struct iwl_trans *trans) +{ + iwl_read32(trans, CSR_OTP_GP_REG); + + iwl_clear_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_OTP_ACCESS_MODE); +} + +static int iwl_nvm_is_otp(struct iwl_trans *trans) +{ + u32 otpgp; + + /* OTP only valid for CP/PP and after */ + switch (trans->hw_rev & CSR_HW_REV_TYPE_MSK) { + case CSR_HW_REV_TYPE_NONE: + IWL_ERR(trans, "Unknown hardware type\n"); + return -EIO; + case CSR_HW_REV_TYPE_5300: + case CSR_HW_REV_TYPE_5350: + case CSR_HW_REV_TYPE_5100: + case CSR_HW_REV_TYPE_5150: + return 0; + default: + otpgp = iwl_read32(trans, CSR_OTP_GP_REG); + if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT) + return 1; + return 0; + } +} + +static int iwl_init_otp_access(struct iwl_trans *trans) +{ + int ret; + + /* Enable 40MHz radio clock */ + iwl_write32(trans, CSR_GP_CNTRL, + iwl_read32(trans, CSR_GP_CNTRL) | + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* wait for clock to be ready */ + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (ret < 0) { + IWL_ERR(trans, "Time out access OTP\n"); + } else { + iwl_set_bits_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); + udelay(5); + iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); + + /* + * CSR auto clock gate disable bit - + * this is only applicable for HW with OTP shadow RAM + */ + if (trans->cfg->base_params->shadow_ram_support) + iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + } + return ret; +} + +static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr, + __le16 *eeprom_data) +{ + int ret = 0; + u32 r; + u32 otpgp; + + iwl_write32(trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); + ret = iwl_poll_bit(trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, + CSR_EEPROM_REG_READ_VALID_MSK, + IWL_EEPROM_ACCESS_TIMEOUT); + if (ret < 0) { + IWL_ERR(trans, "Time out reading OTP[%d]\n", addr); + return ret; + } + r = iwl_read32(trans, CSR_EEPROM_REG); + /* check for ECC errors: */ + otpgp = iwl_read32(trans, CSR_OTP_GP_REG); + if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { + /* stop in this case */ + /* set the uncorrectable OTP ECC bit for acknowledgment */ + iwl_set_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); + IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n"); + return -EINVAL; + } + if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { + /* continue in this case */ + /* set the correctable OTP ECC bit for acknowledgment */ + iwl_set_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); + IWL_ERR(trans, "Correctable OTP ECC error, continue read\n"); + } + *eeprom_data = cpu_to_le16(r >> 16); + return 0; +} + +/* + * iwl_is_otp_empty: check for empty OTP + */ +static bool iwl_is_otp_empty(struct iwl_trans *trans) +{ + u16 next_link_addr = 0; + __le16 link_value; + bool is_empty = false; + + /* locate the beginning of OTP link list */ + if (!iwl_read_otp_word(trans, next_link_addr, &link_value)) { + if (!link_value) { + IWL_ERR(trans, "OTP is empty\n"); + is_empty = true; + } + } else { + IWL_ERR(trans, "Unable to read first block of OTP list.\n"); + is_empty = true; + } + + return is_empty; +} + + +/* + * iwl_find_otp_image: find EEPROM image in OTP + * finding the OTP block that contains the EEPROM image. + * the last valid block on the link list (the block _before_ the last block) + * is the block we should read and used to configure the device. + * If all the available OTP blocks are full, the last block will be the block + * we should read and used to configure the device. + * only perform this operation if shadow RAM is disabled + */ +static int iwl_find_otp_image(struct iwl_trans *trans, + u16 *validblockaddr) +{ + u16 next_link_addr = 0, valid_addr; + __le16 link_value = 0; + int usedblocks = 0; + + /* set addressing mode to absolute to traverse the link list */ + iwl_set_otp_access_absolute(trans); + + /* checking for empty OTP or error */ + if (iwl_is_otp_empty(trans)) + return -EINVAL; + + /* + * start traverse link list + * until reach the max number of OTP blocks + * different devices have different number of OTP blocks + */ + do { + /* save current valid block address + * check for more block on the link list + */ + valid_addr = next_link_addr; + next_link_addr = le16_to_cpu(link_value) * sizeof(u16); + IWL_DEBUG_EEPROM(trans->dev, "OTP blocks %d addr 0x%x\n", + usedblocks, next_link_addr); + if (iwl_read_otp_word(trans, next_link_addr, &link_value)) + return -EINVAL; + if (!link_value) { + /* + * reach the end of link list, return success and + * set address point to the starting address + * of the image + */ + *validblockaddr = valid_addr; + /* skip first 2 bytes (link list pointer) */ + *validblockaddr += 2; + return 0; + } + /* more in the link list, continue */ + usedblocks++; + } while (usedblocks <= trans->cfg->base_params->max_ll_items); + + /* OTP has no valid blocks */ + IWL_DEBUG_EEPROM(trans->dev, "OTP has no valid blocks\n"); + return -EINVAL; +} + +/** + * iwl_read_eeprom - read EEPROM contents + * + * Load the EEPROM contents from adapter and return it + * and its size. + * + * NOTE: This routine uses the non-debug IO access functions. + */ +int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size) +{ + __le16 *e; + u32 gp = iwl_read32(trans, CSR_EEPROM_GP); + int sz; + int ret; + u16 addr; + u16 validblockaddr = 0; + u16 cache_addr = 0; + int nvm_is_otp; + + if (!eeprom || !eeprom_size) + return -EINVAL; + + nvm_is_otp = iwl_nvm_is_otp(trans); + if (nvm_is_otp < 0) + return nvm_is_otp; + + sz = trans->cfg->base_params->eeprom_size; + IWL_DEBUG_EEPROM(trans->dev, "NVM size = %d\n", sz); + + e = kmalloc(sz, GFP_KERNEL); + if (!e) + return -ENOMEM; + + ret = iwl_eeprom_verify_signature(trans, nvm_is_otp); + if (ret < 0) { + IWL_ERR(trans, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); + goto err_free; + } + + /* Make sure driver (instead of uCode) is allowed to read EEPROM */ + ret = iwl_eeprom_acquire_semaphore(trans); + if (ret < 0) { + IWL_ERR(trans, "Failed to acquire EEPROM semaphore.\n"); + goto err_free; + } + + if (nvm_is_otp) { + ret = iwl_init_otp_access(trans); + if (ret) { + IWL_ERR(trans, "Failed to initialize OTP access.\n"); + goto err_unlock; + } + + iwl_write32(trans, CSR_EEPROM_GP, + iwl_read32(trans, CSR_EEPROM_GP) & + ~CSR_EEPROM_GP_IF_OWNER_MSK); + + iwl_set_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | + CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); + /* traversing the linked list if no shadow ram supported */ + if (!trans->cfg->base_params->shadow_ram_support) { + ret = iwl_find_otp_image(trans, &validblockaddr); + if (ret) + goto err_unlock; + } + for (addr = validblockaddr; addr < validblockaddr + sz; + addr += sizeof(u16)) { + __le16 eeprom_data; + + ret = iwl_read_otp_word(trans, addr, &eeprom_data); + if (ret) + goto err_unlock; + e[cache_addr / 2] = eeprom_data; + cache_addr += sizeof(u16); + } + } else { + /* eeprom is an array of 16bit values */ + for (addr = 0; addr < sz; addr += sizeof(u16)) { + u32 r; + + iwl_write32(trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); + + ret = iwl_poll_bit(trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, + CSR_EEPROM_REG_READ_VALID_MSK, + IWL_EEPROM_ACCESS_TIMEOUT); + if (ret < 0) { + IWL_ERR(trans, + "Time out reading EEPROM[%d]\n", addr); + goto err_unlock; + } + r = iwl_read32(trans, CSR_EEPROM_REG); + e[addr / 2] = cpu_to_le16(r >> 16); + } + } + + IWL_DEBUG_EEPROM(trans->dev, "NVM Type: %s\n", + nvm_is_otp ? "OTP" : "EEPROM"); + + iwl_eeprom_release_semaphore(trans); + + *eeprom_size = sz; + *eeprom = (u8 *)e; + return 0; + + err_unlock: + iwl_eeprom_release_semaphore(trans); + err_free: + kfree(e); + + return ret; +} +IWL_EXPORT_SYMBOL(iwl_read_eeprom); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h new file mode 100644 index 000000000000..a6d3bdf82cdd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_eeprom_h__ +#define __iwl_eeprom_h__ + +#include "iwl-trans.h" + +int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size); + +#endif /* __iwl_eeprom_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h new file mode 100644 index 000000000000..d56064861a9c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -0,0 +1,535 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_fh_h__ +#define __iwl_fh_h__ + +#include + +/****************************/ +/* Flow Handler Definitions */ +/****************************/ + +/** + * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) + * Addresses are offsets from device's PCI hardware base address. + */ +#define FH_MEM_LOWER_BOUND (0x1000) +#define FH_MEM_UPPER_BOUND (0x2000) + +/** + * Keep-Warm (KW) buffer base address. + * + * Driver must allocate a 4KByte buffer that is for keeping the + * host DRAM powered on (via dummy accesses to DRAM) to maintain low-latency + * DRAM access when doing Txing or Rxing. The dummy accesses prevent host + * from going into a power-savings mode that would cause higher DRAM latency, + * and possible data over/under-runs, before all Tx/Rx is complete. + * + * Driver loads FH_KW_MEM_ADDR_REG with the physical address (bits 35:4) + * of the buffer, which must be 4K aligned. Once this is set up, the device + * automatically invokes keep-warm accesses when normal accesses might not + * be sufficient to maintain fast DRAM response. + * + * Bit fields: + * 31-0: Keep-warm buffer physical base address [35:4], must be 4K aligned + */ +#define FH_KW_MEM_ADDR_REG (FH_MEM_LOWER_BOUND + 0x97C) + + +/** + * TFD Circular Buffers Base (CBBC) addresses + * + * Device has 16 base pointer registers, one for each of 16 host-DRAM-resident + * circular buffers (CBs/queues) containing Transmit Frame Descriptors (TFDs) + * (see struct iwl_tfd_frame). These 16 pointer registers are offset by 0x04 + * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte + * aligned (address bits 0-7 must be 0). + * Later devices have 20 (5000 series) or 30 (higher) queues, but the registers + * for them are in different places. + * + * Bit fields in each pointer register: + * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned + */ +#define FH_MEM_CBBC_0_15_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0) +#define FH_MEM_CBBC_0_15_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA10) +#define FH_MEM_CBBC_16_19_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBF0) +#define FH_MEM_CBBC_16_19_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) +#define FH_MEM_CBBC_20_31_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xB20) +#define FH_MEM_CBBC_20_31_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xB80) + +/* Find TFD CB base pointer for given queue */ +static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) +{ + if (chnl < 16) + return FH_MEM_CBBC_0_15_LOWER_BOUND + 4 * chnl; + if (chnl < 20) + return FH_MEM_CBBC_16_19_LOWER_BOUND + 4 * (chnl - 16); + WARN_ON_ONCE(chnl >= 32); + return FH_MEM_CBBC_20_31_LOWER_BOUND + 4 * (chnl - 20); +} + + +/** + * Rx SRAM Control and Status Registers (RSCSR) + * + * These registers provide handshake between driver and device for the Rx queue + * (this queue handles *all* command responses, notifications, Rx data, etc. + * sent from uCode to host driver). Unlike Tx, there is only one Rx + * queue, and only one Rx DMA/FIFO channel. Also unlike Tx, which can + * concatenate up to 20 DRAM buffers to form a Tx frame, each Receive Buffer + * Descriptor (RBD) points to only one Rx Buffer (RB); there is a 1:1 + * mapping between RBDs and RBs. + * + * Driver must allocate host DRAM memory for the following, and set the + * physical address of each into device registers: + * + * 1) Receive Buffer Descriptor (RBD) circular buffer (CB), typically with 256 + * entries (although any power of 2, up to 4096, is selectable by driver). + * Each entry (1 dword) points to a receive buffer (RB) of consistent size + * (typically 4K, although 8K or 16K are also selectable by driver). + * Driver sets up RB size and number of RBDs in the CB via Rx config + * register FH_MEM_RCSR_CHNL0_CONFIG_REG. + * + * Bit fields within one RBD: + * 27-0: Receive Buffer physical address bits [35:8], 256-byte aligned + * + * Driver sets physical address [35:8] of base of RBD circular buffer + * into FH_RSCSR_CHNL0_RBDCB_BASE_REG [27:0]. + * + * 2) Rx status buffer, 8 bytes, in which uCode indicates which Rx Buffers + * (RBs) have been filled, via a "write pointer", actually the index of + * the RB's corresponding RBD within the circular buffer. Driver sets + * physical address [35:4] into FH_RSCSR_CHNL0_STTS_WPTR_REG [31:0]. + * + * Bit fields in lower dword of Rx status buffer (upper dword not used + * by driver: + * 31-12: Not used by driver + * 11- 0: Index of last filled Rx buffer descriptor + * (device writes, driver reads this value) + * + * As the driver prepares Receive Buffers (RBs) for device to fill, driver must + * enter pointers to these RBs into contiguous RBD circular buffer entries, + * and update the device's "write" index register, + * FH_RSCSR_CHNL0_RBDCB_WPTR_REG. + * + * This "write" index corresponds to the *next* RBD that the driver will make + * available, i.e. one RBD past the tail of the ready-to-fill RBDs within + * the circular buffer. This value should initially be 0 (before preparing any + * RBs), should be 8 after preparing the first 8 RBs (for example), and must + * wrap back to 0 at the end of the circular buffer (but don't wrap before + * "read" index has advanced past 1! See below). + * NOTE: DEVICE EXPECTS THE WRITE INDEX TO BE INCREMENTED IN MULTIPLES OF 8. + * + * As the device fills RBs (referenced from contiguous RBDs within the circular + * buffer), it updates the Rx status buffer in host DRAM, 2) described above, + * to tell the driver the index of the latest filled RBD. The driver must + * read this "read" index from DRAM after receiving an Rx interrupt from device + * + * The driver must also internally keep track of a third index, which is the + * next RBD to process. When receiving an Rx interrupt, driver should process + * all filled but unprocessed RBs up to, but not including, the RB + * corresponding to the "read" index. For example, if "read" index becomes "1", + * driver may process the RB pointed to by RBD 0. Depending on volume of + * traffic, there may be many RBs to process. + * + * If read index == write index, device thinks there is no room to put new data. + * Due to this, the maximum number of filled RBs is 255, instead of 256. To + * be safe, make sure that there is a gap of at least 2 RBDs between "write" + * and "read" indexes; that is, make sure that there are no more than 254 + * buffers waiting to be filled. + */ +#define FH_MEM_RSCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBC0) +#define FH_MEM_RSCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) +#define FH_MEM_RSCSR_CHNL0 (FH_MEM_RSCSR_LOWER_BOUND) + +/** + * Physical base address of 8-byte Rx Status buffer. + * Bit fields: + * 31-0: Rx status buffer physical base address [35:4], must 16-byte aligned. + */ +#define FH_RSCSR_CHNL0_STTS_WPTR_REG (FH_MEM_RSCSR_CHNL0) + +/** + * Physical base address of Rx Buffer Descriptor Circular Buffer. + * Bit fields: + * 27-0: RBD CD physical base address [35:8], must be 256-byte aligned. + */ +#define FH_RSCSR_CHNL0_RBDCB_BASE_REG (FH_MEM_RSCSR_CHNL0 + 0x004) + +/** + * Rx write pointer (index, really!). + * Bit fields: + * 11-0: Index of driver's most recent prepared-to-be-filled RBD, + 1. + * NOTE: For 256-entry circular buffer, use only bits [7:0]. + */ +#define FH_RSCSR_CHNL0_RBDCB_WPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x008) +#define FH_RSCSR_CHNL0_WPTR (FH_RSCSR_CHNL0_RBDCB_WPTR_REG) + +#define FW_RSCSR_CHNL0_RXDCB_RDPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x00c) +#define FH_RSCSR_CHNL0_RDPTR FW_RSCSR_CHNL0_RXDCB_RDPTR_REG + +/** + * Rx Config/Status Registers (RCSR) + * Rx Config Reg for channel 0 (only channel used) + * + * Driver must initialize FH_MEM_RCSR_CHNL0_CONFIG_REG as follows for + * normal operation (see bit fields). + * + * Clearing FH_MEM_RCSR_CHNL0_CONFIG_REG to 0 turns off Rx DMA. + * Driver should poll FH_MEM_RSSR_RX_STATUS_REG for + * FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (bit 24) before continuing. + * + * Bit fields: + * 31-30: Rx DMA channel enable: '00' off/pause, '01' pause at end of frame, + * '10' operate normally + * 29-24: reserved + * 23-20: # RBDs in circular buffer = 2^value; use "8" for 256 RBDs (normal), + * min "5" for 32 RBDs, max "12" for 4096 RBDs. + * 19-18: reserved + * 17-16: size of each receive buffer; '00' 4K (normal), '01' 8K, + * '10' 12K, '11' 16K. + * 15-14: reserved + * 13-12: IRQ destination; '00' none, '01' host driver (normal operation) + * 11- 4: timeout for closing Rx buffer and interrupting host (units 32 usec) + * typical value 0x10 (about 1/2 msec) + * 3- 0: reserved + */ +#define FH_MEM_RCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) +#define FH_MEM_RCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xCC0) +#define FH_MEM_RCSR_CHNL0 (FH_MEM_RCSR_LOWER_BOUND) + +#define FH_MEM_RCSR_CHNL0_CONFIG_REG (FH_MEM_RCSR_CHNL0) +#define FH_MEM_RCSR_CHNL0_RBDCB_WPTR (FH_MEM_RCSR_CHNL0 + 0x8) +#define FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ (FH_MEM_RCSR_CHNL0 + 0x10) + +#define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */ +#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */ +#define FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK (0x00008000) /* bit 15 */ +#define FH_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MSK (0x00030000) /* bits 16-17 */ +#define FH_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MSK (0x00F00000) /* bits 20-23 */ +#define FH_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MSK (0xC0000000) /* bits 30-31*/ + +#define FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS (20) +#define FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS (4) +#define RX_RB_TIMEOUT (0x11) + +#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000) +#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000) +#define FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL (0x80000000) + +#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000) +#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000) +#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000) +#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000) + +#define FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY (0x00000004) +#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000) +#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000) + +/** + * Rx Shared Status Registers (RSSR) + * + * After stopping Rx DMA channel (writing 0 to + * FH_MEM_RCSR_CHNL0_CONFIG_REG), driver must poll + * FH_MEM_RSSR_RX_STATUS_REG until Rx channel is idle. + * + * Bit fields: + * 24: 1 = Channel 0 is idle + * + * FH_MEM_RSSR_SHARED_CTRL_REG and FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV + * contain default values that should not be altered by the driver. + */ +#define FH_MEM_RSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC40) +#define FH_MEM_RSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xD00) + +#define FH_MEM_RSSR_SHARED_CTRL_REG (FH_MEM_RSSR_LOWER_BOUND) +#define FH_MEM_RSSR_RX_STATUS_REG (FH_MEM_RSSR_LOWER_BOUND + 0x004) +#define FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV\ + (FH_MEM_RSSR_LOWER_BOUND + 0x008) + +#define FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) + +#define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT 28 +#define FH_MEM_TB_MAX_LENGTH (0x00020000) + +/* TFDB Area - TFDs buffer table */ +#define FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF) +#define FH_TFDIB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x900) +#define FH_TFDIB_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x958) +#define FH_TFDIB_CTRL0_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl)) +#define FH_TFDIB_CTRL1_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4) + +/** + * Transmit DMA Channel Control/Status Registers (TCSR) + * + * Device has one configuration register for each of 8 Tx DMA/FIFO channels + * supported in hardware (don't confuse these with the 16 Tx queues in DRAM, + * which feed the DMA/FIFO channels); config regs are separated by 0x20 bytes. + * + * To use a Tx DMA channel, driver must initialize its + * FH_TCSR_CHNL_TX_CONFIG_REG(chnl) with: + * + * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL + * + * All other bits should be 0. + * + * Bit fields: + * 31-30: Tx DMA channel enable: '00' off/pause, '01' pause at end of frame, + * '10' operate normally + * 29- 4: Reserved, set to "0" + * 3: Enable internal DMA requests (1, normal operation), disable (0) + * 2- 0: Reserved, set to "0" + */ +#define FH_TCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xD00) +#define FH_TCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xE60) + +/* Find Control/Status reg for given Tx DMA/FIFO channel */ +#define FH_TCSR_CHNL_NUM (8) + +/* TCSR: tx_config register values */ +#define FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \ + (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl)) +#define FH_TCSR_CHNL_TX_CREDIT_REG(_chnl) \ + (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x4) +#define FH_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \ + (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x8) + +#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRV (0x00000001) + +#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE (0x00000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE (0x00000008) + +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000) +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000) + +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000) +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000) + +#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000) + +#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000) +#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000) +#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003) + +#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20) +#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12) + +/** + * Tx Shared Status Registers (TSSR) + * + * After stopping Tx DMA channel (writing 0 to + * FH_TCSR_CHNL_TX_CONFIG_REG(chnl)), driver must poll + * FH_TSSR_TX_STATUS_REG until selected Tx channel is idle + * (channel's buffers empty | no pending requests). + * + * Bit fields: + * 31-24: 1 = Channel buffers empty (channel 7:0) + * 23-16: 1 = No pending requests (channel 7:0) + */ +#define FH_TSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xEA0) +#define FH_TSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xEC0) + +#define FH_TSSR_TX_STATUS_REG (FH_TSSR_LOWER_BOUND + 0x010) + +/** + * Bit fields for TSSR(Tx Shared Status & Control) error status register: + * 31: Indicates an address error when accessed to internal memory + * uCode/driver must write "1" in order to clear this flag + * 30: Indicates that Host did not send the expected number of dwords to FH + * uCode/driver must write "1" in order to clear this flag + * 16-9:Each status bit is for one channel. Indicates that an (Error) ActDMA + * command was received from the scheduler while the TRB was already full + * with previous command + * uCode/driver must write "1" in order to clear this flag + * 7-0: Each status bit indicates a channel's TxCredit error. When an error + * bit is set, it indicates that the FH has received a full indication + * from the RTC TxFIFO and the current value of the TxCredit counter was + * not equal to zero. This mean that the credit mechanism was not + * synchronized to the TxFIFO status + * uCode/driver must write "1" in order to clear this flag + */ +#define FH_TSSR_TX_ERROR_REG (FH_TSSR_LOWER_BOUND + 0x018) +#define FH_TSSR_TX_MSG_CONFIG_REG (FH_TSSR_LOWER_BOUND + 0x008) + +#define FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16) + +/* Tx service channels */ +#define FH_SRVC_CHNL (9) +#define FH_SRVC_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9C8) +#define FH_SRVC_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0) +#define FH_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \ + (FH_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4) + +#define FH_TX_CHICKEN_BITS_REG (FH_MEM_LOWER_BOUND + 0xE98) +#define FH_TX_TRB_REG(_chan) (FH_MEM_LOWER_BOUND + 0x958 + (_chan) * 4) + +/* Instruct FH to increment the retry count of a packet when + * it is brought from the memory to TX-FIFO + */ +#define FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002) + +#define RX_QUEUE_SIZE 256 +#define RX_QUEUE_MASK 255 +#define RX_QUEUE_SIZE_LOG 8 + +/** + * struct iwl_rb_status - reserve buffer status + * host memory mapped FH registers + * @closed_rb_num [0:11] - Indicates the index of the RB which was closed + * @closed_fr_num [0:11] - Indicates the index of the RX Frame which was closed + * @finished_rb_num [0:11] - Indicates the index of the current RB + * in which the last frame was written to + * @finished_fr_num [0:11] - Indicates the index of the RX Frame + * which was transferred + */ +struct iwl_rb_status { + __le16 closed_rb_num; + __le16 closed_fr_num; + __le16 finished_rb_num; + __le16 finished_fr_nam; + __le32 __unused; +} __packed; + + +#define TFD_QUEUE_SIZE_MAX (256) +#define TFD_QUEUE_SIZE_BC_DUP (64) +#define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP) +#define IWL_TX_DMA_MASK DMA_BIT_MASK(36) +#define IWL_NUM_OF_TBS 20 + +static inline u8 iwl_get_dma_hi_addr(dma_addr_t addr) +{ + return (sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0) & 0xF; +} +/** + * struct iwl_tfd_tb transmit buffer descriptor within transmit frame descriptor + * + * This structure contains dma address and length of transmission address + * + * @lo: low [31:0] portion of the dma address of TX buffer + * every even is unaligned on 16 bit boundary + * @hi_n_len 0-3 [35:32] portion of dma + * 4-15 length of the tx buffer + */ +struct iwl_tfd_tb { + __le32 lo; + __le16 hi_n_len; +} __packed; + +/** + * struct iwl_tfd + * + * Transmit Frame Descriptor (TFD) + * + * @ __reserved1[3] reserved + * @ num_tbs 0-4 number of active tbs + * 5 reserved + * 6-7 padding (not used) + * @ tbs[20] transmit frame buffer descriptors + * @ __pad padding + * + * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM. + * Both driver and device share these circular buffers, each of which must be + * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes + * + * Driver must indicate the physical address of the base of each + * circular buffer via the FH_MEM_CBBC_QUEUE registers. + * + * Each TFD contains pointer/size information for up to 20 data buffers + * in host DRAM. These buffers collectively contain the (one) frame described + * by the TFD. Each buffer must be a single contiguous block of memory within + * itself, but buffers may be scattered in host DRAM. Each buffer has max size + * of (4K - 4). The concatenates all of a TFD's buffers into a single + * Tx frame, up to 8 KBytes in size. + * + * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx. + */ +struct iwl_tfd { + u8 __reserved1[3]; + u8 num_tbs; + struct iwl_tfd_tb tbs[IWL_NUM_OF_TBS]; + __le32 __pad; +} __packed; + +/* Keep Warm Size */ +#define IWL_KW_SIZE 0x1000 /* 4k */ + +/* Fixed (non-configurable) rx data from phy */ + +/** + * struct iwlagn_schedq_bc_tbl scheduler byte count table + * base physical address provided by SCD_DRAM_BASE_ADDR + * @tfd_offset 0-12 - tx command byte count + * 12-16 - station index + */ +struct iwlagn_scd_bc_tbl { + __le16 tfd_offset[TFD_QUEUE_BC_SIZE]; +} __packed; + +#endif /* !__iwl_fh_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h new file mode 100644 index 000000000000..9dbe19cbb4dd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h @@ -0,0 +1,320 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __fw_error_dump_h__ +#define __fw_error_dump_h__ + +#include + +#define IWL_FW_ERROR_DUMP_BARKER 0x14789632 + +/** + * enum iwl_fw_error_dump_type - types of data in the dump file + * @IWL_FW_ERROR_DUMP_CSR: Control Status Registers - from offset 0 + * @IWL_FW_ERROR_DUMP_RXF: + * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as + * &struct iwl_fw_error_dump_txcmd packets + * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info + * info on the device / firmware. + * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor + * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several + * sections like this in a single file. + * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers + * @IWL_FW_ERROR_DUMP_MEM: chunk of memory + * @IWL_FW_ERROR_DUMP_ERROR_INFO: description of what triggered this dump. + * Structured as &struct iwl_fw_error_dump_trigger_desc. + * @IWL_FW_ERROR_DUMP_RB: the content of an RB structured as + * &struct iwl_fw_error_dump_rb + * @IWL_FW_ERROR_PAGING: UMAC's image memory segments which were + * paged to the DRAM. + */ +enum iwl_fw_error_dump_type { + /* 0 is deprecated */ + IWL_FW_ERROR_DUMP_CSR = 1, + IWL_FW_ERROR_DUMP_RXF = 2, + IWL_FW_ERROR_DUMP_TXCMD = 3, + IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4, + IWL_FW_ERROR_DUMP_FW_MONITOR = 5, + IWL_FW_ERROR_DUMP_PRPH = 6, + IWL_FW_ERROR_DUMP_TXF = 7, + IWL_FW_ERROR_DUMP_FH_REGS = 8, + IWL_FW_ERROR_DUMP_MEM = 9, + IWL_FW_ERROR_DUMP_ERROR_INFO = 10, + IWL_FW_ERROR_DUMP_RB = 11, + IWL_FW_ERROR_DUMP_PAGING = 12, + + IWL_FW_ERROR_DUMP_MAX, +}; + +/** + * struct iwl_fw_error_dump_data - data for one type + * @type: %enum iwl_fw_error_dump_type + * @len: the length starting from %data + * @data: the data itself + */ +struct iwl_fw_error_dump_data { + __le32 type; + __le32 len; + __u8 data[]; +} __packed; + +/** + * struct iwl_fw_error_dump_file - the layout of the header of the file + * @barker: must be %IWL_FW_ERROR_DUMP_BARKER + * @file_len: the length of all the file starting from %barker + * @data: array of %struct iwl_fw_error_dump_data + */ +struct iwl_fw_error_dump_file { + __le32 barker; + __le32 file_len; + u8 data[0]; +} __packed; + +/** + * struct iwl_fw_error_dump_txcmd - TX command data + * @cmdlen: original length of command + * @caplen: captured length of command (may be less) + * @data: captured command data, @caplen bytes + */ +struct iwl_fw_error_dump_txcmd { + __le32 cmdlen; + __le32 caplen; + u8 data[]; +} __packed; + +/** + * struct iwl_fw_error_dump_fifo - RX/TX FIFO data + * @fifo_num: number of FIFO (starting from 0) + * @available_bytes: num of bytes available in FIFO (may be less than FIFO size) + * @wr_ptr: position of write pointer + * @rd_ptr: position of read pointer + * @fence_ptr: position of fence pointer + * @fence_mode: the current mode of the fence (before locking) - + * 0=follow RD pointer ; 1 = freeze + * @data: all of the FIFO's data + */ +struct iwl_fw_error_dump_fifo { + __le32 fifo_num; + __le32 available_bytes; + __le32 wr_ptr; + __le32 rd_ptr; + __le32 fence_ptr; + __le32 fence_mode; + u8 data[]; +} __packed; + +enum iwl_fw_error_dump_family { + IWL_FW_ERROR_DUMP_FAMILY_7 = 7, + IWL_FW_ERROR_DUMP_FAMILY_8 = 8, +}; + +/** + * struct iwl_fw_error_dump_info - info on the device / firmware + * @device_family: the family of the device (7 / 8) + * @hw_step: the step of the device + * @fw_human_readable: human readable FW version + * @dev_human_readable: name of the device + * @bus_human_readable: name of the bus used + */ +struct iwl_fw_error_dump_info { + __le32 device_family; + __le32 hw_step; + u8 fw_human_readable[FW_VER_HUMAN_READABLE_SZ]; + u8 dev_human_readable[64]; + u8 bus_human_readable[8]; +} __packed; + +/** + * struct iwl_fw_error_dump_fw_mon - FW monitor data + * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer + * @fw_mon_base_ptr: base pointer of the data + * @fw_mon_cycle_cnt: number of wraparounds + * @reserved: for future use + * @data: captured data + */ +struct iwl_fw_error_dump_fw_mon { + __le32 fw_mon_wr_ptr; + __le32 fw_mon_base_ptr; + __le32 fw_mon_cycle_cnt; + __le32 reserved[3]; + u8 data[]; +} __packed; + +/** + * struct iwl_fw_error_dump_prph - periphery registers data + * @prph_start: address of the first register in this chunk + * @data: the content of the registers + */ +struct iwl_fw_error_dump_prph { + __le32 prph_start; + __le32 data[]; +}; + +enum iwl_fw_error_dump_mem_type { + IWL_FW_ERROR_DUMP_MEM_SRAM, + IWL_FW_ERROR_DUMP_MEM_SMEM, +}; + +/** + * struct iwl_fw_error_dump_mem - chunk of memory + * @type: %enum iwl_fw_error_dump_mem_type + * @offset: the offset from which the memory was read + * @data: the content of the memory + */ +struct iwl_fw_error_dump_mem { + __le32 type; + __le32 offset; + u8 data[]; +}; + +/** + * struct iwl_fw_error_dump_rb - content of an Receive Buffer + * @index: the index of the Receive Buffer in the Rx queue + * @rxq: the RB's Rx queue + * @reserved: + * @data: the content of the Receive Buffer + */ +struct iwl_fw_error_dump_rb { + __le32 index; + __le32 rxq; + __le32 reserved; + u8 data[]; +}; + +/** + * struct iwl_fw_error_dump_paging - content of the UMAC's image page + * block on DRAM + * @index: the index of the page block + * @reserved: + * @data: the content of the page block + */ +struct iwl_fw_error_dump_paging { + __le32 index; + __le32 reserved; + u8 data[]; +}; + +/** + * iwl_fw_error_next_data - advance fw error dump data pointer + * @data: previous data block + * Returns: next data block + */ +static inline struct iwl_fw_error_dump_data * +iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) +{ + return (void *)(data->data + le32_to_cpu(data->len)); +} + +/** + * enum iwl_fw_dbg_trigger - triggers available + * + * @FW_DBG_TRIGGER_USER: trigger log collection by user + * This should not be defined as a trigger to the driver, but a value the + * driver should set to indicate that the trigger was initiated by the + * user. + * @FW_DBG_TRIGGER_FW_ASSERT: trigger log collection when the firmware asserts + * @FW_DBG_TRIGGER_MISSED_BEACONS: trigger log collection when beacons are + * missed. + * @FW_DBG_TRIGGER_CHANNEL_SWITCH: trigger log collection upon channel switch. + * @FW_DBG_TRIGGER_FW_NOTIF: trigger log collection when the firmware sends a + * command response or a notification. + * @FW_DBG_TRIGGER_MLME: trigger log collection upon MLME event. + * @FW_DBG_TRIGGER_STATS: trigger log collection upon statistics threshold. + * @FW_DBG_TRIGGER_RSSI: trigger log collection when the rssi of the beacon + * goes below a threshold. + * @FW_DBG_TRIGGER_TXQ_TIMERS: configures the timers for the Tx queue hang + * detection. + * @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related + * events. + * @FW_DBG_TRIGGER_BA: trigger log collection upon BlockAck related events. + */ +enum iwl_fw_dbg_trigger { + FW_DBG_TRIGGER_INVALID = 0, + FW_DBG_TRIGGER_USER, + FW_DBG_TRIGGER_FW_ASSERT, + FW_DBG_TRIGGER_MISSED_BEACONS, + FW_DBG_TRIGGER_CHANNEL_SWITCH, + FW_DBG_TRIGGER_FW_NOTIF, + FW_DBG_TRIGGER_MLME, + FW_DBG_TRIGGER_STATS, + FW_DBG_TRIGGER_RSSI, + FW_DBG_TRIGGER_TXQ_TIMERS, + FW_DBG_TRIGGER_TIME_EVENT, + FW_DBG_TRIGGER_BA, + + /* must be last */ + FW_DBG_TRIGGER_MAX, +}; + +/** + * struct iwl_fw_error_dump_trigger_desc - describes the trigger condition + * @type: %enum iwl_fw_dbg_trigger + * @data: raw data about what happened + */ +struct iwl_fw_error_dump_trigger_desc { + __le32 type; + u8 data[]; +}; + +#endif /* __fw_error_dump_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h new file mode 100644 index 000000000000..08303db0000f --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -0,0 +1,768 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_fw_file_h__ +#define __iwl_fw_file_h__ + +#include +#include + +/* v1/v2 uCode file layout */ +struct iwl_ucode_header { + __le32 ver; /* major/minor/API/serial */ + union { + struct { + __le32 inst_size; /* bytes of runtime code */ + __le32 data_size; /* bytes of runtime data */ + __le32 init_size; /* bytes of init code */ + __le32 init_data_size; /* bytes of init data */ + __le32 boot_size; /* bytes of bootstrap code */ + u8 data[0]; /* in same order as sizes */ + } v1; + struct { + __le32 build; /* build number */ + __le32 inst_size; /* bytes of runtime code */ + __le32 data_size; /* bytes of runtime data */ + __le32 init_size; /* bytes of init code */ + __le32 init_data_size; /* bytes of init data */ + __le32 boot_size; /* bytes of bootstrap code */ + u8 data[0]; /* in same order as sizes */ + } v2; + } u; +}; + +/* + * new TLV uCode file layout + * + * The new TLV file format contains TLVs, that each specify + * some piece of data. + */ + +enum iwl_ucode_tlv_type { + IWL_UCODE_TLV_INVALID = 0, /* unused */ + IWL_UCODE_TLV_INST = 1, + IWL_UCODE_TLV_DATA = 2, + IWL_UCODE_TLV_INIT = 3, + IWL_UCODE_TLV_INIT_DATA = 4, + IWL_UCODE_TLV_BOOT = 5, + IWL_UCODE_TLV_PROBE_MAX_LEN = 6, /* a u32 value */ + IWL_UCODE_TLV_PAN = 7, + IWL_UCODE_TLV_RUNT_EVTLOG_PTR = 8, + IWL_UCODE_TLV_RUNT_EVTLOG_SIZE = 9, + IWL_UCODE_TLV_RUNT_ERRLOG_PTR = 10, + IWL_UCODE_TLV_INIT_EVTLOG_PTR = 11, + IWL_UCODE_TLV_INIT_EVTLOG_SIZE = 12, + IWL_UCODE_TLV_INIT_ERRLOG_PTR = 13, + IWL_UCODE_TLV_ENHANCE_SENS_TBL = 14, + IWL_UCODE_TLV_PHY_CALIBRATION_SIZE = 15, + IWL_UCODE_TLV_WOWLAN_INST = 16, + IWL_UCODE_TLV_WOWLAN_DATA = 17, + IWL_UCODE_TLV_FLAGS = 18, + IWL_UCODE_TLV_SEC_RT = 19, + IWL_UCODE_TLV_SEC_INIT = 20, + IWL_UCODE_TLV_SEC_WOWLAN = 21, + IWL_UCODE_TLV_DEF_CALIB = 22, + IWL_UCODE_TLV_PHY_SKU = 23, + IWL_UCODE_TLV_SECURE_SEC_RT = 24, + IWL_UCODE_TLV_SECURE_SEC_INIT = 25, + IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26, + IWL_UCODE_TLV_NUM_OF_CPU = 27, + IWL_UCODE_TLV_CSCHEME = 28, + IWL_UCODE_TLV_API_CHANGES_SET = 29, + IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30, + IWL_UCODE_TLV_N_SCAN_CHANNELS = 31, + IWL_UCODE_TLV_PAGING = 32, + IWL_UCODE_TLV_SEC_RT_USNIFFER = 34, + IWL_UCODE_TLV_SDIO_ADMA_ADDR = 35, + IWL_UCODE_TLV_FW_VERSION = 36, + IWL_UCODE_TLV_FW_DBG_DEST = 38, + IWL_UCODE_TLV_FW_DBG_CONF = 39, + IWL_UCODE_TLV_FW_DBG_TRIGGER = 40, + IWL_UCODE_TLV_FW_GSCAN_CAPA = 50, +}; + +struct iwl_ucode_tlv { + __le32 type; /* see above */ + __le32 length; /* not including type/length fields */ + u8 data[0]; +}; + +#define IWL_TLV_UCODE_MAGIC 0x0a4c5749 +#define FW_VER_HUMAN_READABLE_SZ 64 + +struct iwl_tlv_ucode_header { + /* + * The TLV style ucode header is distinguished from + * the v1/v2 style header by first four bytes being + * zero, as such is an invalid combination of + * major/minor/API/serial versions. + */ + __le32 zero; + __le32 magic; + u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; + /* major/minor/API/serial or major in new format */ + __le32 ver; + __le32 build; + __le64 ignore; + /* + * The data contained herein has a TLV layout, + * see above for the TLV header and types. + * Note that each TLV is padded to a length + * that is a multiple of 4 for alignment. + */ + u8 data[0]; +}; + +/* + * ucode TLVs + * + * ability to get extension for: flags & capabilities from ucode binaries files + */ +struct iwl_ucode_api { + __le32 api_index; + __le32 api_flags; +} __packed; + +struct iwl_ucode_capa { + __le32 api_index; + __le32 api_capa; +} __packed; + +/** + * enum iwl_ucode_tlv_flag - ucode API flags + * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously + * was a separate TLV but moved here to save space. + * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behavior on hidden SSID, + * treats good CRC threshold as a boolean + * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w). + * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P. + * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS + * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD + * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan + * offload profile config command. + * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six + * (rather than two) IPv6 addresses + * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element + * from the probe request template. + * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version) + * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version) + * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC + * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and + * P2P client interfaces simultaneously if they are in different bindings. + * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and + * P2P client interfaces simultaneously if they are in same bindings. + * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD + * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save + * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering. + * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients + * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS. + */ +enum iwl_ucode_tlv_flag { + IWL_UCODE_TLV_FLAGS_PAN = BIT(0), + IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1), + IWL_UCODE_TLV_FLAGS_MFP = BIT(2), + IWL_UCODE_TLV_FLAGS_P2P = BIT(3), + IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4), + IWL_UCODE_TLV_FLAGS_SHORT_BL = BIT(7), + IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10), + IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12), + IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = BIT(15), + IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = BIT(16), + IWL_UCODE_TLV_FLAGS_P2P_PM = BIT(21), + IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM = BIT(22), + IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM = BIT(23), + IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24), + IWL_UCODE_TLV_FLAGS_EBS_SUPPORT = BIT(25), + IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26), + IWL_UCODE_TLV_FLAGS_BCAST_FILTERING = BIT(29), + IWL_UCODE_TLV_FLAGS_GO_UAPSD = BIT(30), +}; + +typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t; + +/** + * enum iwl_ucode_tlv_api - ucode api + * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex + * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time + * longer than the passive one, which is essential for fragmented scan. + * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source. + * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header + * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params + * @IWL_UCODE_TLV_API_NEW_VERSION: new versioning format + * @IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY: scan APIs use 8-level priority + * instead of 3. + * @IWL_UCODE_TLV_API_TX_POWER_CHAIN: TX power API has larger command size + * (command version 3) that supports per-chain limits + * + * @NUM_IWL_UCODE_TLV_API: number of bits used + */ +enum iwl_ucode_tlv_api { + IWL_UCODE_TLV_API_BT_COEX_SPLIT = (__force iwl_ucode_tlv_api_t)3, + IWL_UCODE_TLV_API_FRAGMENTED_SCAN = (__force iwl_ucode_tlv_api_t)8, + IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = (__force iwl_ucode_tlv_api_t)9, + IWL_UCODE_TLV_API_WIDE_CMD_HDR = (__force iwl_ucode_tlv_api_t)14, + IWL_UCODE_TLV_API_LQ_SS_PARAMS = (__force iwl_ucode_tlv_api_t)18, + IWL_UCODE_TLV_API_NEW_VERSION = (__force iwl_ucode_tlv_api_t)20, + IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY = (__force iwl_ucode_tlv_api_t)24, + IWL_UCODE_TLV_API_TX_POWER_CHAIN = (__force iwl_ucode_tlv_api_t)27, + + NUM_IWL_UCODE_TLV_API +#ifdef __CHECKER__ + /* sparse says it cannot increment the previous enum member */ + = 128 +#endif +}; + +typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; + +/** + * enum iwl_ucode_tlv_capa - ucode capabilities + * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3 + * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory + * @IWL_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan. + * @IWL_UCODE_TLV_CAPA_BEAMFORMER: supports Beamformer + * @IWL_UCODE_TLV_CAPA_TOF_SUPPORT: supports Time of Flight (802.11mc FTM) + * @IWL_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality + * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current + * tx power value into TPC Report action frame and Link Measurement Report + * action frame + * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports updating current + * channel in DS parameter set element in probe requests. + * @IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in + * probe requests. + * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests + * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA), + * which also implies support for the scheduler configuration command + * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching + * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command + * @IWL_UCODE_TLV_CAPA_DC2DC_SUPPORT: supports DC2DC Command + * @IWL_UCODE_TLV_CAPA_CSUM_SUPPORT: supports TCP Checksum Offload + * @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics + * @IWL_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running + * @IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC: ucode supports LAR updates with different + * sources for the MCC. This TLV bit is a future replacement to + * IWL_UCODE_TLV_API_WIFI_MCC_UPDATE. When either is set, multi-source LAR + * is supported. + * @IWL_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC + * @IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT: supports gscan + * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement + * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts + * + * @NUM_IWL_UCODE_TLV_CAPA: number of bits used + */ +enum iwl_ucode_tlv_capa { + IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = (__force iwl_ucode_tlv_capa_t)0, + IWL_UCODE_TLV_CAPA_LAR_SUPPORT = (__force iwl_ucode_tlv_capa_t)1, + IWL_UCODE_TLV_CAPA_UMAC_SCAN = (__force iwl_ucode_tlv_capa_t)2, + IWL_UCODE_TLV_CAPA_BEAMFORMER = (__force iwl_ucode_tlv_capa_t)3, + IWL_UCODE_TLV_CAPA_TOF_SUPPORT = (__force iwl_ucode_tlv_capa_t)5, + IWL_UCODE_TLV_CAPA_TDLS_SUPPORT = (__force iwl_ucode_tlv_capa_t)6, + IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT = (__force iwl_ucode_tlv_capa_t)8, + IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT = (__force iwl_ucode_tlv_capa_t)9, + IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT = (__force iwl_ucode_tlv_capa_t)10, + IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = (__force iwl_ucode_tlv_capa_t)11, + IWL_UCODE_TLV_CAPA_DQA_SUPPORT = (__force iwl_ucode_tlv_capa_t)12, + IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = (__force iwl_ucode_tlv_capa_t)13, + IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = (__force iwl_ucode_tlv_capa_t)18, + IWL_UCODE_TLV_CAPA_DC2DC_CONFIG_SUPPORT = (__force iwl_ucode_tlv_capa_t)19, + IWL_UCODE_TLV_CAPA_CSUM_SUPPORT = (__force iwl_ucode_tlv_capa_t)21, + IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS = (__force iwl_ucode_tlv_capa_t)22, + IWL_UCODE_TLV_CAPA_BT_COEX_PLCR = (__force iwl_ucode_tlv_capa_t)28, + IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC = (__force iwl_ucode_tlv_capa_t)29, + IWL_UCODE_TLV_CAPA_BT_COEX_RRC = (__force iwl_ucode_tlv_capa_t)30, + IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)31, + IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64, + IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, + + NUM_IWL_UCODE_TLV_CAPA +#ifdef __CHECKER__ + /* sparse says it cannot increment the previous enum member */ + = 128 +#endif +}; + +/* The default calibrate table size if not specified by firmware file */ +#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18 +#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19 +#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE 253 + +/* The default max probe length if not specified by the firmware file */ +#define IWL_DEFAULT_MAX_PROBE_LENGTH 200 + +/* + * For 16.0 uCode and above, there is no differentiation between sections, + * just an offset to the HW address. + */ +#define IWL_UCODE_SECTION_MAX 16 +#define CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC +#define PAGING_SEPARATOR_SECTION 0xAAAABBBB + +/* uCode version contains 4 values: Major/Minor/API/Serial */ +#define IWL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24) +#define IWL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16) +#define IWL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8) +#define IWL_UCODE_SERIAL(ver) ((ver) & 0x000000FF) + +/* + * Calibration control struct. + * Sent as part of the phy configuration command. + * @flow_trigger: bitmap for which calibrations to perform according to + * flow triggers. + * @event_trigger: bitmap for which calibrations to perform according to + * event triggers. + */ +struct iwl_tlv_calib_ctrl { + __le32 flow_trigger; + __le32 event_trigger; +} __packed; + +enum iwl_fw_phy_cfg { + FW_PHY_CFG_RADIO_TYPE_POS = 0, + FW_PHY_CFG_RADIO_TYPE = 0x3 << FW_PHY_CFG_RADIO_TYPE_POS, + FW_PHY_CFG_RADIO_STEP_POS = 2, + FW_PHY_CFG_RADIO_STEP = 0x3 << FW_PHY_CFG_RADIO_STEP_POS, + FW_PHY_CFG_RADIO_DASH_POS = 4, + FW_PHY_CFG_RADIO_DASH = 0x3 << FW_PHY_CFG_RADIO_DASH_POS, + FW_PHY_CFG_TX_CHAIN_POS = 16, + FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS, + FW_PHY_CFG_RX_CHAIN_POS = 20, + FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS, +}; + +#define IWL_UCODE_MAX_CS 1 + +/** + * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW. + * @cipher: a cipher suite selector + * @flags: cipher scheme flags (currently reserved for a future use) + * @hdr_len: a size of MPDU security header + * @pn_len: a size of PN + * @pn_off: an offset of pn from the beginning of the security header + * @key_idx_off: an offset of key index byte in the security header + * @key_idx_mask: a bit mask of key_idx bits + * @key_idx_shift: bit shift needed to get key_idx + * @mic_len: mic length in bytes + * @hw_cipher: a HW cipher index used in host commands + */ +struct iwl_fw_cipher_scheme { + __le32 cipher; + u8 flags; + u8 hdr_len; + u8 pn_len; + u8 pn_off; + u8 key_idx_off; + u8 key_idx_mask; + u8 key_idx_shift; + u8 mic_len; + u8 hw_cipher; +} __packed; + +enum iwl_fw_dbg_reg_operator { + CSR_ASSIGN, + CSR_SETBIT, + CSR_CLEARBIT, + + PRPH_ASSIGN, + PRPH_SETBIT, + PRPH_CLEARBIT, + + INDIRECT_ASSIGN, + INDIRECT_SETBIT, + INDIRECT_CLEARBIT, + + PRPH_BLOCKBIT, +}; + +/** + * struct iwl_fw_dbg_reg_op - an operation on a register + * + * @op: %enum iwl_fw_dbg_reg_operator + * @addr: offset of the register + * @val: value + */ +struct iwl_fw_dbg_reg_op { + u8 op; + u8 reserved[3]; + __le32 addr; + __le32 val; +} __packed; + +/** + * enum iwl_fw_dbg_monitor_mode - available monitor recording modes + * + * @SMEM_MODE: monitor stores the data in SMEM + * @EXTERNAL_MODE: monitor stores the data in allocated DRAM + * @MARBH_MODE: monitor stores the data in MARBH buffer + * @MIPI_MODE: monitor outputs the data through the MIPI interface + */ +enum iwl_fw_dbg_monitor_mode { + SMEM_MODE = 0, + EXTERNAL_MODE = 1, + MARBH_MODE = 2, + MIPI_MODE = 3, +}; + +/** + * struct iwl_fw_dbg_dest_tlv - configures the destination of the debug data + * + * @version: version of the TLV - currently 0 + * @monitor_mode: %enum iwl_fw_dbg_monitor_mode + * @size_power: buffer size will be 2^(size_power + 11) + * @base_reg: addr of the base addr register (PRPH) + * @end_reg: addr of the end addr register (PRPH) + * @write_ptr_reg: the addr of the reg of the write pointer + * @wrap_count: the addr of the reg of the wrap_count + * @base_shift: shift right of the base addr reg + * @end_shift: shift right of the end addr reg + * @reg_ops: array of registers operations + * + * This parses IWL_UCODE_TLV_FW_DBG_DEST + */ +struct iwl_fw_dbg_dest_tlv { + u8 version; + u8 monitor_mode; + u8 size_power; + u8 reserved; + __le32 base_reg; + __le32 end_reg; + __le32 write_ptr_reg; + __le32 wrap_count; + u8 base_shift; + u8 end_shift; + struct iwl_fw_dbg_reg_op reg_ops[0]; +} __packed; + +struct iwl_fw_dbg_conf_hcmd { + u8 id; + u8 reserved; + __le16 len; + u8 data[0]; +} __packed; + +/** + * enum iwl_fw_dbg_trigger_mode - triggers functionalities + * + * @IWL_FW_DBG_TRIGGER_START: when trigger occurs re-conf the dbg mechanism + * @IWL_FW_DBG_TRIGGER_STOP: when trigger occurs pull the dbg data + * @IWL_FW_DBG_TRIGGER_MONITOR_ONLY: when trigger occurs trigger is set to + * collect only monitor data + */ +enum iwl_fw_dbg_trigger_mode { + IWL_FW_DBG_TRIGGER_START = BIT(0), + IWL_FW_DBG_TRIGGER_STOP = BIT(1), + IWL_FW_DBG_TRIGGER_MONITOR_ONLY = BIT(2), +}; + +/** + * enum iwl_fw_dbg_trigger_vif_type - define the VIF type for a trigger + * @IWL_FW_DBG_CONF_VIF_ANY: any vif type + * @IWL_FW_DBG_CONF_VIF_IBSS: IBSS mode + * @IWL_FW_DBG_CONF_VIF_STATION: BSS mode + * @IWL_FW_DBG_CONF_VIF_AP: AP mode + * @IWL_FW_DBG_CONF_VIF_P2P_CLIENT: P2P Client mode + * @IWL_FW_DBG_CONF_VIF_P2P_GO: P2P GO mode + * @IWL_FW_DBG_CONF_VIF_P2P_DEVICE: P2P device + */ +enum iwl_fw_dbg_trigger_vif_type { + IWL_FW_DBG_CONF_VIF_ANY = NL80211_IFTYPE_UNSPECIFIED, + IWL_FW_DBG_CONF_VIF_IBSS = NL80211_IFTYPE_ADHOC, + IWL_FW_DBG_CONF_VIF_STATION = NL80211_IFTYPE_STATION, + IWL_FW_DBG_CONF_VIF_AP = NL80211_IFTYPE_AP, + IWL_FW_DBG_CONF_VIF_P2P_CLIENT = NL80211_IFTYPE_P2P_CLIENT, + IWL_FW_DBG_CONF_VIF_P2P_GO = NL80211_IFTYPE_P2P_GO, + IWL_FW_DBG_CONF_VIF_P2P_DEVICE = NL80211_IFTYPE_P2P_DEVICE, +}; + +/** + * struct iwl_fw_dbg_trigger_tlv - a TLV that describes the trigger + * @id: %enum iwl_fw_dbg_trigger + * @vif_type: %enum iwl_fw_dbg_trigger_vif_type + * @stop_conf_ids: bitmap of configurations this trigger relates to. + * if the mode is %IWL_FW_DBG_TRIGGER_STOP, then if the bit corresponding + * to the currently running configuration is set, the data should be + * collected. + * @stop_delay: how many milliseconds to wait before collecting the data + * after the STOP trigger fires. + * @mode: %enum iwl_fw_dbg_trigger_mode - can be stop / start of both + * @start_conf_id: if mode is %IWL_FW_DBG_TRIGGER_START, this defines what + * configuration should be applied when the triggers kicks in. + * @occurrences: number of occurrences. 0 means the trigger will never fire. + */ +struct iwl_fw_dbg_trigger_tlv { + __le32 id; + __le32 vif_type; + __le32 stop_conf_ids; + __le32 stop_delay; + u8 mode; + u8 start_conf_id; + __le16 occurrences; + __le32 reserved[2]; + + u8 data[0]; +} __packed; + +#define FW_DBG_START_FROM_ALIVE 0 +#define FW_DBG_CONF_MAX 32 +#define FW_DBG_INVALID 0xff + +/** + * struct iwl_fw_dbg_trigger_missed_bcon - configures trigger for missed beacons + * @stop_consec_missed_bcon: stop recording if threshold is crossed. + * @stop_consec_missed_bcon_since_rx: stop recording if threshold is crossed. + * @start_consec_missed_bcon: start recording if threshold is crossed. + * @start_consec_missed_bcon_since_rx: start recording if threshold is crossed. + * @reserved1: reserved + * @reserved2: reserved + */ +struct iwl_fw_dbg_trigger_missed_bcon { + __le32 stop_consec_missed_bcon; + __le32 stop_consec_missed_bcon_since_rx; + __le32 reserved2[2]; + __le32 start_consec_missed_bcon; + __le32 start_consec_missed_bcon_since_rx; + __le32 reserved1[2]; +} __packed; + +/** + * struct iwl_fw_dbg_trigger_cmd - configures trigger for messages from FW. + * cmds: the list of commands to trigger the collection on + */ +struct iwl_fw_dbg_trigger_cmd { + struct cmd { + u8 cmd_id; + u8 group_id; + } __packed cmds[16]; +} __packed; + +/** + * iwl_fw_dbg_trigger_stats - configures trigger for statistics + * @stop_offset: the offset of the value to be monitored + * @stop_threshold: the threshold above which to collect + * @start_offset: the offset of the value to be monitored + * @start_threshold: the threshold above which to start recording + */ +struct iwl_fw_dbg_trigger_stats { + __le32 stop_offset; + __le32 stop_threshold; + __le32 start_offset; + __le32 start_threshold; +} __packed; + +/** + * struct iwl_fw_dbg_trigger_low_rssi - trigger for low beacon RSSI + * @rssi: RSSI value to trigger at + */ +struct iwl_fw_dbg_trigger_low_rssi { + __le32 rssi; +} __packed; + +/** + * struct iwl_fw_dbg_trigger_mlme - configures trigger for mlme events + * @stop_auth_denied: number of denied authentication to collect + * @stop_auth_timeout: number of authentication timeout to collect + * @stop_rx_deauth: number of Rx deauth before to collect + * @stop_tx_deauth: number of Tx deauth before to collect + * @stop_assoc_denied: number of denied association to collect + * @stop_assoc_timeout: number of association timeout to collect + * @stop_connection_loss: number of connection loss to collect + * @start_auth_denied: number of denied authentication to start recording + * @start_auth_timeout: number of authentication timeout to start recording + * @start_rx_deauth: number of Rx deauth to start recording + * @start_tx_deauth: number of Tx deauth to start recording + * @start_assoc_denied: number of denied association to start recording + * @start_assoc_timeout: number of association timeout to start recording + * @start_connection_loss: number of connection loss to start recording + */ +struct iwl_fw_dbg_trigger_mlme { + u8 stop_auth_denied; + u8 stop_auth_timeout; + u8 stop_rx_deauth; + u8 stop_tx_deauth; + + u8 stop_assoc_denied; + u8 stop_assoc_timeout; + u8 stop_connection_loss; + u8 reserved; + + u8 start_auth_denied; + u8 start_auth_timeout; + u8 start_rx_deauth; + u8 start_tx_deauth; + + u8 start_assoc_denied; + u8 start_assoc_timeout; + u8 start_connection_loss; + u8 reserved2; +} __packed; + +/** + * struct iwl_fw_dbg_trigger_txq_timer - configures the Tx queue's timer + * @command_queue: timeout for the command queue in ms + * @bss: timeout for the queues of a BSS (except for TDLS queues) in ms + * @softap: timeout for the queues of a softAP in ms + * @p2p_go: timeout for the queues of a P2P GO in ms + * @p2p_client: timeout for the queues of a P2P client in ms + * @p2p_device: timeout for the queues of a P2P device in ms + * @ibss: timeout for the queues of an IBSS in ms + * @tdls: timeout for the queues of a TDLS station in ms + */ +struct iwl_fw_dbg_trigger_txq_timer { + __le32 command_queue; + __le32 bss; + __le32 softap; + __le32 p2p_go; + __le32 p2p_client; + __le32 p2p_device; + __le32 ibss; + __le32 tdls; + __le32 reserved[4]; +} __packed; + +/** + * struct iwl_fw_dbg_trigger_time_event - configures a time event trigger + * time_Events: a list of tuples . The driver will issue a + * trigger each time a time event notification that relates to time event + * id with one of the actions in the bitmap is received and + * BIT(notif->status) is set in status_bitmap. + * + */ +struct iwl_fw_dbg_trigger_time_event { + struct { + __le32 id; + __le32 action_bitmap; + __le32 status_bitmap; + } __packed time_events[16]; +} __packed; + +/** + * struct iwl_fw_dbg_trigger_ba - configures BlockAck related trigger + * rx_ba_start: tid bitmap to configure on what tid the trigger should occur + * when an Rx BlockAck session is started. + * rx_ba_stop: tid bitmap to configure on what tid the trigger should occur + * when an Rx BlockAck session is stopped. + * tx_ba_start: tid bitmap to configure on what tid the trigger should occur + * when a Tx BlockAck session is started. + * tx_ba_stop: tid bitmap to configure on what tid the trigger should occur + * when a Tx BlockAck session is stopped. + * rx_bar: tid bitmap to configure on what tid the trigger should occur + * when a BAR is received (for a Tx BlockAck session). + * tx_bar: tid bitmap to configure on what tid the trigger should occur + * when a BAR is send (for an Rx BlocAck session). + * frame_timeout: tid bitmap to configure on what tid the trigger should occur + * when a frame times out in the reodering buffer. + */ +struct iwl_fw_dbg_trigger_ba { + __le16 rx_ba_start; + __le16 rx_ba_stop; + __le16 tx_ba_start; + __le16 tx_ba_stop; + __le16 rx_bar; + __le16 tx_bar; + __le16 frame_timeout; +} __packed; + +/** + * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration. + * @id: conf id + * @usniffer: should the uSniffer image be used + * @num_of_hcmds: how many HCMDs to send are present here + * @hcmd: a variable length host command to be sent to apply the configuration. + * If there is more than one HCMD to send, they will appear one after the + * other and be sent in the order that they appear in. + * This parses IWL_UCODE_TLV_FW_DBG_CONF. The user can add up-to + * %FW_DBG_CONF_MAX configuration per run. + */ +struct iwl_fw_dbg_conf_tlv { + u8 id; + u8 usniffer; + u8 reserved; + u8 num_of_hcmds; + struct iwl_fw_dbg_conf_hcmd hcmd; +} __packed; + +/** + * struct iwl_fw_gscan_capabilities - gscan capabilities supported by FW + * @max_scan_cache_size: total space allocated for scan results (in bytes). + * @max_scan_buckets: maximum number of channel buckets. + * @max_ap_cache_per_scan: maximum number of APs that can be stored per scan. + * @max_rssi_sample_size: number of RSSI samples used for averaging RSSI. + * @max_scan_reporting_threshold: max possible report threshold. in percentage. + * @max_hotlist_aps: maximum number of entries for hotlist APs. + * @max_significant_change_aps: maximum number of entries for significant + * change APs. + * @max_bssid_history_entries: number of BSSID/RSSI entries that the device can + * hold. + */ +struct iwl_fw_gscan_capabilities { + __le32 max_scan_cache_size; + __le32 max_scan_buckets; + __le32 max_ap_cache_per_scan; + __le32 max_rssi_sample_size; + __le32 max_scan_reporting_threshold; + __le32 max_hotlist_aps; + __le32 max_significant_change_aps; + __le32 max_bssid_history_entries; +} __packed; + +#endif /* __iwl_fw_file_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h new file mode 100644 index 000000000000..84ec0cefb62a --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h @@ -0,0 +1,322 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_fw_h__ +#define __iwl_fw_h__ +#include +#include + +#include "iwl-fw-file.h" +#include "iwl-fw-error-dump.h" + +/** + * enum iwl_ucode_type + * + * The type of ucode. + * + * @IWL_UCODE_REGULAR: Normal runtime ucode + * @IWL_UCODE_INIT: Initial ucode + * @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode + * @IWL_UCODE_REGULAR_USNIFFER: Normal runtime ucode when using usniffer image + */ +enum iwl_ucode_type { + IWL_UCODE_REGULAR, + IWL_UCODE_INIT, + IWL_UCODE_WOWLAN, + IWL_UCODE_REGULAR_USNIFFER, + IWL_UCODE_TYPE_MAX, +}; + +/* + * enumeration of ucode section. + * This enumeration is used directly for older firmware (before 16.0). + * For new firmware, there can be up to 4 sections (see below) but the + * first one packaged into the firmware file is the DATA section and + * some debugging code accesses that. + */ +enum iwl_ucode_sec { + IWL_UCODE_SECTION_DATA, + IWL_UCODE_SECTION_INST, +}; + +struct iwl_ucode_capabilities { + u32 max_probe_length; + u32 n_scan_channels; + u32 standard_phy_calibration_size; + u32 flags; + unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)]; + unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)]; +}; + +static inline bool +fw_has_api(const struct iwl_ucode_capabilities *capabilities, + iwl_ucode_tlv_api_t api) +{ + return test_bit((__force long)api, capabilities->_api); +} + +static inline bool +fw_has_capa(const struct iwl_ucode_capabilities *capabilities, + iwl_ucode_tlv_capa_t capa) +{ + return test_bit((__force long)capa, capabilities->_capa); +} + +/* one for each uCode image (inst/data, init/runtime/wowlan) */ +struct fw_desc { + const void *data; /* vmalloc'ed data */ + u32 len; /* size in bytes */ + u32 offset; /* offset in the device */ +}; + +struct fw_img { + struct fw_desc sec[IWL_UCODE_SECTION_MAX]; + bool is_dual_cpus; + u32 paging_mem_size; +}; + +struct iwl_sf_region { + u32 addr; + u32 size; +}; + +/* + * Block paging calculations + */ +#define PAGE_2_EXP_SIZE 12 /* 4K == 2^12 */ +#define FW_PAGING_SIZE BIT(PAGE_2_EXP_SIZE) /* page size is 4KB */ +#define PAGE_PER_GROUP_2_EXP_SIZE 3 +/* 8 pages per group */ +#define NUM_OF_PAGE_PER_GROUP BIT(PAGE_PER_GROUP_2_EXP_SIZE) +/* don't change, support only 32KB size */ +#define PAGING_BLOCK_SIZE (NUM_OF_PAGE_PER_GROUP * FW_PAGING_SIZE) +/* 32K == 2^15 */ +#define BLOCK_2_EXP_SIZE (PAGE_2_EXP_SIZE + PAGE_PER_GROUP_2_EXP_SIZE) + +/* + * Image paging calculations + */ +#define BLOCK_PER_IMAGE_2_EXP_SIZE 5 +/* 2^5 == 32 blocks per image */ +#define NUM_OF_BLOCK_PER_IMAGE BIT(BLOCK_PER_IMAGE_2_EXP_SIZE) +/* maximum image size 1024KB */ +#define MAX_PAGING_IMAGE_SIZE (NUM_OF_BLOCK_PER_IMAGE * PAGING_BLOCK_SIZE) + +/* Virtual address signature */ +#define PAGING_ADDR_SIG 0xAA000000 + +#define PAGING_CMD_IS_SECURED BIT(9) +#define PAGING_CMD_IS_ENABLED BIT(8) +#define PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS 0 +#define PAGING_TLV_SECURE_MASK 1 + +/** + * struct iwl_fw_paging + * @fw_paging_phys: page phy pointer + * @fw_paging_block: pointer to the allocated block + * @fw_paging_size: page size + */ +struct iwl_fw_paging { + dma_addr_t fw_paging_phys; + struct page *fw_paging_block; + u32 fw_paging_size; +}; + +/** + * struct iwl_fw_cscheme_list - a cipher scheme list + * @size: a number of entries + * @cs: cipher scheme entries + */ +struct iwl_fw_cscheme_list { + u8 size; + struct iwl_fw_cipher_scheme cs[]; +} __packed; + +/** + * struct iwl_gscan_capabilities - gscan capabilities supported by FW + * @max_scan_cache_size: total space allocated for scan results (in bytes). + * @max_scan_buckets: maximum number of channel buckets. + * @max_ap_cache_per_scan: maximum number of APs that can be stored per scan. + * @max_rssi_sample_size: number of RSSI samples used for averaging RSSI. + * @max_scan_reporting_threshold: max possible report threshold. in percentage. + * @max_hotlist_aps: maximum number of entries for hotlist APs. + * @max_significant_change_aps: maximum number of entries for significant + * change APs. + * @max_bssid_history_entries: number of BSSID/RSSI entries that the device can + * hold. + */ +struct iwl_gscan_capabilities { + u32 max_scan_cache_size; + u32 max_scan_buckets; + u32 max_ap_cache_per_scan; + u32 max_rssi_sample_size; + u32 max_scan_reporting_threshold; + u32 max_hotlist_aps; + u32 max_significant_change_aps; + u32 max_bssid_history_entries; +}; + +/** + * struct iwl_fw - variables associated with the firmware + * + * @ucode_ver: ucode version from the ucode file + * @fw_version: firmware version string + * @img: ucode image like ucode_rt, ucode_init, ucode_wowlan. + * @ucode_capa: capabilities parsed from the ucode file. + * @enhance_sensitivity_table: device can do enhanced sensitivity. + * @init_evtlog_ptr: event log offset for init ucode. + * @init_evtlog_size: event log size for init ucode. + * @init_errlog_ptr: error log offfset for init ucode. + * @inst_evtlog_ptr: event log offset for runtime ucode. + * @inst_evtlog_size: event log size for runtime ucode. + * @inst_errlog_ptr: error log offfset for runtime ucode. + * @mvm_fw: indicates this is MVM firmware + * @cipher_scheme: optional external cipher scheme. + * @human_readable: human readable version + * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until + * we get the ALIVE from the uCode + * @dbg_dest_tlv: points to the destination TLV for debug + * @dbg_conf_tlv: array of pointers to configuration TLVs for debug + * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries + * @dbg_trigger_tlv: array of pointers to triggers TLVs + * @dbg_trigger_tlv_len: lengths of the @dbg_trigger_tlv entries + * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv + */ +struct iwl_fw { + u32 ucode_ver; + + char fw_version[ETHTOOL_FWVERS_LEN]; + + /* ucode images */ + struct fw_img img[IWL_UCODE_TYPE_MAX]; + + struct iwl_ucode_capabilities ucode_capa; + bool enhance_sensitivity_table; + + u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr; + u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr; + + struct iwl_tlv_calib_ctrl default_calib[IWL_UCODE_TYPE_MAX]; + u32 phy_config; + u8 valid_tx_ant; + u8 valid_rx_ant; + + bool mvm_fw; + + struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; + u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; + + u32 sdio_adma_addr; + + struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; + struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX]; + size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX]; + struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX]; + size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX]; + u8 dbg_dest_reg_num; + struct iwl_gscan_capabilities gscan_capa; +}; + +static inline const char *get_fw_dbg_mode_string(int mode) +{ + switch (mode) { + case SMEM_MODE: + return "SMEM"; + case EXTERNAL_MODE: + return "EXTERNAL_DRAM"; + case MARBH_MODE: + return "MARBH"; + case MIPI_MODE: + return "MIPI"; + default: + return "UNKNOWN"; + } +} + +static inline bool +iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id) +{ + const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id]; + + if (!conf_tlv) + return false; + + return conf_tlv->usniffer; +} + +#define iwl_fw_dbg_trigger_enabled(fw, id) ({ \ + void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \ + unlikely(__dbg_trigger); \ +}) + +static inline struct iwl_fw_dbg_trigger_tlv* +iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, u8 id) +{ + if (WARN_ON(id >= ARRAY_SIZE(fw->dbg_trigger_tlv))) + return NULL; + + return fw->dbg_trigger_tlv[id]; +} + +#endif /* __iwl_fw_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c new file mode 100644 index 000000000000..0bd9d4aad0c0 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -0,0 +1,289 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include + +#include "iwl-drv.h" +#include "iwl-io.h" +#include "iwl-csr.h" +#include "iwl-debug.h" +#include "iwl-prph.h" +#include "iwl-fh.h" + +void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val) +{ + trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val); + iwl_trans_write8(trans, ofs, val); +} +IWL_EXPORT_SYMBOL(iwl_write8); + +void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val) +{ + trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val); + iwl_trans_write32(trans, ofs, val); +} +IWL_EXPORT_SYMBOL(iwl_write32); + +u32 iwl_read32(struct iwl_trans *trans, u32 ofs) +{ + u32 val = iwl_trans_read32(trans, ofs); + + trace_iwlwifi_dev_ioread32(trans->dev, ofs, val); + return val; +} +IWL_EXPORT_SYMBOL(iwl_read32); + +#define IWL_POLL_INTERVAL 10 /* microseconds */ + +int iwl_poll_bit(struct iwl_trans *trans, u32 addr, + u32 bits, u32 mask, int timeout) +{ + int t = 0; + + do { + if ((iwl_read32(trans, addr) & mask) == (bits & mask)) + return t; + udelay(IWL_POLL_INTERVAL); + t += IWL_POLL_INTERVAL; + } while (t < timeout); + + return -ETIMEDOUT; +} +IWL_EXPORT_SYMBOL(iwl_poll_bit); + +u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) +{ + u32 value = 0x5a5a5a5a; + unsigned long flags; + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + value = iwl_read32(trans, reg); + iwl_trans_release_nic_access(trans, &flags); + } + + return value; +} +IWL_EXPORT_SYMBOL(iwl_read_direct32); + +void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) +{ + unsigned long flags; + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + iwl_write32(trans, reg, value); + iwl_trans_release_nic_access(trans, &flags); + } +} +IWL_EXPORT_SYMBOL(iwl_write_direct32); + +int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, + int timeout) +{ + int t = 0; + + do { + if ((iwl_read_direct32(trans, addr) & mask) == mask) + return t; + udelay(IWL_POLL_INTERVAL); + t += IWL_POLL_INTERVAL; + } while (t < timeout); + + return -ETIMEDOUT; +} +IWL_EXPORT_SYMBOL(iwl_poll_direct_bit); + +u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs) +{ + u32 val = iwl_trans_read_prph(trans, ofs); + trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val); + return val; +} + +void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) +{ + trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val); + iwl_trans_write_prph(trans, ofs, val); +} + +u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs) +{ + unsigned long flags; + u32 val = 0x5a5a5a5a; + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + val = __iwl_read_prph(trans, ofs); + iwl_trans_release_nic_access(trans, &flags); + } + return val; +} +IWL_EXPORT_SYMBOL(iwl_read_prph); + +void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) +{ + unsigned long flags; + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + __iwl_write_prph(trans, ofs, val); + iwl_trans_release_nic_access(trans, &flags); + } +} +IWL_EXPORT_SYMBOL(iwl_write_prph); + +int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, + u32 bits, u32 mask, int timeout) +{ + int t = 0; + + do { + if ((iwl_read_prph(trans, addr) & mask) == (bits & mask)) + return t; + udelay(IWL_POLL_INTERVAL); + t += IWL_POLL_INTERVAL; + } while (t < timeout); + + return -ETIMEDOUT; +} + +void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) +{ + unsigned long flags; + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + __iwl_write_prph(trans, ofs, + __iwl_read_prph(trans, ofs) | mask); + iwl_trans_release_nic_access(trans, &flags); + } +} +IWL_EXPORT_SYMBOL(iwl_set_bits_prph); + +void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, + u32 bits, u32 mask) +{ + unsigned long flags; + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + __iwl_write_prph(trans, ofs, + (__iwl_read_prph(trans, ofs) & mask) | bits); + iwl_trans_release_nic_access(trans, &flags); + } +} +IWL_EXPORT_SYMBOL(iwl_set_bits_mask_prph); + +void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) +{ + unsigned long flags; + u32 val; + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + val = __iwl_read_prph(trans, ofs); + __iwl_write_prph(trans, ofs, (val & ~mask)); + iwl_trans_release_nic_access(trans, &flags); + } +} +IWL_EXPORT_SYMBOL(iwl_clear_bits_prph); + +void iwl_force_nmi(struct iwl_trans *trans) +{ + if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { + iwl_write_prph(trans, DEVICE_SET_NMI_REG, + DEVICE_SET_NMI_VAL_DRV); + iwl_write_prph(trans, DEVICE_SET_NMI_REG, + DEVICE_SET_NMI_VAL_HW); + } else { + iwl_write_prph(trans, DEVICE_SET_NMI_8000_REG, + DEVICE_SET_NMI_8000_VAL); + iwl_write_prph(trans, DEVICE_SET_NMI_REG, + DEVICE_SET_NMI_VAL_DRV); + } +} +IWL_EXPORT_SYMBOL(iwl_force_nmi); + +static const char *get_fh_string(int cmd) +{ +#define IWL_CMD(x) case x: return #x + switch (cmd) { + IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); + IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); + IWL_CMD(FH_RSCSR_CHNL0_WPTR); + IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); + IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); + IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); + IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); + IWL_CMD(FH_TSSR_TX_STATUS_REG); + IWL_CMD(FH_TSSR_TX_ERROR_REG); + default: + return "UNKNOWN"; + } +#undef IWL_CMD +} + +int iwl_dump_fh(struct iwl_trans *trans, char **buf) +{ + int i; + static const u32 fh_tbl[] = { + FH_RSCSR_CHNL0_STTS_WPTR_REG, + FH_RSCSR_CHNL0_RBDCB_BASE_REG, + FH_RSCSR_CHNL0_WPTR, + FH_MEM_RCSR_CHNL0_CONFIG_REG, + FH_MEM_RSSR_SHARED_CTRL_REG, + FH_MEM_RSSR_RX_STATUS_REG, + FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, + FH_TSSR_TX_STATUS_REG, + FH_TSSR_TX_ERROR_REG + }; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (buf) { + int pos = 0; + size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; + + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + + pos += scnprintf(*buf + pos, bufsz - pos, + "FH register values:\n"); + + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) + pos += scnprintf(*buf + pos, bufsz - pos, + " %34s: 0X%08x\n", + get_fh_string(fh_tbl[i]), + iwl_read_direct32(trans, fh_tbl[i])); + + return pos; + } +#endif + + IWL_ERR(trans, "FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) + IWL_ERR(trans, " %34s: 0X%08x\n", + get_fh_string(fh_tbl[i]), + iwl_read_direct32(trans, fh_tbl[i])); + + return 0; +} diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h new file mode 100644 index 000000000000..501d0560c061 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_io_h__ +#define __iwl_io_h__ + +#include "iwl-devtrace.h" +#include "iwl-trans.h" + +void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val); +void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val); +u32 iwl_read32(struct iwl_trans *trans, u32 ofs); + +static inline void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask) +{ + iwl_trans_set_bits_mask(trans, reg, mask, mask); +} + +static inline void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask) +{ + iwl_trans_set_bits_mask(trans, reg, mask, 0); +} + +int iwl_poll_bit(struct iwl_trans *trans, u32 addr, + u32 bits, u32 mask, int timeout); +int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, + int timeout); + +u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg); +void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value); + + +u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs); +u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs); +void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); +void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); +int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, + u32 bits, u32 mask, int timeout); +void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); +void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, + u32 bits, u32 mask); +void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); +void iwl_force_nmi(struct iwl_trans *trans); + +/* Error handling */ +int iwl_dump_fh(struct iwl_trans *trans, char **buf); + +#endif diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h new file mode 100644 index 000000000000..ac2b90df8413 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -0,0 +1,129 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_modparams_h__ +#define __iwl_modparams_h__ + +#include +#include +#include +#include + +extern struct iwl_mod_params iwlwifi_mod_params; + +enum iwl_power_level { + IWL_POWER_INDEX_1, + IWL_POWER_INDEX_2, + IWL_POWER_INDEX_3, + IWL_POWER_INDEX_4, + IWL_POWER_INDEX_5, + IWL_POWER_NUM +}; + +enum iwl_disable_11n { + IWL_DISABLE_HT_ALL = BIT(0), + IWL_DISABLE_HT_TXAGG = BIT(1), + IWL_DISABLE_HT_RXAGG = BIT(2), + IWL_ENABLE_HT_TXAGG = BIT(3), +}; + +/** + * struct iwl_mod_params + * + * Holds the module parameters + * + * @sw_crypto: using hardware encryption, default = 0 + * @disable_11n: disable 11n capabilities, default = 0, + * use IWL_[DIS,EN]ABLE_HT_* constants + * @amsdu_size_8K: enable 8K amsdu size, default = 0 + * @restart_fw: restart firmware, default = 1 + * @bt_coex_active: enable bt coex, default = true + * @led_mode: system default, default = 0 + * @power_save: enable power save, default = false + * @power_level: power level, default = 1 + * @debug_level: levels are IWL_DL_* + * @ant_coupling: antenna coupling in dB, default = 0 + * @d0i3_disable: disable d0i3, default = 1, + * @lar_disable: disable LAR (regulatory), default = 0 + * @fw_monitor: allow to use firmware monitor + */ +struct iwl_mod_params { + int sw_crypto; + unsigned int disable_11n; + int amsdu_size_8K; + bool restart_fw; + bool bt_coex_active; + int led_mode; + bool power_save; + int power_level; +#ifdef CONFIG_IWLWIFI_DEBUG + u32 debug_level; +#endif + int ant_coupling; + char *nvm_file; + bool uapsd_disable; + bool d0i3_disable; + bool lar_disable; + bool fw_monitor; +}; + +#endif /* #__iwl_modparams_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c new file mode 100644 index 000000000000..6caf2affbbb5 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c @@ -0,0 +1,193 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include + +#include "iwl-drv.h" +#include "iwl-notif-wait.h" + + +void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait) +{ + spin_lock_init(¬if_wait->notif_wait_lock); + INIT_LIST_HEAD(¬if_wait->notif_waits); + init_waitqueue_head(¬if_wait->notif_waitq); +} +IWL_EXPORT_SYMBOL(iwl_notification_wait_init); + +void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt) +{ + bool triggered = false; + + if (!list_empty(¬if_wait->notif_waits)) { + struct iwl_notification_wait *w; + + spin_lock(¬if_wait->notif_wait_lock); + list_for_each_entry(w, ¬if_wait->notif_waits, list) { + int i; + bool found = false; + + /* + * If it already finished (triggered) or has been + * aborted then don't evaluate it again to avoid races, + * Otherwise the function could be called again even + * though it returned true before + */ + if (w->triggered || w->aborted) + continue; + + for (i = 0; i < w->n_cmds; i++) { + if (w->cmds[i] == + WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) { + found = true; + break; + } + } + if (!found) + continue; + + if (!w->fn || w->fn(notif_wait, pkt, w->fn_data)) { + w->triggered = true; + triggered = true; + } + } + spin_unlock(¬if_wait->notif_wait_lock); + + } + + if (triggered) + wake_up_all(¬if_wait->notif_waitq); +} +IWL_EXPORT_SYMBOL(iwl_notification_wait_notify); + +void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) +{ + struct iwl_notification_wait *wait_entry; + + spin_lock(¬if_wait->notif_wait_lock); + list_for_each_entry(wait_entry, ¬if_wait->notif_waits, list) + wait_entry->aborted = true; + spin_unlock(¬if_wait->notif_wait_lock); + + wake_up_all(¬if_wait->notif_waitq); +} +IWL_EXPORT_SYMBOL(iwl_abort_notification_waits); + +void +iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait, + struct iwl_notification_wait *wait_entry, + const u16 *cmds, int n_cmds, + bool (*fn)(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data), + void *fn_data) +{ + if (WARN_ON(n_cmds > MAX_NOTIF_CMDS)) + n_cmds = MAX_NOTIF_CMDS; + + wait_entry->fn = fn; + wait_entry->fn_data = fn_data; + wait_entry->n_cmds = n_cmds; + memcpy(wait_entry->cmds, cmds, n_cmds * sizeof(u16)); + wait_entry->triggered = false; + wait_entry->aborted = false; + + spin_lock_bh(¬if_wait->notif_wait_lock); + list_add(&wait_entry->list, ¬if_wait->notif_waits); + spin_unlock_bh(¬if_wait->notif_wait_lock); +} +IWL_EXPORT_SYMBOL(iwl_init_notification_wait); + +int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, + struct iwl_notification_wait *wait_entry, + unsigned long timeout) +{ + int ret; + + ret = wait_event_timeout(notif_wait->notif_waitq, + wait_entry->triggered || wait_entry->aborted, + timeout); + + spin_lock_bh(¬if_wait->notif_wait_lock); + list_del(&wait_entry->list); + spin_unlock_bh(¬if_wait->notif_wait_lock); + + if (wait_entry->aborted) + return -EIO; + + /* return value is always >= 0 */ + if (ret <= 0) + return -ETIMEDOUT; + return 0; +} +IWL_EXPORT_SYMBOL(iwl_wait_notification); + +void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, + struct iwl_notification_wait *wait_entry) +{ + spin_lock_bh(¬if_wait->notif_wait_lock); + list_del(&wait_entry->list); + spin_unlock_bh(¬if_wait->notif_wait_lock); +} +IWL_EXPORT_SYMBOL(iwl_remove_notification); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h new file mode 100644 index 000000000000..dbe8234521de --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h @@ -0,0 +1,139 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_notif_wait_h__ +#define __iwl_notif_wait_h__ + +#include + +#include "iwl-trans.h" + +struct iwl_notif_wait_data { + struct list_head notif_waits; + spinlock_t notif_wait_lock; + wait_queue_head_t notif_waitq; +}; + +#define MAX_NOTIF_CMDS 5 + +/** + * struct iwl_notification_wait - notification wait entry + * @list: list head for global list + * @fn: Function called with the notification. If the function + * returns true, the wait is over, if it returns false then + * the waiter stays blocked. If no function is given, any + * of the listed commands will unblock the waiter. + * @cmds: command IDs + * @n_cmds: number of command IDs + * @triggered: waiter should be woken up + * @aborted: wait was aborted + * + * This structure is not used directly, to wait for a + * notification declare it on the stack, and call + * iwlagn_init_notification_wait() with appropriate + * parameters. Then do whatever will cause the ucode + * to notify the driver, and to wait for that then + * call iwlagn_wait_notification(). + * + * Each notification is one-shot. If at some point we + * need to support multi-shot notifications (which + * can't be allocated on the stack) we need to modify + * the code for them. + */ +struct iwl_notification_wait { + struct list_head list; + + bool (*fn)(struct iwl_notif_wait_data *notif_data, + struct iwl_rx_packet *pkt, void *data); + void *fn_data; + + u16 cmds[MAX_NOTIF_CMDS]; + u8 n_cmds; + bool triggered, aborted; +}; + + +/* caller functions */ +void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_data); +void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_data, + struct iwl_rx_packet *pkt); +void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_data); + +/* user functions */ +void __acquires(wait_entry) +iwl_init_notification_wait(struct iwl_notif_wait_data *notif_data, + struct iwl_notification_wait *wait_entry, + const u16 *cmds, int n_cmds, + bool (*fn)(struct iwl_notif_wait_data *notif_data, + struct iwl_rx_packet *pkt, void *data), + void *fn_data); + +int __must_check __releases(wait_entry) +iwl_wait_notification(struct iwl_notif_wait_data *notif_data, + struct iwl_notification_wait *wait_entry, + unsigned long timeout); + +void __releases(wait_entry) +iwl_remove_notification(struct iwl_notif_wait_data *notif_data, + struct iwl_notification_wait *wait_entry); + +#endif /* __iwl_notif_wait_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c new file mode 100644 index 000000000000..d82984912e04 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -0,0 +1,844 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#include +#include +#include +#include +#include +#include "iwl-drv.h" +#include "iwl-modparams.h" +#include "iwl-nvm-parse.h" + +/* NVM offsets (in words) definitions */ +enum wkp_nvm_offsets { + /* NVM HW-Section offset (in words) definitions */ + HW_ADDR = 0x15, + + /* NVM SW-Section offset (in words) definitions */ + NVM_SW_SECTION = 0x1C0, + NVM_VERSION = 0, + RADIO_CFG = 1, + SKU = 2, + N_HW_ADDRS = 3, + NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION, + + /* NVM calibration section offset (in words) definitions */ + NVM_CALIB_SECTION = 0x2B8, + XTAL_CALIB = 0x316 - NVM_CALIB_SECTION +}; + +enum family_8000_nvm_offsets { + /* NVM HW-Section offset (in words) definitions */ + HW_ADDR0_WFPM_FAMILY_8000 = 0x12, + HW_ADDR1_WFPM_FAMILY_8000 = 0x16, + HW_ADDR0_PCIE_FAMILY_8000 = 0x8A, + HW_ADDR1_PCIE_FAMILY_8000 = 0x8E, + MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1, + + /* NVM SW-Section offset (in words) definitions */ + NVM_SW_SECTION_FAMILY_8000 = 0x1C0, + NVM_VERSION_FAMILY_8000 = 0, + RADIO_CFG_FAMILY_8000 = 0, + SKU_FAMILY_8000 = 2, + N_HW_ADDRS_FAMILY_8000 = 3, + + /* NVM REGULATORY -Section offset (in words) definitions */ + NVM_CHANNELS_FAMILY_8000 = 0, + NVM_LAR_OFFSET_FAMILY_8000_OLD = 0x4C7, + NVM_LAR_OFFSET_FAMILY_8000 = 0x507, + NVM_LAR_ENABLED_FAMILY_8000 = 0x7, + + /* NVM calibration section offset (in words) definitions */ + NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8, + XTAL_CALIB_FAMILY_8000 = 0x316 - NVM_CALIB_SECTION_FAMILY_8000 +}; + +/* SKU Capabilities (actual values from NVM definition) */ +enum nvm_sku_bits { + NVM_SKU_CAP_BAND_24GHZ = BIT(0), + NVM_SKU_CAP_BAND_52GHZ = BIT(1), + NVM_SKU_CAP_11N_ENABLE = BIT(2), + NVM_SKU_CAP_11AC_ENABLE = BIT(3), + NVM_SKU_CAP_MIMO_DISABLE = BIT(5), +}; + +/* + * These are the channel numbers in the order that they are stored in the NVM + */ +static const u8 iwl_nvm_channels[] = { + /* 2.4 GHz */ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + /* 5 GHz */ + 36, 40, 44 , 48, 52, 56, 60, 64, + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, + 149, 153, 157, 161, 165 +}; + +static const u8 iwl_nvm_channels_family_8000[] = { + /* 2.4 GHz */ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + /* 5 GHz */ + 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, + 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, + 149, 153, 157, 161, 165, 169, 173, 177, 181 +}; + +#define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels) +#define IWL_NUM_CHANNELS_FAMILY_8000 ARRAY_SIZE(iwl_nvm_channels_family_8000) +#define NUM_2GHZ_CHANNELS 14 +#define NUM_2GHZ_CHANNELS_FAMILY_8000 14 +#define FIRST_2GHZ_HT_MINUS 5 +#define LAST_2GHZ_HT_PLUS 9 +#define LAST_5GHZ_HT 165 +#define LAST_5GHZ_HT_FAMILY_8000 181 +#define N_HW_ADDR_MASK 0xF + +/* rate data (static) */ +static struct ieee80211_rate iwl_cfg80211_rates[] = { + { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, }, + { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1, + .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, + { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2, + .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, + { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3, + .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, + { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, }, + { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, }, + { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, }, + { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, }, + { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, }, + { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, }, + { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, }, + { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, }, +}; +#define RATES_24_OFFS 0 +#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates) +#define RATES_52_OFFS 4 +#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS) + +/** + * enum iwl_nvm_channel_flags - channel flags in NVM + * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo + * @NVM_CHANNEL_IBSS: usable as an IBSS channel + * @NVM_CHANNEL_ACTIVE: active scanning allowed + * @NVM_CHANNEL_RADAR: radar detection required + * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed + * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS + * on same channel on 2.4 or same UNII band on 5.2 + * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?) + * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?) + * @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?) + * @NVM_CHANNEL_160MHZ: 160 MHz channel okay (?) + */ +enum iwl_nvm_channel_flags { + NVM_CHANNEL_VALID = BIT(0), + NVM_CHANNEL_IBSS = BIT(1), + NVM_CHANNEL_ACTIVE = BIT(3), + NVM_CHANNEL_RADAR = BIT(4), + NVM_CHANNEL_INDOOR_ONLY = BIT(5), + NVM_CHANNEL_GO_CONCURRENT = BIT(6), + NVM_CHANNEL_WIDE = BIT(8), + NVM_CHANNEL_40MHZ = BIT(9), + NVM_CHANNEL_80MHZ = BIT(10), + NVM_CHANNEL_160MHZ = BIT(11), +}; + +#define CHECK_AND_PRINT_I(x) \ + ((ch_flags & NVM_CHANNEL_##x) ? # x " " : "") + +static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz, + u16 nvm_flags, const struct iwl_cfg *cfg) +{ + u32 flags = IEEE80211_CHAN_NO_HT40; + u32 last_5ghz_ht = LAST_5GHZ_HT; + + if (cfg->device_family == IWL_DEVICE_FAMILY_8000) + last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000; + + if (!is_5ghz && (nvm_flags & NVM_CHANNEL_40MHZ)) { + if (ch_num <= LAST_2GHZ_HT_PLUS) + flags &= ~IEEE80211_CHAN_NO_HT40PLUS; + if (ch_num >= FIRST_2GHZ_HT_MINUS) + flags &= ~IEEE80211_CHAN_NO_HT40MINUS; + } else if (ch_num <= last_5ghz_ht && (nvm_flags & NVM_CHANNEL_40MHZ)) { + if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) + flags &= ~IEEE80211_CHAN_NO_HT40PLUS; + else + flags &= ~IEEE80211_CHAN_NO_HT40MINUS; + } + if (!(nvm_flags & NVM_CHANNEL_80MHZ)) + flags |= IEEE80211_CHAN_NO_80MHZ; + if (!(nvm_flags & NVM_CHANNEL_160MHZ)) + flags |= IEEE80211_CHAN_NO_160MHZ; + + if (!(nvm_flags & NVM_CHANNEL_IBSS)) + flags |= IEEE80211_CHAN_NO_IR; + + if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) + flags |= IEEE80211_CHAN_NO_IR; + + if (nvm_flags & NVM_CHANNEL_RADAR) + flags |= IEEE80211_CHAN_RADAR; + + if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) + flags |= IEEE80211_CHAN_INDOOR_ONLY; + + /* Set the GO concurrent flag only in case that NO_IR is set. + * Otherwise it is meaningless + */ + if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && + (flags & IEEE80211_CHAN_NO_IR)) + flags |= IEEE80211_CHAN_IR_CONCURRENT; + + return flags; +} + +static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + const __le16 * const nvm_ch_flags, + bool lar_supported) +{ + int ch_idx; + int n_channels = 0; + struct ieee80211_channel *channel; + u16 ch_flags; + bool is_5ghz; + int num_of_ch, num_2ghz_channels; + const u8 *nvm_chan; + + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { + num_of_ch = IWL_NUM_CHANNELS; + nvm_chan = &iwl_nvm_channels[0]; + num_2ghz_channels = NUM_2GHZ_CHANNELS; + } else { + num_of_ch = IWL_NUM_CHANNELS_FAMILY_8000; + nvm_chan = &iwl_nvm_channels_family_8000[0]; + num_2ghz_channels = NUM_2GHZ_CHANNELS_FAMILY_8000; + } + + for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { + ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx); + + if (ch_idx >= num_2ghz_channels && + !data->sku_cap_band_52GHz_enable) + continue; + + if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) { + /* + * Channels might become valid later if lar is + * supported, hence we still want to add them to + * the list of supported channels to cfg80211. + */ + IWL_DEBUG_EEPROM(dev, + "Ch. %d Flags %x [%sGHz] - No traffic\n", + nvm_chan[ch_idx], + ch_flags, + (ch_idx >= num_2ghz_channels) ? + "5.2" : "2.4"); + continue; + } + + channel = &data->channels[n_channels]; + n_channels++; + + channel->hw_value = nvm_chan[ch_idx]; + channel->band = (ch_idx < num_2ghz_channels) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + channel->center_freq = + ieee80211_channel_to_frequency( + channel->hw_value, channel->band); + + /* Initialize regulatory-based run-time data */ + + /* + * Default value - highest tx power value. max_power + * is not used in mvm, and is used for backwards compatibility + */ + channel->max_power = IWL_DEFAULT_MAX_TX_POWER; + is_5ghz = channel->band == IEEE80211_BAND_5GHZ; + + /* don't put limitations in case we're using LAR */ + if (!lar_supported) + channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx], + ch_idx, is_5ghz, + ch_flags, cfg); + else + channel->flags = 0; + + IWL_DEBUG_EEPROM(dev, + "Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", + channel->hw_value, + is_5ghz ? "5.2" : "2.4", + CHECK_AND_PRINT_I(VALID), + CHECK_AND_PRINT_I(IBSS), + CHECK_AND_PRINT_I(ACTIVE), + CHECK_AND_PRINT_I(RADAR), + CHECK_AND_PRINT_I(WIDE), + CHECK_AND_PRINT_I(INDOOR_ONLY), + CHECK_AND_PRINT_I(GO_CONCURRENT), + ch_flags, + channel->max_power, + ((ch_flags & NVM_CHANNEL_IBSS) && + !(ch_flags & NVM_CHANNEL_RADAR)) + ? "" : "not "); + } + + return n_channels; +} + +static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + struct ieee80211_sta_vht_cap *vht_cap, + u8 tx_chains, u8 rx_chains) +{ + int num_rx_ants = num_of_ant(rx_chains); + int num_tx_ants = num_of_ant(tx_chains); + unsigned int max_ampdu_exponent = (cfg->max_vht_ampdu_exponent ?: + IEEE80211_VHT_MAX_AMPDU_1024K); + + 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 | + 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | + max_ampdu_exponent << + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + + if (cfg->ht_params->ldpc) + vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC; + + if (data->sku_cap_mimo_disabled) { + num_rx_ants = 1; + num_tx_ants = 1; + } + + if (num_tx_ants > 1) + vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC; + else + vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; + + if (iwlwifi_mod_params.amsdu_size_8K) + vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; + + vht_cap->vht_mcs.rx_mcs_map = + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); + + if (num_rx_ants == 1 || cfg->rx_with_siso_diversity) { + vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN; + /* this works because NOT_SUPPORTED == 3 */ + vht_cap->vht_mcs.rx_mcs_map |= + cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2); + } + + vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; +} + +static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + const __le16 *ch_section, + u8 tx_chains, u8 rx_chains, bool lar_supported) +{ + int n_channels; + int n_used = 0; + struct ieee80211_supported_band *sband; + + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + n_channels = iwl_init_channel_map( + dev, cfg, data, + &ch_section[NVM_CHANNELS], lar_supported); + else + n_channels = iwl_init_channel_map( + dev, cfg, data, + &ch_section[NVM_CHANNELS_FAMILY_8000], + lar_supported); + + sband = &data->bands[IEEE80211_BAND_2GHZ]; + sband->band = IEEE80211_BAND_2GHZ; + sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; + sband->n_bitrates = N_RATES_24; + n_used += iwl_init_sband_channels(data, sband, n_channels, + IEEE80211_BAND_2GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ, + tx_chains, rx_chains); + + sband = &data->bands[IEEE80211_BAND_5GHZ]; + sband->band = IEEE80211_BAND_5GHZ; + sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; + sband->n_bitrates = N_RATES_52; + n_used += iwl_init_sband_channels(data, sband, n_channels, + IEEE80211_BAND_5GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, + tx_chains, rx_chains); + if (data->sku_cap_11ac_enable) + iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap, + tx_chains, rx_chains); + + if (n_channels != n_used) + IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", + n_used, n_channels); +} + +static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw, + const __le16 *phy_sku) +{ + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + SKU); + + return le32_to_cpup((__le32 *)(phy_sku + SKU_FAMILY_8000)); +} + +static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw) +{ + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + NVM_VERSION); + else + return le32_to_cpup((__le32 *)(nvm_sw + + NVM_VERSION_FAMILY_8000)); +} + +static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw, + const __le16 *phy_sku) +{ + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + RADIO_CFG); + + return le32_to_cpup((__le32 *)(phy_sku + RADIO_CFG_FAMILY_8000)); + +} + +static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw) +{ + int n_hw_addr; + + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + N_HW_ADDRS); + + n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000)); + + return n_hw_addr & N_HW_ADDR_MASK; +} + +static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + u32 radio_cfg) +{ + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { + data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg); + data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg); + data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg); + data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg); + return; + } + + /* set the radio configuration for family 8000 */ + data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK_FAMILY_8000(radio_cfg); + data->radio_cfg_step = NVM_RF_CFG_STEP_MSK_FAMILY_8000(radio_cfg); + data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK_FAMILY_8000(radio_cfg); + data->radio_cfg_pnum = NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(radio_cfg); + data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(radio_cfg); + data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(radio_cfg); +} + +static void iwl_set_hw_address(const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + const __le16 *nvm_sec) +{ + const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR); + + /* The byte order is little endian 16 bit, meaning 214365 */ + data->hw_addr[0] = hw_addr[1]; + data->hw_addr[1] = hw_addr[0]; + data->hw_addr[2] = hw_addr[3]; + data->hw_addr[3] = hw_addr[2]; + data->hw_addr[4] = hw_addr[5]; + data->hw_addr[5] = hw_addr[4]; +} + +static void iwl_set_hw_address_family_8000(struct device *dev, + const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + const __le16 *mac_override, + const __le16 *nvm_hw, + u32 mac_addr0, u32 mac_addr1) +{ + const u8 *hw_addr; + + if (mac_override) { + static const u8 reserved_mac[] = { + 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00 + }; + + hw_addr = (const u8 *)(mac_override + + MAC_ADDRESS_OVERRIDE_FAMILY_8000); + + /* + * Store the MAC address from MAO section. + * No byte swapping is required in MAO section + */ + memcpy(data->hw_addr, hw_addr, ETH_ALEN); + + /* + * Force the use of the OTP MAC address in case of reserved MAC + * address in the NVM, or if address is given but invalid. + */ + if (is_valid_ether_addr(data->hw_addr) && + memcmp(reserved_mac, hw_addr, ETH_ALEN) != 0) + return; + + IWL_ERR_DEV(dev, + "mac address from nvm override section is not valid\n"); + } + + if (nvm_hw) { + /* read the MAC address from HW resisters */ + hw_addr = (const u8 *)&mac_addr0; + data->hw_addr[0] = hw_addr[3]; + data->hw_addr[1] = hw_addr[2]; + data->hw_addr[2] = hw_addr[1]; + data->hw_addr[3] = hw_addr[0]; + + hw_addr = (const u8 *)&mac_addr1; + data->hw_addr[4] = hw_addr[1]; + data->hw_addr[5] = hw_addr[0]; + + if (!is_valid_ether_addr(data->hw_addr)) + IWL_ERR_DEV(dev, + "mac address from hw section is not valid\n"); + + return; + } + + IWL_ERR_DEV(dev, "mac address is not found\n"); +} + +#define IWL_4165_DEVICE_ID 0x5501 + +struct iwl_nvm_data * +iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, + const __le16 *nvm_hw, const __le16 *nvm_sw, + const __le16 *nvm_calib, const __le16 *regulatory, + const __le16 *mac_override, const __le16 *phy_sku, + u8 tx_chains, u8 rx_chains, bool lar_fw_supported, + u32 mac_addr0, u32 mac_addr1, u32 hw_id) +{ + struct iwl_nvm_data *data; + u32 sku; + u32 radio_cfg; + u16 lar_config; + + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + data = kzalloc(sizeof(*data) + + sizeof(struct ieee80211_channel) * + IWL_NUM_CHANNELS, + GFP_KERNEL); + else + data = kzalloc(sizeof(*data) + + sizeof(struct ieee80211_channel) * + IWL_NUM_CHANNELS_FAMILY_8000, + GFP_KERNEL); + if (!data) + return NULL; + + data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw); + + radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw, phy_sku); + iwl_set_radio_cfg(cfg, data, radio_cfg); + if (data->valid_tx_ant) + tx_chains &= data->valid_tx_ant; + if (data->valid_rx_ant) + rx_chains &= data->valid_rx_ant; + + sku = iwl_get_sku(cfg, nvm_sw, phy_sku); + data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; + data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; + data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) + data->sku_cap_11n_enable = false; + data->sku_cap_11ac_enable = data->sku_cap_11n_enable && + (sku & NVM_SKU_CAP_11AC_ENABLE); + data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE; + + /* + * OTP 0x52 bug work around + * define antenna 1x1 according to MIMO disabled + */ + if (hw_id == IWL_4165_DEVICE_ID && data->sku_cap_mimo_disabled) { + data->valid_tx_ant = ANT_B; + data->valid_rx_ant = ANT_B; + tx_chains = ANT_B; + rx_chains = ANT_B; + } + + data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw); + + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { + /* Checking for required sections */ + if (!nvm_calib) { + IWL_ERR_DEV(dev, + "Can't parse empty Calib NVM sections\n"); + kfree(data); + return NULL; + } + /* in family 8000 Xtal calibration values moved to OTP */ + data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB); + data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1); + } + + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { + iwl_set_hw_address(cfg, data, nvm_hw); + + iwl_init_sbands(dev, cfg, data, nvm_sw, + tx_chains, rx_chains, lar_fw_supported); + } else { + u16 lar_offset = data->nvm_version < 0xE39 ? + NVM_LAR_OFFSET_FAMILY_8000_OLD : + NVM_LAR_OFFSET_FAMILY_8000; + + lar_config = le16_to_cpup(regulatory + lar_offset); + data->lar_enabled = !!(lar_config & + NVM_LAR_ENABLED_FAMILY_8000); + + /* MAC address in family 8000 */ + iwl_set_hw_address_family_8000(dev, cfg, data, mac_override, + nvm_hw, mac_addr0, mac_addr1); + + iwl_init_sbands(dev, cfg, data, regulatory, + tx_chains, rx_chains, + lar_fw_supported && data->lar_enabled); + } + + data->calib_version = 255; + + return data; +} +IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); + +static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan, + int ch_idx, u16 nvm_flags, + const struct iwl_cfg *cfg) +{ + u32 flags = NL80211_RRF_NO_HT40; + u32 last_5ghz_ht = LAST_5GHZ_HT; + + if (cfg->device_family == IWL_DEVICE_FAMILY_8000) + last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000; + + if (ch_idx < NUM_2GHZ_CHANNELS && + (nvm_flags & NVM_CHANNEL_40MHZ)) { + if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS) + flags &= ~NL80211_RRF_NO_HT40PLUS; + if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS) + flags &= ~NL80211_RRF_NO_HT40MINUS; + } else if (nvm_chan[ch_idx] <= last_5ghz_ht && + (nvm_flags & NVM_CHANNEL_40MHZ)) { + if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) + flags &= ~NL80211_RRF_NO_HT40PLUS; + else + flags &= ~NL80211_RRF_NO_HT40MINUS; + } + + if (!(nvm_flags & NVM_CHANNEL_80MHZ)) + flags |= NL80211_RRF_NO_80MHZ; + if (!(nvm_flags & NVM_CHANNEL_160MHZ)) + flags |= NL80211_RRF_NO_160MHZ; + + if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) + flags |= NL80211_RRF_NO_IR; + + if (nvm_flags & NVM_CHANNEL_RADAR) + flags |= NL80211_RRF_DFS; + + if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) + flags |= NL80211_RRF_NO_OUTDOOR; + + /* Set the GO concurrent flag only in case that NO_IR is set. + * Otherwise it is meaningless + */ + if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && + (flags & NL80211_RRF_NO_IR)) + flags |= NL80211_RRF_GO_CONCURRENT; + + return flags; +} + +struct ieee80211_regdomain * +iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, + int num_of_ch, __le32 *channels, u16 fw_mcc) +{ + int ch_idx; + u16 ch_flags, prev_ch_flags = 0; + const u8 *nvm_chan = cfg->device_family == IWL_DEVICE_FAMILY_8000 ? + iwl_nvm_channels_family_8000 : iwl_nvm_channels; + struct ieee80211_regdomain *regd; + int size_of_regd; + struct ieee80211_reg_rule *rule; + enum ieee80211_band band; + int center_freq, prev_center_freq = 0; + int valid_rules = 0; + bool new_rule; + int max_num_ch = cfg->device_family == IWL_DEVICE_FAMILY_8000 ? + IWL_NUM_CHANNELS_FAMILY_8000 : IWL_NUM_CHANNELS; + + if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES)) + return ERR_PTR(-EINVAL); + + if (WARN_ON(num_of_ch > max_num_ch)) + num_of_ch = max_num_ch; + + IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n", + num_of_ch); + + /* build a regdomain rule for every valid channel */ + size_of_regd = + sizeof(struct ieee80211_regdomain) + + num_of_ch * sizeof(struct ieee80211_reg_rule); + + regd = kzalloc(size_of_regd, GFP_KERNEL); + if (!regd) + return ERR_PTR(-ENOMEM); + + for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { + ch_flags = (u16)__le32_to_cpup(channels + ch_idx); + band = (ch_idx < NUM_2GHZ_CHANNELS) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx], + band); + new_rule = false; + + if (!(ch_flags & NVM_CHANNEL_VALID)) { + IWL_DEBUG_DEV(dev, IWL_DL_LAR, + "Ch. %d Flags %x [%sGHz] - No traffic\n", + nvm_chan[ch_idx], + ch_flags, + (ch_idx >= NUM_2GHZ_CHANNELS) ? + "5.2" : "2.4"); + continue; + } + + /* we can't continue the same rule */ + if (ch_idx == 0 || prev_ch_flags != ch_flags || + center_freq - prev_center_freq > 20) { + valid_rules++; + new_rule = true; + } + + rule = ®d->reg_rules[valid_rules - 1]; + + if (new_rule) + rule->freq_range.start_freq_khz = + MHZ_TO_KHZ(center_freq - 10); + + rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10); + + /* this doesn't matter - not used by FW */ + rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); + rule->power_rule.max_eirp = + DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER); + + rule->flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx, + ch_flags, cfg); + + /* rely on auto-calculation to merge BW of contiguous chans */ + rule->flags |= NL80211_RRF_AUTO_BW; + rule->freq_range.max_bandwidth_khz = 0; + + prev_ch_flags = ch_flags; + prev_center_freq = center_freq; + + IWL_DEBUG_DEV(dev, IWL_DL_LAR, + "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s(0x%02x): Ad-Hoc %ssupported\n", + center_freq, + band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4", + CHECK_AND_PRINT_I(VALID), + CHECK_AND_PRINT_I(ACTIVE), + CHECK_AND_PRINT_I(RADAR), + CHECK_AND_PRINT_I(WIDE), + CHECK_AND_PRINT_I(40MHZ), + CHECK_AND_PRINT_I(80MHZ), + CHECK_AND_PRINT_I(160MHZ), + CHECK_AND_PRINT_I(INDOOR_ONLY), + CHECK_AND_PRINT_I(GO_CONCURRENT), + ch_flags, + ((ch_flags & NVM_CHANNEL_ACTIVE) && + !(ch_flags & NVM_CHANNEL_RADAR)) + ? "" : "not "); + } + + regd->n_reg_rules = valid_rules; + + /* set alpha2 from FW. */ + regd->alpha2[0] = fw_mcc >> 8; + regd->alpha2[1] = fw_mcc & 0xff; + + return regd; +} +IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h new file mode 100644 index 000000000000..9f44d8188c5c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -0,0 +1,97 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifndef __iwl_nvm_parse_h__ +#define __iwl_nvm_parse_h__ + +#include +#include "iwl-eeprom-parse.h" + +/** + * iwl_parse_nvm_data - parse NVM data and return values + * + * This function parses all NVM values we need and then + * returns a (newly allocated) struct containing all the + * relevant values for driver use. The struct must be freed + * later with iwl_free_nvm_data(). + */ +struct iwl_nvm_data * +iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, + const __le16 *nvm_hw, const __le16 *nvm_sw, + const __le16 *nvm_calib, const __le16 *regulatory, + const __le16 *mac_override, const __le16 *phy_sku, + u8 tx_chains, u8 rx_chains, bool lar_fw_supported, + u32 mac_addr0, u32 mac_addr1, u32 hw_id); + +/** + * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW + * + * This function parses the regulatory channel data received as a + * MCC_UPDATE_CMD command. It returns a newly allocation regulatory domain, + * to be fed into the regulatory core. An ERR_PTR is returned on error. + * If not given to the regulatory core, the user is responsible for freeing + * the regdomain returned here with kfree. + */ +struct ieee80211_regdomain * +iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, + int num_of_ch, __le32 *channels, u16 fw_mcc); + +#endif /* __iwl_nvm_parse_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h new file mode 100644 index 000000000000..2a58d6833224 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h @@ -0,0 +1,271 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_op_mode_h__ +#define __iwl_op_mode_h__ + +#include +#include + +struct iwl_op_mode; +struct iwl_trans; +struct sk_buff; +struct iwl_device_cmd; +struct iwl_rx_cmd_buffer; +struct iwl_fw; +struct iwl_cfg; + +/** + * DOC: Operational mode - what is it ? + * + * The operational mode (a.k.a. op_mode) is the layer that implements + * mac80211's handlers. It knows two APIs: mac80211's and the fw's. It uses + * the transport API to access the HW. The op_mode doesn't need to know how the + * underlying HW works, since the transport layer takes care of that. + * + * There can be several op_mode: i.e. different fw APIs will require two + * different op_modes. This is why the op_mode is virtualized. + */ + +/** + * DOC: Life cycle of the Operational mode + * + * The operational mode has a very simple life cycle. + * + * 1) The driver layer (iwl-drv.c) chooses the op_mode based on the + * capabilities advertised by the fw file (in TLV format). + * 2) The driver layer starts the op_mode (ops->start) + * 3) The op_mode registers mac80211 + * 4) The op_mode is governed by mac80211 + * 5) The driver layer stops the op_mode + */ + +/** + * struct iwl_op_mode_ops - op_mode specific operations + * + * The op_mode exports its ops so that external components can start it and + * interact with it. The driver layer typically calls the start and stop + * handlers, the transport layer calls the others. + * + * All the handlers MUST be implemented, except @rx_rss which can be left + * out *iff* the opmode will never run on hardware with multi-queue capability. + * + * @start: start the op_mode. The transport layer is already allocated. + * May sleep + * @stop: stop the op_mode. Must free all the memory allocated. + * May sleep + * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the + * HCMD this Rx responds to. Can't sleep. + * @rx_rss: data queue RX notification to the op_mode, for (data) notifications + * received on the RSS queue(s). The queue parameter indicates which of the + * RSS queues received this frame; it will always be non-zero. + * This method must not sleep. + * @queue_full: notifies that a HW queue is full. + * Must be atomic and called with BH disabled. + * @queue_not_full: notifies that a HW queue is not full any more. + * Must be atomic and called with BH disabled. + * @hw_rf_kill:notifies of a change in the HW rf kill switch. True means that + * the radio is killed. Return %true if the device should be stopped by + * the transport immediately after the call. May sleep. + * @free_skb: allows the transport layer to free skbs that haven't been + * reclaimed by the op_mode. This can happen when the driver is freed and + * there are Tx packets pending in the transport layer. + * Must be atomic + * @nic_error: error notification. Must be atomic and must be called with BH + * disabled. + * @cmd_queue_full: Called when the command queue gets full. Must be atomic and + * called with BH disabled. + * @nic_config: configure NIC, called before firmware is started. + * May sleep + * @wimax_active: invoked when WiMax becomes active. May sleep + * @enter_d0i3: configure the fw to enter d0i3. return 1 to indicate d0i3 + * entrance is aborted (e.g. due to held reference). May sleep. + * @exit_d0i3: configure the fw to exit d0i3. May sleep. + */ +struct iwl_op_mode_ops { + struct iwl_op_mode *(*start)(struct iwl_trans *trans, + const struct iwl_cfg *cfg, + const struct iwl_fw *fw, + struct dentry *dbgfs_dir); + void (*stop)(struct iwl_op_mode *op_mode); + void (*rx)(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb); + void (*rx_rss)(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, unsigned int queue); + void (*queue_full)(struct iwl_op_mode *op_mode, int queue); + void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue); + bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state); + void (*free_skb)(struct iwl_op_mode *op_mode, struct sk_buff *skb); + void (*nic_error)(struct iwl_op_mode *op_mode); + void (*cmd_queue_full)(struct iwl_op_mode *op_mode); + void (*nic_config)(struct iwl_op_mode *op_mode); + void (*wimax_active)(struct iwl_op_mode *op_mode); + int (*enter_d0i3)(struct iwl_op_mode *op_mode); + int (*exit_d0i3)(struct iwl_op_mode *op_mode); +}; + +int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops); +void iwl_opmode_deregister(const char *name); + +/** + * struct iwl_op_mode - operational mode + * @ops: pointer to its own ops + * + * This holds an implementation of the mac80211 / fw API. + */ +struct iwl_op_mode { + const struct iwl_op_mode_ops *ops; + + char op_mode_specific[0] __aligned(sizeof(void *)); +}; + +static inline void iwl_op_mode_stop(struct iwl_op_mode *op_mode) +{ + might_sleep(); + op_mode->ops->stop(op_mode); +} + +static inline void iwl_op_mode_rx(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb) +{ + return op_mode->ops->rx(op_mode, napi, rxb); +} + +static inline void iwl_op_mode_rx_rss(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, + unsigned int queue) +{ + op_mode->ops->rx_rss(op_mode, napi, rxb, queue); +} + +static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode, + int queue) +{ + op_mode->ops->queue_full(op_mode, queue); +} + +static inline void iwl_op_mode_queue_not_full(struct iwl_op_mode *op_mode, + int queue) +{ + op_mode->ops->queue_not_full(op_mode, queue); +} + +static inline bool __must_check +iwl_op_mode_hw_rf_kill(struct iwl_op_mode *op_mode, bool state) +{ + might_sleep(); + return op_mode->ops->hw_rf_kill(op_mode, state); +} + +static inline void iwl_op_mode_free_skb(struct iwl_op_mode *op_mode, + struct sk_buff *skb) +{ + op_mode->ops->free_skb(op_mode, skb); +} + +static inline void iwl_op_mode_nic_error(struct iwl_op_mode *op_mode) +{ + op_mode->ops->nic_error(op_mode); +} + +static inline void iwl_op_mode_cmd_queue_full(struct iwl_op_mode *op_mode) +{ + op_mode->ops->cmd_queue_full(op_mode); +} + +static inline void iwl_op_mode_nic_config(struct iwl_op_mode *op_mode) +{ + might_sleep(); + op_mode->ops->nic_config(op_mode); +} + +static inline void iwl_op_mode_wimax_active(struct iwl_op_mode *op_mode) +{ + might_sleep(); + op_mode->ops->wimax_active(op_mode); +} + +static inline int iwl_op_mode_enter_d0i3(struct iwl_op_mode *op_mode) +{ + might_sleep(); + + if (!op_mode->ops->enter_d0i3) + return 0; + return op_mode->ops->enter_d0i3(op_mode); +} + +static inline int iwl_op_mode_exit_d0i3(struct iwl_op_mode *op_mode) +{ + might_sleep(); + + if (!op_mode->ops->exit_d0i3) + return 0; + return op_mode->ops->exit_d0i3(op_mode); +} + +#endif /* __iwl_op_mode_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c new file mode 100644 index 000000000000..a105455b6a24 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c @@ -0,0 +1,471 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include + +#include "iwl-drv.h" +#include "iwl-phy-db.h" +#include "iwl-debug.h" +#include "iwl-op-mode.h" +#include "iwl-trans.h" + +#define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */ +#define IWL_NUM_PAPD_CH_GROUPS 9 +#define IWL_NUM_TXP_CH_GROUPS 9 + +struct iwl_phy_db_entry { + u16 size; + u8 *data; +}; + +/** + * struct iwl_phy_db - stores phy configuration and calibration data. + * + * @cfg: phy configuration. + * @calib_nch: non channel specific calibration data. + * @calib_ch: channel specific calibration data. + * @calib_ch_group_papd: calibration data related to papd channel group. + * @calib_ch_group_txp: calibration data related to tx power chanel group. + */ +struct iwl_phy_db { + struct iwl_phy_db_entry cfg; + struct iwl_phy_db_entry calib_nch; + struct iwl_phy_db_entry calib_ch_group_papd[IWL_NUM_PAPD_CH_GROUPS]; + struct iwl_phy_db_entry calib_ch_group_txp[IWL_NUM_TXP_CH_GROUPS]; + + struct iwl_trans *trans; +}; + +enum iwl_phy_db_section_type { + IWL_PHY_DB_CFG = 1, + IWL_PHY_DB_CALIB_NCH, + IWL_PHY_DB_UNUSED, + IWL_PHY_DB_CALIB_CHG_PAPD, + IWL_PHY_DB_CALIB_CHG_TXP, + IWL_PHY_DB_MAX +}; + +#define PHY_DB_CMD 0x6c /* TEMP API - The actual is 0x8c */ + +/* + * phy db - configure operational ucode + */ +struct iwl_phy_db_cmd { + __le16 type; + __le16 length; + u8 data[]; +} __packed; + +/* for parsing of tx power channel group data that comes from the firmware*/ +struct iwl_phy_db_chg_txp { + __le32 space; + __le16 max_channel_idx; +} __packed; + +/* + * phy db - Receive phy db chunk after calibrations + */ +struct iwl_calib_res_notif_phy_db { + __le16 type; + __le16 length; + u8 data[]; +} __packed; + +struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans) +{ + struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db), + GFP_KERNEL); + + if (!phy_db) + return phy_db; + + phy_db->trans = trans; + + /* TODO: add default values of the phy db. */ + return phy_db; +} +IWL_EXPORT_SYMBOL(iwl_phy_db_init); + +/* + * get phy db section: returns a pointer to a phy db section specified by + * type and channel group id. + */ +static struct iwl_phy_db_entry * +iwl_phy_db_get_section(struct iwl_phy_db *phy_db, + enum iwl_phy_db_section_type type, + u16 chg_id) +{ + if (!phy_db || type >= IWL_PHY_DB_MAX) + return NULL; + + switch (type) { + case IWL_PHY_DB_CFG: + return &phy_db->cfg; + case IWL_PHY_DB_CALIB_NCH: + return &phy_db->calib_nch; + case IWL_PHY_DB_CALIB_CHG_PAPD: + if (chg_id >= IWL_NUM_PAPD_CH_GROUPS) + return NULL; + return &phy_db->calib_ch_group_papd[chg_id]; + case IWL_PHY_DB_CALIB_CHG_TXP: + if (chg_id >= IWL_NUM_TXP_CH_GROUPS) + return NULL; + return &phy_db->calib_ch_group_txp[chg_id]; + default: + return NULL; + } + return NULL; +} + +static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db, + enum iwl_phy_db_section_type type, + u16 chg_id) +{ + struct iwl_phy_db_entry *entry = + iwl_phy_db_get_section(phy_db, type, chg_id); + if (!entry) + return; + + kfree(entry->data); + entry->data = NULL; + entry->size = 0; +} + +void iwl_phy_db_free(struct iwl_phy_db *phy_db) +{ + int i; + + if (!phy_db) + return; + + iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0); + iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0); + for (i = 0; i < IWL_NUM_PAPD_CH_GROUPS; i++) + iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i); + for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++) + iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i); + + kfree(phy_db); +} +IWL_EXPORT_SYMBOL(iwl_phy_db_free); + +int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt, + gfp_t alloc_ctx) +{ + struct iwl_calib_res_notif_phy_db *phy_db_notif = + (struct iwl_calib_res_notif_phy_db *)pkt->data; + enum iwl_phy_db_section_type type = le16_to_cpu(phy_db_notif->type); + u16 size = le16_to_cpu(phy_db_notif->length); + struct iwl_phy_db_entry *entry; + u16 chg_id = 0; + + if (!phy_db) + return -EINVAL; + + if (type == IWL_PHY_DB_CALIB_CHG_PAPD || + type == IWL_PHY_DB_CALIB_CHG_TXP) + chg_id = le16_to_cpup((__le16 *)phy_db_notif->data); + + entry = iwl_phy_db_get_section(phy_db, type, chg_id); + if (!entry) + return -EINVAL; + + kfree(entry->data); + entry->data = kmemdup(phy_db_notif->data, size, alloc_ctx); + if (!entry->data) { + entry->size = 0; + return -ENOMEM; + } + + entry->size = size; + + IWL_DEBUG_INFO(phy_db->trans, + "%s(%d): [PHYDB]SET: Type %d , Size: %d\n", + __func__, __LINE__, type, size); + + return 0; +} +IWL_EXPORT_SYMBOL(iwl_phy_db_set_section); + +static int is_valid_channel(u16 ch_id) +{ + if (ch_id <= 14 || + (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) || + (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) || + (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1)) + return 1; + return 0; +} + +static u8 ch_id_to_ch_index(u16 ch_id) +{ + if (WARN_ON(!is_valid_channel(ch_id))) + return 0xff; + + if (ch_id <= 14) + return ch_id - 1; + if (ch_id <= 64) + return (ch_id + 20) / 4; + if (ch_id <= 140) + return (ch_id - 12) / 4; + return (ch_id - 13) / 4; +} + + +static u16 channel_id_to_papd(u16 ch_id) +{ + if (WARN_ON(!is_valid_channel(ch_id))) + return 0xff; + + if (1 <= ch_id && ch_id <= 14) + return 0; + if (36 <= ch_id && ch_id <= 64) + return 1; + if (100 <= ch_id && ch_id <= 140) + return 2; + return 3; +} + +static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id) +{ + struct iwl_phy_db_chg_txp *txp_chg; + int i; + u8 ch_index = ch_id_to_ch_index(ch_id); + if (ch_index == 0xff) + return 0xff; + + for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++) { + txp_chg = (void *)phy_db->calib_ch_group_txp[i].data; + if (!txp_chg) + return 0xff; + /* + * Looking for the first channel group that its max channel is + * higher then wanted channel. + */ + if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index) + return i; + } + return 0xff; +} +static +int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db, + u32 type, u8 **data, u16 *size, u16 ch_id) +{ + struct iwl_phy_db_entry *entry; + u16 ch_group_id = 0; + + if (!phy_db) + return -EINVAL; + + /* find wanted channel group */ + if (type == IWL_PHY_DB_CALIB_CHG_PAPD) + ch_group_id = channel_id_to_papd(ch_id); + else if (type == IWL_PHY_DB_CALIB_CHG_TXP) + ch_group_id = channel_id_to_txp(phy_db, ch_id); + + entry = iwl_phy_db_get_section(phy_db, type, ch_group_id); + if (!entry) + return -EINVAL; + + *data = entry->data; + *size = entry->size; + + IWL_DEBUG_INFO(phy_db->trans, + "%s(%d): [PHYDB] GET: Type %d , Size: %d\n", + __func__, __LINE__, type, *size); + + return 0; +} + +static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type, + u16 length, void *data) +{ + struct iwl_phy_db_cmd phy_db_cmd; + struct iwl_host_cmd cmd = { + .id = PHY_DB_CMD, + }; + + IWL_DEBUG_INFO(phy_db->trans, + "Sending PHY-DB hcmd of type %d, of length %d\n", + type, length); + + /* Set phy db cmd variables */ + phy_db_cmd.type = cpu_to_le16(type); + phy_db_cmd.length = cpu_to_le16(length); + + /* Set hcmd variables */ + cmd.data[0] = &phy_db_cmd; + cmd.len[0] = sizeof(struct iwl_phy_db_cmd); + cmd.data[1] = data; + cmd.len[1] = length; + cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY; + + return iwl_trans_send_cmd(phy_db->trans, &cmd); +} + +static int iwl_phy_db_send_all_channel_groups( + struct iwl_phy_db *phy_db, + enum iwl_phy_db_section_type type, + u8 max_ch_groups) +{ + u16 i; + int err; + struct iwl_phy_db_entry *entry; + + /* Send all the channel specific groups to operational fw */ + for (i = 0; i < max_ch_groups; i++) { + entry = iwl_phy_db_get_section(phy_db, + type, + i); + if (!entry) + return -EINVAL; + + if (!entry->size) + continue; + + /* Send the requested PHY DB section */ + err = iwl_send_phy_db_cmd(phy_db, + type, + entry->size, + entry->data); + if (err) { + IWL_ERR(phy_db->trans, + "Can't SEND phy_db section %d (%d), err %d\n", + type, i, err); + return err; + } + + IWL_DEBUG_INFO(phy_db->trans, + "Sent PHY_DB HCMD, type = %d num = %d\n", + type, i); + } + + return 0; +} + +int iwl_send_phy_db_data(struct iwl_phy_db *phy_db) +{ + u8 *data = NULL; + u16 size = 0; + int err; + + IWL_DEBUG_INFO(phy_db->trans, + "Sending phy db data and configuration to runtime image\n"); + + /* Send PHY DB CFG section */ + err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG, + &data, &size, 0); + if (err) { + IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n"); + return err; + } + + err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data); + if (err) { + IWL_ERR(phy_db->trans, + "Cannot send HCMD of Phy DB cfg section\n"); + return err; + } + + err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH, + &data, &size, 0); + if (err) { + IWL_ERR(phy_db->trans, + "Cannot get Phy DB non specific channel section\n"); + return err; + } + + err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data); + if (err) { + IWL_ERR(phy_db->trans, + "Cannot send HCMD of Phy DB non specific channel section\n"); + return err; + } + + /* Send all the TXP channel specific data */ + err = iwl_phy_db_send_all_channel_groups(phy_db, + IWL_PHY_DB_CALIB_CHG_PAPD, + IWL_NUM_PAPD_CH_GROUPS); + if (err) { + IWL_ERR(phy_db->trans, + "Cannot send channel specific PAPD groups\n"); + return err; + } + + /* Send all the TXP channel specific data */ + err = iwl_phy_db_send_all_channel_groups(phy_db, + IWL_PHY_DB_CALIB_CHG_TXP, + IWL_NUM_TXP_CH_GROUPS); + if (err) { + IWL_ERR(phy_db->trans, + "Cannot send channel specific TX power groups\n"); + return err; + } + + IWL_DEBUG_INFO(phy_db->trans, + "Finished sending phy db non channel data\n"); + return 0; +} +IWL_EXPORT_SYMBOL(iwl_send_phy_db_data); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h new file mode 100644 index 000000000000..9ee18d0d2d01 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h @@ -0,0 +1,82 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __IWL_PHYDB_H__ +#define __IWL_PHYDB_H__ + +#include + +#include "iwl-op-mode.h" +#include "iwl-trans.h" + +struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans); + +void iwl_phy_db_free(struct iwl_phy_db *phy_db); + +int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt, + gfp_t alloc_ctx); + + +int iwl_send_phy_db_data(struct iwl_phy_db *phy_db); + +#endif /* __IWL_PHYDB_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h new file mode 100644 index 000000000000..3ab777f79e4f --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -0,0 +1,401 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_prph_h__ +#define __iwl_prph_h__ + +/* + * Registers in this file are internal, not PCI bus memory mapped. + * Driver accesses these via HBUS_TARG_PRPH_* registers. + */ +#define PRPH_BASE (0x00000) +#define PRPH_END (0xFFFFF) + +/* APMG (power management) constants */ +#define APMG_BASE (PRPH_BASE + 0x3000) +#define APMG_CLK_CTRL_REG (APMG_BASE + 0x0000) +#define APMG_CLK_EN_REG (APMG_BASE + 0x0004) +#define APMG_CLK_DIS_REG (APMG_BASE + 0x0008) +#define APMG_PS_CTRL_REG (APMG_BASE + 0x000c) +#define APMG_PCIDEV_STT_REG (APMG_BASE + 0x0010) +#define APMG_RFKILL_REG (APMG_BASE + 0x0014) +#define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c) +#define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020) +#define APMG_DIGITAL_SVR_REG (APMG_BASE + 0x0058) +#define APMG_ANALOG_SVR_REG (APMG_BASE + 0x006C) + +#define APMS_CLK_VAL_MRB_FUNC_MODE (0x00000001) +#define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) +#define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) + +#define APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS (0x00400000) +#define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000) +#define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000) +#define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) +#define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000) +#define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */ +#define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060) + +#define APMG_PCIDEV_STT_VAL_PERSIST_DIS (0x00000200) +#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) +#define APMG_PCIDEV_STT_VAL_WAKE_ME (0x00004000) + +#define APMG_RTC_INT_STT_RFKILL (0x10000000) + +/* Device system time */ +#define DEVICE_SYSTEM_TIME_REG 0xA0206C + +/* Device NMI register */ +#define DEVICE_SET_NMI_REG 0x00a01c30 +#define DEVICE_SET_NMI_VAL_HW BIT(0) +#define DEVICE_SET_NMI_VAL_DRV BIT(7) +#define DEVICE_SET_NMI_8000_REG 0x00a01c24 +#define DEVICE_SET_NMI_8000_VAL 0x1000000 + +/* Shared registers (0x0..0x3ff, via target indirect or periphery */ +#define SHR_BASE 0x00a10000 + +/* Shared GP1 register */ +#define SHR_APMG_GP1_REG 0x01dc +#define SHR_APMG_GP1_REG_PRPH (SHR_BASE + SHR_APMG_GP1_REG) +#define SHR_APMG_GP1_WF_XTAL_LP_EN 0x00000004 +#define SHR_APMG_GP1_CHICKEN_BIT_SELECT 0x80000000 + +/* Shared DL_CFG register */ +#define SHR_APMG_DL_CFG_REG 0x01c4 +#define SHR_APMG_DL_CFG_REG_PRPH (SHR_BASE + SHR_APMG_DL_CFG_REG) +#define SHR_APMG_DL_CFG_RTCS_CLK_SELECTOR_MSK 0x000000c0 +#define SHR_APMG_DL_CFG_RTCS_CLK_INTERNAL_XTAL 0x00000080 +#define SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP 0x00000100 + +/* Shared APMG_XTAL_CFG register */ +#define SHR_APMG_XTAL_CFG_REG 0x1c0 +#define SHR_APMG_XTAL_CFG_XTAL_ON_REQ 0x80000000 + +/* + * Device reset for family 8000 + * write to bit 24 in order to reset the CPU +*/ +#define RELEASE_CPU_RESET (0x300C) +#define RELEASE_CPU_RESET_BIT BIT(24) + +/***************************************************************************** + * 7000/3000 series SHR DTS addresses * + *****************************************************************************/ + +#define SHR_MISC_WFM_DTS_EN (0x00a10024) +#define DTSC_CFG_MODE (0x00a10604) +#define DTSC_VREF_AVG (0x00a10648) +#define DTSC_VREF5_AVG (0x00a1064c) +#define DTSC_CFG_MODE_PERIODIC (0x2) +#define DTSC_PTAT_AVG (0x00a10650) + + +/** + * Tx Scheduler + * + * The Tx Scheduler selects the next frame to be transmitted, choosing TFDs + * (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in + * host DRAM. It steers each frame's Tx command (which contains the frame + * data) into one of up to 7 prioritized Tx DMA FIFO channels within the + * device. A queue maps to only one (selectable by driver) Tx DMA channel, + * but one DMA channel may take input from several queues. + * + * Tx DMA FIFOs have dedicated purposes. + * + * For 5000 series and up, they are used differently + * (cf. iwl5000_default_queue_to_tx_fifo in iwl-5000.c): + * + * 0 -- EDCA BK (background) frames, lowest priority + * 1 -- EDCA BE (best effort) frames, normal priority + * 2 -- EDCA VI (video) frames, higher priority + * 3 -- EDCA VO (voice) and management frames, highest priority + * 4 -- unused + * 5 -- unused + * 6 -- unused + * 7 -- Commands + * + * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6. + * In addition, driver can map the remaining queues to Tx DMA/FIFO + * channels 0-3 to support 11n aggregation via EDCA DMA channels. + * + * The driver sets up each queue to work in one of two modes: + * + * 1) Scheduler-Ack, in which the scheduler automatically supports a + * block-ack (BA) window of up to 64 TFDs. In this mode, each queue + * contains TFDs for a unique combination of Recipient Address (RA) + * and Traffic Identifier (TID), that is, traffic of a given + * Quality-Of-Service (QOS) priority, destined for a single station. + * + * In scheduler-ack mode, the scheduler keeps track of the Tx status of + * each frame within the BA window, including whether it's been transmitted, + * and whether it's been acknowledged by the receiving station. The device + * automatically processes block-acks received from the receiving STA, + * and reschedules un-acked frames to be retransmitted (successful + * Tx completion may end up being out-of-order). + * + * The driver must maintain the queue's Byte Count table in host DRAM + * for this mode. + * This mode does not support fragmentation. + * + * 2) FIFO (a.k.a. non-Scheduler-ACK), in which each TFD is processed in order. + * The device may automatically retry Tx, but will retry only one frame + * at a time, until receiving ACK from receiving station, or reaching + * retry limit and giving up. + * + * The command queue (#4/#9) must use this mode! + * This mode does not require use of the Byte Count table in host DRAM. + * + * Driver controls scheduler operation via 3 means: + * 1) Scheduler registers + * 2) Shared scheduler data base in internal SRAM + * 3) Shared data in host DRAM + * + * Initialization: + * + * When loading, driver should allocate memory for: + * 1) 16 TFD circular buffers, each with space for (typically) 256 TFDs. + * 2) 16 Byte Count circular buffers in 16 KBytes contiguous memory + * (1024 bytes for each queue). + * + * After receiving "Alive" response from uCode, driver must initialize + * the scheduler (especially for queue #4/#9, the command queue, otherwise + * the driver can't issue commands!): + */ +#define SCD_MEM_LOWER_BOUND (0x0000) + +/** + * Max Tx window size is the max number of contiguous TFDs that the scheduler + * can keep track of at one time when creating block-ack chains of frames. + * Note that "64" matches the number of ack bits in a block-ack packet. + */ +#define SCD_WIN_SIZE 64 +#define SCD_FRAME_LIMIT 64 + +#define SCD_TXFIFO_POS_TID (0) +#define SCD_TXFIFO_POS_RA (4) +#define SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF) + +/* agn SCD */ +#define SCD_QUEUE_STTS_REG_POS_TXF (0) +#define SCD_QUEUE_STTS_REG_POS_ACTIVE (3) +#define SCD_QUEUE_STTS_REG_POS_WSL (4) +#define SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (19) +#define SCD_QUEUE_STTS_REG_MSK (0x017F0000) + +#define SCD_QUEUE_CTX_REG1_CREDIT_POS (8) +#define SCD_QUEUE_CTX_REG1_CREDIT_MSK (0x00FFFF00) +#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_POS (24) +#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_MSK (0xFF000000) +#define SCD_QUEUE_CTX_REG2_WIN_SIZE_POS (0) +#define SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK (0x0000007F) +#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16) +#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000) +#define SCD_GP_CTRL_ENABLE_31_QUEUES BIT(0) +#define SCD_GP_CTRL_AUTO_ACTIVE_MODE BIT(18) + +/* Context Data */ +#define SCD_CONTEXT_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x600) +#define SCD_CONTEXT_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0) + +/* Tx status */ +#define SCD_TX_STTS_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0) +#define SCD_TX_STTS_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0) + +/* Translation Data */ +#define SCD_TRANS_TBL_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0) +#define SCD_TRANS_TBL_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x808) + +#define SCD_CONTEXT_QUEUE_OFFSET(x)\ + (SCD_CONTEXT_MEM_LOWER_BOUND + ((x) * 8)) + +#define SCD_TX_STTS_QUEUE_OFFSET(x)\ + (SCD_TX_STTS_MEM_LOWER_BOUND + ((x) * 16)) + +#define SCD_TRANS_TBL_OFFSET_QUEUE(x) \ + ((SCD_TRANS_TBL_MEM_LOWER_BOUND + ((x) * 2)) & 0xfffc) + +#define SCD_BASE (PRPH_BASE + 0xa02c00) + +#define SCD_SRAM_BASE_ADDR (SCD_BASE + 0x0) +#define SCD_DRAM_BASE_ADDR (SCD_BASE + 0x8) +#define SCD_AIT (SCD_BASE + 0x0c) +#define SCD_TXFACT (SCD_BASE + 0x10) +#define SCD_ACTIVE (SCD_BASE + 0x14) +#define SCD_QUEUECHAIN_SEL (SCD_BASE + 0xe8) +#define SCD_CHAINEXT_EN (SCD_BASE + 0x244) +#define SCD_AGGR_SEL (SCD_BASE + 0x248) +#define SCD_INTERRUPT_MASK (SCD_BASE + 0x108) +#define SCD_GP_CTRL (SCD_BASE + 0x1a8) +#define SCD_EN_CTRL (SCD_BASE + 0x254) + +/*********************** END TX SCHEDULER *************************************/ + +/* tcp checksum offload */ +#define RX_EN_CSUM (0x00a00d88) + +/* Oscillator clock */ +#define OSC_CLK (0xa04068) +#define OSC_CLK_FORCE_CONTROL (0x8) + +#define FH_UCODE_LOAD_STATUS (0x1AF0) +#define CSR_UCODE_LOAD_STATUS_ADDR (0x1E70) +enum secure_load_status_reg { + LMPM_CPU_UCODE_LOADING_STARTED = 0x00000001, + LMPM_CPU_HDRS_LOADING_COMPLETED = 0x00000003, + LMPM_CPU_UCODE_LOADING_COMPLETED = 0x00000007, + LMPM_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8, + LMPM_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00, +}; + +#define LMPM_SECURE_INSPECTOR_CODE_ADDR (0x1E38) +#define LMPM_SECURE_INSPECTOR_DATA_ADDR (0x1E3C) +#define LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR (0x1E78) +#define LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR (0x1E7C) + +#define LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE (0x400000) +#define LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE (0x402000) +#define LMPM_SECURE_CPU1_HDR_MEM_SPACE (0x420000) +#define LMPM_SECURE_CPU2_HDR_MEM_SPACE (0x420400) + +/* Rx FIFO */ +#define RXF_SIZE_ADDR (0xa00c88) +#define RXF_RD_D_SPACE (0xa00c40) +#define RXF_RD_WR_PTR (0xa00c50) +#define RXF_RD_RD_PTR (0xa00c54) +#define RXF_RD_FENCE_PTR (0xa00c4c) +#define RXF_SET_FENCE_MODE (0xa00c14) +#define RXF_LD_WR2FENCE (0xa00c1c) +#define RXF_FIFO_RD_FENCE_INC (0xa00c68) +#define RXF_SIZE_BYTE_CND_POS (7) +#define RXF_SIZE_BYTE_CNT_MSK (0x3ff << RXF_SIZE_BYTE_CND_POS) +#define RXF_DIFF_FROM_PREV (0x200) + +#define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10) +#define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c) + +/* Tx FIFO */ +#define TXF_FIFO_ITEM_CNT (0xa00438) +#define TXF_WR_PTR (0xa00414) +#define TXF_RD_PTR (0xa00410) +#define TXF_FENCE_PTR (0xa00418) +#define TXF_LOCK_FENCE (0xa00424) +#define TXF_LARC_NUM (0xa0043c) +#define TXF_READ_MODIFY_DATA (0xa00448) +#define TXF_READ_MODIFY_ADDR (0xa0044c) + +/* FW monitor */ +#define MON_BUFF_SAMPLE_CTL (0xa03c00) +#define MON_BUFF_BASE_ADDR (0xa03c3c) +#define MON_BUFF_END_ADDR (0xa03c40) +#define MON_BUFF_WRPTR (0xa03c44) +#define MON_BUFF_CYCLE_CNT (0xa03c48) + +#define MON_DMARB_RD_CTL_ADDR (0xa03c60) +#define MON_DMARB_RD_DATA_ADDR (0xa03c5c) + +#define DBGC_IN_SAMPLE (0xa03c00) + +/* enable the ID buf for read */ +#define WFPM_PS_CTL_CLR 0xA0300C +#define WFMP_MAC_ADDR_0 0xA03080 +#define WFMP_MAC_ADDR_1 0xA03084 +#define LMPM_PMG_EN 0xA01CEC +#define RADIO_REG_SYS_MANUAL_DFT_0 0xAD4078 +#define RFIC_REG_RD 0xAD0470 +#define WFPM_CTRL_REG 0xA03030 +enum { + ENABLE_WFPM = BIT(31), + WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK = 0x80000000, +}; + +#define AUX_MISC_REG 0xA200B0 +enum { + HW_STEP_LOCATION_BITS = 24, +}; + +#define AUX_MISC_MASTER1_EN 0xA20818 +enum aux_misc_master1_en { + AUX_MISC_MASTER1_EN_SBE_MSK = 0x1, +}; + +#define AUX_MISC_MASTER1_SMPHR_STATUS 0xA20800 +#define RSA_ENABLE 0xA24B08 +#define PREG_AUX_BUS_WPROT_0 0xA04CC0 +#define SB_CPU_1_STATUS 0xA01E30 +#define SB_CPU_2_STATUS 0xA01E34 + +/* FW chicken bits */ +#define LMPM_CHICK 0xA01FF8 +enum { + LMPM_CHICK_EXTENDED_ADDR_SPACE = BIT(0), +}; + +/* FW chicken bits */ +#define LMPM_PAGE_PASS_NOTIF 0xA03824 +enum { + LMPM_PAGE_PASS_NOTIF_POS = BIT(20), +}; + +#endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-scd.h b/drivers/net/wireless/intel/iwlwifi/iwl-scd.h new file mode 100644 index 000000000000..f2353ebf2666 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-scd.h @@ -0,0 +1,143 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __iwl_scd_h__ +#define __iwl_scd_h__ + +#include "iwl-trans.h" +#include "iwl-io.h" +#include "iwl-prph.h" + + +static inline void iwl_scd_txq_set_chain(struct iwl_trans *trans, + u16 txq_id) +{ + iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id)); +} + +static inline void iwl_scd_txq_enable_agg(struct iwl_trans *trans, + u16 txq_id) +{ + iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); +} + +static inline void iwl_scd_txq_disable_agg(struct iwl_trans *trans, + u16 txq_id) +{ + iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); +} + +static inline void iwl_scd_disable_agg(struct iwl_trans *trans) +{ + iwl_set_bits_prph(trans, SCD_AGGR_SEL, 0); +} + +static inline void iwl_scd_activate_fifos(struct iwl_trans *trans) +{ + iwl_write_prph(trans, SCD_TXFACT, IWL_MASK(0, 7)); +} + +static inline void iwl_scd_deactivate_fifos(struct iwl_trans *trans) +{ + iwl_write_prph(trans, SCD_TXFACT, 0); +} + +static inline void iwl_scd_enable_set_active(struct iwl_trans *trans, + u32 value) +{ + iwl_write_prph(trans, SCD_EN_CTRL, value); +} + +static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl) +{ + if (chnl < 20) + return SCD_BASE + 0x18 + chnl * 4; + WARN_ON_ONCE(chnl >= 32); + return SCD_BASE + 0x284 + (chnl - 20) * 4; +} + +static inline unsigned int SCD_QUEUE_RDPTR(unsigned int chnl) +{ + if (chnl < 20) + return SCD_BASE + 0x68 + chnl * 4; + WARN_ON_ONCE(chnl >= 32); + return SCD_BASE + 0x2B4 + chnl * 4; +} + +static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl) +{ + if (chnl < 20) + return SCD_BASE + 0x10c + chnl * 4; + WARN_ON_ONCE(chnl >= 32); + return SCD_BASE + 0x334 + chnl * 4; +} + +static inline void iwl_scd_txq_set_inactive(struct iwl_trans *trans, + u16 txq_id) +{ + iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), + (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)| + (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); +} + +#endif diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c new file mode 100644 index 000000000000..71610968c365 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -0,0 +1,114 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include "iwl-trans.h" + +struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, + struct device *dev, + const struct iwl_cfg *cfg, + const struct iwl_trans_ops *ops, + size_t dev_cmd_headroom) +{ + struct iwl_trans *trans; +#ifdef CONFIG_LOCKDEP + static struct lock_class_key __key; +#endif + + trans = kzalloc(sizeof(*trans) + priv_size, GFP_KERNEL); + if (!trans) + return NULL; + +#ifdef CONFIG_LOCKDEP + lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map", + &__key, 0); +#endif + + trans->dev = dev; + trans->cfg = cfg; + trans->ops = ops; + trans->dev_cmd_headroom = dev_cmd_headroom; + trans->num_rx_queues = 1; + + snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), + "iwl_cmd_pool:%s", dev_name(trans->dev)); + trans->dev_cmd_pool = + kmem_cache_create(trans->dev_cmd_pool_name, + sizeof(struct iwl_device_cmd) + + trans->dev_cmd_headroom, + sizeof(void *), + SLAB_HWCACHE_ALIGN, + NULL); + if (!trans->dev_cmd_pool) + goto free; + + return trans; + free: + kfree(trans); + return NULL; +} + +void iwl_trans_free(struct iwl_trans *trans) +{ + kmem_cache_destroy(trans->dev_cmd_pool); + kfree(trans); +} diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h new file mode 100644 index 000000000000..6f76525088f0 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -0,0 +1,1125 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_trans_h__ +#define __iwl_trans_h__ + +#include +#include /* for page_address */ +#include + +#include "iwl-debug.h" +#include "iwl-config.h" +#include "iwl-fw.h" +#include "iwl-op-mode.h" + +/** + * DOC: Transport layer - what is it ? + * + * The transport layer is the layer that deals with the HW directly. It provides + * an abstraction of the underlying HW to the upper layer. The transport layer + * doesn't provide any policy, algorithm or anything of this kind, but only + * mechanisms to make the HW do something. It is not completely stateless but + * close to it. + * We will have an implementation for each different supported bus. + */ + +/** + * DOC: Life cycle of the transport layer + * + * The transport layer has a very precise life cycle. + * + * 1) A helper function is called during the module initialization and + * registers the bus driver's ops with the transport's alloc function. + * 2) Bus's probe calls to the transport layer's allocation functions. + * Of course this function is bus specific. + * 3) This allocation functions will spawn the upper layer which will + * register mac80211. + * + * 4) At some point (i.e. mac80211's start call), the op_mode will call + * the following sequence: + * start_hw + * start_fw + * + * 5) Then when finished (or reset): + * stop_device + * + * 6) Eventually, the free function will be called. + */ + +/** + * DOC: Host command section + * + * A host command is a command issued by the upper layer to the fw. There are + * several versions of fw that have several APIs. The transport layer is + * completely agnostic to these differences. + * The transport does provide helper functionality (i.e. SYNC / ASYNC mode), + */ +#define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f) +#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8) +#define SEQ_TO_INDEX(s) ((s) & 0xff) +#define INDEX_TO_SEQ(i) ((i) & 0xff) +#define SEQ_RX_FRAME cpu_to_le16(0x8000) + +/* + * those functions retrieve specific information from + * the id field in the iwl_host_cmd struct which contains + * the command id, the group id and the version of the command + * and vice versa +*/ +static inline u8 iwl_cmd_opcode(u32 cmdid) +{ + return cmdid & 0xFF; +} + +static inline u8 iwl_cmd_groupid(u32 cmdid) +{ + return ((cmdid & 0xFF00) >> 8); +} + +static inline u8 iwl_cmd_version(u32 cmdid) +{ + return ((cmdid & 0xFF0000) >> 16); +} + +static inline u32 iwl_cmd_id(u8 opcode, u8 groupid, u8 version) +{ + return opcode + (groupid << 8) + (version << 16); +} + +/* make u16 wide id out of u8 group and opcode */ +#define WIDE_ID(grp, opcode) ((grp << 8) | opcode) + +/* due to the conversion, this group is special; new groups + * should be defined in the appropriate fw-api header files + */ +#define IWL_ALWAYS_LONG_GROUP 1 + +/** + * struct iwl_cmd_header + * + * This header format appears in the beginning of each command sent from the + * driver, and each response/notification received from uCode. + */ +struct iwl_cmd_header { + u8 cmd; /* Command ID: REPLY_RXON, etc. */ + u8 group_id; + /* + * The driver sets up the sequence number to values of its choosing. + * uCode does not use this value, but passes it back to the driver + * when sending the response to each driver-originated command, so + * the driver can match the response to the command. Since the values + * don't get used by uCode, the driver may set up an arbitrary format. + * + * There is one exception: uCode sets bit 15 when it originates + * the response/notification, i.e. when the response/notification + * is not a direct response to a command sent by the driver. For + * example, uCode issues REPLY_RX when it sends a received frame + * to the driver; it is not a direct response to any driver command. + * + * The Linux driver uses the following format: + * + * 0:7 tfd index - position within TX queue + * 8:12 TX queue id + * 13:14 reserved + * 15 unsolicited RX or uCode-originated notification + */ + __le16 sequence; +} __packed; + +/** + * struct iwl_cmd_header_wide + * + * This header format appears in the beginning of each command sent from the + * driver, and each response/notification received from uCode. + * this is the wide version that contains more information about the command + * like length, version and command type + */ +struct iwl_cmd_header_wide { + u8 cmd; + u8 group_id; + __le16 sequence; + __le16 length; + u8 reserved; + u8 version; +} __packed; + +#define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */ +#define FH_RSCSR_FRAME_INVALID 0x55550000 +#define FH_RSCSR_FRAME_ALIGN 0x40 + +struct iwl_rx_packet { + /* + * The first 4 bytes of the RX frame header contain both the RX frame + * size and some flags. + * Bit fields: + * 31: flag flush RB request + * 30: flag ignore TC (terminal counter) request + * 29: flag fast IRQ request + * 28-14: Reserved + * 13-00: RX frame size + */ + __le32 len_n_flags; + struct iwl_cmd_header hdr; + 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 ? + * + * @CMD_ASYNC: Return right away and don't wait for the response + * @CMD_WANT_SKB: Not valid with CMD_ASYNC. The caller needs the buffer of + * the response. The caller needs to call iwl_free_resp when done. + * @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the + * command queue, but after other high priority commands. Valid only + * with CMD_ASYNC. + * @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle. + * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle. + * @CMD_WAKE_UP_TRANS: The command response should wake up the trans + * (i.e. mark it as non-idle). + * @CMD_TB_BITMAP_POS: Position of the first bit for the TB bitmap. We need to + * check that we leave enough room for the TBs bitmap which needs 20 bits. + */ +enum CMD_MODE { + CMD_ASYNC = BIT(0), + CMD_WANT_SKB = BIT(1), + CMD_SEND_IN_RFKILL = BIT(2), + CMD_HIGH_PRIO = BIT(3), + CMD_SEND_IN_IDLE = BIT(4), + CMD_MAKE_TRANS_IDLE = BIT(5), + CMD_WAKE_UP_TRANS = BIT(6), + + CMD_TB_BITMAP_POS = 11, +}; + +#define DEF_CMD_PAYLOAD_SIZE 320 + +/** + * struct iwl_device_cmd + * + * For allocation of the command and tx queues, this establishes the overall + * size of the largest command we send to uCode, except for commands that + * aren't fully copied and use other TFD space. + */ +struct iwl_device_cmd { + union { + struct { + struct iwl_cmd_header hdr; /* uCode API */ + u8 payload[DEF_CMD_PAYLOAD_SIZE]; + }; + struct { + struct iwl_cmd_header_wide hdr_wide; + u8 payload_wide[DEF_CMD_PAYLOAD_SIZE - + sizeof(struct iwl_cmd_header_wide) + + sizeof(struct iwl_cmd_header)]; + }; + }; +} __packed; + +#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) + +/* + * number of transfer buffers (fragments) per transmit frame descriptor; + * this is just the driver's idea, the hardware supports 20 + */ +#define IWL_MAX_CMD_TBS_PER_TFD 2 + +/** + * struct iwl_hcmd_dataflag - flag for each one of the chunks of the command + * + * @IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's + * ring. The transport layer doesn't map the command's buffer to DMA, but + * rather copies it to a previously allocated DMA buffer. This flag tells + * the transport layer not to copy the command, but to map the existing + * buffer (that is passed in) instead. This saves the memcpy and allows + * commands that are bigger than the fixed buffer to be submitted. + * Note that a TFD entry after a NOCOPY one cannot be a normal copied one. + * @IWL_HCMD_DFL_DUP: Only valid without NOCOPY, duplicate the memory for this + * chunk internally and free it again after the command completes. This + * can (currently) be used only once per command. + * Note that a TFD entry after a DUP one cannot be a normal copied one. + */ +enum iwl_hcmd_dataflag { + IWL_HCMD_DFL_NOCOPY = BIT(0), + IWL_HCMD_DFL_DUP = BIT(1), +}; + +/** + * struct iwl_host_cmd - Host command to the uCode + * + * @data: array of chunks that composes the data of the host command + * @resp_pkt: response packet, if %CMD_WANT_SKB was set + * @_rx_page_order: (internally used to free response packet) + * @_rx_page_addr: (internally used to free response packet) + * @flags: can be CMD_* + * @len: array of the lengths of the chunks in data + * @dataflags: IWL_HCMD_DFL_* + * @id: command id of the host command, for wide commands encoding the + * version and group as well + */ +struct iwl_host_cmd { + const void *data[IWL_MAX_CMD_TBS_PER_TFD]; + struct iwl_rx_packet *resp_pkt; + unsigned long _rx_page_addr; + u32 _rx_page_order; + + u32 flags; + u32 id; + u16 len[IWL_MAX_CMD_TBS_PER_TFD]; + u8 dataflags[IWL_MAX_CMD_TBS_PER_TFD]; +}; + +static inline void iwl_free_resp(struct iwl_host_cmd *cmd) +{ + free_pages(cmd->_rx_page_addr, cmd->_rx_page_order); +} + +struct iwl_rx_cmd_buffer { + struct page *_page; + int _offset; + bool _page_stolen; + u32 _rx_page_order; + unsigned int truesize; +}; + +static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r) +{ + return (void *)((unsigned long)page_address(r->_page) + r->_offset); +} + +static inline int rxb_offset(struct iwl_rx_cmd_buffer *r) +{ + return r->_offset; +} + +static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r) +{ + r->_page_stolen = true; + get_page(r->_page); + return r->_page; +} + +static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r) +{ + __free_pages(r->_page, r->_rx_page_order); +} + +#define MAX_NO_RECLAIM_CMDS 6 + +#define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) + +/* + * Maximum number of HW queues the transport layer + * currently supports + */ +#define IWL_MAX_HW_QUEUES 32 +#define IWL_MAX_TID_COUNT 8 +#define IWL_FRAME_LIMIT 64 +#define IWL_MAX_RX_HW_QUEUES 16 + +/** + * enum iwl_wowlan_status - WoWLAN image/device status + * @IWL_D3_STATUS_ALIVE: firmware is still running after resume + * @IWL_D3_STATUS_RESET: device was reset while suspended + */ +enum iwl_d3_status { + IWL_D3_STATUS_ALIVE, + IWL_D3_STATUS_RESET, +}; + +/** + * enum iwl_trans_status: transport status flags + * @STATUS_SYNC_HCMD_ACTIVE: a SYNC command is being processed + * @STATUS_DEVICE_ENABLED: APM is enabled + * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up) + * @STATUS_INT_ENABLED: interrupts are enabled + * @STATUS_RFKILL: the HW RFkill switch is in KILL position + * @STATUS_FW_ERROR: the fw is in error state + * @STATUS_TRANS_GOING_IDLE: shutting down the trans, only special commands + * are sent + * @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent + * @STATUS_TRANS_DEAD: trans is dead - avoid any read/write operation + */ +enum iwl_trans_status { + STATUS_SYNC_HCMD_ACTIVE, + STATUS_DEVICE_ENABLED, + STATUS_TPOWER_PMI, + STATUS_INT_ENABLED, + STATUS_RFKILL, + STATUS_FW_ERROR, + STATUS_TRANS_GOING_IDLE, + STATUS_TRANS_IDLE, + STATUS_TRANS_DEAD, +}; + +/** + * struct iwl_trans_config - transport configuration + * + * @op_mode: pointer to the upper layer. + * @cmd_queue: the index of the command queue. + * Must be set before start_fw. + * @cmd_fifo: the fifo for host commands + * @cmd_q_wdg_timeout: the timeout of the watchdog timer for the command queue. + * @no_reclaim_cmds: Some devices erroneously don't set the + * SEQ_RX_FRAME bit on some notifications, this is the + * list of such notifications to filter. Max length is + * %MAX_NO_RECLAIM_CMDS. + * @n_no_reclaim_cmds: # of commands in list + * @rx_buf_size_8k: 8 kB RX buffer size needed for A-MSDUs, + * if unset 4k will be the RX buffer size + * @bc_table_dword: set to true if the BC table expects the byte count to be + * in DWORD (as opposed to bytes) + * @scd_set_active: should the transport configure the SCD for HCMD queue + * @wide_cmd_header: firmware supports wide host command header + * @command_names: array of command names, must be 256 entries + * (one for each command); for debugging only + * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until + * we get the ALIVE from the uCode + */ +struct iwl_trans_config { + struct iwl_op_mode *op_mode; + + u8 cmd_queue; + u8 cmd_fifo; + unsigned int cmd_q_wdg_timeout; + const u8 *no_reclaim_cmds; + unsigned int n_no_reclaim_cmds; + + bool rx_buf_size_8k; + bool bc_table_dword; + bool scd_set_active; + bool wide_cmd_header; + const char *const *command_names; + + u32 sdio_adma_addr; +}; + +struct iwl_trans_dump_data { + u32 len; + u8 data[]; +}; + +struct iwl_trans; + +struct iwl_trans_txq_scd_cfg { + u8 fifo; + s8 sta_id; + u8 tid; + bool aggregate; + int frame_limit; +}; + +/** + * struct iwl_trans_ops - transport specific operations + * + * All the handlers MUST be implemented + * + * @start_hw: starts the HW. If low_power is true, the NIC needs to be taken + * out of a low power state. From that point on, the HW can send + * interrupts. May sleep. + * @op_mode_leave: Turn off the HW RF kill indication if on + * May sleep + * @start_fw: allocates and inits all the resources for the transport + * layer. Also kick a fw image. + * May sleep + * @fw_alive: called when the fw sends alive notification. If the fw provides + * the SCD base address in SRAM, then provide it here, or 0 otherwise. + * May sleep + * @stop_device: stops the whole device (embedded CPU put to reset) and stops + * the HW. If low_power is true, the NIC will be put in low power state. + * From that point on, the HW will be stopped but will still issue an + * interrupt if the HW RF kill switch is triggered. + * This callback must do the right thing and not crash even if %start_hw() + * was called but not &start_fw(). May sleep. + * @d3_suspend: put the device into the correct mode for WoWLAN during + * suspend. This is optional, if not implemented WoWLAN will not be + * supported. This callback may sleep. + * @d3_resume: resume the device after WoWLAN, enabling the opmode to + * talk to the WoWLAN image to get its status. This is optional, if not + * implemented WoWLAN will not be supported. This callback may sleep. + * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted. + * If RFkill is asserted in the middle of a SYNC host command, it must + * return -ERFKILL straight away. + * May sleep only if CMD_ASYNC is not set + * @tx: send an skb + * Must be atomic + * @reclaim: free packet until ssn. Returns a list of freed packets. + * Must be atomic + * @txq_enable: setup a queue. To setup an AC queue, use the + * iwl_trans_ac_txq_enable wrapper. fw_alive must have been called before + * this one. The op_mode must not configure the HCMD queue. The scheduler + * configuration may be %NULL, in which case the hardware will not be + * configured. May sleep. + * @txq_disable: de-configure a Tx queue to send AMPDUs + * Must be atomic + * @wait_tx_queue_empty: wait until tx queues are empty. May sleep. + * @freeze_txq_timer: prevents the timer of the queue from firing until the + * queue is set to awake. Must be atomic. + * @dbgfs_register: add the dbgfs files under this directory. Files will be + * automatically deleted. + * @write8: write a u8 to a register at offset ofs from the BAR + * @write32: write a u32 to a register at offset ofs from the BAR + * @read32: read a u32 register at offset ofs from the BAR + * @read_prph: read a DWORD from a periphery register + * @write_prph: write a DWORD to a periphery register + * @read_mem: read device's SRAM in DWORD + * @write_mem: write device's SRAM in DWORD. If %buf is %NULL, then the memory + * will be zeroed. + * @configure: configure parameters required by the transport layer from + * the op_mode. May be called several times before start_fw, can't be + * called after that. + * @set_pmi: set the power pmi state + * @grab_nic_access: wake the NIC to be able to access non-HBUS regs. + * Sleeping is not allowed between grab_nic_access and + * release_nic_access. + * @release_nic_access: let the NIC go to sleep. The "flags" parameter + * must be the same one that was sent before to the grab_nic_access. + * @set_bits_mask - set SRAM register according to value and mask. + * @ref: grab a reference to the transport/FW layers, disallowing + * certain low power states + * @unref: release a reference previously taken with @ref. Note that + * initially the reference count is 1, making an initial @unref + * necessary to allow low power states. + * @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last + * TX'ed commands and similar. The buffer will be vfree'd by the caller. + * Note that the transport must fill in the proper file headers. + */ +struct iwl_trans_ops { + + int (*start_hw)(struct iwl_trans *iwl_trans, bool low_power); + void (*op_mode_leave)(struct iwl_trans *iwl_trans); + int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw, + bool run_in_rfkill); + int (*update_sf)(struct iwl_trans *trans, + struct iwl_sf_region *st_fwrd_space); + void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr); + void (*stop_device)(struct iwl_trans *trans, bool low_power); + + void (*d3_suspend)(struct iwl_trans *trans, bool test); + int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status, + bool test); + + int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd); + + int (*tx)(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_device_cmd *dev_cmd, int queue); + void (*reclaim)(struct iwl_trans *trans, int queue, int ssn, + struct sk_buff_head *skbs); + + void (*txq_enable)(struct iwl_trans *trans, int queue, u16 ssn, + const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int queue_wdg_timeout); + void (*txq_disable)(struct iwl_trans *trans, int queue, + bool configure_scd); + + int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); + int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm); + void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs, + bool freeze); + + void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val); + void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val); + u32 (*read32)(struct iwl_trans *trans, u32 ofs); + u32 (*read_prph)(struct iwl_trans *trans, u32 ofs); + void (*write_prph)(struct iwl_trans *trans, u32 ofs, u32 val); + int (*read_mem)(struct iwl_trans *trans, u32 addr, + void *buf, int dwords); + int (*write_mem)(struct iwl_trans *trans, u32 addr, + const void *buf, int dwords); + void (*configure)(struct iwl_trans *trans, + const struct iwl_trans_config *trans_cfg); + void (*set_pmi)(struct iwl_trans *trans, bool state); + bool (*grab_nic_access)(struct iwl_trans *trans, bool silent, + unsigned long *flags); + void (*release_nic_access)(struct iwl_trans *trans, + unsigned long *flags); + void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask, + u32 value); + void (*ref)(struct iwl_trans *trans); + void (*unref)(struct iwl_trans *trans); + int (*suspend)(struct iwl_trans *trans); + void (*resume)(struct iwl_trans *trans); + + struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans, + struct iwl_fw_dbg_trigger_tlv + *trigger); +}; + +/** + * enum iwl_trans_state - state of the transport layer + * + * @IWL_TRANS_NO_FW: no fw has sent an alive response + * @IWL_TRANS_FW_ALIVE: a fw has sent an alive response + */ +enum iwl_trans_state { + IWL_TRANS_NO_FW = 0, + IWL_TRANS_FW_ALIVE = 1, +}; + +/** + * enum iwl_d0i3_mode - d0i3 mode + * + * @IWL_D0I3_MODE_OFF - d0i3 is disabled + * @IWL_D0I3_MODE_ON_IDLE - enter d0i3 when device is idle + * (e.g. no active references) + * @IWL_D0I3_MODE_ON_SUSPEND - enter d0i3 only on suspend + * (in case of 'any' trigger) + */ +enum iwl_d0i3_mode { + IWL_D0I3_MODE_OFF = 0, + IWL_D0I3_MODE_ON_IDLE, + IWL_D0I3_MODE_ON_SUSPEND, +}; + +/** + * struct iwl_trans - transport common data + * + * @ops - pointer to iwl_trans_ops + * @op_mode - pointer to the op_mode + * @cfg - pointer to the configuration + * @status: a bit-mask of transport status flags + * @dev - pointer to struct device * that represents the device + * @max_skb_frags: maximum number of fragments an SKB can have when transmitted. + * 0 indicates that frag SKBs (NETIF_F_SG) aren't supported. + * @hw_id: a u32 with the ID of the device / sub-device. + * Set during transport allocation. + * @hw_id_str: a string with info about HW ID. Set during transport allocation. + * @pm_support: set to true in start_hw if link pm is supported + * @ltr_enabled: set to true if the LTR is enabled + * @num_rx_queues: number of RX queues allocated by the transport; + * the transport must set this before calling iwl_drv_start() + * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. + * The user should use iwl_trans_{alloc,free}_tx_cmd. + * @dev_cmd_headroom: room needed for the transport's private use before the + * device_cmd for Tx - for internal use only + * The user should use iwl_trans_{alloc,free}_tx_cmd. + * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before + * starting the firmware, used for tracing + * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the + * start of the 802.11 header in the @rx_mpdu_cmd + * @dflt_pwr_limit: default power limit fetched from the platform (ACPI) + * @dbg_dest_tlv: points to the destination TLV for debug + * @dbg_conf_tlv: array of pointers to configuration TLVs for debug + * @dbg_trigger_tlv: array of pointers to triggers TLVs for debug + * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv + * @paging_req_addr: The location were the FW will upload / download the pages + * from. The address is set by the opmode + * @paging_db: Pointer to the opmode paging data base, the pointer is set by + * the opmode. + * @paging_download_buf: Buffer used for copying all of the pages before + * downloading them to the FW. The buffer is allocated in the opmode + */ +struct iwl_trans { + const struct iwl_trans_ops *ops; + struct iwl_op_mode *op_mode; + const struct iwl_cfg *cfg; + enum iwl_trans_state state; + unsigned long status; + + struct device *dev; + u32 max_skb_frags; + u32 hw_rev; + u32 hw_id; + char hw_id_str[52]; + + u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size; + + bool pm_support; + bool ltr_enabled; + + u8 num_rx_queues; + + /* The following fields are internal only */ + struct kmem_cache *dev_cmd_pool; + size_t dev_cmd_headroom; + char dev_cmd_pool_name[50]; + + struct dentry *dbgfs_dir; + +#ifdef CONFIG_LOCKDEP + struct lockdep_map sync_cmd_lockdep_map; +#endif + + u64 dflt_pwr_limit; + + const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; + const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX]; + struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv; + u8 dbg_dest_reg_num; + + /* + * Paging parameters - All of the parameters should be set by the + * opmode when paging is enabled + */ + u32 paging_req_addr; + struct iwl_fw_paging *paging_db; + void *paging_download_buf; + + enum iwl_d0i3_mode d0i3_mode; + + bool wowlan_d0i3; + + /* pointer to trans specific struct */ + /*Ensure that this pointer will always be aligned to sizeof pointer */ + char trans_specific[0] __aligned(sizeof(void *)); +}; + +static inline void iwl_trans_configure(struct iwl_trans *trans, + const struct iwl_trans_config *trans_cfg) +{ + trans->op_mode = trans_cfg->op_mode; + + trans->ops->configure(trans, trans_cfg); +} + +static inline int _iwl_trans_start_hw(struct iwl_trans *trans, bool low_power) +{ + might_sleep(); + + return trans->ops->start_hw(trans, low_power); +} + +static inline int iwl_trans_start_hw(struct iwl_trans *trans) +{ + return trans->ops->start_hw(trans, true); +} + +static inline void iwl_trans_op_mode_leave(struct iwl_trans *trans) +{ + might_sleep(); + + if (trans->ops->op_mode_leave) + trans->ops->op_mode_leave(trans); + + trans->op_mode = NULL; + + trans->state = IWL_TRANS_NO_FW; +} + +static inline void iwl_trans_fw_alive(struct iwl_trans *trans, u32 scd_addr) +{ + might_sleep(); + + trans->state = IWL_TRANS_FW_ALIVE; + + trans->ops->fw_alive(trans, scd_addr); +} + +static inline int iwl_trans_start_fw(struct iwl_trans *trans, + const struct fw_img *fw, + bool run_in_rfkill) +{ + might_sleep(); + + WARN_ON_ONCE(!trans->rx_mpdu_cmd); + + clear_bit(STATUS_FW_ERROR, &trans->status); + return trans->ops->start_fw(trans, fw, run_in_rfkill); +} + +static inline int iwl_trans_update_sf(struct iwl_trans *trans, + struct iwl_sf_region *st_fwrd_space) +{ + might_sleep(); + + if (trans->ops->update_sf) + return trans->ops->update_sf(trans, st_fwrd_space); + + return 0; +} + +static inline void _iwl_trans_stop_device(struct iwl_trans *trans, + bool low_power) +{ + might_sleep(); + + trans->ops->stop_device(trans, low_power); + + trans->state = IWL_TRANS_NO_FW; +} + +static inline void iwl_trans_stop_device(struct iwl_trans *trans) +{ + _iwl_trans_stop_device(trans, true); +} + +static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test) +{ + might_sleep(); + if (trans->ops->d3_suspend) + trans->ops->d3_suspend(trans, test); +} + +static inline int iwl_trans_d3_resume(struct iwl_trans *trans, + enum iwl_d3_status *status, + bool test) +{ + might_sleep(); + if (!trans->ops->d3_resume) + return 0; + + return trans->ops->d3_resume(trans, status, test); +} + +static inline void iwl_trans_ref(struct iwl_trans *trans) +{ + if (trans->ops->ref) + trans->ops->ref(trans); +} + +static inline void iwl_trans_unref(struct iwl_trans *trans) +{ + if (trans->ops->unref) + trans->ops->unref(trans); +} + +static inline int iwl_trans_suspend(struct iwl_trans *trans) +{ + if (!trans->ops->suspend) + return 0; + + return trans->ops->suspend(trans); +} + +static inline void iwl_trans_resume(struct iwl_trans *trans) +{ + if (trans->ops->resume) + trans->ops->resume(trans); +} + +static inline struct iwl_trans_dump_data * +iwl_trans_dump_data(struct iwl_trans *trans, + struct iwl_fw_dbg_trigger_tlv *trigger) +{ + if (!trans->ops->dump_data) + return NULL; + return trans->ops->dump_data(trans, trigger); +} + +static inline int iwl_trans_send_cmd(struct iwl_trans *trans, + struct iwl_host_cmd *cmd) +{ + int ret; + + if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) && + test_bit(STATUS_RFKILL, &trans->status))) + return -ERFKILL; + + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) + return -EIO; + + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) { + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + return -EIO; + } + + if (!(cmd->flags & CMD_ASYNC)) + lock_map_acquire_read(&trans->sync_cmd_lockdep_map); + + ret = trans->ops->send_cmd(trans, cmd); + + if (!(cmd->flags & CMD_ASYNC)) + lock_map_release(&trans->sync_cmd_lockdep_map); + + return ret; +} + +static inline struct iwl_device_cmd * +iwl_trans_alloc_tx_cmd(struct iwl_trans *trans) +{ + u8 *dev_cmd_ptr = kmem_cache_alloc(trans->dev_cmd_pool, GFP_ATOMIC); + + if (unlikely(dev_cmd_ptr == NULL)) + return NULL; + + return (struct iwl_device_cmd *) + (dev_cmd_ptr + trans->dev_cmd_headroom); +} + +static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans, + struct iwl_device_cmd *dev_cmd) +{ + u8 *dev_cmd_ptr = (u8 *)dev_cmd - trans->dev_cmd_headroom; + + kmem_cache_free(trans->dev_cmd_pool, dev_cmd_ptr); +} + +static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_device_cmd *dev_cmd, int queue) +{ + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) + return -EIO; + + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + + return trans->ops->tx(trans, skb, dev_cmd, queue); +} + +static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue, + int ssn, struct sk_buff_head *skbs) +{ + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + + trans->ops->reclaim(trans, queue, ssn, skbs); +} + +static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue, + bool configure_scd) +{ + trans->ops->txq_disable(trans, queue, configure_scd); +} + +static inline void +iwl_trans_txq_enable_cfg(struct iwl_trans *trans, int queue, u16 ssn, + const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int queue_wdg_timeout) +{ + might_sleep(); + + if (unlikely((trans->state != IWL_TRANS_FW_ALIVE))) + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + + trans->ops->txq_enable(trans, queue, ssn, cfg, queue_wdg_timeout); +} + +static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, + int fifo, int sta_id, int tid, + int frame_limit, u16 ssn, + unsigned int queue_wdg_timeout) +{ + struct iwl_trans_txq_scd_cfg cfg = { + .fifo = fifo, + .sta_id = sta_id, + .tid = tid, + .frame_limit = frame_limit, + .aggregate = sta_id >= 0, + }; + + iwl_trans_txq_enable_cfg(trans, queue, ssn, &cfg, queue_wdg_timeout); +} + +static inline +void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, int fifo, + unsigned int queue_wdg_timeout) +{ + struct iwl_trans_txq_scd_cfg cfg = { + .fifo = fifo, + .sta_id = -1, + .tid = IWL_MAX_TID_COUNT, + .frame_limit = IWL_FRAME_LIMIT, + .aggregate = false, + }; + + iwl_trans_txq_enable_cfg(trans, queue, 0, &cfg, queue_wdg_timeout); +} + +static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans, + unsigned long txqs, + bool freeze) +{ + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + + if (trans->ops->freeze_txq_timer) + trans->ops->freeze_txq_timer(trans, txqs, freeze); +} + +static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans, + u32 txqs) +{ + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + + return trans->ops->wait_tx_queue_empty(trans, txqs); +} + +static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans, + struct dentry *dir) +{ + return trans->ops->dbgfs_register(trans, dir); +} + +static inline void iwl_trans_write8(struct iwl_trans *trans, u32 ofs, u8 val) +{ + trans->ops->write8(trans, ofs, val); +} + +static inline void iwl_trans_write32(struct iwl_trans *trans, u32 ofs, u32 val) +{ + trans->ops->write32(trans, ofs, val); +} + +static inline u32 iwl_trans_read32(struct iwl_trans *trans, u32 ofs) +{ + return trans->ops->read32(trans, ofs); +} + +static inline u32 iwl_trans_read_prph(struct iwl_trans *trans, u32 ofs) +{ + return trans->ops->read_prph(trans, ofs); +} + +static inline void iwl_trans_write_prph(struct iwl_trans *trans, u32 ofs, + u32 val) +{ + return trans->ops->write_prph(trans, ofs, val); +} + +static inline int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr, + void *buf, int dwords) +{ + return trans->ops->read_mem(trans, addr, buf, dwords); +} + +#define iwl_trans_read_mem_bytes(trans, addr, buf, bufsize) \ + do { \ + if (__builtin_constant_p(bufsize)) \ + BUILD_BUG_ON((bufsize) % sizeof(u32)); \ + iwl_trans_read_mem(trans, addr, buf, (bufsize) / sizeof(u32));\ + } while (0) + +static inline u32 iwl_trans_read_mem32(struct iwl_trans *trans, u32 addr) +{ + u32 value; + + if (WARN_ON(iwl_trans_read_mem(trans, addr, &value, 1))) + return 0xa5a5a5a5; + + return value; +} + +static inline int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr, + const void *buf, int dwords) +{ + return trans->ops->write_mem(trans, addr, buf, dwords); +} + +static inline u32 iwl_trans_write_mem32(struct iwl_trans *trans, u32 addr, + u32 val) +{ + return iwl_trans_write_mem(trans, addr, &val, 1); +} + +static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state) +{ + if (trans->ops->set_pmi) + trans->ops->set_pmi(trans, state); +} + +static inline void +iwl_trans_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value) +{ + trans->ops->set_bits_mask(trans, reg, mask, value); +} + +#define iwl_trans_grab_nic_access(trans, silent, flags) \ + __cond_lock(nic_access, \ + likely((trans)->ops->grab_nic_access(trans, silent, flags))) + +static inline void __releases(nic_access) +iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags) +{ + trans->ops->release_nic_access(trans, flags); + __release(nic_access); +} + +static inline void iwl_trans_fw_error(struct iwl_trans *trans) +{ + if (WARN_ON_ONCE(!trans->op_mode)) + return; + + /* prevent double restarts due to the same erroneous FW */ + if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status)) + iwl_op_mode_nic_error(trans->op_mode); +} + +/***************************************************** + * transport helper functions + *****************************************************/ +struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, + struct device *dev, + const struct iwl_cfg *cfg, + const struct iwl_trans_ops *ops, + size_t dev_cmd_headroom); +void iwl_trans_free(struct iwl_trans *trans); + +/***************************************************** +* driver (transport) register/unregister functions +******************************************************/ +int __must_check iwl_pci_register_driver(void); +void iwl_pci_unregister_driver(void); + +#endif /* __iwl_trans_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile new file mode 100644 index 000000000000..8c2c3d13b092 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile @@ -0,0 +1,12 @@ +obj-$(CONFIG_IWLMVM) += iwlmvm.o +iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o +iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o +iwlmvm-y += scan.o time-event.o rs.o +iwlmvm-y += power.o coex.o coex_legacy.o +iwlmvm-y += tt.o offloading.o tdls.o +iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o +iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o +iwlmvm-y += tof.o +iwlmvm-$(CONFIG_PM_SLEEP) += d3.o + +ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c new file mode 100644 index 000000000000..a1376539d2dc --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c @@ -0,0 +1,211 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include "fw-api.h" +#include "mvm.h" + +struct iwl_mvm_iface_iterator_data { + struct ieee80211_vif *ignore_vif; + int idx; + + struct iwl_mvm_phy_ctxt *phyctxt; + + u16 ids[MAX_MACS_IN_BINDING]; + u16 colors[MAX_MACS_IN_BINDING]; +}; + +static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action, + struct iwl_mvm_iface_iterator_data *data) +{ + struct iwl_binding_cmd cmd; + struct iwl_mvm_phy_ctxt *phyctxt = data->phyctxt; + int i, ret; + u32 status; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id, + phyctxt->color)); + cmd.action = cpu_to_le32(action); + cmd.phy = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id, + phyctxt->color)); + + for (i = 0; i < MAX_MACS_IN_BINDING; i++) + cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID); + for (i = 0; i < data->idx; i++) + cmd.macs[i] = cpu_to_le32(FW_CMD_ID_AND_COLOR(data->ids[i], + data->colors[i])); + + status = 0; + ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD, + sizeof(cmd), &cmd, &status); + if (ret) { + IWL_ERR(mvm, "Failed to send binding (action:%d): %d\n", + action, ret); + return ret; + } + + if (status) { + IWL_ERR(mvm, "Binding command failed: %u\n", status); + ret = -EIO; + } + + return ret; +} + +static void iwl_mvm_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_iface_iterator_data *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (vif == data->ignore_vif) + return; + + if (mvmvif->phy_ctxt != data->phyctxt) + return; + + if (WARN_ON_ONCE(data->idx >= MAX_MACS_IN_BINDING)) + return; + + data->ids[data->idx] = mvmvif->id; + data->colors[data->idx] = mvmvif->color; + data->idx++; +} + +static int iwl_mvm_binding_update(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mvm_phy_ctxt *phyctxt, + bool add) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_iface_iterator_data data = { + .ignore_vif = vif, + .phyctxt = phyctxt, + }; + u32 action = FW_CTXT_ACTION_MODIFY; + + lockdep_assert_held(&mvm->mutex); + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_iface_iterator, + &data); + + /* + * If there are no other interfaces yet we + * need to create a new binding. + */ + if (data.idx == 0) { + if (add) + action = FW_CTXT_ACTION_ADD; + else + action = FW_CTXT_ACTION_REMOVE; + } + + if (add) { + if (WARN_ON_ONCE(data.idx >= MAX_MACS_IN_BINDING)) + return -EINVAL; + + data.ids[data.idx] = mvmvif->id; + data.colors[data.idx] = mvmvif->color; + data.idx++; + } + + return iwl_mvm_binding_cmd(mvm, action, &data); +} + +int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) + return -EINVAL; + + /* + * Update SF - Disable if needed. if this fails, SF might still be on + * while many macs are bound, which is forbidden - so fail the binding. + */ + if (iwl_mvm_sf_update(mvm, vif, false)) + return -EINVAL; + + return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true); +} + +int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) + return -EINVAL; + + ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false); + + if (!ret) + if (iwl_mvm_sf_update(mvm, vif, true)) + IWL_ERR(mvm, "Failed to update SF state\n"); + + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c new file mode 100644 index 000000000000..e290ac67d975 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -0,0 +1,1005 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include + +#include "fw-api-coex.h" +#include "iwl-modparams.h" +#include "mvm.h" +#include "iwl-debug.h" + +/* 20MHz / 40MHz below / 40Mhz above*/ +static const __le64 iwl_ci_mask[][3] = { + /* dummy entry for channel 0 */ + {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)}, + { + cpu_to_le64(0x0000001FFFULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x00007FFFFFULL), + }, + { + cpu_to_le64(0x000000FFFFULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x0003FFFFFFULL), + }, + { + cpu_to_le64(0x000003FFFCULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x000FFFFFFCULL), + }, + { + cpu_to_le64(0x00001FFFE0ULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x007FFFFFE0ULL), + }, + { + cpu_to_le64(0x00007FFF80ULL), + cpu_to_le64(0x00007FFFFFULL), + cpu_to_le64(0x01FFFFFF80ULL), + }, + { + cpu_to_le64(0x0003FFFC00ULL), + cpu_to_le64(0x0003FFFFFFULL), + cpu_to_le64(0x0FFFFFFC00ULL), + }, + { + cpu_to_le64(0x000FFFF000ULL), + cpu_to_le64(0x000FFFFFFCULL), + cpu_to_le64(0x3FFFFFF000ULL), + }, + { + cpu_to_le64(0x007FFF8000ULL), + cpu_to_le64(0x007FFFFFE0ULL), + cpu_to_le64(0xFFFFFF8000ULL), + }, + { + cpu_to_le64(0x01FFFE0000ULL), + cpu_to_le64(0x01FFFFFF80ULL), + cpu_to_le64(0xFFFFFE0000ULL), + }, + { + cpu_to_le64(0x0FFFF00000ULL), + cpu_to_le64(0x0FFFFFFC00ULL), + cpu_to_le64(0x0ULL), + }, + { + cpu_to_le64(0x3FFFC00000ULL), + cpu_to_le64(0x3FFFFFF000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFFE000000ULL), + cpu_to_le64(0xFFFFFF8000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFF8000000ULL), + cpu_to_le64(0xFFFFFE0000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFC0000000ULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x0ULL) + }, +}; + +struct corunning_block_luts { + u8 range; + __le32 lut20[BT_COEX_CORUN_LUT_SIZE]; +}; + +/* + * Ranges for the antenna coupling calibration / co-running block LUT: + * LUT0: [ 0, 12[ + * LUT1: [12, 20[ + * LUT2: [20, 21[ + * LUT3: [21, 23[ + * LUT4: [23, 27[ + * LUT5: [27, 30[ + * LUT6: [30, 32[ + * LUT7: [32, 33[ + * LUT8: [33, - [ + */ +static const struct corunning_block_luts antenna_coupling_ranges[] = { + { + .range = 0, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 12, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 20, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 21, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 23, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 27, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 30, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 32, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 33, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, +}; + +static enum iwl_bt_coex_lut_type +iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) +{ + struct ieee80211_chanctx_conf *chanctx_conf; + enum iwl_bt_coex_lut_type ret; + u16 phy_ctx_id; + u32 primary_ch_phy_id, secondary_ch_phy_id; + + /* + * Checking that we hold mvm->mutex is a good idea, but the rate + * control can't acquire the mutex since it runs in Tx path. + * So this is racy in that case, but in the worst case, the AMPDU + * size limit will be wrong for a short time which is not a big + * issue. + */ + + rcu_read_lock(); + + chanctx_conf = rcu_dereference(vif->chanctx_conf); + + if (!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { + rcu_read_unlock(); + return BT_COEX_INVALID_LUT; + } + + ret = BT_COEX_TX_DIS_LUT; + + if (mvm->cfg->bt_shared_single_ant) { + rcu_read_unlock(); + return ret; + } + + phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); + primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id); + secondary_ch_phy_id = + le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id); + + if (primary_ch_phy_id == phy_ctx_id) + ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut); + else if (secondary_ch_phy_id == phy_ctx_id) + ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut); + /* else - default = TX TX disallowed */ + + rcu_read_unlock(); + + return ret; +} + +int iwl_send_bt_init_conf(struct iwl_mvm *mvm) +{ + struct iwl_bt_coex_cmd bt_cmd = {}; + u32 mode; + + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_send_bt_init_conf_old(mvm); + + lockdep_assert_held(&mvm->mutex); + + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { + switch (mvm->bt_force_ant_mode) { + case BT_FORCE_ANT_BT: + mode = BT_COEX_BT; + break; + case BT_FORCE_ANT_WIFI: + mode = BT_COEX_WIFI; + break; + default: + WARN_ON(1); + mode = 0; + } + + bt_cmd.mode = cpu_to_le32(mode); + goto send_cmd; + } + + mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE; + bt_cmd.mode = cpu_to_le32(mode); + + if (IWL_MVM_BT_COEX_SYNC2SCO) + bt_cmd.enabled_modules |= + cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED); + + if (iwl_mvm_bt_is_plcr_supported(mvm)) + bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED); + + if (IWL_MVM_BT_COEX_MPLUT) { + bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED); + bt_cmd.enabled_modules |= + cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED); + } + + bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET); + +send_cmd: + memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); + memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); + + return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, 0, sizeof(bt_cmd), &bt_cmd); +} + +static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, + bool enable) +{ + struct iwl_bt_coex_reduced_txp_update_cmd cmd = {}; + struct iwl_mvm_sta *mvmsta; + u32 value; + int ret; + + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); + if (!mvmsta) + return 0; + + /* nothing to do */ + if (mvmsta->bt_reduced_txpower == enable) + return 0; + + value = mvmsta->sta_id; + + if (enable) + value |= BT_REDUCED_TX_POWER_BIT; + + IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", + enable ? "en" : "dis", sta_id); + + cmd.reduced_txp = cpu_to_le32(value); + mvmsta->bt_reduced_txpower = enable; + + ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, CMD_ASYNC, + sizeof(cmd), &cmd); + + return ret; +} + +struct iwl_bt_iterator_data { + struct iwl_bt_coex_profile_notif *notif; + struct iwl_mvm *mvm; + struct ieee80211_chanctx_conf *primary; + struct ieee80211_chanctx_conf *secondary; + bool primary_ll; +}; + +static inline +void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable, int rssi) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + mvmvif->bf_data.last_bt_coex_event = rssi; + mvmvif->bf_data.bt_coex_max_thold = + enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0; + mvmvif->bf_data.bt_coex_min_thold = + enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; +} + +/* must be called under rcu_read_lock */ +static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_bt_iterator_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct ieee80211_chanctx_conf *chanctx_conf; + /* default smps_mode is AUTOMATIC - only used for client modes */ + enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; + u32 bt_activity_grading; + int ave_rssi; + + lockdep_assert_held(&mvm->mutex); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + break; + case NL80211_IFTYPE_AP: + if (!mvmvif->ap_ibss_active) + return; + break; + default: + return; + } + + chanctx_conf = rcu_dereference(vif->chanctx_conf); + + /* If channel context is invalid or not on 2.4GHz .. */ + if ((!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) { + if (vif->type == NL80211_IFTYPE_STATION) { + /* ... relax constraints and disable rssi events */ + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); + iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, + false); + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); + } + return; + } + + bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading); + if (bt_activity_grading >= BT_HIGH_TRAFFIC) + smps_mode = IEEE80211_SMPS_STATIC; + else if (bt_activity_grading >= BT_LOW_TRAFFIC) + smps_mode = IEEE80211_SMPS_DYNAMIC; + + /* relax SMPS constraints for next association */ + if (!vif->bss_conf.assoc) + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + if (mvmvif->phy_ctxt && + IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status, + mvmvif->phy_ctxt->id)) + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + IWL_DEBUG_COEX(data->mvm, + "mac %d: bt_activity_grading %d smps_req %d\n", + mvmvif->id, bt_activity_grading, smps_mode); + + if (vif->type == NL80211_IFTYPE_STATION) + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); + + /* low latency is always primary */ + if (iwl_mvm_vif_low_latency(mvmvif)) { + data->primary_ll = true; + + data->secondary = data->primary; + data->primary = chanctx_conf; + } + + if (vif->type == NL80211_IFTYPE_AP) { + if (!mvmvif->ap_ibss_active) + return; + + if (chanctx_conf == data->primary) + return; + + if (!data->primary_ll) { + /* + * downgrade the current primary no matter what its + * type is. + */ + data->secondary = data->primary; + data->primary = chanctx_conf; + } else { + /* there is low latency vif - we will be secondary */ + data->secondary = chanctx_conf; + } + return; + } + + /* + * STA / P2P Client, try to be primary if first vif. If we are in low + * latency mode, we are already in primary and just don't do much + */ + if (!data->primary || data->primary == chanctx_conf) + data->primary = chanctx_conf; + else if (!data->secondary) + /* if secondary is not NULL, it might be a GO */ + data->secondary = chanctx_conf; + + /* + * don't reduce the Tx power if one of these is true: + * we are in LOOSE + * single share antenna product + * BT is active + * we are associated + */ + if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || + mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || + le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) { + iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); + return; + } + + /* try to get the avg rssi from fw */ + ave_rssi = mvmvif->bf_data.ave_beacon_signal; + + /* if the RSSI isn't valid, fake it is very low */ + if (!ave_rssi) + ave_rssi = -100; + if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { + if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) + IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); + } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { + if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) + IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); + } + + /* Begin to monitor the RSSI: it may influence the reduced Tx power */ + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi); +} + +static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) +{ + struct iwl_bt_iterator_data data = { + .mvm = mvm, + .notif = &mvm->last_bt_notif, + }; + struct iwl_bt_coex_ci_cmd cmd = {}; + u8 ci_bw_idx; + + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + + rcu_read_lock(); + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bt_notif_iterator, &data); + + if (data.primary) { + struct ieee80211_chanctx_conf *chan = data.primary; + if (WARN_ON(!chan->def.chan)) { + rcu_read_unlock(); + return; + } + + if (chan->def.width < NL80211_CHAN_WIDTH_40) { + ci_bw_idx = 0; + } else { + if (chan->def.center_freq1 > + chan->def.chan->center_freq) + ci_bw_idx = 2; + else + ci_bw_idx = 1; + } + + cmd.bt_primary_ci = + iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; + cmd.primary_ch_phy_id = + cpu_to_le32(*((u16 *)data.primary->drv_priv)); + } + + if (data.secondary) { + struct ieee80211_chanctx_conf *chan = data.secondary; + if (WARN_ON(!data.secondary->def.chan)) { + rcu_read_unlock(); + return; + } + + if (chan->def.width < NL80211_CHAN_WIDTH_40) { + ci_bw_idx = 0; + } else { + if (chan->def.center_freq1 > + chan->def.chan->center_freq) + ci_bw_idx = 2; + else + ci_bw_idx = 1; + } + + cmd.bt_secondary_ci = + iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; + cmd.secondary_ch_phy_id = + cpu_to_le32(*((u16 *)data.secondary->drv_priv)); + } + + rcu_read_unlock(); + + /* Don't spam the fw with the same command over and over */ + if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) { + if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, + sizeof(cmd), &cmd)) + IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); + memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd)); + } +} + +void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; + + if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { + iwl_mvm_rx_bt_coex_notif_old(mvm, rxb); + return; + } + + IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); + IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); + IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", + le32_to_cpu(notif->primary_ch_lut)); + IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", + le32_to_cpu(notif->secondary_ch_lut)); + IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", + le32_to_cpu(notif->bt_activity_grading)); + + /* remember this notification for future use: rssi fluctuations */ + memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); + + iwl_mvm_bt_coex_notif_handle(mvm); +} + +void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum ieee80211_rssi_event_data rssi_event) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { + iwl_mvm_bt_rssi_event_old(mvm, vif, rssi_event); + return; + } + + lockdep_assert_held(&mvm->mutex); + + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + + /* + * Rssi update while not associated - can happen since the statistics + * are handled asynchronously + */ + if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) + return; + + /* No BT - reports should be disabled */ + if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) + return; + + IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, + rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); + + /* + * Check if rssi is good enough for reduced Tx power, but not in loose + * scheme. + */ + if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant || + iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) + ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, + false); + else + ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true); + + if (ret) + IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); +} + +#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) +#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) + +u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); + struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; + enum iwl_bt_coex_lut_type lut_type; + + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_mvm_coex_agg_time_limit_old(mvm, sta); + + if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id)) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < + BT_HIGH_TRAFFIC) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); + + if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + /* tight coex, high bt traffic, reduce AGG time limit */ + return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; +} + +bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); + struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; + enum iwl_bt_coex_lut_type lut_type; + + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_mvm_bt_coex_is_mimo_allowed_old(mvm, sta); + + if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id)) + return true; + + if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < + BT_HIGH_TRAFFIC) + return true; + + /* + * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas + * since BT is already killed. + * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while + * we Tx. + * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. + */ + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); + return lut_type != BT_COEX_LOOSE_LUT; +} + +bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant) +{ + /* there is no other antenna, shared antenna is always available */ + if (mvm->cfg->bt_shared_single_ant) + return true; + + if (ant & mvm->cfg->non_shared_ant) + return true; + + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm); + + return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < + BT_HIGH_TRAFFIC; +} + +bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm) +{ + /* there is no other antenna, shared antenna is always available */ + if (mvm->cfg->bt_shared_single_ant) + return true; + + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm); + + return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC; +} + +bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, + enum ieee80211_band band) +{ + u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading); + + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) + return iwl_mvm_bt_coex_is_tpc_allowed_old(mvm, band); + + if (band != IEEE80211_BAND_2GHZ) + return false; + + return bt_activity >= BT_LOW_TRAFFIC; +} + +u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, + struct ieee80211_tx_info *info, u8 ac) +{ + __le16 fc = hdr->frame_control; + + if (info->band != IEEE80211_BAND_2GHZ) + return 0; + + if (unlikely(mvm->bt_tx_prio)) + return mvm->bt_tx_prio - 1; + + /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */ + if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO || + is_multicast_ether_addr(hdr->addr1) || + ieee80211_is_ctl(fc) || ieee80211_is_mgmt(fc) || + ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) + return 3; + + switch (ac) { + case IEEE80211_AC_BE: + return 1; + case IEEE80211_AC_VO: + return 3; + case IEEE80211_AC_VI: + return 2; + default: + break; + } + + return 0; +} + +void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm) +{ + if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { + iwl_mvm_bt_coex_vif_change_old(mvm); + return; + } + + iwl_mvm_bt_coex_notif_handle(mvm); +} + +void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u32 ant_isolation = le32_to_cpup((void *)pkt->data); + struct iwl_bt_coex_corun_lut_update_cmd cmd = {}; + u8 __maybe_unused lower_bound, upper_bound; + u8 lut; + + if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { + iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb); + return; + } + + if (!iwl_mvm_bt_is_plcr_supported(mvm)) + return; + + lockdep_assert_held(&mvm->mutex); + + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + + if (ant_isolation == mvm->last_ant_isol) + return; + + for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++) + if (ant_isolation < antenna_coupling_ranges[lut + 1].range) + break; + + lower_bound = antenna_coupling_ranges[lut].range; + + if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1) + upper_bound = antenna_coupling_ranges[lut + 1].range; + else + upper_bound = antenna_coupling_ranges[lut].range; + + IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n", + ant_isolation, lower_bound, upper_bound, lut); + + mvm->last_ant_isol = ant_isolation; + + if (mvm->last_corun_lut == lut) + return; + + mvm->last_corun_lut = lut; + + /* For the moment, use the same LUT for 20GHz and 40GHz */ + memcpy(&cmd.corun_lut20, antenna_coupling_ranges[lut].lut20, + sizeof(cmd.corun_lut20)); + + memcpy(&cmd.corun_lut40, antenna_coupling_ranges[lut].lut20, + sizeof(cmd.corun_lut40)); + + if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_CORUN_LUT, 0, + sizeof(cmd), &cmd)) + IWL_ERR(mvm, + "failed to send BT_COEX_UPDATE_CORUN_LUT command\n"); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c new file mode 100644 index 000000000000..61c07b05fcaa --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c @@ -0,0 +1,1315 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include + +#include "fw-api-coex.h" +#include "iwl-modparams.h" +#include "mvm.h" +#include "iwl-debug.h" + +#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \ + [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \ + ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS)) + +static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1, + BT_COEX_PRIO_TBL_PRIO_BYPASS, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2, + BT_COEX_PRIO_TBL_PRIO_BYPASS, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1, + BT_COEX_PRIO_TBL_PRIO_LOW, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2, + BT_COEX_PRIO_TBL_PRIO_LOW, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1, + BT_COEX_PRIO_TBL_PRIO_HIGH, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2, + BT_COEX_PRIO_TBL_PRIO_HIGH, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM, + BT_COEX_PRIO_TBL_DISABLED, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52, + BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24, + BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE, + BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0), + 0, 0, 0, 0, 0, 0, +}; + +#undef EVENT_PRIO_ANT + +static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) +{ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return 0; + + return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0, + sizeof(struct iwl_bt_coex_prio_tbl_cmd), + &iwl_bt_prio_tbl); +} + +static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = { + cpu_to_le32(0xf0f0f0f0), /* 50% */ + cpu_to_le32(0xc0c0c0c0), /* 25% */ + cpu_to_le32(0xfcfcfcfc), /* 75% */ + cpu_to_le32(0xfefefefe), /* 87.5% */ +}; + +static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, +}; + +static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { + { + /* Tight */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xc0004000), + cpu_to_le32(0x00004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), + }, + { + /* Loose */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), + }, + { + /* Tx Tx disabled */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xeeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), + }, +}; + +/* 20MHz / 40MHz below / 40Mhz above*/ +static const __le64 iwl_ci_mask[][3] = { + /* dummy entry for channel 0 */ + {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)}, + { + cpu_to_le64(0x0000001FFFULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x00007FFFFFULL), + }, + { + cpu_to_le64(0x000000FFFFULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x0003FFFFFFULL), + }, + { + cpu_to_le64(0x000003FFFCULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x000FFFFFFCULL), + }, + { + cpu_to_le64(0x00001FFFE0ULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x007FFFFFE0ULL), + }, + { + cpu_to_le64(0x00007FFF80ULL), + cpu_to_le64(0x00007FFFFFULL), + cpu_to_le64(0x01FFFFFF80ULL), + }, + { + cpu_to_le64(0x0003FFFC00ULL), + cpu_to_le64(0x0003FFFFFFULL), + cpu_to_le64(0x0FFFFFFC00ULL), + }, + { + cpu_to_le64(0x000FFFF000ULL), + cpu_to_le64(0x000FFFFFFCULL), + cpu_to_le64(0x3FFFFFF000ULL), + }, + { + cpu_to_le64(0x007FFF8000ULL), + cpu_to_le64(0x007FFFFFE0ULL), + cpu_to_le64(0xFFFFFF8000ULL), + }, + { + cpu_to_le64(0x01FFFE0000ULL), + cpu_to_le64(0x01FFFFFF80ULL), + cpu_to_le64(0xFFFFFE0000ULL), + }, + { + cpu_to_le64(0x0FFFF00000ULL), + cpu_to_le64(0x0FFFFFFC00ULL), + cpu_to_le64(0x0ULL), + }, + { + cpu_to_le64(0x3FFFC00000ULL), + cpu_to_le64(0x3FFFFFF000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFFE000000ULL), + cpu_to_le64(0xFFFFFF8000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFF8000000ULL), + cpu_to_le64(0xFFFFFE0000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFC0000000ULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x0ULL) + }, +}; + +enum iwl_bt_kill_msk { + BT_KILL_MSK_DEFAULT, + BT_KILL_MSK_NEVER, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_MAX, +}; + +static const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = { + [BT_KILL_MSK_DEFAULT] = 0xfffffc00, + [BT_KILL_MSK_NEVER] = 0xffffffff, + [BT_KILL_MSK_ALWAYS] = 0, +}; + +static const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = { + { + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + }, + { + BT_KILL_MSK_NEVER, + BT_KILL_MSK_NEVER, + BT_KILL_MSK_NEVER, + }, + { + BT_KILL_MSK_NEVER, + BT_KILL_MSK_NEVER, + BT_KILL_MSK_NEVER, + }, + { + BT_KILL_MSK_DEFAULT, + BT_KILL_MSK_NEVER, + BT_KILL_MSK_DEFAULT, + }, +}; + +static const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = { + { + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + }, + { + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + }, + { + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_ALWAYS, + }, + { + BT_KILL_MSK_DEFAULT, + BT_KILL_MSK_ALWAYS, + BT_KILL_MSK_DEFAULT, + }, +}; + +struct corunning_block_luts { + u8 range; + __le32 lut20[BT_COEX_CORUN_LUT_SIZE]; +}; + +/* + * Ranges for the antenna coupling calibration / co-running block LUT: + * LUT0: [ 0, 12[ + * LUT1: [12, 20[ + * LUT2: [20, 21[ + * LUT3: [21, 23[ + * LUT4: [23, 27[ + * LUT5: [27, 30[ + * LUT6: [30, 32[ + * LUT7: [32, 33[ + * LUT8: [33, - [ + */ +static const struct corunning_block_luts antenna_coupling_ranges[] = { + { + .range = 0, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 12, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 20, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 21, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 23, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 27, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 30, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 32, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, + { + .range = 33, + .lut20 = { + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), + }, + }, +}; + +static enum iwl_bt_coex_lut_type +iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) +{ + struct ieee80211_chanctx_conf *chanctx_conf; + enum iwl_bt_coex_lut_type ret; + u16 phy_ctx_id; + + /* + * Checking that we hold mvm->mutex is a good idea, but the rate + * control can't acquire the mutex since it runs in Tx path. + * So this is racy in that case, but in the worst case, the AMPDU + * size limit will be wrong for a short time which is not a big + * issue. + */ + + rcu_read_lock(); + + chanctx_conf = rcu_dereference(vif->chanctx_conf); + + if (!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { + rcu_read_unlock(); + return BT_COEX_INVALID_LUT; + } + + ret = BT_COEX_TX_DIS_LUT; + + if (mvm->cfg->bt_shared_single_ant) { + rcu_read_unlock(); + return ret; + } + + phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); + + if (mvm->last_bt_ci_cmd_old.primary_ch_phy_id == phy_ctx_id) + ret = le32_to_cpu(mvm->last_bt_notif_old.primary_ch_lut); + else if (mvm->last_bt_ci_cmd_old.secondary_ch_phy_id == phy_ctx_id) + ret = le32_to_cpu(mvm->last_bt_notif_old.secondary_ch_lut); + /* else - default = TX TX disallowed */ + + rcu_read_unlock(); + + return ret; +} + +int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm) +{ + struct iwl_bt_coex_cmd_old *bt_cmd; + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + int ret; + u32 flags; + + ret = iwl_send_bt_prio_tbl(mvm); + if (ret) + return ret; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); + if (!bt_cmd) + return -ENOMEM; + cmd.data[0] = bt_cmd; + + lockdep_assert_held(&mvm->mutex); + + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { + switch (mvm->bt_force_ant_mode) { + case BT_FORCE_ANT_AUTO: + flags = BT_COEX_AUTO_OLD; + break; + case BT_FORCE_ANT_BT: + flags = BT_COEX_BT_OLD; + break; + case BT_FORCE_ANT_WIFI: + flags = BT_COEX_WIFI_OLD; + break; + default: + WARN_ON(1); + flags = 0; + } + + bt_cmd->flags = cpu_to_le32(flags); + bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE); + goto send_cmd; + } + + bt_cmd->max_kill = 5; + bt_cmd->bt4_antenna_isolation_thr = + IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS; + bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling; + bt_cmd->bt4_tx_tx_delta_freq_thr = 15; + bt_cmd->bt4_tx_rx_max_freq0 = 15; + bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT; + bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT; + + flags = iwlwifi_mod_params.bt_coex_active ? + BT_COEX_NW_OLD : BT_COEX_DISABLE_OLD; + bt_cmd->flags = cpu_to_le32(flags); + + bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE | + BT_VALID_BT_PRIO_BOOST | + BT_VALID_MAX_KILL | + BT_VALID_3W_TMRS | + BT_VALID_KILL_ACK | + BT_VALID_KILL_CTS | + BT_VALID_REDUCED_TX_POWER | + BT_VALID_LUT | + BT_VALID_WIFI_RX_SW_PRIO_BOOST | + BT_VALID_WIFI_TX_SW_PRIO_BOOST | + BT_VALID_ANT_ISOLATION | + BT_VALID_ANT_ISOLATION_THRS | + BT_VALID_TXTX_DELTA_FREQ_THRS | + BT_VALID_TXRX_MAX_FREQ_0 | + BT_VALID_SYNC_TO_SCO | + BT_VALID_TTC | + BT_VALID_RRC); + + if (IWL_MVM_BT_COEX_SYNC2SCO) + bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO); + + if (iwl_mvm_bt_is_plcr_supported(mvm)) { + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 | + BT_VALID_CORUN_LUT_40); + bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING); + } + + if (IWL_MVM_BT_COEX_MPLUT) { + bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT); + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT); + } + + if (IWL_MVM_BT_COEX_TTC) + bt_cmd->flags |= cpu_to_le32(BT_COEX_TTC); + + if (iwl_mvm_bt_is_rrc_supported(mvm)) + bt_cmd->flags |= cpu_to_le32(BT_COEX_RRC); + + if (mvm->cfg->bt_shared_single_ant) + memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant, + sizeof(iwl_single_shared_ant)); + else + memcpy(&bt_cmd->decision_lut, iwl_combined_lookup, + sizeof(iwl_combined_lookup)); + + /* Take first Co-running block LUT to get started */ + memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20, + sizeof(bt_cmd->bt4_corun_lut20)); + memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20, + sizeof(bt_cmd->bt4_corun_lut40)); + + memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost, + sizeof(iwl_bt_prio_boost)); + bt_cmd->bt4_multiprio_lut[0] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG0); + bt_cmd->bt4_multiprio_lut[1] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG1); + +send_cmd: + memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old)); + memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old)); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; +} + +static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm) +{ + struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif_old; + u32 primary_lut = le32_to_cpu(notif->primary_ch_lut); + u32 ag = le32_to_cpu(notif->bt_activity_grading); + struct iwl_bt_coex_cmd_old *bt_cmd; + u8 ack_kill_msk, cts_kill_msk; + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .data[0] = &bt_cmd, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + int ret = 0; + + lockdep_assert_held(&mvm->mutex); + + ack_kill_msk = iwl_bt_ack_kill_msk[ag][primary_lut]; + cts_kill_msk = iwl_bt_cts_kill_msk[ag][primary_lut]; + + if (mvm->bt_ack_kill_msk[0] == ack_kill_msk && + mvm->bt_cts_kill_msk[0] == cts_kill_msk) + return 0; + + mvm->bt_ack_kill_msk[0] = ack_kill_msk; + mvm->bt_cts_kill_msk[0] = cts_kill_msk; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); + if (!bt_cmd) + return -ENOMEM; + cmd.data[0] = bt_cmd; + bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); + + bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk]); + bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk]); + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | + BT_VALID_KILL_ACK | + BT_VALID_KILL_CTS); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; +} + +static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, + bool enable) +{ + struct iwl_bt_coex_cmd_old *bt_cmd; + /* Send ASYNC since this can be sent from an atomic context */ + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_DUP, }, + .flags = CMD_ASYNC, + }; + struct iwl_mvm_sta *mvmsta; + int ret; + + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); + if (!mvmsta) + return 0; + + /* nothing to do */ + if (mvmsta->bt_reduced_txpower == enable) + return 0; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC); + if (!bt_cmd) + return -ENOMEM; + cmd.data[0] = bt_cmd; + bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); + + bt_cmd->valid_bit_msk = + cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER); + bt_cmd->bt_reduced_tx_power = sta_id; + + if (enable) + bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT; + + IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", + enable ? "en" : "dis", sta_id); + + mvmsta->bt_reduced_txpower = enable; + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; +} + +struct iwl_bt_iterator_data { + struct iwl_bt_coex_profile_notif_old *notif; + struct iwl_mvm *mvm; + struct ieee80211_chanctx_conf *primary; + struct ieee80211_chanctx_conf *secondary; + bool primary_ll; +}; + +static inline +void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable, int rssi) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + mvmvif->bf_data.last_bt_coex_event = rssi; + mvmvif->bf_data.bt_coex_max_thold = + enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0; + mvmvif->bf_data.bt_coex_min_thold = + enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; +} + +/* must be called under rcu_read_lock */ +static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_bt_iterator_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct ieee80211_chanctx_conf *chanctx_conf; + enum ieee80211_smps_mode smps_mode; + u32 bt_activity_grading; + int ave_rssi; + + lockdep_assert_held(&mvm->mutex); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + /* default smps_mode for BSS / P2P client is AUTOMATIC */ + smps_mode = IEEE80211_SMPS_AUTOMATIC; + break; + case NL80211_IFTYPE_AP: + if (!mvmvif->ap_ibss_active) + return; + break; + default: + return; + } + + chanctx_conf = rcu_dereference(vif->chanctx_conf); + + /* If channel context is invalid or not on 2.4GHz .. */ + if ((!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) { + if (vif->type == NL80211_IFTYPE_STATION) { + /* ... relax constraints and disable rssi events */ + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); + iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, + false); + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); + } + return; + } + + bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading); + if (bt_activity_grading >= BT_HIGH_TRAFFIC) + smps_mode = IEEE80211_SMPS_STATIC; + else if (bt_activity_grading >= BT_LOW_TRAFFIC) + smps_mode = vif->type == NL80211_IFTYPE_AP ? + IEEE80211_SMPS_OFF : + IEEE80211_SMPS_DYNAMIC; + + /* relax SMPS contraints for next association */ + if (!vif->bss_conf.assoc) + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + if (mvmvif->phy_ctxt && + data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id)) + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + IWL_DEBUG_COEX(data->mvm, + "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n", + mvmvif->id, data->notif->bt_status, bt_activity_grading, + smps_mode); + + if (vif->type == NL80211_IFTYPE_STATION) + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); + + /* low latency is always primary */ + if (iwl_mvm_vif_low_latency(mvmvif)) { + data->primary_ll = true; + + data->secondary = data->primary; + data->primary = chanctx_conf; + } + + if (vif->type == NL80211_IFTYPE_AP) { + if (!mvmvif->ap_ibss_active) + return; + + if (chanctx_conf == data->primary) + return; + + if (!data->primary_ll) { + /* + * downgrade the current primary no matter what its + * type is. + */ + data->secondary = data->primary; + data->primary = chanctx_conf; + } else { + /* there is low latency vif - we will be secondary */ + data->secondary = chanctx_conf; + } + return; + } + + /* + * STA / P2P Client, try to be primary if first vif. If we are in low + * latency mode, we are already in primary and just don't do much + */ + if (!data->primary || data->primary == chanctx_conf) + data->primary = chanctx_conf; + else if (!data->secondary) + /* if secondary is not NULL, it might be a GO */ + data->secondary = chanctx_conf; + + /* + * don't reduce the Tx power if one of these is true: + * we are in LOOSE + * single share antenna product + * BT is active + * we are associated + */ + if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || + mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || + !data->notif->bt_status) { + iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); + return; + } + + /* try to get the avg rssi from fw */ + ave_rssi = mvmvif->bf_data.ave_beacon_signal; + + /* if the RSSI isn't valid, fake it is very low */ + if (!ave_rssi) + ave_rssi = -100; + if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { + if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) + IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); + } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { + if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) + IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); + } + + /* Begin to monitor the RSSI: it may influence the reduced Tx power */ + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi); +} + +static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) +{ + struct iwl_bt_iterator_data data = { + .mvm = mvm, + .notif = &mvm->last_bt_notif_old, + }; + struct iwl_bt_coex_ci_cmd_old cmd = {}; + u8 ci_bw_idx; + + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + + rcu_read_lock(); + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bt_notif_iterator, &data); + + if (data.primary) { + struct ieee80211_chanctx_conf *chan = data.primary; + + if (WARN_ON(!chan->def.chan)) { + rcu_read_unlock(); + return; + } + + if (chan->def.width < NL80211_CHAN_WIDTH_40) { + ci_bw_idx = 0; + cmd.co_run_bw_primary = 0; + } else { + cmd.co_run_bw_primary = 1; + if (chan->def.center_freq1 > + chan->def.chan->center_freq) + ci_bw_idx = 2; + else + ci_bw_idx = 1; + } + + cmd.bt_primary_ci = + iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; + cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv); + } + + if (data.secondary) { + struct ieee80211_chanctx_conf *chan = data.secondary; + + if (WARN_ON(!data.secondary->def.chan)) { + rcu_read_unlock(); + return; + } + + if (chan->def.width < NL80211_CHAN_WIDTH_40) { + ci_bw_idx = 0; + cmd.co_run_bw_secondary = 0; + } else { + cmd.co_run_bw_secondary = 1; + if (chan->def.center_freq1 > + chan->def.chan->center_freq) + ci_bw_idx = 2; + else + ci_bw_idx = 1; + } + + cmd.bt_secondary_ci = + iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; + cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv); + } + + rcu_read_unlock(); + + /* Don't spam the fw with the same command over and over */ + if (memcmp(&cmd, &mvm->last_bt_ci_cmd_old, sizeof(cmd))) { + if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, + sizeof(cmd), &cmd)) + IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); + memcpy(&mvm->last_bt_ci_cmd_old, &cmd, sizeof(cmd)); + } + + if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm)) + IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); +} + +void iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_bt_coex_profile_notif_old *notif = (void *)pkt->data; + + IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); + IWL_DEBUG_COEX(mvm, "\tBT status: %s\n", + notif->bt_status ? "ON" : "OFF"); + IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn); + IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); + IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", + le32_to_cpu(notif->primary_ch_lut)); + IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", + le32_to_cpu(notif->secondary_ch_lut)); + IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", + le32_to_cpu(notif->bt_activity_grading)); + IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n", + notif->bt_agg_traffic_load); + + /* remember this notification for future use: rssi fluctuations */ + memcpy(&mvm->last_bt_notif_old, notif, sizeof(mvm->last_bt_notif_old)); + + iwl_mvm_bt_coex_notif_handle(mvm); +} + +static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_bt_iterator_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + + struct ieee80211_chanctx_conf *chanctx_conf; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + /* If channel context is invalid or not on 2.4GHz - don't count it */ + if (!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { + rcu_read_unlock(); + return; + } + rcu_read_unlock(); + + if (vif->type != NL80211_IFTYPE_STATION || + mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) + return; + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], + lockdep_is_held(&mvm->mutex)); + + /* This can happen if the station has been removed right now */ + if (IS_ERR_OR_NULL(sta)) + return; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); +} + +void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum ieee80211_rssi_event_data rssi_event) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_bt_iterator_data data = { + .mvm = mvm, + }; + int ret; + + lockdep_assert_held(&mvm->mutex); + + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + + /* + * Rssi update while not associated - can happen since the statistics + * are handled asynchronously + */ + if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) + return; + + /* No BT - reports should be disabled */ + if (!mvm->last_bt_notif_old.bt_status) + return; + + IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, + rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); + + /* + * Check if rssi is good enough for reduced Tx power, but not in loose + * scheme. + */ + if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant || + iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) + ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, + false); + else + ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true); + + if (ret) + IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bt_rssi_iterator, &data); + + if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm)) + IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); +} + +#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) +#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) + +u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + enum iwl_bt_coex_lut_type lut_type; + + if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) < + BT_HIGH_TRAFFIC) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + if (mvm->last_bt_notif_old.ttc_enabled) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); + + if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + /* tight coex, high bt traffic, reduce AGG time limit */ + return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; +} + +bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + enum iwl_bt_coex_lut_type lut_type; + + if (mvm->last_bt_notif_old.ttc_enabled) + return true; + + if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) < + BT_HIGH_TRAFFIC) + return true; + + /* + * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas + * since BT is already killed. + * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while + * we Tx. + * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. + */ + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); + return lut_type != BT_COEX_LOOSE_LUT; +} + +bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm) +{ + u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading); + return ag < BT_HIGH_TRAFFIC; +} + +bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm, + enum ieee80211_band band) +{ + u32 bt_activity = + le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading); + + if (band != IEEE80211_BAND_2GHZ) + return false; + + return bt_activity >= BT_LOW_TRAFFIC; +} + +void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm) +{ + iwl_mvm_bt_coex_notif_handle(mvm); +} + +void iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u32 ant_isolation = le32_to_cpup((void *)pkt->data); + u8 __maybe_unused lower_bound, upper_bound; + u8 lut; + + struct iwl_bt_coex_cmd_old *bt_cmd; + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + + if (!iwl_mvm_bt_is_plcr_supported(mvm)) + return; + + lockdep_assert_held(&mvm->mutex); + + /* Ignore updates if we are in force mode */ + if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) + return; + + if (ant_isolation == mvm->last_ant_isol) + return; + + for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++) + if (ant_isolation < antenna_coupling_ranges[lut + 1].range) + break; + + lower_bound = antenna_coupling_ranges[lut].range; + + if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1) + upper_bound = antenna_coupling_ranges[lut + 1].range; + else + upper_bound = antenna_coupling_ranges[lut].range; + + IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n", + ant_isolation, lower_bound, upper_bound, lut); + + mvm->last_ant_isol = ant_isolation; + + if (mvm->last_corun_lut == lut) + return; + + mvm->last_corun_lut = lut; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); + if (!bt_cmd) + return; + cmd.data[0] = bt_cmd; + + bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | + BT_VALID_CORUN_LUT_20 | + BT_VALID_CORUN_LUT_40); + + /* For the moment, use the same LUT for 20GHz and 40GHz */ + memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20, + sizeof(bt_cmd->bt4_corun_lut20)); + + memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20, + sizeof(bt_cmd->bt4_corun_lut40)); + + if (iwl_mvm_send_cmd(mvm, &cmd)) + IWL_ERR(mvm, "failed to send BT_CONFIG command\n"); + + kfree(bt_cmd); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h new file mode 100644 index 000000000000..5c21231e195d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -0,0 +1,139 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __MVM_CONSTANTS_H +#define __MVM_CONSTANTS_H + +#include + +#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC) +#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC) +#define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC) +#define IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT (10 * USEC_PER_MSEC) +#define IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT (2 * 1024) /* defined in TU */ +#define IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT (40 * 1024) /* defined in TU */ +#define IWL_MVM_P2P_LOWLATENCY_PS_ENABLE 0 +#define IWL_MVM_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC) +#define IWL_MVM_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC) +#define IWL_MVM_UAPSD_QUEUES (IEEE80211_WMM_IE_STA_QOSINFO_AC_VO |\ + IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |\ + IEEE80211_WMM_IE_STA_QOSINFO_AC_BK |\ + IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) +#define IWL_MVM_PS_HEAVY_TX_THLD_PACKETS 20 +#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS 8 +#define IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS 30 +#define IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS 20 +#define IWL_MVM_PS_HEAVY_TX_THLD_PERCENT 50 +#define IWL_MVM_PS_HEAVY_RX_THLD_PERCENT 50 +#define IWL_MVM_PS_SNOOZE_INTERVAL 25 +#define IWL_MVM_PS_SNOOZE_WINDOW 50 +#define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25 +#define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64 +#define IWL_MVM_BT_COEX_EN_RED_TXP_THRESH 62 +#define IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH 65 +#define IWL_MVM_BT_COEX_SYNC2SCO 1 +#define IWL_MVM_BT_COEX_CORUNNING 0 +#define IWL_MVM_BT_COEX_MPLUT 1 +#define IWL_MVM_BT_COEX_RRC 1 +#define IWL_MVM_BT_COEX_TTC 1 +#define IWL_MVM_BT_COEX_MPLUT_REG0 0x22002200 +#define IWL_MVM_BT_COEX_MPLUT_REG1 0x11118451 +#define IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS 30 +#define IWL_MVM_FW_MCAST_FILTER_PASS_ALL 0 +#define IWL_MVM_FW_BCAST_FILTER_PASS_ALL 0 +#define IWL_MVM_QUOTA_THRESHOLD 4 +#define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0 +#define IWL_MVM_RS_80_20_FAR_RANGE_TWEAK 1 +#define IWL_MVM_TOF_IS_RESPONDER 0 +#define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1 +#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2 +#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1 +#define IWL_MVM_RS_INITIAL_MIMO_NUM_RATES 3 +#define IWL_MVM_RS_INITIAL_SISO_NUM_RATES 3 +#define IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES 2 +#define IWL_MVM_RS_INITIAL_LEGACY_RETRIES 2 +#define IWL_MVM_RS_SECONDARY_LEGACY_RETRIES 1 +#define IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES 16 +#define IWL_MVM_RS_SECONDARY_SISO_NUM_RATES 3 +#define IWL_MVM_RS_SECONDARY_SISO_RETRIES 1 +#define IWL_MVM_RS_RATE_MIN_FAILURE_TH 3 +#define IWL_MVM_RS_RATE_MIN_SUCCESS_TH 8 +#define IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT 5 /* Seconds */ +#define IWL_MVM_RS_IDLE_TIMEOUT 5 /* Seconds */ +#define IWL_MVM_RS_MISSED_RATE_MAX 15 +#define IWL_MVM_RS_LEGACY_FAILURE_LIMIT 160 +#define IWL_MVM_RS_LEGACY_SUCCESS_LIMIT 480 +#define IWL_MVM_RS_LEGACY_TABLE_COUNT 160 +#define IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT 400 +#define IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT 4500 +#define IWL_MVM_RS_NON_LEGACY_TABLE_COUNT 1500 +#define IWL_MVM_RS_SR_FORCE_DECREASE 15 /* percent */ +#define IWL_MVM_RS_SR_NO_DECREASE 85 /* percent */ +#define IWL_MVM_RS_AGG_TIME_LIMIT 4000 /* 4 msecs. valid 100-8000 */ +#define IWL_MVM_RS_AGG_DISABLE_START 3 +#define IWL_MVM_RS_TPC_SR_FORCE_INCREASE 75 /* percent */ +#define IWL_MVM_RS_TPC_SR_NO_INCREASE 85 /* percent */ +#define IWL_MVM_RS_TPC_TX_POWER_STEP 3 + +#endif /* __MVM_CONSTANTS_H */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c new file mode 100644 index 000000000000..85ae902df7c0 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -0,0 +1,2104 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "iwl-modparams.h" +#include "fw-api.h" +#include "mvm.h" + +void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (iwlwifi_mod_params.sw_crypto) + return; + + mutex_lock(&mvm->mutex); + + memcpy(mvmvif->rekey_data.kek, data->kek, NL80211_KEK_LEN); + memcpy(mvmvif->rekey_data.kck, data->kck, NL80211_KCK_LEN); + mvmvif->rekey_data.replay_ctr = + cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr)); + mvmvif->rekey_data.valid = true; + + mutex_unlock(&mvm->mutex); +} + +#if IS_ENABLED(CONFIG_IPV6) +void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct inet6_dev *idev) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct inet6_ifaddr *ifa; + int idx = 0; + + read_lock_bh(&idev->lock); + list_for_each_entry(ifa, &idev->addr_list, if_list) { + mvmvif->target_ipv6_addrs[idx] = ifa->addr; + idx++; + if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX) + break; + } + read_unlock_bh(&idev->lock); + + mvmvif->num_target_ipv6_addrs = idx; +} +#endif + +void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int idx) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + mvmvif->tx_key_idx = idx; +} + +static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out) +{ + int i; + + for (i = 0; i < IWL_P1K_SIZE; i++) + out[i] = cpu_to_le16(p1k[i]); +} + +struct wowlan_key_data { + struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc; + struct iwl_wowlan_tkip_params_cmd *tkip; + bool error, use_rsc_tsc, use_tkip; + int wep_key_idx; +}; + +static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct wowlan_key_data *data = _data; + struct aes_sc *aes_sc, *aes_tx_sc = NULL; + struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; + struct iwl_p1k_cache *rx_p1ks; + u8 *rx_mic_key; + struct ieee80211_key_seq seq; + u32 cur_rx_iv32 = 0; + u16 p1k[IWL_P1K_SIZE]; + int ret, i; + + mutex_lock(&mvm->mutex); + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: { /* hack it for now */ + struct { + struct iwl_mvm_wep_key_cmd wep_key_cmd; + struct iwl_mvm_wep_key wep_key; + } __packed wkc = { + .wep_key_cmd.mac_id_n_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)), + .wep_key_cmd.num_keys = 1, + /* firmware sets STA_KEY_FLG_WEP_13BYTES */ + .wep_key_cmd.decryption_type = STA_KEY_FLG_WEP, + .wep_key.key_index = key->keyidx, + .wep_key.key_size = key->keylen, + }; + + /* + * This will fail -- the key functions don't set support + * pairwise WEP keys. However, that's better than silently + * failing WoWLAN. Or maybe not? + */ + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + break; + + memcpy(&wkc.wep_key.key[3], key->key, key->keylen); + if (key->keyidx == mvmvif->tx_key_idx) { + /* TX key must be at offset 0 */ + wkc.wep_key.key_offset = 0; + } else { + /* others start at 1 */ + data->wep_key_idx++; + wkc.wep_key.key_offset = data->wep_key_idx; + } + + ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc); + data->error = ret != 0; + + mvm->ptk_ivlen = key->iv_len; + mvm->ptk_icvlen = key->icv_len; + mvm->gtk_ivlen = key->iv_len; + mvm->gtk_icvlen = key->icv_len; + + /* don't upload key again */ + goto out_unlock; + } + default: + data->error = true; + goto out_unlock; + case WLAN_CIPHER_SUITE_AES_CMAC: + /* + * Ignore CMAC keys -- the WoWLAN firmware doesn't support them + * but we also shouldn't abort suspend due to that. It does have + * support for the IGTK key renewal, but doesn't really use the + * IGTK for anything. This means we could spuriously wake up or + * be deauthenticated, but that was considered acceptable. + */ + goto out_unlock; + case WLAN_CIPHER_SUITE_TKIP: + if (sta) { + tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; + tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; + + rx_p1ks = data->tkip->rx_uni; + + ieee80211_get_key_tx_seq(key, &seq); + tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); + tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); + + ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); + iwl_mvm_convert_p1k(p1k, data->tkip->tx.p1k); + + memcpy(data->tkip->mic_keys.tx, + &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], + IWL_MIC_KEY_SIZE); + + rx_mic_key = data->tkip->mic_keys.rx_unicast; + } else { + tkip_sc = + data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; + rx_p1ks = data->tkip->rx_multi; + rx_mic_key = data->tkip->mic_keys.rx_mcast; + } + + /* + * For non-QoS this relies on the fact that both the uCode and + * mac80211 use TID 0 (as they need to to avoid replay attacks) + * for checking the IV in the frames. + */ + for (i = 0; i < IWL_NUM_RSC; i++) { + ieee80211_get_key_rx_seq(key, i, &seq); + tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); + tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); + /* wrapping isn't allowed, AP must rekey */ + if (seq.tkip.iv32 > cur_rx_iv32) + cur_rx_iv32 = seq.tkip.iv32; + } + + ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid, + cur_rx_iv32, p1k); + iwl_mvm_convert_p1k(p1k, rx_p1ks[0].p1k); + ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid, + cur_rx_iv32 + 1, p1k); + iwl_mvm_convert_p1k(p1k, rx_p1ks[1].p1k); + + memcpy(rx_mic_key, + &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], + IWL_MIC_KEY_SIZE); + + data->use_tkip = true; + data->use_rsc_tsc = true; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (sta) { + u64 pn64; + + aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; + aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; + + pn64 = atomic64_read(&key->tx_pn); + aes_tx_sc->pn = cpu_to_le64(pn64); + } else { + aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; + } + + /* + * For non-QoS this relies on the fact that both the uCode and + * mac80211 use TID 0 for checking the IV in the frames. + */ + for (i = 0; i < IWL_NUM_RSC; i++) { + u8 *pn = seq.ccmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); + aes_sc[i].pn = cpu_to_le64((u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } + data->use_rsc_tsc = true; + break; + } + + /* + * The D3 firmware hardcodes the key offset 0 as the key it uses + * to transmit packets to the AP, i.e. the PTK. + */ + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { + key->hw_key_idx = 0; + mvm->ptk_ivlen = key->iv_len; + mvm->ptk_icvlen = key->icv_len; + } else { + /* + * firmware only supports TSC/RSC for a single key, + * so if there are multiple keep overwriting them + * with new ones -- this relies on mac80211 doing + * list_add_tail(). + */ + key->hw_key_idx = 1; + mvm->gtk_ivlen = key->iv_len; + mvm->gtk_icvlen = key->icv_len; + } + + ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, true); + data->error = ret != 0; +out_unlock: + mutex_unlock(&mvm->mutex); +} + +static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, + struct cfg80211_wowlan *wowlan) +{ + struct iwl_wowlan_patterns_cmd *pattern_cmd; + struct iwl_host_cmd cmd = { + .id = WOWLAN_PATTERNS, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + int i, err; + + if (!wowlan->n_patterns) + return 0; + + cmd.len[0] = sizeof(*pattern_cmd) + + wowlan->n_patterns * sizeof(struct iwl_wowlan_pattern); + + pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); + if (!pattern_cmd) + return -ENOMEM; + + pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); + + for (i = 0; i < wowlan->n_patterns; i++) { + int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); + + memcpy(&pattern_cmd->patterns[i].mask, + wowlan->patterns[i].mask, mask_len); + memcpy(&pattern_cmd->patterns[i].pattern, + wowlan->patterns[i].pattern, + wowlan->patterns[i].pattern_len); + pattern_cmd->patterns[i].mask_size = mask_len; + pattern_cmd->patterns[i].pattern_size = + wowlan->patterns[i].pattern_len; + } + + cmd.data[0] = pattern_cmd; + err = iwl_mvm_send_cmd(mvm, &cmd); + kfree(pattern_cmd); + return err; +} + +enum iwl_mvm_tcp_packet_type { + MVM_TCP_TX_SYN, + MVM_TCP_RX_SYNACK, + MVM_TCP_TX_DATA, + MVM_TCP_RX_ACK, + MVM_TCP_RX_WAKE, + MVM_TCP_TX_FIN, +}; + +static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr) +{ + __sum16 check = tcp_v4_check(len, saddr, daddr, 0); + return cpu_to_le16(be16_to_cpu((__force __be16)check)); +} + +static void iwl_mvm_build_tcp_packet(struct ieee80211_vif *vif, + struct cfg80211_wowlan_tcp *tcp, + void *_pkt, u8 *mask, + __le16 *pseudo_hdr_csum, + enum iwl_mvm_tcp_packet_type ptype) +{ + struct { + struct ethhdr eth; + struct iphdr ip; + struct tcphdr tcp; + u8 data[]; + } __packed *pkt = _pkt; + u16 ip_tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); + int i; + + pkt->eth.h_proto = cpu_to_be16(ETH_P_IP), + pkt->ip.version = 4; + pkt->ip.ihl = 5; + pkt->ip.protocol = IPPROTO_TCP; + + switch (ptype) { + case MVM_TCP_TX_SYN: + case MVM_TCP_TX_DATA: + case MVM_TCP_TX_FIN: + memcpy(pkt->eth.h_dest, tcp->dst_mac, ETH_ALEN); + memcpy(pkt->eth.h_source, vif->addr, ETH_ALEN); + pkt->ip.ttl = 128; + pkt->ip.saddr = tcp->src; + pkt->ip.daddr = tcp->dst; + pkt->tcp.source = cpu_to_be16(tcp->src_port); + pkt->tcp.dest = cpu_to_be16(tcp->dst_port); + /* overwritten for TX SYN later */ + pkt->tcp.doff = sizeof(struct tcphdr) / 4; + pkt->tcp.window = cpu_to_be16(65000); + break; + case MVM_TCP_RX_SYNACK: + case MVM_TCP_RX_ACK: + case MVM_TCP_RX_WAKE: + memcpy(pkt->eth.h_dest, vif->addr, ETH_ALEN); + memcpy(pkt->eth.h_source, tcp->dst_mac, ETH_ALEN); + pkt->ip.saddr = tcp->dst; + pkt->ip.daddr = tcp->src; + pkt->tcp.source = cpu_to_be16(tcp->dst_port); + pkt->tcp.dest = cpu_to_be16(tcp->src_port); + break; + default: + WARN_ON(1); + return; + } + + switch (ptype) { + case MVM_TCP_TX_SYN: + /* firmware assumes 8 option bytes - 8 NOPs for now */ + memset(pkt->data, 0x01, 8); + ip_tot_len += 8; + pkt->tcp.doff = (sizeof(struct tcphdr) + 8) / 4; + pkt->tcp.syn = 1; + break; + case MVM_TCP_TX_DATA: + ip_tot_len += tcp->payload_len; + memcpy(pkt->data, tcp->payload, tcp->payload_len); + pkt->tcp.psh = 1; + pkt->tcp.ack = 1; + break; + case MVM_TCP_TX_FIN: + pkt->tcp.fin = 1; + pkt->tcp.ack = 1; + break; + case MVM_TCP_RX_SYNACK: + pkt->tcp.syn = 1; + pkt->tcp.ack = 1; + break; + case MVM_TCP_RX_ACK: + pkt->tcp.ack = 1; + break; + case MVM_TCP_RX_WAKE: + ip_tot_len += tcp->wake_len; + pkt->tcp.psh = 1; + pkt->tcp.ack = 1; + memcpy(pkt->data, tcp->wake_data, tcp->wake_len); + break; + } + + switch (ptype) { + case MVM_TCP_TX_SYN: + case MVM_TCP_TX_DATA: + case MVM_TCP_TX_FIN: + pkt->ip.tot_len = cpu_to_be16(ip_tot_len); + pkt->ip.check = ip_fast_csum(&pkt->ip, pkt->ip.ihl); + break; + case MVM_TCP_RX_WAKE: + for (i = 0; i < DIV_ROUND_UP(tcp->wake_len, 8); i++) { + u8 tmp = tcp->wake_mask[i]; + mask[i + 6] |= tmp << 6; + if (i + 1 < DIV_ROUND_UP(tcp->wake_len, 8)) + mask[i + 7] = tmp >> 2; + } + /* fall through for ethernet/IP/TCP headers mask */ + case MVM_TCP_RX_SYNACK: + case MVM_TCP_RX_ACK: + mask[0] = 0xff; /* match ethernet */ + /* + * match ethernet, ip.version, ip.ihl + * the ip.ihl half byte is really masked out by firmware + */ + mask[1] = 0x7f; + mask[2] = 0x80; /* match ip.protocol */ + mask[3] = 0xfc; /* match ip.saddr, ip.daddr */ + mask[4] = 0x3f; /* match ip.daddr, tcp.source, tcp.dest */ + mask[5] = 0x80; /* match tcp flags */ + /* leave rest (0 or set for MVM_TCP_RX_WAKE) */ + break; + }; + + *pseudo_hdr_csum = pseudo_hdr_check(ip_tot_len - sizeof(struct iphdr), + pkt->ip.saddr, pkt->ip.daddr); +} + +static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_wowlan_tcp *tcp) +{ + struct iwl_wowlan_remote_wake_config *cfg; + struct iwl_host_cmd cmd = { + .id = REMOTE_WAKE_CONFIG_CMD, + .len = { sizeof(*cfg), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + int ret; + + if (!tcp) + return 0; + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + cmd.data[0] = cfg; + + cfg->max_syn_retries = 10; + cfg->max_data_retries = 10; + cfg->tcp_syn_ack_timeout = 1; /* seconds */ + cfg->tcp_ack_timeout = 1; /* seconds */ + + /* SYN (TX) */ + iwl_mvm_build_tcp_packet( + vif, tcp, cfg->syn_tx.data, NULL, + &cfg->syn_tx.info.tcp_pseudo_header_checksum, + MVM_TCP_TX_SYN); + cfg->syn_tx.info.tcp_payload_length = 0; + + /* SYN/ACK (RX) */ + iwl_mvm_build_tcp_packet( + vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask, + &cfg->synack_rx.info.tcp_pseudo_header_checksum, + MVM_TCP_RX_SYNACK); + cfg->synack_rx.info.tcp_payload_length = 0; + + /* KEEPALIVE/ACK (TX) */ + iwl_mvm_build_tcp_packet( + vif, tcp, cfg->keepalive_tx.data, NULL, + &cfg->keepalive_tx.info.tcp_pseudo_header_checksum, + MVM_TCP_TX_DATA); + cfg->keepalive_tx.info.tcp_payload_length = + cpu_to_le16(tcp->payload_len); + cfg->sequence_number_offset = tcp->payload_seq.offset; + /* length must be 0..4, the field is little endian */ + cfg->sequence_number_length = tcp->payload_seq.len; + cfg->initial_sequence_number = cpu_to_le32(tcp->payload_seq.start); + cfg->keepalive_interval = cpu_to_le16(tcp->data_interval); + if (tcp->payload_tok.len) { + cfg->token_offset = tcp->payload_tok.offset; + cfg->token_length = tcp->payload_tok.len; + cfg->num_tokens = + cpu_to_le16(tcp->tokens_size % tcp->payload_tok.len); + memcpy(cfg->tokens, tcp->payload_tok.token_stream, + tcp->tokens_size); + } else { + /* set tokens to max value to almost never run out */ + cfg->num_tokens = cpu_to_le16(65535); + } + + /* ACK (RX) */ + iwl_mvm_build_tcp_packet( + vif, tcp, cfg->keepalive_ack_rx.data, + cfg->keepalive_ack_rx.rx_mask, + &cfg->keepalive_ack_rx.info.tcp_pseudo_header_checksum, + MVM_TCP_RX_ACK); + cfg->keepalive_ack_rx.info.tcp_payload_length = 0; + + /* WAKEUP (RX) */ + iwl_mvm_build_tcp_packet( + vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask, + &cfg->wake_rx.info.tcp_pseudo_header_checksum, + MVM_TCP_RX_WAKE); + cfg->wake_rx.info.tcp_payload_length = + cpu_to_le16(tcp->wake_len); + + /* FIN */ + iwl_mvm_build_tcp_packet( + vif, tcp, cfg->fin_tx.data, NULL, + &cfg->fin_tx.info.tcp_pseudo_header_checksum, + MVM_TCP_TX_FIN); + cfg->fin_tx.info.tcp_payload_length = 0; + + ret = iwl_mvm_send_cmd(mvm, &cmd); + kfree(cfg); + + return ret; +} + +static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *ap_sta) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct ieee80211_chanctx_conf *ctx; + u8 chains_static, chains_dynamic; + struct cfg80211_chan_def chandef; + int ret, i; + struct iwl_binding_cmd binding_cmd = {}; + struct iwl_time_quota_cmd quota_cmd = {}; + u32 status; + + /* add back the PHY */ + if (WARN_ON(!mvmvif->phy_ctxt)) + return -EINVAL; + + rcu_read_lock(); + ctx = rcu_dereference(vif->chanctx_conf); + if (WARN_ON(!ctx)) { + rcu_read_unlock(); + return -EINVAL; + } + chandef = ctx->def; + chains_static = ctx->rx_chains_static; + chains_dynamic = ctx->rx_chains_dynamic; + rcu_read_unlock(); + + ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, &chandef, + chains_static, chains_dynamic); + if (ret) + return ret; + + /* add back the MAC */ + mvmvif->uploaded = false; + + if (WARN_ON(!vif->bss_conf.assoc)) + return -EINVAL; + + ret = iwl_mvm_mac_ctxt_add(mvm, vif); + if (ret) + return ret; + + /* add back binding - XXX refactor? */ + binding_cmd.id_and_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, + mvmvif->phy_ctxt->color)); + binding_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); + binding_cmd.phy = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, + mvmvif->phy_ctxt->color)); + binding_cmd.macs[0] = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)); + for (i = 1; i < MAX_MACS_IN_BINDING; i++) + binding_cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID); + + status = 0; + ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD, + sizeof(binding_cmd), &binding_cmd, + &status); + if (ret) { + IWL_ERR(mvm, "Failed to add binding: %d\n", ret); + return ret; + } + + if (status) { + IWL_ERR(mvm, "Binding command failed: %u\n", status); + return -EIO; + } + + ret = iwl_mvm_sta_send_to_fw(mvm, ap_sta, false); + if (ret) + return ret; + rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta); + + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + if (ret) + return ret; + + /* and some quota */ + quota_cmd.quotas[0].id_and_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, + mvmvif->phy_ctxt->color)); + quota_cmd.quotas[0].quota = cpu_to_le32(IWL_MVM_MAX_QUOTA); + quota_cmd.quotas[0].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA); + + for (i = 1; i < MAX_BINDINGS; i++) + quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID); + + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, + sizeof(quota_cmd), "a_cmd); + if (ret) + IWL_ERR(mvm, "Failed to send quota: %d\n", ret); + + if (iwl_mvm_is_lar_supported(mvm) && iwl_mvm_init_fw_regd(mvm)) + IWL_ERR(mvm, "Failed to initialize D3 LAR information\n"); + + return 0; +} + +static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_nonqos_seq_query_cmd query_cmd = { + .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_GET), + .mac_id_n_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)), + }; + struct iwl_host_cmd cmd = { + .id = NON_QOS_TX_COUNTER_CMD, + .flags = CMD_WANT_SKB, + }; + int err; + u32 size; + + cmd.data[0] = &query_cmd; + cmd.len[0] = sizeof(query_cmd); + + err = iwl_mvm_send_cmd(mvm, &cmd); + if (err) + return err; + + size = iwl_rx_packet_payload_len(cmd.resp_pkt); + if (size < sizeof(__le16)) { + err = -EINVAL; + } else { + err = le16_to_cpup((__le16 *)cmd.resp_pkt->data); + /* firmware returns next, not last-used seqno */ + err = (u16) (err - 0x10); + } + + iwl_free_resp(&cmd); + return err; +} + +void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_nonqos_seq_query_cmd query_cmd = { + .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_SET), + .mac_id_n_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)), + .value = cpu_to_le16(mvmvif->seqno), + }; + + /* return if called during restart, not resume from D3 */ + if (!mvmvif->seqno_valid) + return; + + mvmvif->seqno_valid = false; + + if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, 0, + sizeof(query_cmd), &query_cmd)) + IWL_ERR(mvm, "failed to set non-QoS seqno\n"); +} + +static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm) +{ + iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); + + iwl_trans_stop_device(mvm->trans); + + /* + * Set the HW restart bit -- this is mostly true as we're + * going to load new firmware and reprogram that, though + * the reprogramming is going to be manual to avoid adding + * all the MACs that aren't support. + * We don't have to clear up everything though because the + * reprogramming is manual. When we resume, we'll actually + * go through a proper restart sequence again to switch + * back to the runtime firmware image. + */ + set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + + /* We reprogram keys and shouldn't allocate new key indices */ + memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); + + mvm->ptk_ivlen = 0; + mvm->ptk_icvlen = 0; + mvm->ptk_ivlen = 0; + mvm->ptk_icvlen = 0; + + return iwl_mvm_load_d3_fw(mvm); +} + +static int +iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, + struct cfg80211_wowlan *wowlan, + struct iwl_wowlan_config_cmd *wowlan_config_cmd, + struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, + struct ieee80211_sta *ap_sta) +{ + int ret; + struct iwl_mvm_sta *mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); + + /* TODO: wowlan_config_cmd->wowlan_ba_teardown_tids */ + + wowlan_config_cmd->is_11n_connection = + ap_sta->ht_cap.ht_supported; + + /* Query the last used seqno and set it */ + ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); + if (ret < 0) + return ret; + + wowlan_config_cmd->non_qos_seq = cpu_to_le16(ret); + + iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, wowlan_config_cmd); + + if (wowlan->disconnect) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | + IWL_WOWLAN_WAKEUP_LINK_CHANGE); + if (wowlan->magic_pkt) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET); + if (wowlan->gtk_rekey_failure) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL); + if (wowlan->eap_identity_req) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ); + if (wowlan->four_way_handshake) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE); + if (wowlan->n_patterns) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH); + + if (wowlan->rfkill_release) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); + + if (wowlan->tcp) { + /* + * Set the "link change" (really "link lost") flag as well + * since that implies losing the TCP connection. + */ + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS | + IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE | + IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET | + IWL_WOWLAN_WAKEUP_LINK_CHANGE); + } + + return 0; +} + +static int +iwl_mvm_wowlan_config(struct iwl_mvm *mvm, + struct cfg80211_wowlan *wowlan, + struct iwl_wowlan_config_cmd *wowlan_config_cmd, + struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, + struct ieee80211_sta *ap_sta) +{ + struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; + struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; + struct wowlan_key_data key_data = { + .use_rsc_tsc = false, + .tkip = &tkip_cmd, + .use_tkip = false, + }; + int ret; + + ret = iwl_mvm_switch_to_d3(mvm); + if (ret) + return ret; + + ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta); + if (ret) + return ret; + + key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); + if (!key_data.rsc_tsc) + return -ENOMEM; + + if (!iwlwifi_mod_params.sw_crypto) { + /* + * This needs to be unlocked due to lock ordering + * constraints. Since we're in the suspend path + * that isn't really a problem though. + */ + mutex_unlock(&mvm->mutex); + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_wowlan_program_keys, + &key_data); + mutex_lock(&mvm->mutex); + if (key_data.error) { + ret = -EIO; + goto out; + } + + if (key_data.use_rsc_tsc) { + struct iwl_host_cmd rsc_tsc_cmd = { + .id = WOWLAN_TSC_RSC_PARAM, + .data[0] = key_data.rsc_tsc, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + .len[0] = sizeof(*key_data.rsc_tsc), + }; + + ret = iwl_mvm_send_cmd(mvm, &rsc_tsc_cmd); + if (ret) + goto out; + } + + if (key_data.use_tkip) { + ret = iwl_mvm_send_cmd_pdu(mvm, + WOWLAN_TKIP_PARAM, + 0, sizeof(tkip_cmd), + &tkip_cmd); + if (ret) + goto out; + } + + if (mvmvif->rekey_data.valid) { + memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); + memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck, + NL80211_KCK_LEN); + kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); + memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek, + NL80211_KEK_LEN); + kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); + kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr; + + ret = iwl_mvm_send_cmd_pdu(mvm, + WOWLAN_KEK_KCK_MATERIAL, 0, + sizeof(kek_kck_cmd), + &kek_kck_cmd); + if (ret) + goto out; + } + } + + ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, + sizeof(*wowlan_config_cmd), + wowlan_config_cmd); + if (ret) + goto out; + + ret = iwl_mvm_send_patterns(mvm, wowlan); + if (ret) + goto out; + + ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0); + if (ret) + goto out; + + ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp); + +out: + kfree(key_data.rsc_tsc); + return ret; +} + +static int +iwl_mvm_netdetect_config(struct iwl_mvm *mvm, + struct cfg80211_wowlan *wowlan, + struct cfg80211_sched_scan_request *nd_config, + struct ieee80211_vif *vif) +{ + struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; + int ret; + + ret = iwl_mvm_switch_to_d3(mvm); + if (ret) + return ret; + + /* rfkill release can be either for wowlan or netdetect */ + if (wowlan->rfkill_release) + wowlan_config_cmd.wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); + + ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, + sizeof(wowlan_config_cmd), + &wowlan_config_cmd); + if (ret) + return ret; + + ret = iwl_mvm_sched_scan_start(mvm, vif, nd_config, &mvm->nd_ies, + IWL_MVM_SCAN_NETDETECT); + if (ret) + return ret; + + if (WARN_ON(mvm->nd_match_sets || mvm->nd_channels)) + return -EBUSY; + + /* save the sched scan matchsets... */ + if (nd_config->n_match_sets) { + mvm->nd_match_sets = kmemdup(nd_config->match_sets, + sizeof(*nd_config->match_sets) * + nd_config->n_match_sets, + GFP_KERNEL); + if (mvm->nd_match_sets) + mvm->n_nd_match_sets = nd_config->n_match_sets; + } + + /* ...and the sched scan channels for later reporting */ + mvm->nd_channels = kmemdup(nd_config->channels, + sizeof(*nd_config->channels) * + nd_config->n_channels, + GFP_KERNEL); + if (mvm->nd_channels) + mvm->n_nd_channels = nd_config->n_channels; + + return 0; +} + +static void iwl_mvm_free_nd(struct iwl_mvm *mvm) +{ + kfree(mvm->nd_match_sets); + mvm->nd_match_sets = NULL; + mvm->n_nd_match_sets = 0; + kfree(mvm->nd_channels); + mvm->nd_channels = NULL; + mvm->n_nd_channels = 0; +} + +static int __iwl_mvm_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan, + bool test) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct ieee80211_vif *vif = NULL; + struct iwl_mvm_vif *mvmvif = NULL; + struct ieee80211_sta *ap_sta = NULL; + struct iwl_d3_manager_config d3_cfg_cmd_data = { + /* + * Program the minimum sleep time to 10 seconds, as many + * platforms have issues processing a wakeup signal while + * still being in the process of suspending. + */ + .min_sleep_time = cpu_to_le32(10 * 1000 * 1000), + }; + struct iwl_host_cmd d3_cfg_cmd = { + .id = D3_CONFIG_CMD, + .flags = CMD_WANT_SKB, + .data[0] = &d3_cfg_cmd_data, + .len[0] = sizeof(d3_cfg_cmd_data), + }; + int ret; + int len __maybe_unused; + + if (!wowlan) { + /* + * mac80211 shouldn't get here, but for D3 test + * it doesn't warrant a warning + */ + WARN_ON(!test); + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + + vif = iwl_mvm_get_bss_vif(mvm); + if (IS_ERR_OR_NULL(vif)) { + ret = 1; + goto out_noreset; + } + + mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) { + /* if we're not associated, this must be netdetect */ + if (!wowlan->nd_config && !mvm->nd_config) { + ret = 1; + goto out_noreset; + } + + ret = iwl_mvm_netdetect_config( + mvm, wowlan, wowlan->nd_config ?: mvm->nd_config, vif); + if (ret) + goto out; + + mvm->net_detect = true; + } else { + struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; + + ap_sta = rcu_dereference_protected( + mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(ap_sta)) { + ret = -EINVAL; + goto out_noreset; + } + + ret = iwl_mvm_get_wowlan_config(mvm, wowlan, &wowlan_config_cmd, + vif, mvmvif, ap_sta); + if (ret) + goto out_noreset; + ret = iwl_mvm_wowlan_config(mvm, wowlan, &wowlan_config_cmd, + vif, mvmvif, ap_sta); + if (ret) + goto out; + + mvm->net_detect = false; + } + + ret = iwl_mvm_power_update_device(mvm); + if (ret) + goto out; + + ret = iwl_mvm_power_update_mac(mvm); + if (ret) + goto out; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvm->d3_wake_sysassert) + d3_cfg_cmd_data.wakeup_flags |= + cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR); +#endif + + /* must be last -- this switches firmware state */ + ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd); + if (ret) + goto out; +#ifdef CONFIG_IWLWIFI_DEBUGFS + 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); + } +#endif + iwl_free_resp(&d3_cfg_cmd); + + clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + + iwl_trans_d3_suspend(mvm->trans, test); + out: + if (ret < 0) { + iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); + ieee80211_restart_hw(mvm->hw); + iwl_mvm_free_nd(mvm); + } + out_noreset: + mutex_unlock(&mvm->mutex); + + return ret; +} + +static int iwl_mvm_enter_d0i3_sync(struct iwl_mvm *mvm) +{ + struct iwl_notification_wait wait_d3; + static const u16 d3_notif[] = { D3_CONFIG_CMD }; + int ret; + + iwl_init_notification_wait(&mvm->notif_wait, &wait_d3, + d3_notif, ARRAY_SIZE(d3_notif), + NULL, NULL); + + ret = iwl_mvm_enter_d0i3(mvm->hw->priv); + if (ret) + goto remove_notif; + + ret = iwl_wait_notification(&mvm->notif_wait, &wait_d3, HZ); + WARN_ON_ONCE(ret); + return ret; + +remove_notif: + iwl_remove_notification(&mvm->notif_wait, &wait_d3); + return ret; +} + +int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + /* make sure the d0i3 exit work is not pending */ + flush_work(&mvm->d0i3_exit_work); + + ret = iwl_trans_suspend(mvm->trans); + if (ret) + return ret; + + mvm->trans->wowlan_d0i3 = wowlan->any; + if (mvm->trans->wowlan_d0i3) { + /* 'any' trigger means d0i3 usage */ + if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { + ret = iwl_mvm_enter_d0i3_sync(mvm); + + if (ret) + return ret; + } + + mutex_lock(&mvm->d0i3_suspend_mutex); + __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); + mutex_unlock(&mvm->d0i3_suspend_mutex); + + iwl_trans_d3_suspend(mvm->trans, false); + + return 0; + } + + return __iwl_mvm_suspend(hw, wowlan, false); +} + +/* converted data from the different status responses */ +struct iwl_wowlan_status_data { + u16 pattern_number; + u16 qos_seq_ctr[8]; + u32 wakeup_reasons; + u32 wake_packet_length; + u32 wake_packet_bufsize; + const u8 *wake_packet; +}; + +static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_wowlan_status_data *status) +{ + struct sk_buff *pkt = NULL; + struct cfg80211_wowlan_wakeup wakeup = { + .pattern_idx = -1, + }; + struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; + u32 reasons = status->wakeup_reasons; + + if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { + wakeup_report = NULL; + goto report; + } + + if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) + wakeup.magic_pkt = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) + wakeup.pattern_idx = + status->pattern_number; + + if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) + wakeup.disconnect = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) + wakeup.gtk_rekey_failure = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) + wakeup.rfkill_release = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) + wakeup.eap_identity_req = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) + wakeup.four_way_handshake = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS) + wakeup.tcp_connlost = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE) + wakeup.tcp_nomoretokens = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET) + wakeup.tcp_match = true; + + if (status->wake_packet_bufsize) { + int pktsize = status->wake_packet_bufsize; + int pktlen = status->wake_packet_length; + const u8 *pktdata = status->wake_packet; + struct ieee80211_hdr *hdr = (void *)pktdata; + int truncated = pktlen - pktsize; + + /* this would be a firmware bug */ + if (WARN_ON_ONCE(truncated < 0)) + truncated = 0; + + if (ieee80211_is_data(hdr->frame_control)) { + int hdrlen = ieee80211_hdrlen(hdr->frame_control); + int ivlen = 0, icvlen = 4; /* also FCS */ + + pkt = alloc_skb(pktsize, GFP_KERNEL); + if (!pkt) + goto report; + + memcpy(skb_put(pkt, hdrlen), pktdata, hdrlen); + pktdata += hdrlen; + pktsize -= hdrlen; + + if (ieee80211_has_protected(hdr->frame_control)) { + /* + * This is unlocked and using gtk_i(c)vlen, + * but since everything is under RTNL still + * that's not really a problem - changing + * it would be difficult. + */ + if (is_multicast_ether_addr(hdr->addr1)) { + ivlen = mvm->gtk_ivlen; + icvlen += mvm->gtk_icvlen; + } else { + ivlen = mvm->ptk_ivlen; + icvlen += mvm->ptk_icvlen; + } + } + + /* if truncated, FCS/ICV is (partially) gone */ + if (truncated >= icvlen) { + icvlen = 0; + truncated -= icvlen; + } else { + icvlen -= truncated; + truncated = 0; + } + + pktsize -= ivlen + icvlen; + pktdata += ivlen; + + memcpy(skb_put(pkt, pktsize), pktdata, pktsize); + + if (ieee80211_data_to_8023(pkt, vif->addr, vif->type)) + goto report; + wakeup.packet = pkt->data; + wakeup.packet_present_len = pkt->len; + wakeup.packet_len = pkt->len - truncated; + wakeup.packet_80211 = false; + } else { + int fcslen = 4; + + if (truncated >= 4) { + truncated -= 4; + fcslen = 0; + } else { + fcslen -= truncated; + truncated = 0; + } + pktsize -= fcslen; + wakeup.packet = status->wake_packet; + wakeup.packet_present_len = pktsize; + wakeup.packet_len = pktlen - truncated; + wakeup.packet_80211 = true; + } + } + + report: + ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); + kfree_skb(pkt); +} + +static void iwl_mvm_aes_sc_to_seq(struct aes_sc *sc, + struct ieee80211_key_seq *seq) +{ + u64 pn; + + pn = le64_to_cpu(sc->pn); + seq->ccmp.pn[0] = pn >> 40; + seq->ccmp.pn[1] = pn >> 32; + seq->ccmp.pn[2] = pn >> 24; + seq->ccmp.pn[3] = pn >> 16; + seq->ccmp.pn[4] = pn >> 8; + seq->ccmp.pn[5] = pn; +} + +static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc, + struct ieee80211_key_seq *seq) +{ + seq->tkip.iv32 = le32_to_cpu(sc->iv32); + seq->tkip.iv16 = le16_to_cpu(sc->iv16); +} + +static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs, + struct ieee80211_key_conf *key) +{ + int tid; + + BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); + + for (tid = 0; tid < IWL_NUM_RSC; tid++) { + struct ieee80211_key_seq seq = {}; + + iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + } +} + +static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs, + struct ieee80211_key_conf *key) +{ + int tid; + + BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); + + for (tid = 0; tid < IWL_NUM_RSC; tid++) { + struct ieee80211_key_seq seq = {}; + + iwl_mvm_tkip_sc_to_seq(&scs[tid], &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + } +} + +static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key, + struct iwl_wowlan_status *status) +{ + union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key); + break; + case WLAN_CIPHER_SUITE_TKIP: + iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key); + break; + default: + WARN_ON(1); + } +} + +struct iwl_mvm_d3_gtk_iter_data { + struct iwl_wowlan_status *status; + void *last_gtk; + u32 cipher; + bool find_phase, unhandled_cipher; + int num_keys; +}; + +static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mvm_d3_gtk_iter_data *data = _data; + + if (data->unhandled_cipher) + return; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* ignore WEP completely, nothing to do */ + return; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_TKIP: + /* we support these */ + break; + default: + /* everything else (even CMAC for MFP) - disconnect from AP */ + data->unhandled_cipher = true; + return; + } + + data->num_keys++; + + /* + * pairwise key - update sequence counters only; + * note that this assumes no TDLS sessions are active + */ + if (sta) { + struct ieee80211_key_seq seq = {}; + union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc; + + if (data->find_phase) + return; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key); + atomic64_set(&key->tx_pn, le64_to_cpu(sc->aes.tsc.pn)); + break; + case WLAN_CIPHER_SUITE_TKIP: + iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq); + iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key); + ieee80211_set_key_tx_seq(key, &seq); + break; + } + + /* that's it for this key */ + return; + } + + if (data->find_phase) { + data->last_gtk = key; + data->cipher = key->cipher; + return; + } + + if (data->status->num_of_gtk_rekeys) + ieee80211_remove_key(key); + else if (data->last_gtk == key) + iwl_mvm_set_key_rx_seq(key, data->status); +} + +static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_wowlan_status *status) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_d3_gtk_iter_data gtkdata = { + .status = status, + }; + u32 disconnection_reasons = + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; + + if (!status || !vif->bss_conf.bssid) + return false; + + if (le32_to_cpu(status->wakeup_reasons) & disconnection_reasons) + return false; + + /* find last GTK that we used initially, if any */ + gtkdata.find_phase = true; + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_d3_update_gtks, >kdata); + /* not trying to keep connections with MFP/unhandled ciphers */ + if (gtkdata.unhandled_cipher) + return false; + if (!gtkdata.num_keys) + goto out; + if (!gtkdata.last_gtk) + return false; + + /* + * invalidate all other GTKs that might still exist and update + * the one that we used + */ + gtkdata.find_phase = false; + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_d3_update_gtks, >kdata); + + if (status->num_of_gtk_rekeys) { + struct ieee80211_key_conf *key; + struct { + struct ieee80211_key_conf conf; + u8 key[32]; + } conf = { + .conf.cipher = gtkdata.cipher, + .conf.keyidx = status->gtk.key_index, + }; + + switch (gtkdata.cipher) { + case WLAN_CIPHER_SUITE_CCMP: + conf.conf.keylen = WLAN_KEY_LEN_CCMP; + memcpy(conf.conf.key, status->gtk.decrypt_key, + WLAN_KEY_LEN_CCMP); + break; + case WLAN_CIPHER_SUITE_TKIP: + conf.conf.keylen = WLAN_KEY_LEN_TKIP; + memcpy(conf.conf.key, status->gtk.decrypt_key, 16); + /* leave TX MIC key zeroed, we don't use it anyway */ + memcpy(conf.conf.key + + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, + status->gtk.tkip_mic_key, 8); + break; + } + + key = ieee80211_gtk_rekey_add(vif, &conf.conf); + if (IS_ERR(key)) + return false; + iwl_mvm_set_key_rx_seq(key, status); + } + + if (status->num_of_gtk_rekeys) { + __be64 replay_ctr = + cpu_to_be64(le64_to_cpu(status->replay_ctr)); + ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid, + (void *)&replay_ctr, GFP_KERNEL); + } + +out: + mvmvif->seqno_valid = true; + /* +0x10 because the set API expects next-to-use, not last-used */ + mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10; + + return true; +} + +static struct iwl_wowlan_status * +iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + u32 base = mvm->error_event_table; + struct error_table_start { + /* cf. struct iwl_error_event_table */ + u32 valid; + u32 error_id; + } err_info; + struct iwl_host_cmd cmd = { + .id = WOWLAN_GET_STATUSES, + .flags = CMD_WANT_SKB, + }; + struct iwl_wowlan_status *status, *fw_status; + int ret, len, status_size; + + iwl_trans_read_mem_bytes(mvm->trans, base, + &err_info, sizeof(err_info)); + + if (err_info.valid) { + IWL_INFO(mvm, "error table is valid (%d) with error (%d)\n", + err_info.valid, err_info.error_id); + if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) { + struct cfg80211_wowlan_wakeup wakeup = { + .rfkill_release = true, + }; + ieee80211_report_wowlan_wakeup(vif, &wakeup, + GFP_KERNEL); + } + return ERR_PTR(-EIO); + } + + /* only for tracing for now */ + ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, 0, 0, NULL); + if (ret) + IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret) { + IWL_ERR(mvm, "failed to query status (%d)\n", ret); + return ERR_PTR(ret); + } + + /* RF-kill already asserted again... */ + if (!cmd.resp_pkt) { + fw_status = ERR_PTR(-ERFKILL); + goto out_free_resp; + } + + status_size = sizeof(*fw_status); + + len = iwl_rx_packet_payload_len(cmd.resp_pkt); + if (len < status_size) { + IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); + fw_status = ERR_PTR(-EIO); + goto out_free_resp; + } + + status = (void *)cmd.resp_pkt->data; + if (len != (status_size + + ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) { + IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); + fw_status = ERR_PTR(-EIO); + goto out_free_resp; + } + + fw_status = kmemdup(status, len, GFP_KERNEL); + +out_free_resp: + iwl_free_resp(&cmd); + return fw_status; +} + +/* releases the MVM mutex */ +static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_wowlan_status_data status; + struct iwl_wowlan_status *fw_status; + int i; + bool keep; + struct ieee80211_sta *ap_sta; + struct iwl_mvm_sta *mvm_ap_sta; + + fw_status = iwl_mvm_get_wakeup_status(mvm, vif); + if (IS_ERR_OR_NULL(fw_status)) + goto out_unlock; + + status.pattern_number = le16_to_cpu(fw_status->pattern_number); + for (i = 0; i < 8; i++) + status.qos_seq_ctr[i] = + le16_to_cpu(fw_status->qos_seq_ctr[i]); + status.wakeup_reasons = le32_to_cpu(fw_status->wakeup_reasons); + status.wake_packet_length = + le32_to_cpu(fw_status->wake_packet_length); + status.wake_packet_bufsize = + le32_to_cpu(fw_status->wake_packet_bufsize); + status.wake_packet = fw_status->wake_packet; + + /* still at hard-coded place 0 for D3 image */ + ap_sta = rcu_dereference_protected( + mvm->fw_id_to_mac_id[0], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(ap_sta)) + goto out_free; + + mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + u16 seq = status.qos_seq_ctr[i]; + /* firmware stores last-used value, we store next value */ + seq += 0x10; + mvm_ap_sta->tid_data[i].seq_number = seq; + } + + /* now we have all the data we need, unlock to avoid mac80211 issues */ + mutex_unlock(&mvm->mutex); + + iwl_mvm_report_wakeup_reasons(mvm, vif, &status); + + keep = iwl_mvm_setup_connection_keep(mvm, vif, fw_status); + + kfree(fw_status); + return keep; + +out_free: + kfree(fw_status); +out_unlock: + mutex_unlock(&mvm->mutex); + return false; +} + +struct iwl_mvm_nd_query_results { + u32 matched_profiles; + struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES]; +}; + +static int +iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm, + struct iwl_mvm_nd_query_results *results) +{ + struct iwl_scan_offload_profiles_query *query; + struct iwl_host_cmd cmd = { + .id = SCAN_OFFLOAD_PROFILES_QUERY_CMD, + .flags = CMD_WANT_SKB, + }; + int ret, len; + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret) { + IWL_ERR(mvm, "failed to query matched profiles (%d)\n", ret); + return ret; + } + + /* RF-kill already asserted again... */ + if (!cmd.resp_pkt) { + ret = -ERFKILL; + goto out_free_resp; + } + + len = iwl_rx_packet_payload_len(cmd.resp_pkt); + if (len < sizeof(*query)) { + IWL_ERR(mvm, "Invalid scan offload profiles query response!\n"); + ret = -EIO; + goto out_free_resp; + } + + query = (void *)cmd.resp_pkt->data; + + results->matched_profiles = le32_to_cpu(query->matched_profiles); + memcpy(results->matches, query->matches, sizeof(results->matches)); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + mvm->last_netdetect_scans = le32_to_cpu(query->n_scans_done); +#endif + +out_free_resp: + iwl_free_resp(&cmd); + return ret; +} + +static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct cfg80211_wowlan_nd_info *net_detect = NULL; + struct cfg80211_wowlan_wakeup wakeup = { + .pattern_idx = -1, + }; + struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; + struct iwl_mvm_nd_query_results query; + struct iwl_wowlan_status *fw_status; + unsigned long matched_profiles; + u32 reasons = 0; + int i, j, n_matches, ret; + + fw_status = iwl_mvm_get_wakeup_status(mvm, vif); + if (!IS_ERR_OR_NULL(fw_status)) { + reasons = le32_to_cpu(fw_status->wakeup_reasons); + kfree(fw_status); + } + + if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) + wakeup.rfkill_release = true; + + if (reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) + goto out; + + ret = iwl_mvm_netdetect_query_results(mvm, &query); + if (ret || !query.matched_profiles) { + wakeup_report = NULL; + goto out; + } + + matched_profiles = query.matched_profiles; + if (mvm->n_nd_match_sets) { + n_matches = hweight_long(matched_profiles); + } else { + IWL_ERR(mvm, "no net detect match information available\n"); + n_matches = 0; + } + + net_detect = kzalloc(sizeof(*net_detect) + + (n_matches * sizeof(net_detect->matches[0])), + GFP_KERNEL); + if (!net_detect || !n_matches) + goto out_report_nd; + + for_each_set_bit(i, &matched_profiles, mvm->n_nd_match_sets) { + struct iwl_scan_offload_profile_match *fw_match; + struct cfg80211_wowlan_nd_match *match; + int idx, n_channels = 0; + + fw_match = &query.matches[i]; + + for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; j++) + n_channels += hweight8(fw_match->matching_channels[j]); + + match = kzalloc(sizeof(*match) + + (n_channels * sizeof(*match->channels)), + GFP_KERNEL); + if (!match) + goto out_report_nd; + + net_detect->matches[net_detect->n_matches++] = match; + + /* We inverted the order of the SSIDs in the scan + * request, so invert the index here. + */ + idx = mvm->n_nd_match_sets - i - 1; + match->ssid.ssid_len = mvm->nd_match_sets[idx].ssid.ssid_len; + memcpy(match->ssid.ssid, mvm->nd_match_sets[idx].ssid.ssid, + match->ssid.ssid_len); + + if (mvm->n_nd_channels < n_channels) + continue; + + for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; j++) + if (fw_match->matching_channels[j / 8] & (BIT(j % 8))) + match->channels[match->n_channels++] = + mvm->nd_channels[j]->center_freq; + } + +out_report_nd: + wakeup.net_detect = net_detect; +out: + iwl_mvm_free_nd(mvm); + + mutex_unlock(&mvm->mutex); + ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); + + if (net_detect) { + for (i = 0; i < net_detect->n_matches; i++) + kfree(net_detect->matches[i]); + kfree(net_detect); + } +} + +static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) +{ +#ifdef CONFIG_IWLWIFI_DEBUGFS + const struct fw_img *img = &mvm->fw->img[IWL_UCODE_WOWLAN]; + u32 len = img->sec[IWL_UCODE_SECTION_DATA].len; + u32 offs = img->sec[IWL_UCODE_SECTION_DATA].offset; + + if (!mvm->store_d3_resume_sram) + return; + + if (!mvm->d3_resume_sram) { + mvm->d3_resume_sram = kzalloc(len, GFP_KERNEL); + if (!mvm->d3_resume_sram) + return; + } + + iwl_trans_read_mem_bytes(mvm->trans, offs, mvm->d3_resume_sram, len); +#endif +} + +static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + /* skip the one we keep connection on */ + if (data == vif) + return; + + if (vif->type == NL80211_IFTYPE_STATION) + ieee80211_resume_disconnect(vif); +} + +static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) +{ + struct ieee80211_vif *vif = NULL; + int ret; + enum iwl_d3_status d3_status; + bool keep = false; + + mutex_lock(&mvm->mutex); + + /* get the BSS vif pointer again */ + vif = iwl_mvm_get_bss_vif(mvm); + if (IS_ERR_OR_NULL(vif)) + goto err; + + ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test); + if (ret) + goto err; + + if (d3_status != IWL_D3_STATUS_ALIVE) { + IWL_INFO(mvm, "Device was reset during suspend\n"); + goto err; + } + + /* query SRAM first in case we want event logging */ + iwl_mvm_read_d3_sram(mvm); + + /* + * Query the current location and source from the D3 firmware so we + * can play it back when we re-intiailize the D0 firmware + */ + iwl_mvm_update_changed_regdom(mvm); + + if (mvm->net_detect) { + iwl_mvm_query_netdetect_reasons(mvm, vif); + /* has unlocked the mutex, so skip that */ + goto out; + } else { + keep = iwl_mvm_query_wakeup_reasons(mvm, vif); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (keep) + mvm->keep_vif = vif; +#endif + /* has unlocked the mutex, so skip that */ + goto out_iterate; + } + +err: + iwl_mvm_free_nd(mvm); + mutex_unlock(&mvm->mutex); + +out_iterate: + if (!test) + ieee80211_iterate_active_interfaces_rtnl(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_d3_disconnect_iter, keep ? vif : NULL); + +out: + /* return 1 to reconfigure the device */ + set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status); + + /* We always return 1, which causes mac80211 to do a reconfig + * with IEEE80211_RECONFIG_TYPE_RESTART. This type of + * reconfig calls iwl_mvm_restart_complete(), where we unref + * the IWL_MVM_REF_UCODE_DOWN, so we need to take the + * reference here. + */ + iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); + return 1; +} + +static int iwl_mvm_resume_d3(struct iwl_mvm *mvm) +{ + iwl_trans_resume(mvm->trans); + + return __iwl_mvm_resume(mvm, false); +} + +static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm) +{ + bool exit_now; + enum iwl_d3_status d3_status; + + iwl_trans_d3_resume(mvm->trans, &d3_status, false); + + /* + * make sure to clear D0I3_DEFER_WAKEUP before + * calling iwl_trans_resume(), which might wait + * for d0i3 exit completion. + */ + mutex_lock(&mvm->d0i3_suspend_mutex); + __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); + exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP, + &mvm->d0i3_suspend_flags); + mutex_unlock(&mvm->d0i3_suspend_mutex); + if (exit_now) { + IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n"); + _iwl_mvm_exit_d0i3(mvm); + } + + iwl_trans_resume(mvm->trans); + + if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { + int ret = iwl_mvm_exit_d0i3(mvm->hw->priv); + + if (ret) + return ret; + /* + * d0i3 exit will be deferred until reconfig_complete. + * make sure there we are out of d0i3. + */ + } + return 0; +} + +int iwl_mvm_resume(struct ieee80211_hw *hw) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + /* 'any' trigger means d0i3 was used */ + if (hw->wiphy->wowlan_config->any) + return iwl_mvm_resume_d0i3(mvm); + else + return iwl_mvm_resume_d3(mvm); +} + +void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + device_set_wakeup_enable(mvm->trans->dev, enabled); +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file) +{ + struct iwl_mvm *mvm = inode->i_private; + int err; + + if (mvm->d3_test_active) + return -EBUSY; + + file->private_data = inode->i_private; + + ieee80211_stop_queues(mvm->hw); + synchronize_net(); + + /* start pseudo D3 */ + rtnl_lock(); + err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true); + rtnl_unlock(); + if (err > 0) + err = -EINVAL; + if (err) { + ieee80211_wake_queues(mvm->hw); + return err; + } + mvm->d3_test_active = true; + mvm->keep_vif = NULL; + return 0; +} + +static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + u32 pme_asserted; + + while (true) { + /* read pme_ptr if available */ + if (mvm->d3_test_pme_ptr) { + pme_asserted = iwl_trans_read_mem32(mvm->trans, + mvm->d3_test_pme_ptr); + if (pme_asserted) + break; + } + + if (msleep_interruptible(100)) + break; + } + + return 0; +} + +static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + /* skip the one we keep connection on */ + if (_data == vif) + return; + + if (vif->type == NL80211_IFTYPE_STATION) + ieee80211_connection_loss(vif); +} + +static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) +{ + struct iwl_mvm *mvm = inode->i_private; + int remaining_time = 10; + + mvm->d3_test_active = false; + rtnl_lock(); + __iwl_mvm_resume(mvm, true); + rtnl_unlock(); + iwl_abort_notification_waits(&mvm->notif_wait); + ieee80211_restart_hw(mvm->hw); + + /* wait for restart and disconnect all interfaces */ + while (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + remaining_time > 0) { + remaining_time--; + msleep(1000); + } + + if (remaining_time == 0) + IWL_ERR(mvm, "Timed out waiting for HW restart to finish!\n"); + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_d3_test_disconn_work_iter, mvm->keep_vif); + + ieee80211_wake_queues(mvm->hw); + + return 0; +} + +const struct file_operations iwl_dbgfs_d3_test_ops = { + .llseek = no_llseek, + .open = iwl_mvm_d3_test_open, + .read = iwl_mvm_d3_test_read, + .release = iwl_mvm_d3_test_release, +}; +#endif diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c new file mode 100644 index 000000000000..7904b41a04c6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -0,0 +1,1483 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include "mvm.h" +#include "fw-api-tof.h" +#include "debugfs.h" + +static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + enum iwl_dbgfs_pm_mask param, int val) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; + + dbgfs_pm->mask |= param; + + switch (param) { + case MVM_DEBUGFS_PM_KEEP_ALIVE: { + int dtimper = vif->bss_conf.dtim_period ?: 1; + int dtimper_msec = dtimper * vif->bss_conf.beacon_int; + + IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); + if (val * MSEC_PER_SEC < 3 * dtimper_msec) + IWL_WARN(mvm, + "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", + val * MSEC_PER_SEC, 3 * dtimper_msec); + dbgfs_pm->keep_alive_seconds = val; + break; + } + case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: + IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", + val ? "enabled" : "disabled"); + dbgfs_pm->skip_over_dtim = val; + break; + case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: + IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); + dbgfs_pm->skip_dtim_periods = val; + break; + case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); + dbgfs_pm->rx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); + dbgfs_pm->tx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_LPRX_ENA: + IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled"); + dbgfs_pm->lprx_ena = val; + break; + case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD: + IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val); + dbgfs_pm->lprx_rssi_threshold = val; + break; + case MVM_DEBUGFS_PM_SNOOZE_ENABLE: + IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val); + dbgfs_pm->snooze_ena = val; + break; + case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING: + IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val); + dbgfs_pm->uapsd_misbehaving = val; + break; + case MVM_DEBUGFS_PM_USE_PS_POLL: + IWL_DEBUG_POWER(mvm, "use_ps_poll=%d\n", val); + dbgfs_pm->use_ps_poll = val; + break; + } +} + +static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + enum iwl_dbgfs_pm_mask param; + int val, ret; + + if (!strncmp("keep_alive=", buf, 11)) { + if (sscanf(buf + 11, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_KEEP_ALIVE; + } else if (!strncmp("skip_over_dtim=", buf, 15)) { + if (sscanf(buf + 15, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; + } else if (!strncmp("skip_dtim_periods=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; + } else if (!strncmp("rx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; + } else if (!strncmp("tx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; + } else if (!strncmp("lprx=", buf, 5)) { + if (sscanf(buf + 5, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_LPRX_ENA; + } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) { + if (sscanf(buf + 20, "%d", &val) != 1) + return -EINVAL; + if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val < + POWER_LPRX_RSSI_THRESHOLD_MIN) + return -EINVAL; + param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD; + } else if (!strncmp("snooze_enable=", buf, 14)) { + if (sscanf(buf + 14, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SNOOZE_ENABLE; + } else if (!strncmp("uapsd_misbehaving=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING; + } else if (!strncmp("use_ps_poll=", buf, 12)) { + if (sscanf(buf + 12, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_USE_PS_POLL; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_pm(mvm, vif, param, val); + ret = iwl_mvm_power_update_mac(mvm); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_tx_pwr_lmt_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + char buf[64]; + int bufsz = sizeof(buf); + int pos; + + pos = scnprintf(buf, bufsz, "bss limit = %d\n", + vif->bss_conf.txpower); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_pm_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + char buf[512]; + int bufsz = sizeof(buf); + int pos; + + pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_mac_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + u8 ap_sta_id; + struct ieee80211_chanctx_conf *chanctx_conf; + char buf[512]; + int bufsz = sizeof(buf); + int pos = 0; + int i; + + mutex_lock(&mvm->mutex); + + ap_sta_id = mvmvif->ap_sta_id; + + switch (ieee80211_vif_type_p2p(vif)) { + case NL80211_IFTYPE_ADHOC: + pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n"); + break; + case NL80211_IFTYPE_STATION: + pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n"); + break; + case NL80211_IFTYPE_AP: + pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n"); + break; + case NL80211_IFTYPE_P2P_CLIENT: + pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n"); + break; + case NL80211_IFTYPE_P2P_GO: + pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n"); + break; + case NL80211_IFTYPE_P2P_DEVICE: + pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n"); + break; + default: + break; + } + + pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n", + mvmvif->id, mvmvif->color); + pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n", + vif->bss_conf.bssid); + pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n"); + for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) + pos += scnprintf(buf+pos, bufsz-pos, + "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n", + i, mvmvif->queue_params[i].txop, + mvmvif->queue_params[i].cw_min, + mvmvif->queue_params[i].cw_max, + mvmvif->queue_params[i].aifs, + mvmvif->queue_params[i].uapsd); + + if (vif->type == NL80211_IFTYPE_STATION && + ap_sta_id != IWL_MVM_STATION_COUNT) { + struct ieee80211_sta *sta; + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], + lockdep_is_held(&mvm->mutex)); + if (!IS_ERR_OR_NULL(sta)) { + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + + pos += scnprintf(buf+pos, bufsz-pos, + "ap_sta_id %d - reduced Tx power %d\n", + ap_sta_id, + mvm_sta->bt_reduced_txpower); + } + } + + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + if (chanctx_conf) + pos += scnprintf(buf+pos, bufsz-pos, + "idle rx chains %d, active rx chains: %d\n", + chanctx_conf->rx_chains_static, + chanctx_conf->rx_chains_dynamic); + rcu_read_unlock(); + + mutex_unlock(&mvm->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, + enum iwl_dbgfs_bf_mask param, int value) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; + + dbgfs_bf->mask |= param; + + switch (param) { + case MVM_DEBUGFS_BF_ENERGY_DELTA: + dbgfs_bf->bf_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA: + dbgfs_bf->bf_roaming_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_STATE: + dbgfs_bf->bf_roaming_state = value; + break; + case MVM_DEBUGFS_BF_TEMP_THRESHOLD: + dbgfs_bf->bf_temp_threshold = value; + break; + case MVM_DEBUGFS_BF_TEMP_FAST_FILTER: + dbgfs_bf->bf_temp_fast_filter = value; + break; + case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER: + dbgfs_bf->bf_temp_slow_filter = value; + break; + case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: + dbgfs_bf->bf_enable_beacon_filter = value; + break; + case MVM_DEBUGFS_BF_DEBUG_FLAG: + dbgfs_bf->bf_debug_flag = value; + break; + case MVM_DEBUGFS_BF_ESCAPE_TIMER: + dbgfs_bf->bf_escape_timer = value; + break; + case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT: + dbgfs_bf->ba_enable_beacon_abort = value; + break; + case MVM_DEBUGFS_BA_ESCAPE_TIMER: + dbgfs_bf->ba_escape_timer = value; + break; + } +} + +static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + enum iwl_dbgfs_bf_mask param; + int value, ret = 0; + + if (!strncmp("bf_energy_delta=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ENERGY_DELTA_MIN || + value > IWL_BF_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN || + value > IWL_BF_ROAMING_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_state=", buf, 17)) { + if (sscanf(buf+17, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_STATE_MIN || + value > IWL_BF_ROAMING_STATE_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_STATE; + } else if (!strncmp("bf_temp_threshold=", buf, 18)) { + if (sscanf(buf+18, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMP_THRESHOLD_MIN || + value > IWL_BF_TEMP_THRESHOLD_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMP_THRESHOLD; + } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) { + if (sscanf(buf+20, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMP_FAST_FILTER_MIN || + value > IWL_BF_TEMP_FAST_FILTER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER; + } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) { + if (sscanf(buf+20, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMP_SLOW_FILTER_MIN || + value > IWL_BF_TEMP_SLOW_FILTER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER; + } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER; + } else if (!strncmp("bf_debug_flag=", buf, 14)) { + if (sscanf(buf+14, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_DEBUG_FLAG; + } else if (!strncmp("bf_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ESCAPE_TIMER_MIN || + value > IWL_BF_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ESCAPE_TIMER; + } else if (!strncmp("ba_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BA_ESCAPE_TIMER_MIN || + value > IWL_BA_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BA_ESCAPE_TIMER; + } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) { + if (sscanf(buf+23, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_bf(vif, param, value); + if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) + ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); + else + ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_bf_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = + cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT), + .ba_enable_beacon_abort = + cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT), + }; + + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); + if (mvmvif->bf_data.bf_enabled) + cmd.bf_enable_beacon_filter = cpu_to_le32(1); + else + cmd.bf_enable_beacon_filter = 0; + + pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", + le32_to_cpu(cmd.bf_energy_delta)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", + le32_to_cpu(cmd.bf_roaming_energy_delta)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", + le32_to_cpu(cmd.bf_roaming_state)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n", + le32_to_cpu(cmd.bf_temp_threshold)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n", + le32_to_cpu(cmd.bf_temp_fast_filter)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n", + le32_to_cpu(cmd.bf_temp_slow_filter)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", + le32_to_cpu(cmd.bf_enable_beacon_filter)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", + le32_to_cpu(cmd.bf_debug_flag)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", + le32_to_cpu(cmd.bf_escape_timer)); + pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", + le32_to_cpu(cmd.ba_escape_timer)); + pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", + le32_to_cpu(cmd.ba_enable_beacon_abort)); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static inline char *iwl_dbgfs_is_match(char *name, char *buf) +{ + int len = strlen(name); + + return !strncmp(name, buf, len) ? buf + len : NULL; +} + +static ssize_t iwl_dbgfs_tof_enable_write(struct ieee80211_vif *vif, + char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + u32 value; + int ret = -EINVAL; + char *data; + + mutex_lock(&mvm->mutex); + + data = iwl_dbgfs_is_match("tof_disabled=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.tof_cfg.tof_disabled = value; + goto out; + } + + data = iwl_dbgfs_is_match("one_sided_disabled=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.tof_cfg.one_sided_disabled = value; + goto out; + } + + data = iwl_dbgfs_is_match("is_debug_mode=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.tof_cfg.is_debug_mode = value; + goto out; + } + + data = iwl_dbgfs_is_match("is_buf=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.tof_cfg.is_buf_required = value; + goto out; + } + + data = iwl_dbgfs_is_match("send_tof_cfg=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0 && value) { + ret = iwl_mvm_tof_config_cmd(mvm); + goto out; + } + } + +out: + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_tof_enable_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + struct iwl_tof_config_cmd *cmd; + + cmd = &mvm->tof_data.tof_cfg; + + mutex_lock(&mvm->mutex); + + pos += scnprintf(buf + pos, bufsz - pos, "tof_disabled = %d\n", + cmd->tof_disabled); + pos += scnprintf(buf + pos, bufsz - pos, "one_sided_disabled = %d\n", + cmd->one_sided_disabled); + pos += scnprintf(buf + pos, bufsz - pos, "is_debug_mode = %d\n", + cmd->is_debug_mode); + pos += scnprintf(buf + pos, bufsz - pos, "is_buf_required = %d\n", + cmd->is_buf_required); + + mutex_unlock(&mvm->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_tof_responder_params_write(struct ieee80211_vif *vif, + char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + u32 value; + int ret = 0; + char *data; + + mutex_lock(&mvm->mutex); + + data = iwl_dbgfs_is_match("burst_period=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (!ret) + mvm->tof_data.responder_cfg.burst_period = + cpu_to_le16(value); + goto out; + } + + data = iwl_dbgfs_is_match("min_delta_ftm=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.min_delta_ftm = value; + goto out; + } + + data = iwl_dbgfs_is_match("burst_duration=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.burst_duration = value; + goto out; + } + + data = iwl_dbgfs_is_match("num_of_burst_exp=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.num_of_burst_exp = value; + goto out; + } + + data = iwl_dbgfs_is_match("abort_responder=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.abort_responder = value; + goto out; + } + + data = iwl_dbgfs_is_match("get_ch_est=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.get_ch_est = value; + goto out; + } + + data = iwl_dbgfs_is_match("recv_sta_req_params=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.recv_sta_req_params = value; + goto out; + } + + data = iwl_dbgfs_is_match("channel_num=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.channel_num = value; + goto out; + } + + data = iwl_dbgfs_is_match("bandwidth=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.bandwidth = value; + goto out; + } + + data = iwl_dbgfs_is_match("rate=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.rate = value; + goto out; + } + + data = iwl_dbgfs_is_match("bssid=", buf); + if (data) { + u8 *mac = mvm->tof_data.responder_cfg.bssid; + + if (!mac_pton(data, mac)) { + ret = -EINVAL; + goto out; + } + } + + data = iwl_dbgfs_is_match("tsf_timer_offset_msecs=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.tsf_timer_offset_msecs = + cpu_to_le16(value); + goto out; + } + + data = iwl_dbgfs_is_match("toa_offset=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.toa_offset = + cpu_to_le16(value); + goto out; + } + + data = iwl_dbgfs_is_match("center_freq=", buf); + if (data) { + struct iwl_tof_responder_config_cmd *cmd = + &mvm->tof_data.responder_cfg; + + ret = kstrtou32(data, 10, &value); + if (ret == 0 && value) { + enum ieee80211_band band = (cmd->channel_num <= 14) ? + IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ; + struct ieee80211_channel chn = { + .band = band, + .center_freq = ieee80211_channel_to_frequency( + cmd->channel_num, band), + }; + struct cfg80211_chan_def chandef = { + .chan = &chn, + .center_freq1 = + ieee80211_channel_to_frequency(value, + band), + }; + + cmd->ctrl_ch_position = iwl_mvm_get_ctrl_pos(&chandef); + } + goto out; + } + + data = iwl_dbgfs_is_match("ftm_per_burst=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.ftm_per_burst = value; + goto out; + } + + data = iwl_dbgfs_is_match("ftm_resp_ts_avail=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.ftm_resp_ts_avail = value; + goto out; + } + + data = iwl_dbgfs_is_match("asap_mode=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.responder_cfg.asap_mode = value; + goto out; + } + + data = iwl_dbgfs_is_match("send_responder_cfg=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0 && value) { + ret = iwl_mvm_tof_responder_cmd(mvm, vif); + goto out; + } + } + +out: + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_tof_responder_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + struct iwl_tof_responder_config_cmd *cmd; + + cmd = &mvm->tof_data.responder_cfg; + + mutex_lock(&mvm->mutex); + + pos += scnprintf(buf + pos, bufsz - pos, "burst_period = %d\n", + le16_to_cpu(cmd->burst_period)); + pos += scnprintf(buf + pos, bufsz - pos, "burst_duration = %d\n", + cmd->burst_duration); + pos += scnprintf(buf + pos, bufsz - pos, "bandwidth = %d\n", + cmd->bandwidth); + pos += scnprintf(buf + pos, bufsz - pos, "channel_num = %d\n", + cmd->channel_num); + pos += scnprintf(buf + pos, bufsz - pos, "ctrl_ch_position = 0x%x\n", + cmd->ctrl_ch_position); + pos += scnprintf(buf + pos, bufsz - pos, "bssid = %pM\n", + cmd->bssid); + pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %d\n", + cmd->min_delta_ftm); + pos += scnprintf(buf + pos, bufsz - pos, "num_of_burst_exp = %d\n", + cmd->num_of_burst_exp); + pos += scnprintf(buf + pos, bufsz - pos, "rate = %d\n", cmd->rate); + pos += scnprintf(buf + pos, bufsz - pos, "abort_responder = %d\n", + cmd->abort_responder); + pos += scnprintf(buf + pos, bufsz - pos, "get_ch_est = %d\n", + cmd->get_ch_est); + pos += scnprintf(buf + pos, bufsz - pos, "recv_sta_req_params = %d\n", + cmd->recv_sta_req_params); + pos += scnprintf(buf + pos, bufsz - pos, "ftm_per_burst = %d\n", + cmd->ftm_per_burst); + pos += scnprintf(buf + pos, bufsz - pos, "ftm_resp_ts_avail = %d\n", + cmd->ftm_resp_ts_avail); + pos += scnprintf(buf + pos, bufsz - pos, "asap_mode = %d\n", + cmd->asap_mode); + pos += scnprintf(buf + pos, bufsz - pos, + "tsf_timer_offset_msecs = %d\n", + le16_to_cpu(cmd->tsf_timer_offset_msecs)); + pos += scnprintf(buf + pos, bufsz - pos, "toa_offset = %d\n", + le16_to_cpu(cmd->toa_offset)); + + mutex_unlock(&mvm->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif, + char *buf, size_t count, + loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + u32 value; + int ret = 0; + char *data; + + mutex_lock(&mvm->mutex); + + data = iwl_dbgfs_is_match("request_id=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req.request_id = value; + goto out; + } + + data = iwl_dbgfs_is_match("initiator=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req.initiator = value; + goto out; + } + + data = iwl_dbgfs_is_match("one_sided_los_disable=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req.one_sided_los_disable = value; + goto out; + } + + data = iwl_dbgfs_is_match("req_timeout=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req.req_timeout = value; + goto out; + } + + data = iwl_dbgfs_is_match("report_policy=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req.report_policy = value; + goto out; + } + + data = iwl_dbgfs_is_match("macaddr_random=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req.macaddr_random = value; + goto out; + } + + data = iwl_dbgfs_is_match("num_of_ap=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req.num_of_ap = value; + goto out; + } + + data = iwl_dbgfs_is_match("macaddr_template=", buf); + if (data) { + u8 mac[ETH_ALEN]; + + if (!mac_pton(data, mac)) { + ret = -EINVAL; + goto out; + } + memcpy(mvm->tof_data.range_req.macaddr_template, mac, ETH_ALEN); + goto out; + } + + data = iwl_dbgfs_is_match("macaddr_mask=", buf); + if (data) { + u8 mac[ETH_ALEN]; + + if (!mac_pton(data, mac)) { + ret = -EINVAL; + goto out; + } + memcpy(mvm->tof_data.range_req.macaddr_mask, mac, ETH_ALEN); + goto out; + } + + data = iwl_dbgfs_is_match("ap=", buf); + if (data) { + struct iwl_tof_range_req_ap_entry ap = {}; + int size = sizeof(struct iwl_tof_range_req_ap_entry); + u16 burst_period; + u8 *mac = ap.bssid; + unsigned int i; + + if (sscanf(data, "%u %hhd %hhd %hhd" + "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx" + "%hhd %hhd %hd" + "%hhd %hhd %d" + "%hhx %hhd %hhd %hhd", + &i, &ap.channel_num, &ap.bandwidth, + &ap.ctrl_ch_position, + mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5, + &ap.measure_type, &ap.num_of_bursts, + &burst_period, + &ap.samples_per_burst, &ap.retries_per_sample, + &ap.tsf_delta, &ap.location_req, &ap.asap_mode, + &ap.enable_dyn_ack, &ap.rssi) != 20) { + ret = -EINVAL; + goto out; + } + if (i >= IWL_MVM_TOF_MAX_APS) { + IWL_ERR(mvm, "Invalid AP index %d\n", i); + ret = -EINVAL; + goto out; + } + + ap.burst_period = cpu_to_le16(burst_period); + + memcpy(&mvm->tof_data.range_req.ap[i], &ap, size); + goto out; + } + + data = iwl_dbgfs_is_match("send_range_request=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0 && value) + ret = iwl_mvm_tof_range_request_cmd(mvm, vif); + goto out; + } + + ret = -EINVAL; +out: + mutex_unlock(&mvm->mutex); + return ret ?: count; +} + +static ssize_t iwl_dbgfs_tof_range_request_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + char buf[512]; + int pos = 0; + const size_t bufsz = sizeof(buf); + struct iwl_tof_range_req_cmd *cmd; + int i; + + cmd = &mvm->tof_data.range_req; + + mutex_lock(&mvm->mutex); + + pos += scnprintf(buf + pos, bufsz - pos, "request_id= %d\n", + cmd->request_id); + pos += scnprintf(buf + pos, bufsz - pos, "initiator= %d\n", + cmd->initiator); + pos += scnprintf(buf + pos, bufsz - pos, "one_sided_los_disable = %d\n", + cmd->one_sided_los_disable); + pos += scnprintf(buf + pos, bufsz - pos, "req_timeout= %d\n", + cmd->req_timeout); + pos += scnprintf(buf + pos, bufsz - pos, "report_policy= %d\n", + cmd->report_policy); + pos += scnprintf(buf + pos, bufsz - pos, "macaddr_random= %d\n", + cmd->macaddr_random); + pos += scnprintf(buf + pos, bufsz - pos, "macaddr_template= %pM\n", + cmd->macaddr_template); + pos += scnprintf(buf + pos, bufsz - pos, "macaddr_mask= %pM\n", + cmd->macaddr_mask); + pos += scnprintf(buf + pos, bufsz - pos, "num_of_ap= %d\n", + cmd->num_of_ap); + for (i = 0; i < cmd->num_of_ap; i++) { + struct iwl_tof_range_req_ap_entry *ap = &cmd->ap[i]; + + pos += scnprintf(buf + pos, bufsz - pos, + "ap %.2d: channel_num=%hhd bw=%hhd" + " control=%hhd bssid=%pM type=%hhd" + " num_of_bursts=%hhd burst_period=%hd ftm=%hhd" + " retries=%hhd tsf_delta=%d" + " tsf_delta_direction=%hhd location_req=0x%hhx " + " asap=%hhd enable=%hhd rssi=%hhd\n", + i, ap->channel_num, ap->bandwidth, + ap->ctrl_ch_position, ap->bssid, + ap->measure_type, ap->num_of_bursts, + ap->burst_period, ap->samples_per_burst, + ap->retries_per_sample, ap->tsf_delta, + ap->tsf_delta_direction, + ap->location_req, ap->asap_mode, + ap->enable_dyn_ack, ap->rssi); + } + + mutex_unlock(&mvm->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_tof_range_req_ext_write(struct ieee80211_vif *vif, + char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + u32 value; + int ret = 0; + char *data; + + mutex_lock(&mvm->mutex); + + data = iwl_dbgfs_is_match("tsf_timer_offset_msec=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req_ext.tsf_timer_offset_msec = + cpu_to_le16(value); + goto out; + } + + data = iwl_dbgfs_is_match("min_delta_ftm=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req_ext.min_delta_ftm = value; + goto out; + } + + data = iwl_dbgfs_is_match("ftm_format_and_bw20M=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req_ext.ftm_format_and_bw20M = + value; + goto out; + } + + data = iwl_dbgfs_is_match("ftm_format_and_bw40M=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req_ext.ftm_format_and_bw40M = + value; + goto out; + } + + data = iwl_dbgfs_is_match("ftm_format_and_bw80M=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.range_req_ext.ftm_format_and_bw80M = + value; + goto out; + } + + data = iwl_dbgfs_is_match("send_range_req_ext=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0 && value) + ret = iwl_mvm_tof_range_request_ext_cmd(mvm, vif); + goto out; + } + + ret = -EINVAL; +out: + mutex_unlock(&mvm->mutex); + return ret ?: count; +} + +static ssize_t iwl_dbgfs_tof_range_req_ext_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + struct iwl_tof_range_req_ext_cmd *cmd; + + cmd = &mvm->tof_data.range_req_ext; + + mutex_lock(&mvm->mutex); + + pos += scnprintf(buf + pos, bufsz - pos, + "tsf_timer_offset_msec = %hd\n", + cmd->tsf_timer_offset_msec); + pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %hhd\n", + cmd->min_delta_ftm); + pos += scnprintf(buf + pos, bufsz - pos, + "ftm_format_and_bw20M = %hhd\n", + cmd->ftm_format_and_bw20M); + pos += scnprintf(buf + pos, bufsz - pos, + "ftm_format_and_bw40M = %hhd\n", + cmd->ftm_format_and_bw40M); + pos += scnprintf(buf + pos, bufsz - pos, + "ftm_format_and_bw80M = %hhd\n", + cmd->ftm_format_and_bw80M); + + mutex_unlock(&mvm->mutex); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_tof_range_abort_write(struct ieee80211_vif *vif, + char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + u32 value; + int abort_id, ret = 0; + char *data; + + mutex_lock(&mvm->mutex); + + data = iwl_dbgfs_is_match("abort_id=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0) + mvm->tof_data.last_abort_id = value; + goto out; + } + + data = iwl_dbgfs_is_match("send_range_abort=", buf); + if (data) { + ret = kstrtou32(data, 10, &value); + if (ret == 0 && value) { + abort_id = mvm->tof_data.last_abort_id; + ret = iwl_mvm_tof_range_abort_cmd(mvm, abort_id); + goto out; + } + } + +out: + mutex_unlock(&mvm->mutex); + return ret ?: count; +} + +static ssize_t iwl_dbgfs_tof_range_abort_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + char buf[32]; + int pos = 0; + const size_t bufsz = sizeof(buf); + int last_abort_id; + + mutex_lock(&mvm->mutex); + last_abort_id = mvm->tof_data.last_abort_id; + mutex_unlock(&mvm->mutex); + + pos += scnprintf(buf + pos, bufsz - pos, "last_abort_id = %d\n", + last_abort_id); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_tof_range_response_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + char *buf; + int pos = 0; + const size_t bufsz = sizeof(struct iwl_tof_range_rsp_ntfy) + 256; + struct iwl_tof_range_rsp_ntfy *cmd; + int i, ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&mvm->mutex); + cmd = &mvm->tof_data.range_resp; + + pos += scnprintf(buf + pos, bufsz - pos, "request_id = %d\n", + cmd->request_id); + pos += scnprintf(buf + pos, bufsz - pos, "status = %d\n", + cmd->request_status); + pos += scnprintf(buf + pos, bufsz - pos, "last_in_batch = %d\n", + cmd->last_in_batch); + pos += scnprintf(buf + pos, bufsz - pos, "num_of_aps = %d\n", + cmd->num_of_aps); + for (i = 0; i < cmd->num_of_aps; i++) { + struct iwl_tof_range_rsp_ap_entry_ntfy *ap = &cmd->ap[i]; + + pos += scnprintf(buf + pos, bufsz - pos, + "ap %.2d: bssid=%pM status=%hhd bw=%hhd" + " rtt=%d rtt_var=%d rtt_spread=%d" + " rssi=%hhd rssi_spread=%hhd" + " range=%d range_var=%d" + " time_stamp=%d\n", + i, ap->bssid, ap->measure_status, + ap->measure_bw, + ap->rtt, ap->rtt_variance, ap->rtt_spread, + ap->rssi, ap->rssi_spread, ap->range, + ap->range_variance, ap->timestamp); + } + mutex_unlock(&mvm->mutex); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + u8 value; + int ret; + + ret = kstrtou8(buf, 0, &value); + if (ret) + return ret; + if (value > 1) + return -EINVAL; + + mutex_lock(&mvm->mutex); + iwl_mvm_update_low_latency(mvm, vif, value); + mutex_unlock(&mvm->mutex); + + return count; +} + +static ssize_t iwl_dbgfs_low_latency_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[2]; + + buf[0] = mvmvif->low_latency ? '1' : '0'; + buf[1] = '\n'; + return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); +} + +static ssize_t iwl_dbgfs_uapsd_misbehaving_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[20]; + int len; + + len = sprintf(buf, "%pM\n", mvmvif->uapsd_misbehaving_bssid); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t iwl_dbgfs_uapsd_misbehaving_write(struct ieee80211_vif *vif, + char *buf, size_t count, + loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + bool ret; + + mutex_lock(&mvm->mutex); + ret = mac_pton(buf, mvmvif->uapsd_misbehaving_bssid); + mutex_unlock(&mvm->mutex); + + return ret ? count : -EINVAL; +} + +static ssize_t iwl_dbgfs_rx_phyinfo_write(struct ieee80211_vif *vif, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + struct ieee80211_chanctx_conf *chanctx_conf; + struct iwl_mvm_phy_ctxt *phy_ctxt; + u16 value; + int ret; + + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; + + mutex_lock(&mvm->mutex); + rcu_read_lock(); + + chanctx_conf = rcu_dereference(vif->chanctx_conf); + /* make sure the channel context is assigned */ + if (!chanctx_conf) { + rcu_read_unlock(); + mutex_unlock(&mvm->mutex); + return -EINVAL; + } + + phy_ctxt = &mvm->phy_ctxts[*(u16 *)chanctx_conf->drv_priv]; + rcu_read_unlock(); + + mvm->dbgfs_rx_phyinfo = value; + + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chanctx_conf->min_def, + chanctx_conf->rx_chains_static, + chanctx_conf->rx_chains_dynamic); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_rx_phyinfo_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[8]; + + snprintf(buf, sizeof(buf), "0x%04x\n", mvmvif->mvm->dbgfs_rx_phyinfo); + + return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); +} + +#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) +#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) +#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, vif, \ + &iwl_dbgfs_##name##_ops)) \ + goto err; \ + } while (0) + +MVM_DEBUGFS_READ_FILE_OPS(mac_params); +MVM_DEBUGFS_READ_FILE_OPS(tx_pwr_lmt); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_enable, 32); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_request, 512); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_req_ext, 32); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_abort, 32); +MVM_DEBUGFS_READ_FILE_OPS(tof_range_response); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_responder_params, 32); + +void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct dentry *dbgfs_dir = vif->debugfs_dir; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[100]; + + /* + * Check if debugfs directory already exist before creating it. + * This may happen when, for example, resetting hw or suspend-resume + */ + if (!dbgfs_dir || mvmvif->dbgfs_dir) + return; + + mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir); + + if (!mvmvif->dbgfs_dir) { + IWL_ERR(mvm, "Failed to create debugfs directory under %s\n", + dbgfs_dir->d_name.name); + return; + } + + if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && + ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) || + (vif->type == NL80211_IFTYPE_STATION && vif->p2p && + mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM))) + MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR | + S_IRUSR); + + MVM_DEBUGFS_ADD_FILE_VIF(tx_pwr_lmt, mvmvif->dbgfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + + if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && + mvmvif == mvm->bf_allowed_vif) + MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT) && + !vif->p2p && (vif->type != NL80211_IFTYPE_P2P_DEVICE)) { + if (IWL_MVM_TOF_IS_RESPONDER && vif->type == NL80211_IFTYPE_AP) + MVM_DEBUGFS_ADD_FILE_VIF(tof_responder_params, + mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + + MVM_DEBUGFS_ADD_FILE_VIF(tof_range_request, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE_VIF(tof_range_req_ext, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE_VIF(tof_enable, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE_VIF(tof_range_abort, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE_VIF(tof_range_response, mvmvif->dbgfs_dir, + S_IRUSR); + } + + /* + * Create symlink for convenience pointing to interface specific + * debugfs entries for the driver. For example, under + * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/ + * find + * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/ + */ + snprintf(buf, 100, "../../../%s/%s/%s/%s", + dbgfs_dir->d_parent->d_parent->d_name.name, + dbgfs_dir->d_parent->d_name.name, + dbgfs_dir->d_name.name, + mvmvif->dbgfs_dir->d_name.name); + + mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name, + mvm->debugfs_dir, buf); + if (!mvmvif->dbgfs_slink) + IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n", + dbgfs_dir->d_name.name); + return; +err: + IWL_ERR(mvm, "Can't create debugfs entity\n"); +} + +void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + debugfs_remove(mvmvif->dbgfs_slink); + mvmvif->dbgfs_slink = NULL; + + debugfs_remove_recursive(mvmvif->dbgfs_dir); + mvmvif->dbgfs_dir = NULL; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c new file mode 100644 index 000000000000..05928fb4021d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -0,0 +1,1516 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include + +#include "mvm.h" +#include "sta.h" +#include "iwl-io.h" +#include "debugfs.h" +#include "iwl-fw-error-dump.h" + +static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + int ret; + u32 scd_q_msk; + + if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) + return -EIO; + + if (sscanf(buf, "%x", &scd_q_msk) != 1) + return -EINVAL; + + IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk); + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_flush_tx_path(mvm, scd_q_msk, 0) ? : count; + mutex_unlock(&mvm->mutex); + + return ret; +} + +static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_sta *mvmsta; + int sta_id, drain, ret; + + if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) + return -EIO; + + if (sscanf(buf, "%d %d", &sta_id, &drain) != 2) + return -EINVAL; + if (sta_id < 0 || sta_id >= IWL_MVM_STATION_COUNT) + return -EINVAL; + if (drain < 0 || drain > 1) + return -EINVAL; + + mutex_lock(&mvm->mutex); + + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); + + if (!mvmsta) + ret = -ENOENT; + else + ret = iwl_mvm_drain_sta(mvm, mvmsta, drain) ? : count; + + mutex_unlock(&mvm->mutex); + + return ret; +} + +static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + const struct fw_img *img; + unsigned int ofs, len; + size_t ret; + u8 *ptr; + + if (!mvm->ucode_loaded) + return -EINVAL; + + /* default is to dump the entire data segment */ + img = &mvm->fw->img[mvm->cur_ucode]; + ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + len = img->sec[IWL_UCODE_SECTION_DATA].len; + + if (mvm->dbgfs_sram_len) { + ofs = mvm->dbgfs_sram_offset; + len = mvm->dbgfs_sram_len; + } + + ptr = kzalloc(len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len); + + ret = simple_read_from_buffer(user_buf, count, ppos, ptr, len); + + kfree(ptr); + + return ret; +} + +static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + const struct fw_img *img; + u32 offset, len; + u32 img_offset, img_len; + + if (!mvm->ucode_loaded) + return -EINVAL; + + img = &mvm->fw->img[mvm->cur_ucode]; + img_offset = img->sec[IWL_UCODE_SECTION_DATA].offset; + img_len = img->sec[IWL_UCODE_SECTION_DATA].len; + + if (sscanf(buf, "%x,%x", &offset, &len) == 2) { + if ((offset & 0x3) || (len & 0x3)) + return -EINVAL; + + if (offset + len > img_offset + img_len) + return -EINVAL; + + mvm->dbgfs_sram_offset = offset; + mvm->dbgfs_sram_len = len; + } else { + mvm->dbgfs_sram_offset = 0; + mvm->dbgfs_sram_len = 0; + } + + return count; +} + +static ssize_t iwl_dbgfs_set_nic_temperature_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char buf[16]; + int pos; + + if (!mvm->temperature_test) + pos = scnprintf(buf , sizeof(buf), "disabled\n"); + else + pos = scnprintf(buf , sizeof(buf), "%d\n", mvm->temperature); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +/* + * Set NIC Temperature + * Cause the driver to ignore the actual NIC temperature reported by the FW + * Enable: any value between IWL_MVM_DEBUG_SET_TEMPERATURE_MIN - + * IWL_MVM_DEBUG_SET_TEMPERATURE_MAX + * Disable: IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE + */ +static ssize_t iwl_dbgfs_set_nic_temperature_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + int temperature; + + if (!mvm->ucode_loaded && !mvm->temperature_test) + return -EIO; + + if (kstrtoint(buf, 10, &temperature)) + return -EINVAL; + /* not a legal temperature */ + if ((temperature > IWL_MVM_DEBUG_SET_TEMPERATURE_MAX && + temperature != IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) || + temperature < IWL_MVM_DEBUG_SET_TEMPERATURE_MIN) + return -EINVAL; + + mutex_lock(&mvm->mutex); + if (temperature == IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) { + if (!mvm->temperature_test) + goto out; + + mvm->temperature_test = false; + /* Since we can't read the temp while awake, just set + * it to zero until we get the next RX stats from the + * firmware. + */ + mvm->temperature = 0; + } else { + mvm->temperature_test = true; + mvm->temperature = temperature; + } + IWL_DEBUG_TEMP(mvm, "%sabling debug set temperature (temp = %d)\n", + mvm->temperature_test ? "En" : "Dis" , + mvm->temperature); + /* handle the temperature change */ + iwl_mvm_tt_handler(mvm); + +out: + mutex_unlock(&mvm->mutex); + + return count; +} + +static ssize_t iwl_dbgfs_nic_temp_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char buf[16]; + int pos, temp; + + if (!mvm->ucode_loaded) + return -EIO; + + mutex_lock(&mvm->mutex); + temp = iwl_mvm_get_temp(mvm); + mutex_unlock(&mvm->mutex); + + if (temp < 0) + return temp; + + pos = scnprintf(buf , sizeof(buf), "%d\n", temp); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + struct ieee80211_sta *sta; + char buf[400]; + int i, pos = 0, bufsz = sizeof(buf); + + mutex_lock(&mvm->mutex); + + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i); + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (!sta) + pos += scnprintf(buf + pos, bufsz - pos, "N/A\n"); + else if (IS_ERR(sta)) + pos += scnprintf(buf + pos, bufsz - pos, "%ld\n", + PTR_ERR(sta)); + else + pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", + sta->addr); + } + + mutex_unlock(&mvm->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char buf[64]; + int bufsz = sizeof(buf); + int pos = 0; + + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d0=%d\n", + mvm->disable_power_off); + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d3=%d\n", + mvm->disable_power_off_d3); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + int ret, val; + + if (!mvm->ucode_loaded) + return -EIO; + + if (!strncmp("disable_power_off_d0=", buf, 21)) { + if (sscanf(buf + 21, "%d", &val) != 1) + return -EINVAL; + mvm->disable_power_off = val; + } else if (!strncmp("disable_power_off_d3=", buf, 21)) { + if (sscanf(buf + 21, "%d", &val) != 1) + return -EINVAL; + mvm->disable_power_off_d3 = val; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_power_update_device(mvm); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +#define BT_MBOX_MSG(_notif, _num, _field) \ + ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ + >> BT_MBOX##_num##_##_field##_POS) + + +#define BT_MBOX_PRINT(_num, _field, _end) \ + pos += scnprintf(buf + pos, bufsz - pos, \ + "\t%s: %d%s", \ + #_field, \ + BT_MBOX_MSG(notif, _num, _field), \ + true ? "\n" : ", "); + +static +int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf, + int pos, int bufsz) +{ + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n"); + + BT_MBOX_PRINT(0, LE_SLAVE_LAT, false); + BT_MBOX_PRINT(0, LE_PROF1, false); + BT_MBOX_PRINT(0, LE_PROF2, false); + BT_MBOX_PRINT(0, LE_PROF_OTHER, false); + BT_MBOX_PRINT(0, CHL_SEQ_N, false); + BT_MBOX_PRINT(0, INBAND_S, false); + BT_MBOX_PRINT(0, LE_MIN_RSSI, false); + BT_MBOX_PRINT(0, LE_SCAN, false); + BT_MBOX_PRINT(0, LE_ADV, false); + BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false); + BT_MBOX_PRINT(0, OPEN_CON_1, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n"); + + BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false); + BT_MBOX_PRINT(1, IP_SR, false); + BT_MBOX_PRINT(1, LE_MSTR, false); + BT_MBOX_PRINT(1, AGGR_TRFC_LD, false); + BT_MBOX_PRINT(1, MSG_TYPE, false); + BT_MBOX_PRINT(1, SSN, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n"); + + BT_MBOX_PRINT(2, SNIFF_ACT, false); + BT_MBOX_PRINT(2, PAG, false); + BT_MBOX_PRINT(2, INQUIRY, false); + BT_MBOX_PRINT(2, CONN, false); + BT_MBOX_PRINT(2, SNIFF_INTERVAL, false); + BT_MBOX_PRINT(2, DISC, false); + BT_MBOX_PRINT(2, SCO_TX_ACT, false); + BT_MBOX_PRINT(2, SCO_RX_ACT, false); + BT_MBOX_PRINT(2, ESCO_RE_TX, false); + BT_MBOX_PRINT(2, SCO_DURATION, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n"); + + BT_MBOX_PRINT(3, SCO_STATE, false); + BT_MBOX_PRINT(3, SNIFF_STATE, false); + BT_MBOX_PRINT(3, A2DP_STATE, false); + BT_MBOX_PRINT(3, ACL_STATE, false); + BT_MBOX_PRINT(3, MSTR_STATE, false); + BT_MBOX_PRINT(3, OBX_STATE, false); + BT_MBOX_PRINT(3, OPEN_CON_2, false); + BT_MBOX_PRINT(3, TRAFFIC_LOAD, false); + BT_MBOX_PRINT(3, CHL_SEQN_LSB, false); + BT_MBOX_PRINT(3, INBAND_P, false); + BT_MBOX_PRINT(3, MSG_TYPE_2, false); + BT_MBOX_PRINT(3, SSN_2, false); + BT_MBOX_PRINT(3, UPDATE_REQUEST, true); + + return pos; +} + +static +int iwl_mvm_coex_dump_mbox_old(struct iwl_bt_coex_profile_notif_old *notif, + char *buf, int pos, int bufsz) +{ + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n"); + + BT_MBOX_PRINT(0, LE_SLAVE_LAT, false); + BT_MBOX_PRINT(0, LE_PROF1, false); + BT_MBOX_PRINT(0, LE_PROF2, false); + BT_MBOX_PRINT(0, LE_PROF_OTHER, false); + BT_MBOX_PRINT(0, CHL_SEQ_N, false); + BT_MBOX_PRINT(0, INBAND_S, false); + BT_MBOX_PRINT(0, LE_MIN_RSSI, false); + BT_MBOX_PRINT(0, LE_SCAN, false); + BT_MBOX_PRINT(0, LE_ADV, false); + BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false); + BT_MBOX_PRINT(0, OPEN_CON_1, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n"); + + BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false); + BT_MBOX_PRINT(1, IP_SR, false); + BT_MBOX_PRINT(1, LE_MSTR, false); + BT_MBOX_PRINT(1, AGGR_TRFC_LD, false); + BT_MBOX_PRINT(1, MSG_TYPE, false); + BT_MBOX_PRINT(1, SSN, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n"); + + BT_MBOX_PRINT(2, SNIFF_ACT, false); + BT_MBOX_PRINT(2, PAG, false); + BT_MBOX_PRINT(2, INQUIRY, false); + BT_MBOX_PRINT(2, CONN, false); + BT_MBOX_PRINT(2, SNIFF_INTERVAL, false); + BT_MBOX_PRINT(2, DISC, false); + BT_MBOX_PRINT(2, SCO_TX_ACT, false); + BT_MBOX_PRINT(2, SCO_RX_ACT, false); + BT_MBOX_PRINT(2, ESCO_RE_TX, false); + BT_MBOX_PRINT(2, SCO_DURATION, true); + + pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n"); + + BT_MBOX_PRINT(3, SCO_STATE, false); + BT_MBOX_PRINT(3, SNIFF_STATE, false); + BT_MBOX_PRINT(3, A2DP_STATE, false); + BT_MBOX_PRINT(3, ACL_STATE, false); + BT_MBOX_PRINT(3, MSTR_STATE, false); + BT_MBOX_PRINT(3, OBX_STATE, false); + BT_MBOX_PRINT(3, OPEN_CON_2, false); + BT_MBOX_PRINT(3, TRAFFIC_LOAD, false); + BT_MBOX_PRINT(3, CHL_SEQN_LSB, false); + BT_MBOX_PRINT(3, INBAND_P, false); + BT_MBOX_PRINT(3, MSG_TYPE_2, false); + BT_MBOX_PRINT(3, SSN_2, false); + BT_MBOX_PRINT(3, UPDATE_REQUEST, true); + + return pos; +} + +static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char *buf; + int ret, pos = 0, bufsz = sizeof(char) * 1024; + + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&mvm->mutex); + + if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { + struct iwl_bt_coex_profile_notif_old *notif = + &mvm->last_bt_notif_old; + + pos += iwl_mvm_coex_dump_mbox_old(notif, buf, pos, bufsz); + + pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", + notif->bt_ci_compliance); + pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", + le32_to_cpu(notif->primary_ch_lut)); + pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n", + le32_to_cpu(notif->secondary_ch_lut)); + pos += scnprintf(buf+pos, + bufsz-pos, "bt_activity_grading = %d\n", + le32_to_cpu(notif->bt_activity_grading)); + pos += scnprintf(buf+pos, bufsz-pos, + "antenna isolation = %d CORUN LUT index = %d\n", + mvm->last_ant_isol, mvm->last_corun_lut); + } else { + struct iwl_bt_coex_profile_notif *notif = + &mvm->last_bt_notif; + + pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz); + + pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", + notif->bt_ci_compliance); + pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", + le32_to_cpu(notif->primary_ch_lut)); + pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n", + le32_to_cpu(notif->secondary_ch_lut)); + pos += scnprintf(buf+pos, + bufsz-pos, "bt_activity_grading = %d\n", + le32_to_cpu(notif->bt_activity_grading)); + pos += scnprintf(buf+pos, bufsz-pos, + "antenna isolation = %d CORUN LUT index = %d\n", + mvm->last_ant_isol, mvm->last_corun_lut); + } + + mutex_unlock(&mvm->mutex); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + + return ret; +} +#undef BT_MBOX_PRINT + +static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char buf[256]; + int bufsz = sizeof(buf); + int pos = 0; + + mutex_lock(&mvm->mutex); + + if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { + struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd_old; + + pos += scnprintf(buf+pos, bufsz-pos, + "Channel inhibition CMD\n"); + pos += scnprintf(buf+pos, bufsz-pos, + "\tPrimary Channel Bitmap 0x%016llx\n", + le64_to_cpu(cmd->bt_primary_ci)); + pos += scnprintf(buf+pos, bufsz-pos, + "\tSecondary Channel Bitmap 0x%016llx\n", + le64_to_cpu(cmd->bt_secondary_ci)); + + pos += scnprintf(buf+pos, bufsz-pos, + "BT Configuration CMD - 0=default, 1=never, 2=always\n"); + pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill msk idx %d\n", + mvm->bt_ack_kill_msk[0]); + pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill msk idx %d\n", + mvm->bt_cts_kill_msk[0]); + + } else { + struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd; + + pos += scnprintf(buf+pos, bufsz-pos, + "Channel inhibition CMD\n"); + pos += scnprintf(buf+pos, bufsz-pos, + "\tPrimary Channel Bitmap 0x%016llx\n", + le64_to_cpu(cmd->bt_primary_ci)); + pos += scnprintf(buf+pos, bufsz-pos, + "\tSecondary Channel Bitmap 0x%016llx\n", + le64_to_cpu(cmd->bt_secondary_ci)); + } + + mutex_unlock(&mvm->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + u32 bt_tx_prio; + + if (sscanf(buf, "%u", &bt_tx_prio) != 1) + return -EINVAL; + if (bt_tx_prio > 4) + return -EINVAL; + + mvm->bt_tx_prio = bt_tx_prio; + + return count; +} + +static ssize_t +iwl_dbgfs_bt_force_ant_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + static const char * const modes_str[BT_FORCE_ANT_MAX] = { + [BT_FORCE_ANT_DIS] = "dis", + [BT_FORCE_ANT_AUTO] = "auto", + [BT_FORCE_ANT_BT] = "bt", + [BT_FORCE_ANT_WIFI] = "wifi", + }; + int ret, bt_force_ant_mode; + + for (bt_force_ant_mode = 0; + bt_force_ant_mode < ARRAY_SIZE(modes_str); + bt_force_ant_mode++) { + if (!strcmp(buf, modes_str[bt_force_ant_mode])) + break; + } + + if (bt_force_ant_mode >= ARRAY_SIZE(modes_str)) + return -EINVAL; + + ret = 0; + mutex_lock(&mvm->mutex); + if (mvm->bt_force_ant_mode == bt_force_ant_mode) + goto out; + + mvm->bt_force_ant_mode = bt_force_ant_mode; + IWL_DEBUG_COEX(mvm, "Force mode: %s\n", + modes_str[mvm->bt_force_ant_mode]); + ret = iwl_send_bt_init_conf(mvm); + +out: + mutex_unlock(&mvm->mutex); + return ret ?: count; +} + +#define PRINT_STATS_LE32(_struct, _memb) \ + pos += scnprintf(buf + pos, bufsz - pos, \ + fmt_table, #_memb, \ + le32_to_cpu(_struct->_memb)) + +static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + static const char *fmt_table = "\t%-30s %10u\n"; + static const char *fmt_header = "%-32s\n"; + int pos = 0; + char *buf; + int ret; + /* 43 is the size of each data line, 33 is the size of each header */ + size_t bufsz = + ((sizeof(struct mvm_statistics_rx) / sizeof(__le32)) * 43) + + (4 * 33) + 1; + + struct mvm_statistics_rx_phy *ofdm; + struct mvm_statistics_rx_phy *cck; + struct mvm_statistics_rx_non_phy *general; + struct mvm_statistics_rx_ht_phy *ht; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&mvm->mutex); + + ofdm = &mvm->rx_stats.ofdm; + cck = &mvm->rx_stats.cck; + general = &mvm->rx_stats.general; + ht = &mvm->rx_stats.ofdm_ht; + + pos += scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - OFDM"); + PRINT_STATS_LE32(ofdm, ina_cnt); + PRINT_STATS_LE32(ofdm, fina_cnt); + PRINT_STATS_LE32(ofdm, plcp_err); + PRINT_STATS_LE32(ofdm, crc32_err); + PRINT_STATS_LE32(ofdm, overrun_err); + PRINT_STATS_LE32(ofdm, early_overrun_err); + PRINT_STATS_LE32(ofdm, crc32_good); + PRINT_STATS_LE32(ofdm, false_alarm_cnt); + PRINT_STATS_LE32(ofdm, fina_sync_err_cnt); + PRINT_STATS_LE32(ofdm, sfd_timeout); + PRINT_STATS_LE32(ofdm, fina_timeout); + PRINT_STATS_LE32(ofdm, unresponded_rts); + PRINT_STATS_LE32(ofdm, rxe_frame_lmt_overrun); + PRINT_STATS_LE32(ofdm, sent_ack_cnt); + PRINT_STATS_LE32(ofdm, sent_cts_cnt); + PRINT_STATS_LE32(ofdm, sent_ba_rsp_cnt); + PRINT_STATS_LE32(ofdm, dsp_self_kill); + PRINT_STATS_LE32(ofdm, mh_format_err); + PRINT_STATS_LE32(ofdm, re_acq_main_rssi_sum); + PRINT_STATS_LE32(ofdm, reserved); + + pos += scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - CCK"); + PRINT_STATS_LE32(cck, ina_cnt); + PRINT_STATS_LE32(cck, fina_cnt); + PRINT_STATS_LE32(cck, plcp_err); + PRINT_STATS_LE32(cck, crc32_err); + PRINT_STATS_LE32(cck, overrun_err); + PRINT_STATS_LE32(cck, early_overrun_err); + PRINT_STATS_LE32(cck, crc32_good); + PRINT_STATS_LE32(cck, false_alarm_cnt); + PRINT_STATS_LE32(cck, fina_sync_err_cnt); + PRINT_STATS_LE32(cck, sfd_timeout); + PRINT_STATS_LE32(cck, fina_timeout); + PRINT_STATS_LE32(cck, unresponded_rts); + PRINT_STATS_LE32(cck, rxe_frame_lmt_overrun); + PRINT_STATS_LE32(cck, sent_ack_cnt); + PRINT_STATS_LE32(cck, sent_cts_cnt); + PRINT_STATS_LE32(cck, sent_ba_rsp_cnt); + PRINT_STATS_LE32(cck, dsp_self_kill); + PRINT_STATS_LE32(cck, mh_format_err); + PRINT_STATS_LE32(cck, re_acq_main_rssi_sum); + PRINT_STATS_LE32(cck, reserved); + + pos += scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - GENERAL"); + PRINT_STATS_LE32(general, bogus_cts); + PRINT_STATS_LE32(general, bogus_ack); + PRINT_STATS_LE32(general, non_bssid_frames); + PRINT_STATS_LE32(general, filtered_frames); + PRINT_STATS_LE32(general, non_channel_beacons); + PRINT_STATS_LE32(general, channel_beacons); + PRINT_STATS_LE32(general, num_missed_bcon); + PRINT_STATS_LE32(general, adc_rx_saturation_time); + PRINT_STATS_LE32(general, ina_detection_search_time); + PRINT_STATS_LE32(general, beacon_silence_rssi_a); + PRINT_STATS_LE32(general, beacon_silence_rssi_b); + PRINT_STATS_LE32(general, beacon_silence_rssi_c); + PRINT_STATS_LE32(general, interference_data_flag); + PRINT_STATS_LE32(general, channel_load); + PRINT_STATS_LE32(general, dsp_false_alarms); + PRINT_STATS_LE32(general, beacon_rssi_a); + PRINT_STATS_LE32(general, beacon_rssi_b); + PRINT_STATS_LE32(general, beacon_rssi_c); + PRINT_STATS_LE32(general, beacon_energy_a); + PRINT_STATS_LE32(general, beacon_energy_b); + PRINT_STATS_LE32(general, beacon_energy_c); + PRINT_STATS_LE32(general, num_bt_kills); + PRINT_STATS_LE32(general, mac_id); + PRINT_STATS_LE32(general, directed_data_mpdu); + + pos += scnprintf(buf + pos, bufsz - pos, fmt_header, + "Statistics_Rx - HT"); + PRINT_STATS_LE32(ht, plcp_err); + PRINT_STATS_LE32(ht, overrun_err); + PRINT_STATS_LE32(ht, early_overrun_err); + PRINT_STATS_LE32(ht, crc32_good); + PRINT_STATS_LE32(ht, crc32_err); + PRINT_STATS_LE32(ht, mh_format_err); + PRINT_STATS_LE32(ht, agg_crc32_good); + PRINT_STATS_LE32(ht, agg_mpdu_cnt); + PRINT_STATS_LE32(ht, agg_cnt); + PRINT_STATS_LE32(ht, unsupport_mcs); + + mutex_unlock(&mvm->mutex); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + + return ret; +} +#undef PRINT_STAT_LE32 + +static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm, + char __user *user_buf, size_t count, + loff_t *ppos, + struct iwl_mvm_frame_stats *stats) +{ + char *buff, *pos, *endpos; + int idx, i; + int ret; + static const size_t bufsz = 1024; + + buff = kmalloc(bufsz, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + spin_lock_bh(&mvm->drv_stats_lock); + + pos = buff; + endpos = pos + bufsz; + + pos += scnprintf(pos, endpos - pos, + "Legacy/HT/VHT\t:\t%d/%d/%d\n", + stats->legacy_frames, + stats->ht_frames, + stats->vht_frames); + pos += scnprintf(pos, endpos - pos, "20/40/80\t:\t%d/%d/%d\n", + stats->bw_20_frames, + stats->bw_40_frames, + stats->bw_80_frames); + pos += scnprintf(pos, endpos - pos, "NGI/SGI\t\t:\t%d/%d\n", + stats->ngi_frames, + stats->sgi_frames); + pos += scnprintf(pos, endpos - pos, "SISO/MIMO2\t:\t%d/%d\n", + stats->siso_frames, + stats->mimo2_frames); + pos += scnprintf(pos, endpos - pos, "FAIL/SCSS\t:\t%d/%d\n", + stats->fail_frames, + stats->success_frames); + pos += scnprintf(pos, endpos - pos, "MPDUs agg\t:\t%d\n", + stats->agg_frames); + pos += scnprintf(pos, endpos - pos, "A-MPDUs\t\t:\t%d\n", + stats->ampdu_count); + pos += scnprintf(pos, endpos - pos, "Avg MPDUs/A-MPDU:\t%d\n", + stats->ampdu_count > 0 ? + (stats->agg_frames / stats->ampdu_count) : 0); + + pos += scnprintf(pos, endpos - pos, "Last Rates\n"); + + idx = stats->last_frame_idx - 1; + for (i = 0; i < ARRAY_SIZE(stats->last_rates); i++) { + idx = (idx + 1) % ARRAY_SIZE(stats->last_rates); + if (stats->last_rates[idx] == 0) + continue; + pos += scnprintf(pos, endpos - pos, "Rate[%d]: ", + (int)(ARRAY_SIZE(stats->last_rates) - i)); + pos += rs_pretty_print_rate(pos, stats->last_rates[idx]); + } + spin_unlock_bh(&mvm->drv_stats_lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); + kfree(buff); + + return ret; +} + +static ssize_t iwl_dbgfs_drv_rx_stats_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + + return iwl_dbgfs_frame_stats_read(mvm, user_buf, count, ppos, + &mvm->drv_rx_stats); +} + +static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + int ret; + + mutex_lock(&mvm->mutex); + + /* allow one more restart that we're provoking here */ + if (mvm->restart_fw >= 0) + mvm->restart_fw++; + + /* take the return value to make compiler happy - it will fail anyway */ + ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, 0, 0, NULL); + + mutex_unlock(&mvm->mutex); + + return count; +} + +static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_NMI); + if (ret) + return ret; + + iwl_force_nmi(mvm->trans); + + iwl_mvm_unref(mvm, IWL_MVM_REF_NMI); + + return count; +} + +static ssize_t +iwl_dbgfs_scan_ant_rxchain_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + int pos = 0; + char buf[32]; + const size_t bufsz = sizeof(buf); + + /* print which antennas were set for the scan command by the user */ + pos += scnprintf(buf + pos, bufsz - pos, "Antennas for scan: "); + if (mvm->scan_rx_ant & ANT_A) + pos += scnprintf(buf + pos, bufsz - pos, "A"); + if (mvm->scan_rx_ant & ANT_B) + pos += scnprintf(buf + pos, bufsz - pos, "B"); + if (mvm->scan_rx_ant & ANT_C) + pos += scnprintf(buf + pos, bufsz - pos, "C"); + pos += scnprintf(buf + pos, bufsz - pos, " (%hhx)\n", mvm->scan_rx_ant); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + u8 scan_rx_ant; + + if (sscanf(buf, "%hhx", &scan_rx_ant) != 1) + return -EINVAL; + if (scan_rx_ant > ANT_ABC) + return -EINVAL; + if (scan_rx_ant & ~(iwl_mvm_get_valid_rx_ant(mvm))) + return -EINVAL; + + if (mvm->scan_rx_ant != scan_rx_ant) { + mvm->scan_rx_ant = scan_rx_ant; + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_UMAC_SCAN)) + iwl_mvm_config_scan(mvm); + } + + return count; +} + +static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + int conf; + char buf[8]; + const size_t bufsz = sizeof(buf); + int pos = 0; + + mutex_lock(&mvm->mutex); + conf = mvm->fw_dbg_conf; + mutex_unlock(&mvm->mutex); + + pos += scnprintf(buf + pos, bufsz - pos, "%d\n", conf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + unsigned int conf_id; + int ret; + + ret = kstrtouint(buf, 0, &conf_id); + if (ret) + return ret; + + if (WARN_ON(conf_id >= FW_DBG_CONF_MAX)) + return -EINVAL; + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_start_fw_dbg_conf(mvm, conf_id); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE); + + if (ret) + return ret; + + iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, NULL, 0, NULL); + + iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE); + + return count; +} + +#define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__) +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING +static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + struct iwl_bcast_filter_cmd cmd; + const struct iwl_fw_bcast_filter *filter; + char *buf; + int bufsz = 1024; + int i, j, pos = 0; + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&mvm->mutex); + if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) { + ADD_TEXT("None\n"); + mutex_unlock(&mvm->mutex); + goto out; + } + mutex_unlock(&mvm->mutex); + + for (i = 0; cmd.filters[i].attrs[0].mask; i++) { + filter = &cmd.filters[i]; + + ADD_TEXT("Filter [%d]:\n", i); + ADD_TEXT("\tDiscard=%d\n", filter->discard); + ADD_TEXT("\tFrame Type: %s\n", + filter->frame_type ? "IPv4" : "Generic"); + + for (j = 0; j < ARRAY_SIZE(filter->attrs); j++) { + const struct iwl_fw_bcast_filter_attr *attr; + + attr = &filter->attrs[j]; + if (!attr->mask) + break; + + ADD_TEXT("\tAttr [%d]: offset=%d (from %s), mask=0x%x, value=0x%x reserved=0x%x\n", + j, attr->offset, + attr->offset_type ? "IP End" : + "Payload Start", + be32_to_cpu(attr->mask), + be32_to_cpu(attr->val), + le16_to_cpu(attr->reserved1)); + } + } +out: + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + int pos, next_pos; + struct iwl_fw_bcast_filter filter = {}; + struct iwl_bcast_filter_cmd cmd; + u32 filter_id, attr_id, mask, value; + int err = 0; + + if (sscanf(buf, "%d %hhi %hhi %n", &filter_id, &filter.discard, + &filter.frame_type, &pos) != 3) + return -EINVAL; + + if (filter_id >= ARRAY_SIZE(mvm->dbgfs_bcast_filtering.cmd.filters) || + filter.frame_type > BCAST_FILTER_FRAME_TYPE_IPV4) + return -EINVAL; + + for (attr_id = 0; attr_id < ARRAY_SIZE(filter.attrs); + attr_id++) { + struct iwl_fw_bcast_filter_attr *attr = + &filter.attrs[attr_id]; + + if (pos >= count) + break; + + if (sscanf(&buf[pos], "%hhi %hhi %i %i %n", + &attr->offset, &attr->offset_type, + &mask, &value, &next_pos) != 4) + return -EINVAL; + + attr->mask = cpu_to_be32(mask); + attr->val = cpu_to_be32(value); + if (mask) + filter.num_attrs++; + + pos += next_pos; + } + + mutex_lock(&mvm->mutex); + memcpy(&mvm->dbgfs_bcast_filtering.cmd.filters[filter_id], + &filter, sizeof(filter)); + + /* send updated bcast filtering configuration */ + if (mvm->dbgfs_bcast_filtering.override && + iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) + err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, + sizeof(cmd), &cmd); + mutex_unlock(&mvm->mutex); + + return err ?: count; +} + +static ssize_t iwl_dbgfs_bcast_filters_macs_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + struct iwl_bcast_filter_cmd cmd; + char *buf; + int bufsz = 1024; + int i, pos = 0; + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&mvm->mutex); + if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) { + ADD_TEXT("None\n"); + mutex_unlock(&mvm->mutex); + goto out; + } + mutex_unlock(&mvm->mutex); + + for (i = 0; i < ARRAY_SIZE(cmd.macs); i++) { + const struct iwl_fw_bcast_mac *mac = &cmd.macs[i]; + + ADD_TEXT("Mac [%d]: discard=%d attached_filters=0x%x\n", + i, mac->default_discard, mac->attached_filters); + } +out: + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + struct iwl_bcast_filter_cmd cmd; + struct iwl_fw_bcast_mac mac = {}; + u32 mac_id, attached_filters; + int err = 0; + + if (!mvm->bcast_filters) + return -ENOENT; + + if (sscanf(buf, "%d %hhi %i", &mac_id, &mac.default_discard, + &attached_filters) != 3) + return -EINVAL; + + if (mac_id >= ARRAY_SIZE(cmd.macs) || + mac.default_discard > 1 || + attached_filters >= BIT(ARRAY_SIZE(cmd.filters))) + return -EINVAL; + + mac.attached_filters = cpu_to_le16(attached_filters); + + mutex_lock(&mvm->mutex); + memcpy(&mvm->dbgfs_bcast_filtering.cmd.macs[mac_id], + &mac, sizeof(mac)); + + /* send updated bcast filtering configuration */ + if (mvm->dbgfs_bcast_filtering.override && + iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) + err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, + sizeof(cmd), &cmd); + mutex_unlock(&mvm->mutex); + + return err ?: count; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static ssize_t iwl_dbgfs_d3_sram_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + int store; + + if (sscanf(buf, "%d", &store) != 1) + return -EINVAL; + + mvm->store_d3_resume_sram = store; + + return count; +} + +static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + const struct fw_img *img; + int ofs, len, pos = 0; + size_t bufsz, ret; + char *buf; + u8 *ptr = mvm->d3_resume_sram; + + img = &mvm->fw->img[IWL_UCODE_WOWLAN]; + len = img->sec[IWL_UCODE_SECTION_DATA].len; + + bufsz = len * 4 + 256; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf, bufsz, "D3 SRAM capture: %sabled\n", + mvm->store_d3_resume_sram ? "en" : "dis"); + + if (ptr) { + for (ofs = 0; ofs < len; ofs += 16) { + pos += scnprintf(buf + pos, bufsz - pos, + "0x%.4x %16ph\n", ofs, ptr + ofs); + } + } else { + pos += scnprintf(buf + pos, bufsz - pos, + "(no data captured)\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + + kfree(buf); + + return ret; +} +#endif + +#define PRINT_MVM_REF(ref) do { \ + if (mvm->refs[ref]) \ + pos += scnprintf(buf + pos, bufsz - pos, \ + "\t(0x%lx): %d %s\n", \ + BIT(ref), mvm->refs[ref], #ref); \ +} while (0) + +static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + int i, pos = 0; + char buf[256]; + const size_t bufsz = sizeof(buf); + u32 refs = 0; + + for (i = 0; i < IWL_MVM_REF_COUNT; i++) + if (mvm->refs[i]) + refs |= BIT(i); + + pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%x\n", + refs); + + PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN); + PRINT_MVM_REF(IWL_MVM_REF_SCAN); + PRINT_MVM_REF(IWL_MVM_REF_ROC); + PRINT_MVM_REF(IWL_MVM_REF_ROC_AUX); + PRINT_MVM_REF(IWL_MVM_REF_P2P_CLIENT); + PRINT_MVM_REF(IWL_MVM_REF_AP_IBSS); + PRINT_MVM_REF(IWL_MVM_REF_USER); + PRINT_MVM_REF(IWL_MVM_REF_TX); + PRINT_MVM_REF(IWL_MVM_REF_TX_AGG); + PRINT_MVM_REF(IWL_MVM_REF_ADD_IF); + PRINT_MVM_REF(IWL_MVM_REF_START_AP); + PRINT_MVM_REF(IWL_MVM_REF_BSS_CHANGED); + PRINT_MVM_REF(IWL_MVM_REF_PREPARE_TX); + PRINT_MVM_REF(IWL_MVM_REF_PROTECT_TDLS); + PRINT_MVM_REF(IWL_MVM_REF_CHECK_CTKILL); + PRINT_MVM_REF(IWL_MVM_REF_PRPH_READ); + PRINT_MVM_REF(IWL_MVM_REF_PRPH_WRITE); + PRINT_MVM_REF(IWL_MVM_REF_NMI); + PRINT_MVM_REF(IWL_MVM_REF_TM_CMD); + PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK); + PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA); + PRINT_MVM_REF(IWL_MVM_REF_FW_DBG_COLLECT); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + unsigned long value; + int ret; + bool taken; + + ret = kstrtoul(buf, 10, &value); + if (ret < 0) + return ret; + + mutex_lock(&mvm->mutex); + + taken = mvm->refs[IWL_MVM_REF_USER]; + if (value == 1 && !taken) + iwl_mvm_ref(mvm, IWL_MVM_REF_USER); + else if (value == 0 && taken) + iwl_mvm_unref(mvm, IWL_MVM_REF_USER); + else + ret = -EINVAL; + + mutex_unlock(&mvm->mutex); + + if (ret < 0) + return ret; + return count; +} + +#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm) +#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm) +#define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \ + if (!debugfs_create_file(alias, mode, parent, mvm, \ + &iwl_dbgfs_##name##_ops)) \ + goto err; \ + } while (0) +#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \ + MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) + +static ssize_t +iwl_dbgfs_prph_reg_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + int pos = 0; + char buf[32]; + const size_t bufsz = sizeof(buf); + int ret; + + if (!mvm->dbgfs_prph_reg_addr) + return -EINVAL; + + ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_READ); + if (ret) + return ret; + + pos += scnprintf(buf + pos, bufsz - pos, "Reg 0x%x: (0x%x)\n", + mvm->dbgfs_prph_reg_addr, + iwl_read_prph(mvm->trans, mvm->dbgfs_prph_reg_addr)); + + iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_READ); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + u8 args; + u32 value; + int ret; + + args = sscanf(buf, "%i %i", &mvm->dbgfs_prph_reg_addr, &value); + /* if we only want to set the reg address - nothing more to do */ + if (args == 1) + goto out; + + /* otherwise, make sure we have both address and value */ + if (args != 2) + return -EINVAL; + + ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE); + if (ret) + return ret; + + iwl_write_prph(mvm->trans, mvm->dbgfs_prph_reg_addr, value); + + iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE); +out: + return count; +} + +static ssize_t +iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + int ret; + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_send_cmd_pdu(mvm, ECHO_CMD, 0, 0, NULL); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); + +/* Device wide debugfs entries */ +MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16); +MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(set_nic_temperature, 64); +MVM_DEBUGFS_READ_FILE_OPS(nic_temp); +MVM_DEBUGFS_READ_FILE_OPS(stations); +MVM_DEBUGFS_READ_FILE_OPS(bt_notif); +MVM_DEBUGFS_READ_FILE_OPS(bt_cmd); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64); +MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats); +MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats); +MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10); +MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10); +MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10); +MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8); + +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING +MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256); +#endif + +#ifdef CONFIG_PM_SLEEP +MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8); +#endif + +int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) +{ + struct dentry *bcast_dir __maybe_unused; + char buf[100]; + + spin_lock_init(&mvm->drv_stats_lock); + + mvm->debugfs_dir = dbgfs_dir; + + MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR); + MVM_DEBUGFS_ADD_FILE(set_nic_temperature, mvm->debugfs_dir, + S_IWUSR | S_IRUSR); + MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir, + S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, + S_IWUSR | S_IRUSR); + MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR); + MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR); + if (!debugfs_create_bool("enable_scan_iteration_notif", + S_IRUSR | S_IWUSR, + mvm->debugfs_dir, + &mvm->scan_iter_notif_enabled)) + goto err; + +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) { + bcast_dir = debugfs_create_dir("bcast_filtering", + mvm->debugfs_dir); + if (!bcast_dir) + goto err; + + if (!debugfs_create_bool("override", S_IRUSR | S_IWUSR, + bcast_dir, + &mvm->dbgfs_bcast_filtering.override)) + goto err; + + MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters, + bcast_dir, S_IWUSR | S_IRUSR); + MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs, + bcast_dir, S_IWUSR | S_IRUSR); + } +#endif + +#ifdef CONFIG_PM_SLEEP + MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR); + if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR, + mvm->debugfs_dir, &mvm->d3_wake_sysassert)) + goto err; + if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR, + mvm->debugfs_dir, &mvm->last_netdetect_scans)) + goto err; +#endif + + if (!debugfs_create_u8("low_latency_agg_frame_limit", S_IRUSR | S_IWUSR, + mvm->debugfs_dir, + &mvm->low_latency_agg_frame_limit)) + goto err; + if (!debugfs_create_u8("ps_disabled", S_IRUSR, + mvm->debugfs_dir, &mvm->ps_disabled)) + goto err; + if (!debugfs_create_blob("nvm_hw", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_hw_blob)) + goto err; + if (!debugfs_create_blob("nvm_sw", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_sw_blob)) + goto err; + if (!debugfs_create_blob("nvm_calib", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_calib_blob)) + goto err; + if (!debugfs_create_blob("nvm_prod", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_prod_blob)) + goto err; + if (!debugfs_create_blob("nvm_phy_sku", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_phy_sku_blob)) + goto err; + + /* + * Create a symlink with mac80211. It will be removed when mac80211 + * exists (before the opmode exists which removes the target.) + */ + snprintf(buf, 100, "../../%s/%s", + dbgfs_dir->d_parent->d_parent->d_name.name, + dbgfs_dir->d_parent->d_name.name); + if (!debugfs_create_symlink("iwlwifi", mvm->hw->wiphy->debugfsdir, buf)) + goto err; + + return 0; +err: + IWL_ERR(mvm, "Can't create the mvm debugfs directory\n"); + return -ENOMEM; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h new file mode 100644 index 000000000000..8c4190e7e027 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h @@ -0,0 +1,103 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#define MVM_DEBUGFS_READ_FILE_OPS(name) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +} + +#define MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ +static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + argtype *arg = file->private_data; \ + char buf[buflen] = {}; \ + size_t buf_size = min(count, sizeof(buf) - 1); \ + \ + if (copy_from_user(buf, user_buf, buf_size)) \ + return -EFAULT; \ + \ + return iwl_dbgfs_##name##_write(arg, buf, buf_size, ppos); \ +} \ + +#define _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \ +MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = _iwl_dbgfs_##name##_write, \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define _MVM_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \ +MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = _iwl_dbgfs_##name##_write, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h new file mode 100644 index 000000000000..d398a6102805 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h @@ -0,0 +1,476 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __fw_api_bt_coex_h__ +#define __fw_api_bt_coex_h__ + +#include +#include + +#define BITS(nb) (BIT(nb) - 1) + +/** + * enum iwl_bt_coex_flags - flags for BT_COEX command + * @BT_COEX_MODE_POS: + * @BT_COEX_MODE_MSK: + * @BT_COEX_DISABLE_OLD: + * @BT_COEX_2W_OLD: + * @BT_COEX_3W_OLD: + * @BT_COEX_NW_OLD: + * @BT_COEX_AUTO_OLD: + * @BT_COEX_BT_OLD: Antenna is for BT (manufacuring tests) + * @BT_COEX_WIFI_OLD: Antenna is for BT (manufacuring tests) + * @BT_COEX_SYNC2SCO: + * @BT_COEX_CORUNNING: + * @BT_COEX_MPLUT: + * @BT_COEX_TTC: + * @BT_COEX_RRC: + * + * The COEX_MODE must be set for each command. Even if it is not changed. + */ +enum iwl_bt_coex_flags { + BT_COEX_MODE_POS = 3, + BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS, + BT_COEX_DISABLE_OLD = 0x0 << BT_COEX_MODE_POS, + BT_COEX_2W_OLD = 0x1 << BT_COEX_MODE_POS, + BT_COEX_3W_OLD = 0x2 << BT_COEX_MODE_POS, + BT_COEX_NW_OLD = 0x3 << BT_COEX_MODE_POS, + BT_COEX_AUTO_OLD = 0x5 << BT_COEX_MODE_POS, + BT_COEX_BT_OLD = 0x6 << BT_COEX_MODE_POS, + BT_COEX_WIFI_OLD = 0x7 << BT_COEX_MODE_POS, + BT_COEX_SYNC2SCO = BIT(7), + BT_COEX_CORUNNING = BIT(8), + BT_COEX_MPLUT = BIT(9), + BT_COEX_TTC = BIT(20), + BT_COEX_RRC = BIT(21), +}; + +/* + * indicates what has changed in the BT_COEX command. + * BT_VALID_ENABLE must be set for each command. Commands without this bit will + * discarded by the firmware + */ +enum iwl_bt_coex_valid_bit_msk { + BT_VALID_ENABLE = BIT(0), + BT_VALID_BT_PRIO_BOOST = BIT(1), + BT_VALID_MAX_KILL = BIT(2), + BT_VALID_3W_TMRS = BIT(3), + BT_VALID_KILL_ACK = BIT(4), + BT_VALID_KILL_CTS = BIT(5), + BT_VALID_REDUCED_TX_POWER = BIT(6), + BT_VALID_LUT = BIT(7), + BT_VALID_WIFI_RX_SW_PRIO_BOOST = BIT(8), + BT_VALID_WIFI_TX_SW_PRIO_BOOST = BIT(9), + BT_VALID_MULTI_PRIO_LUT = BIT(10), + BT_VALID_TRM_KICK_FILTER = BIT(11), + BT_VALID_CORUN_LUT_20 = BIT(12), + BT_VALID_CORUN_LUT_40 = BIT(13), + BT_VALID_ANT_ISOLATION = BIT(14), + BT_VALID_ANT_ISOLATION_THRS = BIT(15), + BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16), + BT_VALID_TXRX_MAX_FREQ_0 = BIT(17), + BT_VALID_SYNC_TO_SCO = BIT(18), + BT_VALID_TTC = BIT(20), + BT_VALID_RRC = BIT(21), +}; + +/** + * enum iwl_bt_reduced_tx_power - allows to reduce txpower for WiFi frames. + * @BT_REDUCED_TX_POWER_CTL: reduce Tx power for control frames + * @BT_REDUCED_TX_POWER_DATA: reduce Tx power for data frames + * + * This mechanism allows to have BT and WiFi run concurrently. Since WiFi + * reduces its Tx power, it can work along with BT, hence reducing the amount + * of WiFi frames being killed by BT. + */ +enum iwl_bt_reduced_tx_power { + BT_REDUCED_TX_POWER_CTL = BIT(0), + BT_REDUCED_TX_POWER_DATA = BIT(1), +}; + +enum iwl_bt_coex_lut_type { + BT_COEX_TIGHT_LUT = 0, + BT_COEX_LOOSE_LUT, + BT_COEX_TX_DIS_LUT, + + BT_COEX_MAX_LUT, + BT_COEX_INVALID_LUT = 0xff, +}; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */ + +#define BT_COEX_LUT_SIZE (12) +#define BT_COEX_CORUN_LUT_SIZE (32) +#define BT_COEX_MULTI_PRIO_LUT_SIZE (2) +#define BT_COEX_BOOST_SIZE (4) +#define BT_REDUCED_TX_POWER_BIT BIT(7) + +/** + * struct iwl_bt_coex_cmd_old - bt coex configuration command + * @flags:&enum iwl_bt_coex_flags + * @max_kill: + * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power + * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT + * should be set by default + * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT + * should be set by default + * @bt4_antenna_isolation: antenna isolation + * @bt4_antenna_isolation_thr: antenna threshold value + * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency + * @bt4_tx_rx_max_freq0: TxRx max frequency + * @bt_prio_boost: BT priority boost registers + * @wifi_tx_prio_boost: SW boost of wifi tx priority + * @wifi_rx_prio_boost: SW boost of wifi rx priority + * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK. + * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS. + * @decision_lut: PTA decision LUT, per Prio-Ch + * @bt4_multiprio_lut: multi priority LUT configuration + * @bt4_corun_lut20: co-running 20 MHz LUT configuration + * @bt4_corun_lut40: co-running 40 MHz LUT configuration + * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk + * + * The structure is used for the BT_COEX command. + */ +struct iwl_bt_coex_cmd_old { + __le32 flags; + u8 max_kill; + u8 bt_reduced_tx_power; + u8 override_primary_lut; + u8 override_secondary_lut; + + u8 bt4_antenna_isolation; + u8 bt4_antenna_isolation_thr; + u8 bt4_tx_tx_delta_freq_thr; + u8 bt4_tx_rx_max_freq0; + + __le32 bt_prio_boost[BT_COEX_BOOST_SIZE]; + __le32 wifi_tx_prio_boost; + __le32 wifi_rx_prio_boost; + __le32 kill_ack_msk; + __le32 kill_cts_msk; + + __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE]; + __le32 bt4_multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE]; + __le32 bt4_corun_lut20[BT_COEX_CORUN_LUT_SIZE]; + __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE]; + + __le32 valid_bit_msk; +} __packed; /* BT_COEX_CMD_API_S_VER_5 */ + +enum iwl_bt_coex_mode { + BT_COEX_DISABLE = 0x0, + BT_COEX_NW = 0x1, + BT_COEX_BT = 0x2, + BT_COEX_WIFI = 0x3, +}; /* BT_COEX_MODES_E */ + +enum iwl_bt_coex_enabled_modules { + BT_COEX_MPLUT_ENABLED = BIT(0), + BT_COEX_MPLUT_BOOST_ENABLED = BIT(1), + BT_COEX_SYNC2SCO_ENABLED = BIT(2), + BT_COEX_CORUN_ENABLED = BIT(3), + BT_COEX_HIGH_BAND_RET = BIT(4), +}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */ + +/** + * struct iwl_bt_coex_cmd - bt coex configuration command + * @mode: enum %iwl_bt_coex_mode + * @enabled_modules: enum %iwl_bt_coex_enabled_modules + * + * The structure is used for the BT_COEX command. + */ +struct iwl_bt_coex_cmd { + __le32 mode; + __le32 enabled_modules; +} __packed; /* BT_COEX_CMD_API_S_VER_6 */ + +/** + * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut + * @corun_lut20: co-running 20 MHz LUT configuration + * @corun_lut40: co-running 40 MHz LUT configuration + * + * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command. + */ +struct iwl_bt_coex_corun_lut_update_cmd { + __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE]; + __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE]; +} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */ + +/** + * struct iwl_bt_coex_reduced_txp_update_cmd + * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the + * bits are the sta_id (value) + */ +struct iwl_bt_coex_reduced_txp_update_cmd { + __le32 reduced_txp; +} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */ + +/** + * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command + * @bt_primary_ci: + * @primary_ch_phy_id: + * @bt_secondary_ci: + * @secondary_ch_phy_id: + * + * Used for BT_COEX_CI command + */ +struct iwl_bt_coex_ci_cmd { + __le64 bt_primary_ci; + __le32 primary_ch_phy_id; + + __le64 bt_secondary_ci; + __le32 secondary_ch_phy_id; +} __packed; /* BT_CI_MSG_API_S_VER_2 */ + +#define BT_MBOX(n_dw, _msg, _pos, _nbits) \ + BT_MBOX##n_dw##_##_msg##_POS = (_pos), \ + BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS + +enum iwl_bt_mxbox_dw0 { + BT_MBOX(0, LE_SLAVE_LAT, 0, 3), + BT_MBOX(0, LE_PROF1, 3, 1), + BT_MBOX(0, LE_PROF2, 4, 1), + BT_MBOX(0, LE_PROF_OTHER, 5, 1), + BT_MBOX(0, CHL_SEQ_N, 8, 4), + BT_MBOX(0, INBAND_S, 13, 1), + BT_MBOX(0, LE_MIN_RSSI, 16, 4), + BT_MBOX(0, LE_SCAN, 20, 1), + BT_MBOX(0, LE_ADV, 21, 1), + BT_MBOX(0, LE_MAX_TX_POWER, 24, 4), + BT_MBOX(0, OPEN_CON_1, 28, 2), +}; + +enum iwl_bt_mxbox_dw1 { + BT_MBOX(1, BR_MAX_TX_POWER, 0, 4), + BT_MBOX(1, IP_SR, 4, 1), + BT_MBOX(1, LE_MSTR, 5, 1), + BT_MBOX(1, AGGR_TRFC_LD, 8, 6), + BT_MBOX(1, MSG_TYPE, 16, 3), + BT_MBOX(1, SSN, 19, 2), +}; + +enum iwl_bt_mxbox_dw2 { + BT_MBOX(2, SNIFF_ACT, 0, 3), + BT_MBOX(2, PAG, 3, 1), + BT_MBOX(2, INQUIRY, 4, 1), + BT_MBOX(2, CONN, 5, 1), + BT_MBOX(2, SNIFF_INTERVAL, 8, 5), + BT_MBOX(2, DISC, 13, 1), + BT_MBOX(2, SCO_TX_ACT, 16, 2), + BT_MBOX(2, SCO_RX_ACT, 18, 2), + BT_MBOX(2, ESCO_RE_TX, 20, 2), + BT_MBOX(2, SCO_DURATION, 24, 6), +}; + +enum iwl_bt_mxbox_dw3 { + BT_MBOX(3, SCO_STATE, 0, 1), + BT_MBOX(3, SNIFF_STATE, 1, 1), + BT_MBOX(3, A2DP_STATE, 2, 1), + BT_MBOX(3, ACL_STATE, 3, 1), + BT_MBOX(3, MSTR_STATE, 4, 1), + BT_MBOX(3, OBX_STATE, 5, 1), + BT_MBOX(3, OPEN_CON_2, 8, 2), + BT_MBOX(3, TRAFFIC_LOAD, 10, 2), + BT_MBOX(3, CHL_SEQN_LSB, 12, 1), + BT_MBOX(3, INBAND_P, 13, 1), + BT_MBOX(3, MSG_TYPE_2, 16, 3), + BT_MBOX(3, SSN_2, 19, 2), + BT_MBOX(3, UPDATE_REQUEST, 21, 1), +}; + +#define BT_MBOX_MSG(_notif, _num, _field) \ + ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ + >> BT_MBOX##_num##_##_field##_POS) + +enum iwl_bt_activity_grading { + BT_OFF = 0, + BT_ON_NO_CONNECTION = 1, + BT_LOW_TRAFFIC = 2, + BT_HIGH_TRAFFIC = 3, + + BT_MAX_AG, +}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */ + +enum iwl_bt_ci_compliance { + BT_CI_COMPLIANCE_NONE = 0, + BT_CI_COMPLIANCE_PRIMARY = 1, + BT_CI_COMPLIANCE_SECONDARY = 2, + BT_CI_COMPLIANCE_BOTH = 3, +}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */ + +#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id) \ + (_ttc_rrc_status & BIT(_phy_id)) + +#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id) \ + ((_ttc_rrc_status >> 4) & BIT(_phy_id)) + +/** + * struct iwl_bt_coex_profile_notif - notification about BT coex + * @mbox_msg: message from BT to WiFi + * @msg_idx: the index of the message + * @bt_ci_compliance: enum %iwl_bt_ci_compliance + * @primary_ch_lut: LUT used for primary channel enum %iwl_bt_coex_lut_type + * @secondary_ch_lut: LUT used for secondary channel enume %iwl_bt_coex_lut_type + * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading + * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY + */ +struct iwl_bt_coex_profile_notif { + __le32 mbox_msg[4]; + __le32 msg_idx; + __le32 bt_ci_compliance; + + __le32 primary_ch_lut; + __le32 secondary_ch_lut; + __le32 bt_activity_grading; + u8 ttc_rrc_status; + u8 reserved[3]; +} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */ + +enum iwl_bt_coex_prio_table_event { + BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2 = 1, + BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1 = 2, + BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2 = 3, + BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1 = 4, + BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2 = 5, + BT_COEX_PRIO_TBL_EVT_DTIM = 6, + BT_COEX_PRIO_TBL_EVT_SCAN52 = 7, + BT_COEX_PRIO_TBL_EVT_SCAN24 = 8, + BT_COEX_PRIO_TBL_EVT_IDLE = 9, + BT_COEX_PRIO_TBL_EVT_MAX = 16, +}; /* BT_COEX_PRIO_TABLE_EVENTS_API_E_VER_1 */ + +enum iwl_bt_coex_prio_table_prio { + BT_COEX_PRIO_TBL_DISABLED = 0, + BT_COEX_PRIO_TBL_PRIO_LOW = 1, + BT_COEX_PRIO_TBL_PRIO_HIGH = 2, + BT_COEX_PRIO_TBL_PRIO_BYPASS = 3, + BT_COEX_PRIO_TBL_PRIO_COEX_OFF = 4, + BT_COEX_PRIO_TBL_PRIO_COEX_ON = 5, + BT_COEX_PRIO_TBL_PRIO_COEX_IDLE = 6, + BT_COEX_PRIO_TBL_MAX = 8, +}; /* BT_COEX_PRIO_TABLE_PRIORITIES_API_E_VER_1 */ + +#define BT_COEX_PRIO_TBL_SHRD_ANT_POS (0) +#define BT_COEX_PRIO_TBL_PRIO_POS (1) +#define BT_COEX_PRIO_TBL_RESERVED_POS (4) + +/** + * struct iwl_bt_coex_prio_tbl_cmd - priority table for BT coex + * @prio_tbl: + */ +struct iwl_bt_coex_prio_tbl_cmd { + u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX]; +} __packed; + +/** + * struct iwl_bt_coex_ci_cmd_old - bt coex channel inhibition command + * @bt_primary_ci: + * @bt_secondary_ci: + * @co_run_bw_primary: + * @co_run_bw_secondary: + * @primary_ch_phy_id: + * @secondary_ch_phy_id: + * + * Used for BT_COEX_CI command + */ +struct iwl_bt_coex_ci_cmd_old { + __le64 bt_primary_ci; + __le64 bt_secondary_ci; + + u8 co_run_bw_primary; + u8 co_run_bw_secondary; + u8 primary_ch_phy_id; + u8 secondary_ch_phy_id; +} __packed; /* BT_CI_MSG_API_S_VER_1 */ + +/** + * struct iwl_bt_coex_profile_notif_old - notification about BT coex + * @mbox_msg: message from BT to WiFi + * @msg_idx: the index of the message + * @bt_status: 0 - off, 1 - on + * @bt_open_conn: number of BT connections open + * @bt_traffic_load: load of BT traffic + * @bt_agg_traffic_load: aggregated load of BT traffic + * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant + * @primary_ch_lut: LUT used for primary channel + * @secondary_ch_lut: LUT used for secondary channel + * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading + */ +struct iwl_bt_coex_profile_notif_old { + __le32 mbox_msg[4]; + __le32 msg_idx; + u8 bt_status; + u8 bt_open_conn; + u8 bt_traffic_load; + u8 bt_agg_traffic_load; + u8 bt_ci_compliance; + u8 ttc_enabled; + u8 rrc_enabled; + u8 reserved; + + __le32 primary_ch_lut; + __le32 secondary_ch_lut; + __le32 bt_activity_grading; +} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */ + +#endif /* __fw_api_bt_coex_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h new file mode 100644 index 000000000000..20521bebb0b1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h @@ -0,0 +1,425 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __fw_api_d3_h__ +#define __fw_api_d3_h__ + +/** + * enum iwl_d3_wakeup_flags - D3 manager wakeup flags + * @IWL_WAKEUP_D3_CONFIG_FW_ERROR: wake up on firmware sysassert + */ +enum iwl_d3_wakeup_flags { + IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0), +}; /* D3_MANAGER_WAKEUP_CONFIG_API_E_VER_3 */ + +/** + * struct iwl_d3_manager_config - D3 manager configuration command + * @min_sleep_time: minimum sleep time (in usec) + * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags + * @wakeup_host_timer: force wakeup after this many seconds + * + * The structure is used for the D3_CONFIG_CMD command. + */ +struct iwl_d3_manager_config { + __le32 min_sleep_time; + __le32 wakeup_flags; + __le32 wakeup_host_timer; +} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */ + + +/* TODO: OFFLOADS_QUERY_API_S_VER_1 */ + +/** + * enum iwl_d3_proto_offloads - enabled protocol offloads + * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled + * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled + */ +enum iwl_proto_offloads { + IWL_D3_PROTO_OFFLOAD_ARP = BIT(0), + IWL_D3_PROTO_OFFLOAD_NS = BIT(1), +}; + +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L 12 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S 4 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 12 + +#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L 4 +#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S 2 + +/** + * struct iwl_proto_offload_cmd_common - ARP/NS offload common part + * @enabled: enable flags + * @remote_ipv4_addr: remote address to answer to (or zero if all) + * @host_ipv4_addr: our IPv4 address to respond to queries for + * @arp_mac_addr: our MAC address for ARP responses + * @reserved: unused + */ +struct iwl_proto_offload_cmd_common { + __le32 enabled; + __be32 remote_ipv4_addr; + __be32 host_ipv4_addr; + u8 arp_mac_addr[ETH_ALEN]; + __le16 reserved; +} __packed; + +/** + * struct iwl_proto_offload_cmd_v1 - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @remote_ipv6_addr: remote address to answer to (or zero if all) + * @solicited_node_ipv6_addr: broken -- solicited node address exists + * for each target address + * @target_ipv6_addr: our target addresses + * @ndp_mac_addr: neighbor solicitation response MAC address + */ +struct iwl_proto_offload_cmd_v1 { + struct iwl_proto_offload_cmd_common common; + u8 remote_ipv6_addr[16]; + u8 solicited_node_ipv6_addr[16]; + u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1][16]; + u8 ndp_mac_addr[ETH_ALEN]; + __le16 reserved2; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */ + +/** + * struct iwl_proto_offload_cmd_v2 - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @remote_ipv6_addr: remote address to answer to (or zero if all) + * @solicited_node_ipv6_addr: broken -- solicited node address exists + * for each target address + * @target_ipv6_addr: our target addresses + * @ndp_mac_addr: neighbor solicitation response MAC address + */ +struct iwl_proto_offload_cmd_v2 { + struct iwl_proto_offload_cmd_common common; + u8 remote_ipv6_addr[16]; + u8 solicited_node_ipv6_addr[16]; + u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2][16]; + u8 ndp_mac_addr[ETH_ALEN]; + u8 numValidIPv6Addresses; + u8 reserved2[3]; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */ + +struct iwl_ns_config { + struct in6_addr source_ipv6_addr; + struct in6_addr dest_ipv6_addr; + u8 target_mac_addr[ETH_ALEN]; + __le16 reserved; +} __packed; /* NS_OFFLOAD_CONFIG */ + +struct iwl_targ_addr { + struct in6_addr addr; + __le32 config_num; +} __packed; /* TARGET_IPV6_ADDRESS */ + +/** + * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @target_ipv6_addr: target IPv6 addresses + * @ns_config: NS offload configurations + */ +struct iwl_proto_offload_cmd_v3_small { + struct iwl_proto_offload_cmd_common common; + __le32 num_valid_ipv6_addrs; + struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S]; + struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S]; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ + +/** + * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @target_ipv6_addr: target IPv6 addresses + * @ns_config: NS offload configurations + */ +struct iwl_proto_offload_cmd_v3_large { + struct iwl_proto_offload_cmd_common common; + __le32 num_valid_ipv6_addrs; + struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L]; + struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L]; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ + +/* + * WOWLAN_PATTERNS + */ +#define IWL_WOWLAN_MIN_PATTERN_LEN 16 +#define IWL_WOWLAN_MAX_PATTERN_LEN 128 + +struct iwl_wowlan_pattern { + u8 mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; + u8 pattern[IWL_WOWLAN_MAX_PATTERN_LEN]; + u8 mask_size; + u8 pattern_size; + __le16 reserved; +} __packed; /* WOWLAN_PATTERN_API_S_VER_1 */ + +#define IWL_WOWLAN_MAX_PATTERNS 20 + +struct iwl_wowlan_patterns_cmd { + __le32 n_patterns; + struct iwl_wowlan_pattern patterns[]; +} __packed; /* WOWLAN_PATTERN_ARRAY_API_S_VER_1 */ + +enum iwl_wowlan_wakeup_filters { + IWL_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0), + IWL_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1), + IWL_WOWLAN_WAKEUP_BEACON_MISS = BIT(2), + IWL_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3), + IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4), + IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5), + IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6), + IWL_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(7), + IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT = BIT(8), + IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS = BIT(9), + IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE = BIT(10), + IWL_WOWLAN_WAKEUP_REMOTE_TCP_EXTERNAL = BIT(11), + IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET = BIT(12), + IWL_WOWLAN_WAKEUP_IOAC_MAGIC_PACKET = BIT(13), + IWL_WOWLAN_WAKEUP_HOST_TIMER = BIT(14), + IWL_WOWLAN_WAKEUP_RX_FRAME = BIT(15), + IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16), +}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */ + +struct iwl_wowlan_config_cmd { + __le32 wakeup_filter; + __le16 non_qos_seq; + __le16 qos_seq[8]; + u8 wowlan_ba_teardown_tids; + u8 is_11n_connection; + u8 offloading_tid; + u8 reserved[3]; +} __packed; /* WOWLAN_CONFIG_API_S_VER_3 */ + +/* + * WOWLAN_TSC_RSC_PARAMS + */ +#define IWL_NUM_RSC 16 + +struct tkip_sc { + __le16 iv16; + __le16 pad; + __le32 iv32; +} __packed; /* TKIP_SC_API_U_VER_1 */ + +struct iwl_tkip_rsc_tsc { + struct tkip_sc unicast_rsc[IWL_NUM_RSC]; + struct tkip_sc multicast_rsc[IWL_NUM_RSC]; + struct tkip_sc tsc; +} __packed; /* TKIP_TSC_RSC_API_S_VER_1 */ + +struct aes_sc { + __le64 pn; +} __packed; /* TKIP_AES_SC_API_U_VER_1 */ + +struct iwl_aes_rsc_tsc { + struct aes_sc unicast_rsc[IWL_NUM_RSC]; + struct aes_sc multicast_rsc[IWL_NUM_RSC]; + struct aes_sc tsc; +} __packed; /* AES_TSC_RSC_API_S_VER_1 */ + +union iwl_all_tsc_rsc { + struct iwl_tkip_rsc_tsc tkip; + struct iwl_aes_rsc_tsc aes; +}; /* ALL_TSC_RSC_API_S_VER_2 */ + +struct iwl_wowlan_rsc_tsc_params_cmd { + union iwl_all_tsc_rsc all_tsc_rsc; +} __packed; /* ALL_TSC_RSC_API_S_VER_2 */ + +#define IWL_MIC_KEY_SIZE 8 +struct iwl_mic_keys { + u8 tx[IWL_MIC_KEY_SIZE]; + u8 rx_unicast[IWL_MIC_KEY_SIZE]; + u8 rx_mcast[IWL_MIC_KEY_SIZE]; +} __packed; /* MIC_KEYS_API_S_VER_1 */ + +#define IWL_P1K_SIZE 5 +struct iwl_p1k_cache { + __le16 p1k[IWL_P1K_SIZE]; +} __packed; + +#define IWL_NUM_RX_P1K_CACHE 2 + +struct iwl_wowlan_tkip_params_cmd { + struct iwl_mic_keys mic_keys; + struct iwl_p1k_cache tx; + struct iwl_p1k_cache rx_uni[IWL_NUM_RX_P1K_CACHE]; + struct iwl_p1k_cache rx_multi[IWL_NUM_RX_P1K_CACHE]; +} __packed; /* WOWLAN_TKIP_SETTING_API_S_VER_1 */ + +#define IWL_KCK_MAX_SIZE 32 +#define IWL_KEK_MAX_SIZE 32 + +struct iwl_wowlan_kek_kck_material_cmd { + u8 kck[IWL_KCK_MAX_SIZE]; + u8 kek[IWL_KEK_MAX_SIZE]; + __le16 kck_len; + __le16 kek_len; + __le64 replay_ctr; +} __packed; /* KEK_KCK_MATERIAL_API_S_VER_2 */ + +#define RF_KILL_INDICATOR_FOR_WOWLAN 0x87 + +enum iwl_wowlan_rekey_status { + IWL_WOWLAN_REKEY_POST_REKEY = 0, + IWL_WOWLAN_REKEY_WHILE_REKEY = 1, +}; /* WOWLAN_REKEY_STATUS_API_E_VER_1 */ + +enum iwl_wowlan_wakeup_reason { + IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS = 0, + IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET = BIT(0), + IWL_WOWLAN_WAKEUP_BY_PATTERN = BIT(1), + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON = BIT(2), + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH = BIT(3), + IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE = BIT(4), + IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED = BIT(5), + IWL_WOWLAN_WAKEUP_BY_UCODE_ERROR = BIT(6), + IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST = BIT(7), + IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE = BIT(8), + IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS = BIT(9), + IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE = BIT(10), + IWL_WOWLAN_WAKEUP_BY_REM_WAKE_TCP_EXTERNAL = BIT(11), + IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12), + IWL_WOWLAN_WAKEUP_BY_IOAC_MAGIC_PACKET = BIT(13), + IWL_WOWLAN_WAKEUP_BY_D3_WAKEUP_HOST_TIMER = BIT(14), + IWL_WOWLAN_WAKEUP_BY_RXFRAME_FILTERED_IN = BIT(15), + IWL_WOWLAN_WAKEUP_BY_BEACON_FILTERED_IN = BIT(16), + +}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */ + +struct iwl_wowlan_gtk_status { + u8 key_index; + u8 reserved[3]; + u8 decrypt_key[16]; + u8 tkip_mic_key[8]; + struct iwl_wowlan_rsc_tsc_params_cmd rsc; +} __packed; + +struct iwl_wowlan_status { + struct iwl_wowlan_gtk_status gtk; + __le64 replay_ctr; + __le16 pattern_number; + __le16 non_qos_seq_ctr; + __le16 qos_seq_ctr[8]; + __le32 wakeup_reasons; + __le32 num_of_gtk_rekeys; + __le32 transmitted_ndps; + __le32 received_beacons; + __le32 wake_packet_length; + __le32 wake_packet_bufsize; + u8 wake_packet[]; /* can be truncated from _length to _bufsize */ +} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */ + +#define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64 +#define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128 +#define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048 + +struct iwl_tcp_packet_info { + __le16 tcp_pseudo_header_checksum; + __le16 tcp_payload_length; +} __packed; /* TCP_PACKET_INFO_API_S_VER_2 */ + +struct iwl_tcp_packet { + struct iwl_tcp_packet_info info; + u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; + u8 data[IWL_WOWLAN_TCP_MAX_PACKET_LEN]; +} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */ + +struct iwl_remote_wake_packet { + struct iwl_tcp_packet_info info; + u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; + u8 data[IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN]; +} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */ + +struct iwl_wowlan_remote_wake_config { + __le32 connection_max_time; /* unused */ + /* TCP_PROTOCOL_CONFIG_API_S_VER_1 */ + u8 max_syn_retries; + u8 max_data_retries; + u8 tcp_syn_ack_timeout; + u8 tcp_ack_timeout; + + struct iwl_tcp_packet syn_tx; + struct iwl_tcp_packet synack_rx; + struct iwl_tcp_packet keepalive_ack_rx; + struct iwl_tcp_packet fin_tx; + + struct iwl_remote_wake_packet keepalive_tx; + struct iwl_remote_wake_packet wake_rx; + + /* REMOTE_WAKE_OFFSET_INFO_API_S_VER_1 */ + u8 sequence_number_offset; + u8 sequence_number_length; + u8 token_offset; + u8 token_length; + /* REMOTE_WAKE_PROTOCOL_PARAMS_API_S_VER_1 */ + __le32 initial_sequence_number; + __le16 keepalive_interval; + __le16 num_tokens; + u8 tokens[IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS]; +} __packed; /* REMOTE_WAKE_CONFIG_API_S_VER_2 */ + +/* TODO: NetDetect API */ + +#endif /* __fw_api_d3_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h new file mode 100644 index 000000000000..f3f3ee0a766b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h @@ -0,0 +1,387 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __fw_api_mac_h__ +#define __fw_api_mac_h__ + +/* + * The first MAC indices (starting from 0) + * are available to the driver, AUX follows + */ +#define MAC_INDEX_AUX 4 +#define MAC_INDEX_MIN_DRIVER 0 +#define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX +#define NUM_MAC_INDEX (MAC_INDEX_AUX + 1) + +enum iwl_ac { + AC_BK, + AC_BE, + AC_VI, + AC_VO, + AC_NUM, +}; + +/** + * enum iwl_mac_protection_flags - MAC context flags + * @MAC_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames, + * this will require CCK RTS/CTS2self. + * RTS/CTS will protect full burst time. + * @MAC_PROT_FLG_HT_PROT: enable HT protection + * @MAC_PROT_FLG_FAT_PROT: protect 40 MHz transmissions + * @MAC_PROT_FLG_SELF_CTS_EN: allow CTS2self + */ +enum iwl_mac_protection_flags { + MAC_PROT_FLG_TGG_PROTECT = BIT(3), + MAC_PROT_FLG_HT_PROT = BIT(23), + MAC_PROT_FLG_FAT_PROT = BIT(24), + MAC_PROT_FLG_SELF_CTS_EN = BIT(30), +}; + +#define MAC_FLG_SHORT_SLOT BIT(4) +#define MAC_FLG_SHORT_PREAMBLE BIT(5) + +/** + * enum iwl_mac_types - Supported MAC types + * @FW_MAC_TYPE_FIRST: lowest supported MAC type + * @FW_MAC_TYPE_AUX: Auxiliary MAC (internal) + * @FW_MAC_TYPE_LISTENER: monitor MAC type (?) + * @FW_MAC_TYPE_PIBSS: Pseudo-IBSS + * @FW_MAC_TYPE_IBSS: IBSS + * @FW_MAC_TYPE_BSS_STA: BSS (managed) station + * @FW_MAC_TYPE_P2P_DEVICE: P2P Device + * @FW_MAC_TYPE_P2P_STA: P2P client + * @FW_MAC_TYPE_GO: P2P GO + * @FW_MAC_TYPE_TEST: ? + * @FW_MAC_TYPE_MAX: highest support MAC type + */ +enum iwl_mac_types { + FW_MAC_TYPE_FIRST = 1, + FW_MAC_TYPE_AUX = FW_MAC_TYPE_FIRST, + FW_MAC_TYPE_LISTENER, + FW_MAC_TYPE_PIBSS, + FW_MAC_TYPE_IBSS, + FW_MAC_TYPE_BSS_STA, + FW_MAC_TYPE_P2P_DEVICE, + FW_MAC_TYPE_P2P_STA, + FW_MAC_TYPE_GO, + FW_MAC_TYPE_TEST, + FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST +}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */ + +/** + * enum iwl_tsf_id - TSF hw timer ID + * @TSF_ID_A: use TSF A + * @TSF_ID_B: use TSF B + * @TSF_ID_C: use TSF C + * @TSF_ID_D: use TSF D + * @NUM_TSF_IDS: number of TSF timers available + */ +enum iwl_tsf_id { + TSF_ID_A = 0, + TSF_ID_B = 1, + TSF_ID_C = 2, + TSF_ID_D = 3, + NUM_TSF_IDS = 4, +}; /* TSF_ID_API_E_VER_1 */ + +/** + * struct iwl_mac_data_ap - configuration data for AP MAC context + * @beacon_time: beacon transmit time in system time + * @beacon_tsf: beacon transmit time in TSF + * @bi: beacon interval in TU + * @bi_reciprocal: 2^32 / bi + * @dtim_interval: dtim transmit time in TU + * @dtim_reciprocal: 2^32 / dtim_interval + * @mcast_qid: queue ID for multicast traffic + * @beacon_template: beacon template ID + */ +struct iwl_mac_data_ap { + __le32 beacon_time; + __le64 beacon_tsf; + __le32 bi; + __le32 bi_reciprocal; + __le32 dtim_interval; + __le32 dtim_reciprocal; + __le32 mcast_qid; + __le32 beacon_template; +} __packed; /* AP_MAC_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_data_ibss - configuration data for IBSS MAC context + * @beacon_time: beacon transmit time in system time + * @beacon_tsf: beacon transmit time in TSF + * @bi: beacon interval in TU + * @bi_reciprocal: 2^32 / bi + * @beacon_template: beacon template ID + */ +struct iwl_mac_data_ibss { + __le32 beacon_time; + __le64 beacon_tsf; + __le32 bi; + __le32 bi_reciprocal; + __le32 beacon_template; +} __packed; /* IBSS_MAC_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_data_sta - configuration data for station MAC context + * @is_assoc: 1 for associated state, 0 otherwise + * @dtim_time: DTIM arrival time in system time + * @dtim_tsf: DTIM arrival time in TSF + * @bi: beacon interval in TU, applicable only when associated + * @bi_reciprocal: 2^32 / bi , applicable only when associated + * @dtim_interval: DTIM interval in TU, applicable only when associated + * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated + * @listen_interval: in beacon intervals, applicable only when associated + * @assoc_id: unique ID assigned by the AP during association + */ +struct iwl_mac_data_sta { + __le32 is_assoc; + __le32 dtim_time; + __le64 dtim_tsf; + __le32 bi; + __le32 bi_reciprocal; + __le32 dtim_interval; + __le32 dtim_reciprocal; + __le32 listen_interval; + __le32 assoc_id; + __le32 assoc_beacon_arrive_time; +} __packed; /* STA_MAC_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_data_go - configuration data for P2P GO MAC context + * @ap: iwl_mac_data_ap struct with most config data + * @ctwin: client traffic window in TU (period after TBTT when GO is present). + * 0 indicates that there is no CT window. + * @opp_ps_enabled: indicate that opportunistic PS allowed + */ +struct iwl_mac_data_go { + struct iwl_mac_data_ap ap; + __le32 ctwin; + __le32 opp_ps_enabled; +} __packed; /* GO_MAC_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_data_p2p_sta - configuration data for P2P client MAC context + * @sta: iwl_mac_data_sta struct with most config data + * @ctwin: client traffic window in TU (period after TBTT when GO is present). + * 0 indicates that there is no CT window. + */ +struct iwl_mac_data_p2p_sta { + struct iwl_mac_data_sta sta; + __le32 ctwin; +} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */ + +/** + * struct iwl_mac_data_pibss - Pseudo IBSS config data + * @stats_interval: interval in TU between statistics notifications to host. + */ +struct iwl_mac_data_pibss { + __le32 stats_interval; +} __packed; /* PIBSS_MAC_DATA_API_S_VER_1 */ + +/* + * struct iwl_mac_data_p2p_dev - configuration data for the P2P Device MAC + * context. + * @is_disc_extended: if set to true, P2P Device discoverability is enabled on + * other channels as well. This should be to true only in case that the + * device is discoverable and there is an active GO. Note that setting this + * field when not needed, will increase the number of interrupts and have + * effect on the platform power, as this setting opens the Rx filters on + * all macs. + */ +struct iwl_mac_data_p2p_dev { + __le32 is_disc_extended; +} __packed; /* _P2P_DEV_MAC_DATA_API_S_VER_1 */ + +/** + * enum iwl_mac_filter_flags - MAC context filter flags + * @MAC_FILTER_IN_PROMISC: accept all data frames + * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all management and + * control frames to the host + * @MAC_FILTER_ACCEPT_GRP: accept multicast frames + * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames + * @MAC_FILTER_DIS_GRP_DECRYPT: don't decrypt multicast frames + * @MAC_FILTER_IN_BEACON: transfer foreign BSS's beacons to host + * (in station mode when associated) + * @MAC_FILTER_OUT_BCAST: filter out all broadcast frames + * @MAC_FILTER_IN_CRC32: extract FCS and append it to frames + * @MAC_FILTER_IN_PROBE_REQUEST: pass probe requests to host + */ +enum iwl_mac_filter_flags { + MAC_FILTER_IN_PROMISC = BIT(0), + MAC_FILTER_IN_CONTROL_AND_MGMT = BIT(1), + MAC_FILTER_ACCEPT_GRP = BIT(2), + MAC_FILTER_DIS_DECRYPT = BIT(3), + MAC_FILTER_DIS_GRP_DECRYPT = BIT(4), + MAC_FILTER_IN_BEACON = BIT(6), + MAC_FILTER_OUT_BCAST = BIT(8), + MAC_FILTER_IN_CRC32 = BIT(11), + MAC_FILTER_IN_PROBE_REQUEST = BIT(12), +}; + +/** + * enum iwl_mac_qos_flags - QoS flags + * @MAC_QOS_FLG_UPDATE_EDCA: ? + * @MAC_QOS_FLG_TGN: HT is enabled + * @MAC_QOS_FLG_TXOP_TYPE: ? + * + */ +enum iwl_mac_qos_flags { + MAC_QOS_FLG_UPDATE_EDCA = BIT(0), + MAC_QOS_FLG_TGN = BIT(1), + MAC_QOS_FLG_TXOP_TYPE = BIT(4), +}; + +/** + * struct iwl_ac_qos - QOS timing params for MAC_CONTEXT_CMD + * @cw_min: Contention window, start value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x0f. + * @cw_max: Contention window, max value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x3f. + * @aifsn: Number of slots in Arbitration Interframe Space (before + * performing random backoff timing prior to Tx). Device default 1. + * @fifos_mask: FIFOs used by this MAC for this AC + * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. + * + * One instance of this config struct for each of 4 EDCA access categories + * in struct iwl_qosparam_cmd. + * + * Device will automatically increase contention window by (2*CW) + 1 for each + * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW + * value, to cap the CW value. + */ +struct iwl_ac_qos { + __le16 cw_min; + __le16 cw_max; + u8 aifsn; + u8 fifos_mask; + __le16 edca_txop; +} __packed; /* AC_QOS_API_S_VER_2 */ + +/** + * struct iwl_mac_ctx_cmd - command structure to configure MAC contexts + * ( MAC_CONTEXT_CMD = 0x28 ) + * @id_and_color: ID and color of the MAC + * @action: action to perform, one of FW_CTXT_ACTION_* + * @mac_type: one of FW_MAC_TYPE_* + * @tsd_id: TSF HW timer, one of TSF_ID_* + * @node_addr: MAC address + * @bssid_addr: BSSID + * @cck_rates: basic rates available for CCK + * @ofdm_rates: basic rates available for OFDM + * @protection_flags: combination of MAC_PROT_FLG_FLAG_* + * @cck_short_preamble: 0x20 for enabling short preamble, 0 otherwise + * @short_slot: 0x10 for enabling short slots, 0 otherwise + * @filter_flags: combination of MAC_FILTER_* + * @qos_flags: from MAC_QOS_FLG_* + * @ac: one iwl_mac_qos configuration for each AC + * @mac_specific: one of struct iwl_mac_data_*, according to mac_type + */ +struct iwl_mac_ctx_cmd { + /* COMMON_INDEX_HDR_API_S_VER_1 */ + __le32 id_and_color; + __le32 action; + /* MAC_CONTEXT_COMMON_DATA_API_S_VER_1 */ + __le32 mac_type; + __le32 tsf_id; + u8 node_addr[6]; + __le16 reserved_for_node_addr; + u8 bssid_addr[6]; + __le16 reserved_for_bssid_addr; + __le32 cck_rates; + __le32 ofdm_rates; + __le32 protection_flags; + __le32 cck_short_preamble; + __le32 short_slot; + __le32 filter_flags; + /* MAC_QOS_PARAM_API_S_VER_1 */ + __le32 qos_flags; + struct iwl_ac_qos ac[AC_NUM+1]; + /* MAC_CONTEXT_COMMON_DATA_API_S */ + union { + struct iwl_mac_data_ap ap; + struct iwl_mac_data_go go; + struct iwl_mac_data_sta sta; + struct iwl_mac_data_p2p_sta p2p_sta; + struct iwl_mac_data_p2p_dev p2p_dev; + struct iwl_mac_data_pibss pibss; + struct iwl_mac_data_ibss ibss; + }; +} __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */ + +static inline u32 iwl_mvm_reciprocal(u32 v) +{ + if (!v) + return 0; + return 0xFFFFFFFF / v; +} + +#define IWL_NONQOS_SEQ_GET 0x1 +#define IWL_NONQOS_SEQ_SET 0x2 +struct iwl_nonqos_seq_query_cmd { + __le32 get_set_flag; + __le32 mac_id_n_color; + __le16 value; + __le16 reserved; +} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */ + +#endif /* __fw_api_mac_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h new file mode 100644 index 000000000000..c8f3e2536cbb --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h @@ -0,0 +1,467 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __fw_api_power_h__ +#define __fw_api_power_h__ + +/* Power Management Commands, Responses, Notifications */ + +/** + * enum iwl_ltr_config_flags - masks for LTR config command flags + * @LTR_CFG_FLAG_FEATURE_ENABLE: Feature operational status + * @LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS: allow LTR change on shadow + * memory access + * @LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH: allow LTR msg send on ANY LTR + * reg change + * @LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3: allow LTR msg send on transition from + * D0 to D3 + * @LTR_CFG_FLAG_SW_SET_SHORT: fixed static short LTR register + * @LTR_CFG_FLAG_SW_SET_LONG: fixed static short LONG register + * @LTR_CFG_FLAG_DENIE_C10_ON_PD: allow going into C10 on PD + */ +enum iwl_ltr_config_flags { + LTR_CFG_FLAG_FEATURE_ENABLE = BIT(0), + LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS = BIT(1), + LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH = BIT(2), + LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3 = BIT(3), + LTR_CFG_FLAG_SW_SET_SHORT = BIT(4), + LTR_CFG_FLAG_SW_SET_LONG = BIT(5), + LTR_CFG_FLAG_DENIE_C10_ON_PD = BIT(6), +}; + +/** + * struct iwl_ltr_config_cmd_v1 - configures the LTR + * @flags: See %enum iwl_ltr_config_flags + */ +struct iwl_ltr_config_cmd_v1 { + __le32 flags; + __le32 static_long; + __le32 static_short; +} __packed; /* LTR_CAPABLE_API_S_VER_1 */ + +#define LTR_VALID_STATES_NUM 4 + +/** + * struct iwl_ltr_config_cmd - configures the LTR + * @flags: See %enum iwl_ltr_config_flags + * @static_long: + * @static_short: + * @ltr_cfg_values: + * @ltr_short_idle_timeout: + */ +struct iwl_ltr_config_cmd { + __le32 flags; + __le32 static_long; + __le32 static_short; + __le32 ltr_cfg_values[LTR_VALID_STATES_NUM]; + __le32 ltr_short_idle_timeout; +} __packed; /* LTR_CAPABLE_API_S_VER_2 */ + +/* Radio LP RX Energy Threshold measured in dBm */ +#define POWER_LPRX_RSSI_THRESHOLD 75 +#define POWER_LPRX_RSSI_THRESHOLD_MAX 94 +#define POWER_LPRX_RSSI_THRESHOLD_MIN 30 + +/** + * enum iwl_power_flags - masks for power table command flags + * @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off + * receiver and transmitter. '0' - does not allow. + * @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management, + * '1' Driver enables PM (use rest of parameters) + * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM, + * '1' PM could sleep over DTIM till listen Interval. + * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all + * access categories are both delivery and trigger enabled. + * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and + * PBW Snoozing enabled + * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask + * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable. + * @POWER_FLAGS_AP_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving + * detection enablement +*/ +enum iwl_power_flags { + POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), + POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1), + POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2), + POWER_FLAGS_SNOOZE_ENA_MSK = BIT(5), + POWER_FLAGS_BT_SCO_ENA = BIT(8), + POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9), + POWER_FLAGS_LPRX_ENA_MSK = BIT(11), + POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12), +}; + +#define IWL_POWER_VEC_SIZE 5 + +/** + * struct iwl_powertable_cmd - legacy power command. Beside old API support this + * is used also with a new power API for device wide power settings. + * POWER_TABLE_CMD = 0x77 (command, has simple generic response) + * + * @flags: Power table command flags from POWER_FLAGS_* + * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. + * Minimum allowed:- 3 * DTIM. Keep alive period must be + * set regardless of power scheme or current power state. + * FW use this value also when PM is disabled. + * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to + * PSM transition - legacy PM + * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to + * PSM transition - legacy PM + * @sleep_interval: not in use + * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag + * is set. For example, if it is required to skip over + * one DTIM, this value need to be set to 2 (DTIM periods). + * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. + * Default: 80dbm + */ +struct iwl_powertable_cmd { + /* PM_POWER_TABLE_CMD_API_S_VER_6 */ + __le16 flags; + u8 keep_alive_seconds; + u8 debug_flags; + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 sleep_interval[IWL_POWER_VEC_SIZE]; + __le32 skip_dtim_periods; + __le32 lprx_rssi_threshold; +} __packed; + +/** + * enum iwl_device_power_flags - masks for device power command flags + * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off + * receiver and transmitter. '0' - does not allow. +*/ +enum iwl_device_power_flags { + DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), +}; + +/** + * struct iwl_device_power_cmd - device wide power command. + * DEVICE_POWER_CMD = 0x77 (command, has simple generic response) + * + * @flags: Power table command flags from DEVICE_POWER_FLAGS_* + */ +struct iwl_device_power_cmd { + /* PM_POWER_TABLE_CMD_API_S_VER_6 */ + __le16 flags; + __le16 reserved; +} __packed; + +/** + * struct iwl_mac_power_cmd - New power command containing uAPSD support + * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response) + * @id_and_color: MAC contex identifier + * @flags: Power table command flags from POWER_FLAGS_* + * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. + * Minimum allowed:- 3 * DTIM. Keep alive period must be + * set regardless of power scheme or current power state. + * FW use this value also when PM is disabled. + * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to + * PSM transition - legacy PM + * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to + * PSM transition - legacy PM + * @sleep_interval: not in use + * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag + * is set. For example, if it is required to skip over + * one DTIM, this value need to be set to 2 (DTIM periods). + * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to + * PSM transition - uAPSD + * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to + * PSM transition - uAPSD + * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. + * Default: 80dbm + * @num_skip_dtim: Number of DTIMs to skip if Skip over DTIM flag is set + * @snooze_interval: Maximum time between attempts to retrieve buffered data + * from the AP [msec] + * @snooze_window: A window of time in which PBW snoozing insures that all + * packets received. It is also the minimum time from last + * received unicast RX packet, before client stops snoozing + * for data. [msec] + * @snooze_step: TBD + * @qndp_tid: TID client shall use for uAPSD QNDP triggers + * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for + * each corresponding AC. + * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values. + * @uapsd_max_sp: Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct + * values. + * @heavy_tx_thld_packets: TX threshold measured in number of packets + * @heavy_rx_thld_packets: RX threshold measured in number of packets + * @heavy_tx_thld_percentage: TX threshold measured in load's percentage + * @heavy_rx_thld_percentage: RX threshold measured in load's percentage + * @limited_ps_threshold: +*/ +struct iwl_mac_power_cmd { + /* CONTEXT_DESC_API_T_VER_1 */ + __le32 id_and_color; + + /* CLIENT_PM_POWER_TABLE_S_VER_1 */ + __le16 flags; + __le16 keep_alive_seconds; + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 rx_data_timeout_uapsd; + __le32 tx_data_timeout_uapsd; + u8 lprx_rssi_threshold; + u8 skip_dtim_periods; + __le16 snooze_interval; + __le16 snooze_window; + u8 snooze_step; + u8 qndp_tid; + u8 uapsd_ac_flags; + u8 uapsd_max_sp; + u8 heavy_tx_thld_packets; + u8 heavy_rx_thld_packets; + u8 heavy_tx_thld_percentage; + u8 heavy_rx_thld_percentage; + u8 limited_ps_threshold; + u8 reserved; +} __packed; + +/* + * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when + * associated AP is identified as improperly implementing uAPSD protocol. + * PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78 + * @sta_id: index of station in uCode's station table - associated AP ID in + * this context. + */ +struct iwl_uapsd_misbehaving_ap_notif { + __le32 sta_id; + u8 mac_id; + u8 reserved[3]; +} __packed; + +/** + * struct iwl_reduce_tx_power_cmd - TX power reduction command + * REDUCE_TX_POWER_CMD = 0x9f + * @flags: (reserved for future implementation) + * @mac_context_id: id of the mac ctx for which we are reducing TX power. + * @pwr_restriction: TX power restriction in dBms. + */ +struct iwl_reduce_tx_power_cmd { + u8 flags; + u8 mac_context_id; + __le16 pwr_restriction; +} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */ + +enum iwl_dev_tx_power_cmd_mode { + IWL_TX_POWER_MODE_SET_MAC = 0, + IWL_TX_POWER_MODE_SET_DEVICE = 1, + IWL_TX_POWER_MODE_SET_CHAINS = 2, +}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_2 */; + +/** + * struct iwl_dev_tx_power_cmd_v2 - TX power reduction command + * @set_mode: see &enum iwl_dev_tx_power_cmd_mode + * @mac_context_id: id of the mac ctx for which we are reducing TX power. + * @pwr_restriction: TX power restriction in 1/8 dBms. + * @dev_24: device TX power restriction in 1/8 dBms + * @dev_52_low: device TX power restriction upper band - low + * @dev_52_high: device TX power restriction upper band - high + */ +struct iwl_dev_tx_power_cmd_v2 { + __le32 set_mode; + __le32 mac_context_id; + __le16 pwr_restriction; + __le16 dev_24; + __le16 dev_52_low; + __le16 dev_52_high; +} __packed; /* TX_REDUCED_POWER_API_S_VER_2 */ + +#define IWL_NUM_CHAIN_LIMITS 2 +#define IWL_NUM_SUB_BANDS 5 + +/** + * struct iwl_dev_tx_power_cmd - TX power reduction command + * @v2: version 2 of the command, embedded here for easier software handling + * @per_chain_restriction: per chain restrictions + */ +struct iwl_dev_tx_power_cmd { + /* v3 is just an extension of v2 - keep this here */ + struct iwl_dev_tx_power_cmd_v2 v2; + __le16 per_chain_restriction[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS]; +} __packed; /* TX_REDUCED_POWER_API_S_VER_3 */ + +#define IWL_DEV_MAX_TX_POWER 0x7FFF + +/** + * struct iwl_beacon_filter_cmd + * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) + * @id_and_color: MAC contex identifier + * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon + * to driver if delta in Energy values calculated for this and last + * passed beacon is greater than this threshold. Zero value means that + * the Energy change is ignored for beacon filtering, and beacon will + * not be forced to be sent to driver regardless of this delta. Typical + * energy delta 5dB. + * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state. + * Send beacon to driver if delta in Energy values calculated for this + * and last passed beacon is greater than this threshold. Zero value + * means that the Energy change is ignored for beacon filtering while in + * Roaming state, typical energy delta 1dB. + * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values + * calculated for current beacon is less than the threshold, use + * Roaming Energy Delta Threshold, otherwise use normal Energy Delta + * Threshold. Typical energy threshold is -72dBm. + * @bf_temp_threshold: This threshold determines the type of temperature + * filtering (Slow or Fast) that is selected (Units are in Celsuis): + * If the current temperature is above this threshold - Fast filter + * will be used, If the current temperature is below this threshold - + * Slow filter will be used. + * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values + * calculated for this and the last passed beacon is greater than this + * threshold. Zero value means that the temperature change is ignored for + * beacon filtering; beacons will not be forced to be sent to driver + * regardless of whether its temerature has been changed. + * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values + * calculated for this and the last passed beacon is greater than this + * threshold. Zero value means that the temperature change is ignored for + * beacon filtering; beacons will not be forced to be sent to driver + * regardless of whether its temerature has been changed. + * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled. + * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed + * for a specific period of time. Units: Beacons. + * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed + * for a longer period of time then this escape-timeout. Units: Beacons. + * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled. + */ +struct iwl_beacon_filter_cmd { + __le32 bf_energy_delta; + __le32 bf_roaming_energy_delta; + __le32 bf_roaming_state; + __le32 bf_temp_threshold; + __le32 bf_temp_fast_filter; + __le32 bf_temp_slow_filter; + __le32 bf_enable_beacon_filter; + __le32 bf_debug_flag; + __le32 bf_escape_timer; + __le32 ba_escape_timer; + __le32 ba_enable_beacon_abort; +} __packed; + +/* Beacon filtering and beacon abort */ +#define IWL_BF_ENERGY_DELTA_DEFAULT 5 +#define IWL_BF_ENERGY_DELTA_D0I3 20 +#define IWL_BF_ENERGY_DELTA_MAX 255 +#define IWL_BF_ENERGY_DELTA_MIN 0 + +#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1 +#define IWL_BF_ROAMING_ENERGY_DELTA_D0I3 20 +#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255 +#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0 + +#define IWL_BF_ROAMING_STATE_DEFAULT 72 +#define IWL_BF_ROAMING_STATE_D0I3 72 +#define IWL_BF_ROAMING_STATE_MAX 255 +#define IWL_BF_ROAMING_STATE_MIN 0 + +#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112 +#define IWL_BF_TEMP_THRESHOLD_D0I3 112 +#define IWL_BF_TEMP_THRESHOLD_MAX 255 +#define IWL_BF_TEMP_THRESHOLD_MIN 0 + +#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1 +#define IWL_BF_TEMP_FAST_FILTER_D0I3 1 +#define IWL_BF_TEMP_FAST_FILTER_MAX 255 +#define IWL_BF_TEMP_FAST_FILTER_MIN 0 + +#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5 +#define IWL_BF_TEMP_SLOW_FILTER_D0I3 20 +#define IWL_BF_TEMP_SLOW_FILTER_MAX 255 +#define IWL_BF_TEMP_SLOW_FILTER_MIN 0 + +#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1 + +#define IWL_BF_DEBUG_FLAG_DEFAULT 0 +#define IWL_BF_DEBUG_FLAG_D0I3 0 + +#define IWL_BF_ESCAPE_TIMER_DEFAULT 0 +#define IWL_BF_ESCAPE_TIMER_D0I3 0 +#define IWL_BF_ESCAPE_TIMER_MAX 1024 +#define IWL_BF_ESCAPE_TIMER_MIN 0 + +#define IWL_BA_ESCAPE_TIMER_DEFAULT 6 +#define IWL_BA_ESCAPE_TIMER_D0I3 6 +#define IWL_BA_ESCAPE_TIMER_D3 9 +#define IWL_BA_ESCAPE_TIMER_MAX 1024 +#define IWL_BA_ESCAPE_TIMER_MIN 0 + +#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1 + +#define IWL_BF_CMD_CONFIG(mode) \ + .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA ## mode), \ + .bf_roaming_energy_delta = \ + cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA ## mode), \ + .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE ## mode), \ + .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD ## mode), \ + .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER ## mode), \ + .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER ## mode), \ + .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG ## mode), \ + .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER ## mode), \ + .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER ## mode) + +#define IWL_BF_CMD_CONFIG_DEFAULTS IWL_BF_CMD_CONFIG(_DEFAULT) +#define IWL_BF_CMD_CONFIG_D0I3 IWL_BF_CMD_CONFIG(_D0I3) +#endif diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h new file mode 100644 index 000000000000..0f1ea80a55ef --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h @@ -0,0 +1,389 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __fw_api_rs_h__ +#define __fw_api_rs_h__ + +#include "fw-api-mac.h" + +/* + * These serve as indexes into + * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT]; + * TODO: avoid overlap between legacy and HT rates + */ +enum { + IWL_RATE_1M_INDEX = 0, + IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, + IWL_RATE_2M_INDEX, + IWL_RATE_5M_INDEX, + IWL_RATE_11M_INDEX, + IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, + IWL_RATE_6M_INDEX, + IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, + IWL_RATE_MCS_0_INDEX = IWL_RATE_6M_INDEX, + IWL_FIRST_HT_RATE = IWL_RATE_MCS_0_INDEX, + IWL_FIRST_VHT_RATE = IWL_RATE_MCS_0_INDEX, + IWL_RATE_9M_INDEX, + IWL_RATE_12M_INDEX, + IWL_RATE_MCS_1_INDEX = IWL_RATE_12M_INDEX, + IWL_RATE_18M_INDEX, + IWL_RATE_MCS_2_INDEX = IWL_RATE_18M_INDEX, + IWL_RATE_24M_INDEX, + IWL_RATE_MCS_3_INDEX = IWL_RATE_24M_INDEX, + IWL_RATE_36M_INDEX, + IWL_RATE_MCS_4_INDEX = IWL_RATE_36M_INDEX, + IWL_RATE_48M_INDEX, + IWL_RATE_MCS_5_INDEX = IWL_RATE_48M_INDEX, + IWL_RATE_54M_INDEX, + IWL_RATE_MCS_6_INDEX = IWL_RATE_54M_INDEX, + IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX, + IWL_RATE_60M_INDEX, + IWL_RATE_MCS_7_INDEX = IWL_RATE_60M_INDEX, + IWL_LAST_HT_RATE = IWL_RATE_MCS_7_INDEX, + IWL_RATE_MCS_8_INDEX, + IWL_RATE_MCS_9_INDEX, + IWL_LAST_VHT_RATE = IWL_RATE_MCS_9_INDEX, + IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1, + IWL_RATE_COUNT = IWL_LAST_VHT_RATE + 1, +}; + +#define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX) + +/* fw API values for legacy bit rates, both OFDM and CCK */ +enum { + IWL_RATE_6M_PLCP = 13, + IWL_RATE_9M_PLCP = 15, + IWL_RATE_12M_PLCP = 5, + IWL_RATE_18M_PLCP = 7, + IWL_RATE_24M_PLCP = 9, + IWL_RATE_36M_PLCP = 11, + IWL_RATE_48M_PLCP = 1, + IWL_RATE_54M_PLCP = 3, + IWL_RATE_1M_PLCP = 10, + IWL_RATE_2M_PLCP = 20, + IWL_RATE_5M_PLCP = 55, + IWL_RATE_11M_PLCP = 110, + IWL_RATE_INVM_PLCP = -1, +}; + +/* + * rate_n_flags bit fields + * + * The 32-bit value has different layouts in the low 8 bites depending on the + * format. There are three formats, HT, VHT and legacy (11abg, with subformats + * for CCK and OFDM). + * + * High-throughput (HT) rate format + * bit 8 is 1, bit 26 is 0, bit 9 is 0 (OFDM) + * Very High-throughput (VHT) rate format + * bit 8 is 0, bit 26 is 1, bit 9 is 0 (OFDM) + * Legacy OFDM rate format for bits 7:0 + * bit 8 is 0, bit 26 is 0, bit 9 is 0 (OFDM) + * Legacy CCK rate format for bits 7:0: + * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK) + */ + +/* Bit 8: (1) HT format, (0) legacy or VHT format */ +#define RATE_MCS_HT_POS 8 +#define RATE_MCS_HT_MSK (1 << RATE_MCS_HT_POS) + +/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ +#define RATE_MCS_CCK_POS 9 +#define RATE_MCS_CCK_MSK (1 << RATE_MCS_CCK_POS) + +/* Bit 26: (1) VHT format, (0) legacy format in bits 8:0 */ +#define RATE_MCS_VHT_POS 26 +#define RATE_MCS_VHT_MSK (1 << RATE_MCS_VHT_POS) + + +/* + * High-throughput (HT) rate format for bits 7:0 + * + * 2-0: MCS rate base + * 0) 6 Mbps + * 1) 12 Mbps + * 2) 18 Mbps + * 3) 24 Mbps + * 4) 36 Mbps + * 5) 48 Mbps + * 6) 54 Mbps + * 7) 60 Mbps + * 4-3: 0) Single stream (SISO) + * 1) Dual stream (MIMO) + * 2) Triple stream (MIMO) + * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data + * (bits 7-6 are zero) + * + * Together the low 5 bits work out to the MCS index because we don't + * support MCSes above 15/23, and 0-7 have one stream, 8-15 have two + * streams and 16-23 have three streams. We could also support MCS 32 + * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.) + */ +#define RATE_HT_MCS_RATE_CODE_MSK 0x7 +#define RATE_HT_MCS_NSS_POS 3 +#define RATE_HT_MCS_NSS_MSK (3 << RATE_HT_MCS_NSS_POS) + +/* Bit 10: (1) Use Green Field preamble */ +#define RATE_HT_MCS_GF_POS 10 +#define RATE_HT_MCS_GF_MSK (1 << RATE_HT_MCS_GF_POS) + +#define RATE_HT_MCS_INDEX_MSK 0x3f + +/* + * Very High-throughput (VHT) rate format for bits 7:0 + * + * 3-0: VHT MCS (0-9) + * 5-4: number of streams - 1: + * 0) Single stream (SISO) + * 1) Dual stream (MIMO) + * 2) Triple stream (MIMO) + */ + +/* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */ +#define RATE_VHT_MCS_RATE_CODE_MSK 0xf +#define RATE_VHT_MCS_NSS_POS 4 +#define RATE_VHT_MCS_NSS_MSK (3 << RATE_VHT_MCS_NSS_POS) + +/* + * Legacy OFDM rate format for bits 7:0 + * + * 3-0: 0xD) 6 Mbps + * 0xF) 9 Mbps + * 0x5) 12 Mbps + * 0x7) 18 Mbps + * 0x9) 24 Mbps + * 0xB) 36 Mbps + * 0x1) 48 Mbps + * 0x3) 54 Mbps + * (bits 7-4 are 0) + * + * Legacy CCK rate format for bits 7:0: + * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK): + * + * 6-0: 10) 1 Mbps + * 20) 2 Mbps + * 55) 5.5 Mbps + * 110) 11 Mbps + * (bit 7 is 0) + */ +#define RATE_LEGACY_RATE_MSK 0xff + + +/* + * Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz + * 0 and 1 are valid for HT and VHT, 2 and 3 only for VHT + */ +#define RATE_MCS_CHAN_WIDTH_POS 11 +#define RATE_MCS_CHAN_WIDTH_MSK (3 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_20 (0 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_40 (1 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_80 (2 << RATE_MCS_CHAN_WIDTH_POS) +#define RATE_MCS_CHAN_WIDTH_160 (3 << RATE_MCS_CHAN_WIDTH_POS) + +/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ +#define RATE_MCS_SGI_POS 13 +#define RATE_MCS_SGI_MSK (1 << RATE_MCS_SGI_POS) + +/* Bit 14-16: Antenna selection (1) Ant A, (2) Ant B, (4) Ant C */ +#define RATE_MCS_ANT_POS 14 +#define RATE_MCS_ANT_A_MSK (1 << RATE_MCS_ANT_POS) +#define RATE_MCS_ANT_B_MSK (2 << RATE_MCS_ANT_POS) +#define RATE_MCS_ANT_C_MSK (4 << RATE_MCS_ANT_POS) +#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | \ + RATE_MCS_ANT_B_MSK) +#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | \ + RATE_MCS_ANT_C_MSK) +#define RATE_MCS_ANT_MSK RATE_MCS_ANT_ABC_MSK +#define RATE_MCS_ANT_NUM 3 + +/* Bit 17-18: (0) SS, (1) SS*2 */ +#define RATE_MCS_STBC_POS 17 +#define RATE_MCS_HT_STBC_MSK (3 << RATE_MCS_STBC_POS) +#define RATE_MCS_VHT_STBC_MSK (1 << RATE_MCS_STBC_POS) + +/* Bit 19: (0) Beamforming is off, (1) Beamforming is on */ +#define RATE_MCS_BF_POS 19 +#define RATE_MCS_BF_MSK (1 << RATE_MCS_BF_POS) + +/* Bit 20: (0) ZLF is off, (1) ZLF is on */ +#define RATE_MCS_ZLF_POS 20 +#define RATE_MCS_ZLF_MSK (1 << RATE_MCS_ZLF_POS) + +/* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */ +#define RATE_MCS_DUP_POS 24 +#define RATE_MCS_DUP_MSK (3 << RATE_MCS_DUP_POS) + +/* Bit 27: (1) LDPC enabled, (0) LDPC disabled */ +#define RATE_MCS_LDPC_POS 27 +#define RATE_MCS_LDPC_MSK (1 << RATE_MCS_LDPC_POS) + + +/* Link Quality definitions */ + +/* # entries in rate scale table to support Tx retries */ +#define LQ_MAX_RETRY_NUM 16 + +/* Link quality command flags bit fields */ + +/* Bit 0: (0) Don't use RTS (1) Use RTS */ +#define LQ_FLAG_USE_RTS_POS 0 +#define LQ_FLAG_USE_RTS_MSK (1 << LQ_FLAG_USE_RTS_POS) + +/* Bit 1-3: LQ command color. Used to match responses to LQ commands */ +#define LQ_FLAG_COLOR_POS 1 +#define LQ_FLAG_COLOR_MSK (7 << LQ_FLAG_COLOR_POS) + +/* Bit 4-5: Tx RTS BW Signalling + * (0) No RTS BW signalling + * (1) Static BW signalling + * (2) Dynamic BW signalling + */ +#define LQ_FLAG_RTS_BW_SIG_POS 4 +#define LQ_FLAG_RTS_BW_SIG_NONE (0 << LQ_FLAG_RTS_BW_SIG_POS) +#define LQ_FLAG_RTS_BW_SIG_STATIC (1 << LQ_FLAG_RTS_BW_SIG_POS) +#define LQ_FLAG_RTS_BW_SIG_DYNAMIC (2 << LQ_FLAG_RTS_BW_SIG_POS) + +/* Bit 6: (0) No dynamic BW selection (1) Allow dynamic BW selection + * Dyanmic BW selection allows Tx with narrower BW then requested in rates + */ +#define LQ_FLAG_DYNAMIC_BW_POS 6 +#define LQ_FLAG_DYNAMIC_BW_MSK (1 << LQ_FLAG_DYNAMIC_BW_POS) + +/* Single Stream Tx Parameters (lq_cmd->ss_params) + * Flags to control a smart FW decision about whether BFER/STBC/SISO will be + * used for single stream Tx. + */ + +/* Bit 0-1: Max STBC streams allowed. Can be 0-3. + * (0) - No STBC allowed + * (1) - 2x1 STBC allowed (HT/VHT) + * (2) - 4x2 STBC allowed (HT/VHT) + * (3) - 3x2 STBC allowed (HT only) + * All our chips are at most 2 antennas so only (1) is valid for now. + */ +#define LQ_SS_STBC_ALLOWED_POS 0 +#define LQ_SS_STBC_ALLOWED_MSK (3 << LQ_SS_STBC_ALLOWED_MSK) + +/* 2x1 STBC is allowed */ +#define LQ_SS_STBC_1SS_ALLOWED (1 << LQ_SS_STBC_ALLOWED_POS) + +/* Bit 2: Beamformer (VHT only) is allowed */ +#define LQ_SS_BFER_ALLOWED_POS 2 +#define LQ_SS_BFER_ALLOWED (1 << LQ_SS_BFER_ALLOWED_POS) + +/* Bit 3: Force BFER or STBC for testing + * If this is set: + * If BFER is allowed then force the ucode to choose BFER else + * If STBC is allowed then force the ucode to choose STBC over SISO + */ +#define LQ_SS_FORCE_POS 3 +#define LQ_SS_FORCE (1 << LQ_SS_FORCE_POS) + +/* Bit 31: ss_params field is valid. Used for FW backward compatibility + * with other drivers which don't support the ss_params API yet + */ +#define LQ_SS_PARAMS_VALID_POS 31 +#define LQ_SS_PARAMS_VALID (1 << LQ_SS_PARAMS_VALID_POS) + +/** + * struct iwl_lq_cmd - link quality command + * @sta_id: station to update + * @control: not used + * @flags: combination of LQ_FLAG_* + * @mimo_delim: the first SISO index in rs_table, which separates MIMO + * and SISO rates + * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD). + * Should be ANT_[ABC] + * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC] + * @initial_rate_index: first index from rs_table per AC category + * @agg_time_limit: aggregation max time threshold in usec/100, meaning + * value of 100 is one usec. Range is 100 to 8000 + * @agg_disable_start_th: try-count threshold for starting aggregation. + * If a frame has higher try-count, it should not be selected for + * starting an aggregation sequence. + * @agg_frame_cnt_limit: max frame count in an aggregation. + * 0: no limit + * 1: no aggregation (one frame per aggregation) + * 2 - 0x3f: maximal number of frames (up to 3f == 63) + * @rs_table: array of rates for each TX try, each is rate_n_flags, + * meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP + * @ss_params: single stream features. declare whether STBC or BFER are allowed. + */ +struct iwl_lq_cmd { + u8 sta_id; + u8 reduced_tpc; + u16 control; + /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */ + u8 flags; + u8 mimo_delim; + u8 single_stream_ant_msk; + u8 dual_stream_ant_msk; + u8 initial_rate_index[AC_NUM]; + /* LINK_QUAL_AGG_PARAMS_API_S_VER_1 */ + __le16 agg_time_limit; + u8 agg_disable_start_th; + u8 agg_frame_cnt_limit; + __le32 reserved2; + __le32 rs_table[LQ_MAX_RETRY_NUM]; + __le32 ss_params; +}; /* LINK_QUALITY_CMD_API_S_VER_1 */ +#endif /* __fw_api_rs_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h new file mode 100644 index 000000000000..9b7e49d4620f --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -0,0 +1,238 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __fw_api_rx_h__ +#define __fw_api_rx_h__ + +#define IWL_RX_INFO_PHY_CNT 8 +#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1 +#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff +#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00 +#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000 +#define IWL_RX_INFO_ENERGY_ANT_A_POS 0 +#define IWL_RX_INFO_ENERGY_ANT_B_POS 8 +#define IWL_RX_INFO_ENERGY_ANT_C_POS 16 + +/** + * struct iwl_rx_phy_info - phy info + * (REPLY_RX_PHY_CMD = 0xc0) + * @non_cfg_phy_cnt: non configurable DSP phy data byte count + * @cfg_phy_cnt: configurable DSP phy data byte count + * @stat_id: configurable DSP phy data set ID + * @reserved1: + * @system_timestamp: GP2 at on air rise + * @timestamp: TSF at on air rise + * @beacon_time_stamp: beacon at on-air rise + * @phy_flags: general phy flags: band, modulation, ... + * @channel: channel number + * @non_cfg_phy_buf: for various implementations of non_cfg_phy + * @rate_n_flags: RATE_MCS_* + * @byte_count: frame's byte-count + * @frame_time: frame's time on the air, based on byte count and frame rate + * calculation + * @mac_active_msk: what MACs were active when the frame was received + * + * Before each Rx, the device sends this data. It contains PHY information + * about the reception of the packet. + */ +struct iwl_rx_phy_info { + u8 non_cfg_phy_cnt; + u8 cfg_phy_cnt; + u8 stat_id; + u8 reserved1; + __le32 system_timestamp; + __le64 timestamp; + __le32 beacon_time_stamp; + __le16 phy_flags; + __le16 channel; + __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT]; + __le32 rate_n_flags; + __le32 byte_count; + __le16 mac_active_msk; + __le16 frame_time; +} __packed; + +/* + * TCP offload Rx assist info + * + * bits 0:3 - reserved + * bits 4:7 - MIC CRC length + * bits 8:12 - MAC header length + * bit 13 - Padding indication + * bit 14 - A-AMSDU indication + * bit 15 - Offload enabled + */ +enum iwl_csum_rx_assist_info { + CSUM_RXA_RESERVED_MASK = 0x000f, + CSUM_RXA_MICSIZE_MASK = 0x00f0, + CSUM_RXA_HEADERLEN_MASK = 0x1f00, + CSUM_RXA_PADD = BIT(13), + CSUM_RXA_AMSDU = BIT(14), + CSUM_RXA_ENA = BIT(15) +}; + +/** + * struct iwl_rx_mpdu_res_start - phy info + * @assist: see CSUM_RX_ASSIST_ above + */ +struct iwl_rx_mpdu_res_start { + __le16 byte_count; + __le16 assist; +} __packed; /* _RX_MPDU_RES_START_API_S_VER_2 */ + +/** + * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags + * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band + * @RX_RES_PHY_FLAGS_MOD_CCK: + * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short + * @RX_RES_PHY_FLAGS_NARROW_BAND: + * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received + * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU + * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame + * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble + * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame + */ +enum iwl_rx_phy_flags { + RX_RES_PHY_FLAGS_BAND_24 = BIT(0), + RX_RES_PHY_FLAGS_MOD_CCK = BIT(1), + RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2), + RX_RES_PHY_FLAGS_NARROW_BAND = BIT(3), + RX_RES_PHY_FLAGS_ANTENNA = (0x7 << 4), + RX_RES_PHY_FLAGS_ANTENNA_POS = 4, + RX_RES_PHY_FLAGS_AGG = BIT(7), + RX_RES_PHY_FLAGS_OFDM_HT = BIT(8), + RX_RES_PHY_FLAGS_OFDM_GF = BIT(9), + RX_RES_PHY_FLAGS_OFDM_VHT = BIT(10), +}; + +/** + * enum iwl_mvm_rx_status - written by fw for each Rx packet + * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine + * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow + * @RX_MPDU_RES_STATUS_SRC_STA_FOUND: + * @RX_MPDU_RES_STATUS_KEY_VALID: + * @RX_MPDU_RES_STATUS_KEY_PARAM_OK: + * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed + * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked + * in the driver. + * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine + * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR: valid for alg = CCM_CMAC or + * alg = CCM only. Checks replay attack for 11w frames. Relevant only if + * %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set. + * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted + * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP + * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM + * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP + * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC + * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted + * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm + * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted + * @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP: + * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP: + * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT: + * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame + * @RX_MPDU_RES_STATUS_CSUM_DONE: checksum was done by the hw + * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors + * @RX_MPDU_RES_STATUS_HASH_INDEX_MSK: + * @RX_MPDU_RES_STATUS_STA_ID_MSK: + * @RX_MPDU_RES_STATUS_RRF_KILL: + * @RX_MPDU_RES_STATUS_FILTERING_MSK: + * @RX_MPDU_RES_STATUS2_FILTERING_MSK: + */ +enum iwl_mvm_rx_status { + RX_MPDU_RES_STATUS_CRC_OK = BIT(0), + RX_MPDU_RES_STATUS_OVERRUN_OK = BIT(1), + RX_MPDU_RES_STATUS_SRC_STA_FOUND = BIT(2), + RX_MPDU_RES_STATUS_KEY_VALID = BIT(3), + RX_MPDU_RES_STATUS_KEY_PARAM_OK = BIT(4), + RX_MPDU_RES_STATUS_ICV_OK = BIT(5), + RX_MPDU_RES_STATUS_MIC_OK = BIT(6), + RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), + RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR = BIT(7), + RX_MPDU_RES_STATUS_SEC_NO_ENC = (0 << 8), + RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8), + RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8), + RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8), + RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8), + RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8), + RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8), + RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8), + RX_MPDU_RES_STATUS_DEC_DONE = BIT(11), + RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP = BIT(12), + RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP = BIT(13), + RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT = BIT(14), + RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME = BIT(15), + RX_MPDU_RES_STATUS_CSUM_DONE = BIT(16), + RX_MPDU_RES_STATUS_CSUM_OK = BIT(17), + RX_MPDU_RES_STATUS_HASH_INDEX_MSK = (0x3F0000), + RX_MPDU_RES_STATUS_STA_ID_MSK = (0x1f000000), + RX_MPDU_RES_STATUS_RRF_KILL = BIT(29), + RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000), + RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000), +}; + +#endif /* __fw_api_rx_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h new file mode 100644 index 000000000000..3a657e4b60ac --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h @@ -0,0 +1,730 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __fw_api_scan_h__ +#define __fw_api_scan_h__ + +#include "fw-api.h" + +/* Scan Commands, Responses, Notifications */ + +/* Max number of IEs for direct SSID scans in a command */ +#define PROBE_OPTION_MAX 20 + +/** + * struct iwl_ssid_ie - directed scan network information element + * + * Up to 20 of these may appear in REPLY_SCAN_CMD, + * selected by "type" bit field in struct iwl_scan_channel; + * each channel may select different ssids from among the 20 entries. + * SSID IEs get transmitted in reverse order of entry. + */ +struct iwl_ssid_ie { + u8 id; + u8 len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; +} __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */ + +/* scan offload */ +#define IWL_SCAN_MAX_BLACKLIST_LEN 64 +#define IWL_SCAN_SHORT_BLACKLIST_LEN 16 +#define IWL_SCAN_MAX_PROFILES 11 +#define SCAN_OFFLOAD_PROBE_REQ_SIZE 512 + +/* Default watchdog (in MS) for scheduled scan iteration */ +#define IWL_SCHED_SCAN_WATCHDOG cpu_to_le16(15000) + +#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) +#define CAN_ABORT_STATUS 1 + +#define IWL_FULL_SCAN_MULTIPLIER 5 +#define IWL_FAST_SCHED_SCAN_ITERATIONS 3 +#define IWL_MAX_SCHED_SCAN_PLANS 2 + +enum scan_framework_client { + SCAN_CLIENT_SCHED_SCAN = BIT(0), + SCAN_CLIENT_NETDETECT = BIT(1), + SCAN_CLIENT_ASSET_TRACKING = BIT(2), +}; + +/** + * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S + * @ssid: MAC address to filter out + * @reported_rssi: AP rssi reported to the host + * @client_bitmap: clients ignore this entry - enum scan_framework_client + */ +struct iwl_scan_offload_blacklist { + u8 ssid[ETH_ALEN]; + u8 reported_rssi; + u8 client_bitmap; +} __packed; + +enum iwl_scan_offload_network_type { + IWL_NETWORK_TYPE_BSS = 1, + IWL_NETWORK_TYPE_IBSS = 2, + IWL_NETWORK_TYPE_ANY = 3, +}; + +enum iwl_scan_offload_band_selection { + IWL_SCAN_OFFLOAD_SELECT_2_4 = 0x4, + IWL_SCAN_OFFLOAD_SELECT_5_2 = 0x8, + IWL_SCAN_OFFLOAD_SELECT_ANY = 0xc, +}; + +/** + * iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S + * @ssid_index: index to ssid list in fixed part + * @unicast_cipher: encryption algorithm to match - bitmap + * @aut_alg: authentication algorithm to match - bitmap + * @network_type: enum iwl_scan_offload_network_type + * @band_selection: enum iwl_scan_offload_band_selection + * @client_bitmap: clients waiting for match - enum scan_framework_client + */ +struct iwl_scan_offload_profile { + u8 ssid_index; + u8 unicast_cipher; + u8 auth_alg; + u8 network_type; + u8 band_selection; + u8 client_bitmap; + u8 reserved[2]; +} __packed; + +/** + * iwl_scan_offload_profile_cfg - SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1 + * @blaclist: AP list to filter off from scan results + * @profiles: profiles to search for match + * @blacklist_len: length of blacklist + * @num_profiles: num of profiles in the list + * @match_notify: clients waiting for match found notification + * @pass_match: clients waiting for the results + * @active_clients: active clients bitmap - enum scan_framework_client + * @any_beacon_notify: clients waiting for match notification without match + */ +struct iwl_scan_offload_profile_cfg { + struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES]; + u8 blacklist_len; + u8 num_profiles; + u8 match_notify; + u8 pass_match; + u8 active_clients; + u8 any_beacon_notify; + u8 reserved[2]; +} __packed; + +/** + * iwl_scan_schedule_lmac - schedule of scan offload + * @delay: delay between iterations, in seconds. + * @iterations: num of scan iterations + * @full_scan_mul: number of partial scans before each full scan + */ +struct iwl_scan_schedule_lmac { + __le16 delay; + u8 iterations; + u8 full_scan_mul; +} __packed; /* SCAN_SCHEDULE_API_S */ + +enum iwl_scan_offload_complete_status { + IWL_SCAN_OFFLOAD_COMPLETED = 1, + IWL_SCAN_OFFLOAD_ABORTED = 2, +}; + +enum iwl_scan_ebs_status { + IWL_SCAN_EBS_SUCCESS, + IWL_SCAN_EBS_FAILED, + IWL_SCAN_EBS_CHAN_NOT_FOUND, + IWL_SCAN_EBS_INACTIVE, +}; + +/** + * iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S + * @tx_flags: combination of TX_CMD_FLG_* + * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is + * cleared. Combination of RATE_MCS_* + * @sta_id: index of destination station in FW station table + * @reserved: for alignment and future use + */ +struct iwl_scan_req_tx_cmd { + __le32 tx_flags; + __le32 rate_n_flags; + u8 sta_id; + u8 reserved[3]; +} __packed; + +enum iwl_scan_channel_flags_lmac { + IWL_UNIFIED_SCAN_CHANNEL_FULL = BIT(27), + IWL_UNIFIED_SCAN_CHANNEL_PARTIAL = BIT(28), +}; + +/** + * iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2 + * @flags: bits 1-20: directed scan to i'th ssid + * other bits &enum iwl_scan_channel_flags_lmac + * @channel_number: channel number 1-13 etc + * @iter_count: scan iteration on this channel + * @iter_interval: interval in seconds between iterations on one channel + */ +struct iwl_scan_channel_cfg_lmac { + __le32 flags; + __le16 channel_num; + __le16 iter_count; + __le32 iter_interval; +} __packed; + +/* + * iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1 + * @offset: offset in the data block + * @len: length of the segment + */ +struct iwl_scan_probe_segment { + __le16 offset; + __le16 len; +} __packed; + +/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2 + * @mac_header: first (and common) part of the probe + * @band_data: band specific data + * @common_data: last (and common) part of the probe + * @buf: raw data block + */ +struct iwl_scan_probe_req { + struct iwl_scan_probe_segment mac_header; + struct iwl_scan_probe_segment band_data[2]; + struct iwl_scan_probe_segment common_data; + u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE]; +} __packed; + +enum iwl_scan_channel_flags { + IWL_SCAN_CHANNEL_FLAG_EBS = BIT(0), + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE = BIT(1), + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD = BIT(2), +}; + +/* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S + * @flags: enum iwl_scan_channel_flags + * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is + * involved. + * 1 - EBS is disabled. + * 2 - every second scan will be full scan(and so on). + */ +struct iwl_scan_channel_opt { + __le16 flags; + __le16 non_ebs_ratio; +} __packed; + +/** + * iwl_mvm_lmac_scan_flags + * @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses + * without filtering. + * @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels + * @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan + * @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification + * @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching + * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented + * @IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report + * and DS parameter set IEs into probe requests. + * @IWL_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches + */ +enum iwl_mvm_lmac_scan_flags { + IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL = BIT(0), + IWL_MVM_LMAC_SCAN_FLAG_PASSIVE = BIT(1), + IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = BIT(2), + IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = BIT(3), + IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4), + IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5), + IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = BIT(6), + IWL_MVM_LMAC_SCAN_FLAG_MATCH = BIT(9), +}; + +enum iwl_scan_priority { + IWL_SCAN_PRIORITY_LOW, + IWL_SCAN_PRIORITY_MEDIUM, + IWL_SCAN_PRIORITY_HIGH, +}; + +enum iwl_scan_priority_ext { + IWL_SCAN_PRIORITY_EXT_0_LOWEST, + IWL_SCAN_PRIORITY_EXT_1, + IWL_SCAN_PRIORITY_EXT_2, + IWL_SCAN_PRIORITY_EXT_3, + IWL_SCAN_PRIORITY_EXT_4, + IWL_SCAN_PRIORITY_EXT_5, + IWL_SCAN_PRIORITY_EXT_6, + IWL_SCAN_PRIORITY_EXT_7_HIGHEST, +}; + +/** + * iwl_scan_req_lmac - SCAN_REQUEST_CMD_API_S_VER_1 + * @reserved1: for alignment and future use + * @channel_num: num of channels to scan + * @active-dwell: dwell time for active channels + * @passive-dwell: dwell time for passive channels + * @fragmented-dwell: dwell time for fragmented passive scan + * @reserved2: for alignment and future use + * @rx_chain_selct: PHY_RX_CHAIN_* flags + * @scan_flags: &enum iwl_mvm_lmac_scan_flags + * @max_out_time: max time (in TU) to be out of associated channel + * @suspend_time: pause scan this long (TUs) when returning to service channel + * @flags: RXON flags + * @filter_flags: RXON filter + * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz + * @direct_scan: list of SSIDs for directed active scan + * @scan_prio: enum iwl_scan_priority + * @iter_num: number of scan iterations + * @delay: delay in seconds before first iteration + * @schedule: two scheduling plans. The first one is finite, the second one can + * be infinite. + * @channel_opt: channel optimization options, for full and partial scan + * @data: channel configuration and probe request packet. + */ +struct iwl_scan_req_lmac { + /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */ + __le32 reserved1; + u8 n_channels; + u8 active_dwell; + u8 passive_dwell; + u8 fragmented_dwell; + __le16 reserved2; + __le16 rx_chain_select; + __le32 scan_flags; + __le32 max_out_time; + __le32 suspend_time; + /* RX_ON_FLAGS_API_S_VER_1 */ + __le32 flags; + __le32 filter_flags; + struct iwl_scan_req_tx_cmd tx_cmd[2]; + struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; + __le32 scan_prio; + /* SCAN_REQ_PERIODIC_PARAMS_API_S */ + __le32 iter_num; + __le32 delay; + struct iwl_scan_schedule_lmac schedule[IWL_MAX_SCHED_SCAN_PLANS]; + struct iwl_scan_channel_opt channel_opt[2]; + u8 data[]; +} __packed; + +/** + * struct iwl_scan_results_notif - scan results for one channel - + * SCAN_RESULT_NTF_API_S_VER_3 + * @channel: which channel the results are from + * @band: 0 for 5.2 GHz, 1 for 2.4 GHz + * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request + * @num_probe_not_sent: # of request that weren't sent due to not enough time + * @duration: duration spent in channel, in usecs + */ +struct iwl_scan_results_notif { + u8 channel; + u8 band; + u8 probe_status; + u8 num_probe_not_sent; + __le32 duration; +} __packed; + +/** + * struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels) + * SCAN_COMPLETE_NTF_API_S_VER_3 + * @scanned_channels: number of channels scanned (and number of valid results) + * @status: one of SCAN_COMP_STATUS_* + * @bt_status: BT on/off status + * @last_channel: last channel that was scanned + * @tsf_low: TSF timer (lower half) in usecs + * @tsf_high: TSF timer (higher half) in usecs + * @results: an array of scan results, only "scanned_channels" of them are valid + */ +struct iwl_lmac_scan_complete_notif { + u8 scanned_channels; + u8 status; + u8 bt_status; + u8 last_channel; + __le32 tsf_low; + __le32 tsf_high; + struct iwl_scan_results_notif results[]; +} __packed; + +/** + * iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2 + * @last_schedule_line: last schedule line executed (fast or regular) + * @last_schedule_iteration: last scan iteration executed before scan abort + * @status: enum iwl_scan_offload_complete_status + * @ebs_status: EBS success status &enum iwl_scan_ebs_status + * @time_after_last_iter; time in seconds elapsed after last iteration + */ +struct iwl_periodic_scan_complete { + u8 last_schedule_line; + u8 last_schedule_iteration; + u8 status; + u8 ebs_status; + __le32 time_after_last_iter; + __le32 reserved; +} __packed; + +/* UMAC Scan API */ + +/* The maximum of either of these cannot exceed 8, because we use an + * 8-bit mask (see IWL_MVM_SCAN_MASK in mvm.h). + */ +#define IWL_MVM_MAX_UMAC_SCANS 8 +#define IWL_MVM_MAX_LMAC_SCANS 1 + +enum scan_config_flags { + SCAN_CONFIG_FLAG_ACTIVATE = BIT(0), + SCAN_CONFIG_FLAG_DEACTIVATE = BIT(1), + SCAN_CONFIG_FLAG_FORBID_CHUB_REQS = BIT(2), + SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS = BIT(3), + SCAN_CONFIG_FLAG_SET_TX_CHAINS = BIT(8), + SCAN_CONFIG_FLAG_SET_RX_CHAINS = BIT(9), + SCAN_CONFIG_FLAG_SET_AUX_STA_ID = BIT(10), + SCAN_CONFIG_FLAG_SET_ALL_TIMES = BIT(11), + SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES = BIT(12), + SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS = BIT(13), + SCAN_CONFIG_FLAG_SET_LEGACY_RATES = BIT(14), + SCAN_CONFIG_FLAG_SET_MAC_ADDR = BIT(15), + SCAN_CONFIG_FLAG_SET_FRAGMENTED = BIT(16), + SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED = BIT(17), + SCAN_CONFIG_FLAG_SET_CAM_MODE = BIT(18), + SCAN_CONFIG_FLAG_CLEAR_CAM_MODE = BIT(19), + SCAN_CONFIG_FLAG_SET_PROMISC_MODE = BIT(20), + SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE = BIT(21), + + /* Bits 26-31 are for num of channels in channel_array */ +#define SCAN_CONFIG_N_CHANNELS(n) ((n) << 26) +}; + +enum scan_config_rates { + /* OFDM basic rates */ + SCAN_CONFIG_RATE_6M = BIT(0), + SCAN_CONFIG_RATE_9M = BIT(1), + SCAN_CONFIG_RATE_12M = BIT(2), + SCAN_CONFIG_RATE_18M = BIT(3), + SCAN_CONFIG_RATE_24M = BIT(4), + SCAN_CONFIG_RATE_36M = BIT(5), + SCAN_CONFIG_RATE_48M = BIT(6), + SCAN_CONFIG_RATE_54M = BIT(7), + /* CCK basic rates */ + SCAN_CONFIG_RATE_1M = BIT(8), + SCAN_CONFIG_RATE_2M = BIT(9), + SCAN_CONFIG_RATE_5M = BIT(10), + SCAN_CONFIG_RATE_11M = BIT(11), + + /* Bits 16-27 are for supported rates */ +#define SCAN_CONFIG_SUPPORTED_RATE(rate) ((rate) << 16) +}; + +enum iwl_channel_flags { + IWL_CHANNEL_FLAG_EBS = BIT(0), + IWL_CHANNEL_FLAG_ACCURATE_EBS = BIT(1), + IWL_CHANNEL_FLAG_EBS_ADD = BIT(2), + IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE = BIT(3), +}; + +/** + * struct iwl_scan_config + * @flags: enum scan_config_flags + * @tx_chains: valid_tx antenna - ANT_* definitions + * @rx_chains: valid_rx antenna - ANT_* definitions + * @legacy_rates: default legacy rates - enum scan_config_rates + * @out_of_channel_time: default max out of serving channel time + * @suspend_time: default max suspend time + * @dwell_active: default dwell time for active scan + * @dwell_passive: default dwell time for passive scan + * @dwell_fragmented: default dwell time for fragmented scan + * @reserved: for future use and alignment + * @mac_addr: default mac address to be used in probes + * @bcast_sta_id: the index of the station in the fw + * @channel_flags: default channel flags - enum iwl_channel_flags + * scan_config_channel_flag + * @channel_array: default supported channels + */ +struct iwl_scan_config { + __le32 flags; + __le32 tx_chains; + __le32 rx_chains; + __le32 legacy_rates; + __le32 out_of_channel_time; + __le32 suspend_time; + u8 dwell_active; + u8 dwell_passive; + u8 dwell_fragmented; + u8 reserved; + u8 mac_addr[ETH_ALEN]; + u8 bcast_sta_id; + u8 channel_flags; + u8 channel_array[]; +} __packed; /* SCAN_CONFIG_DB_CMD_API_S */ + +/** + * iwl_umac_scan_flags + *@IWL_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request + * can be preempted by other scan requests with higher priority. + * The low priority scan will be resumed when the higher proirity scan is + * completed. + *@IWL_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver + * when scan starts. + */ +enum iwl_umac_scan_flags { + IWL_UMAC_SCAN_FLAG_PREEMPTIVE = BIT(0), + IWL_UMAC_SCAN_FLAG_START_NOTIF = BIT(1), +}; + +enum iwl_umac_scan_uid_offsets { + IWL_UMAC_SCAN_UID_TYPE_OFFSET = 0, + IWL_UMAC_SCAN_UID_SEQ_OFFSET = 8, +}; + +enum iwl_umac_scan_general_flags { + IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC = BIT(0), + IWL_UMAC_SCAN_GEN_FLAGS_OVER_BT = BIT(1), + IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL = BIT(2), + IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE = BIT(3), + IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT = BIT(4), + IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE = BIT(5), + IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = BIT(6), + IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = BIT(7), + IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = BIT(8), + IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9) +}; + +/** + * struct iwl_scan_channel_cfg_umac + * @flags: bitmap - 0-19: directed scan to i'th ssid. + * @channel_num: channel number 1-13 etc. + * @iter_count: repetition count for the channel. + * @iter_interval: interval between two scan iterations on one channel. + */ +struct iwl_scan_channel_cfg_umac { + __le32 flags; + u8 channel_num; + u8 iter_count; + __le16 iter_interval; +} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */ + +/** + * struct iwl_scan_umac_schedule + * @interval: interval in seconds between scan iterations + * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop + * @reserved: for alignment and future use + */ +struct iwl_scan_umac_schedule { + __le16 interval; + u8 iter_count; + u8 reserved; +} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */ + +/** + * struct iwl_scan_req_umac_tail - the rest of the UMAC scan request command + * parameters following channels configuration array. + * @schedule: two scheduling plans. + * @delay: delay in TUs before starting the first scan iteration + * @reserved: for future use and alignment + * @preq: probe request with IEs blocks + * @direct_scan: list of SSIDs for directed active scan + */ +struct iwl_scan_req_umac_tail { + /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */ + struct iwl_scan_umac_schedule schedule[IWL_MAX_SCHED_SCAN_PLANS]; + __le16 delay; + __le16 reserved; + /* SCAN_PROBE_PARAMS_API_S_VER_1 */ + struct iwl_scan_probe_req preq; + struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; +} __packed; + +/** + * struct iwl_scan_req_umac + * @flags: &enum iwl_umac_scan_flags + * @uid: scan id, &enum iwl_umac_scan_uid_offsets + * @ooc_priority: out of channel priority - &enum iwl_scan_priority + * @general_flags: &enum iwl_umac_scan_general_flags + * @reserved1: for future use and alignment + * @active_dwell: dwell time for active scan + * @passive_dwell: dwell time for passive scan + * @fragmented_dwell: dwell time for fragmented passive scan + * @max_out_time: max out of serving channel time + * @suspend_time: max suspend time + * @scan_priority: scan internal prioritization &enum iwl_scan_priority + * @channel_flags: &enum iwl_scan_channel_flags + * @n_channels: num of channels in scan request + * @reserved2: for future use and alignment + * @data: &struct iwl_scan_channel_cfg_umac and + * &struct iwl_scan_req_umac_tail + */ +struct iwl_scan_req_umac { + __le32 flags; + __le32 uid; + __le32 ooc_priority; + /* SCAN_GENERAL_PARAMS_API_S_VER_1 */ + __le32 general_flags; + u8 reserved1; + u8 active_dwell; + u8 passive_dwell; + u8 fragmented_dwell; + __le32 max_out_time; + __le32 suspend_time; + __le32 scan_priority; + /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */ + u8 channel_flags; + u8 n_channels; + __le16 reserved2; + u8 data[]; +} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ + +/** + * struct iwl_umac_scan_abort + * @uid: scan id, &enum iwl_umac_scan_uid_offsets + * @flags: reserved + */ +struct iwl_umac_scan_abort { + __le32 uid; + __le32 flags; +} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */ + +/** + * struct iwl_umac_scan_complete + * @uid: scan id, &enum iwl_umac_scan_uid_offsets + * @last_schedule: last scheduling line + * @last_iter: last scan iteration number + * @scan status: &enum iwl_scan_offload_complete_status + * @ebs_status: &enum iwl_scan_ebs_status + * @time_from_last_iter: time elapsed from last iteration + * @reserved: for future use + */ +struct iwl_umac_scan_complete { + __le32 uid; + u8 last_schedule; + u8 last_iter; + u8 status; + u8 ebs_status; + __le32 time_from_last_iter; + __le32 reserved; +} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */ + +#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5 +/** + * struct iwl_scan_offload_profile_match - match information + * @bssid: matched bssid + * @channel: channel where the match occurred + * @energy: + * @matching_feature: + * @matching_channels: bitmap of channels that matched, referencing + * the channels passed in tue scan offload request + */ +struct iwl_scan_offload_profile_match { + u8 bssid[ETH_ALEN]; + __le16 reserved; + u8 channel; + u8 energy; + u8 matching_feature; + u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN]; +} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */ + +/** + * struct iwl_scan_offload_profiles_query - match results query response + * @matched_profiles: bitmap of matched profiles, referencing the + * matches passed in the scan offload request + * @last_scan_age: age of the last offloaded scan + * @n_scans_done: number of offloaded scans done + * @gp2_d0u: GP2 when D0U occurred + * @gp2_invoked: GP2 when scan offload was invoked + * @resume_while_scanning: not used + * @self_recovery: obsolete + * @reserved: reserved + * @matches: array of match information, one for each match + */ +struct iwl_scan_offload_profiles_query { + __le32 matched_profiles; + __le32 last_scan_age; + __le32 n_scans_done; + __le32 gp2_d0u; + __le32 gp2_invoked; + u8 resume_while_scanning; + u8 self_recovery; + __le16 reserved; + struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES]; +} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */ + +/** + * struct iwl_umac_scan_iter_complete_notif - notifies end of scanning iteration + * @uid: scan id, &enum iwl_umac_scan_uid_offsets + * @scanned_channels: number of channels scanned and number of valid elements in + * results array + * @status: one of SCAN_COMP_STATUS_* + * @bt_status: BT on/off status + * @last_channel: last channel that was scanned + * @tsf_low: TSF timer (lower half) in usecs + * @tsf_high: TSF timer (higher half) in usecs + * @results: array of scan results, only "scanned_channels" of them are valid + */ +struct iwl_umac_scan_iter_complete_notif { + __le32 uid; + u8 scanned_channels; + u8 status; + u8 bt_status; + u8 last_channel; + __le32 tsf_low; + __le32 tsf_high; + struct iwl_scan_results_notif results[]; +} __packed; /* SCAN_ITER_COMPLETE_NTF_UMAC_API_S_VER_1 */ + +#endif diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h new file mode 100644 index 000000000000..493a8bdfbc9e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h @@ -0,0 +1,414 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __fw_api_sta_h__ +#define __fw_api_sta_h__ + +/** + * enum iwl_sta_flags - flags for the ADD_STA host command + * @STA_FLG_REDUCED_TX_PWR_CTRL: + * @STA_FLG_REDUCED_TX_PWR_DATA: + * @STA_FLG_DISABLE_TX: set if TX should be disabled + * @STA_FLG_PS: set if STA is in Power Save + * @STA_FLG_INVALID: set if STA is invalid + * @STA_FLG_DLP_EN: Direct Link Protocol is enabled + * @STA_FLG_SET_ALL_KEYS: the current key applies to all key IDs + * @STA_FLG_DRAIN_FLOW: drain flow + * @STA_FLG_PAN: STA is for PAN interface + * @STA_FLG_CLASS_AUTH: + * @STA_FLG_CLASS_ASSOC: + * @STA_FLG_CLASS_MIMO_PROT: + * @STA_FLG_MAX_AGG_SIZE_MSK: maximal size for A-MPDU + * @STA_FLG_AGG_MPDU_DENS_MSK: maximal MPDU density for Tx aggregation + * @STA_FLG_FAT_EN_MSK: support for channel width (for Tx). This flag is + * initialised by driver and can be updated by fw upon reception of + * action frames that can change the channel width. When cleared the fw + * will send all the frames in 20MHz even when FAT channel is requested. + * @STA_FLG_MIMO_EN_MSK: support for MIMO. This flag is initialised by the + * driver and can be updated by fw upon reception of action frames. + * @STA_FLG_MFP_EN: Management Frame Protection + */ +enum iwl_sta_flags { + STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3), + STA_FLG_REDUCED_TX_PWR_DATA = BIT(6), + + STA_FLG_DISABLE_TX = BIT(4), + + STA_FLG_PS = BIT(8), + STA_FLG_DRAIN_FLOW = BIT(12), + STA_FLG_PAN = BIT(13), + STA_FLG_CLASS_AUTH = BIT(14), + STA_FLG_CLASS_ASSOC = BIT(15), + STA_FLG_RTS_MIMO_PROT = BIT(17), + + STA_FLG_MAX_AGG_SIZE_SHIFT = 19, + STA_FLG_MAX_AGG_SIZE_8K = (0 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_16K = (1 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_32K = (2 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_64K = (3 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_128K = (4 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_256K = (5 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_512K = (6 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_1024K = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), + STA_FLG_MAX_AGG_SIZE_MSK = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), + + STA_FLG_AGG_MPDU_DENS_SHIFT = 23, + STA_FLG_AGG_MPDU_DENS_2US = (4 << STA_FLG_AGG_MPDU_DENS_SHIFT), + STA_FLG_AGG_MPDU_DENS_4US = (5 << STA_FLG_AGG_MPDU_DENS_SHIFT), + STA_FLG_AGG_MPDU_DENS_8US = (6 << STA_FLG_AGG_MPDU_DENS_SHIFT), + STA_FLG_AGG_MPDU_DENS_16US = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT), + STA_FLG_AGG_MPDU_DENS_MSK = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT), + + STA_FLG_FAT_EN_20MHZ = (0 << 26), + STA_FLG_FAT_EN_40MHZ = (1 << 26), + STA_FLG_FAT_EN_80MHZ = (2 << 26), + STA_FLG_FAT_EN_160MHZ = (3 << 26), + STA_FLG_FAT_EN_MSK = (3 << 26), + + STA_FLG_MIMO_EN_SISO = (0 << 28), + STA_FLG_MIMO_EN_MIMO2 = (1 << 28), + STA_FLG_MIMO_EN_MIMO3 = (2 << 28), + STA_FLG_MIMO_EN_MSK = (3 << 28), +}; + +/** + * enum iwl_sta_key_flag - key flags for the ADD_STA host command + * @STA_KEY_FLG_NO_ENC: no encryption + * @STA_KEY_FLG_WEP: WEP encryption algorithm + * @STA_KEY_FLG_CCM: CCMP encryption algorithm + * @STA_KEY_FLG_TKIP: TKIP encryption algorithm + * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support) + * @STA_KEY_FLG_CMAC: CMAC encryption algorithm + * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm + * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value + * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from + * station info array (1 - n 1X mode) + * @STA_KEY_FLG_KEYID_MSK: the index of the key + * @STA_KEY_NOT_VALID: key is invalid + * @STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key + * @STA_KEY_MULTICAST: set for multical key + * @STA_KEY_MFP: key is used for Management Frame Protection + */ +enum iwl_sta_key_flag { + STA_KEY_FLG_NO_ENC = (0 << 0), + STA_KEY_FLG_WEP = (1 << 0), + STA_KEY_FLG_CCM = (2 << 0), + STA_KEY_FLG_TKIP = (3 << 0), + STA_KEY_FLG_EXT = (4 << 0), + STA_KEY_FLG_CMAC = (6 << 0), + STA_KEY_FLG_ENC_UNKNOWN = (7 << 0), + STA_KEY_FLG_EN_MSK = (7 << 0), + + STA_KEY_FLG_WEP_KEY_MAP = BIT(3), + STA_KEY_FLG_KEYID_POS = 8, + STA_KEY_FLG_KEYID_MSK = (3 << STA_KEY_FLG_KEYID_POS), + STA_KEY_NOT_VALID = BIT(11), + STA_KEY_FLG_WEP_13BYTES = BIT(12), + STA_KEY_MULTICAST = BIT(14), + STA_KEY_MFP = BIT(15), +}; + +/** + * enum iwl_sta_modify_flag - indicate to the fw what flag are being changed + * @STA_MODIFY_KEY: this command modifies %key + * @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx + * @STA_MODIFY_TX_RATE: unused + * @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid + * @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid + * @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count + * @STA_MODIFY_PROT_TH: + * @STA_MODIFY_QUEUES: modify the queues used by this station + */ +enum iwl_sta_modify_flag { + STA_MODIFY_KEY = BIT(0), + STA_MODIFY_TID_DISABLE_TX = BIT(1), + STA_MODIFY_TX_RATE = BIT(2), + STA_MODIFY_ADD_BA_TID = BIT(3), + STA_MODIFY_REMOVE_BA_TID = BIT(4), + STA_MODIFY_SLEEPING_STA_TX_COUNT = BIT(5), + STA_MODIFY_PROT_TH = BIT(6), + STA_MODIFY_QUEUES = BIT(7), +}; + +#define STA_MODE_MODIFY 1 + +/** + * enum iwl_sta_sleep_flag - type of sleep of the station + * @STA_SLEEP_STATE_AWAKE: + * @STA_SLEEP_STATE_PS_POLL: + * @STA_SLEEP_STATE_UAPSD: + * @STA_SLEEP_STATE_MOREDATA: set more-data bit on + * (last) released frame + */ +enum iwl_sta_sleep_flag { + STA_SLEEP_STATE_AWAKE = 0, + STA_SLEEP_STATE_PS_POLL = BIT(0), + STA_SLEEP_STATE_UAPSD = BIT(1), + STA_SLEEP_STATE_MOREDATA = BIT(2), +}; + +/* STA ID and color bits definitions */ +#define STA_ID_SEED (0x0f) +#define STA_ID_POS (0) +#define STA_ID_MSK (STA_ID_SEED << STA_ID_POS) + +#define STA_COLOR_SEED (0x7) +#define STA_COLOR_POS (4) +#define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS) + +#define STA_ID_N_COLOR_GET_COLOR(id_n_color) \ + (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS) +#define STA_ID_N_COLOR_GET_ID(id_n_color) \ + (((id_n_color) & STA_ID_MSK) >> STA_ID_POS) + +#define STA_KEY_MAX_NUM (16) +#define STA_KEY_IDX_INVALID (0xff) +#define STA_KEY_MAX_DATA_KEY_NUM (4) +#define IWL_MAX_GLOBAL_KEYS (4) +#define STA_KEY_LEN_WEP40 (5) +#define STA_KEY_LEN_WEP104 (13) + +/** + * struct iwl_mvm_keyinfo - key information + * @key_flags: type %iwl_sta_key_flag + * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection + * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx + * @key_offset: key offset in the fw's key table + * @key: 16-byte unicast decryption key + * @tx_secur_seq_cnt: initial RSC / PN needed for replay check + * @hw_tkip_mic_rx_key: byte: MIC Rx Key - used for TKIP only + * @hw_tkip_mic_tx_key: byte: MIC Tx Key - used for TKIP only + */ +struct iwl_mvm_keyinfo { + __le16 key_flags; + u8 tkip_rx_tsc_byte2; + u8 reserved1; + __le16 tkip_rx_ttak[5]; + u8 key_offset; + u8 reserved2; + u8 key[16]; + __le64 tx_secur_seq_cnt; + __le64 hw_tkip_mic_rx_key; + __le64 hw_tkip_mic_tx_key; +} __packed; + +/** + * struct iwl_mvm_add_sta_cmd - Add/modify a station in the fw's sta table. + * ( REPLY_ADD_STA = 0x18 ) + * @add_modify: 1: modify existing, 0: add new station + * @awake_acs: + * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable + * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field. + * @mac_id_n_color: the Mac context this station belongs to + * @addr[ETH_ALEN]: station's MAC address + * @sta_id: index of station in uCode's station table + * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave + * alone. 1 - modify, 0 - don't change. + * @station_flags: look at %iwl_sta_flags + * @station_flags_msk: what of %station_flags have changed + * @add_immediate_ba_tid: tid for which to add block-ack support (Rx) + * Set %STA_MODIFY_ADD_BA_TID to use this field, and also set + * add_immediate_ba_ssn. + * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx) + * Set %STA_MODIFY_REMOVE_BA_TID to use this field + * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with + * add_immediate_ba_tid. + * @sleep_tx_count: number of packets to transmit to station even though it is + * asleep. Used to synchronise PS-poll and u-APSD responses while ucode + * keeps track of STA sleep state. + * @sleep_state_flags: Look at %iwl_sta_sleep_flag. + * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP + * mac-addr. + * @beamform_flags: beam forming controls + * @tfd_queue_msk: tfd queues used by this station + * + * The device contains an internal table of per-station information, with info + * on security keys, aggregation parameters, and Tx rates for initial Tx + * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD). + * + * ADD_STA sets up the table entry for one station, either creating a new + * entry, or modifying a pre-existing one. + */ +struct iwl_mvm_add_sta_cmd { + u8 add_modify; + u8 awake_acs; + __le16 tid_disable_tx; + __le32 mac_id_n_color; + u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ + __le16 reserved2; + u8 sta_id; + u8 modify_mask; + __le16 reserved3; + __le32 station_flags; + __le32 station_flags_msk; + u8 add_immediate_ba_tid; + u8 remove_immediate_ba_tid; + __le16 add_immediate_ba_ssn; + __le16 sleep_tx_count; + __le16 sleep_state_flags; + __le16 assoc_id; + __le16 beamform_flags; + __le32 tfd_queue_msk; +} __packed; /* ADD_STA_CMD_API_S_VER_7 */ + +/** + * struct iwl_mvm_add_sta_key_cmd - add/modify sta key + * ( REPLY_ADD_STA_KEY = 0x17 ) + * @sta_id: index of station in uCode's station table + * @key_offset: key offset in key storage + * @key_flags: type %iwl_sta_key_flag + * @key: key material data + * @key2: key material data + * @rx_secur_seq_cnt: RX security sequence counter for the key + * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection + * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx + */ +struct iwl_mvm_add_sta_key_cmd { + u8 sta_id; + u8 key_offset; + __le16 key_flags; + u8 key[16]; + u8 key2[16]; + u8 rx_secur_seq_cnt[16]; + u8 tkip_rx_tsc_byte2; + u8 reserved; + __le16 tkip_rx_ttak[5]; +} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */ + +/** + * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command + * @ADD_STA_SUCCESS: operation was executed successfully + * @ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table + * @ADD_STA_IMMEDIATE_BA_FAILURE: can't add Rx block ack session + * @ADD_STA_MODIFY_NON_EXISTING_STA: driver requested to modify a station that + * doesn't exist. + */ +enum iwl_mvm_add_sta_rsp_status { + ADD_STA_SUCCESS = 0x1, + ADD_STA_STATIONS_OVERLOAD = 0x2, + ADD_STA_IMMEDIATE_BA_FAILURE = 0x4, + ADD_STA_MODIFY_NON_EXISTING_STA = 0x8, +}; + +/** + * struct iwl_mvm_rm_sta_cmd - Add / modify a station in the fw's station table + * ( REMOVE_STA = 0x19 ) + * @sta_id: the station id of the station to be removed + */ +struct iwl_mvm_rm_sta_cmd { + u8 sta_id; + u8 reserved[3]; +} __packed; /* REMOVE_STA_CMD_API_S_VER_2 */ + +/** + * struct iwl_mvm_mgmt_mcast_key_cmd + * ( MGMT_MCAST_KEY = 0x1f ) + * @ctrl_flags: %iwl_sta_key_flag + * @IGTK: + * @K1: unused + * @K2: unused + * @sta_id: station ID that support IGTK + * @key_id: + * @receive_seq_cnt: initial RSC/PN needed for replay check + */ +struct iwl_mvm_mgmt_mcast_key_cmd { + __le32 ctrl_flags; + u8 IGTK[16]; + u8 K1[16]; + u8 K2[16]; + __le32 key_id; + __le32 sta_id; + __le64 receive_seq_cnt; +} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */ + +struct iwl_mvm_wep_key { + u8 key_index; + u8 key_offset; + __le16 reserved1; + u8 key_size; + u8 reserved2[3]; + u8 key[16]; +} __packed; + +struct iwl_mvm_wep_key_cmd { + __le32 mac_id_n_color; + u8 num_keys; + u8 decryption_type; + u8 flags; + u8 reserved; + struct iwl_mvm_wep_key wep_key[0]; +} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */ + +/** + * struct iwl_mvm_eosp_notification - EOSP notification from firmware + * @remain_frame_count: # of frames remaining, non-zero if SP was cut + * short by GO absence + * @sta_id: station ID + */ +struct iwl_mvm_eosp_notification { + __le32 remain_frame_count; + __le32 sta_id; +} __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */ + +#endif /* __fw_api_sta_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h new file mode 100644 index 000000000000..0c321f63ee42 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h @@ -0,0 +1,284 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __fw_api_stats_h__ +#define __fw_api_stats_h__ +#include "fw-api-mac.h" + +struct mvm_statistics_dbg { + __le32 burst_check; + __le32 burst_count; + __le32 wait_for_silence_timeout_cnt; + __le32 reserved[3]; +} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */ + +struct mvm_statistics_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; + __le32 rssi_ant; + __le32 reserved2; +} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */ + +struct mvm_statistics_rx_non_phy { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ + __le32 channel_beacons; /* beacons with our bss id and in our + * serving channel */ + __le32 num_missed_bcon; /* number of missed beacons */ + __le32 adc_rx_saturation_time; /* count in 0.8us units the time the + * ADC was in saturation */ + __le32 ina_detection_search_time;/* total time (in 0.8us) searched + * for INA */ + __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ + __le32 interference_data_flag; /* flag for interference data + * availability. 1 when data is + * available. */ + __le32 channel_load; /* counts RX Enable time in uSec */ + __le32 dsp_false_alarms; /* DSP false alarm (both OFDM + * and CCK) counter */ + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 beacon_rssi_c; + __le32 beacon_energy_a; + __le32 beacon_energy_b; + __le32 beacon_energy_c; + __le32 num_bt_kills; + __le32 mac_id; + __le32 directed_data_mpdu; +} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */ + +struct mvm_statistics_rx_phy { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_lmt_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 mh_format_err; + __le32 re_acq_main_rssi_sum; + __le32 reserved; +} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */ + +struct mvm_statistics_rx_ht_phy { + __le32 plcp_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 crc32_err; + __le32 mh_format_err; + __le32 agg_crc32_good; + __le32 agg_mpdu_cnt; + __le32 agg_cnt; + __le32 unsupport_mcs; +} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */ + +struct mvm_statistics_tx_non_phy { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; + __le32 dump_msdu_cnt; + __le32 burst_abort_next_frame_mismatch_cnt; + __le32 burst_abort_missing_next_frame_cnt; + __le32 cts_timeout_collision; + __le32 ack_or_ba_timeout_collision; +} __packed; /* STATISTICS_TX_NON_PHY_API_S_VER_3 */ + +#define MAX_CHAINS 3 + +struct mvm_statistics_tx_non_phy_agg { + __le32 ba_timeout; + __le32 ba_reschedule_frames; + __le32 scd_query_agg_frame_cnt; + __le32 scd_query_no_agg; + __le32 scd_query_agg; + __le32 scd_query_mismatch; + __le32 frame_not_ready; + __le32 underrun; + __le32 bt_prio_kill; + __le32 rx_ba_rsp_cnt; + __s8 txpower[MAX_CHAINS]; + __s8 reserved; + __le32 reserved2; +} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */ + +struct mvm_statistics_tx_channel_width { + __le32 ext_cca_narrow_ch20[1]; + __le32 ext_cca_narrow_ch40[2]; + __le32 ext_cca_narrow_ch80[3]; + __le32 ext_cca_narrow_ch160[4]; + __le32 last_tx_ch_width_indx; + __le32 rx_detected_per_ch_width[4]; + __le32 success_per_ch_width[4]; + __le32 fail_per_ch_width[4]; +}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */ + +struct mvm_statistics_tx { + struct mvm_statistics_tx_non_phy general; + struct mvm_statistics_tx_non_phy_agg agg; + struct mvm_statistics_tx_channel_width channel_width; +} __packed; /* STATISTICS_TX_API_S_VER_4 */ + + +struct mvm_statistics_bt_activity { + __le32 hi_priority_tx_req_cnt; + __le32 hi_priority_tx_denied_cnt; + __le32 lo_priority_tx_req_cnt; + __le32 lo_priority_tx_denied_cnt; + __le32 hi_priority_rx_req_cnt; + __le32 hi_priority_rx_denied_cnt; + __le32 lo_priority_rx_req_cnt; + __le32 lo_priority_rx_denied_cnt; +} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ + +struct mvm_statistics_general_v8 { + __le32 radio_temperature; + __le32 radio_voltage; + struct mvm_statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct mvm_statistics_div slow_div; + __le32 rx_enable_counter; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; + __le32 beacon_filtered; + __le32 missed_beacons; + u8 beacon_filter_average_energy; + u8 beacon_filter_reason; + u8 beacon_filter_current_energy; + u8 beacon_filter_reserved; + __le32 beacon_filter_delta_time; + struct mvm_statistics_bt_activity bt_activity; + __le64 rx_time; + __le64 on_time_rf; + __le64 on_time_scan; + __le64 tx_time; + __le32 beacon_counter[NUM_MAC_INDEX]; + u8 beacon_average_energy[NUM_MAC_INDEX]; + u8 reserved[4 - (NUM_MAC_INDEX % 4)]; +} __packed; /* STATISTICS_GENERAL_API_S_VER_8 */ + +struct mvm_statistics_rx { + struct mvm_statistics_rx_phy ofdm; + struct mvm_statistics_rx_phy cck; + struct mvm_statistics_rx_non_phy general; + struct mvm_statistics_rx_ht_phy ofdm_ht; +} __packed; /* STATISTICS_RX_API_S_VER_3 */ + +/* + * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) + * + * By default, uCode issues this notification after receiving a beacon + * while associated. To disable this behavior, set DISABLE_NOTIF flag in the + * STATISTICS_CMD (0x9c), below. + */ + +struct iwl_notif_statistics_v10 { + __le32 flag; + struct mvm_statistics_rx rx; + struct mvm_statistics_tx tx; + struct mvm_statistics_general_v8 general; +} __packed; /* STATISTICS_NTFY_API_S_VER_10 */ + +#define IWL_STATISTICS_FLG_CLEAR 0x1 +#define IWL_STATISTICS_FLG_DISABLE_NOTIF 0x2 + +struct iwl_statistics_cmd { + __le32 flags; +} __packed; /* STATISTICS_CMD_API_S_VER_1 */ + +#endif /* __fw_api_stats_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h new file mode 100644 index 000000000000..eed6271d01a3 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h @@ -0,0 +1,386 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __fw_api_tof_h__ +#define __fw_api_tof_h__ + +#include "fw-api.h" + +/* ToF sub-group command IDs */ +enum iwl_mvm_tof_sub_grp_ids { + TOF_RANGE_REQ_CMD = 0x1, + TOF_CONFIG_CMD = 0x2, + TOF_RANGE_ABORT_CMD = 0x3, + TOF_RANGE_REQ_EXT_CMD = 0x4, + TOF_RESPONDER_CONFIG_CMD = 0x5, + TOF_NW_INITIATED_RES_SEND_CMD = 0x6, + TOF_NEIGHBOR_REPORT_REQ_CMD = 0x7, + TOF_NEIGHBOR_REPORT_RSP_NOTIF = 0xFC, + TOF_NW_INITIATED_REQ_RCVD_NOTIF = 0xFD, + TOF_RANGE_RESPONSE_NOTIF = 0xFE, + TOF_MCSI_DEBUG_NOTIF = 0xFB, +}; + +/** + * struct iwl_tof_config_cmd - ToF configuration + * @tof_disabled: 0 enabled, 1 - disabled + * @one_sided_disabled: 0 enabled, 1 - disabled + * @is_debug_mode: 1 debug mode, 0 - otherwise + * @is_buf_required: 1 channel estimation buffer required, 0 - otherwise + */ +struct iwl_tof_config_cmd { + __le32 sub_grp_cmd_id; + u8 tof_disabled; + u8 one_sided_disabled; + u8 is_debug_mode; + u8 is_buf_required; +} __packed; + +/** + * struct iwl_tof_responder_config_cmd - ToF AP mode (for debug) + * @burst_period: future use: (currently hard coded in the LMAC) + * The interval between two sequential bursts. + * @min_delta_ftm: future use: (currently hard coded in the LMAC) + * The minimum delay between two sequential FTM Responses + * in the same burst. + * @burst_duration: future use: (currently hard coded in the LMAC) + * The total time for all FTMs handshake in the same burst. + * Affect the time events duration in the LMAC. + * @num_of_burst_exp: future use: (currently hard coded in the LMAC) + * The number of bursts for the current ToF request. Affect + * the number of events allocations in the current iteration. + * @get_ch_est: for xVT only, NA for driver + * @abort_responder: when set to '1' - Responder will terminate its activity + * (all other fields in the command are ignored) + * @recv_sta_req_params: 1 - Responder will ignore the other Responder's + * params and use the recomended Initiator params. + * 0 - otherwise + * @channel_num: current AP Channel + * @bandwidth: current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz + * @rate: current AP rate + * @ctrl_ch_position: coding of the control channel position relative to + * the center frequency. + * 40MHz 0 below center, 1 above center + * 80MHz bits [0..1]: 0 the near 20MHz to the center, + * 1 the far 20MHz to the center + * bit[2] as above 40MHz + * @ftm_per_burst: FTMs per Burst + * @ftm_resp_ts_avail: '0' - we don't measure over the Initial FTM Response, + * '1' - we measure over the Initial FTM Response + * @asap_mode: ASAP / Non ASAP mode for the current WLS station + * @sta_id: index of the AP STA when in AP mode + * @tsf_timer_offset_msecs: The dictated time offset (mSec) from the AP's TSF + * @toa_offset: Artificial addition [0.1nsec] for the ToA - to be used for debug + * purposes, simulating station movement by adding various values + * to this field + * @bssid: Current AP BSSID + */ +struct iwl_tof_responder_config_cmd { + __le32 sub_grp_cmd_id; + __le16 burst_period; + u8 min_delta_ftm; + u8 burst_duration; + u8 num_of_burst_exp; + u8 get_ch_est; + u8 abort_responder; + u8 recv_sta_req_params; + u8 channel_num; + u8 bandwidth; + u8 rate; + u8 ctrl_ch_position; + u8 ftm_per_burst; + u8 ftm_resp_ts_avail; + u8 asap_mode; + u8 sta_id; + __le16 tsf_timer_offset_msecs; + __le16 toa_offset; + u8 bssid[ETH_ALEN]; +} __packed; + +/** + * struct iwl_tof_range_request_ext_cmd - extended range req for WLS + * @tsf_timer_offset_msec: the recommended time offset (mSec) from the AP's TSF + * @min_delta_ftm: Minimal time between two consecutive measurements, + * in units of 100us. 0 means no preference by station + * @ftm_format_and_bw20M: FTM Channel Spacing/Format for 20MHz: recommended + * value be sent to the AP + * @ftm_format_and_bw40M: FTM Channel Spacing/Format for 40MHz: recommended + * value to be sent to the AP + * @ftm_format_and_bw80M: FTM Channel Spacing/Format for 80MHz: recommended + * value to be sent to the AP + */ +struct iwl_tof_range_req_ext_cmd { + __le32 sub_grp_cmd_id; + __le16 tsf_timer_offset_msec; + __le16 reserved; + u8 min_delta_ftm; + u8 ftm_format_and_bw20M; + u8 ftm_format_and_bw40M; + u8 ftm_format_and_bw80M; +} __packed; + +#define IWL_MVM_TOF_MAX_APS 21 + +/** + * struct iwl_tof_range_req_ap_entry - AP configuration parameters + * @channel_num: Current AP Channel + * @bandwidth: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz + * @tsf_delta_direction: TSF relatively to the subject AP + * @ctrl_ch_position: Coding of the control channel position relative to the + * center frequency. + * 40MHz 0 below center, 1 above center + * 80MHz bits [0..1]: 0 the near 20MHz to the center, + * 1 the far 20MHz to the center + * bit[2] as above 40MHz + * @bssid: AP's bss id + * @measure_type: Measurement type: 0 - two sided, 1 - One sided + * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of the + * number of measurement iterations (min 2^0 = 1, max 2^14) + * @burst_period: Recommended value to be sent to the AP. Measurement + * periodicity In units of 100ms. ignored if num_of_bursts = 0 + * @samples_per_burst: 2-sided: the number of FTMs pairs in single Burst (1-31) + * 1-sided: how many rts/cts pairs should be used per burst. + * @retries_per_sample: Max number of retries that the LMAC should send + * in case of no replies by the AP. + * @tsf_delta: TSF Delta in units of microseconds. + * The difference between the AP TSF and the device local clock. + * @location_req: Location Request Bit[0] LCI should be sent in the FTMR + * Bit[1] Civic should be sent in the FTMR + * @asap_mode: 0 - non asap mode, 1 - asap mode (not relevant for one sided) + * @enable_dyn_ack: Enable Dynamic ACK BW. + * 0 Initiator interact with regular AP + * 1 Initiator interact with Responder machine: need to send the + * Initiator Acks with HT 40MHz / 80MHz, since the Responder should + * use it for its ch est measurement (this flag will be set when we + * configure the opposite machine to be Responder). + * @rssi: Last received value + * leagal values: -128-0 (0x7f). above 0x0 indicating an invalid value. + */ +struct iwl_tof_range_req_ap_entry { + u8 channel_num; + u8 bandwidth; + u8 tsf_delta_direction; + u8 ctrl_ch_position; + u8 bssid[ETH_ALEN]; + u8 measure_type; + u8 num_of_bursts; + __le16 burst_period; + u8 samples_per_burst; + u8 retries_per_sample; + __le32 tsf_delta; + u8 location_req; + u8 asap_mode; + u8 enable_dyn_ack; + s8 rssi; +} __packed; + +/** + * enum iwl_tof_response_mode + * @IWL_MVM_TOF_RESPOSE_ASAP: report each AP measurement separately as soon as + * possible (not supported for this release) + * @IWL_MVM_TOF_RESPOSE_TIMEOUT: report all AP measurements as a batch upon + * timeout expiration + * @IWL_MVM_TOF_RESPOSE_COMPLETE: report all AP measurements as a batch at the + * earlier of: measurements completion / timeout + * expiration. + */ +enum iwl_tof_response_mode { + IWL_MVM_TOF_RESPOSE_ASAP = 1, + IWL_MVM_TOF_RESPOSE_TIMEOUT, + IWL_MVM_TOF_RESPOSE_COMPLETE, +}; + +/** + * struct iwl_tof_range_req_cmd - start measurement cmd + * @request_id: A Token incremented per request. The same Token will be + * sent back in the range response + * @initiator: 0- NW initiated, 1 - Client Initiated + * @one_sided_los_disable: '0'- run ML-Algo for both ToF/OneSided, + * '1' - run ML-Algo for ToF only + * @req_timeout: Requested timeout of the response in units of 100ms. + * This is equivalent to the session time configured to the + * LMAC in Initiator Request + * @report_policy: Supported partially for this release: For current release - + * the range report will be uploaded as a batch when ready or + * when the session is done (successfully / partially). + * one of iwl_tof_response_mode. + * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @macaddr_random: '0' Use default source MAC address (i.e. p2_p), + * '1' Use MAC Address randomization according to the below + * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. + * Bits set to 1 shall be randomized by the UMAC + */ +struct iwl_tof_range_req_cmd { + __le32 sub_grp_cmd_id; + u8 request_id; + u8 initiator; + u8 one_sided_los_disable; + u8 req_timeout; + u8 report_policy; + u8 los_det_disable; + u8 num_of_ap; + u8 macaddr_random; + u8 macaddr_template[ETH_ALEN]; + u8 macaddr_mask[ETH_ALEN]; + struct iwl_tof_range_req_ap_entry ap[IWL_MVM_TOF_MAX_APS]; +} __packed; + +/** + * struct iwl_tof_gen_resp_cmd - generic ToF response + */ +struct iwl_tof_gen_resp_cmd { + __le32 sub_grp_cmd_id; + u8 data[]; +} __packed; + +/** + * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response) + * @measure_status: current APs measurement status + * @measure_bw: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz + * @rtt: The Round Trip Time that took for the last measurement for + * current AP [nSec] + * @rtt_variance: The Variance of the RTT values measured for current AP + * @rtt_spread: The Difference between the maximum and the minimum RTT + * values measured for current AP in the current session [nsec] + * @rssi: RSSI as uploaded in the Channel Estimation notification + * @rssi_spread: The Difference between the maximum and the minimum RSSI values + * measured for current AP in the current session + * @range: Measured range [cm] + * @range_variance: Measured range variance [cm] + * @timestamp: The GP2 Clock [usec] where Channel Estimation notification was + * uploaded by the LMAC + */ +struct iwl_tof_range_rsp_ap_entry_ntfy { + u8 bssid[ETH_ALEN]; + u8 measure_status; + u8 measure_bw; + __le32 rtt; + __le32 rtt_variance; + __le32 rtt_spread; + s8 rssi; + u8 rssi_spread; + __le16 reserved; + __le32 range; + __le32 range_variance; + __le32 timestamp; +} __packed; + +/** + * struct iwl_tof_range_rsp_ntfy - + * @request_id: A Token ID of the corresponding Range request + * @request_status: status of current measurement session + * @last_in_batch: reprot policy (when not all responses are uploaded at once) + * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + */ +struct iwl_tof_range_rsp_ntfy { + u8 request_id; + u8 request_status; + u8 last_in_batch; + u8 num_of_aps; + struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_MVM_TOF_MAX_APS]; +} __packed; + +#define IWL_MVM_TOF_MCSI_BUF_SIZE (245) +/** + * struct iwl_tof_mcsi_notif - used for debug + * @token: token ID for the current session + * @role: '0' - initiator, '1' - responder + * @initiator_bssid: initiator machine + * @responder_bssid: responder machine + * @mcsi_buffer: debug data + */ +struct iwl_tof_mcsi_notif { + u8 token; + u8 role; + __le16 reserved; + u8 initiator_bssid[ETH_ALEN]; + u8 responder_bssid[ETH_ALEN]; + u8 mcsi_buffer[IWL_MVM_TOF_MCSI_BUF_SIZE * 4]; +} __packed; + +/** + * struct iwl_tof_neighbor_report_notif + * @bssid: BSSID of the AP which sent the report + * @request_token: same token as the corresponding request + * @status: + * @report_ie_len: the length of the response frame starting from the Element ID + * @data: the IEs + */ +struct iwl_tof_neighbor_report { + u8 bssid[ETH_ALEN]; + u8 request_token; + u8 status; + __le16 report_ie_len; + u8 data[]; +} __packed; + +/** + * struct iwl_tof_range_abort_cmd + * @request_id: corresponds to a range request + */ +struct iwl_tof_range_abort_cmd { + __le32 sub_grp_cmd_id; + u8 request_id; + u8 reserved[3]; +} __packed; + +#endif diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h new file mode 100644 index 000000000000..853698ab8b05 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h @@ -0,0 +1,646 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __fw_api_tx_h__ +#define __fw_api_tx_h__ + +/** + * enum iwl_tx_flags - bitmasks for tx_flags in TX command + * @TX_CMD_FLG_PROT_REQUIRE: use RTS or CTS-to-self to protect the frame + * @TX_CMD_FLG_WRITE_TX_POWER: update current tx power value in the mgmt frame + * @TX_CMD_FLG_ACK: expect ACK from receiving station + * @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command. + * Otherwise, use rate_n_flags from the TX command + * @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected + * Must set TX_CMD_FLG_ACK with this flag. + * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence + * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence + * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC) + * @TX_CMD_FLG_BT_PRIO_POS: the position of the BT priority (bit 11 is ignored + * on old firmwares). + * @TX_CMD_FLG_BT_DIS: disable BT priority for this frame + * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control. + * Should be set for mgmt, non-QOS data, mcast, bcast and in scan command + * @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU + * @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame + * Should be set for beacons and probe responses + * @TX_CMD_FLG_CALIB: activate PA TX power calibrations + * @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count + * @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header. + * Should be set for 26/30 length MAC headers + * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW + * @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration + * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation + * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id + * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped + * @TX_CMD_FLG_EXEC_PAPD: execute PAPD + * @TX_CMD_FLG_PAPD_TYPE: 0 for reference power, 1 for nominal power + * @TX_CMD_FLG_HCCA_CHUNK: mark start of TSPEC chunk + */ +enum iwl_tx_flags { + TX_CMD_FLG_PROT_REQUIRE = BIT(0), + TX_CMD_FLG_WRITE_TX_POWER = BIT(1), + TX_CMD_FLG_ACK = BIT(3), + TX_CMD_FLG_STA_RATE = BIT(4), + TX_CMD_FLG_BAR = BIT(6), + TX_CMD_FLG_TXOP_PROT = BIT(7), + TX_CMD_FLG_VHT_NDPA = BIT(8), + TX_CMD_FLG_HT_NDPA = BIT(9), + TX_CMD_FLG_CSI_FDBK2HOST = BIT(10), + TX_CMD_FLG_BT_PRIO_POS = 11, + TX_CMD_FLG_BT_DIS = BIT(12), + TX_CMD_FLG_SEQ_CTL = BIT(13), + TX_CMD_FLG_MORE_FRAG = BIT(14), + TX_CMD_FLG_TSF = BIT(16), + TX_CMD_FLG_CALIB = BIT(17), + TX_CMD_FLG_KEEP_SEQ_CTL = BIT(18), + TX_CMD_FLG_MH_PAD = BIT(20), + TX_CMD_FLG_RESP_TO_DRV = BIT(21), + TX_CMD_FLG_CCMP_AGG = BIT(22), + TX_CMD_FLG_TKIP_MIC_DONE = BIT(23), + TX_CMD_FLG_DUR = BIT(25), + TX_CMD_FLG_FW_DROP = BIT(26), + TX_CMD_FLG_EXEC_PAPD = BIT(27), + TX_CMD_FLG_PAPD_TYPE = BIT(28), + TX_CMD_FLG_HCCA_CHUNK = BIT(31) +}; /* TX_FLAGS_BITS_API_S_VER_1 */ + +/** + * enum iwl_tx_pm_timeouts - pm timeout values in TX command + * @PM_FRAME_NONE: no need to suspend sleep mode + * @PM_FRAME_MGMT: fw suspend sleep mode for 100TU + * @PM_FRAME_ASSOC: fw suspend sleep mode for 10sec + */ +enum iwl_tx_pm_timeouts { + PM_FRAME_NONE = 0, + PM_FRAME_MGMT = 2, + PM_FRAME_ASSOC = 3, +}; + +/* + * TX command security control + */ +#define TX_CMD_SEC_WEP 0x01 +#define TX_CMD_SEC_CCM 0x02 +#define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_EXT 0x04 +#define TX_CMD_SEC_MSK 0x07 +#define TX_CMD_SEC_WEP_KEY_IDX_POS 6 +#define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 +#define TX_CMD_SEC_KEY128 0x08 + +/* TODO: how does these values are OK with only 16 bit variable??? */ +/* + * TX command next frame info + * + * bits 0:2 - security control (TX_CMD_SEC_*) + * bit 3 - immediate ACK required + * bit 4 - rate is taken from STA table + * bit 5 - frame belongs to BA stream + * bit 6 - immediate BA response expected + * bit 7 - unused + * bits 8:15 - Station ID + * bits 16:31 - rate + */ +#define TX_CMD_NEXT_FRAME_ACK_MSK (0x8) +#define TX_CMD_NEXT_FRAME_STA_RATE_MSK (0x10) +#define TX_CMD_NEXT_FRAME_BA_MSK (0x20) +#define TX_CMD_NEXT_FRAME_IMM_BA_RSP_MSK (0x40) +#define TX_CMD_NEXT_FRAME_FLAGS_MSK (0xf8) +#define TX_CMD_NEXT_FRAME_STA_ID_MSK (0xff00) +#define TX_CMD_NEXT_FRAME_STA_ID_POS (8) +#define TX_CMD_NEXT_FRAME_RATE_MSK (0xffff0000) +#define TX_CMD_NEXT_FRAME_RATE_POS (16) + +/* + * TX command Frame life time in us - to be written in pm_frame_timeout + */ +#define TX_CMD_LIFE_TIME_INFINITE 0xFFFFFFFF +#define TX_CMD_LIFE_TIME_DEFAULT 2000000 /* 2000 ms*/ +#define TX_CMD_LIFE_TIME_PROBE_RESP 40000 /* 40 ms */ +#define TX_CMD_LIFE_TIME_EXPIRED_FRAME 0 + +/* + * TID for non QoS frames - to be written in tid_tspec + */ +#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT + +/* + * Limits on the retransmissions - to be written in {data,rts}_retry_limit + */ +#define IWL_DEFAULT_TX_RETRY 15 +#define IWL_MGMT_DFAULT_RETRY_LIMIT 3 +#define IWL_RTS_DFAULT_RETRY_LIMIT 60 +#define IWL_BAR_DFAULT_RETRY_LIMIT 60 +#define IWL_LOW_RETRY_LIMIT 7 + +/* TODO: complete documentation for try_cnt and btkill_cnt */ +/** + * struct iwl_tx_cmd - TX command struct to FW + * ( TX_CMD = 0x1c ) + * @len: in bytes of the payload, see below for details + * @tx_flags: combination of TX_CMD_FLG_* + * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is + * cleared. Combination of RATE_MCS_* + * @sta_id: index of destination station in FW station table + * @sec_ctl: security control, TX_CMD_SEC_* + * @initial_rate_index: index into the the rate table for initial TX attempt. + * Applied if TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames. + * @key: security key + * @next_frame_flags: TX_CMD_SEC_* and TX_CMD_NEXT_FRAME_* + * @life_time: frame life time (usecs??) + * @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt + + * btkill_cnd + reserved), first 32 bits. "0" disables usage. + * @dram_msb_ptr: upper bits of the scratch physical address + * @rts_retry_limit: max attempts for RTS + * @data_retry_limit: max attempts to send the data packet + * @tid_spec: TID/tspec + * @pm_frame_timeout: PM TX frame timeout + * + * The byte count (both len and next_frame_len) includes MAC header + * (24/26/30/32 bytes) + * + 2 bytes pad if 26/30 header size + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * It does not include post-MAC padding, i.e., + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes. + * Range of len: 14-2342 bytes. + * + * After the struct fields the MAC header is placed, plus any padding, + * and then the actial payload. + */ +struct iwl_tx_cmd { + __le16 len; + __le16 next_frame_len; + __le32 tx_flags; + struct { + u8 try_cnt; + u8 btkill_cnt; + __le16 reserved; + } scratch; /* DRAM_SCRATCH_API_U_VER_1 */ + __le32 rate_n_flags; + u8 sta_id; + u8 sec_ctl; + u8 initial_rate_index; + u8 reserved2; + u8 key[16]; + __le32 reserved3; + __le32 life_time; + __le32 dram_lsb_ptr; + u8 dram_msb_ptr; + u8 rts_retry_limit; + u8 data_retry_limit; + u8 tid_tspec; + __le16 pm_frame_timeout; + __le16 reserved4; + u8 payload[0]; + struct ieee80211_hdr hdr[0]; +} __packed; /* TX_CMD_API_S_VER_3 */ + +/* + * TX response related data + */ + +/* + * enum iwl_tx_status - status that is returned by the fw after attempts to Tx + * @TX_STATUS_SUCCESS: + * @TX_STATUS_DIRECT_DONE: + * @TX_STATUS_POSTPONE_DELAY: + * @TX_STATUS_POSTPONE_FEW_BYTES: + * @TX_STATUS_POSTPONE_BT_PRIO: + * @TX_STATUS_POSTPONE_QUIET_PERIOD: + * @TX_STATUS_POSTPONE_CALC_TTAK: + * @TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: + * @TX_STATUS_FAIL_SHORT_LIMIT: + * @TX_STATUS_FAIL_LONG_LIMIT: + * @TX_STATUS_FAIL_UNDERRUN: + * @TX_STATUS_FAIL_DRAIN_FLOW: + * @TX_STATUS_FAIL_RFKILL_FLUSH: + * @TX_STATUS_FAIL_LIFE_EXPIRE: + * @TX_STATUS_FAIL_DEST_PS: + * @TX_STATUS_FAIL_HOST_ABORTED: + * @TX_STATUS_FAIL_BT_RETRY: + * @TX_STATUS_FAIL_STA_INVALID: + * @TX_TATUS_FAIL_FRAG_DROPPED: + * @TX_STATUS_FAIL_TID_DISABLE: + * @TX_STATUS_FAIL_FIFO_FLUSHED: + * @TX_STATUS_FAIL_SMALL_CF_POLL: + * @TX_STATUS_FAIL_FW_DROP: + * @TX_STATUS_FAIL_STA_COLOR_MISMATCH: mismatch between color of Tx cmd and + * STA table + * @TX_FRAME_STATUS_INTERNAL_ABORT: + * @TX_MODE_MSK: + * @TX_MODE_NO_BURST: + * @TX_MODE_IN_BURST_SEQ: + * @TX_MODE_FIRST_IN_BURST: + * @TX_QUEUE_NUM_MSK: + * + * Valid only if frame_count =1 + * TODO: complete documentation + */ +enum iwl_tx_status { + TX_STATUS_MSK = 0x000000ff, + TX_STATUS_SUCCESS = 0x01, + TX_STATUS_DIRECT_DONE = 0x02, + /* postpone TX */ + TX_STATUS_POSTPONE_DELAY = 0x40, + TX_STATUS_POSTPONE_FEW_BYTES = 0x41, + TX_STATUS_POSTPONE_BT_PRIO = 0x42, + TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, + TX_STATUS_POSTPONE_CALC_TTAK = 0x44, + /* abort TX */ + TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, + TX_STATUS_FAIL_SHORT_LIMIT = 0x82, + TX_STATUS_FAIL_LONG_LIMIT = 0x83, + TX_STATUS_FAIL_UNDERRUN = 0x84, + TX_STATUS_FAIL_DRAIN_FLOW = 0x85, + TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, + TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, + TX_STATUS_FAIL_DEST_PS = 0x88, + TX_STATUS_FAIL_HOST_ABORTED = 0x89, + TX_STATUS_FAIL_BT_RETRY = 0x8a, + TX_STATUS_FAIL_STA_INVALID = 0x8b, + TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, + TX_STATUS_FAIL_TID_DISABLE = 0x8d, + TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, + TX_STATUS_FAIL_SMALL_CF_POLL = 0x8f, + TX_STATUS_FAIL_FW_DROP = 0x90, + TX_STATUS_FAIL_STA_COLOR_MISMATCH = 0x91, + TX_STATUS_INTERNAL_ABORT = 0x92, + TX_MODE_MSK = 0x00000f00, + TX_MODE_NO_BURST = 0x00000000, + TX_MODE_IN_BURST_SEQ = 0x00000100, + TX_MODE_FIRST_IN_BURST = 0x00000200, + TX_QUEUE_NUM_MSK = 0x0001f000, + TX_NARROW_BW_MSK = 0x00060000, + TX_NARROW_BW_1DIV2 = 0x00020000, + TX_NARROW_BW_1DIV4 = 0x00040000, + TX_NARROW_BW_1DIV8 = 0x00060000, +}; + +/* + * enum iwl_tx_agg_status - TX aggregation status + * @AGG_TX_STATE_STATUS_MSK: + * @AGG_TX_STATE_TRANSMITTED: + * @AGG_TX_STATE_UNDERRUN: + * @AGG_TX_STATE_BT_PRIO: + * @AGG_TX_STATE_FEW_BYTES: + * @AGG_TX_STATE_ABORT: + * @AGG_TX_STATE_LAST_SENT_TTL: + * @AGG_TX_STATE_LAST_SENT_TRY_CNT: + * @AGG_TX_STATE_LAST_SENT_BT_KILL: + * @AGG_TX_STATE_SCD_QUERY: + * @AGG_TX_STATE_TEST_BAD_CRC32: + * @AGG_TX_STATE_RESPONSE: + * @AGG_TX_STATE_DUMP_TX: + * @AGG_TX_STATE_DELAY_TX: + * @AGG_TX_STATE_TRY_CNT_MSK: Retry count for 1st frame in aggregation (retries + * occur if tx failed for this frame when it was a member of a previous + * aggregation block). If rate scaling is used, retry count indicates the + * rate table entry used for all frames in the new agg. + *@ AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for + * this frame + * + * TODO: complete documentation + */ +enum iwl_tx_agg_status { + AGG_TX_STATE_STATUS_MSK = 0x00fff, + AGG_TX_STATE_TRANSMITTED = 0x000, + AGG_TX_STATE_UNDERRUN = 0x001, + AGG_TX_STATE_BT_PRIO = 0x002, + AGG_TX_STATE_FEW_BYTES = 0x004, + AGG_TX_STATE_ABORT = 0x008, + AGG_TX_STATE_LAST_SENT_TTL = 0x010, + AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020, + AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040, + AGG_TX_STATE_SCD_QUERY = 0x080, + AGG_TX_STATE_TEST_BAD_CRC32 = 0x0100, + AGG_TX_STATE_RESPONSE = 0x1ff, + AGG_TX_STATE_DUMP_TX = 0x200, + AGG_TX_STATE_DELAY_TX = 0x400, + AGG_TX_STATE_TRY_CNT_POS = 12, + AGG_TX_STATE_TRY_CNT_MSK = 0xf << AGG_TX_STATE_TRY_CNT_POS, +}; + +#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL| \ + AGG_TX_STATE_LAST_SENT_TRY_CNT| \ + AGG_TX_STATE_LAST_SENT_BT_KILL) + +/* + * The mask below describes a status where we are absolutely sure that the MPDU + * wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've + * written the bytes to the TXE, but we know nothing about what the DSP did. + */ +#define AGG_TX_STAT_FRAME_NOT_SENT (AGG_TX_STATE_FEW_BYTES | \ + AGG_TX_STATE_ABORT | \ + AGG_TX_STATE_SCD_QUERY) + +/* + * REPLY_TX = 0x1c (response) + * + * This response may be in one of two slightly different formats, indicated + * by the frame_count field: + * + * 1) No aggregation (frame_count == 1). This reports Tx results for a single + * frame. Multiple attempts, at various bit rates, may have been made for + * this frame. + * + * 2) Aggregation (frame_count > 1). This reports Tx results for two or more + * frames that used block-acknowledge. All frames were transmitted at + * same rate. Rate scaling may have been used if first frame in this new + * agg block failed in previous agg block(s). + * + * Note that, for aggregation, ACK (block-ack) status is not delivered + * here; block-ack has not been received by the time the device records + * this status. + * This status relates to reasons the tx might have been blocked or aborted + * within the device, rather than whether it was received successfully by + * the destination station. + */ + +/** + * struct agg_tx_status - per packet TX aggregation status + * @status: enum iwl_tx_agg_status + * @sequence: Sequence # for this frame's Tx cmd (not SSN!) + */ +struct agg_tx_status { + __le16 status; + __le16 sequence; +} __packed; + +/* + * definitions for initial rate index field + * bits [3:0] initial rate index + * bits [6:4] rate table color, used for the initial rate + * bit-7 invalid rate indication + */ +#define TX_RES_INIT_RATE_INDEX_MSK 0x0f +#define TX_RES_RATE_TABLE_COLOR_MSK 0x70 +#define TX_RES_INV_RATE_INDEX_MSK 0x80 + +#define IWL_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f) +#define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4) + +/** + * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet + * ( REPLY_TX = 0x1c ) + * @frame_count: 1 no aggregation, >1 aggregation + * @bt_kill_count: num of times blocked by bluetooth (unused for agg) + * @failure_rts: num of failures due to unsuccessful RTS + * @failure_frame: num failures due to no ACK (unused for agg) + * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the + * Tx of all the batch. RATE_MCS_* + * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK. + * for agg: RTS + CTS + aggregation tx time + block-ack time. + * in usec. + * @pa_status: tx power info + * @pa_integ_res_a: tx power info + * @pa_integ_res_b: tx power info + * @pa_integ_res_c: tx power info + * @measurement_req_id: tx power info + * @tfd_info: TFD information set by the FH + * @seq_ctl: sequence control from the Tx cmd + * @byte_cnt: byte count from the Tx cmd + * @tlc_info: TLC rate info + * @ra_tid: bits [3:0] = ra, bits [7:4] = tid + * @frame_ctrl: frame control + * @status: for non-agg: frame status TX_STATUS_* + * for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields + * follow this one, up to frame_count. + * + * After the array of statuses comes the SSN of the SCD. Look at + * %iwl_mvm_get_scd_ssn for more details. + */ +struct iwl_mvm_tx_resp { + u8 frame_count; + u8 bt_kill_count; + u8 failure_rts; + u8 failure_frame; + __le32 initial_rate; + __le16 wireless_media_time; + + u8 pa_status; + u8 pa_integ_res_a[3]; + u8 pa_integ_res_b[3]; + u8 pa_integ_res_c[3]; + __le16 measurement_req_id; + u8 reduced_tpc; + u8 reserved; + + __le32 tfd_info; + __le16 seq_ctl; + __le16 byte_cnt; + u8 tlc_info; + u8 ra_tid; + __le16 frame_ctrl; + + struct agg_tx_status status; +} __packed; /* TX_RSP_API_S_VER_3 */ + +/** + * struct iwl_mvm_ba_notif - notifies about reception of BA + * ( BA_NOTIF = 0xc5 ) + * @sta_addr_lo32: lower 32 bits of the MAC address + * @sta_addr_hi16: upper 16 bits of the MAC address + * @sta_id: Index of recipient (BA-sending) station in fw's station table + * @tid: tid of the session + * @seq_ctl: + * @bitmap: the bitmap of the BA notification as seen in the air + * @scd_flow: the tx queue this BA relates to + * @scd_ssn: the index of the last contiguously sent packet + * @txed: number of Txed frames in this batch + * @txed_2_done: number of Acked frames in this batch + */ +struct iwl_mvm_ba_notif { + __le32 sta_addr_lo32; + __le16 sta_addr_hi16; + __le16 reserved; + + u8 sta_id; + u8 tid; + __le16 seq_ctl; + __le64 bitmap; + __le16 scd_flow; + __le16 scd_ssn; + u8 txed; + u8 txed_2_done; + __le16 reserved1; +} __packed; + +/* + * struct iwl_mac_beacon_cmd - beacon template command + * @tx: the tx commands associated with the beacon frame + * @template_id: currently equal to the mac context id of the coresponding + * mac. + * @tim_idx: the offset of the tim IE in the beacon + * @tim_size: the length of the tim IE + * @frame: the template of the beacon frame + */ +struct iwl_mac_beacon_cmd { + struct iwl_tx_cmd tx; + __le32 template_id; + __le32 tim_idx; + __le32 tim_size; + struct ieee80211_hdr frame[0]; +} __packed; + +struct iwl_beacon_notif { + struct iwl_mvm_tx_resp beacon_notify_hdr; + __le64 tsf; + __le32 ibss_mgr_status; +} __packed; + +/** + * struct iwl_extended_beacon_notif - notifies about beacon transmission + * @beacon_notify_hdr: tx response command associated with the beacon + * @tsf: last beacon tsf + * @ibss_mgr_status: whether IBSS is manager + * @gp2: last beacon time in gp2 + */ +struct iwl_extended_beacon_notif { + struct iwl_mvm_tx_resp beacon_notify_hdr; + __le64 tsf; + __le32 ibss_mgr_status; + __le32 gp2; +} __packed; /* BEACON_NTFY_API_S_VER_5 */ + +/** + * enum iwl_dump_control - dump (flush) control flags + * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty + * and the TFD queues are empty. + */ +enum iwl_dump_control { + DUMP_TX_FIFO_FLUSH = BIT(1), +}; + +/** + * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command + * @queues_ctl: bitmap of queues to flush + * @flush_ctl: control flags + * @reserved: reserved + */ +struct iwl_tx_path_flush_cmd { + __le32 queues_ctl; + __le16 flush_ctl; + __le16 reserved; +} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */ + +/** + * iwl_mvm_get_scd_ssn - returns the SSN of the SCD + * @tx_resp: the Tx response from the fw (agg or non-agg) + * + * When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since + * it can't know that everything will go well until the end of the AMPDU, it + * can't know in advance the number of MPDUs that will be sent in the current + * batch. This is why it writes the agg Tx response while it fetches the MPDUs. + * Hence, it can't know in advance what the SSN of the SCD will be at the end + * of the batch. This is why the SSN of the SCD is written at the end of the + * whole struct at a variable offset. This function knows how to cope with the + * variable offset and returns the SSN of the SCD. + */ +static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm_tx_resp *tx_resp) +{ + return le32_to_cpup((__le32 *)&tx_resp->status + + tx_resp->frame_count) & 0xfff; +} + +/** + * struct iwl_scd_txq_cfg_cmd - New txq hw scheduler config command + * @token: + * @sta_id: station id + * @tid: + * @scd_queue: scheduler queue to confiug + * @enable: 1 queue enable, 0 queue disable + * @aggregate: 1 aggregated queue, 0 otherwise + * @tx_fifo: %enum iwl_mvm_tx_fifo + * @window: BA window size + * @ssn: SSN for the BA agreement + */ +struct iwl_scd_txq_cfg_cmd { + u8 token; + u8 sta_id; + u8 tid; + u8 scd_queue; + u8 enable; + u8 aggregate; + u8 tx_fifo; + u8 window; + __le16 ssn; + __le16 reserved; +} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */ + +/** + * struct iwl_scd_txq_cfg_rsp + * @token: taken from the command + * @sta_id: station id from the command + * @tid: tid from the command + * @scd_queue: scd_queue from the command + */ +struct iwl_scd_txq_cfg_rsp { + u8 token; + u8 sta_id; + u8 tid; + u8 scd_queue; +} __packed; /* SCD_QUEUE_CFG_RSP_API_S_VER_1 */ + +#endif /* __fw_api_tx_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h new file mode 100644 index 000000000000..181590fbd3b3 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -0,0 +1,1773 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __fw_api_h__ +#define __fw_api_h__ + +#include "fw-api-rs.h" +#include "fw-api-rx.h" +#include "fw-api-tx.h" +#include "fw-api-sta.h" +#include "fw-api-mac.h" +#include "fw-api-power.h" +#include "fw-api-d3.h" +#include "fw-api-coex.h" +#include "fw-api-scan.h" +#include "fw-api-stats.h" +#include "fw-api-tof.h" + +/* Tx queue numbers */ +enum { + IWL_MVM_OFFCHANNEL_QUEUE = 8, + IWL_MVM_CMD_QUEUE = 9, +}; + +enum iwl_mvm_tx_fifo { + IWL_MVM_TX_FIFO_BK = 0, + IWL_MVM_TX_FIFO_BE, + IWL_MVM_TX_FIFO_VI, + IWL_MVM_TX_FIFO_VO, + IWL_MVM_TX_FIFO_MCAST = 5, + IWL_MVM_TX_FIFO_CMD = 7, +}; + +#define IWL_MVM_STATION_COUNT 16 + +#define IWL_MVM_TDLS_STA_COUNT 4 + +/* commands */ +enum { + MVM_ALIVE = 0x1, + REPLY_ERROR = 0x2, + ECHO_CMD = 0x3, + + INIT_COMPLETE_NOTIF = 0x4, + + /* PHY context commands */ + PHY_CONTEXT_CMD = 0x8, + DBG_CFG = 0x9, + ANTENNA_COUPLING_NOTIFICATION = 0xa, + + /* UMAC scan commands */ + SCAN_ITERATION_COMPLETE_UMAC = 0xb5, + SCAN_CFG_CMD = 0xc, + SCAN_REQ_UMAC = 0xd, + SCAN_ABORT_UMAC = 0xe, + SCAN_COMPLETE_UMAC = 0xf, + + /* station table */ + ADD_STA_KEY = 0x17, + ADD_STA = 0x18, + REMOVE_STA = 0x19, + + /* paging get item */ + FW_GET_ITEM_CMD = 0x1a, + + /* TX */ + TX_CMD = 0x1c, + TXPATH_FLUSH = 0x1e, + MGMT_MCAST_KEY = 0x1f, + + /* scheduler config */ + SCD_QUEUE_CFG = 0x1d, + + /* global key */ + WEP_KEY = 0x20, + + /* Memory */ + SHARED_MEM_CFG = 0x25, + + /* TDLS */ + TDLS_CHANNEL_SWITCH_CMD = 0x27, + TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa, + TDLS_CONFIG_CMD = 0xa7, + + /* MAC and Binding commands */ + MAC_CONTEXT_CMD = 0x28, + TIME_EVENT_CMD = 0x29, /* both CMD and response */ + TIME_EVENT_NOTIFICATION = 0x2a, + BINDING_CONTEXT_CMD = 0x2b, + TIME_QUOTA_CMD = 0x2c, + NON_QOS_TX_COUNTER_CMD = 0x2d, + + LQ_CMD = 0x4e, + + /* paging block to FW cpu2 */ + FW_PAGING_BLOCK_CMD = 0x4f, + + /* Scan offload */ + SCAN_OFFLOAD_REQUEST_CMD = 0x51, + SCAN_OFFLOAD_ABORT_CMD = 0x52, + HOT_SPOT_CMD = 0x53, + SCAN_OFFLOAD_COMPLETE = 0x6D, + SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, + SCAN_OFFLOAD_CONFIG_CMD = 0x6f, + MATCH_FOUND_NOTIFICATION = 0xd9, + SCAN_ITERATION_COMPLETE = 0xe7, + + /* Phy */ + PHY_CONFIGURATION_CMD = 0x6a, + CALIB_RES_NOTIF_PHY_DB = 0x6b, + /* PHY_DB_CMD = 0x6c, */ + + /* ToF - 802.11mc FTM */ + TOF_CMD = 0x10, + TOF_NOTIFICATION = 0x11, + + /* Power - legacy power table command */ + POWER_TABLE_CMD = 0x77, + PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78, + LTR_CONFIG = 0xee, + + /* Thermal Throttling*/ + REPLY_THERMAL_MNG_BACKOFF = 0x7e, + + /* Set/Get DC2DC frequency tune */ + DC2DC_CONFIG_CMD = 0x83, + + /* NVM */ + NVM_ACCESS_CMD = 0x88, + + SET_CALIB_DEFAULT_CMD = 0x8e, + + BEACON_NOTIFICATION = 0x90, + BEACON_TEMPLATE_CMD = 0x91, + TX_ANT_CONFIGURATION_CMD = 0x98, + STATISTICS_CMD = 0x9c, + STATISTICS_NOTIFICATION = 0x9d, + EOSP_NOTIFICATION = 0x9e, + REDUCE_TX_POWER_CMD = 0x9f, + + /* RF-KILL commands and notifications */ + CARD_STATE_CMD = 0xa0, + CARD_STATE_NOTIFICATION = 0xa1, + + MISSED_BEACONS_NOTIFICATION = 0xa2, + + /* Power - new power table command */ + MAC_PM_POWER_TABLE = 0xa9, + + MFUART_LOAD_NOTIFICATION = 0xb1, + + REPLY_RX_PHY_CMD = 0xc0, + REPLY_RX_MPDU_CMD = 0xc1, + BA_NOTIF = 0xc5, + + /* Location Aware Regulatory */ + MCC_UPDATE_CMD = 0xc8, + MCC_CHUB_UPDATE_CMD = 0xc9, + + MARKER_CMD = 0xcb, + + /* BT Coex */ + BT_COEX_PRIO_TABLE = 0xcc, + BT_COEX_PROT_ENV = 0xcd, + BT_PROFILE_NOTIFICATION = 0xce, + BT_CONFIG = 0x9b, + BT_COEX_UPDATE_SW_BOOST = 0x5a, + BT_COEX_UPDATE_CORUN_LUT = 0x5b, + BT_COEX_UPDATE_REDUCED_TXP = 0x5c, + BT_COEX_CI = 0x5d, + + REPLY_SF_CFG_CMD = 0xd1, + REPLY_BEACON_FILTERING_CMD = 0xd2, + + /* DTS measurements */ + CMD_DTS_MEASUREMENT_TRIGGER = 0xdc, + DTS_MEASUREMENT_NOTIFICATION = 0xdd, + + REPLY_DEBUG_CMD = 0xf0, + DEBUG_LOG_MSG = 0xf7, + + BCAST_FILTER_CMD = 0xcf, + MCAST_FILTER_CMD = 0xd0, + + /* D3 commands/notifications */ + D3_CONFIG_CMD = 0xd3, + PROT_OFFLOAD_CONFIG_CMD = 0xd4, + OFFLOADS_QUERY_CMD = 0xd5, + REMOTE_WAKE_CONFIG_CMD = 0xd6, + D0I3_END_CMD = 0xed, + + /* for WoWLAN in particular */ + WOWLAN_PATTERNS = 0xe0, + WOWLAN_CONFIGURATION = 0xe1, + WOWLAN_TSC_RSC_PARAM = 0xe2, + WOWLAN_TKIP_PARAM = 0xe3, + WOWLAN_KEK_KCK_MATERIAL = 0xe4, + WOWLAN_GET_STATUSES = 0xe5, + WOWLAN_TX_POWER_PER_DB = 0xe6, + + /* and for NetDetect */ + SCAN_OFFLOAD_PROFILES_QUERY_CMD = 0x56, + SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD = 0x58, + SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD = 0x59, + + REPLY_MAX = 0xff, +}; + +enum iwl_phy_ops_subcmd_ids { + CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, + DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, +}; + +/* command groups */ +enum { + PHY_OPS_GROUP = 0x4, +}; + +/** + * struct iwl_cmd_response - generic response struct for most commands + * @status: status of the command asked, changes for each one + */ +struct iwl_cmd_response { + __le32 status; +}; + +/* + * struct iwl_tx_ant_cfg_cmd + * @valid: valid antenna configuration + */ +struct iwl_tx_ant_cfg_cmd { + __le32 valid; +} __packed; + +/* + * Calibration control struct. + * Sent as part of the phy configuration command. + * @flow_trigger: bitmap for which calibrations to perform according to + * flow triggers. + * @event_trigger: bitmap for which calibrations to perform according to + * event triggers. + */ +struct iwl_calib_ctrl { + __le32 flow_trigger; + __le32 event_trigger; +} __packed; + +/* This enum defines the bitmap of various calibrations to enable in both + * init ucode and runtime ucode through CALIBRATION_CFG_CMD. + */ +enum iwl_calib_cfg { + IWL_CALIB_CFG_XTAL_IDX = BIT(0), + IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(1), + IWL_CALIB_CFG_VOLTAGE_READ_IDX = BIT(2), + IWL_CALIB_CFG_PAPD_IDX = BIT(3), + IWL_CALIB_CFG_TX_PWR_IDX = BIT(4), + IWL_CALIB_CFG_DC_IDX = BIT(5), + IWL_CALIB_CFG_BB_FILTER_IDX = BIT(6), + IWL_CALIB_CFG_LO_LEAKAGE_IDX = BIT(7), + IWL_CALIB_CFG_TX_IQ_IDX = BIT(8), + IWL_CALIB_CFG_TX_IQ_SKEW_IDX = BIT(9), + IWL_CALIB_CFG_RX_IQ_IDX = BIT(10), + IWL_CALIB_CFG_RX_IQ_SKEW_IDX = BIT(11), + IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(12), + IWL_CALIB_CFG_CHAIN_NOISE_IDX = BIT(13), + IWL_CALIB_CFG_DISCONNECTED_ANT_IDX = BIT(14), + IWL_CALIB_CFG_ANT_COUPLING_IDX = BIT(15), + IWL_CALIB_CFG_DAC_IDX = BIT(16), + IWL_CALIB_CFG_ABS_IDX = BIT(17), + IWL_CALIB_CFG_AGC_IDX = BIT(18), +}; + +/* + * Phy configuration command. + */ +struct iwl_phy_cfg_cmd { + __le32 phy_cfg; + struct iwl_calib_ctrl calib_control; +} __packed; + +#define PHY_CFG_RADIO_TYPE (BIT(0) | BIT(1)) +#define PHY_CFG_RADIO_STEP (BIT(2) | BIT(3)) +#define PHY_CFG_RADIO_DASH (BIT(4) | BIT(5)) +#define PHY_CFG_PRODUCT_NUMBER (BIT(6) | BIT(7)) +#define PHY_CFG_TX_CHAIN_A BIT(8) +#define PHY_CFG_TX_CHAIN_B BIT(9) +#define PHY_CFG_TX_CHAIN_C BIT(10) +#define PHY_CFG_RX_CHAIN_A BIT(12) +#define PHY_CFG_RX_CHAIN_B BIT(13) +#define PHY_CFG_RX_CHAIN_C BIT(14) + + +/* Target of the NVM_ACCESS_CMD */ +enum { + NVM_ACCESS_TARGET_CACHE = 0, + NVM_ACCESS_TARGET_OTP = 1, + NVM_ACCESS_TARGET_EEPROM = 2, +}; + +/* Section types for NVM_ACCESS_CMD */ +enum { + NVM_SECTION_TYPE_SW = 1, + NVM_SECTION_TYPE_REGULATORY = 3, + NVM_SECTION_TYPE_CALIBRATION = 4, + NVM_SECTION_TYPE_PRODUCTION = 5, + NVM_SECTION_TYPE_MAC_OVERRIDE = 11, + NVM_SECTION_TYPE_PHY_SKU = 12, + NVM_MAX_NUM_SECTIONS = 13, +}; + +/** + * struct iwl_nvm_access_cmd_ver2 - Request the device to send an NVM section + * @op_code: 0 - read, 1 - write + * @target: NVM_ACCESS_TARGET_* + * @type: NVM_SECTION_TYPE_* + * @offset: offset in bytes into the section + * @length: in bytes, to read/write + * @data: if write operation, the data to write. On read its empty + */ +struct iwl_nvm_access_cmd { + u8 op_code; + u8 target; + __le16 type; + __le16 offset; + __le16 length; + u8 data[]; +} __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */ + +#define NUM_OF_FW_PAGING_BLOCKS 33 /* 32 for data and 1 block for CSS */ + +/* + * struct iwl_fw_paging_cmd - paging layout + * + * (FW_PAGING_BLOCK_CMD = 0x4f) + * + * Send to FW the paging layout in the driver. + * + * @flags: various flags for the command + * @block_size: the block size in powers of 2 + * @block_num: number of blocks specified in the command. + * @device_phy_addr: virtual addresses from device side +*/ +struct iwl_fw_paging_cmd { + __le32 flags; + __le32 block_size; + __le32 block_num; + __le32 device_phy_addr[NUM_OF_FW_PAGING_BLOCKS]; +} __packed; /* FW_PAGING_BLOCK_CMD_API_S_VER_1 */ + +/* + * Fw items ID's + * + * @IWL_FW_ITEM_ID_PAGING: Address of the pages that the FW will upload + * download + */ +enum iwl_fw_item_id { + IWL_FW_ITEM_ID_PAGING = 3, +}; + +/* + * struct iwl_fw_get_item_cmd - get an item from the fw + */ +struct iwl_fw_get_item_cmd { + __le32 item_id; +} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */ + +struct iwl_fw_get_item_resp { + __le32 item_id; + __le32 item_byte_cnt; + __le32 item_val; +} __packed; /* FW_GET_ITEM_RSP_S_VER_1 */ + +/** + * struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD + * @offset: offset in bytes into the section + * @length: in bytes, either how much was written or read + * @type: NVM_SECTION_TYPE_* + * @status: 0 for success, fail otherwise + * @data: if read operation, the data returned. Empty on write. + */ +struct iwl_nvm_access_resp { + __le16 offset; + __le16 length; + __le16 type; + __le16 status; + u8 data[]; +} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_2 */ + +/* MVM_ALIVE 0x1 */ + +/* alive response is_valid values */ +#define ALIVE_RESP_UCODE_OK BIT(0) +#define ALIVE_RESP_RFKILL BIT(1) + +/* alive response ver_type values */ +enum { + FW_TYPE_HW = 0, + FW_TYPE_PROT = 1, + FW_TYPE_AP = 2, + FW_TYPE_WOWLAN = 3, + FW_TYPE_TIMING = 4, + FW_TYPE_WIPAN = 5 +}; + +/* alive response ver_subtype values */ +enum { + FW_SUBTYPE_FULL_FEATURE = 0, + FW_SUBTYPE_BOOTSRAP = 1, /* Not valid */ + FW_SUBTYPE_REDUCED = 2, + FW_SUBTYPE_ALIVE_ONLY = 3, + FW_SUBTYPE_WOWLAN = 4, + FW_SUBTYPE_AP_SUBTYPE = 5, + FW_SUBTYPE_WIPAN = 6, + FW_SUBTYPE_INITIALIZE = 9 +}; + +#define IWL_ALIVE_STATUS_ERR 0xDEAD +#define IWL_ALIVE_STATUS_OK 0xCAFE + +#define IWL_ALIVE_FLG_RFKILL BIT(0) + +struct mvm_alive_resp_ver1 { + __le16 status; + __le16 flags; + u8 ucode_minor; + u8 ucode_major; + __le16 id; + u8 api_minor; + u8 api_major; + u8 ver_subtype; + u8 ver_type; + u8 mac; + u8 opt; + __le16 reserved2; + __le32 timestamp; + __le32 error_event_table_ptr; /* SRAM address for error log */ + __le32 log_event_table_ptr; /* SRAM address for event log */ + __le32 cpu_register_ptr; + __le32 dbgm_config_ptr; + __le32 alive_counter_ptr; + __le32 scd_base_ptr; /* SRAM address for SCD */ +} __packed; /* ALIVE_RES_API_S_VER_1 */ + +struct mvm_alive_resp_ver2 { + __le16 status; + __le16 flags; + u8 ucode_minor; + u8 ucode_major; + __le16 id; + u8 api_minor; + u8 api_major; + u8 ver_subtype; + u8 ver_type; + u8 mac; + u8 opt; + __le16 reserved2; + __le32 timestamp; + __le32 error_event_table_ptr; /* SRAM address for error log */ + __le32 log_event_table_ptr; /* SRAM address for LMAC event log */ + __le32 cpu_register_ptr; + __le32 dbgm_config_ptr; + __le32 alive_counter_ptr; + __le32 scd_base_ptr; /* SRAM address for SCD */ + __le32 st_fwrd_addr; /* pointer to Store and forward */ + __le32 st_fwrd_size; + u8 umac_minor; /* UMAC version: minor */ + u8 umac_major; /* UMAC version: major */ + __le16 umac_id; /* UMAC version: id */ + __le32 error_info_addr; /* SRAM address for UMAC error log */ + __le32 dbg_print_buff_addr; +} __packed; /* ALIVE_RES_API_S_VER_2 */ + +struct mvm_alive_resp { + __le16 status; + __le16 flags; + __le32 ucode_minor; + __le32 ucode_major; + u8 ver_subtype; + u8 ver_type; + u8 mac; + u8 opt; + __le32 timestamp; + __le32 error_event_table_ptr; /* SRAM address for error log */ + __le32 log_event_table_ptr; /* SRAM address for LMAC event log */ + __le32 cpu_register_ptr; + __le32 dbgm_config_ptr; + __le32 alive_counter_ptr; + __le32 scd_base_ptr; /* SRAM address for SCD */ + __le32 st_fwrd_addr; /* pointer to Store and forward */ + __le32 st_fwrd_size; + __le32 umac_minor; /* UMAC version: minor */ + __le32 umac_major; /* UMAC version: major */ + __le32 error_info_addr; /* SRAM address for UMAC error log */ + __le32 dbg_print_buff_addr; +} __packed; /* ALIVE_RES_API_S_VER_3 */ + +/* Error response/notification */ +enum { + FW_ERR_UNKNOWN_CMD = 0x0, + FW_ERR_INVALID_CMD_PARAM = 0x1, + FW_ERR_SERVICE = 0x2, + FW_ERR_ARC_MEMORY = 0x3, + FW_ERR_ARC_CODE = 0x4, + FW_ERR_WATCH_DOG = 0x5, + FW_ERR_WEP_GRP_KEY_INDX = 0x10, + FW_ERR_WEP_KEY_SIZE = 0x11, + FW_ERR_OBSOLETE_FUNC = 0x12, + FW_ERR_UNEXPECTED = 0xFE, + FW_ERR_FATAL = 0xFF +}; + +/** + * struct iwl_error_resp - FW error indication + * ( REPLY_ERROR = 0x2 ) + * @error_type: one of FW_ERR_* + * @cmd_id: the command ID for which the error occured + * @bad_cmd_seq_num: sequence number of the erroneous command + * @error_service: which service created the error, applicable only if + * error_type = 2, otherwise 0 + * @timestamp: TSF in usecs. + */ +struct iwl_error_resp { + __le32 error_type; + u8 cmd_id; + u8 reserved1; + __le16 bad_cmd_seq_num; + __le32 error_service; + __le64 timestamp; +} __packed; + + +/* Common PHY, MAC and Bindings definitions */ + +#define MAX_MACS_IN_BINDING (3) +#define MAX_BINDINGS (4) +#define AUX_BINDING_INDEX (3) +#define MAX_PHYS (4) + +/* Used to extract ID and color from the context dword */ +#define FW_CTXT_ID_POS (0) +#define FW_CTXT_ID_MSK (0xff << FW_CTXT_ID_POS) +#define FW_CTXT_COLOR_POS (8) +#define FW_CTXT_COLOR_MSK (0xff << FW_CTXT_COLOR_POS) +#define FW_CTXT_INVALID (0xffffffff) + +#define FW_CMD_ID_AND_COLOR(_id, _color) ((_id << FW_CTXT_ID_POS) |\ + (_color << FW_CTXT_COLOR_POS)) + +/* Possible actions on PHYs, MACs and Bindings */ +enum { + FW_CTXT_ACTION_STUB = 0, + FW_CTXT_ACTION_ADD, + FW_CTXT_ACTION_MODIFY, + FW_CTXT_ACTION_REMOVE, + FW_CTXT_ACTION_NUM +}; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */ + +/* Time Events */ + +/* Time Event types, according to MAC type */ +enum iwl_time_event_type { + /* BSS Station Events */ + TE_BSS_STA_AGGRESSIVE_ASSOC, + TE_BSS_STA_ASSOC, + TE_BSS_EAP_DHCP_PROT, + TE_BSS_QUIET_PERIOD, + + /* P2P Device Events */ + TE_P2P_DEVICE_DISCOVERABLE, + TE_P2P_DEVICE_LISTEN, + TE_P2P_DEVICE_ACTION_SCAN, + TE_P2P_DEVICE_FULL_SCAN, + + /* P2P Client Events */ + TE_P2P_CLIENT_AGGRESSIVE_ASSOC, + TE_P2P_CLIENT_ASSOC, + TE_P2P_CLIENT_QUIET_PERIOD, + + /* P2P GO Events */ + TE_P2P_GO_ASSOC_PROT, + TE_P2P_GO_REPETITIVE_NOA, + TE_P2P_GO_CT_WINDOW, + + /* WiDi Sync Events */ + TE_WIDI_TX_SYNC, + + /* Channel Switch NoA */ + TE_CHANNEL_SWITCH_PERIOD, + + TE_MAX +}; /* MAC_EVENT_TYPE_API_E_VER_1 */ + + + +/* Time event - defines for command API v1 */ + +/* + * @TE_V1_FRAG_NONE: fragmentation of the time event is NOT allowed. + * @TE_V1_FRAG_SINGLE: fragmentation of the time event is allowed, but only + * the first fragment is scheduled. + * @TE_V1_FRAG_DUAL: fragmentation of the time event is allowed, but only + * the first 2 fragments are scheduled. + * @TE_V1_FRAG_ENDLESS: fragmentation of the time event is allowed, and any + * number of fragments are valid. + * + * Other than the constant defined above, specifying a fragmentation value 'x' + * means that the event can be fragmented but only the first 'x' will be + * scheduled. + */ +enum { + TE_V1_FRAG_NONE = 0, + TE_V1_FRAG_SINGLE = 1, + TE_V1_FRAG_DUAL = 2, + TE_V1_FRAG_ENDLESS = 0xffffffff +}; + +/* If a Time Event can be fragmented, this is the max number of fragments */ +#define TE_V1_FRAG_MAX_MSK 0x0fffffff +/* Repeat the time event endlessly (until removed) */ +#define TE_V1_REPEAT_ENDLESS 0xffffffff +/* If a Time Event has bounded repetitions, this is the maximal value */ +#define TE_V1_REPEAT_MAX_MSK_V1 0x0fffffff + +/* Time Event dependencies: none, on another TE, or in a specific time */ +enum { + TE_V1_INDEPENDENT = 0, + TE_V1_DEP_OTHER = BIT(0), + TE_V1_DEP_TSF = BIT(1), + TE_V1_EVENT_SOCIOPATHIC = BIT(2), +}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */ + +/* + * @TE_V1_NOTIF_NONE: no notifications + * @TE_V1_NOTIF_HOST_EVENT_START: request/receive notification on event start + * @TE_V1_NOTIF_HOST_EVENT_END:request/receive notification on event end + * @TE_V1_NOTIF_INTERNAL_EVENT_START: internal FW use + * @TE_V1_NOTIF_INTERNAL_EVENT_END: internal FW use. + * @TE_V1_NOTIF_HOST_FRAG_START: request/receive notification on frag start + * @TE_V1_NOTIF_HOST_FRAG_END:request/receive notification on frag end + * @TE_V1_NOTIF_INTERNAL_FRAG_START: internal FW use. + * @TE_V1_NOTIF_INTERNAL_FRAG_END: internal FW use. + * + * Supported Time event notifications configuration. + * A notification (both event and fragment) includes a status indicating weather + * the FW was able to schedule the event or not. For fragment start/end + * notification the status is always success. There is no start/end fragment + * notification for monolithic events. + */ +enum { + TE_V1_NOTIF_NONE = 0, + TE_V1_NOTIF_HOST_EVENT_START = BIT(0), + TE_V1_NOTIF_HOST_EVENT_END = BIT(1), + TE_V1_NOTIF_INTERNAL_EVENT_START = BIT(2), + TE_V1_NOTIF_INTERNAL_EVENT_END = BIT(3), + TE_V1_NOTIF_HOST_FRAG_START = BIT(4), + TE_V1_NOTIF_HOST_FRAG_END = BIT(5), + TE_V1_NOTIF_INTERNAL_FRAG_START = BIT(6), + TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7), +}; /* MAC_EVENT_ACTION_API_E_VER_2 */ + +/* Time event - defines for command API */ + +/* + * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed. + * @TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only + * the first fragment is scheduled. + * @TE_V2_FRAG_DUAL: fragmentation of the time event is allowed, but only + * the first 2 fragments are scheduled. + * @TE_V2_FRAG_ENDLESS: fragmentation of the time event is allowed, and any + * number of fragments are valid. + * + * Other than the constant defined above, specifying a fragmentation value 'x' + * means that the event can be fragmented but only the first 'x' will be + * scheduled. + */ +enum { + TE_V2_FRAG_NONE = 0, + TE_V2_FRAG_SINGLE = 1, + TE_V2_FRAG_DUAL = 2, + TE_V2_FRAG_MAX = 0xfe, + TE_V2_FRAG_ENDLESS = 0xff +}; + +/* Repeat the time event endlessly (until removed) */ +#define TE_V2_REPEAT_ENDLESS 0xff +/* If a Time Event has bounded repetitions, this is the maximal value */ +#define TE_V2_REPEAT_MAX 0xfe + +#define TE_V2_PLACEMENT_POS 12 +#define TE_V2_ABSENCE_POS 15 + +/* Time event policy values + * A notification (both event and fragment) includes a status indicating weather + * the FW was able to schedule the event or not. For fragment start/end + * notification the status is always success. There is no start/end fragment + * notification for monolithic events. + * + * @TE_V2_DEFAULT_POLICY: independent, social, present, unoticable + * @TE_V2_NOTIF_HOST_EVENT_START: request/receive notification on event start + * @TE_V2_NOTIF_HOST_EVENT_END:request/receive notification on event end + * @TE_V2_NOTIF_INTERNAL_EVENT_START: internal FW use + * @TE_V2_NOTIF_INTERNAL_EVENT_END: internal FW use. + * @TE_V2_NOTIF_HOST_FRAG_START: request/receive notification on frag start + * @TE_V2_NOTIF_HOST_FRAG_END:request/receive notification on frag end + * @TE_V2_NOTIF_INTERNAL_FRAG_START: internal FW use. + * @TE_V2_NOTIF_INTERNAL_FRAG_END: internal FW use. + * @TE_V2_DEP_OTHER: depends on another time event + * @TE_V2_DEP_TSF: depends on a specific time + * @TE_V2_EVENT_SOCIOPATHIC: can't co-exist with other events of tha same MAC + * @TE_V2_ABSENCE: are we present or absent during the Time Event. + */ +enum { + TE_V2_DEFAULT_POLICY = 0x0, + + /* notifications (event start/stop, fragment start/stop) */ + TE_V2_NOTIF_HOST_EVENT_START = BIT(0), + TE_V2_NOTIF_HOST_EVENT_END = BIT(1), + TE_V2_NOTIF_INTERNAL_EVENT_START = BIT(2), + TE_V2_NOTIF_INTERNAL_EVENT_END = BIT(3), + + TE_V2_NOTIF_HOST_FRAG_START = BIT(4), + TE_V2_NOTIF_HOST_FRAG_END = BIT(5), + TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6), + TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7), + T2_V2_START_IMMEDIATELY = BIT(11), + + TE_V2_NOTIF_MSK = 0xff, + + /* placement characteristics */ + TE_V2_DEP_OTHER = BIT(TE_V2_PLACEMENT_POS), + TE_V2_DEP_TSF = BIT(TE_V2_PLACEMENT_POS + 1), + TE_V2_EVENT_SOCIOPATHIC = BIT(TE_V2_PLACEMENT_POS + 2), + + /* are we present or absent during the Time Event. */ + TE_V2_ABSENCE = BIT(TE_V2_ABSENCE_POS), +}; + +/** + * struct iwl_time_event_cmd_api - configuring Time Events + * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also + * with version 1. determined by IWL_UCODE_TLV_FLAGS) + * ( TIME_EVENT_CMD = 0x29 ) + * @id_and_color: ID and color of the relevant MAC + * @action: action to perform, one of FW_CTXT_ACTION_* + * @id: this field has two meanings, depending on the action: + * If the action is ADD, then it means the type of event to add. + * For all other actions it is the unique event ID assigned when the + * event was added by the FW. + * @apply_time: When to start the Time Event (in GP2) + * @max_delay: maximum delay to event's start (apply time), in TU + * @depends_on: the unique ID of the event we depend on (if any) + * @interval: interval between repetitions, in TU + * @duration: duration of event in TU + * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS + * @max_frags: maximal number of fragments the Time Event can be divided to + * @policy: defines whether uCode shall notify the host or other uCode modules + * on event and/or fragment start and/or end + * using one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF + * TE_EVENT_SOCIOPATHIC + * using TE_ABSENCE and using TE_NOTIF_* + */ +struct iwl_time_event_cmd { + /* COMMON_INDEX_HDR_API_S_VER_1 */ + __le32 id_and_color; + __le32 action; + __le32 id; + /* MAC_TIME_EVENT_DATA_API_S_VER_2 */ + __le32 apply_time; + __le32 max_delay; + __le32 depends_on; + __le32 interval; + __le32 duration; + u8 repeat; + u8 max_frags; + __le16 policy; +} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_2 */ + +/** + * struct iwl_time_event_resp - response structure to iwl_time_event_cmd + * @status: bit 0 indicates success, all others specify errors + * @id: the Time Event type + * @unique_id: the unique ID assigned (in ADD) or given (others) to the TE + * @id_and_color: ID and color of the relevant MAC + */ +struct iwl_time_event_resp { + __le32 status; + __le32 id; + __le32 unique_id; + __le32 id_and_color; +} __packed; /* MAC_TIME_EVENT_RSP_API_S_VER_1 */ + +/** + * struct iwl_time_event_notif - notifications of time event start/stop + * ( TIME_EVENT_NOTIFICATION = 0x2a ) + * @timestamp: action timestamp in GP2 + * @session_id: session's unique id + * @unique_id: unique id of the Time Event itself + * @id_and_color: ID and color of the relevant MAC + * @action: one of TE_NOTIF_START or TE_NOTIF_END + * @status: true if scheduled, false otherwise (not executed) + */ +struct iwl_time_event_notif { + __le32 timestamp; + __le32 session_id; + __le32 unique_id; + __le32 id_and_color; + __le32 action; + __le32 status; +} __packed; /* MAC_TIME_EVENT_NTFY_API_S_VER_1 */ + + +/* Bindings and Time Quota */ + +/** + * struct iwl_binding_cmd - configuring bindings + * ( BINDING_CONTEXT_CMD = 0x2b ) + * @id_and_color: ID and color of the relevant Binding + * @action: action to perform, one of FW_CTXT_ACTION_* + * @macs: array of MAC id and colors which belong to the binding + * @phy: PHY id and color which belongs to the binding + */ +struct iwl_binding_cmd { + /* COMMON_INDEX_HDR_API_S_VER_1 */ + __le32 id_and_color; + __le32 action; + /* BINDING_DATA_API_S_VER_1 */ + __le32 macs[MAX_MACS_IN_BINDING]; + __le32 phy; +} __packed; /* BINDING_CMD_API_S_VER_1 */ + +/* The maximal number of fragments in the FW's schedule session */ +#define IWL_MVM_MAX_QUOTA 128 + +/** + * struct iwl_time_quota_data - configuration of time quota per binding + * @id_and_color: ID and color of the relevant Binding + * @quota: absolute time quota in TU. The scheduler will try to divide the + * remainig quota (after Time Events) according to this quota. + * @max_duration: max uninterrupted context duration in TU + */ +struct iwl_time_quota_data { + __le32 id_and_color; + __le32 quota; + __le32 max_duration; +} __packed; /* TIME_QUOTA_DATA_API_S_VER_1 */ + +/** + * struct iwl_time_quota_cmd - configuration of time quota between bindings + * ( TIME_QUOTA_CMD = 0x2c ) + * @quotas: allocations per binding + */ +struct iwl_time_quota_cmd { + struct iwl_time_quota_data quotas[MAX_BINDINGS]; +} __packed; /* TIME_QUOTA_ALLOCATION_CMD_API_S_VER_1 */ + + +/* PHY context */ + +/* Supported bands */ +#define PHY_BAND_5 (0) +#define PHY_BAND_24 (1) + +/* Supported channel width, vary if there is VHT support */ +#define PHY_VHT_CHANNEL_MODE20 (0x0) +#define PHY_VHT_CHANNEL_MODE40 (0x1) +#define PHY_VHT_CHANNEL_MODE80 (0x2) +#define PHY_VHT_CHANNEL_MODE160 (0x3) + +/* + * Control channel position: + * For legacy set bit means upper channel, otherwise lower. + * For VHT - bit-2 marks if the control is lower/upper relative to center-freq + * bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0. + * center_freq + * | + * 40Mhz |_______|_______| + * 80Mhz |_______|_______|_______|_______| + * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______| + * code 011 010 001 000 | 100 101 110 111 + */ +#define PHY_VHT_CTRL_POS_1_BELOW (0x0) +#define PHY_VHT_CTRL_POS_2_BELOW (0x1) +#define PHY_VHT_CTRL_POS_3_BELOW (0x2) +#define PHY_VHT_CTRL_POS_4_BELOW (0x3) +#define PHY_VHT_CTRL_POS_1_ABOVE (0x4) +#define PHY_VHT_CTRL_POS_2_ABOVE (0x5) +#define PHY_VHT_CTRL_POS_3_ABOVE (0x6) +#define PHY_VHT_CTRL_POS_4_ABOVE (0x7) + +/* + * @band: PHY_BAND_* + * @channel: channel number + * @width: PHY_[VHT|LEGACY]_CHANNEL_* + * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_* + */ +struct iwl_fw_channel_info { + u8 band; + u8 channel; + u8 width; + u8 ctrl_pos; +} __packed; + +#define PHY_RX_CHAIN_DRIVER_FORCE_POS (0) +#define PHY_RX_CHAIN_DRIVER_FORCE_MSK \ + (0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS) +#define PHY_RX_CHAIN_VALID_POS (1) +#define PHY_RX_CHAIN_VALID_MSK \ + (0x7 << PHY_RX_CHAIN_VALID_POS) +#define PHY_RX_CHAIN_FORCE_SEL_POS (4) +#define PHY_RX_CHAIN_FORCE_SEL_MSK \ + (0x7 << PHY_RX_CHAIN_FORCE_SEL_POS) +#define PHY_RX_CHAIN_FORCE_MIMO_SEL_POS (7) +#define PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK \ + (0x7 << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS) +#define PHY_RX_CHAIN_CNT_POS (10) +#define PHY_RX_CHAIN_CNT_MSK \ + (0x3 << PHY_RX_CHAIN_CNT_POS) +#define PHY_RX_CHAIN_MIMO_CNT_POS (12) +#define PHY_RX_CHAIN_MIMO_CNT_MSK \ + (0x3 << PHY_RX_CHAIN_MIMO_CNT_POS) +#define PHY_RX_CHAIN_MIMO_FORCE_POS (14) +#define PHY_RX_CHAIN_MIMO_FORCE_MSK \ + (0x1 << PHY_RX_CHAIN_MIMO_FORCE_POS) + +/* TODO: fix the value, make it depend on firmware at runtime? */ +#define NUM_PHY_CTX 3 + +/* TODO: complete missing documentation */ +/** + * struct iwl_phy_context_cmd - config of the PHY context + * ( PHY_CONTEXT_CMD = 0x8 ) + * @id_and_color: ID and color of the relevant Binding + * @action: action to perform, one of FW_CTXT_ACTION_* + * @apply_time: 0 means immediate apply and context switch. + * other value means apply new params after X usecs + * @tx_param_color: ??? + * @channel_info: + * @txchain_info: ??? + * @rxchain_info: ??? + * @acquisition_data: ??? + * @dsp_cfg_flags: set to 0 + */ +struct iwl_phy_context_cmd { + /* COMMON_INDEX_HDR_API_S_VER_1 */ + __le32 id_and_color; + __le32 action; + /* PHY_CONTEXT_DATA_API_S_VER_1 */ + __le32 apply_time; + __le32 tx_param_color; + struct iwl_fw_channel_info ci; + __le32 txchain_info; + __le32 rxchain_info; + __le32 acquisition_data; + __le32 dsp_cfg_flags; +} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */ + +/* + * Aux ROC command + * + * Command requests the firmware to create a time event for a certain duration + * and remain on the given channel. This is done by using the Aux framework in + * the FW. + * The command was first used for Hot Spot issues - but can be used regardless + * to Hot Spot. + * + * ( HOT_SPOT_CMD 0x53 ) + * + * @id_and_color: ID and color of the MAC + * @action: action to perform, one of FW_CTXT_ACTION_* + * @event_unique_id: If the action FW_CTXT_ACTION_REMOVE then the + * event_unique_id should be the id of the time event assigned by ucode. + * Otherwise ignore the event_unique_id. + * @sta_id_and_color: station id and color, resumed during "Remain On Channel" + * activity. + * @channel_info: channel info + * @node_addr: Our MAC Address + * @reserved: reserved for alignment + * @apply_time: GP2 value to start (should always be the current GP2 value) + * @apply_time_max_delay: Maximum apply time delay value in TU. Defines max + * time by which start of the event is allowed to be postponed. + * @duration: event duration in TU To calculate event duration: + * timeEventDuration = min(duration, remainingQuota) + */ +struct iwl_hs20_roc_req { + /* COMMON_INDEX_HDR_API_S_VER_1 hdr */ + __le32 id_and_color; + __le32 action; + __le32 event_unique_id; + __le32 sta_id_and_color; + struct iwl_fw_channel_info channel_info; + u8 node_addr[ETH_ALEN]; + __le16 reserved; + __le32 apply_time; + __le32 apply_time_max_delay; + __le32 duration; +} __packed; /* HOT_SPOT_CMD_API_S_VER_1 */ + +/* + * values for AUX ROC result values + */ +enum iwl_mvm_hot_spot { + HOT_SPOT_RSP_STATUS_OK, + HOT_SPOT_RSP_STATUS_TOO_MANY_EVENTS, + HOT_SPOT_MAX_NUM_OF_SESSIONS, +}; + +/* + * Aux ROC command response + * + * In response to iwl_hs20_roc_req the FW sends this command to notify the + * driver the uid of the timevent. + * + * ( HOT_SPOT_CMD 0x53 ) + * + * @event_unique_id: Unique ID of time event assigned by ucode + * @status: Return status 0 is success, all the rest used for specific errors + */ +struct iwl_hs20_roc_res { + __le32 event_unique_id; + __le32 status; +} __packed; /* HOT_SPOT_RSP_API_S_VER_1 */ + +/** + * struct iwl_radio_version_notif - information on the radio version + * ( RADIO_VERSION_NOTIFICATION = 0x68 ) + * @radio_flavor: + * @radio_step: + * @radio_dash: + */ +struct iwl_radio_version_notif { + __le32 radio_flavor; + __le32 radio_step; + __le32 radio_dash; +} __packed; /* RADIO_VERSION_NOTOFICATION_S_VER_1 */ + +enum iwl_card_state_flags { + CARD_ENABLED = 0x00, + HW_CARD_DISABLED = 0x01, + SW_CARD_DISABLED = 0x02, + CT_KILL_CARD_DISABLED = 0x04, + HALT_CARD_DISABLED = 0x08, + CARD_DISABLED_MSK = 0x0f, + CARD_IS_RX_ON = 0x10, +}; + +/** + * struct iwl_radio_version_notif - information on the radio version + * ( CARD_STATE_NOTIFICATION = 0xa1 ) + * @flags: %iwl_card_state_flags + */ +struct iwl_card_state_notif { + __le32 flags; +} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */ + +/** + * struct iwl_missed_beacons_notif - information on missed beacons + * ( MISSED_BEACONS_NOTIFICATION = 0xa2 ) + * @mac_id: interface ID + * @consec_missed_beacons_since_last_rx: number of consecutive missed + * beacons since last RX. + * @consec_missed_beacons: number of consecutive missed beacons + * @num_expected_beacons: + * @num_recvd_beacons: + */ +struct iwl_missed_beacons_notif { + __le32 mac_id; + __le32 consec_missed_beacons_since_last_rx; + __le32 consec_missed_beacons; + __le32 num_expected_beacons; + __le32 num_recvd_beacons; +} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */ + +/** + * struct iwl_mfuart_load_notif - mfuart image version & status + * ( MFUART_LOAD_NOTIFICATION = 0xb1 ) + * @installed_ver: installed image version + * @external_ver: external image version + * @status: MFUART loading status + * @duration: MFUART loading time +*/ +struct iwl_mfuart_load_notif { + __le32 installed_ver; + __le32 external_ver; + __le32 status; + __le32 duration; +} __packed; /*MFU_LOADER_NTFY_API_S_VER_1*/ + +/** + * struct iwl_set_calib_default_cmd - set default value for calibration. + * ( SET_CALIB_DEFAULT_CMD = 0x8e ) + * @calib_index: the calibration to set value for + * @length: of data + * @data: the value to set for the calibration result + */ +struct iwl_set_calib_default_cmd { + __le16 calib_index; + __le16 length; + u8 data[0]; +} __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */ + +#define MAX_PORT_ID_NUM 2 +#define MAX_MCAST_FILTERING_ADDRESSES 256 + +/** + * struct iwl_mcast_filter_cmd - configure multicast filter. + * @filter_own: Set 1 to filter out multicast packets sent by station itself + * @port_id: Multicast MAC addresses array specifier. This is a strange way + * to identify network interface adopted in host-device IF. + * It is used by FW as index in array of addresses. This array has + * MAX_PORT_ID_NUM members. + * @count: Number of MAC addresses in the array + * @pass_all: Set 1 to pass all multicast packets. + * @bssid: current association BSSID. + * @addr_list: Place holder for array of MAC addresses. + * IMPORTANT: add padding if necessary to ensure DWORD alignment. + */ +struct iwl_mcast_filter_cmd { + u8 filter_own; + u8 port_id; + u8 count; + u8 pass_all; + u8 bssid[6]; + u8 reserved[2]; + u8 addr_list[0]; +} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */ + +#define MAX_BCAST_FILTERS 8 +#define MAX_BCAST_FILTER_ATTRS 2 + +/** + * enum iwl_mvm_bcast_filter_attr_offset - written by fw for each Rx packet + * @BCAST_FILTER_OFFSET_PAYLOAD_START: offset is from payload start. + * @BCAST_FILTER_OFFSET_IP_END: offset is from ip header end (i.e. + * start of ip payload). + */ +enum iwl_mvm_bcast_filter_attr_offset { + BCAST_FILTER_OFFSET_PAYLOAD_START = 0, + BCAST_FILTER_OFFSET_IP_END = 1, +}; + +/** + * struct iwl_fw_bcast_filter_attr - broadcast filter attribute + * @offset_type: &enum iwl_mvm_bcast_filter_attr_offset. + * @offset: starting offset of this pattern. + * @val: value to match - big endian (MSB is the first + * byte to match from offset pos). + * @mask: mask to match (big endian). + */ +struct iwl_fw_bcast_filter_attr { + u8 offset_type; + u8 offset; + __le16 reserved1; + __be32 val; + __be32 mask; +} __packed; /* BCAST_FILTER_ATT_S_VER_1 */ + +/** + * enum iwl_mvm_bcast_filter_frame_type - filter frame type + * @BCAST_FILTER_FRAME_TYPE_ALL: consider all frames. + * @BCAST_FILTER_FRAME_TYPE_IPV4: consider only ipv4 frames + */ +enum iwl_mvm_bcast_filter_frame_type { + BCAST_FILTER_FRAME_TYPE_ALL = 0, + BCAST_FILTER_FRAME_TYPE_IPV4 = 1, +}; + +/** + * struct iwl_fw_bcast_filter - broadcast filter + * @discard: discard frame (1) or let it pass (0). + * @frame_type: &enum iwl_mvm_bcast_filter_frame_type. + * @num_attrs: number of valid attributes in this filter. + * @attrs: attributes of this filter. a filter is considered matched + * only when all its attributes are matched (i.e. AND relationship) + */ +struct iwl_fw_bcast_filter { + u8 discard; + u8 frame_type; + u8 num_attrs; + u8 reserved1; + struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS]; +} __packed; /* BCAST_FILTER_S_VER_1 */ + +/** + * struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration. + * @default_discard: default action for this mac (discard (1) / pass (0)). + * @attached_filters: bitmap of relevant filters for this mac. + */ +struct iwl_fw_bcast_mac { + u8 default_discard; + u8 reserved1; + __le16 attached_filters; +} __packed; /* BCAST_MAC_CONTEXT_S_VER_1 */ + +/** + * struct iwl_bcast_filter_cmd - broadcast filtering configuration + * @disable: enable (0) / disable (1) + * @max_bcast_filters: max number of filters (MAX_BCAST_FILTERS) + * @max_macs: max number of macs (NUM_MAC_INDEX_DRIVER) + * @filters: broadcast filters + * @macs: broadcast filtering configuration per-mac + */ +struct iwl_bcast_filter_cmd { + u8 disable; + u8 max_bcast_filters; + u8 max_macs; + u8 reserved1; + struct iwl_fw_bcast_filter filters[MAX_BCAST_FILTERS]; + struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER]; +} __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */ + +/* + * enum iwl_mvm_marker_id - maker ids + * + * The ids for different type of markers to insert into the usniffer logs + */ +enum iwl_mvm_marker_id { + MARKER_ID_TX_FRAME_LATENCY = 1, +}; /* MARKER_ID_API_E_VER_1 */ + +/** + * struct iwl_mvm_marker - mark info into the usniffer logs + * + * (MARKER_CMD = 0xcb) + * + * Mark the UTC time stamp into the usniffer logs together with additional + * metadata, so the usniffer output can be parsed. + * In the command response the ucode will return the GP2 time. + * + * @dw_len: The amount of dwords following this byte including this byte. + * @marker_id: A unique marker id (iwl_mvm_marker_id). + * @reserved: reserved. + * @timestamp: in milliseconds since 1970-01-01 00:00:00 UTC + * @metadata: additional meta data that will be written to the unsiffer log + */ +struct iwl_mvm_marker { + u8 dwLen; + u8 markerId; + __le16 reserved; + __le64 timestamp; + __le32 metadata[0]; +} __packed; /* MARKER_API_S_VER_1 */ + +/* + * enum iwl_dc2dc_config_id - flag ids + * + * Ids of dc2dc configuration flags + */ +enum iwl_dc2dc_config_id { + DCDC_LOW_POWER_MODE_MSK_SET = 0x1, /* not used */ + DCDC_FREQ_TUNE_SET = 0x2, +}; /* MARKER_ID_API_E_VER_1 */ + +/** + * struct iwl_dc2dc_config_cmd - configure dc2dc values + * + * (DC2DC_CONFIG_CMD = 0x83) + * + * Set/Get & configure dc2dc values. + * The command always returns the current dc2dc values. + * + * @flags: set/get dc2dc + * @enable_low_power_mode: not used. + * @dc2dc_freq_tune0: frequency divider - digital domain + * @dc2dc_freq_tune1: frequency divider - analog domain + */ +struct iwl_dc2dc_config_cmd { + __le32 flags; + __le32 enable_low_power_mode; /* not used */ + __le32 dc2dc_freq_tune0; + __le32 dc2dc_freq_tune1; +} __packed; /* DC2DC_CONFIG_CMD_API_S_VER_1 */ + +/** + * struct iwl_dc2dc_config_resp - response for iwl_dc2dc_config_cmd + * + * Current dc2dc values returned by the FW. + * + * @dc2dc_freq_tune0: frequency divider - digital domain + * @dc2dc_freq_tune1: frequency divider - analog domain + */ +struct iwl_dc2dc_config_resp { + __le32 dc2dc_freq_tune0; + __le32 dc2dc_freq_tune1; +} __packed; /* DC2DC_CONFIG_RESP_API_S_VER_1 */ + +/*********************************** + * Smart Fifo API + ***********************************/ +/* Smart Fifo state */ +enum iwl_sf_state { + SF_LONG_DELAY_ON = 0, /* should never be called by driver */ + SF_FULL_ON, + SF_UNINIT, + SF_INIT_OFF, + SF_HW_NUM_STATES +}; + +/* Smart Fifo possible scenario */ +enum iwl_sf_scenario { + SF_SCENARIO_SINGLE_UNICAST, + SF_SCENARIO_AGG_UNICAST, + SF_SCENARIO_MULTICAST, + SF_SCENARIO_BA_RESP, + SF_SCENARIO_TX_RESP, + SF_NUM_SCENARIO +}; + +#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */ +#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */ + +/* smart FIFO default values */ +#define SF_W_MARK_SISO 6144 +#define SF_W_MARK_MIMO2 8192 +#define SF_W_MARK_MIMO3 6144 +#define SF_W_MARK_LEGACY 4096 +#define SF_W_MARK_SCAN 4096 + +/* SF Scenarios timers for default configuration (aligned to 32 uSec) */ +#define SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_SINGLE_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_AGG_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_AGG_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_MCAST_IDLE_TIMER_DEF 160 /* 150 mSec */ +#define SF_MCAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_BA_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_BA_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define SF_TX_RE_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define SF_TX_RE_AGING_TIMER_DEF 400 /* 0.4 mSec */ + +/* SF Scenarios timers for BSS MAC configuration (aligned to 32 uSec) */ +#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */ +#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */ +#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */ +#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */ +#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */ +#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */ +#define SF_BA_IDLE_TIMER 320 /* 300 uSec */ +#define SF_BA_AGING_TIMER 2016 /* 2 mSec */ +#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */ +#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */ + +#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */ + +#define SF_CFG_DUMMY_NOTIF_OFF BIT(16) + +/** + * Smart Fifo configuration command. + * @state: smart fifo state, types listed in enum %iwl_sf_sate. + * @watermark: Minimum allowed availabe free space in RXF for transient state. + * @long_delay_timeouts: aging and idle timer values for each scenario + * in long delay state. + * @full_on_timeouts: timer values for each scenario in full on state. + */ +struct iwl_sf_cfg_cmd { + __le32 state; + __le32 watermark[SF_TRANSIENT_STATES_NUMBER]; + __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; + __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; +} __packed; /* SF_CFG_API_S_VER_2 */ + +/*********************************** + * Location Aware Regulatory (LAR) API - MCC updates + ***********************************/ + +/** + * struct iwl_mcc_update_cmd - Request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: the source from where we got the MCC, see iwl_mcc_source + * @reserved: reserved for alignment + */ +struct iwl_mcc_update_cmd { + __le16 mcc; + u8 source_id; + u8 reserved; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S */ + +/** + * iwl_mcc_update_resp - response to MCC_UPDATE_CMD. + * Contains the new channel control profile map, if changed, and the new MCC + * (mobile country code). + * The new MCC may be different than what was requested in MCC_UPDATE_CMD. + * @status: see &enum iwl_mcc_update_status + * @mcc: the new applied MCC + * @cap: capabilities for all channels which matches the MCC + * @source_id: the MCC source, see iwl_mcc_source + * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 + * channels, depending on platform) + * @channels: channel control data map, DWORD for each channel. Only the first + * 16bits are used. + */ +struct iwl_mcc_update_resp { + __le32 status; + __le16 mcc; + u8 cap; + u8 source_id; + __le32 n_channels; + __le32 channels[0]; +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S */ + +/** + * struct iwl_mcc_chub_notif - chub notifies of mcc change + * (MCC_CHUB_UPDATE_CMD = 0xc9) + * The Chub (Communication Hub, CommsHUB) is a HW component that connects to + * the cellular and connectivity cores that gets updates of the mcc, and + * notifies the ucode directly of any mcc change. + * The ucode requests the driver to request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: identity of the change originator, see iwl_mcc_source + * @reserved1: reserved for alignment + */ +struct iwl_mcc_chub_notif { + u16 mcc; + u8 source_id; + u8 reserved1; +} __packed; /* LAR_MCC_NOTIFY_S */ + +enum iwl_mcc_update_status { + MCC_RESP_NEW_CHAN_PROFILE, + MCC_RESP_SAME_CHAN_PROFILE, + MCC_RESP_INVALID, + MCC_RESP_NVM_DISABLED, + MCC_RESP_ILLEGAL, + MCC_RESP_LOW_PRIORITY, +}; + +enum iwl_mcc_source { + MCC_SOURCE_OLD_FW = 0, + MCC_SOURCE_ME = 1, + MCC_SOURCE_BIOS = 2, + MCC_SOURCE_3G_LTE_HOST = 3, + MCC_SOURCE_3G_LTE_DEVICE = 4, + MCC_SOURCE_WIFI = 5, + MCC_SOURCE_RESERVED = 6, + MCC_SOURCE_DEFAULT = 7, + MCC_SOURCE_UNINITIALIZED = 8, + MCC_SOURCE_GET_CURRENT = 0x10 +}; + +/* DTS measurements */ + +enum iwl_dts_measurement_flags { + DTS_TRIGGER_CMD_FLAGS_TEMP = BIT(0), + DTS_TRIGGER_CMD_FLAGS_VOLT = BIT(1), +}; + +/** + * iwl_dts_measurement_cmd - request DTS temperature and/or voltage measurements + * + * @flags: indicates which measurements we want as specified in &enum + * iwl_dts_measurement_flags + */ +struct iwl_dts_measurement_cmd { + __le32 flags; +} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_CMD_S */ + +/** +* enum iwl_dts_control_measurement_mode - DTS measurement type +* @DTS_AUTOMATIC: Automatic mode (full SW control). Provide temperature read +* back (latest value. Not waiting for new value). Use automatic +* SW DTS configuration. +* @DTS_REQUEST_READ: Request DTS read. Configure DTS with manual settings, +* trigger DTS reading and provide read back temperature read +* when available. +* @DTS_OVER_WRITE: over-write the DTS temperatures in the SW until next read +* @DTS_DIRECT_WITHOUT_MEASURE: DTS returns its latest temperature result, +* without measurement trigger. +*/ +enum iwl_dts_control_measurement_mode { + DTS_AUTOMATIC = 0, + DTS_REQUEST_READ = 1, + DTS_OVER_WRITE = 2, + DTS_DIRECT_WITHOUT_MEASURE = 3, +}; + +/** +* enum iwl_dts_used - DTS to use or used for measurement in the DTS request +* @DTS_USE_TOP: Top +* @DTS_USE_CHAIN_A: chain A +* @DTS_USE_CHAIN_B: chain B +* @DTS_USE_CHAIN_C: chain C +* @XTAL_TEMPERATURE - read temperature from xtal +*/ +enum iwl_dts_used { + DTS_USE_TOP = 0, + DTS_USE_CHAIN_A = 1, + DTS_USE_CHAIN_B = 2, + DTS_USE_CHAIN_C = 3, + XTAL_TEMPERATURE = 4, +}; + +/** +* enum iwl_dts_bit_mode - bit-mode to use in DTS request read mode +* @DTS_BIT6_MODE: bit 6 mode +* @DTS_BIT8_MODE: bit 8 mode +*/ +enum iwl_dts_bit_mode { + DTS_BIT6_MODE = 0, + DTS_BIT8_MODE = 1, +}; + +/** + * iwl_ext_dts_measurement_cmd - request extended DTS temperature measurements + * @control_mode: see &enum iwl_dts_control_measurement_mode + * @temperature: used when over write DTS mode is selected + * @sensor: set temperature sensor to use. See &enum iwl_dts_used + * @avg_factor: average factor to DTS in request DTS read mode + * @bit_mode: value defines the DTS bit mode to use. See &enum iwl_dts_bit_mode + * @step_duration: step duration for the DTS + */ +struct iwl_ext_dts_measurement_cmd { + __le32 control_mode; + __le32 temperature; + __le32 sensor; + __le32 avg_factor; + __le32 bit_mode; + __le32 step_duration; +} __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */ + +/** + * iwl_dts_measurement_notif - notification received with the measurements + * + * @temp: the measured temperature + * @voltage: the measured voltage + */ +struct iwl_dts_measurement_notif { + __le32 temp; + __le32 voltage; +} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S */ + +/*********************************** + * TDLS API + ***********************************/ + +/* Type of TDLS request */ +enum iwl_tdls_channel_switch_type { + TDLS_SEND_CHAN_SW_REQ = 0, + TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH, + TDLS_MOVE_CH, +}; /* TDLS_STA_CHANNEL_SWITCH_CMD_TYPE_API_E_VER_1 */ + +/** + * Switch timing sub-element in a TDLS channel-switch command + * @frame_timestamp: GP2 timestamp of channel-switch request/response packet + * received from peer + * @max_offchan_duration: What amount of microseconds out of a DTIM is given + * to the TDLS off-channel communication. For instance if the DTIM is + * 200TU and the TDLS peer is to be given 25% of the time, the value + * given will be 50TU, or 50 * 1024 if translated into microseconds. + * @switch_time: switch time the peer sent in its channel switch timing IE + * @switch_timout: switch timeout the peer sent in its channel switch timing IE + */ +struct iwl_tdls_channel_switch_timing { + __le32 frame_timestamp; /* GP2 time of peer packet Rx */ + __le32 max_offchan_duration; /* given in micro-seconds */ + __le32 switch_time; /* given in micro-seconds */ + __le32 switch_timeout; /* given in micro-seconds */ +} __packed; /* TDLS_STA_CHANNEL_SWITCH_TIMING_DATA_API_S_VER_1 */ + +#define IWL_TDLS_CH_SW_FRAME_MAX_SIZE 200 + +/** + * TDLS channel switch frame template + * + * A template representing a TDLS channel-switch request or response frame + * + * @switch_time_offset: offset to the channel switch timing IE in the template + * @tx_cmd: Tx parameters for the frame + * @data: frame data + */ +struct iwl_tdls_channel_switch_frame { + __le32 switch_time_offset; + struct iwl_tx_cmd tx_cmd; + u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE]; +} __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */ + +/** + * TDLS channel switch command + * + * The command is sent to initiate a channel switch and also in response to + * incoming TDLS channel-switch request/response packets from remote peers. + * + * @switch_type: see &enum iwl_tdls_channel_switch_type + * @peer_sta_id: station id of TDLS peer + * @ci: channel we switch to + * @timing: timing related data for command + * @frame: channel-switch request/response template, depending to switch_type + */ +struct iwl_tdls_channel_switch_cmd { + u8 switch_type; + __le32 peer_sta_id; + struct iwl_fw_channel_info ci; + struct iwl_tdls_channel_switch_timing timing; + struct iwl_tdls_channel_switch_frame frame; +} __packed; /* TDLS_STA_CHANNEL_SWITCH_CMD_API_S_VER_1 */ + +/** + * TDLS channel switch start notification + * + * @status: non-zero on success + * @offchannel_duration: duration given in microseconds + * @sta_id: peer currently performing the channel-switch with + */ +struct iwl_tdls_channel_switch_notif { + __le32 status; + __le32 offchannel_duration; + __le32 sta_id; +} __packed; /* TDLS_STA_CHANNEL_SWITCH_NTFY_API_S_VER_1 */ + +/** + * TDLS station info + * + * @sta_id: station id of the TDLS peer + * @tx_to_peer_tid: TID reserved vs. the peer for FW based Tx + * @tx_to_peer_ssn: initial SSN the FW should use for Tx on its TID vs the peer + * @is_initiator: 1 if the peer is the TDLS link initiator, 0 otherwise + */ +struct iwl_tdls_sta_info { + u8 sta_id; + u8 tx_to_peer_tid; + __le16 tx_to_peer_ssn; + __le32 is_initiator; +} __packed; /* TDLS_STA_INFO_VER_1 */ + +/** + * TDLS basic config command + * + * @id_and_color: MAC id and color being configured + * @tdls_peer_count: amount of currently connected TDLS peers + * @tx_to_ap_tid: TID reverved vs. the AP for FW based Tx + * @tx_to_ap_ssn: initial SSN the FW should use for Tx on its TID vs. the AP + * @sta_info: per-station info. Only the first tdls_peer_count entries are set + * @pti_req_data_offset: offset of network-level data for the PTI template + * @pti_req_tx_cmd: Tx parameters for PTI request template + * @pti_req_template: PTI request template data + */ +struct iwl_tdls_config_cmd { + __le32 id_and_color; /* mac id and color */ + u8 tdls_peer_count; + u8 tx_to_ap_tid; + __le16 tx_to_ap_ssn; + struct iwl_tdls_sta_info sta_info[IWL_MVM_TDLS_STA_COUNT]; + + __le32 pti_req_data_offset; + struct iwl_tx_cmd pti_req_tx_cmd; + u8 pti_req_template[0]; +} __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */ + +/** + * TDLS per-station config information from FW + * + * @sta_id: station id of the TDLS peer + * @tx_to_peer_last_seq: last sequence number used by FW during FW-based Tx to + * the peer + */ +struct iwl_tdls_config_sta_info_res { + __le16 sta_id; + __le16 tx_to_peer_last_seq; +} __packed; /* TDLS_STA_INFO_RSP_VER_1 */ + +/** + * TDLS config information from FW + * + * @tx_to_ap_last_seq: last sequence number used by FW during FW-based Tx to AP + * @sta_info: per-station TDLS config information + */ +struct iwl_tdls_config_res { + __le32 tx_to_ap_last_seq; + struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT]; +} __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */ + +#define TX_FIFO_MAX_NUM 8 +#define RX_FIFO_MAX_NUM 2 + +/** + * Shared memory configuration information from the FW + * + * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not + * accessible) + * @shared_mem_size: shared memory size + * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to + * 0x0 as accessible only via DBGM RDAT) + * @sample_buff_size: internal sample buff size + * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre + * 8000 HW set to 0x0 as not accessible) + * @txfifo_size: size of TXF0 ... TXF7 + * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0 + * @page_buff_addr: used by UMAC and performance debug (page miss analysis), + * when paging is not supported this should be 0 + * @page_buff_size: size of %page_buff_addr + */ +struct iwl_shared_mem_cfg { + __le32 shared_mem_addr; + __le32 shared_mem_size; + __le32 sample_buff_addr; + __le32 sample_buff_size; + __le32 txfifo_addr; + __le32 txfifo_size[TX_FIFO_MAX_NUM]; + __le32 rxfifo_size[RX_FIFO_MAX_NUM]; + __le32 page_buff_addr; + __le32 page_buff_size; +} __packed; /* SHARED_MEM_ALLOC_API_S_VER_1 */ + +#endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c new file mode 100644 index 000000000000..d906fa13ba97 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -0,0 +1,1166 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include + +#include "iwl-trans.h" +#include "iwl-op-mode.h" +#include "iwl-fw.h" +#include "iwl-debug.h" +#include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */ +#include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */ +#include "iwl-prph.h" +#include "iwl-eeprom-parse.h" + +#include "mvm.h" +#include "iwl-phy-db.h" + +#define MVM_UCODE_ALIVE_TIMEOUT HZ +#define MVM_UCODE_CALIB_TIMEOUT (2*HZ) + +#define UCODE_VALID_OK cpu_to_le32(0x1) + +struct iwl_mvm_alive_data { + bool valid; + u32 scd_base_addr; +}; + +static inline const struct fw_img * +iwl_get_ucode_image(struct iwl_mvm *mvm, enum iwl_ucode_type ucode_type) +{ + if (ucode_type >= IWL_UCODE_TYPE_MAX) + return NULL; + + return &mvm->fw->img[ucode_type]; +} + +static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant) +{ + struct iwl_tx_ant_cfg_cmd tx_ant_cmd = { + .valid = cpu_to_le32(valid_tx_ant), + }; + + IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant); + return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, 0, + sizeof(tx_ant_cmd), &tx_ant_cmd); +} + +static void iwl_free_fw_paging(struct iwl_mvm *mvm) +{ + int i; + + if (!mvm->fw_paging_db[0].fw_paging_block) + return; + + for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) { + if (!mvm->fw_paging_db[i].fw_paging_block) { + IWL_DEBUG_FW(mvm, + "Paging: block %d already freed, continue to next page\n", + i); + + continue; + } + + __free_pages(mvm->fw_paging_db[i].fw_paging_block, + get_order(mvm->fw_paging_db[i].fw_paging_size)); + } + kfree(mvm->trans->paging_download_buf); + memset(mvm->fw_paging_db, 0, sizeof(mvm->fw_paging_db)); +} + +static int iwl_fill_paging_mem(struct iwl_mvm *mvm, const struct fw_img *image) +{ + int sec_idx, idx; + u32 offset = 0; + + /* + * find where is the paging image start point: + * if CPU2 exist and it's in paging format, then the image looks like: + * CPU1 sections (2 or more) + * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2 + * CPU2 sections (not paged) + * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2 + * non paged to CPU2 paging sec + * CPU2 paging CSS + * CPU2 paging image (including instruction and data) + */ + for (sec_idx = 0; sec_idx < IWL_UCODE_SECTION_MAX; sec_idx++) { + if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) { + sec_idx++; + break; + } + } + + if (sec_idx >= IWL_UCODE_SECTION_MAX) { + IWL_ERR(mvm, "driver didn't find paging image\n"); + iwl_free_fw_paging(mvm); + return -EINVAL; + } + + /* copy the CSS block to the dram */ + IWL_DEBUG_FW(mvm, "Paging: load paging CSS to FW, sec = %d\n", + sec_idx); + + memcpy(page_address(mvm->fw_paging_db[0].fw_paging_block), + image->sec[sec_idx].data, + mvm->fw_paging_db[0].fw_paging_size); + + IWL_DEBUG_FW(mvm, + "Paging: copied %d CSS bytes to first block\n", + mvm->fw_paging_db[0].fw_paging_size); + + sec_idx++; + + /* + * copy the paging blocks to the dram + * loop index start from 1 since that CSS block already copied to dram + * and CSS index is 0. + * loop stop at num_of_paging_blk since that last block is not full. + */ + for (idx = 1; idx < mvm->num_of_paging_blk; idx++) { + memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block), + image->sec[sec_idx].data + offset, + mvm->fw_paging_db[idx].fw_paging_size); + + IWL_DEBUG_FW(mvm, + "Paging: copied %d paging bytes to block %d\n", + mvm->fw_paging_db[idx].fw_paging_size, + idx); + + offset += mvm->fw_paging_db[idx].fw_paging_size; + } + + /* copy the last paging block */ + if (mvm->num_of_pages_in_last_blk > 0) { + memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block), + image->sec[sec_idx].data + offset, + FW_PAGING_SIZE * mvm->num_of_pages_in_last_blk); + + IWL_DEBUG_FW(mvm, + "Paging: copied %d pages in the last block %d\n", + mvm->num_of_pages_in_last_blk, idx); + } + + return 0; +} + +static int iwl_alloc_fw_paging_mem(struct iwl_mvm *mvm, + const struct fw_img *image) +{ + struct page *block; + dma_addr_t phys = 0; + int blk_idx = 0; + int order, num_of_pages; + int dma_enabled; + + if (mvm->fw_paging_db[0].fw_paging_block) + return 0; + + dma_enabled = is_device_dma_capable(mvm->trans->dev); + + /* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */ + BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE); + + num_of_pages = image->paging_mem_size / FW_PAGING_SIZE; + mvm->num_of_paging_blk = ((num_of_pages - 1) / + NUM_OF_PAGE_PER_GROUP) + 1; + + mvm->num_of_pages_in_last_blk = + num_of_pages - + NUM_OF_PAGE_PER_GROUP * (mvm->num_of_paging_blk - 1); + + IWL_DEBUG_FW(mvm, + "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n", + mvm->num_of_paging_blk, + mvm->num_of_pages_in_last_blk); + + /* allocate block of 4Kbytes for paging CSS */ + order = get_order(FW_PAGING_SIZE); + block = alloc_pages(GFP_KERNEL, order); + if (!block) { + /* free all the previous pages since we failed */ + iwl_free_fw_paging(mvm); + return -ENOMEM; + } + + mvm->fw_paging_db[blk_idx].fw_paging_block = block; + mvm->fw_paging_db[blk_idx].fw_paging_size = FW_PAGING_SIZE; + + if (dma_enabled) { + phys = dma_map_page(mvm->trans->dev, block, 0, + PAGE_SIZE << order, DMA_BIDIRECTIONAL); + if (dma_mapping_error(mvm->trans->dev, phys)) { + /* + * free the previous pages and the current one since + * we failed to map_page. + */ + iwl_free_fw_paging(mvm); + return -ENOMEM; + } + mvm->fw_paging_db[blk_idx].fw_paging_phys = phys; + } else { + mvm->fw_paging_db[blk_idx].fw_paging_phys = PAGING_ADDR_SIG | + blk_idx << BLOCK_2_EXP_SIZE; + } + + IWL_DEBUG_FW(mvm, + "Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n", + order); + + /* + * allocate blocks in dram. + * since that CSS allocated in fw_paging_db[0] loop start from index 1 + */ + for (blk_idx = 1; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) { + /* allocate block of PAGING_BLOCK_SIZE (32K) */ + order = get_order(PAGING_BLOCK_SIZE); + block = alloc_pages(GFP_KERNEL, order); + if (!block) { + /* free all the previous pages since we failed */ + iwl_free_fw_paging(mvm); + return -ENOMEM; + } + + mvm->fw_paging_db[blk_idx].fw_paging_block = block; + mvm->fw_paging_db[blk_idx].fw_paging_size = PAGING_BLOCK_SIZE; + + if (dma_enabled) { + phys = dma_map_page(mvm->trans->dev, block, 0, + PAGE_SIZE << order, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(mvm->trans->dev, phys)) { + /* + * free the previous pages and the current one + * since we failed to map_page. + */ + iwl_free_fw_paging(mvm); + return -ENOMEM; + } + mvm->fw_paging_db[blk_idx].fw_paging_phys = phys; + } else { + mvm->fw_paging_db[blk_idx].fw_paging_phys = + PAGING_ADDR_SIG | + blk_idx << BLOCK_2_EXP_SIZE; + } + + IWL_DEBUG_FW(mvm, + "Paging: allocated 32K bytes (order %d) for firmware paging.\n", + order); + } + + return 0; +} + +static int iwl_save_fw_paging(struct iwl_mvm *mvm, + const struct fw_img *fw) +{ + int ret; + + ret = iwl_alloc_fw_paging_mem(mvm, fw); + if (ret) + return ret; + + return iwl_fill_paging_mem(mvm, fw); +} + +/* send paging cmd to FW in case CPU2 has paging image */ +static int iwl_send_paging_cmd(struct iwl_mvm *mvm, const struct fw_img *fw) +{ + int blk_idx; + __le32 dev_phy_addr; + struct iwl_fw_paging_cmd fw_paging_cmd = { + .flags = + cpu_to_le32(PAGING_CMD_IS_SECURED | + PAGING_CMD_IS_ENABLED | + (mvm->num_of_pages_in_last_blk << + PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)), + .block_size = cpu_to_le32(BLOCK_2_EXP_SIZE), + .block_num = cpu_to_le32(mvm->num_of_paging_blk), + }; + + /* loop for for all paging blocks + CSS block */ + for (blk_idx = 0; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) { + dev_phy_addr = + cpu_to_le32(mvm->fw_paging_db[blk_idx].fw_paging_phys >> + PAGE_2_EXP_SIZE); + fw_paging_cmd.device_phy_addr[blk_idx] = dev_phy_addr; + } + + return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(FW_PAGING_BLOCK_CMD, + IWL_ALWAYS_LONG_GROUP, 0), + 0, sizeof(fw_paging_cmd), &fw_paging_cmd); +} + +/* + * Send paging item cmd to FW in case CPU2 has paging image + */ +static int iwl_trans_get_paging_item(struct iwl_mvm *mvm) +{ + int ret; + struct iwl_fw_get_item_cmd fw_get_item_cmd = { + .item_id = cpu_to_le32(IWL_FW_ITEM_ID_PAGING), + }; + + struct iwl_fw_get_item_resp *item_resp; + struct iwl_host_cmd cmd = { + .id = iwl_cmd_id(FW_GET_ITEM_CMD, IWL_ALWAYS_LONG_GROUP, 0), + .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, + .data = { &fw_get_item_cmd, }, + }; + + cmd.len[0] = sizeof(struct iwl_fw_get_item_cmd); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret) { + IWL_ERR(mvm, + "Paging: Failed to send FW_GET_ITEM_CMD cmd (err = %d)\n", + ret); + return ret; + } + + item_resp = (void *)((struct iwl_rx_packet *)cmd.resp_pkt)->data; + if (item_resp->item_id != cpu_to_le32(IWL_FW_ITEM_ID_PAGING)) { + IWL_ERR(mvm, + "Paging: got wrong item in FW_GET_ITEM_CMD resp (item_id = %u)\n", + le32_to_cpu(item_resp->item_id)); + ret = -EIO; + goto exit; + } + + mvm->trans->paging_download_buf = kzalloc(MAX_PAGING_IMAGE_SIZE, + GFP_KERNEL); + if (!mvm->trans->paging_download_buf) { + ret = -ENOMEM; + goto exit; + } + mvm->trans->paging_req_addr = le32_to_cpu(item_resp->item_val); + mvm->trans->paging_db = mvm->fw_paging_db; + IWL_DEBUG_FW(mvm, + "Paging: got paging request address (paging_req_addr 0x%08x)\n", + mvm->trans->paging_req_addr); + +exit: + iwl_free_resp(&cmd); + + return ret; +} + +static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mvm *mvm = + container_of(notif_wait, struct iwl_mvm, notif_wait); + struct iwl_mvm_alive_data *alive_data = data; + struct mvm_alive_resp_ver1 *palive1; + struct mvm_alive_resp_ver2 *palive2; + struct mvm_alive_resp *palive; + + if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive1)) { + palive1 = (void *)pkt->data; + + mvm->support_umac_log = false; + mvm->error_event_table = + le32_to_cpu(palive1->error_event_table_ptr); + mvm->log_event_table = + le32_to_cpu(palive1->log_event_table_ptr); + alive_data->scd_base_addr = le32_to_cpu(palive1->scd_base_ptr); + + alive_data->valid = le16_to_cpu(palive1->status) == + IWL_ALIVE_STATUS_OK; + IWL_DEBUG_FW(mvm, + "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", + le16_to_cpu(palive1->status), palive1->ver_type, + palive1->ver_subtype, palive1->flags); + } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive2)) { + palive2 = (void *)pkt->data; + + mvm->error_event_table = + le32_to_cpu(palive2->error_event_table_ptr); + mvm->log_event_table = + le32_to_cpu(palive2->log_event_table_ptr); + alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr); + mvm->umac_error_event_table = + le32_to_cpu(palive2->error_info_addr); + mvm->sf_space.addr = le32_to_cpu(palive2->st_fwrd_addr); + mvm->sf_space.size = le32_to_cpu(palive2->st_fwrd_size); + + alive_data->valid = le16_to_cpu(palive2->status) == + IWL_ALIVE_STATUS_OK; + if (mvm->umac_error_event_table) + mvm->support_umac_log = true; + + IWL_DEBUG_FW(mvm, + "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", + le16_to_cpu(palive2->status), palive2->ver_type, + palive2->ver_subtype, palive2->flags); + + IWL_DEBUG_FW(mvm, + "UMAC version: Major - 0x%x, Minor - 0x%x\n", + palive2->umac_major, palive2->umac_minor); + } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) { + palive = (void *)pkt->data; + + mvm->error_event_table = + le32_to_cpu(palive->error_event_table_ptr); + mvm->log_event_table = + le32_to_cpu(palive->log_event_table_ptr); + alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr); + mvm->umac_error_event_table = + le32_to_cpu(palive->error_info_addr); + mvm->sf_space.addr = le32_to_cpu(palive->st_fwrd_addr); + mvm->sf_space.size = le32_to_cpu(palive->st_fwrd_size); + + alive_data->valid = le16_to_cpu(palive->status) == + IWL_ALIVE_STATUS_OK; + if (mvm->umac_error_event_table) + mvm->support_umac_log = true; + + IWL_DEBUG_FW(mvm, + "Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", + le16_to_cpu(palive->status), palive->ver_type, + palive->ver_subtype, palive->flags); + + IWL_DEBUG_FW(mvm, + "UMAC version: Major - 0x%x, Minor - 0x%x\n", + le32_to_cpu(palive->umac_major), + le32_to_cpu(palive->umac_minor)); + } + + return true; +} + +static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_phy_db *phy_db = data; + + if (pkt->hdr.cmd != CALIB_RES_NOTIF_PHY_DB) { + WARN_ON(pkt->hdr.cmd != INIT_COMPLETE_NOTIF); + return true; + } + + WARN_ON(iwl_phy_db_set_section(phy_db, pkt, GFP_ATOMIC)); + + return false; +} + +static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, + enum iwl_ucode_type ucode_type) +{ + struct iwl_notification_wait alive_wait; + struct iwl_mvm_alive_data alive_data; + const struct fw_img *fw; + int ret, i; + enum iwl_ucode_type old_type = mvm->cur_ucode; + static const u16 alive_cmd[] = { MVM_ALIVE }; + struct iwl_sf_region st_fwrd_space; + + if (ucode_type == IWL_UCODE_REGULAR && + iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE)) + fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER); + else + fw = iwl_get_ucode_image(mvm, ucode_type); + if (WARN_ON(!fw)) + return -EINVAL; + mvm->cur_ucode = ucode_type; + mvm->ucode_loaded = false; + + iwl_init_notification_wait(&mvm->notif_wait, &alive_wait, + alive_cmd, ARRAY_SIZE(alive_cmd), + iwl_alive_fn, &alive_data); + + ret = iwl_trans_start_fw(mvm->trans, fw, ucode_type == IWL_UCODE_INIT); + if (ret) { + mvm->cur_ucode = old_type; + iwl_remove_notification(&mvm->notif_wait, &alive_wait); + return ret; + } + + /* + * Some things may run in the background now, but we + * just wait for the ALIVE notification here. + */ + ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait, + MVM_UCODE_ALIVE_TIMEOUT); + if (ret) { + if (mvm->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + IWL_ERR(mvm, + "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", + iwl_read_prph(mvm->trans, SB_CPU_1_STATUS), + iwl_read_prph(mvm->trans, SB_CPU_2_STATUS)); + mvm->cur_ucode = old_type; + return ret; + } + + if (!alive_data.valid) { + IWL_ERR(mvm, "Loaded ucode is not valid!\n"); + mvm->cur_ucode = old_type; + return -EIO; + } + + /* + * update the sdio allocation according to the pointer we get in the + * alive notification. + */ + st_fwrd_space.addr = mvm->sf_space.addr; + st_fwrd_space.size = mvm->sf_space.size; + ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space); + if (ret) { + IWL_ERR(mvm, "Failed to update SF size. ret %d\n", ret); + return ret; + } + + iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr); + + /* + * configure and operate fw paging mechanism. + * driver configures the paging flow only once, CPU2 paging image + * included in the IWL_UCODE_INIT image. + */ + if (fw->paging_mem_size) { + /* + * When dma is not enabled, the driver needs to copy / write + * the downloaded / uploaded page to / from the smem. + * This gets the location of the place were the pages are + * stored. + */ + if (!is_device_dma_capable(mvm->trans->dev)) { + ret = iwl_trans_get_paging_item(mvm); + if (ret) { + IWL_ERR(mvm, "failed to get FW paging item\n"); + return ret; + } + } + + ret = iwl_save_fw_paging(mvm, fw); + if (ret) { + IWL_ERR(mvm, "failed to save the FW paging image\n"); + return ret; + } + + ret = iwl_send_paging_cmd(mvm, fw); + if (ret) { + IWL_ERR(mvm, "failed to send the paging cmd\n"); + iwl_free_fw_paging(mvm); + return ret; + } + } + + /* + * Note: all the queues are enabled as part of the interface + * initialization, but in firmware restart scenarios they + * could be stopped, so wake them up. In firmware restart, + * mac80211 will have the queues stopped as well until the + * reconfiguration completes. During normal startup, they + * will be empty. + */ + + memset(&mvm->queue_info, 0, sizeof(mvm->queue_info)); + mvm->queue_info[IWL_MVM_CMD_QUEUE].hw_queue_refcount = 1; + + for (i = 0; i < IEEE80211_MAX_QUEUES; i++) + atomic_set(&mvm->mac80211_queue_stop_count[i], 0); + + mvm->ucode_loaded = true; + + return 0; +} + +static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) +{ + struct iwl_phy_cfg_cmd phy_cfg_cmd; + enum iwl_ucode_type ucode_type = mvm->cur_ucode; + + /* Set parameters */ + phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm)); + phy_cfg_cmd.calib_control.event_trigger = + mvm->fw->default_calib[ucode_type].event_trigger; + phy_cfg_cmd.calib_control.flow_trigger = + mvm->fw->default_calib[ucode_type].flow_trigger; + + IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n", + phy_cfg_cmd.phy_cfg); + + return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, 0, + sizeof(phy_cfg_cmd), &phy_cfg_cmd); +} + +int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) +{ + struct iwl_notification_wait calib_wait; + static const u16 init_complete[] = { + INIT_COMPLETE_NOTIF, + CALIB_RES_NOTIF_PHY_DB + }; + int ret; + + lockdep_assert_held(&mvm->mutex); + + if (WARN_ON_ONCE(mvm->calibrating)) + return 0; + + iwl_init_notification_wait(&mvm->notif_wait, + &calib_wait, + init_complete, + ARRAY_SIZE(init_complete), + iwl_wait_phy_db_entry, + mvm->phy_db); + + /* Will also start the device */ + ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_INIT); + if (ret) { + IWL_ERR(mvm, "Failed to start INIT ucode: %d\n", ret); + goto error; + } + + ret = iwl_send_bt_init_conf(mvm); + if (ret) + goto error; + + /* Read the NVM only at driver load time, no need to do this twice */ + if (read_nvm) { + /* Read nvm */ + ret = iwl_nvm_init(mvm, true); + if (ret) { + IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); + goto error; + } + } + + /* In case we read the NVM from external file, load it to the NIC */ + if (mvm->nvm_file_name) + iwl_mvm_load_nvm_to_nic(mvm); + + ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); + WARN_ON(ret); + + /* + * abort after reading the nvm in case RF Kill is on, we will complete + * the init seq later when RF kill will switch to off + */ + if (iwl_mvm_is_radio_hw_killed(mvm)) { + IWL_DEBUG_RF_KILL(mvm, + "jump over all phy activities due to RF kill\n"); + iwl_remove_notification(&mvm->notif_wait, &calib_wait); + ret = 1; + goto out; + } + + mvm->calibrating = true; + + /* Send TX valid antennas before triggering calibrations */ + ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); + if (ret) + goto error; + + /* + * Send phy configurations command to init uCode + * to start the 16.0 uCode init image internal calibrations. + */ + ret = iwl_send_phy_cfg_cmd(mvm); + if (ret) { + IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n", + ret); + goto error; + } + + /* + * Some things may run in the background now, but we + * just wait for the calibration complete notification. + */ + ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait, + MVM_UCODE_CALIB_TIMEOUT); + + if (ret && iwl_mvm_is_radio_hw_killed(mvm)) { + IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n"); + ret = 1; + } + goto out; + +error: + iwl_remove_notification(&mvm->notif_wait, &calib_wait); +out: + mvm->calibrating = false; + if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) { + /* we want to debug INIT and we have no NVM - fake */ + mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) + + sizeof(struct ieee80211_channel) + + sizeof(struct ieee80211_rate), + GFP_KERNEL); + if (!mvm->nvm_data) + return -ENOMEM; + mvm->nvm_data->bands[0].channels = mvm->nvm_data->channels; + mvm->nvm_data->bands[0].n_channels = 1; + mvm->nvm_data->bands[0].n_bitrates = 1; + mvm->nvm_data->bands[0].bitrates = + (void *)mvm->nvm_data->channels + 1; + mvm->nvm_data->bands[0].bitrates->hw_value = 10; + } + + return ret; +} + +static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm) +{ + struct iwl_host_cmd cmd = { + .id = SHARED_MEM_CFG, + .flags = CMD_WANT_SKB, + .data = { NULL, }, + .len = { 0, }, + }; + struct iwl_rx_packet *pkt; + struct iwl_shared_mem_cfg *mem_cfg; + u32 i; + + lockdep_assert_held(&mvm->mutex); + + if (WARN_ON(iwl_mvm_send_cmd(mvm, &cmd))) + return; + + pkt = cmd.resp_pkt; + mem_cfg = (void *)pkt->data; + + mvm->shared_mem_cfg.shared_mem_addr = + le32_to_cpu(mem_cfg->shared_mem_addr); + mvm->shared_mem_cfg.shared_mem_size = + le32_to_cpu(mem_cfg->shared_mem_size); + mvm->shared_mem_cfg.sample_buff_addr = + le32_to_cpu(mem_cfg->sample_buff_addr); + mvm->shared_mem_cfg.sample_buff_size = + le32_to_cpu(mem_cfg->sample_buff_size); + mvm->shared_mem_cfg.txfifo_addr = le32_to_cpu(mem_cfg->txfifo_addr); + for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) + mvm->shared_mem_cfg.txfifo_size[i] = + le32_to_cpu(mem_cfg->txfifo_size[i]); + for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) + mvm->shared_mem_cfg.rxfifo_size[i] = + le32_to_cpu(mem_cfg->rxfifo_size[i]); + mvm->shared_mem_cfg.page_buff_addr = + le32_to_cpu(mem_cfg->page_buff_addr); + mvm->shared_mem_cfg.page_buff_size = + le32_to_cpu(mem_cfg->page_buff_size); + IWL_DEBUG_INFO(mvm, "SHARED MEM CFG: got memory offsets/sizes\n"); + + iwl_free_resp(&cmd); +} + +int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, + struct iwl_mvm_dump_desc *desc, + struct iwl_fw_dbg_trigger_tlv *trigger) +{ + unsigned int delay = 0; + + if (trigger) + delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); + + if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status)) + return -EBUSY; + + if (WARN_ON(mvm->fw_dump_desc)) + iwl_mvm_free_fw_dump_desc(mvm); + + IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", + le32_to_cpu(desc->trig_desc.type)); + + mvm->fw_dump_desc = desc; + mvm->fw_dump_trig = trigger; + + queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay); + + return 0; +} + +int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, + const char *str, size_t len, + struct iwl_fw_dbg_trigger_tlv *trigger) +{ + struct iwl_mvm_dump_desc *desc; + + desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); + if (!desc) + return -ENOMEM; + + desc->len = len; + desc->trig_desc.type = cpu_to_le32(trig); + memcpy(desc->trig_desc.data, str, len); + + return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger); +} + +int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, + struct iwl_fw_dbg_trigger_tlv *trigger, + const char *fmt, ...) +{ + u16 occurrences = le16_to_cpu(trigger->occurrences); + int ret, len = 0; + char buf[64]; + + if (!occurrences) + return 0; + + if (fmt) { + va_list ap; + + buf[sizeof(buf) - 1] = '\0'; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + /* check for truncation */ + if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) + buf[sizeof(buf) - 1] = '\0'; + + len = strlen(buf) + 1; + } + + ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len, + trigger); + + if (ret) + return ret; + + trigger->occurrences = cpu_to_le16(occurrences - 1); + return 0; +} + +static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm) +{ + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) + iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); + else + iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1); +} + +int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id) +{ + u8 *ptr; + int ret; + int i; + + if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv), + "Invalid configuration %d\n", conf_id)) + return -EINVAL; + + /* EARLY START - firmware's configuration is hard coded */ + if ((!mvm->fw->dbg_conf_tlv[conf_id] || + !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) && + conf_id == FW_DBG_START_FROM_ALIVE) { + iwl_mvm_restart_early_start(mvm); + return 0; + } + + if (!mvm->fw->dbg_conf_tlv[conf_id]) + return -EINVAL; + + if (mvm->fw_dbg_conf != FW_DBG_INVALID) + IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n", + mvm->fw_dbg_conf); + + /* Send all HCMDs for configuring the FW debug */ + ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd; + for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) { + struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr; + + ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0, + le16_to_cpu(cmd->len), cmd->data); + if (ret) + return ret; + + ptr += sizeof(*cmd); + ptr += le16_to_cpu(cmd->len); + } + + mvm->fw_dbg_conf = conf_id; + return ret; +} + +static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) +{ + struct iwl_ltr_config_cmd cmd = { + .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE), + }; + + if (!mvm->trans->ltr_enabled) + return 0; + + return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0, + sizeof(cmd), &cmd); +} + +int iwl_mvm_up(struct iwl_mvm *mvm) +{ + int ret, i; + struct ieee80211_channel *chan; + struct cfg80211_chan_def chandef; + + lockdep_assert_held(&mvm->mutex); + + ret = iwl_trans_start_hw(mvm->trans); + if (ret) + return ret; + + /* + * If we haven't completed the run of the init ucode during + * module loading, load init ucode now + * (for example, if we were in RFKILL) + */ + ret = iwl_run_init_mvm_ucode(mvm, false); + if (ret && !iwlmvm_mod_params.init_dbg) { + IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret); + /* this can't happen */ + if (WARN_ON(ret > 0)) + ret = -ERFKILL; + goto error; + } + if (!iwlmvm_mod_params.init_dbg) { + /* + * Stop and start the transport without entering low power + * mode. This will save the state of other components on the + * device that are triggered by the INIT firwmare (MFUART). + */ + _iwl_trans_stop_device(mvm->trans, false); + ret = _iwl_trans_start_hw(mvm->trans, false); + if (ret) + goto error; + } + + if (iwlmvm_mod_params.init_dbg) + return 0; + + ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); + if (ret) { + IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); + goto error; + } + + iwl_mvm_get_shared_mem_conf(mvm); + + ret = iwl_mvm_sf_update(mvm, NULL, false); + if (ret) + IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); + + mvm->fw_dbg_conf = FW_DBG_INVALID; + /* if we have a destination, assume EARLY START */ + if (mvm->fw->dbg_dest_tlv) + mvm->fw_dbg_conf = FW_DBG_START_FROM_ALIVE; + iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_START_FROM_ALIVE); + + ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); + if (ret) + goto error; + + ret = iwl_send_bt_init_conf(mvm); + if (ret) + goto error; + + /* Send phy db control command and then phy db calibration*/ + ret = iwl_send_phy_db_data(mvm->phy_db); + if (ret) + goto error; + + ret = iwl_send_phy_cfg_cmd(mvm); + if (ret) + goto error; + + /* init the fw <-> mac80211 STA mapping */ + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); + + mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; + + /* reset quota debouncing buffer - 0xff will yield invalid data */ + memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd)); + + /* Add auxiliary station for scanning */ + ret = iwl_mvm_add_aux_sta(mvm); + if (ret) + goto error; + + /* Add all the PHY contexts */ + chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); + for (i = 0; i < NUM_PHY_CTX; i++) { + /* + * The channel used here isn't relevant as it's + * going to be overwritten in the other flows. + * For now use the first channel we have. + */ + ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i], + &chandef, 1, 1); + if (ret) + goto error; + } + + /* Initialize tx backoffs to the minimal possible */ + iwl_mvm_tt_tx_backoff(mvm, 0); + + WARN_ON(iwl_mvm_config_ltr(mvm)); + + ret = iwl_mvm_power_update_device(mvm); + if (ret) + goto error; + + /* + * RTNL is not taken during Ct-kill, but we don't need to scan/Tx + * anyway, so don't init MCC. + */ + if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) { + ret = iwl_mvm_init_mcc(mvm); + if (ret) + goto error; + } + + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { + ret = iwl_mvm_config_scan(mvm); + if (ret) + goto error; + } + + if (iwl_mvm_is_csum_supported(mvm) && + mvm->cfg->features & NETIF_F_RXCSUM) + iwl_trans_write_prph(mvm->trans, RX_EN_CSUM, 0x3); + + /* allow FW/transport low power modes if not during restart */ + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); + + IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); + return 0; + error: + iwl_trans_stop_device(mvm->trans); + return ret; +} + +int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm) +{ + int ret, i; + + lockdep_assert_held(&mvm->mutex); + + ret = iwl_trans_start_hw(mvm->trans); + if (ret) + return ret; + + ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_WOWLAN); + if (ret) { + IWL_ERR(mvm, "Failed to start WoWLAN firmware: %d\n", ret); + goto error; + } + + ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); + if (ret) + goto error; + + /* Send phy db control command and then phy db calibration*/ + ret = iwl_send_phy_db_data(mvm->phy_db); + if (ret) + goto error; + + ret = iwl_send_phy_cfg_cmd(mvm); + if (ret) + goto error; + + /* init the fw <-> mac80211 STA mapping */ + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); + + /* Add auxiliary station for scanning */ + ret = iwl_mvm_add_aux_sta(mvm); + if (ret) + goto error; + + return 0; + error: + iwl_trans_stop_device(mvm->trans); + return ret; +} + +void iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_card_state_notif *card_state_notif = (void *)pkt->data; + u32 flags = le32_to_cpu(card_state_notif->flags); + + IWL_DEBUG_RF_KILL(mvm, "Card state received: HW:%s SW:%s CT:%s\n", + (flags & HW_CARD_DISABLED) ? "Kill" : "On", + (flags & SW_CARD_DISABLED) ? "Kill" : "On", + (flags & CT_KILL_CARD_DISABLED) ? + "Reached" : "Not reached"); +} + +void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data; + + IWL_DEBUG_INFO(mvm, + "MFUART: installed ver: 0x%08x, external ver: 0x%08x, status: 0x%08x, duration: 0x%08x\n", + le32_to_cpu(mfuart_notif->installed_ver), + le32_to_cpu(mfuart_notif->external_ver), + le32_to_cpu(mfuart_notif->status), + le32_to_cpu(mfuart_notif->duration)); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c new file mode 100644 index 000000000000..e3b3cf4dbd77 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c @@ -0,0 +1,136 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include "iwl-io.h" +#include "iwl-csr.h" +#include "mvm.h" + +/* Set led register on */ +static void iwl_mvm_led_enable(struct iwl_mvm *mvm) +{ + iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON); +} + +/* Set led register off */ +static void iwl_mvm_led_disable(struct iwl_mvm *mvm) +{ + iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_OFF); +} + +static void iwl_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct iwl_mvm *mvm = container_of(led_cdev, struct iwl_mvm, led); + if (brightness > 0) + iwl_mvm_led_enable(mvm); + else + iwl_mvm_led_disable(mvm); +} + +int iwl_mvm_leds_init(struct iwl_mvm *mvm) +{ + int mode = iwlwifi_mod_params.led_mode; + int ret; + + switch (mode) { + case IWL_LED_BLINK: + IWL_ERR(mvm, "Blink led mode not supported, used default\n"); + case IWL_LED_DEFAULT: + case IWL_LED_RF_STATE: + mode = IWL_LED_RF_STATE; + break; + case IWL_LED_DISABLE: + IWL_INFO(mvm, "Led disabled\n"); + return 0; + default: + return -EINVAL; + } + + mvm->led.name = kasprintf(GFP_KERNEL, "%s-led", + wiphy_name(mvm->hw->wiphy)); + mvm->led.brightness_set = iwl_led_brightness_set; + mvm->led.max_brightness = 1; + + if (mode == IWL_LED_RF_STATE) + mvm->led.default_trigger = + ieee80211_get_radio_led_name(mvm->hw); + + ret = led_classdev_register(mvm->trans->dev, &mvm->led); + if (ret) { + kfree(mvm->led.name); + IWL_INFO(mvm, "Failed to enable led\n"); + return ret; + } + + return 0; +} + +void iwl_mvm_leds_exit(struct iwl_mvm *mvm) +{ + if (iwlwifi_mod_params.led_mode == IWL_LED_DISABLE) + return; + + led_classdev_unregister(&mvm->led); + kfree(mvm->led.name); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c new file mode 100644 index 000000000000..ad7ad720d2e7 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -0,0 +1,1452 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include "iwl-io.h" +#include "iwl-prph.h" +#include "fw-api.h" +#include "mvm.h" +#include "time-event.h" + +const u8 iwl_mvm_ac_to_tx_fifo[] = { + IWL_MVM_TX_FIFO_VO, + IWL_MVM_TX_FIFO_VI, + IWL_MVM_TX_FIFO_BE, + IWL_MVM_TX_FIFO_BK, +}; + +struct iwl_mvm_mac_iface_iterator_data { + struct iwl_mvm *mvm; + struct ieee80211_vif *vif; + unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)]; + unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)]; + enum iwl_tsf_id preferred_tsf; + bool found_vif; +}; + +struct iwl_mvm_hw_queues_iface_iterator_data { + struct ieee80211_vif *exclude_vif; + unsigned long used_hw_queues; +}; + +static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_mac_iface_iterator_data *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u16 min_bi; + + /* Skip the interface for which we are trying to assign a tsf_id */ + if (vif == data->vif) + return; + + /* + * The TSF is a hardware/firmware resource, there are 4 and + * the driver should assign and free them as needed. However, + * there are cases where 2 MACs should share the same TSF ID + * for the purpose of clock sync, an optimization to avoid + * clock drift causing overlapping TBTTs/DTIMs for a GO and + * client in the system. + * + * The firmware will decide according to the MAC type which + * will be the master and slave. Clients that need to sync + * with a remote station will be the master, and an AP or GO + * will be the slave. + * + * Depending on the new interface type it can be slaved to + * or become the master of an existing interface. + */ + switch (data->vif->type) { + case NL80211_IFTYPE_STATION: + /* + * The new interface is a client, so if the one we're iterating + * is an AP, and the beacon interval of the AP is a multiple or + * divisor of the beacon interval of the client, the same TSF + * should be used to avoid drift between the new client and + * existing AP. The existing AP will get drift updates from the + * new client context in this case. + */ + if (vif->type != NL80211_IFTYPE_AP || + data->preferred_tsf != NUM_TSF_IDS || + !test_bit(mvmvif->tsf_id, data->available_tsf_ids)) + break; + + min_bi = min(data->vif->bss_conf.beacon_int, + vif->bss_conf.beacon_int); + + if (!min_bi) + break; + + if ((data->vif->bss_conf.beacon_int - + vif->bss_conf.beacon_int) % min_bi == 0) { + data->preferred_tsf = mvmvif->tsf_id; + return; + } + break; + + case NL80211_IFTYPE_AP: + /* + * The new interface is AP/GO, so if its beacon interval is a + * multiple or a divisor of the beacon interval of an existing + * interface, it should get drift updates from an existing + * client or use the same TSF as an existing GO. There's no + * drift between TSFs internally but if they used different + * TSFs then a new client MAC could update one of them and + * cause drift that way. + */ + if ((vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_STATION) || + data->preferred_tsf != NUM_TSF_IDS || + !test_bit(mvmvif->tsf_id, data->available_tsf_ids)) + break; + + min_bi = min(data->vif->bss_conf.beacon_int, + vif->bss_conf.beacon_int); + + if (!min_bi) + break; + + if ((data->vif->bss_conf.beacon_int - + vif->bss_conf.beacon_int) % min_bi == 0) { + data->preferred_tsf = mvmvif->tsf_id; + return; + } + break; + default: + /* + * For all other interface types there's no need to + * take drift into account. Either they're exclusive + * like IBSS and monitor, or we don't care much about + * their TSF (like P2P Device), but we won't be able + * to share the TSF resource. + */ + break; + } + + /* + * Unless we exited above, we can't share the TSF resource + * that the virtual interface we're iterating over is using + * with the new one, so clear the available bit and if this + * was the preferred one, reset that as well. + */ + __clear_bit(mvmvif->tsf_id, data->available_tsf_ids); + + if (data->preferred_tsf == mvmvif->tsf_id) + data->preferred_tsf = NUM_TSF_IDS; +} + +/* + * Get the mask of the queues used by the vif + */ +u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif) +{ + u32 qmask = 0, ac; + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) + return BIT(IWL_MVM_OFFCHANNEL_QUEUE); + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) + qmask |= BIT(vif->hw_queue[ac]); + } + + if (vif->type == NL80211_IFTYPE_AP) + qmask |= BIT(vif->cab_queue); + + return qmask; +} + +static void iwl_mvm_iface_hw_queues_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_hw_queues_iface_iterator_data *data = _data; + + /* exclude the given vif */ + if (vif == data->exclude_vif) + return; + + data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif); +} + +static void iwl_mvm_mac_sta_hw_queues_iter(void *_data, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_hw_queues_iface_iterator_data *data = _data; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + + /* Mark the queues used by the sta */ + data->used_hw_queues |= mvmsta->tfd_queue_msk; +} + +unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, + struct ieee80211_vif *exclude_vif) +{ + u8 sta_id; + struct iwl_mvm_hw_queues_iface_iterator_data data = { + .exclude_vif = exclude_vif, + .used_hw_queues = + BIT(IWL_MVM_OFFCHANNEL_QUEUE) | + BIT(mvm->aux_queue) | + BIT(IWL_MVM_CMD_QUEUE), + }; + + lockdep_assert_held(&mvm->mutex); + + /* mark all VIF used hw queues */ + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + iwl_mvm_iface_hw_queues_iter, &data); + + /* don't assign the same hw queues as TDLS stations */ + ieee80211_iterate_stations_atomic(mvm->hw, + iwl_mvm_mac_sta_hw_queues_iter, + &data); + + /* + * Some TDLS stations may be removed but are in the process of being + * drained. Don't touch their queues. + */ + for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) + data.used_hw_queues |= mvm->tfd_drained[sta_id]; + + return data.used_hw_queues; +} + +static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_mac_iface_iterator_data *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + /* Iterator may already find the interface being added -- skip it */ + if (vif == data->vif) { + data->found_vif = true; + return; + } + + /* Mark MAC IDs as used by clearing the available bit, and + * (below) mark TSFs as used if their existing use is not + * compatible with the new interface type. + * No locking or atomic bit operations are needed since the + * data is on the stack of the caller function. + */ + __clear_bit(mvmvif->id, data->available_mac_ids); + + /* find a suitable tsf_id */ + iwl_mvm_mac_tsf_id_iter(_data, mac, vif); +} + +void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_mac_iface_iterator_data data = { + .mvm = mvm, + .vif = vif, + .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 }, + /* no preference yet */ + .preferred_tsf = NUM_TSF_IDS, + }; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + iwl_mvm_mac_tsf_id_iter, &data); + + if (data.preferred_tsf != NUM_TSF_IDS) + mvmvif->tsf_id = data.preferred_tsf; + else if (!test_bit(mvmvif->tsf_id, data.available_tsf_ids)) + mvmvif->tsf_id = find_first_bit(data.available_tsf_ids, + NUM_TSF_IDS); +} + +static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_mac_iface_iterator_data data = { + .mvm = mvm, + .vif = vif, + .available_mac_ids = { (1 << NUM_MAC_INDEX_DRIVER) - 1 }, + .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 }, + /* no preference yet */ + .preferred_tsf = NUM_TSF_IDS, + .found_vif = false, + }; + u32 ac; + int ret, i; + unsigned long used_hw_queues; + + /* + * Allocate a MAC ID and a TSF for this MAC, along with the queues + * and other resources. + */ + + /* + * Before the iterator, we start with all MAC IDs and TSFs available. + * + * During iteration, all MAC IDs are cleared that are in use by other + * virtual interfaces, and all TSF IDs are cleared that can't be used + * by this new virtual interface because they're used by an interface + * that can't share it with the new one. + * At the same time, we check if there's a preferred TSF in the case + * that we should share it with another interface. + */ + + /* Currently, MAC ID 0 should be used only for the managed/IBSS vif */ + switch (vif->type) { + case NL80211_IFTYPE_ADHOC: + break; + case NL80211_IFTYPE_STATION: + if (!vif->p2p) + break; + /* fall through */ + default: + __clear_bit(0, data.available_mac_ids); + } + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + iwl_mvm_mac_iface_iterator, &data); + + used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, vif); + + /* + * In the case we're getting here during resume, it's similar to + * firmware restart, and with RESUME_ALL the iterator will find + * the vif being added already. + * We don't want to reassign any IDs in either case since doing + * so would probably assign different IDs (as interfaces aren't + * necessarily added in the same order), but the old IDs were + * preserved anyway, so skip ID assignment for both resume and + * recovery. + */ + if (data.found_vif) + return 0; + + /* Therefore, in recovery, we can't get here */ + if (WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))) + return -EBUSY; + + mvmvif->id = find_first_bit(data.available_mac_ids, + NUM_MAC_INDEX_DRIVER); + if (mvmvif->id == NUM_MAC_INDEX_DRIVER) { + IWL_ERR(mvm, "Failed to init MAC context - no free ID!\n"); + ret = -EIO; + goto exit_fail; + } + + if (data.preferred_tsf != NUM_TSF_IDS) + mvmvif->tsf_id = data.preferred_tsf; + else + mvmvif->tsf_id = find_first_bit(data.available_tsf_ids, + NUM_TSF_IDS); + if (mvmvif->tsf_id == NUM_TSF_IDS) { + IWL_ERR(mvm, "Failed to init MAC context - no free TSF!\n"); + ret = -EIO; + goto exit_fail; + } + + mvmvif->color = 0; + + INIT_LIST_HEAD(&mvmvif->time_event_data.list); + mvmvif->time_event_data.id = TE_MAX; + + /* No need to allocate data queues to P2P Device MAC.*/ + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE; + + return 0; + } + + /* Find available queues, and allocate them to the ACs */ + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + u8 queue = find_first_zero_bit(&used_hw_queues, + mvm->first_agg_queue); + + if (queue >= mvm->first_agg_queue) { + IWL_ERR(mvm, "Failed to allocate queue\n"); + ret = -EIO; + goto exit_fail; + } + + __set_bit(queue, &used_hw_queues); + vif->hw_queue[ac] = queue; + } + + /* Allocate the CAB queue for softAP and GO interfaces */ + if (vif->type == NL80211_IFTYPE_AP) { + u8 queue = find_first_zero_bit(&used_hw_queues, + mvm->first_agg_queue); + + if (queue >= mvm->first_agg_queue) { + IWL_ERR(mvm, "Failed to allocate cab queue\n"); + ret = -EIO; + goto exit_fail; + } + + vif->cab_queue = queue; + } else { + vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; + } + + mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT; + mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + + for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) + mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC; + + return 0; + +exit_fail: + memset(mvmvif, 0, sizeof(struct iwl_mvm_vif)); + memset(vif->hw_queue, IEEE80211_INVAL_HW_QUEUE, sizeof(vif->hw_queue)); + vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; + return ret; +} + +int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + unsigned int wdg_timeout = + iwl_mvm_get_wd_timeout(mvm, vif, false, false); + u32 ac; + int ret; + + lockdep_assert_held(&mvm->mutex); + + ret = iwl_mvm_mac_ctxt_allocate_resources(mvm, vif); + if (ret) + return ret; + + switch (vif->type) { + case NL80211_IFTYPE_P2P_DEVICE: + iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, + IWL_MVM_OFFCHANNEL_QUEUE, + IWL_MVM_TX_FIFO_VO, 0, wdg_timeout); + break; + case NL80211_IFTYPE_AP: + iwl_mvm_enable_ac_txq(mvm, vif->cab_queue, vif->cab_queue, + IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); + /* fall through */ + default: + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac], + vif->hw_queue[ac], + iwl_mvm_ac_to_tx_fifo[ac], 0, + wdg_timeout); + break; + } + + return 0; +} + +void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + int ac; + + lockdep_assert_held(&mvm->mutex); + + switch (vif->type) { + case NL80211_IFTYPE_P2P_DEVICE: + iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, + IWL_MVM_OFFCHANNEL_QUEUE, IWL_MAX_TID_COUNT, + 0); + break; + case NL80211_IFTYPE_AP: + iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue, + IWL_MAX_TID_COUNT, 0); + /* fall through */ + default: + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + iwl_mvm_disable_txq(mvm, vif->hw_queue[ac], + vif->hw_queue[ac], + IWL_MAX_TID_COUNT, 0); + } +} + +static void iwl_mvm_ack_rates(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + enum ieee80211_band band, + u8 *cck_rates, u8 *ofdm_rates) +{ + struct ieee80211_supported_band *sband; + unsigned long basic = vif->bss_conf.basic_rates; + int lowest_present_ofdm = 100; + int lowest_present_cck = 100; + u8 cck = 0; + u8 ofdm = 0; + int i; + + sband = mvm->hw->wiphy->bands[band]; + + for_each_set_bit(i, &basic, BITS_PER_LONG) { + int hw = sband->bitrates[i].hw_value; + if (hw >= IWL_FIRST_OFDM_RATE) { + ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE); + if (lowest_present_ofdm > hw) + lowest_present_ofdm = hw; + } else { + BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); + + cck |= BIT(hw); + if (lowest_present_cck > hw) + lowest_present_cck = hw; + } + } + + /* + * Now we've got the basic rates as bitmaps in the ofdm and cck + * variables. This isn't sufficient though, as there might not + * be all the right rates in the bitmap. E.g. if the only basic + * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps + * and 6 Mbps because the 802.11-2007 standard says in 9.6: + * + * [...] a STA responding to a received frame shall transmit + * its Control Response frame [...] at the highest rate in the + * BSSBasicRateSet parameter that is less than or equal to the + * rate of the immediately previous frame in the frame exchange + * sequence ([...]) and that is of the same modulation class + * ([...]) as the received frame. If no rate contained in the + * BSSBasicRateSet parameter meets these conditions, then the + * control frame sent in response to a received frame shall be + * transmitted at the highest mandatory rate of the PHY that is + * less than or equal to the rate of the received frame, and + * that is of the same modulation class as the received frame. + * + * As a consequence, we need to add all mandatory rates that are + * lower than all of the basic rates to these bitmaps. + */ + + if (IWL_RATE_24M_INDEX < lowest_present_ofdm) + ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE; + if (IWL_RATE_12M_INDEX < lowest_present_ofdm) + ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE; + /* 6M already there or needed so always add */ + ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE; + + /* + * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. + * Note, however: + * - if no CCK rates are basic, it must be ERP since there must + * be some basic rates at all, so they're OFDM => ERP PHY + * (or we're in 5 GHz, and the cck bitmap will never be used) + * - if 11M is a basic rate, it must be ERP as well, so add 5.5M + * - if 5.5M is basic, 1M and 2M are mandatory + * - if 2M is basic, 1M is mandatory + * - if 1M is basic, that's the only valid ACK rate. + * As a consequence, it's not as complicated as it sounds, just add + * any lower rates to the ACK rate bitmap. + */ + if (IWL_RATE_11M_INDEX < lowest_present_cck) + cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE; + if (IWL_RATE_5M_INDEX < lowest_present_cck) + cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE; + if (IWL_RATE_2M_INDEX < lowest_present_cck) + cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE; + /* 1M already there or needed so always add */ + cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE; + + *cck_rates = cck; + *ofdm_rates = ofdm; +} + +static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_ctx_cmd *cmd) +{ + /* for both sta and ap, ht_operation_mode hold the protection_mode */ + u8 protection_mode = vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION; + /* The fw does not distinguish between ht and fat */ + u32 ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT; + + IWL_DEBUG_RATE(mvm, "protection mode set to %d\n", protection_mode); + /* + * See section 9.23.3.1 of IEEE 80211-2012. + * Nongreenfield HT STAs Present is not supported. + */ + switch (protection_mode) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONE: + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + cmd->protection_flags |= cpu_to_le32(ht_flag); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + /* Protect when channel wider than 20MHz */ + if (vif->bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) + cmd->protection_flags |= cpu_to_le32(ht_flag); + break; + default: + IWL_ERR(mvm, "Illegal protection mode %d\n", + protection_mode); + break; + } +} + +static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_ctx_cmd *cmd, + const u8 *bssid_override, + u32 action) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct ieee80211_chanctx_conf *chanctx; + bool ht_enabled = !!(vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION); + u8 cck_ack_rates, ofdm_ack_rates; + const u8 *bssid = bssid_override ?: vif->bss_conf.bssid; + int i; + + cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)); + cmd->action = cpu_to_le32(action); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + if (vif->p2p) + cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_STA); + else + cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_BSS_STA); + break; + case NL80211_IFTYPE_AP: + cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_GO); + break; + case NL80211_IFTYPE_MONITOR: + cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_LISTENER); + break; + case NL80211_IFTYPE_P2P_DEVICE: + cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_DEVICE); + break; + case NL80211_IFTYPE_ADHOC: + cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_IBSS); + break; + default: + WARN_ON_ONCE(1); + } + + cmd->tsf_id = cpu_to_le32(mvmvif->tsf_id); + + memcpy(cmd->node_addr, vif->addr, ETH_ALEN); + + if (bssid) + memcpy(cmd->bssid_addr, bssid, ETH_ALEN); + else + eth_broadcast_addr(cmd->bssid_addr); + + rcu_read_lock(); + chanctx = rcu_dereference(vif->chanctx_conf); + iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band + : IEEE80211_BAND_2GHZ, + &cck_ack_rates, &ofdm_ack_rates); + rcu_read_unlock(); + + cmd->cck_rates = cpu_to_le32((u32)cck_ack_rates); + cmd->ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates); + + cmd->cck_short_preamble = + cpu_to_le32(vif->bss_conf.use_short_preamble ? + MAC_FLG_SHORT_PREAMBLE : 0); + cmd->short_slot = + cpu_to_le32(vif->bss_conf.use_short_slot ? + MAC_FLG_SHORT_SLOT : 0); + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u8 txf = iwl_mvm_ac_to_tx_fifo[i]; + + cmd->ac[txf].cw_min = + cpu_to_le16(mvmvif->queue_params[i].cw_min); + cmd->ac[txf].cw_max = + cpu_to_le16(mvmvif->queue_params[i].cw_max); + cmd->ac[txf].edca_txop = + cpu_to_le16(mvmvif->queue_params[i].txop * 32); + cmd->ac[txf].aifsn = mvmvif->queue_params[i].aifs; + cmd->ac[txf].fifos_mask = BIT(txf); + } + + /* in AP mode, the MCAST FIFO takes the EDCA params from VO */ + if (vif->type == NL80211_IFTYPE_AP) + cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |= + BIT(IWL_MVM_TX_FIFO_MCAST); + + if (vif->bss_conf.qos) + cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); + + if (vif->bss_conf.use_cts_prot) + cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); + + IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n", + vif->bss_conf.use_cts_prot, + vif->bss_conf.ht_operation_mode); + if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) + cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); + if (ht_enabled) + iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd); + + cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP); +} + +static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, + struct iwl_mac_ctx_cmd *cmd) +{ + int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, + sizeof(*cmd), cmd); + if (ret) + IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n", + le32_to_cpu(cmd->action), ret); + return ret; +} + +static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action, bool force_assoc_off, + const u8 *bssid_override) +{ + struct iwl_mac_ctx_cmd cmd = {}; + struct iwl_mac_data_sta *ctxt_sta; + + WARN_ON(vif->type != NL80211_IFTYPE_STATION); + + /* Fill the common data for all mac context types */ + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, bssid_override, action); + + if (vif->p2p) { + struct ieee80211_p2p_noa_attr *noa = + &vif->bss_conf.p2p_noa_attr; + + cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow & + IEEE80211_P2P_OPPPS_CTWINDOW_MASK); + ctxt_sta = &cmd.p2p_sta.sta; + } else { + ctxt_sta = &cmd.sta; + } + + /* We need the dtim_period to set the MAC as associated */ + if (vif->bss_conf.assoc && vif->bss_conf.dtim_period && + !force_assoc_off) { + u32 dtim_offs; + + /* + * The DTIM count counts down, so when it is N that means N + * more beacon intervals happen until the DTIM TBTT. Therefore + * add this to the current time. If that ends up being in the + * future, the firmware will handle it. + * + * Also note that the system_timestamp (which we get here as + * "sync_device_ts") and TSF timestamp aren't at exactly the + * same offset in the frame -- the TSF is at the first symbol + * of the TSF, the system timestamp is at signal acquisition + * time. This means there's an offset between them of at most + * a few hundred microseconds (24 * 8 bits + PLCP time gives + * 384us in the longest case), this is currently not relevant + * as the firmware wakes up around 2ms before the TBTT. + */ + dtim_offs = vif->bss_conf.sync_dtim_count * + vif->bss_conf.beacon_int; + /* convert TU to usecs */ + dtim_offs *= 1024; + + ctxt_sta->dtim_tsf = + cpu_to_le64(vif->bss_conf.sync_tsf + dtim_offs); + ctxt_sta->dtim_time = + cpu_to_le32(vif->bss_conf.sync_device_ts + dtim_offs); + + IWL_DEBUG_INFO(mvm, "DTIM TBTT is 0x%llx/0x%x, offset %d\n", + le64_to_cpu(ctxt_sta->dtim_tsf), + le32_to_cpu(ctxt_sta->dtim_time), + dtim_offs); + + ctxt_sta->is_assoc = cpu_to_le32(1); + } else { + ctxt_sta->is_assoc = cpu_to_le32(0); + + /* Allow beacons to pass through as long as we are not + * associated, or we do not have dtim period information. + */ + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); + } + + ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int); + ctxt_sta->bi_reciprocal = + cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int)); + ctxt_sta->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * + vif->bss_conf.dtim_period); + ctxt_sta->dtim_reciprocal = + cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int * + vif->bss_conf.dtim_period)); + + ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval); + ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid); + + if (vif->probe_req_reg && vif->bss_conf.assoc && vif->p2p) + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); + + return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mac_ctx_cmd cmd = {}; + + WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); + + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); + + cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC | + MAC_FILTER_IN_CONTROL_AND_MGMT | + MAC_FILTER_IN_BEACON | + MAC_FILTER_IN_PROBE_REQUEST | + MAC_FILTER_IN_CRC32); + ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); + + return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_ctx_cmd cmd = {}; + + WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); + + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); + + cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_BEACON | + MAC_FILTER_IN_PROBE_REQUEST); + + /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */ + cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int); + cmd.ibss.bi_reciprocal = + cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int)); + + /* TODO: Assumes that the beacon id == mac context id */ + cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id); + + return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); +} + +struct iwl_mvm_go_iterator_data { + bool go_active; +}; + +static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ + struct iwl_mvm_go_iterator_data *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (vif->type == NL80211_IFTYPE_AP && vif->p2p && + mvmvif->ap_ibss_active) + data->go_active = true; +} + +static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mac_ctx_cmd cmd = {}; + struct iwl_mvm_go_iterator_data data = {}; + + WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); + + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); + + cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); + + /* Override the filter flags to accept only probe requests */ + cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); + + /* + * This flag should be set to true when the P2P Device is + * discoverable and there is at least another active P2P GO. Settings + * this flag will allow the P2P Device to be discoverable on other + * channels in addition to its listen channel. + * Note that this flag should not be set in other cases as it opens the + * Rx filters on all MAC and increases the number of interrupts. + */ + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + iwl_mvm_go_iterator, &data); + + cmd.p2p_dev.is_disc_extended = cpu_to_le32(data.go_active ? 1 : 0); + return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); +} + +static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm, + struct iwl_mac_beacon_cmd *beacon_cmd, + u8 *beacon, u32 frame_size) +{ + u32 tim_idx; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; + + /* The index is relative to frame start but we start looking at the + * variable-length part of the beacon. */ + tim_idx = mgmt->u.beacon.variable - beacon; + + /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ + while ((tim_idx < (frame_size - 2)) && + (beacon[tim_idx] != WLAN_EID_TIM)) + tim_idx += beacon[tim_idx+1] + 2; + + /* If TIM field was found, set variables */ + if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { + beacon_cmd->tim_idx = cpu_to_le32(tim_idx); + beacon_cmd->tim_size = cpu_to_le32((u32)beacon[tim_idx+1]); + } else { + IWL_WARN(mvm, "Unable to find TIM Element in beacon\n"); + } +} + +static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct sk_buff *beacon) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_host_cmd cmd = { + .id = BEACON_TEMPLATE_CMD, + .flags = CMD_ASYNC, + }; + struct iwl_mac_beacon_cmd beacon_cmd = {}; + struct ieee80211_tx_info *info; + u32 beacon_skb_len; + u32 rate, tx_flags; + + if (WARN_ON(!beacon)) + return -EINVAL; + + beacon_skb_len = beacon->len; + + /* TODO: for now the beacon template id is set to be the mac context id. + * Might be better to handle it as another resource ... */ + beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); + info = IEEE80211_SKB_CB(beacon); + + /* Set up TX command fields */ + beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len); + beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id; + beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); + tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF; + tx_flags |= + iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) << + TX_CMD_FLG_BT_PRIO_POS; + beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags); + + mvm->mgmt_last_antenna_idx = + iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), + mvm->mgmt_last_antenna_idx); + + beacon_cmd.tx.rate_n_flags = + cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << + RATE_MCS_ANT_POS); + + if (info->band == IEEE80211_BAND_5GHZ || vif->p2p) { + rate = IWL_FIRST_OFDM_RATE; + } else { + rate = IWL_FIRST_CCK_RATE; + beacon_cmd.tx.rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK); + } + beacon_cmd.tx.rate_n_flags |= + cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate)); + + /* Set up TX beacon command fields */ + if (vif->type == NL80211_IFTYPE_AP) + iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd, + beacon->data, + beacon_skb_len); + + /* Submit command */ + cmd.len[0] = sizeof(beacon_cmd); + cmd.data[0] = &beacon_cmd; + cmd.dataflags[0] = 0; + cmd.len[1] = beacon_skb_len; + cmd.data[1] = beacon->data; + cmd.dataflags[1] = IWL_HCMD_DFL_DUP; + + return iwl_mvm_send_cmd(mvm, &cmd); +} + +/* The beacon template for the AP/GO/IBSS has changed and needs update */ +int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct sk_buff *beacon; + int ret; + + WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC); + + beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL); + if (!beacon) + return -ENOMEM; + + ret = iwl_mvm_mac_ctxt_send_beacon(mvm, vif, beacon); + dev_kfree_skb(beacon); + return ret; +} + +struct iwl_mvm_mac_ap_iterator_data { + struct iwl_mvm *mvm; + struct ieee80211_vif *vif; + u32 beacon_device_ts; + u16 beacon_int; +}; + +/* Find the beacon_device_ts and beacon_int for a managed interface */ +static void iwl_mvm_mac_ap_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_mac_ap_iterator_data *data = _data; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) + return; + + /* Station client has higher priority over P2P client*/ + if (vif->p2p && data->beacon_device_ts) + return; + + data->beacon_device_ts = vif->bss_conf.sync_device_ts; + data->beacon_int = vif->bss_conf.beacon_int; +} + +/* + * Fill the specific data for mac context of type AP of P2P GO + */ +static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_data_ap *ctxt_ap, + bool add) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_mac_ap_iterator_data data = { + .mvm = mvm, + .vif = vif, + .beacon_device_ts = 0 + }; + + ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int); + ctxt_ap->bi_reciprocal = + cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int)); + ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * + vif->bss_conf.dtim_period); + ctxt_ap->dtim_reciprocal = + cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int * + vif->bss_conf.dtim_period)); + + ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue); + + /* + * Only set the beacon time when the MAC is being added, when we + * just modify the MAC then we should keep the time -- the firmware + * can otherwise have a "jumping" TBTT. + */ + if (add) { + /* + * If there is a station/P2P client interface which is + * associated, set the AP's TBTT far enough from the station's + * TBTT. Otherwise, set it to the current system time + */ + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + iwl_mvm_mac_ap_iterator, &data); + + if (data.beacon_device_ts) { + u32 rand = (prandom_u32() % (64 - 36)) + 36; + mvmvif->ap_beacon_time = data.beacon_device_ts + + ieee80211_tu_to_usec(data.beacon_int * rand / + 100); + } else { + mvmvif->ap_beacon_time = + iwl_read_prph(mvm->trans, + DEVICE_SYSTEM_TIME_REG); + } + } + + ctxt_ap->beacon_time = cpu_to_le32(mvmvif->ap_beacon_time); + ctxt_ap->beacon_tsf = 0; /* unused */ + + /* TODO: Assume that the beacon id == mac context id */ + ctxt_ap->beacon_template = cpu_to_le32(mvmvif->id); +} + +static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_ctx_cmd cmd = {}; + + WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p); + + /* Fill the common data for all mac context types */ + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); + + /* + * pass probe requests and beacons from other APs (needed + * for ht protection); when there're no any associated station + * don't ask FW to pass beacons to prevent unnecessary wake-ups. + */ + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); + if (mvmvif->ap_assoc_sta_count) { + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); + IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); + } else { + IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); + } + + /* Fill the data specific for ap mode */ + iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap, + action == FW_CTXT_ACTION_ADD); + + return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mac_ctx_cmd cmd = {}; + struct ieee80211_p2p_noa_attr *noa = &vif->bss_conf.p2p_noa_attr; + + WARN_ON(vif->type != NL80211_IFTYPE_AP || !vif->p2p); + + /* Fill the common data for all mac context types */ + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); + + /* + * pass probe requests and beacons from other APs (needed + * for ht protection) + */ + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST | + MAC_FILTER_IN_BEACON); + + /* Fill the data specific for GO mode */ + iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap, + action == FW_CTXT_ACTION_ADD); + + cmd.go.ctwin = cpu_to_le32(noa->oppps_ctwindow & + IEEE80211_P2P_OPPPS_CTWINDOW_MASK); + cmd.go.opp_ps_enabled = + cpu_to_le32(!!(noa->oppps_ctwindow & + IEEE80211_P2P_OPPPS_ENABLE_BIT)); + + return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + u32 action, bool force_assoc_off, + const u8 *bssid_override) +{ + switch (vif->type) { + case NL80211_IFTYPE_STATION: + return iwl_mvm_mac_ctxt_cmd_sta(mvm, vif, action, + force_assoc_off, + bssid_override); + break; + case NL80211_IFTYPE_AP: + if (!vif->p2p) + return iwl_mvm_mac_ctxt_cmd_ap(mvm, vif, action); + else + return iwl_mvm_mac_ctxt_cmd_go(mvm, vif, action); + break; + case NL80211_IFTYPE_MONITOR: + return iwl_mvm_mac_ctxt_cmd_listener(mvm, vif, action); + case NL80211_IFTYPE_P2P_DEVICE: + return iwl_mvm_mac_ctxt_cmd_p2p_device(mvm, vif, action); + case NL80211_IFTYPE_ADHOC: + return iwl_mvm_mac_ctxt_cmd_ibss(mvm, vif, action); + default: + break; + } + + return -EOPNOTSUPP; +} + +int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n", + vif->addr, ieee80211_vif_type_p2p(vif))) + return -EIO; + + ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, + true, NULL); + if (ret) + return ret; + + /* will only do anything at resume from D3 time */ + iwl_mvm_set_last_nonqos_seq(mvm, vif); + + mvmvif->uploaded = true; + return 0; +} + +int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool force_assoc_off, const u8 *bssid_override) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n", + vif->addr, ieee80211_vif_type_p2p(vif))) + return -EIO; + + return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, + force_assoc_off, bssid_override); +} + +int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_ctx_cmd cmd; + int ret; + + if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n", + vif->addr, ieee80211_vif_type_p2p(vif))) + return -EIO; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)); + cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); + + ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, + sizeof(cmd), &cmd); + if (ret) { + IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret); + return ret; + } + + mvmvif->uploaded = false; + + if (vif->type == NL80211_IFTYPE_MONITOR) + __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags); + + return 0; +} + +static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm, + struct ieee80211_vif *csa_vif, u32 gp2, + bool tx_success) +{ + struct iwl_mvm_vif *mvmvif = + iwl_mvm_vif_from_mac80211(csa_vif); + + /* Don't start to countdown from a failed beacon */ + if (!tx_success && !mvmvif->csa_countdown) + return; + + mvmvif->csa_countdown = true; + + if (!ieee80211_csa_is_complete(csa_vif)) { + int c = ieee80211_csa_update_counter(csa_vif); + + iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif); + if (csa_vif->p2p && + !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2 && + tx_success) { + u32 rel_time = (c + 1) * + csa_vif->bss_conf.beacon_int - + IWL_MVM_CHANNEL_SWITCH_TIME_GO; + u32 apply_time = gp2 + rel_time * 1024; + + iwl_mvm_schedule_csa_period(mvm, csa_vif, + IWL_MVM_CHANNEL_SWITCH_TIME_GO - + IWL_MVM_CHANNEL_SWITCH_MARGIN, + apply_time); + } + } else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) { + /* we don't have CSA NoA scheduled yet, switch now */ + ieee80211_csa_finish(csa_vif); + RCU_INIT_POINTER(mvm->csa_vif, NULL); + } +} + +void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_extended_beacon_notif *beacon = (void *)pkt->data; + struct iwl_mvm_tx_resp *beacon_notify_hdr; + struct ieee80211_vif *csa_vif; + struct ieee80211_vif *tx_blocked_vif; + u16 status; + + lockdep_assert_held(&mvm->mutex); + + beacon_notify_hdr = &beacon->beacon_notify_hdr; + mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2); + + status = le16_to_cpu(beacon_notify_hdr->status.status) & TX_STATUS_MSK; + IWL_DEBUG_RX(mvm, + "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n", + status, beacon_notify_hdr->failure_frame, + le64_to_cpu(beacon->tsf), + mvm->ap_last_beacon_gp2, + le32_to_cpu(beacon_notify_hdr->initial_rate)); + + csa_vif = rcu_dereference_protected(mvm->csa_vif, + lockdep_is_held(&mvm->mutex)); + if (unlikely(csa_vif && csa_vif->csa_active)) + iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2, + (status == TX_STATUS_SUCCESS)); + + tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif, + lockdep_is_held(&mvm->mutex)); + if (unlikely(tx_blocked_vif)) { + struct iwl_mvm_vif *mvmvif = + iwl_mvm_vif_from_mac80211(tx_blocked_vif); + + /* + * The channel switch is started and we have blocked the + * stations. If this is the first beacon (the timeout wasn't + * set), set the unblock timeout, otherwise countdown + */ + if (!mvm->csa_tx_block_bcn_timeout) + mvm->csa_tx_block_bcn_timeout = + IWL_MVM_CS_UNBLOCK_TX_TIMEOUT; + else + mvm->csa_tx_block_bcn_timeout--; + + /* Check if the timeout is expired, and unblock tx */ + if (mvm->csa_tx_block_bcn_timeout == 0) { + iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false); + RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL); + } + } +} + +static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_missed_beacons_notif *missed_beacons = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + struct iwl_fw_dbg_trigger_missed_bcon *bcon_trig; + struct iwl_fw_dbg_trigger_tlv *trigger; + u32 stop_trig_missed_bcon, stop_trig_missed_bcon_since_rx; + u32 rx_missed_bcon, rx_missed_bcon_since_rx; + + if (mvmvif->id != (u16)le32_to_cpu(missed_beacons->mac_id)) + return; + + rx_missed_bcon = le32_to_cpu(missed_beacons->consec_missed_beacons); + rx_missed_bcon_since_rx = + le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx); + /* + * TODO: the threshold should be adjusted based on latency conditions, + * and/or in case of a CS flow on one of the other AP vifs. + */ + if (le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx) > + IWL_MVM_MISSED_BEACONS_THRESHOLD) + ieee80211_beacon_loss(vif); + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, + FW_DBG_TRIGGER_MISSED_BEACONS)) + return; + + trigger = iwl_fw_dbg_get_trigger(mvm->fw, + FW_DBG_TRIGGER_MISSED_BEACONS); + bcon_trig = (void *)trigger->data; + stop_trig_missed_bcon = le32_to_cpu(bcon_trig->stop_consec_missed_bcon); + stop_trig_missed_bcon_since_rx = + le32_to_cpu(bcon_trig->stop_consec_missed_bcon_since_rx); + + /* TODO: implement start trigger */ + + if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) + return; + + if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx || + rx_missed_bcon >= stop_trig_missed_bcon) + iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); +} + +void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_missed_beacons_notif *mb = (void *)pkt->data; + + IWL_DEBUG_INFO(mvm, + "missed bcn mac_id=%u, consecutive=%u (%u, %u, %u)\n", + le32_to_cpu(mb->mac_id), + le32_to_cpu(mb->consec_missed_beacons), + le32_to_cpu(mb->consec_missed_beacons_since_last_rx), + le32_to_cpu(mb->num_recvd_beacons), + le32_to_cpu(mb->num_expected_beacons)); + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_beacon_loss_iterator, + mb); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c new file mode 100644 index 000000000000..1fb684693040 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -0,0 +1,4260 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iwl-op-mode.h" +#include "iwl-io.h" +#include "mvm.h" +#include "sta.h" +#include "time-event.h" +#include "iwl-eeprom-parse.h" +#include "iwl-phy-db.h" +#include "testmode.h" +#include "iwl-fw-error-dump.h" +#include "iwl-prph.h" +#include "iwl-csr.h" +#include "iwl-nvm-parse.h" + +static const struct ieee80211_iface_limit iwl_mvm_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE), + }, +}; + +static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = { + { + .num_different_channels = 2, + .max_interfaces = 3, + .limits = iwl_mvm_limits, + .n_limits = ARRAY_SIZE(iwl_mvm_limits), + }, +}; + +#ifdef CONFIG_PM_SLEEP +static const struct nl80211_wowlan_tcp_data_token_feature +iwl_mvm_wowlan_tcp_token_feature = { + .min_len = 0, + .max_len = 255, + .bufsize = IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS, +}; + +static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = { + .tok = &iwl_mvm_wowlan_tcp_token_feature, + .data_payload_max = IWL_WOWLAN_TCP_MAX_PACKET_LEN - + sizeof(struct ethhdr) - + sizeof(struct iphdr) - + sizeof(struct tcphdr), + .data_interval_max = 65535, /* __le16 in API */ + .wake_payload_max = IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN - + sizeof(struct ethhdr) - + sizeof(struct iphdr) - + sizeof(struct tcphdr), + .seq = true, +}; +#endif + +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING +/* + * Use the reserved field to indicate magic values. + * these values will only be used internally by the driver, + * and won't make it to the fw (reserved will be 0). + * BC_FILTER_MAGIC_IP - configure the val of this attribute to + * be the vif's ip address. in case there is not a single + * ip address (0, or more than 1), this attribute will + * be skipped. + * BC_FILTER_MAGIC_MAC - set the val of this attribute to + * the LSB bytes of the vif's mac address + */ +enum { + BC_FILTER_MAGIC_NONE = 0, + BC_FILTER_MAGIC_IP, + BC_FILTER_MAGIC_MAC, +}; + +static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = { + { + /* arp */ + .discard = 0, + .frame_type = BCAST_FILTER_FRAME_TYPE_ALL, + .attrs = { + { + /* frame type - arp, hw type - ethernet */ + .offset_type = + BCAST_FILTER_OFFSET_PAYLOAD_START, + .offset = sizeof(rfc1042_header), + .val = cpu_to_be32(0x08060001), + .mask = cpu_to_be32(0xffffffff), + }, + { + /* arp dest ip */ + .offset_type = + BCAST_FILTER_OFFSET_PAYLOAD_START, + .offset = sizeof(rfc1042_header) + 2 + + sizeof(struct arphdr) + + ETH_ALEN + sizeof(__be32) + + ETH_ALEN, + .mask = cpu_to_be32(0xffffffff), + /* mark it as special field */ + .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP), + }, + }, + }, + { + /* dhcp offer bcast */ + .discard = 0, + .frame_type = BCAST_FILTER_FRAME_TYPE_IPV4, + .attrs = { + { + /* udp dest port - 68 (bootp client)*/ + .offset_type = BCAST_FILTER_OFFSET_IP_END, + .offset = offsetof(struct udphdr, dest), + .val = cpu_to_be32(0x00440000), + .mask = cpu_to_be32(0xffff0000), + }, + { + /* dhcp - lsb bytes of client hw address */ + .offset_type = BCAST_FILTER_OFFSET_IP_END, + .offset = 38, + .mask = cpu_to_be32(0xffffffff), + /* mark it as special field */ + .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC), + }, + }, + }, + /* last filter must be empty */ + {}, +}; +#endif + +void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) +{ + if (!iwl_mvm_is_d0i3_supported(mvm)) + return; + + IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type); + spin_lock_bh(&mvm->refs_lock); + mvm->refs[ref_type]++; + spin_unlock_bh(&mvm->refs_lock); + iwl_trans_ref(mvm->trans); +} + +void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) +{ + if (!iwl_mvm_is_d0i3_supported(mvm)) + return; + + IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type); + spin_lock_bh(&mvm->refs_lock); + WARN_ON(!mvm->refs[ref_type]--); + spin_unlock_bh(&mvm->refs_lock); + iwl_trans_unref(mvm->trans); +} + +static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm, + enum iwl_mvm_ref_type except_ref) +{ + int i, j; + + if (!iwl_mvm_is_d0i3_supported(mvm)) + return; + + spin_lock_bh(&mvm->refs_lock); + for (i = 0; i < IWL_MVM_REF_COUNT; i++) { + if (except_ref == i || !mvm->refs[i]) + continue; + + IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d (%d)\n", + i, mvm->refs[i]); + for (j = 0; j < mvm->refs[i]; j++) + iwl_trans_unref(mvm->trans); + mvm->refs[i] = 0; + } + spin_unlock_bh(&mvm->refs_lock); +} + +bool iwl_mvm_ref_taken(struct iwl_mvm *mvm) +{ + int i; + bool taken = false; + + if (!iwl_mvm_is_d0i3_supported(mvm)) + return true; + + spin_lock_bh(&mvm->refs_lock); + for (i = 0; i < IWL_MVM_REF_COUNT; i++) { + if (mvm->refs[i]) { + taken = true; + break; + } + } + spin_unlock_bh(&mvm->refs_lock); + + return taken; +} + +int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) +{ + iwl_mvm_ref(mvm, ref_type); + + if (!wait_event_timeout(mvm->d0i3_exit_waitq, + !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), + HZ)) { + WARN_ON_ONCE(1); + iwl_mvm_unref(mvm, ref_type); + return -EIO; + } + + return 0; +} + +static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm) +{ + int i; + + memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts)); + for (i = 0; i < NUM_PHY_CTX; i++) { + mvm->phy_ctxts[i].id = i; + mvm->phy_ctxts[i].ref = 0; + } +} + +struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, + const char *alpha2, + enum iwl_mcc_source src_id, + bool *changed) +{ + struct ieee80211_regdomain *regd = NULL; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mcc_update_resp *resp; + + IWL_DEBUG_LAR(mvm, "Getting regdomain data for %s from FW\n", alpha2); + + lockdep_assert_held(&mvm->mutex); + + resp = iwl_mvm_update_mcc(mvm, alpha2, src_id); + if (IS_ERR_OR_NULL(resp)) { + IWL_DEBUG_LAR(mvm, "Could not get update from FW %d\n", + PTR_ERR_OR_ZERO(resp)); + goto out; + } + + if (changed) + *changed = (resp->status == MCC_RESP_NEW_CHAN_PROFILE); + + regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg, + __le32_to_cpu(resp->n_channels), + resp->channels, + __le16_to_cpu(resp->mcc)); + /* Store the return source id */ + src_id = resp->source_id; + kfree(resp); + if (IS_ERR_OR_NULL(regd)) { + IWL_DEBUG_LAR(mvm, "Could not get parse update from FW %d\n", + PTR_ERR_OR_ZERO(regd)); + goto out; + } + + IWL_DEBUG_LAR(mvm, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n", + regd->alpha2, regd->alpha2[0], regd->alpha2[1], src_id); + mvm->lar_regdom_set = true; + mvm->mcc_src = src_id; + +out: + return regd; +} + +void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm) +{ + bool changed; + struct ieee80211_regdomain *regd; + + if (!iwl_mvm_is_lar_supported(mvm)) + return; + + regd = iwl_mvm_get_current_regdomain(mvm, &changed); + if (!IS_ERR_OR_NULL(regd)) { + /* only update the regulatory core if changed */ + if (changed) + regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); + + kfree(regd); + } +} + +struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm, + bool *changed) +{ + return iwl_mvm_get_regdomain(mvm->hw->wiphy, "ZZ", + iwl_mvm_is_wifi_mcc_supported(mvm) ? + MCC_SOURCE_GET_CURRENT : + MCC_SOURCE_OLD_FW, changed); +} + +int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm) +{ + enum iwl_mcc_source used_src; + struct ieee80211_regdomain *regd; + int ret; + bool changed; + const struct ieee80211_regdomain *r = + rtnl_dereference(mvm->hw->wiphy->regd); + + if (!r) + return -ENOENT; + + /* save the last source in case we overwrite it below */ + used_src = mvm->mcc_src; + if (iwl_mvm_is_wifi_mcc_supported(mvm)) { + /* Notify the firmware we support wifi location updates */ + regd = iwl_mvm_get_current_regdomain(mvm, NULL); + if (!IS_ERR_OR_NULL(regd)) + kfree(regd); + } + + /* Now set our last stored MCC and source */ + regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src, + &changed); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + + /* update cfg80211 if the regdomain was changed */ + if (changed) + ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd); + else + ret = 0; + + kfree(regd); + return ret; +} + +int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) +{ + struct ieee80211_hw *hw = mvm->hw; + int num_mac, ret, i; + static const u32 mvm_ciphers[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + }; + + /* Tell mac80211 our characteristics */ + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, SPECTRUM_MGMT); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(hw, QUEUE_CONTROL); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, TIMING_BEACON_ONLY); + ieee80211_hw_set(hw, CONNECTION_MONITOR); + ieee80211_hw_set(hw, CHANCTX_STA_CSA); + ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); + ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); + + hw->queues = mvm->first_agg_queue; + hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; + hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | + IEEE80211_RADIOTAP_MCS_HAVE_STBC; + hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC | + IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED; + hw->rate_control_algorithm = "iwl-mvm-rs"; + hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES; + hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; + + BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 2); + memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers)); + hw->wiphy->n_cipher_suites = ARRAY_SIZE(mvm_ciphers); + hw->wiphy->cipher_suites = mvm->ciphers; + + /* + * Enable 11w if advertised by firmware and software crypto + * is not enabled (as the firmware will interpret some mgmt + * packets, so enabling it with software crypto isn't safe) + */ + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP && + !iwlwifi_mod_params.sw_crypto) { + ieee80211_hw_set(hw, MFP_CAPABLE); + mvm->ciphers[hw->wiphy->n_cipher_suites] = + WLAN_CIPHER_SUITE_AES_CMAC; + hw->wiphy->n_cipher_suites++; + } + + /* currently FW API supports only one optional cipher scheme */ + if (mvm->fw->cs[0].cipher) { + mvm->hw->n_cipher_schemes = 1; + mvm->hw->cipher_schemes = &mvm->fw->cs[0]; + mvm->ciphers[hw->wiphy->n_cipher_suites] = + mvm->fw->cs[0].cipher; + hw->wiphy->n_cipher_suites++; + } + + ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); + hw->wiphy->features |= + NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_ND_RANDOM_MAC_ADDR; + + hw->sta_data_size = sizeof(struct iwl_mvm_sta); + hw->vif_data_size = sizeof(struct iwl_mvm_vif); + hw->chanctx_data_size = sizeof(u16); + + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_DEVICE) | + BIT(NL80211_IFTYPE_ADHOC); + + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR; + if (iwl_mvm_is_lar_supported(mvm)) + hw->wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; + else + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD) + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + + hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; + + hw->wiphy->iface_combinations = iwl_mvm_iface_combinations; + hw->wiphy->n_iface_combinations = + ARRAY_SIZE(iwl_mvm_iface_combinations); + + hw->wiphy->max_remain_on_channel_duration = 10000; + hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; + /* we can compensate an offset of up to 3 channels = 15 MHz */ + hw->wiphy->max_adj_channel_rssi_comp = 3 * 5; + + /* Extract MAC address */ + memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN); + hw->wiphy->addresses = mvm->addresses; + hw->wiphy->n_addresses = 1; + + /* Extract additional MAC addresses if available */ + num_mac = (mvm->nvm_data->n_hw_addrs > 1) ? + min(IWL_MVM_MAX_ADDRESSES, mvm->nvm_data->n_hw_addrs) : 1; + + for (i = 1; i < num_mac; i++) { + memcpy(mvm->addresses[i].addr, mvm->addresses[i-1].addr, + ETH_ALEN); + mvm->addresses[i].addr[5]++; + hw->wiphy->n_addresses++; + } + + iwl_mvm_reset_phy_ctxts(mvm); + + hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm); + + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; + + BUILD_BUG_ON(IWL_MVM_SCAN_STOPPING_MASK & IWL_MVM_SCAN_MASK); + BUILD_BUG_ON(IWL_MVM_MAX_UMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK) || + IWL_MVM_MAX_LMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK)); + + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) + mvm->max_scans = IWL_MVM_MAX_UMAC_SCANS; + else + mvm->max_scans = IWL_MVM_MAX_LMAC_SCANS; + + if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels) + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; + if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels) { + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BEAMFORMER) && + fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_LQ_SS_PARAMS)) + hw->wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap |= + IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; + } + + hw->wiphy->hw_version = mvm->trans->hw_id; + + if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) + hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; + else + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; + hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES; + /* we create the 802.11 header and zero length SSID IE. */ + hw->wiphy->max_sched_scan_ie_len = + SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2; + hw->wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS; + hw->wiphy->max_sched_scan_plan_interval = U16_MAX; + + /* + * the firmware uses u8 for num of iterations, but 0xff is saved for + * infinite loop, so the maximum number of iterations is actually 254. + */ + hw->wiphy->max_sched_scan_plan_iterations = 254; + + hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN | + NL80211_FEATURE_LOW_PRIORITY_SCAN | + NL80211_FEATURE_P2P_GO_OPPPS | + NL80211_FEATURE_DYNAMIC_SMPS | + NL80211_FEATURE_STATIC_SMPS | + NL80211_FEATURE_SUPPORTS_WMM_ADMISSION; + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT)) + hw->wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION; + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT)) + hw->wiphy->features |= NL80211_FEATURE_QUIET; + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)) + hw->wiphy->features |= + NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES; + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT)) + hw->wiphy->features |= NL80211_FEATURE_WFA_TPC_IE_IN_PROBES; + + mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; + +#ifdef CONFIG_PM_SLEEP + if (iwl_mvm_is_d0i3_supported(mvm) && + device_can_wakeup(mvm->trans->dev)) { + mvm->wowlan.flags = WIPHY_WOWLAN_ANY; + hw->wiphy->wowlan = &mvm->wowlan; + } + + if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len && + mvm->trans->ops->d3_suspend && + mvm->trans->ops->d3_resume && + device_can_wakeup(mvm->trans->dev)) { + mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_RFKILL_RELEASE | + WIPHY_WOWLAN_NET_DETECT; + if (!iwlwifi_mod_params.sw_crypto) + mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE | + WIPHY_WOWLAN_4WAY_HANDSHAKE; + + mvm->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS; + mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN; + mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN; + mvm->wowlan.max_nd_match_sets = IWL_SCAN_MAX_PROFILES; + mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support; + hw->wiphy->wowlan = &mvm->wowlan; + } +#endif + +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING + /* assign default bcast filtering configuration */ + mvm->bcast_filters = iwl_mvm_default_bcast_filters; +#endif + + ret = iwl_mvm_leds_init(mvm); + if (ret) + return ret; + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_TDLS_SUPPORT)) { + IWL_DEBUG_TDLS(mvm, "TDLS supported\n"); + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; + ieee80211_hw_set(hw, TDLS_WIDER_BW); + } + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH)) { + IWL_DEBUG_TDLS(mvm, "TDLS channel switch supported\n"); + hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH; + } + + hw->netdev_features |= mvm->cfg->features; + if (!iwl_mvm_is_csum_supported(mvm)) + hw->netdev_features &= ~NETIF_F_RXCSUM; + + ret = ieee80211_register_hw(mvm->hw); + if (ret) + iwl_mvm_leds_exit(mvm); + + return ret; +} + +static bool iwl_mvm_defer_tx(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct sk_buff *skb) +{ + struct iwl_mvm_sta *mvmsta; + bool defer = false; + + /* + * double check the IN_D0I3 flag both before and after + * taking the spinlock, in order to prevent taking + * the spinlock when not needed. + */ + if (likely(!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status))) + return false; + + spin_lock(&mvm->d0i3_tx_lock); + /* + * testing the flag again ensures the skb dequeue + * loop (on d0i3 exit) hasn't run yet. + */ + if (!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) + goto out; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + if (mvmsta->sta_id == IWL_MVM_STATION_COUNT || + mvmsta->sta_id != mvm->d0i3_ap_sta_id) + goto out; + + __skb_queue_tail(&mvm->d0i3_tx, skb); + ieee80211_stop_queues(mvm->hw); + + /* trigger wakeup */ + iwl_mvm_ref(mvm, IWL_MVM_REF_TX); + iwl_mvm_unref(mvm, IWL_MVM_REF_TX); + + defer = true; +out: + spin_unlock(&mvm->d0i3_tx_lock); + return defer; +} + +static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct ieee80211_sta *sta = control->sta; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + + if (iwl_mvm_is_radio_killed(mvm)) { + IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n"); + goto drop; + } + + if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE && + !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) && + !test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) + goto drop; + + /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */ + if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER && + ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_is_deauth(hdr->frame_control) && + !ieee80211_is_disassoc(hdr->frame_control) && + !ieee80211_is_action(hdr->frame_control))) + sta = NULL; + + if (sta) { + if (iwl_mvm_defer_tx(mvm, sta, skb)) + return; + if (iwl_mvm_tx_skb(mvm, skb, sta)) + goto drop; + return; + } + + if (iwl_mvm_tx_skb_non_sta(mvm, skb)) + goto drop; + return; + drop: + ieee80211_free_txskb(hw, skb); +} + +static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg) +{ + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) + return false; + return true; +} + +static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) +{ + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) + return false; + if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG) + return true; + + /* enabled by default */ + return true; +} + +#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \ + do { \ + if (!(le16_to_cpu(_tid_bm) & BIT(_tid))) \ + break; \ + iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt); \ + } while (0) + +static void +iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u16 rx_ba_ssn, + enum ieee80211_ampdu_mlme_action action) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_ba *ba_trig; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); + ba_trig = (void *)trig->data; + + if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + return; + + switch (action) { + case IEEE80211_AMPDU_TX_OPERATIONAL: { + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + + CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_start, tid, + "TX AGG START: MAC %pM tid %d ssn %d\n", + sta->addr, tid, tid_data->ssn); + break; + } + case IEEE80211_AMPDU_TX_STOP_CONT: + CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_stop, tid, + "TX AGG STOP: MAC %pM tid %d\n", + sta->addr, tid); + break; + case IEEE80211_AMPDU_RX_START: + CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_start, tid, + "RX AGG START: MAC %pM tid %d ssn %d\n", + sta->addr, tid, rx_ba_ssn); + break; + case IEEE80211_AMPDU_RX_STOP: + CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_stop, tid, + "RX AGG STOP: MAC %pM tid %d\n", + sta->addr, tid); + break; + default: + break; + } +} + +static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, + u16 *ssn, u8 buf_size, bool amsdu) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + bool tx_agg_ref = false; + + IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n", + sta->addr, tid, action); + + if (!(mvm->nvm_data->sku_cap_11n_enable)) + return -EACCES; + + /* return from D0i3 before starting a new Tx aggregation */ + switch (action) { + case IEEE80211_AMPDU_TX_START: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_OPERATIONAL: + /* + * for tx start, wait synchronously until D0i3 exit to + * get the correct sequence number for the tid. + * additionally, some other ampdu actions use direct + * target access, which is not handled automatically + * by the trans layer (unlike commands), so wait for + * d0i3 exit in these cases as well. + */ + ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_TX_AGG); + if (ret) + return ret; + + tx_agg_ref = true; + break; + default: + break; + } + + mutex_lock(&mvm->mutex); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + if (!iwl_enable_rx_ampdu(mvm->cfg)) { + ret = -EINVAL; + break; + } + ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, *ssn, true); + break; + case IEEE80211_AMPDU_RX_STOP: + ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false); + break; + case IEEE80211_AMPDU_TX_START: + if (!iwl_enable_tx_ampdu(mvm->cfg)) { + ret = -EINVAL; + break; + } + ret = iwl_mvm_sta_tx_agg_start(mvm, vif, sta, tid, ssn); + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size); + break; + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + break; + } + + if (!ret) { + u16 rx_ba_ssn = 0; + + if (action == IEEE80211_AMPDU_RX_START) + rx_ba_ssn = *ssn; + + iwl_mvm_ampdu_check_trigger(mvm, vif, sta, tid, + rx_ba_ssn, action); + } + mutex_unlock(&mvm->mutex); + + /* + * If the tid is marked as started, we won't use it for offloaded + * traffic on the next D0i3 entry. It's safe to unref. + */ + if (tx_agg_ref) + iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG); + + return ret; +} + +static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + mvmvif->uploaded = false; + mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + + spin_lock_bh(&mvm->time_event_lock); + iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data); + spin_unlock_bh(&mvm->time_event_lock); + + mvmvif->phy_ctxt = NULL; + memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data)); +} + +static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, + const void *data, size_t datalen) +{ + const struct iwl_mvm_dump_ptrs *dump_ptrs = data; + ssize_t bytes_read; + ssize_t bytes_read_trans; + + if (offset < dump_ptrs->op_mode_len) { + bytes_read = min_t(ssize_t, count, + dump_ptrs->op_mode_len - offset); + memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset, + bytes_read); + offset += bytes_read; + count -= bytes_read; + + if (count == 0) + return bytes_read; + } else { + bytes_read = 0; + } + + if (!dump_ptrs->trans_ptr) + return bytes_read; + + offset -= dump_ptrs->op_mode_len; + bytes_read_trans = min_t(ssize_t, count, + dump_ptrs->trans_ptr->len - offset); + memcpy(buffer + bytes_read, + (u8 *)dump_ptrs->trans_ptr->data + offset, + bytes_read_trans); + + return bytes_read + bytes_read_trans; +} + +static void iwl_mvm_free_coredump(const void *data) +{ + const struct iwl_mvm_dump_ptrs *fw_error_dump = data; + + vfree(fw_error_dump->op_mode_ptr); + vfree(fw_error_dump->trans_ptr); + kfree(fw_error_dump); +} + +static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, + struct iwl_fw_error_dump_data **dump_data) +{ + struct iwl_fw_error_dump_fifo *fifo_hdr; + u32 *fifo_data; + u32 fifo_len; + unsigned long flags; + int i, j; + + if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) + return; + + /* Pull RXF data from all RXFs */ + for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) { + /* + * Keep aside the additional offset that might be needed for + * next RXF + */ + u32 offset_diff = RXF_DIFF_FROM_PREV * i; + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = mvm->shared_mem_cfg.rxfifo_size[i]; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + continue; + + /* Add a TLV for the RXF */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(i); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_D_SPACE + + offset_diff)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_WR_PTR + + offset_diff)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_RD_PTR + + offset_diff)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_FENCE_PTR + + offset_diff)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_SET_FENCE_MODE + + offset_diff)); + + /* Lock fence */ + iwl_trans_write_prph(mvm->trans, + RXF_SET_FENCE_MODE + offset_diff, 0x1); + /* Set fence pointer to the same place like WR pointer */ + iwl_trans_write_prph(mvm->trans, + RXF_LD_WR2FENCE + offset_diff, 0x1); + /* Set fence offset */ + iwl_trans_write_prph(mvm->trans, + RXF_LD_FENCE_OFFSET_ADDR + offset_diff, + 0x0); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (j = 0; j < fifo_len; j++) + fifo_data[j] = iwl_trans_read_prph(mvm->trans, + RXF_FIFO_RD_FENCE_INC + + offset_diff); + *dump_data = iwl_fw_error_next_data(*dump_data); + } + + /* Pull TXF data from all TXFs */ + for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) { + /* Mark the number of TXF we're pulling now */ + iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = mvm->shared_mem_cfg.txfifo_size[i]; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + continue; + + /* Add a TLV for the FIFO */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(i); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_FIFO_ITEM_CNT)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_WR_PTR)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_RD_PTR)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_FENCE_PTR)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_LOCK_FENCE)); + + /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ + iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR, + TXF_WR_PTR); + + /* Dummy-read to advance the read pointer to the head */ + iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (j = 0; j < fifo_len; j++) + fifo_data[j] = iwl_trans_read_prph(mvm->trans, + TXF_READ_MODIFY_DATA); + *dump_data = iwl_fw_error_next_data(*dump_data); + } + + iwl_trans_release_nic_access(mvm->trans, &flags); +} + +void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) +{ + if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert || + !mvm->fw_dump_desc) + return; + + kfree(mvm->fw_dump_desc); + mvm->fw_dump_desc = NULL; +} + +#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ +#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ + +void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) +{ + struct iwl_fw_error_dump_file *dump_file; + struct iwl_fw_error_dump_data *dump_data; + struct iwl_fw_error_dump_info *dump_info; + struct iwl_fw_error_dump_mem *dump_mem; + struct iwl_fw_error_dump_trigger_desc *dump_trig; + struct iwl_mvm_dump_ptrs *fw_error_dump; + u32 sram_len, sram_ofs; + u32 file_len, fifo_data_len = 0; + u32 smem_len = mvm->cfg->smem_len; + u32 sram2_len = mvm->cfg->dccm2_len; + bool monitor_dump_only = false; + + lockdep_assert_held(&mvm->mutex); + + /* there's no point in fw dump if the bus is dead */ + if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { + IWL_ERR(mvm, "Skip fw error dump since bus is dead\n"); + return; + } + + if (mvm->fw_dump_trig && + mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY) + monitor_dump_only = true; + + fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); + if (!fw_error_dump) + return; + + /* SRAM - include stack CCM if driver knows the values for it */ + if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { + const struct fw_img *img; + + img = &mvm->fw->img[mvm->cur_ucode]; + sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + } else { + sram_ofs = mvm->cfg->dccm_offset; + sram_len = mvm->cfg->dccm_len; + } + + /* reading RXF/TXF sizes */ + if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { + struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg; + int i; + + fifo_data_len = 0; + + /* Count RXF size */ + for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) { + if (!mem_cfg->rxfifo_size[i]) + continue; + + /* Add header info */ + fifo_data_len += mem_cfg->rxfifo_size[i] + + sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + } + + for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) { + if (!mem_cfg->txfifo_size[i]) + continue; + + /* Add header info */ + fifo_data_len += mem_cfg->txfifo_size[i] + + sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + } + } + + file_len = sizeof(*dump_file) + + sizeof(*dump_data) * 2 + + sram_len + sizeof(*dump_mem) + + fifo_data_len + + sizeof(*dump_info); + + /* Make room for the SMEM, if it exists */ + if (smem_len) + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; + + /* Make room for the secondary SRAM, if it exists */ + if (sram2_len) + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; + + /* Make room for fw's virtual image pages, if it exists */ + if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) + file_len += mvm->num_of_paging_blk * + (sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_paging) + + PAGING_BLOCK_SIZE); + + /* If we only want a monitor dump, reset the file length */ + if (monitor_dump_only) { + file_len = sizeof(*dump_file) + sizeof(*dump_data) + + sizeof(*dump_info); + } + + /* + * In 8000 HW family B-step include the ICCM (which resides separately) + */ + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && + CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + + IWL8260_ICCM_LEN; + + if (mvm->fw_dump_desc) + file_len += sizeof(*dump_data) + sizeof(*dump_trig) + + mvm->fw_dump_desc->len; + + dump_file = vzalloc(file_len); + if (!dump_file) { + kfree(fw_error_dump); + iwl_mvm_free_fw_dump_desc(mvm); + return; + } + + fw_error_dump->op_mode_ptr = dump_file; + + dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); + dump_data = (void *)dump_file->data; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_info)); + dump_info = (void *) dump_data->data; + dump_info->device_family = + mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); + dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev)); + memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, + sizeof(dump_info->fw_human_readable)); + strncpy(dump_info->dev_human_readable, mvm->cfg->name, + sizeof(dump_info->dev_human_readable)); + strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, + sizeof(dump_info->bus_human_readable)); + + dump_data = iwl_fw_error_next_data(dump_data); + /* We only dump the FIFOs if the FW is in error state */ + if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) + iwl_mvm_dump_fifos(mvm, &dump_data); + + if (mvm->fw_dump_desc) { + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_trig) + + mvm->fw_dump_desc->len); + dump_trig = (void *)dump_data->data; + memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc, + sizeof(*dump_trig) + mvm->fw_dump_desc->len); + + /* now we can free this copy */ + iwl_mvm_free_fw_dump_desc(mvm); + dump_data = iwl_fw_error_next_data(dump_data); + } + + /* In case we only want monitor dump, skip to dump trasport data */ + if (monitor_dump_only) + goto dump_trans_data; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(sram_ofs); + iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data, + sram_len); + + if (smem_len) { + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); + dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset); + iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, + dump_mem->data, smem_len); + } + + if (sram2_len) { + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset); + iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset, + dump_mem->data, sram2_len); + } + + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && + CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) { + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN + + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET); + iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET, + dump_mem->data, IWL8260_ICCM_LEN); + } + + /* Dump fw's virtual image */ + if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) { + u32 i; + + for (i = 1; i < mvm->num_of_paging_blk + 1; i++) { + struct iwl_fw_error_dump_paging *paging; + struct page *pages = + mvm->fw_paging_db[i].fw_paging_block; + + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); + dump_data->len = cpu_to_le32(sizeof(*paging) + + PAGING_BLOCK_SIZE); + paging = (void *)dump_data->data; + paging->index = cpu_to_le32(i); + memcpy(paging->data, page_address(pages), + PAGING_BLOCK_SIZE); + } + } + +dump_trans_data: + fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans, + mvm->fw_dump_trig); + fw_error_dump->op_mode_len = file_len; + if (fw_error_dump->trans_ptr) + file_len += fw_error_dump->trans_ptr->len; + dump_file->file_len = cpu_to_le32(file_len); + + dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, + GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); + + mvm->fw_dump_trig = NULL; + clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); +} + +struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { + .trig_desc = { + .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), + }, +}; + +static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) +{ + /* clear the D3 reconfig, we only need it to avoid dumping a + * firmware coredump on reconfiguration, we shouldn't do that + * on D3->D0 transition + */ + if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) { + mvm->fw_dump_desc = &iwl_mvm_dump_desc_assert; + iwl_mvm_fw_error_dump(mvm); + } + + /* cleanup all stale references (scan, roc), but keep the + * ucode_down ref until reconfig is complete + */ + iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN); + + iwl_trans_stop_device(mvm->trans); + + mvm->scan_status = 0; + mvm->ps_disabled = false; + mvm->calibrating = false; + + /* just in case one was running */ + ieee80211_remain_on_channel_expired(mvm->hw); + + /* + * cleanup all interfaces, even inactive ones, as some might have + * gone down during the HW restart + */ + ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_cleanup_iterator, mvm); + + mvm->p2p_device_vif = NULL; + mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + + iwl_mvm_reset_phy_ctxts(mvm); + memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained)); + memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained)); + memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); + memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old)); + memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); + memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old)); + memset(&mvm->bt_ack_kill_msk, 0, sizeof(mvm->bt_ack_kill_msk)); + memset(&mvm->bt_cts_kill_msk, 0, sizeof(mvm->bt_cts_kill_msk)); + + ieee80211_wake_queues(mvm->hw); + + /* clear any stale d0i3 state */ + clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); + + mvm->vif_count = 0; + mvm->rx_ba_sessions = 0; + mvm->fw_dbg_conf = FW_DBG_INVALID; + + /* keep statistics ticking */ + iwl_mvm_accu_radio_stats(mvm); +} + +int __iwl_mvm_mac_start(struct iwl_mvm *mvm) +{ + int ret; + + lockdep_assert_held(&mvm->mutex); + + /* Clean up some internal and mac80211 state on restart */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + iwl_mvm_restart_cleanup(mvm); + + ret = iwl_mvm_up(mvm); + + if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + /* Something went wrong - we need to finish some cleanup + * that normally iwl_mvm_mac_restart_complete() below + * would do. + */ + clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + iwl_mvm_d0i3_enable_tx(mvm, NULL); + } + + return ret; +} + +static int iwl_mvm_mac_start(struct ieee80211_hw *hw) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + /* Some hw restart cleanups must not hold the mutex */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + /* + * Make sure we are out of d0i3. This is needed + * to make sure the reference accounting is correct + * (and there is no stale d0i3_exit_work). + */ + wait_event_timeout(mvm->d0i3_exit_waitq, + !test_bit(IWL_MVM_STATUS_IN_D0I3, + &mvm->status), + HZ); + } + + mutex_lock(&mvm->mutex); + ret = __iwl_mvm_mac_start(mvm); + mutex_unlock(&mvm->mutex); + + return ret; +} + +static void iwl_mvm_restart_complete(struct iwl_mvm *mvm) +{ + int ret; + + mutex_lock(&mvm->mutex); + + clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + iwl_mvm_d0i3_enable_tx(mvm, NULL); + ret = iwl_mvm_update_quotas(mvm, true, NULL); + if (ret) + IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n", + ret); + + /* allow transport/FW low power modes */ + iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); + + /* + * If we have TDLS peers, remove them. We don't know the last seqno/PN + * of packets the FW sent out, so we must reconnect. + */ + iwl_mvm_teardown_tdls_peers(mvm); + + mutex_unlock(&mvm->mutex); +} + +static void iwl_mvm_resume_complete(struct iwl_mvm *mvm) +{ + if (!iwl_mvm_is_d0i3_supported(mvm)) + return; + + if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) + if (!wait_event_timeout(mvm->d0i3_exit_waitq, + !test_bit(IWL_MVM_STATUS_IN_D0I3, + &mvm->status), + HZ)) + WARN_ONCE(1, "D0i3 exit on resume timed out\n"); +} + +static void +iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw, + enum ieee80211_reconfig_type reconfig_type) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + switch (reconfig_type) { + case IEEE80211_RECONFIG_TYPE_RESTART: + iwl_mvm_restart_complete(mvm); + break; + case IEEE80211_RECONFIG_TYPE_SUSPEND: + iwl_mvm_resume_complete(mvm); + break; + } +} + +void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) +{ + lockdep_assert_held(&mvm->mutex); + + /* firmware counters are obviously reset now, but we shouldn't + * partially track so also clear the fw_reset_accu counters. + */ + memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats)); + + /* + * Disallow low power states when the FW is down by taking + * the UCODE_DOWN ref. in case of ongoing hw restart the + * ref is already taken, so don't take it again. + */ + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); + + /* async_handlers_wk is now blocked */ + + /* + * The work item could be running or queued if the + * ROC time event stops just as we get here. + */ + flush_work(&mvm->roc_done_wk); + + iwl_trans_stop_device(mvm->trans); + + iwl_mvm_async_handlers_purge(mvm); + /* async_handlers_list is empty and will stay empty: HW is stopped */ + + /* the fw is stopped, the aux sta is dead: clean up driver state */ + iwl_mvm_del_aux_sta(mvm); + + /* + * Clear IN_HW_RESTART flag when stopping the hw (as restart_complete() + * won't be called in this case). + * But make sure to cleanup interfaces that have gone down before/during + * HW restart was requested. + */ + if (test_and_clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + ieee80211_iterate_interfaces(mvm->hw, 0, + iwl_mvm_cleanup_iterator, mvm); + + /* We shouldn't have any UIDs still set. Loop over all the UIDs to + * make sure there's nothing left there and warn if any is found. + */ + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { + int i; + + for (i = 0; i < mvm->max_scans; i++) { + if (WARN_ONCE(mvm->scan_uid_status[i], + "UMAC scan UID %d status was not cleaned\n", + i)) + mvm->scan_uid_status[i] = 0; + } + } + + mvm->ucode_loaded = false; +} + +static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + flush_work(&mvm->d0i3_exit_work); + flush_work(&mvm->async_handlers_wk); + cancel_delayed_work_sync(&mvm->fw_dump_wk); + iwl_mvm_free_fw_dump_desc(mvm); + + mutex_lock(&mvm->mutex); + __iwl_mvm_mac_stop(mvm); + mutex_unlock(&mvm->mutex); + + /* + * The worker might have been waiting for the mutex, let it run and + * discover that its list is now empty. + */ + cancel_work_sync(&mvm->async_handlers_wk); +} + +static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) +{ + u16 i; + + lockdep_assert_held(&mvm->mutex); + + for (i = 0; i < NUM_PHY_CTX; i++) + if (!mvm->phy_ctxts[i].ref) + return &mvm->phy_ctxts[i]; + + IWL_ERR(mvm, "No available PHY context\n"); + return NULL; +} + +static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + s16 tx_power) +{ + struct iwl_dev_tx_power_cmd cmd = { + .v2.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC), + .v2.mac_context_id = + cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id), + .v2.pwr_restriction = cpu_to_le16(8 * tx_power), + }; + int len = sizeof(cmd); + + if (tx_power == IWL_DEFAULT_MAX_TX_POWER) + cmd.v2.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER); + + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TX_POWER_CHAIN)) + len = sizeof(cmd.v2); + + return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); +} + +static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + mvmvif->mvm = mvm; + + /* + * make sure D0i3 exit is completed, otherwise a target access + * during tx queue configuration could be done when still in + * D0i3 state. + */ + ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_ADD_IF); + if (ret) + return ret; + + /* + * Not much to do here. The stack will not allow interface + * types or combinations that we didn't advertise, so we + * don't really have to check the types. + */ + + mutex_lock(&mvm->mutex); + + /* make sure that beacon statistics don't go backwards with FW reset */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + mvmvif->beacon_stats.accu_num_beacons += + mvmvif->beacon_stats.num_beacons; + + /* Allocate resources for the MAC context, and add it to the fw */ + ret = iwl_mvm_mac_ctxt_init(mvm, vif); + if (ret) + goto out_unlock; + + /* Counting number of interfaces is needed for legacy PM */ + if (vif->type != NL80211_IFTYPE_P2P_DEVICE) + mvm->vif_count++; + + /* + * The AP binding flow can be done only after the beacon + * template is configured (which happens only in the mac80211 + * start_ap() flow), and adding the broadcast station can happen + * only after the binding. + * In addition, since modifying the MAC before adding a bcast + * station is not allowed by the FW, delay the adding of MAC context to + * the point where we can also add the bcast station. + * In short: there's not much we can do at this point, other than + * allocating resources :) + */ + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) { + ret = iwl_mvm_alloc_bcast_sta(mvm, vif); + if (ret) { + IWL_ERR(mvm, "Failed to allocate bcast sta\n"); + goto out_release; + } + + iwl_mvm_vif_dbgfs_register(mvm, vif); + goto out_unlock; + } + + mvmvif->features |= hw->netdev_features; + + ret = iwl_mvm_mac_ctxt_add(mvm, vif); + if (ret) + goto out_release; + + ret = iwl_mvm_power_update_mac(mvm); + if (ret) + goto out_remove_mac; + + /* beacon filtering */ + ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); + if (ret) + goto out_remove_mac; + + if (!mvm->bf_allowed_vif && + vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { + mvm->bf_allowed_vif = mvmvif; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; + } + + /* + * P2P_DEVICE interface does not have a channel context assigned to it, + * so a dedicated PHY context is allocated to it and the corresponding + * MAC context is bound to it at this stage. + */ + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + + mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!mvmvif->phy_ctxt) { + ret = -ENOSPC; + goto out_free_bf; + } + + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (ret) + goto out_unref_phy; + + ret = iwl_mvm_add_bcast_sta(mvm, vif); + if (ret) + goto out_unbind; + + /* Save a pointer to p2p device vif, so it can later be used to + * update the p2p device MAC when a GO is started/stopped */ + mvm->p2p_device_vif = vif; + } + + iwl_mvm_vif_dbgfs_register(mvm, vif); + goto out_unlock; + + out_unbind: + iwl_mvm_binding_remove_vif(mvm, vif); + out_unref_phy: + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + out_free_bf: + if (mvm->bf_allowed_vif == mvmvif) { + mvm->bf_allowed_vif = NULL; + vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI); + } + out_remove_mac: + mvmvif->phy_ctxt = NULL; + iwl_mvm_mac_ctxt_remove(mvm, vif); + out_release: + if (vif->type != NL80211_IFTYPE_P2P_DEVICE) + mvm->vif_count--; + + iwl_mvm_mac_ctxt_release(mvm, vif); + out_unlock: + mutex_unlock(&mvm->mutex); + + iwl_mvm_unref(mvm, IWL_MVM_REF_ADD_IF); + + return ret; +} + +static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif); + + if (tfd_msk) { + /* + * mac80211 first removes all the stations of the vif and + * then removes the vif. When it removes a station it also + * flushes the AMPDU session. So by now, all the AMPDU sessions + * of all the stations of this vif are closed, and the queues + * of these AMPDU sessions are properly closed. + * We still need to take care of the shared queues of the vif. + * Flush them here. + */ + mutex_lock(&mvm->mutex); + iwl_mvm_flush_tx_path(mvm, tfd_msk, 0); + mutex_unlock(&mvm->mutex); + + /* + * There are transports that buffer a few frames in the host. + * For these, the flush above isn't enough since while we were + * flushing, the transport might have sent more frames to the + * device. To solve this, wait here until the transport is + * empty. Technically, this could have replaced the flush + * above, but flush is much faster than draining. So flush + * first, and drain to make sure we have no frames in the + * transport anymore. + * If a station still had frames on the shared queues, it is + * already marked as draining, so to complete the draining, we + * just need to wait until the transport is empty. + */ + iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_msk); + } + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + /* + * Flush the ROC worker which will flush the OFFCHANNEL queue. + * We assume here that all the packets sent to the OFFCHANNEL + * queue are sent in ROC session. + */ + flush_work(&mvm->roc_done_wk); + } else { + /* + * By now, all the AC queues are empty. The AGG queues are + * empty too. We already got all the Tx responses for all the + * packets in the queues. The drain work can have been + * triggered. Flush it. + */ + flush_work(&mvm->sta_drained_wk); + } +} + +static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + iwl_mvm_prepare_mac_removal(mvm, vif); + + mutex_lock(&mvm->mutex); + + if (mvm->bf_allowed_vif == mvmvif) { + mvm->bf_allowed_vif = NULL; + vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI); + } + + iwl_mvm_vif_dbgfs_clean(mvm, vif); + + /* + * For AP/GO interface, the tear down of the resources allocated to the + * interface is be handled as part of the stop_ap flow. + */ + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) { +#ifdef CONFIG_NL80211_TESTMODE + if (vif == mvm->noa_vif) { + mvm->noa_vif = NULL; + mvm->noa_duration = 0; + } +#endif + iwl_mvm_dealloc_bcast_sta(mvm, vif); + goto out_release; + } + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + mvm->p2p_device_vif = NULL; + iwl_mvm_rm_bcast_sta(mvm, vif); + iwl_mvm_binding_remove_vif(mvm, vif); + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + mvmvif->phy_ctxt = NULL; + } + + if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE) + mvm->vif_count--; + + iwl_mvm_power_update_mac(mvm); + iwl_mvm_mac_ctxt_remove(mvm, vif); + +out_release: + iwl_mvm_mac_ctxt_release(mvm, vif); + mutex_unlock(&mvm->mutex); +} + +static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed) +{ + return 0; +} + +struct iwl_mvm_mc_iter_data { + struct iwl_mvm *mvm; + int port_id; +}; + +static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_mc_iter_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd; + int ret, len; + + /* if we don't have free ports, mcast frames will be dropped */ + if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM)) + return; + + if (vif->type != NL80211_IFTYPE_STATION || + !vif->bss_conf.assoc) + return; + + cmd->port_id = data->port_id++; + memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); + len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4); + + ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_ASYNC, len, cmd); + if (ret) + IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret); +} + +static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm) +{ + struct iwl_mvm_mc_iter_data iter_data = { + .mvm = mvm, + }; + + lockdep_assert_held(&mvm->mutex); + + if (WARN_ON_ONCE(!mvm->mcast_filter_cmd)) + return; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_mc_iface_iterator, &iter_data); +} + +static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mcast_filter_cmd *cmd; + struct netdev_hw_addr *addr; + int addr_count; + bool pass_all; + int len; + + addr_count = netdev_hw_addr_list_count(mc_list); + pass_all = addr_count > MAX_MCAST_FILTERING_ADDRESSES || + IWL_MVM_FW_MCAST_FILTER_PASS_ALL; + if (pass_all) + addr_count = 0; + + len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4); + cmd = kzalloc(len, GFP_ATOMIC); + if (!cmd) + return 0; + + if (pass_all) { + cmd->pass_all = 1; + return (u64)(unsigned long)cmd; + } + + netdev_hw_addr_list_for_each(addr, mc_list) { + IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n", + cmd->count, addr->addr); + memcpy(&cmd->addr_list[cmd->count * ETH_ALEN], + addr->addr, ETH_ALEN); + cmd->count++; + } + + return (u64)(unsigned long)cmd; +} + +static void iwl_mvm_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast; + + mutex_lock(&mvm->mutex); + + /* replace previous configuration */ + kfree(mvm->mcast_filter_cmd); + mvm->mcast_filter_cmd = cmd; + + if (!cmd) + goto out; + + iwl_mvm_recalc_multicast(mvm); +out: + mutex_unlock(&mvm->mutex); + *total_flags = 0; +} + +static void iwl_mvm_config_iface_filter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int filter_flags, + unsigned int changed_flags) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + /* We support only filter for probe requests */ + if (!(changed_flags & FIF_PROBE_REQ)) + return; + + /* Supported only for p2p client interfaces */ + if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc || + !vif->p2p) + return; + + mutex_lock(&mvm->mutex); + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + mutex_unlock(&mvm->mutex); +} + +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING +struct iwl_bcast_iter_data { + struct iwl_mvm *mvm; + struct iwl_bcast_filter_cmd *cmd; + u8 current_filter; +}; + +static void +iwl_mvm_set_bcast_filter(struct ieee80211_vif *vif, + const struct iwl_fw_bcast_filter *in_filter, + struct iwl_fw_bcast_filter *out_filter) +{ + struct iwl_fw_bcast_filter_attr *attr; + int i; + + memcpy(out_filter, in_filter, sizeof(*out_filter)); + + for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) { + attr = &out_filter->attrs[i]; + + if (!attr->mask) + break; + + switch (attr->reserved1) { + case cpu_to_le16(BC_FILTER_MAGIC_IP): + if (vif->bss_conf.arp_addr_cnt != 1) { + attr->mask = 0; + continue; + } + + attr->val = vif->bss_conf.arp_addr_list[0]; + break; + case cpu_to_le16(BC_FILTER_MAGIC_MAC): + attr->val = *(__be32 *)&vif->addr[2]; + break; + default: + break; + } + attr->reserved1 = 0; + out_filter->num_attrs++; + } +} + +static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_bcast_iter_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct iwl_bcast_filter_cmd *cmd = data->cmd; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_fw_bcast_mac *bcast_mac; + int i; + + if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs))) + return; + + bcast_mac = &cmd->macs[mvmvif->id]; + + /* + * enable filtering only for associated stations, but not for P2P + * Clients + */ + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p || + !vif->bss_conf.assoc) + return; + + bcast_mac->default_discard = 1; + + /* copy all configured filters */ + for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) { + /* + * Make sure we don't exceed our filters limit. + * if there is still a valid filter to be configured, + * be on the safe side and just allow bcast for this mac. + */ + if (WARN_ON_ONCE(data->current_filter >= + ARRAY_SIZE(cmd->filters))) { + bcast_mac->default_discard = 0; + bcast_mac->attached_filters = 0; + break; + } + + iwl_mvm_set_bcast_filter(vif, + &mvm->bcast_filters[i], + &cmd->filters[data->current_filter]); + + /* skip current filter if it contains no attributes */ + if (!cmd->filters[data->current_filter].num_attrs) + continue; + + /* attach the filter to current mac */ + bcast_mac->attached_filters |= + cpu_to_le16(BIT(data->current_filter)); + + data->current_filter++; + } +} + +bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, + struct iwl_bcast_filter_cmd *cmd) +{ + struct iwl_bcast_iter_data iter_data = { + .mvm = mvm, + .cmd = cmd, + }; + + if (IWL_MVM_FW_BCAST_FILTER_PASS_ALL) + return false; + + memset(cmd, 0, sizeof(*cmd)); + cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters); + cmd->max_macs = ARRAY_SIZE(cmd->macs); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* use debugfs filters/macs if override is configured */ + if (mvm->dbgfs_bcast_filtering.override) { + memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters, + sizeof(cmd->filters)); + memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs, + sizeof(cmd->macs)); + return true; + } +#endif + + /* if no filters are configured, do nothing */ + if (!mvm->bcast_filters) + return false; + + /* configure and attach these filters for each associated sta vif */ + ieee80211_iterate_active_interfaces( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bcast_filter_iterator, &iter_data); + + return true; +} +static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_bcast_filter_cmd cmd; + + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING)) + return 0; + + if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) + return 0; + + return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, + sizeof(cmd), &cmd); +} +#else +static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + return 0; +} +#endif + +static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + /* + * Re-calculate the tsf id, as the master-slave relations depend on the + * beacon interval, which was not known when the station interface was + * added. + */ + if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) + iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); + + /* + * If we're not associated yet, take the (new) BSSID before associating + * so the firmware knows. If we're already associated, then use the old + * BSSID here, and we'll send a cleared one later in the CHANGED_ASSOC + * branch for disassociation below. + */ + if (changes & BSS_CHANGED_BSSID && !mvmvif->associated) + memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN); + + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->bssid); + if (ret) + IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); + + /* after sending it once, adopt mac80211 data */ + memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN); + mvmvif->associated = bss_conf->assoc; + + if (changes & BSS_CHANGED_ASSOC) { + if (bss_conf->assoc) { + /* clear statistics to get clean beacon counter */ + iwl_mvm_request_statistics(mvm, true); + memset(&mvmvif->beacon_stats, 0, + sizeof(mvmvif->beacon_stats)); + + /* add quota for this interface */ + ret = iwl_mvm_update_quotas(mvm, true, NULL); + if (ret) { + IWL_ERR(mvm, "failed to update quotas\n"); + return; + } + + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, + &mvm->status)) { + /* + * If we're restarting then the firmware will + * obviously have lost synchronisation with + * the AP. It will attempt to synchronise by + * itself, but we can make it more reliable by + * scheduling a session protection time event. + * + * The firmware needs to receive a beacon to + * catch up with synchronisation, use 110% of + * the beacon interval. + * + * Set a large maximum delay to allow for more + * than a single interface. + */ + u32 dur = (11 * vif->bss_conf.beacon_int) / 10; + iwl_mvm_protect_session(mvm, vif, dur, dur, + 5 * dur, false); + } + + iwl_mvm_sf_update(mvm, vif, false); + iwl_mvm_power_vif_assoc(mvm, vif); + if (vif->p2p) { + iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT); + iwl_mvm_update_smps(mvm, vif, + IWL_MVM_SMPS_REQ_PROT, + IEEE80211_SMPS_DYNAMIC); + } + } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { + /* + * If update fails - SF might be running in associated + * mode while disassociated - which is forbidden. + */ + WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false), + "Failed to update SF upon disassociation\n"); + + /* remove AP station now that the MAC is unassoc */ + ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); + if (ret) + IWL_ERR(mvm, "failed to remove AP station\n"); + + if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) + mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + /* remove quota for this interface */ + ret = iwl_mvm_update_quotas(mvm, false, NULL); + if (ret) + IWL_ERR(mvm, "failed to update quotas\n"); + + if (vif->p2p) + iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT); + + /* this will take the cleared BSSID from bss_conf */ + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + if (ret) + IWL_ERR(mvm, + "failed to update MAC %pM (clear after unassoc)\n", + vif->addr); + } + + iwl_mvm_recalc_multicast(mvm); + iwl_mvm_configure_bcast_filter(mvm, vif); + + /* reset rssi values */ + mvmvif->bf_data.ave_beacon_signal = 0; + + 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 + * remove the session protection. + */ + iwl_mvm_remove_time_event(mvm, mvmvif, + &mvmvif->time_event_data); + } + + if (changes & BSS_CHANGED_BEACON_INFO) { + iwl_mvm_sf_update(mvm, vif, false); + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); + } + + if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS)) { + ret = iwl_mvm_power_update_mac(mvm); + if (ret) + IWL_ERR(mvm, "failed to update power mode\n"); + } + + if (changes & BSS_CHANGED_TXPOWER) { + IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n", + bss_conf->txpower); + iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower); + } + + if (changes & BSS_CHANGED_CQM) { + IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n"); + /* reset cqm events tracking */ + mvmvif->bf_data.last_cqm_event = 0; + if (mvmvif->bf_data.bf_enabled) { + ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); + if (ret) + IWL_ERR(mvm, + "failed to update CQM thresholds\n"); + } + } + + if (changes & BSS_CHANGED_ARP_FILTER) { + IWL_DEBUG_MAC80211(mvm, "arp filter changed\n"); + iwl_mvm_configure_bcast_filter(mvm, vif); + } +} + +static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + /* + * iwl_mvm_mac_ctxt_add() might read directly from the device + * (the system time), so make sure it is available. + */ + ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_START_AP); + if (ret) + return ret; + + mutex_lock(&mvm->mutex); + + /* Send the beacon template */ + ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif); + if (ret) + goto out_unlock; + + /* + * Re-calculate the tsf id, as the master-slave relations depend on the + * beacon interval, which was not known when the AP interface was added. + */ + if (vif->type == NL80211_IFTYPE_AP) + iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); + + mvmvif->ap_assoc_sta_count = 0; + + /* Add the mac context */ + ret = iwl_mvm_mac_ctxt_add(mvm, vif); + if (ret) + goto out_unlock; + + /* Perform the binding */ + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (ret) + goto out_remove; + + /* Send the bcast station. At this stage the TBTT and DTIM time events + * are added and applied to the scheduler */ + ret = iwl_mvm_send_add_bcast_sta(mvm, vif); + if (ret) + goto out_unbind; + + /* must be set before quota calculations */ + mvmvif->ap_ibss_active = true; + + /* power updated needs to be done before quotas */ + iwl_mvm_power_update_mac(mvm); + + ret = iwl_mvm_update_quotas(mvm, false, NULL); + if (ret) + goto out_quota_failed; + + /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ + if (vif->p2p && mvm->p2p_device_vif) + iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL); + + iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS); + + iwl_mvm_bt_coex_vif_change(mvm); + + /* we don't support TDLS during DCM */ + if (iwl_mvm_phy_ctx_count(mvm) > 1) + iwl_mvm_teardown_tdls_peers(mvm); + + goto out_unlock; + +out_quota_failed: + iwl_mvm_power_update_mac(mvm); + mvmvif->ap_ibss_active = false; + iwl_mvm_send_rm_bcast_sta(mvm, vif); +out_unbind: + iwl_mvm_binding_remove_vif(mvm, vif); +out_remove: + iwl_mvm_mac_ctxt_remove(mvm, vif); +out_unlock: + mutex_unlock(&mvm->mutex); + iwl_mvm_unref(mvm, IWL_MVM_REF_START_AP); + return ret; +} + +static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + iwl_mvm_prepare_mac_removal(mvm, vif); + + mutex_lock(&mvm->mutex); + + /* Handle AP stop while in CSA */ + if (rcu_access_pointer(mvm->csa_vif) == vif) { + iwl_mvm_remove_time_event(mvm, mvmvif, + &mvmvif->time_event_data); + RCU_INIT_POINTER(mvm->csa_vif, NULL); + mvmvif->csa_countdown = false; + } + + if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) { + RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL); + mvm->csa_tx_block_bcn_timeout = 0; + } + + mvmvif->ap_ibss_active = false; + mvm->ap_last_beacon_gp2 = 0; + + iwl_mvm_bt_coex_vif_change(mvm); + + iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS); + + /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ + if (vif->p2p && mvm->p2p_device_vif) + iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL); + + iwl_mvm_update_quotas(mvm, false, NULL); + iwl_mvm_send_rm_bcast_sta(mvm, vif); + iwl_mvm_binding_remove_vif(mvm, vif); + + iwl_mvm_power_update_mac(mvm); + + iwl_mvm_mac_ctxt_remove(mvm, vif); + + mutex_unlock(&mvm->mutex); +} + +static void +iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + /* Changes will be applied when the AP/IBSS is started */ + if (!mvmvif->ap_ibss_active) + return; + + if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT | + BSS_CHANGED_BANDWIDTH | BSS_CHANGED_QOS) && + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL)) + IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); + + /* Need to send a new beacon template to the FW */ + if (changes & BSS_CHANGED_BEACON && + iwl_mvm_mac_ctxt_beacon_changed(mvm, vif)) + IWL_WARN(mvm, "Failed updating beacon data\n"); + + if (changes & BSS_CHANGED_TXPOWER) { + IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n", + bss_conf->txpower); + iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower); + } + +} + +static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + /* + * iwl_mvm_bss_info_changed_station() might call + * iwl_mvm_protect_session(), which reads directly from + * the device (the system time), so make sure it is available. + */ + if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_BSS_CHANGED)) + return; + + mutex_lock(&mvm->mutex); + + if (changes & BSS_CHANGED_IDLE && !bss_conf->idle) + iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes); + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes); + break; + default: + /* shouldn't happen */ + WARN_ON_ONCE(1); + } + + mutex_unlock(&mvm->mutex); + iwl_mvm_unref(mvm, IWL_MVM_REF_BSS_CHANGED); +} + +static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + if (hw_req->req.n_channels == 0 || + hw_req->req.n_channels > mvm->fw->ucode_capa.n_scan_channels) + return -EINVAL; + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_reg_scan_start(mvm, vif, &hw_req->req, &hw_req->ies); + mutex_unlock(&mvm->mutex); + + return ret; +} + +static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + mutex_lock(&mvm->mutex); + + /* Due to a race condition, it's possible that mac80211 asks + * us to stop a hw_scan when it's already stopped. This can + * happen, for instance, if we stopped the scan ourselves, + * called ieee80211_scan_completed() and the userspace called + * cancel scan scan before ieee80211_scan_work() could run. + * To handle that, simply return if the scan is not running. + */ + if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) + iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); + + mutex_unlock(&mvm->mutex); +} + +static void +iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, u16 tids, + int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + /* Called when we need to transmit (a) frame(s) from mac80211 */ + + iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, + tids, more_data, false); +} + +static void +iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, u16 tids, + int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + /* Called when we need to transmit (a) frame(s) from agg queue */ + + iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, + tids, more_data, true); +} + +static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + unsigned long txqs = 0, tids = 0; + int tid; + + spin_lock_bh(&mvmsta->lock); + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + + if (tid_data->state != IWL_AGG_ON && + tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA) + continue; + + __set_bit(tid_data->txq_id, &txqs); + + if (iwl_mvm_tid_queued(tid_data) == 0) + continue; + + __set_bit(tid, &tids); + } + + switch (cmd) { + case STA_NOTIFY_SLEEP: + if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0) + ieee80211_sta_block_awake(hw, sta, true); + + for_each_set_bit(tid, &tids, IWL_MAX_TID_COUNT) + ieee80211_sta_set_buffered(sta, tid, true); + + if (txqs) + iwl_trans_freeze_txq_timer(mvm->trans, txqs, true); + /* + * The fw updates the STA to be asleep. Tx packets on the Tx + * queues to this station will not be transmitted. The fw will + * send a Tx response with TX_STATUS_FAIL_DEST_PS. + */ + break; + case STA_NOTIFY_AWAKE: + if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) + break; + + if (txqs) + iwl_trans_freeze_txq_timer(mvm->trans, txqs, false); + iwl_mvm_sta_modify_ps_wake(mvm, sta); + break; + default: + break; + } + spin_unlock_bh(&mvmsta->lock); +} + +static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + + /* + * This is called before mac80211 does RCU synchronisation, + * so here we already invalidate our internal RCU-protected + * station pointer. The rest of the code will thus no longer + * be able to find the station this way, and we don't rely + * on further RCU synchronisation after the sta_state() + * callback deleted the station. + */ + mutex_lock(&mvm->mutex); + if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id])) + rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], + ERR_PTR(-ENOENT)); + + if (mvm_sta->vif->type == NL80211_IFTYPE_AP) { + mvmvif->ap_assoc_sta_count--; + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + } + + mutex_unlock(&mvm->mutex); +} + +static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + const u8 *bssid) +{ + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT)) + return; + + if (iwlwifi_mod_params.uapsd_disable) { + vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; + return; + } + + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; +} + +static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n", + sta->addr, old_state, new_state); + + /* this would be a mac80211 bug ... but don't crash */ + if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) + return -EINVAL; + + /* if a STA is being removed, reuse its ID */ + flush_work(&mvm->sta_drained_wk); + + mutex_lock(&mvm->mutex); + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { + /* + * Firmware bug - it'll crash if the beacon interval is less + * than 16. We can't avoid connecting at all, so refuse the + * station state change, this will cause mac80211 to abandon + * attempts to connect to this AP, and eventually wpa_s will + * blacklist the AP... + */ + if (vif->type == NL80211_IFTYPE_STATION && + vif->bss_conf.beacon_int < 16) { + IWL_ERR(mvm, + "AP %pM beacon interval is %d, refusing due to firmware bug!\n", + sta->addr, vif->bss_conf.beacon_int); + ret = -EINVAL; + goto out_unlock; + } + + if (sta->tdls && + (vif->p2p || + iwl_mvm_tdls_sta_count(mvm, NULL) == + IWL_MVM_TDLS_STA_COUNT || + iwl_mvm_phy_ctx_count(mvm) > 1)) { + IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n"); + ret = -EBUSY; + goto out_unlock; + } + + ret = iwl_mvm_add_sta(mvm, vif, sta); + if (sta->tdls && ret == 0) + iwl_mvm_recalc_tdls_state(mvm, vif, true); + } else if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_AUTH) { + /* + * EBS may be disabled due to previous failures reported by FW. + * Reset EBS status here assuming environment has been changed. + */ + mvm->last_ebs_successful = true; + iwl_mvm_check_uapsd(mvm, vif, sta->addr); + ret = 0; + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) { + ret = iwl_mvm_update_sta(mvm, vif, sta); + if (ret == 0) + iwl_mvm_rs_rate_init(mvm, sta, + mvmvif->phy_ctxt->channel->band, + true); + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTHORIZED) { + + /* we don't support TDLS during DCM */ + if (iwl_mvm_phy_ctx_count(mvm) > 1) + iwl_mvm_teardown_tdls_peers(mvm); + + /* enable beacon filtering */ + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); + ret = 0; + } else if (old_state == IEEE80211_STA_AUTHORIZED && + new_state == IEEE80211_STA_ASSOC) { + /* disable beacon filtering */ + WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, 0)); + ret = 0; + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH) { + ret = 0; + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_NONE) { + ret = 0; + } else if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST) { + ret = iwl_mvm_rm_sta(mvm, vif, sta); + if (sta->tdls) + iwl_mvm_recalc_tdls_state(mvm, vif, false); + } else { + ret = -EIO; + } + out_unlock: + mutex_unlock(&mvm->mutex); + + if (sta->tdls && ret == 0) { + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) + ieee80211_reserve_tid(sta, IWL_MVM_TDLS_FW_TID); + else if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST) + ieee80211_unreserve_tid(sta, IWL_MVM_TDLS_FW_TID); + } + + return ret; +} + +static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + mvm->rts_threshold = value; + + return 0; +} + +static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u32 changed) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + if (vif->type == NL80211_IFTYPE_STATION && + changed & IEEE80211_RC_NSS_CHANGED) + iwl_mvm_sf_update(mvm, vif, false); +} + +static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + mvmvif->queue_params[ac] = *params; + + /* + * No need to update right away, we'll get BSS_CHANGED_QOS + * The exception is P2P_DEVICE interface which needs immediate update. + */ + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + int ret; + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + mutex_unlock(&mvm->mutex); + return ret; + } + return 0; +} + +static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + u32 duration = min(IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS, + 200 + vif->bss_conf.beacon_int); + u32 min_duration = min(IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS, + 100 + vif->bss_conf.beacon_int); + + if (WARN_ON_ONCE(vif->bss_conf.assoc)) + return; + + /* + * iwl_mvm_protect_session() reads directly from the device + * (the system time), so make sure it is available. + */ + if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PREPARE_TX)) + return; + + mutex_lock(&mvm->mutex); + /* Try really hard to protect the session and hear a beacon */ + iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500, false); + mutex_unlock(&mvm->mutex); + + iwl_mvm_unref(mvm, IWL_MVM_REF_PREPARE_TX); +} + +static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + int ret; + + mutex_lock(&mvm->mutex); + + if (!vif->bss_conf.idle) { + ret = -EBUSY; + goto out; + } + + ret = iwl_mvm_sched_scan_start(mvm, vif, req, ies, IWL_MVM_SCAN_SCHED); + +out: + mutex_unlock(&mvm->mutex); + return ret; +} + +static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + mutex_lock(&mvm->mutex); + + /* Due to a race condition, it's possible that mac80211 asks + * us to stop a sched_scan when it's already stopped. This + * can happen, for instance, if we stopped the scan ourselves, + * called ieee80211_sched_scan_stopped() and the userspace called + * stop sched scan scan before ieee80211_sched_scan_stopped_work() + * could run. To handle this, simply return if the scan is + * not running. + */ + if (!(mvm->scan_status & IWL_MVM_SCAN_SCHED)) { + mutex_unlock(&mvm->mutex); + return 0; + } + + ret = iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, false); + mutex_unlock(&mvm->mutex); + iwl_mvm_wait_for_async_handlers(mvm); + + return ret; +} + +static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + if (iwlwifi_mod_params.sw_crypto) { + IWL_DEBUG_MAC80211(mvm, "leave - hwcrypto disabled\n"); + return -EOPNOTSUPP; + } + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + break; + case WLAN_CIPHER_SUITE_CCMP: + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + WARN_ON_ONCE(!ieee80211_hw_check(hw, MFP_CAPABLE)); + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* For non-client mode, only use WEP keys for TX as we probably + * don't have a station yet anyway and would then have to keep + * track of the keys, linking them to each of the clients/peers + * as they appear. For now, don't do that, for performance WEP + * offload doesn't really matter much, but we need it for some + * other offload features in client mode. + */ + if (vif->type != NL80211_IFTYPE_STATION) + return 0; + break; + default: + /* currently FW supports only one optional cipher scheme */ + if (hw->n_cipher_schemes && + hw->cipher_schemes->cipher == key->cipher) + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + else + return -EOPNOTSUPP; + } + + mutex_lock(&mvm->mutex); + + switch (cmd) { + case SET_KEY: + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_AP) && !sta) { + /* + * GTK on AP interface is a TX-only key, return 0; + * on IBSS they're per-station and because we're lazy + * we don't support them for RX, so do the same. + */ + ret = 0; + key->hw_key_idx = STA_KEY_IDX_INVALID; + break; + } + + /* During FW restart, in order to restore the state as it was, + * don't try to reprogram keys we previously failed for. + */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + key->hw_key_idx == STA_KEY_IDX_INVALID) { + IWL_DEBUG_MAC80211(mvm, + "skip invalid idx key programming during restart\n"); + ret = 0; + break; + } + + IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n"); + ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, + test_bit(IWL_MVM_STATUS_IN_HW_RESTART, + &mvm->status)); + if (ret) { + IWL_WARN(mvm, "set key failed\n"); + /* + * can't add key for RX, but we don't need it + * in the device for TX so still return 0 + */ + key->hw_key_idx = STA_KEY_IDX_INVALID; + ret = 0; + } + + break; + case DISABLE_KEY: + if (key->hw_key_idx == STA_KEY_IDX_INVALID) { + ret = 0; + break; + } + + IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n"); + ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&mvm->mutex); + return ret; +} + +static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, + u32 iv32, u16 *phase1key) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID) + return; + + iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key); +} + + +static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mvm *mvm = + container_of(notif_wait, struct iwl_mvm, notif_wait); + struct iwl_hs20_roc_res *resp; + int resp_len = iwl_rx_packet_payload_len(pkt); + struct iwl_mvm_time_event_data *te_data = data; + + if (WARN_ON(pkt->hdr.cmd != HOT_SPOT_CMD)) + return true; + + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { + IWL_ERR(mvm, "Invalid HOT_SPOT_CMD response\n"); + return true; + } + + resp = (void *)pkt->data; + + IWL_DEBUG_TE(mvm, + "Aux ROC: Recieved response from ucode: status=%d uid=%d\n", + resp->status, resp->event_unique_id); + + te_data->uid = le32_to_cpu(resp->event_unique_id); + IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", + te_data->uid); + + spin_lock_bh(&mvm->time_event_lock); + list_add_tail(&te_data->list, &mvm->aux_roc_te_list); + spin_unlock_bh(&mvm->time_event_lock); + + return true; +} + +#define AUX_ROC_MAX_DELAY_ON_CHANNEL 200 +static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, + struct ieee80211_channel *channel, + struct ieee80211_vif *vif, + int duration) +{ + int res, time_reg = DEVICE_SYSTEM_TIME_REG; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data; + static const u16 time_event_response[] = { HOT_SPOT_CMD }; + struct iwl_notification_wait wait_time_event; + struct iwl_hs20_roc_req aux_roc_req = { + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + .id_and_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(MAC_INDEX_AUX, 0)), + .sta_id_and_color = cpu_to_le32(mvm->aux_sta.sta_id), + /* Set the channel info data */ + .channel_info.band = (channel->band == IEEE80211_BAND_2GHZ) ? + PHY_BAND_24 : PHY_BAND_5, + .channel_info.channel = channel->hw_value, + .channel_info.width = PHY_VHT_CHANNEL_MODE20, + /* Set the time and duration */ + .apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)), + .apply_time_max_delay = + cpu_to_le32(MSEC_TO_TU(AUX_ROC_MAX_DELAY_ON_CHANNEL)), + .duration = cpu_to_le32(MSEC_TO_TU(duration)), + }; + + /* Set the node address */ + memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN); + + lockdep_assert_held(&mvm->mutex); + + spin_lock_bh(&mvm->time_event_lock); + + if (WARN_ON(te_data->id == HOT_SPOT_CMD)) { + spin_unlock_bh(&mvm->time_event_lock); + return -EIO; + } + + te_data->vif = vif; + te_data->duration = duration; + te_data->id = HOT_SPOT_CMD; + + spin_unlock_bh(&mvm->time_event_lock); + + /* + * Use a notification wait, which really just processes the + * command response and doesn't wait for anything, in order + * to be able to process the response and get the UID inside + * the RX path. Using CMD_WANT_SKB doesn't work because it + * stores the buffer and then wakes up this thread, by which + * time another notification (that the time event started) + * might already be processed unsuccessfully. + */ + iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, + time_event_response, + ARRAY_SIZE(time_event_response), + iwl_mvm_rx_aux_roc, te_data); + + res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, sizeof(aux_roc_req), + &aux_roc_req); + + if (res) { + IWL_ERR(mvm, "Couldn't send HOT_SPOT_CMD: %d\n", res); + iwl_remove_notification(&mvm->notif_wait, &wait_time_event); + goto out_clear_te; + } + + /* No need to wait for anything, so just pass 1 (0 isn't valid) */ + res = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1); + /* should never fail */ + WARN_ON_ONCE(res); + + if (res) { + out_clear_te: + spin_lock_bh(&mvm->time_event_lock); + iwl_mvm_te_clear_data(mvm, te_data); + spin_unlock_bh(&mvm->time_event_lock); + } + + return res; +} + +static int iwl_mvm_roc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *channel, + int duration, + enum ieee80211_roc_type type) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct cfg80211_chan_def chandef; + struct iwl_mvm_phy_ctxt *phy_ctxt; + int ret, i; + + IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, + duration, type); + + flush_work(&mvm->roc_done_wk); + + mutex_lock(&mvm->mutex); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) { + /* Use aux roc framework (HS20) */ + ret = iwl_mvm_send_aux_roc_cmd(mvm, channel, + vif, duration); + goto out_unlock; + } + IWL_ERR(mvm, "hotspot not supported\n"); + ret = -EINVAL; + goto out_unlock; + case NL80211_IFTYPE_P2P_DEVICE: + /* handle below */ + break; + default: + IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type); + ret = -EINVAL; + goto out_unlock; + } + + for (i = 0; i < NUM_PHY_CTX; i++) { + phy_ctxt = &mvm->phy_ctxts[i]; + if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt) + continue; + + if (phy_ctxt->ref && channel == phy_ctxt->channel) { + /* + * Unbind the P2P_DEVICE from the current PHY context, + * and if the PHY context is not used remove it. + */ + ret = iwl_mvm_binding_remove_vif(mvm, vif); + if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + + /* Bind the P2P_DEVICE to the current PHY Context */ + mvmvif->phy_ctxt = phy_ctxt; + + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (WARN(ret, "Failed binding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); + goto schedule_time_event; + } + } + + /* Need to update the PHY context only if the ROC channel changed */ + if (channel == mvmvif->phy_ctxt->channel) + goto schedule_time_event; + + cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); + + /* + * Change the PHY context configuration as it is currently referenced + * only by the P2P Device MAC + */ + if (mvmvif->phy_ctxt->ref == 1) { + ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt, + &chandef, 1, 1); + if (ret) + goto out_unlock; + } else { + /* + * The PHY context is shared with other MACs. Need to remove the + * P2P Device from the binding, allocate an new PHY context and + * create a new binding + */ + phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!phy_ctxt) { + ret = -ENOSPC; + goto out_unlock; + } + + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef, + 1, 1); + if (ret) { + IWL_ERR(mvm, "Failed to change PHY context\n"); + goto out_unlock; + } + + /* Unbind the P2P_DEVICE from the current PHY context */ + ret = iwl_mvm_binding_remove_vif(mvm, vif); + if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + + /* Bind the P2P_DEVICE to the new allocated PHY context */ + mvmvif->phy_ctxt = phy_ctxt; + + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (WARN(ret, "Failed binding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); + } + +schedule_time_event: + /* Schedule the time events */ + ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type); + +out_unlock: + mutex_unlock(&mvm->mutex); + IWL_DEBUG_MAC80211(mvm, "leave\n"); + return ret; +} + +static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + IWL_DEBUG_MAC80211(mvm, "enter\n"); + + mutex_lock(&mvm->mutex); + iwl_mvm_stop_roc(mvm); + mutex_unlock(&mvm->mutex); + + IWL_DEBUG_MAC80211(mvm, "leave\n"); + return 0; +} + +static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm, + struct ieee80211_chanctx_conf *ctx) +{ + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt; + int ret; + + lockdep_assert_held(&mvm->mutex); + + IWL_DEBUG_MAC80211(mvm, "Add channel context\n"); + + phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!phy_ctxt) { + ret = -ENOSPC; + goto out; + } + + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def, + ctx->rx_chains_static, + ctx->rx_chains_dynamic); + if (ret) { + IWL_ERR(mvm, "Failed to add PHY context\n"); + goto out; + } + + iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt); + *phy_ctxt_id = phy_ctxt->id; +out: + return ret; +} + +static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + mutex_lock(&mvm->mutex); + ret = __iwl_mvm_add_chanctx(mvm, ctx); + mutex_unlock(&mvm->mutex); + + return ret; +} + +static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm, + struct ieee80211_chanctx_conf *ctx) +{ + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; + + lockdep_assert_held(&mvm->mutex); + + iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt); +} + +static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + mutex_lock(&mvm->mutex); + __iwl_mvm_remove_chanctx(mvm, ctx); + mutex_unlock(&mvm->mutex); +} + +static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; + + if (WARN_ONCE((phy_ctxt->ref > 1) && + (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH | + IEEE80211_CHANCTX_CHANGE_RX_CHAINS | + IEEE80211_CHANCTX_CHANGE_RADAR | + IEEE80211_CHANCTX_CHANGE_MIN_WIDTH)), + "Cannot change PHY. Ref=%d, changed=0x%X\n", + phy_ctxt->ref, changed)) + return; + + mutex_lock(&mvm->mutex); + iwl_mvm_bt_coex_vif_change(mvm); + iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def, + ctx->rx_chains_static, + ctx->rx_chains_dynamic); + mutex_unlock(&mvm->mutex); +} + +static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx, + bool switching_chanctx) +{ + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + lockdep_assert_held(&mvm->mutex); + + mvmvif->phy_ctxt = phy_ctxt; + + switch (vif->type) { + case NL80211_IFTYPE_AP: + /* only needed if we're switching chanctx (i.e. during CSA) */ + if (switching_chanctx) { + mvmvif->ap_ibss_active = true; + break; + } + case NL80211_IFTYPE_ADHOC: + /* + * The AP binding flow is handled as part of the start_ap flow + * (in bss_info_changed), similarly for IBSS. + */ + ret = 0; + goto out; + case NL80211_IFTYPE_STATION: + break; + case NL80211_IFTYPE_MONITOR: + /* always disable PS when a monitor interface is active */ + mvmvif->ps_disabled = true; + break; + default: + ret = -EINVAL; + goto out; + } + + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (ret) + goto out; + + /* + * Power state must be updated before quotas, + * otherwise fw will complain. + */ + iwl_mvm_power_update_mac(mvm); + + /* Setting the quota at this stage is only required for monitor + * interfaces. For the other types, the bss_info changed flow + * will handle quota settings. + */ + if (vif->type == NL80211_IFTYPE_MONITOR) { + mvmvif->monitor_active = true; + ret = iwl_mvm_update_quotas(mvm, false, NULL); + if (ret) + goto out_remove_binding; + } + + /* Handle binding during CSA */ + if (vif->type == NL80211_IFTYPE_AP) { + iwl_mvm_update_quotas(mvm, false, NULL); + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + } + + if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) { + u32 duration = 2 * vif->bss_conf.beacon_int; + + /* iwl_mvm_protect_session() reads directly from the + * device (the system time), so make sure it is + * available. + */ + ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA); + if (ret) + goto out_remove_binding; + + /* Protect the session to make sure we hear the first + * beacon on the new channel. + */ + iwl_mvm_protect_session(mvm, vif, duration, duration, + vif->bss_conf.beacon_int / 2, + true); + + iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA); + + iwl_mvm_update_quotas(mvm, false, NULL); + } + + goto out; + +out_remove_binding: + iwl_mvm_binding_remove_vif(mvm, vif); + iwl_mvm_power_update_mac(mvm); +out: + if (ret) + mvmvif->phy_ctxt = NULL; + return ret; +} +static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + mutex_lock(&mvm->mutex); + ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false); + mutex_unlock(&mvm->mutex); + + return ret; +} + +static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx, + bool switching_chanctx) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct ieee80211_vif *disabled_vif = NULL; + + lockdep_assert_held(&mvm->mutex); + + iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); + + switch (vif->type) { + case NL80211_IFTYPE_ADHOC: + goto out; + case NL80211_IFTYPE_MONITOR: + mvmvif->monitor_active = false; + mvmvif->ps_disabled = false; + break; + case NL80211_IFTYPE_AP: + /* This part is triggered only during CSA */ + if (!switching_chanctx || !mvmvif->ap_ibss_active) + goto out; + + mvmvif->csa_countdown = false; + + /* Set CS bit on all the stations */ + iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true); + + /* Save blocked iface, the timeout is set on the next beacon */ + rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif); + + mvmvif->ap_ibss_active = false; + break; + case NL80211_IFTYPE_STATION: + if (!switching_chanctx) + break; + + disabled_vif = vif; + + iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL); + break; + default: + break; + } + + iwl_mvm_update_quotas(mvm, false, disabled_vif); + iwl_mvm_binding_remove_vif(mvm, vif); + +out: + mvmvif->phy_ctxt = NULL; + iwl_mvm_power_update_mac(mvm); +} + +static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + mutex_lock(&mvm->mutex); + __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false); + mutex_unlock(&mvm->mutex); +} + +static int +iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm, + struct ieee80211_vif_chanctx_switch *vifs) +{ + int ret; + + mutex_lock(&mvm->mutex); + __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true); + __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx); + + ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx); + if (ret) { + IWL_ERR(mvm, "failed to add new_ctx during channel switch\n"); + goto out_reassign; + } + + ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx, + true); + if (ret) { + IWL_ERR(mvm, + "failed to assign new_ctx during channel switch\n"); + goto out_remove; + } + + /* we don't support TDLS during DCM - can be caused by channel switch */ + if (iwl_mvm_phy_ctx_count(mvm) > 1) + iwl_mvm_teardown_tdls_peers(mvm); + + goto out; + +out_remove: + __iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx); + +out_reassign: + if (__iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx)) { + IWL_ERR(mvm, "failed to add old_ctx back after failure.\n"); + goto out_restart; + } + + if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, + true)) { + IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n"); + goto out_restart; + } + + goto out; + +out_restart: + /* things keep failing, better restart the hw */ + iwl_mvm_nic_restart(mvm, false); + +out: + mutex_unlock(&mvm->mutex); + + return ret; +} + +static int +iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm, + struct ieee80211_vif_chanctx_switch *vifs) +{ + int ret; + + mutex_lock(&mvm->mutex); + __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true); + + ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx, + true); + if (ret) { + IWL_ERR(mvm, + "failed to assign new_ctx during channel switch\n"); + goto out_reassign; + } + + goto out; + +out_reassign: + if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, + true)) { + IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n"); + goto out_restart; + } + + goto out; + +out_restart: + /* things keep failing, better restart the hw */ + iwl_mvm_nic_restart(mvm, false); + +out: + mutex_unlock(&mvm->mutex); + + return ret; +} + +static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + /* we only support a single-vif right now */ + if (n_vifs > 1) + return -EOPNOTSUPP; + + switch (mode) { + case CHANCTX_SWMODE_SWAP_CONTEXTS: + ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs); + break; + case CHANCTX_SWMODE_REASSIGN_VIF: + ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static int iwl_mvm_set_tim(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + bool set) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + + if (!mvm_sta || !mvm_sta->vif) { + IWL_ERR(mvm, "Station is not associated to a vif\n"); + return -EINVAL; + } + + return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif); +} + +#ifdef CONFIG_NL80211_TESTMODE +static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = { + [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 }, + [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 }, + [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 }, +}; + +static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + void *data, int len) +{ + struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1]; + int err; + u32 noa_duration; + + err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy); + if (err) + return err; + + if (!tb[IWL_MVM_TM_ATTR_CMD]) + return -EINVAL; + + switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) { + case IWL_MVM_TM_CMD_SET_NOA: + if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p || + !vif->bss_conf.enable_beacon || + !tb[IWL_MVM_TM_ATTR_NOA_DURATION]) + return -EINVAL; + + noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]); + if (noa_duration >= vif->bss_conf.beacon_int) + return -EINVAL; + + mvm->noa_duration = noa_duration; + mvm->noa_vif = vif; + + return iwl_mvm_update_quotas(mvm, false, NULL); + case IWL_MVM_TM_CMD_SET_BEACON_FILTER: + /* must be associated client vif - ignore authorized */ + if (!vif || vif->type != NL80211_IFTYPE_STATION || + !vif->bss_conf.assoc || !vif->bss_conf.dtim_period || + !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]) + return -EINVAL; + + if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])) + return iwl_mvm_enable_beacon_filter(mvm, vif, 0); + return iwl_mvm_disable_beacon_filter(mvm, vif, 0); + } + + return -EOPNOTSUPP; +} + +static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, int len) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int err; + + mutex_lock(&mvm->mutex); + err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len); + mutex_unlock(&mvm->mutex); + + return err; +} +#endif + +static void iwl_mvm_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + /* By implementing this operation, we prevent mac80211 from + * starting its own channel switch timer, so that we can call + * ieee80211_chswitch_done() ourselves at the right time + * (which is when the absence time event starts). + */ + + IWL_DEBUG_MAC80211(IWL_MAC80211_GET_MVM(hw), + "dummy channel switch op\n"); +} + +static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct ieee80211_vif *csa_vif; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u32 apply_time; + int ret; + + mutex_lock(&mvm->mutex); + + mvmvif->csa_failed = false; + + IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n", + chsw->chandef.center_freq1); + + iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH); + + switch (vif->type) { + case NL80211_IFTYPE_AP: + csa_vif = + rcu_dereference_protected(mvm->csa_vif, + lockdep_is_held(&mvm->mutex)); + if (WARN_ONCE(csa_vif && csa_vif->csa_active, + "Another CSA is already in progress")) { + ret = -EBUSY; + goto out_unlock; + } + + rcu_assign_pointer(mvm->csa_vif, vif); + + if (WARN_ONCE(mvmvif->csa_countdown, + "Previous CSA countdown didn't complete")) { + ret = -EBUSY; + goto out_unlock; + } + + break; + case NL80211_IFTYPE_STATION: + /* Schedule the time event to a bit before beacon 1, + * to make sure we're in the new channel when the + * GO/AP arrives. + */ + apply_time = chsw->device_timestamp + + ((vif->bss_conf.beacon_int * (chsw->count - 1) - + IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024); + + if (chsw->block_tx) + iwl_mvm_csa_client_absent(mvm, vif); + + iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int, + apply_time); + if (mvmvif->bf_data.bf_enabled) { + ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); + if (ret) + goto out_unlock; + } + + break; + default: + break; + } + + mvmvif->ps_disabled = true; + + ret = iwl_mvm_power_update_ps(mvm); + if (ret) + goto out_unlock; + + /* we won't be on this channel any longer */ + iwl_mvm_teardown_tdls_peers(mvm); + +out_unlock: + mutex_unlock(&mvm->mutex); + + return ret; +} + +static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + mutex_lock(&mvm->mutex); + + if (mvmvif->csa_failed) { + mvmvif->csa_failed = false; + ret = -EIO; + goto out_unlock; + } + + if (vif->type == NL80211_IFTYPE_STATION) { + struct iwl_mvm_sta *mvmsta; + + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, + mvmvif->ap_sta_id); + + if (WARN_ON(!mvmsta)) { + ret = -EIO; + goto out_unlock; + } + + iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false); + + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + + ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); + if (ret) + goto out_unlock; + + iwl_mvm_stop_session_protection(mvm, vif); + } + + mvmvif->ps_disabled = false; + + ret = iwl_mvm_power_update_ps(mvm); + +out_unlock: + mutex_unlock(&mvm->mutex); + + return ret; +} + +static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u32 queues, bool drop) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif; + struct iwl_mvm_sta *mvmsta; + struct ieee80211_sta *sta; + int i; + u32 msk = 0; + + if (!vif || vif->type != NL80211_IFTYPE_STATION) + return; + + mutex_lock(&mvm->mutex); + mvmvif = iwl_mvm_vif_from_mac80211(vif); + + /* flush the AP-station and all TDLS peers */ + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(sta)) + continue; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + if (mvmsta->vif != vif) + continue; + + /* make sure only TDLS peers or the AP are flushed */ + WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls); + + msk |= mvmsta->tfd_queue_msk; + } + + if (drop) { + if (iwl_mvm_flush_tx_path(mvm, msk, 0)) + IWL_ERR(mvm, "flush request fail\n"); + mutex_unlock(&mvm->mutex); + } else { + mutex_unlock(&mvm->mutex); + + /* this can take a while, and we may need/want other operations + * to succeed while doing this, so do it without the mutex held + */ + iwl_trans_wait_tx_queue_empty(mvm->trans, msk); + } +} + +static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + memset(survey, 0, sizeof(*survey)); + + /* only support global statistics right now */ + if (idx != 0) + return -ENOENT; + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) + return -ENOENT; + + mutex_lock(&mvm->mutex); + + if (mvm->ucode_loaded) { + ret = iwl_mvm_request_statistics(mvm, false); + if (ret) + goto out; + } + + survey->filled = SURVEY_INFO_TIME | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_SCAN; + survey->time = mvm->accu_radio_stats.on_time_rf + + mvm->radio_stats.on_time_rf; + do_div(survey->time, USEC_PER_MSEC); + + survey->time_rx = mvm->accu_radio_stats.rx_time + + mvm->radio_stats.rx_time; + do_div(survey->time_rx, USEC_PER_MSEC); + + survey->time_tx = mvm->accu_radio_stats.tx_time + + mvm->radio_stats.tx_time; + do_div(survey->time_tx, USEC_PER_MSEC); + + survey->time_scan = mvm->accu_radio_stats.on_time_scan + + mvm->radio_stats.on_time_scan; + do_div(survey->time_scan, USEC_PER_MSEC); + + ret = 0; + out: + mutex_unlock(&mvm->mutex); + return ret; +} + +static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) + return; + + /* if beacon filtering isn't on mac80211 does it anyway */ + if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER)) + return; + + if (!vif->bss_conf.assoc) + return; + + mutex_lock(&mvm->mutex); + + if (mvmvif->ap_sta_id != mvmsta->sta_id) + goto unlock; + + if (iwl_mvm_request_statistics(mvm, false)) + goto unlock; + + sinfo->rx_beacon = mvmvif->beacon_stats.num_beacons + + mvmvif->beacon_stats.accu_num_beacons; + sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX); + if (mvmvif->beacon_stats.avg_signal) { + /* firmware only reports a value after RXing a few beacons */ + sinfo->rx_beacon_signal_avg = mvmvif->beacon_stats.avg_signal; + sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG); + } + unlock: + mutex_unlock(&mvm->mutex); +} + +static void iwl_mvm_event_mlme_callback(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + const struct ieee80211_event *event) +{ +#define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...) \ + do { \ + if ((_cnt) && --(_cnt)) \ + break; \ + iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\ + } while (0) + + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_mlme *trig_mlme; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME)) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME); + trig_mlme = (void *)trig->data; + if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + return; + + if (event->u.mlme.data == ASSOC_EVENT) { + if (event->u.mlme.status == MLME_DENIED) + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_assoc_denied, + "DENIED ASSOC: reason %d", + event->u.mlme.reason); + else if (event->u.mlme.status == MLME_TIMEOUT) + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_assoc_timeout, + "ASSOC TIMEOUT"); + } else if (event->u.mlme.data == AUTH_EVENT) { + if (event->u.mlme.status == MLME_DENIED) + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_auth_denied, + "DENIED AUTH: reason %d", + event->u.mlme.reason); + else if (event->u.mlme.status == MLME_TIMEOUT) + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_auth_timeout, + "AUTH TIMEOUT"); + } else if (event->u.mlme.data == DEAUTH_RX_EVENT) { + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_rx_deauth, + "DEAUTH RX %d", event->u.mlme.reason); + } else if (event->u.mlme.data == DEAUTH_TX_EVENT) { + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_tx_deauth, + "DEAUTH TX %d", event->u.mlme.reason); + } +#undef CHECK_MLME_TRIGGER +} + +static void iwl_mvm_event_bar_rx_callback(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + const struct ieee80211_event *event) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_ba *ba_trig; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); + ba_trig = (void *)trig->data; + if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + return; + + if (!(le16_to_cpu(ba_trig->rx_bar) & BIT(event->u.ba.tid))) + return; + + iwl_mvm_fw_dbg_collect_trig(mvm, trig, + "BAR received from %pM, tid %d, ssn %d", + event->u.ba.sta->addr, event->u.ba.tid, + event->u.ba.ssn); +} + +static void +iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + const struct ieee80211_event *event) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_ba *ba_trig; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); + ba_trig = (void *)trig->data; + if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + return; + + if (!(le16_to_cpu(ba_trig->frame_timeout) & BIT(event->u.ba.tid))) + return; + + iwl_mvm_fw_dbg_collect_trig(mvm, trig, + "Frame from %pM timed out, tid %d", + event->u.ba.sta->addr, event->u.ba.tid); +} + +static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct ieee80211_event *event) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + switch (event->type) { + case MLME_EVENT: + iwl_mvm_event_mlme_callback(mvm, vif, event); + break; + case BAR_RX_EVENT: + iwl_mvm_event_bar_rx_callback(mvm, vif, event); + break; + case BA_FRAME_TIMEOUT: + iwl_mvm_event_frame_timeout_callback(mvm, vif, event); + break; + default: + break; + } +} + +const struct ieee80211_ops iwl_mvm_hw_ops = { + .tx = iwl_mvm_mac_tx, + .ampdu_action = iwl_mvm_mac_ampdu_action, + .start = iwl_mvm_mac_start, + .reconfig_complete = iwl_mvm_mac_reconfig_complete, + .stop = iwl_mvm_mac_stop, + .add_interface = iwl_mvm_mac_add_interface, + .remove_interface = iwl_mvm_mac_remove_interface, + .config = iwl_mvm_mac_config, + .prepare_multicast = iwl_mvm_prepare_multicast, + .configure_filter = iwl_mvm_configure_filter, + .config_iface_filter = iwl_mvm_config_iface_filter, + .bss_info_changed = iwl_mvm_bss_info_changed, + .hw_scan = iwl_mvm_mac_hw_scan, + .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan, + .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove, + .sta_state = iwl_mvm_mac_sta_state, + .sta_notify = iwl_mvm_mac_sta_notify, + .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, + .release_buffered_frames = iwl_mvm_mac_release_buffered_frames, + .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, + .sta_rc_update = iwl_mvm_sta_rc_update, + .conf_tx = iwl_mvm_mac_conf_tx, + .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, + .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover, + .flush = iwl_mvm_mac_flush, + .sched_scan_start = iwl_mvm_mac_sched_scan_start, + .sched_scan_stop = iwl_mvm_mac_sched_scan_stop, + .set_key = iwl_mvm_mac_set_key, + .update_tkip_key = iwl_mvm_mac_update_tkip_key, + .remain_on_channel = iwl_mvm_roc, + .cancel_remain_on_channel = iwl_mvm_cancel_roc, + .add_chanctx = iwl_mvm_add_chanctx, + .remove_chanctx = iwl_mvm_remove_chanctx, + .change_chanctx = iwl_mvm_change_chanctx, + .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx, + .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx, + .switch_vif_chanctx = iwl_mvm_switch_vif_chanctx, + + .start_ap = iwl_mvm_start_ap_ibss, + .stop_ap = iwl_mvm_stop_ap_ibss, + .join_ibss = iwl_mvm_start_ap_ibss, + .leave_ibss = iwl_mvm_stop_ap_ibss, + + .set_tim = iwl_mvm_set_tim, + + .channel_switch = iwl_mvm_channel_switch, + .pre_channel_switch = iwl_mvm_pre_channel_switch, + .post_channel_switch = iwl_mvm_post_channel_switch, + + .tdls_channel_switch = iwl_mvm_tdls_channel_switch, + .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch, + .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch, + + .event_callback = iwl_mvm_mac_event_callback, + + CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) + +#ifdef CONFIG_PM_SLEEP + /* look at d3.c */ + .suspend = iwl_mvm_suspend, + .resume = iwl_mvm_resume, + .set_wakeup = iwl_mvm_set_wakeup, + .set_rekey_data = iwl_mvm_set_rekey_data, +#if IS_ENABLED(CONFIG_IPV6) + .ipv6_addr_change = iwl_mvm_ipv6_addr_change, +#endif + .set_default_unicast_key = iwl_mvm_set_default_unicast_key, +#endif + .get_survey = iwl_mvm_mac_get_survey, + .sta_statistics = iwl_mvm_mac_sta_statistics, +}; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h new file mode 100644 index 000000000000..4bde2d027dcd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -0,0 +1,1535 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __IWL_MVM_H__ +#define __IWL_MVM_H__ + +#include +#include +#include +#include + +#include "iwl-op-mode.h" +#include "iwl-trans.h" +#include "iwl-notif-wait.h" +#include "iwl-eeprom-parse.h" +#include "iwl-fw-file.h" +#include "iwl-config.h" +#include "sta.h" +#include "fw-api.h" +#include "constants.h" +#include "tof.h" + +#define IWL_MVM_MAX_ADDRESSES 5 +/* RSSI offset for WkP */ +#define IWL_RSSI_OFFSET 50 +#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8 +/* A TimeUnit is 1024 microsecond */ +#define MSEC_TO_TU(_msec) (_msec*1000/1024) + +/* For GO, this value represents the number of TUs before CSA "beacon + * 0" TBTT when the CSA time-event needs to be scheduled to start. It + * must be big enough to ensure that we switch in time. + */ +#define IWL_MVM_CHANNEL_SWITCH_TIME_GO 40 + +/* For client, this value represents the number of TUs before CSA + * "beacon 1" TBTT, instead. This is because we don't know when the + * GO/AP will be in the new channel, so we switch early enough. + */ +#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT 10 + +/* + * This value (in TUs) is used to fine tune the CSA NoA end time which should + * be just before "beacon 0" TBTT. + */ +#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4 + +/* + * Number of beacons to transmit on a new channel until we unblock tx to + * the stations, even if we didn't identify them on a new channel + */ +#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3 + +extern const struct ieee80211_ops iwl_mvm_hw_ops; + +/** + * struct iwl_mvm_mod_params - module parameters for iwlmvm + * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted. + * We will register to mac80211 to have testmode working. The NIC must not + * be up'ed after the INIT fw asserted. This is useful to be able to use + * proprietary tools over testmode to debug the INIT fw. + * @tfd_q_hang_detect: enabled the detection of hung transmit queues + * @power_scheme: one of enum iwl_power_scheme + */ +struct iwl_mvm_mod_params { + bool init_dbg; + bool tfd_q_hang_detect; + int power_scheme; +}; +extern struct iwl_mvm_mod_params iwlmvm_mod_params; + +/** + * struct iwl_mvm_dump_ptrs - set of pointers needed for the fw-error-dump + * + * @op_mode_ptr: pointer to the buffer coming from the mvm op_mode + * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the + * transport's data. + * @trans_len: length of the valid data in trans_ptr + * @op_mode_len: length of the valid data in op_mode_ptr + */ +struct iwl_mvm_dump_ptrs { + struct iwl_trans_dump_data *trans_ptr; + void *op_mode_ptr; + u32 op_mode_len; +}; + +/** + * struct iwl_mvm_dump_desc - describes the dump + * @len: length of trig_desc->data + * @trig_desc: the description of the dump + */ +struct iwl_mvm_dump_desc { + size_t len; + /* must be last */ + struct iwl_fw_error_dump_trigger_desc trig_desc; +}; + +extern struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert; + +struct iwl_mvm_phy_ctxt { + u16 id; + u16 color; + u32 ref; + + /* + * TODO: This should probably be removed. Currently here only for rate + * scaling algorithm + */ + struct ieee80211_channel *channel; +}; + +struct iwl_mvm_time_event_data { + struct ieee80211_vif *vif; + struct list_head list; + unsigned long end_jiffies; + u32 duration; + bool running; + u32 uid; + + /* + * The access to the 'id' field must be done when the + * mvm->time_event_lock is held, as it value is used to indicate + * if the te is in the time event list or not (when id == TE_MAX) + */ + u32 id; +}; + + /* Power management */ + +/** + * enum iwl_power_scheme + * @IWL_POWER_LEVEL_CAM - Continuously Active Mode + * @IWL_POWER_LEVEL_BPS - Balanced Power Save (default) + * @IWL_POWER_LEVEL_LP - Low Power + */ +enum iwl_power_scheme { + IWL_POWER_SCHEME_CAM = 1, + IWL_POWER_SCHEME_BPS, + IWL_POWER_SCHEME_LP +}; + +#define IWL_CONN_MAX_LISTEN_INTERVAL 10 +#define IWL_UAPSD_MAX_SP IEEE80211_WMM_IE_STA_QOSINFO_SP_2 + +#ifdef CONFIG_IWLWIFI_DEBUGFS +enum iwl_dbgfs_pm_mask { + MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0), + MVM_DEBUGFS_PM_SKIP_OVER_DTIM = BIT(1), + MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2), + MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3), + MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4), + MVM_DEBUGFS_PM_LPRX_ENA = BIT(6), + MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7), + MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8), + MVM_DEBUGFS_PM_UAPSD_MISBEHAVING = BIT(9), + MVM_DEBUGFS_PM_USE_PS_POLL = BIT(10), +}; + +struct iwl_dbgfs_pm { + u16 keep_alive_seconds; + u32 rx_data_timeout; + u32 tx_data_timeout; + bool skip_over_dtim; + u8 skip_dtim_periods; + bool lprx_ena; + u32 lprx_rssi_threshold; + bool snooze_ena; + bool uapsd_misbehaving; + bool use_ps_poll; + int mask; +}; + +/* beacon filtering */ + +enum iwl_dbgfs_bf_mask { + MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0), + MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1), + MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2), + MVM_DEBUGFS_BF_TEMP_THRESHOLD = BIT(3), + MVM_DEBUGFS_BF_TEMP_FAST_FILTER = BIT(4), + MVM_DEBUGFS_BF_TEMP_SLOW_FILTER = BIT(5), + MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(6), + MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(7), + MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(8), + MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(9), + MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(10), +}; + +struct iwl_dbgfs_bf { + u32 bf_energy_delta; + u32 bf_roaming_energy_delta; + u32 bf_roaming_state; + u32 bf_temp_threshold; + u32 bf_temp_fast_filter; + u32 bf_temp_slow_filter; + u32 bf_enable_beacon_filter; + u32 bf_debug_flag; + u32 bf_escape_timer; + u32 ba_escape_timer; + u32 ba_enable_beacon_abort; + int mask; +}; +#endif + +enum iwl_mvm_smps_type_request { + IWL_MVM_SMPS_REQ_BT_COEX, + IWL_MVM_SMPS_REQ_TT, + IWL_MVM_SMPS_REQ_PROT, + NUM_IWL_MVM_SMPS_REQ, +}; + +enum iwl_mvm_ref_type { + IWL_MVM_REF_UCODE_DOWN, + IWL_MVM_REF_SCAN, + IWL_MVM_REF_ROC, + IWL_MVM_REF_ROC_AUX, + IWL_MVM_REF_P2P_CLIENT, + IWL_MVM_REF_AP_IBSS, + IWL_MVM_REF_USER, + IWL_MVM_REF_TX, + IWL_MVM_REF_TX_AGG, + IWL_MVM_REF_ADD_IF, + IWL_MVM_REF_START_AP, + IWL_MVM_REF_BSS_CHANGED, + IWL_MVM_REF_PREPARE_TX, + IWL_MVM_REF_PROTECT_TDLS, + IWL_MVM_REF_CHECK_CTKILL, + IWL_MVM_REF_PRPH_READ, + IWL_MVM_REF_PRPH_WRITE, + IWL_MVM_REF_NMI, + IWL_MVM_REF_TM_CMD, + IWL_MVM_REF_EXIT_WORK, + IWL_MVM_REF_PROTECT_CSA, + IWL_MVM_REF_FW_DBG_COLLECT, + + /* update debugfs.c when changing this */ + + IWL_MVM_REF_COUNT, +}; + +enum iwl_bt_force_ant_mode { + BT_FORCE_ANT_DIS = 0, + BT_FORCE_ANT_AUTO, + BT_FORCE_ANT_BT, + BT_FORCE_ANT_WIFI, + + BT_FORCE_ANT_MAX, +}; + +/** +* struct iwl_mvm_vif_bf_data - beacon filtering related data +* @bf_enabled: indicates if beacon filtering is enabled +* @ba_enabled: indicated if beacon abort is enabled +* @ave_beacon_signal: average beacon signal +* @last_cqm_event: rssi of the last cqm event +* @bt_coex_min_thold: minimum threshold for BT coex +* @bt_coex_max_thold: maximum threshold for BT coex +* @last_bt_coex_event: rssi of the last BT coex event +*/ +struct iwl_mvm_vif_bf_data { + bool bf_enabled; + bool ba_enabled; + int ave_beacon_signal; + int last_cqm_event; + int bt_coex_min_thold; + int bt_coex_max_thold; + int last_bt_coex_event; +}; + +/** + * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context + * @id: between 0 and 3 + * @color: to solve races upon MAC addition and removal + * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA + * @bssid: BSSID for this (client) interface + * @associated: indicates that we're currently associated, used only for + * managing the firmware state in iwl_mvm_bss_info_changed_station() + * @ap_assoc_sta_count: count of stations associated to us - valid only + * if VIF type is AP + * @uploaded: indicates the MAC context has been added to the device + * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface + * should get quota etc. + * @pm_enabled - Indicate if MAC power management is allowed + * @monitor_active: indicates that monitor context is configured, and that the + * interface should get quota etc. + * @low_latency: indicates that this interface is in low-latency mode + * (VMACLowLatencyMode) + * @ps_disabled: indicates that this interface requires PS to be disabled + * @queue_params: QoS params for this MAC + * @bcast_sta: station used for broadcast packets. Used by the following + * vifs: P2P_DEVICE, GO and AP. + * @beacon_skb: the skb used to hold the AP/GO beacon template + * @smps_requests: the SMPS requests of different parts of the driver, + * combined on update to yield the overall request to mac80211. + * @beacon_stats: beacon statistics, containing the # of received beacons, + * # of received beacons accumulated over FW restart, and the current + * average signal of beacons retrieved from the firmware + * @csa_failed: CSA failed to schedule time event, report an error later + * @features: hw features active for this vif + */ +struct iwl_mvm_vif { + struct iwl_mvm *mvm; + u16 id; + u16 color; + u8 ap_sta_id; + + u8 bssid[ETH_ALEN]; + bool associated; + u8 ap_assoc_sta_count; + + bool uploaded; + bool ap_ibss_active; + bool pm_enabled; + bool monitor_active; + bool low_latency; + bool ps_disabled; + struct iwl_mvm_vif_bf_data bf_data; + + struct { + u32 num_beacons, accu_num_beacons; + u8 avg_signal; + } beacon_stats; + + u32 ap_beacon_time; + + enum iwl_tsf_id tsf_id; + + /* + * QoS data from mac80211, need to store this here + * as mac80211 has a separate callback but we need + * to have the data for the MAC context + */ + struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; + struct iwl_mvm_time_event_data time_event_data; + struct iwl_mvm_time_event_data hs_time_event_data; + + struct iwl_mvm_int_sta bcast_sta; + + /* + * Assigned while mac80211 has the interface in a channel context, + * or, for P2P Device, while it exists. + */ + struct iwl_mvm_phy_ctxt *phy_ctxt; + +#ifdef CONFIG_PM_SLEEP + /* WoWLAN GTK rekey data */ + struct { + u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; + __le64 replay_ctr; + bool valid; + } rekey_data; + + int tx_key_idx; + + bool seqno_valid; + u16 seqno; +#endif + +#if IS_ENABLED(CONFIG_IPV6) + /* IPv6 addresses for WoWLAN */ + struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; + int num_target_ipv6_addrs; +#endif + +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct dentry *dbgfs_dir; + struct dentry *dbgfs_slink; + struct iwl_dbgfs_pm dbgfs_pm; + struct iwl_dbgfs_bf dbgfs_bf; + struct iwl_mac_power_cmd mac_pwr_cmd; +#endif + + enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; + + /* FW identified misbehaving AP */ + u8 uapsd_misbehaving_bssid[ETH_ALEN]; + + /* Indicates that CSA countdown may be started */ + bool csa_countdown; + bool csa_failed; + + /* TCP Checksum Offload */ + netdev_features_t features; +}; + +static inline struct iwl_mvm_vif * +iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif) +{ + return (void *)vif->drv_priv; +} + +extern const u8 tid_to_mac80211_ac[]; + +#define IWL_MVM_SCAN_STOPPING_SHIFT 8 + +enum iwl_scan_status { + IWL_MVM_SCAN_REGULAR = BIT(0), + IWL_MVM_SCAN_SCHED = BIT(1), + IWL_MVM_SCAN_NETDETECT = BIT(2), + + IWL_MVM_SCAN_STOPPING_REGULAR = BIT(8), + IWL_MVM_SCAN_STOPPING_SCHED = BIT(9), + IWL_MVM_SCAN_STOPPING_NETDETECT = BIT(10), + + IWL_MVM_SCAN_REGULAR_MASK = IWL_MVM_SCAN_REGULAR | + IWL_MVM_SCAN_STOPPING_REGULAR, + IWL_MVM_SCAN_SCHED_MASK = IWL_MVM_SCAN_SCHED | + IWL_MVM_SCAN_STOPPING_SCHED, + IWL_MVM_SCAN_NETDETECT_MASK = IWL_MVM_SCAN_NETDETECT | + IWL_MVM_SCAN_STOPPING_NETDETECT, + + IWL_MVM_SCAN_STOPPING_MASK = 0xff << IWL_MVM_SCAN_STOPPING_SHIFT, + IWL_MVM_SCAN_MASK = 0xff, +}; + +/** + * struct iwl_nvm_section - describes an NVM section in memory. + * + * This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD, + * and saved for later use by the driver. Not all NVM sections are saved + * this way, only the needed ones. + */ +struct iwl_nvm_section { + u16 length; + const u8 *data; +}; + +/** + * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure + * @ct_kill_exit: worker to exit thermal kill + * @dynamic_smps: Is thermal throttling enabled dynamic_smps? + * @tx_backoff: The current thremal throttling tx backoff in uSec. + * @min_backoff: The minimal tx backoff due to power restrictions + * @params: Parameters to configure the thermal throttling algorithm. + * @throttle: Is thermal throttling is active? + */ +struct iwl_mvm_tt_mgmt { + struct delayed_work ct_kill_exit; + bool dynamic_smps; + u32 tx_backoff; + u32 min_backoff; + struct iwl_tt_params params; + bool throttle; +}; + +#define IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES 8 + +struct iwl_mvm_frame_stats { + u32 legacy_frames; + u32 ht_frames; + u32 vht_frames; + u32 bw_20_frames; + u32 bw_40_frames; + u32 bw_80_frames; + u32 bw_160_frames; + u32 sgi_frames; + u32 ngi_frames; + u32 siso_frames; + u32 mimo2_frames; + u32 agg_frames; + u32 ampdu_count; + u32 success_frames; + u32 fail_frames; + u32 last_rates[IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES]; + int last_frame_idx; +}; + +enum { + D0I3_DEFER_WAKEUP, + D0I3_PENDING_WAKEUP, +}; + +#define IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE 0xff +#define IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -100 +#define IWL_MVM_DEBUG_SET_TEMPERATURE_MAX 200 + +enum iwl_mvm_tdls_cs_state { + IWL_MVM_TDLS_SW_IDLE = 0, + IWL_MVM_TDLS_SW_REQ_SENT, + IWL_MVM_TDLS_SW_RESP_RCVD, + IWL_MVM_TDLS_SW_REQ_RCVD, + IWL_MVM_TDLS_SW_ACTIVE, +}; + +struct iwl_mvm_shared_mem_cfg { + u32 shared_mem_addr; + u32 shared_mem_size; + u32 sample_buff_addr; + u32 sample_buff_size; + u32 txfifo_addr; + u32 txfifo_size[TX_FIFO_MAX_NUM]; + u32 rxfifo_size[RX_FIFO_MAX_NUM]; + u32 page_buff_addr; + u32 page_buff_size; +}; + +struct iwl_mvm { + /* for logger access */ + struct device *dev; + + struct iwl_trans *trans; + const struct iwl_fw *fw; + const struct iwl_cfg *cfg; + struct iwl_phy_db *phy_db; + struct ieee80211_hw *hw; + + /* for protecting access to iwl_mvm */ + struct mutex mutex; + struct list_head async_handlers_list; + spinlock_t async_handlers_lock; + struct work_struct async_handlers_wk; + + struct work_struct roc_done_wk; + + unsigned long status; + + /* + * for beacon filtering - + * currently only one interface can be supported + */ + struct iwl_mvm_vif *bf_allowed_vif; + + enum iwl_ucode_type cur_ucode; + bool ucode_loaded; + bool calibrating; + u32 error_event_table; + u32 log_event_table; + u32 umac_error_event_table; + bool support_umac_log; + struct iwl_sf_region sf_space; + + u32 ampdu_ref; + + struct iwl_notif_wait_data notif_wait; + + struct mvm_statistics_rx rx_stats; + + struct { + u64 rx_time; + u64 tx_time; + u64 on_time_rf; + u64 on_time_scan; + } radio_stats, accu_radio_stats; + + struct { + /* Map to HW queue */ + u32 hw_queue_to_mac80211; + u8 hw_queue_refcount; + bool setup_reserved; + u16 tid_bitmap; /* Bitmap of the TIDs mapped to this queue */ + } queue_info[IWL_MAX_HW_QUEUES]; + spinlock_t queue_info_lock; /* For syncing queue mgmt operations */ + atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES]; + + const char *nvm_file_name; + struct iwl_nvm_data *nvm_data; + /* NVM sections */ + struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS]; + + /* Paging section */ + struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS]; + u16 num_of_paging_blk; + u16 num_of_pages_in_last_blk; + + /* EEPROM MAC addresses */ + struct mac_address addresses[IWL_MVM_MAX_ADDRESSES]; + + /* data related to data path */ + struct iwl_rx_phy_info last_phy_info; + struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT]; + struct work_struct sta_drained_wk; + unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)]; + atomic_t pending_frames[IWL_MVM_STATION_COUNT]; + u32 tfd_drained[IWL_MVM_STATION_COUNT]; + u8 rx_ba_sessions; + + /* configured by mac80211 */ + u32 rts_threshold; + + /* Scan status, cmd (pre-allocated) and auxiliary station */ + unsigned int scan_status; + void *scan_cmd; + struct iwl_mcast_filter_cmd *mcast_filter_cmd; + + /* max number of simultaneous scans the FW supports */ + unsigned int max_scans; + + /* UMAC scan tracking */ + u32 scan_uid_status[IWL_MVM_MAX_UMAC_SCANS]; + + /* rx chain antennas set through debugfs for the scan command */ + u8 scan_rx_ant; + +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING + /* broadcast filters to configure for each associated station */ + const struct iwl_fw_bcast_filter *bcast_filters; +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct { + bool override; + struct iwl_bcast_filter_cmd cmd; + } dbgfs_bcast_filtering; +#endif +#endif + + /* Internal station */ + struct iwl_mvm_int_sta aux_sta; + + bool last_ebs_successful; + + u8 scan_last_antenna_idx; /* to toggle TX between antennas */ + u8 mgmt_last_antenna_idx; + + /* last smart fifo state that was successfully sent to firmware */ + enum iwl_sf_state sf_state; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct dentry *debugfs_dir; + u32 dbgfs_sram_offset, dbgfs_sram_len; + u32 dbgfs_prph_reg_addr; + bool disable_power_off; + bool disable_power_off_d3; + + bool scan_iter_notif_enabled; + + struct debugfs_blob_wrapper nvm_hw_blob; + struct debugfs_blob_wrapper nvm_sw_blob; + struct debugfs_blob_wrapper nvm_calib_blob; + struct debugfs_blob_wrapper nvm_prod_blob; + struct debugfs_blob_wrapper nvm_phy_sku_blob; + + struct iwl_mvm_frame_stats drv_rx_stats; + spinlock_t drv_stats_lock; + u16 dbgfs_rx_phyinfo; +#endif + + struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX]; + + struct list_head time_event_list; + spinlock_t time_event_lock; + + /* + * A bitmap indicating the index of the key in use. The firmware + * can hold 16 keys at most. Reflect this fact. + */ + unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)]; + u8 fw_key_deleted[STA_KEY_MAX_NUM]; + + /* references taken by the driver and spinlock protecting them */ + spinlock_t refs_lock; + u8 refs[IWL_MVM_REF_COUNT]; + + u8 vif_count; + + /* -1 for always, 0 for never, >0 for that many times */ + s8 restart_fw; + u8 fw_dbg_conf; + struct delayed_work fw_dump_wk; + struct iwl_mvm_dump_desc *fw_dump_desc; + struct iwl_fw_dbg_trigger_tlv *fw_dump_trig; + +#ifdef CONFIG_IWLWIFI_LEDS + struct led_classdev led; +#endif + + struct ieee80211_vif *p2p_device_vif; + +#ifdef CONFIG_PM_SLEEP + struct wiphy_wowlan_support wowlan; + int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; + + /* sched scan settings for net detect */ + struct cfg80211_sched_scan_request *nd_config; + struct ieee80211_scan_ies nd_ies; + struct cfg80211_match_set *nd_match_sets; + int n_nd_match_sets; + struct ieee80211_channel **nd_channels; + int n_nd_channels; + bool net_detect; +#ifdef CONFIG_IWLWIFI_DEBUGFS + bool d3_wake_sysassert; + bool d3_test_active; + bool store_d3_resume_sram; + void *d3_resume_sram; + u32 d3_test_pme_ptr; + struct ieee80211_vif *keep_vif; + u32 last_netdetect_scans; /* no. of scans in the last net-detect wake */ +#endif +#endif + + /* d0i3 */ + u8 d0i3_ap_sta_id; + bool d0i3_offloading; + struct work_struct d0i3_exit_work; + struct sk_buff_head d0i3_tx; + /* protect d0i3_suspend_flags */ + struct mutex d0i3_suspend_mutex; + unsigned long d0i3_suspend_flags; + /* sync d0i3_tx queue and IWL_MVM_STATUS_IN_D0I3 status flag */ + spinlock_t d0i3_tx_lock; + wait_queue_head_t d0i3_exit_waitq; + + /* BT-Coex */ + u8 bt_ack_kill_msk[NUM_PHY_CTX]; + u8 bt_cts_kill_msk[NUM_PHY_CTX]; + + struct iwl_bt_coex_profile_notif_old last_bt_notif_old; + struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old; + struct iwl_bt_coex_profile_notif last_bt_notif; + struct iwl_bt_coex_ci_cmd last_bt_ci_cmd; + + u32 last_ant_isol; + u8 last_corun_lut; + u8 bt_tx_prio; + enum iwl_bt_force_ant_mode bt_force_ant_mode; + + /* Aux ROC */ + struct list_head aux_roc_te_list; + + /* Thermal Throttling and CTkill */ + struct iwl_mvm_tt_mgmt thermal_throttle; + s32 temperature; /* Celsius */ + /* + * Debug option to set the NIC temperature. This option makes the + * driver think this is the actual NIC temperature, and ignore the + * real temperature that is received from the fw + */ + bool temperature_test; /* Debug test temperature is enabled */ + + struct iwl_time_quota_cmd last_quota_cmd; + +#ifdef CONFIG_NL80211_TESTMODE + u32 noa_duration; + struct ieee80211_vif *noa_vif; +#endif + + /* Tx queues */ + u8 aux_queue; + u8 first_agg_queue; + u8 last_agg_queue; + + /* Indicate if device power save is allowed */ + u8 ps_disabled; /* u8 instead of bool to ease debugfs_create_* usage */ + + struct ieee80211_vif __rcu *csa_vif; + struct ieee80211_vif __rcu *csa_tx_blocked_vif; + u8 csa_tx_block_bcn_timeout; + + /* system time of last beacon (for AP/GO interface) */ + u32 ap_last_beacon_gp2; + + bool lar_regdom_set; + enum iwl_mcc_source mcc_src; + + u8 low_latency_agg_frame_limit; + + /* TDLS channel switch data */ + struct { + struct delayed_work dwork; + enum iwl_mvm_tdls_cs_state state; + + /* + * Current cs sta - might be different from periodic cs peer + * station. Value is meaningless when the cs-state is idle. + */ + u8 cur_sta_id; + + /* TDLS periodic channel-switch peer */ + struct { + u8 sta_id; + u8 op_class; + bool initiator; /* are we the link initiator */ + struct cfg80211_chan_def chandef; + struct sk_buff *skb; /* ch sw template */ + u32 ch_sw_tm_ie; + + /* timestamp of last ch-sw request sent (GP2 time) */ + u32 sent_timestamp; + } peer; + } tdls_cs; + + struct iwl_mvm_shared_mem_cfg shared_mem_cfg; + + u32 ciphers[6]; + struct iwl_mvm_tof_data tof_data; +}; + +/* Extract MVM priv from op_mode and _hw */ +#define IWL_OP_MODE_GET_MVM(_iwl_op_mode) \ + ((struct iwl_mvm *)(_iwl_op_mode)->op_mode_specific) + +#define IWL_MAC80211_GET_MVM(_hw) \ + IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv)) + +enum iwl_mvm_status { + IWL_MVM_STATUS_HW_RFKILL, + IWL_MVM_STATUS_HW_CTKILL, + IWL_MVM_STATUS_ROC_RUNNING, + IWL_MVM_STATUS_IN_HW_RESTART, + IWL_MVM_STATUS_IN_D0I3, + IWL_MVM_STATUS_ROC_AUX_RUNNING, + IWL_MVM_STATUS_D3_RECONFIG, + IWL_MVM_STATUS_DUMPING_FW_LOG, +}; + +static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) +{ + return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status) || + test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); +} + +static inline bool iwl_mvm_is_radio_hw_killed(struct iwl_mvm *mvm) +{ + return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); +} + +/* Must be called with rcu_read_lock() held and it can only be + * released when mvmsta is not needed anymore. + */ +static inline struct iwl_mvm_sta * +iwl_mvm_sta_from_staid_rcu(struct iwl_mvm *mvm, u8 sta_id) +{ + struct ieee80211_sta *sta; + + if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id)) + return NULL; + + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + + /* This can happen if the station has been removed right now */ + if (IS_ERR_OR_NULL(sta)) + return NULL; + + return iwl_mvm_sta_from_mac80211(sta); +} + +static inline struct iwl_mvm_sta * +iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id) +{ + struct ieee80211_sta *sta; + + if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id)) + return NULL; + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + + /* This can happen if the station has been removed right now */ + if (IS_ERR_OR_NULL(sta)) + return NULL; + + return iwl_mvm_sta_from_mac80211(sta); +} + +static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) +{ + return mvm->trans->cfg->d0i3 && + mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF && + !iwlwifi_mod_params.d0i3_disable && + fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); +} + +static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm) +{ + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_DQA_SUPPORT); +} + +static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm) +{ + bool nvm_lar = mvm->nvm_data->lar_enabled; + bool tlv_lar = fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_LAR_SUPPORT); + + if (iwlwifi_mod_params.lar_disable) + return false; + + /* + * Enable LAR only if it is supported by the FW (TLV) && + * enabled in the NVM + */ + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) + return nvm_lar && tlv_lar; + else + return tlv_lar; +} + +static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm) +{ + return fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_WIFI_MCC_UPDATE) || + fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC); +} + +static inline bool iwl_mvm_bt_is_plcr_supported(struct iwl_mvm *mvm) +{ + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BT_COEX_PLCR) && + IWL_MVM_BT_COEX_CORUNNING; +} + +static inline bool iwl_mvm_bt_is_rrc_supported(struct iwl_mvm *mvm) +{ + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BT_COEX_RRC) && + IWL_MVM_BT_COEX_RRC; +} + +static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm) +{ + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CSUM_SUPPORT); +} + +static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm) +{ + /* firmware flag isn't defined yet */ + return false; +} + +extern const u8 iwl_mvm_ac_to_tx_fifo[]; + +struct iwl_rate_info { + u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ + u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ + u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ + u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */ + u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ +}; + +void __iwl_mvm_mac_stop(struct iwl_mvm *mvm); +int __iwl_mvm_mac_start(struct iwl_mvm *mvm); + +/****************** + * MVM Methods + ******************/ +/* uCode */ +int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm); + +/* Utils */ +int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, + enum ieee80211_band band); +void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, + enum ieee80211_band band, + struct ieee80211_tx_rate *r); +u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx); +void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm); +u8 first_antenna(u8 mask); +u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); + +/* Tx / Host Commands */ +int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm, + struct iwl_host_cmd *cmd); +int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, + u32 flags, u16 len, const void *data); +int __must_check iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, + struct iwl_host_cmd *cmd, + u32 *status); +int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, + u16 len, const void *data, + u32 *status); +int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, + struct ieee80211_sta *sta); +int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb); +void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, + struct iwl_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, u8 sta_id); +void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, __le16 fc); +#ifdef CONFIG_IWLWIFI_DEBUG +const char *iwl_mvm_get_tx_fail_reason(u32 status); +#else +static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; } +#endif +int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags); +void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm); + +static inline void iwl_mvm_set_tx_cmd_ccmp(struct ieee80211_tx_info *info, + struct iwl_tx_cmd *tx_cmd) +{ + struct ieee80211_key_conf *keyconf = info->control.hw_key; + + tx_cmd->sec_ctl = TX_CMD_SEC_CCM; + memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); + if (info->flags & IEEE80211_TX_CTL_AMPDU) + tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_CCMP_AGG); +} + +static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm) +{ + flush_work(&mvm->async_handlers_wk); +} + +/* Statistics */ +void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, + struct iwl_rx_packet *pkt); +void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear); +void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm); + +/* NVM */ +int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic); +int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); + +static inline u8 iwl_mvm_get_valid_tx_ant(struct iwl_mvm *mvm) +{ + return mvm->nvm_data && mvm->nvm_data->valid_tx_ant ? + mvm->fw->valid_tx_ant & mvm->nvm_data->valid_tx_ant : + mvm->fw->valid_tx_ant; +} + +static inline u8 iwl_mvm_get_valid_rx_ant(struct iwl_mvm *mvm) +{ + return mvm->nvm_data && mvm->nvm_data->valid_rx_ant ? + mvm->fw->valid_rx_ant & mvm->nvm_data->valid_rx_ant : + mvm->fw->valid_rx_ant; +} + +static inline u32 iwl_mvm_get_phy_config(struct iwl_mvm *mvm) +{ + u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN | + FW_PHY_CFG_RX_CHAIN); + u32 valid_rx_ant = iwl_mvm_get_valid_rx_ant(mvm); + u32 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); + + phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS | + valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS; + + return mvm->fw->phy_config & phy_config; +} + +int iwl_mvm_up(struct iwl_mvm *mvm); +int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm); + +int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm); +bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, + struct iwl_bcast_filter_cmd *cmd); + +/* + * FW notifications / CMD responses handlers + * Convention: iwl_mvm_rx_ + */ +void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_shared_mem_cfg_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); + +/* MVM PHY */ +int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, + struct cfg80211_chan_def *chandef, + u8 chains_static, u8 chains_dynamic); +int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, + struct cfg80211_chan_def *chandef, + u8 chains_static, u8 chains_dynamic); +void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, + struct iwl_mvm_phy_ctxt *ctxt); +void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, + struct iwl_mvm_phy_ctxt *ctxt); +int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm); +u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef); +u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef); + +/* MAC (virtual interface) programming */ +int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool force_assoc_off, const u8 *bssid_override); +int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif); +int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, + struct ieee80211_vif *exclude_vif); +/* Bindings */ +int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); + +/* Quota management */ +int iwl_mvm_update_quotas(struct iwl_mvm *mvm, bool force_upload, + struct ieee80211_vif *disabled_vif); + +/* Scanning */ +int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req, + struct ieee80211_scan_ies *ies); +int iwl_mvm_scan_size(struct iwl_mvm *mvm); +int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify); +int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm); +void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm); + +/* Scheduled scan */ +void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies, + int type); +void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); + +/* UMAC scan */ +int iwl_mvm_config_scan(struct iwl_mvm *mvm); +void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); + +/* MVM debugfs */ +#ifdef CONFIG_IWLWIFI_DEBUGFS +int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir); +void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +#else +static inline int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, + struct dentry *dbgfs_dir) +{ + return 0; +} +static inline void +iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ +} +static inline void +iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ +} +#endif /* CONFIG_IWLWIFI_DEBUGFS */ + +/* rate scaling */ +int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init); +void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg); +int rs_pretty_print_rate(char *buf, const u32 rate); +void rs_update_last_rssi(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct ieee80211_rx_status *rx_status); + +/* power management */ +int iwl_mvm_power_update_device(struct iwl_mvm *mvm); +int iwl_mvm_power_update_mac(struct iwl_mvm *mvm); +int iwl_mvm_power_update_ps(struct iwl_mvm *mvm); +int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + char *buf, int bufsz); + +void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); + +#ifdef CONFIG_IWLWIFI_LEDS +int iwl_mvm_leds_init(struct iwl_mvm *mvm); +void iwl_mvm_leds_exit(struct iwl_mvm *mvm); +#else +static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm) +{ + return 0; +} +static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm) +{ +} +#endif + +/* D3 (WoWLAN, NetDetect) */ +int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); +int iwl_mvm_resume(struct ieee80211_hw *hw); +void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled); +void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data); +void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct inet6_dev *idev); +void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int idx); +extern const struct file_operations iwl_dbgfs_d3_test_ops; +#ifdef CONFIG_PM_SLEEP +void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +#else +static inline void +iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ +} +#endif +void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, + struct iwl_wowlan_config_cmd *cmd); +int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool disable_offloading, + u32 cmd_flags); + +/* D0i3 */ +void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); +void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); +int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); +bool iwl_mvm_ref_taken(struct iwl_mvm *mvm); +void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq); +int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode); +int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode); +int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm); + +/* BT Coex */ +int iwl_send_bt_init_conf(struct iwl_mvm *mvm); +void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum ieee80211_rssi_event_data); +void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm); +u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, + struct ieee80211_sta *sta); +bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, + struct ieee80211_sta *sta); +bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant); +bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm); +bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, + enum ieee80211_band band); +u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, + struct ieee80211_tx_info *info, u8 ac); + +bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm); +void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm); +int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm); +void iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum ieee80211_rssi_event_data); +u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm, + struct ieee80211_sta *sta); +bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm, + struct ieee80211_sta *sta); +bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm, + enum ieee80211_band band); +void iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); + +/* beacon filtering */ +#ifdef CONFIG_IWLWIFI_DEBUGFS +void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd); +#else +static inline void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd) +{} +#endif +int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable, u32 flags); +int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 flags); +int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 flags); +/* SMPS */ +void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum iwl_mvm_smps_type_request req_type, + enum ieee80211_smps_mode smps_request); +bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm); + +/* Low latency */ +int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool value); +/* get SystemLowLatencyMode - only needed for beacon threshold? */ +bool iwl_mvm_low_latency(struct iwl_mvm *mvm); +/* get VMACLowLatencyMode */ +static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif) +{ + /* + * should this consider associated/active/... state? + * + * Normally low-latency should only be active on interfaces + * that are active, but at least with debugfs it can also be + * enabled on interfaces that aren't active. However, when + * interface aren't active then they aren't added into the + * binding, so this has no real impact. For now, just return + * the current desired low-latency state. + */ + + return mvmvif->low_latency; +} + +/* hw scheduler queue config */ +void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, + u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int wdg_timeout); +/* + * Disable a TXQ. + * Note that in non-DQA mode the %mac80211_queue and %tid params are ignored. + */ +void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, + u8 tid, u8 flags); +int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq); + +static inline +void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, + u8 fifo, u16 ssn, unsigned int wdg_timeout) +{ + struct iwl_trans_txq_scd_cfg cfg = { + .fifo = fifo, + .tid = IWL_MAX_TID_COUNT, + .aggregate = false, + .frame_limit = IWL_FRAME_LIMIT, + }; + + iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout); +} + +static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue, + int mac80211_queue, int fifo, + int sta_id, int tid, int frame_limit, + u16 ssn, unsigned int wdg_timeout) +{ + struct iwl_trans_txq_scd_cfg cfg = { + .fifo = fifo, + .sta_id = sta_id, + .tid = tid, + .frame_limit = frame_limit, + .aggregate = true, + }; + + iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout); +} + +/* Thermal management and CT-kill */ +void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff); +void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp); +void iwl_mvm_temp_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_tt_handler(struct iwl_mvm *mvm); +void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff); +void iwl_mvm_tt_exit(struct iwl_mvm *mvm); +void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); +int iwl_mvm_get_temp(struct iwl_mvm *mvm); + +/* Location Aware Regulatory */ +struct iwl_mcc_update_resp * +iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, + enum iwl_mcc_source src_id); +int iwl_mvm_init_mcc(struct iwl_mvm *mvm); +void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, + const char *alpha2, + enum iwl_mcc_source src_id, + bool *changed); +struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm, + bool *changed); +int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm); +void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm); + +/* smart fifo */ +int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool added_vif); + +/* TDLS */ + +/* + * We use TID 4 (VI) as a FW-used-only TID when TDLS connections are present. + * This TID is marked as used vs the AP and all connected TDLS peers. + */ +#define IWL_MVM_TDLS_FW_TID 4 + +int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm); +void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool sta_added); +void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +int iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u8 oper_class, + struct cfg80211_chan_def *chandef, + struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie); +void iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_tdls_ch_sw_params *params); +void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_tdls_ch_switch_work(struct work_struct *work); + +struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); + +void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); +void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); + +int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id); +int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, + const char *str, size_t len, + struct iwl_fw_dbg_trigger_tlv *trigger); +int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, + struct iwl_mvm_dump_desc *desc, + struct iwl_fw_dbg_trigger_tlv *trigger); +void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm); +int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, + struct iwl_fw_dbg_trigger_tlv *trigger, + const char *fmt, ...) __printf(3, 4); +unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool tdls, bool cmd_q); +void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + const char *errmsg); +static inline bool +iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig, + struct ieee80211_vif *vif) +{ + u32 trig_vif = le32_to_cpu(trig->vif_type); + + return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif; +} + +static inline bool +iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm, + struct iwl_fw_dbg_trigger_tlv *trig) +{ + return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) && + (mvm->fw_dbg_conf == FW_DBG_INVALID || + (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids)))); +} + +static inline bool +iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_fw_dbg_trigger_tlv *trig) +{ + if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif)) + return false; + + return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig); +} + +static inline void +iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + enum iwl_fw_dbg_trigger trig) +{ + struct iwl_fw_dbg_trigger_tlv *trigger; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, trig)) + return; + + trigger = iwl_fw_dbg_get_trigger(mvm->fw, trig); + if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) + return; + + iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); +} + +#endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c new file mode 100644 index 000000000000..2ee0f6fe56a1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -0,0 +1,864 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include +#include +#include +#include "iwl-trans.h" +#include "iwl-csr.h" +#include "mvm.h" +#include "iwl-eeprom-parse.h" +#include "iwl-eeprom-read.h" +#include "iwl-nvm-parse.h" +#include "iwl-prph.h" + +/* Default NVM size to read */ +#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) +#define IWL_MAX_NVM_SECTION_SIZE 0x1b58 +#define IWL_MAX_NVM_8000_SECTION_SIZE 0x1ffc + +#define NVM_WRITE_OPCODE 1 +#define NVM_READ_OPCODE 0 + +/* load nvm chunk response */ +enum { + READ_NVM_CHUNK_SUCCEED = 0, + READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1 +}; + +/* + * prepare the NVM host command w/ the pointers to the nvm buffer + * and send it to fw + */ +static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, + u16 offset, u16 length, const u8 *data) +{ + struct iwl_nvm_access_cmd nvm_access_cmd = { + .offset = cpu_to_le16(offset), + .length = cpu_to_le16(length), + .type = cpu_to_le16(section), + .op_code = NVM_WRITE_OPCODE, + }; + struct iwl_host_cmd cmd = { + .id = NVM_ACCESS_CMD, + .len = { sizeof(struct iwl_nvm_access_cmd), length }, + .flags = CMD_SEND_IN_RFKILL, + .data = { &nvm_access_cmd, data }, + /* data may come from vmalloc, so use _DUP */ + .dataflags = { 0, IWL_HCMD_DFL_DUP }, + }; + + return iwl_mvm_send_cmd(mvm, &cmd); +} + +static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, + u16 offset, u16 length, u8 *data) +{ + struct iwl_nvm_access_cmd nvm_access_cmd = { + .offset = cpu_to_le16(offset), + .length = cpu_to_le16(length), + .type = cpu_to_le16(section), + .op_code = NVM_READ_OPCODE, + }; + struct iwl_nvm_access_resp *nvm_resp; + struct iwl_rx_packet *pkt; + struct iwl_host_cmd cmd = { + .id = NVM_ACCESS_CMD, + .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, + .data = { &nvm_access_cmd, }, + }; + int ret, bytes_read, offset_read; + u8 *resp_data; + + cmd.len[0] = sizeof(struct iwl_nvm_access_cmd); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret) + return ret; + + pkt = cmd.resp_pkt; + + /* Extract NVM response */ + nvm_resp = (void *)pkt->data; + ret = le16_to_cpu(nvm_resp->status); + bytes_read = le16_to_cpu(nvm_resp->length); + offset_read = le16_to_cpu(nvm_resp->offset); + resp_data = nvm_resp->data; + if (ret) { + if ((offset != 0) && + (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) { + /* + * meaning of NOT_VALID_ADDRESS: + * driver try to read chunk from address that is + * multiple of 2K and got an error since addr is empty. + * meaning of (offset != 0): driver already + * read valid data from another chunk so this case + * is not an error. + */ + IWL_DEBUG_EEPROM(mvm->trans->dev, + "NVM access command failed on offset 0x%x since that section size is multiple 2K\n", + offset); + ret = 0; + } else { + IWL_DEBUG_EEPROM(mvm->trans->dev, + "NVM access command failed with status %d (device: %s)\n", + ret, mvm->cfg->name); + ret = -EIO; + } + goto exit; + } + + if (offset_read != offset) { + IWL_ERR(mvm, "NVM ACCESS response with invalid offset %d\n", + offset_read); + ret = -EINVAL; + goto exit; + } + + /* Write data to NVM */ + memcpy(data + offset, resp_data, bytes_read); + ret = bytes_read; + +exit: + iwl_free_resp(&cmd); + return ret; +} + +static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section, + const u8 *data, u16 length) +{ + int offset = 0; + + /* copy data in chunks of 2k (and remainder if any) */ + + while (offset < length) { + int chunk_size, ret; + + chunk_size = min(IWL_NVM_DEFAULT_CHUNK_SIZE, + length - offset); + + ret = iwl_nvm_write_chunk(mvm, section, offset, + chunk_size, data + offset); + if (ret < 0) + return ret; + + offset += chunk_size; + } + + return 0; +} + +/* + * Reads an NVM section completely. + * NICs prior to 7000 family doesn't have a real NVM, but just read + * section 0 which is the EEPROM. Because the EEPROM reading is unlimited + * by uCode, we need to manually check in this case that we don't + * overflow and try to read more than the EEPROM size. + * For 7000 family NICs, we supply the maximal size we can read, and + * the uCode fills the response with as much data as we can, + * without overflowing, so no check is needed. + */ +static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, + u8 *data, u32 size_read) +{ + u16 length, offset = 0; + int ret; + + /* Set nvm section read length */ + length = IWL_NVM_DEFAULT_CHUNK_SIZE; + + ret = length; + + /* Read the NVM until exhausted (reading less than requested) */ + while (ret == length) { + /* Check no memory assumptions fail and cause an overflow */ + if ((size_read + offset + length) > + mvm->cfg->base_params->eeprom_size) { + IWL_ERR(mvm, "EEPROM size is too small for NVM\n"); + return -ENOBUFS; + } + + ret = iwl_nvm_read_chunk(mvm, section, offset, length, data); + if (ret < 0) { + IWL_DEBUG_EEPROM(mvm->trans->dev, + "Cannot read NVM from section %d offset %d, length %d\n", + section, offset, length); + return ret; + } + offset += ret; + } + + IWL_DEBUG_EEPROM(mvm->trans->dev, + "NVM section %d read completed\n", section); + return offset; +} + +static struct iwl_nvm_data * +iwl_parse_nvm_sections(struct iwl_mvm *mvm) +{ + struct iwl_nvm_section *sections = mvm->nvm_sections; + const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; + bool lar_enabled; + u32 mac_addr0, mac_addr1; + + /* Checking for required sections */ + if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { + if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || + !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) { + IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n"); + return NULL; + } + } else { + /* SW and REGULATORY sections are mandatory */ + if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || + !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) { + IWL_ERR(mvm, + "Can't parse empty family 8000 OTP/NVM sections\n"); + return NULL; + } + /* MAC_OVERRIDE or at least HW section must exist */ + if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data && + !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) { + IWL_ERR(mvm, + "Can't parse mac_address, empty sections\n"); + return NULL; + } + + /* PHY_SKU section is mandatory in B0 */ + if (!mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) { + IWL_ERR(mvm, + "Can't parse phy_sku in B0, empty sections\n"); + return NULL; + } + } + + if (WARN_ON(!mvm->cfg)) + return NULL; + + /* read the mac address from WFMP registers */ + mac_addr0 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_0); + mac_addr1 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_1); + + hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; + sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; + calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; + regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data; + mac_override = + (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data; + phy_sku = (const __le16 *)sections[NVM_SECTION_TYPE_PHY_SKU].data; + + lar_enabled = !iwlwifi_mod_params.lar_disable && + fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_LAR_SUPPORT); + + return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, + regulatory, mac_override, phy_sku, + mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant, + lar_enabled, mac_addr0, mac_addr1, + mvm->trans->hw_id); +} + +#define MAX_NVM_FILE_LEN 16384 + +/* + * Reads external NVM from a file into mvm->nvm_sections + * + * HOW TO CREATE THE NVM FILE FORMAT: + * ------------------------------ + * 1. create hex file, format: + * 3800 -> header + * 0000 -> header + * 5a40 -> data + * + * rev - 6 bit (word1) + * len - 10 bit (word1) + * id - 4 bit (word2) + * rsv - 12 bit (word2) + * + * 2. flip 8bits with 8 bits per line to get the right NVM file format + * + * 3. create binary file from the hex file + * + * 4. save as "iNVM_xxx.bin" under /lib/firmware + */ +static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) +{ + int ret, section_size; + u16 section_id; + const struct firmware *fw_entry; + const struct { + __le16 word1; + __le16 word2; + u8 data[]; + } *file_sec; + const u8 *eof, *temp; + int max_section_size; + const __le32 *dword_buff; + +#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) +#define NVM_WORD2_ID(x) (x >> 12) +#define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8)) +#define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4) +#define NVM_HEADER_0 (0x2A504C54) +#define NVM_HEADER_1 (0x4E564D2A) +#define NVM_HEADER_SIZE (4 * sizeof(u32)) + + IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n"); + + /* Maximal size depends on HW family and step */ + if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + max_section_size = IWL_MAX_NVM_SECTION_SIZE; + else + max_section_size = IWL_MAX_NVM_8000_SECTION_SIZE; + + /* + * Obtain NVM image via request_firmware. Since we already used + * request_firmware_nowait() for the firmware binary load and only + * get here after that we assume the NVM request can be satisfied + * synchronously. + */ + ret = request_firmware(&fw_entry, mvm->nvm_file_name, + mvm->trans->dev); + if (ret) { + IWL_ERR(mvm, "ERROR: %s isn't available %d\n", + mvm->nvm_file_name, ret); + return ret; + } + + IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", + mvm->nvm_file_name, fw_entry->size); + + if (fw_entry->size > MAX_NVM_FILE_LEN) { + IWL_ERR(mvm, "NVM file too large\n"); + ret = -EINVAL; + goto out; + } + + eof = fw_entry->data + fw_entry->size; + dword_buff = (__le32 *)fw_entry->data; + + /* some NVM file will contain a header. + * The header is identified by 2 dwords header as follow: + * dword[0] = 0x2A504C54 + * dword[1] = 0x4E564D2A + * + * This header must be skipped when providing the NVM data to the FW. + */ + if (fw_entry->size > NVM_HEADER_SIZE && + dword_buff[0] == cpu_to_le32(NVM_HEADER_0) && + dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) { + file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE); + IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2])); + IWL_INFO(mvm, "NVM Manufacturing date %08X\n", + le32_to_cpu(dword_buff[3])); + + /* nvm file validation, dword_buff[2] holds the file version */ + if ((CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_C_STEP && + le32_to_cpu(dword_buff[2]) < 0xE4A) || + (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP && + le32_to_cpu(dword_buff[2]) >= 0xE4A)) { + ret = -EFAULT; + goto out; + } + } else { + file_sec = (void *)fw_entry->data; + } + + while (true) { + if (file_sec->data > eof) { + IWL_ERR(mvm, + "ERROR - NVM file too short for section header\n"); + ret = -EINVAL; + break; + } + + /* check for EOF marker */ + if (!file_sec->word1 && !file_sec->word2) { + ret = 0; + break; + } + + if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { + section_size = + 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); + section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); + } else { + section_size = 2 * NVM_WORD2_LEN_FAMILY_8000( + le16_to_cpu(file_sec->word2)); + section_id = NVM_WORD1_ID_FAMILY_8000( + le16_to_cpu(file_sec->word1)); + } + + if (section_size > max_section_size) { + IWL_ERR(mvm, "ERROR - section too large (%d)\n", + section_size); + ret = -EINVAL; + break; + } + + if (!section_size) { + IWL_ERR(mvm, "ERROR - section empty\n"); + ret = -EINVAL; + break; + } + + if (file_sec->data + section_size > eof) { + IWL_ERR(mvm, + "ERROR - NVM file too short for section (%d bytes)\n", + section_size); + ret = -EINVAL; + break; + } + + if (WARN(section_id >= NVM_MAX_NUM_SECTIONS, + "Invalid NVM section ID %d\n", section_id)) { + ret = -EINVAL; + break; + } + + temp = kmemdup(file_sec->data, section_size, GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + break; + } + kfree(mvm->nvm_sections[section_id].data); + mvm->nvm_sections[section_id].data = temp; + mvm->nvm_sections[section_id].length = section_size; + + /* advance to the next section */ + file_sec = (void *)(file_sec->data + section_size); + } +out: + release_firmware(fw_entry); + return ret; +} + +/* 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 = 0; + struct iwl_nvm_section *sections = mvm->nvm_sections; + + IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n"); + + 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; + } + } + return ret; +} + +int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) +{ + int ret, section; + u32 size_read = 0; + u8 *nvm_buffer, *temp; + const char *nvm_file_B = mvm->cfg->default_nvm_file_B_step; + const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step; + + if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) + return -EINVAL; + + /* load NVM values from nic */ + if (read_nvm_from_nic) { + /* Read From FW NVM */ + IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); + + nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, + GFP_KERNEL); + if (!nvm_buffer) + return -ENOMEM; + for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) { + /* we override the constness for initial read */ + ret = iwl_nvm_read_section(mvm, section, nvm_buffer, + size_read); + if (ret < 0) + continue; + size_read += ret; + temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + break; + } + mvm->nvm_sections[section].data = temp; + mvm->nvm_sections[section].length = ret; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + switch (section) { + case NVM_SECTION_TYPE_SW: + mvm->nvm_sw_blob.data = temp; + mvm->nvm_sw_blob.size = ret; + break; + case NVM_SECTION_TYPE_CALIBRATION: + mvm->nvm_calib_blob.data = temp; + mvm->nvm_calib_blob.size = ret; + break; + case NVM_SECTION_TYPE_PRODUCTION: + mvm->nvm_prod_blob.data = temp; + mvm->nvm_prod_blob.size = ret; + break; + case NVM_SECTION_TYPE_PHY_SKU: + mvm->nvm_phy_sku_blob.data = temp; + mvm->nvm_phy_sku_blob.size = ret; + break; + default: + if (section == mvm->cfg->nvm_hw_section_num) { + mvm->nvm_hw_blob.data = temp; + mvm->nvm_hw_blob.size = ret; + break; + } + } +#endif + } + if (!size_read) + IWL_ERR(mvm, "OTP is blank\n"); + kfree(nvm_buffer); + } + + /* Only if PNVM selected in the mod param - load external NVM */ + if (mvm->nvm_file_name) { + /* read External NVM file from the mod param */ + ret = iwl_mvm_read_external_nvm(mvm); + if (ret) { + /* choose the nvm_file name according to the + * HW step + */ + if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == + SILICON_B_STEP) + mvm->nvm_file_name = nvm_file_B; + else + mvm->nvm_file_name = nvm_file_C; + + if (ret == -EFAULT && mvm->nvm_file_name) { + /* in case nvm file was failed try again */ + ret = iwl_mvm_read_external_nvm(mvm); + if (ret) + return ret; + } else { + return ret; + } + } + } + + /* parse the relevant nvm sections */ + mvm->nvm_data = iwl_parse_nvm_sections(mvm); + if (!mvm->nvm_data) + return -ENODATA; + IWL_DEBUG_EEPROM(mvm->trans->dev, "nvm version = %x\n", + mvm->nvm_data->nvm_version); + + return 0; +} + +struct iwl_mcc_update_resp * +iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, + enum iwl_mcc_source src_id) +{ + struct iwl_mcc_update_cmd mcc_update_cmd = { + .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]), + .source_id = (u8)src_id, + }; + struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL; + struct iwl_rx_packet *pkt; + struct iwl_host_cmd cmd = { + .id = MCC_UPDATE_CMD, + .flags = CMD_WANT_SKB, + .data = { &mcc_update_cmd }, + }; + + int ret; + u32 status; + int resp_len, n_channels; + u16 mcc; + + if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) + return ERR_PTR(-EOPNOTSUPP); + + cmd.len[0] = sizeof(struct iwl_mcc_update_cmd); + + IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n", + alpha2[0], alpha2[1], src_id); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret) + return ERR_PTR(ret); + + pkt = cmd.resp_pkt; + + /* Extract MCC response */ + mcc_resp = (void *)pkt->data; + status = le32_to_cpu(mcc_resp->status); + + mcc = le16_to_cpu(mcc_resp->mcc); + + /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ + if (mcc == 0) { + mcc = 0x3030; /* "00" - world */ + mcc_resp->mcc = cpu_to_le16(mcc); + } + + n_channels = __le32_to_cpu(mcc_resp->n_channels); + IWL_DEBUG_LAR(mvm, + "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n", + status, mcc, mcc >> 8, mcc & 0xff, + !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels); + + resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32); + resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL); + if (!resp_cp) { + ret = -ENOMEM; + goto exit; + } + + ret = 0; +exit: + iwl_free_resp(&cmd); + if (ret) + return ERR_PTR(ret); + return resp_cp; +} + +#ifdef CONFIG_ACPI +#define WRD_METHOD "WRDD" +#define WRDD_WIFI (0x07) +#define WRDD_WIGIG (0x10) + +static u32 iwl_mvm_wrdd_get_mcc(struct iwl_mvm *mvm, union acpi_object *wrdd) +{ + union acpi_object *mcc_pkg, *domain_type, *mcc_value; + u32 i; + + if (wrdd->type != ACPI_TYPE_PACKAGE || + wrdd->package.count < 2 || + wrdd->package.elements[0].type != ACPI_TYPE_INTEGER || + wrdd->package.elements[0].integer.value != 0) { + IWL_DEBUG_LAR(mvm, "Unsupported wrdd structure\n"); + return 0; + } + + for (i = 1 ; i < wrdd->package.count ; ++i) { + mcc_pkg = &wrdd->package.elements[i]; + + if (mcc_pkg->type != ACPI_TYPE_PACKAGE || + mcc_pkg->package.count < 2 || + mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER || + mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { + mcc_pkg = NULL; + continue; + } + + domain_type = &mcc_pkg->package.elements[0]; + if (domain_type->integer.value == WRDD_WIFI) + break; + + mcc_pkg = NULL; + } + + if (mcc_pkg) { + mcc_value = &mcc_pkg->package.elements[1]; + return mcc_value->integer.value; + } + + return 0; +} + +static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc) +{ + acpi_handle root_handle; + acpi_handle handle; + struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + u32 mcc_val; + struct pci_dev *pdev = to_pci_dev(mvm->dev); + + root_handle = ACPI_HANDLE(&pdev->dev); + if (!root_handle) { + IWL_DEBUG_LAR(mvm, + "Could not retrieve root port ACPI handle\n"); + return -ENOENT; + } + + /* Get the method's handle */ + status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_LAR(mvm, "WRD method not found\n"); + return -ENOENT; + } + + /* Call WRDD with no arguments */ + status = acpi_evaluate_object(handle, NULL, NULL, &wrdd); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_LAR(mvm, "WRDC invocation failed (0x%x)\n", status); + return -ENOENT; + } + + mcc_val = iwl_mvm_wrdd_get_mcc(mvm, wrdd.pointer); + kfree(wrdd.pointer); + if (!mcc_val) + return -ENOENT; + + mcc[0] = (mcc_val >> 8) & 0xff; + mcc[1] = mcc_val & 0xff; + mcc[2] = '\0'; + return 0; +} +#else /* CONFIG_ACPI */ +static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc) +{ + return -ENOENT; +} +#endif + +int iwl_mvm_init_mcc(struct iwl_mvm *mvm) +{ + bool tlv_lar; + bool nvm_lar; + int retval; + struct ieee80211_regdomain *regd; + char mcc[3]; + + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) { + tlv_lar = fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_LAR_SUPPORT); + nvm_lar = mvm->nvm_data->lar_enabled; + if (tlv_lar != nvm_lar) + IWL_INFO(mvm, + "Conflict between TLV & NVM regarding enabling LAR (TLV = %s NVM =%s)\n", + tlv_lar ? "enabled" : "disabled", + nvm_lar ? "enabled" : "disabled"); + } + + if (!iwl_mvm_is_lar_supported(mvm)) + return 0; + + /* + * try to replay the last set MCC to FW. If it doesn't exist, + * queue an update to cfg80211 to retrieve the default alpha2 from FW. + */ + retval = iwl_mvm_init_fw_regd(mvm); + if (retval != -ENOENT) + return retval; + + /* + * Driver regulatory hint for initial update, this also informs the + * firmware we support wifi location updates. + * Disallow scans that might crash the FW while the LAR regdomain + * is not set. + */ + mvm->lar_regdom_set = false; + + regd = iwl_mvm_get_current_regdomain(mvm, NULL); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + + if (iwl_mvm_is_wifi_mcc_supported(mvm) && + !iwl_mvm_get_bios_mcc(mvm, mcc)) { + kfree(regd); + regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, + MCC_SOURCE_BIOS, NULL); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + } + + retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd); + kfree(regd); + return retval; +} + +void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mcc_chub_notif *notif = (void *)pkt->data; + enum iwl_mcc_source src; + char mcc[3]; + struct ieee80211_regdomain *regd; + + lockdep_assert_held(&mvm->mutex); + + if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) + return; + + mcc[0] = notif->mcc >> 8; + mcc[1] = notif->mcc & 0xff; + mcc[2] = '\0'; + src = notif->source_id; + + IWL_DEBUG_LAR(mvm, + "RX: received chub update mcc cmd (mcc '%s' src %d)\n", + mcc, src); + regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL); + if (IS_ERR_OR_NULL(regd)) + return; + + regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); + kfree(regd); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c new file mode 100644 index 000000000000..68b0169c8892 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c @@ -0,0 +1,217 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include +#include "mvm.h" + +void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, + struct iwl_wowlan_config_cmd *cmd) +{ + int i; + + /* + * For QoS counters, we store the one to use next, so subtract 0x10 + * since the uCode will add 0x10 *before* using the value while we + * increment after using the value (i.e. store the next value to use). + */ + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + u16 seq = mvm_ap_sta->tid_data[i].seq_number; + seq -= 0x10; + cmd->qos_seq[i] = cpu_to_le16(seq); + } +} + +int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool disable_offloading, + u32 cmd_flags) +{ + union { + struct iwl_proto_offload_cmd_v1 v1; + struct iwl_proto_offload_cmd_v2 v2; + struct iwl_proto_offload_cmd_v3_small v3s; + struct iwl_proto_offload_cmd_v3_large v3l; + } cmd = {}; + struct iwl_host_cmd hcmd = { + .id = PROT_OFFLOAD_CONFIG_CMD, + .flags = cmd_flags, + .data[0] = &cmd, + .dataflags[0] = IWL_HCMD_DFL_DUP, + }; + struct iwl_proto_offload_cmd_common *common; + u32 enabled = 0, size; + u32 capa_flags = mvm->fw->ucode_capa.flags; +#if IS_ENABLED(CONFIG_IPV6) + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int i; + + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || + capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { + struct iwl_ns_config *nsc; + struct iwl_targ_addr *addrs; + int n_nsc, n_addrs; + int c; + + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { + nsc = cmd.v3s.ns_config; + n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S; + addrs = cmd.v3s.targ_addrs; + n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S; + } else { + nsc = cmd.v3l.ns_config; + n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; + addrs = cmd.v3l.targ_addrs; + n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; + } + + if (mvmvif->num_target_ipv6_addrs) + enabled |= IWL_D3_PROTO_OFFLOAD_NS; + + /* + * For each address we have (and that will fit) fill a target + * address struct and combine for NS offload structs with the + * solicited node addresses. + */ + for (i = 0, c = 0; + i < mvmvif->num_target_ipv6_addrs && + i < n_addrs && c < n_nsc; i++) { + struct in6_addr solicited_addr; + int j; + + addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], + &solicited_addr); + for (j = 0; j < c; j++) + if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, + &solicited_addr) == 0) + break; + if (j == c) + c++; + addrs[i].addr = mvmvif->target_ipv6_addrs[i]; + addrs[i].config_num = cpu_to_le32(j); + nsc[j].dest_ipv6_addr = solicited_addr; + memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); + } + + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) + cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i); + else + cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i); + } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { + if (mvmvif->num_target_ipv6_addrs) { + enabled |= IWL_D3_PROTO_OFFLOAD_NS; + memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); + } + + BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != + sizeof(mvmvif->target_ipv6_addrs[0])); + + for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, + IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) + memcpy(cmd.v2.target_ipv6_addr[i], + &mvmvif->target_ipv6_addrs[i], + sizeof(cmd.v2.target_ipv6_addr[i])); + } else { + if (mvmvif->num_target_ipv6_addrs) { + enabled |= IWL_D3_PROTO_OFFLOAD_NS; + memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); + } + + BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != + sizeof(mvmvif->target_ipv6_addrs[0])); + + for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, + IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) + memcpy(cmd.v1.target_ipv6_addr[i], + &mvmvif->target_ipv6_addrs[i], + sizeof(cmd.v1.target_ipv6_addr[i])); + } +#endif + + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { + common = &cmd.v3s.common; + size = sizeof(cmd.v3s); + } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { + common = &cmd.v3l.common; + size = sizeof(cmd.v3l); + } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { + common = &cmd.v2.common; + size = sizeof(cmd.v2); + } else { + common = &cmd.v1.common; + size = sizeof(cmd.v1); + } + + if (vif->bss_conf.arp_addr_cnt) { + enabled |= IWL_D3_PROTO_OFFLOAD_ARP; + common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; + memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); + } + + if (!disable_offloading) + common->enabled = cpu_to_le32(enabled); + + hcmd.len[0] = size; + return iwl_mvm_send_cmd(mvm, &hcmd); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c new file mode 100644 index 000000000000..13c97f665ba8 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -0,0 +1,1434 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include +#include + +#include "iwl-notif-wait.h" +#include "iwl-trans.h" +#include "iwl-op-mode.h" +#include "iwl-fw.h" +#include "iwl-debug.h" +#include "iwl-drv.h" +#include "iwl-modparams.h" +#include "mvm.h" +#include "iwl-phy-db.h" +#include "iwl-eeprom-parse.h" +#include "iwl-csr.h" +#include "iwl-io.h" +#include "iwl-prph.h" +#include "rs.h" +#include "fw-api-scan.h" +#include "time-event.h" + +#define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_LICENSE("GPL"); + +static const struct iwl_op_mode_ops iwl_mvm_ops; +static const struct iwl_op_mode_ops iwl_mvm_ops_mq; + +struct iwl_mvm_mod_params iwlmvm_mod_params = { + .power_scheme = IWL_POWER_SCHEME_BPS, + .tfd_q_hang_detect = true + /* rest of fields are 0 by default */ +}; + +module_param_named(init_dbg, iwlmvm_mod_params.init_dbg, bool, S_IRUGO); +MODULE_PARM_DESC(init_dbg, + "set to true to debug an ASSERT in INIT fw (default: false"); +module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO); +MODULE_PARM_DESC(power_scheme, + "power management scheme: 1-active, 2-balanced, 3-low power, default: 2"); +module_param_named(tfd_q_hang_detect, iwlmvm_mod_params.tfd_q_hang_detect, + bool, S_IRUGO); +MODULE_PARM_DESC(tfd_q_hang_detect, + "TFD queues hang detection (default: true"); + +/* + * module init and exit functions + */ +static int __init iwl_mvm_init(void) +{ + int ret; + + ret = iwl_mvm_rate_control_register(); + if (ret) { + pr_err("Unable to register rate control algorithm: %d\n", ret); + return ret; + } + + ret = iwl_opmode_register("iwlmvm", &iwl_mvm_ops); + + if (ret) { + pr_err("Unable to register MVM op_mode: %d\n", ret); + iwl_mvm_rate_control_unregister(); + } + + return ret; +} +module_init(iwl_mvm_init); + +static void __exit iwl_mvm_exit(void) +{ + iwl_opmode_deregister("iwlmvm"); + iwl_mvm_rate_control_unregister(); +} +module_exit(iwl_mvm_exit); + +static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash; + u32 reg_val = 0; + u32 phy_config = iwl_mvm_get_phy_config(mvm); + + radio_cfg_type = (phy_config & FW_PHY_CFG_RADIO_TYPE) >> + FW_PHY_CFG_RADIO_TYPE_POS; + radio_cfg_step = (phy_config & FW_PHY_CFG_RADIO_STEP) >> + FW_PHY_CFG_RADIO_STEP_POS; + radio_cfg_dash = (phy_config & FW_PHY_CFG_RADIO_DASH) >> + FW_PHY_CFG_RADIO_DASH_POS; + + /* SKU control */ + reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) << + CSR_HW_IF_CONFIG_REG_POS_MAC_STEP; + reg_val |= CSR_HW_REV_DASH(mvm->trans->hw_rev) << + CSR_HW_IF_CONFIG_REG_POS_MAC_DASH; + + /* radio configuration */ + reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; + reg_val |= radio_cfg_step << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP; + reg_val |= radio_cfg_dash << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; + + WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) & + ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE); + + /* + * TODO: Bits 7-8 of CSR in 8000 HW family set the ADC sampling, and + * shouldn't be set to any non-zero value. The same is supposed to be + * true of the other HW, but unsetting them (such as the 7260) causes + * automatic tests to fail on seemingly unrelated errors. Need to + * further investigate this, but for now we'll separate cases. + */ + if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI; + + iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | + CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP | + CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | + CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | + CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH | + CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | + CSR_HW_IF_CONFIG_REG_BIT_MAC_SI, + reg_val); + + IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, + radio_cfg_step, radio_cfg_dash); + + /* + * W/A : NIC is stuck in a reset state after Early PCIe power off + * (PCIe power is lost before PERST# is asserted), causing ME FW + * to lose ownership and not being able to obtain it back. + */ + if (!mvm->trans->cfg->apmg_not_supported) + iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, + ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); +} + +struct iwl_rx_handlers { + u16 cmd_id; + bool async; + void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +}; + +#define RX_HANDLER(_cmd_id, _fn, _async) \ + { .cmd_id = _cmd_id , .fn = _fn , .async = _async } +#define RX_HANDLER_GRP(_grp, _cmd, _fn, _async) \ + { .cmd_id = WIDE_ID(_grp, _cmd), .fn = _fn, .async = _async } + +/* + * Handlers for fw notifications + * Convention: RX_HANDLER(CMD_NAME, iwl_mvm_rx_CMD_NAME + * This list should be in order of frequency for performance purposes. + * + * The handler can be SYNC - this means that it will be called in the Rx path + * which can't acquire mvm->mutex. If the handler needs to hold mvm->mutex (and + * only in this case!), it should be set as ASYNC. In that case, it will be + * called from a worker with mvm->mutex held. + */ +static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { + RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false), + RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false), + + RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), + RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true), + RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), + RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION, + iwl_mvm_rx_ant_coupling_notif, true), + + RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false), + RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, true), + + RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false), + + RX_HANDLER(SCAN_ITERATION_COMPLETE, + iwl_mvm_rx_lmac_scan_iter_complete_notif, false), + RX_HANDLER(SCAN_OFFLOAD_COMPLETE, + iwl_mvm_rx_lmac_scan_complete_notif, true), + RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_match_found, + false), + RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif, + true), + RX_HANDLER(SCAN_ITERATION_COMPLETE_UMAC, + iwl_mvm_rx_umac_scan_iter_complete_notif, false), + + RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), + + RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif, + false), + + RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false), + RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION, + iwl_mvm_power_uapsd_misbehaving_ap_notif, false), + RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true), + RX_HANDLER_GRP(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE, + iwl_mvm_temp_notif, true), + + RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif, + true), + RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, false), + RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler, true), + +}; +#undef RX_HANDLER +#undef RX_HANDLER_GRP +#define CMD(x) [x] = #x + +static const char *const iwl_mvm_cmd_strings[REPLY_MAX + 1] = { + CMD(MVM_ALIVE), + CMD(REPLY_ERROR), + CMD(ECHO_CMD), + CMD(INIT_COMPLETE_NOTIF), + CMD(PHY_CONTEXT_CMD), + CMD(MGMT_MCAST_KEY), + CMD(TX_CMD), + CMD(TXPATH_FLUSH), + CMD(SHARED_MEM_CFG), + CMD(MAC_CONTEXT_CMD), + CMD(TIME_EVENT_CMD), + CMD(TIME_EVENT_NOTIFICATION), + CMD(BINDING_CONTEXT_CMD), + CMD(TIME_QUOTA_CMD), + CMD(NON_QOS_TX_COUNTER_CMD), + CMD(DC2DC_CONFIG_CMD), + CMD(NVM_ACCESS_CMD), + CMD(PHY_CONFIGURATION_CMD), + CMD(CALIB_RES_NOTIF_PHY_DB), + CMD(SET_CALIB_DEFAULT_CMD), + CMD(FW_PAGING_BLOCK_CMD), + CMD(ADD_STA_KEY), + CMD(ADD_STA), + CMD(FW_GET_ITEM_CMD), + CMD(REMOVE_STA), + CMD(LQ_CMD), + CMD(SCAN_OFFLOAD_CONFIG_CMD), + CMD(MATCH_FOUND_NOTIFICATION), + CMD(SCAN_OFFLOAD_REQUEST_CMD), + CMD(SCAN_OFFLOAD_ABORT_CMD), + CMD(HOT_SPOT_CMD), + CMD(SCAN_OFFLOAD_COMPLETE), + CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD), + CMD(SCAN_ITERATION_COMPLETE), + CMD(POWER_TABLE_CMD), + CMD(WEP_KEY), + CMD(REPLY_RX_PHY_CMD), + CMD(REPLY_RX_MPDU_CMD), + CMD(BEACON_NOTIFICATION), + CMD(BEACON_TEMPLATE_CMD), + CMD(STATISTICS_CMD), + CMD(STATISTICS_NOTIFICATION), + CMD(EOSP_NOTIFICATION), + CMD(REDUCE_TX_POWER_CMD), + CMD(TX_ANT_CONFIGURATION_CMD), + CMD(D3_CONFIG_CMD), + CMD(D0I3_END_CMD), + CMD(PROT_OFFLOAD_CONFIG_CMD), + CMD(OFFLOADS_QUERY_CMD), + CMD(REMOTE_WAKE_CONFIG_CMD), + CMD(WOWLAN_PATTERNS), + CMD(WOWLAN_CONFIGURATION), + CMD(WOWLAN_TSC_RSC_PARAM), + CMD(WOWLAN_TKIP_PARAM), + CMD(WOWLAN_KEK_KCK_MATERIAL), + CMD(WOWLAN_GET_STATUSES), + CMD(WOWLAN_TX_POWER_PER_DB), + CMD(SCAN_OFFLOAD_PROFILES_QUERY_CMD), + CMD(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD), + CMD(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD), + CMD(CARD_STATE_NOTIFICATION), + CMD(MISSED_BEACONS_NOTIFICATION), + CMD(BT_COEX_PRIO_TABLE), + CMD(BT_COEX_PROT_ENV), + CMD(BT_PROFILE_NOTIFICATION), + CMD(BT_CONFIG), + CMD(MCAST_FILTER_CMD), + CMD(BCAST_FILTER_CMD), + CMD(REPLY_SF_CFG_CMD), + CMD(REPLY_BEACON_FILTERING_CMD), + CMD(CMD_DTS_MEASUREMENT_TRIGGER), + CMD(DTS_MEASUREMENT_NOTIFICATION), + CMD(REPLY_THERMAL_MNG_BACKOFF), + CMD(MAC_PM_POWER_TABLE), + CMD(LTR_CONFIG), + CMD(BT_COEX_CI), + CMD(BT_COEX_UPDATE_SW_BOOST), + CMD(BT_COEX_UPDATE_CORUN_LUT), + CMD(BT_COEX_UPDATE_REDUCED_TXP), + CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), + CMD(ANTENNA_COUPLING_NOTIFICATION), + CMD(SCD_QUEUE_CFG), + CMD(SCAN_CFG_CMD), + CMD(SCAN_REQ_UMAC), + CMD(SCAN_ABORT_UMAC), + CMD(SCAN_COMPLETE_UMAC), + CMD(TDLS_CHANNEL_SWITCH_CMD), + CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION), + CMD(TDLS_CONFIG_CMD), + CMD(MCC_UPDATE_CMD), + CMD(SCAN_ITERATION_COMPLETE_UMAC), +}; +#undef CMD + +/* this forward declaration can avoid to export the function */ +static void iwl_mvm_async_handlers_wk(struct work_struct *wk); +static void iwl_mvm_d0i3_exit_work(struct work_struct *wk); + +static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg) +{ + const struct iwl_pwr_tx_backoff *pwr_tx_backoff = cfg->pwr_tx_backoffs; + + if (!pwr_tx_backoff) + return 0; + + while (pwr_tx_backoff->pwr) { + if (trans->dflt_pwr_limit >= pwr_tx_backoff->pwr) + return pwr_tx_backoff->backoff; + + pwr_tx_backoff++; + } + + return 0; +} + +static void iwl_mvm_fw_error_dump_wk(struct work_struct *work); + +static struct iwl_op_mode * +iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, + const struct iwl_fw *fw, struct dentry *dbgfs_dir) +{ + struct ieee80211_hw *hw; + struct iwl_op_mode *op_mode; + struct iwl_mvm *mvm; + struct iwl_trans_config trans_cfg = {}; + static const u8 no_reclaim_cmds[] = { + TX_CMD, + }; + int err, scan_size; + u32 min_backoff; + + /* + * We use IWL_MVM_STATION_COUNT to check the validity of the station + * index all over the driver - check that its value corresponds to the + * array size. + */ + BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT); + + /******************************** + * 1. Allocating and configuring HW data + ********************************/ + hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) + + sizeof(struct iwl_mvm), + &iwl_mvm_hw_ops); + if (!hw) + return NULL; + + if (cfg->max_rx_agg_size) + hw->max_rx_aggregation_subframes = cfg->max_rx_agg_size; + + if (cfg->max_tx_agg_size) + hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size; + + op_mode = hw->priv; + + mvm = IWL_OP_MODE_GET_MVM(op_mode); + mvm->dev = trans->dev; + mvm->trans = trans; + mvm->cfg = cfg; + mvm->fw = fw; + mvm->hw = hw; + + if (iwl_mvm_has_new_rx_api(mvm)) { + op_mode->ops = &iwl_mvm_ops_mq; + } else { + op_mode->ops = &iwl_mvm_ops; + + if (WARN_ON(trans->num_rx_queues > 1)) + goto out_free; + } + + mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0; + + mvm->aux_queue = 15; + mvm->first_agg_queue = 16; + mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1; + if (mvm->cfg->base_params->num_of_queues == 16) { + mvm->aux_queue = 11; + mvm->first_agg_queue = 12; + } + mvm->sf_state = SF_UNINIT; + mvm->low_latency_agg_frame_limit = 6; + mvm->cur_ucode = IWL_UCODE_INIT; + + mutex_init(&mvm->mutex); + mutex_init(&mvm->d0i3_suspend_mutex); + spin_lock_init(&mvm->async_handlers_lock); + INIT_LIST_HEAD(&mvm->time_event_list); + INIT_LIST_HEAD(&mvm->aux_roc_te_list); + INIT_LIST_HEAD(&mvm->async_handlers_list); + spin_lock_init(&mvm->time_event_lock); + spin_lock_init(&mvm->queue_info_lock); + + INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); + INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); + INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); + INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); + INIT_DELAYED_WORK(&mvm->fw_dump_wk, iwl_mvm_fw_error_dump_wk); + INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work); + + spin_lock_init(&mvm->d0i3_tx_lock); + spin_lock_init(&mvm->refs_lock); + skb_queue_head_init(&mvm->d0i3_tx); + init_waitqueue_head(&mvm->d0i3_exit_waitq); + + SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); + + /* + * Populate the state variables that the transport layer needs + * to know about. + */ + trans_cfg.op_mode = op_mode; + trans_cfg.no_reclaim_cmds = no_reclaim_cmds; + trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); + trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; + trans_cfg.wide_cmd_header = fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_WIDE_CMD_HDR); + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) + trans_cfg.bc_table_dword = true; + + trans_cfg.command_names = iwl_mvm_cmd_strings; + + trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; + trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; + trans_cfg.scd_set_active = true; + + trans_cfg.sdio_adma_addr = fw->sdio_adma_addr; + + /* Set a short watchdog for the command queue */ + trans_cfg.cmd_q_wdg_timeout = + iwl_mvm_get_wd_timeout(mvm, NULL, false, true); + + snprintf(mvm->hw->wiphy->fw_version, + sizeof(mvm->hw->wiphy->fw_version), + "%s", fw->fw_version); + + /* Configure transport layer */ + iwl_trans_configure(mvm->trans, &trans_cfg); + + trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; + trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); + trans->dbg_dest_tlv = mvm->fw->dbg_dest_tlv; + trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num; + memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv, + sizeof(trans->dbg_conf_tlv)); + trans->dbg_trigger_tlv = mvm->fw->dbg_trigger_tlv; + + /* set up notification wait support */ + iwl_notification_wait_init(&mvm->notif_wait); + + /* Init phy db */ + mvm->phy_db = iwl_phy_db_init(trans); + if (!mvm->phy_db) { + IWL_ERR(mvm, "Cannot init phy_db\n"); + goto out_free; + } + + IWL_INFO(mvm, "Detected %s, REV=0x%X\n", + mvm->cfg->name, mvm->trans->hw_rev); + + min_backoff = calc_min_backoff(trans, cfg); + iwl_mvm_tt_initialize(mvm, min_backoff); + + if (iwlwifi_mod_params.nvm_file) + mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; + else + IWL_DEBUG_EEPROM(mvm->trans->dev, + "working without external nvm file\n"); + + if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, + "not allowing power-up and not having nvm_file\n")) + goto out_free; + + /* + * Even if nvm exists in the nvm_file driver should read again the nvm + * from the nic because there might be entries that exist in the OTP + * and not in the file. + * for nics with no_power_up_nic_in_init: rely completley on nvm_file + */ + if (cfg->no_power_up_nic_in_init && mvm->nvm_file_name) { + err = iwl_nvm_init(mvm, false); + if (err) + goto out_free; + } else { + err = iwl_trans_start_hw(mvm->trans); + if (err) + goto out_free; + + mutex_lock(&mvm->mutex); + err = iwl_run_init_mvm_ucode(mvm, true); + if (!err || !iwlmvm_mod_params.init_dbg) + iwl_trans_stop_device(trans); + mutex_unlock(&mvm->mutex); + /* returns 0 if successful, 1 if success but in rfkill */ + if (err < 0 && !iwlmvm_mod_params.init_dbg) { + IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); + goto out_free; + } + } + + scan_size = iwl_mvm_scan_size(mvm); + + mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL); + if (!mvm->scan_cmd) + goto out_free; + + /* Set EBS as successful as long as not stated otherwise by the FW. */ + mvm->last_ebs_successful = true; + + err = iwl_mvm_mac_setup_register(mvm); + if (err) + goto out_free; + + err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir); + if (err) + goto out_unregister; + + memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); + + /* rpm starts with a taken ref. only set the appropriate bit here. */ + mvm->refs[IWL_MVM_REF_UCODE_DOWN] = 1; + + iwl_mvm_tof_init(mvm); + + return op_mode; + + out_unregister: + ieee80211_unregister_hw(mvm->hw); + iwl_mvm_leds_exit(mvm); + out_free: + flush_delayed_work(&mvm->fw_dump_wk); + iwl_phy_db_free(mvm->phy_db); + kfree(mvm->scan_cmd); + if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name) + iwl_trans_op_mode_leave(trans); + ieee80211_free_hw(mvm->hw); + return NULL; +} + +static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + int i; + + iwl_mvm_leds_exit(mvm); + + iwl_mvm_tt_exit(mvm); + + ieee80211_unregister_hw(mvm->hw); + + kfree(mvm->scan_cmd); + kfree(mvm->mcast_filter_cmd); + mvm->mcast_filter_cmd = NULL; + +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS) + kfree(mvm->d3_resume_sram); + if (mvm->nd_config) { + kfree(mvm->nd_config->match_sets); + kfree(mvm->nd_config->scan_plans); + kfree(mvm->nd_config); + mvm->nd_config = NULL; + } +#endif + + iwl_trans_op_mode_leave(mvm->trans); + + iwl_phy_db_free(mvm->phy_db); + mvm->phy_db = NULL; + + iwl_free_nvm_data(mvm->nvm_data); + for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++) + kfree(mvm->nvm_sections[i].data); + + iwl_mvm_tof_clean(mvm); + + ieee80211_free_hw(mvm->hw); +} + +struct iwl_async_handler_entry { + struct list_head list; + struct iwl_rx_cmd_buffer rxb; + void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +}; + +void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm) +{ + struct iwl_async_handler_entry *entry, *tmp; + + spin_lock_bh(&mvm->async_handlers_lock); + list_for_each_entry_safe(entry, tmp, &mvm->async_handlers_list, list) { + iwl_free_rxb(&entry->rxb); + list_del(&entry->list); + kfree(entry); + } + spin_unlock_bh(&mvm->async_handlers_lock); +} + +static void iwl_mvm_async_handlers_wk(struct work_struct *wk) +{ + struct iwl_mvm *mvm = + container_of(wk, struct iwl_mvm, async_handlers_wk); + struct iwl_async_handler_entry *entry, *tmp; + struct list_head local_list; + + INIT_LIST_HEAD(&local_list); + + /* Ensure that we are not in stop flow (check iwl_mvm_mac_stop) */ + mutex_lock(&mvm->mutex); + + /* + * Sync with Rx path with a lock. Remove all the entries from this list, + * add them to a local one (lock free), and then handle them. + */ + spin_lock_bh(&mvm->async_handlers_lock); + list_splice_init(&mvm->async_handlers_list, &local_list); + spin_unlock_bh(&mvm->async_handlers_lock); + + list_for_each_entry_safe(entry, tmp, &local_list, list) { + entry->fn(mvm, &entry->rxb); + iwl_free_rxb(&entry->rxb); + list_del(&entry->list); + kfree(entry); + } + mutex_unlock(&mvm->mutex); +} + +static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm, + struct iwl_rx_packet *pkt) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_cmd *cmds_trig; + int i; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF)) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF); + cmds_trig = (void *)trig->data; + + if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) + return; + + for (i = 0; i < ARRAY_SIZE(cmds_trig->cmds); i++) { + /* don't collect on CMD 0 */ + if (!cmds_trig->cmds[i].cmd_id) + break; + + if (cmds_trig->cmds[i].cmd_id != pkt->hdr.cmd || + cmds_trig->cmds[i].group_id != pkt->hdr.group_id) + continue; + + iwl_mvm_fw_dbg_collect_trig(mvm, trig, + "CMD 0x%02x.%02x received", + pkt->hdr.group_id, pkt->hdr.cmd); + break; + } +} + +static void iwl_mvm_rx_common(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_rx_packet *pkt) +{ + int i; + + iwl_mvm_rx_check_trigger(mvm, pkt); + + /* + * Do the notification wait before RX handlers so + * even if the RX handler consumes the RXB we have + * access to it in the notification wait entry. + */ + iwl_notification_wait_notify(&mvm->notif_wait, pkt); + + for (i = 0; i < ARRAY_SIZE(iwl_mvm_rx_handlers); i++) { + const struct iwl_rx_handlers *rx_h = &iwl_mvm_rx_handlers[i]; + struct iwl_async_handler_entry *entry; + + if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) + continue; + + if (!rx_h->async) { + rx_h->fn(mvm, rxb); + return; + } + + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + /* we can't do much... */ + if (!entry) + return; + + entry->rxb._page = rxb_steal_page(rxb); + entry->rxb._offset = rxb->_offset; + entry->rxb._rx_page_order = rxb->_rx_page_order; + entry->fn = rx_h->fn; + spin_lock(&mvm->async_handlers_lock); + list_add_tail(&entry->list, &mvm->async_handlers_list); + spin_unlock(&mvm->async_handlers_lock); + schedule_work(&mvm->async_handlers_wk); + break; + } +} + +static void iwl_mvm_rx(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) + iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); + else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) + iwl_mvm_rx_rx_phy_cmd(mvm, rxb); + else + iwl_mvm_rx_common(mvm, rxb, pkt); +} + +static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) + iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); + else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) + iwl_mvm_rx_rx_phy_cmd(mvm, rxb); + else + iwl_mvm_rx_common(mvm, rxb, pkt); +} + +static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + unsigned long mq; + int q; + + spin_lock_bh(&mvm->queue_info_lock); + mq = mvm->queue_info[queue].hw_queue_to_mac80211; + spin_unlock_bh(&mvm->queue_info_lock); + + if (WARN_ON_ONCE(!mq)) + return; + + for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) { + if (atomic_inc_return(&mvm->mac80211_queue_stop_count[q]) > 1) { + IWL_DEBUG_TX_QUEUES(mvm, + "queue %d (mac80211 %d) already stopped\n", + queue, q); + continue; + } + + ieee80211_stop_queue(mvm->hw, q); + } +} + +static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + unsigned long mq; + int q; + + spin_lock_bh(&mvm->queue_info_lock); + mq = mvm->queue_info[queue].hw_queue_to_mac80211; + spin_unlock_bh(&mvm->queue_info_lock); + + if (WARN_ON_ONCE(!mq)) + return; + + for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) { + if (atomic_dec_return(&mvm->mac80211_queue_stop_count[q]) > 0) { + IWL_DEBUG_TX_QUEUES(mvm, + "queue %d (mac80211 %d) still stopped\n", + queue, q); + continue; + } + + ieee80211_wake_queue(mvm->hw, q); + } +} + +void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) +{ + if (state) + set_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); + else + clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); + + wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); +} + +static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + bool calibrating = ACCESS_ONCE(mvm->calibrating); + + if (state) + set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); + else + clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); + + wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); + + /* iwl_run_init_mvm_ucode is waiting for results, abort it */ + if (calibrating) + iwl_abort_notification_waits(&mvm->notif_wait); + + /* + * Stop the device if we run OPERATIONAL firmware or if we are in the + * middle of the calibrations. + */ + return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating); +} + +static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + struct ieee80211_tx_info *info; + + info = IEEE80211_SKB_CB(skb); + iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); + ieee80211_free_txskb(mvm->hw, skb); +} + +struct iwl_mvm_reprobe { + struct device *dev; + struct work_struct work; +}; + +static void iwl_mvm_reprobe_wk(struct work_struct *wk) +{ + struct iwl_mvm_reprobe *reprobe; + + reprobe = container_of(wk, struct iwl_mvm_reprobe, work); + if (device_reprobe(reprobe->dev)) + dev_err(reprobe->dev, "reprobe failed!\n"); + kfree(reprobe); + module_put(THIS_MODULE); +} + +static void iwl_mvm_fw_error_dump_wk(struct work_struct *work) +{ + struct iwl_mvm *mvm = + container_of(work, struct iwl_mvm, fw_dump_wk.work); + + if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT)) + return; + + mutex_lock(&mvm->mutex); + + /* stop recording */ + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { + iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); + } else { + iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0); + /* wait before we collect the data till the DBGC stop */ + udelay(100); + } + + iwl_mvm_fw_error_dump(mvm); + + /* start recording again if the firmware is not crashed */ + WARN_ON_ONCE((!test_bit(STATUS_FW_ERROR, &mvm->trans->status)) && + mvm->fw->dbg_dest_tlv && + iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf)); + + mutex_unlock(&mvm->mutex); + + iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT); +} + +void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) +{ + iwl_abort_notification_waits(&mvm->notif_wait); + + /* + * This is a bit racy, but worst case we tell mac80211 about + * a stopped/aborted scan when that was already done which + * is not a problem. It is necessary to abort any os scan + * here because mac80211 requires having the scan cleared + * before restarting. + * We'll reset the scan_status to NONE in restart cleanup in + * the next start() call from mac80211. If restart isn't called + * (no fw restart) scan status will stay busy. + */ + iwl_mvm_report_scan_aborted(mvm); + + /* + * If we're restarting already, don't cycle restarts. + * If INIT fw asserted, it will likely fail again. + * If WoWLAN fw asserted, don't restart either, mac80211 + * can't recover this since we're already half suspended. + */ + if (!mvm->restart_fw && fw_error) { + iwl_mvm_fw_dbg_collect_desc(mvm, &iwl_mvm_dump_desc_assert, + NULL); + } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, + &mvm->status)) { + struct iwl_mvm_reprobe *reprobe; + + IWL_ERR(mvm, + "Firmware error during reconfiguration - reprobe!\n"); + + /* + * get a module reference to avoid doing this while unloading + * anyway and to avoid scheduling a work with code that's + * being removed. + */ + if (!try_module_get(THIS_MODULE)) { + IWL_ERR(mvm, "Module is being unloaded - abort\n"); + return; + } + + reprobe = kzalloc(sizeof(*reprobe), GFP_ATOMIC); + if (!reprobe) { + module_put(THIS_MODULE); + return; + } + reprobe->dev = mvm->trans->dev; + INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); + schedule_work(&reprobe->work); + } else if (mvm->cur_ucode == IWL_UCODE_REGULAR) { + /* don't let the transport/FW power down */ + iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); + + if (fw_error && mvm->restart_fw > 0) + mvm->restart_fw--; + ieee80211_restart_hw(mvm->hw); + } +} + +static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + iwl_mvm_dump_nic_error_log(mvm); + + iwl_mvm_nic_restart(mvm, true); +} + +static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + WARN_ON(1); + iwl_mvm_nic_restart(mvm, true); +} + +struct iwl_d0i3_iter_data { + struct iwl_mvm *mvm; + u8 ap_sta_id; + u8 vif_count; + u8 offloading_tid; + bool disable_offloading; +}; + +static bool iwl_mvm_disallow_offloading(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_d0i3_iter_data *iter_data) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct ieee80211_sta *ap_sta; + struct iwl_mvm_sta *mvmsta; + u32 available_tids = 0; + u8 tid; + + if (WARN_ON(vif->type != NL80211_IFTYPE_STATION || + mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)) + return false; + + ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id]); + if (IS_ERR_OR_NULL(ap_sta)) + return false; + + mvmsta = iwl_mvm_sta_from_mac80211(ap_sta); + spin_lock_bh(&mvmsta->lock); + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + + /* + * in case of pending tx packets, don't use this tid + * for offloading in order to prevent reuse of the same + * qos seq counters. + */ + if (iwl_mvm_tid_queued(tid_data)) + continue; + + if (tid_data->state != IWL_AGG_OFF) + continue; + + available_tids |= BIT(tid); + } + spin_unlock_bh(&mvmsta->lock); + + /* + * disallow protocol offloading if we have no available tid + * (with no pending frames and no active aggregation, + * as we don't handle "holes" properly - the scheduler needs the + * frame's seq number and TFD index to match) + */ + if (!available_tids) + return true; + + /* for simplicity, just use the first available tid */ + iter_data->offloading_tid = ffs(available_tids) - 1; + return false; +} + +static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_d0i3_iter_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; + + IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr); + if (vif->type != NL80211_IFTYPE_STATION || + !vif->bss_conf.assoc) + return; + + /* + * in case of pending tx packets or active aggregations, + * avoid offloading features in order to prevent reuse of + * the same qos seq counters. + */ + if (iwl_mvm_disallow_offloading(mvm, vif, data)) + data->disable_offloading = true; + + iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags); + iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading, flags); + + /* + * on init/association, mvm already configures POWER_TABLE_CMD + * and REPLY_MCAST_FILTER_CMD, so currently don't + * reconfigure them (we might want to use different + * params later on, though). + */ + data->ap_sta_id = mvmvif->ap_sta_id; + data->vif_count++; +} + +static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, + struct iwl_wowlan_config_cmd *cmd, + struct iwl_d0i3_iter_data *iter_data) +{ + struct ieee80211_sta *ap_sta; + struct iwl_mvm_sta *mvm_ap_sta; + + if (iter_data->ap_sta_id == IWL_MVM_STATION_COUNT) + return; + + rcu_read_lock(); + + ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[iter_data->ap_sta_id]); + if (IS_ERR_OR_NULL(ap_sta)) + goto out; + + mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); + cmd->is_11n_connection = ap_sta->ht_cap.ht_supported; + cmd->offloading_tid = iter_data->offloading_tid; + + /* + * The d0i3 uCode takes care of the nonqos counters, + * so configure only the qos seq ones. + */ + iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, cmd); +out: + rcu_read_unlock(); +} + +int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; + int ret; + struct iwl_d0i3_iter_data d0i3_iter_data = { + .mvm = mvm, + }; + struct iwl_wowlan_config_cmd wowlan_config_cmd = { + .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME | + IWL_WOWLAN_WAKEUP_BEACON_MISS | + IWL_WOWLAN_WAKEUP_LINK_CHANGE | + IWL_WOWLAN_WAKEUP_BCN_FILTERING), + }; + struct iwl_d3_manager_config d3_cfg_cmd = { + .min_sleep_time = cpu_to_le32(1000), + .wakeup_flags = cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR), + }; + + IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n"); + + set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); + + /* + * iwl_mvm_ref_sync takes a reference before checking the flag. + * so by checking there is no held reference we prevent a state + * in which iwl_mvm_ref_sync continues successfully while we + * configure the firmware to enter d0i3 + */ + if (iwl_mvm_ref_taken(mvm)) { + IWL_DEBUG_RPM(mvm->trans, "abort d0i3 due to taken ref\n"); + clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); + wake_up(&mvm->d0i3_exit_waitq); + return 1; + } + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_enter_d0i3_iterator, + &d0i3_iter_data); + if (d0i3_iter_data.vif_count == 1) { + mvm->d0i3_ap_sta_id = d0i3_iter_data.ap_sta_id; + mvm->d0i3_offloading = !d0i3_iter_data.disable_offloading; + } else { + WARN_ON_ONCE(d0i3_iter_data.vif_count > 1); + mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + mvm->d0i3_offloading = false; + } + + /* make sure we have no running tx while configuring the seqno */ + synchronize_net(); + + /* configure wowlan configuration only if needed */ + if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) { + iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, + &d0i3_iter_data); + + ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags, + sizeof(wowlan_config_cmd), + &wowlan_config_cmd); + if (ret) + return ret; + } + + return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, + flags | CMD_MAKE_TRANS_IDLE, + sizeof(d3_cfg_cmd), &d3_cfg_cmd); +} + +static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = _data; + u32 flags = CMD_ASYNC | CMD_HIGH_PRIO; + + IWL_DEBUG_RPM(mvm, "exiting D0i3 - vif %pM\n", vif->addr); + if (vif->type != NL80211_IFTYPE_STATION || + !vif->bss_conf.assoc) + return; + + iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags); +} + +struct iwl_mvm_wakeup_reason_iter_data { + struct iwl_mvm *mvm; + u32 wakeup_reasons; +}; + +static void iwl_mvm_d0i3_wakeup_reason_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_wakeup_reason_iter_data *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc && + data->mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) { + if (data->wakeup_reasons & + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH) + iwl_mvm_connection_loss(data->mvm, vif, "D0i3"); + else + ieee80211_beacon_loss(vif); + } +} + +void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq) +{ + struct ieee80211_sta *sta = NULL; + struct iwl_mvm_sta *mvm_ap_sta; + int i; + bool wake_queues = false; + + lockdep_assert_held(&mvm->mutex); + + spin_lock_bh(&mvm->d0i3_tx_lock); + + if (mvm->d0i3_ap_sta_id == IWL_MVM_STATION_COUNT) + goto out; + + IWL_DEBUG_RPM(mvm, "re-enqueue packets\n"); + + /* get the sta in order to update seq numbers and re-enqueue skbs */ + sta = rcu_dereference_protected( + mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id], + lockdep_is_held(&mvm->mutex)); + + if (IS_ERR_OR_NULL(sta)) { + sta = NULL; + goto out; + } + + if (mvm->d0i3_offloading && qos_seq) { + /* update qos seq numbers if offloading was enabled */ + mvm_ap_sta = iwl_mvm_sta_from_mac80211(sta); + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + u16 seq = le16_to_cpu(qos_seq[i]); + /* firmware stores last-used one, we store next one */ + seq += 0x10; + mvm_ap_sta->tid_data[i].seq_number = seq; + } + } +out: + /* re-enqueue (or drop) all packets */ + while (!skb_queue_empty(&mvm->d0i3_tx)) { + struct sk_buff *skb = __skb_dequeue(&mvm->d0i3_tx); + + if (!sta || iwl_mvm_tx_skb(mvm, skb, sta)) + ieee80211_free_txskb(mvm->hw, skb); + + /* if the skb_queue is not empty, we need to wake queues */ + wake_queues = true; + } + clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); + wake_up(&mvm->d0i3_exit_waitq); + mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + if (wake_queues) + ieee80211_wake_queues(mvm->hw); + + spin_unlock_bh(&mvm->d0i3_tx_lock); +} + +static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) +{ + struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work); + struct iwl_host_cmd get_status_cmd = { + .id = WOWLAN_GET_STATUSES, + .flags = CMD_HIGH_PRIO | CMD_WANT_SKB, + }; + struct iwl_wowlan_status *status; + int ret; + u32 handled_reasons, wakeup_reasons = 0; + __le16 *qos_seq = NULL; + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_send_cmd(mvm, &get_status_cmd); + if (ret) + goto out; + + if (!get_status_cmd.resp_pkt) + goto out; + + status = (void *)get_status_cmd.resp_pkt->data; + wakeup_reasons = le32_to_cpu(status->wakeup_reasons); + qos_seq = status->qos_seq_ctr; + + IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons); + + handled_reasons = IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; + if (wakeup_reasons & handled_reasons) { + struct iwl_mvm_wakeup_reason_iter_data data = { + .mvm = mvm, + .wakeup_reasons = wakeup_reasons, + }; + + ieee80211_iterate_active_interfaces( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_d0i3_wakeup_reason_iter, &data); + } +out: + iwl_mvm_d0i3_enable_tx(mvm, qos_seq); + + IWL_DEBUG_INFO(mvm, "d0i3 exit completed (wakeup reasons: 0x%x)\n", + wakeup_reasons); + + /* qos_seq might point inside resp_pkt, so free it only now */ + if (get_status_cmd.resp_pkt) + iwl_free_resp(&get_status_cmd); + + /* the FW might have updated the regdomain */ + iwl_mvm_update_changed_regdom(mvm); + + iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK); + mutex_unlock(&mvm->mutex); +} + +int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm) +{ + u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE | + CMD_WAKE_UP_TRANS; + int ret; + + IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n"); + + mutex_lock(&mvm->d0i3_suspend_mutex); + if (test_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags)) { + IWL_DEBUG_RPM(mvm, "Deferring d0i3 exit until resume\n"); + __set_bit(D0I3_PENDING_WAKEUP, &mvm->d0i3_suspend_flags); + mutex_unlock(&mvm->d0i3_suspend_mutex); + return 0; + } + mutex_unlock(&mvm->d0i3_suspend_mutex); + + ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL); + if (ret) + goto out; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_exit_d0i3_iterator, + mvm); +out: + schedule_work(&mvm->d0i3_exit_work); + return ret; +} + +int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + iwl_mvm_ref(mvm, IWL_MVM_REF_EXIT_WORK); + return _iwl_mvm_exit_d0i3(mvm); +} + +#define IWL_MVM_COMMON_OPS \ + /* these could be differentiated */ \ + .queue_full = iwl_mvm_stop_sw_queue, \ + .queue_not_full = iwl_mvm_wake_sw_queue, \ + .hw_rf_kill = iwl_mvm_set_hw_rfkill_state, \ + .free_skb = iwl_mvm_free_skb, \ + .nic_error = iwl_mvm_nic_error, \ + .cmd_queue_full = iwl_mvm_cmd_queue_full, \ + .nic_config = iwl_mvm_nic_config, \ + .enter_d0i3 = iwl_mvm_enter_d0i3, \ + .exit_d0i3 = iwl_mvm_exit_d0i3, \ + /* as we only register one, these MUST be common! */ \ + .start = iwl_op_mode_mvm_start, \ + .stop = iwl_op_mode_mvm_stop + +static const struct iwl_op_mode_ops iwl_mvm_ops = { + IWL_MVM_COMMON_OPS, + .rx = iwl_mvm_rx, +}; + +static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, + unsigned int queue) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); +} + +static const struct iwl_op_mode_ops iwl_mvm_ops_mq = { + IWL_MVM_COMMON_OPS, + .rx = iwl_mvm_rx_mq, + .rx_rss = iwl_mvm_rx_mq_rss, +}; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c new file mode 100644 index 000000000000..e68a475e3071 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c @@ -0,0 +1,295 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include "fw-api.h" +#include "mvm.h" + +/* Maps the driver specific channel width definition to the fw values */ +u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef) +{ + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + return PHY_VHT_CHANNEL_MODE20; + case NL80211_CHAN_WIDTH_40: + return PHY_VHT_CHANNEL_MODE40; + case NL80211_CHAN_WIDTH_80: + return PHY_VHT_CHANNEL_MODE80; + case NL80211_CHAN_WIDTH_160: + return PHY_VHT_CHANNEL_MODE160; + default: + WARN(1, "Invalid channel width=%u", chandef->width); + return PHY_VHT_CHANNEL_MODE20; + } +} + +/* + * Maps the driver specific control channel position (relative to the center + * freq) definitions to the the fw values + */ +u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef) +{ + switch (chandef->chan->center_freq - chandef->center_freq1) { + case -70: + return PHY_VHT_CTRL_POS_4_BELOW; + case -50: + return PHY_VHT_CTRL_POS_3_BELOW; + case -30: + return PHY_VHT_CTRL_POS_2_BELOW; + case -10: + return PHY_VHT_CTRL_POS_1_BELOW; + case 10: + return PHY_VHT_CTRL_POS_1_ABOVE; + case 30: + return PHY_VHT_CTRL_POS_2_ABOVE; + case 50: + return PHY_VHT_CTRL_POS_3_ABOVE; + case 70: + return PHY_VHT_CTRL_POS_4_ABOVE; + default: + WARN(1, "Invalid channel definition"); + case 0: + /* + * The FW is expected to check the control channel position only + * when in HT/VHT and the channel width is not 20MHz. Return + * this value as the default one. + */ + return PHY_VHT_CTRL_POS_1_BELOW; + } +} + +/* + * Construct the generic fields of the PHY context command + */ +static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt, + struct iwl_phy_context_cmd *cmd, + u32 action, u32 apply_time) +{ + memset(cmd, 0, sizeof(struct iwl_phy_context_cmd)); + + cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id, + ctxt->color)); + cmd->action = cpu_to_le32(action); + cmd->apply_time = cpu_to_le32(apply_time); +} + +/* + * Add the phy configuration to the PHY context command + */ +static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm, + struct iwl_phy_context_cmd *cmd, + struct cfg80211_chan_def *chandef, + u8 chains_static, u8 chains_dynamic) +{ + u8 active_cnt, idle_cnt; + + /* Set the channel info data */ + cmd->ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ? + PHY_BAND_24 : PHY_BAND_5); + + cmd->ci.channel = chandef->chan->hw_value; + cmd->ci.width = iwl_mvm_get_channel_width(chandef); + cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef); + + /* Set rx the chains */ + idle_cnt = chains_static; + active_cnt = chains_dynamic; + + /* In scenarios where we only ever use a single-stream rates, + * i.e. legacy 11b/g/a associations, single-stream APs or even + * static SMPS, enable both chains to get diversity, improving + * the case where we're far enough from the AP that attenuation + * between the two antennas is sufficiently different to impact + * performance. + */ + if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) { + idle_cnt = 2; + active_cnt = 2; + } + + cmd->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) << + PHY_RX_CHAIN_VALID_POS); + cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS); + cmd->rxchain_info |= cpu_to_le32(active_cnt << + PHY_RX_CHAIN_MIMO_CNT_POS); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (unlikely(mvm->dbgfs_rx_phyinfo)) + cmd->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo); +#endif + + cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); +} + +/* + * Send a command to apply the current phy configuration. The command is send + * only if something in the configuration changed: in case that this is the + * first time that the phy configuration is applied or in case that the phy + * configuration changed from the previous apply. + */ +static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, + struct iwl_mvm_phy_ctxt *ctxt, + struct cfg80211_chan_def *chandef, + u8 chains_static, u8 chains_dynamic, + u32 action, u32 apply_time) +{ + struct iwl_phy_context_cmd cmd; + int ret; + + /* Set the command header fields */ + iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time); + + /* Set the command data */ + iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef, + chains_static, chains_dynamic); + + ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0, + sizeof(struct iwl_phy_context_cmd), + &cmd); + if (ret) + IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret); + return ret; +} + +/* + * Send a command to add a PHY context based on the current HW configuration. + */ +int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, + struct cfg80211_chan_def *chandef, + u8 chains_static, u8 chains_dynamic) +{ + WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + ctxt->ref); + lockdep_assert_held(&mvm->mutex); + + ctxt->channel = chandef->chan; + + return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, + chains_static, chains_dynamic, + FW_CTXT_ACTION_ADD, 0); +} + +/* + * Update the number of references to the given PHY context. This is valid only + * in case the PHY context was already created, i.e., its reference count > 0. + */ +void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) +{ + lockdep_assert_held(&mvm->mutex); + ctxt->ref++; +} + +/* + * Send a command to modify the PHY context based on the current HW + * configuration. Note that the function does not check that the configuration + * changed. + */ +int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, + struct cfg80211_chan_def *chandef, + u8 chains_static, u8 chains_dynamic) +{ + lockdep_assert_held(&mvm->mutex); + + ctxt->channel = chandef->chan; + return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, + chains_static, chains_dynamic, + FW_CTXT_ACTION_MODIFY, 0); +} + +void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) +{ + lockdep_assert_held(&mvm->mutex); + + if (WARN_ON_ONCE(!ctxt)) + return; + + ctxt->ref--; +} + +static void iwl_mvm_binding_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + unsigned long *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (!mvmvif->phy_ctxt) + return; + + if (vif->type == NL80211_IFTYPE_STATION || + vif->type == NL80211_IFTYPE_AP) + __set_bit(mvmvif->phy_ctxt->id, data); +} + +int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm) +{ + unsigned long phy_ctxt_counter = 0; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_binding_iterator, + &phy_ctxt_counter); + + return hweight8(phy_ctxt_counter); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c new file mode 100644 index 000000000000..bed9696ee410 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -0,0 +1,1040 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include +#include + +#include + +#include "iwl-debug.h" +#include "mvm.h" +#include "iwl-modparams.h" +#include "fw-api-power.h" + +#define POWER_KEEP_ALIVE_PERIOD_SEC 25 + +static +int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, + struct iwl_beacon_filter_cmd *cmd, + u32 flags) +{ + IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", + le32_to_cpu(cmd->ba_enable_beacon_abort)); + IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", + le32_to_cpu(cmd->ba_escape_timer)); + IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", + le32_to_cpu(cmd->bf_debug_flag)); + IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", + le32_to_cpu(cmd->bf_enable_beacon_filter)); + IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", + le32_to_cpu(cmd->bf_energy_delta)); + IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", + le32_to_cpu(cmd->bf_escape_timer)); + IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", + le32_to_cpu(cmd->bf_roaming_energy_delta)); + IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", + le32_to_cpu(cmd->bf_roaming_state)); + IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n", + le32_to_cpu(cmd->bf_temp_threshold)); + IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n", + le32_to_cpu(cmd->bf_temp_fast_filter)); + IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n", + le32_to_cpu(cmd->bf_temp_slow_filter)); + + return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags, + sizeof(struct iwl_beacon_filter_cmd), cmd); +} + +static +void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd, + bool d0i3) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (vif->bss_conf.cqm_rssi_thold && !d0i3) { + cmd->bf_energy_delta = + cpu_to_le32(vif->bss_conf.cqm_rssi_hyst); + /* fw uses an absolute value for this */ + cmd->bf_roaming_state = + cpu_to_le32(-vif->bss_conf.cqm_rssi_thold); + } + cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled); +} + +static void iwl_mvm_power_log(struct iwl_mvm *mvm, + struct iwl_mac_power_cmd *cmd) +{ + IWL_DEBUG_POWER(mvm, + "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n", + cmd->id_and_color, iwlmvm_mod_params.power_scheme, + le16_to_cpu(cmd->flags)); + IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", + le16_to_cpu(cmd->keep_alive_seconds)); + + if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) { + IWL_DEBUG_POWER(mvm, "Disable power management\n"); + return; + } + + IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n", + le32_to_cpu(cmd->rx_data_timeout)); + IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n", + le32_to_cpu(cmd->tx_data_timeout)); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) + IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", + cmd->skip_dtim_periods); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) + IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", + cmd->lprx_rssi_threshold); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { + IWL_DEBUG_POWER(mvm, "uAPSD enabled\n"); + IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n", + le32_to_cpu(cmd->rx_data_timeout_uapsd)); + IWL_DEBUG_POWER(mvm, "Tx timeout (uAPSD) = %u usec\n", + le32_to_cpu(cmd->tx_data_timeout_uapsd)); + IWL_DEBUG_POWER(mvm, "QNDP TID = %d\n", cmd->qndp_tid); + IWL_DEBUG_POWER(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags); + IWL_DEBUG_POWER(mvm, "Max SP = %d\n", cmd->uapsd_max_sp); + } +} + +static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_power_cmd *cmd) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + enum ieee80211_ac_numbers ac; + bool tid_found = false; + + for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) { + if (!mvmvif->queue_params[ac].uapsd) + continue; + + if (mvm->cur_ucode != IWL_UCODE_WOWLAN) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); + + cmd->uapsd_ac_flags |= BIT(ac); + + /* QNDP TID - the highest TID with no admission control */ + if (!tid_found && !mvmvif->queue_params[ac].acm) { + tid_found = true; + switch (ac) { + case IEEE80211_AC_VO: + cmd->qndp_tid = 6; + break; + case IEEE80211_AC_VI: + cmd->qndp_tid = 5; + break; + case IEEE80211_AC_BE: + cmd->qndp_tid = 0; + break; + case IEEE80211_AC_BK: + cmd->qndp_tid = 1; + break; + } + } + } + + if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* set advanced pm flag with no uapsd ACs to enable ps-poll */ + if (mvmvif->dbgfs_pm.use_ps_poll) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); +#endif + return; + } + + cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK); + + if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | + BIT(IEEE80211_AC_VI) | + BIT(IEEE80211_AC_BE) | + BIT(IEEE80211_AC_BK))) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); + cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL); + cmd->snooze_window = (mvm->cur_ucode == IWL_UCODE_WOWLAN) ? + cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) : + cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW); + } + + cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP; + + if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags & + cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { + cmd->rx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); + } else { + cmd->rx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT); + cmd->tx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT); + } + + if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { + cmd->heavy_tx_thld_packets = + IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS; + cmd->heavy_rx_thld_packets = + IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS; + } else { + cmd->heavy_tx_thld_packets = + IWL_MVM_PS_HEAVY_TX_THLD_PACKETS; + cmd->heavy_rx_thld_packets = + IWL_MVM_PS_HEAVY_RX_THLD_PACKETS; + } + cmd->heavy_tx_thld_percentage = + IWL_MVM_PS_HEAVY_TX_THLD_PERCENT; + cmd->heavy_rx_thld_percentage = + IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; +} + +static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, + ETH_ALEN)) + return false; + + if (vif->p2p && + !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD)) + return false; + /* + * Avoid using uAPSD if P2P client is associated to GO that uses + * opportunistic power save. This is due to current FW limitation. + */ + if (vif->p2p && + (vif->bss_conf.p2p_noa_attr.oppps_ctwindow & + IEEE80211_P2P_OPPPS_ENABLE_BIT)) + return false; + + /* + * Avoid using uAPSD if client is in DCM - + * low latency issue in Miracast + */ + if (iwl_mvm_phy_ctx_count(mvm) >= 2) + return false; + + return true; +} + +static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif) +{ + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *chan; + bool radar_detect = false; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + WARN_ON(!chanctx_conf); + if (chanctx_conf) { + chan = chanctx_conf->def.chan; + radar_detect = chan->flags & IEEE80211_CHAN_RADAR; + } + rcu_read_unlock(); + + return radar_detect; +} + +static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_power_cmd *cmd, + bool host_awake) +{ + int dtimper = vif->bss_conf.dtim_period ?: 1; + int skip; + + /* disable, in case we're supposed to override */ + cmd->skip_dtim_periods = 0; + cmd->flags &= ~cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + + if (iwl_mvm_power_is_radar(vif)) + return; + + if (dtimper >= 10) + return; + + /* TODO: check that multicast wake lock is off */ + + if (host_awake) { + if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_LP) + return; + skip = 2; + } else { + int dtimper_tu = dtimper * vif->bss_conf.beacon_int; + + if (WARN_ON(!dtimper_tu)) + return; + /* configure skip over dtim up to 306TU - 314 msec */ + skip = max_t(u8, 1, 306 / dtimper_tu); + } + + /* the firmware really expects "look at every X DTIMs", so add 1 */ + cmd->skip_dtim_periods = 1 + skip; + cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); +} + +static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_power_cmd *cmd, + bool host_awake) +{ + int dtimper, bi; + int keep_alive; + struct iwl_mvm_vif *mvmvif __maybe_unused = + iwl_mvm_vif_from_mac80211(vif); + + cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)); + dtimper = vif->bss_conf.dtim_period; + bi = vif->bss_conf.beacon_int; + + /* + * Regardless of power management state the driver must set + * keep alive period. FW will use it for sending keep alive NDPs + * immediately after association. Check that keep alive period + * is at least 3 * DTIM + */ + keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi), + USEC_PER_SEC); + keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC); + cmd->keep_alive_seconds = cpu_to_le16(keep_alive); + + if (mvm->ps_disabled) + return; + + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); + + if (!vif->bss_conf.ps || !mvmvif->pm_enabled) + return; + + if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p && + (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS) || + !IWL_MVM_P2P_LOWLATENCY_PS_ENABLE)) + return; + + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); + + if (vif->bss_conf.beacon_rate && + (vif->bss_conf.beacon_rate->bitrate == 10 || + vif->bss_conf.beacon_rate->bitrate == 60)) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); + cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD; + } + + iwl_mvm_power_config_skip_dtim(mvm, vif, cmd, host_awake); + + if (!host_awake) { + cmd->rx_data_timeout = + cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout = + cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); + } else if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p && + fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS)) { + cmd->tx_data_timeout = + cpu_to_le32(IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT); + cmd->rx_data_timeout = + cpu_to_le32(IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT); + } else { + cmd->rx_data_timeout = + cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout = + cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT); + } + + if (iwl_mvm_power_allow_uapsd(mvm, vif)) + iwl_mvm_power_configure_uapsd(mvm, vif, cmd); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) + cmd->keep_alive_seconds = + cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) { + if (mvmvif->dbgfs_pm.skip_over_dtim) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + else + cmd->flags &= + cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK); + } + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT) + cmd->rx_data_timeout = + cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT) + cmd->tx_data_timeout = + cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS) + cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods; + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) { + if (mvmvif->dbgfs_pm.lprx_ena) + cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); + else + cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK); + } + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD) + cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold; + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) { + if (mvmvif->dbgfs_pm.snooze_ena) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); + else + cmd->flags &= + cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK); + } + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) { + u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK; + if (mvmvif->dbgfs_pm.uapsd_misbehaving) + cmd->flags |= cpu_to_le16(flag); + else + cmd->flags &= cpu_to_le16(flag); + } +#endif /* CONFIG_IWLWIFI_DEBUGFS */ +} + +static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mac_power_cmd cmd = {}; + + iwl_mvm_power_build_cmd(mvm, vif, &cmd, + mvm->cur_ucode != IWL_UCODE_WOWLAN); + iwl_mvm_power_log(mvm, &cmd); +#ifdef CONFIG_IWLWIFI_DEBUGFS + memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd)); +#endif + + return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0, + sizeof(cmd), &cmd); +} + +int iwl_mvm_power_update_device(struct iwl_mvm *mvm) +{ + struct iwl_device_power_cmd cmd = { + .flags = 0, + }; + + if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) + mvm->ps_disabled = true; + + if (!mvm->ps_disabled) + cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 : + mvm->disable_power_off) + cmd.flags &= + cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); +#endif + IWL_DEBUG_POWER(mvm, + "Sending device power command with flags = 0x%X\n", + cmd.flags); + + return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd), + &cmd); +} + +void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid, + ETH_ALEN)) + eth_zero_addr(mvmvif->uapsd_misbehaving_bssid); +} + +static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + u8 *ap_sta_id = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + /* The ap_sta_id is not expected to change during current association + * so no explicit protection is needed + */ + if (mvmvif->ap_sta_id == *ap_sta_id) + memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, + ETH_ALEN); +} + +void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data; + u8 ap_sta_id = le32_to_cpu(notif->sta_id); + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id); +} + +struct iwl_power_vifs { + struct iwl_mvm *mvm; + struct ieee80211_vif *bf_vif; + struct ieee80211_vif *bss_vif; + struct ieee80211_vif *p2p_vif; + struct ieee80211_vif *ap_vif; + struct ieee80211_vif *monitor_vif; + bool p2p_active; + bool bss_active; + bool ap_active; + bool monitor_active; +}; + +static void iwl_mvm_power_disable_pm_iterator(void *_data, u8* mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + mvmvif->pm_enabled = false; +} + +static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8* mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + bool *disable_ps = _data; + + if (mvmvif->phy_ctxt) + if (mvmvif->phy_ctxt->id < MAX_PHYS) + *disable_ps |= mvmvif->ps_disabled; +} + +static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_power_vifs *power_iterator = _data; + + switch (ieee80211_vif_type_p2p(vif)) { + case NL80211_IFTYPE_P2P_DEVICE: + break; + + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_AP: + /* only a single MAC of the same type */ + WARN_ON(power_iterator->ap_vif); + power_iterator->ap_vif = vif; + if (mvmvif->phy_ctxt) + if (mvmvif->phy_ctxt->id < MAX_PHYS) + power_iterator->ap_active = true; + break; + + case NL80211_IFTYPE_MONITOR: + /* only a single MAC of the same type */ + WARN_ON(power_iterator->monitor_vif); + power_iterator->monitor_vif = vif; + if (mvmvif->phy_ctxt) + if (mvmvif->phy_ctxt->id < MAX_PHYS) + power_iterator->monitor_active = true; + break; + + case NL80211_IFTYPE_P2P_CLIENT: + /* only a single MAC of the same type */ + WARN_ON(power_iterator->p2p_vif); + power_iterator->p2p_vif = vif; + if (mvmvif->phy_ctxt) + if (mvmvif->phy_ctxt->id < MAX_PHYS) + power_iterator->p2p_active = true; + break; + + case NL80211_IFTYPE_STATION: + /* only a single MAC of the same type */ + WARN_ON(power_iterator->bss_vif); + power_iterator->bss_vif = vif; + if (mvmvif->phy_ctxt) + if (mvmvif->phy_ctxt->id < MAX_PHYS) + power_iterator->bss_active = true; + + if (mvmvif->bf_data.bf_enabled && + !WARN_ON(power_iterator->bf_vif)) + power_iterator->bf_vif = vif; + + break; + + default: + break; + } +} + +static void iwl_mvm_power_set_pm(struct iwl_mvm *mvm, + struct iwl_power_vifs *vifs) +{ + struct iwl_mvm_vif *bss_mvmvif = NULL; + struct iwl_mvm_vif *p2p_mvmvif = NULL; + struct iwl_mvm_vif *ap_mvmvif = NULL; + bool client_same_channel = false; + bool ap_same_channel = false; + + lockdep_assert_held(&mvm->mutex); + + /* set pm_enable to false */ + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_disable_pm_iterator, + NULL); + + if (vifs->bss_vif) + bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif); + + if (vifs->p2p_vif) + p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif); + + if (vifs->ap_vif) + ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif); + + /* don't allow PM if any TDLS stations exist */ + if (iwl_mvm_tdls_sta_count(mvm, NULL)) + return; + + /* enable PM on bss if bss stand alone */ + if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) { + bss_mvmvif->pm_enabled = true; + return; + } + + /* enable PM on p2p if p2p stand alone */ + if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) { + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM) + p2p_mvmvif->pm_enabled = true; + return; + } + + if (vifs->bss_active && vifs->p2p_active) + client_same_channel = (bss_mvmvif->phy_ctxt->id == + p2p_mvmvif->phy_ctxt->id); + if (vifs->bss_active && vifs->ap_active) + ap_same_channel = (bss_mvmvif->phy_ctxt->id == + ap_mvmvif->phy_ctxt->id); + + /* clients are not stand alone: enable PM if DCM */ + if (!(client_same_channel || ap_same_channel) && + (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) { + if (vifs->bss_active) + bss_mvmvif->pm_enabled = true; + if (vifs->p2p_active && + (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)) + p2p_mvmvif->pm_enabled = true; + return; + } + + /* + * There is only one channel in the system and there are only + * bss and p2p clients that share it + */ + if (client_same_channel && !vifs->ap_active && + (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM)) { + /* share same channel*/ + bss_mvmvif->pm_enabled = true; + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM) + p2p_mvmvif->pm_enabled = true; + } +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, char *buf, + int bufsz) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_power_cmd cmd = {}; + int pos = 0; + + mutex_lock(&mvm->mutex); + memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd)); + mutex_unlock(&mvm->mutex); + + pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", + iwlmvm_mod_params.power_scheme); + pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n", + le16_to_cpu(cmd.flags)); + pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", + le16_to_cpu(cmd.keep_alive_seconds)); + + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) + return pos; + + pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0); + pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", + cmd.skip_dtim_periods); + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { + pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", + le32_to_cpu(cmd.rx_data_timeout)); + pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", + le32_to_cpu(cmd.tx_data_timeout)); + } + if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) + pos += scnprintf(buf+pos, bufsz-pos, + "lprx_rssi_threshold = %d\n", + cmd.lprx_rssi_threshold); + + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) + return pos; + + pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n", + le32_to_cpu(cmd.rx_data_timeout_uapsd)); + pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n", + le32_to_cpu(cmd.tx_data_timeout_uapsd)); + pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid); + pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n", + cmd.uapsd_ac_flags); + pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n", + cmd.uapsd_max_sp); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n", + cmd.heavy_tx_thld_packets); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n", + cmd.heavy_rx_thld_packets); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n", + cmd.heavy_tx_thld_percentage); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n", + cmd.heavy_rx_thld_percentage); + pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ? + 1 : 0); + + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK))) + return pos; + + pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n", + cmd.snooze_interval); + pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n", + cmd.snooze_window); + + return pos; +} + +void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; + + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA) + cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta); + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA) + cmd->bf_roaming_energy_delta = + cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta); + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE) + cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state); + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD) + cmd->bf_temp_threshold = + cpu_to_le32(dbgfs_bf->bf_temp_threshold); + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER) + cmd->bf_temp_fast_filter = + cpu_to_le32(dbgfs_bf->bf_temp_fast_filter); + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER) + cmd->bf_temp_slow_filter = + cpu_to_le32(dbgfs_bf->bf_temp_slow_filter); + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG) + cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag); + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER) + cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer); + if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER) + cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer); + if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT) + cmd->ba_enable_beacon_abort = + cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort); +} +#endif + +static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd, + u32 cmd_flags, + bool d0i3) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period || + vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd, d0i3); + if (!d0i3) + iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd); + ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags); + + /* don't change bf_enabled in case of temporary d0i3 configuration */ + if (!ret && !d0i3) + mvmvif->bf_data.bf_enabled = true; + + return ret; +} + +int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 flags) +{ + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = cpu_to_le32(1), + }; + + return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false); +} + +static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = cpu_to_le32(1), + }; + + if (!mvmvif->bf_data.bf_enabled) + return 0; + + if (mvm->cur_ucode == IWL_UCODE_WOWLAN) + cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); + + mvmvif->bf_data.ba_enabled = enable; + return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false); +} + +int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 flags) +{ + struct iwl_beacon_filter_cmd cmd = {}; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags); + + if (!ret) + mvmvif->bf_data.bf_enabled = false; + + return ret; +} + +static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm) +{ + bool disable_ps; + int ret; + + /* disable PS if CAM */ + disable_ps = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM); + /* ...or if any of the vifs require PS to be off */ + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_ps_disabled_iterator, + &disable_ps); + + /* update device power state if it has changed */ + if (mvm->ps_disabled != disable_ps) { + bool old_ps_disabled = mvm->ps_disabled; + + mvm->ps_disabled = disable_ps; + ret = iwl_mvm_power_update_device(mvm); + if (ret) { + mvm->ps_disabled = old_ps_disabled; + return ret; + } + } + + return 0; +} + +static int iwl_mvm_power_set_ba(struct iwl_mvm *mvm, + struct iwl_power_vifs *vifs) +{ + struct iwl_mvm_vif *mvmvif; + bool ba_enable; + + if (!vifs->bf_vif) + return 0; + + mvmvif = iwl_mvm_vif_from_mac80211(vifs->bf_vif); + + ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled || + !vifs->bf_vif->bss_conf.ps || + iwl_mvm_vif_low_latency(mvmvif)); + + return iwl_mvm_update_beacon_abort(mvm, vifs->bf_vif, ba_enable); +} + +int iwl_mvm_power_update_ps(struct iwl_mvm *mvm) +{ + struct iwl_power_vifs vifs = { + .mvm = mvm, + }; + int ret; + + lockdep_assert_held(&mvm->mutex); + + /* get vifs info */ + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_get_vifs_iterator, &vifs); + + ret = iwl_mvm_power_set_ps(mvm); + if (ret) + return ret; + + return iwl_mvm_power_set_ba(mvm, &vifs); +} + +int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) +{ + struct iwl_power_vifs vifs = { + .mvm = mvm, + }; + int ret; + + lockdep_assert_held(&mvm->mutex); + + /* get vifs info */ + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_get_vifs_iterator, &vifs); + + iwl_mvm_power_set_pm(mvm, &vifs); + + ret = iwl_mvm_power_set_ps(mvm); + if (ret) + return ret; + + if (vifs.bss_vif) { + ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif); + if (ret) + return ret; + } + + if (vifs.p2p_vif) { + ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif); + if (ret) + return ret; + } + + return iwl_mvm_power_set_ba(mvm, &vifs); +} + +int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable, u32 flags) +{ + int ret; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_power_cmd cmd = {}; + + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + if (!vif->bss_conf.assoc) + return 0; + + iwl_mvm_power_build_cmd(mvm, vif, &cmd, !enable); + + iwl_mvm_power_log(mvm, &cmd); +#ifdef CONFIG_IWLWIFI_DEBUGFS + memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd)); +#endif + ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, flags, + sizeof(cmd), &cmd); + if (ret) + return ret; + + /* configure beacon filtering */ + if (mvmvif != mvm->bf_allowed_vif) + return 0; + + if (enable) { + struct iwl_beacon_filter_cmd cmd_bf = { + IWL_BF_CMD_CONFIG_D0I3, + .bf_enable_beacon_filter = cpu_to_le32(1), + }; + ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf, + flags, true); + } else { + if (mvmvif->bf_data.bf_enabled) + ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags); + else + ret = iwl_mvm_disable_beacon_filter(mvm, vif, flags); + } + + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c new file mode 100644 index 000000000000..509a66d05245 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c @@ -0,0 +1,328 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include "fw-api.h" +#include "mvm.h" + +#define QUOTA_100 IWL_MVM_MAX_QUOTA +#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100) + +struct iwl_mvm_quota_iterator_data { + int n_interfaces[MAX_BINDINGS]; + int colors[MAX_BINDINGS]; + int low_latency[MAX_BINDINGS]; + int n_low_latency_bindings; + struct ieee80211_vif *disabled_vif; +}; + +static void iwl_mvm_quota_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_quota_iterator_data *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u16 id; + + /* skip disabled interfaces here immediately */ + if (vif == data->disabled_vif) + return; + + if (!mvmvif->phy_ctxt) + return; + + /* currently, PHY ID == binding ID */ + id = mvmvif->phy_ctxt->id; + + /* need at least one binding per PHY */ + BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS); + + if (WARN_ON_ONCE(id >= MAX_BINDINGS)) + return; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + if (vif->bss_conf.assoc) + break; + return; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + if (mvmvif->ap_ibss_active) + break; + return; + case NL80211_IFTYPE_MONITOR: + if (mvmvif->monitor_active) + break; + return; + case NL80211_IFTYPE_P2P_DEVICE: + return; + default: + WARN_ON_ONCE(1); + return; + } + + if (data->colors[id] < 0) + data->colors[id] = mvmvif->phy_ctxt->color; + else + WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color); + + data->n_interfaces[id]++; + + if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) { + data->n_low_latency_bindings++; + data->low_latency[id] = true; + } +} + +static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm, + struct iwl_time_quota_cmd *cmd) +{ +#ifdef CONFIG_NL80211_TESTMODE + struct iwl_mvm_vif *mvmvif; + int i, phy_id = -1, beacon_int = 0; + + if (!mvm->noa_duration || !mvm->noa_vif) + return; + + mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif); + if (!mvmvif->ap_ibss_active) + return; + + phy_id = mvmvif->phy_ctxt->id; + beacon_int = mvm->noa_vif->bss_conf.beacon_int; + + for (i = 0; i < MAX_BINDINGS; i++) { + u32 id_n_c = le32_to_cpu(cmd->quotas[i].id_and_color); + u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS; + u32 quota = le32_to_cpu(cmd->quotas[i].quota); + + if (id != phy_id) + continue; + + quota *= (beacon_int - mvm->noa_duration); + quota /= beacon_int; + + IWL_DEBUG_QUOTA(mvm, "quota: adjust for NoA from %d to %d\n", + le32_to_cpu(cmd->quotas[i].quota), quota); + + cmd->quotas[i].quota = cpu_to_le32(quota); + } +#endif +} + +int iwl_mvm_update_quotas(struct iwl_mvm *mvm, + bool force_update, + struct ieee80211_vif *disabled_vif) +{ + struct iwl_time_quota_cmd cmd = {}; + int i, idx, err, num_active_macs, quota, quota_rem, n_non_lowlat; + struct iwl_mvm_quota_iterator_data data = { + .n_interfaces = {}, + .colors = { -1, -1, -1, -1 }, + .disabled_vif = disabled_vif, + }; + struct iwl_time_quota_cmd *last = &mvm->last_quota_cmd; + bool send = false; + + lockdep_assert_held(&mvm->mutex); + + /* update all upon completion */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + return 0; + + /* iterator data above must match */ + BUILD_BUG_ON(MAX_BINDINGS != 4); + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_quota_iterator, &data); + + /* + * The FW's scheduling session consists of + * IWL_MVM_MAX_QUOTA fragments. Divide these fragments + * equally between all the bindings that require quota + */ + num_active_macs = 0; + for (i = 0; i < MAX_BINDINGS; i++) { + cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID); + num_active_macs += data.n_interfaces[i]; + } + + n_non_lowlat = num_active_macs; + + if (data.n_low_latency_bindings == 1) { + for (i = 0; i < MAX_BINDINGS; i++) { + if (data.low_latency[i]) { + n_non_lowlat -= data.n_interfaces[i]; + break; + } + } + } + + if (data.n_low_latency_bindings == 1 && n_non_lowlat) { + /* + * Reserve quota for the low latency binding in case that + * there are several data bindings but only a single + * low latency one. Split the rest of the quota equally + * between the other data interfaces. + */ + quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat; + quota_rem = QUOTA_100 - n_non_lowlat * quota - + QUOTA_LOWLAT_MIN; + IWL_DEBUG_QUOTA(mvm, + "quota: low-latency binding active, remaining quota per other binding: %d\n", + quota); + } else if (num_active_macs) { + /* + * There are 0 or more than 1 low latency bindings, or all the + * data interfaces belong to the single low latency binding. + * Split the quota equally between the data interfaces. + */ + quota = QUOTA_100 / num_active_macs; + quota_rem = QUOTA_100 % num_active_macs; + IWL_DEBUG_QUOTA(mvm, + "quota: splitting evenly per binding: %d\n", + quota); + } else { + /* values don't really matter - won't be used */ + quota = 0; + quota_rem = 0; + } + + for (idx = 0, i = 0; i < MAX_BINDINGS; i++) { + if (data.colors[i] < 0) + continue; + + cmd.quotas[idx].id_and_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i])); + + if (data.n_interfaces[i] <= 0) + cmd.quotas[idx].quota = cpu_to_le32(0); + else if (data.n_low_latency_bindings == 1 && n_non_lowlat && + data.low_latency[i]) + /* + * There is more than one binding, but only one of the + * bindings is in low latency. For this case, allocate + * the minimal required quota for the low latency + * binding. + */ + cmd.quotas[idx].quota = cpu_to_le32(QUOTA_LOWLAT_MIN); + else + cmd.quotas[idx].quota = + cpu_to_le32(quota * data.n_interfaces[i]); + + WARN_ONCE(le32_to_cpu(cmd.quotas[idx].quota) > QUOTA_100, + "Binding=%d, quota=%u > max=%u\n", + idx, le32_to_cpu(cmd.quotas[idx].quota), QUOTA_100); + + cmd.quotas[idx].max_duration = cpu_to_le32(0); + + idx++; + } + + /* Give the remainder of the session to the first data binding */ + for (i = 0; i < MAX_BINDINGS; i++) { + if (le32_to_cpu(cmd.quotas[i].quota) != 0) { + le32_add_cpu(&cmd.quotas[i].quota, quota_rem); + IWL_DEBUG_QUOTA(mvm, + "quota: giving remainder of %d to binding %d\n", + quota_rem, i); + break; + } + } + + iwl_mvm_adjust_quota_for_noa(mvm, &cmd); + + /* check that we have non-zero quota for all valid bindings */ + for (i = 0; i < MAX_BINDINGS; i++) { + if (cmd.quotas[i].id_and_color != last->quotas[i].id_and_color) + send = true; + if (cmd.quotas[i].max_duration != last->quotas[i].max_duration) + send = true; + if (abs((int)le32_to_cpu(cmd.quotas[i].quota) - + (int)le32_to_cpu(last->quotas[i].quota)) + > IWL_MVM_QUOTA_THRESHOLD) + send = true; + if (cmd.quotas[i].id_and_color == cpu_to_le32(FW_CTXT_INVALID)) + continue; + WARN_ONCE(cmd.quotas[i].quota == 0, + "zero quota on binding %d\n", i); + } + + if (!send && !force_update) { + /* don't send a practically unchanged command, the firmware has + * to re-initialize a lot of state and that can have an adverse + * impact on it + */ + return 0; + } + + err = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, sizeof(cmd), &cmd); + + if (err) + IWL_ERR(mvm, "Failed to send quota: %d\n", err); + else + mvm->last_quota_cmd = cmd; + return err; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c new file mode 100644 index 000000000000..d1ad10391b47 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -0,0 +1,3983 @@ +/****************************************************************************** + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "rs.h" +#include "fw-api.h" +#include "sta.h" +#include "iwl-op-mode.h" +#include "mvm.h" +#include "debugfs.h" + +#define RS_NAME "iwl-mvm-rs" + +#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ + +/* Calculations of success ratio are done in fixed point where 12800 is 100%. + * Use this macro when dealing with thresholds consts set as a percentage + */ +#define RS_PERCENT(x) (128 * x) + +static u8 rs_ht_to_legacy[] = { + [IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX, + [IWL_RATE_MCS_1_INDEX] = IWL_RATE_9M_INDEX, + [IWL_RATE_MCS_2_INDEX] = IWL_RATE_12M_INDEX, + [IWL_RATE_MCS_3_INDEX] = IWL_RATE_18M_INDEX, + [IWL_RATE_MCS_4_INDEX] = IWL_RATE_24M_INDEX, + [IWL_RATE_MCS_5_INDEX] = IWL_RATE_36M_INDEX, + [IWL_RATE_MCS_6_INDEX] = IWL_RATE_48M_INDEX, + [IWL_RATE_MCS_7_INDEX] = IWL_RATE_54M_INDEX, + [IWL_RATE_MCS_8_INDEX] = IWL_RATE_54M_INDEX, + [IWL_RATE_MCS_9_INDEX] = IWL_RATE_54M_INDEX, +}; + +static const u8 ant_toggle_lookup[] = { + [ANT_NONE] = ANT_NONE, + [ANT_A] = ANT_B, + [ANT_B] = ANT_C, + [ANT_AB] = ANT_BC, + [ANT_C] = ANT_A, + [ANT_AC] = ANT_AB, + [ANT_BC] = ANT_AC, + [ANT_ABC] = ANT_ABC, +}; + +#define IWL_DECLARE_RATE_INFO(r, s, rp, rn) \ + [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ + IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ + IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP,\ + IWL_RATE_##rp##M_INDEX, \ + IWL_RATE_##rn##M_INDEX } + +#define IWL_DECLARE_MCS_RATE(s) \ + [IWL_RATE_MCS_##s##_INDEX] = { IWL_RATE_INVM_PLCP, \ + IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ + IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP, \ + IWL_RATE_INVM_INDEX, \ + IWL_RATE_INVM_INDEX } + +/* + * Parameter order: + * rate, ht rate, prev rate, next rate + * + * If there isn't a valid next or previous rate then INV is used which + * maps to IWL_RATE_INVALID + * + */ +static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = { + IWL_DECLARE_RATE_INFO(1, INV, INV, 2), /* 1mbps */ + IWL_DECLARE_RATE_INFO(2, INV, 1, 5), /* 2mbps */ + IWL_DECLARE_RATE_INFO(5, INV, 2, 11), /*5.5mbps */ + IWL_DECLARE_RATE_INFO(11, INV, 9, 12), /* 11mbps */ + IWL_DECLARE_RATE_INFO(6, 0, 5, 11), /* 6mbps ; MCS 0 */ + IWL_DECLARE_RATE_INFO(9, INV, 6, 11), /* 9mbps */ + IWL_DECLARE_RATE_INFO(12, 1, 11, 18), /* 12mbps ; MCS 1 */ + IWL_DECLARE_RATE_INFO(18, 2, 12, 24), /* 18mbps ; MCS 2 */ + IWL_DECLARE_RATE_INFO(24, 3, 18, 36), /* 24mbps ; MCS 3 */ + IWL_DECLARE_RATE_INFO(36, 4, 24, 48), /* 36mbps ; MCS 4 */ + IWL_DECLARE_RATE_INFO(48, 5, 36, 54), /* 48mbps ; MCS 5 */ + IWL_DECLARE_RATE_INFO(54, 6, 48, INV), /* 54mbps ; MCS 6 */ + IWL_DECLARE_MCS_RATE(7), /* MCS 7 */ + IWL_DECLARE_MCS_RATE(8), /* MCS 8 */ + IWL_DECLARE_MCS_RATE(9), /* MCS 9 */ +}; + +enum rs_action { + RS_ACTION_STAY = 0, + RS_ACTION_DOWNSCALE = -1, + RS_ACTION_UPSCALE = 1, +}; + +enum rs_column_mode { + RS_INVALID = 0, + RS_LEGACY, + RS_SISO, + RS_MIMO2, +}; + +#define MAX_NEXT_COLUMNS 7 +#define MAX_COLUMN_CHECKS 3 + +struct rs_tx_column; + +typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct rs_rate *rate, + const struct rs_tx_column *next_col); + +struct rs_tx_column { + enum rs_column_mode mode; + u8 ant; + bool sgi; + enum rs_column next_columns[MAX_NEXT_COLUMNS]; + allow_column_func_t checks[MAX_COLUMN_CHECKS]; +}; + +static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct rs_rate *rate, + const struct rs_tx_column *next_col) +{ + return iwl_mvm_bt_coex_is_ant_avail(mvm, next_col->ant); +} + +static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct rs_rate *rate, + const struct rs_tx_column *next_col) +{ + struct iwl_mvm_sta *mvmsta; + struct iwl_mvm_vif *mvmvif; + + if (!sta->ht_cap.ht_supported) + return false; + + if (sta->smps_mode == IEEE80211_SMPS_STATIC) + return false; + + if (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) < 2) + return false; + + if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) + return false; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); + + if (mvm->nvm_data->sku_cap_mimo_disabled) + return false; + + return true; +} + +static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct rs_rate *rate, + const struct rs_tx_column *next_col) +{ + if (!sta->ht_cap.ht_supported) + return false; + + return true; +} + +static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct rs_rate *rate, + const struct rs_tx_column *next_col) +{ + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + + if (is_ht20(rate) && (ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + return true; + if (is_ht40(rate) && (ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + return true; + if (is_ht80(rate) && (vht_cap->cap & + IEEE80211_VHT_CAP_SHORT_GI_80)) + return true; + + return false; +} + +static const struct rs_tx_column rs_tx_columns[] = { + [RS_COLUMN_LEGACY_ANT_A] = { + .mode = RS_LEGACY, + .ant = ANT_A, + .next_columns = { + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_MIMO2, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_ant_allow, + }, + }, + [RS_COLUMN_LEGACY_ANT_B] = { + .mode = RS_LEGACY, + .ant = ANT_B, + .next_columns = { + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_MIMO2, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_ant_allow, + }, + }, + [RS_COLUMN_SISO_ANT_A] = { + .mode = RS_SISO, + .ant = ANT_A, + .next_columns = { + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_MIMO2, + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + rs_ant_allow, + }, + }, + [RS_COLUMN_SISO_ANT_B] = { + .mode = RS_SISO, + .ant = ANT_B, + .next_columns = { + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_MIMO2, + RS_COLUMN_SISO_ANT_B_SGI, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + rs_ant_allow, + }, + }, + [RS_COLUMN_SISO_ANT_A_SGI] = { + .mode = RS_SISO, + .ant = ANT_A, + .sgi = true, + .next_columns = { + RS_COLUMN_SISO_ANT_B_SGI, + RS_COLUMN_MIMO2_SGI, + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + rs_ant_allow, + rs_sgi_allow, + }, + }, + [RS_COLUMN_SISO_ANT_B_SGI] = { + .mode = RS_SISO, + .ant = ANT_B, + .sgi = true, + .next_columns = { + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_MIMO2_SGI, + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + rs_ant_allow, + rs_sgi_allow, + }, + }, + [RS_COLUMN_MIMO2] = { + .mode = RS_MIMO2, + .ant = ANT_AB, + .next_columns = { + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_MIMO2_SGI, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_mimo_allow, + }, + }, + [RS_COLUMN_MIMO2_SGI] = { + .mode = RS_MIMO2, + .ant = ANT_AB, + .sgi = true, + .next_columns = { + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_MIMO2, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_mimo_allow, + rs_sgi_allow, + }, + }, +}; + +static inline u8 rs_extract_rate(u32 rate_n_flags) +{ + /* also works for HT because bits 7:6 are zero there */ + return (u8)(rate_n_flags & RATE_LEGACY_RATE_MSK); +} + +static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) +{ + int idx = 0; + + if (rate_n_flags & RATE_MCS_HT_MSK) { + idx = rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK; + idx += IWL_RATE_MCS_0_INDEX; + + /* skip 9M not supported in HT*/ + if (idx >= IWL_RATE_9M_INDEX) + idx += 1; + if ((idx >= IWL_FIRST_HT_RATE) && (idx <= IWL_LAST_HT_RATE)) + return idx; + } else if (rate_n_flags & RATE_MCS_VHT_MSK) { + idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; + idx += IWL_RATE_MCS_0_INDEX; + + /* skip 9M not supported in VHT*/ + if (idx >= IWL_RATE_9M_INDEX) + idx++; + if ((idx >= IWL_FIRST_VHT_RATE) && (idx <= IWL_LAST_VHT_RATE)) + return idx; + } else { + /* legacy rate format, search for match in table */ + + u8 legacy_rate = rs_extract_rate(rate_n_flags); + for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) + if (iwl_rates[idx].plcp == legacy_rate) + return idx; + } + + return IWL_RATE_INVALID; +} + +static void rs_rate_scale_perform(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + int tid); +static void rs_fill_lq_cmd(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + const struct rs_rate *initial_rate); +static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); + +/** + * The following tables contain the expected throughput metrics for all rates + * + * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits + * + * where invalid entries are zeros. + * + * CCK rates are only valid in legacy table and will only be used in G + * (2.4 GHz) band. + */ + +static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = { + 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0 +}; + +/* Expected TpT tables. 4 indexes: + * 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI + */ +static const u16 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202, 216, 0}, + {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210, 225, 0}, + {0, 0, 0, 0, 49, 0, 97, 145, 192, 285, 375, 420, 464, 551, 0}, + {0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0}, +}; + +static const u16 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257, 269, 275}, + {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264, 275, 280}, + {0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828, 911, 1070, 1173}, + {0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284}, +}; + +static const u16 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 130, 0, 191, 223, 244, 273, 288, 294, 298, 305, 308}, + {0, 0, 0, 0, 138, 0, 200, 231, 251, 279, 293, 298, 302, 308, 312}, + {0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466}, + {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691}, +}; + +static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250, 261, 0}, + {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256, 267, 0}, + {0, 0, 0, 0, 98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0}, + {0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0}, +}; + +static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289, 296, 300}, + {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293, 300, 303}, + {0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053}, + {0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221}, +}; + +static const u16 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 182, 0, 240, 264, 278, 299, 308, 311, 313, 317, 319}, + {0, 0, 0, 0, 190, 0, 247, 269, 282, 302, 310, 313, 315, 319, 320}, + {0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219}, + {0, 0, 0, 0, 474, 0, 920, 1338, 1732, 2464, 3116, 3418, 3705, 4225, 4545}, +}; + +/* mbps, mcs */ +static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { + { "1", "BPSK DSSS"}, + { "2", "QPSK DSSS"}, + {"5.5", "BPSK CCK"}, + { "11", "QPSK CCK"}, + { "6", "BPSK 1/2"}, + { "9", "BPSK 1/2"}, + { "12", "QPSK 1/2"}, + { "18", "QPSK 3/4"}, + { "24", "16QAM 1/2"}, + { "36", "16QAM 3/4"}, + { "48", "64QAM 2/3"}, + { "54", "64QAM 3/4"}, + { "60", "64QAM 5/6"}, +}; + +#define MCS_INDEX_PER_STREAM (8) + +static const char *rs_pretty_ant(u8 ant) +{ + static const char * const ant_name[] = { + [ANT_NONE] = "None", + [ANT_A] = "A", + [ANT_B] = "B", + [ANT_AB] = "AB", + [ANT_C] = "C", + [ANT_AC] = "AC", + [ANT_BC] = "BC", + [ANT_ABC] = "ABC", + }; + + if (ant > ANT_ABC) + return "UNKNOWN"; + + return ant_name[ant]; +} + +static const char *rs_pretty_lq_type(enum iwl_table_type type) +{ + static const char * const lq_types[] = { + [LQ_NONE] = "NONE", + [LQ_LEGACY_A] = "LEGACY_A", + [LQ_LEGACY_G] = "LEGACY_G", + [LQ_HT_SISO] = "HT SISO", + [LQ_HT_MIMO2] = "HT MIMO", + [LQ_VHT_SISO] = "VHT SISO", + [LQ_VHT_MIMO2] = "VHT MIMO", + }; + + if (type < LQ_NONE || type >= LQ_MAX) + return "UNKNOWN"; + + return lq_types[type]; +} + +static char *rs_pretty_rate(const struct rs_rate *rate) +{ + static char buf[40]; + static const char * const legacy_rates[] = { + [IWL_RATE_1M_INDEX] = "1M", + [IWL_RATE_2M_INDEX] = "2M", + [IWL_RATE_5M_INDEX] = "5.5M", + [IWL_RATE_11M_INDEX] = "11M", + [IWL_RATE_6M_INDEX] = "6M", + [IWL_RATE_9M_INDEX] = "9M", + [IWL_RATE_12M_INDEX] = "12M", + [IWL_RATE_18M_INDEX] = "18M", + [IWL_RATE_24M_INDEX] = "24M", + [IWL_RATE_36M_INDEX] = "36M", + [IWL_RATE_48M_INDEX] = "48M", + [IWL_RATE_54M_INDEX] = "54M", + }; + static const char *const ht_vht_rates[] = { + [IWL_RATE_MCS_0_INDEX] = "MCS0", + [IWL_RATE_MCS_1_INDEX] = "MCS1", + [IWL_RATE_MCS_2_INDEX] = "MCS2", + [IWL_RATE_MCS_3_INDEX] = "MCS3", + [IWL_RATE_MCS_4_INDEX] = "MCS4", + [IWL_RATE_MCS_5_INDEX] = "MCS5", + [IWL_RATE_MCS_6_INDEX] = "MCS6", + [IWL_RATE_MCS_7_INDEX] = "MCS7", + [IWL_RATE_MCS_8_INDEX] = "MCS8", + [IWL_RATE_MCS_9_INDEX] = "MCS9", + }; + const char *rate_str; + + if (is_type_legacy(rate->type)) + rate_str = legacy_rates[rate->index]; + else if (is_type_ht(rate->type) || is_type_vht(rate->type)) + rate_str = ht_vht_rates[rate->index]; + else + rate_str = "BAD_RATE"; + + sprintf(buf, "(%s|%s|%s)", rs_pretty_lq_type(rate->type), + rs_pretty_ant(rate->ant), rate_str); + return buf; +} + +static inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate, + const char *prefix) +{ + IWL_DEBUG_RATE(mvm, + "%s: %s BW: %d SGI: %d LDPC: %d STBC: %d\n", + prefix, rs_pretty_rate(rate), rate->bw, + rate->sgi, rate->ldpc, rate->stbc); +} + +static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) +{ + window->data = 0; + window->success_counter = 0; + window->success_ratio = IWL_INVALID_VALUE; + window->counter = 0; + window->average_tpt = IWL_INVALID_VALUE; +} + +static void rs_rate_scale_clear_tbl_windows(struct iwl_mvm *mvm, + struct iwl_scale_tbl_info *tbl) +{ + int i; + + IWL_DEBUG_RATE(mvm, "Clearing up window stats\n"); + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&tbl->win[i]); + + for (i = 0; i < ARRAY_SIZE(tbl->tpc_win); i++) + rs_rate_scale_clear_window(&tbl->tpc_win[i]); +} + +static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) +{ + return (ant_type & valid_antenna) == ant_type; +} + +static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_data, u8 tid, + struct ieee80211_sta *sta) +{ + int ret = -EAGAIN; + + IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n", + sta->addr, tid); + ret = ieee80211_start_tx_ba_session(sta, tid, 5000); + if (ret == -EAGAIN) { + /* + * driver and mac80211 is out of sync + * this might be cause by reloading firmware + * stop the tx ba session here + */ + IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n", + tid); + ieee80211_stop_tx_ba_session(sta, tid); + } + return ret; +} + +static void rs_tl_turn_on_agg(struct iwl_mvm *mvm, u8 tid, + struct iwl_lq_sta *lq_data, + struct ieee80211_sta *sta) +{ + if (tid < IWL_MAX_TID_COUNT) + rs_tl_turn_on_agg_for_tid(mvm, lq_data, tid, sta); + else + IWL_ERR(mvm, "tid exceeds max TID count: %d/%d\n", + tid, IWL_MAX_TID_COUNT); +} + +static inline int get_num_of_ant_from_rate(u32 rate_n_flags) +{ + return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_C_MSK); +} + +/* + * Static function to get the expected throughput from an iwl_scale_tbl_info + * that wraps a NULL pointer check + */ +static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) +{ + if (tbl->expected_tpt) + return tbl->expected_tpt[rs_index]; + return 0; +} + +/** + * rs_collect_tx_data - Update the success/failure sliding window + * + * We keep a sliding window of the last 62 packets transmitted + * at this rate. window->data contains the bitmask of successful + * packets. + */ +static int _rs_collect_tx_data(struct iwl_mvm *mvm, + struct iwl_scale_tbl_info *tbl, + int scale_index, int attempts, int successes, + struct iwl_rate_scale_data *window) +{ + static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); + s32 fail_count, tpt; + + /* Get expected throughput */ + tpt = get_expected_tpt(tbl, scale_index); + + /* + * Keep track of only the latest 62 tx frame attempts in this rate's + * history window; anything older isn't really relevant any more. + * If we have filled up the sliding window, drop the oldest attempt; + * if the oldest attempt (highest bit in bitmap) shows "success", + * subtract "1" from the success counter (this is the main reason + * we keep these bitmaps!). + */ + while (attempts > 0) { + if (window->counter >= IWL_RATE_MAX_WINDOW) { + /* remove earliest */ + window->counter = IWL_RATE_MAX_WINDOW - 1; + + if (window->data & mask) { + window->data &= ~mask; + window->success_counter--; + } + } + + /* Increment frames-attempted counter */ + window->counter++; + + /* Shift bitmap by one frame to throw away oldest history */ + window->data <<= 1; + + /* Mark the most recent #successes attempts as successful */ + if (successes > 0) { + window->success_counter++; + window->data |= 0x1; + successes--; + } + + attempts--; + } + + /* Calculate current success ratio, avoid divide-by-0! */ + if (window->counter > 0) + window->success_ratio = 128 * (100 * window->success_counter) + / window->counter; + else + window->success_ratio = IWL_INVALID_VALUE; + + fail_count = window->counter - window->success_counter; + + /* Calculate average throughput, if we have enough history. */ + if ((fail_count >= IWL_MVM_RS_RATE_MIN_FAILURE_TH) || + (window->success_counter >= IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) + window->average_tpt = (window->success_ratio * tpt + 64) / 128; + else + window->average_tpt = IWL_INVALID_VALUE; + + return 0; +} + +static int rs_collect_tx_data(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + int scale_index, int attempts, int successes, + u8 reduced_txp) +{ + struct iwl_rate_scale_data *window = NULL; + int ret; + + if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) + return -EINVAL; + + if (tbl->column != RS_COLUMN_INVALID) { + struct lq_sta_pers *pers = &lq_sta->pers; + + pers->tx_stats[tbl->column][scale_index].total += attempts; + pers->tx_stats[tbl->column][scale_index].success += successes; + } + + /* Select window for current tx bit rate */ + window = &(tbl->win[scale_index]); + + ret = _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, + window); + if (ret) + return ret; + + if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION)) + return -EINVAL; + + window = &tbl->tpc_win[reduced_txp]; + return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, + window); +} + +/* Convert rs_rate object into ucode rate bitmask */ +static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm, + struct rs_rate *rate) +{ + u32 ucode_rate = 0; + int index = rate->index; + + ucode_rate |= ((rate->ant << RATE_MCS_ANT_POS) & + RATE_MCS_ANT_ABC_MSK); + + if (is_legacy(rate)) { + ucode_rate |= iwl_rates[index].plcp; + if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) + ucode_rate |= RATE_MCS_CCK_MSK; + return ucode_rate; + } + + if (is_ht(rate)) { + if (index < IWL_FIRST_HT_RATE || index > IWL_LAST_HT_RATE) { + IWL_ERR(mvm, "Invalid HT rate index %d\n", index); + index = IWL_LAST_HT_RATE; + } + ucode_rate |= RATE_MCS_HT_MSK; + + if (is_ht_siso(rate)) + ucode_rate |= iwl_rates[index].plcp_ht_siso; + else if (is_ht_mimo2(rate)) + ucode_rate |= iwl_rates[index].plcp_ht_mimo2; + else + WARN_ON_ONCE(1); + } else if (is_vht(rate)) { + if (index < IWL_FIRST_VHT_RATE || index > IWL_LAST_VHT_RATE) { + IWL_ERR(mvm, "Invalid VHT rate index %d\n", index); + index = IWL_LAST_VHT_RATE; + } + ucode_rate |= RATE_MCS_VHT_MSK; + if (is_vht_siso(rate)) + ucode_rate |= iwl_rates[index].plcp_vht_siso; + else if (is_vht_mimo2(rate)) + ucode_rate |= iwl_rates[index].plcp_vht_mimo2; + else + WARN_ON_ONCE(1); + + } else { + IWL_ERR(mvm, "Invalid rate->type %d\n", rate->type); + } + + if (is_siso(rate) && rate->stbc) { + /* To enable STBC we need to set both a flag and ANT_AB */ + ucode_rate |= RATE_MCS_ANT_AB_MSK; + ucode_rate |= RATE_MCS_VHT_STBC_MSK; + } + + ucode_rate |= rate->bw; + if (rate->sgi) + ucode_rate |= RATE_MCS_SGI_MSK; + if (rate->ldpc) + ucode_rate |= RATE_MCS_LDPC_MSK; + + return ucode_rate; +} + +/* Convert a ucode rate into an rs_rate object */ +static int rs_rate_from_ucode_rate(const u32 ucode_rate, + enum ieee80211_band band, + struct rs_rate *rate) +{ + u32 ant_msk = ucode_rate & RATE_MCS_ANT_ABC_MSK; + u8 num_of_ant = get_num_of_ant_from_rate(ucode_rate); + u8 nss; + + memset(rate, 0, sizeof(*rate)); + rate->index = iwl_hwrate_to_plcp_idx(ucode_rate); + + if (rate->index == IWL_RATE_INVALID) + return -EINVAL; + + rate->ant = (ant_msk >> RATE_MCS_ANT_POS); + + /* Legacy */ + if (!(ucode_rate & RATE_MCS_HT_MSK) && + !(ucode_rate & RATE_MCS_VHT_MSK)) { + if (num_of_ant == 1) { + if (band == IEEE80211_BAND_5GHZ) + rate->type = LQ_LEGACY_A; + else + rate->type = LQ_LEGACY_G; + } + + return 0; + } + + /* HT or VHT */ + if (ucode_rate & RATE_MCS_SGI_MSK) + rate->sgi = true; + if (ucode_rate & RATE_MCS_LDPC_MSK) + rate->ldpc = true; + if (ucode_rate & RATE_MCS_VHT_STBC_MSK) + rate->stbc = true; + if (ucode_rate & RATE_MCS_BF_MSK) + rate->bfer = true; + + rate->bw = ucode_rate & RATE_MCS_CHAN_WIDTH_MSK; + + if (ucode_rate & RATE_MCS_HT_MSK) { + nss = ((ucode_rate & RATE_HT_MCS_NSS_MSK) >> + RATE_HT_MCS_NSS_POS) + 1; + + if (nss == 1) { + rate->type = LQ_HT_SISO; + WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1, + "stbc %d bfer %d", + rate->stbc, rate->bfer); + } else if (nss == 2) { + rate->type = LQ_HT_MIMO2; + WARN_ON_ONCE(num_of_ant != 2); + } else { + WARN_ON_ONCE(1); + } + } else if (ucode_rate & RATE_MCS_VHT_MSK) { + nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >> + RATE_VHT_MCS_NSS_POS) + 1; + + if (nss == 1) { + rate->type = LQ_VHT_SISO; + WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1, + "stbc %d bfer %d", + rate->stbc, rate->bfer); + } else if (nss == 2) { + rate->type = LQ_VHT_MIMO2; + WARN_ON_ONCE(num_of_ant != 2); + } else { + WARN_ON_ONCE(1); + } + } + + WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_160); + WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_80 && + !is_vht(rate)); + + return 0; +} + +/* switch to another antenna/antennas and return 1 */ +/* if no other valid antenna found, return 0 */ +static int rs_toggle_antenna(u32 valid_ant, struct rs_rate *rate) +{ + u8 new_ant_type; + + if (!rate->ant || rate->ant > ANT_ABC) + return 0; + + if (!rs_is_valid_ant(valid_ant, rate->ant)) + return 0; + + new_ant_type = ant_toggle_lookup[rate->ant]; + + while ((new_ant_type != rate->ant) && + !rs_is_valid_ant(valid_ant, new_ant_type)) + new_ant_type = ant_toggle_lookup[new_ant_type]; + + if (new_ant_type == rate->ant) + return 0; + + rate->ant = new_ant_type; + + return 1; +} + +static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, + struct rs_rate *rate) +{ + if (is_legacy(rate)) + return lq_sta->active_legacy_rate; + else if (is_siso(rate)) + return lq_sta->active_siso_rate; + else if (is_mimo2(rate)) + return lq_sta->active_mimo2_rate; + + WARN_ON_ONCE(1); + return 0; +} + +static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, + int rate_type) +{ + u8 high = IWL_RATE_INVALID; + u8 low = IWL_RATE_INVALID; + + /* 802.11A or ht walks to the next literal adjacent rate in + * the rate table */ + if (is_type_a_band(rate_type) || !is_type_legacy(rate_type)) { + int i; + u32 mask; + + /* Find the previous rate that is in the rate mask */ + i = index - 1; + for (mask = (1 << i); i >= 0; i--, mask >>= 1) { + if (rate_mask & mask) { + low = i; + break; + } + } + + /* Find the next rate that is in the rate mask */ + i = index + 1; + for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { + if (rate_mask & mask) { + high = i; + break; + } + } + + return (high << 8) | low; + } + + low = index; + while (low != IWL_RATE_INVALID) { + low = iwl_rates[low].prev_rs; + if (low == IWL_RATE_INVALID) + break; + if (rate_mask & (1 << low)) + break; + } + + high = index; + while (high != IWL_RATE_INVALID) { + high = iwl_rates[high].next_rs; + if (high == IWL_RATE_INVALID) + break; + if (rate_mask & (1 << high)) + break; + } + + return (high << 8) | low; +} + +static inline bool rs_rate_supported(struct iwl_lq_sta *lq_sta, + struct rs_rate *rate) +{ + return BIT(rate->index) & rs_get_supported_rates(lq_sta, rate); +} + +/* Get the next supported lower rate in the current column. + * Return true if bottom rate in the current column was reached + */ +static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta, + struct rs_rate *rate) +{ + u8 low; + u16 high_low; + u16 rate_mask; + struct iwl_mvm *mvm = lq_sta->pers.drv; + + rate_mask = rs_get_supported_rates(lq_sta, rate); + high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask, + rate->type); + low = high_low & 0xff; + + /* Bottom rate of column reached */ + if (low == IWL_RATE_INVALID) + return true; + + rate->index = low; + return false; +} + +/* Get the next rate to use following a column downgrade */ +static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta, + struct rs_rate *rate) +{ + struct iwl_mvm *mvm = lq_sta->pers.drv; + + if (is_legacy(rate)) { + /* No column to downgrade from Legacy */ + return; + } else if (is_siso(rate)) { + /* Downgrade to Legacy if we were in SISO */ + if (lq_sta->band == IEEE80211_BAND_5GHZ) + rate->type = LQ_LEGACY_A; + else + rate->type = LQ_LEGACY_G; + + rate->bw = RATE_MCS_CHAN_WIDTH_20; + + WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX || + rate->index > IWL_RATE_MCS_9_INDEX); + + rate->index = rs_ht_to_legacy[rate->index]; + rate->ldpc = false; + } else { + /* Downgrade to SISO with same MCS if in MIMO */ + rate->type = is_vht_mimo2(rate) ? + LQ_VHT_SISO : LQ_HT_SISO; + } + + if (num_of_ant(rate->ant) > 1) + rate->ant = first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); + + /* Relevant in both switching to SISO or Legacy */ + rate->sgi = false; + + if (!rs_rate_supported(lq_sta, rate)) + rs_get_lower_rate_in_column(lq_sta, rate); +} + +/* Check if both rates are identical + * allow_ant_mismatch enables matching a SISO rate on ANT_A or ANT_B + * with a rate indicating STBC/BFER and ANT_AB. + */ +static inline bool rs_rate_equal(struct rs_rate *a, + struct rs_rate *b, + bool allow_ant_mismatch) + +{ + bool ant_match = (a->ant == b->ant) && (a->stbc == b->stbc) && + (a->bfer == b->bfer); + + if (allow_ant_mismatch) { + if (a->stbc || a->bfer) { + WARN_ONCE(a->ant != ANT_AB, "stbc %d bfer %d ant %d", + a->stbc, a->bfer, a->ant); + ant_match |= (b->ant == ANT_A || b->ant == ANT_B); + } else if (b->stbc || b->bfer) { + WARN_ONCE(b->ant != ANT_AB, "stbc %d bfer %d ant %d", + b->stbc, b->bfer, b->ant); + ant_match |= (a->ant == ANT_A || a->ant == ANT_B); + } + } + + return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi) && + (a->ldpc == b->ldpc) && (a->index == b->index) && ant_match; +} + +/* Check if both rates share the same column */ +static inline bool rs_rate_column_match(struct rs_rate *a, + struct rs_rate *b) +{ + bool ant_match; + + if (a->stbc || a->bfer) + ant_match = (b->ant == ANT_A || b->ant == ANT_B); + else + ant_match = (a->ant == b->ant); + + return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi) + && ant_match; +} + +static inline enum rs_column rs_get_column_from_rate(struct rs_rate *rate) +{ + if (is_legacy(rate)) { + if (rate->ant == ANT_A) + return RS_COLUMN_LEGACY_ANT_A; + + if (rate->ant == ANT_B) + return RS_COLUMN_LEGACY_ANT_B; + + goto err; + } + + if (is_siso(rate)) { + if (rate->ant == ANT_A || rate->stbc || rate->bfer) + return rate->sgi ? RS_COLUMN_SISO_ANT_A_SGI : + RS_COLUMN_SISO_ANT_A; + + if (rate->ant == ANT_B) + return rate->sgi ? RS_COLUMN_SISO_ANT_B_SGI : + RS_COLUMN_SISO_ANT_B; + + goto err; + } + + if (is_mimo(rate)) + return rate->sgi ? RS_COLUMN_MIMO2_SGI : RS_COLUMN_MIMO2; + +err: + return RS_COLUMN_INVALID; +} + +static u8 rs_get_tid(struct ieee80211_hdr *hdr) +{ + u8 tid = IWL_MAX_TID_COUNT; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + } + + if (unlikely(tid > IWL_MAX_TID_COUNT)) + tid = IWL_MAX_TID_COUNT; + + return tid; +} + +void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + int tid, struct ieee80211_tx_info *info) +{ + int legacy_success; + int retries; + int i; + struct iwl_lq_cmd *table; + u32 lq_hwrate; + struct rs_rate lq_rate, tx_resp_rate; + struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; + u8 reduced_txp = (uintptr_t)info->status.status_driver_data[0]; + u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1]; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta; + bool allow_ant_mismatch = fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_LQ_SS_PARAMS); + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (!lq_sta) { + IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n"); + return; + } else if (!lq_sta->pers.drv) { + IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); + return; + } + + /* This packet was aggregated but doesn't carry status info */ + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && + !(info->flags & IEEE80211_TX_STAT_AMPDU)) + return; + + rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, &tx_resp_rate); + +#ifdef CONFIG_MAC80211_DEBUGFS + /* Disable last tx check if we are debugging with fixed rate but + * update tx stats */ + if (lq_sta->pers.dbg_fixed_rate) { + int index = tx_resp_rate.index; + enum rs_column column; + int attempts, success; + + column = rs_get_column_from_rate(&tx_resp_rate); + if (WARN_ONCE(column == RS_COLUMN_INVALID, + "Can't map rate 0x%x to column", + tx_resp_hwrate)) + return; + + if (info->flags & IEEE80211_TX_STAT_AMPDU) { + attempts = info->status.ampdu_len; + success = info->status.ampdu_ack_len; + } else { + attempts = info->status.rates[0].count; + success = !!(info->flags & IEEE80211_TX_STAT_ACK); + } + + lq_sta->pers.tx_stats[column][index].total += attempts; + lq_sta->pers.tx_stats[column][index].success += success; + + IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n", + tx_resp_hwrate, success, attempts); + return; + } +#endif + + if (time_after(jiffies, + (unsigned long)(lq_sta->last_tx + + (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) { + int t; + + IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); + for (t = 0; t < IWL_MAX_TID_COUNT; t++) + ieee80211_stop_tx_ba_session(sta, t); + + iwl_mvm_rs_rate_init(mvm, sta, info->band, false); + return; + } + lq_sta->last_tx = jiffies; + + /* Ignore this Tx frame response if its initial rate doesn't match + * that of latest Link Quality command. There may be stragglers + * from a previous Link Quality command, but we're no longer interested + * in those; they're either from the "active" mode while we're trying + * to check "search" mode, or a prior "search" mode after we've moved + * to a new "search" mode (which might become the new "active" mode). + */ + table = &lq_sta->lq; + lq_hwrate = le32_to_cpu(table->rs_table[0]); + rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate); + + /* Here we actually compare this rate to the latest LQ command */ + if (!rs_rate_equal(&tx_resp_rate, &lq_rate, allow_ant_mismatch)) { + IWL_DEBUG_RATE(mvm, + "initial tx resp rate 0x%x does not match 0x%x\n", + tx_resp_hwrate, lq_hwrate); + + /* + * Since rates mis-match, the last LQ command may have failed. + * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with + * ... driver. + */ + lq_sta->missed_rate_counter++; + if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) { + lq_sta->missed_rate_counter = 0; + IWL_DEBUG_RATE(mvm, + "Too many rates mismatch. Send sync LQ. rs_state %d\n", + lq_sta->rs_state); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); + } + /* Regardless, ignore this status info for outdated rate */ + return; + } else + /* Rate did match, so reset the missed_rate_counter */ + lq_sta->missed_rate_counter = 0; + + if (!lq_sta->search_better_tbl) { + curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + } else { + curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + } + + if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) { + IWL_DEBUG_RATE(mvm, + "Neither active nor search matches tx rate\n"); + tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE"); + tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH"); + rs_dump_rate(mvm, &lq_rate, "ACTUAL"); + + /* + * no matching table found, let's by-pass the data collection + * and continue to perform rate scale to find the rate table + */ + rs_stay_in_table(lq_sta, true); + goto done; + } + + /* + * Updating the frame history depends on whether packets were + * aggregated. + * + * For aggregation, all packets were transmitted at the same rate, the + * first index into rate scale table. + */ + if (info->flags & IEEE80211_TX_STAT_AMPDU) { + /* ampdu_ack_len = 0 marks no BA was received. In this case + * treat it as a single frame loss as we don't want the success + * ratio to dip too quickly because a BA wasn't received + */ + if (info->status.ampdu_ack_len == 0) + info->status.ampdu_len = 1; + + rs_collect_tx_data(mvm, lq_sta, curr_tbl, lq_rate.index, + info->status.ampdu_len, + info->status.ampdu_ack_len, + reduced_txp); + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { + lq_sta->total_success += info->status.ampdu_ack_len; + lq_sta->total_failed += (info->status.ampdu_len - + info->status.ampdu_ack_len); + } + } else { + /* For legacy, update frame history with for each Tx retry. */ + retries = info->status.rates[0].count - 1; + /* HW doesn't send more than 15 retries */ + retries = min(retries, 15); + + /* The last transmission may have been successful */ + legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); + /* Collect data for each rate used during failed TX attempts */ + for (i = 0; i <= retries; ++i) { + lq_hwrate = le32_to_cpu(table->rs_table[i]); + rs_rate_from_ucode_rate(lq_hwrate, info->band, + &lq_rate); + /* + * Only collect stats if retried rate is in the same RS + * table as active/search. + */ + if (rs_rate_column_match(&lq_rate, &curr_tbl->rate)) + tmp_tbl = curr_tbl; + else if (rs_rate_column_match(&lq_rate, + &other_tbl->rate)) + tmp_tbl = other_tbl; + else + continue; + + rs_collect_tx_data(mvm, lq_sta, tmp_tbl, lq_rate.index, + 1, i < retries ? 0 : legacy_success, + reduced_txp); + } + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { + lq_sta->total_success += legacy_success; + lq_sta->total_failed += retries + (1 - legacy_success); + } + } + /* The last TX rate is cached in lq_sta; it's set in if/else above */ + lq_sta->last_rate_n_flags = lq_hwrate; + IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp); +done: + /* See if there's a better rate or modulation mode to try. */ + if (sta->supp_rates[info->band]) + rs_rate_scale_perform(mvm, sta, lq_sta, tid); +} + +/* + * mac80211 sends us Tx status + */ +static void rs_mac80211_tx_status(void *mvm_r, + struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_r; + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (!iwl_mvm_sta_from_mac80211(sta)->vif) + return; + + if (!ieee80211_is_data(hdr->frame_control) || + info->flags & IEEE80211_TX_CTL_NO_ACK) + return; + + iwl_mvm_rs_tx_status(mvm, sta, rs_get_tid(hdr), info); +} + +/* + * Begin a period of staying with a selected modulation mode. + * Set "stay_in_tbl" flag to prevent any mode switches. + * Set frame tx success limits according to legacy vs. high-throughput, + * and reset overall (spanning all rates) tx success history statistics. + * These control how long we stay using same modulation mode before + * searching for a new mode. + */ +static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, + struct iwl_lq_sta *lq_sta) +{ + IWL_DEBUG_RATE(mvm, "Moving to RS_STATE_STAY_IN_COLUMN\n"); + lq_sta->rs_state = RS_STATE_STAY_IN_COLUMN; + if (is_legacy) { + lq_sta->table_count_limit = IWL_MVM_RS_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_MVM_RS_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_MVM_RS_LEGACY_SUCCESS_LIMIT; + } else { + lq_sta->table_count_limit = IWL_MVM_RS_NON_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT; + } + lq_sta->table_count = 0; + lq_sta->total_failed = 0; + lq_sta->total_success = 0; + lq_sta->flush_timer = jiffies; + lq_sta->visited_columns = 0; +} + +static inline int rs_get_max_rate_from_mask(unsigned long rate_mask) +{ + if (rate_mask) + return find_last_bit(&rate_mask, BITS_PER_LONG); + return IWL_RATE_INVALID; +} + +static int rs_get_max_allowed_rate(struct iwl_lq_sta *lq_sta, + const struct rs_tx_column *column) +{ + switch (column->mode) { + case RS_LEGACY: + return lq_sta->max_legacy_rate_idx; + case RS_SISO: + return lq_sta->max_siso_rate_idx; + case RS_MIMO2: + return lq_sta->max_mimo2_rate_idx; + default: + WARN_ON_ONCE(1); + } + + return lq_sta->max_legacy_rate_idx; +} + +static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta, + const struct rs_tx_column *column, + u32 bw) +{ + /* Used to choose among HT tables */ + const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT]; + + if (WARN_ON_ONCE(column->mode != RS_LEGACY && + column->mode != RS_SISO && + column->mode != RS_MIMO2)) + return expected_tpt_legacy; + + /* Legacy rates have only one table */ + if (column->mode == RS_LEGACY) + return expected_tpt_legacy; + + ht_tbl_pointer = expected_tpt_mimo2_20MHz; + /* Choose among many HT tables depending on number of streams + * (SISO/MIMO2), channel width (20/40/80), SGI, and aggregation + * status */ + if (column->mode == RS_SISO) { + switch (bw) { + case RATE_MCS_CHAN_WIDTH_20: + ht_tbl_pointer = expected_tpt_siso_20MHz; + break; + case RATE_MCS_CHAN_WIDTH_40: + ht_tbl_pointer = expected_tpt_siso_40MHz; + break; + case RATE_MCS_CHAN_WIDTH_80: + ht_tbl_pointer = expected_tpt_siso_80MHz; + break; + default: + WARN_ON_ONCE(1); + } + } else if (column->mode == RS_MIMO2) { + switch (bw) { + case RATE_MCS_CHAN_WIDTH_20: + ht_tbl_pointer = expected_tpt_mimo2_20MHz; + break; + case RATE_MCS_CHAN_WIDTH_40: + ht_tbl_pointer = expected_tpt_mimo2_40MHz; + break; + case RATE_MCS_CHAN_WIDTH_80: + ht_tbl_pointer = expected_tpt_mimo2_80MHz; + break; + default: + WARN_ON_ONCE(1); + } + } else { + WARN_ON_ONCE(1); + } + + if (!column->sgi && !lq_sta->is_agg) /* Normal */ + return ht_tbl_pointer[0]; + else if (column->sgi && !lq_sta->is_agg) /* SGI */ + return ht_tbl_pointer[1]; + else if (!column->sgi && lq_sta->is_agg) /* AGG */ + return ht_tbl_pointer[2]; + else /* AGG+SGI */ + return ht_tbl_pointer[3]; +} + +static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl) +{ + struct rs_rate *rate = &tbl->rate; + const struct rs_tx_column *column = &rs_tx_columns[tbl->column]; + + tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw); +} + +static s32 rs_get_best_rate(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, /* "search" */ + unsigned long rate_mask, s8 index) +{ + struct iwl_scale_tbl_info *active_tbl = + &(lq_sta->lq_info[lq_sta->active_tbl]); + s32 success_ratio = active_tbl->win[index].success_ratio; + u16 expected_current_tpt = active_tbl->expected_tpt[index]; + const u16 *tpt_tbl = tbl->expected_tpt; + u16 high_low; + u32 target_tpt; + int rate_idx; + + if (success_ratio >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) { + target_tpt = 100 * expected_current_tpt; + IWL_DEBUG_RATE(mvm, + "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n", + success_ratio, target_tpt); + } else { + target_tpt = lq_sta->last_tpt; + IWL_DEBUG_RATE(mvm, + "SR %d not that good. Find rate exceeding ACTUAL_TPT %d\n", + success_ratio, target_tpt); + } + + rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG); + + while (rate_idx != IWL_RATE_INVALID) { + if (target_tpt < (100 * tpt_tbl[rate_idx])) + break; + + high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask, + tbl->rate.type); + + rate_idx = (high_low >> 8) & 0xff; + } + + IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n", + rate_idx, target_tpt, + rate_idx != IWL_RATE_INVALID ? + 100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE); + + return rate_idx; +} + +static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta) +{ + if (sta->bandwidth >= IEEE80211_STA_RX_BW_80) + return RATE_MCS_CHAN_WIDTH_80; + else if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) + return RATE_MCS_CHAN_WIDTH_40; + + return RATE_MCS_CHAN_WIDTH_20; +} + +/* + * Check whether we should continue using same modulation mode, or + * begin search for a new mode, based on: + * 1) # tx successes or failures while using this mode + * 2) # times calling this function + * 3) elapsed time in this mode (not used, for now) + */ +static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) +{ + struct iwl_scale_tbl_info *tbl; + int active_tbl; + int flush_interval_passed = 0; + struct iwl_mvm *mvm; + + mvm = lq_sta->pers.drv; + active_tbl = lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + + /* If we've been disallowing search, see if we should now allow it */ + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { + /* Elapsed time using current modulation mode */ + if (lq_sta->flush_timer) + flush_interval_passed = + time_after(jiffies, + (unsigned long)(lq_sta->flush_timer + + (IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT * HZ))); + + /* + * Check if we should allow search for new modulation mode. + * If many frames have failed or succeeded, or we've used + * this same modulation for a long time, allow search, and + * reset history stats that keep track of whether we should + * allow a new search. Also (below) reset all bitmaps and + * stats in active history. + */ + if (force_search || + (lq_sta->total_failed > lq_sta->max_failure_limit) || + (lq_sta->total_success > lq_sta->max_success_limit) || + ((!lq_sta->search_better_tbl) && + (lq_sta->flush_timer) && (flush_interval_passed))) { + IWL_DEBUG_RATE(mvm, + "LQ: stay is expired %d %d %d\n", + lq_sta->total_failed, + lq_sta->total_success, + flush_interval_passed); + + /* Allow search for new mode */ + lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_STARTED; + IWL_DEBUG_RATE(mvm, + "Moving to RS_STATE_SEARCH_CYCLE_STARTED\n"); + lq_sta->total_failed = 0; + lq_sta->total_success = 0; + lq_sta->flush_timer = 0; + /* mark the current column as visited */ + lq_sta->visited_columns = BIT(tbl->column); + /* + * Else if we've used this modulation mode enough repetitions + * (regardless of elapsed time or success/failure), reset + * history bitmaps and rate-specific stats for all rates in + * active table. + */ + } else { + lq_sta->table_count++; + if (lq_sta->table_count >= + lq_sta->table_count_limit) { + lq_sta->table_count = 0; + + IWL_DEBUG_RATE(mvm, + "LQ: stay in table clear win\n"); + rs_rate_scale_clear_tbl_windows(mvm, tbl); + } + } + + /* If transitioning to allow "search", reset all history + * bitmaps and stats in active table (this will become the new + * "search" table). */ + if (lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED) { + rs_rate_scale_clear_tbl_windows(mvm, tbl); + } + } +} + +/* + * setup rate table in uCode + */ +static void rs_update_rate_tbl(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl) +{ + rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); +} + +static bool rs_tweak_rate_tbl(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + enum rs_action scale_action) +{ + if (sta->bandwidth != IEEE80211_STA_RX_BW_80) + return false; + + if (!is_vht_siso(&tbl->rate)) + return false; + + if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_80) && + (tbl->rate.index == IWL_RATE_MCS_0_INDEX) && + (scale_action == RS_ACTION_DOWNSCALE)) { + tbl->rate.bw = RATE_MCS_CHAN_WIDTH_20; + tbl->rate.index = IWL_RATE_MCS_4_INDEX; + IWL_DEBUG_RATE(mvm, "Switch 80Mhz SISO MCS0 -> 20Mhz MCS4\n"); + goto tweaked; + } + + /* Go back to 80Mhz MCS1 only if we've established that 20Mhz MCS5 is + * sustainable, i.e. we're past the test window. We can't go back + * if MCS5 is just tested as this will happen always after switching + * to 20Mhz MCS4 because the rate stats are cleared. + */ + if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_20) && + (((tbl->rate.index == IWL_RATE_MCS_5_INDEX) && + (scale_action == RS_ACTION_STAY)) || + ((tbl->rate.index > IWL_RATE_MCS_5_INDEX) && + (scale_action == RS_ACTION_UPSCALE)))) { + tbl->rate.bw = RATE_MCS_CHAN_WIDTH_80; + tbl->rate.index = IWL_RATE_MCS_1_INDEX; + IWL_DEBUG_RATE(mvm, "Switch 20Mhz SISO MCS5 -> 80Mhz MCS1\n"); + goto tweaked; + } + + return false; + +tweaked: + rs_set_expected_tpt_table(lq_sta, tbl); + rs_rate_scale_clear_tbl_windows(mvm, tbl); + return true; +} + +static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl) +{ + int i, j, max_rate; + enum rs_column next_col_id; + const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column]; + const struct rs_tx_column *next_col; + allow_column_func_t allow_func; + u8 valid_ants = iwl_mvm_get_valid_tx_ant(mvm); + const u16 *expected_tpt_tbl; + u16 tpt, max_expected_tpt; + + for (i = 0; i < MAX_NEXT_COLUMNS; i++) { + next_col_id = curr_col->next_columns[i]; + + if (next_col_id == RS_COLUMN_INVALID) + continue; + + if (lq_sta->visited_columns & BIT(next_col_id)) { + IWL_DEBUG_RATE(mvm, "Skip already visited column %d\n", + next_col_id); + continue; + } + + next_col = &rs_tx_columns[next_col_id]; + + if (!rs_is_valid_ant(valid_ants, next_col->ant)) { + IWL_DEBUG_RATE(mvm, + "Skip column %d as ANT config isn't supported by chip. valid_ants 0x%x column ant 0x%x\n", + next_col_id, valid_ants, next_col->ant); + continue; + } + + for (j = 0; j < MAX_COLUMN_CHECKS; j++) { + allow_func = next_col->checks[j]; + if (allow_func && !allow_func(mvm, sta, &tbl->rate, + next_col)) + break; + } + + if (j != MAX_COLUMN_CHECKS) { + IWL_DEBUG_RATE(mvm, + "Skip column %d: not allowed (check %d failed)\n", + next_col_id, j); + + continue; + } + + tpt = lq_sta->last_tpt / 100; + expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col, + rs_bw_from_sta_bw(sta)); + if (WARN_ON_ONCE(!expected_tpt_tbl)) + continue; + + max_rate = rs_get_max_allowed_rate(lq_sta, next_col); + if (max_rate == IWL_RATE_INVALID) { + IWL_DEBUG_RATE(mvm, + "Skip column %d: no rate is allowed in this column\n", + next_col_id); + continue; + } + + max_expected_tpt = expected_tpt_tbl[max_rate]; + if (tpt >= max_expected_tpt) { + IWL_DEBUG_RATE(mvm, + "Skip column %d: can't beat current TPT. Max expected %d current %d\n", + next_col_id, max_expected_tpt, tpt); + continue; + } + + IWL_DEBUG_RATE(mvm, + "Found potential column %d. Max expected %d current %d\n", + next_col_id, max_expected_tpt, tpt); + break; + } + + if (i == MAX_NEXT_COLUMNS) + return RS_COLUMN_INVALID; + + return next_col_id; +} + +static int rs_switch_to_column(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct ieee80211_sta *sta, + enum rs_column col_id) +{ + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct rs_rate *rate = &search_tbl->rate; + const struct rs_tx_column *column = &rs_tx_columns[col_id]; + const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column]; + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + unsigned long rate_mask = 0; + u32 rate_idx = 0; + + memcpy(search_tbl, tbl, sz); + + rate->sgi = column->sgi; + rate->ant = column->ant; + + if (column->mode == RS_LEGACY) { + if (lq_sta->band == IEEE80211_BAND_5GHZ) + rate->type = LQ_LEGACY_A; + else + rate->type = LQ_LEGACY_G; + + rate->bw = RATE_MCS_CHAN_WIDTH_20; + rate->ldpc = false; + rate_mask = lq_sta->active_legacy_rate; + } else if (column->mode == RS_SISO) { + rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; + rate_mask = lq_sta->active_siso_rate; + } else if (column->mode == RS_MIMO2) { + rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; + rate_mask = lq_sta->active_mimo2_rate; + } else { + WARN_ON_ONCE("Bad column mode"); + } + + if (column->mode != RS_LEGACY) { + rate->bw = rs_bw_from_sta_bw(sta); + rate->ldpc = lq_sta->ldpc; + } + + 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 + */ + if (curr_column->mode != column->mode) { + rate_idx = rs_get_best_rate(mvm, lq_sta, search_tbl, + rate_mask, rate->index); + + if ((rate_idx == IWL_RATE_INVALID) || + !(BIT(rate_idx) & rate_mask)) { + IWL_DEBUG_RATE(mvm, + "can not switch with index %d" + " rate mask %lx\n", + rate_idx, rate_mask); + + goto err; + } + + rate->index = rate_idx; + } + + IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n", + col_id, rate->index); + + return 0; + +err: + rate->type = LQ_NONE; + return -1; +} + +static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, + struct iwl_scale_tbl_info *tbl, + s32 sr, int low, int high, + int current_tpt, + int low_tpt, int high_tpt) +{ + enum rs_action action = RS_ACTION_STAY; + + if ((sr <= RS_PERCENT(IWL_MVM_RS_SR_FORCE_DECREASE)) || + (current_tpt == 0)) { + IWL_DEBUG_RATE(mvm, + "Decrease rate because of low SR\n"); + return RS_ACTION_DOWNSCALE; + } + + if ((low_tpt == IWL_INVALID_VALUE) && + (high_tpt == IWL_INVALID_VALUE) && + (high != IWL_RATE_INVALID)) { + IWL_DEBUG_RATE(mvm, + "No data about high/low rates. Increase rate\n"); + return RS_ACTION_UPSCALE; + } + + if ((high_tpt == IWL_INVALID_VALUE) && + (high != IWL_RATE_INVALID) && + (low_tpt != IWL_INVALID_VALUE) && + (low_tpt < current_tpt)) { + IWL_DEBUG_RATE(mvm, + "No data about high rate and low rate is worse. Increase rate\n"); + return RS_ACTION_UPSCALE; + } + + if ((high_tpt != IWL_INVALID_VALUE) && + (high_tpt > current_tpt)) { + IWL_DEBUG_RATE(mvm, + "Higher rate is better. Increate rate\n"); + return RS_ACTION_UPSCALE; + } + + if ((low_tpt != IWL_INVALID_VALUE) && + (high_tpt != IWL_INVALID_VALUE) && + (low_tpt < current_tpt) && + (high_tpt < current_tpt)) { + IWL_DEBUG_RATE(mvm, + "Both high and low are worse. Maintain rate\n"); + return RS_ACTION_STAY; + } + + if ((low_tpt != IWL_INVALID_VALUE) && + (low_tpt > current_tpt)) { + IWL_DEBUG_RATE(mvm, + "Lower rate is better\n"); + action = RS_ACTION_DOWNSCALE; + goto out; + } + + if ((low_tpt == IWL_INVALID_VALUE) && + (low != IWL_RATE_INVALID)) { + IWL_DEBUG_RATE(mvm, + "No data about lower rate\n"); + action = RS_ACTION_DOWNSCALE; + goto out; + } + + IWL_DEBUG_RATE(mvm, "Maintain rate\n"); + +out: + if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID)) { + if (sr >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) { + IWL_DEBUG_RATE(mvm, + "SR is above NO DECREASE. Avoid downscale\n"); + action = RS_ACTION_STAY; + } else if (current_tpt > (100 * tbl->expected_tpt[low])) { + IWL_DEBUG_RATE(mvm, + "Current TPT is higher than max expected in low rate. Avoid downscale\n"); + action = RS_ACTION_STAY; + } else { + IWL_DEBUG_RATE(mvm, "Decrease rate\n"); + } + } + + return action; +} + +static bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta) +{ + /* Our chip supports Tx STBC and the peer is an HT/VHT STA which + * supports STBC of at least 1*SS + */ + if (!lq_sta->stbc_capable) + return false; + + if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) + return false; + + return true; +} + +static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index, + int *weaker, int *stronger) +{ + *weaker = index + IWL_MVM_RS_TPC_TX_POWER_STEP; + if (*weaker > TPC_MAX_REDUCTION) + *weaker = TPC_INVALID; + + *stronger = index - IWL_MVM_RS_TPC_TX_POWER_STEP; + if (*stronger < 0) + *stronger = TPC_INVALID; +} + +static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct rs_rate *rate, enum ieee80211_band band) +{ + int index = rate->index; + bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM); + bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION && + !vif->bss_conf.ps); + + IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n", + cam, sta_ps_disabled); + /* + * allow tpc only if power management is enabled, or bt coex + * activity grade allows it and we are on 2.4Ghz. + */ + if ((cam || sta_ps_disabled) && + !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band)) + return false; + + IWL_DEBUG_RATE(mvm, "check rate, table type: %d\n", rate->type); + if (is_legacy(rate)) + return index == IWL_RATE_54M_INDEX; + if (is_ht(rate)) + return index == IWL_RATE_MCS_7_INDEX; + if (is_vht(rate)) + return index == IWL_RATE_MCS_7_INDEX || + index == IWL_RATE_MCS_8_INDEX || + index == IWL_RATE_MCS_9_INDEX; + + WARN_ON_ONCE(1); + return false; +} + +enum tpc_action { + TPC_ACTION_STAY, + TPC_ACTION_DECREASE, + TPC_ACTION_INCREASE, + TPC_ACTION_NO_RESTIRCTION, +}; + +static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm, + s32 sr, int weak, int strong, + int current_tpt, + int weak_tpt, int strong_tpt) +{ + /* stay until we have valid tpt */ + if (current_tpt == IWL_INVALID_VALUE) { + IWL_DEBUG_RATE(mvm, "no current tpt. stay.\n"); + return TPC_ACTION_STAY; + } + + /* Too many failures, increase txp */ + if (sr <= RS_PERCENT(IWL_MVM_RS_TPC_SR_FORCE_INCREASE) || + current_tpt == 0) { + IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n"); + return TPC_ACTION_NO_RESTIRCTION; + } + + /* try decreasing first if applicable */ + if (weak != TPC_INVALID) { + if (weak_tpt == IWL_INVALID_VALUE && + (strong_tpt == IWL_INVALID_VALUE || + current_tpt >= strong_tpt)) { + IWL_DEBUG_RATE(mvm, + "no weak txp measurement. decrease txp\n"); + return TPC_ACTION_DECREASE; + } + + if (weak_tpt > current_tpt) { + IWL_DEBUG_RATE(mvm, + "lower txp has better tpt. decrease txp\n"); + return TPC_ACTION_DECREASE; + } + } + + /* next, increase if needed */ + if (sr < RS_PERCENT(IWL_MVM_RS_TPC_SR_NO_INCREASE) && + strong != TPC_INVALID) { + if (weak_tpt == IWL_INVALID_VALUE && + strong_tpt != IWL_INVALID_VALUE && + current_tpt < strong_tpt) { + IWL_DEBUG_RATE(mvm, + "higher txp has better tpt. increase txp\n"); + return TPC_ACTION_INCREASE; + } + + if (weak_tpt < current_tpt && + (strong_tpt == IWL_INVALID_VALUE || + strong_tpt > current_tpt)) { + IWL_DEBUG_RATE(mvm, + "lower txp has worse tpt. increase txp\n"); + return TPC_ACTION_INCREASE; + } + } + + IWL_DEBUG_RATE(mvm, "no need to increase or decrease txp - stay\n"); + return TPC_ACTION_STAY; +} + +static bool rs_tpc_perform(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl) +{ + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + struct ieee80211_vif *vif = mvm_sta->vif; + struct ieee80211_chanctx_conf *chanctx_conf; + enum ieee80211_band band; + struct iwl_rate_scale_data *window; + struct rs_rate *rate = &tbl->rate; + enum tpc_action action; + s32 sr; + u8 cur = lq_sta->lq.reduced_tpc; + int current_tpt; + int weak, strong; + int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE; + +#ifdef CONFIG_MAC80211_DEBUGFS + if (lq_sta->pers.dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) { + IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n", + lq_sta->pers.dbg_fixed_txp_reduction); + lq_sta->lq.reduced_tpc = lq_sta->pers.dbg_fixed_txp_reduction; + return cur != lq_sta->pers.dbg_fixed_txp_reduction; + } +#endif + + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + if (WARN_ON(!chanctx_conf)) + band = IEEE80211_NUM_BANDS; + else + band = chanctx_conf->def.chan->band; + rcu_read_unlock(); + + if (!rs_tpc_allowed(mvm, vif, rate, band)) { + IWL_DEBUG_RATE(mvm, + "tpc is not allowed. remove txp restrictions\n"); + lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; + return cur != TPC_NO_REDUCTION; + } + + rs_get_adjacent_txp(mvm, cur, &weak, &strong); + + /* Collect measured throughputs for current and adjacent rates */ + window = tbl->tpc_win; + sr = window[cur].success_ratio; + current_tpt = window[cur].average_tpt; + if (weak != TPC_INVALID) + weak_tpt = window[weak].average_tpt; + if (strong != TPC_INVALID) + strong_tpt = window[strong].average_tpt; + + IWL_DEBUG_RATE(mvm, + "(TPC: %d): cur_tpt %d SR %d weak %d strong %d weak_tpt %d strong_tpt %d\n", + cur, current_tpt, sr, weak, strong, + weak_tpt, strong_tpt); + + action = rs_get_tpc_action(mvm, sr, weak, strong, + current_tpt, weak_tpt, strong_tpt); + + /* override actions if we are on the edge */ + if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) { + IWL_DEBUG_RATE(mvm, "already in lowest txp, stay\n"); + action = TPC_ACTION_STAY; + } else if (strong == TPC_INVALID && + (action == TPC_ACTION_INCREASE || + action == TPC_ACTION_NO_RESTIRCTION)) { + IWL_DEBUG_RATE(mvm, "already in highest txp, stay\n"); + action = TPC_ACTION_STAY; + } + + switch (action) { + case TPC_ACTION_DECREASE: + lq_sta->lq.reduced_tpc = weak; + return true; + case TPC_ACTION_INCREASE: + lq_sta->lq.reduced_tpc = strong; + return true; + case TPC_ACTION_NO_RESTIRCTION: + lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; + return true; + case TPC_ACTION_STAY: + /* do nothing */ + break; + } + return false; +} + +/* + * Do rate scaling and search for new modulation mode. + */ +static void rs_rate_scale_perform(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + int tid) +{ + int low = IWL_RATE_INVALID; + int high = IWL_RATE_INVALID; + int index; + struct iwl_rate_scale_data *window = NULL; + int current_tpt = IWL_INVALID_VALUE; + int low_tpt = IWL_INVALID_VALUE; + int high_tpt = IWL_INVALID_VALUE; + u32 fail_count; + enum rs_action scale_action = RS_ACTION_STAY; + u16 rate_mask; + u8 update_lq = 0; + struct iwl_scale_tbl_info *tbl, *tbl1; + u8 active_tbl = 0; + u8 done_search = 0; + u16 high_low; + s32 sr; + u8 prev_agg = lq_sta->is_agg; + struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_tid_data *tid_data; + struct rs_rate *rate; + + lq_sta->is_agg = !!sta_priv->agg_tids; + + /* + * Select rate-scale / modulation-mode table to work with in + * the rest of this function: "search" if searching for better + * modulation mode, or "active" if doing rate scaling within a mode. + */ + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + rate = &tbl->rate; + + if (prev_agg != lq_sta->is_agg) { + IWL_DEBUG_RATE(mvm, + "Aggregation changed: prev %d current %d. Update expected TPT table\n", + prev_agg, lq_sta->is_agg); + rs_set_expected_tpt_table(lq_sta, tbl); + rs_rate_scale_clear_tbl_windows(mvm, tbl); + } + + /* current tx rate */ + index = rate->index; + + /* rates available for this association, and for modulation mode */ + rate_mask = rs_get_supported_rates(lq_sta, rate); + + if (!(BIT(index) & rate_mask)) { + IWL_ERR(mvm, "Current Rate is not valid\n"); + if (lq_sta->search_better_tbl) { + /* revert to active table if search table is not valid*/ + rate->type = LQ_NONE; + lq_sta->search_better_tbl = 0; + tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + rs_update_rate_tbl(mvm, sta, lq_sta, tbl); + } + return; + } + + /* Get expected throughput table and history window for current rate */ + if (!tbl->expected_tpt) { + IWL_ERR(mvm, "tbl->expected_tpt is NULL\n"); + return; + } + + /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ + window = &(tbl->win[index]); + + /* + * If there is not enough history to calculate actual average + * throughput, keep analyzing results of more tx frames, without + * changing rate or mode (bypass most of the rest of this function). + * Set up new rate table in uCode only if old rate is not supported + * in current association (use new rate found above). + */ + fail_count = window->counter - window->success_counter; + if ((fail_count < IWL_MVM_RS_RATE_MIN_FAILURE_TH) && + (window->success_counter < IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) { + IWL_DEBUG_RATE(mvm, + "%s: Test Window: succ %d total %d\n", + rs_pretty_rate(rate), + window->success_counter, window->counter); + + /* Can't calculate this yet; not enough history */ + window->average_tpt = IWL_INVALID_VALUE; + + /* Should we stay with this modulation mode, + * or search for a new one? */ + rs_stay_in_table(lq_sta, false); + + return; + } + + /* If we are searching for better modulation mode, check success. */ + if (lq_sta->search_better_tbl) { + /* If good success, continue using the "search" mode; + * no need to send new link quality command, since we're + * continuing to use the setup that we've been trying. */ + if (window->average_tpt > lq_sta->last_tpt) { + IWL_DEBUG_RATE(mvm, + "SWITCHING TO NEW TABLE SR: %d " + "cur-tpt %d old-tpt %d\n", + window->success_ratio, + window->average_tpt, + lq_sta->last_tpt); + + /* Swap tables; "search" becomes "active" */ + lq_sta->active_tbl = active_tbl; + current_tpt = window->average_tpt; + /* Else poor success; go back to mode in "active" table */ + } else { + IWL_DEBUG_RATE(mvm, + "GOING BACK TO THE OLD TABLE: SR %d " + "cur-tpt %d old-tpt %d\n", + window->success_ratio, + window->average_tpt, + lq_sta->last_tpt); + + /* Nullify "search" table */ + rate->type = LQ_NONE; + + /* Revert to "active" table */ + active_tbl = lq_sta->active_tbl; + tbl = &(lq_sta->lq_info[active_tbl]); + + /* Revert to "active" rate and throughput info */ + index = tbl->rate.index; + current_tpt = lq_sta->last_tpt; + + /* Need to set up a new rate table in uCode */ + update_lq = 1; + } + + /* Either way, we've made a decision; modulation mode + * search is done, allow rate adjustment next time. */ + lq_sta->search_better_tbl = 0; + done_search = 1; /* Don't switch modes below! */ + goto lq_update; + } + + /* (Else) not in search of better modulation mode, try for better + * starting rate, while staying in this mode. */ + high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type); + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ + + sr = window->success_ratio; + + /* Collect measured throughputs for current and adjacent rates */ + current_tpt = window->average_tpt; + if (low != IWL_RATE_INVALID) + low_tpt = tbl->win[low].average_tpt; + if (high != IWL_RATE_INVALID) + high_tpt = tbl->win[high].average_tpt; + + IWL_DEBUG_RATE(mvm, + "%s: cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n", + rs_pretty_rate(rate), current_tpt, sr, + low, high, low_tpt, high_tpt); + + scale_action = rs_get_rate_action(mvm, tbl, sr, low, high, + current_tpt, low_tpt, high_tpt); + + /* Force a search in case BT doesn't like us being in MIMO */ + if (is_mimo(rate) && + !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) { + IWL_DEBUG_RATE(mvm, + "BT Coex forbids MIMO. Search for new config\n"); + rs_stay_in_table(lq_sta, true); + goto lq_update; + } + + switch (scale_action) { + case RS_ACTION_DOWNSCALE: + /* Decrease starting rate, update uCode's rate table */ + if (low != IWL_RATE_INVALID) { + update_lq = 1; + index = low; + } else { + IWL_DEBUG_RATE(mvm, + "At the bottom rate. Can't decrease\n"); + } + + break; + case RS_ACTION_UPSCALE: + /* Increase starting rate, update uCode's rate table */ + if (high != IWL_RATE_INVALID) { + update_lq = 1; + index = high; + } else { + IWL_DEBUG_RATE(mvm, + "At the top rate. Can't increase\n"); + } + + break; + case RS_ACTION_STAY: + /* No change */ + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) + update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl); + break; + default: + break; + } + +lq_update: + /* Replace uCode's rate table for the destination station. */ + if (update_lq) { + tbl->rate.index = index; + if (IWL_MVM_RS_80_20_FAR_RANGE_TWEAK) + rs_tweak_rate_tbl(mvm, sta, lq_sta, tbl, scale_action); + rs_update_rate_tbl(mvm, sta, lq_sta, tbl); + } + + rs_stay_in_table(lq_sta, false); + + /* + * Search for new modulation mode if we're: + * 1) Not changing rates right now + * 2) Not just finishing up a search + * 3) Allowing a new search + */ + if (!update_lq && !done_search && + lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED + && window->counter) { + enum rs_column next_column; + + /* Save current throughput to compare with "search" throughput*/ + lq_sta->last_tpt = current_tpt; + + IWL_DEBUG_RATE(mvm, + "Start Search: update_lq %d done_search %d rs_state %d win->counter %d\n", + update_lq, done_search, lq_sta->rs_state, + window->counter); + + next_column = rs_get_next_column(mvm, lq_sta, sta, tbl); + if (next_column != RS_COLUMN_INVALID) { + int ret = rs_switch_to_column(mvm, lq_sta, sta, + next_column); + if (!ret) + lq_sta->search_better_tbl = 1; + } else { + IWL_DEBUG_RATE(mvm, + "No more columns to explore in search cycle. Go to RS_STATE_SEARCH_CYCLE_ENDED\n"); + lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_ENDED; + } + + /* If new "search" mode was selected, set up in uCode table */ + if (lq_sta->search_better_tbl) { + /* Access the "search" table, clear its history. */ + tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + rs_rate_scale_clear_tbl_windows(mvm, tbl); + + /* Use new "search" start rate */ + index = tbl->rate.index; + + rs_dump_rate(mvm, &tbl->rate, + "Switch to SEARCH TABLE:"); + rs_update_rate_tbl(mvm, sta, lq_sta, tbl); + } else { + done_search = 1; + } + } + + if (done_search && lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_ENDED) { + /* If the "active" (non-search) mode was legacy, + * and we've tried switching antennas, + * but we haven't been able to try HT modes (not available), + * stay with best antenna legacy modulation for a while + * before next round of mode comparisons. */ + tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); + if (is_legacy(&tbl1->rate)) { + IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n"); + + if (tid != IWL_MAX_TID_COUNT) { + tid_data = &sta_priv->tid_data[tid]; + if (tid_data->state != IWL_AGG_OFF) { + IWL_DEBUG_RATE(mvm, + "Stop aggregation on tid %d\n", + tid); + ieee80211_stop_tx_ba_session(sta, tid); + } + } + rs_set_stay_in_table(mvm, 1, lq_sta); + } else { + /* If we're in an HT mode, and all 3 mode switch actions + * have been tried and compared, stay in this best modulation + * mode for a while before next round of mode comparisons. */ + if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && + (lq_sta->tx_agg_tid_en & (1 << tid)) && + (tid != IWL_MAX_TID_COUNT)) { + tid_data = &sta_priv->tid_data[tid]; + if (tid_data->state == IWL_AGG_OFF) { + IWL_DEBUG_RATE(mvm, + "try to aggregate tid %d\n", + tid); + rs_tl_turn_on_agg(mvm, tid, + lq_sta, sta); + } + } + rs_set_stay_in_table(mvm, 0, lq_sta); + } + } +} + +struct rs_init_rate_info { + s8 rssi; + u8 rate_idx; +}; + +static const struct rs_init_rate_info rs_optimal_rates_24ghz_legacy[] = { + { -60, IWL_RATE_54M_INDEX }, + { -64, IWL_RATE_48M_INDEX }, + { -68, IWL_RATE_36M_INDEX }, + { -80, IWL_RATE_24M_INDEX }, + { -84, IWL_RATE_18M_INDEX }, + { -85, IWL_RATE_12M_INDEX }, + { -86, IWL_RATE_11M_INDEX }, + { -88, IWL_RATE_5M_INDEX }, + { -90, IWL_RATE_2M_INDEX }, + { S8_MIN, IWL_RATE_1M_INDEX }, +}; + +static const struct rs_init_rate_info rs_optimal_rates_5ghz_legacy[] = { + { -60, IWL_RATE_54M_INDEX }, + { -64, IWL_RATE_48M_INDEX }, + { -72, IWL_RATE_36M_INDEX }, + { -80, IWL_RATE_24M_INDEX }, + { -84, IWL_RATE_18M_INDEX }, + { -85, IWL_RATE_12M_INDEX }, + { -87, IWL_RATE_9M_INDEX }, + { S8_MIN, IWL_RATE_6M_INDEX }, +}; + +static const struct rs_init_rate_info rs_optimal_rates_ht[] = { + { -60, IWL_RATE_MCS_7_INDEX }, + { -64, IWL_RATE_MCS_6_INDEX }, + { -68, IWL_RATE_MCS_5_INDEX }, + { -72, IWL_RATE_MCS_4_INDEX }, + { -80, IWL_RATE_MCS_3_INDEX }, + { -84, IWL_RATE_MCS_2_INDEX }, + { -85, IWL_RATE_MCS_1_INDEX }, + { S8_MIN, IWL_RATE_MCS_0_INDEX}, +}; + +static const struct rs_init_rate_info rs_optimal_rates_vht_20mhz[] = { + { -60, IWL_RATE_MCS_8_INDEX }, + { -64, IWL_RATE_MCS_7_INDEX }, + { -68, IWL_RATE_MCS_6_INDEX }, + { -72, IWL_RATE_MCS_5_INDEX }, + { -80, IWL_RATE_MCS_4_INDEX }, + { -84, IWL_RATE_MCS_3_INDEX }, + { -85, IWL_RATE_MCS_2_INDEX }, + { -87, IWL_RATE_MCS_1_INDEX }, + { S8_MIN, IWL_RATE_MCS_0_INDEX}, +}; + +static const struct rs_init_rate_info rs_optimal_rates_vht_40_80mhz[] = { + { -60, IWL_RATE_MCS_9_INDEX }, + { -64, IWL_RATE_MCS_8_INDEX }, + { -68, IWL_RATE_MCS_7_INDEX }, + { -72, IWL_RATE_MCS_6_INDEX }, + { -80, IWL_RATE_MCS_5_INDEX }, + { -84, IWL_RATE_MCS_4_INDEX }, + { -85, IWL_RATE_MCS_3_INDEX }, + { -87, IWL_RATE_MCS_2_INDEX }, + { -88, IWL_RATE_MCS_1_INDEX }, + { S8_MIN, IWL_RATE_MCS_0_INDEX }, +}; + +/* Init the optimal rate based on STA caps + * This combined with rssi is used to report the last tx rate + * to userspace when we haven't transmitted enough frames. + */ +static void rs_init_optimal_rate(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta) +{ + struct rs_rate *rate = &lq_sta->optimal_rate; + + if (lq_sta->max_mimo2_rate_idx != IWL_RATE_INVALID) + rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; + else if (lq_sta->max_siso_rate_idx != IWL_RATE_INVALID) + rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; + else if (lq_sta->band == IEEE80211_BAND_5GHZ) + rate->type = LQ_LEGACY_A; + else + rate->type = LQ_LEGACY_G; + + rate->bw = rs_bw_from_sta_bw(sta); + rate->sgi = rs_sgi_allow(mvm, sta, rate, NULL); + + /* ANT/LDPC/STBC aren't relevant for the rate reported to userspace */ + + if (is_mimo(rate)) { + lq_sta->optimal_rate_mask = lq_sta->active_mimo2_rate; + } else if (is_siso(rate)) { + lq_sta->optimal_rate_mask = lq_sta->active_siso_rate; + } else { + lq_sta->optimal_rate_mask = lq_sta->active_legacy_rate; + + if (lq_sta->band == IEEE80211_BAND_5GHZ) { + lq_sta->optimal_rates = rs_optimal_rates_5ghz_legacy; + lq_sta->optimal_nentries = + ARRAY_SIZE(rs_optimal_rates_5ghz_legacy); + } else { + lq_sta->optimal_rates = rs_optimal_rates_24ghz_legacy; + lq_sta->optimal_nentries = + ARRAY_SIZE(rs_optimal_rates_24ghz_legacy); + } + } + + if (is_vht(rate)) { + if (rate->bw == RATE_MCS_CHAN_WIDTH_20) { + lq_sta->optimal_rates = rs_optimal_rates_vht_20mhz; + lq_sta->optimal_nentries = + ARRAY_SIZE(rs_optimal_rates_vht_20mhz); + } else { + lq_sta->optimal_rates = rs_optimal_rates_vht_40_80mhz; + lq_sta->optimal_nentries = + ARRAY_SIZE(rs_optimal_rates_vht_40_80mhz); + } + } else if (is_ht(rate)) { + lq_sta->optimal_rates = rs_optimal_rates_ht; + lq_sta->optimal_nentries = ARRAY_SIZE(rs_optimal_rates_ht); + } +} + +/* Compute the optimal rate index based on RSSI */ +static struct rs_rate *rs_get_optimal_rate(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta) +{ + struct rs_rate *rate = &lq_sta->optimal_rate; + int i; + + rate->index = find_first_bit(&lq_sta->optimal_rate_mask, + BITS_PER_LONG); + + for (i = 0; i < lq_sta->optimal_nentries; i++) { + int rate_idx = lq_sta->optimal_rates[i].rate_idx; + + if ((lq_sta->pers.last_rssi >= lq_sta->optimal_rates[i].rssi) && + (BIT(rate_idx) & lq_sta->optimal_rate_mask)) { + rate->index = rate_idx; + break; + } + } + + return rate; +} + +/* Choose an initial legacy rate and antenna to use based on the RSSI + * of last Rx + */ +static void rs_get_initial_rate(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + enum ieee80211_band band, + struct rs_rate *rate) +{ + int i, nentries; + s8 best_rssi = S8_MIN; + u8 best_ant = ANT_NONE; + u8 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); + const struct rs_init_rate_info *initial_rates; + + for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) { + if (!(lq_sta->pers.chains & BIT(i))) + continue; + + if (lq_sta->pers.chain_signal[i] > best_rssi) { + best_rssi = lq_sta->pers.chain_signal[i]; + best_ant = BIT(i); + } + } + + IWL_DEBUG_RATE(mvm, "Best ANT: %s Best RSSI: %d\n", + rs_pretty_ant(best_ant), best_rssi); + + if (best_ant != ANT_A && best_ant != ANT_B) + rate->ant = first_antenna(valid_tx_ant); + else + rate->ant = best_ant; + + rate->sgi = false; + rate->ldpc = false; + rate->bw = RATE_MCS_CHAN_WIDTH_20; + + rate->index = find_first_bit(&lq_sta->active_legacy_rate, + BITS_PER_LONG); + + if (band == IEEE80211_BAND_5GHZ) { + rate->type = LQ_LEGACY_A; + initial_rates = rs_optimal_rates_5ghz_legacy; + nentries = ARRAY_SIZE(rs_optimal_rates_5ghz_legacy); + } else { + rate->type = LQ_LEGACY_G; + initial_rates = rs_optimal_rates_24ghz_legacy; + nentries = ARRAY_SIZE(rs_optimal_rates_24ghz_legacy); + } + + if (IWL_MVM_RS_RSSI_BASED_INIT_RATE) { + for (i = 0; i < nentries; i++) { + int rate_idx = initial_rates[i].rate_idx; + if ((best_rssi >= initial_rates[i].rssi) && + (BIT(rate_idx) & lq_sta->active_legacy_rate)) { + rate->index = rate_idx; + break; + } + } + } + + IWL_DEBUG_RATE(mvm, "rate_idx %d ANT %s\n", rate->index, + rs_pretty_ant(rate->ant)); +} + +/* Save info about RSSI of last Rx */ +void rs_update_last_rssi(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct ieee80211_rx_status *rx_status) +{ + int i; + + lq_sta->pers.chains = rx_status->chains; + lq_sta->pers.chain_signal[0] = rx_status->chain_signal[0]; + lq_sta->pers.chain_signal[1] = rx_status->chain_signal[1]; + lq_sta->pers.chain_signal[2] = rx_status->chain_signal[2]; + lq_sta->pers.last_rssi = S8_MIN; + + for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) { + if (!(lq_sta->pers.chains & BIT(i))) + continue; + + if (lq_sta->pers.chain_signal[i] > lq_sta->pers.last_rssi) + lq_sta->pers.last_rssi = lq_sta->pers.chain_signal[i]; + } +} + +/** + * rs_initialize_lq - Initialize a station's hardware rate table + * + * The uCode's station table contains a table of fallback rates + * for automatic fallback during transmission. + * + * NOTE: This sets up a default set of values. These will be replaced later + * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of + * rc80211_simple. + * + * NOTE: Run REPLY_ADD_STA command to set up station table entry, before + * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, + * which requires station table entry to exist). + */ +static void rs_initialize_lq(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + enum ieee80211_band band, + bool init) +{ + struct iwl_scale_tbl_info *tbl; + struct rs_rate *rate; + u8 active_tbl = 0; + + if (!sta || !lq_sta) + return; + + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + rate = &tbl->rate; + + rs_get_initial_rate(mvm, lq_sta, band, rate); + rs_init_optimal_rate(mvm, sta, lq_sta); + + WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B); + if (rate->ant == ANT_A) + tbl->column = RS_COLUMN_LEGACY_ANT_A; + else + tbl->column = RS_COLUMN_LEGACY_ANT_B; + + rs_set_expected_tpt_table(lq_sta, tbl); + rs_fill_lq_cmd(mvm, sta, lq_sta, rate); + /* TODO restore station should remember the lq cmd */ + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init); +} + +static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, + struct ieee80211_tx_rate_control *txrc) +{ + struct sk_buff *skb = txrc->skb; + struct iwl_op_mode *op_mode __maybe_unused = + (struct iwl_op_mode *)mvm_r; + struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_lq_sta *lq_sta = mvm_sta; + struct rs_rate *optimal_rate; + u32 last_ucode_rate; + + if (sta && !iwl_mvm_sta_from_mac80211(sta)->vif) { + /* if vif isn't initialized mvm doesn't know about + * this station, so don't do anything with the it + */ + sta = NULL; + mvm_sta = NULL; + } + + /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (lq_sta && !lq_sta->pers.drv) { + IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); + mvm_sta = NULL; + } + + /* Send management frames and NO_ACK data using lowest rate. */ + if (rate_control_send_low(sta, mvm_sta, txrc)) + return; + + iwl_mvm_hwrate_to_tx_rate(lq_sta->last_rate_n_flags, + info->band, &info->control.rates[0]); + info->control.rates[0].count = 1; + + /* Report the optimal rate based on rssi and STA caps if we haven't + * converged yet (too little traffic) or exploring other modulations + */ + if (lq_sta->rs_state != RS_STATE_STAY_IN_COLUMN) { + optimal_rate = rs_get_optimal_rate(mvm, lq_sta); + last_ucode_rate = ucode_rate_from_rs_rate(mvm, + optimal_rate); + iwl_mvm_hwrate_to_tx_rate(last_ucode_rate, info->band, + &txrc->reported_rate); + } +} + +static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, + gfp_t gfp) +{ + struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); + struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate; + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; + + IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); + + lq_sta->pers.drv = mvm; +#ifdef CONFIG_MAC80211_DEBUGFS + lq_sta->pers.dbg_fixed_rate = 0; + lq_sta->pers.dbg_fixed_txp_reduction = TPC_INVALID; + lq_sta->pers.ss_force = RS_SS_FORCE_NONE; +#endif + lq_sta->pers.chains = 0; + memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal)); + lq_sta->pers.last_rssi = S8_MIN; + + return &sta_priv->lq_sta; +} + +static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap, + int nss) +{ + u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & + (0x3 << (2 * (nss - 1))); + rx_mcs >>= (2 * (nss - 1)); + + if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_7) + return IWL_RATE_MCS_7_INDEX; + else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_8) + return IWL_RATE_MCS_8_INDEX; + else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_9) + return IWL_RATE_MCS_9_INDEX; + + WARN_ON_ONCE(rx_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED); + return -1; +} + +static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, + struct ieee80211_sta_vht_cap *vht_cap, + struct iwl_lq_sta *lq_sta) +{ + int i; + int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1); + + if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { + for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { + if (i == IWL_RATE_9M_INDEX) + continue; + + /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ + if (i == IWL_RATE_MCS_9_INDEX && + sta->bandwidth == IEEE80211_STA_RX_BW_20) + continue; + + lq_sta->active_siso_rate |= BIT(i); + } + } + + if (sta->rx_nss < 2) + return; + + highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2); + if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { + for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { + if (i == IWL_RATE_9M_INDEX) + continue; + + /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ + if (i == IWL_RATE_MCS_9_INDEX && + sta->bandwidth == IEEE80211_STA_RX_BW_20) + continue; + + lq_sta->active_mimo2_rate |= BIT(i); + } + } +} + +static void rs_ht_init(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + struct ieee80211_sta_ht_cap *ht_cap) +{ + /* active_siso_rate mask includes 9 MBits (bit 5), + * and CCK (bits 0-3), supp_rates[] does not; + * shift to convert format, force 9 MBits off. + */ + lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; + lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; + lq_sta->active_siso_rate &= ~((u16)0x2); + lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; + + lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; + lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; + lq_sta->active_mimo2_rate &= ~((u16)0x2); + lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; + + if (mvm->cfg->ht_params->ldpc && + (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)) + lq_sta->ldpc = true; + + if (mvm->cfg->ht_params->stbc && + (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && + (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) + lq_sta->stbc_capable = true; + + lq_sta->is_vht = false; +} + +static void rs_vht_init(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + struct ieee80211_sta_vht_cap *vht_cap) +{ + rs_vht_set_enabled_rates(sta, vht_cap, lq_sta); + + if (mvm->cfg->ht_params->ldpc && + (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)) + lq_sta->ldpc = true; + + if (mvm->cfg->ht_params->stbc && + (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && + (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)) + lq_sta->stbc_capable = true; + + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEAMFORMER) && + (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && + (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + lq_sta->bfer_capable = true; + + lq_sta->is_vht = true; +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm) +{ + spin_lock_bh(&mvm->drv_stats_lock); + memset(&mvm->drv_rx_stats, 0, sizeof(mvm->drv_rx_stats)); + spin_unlock_bh(&mvm->drv_stats_lock); +} + +void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg) +{ + u8 nss = 0, mcs = 0; + + spin_lock(&mvm->drv_stats_lock); + + if (agg) + mvm->drv_rx_stats.agg_frames++; + + mvm->drv_rx_stats.success_frames++; + + switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + mvm->drv_rx_stats.bw_20_frames++; + break; + case RATE_MCS_CHAN_WIDTH_40: + mvm->drv_rx_stats.bw_40_frames++; + break; + case RATE_MCS_CHAN_WIDTH_80: + mvm->drv_rx_stats.bw_80_frames++; + break; + default: + WARN_ONCE(1, "bad BW. rate 0x%x", rate); + } + + if (rate & RATE_MCS_HT_MSK) { + mvm->drv_rx_stats.ht_frames++; + mcs = rate & RATE_HT_MCS_RATE_CODE_MSK; + nss = ((rate & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS) + 1; + } else if (rate & RATE_MCS_VHT_MSK) { + mvm->drv_rx_stats.vht_frames++; + mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; + nss = ((rate & RATE_VHT_MCS_NSS_MSK) >> + RATE_VHT_MCS_NSS_POS) + 1; + } else { + mvm->drv_rx_stats.legacy_frames++; + } + + if (nss == 1) + mvm->drv_rx_stats.siso_frames++; + else if (nss == 2) + mvm->drv_rx_stats.mimo2_frames++; + + if (rate & RATE_MCS_SGI_MSK) + mvm->drv_rx_stats.sgi_frames++; + else + mvm->drv_rx_stats.ngi_frames++; + + mvm->drv_rx_stats.last_rates[mvm->drv_rx_stats.last_frame_idx] = rate; + mvm->drv_rx_stats.last_frame_idx = + (mvm->drv_rx_stats.last_frame_idx + 1) % + ARRAY_SIZE(mvm->drv_rx_stats.last_rates); + + spin_unlock(&mvm->drv_stats_lock); +} +#endif + +/* + * Called after adding a new station to initialize rate scaling + */ +void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + enum ieee80211_band band, bool init) +{ + int i, j; + struct ieee80211_hw *hw = mvm->hw; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); + struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; + struct ieee80211_supported_band *sband; + unsigned long supp; /* must be unsigned long for for_each_set_bit */ + + /* clear all non-persistent lq data */ + memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); + + sband = hw->wiphy->bands[band]; + + lq_sta->lq.sta_id = sta_priv->sta_id; + + for (j = 0; j < LQ_SIZE; j++) + rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]); + + lq_sta->flush_timer = 0; + lq_sta->last_tx = jiffies; + + IWL_DEBUG_RATE(mvm, + "LQ: *** rate scale station global init for station %d ***\n", + sta_priv->sta_id); + /* TODO: what is a good starting rate for STA? About middle? Maybe not + * the lowest or the highest rate.. Could consider using RSSI from + * previous packets? Need to have IEEE 802.1X auth succeed immediately + * after assoc.. */ + + lq_sta->missed_rate_counter = IWL_MVM_RS_MISSED_RATE_MAX; + lq_sta->band = sband->band; + /* + * active legacy rates as per supported rates bitmap + */ + supp = sta->supp_rates[sband->band]; + lq_sta->active_legacy_rate = 0; + for_each_set_bit(i, &supp, BITS_PER_LONG) + lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); + + /* TODO: should probably account for rx_highest for both HT/VHT */ + if (!vht_cap || !vht_cap->vht_supported) + rs_ht_init(mvm, sta, lq_sta, ht_cap); + else + rs_vht_init(mvm, sta, lq_sta, vht_cap); + + lq_sta->max_legacy_rate_idx = + rs_get_max_rate_from_mask(lq_sta->active_legacy_rate); + lq_sta->max_siso_rate_idx = + rs_get_max_rate_from_mask(lq_sta->active_siso_rate); + lq_sta->max_mimo2_rate_idx = + rs_get_max_rate_from_mask(lq_sta->active_mimo2_rate); + + IWL_DEBUG_RATE(mvm, + "LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC=%d BFER=%d\n", + lq_sta->active_legacy_rate, + lq_sta->active_siso_rate, + lq_sta->active_mimo2_rate, + lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc_capable, + lq_sta->bfer_capable); + IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n", + lq_sta->max_legacy_rate_idx, + lq_sta->max_siso_rate_idx, + lq_sta->max_mimo2_rate_idx); + + /* These values will be overridden later */ + lq_sta->lq.single_stream_ant_msk = + first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); + lq_sta->lq.dual_stream_ant_msk = ANT_AB; + + /* as default allow aggregation for all tids */ + lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; + lq_sta->is_agg = 0; +#ifdef CONFIG_IWLWIFI_DEBUGFS + iwl_mvm_reset_frame_stats(mvm); +#endif + rs_initialize_lq(mvm, sta, lq_sta, band, init); +} + +static void rs_rate_update(void *mvm_r, + struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *priv_sta, + u32 changed) +{ + u8 tid; + struct iwl_op_mode *op_mode = + (struct iwl_op_mode *)mvm_r; + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + if (!iwl_mvm_sta_from_mac80211(sta)->vif) + return; + + /* Stop any ongoing aggregations as rs starts off assuming no agg */ + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) + ieee80211_stop_tx_ba_session(sta, tid); + + iwl_mvm_rs_rate_init(mvm, sta, sband->band, false); +} + +#ifdef CONFIG_MAC80211_DEBUGFS +static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm, + struct iwl_lq_cmd *lq_cmd, + enum ieee80211_band band, + u32 ucode_rate) +{ + struct rs_rate rate; + int i; + int num_rates = ARRAY_SIZE(lq_cmd->rs_table); + __le32 ucode_rate_le32 = cpu_to_le32(ucode_rate); + u8 ant = (ucode_rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; + + for (i = 0; i < num_rates; i++) + lq_cmd->rs_table[i] = ucode_rate_le32; + + rs_rate_from_ucode_rate(ucode_rate, band, &rate); + + if (is_mimo(&rate)) + lq_cmd->mimo_delim = num_rates - 1; + else + lq_cmd->mimo_delim = 0; + + lq_cmd->reduced_tpc = 0; + + if (num_of_ant(ant) == 1) + lq_cmd->single_stream_ant_msk = ant; + + lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; +} +#endif /* CONFIG_MAC80211_DEBUGFS */ + +static void rs_fill_rates_for_column(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct rs_rate *rate, + __le32 *rs_table, int *rs_table_index, + int num_rates, int num_retries, + u8 valid_tx_ant, bool toggle_ant) +{ + int i, j; + __le32 ucode_rate; + bool bottom_reached = false; + int prev_rate_idx = rate->index; + int end = LINK_QUAL_MAX_RETRY_NUM; + int index = *rs_table_index; + + for (i = 0; i < num_rates && index < end; i++) { + for (j = 0; j < num_retries && index < end; j++, index++) { + ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, + rate)); + rs_table[index] = ucode_rate; + if (toggle_ant) + rs_toggle_antenna(valid_tx_ant, rate); + } + + prev_rate_idx = rate->index; + bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate); + if (bottom_reached && !is_legacy(rate)) + break; + } + + if (!bottom_reached && !is_legacy(rate)) + rate->index = prev_rate_idx; + + *rs_table_index = index; +} + +/* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI + * column the rate table should look like this: + * + * rate[0] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI + * rate[1] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI + * rate[2] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI + * rate[3] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI + * rate[4] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI + * rate[5] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI + * rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI + * rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI + * rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI + * rate[9] 0x800B Legacy | ANT: B Rate: 36 Mbps + * rate[10] 0x4009 Legacy | ANT: A Rate: 24 Mbps + * rate[11] 0x8007 Legacy | ANT: B Rate: 18 Mbps + * rate[12] 0x4005 Legacy | ANT: A Rate: 12 Mbps + * rate[13] 0x800F Legacy | ANT: B Rate: 9 Mbps + * rate[14] 0x400D Legacy | ANT: A Rate: 6 Mbps + * rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps + */ +static void rs_build_rates_table(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + const struct rs_rate *initial_rate) +{ + struct rs_rate rate; + int num_rates, num_retries, index = 0; + u8 valid_tx_ant = 0; + struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; + bool toggle_ant = false; + + memcpy(&rate, initial_rate, sizeof(rate)); + + valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); + + /* TODO: remove old API when min FW API hits 14 */ + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS) && + rs_stbc_allow(mvm, sta, lq_sta)) + rate.stbc = true; + + if (is_siso(&rate)) { + num_rates = IWL_MVM_RS_INITIAL_SISO_NUM_RATES; + num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; + } else if (is_mimo(&rate)) { + num_rates = IWL_MVM_RS_INITIAL_MIMO_NUM_RATES; + num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; + } else { + num_rates = IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES; + num_retries = IWL_MVM_RS_INITIAL_LEGACY_RETRIES; + toggle_ant = true; + } + + rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, + num_rates, num_retries, valid_tx_ant, + toggle_ant); + + rs_get_lower_rate_down_column(lq_sta, &rate); + + if (is_siso(&rate)) { + num_rates = IWL_MVM_RS_SECONDARY_SISO_NUM_RATES; + num_retries = IWL_MVM_RS_SECONDARY_SISO_RETRIES; + lq_cmd->mimo_delim = index; + } else if (is_legacy(&rate)) { + num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; + num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES; + } else { + WARN_ON_ONCE(1); + } + + toggle_ant = true; + + rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, + num_rates, num_retries, valid_tx_ant, + toggle_ant); + + rs_get_lower_rate_down_column(lq_sta, &rate); + + num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; + num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES; + + rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, + num_rates, num_retries, valid_tx_ant, + toggle_ant); + +} + +struct rs_bfer_active_iter_data { + struct ieee80211_sta *exclude_sta; + struct iwl_mvm_sta *bfer_mvmsta; +}; + +static void rs_bfer_active_iter(void *_data, + struct ieee80211_sta *sta) +{ + struct rs_bfer_active_iter_data *data = _data; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.lq; + u32 ss_params = le32_to_cpu(lq_cmd->ss_params); + + if (sta == data->exclude_sta) + return; + + /* The current sta has BFER allowed */ + if (ss_params & LQ_SS_BFER_ALLOWED) { + WARN_ON_ONCE(data->bfer_mvmsta != NULL); + + data->bfer_mvmsta = mvmsta; + } +} + +static int rs_bfer_priority(struct iwl_mvm_sta *sta) +{ + int prio = -1; + enum nl80211_iftype viftype = ieee80211_vif_type_p2p(sta->vif); + + switch (viftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + prio = 3; + break; + case NL80211_IFTYPE_P2P_CLIENT: + prio = 2; + break; + case NL80211_IFTYPE_STATION: + prio = 1; + break; + default: + WARN_ONCE(true, "viftype %d sta_id %d", viftype, sta->sta_id); + prio = -1; + } + + return prio; +} + +/* Returns >0 if sta1 has a higher BFER priority compared to sta2 */ +static int rs_bfer_priority_cmp(struct iwl_mvm_sta *sta1, + struct iwl_mvm_sta *sta2) +{ + int prio1 = rs_bfer_priority(sta1); + int prio2 = rs_bfer_priority(sta2); + + if (prio1 > prio2) + return 1; + if (prio1 < prio2) + return -1; + return 0; +} + +static void rs_set_lq_ss_params(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + const struct rs_rate *initial_rate) +{ + struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct rs_bfer_active_iter_data data = { + .exclude_sta = sta, + .bfer_mvmsta = NULL, + }; + struct iwl_mvm_sta *bfer_mvmsta = NULL; + u32 ss_params = LQ_SS_PARAMS_VALID; + + if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) + goto out; + +#ifdef CONFIG_MAC80211_DEBUGFS + /* Check if forcing the decision is configured. + * Note that SISO is forced by not allowing STBC or BFER + */ + if (lq_sta->pers.ss_force == RS_SS_FORCE_STBC) + ss_params |= (LQ_SS_STBC_1SS_ALLOWED | LQ_SS_FORCE); + else if (lq_sta->pers.ss_force == RS_SS_FORCE_BFER) + ss_params |= (LQ_SS_BFER_ALLOWED | LQ_SS_FORCE); + + if (lq_sta->pers.ss_force != RS_SS_FORCE_NONE) { + IWL_DEBUG_RATE(mvm, "Forcing single stream Tx decision %d\n", + lq_sta->pers.ss_force); + goto out; + } +#endif + + if (lq_sta->stbc_capable) + ss_params |= LQ_SS_STBC_1SS_ALLOWED; + + if (!lq_sta->bfer_capable) + goto out; + + ieee80211_iterate_stations_atomic(mvm->hw, + rs_bfer_active_iter, + &data); + bfer_mvmsta = data.bfer_mvmsta; + + /* This code is safe as it doesn't run concurrently for different + * stations. This is guaranteed by the fact that calls to + * ieee80211_tx_status wouldn't run concurrently for a single HW. + */ + if (!bfer_mvmsta) { + IWL_DEBUG_RATE(mvm, "No sta with BFER allowed found. Allow\n"); + + ss_params |= LQ_SS_BFER_ALLOWED; + goto out; + } + + IWL_DEBUG_RATE(mvm, "Found existing sta %d with BFER activated\n", + bfer_mvmsta->sta_id); + + /* Disallow BFER on another STA if active and we're a higher priority */ + if (rs_bfer_priority_cmp(mvmsta, bfer_mvmsta) > 0) { + struct iwl_lq_cmd *bfersta_lq_cmd = &bfer_mvmsta->lq_sta.lq; + u32 bfersta_ss_params = le32_to_cpu(bfersta_lq_cmd->ss_params); + + bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED; + bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params); + iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd, false); + + ss_params |= LQ_SS_BFER_ALLOWED; + IWL_DEBUG_RATE(mvm, + "Lower priority BFER sta found (%d). Switch BFER\n", + bfer_mvmsta->sta_id); + } +out: + lq_cmd->ss_params = cpu_to_le32(ss_params); +} + +static void rs_fill_lq_cmd(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + const struct rs_rate *initial_rate) +{ + struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; + struct iwl_mvm_sta *mvmsta; + struct iwl_mvm_vif *mvmvif; + + lq_cmd->agg_disable_start_th = IWL_MVM_RS_AGG_DISABLE_START; + lq_cmd->agg_time_limit = + cpu_to_le16(IWL_MVM_RS_AGG_TIME_LIMIT); + +#ifdef CONFIG_MAC80211_DEBUGFS + if (lq_sta->pers.dbg_fixed_rate) { + rs_build_rates_table_from_fixed(mvm, lq_cmd, + lq_sta->band, + lq_sta->pers.dbg_fixed_rate); + return; + } +#endif + if (WARN_ON_ONCE(!sta || !initial_rate)) + return; + + rs_build_rates_table(mvm, sta, lq_sta, initial_rate); + + if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS)) + rs_set_lq_ss_params(mvm, sta, lq_sta, initial_rate); + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); + + if (num_of_ant(initial_rate->ant) == 1) + lq_cmd->single_stream_ant_msk = initial_rate->ant; + + lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize; + + /* + * In case of low latency, tell the firmware to leave a frame in the + * Tx Fifo so that it can start a transaction in the same TxOP. This + * basically allows the firmware to send bursts. + */ + if (iwl_mvm_vif_low_latency(mvmvif)) { + lq_cmd->agg_frame_cnt_limit--; + + if (mvm->low_latency_agg_frame_limit) + lq_cmd->agg_frame_cnt_limit = + min(lq_cmd->agg_frame_cnt_limit, + mvm->low_latency_agg_frame_limit); + } + + if (mvmsta->vif->p2p) + lq_cmd->flags |= LQ_FLAG_USE_RTS_MSK; + + lq_cmd->agg_time_limit = + cpu_to_le16(iwl_mvm_coex_agg_time_limit(mvm, sta)); +} + +static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) +{ + return hw->priv; +} +/* rate scale requires free function to be implemented */ +static void rs_free(void *mvm_rate) +{ + return; +} + +static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta, + void *mvm_sta) +{ + struct iwl_op_mode *op_mode __maybe_unused = mvm_r; + struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); + + IWL_DEBUG_RATE(mvm, "enter\n"); + IWL_DEBUG_RATE(mvm, "leave\n"); +} + +#ifdef CONFIG_MAC80211_DEBUGFS +int rs_pretty_print_rate(char *buf, const u32 rate) +{ + + char *type, *bw; + u8 mcs = 0, nss = 0; + u8 ant = (rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; + + if (!(rate & RATE_MCS_HT_MSK) && + !(rate & RATE_MCS_VHT_MSK)) { + int index = iwl_hwrate_to_plcp_idx(rate); + + return sprintf(buf, "Legacy | ANT: %s Rate: %s Mbps\n", + rs_pretty_ant(ant), + index == IWL_RATE_INVALID ? "BAD" : + iwl_rate_mcs[index].mbps); + } + + if (rate & RATE_MCS_VHT_MSK) { + type = "VHT"; + mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; + nss = ((rate & RATE_VHT_MCS_NSS_MSK) + >> RATE_VHT_MCS_NSS_POS) + 1; + } else if (rate & RATE_MCS_HT_MSK) { + type = "HT"; + mcs = rate & RATE_HT_MCS_INDEX_MSK; + } else { + type = "Unknown"; /* shouldn't happen */ + } + + switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + bw = "20Mhz"; + break; + case RATE_MCS_CHAN_WIDTH_40: + bw = "40Mhz"; + break; + case RATE_MCS_CHAN_WIDTH_80: + bw = "80Mhz"; + break; + case RATE_MCS_CHAN_WIDTH_160: + bw = "160Mhz"; + break; + default: + bw = "BAD BW"; + } + + return sprintf(buf, "%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s\n", + type, rs_pretty_ant(ant), bw, mcs, nss, + (rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ", + (rate & RATE_MCS_HT_STBC_MSK) ? "STBC " : "", + (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "", + (rate & RATE_MCS_BF_MSK) ? "BF " : "", + (rate & RATE_MCS_ZLF_MSK) ? "ZLF " : ""); +} + +/** + * Program the device to use fixed rate for frame transmit + * This is for debugging/testing only + * once the device start use fixed rate, we need to reload the module + * to being back the normal operation. + */ +static void rs_program_fix_rate(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta) +{ + lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ + lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + + IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n", + lq_sta->lq.sta_id, lq_sta->pers.dbg_fixed_rate); + + if (lq_sta->pers.dbg_fixed_rate) { + rs_fill_lq_cmd(mvm, NULL, lq_sta, NULL); + iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false); + } +} + +static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_mvm *mvm; + char buf[64]; + size_t buf_size; + u32 parsed_rate; + + mvm = lq_sta->pers.drv; + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%x", &parsed_rate) == 1) + lq_sta->pers.dbg_fixed_rate = parsed_rate; + else + lq_sta->pers.dbg_fixed_rate = 0; + + rs_program_fix_rate(mvm, lq_sta); + + return count; +} + +static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *buff; + int desc = 0; + int i = 0; + ssize_t ret; + + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_mvm *mvm; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct rs_rate *rate = &tbl->rate; + u32 ss_params; + mvm = lq_sta->pers.drv; + buff = kmalloc(2048, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); + desc += sprintf(buff+desc, "failed=%d success=%d rate=0%lX\n", + lq_sta->total_failed, lq_sta->total_success, + lq_sta->active_legacy_rate); + desc += sprintf(buff+desc, "fixed rate 0x%X\n", + lq_sta->pers.dbg_fixed_rate); + desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", + (iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "", + (iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "", + (iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : ""); + desc += sprintf(buff+desc, "lq type %s\n", + (is_legacy(rate)) ? "legacy" : + is_vht(rate) ? "VHT" : "HT"); + if (!is_legacy(rate)) { + desc += sprintf(buff + desc, " %s", + (is_siso(rate)) ? "SISO" : "MIMO2"); + desc += sprintf(buff + desc, " %s", + (is_ht20(rate)) ? "20MHz" : + (is_ht40(rate)) ? "40MHz" : + (is_ht80(rate)) ? "80Mhz" : "BAD BW"); + desc += sprintf(buff + desc, " %s %s %s\n", + (rate->sgi) ? "SGI" : "NGI", + (rate->ldpc) ? "LDPC" : "BCC", + (lq_sta->is_agg) ? "AGG on" : ""); + } + desc += sprintf(buff+desc, "last tx rate=0x%X\n", + lq_sta->last_rate_n_flags); + desc += sprintf(buff+desc, + "general: flags=0x%X mimo-d=%d s-ant=0x%x d-ant=0x%x\n", + lq_sta->lq.flags, + lq_sta->lq.mimo_delim, + lq_sta->lq.single_stream_ant_msk, + lq_sta->lq.dual_stream_ant_msk); + + desc += sprintf(buff+desc, + "agg: time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", + le16_to_cpu(lq_sta->lq.agg_time_limit), + lq_sta->lq.agg_disable_start_th, + lq_sta->lq.agg_frame_cnt_limit); + + desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc); + ss_params = le32_to_cpu(lq_sta->lq.ss_params); + desc += sprintf(buff+desc, "single stream params: %s%s%s%s\n", + (ss_params & LQ_SS_PARAMS_VALID) ? + "VALID" : "INVALID", + (ss_params & LQ_SS_BFER_ALLOWED) ? + ", BFER" : "", + (ss_params & LQ_SS_STBC_1SS_ALLOWED) ? + ", STBC" : "", + (ss_params & LQ_SS_FORCE) ? + ", FORCE" : ""); + desc += sprintf(buff+desc, + "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", + lq_sta->lq.initial_rate_index[0], + lq_sta->lq.initial_rate_index[1], + lq_sta->lq.initial_rate_index[2], + lq_sta->lq.initial_rate_index[3]); + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]); + + desc += sprintf(buff+desc, " rate[%d] 0x%X ", i, r); + desc += rs_pretty_print_rate(buff+desc, r); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_scale_table_ops = { + .write = rs_sta_dbgfs_scale_table_write, + .read = rs_sta_dbgfs_scale_table_read, + .open = simple_open, + .llseek = default_llseek, +}; +static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *buff; + int desc = 0; + int i, j; + ssize_t ret; + struct iwl_scale_tbl_info *tbl; + struct rs_rate *rate; + struct iwl_lq_sta *lq_sta = file->private_data; + + buff = kmalloc(1024, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + for (i = 0; i < LQ_SIZE; i++) { + tbl = &(lq_sta->lq_info[i]); + rate = &tbl->rate; + desc += sprintf(buff+desc, + "%s type=%d SGI=%d BW=%s DUP=0\n" + "index=%d\n", + lq_sta->active_tbl == i ? "*" : "x", + rate->type, + rate->sgi, + is_ht20(rate) ? "20Mhz" : + is_ht40(rate) ? "40Mhz" : + is_ht80(rate) ? "80Mhz" : "ERR", + rate->index); + for (j = 0; j < IWL_RATE_COUNT; j++) { + desc += sprintf(buff+desc, + "counter=%d success=%d %%=%d\n", + tbl->win[j].counter, + tbl->win[j].success_counter, + tbl->win[j].success_ratio); + } + } + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_stats_table_ops = { + .read = rs_sta_dbgfs_stats_table_read, + .open = simple_open, + .llseek = default_llseek, +}; + +static ssize_t rs_sta_dbgfs_drv_tx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + static const char * const column_name[] = { + [RS_COLUMN_LEGACY_ANT_A] = "LEGACY_ANT_A", + [RS_COLUMN_LEGACY_ANT_B] = "LEGACY_ANT_B", + [RS_COLUMN_SISO_ANT_A] = "SISO_ANT_A", + [RS_COLUMN_SISO_ANT_B] = "SISO_ANT_B", + [RS_COLUMN_SISO_ANT_A_SGI] = "SISO_ANT_A_SGI", + [RS_COLUMN_SISO_ANT_B_SGI] = "SISO_ANT_B_SGI", + [RS_COLUMN_MIMO2] = "MIMO2", + [RS_COLUMN_MIMO2_SGI] = "MIMO2_SGI", + }; + + static const char * const rate_name[] = { + [IWL_RATE_1M_INDEX] = "1M", + [IWL_RATE_2M_INDEX] = "2M", + [IWL_RATE_5M_INDEX] = "5.5M", + [IWL_RATE_11M_INDEX] = "11M", + [IWL_RATE_6M_INDEX] = "6M|MCS0", + [IWL_RATE_9M_INDEX] = "9M", + [IWL_RATE_12M_INDEX] = "12M|MCS1", + [IWL_RATE_18M_INDEX] = "18M|MCS2", + [IWL_RATE_24M_INDEX] = "24M|MCS3", + [IWL_RATE_36M_INDEX] = "36M|MCS4", + [IWL_RATE_48M_INDEX] = "48M|MCS5", + [IWL_RATE_54M_INDEX] = "54M|MCS6", + [IWL_RATE_MCS_7_INDEX] = "MCS7", + [IWL_RATE_MCS_8_INDEX] = "MCS8", + [IWL_RATE_MCS_9_INDEX] = "MCS9", + }; + + char *buff, *pos, *endpos; + int col, rate; + ssize_t ret; + struct iwl_lq_sta *lq_sta = file->private_data; + struct rs_rate_stats *stats; + static const size_t bufsz = 1024; + + buff = kmalloc(bufsz, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + pos = buff; + endpos = pos + bufsz; + + pos += scnprintf(pos, endpos - pos, "COLUMN,"); + for (rate = 0; rate < IWL_RATE_COUNT; rate++) + pos += scnprintf(pos, endpos - pos, "%s,", rate_name[rate]); + pos += scnprintf(pos, endpos - pos, "\n"); + + for (col = 0; col < RS_COLUMN_COUNT; col++) { + pos += scnprintf(pos, endpos - pos, + "%s,", column_name[col]); + + for (rate = 0; rate < IWL_RATE_COUNT; rate++) { + stats = &(lq_sta->pers.tx_stats[col][rate]); + pos += scnprintf(pos, endpos - pos, + "%llu/%llu,", + stats->success, + stats->total); + } + pos += scnprintf(pos, endpos - pos, "\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); + kfree(buff); + return ret; +} + +static ssize_t rs_sta_dbgfs_drv_tx_stats_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_lq_sta *lq_sta = file->private_data; + memset(lq_sta->pers.tx_stats, 0, sizeof(lq_sta->pers.tx_stats)); + + return count; +} + +static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = { + .read = rs_sta_dbgfs_drv_tx_stats_read, + .write = rs_sta_dbgfs_drv_tx_stats_write, + .open = simple_open, + .llseek = default_llseek, +}; + +static ssize_t iwl_dbgfs_ss_force_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_lq_sta *lq_sta = file->private_data; + char buf[12]; + int bufsz = sizeof(buf); + int pos = 0; + static const char * const ss_force_name[] = { + [RS_SS_FORCE_NONE] = "none", + [RS_SS_FORCE_STBC] = "stbc", + [RS_SS_FORCE_BFER] = "bfer", + [RS_SS_FORCE_SISO] = "siso", + }; + + pos += scnprintf(buf+pos, bufsz-pos, "%s\n", + ss_force_name[lq_sta->pers.ss_force]); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = lq_sta->pers.drv; + int ret = 0; + + if (!strncmp("none", buf, 4)) { + lq_sta->pers.ss_force = RS_SS_FORCE_NONE; + } else if (!strncmp("siso", buf, 4)) { + lq_sta->pers.ss_force = RS_SS_FORCE_SISO; + } else if (!strncmp("stbc", buf, 4)) { + if (lq_sta->stbc_capable) { + lq_sta->pers.ss_force = RS_SS_FORCE_STBC; + } else { + IWL_ERR(mvm, + "can't force STBC. peer doesn't support\n"); + ret = -EINVAL; + } + } else if (!strncmp("bfer", buf, 4)) { + if (lq_sta->bfer_capable) { + lq_sta->pers.ss_force = RS_SS_FORCE_BFER; + } else { + IWL_ERR(mvm, + "can't force BFER. peer doesn't support\n"); + ret = -EINVAL; + } + } else { + IWL_ERR(mvm, "valid values none|siso|stbc|bfer\n"); + ret = -EINVAL; + } + return ret ?: count; +} + +#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_lq_sta) +#define MVM_DEBUGFS_ADD_FILE_RS(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, lq_sta, \ + &iwl_dbgfs_##name##_ops)) \ + goto err; \ + } while (0) + +MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32); + +static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir) +{ + struct iwl_lq_sta *lq_sta = priv_sta; + struct iwl_mvm_sta *mvmsta; + + mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta); + + if (!mvmsta->vif) + return; + + debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, + lq_sta, &rs_sta_dbgfs_scale_table_ops); + debugfs_create_file("rate_stats_table", S_IRUSR, dir, + lq_sta, &rs_sta_dbgfs_stats_table_ops); + debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir, + lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops); + debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, + &lq_sta->tx_agg_tid_en); + debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir, + &lq_sta->pers.dbg_fixed_txp_reduction); + + MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, S_IRUSR | S_IWUSR); + return; +err: + IWL_ERR((struct iwl_mvm *)mvm, "Can't create debugfs entity\n"); +} + +static void rs_remove_debugfs(void *mvm, void *mvm_sta) +{ +} +#endif + +/* + * Initialization of rate scaling information is done by driver after + * the station is added. Since mac80211 calls this function before a + * station is added we ignore it. + */ +static void rs_rate_init_stub(void *mvm_r, + struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *mvm_sta) +{ +} + +static const struct rate_control_ops rs_mvm_ops = { + .name = RS_NAME, + .tx_status = rs_mac80211_tx_status, + .get_rate = rs_get_rate, + .rate_init = rs_rate_init_stub, + .alloc = rs_alloc, + .free = rs_free, + .alloc_sta = rs_alloc_sta, + .free_sta = rs_free_sta, + .rate_update = rs_rate_update, +#ifdef CONFIG_MAC80211_DEBUGFS + .add_sta_debugfs = rs_add_debugfs, + .remove_sta_debugfs = rs_remove_debugfs, +#endif +}; + +int iwl_mvm_rate_control_register(void) +{ + return ieee80211_rate_control_register(&rs_mvm_ops); +} + +void iwl_mvm_rate_control_unregister(void) +{ + ieee80211_rate_control_unregister(&rs_mvm_ops); +} + +/** + * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable + * Tx protection, according to this request and previous requests, + * and send the LQ command. + * @mvmsta: The station + * @enable: Enable Tx protection? + */ +int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, + bool enable) +{ + struct iwl_lq_cmd *lq = &mvmsta->lq_sta.lq; + + lockdep_assert_held(&mvm->mutex); + + if (enable) { + if (mvmsta->tx_protection == 0) + lq->flags |= LQ_FLAG_USE_RTS_MSK; + mvmsta->tx_protection++; + } else { + mvmsta->tx_protection--; + if (mvmsta->tx_protection == 0) + lq->flags &= ~LQ_FLAG_USE_RTS_MSK; + } + + return iwl_mvm_send_lq_cmd(mvm, lq, false); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h new file mode 100644 index 000000000000..81314ad9ebe0 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h @@ -0,0 +1,392 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __rs_h__ +#define __rs_h__ + +#include + +#include "iwl-config.h" + +#include "fw-api.h" +#include "iwl-trans.h" + +struct iwl_rs_rate_info { + u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ + u8 plcp_ht_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ + u8 plcp_ht_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ + u8 plcp_vht_siso; + u8 plcp_vht_mimo2; + u8 prev_rs; /* previous rate used in rs algo */ + u8 next_rs; /* next rate used in rs algo */ +}; + +#define IWL_RATE_60M_PLCP 3 + +enum { + IWL_RATE_INVM_INDEX = IWL_RATE_COUNT, + IWL_RATE_INVALID = IWL_RATE_COUNT, +}; + +#define LINK_QUAL_MAX_RETRY_NUM 16 + +enum { + IWL_RATE_6M_INDEX_TABLE = 0, + IWL_RATE_9M_INDEX_TABLE, + IWL_RATE_12M_INDEX_TABLE, + IWL_RATE_18M_INDEX_TABLE, + IWL_RATE_24M_INDEX_TABLE, + IWL_RATE_36M_INDEX_TABLE, + IWL_RATE_48M_INDEX_TABLE, + IWL_RATE_54M_INDEX_TABLE, + IWL_RATE_1M_INDEX_TABLE, + IWL_RATE_2M_INDEX_TABLE, + IWL_RATE_5M_INDEX_TABLE, + IWL_RATE_11M_INDEX_TABLE, + IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1, +}; + +/* #define vs. enum to keep from defaulting to 'large integer' */ +#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX) +#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX) +#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX) +#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX) +#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX) +#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX) +#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX) +#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX) +#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX) +#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX) +#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX) +#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX) +#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) + + +/* uCode API values for HT/VHT bit rates */ +enum { + IWL_RATE_HT_SISO_MCS_0_PLCP = 0, + IWL_RATE_HT_SISO_MCS_1_PLCP = 1, + IWL_RATE_HT_SISO_MCS_2_PLCP = 2, + IWL_RATE_HT_SISO_MCS_3_PLCP = 3, + IWL_RATE_HT_SISO_MCS_4_PLCP = 4, + IWL_RATE_HT_SISO_MCS_5_PLCP = 5, + IWL_RATE_HT_SISO_MCS_6_PLCP = 6, + IWL_RATE_HT_SISO_MCS_7_PLCP = 7, + IWL_RATE_HT_MIMO2_MCS_0_PLCP = 0x8, + IWL_RATE_HT_MIMO2_MCS_1_PLCP = 0x9, + IWL_RATE_HT_MIMO2_MCS_2_PLCP = 0xA, + IWL_RATE_HT_MIMO2_MCS_3_PLCP = 0xB, + IWL_RATE_HT_MIMO2_MCS_4_PLCP = 0xC, + IWL_RATE_HT_MIMO2_MCS_5_PLCP = 0xD, + IWL_RATE_HT_MIMO2_MCS_6_PLCP = 0xE, + IWL_RATE_HT_MIMO2_MCS_7_PLCP = 0xF, + IWL_RATE_VHT_SISO_MCS_0_PLCP = 0, + IWL_RATE_VHT_SISO_MCS_1_PLCP = 1, + IWL_RATE_VHT_SISO_MCS_2_PLCP = 2, + IWL_RATE_VHT_SISO_MCS_3_PLCP = 3, + IWL_RATE_VHT_SISO_MCS_4_PLCP = 4, + IWL_RATE_VHT_SISO_MCS_5_PLCP = 5, + IWL_RATE_VHT_SISO_MCS_6_PLCP = 6, + IWL_RATE_VHT_SISO_MCS_7_PLCP = 7, + IWL_RATE_VHT_SISO_MCS_8_PLCP = 8, + IWL_RATE_VHT_SISO_MCS_9_PLCP = 9, + IWL_RATE_VHT_MIMO2_MCS_0_PLCP = 0x10, + IWL_RATE_VHT_MIMO2_MCS_1_PLCP = 0x11, + IWL_RATE_VHT_MIMO2_MCS_2_PLCP = 0x12, + IWL_RATE_VHT_MIMO2_MCS_3_PLCP = 0x13, + IWL_RATE_VHT_MIMO2_MCS_4_PLCP = 0x14, + IWL_RATE_VHT_MIMO2_MCS_5_PLCP = 0x15, + IWL_RATE_VHT_MIMO2_MCS_6_PLCP = 0x16, + IWL_RATE_VHT_MIMO2_MCS_7_PLCP = 0x17, + IWL_RATE_VHT_MIMO2_MCS_8_PLCP = 0x18, + IWL_RATE_VHT_MIMO2_MCS_9_PLCP = 0x19, + IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_VHT_SISO_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_VHT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_SISO_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_SISO_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_MIMO2_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_MIMO2_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, +}; + +#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) + +#define IWL_INVALID_VALUE -1 + +#define TPC_MAX_REDUCTION 15 +#define TPC_NO_REDUCTION 0 +#define TPC_INVALID 0xff + +#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) +#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) +#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) + +#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ + +/* load per tid defines for A-MPDU activation */ +#define IWL_AGG_TPT_THREHOLD 0 +#define IWL_AGG_ALL_TID 0xff + +enum iwl_table_type { + LQ_NONE, + LQ_LEGACY_G, /* legacy types */ + LQ_LEGACY_A, + LQ_HT_SISO, /* HT types */ + LQ_HT_MIMO2, + LQ_VHT_SISO, /* VHT types */ + LQ_VHT_MIMO2, + LQ_MAX, +}; + +struct rs_rate { + int index; + enum iwl_table_type type; + u8 ant; + u32 bw; + bool sgi; + bool ldpc; + bool stbc; + bool bfer; +}; + + +#define is_type_legacy(type) (((type) == LQ_LEGACY_G) || \ + ((type) == LQ_LEGACY_A)) +#define is_type_ht_siso(type) ((type) == LQ_HT_SISO) +#define is_type_ht_mimo2(type) ((type) == LQ_HT_MIMO2) +#define is_type_vht_siso(type) ((type) == LQ_VHT_SISO) +#define is_type_vht_mimo2(type) ((type) == LQ_VHT_MIMO2) +#define is_type_siso(type) (is_type_ht_siso(type) || is_type_vht_siso(type)) +#define is_type_mimo2(type) (is_type_ht_mimo2(type) || is_type_vht_mimo2(type)) +#define is_type_mimo(type) (is_type_mimo2(type)) +#define is_type_ht(type) (is_type_ht_siso(type) || is_type_ht_mimo2(type)) +#define is_type_vht(type) (is_type_vht_siso(type) || is_type_vht_mimo2(type)) +#define is_type_a_band(type) ((type) == LQ_LEGACY_A) +#define is_type_g_band(type) ((type) == LQ_LEGACY_G) + +#define is_legacy(rate) is_type_legacy((rate)->type) +#define is_ht_siso(rate) is_type_ht_siso((rate)->type) +#define is_ht_mimo2(rate) is_type_ht_mimo2((rate)->type) +#define is_vht_siso(rate) is_type_vht_siso((rate)->type) +#define is_vht_mimo2(rate) is_type_vht_mimo2((rate)->type) +#define is_siso(rate) is_type_siso((rate)->type) +#define is_mimo2(rate) is_type_mimo2((rate)->type) +#define is_mimo(rate) is_type_mimo((rate)->type) +#define is_ht(rate) is_type_ht((rate)->type) +#define is_vht(rate) is_type_vht((rate)->type) +#define is_a_band(rate) is_type_a_band((rate)->type) +#define is_g_band(rate) is_type_g_band((rate)->type) + +#define is_ht20(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_20) +#define is_ht40(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_40) +#define is_ht80(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_80) + +#define IWL_MAX_MCS_DISPLAY_SIZE 12 + +struct iwl_rate_mcs_info { + char mbps[IWL_MAX_MCS_DISPLAY_SIZE]; + char mcs[IWL_MAX_MCS_DISPLAY_SIZE]; +}; + +/** + * struct iwl_rate_scale_data -- tx success history for one rate + */ +struct iwl_rate_scale_data { + u64 data; /* bitmap of successful frames */ + s32 success_counter; /* number of frames successful */ + s32 success_ratio; /* per-cent * 128 */ + s32 counter; /* number of frames attempted */ + s32 average_tpt; /* success ratio * expected throughput */ +}; + +/* Possible Tx columns + * Tx Column = a combo of legacy/siso/mimo x antenna x SGI + */ +enum rs_column { + RS_COLUMN_LEGACY_ANT_A = 0, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_SISO_ANT_B_SGI, + RS_COLUMN_MIMO2, + RS_COLUMN_MIMO2_SGI, + + RS_COLUMN_LAST = RS_COLUMN_MIMO2_SGI, + RS_COLUMN_COUNT = RS_COLUMN_LAST + 1, + RS_COLUMN_INVALID, +}; + +enum rs_ss_force_opt { + RS_SS_FORCE_NONE = 0, + RS_SS_FORCE_STBC, + RS_SS_FORCE_BFER, + RS_SS_FORCE_SISO, +}; + +/* Packet stats per rate */ +struct rs_rate_stats { + u64 success; + u64 total; +}; + +/** + * struct iwl_scale_tbl_info -- tx params and success history for all rates + * + * There are two of these in struct iwl_lq_sta, + * one for "active", and one for "search". + */ +struct iwl_scale_tbl_info { + struct rs_rate rate; + enum rs_column column; + const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ + struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ + /* per txpower-reduction history */ + struct iwl_rate_scale_data tpc_win[TPC_MAX_REDUCTION + 1]; +}; + +enum { + RS_STATE_SEARCH_CYCLE_STARTED, + RS_STATE_SEARCH_CYCLE_ENDED, + RS_STATE_STAY_IN_COLUMN, +}; + +/** + * struct iwl_lq_sta -- driver's rate scaling private structure + * + * Pointer to this gets passed back and forth between driver and mac80211. + */ +struct iwl_lq_sta { + u8 active_tbl; /* index of active table, range 0-1 */ + u8 rs_state; /* RS_STATE_* */ + u8 search_better_tbl; /* 1: currently trying alternate mode */ + s32 last_tpt; + + /* The following determine when to search for a new mode */ + u32 table_count_limit; + u32 max_failure_limit; /* # failed frames before new search */ + u32 max_success_limit; /* # successful frames before new search */ + u32 table_count; + u32 total_failed; /* total failed frames, any/all rates */ + u32 total_success; /* total successful frames, any/all rates */ + u64 flush_timer; /* time staying in mode before new search */ + + u32 visited_columns; /* Bitmask marking which Tx columns were + * explored during a search cycle + */ + u64 last_tx; + bool is_vht; + bool ldpc; /* LDPC Rx is supported by the STA */ + bool stbc_capable; /* Tx STBC is supported by chip and Rx by STA */ + bool bfer_capable; /* Remote supports beamformee and we BFer */ + + enum ieee80211_band band; + + /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ + unsigned long active_legacy_rate; + unsigned long active_siso_rate; + unsigned long active_mimo2_rate; + + /* Highest rate per Tx mode */ + u8 max_legacy_rate_idx; + u8 max_siso_rate_idx; + u8 max_mimo2_rate_idx; + + /* Optimal rate based on RSSI and STA caps. + * Used only to reflect link speed to userspace. + */ + struct rs_rate optimal_rate; + unsigned long optimal_rate_mask; + const struct rs_init_rate_info *optimal_rates; + int optimal_nentries; + + u8 missed_rate_counter; + + struct iwl_lq_cmd lq; + struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ + u8 tx_agg_tid_en; + + /* last tx rate_n_flags */ + u32 last_rate_n_flags; + /* packets destined for this STA are aggregated */ + u8 is_agg; + + /* tx power reduce for this sta */ + int tpc_reduce; + + /* persistent fields - initialized only once - keep last! */ + struct lq_sta_pers { +#ifdef CONFIG_MAC80211_DEBUGFS + u32 dbg_fixed_rate; + u8 dbg_fixed_txp_reduction; + + /* force STBC/BFER/SISO for testing */ + enum rs_ss_force_opt ss_force; +#endif + u8 chains; + s8 chain_signal[IEEE80211_MAX_CHAINS]; + s8 last_rssi; + struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT]; + struct iwl_mvm *drv; + } pers; +}; + +/* Initialize station's rate scaling information after adding station */ +void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + enum ieee80211_band band, bool init); + +/* Notify RS about Tx status */ +void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + int tid, struct ieee80211_tx_info *info); + +/** + * iwl_rate_control_register - Register the rate control algorithm callbacks + * + * Since the rate control algorithm is hardware specific, there is no need + * or reason to place it as a stand alone module. The driver can call + * iwl_rate_control_register in order to register the rate control callbacks + * with the mac80211 subsystem. This should be performed prior to calling + * ieee80211_register_hw + * + */ +int iwl_mvm_rate_control_register(void); + +/** + * iwl_rate_control_unregister - Unregister the rate control callbacks + * + * This should be called after calling ieee80211_unregister_hw, but before + * the driver is unloaded. + */ +void iwl_mvm_rate_control_unregister(void); + +struct iwl_mvm_sta; + +int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, + bool enable); + +#endif /* __rs__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c new file mode 100644 index 000000000000..5b58f5320e8d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -0,0 +1,612 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#include +#include "iwl-trans.h" +#include "mvm.h" +#include "fw-api.h" + +/* + * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler + * + * Copies the phy information in mvm->last_phy_info, it will be used when the + * actual data will come from the fw in the next packet. + */ +void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info)); + mvm->ampdu_ref++; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { + spin_lock(&mvm->drv_stats_lock); + mvm->drv_rx_stats.ampdu_count++; + spin_unlock(&mvm->drv_stats_lock); + } +#endif +} + +/* + * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211 + * + * Adds the rxb to a new skb and give it to mac80211 + */ +static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, + struct napi_struct *napi, + struct sk_buff *skb, + struct ieee80211_hdr *hdr, u16 len, + u32 ampdu_status, u8 crypt_len, + struct iwl_rx_cmd_buffer *rxb) +{ + unsigned int hdrlen, fraglen; + + /* If frame is small enough to fit in skb->head, pull it completely. + * If not, only pull ieee80211_hdr (including crypto if present, and + * an additional 8 bytes for SNAP/ethertype, see below) so that + * splice() or TCP coalesce are more efficient. + * + * Since, in addition, ieee80211_data_to_8023() always pull in at + * least 8 bytes (possibly more for mesh) we can do the same here + * to save the cost of doing it later. That still doesn't pull in + * the actual IP header since the typical case has a SNAP header. + * If the latter changes (there are efforts in the standards group + * to do so) we should revisit this and ieee80211_data_to_8023(). + */ + hdrlen = (len <= skb_tailroom(skb)) ? len : + sizeof(*hdr) + crypt_len + 8; + + memcpy(skb_put(skb, hdrlen), hdr, hdrlen); + fraglen = len - hdrlen; + + if (fraglen) { + int offset = (void *)hdr + hdrlen - + rxb_addr(rxb) + rxb_offset(rxb); + + skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, + fraglen, rxb->truesize); + } + + ieee80211_rx_napi(mvm->hw, skb, napi); +} + +/* + * iwl_mvm_get_signal_strength - use new rx PHY INFO API + * values are reported by the fw as positive values - need to negate + * to obtain their dBM. Account for missing antennas by replacing 0 + * values by -256dBm: practically 0 power and a non-feasible 8 bit value. + */ +static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, + struct iwl_rx_phy_info *phy_info, + struct ieee80211_rx_status *rx_status) +{ + int energy_a, energy_b, energy_c, max_energy; + u32 val; + + val = + le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]); + energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >> + IWL_RX_INFO_ENERGY_ANT_A_POS; + energy_a = energy_a ? -energy_a : S8_MIN; + energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >> + IWL_RX_INFO_ENERGY_ANT_B_POS; + energy_b = energy_b ? -energy_b : S8_MIN; + energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >> + IWL_RX_INFO_ENERGY_ANT_C_POS; + energy_c = energy_c ? -energy_c : S8_MIN; + max_energy = max(energy_a, energy_b); + max_energy = max(max_energy, energy_c); + + IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n", + energy_a, energy_b, energy_c, max_energy); + + rx_status->signal = max_energy; + rx_status->chains = (le16_to_cpu(phy_info->phy_flags) & + RX_RES_PHY_FLAGS_ANTENNA) + >> RX_RES_PHY_FLAGS_ANTENNA_POS; + rx_status->chain_signal[0] = energy_a; + rx_status->chain_signal[1] = energy_b; + rx_status->chain_signal[2] = energy_c; +} + +/* + * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format + * @mvm: the mvm object + * @hdr: 80211 header + * @stats: status in mac80211's format + * @rx_pkt_status: status coming from fw + * + * returns non 0 value if the packet should be dropped + */ +static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, + struct ieee80211_hdr *hdr, + struct ieee80211_rx_status *stats, + u32 rx_pkt_status, + u8 *crypt_len) +{ + if (!ieee80211_has_protected(hdr->frame_control) || + (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == + RX_MPDU_RES_STATUS_SEC_NO_ENC) + return 0; + + /* packet was encrypted with unknown alg */ + if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == + RX_MPDU_RES_STATUS_SEC_ENC_ERR) + return 0; + + switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) { + case RX_MPDU_RES_STATUS_SEC_CCM_ENC: + /* alg is CCM: check MIC only */ + if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) + return -1; + + stats->flag |= RX_FLAG_DECRYPTED; + *crypt_len = IEEE80211_CCMP_HDR_LEN; + return 0; + + case RX_MPDU_RES_STATUS_SEC_TKIP_ENC: + /* Don't drop the frame and decrypt it in SW */ + if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK)) + return 0; + *crypt_len = IEEE80211_TKIP_IV_LEN; + /* fall through if TTAK OK */ + + case RX_MPDU_RES_STATUS_SEC_WEP_ENC: + if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK)) + return -1; + + stats->flag |= RX_FLAG_DECRYPTED; + if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == + RX_MPDU_RES_STATUS_SEC_WEP_ENC) + *crypt_len = IEEE80211_WEP_IV_LEN; + return 0; + + case RX_MPDU_RES_STATUS_SEC_EXT_ENC: + if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) + return -1; + stats->flag |= RX_FLAG_DECRYPTED; + return 0; + + default: + IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status); + } + + return 0; +} + +static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, + struct sk_buff *skb, + u32 status) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); + + if (mvmvif->features & NETIF_F_RXCSUM && + status & RX_MPDU_RES_STATUS_CSUM_DONE && + status & RX_MPDU_RES_STATUS_CSUM_OK) + skb->ip_summed = CHECKSUM_UNNECESSARY; +} + +/* + * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler + * + * Handles the actual data of the Rx packet from the fw + */ +void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb) +{ + struct ieee80211_hdr *hdr; + struct ieee80211_rx_status *rx_status; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_rx_phy_info *phy_info; + struct iwl_rx_mpdu_res_start *rx_res; + struct ieee80211_sta *sta; + struct sk_buff *skb; + u32 len; + u32 ampdu_status; + u32 rate_n_flags; + u32 rx_pkt_status; + u8 crypt_len = 0; + + phy_info = &mvm->last_phy_info; + rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data; + hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res)); + len = le16_to_cpu(rx_res->byte_count); + rx_pkt_status = le32_to_cpup((__le32 *) + (pkt->data + sizeof(*rx_res) + len)); + + /* Dont use dev_alloc_skb(), we'll have enough headroom once + * ieee80211_hdr pulled. + */ + skb = alloc_skb(128, GFP_ATOMIC); + if (!skb) { + IWL_ERR(mvm, "alloc_skb failed\n"); + return; + } + + rx_status = IEEE80211_SKB_RXCB(skb); + + /* + * drop the packet if it has failed being decrypted by HW + */ + if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status, + &crypt_len)) { + IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n", + rx_pkt_status); + kfree_skb(skb); + return; + } + + /* + * Keep packets with CRC errors (and with overrun) for monitor mode + * (otherwise the firmware discards them) but mark them as bad. + */ + if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) || + !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) { + IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + } + + /* This will be used in several places later */ + rate_n_flags = le32_to_cpu(phy_info->rate_n_flags); + + /* rx_status carries information about the packet to mac80211 */ + rx_status->mactime = le64_to_cpu(phy_info->timestamp); + rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp); + rx_status->band = + (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + rx_status->freq = + ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel), + rx_status->band); + /* + * TSF as indicated by the fw is at INA time, but mac80211 expects the + * TSF at the beginning of the MPDU. + */ + /*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/ + + iwl_mvm_get_signal_strength(mvm, phy_info, rx_status); + + IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal, + (unsigned long long)rx_status->mactime); + + rcu_read_lock(); + /* + * We have tx blocked stations (with CS bit). If we heard frames from + * a blocked station on a new channel we can TX to it again. + */ + if (unlikely(mvm->csa_tx_block_bcn_timeout)) { + sta = ieee80211_find_sta( + rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2); + if (sta) + iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); + } + + /* This is fine since we don't support multiple AP interfaces */ + sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); + if (sta) { + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + + rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); + + if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) && + ieee80211_is_beacon(hdr->frame_control)) { + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_low_rssi *rssi_trig; + bool trig_check; + s32 rssi; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, + FW_DBG_TRIGGER_RSSI); + rssi_trig = (void *)trig->data; + rssi = le32_to_cpu(rssi_trig->rssi); + + trig_check = + iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif, + trig); + if (trig_check && rx_status->signal < rssi) + iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); + } + } + + if (sta && ieee80211_is_data(hdr->frame_control)) + iwl_mvm_rx_csum(sta, skb, rx_pkt_status); + + rcu_read_unlock(); + + /* set the preamble flag if appropriate */ + if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE)) + rx_status->flag |= RX_FLAG_SHORTPRE; + + if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { + /* + * We know which subframes of an A-MPDU belong + * together since we get a single PHY response + * from the firmware for all of them + */ + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + rx_status->ampdu_reference = mvm->ampdu_ref; + } + + /* Set up the HT phy flags */ + switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + break; + case RATE_MCS_CHAN_WIDTH_40: + rx_status->flag |= RX_FLAG_40MHZ; + break; + case RATE_MCS_CHAN_WIDTH_80: + rx_status->vht_flag |= RX_VHT_FLAG_80MHZ; + break; + case RATE_MCS_CHAN_WIDTH_160: + rx_status->vht_flag |= RX_VHT_FLAG_160MHZ; + break; + } + if (rate_n_flags & RATE_MCS_SGI_MSK) + rx_status->flag |= RX_FLAG_SHORT_GI; + if (rate_n_flags & RATE_HT_MCS_GF_MSK) + rx_status->flag |= RX_FLAG_HT_GF; + if (rate_n_flags & RATE_MCS_LDPC_MSK) + rx_status->flag |= RX_FLAG_LDPC; + if (rate_n_flags & RATE_MCS_HT_MSK) { + u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> + RATE_MCS_STBC_POS; + rx_status->flag |= RX_FLAG_HT; + rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; + rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; + } else if (rate_n_flags & RATE_MCS_VHT_MSK) { + u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >> + RATE_MCS_STBC_POS; + rx_status->vht_nss = + ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> + RATE_VHT_MCS_NSS_POS) + 1; + rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; + rx_status->flag |= RX_FLAG_VHT; + rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; + if (rate_n_flags & RATE_MCS_BF_MSK) + rx_status->vht_flag |= RX_VHT_FLAG_BF; + } else { + rx_status->rate_idx = + iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, + rx_status->band); + } + +#ifdef CONFIG_IWLWIFI_DEBUGFS + iwl_mvm_update_frame_stats(mvm, rate_n_flags, + rx_status->flag & RX_FLAG_AMPDU_DETAILS); +#endif + iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status, + crypt_len, rxb); +} + +static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm, + struct mvm_statistics_rx *rx_stats) +{ + lockdep_assert_held(&mvm->mutex); + + mvm->rx_stats = *rx_stats; +} + +struct iwl_mvm_stat_data { + struct iwl_mvm *mvm; + __le32 mac_id; + u8 beacon_filter_average_energy; + struct mvm_statistics_general_v8 *general; +}; + +static void iwl_mvm_stat_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_stat_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + int sig = -data->beacon_filter_average_energy; + int last_event; + int thold = vif->bss_conf.cqm_rssi_thold; + int hyst = vif->bss_conf.cqm_rssi_hyst; + u16 id = le32_to_cpu(data->mac_id); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + /* This doesn't need the MAC ID check since it's not taking the + * data copied into the "data" struct, but rather the data from + * the notification directly. + */ + if (data->general) { + mvmvif->beacon_stats.num_beacons = + le32_to_cpu(data->general->beacon_counter[mvmvif->id]); + mvmvif->beacon_stats.avg_signal = + -data->general->beacon_average_energy[mvmvif->id]; + } + + if (mvmvif->id != id) + return; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (sig == 0) { + IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n"); + return; + } + + mvmvif->bf_data.ave_beacon_signal = sig; + + /* BT Coex */ + if (mvmvif->bf_data.bt_coex_min_thold != + mvmvif->bf_data.bt_coex_max_thold) { + last_event = mvmvif->bf_data.last_bt_coex_event; + if (sig > mvmvif->bf_data.bt_coex_max_thold && + (last_event <= mvmvif->bf_data.bt_coex_min_thold || + last_event == 0)) { + mvmvif->bf_data.last_bt_coex_event = sig; + IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n", + sig); + iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH); + } else if (sig < mvmvif->bf_data.bt_coex_min_thold && + (last_event >= mvmvif->bf_data.bt_coex_max_thold || + last_event == 0)) { + mvmvif->bf_data.last_bt_coex_event = sig; + IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n", + sig); + iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW); + } + } + + if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) + return; + + /* CQM Notification */ + last_event = mvmvif->bf_data.last_cqm_event; + if (thold && sig < thold && (last_event == 0 || + sig < last_event - hyst)) { + mvmvif->bf_data.last_cqm_event = sig; + IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n", + sig); + ieee80211_cqm_rssi_notify( + vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + GFP_KERNEL); + } else if (sig > thold && + (last_event == 0 || sig > last_event + hyst)) { + mvmvif->bf_data.last_cqm_event = sig; + IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n", + sig); + ieee80211_cqm_rssi_notify( + vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + GFP_KERNEL); + } +} + +static inline void +iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_stats *trig_stats; + u32 trig_offset, trig_thold; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_STATS)) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_STATS); + trig_stats = (void *)trig->data; + + if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) + return; + + trig_offset = le32_to_cpu(trig_stats->stop_offset); + trig_thold = le32_to_cpu(trig_stats->stop_threshold); + + if (WARN_ON_ONCE(trig_offset >= iwl_rx_packet_payload_len(pkt))) + return; + + if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold) + return; + + iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); +} + +void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, + struct iwl_rx_packet *pkt) +{ + struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data; + struct iwl_mvm_stat_data data = { + .mvm = mvm, + }; + u32 temperature; + + if (iwl_rx_packet_payload_len(pkt) != sizeof(*stats)) + goto invalid; + + temperature = le32_to_cpu(stats->general.radio_temperature); + data.mac_id = stats->rx.general.mac_id; + data.beacon_filter_average_energy = + stats->general.beacon_filter_average_energy; + + iwl_mvm_update_rx_statistics(mvm, &stats->rx); + + mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time); + mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time); + mvm->radio_stats.on_time_rf = + le64_to_cpu(stats->general.on_time_rf); + mvm->radio_stats.on_time_scan = + le64_to_cpu(stats->general.on_time_scan); + + data.general = &stats->general; + + iwl_mvm_rx_stats_check_trigger(mvm, pkt); + + ieee80211_iterate_active_interfaces(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_stat_iterator, + &data); + return; + invalid: + IWL_ERR(mvm, "received invalid statistics size (%d)!\n", + iwl_rx_packet_payload_len(pkt)); +} + +void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) +{ + iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb)); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c new file mode 100644 index 000000000000..d6e0c1b5c20c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -0,0 +1,1552 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include + +#include "mvm.h" +#include "fw-api-scan.h" + +#define IWL_DENSE_EBS_SCAN_RATIO 5 +#define IWL_SPARSE_EBS_SCAN_RATIO 1 + +enum iwl_mvm_scan_type { + IWL_SCAN_TYPE_UNASSOC, + IWL_SCAN_TYPE_WILD, + IWL_SCAN_TYPE_MILD, + IWL_SCAN_TYPE_FRAGMENTED, +}; + +enum iwl_mvm_traffic_load { + IWL_MVM_TRAFFIC_LOW, + IWL_MVM_TRAFFIC_MEDIUM, + IWL_MVM_TRAFFIC_HIGH, +}; + +struct iwl_mvm_scan_timing_params { + u32 dwell_active; + u32 dwell_passive; + u32 dwell_fragmented; + u32 suspend_time; + u32 max_out_time; +}; + +static struct iwl_mvm_scan_timing_params scan_timing[] = { + [IWL_SCAN_TYPE_UNASSOC] = { + .dwell_active = 10, + .dwell_passive = 110, + .dwell_fragmented = 44, + .suspend_time = 0, + .max_out_time = 0, + }, + [IWL_SCAN_TYPE_WILD] = { + .dwell_active = 10, + .dwell_passive = 110, + .dwell_fragmented = 44, + .suspend_time = 30, + .max_out_time = 120, + }, + [IWL_SCAN_TYPE_MILD] = { + .dwell_active = 10, + .dwell_passive = 110, + .dwell_fragmented = 44, + .suspend_time = 120, + .max_out_time = 120, + }, + [IWL_SCAN_TYPE_FRAGMENTED] = { + .dwell_active = 10, + .dwell_passive = 110, + .dwell_fragmented = 44, + .suspend_time = 95, + .max_out_time = 44, + }, +}; + +struct iwl_mvm_scan_params { + enum iwl_mvm_scan_type type; + u32 n_channels; + u16 delay; + int n_ssids; + struct cfg80211_ssid *ssids; + struct ieee80211_channel **channels; + u32 flags; + u8 *mac_addr; + u8 *mac_addr_mask; + bool no_cck; + bool pass_all; + int n_match_sets; + struct iwl_scan_probe_req preq; + struct cfg80211_match_set *match_sets; + int n_scan_plans; + struct cfg80211_sched_scan_plan *scan_plans; +}; + +static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm) +{ + if (mvm->scan_rx_ant != ANT_NONE) + return mvm->scan_rx_ant; + return iwl_mvm_get_valid_rx_ant(mvm); +} + +static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) +{ + u16 rx_chain; + u8 rx_ant; + + rx_ant = iwl_mvm_scan_rx_ant(mvm); + rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS; + rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; + rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS; + rx_chain |= 0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS; + return cpu_to_le16(rx_chain); +} + +static __le32 iwl_mvm_scan_rxon_flags(enum ieee80211_band band) +{ + if (band == IEEE80211_BAND_2GHZ) + return cpu_to_le32(PHY_BAND_24); + else + return cpu_to_le32(PHY_BAND_5); +} + +static inline __le32 +iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band, + bool no_cck) +{ + u32 tx_ant; + + mvm->scan_last_antenna_idx = + iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), + mvm->scan_last_antenna_idx); + tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS; + + if (band == IEEE80211_BAND_2GHZ && !no_cck) + return cpu_to_le32(IWL_RATE_1M_PLCP | RATE_MCS_CCK_MSK | + tx_ant); + else + return cpu_to_le32(IWL_RATE_6M_PLCP | tx_ant); +} + +static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int *global_cnt = data; + + if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt && + mvmvif->phy_ctxt->id < MAX_PHYS) + *global_cnt += 1; +} + +static enum iwl_mvm_traffic_load iwl_mvm_get_traffic_load(struct iwl_mvm *mvm) +{ + return IWL_MVM_TRAFFIC_LOW; +} + +static enum +iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mvm_scan_params *params) +{ + int global_cnt = 0; + enum iwl_mvm_traffic_load load; + bool low_latency; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_scan_condition_iterator, + &global_cnt); + if (!global_cnt) + return IWL_SCAN_TYPE_UNASSOC; + + load = iwl_mvm_get_traffic_load(mvm); + low_latency = iwl_mvm_low_latency(mvm); + + if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) && + vif->type != NL80211_IFTYPE_P2P_DEVICE && + fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAGMENTED_SCAN)) + return IWL_SCAN_TYPE_FRAGMENTED; + + if (load >= IWL_MVM_TRAFFIC_MEDIUM || low_latency) + return IWL_SCAN_TYPE_MILD; + + return IWL_SCAN_TYPE_WILD; +} + +static inline bool iwl_mvm_rrm_scan_needed(struct iwl_mvm *mvm) +{ + /* require rrm scan whenever the fw supports it */ + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT); +} + +static int iwl_mvm_max_scan_ie_fw_cmd_room(struct iwl_mvm *mvm) +{ + int max_probe_len; + + max_probe_len = SCAN_OFFLOAD_PROBE_REQ_SIZE; + + /* we create the 802.11 header and SSID element */ + max_probe_len -= 24 + 2; + + /* DS parameter set element is added on 2.4GHZ band if required */ + if (iwl_mvm_rrm_scan_needed(mvm)) + max_probe_len -= 3; + + return max_probe_len; +} + +int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm) +{ + int max_ie_len = iwl_mvm_max_scan_ie_fw_cmd_room(mvm); + + /* TODO: [BUG] This function should return the maximum allowed size of + * scan IEs, however the LMAC scan api contains both 2GHZ and 5GHZ IEs + * in the same command. So the correct implementation of this function + * is just iwl_mvm_max_scan_ie_fw_cmd_room() / 2. Currently the scan + * command has only 512 bytes and it would leave us with about 240 + * bytes for scan IEs, which is clearly not enough. So meanwhile + * we will report an incorrect value. This may result in a failure to + * issue a scan in unified_scan_lmac and unified_sched_scan_lmac + * functions with -ENOBUFS, if a large enough probe will be provided. + */ + return max_ie_len; +} + +static u8 *iwl_mvm_dump_channel_list(struct iwl_scan_results_notif *res, + int num_res, u8 *buf, size_t buf_size) +{ + int i; + u8 *pos = buf, *end = buf + buf_size; + + for (i = 0; pos < end && i < num_res; i++) + pos += snprintf(pos, end - pos, " %u", res[i].channel); + + /* terminate the string in case the buffer was too short */ + *(buf + buf_size - 1) = '\0'; + + return buf; +} + +void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_lmac_scan_complete_notif *notif = (void *)pkt->data; + u8 buf[256]; + + IWL_DEBUG_SCAN(mvm, + "Scan offload iteration complete: status=0x%x scanned channels=%d channels list: %s\n", + notif->status, notif->scanned_channels, + iwl_mvm_dump_channel_list(notif->results, + notif->scanned_channels, buf, + sizeof(buf))); +} + +void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n"); + ieee80211_sched_scan_results(mvm->hw); +} + +static const char *iwl_mvm_ebs_status_str(enum iwl_scan_ebs_status status) +{ + switch (status) { + case IWL_SCAN_EBS_SUCCESS: + return "successful"; + case IWL_SCAN_EBS_INACTIVE: + return "inactive"; + case IWL_SCAN_EBS_FAILED: + case IWL_SCAN_EBS_CHAN_NOT_FOUND: + default: + return "failed"; + } +} + +void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_periodic_scan_complete *scan_notif = (void *)pkt->data; + bool aborted = (scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED); + + /* scan status must be locked for proper checking */ + lockdep_assert_held(&mvm->mutex); + + /* We first check if we were stopping a scan, in which case we + * just clear the stopping flag. Then we check if it was a + * firmware initiated stop, in which case we need to inform + * mac80211. + * Note that we can have a stopping and a running scan + * simultaneously, but we can't have two different types of + * scans stopping or running at the same time (since LMAC + * doesn't support it). + */ + + if (mvm->scan_status & IWL_MVM_SCAN_STOPPING_SCHED) { + WARN_ON_ONCE(mvm->scan_status & IWL_MVM_SCAN_STOPPING_REGULAR); + + IWL_DEBUG_SCAN(mvm, "Scheduled scan %s, EBS status %s\n", + aborted ? "aborted" : "completed", + iwl_mvm_ebs_status_str(scan_notif->ebs_status)); + IWL_DEBUG_SCAN(mvm, + "Last line %d, Last iteration %d, Time after last iteration %d\n", + scan_notif->last_schedule_line, + scan_notif->last_schedule_iteration, + __le32_to_cpu(scan_notif->time_after_last_iter)); + + mvm->scan_status &= ~IWL_MVM_SCAN_STOPPING_SCHED; + } else if (mvm->scan_status & IWL_MVM_SCAN_STOPPING_REGULAR) { + IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s\n", + aborted ? "aborted" : "completed", + iwl_mvm_ebs_status_str(scan_notif->ebs_status)); + + mvm->scan_status &= ~IWL_MVM_SCAN_STOPPING_REGULAR; + } else if (mvm->scan_status & IWL_MVM_SCAN_SCHED) { + WARN_ON_ONCE(mvm->scan_status & IWL_MVM_SCAN_REGULAR); + + IWL_DEBUG_SCAN(mvm, "Scheduled scan %s, EBS status %s\n", + aborted ? "aborted" : "completed", + iwl_mvm_ebs_status_str(scan_notif->ebs_status)); + IWL_DEBUG_SCAN(mvm, + "Last line %d, Last iteration %d, Time after last iteration %d (FW)\n", + scan_notif->last_schedule_line, + scan_notif->last_schedule_iteration, + __le32_to_cpu(scan_notif->time_after_last_iter)); + + mvm->scan_status &= ~IWL_MVM_SCAN_SCHED; + ieee80211_sched_scan_stopped(mvm->hw); + } else if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) { + IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s (FW)\n", + aborted ? "aborted" : "completed", + iwl_mvm_ebs_status_str(scan_notif->ebs_status)); + + mvm->scan_status &= ~IWL_MVM_SCAN_REGULAR; + ieee80211_scan_completed(mvm->hw, + scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED); + iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); + } + + mvm->last_ebs_successful = + scan_notif->ebs_status == IWL_SCAN_EBS_SUCCESS || + scan_notif->ebs_status == IWL_SCAN_EBS_INACTIVE; +} + +static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list) +{ + int i; + + for (i = 0; i < PROBE_OPTION_MAX; i++) { + if (!ssid_list[i].len) + break; + if (ssid_list[i].len == ssid_len && + !memcmp(ssid_list->ssid, ssid, ssid_len)) + return i; + } + return -1; +} + +/* We insert the SSIDs in an inverted order, because the FW will + * invert it back. + */ +static void iwl_scan_build_ssids(struct iwl_mvm_scan_params *params, + struct iwl_ssid_ie *ssids, + u32 *ssid_bitmap) +{ + int i, j; + int index; + + /* + * copy SSIDs from match list. + * iwl_config_sched_scan_profiles() uses the order of these ssids to + * config match list. + */ + for (i = 0, j = params->n_match_sets - 1; + j >= 0 && i < PROBE_OPTION_MAX; + i++, j--) { + /* skip empty SSID matchsets */ + if (!params->match_sets[j].ssid.ssid_len) + continue; + ssids[i].id = WLAN_EID_SSID; + ssids[i].len = params->match_sets[j].ssid.ssid_len; + memcpy(ssids[i].ssid, params->match_sets[j].ssid.ssid, + ssids[i].len); + } + + /* add SSIDs from scan SSID list */ + *ssid_bitmap = 0; + for (j = params->n_ssids - 1; + j >= 0 && i < PROBE_OPTION_MAX; + i++, j--) { + index = iwl_ssid_exist(params->ssids[j].ssid, + params->ssids[j].ssid_len, + ssids); + if (index < 0) { + ssids[i].id = WLAN_EID_SSID; + ssids[i].len = params->ssids[j].ssid_len; + memcpy(ssids[i].ssid, params->ssids[j].ssid, + ssids[i].len); + *ssid_bitmap |= BIT(i); + } else { + *ssid_bitmap |= BIT(index); + } + } +} + +static int +iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, + struct cfg80211_sched_scan_request *req) +{ + struct iwl_scan_offload_profile *profile; + struct iwl_scan_offload_profile_cfg *profile_cfg; + struct iwl_scan_offload_blacklist *blacklist; + struct iwl_host_cmd cmd = { + .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD, + .len[1] = sizeof(*profile_cfg), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + .dataflags[1] = IWL_HCMD_DFL_NOCOPY, + }; + int blacklist_len; + int i; + int ret; + + if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES)) + return -EIO; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL) + blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN; + else + blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN; + + blacklist = kzalloc(sizeof(*blacklist) * blacklist_len, GFP_KERNEL); + if (!blacklist) + return -ENOMEM; + + profile_cfg = kzalloc(sizeof(*profile_cfg), GFP_KERNEL); + if (!profile_cfg) { + ret = -ENOMEM; + goto free_blacklist; + } + + cmd.data[0] = blacklist; + cmd.len[0] = sizeof(*blacklist) * blacklist_len; + cmd.data[1] = profile_cfg; + + /* No blacklist configuration */ + + profile_cfg->num_profiles = req->n_match_sets; + profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN; + profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN; + profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN; + if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len) + profile_cfg->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN; + + for (i = 0; i < req->n_match_sets; i++) { + profile = &profile_cfg->profiles[i]; + profile->ssid_index = i; + /* Support any cipher and auth algorithm */ + profile->unicast_cipher = 0xff; + profile->auth_alg = 0xff; + profile->network_type = IWL_NETWORK_TYPE_ANY; + profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY; + profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN; + } + + IWL_DEBUG_SCAN(mvm, "Sending scheduled scan profile config\n"); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + kfree(profile_cfg); +free_blacklist: + kfree(blacklist); + + return ret; +} + +static bool iwl_mvm_scan_pass_all(struct iwl_mvm *mvm, + struct cfg80211_sched_scan_request *req) +{ + if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { + IWL_DEBUG_SCAN(mvm, + "Sending scheduled scan with filtering, n_match_sets %d\n", + req->n_match_sets); + return false; + } + + IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n"); + return true; +} + +static int iwl_mvm_lmac_scan_abort(struct iwl_mvm *mvm) +{ + int ret; + struct iwl_host_cmd cmd = { + .id = SCAN_OFFLOAD_ABORT_CMD, + }; + u32 status; + + ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status); + if (ret) + return ret; + + if (status != CAN_ABORT_STATUS) { + /* + * The scan abort will return 1 for success or + * 2 for "failure". A failure condition can be + * due to simply not being in an active scan which + * can occur if we send the scan abort before the + * microcode has notified us that a scan is completed. + */ + IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status); + ret = -ENOENT; + } + + return ret; +} + +static void iwl_mvm_scan_fill_tx_cmd(struct iwl_mvm *mvm, + struct iwl_scan_req_tx_cmd *tx_cmd, + bool no_cck) +{ + tx_cmd[0].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | + TX_CMD_FLG_BT_DIS); + tx_cmd[0].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, + IEEE80211_BAND_2GHZ, + no_cck); + tx_cmd[0].sta_id = mvm->aux_sta.sta_id; + + tx_cmd[1].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | + TX_CMD_FLG_BT_DIS); + tx_cmd[1].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, + IEEE80211_BAND_5GHZ, + no_cck); + tx_cmd[1].sta_id = mvm->aux_sta.sta_id; +} + +static void +iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm, + struct ieee80211_channel **channels, + int n_channels, u32 ssid_bitmap, + struct iwl_scan_req_lmac *cmd) +{ + struct iwl_scan_channel_cfg_lmac *channel_cfg = (void *)&cmd->data; + int i; + + for (i = 0; i < n_channels; i++) { + channel_cfg[i].channel_num = + cpu_to_le16(channels[i]->hw_value); + channel_cfg[i].iter_count = cpu_to_le16(1); + channel_cfg[i].iter_interval = 0; + channel_cfg[i].flags = + cpu_to_le32(IWL_UNIFIED_SCAN_CHANNEL_PARTIAL | + ssid_bitmap); + } +} + +static u8 *iwl_mvm_copy_and_insert_ds_elem(struct iwl_mvm *mvm, const u8 *ies, + size_t len, u8 *const pos) +{ + static const u8 before_ds_params[] = { + WLAN_EID_SSID, + WLAN_EID_SUPP_RATES, + WLAN_EID_REQUEST, + WLAN_EID_EXT_SUPP_RATES, + }; + size_t offs; + u8 *newpos = pos; + + if (!iwl_mvm_rrm_scan_needed(mvm)) { + memcpy(newpos, ies, len); + return newpos + len; + } + + offs = ieee80211_ie_split(ies, len, + before_ds_params, + ARRAY_SIZE(before_ds_params), + 0); + + memcpy(newpos, ies, offs); + newpos += offs; + + /* Add a placeholder for DS Parameter Set element */ + *newpos++ = WLAN_EID_DS_PARAMS; + *newpos++ = 1; + *newpos++ = 0; + + memcpy(newpos, ies + offs, len - offs); + newpos += len - offs; + + return newpos; +} + +static void +iwl_mvm_build_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_scan_ies *ies, + struct iwl_mvm_scan_params *params) +{ + struct ieee80211_mgmt *frame = (void *)params->preq.buf; + u8 *pos, *newpos; + const u8 *mac_addr = params->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ? + params->mac_addr : NULL; + + /* + * Unfortunately, right now the offload scan doesn't support randomising + * within the firmware, so until the firmware API is ready we implement + * it in the driver. This means that the scan iterations won't really be + * random, only when it's restarted, but at least that helps a bit. + */ + if (mac_addr) + get_random_mask_addr(frame->sa, mac_addr, + params->mac_addr_mask); + else + memcpy(frame->sa, vif->addr, ETH_ALEN); + + frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); + eth_broadcast_addr(frame->da); + eth_broadcast_addr(frame->bssid); + frame->seq_ctrl = 0; + + pos = frame->u.probe_req.variable; + *pos++ = WLAN_EID_SSID; + *pos++ = 0; + + params->preq.mac_header.offset = 0; + params->preq.mac_header.len = cpu_to_le16(24 + 2); + + /* Insert ds parameter set element on 2.4 GHz band */ + newpos = iwl_mvm_copy_and_insert_ds_elem(mvm, + ies->ies[IEEE80211_BAND_2GHZ], + ies->len[IEEE80211_BAND_2GHZ], + pos); + params->preq.band_data[0].offset = cpu_to_le16(pos - params->preq.buf); + params->preq.band_data[0].len = cpu_to_le16(newpos - pos); + pos = newpos; + + memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ], + ies->len[IEEE80211_BAND_5GHZ]); + params->preq.band_data[1].offset = cpu_to_le16(pos - params->preq.buf); + params->preq.band_data[1].len = + cpu_to_le16(ies->len[IEEE80211_BAND_5GHZ]); + pos += ies->len[IEEE80211_BAND_5GHZ]; + + memcpy(pos, ies->common_ies, ies->common_ie_len); + params->preq.common_data.offset = cpu_to_le16(pos - params->preq.buf); + params->preq.common_data.len = cpu_to_le16(ies->common_ie_len); +} + +static __le32 iwl_mvm_scan_priority(struct iwl_mvm *mvm, + enum iwl_scan_priority_ext prio) +{ + if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY)) + return cpu_to_le32(prio); + + if (prio <= IWL_SCAN_PRIORITY_EXT_2) + return cpu_to_le32(IWL_SCAN_PRIORITY_LOW); + + if (prio <= IWL_SCAN_PRIORITY_EXT_4) + return cpu_to_le32(IWL_SCAN_PRIORITY_MEDIUM); + + return cpu_to_le32(IWL_SCAN_PRIORITY_HIGH); +} + +static void iwl_mvm_scan_lmac_dwell(struct iwl_mvm *mvm, + struct iwl_scan_req_lmac *cmd, + struct iwl_mvm_scan_params *params) +{ + cmd->active_dwell = scan_timing[params->type].dwell_active; + cmd->passive_dwell = scan_timing[params->type].dwell_passive; + cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented; + cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time); + cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time); + cmd->scan_prio = iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); +} + +static inline bool iwl_mvm_scan_fits(struct iwl_mvm *mvm, int n_ssids, + struct ieee80211_scan_ies *ies, + int n_channels) +{ + return ((n_ssids <= PROBE_OPTION_MAX) && + (n_channels <= mvm->fw->ucode_capa.n_scan_channels) & + (ies->common_ie_len + + ies->len[NL80211_BAND_2GHZ] + + ies->len[NL80211_BAND_5GHZ] <= + iwl_mvm_max_scan_ie_fw_cmd_room(mvm))); +} + +static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + const struct iwl_ucode_capabilities *capa = &mvm->fw->ucode_capa; + + /* We can only use EBS if: + * 1. the feature is supported; + * 2. the last EBS was successful; + * 3. if only single scan, the single scan EBS API is supported; + * 4. it's not a p2p find operation. + */ + return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) && + mvm->last_ebs_successful && + vif->type != NL80211_IFTYPE_P2P_DEVICE); +} + +static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm, + struct iwl_mvm_scan_params *params) +{ + int flags = 0; + + if (params->n_ssids == 0) + flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE; + + if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0) + flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION; + + if (params->type == IWL_SCAN_TYPE_FRAGMENTED) + flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED; + + if (iwl_mvm_rrm_scan_needed(mvm)) + flags |= IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED; + + if (params->pass_all) + flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL; + else + flags |= IWL_MVM_LMAC_SCAN_FLAG_MATCH; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvm->scan_iter_notif_enabled) + flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE; +#endif + + return flags; +} + +static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct iwl_mvm_scan_params *params) +{ + struct iwl_scan_req_lmac *cmd = mvm->scan_cmd; + struct iwl_scan_probe_req *preq = + (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) * + mvm->fw->ucode_capa.n_scan_channels); + u32 ssid_bitmap = 0; + int i; + + lockdep_assert_held(&mvm->mutex); + + memset(cmd, 0, ksize(cmd)); + + if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) + return -EINVAL; + + iwl_mvm_scan_lmac_dwell(mvm, cmd, params); + + cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm); + cmd->iter_num = cpu_to_le32(1); + cmd->n_channels = (u8)params->n_channels; + + cmd->delay = cpu_to_le32(params->delay); + + cmd->scan_flags = cpu_to_le32(iwl_mvm_scan_lmac_flags(mvm, params)); + + cmd->flags = iwl_mvm_scan_rxon_flags(params->channels[0]->band); + cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | + MAC_FILTER_IN_BEACON); + iwl_mvm_scan_fill_tx_cmd(mvm, cmd->tx_cmd, params->no_cck); + iwl_scan_build_ssids(params, cmd->direct_scan, &ssid_bitmap); + + /* this API uses bits 1-20 instead of 0-19 */ + ssid_bitmap <<= 1; + + for (i = 0; i < params->n_scan_plans; i++) { + struct cfg80211_sched_scan_plan *scan_plan = + ¶ms->scan_plans[i]; + + cmd->schedule[i].delay = + cpu_to_le16(scan_plan->interval); + cmd->schedule[i].iterations = scan_plan->iterations; + cmd->schedule[i].full_scan_mul = 1; + } + + /* + * If the number of iterations of the last scan plan is set to + * zero, it should run infinitely. However, this is not always the case. + * For example, when regular scan is requested the driver sets one scan + * plan with one iteration. + */ + if (!cmd->schedule[i - 1].iterations) + cmd->schedule[i - 1].iterations = 0xff; + + if (iwl_mvm_scan_use_ebs(mvm, vif)) { + cmd->channel_opt[0].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[0].non_ebs_ratio = + cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO); + cmd->channel_opt[1].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[1].non_ebs_ratio = + cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO); + } + + iwl_mvm_lmac_scan_cfg_channels(mvm, params->channels, + params->n_channels, ssid_bitmap, cmd); + + *preq = params->preq; + + return 0; +} + +static int rate_to_scan_rate_flag(unsigned int rate) +{ + static const int rate_to_scan_rate[IWL_RATE_COUNT] = { + [IWL_RATE_1M_INDEX] = SCAN_CONFIG_RATE_1M, + [IWL_RATE_2M_INDEX] = SCAN_CONFIG_RATE_2M, + [IWL_RATE_5M_INDEX] = SCAN_CONFIG_RATE_5M, + [IWL_RATE_11M_INDEX] = SCAN_CONFIG_RATE_11M, + [IWL_RATE_6M_INDEX] = SCAN_CONFIG_RATE_6M, + [IWL_RATE_9M_INDEX] = SCAN_CONFIG_RATE_9M, + [IWL_RATE_12M_INDEX] = SCAN_CONFIG_RATE_12M, + [IWL_RATE_18M_INDEX] = SCAN_CONFIG_RATE_18M, + [IWL_RATE_24M_INDEX] = SCAN_CONFIG_RATE_24M, + [IWL_RATE_36M_INDEX] = SCAN_CONFIG_RATE_36M, + [IWL_RATE_48M_INDEX] = SCAN_CONFIG_RATE_48M, + [IWL_RATE_54M_INDEX] = SCAN_CONFIG_RATE_54M, + }; + + return rate_to_scan_rate[rate]; +} + +static __le32 iwl_mvm_scan_config_rates(struct iwl_mvm *mvm) +{ + struct ieee80211_supported_band *band; + unsigned int rates = 0; + int i; + + band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; + for (i = 0; i < band->n_bitrates; i++) + rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value); + band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; + for (i = 0; i < band->n_bitrates; i++) + rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value); + + /* Set both basic rates and supported rates */ + rates |= SCAN_CONFIG_SUPPORTED_RATE(rates); + + return cpu_to_le32(rates); +} + +int iwl_mvm_config_scan(struct iwl_mvm *mvm) +{ + struct iwl_scan_config *scan_config; + struct ieee80211_supported_band *band; + int num_channels = + mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels + + mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; + int ret, i, j = 0, cmd_size; + struct iwl_host_cmd cmd = { + .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0), + }; + + if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels)) + return -ENOBUFS; + + cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels; + + scan_config = kzalloc(cmd_size, GFP_KERNEL); + if (!scan_config) + return -ENOMEM; + + scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE | + SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS | + SCAN_CONFIG_FLAG_SET_TX_CHAINS | + SCAN_CONFIG_FLAG_SET_RX_CHAINS | + SCAN_CONFIG_FLAG_SET_ALL_TIMES | + SCAN_CONFIG_FLAG_SET_LEGACY_RATES | + SCAN_CONFIG_FLAG_SET_MAC_ADDR | + SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS| + SCAN_CONFIG_N_CHANNELS(num_channels)); + scan_config->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); + scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm)); + scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm); + scan_config->out_of_channel_time = cpu_to_le32(170); + scan_config->suspend_time = cpu_to_le32(30); + scan_config->dwell_active = 20; + scan_config->dwell_passive = 110; + scan_config->dwell_fragmented = 20; + + memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); + + scan_config->bcast_sta_id = mvm->aux_sta.sta_id; + scan_config->channel_flags = IWL_CHANNEL_FLAG_EBS | + IWL_CHANNEL_FLAG_ACCURATE_EBS | + IWL_CHANNEL_FLAG_EBS_ADD | + IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE; + + band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; + for (i = 0; i < band->n_channels; i++, j++) + scan_config->channel_array[j] = band->channels[i].hw_value; + band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; + for (i = 0; i < band->n_channels; i++, j++) + scan_config->channel_array[j] = band->channels[i].hw_value; + + cmd.data[0] = scan_config; + cmd.len[0] = cmd_size; + cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + + IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n"); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(scan_config); + return ret; +} + +static int iwl_mvm_scan_uid_by_status(struct iwl_mvm *mvm, int status) +{ + int i; + + for (i = 0; i < mvm->max_scans; i++) + if (mvm->scan_uid_status[i] == status) + return i; + + return -ENOENT; +} + +static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params) +{ + return params->n_scan_plans == 1 && + params->scan_plans[0].iterations == 1; +} + +static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, + struct iwl_scan_req_umac *cmd, + struct iwl_mvm_scan_params *params) +{ + cmd->active_dwell = scan_timing[params->type].dwell_active; + cmd->passive_dwell = scan_timing[params->type].dwell_passive; + cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented; + cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time); + cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time); + cmd->scan_priority = + iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); + + if (iwl_mvm_is_regular_scan(params)) + cmd->ooc_priority = + iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); + else + cmd->ooc_priority = + iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_2); +} + +static void +iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm, + struct ieee80211_channel **channels, + int n_channels, u32 ssid_bitmap, + struct iwl_scan_req_umac *cmd) +{ + struct iwl_scan_channel_cfg_umac *channel_cfg = (void *)&cmd->data; + int i; + + for (i = 0; i < n_channels; i++) { + channel_cfg[i].flags = cpu_to_le32(ssid_bitmap); + channel_cfg[i].channel_num = channels[i]->hw_value; + channel_cfg[i].iter_count = 1; + channel_cfg[i].iter_interval = 0; + } +} + +static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm, + struct iwl_mvm_scan_params *params) +{ + int flags = 0; + + if (params->n_ssids == 0) + flags = IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE; + + if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT; + + if (params->type == IWL_SCAN_TYPE_FRAGMENTED) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED; + + if (iwl_mvm_rrm_scan_needed(mvm)) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED; + + if (params->pass_all) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL; + else + flags |= IWL_UMAC_SCAN_GEN_FLAGS_MATCH; + + if (!iwl_mvm_is_regular_scan(params)) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvm->scan_iter_notif_enabled) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE; +#endif + return flags; +} + +static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct iwl_mvm_scan_params *params, + int type) +{ + struct iwl_scan_req_umac *cmd = mvm->scan_cmd; + struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data + + sizeof(struct iwl_scan_channel_cfg_umac) * + mvm->fw->ucode_capa.n_scan_channels; + int uid, i; + u32 ssid_bitmap = 0; + + lockdep_assert_held(&mvm->mutex); + + if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) + return -EINVAL; + + uid = iwl_mvm_scan_uid_by_status(mvm, 0); + if (uid < 0) + return uid; + + memset(cmd, 0, ksize(cmd)); + + iwl_mvm_scan_umac_dwell(mvm, cmd, params); + + mvm->scan_uid_status[uid] = type; + + cmd->uid = cpu_to_le32(uid); + cmd->general_flags = cpu_to_le32(iwl_mvm_scan_umac_flags(mvm, params)); + + if (type == IWL_MVM_SCAN_SCHED) + cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE); + + if (iwl_mvm_scan_use_ebs(mvm, vif)) + cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; + + cmd->n_channels = params->n_channels; + + iwl_scan_build_ssids(params, sec_part->direct_scan, &ssid_bitmap); + + iwl_mvm_umac_scan_cfg_channels(mvm, params->channels, + params->n_channels, ssid_bitmap, cmd); + + for (i = 0; i < params->n_scan_plans; i++) { + struct cfg80211_sched_scan_plan *scan_plan = + ¶ms->scan_plans[i]; + + sec_part->schedule[i].iter_count = scan_plan->iterations; + sec_part->schedule[i].interval = + cpu_to_le16(scan_plan->interval); + } + + /* + * If the number of iterations of the last scan plan is set to + * zero, it should run infinitely. However, this is not always the case. + * For example, when regular scan is requested the driver sets one scan + * plan with one iteration. + */ + if (!sec_part->schedule[i - 1].iter_count) + sec_part->schedule[i - 1].iter_count = 0xff; + + sec_part->delay = cpu_to_le16(params->delay); + sec_part->preq = params->preq; + + return 0; +} + +static int iwl_mvm_num_scans(struct iwl_mvm *mvm) +{ + return hweight32(mvm->scan_status & IWL_MVM_SCAN_MASK); +} + +static int iwl_mvm_check_running_scans(struct iwl_mvm *mvm, int type) +{ + /* This looks a bit arbitrary, but the idea is that if we run + * out of possible simultaneous scans and the userspace is + * trying to run a scan type that is already running, we + * return -EBUSY. But if the userspace wants to start a + * different type of scan, we stop the opposite type to make + * space for the new request. The reason is backwards + * compatibility with old wpa_supplicant that wouldn't stop a + * scheduled scan before starting a normal scan. + */ + + if (iwl_mvm_num_scans(mvm) < mvm->max_scans) + return 0; + + /* Use a switch, even though this is a bitmask, so that more + * than one bits set will fall in default and we will warn. + */ + switch (type) { + case IWL_MVM_SCAN_REGULAR: + if (mvm->scan_status & IWL_MVM_SCAN_REGULAR_MASK) + return -EBUSY; + return iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true); + case IWL_MVM_SCAN_SCHED: + if (mvm->scan_status & IWL_MVM_SCAN_SCHED_MASK) + return -EBUSY; + iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); + case IWL_MVM_SCAN_NETDETECT: + /* No need to stop anything for net-detect since the + * firmware is restarted anyway. This way, any sched + * scans that were running will be restarted when we + * resume. + */ + return 0; + default: + WARN_ON(1); + break; + } + + return -EIO; +} + +int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req, + struct ieee80211_scan_ies *ies) +{ + struct iwl_host_cmd hcmd = { + .len = { iwl_mvm_scan_size(mvm), }, + .data = { mvm->scan_cmd, }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + struct iwl_mvm_scan_params params = {}; + int ret; + struct cfg80211_sched_scan_plan scan_plan = { .iterations = 1 }; + + lockdep_assert_held(&mvm->mutex); + + if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) { + IWL_ERR(mvm, "scan while LAR regdomain is not set\n"); + return -EBUSY; + } + + ret = iwl_mvm_check_running_scans(mvm, IWL_MVM_SCAN_REGULAR); + if (ret) + return ret; + + /* we should have failed registration if scan_cmd was NULL */ + if (WARN_ON(!mvm->scan_cmd)) + return -ENOMEM; + + if (!iwl_mvm_scan_fits(mvm, req->n_ssids, ies, req->n_channels)) + return -ENOBUFS; + + params.n_ssids = req->n_ssids; + params.flags = req->flags; + params.n_channels = req->n_channels; + params.delay = 0; + params.ssids = req->ssids; + params.channels = req->channels; + params.mac_addr = req->mac_addr; + params.mac_addr_mask = req->mac_addr_mask; + params.no_cck = req->no_cck; + params.pass_all = true; + params.n_match_sets = 0; + params.match_sets = NULL; + + params.scan_plans = &scan_plan; + params.n_scan_plans = 1; + + params.type = iwl_mvm_get_scan_type(mvm, vif, ¶ms); + + iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); + + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { + hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); + ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, + IWL_MVM_SCAN_REGULAR); + } else { + hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; + ret = iwl_mvm_scan_lmac(mvm, vif, ¶ms); + } + + if (ret) + return ret; + + ret = iwl_mvm_send_cmd(mvm, &hcmd); + if (ret) { + /* If the scan failed, it usually means that the FW was unable + * to allocate the time events. Warn on it, but maybe we + * should try to send the command again with different params. + */ + IWL_ERR(mvm, "Scan failed! ret %d\n", ret); + return ret; + } + + IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n"); + mvm->scan_status |= IWL_MVM_SCAN_REGULAR; + iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN); + + return 0; +} + +int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies, + int type) +{ + struct iwl_host_cmd hcmd = { + .len = { iwl_mvm_scan_size(mvm), }, + .data = { mvm->scan_cmd, }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + struct iwl_mvm_scan_params params = {}; + int ret; + + lockdep_assert_held(&mvm->mutex); + + if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) { + IWL_ERR(mvm, "sched-scan while LAR regdomain is not set\n"); + return -EBUSY; + } + + ret = iwl_mvm_check_running_scans(mvm, type); + if (ret) + return ret; + + /* we should have failed registration if scan_cmd was NULL */ + if (WARN_ON(!mvm->scan_cmd)) + return -ENOMEM; + + if (!iwl_mvm_scan_fits(mvm, req->n_ssids, ies, req->n_channels)) + return -ENOBUFS; + + params.n_ssids = req->n_ssids; + params.flags = req->flags; + params.n_channels = req->n_channels; + params.ssids = req->ssids; + params.channels = req->channels; + params.mac_addr = req->mac_addr; + params.mac_addr_mask = req->mac_addr_mask; + params.no_cck = false; + params.pass_all = iwl_mvm_scan_pass_all(mvm, req); + params.n_match_sets = req->n_match_sets; + params.match_sets = req->match_sets; + if (!req->n_scan_plans) + return -EINVAL; + + params.n_scan_plans = req->n_scan_plans; + params.scan_plans = req->scan_plans; + + params.type = iwl_mvm_get_scan_type(mvm, vif, ¶ms); + + /* In theory, LMAC scans can handle a 32-bit delay, but since + * waiting for over 18 hours to start the scan is a bit silly + * and to keep it aligned with UMAC scans (which only support + * 16-bit delays), trim it down to 16-bits. + */ + if (req->delay > U16_MAX) { + IWL_DEBUG_SCAN(mvm, + "delay value is > 16-bits, set to max possible\n"); + params.delay = U16_MAX; + } else { + params.delay = req->delay; + } + + ret = iwl_mvm_config_sched_scan_profiles(mvm, req); + if (ret) + return ret; + + iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); + + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { + hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); + ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, IWL_MVM_SCAN_SCHED); + } else { + hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; + ret = iwl_mvm_scan_lmac(mvm, vif, ¶ms); + } + + if (ret) + return ret; + + ret = iwl_mvm_send_cmd(mvm, &hcmd); + if (!ret) { + IWL_DEBUG_SCAN(mvm, + "Sched scan request was sent successfully\n"); + mvm->scan_status |= type; + } else { + /* If the scan failed, it usually means that the FW was unable + * to allocate the time events. Warn on it, but maybe we + * should try to send the command again with different params. + */ + IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret); + } + + return ret; +} + +void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_umac_scan_complete *notif = (void *)pkt->data; + u32 uid = __le32_to_cpu(notif->uid); + bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED); + + if (WARN_ON(!(mvm->scan_uid_status[uid] & mvm->scan_status))) + return; + + /* if the scan is already stopping, we don't need to notify mac80211 */ + if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_REGULAR) { + ieee80211_scan_completed(mvm->hw, aborted); + iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); + } else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) { + ieee80211_sched_scan_stopped(mvm->hw); + } + + mvm->scan_status &= ~mvm->scan_uid_status[uid]; + IWL_DEBUG_SCAN(mvm, + "Scan completed, uid %u type %u, status %s, EBS status %s\n", + uid, mvm->scan_uid_status[uid], + notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? + "completed" : "aborted", + iwl_mvm_ebs_status_str(notif->ebs_status)); + IWL_DEBUG_SCAN(mvm, + "Last line %d, Last iteration %d, Time from last iteration %d\n", + notif->last_schedule, notif->last_iter, + __le32_to_cpu(notif->time_from_last_iter)); + + if (notif->ebs_status != IWL_SCAN_EBS_SUCCESS && + notif->ebs_status != IWL_SCAN_EBS_INACTIVE) + mvm->last_ebs_successful = false; + + mvm->scan_uid_status[uid] = 0; +} + +void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_umac_scan_iter_complete_notif *notif = (void *)pkt->data; + u8 buf[256]; + + IWL_DEBUG_SCAN(mvm, + "UMAC Scan iteration complete: status=0x%x scanned_channels=%d channels list: %s\n", + notif->status, notif->scanned_channels, + iwl_mvm_dump_channel_list(notif->results, + notif->scanned_channels, buf, + sizeof(buf))); +} + +static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type) +{ + struct iwl_umac_scan_abort cmd = {}; + int uid, ret; + + lockdep_assert_held(&mvm->mutex); + + /* We should always get a valid index here, because we already + * checked that this type of scan was running in the generic + * code. + */ + uid = iwl_mvm_scan_uid_by_status(mvm, type); + if (WARN_ON_ONCE(uid < 0)) + return uid; + + cmd.uid = cpu_to_le32(uid); + + IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid); + + ret = iwl_mvm_send_cmd_pdu(mvm, + iwl_cmd_id(SCAN_ABORT_UMAC, + IWL_ALWAYS_LONG_GROUP, 0), + 0, sizeof(cmd), &cmd); + if (!ret) + mvm->scan_uid_status[uid] = type << IWL_MVM_SCAN_STOPPING_SHIFT; + + return ret; +} + +static int iwl_mvm_scan_stop_wait(struct iwl_mvm *mvm, int type) +{ + struct iwl_notification_wait wait_scan_done; + static const u16 scan_done_notif[] = { SCAN_COMPLETE_UMAC, + SCAN_OFFLOAD_COMPLETE, }; + int ret; + + lockdep_assert_held(&mvm->mutex); + + iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done, + scan_done_notif, + ARRAY_SIZE(scan_done_notif), + NULL, NULL); + + IWL_DEBUG_SCAN(mvm, "Preparing to stop scan, type %x\n", type); + + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) + ret = iwl_mvm_umac_scan_abort(mvm, type); + else + ret = iwl_mvm_lmac_scan_abort(mvm); + + if (ret) { + IWL_DEBUG_SCAN(mvm, "couldn't stop scan type %d\n", type); + iwl_remove_notification(&mvm->notif_wait, &wait_scan_done); + return ret; + } + + ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ); + + return ret; +} + +int iwl_mvm_scan_size(struct iwl_mvm *mvm) +{ + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) + return sizeof(struct iwl_scan_req_umac) + + sizeof(struct iwl_scan_channel_cfg_umac) * + mvm->fw->ucode_capa.n_scan_channels + + sizeof(struct iwl_scan_req_umac_tail); + + return sizeof(struct iwl_scan_req_lmac) + + sizeof(struct iwl_scan_channel_cfg_lmac) * + mvm->fw->ucode_capa.n_scan_channels + + sizeof(struct iwl_scan_probe_req); +} + +/* + * This function is used in nic restart flow, to inform mac80211 about scans + * that was aborted by restart flow or by an assert. + */ +void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm) +{ + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { + int uid, i; + + uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_REGULAR); + if (uid >= 0) { + ieee80211_scan_completed(mvm->hw, true); + mvm->scan_uid_status[uid] = 0; + } + uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_SCHED); + if (uid >= 0 && !mvm->restart_fw) { + ieee80211_sched_scan_stopped(mvm->hw); + mvm->scan_uid_status[uid] = 0; + } + + /* We shouldn't have any UIDs still set. Loop over all the + * UIDs to make sure there's nothing left there and warn if + * any is found. + */ + for (i = 0; i < mvm->max_scans; i++) { + if (WARN_ONCE(mvm->scan_uid_status[i], + "UMAC scan UID %d status was not cleaned\n", + i)) + mvm->scan_uid_status[i] = 0; + } + } else { + if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) + ieee80211_scan_completed(mvm->hw, true); + + /* Sched scan will be restarted by mac80211 in + * restart_hw, so do not report if FW is about to be + * restarted. + */ + if ((mvm->scan_status & IWL_MVM_SCAN_SCHED) && !mvm->restart_fw) + ieee80211_sched_scan_stopped(mvm->hw); + } +} + +int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify) +{ + int ret; + + if (!(mvm->scan_status & type)) + return 0; + + if (iwl_mvm_is_radio_killed(mvm)) { + ret = 0; + goto out; + } + + ret = iwl_mvm_scan_stop_wait(mvm, type); + if (!ret) + mvm->scan_status |= type << IWL_MVM_SCAN_STOPPING_SHIFT; +out: + /* Clear the scan status so the next scan requests will + * succeed and mark the scan as stopping, so that the Rx + * handler doesn't do anything, as the scan was stopped from + * above. + */ + mvm->scan_status &= ~type; + + if (type == IWL_MVM_SCAN_REGULAR) { + /* Since the rx handler won't do anything now, we have + * to release the scan reference here. + */ + iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); + if (notify) + ieee80211_scan_completed(mvm->hw, true); + } else if (notify) { + ieee80211_sched_scan_stopped(mvm->hw); + } + + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c new file mode 100644 index 000000000000..b0f59fdd287c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c @@ -0,0 +1,340 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include "mvm.h" + +/* For counting bound interfaces */ +struct iwl_mvm_active_iface_iterator_data { + struct ieee80211_vif *ignore_vif; + u8 sta_vif_ap_sta_id; + enum iwl_sf_state sta_vif_state; + int num_active_macs; +}; + +/* + * Count bound interfaces which are not p2p, besides data->ignore_vif. + * data->station_vif will point to one bound vif of type station, if exists. + */ +static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_active_iface_iterator_data *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (vif == data->ignore_vif || !mvmvif->phy_ctxt || + vif->type == NL80211_IFTYPE_P2P_DEVICE) + return; + + data->num_active_macs++; + + if (vif->type == NL80211_IFTYPE_STATION) { + data->sta_vif_ap_sta_id = mvmvif->ap_sta_id; + if (vif->bss_conf.assoc) + data->sta_vif_state = SF_FULL_ON; + else + data->sta_vif_state = SF_INIT_OFF; + } +} + +/* + * Aging and idle timeouts for the different possible scenarios + * in default configuration + */ +static const +__le32 sf_full_timeout_def[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = { + { + cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER_DEF), + cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER_DEF) + }, + { + cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER_DEF), + cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER_DEF) + }, + { + cpu_to_le32(SF_MCAST_AGING_TIMER_DEF), + cpu_to_le32(SF_MCAST_IDLE_TIMER_DEF) + }, + { + cpu_to_le32(SF_BA_AGING_TIMER_DEF), + cpu_to_le32(SF_BA_IDLE_TIMER_DEF) + }, + { + cpu_to_le32(SF_TX_RE_AGING_TIMER_DEF), + cpu_to_le32(SF_TX_RE_IDLE_TIMER_DEF) + }, +}; + +/* + * Aging and idle timeouts for the different possible scenarios + * in single BSS MAC configuration. + */ +static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = { + { + cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER), + cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER) + }, + { + cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER), + cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER) + }, + { + cpu_to_le32(SF_MCAST_AGING_TIMER), + cpu_to_le32(SF_MCAST_IDLE_TIMER) + }, + { + cpu_to_le32(SF_BA_AGING_TIMER), + cpu_to_le32(SF_BA_IDLE_TIMER) + }, + { + cpu_to_le32(SF_TX_RE_AGING_TIMER), + cpu_to_le32(SF_TX_RE_IDLE_TIMER) + }, +}; + +static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm, + struct iwl_sf_cfg_cmd *sf_cmd, + struct ieee80211_sta *sta) +{ + int i, j, watermark; + + sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN); + + /* + * If we are in association flow - check antenna configuration + * capabilities of the AP station, and choose the watermark accordingly. + */ + if (sta) { + if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) { + switch (sta->rx_nss) { + case 1: + watermark = SF_W_MARK_SISO; + break; + case 2: + watermark = SF_W_MARK_MIMO2; + break; + default: + watermark = SF_W_MARK_MIMO3; + break; + } + } else { + watermark = SF_W_MARK_LEGACY; + } + /* default watermark value for unassociated mode. */ + } else { + watermark = SF_W_MARK_MIMO2; + } + sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark); + + for (i = 0; i < SF_NUM_SCENARIO; i++) { + for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) { + sf_cmd->long_delay_timeouts[i][j] = + cpu_to_le32(SF_LONG_DELAY_AGING_TIMER); + } + } + + if (sta || IWL_UCODE_API(mvm->fw->ucode_ver) < 13) { + BUILD_BUG_ON(sizeof(sf_full_timeout) != + sizeof(__le32) * SF_NUM_SCENARIO * + SF_NUM_TIMEOUT_TYPES); + + memcpy(sf_cmd->full_on_timeouts, sf_full_timeout, + sizeof(sf_full_timeout)); + } else { + BUILD_BUG_ON(sizeof(sf_full_timeout_def) != + sizeof(__le32) * SF_NUM_SCENARIO * + SF_NUM_TIMEOUT_TYPES); + + memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def, + sizeof(sf_full_timeout_def)); + } + +} + +static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, + enum iwl_sf_state new_state) +{ + struct iwl_sf_cfg_cmd sf_cmd = { + .state = cpu_to_le32(SF_FULL_ON), + }; + struct ieee80211_sta *sta; + int ret = 0; + + if (IWL_UCODE_API(mvm->fw->ucode_ver) < 13) + sf_cmd.state = cpu_to_le32(new_state); + + if (mvm->cfg->disable_dummy_notification) + sf_cmd.state |= cpu_to_le32(SF_CFG_DUMMY_NOTIF_OFF); + + /* + * If an associated AP sta changed its antenna configuration, the state + * will remain FULL_ON but SF parameters need to be reconsidered. + */ + if (new_state != SF_FULL_ON && mvm->sf_state == new_state) + return 0; + + switch (new_state) { + case SF_UNINIT: + if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 13) + iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL); + break; + case SF_FULL_ON: + if (sta_id == IWL_MVM_STATION_COUNT) { + IWL_ERR(mvm, + "No station: Cannot switch SF to FULL_ON\n"); + return -EINVAL; + } + rcu_read_lock(); + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + if (IS_ERR_OR_NULL(sta)) { + IWL_ERR(mvm, "Invalid station id\n"); + rcu_read_unlock(); + return -EINVAL; + } + iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta); + rcu_read_unlock(); + break; + case SF_INIT_OFF: + iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL); + break; + default: + WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n", + new_state); + return -EINVAL; + } + + ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC, + sizeof(sf_cmd), &sf_cmd); + if (!ret) + mvm->sf_state = new_state; + + return ret; +} + +/* + * Update Smart fifo: + * Count bound interfaces that are not to be removed, ignoring p2p devices, + * and set new state accordingly. + */ +int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif, + bool remove_vif) +{ + enum iwl_sf_state new_state; + u8 sta_id = IWL_MVM_STATION_COUNT; + struct iwl_mvm_vif *mvmvif = NULL; + struct iwl_mvm_active_iface_iterator_data data = { + .ignore_vif = changed_vif, + .sta_vif_state = SF_UNINIT, + .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT, + }; + + /* + * Ignore the call if we are in HW Restart flow, or if the handled + * vif is a p2p device. + */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || + (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE)) + return 0; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bound_iface_iterator, + &data); + + /* If changed_vif exists and is not to be removed, add to the count */ + if (changed_vif && !remove_vif) + data.num_active_macs++; + + switch (data.num_active_macs) { + case 0: + /* If there are no active macs - change state to SF_INIT_OFF */ + new_state = SF_INIT_OFF; + break; + case 1: + if (remove_vif) { + /* The one active mac left is of type station + * and we filled the relevant data during iteration + */ + new_state = data.sta_vif_state; + sta_id = data.sta_vif_ap_sta_id; + } else { + if (WARN_ON(!changed_vif)) + return -EINVAL; + if (changed_vif->type != NL80211_IFTYPE_STATION) { + new_state = SF_UNINIT; + } else if (changed_vif->bss_conf.assoc && + changed_vif->bss_conf.dtim_period) { + mvmvif = iwl_mvm_vif_from_mac80211(changed_vif); + sta_id = mvmvif->ap_sta_id; + new_state = SF_FULL_ON; + } else { + new_state = SF_INIT_OFF; + } + } + break; + default: + /* If there are multiple active macs - change to SF_UNINIT */ + new_state = SF_UNINIT; + } + return iwl_mvm_sf_config(mvm, sta_id, new_state); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c new file mode 100644 index 000000000000..300a249486e4 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -0,0 +1,1810 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include + +#include "mvm.h" +#include "sta.h" +#include "rs.h" + +static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, + enum nl80211_iftype iftype) +{ + int sta_id; + u32 reserved_ids = 0; + + BUILD_BUG_ON(IWL_MVM_STATION_COUNT > 32); + WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)); + + lockdep_assert_held(&mvm->mutex); + + /* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */ + if (iftype != NL80211_IFTYPE_STATION) + reserved_ids = BIT(0); + + /* Don't take rcu_read_lock() since we are protected by mvm->mutex */ + for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) { + if (BIT(sta_id) & reserved_ids) + continue; + + if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex))) + return sta_id; + } + return IWL_MVM_STATION_COUNT; +} + +/* send station add/update command to firmware */ +int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + bool update) +{ + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_add_sta_cmd add_sta_cmd = { + .sta_id = mvm_sta->sta_id, + .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color), + .add_modify = update ? 1 : 0, + .station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK | + STA_FLG_MIMO_EN_MSK), + }; + int ret; + u32 status; + u32 agg_size = 0, mpdu_dens = 0; + + if (!update) { + add_sta_cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk); + memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN); + } + + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_160: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_160MHZ); + /* fall through */ + case IEEE80211_STA_RX_BW_80: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_80MHZ); + /* fall through */ + case IEEE80211_STA_RX_BW_40: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_40MHZ); + /* fall through */ + case IEEE80211_STA_RX_BW_20: + if (sta->ht_cap.ht_supported) + add_sta_cmd.station_flags |= + cpu_to_le32(STA_FLG_FAT_EN_20MHZ); + break; + } + + switch (sta->rx_nss) { + case 1: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO); + break; + case 2: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO2); + break; + case 3 ... 8: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO3); + break; + } + + switch (sta->smps_mode) { + case IEEE80211_SMPS_AUTOMATIC: + case IEEE80211_SMPS_NUM_MODES: + WARN_ON(1); + break; + case IEEE80211_SMPS_STATIC: + /* override NSS */ + add_sta_cmd.station_flags &= ~cpu_to_le32(STA_FLG_MIMO_EN_MSK); + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO); + break; + case IEEE80211_SMPS_DYNAMIC: + add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_RTS_MIMO_PROT); + break; + case IEEE80211_SMPS_OFF: + /* nothing */ + break; + } + + if (sta->ht_cap.ht_supported) { + add_sta_cmd.station_flags_msk |= + cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK | + STA_FLG_AGG_MPDU_DENS_MSK); + + mpdu_dens = sta->ht_cap.ampdu_density; + } + + if (sta->vht_cap.vht_supported) { + agg_size = sta->vht_cap.cap & + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; + agg_size >>= + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + } else if (sta->ht_cap.ht_supported) { + agg_size = sta->ht_cap.ampdu_factor; + } + + add_sta_cmd.station_flags |= + cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT); + add_sta_cmd.station_flags |= + cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT); + + status = ADD_STA_SUCCESS; + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd), + &add_sta_cmd, &status); + if (ret) + return ret; + + switch (status) { + case ADD_STA_SUCCESS: + IWL_DEBUG_ASSOC(mvm, "ADD_STA PASSED\n"); + break; + default: + ret = -EIO; + IWL_ERR(mvm, "ADD_STA failed\n"); + break; + } + + return ret; +} + +static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) +{ + unsigned long used_hw_queues; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + unsigned int wdg_timeout = + iwl_mvm_get_wd_timeout(mvm, NULL, true, false); + u32 ac; + + lockdep_assert_held(&mvm->mutex); + + used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, NULL); + + /* Find available queues, and allocate them to the ACs */ + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + u8 queue = find_first_zero_bit(&used_hw_queues, + mvm->first_agg_queue); + + if (queue >= mvm->first_agg_queue) { + IWL_ERR(mvm, "Failed to allocate STA queue\n"); + return -EBUSY; + } + + __set_bit(queue, &used_hw_queues); + mvmsta->hw_queue[ac] = queue; + } + + /* Found a place for all queues - enable them */ + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac], + mvmsta->hw_queue[ac], + iwl_mvm_ac_to_tx_fifo[ac], 0, + wdg_timeout); + mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]); + } + + return 0; +} + +static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + unsigned long sta_msk; + int i; + + lockdep_assert_held(&mvm->mutex); + + /* disable the TDLS STA-specific queues */ + sta_msk = mvmsta->tfd_queue_msk; + for_each_set_bit(i, &sta_msk, sizeof(sta_msk) * BITS_PER_BYTE) + iwl_mvm_disable_txq(mvm, i, i, IWL_MAX_TID_COUNT, 0); +} + +int iwl_mvm_add_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + int i, ret, sta_id; + + lockdep_assert_held(&mvm->mutex); + + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + sta_id = iwl_mvm_find_free_sta_id(mvm, + ieee80211_vif_type_p2p(vif)); + else + sta_id = mvm_sta->sta_id; + + if (sta_id == IWL_MVM_STATION_COUNT) + return -ENOSPC; + + if (vif->type == NL80211_IFTYPE_AP) { + mvmvif->ap_assoc_sta_count++; + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + } + + spin_lock_init(&mvm_sta->lock); + + mvm_sta->sta_id = sta_id; + mvm_sta->mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color); + mvm_sta->vif = vif; + mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; + mvm_sta->tx_protection = 0; + mvm_sta->tt_tx_protection = false; + + /* HW restart, don't assume the memory has been zeroed */ + atomic_set(&mvm->pending_frames[sta_id], 0); + mvm_sta->tid_disable_agg = 0xffff; /* No aggs at first */ + mvm_sta->tfd_queue_msk = 0; + + /* allocate new queues for a TDLS station */ + if (sta->tdls) { + ret = iwl_mvm_tdls_sta_init(mvm, sta); + if (ret) + return ret; + } else { + for (i = 0; i < IEEE80211_NUM_ACS; i++) + if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) + mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]); + } + + /* for HW restart - reset everything but the sequence number */ + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + u16 seq = mvm_sta->tid_data[i].seq_number; + memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i])); + mvm_sta->tid_data[i].seq_number = seq; + } + mvm_sta->agg_tids = 0; + + ret = iwl_mvm_sta_send_to_fw(mvm, sta, false); + if (ret) + goto err; + + if (vif->type == NL80211_IFTYPE_STATION) { + if (!sta->tdls) { + WARN_ON(mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT); + mvmvif->ap_sta_id = sta_id; + } else { + WARN_ON(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT); + } + } + + rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta); + + return 0; + +err: + iwl_mvm_tdls_sta_deinit(mvm, sta); + return ret; +} + +int iwl_mvm_update_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + return iwl_mvm_sta_send_to_fw(mvm, sta, true); +} + +int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, + bool drain) +{ + struct iwl_mvm_add_sta_cmd cmd = {}; + int ret; + u32 status; + + lockdep_assert_held(&mvm->mutex); + + cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color); + cmd.sta_id = mvmsta->sta_id; + cmd.add_modify = STA_MODE_MODIFY; + cmd.station_flags = drain ? cpu_to_le32(STA_FLG_DRAIN_FLOW) : 0; + cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW); + + status = ADD_STA_SUCCESS; + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), + &cmd, &status); + if (ret) + return ret; + + switch (status) { + case ADD_STA_SUCCESS: + IWL_DEBUG_INFO(mvm, "Frames for staid %d will drained in fw\n", + mvmsta->sta_id); + break; + default: + ret = -EIO; + IWL_ERR(mvm, "Couldn't drain frames for staid %d\n", + mvmsta->sta_id); + break; + } + + return ret; +} + +/* + * Remove a station from the FW table. Before sending the command to remove + * the station validate that the station is indeed known to the driver (sanity + * only). + */ +static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_rm_sta_cmd rm_sta_cmd = { + .sta_id = sta_id, + }; + int ret; + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + + /* Note: internal stations are marked as error values */ + if (!sta) { + IWL_ERR(mvm, "Invalid station id\n"); + return -EINVAL; + } + + ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, 0, + sizeof(rm_sta_cmd), &rm_sta_cmd); + if (ret) { + IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id); + return ret; + } + + return 0; +} + +void iwl_mvm_sta_drained_wk(struct work_struct *wk) +{ + struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, sta_drained_wk); + u8 sta_id; + + /* + * The mutex is needed because of the SYNC cmd, but not only: if the + * work would run concurrently with iwl_mvm_rm_sta, it would run before + * iwl_mvm_rm_sta sets the station as busy, and exit. Then + * iwl_mvm_rm_sta would set the station as busy, and nobody will clean + * that later. + */ + mutex_lock(&mvm->mutex); + + for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) { + int ret; + struct ieee80211_sta *sta = + rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + + /* + * This station is in use or RCU-removed; the latter happens in + * managed mode, where mac80211 removes the station before we + * can remove it from firmware (we can only do that after the + * MAC is marked unassociated), and possibly while the deauth + * frame to disconnect from the AP is still queued. Then, the + * station pointer is -ENOENT when the last skb is reclaimed. + */ + if (!IS_ERR(sta) || PTR_ERR(sta) == -ENOENT) + continue; + + if (PTR_ERR(sta) == -EINVAL) { + IWL_ERR(mvm, "Drained sta %d, but it is internal?\n", + sta_id); + continue; + } + + if (!sta) { + IWL_ERR(mvm, "Drained sta %d, but it was NULL?\n", + sta_id); + continue; + } + + WARN_ON(PTR_ERR(sta) != -EBUSY); + /* This station was removed and we waited until it got drained, + * we can now proceed and remove it. + */ + ret = iwl_mvm_rm_sta_common(mvm, sta_id); + if (ret) { + IWL_ERR(mvm, + "Couldn't remove sta %d after it was drained\n", + sta_id); + continue; + } + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL); + clear_bit(sta_id, mvm->sta_drained); + + if (mvm->tfd_drained[sta_id]) { + unsigned long i, msk = mvm->tfd_drained[sta_id]; + + for_each_set_bit(i, &msk, sizeof(msk) * BITS_PER_BYTE) + iwl_mvm_disable_txq(mvm, i, i, + IWL_MAX_TID_COUNT, 0); + + mvm->tfd_drained[sta_id] = 0; + IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n", + sta_id, msk); + } + } + + mutex_unlock(&mvm->mutex); +} + +int iwl_mvm_rm_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + int ret; + + lockdep_assert_held(&mvm->mutex); + + if (vif->type == NL80211_IFTYPE_STATION && + mvmvif->ap_sta_id == mvm_sta->sta_id) { + ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); + if (ret) + return ret; + /* flush its queues here since we are freeing mvm_sta */ + ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, 0); + if (ret) + return ret; + ret = iwl_trans_wait_tx_queue_empty(mvm->trans, + mvm_sta->tfd_queue_msk); + if (ret) + return ret; + ret = iwl_mvm_drain_sta(mvm, mvm_sta, false); + + /* if we are associated - we can't remove the AP STA now */ + if (vif->bss_conf.assoc) + return ret; + + /* unassoc - go ahead - remove the AP STA now */ + mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + + /* clear d0i3_ap_sta_id if no longer relevant */ + if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id) + mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + } + + /* + * This shouldn't happen - the TDLS channel switch should be canceled + * before the STA is removed. + */ + if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == mvm_sta->sta_id)) { + mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; + cancel_delayed_work(&mvm->tdls_cs.dwork); + } + + /* + * Make sure that the tx response code sees the station as -EBUSY and + * calls the drain worker. + */ + spin_lock_bh(&mvm_sta->lock); + /* + * There are frames pending on the AC queues for this station. + * We need to wait until all the frames are drained... + */ + if (atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) { + rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], + ERR_PTR(-EBUSY)); + spin_unlock_bh(&mvm_sta->lock); + + /* disable TDLS sta queues on drain complete */ + if (sta->tdls) { + mvm->tfd_drained[mvm_sta->sta_id] = + mvm_sta->tfd_queue_msk; + IWL_DEBUG_TDLS(mvm, "Draining TDLS sta %d\n", + mvm_sta->sta_id); + } + + ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); + } else { + spin_unlock_bh(&mvm_sta->lock); + + if (sta->tdls) + iwl_mvm_tdls_sta_deinit(mvm, sta); + + ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id); + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL); + } + + return ret; +} + +int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u8 sta_id) +{ + int ret = iwl_mvm_rm_sta_common(mvm, sta_id); + + lockdep_assert_held(&mvm->mutex); + + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL); + return ret; +} + +static int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *sta, + u32 qmask, enum nl80211_iftype iftype) +{ + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype); + if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT)) + return -ENOSPC; + } + + sta->tfd_queue_msk = qmask; + + /* put a non-NULL value so iterating over the stations won't stop */ + rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], ERR_PTR(-EINVAL)); + return 0; +} + +static void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *sta) +{ + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL); + memset(sta, 0, sizeof(struct iwl_mvm_int_sta)); + sta->sta_id = IWL_MVM_STATION_COUNT; +} + +static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *sta, + const u8 *addr, + u16 mac_id, u16 color) +{ + struct iwl_mvm_add_sta_cmd cmd; + int ret; + u32 status; + + lockdep_assert_held(&mvm->mutex); + + memset(&cmd, 0, sizeof(cmd)); + cmd.sta_id = sta->sta_id; + cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, + color)); + + cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk); + + if (addr) + memcpy(cmd.addr, addr, ETH_ALEN); + + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), + &cmd, &status); + if (ret) + return ret; + + switch (status) { + case ADD_STA_SUCCESS: + IWL_DEBUG_INFO(mvm, "Internal station added.\n"); + return 0; + default: + ret = -EIO; + IWL_ERR(mvm, "Add internal station failed, status=0x%x\n", + status); + break; + } + return ret; +} + +int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) +{ + unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? + mvm->cfg->base_params->wd_timeout : + IWL_WATCHDOG_DISABLED; + int ret; + + lockdep_assert_held(&mvm->mutex); + + /* Map Aux queue to fifo - needs to happen before adding Aux station */ + iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue, + IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); + + /* Allocate aux station and assign to it the aux queue */ + ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue), + NL80211_IFTYPE_UNSPECIFIED); + if (ret) + return ret; + + ret = iwl_mvm_add_int_sta_common(mvm, &mvm->aux_sta, NULL, + MAC_INDEX_AUX, 0); + + if (ret) + iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta); + return ret; +} + +void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm) +{ + lockdep_assert_held(&mvm->mutex); + + iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta); +} + +/* + * Send the add station command for the vif's broadcast station. + * Assumes that the station was already allocated. + * + * @mvm: the mvm component + * @vif: the interface to which the broadcast station is added + * @bsta: the broadcast station to add. + */ +int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; + static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + const u8 *baddr = _baddr; + + lockdep_assert_held(&mvm->mutex); + + if (vif->type == NL80211_IFTYPE_ADHOC) + baddr = vif->bss_conf.bssid; + + if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT)) + return -ENOSPC; + + return iwl_mvm_add_int_sta_common(mvm, bsta, baddr, + mvmvif->id, mvmvif->color); +} + +/* Send the FW a request to remove the station from it's internal data + * structures, but DO NOT remove the entry from the local data structures. */ +int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + lockdep_assert_held(&mvm->mutex); + + ret = iwl_mvm_rm_sta_common(mvm, mvmvif->bcast_sta.sta_id); + if (ret) + IWL_WARN(mvm, "Failed sending remove station\n"); + return ret; +} + +int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u32 qmask; + + lockdep_assert_held(&mvm->mutex); + + qmask = iwl_mvm_mac_get_queues_mask(vif); + + /* + * The firmware defines the TFD queue mask to only be relevant + * for *unicast* queues, so the multicast (CAB) queue shouldn't + * be included. + */ + if (vif->type == NL80211_IFTYPE_AP) + qmask &= ~BIT(vif->cab_queue); + + return iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, qmask, + ieee80211_vif_type_p2p(vif)); +} + +/* Allocate a new station entry for the broadcast station to the given vif, + * and send it to the FW. + * Note that each P2P mac should have its own broadcast station. + * + * @mvm: the mvm component + * @vif: the interface to which the broadcast station is added + * @bsta: the broadcast station to add. */ +int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; + int ret; + + lockdep_assert_held(&mvm->mutex); + + ret = iwl_mvm_alloc_bcast_sta(mvm, vif); + if (ret) + return ret; + + ret = iwl_mvm_send_add_bcast_sta(mvm, vif); + + if (ret) + iwl_mvm_dealloc_int_sta(mvm, bsta); + + return ret; +} + +void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta); +} + +/* + * Send the FW a request to remove the station from it's internal data + * structures, and in addition remove it from the local data structure. + */ +int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + int ret; + + lockdep_assert_held(&mvm->mutex); + + ret = iwl_mvm_send_rm_bcast_sta(mvm, vif); + + iwl_mvm_dealloc_bcast_sta(mvm, vif); + + return ret; +} + +#define IWL_MAX_RX_BA_SESSIONS 16 + +int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + int tid, u16 ssn, bool start) +{ + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_add_sta_cmd cmd = {}; + int ret; + u32 status; + + lockdep_assert_held(&mvm->mutex); + + if (start && mvm->rx_ba_sessions >= IWL_MAX_RX_BA_SESSIONS) { + IWL_WARN(mvm, "Not enough RX BA SESSIONS\n"); + return -ENOSPC; + } + + cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); + cmd.sta_id = mvm_sta->sta_id; + cmd.add_modify = STA_MODE_MODIFY; + if (start) { + cmd.add_immediate_ba_tid = (u8) tid; + cmd.add_immediate_ba_ssn = cpu_to_le16(ssn); + } else { + cmd.remove_immediate_ba_tid = (u8) tid; + } + cmd.modify_mask = start ? STA_MODIFY_ADD_BA_TID : + STA_MODIFY_REMOVE_BA_TID; + + status = ADD_STA_SUCCESS; + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), + &cmd, &status); + if (ret) + return ret; + + switch (status) { + case ADD_STA_SUCCESS: + IWL_DEBUG_INFO(mvm, "RX BA Session %sed in fw\n", + start ? "start" : "stopp"); + break; + case ADD_STA_IMMEDIATE_BA_FAILURE: + IWL_WARN(mvm, "RX BA Session refused by fw\n"); + ret = -ENOSPC; + break; + default: + ret = -EIO; + IWL_ERR(mvm, "RX BA Session failed %sing, status 0x%x\n", + start ? "start" : "stopp", status); + break; + } + + if (!ret) { + if (start) + mvm->rx_ba_sessions++; + else if (mvm->rx_ba_sessions > 0) + /* check that restart flow didn't zero the counter */ + mvm->rx_ba_sessions--; + } + + return ret; +} + +static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + int tid, u8 queue, bool start) +{ + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_add_sta_cmd cmd = {}; + int ret; + u32 status; + + lockdep_assert_held(&mvm->mutex); + + if (start) { + mvm_sta->tfd_queue_msk |= BIT(queue); + mvm_sta->tid_disable_agg &= ~BIT(tid); + } else { + mvm_sta->tfd_queue_msk &= ~BIT(queue); + mvm_sta->tid_disable_agg |= BIT(tid); + } + + cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); + cmd.sta_id = mvm_sta->sta_id; + cmd.add_modify = STA_MODE_MODIFY; + cmd.modify_mask = STA_MODIFY_QUEUES | STA_MODIFY_TID_DISABLE_TX; + cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk); + cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg); + + status = ADD_STA_SUCCESS; + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), + &cmd, &status); + if (ret) + return ret; + + switch (status) { + case ADD_STA_SUCCESS: + break; + default: + ret = -EIO; + IWL_ERR(mvm, "TX BA Session failed %sing, status 0x%x\n", + start ? "start" : "stopp", status); + break; + } + + return ret; +} + +const u8 tid_to_mac80211_ac[] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO, +}; + +static const u8 tid_to_ucode_ac[] = { + AC_BE, + AC_BK, + AC_BK, + AC_BE, + AC_VI, + AC_VI, + AC_VO, + AC_VO, +}; + +int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_tid_data *tid_data; + int txq_id; + int ret; + + if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) + return -EINVAL; + + if (mvmsta->tid_data[tid].state != IWL_AGG_OFF) { + IWL_ERR(mvm, "Start AGG when state is not IWL_AGG_OFF %d!\n", + mvmsta->tid_data[tid].state); + return -ENXIO; + } + + lockdep_assert_held(&mvm->mutex); + + spin_lock_bh(&mvmsta->lock); + + /* possible race condition - we entered D0i3 while starting agg */ + if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) { + spin_unlock_bh(&mvmsta->lock); + IWL_ERR(mvm, "Entered D0i3 while starting Tx agg\n"); + return -EIO; + } + + spin_lock_bh(&mvm->queue_info_lock); + + txq_id = iwl_mvm_find_free_queue(mvm, mvm->first_agg_queue, + mvm->last_agg_queue); + if (txq_id < 0) { + ret = txq_id; + spin_unlock_bh(&mvm->queue_info_lock); + IWL_ERR(mvm, "Failed to allocate agg queue\n"); + goto release_locks; + } + mvm->queue_info[txq_id].setup_reserved = true; + spin_unlock_bh(&mvm->queue_info_lock); + + tid_data = &mvmsta->tid_data[tid]; + tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); + tid_data->txq_id = txq_id; + *ssn = tid_data->ssn; + + IWL_DEBUG_TX_QUEUES(mvm, + "Start AGG: sta %d tid %d queue %d - ssn = %d, next_recl = %d\n", + mvmsta->sta_id, tid, txq_id, tid_data->ssn, + tid_data->next_reclaimed); + + if (tid_data->ssn == tid_data->next_reclaimed) { + tid_data->state = IWL_AGG_STARTING; + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + } else { + tid_data->state = IWL_EMPTYING_HW_QUEUE_ADDBA; + } + + ret = 0; + +release_locks: + spin_unlock_bh(&mvmsta->lock); + + return ret; +} + +int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u8 buf_size) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + unsigned int wdg_timeout = + iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false); + int queue, fifo, ret; + u16 ssn; + + BUILD_BUG_ON((sizeof(mvmsta->agg_tids) * BITS_PER_BYTE) + != IWL_MAX_TID_COUNT); + + buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); + + spin_lock_bh(&mvmsta->lock); + ssn = tid_data->ssn; + queue = tid_data->txq_id; + tid_data->state = IWL_AGG_ON; + mvmsta->agg_tids |= BIT(tid); + tid_data->ssn = 0xffff; + spin_unlock_bh(&mvmsta->lock); + + fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; + + iwl_mvm_enable_agg_txq(mvm, queue, + vif->hw_queue[tid_to_mac80211_ac[tid]], fifo, + mvmsta->sta_id, tid, buf_size, ssn, wdg_timeout); + + ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); + if (ret) + return -EIO; + + /* No need to mark as reserved */ + spin_lock_bh(&mvm->queue_info_lock); + mvm->queue_info[queue].setup_reserved = false; + spin_unlock_bh(&mvm->queue_info_lock); + + /* + * Even though in theory the peer could have different + * aggregation reorder buffer sizes for different sessions, + * our ucode doesn't allow for that and has a global limit + * for each station. Therefore, use the minimum of all the + * aggregation sessions and our default value. + */ + mvmsta->max_agg_bufsize = + min(mvmsta->max_agg_bufsize, buf_size); + mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize; + + IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", + sta->addr, tid); + + return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false); +} + +int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + u16 txq_id; + int err; + + + /* + * If mac80211 is cleaning its state, then say that we finished since + * our state has been cleared anyway. + */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + return 0; + } + + spin_lock_bh(&mvmsta->lock); + + txq_id = tid_data->txq_id; + + IWL_DEBUG_TX_QUEUES(mvm, "Stop AGG: sta %d tid %d q %d state %d\n", + mvmsta->sta_id, tid, txq_id, tid_data->state); + + mvmsta->agg_tids &= ~BIT(tid); + + /* No need to mark as reserved anymore */ + spin_lock_bh(&mvm->queue_info_lock); + mvm->queue_info[txq_id].setup_reserved = false; + spin_unlock_bh(&mvm->queue_info_lock); + + switch (tid_data->state) { + case IWL_AGG_ON: + tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); + + IWL_DEBUG_TX_QUEUES(mvm, + "ssn = %d, next_recl = %d\n", + tid_data->ssn, tid_data->next_reclaimed); + + /* There are still packets for this RA / TID in the HW */ + if (tid_data->ssn != tid_data->next_reclaimed) { + tid_data->state = IWL_EMPTYING_HW_QUEUE_DELBA; + err = 0; + break; + } + + tid_data->ssn = 0xffff; + tid_data->state = IWL_AGG_OFF; + spin_unlock_bh(&mvmsta->lock); + + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + + iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); + + iwl_mvm_disable_txq(mvm, txq_id, + vif->hw_queue[tid_to_mac80211_ac[tid]], tid, + 0); + return 0; + case IWL_AGG_STARTING: + case IWL_EMPTYING_HW_QUEUE_ADDBA: + /* + * The agg session has been stopped before it was set up. This + * can happen when the AddBA timer times out for example. + */ + + /* No barriers since we are under mutex */ + lockdep_assert_held(&mvm->mutex); + + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + tid_data->state = IWL_AGG_OFF; + err = 0; + break; + default: + IWL_ERR(mvm, + "Stopping AGG while state not ON or starting for %d on %d (%d)\n", + mvmsta->sta_id, tid, tid_data->state); + IWL_ERR(mvm, + "\ttid_data->txq_id = %d\n", tid_data->txq_id); + err = -EINVAL; + } + + spin_unlock_bh(&mvmsta->lock); + + return err; +} + +int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + u16 txq_id; + enum iwl_mvm_agg_state old_state; + + /* + * First set the agg state to OFF to avoid calling + * ieee80211_stop_tx_ba_cb in iwl_mvm_check_ratid_empty. + */ + spin_lock_bh(&mvmsta->lock); + txq_id = tid_data->txq_id; + IWL_DEBUG_TX_QUEUES(mvm, "Flush AGG: sta %d tid %d q %d state %d\n", + mvmsta->sta_id, tid, txq_id, tid_data->state); + old_state = tid_data->state; + tid_data->state = IWL_AGG_OFF; + mvmsta->agg_tids &= ~BIT(tid); + spin_unlock_bh(&mvmsta->lock); + + /* No need to mark as reserved */ + spin_lock_bh(&mvm->queue_info_lock); + mvm->queue_info[txq_id].setup_reserved = false; + spin_unlock_bh(&mvm->queue_info_lock); + + if (old_state >= IWL_AGG_ON) { + iwl_mvm_drain_sta(mvm, mvmsta, true); + if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0)) + IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); + iwl_trans_wait_tx_queue_empty(mvm->trans, + mvmsta->tfd_queue_msk); + iwl_mvm_drain_sta(mvm, mvmsta, false); + + iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); + + iwl_mvm_disable_txq(mvm, tid_data->txq_id, + vif->hw_queue[tid_to_mac80211_ac[tid]], tid, + 0); + } + + return 0; +} + +static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm) +{ + int i, max = -1, max_offs = -1; + + lockdep_assert_held(&mvm->mutex); + + /* Pick the unused key offset with the highest 'deleted' + * counter. Every time a key is deleted, all the counters + * are incremented and the one that was just deleted is + * reset to zero. Thus, the highest counter is the one + * that was deleted longest ago. Pick that one. + */ + for (i = 0; i < STA_KEY_MAX_NUM; i++) { + if (test_bit(i, mvm->fw_key_table)) + continue; + if (mvm->fw_key_deleted[i] > max) { + max = mvm->fw_key_deleted[i]; + max_offs = i; + } + } + + if (max_offs < 0) + return STA_KEY_IDX_INVALID; + + __set_bit(max_offs, mvm->fw_key_table); + + return max_offs; +} + +static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (sta) { + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + + return mvm_sta->sta_id; + } + + /* + * The device expects GTKs for station interfaces to be + * installed as GTKs for the AP station. If we have no + * station ID, then use AP's station ID. + */ + if (vif->type == NL80211_IFTYPE_STATION && + mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) + return mvmvif->ap_sta_id; + + return IWL_MVM_STATION_COUNT; +} + +static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvm_sta, + struct ieee80211_key_conf *keyconf, bool mcast, + u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags) +{ + struct iwl_mvm_add_sta_key_cmd cmd = {}; + __le16 key_flags; + int ret; + u32 status; + u16 keyidx; + int i; + u8 sta_id = mvm_sta->sta_id; + + keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & + STA_KEY_FLG_KEYID_MSK; + key_flags = cpu_to_le16(keyidx); + key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_KEY_MAP); + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP); + cmd.tkip_rx_tsc_byte2 = tkip_iv32; + for (i = 0; i < 5; i++) + cmd.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); + memcpy(cmd.key, keyconf->key, keyconf->keylen); + break; + case WLAN_CIPHER_SUITE_CCMP: + key_flags |= cpu_to_le16(STA_KEY_FLG_CCM); + memcpy(cmd.key, keyconf->key, keyconf->keylen); + break; + case WLAN_CIPHER_SUITE_WEP104: + key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES); + /* fall through */ + case WLAN_CIPHER_SUITE_WEP40: + key_flags |= cpu_to_le16(STA_KEY_FLG_WEP); + memcpy(cmd.key + 3, keyconf->key, keyconf->keylen); + break; + default: + key_flags |= cpu_to_le16(STA_KEY_FLG_EXT); + memcpy(cmd.key, keyconf->key, keyconf->keylen); + } + + if (mcast) + key_flags |= cpu_to_le16(STA_KEY_MULTICAST); + + cmd.key_offset = keyconf->hw_key_idx; + cmd.key_flags = key_flags; + cmd.sta_id = sta_id; + + status = ADD_STA_SUCCESS; + if (cmd_flags & CMD_ASYNC) + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC, + sizeof(cmd), &cmd); + else + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), + &cmd, &status); + + switch (status) { + case ADD_STA_SUCCESS: + IWL_DEBUG_WEP(mvm, "MODIFY_STA: set dynamic key passed\n"); + break; + default: + ret = -EIO; + IWL_ERR(mvm, "MODIFY_STA: set dynamic key failed\n"); + break; + } + + return ret; +} + +static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm, + struct ieee80211_key_conf *keyconf, + u8 sta_id, bool remove_key) +{ + struct iwl_mvm_mgmt_mcast_key_cmd igtk_cmd = {}; + + /* verify the key details match the required command's expectations */ + if (WARN_ON((keyconf->cipher != WLAN_CIPHER_SUITE_AES_CMAC) || + (keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE) || + (keyconf->keyidx != 4 && keyconf->keyidx != 5))) + return -EINVAL; + + igtk_cmd.key_id = cpu_to_le32(keyconf->keyidx); + igtk_cmd.sta_id = cpu_to_le32(sta_id); + + if (remove_key) { + igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_NOT_VALID); + } else { + struct ieee80211_key_seq seq; + const u8 *pn; + + memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen); + ieee80211_get_key_rx_seq(keyconf, 0, &seq); + pn = seq.aes_cmac.pn; + igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) | + ((u64) pn[4] << 8) | + ((u64) pn[3] << 16) | + ((u64) pn[2] << 24) | + ((u64) pn[1] << 32) | + ((u64) pn[0] << 40)); + } + + IWL_DEBUG_INFO(mvm, "%s igtk for sta %u\n", + remove_key ? "removing" : "installing", + igtk_cmd.sta_id); + + return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0, + sizeof(igtk_cmd), &igtk_cmd); +} + + +static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (sta) + return sta->addr; + + if (vif->type == NL80211_IFTYPE_STATION && + mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { + u8 sta_id = mvmvif->ap_sta_id; + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + return sta->addr; + } + + + return NULL; +} + +static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf, + bool mcast) +{ + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + int ret; + const u8 *addr; + struct ieee80211_key_seq seq; + u16 p1k[5]; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + addr = iwl_mvm_get_mac_addr(mvm, vif, sta); + /* get phase 1 key from mac80211 */ + ieee80211_get_key_rx_seq(keyconf, 0, &seq); + ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); + ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, + seq.tkip.iv32, p1k, 0); + break; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, + 0, NULL, 0); + break; + default: + ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, + 0, NULL, 0); + } + + return ret; +} + +static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, + struct ieee80211_key_conf *keyconf, + bool mcast) +{ + struct iwl_mvm_add_sta_key_cmd cmd = {}; + __le16 key_flags; + int ret; + u32 status; + + key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & + STA_KEY_FLG_KEYID_MSK); + key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP); + key_flags |= cpu_to_le16(STA_KEY_NOT_VALID); + + if (mcast) + key_flags |= cpu_to_le16(STA_KEY_MULTICAST); + + cmd.key_flags = key_flags; + cmd.key_offset = keyconf->hw_key_idx; + cmd.sta_id = sta_id; + + status = ADD_STA_SUCCESS; + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), + &cmd, &status); + + switch (status) { + case ADD_STA_SUCCESS: + IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n"); + break; + default: + ret = -EIO; + IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n"); + break; + } + + return ret; +} + +int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf, + bool have_key_offset) +{ + bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); + u8 sta_id; + int ret; + static const u8 __maybe_unused zero_addr[ETH_ALEN] = {0}; + + lockdep_assert_held(&mvm->mutex); + + /* Get the station id from the mvm local station table */ + sta_id = iwl_mvm_get_key_sta_id(vif, sta); + if (sta_id == IWL_MVM_STATION_COUNT) { + IWL_ERR(mvm, "Failed to find station id\n"); + return -EINVAL; + } + + if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false); + goto end; + } + + /* + * It is possible that the 'sta' parameter is NULL, and thus + * there is a need to retrieve the sta from the local station table. + */ + if (!sta) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(sta)) { + IWL_ERR(mvm, "Invalid station id\n"); + return -EINVAL; + } + } + + if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif)) + return -EINVAL; + + if (!have_key_offset) { + /* + * The D3 firmware hardcodes the PTK offset to 0, so we have to + * configure it there. As a result, this workaround exists to + * let the caller set the key offset (hw_key_idx), see d3.c. + */ + keyconf->hw_key_idx = iwl_mvm_set_fw_key_idx(mvm); + if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID) + return -ENOSPC; + } + + ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, mcast); + if (ret) { + __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table); + goto end; + } + + /* + * For WEP, the same key is used for multicast and unicast. Upload it + * again, using the same key offset, and now pointing the other one + * to the same key slot (offset). + * If this fails, remove the original as well. + */ + if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || + keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) { + ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, !mcast); + if (ret) { + __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table); + __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast); + } + } + +end: + IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", + keyconf->cipher, keyconf->keylen, keyconf->keyidx, + sta ? sta->addr : zero_addr, ret); + return ret; +} + +int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf) +{ + bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); + u8 sta_id; + int ret, i; + + lockdep_assert_held(&mvm->mutex); + + /* Get the station id from the mvm local station table */ + sta_id = iwl_mvm_get_key_sta_id(vif, sta); + + IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n", + keyconf->keyidx, sta_id); + + if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) + return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true); + + if (!__test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table)) { + IWL_ERR(mvm, "offset %d not used in fw key table.\n", + keyconf->hw_key_idx); + return -ENOENT; + } + + /* track which key was deleted last */ + for (i = 0; i < STA_KEY_MAX_NUM; i++) { + if (mvm->fw_key_deleted[i] < U8_MAX) + mvm->fw_key_deleted[i]++; + } + mvm->fw_key_deleted[keyconf->hw_key_idx] = 0; + + if (sta_id == IWL_MVM_STATION_COUNT) { + IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n"); + return 0; + } + + /* + * It is possible that the 'sta' parameter is NULL, and thus + * there is a need to retrieve the sta from the local station table, + * for example when a GTK is removed (where the sta_id will then be + * the AP ID, and no station was passed by mac80211.) + */ + if (!sta) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + if (!sta) { + IWL_ERR(mvm, "Invalid station id\n"); + return -EINVAL; + } + } + + if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif)) + return -EINVAL; + + ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast); + if (ret) + return ret; + + /* delete WEP key twice to get rid of (now useless) offset */ + if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || + keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) + ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, !mcast); + + return ret; +} + +void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, + u16 *phase1key) +{ + struct iwl_mvm_sta *mvm_sta; + u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta); + bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); + + if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT)) + return; + + rcu_read_lock(); + + if (!sta) { + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + if (WARN_ON(IS_ERR_OR_NULL(sta))) { + rcu_read_unlock(); + return; + } + } + + mvm_sta = iwl_mvm_sta_from_mac80211(sta); + iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, + iv32, phase1key, CMD_ASYNC); + rcu_read_unlock(); +} + +void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_add_sta_cmd cmd = { + .add_modify = STA_MODE_MODIFY, + .sta_id = mvmsta->sta_id, + .station_flags_msk = cpu_to_le32(STA_FLG_PS), + .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), + }; + int ret; + + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); +} + +void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + enum ieee80211_frame_release_type reason, + u16 cnt, u16 tids, bool more_data, + bool agg) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_add_sta_cmd cmd = { + .add_modify = STA_MODE_MODIFY, + .sta_id = mvmsta->sta_id, + .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, + .sleep_tx_count = cpu_to_le16(cnt), + .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), + }; + int tid, ret; + unsigned long _tids = tids; + + /* convert TIDs to ACs - we don't support TSPEC so that's OK + * Note that this field is reserved and unused by firmware not + * supporting GO uAPSD, so it's safe to always do this. + */ + for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) + cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]); + + /* If we're releasing frames from aggregation queues then check if the + * all queues combined that we're releasing frames from have + * - more frames than the service period, in which case more_data + * needs to be set + * - fewer than 'cnt' frames, in which case we need to adjust the + * firmware command (but do that unconditionally) + */ + if (agg) { + int remaining = cnt; + + spin_lock_bh(&mvmsta->lock); + for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) { + struct iwl_mvm_tid_data *tid_data; + u16 n_queued; + + tid_data = &mvmsta->tid_data[tid]; + if (WARN(tid_data->state != IWL_AGG_ON && + tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA, + "TID %d state is %d\n", + tid, tid_data->state)) { + spin_unlock_bh(&mvmsta->lock); + ieee80211_sta_eosp(sta); + return; + } + + n_queued = iwl_mvm_tid_queued(tid_data); + if (n_queued > remaining) { + more_data = true; + remaining = 0; + break; + } + remaining -= n_queued; + } + spin_unlock_bh(&mvmsta->lock); + + cmd.sleep_tx_count = cpu_to_le16(cnt - remaining); + if (WARN_ON(cnt - remaining == 0)) { + ieee80211_sta_eosp(sta); + return; + } + } + + /* Note: this is ignored by firmware not supporting GO uAPSD */ + if (more_data) + cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA); + + if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) { + mvmsta->next_status_eosp = true; + cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL); + } else { + cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD); + } + + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); +} + +void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mvm_eosp_notification *notif = (void *)pkt->data; + struct ieee80211_sta *sta; + u32 sta_id = le32_to_cpu(notif->sta_id); + + if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT)) + return; + + rcu_read_lock(); + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + if (!IS_ERR_OR_NULL(sta)) + ieee80211_sta_eosp(sta); + rcu_read_unlock(); +} + +void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, bool disable) +{ + struct iwl_mvm_add_sta_cmd cmd = { + .add_modify = STA_MODE_MODIFY, + .sta_id = mvmsta->sta_id, + .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0, + .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX), + .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), + }; + int ret; + + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); +} + +void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + bool disable) +{ + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + + spin_lock_bh(&mvm_sta->lock); + + if (mvm_sta->disable_tx == disable) { + spin_unlock_bh(&mvm_sta->lock); + return; + } + + mvm_sta->disable_tx = disable; + + /* + * Tell mac80211 to start/stop queuing tx for this station, + * but don't stop queuing if there are still pending frames + * for this station. + */ + if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) + ieee80211_sta_block_awake(mvm->hw, sta, disable); + + iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable); + + spin_unlock_bh(&mvm_sta->lock); +} + +void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + bool disable) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvm_sta; + int i; + + lockdep_assert_held(&mvm->mutex); + + /* Block/unblock all the stations of the given mvmvif */ + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(sta)) + continue; + + mvm_sta = iwl_mvm_sta_from_mac80211(sta); + if (mvm_sta->mac_id_n_color != + FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)) + continue; + + iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable); + } +} + +void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_sta *mvmsta; + + rcu_read_lock(); + + mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id); + + if (!WARN_ON(!mvmsta)) + iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true); + + rcu_read_unlock(); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h new file mode 100644 index 000000000000..eedb215eba3f --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -0,0 +1,426 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __sta_h__ +#define __sta_h__ + +#include +#include +#include + +#include "iwl-trans.h" /* for IWL_MAX_TID_COUNT */ +#include "fw-api.h" /* IWL_MVM_STATION_COUNT */ +#include "rs.h" + +struct iwl_mvm; +struct iwl_mvm_vif; + +/** + * DOC: station table - introduction + * + * The station table is a list of data structure that reprensent the stations. + * In STA/P2P client mode, the driver will hold one station for the AP/ GO. + * In GO/AP mode, the driver will have as many stations as associated clients. + * All these stations are reflected in the fw's station table. The driver + * keeps the fw's station table up to date with the ADD_STA command. Stations + * can be removed by the REMOVE_STA command. + * + * All the data related to a station is held in the structure %iwl_mvm_sta + * which is embed in the mac80211's %ieee80211_sta (in the drv_priv) area. + * This data includes the index of the station in the fw, per tid information + * (sequence numbers, Block-ack state machine, etc...). The stations are + * created and deleted by the %sta_state callback from %ieee80211_ops. + * + * The driver holds a map: %fw_id_to_mac_id that allows to fetch a + * %ieee80211_sta (and the %iwl_mvm_sta embedded into it) based on a fw + * station index. That way, the driver is able to get the tid related data in + * O(1) in time sensitive paths (Tx / Tx response / BA notification). These + * paths are triggered by the fw, and the driver needs to get a pointer to the + * %ieee80211 structure. This map helps to get that pointer quickly. + */ + +/** + * DOC: station table - locking + * + * As stated before, the station is created / deleted by mac80211's %sta_state + * callback from %ieee80211_ops which can sleep. The next paragraph explains + * the locking of a single stations, the next ones relates to the station + * table. + * + * The station holds the sequence number per tid. So this data needs to be + * accessed in the Tx path (which is softIRQ). It also holds the Block-Ack + * information (the state machine / and the logic that checks if the queues + * were drained), so it also needs to be accessible from the Tx response flow. + * In short, the station needs to be access from sleepable context as well as + * from tasklets, so the station itself needs a spinlock. + * + * The writers of %fw_id_to_mac_id map are serialized by the global mutex of + * the mvm op_mode. This is possible since %sta_state can sleep. + * The pointers in this map are RCU protected, hence we won't replace the + * station while we have Tx / Tx response / BA notification running. + * + * If a station is deleted while it still has packets in its A-MPDU queues, + * then the reclaim flow will notice that there is no station in the map for + * sta_id and it will dump the responses. + */ + +/** + * DOC: station table - internal stations + * + * The FW needs a few internal stations that are not reflected in + * mac80211, such as broadcast station in AP / GO mode, or AUX sta for + * scanning and P2P device (during the GO negotiation). + * For these kind of stations we have %iwl_mvm_int_sta struct which holds the + * data relevant for them from both %iwl_mvm_sta and %ieee80211_sta. + * Usually the data for these stations is static, so no locking is required, + * and no TID data as this is also not needed. + * One thing to note, is that these stations have an ID in the fw, but not + * in mac80211. In order to "reserve" them a sta_id in %fw_id_to_mac_id + * we fill ERR_PTR(EINVAL) in this mapping and all other dereferencing of + * pointers from this mapping need to check that the value is not error + * or NULL. + * + * Currently there is only one auxiliary station for scanning, initialized + * on init. + */ + +/** + * DOC: station table - AP Station in STA mode + * + * %iwl_mvm_vif includes the index of the AP station in the fw's STA table: + * %ap_sta_id. To get the point to the corresponding %ieee80211_sta, + * &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove + * the AP station from the fw before setting the MAC context as unassociated. + * Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is + * removed by mac80211, but the station won't be removed in the fw until the + * VIF is set as unassociated. Then, %ap_sta_id will be invalidated. + */ + +/** + * DOC: station table - Drain vs. Flush + * + * Flush means that all the frames in the SCD queue are dumped regardless the + * station to which they were sent. We do that when we disassociate and before + * we remove the STA of the AP. The flush can be done synchronously against the + * fw. + * Drain means that the fw will drop all the frames sent to a specific station. + * This is useful when a client (if we are IBSS / GO or AP) disassociates. In + * that case, we need to drain all the frames for that client from the AC queues + * that are shared with the other clients. Only then, we can remove the STA in + * the fw. In order to do so, we track the non-AMPDU packets for each station. + * If mac80211 removes a STA and if it still has non-AMPDU packets pending in + * the queues, we mark this station as %EBUSY in %fw_id_to_mac_id, and drop all + * the frames for this STA (%iwl_mvm_rm_sta). When the last frame is dropped + * (we know about it with its Tx response), we remove the station in fw and set + * it as %NULL in %fw_id_to_mac_id: this is the purpose of + * %iwl_mvm_sta_drained_wk. + */ + +/** + * DOC: station table - fw restart + * + * When the fw asserts, or we have any other issue that requires to reset the + * driver, we require mac80211 to reconfigure the driver. Since the private + * data of the stations is embed in mac80211's %ieee80211_sta, that data will + * not be zeroed and needs to be reinitialized manually. + * %IWL_MVM_STATUS_IN_HW_RESTART is set during restart and that will hint us + * that we must not allocate a new sta_id but reuse the previous one. This + * means that the stations being re-added after the reset will have the same + * place in the fw as before the reset. We do need to zero the %fw_id_to_mac_id + * map, since the stations aren't in the fw any more. Internal stations that + * are not added by mac80211 will be re-added in the init flow that is called + * after the restart: mac80211 call's %iwl_mvm_mac_start which calls to + * %iwl_mvm_up. + */ + +/** + * DOC: AP mode - PS + * + * When a station is asleep, the fw will set it as "asleep". All frames on + * shared queues (i.e. non-aggregation queues) to that station will be dropped + * by the fw (%TX_STATUS_FAIL_DEST_PS failure code). + * + * AMPDUs are in a separate queue that is stopped by the fw. We just need to + * let mac80211 know when there are frames in these queues so that it can + * properly handle trigger frames. + * + * When a trigger frame is received, mac80211 tells the driver to send frames + * from the AMPDU queues or sends frames to non-aggregation queues itself, + * depending on which ACs are delivery-enabled and what TID has frames to + * transmit. Note that mac80211 has all the knowledge since all the non-agg + * frames are buffered / filtered, and the driver tells mac80211 about agg + * frames). The driver needs to tell the fw to let frames out even if the + * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count. + * + * When we receive a frame from that station with PM bit unset, the driver + * needs to let the fw know that this station isn't asleep any more. This is + * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signaling the + * station's wakeup. + * + * For a GO, the Service Period might be cut short due to an absence period + * of the GO. In this (and all other cases) the firmware notifies us with the + * EOSP_NOTIFICATION, and we notify mac80211 of that. Further frames that we + * already sent to the device will be rejected again. + * + * See also "AP support for powersaving clients" in mac80211.h. + */ + +/** + * enum iwl_mvm_agg_state + * + * The state machine of the BA agreement establishment / tear down. + * These states relate to a specific RA / TID. + * + * @IWL_AGG_OFF: aggregation is not used + * @IWL_AGG_STARTING: aggregation are starting (between start and oper) + * @IWL_AGG_ON: aggregation session is up + * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the + * HW queue to be empty from packets for this RA /TID. + * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the + * HW queue to be empty from packets for this RA /TID. + */ +enum iwl_mvm_agg_state { + IWL_AGG_OFF = 0, + IWL_AGG_STARTING, + IWL_AGG_ON, + IWL_EMPTYING_HW_QUEUE_ADDBA, + IWL_EMPTYING_HW_QUEUE_DELBA, +}; + +/** + * struct iwl_mvm_tid_data - holds the states for each RA / TID + * @seq_number: the next WiFi sequence number to use + * @next_reclaimed: the WiFi sequence number of the next packet to be acked. + * This is basically (last acked packet++). + * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the + * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). + * @reduced_tpc: Reduced tx power. Holds the data between the + * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). + * @state: state of the BA agreement establishment / tear down. + * @txq_id: Tx queue used by the BA session + * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or + * the first packet to be sent in legacy HW queue in Tx AGG stop flow. + * Basically when next_reclaimed reaches ssn, we can tell mac80211 that + * we are ready to finish the Tx AGG stop / start flow. + * @tx_time: medium time consumed by this A-MPDU + */ +struct iwl_mvm_tid_data { + u16 seq_number; + u16 next_reclaimed; + /* The rest is Tx AGG related */ + u32 rate_n_flags; + u8 reduced_tpc; + enum iwl_mvm_agg_state state; + u16 txq_id; + u16 ssn; + u16 tx_time; +}; + +static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) +{ + return ieee80211_sn_sub(IEEE80211_SEQ_TO_SN(tid_data->seq_number), + tid_data->next_reclaimed); +} + +/** + * struct iwl_mvm_sta - representation of a station in the driver + * @sta_id: the index of the station in the fw (will be replaced by id_n_color) + * @tfd_queue_msk: the tfd queues used by the station + * @hw_queue: per-AC mapping of the TFD queues used by station + * @mac_id_n_color: the MAC context this station is linked to + * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for + * tid. + * @max_agg_bufsize: the maximal size of the AGG buffer for this station + * @bt_reduced_txpower: is reduced tx power enabled for this station + * @next_status_eosp: the next reclaimed packet is a PS-Poll response and + * we need to signal the EOSP + * @lock: lock to protect the whole struct. Since %tid_data is access from Tx + * and from Tx response flow, it needs a spinlock. + * @tid_data: per tid data. Look at %iwl_mvm_tid_data. + * @tx_protection: reference counter for controlling the Tx protection. + * @tt_tx_protection: is thermal throttling enable Tx protection? + * @disable_tx: is tx to this STA disabled? + * @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON) + * + * When mac80211 creates a station it reserves some space (hw->sta_data_size) + * in the structure for use by driver. This structure is placed in that + * space. + * + */ +struct iwl_mvm_sta { + u32 sta_id; + u32 tfd_queue_msk; + u8 hw_queue[IEEE80211_NUM_ACS]; + u32 mac_id_n_color; + u16 tid_disable_agg; + u8 max_agg_bufsize; + bool bt_reduced_txpower; + bool next_status_eosp; + spinlock_t lock; + struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT]; + struct iwl_lq_sta lq_sta; + struct ieee80211_vif *vif; + + /* Temporary, until the new TLC will control the Tx protection */ + s8 tx_protection; + bool tt_tx_protection; + + bool disable_tx; + u8 agg_tids; +}; + +static inline struct iwl_mvm_sta * +iwl_mvm_sta_from_mac80211(struct ieee80211_sta *sta) +{ + return (void *)sta->drv_priv; +} + +/** + * struct iwl_mvm_int_sta - representation of an internal station (auxiliary or + * broadcast) + * @sta_id: the index of the station in the fw (will be replaced by id_n_color) + * @tfd_queue_msk: the tfd queues used by the station + */ +struct iwl_mvm_int_sta { + u32 sta_id; + u32 tfd_queue_msk; +}; + +int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + bool update); +int iwl_mvm_add_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int iwl_mvm_update_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int iwl_mvm_rm_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u8 sta_id); +int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + bool have_key_offset); +int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *keyconf); + +void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, + u16 *phase1key); + +void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); + +/* AMPDU */ +int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + int tid, u16 ssn, bool start); +int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u16 *ssn); +int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u8 buf_size); +int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid); +int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid); + +int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm); +void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm); + +int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); + +void iwl_mvm_sta_drained_wk(struct work_struct *wk); +void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, + struct ieee80211_sta *sta); +void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + enum ieee80211_frame_release_type reason, + u16 cnt, u16 tids, bool more_data, + bool agg); +int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, + bool drain); +void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, bool disable); +void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + bool disable); +void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + bool disable); +void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif); + +#endif /* __sta_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c new file mode 100644 index 000000000000..fe2fa5650443 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c @@ -0,0 +1,732 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include "mvm.h" +#include "time-event.h" +#include "iwl-io.h" +#include "iwl-prph.h" + +#define TU_TO_US(x) (x * 1024) +#define TU_TO_MS(x) (TU_TO_US(x) / 1000) + +void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + int i; + + lockdep_assert_held(&mvm->mutex); + + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (!sta || IS_ERR(sta) || !sta->tdls) + continue; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + ieee80211_tdls_oper_request(mvmsta->vif, sta->addr, + NL80211_TDLS_TEARDOWN, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, + GFP_KERNEL); + } +} + +int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + int count = 0; + int i; + + lockdep_assert_held(&mvm->mutex); + + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (!sta || IS_ERR(sta) || !sta->tdls) + continue; + + if (vif) { + mvmsta = iwl_mvm_sta_from_mac80211(sta); + if (mvmsta->vif != vif) + continue; + } + + count++; + } + + return count; +} + +static void iwl_mvm_tdls_config(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_rx_packet *pkt; + struct iwl_tdls_config_res *resp; + struct iwl_tdls_config_cmd tdls_cfg_cmd = {}; + struct iwl_host_cmd cmd = { + .id = TDLS_CONFIG_CMD, + .flags = CMD_WANT_SKB, + .data = { &tdls_cfg_cmd, }, + .len = { sizeof(struct iwl_tdls_config_cmd), }, + }; + struct ieee80211_sta *sta; + int ret, i, cnt; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + lockdep_assert_held(&mvm->mutex); + + tdls_cfg_cmd.id_and_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); + tdls_cfg_cmd.tx_to_ap_tid = IWL_MVM_TDLS_FW_TID; + tdls_cfg_cmd.tx_to_ap_ssn = cpu_to_le16(0); /* not used for now */ + + /* for now the Tx cmd is empty and unused */ + + /* populate TDLS peer data */ + cnt = 0; + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(sta) || !sta->tdls) + continue; + + tdls_cfg_cmd.sta_info[cnt].sta_id = i; + tdls_cfg_cmd.sta_info[cnt].tx_to_peer_tid = + IWL_MVM_TDLS_FW_TID; + tdls_cfg_cmd.sta_info[cnt].tx_to_peer_ssn = cpu_to_le16(0); + tdls_cfg_cmd.sta_info[cnt].is_initiator = + cpu_to_le32(sta->tdls_initiator ? 1 : 0); + + cnt++; + } + + tdls_cfg_cmd.tdls_peer_count = cnt; + IWL_DEBUG_TDLS(mvm, "send TDLS config to FW for %d peers\n", cnt); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (WARN_ON_ONCE(ret)) + return; + + pkt = cmd.resp_pkt; + + WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) != sizeof(*resp)); + + /* we don't really care about the response at this point */ + + iwl_free_resp(&cmd); +} + +void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool sta_added) +{ + int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif); + + /* when the first peer joins, send a power update first */ + if (tdls_sta_cnt == 1 && sta_added) + iwl_mvm_power_update_mac(mvm); + + /* configure the FW with TDLS peer info */ + iwl_mvm_tdls_config(mvm, vif); + + /* when the last peer leaves, send a power update last */ + if (tdls_sta_cnt == 0 && !sta_added) + iwl_mvm_power_update_mac(mvm); +} + +void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int; + + /* + * iwl_mvm_protect_session() reads directly from the device + * (the system time), so make sure it is available. + */ + if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_TDLS)) + return; + + mutex_lock(&mvm->mutex); + /* Protect the session to hear the TDLS setup response on the channel */ + iwl_mvm_protect_session(mvm, vif, duration, duration, 100, true); + mutex_unlock(&mvm->mutex); + + iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS); +} + +static const char * +iwl_mvm_tdls_cs_state_str(enum iwl_mvm_tdls_cs_state state) +{ + switch (state) { + case IWL_MVM_TDLS_SW_IDLE: + return "IDLE"; + case IWL_MVM_TDLS_SW_REQ_SENT: + return "REQ SENT"; + case IWL_MVM_TDLS_SW_RESP_RCVD: + return "RESP RECEIVED"; + case IWL_MVM_TDLS_SW_REQ_RCVD: + return "REQ RECEIVED"; + case IWL_MVM_TDLS_SW_ACTIVE: + return "ACTIVE"; + } + + return NULL; +} + +static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm, + enum iwl_mvm_tdls_cs_state state) +{ + if (mvm->tdls_cs.state == state) + return; + + IWL_DEBUG_TDLS(mvm, "TDLS channel switch state: %s -> %s\n", + iwl_mvm_tdls_cs_state_str(mvm->tdls_cs.state), + iwl_mvm_tdls_cs_state_str(state)); + mvm->tdls_cs.state = state; + + /* we only send requests to our switching peer - update sent time */ + if (state == IWL_MVM_TDLS_SW_REQ_SENT) + mvm->tdls_cs.peer.sent_timestamp = + iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG); + + if (state == IWL_MVM_TDLS_SW_IDLE) + mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT; +} + +void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_tdls_channel_switch_notif *notif = (void *)pkt->data; + struct ieee80211_sta *sta; + unsigned int delay; + struct iwl_mvm_sta *mvmsta; + struct ieee80211_vif *vif; + u32 sta_id = le32_to_cpu(notif->sta_id); + + lockdep_assert_held(&mvm->mutex); + + /* can fail sometimes */ + if (!le32_to_cpu(notif->status)) { + iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); + return; + } + + if (WARN_ON(sta_id >= IWL_MVM_STATION_COUNT)) + return; + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + /* the station may not be here, but if it is, it must be a TDLS peer */ + if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls)) + return; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + vif = mvmsta->vif; + + /* + * Update state and possibly switch again after this is over (DTIM). + * Also convert TU to msec. + */ + delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int); + mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, + msecs_to_jiffies(delay)); + + iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE); +} + +static int +iwl_mvm_tdls_check_action(struct iwl_mvm *mvm, + enum iwl_tdls_channel_switch_type type, + const u8 *peer, bool peer_initiator, u32 timestamp) +{ + bool same_peer = false; + int ret = 0; + + /* get the existing peer if it's there */ + if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE && + mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) { + struct ieee80211_sta *sta = rcu_dereference_protected( + mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id], + lockdep_is_held(&mvm->mutex)); + if (!IS_ERR_OR_NULL(sta)) + same_peer = ether_addr_equal(peer, sta->addr); + } + + switch (mvm->tdls_cs.state) { + case IWL_MVM_TDLS_SW_IDLE: + /* + * might be spurious packet from the peer after the switch is + * already done + */ + if (type == TDLS_MOVE_CH) + ret = -EINVAL; + break; + case IWL_MVM_TDLS_SW_REQ_SENT: + /* only allow requests from the same peer */ + if (!same_peer) + ret = -EBUSY; + else if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH && + !peer_initiator) + /* + * We received a ch-switch request while an outgoing + * one is pending. Allow it if the peer is the link + * initiator. + */ + ret = -EBUSY; + else if (type == TDLS_SEND_CHAN_SW_REQ) + /* wait for idle before sending another request */ + ret = -EBUSY; + else if (timestamp <= mvm->tdls_cs.peer.sent_timestamp) + /* we got a stale response - ignore it */ + ret = -EINVAL; + break; + case IWL_MVM_TDLS_SW_RESP_RCVD: + /* + * we are waiting for the FW to give an "active" notification, + * so ignore requests in the meantime + */ + ret = -EBUSY; + break; + case IWL_MVM_TDLS_SW_REQ_RCVD: + /* as above, allow the link initiator to proceed */ + if (type == TDLS_SEND_CHAN_SW_REQ) { + if (!same_peer) + ret = -EBUSY; + else if (peer_initiator) /* they are the initiator */ + ret = -EBUSY; + } else if (type == TDLS_MOVE_CH) { + ret = -EINVAL; + } + break; + case IWL_MVM_TDLS_SW_ACTIVE: + /* + * the only valid request when active is a request to return + * to the base channel by the current off-channel peer + */ + if (type != TDLS_MOVE_CH || !same_peer) + ret = -EBUSY; + break; + } + + if (ret) + IWL_DEBUG_TDLS(mvm, + "Invalid TDLS action %d state %d peer %pM same_peer %d initiator %d\n", + type, mvm->tdls_cs.state, peer, same_peer, + peer_initiator); + + return ret; +} + +static int +iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + enum iwl_tdls_channel_switch_type type, + const u8 *peer, bool peer_initiator, + u8 oper_class, + struct cfg80211_chan_def *chandef, + u32 timestamp, u16 switch_time, + u16 switch_timeout, struct sk_buff *skb, + u32 ch_sw_tm_ie) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + struct ieee80211_tx_info *info; + struct ieee80211_hdr *hdr; + struct iwl_tdls_channel_switch_cmd cmd = {0}; + int ret; + + lockdep_assert_held(&mvm->mutex); + + ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator, + timestamp); + if (ret) + return ret; + + if (!skb || WARN_ON(skb->len > IWL_TDLS_CH_SW_FRAME_MAX_SIZE)) { + ret = -EINVAL; + goto out; + } + + cmd.switch_type = type; + cmd.timing.frame_timestamp = cpu_to_le32(timestamp); + cmd.timing.switch_time = cpu_to_le32(switch_time); + cmd.timing.switch_timeout = cpu_to_le32(switch_timeout); + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, peer); + if (!sta) { + rcu_read_unlock(); + ret = -ENOENT; + goto out; + } + mvmsta = iwl_mvm_sta_from_mac80211(sta); + cmd.peer_sta_id = cpu_to_le32(mvmsta->sta_id); + + if (!chandef) { + if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT && + mvm->tdls_cs.peer.chandef.chan) { + /* actually moving to the channel */ + chandef = &mvm->tdls_cs.peer.chandef; + } else if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_ACTIVE && + type == TDLS_MOVE_CH) { + /* we need to return to base channel */ + struct ieee80211_chanctx_conf *chanctx = + rcu_dereference(vif->chanctx_conf); + + if (WARN_ON_ONCE(!chanctx)) { + rcu_read_unlock(); + goto out; + } + + chandef = &chanctx->def; + } + } + + if (chandef) { + cmd.ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ? + PHY_BAND_24 : PHY_BAND_5); + cmd.ci.channel = chandef->chan->hw_value; + cmd.ci.width = iwl_mvm_get_channel_width(chandef); + cmd.ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef); + } + + /* keep quota calculation simple for now - 50% of DTIM for TDLS */ + cmd.timing.max_offchan_duration = + cpu_to_le32(TU_TO_US(vif->bss_conf.dtim_period * + vif->bss_conf.beacon_int) / 2); + + /* Switch time is the first element in the switch-timing IE. */ + cmd.frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2); + + info = IEEE80211_SKB_CB(skb); + hdr = (void *)skb->data; + if (info->control.hw_key) { + if (info->control.hw_key->cipher != WLAN_CIPHER_SUITE_CCMP) { + rcu_read_unlock(); + ret = -EINVAL; + goto out; + } + iwl_mvm_set_tx_cmd_ccmp(info, &cmd.frame.tx_cmd); + } + + iwl_mvm_set_tx_cmd(mvm, skb, &cmd.frame.tx_cmd, info, + mvmsta->sta_id); + + iwl_mvm_set_tx_cmd_rate(mvm, &cmd.frame.tx_cmd, info, sta, + hdr->frame_control); + rcu_read_unlock(); + + memcpy(cmd.frame.data, skb->data, skb->len); + + ret = iwl_mvm_send_cmd_pdu(mvm, TDLS_CHANNEL_SWITCH_CMD, 0, + sizeof(cmd), &cmd); + if (ret) { + IWL_ERR(mvm, "Failed to send TDLS_CHANNEL_SWITCH cmd: %d\n", + ret); + goto out; + } + + /* channel switch has started, update state */ + if (type != TDLS_MOVE_CH) { + mvm->tdls_cs.cur_sta_id = mvmsta->sta_id; + iwl_mvm_tdls_update_cs_state(mvm, + type == TDLS_SEND_CHAN_SW_REQ ? + IWL_MVM_TDLS_SW_REQ_SENT : + IWL_MVM_TDLS_SW_REQ_RCVD); + } else { + iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_RESP_RCVD); + } + +out: + + /* channel switch failed - we are idle */ + if (ret) + iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); + + return ret; +} + +void iwl_mvm_tdls_ch_switch_work(struct work_struct *work) +{ + struct iwl_mvm *mvm; + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + struct ieee80211_vif *vif; + unsigned int delay; + int ret; + + mvm = container_of(work, struct iwl_mvm, tdls_cs.dwork.work); + mutex_lock(&mvm->mutex); + + /* called after an active channel switch has finished or timed-out */ + iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); + + /* station might be gone, in that case do nothing */ + if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) + goto out; + + sta = rcu_dereference_protected( + mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id], + lockdep_is_held(&mvm->mutex)); + /* the station may not be here, but if it is, it must be a TDLS peer */ + if (!sta || IS_ERR(sta) || WARN_ON(!sta->tdls)) + goto out; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + vif = mvmsta->vif; + ret = iwl_mvm_tdls_config_channel_switch(mvm, vif, + TDLS_SEND_CHAN_SW_REQ, + sta->addr, + mvm->tdls_cs.peer.initiator, + mvm->tdls_cs.peer.op_class, + &mvm->tdls_cs.peer.chandef, + 0, 0, 0, + mvm->tdls_cs.peer.skb, + mvm->tdls_cs.peer.ch_sw_tm_ie); + if (ret) + IWL_ERR(mvm, "Not sending TDLS channel switch: %d\n", ret); + + /* retry after a DTIM if we failed sending now */ + delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int); + queue_delayed_work(system_wq, &mvm->tdls_cs.dwork, + msecs_to_jiffies(delay)); +out: + mutex_unlock(&mvm->mutex); +} + +int +iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u8 oper_class, + struct cfg80211_chan_def *chandef, + struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_sta *mvmsta; + unsigned int delay; + int ret; + + mutex_lock(&mvm->mutex); + + IWL_DEBUG_TDLS(mvm, "TDLS channel switch with %pM ch %d width %d\n", + sta->addr, chandef->chan->center_freq, chandef->width); + + /* we only support a single peer for channel switching */ + if (mvm->tdls_cs.peer.sta_id != IWL_MVM_STATION_COUNT) { + IWL_DEBUG_TDLS(mvm, + "Existing peer. Can't start switch with %pM\n", + sta->addr); + ret = -EBUSY; + goto out; + } + + ret = iwl_mvm_tdls_config_channel_switch(mvm, vif, + TDLS_SEND_CHAN_SW_REQ, + sta->addr, sta->tdls_initiator, + oper_class, chandef, 0, 0, 0, + tmpl_skb, ch_sw_tm_ie); + if (ret) + goto out; + + /* + * Mark the peer as "in tdls switch" for this vif. We only allow a + * single such peer per vif. + */ + mvm->tdls_cs.peer.skb = skb_copy(tmpl_skb, GFP_KERNEL); + if (!mvm->tdls_cs.peer.skb) { + ret = -ENOMEM; + goto out; + } + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + mvm->tdls_cs.peer.sta_id = mvmsta->sta_id; + mvm->tdls_cs.peer.chandef = *chandef; + mvm->tdls_cs.peer.initiator = sta->tdls_initiator; + mvm->tdls_cs.peer.op_class = oper_class; + mvm->tdls_cs.peer.ch_sw_tm_ie = ch_sw_tm_ie; + + /* + * Wait for 2 DTIM periods before attempting the next switch. The next + * switch will be made sooner if the current one completes before that. + */ + delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period * + vif->bss_conf.beacon_int); + mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, + msecs_to_jiffies(delay)); + +out: + mutex_unlock(&mvm->mutex); + return ret; +} + +void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct ieee80211_sta *cur_sta; + bool wait_for_phy = false; + + mutex_lock(&mvm->mutex); + + IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr); + + /* we only support a single peer for channel switching */ + if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) { + IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr); + goto out; + } + + cur_sta = rcu_dereference_protected( + mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id], + lockdep_is_held(&mvm->mutex)); + /* make sure it's the same peer */ + if (cur_sta != sta) + goto out; + + /* + * If we're currently in a switch because of the now canceled peer, + * wait a DTIM here to make sure the phy is back on the base channel. + * We can't otherwise force it. + */ + if (mvm->tdls_cs.cur_sta_id == mvm->tdls_cs.peer.sta_id && + mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE) + wait_for_phy = true; + + mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; + dev_kfree_skb(mvm->tdls_cs.peer.skb); + mvm->tdls_cs.peer.skb = NULL; + +out: + mutex_unlock(&mvm->mutex); + + /* make sure the phy is on the base channel */ + if (wait_for_phy) + msleep(TU_TO_MS(vif->bss_conf.dtim_period * + vif->bss_conf.beacon_int)); + + /* flush the channel switch state */ + flush_delayed_work(&mvm->tdls_cs.dwork); + + IWL_DEBUG_TDLS(mvm, "TDLS ending channel switch with %pM\n", sta->addr); +} + +void +iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_tdls_ch_sw_params *params) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + enum iwl_tdls_channel_switch_type type; + unsigned int delay; + const char *action_str = + params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ? + "REQ" : "RESP"; + + mutex_lock(&mvm->mutex); + + IWL_DEBUG_TDLS(mvm, + "Received TDLS ch switch action %s from %pM status %d\n", + action_str, params->sta->addr, params->status); + + /* + * we got a non-zero status from a peer we were switching to - move to + * the idle state and retry again later + */ + if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE && + params->status != 0 && + mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT && + mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) { + struct ieee80211_sta *cur_sta; + + /* make sure it's the same peer */ + cur_sta = rcu_dereference_protected( + mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id], + lockdep_is_held(&mvm->mutex)); + if (cur_sta == params->sta) { + iwl_mvm_tdls_update_cs_state(mvm, + IWL_MVM_TDLS_SW_IDLE); + goto retry; + } + } + + type = (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST) ? + TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH : TDLS_MOVE_CH; + + iwl_mvm_tdls_config_channel_switch(mvm, vif, type, params->sta->addr, + params->sta->tdls_initiator, 0, + params->chandef, params->timestamp, + params->switch_time, + params->switch_timeout, + params->tmpl_skb, + params->ch_sw_tm_ie); + +retry: + /* register a timeout in case we don't succeed in switching */ + delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int * + 1024 / 1000; + mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, + msecs_to_jiffies(delay)); + mutex_unlock(&mvm->mutex); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h b/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h new file mode 100644 index 000000000000..79ab6beb6b26 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h @@ -0,0 +1,97 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __IWL_MVM_TESTMODE_H__ +#define __IWL_MVM_TESTMODE_H__ + +/** + * enum iwl_mvm_testmode_attrs - testmode attributes inside NL80211_ATTR_TESTDATA + * @IWL_MVM_TM_ATTR_UNSPEC: (invalid attribute) + * @IWL_MVM_TM_ATTR_CMD: sub command, see &enum iwl_mvm_testmode_commands (u32) + * @IWL_MVM_TM_ATTR_NOA_DURATION: requested NoA duration (u32) + * @IWL_MVM_TM_ATTR_BEACON_FILTER_STATE: beacon filter state (0 or 1, u32) + */ +enum iwl_mvm_testmode_attrs { + IWL_MVM_TM_ATTR_UNSPEC, + IWL_MVM_TM_ATTR_CMD, + IWL_MVM_TM_ATTR_NOA_DURATION, + IWL_MVM_TM_ATTR_BEACON_FILTER_STATE, + + /* keep last */ + NUM_IWL_MVM_TM_ATTRS, + IWL_MVM_TM_ATTR_MAX = NUM_IWL_MVM_TM_ATTRS - 1, +}; + +/** + * enum iwl_mvm_testmode_commands - MVM testmode commands + * @IWL_MVM_TM_CMD_SET_NOA: set NoA on GO vif for testing + * @IWL_MVM_TM_CMD_SET_BEACON_FILTER: turn beacon filtering off/on + */ +enum iwl_mvm_testmode_commands { + IWL_MVM_TM_CMD_SET_NOA, + IWL_MVM_TM_CMD_SET_BEACON_FILTER, +}; + +#endif /* __IWL_MVM_TESTMODE_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c new file mode 100644 index 000000000000..7530eb23035d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -0,0 +1,872 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include + +#include "iwl-notif-wait.h" +#include "iwl-trans.h" +#include "fw-api.h" +#include "time-event.h" +#include "mvm.h" +#include "iwl-io.h" +#include "iwl-prph.h" + +/* + * For the high priority TE use a time event type that has similar priority to + * the FW's action scan priority. + */ +#define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE +#define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC + +void iwl_mvm_te_clear_data(struct iwl_mvm *mvm, + struct iwl_mvm_time_event_data *te_data) +{ + lockdep_assert_held(&mvm->time_event_lock); + + if (!te_data->vif) + return; + + list_del(&te_data->list); + te_data->running = false; + te_data->uid = 0; + te_data->id = TE_MAX; + te_data->vif = NULL; +} + +void iwl_mvm_roc_done_wk(struct work_struct *wk) +{ + struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk); + u32 queues = 0; + + /* + * Clear the ROC_RUNNING /ROC_AUX_RUNNING status bit. + * This will cause the TX path to drop offchannel transmissions. + * That would also be done by mac80211, but it is racy, in particular + * in the case that the time event actually completed in the firmware + * (which is handled in iwl_mvm_te_handle_notif). + */ + if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status)) { + queues |= BIT(IWL_MVM_OFFCHANNEL_QUEUE); + iwl_mvm_unref(mvm, IWL_MVM_REF_ROC); + } + if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) { + queues |= BIT(mvm->aux_queue); + iwl_mvm_unref(mvm, IWL_MVM_REF_ROC_AUX); + } + + synchronize_net(); + + /* + * Flush the offchannel queue -- this is called when the time + * event finishes or is canceled, so that frames queued for it + * won't get stuck on the queue and be transmitted in the next + * time event. + * We have to send the command asynchronously since this cannot + * be under the mutex for locking reasons, but that's not an + * issue as it will have to complete before the next command is + * executed, and a new time event means a new command. + */ + iwl_mvm_flush_tx_path(mvm, queues, CMD_ASYNC); +} + +static void iwl_mvm_roc_finished(struct iwl_mvm *mvm) +{ + /* + * Of course, our status bit is just as racy as mac80211, so in + * addition, fire off the work struct which will drop all frames + * from the hardware queues that made it through the race. First + * it will of course synchronize the TX path to make sure that + * any *new* TX will be rejected. + */ + schedule_work(&mvm->roc_done_wk); +} + +static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm) +{ + struct ieee80211_vif *csa_vif; + + rcu_read_lock(); + + csa_vif = rcu_dereference(mvm->csa_vif); + if (!csa_vif || !csa_vif->csa_active) + goto out_unlock; + + IWL_DEBUG_TE(mvm, "CSA NOA started\n"); + + /* + * CSA NoA is started but we still have beacons to + * transmit on the current channel. + * So we just do nothing here and the switch + * will be performed on the last TBTT. + */ + if (!ieee80211_csa_is_complete(csa_vif)) { + IWL_WARN(mvm, "CSA NOA started too early\n"); + goto out_unlock; + } + + ieee80211_csa_finish(csa_vif); + + rcu_read_unlock(); + + RCU_INIT_POINTER(mvm->csa_vif, NULL); + + return; + +out_unlock: + rcu_read_unlock(); +} + +static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + const char *errmsg) +{ + if (vif->type != NL80211_IFTYPE_STATION) + return false; + if (vif->bss_conf.assoc && vif->bss_conf.dtim_period) + return false; + if (errmsg) + IWL_ERR(mvm, "%s\n", errmsg); + + iwl_mvm_connection_loss(mvm, vif, errmsg); + return true; +} + +static void +iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm, + struct iwl_mvm_time_event_data *te_data, + struct iwl_time_event_notif *notif) +{ + struct ieee80211_vif *vif = te_data->vif; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (!notif->status) + IWL_DEBUG_TE(mvm, "CSA time event failed to start\n"); + + switch (te_data->vif->type) { + case NL80211_IFTYPE_AP: + if (!notif->status) + mvmvif->csa_failed = true; + iwl_mvm_csa_noa_start(mvm); + break; + case NL80211_IFTYPE_STATION: + if (!notif->status) { + iwl_mvm_connection_loss(mvm, vif, + "CSA TE failed to start"); + break; + } + iwl_mvm_csa_client_absent(mvm, te_data->vif); + ieee80211_chswitch_done(te_data->vif, true); + break; + default: + /* should never happen */ + WARN_ON_ONCE(1); + break; + } + + /* we don't need it anymore */ + iwl_mvm_te_clear_data(mvm, te_data); +} + +static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm, + struct iwl_time_event_notif *notif, + struct iwl_mvm_time_event_data *te_data) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_time_event *te_trig; + int i; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT)) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT); + te_trig = (void *)trig->data; + + if (!iwl_fw_dbg_trigger_check_stop(mvm, te_data->vif, trig)) + return; + + for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) { + u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id); + u32 trig_action_bitmap = + le32_to_cpu(te_trig->time_events[i].action_bitmap); + u32 trig_status_bitmap = + le32_to_cpu(te_trig->time_events[i].status_bitmap); + + if (trig_te_id != te_data->id || + !(trig_action_bitmap & le32_to_cpu(notif->action)) || + !(trig_status_bitmap & BIT(le32_to_cpu(notif->status)))) + continue; + + iwl_mvm_fw_dbg_collect_trig(mvm, trig, + "Time event %d Action 0x%x received status: %d", + te_data->id, + le32_to_cpu(notif->action), + le32_to_cpu(notif->status)); + break; + } +} + +/* + * Handles a FW notification for an event that is known to the driver. + * + * @mvm: the mvm component + * @te_data: the time event data + * @notif: the notification data corresponding the time event data. + */ +static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, + struct iwl_mvm_time_event_data *te_data, + struct iwl_time_event_notif *notif) +{ + lockdep_assert_held(&mvm->time_event_lock); + + IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n", + le32_to_cpu(notif->unique_id), + le32_to_cpu(notif->action)); + + iwl_mvm_te_check_trigger(mvm, notif, te_data); + + /* + * The FW sends the start/end time event notifications even for events + * that it fails to schedule. This is indicated in the status field of + * the notification. This happens in cases that the scheduler cannot + * find a schedule that can handle the event (for example requesting a + * P2P Device discoveribility, while there are other higher priority + * events in the system). + */ + if (!le32_to_cpu(notif->status)) { + const char *msg; + + if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START)) + msg = "Time Event start notification failure"; + else + msg = "Time Event end notification failure"; + + IWL_DEBUG_TE(mvm, "%s\n", msg); + + if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) { + iwl_mvm_te_clear_data(mvm, te_data); + return; + } + } + + if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) { + IWL_DEBUG_TE(mvm, + "TE ended - current time %lu, estimated end %lu\n", + jiffies, te_data->end_jiffies); + + switch (te_data->vif->type) { + case NL80211_IFTYPE_P2P_DEVICE: + ieee80211_remain_on_channel_expired(mvm->hw); + iwl_mvm_roc_finished(mvm); + break; + case NL80211_IFTYPE_STATION: + /* + * By now, we should have finished association + * and know the dtim period. + */ + iwl_mvm_te_check_disconnect(mvm, te_data->vif, + "No association and the time event is over already..."); + break; + default: + break; + } + + iwl_mvm_te_clear_data(mvm, te_data); + } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) { + te_data->running = true; + te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration); + + if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { + set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); + iwl_mvm_ref(mvm, IWL_MVM_REF_ROC); + ieee80211_ready_on_channel(mvm->hw); + } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) { + iwl_mvm_te_handle_notify_csa(mvm, te_data, notif); + } + } else { + IWL_WARN(mvm, "Got TE with unknown action\n"); + } +} + +/* + * Handle A Aux ROC time event + */ +static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm, + struct iwl_time_event_notif *notif) +{ + struct iwl_mvm_time_event_data *te_data, *tmp; + bool aux_roc_te = false; + + list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) { + if (le32_to_cpu(notif->unique_id) == te_data->uid) { + aux_roc_te = true; + break; + } + } + if (!aux_roc_te) /* Not a Aux ROC time event */ + return -EINVAL; + + iwl_mvm_te_check_trigger(mvm, notif, te_data); + + if (!le32_to_cpu(notif->status)) { + IWL_DEBUG_TE(mvm, + "ERROR: Aux ROC Time Event %s notification failure\n", + (le32_to_cpu(notif->action) & + TE_V2_NOTIF_HOST_EVENT_START) ? "start" : "end"); + return -EINVAL; + } + + IWL_DEBUG_TE(mvm, + "Aux ROC time event notification - UID = 0x%x action %d\n", + le32_to_cpu(notif->unique_id), + le32_to_cpu(notif->action)); + + if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) { + /* End TE, notify mac80211 */ + ieee80211_remain_on_channel_expired(mvm->hw); + iwl_mvm_roc_finished(mvm); /* flush aux queue */ + list_del(&te_data->list); /* remove from list */ + te_data->running = false; + te_data->vif = NULL; + te_data->uid = 0; + te_data->id = TE_MAX; + } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) { + set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status); + te_data->running = true; + iwl_mvm_ref(mvm, IWL_MVM_REF_ROC_AUX); + ieee80211_ready_on_channel(mvm->hw); /* Start TE */ + } else { + IWL_DEBUG_TE(mvm, + "ERROR: Unknown Aux ROC Time Event (action = %d)\n", + le32_to_cpu(notif->action)); + return -EINVAL; + } + + return 0; +} + +/* + * The Rx handler for time event notifications + */ +void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_time_event_notif *notif = (void *)pkt->data; + struct iwl_mvm_time_event_data *te_data, *tmp; + + IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n", + le32_to_cpu(notif->unique_id), + le32_to_cpu(notif->action)); + + spin_lock_bh(&mvm->time_event_lock); + /* This time event is triggered for Aux ROC request */ + if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif)) + goto unlock; + + list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) { + if (le32_to_cpu(notif->unique_id) == te_data->uid) + iwl_mvm_te_handle_notif(mvm, te_data, notif); + } +unlock: + spin_unlock_bh(&mvm->time_event_lock); +} + +static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mvm *mvm = + container_of(notif_wait, struct iwl_mvm, notif_wait); + struct iwl_mvm_time_event_data *te_data = data; + struct iwl_time_event_notif *resp; + int resp_len = iwl_rx_packet_payload_len(pkt); + + if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION)) + return true; + + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { + IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n"); + return true; + } + + resp = (void *)pkt->data; + + /* te_data->uid is already set in the TIME_EVENT_CMD response */ + if (le32_to_cpu(resp->unique_id) != te_data->uid) + return false; + + IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n", + te_data->uid); + if (!resp->status) + IWL_ERR(mvm, + "TIME_EVENT_NOTIFICATION received but not executed\n"); + + return true; +} + +static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mvm *mvm = + 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 = iwl_rx_packet_payload_len(pkt); + + if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD)) + return true; + + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { + IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n"); + return true; + } + + resp = (void *)pkt->data; + + /* we should never get a response to another TIME_EVENT_CMD here */ + if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id)) + return false; + + te_data->uid = le32_to_cpu(resp->unique_id); + IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", + te_data->uid); + return true; +} + +static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mvm_time_event_data *te_data, + struct iwl_time_event_cmd *te_cmd) +{ + static const u16 time_event_response[] = { TIME_EVENT_CMD }; + struct iwl_notification_wait wait_time_event; + int ret; + + lockdep_assert_held(&mvm->mutex); + + IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n", + le32_to_cpu(te_cmd->duration)); + + spin_lock_bh(&mvm->time_event_lock); + if (WARN_ON(te_data->id != TE_MAX)) { + spin_unlock_bh(&mvm->time_event_lock); + return -EIO; + } + te_data->vif = vif; + te_data->duration = le32_to_cpu(te_cmd->duration); + te_data->id = le32_to_cpu(te_cmd->id); + list_add_tail(&te_data->list, &mvm->time_event_list); + spin_unlock_bh(&mvm->time_event_lock); + + /* + * Use a notification wait, which really just processes the + * command response and doesn't wait for anything, in order + * to be able to process the response and get the UID inside + * the RX path. Using CMD_WANT_SKB doesn't work because it + * stores the buffer and then wakes up this thread, by which + * time another notification (that the time event started) + * might already be processed unsuccessfully. + */ + iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, + time_event_response, + ARRAY_SIZE(time_event_response), + iwl_mvm_time_event_response, te_data); + + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, + sizeof(*te_cmd), te_cmd); + if (ret) { + IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); + iwl_remove_notification(&mvm->notif_wait, &wait_time_event); + goto out_clear_te; + } + + /* No need to wait for anything, so just pass 1 (0 isn't valid) */ + ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1); + /* should never fail */ + WARN_ON_ONCE(ret); + + if (ret) { + out_clear_te: + spin_lock_bh(&mvm->time_event_lock); + iwl_mvm_te_clear_data(mvm, te_data); + spin_unlock_bh(&mvm->time_event_lock); + } + return ret; +} + +void iwl_mvm_protect_session(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + u32 max_delay, bool wait_for_notif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; + const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION }; + struct iwl_notification_wait wait_te_notif; + struct iwl_time_event_cmd time_cmd = {}; + + lockdep_assert_held(&mvm->mutex); + + if (te_data->running && + time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { + IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", + jiffies_to_msecs(te_data->end_jiffies - jiffies)); + return; + } + + if (te_data->running) { + IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n", + te_data->uid, + jiffies_to_msecs(te_data->end_jiffies - jiffies)); + /* + * we don't have enough time + * cancel the current TE and issue a new one + * Of course it would be better to remove the old one only + * when the new one is added, but we don't care if we are off + * channel for a bit. All we need to do, is not to return + * before we actually begin to be on the channel. + */ + iwl_mvm_stop_session_protection(mvm, vif); + } + + time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); + time_cmd.id_and_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); + time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC); + + time_cmd.apply_time = cpu_to_le32(0); + + time_cmd.max_frags = TE_V2_FRAG_NONE; + time_cmd.max_delay = cpu_to_le32(max_delay); + /* TODO: why do we need to interval = bi if it is not periodic? */ + time_cmd.interval = cpu_to_le32(1); + time_cmd.duration = cpu_to_le32(duration); + time_cmd.repeat = 1; + time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | + TE_V2_NOTIF_HOST_EVENT_END | + T2_V2_START_IMMEDIATELY); + + if (!wait_for_notif) { + iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); + return; + } + + /* + * Create notification_wait for the TIME_EVENT_NOTIFICATION to use + * right after we send the time event + */ + iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif, + te_notif_response, + ARRAY_SIZE(te_notif_response), + iwl_mvm_te_notif, te_data); + + /* If TE was sent OK - wait for the notification that started */ + if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) { + IWL_ERR(mvm, "Failed to add TE to protect session\n"); + iwl_remove_notification(&mvm->notif_wait, &wait_te_notif); + } else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif, + TU_TO_JIFFIES(max_delay))) { + IWL_ERR(mvm, "Failed to protect session until TE\n"); + } +} + +static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, + struct iwl_mvm_time_event_data *te_data, + u32 *uid) +{ + u32 id; + + /* + * It is possible that by the time we got to this point the time + * event was already removed. + */ + spin_lock_bh(&mvm->time_event_lock); + + /* Save time event uid before clearing its data */ + *uid = te_data->uid; + id = te_data->id; + + /* + * The clear_data function handles time events that were already removed + */ + iwl_mvm_te_clear_data(mvm, te_data); + spin_unlock_bh(&mvm->time_event_lock); + + /* + * It is possible that by the time we try to remove it, the time event + * has already ended and removed. In such a case there is no need to + * send a removal command. + */ + if (id == TE_MAX) { + IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid); + return false; + } + + return true; +} + +/* + * Explicit request to remove a aux roc time event. The removal of a time + * event needs to be synchronized with the flow of a time event's end + * notification, which also removes the time event from the op mode + * data structures. + */ +static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + struct iwl_mvm_time_event_data *te_data) +{ + struct iwl_hs20_roc_req aux_cmd = {}; + u32 uid; + int ret; + + if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) + return; + + aux_cmd.event_unique_id = cpu_to_le32(uid); + aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); + aux_cmd.id_and_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); + IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n", + le32_to_cpu(aux_cmd.event_unique_id)); + ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, + sizeof(aux_cmd), &aux_cmd); + + if (WARN_ON(ret)) + return; +} + +/* + * Explicit request to remove a time event. The removal of a time event needs to + * be synchronized with the flow of a time event's end notification, which also + * removes the time event from the op mode data structures. + */ +void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + struct iwl_mvm_time_event_data *te_data) +{ + struct iwl_time_event_cmd time_cmd = {}; + u32 uid; + int ret; + + if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) + return; + + /* When we remove a TE, the UID is to be set in the id field */ + time_cmd.id = cpu_to_le32(uid); + time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); + time_cmd.id_and_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); + + IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id)); + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, + sizeof(time_cmd), &time_cmd); + if (WARN_ON(ret)) + return; +} + +void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; + + lockdep_assert_held(&mvm->mutex); + iwl_mvm_remove_time_event(mvm, mvmvif, te_data); +} + +int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + int duration, enum ieee80211_roc_type type) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; + struct iwl_time_event_cmd time_cmd = {}; + + lockdep_assert_held(&mvm->mutex); + if (te_data->running) { + IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n"); + return -EBUSY; + } + + /* + * Flush the done work, just in case it's still pending, so that + * the work it does can complete and we can accept new frames. + */ + flush_work(&mvm->roc_done_wk); + + time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); + time_cmd.id_and_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); + + switch (type) { + case IEEE80211_ROC_TYPE_NORMAL: + time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL); + break; + case IEEE80211_ROC_TYPE_MGMT_TX: + time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX); + break; + default: + WARN_ONCE(1, "Got an invalid ROC type\n"); + return -EINVAL; + } + + time_cmd.apply_time = cpu_to_le32(0); + time_cmd.interval = cpu_to_le32(1); + + /* + * The P2P Device TEs can have lower priority than other events + * that are being scheduled by the driver/fw, and thus it might not be + * scheduled. To improve the chances of it being scheduled, allow them + * to be fragmented, and in addition allow them to be delayed. + */ + time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS); + time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2)); + time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); + time_cmd.repeat = 1; + time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | + TE_V2_NOTIF_HOST_EVENT_END | + T2_V2_START_IMMEDIATELY); + + return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); +} + +void iwl_mvm_stop_roc(struct iwl_mvm *mvm) +{ + struct iwl_mvm_vif *mvmvif = NULL; + struct iwl_mvm_time_event_data *te_data; + bool is_p2p = false; + + lockdep_assert_held(&mvm->mutex); + + spin_lock_bh(&mvm->time_event_lock); + + /* + * Iterate over the list of time events and find the time event that is + * associated with a P2P_DEVICE interface. + * This assumes that a P2P_DEVICE interface can have only a single time + * event at any given time and this time event coresponds to a ROC + * request + */ + list_for_each_entry(te_data, &mvm->time_event_list, list) { + if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { + mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); + is_p2p = true; + goto remove_te; + } + } + + /* There can only be at most one AUX ROC time event, we just use the + * list to simplify/unify code. Remove it if it exists. + */ + te_data = list_first_entry_or_null(&mvm->aux_roc_te_list, + struct iwl_mvm_time_event_data, + list); + if (te_data) + mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); + +remove_te: + spin_unlock_bh(&mvm->time_event_lock); + + if (!mvmvif) { + IWL_WARN(mvm, "No remain on channel event\n"); + return; + } + + if (is_p2p) + iwl_mvm_remove_time_event(mvm, mvmvif, te_data); + else + iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data); + + iwl_mvm_roc_finished(mvm); +} + +int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 duration, u32 apply_time) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; + struct iwl_time_event_cmd time_cmd = {}; + + lockdep_assert_held(&mvm->mutex); + + if (te_data->running) { + IWL_DEBUG_TE(mvm, "CS period is already scheduled\n"); + return -EBUSY; + } + + time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); + time_cmd.id_and_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); + time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD); + time_cmd.apply_time = cpu_to_le32(apply_time); + time_cmd.max_frags = TE_V2_FRAG_NONE; + time_cmd.duration = cpu_to_le32(duration); + time_cmd.repeat = 1; + time_cmd.interval = cpu_to_le32(1); + time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | + TE_V2_ABSENCE); + + return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h new file mode 100644 index 000000000000..cbdf8e52a5f1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h @@ -0,0 +1,249 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __time_event_h__ +#define __time_event_h__ + +#include "fw-api.h" + +#include "mvm.h" + +/** + * DOC: Time Events - what is it? + * + * Time Events are a fw feature that allows the driver to control the presence + * of the device on the channel. Since the fw supports multiple channels + * concurrently, the fw may choose to jump to another channel at any time. + * In order to make sure that the fw is on a specific channel at a certain time + * and for a certain duration, the driver needs to issue a time event. + * + * The simplest example is for BSS association. The driver issues a time event, + * waits for it to start, and only then tells mac80211 that we can start the + * association. This way, we make sure that the association will be done + * smoothly and won't be interrupted by channel switch decided within the fw. + */ + + /** + * DOC: The flow against the fw + * + * When the driver needs to make sure we are in a certain channel, at a certain + * time and for a certain duration, it sends a Time Event. The flow against the + * fw goes like this: + * 1) Driver sends a TIME_EVENT_CMD to the fw + * 2) Driver gets the response for that command. This response contains the + * Unique ID (UID) of the event. + * 3) The fw sends notification when the event starts. + * + * Of course the API provides various options that allow to cover parameters + * of the flow. + * What is the duration of the event? + * What is the start time of the event? + * Is there an end-time for the event? + * How much can the event be delayed? + * Can the event be split? + * If yes what is the maximal number of chunks? + * etc... + */ + +/** + * DOC: Abstraction to the driver + * + * In order to simplify the use of time events to the rest of the driver, + * we abstract the use of time events. This component provides the functions + * needed by the driver. + */ + +#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500 +#define IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400 + +/** + * iwl_mvm_protect_session - start / extend the session protection. + * @mvm: the mvm component + * @vif: the virtual interface for which the session is issued + * @duration: the duration of the session in TU. + * @min_duration: will start a new session if the current session will end + * in less than min_duration. + * @max_delay: maximum delay before starting the time event (in TU) + * @wait_for_notif: true if it is required that a time event notification be + * waited for (that the time event has been scheduled before returning) + * + * This function can be used to start a session protection which means that the + * fw will stay on the channel for %duration_ms milliseconds. This function + * can block (sleep) until the session starts. This function can also be used + * to extend a currently running session. + * This function is meant to be used for BSS association for example, where we + * want to make sure that the fw stays on the channel during the association. + */ +void iwl_mvm_protect_session(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + u32 max_delay, bool wait_for_notif); + +/** + * iwl_mvm_stop_session_protection - cancel the session protection. + * @mvm: the mvm component + * @vif: the virtual interface for which the session is issued + * + * This functions cancels the session protection which is an act of good + * citizenship. If it is not needed any more it should be canceled because + * the other bindings wait for the medium during that time. + * This funtions doesn't sleep. + */ +void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); + +/* + * iwl_mvm_rx_time_event_notif - handles %TIME_EVENT_NOTIFICATION. + */ +void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); + +/** + * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionality + * @mvm: the mvm component + * @vif: the virtual interface for which the roc is requested. It is assumed + * that the vif type is NL80211_IFTYPE_P2P_DEVICE + * @duration: the requested duration in millisecond for the fw to be on the + * channel that is bound to the vif. + * @type: the remain on channel request type + * + * This function can be used to issue a remain on channel session, + * which means that the fw will stay in the channel for the request %duration + * milliseconds. The function is async, meaning that it only issues the ROC + * request but does not wait for it to start. Once the FW is ready to serve the + * ROC request, it will issue a notification to the driver that it is on the + * requested channel. Once the FW completes the ROC request it will issue + * another notification to the driver. + */ +int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + int duration, enum ieee80211_roc_type type); + +/** + * iwl_mvm_stop_roc - stop remain on channel functionality + * @mvm: the mvm component + * + * This function can be used to cancel an ongoing ROC session. + * The function is async, it will instruct the FW to stop serving the ROC + * session, but will not wait for the actual stopping of the session. + */ +void iwl_mvm_stop_roc(struct iwl_mvm *mvm); + +/** + * iwl_mvm_remove_time_event - general function to clean up of time event + * @mvm: the mvm component + * @vif: the vif to which the time event belongs + * @te_data: the time event data that corresponds to that time event + * + * This function can be used to cancel a time event regardless its type. + * It is useful for cleaning up time events running before removing an + * interface. + */ +void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + struct iwl_mvm_time_event_data *te_data); + +/** + * iwl_mvm_te_clear_data - remove time event from list + * @mvm: the mvm component + * @te_data: the time event data to remove + * + * This function is mostly internal, it is made available here only + * for firmware restart purposes. + */ +void iwl_mvm_te_clear_data(struct iwl_mvm *mvm, + struct iwl_mvm_time_event_data *te_data); + +void iwl_mvm_roc_done_wk(struct work_struct *wk); + +/** + * iwl_mvm_schedule_csa_period - request channel switch absence period + * @mvm: the mvm component + * @vif: the virtual interface for which the channel switch is issued + * @duration: the duration of the NoA in TU. + * @apply_time: NoA start time in GP2. + * + * This function is used to schedule NoA time event and is used to perform + * the channel switch flow. + */ +int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 duration, u32 apply_time); + +/** + * iwl_mvm_te_scheduled - check if the fw received the TE cmd + * @te_data: the time event data that corresponds to that time event + * + * This function returns true iff this TE is added to the fw. + */ +static inline bool +iwl_mvm_te_scheduled(struct iwl_mvm_time_event_data *te_data) +{ + if (!te_data) + return false; + + return !!te_data->uid; +} + +#endif /* __time_event_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.c b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c new file mode 100644 index 000000000000..4007f1d421dd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c @@ -0,0 +1,306 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include "mvm.h" +#include "fw-api-tof.h" + +#define IWL_MVM_TOF_RANGE_REQ_MAX_ID 256 + +void iwl_mvm_tof_init(struct iwl_mvm *mvm) +{ + struct iwl_mvm_tof_data *tof_data = &mvm->tof_data; + + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) + return; + + memset(tof_data, 0, sizeof(*tof_data)); + + tof_data->tof_cfg.sub_grp_cmd_id = cpu_to_le32(TOF_CONFIG_CMD); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (IWL_MVM_TOF_IS_RESPONDER) { + tof_data->responder_cfg.sub_grp_cmd_id = + cpu_to_le32(TOF_RESPONDER_CONFIG_CMD); + tof_data->responder_cfg.sta_id = IWL_MVM_STATION_COUNT; + } +#endif + + tof_data->range_req.sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_REQ_CMD); + tof_data->range_req.req_timeout = 1; + tof_data->range_req.initiator = 1; + tof_data->range_req.report_policy = 3; + + tof_data->range_req_ext.sub_grp_cmd_id = + cpu_to_le32(TOF_RANGE_REQ_EXT_CMD); + + mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; +} + +void iwl_mvm_tof_clean(struct iwl_mvm *mvm) +{ + struct iwl_mvm_tof_data *tof_data = &mvm->tof_data; + + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) + return; + + memset(tof_data, 0, sizeof(*tof_data)); + mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; +} + +static void iwl_tof_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + bool *enabled = _data; + + /* non bss vif exists */ + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) + *enabled = false; +} + +int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm) +{ + struct iwl_tof_config_cmd *cmd = &mvm->tof_data.tof_cfg; + bool enabled; + + lockdep_assert_held(&mvm->mutex); + + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) + return -EINVAL; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_tof_iterator, &enabled); + if (!enabled) { + IWL_DEBUG_INFO(mvm, "ToF is not supported (non bss vif)\n"); + return -EINVAL; + } + + mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; + return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, + IWL_ALWAYS_LONG_GROUP, 0), + 0, sizeof(*cmd), cmd); +} + +int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id) +{ + struct iwl_tof_range_abort_cmd cmd = { + .sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_ABORT_CMD), + .request_id = id, + }; + + lockdep_assert_held(&mvm->mutex); + + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) + return -EINVAL; + + if (id != mvm->tof_data.active_range_request) { + IWL_ERR(mvm, "Invalid range request id %d (active %d)\n", + id, mvm->tof_data.active_range_request); + return -EINVAL; + } + + /* after abort is sent there's no active request anymore */ + mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; + + return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, + IWL_ALWAYS_LONG_GROUP, 0), + 0, sizeof(cmd), &cmd); +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_tof_responder_config_cmd *cmd = &mvm->tof_data.responder_cfg; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + lockdep_assert_held(&mvm->mutex); + + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) + return -EINVAL; + + if (vif->p2p || vif->type != NL80211_IFTYPE_AP || + !mvmvif->ap_ibss_active) { + IWL_ERR(mvm, "Cannot start responder, not in AP mode\n"); + return -EIO; + } + + cmd->sta_id = mvmvif->bcast_sta.sta_id; + memcpy(cmd->bssid, vif->addr, ETH_ALEN); + return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, + IWL_ALWAYS_LONG_GROUP, 0), + 0, sizeof(*cmd), cmd); +} +#endif + +int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_host_cmd cmd = { + .id = iwl_cmd_id(TOF_CMD, IWL_ALWAYS_LONG_GROUP, 0), + .len = { sizeof(mvm->tof_data.range_req), }, + /* no copy because of the command size */ + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + + lockdep_assert_held(&mvm->mutex); + + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) + return -EINVAL; + + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) { + IWL_ERR(mvm, "Cannot send range request, not STA mode\n"); + return -EIO; + } + + /* nesting of range requests is not supported in FW */ + if (mvm->tof_data.active_range_request != + IWL_MVM_TOF_RANGE_REQ_MAX_ID) { + IWL_ERR(mvm, "Cannot send range req, already active req %d\n", + mvm->tof_data.active_range_request); + return -EIO; + } + + mvm->tof_data.active_range_request = mvm->tof_data.range_req.request_id; + + cmd.data[0] = &mvm->tof_data.range_req; + return iwl_mvm_send_cmd(mvm, &cmd); +} + +int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + lockdep_assert_held(&mvm->mutex); + + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) + return -EINVAL; + + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) { + IWL_ERR(mvm, "Cannot send ext range req, not in STA mode\n"); + return -EIO; + } + + return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, + IWL_ALWAYS_LONG_GROUP, 0), + 0, sizeof(mvm->tof_data.range_req_ext), + &mvm->tof_data.range_req_ext); +} + +static int iwl_mvm_tof_range_resp(struct iwl_mvm *mvm, void *data) +{ + struct iwl_tof_range_rsp_ntfy *resp = (void *)data; + + if (resp->request_id != mvm->tof_data.active_range_request) { + IWL_ERR(mvm, "Request id mismatch, got %d, active %d\n", + resp->request_id, mvm->tof_data.active_range_request); + return -EIO; + } + + memcpy(&mvm->tof_data.range_resp, resp, + sizeof(struct iwl_tof_range_rsp_ntfy)); + mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; + + return 0; +} + +static int iwl_mvm_tof_mcsi_notif(struct iwl_mvm *mvm, void *data) +{ + struct iwl_tof_mcsi_notif *resp = (struct iwl_tof_mcsi_notif *)data; + + IWL_DEBUG_INFO(mvm, "MCSI notification, token %d\n", resp->token); + return 0; +} + +static int iwl_mvm_tof_nb_report_notif(struct iwl_mvm *mvm, void *data) +{ + struct iwl_tof_neighbor_report *report = + (struct iwl_tof_neighbor_report *)data; + + IWL_DEBUG_INFO(mvm, "NB report, bssid %pM, token %d, status 0x%x\n", + report->bssid, report->request_token, report->status); + return 0; +} + +void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_tof_gen_resp_cmd *resp = (void *)pkt->data; + + lockdep_assert_held(&mvm->mutex); + + switch (le32_to_cpu(resp->sub_grp_cmd_id)) { + case TOF_RANGE_RESPONSE_NOTIF: + iwl_mvm_tof_range_resp(mvm, resp->data); + break; + case TOF_MCSI_DEBUG_NOTIF: + iwl_mvm_tof_mcsi_notif(mvm, resp->data); + break; + case TOF_NEIGHBOR_REPORT_RSP_NOTIF: + iwl_mvm_tof_nb_report_notif(mvm, resp->data); + break; + default: + IWL_ERR(mvm, "Unknown sub-group command 0x%x\n", + resp->sub_grp_cmd_id); + break; + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h new file mode 100644 index 000000000000..9beebc33cb8d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __tof_h__ +#define __tof_h__ + +#include "fw-api-tof.h" + +struct iwl_mvm_tof_data { + struct iwl_tof_config_cmd tof_cfg; + struct iwl_tof_range_req_cmd range_req; + struct iwl_tof_range_req_ext_cmd range_req_ext; +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct iwl_tof_responder_config_cmd responder_cfg; +#endif + struct iwl_tof_range_rsp_ntfy range_resp; + u8 last_abort_id; + u16 active_range_request; +}; + +void iwl_mvm_tof_init(struct iwl_mvm *mvm); +void iwl_mvm_tof_clean(struct iwl_mvm *mvm); +int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm); +int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id); +int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +#ifdef CONFIG_IWLWIFI_DEBUGFS +int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +#endif +#endif /* __tof_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c new file mode 100644 index 000000000000..cadfc0460597 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -0,0 +1,460 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include "mvm.h" + +#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ + +static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) +{ + struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; + u32 duration = tt->params.ct_kill_duration; + + if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) + return; + + IWL_ERR(mvm, "Enter CT Kill\n"); + iwl_mvm_set_hw_ctkill_state(mvm, true); + + tt->throttle = false; + tt->dynamic_smps = false; + + /* Don't schedule an exit work if we're in test mode, since + * the temperature will not change unless we manually set it + * again (or disable testing). + */ + if (!mvm->temperature_test) + schedule_delayed_work(&tt->ct_kill_exit, + round_jiffies_relative(duration * HZ)); +} + +static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm) +{ + if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) + return; + + IWL_ERR(mvm, "Exit CT Kill\n"); + iwl_mvm_set_hw_ctkill_state(mvm, false); +} + +void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp) +{ + /* ignore the notification if we are in test mode */ + if (mvm->temperature_test) + return; + + if (mvm->temperature == temp) + return; + + mvm->temperature = temp; + iwl_mvm_tt_handler(mvm); +} + +static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm, + struct iwl_rx_packet *pkt) +{ + struct iwl_dts_measurement_notif *notif; + int len = iwl_rx_packet_payload_len(pkt); + int temp; + + if (WARN_ON_ONCE(len != sizeof(*notif))) { + IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n"); + return -EINVAL; + } + + notif = (void *)pkt->data; + + temp = le32_to_cpu(notif->temp); + + /* shouldn't be negative, but since it's s32, make sure it isn't */ + if (WARN_ON_ONCE(temp < 0)) + temp = 0; + + IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp); + + return temp; +} + +static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mvm *mvm = + container_of(notif_wait, struct iwl_mvm, notif_wait); + int *temp = data; + int ret; + + ret = iwl_mvm_temp_notif_parse(mvm, pkt); + if (ret < 0) + return true; + + *temp = ret; + + return true; +} + +void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + int temp; + + /* the notification is handled synchronously in ctkill, so skip here */ + if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) + return; + + temp = iwl_mvm_temp_notif_parse(mvm, pkt); + if (temp < 0) + return; + + iwl_mvm_tt_temp_changed(mvm, temp); +} + +static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm) +{ + struct iwl_dts_measurement_cmd cmd = { + .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP), + }; + struct iwl_ext_dts_measurement_cmd extcmd = { + .control_mode = cpu_to_le32(DTS_AUTOMATIC), + }; + u32 cmdid; + + if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR)) + cmdid = iwl_cmd_id(CMD_DTS_MEASUREMENT_TRIGGER_WIDE, + PHY_OPS_GROUP, 0); + else + cmdid = CMD_DTS_MEASUREMENT_TRIGGER; + + if (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE)) + return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(cmd), &cmd); + + return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(extcmd), &extcmd); +} + +int iwl_mvm_get_temp(struct iwl_mvm *mvm) +{ + struct iwl_notification_wait wait_temp_notif; + static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP, + DTS_MEASUREMENT_NOTIF_WIDE) }; + int ret, temp; + + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR)) + temp_notif[0] = DTS_MEASUREMENT_NOTIFICATION; + + lockdep_assert_held(&mvm->mutex); + + iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif, + temp_notif, ARRAY_SIZE(temp_notif), + iwl_mvm_temp_notif_wait, &temp); + + ret = iwl_mvm_get_temp_cmd(mvm); + if (ret) { + IWL_ERR(mvm, "Failed to get the temperature (err=%d)\n", ret); + iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif); + return ret; + } + + ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif, + IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT); + if (ret) { + IWL_ERR(mvm, "Getting the temperature timed out\n"); + return ret; + } + + return temp; +} + +static void check_exit_ctkill(struct work_struct *work) +{ + struct iwl_mvm_tt_mgmt *tt; + struct iwl_mvm *mvm; + u32 duration; + s32 temp; + + tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work); + mvm = container_of(tt, struct iwl_mvm, thermal_throttle); + + duration = tt->params.ct_kill_duration; + + mutex_lock(&mvm->mutex); + + if (__iwl_mvm_mac_start(mvm)) + goto reschedule; + + /* make sure the device is available for direct read/writes */ + if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL)) { + __iwl_mvm_mac_stop(mvm); + goto reschedule; + } + + temp = iwl_mvm_get_temp(mvm); + + iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL); + + __iwl_mvm_mac_stop(mvm); + + if (temp < 0) + goto reschedule; + + IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp); + + if (temp <= tt->params.ct_kill_exit) { + mutex_unlock(&mvm->mutex); + iwl_mvm_exit_ctkill(mvm); + return; + } + +reschedule: + mutex_unlock(&mvm->mutex); + schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, + round_jiffies(duration * HZ)); +} + +static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = _data; + enum ieee80211_smps_mode smps_mode; + + lockdep_assert_held(&mvm->mutex); + + if (mvm->thermal_throttle.dynamic_smps) + smps_mode = IEEE80211_SMPS_DYNAMIC; + else + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode); +} + +static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + int i, err; + + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(sta)) + continue; + mvmsta = iwl_mvm_sta_from_mac80211(sta); + if (enable == mvmsta->tt_tx_protection) + continue; + err = iwl_mvm_tx_protection(mvm, mvmsta, enable); + if (err) { + IWL_ERR(mvm, "Failed to %s Tx protection\n", + enable ? "enable" : "disable"); + } else { + IWL_DEBUG_TEMP(mvm, "%s Tx protection\n", + enable ? "Enable" : "Disable"); + mvmsta->tt_tx_protection = enable; + } + } +} + +void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_THERMAL_MNG_BACKOFF, + .len = { sizeof(u32), }, + .data = { &backoff, }, + }; + + backoff = max(backoff, mvm->thermal_throttle.min_backoff); + + if (iwl_mvm_send_cmd(mvm, &cmd) == 0) { + IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n", + backoff); + mvm->thermal_throttle.tx_backoff = backoff; + } else { + IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n"); + } +} + +void iwl_mvm_tt_handler(struct iwl_mvm *mvm) +{ + struct iwl_tt_params *params = &mvm->thermal_throttle.params; + struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; + s32 temperature = mvm->temperature; + bool throttle_enable = false; + int i; + u32 tx_backoff; + + IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature); + + if (params->support_ct_kill && temperature >= params->ct_kill_entry) { + iwl_mvm_enter_ctkill(mvm); + return; + } + + if (params->support_ct_kill && + temperature <= params->ct_kill_exit) { + iwl_mvm_exit_ctkill(mvm); + return; + } + + if (params->support_dynamic_smps) { + if (!tt->dynamic_smps && + temperature >= params->dynamic_smps_entry) { + IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n"); + tt->dynamic_smps = true; + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_tt_smps_iterator, mvm); + throttle_enable = true; + } else if (tt->dynamic_smps && + temperature <= params->dynamic_smps_exit) { + IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n"); + tt->dynamic_smps = false; + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_tt_smps_iterator, mvm); + } + } + + if (params->support_tx_protection) { + if (temperature >= params->tx_protection_entry) { + iwl_mvm_tt_tx_protection(mvm, true); + throttle_enable = true; + } else if (temperature <= params->tx_protection_exit) { + iwl_mvm_tt_tx_protection(mvm, false); + } + } + + if (params->support_tx_backoff) { + tx_backoff = tt->min_backoff; + for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) { + if (temperature < params->tx_backoff[i].temperature) + break; + tx_backoff = max(tt->min_backoff, + params->tx_backoff[i].backoff); + } + if (tx_backoff != tt->min_backoff) + throttle_enable = true; + if (tt->tx_backoff != tx_backoff) + iwl_mvm_tt_tx_backoff(mvm, tx_backoff); + } + + if (!tt->throttle && throttle_enable) { + IWL_WARN(mvm, + "Due to high temperature thermal throttling initiated\n"); + tt->throttle = true; + } else if (tt->throttle && !tt->dynamic_smps && + tt->tx_backoff == tt->min_backoff && + temperature <= params->tx_protection_exit) { + IWL_WARN(mvm, + "Temperature is back to normal thermal throttling stopped\n"); + tt->throttle = false; + } +} + +static const struct iwl_tt_params iwl_mvm_default_tt_params = { + .ct_kill_entry = 118, + .ct_kill_exit = 96, + .ct_kill_duration = 5, + .dynamic_smps_entry = 114, + .dynamic_smps_exit = 110, + .tx_protection_entry = 114, + .tx_protection_exit = 108, + .tx_backoff = { + {.temperature = 112, .backoff = 200}, + {.temperature = 113, .backoff = 600}, + {.temperature = 114, .backoff = 1200}, + {.temperature = 115, .backoff = 2000}, + {.temperature = 116, .backoff = 4000}, + {.temperature = 117, .backoff = 10000}, + }, + .support_ct_kill = true, + .support_dynamic_smps = true, + .support_tx_protection = true, + .support_tx_backoff = true, +}; + +void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff) +{ + struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; + + IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n"); + + if (mvm->cfg->thermal_params) + tt->params = *mvm->cfg->thermal_params; + else + tt->params = iwl_mvm_default_tt_params; + + tt->throttle = false; + tt->dynamic_smps = false; + tt->min_backoff = min_backoff; + INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); +} + +void iwl_mvm_tt_exit(struct iwl_mvm *mvm) +{ + cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit); + IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n"); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c new file mode 100644 index 000000000000..c652a66be803 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -0,0 +1,1115 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include + +#include "iwl-trans.h" +#include "iwl-eeprom-parse.h" +#include "mvm.h" +#include "sta.h" + +static void +iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr, + u16 tid, u16 ssn) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_ba *ba_trig; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); + ba_trig = (void *)trig->data; + + if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) + return; + + if (!(le16_to_cpu(ba_trig->tx_bar) & BIT(tid))) + return; + + iwl_mvm_fw_dbg_collect_trig(mvm, trig, + "BAR sent to %pM, tid %d, ssn %d", + addr, tid, ssn); +} + +/* + * Sets most of the Tx cmd's fields + */ +void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, + struct iwl_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, u8 sta_id) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + __le16 fc = hdr->frame_control; + u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags); + u32 len = skb->len + FCS_LEN; + u8 ac; + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + tx_flags |= TX_CMD_FLG_ACK; + else + tx_flags &= ~TX_CMD_FLG_ACK; + + if (ieee80211_is_probe_resp(fc)) + tx_flags |= TX_CMD_FLG_TSF; + + if (ieee80211_has_morefrags(fc)) + tx_flags |= TX_CMD_FLG_MORE_FRAG; + + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tx_cmd->tid_tspec = qc[0] & 0xf; + tx_flags &= ~TX_CMD_FLG_SEQ_CTL; + } else if (ieee80211_is_back_req(fc)) { + struct ieee80211_bar *bar = (void *)skb->data; + u16 control = le16_to_cpu(bar->control); + u16 ssn = le16_to_cpu(bar->start_seq_num); + + tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; + tx_cmd->tid_tspec = (control & + IEEE80211_BAR_CTRL_TID_INFO_MASK) >> + IEEE80211_BAR_CTRL_TID_INFO_SHIFT; + WARN_ON_ONCE(tx_cmd->tid_tspec >= IWL_MAX_TID_COUNT); + iwl_mvm_bar_check_trigger(mvm, bar->ra, tx_cmd->tid_tspec, + ssn); + } else { + tx_cmd->tid_tspec = IWL_TID_NON_QOS; + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + tx_flags |= TX_CMD_FLG_SEQ_CTL; + else + tx_flags &= ~TX_CMD_FLG_SEQ_CTL; + } + + /* Default to 0 (BE) when tid_spec is set to IWL_TID_NON_QOS */ + if (tx_cmd->tid_tspec < IWL_MAX_TID_COUNT) + ac = tid_to_mac80211_ac[tx_cmd->tid_tspec]; + else + ac = tid_to_mac80211_ac[0]; + + tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, hdr, info, ac) << + TX_CMD_FLG_BT_PRIO_POS; + + if (ieee80211_is_mgmt(fc)) { + if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) + tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_ASSOC); + else if (ieee80211_is_action(fc)) + tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE); + else + tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT); + + /* The spec allows Action frames in A-MPDU, we don't support + * it + */ + WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU); + } else if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) { + tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT); + } else { + tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE); + } + + if (ieee80211_is_data(fc) && len > mvm->rts_threshold && + !is_multicast_ether_addr(ieee80211_get_DA(hdr))) + tx_flags |= TX_CMD_FLG_PROT_REQUIRE; + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT) && + ieee80211_action_contains_tpc(skb)) + tx_flags |= TX_CMD_FLG_WRITE_TX_POWER; + + tx_cmd->tx_flags = cpu_to_le32(tx_flags); + /* Total # bytes to be transmitted */ + tx_cmd->len = cpu_to_le16((u16)skb->len); + tx_cmd->next_frame_len = 0; + tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); + tx_cmd->sta_id = sta_id; +} + +/* + * Sets the fields in the Tx cmd that are rate related + */ +void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, __le16 fc) +{ + u32 rate_flags; + int rate_idx; + u8 rate_plcp; + + /* Set retry limit on RTS packets */ + tx_cmd->rts_retry_limit = IWL_RTS_DFAULT_RETRY_LIMIT; + + /* Set retry limit on DATA packets and Probe Responses*/ + if (ieee80211_is_probe_resp(fc)) { + tx_cmd->data_retry_limit = IWL_MGMT_DFAULT_RETRY_LIMIT; + tx_cmd->rts_retry_limit = + min(tx_cmd->data_retry_limit, tx_cmd->rts_retry_limit); + } else if (ieee80211_is_back_req(fc)) { + tx_cmd->data_retry_limit = IWL_BAR_DFAULT_RETRY_LIMIT; + } else { + tx_cmd->data_retry_limit = IWL_DEFAULT_TX_RETRY; + } + + /* + * for data packets, rate info comes from the table inside the fw. This + * table is controlled by LINK_QUALITY commands + */ + + if (ieee80211_is_data(fc) && sta) { + tx_cmd->initial_rate_index = 0; + tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE); + return; + } else if (ieee80211_is_back_req(fc)) { + tx_cmd->tx_flags |= + cpu_to_le32(TX_CMD_FLG_ACK | TX_CMD_FLG_BAR); + } + + /* HT rate doesn't make sense for a non data frame */ + WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS, + "Got an HT rate (flags:0x%x/mcs:%d) for a non data frame (fc:0x%x)\n", + info->control.rates[0].flags, + info->control.rates[0].idx, + le16_to_cpu(fc)); + + rate_idx = info->control.rates[0].idx; + /* if the rate isn't a well known legacy rate, take the lowest one */ + if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT_LEGACY) + rate_idx = rate_lowest_index( + &mvm->nvm_data->bands[info->band], sta); + + /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ + if (info->band == IEEE80211_BAND_5GHZ) + rate_idx += IWL_FIRST_OFDM_RATE; + + /* For 2.4 GHZ band, check that there is no need to remap */ + BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); + + /* Get PLCP rate for tx_cmd->rate_n_flags */ + rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx); + + mvm->mgmt_last_antenna_idx = + iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), + mvm->mgmt_last_antenna_idx); + + if (info->band == IEEE80211_BAND_2GHZ && + !iwl_mvm_bt_coex_is_shared_ant_avail(mvm)) + rate_flags = mvm->cfg->non_shared_ant << RATE_MCS_ANT_POS; + else + rate_flags = + BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS; + + /* Set CCK flag as needed */ + if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) + rate_flags |= RATE_MCS_CCK_MSK; + + /* Set the rate in the TX cmd */ + tx_cmd->rate_n_flags = cpu_to_le32((u32)rate_plcp | rate_flags); +} + +/* + * Sets the fields in the Tx cmd that are crypto related + */ +static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, + struct ieee80211_tx_info *info, + struct iwl_tx_cmd *tx_cmd, + struct sk_buff *skb_frag, + int hdrlen) +{ + struct ieee80211_key_conf *keyconf = info->control.hw_key; + u8 *crypto_hdr = skb_frag->data + hdrlen; + u64 pn; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + iwl_mvm_set_tx_cmd_ccmp(info, tx_cmd); + pn = atomic64_inc_return(&keyconf->tx_pn); + crypto_hdr[0] = pn; + crypto_hdr[2] = 0; + crypto_hdr[3] = 0x20 | (keyconf->keyidx << 6); + crypto_hdr[1] = pn >> 8; + crypto_hdr[4] = pn >> 16; + crypto_hdr[5] = pn >> 24; + crypto_hdr[6] = pn >> 32; + crypto_hdr[7] = pn >> 40; + break; + + case WLAN_CIPHER_SUITE_TKIP: + tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; + ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); + break; + + case WLAN_CIPHER_SUITE_WEP104: + tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; + /* fall through */ + case WLAN_CIPHER_SUITE_WEP40: + tx_cmd->sec_ctl |= TX_CMD_SEC_WEP | + ((keyconf->keyidx << TX_CMD_SEC_WEP_KEY_IDX_POS) & + TX_CMD_SEC_WEP_KEY_IDX_MSK); + + memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); + break; + default: + tx_cmd->sec_ctl |= TX_CMD_SEC_EXT; + } +} + +/* + * Allocates and sets the Tx cmd the driver data pointers in the skb + */ +static struct iwl_device_cmd * +iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, + int hdrlen, struct ieee80211_sta *sta, u8 sta_id) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_device_cmd *dev_cmd; + struct iwl_tx_cmd *tx_cmd; + + dev_cmd = iwl_trans_alloc_tx_cmd(mvm->trans); + + if (unlikely(!dev_cmd)) + return NULL; + + memset(dev_cmd, 0, sizeof(*dev_cmd)); + dev_cmd->hdr.cmd = TX_CMD; + tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; + + if (info->control.hw_key) + iwl_mvm_set_tx_cmd_crypto(mvm, info, tx_cmd, skb, hdrlen); + + iwl_mvm_set_tx_cmd(mvm, skb, tx_cmd, info, sta_id); + + iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control); + + memset(&info->status, 0, sizeof(info->status)); + + info->driver_data[0] = NULL; + info->driver_data[1] = dev_cmd; + + return dev_cmd; +} + +int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_device_cmd *dev_cmd; + struct iwl_tx_cmd *tx_cmd; + u8 sta_id; + int hdrlen = ieee80211_hdrlen(hdr->frame_control); + + if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU)) + return -1; + + if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && + (!info->control.vif || + info->hw_queue != info->control.vif->cab_queue))) + return -1; + + /* + * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used + * in 2 different types of vifs, P2P & STATION. P2P uses the offchannel + * queue. STATION (HS2.0) uses the auxiliary context of the FW, + * and hence needs to be sent on the aux queue + */ + if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE && + info->control.vif->type == NL80211_IFTYPE_STATION) + IEEE80211_SKB_CB(skb)->hw_queue = mvm->aux_queue; + + /* + * If the interface on which the frame is sent is the P2P_DEVICE + * or an AP/GO interface use the broadcast station associated + * with it; otherwise if the interface is a managed interface + * use the AP station associated with it for multicast traffic + * (this is not possible for unicast packets as a TLDS discovery + * response are sent without a station entry); otherwise use the + * AUX station. + */ + sta_id = mvm->aux_sta.sta_id; + if (info->control.vif) { + struct iwl_mvm_vif *mvmvif = + iwl_mvm_vif_from_mac80211(info->control.vif); + + if (info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE || + info->control.vif->type == NL80211_IFTYPE_AP) + sta_id = mvmvif->bcast_sta.sta_id; + else if (info->control.vif->type == NL80211_IFTYPE_STATION && + is_multicast_ether_addr(hdr->addr1)) { + u8 ap_sta_id = ACCESS_ONCE(mvmvif->ap_sta_id); + + if (ap_sta_id != IWL_MVM_STATION_COUNT) + sta_id = ap_sta_id; + } + } + + IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue); + + dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, NULL, sta_id); + if (!dev_cmd) + return -1; + + /* From now on, we cannot access info->control */ + tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; + + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, hdrlen); + + if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) { + iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); + return -1; + } + + return 0; +} + +/* + * Sets the fields in the Tx cmd that are crypto related + */ +int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, + struct ieee80211_sta *sta) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_mvm_sta *mvmsta; + struct iwl_device_cmd *dev_cmd; + struct iwl_tx_cmd *tx_cmd; + __le16 fc; + u16 seq_number = 0; + u8 tid = IWL_MAX_TID_COUNT; + u8 txq_id = info->hw_queue; + bool is_data_qos = false, is_ampdu = false; + int hdrlen; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + fc = hdr->frame_control; + hdrlen = ieee80211_hdrlen(fc); + + if (WARN_ON_ONCE(!mvmsta)) + return -1; + + if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) + return -1; + + dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, sta, mvmsta->sta_id); + if (!dev_cmd) + goto drop; + + tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; + /* From now on, we cannot access info->control */ + + /* + * we handle that entirely ourselves -- for uAPSD the firmware + * will always send a notification, and for PS-Poll responses + * we'll notify mac80211 when getting frame status + */ + info->flags &= ~IEEE80211_TX_STATUS_EOSP; + + spin_lock(&mvmsta->lock); + + if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { + u8 *qc = NULL; + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) + goto drop_unlock_sta; + + seq_number = mvmsta->tid_data[tid].seq_number; + seq_number &= IEEE80211_SCTL_SEQ; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(seq_number); + is_data_qos = true; + is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU; + } + + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, hdrlen); + + WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); + + if (sta->tdls) { + /* default to TID 0 for non-QoS packets */ + u8 tdls_tid = tid == IWL_MAX_TID_COUNT ? 0 : tid; + + txq_id = mvmsta->hw_queue[tid_to_mac80211_ac[tdls_tid]]; + } + + if (is_ampdu) { + if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON)) + goto drop_unlock_sta; + txq_id = mvmsta->tid_data[tid].txq_id; + } + + IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id, + 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 + 0x10; + + spin_unlock(&mvmsta->lock); + + if (txq_id < mvm->first_agg_queue) + atomic_inc(&mvm->pending_frames[mvmsta->sta_id]); + + return 0; + +drop_unlock_sta: + iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); + spin_unlock(&mvmsta->lock); +drop: + return -1; +} + +static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, u8 tid) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + struct ieee80211_vif *vif = mvmsta->vif; + + lockdep_assert_held(&mvmsta->lock); + + if ((tid_data->state == IWL_AGG_ON || + tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && + iwl_mvm_tid_queued(tid_data) == 0) { + /* + * Now that this aggregation queue is empty tell mac80211 so it + * knows we no longer have frames buffered for the station on + * this TID (for the TIM bitmap calculation.) + */ + ieee80211_sta_set_buffered(sta, tid, false); + } + + if (tid_data->ssn != tid_data->next_reclaimed) + return; + + switch (tid_data->state) { + case IWL_EMPTYING_HW_QUEUE_ADDBA: + IWL_DEBUG_TX_QUEUES(mvm, + "Can continue addBA flow ssn = next_recl = %d\n", + tid_data->next_reclaimed); + tid_data->state = IWL_AGG_STARTING; + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + + case IWL_EMPTYING_HW_QUEUE_DELBA: + IWL_DEBUG_TX_QUEUES(mvm, + "Can continue DELBA flow ssn = next_recl = %d\n", + tid_data->next_reclaimed); + iwl_mvm_disable_txq(mvm, tid_data->txq_id, + vif->hw_queue[tid_to_mac80211_ac[tid]], tid, + CMD_ASYNC); + tid_data->state = IWL_AGG_OFF; + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + + default: + break; + } +} + +#ifdef CONFIG_IWLWIFI_DEBUG +const char *iwl_mvm_get_tx_fail_reason(u32 status) +{ +#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x +#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x + + switch (status & TX_STATUS_MSK) { + case TX_STATUS_SUCCESS: + return "SUCCESS"; + TX_STATUS_POSTPONE(DELAY); + TX_STATUS_POSTPONE(FEW_BYTES); + TX_STATUS_POSTPONE(BT_PRIO); + TX_STATUS_POSTPONE(QUIET_PERIOD); + TX_STATUS_POSTPONE(CALC_TTAK); + TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); + TX_STATUS_FAIL(SHORT_LIMIT); + TX_STATUS_FAIL(LONG_LIMIT); + TX_STATUS_FAIL(UNDERRUN); + TX_STATUS_FAIL(DRAIN_FLOW); + TX_STATUS_FAIL(RFKILL_FLUSH); + TX_STATUS_FAIL(LIFE_EXPIRE); + TX_STATUS_FAIL(DEST_PS); + TX_STATUS_FAIL(HOST_ABORTED); + TX_STATUS_FAIL(BT_RETRY); + TX_STATUS_FAIL(STA_INVALID); + TX_STATUS_FAIL(FRAG_DROPPED); + TX_STATUS_FAIL(TID_DISABLE); + TX_STATUS_FAIL(FIFO_FLUSHED); + TX_STATUS_FAIL(SMALL_CF_POLL); + TX_STATUS_FAIL(FW_DROP); + TX_STATUS_FAIL(STA_COLOR_MISMATCH); + } + + return "UNKNOWN"; + +#undef TX_STATUS_FAIL +#undef TX_STATUS_POSTPONE +} +#endif /* CONFIG_IWLWIFI_DEBUG */ + +void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, + enum ieee80211_band band, + struct ieee80211_tx_rate *r) +{ + if (rate_n_flags & RATE_HT_MCS_GF_MSK) + r->flags |= IEEE80211_TX_RC_GREEN_FIELD; + switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + break; + case RATE_MCS_CHAN_WIDTH_40: + r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + break; + case RATE_MCS_CHAN_WIDTH_80: + r->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; + break; + case RATE_MCS_CHAN_WIDTH_160: + r->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH; + break; + } + if (rate_n_flags & RATE_MCS_SGI_MSK) + r->flags |= IEEE80211_TX_RC_SHORT_GI; + if (rate_n_flags & RATE_MCS_HT_MSK) { + r->flags |= IEEE80211_TX_RC_MCS; + r->idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; + } else if (rate_n_flags & RATE_MCS_VHT_MSK) { + ieee80211_rate_set_vht( + r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK, + ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> + RATE_VHT_MCS_NSS_POS) + 1); + r->flags |= IEEE80211_TX_RC_VHT_MCS; + } else { + r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, + band); + } +} + +/** + * translate ucode response to mac80211 tx status control values + */ +static void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags, + struct ieee80211_tx_info *info) +{ + struct ieee80211_tx_rate *r = &info->status.rates[0]; + + info->status.antenna = + ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); + iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r); +} + +static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, + struct iwl_rx_packet *pkt) +{ + struct ieee80211_sta *sta; + u16 sequence = le16_to_cpu(pkt->hdr.sequence); + int txq_id = SEQ_TO_QUEUE(sequence); + struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; + int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); + int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); + u32 status = le16_to_cpu(tx_resp->status.status); + u16 ssn = iwl_mvm_get_scd_ssn(tx_resp); + struct iwl_mvm_sta *mvmsta; + struct sk_buff_head skbs; + u8 skb_freed = 0; + u16 next_reclaimed, seq_ctl; + + __skb_queue_head_init(&skbs); + + seq_ctl = le16_to_cpu(tx_resp->seq_ctl); + + /* we can free until ssn % q.n_bd not inclusive */ + iwl_trans_reclaim(mvm->trans, txq_id, ssn, &skbs); + + while (!skb_queue_empty(&skbs)) { + struct sk_buff *skb = __skb_dequeue(&skbs); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + skb_freed++; + + iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); + + memset(&info->status, 0, sizeof(info->status)); + + info->flags &= ~IEEE80211_TX_CTL_AMPDU; + + /* inform mac80211 about what happened with the frame */ + switch (status & TX_STATUS_MSK) { + case TX_STATUS_SUCCESS: + case TX_STATUS_DIRECT_DONE: + info->flags |= IEEE80211_TX_STAT_ACK; + break; + case TX_STATUS_FAIL_DEST_PS: + info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + break; + default: + break; + } + + info->status.rates[0].count = tx_resp->failure_frame + 1; + iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate), + info); + info->status.status_driver_data[1] = + (void *)(uintptr_t)le32_to_cpu(tx_resp->initial_rate); + + /* Single frame failure in an AMPDU queue => send BAR */ + if (txq_id >= mvm->first_agg_queue && + !(info->flags & IEEE80211_TX_STAT_ACK) && + !(info->flags & IEEE80211_TX_STAT_TX_FILTERED)) + info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; + + /* W/A FW bug: seq_ctl is wrong when the status isn't success */ + if (status != TX_STATUS_SUCCESS) { + struct ieee80211_hdr *hdr = (void *)skb->data; + seq_ctl = le16_to_cpu(hdr->seq_ctrl); + } + + /* + * TODO: this is not accurate if we are freeing more than one + * packet. + */ + info->status.tx_time = + le16_to_cpu(tx_resp->wireless_media_time); + BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1); + info->status.status_driver_data[0] = + (void *)(uintptr_t)tx_resp->reduced_tpc; + + ieee80211_tx_status(mvm->hw, skb); + } + + if (txq_id >= mvm->first_agg_queue) { + /* If this is an aggregation queue, we use the ssn since: + * ssn = wifi seq_num % 256. + * The seq_ctl is the sequence control of the packet to which + * this Tx response relates. But if there is a hole in the + * bitmap of the BA we received, this Tx response may allow to + * reclaim the hole and all the subsequent packets that were + * already acked. In that case, seq_ctl != ssn, and the next + * packet to be reclaimed will be ssn and not seq_ctl. In that + * case, several packets will be reclaimed even if + * frame_count = 1. + * + * The ssn is the index (% 256) of the latest packet that has + * treated (acked / dropped) + 1. + */ + next_reclaimed = ssn; + } else { + /* The next packet to be reclaimed is the one after this one */ + next_reclaimed = IEEE80211_SEQ_TO_SN(seq_ctl + 0x10); + } + + IWL_DEBUG_TX_REPLY(mvm, + "TXQ %d status %s (0x%08x)\n", + txq_id, iwl_mvm_get_tx_fail_reason(status), status); + + IWL_DEBUG_TX_REPLY(mvm, + "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d next_reclaimed=0x%x seq_ctl=0x%x\n", + le32_to_cpu(tx_resp->initial_rate), + tx_resp->failure_frame, SEQ_TO_INDEX(sequence), + ssn, next_reclaimed, seq_ctl); + + rcu_read_lock(); + + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + /* + * sta can't be NULL otherwise it'd mean that the sta has been freed in + * the firmware while we still have packets for it in the Tx queues. + */ + if (WARN_ON_ONCE(!sta)) + goto out; + + if (!IS_ERR(sta)) { + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + if (tid != IWL_TID_NON_QOS) { + struct iwl_mvm_tid_data *tid_data = + &mvmsta->tid_data[tid]; + + spin_lock_bh(&mvmsta->lock); + tid_data->next_reclaimed = next_reclaimed; + IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n", + next_reclaimed); + iwl_mvm_check_ratid_empty(mvm, sta, tid); + spin_unlock_bh(&mvmsta->lock); + } + + if (mvmsta->next_status_eosp) { + mvmsta->next_status_eosp = false; + ieee80211_sta_eosp(sta); + } + } else { + mvmsta = NULL; + } + + /* + * If the txq is not an AMPDU queue, there is no chance we freed + * several skbs. Check that out... + */ + if (txq_id >= mvm->first_agg_queue) + goto out; + + /* We can't free more than one frame at once on a shared queue */ + WARN_ON(skb_freed > 1); + + /* If we have still frames for this STA nothing to do here */ + if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) + goto out; + + if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) { + + /* + * If there are no pending frames for this STA and + * the tx to this station is not disabled, notify + * mac80211 that this station can now wake up in its + * STA table. + * If mvmsta is not NULL, sta is valid. + */ + + spin_lock_bh(&mvmsta->lock); + + if (!mvmsta->disable_tx) + ieee80211_sta_block_awake(mvm->hw, sta, false); + + spin_unlock_bh(&mvmsta->lock); + } + + if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) { + /* + * We are draining and this was the last packet - pre_rcu_remove + * has been called already. We might be after the + * synchronize_net already. + * Don't rely on iwl_mvm_rm_sta to see the empty Tx queues. + */ + set_bit(sta_id, mvm->sta_drained); + schedule_work(&mvm->sta_drained_wk); + } + +out: + rcu_read_unlock(); +} + +#ifdef CONFIG_IWLWIFI_DEBUG +#define AGG_TX_STATE_(x) case AGG_TX_STATE_ ## x: return #x +static const char *iwl_get_agg_tx_status(u16 status) +{ + switch (status & AGG_TX_STATE_STATUS_MSK) { + AGG_TX_STATE_(TRANSMITTED); + AGG_TX_STATE_(UNDERRUN); + AGG_TX_STATE_(BT_PRIO); + AGG_TX_STATE_(FEW_BYTES); + AGG_TX_STATE_(ABORT); + AGG_TX_STATE_(LAST_SENT_TTL); + AGG_TX_STATE_(LAST_SENT_TRY_CNT); + AGG_TX_STATE_(LAST_SENT_BT_KILL); + AGG_TX_STATE_(SCD_QUERY); + AGG_TX_STATE_(TEST_BAD_CRC32); + AGG_TX_STATE_(RESPONSE); + AGG_TX_STATE_(DUMP_TX); + AGG_TX_STATE_(DELAY_TX); + } + + return "UNKNOWN"; +} + +static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm, + struct iwl_rx_packet *pkt) +{ + struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; + struct agg_tx_status *frame_status = &tx_resp->status; + int i; + + for (i = 0; i < tx_resp->frame_count; i++) { + u16 fstatus = le16_to_cpu(frame_status[i].status); + + IWL_DEBUG_TX_REPLY(mvm, + "status %s (0x%04x), try-count (%d) seq (0x%x)\n", + iwl_get_agg_tx_status(fstatus), + fstatus & AGG_TX_STATE_STATUS_MSK, + (fstatus & AGG_TX_STATE_TRY_CNT_MSK) >> + AGG_TX_STATE_TRY_CNT_POS, + le16_to_cpu(frame_status[i].sequence)); + } +} +#else +static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm, + struct iwl_rx_packet *pkt) +{} +#endif /* CONFIG_IWLWIFI_DEBUG */ + +static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, + struct iwl_rx_packet *pkt) +{ + struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; + int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); + int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); + u16 sequence = le16_to_cpu(pkt->hdr.sequence); + struct ieee80211_sta *sta; + + if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < mvm->first_agg_queue)) + return; + + if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS)) + return; + + iwl_mvm_rx_tx_cmd_agg_dbg(mvm, pkt); + + rcu_read_lock(); + + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + + if (!WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + mvmsta->tid_data[tid].rate_n_flags = + le32_to_cpu(tx_resp->initial_rate); + mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc; + mvmsta->tid_data[tid].tx_time = + le16_to_cpu(tx_resp->wireless_media_time); + } + + rcu_read_unlock(); +} + +void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; + + if (tx_resp->frame_count == 1) + iwl_mvm_rx_tx_cmd_single(mvm, pkt); + else + iwl_mvm_rx_tx_cmd_agg(mvm, pkt); +} + +static void iwl_mvm_tx_info_from_ba_notif(struct ieee80211_tx_info *info, + struct iwl_mvm_ba_notif *ba_notif, + struct iwl_mvm_tid_data *tid_data) +{ + info->flags |= IEEE80211_TX_STAT_AMPDU; + info->status.ampdu_ack_len = ba_notif->txed_2_done; + info->status.ampdu_len = ba_notif->txed; + iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags, + info); + /* TODO: not accounted if the whole A-MPDU failed */ + info->status.tx_time = tid_data->tx_time; + info->status.status_driver_data[0] = + (void *)(uintptr_t)tid_data->reduced_tpc; + info->status.status_driver_data[1] = + (void *)(uintptr_t)tid_data->rate_n_flags; +} + +void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mvm_ba_notif *ba_notif = (void *)pkt->data; + struct sk_buff_head reclaimed_skbs; + struct iwl_mvm_tid_data *tid_data; + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + struct sk_buff *skb; + int sta_id, tid, freed; + /* "flow" corresponds to Tx queue */ + u16 scd_flow = le16_to_cpu(ba_notif->scd_flow); + /* "ssn" is start of block-ack Tx window, corresponds to index + * (in Tx queue's circular buffer) of first TFD/frame in window */ + u16 ba_resp_scd_ssn = le16_to_cpu(ba_notif->scd_ssn); + + sta_id = ba_notif->sta_id; + tid = ba_notif->tid; + + if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT || + tid >= IWL_MAX_TID_COUNT, + "sta_id %d tid %d", sta_id, tid)) + return; + + rcu_read_lock(); + + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + + /* Reclaiming frames for a station that has been deleted ? */ + if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { + rcu_read_unlock(); + return; + } + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + tid_data = &mvmsta->tid_data[tid]; + + if (tid_data->txq_id != scd_flow) { + IWL_ERR(mvm, + "invalid BA notification: Q %d, tid %d, flow %d\n", + tid_data->txq_id, tid, scd_flow); + rcu_read_unlock(); + return; + } + + spin_lock_bh(&mvmsta->lock); + + __skb_queue_head_init(&reclaimed_skbs); + + /* + * Release all TFDs before the SSN, i.e. all TFDs in front of + * block-ack window (we assume that they've been successfully + * transmitted ... if not, it's too late anyway). + */ + iwl_trans_reclaim(mvm->trans, scd_flow, ba_resp_scd_ssn, + &reclaimed_skbs); + + IWL_DEBUG_TX_REPLY(mvm, + "BA_NOTIFICATION Received from %pM, sta_id = %d\n", + (u8 *)&ba_notif->sta_addr_lo32, + ba_notif->sta_id); + IWL_DEBUG_TX_REPLY(mvm, + "TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n", + ba_notif->tid, le16_to_cpu(ba_notif->seq_ctl), + (unsigned long long)le64_to_cpu(ba_notif->bitmap), + scd_flow, ba_resp_scd_ssn, ba_notif->txed, + ba_notif->txed_2_done); + + tid_data->next_reclaimed = ba_resp_scd_ssn; + + iwl_mvm_check_ratid_empty(mvm, sta, tid); + + freed = 0; + + skb_queue_walk(&reclaimed_skbs, skb) { + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (ieee80211_is_data_qos(hdr->frame_control)) + freed++; + else + WARN_ON_ONCE(1); + + iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); + + memset(&info->status, 0, sizeof(info->status)); + /* Packet was transmitted successfully, failures come as single + * frames because before failing a frame the firmware transmits + * it without aggregation at least once. + */ + info->flags |= IEEE80211_TX_STAT_ACK; + + /* this is the first skb we deliver in this batch */ + /* put the rate scaling data there */ + if (freed == 1) + iwl_mvm_tx_info_from_ba_notif(info, ba_notif, tid_data); + } + + spin_unlock_bh(&mvmsta->lock); + + /* We got a BA notif with 0 acked or scd_ssn didn't progress which is + * possible (i.e. first MPDU in the aggregation wasn't acked) + * Still it's important to update RS about sent vs. acked. + */ + if (skb_queue_empty(&reclaimed_skbs)) { + struct ieee80211_tx_info ba_info = {}; + struct ieee80211_chanctx_conf *chanctx_conf = NULL; + + if (mvmsta->vif) + chanctx_conf = + rcu_dereference(mvmsta->vif->chanctx_conf); + + if (WARN_ON_ONCE(!chanctx_conf)) + goto out; + + ba_info.band = chanctx_conf->def.chan->band; + iwl_mvm_tx_info_from_ba_notif(&ba_info, ba_notif, tid_data); + + IWL_DEBUG_TX_REPLY(mvm, "No reclaim. Update rs directly\n"); + iwl_mvm_rs_tx_status(mvm, sta, tid, &ba_info); + } + +out: + rcu_read_unlock(); + + while (!skb_queue_empty(&reclaimed_skbs)) { + skb = __skb_dequeue(&reclaimed_skbs); + ieee80211_tx_status(mvm->hw, skb); + } +} + +/* + * Note that there are transports that buffer frames before they reach + * the firmware. This means that after flush_tx_path is called, the + * queue might not be empty. The race-free way to handle this is to: + * 1) set the station as draining + * 2) flush the Tx path + * 3) wait for the transport queues to be empty + */ +int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags) +{ + int ret; + struct iwl_tx_path_flush_cmd flush_cmd = { + .queues_ctl = cpu_to_le32(tfd_msk), + .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH), + }; + + ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags, + sizeof(flush_cmd), &flush_cmd); + if (ret) + IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c new file mode 100644 index 000000000000..ad0f16909e2e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -0,0 +1,1083 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright (C) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include + +#include "iwl-debug.h" +#include "iwl-io.h" +#include "iwl-prph.h" + +#include "mvm.h" +#include "fw-api-rs.h" + +/* + * Will return 0 even if the cmd failed when RFKILL is asserted unless + * CMD_WANT_SKB is set in cmd->flags. + */ +int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd) +{ + int ret; + +#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) + if (WARN_ON(mvm->d3_test_active)) + return -EIO; +#endif + + /* + * Synchronous commands from this op-mode must hold + * the mutex, this ensures we don't try to send two + * (or more) synchronous commands at a time. + */ + if (!(cmd->flags & CMD_ASYNC)) + lockdep_assert_held(&mvm->mutex); + + ret = iwl_trans_send_cmd(mvm->trans, cmd); + + /* + * If the caller wants the SKB, then don't hide any problems, the + * caller might access the response buffer which will be NULL if + * the command failed. + */ + if (cmd->flags & CMD_WANT_SKB) + return ret; + + /* Silently ignore failures if RFKILL is asserted */ + if (!ret || ret == -ERFKILL) + return 0; + return ret; +} + +int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, + u32 flags, u16 len, const void *data) +{ + struct iwl_host_cmd cmd = { + .id = id, + .len = { len, }, + .data = { data, }, + .flags = flags, + }; + + return iwl_mvm_send_cmd(mvm, &cmd); +} + +/* + * We assume that the caller set the status to the success value + */ +int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, + u32 *status) +{ + struct iwl_rx_packet *pkt; + struct iwl_cmd_response *resp; + int ret, resp_len; + + lockdep_assert_held(&mvm->mutex); + +#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) + if (WARN_ON(mvm->d3_test_active)) + return -EIO; +#endif + + /* + * Only synchronous commands can wait for status, + * we use WANT_SKB so the caller can't. + */ + if (WARN_ONCE(cmd->flags & (CMD_ASYNC | CMD_WANT_SKB), + "cmd flags %x", cmd->flags)) + return -EINVAL; + + cmd->flags |= CMD_WANT_SKB; + + ret = iwl_trans_send_cmd(mvm->trans, cmd); + if (ret == -ERFKILL) { + /* + * The command failed because of RFKILL, don't update + * the status, leave it as success and return 0. + */ + return 0; + } else if (ret) { + return ret; + } + + pkt = cmd->resp_pkt; + /* Can happen if RFKILL is asserted */ + if (!pkt) { + ret = 0; + goto out_free_resp; + } + + resp_len = iwl_rx_packet_payload_len(pkt); + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { + ret = -EIO; + goto out_free_resp; + } + + resp = (void *)pkt->data; + *status = le32_to_cpu(resp->status); + out_free_resp: + iwl_free_resp(cmd); + return ret; +} + +/* + * We assume that the caller set the status to the sucess value + */ +int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len, + const void *data, u32 *status) +{ + struct iwl_host_cmd cmd = { + .id = id, + .len = { len, }, + .data = { data, }, + }; + + return iwl_mvm_send_cmd_status(mvm, &cmd, status); +} + +#define IWL_DECLARE_RATE_INFO(r) \ + [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP + +/* + * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP + */ +static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = { + IWL_DECLARE_RATE_INFO(1), + IWL_DECLARE_RATE_INFO(2), + IWL_DECLARE_RATE_INFO(5), + IWL_DECLARE_RATE_INFO(11), + IWL_DECLARE_RATE_INFO(6), + IWL_DECLARE_RATE_INFO(9), + IWL_DECLARE_RATE_INFO(12), + IWL_DECLARE_RATE_INFO(18), + IWL_DECLARE_RATE_INFO(24), + IWL_DECLARE_RATE_INFO(36), + IWL_DECLARE_RATE_INFO(48), + IWL_DECLARE_RATE_INFO(54), +}; + +int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, + enum ieee80211_band band) +{ + int rate = rate_n_flags & RATE_LEGACY_RATE_MSK; + int idx; + int band_offset = 0; + + /* Legacy rate format, search for match in table */ + if (band == IEEE80211_BAND_5GHZ) + band_offset = IWL_FIRST_OFDM_RATE; + for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) + if (fw_rate_idx_to_plcp[idx] == rate) + return idx - band_offset; + + return -1; +} + +u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx) +{ + /* Get PLCP rate for tx_cmd->rate_n_flags */ + return fw_rate_idx_to_plcp[rate_idx]; +} + +void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_error_resp *err_resp = (void *)pkt->data; + + IWL_ERR(mvm, "FW Error notification: type 0x%08X cmd_id 0x%02X\n", + le32_to_cpu(err_resp->error_type), err_resp->cmd_id); + IWL_ERR(mvm, "FW Error notification: seq 0x%04X service 0x%08X\n", + le16_to_cpu(err_resp->bad_cmd_seq_num), + le32_to_cpu(err_resp->error_service)); + IWL_ERR(mvm, "FW Error notification: timestamp 0x%16llX\n", + le64_to_cpu(err_resp->timestamp)); +} + +/* + * Returns the first antenna as ANT_[ABC], as defined in iwl-config.h. + * The parameter should also be a combination of ANT_[ABC]. + */ +u8 first_antenna(u8 mask) +{ + BUILD_BUG_ON(ANT_A != BIT(0)); /* using ffs is wrong if not */ + if (WARN_ON_ONCE(!mask)) /* ffs will return 0 if mask is zeroed */ + return BIT(0); + return BIT(ffs(mask) - 1); +} + +/* + * Toggles between TX antennas to send the probe request on. + * Receives the bitmask of valid TX antennas and the *index* used + * for the last TX, and returns the next valid *index* to use. + * In order to set it in the tx_cmd, must do BIT(idx). + */ +u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx) +{ + u8 ind = last_idx; + int i; + + for (i = 0; i < RATE_MCS_ANT_NUM; i++) { + ind = (ind + 1) % RATE_MCS_ANT_NUM; + if (valid & BIT(ind)) + return ind; + } + + WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid); + return last_idx; +} + +static const struct { + const char *name; + u8 num; +} advanced_lookup[] = { + { "NMI_INTERRUPT_WDG", 0x34 }, + { "SYSASSERT", 0x35 }, + { "UCODE_VERSION_MISMATCH", 0x37 }, + { "BAD_COMMAND", 0x38 }, + { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, + { "FATAL_ERROR", 0x3D }, + { "NMI_TRM_HW_ERR", 0x46 }, + { "NMI_INTERRUPT_TRM", 0x4C }, + { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, + { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, + { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, + { "NMI_INTERRUPT_HOST", 0x66 }, + { "NMI_INTERRUPT_ACTION_PT", 0x7C }, + { "NMI_INTERRUPT_UNKNOWN", 0x84 }, + { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, + { "ADVANCED_SYSASSERT", 0 }, +}; + +static const char *desc_lookup(u32 num) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(advanced_lookup) - 1; i++) + if (advanced_lookup[i].num == num) + return advanced_lookup[i].name; + + /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */ + return advanced_lookup[i].name; +} + +/* + * Note: This structure is read from the device with IO accesses, + * and the reading already does the endian conversion. As it is + * read with u32-sized accesses, any members with a different size + * need to be ordered correctly though! + */ +struct iwl_error_event_table_v1 { + u32 valid; /* (nonzero) valid, (0) log is empty */ + u32 error_id; /* type of error */ + u32 pc; /* program counter */ + u32 blink1; /* branch link */ + u32 blink2; /* branch link */ + u32 ilink1; /* interrupt link */ + u32 ilink2; /* interrupt link */ + u32 data1; /* error-specific data */ + u32 data2; /* error-specific data */ + u32 data3; /* error-specific data */ + u32 bcon_time; /* beacon timer */ + u32 tsf_low; /* network timestamp function timer */ + u32 tsf_hi; /* network timestamp function timer */ + u32 gp1; /* GP1 timer register */ + u32 gp2; /* GP2 timer register */ + u32 gp3; /* GP3 timer register */ + u32 ucode_ver; /* uCode version */ + u32 hw_ver; /* HW Silicon version */ + u32 brd_ver; /* HW board version */ + u32 log_pc; /* log program counter */ + u32 frame_ptr; /* frame pointer */ + u32 stack_ptr; /* stack pointer */ + u32 hcmd; /* last host command header */ + u32 isr0; /* isr status register LMPM_NIC_ISR0: + * rxtx_flag */ + u32 isr1; /* isr status register LMPM_NIC_ISR1: + * host_flag */ + u32 isr2; /* isr status register LMPM_NIC_ISR2: + * enc_flag */ + u32 isr3; /* isr status register LMPM_NIC_ISR3: + * time_flag */ + u32 isr4; /* isr status register LMPM_NIC_ISR4: + * wico interrupt */ + u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ + u32 wait_event; /* wait event() caller address */ + u32 l2p_control; /* L2pControlField */ + u32 l2p_duration; /* L2pDurationField */ + u32 l2p_mhvalid; /* L2pMhValidBits */ + u32 l2p_addr_match; /* L2pAddrMatchStat */ + u32 lmpm_pmg_sel; /* indicate which clocks are turned on + * (LMPM_PMG_SEL) */ + u32 u_timestamp; /* indicate when the date and time of the + * compilation */ + u32 flow_handler; /* FH read/write pointers, RX credit */ +} __packed /* LOG_ERROR_TABLE_API_S_VER_1 */; + +struct iwl_error_event_table { + u32 valid; /* (nonzero) valid, (0) log is empty */ + u32 error_id; /* type of error */ + u32 pc; /* program counter */ + u32 blink1; /* branch link */ + u32 blink2; /* branch link */ + u32 ilink1; /* interrupt link */ + u32 ilink2; /* interrupt link */ + u32 data1; /* error-specific data */ + u32 data2; /* error-specific data */ + u32 data3; /* error-specific data */ + u32 bcon_time; /* beacon timer */ + u32 tsf_low; /* network timestamp function timer */ + u32 tsf_hi; /* network timestamp function timer */ + u32 gp1; /* GP1 timer register */ + u32 gp2; /* GP2 timer register */ + u32 gp3; /* GP3 timer register */ + u32 major; /* uCode version major */ + u32 minor; /* uCode version minor */ + u32 hw_ver; /* HW Silicon version */ + u32 brd_ver; /* HW board version */ + u32 log_pc; /* log program counter */ + u32 frame_ptr; /* frame pointer */ + u32 stack_ptr; /* stack pointer */ + u32 hcmd; /* last host command header */ + u32 isr0; /* isr status register LMPM_NIC_ISR0: + * rxtx_flag */ + u32 isr1; /* isr status register LMPM_NIC_ISR1: + * host_flag */ + u32 isr2; /* isr status register LMPM_NIC_ISR2: + * enc_flag */ + u32 isr3; /* isr status register LMPM_NIC_ISR3: + * time_flag */ + u32 isr4; /* isr status register LMPM_NIC_ISR4: + * wico interrupt */ + u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ + u32 wait_event; /* wait event() caller address */ + u32 l2p_control; /* L2pControlField */ + u32 l2p_duration; /* L2pDurationField */ + u32 l2p_mhvalid; /* L2pMhValidBits */ + u32 l2p_addr_match; /* L2pAddrMatchStat */ + u32 lmpm_pmg_sel; /* indicate which clocks are turned on + * (LMPM_PMG_SEL) */ + u32 u_timestamp; /* indicate when the date and time of the + * compilation */ + u32 flow_handler; /* FH read/write pointers, RX credit */ +} __packed /* LOG_ERROR_TABLE_API_S_VER_2 */; + +/* + * UMAC error struct - relevant starting from family 8000 chip. + * Note: This structure is read from the device with IO accesses, + * and the reading already does the endian conversion. As it is + * read with u32-sized accesses, any members with a different size + * need to be ordered correctly though! + */ +struct iwl_umac_error_event_table { + u32 valid; /* (nonzero) valid, (0) log is empty */ + u32 error_id; /* type of error */ + u32 blink1; /* branch link */ + u32 blink2; /* branch link */ + u32 ilink1; /* interrupt link */ + u32 ilink2; /* interrupt link */ + u32 data1; /* error-specific data */ + u32 data2; /* error-specific data */ + u32 data3; /* error-specific data */ + u32 umac_major; + u32 umac_minor; + u32 frame_pointer; /* core register 27*/ + u32 stack_pointer; /* core register 28 */ + u32 cmd_header; /* latest host cmd sent to UMAC */ + u32 nic_isr_pref; /* ISR status register */ +} __packed; + +#define ERROR_START_OFFSET (1 * sizeof(u32)) +#define ERROR_ELEM_SIZE (7 * sizeof(u32)) + +static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm) +{ + struct iwl_trans *trans = mvm->trans; + struct iwl_umac_error_event_table table; + u32 base; + + base = mvm->umac_error_event_table; + + if (base < 0x800000) { + IWL_ERR(mvm, + "Not valid error log pointer 0x%08X for %s uCode\n", + base, + (mvm->cur_ucode == IWL_UCODE_INIT) + ? "Init" : "RT"); + return; + } + + iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); + + if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { + IWL_ERR(trans, "Start IWL Error Log Dump:\n"); + IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", + mvm->status, table.valid); + } + + IWL_ERR(mvm, "0x%08X | %s\n", table.error_id, + desc_lookup(table.error_id)); + IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1); + IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2); + IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1); + IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2); + IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1); + IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2); + IWL_ERR(mvm, "0x%08X | umac data3\n", table.data3); + IWL_ERR(mvm, "0x%08X | umac major\n", table.umac_major); + IWL_ERR(mvm, "0x%08X | umac minor\n", table.umac_minor); + IWL_ERR(mvm, "0x%08X | frame pointer\n", table.frame_pointer); + IWL_ERR(mvm, "0x%08X | stack pointer\n", table.stack_pointer); + IWL_ERR(mvm, "0x%08X | last host cmd\n", table.cmd_header); + IWL_ERR(mvm, "0x%08X | isr status reg\n", table.nic_isr_pref); +} + +static void iwl_mvm_dump_nic_error_log_old(struct iwl_mvm *mvm) +{ + struct iwl_trans *trans = mvm->trans; + struct iwl_error_event_table_v1 table; + u32 base; + + base = mvm->error_event_table; + if (mvm->cur_ucode == IWL_UCODE_INIT) { + if (!base) + base = mvm->fw->init_errlog_ptr; + } else { + if (!base) + base = mvm->fw->inst_errlog_ptr; + } + + if (base < 0x800000) { + IWL_ERR(mvm, + "Not valid error log pointer 0x%08X for %s uCode\n", + base, + (mvm->cur_ucode == IWL_UCODE_INIT) + ? "Init" : "RT"); + return; + } + + iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); + + if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { + IWL_ERR(trans, "Start IWL Error Log Dump:\n"); + IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", + mvm->status, table.valid); + } + + /* Do not change this output - scripts rely on it */ + + IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version); + + trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, + table.data1, table.data2, table.data3, + table.blink1, table.blink2, table.ilink1, + table.ilink2, table.bcon_time, table.gp1, + table.gp2, table.gp3, table.ucode_ver, 0, + table.hw_ver, table.brd_ver); + IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, + desc_lookup(table.error_id)); + IWL_ERR(mvm, "0x%08X | uPc\n", table.pc); + IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1); + IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2); + IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1); + IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2); + IWL_ERR(mvm, "0x%08X | data1\n", table.data1); + IWL_ERR(mvm, "0x%08X | data2\n", table.data2); + IWL_ERR(mvm, "0x%08X | data3\n", table.data3); + IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time); + IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low); + IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi); + IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1); + IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2); + IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3); + IWL_ERR(mvm, "0x%08X | uCode version\n", table.ucode_ver); + IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver); + IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver); + IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd); + IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0); + IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1); + IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2); + IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3); + IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4); + IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref); + IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event); + IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control); + IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration); + IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); + IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); + IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); + IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp); + IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler); + + if (mvm->support_umac_log) + iwl_mvm_dump_umac_error_log(mvm); +} + +void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) +{ + struct iwl_trans *trans = mvm->trans; + struct iwl_error_event_table table; + u32 base; + + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION)) { + iwl_mvm_dump_nic_error_log_old(mvm); + return; + } + + base = mvm->error_event_table; + if (mvm->cur_ucode == IWL_UCODE_INIT) { + if (!base) + base = mvm->fw->init_errlog_ptr; + } else { + if (!base) + base = mvm->fw->inst_errlog_ptr; + } + + if (base < 0x800000) { + IWL_ERR(mvm, + "Not valid error log pointer 0x%08X for %s uCode\n", + base, + (mvm->cur_ucode == IWL_UCODE_INIT) + ? "Init" : "RT"); + return; + } + + iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); + + if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { + IWL_ERR(trans, "Start IWL Error Log Dump:\n"); + IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", + mvm->status, table.valid); + } + + /* Do not change this output - scripts rely on it */ + + IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version); + + trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, + table.data1, table.data2, table.data3, + table.blink1, table.blink2, table.ilink1, + table.ilink2, table.bcon_time, table.gp1, + table.gp2, table.gp3, table.major, + table.minor, table.hw_ver, table.brd_ver); + IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, + desc_lookup(table.error_id)); + IWL_ERR(mvm, "0x%08X | uPc\n", table.pc); + IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1); + IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2); + IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1); + IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2); + IWL_ERR(mvm, "0x%08X | data1\n", table.data1); + IWL_ERR(mvm, "0x%08X | data2\n", table.data2); + IWL_ERR(mvm, "0x%08X | data3\n", table.data3); + IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time); + IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low); + IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi); + IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1); + IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2); + IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3); + IWL_ERR(mvm, "0x%08X | uCode version major\n", table.major); + IWL_ERR(mvm, "0x%08X | uCode version minor\n", table.minor); + IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver); + IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver); + IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd); + IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0); + IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1); + IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2); + IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3); + IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4); + IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref); + IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event); + IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control); + IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration); + IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); + IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); + IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); + IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp); + IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler); + + if (mvm->support_umac_log) + iwl_mvm_dump_umac_error_log(mvm); +} + +int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq) +{ + int i; + + lockdep_assert_held(&mvm->queue_info_lock); + + for (i = minq; i <= maxq; i++) + if (mvm->queue_info[i].hw_queue_refcount == 0 && + !mvm->queue_info[i].setup_reserved) + return i; + + return -ENOSPC; +} + +void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, + u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int wdg_timeout) +{ + bool enable_queue = true; + + spin_lock_bh(&mvm->queue_info_lock); + + /* Make sure this TID isn't already enabled */ + if (mvm->queue_info[queue].tid_bitmap & BIT(cfg->tid)) { + spin_unlock_bh(&mvm->queue_info_lock); + IWL_ERR(mvm, "Trying to enable TXQ with existing TID %d\n", + cfg->tid); + return; + } + + /* Update mappings and refcounts */ + mvm->queue_info[queue].hw_queue_to_mac80211 |= BIT(mac80211_queue); + mvm->queue_info[queue].hw_queue_refcount++; + if (mvm->queue_info[queue].hw_queue_refcount > 1) + enable_queue = false; + mvm->queue_info[queue].tid_bitmap |= BIT(cfg->tid); + + IWL_DEBUG_TX_QUEUES(mvm, + "Enabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n", + queue, mvm->queue_info[queue].hw_queue_refcount, + mvm->queue_info[queue].hw_queue_to_mac80211); + + spin_unlock_bh(&mvm->queue_info_lock); + + /* Send the enabling command if we need to */ + if (enable_queue) { + struct iwl_scd_txq_cfg_cmd cmd = { + .scd_queue = queue, + .enable = 1, + .window = cfg->frame_limit, + .sta_id = cfg->sta_id, + .ssn = cpu_to_le16(ssn), + .tx_fifo = cfg->fifo, + .aggregate = cfg->aggregate, + .tid = cfg->tid, + }; + + iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, + wdg_timeout); + WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), + &cmd), + "Failed to configure queue %d on FIFO %d\n", queue, + cfg->fifo); + } +} + +void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, + u8 tid, u8 flags) +{ + struct iwl_scd_txq_cfg_cmd cmd = { + .scd_queue = queue, + .enable = 0, + }; + bool remove_mac_queue = true; + int ret; + + spin_lock_bh(&mvm->queue_info_lock); + + if (WARN_ON(mvm->queue_info[queue].hw_queue_refcount == 0)) { + spin_unlock_bh(&mvm->queue_info_lock); + return; + } + + mvm->queue_info[queue].tid_bitmap &= ~BIT(tid); + + /* + * If there is another TID with the same AC - don't remove the MAC queue + * from the mapping + */ + if (tid < IWL_MAX_TID_COUNT) { + unsigned long tid_bitmap = + mvm->queue_info[queue].tid_bitmap; + int ac = tid_to_mac80211_ac[tid]; + int i; + + for_each_set_bit(i, &tid_bitmap, IWL_MAX_TID_COUNT) { + if (tid_to_mac80211_ac[i] == ac) + remove_mac_queue = false; + } + } + + if (remove_mac_queue) + mvm->queue_info[queue].hw_queue_to_mac80211 &= + ~BIT(mac80211_queue); + mvm->queue_info[queue].hw_queue_refcount--; + + cmd.enable = mvm->queue_info[queue].hw_queue_refcount ? 1 : 0; + + IWL_DEBUG_TX_QUEUES(mvm, + "Disabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n", + queue, + mvm->queue_info[queue].hw_queue_refcount, + mvm->queue_info[queue].hw_queue_to_mac80211); + + /* If the queue is still enabled - nothing left to do in this func */ + if (cmd.enable) { + spin_unlock_bh(&mvm->queue_info_lock); + return; + } + + /* Make sure queue info is correct even though we overwrite it */ + WARN(mvm->queue_info[queue].hw_queue_refcount || + mvm->queue_info[queue].tid_bitmap || + mvm->queue_info[queue].hw_queue_to_mac80211, + "TXQ #%d info out-of-sync - refcount=%d, mac map=0x%x, tid=0x%x\n", + queue, mvm->queue_info[queue].hw_queue_refcount, + mvm->queue_info[queue].hw_queue_to_mac80211, + mvm->queue_info[queue].tid_bitmap); + + /* If we are here - the queue is freed and we can zero out these vals */ + mvm->queue_info[queue].hw_queue_refcount = 0; + mvm->queue_info[queue].tid_bitmap = 0; + mvm->queue_info[queue].hw_queue_to_mac80211 = 0; + + spin_unlock_bh(&mvm->queue_info_lock); + + iwl_trans_txq_disable(mvm->trans, queue, false); + ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, + sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n", + queue, ret); +} + +/** + * iwl_mvm_send_lq_cmd() - Send link quality command + * @init: This command is sent as part of station initialization right + * after station has been added. + * + * The link quality command is sent as the last step of station creation. + * This is the special case in which init is set and we call a callback in + * this case to clear the state indicating that station creation is in + * progress. + */ +int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init) +{ + struct iwl_host_cmd cmd = { + .id = LQ_CMD, + .len = { sizeof(struct iwl_lq_cmd), }, + .flags = init ? 0 : CMD_ASYNC, + .data = { lq, }, + }; + + if (WARN_ON(lq->sta_id == IWL_MVM_STATION_COUNT)) + return -EINVAL; + + return iwl_mvm_send_cmd(mvm, &cmd); +} + +/** + * iwl_mvm_update_smps - Get a request to change the SMPS mode + * @req_type: The part of the driver who call for a change. + * @smps_requests: The request to change the SMPS mode. + * + * Get a requst to change the SMPS mode, + * and change it according to all other requests in the driver. + */ +void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum iwl_mvm_smps_type_request req_type, + enum ieee80211_smps_mode smps_request) +{ + struct iwl_mvm_vif *mvmvif; + enum ieee80211_smps_mode smps_mode; + int i; + + lockdep_assert_held(&mvm->mutex); + + /* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */ + if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1) + return; + + if (vif->type == NL80211_IFTYPE_AP) + smps_mode = IEEE80211_SMPS_OFF; + else + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + mvmvif = iwl_mvm_vif_from_mac80211(vif); + mvmvif->smps_requests[req_type] = smps_request; + for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { + if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) { + smps_mode = IEEE80211_SMPS_STATIC; + break; + } + if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) + smps_mode = IEEE80211_SMPS_DYNAMIC; + } + + ieee80211_request_smps(vif, smps_mode); +} + +int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear) +{ + struct iwl_statistics_cmd scmd = { + .flags = clear ? cpu_to_le32(IWL_STATISTICS_FLG_CLEAR) : 0, + }; + struct iwl_host_cmd cmd = { + .id = STATISTICS_CMD, + .len[0] = sizeof(scmd), + .data[0] = &scmd, + .flags = CMD_WANT_SKB, + }; + int ret; + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret) + return ret; + + iwl_mvm_handle_rx_statistics(mvm, cmd.resp_pkt); + iwl_free_resp(&cmd); + + if (clear) + iwl_mvm_accu_radio_stats(mvm); + + return 0; +} + +void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm) +{ + mvm->accu_radio_stats.rx_time += mvm->radio_stats.rx_time; + mvm->accu_radio_stats.tx_time += mvm->radio_stats.tx_time; + mvm->accu_radio_stats.on_time_rf += mvm->radio_stats.on_time_rf; + mvm->accu_radio_stats.on_time_scan += mvm->radio_stats.on_time_scan; +} + +static void iwl_mvm_diversity_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + bool *result = _data; + int i; + + for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { + if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC || + mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) + *result = false; + } +} + +bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm) +{ + bool result = true; + + lockdep_assert_held(&mvm->mutex); + + if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1) + return false; + + if (mvm->cfg->rx_with_siso_diversity) + return false; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_diversity_iter, &result); + + return result; +} + +int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool value) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int res; + + lockdep_assert_held(&mvm->mutex); + + if (mvmvif->low_latency == value) + return 0; + + mvmvif->low_latency = value; + + res = iwl_mvm_update_quotas(mvm, false, NULL); + if (res) + return res; + + iwl_mvm_bt_coex_vif_change(mvm); + + return iwl_mvm_power_update_mac(mvm); +} + +static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ + bool *result = _data; + + if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(vif))) + *result = true; +} + +bool iwl_mvm_low_latency(struct iwl_mvm *mvm) +{ + bool result = false; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_ll_iter, &result); + + return result; +} + +struct iwl_bss_iter_data { + struct ieee80211_vif *vif; + bool error; +}; + +static void iwl_mvm_bss_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_bss_iter_data *data = _data; + + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return; + + if (data->vif) { + data->error = true; + return; + } + + data->vif = vif; +} + +struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm) +{ + struct iwl_bss_iter_data bss_iter_data = {}; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bss_iface_iterator, &bss_iter_data); + + if (bss_iter_data.error) { + IWL_ERR(mvm, "More than one managed interface active!\n"); + return ERR_PTR(-EINVAL); + } + + return bss_iter_data.vif; +} + +unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool tdls, bool cmd_q) +{ + struct iwl_fw_dbg_trigger_tlv *trigger; + struct iwl_fw_dbg_trigger_txq_timer *txq_timer; + unsigned int default_timeout = + cmd_q ? IWL_DEF_WD_TIMEOUT : mvm->cfg->base_params->wd_timeout; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS)) + return iwlmvm_mod_params.tfd_q_hang_detect ? + default_timeout : IWL_WATCHDOG_DISABLED; + + trigger = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS); + txq_timer = (void *)trigger->data; + + if (tdls) + return le32_to_cpu(txq_timer->tdls); + + if (cmd_q) + return le32_to_cpu(txq_timer->command_queue); + + if (WARN_ON(!vif)) + return default_timeout; + + switch (ieee80211_vif_type_p2p(vif)) { + case NL80211_IFTYPE_ADHOC: + return le32_to_cpu(txq_timer->ibss); + case NL80211_IFTYPE_STATION: + return le32_to_cpu(txq_timer->bss); + case NL80211_IFTYPE_AP: + return le32_to_cpu(txq_timer->softap); + case NL80211_IFTYPE_P2P_CLIENT: + return le32_to_cpu(txq_timer->p2p_client); + case NL80211_IFTYPE_P2P_GO: + return le32_to_cpu(txq_timer->p2p_go); + case NL80211_IFTYPE_P2P_DEVICE: + return le32_to_cpu(txq_timer->p2p_device); + default: + WARN_ON(1); + return mvm->cfg->base_params->wd_timeout; + } +} + +void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + const char *errmsg) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_mlme *trig_mlme; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME)) + goto out; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME); + trig_mlme = (void *)trig->data; + if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + goto out; + + if (trig_mlme->stop_connection_loss && + --trig_mlme->stop_connection_loss) + goto out; + + iwl_mvm_fw_dbg_collect_trig(mvm, trig, "%s", errmsg); + +out: + ieee80211_connection_loss(vif); +} diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c new file mode 100644 index 000000000000..644b58bc5226 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -0,0 +1,685 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +#include "iwl-trans.h" +#include "iwl-drv.h" +#include "internal.h" + +#define IWL_PCI_DEVICE(dev, subdev, cfg) \ + .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = (subdev), \ + .driver_data = (kernel_ulong_t)&(cfg) + +/* Hardware specific file defines the PCI IDs table for that hardware module */ +static const struct pci_device_id iwl_hw_card_ids[] = { +#if IS_ENABLED(CONFIG_IWLDVM) + {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */ + +/* 5300 Series WiFi */ + {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */ + +/* 5350 Series WiFi/WiMax */ + {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */ + +/* 5150 Series Wifi/WiMax */ + {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1326, iwl5150_abg_cfg)}, /* Half Mini Card */ + + {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */ + +/* 6x00 Series */ + {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x422B, 0x1108, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x422B, 0x1128, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)}, + {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x4238, 0x1118, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)}, + {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)}, + +/* 6x05 Series */ + {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1308, iwl6005_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1328, iwl6005_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0x1318, iwl6005_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0xC228, iwl6005_2agn_sff_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */ + {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */ + +/* 6x30 Series */ + {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)}, + {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1030_bg_cfg)}, + {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1030_bgn_cfg)}, + {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1030_bg_cfg)}, + {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1030_bgn_cfg)}, + {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1030_bg_cfg)}, + {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_2bg_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_2abg_cfg)}, + +/* 6x50 WiFi/WiMax Series */ + {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)}, + +/* 6150 WiFi/WiMax Series */ + {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_bg_cfg)}, + {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_bg_cfg)}, + {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_bg_cfg)}, + +/* 1000 Series WiFi */ + {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)}, + +/* 100 Series WiFi */ + {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl100_bgn_cfg)}, + {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl100_bg_cfg)}, + {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl100_bgn_cfg)}, + {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl100_bg_cfg)}, + {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl100_bgn_cfg)}, + {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl100_bg_cfg)}, + +/* 130 Series WiFi */ + {IWL_PCI_DEVICE(0x0896, 0x5005, iwl130_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0896, 0x5007, iwl130_bg_cfg)}, + {IWL_PCI_DEVICE(0x0897, 0x5015, iwl130_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0897, 0x5017, iwl130_bg_cfg)}, + {IWL_PCI_DEVICE(0x0896, 0x5025, iwl130_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0896, 0x5027, iwl130_bg_cfg)}, + +/* 2x00 Series */ + {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_2bgn_d_cfg)}, + +/* 2x30 Series */ + {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)}, + +/* 6x35 Series */ + {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)}, + {IWL_PCI_DEVICE(0x088E, 0x406A, iwl6035_2agn_sff_cfg)}, + {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)}, + {IWL_PCI_DEVICE(0x088F, 0x426A, iwl6035_2agn_sff_cfg)}, + {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)}, + {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6035_2agn_sff_cfg)}, + {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)}, + {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6035_2agn_cfg)}, + +/* 105 Series */ + {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_bgn_d_cfg)}, + +/* 135 Series */ + {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)}, +#endif /* CONFIG_IWLDVM */ + +#if IS_ENABLED(CONFIG_IWLMVM) +/* 7260 Series */ + {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4C60, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4C70, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x406A, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4272, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x426A, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4472, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x446A, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)}, + {IWL_PCI_DEVICE(0x08B1, 0x4570, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4560, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5072, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5170, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5770, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC072, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC06A, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC770, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC760, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xCC70, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xCC60, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC272, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC26A, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC472, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC570, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC560, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC370, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC360, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC02A, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)}, + +/* 3160 Series */ + {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0072, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0172, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x0272, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0472, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x0370, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8072, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8172, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x8370, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x8272, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x1070, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x1170, iwl3160_2ac_cfg)}, + +/* 3165 Series */ + {IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3166, 0x4212, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3166, 0x4310, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3166, 0x4210, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x8110, iwl3165_2ac_cfg)}, + +/* 7265 Series */ + {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_n_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7265_n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5102, iwl7265_n_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7265_n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9012, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x900A, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5420, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5090, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5190, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5F10, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5212, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x520A, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9000, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9400, iwl7265_2ac_cfg)}, + +/* 8000 Series */ + {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x1130, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xC110, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)}, + {IWL_PCI_DEVICE(0x24F5, 0x0010, iwl4165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F6, 0x0030, iwl4165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)}, +#endif /* CONFIG_IWLMVM */ + + {0} +}; +MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); + +#ifdef CONFIG_ACPI +#define SPL_METHOD "SPLC" +#define SPL_DOMAINTYPE_MODULE BIT(0) +#define SPL_DOMAINTYPE_WIFI BIT(1) +#define SPL_DOMAINTYPE_WIGIG BIT(2) +#define SPL_DOMAINTYPE_RFEM BIT(3) + +static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx) +{ + union acpi_object *limits, *domain_type, *power_limit; + + if (splx->type != ACPI_TYPE_PACKAGE || + splx->package.count != 2 || + splx->package.elements[0].type != ACPI_TYPE_INTEGER || + splx->package.elements[0].integer.value != 0) { + IWL_ERR(trans, "Unsupported splx structure\n"); + return 0; + } + + limits = &splx->package.elements[1]; + if (limits->type != ACPI_TYPE_PACKAGE || + limits->package.count < 2 || + limits->package.elements[0].type != ACPI_TYPE_INTEGER || + limits->package.elements[1].type != ACPI_TYPE_INTEGER) { + IWL_ERR(trans, "Invalid limits element\n"); + return 0; + } + + domain_type = &limits->package.elements[0]; + power_limit = &limits->package.elements[1]; + if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) { + IWL_DEBUG_INFO(trans, "WiFi power is not limited\n"); + return 0; + } + + return power_limit->integer.value; +} + +static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) +{ + acpi_handle pxsx_handle; + acpi_handle handle; + struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + + pxsx_handle = ACPI_HANDLE(&pdev->dev); + if (!pxsx_handle) { + IWL_DEBUG_INFO(trans, + "Could not retrieve root port ACPI handle\n"); + return; + } + + /* Get the method's handle */ + status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_INFO(trans, "SPL method not found\n"); + return; + } + + /* Call SPLC with no arguments */ + status = acpi_evaluate_object(handle, NULL, NULL, &splx); + if (ACPI_FAILURE(status)) { + IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status); + return; + } + + trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer); + IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n", + trans->dflt_pwr_limit); + kfree(splx.pointer); +} + +#else /* CONFIG_ACPI */ +static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) {} +#endif + +/* PCI registers */ +#define PCI_CFG_RETRY_TIMEOUT 0x041 + +static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); + const struct iwl_cfg *cfg_7265d __maybe_unused = NULL; + struct iwl_trans *iwl_trans; + struct iwl_trans_pcie *trans_pcie; + int ret; + + iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg); + if (IS_ERR(iwl_trans)) + return PTR_ERR(iwl_trans); + +#if IS_ENABLED(CONFIG_IWLMVM) + /* + * special-case 7265D, it has the same PCI IDs. + * + * Note that because we already pass the cfg to the transport above, + * all the parameters that the transport uses must, until that is + * changed, be identical to the ones in the 7265D configuration. + */ + if (cfg == &iwl7265_2ac_cfg) + cfg_7265d = &iwl7265d_2ac_cfg; + else if (cfg == &iwl7265_2n_cfg) + cfg_7265d = &iwl7265d_2n_cfg; + else if (cfg == &iwl7265_n_cfg) + cfg_7265d = &iwl7265d_n_cfg; + if (cfg_7265d && + (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D) { + cfg = cfg_7265d; + iwl_trans->cfg = cfg_7265d; + } +#endif + + pci_set_drvdata(pdev, iwl_trans); + + trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans); + trans_pcie->drv = iwl_drv_start(iwl_trans, cfg); + + if (IS_ERR(trans_pcie->drv)) { + ret = PTR_ERR(trans_pcie->drv); + goto out_free_trans; + } + + set_dflt_pwr_limit(iwl_trans, pdev); + + /* register transport layer debugfs here */ + ret = iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir); + if (ret) + goto out_free_drv; + + return 0; + +out_free_drv: + iwl_drv_stop(trans_pcie->drv); +out_free_trans: + iwl_trans_pcie_free(iwl_trans); + return ret; +} + +static void iwl_pci_remove(struct pci_dev *pdev) +{ + struct iwl_trans *trans = pci_get_drvdata(pdev); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + iwl_drv_stop(trans_pcie->drv); + iwl_trans_pcie_free(trans); +} + +#ifdef CONFIG_PM_SLEEP + +static int iwl_pci_suspend(struct device *device) +{ + /* Before you put code here, think about WoWLAN. You cannot check here + * whether WoWLAN is enabled or not, and your code will run even if + * WoWLAN is enabled - don't kill the NIC, someone may need it in Sx. + */ + + return 0; +} + +static int iwl_pci_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct iwl_trans *trans = pci_get_drvdata(pdev); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + bool hw_rfkill; + + /* Before you put code here, think about WoWLAN. You cannot check here + * whether WoWLAN is enabled or not, and your code will run even if + * WoWLAN is enabled - the NIC may be alive. + */ + + /* + * We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); + + if (!trans->op_mode) + return 0; + + /* + * Enable rfkill interrupt (in order to keep track of + * the rfkill status) + */ + iwl_enable_rfkill_int(trans); + + hw_rfkill = iwl_is_rfkill_set(trans); + + mutex_lock(&trans_pcie->mutex); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); + mutex_unlock(&trans_pcie->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume); + +#define IWL_PM_OPS (&iwl_dev_pm_ops) + +#else + +#define IWL_PM_OPS NULL + +#endif + +static struct pci_driver iwl_pci_driver = { + .name = DRV_NAME, + .id_table = iwl_hw_card_ids, + .probe = iwl_pci_probe, + .remove = iwl_pci_remove, + .driver.pm = IWL_PM_OPS, +}; + +int __must_check iwl_pci_register_driver(void) +{ + int ret; + ret = pci_register_driver(&iwl_pci_driver); + if (ret) + pr_err("Unable to initialize PCI module\n"); + + return ret; +} + +void iwl_pci_unregister_driver(void) +{ + pci_unregister_driver(&iwl_pci_driver); +} diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h new file mode 100644 index 000000000000..feb2f7e81134 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -0,0 +1,569 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2015 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#ifndef __iwl_trans_int_pcie_h__ +#define __iwl_trans_int_pcie_h__ + +#include +#include +#include +#include +#include +#include + +#include "iwl-fh.h" +#include "iwl-csr.h" +#include "iwl-trans.h" +#include "iwl-debug.h" +#include "iwl-io.h" +#include "iwl-op-mode.h" + +/* We need 2 entries for the TX command and header, and another one might + * be needed for potential data in the SKB's head. The remaining ones can + * be used for frags. + */ +#define IWL_PCIE_MAX_FRAGS (IWL_NUM_OF_TBS - 3) + +/* + * RX related structures and functions + */ +#define RX_NUM_QUEUES 1 +#define RX_POST_REQ_ALLOC 2 +#define RX_CLAIM_REQ_ALLOC 8 +#define RX_POOL_SIZE ((RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC) * RX_NUM_QUEUES) +#define RX_LOW_WATERMARK 8 + +struct iwl_host_cmd; + +/*This file includes the declaration that are internal to the + * trans_pcie layer */ + +struct iwl_rx_mem_buffer { + dma_addr_t page_dma; + struct page *page; + struct list_head list; +}; + +/** + * struct isr_statistics - interrupt statistics + * + */ +struct isr_statistics { + u32 hw; + u32 sw; + u32 err_code; + u32 sch; + u32 alive; + u32 rfkill; + u32 ctkill; + u32 wakeup; + u32 rx; + u32 tx; + u32 unhandled; +}; + +/** + * struct iwl_rxq - Rx queue + * @bd: driver's pointer to buffer of receive buffer descriptors (rbd) + * @bd_dma: bus address of buffer of receive buffer descriptors (rbd) + * @read: Shared index to newest available Rx buffer + * @write: Shared index to oldest written Rx packet + * @free_count: Number of pre-allocated buffers in rx_free + * @used_count: Number of RBDs handled to allocator to use for allocation + * @write_actual: + * @rx_free: list of RBDs with allocated RB ready for use + * @rx_used: list of RBDs with no RB attached + * @need_update: flag to indicate we need to update read/write index + * @rb_stts: driver's pointer to receive buffer status + * @rb_stts_dma: bus address of receive buffer status + * @lock: + * @pool: initial pool of iwl_rx_mem_buffer for the queue + * @queue: actual rx queue + * + * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers + */ +struct iwl_rxq { + __le32 *bd; + dma_addr_t bd_dma; + u32 read; + u32 write; + u32 free_count; + u32 used_count; + u32 write_actual; + struct list_head rx_free; + struct list_head rx_used; + bool need_update; + struct iwl_rb_status *rb_stts; + dma_addr_t rb_stts_dma; + spinlock_t lock; + struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE]; + struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE]; +}; + +/** + * struct iwl_rb_allocator - Rx allocator + * @pool: initial pool of allocator + * @req_pending: number of requests the allcator had not processed yet + * @req_ready: number of requests honored and ready for claiming + * @rbd_allocated: RBDs with pages allocated and ready to be handled to + * the queue. This is a list of &struct iwl_rx_mem_buffer + * @rbd_empty: RBDs with no page attached for allocator use. This is a list + * of &struct iwl_rx_mem_buffer + * @lock: protects the rbd_allocated and rbd_empty lists + * @alloc_wq: work queue for background calls + * @rx_alloc: work struct for background calls + */ +struct iwl_rb_allocator { + struct iwl_rx_mem_buffer pool[RX_POOL_SIZE]; + atomic_t req_pending; + atomic_t req_ready; + struct list_head rbd_allocated; + struct list_head rbd_empty; + spinlock_t lock; + struct workqueue_struct *alloc_wq; + struct work_struct rx_alloc; +}; + +struct iwl_dma_ptr { + dma_addr_t dma; + void *addr; + size_t size; +}; + +/** + * iwl_queue_inc_wrap - increment queue index, wrap back to beginning + * @index -- current index + */ +static inline int iwl_queue_inc_wrap(int index) +{ + return ++index & (TFD_QUEUE_SIZE_MAX - 1); +} + +/** + * iwl_queue_dec_wrap - decrement queue index, wrap back to end + * @index -- current index + */ +static inline int iwl_queue_dec_wrap(int index) +{ + return --index & (TFD_QUEUE_SIZE_MAX - 1); +} + +struct iwl_cmd_meta { + /* only for SYNC commands, iff the reply skb is wanted */ + struct iwl_host_cmd *source; + u32 flags; +}; + +/* + * Generic queue structure + * + * Contains common data for Rx and Tx queues. + * + * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware + * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless + * there might be HW changes in the future). For the normal TX + * queues, n_window, which is the size of the software queue data + * is also 256; however, for the command queue, n_window is only + * 32 since we don't need so many commands pending. Since the HW + * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. As a result, + * the software buffers (in the variables @meta, @txb in struct + * iwl_txq) only have 32 entries, while the HW buffers (@tfds in + * the same struct) have 256. + * This means that we end up with the following: + * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 | + * SW entries: | 0 | ... | 31 | + * where N is a number between 0 and 7. This means that the SW + * data is a window overlayed over the HW queue. + */ +struct iwl_queue { + int write_ptr; /* 1-st empty entry (index) host_w*/ + int read_ptr; /* last used entry (index) host_r*/ + /* use for monitoring and recovering the stuck queue */ + dma_addr_t dma_addr; /* physical addr for BD's */ + int n_window; /* safe queue window */ + u32 id; + int low_mark; /* low watermark, resume queue if free + * space more than this */ + int high_mark; /* high watermark, stop queue if free + * space less than this */ +}; + +#define TFD_TX_CMD_SLOTS 256 +#define TFD_CMD_SLOTS 32 + +/* + * The FH will write back to the first TB only, so we need + * to copy some data into the buffer regardless of whether + * it should be mapped or not. This indicates how big the + * first TB must be to include the scratch buffer. Since + * the scratch is 4 bytes at offset 12, it's 16 now. If we + * make it bigger then allocations will be bigger and copy + * slower, so that's probably not useful. + */ +#define IWL_HCMD_SCRATCHBUF_SIZE 16 + +struct iwl_pcie_txq_entry { + struct iwl_device_cmd *cmd; + struct sk_buff *skb; + /* buffer to free after command completes */ + const void *free_buf; + struct iwl_cmd_meta meta; +}; + +struct iwl_pcie_txq_scratch_buf { + struct iwl_cmd_header hdr; + u8 buf[8]; + __le32 scratch; +}; + +/** + * struct iwl_txq - Tx Queue for DMA + * @q: generic Rx/Tx queue descriptor + * @tfds: transmit frame descriptors (DMA memory) + * @scratchbufs: start of command headers, including scratch buffers, for + * the writeback -- this is DMA memory and an array holding one buffer + * for each command on the queue + * @scratchbufs_dma: DMA address for the scratchbufs start + * @entries: transmit entries (driver state) + * @lock: queue lock + * @stuck_timer: timer that fires if queue gets stuck + * @trans_pcie: pointer back to transport (for timer) + * @need_update: indicates need to update read/write index + * @active: stores if queue is active + * @ampdu: true if this queue is an ampdu queue for an specific RA/TID + * @wd_timeout: queue watchdog timeout (jiffies) - per queue + * @frozen: tx stuck queue timer is frozen + * @frozen_expiry_remainder: remember how long until the timer fires + * + * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame + * descriptors) and required locking structures. + */ +struct iwl_txq { + struct iwl_queue q; + struct iwl_tfd *tfds; + struct iwl_pcie_txq_scratch_buf *scratchbufs; + dma_addr_t scratchbufs_dma; + struct iwl_pcie_txq_entry *entries; + spinlock_t lock; + unsigned long frozen_expiry_remainder; + struct timer_list stuck_timer; + struct iwl_trans_pcie *trans_pcie; + bool need_update; + bool frozen; + u8 active; + bool ampdu; + unsigned long wd_timeout; +}; + +static inline dma_addr_t +iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) +{ + return txq->scratchbufs_dma + + sizeof(struct iwl_pcie_txq_scratch_buf) * idx; +} + +/** + * struct iwl_trans_pcie - PCIe transport specific data + * @rxq: all the RX queue data + * @rba: allocator for RX replenishing + * @drv - pointer to iwl_drv + * @trans: pointer to the generic transport area + * @scd_base_addr: scheduler sram base address in SRAM + * @scd_bc_tbls: pointer to the byte count table of the scheduler + * @kw: keep warm address + * @pci_dev: basic pci-network driver stuff + * @hw_base: pci hardware address support + * @ucode_write_complete: indicates that the ucode has been copied. + * @ucode_write_waitq: wait queue for uCode load + * @cmd_queue - command queue number + * @rx_buf_size_8k: 8 kB RX buffer size + * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) + * @scd_set_active: should the transport configure the SCD for HCMD queue + * @wide_cmd_header: true when ucode supports wide command header format + * @rx_page_order: page order for receive buffer size + * @reg_lock: protect hw register access + * @mutex: to protect stop_device / start_fw / start_hw + * @cmd_in_flight: true when we have a host command in flight + * @fw_mon_phys: physical address of the buffer for the firmware monitor + * @fw_mon_page: points to the first page of the buffer for the firmware monitor + * @fw_mon_size: size of the buffer for the firmware monitor + */ +struct iwl_trans_pcie { + struct iwl_rxq rxq; + struct iwl_rb_allocator rba; + struct iwl_trans *trans; + struct iwl_drv *drv; + + struct net_device napi_dev; + struct napi_struct napi; + + /* INT ICT Table */ + __le32 *ict_tbl; + dma_addr_t ict_tbl_dma; + int ict_index; + bool use_ict; + bool is_down; + struct isr_statistics isr_stats; + + spinlock_t irq_lock; + struct mutex mutex; + u32 inta_mask; + u32 scd_base_addr; + struct iwl_dma_ptr scd_bc_tbls; + struct iwl_dma_ptr kw; + + struct iwl_txq *txq; + unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; + unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; + + /* PCI bus related data */ + struct pci_dev *pci_dev; + void __iomem *hw_base; + + bool ucode_write_complete; + wait_queue_head_t ucode_write_waitq; + wait_queue_head_t wait_command_queue; + + u8 cmd_queue; + u8 cmd_fifo; + unsigned int cmd_q_wdg_timeout; + u8 n_no_reclaim_cmds; + u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; + + bool rx_buf_size_8k; + bool bc_table_dword; + bool scd_set_active; + bool wide_cmd_header; + u32 rx_page_order; + + const char *const *command_names; + + /*protect hw register */ + spinlock_t reg_lock; + bool cmd_hold_nic_awake; + bool ref_cmd_in_flight; + + /* protect ref counter */ + spinlock_t ref_lock; + u32 ref_count; + + dma_addr_t fw_mon_phys; + struct page *fw_mon_page; + u32 fw_mon_size; +}; + +#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ + ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) + +static inline struct iwl_trans * +iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie) +{ + return container_of((void *)trans_pcie, struct iwl_trans, + trans_specific); +} + +/* + * Convention: trans API functions: iwl_trans_pcie_XXX + * Other functions: iwl_pcie_XXX + */ +struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, + const struct pci_device_id *ent, + const struct iwl_cfg *cfg); +void iwl_trans_pcie_free(struct iwl_trans *trans); + +/***************************************************** +* RX +******************************************************/ +int iwl_pcie_rx_init(struct iwl_trans *trans); +irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id); +int iwl_pcie_rx_stop(struct iwl_trans *trans); +void iwl_pcie_rx_free(struct iwl_trans *trans); + +/***************************************************** +* ICT - interrupt handling +******************************************************/ +irqreturn_t iwl_pcie_isr(int irq, void *data); +int iwl_pcie_alloc_ict(struct iwl_trans *trans); +void iwl_pcie_free_ict(struct iwl_trans *trans); +void iwl_pcie_reset_ict(struct iwl_trans *trans); +void iwl_pcie_disable_ict(struct iwl_trans *trans); + +/***************************************************** +* TX / HCMD +******************************************************/ +int iwl_pcie_tx_init(struct iwl_trans *trans); +void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr); +int iwl_pcie_tx_stop(struct iwl_trans *trans); +void iwl_pcie_tx_free(struct iwl_trans *trans); +void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int queue, u16 ssn, + const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int wdg_timeout); +void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue, + bool configure_scd); +int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_device_cmd *dev_cmd, int txq_id); +void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans); +int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); +void iwl_pcie_hcmd_complete(struct iwl_trans *trans, + struct iwl_rx_cmd_buffer *rxb); +void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, + struct sk_buff_head *skbs); +void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); + +void iwl_trans_pcie_ref(struct iwl_trans *trans); +void iwl_trans_pcie_unref(struct iwl_trans *trans); + +static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) +{ + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + + return le16_to_cpu(tb->hi_n_len) >> 4; +} + +/***************************************************** +* Error handling +******************************************************/ +void iwl_pcie_dump_csr(struct iwl_trans *trans); + +/***************************************************** +* Helpers +******************************************************/ +static inline void iwl_disable_interrupts(struct iwl_trans *trans) +{ + clear_bit(STATUS_INT_ENABLED, &trans->status); + + /* disable interrupts from uCode/NIC to host */ + iwl_write32(trans, CSR_INT_MASK, 0x00000000); + + /* acknowledge/clear/reset any interrupts still pending + * from uCode or flow handler (Rx/Tx DMA) */ + iwl_write32(trans, CSR_INT, 0xffffffff); + iwl_write32(trans, CSR_FH_INT_STATUS, 0xffffffff); + IWL_DEBUG_ISR(trans, "Disabled interrupts\n"); +} + +static inline void iwl_enable_interrupts(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + IWL_DEBUG_ISR(trans, "Enabling interrupts\n"); + set_bit(STATUS_INT_ENABLED, &trans->status); + trans_pcie->inta_mask = CSR_INI_SET_MASK; + iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); +} + +static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + IWL_DEBUG_ISR(trans, "Enabling rfkill interrupt\n"); + trans_pcie->inta_mask = CSR_INT_BIT_RF_KILL; + iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); +} + +static inline void iwl_wake_queue(struct iwl_trans *trans, + struct iwl_txq *txq) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (test_and_clear_bit(txq->q.id, trans_pcie->queue_stopped)) { + IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d\n", txq->q.id); + iwl_op_mode_queue_not_full(trans->op_mode, txq->q.id); + } +} + +static inline void iwl_stop_queue(struct iwl_trans *trans, + struct iwl_txq *txq) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (!test_and_set_bit(txq->q.id, trans_pcie->queue_stopped)) { + iwl_op_mode_queue_full(trans->op_mode, txq->q.id); + IWL_DEBUG_TX_QUEUES(trans, "Stop hwq %d\n", txq->q.id); + } else + IWL_DEBUG_TX_QUEUES(trans, "hwq %d already stopped\n", + txq->q.id); +} + +static inline bool iwl_queue_used(const struct iwl_queue *q, int i) +{ + return q->write_ptr >= q->read_ptr ? + (i >= q->read_ptr && i < q->write_ptr) : + !(i < q->read_ptr && i >= q->write_ptr); +} + +static inline u8 get_cmd_index(struct iwl_queue *q, u32 index) +{ + return index & (q->n_window - 1); +} + +static inline const char *get_cmd_string(struct iwl_trans_pcie *trans_pcie, + u8 cmd) +{ + if (!trans_pcie->command_names || !trans_pcie->command_names[cmd]) + return "UNKNOWN"; + return trans_pcie->command_names[cmd]; +} + +static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) +{ + return !(iwl_read32(trans, CSR_GP_CNTRL) & + CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); +} + +static inline void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, + u32 reg, u32 mask, u32 value) +{ + u32 v; + +#ifdef CONFIG_IWLWIFI_DEBUG + WARN_ON_ONCE(value & ~mask); +#endif + + v = iwl_read32(trans, reg); + v &= ~mask; + v |= value; + iwl_write32(trans, reg, v); +} + +static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans, + u32 reg, u32 mask) +{ + __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0); +} + +static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans, + u32 reg, u32 mask) +{ + __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask); +} + +void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state); + +#endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c new file mode 100644 index 000000000000..e06591f625c4 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -0,0 +1,1548 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include + +#include "iwl-prph.h" +#include "iwl-io.h" +#include "internal.h" +#include "iwl-op-mode.h" + +/****************************************************************************** + * + * RX path functions + * + ******************************************************************************/ + +/* + * Rx theory of operation + * + * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), + * each of which point to Receive Buffers to be filled by the NIC. These get + * used not only for Rx frames, but for any command response or notification + * from the NIC. The driver and NIC manage the Rx buffers by means + * of indexes into the circular buffer. + * + * Rx Queue Indexes + * The host/firmware share two index registers for managing the Rx buffers. + * + * The READ index maps to the first position that the firmware may be writing + * to -- the driver can read up to (but not including) this position and get + * good data. + * The READ index is managed by the firmware once the card is enabled. + * + * The WRITE index maps to the last position the driver has read from -- the + * position preceding WRITE is the last slot the firmware can place a packet. + * + * The queue is empty (no good data) if WRITE = READ - 1, and is full if + * WRITE = READ. + * + * During initialization, the host sets up the READ queue position to the first + * INDEX position, and WRITE to the last (READ - 1 wrapped) + * + * When the firmware places a packet in a buffer, it will advance the READ index + * and fire the RX interrupt. The driver can then query the READ index and + * process as many packets as possible, moving the WRITE index forward as it + * resets the Rx queue buffers with new memory. + * + * The management in the driver is as follows: + * + A list of pre-allocated RBDs is stored in iwl->rxq->rx_free. + * When the interrupt handler is called, the request is processed. + * The page is either stolen - transferred to the upper layer + * or reused - added immediately to the iwl->rxq->rx_free list. + * + When the page is stolen - the driver updates the matching queue's used + * count, detaches the RBD and transfers it to the queue used list. + * When there are two used RBDs - they are transferred to the allocator empty + * list. Work is then scheduled for the allocator to start allocating + * eight buffers. + * When there are another 6 used RBDs - they are transferred to the allocator + * empty list and the driver tries to claim the pre-allocated buffers and + * add them to iwl->rxq->rx_free. If it fails - it continues to claim them + * until ready. + * When there are 8+ buffers in the free list - either from allocation or from + * 8 reused unstolen pages - restock is called to update the FW and indexes. + * + In order to make sure the allocator always has RBDs to use for allocation + * the allocator has initial pool in the size of num_queues*(8-2) - the + * maximum missing RBDs per allocation request (request posted with 2 + * empty RBDs, there is no guarantee when the other 6 RBDs are supplied). + * The queues supplies the recycle of the rest of the RBDs. + * + A received packet is processed and handed to the kernel network stack, + * detached from the iwl->rxq. The driver 'processed' index is updated. + * + If there are no allocated buffers in iwl->rxq->rx_free, + * the READ INDEX is not incremented and iwl->status(RX_STALLED) is set. + * If there were enough free buffers and RX_STALLED is set it is cleared. + * + * + * Driver sequence: + * + * iwl_rxq_alloc() Allocates rx_free + * iwl_pcie_rx_replenish() Replenishes rx_free list from rx_used, and calls + * iwl_pcie_rxq_restock. + * Used only during initialization. + * iwl_pcie_rxq_restock() Moves available buffers from rx_free into Rx + * queue, updates firmware pointers, and updates + * the WRITE index. + * iwl_pcie_rx_allocator() Background work for allocating pages. + * + * -- enable interrupts -- + * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the + * READ INDEX, detaching the SKB from the pool. + * Moves the packet buffer from queue to rx_used. + * Posts and claims requests to the allocator. + * Calls iwl_pcie_rxq_restock to refill any empty + * slots. + * + * RBD life-cycle: + * + * Init: + * rxq.pool -> rxq.rx_used -> rxq.rx_free -> rxq.queue + * + * Regular Receive interrupt: + * Page Stolen: + * rxq.queue -> rxq.rx_used -> allocator.rbd_empty -> + * allocator.rbd_allocated -> rxq.rx_free -> rxq.queue + * Page not Stolen: + * rxq.queue -> rxq.rx_free -> rxq.queue + * ... + * + */ + +/* + * iwl_rxq_space - Return number of free slots available in queue. + */ +static int iwl_rxq_space(const struct iwl_rxq *rxq) +{ + /* Make sure RX_QUEUE_SIZE is a power of 2 */ + BUILD_BUG_ON(RX_QUEUE_SIZE & (RX_QUEUE_SIZE - 1)); + + /* + * There can be up to (RX_QUEUE_SIZE - 1) free slots, to avoid ambiguity + * between empty and completely full queues. + * The following is equivalent to modulo by RX_QUEUE_SIZE and is well + * defined for negative dividends. + */ + return (rxq->read - rxq->write - 1) & (RX_QUEUE_SIZE - 1); +} + +/* + * iwl_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr + */ +static inline __le32 iwl_pcie_dma_addr2rbd_ptr(dma_addr_t dma_addr) +{ + return cpu_to_le32((u32)(dma_addr >> 8)); +} + +/* + * iwl_pcie_rx_stop - stops the Rx DMA + */ +int iwl_pcie_rx_stop(struct iwl_trans *trans) +{ + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG, + FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); +} + +/* + * iwl_pcie_rxq_inc_wr_ptr - Update the write pointer for the RX queue + */ +static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + u32 reg; + + lockdep_assert_held(&rxq->lock); + + /* + * explicitly wake up the NIC if: + * 1. shadow registers aren't enabled + * 2. there is a chance that the NIC is asleep + */ + if (!trans->cfg->base_params->shadow_reg_enable && + test_bit(STATUS_TPOWER_PMI, &trans->status)) { + reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + IWL_DEBUG_INFO(trans, "Rx queue requesting wakeup, GP1 = 0x%x\n", + reg); + iwl_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + rxq->need_update = true; + return; + } + } + + rxq->write_actual = round_down(rxq->write, 8); + iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); +} + +static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + + spin_lock(&rxq->lock); + + if (!rxq->need_update) + goto exit_unlock; + + iwl_pcie_rxq_inc_wr_ptr(trans); + rxq->need_update = false; + + exit_unlock: + spin_unlock(&rxq->lock); +} + +/* + * iwl_pcie_rxq_restock - refill RX queue from pre-allocated pool + * + * If there are slots in the RX queue that need to be restocked, + * and we have free pre-allocated buffers, fill the ranks as much + * as we can, pulling from rx_free. + * + * This moves the 'write' index forward to catch up with 'processed', and + * also updates the memory address in the firmware to reference the new + * target buffer. + */ +static void iwl_pcie_rxq_restock(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + struct iwl_rx_mem_buffer *rxb; + + /* + * If the device isn't enabled - not need to try to add buffers... + * This can happen when we stop the device and still have an interrupt + * pending. We stop the APM before we sync the interrupts because we + * have to (see comment there). On the other hand, since the APM is + * stopped, we cannot access the HW (in particular not prph). + * So don't try to restock if the APM has been already stopped. + */ + if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) + return; + + spin_lock(&rxq->lock); + while ((iwl_rxq_space(rxq) > 0) && (rxq->free_count)) { + /* The overwritten rxb must be a used one */ + rxb = rxq->queue[rxq->write]; + BUG_ON(rxb && rxb->page); + + /* Get next free Rx buffer, remove from free list */ + rxb = list_first_entry(&rxq->rx_free, struct iwl_rx_mem_buffer, + list); + list_del(&rxb->list); + + /* Point to Rx buffer via next RBD in circular buffer */ + rxq->bd[rxq->write] = iwl_pcie_dma_addr2rbd_ptr(rxb->page_dma); + rxq->queue[rxq->write] = rxb; + rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; + rxq->free_count--; + } + spin_unlock(&rxq->lock); + + /* If we've added more space for the firmware to place data, tell it. + * Increment device's write pointer in multiples of 8. */ + if (rxq->write_actual != (rxq->write & ~0x7)) { + spin_lock(&rxq->lock); + iwl_pcie_rxq_inc_wr_ptr(trans); + spin_unlock(&rxq->lock); + } +} + +/* + * iwl_pcie_rx_alloc_page - allocates and returns a page. + * + */ +static struct page *iwl_pcie_rx_alloc_page(struct iwl_trans *trans, + gfp_t priority) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + struct page *page; + gfp_t gfp_mask = priority; + + if (rxq->free_count > RX_LOW_WATERMARK) + gfp_mask |= __GFP_NOWARN; + + if (trans_pcie->rx_page_order > 0) + gfp_mask |= __GFP_COMP; + + /* Alloc a new receive buffer */ + page = alloc_pages(gfp_mask, trans_pcie->rx_page_order); + if (!page) { + if (net_ratelimit()) + IWL_DEBUG_INFO(trans, "alloc_pages failed, order: %d\n", + trans_pcie->rx_page_order); + /* Issue an error if the hardware has consumed more than half + * of its free buffer list and we don't have enough + * pre-allocated buffers. +` */ + if (rxq->free_count <= RX_LOW_WATERMARK && + iwl_rxq_space(rxq) > (RX_QUEUE_SIZE / 2) && + net_ratelimit()) + IWL_CRIT(trans, + "Failed to alloc_pages with GFP_KERNEL. Only %u free buffers remaining.\n", + rxq->free_count); + return NULL; + } + return page; +} + +/* + * iwl_pcie_rxq_alloc_rbs - allocate a page for each used RBD + * + * A used RBD is an Rx buffer that has been given to the stack. To use it again + * a page must be allocated and the RBD must point to the page. This function + * doesn't change the HW pointer but handles the list of pages that is used by + * iwl_pcie_rxq_restock. The latter function will update the HW to use the newly + * allocated buffers. + */ +static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + struct iwl_rx_mem_buffer *rxb; + struct page *page; + + while (1) { + spin_lock(&rxq->lock); + if (list_empty(&rxq->rx_used)) { + spin_unlock(&rxq->lock); + return; + } + spin_unlock(&rxq->lock); + + /* Alloc a new receive buffer */ + page = iwl_pcie_rx_alloc_page(trans, priority); + if (!page) + return; + + spin_lock(&rxq->lock); + + if (list_empty(&rxq->rx_used)) { + spin_unlock(&rxq->lock); + __free_pages(page, trans_pcie->rx_page_order); + return; + } + rxb = list_first_entry(&rxq->rx_used, struct iwl_rx_mem_buffer, + list); + list_del(&rxb->list); + spin_unlock(&rxq->lock); + + BUG_ON(rxb->page); + rxb->page = page; + /* Get physical address of the RB */ + rxb->page_dma = + dma_map_page(trans->dev, page, 0, + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); + if (dma_mapping_error(trans->dev, rxb->page_dma)) { + rxb->page = NULL; + spin_lock(&rxq->lock); + list_add(&rxb->list, &rxq->rx_used); + spin_unlock(&rxq->lock); + __free_pages(page, trans_pcie->rx_page_order); + return; + } + /* dma address must be no more than 36 bits */ + BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); + /* and also 256 byte aligned! */ + BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); + + spin_lock(&rxq->lock); + + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + + spin_unlock(&rxq->lock); + } +} + +static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + int i; + + lockdep_assert_held(&rxq->lock); + + for (i = 0; i < RX_QUEUE_SIZE; i++) { + if (!rxq->pool[i].page) + continue; + dma_unmap_page(trans->dev, rxq->pool[i].page_dma, + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); + __free_pages(rxq->pool[i].page, trans_pcie->rx_page_order); + rxq->pool[i].page = NULL; + } +} + +/* + * iwl_pcie_rx_replenish - Move all used buffers from rx_used to rx_free + * + * When moving to rx_free an page is allocated for the slot. + * + * Also restock the Rx queue via iwl_pcie_rxq_restock. + * This is called only during initialization + */ +static void iwl_pcie_rx_replenish(struct iwl_trans *trans) +{ + iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL); + + iwl_pcie_rxq_restock(trans); +} + +/* + * iwl_pcie_rx_allocator - Allocates pages in the background for RX queues + * + * Allocates for each received request 8 pages + * Called as a scheduled work item. + */ +static void iwl_pcie_rx_allocator(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rb_allocator *rba = &trans_pcie->rba; + struct list_head local_empty; + int pending = atomic_xchg(&rba->req_pending, 0); + + IWL_DEBUG_RX(trans, "Pending allocation requests = %d\n", pending); + + /* If we were scheduled - there is at least one request */ + spin_lock(&rba->lock); + /* swap out the rba->rbd_empty to a local list */ + list_replace_init(&rba->rbd_empty, &local_empty); + spin_unlock(&rba->lock); + + while (pending) { + int i; + struct list_head local_allocated; + + INIT_LIST_HEAD(&local_allocated); + + for (i = 0; i < RX_CLAIM_REQ_ALLOC;) { + struct iwl_rx_mem_buffer *rxb; + struct page *page; + + /* List should never be empty - each reused RBD is + * returned to the list, and initial pool covers any + * possible gap between the time the page is allocated + * to the time the RBD is added. + */ + BUG_ON(list_empty(&local_empty)); + /* Get the first rxb from the rbd list */ + rxb = list_first_entry(&local_empty, + struct iwl_rx_mem_buffer, list); + BUG_ON(rxb->page); + + /* Alloc a new receive buffer */ + page = iwl_pcie_rx_alloc_page(trans, GFP_KERNEL); + if (!page) + continue; + rxb->page = page; + + /* Get physical address of the RB */ + rxb->page_dma = dma_map_page(trans->dev, page, 0, + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); + if (dma_mapping_error(trans->dev, rxb->page_dma)) { + rxb->page = NULL; + __free_pages(page, trans_pcie->rx_page_order); + continue; + } + /* dma address must be no more than 36 bits */ + BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); + /* and also 256 byte aligned! */ + BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); + + /* move the allocated entry to the out list */ + list_move(&rxb->list, &local_allocated); + i++; + } + + pending--; + if (!pending) { + pending = atomic_xchg(&rba->req_pending, 0); + IWL_DEBUG_RX(trans, + "Pending allocation requests = %d\n", + pending); + } + + spin_lock(&rba->lock); + /* add the allocated rbds to the allocator allocated list */ + list_splice_tail(&local_allocated, &rba->rbd_allocated); + /* get more empty RBDs for current pending requests */ + list_splice_tail_init(&rba->rbd_empty, &local_empty); + spin_unlock(&rba->lock); + + atomic_inc(&rba->req_ready); + } + + spin_lock(&rba->lock); + /* return unused rbds to the allocator empty list */ + list_splice_tail(&local_empty, &rba->rbd_empty); + spin_unlock(&rba->lock); +} + +/* + * iwl_pcie_rx_allocator_get - Returns the pre-allocated pages +.* +.* Called by queue when the queue posted allocation request and + * has freed 8 RBDs in order to restock itself. + */ +static int iwl_pcie_rx_allocator_get(struct iwl_trans *trans, + struct iwl_rx_mem_buffer + *out[RX_CLAIM_REQ_ALLOC]) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rb_allocator *rba = &trans_pcie->rba; + int i; + + /* + * atomic_dec_if_positive returns req_ready - 1 for any scenario. + * If req_ready is 0 atomic_dec_if_positive will return -1 and this + * function will return -ENOMEM, as there are no ready requests. + * atomic_dec_if_positive will perofrm the *actual* decrement only if + * req_ready > 0, i.e. - there are ready requests and the function + * hands one request to the caller. + */ + if (atomic_dec_if_positive(&rba->req_ready) < 0) + return -ENOMEM; + + spin_lock(&rba->lock); + for (i = 0; i < RX_CLAIM_REQ_ALLOC; i++) { + /* Get next free Rx buffer, remove it from free list */ + out[i] = list_first_entry(&rba->rbd_allocated, + struct iwl_rx_mem_buffer, list); + list_del(&out[i]->list); + } + spin_unlock(&rba->lock); + + return 0; +} + +static void iwl_pcie_rx_allocator_work(struct work_struct *data) +{ + struct iwl_rb_allocator *rba_p = + container_of(data, struct iwl_rb_allocator, rx_alloc); + struct iwl_trans_pcie *trans_pcie = + container_of(rba_p, struct iwl_trans_pcie, rba); + + iwl_pcie_rx_allocator(trans_pcie->trans); +} + +static int iwl_pcie_rx_alloc(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + struct iwl_rb_allocator *rba = &trans_pcie->rba; + struct device *dev = trans->dev; + + memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq)); + + spin_lock_init(&rxq->lock); + spin_lock_init(&rba->lock); + + if (WARN_ON(rxq->bd || rxq->rb_stts)) + return -EINVAL; + + /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */ + rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, + &rxq->bd_dma, GFP_KERNEL); + if (!rxq->bd) + goto err_bd; + + /*Allocate the driver's pointer to receive buffer status */ + rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts), + &rxq->rb_stts_dma, GFP_KERNEL); + if (!rxq->rb_stts) + goto err_rb_stts; + + return 0; + +err_rb_stts: + dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, + rxq->bd, rxq->bd_dma); + rxq->bd_dma = 0; + rxq->bd = NULL; +err_bd: + return -ENOMEM; +} + +static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 rb_size; + const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ + + if (trans_pcie->rx_buf_size_8k) + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; + else + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; + + /* Stop Rx DMA */ + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + /* reset and flush pointers */ + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0); + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0); + iwl_write_direct32(trans, FH_RSCSR_CHNL0_RDPTR, 0); + + /* Reset driver's Rx queue write index */ + iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); + + /* Tell device where to find RBD circular buffer in DRAM */ + iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_BASE_REG, + (u32)(rxq->bd_dma >> 8)); + + /* Tell device where in DRAM to update its Rx status */ + iwl_write_direct32(trans, FH_RSCSR_CHNL0_STTS_WPTR_REG, + rxq->rb_stts_dma >> 4); + + /* Enable Rx DMA + * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in + * the credit mechanism in 5000 HW RX FIFO + * Direct rx interrupts to hosts + * Rx buffer size 4 or 8k + * RB timeout 0x10 + * 256 RBDs + */ + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, + FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | + FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | + FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | + rb_size| + (RX_RB_TIMEOUT << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| + (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); + + /* Set interrupt coalescing timer to default (2048 usecs) */ + iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); + + /* W/A for interrupt coalescing bug in 7260 and 3160 */ + if (trans->cfg->host_interrupt_operation_mode) + iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE); +} + +static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq) +{ + int i; + + lockdep_assert_held(&rxq->lock); + + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + rxq->free_count = 0; + rxq->used_count = 0; + + for (i = 0; i < RX_QUEUE_SIZE; i++) + list_add(&rxq->pool[i].list, &rxq->rx_used); +} + +static void iwl_pcie_rx_init_rba(struct iwl_rb_allocator *rba) +{ + int i; + + lockdep_assert_held(&rba->lock); + + INIT_LIST_HEAD(&rba->rbd_allocated); + INIT_LIST_HEAD(&rba->rbd_empty); + + for (i = 0; i < RX_POOL_SIZE; i++) + list_add(&rba->pool[i].list, &rba->rbd_empty); +} + +static void iwl_pcie_rx_free_rba(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rb_allocator *rba = &trans_pcie->rba; + int i; + + lockdep_assert_held(&rba->lock); + + for (i = 0; i < RX_POOL_SIZE; i++) { + if (!rba->pool[i].page) + continue; + dma_unmap_page(trans->dev, rba->pool[i].page_dma, + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); + __free_pages(rba->pool[i].page, trans_pcie->rx_page_order); + rba->pool[i].page = NULL; + } +} + +int iwl_pcie_rx_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + struct iwl_rb_allocator *rba = &trans_pcie->rba; + int i, err; + + if (!rxq->bd) { + err = iwl_pcie_rx_alloc(trans); + if (err) + return err; + } + if (!rba->alloc_wq) + rba->alloc_wq = alloc_workqueue("rb_allocator", + WQ_HIGHPRI | WQ_UNBOUND, 1); + INIT_WORK(&rba->rx_alloc, iwl_pcie_rx_allocator_work); + + spin_lock(&rba->lock); + atomic_set(&rba->req_pending, 0); + atomic_set(&rba->req_ready, 0); + /* free all first - we might be reconfigured for a different size */ + iwl_pcie_rx_free_rba(trans); + iwl_pcie_rx_init_rba(rba); + spin_unlock(&rba->lock); + + spin_lock(&rxq->lock); + + /* free all first - we might be reconfigured for a different size */ + iwl_pcie_rxq_free_rbs(trans); + iwl_pcie_rx_init_rxb_lists(rxq); + + for (i = 0; i < RX_QUEUE_SIZE; i++) + rxq->queue[i] = NULL; + + /* Set us so that we have processed and used all buffers, but have + * not restocked the Rx queue with fresh buffers */ + rxq->read = rxq->write = 0; + rxq->write_actual = 0; + memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts)); + spin_unlock(&rxq->lock); + + iwl_pcie_rx_replenish(trans); + + iwl_pcie_rx_hw_init(trans, rxq); + + spin_lock(&rxq->lock); + iwl_pcie_rxq_inc_wr_ptr(trans); + spin_unlock(&rxq->lock); + + return 0; +} + +void iwl_pcie_rx_free(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + struct iwl_rb_allocator *rba = &trans_pcie->rba; + + /*if rxq->bd is NULL, it means that nothing has been allocated, + * exit now */ + if (!rxq->bd) { + IWL_DEBUG_INFO(trans, "Free NULL rx context\n"); + return; + } + + cancel_work_sync(&rba->rx_alloc); + if (rba->alloc_wq) { + destroy_workqueue(rba->alloc_wq); + rba->alloc_wq = NULL; + } + + spin_lock(&rba->lock); + iwl_pcie_rx_free_rba(trans); + spin_unlock(&rba->lock); + + spin_lock(&rxq->lock); + iwl_pcie_rxq_free_rbs(trans); + spin_unlock(&rxq->lock); + + dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE, + rxq->bd, rxq->bd_dma); + rxq->bd_dma = 0; + rxq->bd = NULL; + + if (rxq->rb_stts) + dma_free_coherent(trans->dev, + sizeof(struct iwl_rb_status), + rxq->rb_stts, rxq->rb_stts_dma); + else + IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n"); + rxq->rb_stts_dma = 0; + rxq->rb_stts = NULL; +} + +/* + * iwl_pcie_rx_reuse_rbd - Recycle used RBDs + * + * Called when a RBD can be reused. The RBD is transferred to the allocator. + * When there are 2 empty RBDs - a request for allocation is posted + */ +static void iwl_pcie_rx_reuse_rbd(struct iwl_trans *trans, + struct iwl_rx_mem_buffer *rxb, + struct iwl_rxq *rxq, bool emergency) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rb_allocator *rba = &trans_pcie->rba; + + /* Move the RBD to the used list, will be moved to allocator in batches + * before claiming or posting a request*/ + list_add_tail(&rxb->list, &rxq->rx_used); + + if (unlikely(emergency)) + return; + + /* Count the allocator owned RBDs */ + rxq->used_count++; + + /* If we have RX_POST_REQ_ALLOC new released rx buffers - + * issue a request for allocator. Modulo RX_CLAIM_REQ_ALLOC is + * used for the case we failed to claim RX_CLAIM_REQ_ALLOC, + * after but we still need to post another request. + */ + if ((rxq->used_count % RX_CLAIM_REQ_ALLOC) == RX_POST_REQ_ALLOC) { + /* Move the 2 RBDs to the allocator ownership. + Allocator has another 6 from pool for the request completion*/ + spin_lock(&rba->lock); + list_splice_tail_init(&rxq->rx_used, &rba->rbd_empty); + spin_unlock(&rba->lock); + + atomic_inc(&rba->req_pending); + queue_work(rba->alloc_wq, &rba->rx_alloc); + } +} + +static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, + struct iwl_rx_mem_buffer *rxb, + bool emergency) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + bool page_stolen = false; + int max_len = PAGE_SIZE << trans_pcie->rx_page_order; + u32 offset = 0; + + if (WARN_ON(!rxb)) + return; + + dma_unmap_page(trans->dev, rxb->page_dma, max_len, DMA_FROM_DEVICE); + + while (offset + sizeof(u32) + sizeof(struct iwl_cmd_header) < max_len) { + struct iwl_rx_packet *pkt; + u16 sequence; + bool reclaim; + int index, cmd_index, len; + struct iwl_rx_cmd_buffer rxcb = { + ._offset = offset, + ._rx_page_order = trans_pcie->rx_page_order, + ._page = rxb->page, + ._page_stolen = false, + .truesize = max_len, + }; + + pkt = rxb_addr(&rxcb); + + if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID)) + break; + + IWL_DEBUG_RX(trans, + "cmd at offset %d: %s (0x%.2x, seq 0x%x)\n", + rxcb._offset, + get_cmd_string(trans_pcie, pkt->hdr.cmd), + pkt->hdr.cmd, le16_to_cpu(pkt->hdr.sequence)); + + 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); + + /* Reclaim a command buffer only if this packet is a response + * to a (driver-originated) command. + * If the packet (e.g. Rx frame) originated from uCode, + * there is no command buffer to reclaim. + * Ucode should set SEQ_RX_FRAME bit if ucode-originated, + * but apparently a few don't get set; catch them here. */ + reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME); + if (reclaim) { + int i; + + for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) { + if (trans_pcie->no_reclaim_cmds[i] == + pkt->hdr.cmd) { + reclaim = false; + break; + } + } + } + + sequence = le16_to_cpu(pkt->hdr.sequence); + index = SEQ_TO_INDEX(sequence); + cmd_index = get_cmd_index(&txq->q, index); + + iwl_op_mode_rx(trans->op_mode, &trans_pcie->napi, &rxcb); + + if (reclaim) { + kzfree(txq->entries[cmd_index].free_buf); + txq->entries[cmd_index].free_buf = NULL; + } + + /* + * After here, we should always check rxcb._page_stolen, + * if it is true then one of the handlers took the page. + */ + + if (reclaim) { + /* Invoke any callbacks, transfer the buffer to caller, + * and fire off the (possibly) blocking + * iwl_trans_send_cmd() + * as we reclaim the driver command queue */ + if (!rxcb._page_stolen) + iwl_pcie_hcmd_complete(trans, &rxcb); + else + IWL_WARN(trans, "Claim null rxb?\n"); + } + + page_stolen |= rxcb._page_stolen; + offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN); + } + + /* page was stolen from us -- free our reference */ + if (page_stolen) { + __free_pages(rxb->page, trans_pcie->rx_page_order); + rxb->page = NULL; + } + + /* Reuse the page if possible. For notification packets and + * SKBs that fail to Rx correctly, add them back into the + * rx_free list for reuse later. */ + if (rxb->page != NULL) { + rxb->page_dma = + dma_map_page(trans->dev, rxb->page, 0, + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); + if (dma_mapping_error(trans->dev, rxb->page_dma)) { + /* + * free the page(s) as well to not break + * the invariant that the items on the used + * list have no page(s) + */ + __free_pages(rxb->page, trans_pcie->rx_page_order); + rxb->page = NULL; + iwl_pcie_rx_reuse_rbd(trans, rxb, rxq, emergency); + } else { + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + } + } else + iwl_pcie_rx_reuse_rbd(trans, rxb, rxq, emergency); +} + +/* + * iwl_pcie_rx_handle - Main entry function for receiving responses from fw + */ +static void iwl_pcie_rx_handle(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + u32 r, i, j, count = 0; + bool emergency = false; + +restart: + spin_lock(&rxq->lock); + /* uCode's read index (stored in shared DRAM) indicates the last Rx + * buffer that the driver may process (last buffer filled by ucode). */ + r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF; + i = rxq->read; + + /* Rx interrupt, but nothing sent from uCode */ + if (i == r) + IWL_DEBUG_RX(trans, "HW = SW = %d\n", r); + + while (i != r) { + struct iwl_rx_mem_buffer *rxb; + + if (unlikely(rxq->used_count == RX_QUEUE_SIZE / 2)) + emergency = true; + + rxb = rxq->queue[i]; + rxq->queue[i] = NULL; + + IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d (%p)\n", + r, i, rxb); + iwl_pcie_rx_handle_rb(trans, rxb, emergency); + + i = (i + 1) & RX_QUEUE_MASK; + + /* If we have RX_CLAIM_REQ_ALLOC released rx buffers - + * try to claim the pre-allocated buffers from the allocator */ + if (rxq->used_count >= RX_CLAIM_REQ_ALLOC) { + struct iwl_rb_allocator *rba = &trans_pcie->rba; + struct iwl_rx_mem_buffer *out[RX_CLAIM_REQ_ALLOC]; + + if (rxq->used_count % RX_CLAIM_REQ_ALLOC == 0 && + !emergency) { + /* Add the remaining 6 empty RBDs + * for allocator use + */ + spin_lock(&rba->lock); + list_splice_tail_init(&rxq->rx_used, + &rba->rbd_empty); + spin_unlock(&rba->lock); + } + + /* If not ready - continue, will try to reclaim later. + * No need to reschedule work - allocator exits only on + * success */ + if (!iwl_pcie_rx_allocator_get(trans, out)) { + /* If success - then RX_CLAIM_REQ_ALLOC + * buffers were retrieved and should be added + * to free list */ + rxq->used_count -= RX_CLAIM_REQ_ALLOC; + for (j = 0; j < RX_CLAIM_REQ_ALLOC; j++) { + list_add_tail(&out[j]->list, + &rxq->rx_free); + rxq->free_count++; + } + } + } + if (emergency) { + count++; + if (count == 8) { + count = 0; + if (rxq->used_count < RX_QUEUE_SIZE / 3) + emergency = false; + spin_unlock(&rxq->lock); + iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC); + spin_lock(&rxq->lock); + } + } + /* handle restock for three cases, can be all of them at once: + * - we just pulled buffers from the allocator + * - we have 8+ unstolen pages accumulated + * - we are in emergency and allocated buffers + */ + if (rxq->free_count >= RX_CLAIM_REQ_ALLOC) { + rxq->read = i; + spin_unlock(&rxq->lock); + iwl_pcie_rxq_restock(trans); + goto restart; + } + } + + /* Backtrack one entry */ + rxq->read = i; + spin_unlock(&rxq->lock); + + /* + * handle a case where in emergency there are some unallocated RBDs. + * those RBDs are in the used list, but are not tracked by the queue's + * used_count which counts allocator owned RBDs. + * unallocated emergency RBDs must be allocated on exit, otherwise + * when called again the function may not be in emergency mode and + * they will be handed to the allocator with no tracking in the RBD + * allocator counters, which will lead to them never being claimed back + * by the queue. + * by allocating them here, they are now in the queue free list, and + * will be restocked by the next call of iwl_pcie_rxq_restock. + */ + if (unlikely(emergency && count)) + iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC); + + if (trans_pcie->napi.poll) + napi_gro_flush(&trans_pcie->napi, false); +} + +/* + * iwl_pcie_irq_handle_error - called for HW or SW error interrupt from card + */ +static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int i; + + /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ + if (trans->cfg->internal_wimax_coex && + !trans->cfg->apmg_not_supported && + (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & + APMS_CLK_VAL_MRB_FUNC_MODE) || + (iwl_read_prph(trans, APMG_PS_CTRL_REG) & + APMG_PS_CTRL_VAL_RESET_REQ))) { + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + iwl_op_mode_wimax_active(trans->op_mode); + wake_up(&trans_pcie->wait_command_queue); + return; + } + + iwl_pcie_dump_csr(trans); + iwl_dump_fh(trans, NULL); + + local_bh_disable(); + /* The STATUS_FW_ERROR bit is set in this function. This must happen + * before we wake up the command caller, to ensure a proper cleanup. */ + iwl_trans_fw_error(trans); + local_bh_enable(); + + for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) + del_timer(&trans_pcie->txq[i].stuck_timer); + + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + wake_up(&trans_pcie->wait_command_queue); +} + +static u32 iwl_pcie_int_cause_non_ict(struct iwl_trans *trans) +{ + u32 inta; + + lockdep_assert_held(&IWL_TRANS_GET_PCIE_TRANS(trans)->irq_lock); + + trace_iwlwifi_dev_irq(trans->dev); + + /* Discover which interrupts are active/pending */ + inta = iwl_read32(trans, CSR_INT); + + /* the thread will service interrupts and re-enable them */ + return inta; +} + +/* a device (PCI-E) page is 4096 bytes long */ +#define ICT_SHIFT 12 +#define ICT_SIZE (1 << ICT_SHIFT) +#define ICT_COUNT (ICT_SIZE / sizeof(u32)) + +/* interrupt handler using ict table, with this interrupt driver will + * stop using INTA register to get device's interrupt, reading this register + * is expensive, device will write interrupts in ICT dram table, increment + * index then will fire interrupt to driver, driver will OR all ICT table + * entries from current index up to table entry with 0 value. the result is + * the interrupt we need to service, driver will set the entries back to 0 and + * set index. + */ +static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 inta; + u32 val = 0; + u32 read; + + trace_iwlwifi_dev_irq(trans->dev); + + /* Ignore interrupt if there's nothing in NIC to service. + * This may be due to IRQ shared with another device, + * or due to sporadic interrupts thrown from our NIC. */ + read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); + trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read); + if (!read) + return 0; + + /* + * Collect all entries up to the first 0, starting from ict_index; + * note we already read at ict_index. + */ + do { + val |= read; + IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n", + trans_pcie->ict_index, read); + trans_pcie->ict_tbl[trans_pcie->ict_index] = 0; + trans_pcie->ict_index = + ((trans_pcie->ict_index + 1) & (ICT_COUNT - 1)); + + read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); + trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, + read); + } while (read); + + /* We should not get this value, just ignore it. */ + if (val == 0xffffffff) + val = 0; + + /* + * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit + * (bit 15 before shifting it to 31) to clear when using interrupt + * coalescing. fortunately, bits 18 and 19 stay set when this happens + * so we use them to decide on the real state of the Rx bit. + * In order words, bit 15 is set if bit 18 or bit 19 are set. + */ + if (val & 0xC0000) + val |= 0x8000; + + inta = (0xff & val) | ((0xff00 & val) << 16); + return inta; +} + +irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) +{ + struct iwl_trans *trans = dev_id; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct isr_statistics *isr_stats = &trans_pcie->isr_stats; + u32 inta = 0; + u32 handled = 0; + + lock_map_acquire(&trans->sync_cmd_lockdep_map); + + spin_lock(&trans_pcie->irq_lock); + + /* dram interrupt table not set yet, + * use legacy interrupt. + */ + if (likely(trans_pcie->use_ict)) + inta = iwl_pcie_int_cause_ict(trans); + else + inta = iwl_pcie_int_cause_non_ict(trans); + + if (iwl_have_debug_level(IWL_DL_ISR)) { + IWL_DEBUG_ISR(trans, + "ISR inta 0x%08x, enabled 0x%08x(sw), enabled(hw) 0x%08x, fh 0x%08x\n", + inta, trans_pcie->inta_mask, + iwl_read32(trans, CSR_INT_MASK), + iwl_read32(trans, CSR_FH_INT_STATUS)); + if (inta & (~trans_pcie->inta_mask)) + IWL_DEBUG_ISR(trans, + "We got a masked interrupt (0x%08x)\n", + inta & (~trans_pcie->inta_mask)); + } + + inta &= trans_pcie->inta_mask; + + /* + * Ignore interrupt if there's nothing in NIC to service. + * This may be due to IRQ shared with another device, + * or due to sporadic interrupts thrown from our NIC. + */ + if (unlikely(!inta)) { + IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); + /* + * Re-enable interrupts here since we don't + * have anything to service + */ + if (test_bit(STATUS_INT_ENABLED, &trans->status)) + iwl_enable_interrupts(trans); + spin_unlock(&trans_pcie->irq_lock); + lock_map_release(&trans->sync_cmd_lockdep_map); + return IRQ_NONE; + } + + if (unlikely(inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { + /* + * Hardware disappeared. It might have + * already raised an interrupt. + */ + IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta); + spin_unlock(&trans_pcie->irq_lock); + goto out; + } + + /* Ack/clear/reset pending uCode interrupts. + * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, + */ + /* There is a hardware bug in the interrupt mask function that some + * interrupts (i.e. CSR_INT_BIT_SCD) can still be generated even if + * they are disabled in the CSR_INT_MASK register. Furthermore the + * ICT interrupt handling mechanism has another bug that might cause + * these unmasked interrupts fail to be detected. We workaround the + * hardware bugs here by ACKing all the possible interrupts so that + * interrupt coalescing can still be achieved. + */ + iwl_write32(trans, CSR_INT, inta | ~trans_pcie->inta_mask); + + if (iwl_have_debug_level(IWL_DL_ISR)) + IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n", + inta, iwl_read32(trans, CSR_INT_MASK)); + + spin_unlock(&trans_pcie->irq_lock); + + /* Now service all interrupt bits discovered above. */ + if (inta & CSR_INT_BIT_HW_ERR) { + IWL_ERR(trans, "Hardware error detected. Restarting.\n"); + + /* Tell the device to stop sending interrupts */ + iwl_disable_interrupts(trans); + + isr_stats->hw++; + iwl_pcie_irq_handle_error(trans); + + handled |= CSR_INT_BIT_HW_ERR; + + goto out; + } + + if (iwl_have_debug_level(IWL_DL_ISR)) { + /* NIC fires this, but we don't use it, redundant with WAKEUP */ + if (inta & CSR_INT_BIT_SCD) { + IWL_DEBUG_ISR(trans, + "Scheduler finished to transmit the frame/frames.\n"); + isr_stats->sch++; + } + + /* Alive notification via Rx interrupt will do the real work */ + if (inta & CSR_INT_BIT_ALIVE) { + IWL_DEBUG_ISR(trans, "Alive interrupt\n"); + isr_stats->alive++; + } + } + + /* Safely ignore these bits for debug checks below */ + inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); + + /* HW RF KILL switch toggled */ + if (inta & CSR_INT_BIT_RF_KILL) { + bool hw_rfkill; + + hw_rfkill = iwl_is_rfkill_set(trans); + IWL_WARN(trans, "RF_KILL bit toggled to %s.\n", + hw_rfkill ? "disable radio" : "enable radio"); + + isr_stats->rfkill++; + + mutex_lock(&trans_pcie->mutex); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); + mutex_unlock(&trans_pcie->mutex); + if (hw_rfkill) { + set_bit(STATUS_RFKILL, &trans->status); + if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status)) + IWL_DEBUG_RF_KILL(trans, + "Rfkill while SYNC HCMD in flight\n"); + wake_up(&trans_pcie->wait_command_queue); + } else { + clear_bit(STATUS_RFKILL, &trans->status); + } + + handled |= CSR_INT_BIT_RF_KILL; + } + + /* Chip got too hot and stopped itself */ + if (inta & CSR_INT_BIT_CT_KILL) { + IWL_ERR(trans, "Microcode CT kill error detected.\n"); + isr_stats->ctkill++; + handled |= CSR_INT_BIT_CT_KILL; + } + + /* Error detected by uCode */ + if (inta & CSR_INT_BIT_SW_ERR) { + IWL_ERR(trans, "Microcode SW error detected. " + " Restarting 0x%X.\n", inta); + isr_stats->sw++; + iwl_pcie_irq_handle_error(trans); + handled |= CSR_INT_BIT_SW_ERR; + } + + /* uCode wakes up after power-down sleep */ + if (inta & CSR_INT_BIT_WAKEUP) { + IWL_DEBUG_ISR(trans, "Wakeup interrupt\n"); + iwl_pcie_rxq_check_wrptr(trans); + iwl_pcie_txq_check_wrptrs(trans); + + isr_stats->wakeup++; + + handled |= CSR_INT_BIT_WAKEUP; + } + + /* All uCode command responses, including Tx command responses, + * Rx "responses" (frame-received notification), and other + * notifications from uCode come through here*/ + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX | + CSR_INT_BIT_RX_PERIODIC)) { + IWL_DEBUG_ISR(trans, "Rx interrupt\n"); + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { + handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); + iwl_write32(trans, CSR_FH_INT_STATUS, + CSR_FH_INT_RX_MASK); + } + if (inta & CSR_INT_BIT_RX_PERIODIC) { + handled |= CSR_INT_BIT_RX_PERIODIC; + iwl_write32(trans, + CSR_INT, CSR_INT_BIT_RX_PERIODIC); + } + /* Sending RX interrupt require many steps to be done in the + * the device: + * 1- write interrupt to current index in ICT table. + * 2- dma RX frame. + * 3- update RX shared data to indicate last write index. + * 4- send interrupt. + * This could lead to RX race, driver could receive RX interrupt + * but the shared data changes does not reflect this; + * periodic interrupt will detect any dangling Rx activity. + */ + + /* Disable periodic interrupt; we use it as just a one-shot. */ + iwl_write8(trans, CSR_INT_PERIODIC_REG, + CSR_INT_PERIODIC_DIS); + + /* + * Enable periodic interrupt in 8 msec only if we received + * real RX interrupt (instead of just periodic int), to catch + * any dangling Rx interrupt. If it was just the periodic + * interrupt, there was no dangling Rx activity, and no need + * to extend the periodic interrupt; one-shot is enough. + */ + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) + iwl_write8(trans, CSR_INT_PERIODIC_REG, + CSR_INT_PERIODIC_ENA); + + isr_stats->rx++; + + local_bh_disable(); + iwl_pcie_rx_handle(trans); + local_bh_enable(); + } + + /* This "Tx" DMA channel is used only for loading uCode */ + if (inta & CSR_INT_BIT_FH_TX) { + iwl_write32(trans, CSR_FH_INT_STATUS, CSR_FH_INT_TX_MASK); + IWL_DEBUG_ISR(trans, "uCode load interrupt\n"); + isr_stats->tx++; + handled |= CSR_INT_BIT_FH_TX; + /* Wake up uCode load routine, now that load is complete */ + trans_pcie->ucode_write_complete = true; + wake_up(&trans_pcie->ucode_write_waitq); + } + + if (inta & ~handled) { + IWL_ERR(trans, "Unhandled INTA bits 0x%08x\n", inta & ~handled); + isr_stats->unhandled++; + } + + if (inta & ~(trans_pcie->inta_mask)) { + IWL_WARN(trans, "Disabled INTA bits 0x%08x were pending\n", + inta & ~trans_pcie->inta_mask); + } + + /* Re-enable all interrupts */ + /* only Re-enable if disabled by irq */ + if (test_bit(STATUS_INT_ENABLED, &trans->status)) + iwl_enable_interrupts(trans); + /* Re-enable RF_KILL if it occurred */ + else if (handled & CSR_INT_BIT_RF_KILL) + iwl_enable_rfkill_int(trans); + +out: + lock_map_release(&trans->sync_cmd_lockdep_map); + return IRQ_HANDLED; +} + +/****************************************************************************** + * + * ICT functions + * + ******************************************************************************/ + +/* Free dram table */ +void iwl_pcie_free_ict(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (trans_pcie->ict_tbl) { + dma_free_coherent(trans->dev, ICT_SIZE, + trans_pcie->ict_tbl, + trans_pcie->ict_tbl_dma); + trans_pcie->ict_tbl = NULL; + trans_pcie->ict_tbl_dma = 0; + } +} + +/* + * allocate dram shared table, it is an aligned memory + * block of ICT_SIZE. + * also reset all data related to ICT table interrupt. + */ +int iwl_pcie_alloc_ict(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + trans_pcie->ict_tbl = + dma_zalloc_coherent(trans->dev, ICT_SIZE, + &trans_pcie->ict_tbl_dma, + GFP_KERNEL); + if (!trans_pcie->ict_tbl) + return -ENOMEM; + + /* just an API sanity check ... it is guaranteed to be aligned */ + if (WARN_ON(trans_pcie->ict_tbl_dma & (ICT_SIZE - 1))) { + iwl_pcie_free_ict(trans); + return -EINVAL; + } + + IWL_DEBUG_ISR(trans, "ict dma addr %Lx ict vir addr %p\n", + (unsigned long long)trans_pcie->ict_tbl_dma, + trans_pcie->ict_tbl); + + return 0; +} + +/* Device is going up inform it about using ICT interrupt table, + * also we need to tell the driver to start using ICT interrupt. + */ +void iwl_pcie_reset_ict(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 val; + + if (!trans_pcie->ict_tbl) + return; + + spin_lock(&trans_pcie->irq_lock); + iwl_disable_interrupts(trans); + + memset(trans_pcie->ict_tbl, 0, ICT_SIZE); + + val = trans_pcie->ict_tbl_dma >> ICT_SHIFT; + + val |= CSR_DRAM_INT_TBL_ENABLE | + CSR_DRAM_INIT_TBL_WRAP_CHECK | + CSR_DRAM_INIT_TBL_WRITE_POINTER; + + IWL_DEBUG_ISR(trans, "CSR_DRAM_INT_TBL_REG =0x%x\n", val); + + iwl_write32(trans, CSR_DRAM_INT_TBL_REG, val); + trans_pcie->use_ict = true; + trans_pcie->ict_index = 0; + iwl_write32(trans, CSR_INT, trans_pcie->inta_mask); + iwl_enable_interrupts(trans); + spin_unlock(&trans_pcie->irq_lock); +} + +/* Device is going down disable ict interrupt usage */ +void iwl_pcie_disable_ict(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + spin_lock(&trans_pcie->irq_lock); + trans_pcie->use_ict = false; + spin_unlock(&trans_pcie->irq_lock); +} + +irqreturn_t iwl_pcie_isr(int irq, void *data) +{ + struct iwl_trans *trans = data; + + if (!trans) + return IRQ_NONE; + + /* Disable (but don't clear!) interrupts here to avoid + * back-to-back ISRs and sporadic interrupts from our NIC. + * If we have something to service, the tasklet will re-enable ints. + * If we *don't* have something, we'll re-enable before leaving here. + */ + iwl_write32(trans, CSR_INT_MASK, 0x00000000); + + return IRQ_WAKE_THREAD; +} diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c new file mode 100644 index 000000000000..90283453073c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -0,0 +1,2825 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iwl-drv.h" +#include "iwl-trans.h" +#include "iwl-csr.h" +#include "iwl-prph.h" +#include "iwl-scd.h" +#include "iwl-agn-hw.h" +#include "iwl-fw-error-dump.h" +#include "internal.h" +#include "iwl-fh.h" + +/* extended range in FW SRAM */ +#define IWL_FW_MEM_EXTENDED_START 0x40000 +#define IWL_FW_MEM_EXTENDED_END 0x57FFF + +static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (!trans_pcie->fw_mon_page) + return; + + dma_unmap_page(trans->dev, trans_pcie->fw_mon_phys, + trans_pcie->fw_mon_size, DMA_FROM_DEVICE); + __free_pages(trans_pcie->fw_mon_page, + get_order(trans_pcie->fw_mon_size)); + trans_pcie->fw_mon_page = NULL; + trans_pcie->fw_mon_phys = 0; + trans_pcie->fw_mon_size = 0; +} + +static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct page *page = NULL; + dma_addr_t phys; + u32 size = 0; + u8 power; + + if (!max_power) { + /* default max_power is maximum */ + max_power = 26; + } else { + max_power += 11; + } + + if (WARN(max_power > 26, + "External buffer size for monitor is too big %d, check the FW TLV\n", + max_power)) + return; + + if (trans_pcie->fw_mon_page) { + dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys, + trans_pcie->fw_mon_size, + DMA_FROM_DEVICE); + return; + } + + phys = 0; + for (power = max_power; power >= 11; power--) { + int order; + + size = BIT(power); + order = get_order(size); + page = alloc_pages(__GFP_COMP | __GFP_NOWARN | __GFP_ZERO, + order); + if (!page) + continue; + + phys = dma_map_page(trans->dev, page, 0, PAGE_SIZE << order, + DMA_FROM_DEVICE); + if (dma_mapping_error(trans->dev, phys)) { + __free_pages(page, order); + page = NULL; + continue; + } + IWL_INFO(trans, + "Allocated 0x%08x bytes (order %d) for firmware monitor.\n", + size, order); + break; + } + + if (WARN_ON_ONCE(!page)) + return; + + if (power != max_power) + IWL_ERR(trans, + "Sorry - debug buffer is only %luK while you requested %luK\n", + (unsigned long)BIT(power - 10), + (unsigned long)BIT(max_power - 10)); + + trans_pcie->fw_mon_page = page; + trans_pcie->fw_mon_phys = phys; + trans_pcie->fw_mon_size = size; +} + +static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) +{ + iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, + ((reg & 0x0000ffff) | (2 << 28))); + return iwl_read32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG); +} + +static void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val) +{ + iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG, val); + iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, + ((reg & 0x0000ffff) | (3 << 28))); +} + +static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux) +{ + if (trans->cfg->apmg_not_supported) + return; + + if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold)) + iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VAUX, + ~APMG_PS_CTRL_MSK_PWR_SRC); + else + iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, + ~APMG_PS_CTRL_MSK_PWR_SRC); +} + +/* PCI registers */ +#define PCI_CFG_RETRY_TIMEOUT 0x041 + +static void iwl_pcie_apm_config(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u16 lctl; + u16 cap; + + /* + * HW bug W/A for instability in PCIe bus L0S->L1 transition. + * Check if BIOS (or OS) enabled L1-ASPM on this device. + * If so (likely), disable L0S, so device moves directly L0->L1; + * costs negligible amount of power savings. + * If not (unlikely), enable L0S, so there is at least some + * power savings, even without L1. + */ + pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl); + if (lctl & PCI_EXP_LNKCTL_ASPM_L1) + iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); + else + iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); + trans->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S); + + pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_DEVCTL2, &cap); + trans->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN; + dev_info(trans->dev, "L1 %sabled - LTR %sabled\n", + (lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis", + trans->ltr_enabled ? "En" : "Dis"); +} + +/* + * Start up NIC's basic functionality after it has been reset + * (e.g. after platform boot, or shutdown via iwl_pcie_apm_stop()) + * NOTE: This does not load uCode nor start the embedded processor + */ +static int iwl_pcie_apm_init(struct iwl_trans *trans) +{ + int ret = 0; + IWL_DEBUG_INFO(trans, "Init card's basic functions\n"); + + /* + * Use "set_bit" below rather than "write", to preserve any hardware + * bits already set by default after reset. + */ + + /* Disable L0S exit timer (platform NMI Work/Around) */ + if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + + /* + * Disable L0s without affecting L1; + * don't wait for ICH L0s (ICH bug W/A) + */ + iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); + + /* Set FH wait threshold to maximum (HW error during stress W/A) */ + iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); + + /* + * Enable HAP INTA (interrupt from management bus) to + * wake device's PCI Express link L1a -> L0s + */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); + + iwl_pcie_apm_config(trans); + + /* Configure analog phase-lock-loop before activating to D0A */ + if (trans->cfg->base_params->pll_cfg_val) + iwl_set_bit(trans, CSR_ANA_PLL_CFG, + trans->cfg->base_params->pll_cfg_val); + + /* + * Set "initialization complete" bit to move adapter from + * D0U* --> D0A* (powered-up active) state. + */ + iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* + * Wait for clock stabilization; once stabilized, access to + * device-internal resources is supported, e.g. iwl_write_prph() + * and accesses to uCode SRAM. + */ + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); + if (ret < 0) { + IWL_DEBUG_INFO(trans, "Failed to init the card\n"); + 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. + * + * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" + * bits do not disable clocks. This preserves any hardware + * bits already set by default in "CLK_CTRL_REG" after reset. + */ + if (!trans->cfg->apmg_not_supported) { + iwl_write_prph(trans, APMG_CLK_EN_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + udelay(20); + + /* Disable L1-Active */ + iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + + /* Clear the interrupt in APMG if the NIC is in RFKILL */ + iwl_write_prph(trans, APMG_RTC_INT_STT_REG, + APMG_RTC_INT_STT_RFKILL); + } + + set_bit(STATUS_DEVICE_ENABLED, &trans->status); + +out: + return ret; +} + +/* + * Enable LP XTAL to avoid HW bug where device may consume much power if + * FW is not loaded after device reset. LP XTAL is disabled by default + * after device HW reset. Do it only if XTAL is fed by internal source. + * Configure device's "persistence" mode to avoid resetting XTAL again when + * SHRD_HW_RST occurs in S3. + */ +static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans) +{ + int ret; + u32 apmg_gp1_reg; + u32 apmg_xtal_cfg_reg; + u32 dl_cfg_reg; + + /* Force XTAL ON */ + __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_XTAL_ON); + + /* Reset entire device - do controller reset (results in SHRD_HW_RST) */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + udelay(10); + + /* + * Set "initialization complete" bit to move adapter from + * D0U* --> D0A* (powered-up active) state. + */ + iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* + * Wait for clock stabilization; once stabilized, access to + * device-internal resources is possible. + */ + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (WARN_ON(ret < 0)) { + IWL_ERR(trans, "Access time out - failed to enable LP XTAL\n"); + /* Release XTAL ON request */ + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_XTAL_ON); + return; + } + + /* + * Clear "disable persistence" to avoid LP XTAL resetting when + * SHRD_HW_RST is applied in S3. + */ + iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_PERSIST_DIS); + + /* + * Force APMG XTAL to be active to prevent its disabling by HW + * caused by APMG idle state. + */ + apmg_xtal_cfg_reg = iwl_trans_pcie_read_shr(trans, + SHR_APMG_XTAL_CFG_REG); + iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, + apmg_xtal_cfg_reg | + SHR_APMG_XTAL_CFG_XTAL_ON_REQ); + + /* + * Reset entire device again - do controller reset (results in + * SHRD_HW_RST). Turn MAC off before proceeding. + */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + udelay(10); + + /* Enable LP XTAL by indirect access through CSR */ + apmg_gp1_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_GP1_REG); + iwl_trans_pcie_write_shr(trans, SHR_APMG_GP1_REG, apmg_gp1_reg | + SHR_APMG_GP1_WF_XTAL_LP_EN | + SHR_APMG_GP1_CHICKEN_BIT_SELECT); + + /* Clear delay line clock power up */ + dl_cfg_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_DL_CFG_REG); + iwl_trans_pcie_write_shr(trans, SHR_APMG_DL_CFG_REG, dl_cfg_reg & + ~SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP); + + /* + * Enable persistence mode to avoid LP XTAL resetting when + * SHRD_HW_RST is applied in S3. + */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PERSIST_MODE); + + /* + * Clear "initialization complete" bit to move adapter from + * D0A* (powered-up Active) --> D0U* (Uninitialized) state. + */ + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* Activates XTAL resources monitor */ + __iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG, + CSR_MONITOR_XTAL_RESOURCES); + + /* Release XTAL ON request */ + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_XTAL_ON); + udelay(10); + + /* Release APMG XTAL */ + iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, + apmg_xtal_cfg_reg & + ~SHR_APMG_XTAL_CFG_XTAL_ON_REQ); +} + +static int iwl_pcie_apm_stop_master(struct iwl_trans *trans) +{ + int ret = 0; + + /* stop device's busmaster DMA activity */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); + + ret = iwl_poll_bit(trans, CSR_RESET, + CSR_RESET_REG_FLAG_MASTER_DISABLED, + CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); + if (ret < 0) + IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n"); + + IWL_DEBUG_INFO(trans, "stop master\n"); + + return ret; +} + +static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave) +{ + IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); + + if (op_mode_leave) { + if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) + iwl_pcie_apm_init(trans); + + /* inform ME that we are leaving */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) + iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_WAKE_ME); + else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { + iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PREPARE | + CSR_HW_IF_CONFIG_REG_ENABLE_PME); + mdelay(1); + iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + } + mdelay(5); + } + + clear_bit(STATUS_DEVICE_ENABLED, &trans->status); + + /* Stop device's DMA activity */ + iwl_pcie_apm_stop_master(trans); + + if (trans->cfg->lp_xtal_workaround) { + iwl_pcie_apm_lp_xtal_enable(trans); + return; + } + + /* Reset the entire device */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + udelay(10); + + /* + * Clear "initialization complete" bit to move adapter from + * D0A* (powered-up Active) --> D0U* (Uninitialized) state. + */ + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); +} + +static int iwl_pcie_nic_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + /* nic_init */ + spin_lock(&trans_pcie->irq_lock); + iwl_pcie_apm_init(trans); + + spin_unlock(&trans_pcie->irq_lock); + + iwl_pcie_set_pwr(trans, false); + + iwl_op_mode_nic_config(trans->op_mode); + + /* Allocate the RX queue, or reset if it is already allocated */ + iwl_pcie_rx_init(trans); + + /* Allocate or reset and init all Tx and Command queues */ + if (iwl_pcie_tx_init(trans)) + return -ENOMEM; + + if (trans->cfg->base_params->shadow_reg_enable) { + /* enable shadow regs in HW */ + iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); + IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n"); + } + + return 0; +} + +#define HW_READY_TIMEOUT (50) + +/* Note: returns poll_bit return value, which is >= 0 if success */ +static int iwl_pcie_set_hw_ready(struct iwl_trans *trans) +{ + int ret; + + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); + + /* See if we got it */ + ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + HW_READY_TIMEOUT); + + if (ret >= 0) + iwl_set_bit(trans, CSR_MBOX_SET_REG, CSR_MBOX_SET_REG_OS_ALIVE); + + IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : ""); + return ret; +} + +/* Note: returns standard 0/-ERROR code */ +static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) +{ + int ret; + int t = 0; + int iter; + + IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); + + ret = iwl_pcie_set_hw_ready(trans); + /* If the card is ready, exit 0 */ + if (ret >= 0) + return 0; + + iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + msleep(1); + + for (iter = 0; iter < 10; iter++) { + /* If HW is not ready, prepare the conditions to check again */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PREPARE); + + do { + ret = iwl_pcie_set_hw_ready(trans); + if (ret >= 0) + return 0; + + usleep_range(200, 1000); + t += 200; + } while (t < 150000); + msleep(25); + } + + IWL_ERR(trans, "Couldn't prepare the card\n"); + + return ret; +} + +/* + * ucode + */ +static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr, + dma_addr_t phy_addr, u32 byte_cnt) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + + trans_pcie->ucode_write_complete = false; + + iwl_write_direct32(trans, + FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); + + iwl_write_direct32(trans, + FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), + dst_addr); + + iwl_write_direct32(trans, + FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), + phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); + + iwl_write_direct32(trans, + FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), + (iwl_get_dma_hi_addr(phy_addr) + << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); + + iwl_write_direct32(trans, + FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), + 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | + 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | + FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); + + iwl_write_direct32(trans, + FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | + FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); + + ret = wait_event_timeout(trans_pcie->ucode_write_waitq, + trans_pcie->ucode_write_complete, 5 * HZ); + if (!ret) { + IWL_ERR(trans, "Failed to load firmware chunk!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, + const struct fw_desc *section) +{ + u8 *v_addr; + dma_addr_t p_addr; + u32 offset, chunk_sz = min_t(u32, FH_MEM_TB_MAX_LENGTH, section->len); + int ret = 0; + + IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", + section_num); + + v_addr = dma_alloc_coherent(trans->dev, chunk_sz, &p_addr, + GFP_KERNEL | __GFP_NOWARN); + if (!v_addr) { + IWL_DEBUG_INFO(trans, "Falling back to small chunks of DMA\n"); + chunk_sz = PAGE_SIZE; + v_addr = dma_alloc_coherent(trans->dev, chunk_sz, + &p_addr, GFP_KERNEL); + if (!v_addr) + return -ENOMEM; + } + + for (offset = 0; offset < section->len; offset += chunk_sz) { + u32 copy_size, dst_addr; + bool extended_addr = false; + + copy_size = min_t(u32, chunk_sz, section->len - offset); + dst_addr = section->offset + offset; + + if (dst_addr >= IWL_FW_MEM_EXTENDED_START && + dst_addr <= IWL_FW_MEM_EXTENDED_END) + extended_addr = true; + + if (extended_addr) + iwl_set_bits_prph(trans, LMPM_CHICK, + LMPM_CHICK_EXTENDED_ADDR_SPACE); + + memcpy(v_addr, (u8 *)section->data + offset, copy_size); + ret = iwl_pcie_load_firmware_chunk(trans, dst_addr, p_addr, + copy_size); + + if (extended_addr) + iwl_clear_bits_prph(trans, LMPM_CHICK, + LMPM_CHICK_EXTENDED_ADDR_SPACE); + + if (ret) { + IWL_ERR(trans, + "Could not load the [%d] uCode section\n", + section_num); + break; + } + } + + dma_free_coherent(trans->dev, chunk_sz, v_addr, p_addr); + return ret; +} + +/* + * Driver Takes the ownership on secure machine before FW load + * and prevent race with the BT load. + * W/A for ROM bug. (should be remove in the next Si step) + */ +static int iwl_pcie_rsa_race_bug_wa(struct iwl_trans *trans) +{ + u32 val, loop = 1000; + + /* + * Check the RSA semaphore is accessible. + * If the HW isn't locked and the rsa semaphore isn't accessible, + * we are in trouble. + */ + val = iwl_read_prph(trans, PREG_AUX_BUS_WPROT_0); + if (val & (BIT(1) | BIT(17))) { + IWL_INFO(trans, + "can't access the RSA semaphore it is write protected\n"); + return 0; + } + + /* take ownership on the AUX IF */ + iwl_write_prph(trans, WFPM_CTRL_REG, WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK); + iwl_write_prph(trans, AUX_MISC_MASTER1_EN, AUX_MISC_MASTER1_EN_SBE_MSK); + + do { + iwl_write_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS, 0x1); + val = iwl_read_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS); + if (val == 0x1) { + iwl_write_prph(trans, RSA_ENABLE, 0); + return 0; + } + + udelay(10); + loop--; + } while (loop > 0); + + IWL_ERR(trans, "Failed to take ownership on secure machine\n"); + return -EIO; +} + +static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans, + const struct fw_img *image, + int cpu, + int *first_ucode_section) +{ + int shift_param; + int i, ret = 0, sec_num = 0x1; + u32 val, last_read_idx = 0; + + if (cpu == 1) { + shift_param = 0; + *first_ucode_section = 0; + } else { + shift_param = 16; + (*first_ucode_section)++; + } + + for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) { + last_read_idx = i; + + /* + * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between + * CPU1 to CPU2. + * PAGING_SEPARATOR_SECTION delimiter - separate between + * CPU2 non paged to CPU2 paging sec. + */ + if (!image->sec[i].data || + image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION || + image->sec[i].offset == PAGING_SEPARATOR_SECTION) { + IWL_DEBUG_FW(trans, + "Break since Data not valid or Empty section, sec = %d\n", + i); + break; + } + + ret = iwl_pcie_load_section(trans, i, &image->sec[i]); + if (ret) + return ret; + + /* Notify the ucode of the loaded section number and status */ + val = iwl_read_direct32(trans, FH_UCODE_LOAD_STATUS); + val = val | (sec_num << shift_param); + iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, val); + sec_num = (sec_num << 1) | 0x1; + } + + *first_ucode_section = last_read_idx; + + if (cpu == 1) + iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFF); + else + iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF); + + return 0; +} + +static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans, + const struct fw_img *image, + int cpu, + int *first_ucode_section) +{ + int shift_param; + int i, ret = 0; + u32 last_read_idx = 0; + + if (cpu == 1) { + shift_param = 0; + *first_ucode_section = 0; + } else { + shift_param = 16; + (*first_ucode_section)++; + } + + for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) { + last_read_idx = i; + + /* + * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between + * CPU1 to CPU2. + * PAGING_SEPARATOR_SECTION delimiter - separate between + * CPU2 non paged to CPU2 paging sec. + */ + if (!image->sec[i].data || + image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION || + image->sec[i].offset == PAGING_SEPARATOR_SECTION) { + IWL_DEBUG_FW(trans, + "Break since Data not valid or Empty section, sec = %d\n", + i); + break; + } + + ret = iwl_pcie_load_section(trans, i, &image->sec[i]); + if (ret) + return ret; + } + + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + iwl_set_bits_prph(trans, + CSR_UCODE_LOAD_STATUS_ADDR, + (LMPM_CPU_UCODE_LOADING_COMPLETED | + LMPM_CPU_HDRS_LOADING_COMPLETED | + LMPM_CPU_UCODE_LOADING_STARTED) << + shift_param); + + *first_ucode_section = last_read_idx; + + return 0; +} + +static void iwl_pcie_apply_destination(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv; + int i; + + if (dest->version) + IWL_ERR(trans, + "DBG DEST version is %d - expect issues\n", + dest->version); + + IWL_INFO(trans, "Applying debug destination %s\n", + get_fw_dbg_mode_string(dest->monitor_mode)); + + if (dest->monitor_mode == EXTERNAL_MODE) + iwl_pcie_alloc_fw_monitor(trans, dest->size_power); + else + IWL_WARN(trans, "PCI should have external buffer debug\n"); + + for (i = 0; i < trans->dbg_dest_reg_num; i++) { + u32 addr = le32_to_cpu(dest->reg_ops[i].addr); + u32 val = le32_to_cpu(dest->reg_ops[i].val); + + switch (dest->reg_ops[i].op) { + case CSR_ASSIGN: + iwl_write32(trans, addr, val); + break; + case CSR_SETBIT: + iwl_set_bit(trans, addr, BIT(val)); + break; + case CSR_CLEARBIT: + iwl_clear_bit(trans, addr, BIT(val)); + break; + case PRPH_ASSIGN: + iwl_write_prph(trans, addr, val); + break; + case PRPH_SETBIT: + iwl_set_bits_prph(trans, addr, BIT(val)); + break; + case PRPH_CLEARBIT: + iwl_clear_bits_prph(trans, addr, BIT(val)); + break; + case PRPH_BLOCKBIT: + if (iwl_read_prph(trans, addr) & BIT(val)) { + IWL_ERR(trans, + "BIT(%u) in address 0x%x is 1, stopping FW configuration\n", + val, addr); + goto monitor; + } + break; + default: + IWL_ERR(trans, "FW debug - unknown OP %d\n", + dest->reg_ops[i].op); + break; + } + } + +monitor: + if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) { + iwl_write_prph(trans, le32_to_cpu(dest->base_reg), + trans_pcie->fw_mon_phys >> dest->base_shift); + iwl_write_prph(trans, le32_to_cpu(dest->end_reg), + (trans_pcie->fw_mon_phys + + trans_pcie->fw_mon_size) >> dest->end_shift); + } +} + +static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, + const struct fw_img *image) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret = 0; + int first_ucode_section; + + IWL_DEBUG_FW(trans, "working with %s CPU\n", + image->is_dual_cpus ? "Dual" : "Single"); + + /* load to FW the binary non secured sections of CPU1 */ + ret = iwl_pcie_load_cpu_sections(trans, image, 1, &first_ucode_section); + if (ret) + return ret; + + if (image->is_dual_cpus) { + /* set CPU2 header address */ + iwl_write_prph(trans, + LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR, + LMPM_SECURE_CPU2_HDR_MEM_SPACE); + + /* load to FW the binary sections of CPU2 */ + ret = iwl_pcie_load_cpu_sections(trans, image, 2, + &first_ucode_section); + if (ret) + return ret; + } + + /* supported for 7000 only for the moment */ + if (iwlwifi_mod_params.fw_monitor && + trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { + iwl_pcie_alloc_fw_monitor(trans, 0); + + if (trans_pcie->fw_mon_size) { + iwl_write_prph(trans, MON_BUFF_BASE_ADDR, + trans_pcie->fw_mon_phys >> 4); + iwl_write_prph(trans, MON_BUFF_END_ADDR, + (trans_pcie->fw_mon_phys + + trans_pcie->fw_mon_size) >> 4); + } + } else if (trans->dbg_dest_tlv) { + iwl_pcie_apply_destination(trans); + } + + /* release CPU reset */ + iwl_write32(trans, CSR_RESET, 0); + + return 0; +} + +static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans, + const struct fw_img *image) +{ + int ret = 0; + int first_ucode_section; + + IWL_DEBUG_FW(trans, "working with %s CPU\n", + image->is_dual_cpus ? "Dual" : "Single"); + + if (trans->dbg_dest_tlv) + iwl_pcie_apply_destination(trans); + + /* TODO: remove in the next Si step */ + ret = iwl_pcie_rsa_race_bug_wa(trans); + if (ret) + return ret; + + /* configure the ucode to be ready to get the secured image */ + /* release CPU reset */ + iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); + + /* load to FW the binary Secured sections of CPU1 */ + ret = iwl_pcie_load_cpu_sections_8000(trans, image, 1, + &first_ucode_section); + if (ret) + return ret; + + /* load to FW the binary sections of CPU2 */ + return iwl_pcie_load_cpu_sections_8000(trans, image, 2, + &first_ucode_section); +} + +static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, + const struct fw_img *fw, bool run_in_rfkill) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + bool hw_rfkill; + int ret; + + mutex_lock(&trans_pcie->mutex); + + /* Someone called stop_device, don't try to start_fw */ + if (trans_pcie->is_down) { + IWL_WARN(trans, + "Can't start_fw since the HW hasn't been started\n"); + ret = EIO; + goto out; + } + + /* This may fail if AMT took ownership of the device */ + if (iwl_pcie_prepare_card_hw(trans)) { + IWL_WARN(trans, "Exit HW not ready\n"); + ret = -EIO; + goto out; + } + + iwl_enable_rfkill_int(trans); + + /* If platform's RF_KILL switch is NOT set to KILL */ + hw_rfkill = iwl_is_rfkill_set(trans); + if (hw_rfkill) + set_bit(STATUS_RFKILL, &trans->status); + else + clear_bit(STATUS_RFKILL, &trans->status); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); + if (hw_rfkill && !run_in_rfkill) { + ret = -ERFKILL; + goto out; + } + + iwl_write32(trans, CSR_INT, 0xFFFFFFFF); + + ret = iwl_pcie_nic_init(trans); + if (ret) { + IWL_ERR(trans, "Unable to init nic\n"); + goto out; + } + + /* make sure rfkill handshake bits are cleared */ + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + /* clear (again), then enable host interrupts */ + iwl_write32(trans, CSR_INT, 0xFFFFFFFF); + iwl_enable_interrupts(trans); + + /* really make sure rfkill handshake bits are cleared */ + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + + /* Load the given image to the HW */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + ret = iwl_pcie_load_given_ucode_8000(trans, fw); + else + ret = iwl_pcie_load_given_ucode(trans, fw); + +out: + mutex_unlock(&trans_pcie->mutex); + return ret; +} + +static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) +{ + iwl_pcie_reset_ict(trans); + iwl_pcie_tx_start(trans, scd_addr); +} + +static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + bool hw_rfkill, was_hw_rfkill; + + lockdep_assert_held(&trans_pcie->mutex); + + if (trans_pcie->is_down) + return; + + trans_pcie->is_down = true; + + was_hw_rfkill = iwl_is_rfkill_set(trans); + + /* tell the device to stop sending interrupts */ + spin_lock(&trans_pcie->irq_lock); + iwl_disable_interrupts(trans); + spin_unlock(&trans_pcie->irq_lock); + + /* device going down, Stop using ICT table */ + iwl_pcie_disable_ict(trans); + + /* + * If a HW restart happens during firmware loading, + * then the firmware loading might call this function + * and later it might be called again due to the + * restart. So don't process again if the device is + * already dead. + */ + if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) { + IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n"); + iwl_pcie_tx_stop(trans); + iwl_pcie_rx_stop(trans); + + /* Power-down device's busmaster DMA clocks */ + if (!trans->cfg->apmg_not_supported) { + iwl_write_prph(trans, APMG_CLK_DIS_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + udelay(5); + } + } + + /* Make sure (redundant) we've released our request to stay awake */ + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + /* Stop the device, and put it in low power state */ + iwl_pcie_apm_stop(trans, false); + + /* stop and reset the on-board processor */ + iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + udelay(20); + + /* + * Upon stop, the APM issues an interrupt if HW RF kill is set. + * This is a bug in certain verions of the hardware. + * Certain devices also keep sending HW RF kill interrupt all + * the time, unless the interrupt is ACKed even if the interrupt + * should be masked. Re-ACK all the interrupts here. + */ + spin_lock(&trans_pcie->irq_lock); + iwl_disable_interrupts(trans); + spin_unlock(&trans_pcie->irq_lock); + + + /* clear all status bits */ + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + clear_bit(STATUS_INT_ENABLED, &trans->status); + clear_bit(STATUS_TPOWER_PMI, &trans->status); + clear_bit(STATUS_RFKILL, &trans->status); + + /* + * Even if we stop the HW, we still want the RF kill + * interrupt + */ + iwl_enable_rfkill_int(trans); + + /* + * Check again since the RF kill state may have changed while + * all the interrupts were disabled, in this case we couldn't + * receive the RF kill interrupt and update the state in the + * op_mode. + * Don't call the op_mode if the rkfill state hasn't changed. + * This allows the op_mode to call stop_device from the rfkill + * notification without endless recursion. Under very rare + * circumstances, we might have a small recursion if the rfkill + * state changed exactly now while we were called from stop_device. + * This is very unlikely but can happen and is supported. + */ + hw_rfkill = iwl_is_rfkill_set(trans); + if (hw_rfkill) + set_bit(STATUS_RFKILL, &trans->status); + else + clear_bit(STATUS_RFKILL, &trans->status); + if (hw_rfkill != was_hw_rfkill) + iwl_trans_pcie_rf_kill(trans, hw_rfkill); + + /* re-take ownership to prevent other users from stealing the deivce */ + iwl_pcie_prepare_card_hw(trans); +} + +static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + mutex_lock(&trans_pcie->mutex); + _iwl_trans_pcie_stop_device(trans, low_power); + mutex_unlock(&trans_pcie->mutex); +} + +void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state) +{ + struct iwl_trans_pcie __maybe_unused *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + + lockdep_assert_held(&trans_pcie->mutex); + + if (iwl_op_mode_hw_rf_kill(trans->op_mode, state)) + _iwl_trans_pcie_stop_device(trans, true); +} + +static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (trans->wowlan_d0i3) { + /* Enable persistence mode to avoid reset */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PERSIST_MODE); + } + + iwl_disable_interrupts(trans); + + /* + * in testing mode, the host stays awake and the + * hardware won't be reset (not even partially) + */ + if (test) + return; + + iwl_pcie_disable_ict(trans); + + synchronize_irq(trans_pcie->pci_dev->irq); + + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + if (!trans->wowlan_d0i3) { + /* + * reset TX queues -- some of their registers reset during S3 + * so if we don't reset everything here the D3 image would try + * to execute some invalid memory upon resume + */ + iwl_trans_pcie_tx_reset(trans); + } + + iwl_pcie_set_pwr(trans, true); +} + +static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, + enum iwl_d3_status *status, + bool test) +{ + u32 val; + int ret; + + if (test) { + iwl_enable_interrupts(trans); + *status = IWL_D3_STATUS_ALIVE; + return 0; + } + + /* + * Also enables interrupts - none will happen as the device doesn't + * know we're waking it up, only when the opmode actually tells it + * after this call. + */ + iwl_pcie_reset_ict(trans); + + iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + udelay(2); + + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (ret < 0) { + IWL_ERR(trans, "Failed to resume the device (mac ready)\n"); + return ret; + } + + iwl_pcie_set_pwr(trans, false); + + if (trans->wowlan_d0i3) { + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + } else { + iwl_trans_pcie_tx_reset(trans); + + ret = iwl_pcie_rx_init(trans); + if (ret) { + IWL_ERR(trans, + "Failed to resume the device (RX reset)\n"); + return ret; + } + } + + val = iwl_read32(trans, CSR_RESET); + if (val & CSR_RESET_REG_FLAG_NEVO_RESET) + *status = IWL_D3_STATUS_RESET; + else + *status = IWL_D3_STATUS_ALIVE; + + return 0; +} + +static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + bool hw_rfkill; + int err; + + lockdep_assert_held(&trans_pcie->mutex); + + err = iwl_pcie_prepare_card_hw(trans); + if (err) { + IWL_ERR(trans, "Error while preparing HW: %d\n", err); + return err; + } + + /* Reset the entire device */ + iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + usleep_range(10, 15); + + iwl_pcie_apm_init(trans); + + /* From now on, the op_mode will be kept updated about RF kill state */ + iwl_enable_rfkill_int(trans); + + /* Set is_down to false here so that...*/ + trans_pcie->is_down = false; + + hw_rfkill = iwl_is_rfkill_set(trans); + if (hw_rfkill) + set_bit(STATUS_RFKILL, &trans->status); + else + clear_bit(STATUS_RFKILL, &trans->status); + /* ... rfkill can call stop_device and set it false if needed */ + iwl_trans_pcie_rf_kill(trans, hw_rfkill); + + return 0; +} + +static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + + mutex_lock(&trans_pcie->mutex); + ret = _iwl_trans_pcie_start_hw(trans, low_power); + mutex_unlock(&trans_pcie->mutex); + + return ret; +} + +static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + mutex_lock(&trans_pcie->mutex); + + /* disable interrupts - don't enable HW RF kill interrupt */ + spin_lock(&trans_pcie->irq_lock); + iwl_disable_interrupts(trans); + spin_unlock(&trans_pcie->irq_lock); + + iwl_pcie_apm_stop(trans, true); + + spin_lock(&trans_pcie->irq_lock); + iwl_disable_interrupts(trans); + spin_unlock(&trans_pcie->irq_lock); + + iwl_pcie_disable_ict(trans); + + mutex_unlock(&trans_pcie->mutex); + + synchronize_irq(trans_pcie->pci_dev->irq); +} + +static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) +{ + writeb(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); +} + +static void iwl_trans_pcie_write32(struct iwl_trans *trans, u32 ofs, u32 val) +{ + writel(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); +} + +static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs) +{ + return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); +} + +static u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg) +{ + iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR, + ((reg & 0x000FFFFF) | (3 << 24))); + return iwl_trans_pcie_read32(trans, HBUS_TARG_PRPH_RDAT); +} + +static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, + u32 val) +{ + iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WADDR, + ((addr & 0x000FFFFF) | (3 << 24))); + iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val); +} + +static int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget) +{ + WARN_ON(1); + return 0; +} + +static void iwl_trans_pcie_configure(struct iwl_trans *trans, + const struct iwl_trans_config *trans_cfg) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + trans_pcie->cmd_queue = trans_cfg->cmd_queue; + trans_pcie->cmd_fifo = trans_cfg->cmd_fifo; + trans_pcie->cmd_q_wdg_timeout = trans_cfg->cmd_q_wdg_timeout; + if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) + trans_pcie->n_no_reclaim_cmds = 0; + else + trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds; + if (trans_pcie->n_no_reclaim_cmds) + memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds, + trans_pcie->n_no_reclaim_cmds * sizeof(u8)); + + trans_pcie->rx_buf_size_8k = trans_cfg->rx_buf_size_8k; + if (trans_pcie->rx_buf_size_8k) + trans_pcie->rx_page_order = get_order(8 * 1024); + else + trans_pcie->rx_page_order = get_order(4 * 1024); + + trans_pcie->wide_cmd_header = trans_cfg->wide_cmd_header; + trans_pcie->command_names = trans_cfg->command_names; + trans_pcie->bc_table_dword = trans_cfg->bc_table_dword; + trans_pcie->scd_set_active = trans_cfg->scd_set_active; + + /* init ref_count to 1 (should be cleared when ucode is loaded) */ + trans_pcie->ref_count = 1; + + /* Initialize NAPI here - it should be before registering to mac80211 + * in the opmode but after the HW struct is allocated. + * As this function may be called again in some corner cases don't + * do anything if NAPI was already initialized. + */ + if (!trans_pcie->napi.poll) { + init_dummy_netdev(&trans_pcie->napi_dev); + netif_napi_add(&trans_pcie->napi_dev, &trans_pcie->napi, + iwl_pcie_dummy_napi_poll, 64); + } +} + +void iwl_trans_pcie_free(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + synchronize_irq(trans_pcie->pci_dev->irq); + + iwl_pcie_tx_free(trans); + iwl_pcie_rx_free(trans); + + free_irq(trans_pcie->pci_dev->irq, trans); + iwl_pcie_free_ict(trans); + + pci_disable_msi(trans_pcie->pci_dev); + iounmap(trans_pcie->hw_base); + pci_release_regions(trans_pcie->pci_dev); + pci_disable_device(trans_pcie->pci_dev); + + if (trans_pcie->napi.poll) + netif_napi_del(&trans_pcie->napi); + + iwl_pcie_free_fw_monitor(trans); + + iwl_trans_free(trans); +} + +static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) +{ + if (state) + set_bit(STATUS_TPOWER_PMI, &trans->status); + else + clear_bit(STATUS_TPOWER_PMI, &trans->status); +} + +static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, + unsigned long *flags) +{ + int ret; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + spin_lock_irqsave(&trans_pcie->reg_lock, *flags); + + if (trans_pcie->cmd_hold_nic_awake) + goto out; + + /* this bit wakes up the NIC */ + __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + udelay(2); + + /* + * These bits say the device is running, and should keep running for + * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), + * but they do not indicate that embedded SRAM is restored yet; + * 3945 and 4965 have volatile SRAM, and must save/restore contents + * to/from host DRAM when sleeping/waking for power-saving. + * Each direction takes approximately 1/4 millisecond; with this + * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a + * series of register accesses are expected (e.g. reading Event Log), + * to keep device from sleeping. + * + * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that + * SRAM is okay/restored. We don't check that here because this call + * is just for hardware register access; but GP1 MAC_SLEEP check is a + * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). + * + * 5000 series and later (including 1000 series) have non-volatile SRAM, + * and do not save/restore SRAM when power cycling. + */ + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, + (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | + CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); + if (unlikely(ret < 0)) { + iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); + if (!silent) { + u32 val = iwl_read32(trans, CSR_GP_CNTRL); + WARN_ONCE(1, + "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n", + val); + spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags); + return false; + } + } + +out: + /* + * Fool sparse by faking we release the lock - sparse will + * track nic_access anyway. + */ + __release(&trans_pcie->reg_lock); + return true; +} + +static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans, + unsigned long *flags) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + lockdep_assert_held(&trans_pcie->reg_lock); + + /* + * Fool sparse by faking we acquiring the lock - sparse will + * track nic_access anyway. + */ + __acquire(&trans_pcie->reg_lock); + + if (trans_pcie->cmd_hold_nic_awake) + goto out; + + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + /* + * Above we read the CSR_GP_CNTRL register, which will flush + * any previous writes, but we need the write that clears the + * MAC_ACCESS_REQ bit to be performed before any other writes + * scheduled on different CPUs (after we drop reg_lock). + */ + mmiowb(); +out: + spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags); +} + +static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, + void *buf, int dwords) +{ + unsigned long flags; + int offs, ret = 0; + u32 *vals = buf; + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr); + for (offs = 0; offs < dwords; offs++) + vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT); + iwl_trans_release_nic_access(trans, &flags); + } else { + ret = -EBUSY; + } + return ret; +} + +static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, + const void *buf, int dwords) +{ + unsigned long flags; + int offs, ret = 0; + const u32 *vals = buf; + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr); + for (offs = 0; offs < dwords; offs++) + iwl_write32(trans, HBUS_TARG_MEM_WDAT, + vals ? vals[offs] : 0); + iwl_trans_release_nic_access(trans, &flags); + } else { + ret = -EBUSY; + } + return ret; +} + +static void iwl_trans_pcie_freeze_txq_timer(struct iwl_trans *trans, + unsigned long txqs, + bool freeze) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int queue; + + for_each_set_bit(queue, &txqs, BITS_PER_LONG) { + struct iwl_txq *txq = &trans_pcie->txq[queue]; + unsigned long now; + + spin_lock_bh(&txq->lock); + + now = jiffies; + + if (txq->frozen == freeze) + goto next_queue; + + IWL_DEBUG_TX_QUEUES(trans, "%s TXQ %d\n", + freeze ? "Freezing" : "Waking", queue); + + txq->frozen = freeze; + + if (txq->q.read_ptr == txq->q.write_ptr) + goto next_queue; + + if (freeze) { + if (unlikely(time_after(now, + txq->stuck_timer.expires))) { + /* + * The timer should have fired, maybe it is + * spinning right now on the lock. + */ + goto next_queue; + } + /* remember how long until the timer fires */ + txq->frozen_expiry_remainder = + txq->stuck_timer.expires - now; + del_timer(&txq->stuck_timer); + goto next_queue; + } + + /* + * Wake a non-empty queue -> arm timer with the + * remainder before it froze + */ + mod_timer(&txq->stuck_timer, + now + txq->frozen_expiry_remainder); + +next_queue: + spin_unlock_bh(&txq->lock); + } +} + +#define IWL_FLUSH_WAIT_MS 2000 + +static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq; + struct iwl_queue *q; + int cnt; + unsigned long now = jiffies; + u32 scd_sram_addr; + u8 buf[16]; + int ret = 0; + + /* waiting for all the tx frames complete might take a while */ + for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { + u8 wr_ptr; + + if (cnt == trans_pcie->cmd_queue) + continue; + if (!test_bit(cnt, trans_pcie->queue_used)) + continue; + if (!(BIT(cnt) & txq_bm)) + continue; + + IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", cnt); + txq = &trans_pcie->txq[cnt]; + q = &txq->q; + wr_ptr = ACCESS_ONCE(q->write_ptr); + + while (q->read_ptr != ACCESS_ONCE(q->write_ptr) && + !time_after(jiffies, + now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) { + u8 write_ptr = ACCESS_ONCE(q->write_ptr); + + if (WARN_ONCE(wr_ptr != write_ptr, + "WR pointer moved while flushing %d -> %d\n", + wr_ptr, write_ptr)) + return -ETIMEDOUT; + msleep(1); + } + + if (q->read_ptr != q->write_ptr) { + IWL_ERR(trans, + "fail to flush all tx fifo queues Q %d\n", cnt); + ret = -ETIMEDOUT; + break; + } + IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", cnt); + } + + if (!ret) + return 0; + + IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", + txq->q.read_ptr, txq->q.write_ptr); + + scd_sram_addr = trans_pcie->scd_base_addr + + SCD_TX_STTS_QUEUE_OFFSET(txq->q.id); + iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); + + iwl_print_hex_error(trans, buf, sizeof(buf)); + + for (cnt = 0; cnt < FH_TCSR_CHNL_NUM; cnt++) + IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", cnt, + iwl_read_direct32(trans, FH_TX_TRB_REG(cnt))); + + for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { + u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(cnt)); + u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; + bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); + u32 tbl_dw = + iwl_trans_read_mem32(trans, trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE(cnt)); + + if (cnt & 0x1) + tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; + else + tbl_dw = tbl_dw & 0x0000FFFF; + + IWL_ERR(trans, + "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", + cnt, active ? "" : "in", fifo, tbl_dw, + iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) & + (TFD_QUEUE_SIZE_MAX - 1), + iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt))); + } + + return ret; +} + +static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, + u32 mask, u32 value) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + spin_lock_irqsave(&trans_pcie->reg_lock, flags); + __iwl_trans_pcie_set_bits_mask(trans, reg, mask, value); + spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); +} + +void iwl_trans_pcie_ref(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + if (iwlwifi_mod_params.d0i3_disable) + return; + + spin_lock_irqsave(&trans_pcie->ref_lock, flags); + IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); + trans_pcie->ref_count++; + spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); +} + +void iwl_trans_pcie_unref(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + if (iwlwifi_mod_params.d0i3_disable) + return; + + spin_lock_irqsave(&trans_pcie->ref_lock, flags); + IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); + if (WARN_ON_ONCE(trans_pcie->ref_count == 0)) { + spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); + return; + } + trans_pcie->ref_count--; + spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); +} + +static const char *get_csr_string(int cmd) +{ +#define IWL_CMD(x) case x: return #x + switch (cmd) { + IWL_CMD(CSR_HW_IF_CONFIG_REG); + IWL_CMD(CSR_INT_COALESCING); + IWL_CMD(CSR_INT); + IWL_CMD(CSR_INT_MASK); + IWL_CMD(CSR_FH_INT_STATUS); + IWL_CMD(CSR_GPIO_IN); + IWL_CMD(CSR_RESET); + IWL_CMD(CSR_GP_CNTRL); + IWL_CMD(CSR_HW_REV); + IWL_CMD(CSR_EEPROM_REG); + IWL_CMD(CSR_EEPROM_GP); + IWL_CMD(CSR_OTP_GP_REG); + IWL_CMD(CSR_GIO_REG); + IWL_CMD(CSR_GP_UCODE_REG); + IWL_CMD(CSR_GP_DRIVER_REG); + IWL_CMD(CSR_UCODE_DRV_GP1); + IWL_CMD(CSR_UCODE_DRV_GP2); + IWL_CMD(CSR_LED_REG); + IWL_CMD(CSR_DRAM_INT_TBL_REG); + IWL_CMD(CSR_GIO_CHICKEN_BITS); + IWL_CMD(CSR_ANA_PLL_CFG); + IWL_CMD(CSR_HW_REV_WA_REG); + IWL_CMD(CSR_MONITOR_STATUS_REG); + IWL_CMD(CSR_DBG_HPET_MEM_REG); + default: + return "UNKNOWN"; + } +#undef IWL_CMD +} + +void iwl_pcie_dump_csr(struct iwl_trans *trans) +{ + int i; + static const u32 csr_tbl[] = { + CSR_HW_IF_CONFIG_REG, + CSR_INT_COALESCING, + CSR_INT, + CSR_INT_MASK, + CSR_FH_INT_STATUS, + CSR_GPIO_IN, + CSR_RESET, + CSR_GP_CNTRL, + CSR_HW_REV, + CSR_EEPROM_REG, + CSR_EEPROM_GP, + CSR_OTP_GP_REG, + CSR_GIO_REG, + CSR_GP_UCODE_REG, + CSR_GP_DRIVER_REG, + CSR_UCODE_DRV_GP1, + CSR_UCODE_DRV_GP2, + CSR_LED_REG, + CSR_DRAM_INT_TBL_REG, + CSR_GIO_CHICKEN_BITS, + CSR_ANA_PLL_CFG, + CSR_MONITOR_STATUS_REG, + CSR_HW_REV_WA_REG, + CSR_DBG_HPET_MEM_REG + }; + IWL_ERR(trans, "CSR values:\n"); + IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is " + "CSR_INT_PERIODIC_REG)\n"); + for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) { + IWL_ERR(trans, " %25s: 0X%08x\n", + get_csr_string(csr_tbl[i]), + iwl_read32(trans, csr_tbl[i])); + } +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +/* create and remove of files */ +#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, trans, \ + &iwl_dbgfs_##name##_ops)) \ + goto err; \ +} while (0) + +/* file operation */ +#define DEBUGFS_READ_FILE_OPS(name) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_WRITE_FILE_OPS(name) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = iwl_dbgfs_##name##_write, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = iwl_dbgfs_##name##_write, \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq; + struct iwl_queue *q; + char *buf; + int pos = 0; + int cnt; + int ret; + size_t bufsz; + + bufsz = sizeof(char) * 75 * trans->cfg->base_params->num_of_queues; + + if (!trans_pcie->txq) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { + txq = &trans_pcie->txq[cnt]; + q = &txq->q; + pos += scnprintf(buf + pos, bufsz - pos, + "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d frozen=%d%s\n", + cnt, q->read_ptr, q->write_ptr, + !!test_bit(cnt, trans_pcie->queue_used), + !!test_bit(cnt, trans_pcie->queue_stopped), + txq->need_update, txq->frozen, + (cnt == trans_pcie->cmd_queue ? " HCMD" : "")); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", + rxq->read); + pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", + rxq->write); + pos += scnprintf(buf + pos, bufsz - pos, "write_actual: %u\n", + rxq->write_actual); + pos += scnprintf(buf + pos, bufsz - pos, "need_update: %d\n", + rxq->need_update); + pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", + rxq->free_count); + if (rxq->rb_stts) { + pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", + le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF); + } else { + pos += scnprintf(buf + pos, bufsz - pos, + "closed_rb_num: Not Allocated\n"); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_interrupt_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct isr_statistics *isr_stats = &trans_pcie->isr_stats; + + int pos = 0; + char *buf; + int bufsz = 24 * 64; /* 24 items * 64 char per item */ + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, + "Interrupt Statistics Report:\n"); + + pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", + isr_stats->hw); + pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", + isr_stats->sw); + if (isr_stats->sw || isr_stats->hw) { + pos += scnprintf(buf + pos, bufsz - pos, + "\tLast Restarting Code: 0x%X\n", + isr_stats->err_code); + } +#ifdef CONFIG_IWLWIFI_DEBUG + pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", + isr_stats->sch); + pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", + isr_stats->alive); +#endif + pos += scnprintf(buf + pos, bufsz - pos, + "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill); + + pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", + isr_stats->ctkill); + + pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", + isr_stats->wakeup); + + pos += scnprintf(buf + pos, bufsz - pos, + "Rx command responses:\t\t %u\n", isr_stats->rx); + + pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", + isr_stats->tx); + + pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", + isr_stats->unhandled); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_interrupt_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct isr_statistics *isr_stats = &trans_pcie->isr_stats; + + char buf[8]; + int buf_size; + u32 reset_flag; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &reset_flag) != 1) + return -EFAULT; + if (reset_flag == 0) + memset(isr_stats, 0, sizeof(*isr_stats)); + + return count; +} + +static ssize_t iwl_dbgfs_csr_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + char buf[8]; + int buf_size; + int csr; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &csr) != 1) + return -EFAULT; + + iwl_pcie_dump_csr(trans); + + return count; +} + +static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + char *buf = NULL; + ssize_t ret; + + ret = iwl_dump_fh(trans, &buf); + if (ret < 0) + return ret; + if (!buf) + return -EINVAL; + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); + return ret; +} + +DEBUGFS_READ_WRITE_FILE_OPS(interrupt); +DEBUGFS_READ_FILE_OPS(fh_reg); +DEBUGFS_READ_FILE_OPS(rx_queue); +DEBUGFS_READ_FILE_OPS(tx_queue); +DEBUGFS_WRITE_FILE_OPS(csr); + +/* + * Create the debugfs files and directories + * + */ +static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, + struct dentry *dir) +{ + DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR); + DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR); + DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(csr, dir, S_IWUSR); + DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR); + return 0; + +err: + IWL_ERR(trans, "failed to create the trans debugfs entry\n"); + return -ENOMEM; +} +#else +static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, + struct dentry *dir) +{ + return 0; +} +#endif /*CONFIG_IWLWIFI_DEBUGFS */ + +static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd) +{ + u32 cmdlen = 0; + int i; + + for (i = 0; i < IWL_NUM_OF_TBS; i++) + cmdlen += iwl_pcie_tfd_tb_get_len(tfd, i); + + return cmdlen; +} + +static const struct { + u32 start, end; +} iwl_prph_dump_addr[] = { + { .start = 0x00a00000, .end = 0x00a00000 }, + { .start = 0x00a0000c, .end = 0x00a00024 }, + { .start = 0x00a0002c, .end = 0x00a0003c }, + { .start = 0x00a00410, .end = 0x00a00418 }, + { .start = 0x00a00420, .end = 0x00a00420 }, + { .start = 0x00a00428, .end = 0x00a00428 }, + { .start = 0x00a00430, .end = 0x00a0043c }, + { .start = 0x00a00444, .end = 0x00a00444 }, + { .start = 0x00a004c0, .end = 0x00a004cc }, + { .start = 0x00a004d8, .end = 0x00a004d8 }, + { .start = 0x00a004e0, .end = 0x00a004f0 }, + { .start = 0x00a00840, .end = 0x00a00840 }, + { .start = 0x00a00850, .end = 0x00a00858 }, + { .start = 0x00a01004, .end = 0x00a01008 }, + { .start = 0x00a01010, .end = 0x00a01010 }, + { .start = 0x00a01018, .end = 0x00a01018 }, + { .start = 0x00a01024, .end = 0x00a01024 }, + { .start = 0x00a0102c, .end = 0x00a01034 }, + { .start = 0x00a0103c, .end = 0x00a01040 }, + { .start = 0x00a01048, .end = 0x00a01094 }, + { .start = 0x00a01c00, .end = 0x00a01c20 }, + { .start = 0x00a01c58, .end = 0x00a01c58 }, + { .start = 0x00a01c7c, .end = 0x00a01c7c }, + { .start = 0x00a01c28, .end = 0x00a01c54 }, + { .start = 0x00a01c5c, .end = 0x00a01c5c }, + { .start = 0x00a01c60, .end = 0x00a01cdc }, + { .start = 0x00a01ce0, .end = 0x00a01d0c }, + { .start = 0x00a01d18, .end = 0x00a01d20 }, + { .start = 0x00a01d2c, .end = 0x00a01d30 }, + { .start = 0x00a01d40, .end = 0x00a01d5c }, + { .start = 0x00a01d80, .end = 0x00a01d80 }, + { .start = 0x00a01d98, .end = 0x00a01d9c }, + { .start = 0x00a01da8, .end = 0x00a01da8 }, + { .start = 0x00a01db8, .end = 0x00a01df4 }, + { .start = 0x00a01dc0, .end = 0x00a01dfc }, + { .start = 0x00a01e00, .end = 0x00a01e2c }, + { .start = 0x00a01e40, .end = 0x00a01e60 }, + { .start = 0x00a01e68, .end = 0x00a01e6c }, + { .start = 0x00a01e74, .end = 0x00a01e74 }, + { .start = 0x00a01e84, .end = 0x00a01e90 }, + { .start = 0x00a01e9c, .end = 0x00a01ec4 }, + { .start = 0x00a01ed0, .end = 0x00a01ee0 }, + { .start = 0x00a01f00, .end = 0x00a01f1c }, + { .start = 0x00a01f44, .end = 0x00a01ffc }, + { .start = 0x00a02000, .end = 0x00a02048 }, + { .start = 0x00a02068, .end = 0x00a020f0 }, + { .start = 0x00a02100, .end = 0x00a02118 }, + { .start = 0x00a02140, .end = 0x00a0214c }, + { .start = 0x00a02168, .end = 0x00a0218c }, + { .start = 0x00a021c0, .end = 0x00a021c0 }, + { .start = 0x00a02400, .end = 0x00a02410 }, + { .start = 0x00a02418, .end = 0x00a02420 }, + { .start = 0x00a02428, .end = 0x00a0242c }, + { .start = 0x00a02434, .end = 0x00a02434 }, + { .start = 0x00a02440, .end = 0x00a02460 }, + { .start = 0x00a02468, .end = 0x00a024b0 }, + { .start = 0x00a024c8, .end = 0x00a024cc }, + { .start = 0x00a02500, .end = 0x00a02504 }, + { .start = 0x00a0250c, .end = 0x00a02510 }, + { .start = 0x00a02540, .end = 0x00a02554 }, + { .start = 0x00a02580, .end = 0x00a025f4 }, + { .start = 0x00a02600, .end = 0x00a0260c }, + { .start = 0x00a02648, .end = 0x00a02650 }, + { .start = 0x00a02680, .end = 0x00a02680 }, + { .start = 0x00a026c0, .end = 0x00a026d0 }, + { .start = 0x00a02700, .end = 0x00a0270c }, + { .start = 0x00a02804, .end = 0x00a02804 }, + { .start = 0x00a02818, .end = 0x00a0281c }, + { .start = 0x00a02c00, .end = 0x00a02db4 }, + { .start = 0x00a02df4, .end = 0x00a02fb0 }, + { .start = 0x00a03000, .end = 0x00a03014 }, + { .start = 0x00a0301c, .end = 0x00a0302c }, + { .start = 0x00a03034, .end = 0x00a03038 }, + { .start = 0x00a03040, .end = 0x00a03048 }, + { .start = 0x00a03060, .end = 0x00a03068 }, + { .start = 0x00a03070, .end = 0x00a03074 }, + { .start = 0x00a0307c, .end = 0x00a0307c }, + { .start = 0x00a03080, .end = 0x00a03084 }, + { .start = 0x00a0308c, .end = 0x00a03090 }, + { .start = 0x00a03098, .end = 0x00a03098 }, + { .start = 0x00a030a0, .end = 0x00a030a0 }, + { .start = 0x00a030a8, .end = 0x00a030b4 }, + { .start = 0x00a030bc, .end = 0x00a030bc }, + { .start = 0x00a030c0, .end = 0x00a0312c }, + { .start = 0x00a03c00, .end = 0x00a03c5c }, + { .start = 0x00a04400, .end = 0x00a04454 }, + { .start = 0x00a04460, .end = 0x00a04474 }, + { .start = 0x00a044c0, .end = 0x00a044ec }, + { .start = 0x00a04500, .end = 0x00a04504 }, + { .start = 0x00a04510, .end = 0x00a04538 }, + { .start = 0x00a04540, .end = 0x00a04548 }, + { .start = 0x00a04560, .end = 0x00a0457c }, + { .start = 0x00a04590, .end = 0x00a04598 }, + { .start = 0x00a045c0, .end = 0x00a045f4 }, +}; + +static u32 iwl_trans_pcie_dump_prph(struct iwl_trans *trans, + struct iwl_fw_error_dump_data **data) +{ + struct iwl_fw_error_dump_prph *prph; + unsigned long flags; + u32 prph_len = 0, i; + + if (!iwl_trans_grab_nic_access(trans, false, &flags)) + return 0; + + for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4; + int reg; + __le32 *val; + + prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk; + + (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); + (*data)->len = cpu_to_le32(sizeof(*prph) + + num_bytes_in_chunk); + prph = (void *)(*data)->data; + prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); + val = (void *)prph->data; + + for (reg = iwl_prph_dump_addr[i].start; + reg <= iwl_prph_dump_addr[i].end; + reg += 4) + *val++ = cpu_to_le32(iwl_trans_pcie_read_prph(trans, + reg)); + *data = iwl_fw_error_next_data(*data); + } + + iwl_trans_release_nic_access(trans, &flags); + + return prph_len; +} + +static u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans, + struct iwl_fw_error_dump_data **data, + int allocated_rb_nums) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int max_len = PAGE_SIZE << trans_pcie->rx_page_order; + struct iwl_rxq *rxq = &trans_pcie->rxq; + u32 i, r, j, rb_len = 0; + + spin_lock(&rxq->lock); + + r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF; + + for (i = rxq->read, j = 0; + i != r && j < allocated_rb_nums; + i = (i + 1) & RX_QUEUE_MASK, j++) { + struct iwl_rx_mem_buffer *rxb = rxq->queue[i]; + struct iwl_fw_error_dump_rb *rb; + + dma_unmap_page(trans->dev, rxb->page_dma, max_len, + DMA_FROM_DEVICE); + + rb_len += sizeof(**data) + sizeof(*rb) + max_len; + + (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RB); + (*data)->len = cpu_to_le32(sizeof(*rb) + max_len); + rb = (void *)(*data)->data; + rb->index = cpu_to_le32(i); + memcpy(rb->data, page_address(rxb->page), max_len); + /* remap the page for the free benefit */ + rxb->page_dma = dma_map_page(trans->dev, rxb->page, 0, + max_len, + DMA_FROM_DEVICE); + + *data = iwl_fw_error_next_data(*data); + } + + spin_unlock(&rxq->lock); + + return rb_len; +} +#define IWL_CSR_TO_DUMP (0x250) + +static u32 iwl_trans_pcie_dump_csr(struct iwl_trans *trans, + struct iwl_fw_error_dump_data **data) +{ + u32 csr_len = sizeof(**data) + IWL_CSR_TO_DUMP; + __le32 *val; + int i; + + (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_CSR); + (*data)->len = cpu_to_le32(IWL_CSR_TO_DUMP); + val = (void *)(*data)->data; + + for (i = 0; i < IWL_CSR_TO_DUMP; i += 4) + *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i)); + + *data = iwl_fw_error_next_data(*data); + + return csr_len; +} + +static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans, + struct iwl_fw_error_dump_data **data) +{ + u32 fh_regs_len = FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND; + unsigned long flags; + __le32 *val; + int i; + + if (!iwl_trans_grab_nic_access(trans, false, &flags)) + return 0; + + (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FH_REGS); + (*data)->len = cpu_to_le32(fh_regs_len); + val = (void *)(*data)->data; + + for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; i += sizeof(u32)) + *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i)); + + iwl_trans_release_nic_access(trans, &flags); + + *data = iwl_fw_error_next_data(*data); + + return sizeof(**data) + fh_regs_len; +} + +static u32 +iwl_trans_pci_dump_marbh_monitor(struct iwl_trans *trans, + struct iwl_fw_error_dump_fw_mon *fw_mon_data, + u32 monitor_len) +{ + u32 buf_size_in_dwords = (monitor_len >> 2); + u32 *buffer = (u32 *)fw_mon_data->data; + unsigned long flags; + u32 i; + + if (!iwl_trans_grab_nic_access(trans, false, &flags)) + return 0; + + __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x1); + for (i = 0; i < buf_size_in_dwords; i++) + buffer[i] = __iwl_read_prph(trans, MON_DMARB_RD_DATA_ADDR); + __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x0); + + iwl_trans_release_nic_access(trans, &flags); + + return monitor_len; +} + +static u32 +iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, + struct iwl_fw_error_dump_data **data, + u32 monitor_len) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 len = 0; + + if ((trans_pcie->fw_mon_page && + trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) || + trans->dbg_dest_tlv) { + struct iwl_fw_error_dump_fw_mon *fw_mon_data; + u32 base, write_ptr, wrap_cnt; + + /* If there was a dest TLV - use the values from there */ + if (trans->dbg_dest_tlv) { + write_ptr = + le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg); + wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count); + base = le32_to_cpu(trans->dbg_dest_tlv->base_reg); + } else { + base = MON_BUFF_BASE_ADDR; + write_ptr = MON_BUFF_WRPTR; + wrap_cnt = MON_BUFF_CYCLE_CNT; + } + + (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR); + fw_mon_data = (void *)(*data)->data; + fw_mon_data->fw_mon_wr_ptr = + cpu_to_le32(iwl_read_prph(trans, write_ptr)); + fw_mon_data->fw_mon_cycle_cnt = + cpu_to_le32(iwl_read_prph(trans, wrap_cnt)); + fw_mon_data->fw_mon_base_ptr = + cpu_to_le32(iwl_read_prph(trans, base)); + + len += sizeof(**data) + sizeof(*fw_mon_data); + if (trans_pcie->fw_mon_page) { + /* + * The firmware is now asserted, it won't write anything + * to the buffer. CPU can take ownership to fetch the + * data. The buffer will be handed back to the device + * before the firmware will be restarted. + */ + dma_sync_single_for_cpu(trans->dev, + trans_pcie->fw_mon_phys, + trans_pcie->fw_mon_size, + DMA_FROM_DEVICE); + memcpy(fw_mon_data->data, + page_address(trans_pcie->fw_mon_page), + trans_pcie->fw_mon_size); + + monitor_len = trans_pcie->fw_mon_size; + } else if (trans->dbg_dest_tlv->monitor_mode == SMEM_MODE) { + /* + * Update pointers to reflect actual values after + * shifting + */ + base = iwl_read_prph(trans, base) << + trans->dbg_dest_tlv->base_shift; + iwl_trans_read_mem(trans, base, fw_mon_data->data, + monitor_len / sizeof(u32)); + } else if (trans->dbg_dest_tlv->monitor_mode == MARBH_MODE) { + monitor_len = + iwl_trans_pci_dump_marbh_monitor(trans, + fw_mon_data, + monitor_len); + } else { + /* Didn't match anything - output no monitor data */ + monitor_len = 0; + } + + len += monitor_len; + (*data)->len = cpu_to_le32(monitor_len + sizeof(*fw_mon_data)); + } + + return len; +} + +static struct iwl_trans_dump_data +*iwl_trans_pcie_dump_data(struct iwl_trans *trans, + struct iwl_fw_dbg_trigger_tlv *trigger) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_fw_error_dump_data *data; + struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_fw_error_dump_txcmd *txcmd; + struct iwl_trans_dump_data *dump_data; + u32 len, num_rbs; + u32 monitor_len; + int i, ptr; + bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status); + + /* transport dump header */ + len = sizeof(*dump_data); + + /* host commands */ + len += sizeof(*data) + + cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE); + + /* FW monitor */ + if (trans_pcie->fw_mon_page) { + len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) + + trans_pcie->fw_mon_size; + monitor_len = trans_pcie->fw_mon_size; + } else if (trans->dbg_dest_tlv) { + u32 base, end; + + base = le32_to_cpu(trans->dbg_dest_tlv->base_reg); + end = le32_to_cpu(trans->dbg_dest_tlv->end_reg); + + base = iwl_read_prph(trans, base) << + trans->dbg_dest_tlv->base_shift; + end = iwl_read_prph(trans, end) << + trans->dbg_dest_tlv->end_shift; + + /* Make "end" point to the actual end */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 || + trans->dbg_dest_tlv->monitor_mode == MARBH_MODE) + end += (1 << trans->dbg_dest_tlv->end_shift); + monitor_len = end - base; + len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) + + monitor_len; + } else { + monitor_len = 0; + } + + if (trigger && (trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)) { + dump_data = vzalloc(len); + if (!dump_data) + return NULL; + + data = (void *)dump_data->data; + len = iwl_trans_pcie_dump_monitor(trans, &data, monitor_len); + dump_data->len = len; + + return dump_data; + } + + /* CSR registers */ + len += sizeof(*data) + IWL_CSR_TO_DUMP; + + /* PRPH registers */ + for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4; + + len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_prph) + + num_bytes_in_chunk; + } + + /* FH registers */ + len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND); + + if (dump_rbs) { + /* RBs */ + num_rbs = le16_to_cpu(ACCESS_ONCE( + trans_pcie->rxq.rb_stts->closed_rb_num)) + & 0x0FFF; + num_rbs = (num_rbs - trans_pcie->rxq.read) & RX_QUEUE_MASK; + len += num_rbs * (sizeof(*data) + + sizeof(struct iwl_fw_error_dump_rb) + + (PAGE_SIZE << trans_pcie->rx_page_order)); + } + + dump_data = vzalloc(len); + if (!dump_data) + return NULL; + + len = 0; + data = (void *)dump_data->data; + data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD); + txcmd = (void *)data->data; + spin_lock_bh(&cmdq->lock); + ptr = cmdq->q.write_ptr; + for (i = 0; i < cmdq->q.n_window; i++) { + u8 idx = get_cmd_index(&cmdq->q, ptr); + u32 caplen, cmdlen; + + cmdlen = iwl_trans_pcie_get_cmdlen(&cmdq->tfds[ptr]); + caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen); + + if (cmdlen) { + len += sizeof(*txcmd) + caplen; + txcmd->cmdlen = cpu_to_le32(cmdlen); + txcmd->caplen = cpu_to_le32(caplen); + memcpy(txcmd->data, cmdq->entries[idx].cmd, caplen); + txcmd = (void *)((u8 *)txcmd->data + caplen); + } + + ptr = iwl_queue_dec_wrap(ptr); + } + spin_unlock_bh(&cmdq->lock); + + data->len = cpu_to_le32(len); + len += sizeof(*data); + data = iwl_fw_error_next_data(data); + + len += iwl_trans_pcie_dump_prph(trans, &data); + len += iwl_trans_pcie_dump_csr(trans, &data); + len += iwl_trans_pcie_fh_regs_dump(trans, &data); + if (dump_rbs) + len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs); + + len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len); + + dump_data->len = len; + + return dump_data; +} + +static const struct iwl_trans_ops trans_ops_pcie = { + .start_hw = iwl_trans_pcie_start_hw, + .op_mode_leave = iwl_trans_pcie_op_mode_leave, + .fw_alive = iwl_trans_pcie_fw_alive, + .start_fw = iwl_trans_pcie_start_fw, + .stop_device = iwl_trans_pcie_stop_device, + + .d3_suspend = iwl_trans_pcie_d3_suspend, + .d3_resume = iwl_trans_pcie_d3_resume, + + .send_cmd = iwl_trans_pcie_send_hcmd, + + .tx = iwl_trans_pcie_tx, + .reclaim = iwl_trans_pcie_reclaim, + + .txq_disable = iwl_trans_pcie_txq_disable, + .txq_enable = iwl_trans_pcie_txq_enable, + + .dbgfs_register = iwl_trans_pcie_dbgfs_register, + + .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, + .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, + + .write8 = iwl_trans_pcie_write8, + .write32 = iwl_trans_pcie_write32, + .read32 = iwl_trans_pcie_read32, + .read_prph = iwl_trans_pcie_read_prph, + .write_prph = iwl_trans_pcie_write_prph, + .read_mem = iwl_trans_pcie_read_mem, + .write_mem = iwl_trans_pcie_write_mem, + .configure = iwl_trans_pcie_configure, + .set_pmi = iwl_trans_pcie_set_pmi, + .grab_nic_access = iwl_trans_pcie_grab_nic_access, + .release_nic_access = iwl_trans_pcie_release_nic_access, + .set_bits_mask = iwl_trans_pcie_set_bits_mask, + + .ref = iwl_trans_pcie_ref, + .unref = iwl_trans_pcie_unref, + + .dump_data = iwl_trans_pcie_dump_data, +}; + +struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, + const struct pci_device_id *ent, + const struct iwl_cfg *cfg) +{ + struct iwl_trans_pcie *trans_pcie; + struct iwl_trans *trans; + u16 pci_cmd; + int ret; + + trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), + &pdev->dev, cfg, &trans_ops_pcie, 0); + if (!trans) + return ERR_PTR(-ENOMEM); + + trans->max_skb_frags = IWL_PCIE_MAX_FRAGS; + + trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + trans_pcie->trans = trans; + spin_lock_init(&trans_pcie->irq_lock); + spin_lock_init(&trans_pcie->reg_lock); + spin_lock_init(&trans_pcie->ref_lock); + mutex_init(&trans_pcie->mutex); + init_waitqueue_head(&trans_pcie->ucode_write_waitq); + + ret = pci_enable_device(pdev); + if (ret) + goto out_no_pci; + + if (!cfg->base_params->pcie_l1_allowed) { + /* + * W/A - seems to solve weird behavior. We need to remove this + * if we don't want to stay in L1 all the time. This wastes a + * lot of power. + */ + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | + PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM); + } + + pci_set_master(pdev); + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); + if (!ret) + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36)); + if (ret) { + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (!ret) + ret = pci_set_consistent_dma_mask(pdev, + DMA_BIT_MASK(32)); + /* both attempts failed: */ + if (ret) { + dev_err(&pdev->dev, "No suitable DMA available\n"); + goto out_pci_disable_device; + } + } + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret) { + dev_err(&pdev->dev, "pci_request_regions failed\n"); + goto out_pci_disable_device; + } + + trans_pcie->hw_base = pci_ioremap_bar(pdev, 0); + if (!trans_pcie->hw_base) { + dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); + ret = -ENODEV; + goto out_pci_release_regions; + } + + /* We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state */ + pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); + + trans->dev = &pdev->dev; + trans_pcie->pci_dev = pdev; + iwl_disable_interrupts(trans); + + ret = pci_enable_msi(pdev); + if (ret) { + dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", ret); + /* enable rfkill interrupt: hw bug w/a */ + pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); + if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { + pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); + } + } + + trans->hw_rev = iwl_read32(trans, CSR_HW_REV); + /* + * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have + * changed, and now the revision step also includes bit 0-1 (no more + * "dash" value). To keep hw_rev backwards compatible - we'll store it + * in the old format. + */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { + unsigned long flags; + + trans->hw_rev = (trans->hw_rev & 0xfff0) | + (CSR_HW_REV_STEP(trans->hw_rev << 2) << 2); + + ret = iwl_pcie_prepare_card_hw(trans); + if (ret) { + IWL_WARN(trans, "Exit HW not ready\n"); + goto out_pci_disable_msi; + } + + /* + * in-order to recognize C step driver should read chip version + * id located at the AUX bus MISC address space. + */ + iwl_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + udelay(2); + + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (ret < 0) { + IWL_DEBUG_INFO(trans, "Failed to wake up the nic\n"); + goto out_pci_disable_msi; + } + + if (iwl_trans_grab_nic_access(trans, false, &flags)) { + u32 hw_step; + + hw_step = __iwl_read_prph(trans, WFPM_CTRL_REG); + hw_step |= ENABLE_WFPM; + __iwl_write_prph(trans, WFPM_CTRL_REG, hw_step); + hw_step = __iwl_read_prph(trans, AUX_MISC_REG); + hw_step = (hw_step >> HW_STEP_LOCATION_BITS) & 0xF; + if (hw_step == 0x3) + trans->hw_rev = (trans->hw_rev & 0xFFFFFFF3) | + (SILICON_C_STEP << 2); + iwl_trans_release_nic_access(trans, &flags); + } + } + + trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; + snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), + "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); + + /* Initialize the wait queue for commands */ + init_waitqueue_head(&trans_pcie->wait_command_queue); + + ret = iwl_pcie_alloc_ict(trans); + if (ret) + goto out_pci_disable_msi; + + ret = request_threaded_irq(pdev->irq, iwl_pcie_isr, + iwl_pcie_irq_handler, + IRQF_SHARED, DRV_NAME, trans); + if (ret) { + IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq); + goto out_free_ict; + } + + trans_pcie->inta_mask = CSR_INI_SET_MASK; + trans->d0i3_mode = IWL_D0I3_MODE_ON_SUSPEND; + + return trans; + +out_free_ict: + iwl_pcie_free_ict(trans); +out_pci_disable_msi: + pci_disable_msi(pdev); +out_pci_release_regions: + pci_release_regions(pdev); +out_pci_disable_device: + pci_disable_device(pdev); +out_no_pci: + iwl_trans_free(trans); + return ERR_PTR(ret); +} diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c new file mode 100644 index 000000000000..a8c8a4a7420b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -0,0 +1,1988 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include + +#include "iwl-debug.h" +#include "iwl-csr.h" +#include "iwl-prph.h" +#include "iwl-io.h" +#include "iwl-scd.h" +#include "iwl-op-mode.h" +#include "internal.h" +/* FIXME: need to abstract out TX command (once we know what it looks like) */ +#include "dvm/commands.h" + +#define IWL_TX_CRC_SIZE 4 +#define IWL_TX_DELIMITER_SIZE 4 + +/*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** + * DMA services + * + * Theory of operation + * + * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer + * of buffer descriptors, each of which points to one or more data buffers for + * the device to read from or fill. Driver and device exchange status of each + * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty + * entries in each circular buffer, to protect against confusing empty and full + * queue states. + * + * The device reads or writes the data in the queues via the device's several + * DMA/FIFO channels. Each queue is mapped to a single DMA channel. + * + * For Tx queue, there are low mark and high mark limits. If, after queuing + * the packet for Tx, free space become < low mark, Tx queue stopped. When + * reclaiming packets (on 'tx done IRQ), if free space become > high mark, + * Tx queue resumed. + * + ***************************************************/ +static int iwl_queue_space(const struct iwl_queue *q) +{ + unsigned int max; + unsigned int used; + + /* + * To avoid ambiguity between empty and completely full queues, there + * should always be less than TFD_QUEUE_SIZE_MAX elements in the queue. + * If q->n_window is smaller than TFD_QUEUE_SIZE_MAX, there is no need + * to reserve any queue entries for this purpose. + */ + if (q->n_window < TFD_QUEUE_SIZE_MAX) + max = q->n_window; + else + max = TFD_QUEUE_SIZE_MAX - 1; + + /* + * TFD_QUEUE_SIZE_MAX is a power of 2, so the following is equivalent to + * modulo by TFD_QUEUE_SIZE_MAX and is well defined. + */ + used = (q->write_ptr - q->read_ptr) & (TFD_QUEUE_SIZE_MAX - 1); + + if (WARN_ON(used > max)) + return 0; + + return max - used; +} + +/* + * iwl_queue_init - Initialize queue's high/low-water and read/write indexes + */ +static int iwl_queue_init(struct iwl_queue *q, int slots_num, u32 id) +{ + q->n_window = slots_num; + q->id = id; + + /* slots_num must be power-of-two size, otherwise + * get_cmd_index is broken. */ + if (WARN_ON(!is_power_of_2(slots_num))) + return -EINVAL; + + q->low_mark = q->n_window / 4; + if (q->low_mark < 4) + q->low_mark = 4; + + q->high_mark = q->n_window / 8; + if (q->high_mark < 2) + q->high_mark = 2; + + q->write_ptr = 0; + q->read_ptr = 0; + + return 0; +} + +static int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr, size_t size) +{ + if (WARN_ON(ptr->addr)) + return -EINVAL; + + ptr->addr = dma_alloc_coherent(trans->dev, size, + &ptr->dma, GFP_KERNEL); + if (!ptr->addr) + return -ENOMEM; + ptr->size = size; + return 0; +} + +static void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr) +{ + if (unlikely(!ptr->addr)) + return; + + dma_free_coherent(trans->dev, ptr->size, ptr->addr, ptr->dma); + memset(ptr, 0, sizeof(*ptr)); +} + +static void iwl_pcie_txq_stuck_timer(unsigned long data) +{ + struct iwl_txq *txq = (void *)data; + struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; + struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); + u32 scd_sram_addr = trans_pcie->scd_base_addr + + SCD_TX_STTS_QUEUE_OFFSET(txq->q.id); + u8 buf[16]; + int i; + + spin_lock(&txq->lock); + /* check if triggered erroneously */ + if (txq->q.read_ptr == txq->q.write_ptr) { + spin_unlock(&txq->lock); + return; + } + spin_unlock(&txq->lock); + + IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id, + jiffies_to_msecs(txq->wd_timeout)); + IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", + txq->q.read_ptr, txq->q.write_ptr); + + iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); + + iwl_print_hex_error(trans, buf, sizeof(buf)); + + for (i = 0; i < FH_TCSR_CHNL_NUM; i++) + IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i, + iwl_read_direct32(trans, FH_TX_TRB_REG(i))); + + for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { + u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(i)); + u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; + bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); + u32 tbl_dw = + iwl_trans_read_mem32(trans, + trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE(i)); + + if (i & 0x1) + tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; + else + tbl_dw = tbl_dw & 0x0000FFFF; + + IWL_ERR(trans, + "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", + i, active ? "" : "in", fifo, tbl_dw, + iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) & + (TFD_QUEUE_SIZE_MAX - 1), + iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); + } + + iwl_force_nmi(trans); +} + +/* + * iwl_pcie_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array + */ +static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans, + struct iwl_txq *txq, u16 byte_cnt) +{ + struct iwlagn_scd_bc_tbl *scd_bc_tbl; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int write_ptr = txq->q.write_ptr; + int txq_id = txq->q.id; + u8 sec_ctl = 0; + u8 sta_id = 0; + u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE; + __le16 bc_ent; + struct iwl_tx_cmd *tx_cmd = + (void *) txq->entries[txq->q.write_ptr].cmd->payload; + + scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; + + sta_id = tx_cmd->sta_id; + sec_ctl = tx_cmd->sec_ctl; + + switch (sec_ctl & TX_CMD_SEC_MSK) { + case TX_CMD_SEC_CCM: + len += IEEE80211_CCMP_MIC_LEN; + break; + case TX_CMD_SEC_TKIP: + len += IEEE80211_TKIP_ICV_LEN; + break; + case TX_CMD_SEC_WEP: + len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN; + break; + } + + if (trans_pcie->bc_table_dword) + len = DIV_ROUND_UP(len, 4); + + if (WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX)) + return; + + bc_ent = cpu_to_le16(len | (sta_id << 12)); + + scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; + + if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) + scd_bc_tbl[txq_id]. + tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; +} + +static void iwl_pcie_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, + struct iwl_txq *txq) +{ + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; + int txq_id = txq->q.id; + int read_ptr = txq->q.read_ptr; + u8 sta_id = 0; + __le16 bc_ent; + struct iwl_tx_cmd *tx_cmd = + (void *)txq->entries[txq->q.read_ptr].cmd->payload; + + WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX); + + if (txq_id != trans_pcie->cmd_queue) + sta_id = tx_cmd->sta_id; + + bc_ent = cpu_to_le16(1 | (sta_id << 12)); + scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent; + + if (read_ptr < TFD_QUEUE_SIZE_BC_DUP) + scd_bc_tbl[txq_id]. + tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; +} + +/* + * iwl_pcie_txq_inc_wr_ptr - Send new write index to hardware + */ +static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, + struct iwl_txq *txq) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 reg = 0; + int txq_id = txq->q.id; + + lockdep_assert_held(&txq->lock); + + /* + * explicitly wake up the NIC if: + * 1. shadow registers aren't enabled + * 2. NIC is woken up for CMD regardless of shadow outside this function + * 3. there is a chance that the NIC is asleep + */ + if (!trans->cfg->base_params->shadow_reg_enable && + txq_id != trans_pcie->cmd_queue && + test_bit(STATUS_TPOWER_PMI, &trans->status)) { + /* + * wake up nic if it's powered down ... + * uCode will wake up, and interrupt us again, so next + * time we'll skip this part. + */ + reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + IWL_DEBUG_INFO(trans, "Tx queue %d requesting wakeup, GP1 = 0x%x\n", + txq_id, reg); + iwl_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + txq->need_update = true; + return; + } + } + + /* + * if not in power-save mode, uCode will never sleep when we're + * trying to tx (during RFKILL, we're not trying to tx). + */ + IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->q.write_ptr); + iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); +} + +void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int i; + + for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { + struct iwl_txq *txq = &trans_pcie->txq[i]; + + spin_lock_bh(&txq->lock); + if (trans_pcie->txq[i].need_update) { + iwl_pcie_txq_inc_wr_ptr(trans, txq); + trans_pcie->txq[i].need_update = false; + } + spin_unlock_bh(&txq->lock); + } +} + +static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) +{ + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + + dma_addr_t addr = get_unaligned_le32(&tb->lo); + if (sizeof(dma_addr_t) > sizeof(u32)) + addr |= + ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16; + + return addr; +} + +static inline void iwl_pcie_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, + dma_addr_t addr, u16 len) +{ + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + u16 hi_n_len = len << 4; + + put_unaligned_le32(addr, &tb->lo); + if (sizeof(dma_addr_t) > sizeof(u32)) + hi_n_len |= ((addr >> 16) >> 16) & 0xF; + + tb->hi_n_len = cpu_to_le16(hi_n_len); + + tfd->num_tbs = idx + 1; +} + +static inline u8 iwl_pcie_tfd_get_num_tbs(struct iwl_tfd *tfd) +{ + return tfd->num_tbs & 0x1f; +} + +static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, + struct iwl_cmd_meta *meta, + struct iwl_tfd *tfd) +{ + int i; + int num_tbs; + + /* Sanity check on number of chunks */ + num_tbs = iwl_pcie_tfd_get_num_tbs(tfd); + + if (num_tbs >= IWL_NUM_OF_TBS) { + IWL_ERR(trans, "Too many chunks: %i\n", num_tbs); + /* @todo issue fatal error, it is quite serious situation */ + return; + } + + /* first TB is never freed - it's the scratchbuf data */ + + for (i = 1; i < num_tbs; i++) { + if (meta->flags & BIT(i + CMD_TB_BITMAP_POS)) + dma_unmap_page(trans->dev, + iwl_pcie_tfd_tb_get_addr(tfd, i), + iwl_pcie_tfd_tb_get_len(tfd, i), + DMA_TO_DEVICE); + else + dma_unmap_single(trans->dev, + iwl_pcie_tfd_tb_get_addr(tfd, i), + iwl_pcie_tfd_tb_get_len(tfd, i), + DMA_TO_DEVICE); + } + tfd->num_tbs = 0; +} + +/* + * iwl_pcie_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] + * @trans - transport private data + * @txq - tx queue + * @dma_dir - the direction of the DMA mapping + * + * Does NOT advance any TFD circular buffer read/write indexes + * Does NOT free the TFD itself (which is within circular buffer) + */ +static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) +{ + struct iwl_tfd *tfd_tmp = txq->tfds; + + /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and + * idx is bounded by n_window + */ + int rd_ptr = txq->q.read_ptr; + int idx = get_cmd_index(&txq->q, rd_ptr); + + lockdep_assert_held(&txq->lock); + + /* We have only q->n_window txq->entries, but we use + * TFD_QUEUE_SIZE_MAX tfds + */ + iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]); + + /* free SKB */ + if (txq->entries) { + struct sk_buff *skb; + + skb = txq->entries[idx].skb; + + /* Can be called from irqs-disabled context + * If skb is not NULL, it means that the whole queue is being + * freed and that the queue is not empty - free the skb + */ + if (skb) { + iwl_op_mode_free_skb(trans->op_mode, skb); + txq->entries[idx].skb = NULL; + } + } +} + +static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, + dma_addr_t addr, u16 len, bool reset) +{ + struct iwl_queue *q; + struct iwl_tfd *tfd, *tfd_tmp; + u32 num_tbs; + + q = &txq->q; + tfd_tmp = txq->tfds; + tfd = &tfd_tmp[q->write_ptr]; + + if (reset) + memset(tfd, 0, sizeof(*tfd)); + + num_tbs = iwl_pcie_tfd_get_num_tbs(tfd); + + /* Each TFD can point to a maximum 20 Tx buffers */ + if (num_tbs >= IWL_NUM_OF_TBS) { + IWL_ERR(trans, "Error can not send more than %d chunks\n", + IWL_NUM_OF_TBS); + return -EINVAL; + } + + if (WARN(addr & ~IWL_TX_DMA_MASK, + "Unaligned address = %llx\n", (unsigned long long)addr)) + return -EINVAL; + + iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len); + + return num_tbs; +} + +static int iwl_pcie_txq_alloc(struct iwl_trans *trans, + struct iwl_txq *txq, int slots_num, + u32 txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; + size_t scratchbuf_sz; + int i; + + if (WARN_ON(txq->entries || txq->tfds)) + return -EINVAL; + + setup_timer(&txq->stuck_timer, iwl_pcie_txq_stuck_timer, + (unsigned long)txq); + txq->trans_pcie = trans_pcie; + + txq->q.n_window = slots_num; + + txq->entries = kcalloc(slots_num, + sizeof(struct iwl_pcie_txq_entry), + GFP_KERNEL); + + if (!txq->entries) + goto error; + + if (txq_id == trans_pcie->cmd_queue) + for (i = 0; i < slots_num; i++) { + txq->entries[i].cmd = + kmalloc(sizeof(struct iwl_device_cmd), + GFP_KERNEL); + if (!txq->entries[i].cmd) + goto error; + } + + /* Circular buffer of transmit frame descriptors (TFDs), + * shared with device */ + txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz, + &txq->q.dma_addr, GFP_KERNEL); + if (!txq->tfds) + goto error; + + BUILD_BUG_ON(IWL_HCMD_SCRATCHBUF_SIZE != sizeof(*txq->scratchbufs)); + BUILD_BUG_ON(offsetof(struct iwl_pcie_txq_scratch_buf, scratch) != + sizeof(struct iwl_cmd_header) + + offsetof(struct iwl_tx_cmd, scratch)); + + scratchbuf_sz = sizeof(*txq->scratchbufs) * slots_num; + + txq->scratchbufs = dma_alloc_coherent(trans->dev, scratchbuf_sz, + &txq->scratchbufs_dma, + GFP_KERNEL); + if (!txq->scratchbufs) + goto err_free_tfds; + + txq->q.id = txq_id; + + return 0; +err_free_tfds: + dma_free_coherent(trans->dev, tfd_sz, txq->tfds, txq->q.dma_addr); +error: + if (txq->entries && txq_id == trans_pcie->cmd_queue) + for (i = 0; i < slots_num; i++) + kfree(txq->entries[i].cmd); + kfree(txq->entries); + txq->entries = NULL; + + return -ENOMEM; + +} + +static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, + int slots_num, u32 txq_id) +{ + int ret; + + txq->need_update = false; + + /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise + * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ + BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); + + /* Initialize queue's high/low-water marks, and head/tail indexes */ + ret = iwl_queue_init(&txq->q, slots_num, txq_id); + if (ret) + return ret; + + spin_lock_init(&txq->lock); + + /* + * Tell nic where to find circular buffer of Tx Frame Descriptors for + * given Tx queue, and enable the DMA channel used for that queue. + * Circular buffer (TFD queue in DRAM) physical base address */ + iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), + txq->q.dma_addr >> 8); + + return 0; +} + +/* + * iwl_pcie_txq_unmap - Unmap any remaining DMA mappings and free skb's + */ +static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_queue *q = &txq->q; + + spin_lock_bh(&txq->lock); + while (q->write_ptr != q->read_ptr) { + IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", + txq_id, q->read_ptr); + iwl_pcie_txq_free_tfd(trans, txq); + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr); + } + txq->active = false; + spin_unlock_bh(&txq->lock); + + /* just in case - this queue may have been stopped */ + iwl_wake_queue(trans, txq); +} + +/* + * iwl_pcie_txq_free - Deallocate DMA queue. + * @txq: Transmit queue to deallocate. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * 0-fill, but do not free "txq" descriptor structure. + */ +static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct device *dev = trans->dev; + int i; + + if (WARN_ON(!txq)) + return; + + iwl_pcie_txq_unmap(trans, txq_id); + + /* De-alloc array of command/tx buffers */ + if (txq_id == trans_pcie->cmd_queue) + for (i = 0; i < txq->q.n_window; i++) { + kzfree(txq->entries[i].cmd); + kzfree(txq->entries[i].free_buf); + } + + /* De-alloc circular buffer of TFDs */ + if (txq->tfds) { + dma_free_coherent(dev, + sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX, + txq->tfds, txq->q.dma_addr); + txq->q.dma_addr = 0; + txq->tfds = NULL; + + dma_free_coherent(dev, + sizeof(*txq->scratchbufs) * txq->q.n_window, + txq->scratchbufs, txq->scratchbufs_dma); + } + + kfree(txq->entries); + txq->entries = NULL; + + del_timer_sync(&txq->stuck_timer); + + /* 0-fill queue descriptor structure */ + memset(txq, 0, sizeof(*txq)); +} + +void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int nq = trans->cfg->base_params->num_of_queues; + int chan; + u32 reg_val; + int clear_dwords = (SCD_TRANS_TBL_OFFSET_QUEUE(nq) - + SCD_CONTEXT_MEM_LOWER_BOUND) / sizeof(u32); + + /* make sure all queue are not stopped/used */ + memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); + memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); + + trans_pcie->scd_base_addr = + iwl_read_prph(trans, SCD_SRAM_BASE_ADDR); + + WARN_ON(scd_base_addr != 0 && + scd_base_addr != trans_pcie->scd_base_addr); + + /* reset context data, TX status and translation data */ + iwl_trans_write_mem(trans, trans_pcie->scd_base_addr + + SCD_CONTEXT_MEM_LOWER_BOUND, + NULL, clear_dwords); + + iwl_write_prph(trans, SCD_DRAM_BASE_ADDR, + trans_pcie->scd_bc_tbls.dma >> 10); + + /* The chain extension of the SCD doesn't work well. This feature is + * enabled by default by the HW, so we need to disable it manually. + */ + if (trans->cfg->base_params->scd_chain_ext_wa) + iwl_write_prph(trans, SCD_CHAINEXT_EN, 0); + + iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue, + trans_pcie->cmd_fifo, + trans_pcie->cmd_q_wdg_timeout); + + /* Activate all Tx DMA/FIFO channels */ + iwl_scd_activate_fifos(trans); + + /* Enable DMA channel */ + for (chan = 0; chan < FH_TCSR_CHNL_NUM; chan++) + iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(chan), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); + + /* Update FH chicken bits */ + reg_val = iwl_read_direct32(trans, FH_TX_CHICKEN_BITS_REG); + iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG, + reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); + + /* Enable L1-Active */ + if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); +} + +void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int txq_id; + + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) { + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + + iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), + txq->q.dma_addr >> 8); + iwl_pcie_txq_unmap(trans, txq_id); + txq->q.read_ptr = 0; + txq->q.write_ptr = 0; + } + + /* Tell NIC where to find the "keep warm" buffer */ + iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, + trans_pcie->kw.dma >> 4); + + /* + * Send 0 as the scd_base_addr since the device may have be reset + * while we were in WoWLAN in which case SCD_SRAM_BASE_ADDR will + * contain garbage. + */ + iwl_pcie_tx_start(trans, 0); +} + +static void iwl_pcie_tx_stop_fh(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + int ch, ret; + u32 mask = 0; + + spin_lock(&trans_pcie->irq_lock); + + if (!iwl_trans_grab_nic_access(trans, false, &flags)) + goto out; + + /* Stop each Tx DMA channel */ + for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { + iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); + mask |= FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch); + } + + /* Wait for DMA channels to be idle */ + ret = iwl_poll_bit(trans, FH_TSSR_TX_STATUS_REG, mask, mask, 5000); + if (ret < 0) + IWL_ERR(trans, + "Failing on timeout while stopping DMA channel %d [0x%08x]\n", + ch, iwl_read32(trans, FH_TSSR_TX_STATUS_REG)); + + iwl_trans_release_nic_access(trans, &flags); + +out: + spin_unlock(&trans_pcie->irq_lock); +} + +/* + * iwl_pcie_tx_stop - Stop all Tx DMA channels + */ +int iwl_pcie_tx_stop(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int txq_id; + + /* Turn off all Tx DMA fifos */ + iwl_scd_deactivate_fifos(trans); + + /* Turn off all Tx DMA channels */ + iwl_pcie_tx_stop_fh(trans); + + /* + * This function can be called before the op_mode disabled the + * queues. This happens when we have an rfkill interrupt. + * Since we stop Tx altogether - mark the queues as stopped. + */ + memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); + memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); + + /* This can happen: start_hw, stop_device */ + if (!trans_pcie->txq) + return 0; + + /* Unmap DMA from host system and free skb's */ + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) + iwl_pcie_txq_unmap(trans, txq_id); + + return 0; +} + +/* + * iwl_trans_tx_free - Free TXQ Context + * + * Destroy all TX DMA queues and structures + */ +void iwl_pcie_tx_free(struct iwl_trans *trans) +{ + int txq_id; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + /* Tx queues */ + if (trans_pcie->txq) { + for (txq_id = 0; + txq_id < trans->cfg->base_params->num_of_queues; txq_id++) + iwl_pcie_txq_free(trans, txq_id); + } + + kfree(trans_pcie->txq); + trans_pcie->txq = NULL; + + iwl_pcie_free_dma_ptr(trans, &trans_pcie->kw); + + iwl_pcie_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls); +} + +/* + * iwl_pcie_tx_alloc - allocate TX context + * Allocate all Tx DMA structures and initialize them + */ +static int iwl_pcie_tx_alloc(struct iwl_trans *trans) +{ + int ret; + int txq_id, slots_num; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + u16 scd_bc_tbls_size = trans->cfg->base_params->num_of_queues * + sizeof(struct iwlagn_scd_bc_tbl); + + /*It is not allowed to alloc twice, so warn when this happens. + * We cannot rely on the previous allocation, so free and fail */ + if (WARN_ON(trans_pcie->txq)) { + ret = -EINVAL; + goto error; + } + + ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls, + scd_bc_tbls_size); + if (ret) { + IWL_ERR(trans, "Scheduler BC Table allocation failed\n"); + goto error; + } + + /* Alloc keep-warm buffer */ + ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE); + if (ret) { + IWL_ERR(trans, "Keep Warm allocation failed\n"); + goto error; + } + + trans_pcie->txq = kcalloc(trans->cfg->base_params->num_of_queues, + sizeof(struct iwl_txq), GFP_KERNEL); + if (!trans_pcie->txq) { + IWL_ERR(trans, "Not enough memory for txq\n"); + ret = -ENOMEM; + goto error; + } + + /* Alloc and init all Tx queues, including the command queue (#4/#9) */ + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) { + slots_num = (txq_id == trans_pcie->cmd_queue) ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + ret = iwl_pcie_txq_alloc(trans, &trans_pcie->txq[txq_id], + slots_num, txq_id); + if (ret) { + IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); + goto error; + } + } + + return 0; + +error: + iwl_pcie_tx_free(trans); + + return ret; +} +int iwl_pcie_tx_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + int txq_id, slots_num; + bool alloc = false; + + if (!trans_pcie->txq) { + ret = iwl_pcie_tx_alloc(trans); + if (ret) + goto error; + alloc = true; + } + + spin_lock(&trans_pcie->irq_lock); + + /* Turn off all Tx DMA fifos */ + iwl_scd_deactivate_fifos(trans); + + /* Tell NIC where to find the "keep warm" buffer */ + iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, + trans_pcie->kw.dma >> 4); + + spin_unlock(&trans_pcie->irq_lock); + + /* Alloc and init all Tx queues, including the command queue (#4/#9) */ + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) { + slots_num = (txq_id == trans_pcie->cmd_queue) ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + ret = iwl_pcie_txq_init(trans, &trans_pcie->txq[txq_id], + slots_num, txq_id); + if (ret) { + IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); + goto error; + } + } + + iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_AUTO_ACTIVE_MODE); + if (trans->cfg->base_params->num_of_queues > 20) + iwl_set_bits_prph(trans, SCD_GP_CTRL, + SCD_GP_CTRL_ENABLE_31_QUEUES); + + return 0; +error: + /*Upon error, free only if we allocated something */ + if (alloc) + iwl_pcie_tx_free(trans); + return ret; +} + +static inline void iwl_pcie_txq_progress(struct iwl_txq *txq) +{ + lockdep_assert_held(&txq->lock); + + if (!txq->wd_timeout) + return; + + /* + * station is asleep and we send data - that must + * be uAPSD or PS-Poll. Don't rearm the timer. + */ + if (txq->frozen) + return; + + /* + * if empty delete timer, otherwise move timer forward + * since we're making progress on this queue + */ + if (txq->q.read_ptr == txq->q.write_ptr) + del_timer(&txq->stuck_timer); + else + mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); +} + +/* Frees buffers until index _not_ inclusive */ +void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, + struct sk_buff_head *skbs) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + int tfd_num = ssn & (TFD_QUEUE_SIZE_MAX - 1); + struct iwl_queue *q = &txq->q; + int last_to_free; + + /* This function is not meant to release cmd queue*/ + if (WARN_ON(txq_id == trans_pcie->cmd_queue)) + return; + + spin_lock_bh(&txq->lock); + + if (!txq->active) { + IWL_DEBUG_TX_QUEUES(trans, "Q %d inactive - ignoring idx %d\n", + txq_id, ssn); + goto out; + } + + if (txq->q.read_ptr == tfd_num) + goto out; + + IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n", + txq_id, txq->q.read_ptr, tfd_num, ssn); + + /*Since we free until index _not_ inclusive, the one before index is + * the last we will free. This one must be used */ + last_to_free = iwl_queue_dec_wrap(tfd_num); + + if (!iwl_queue_used(q, last_to_free)) { + IWL_ERR(trans, + "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", + __func__, txq_id, last_to_free, TFD_QUEUE_SIZE_MAX, + q->write_ptr, q->read_ptr); + goto out; + } + + if (WARN_ON(!skb_queue_empty(skbs))) + goto out; + + for (; + q->read_ptr != tfd_num; + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) { + + if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL)) + continue; + + __skb_queue_tail(skbs, txq->entries[txq->q.read_ptr].skb); + + txq->entries[txq->q.read_ptr].skb = NULL; + + iwl_pcie_txq_inval_byte_cnt_tbl(trans, txq); + + iwl_pcie_txq_free_tfd(trans, txq); + } + + iwl_pcie_txq_progress(txq); + + if (iwl_queue_space(&txq->q) > txq->q.low_mark) + iwl_wake_queue(trans, txq); + + if (q->read_ptr == q->write_ptr) { + IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id); + iwl_trans_pcie_unref(trans); + } + +out: + spin_unlock_bh(&txq->lock); +} + +static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, + const struct iwl_host_cmd *cmd) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + + lockdep_assert_held(&trans_pcie->reg_lock); + + if (!(cmd->flags & CMD_SEND_IN_IDLE) && + !trans_pcie->ref_cmd_in_flight) { + trans_pcie->ref_cmd_in_flight = true; + IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n"); + iwl_trans_pcie_ref(trans); + } + + /* + * wake up the NIC to make sure that the firmware will see the host + * command - we will let the NIC sleep once all the host commands + * returned. This needs to be done only on NICs that have + * apmg_wake_up_wa set. + */ + if (trans->cfg->base_params->apmg_wake_up_wa && + !trans_pcie->cmd_hold_nic_awake) { + __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, + (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | + CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), + 15000); + if (ret < 0) { + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + IWL_ERR(trans, "Failed to wake NIC for hcmd\n"); + return -EIO; + } + trans_pcie->cmd_hold_nic_awake = true; + } + + return 0; +} + +static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + lockdep_assert_held(&trans_pcie->reg_lock); + + if (trans_pcie->ref_cmd_in_flight) { + trans_pcie->ref_cmd_in_flight = false; + IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n"); + iwl_trans_pcie_unref(trans); + } + + if (trans->cfg->base_params->apmg_wake_up_wa) { + if (WARN_ON(!trans_pcie->cmd_hold_nic_awake)) + return 0; + + trans_pcie->cmd_hold_nic_awake = false; + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + } + return 0; +} + +/* + * iwl_pcie_cmdq_reclaim - Reclaim TX command queue entries already Tx'd + * + * When FW advances 'R' index, all entries between old and new 'R' index + * need to be reclaimed. As result, some free space forms. If there is + * enough free space (> low mark), wake the stack that feeds us. + */ +static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_queue *q = &txq->q; + unsigned long flags; + int nfreed = 0; + + lockdep_assert_held(&txq->lock); + + if ((idx >= TFD_QUEUE_SIZE_MAX) || (!iwl_queue_used(q, idx))) { + IWL_ERR(trans, + "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", + __func__, txq_id, idx, TFD_QUEUE_SIZE_MAX, + q->write_ptr, q->read_ptr); + return; + } + + for (idx = iwl_queue_inc_wrap(idx); q->read_ptr != idx; + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) { + + if (nfreed++ > 0) { + IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", + idx, q->write_ptr, q->read_ptr); + iwl_force_nmi(trans); + } + } + + if (q->read_ptr == q->write_ptr) { + spin_lock_irqsave(&trans_pcie->reg_lock, flags); + iwl_pcie_clear_cmd_in_flight(trans); + spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); + } + + iwl_pcie_txq_progress(txq); +} + +static int iwl_pcie_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid, + u16 txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 tbl_dw_addr; + u32 tbl_dw; + u16 scd_q2ratid; + + scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK; + + tbl_dw_addr = trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE(txq_id); + + tbl_dw = iwl_trans_read_mem32(trans, tbl_dw_addr); + + if (txq_id & 0x1) + tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); + else + tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); + + iwl_trans_write_mem32(trans, tbl_dw_addr, tbl_dw); + + return 0; +} + +/* Receiver address (actually, Rx station's index into station table), + * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ +#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) + +void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, + const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int wdg_timeout) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + int fifo = -1; + + if (test_and_set_bit(txq_id, trans_pcie->queue_used)) + WARN_ONCE(1, "queue %d already used - expect issues", txq_id); + + txq->wd_timeout = msecs_to_jiffies(wdg_timeout); + + if (cfg) { + fifo = cfg->fifo; + + /* Disable the scheduler prior configuring the cmd queue */ + if (txq_id == trans_pcie->cmd_queue && + trans_pcie->scd_set_active) + iwl_scd_enable_set_active(trans, 0); + + /* Stop this Tx queue before configuring it */ + iwl_scd_txq_set_inactive(trans, txq_id); + + /* Set this queue as a chain-building queue unless it is CMD */ + if (txq_id != trans_pcie->cmd_queue) + iwl_scd_txq_set_chain(trans, txq_id); + + if (cfg->aggregate) { + u16 ra_tid = BUILD_RAxTID(cfg->sta_id, cfg->tid); + + /* Map receiver-address / traffic-ID to this queue */ + iwl_pcie_txq_set_ratid_map(trans, ra_tid, txq_id); + + /* enable aggregations for the queue */ + iwl_scd_txq_enable_agg(trans, txq_id); + txq->ampdu = true; + } else { + /* + * disable aggregations for the queue, this will also + * make the ra_tid mapping configuration irrelevant + * since it is now a non-AGG queue. + */ + iwl_scd_txq_disable_agg(trans, txq_id); + + ssn = txq->q.read_ptr; + } + } + + /* Place first TFD at index corresponding to start sequence number. + * Assumes that ssn_idx is valid (!= 0xFFF) */ + txq->q.read_ptr = (ssn & 0xff); + txq->q.write_ptr = (ssn & 0xff); + iwl_write_direct32(trans, HBUS_TARG_WRPTR, + (ssn & 0xff) | (txq_id << 8)); + + if (cfg) { + u8 frame_limit = cfg->frame_limit; + + iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn); + + /* Set up Tx window size and frame limit for this queue */ + iwl_trans_write_mem32(trans, trans_pcie->scd_base_addr + + SCD_CONTEXT_QUEUE_OFFSET(txq_id), 0); + iwl_trans_write_mem32(trans, + trans_pcie->scd_base_addr + + SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), + ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & + SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | + ((frame_limit << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & + SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); + + /* Set up status area in SRAM, map to Tx DMA/FIFO, activate */ + iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), + (1 << SCD_QUEUE_STTS_REG_POS_ACTIVE) | + (cfg->fifo << SCD_QUEUE_STTS_REG_POS_TXF) | + (1 << SCD_QUEUE_STTS_REG_POS_WSL) | + SCD_QUEUE_STTS_REG_MSK); + + /* enable the scheduler for this queue (only) */ + if (txq_id == trans_pcie->cmd_queue && + trans_pcie->scd_set_active) + iwl_scd_enable_set_active(trans, BIT(txq_id)); + + IWL_DEBUG_TX_QUEUES(trans, + "Activate queue %d on FIFO %d WrPtr: %d\n", + txq_id, fifo, ssn & 0xff); + } else { + IWL_DEBUG_TX_QUEUES(trans, + "Activate queue %d WrPtr: %d\n", + txq_id, ssn & 0xff); + } + + txq->active = true; +} + +void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, + bool configure_scd) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 stts_addr = trans_pcie->scd_base_addr + + SCD_TX_STTS_QUEUE_OFFSET(txq_id); + static const u32 zero_val[4] = {}; + + trans_pcie->txq[txq_id].frozen_expiry_remainder = 0; + trans_pcie->txq[txq_id].frozen = false; + + /* + * Upon HW Rfkill - we stop the device, and then stop the queues + * in the op_mode. Just for the sake of the simplicity of the op_mode, + * allow the op_mode to call txq_disable after it already called + * stop_device. + */ + if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) { + WARN_ONCE(test_bit(STATUS_DEVICE_ENABLED, &trans->status), + "queue %d not used", txq_id); + return; + } + + if (configure_scd) { + iwl_scd_txq_set_inactive(trans, txq_id); + + iwl_trans_write_mem(trans, stts_addr, (void *)zero_val, + ARRAY_SIZE(zero_val)); + } + + iwl_pcie_txq_unmap(trans, txq_id); + trans_pcie->txq[txq_id].ampdu = false; + + IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); +} + +/*************** HOST COMMAND QUEUE FUNCTIONS *****/ + +/* + * iwl_pcie_enqueue_hcmd - enqueue a uCode command + * @priv: device private data point + * @cmd: a pointer to the ucode command structure + * + * The function returns < 0 values to indicate the operation + * failed. On success, it returns the index (>= 0) of command in the + * command queue. + */ +static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, + struct iwl_host_cmd *cmd) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_queue *q = &txq->q; + struct iwl_device_cmd *out_cmd; + struct iwl_cmd_meta *out_meta; + unsigned long flags; + void *dup_buf = NULL; + dma_addr_t phys_addr; + int idx; + u16 copy_size, cmd_size, scratch_size; + bool had_nocopy = false; + u8 group_id = iwl_cmd_groupid(cmd->id); + int i, ret; + u32 cmd_pos; + const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; + u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; + + if (WARN(!trans_pcie->wide_cmd_header && + group_id > IWL_ALWAYS_LONG_GROUP, + "unsupported wide command %#x\n", cmd->id)) + return -EINVAL; + + if (group_id != 0) { + copy_size = sizeof(struct iwl_cmd_header_wide); + cmd_size = sizeof(struct iwl_cmd_header_wide); + } else { + copy_size = sizeof(struct iwl_cmd_header); + cmd_size = sizeof(struct iwl_cmd_header); + } + + /* need one for the header if the first is NOCOPY */ + BUILD_BUG_ON(IWL_MAX_CMD_TBS_PER_TFD > IWL_NUM_OF_TBS - 1); + + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { + cmddata[i] = cmd->data[i]; + cmdlen[i] = cmd->len[i]; + + if (!cmd->len[i]) + continue; + + /* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */ + if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { + int copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; + + if (copy > cmdlen[i]) + copy = cmdlen[i]; + cmdlen[i] -= copy; + cmddata[i] += copy; + copy_size += copy; + } + + if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { + had_nocopy = true; + if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) { + idx = -EINVAL; + goto free_dup_buf; + } + } else if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) { + /* + * This is also a chunk that isn't copied + * to the static buffer so set had_nocopy. + */ + had_nocopy = true; + + /* only allowed once */ + if (WARN_ON(dup_buf)) { + idx = -EINVAL; + goto free_dup_buf; + } + + dup_buf = kmemdup(cmddata[i], cmdlen[i], + GFP_ATOMIC); + if (!dup_buf) + return -ENOMEM; + } else { + /* NOCOPY must not be followed by normal! */ + if (WARN_ON(had_nocopy)) { + idx = -EINVAL; + goto free_dup_buf; + } + copy_size += cmdlen[i]; + } + cmd_size += cmd->len[i]; + } + + /* + * If any of the command structures end up being larger than + * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically + * allocated into separate TFDs, then we will need to + * increase the size of the buffers. + */ + if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE, + "Command %s (%#x) is too large (%d bytes)\n", + get_cmd_string(trans_pcie, cmd->id), cmd->id, copy_size)) { + idx = -EINVAL; + goto free_dup_buf; + } + + spin_lock_bh(&txq->lock); + + if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { + spin_unlock_bh(&txq->lock); + + IWL_ERR(trans, "No space in command queue\n"); + iwl_op_mode_cmd_queue_full(trans->op_mode); + idx = -ENOSPC; + goto free_dup_buf; + } + + idx = get_cmd_index(q, q->write_ptr); + out_cmd = txq->entries[idx].cmd; + out_meta = &txq->entries[idx].meta; + + memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ + if (cmd->flags & CMD_WANT_SKB) + out_meta->source = cmd; + + /* set up the header */ + if (group_id != 0) { + out_cmd->hdr_wide.cmd = iwl_cmd_opcode(cmd->id); + out_cmd->hdr_wide.group_id = group_id; + out_cmd->hdr_wide.version = iwl_cmd_version(cmd->id); + out_cmd->hdr_wide.length = + cpu_to_le16(cmd_size - + sizeof(struct iwl_cmd_header_wide)); + out_cmd->hdr_wide.reserved = 0; + out_cmd->hdr_wide.sequence = + cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | + INDEX_TO_SEQ(q->write_ptr)); + + cmd_pos = sizeof(struct iwl_cmd_header_wide); + copy_size = sizeof(struct iwl_cmd_header_wide); + } else { + out_cmd->hdr.cmd = iwl_cmd_opcode(cmd->id); + out_cmd->hdr.sequence = + cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | + INDEX_TO_SEQ(q->write_ptr)); + out_cmd->hdr.group_id = 0; + + cmd_pos = sizeof(struct iwl_cmd_header); + copy_size = sizeof(struct iwl_cmd_header); + } + + /* and copy the data that needs to be copied */ + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { + int copy; + + if (!cmd->len[i]) + continue; + + /* copy everything if not nocopy/dup */ + if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | + IWL_HCMD_DFL_DUP))) { + copy = cmd->len[i]; + + memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); + cmd_pos += copy; + copy_size += copy; + continue; + } + + /* + * Otherwise we need at least IWL_HCMD_SCRATCHBUF_SIZE copied + * in total (for the scratchbuf handling), but copy up to what + * we can fit into the payload for debug dump purposes. + */ + copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]); + + memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); + cmd_pos += copy; + + /* However, treat copy_size the proper way, we need it below */ + if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { + copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; + + if (copy > cmd->len[i]) + copy = cmd->len[i]; + copy_size += copy; + } + } + + IWL_DEBUG_HC(trans, + "Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", + get_cmd_string(trans_pcie, out_cmd->hdr.cmd), + group_id, out_cmd->hdr.cmd, + le16_to_cpu(out_cmd->hdr.sequence), + cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); + + /* start the TFD with the scratchbuf */ + scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE); + memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size); + iwl_pcie_txq_build_tfd(trans, txq, + iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr), + scratch_size, true); + + /* map first command fragment, if any remains */ + if (copy_size > scratch_size) { + phys_addr = dma_map_single(trans->dev, + ((u8 *)&out_cmd->hdr) + scratch_size, + copy_size - scratch_size, + DMA_TO_DEVICE); + if (dma_mapping_error(trans->dev, phys_addr)) { + iwl_pcie_tfd_unmap(trans, out_meta, + &txq->tfds[q->write_ptr]); + idx = -ENOMEM; + goto out; + } + + iwl_pcie_txq_build_tfd(trans, txq, phys_addr, + copy_size - scratch_size, false); + } + + /* map the remaining (adjusted) nocopy/dup fragments */ + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { + const void *data = cmddata[i]; + + if (!cmdlen[i]) + continue; + if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | + IWL_HCMD_DFL_DUP))) + continue; + if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) + data = dup_buf; + phys_addr = dma_map_single(trans->dev, (void *)data, + cmdlen[i], DMA_TO_DEVICE); + if (dma_mapping_error(trans->dev, phys_addr)) { + iwl_pcie_tfd_unmap(trans, out_meta, + &txq->tfds[q->write_ptr]); + idx = -ENOMEM; + goto out; + } + + iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false); + } + + BUILD_BUG_ON(IWL_NUM_OF_TBS + CMD_TB_BITMAP_POS > + sizeof(out_meta->flags) * BITS_PER_BYTE); + out_meta->flags = cmd->flags; + if (WARN_ON_ONCE(txq->entries[idx].free_buf)) + kzfree(txq->entries[idx].free_buf); + txq->entries[idx].free_buf = dup_buf; + + trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr_wide); + + /* start timer if queue currently empty */ + if (q->read_ptr == q->write_ptr && txq->wd_timeout) + mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); + + spin_lock_irqsave(&trans_pcie->reg_lock, flags); + ret = iwl_pcie_set_cmd_in_flight(trans, cmd); + if (ret < 0) { + idx = ret; + spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); + goto out; + } + + /* Increment and update queue's write index */ + q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); + iwl_pcie_txq_inc_wr_ptr(trans, txq); + + spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); + + out: + spin_unlock_bh(&txq->lock); + free_dup_buf: + if (idx < 0) + kfree(dup_buf); + return idx; +} + +/* + * iwl_pcie_hcmd_complete - Pull unused buffers off the queue and reclaim them + * @rxb: Rx buffer to reclaim + * + * If an Rx buffer has an async callback associated with it the callback + * will be executed. The attached skb (if present) will only be freed + * if the callback returns 1 + */ +void iwl_pcie_hcmd_complete(struct iwl_trans *trans, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u16 sequence = le16_to_cpu(pkt->hdr.sequence); + int txq_id = SEQ_TO_QUEUE(sequence); + int index = SEQ_TO_INDEX(sequence); + int cmd_index; + struct iwl_device_cmd *cmd; + struct iwl_cmd_meta *meta; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + + /* If a Tx command is being handled and it isn't in the actual + * command queue then there a command routing bug has been introduced + * in the queue management code. */ + if (WARN(txq_id != trans_pcie->cmd_queue, + "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", + txq_id, trans_pcie->cmd_queue, sequence, + trans_pcie->txq[trans_pcie->cmd_queue].q.read_ptr, + trans_pcie->txq[trans_pcie->cmd_queue].q.write_ptr)) { + iwl_print_hex_error(trans, pkt, 32); + return; + } + + spin_lock_bh(&txq->lock); + + cmd_index = get_cmd_index(&txq->q, index); + cmd = txq->entries[cmd_index].cmd; + meta = &txq->entries[cmd_index].meta; + + iwl_pcie_tfd_unmap(trans, meta, &txq->tfds[index]); + + /* Input error checking is done when commands are added to queue. */ + if (meta->flags & CMD_WANT_SKB) { + struct page *p = rxb_steal_page(rxb); + + meta->source->resp_pkt = pkt; + meta->source->_rx_page_addr = (unsigned long)page_address(p); + meta->source->_rx_page_order = trans_pcie->rx_page_order; + } + + iwl_pcie_cmdq_reclaim(trans, txq_id, index); + + if (!(meta->flags & CMD_ASYNC)) { + if (!test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status)) { + IWL_WARN(trans, + "HCMD_ACTIVE already clear for command %s\n", + get_cmd_string(trans_pcie, cmd->hdr.cmd)); + } + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", + get_cmd_string(trans_pcie, cmd->hdr.cmd)); + wake_up(&trans_pcie->wait_command_queue); + } + + meta->flags = 0; + + spin_unlock_bh(&txq->lock); +} + +#define HOST_COMPLETE_TIMEOUT (2 * HZ) + +static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans, + struct iwl_host_cmd *cmd) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + + /* An asynchronous command can not expect an SKB to be set. */ + if (WARN_ON(cmd->flags & CMD_WANT_SKB)) + return -EINVAL; + + ret = iwl_pcie_enqueue_hcmd(trans, cmd); + if (ret < 0) { + IWL_ERR(trans, + "Error sending %s: enqueue_hcmd failed: %d\n", + get_cmd_string(trans_pcie, cmd->id), ret); + return ret; + } + return 0; +} + +static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, + struct iwl_host_cmd *cmd) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int cmd_idx; + int ret; + + IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", + get_cmd_string(trans_pcie, cmd->id)); + + if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status), + "Command %s: a command is already active!\n", + get_cmd_string(trans_pcie, cmd->id))) + return -EIO; + + IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", + get_cmd_string(trans_pcie, cmd->id)); + + cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd); + if (cmd_idx < 0) { + ret = cmd_idx; + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + IWL_ERR(trans, + "Error sending %s: enqueue_hcmd failed: %d\n", + get_cmd_string(trans_pcie, cmd->id), ret); + return ret; + } + + ret = wait_event_timeout(trans_pcie->wait_command_queue, + !test_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status), + HOST_COMPLETE_TIMEOUT); + if (!ret) { + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_queue *q = &txq->q; + + IWL_ERR(trans, "Error sending %s: time out after %dms.\n", + get_cmd_string(trans_pcie, cmd->id), + jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); + + IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n", + q->read_ptr, q->write_ptr); + + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", + get_cmd_string(trans_pcie, cmd->id)); + ret = -ETIMEDOUT; + + iwl_force_nmi(trans); + iwl_trans_fw_error(trans); + + goto cancel; + } + + if (test_bit(STATUS_FW_ERROR, &trans->status)) { + IWL_ERR(trans, "FW error in SYNC CMD %s\n", + get_cmd_string(trans_pcie, cmd->id)); + dump_stack(); + ret = -EIO; + goto cancel; + } + + if (!(cmd->flags & CMD_SEND_IN_RFKILL) && + test_bit(STATUS_RFKILL, &trans->status)) { + IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n"); + ret = -ERFKILL; + goto cancel; + } + + if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { + IWL_ERR(trans, "Error: Response NULL in '%s'\n", + get_cmd_string(trans_pcie, cmd->id)); + ret = -EIO; + goto cancel; + } + + return 0; + +cancel: + if (cmd->flags & CMD_WANT_SKB) { + /* + * Cancel the CMD_WANT_SKB flag for the cmd in the + * TX cmd queue. Otherwise in case the cmd comes + * in later, it will possibly set an invalid + * address (cmd->meta.source). + */ + trans_pcie->txq[trans_pcie->cmd_queue]. + entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB; + } + + if (cmd->resp_pkt) { + iwl_free_resp(cmd); + cmd->resp_pkt = NULL; + } + + return ret; +} + +int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) +{ + if (!(cmd->flags & CMD_SEND_IN_RFKILL) && + test_bit(STATUS_RFKILL, &trans->status)) { + IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n", + cmd->id); + return -ERFKILL; + } + + if (cmd->flags & CMD_ASYNC) + return iwl_pcie_send_hcmd_async(trans, cmd); + + /* We still can fail on RFKILL that can be asserted while we wait */ + return iwl_pcie_send_hcmd_sync(trans, cmd); +} + +int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_device_cmd *dev_cmd, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct ieee80211_hdr *hdr; + struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; + struct iwl_cmd_meta *out_meta; + struct iwl_txq *txq; + struct iwl_queue *q; + dma_addr_t tb0_phys, tb1_phys, scratch_phys; + void *tb1_addr; + u16 len, tb1_len, tb2_len; + bool wait_write_ptr; + __le16 fc; + u8 hdr_len; + u16 wifi_seq; + int i; + + txq = &trans_pcie->txq[txq_id]; + q = &txq->q; + + if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used), + "TX on unused queue %d\n", txq_id)) + return -EINVAL; + + if (skb_is_nonlinear(skb) && + skb_shinfo(skb)->nr_frags > IWL_PCIE_MAX_FRAGS && + __skb_linearize(skb)) + return -ENOMEM; + + /* mac80211 always puts the full header into the SKB's head, + * so there's no need to check if it's readable there + */ + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + hdr_len = ieee80211_hdrlen(fc); + + spin_lock(&txq->lock); + + /* In AGG mode, the index in the ring must correspond to the WiFi + * sequence number. This is a HW requirements to help the SCD to parse + * the BA. + * Check here that the packets are in the right place on the ring. + */ + wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); + WARN_ONCE(txq->ampdu && + (wifi_seq & 0xff) != q->write_ptr, + "Q: %d WiFi Seq %d tfdNum %d", + txq_id, wifi_seq, q->write_ptr); + + /* Set up driver data for this TFD */ + txq->entries[q->write_ptr].skb = skb; + txq->entries[q->write_ptr].cmd = dev_cmd; + + dev_cmd->hdr.sequence = + cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | + INDEX_TO_SEQ(q->write_ptr))); + + tb0_phys = iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr); + scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) + + offsetof(struct iwl_tx_cmd, scratch); + + tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); + tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); + + /* Set up first empty entry in queue's array of Tx/cmd buffers */ + out_meta = &txq->entries[q->write_ptr].meta; + out_meta->flags = 0; + + /* + * The second TB (tb1) points to the remainder of the TX command + * and the 802.11 header - dword aligned size + * (This calculation modifies the TX command, so do it before the + * setup of the first TB) + */ + len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + + hdr_len - IWL_HCMD_SCRATCHBUF_SIZE; + tb1_len = ALIGN(len, 4); + + /* Tell NIC about any 2-byte padding after MAC header */ + if (tb1_len != len) + tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; + + /* The first TB points to the scratchbuf data - min_copy bytes */ + memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr, + IWL_HCMD_SCRATCHBUF_SIZE); + iwl_pcie_txq_build_tfd(trans, txq, tb0_phys, + IWL_HCMD_SCRATCHBUF_SIZE, true); + + /* there must be data left over for TB1 or this code must be changed */ + BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE); + + /* map the data for TB1 */ + tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_HCMD_SCRATCHBUF_SIZE; + tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb1_phys))) + goto out_err; + iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false); + + /* + * Set up TFD's third entry to point directly to remainder + * of skb's head, if any + */ + tb2_len = skb_headlen(skb) - hdr_len; + if (tb2_len > 0) { + dma_addr_t tb2_phys = dma_map_single(trans->dev, + skb->data + hdr_len, + tb2_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) { + iwl_pcie_tfd_unmap(trans, out_meta, + &txq->tfds[q->write_ptr]); + goto out_err; + } + iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false); + } + + /* set up the remaining entries to point to the data */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + dma_addr_t tb_phys; + int tb_idx; + + if (!skb_frag_size(frag)) + continue; + + tb_phys = skb_frag_dma_map(trans->dev, frag, 0, + skb_frag_size(frag), DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { + iwl_pcie_tfd_unmap(trans, out_meta, + &txq->tfds[q->write_ptr]); + goto out_err; + } + tb_idx = iwl_pcie_txq_build_tfd(trans, txq, tb_phys, + skb_frag_size(frag), false); + + out_meta->flags |= BIT(tb_idx + CMD_TB_BITMAP_POS); + } + + /* Set up entry for this TFD in Tx byte-count array */ + iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); + + trace_iwlwifi_dev_tx(trans->dev, skb, + &txq->tfds[txq->q.write_ptr], + sizeof(struct iwl_tfd), + &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len, + skb->data + hdr_len, tb2_len); + trace_iwlwifi_dev_tx_data(trans->dev, skb, + hdr_len, skb->len - hdr_len); + + wait_write_ptr = ieee80211_has_morefrags(fc); + + /* start timer if queue currently empty */ + if (q->read_ptr == q->write_ptr) { + if (txq->wd_timeout) { + /* + * If the TXQ is active, then set the timer, if not, + * set the timer in remainder so that the timer will + * be armed with the right value when the station will + * wake up. + */ + if (!txq->frozen) + mod_timer(&txq->stuck_timer, + jiffies + txq->wd_timeout); + else + txq->frozen_expiry_remainder = txq->wd_timeout; + } + IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id); + iwl_trans_pcie_ref(trans); + } + + /* Tell device the write index *just past* this latest filled TFD */ + q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); + if (!wait_write_ptr) + iwl_pcie_txq_inc_wr_ptr(trans, txq); + + /* + * At this point the frame is "transmitted" successfully + * and we will get a TX status notification eventually. + */ + if (iwl_queue_space(q) < q->high_mark) { + if (wait_write_ptr) + iwl_pcie_txq_inc_wr_ptr(trans, txq); + else + iwl_stop_queue(trans, txq); + } + spin_unlock(&txq->lock); + return 0; +out_err: + spin_unlock(&txq->lock); + return -1; +} diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig deleted file mode 100644 index 6e949df399d6..000000000000 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ /dev/null @@ -1,161 +0,0 @@ -config IWLWIFI - tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) " - depends on PCI && MAC80211 && HAS_IOMEM - select FW_LOADER - ---help--- - Select to build the driver supporting the: - - Intel Wireless WiFi Link Next-Gen AGN - - This option enables support for use with the following hardware: - Intel Wireless WiFi Link 6250AGN Adapter - Intel 6000 Series Wi-Fi Adapters (6200AGN and 6300AGN) - Intel WiFi Link 1000BGN - Intel Wireless WiFi 5150AGN - Intel Wireless WiFi 5100AGN, 5300AGN, and 5350AGN - Intel 6005 Series Wi-Fi Adapters - Intel 6030 Series Wi-Fi Adapters - Intel Wireless WiFi Link 6150BGN 2 Adapter - Intel 100 Series Wi-Fi Adapters (100BGN and 130BGN) - Intel 2000 Series Wi-Fi Adapters - Intel 7260 Wi-Fi Adapter - Intel 3160 Wi-Fi Adapter - Intel 7265 Wi-Fi Adapter - Intel 8260 Wi-Fi Adapter - Intel 3165 Wi-Fi Adapter - - - This driver uses the kernel's mac80211 subsystem. - - In order to use this driver, you will need a firmware - image for it. You can obtain the microcode from: - - . - - The firmware is typically installed in /lib/firmware. You can - look in the hotplug script /etc/hotplug/firmware.agent to - determine which directory FIRMWARE_DIR is set to when the script - runs. - - If you want to compile the driver as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read . The - module will be called iwlwifi. - -if IWLWIFI - -config IWLWIFI_LEDS - bool - depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI - select LEDS_TRIGGERS - select MAC80211_LEDS - default y - -config IWLDVM - tristate "Intel Wireless WiFi DVM Firmware support" - default IWLWIFI - help - This is the driver that supports the DVM firmware. The list - of the devices that use this firmware is available here: - https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware - -config IWLMVM - tristate "Intel Wireless WiFi MVM Firmware support" - select WANT_DEV_COREDUMP - help - This is the driver that supports the MVM firmware. The list - of the devices that use this firmware is available here: - https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware - -# don't call it _MODULE -- will confuse Kconfig/fixdep/... -config IWLWIFI_OPMODE_MODULAR - bool - default y if IWLDVM=m - default y if IWLMVM=m - -comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM" - depends on IWLDVM=n && IWLMVM=n - -config IWLWIFI_BCAST_FILTERING - bool "Enable broadcast filtering" - depends on IWLMVM - help - Say Y here to enable default bcast filtering configuration. - - Enabling broadcast filtering will drop any incoming wireless - broadcast frames, except some very specific predefined - patterns (e.g. incoming arp requests). - - If unsure, don't enable this option, as some programs might - expect incoming broadcasts for their normal operations. - -config IWLWIFI_UAPSD - bool "enable U-APSD by default" - depends on IWLMVM - help - Say Y here to enable U-APSD by default. This may cause - interoperability problems with some APs, manifesting in lower than - expected throughput due to those APs not enabling aggregation - - If unsure, say N. - -menu "Debugging Options" - -config IWLWIFI_DEBUG - bool "Enable full debugging output in the iwlwifi driver" - ---help--- - This option will enable debug tracing output for the iwlwifi drivers - - This will result in the kernel module being ~100k larger. You can - control which debug output is sent to the kernel log by setting the - value in - - /sys/module/iwlwifi/parameters/debug - - This entry will only exist if this option is enabled. - - To set a value, simply echo an 8-byte hex value to the same file: - - % echo 0x43fff > /sys/module/iwlwifi/parameters/debug - - You can find the list of debug mask values in: - drivers/net/wireless/iwlwifi/iwl-debug.h - - If this is your first time using this driver, you should say Y here - as the debug information can assist others in helping you resolve - any problems you may encounter. - -config IWLWIFI_DEBUGFS - bool "iwlwifi debugfs support" - depends on MAC80211_DEBUGFS - ---help--- - Enable creation of debugfs files for the iwlwifi drivers. This - is a low-impact option that allows getting insight into the - driver's state at runtime. - -config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE - bool "Experimental uCode support" - depends on IWLWIFI_DEBUG - ---help--- - Enable use of experimental ucode for testing and debugging. - -config IWLWIFI_DEVICE_TRACING - bool "iwlwifi device access tracing" - depends on EVENT_TRACING - default y - help - Say Y here to trace all commands, including TX frames and IO - accesses, sent to the device. If you say yes, iwlwifi will - register with the ftrace framework for event tracing and dump - all this information to the ringbuffer, you may need to - increase the ringbuffer size. See the ftrace documentation - for more information. - - When tracing is not enabled, this option still has some - (though rather small) overhead. - - If unsure, say Y so we can help you better when problems - occur. -endmenu - -endif diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile deleted file mode 100644 index dbfc5b18bcb7..000000000000 --- a/drivers/net/wireless/iwlwifi/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# common -obj-$(CONFIG_IWLWIFI) += iwlwifi.o -iwlwifi-objs += iwl-io.o -iwlwifi-objs += iwl-drv.o -iwlwifi-objs += iwl-debug.o -iwlwifi-objs += iwl-notif-wait.o -iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o -iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o -iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o -iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o -iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o -iwlwifi-objs += iwl-trans.o - -iwlwifi-objs += $(iwlwifi-m) - -iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o - -ccflags-y += -D__CHECK_ENDIAN__ -I$(src) - -obj-$(CONFIG_IWLDVM) += dvm/ -obj-$(CONFIG_IWLMVM) += mvm/ - -CFLAGS_iwl-devtrace.o := -I$(src) diff --git a/drivers/net/wireless/iwlwifi/dvm/Makefile b/drivers/net/wireless/iwlwifi/dvm/Makefile deleted file mode 100644 index 4d19685f31c3..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# DVM -obj-$(CONFIG_IWLDVM) += iwldvm.o -iwldvm-objs += main.o rs.o mac80211.o ucode.o tx.o -iwldvm-objs += lib.o calib.o tt.o sta.o rx.o - -iwldvm-objs += power.o -iwldvm-objs += scan.o -iwldvm-objs += rxon.o devices.o - -iwldvm-$(CONFIG_IWLWIFI_LEDS) += led.o -iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o - -ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h deleted file mode 100644 index 991def878881..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ /dev/null @@ -1,485 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __iwl_agn_h__ -#define __iwl_agn_h__ - -#include "iwl-config.h" - -#include "dev.h" - -/* The first 11 queues (0-10) are used otherwise */ -#define IWLAGN_FIRST_AMPDU_QUEUE 11 - -/* AUX (TX during scan dwell) queue */ -#define IWL_AUX_QUEUE 10 - -#define IWL_INVALID_STATION 255 - -/* device operations */ -extern const struct iwl_dvm_cfg iwl_dvm_1000_cfg; -extern const struct iwl_dvm_cfg iwl_dvm_2000_cfg; -extern const struct iwl_dvm_cfg iwl_dvm_105_cfg; -extern const struct iwl_dvm_cfg iwl_dvm_2030_cfg; -extern const struct iwl_dvm_cfg iwl_dvm_5000_cfg; -extern const struct iwl_dvm_cfg iwl_dvm_5150_cfg; -extern const struct iwl_dvm_cfg iwl_dvm_6000_cfg; -extern const struct iwl_dvm_cfg iwl_dvm_6005_cfg; -extern const struct iwl_dvm_cfg iwl_dvm_6050_cfg; -extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg; - - -#define TIME_UNIT 1024 - -/***************************************************** -* DRIVER STATUS FUNCTIONS -******************************************************/ -#define STATUS_RF_KILL_HW 0 -#define STATUS_CT_KILL 1 -#define STATUS_ALIVE 2 -#define STATUS_READY 3 -#define STATUS_EXIT_PENDING 5 -#define STATUS_STATISTICS 6 -#define STATUS_SCANNING 7 -#define STATUS_SCAN_ABORTING 8 -#define STATUS_SCAN_HW 9 -#define STATUS_FW_ERROR 10 -#define STATUS_CHANNEL_SWITCH_PENDING 11 -#define STATUS_SCAN_COMPLETE 12 -#define STATUS_POWER_PMI 13 - -struct iwl_ucode_capabilities; - -extern const struct ieee80211_ops iwlagn_hw_ops; - -static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd) -{ - hdr->op_code = cmd; - hdr->first_group = 0; - hdr->groups_num = 1; - hdr->data_valid = 1; -} - -void iwl_down(struct iwl_priv *priv); -void iwl_cancel_deferred_work(struct iwl_priv *priv); -void iwlagn_prepare_restart(struct iwl_priv *priv); -void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb); - -bool iwl_check_for_ct_kill(struct iwl_priv *priv); - -void iwlagn_lift_passive_no_rx(struct iwl_priv *priv); - -/* MAC80211 */ -struct ieee80211_hw *iwl_alloc_all(void); -int iwlagn_mac_setup_register(struct iwl_priv *priv, - const struct iwl_ucode_capabilities *capa); -void iwlagn_mac_unregister(struct iwl_priv *priv); - -/* commands */ -int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd); -int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, - u32 flags, u16 len, const void *data); - -/* RXON */ -void iwl_connection_init_rx_config(struct iwl_priv *priv, - struct iwl_rxon_context *ctx); -int iwlagn_set_pan_params(struct iwl_priv *priv); -int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx); -void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx); -int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed); -void iwlagn_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes); -void iwlagn_config_ht40(struct ieee80211_conf *conf, - struct iwl_rxon_context *ctx); -void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf); -void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, - struct iwl_rxon_context *ctx); -void iwl_set_flags_for_band(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - enum ieee80211_band band, - struct ieee80211_vif *vif); - -/* uCode */ -int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type); -void iwl_send_prio_tbl(struct iwl_priv *priv); -int iwl_init_alive_start(struct iwl_priv *priv); -int iwl_run_init_ucode(struct iwl_priv *priv); -int iwl_load_ucode_wait_alive(struct iwl_priv *priv, - enum iwl_ucode_type ucode_type); -int iwl_send_calib_results(struct iwl_priv *priv); -int iwl_calib_set(struct iwl_priv *priv, - const struct iwl_calib_hdr *cmd, int len); -void iwl_calib_free_results(struct iwl_priv *priv); -int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, - char **buf); -int iwlagn_hw_valid_rtc_data_addr(u32 addr); - -/* lib */ -int iwlagn_send_tx_power(struct iwl_priv *priv); -void iwlagn_temperature(struct iwl_priv *priv); -int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk); -void iwlagn_dev_txfifo_flush(struct iwl_priv *priv); -int iwlagn_send_beacon_cmd(struct iwl_priv *priv); -int iwl_send_statistics_request(struct iwl_priv *priv, - u8 flags, bool clear); - -static inline const struct ieee80211_supported_band *iwl_get_hw_mode( - struct iwl_priv *priv, enum ieee80211_band band) -{ - return priv->hw->wiphy->bands[band]; -} - -#ifdef CONFIG_PM_SLEEP -int iwlagn_send_patterns(struct iwl_priv *priv, - struct cfg80211_wowlan *wowlan); -int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan); -#endif - -/* rx */ -int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); -void iwl_setup_rx_handlers(struct iwl_priv *priv); -void iwl_chswitch_done(struct iwl_priv *priv, bool is_success); - - -/* tx */ -int iwlagn_tx_skb(struct iwl_priv *priv, - struct ieee80211_sta *sta, - struct sk_buff *skb); -int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 *ssn); -int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u8 buf_size); -int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid); -int iwlagn_tx_agg_flush(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid); -void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb); -void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb); - -static inline u32 iwl_tx_status_to_mac80211(u32 status) -{ - status &= TX_STATUS_MSK; - - switch (status) { - case TX_STATUS_SUCCESS: - case TX_STATUS_DIRECT_DONE: - return IEEE80211_TX_STAT_ACK; - case TX_STATUS_FAIL_DEST_PS: - case TX_STATUS_FAIL_PASSIVE_NO_RX: - return IEEE80211_TX_STAT_TX_FILTERED; - default: - return 0; - } -} - -static inline bool iwl_is_tx_success(u32 status) -{ - status &= TX_STATUS_MSK; - return (status == TX_STATUS_SUCCESS) || - (status == TX_STATUS_DIRECT_DONE); -} - -u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid); - -/* scan */ -void iwlagn_post_scan(struct iwl_priv *priv); -int iwl_force_rf_reset(struct iwl_priv *priv, bool external); -void iwl_init_scan_params(struct iwl_priv *priv); -int iwl_scan_cancel(struct iwl_priv *priv); -void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); -void iwl_force_scan_end(struct iwl_priv *priv); -void iwl_internal_short_hw_scan(struct iwl_priv *priv); -void iwl_setup_rx_scan_handlers(struct iwl_priv *priv); -void iwl_setup_scan_deferred_work(struct iwl_priv *priv); -void iwl_cancel_scan_deferred_work(struct iwl_priv *priv); -int __must_check iwl_scan_initiate(struct iwl_priv *priv, - struct ieee80211_vif *vif, - enum iwl_scan_type scan_type, - enum ieee80211_band band); - -/* For faster active scanning, scan will move to the next channel if fewer than - * PLCP_QUIET_THRESH packets are heard on this channel within - * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell - * time if it's a quiet channel (nothing responded to our probe, and there's - * no other traffic). - * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */ -#define IWL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */ -#define IWL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */ - -#define IWL_SCAN_CHECK_WATCHDOG (HZ * 15) - - -/* bt coex */ -void iwlagn_send_advance_bt_config(struct iwl_priv *priv); -void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv); -void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv); -void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv); -void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv); -void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena); - -static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv) -{ - return priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist; -} - -#ifdef CONFIG_IWLWIFI_DEBUG -const char *iwl_get_tx_fail_reason(u32 status); -const char *iwl_get_agg_tx_fail_reason(u16 status); -#else -static inline const char *iwl_get_tx_fail_reason(u32 status) { return ""; } -static inline const char *iwl_get_agg_tx_fail_reason(u16 status) { return ""; } -#endif - - -/* station management */ -int iwlagn_manage_ibss_station(struct iwl_priv *priv, - struct ieee80211_vif *vif, bool add); -#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ -#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ -#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of - being activated */ -#define IWL_STA_LOCAL BIT(3) /* station state not directed by mac80211; - (this is for the IBSS BSSID stations) */ -#define IWL_STA_BCAST BIT(4) /* this station is the special bcast station */ - - -void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx); -void iwl_clear_ucode_stations(struct iwl_priv *priv, - struct iwl_rxon_context *ctx); -void iwl_dealloc_bcast_stations(struct iwl_priv *priv); -int iwl_get_free_ucode_key_offset(struct iwl_priv *priv); -int iwl_send_add_sta(struct iwl_priv *priv, - struct iwl_addsta_cmd *sta, u8 flags); -int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - const u8 *addr, bool is_ap, - struct ieee80211_sta *sta, u8 *sta_id_r); -int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, - const u8 *addr); -void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, - const u8 *addr); -u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - const u8 *addr, bool is_ap, struct ieee80211_sta *sta); - -int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct iwl_link_quality_cmd *lq, u8 flags, bool init); -void iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb); -int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct ieee80211_sta *sta); - -bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_sta *sta); - -static inline int iwl_sta_id(struct ieee80211_sta *sta) -{ - if (WARN_ON(!sta)) - return IWL_INVALID_STATION; - - return ((struct iwl_station_priv *)sta->drv_priv)->sta_id; -} - -int iwlagn_alloc_bcast_station(struct iwl_priv *priv, - struct iwl_rxon_context *ctx); -int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - const u8 *addr, u8 *sta_id_r); -int iwl_remove_default_wep_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *key); -int iwl_set_default_wep_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *key); -int iwl_restore_default_wep_keys(struct iwl_priv *priv, - struct iwl_rxon_context *ctx); -int iwl_set_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *key, - struct ieee80211_sta *sta); -int iwl_remove_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *key, - struct ieee80211_sta *sta); -void iwl_update_tkip_key(struct iwl_priv *priv, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); -int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid); -int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, - int tid, u16 ssn); -int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, - int tid); -void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt); -int iwl_update_bcast_station(struct iwl_priv *priv, - struct iwl_rxon_context *ctx); -int iwl_update_bcast_stations(struct iwl_priv *priv); - -/* rate */ -static inline u32 iwl_ant_idx_to_flags(u8 ant_idx) -{ - return BIT(ant_idx) << RATE_MCS_ANT_POS; -} - -static inline u8 iwl_hw_get_rate(__le32 rate_n_flags) -{ - return le32_to_cpu(rate_n_flags) & RATE_MCS_RATE_MSK; -} - -static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) -{ - return cpu_to_le32(flags|(u32)rate); -} - -int iwl_alive_start(struct iwl_priv *priv); - -#ifdef CONFIG_IWLWIFI_DEBUG -void iwl_print_rx_config_cmd(struct iwl_priv *priv, - enum iwl_rxon_context_id ctxid); -#else -static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv, - enum iwl_rxon_context_id ctxid) -{ -} -#endif - -/* status checks */ - -static inline int iwl_is_ready(struct iwl_priv *priv) -{ - /* The adapter is 'ready' if READY EXIT_PENDING is not set */ - return test_bit(STATUS_READY, &priv->status) && - !test_bit(STATUS_EXIT_PENDING, &priv->status); -} - -static inline int iwl_is_alive(struct iwl_priv *priv) -{ - return test_bit(STATUS_ALIVE, &priv->status); -} - -static inline int iwl_is_rfkill(struct iwl_priv *priv) -{ - return test_bit(STATUS_RF_KILL_HW, &priv->status); -} - -static inline int iwl_is_ctkill(struct iwl_priv *priv) -{ - return test_bit(STATUS_CT_KILL, &priv->status); -} - -static inline int iwl_is_ready_rf(struct iwl_priv *priv) -{ - if (iwl_is_rfkill(priv)) - return 0; - - return iwl_is_ready(priv); -} - -static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state) -{ - if (state) - set_bit(STATUS_POWER_PMI, &priv->status); - else - clear_bit(STATUS_POWER_PMI, &priv->status); - iwl_trans_set_pmi(priv->trans, state); -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir); -#else -static inline int iwl_dbgfs_register(struct iwl_priv *priv, - struct dentry *dbgfs_dir) -{ - return 0; -} -#endif /* CONFIG_IWLWIFI_DEBUGFS */ - -#ifdef CONFIG_IWLWIFI_DEBUG -#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \ -do { \ - if (!iwl_is_rfkill((m))) \ - IWL_ERR(m, fmt, ##args); \ - else \ - __iwl_err((m)->dev, true, \ - !iwl_have_debug_level(IWL_DL_RADIO), \ - fmt, ##args); \ -} while (0) -#else -#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \ -do { \ - if (!iwl_is_rfkill((m))) \ - IWL_ERR(m, fmt, ##args); \ - else \ - __iwl_err((m)->dev, true, true, fmt, ##args); \ -} while (0) -#endif /* CONFIG_IWLWIFI_DEBUG */ - -extern const char *const iwl_dvm_cmd_strings[REPLY_MAX + 1]; - -static inline const char *iwl_dvm_get_cmd_string(u8 cmd) -{ - const char *s = iwl_dvm_cmd_strings[cmd]; - if (s) - return s; - return "UNKNOWN"; -} -#endif /* __iwl_agn_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c deleted file mode 100644 index 20e6aa910700..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/calib.c +++ /dev/null @@ -1,1113 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#include -#include - -#include "iwl-trans.h" - -#include "dev.h" -#include "calib.h" -#include "agn.h" - -/***************************************************************************** - * INIT calibrations framework - *****************************************************************************/ - -/* Opaque calibration results */ -struct iwl_calib_result { - struct list_head list; - size_t cmd_len; - struct iwl_calib_hdr hdr; - /* data follows */ -}; - -struct statistics_general_data { - u32 beacon_silence_rssi_a; - u32 beacon_silence_rssi_b; - u32 beacon_silence_rssi_c; - u32 beacon_energy_a; - u32 beacon_energy_b; - u32 beacon_energy_c; -}; - -int iwl_send_calib_results(struct iwl_priv *priv) -{ - struct iwl_host_cmd hcmd = { - .id = REPLY_PHY_CALIBRATION_CMD, - }; - struct iwl_calib_result *res; - - list_for_each_entry(res, &priv->calib_results, list) { - int ret; - - hcmd.len[0] = res->cmd_len; - hcmd.data[0] = &res->hdr; - hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - ret = iwl_dvm_send_cmd(priv, &hcmd); - if (ret) { - IWL_ERR(priv, "Error %d on calib cmd %d\n", - ret, res->hdr.op_code); - return ret; - } - } - - return 0; -} - -int iwl_calib_set(struct iwl_priv *priv, - const struct iwl_calib_hdr *cmd, int len) -{ - struct iwl_calib_result *res, *tmp; - - res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr), - GFP_ATOMIC); - if (!res) - return -ENOMEM; - memcpy(&res->hdr, cmd, len); - res->cmd_len = len; - - list_for_each_entry(tmp, &priv->calib_results, list) { - if (tmp->hdr.op_code == res->hdr.op_code) { - list_replace(&tmp->list, &res->list); - kfree(tmp); - return 0; - } - } - - /* wasn't in list already */ - list_add_tail(&res->list, &priv->calib_results); - - return 0; -} - -void iwl_calib_free_results(struct iwl_priv *priv) -{ - struct iwl_calib_result *res, *tmp; - - list_for_each_entry_safe(res, tmp, &priv->calib_results, list) { - list_del(&res->list); - kfree(res); - } -} - -/***************************************************************************** - * RUNTIME calibrations framework - *****************************************************************************/ - -/* "false alarms" are signals that our DSP tries to lock onto, - * but then determines that they are either noise, or transmissions - * from a distant wireless network (also "noise", really) that get - * "stepped on" by stronger transmissions within our own network. - * This algorithm attempts to set a sensitivity level that is high - * enough to receive all of our own network traffic, but not so - * high that our DSP gets too busy trying to lock onto non-network - * activity/noise. */ -static int iwl_sens_energy_cck(struct iwl_priv *priv, - u32 norm_fa, - u32 rx_enable_time, - struct statistics_general_data *rx_info) -{ - u32 max_nrg_cck = 0; - int i = 0; - u8 max_silence_rssi = 0; - u32 silence_ref = 0; - u8 silence_rssi_a = 0; - u8 silence_rssi_b = 0; - u8 silence_rssi_c = 0; - u32 val; - - /* "false_alarms" values below are cross-multiplications to assess the - * numbers of false alarms within the measured period of actual Rx - * (Rx is off when we're txing), vs the min/max expected false alarms - * (some should be expected if rx is sensitive enough) in a - * hypothetical listening period of 200 time units (TU), 204.8 msec: - * - * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time - * - * */ - u32 false_alarms = norm_fa * 200 * 1024; - u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; - u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; - struct iwl_sensitivity_data *data = NULL; - const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; - - data = &(priv->sensitivity_data); - - data->nrg_auto_corr_silence_diff = 0; - - /* Find max silence rssi among all 3 receivers. - * This is background noise, which may include transmissions from other - * networks, measured during silence before our network's beacon */ - silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a & - ALL_BAND_FILTER) >> 8); - silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b & - ALL_BAND_FILTER) >> 8); - silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c & - ALL_BAND_FILTER) >> 8); - - val = max(silence_rssi_b, silence_rssi_c); - max_silence_rssi = max(silence_rssi_a, (u8) val); - - /* Store silence rssi in 20-beacon history table */ - data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; - data->nrg_silence_idx++; - if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) - data->nrg_silence_idx = 0; - - /* Find max silence rssi across 20 beacon history */ - for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { - val = data->nrg_silence_rssi[i]; - silence_ref = max(silence_ref, val); - } - IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n", - silence_rssi_a, silence_rssi_b, silence_rssi_c, - silence_ref); - - /* Find max rx energy (min value!) among all 3 receivers, - * measured during beacon frame. - * Save it in 10-beacon history table. */ - i = data->nrg_energy_idx; - val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); - data->nrg_value[i] = min(rx_info->beacon_energy_a, val); - - data->nrg_energy_idx++; - if (data->nrg_energy_idx >= 10) - data->nrg_energy_idx = 0; - - /* Find min rx energy (max value) across 10 beacon history. - * This is the minimum signal level that we want to receive well. - * Add backoff (margin so we don't miss slightly lower energy frames). - * This establishes an upper bound (min value) for energy threshold. */ - max_nrg_cck = data->nrg_value[0]; - for (i = 1; i < 10; i++) - max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); - max_nrg_cck += 6; - - IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", - rx_info->beacon_energy_a, rx_info->beacon_energy_b, - rx_info->beacon_energy_c, max_nrg_cck - 6); - - /* Count number of consecutive beacons with fewer-than-desired - * false alarms. */ - if (false_alarms < min_false_alarms) - data->num_in_cck_no_fa++; - else - data->num_in_cck_no_fa = 0; - IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n", - data->num_in_cck_no_fa); - - /* If we got too many false alarms this time, reduce sensitivity */ - if ((false_alarms > max_false_alarms) && - (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) { - IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n", - false_alarms, max_false_alarms); - IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n"); - data->nrg_curr_state = IWL_FA_TOO_MANY; - /* Store for "fewer than desired" on later beacon */ - data->nrg_silence_ref = silence_ref; - - /* increase energy threshold (reduce nrg value) - * to decrease sensitivity */ - data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; - /* Else if we got fewer than desired, increase sensitivity */ - } else if (false_alarms < min_false_alarms) { - data->nrg_curr_state = IWL_FA_TOO_FEW; - - /* Compare silence level with silence level for most recent - * healthy number or too many false alarms */ - data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref - - (s32)silence_ref; - - IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u, silence diff %d\n", - false_alarms, min_false_alarms, - data->nrg_auto_corr_silence_diff); - - /* Increase value to increase sensitivity, but only if: - * 1a) previous beacon did *not* have *too many* false alarms - * 1b) AND there's a significant difference in Rx levels - * from a previous beacon with too many, or healthy # FAs - * OR 2) We've seen a lot of beacons (100) with too few - * false alarms */ - if ((data->nrg_prev_state != IWL_FA_TOO_MANY) && - ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || - (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { - - IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n"); - /* Increase nrg value to increase sensitivity */ - val = data->nrg_th_cck + NRG_STEP_CCK; - data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val); - } else { - IWL_DEBUG_CALIB(priv, "... but not changing sensitivity\n"); - } - - /* Else we got a healthy number of false alarms, keep status quo */ - } else { - IWL_DEBUG_CALIB(priv, " FA in safe zone\n"); - data->nrg_curr_state = IWL_FA_GOOD_RANGE; - - /* Store for use in "fewer than desired" with later beacon */ - data->nrg_silence_ref = silence_ref; - - /* If previous beacon had too many false alarms, - * give it some extra margin by reducing sensitivity again - * (but don't go below measured energy of desired Rx) */ - if (IWL_FA_TOO_MANY == data->nrg_prev_state) { - IWL_DEBUG_CALIB(priv, "... increasing margin\n"); - if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) - data->nrg_th_cck -= NRG_MARGIN; - else - data->nrg_th_cck = max_nrg_cck; - } - } - - /* Make sure the energy threshold does not go above the measured - * energy of the desired Rx signals (reduced by backoff margin), - * or else we might start missing Rx frames. - * Lower value is higher energy, so we use max()! - */ - data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); - IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck); - - data->nrg_prev_state = data->nrg_curr_state; - - /* Auto-correlation CCK algorithm */ - if (false_alarms > min_false_alarms) { - - /* increase auto_corr values to decrease sensitivity - * so the DSP won't be disturbed by the noise - */ - if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) - data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; - else { - val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; - data->auto_corr_cck = - min((u32)ranges->auto_corr_max_cck, val); - } - val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; - data->auto_corr_cck_mrc = - min((u32)ranges->auto_corr_max_cck_mrc, val); - } else if ((false_alarms < min_false_alarms) && - ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || - (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { - - /* Decrease auto_corr values to increase sensitivity */ - val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; - data->auto_corr_cck = - max((u32)ranges->auto_corr_min_cck, val); - val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; - data->auto_corr_cck_mrc = - max((u32)ranges->auto_corr_min_cck_mrc, val); - } - - return 0; -} - - -static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv, - u32 norm_fa, - u32 rx_enable_time) -{ - u32 val; - u32 false_alarms = norm_fa * 200 * 1024; - u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; - u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; - struct iwl_sensitivity_data *data = NULL; - const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; - - data = &(priv->sensitivity_data); - - /* If we got too many false alarms this time, reduce sensitivity */ - if (false_alarms > max_false_alarms) { - - IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n", - false_alarms, max_false_alarms); - - val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm = - min((u32)ranges->auto_corr_max_ofdm, val); - - val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc = - min((u32)ranges->auto_corr_max_ofdm_mrc, val); - - val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_x1 = - min((u32)ranges->auto_corr_max_ofdm_x1, val); - - val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc_x1 = - min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val); - } - - /* Else if we got fewer than desired, increase sensitivity */ - else if (false_alarms < min_false_alarms) { - - IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n", - false_alarms, min_false_alarms); - - val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm = - max((u32)ranges->auto_corr_min_ofdm, val); - - val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc = - max((u32)ranges->auto_corr_min_ofdm_mrc, val); - - val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_x1 = - max((u32)ranges->auto_corr_min_ofdm_x1, val); - - val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc_x1 = - max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val); - } else { - IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n", - min_false_alarms, false_alarms, max_false_alarms); - } - return 0; -} - -static void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv, - struct iwl_sensitivity_data *data, - __le16 *tbl) -{ - tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] = - cpu_to_le16((u16)data->auto_corr_ofdm); - tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] = - cpu_to_le16((u16)data->auto_corr_ofdm_mrc); - tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] = - cpu_to_le16((u16)data->auto_corr_ofdm_x1); - tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] = - cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1); - - tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] = - cpu_to_le16((u16)data->auto_corr_cck); - tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] = - cpu_to_le16((u16)data->auto_corr_cck_mrc); - - tbl[HD_MIN_ENERGY_CCK_DET_INDEX] = - cpu_to_le16((u16)data->nrg_th_cck); - tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] = - cpu_to_le16((u16)data->nrg_th_ofdm); - - tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] = - cpu_to_le16(data->barker_corr_th_min); - tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] = - cpu_to_le16(data->barker_corr_th_min_mrc); - tbl[HD_OFDM_ENERGY_TH_IN_INDEX] = - cpu_to_le16(data->nrg_th_cca); - - IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", - data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, - data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, - data->nrg_th_ofdm); - - IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n", - data->auto_corr_cck, data->auto_corr_cck_mrc, - data->nrg_th_cck); -} - -/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ -static int iwl_sensitivity_write(struct iwl_priv *priv) -{ - struct iwl_sensitivity_cmd cmd; - struct iwl_sensitivity_data *data = NULL; - struct iwl_host_cmd cmd_out = { - .id = SENSITIVITY_CMD, - .len = { sizeof(struct iwl_sensitivity_cmd), }, - .flags = CMD_ASYNC, - .data = { &cmd, }, - }; - - data = &(priv->sensitivity_data); - - memset(&cmd, 0, sizeof(cmd)); - - iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]); - - /* Update uCode's "work" table, and copy it to DSP */ - cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; - - /* Don't send command to uCode if nothing has changed */ - if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]), - sizeof(u16)*HD_TABLE_SIZE)) { - IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); - return 0; - } - - /* Copy table for comparison next time */ - memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]), - sizeof(u16)*HD_TABLE_SIZE); - - return iwl_dvm_send_cmd(priv, &cmd_out); -} - -/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ -static int iwl_enhance_sensitivity_write(struct iwl_priv *priv) -{ - struct iwl_enhance_sensitivity_cmd cmd; - struct iwl_sensitivity_data *data = NULL; - struct iwl_host_cmd cmd_out = { - .id = SENSITIVITY_CMD, - .len = { sizeof(struct iwl_enhance_sensitivity_cmd), }, - .flags = CMD_ASYNC, - .data = { &cmd, }, - }; - - data = &(priv->sensitivity_data); - - memset(&cmd, 0, sizeof(cmd)); - - iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]); - - if (priv->lib->hd_v2) { - cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = - HD_INA_NON_SQUARE_DET_OFDM_DATA_V2; - cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = - HD_INA_NON_SQUARE_DET_CCK_DATA_V2; - cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = - HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = - HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = - HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = - HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = - HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = - HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = - HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = - HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = - HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2; - } else { - cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = - HD_INA_NON_SQUARE_DET_OFDM_DATA_V1; - cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = - HD_INA_NON_SQUARE_DET_CCK_DATA_V1; - cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = - HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = - HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = - HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = - HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = - HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = - HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = - HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = - HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = - HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1; - } - - /* Update uCode's "work" table, and copy it to DSP */ - cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; - - /* Don't send command to uCode if nothing has changed */ - if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]), - sizeof(u16)*HD_TABLE_SIZE) && - !memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX], - &(priv->enhance_sensitivity_tbl[0]), - sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) { - IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); - return 0; - } - - /* Copy table for comparison next time */ - memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]), - sizeof(u16)*HD_TABLE_SIZE); - memcpy(&(priv->enhance_sensitivity_tbl[0]), - &(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]), - sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES); - - return iwl_dvm_send_cmd(priv, &cmd_out); -} - -void iwl_init_sensitivity(struct iwl_priv *priv) -{ - int ret = 0; - int i; - struct iwl_sensitivity_data *data = NULL; - const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; - - if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) - return; - - IWL_DEBUG_CALIB(priv, "Start iwl_init_sensitivity\n"); - - /* Clear driver's sensitivity algo data */ - data = &(priv->sensitivity_data); - - if (ranges == NULL) - return; - - memset(data, 0, sizeof(struct iwl_sensitivity_data)); - - data->num_in_cck_no_fa = 0; - data->nrg_curr_state = IWL_FA_TOO_MANY; - data->nrg_prev_state = IWL_FA_TOO_MANY; - data->nrg_silence_ref = 0; - data->nrg_silence_idx = 0; - data->nrg_energy_idx = 0; - - for (i = 0; i < 10; i++) - data->nrg_value[i] = 0; - - for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) - data->nrg_silence_rssi[i] = 0; - - data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; - data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; - data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; - data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; - data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; - data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; - data->nrg_th_cck = ranges->nrg_th_cck; - data->nrg_th_ofdm = ranges->nrg_th_ofdm; - data->barker_corr_th_min = ranges->barker_corr_th_min; - data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; - data->nrg_th_cca = ranges->nrg_th_cca; - - data->last_bad_plcp_cnt_ofdm = 0; - data->last_fa_cnt_ofdm = 0; - data->last_bad_plcp_cnt_cck = 0; - data->last_fa_cnt_cck = 0; - - if (priv->fw->enhance_sensitivity_table) - ret |= iwl_enhance_sensitivity_write(priv); - else - ret |= iwl_sensitivity_write(priv); - IWL_DEBUG_CALIB(priv, "<calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) - return; - - data = &(priv->sensitivity_data); - - if (!iwl_is_any_associated(priv)) { - IWL_DEBUG_CALIB(priv, "<< - not associated\n"); - return; - } - - spin_lock_bh(&priv->statistics.lock); - rx_info = &priv->statistics.rx_non_phy; - ofdm = &priv->statistics.rx_ofdm; - cck = &priv->statistics.rx_cck; - if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { - IWL_DEBUG_CALIB(priv, "<< invalid data.\n"); - spin_unlock_bh(&priv->statistics.lock); - return; - } - - /* Extract Statistics: */ - rx_enable_time = le32_to_cpu(rx_info->channel_load); - fa_cck = le32_to_cpu(cck->false_alarm_cnt); - fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); - bad_plcp_cck = le32_to_cpu(cck->plcp_err); - bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); - - statis.beacon_silence_rssi_a = - le32_to_cpu(rx_info->beacon_silence_rssi_a); - statis.beacon_silence_rssi_b = - le32_to_cpu(rx_info->beacon_silence_rssi_b); - statis.beacon_silence_rssi_c = - le32_to_cpu(rx_info->beacon_silence_rssi_c); - statis.beacon_energy_a = - le32_to_cpu(rx_info->beacon_energy_a); - statis.beacon_energy_b = - le32_to_cpu(rx_info->beacon_energy_b); - statis.beacon_energy_c = - le32_to_cpu(rx_info->beacon_energy_c); - - spin_unlock_bh(&priv->statistics.lock); - - IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time); - - if (!rx_enable_time) { - IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n"); - return; - } - - /* These statistics increase monotonically, and do not reset - * at each beacon. Calculate difference from last value, or just - * use the new statistics value if it has reset or wrapped around. */ - if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) - data->last_bad_plcp_cnt_cck = bad_plcp_cck; - else { - bad_plcp_cck -= data->last_bad_plcp_cnt_cck; - data->last_bad_plcp_cnt_cck += bad_plcp_cck; - } - - if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) - data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; - else { - bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; - data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; - } - - if (data->last_fa_cnt_ofdm > fa_ofdm) - data->last_fa_cnt_ofdm = fa_ofdm; - else { - fa_ofdm -= data->last_fa_cnt_ofdm; - data->last_fa_cnt_ofdm += fa_ofdm; - } - - if (data->last_fa_cnt_cck > fa_cck) - data->last_fa_cnt_cck = fa_cck; - else { - fa_cck -= data->last_fa_cnt_cck; - data->last_fa_cnt_cck += fa_cck; - } - - /* Total aborted signal locks */ - norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; - norm_fa_cck = fa_cck + bad_plcp_cck; - - IWL_DEBUG_CALIB(priv, "cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, - bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); - - iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time); - iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis); - if (priv->fw->enhance_sensitivity_table) - iwl_enhance_sensitivity_write(priv); - else - iwl_sensitivity_write(priv); -} - -static inline u8 find_first_chain(u8 mask) -{ - if (mask & ANT_A) - return CHAIN_A; - if (mask & ANT_B) - return CHAIN_B; - return CHAIN_C; -} - -/** - * Run disconnected antenna algorithm to find out which antennas are - * disconnected. - */ -static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, - struct iwl_chain_noise_data *data) -{ - u32 active_chains = 0; - u32 max_average_sig; - u16 max_average_sig_antenna_i; - u8 num_tx_chains; - u8 first_chain; - u16 i = 0; - - average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS; - average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS; - average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS; - - if (average_sig[0] >= average_sig[1]) { - max_average_sig = average_sig[0]; - max_average_sig_antenna_i = 0; - active_chains = (1 << max_average_sig_antenna_i); - } else { - max_average_sig = average_sig[1]; - max_average_sig_antenna_i = 1; - active_chains = (1 << max_average_sig_antenna_i); - } - - if (average_sig[2] >= max_average_sig) { - max_average_sig = average_sig[2]; - max_average_sig_antenna_i = 2; - active_chains = (1 << max_average_sig_antenna_i); - } - - IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n", - average_sig[0], average_sig[1], average_sig[2]); - IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n", - max_average_sig, max_average_sig_antenna_i); - - /* Compare signal strengths for all 3 receivers. */ - for (i = 0; i < NUM_RX_CHAINS; i++) { - if (i != max_average_sig_antenna_i) { - s32 rssi_delta = (max_average_sig - average_sig[i]); - - /* If signal is very weak, compared with - * strongest, mark it as disconnected. */ - if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) - data->disconn_array[i] = 1; - else - active_chains |= (1 << i); - IWL_DEBUG_CALIB(priv, "i = %d rssiDelta = %d " - "disconn_array[i] = %d\n", - i, rssi_delta, data->disconn_array[i]); - } - } - - /* - * The above algorithm sometimes fails when the ucode - * reports 0 for all chains. It's not clear why that - * happens to start with, but it is then causing trouble - * because this can make us enable more chains than the - * hardware really has. - * - * To be safe, simply mask out any chains that we know - * are not on the device. - */ - active_chains &= priv->nvm_data->valid_rx_ant; - - num_tx_chains = 0; - for (i = 0; i < NUM_RX_CHAINS; i++) { - /* loops on all the bits of - * priv->hw_setting.valid_tx_ant */ - u8 ant_msk = (1 << i); - if (!(priv->nvm_data->valid_tx_ant & ant_msk)) - continue; - - num_tx_chains++; - if (data->disconn_array[i] == 0) - /* there is a Tx antenna connected */ - break; - if (num_tx_chains == priv->hw_params.tx_chains_num && - data->disconn_array[i]) { - /* - * If all chains are disconnected - * connect the first valid tx chain - */ - first_chain = - find_first_chain(priv->nvm_data->valid_tx_ant); - data->disconn_array[first_chain] = 0; - active_chains |= BIT(first_chain); - IWL_DEBUG_CALIB(priv, - "All Tx chains are disconnected W/A - declare %d as connected\n", - first_chain); - break; - } - } - - if (active_chains != priv->nvm_data->valid_rx_ant && - active_chains != priv->chain_noise_data.active_chains) - IWL_DEBUG_CALIB(priv, - "Detected that not all antennas are connected! " - "Connected: %#x, valid: %#x.\n", - active_chains, - priv->nvm_data->valid_rx_ant); - - /* Save for use within RXON, TX, SCAN commands, etc. */ - data->active_chains = active_chains; - IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n", - active_chains); -} - -static void iwlagn_gain_computation(struct iwl_priv *priv, - u32 average_noise[NUM_RX_CHAINS], - u8 default_chain) -{ - int i; - s32 delta_g; - struct iwl_chain_noise_data *data = &priv->chain_noise_data; - - /* - * Find Gain Code for the chains based on "default chain" - */ - for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) { - if ((data->disconn_array[i])) { - data->delta_gain_code[i] = 0; - continue; - } - - delta_g = (priv->lib->chain_noise_scale * - ((s32)average_noise[default_chain] - - (s32)average_noise[i])) / 1500; - - /* bound gain by 2 bits value max, 3rd bit is sign */ - data->delta_gain_code[i] = - min(abs(delta_g), - (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); - - if (delta_g < 0) - /* - * set negative sign ... - * note to Intel developers: This is uCode API format, - * not the format of any internal device registers. - * Do not change this format for e.g. 6050 or similar - * devices. Change format only if more resolution - * (i.e. more than 2 bits magnitude) is needed. - */ - data->delta_gain_code[i] |= (1 << 2); - } - - IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d ANT_C = %d\n", - data->delta_gain_code[1], data->delta_gain_code[2]); - - if (!data->radio_write) { - struct iwl_calib_chain_noise_gain_cmd cmd; - - memset(&cmd, 0, sizeof(cmd)); - - iwl_set_calib_hdr(&cmd.hdr, - priv->phy_calib_chain_noise_gain_cmd); - cmd.delta_gain_1 = data->delta_gain_code[1]; - cmd.delta_gain_2 = data->delta_gain_code[2]; - iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, - CMD_ASYNC, sizeof(cmd), &cmd); - - data->radio_write = 1; - data->state = IWL_CHAIN_NOISE_CALIBRATED; - } -} - -/* - * Accumulate 16 beacons of signal and noise statistics for each of - * 3 receivers/antennas/rx-chains, then figure out: - * 1) Which antennas are connected. - * 2) Differential rx gain settings to balance the 3 receivers. - */ -void iwl_chain_noise_calibration(struct iwl_priv *priv) -{ - struct iwl_chain_noise_data *data = NULL; - - u32 chain_noise_a; - u32 chain_noise_b; - u32 chain_noise_c; - u32 chain_sig_a; - u32 chain_sig_b; - u32 chain_sig_c; - u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; - u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; - u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; - u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; - u16 i = 0; - u16 rxon_chnum = INITIALIZATION_VALUE; - u16 stat_chnum = INITIALIZATION_VALUE; - u8 rxon_band24; - u8 stat_band24; - struct statistics_rx_non_phy *rx_info; - - /* - * MULTI-FIXME: - * When we support multiple interfaces on different channels, - * this must be modified/fixed. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - - if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED) - return; - - data = &(priv->chain_noise_data); - - /* - * Accumulate just the first "chain_noise_num_beacons" after - * the first association, then we're done forever. - */ - if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) { - if (data->state == IWL_CHAIN_NOISE_ALIVE) - IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n"); - return; - } - - spin_lock_bh(&priv->statistics.lock); - - rx_info = &priv->statistics.rx_non_phy; - - if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { - IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n"); - spin_unlock_bh(&priv->statistics.lock); - return; - } - - rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK); - rxon_chnum = le16_to_cpu(ctx->staging.channel); - stat_band24 = - !!(priv->statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK); - stat_chnum = le32_to_cpu(priv->statistics.flag) >> 16; - - /* Make sure we accumulate data for just the associated channel - * (even if scanning). */ - if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) { - IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n", - rxon_chnum, rxon_band24); - spin_unlock_bh(&priv->statistics.lock); - return; - } - - /* - * Accumulate beacon statistics values across - * "chain_noise_num_beacons" - */ - chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & - IN_BAND_FILTER; - chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & - IN_BAND_FILTER; - chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & - IN_BAND_FILTER; - - chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; - chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; - chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; - - spin_unlock_bh(&priv->statistics.lock); - - data->beacon_count++; - - data->chain_noise_a = (chain_noise_a + data->chain_noise_a); - data->chain_noise_b = (chain_noise_b + data->chain_noise_b); - data->chain_noise_c = (chain_noise_c + data->chain_noise_c); - - data->chain_signal_a = (chain_sig_a + data->chain_signal_a); - data->chain_signal_b = (chain_sig_b + data->chain_signal_b); - data->chain_signal_c = (chain_sig_c + data->chain_signal_c); - - IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n", - rxon_chnum, rxon_band24, data->beacon_count); - IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n", - chain_sig_a, chain_sig_b, chain_sig_c); - IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n", - chain_noise_a, chain_noise_b, chain_noise_c); - - /* If this is the "chain_noise_num_beacons", determine: - * 1) Disconnected antennas (using signal strengths) - * 2) Differential gain (using silence noise) to balance receivers */ - if (data->beacon_count != IWL_CAL_NUM_BEACONS) - return; - - /* Analyze signal for disconnected antenna */ - if (priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist) { - /* Disable disconnected antenna algorithm for advanced - bt coex, assuming valid antennas are connected */ - data->active_chains = priv->nvm_data->valid_rx_ant; - for (i = 0; i < NUM_RX_CHAINS; i++) - if (!(data->active_chains & (1<disconn_array[i] = 1; - } else - iwl_find_disconn_antenna(priv, average_sig, data); - - /* Analyze noise for rx balance */ - average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS; - average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS; - average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS; - - for (i = 0; i < NUM_RX_CHAINS; i++) { - if (!(data->disconn_array[i]) && - (average_noise[i] <= min_average_noise)) { - /* This means that chain i is active and has - * lower noise values so far: */ - min_average_noise = average_noise[i]; - min_average_noise_antenna_i = i; - } - } - - IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n", - average_noise[0], average_noise[1], - average_noise[2]); - - IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n", - min_average_noise, min_average_noise_antenna_i); - - iwlagn_gain_computation( - priv, average_noise, - find_first_chain(priv->nvm_data->valid_rx_ant)); - - /* Some power changes may have been made during the calibration. - * Update and commit the RXON - */ - iwl_update_chain_flags(priv); - - data->state = IWL_CHAIN_NOISE_DONE; - iwl_power_update_mode(priv, false); -} - -void iwl_reset_run_time_calib(struct iwl_priv *priv) -{ - int i; - memset(&(priv->sensitivity_data), 0, - sizeof(struct iwl_sensitivity_data)); - memset(&(priv->chain_noise_data), 0, - sizeof(struct iwl_chain_noise_data)); - for (i = 0; i < NUM_RX_CHAINS; i++) - priv->chain_noise_data.delta_gain_code[i] = - CHAIN_NOISE_DELTA_GAIN_INIT_VAL; - - /* Ask for statistics now, the uCode will send notification - * periodically after association */ - iwl_send_statistics_request(priv, CMD_ASYNC, true); -} diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.h b/drivers/net/wireless/iwlwifi/dvm/calib.h deleted file mode 100644 index aeae4e80ea40..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/calib.h +++ /dev/null @@ -1,74 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ -#ifndef __iwl_calib_h__ -#define __iwl_calib_h__ - -#include "dev.h" -#include "commands.h" - -void iwl_chain_noise_calibration(struct iwl_priv *priv); -void iwl_sensitivity_calibration(struct iwl_priv *priv); - -void iwl_init_sensitivity(struct iwl_priv *priv); -void iwl_reset_run_time_calib(struct iwl_priv *priv); - -#endif /* __iwl_calib_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h deleted file mode 100644 index 7a34e4d158d1..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ /dev/null @@ -1,4008 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -/* - * Please use this file (commands.h) only for uCode API definitions. - * Please use iwl-xxxx-hw.h for hardware-related definitions. - * Please use dev.h for driver implementation definitions. - */ - -#ifndef __iwl_commands_h__ -#define __iwl_commands_h__ - -#include -#include - - -enum { - REPLY_ALIVE = 0x1, - REPLY_ERROR = 0x2, - REPLY_ECHO = 0x3, /* test command */ - - /* RXON and QOS commands */ - REPLY_RXON = 0x10, - REPLY_RXON_ASSOC = 0x11, - REPLY_QOS_PARAM = 0x13, - REPLY_RXON_TIMING = 0x14, - - /* Multi-Station support */ - REPLY_ADD_STA = 0x18, - REPLY_REMOVE_STA = 0x19, - REPLY_REMOVE_ALL_STA = 0x1a, /* not used */ - REPLY_TXFIFO_FLUSH = 0x1e, - - /* Security */ - REPLY_WEPKEY = 0x20, - - /* RX, TX, LEDs */ - REPLY_TX = 0x1c, - REPLY_LEDS_CMD = 0x48, - REPLY_TX_LINK_QUALITY_CMD = 0x4e, - - /* WiMAX coexistence */ - COEX_PRIORITY_TABLE_CMD = 0x5a, - COEX_MEDIUM_NOTIFICATION = 0x5b, - COEX_EVENT_CMD = 0x5c, - - /* Calibration */ - TEMPERATURE_NOTIFICATION = 0x62, - CALIBRATION_CFG_CMD = 0x65, - CALIBRATION_RES_NOTIFICATION = 0x66, - CALIBRATION_COMPLETE_NOTIFICATION = 0x67, - - /* 802.11h related */ - REPLY_QUIET_CMD = 0x71, /* not used */ - REPLY_CHANNEL_SWITCH = 0x72, - CHANNEL_SWITCH_NOTIFICATION = 0x73, - REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74, - SPECTRUM_MEASURE_NOTIFICATION = 0x75, - - /* Power Management */ - POWER_TABLE_CMD = 0x77, - PM_SLEEP_NOTIFICATION = 0x7A, - PM_DEBUG_STATISTIC_NOTIFIC = 0x7B, - - /* Scan commands and notifications */ - REPLY_SCAN_CMD = 0x80, - REPLY_SCAN_ABORT_CMD = 0x81, - SCAN_START_NOTIFICATION = 0x82, - SCAN_RESULTS_NOTIFICATION = 0x83, - SCAN_COMPLETE_NOTIFICATION = 0x84, - - /* IBSS/AP commands */ - BEACON_NOTIFICATION = 0x90, - REPLY_TX_BEACON = 0x91, - WHO_IS_AWAKE_NOTIFICATION = 0x94, /* not used */ - - /* Miscellaneous commands */ - REPLY_TX_POWER_DBM_CMD = 0x95, - QUIET_NOTIFICATION = 0x96, /* not used */ - REPLY_TX_PWR_TABLE_CMD = 0x97, - REPLY_TX_POWER_DBM_CMD_V1 = 0x98, /* old version of API */ - TX_ANT_CONFIGURATION_CMD = 0x98, - MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */ - - /* Bluetooth device coexistence config command */ - REPLY_BT_CONFIG = 0x9b, - - /* Statistics */ - REPLY_STATISTICS_CMD = 0x9c, - STATISTICS_NOTIFICATION = 0x9d, - - /* RF-KILL commands and notifications */ - REPLY_CARD_STATE_CMD = 0xa0, - CARD_STATE_NOTIFICATION = 0xa1, - - /* Missed beacons notification */ - MISSED_BEACONS_NOTIFICATION = 0xa2, - - REPLY_CT_KILL_CONFIG_CMD = 0xa4, - SENSITIVITY_CMD = 0xa8, - REPLY_PHY_CALIBRATION_CMD = 0xb0, - REPLY_RX_PHY_CMD = 0xc0, - REPLY_RX_MPDU_CMD = 0xc1, - REPLY_RX = 0xc3, - REPLY_COMPRESSED_BA = 0xc5, - - /* BT Coex */ - REPLY_BT_COEX_PRIO_TABLE = 0xcc, - REPLY_BT_COEX_PROT_ENV = 0xcd, - REPLY_BT_COEX_PROFILE_NOTIF = 0xce, - - /* PAN commands */ - REPLY_WIPAN_PARAMS = 0xb2, - REPLY_WIPAN_RXON = 0xb3, /* use REPLY_RXON structure */ - REPLY_WIPAN_RXON_TIMING = 0xb4, /* use REPLY_RXON_TIMING structure */ - REPLY_WIPAN_RXON_ASSOC = 0xb6, /* use REPLY_RXON_ASSOC structure */ - REPLY_WIPAN_QOS_PARAM = 0xb7, /* use REPLY_QOS_PARAM structure */ - REPLY_WIPAN_WEPKEY = 0xb8, /* use REPLY_WEPKEY structure */ - REPLY_WIPAN_P2P_CHANNEL_SWITCH = 0xb9, - REPLY_WIPAN_NOA_NOTIFICATION = 0xbc, - REPLY_WIPAN_DEACTIVATION_COMPLETE = 0xbd, - - REPLY_WOWLAN_PATTERNS = 0xe0, - REPLY_WOWLAN_WAKEUP_FILTER = 0xe1, - REPLY_WOWLAN_TSC_RSC_PARAMS = 0xe2, - REPLY_WOWLAN_TKIP_PARAMS = 0xe3, - REPLY_WOWLAN_KEK_KCK_MATERIAL = 0xe4, - REPLY_WOWLAN_GET_STATUS = 0xe5, - REPLY_D3_CONFIG = 0xd3, - - REPLY_MAX = 0xff -}; - -/* - * Minimum number of queues. MAX_NUM is defined in hw specific files. - * Set the minimum to accommodate - * - 4 standard TX queues - * - the command queue - * - 4 PAN TX queues - * - the PAN multicast queue, and - * - the AUX (TX during scan dwell) queue. - */ -#define IWL_MIN_NUM_QUEUES 11 - -/* - * Command queue depends on iPAN support. - */ -#define IWL_DEFAULT_CMD_QUEUE_NUM 4 -#define IWL_IPAN_CMD_QUEUE_NUM 9 - -#define IWL_TX_FIFO_BK 0 /* shared */ -#define IWL_TX_FIFO_BE 1 -#define IWL_TX_FIFO_VI 2 /* shared */ -#define IWL_TX_FIFO_VO 3 -#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK -#define IWL_TX_FIFO_BE_IPAN 4 -#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI -#define IWL_TX_FIFO_VO_IPAN 5 -/* re-uses the VO FIFO, uCode will properly flush/schedule */ -#define IWL_TX_FIFO_AUX 5 -#define IWL_TX_FIFO_UNUSED 255 - -#define IWLAGN_CMD_FIFO_NUM 7 - -/* - * This queue number is required for proper operation - * because the ucode will stop/start the scheduler as - * required. - */ -#define IWL_IPAN_MCAST_QUEUE 8 - -/****************************************************************************** - * (0) - * Commonly used structures and definitions: - * Command header, rate_n_flags, txpower - * - *****************************************************************************/ - -/** - * iwlagn rate_n_flags bit fields - * - * rate_n_flags format is used in following iwlagn commands: - * REPLY_RX (response only) - * REPLY_RX_MPDU (response only) - * REPLY_TX (both command and response) - * REPLY_TX_LINK_QUALITY_CMD - * - * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"): - * 2-0: 0) 6 Mbps - * 1) 12 Mbps - * 2) 18 Mbps - * 3) 24 Mbps - * 4) 36 Mbps - * 5) 48 Mbps - * 6) 54 Mbps - * 7) 60 Mbps - * - * 4-3: 0) Single stream (SISO) - * 1) Dual stream (MIMO) - * 2) Triple stream (MIMO) - * - * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data - * - * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): - * 3-0: 0xD) 6 Mbps - * 0xF) 9 Mbps - * 0x5) 12 Mbps - * 0x7) 18 Mbps - * 0x9) 24 Mbps - * 0xB) 36 Mbps - * 0x1) 48 Mbps - * 0x3) 54 Mbps - * - * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"): - * 6-0: 10) 1 Mbps - * 20) 2 Mbps - * 55) 5.5 Mbps - * 110) 11 Mbps - */ -#define RATE_MCS_CODE_MSK 0x7 -#define RATE_MCS_SPATIAL_POS 3 -#define RATE_MCS_SPATIAL_MSK 0x18 -#define RATE_MCS_HT_DUP_POS 5 -#define RATE_MCS_HT_DUP_MSK 0x20 -/* Both legacy and HT use bits 7:0 as the CCK/OFDM rate or HT MCS */ -#define RATE_MCS_RATE_MSK 0xff - -/* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */ -#define RATE_MCS_FLAGS_POS 8 -#define RATE_MCS_HT_POS 8 -#define RATE_MCS_HT_MSK 0x100 - -/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ -#define RATE_MCS_CCK_POS 9 -#define RATE_MCS_CCK_MSK 0x200 - -/* Bit 10: (1) Use Green Field preamble */ -#define RATE_MCS_GF_POS 10 -#define RATE_MCS_GF_MSK 0x400 - -/* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */ -#define RATE_MCS_HT40_POS 11 -#define RATE_MCS_HT40_MSK 0x800 - -/* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */ -#define RATE_MCS_DUP_POS 12 -#define RATE_MCS_DUP_MSK 0x1000 - -/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ -#define RATE_MCS_SGI_POS 13 -#define RATE_MCS_SGI_MSK 0x2000 - -/** - * rate_n_flags Tx antenna masks - * 4965 has 2 transmitters - * 5100 has 1 transmitter B - * 5150 has 1 transmitter A - * 5300 has 3 transmitters - * 5350 has 3 transmitters - * bit14:16 - */ -#define RATE_MCS_ANT_POS 14 -#define RATE_MCS_ANT_A_MSK 0x04000 -#define RATE_MCS_ANT_B_MSK 0x08000 -#define RATE_MCS_ANT_C_MSK 0x10000 -#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | RATE_MCS_ANT_B_MSK) -#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | RATE_MCS_ANT_C_MSK) -#define RATE_ANT_NUM 3 - -#define POWER_TABLE_NUM_ENTRIES 33 -#define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32 -#define POWER_TABLE_CCK_ENTRY 32 - -#define IWL_PWR_NUM_HT_OFDM_ENTRIES 24 -#define IWL_PWR_CCK_ENTRIES 2 - -/** - * struct tx_power_dual_stream - * - * Table entries in REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH - * - * Same format as iwl_tx_power_dual_stream, but __le32 - */ -struct tx_power_dual_stream { - __le32 dw; -} __packed; - -/** - * Command REPLY_TX_POWER_DBM_CMD = 0x98 - * struct iwlagn_tx_power_dbm_cmd - */ -#define IWLAGN_TX_POWER_AUTO 0x7f -#define IWLAGN_TX_POWER_NO_CLOSED (0x1 << 6) - -struct iwlagn_tx_power_dbm_cmd { - s8 global_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */ - u8 flags; - s8 srv_chan_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */ - u8 reserved; -} __packed; - -/** - * Command TX_ANT_CONFIGURATION_CMD = 0x98 - * This command is used to configure valid Tx antenna. - * By default uCode concludes the valid antenna according to the radio flavor. - * This command enables the driver to override/modify this conclusion. - */ -struct iwl_tx_ant_config_cmd { - __le32 valid; -} __packed; - -/****************************************************************************** - * (0a) - * Alive and Error Commands & Responses: - * - *****************************************************************************/ - -#define UCODE_VALID_OK cpu_to_le32(0x1) - -/** - * REPLY_ALIVE = 0x1 (response only, not a command) - * - * uCode issues this "alive" notification once the runtime image is ready - * to receive commands from the driver. This is the *second* "alive" - * notification that the driver will receive after rebooting uCode; - * this "alive" is indicated by subtype field != 9. - * - * See comments documenting "BSM" (bootstrap state machine). - * - * This response includes two pointers to structures within the device's - * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging: - * - * 1) log_event_table_ptr indicates base of the event log. This traces - * a 256-entry history of uCode execution within a circular buffer. - * Its header format is: - * - * __le32 log_size; log capacity (in number of entries) - * __le32 type; (1) timestamp with each entry, (0) no timestamp - * __le32 wraps; # times uCode has wrapped to top of circular buffer - * __le32 write_index; next circular buffer entry that uCode would fill - * - * The header is followed by the circular buffer of log entries. Entries - * with timestamps have the following format: - * - * __le32 event_id; range 0 - 1500 - * __le32 timestamp; low 32 bits of TSF (of network, if associated) - * __le32 data; event_id-specific data value - * - * Entries without timestamps contain only event_id and data. - * - * - * 2) error_event_table_ptr indicates base of the error log. This contains - * information about any uCode error that occurs. For agn, the format - * of the error log is defined by struct iwl_error_event_table. - * - * The Linux driver can print both logs to the system log when a uCode error - * occurs. - */ - -/* - * Note: This structure is read from the device with IO accesses, - * and the reading already does the endian conversion. As it is - * read with u32-sized accesses, any members with a different size - * need to be ordered correctly though! - */ -struct iwl_error_event_table { - u32 valid; /* (nonzero) valid, (0) log is empty */ - u32 error_id; /* type of error */ - u32 pc; /* program counter */ - u32 blink1; /* branch link */ - u32 blink2; /* branch link */ - u32 ilink1; /* interrupt link */ - u32 ilink2; /* interrupt link */ - u32 data1; /* error-specific data */ - u32 data2; /* error-specific data */ - u32 line; /* source code line of error */ - u32 bcon_time; /* beacon timer */ - u32 tsf_low; /* network timestamp function timer */ - u32 tsf_hi; /* network timestamp function timer */ - u32 gp1; /* GP1 timer register */ - u32 gp2; /* GP2 timer register */ - u32 gp3; /* GP3 timer register */ - u32 ucode_ver; /* uCode version */ - u32 hw_ver; /* HW Silicon version */ - u32 brd_ver; /* HW board version */ - u32 log_pc; /* log program counter */ - u32 frame_ptr; /* frame pointer */ - u32 stack_ptr; /* stack pointer */ - u32 hcmd; /* last host command header */ - u32 isr0; /* isr status register LMPM_NIC_ISR0: - * rxtx_flag */ - u32 isr1; /* isr status register LMPM_NIC_ISR1: - * host_flag */ - u32 isr2; /* isr status register LMPM_NIC_ISR2: - * enc_flag */ - u32 isr3; /* isr status register LMPM_NIC_ISR3: - * time_flag */ - u32 isr4; /* isr status register LMPM_NIC_ISR4: - * wico interrupt */ - u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ - u32 wait_event; /* wait event() caller address */ - u32 l2p_control; /* L2pControlField */ - u32 l2p_duration; /* L2pDurationField */ - u32 l2p_mhvalid; /* L2pMhValidBits */ - u32 l2p_addr_match; /* L2pAddrMatchStat */ - u32 lmpm_pmg_sel; /* indicate which clocks are turned on - * (LMPM_PMG_SEL) */ - u32 u_timestamp; /* indicate when the date and time of the - * compilation */ - u32 flow_handler; /* FH read/write pointers, RX credit */ -} __packed; - -struct iwl_alive_resp { - u8 ucode_minor; - u8 ucode_major; - __le16 reserved1; - u8 sw_rev[8]; - u8 ver_type; - u8 ver_subtype; /* not "9" for runtime alive */ - __le16 reserved2; - __le32 log_event_table_ptr; /* SRAM address for event log */ - __le32 error_event_table_ptr; /* SRAM address for error log */ - __le32 timestamp; - __le32 is_valid; -} __packed; - -/* - * REPLY_ERROR = 0x2 (response only, not a command) - */ -struct iwl_error_resp { - __le32 error_type; - u8 cmd_id; - u8 reserved1; - __le16 bad_cmd_seq_num; - __le32 error_info; - __le64 timestamp; -} __packed; - -/****************************************************************************** - * (1) - * RXON Commands & Responses: - * - *****************************************************************************/ - -/* - * Rx config defines & structure - */ -/* rx_config device types */ -enum { - RXON_DEV_TYPE_AP = 1, - RXON_DEV_TYPE_ESS = 3, - RXON_DEV_TYPE_IBSS = 4, - RXON_DEV_TYPE_SNIFFER = 6, - RXON_DEV_TYPE_CP = 7, - RXON_DEV_TYPE_2STA = 8, - RXON_DEV_TYPE_P2P = 9, -}; - - -#define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0) -#define RXON_RX_CHAIN_DRIVER_FORCE_POS (0) -#define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1) -#define RXON_RX_CHAIN_VALID_POS (1) -#define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4) -#define RXON_RX_CHAIN_FORCE_SEL_POS (4) -#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7) -#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7) -#define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10) -#define RXON_RX_CHAIN_CNT_POS (10) -#define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12) -#define RXON_RX_CHAIN_MIMO_CNT_POS (12) -#define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14) -#define RXON_RX_CHAIN_MIMO_FORCE_POS (14) - -/* rx_config flags */ -/* band & modulation selection */ -#define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0) -#define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1) -/* auto detection enable */ -#define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2) -/* TGg protection when tx */ -#define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3) -/* cck short slot & preamble */ -#define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4) -#define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5) -/* antenna selection */ -#define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7) -#define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00) -#define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8) -#define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9) -/* radar detection enable */ -#define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12) -#define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13) -/* rx response to host with 8-byte TSF -* (according to ON_AIR deassertion) */ -#define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15) - - -/* HT flags */ -#define RXON_FLG_CTRL_CHANNEL_LOC_POS (22) -#define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22) - -#define RXON_FLG_HT_OPERATING_MODE_POS (23) - -#define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23) -#define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23) - -#define RXON_FLG_CHANNEL_MODE_POS (25) -#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) - -/* channel mode */ -enum { - CHANNEL_MODE_LEGACY = 0, - CHANNEL_MODE_PURE_40 = 1, - CHANNEL_MODE_MIXED = 2, - CHANNEL_MODE_RESERVED = 3, -}; -#define RXON_FLG_CHANNEL_MODE_LEGACY cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS) -#define RXON_FLG_CHANNEL_MODE_PURE_40 cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS) -#define RXON_FLG_CHANNEL_MODE_MIXED cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS) - -/* CTS to self (if spec allows) flag */ -#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30) - -/* rx_config filter flags */ -/* accept all data frames */ -#define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0) -/* pass control & management to host */ -#define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1) -/* accept multi-cast */ -#define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2) -/* don't decrypt uni-cast frames */ -#define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3) -/* don't decrypt multi-cast frames */ -#define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4) -/* STA is associated */ -#define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5) -/* transfer to host non bssid beacons in associated state */ -#define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6) - -/** - * REPLY_RXON = 0x10 (command, has simple generic response) - * - * RXON tunes the radio tuner to a service channel, and sets up a number - * of parameters that are used primarily for Rx, but also for Tx operations. - * - * NOTE: When tuning to a new channel, driver must set the - * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent - * info within the device, including the station tables, tx retry - * rate tables, and txpower tables. Driver must build a new station - * table and txpower table before transmitting anything on the RXON - * channel. - * - * NOTE: All RXONs wipe clean the internal txpower table. Driver must - * issue a new REPLY_TX_PWR_TABLE_CMD after each REPLY_RXON (0x10), - * regardless of whether RXON_FILTER_ASSOC_MSK is set. - */ - -struct iwl_rxon_cmd { - u8 node_addr[6]; - __le16 reserved1; - u8 bssid_addr[6]; - __le16 reserved2; - u8 wlap_bssid_addr[6]; - __le16 reserved3; - u8 dev_type; - u8 air_propagation; - __le16 rx_chain; - u8 ofdm_basic_rates; - u8 cck_basic_rates; - __le16 assoc_id; - __le32 flags; - __le32 filter_flags; - __le16 channel; - u8 ofdm_ht_single_stream_basic_rates; - u8 ofdm_ht_dual_stream_basic_rates; - u8 ofdm_ht_triple_stream_basic_rates; - u8 reserved5; - __le16 acquisition_data; - __le16 reserved6; -} __packed; - -/* - * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response) - */ -struct iwl_rxon_assoc_cmd { - __le32 flags; - __le32 filter_flags; - u8 ofdm_basic_rates; - u8 cck_basic_rates; - __le16 reserved1; - u8 ofdm_ht_single_stream_basic_rates; - u8 ofdm_ht_dual_stream_basic_rates; - u8 ofdm_ht_triple_stream_basic_rates; - u8 reserved2; - __le16 rx_chain_select_flags; - __le16 acquisition_data; - __le32 reserved3; -} __packed; - -#define IWL_CONN_MAX_LISTEN_INTERVAL 10 -#define IWL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */ - -/* - * REPLY_RXON_TIMING = 0x14 (command, has simple generic response) - */ -struct iwl_rxon_time_cmd { - __le64 timestamp; - __le16 beacon_interval; - __le16 atim_window; - __le32 beacon_init_val; - __le16 listen_interval; - u8 dtim_period; - u8 delta_cp_bss_tbtts; -} __packed; - -/* - * REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response) - */ -/** - * struct iwl5000_channel_switch_cmd - * @band: 0- 5.2GHz, 1- 2.4GHz - * @expect_beacon: 0- resume transmits after channel switch - * 1- wait for beacon to resume transmits - * @channel: new channel number - * @rxon_flags: Rx on flags - * @rxon_filter_flags: filtering parameters - * @switch_time: switch time in extended beacon format - * @reserved: reserved bytes - */ -struct iwl5000_channel_switch_cmd { - u8 band; - u8 expect_beacon; - __le16 channel; - __le32 rxon_flags; - __le32 rxon_filter_flags; - __le32 switch_time; - __le32 reserved[2][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; -} __packed; - -/** - * struct iwl6000_channel_switch_cmd - * @band: 0- 5.2GHz, 1- 2.4GHz - * @expect_beacon: 0- resume transmits after channel switch - * 1- wait for beacon to resume transmits - * @channel: new channel number - * @rxon_flags: Rx on flags - * @rxon_filter_flags: filtering parameters - * @switch_time: switch time in extended beacon format - * @reserved: reserved bytes - */ -struct iwl6000_channel_switch_cmd { - u8 band; - u8 expect_beacon; - __le16 channel; - __le32 rxon_flags; - __le32 rxon_filter_flags; - __le32 switch_time; - __le32 reserved[3][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; -} __packed; - -/* - * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command) - */ -struct iwl_csa_notification { - __le16 band; - __le16 channel; - __le32 status; /* 0 - OK, 1 - fail */ -} __packed; - -/****************************************************************************** - * (2) - * Quality-of-Service (QOS) Commands & Responses: - * - *****************************************************************************/ - -/** - * struct iwl_ac_qos -- QOS timing params for REPLY_QOS_PARAM - * One for each of 4 EDCA access categories in struct iwl_qosparam_cmd - * - * @cw_min: Contention window, start value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x0f. - * @cw_max: Contention window, max value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x3f. - * @aifsn: Number of slots in Arbitration Interframe Space (before - * performing random backoff timing prior to Tx). Device default 1. - * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. - * - * Device will automatically increase contention window by (2*CW) + 1 for each - * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW - * value, to cap the CW value. - */ -struct iwl_ac_qos { - __le16 cw_min; - __le16 cw_max; - u8 aifsn; - u8 reserved1; - __le16 edca_txop; -} __packed; - -/* QoS flags defines */ -#define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01) -#define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02) -#define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10) - -/* Number of Access Categories (AC) (EDCA), queues 0..3 */ -#define AC_NUM 4 - -/* - * REPLY_QOS_PARAM = 0x13 (command, has simple generic response) - * - * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs - * 0: Background, 1: Best Effort, 2: Video, 3: Voice. - */ -struct iwl_qosparam_cmd { - __le32 qos_flags; - struct iwl_ac_qos ac[AC_NUM]; -} __packed; - -/****************************************************************************** - * (3) - * Add/Modify Stations Commands & Responses: - * - *****************************************************************************/ -/* - * Multi station support - */ - -/* Special, dedicated locations within device's station table */ -#define IWL_AP_ID 0 -#define IWL_AP_ID_PAN 1 -#define IWL_STA_ID 2 -#define IWLAGN_PAN_BCAST_ID 14 -#define IWLAGN_BROADCAST_ID 15 -#define IWLAGN_STATION_COUNT 16 - -#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT - -#define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) -#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) -#define STA_FLG_PAN_STATION cpu_to_le32(1 << 13) -#define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17) -#define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18) -#define STA_FLG_MAX_AGG_SIZE_POS (19) -#define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19) -#define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21) -#define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22) -#define STA_FLG_AGG_MPDU_DENSITY_POS (23) -#define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23) - -/* Use in mode field. 1: modify existing entry, 0: add new station entry */ -#define STA_CONTROL_MODIFY_MSK 0x01 - -/* key flags __le16*/ -#define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007) -#define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000) -#define STA_KEY_FLG_WEP cpu_to_le16(0x0001) -#define STA_KEY_FLG_CCMP cpu_to_le16(0x0002) -#define STA_KEY_FLG_TKIP cpu_to_le16(0x0003) - -#define STA_KEY_FLG_KEYID_POS 8 -#define STA_KEY_FLG_INVALID cpu_to_le16(0x0800) -/* wep key is either from global key (0) or from station info array (1) */ -#define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008) - -/* wep key in STA: 5-bytes (0) or 13-bytes (1) */ -#define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000) -#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000) -#define STA_KEY_MAX_NUM 8 -#define STA_KEY_MAX_NUM_PAN 16 -/* must not match WEP_INVALID_OFFSET */ -#define IWLAGN_HW_KEY_DEFAULT 0xfe - -/* Flags indicate whether to modify vs. don't change various station params */ -#define STA_MODIFY_KEY_MASK 0x01 -#define STA_MODIFY_TID_DISABLE_TX 0x02 -#define STA_MODIFY_TX_RATE_MSK 0x04 -#define STA_MODIFY_ADDBA_TID_MSK 0x08 -#define STA_MODIFY_DELBA_TID_MSK 0x10 -#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 - -/* agn */ -struct iwl_keyinfo { - __le16 key_flags; - u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */ - u8 reserved1; - __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */ - u8 key_offset; - u8 reserved2; - u8 key[16]; /* 16-byte unicast decryption key */ - __le64 tx_secur_seq_cnt; - __le64 hw_tkip_mic_rx_key; - __le64 hw_tkip_mic_tx_key; -} __packed; - -/** - * struct sta_id_modify - * @addr[ETH_ALEN]: station's MAC address - * @sta_id: index of station in uCode's station table - * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change - * - * Driver selects unused table index when adding new station, - * or the index to a pre-existing station entry when modifying that station. - * Some indexes have special purposes (IWL_AP_ID, index 0, is for AP). - * - * modify_mask flags select which parameters to modify vs. leave alone. - */ -struct sta_id_modify { - u8 addr[ETH_ALEN]; - __le16 reserved1; - u8 sta_id; - u8 modify_mask; - __le16 reserved2; -} __packed; - -/* - * REPLY_ADD_STA = 0x18 (command) - * - * The device contains an internal table of per-station information, - * with info on security keys, aggregation parameters, and Tx rates for - * initial Tx attempt and any retries (agn devices uses - * REPLY_TX_LINK_QUALITY_CMD, - * - * REPLY_ADD_STA sets up the table entry for one station, either creating - * a new entry, or modifying a pre-existing one. - * - * NOTE: RXON command (without "associated" bit set) wipes the station table - * clean. Moving into RF_KILL state does this also. Driver must set up - * new station table before transmitting anything on the RXON channel - * (except active scans or active measurements; those commands carry - * their own txpower/rate setup data). - * - * When getting started on a new channel, driver must set up the - * IWL_BROADCAST_ID entry (last entry in the table). For a client - * station in a BSS, once an AP is selected, driver sets up the AP STA - * in the IWL_AP_ID entry (1st entry in the table). BROADCAST and AP - * are all that are needed for a BSS client station. If the device is - * used as AP, or in an IBSS network, driver must set up station table - * entries for all STAs in network, starting with index IWL_STA_ID. - */ - -struct iwl_addsta_cmd { - u8 mode; /* 1: modify existing, 0: add new station */ - u8 reserved[3]; - struct sta_id_modify sta; - struct iwl_keyinfo key; - __le32 station_flags; /* STA_FLG_* */ - __le32 station_flags_msk; /* STA_FLG_* */ - - /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) - * corresponding to bit (e.g. bit 5 controls TID 5). - * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ - __le16 tid_disable_tx; - __le16 legacy_reserved; - - /* TID for which to add block-ack support. - * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ - u8 add_immediate_ba_tid; - - /* TID for which to remove block-ack support. - * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ - u8 remove_immediate_ba_tid; - - /* Starting Sequence Number for added block-ack support. - * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ - __le16 add_immediate_ba_ssn; - - /* - * Number of packets OK to transmit to station even though - * it is asleep -- used to synchronise PS-poll and u-APSD - * responses while ucode keeps track of STA sleep state. - */ - __le16 sleep_tx_count; - - __le16 reserved2; -} __packed; - - -#define ADD_STA_SUCCESS_MSK 0x1 -#define ADD_STA_NO_ROOM_IN_TABLE 0x2 -#define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4 -#define ADD_STA_MODIFY_NON_EXIST_STA 0x8 -/* - * REPLY_ADD_STA = 0x18 (response) - */ -struct iwl_add_sta_resp { - u8 status; /* ADD_STA_* */ -} __packed; - -#define REM_STA_SUCCESS_MSK 0x1 -/* - * REPLY_REM_STA = 0x19 (response) - */ -struct iwl_rem_sta_resp { - u8 status; -} __packed; - -/* - * REPLY_REM_STA = 0x19 (command) - */ -struct iwl_rem_sta_cmd { - u8 num_sta; /* number of removed stations */ - u8 reserved[3]; - u8 addr[ETH_ALEN]; /* MAC addr of the first station */ - u8 reserved2[2]; -} __packed; - - -/* WiFi queues mask */ -#define IWL_SCD_BK_MSK BIT(0) -#define IWL_SCD_BE_MSK BIT(1) -#define IWL_SCD_VI_MSK BIT(2) -#define IWL_SCD_VO_MSK BIT(3) -#define IWL_SCD_MGMT_MSK BIT(3) - -/* PAN queues mask */ -#define IWL_PAN_SCD_BK_MSK BIT(4) -#define IWL_PAN_SCD_BE_MSK BIT(5) -#define IWL_PAN_SCD_VI_MSK BIT(6) -#define IWL_PAN_SCD_VO_MSK BIT(7) -#define IWL_PAN_SCD_MGMT_MSK BIT(7) -#define IWL_PAN_SCD_MULTICAST_MSK BIT(8) - -#define IWL_AGG_TX_QUEUE_MSK 0xffc00 - -#define IWL_DROP_ALL BIT(1) - -/* - * REPLY_TXFIFO_FLUSH = 0x1e(command and response) - * - * When using full FIFO flush this command checks the scheduler HW block WR/RD - * pointers to check if all the frames were transferred by DMA into the - * relevant TX FIFO queue. Only when the DMA is finished and the queue is - * empty the command can finish. - * This command is used to flush the TXFIFO from transmit commands, it may - * operate on single or multiple queues, the command queue can't be flushed by - * this command. The command response is returned when all the queue flush - * operations are done. Each TX command flushed return response with the FLUSH - * status set in the TX response status. When FIFO flush operation is used, - * the flush operation ends when both the scheduler DMA done and TXFIFO empty - * are set. - * - * @queue_control: bit mask for which queues to flush - * @flush_control: flush controls - * 0: Dump single MSDU - * 1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable. - * 2: Dump all FIFO - */ -struct iwl_txfifo_flush_cmd_v3 { - __le32 queue_control; - __le16 flush_control; - __le16 reserved; -} __packed; - -struct iwl_txfifo_flush_cmd_v2 { - __le16 queue_control; - __le16 flush_control; -} __packed; - -/* - * REPLY_WEP_KEY = 0x20 - */ -struct iwl_wep_key { - u8 key_index; - u8 key_offset; - u8 reserved1[2]; - u8 key_size; - u8 reserved2[3]; - u8 key[16]; -} __packed; - -struct iwl_wep_cmd { - u8 num_keys; - u8 global_key_type; - u8 flags; - u8 reserved; - struct iwl_wep_key key[0]; -} __packed; - -#define WEP_KEY_WEP_TYPE 1 -#define WEP_KEYS_MAX 4 -#define WEP_INVALID_OFFSET 0xff -#define WEP_KEY_LEN_64 5 -#define WEP_KEY_LEN_128 13 - -/****************************************************************************** - * (4) - * Rx Responses: - * - *****************************************************************************/ - -#define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0) -#define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1) - -#define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0) -#define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) -#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) -#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) -#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0x70 -#define RX_RES_PHY_FLAGS_ANTENNA_POS 4 -#define RX_RES_PHY_FLAGS_AGG_MSK cpu_to_le16(1 << 7) - -#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) -#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) -#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8) -#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8) -#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8) -#define RX_RES_STATUS_SEC_TYPE_ERR (0x7 << 8) - -#define RX_RES_STATUS_STATION_FOUND (1<<6) -#define RX_RES_STATUS_NO_STATION_INFO_MISMATCH (1<<7) - -#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11) -#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11) -#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11) -#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11) -#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11) - -#define RX_MPDU_RES_STATUS_ICV_OK (0x20) -#define RX_MPDU_RES_STATUS_MIC_OK (0x40) -#define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7) -#define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800) - - -#define IWLAGN_RX_RES_PHY_CNT 8 -#define IWLAGN_RX_RES_AGC_IDX 1 -#define IWLAGN_RX_RES_RSSI_AB_IDX 2 -#define IWLAGN_RX_RES_RSSI_C_IDX 3 -#define IWLAGN_OFDM_AGC_MSK 0xfe00 -#define IWLAGN_OFDM_AGC_BIT_POS 9 -#define IWLAGN_OFDM_RSSI_INBAND_A_BITMSK 0x00ff -#define IWLAGN_OFDM_RSSI_ALLBAND_A_BITMSK 0xff00 -#define IWLAGN_OFDM_RSSI_A_BIT_POS 0 -#define IWLAGN_OFDM_RSSI_INBAND_B_BITMSK 0xff0000 -#define IWLAGN_OFDM_RSSI_ALLBAND_B_BITMSK 0xff000000 -#define IWLAGN_OFDM_RSSI_B_BIT_POS 16 -#define IWLAGN_OFDM_RSSI_INBAND_C_BITMSK 0x00ff -#define IWLAGN_OFDM_RSSI_ALLBAND_C_BITMSK 0xff00 -#define IWLAGN_OFDM_RSSI_C_BIT_POS 0 - -struct iwlagn_non_cfg_phy { - __le32 non_cfg_phy[IWLAGN_RX_RES_PHY_CNT]; /* up to 8 phy entries */ -} __packed; - - -/* - * REPLY_RX = 0xc3 (response only, not a command) - * Used only for legacy (non 11n) frames. - */ -struct iwl_rx_phy_res { - u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */ - u8 cfg_phy_cnt; /* configurable DSP phy data byte count */ - u8 stat_id; /* configurable DSP phy data set ID */ - u8 reserved1; - __le64 timestamp; /* TSF at on air rise */ - __le32 beacon_time_stamp; /* beacon at on-air rise */ - __le16 phy_flags; /* general phy flags: band, modulation, ... */ - __le16 channel; /* channel number */ - u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */ - __le32 rate_n_flags; /* RATE_MCS_* */ - __le16 byte_count; /* frame's byte-count */ - __le16 frame_time; /* frame's time on the air */ -} __packed; - -struct iwl_rx_mpdu_res_start { - __le16 byte_count; - __le16 reserved; -} __packed; - - -/****************************************************************************** - * (5) - * Tx Commands & Responses: - * - * Driver must place each REPLY_TX command into one of the prioritized Tx - * queues in host DRAM, shared between driver and device (see comments for - * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode - * are preparing to transmit, the device pulls the Tx command over the PCI - * bus via one of the device's Tx DMA channels, to fill an internal FIFO - * from which data will be transmitted. - * - * uCode handles all timing and protocol related to control frames - * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler - * handle reception of block-acks; uCode updates the host driver via - * REPLY_COMPRESSED_BA. - * - * uCode handles retrying Tx when an ACK is expected but not received. - * This includes trying lower data rates than the one requested in the Tx - * command, as set up by the REPLY_TX_LINK_QUALITY_CMD (agn). - * - * Driver sets up transmit power for various rates via REPLY_TX_PWR_TABLE_CMD. - * This command must be executed after every RXON command, before Tx can occur. - *****************************************************************************/ - -/* REPLY_TX Tx flags field */ - -/* - * 1: Use RTS/CTS protocol or CTS-to-self if spec allows it - * before this frame. if CTS-to-self required check - * RXON_FLG_SELF_CTS_EN status. - */ -#define TX_CMD_FLG_PROT_REQUIRE_MSK cpu_to_le32(1 << 0) - -/* 1: Expect ACK from receiving station - * 0: Don't expect ACK (MAC header's duration field s/b 0) - * Set this for unicast frames, but not broadcast/multicast. */ -#define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3) - -/* For agn devices: - * 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD). - * Tx command's initial_rate_index indicates first rate to try; - * uCode walks through table for additional Tx attempts. - * 0: Use Tx rate/MCS from Tx command's rate_n_flags field. - * This rate will be used for all Tx attempts; it will not be scaled. */ -#define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4) - -/* 1: Expect immediate block-ack. - * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ -#define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6) - -/* Tx antenna selection field; reserved (0) for agn devices. */ -#define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00) - -/* 1: Ignore Bluetooth priority for this frame. - * 0: Delay Tx until Bluetooth device is done (normal usage). */ -#define TX_CMD_FLG_IGNORE_BT cpu_to_le32(1 << 12) - -/* 1: uCode overrides sequence control field in MAC header. - * 0: Driver provides sequence control field in MAC header. - * Set this for management frames, non-QOS data frames, non-unicast frames, - * and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */ -#define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13) - -/* 1: This frame is non-last MPDU; more fragments are coming. - * 0: Last fragment, or not using fragmentation. */ -#define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14) - -/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame. - * 0: No TSF required in outgoing frame. - * Set this for transmitting beacons and probe responses. */ -#define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16) - -/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword - * alignment of frame's payload data field. - * 0: No pad - * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4 - * field (but not both). Driver must align frame data (i.e. data following - * MAC header) to DWORD boundary. */ -#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20) - -/* accelerate aggregation support - * 0 - no CCMP encryption; 1 - CCMP encryption */ -#define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22) - -/* HCCA-AP - disable duration overwriting. */ -#define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25) - - -/* - * TX command security control - */ -#define TX_CMD_SEC_WEP 0x01 -#define TX_CMD_SEC_CCM 0x02 -#define TX_CMD_SEC_TKIP 0x03 -#define TX_CMD_SEC_MSK 0x03 -#define TX_CMD_SEC_SHIFT 6 -#define TX_CMD_SEC_KEY128 0x08 - -/* - * REPLY_TX = 0x1c (command) - */ - -/* - * 4965 uCode updates these Tx attempt count values in host DRAM. - * Used for managing Tx retries when expecting block-acks. - * Driver should set these fields to 0. - */ -struct iwl_dram_scratch { - u8 try_cnt; /* Tx attempts */ - u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */ - __le16 reserved; -} __packed; - -struct iwl_tx_cmd { - /* - * MPDU byte count: - * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, - * + 8 byte IV for CCM or TKIP (not used for WEP) - * + Data payload - * + 8-byte MIC (not used for CCM/WEP) - * NOTE: Does not include Tx command bytes, post-MAC pad bytes, - * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i - * Range: 14-2342 bytes. - */ - __le16 len; - - /* - * MPDU or MSDU byte count for next frame. - * Used for fragmentation and bursting, but not 11n aggregation. - * Same as "len", but for next frame. Set to 0 if not applicable. - */ - __le16 next_frame_len; - - __le32 tx_flags; /* TX_CMD_FLG_* */ - - /* uCode may modify this field of the Tx command (in host DRAM!). - * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ - struct iwl_dram_scratch scratch; - - /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ - __le32 rate_n_flags; /* RATE_MCS_* */ - - /* Index of destination station in uCode's station table */ - u8 sta_id; - - /* Type of security encryption: CCM or TKIP */ - u8 sec_ctl; /* TX_CMD_SEC_* */ - - /* - * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial - * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for - * data frames, this field may be used to selectively reduce initial - * rate (via non-0 value) for special frames (e.g. management), while - * still supporting rate scaling for all frames. - */ - u8 initial_rate_index; - u8 reserved; - u8 key[16]; - __le16 next_frame_flags; - __le16 reserved2; - union { - __le32 life_time; - __le32 attempt; - } stop_time; - - /* Host DRAM physical address pointer to "scratch" in this command. - * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ - __le32 dram_lsb_ptr; - u8 dram_msb_ptr; - - u8 rts_retry_limit; /*byte 50 */ - u8 data_retry_limit; /*byte 51 */ - u8 tid_tspec; - union { - __le16 pm_frame_timeout; - __le16 attempt_duration; - } timeout; - - /* - * Duration of EDCA burst Tx Opportunity, in 32-usec units. - * Set this if txop time is not specified by HCCA protocol (e.g. by AP). - */ - __le16 driver_txop; - - /* - * MAC header goes here, followed by 2 bytes padding if MAC header - * length is 26 or 30 bytes, followed by payload data - */ - u8 payload[0]; - struct ieee80211_hdr hdr[0]; -} __packed; - -/* - * TX command response is sent after *agn* transmission attempts. - * - * both postpone and abort status are expected behavior from uCode. there is - * no special operation required from driver; except for RFKILL_FLUSH, - * which required tx flush host command to flush all the tx frames in queues - */ -enum { - TX_STATUS_SUCCESS = 0x01, - TX_STATUS_DIRECT_DONE = 0x02, - /* postpone TX */ - TX_STATUS_POSTPONE_DELAY = 0x40, - TX_STATUS_POSTPONE_FEW_BYTES = 0x41, - TX_STATUS_POSTPONE_BT_PRIO = 0x42, - TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, - TX_STATUS_POSTPONE_CALC_TTAK = 0x44, - /* abort TX */ - TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, - TX_STATUS_FAIL_SHORT_LIMIT = 0x82, - TX_STATUS_FAIL_LONG_LIMIT = 0x83, - TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84, - TX_STATUS_FAIL_DRAIN_FLOW = 0x85, - TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, - TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, - TX_STATUS_FAIL_DEST_PS = 0x88, - TX_STATUS_FAIL_HOST_ABORTED = 0x89, - TX_STATUS_FAIL_BT_RETRY = 0x8a, - TX_STATUS_FAIL_STA_INVALID = 0x8b, - TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, - TX_STATUS_FAIL_TID_DISABLE = 0x8d, - TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, - TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, - TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90, - TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, -}; - -#define TX_PACKET_MODE_REGULAR 0x0000 -#define TX_PACKET_MODE_BURST_SEQ 0x0100 -#define TX_PACKET_MODE_BURST_FIRST 0x0200 - -enum { - TX_POWER_PA_NOT_ACTIVE = 0x0, -}; - -enum { - TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */ - TX_STATUS_DELAY_MSK = 0x00000040, - TX_STATUS_ABORT_MSK = 0x00000080, - TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */ - TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */ - TX_RESERVED = 0x00780000, /* bits 19:22 */ - TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */ - TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */ -}; - -/* ******************************* - * TX aggregation status - ******************************* */ - -enum { - AGG_TX_STATE_TRANSMITTED = 0x00, - AGG_TX_STATE_UNDERRUN_MSK = 0x01, - AGG_TX_STATE_BT_PRIO_MSK = 0x02, - AGG_TX_STATE_FEW_BYTES_MSK = 0x04, - AGG_TX_STATE_ABORT_MSK = 0x08, - AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10, - AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20, - AGG_TX_STATE_LAST_SENT_BT_KILL_MSK = 0x40, - AGG_TX_STATE_SCD_QUERY_MSK = 0x80, - AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100, - AGG_TX_STATE_RESPONSE_MSK = 0x1ff, - AGG_TX_STATE_DUMP_TX_MSK = 0x200, - AGG_TX_STATE_DELAY_TX_MSK = 0x400 -}; - -#define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */ -#define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */ -#define AGG_TX_TRY_POS 12 - -#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \ - AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \ - AGG_TX_STATE_LAST_SENT_BT_KILL_MSK) - -/* # tx attempts for first frame in aggregation */ -#define AGG_TX_STATE_TRY_CNT_POS 12 -#define AGG_TX_STATE_TRY_CNT_MSK 0xf000 - -/* Command ID and sequence number of Tx command for this frame */ -#define AGG_TX_STATE_SEQ_NUM_POS 16 -#define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 - -/* - * REPLY_TX = 0x1c (response) - * - * This response may be in one of two slightly different formats, indicated - * by the frame_count field: - * - * 1) No aggregation (frame_count == 1). This reports Tx results for - * a single frame. Multiple attempts, at various bit rates, may have - * been made for this frame. - * - * 2) Aggregation (frame_count > 1). This reports Tx results for - * 2 or more frames that used block-acknowledge. All frames were - * transmitted at same rate. Rate scaling may have been used if first - * frame in this new agg block failed in previous agg block(s). - * - * Note that, for aggregation, ACK (block-ack) status is not delivered here; - * block-ack has not been received by the time the agn device records - * this status. - * This status relates to reasons the tx might have been blocked or aborted - * within the sending station (this agn device), rather than whether it was - * received successfully by the destination station. - */ -struct agg_tx_status { - __le16 status; - __le16 sequence; -} __packed; - -/* - * definitions for initial rate index field - * bits [3:0] initial rate index - * bits [6:4] rate table color, used for the initial rate - * bit-7 invalid rate indication - * i.e. rate was not chosen from rate table - * or rate table color was changed during frame retries - * refer tlc rate info - */ - -#define IWL50_TX_RES_INIT_RATE_INDEX_POS 0 -#define IWL50_TX_RES_INIT_RATE_INDEX_MSK 0x0f -#define IWL50_TX_RES_RATE_TABLE_COLOR_POS 4 -#define IWL50_TX_RES_RATE_TABLE_COLOR_MSK 0x70 -#define IWL50_TX_RES_INV_RATE_INDEX_MSK 0x80 - -/* refer to ra_tid */ -#define IWLAGN_TX_RES_TID_POS 0 -#define IWLAGN_TX_RES_TID_MSK 0x0f -#define IWLAGN_TX_RES_RA_POS 4 -#define IWLAGN_TX_RES_RA_MSK 0xf0 - -struct iwlagn_tx_resp { - u8 frame_count; /* 1 no aggregation, >1 aggregation */ - u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */ - u8 failure_rts; /* # failures due to unsuccessful RTS */ - u8 failure_frame; /* # failures due to no ACK (unused for agg) */ - - /* For non-agg: Rate at which frame was successful. - * For agg: Rate at which all frames were transmitted. */ - __le32 rate_n_flags; /* RATE_MCS_* */ - - /* For non-agg: RTS + CTS + frame tx attempts time + ACK. - * For agg: RTS + CTS + aggregation tx time + block-ack time. */ - __le16 wireless_media_time; /* uSecs */ - - u8 pa_status; /* RF power amplifier measurement (not used) */ - u8 pa_integ_res_a[3]; - u8 pa_integ_res_b[3]; - u8 pa_integ_res_C[3]; - - __le32 tfd_info; - __le16 seq_ctl; - __le16 byte_cnt; - u8 tlc_info; - u8 ra_tid; /* tid (0:3), sta_id (4:7) */ - __le16 frame_ctrl; - /* - * For non-agg: frame status TX_STATUS_* - * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status - * fields follow this one, up to frame_count. - * Bit fields: - * 11- 0: AGG_TX_STATE_* status code - * 15-12: Retry count for 1st frame in aggregation (retries - * occur if tx failed for this frame when it was a - * member of a previous aggregation block). If rate - * scaling is used, retry count indicates the rate - * table entry used for all frames in the new agg. - * 31-16: Sequence # for this frame's Tx cmd (not SSN!) - */ - struct agg_tx_status status; /* TX status (in aggregation - - * status of 1st frame) */ -} __packed; -/* - * REPLY_COMPRESSED_BA = 0xc5 (response only, not a command) - * - * Reports Block-Acknowledge from recipient station - */ -struct iwl_compressed_ba_resp { - __le32 sta_addr_lo32; - __le16 sta_addr_hi16; - __le16 reserved; - - /* Index of recipient (BA-sending) station in uCode's station table */ - u8 sta_id; - u8 tid; - __le16 seq_ctl; - __le64 bitmap; - __le16 scd_flow; - __le16 scd_ssn; - u8 txed; /* number of frames sent */ - u8 txed_2_done; /* number of frames acked */ - __le16 reserved1; -} __packed; - -/* - * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response) - * - */ - -/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */ -#define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1 << 0) - -/* # of EDCA prioritized tx fifos */ -#define LINK_QUAL_AC_NUM AC_NUM - -/* # entries in rate scale table to support Tx retries */ -#define LINK_QUAL_MAX_RETRY_NUM 16 - -/* Tx antenna selection values */ -#define LINK_QUAL_ANT_A_MSK (1 << 0) -#define LINK_QUAL_ANT_B_MSK (1 << 1) -#define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK) - - -/** - * struct iwl_link_qual_general_params - * - * Used in REPLY_TX_LINK_QUALITY_CMD - */ -struct iwl_link_qual_general_params { - u8 flags; - - /* No entries at or above this (driver chosen) index contain MIMO */ - u8 mimo_delimiter; - - /* Best single antenna to use for single stream (legacy, SISO). */ - u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */ - - /* Best antennas to use for MIMO (unused for 4965, assumes both). */ - u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */ - - /* - * If driver needs to use different initial rates for different - * EDCA QOS access categories (as implemented by tx fifos 0-3), - * this table will set that up, by indicating the indexes in the - * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start. - * Otherwise, driver should set all entries to 0. - * - * Entry usage: - * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice - * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3. - */ - u8 start_rate_index[LINK_QUAL_AC_NUM]; -} __packed; - -#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ -#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) -#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) - -#define LINK_QUAL_AGG_DISABLE_START_DEF (3) -#define LINK_QUAL_AGG_DISABLE_START_MAX (255) -#define LINK_QUAL_AGG_DISABLE_START_MIN (0) - -#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) -#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) -#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) - -/** - * struct iwl_link_qual_agg_params - * - * Used in REPLY_TX_LINK_QUALITY_CMD - */ -struct iwl_link_qual_agg_params { - - /* - *Maximum number of uSec in aggregation. - * default set to 4000 (4 milliseconds) if not configured in .cfg - */ - __le16 agg_time_limit; - - /* - * Number of Tx retries allowed for a frame, before that frame will - * no longer be considered for the start of an aggregation sequence - * (scheduler will then try to tx it as single frame). - * Driver should set this to 3. - */ - u8 agg_dis_start_th; - - /* - * Maximum number of frames in aggregation. - * 0 = no limit (default). 1 = no aggregation. - * Other values = max # frames in aggregation. - */ - u8 agg_frame_cnt_limit; - - __le32 reserved; -} __packed; - -/* - * REPLY_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response) - * - * For agn devices - * - * Each station in the agn device's internal station table has its own table - * of 16 - * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when - * an ACK is not received. This command replaces the entire table for - * one station. - * - * NOTE: Station must already be in agn device's station table. - * Use REPLY_ADD_STA. - * - * The rate scaling procedures described below work well. Of course, other - * procedures are possible, and may work better for particular environments. - * - * - * FILLING THE RATE TABLE - * - * Given a particular initial rate and mode, as determined by the rate - * scaling algorithm described below, the Linux driver uses the following - * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the - * Link Quality command: - * - * - * 1) If using High-throughput (HT) (SISO or MIMO) initial rate: - * a) Use this same initial rate for first 3 entries. - * b) Find next lower available rate using same mode (SISO or MIMO), - * use for next 3 entries. If no lower rate available, switch to - * legacy mode (no HT40 channel, no MIMO, no short guard interval). - * c) If using MIMO, set command's mimo_delimiter to number of entries - * using MIMO (3 or 6). - * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel, - * no MIMO, no short guard interval), at the next lower bit rate - * (e.g. if second HT bit rate was 54, try 48 legacy), and follow - * legacy procedure for remaining table entries. - * - * 2) If using legacy initial rate: - * a) Use the initial rate for only one entry. - * b) For each following entry, reduce the rate to next lower available - * rate, until reaching the lowest available rate. - * c) When reducing rate, also switch antenna selection. - * d) Once lowest available rate is reached, repeat this rate until - * rate table is filled (16 entries), switching antenna each entry. - * - * - * ACCUMULATING HISTORY - * - * The rate scaling algorithm for agn devices, as implemented in Linux driver, - * uses two sets of frame Tx success history: One for the current/active - * modulation mode, and one for a speculative/search mode that is being - * attempted. If the speculative mode turns out to be more effective (i.e. - * actual transfer rate is better), then the driver continues to use the - * speculative mode as the new current active mode. - * - * Each history set contains, separately for each possible rate, data for a - * sliding window of the 62 most recent tx attempts at that rate. The data - * includes a shifting bitmap of success(1)/failure(0), and sums of successful - * and attempted frames, from which the driver can additionally calculate a - * success ratio (success / attempted) and number of failures - * (attempted - success), and control the size of the window (attempted). - * The driver uses the bit map to remove successes from the success sum, as - * the oldest tx attempts fall out of the window. - * - * When the agn device makes multiple tx attempts for a given frame, each - * attempt might be at a different rate, and have different modulation - * characteristics (e.g. antenna, fat channel, short guard interval), as set - * up in the rate scaling table in the Link Quality command. The driver must - * determine which rate table entry was used for each tx attempt, to determine - * which rate-specific history to update, and record only those attempts that - * match the modulation characteristics of the history set. - * - * When using block-ack (aggregation), all frames are transmitted at the same - * rate, since there is no per-attempt acknowledgment from the destination - * station. The Tx response struct iwl_tx_resp indicates the Tx rate in - * rate_n_flags field. After receiving a block-ack, the driver can update - * history for the entire block all at once. - * - * - * FINDING BEST STARTING RATE: - * - * When working with a selected initial modulation mode (see below), the - * driver attempts to find a best initial rate. The initial rate is the - * first entry in the Link Quality command's rate table. - * - * 1) Calculate actual throughput (success ratio * expected throughput, see - * table below) for current initial rate. Do this only if enough frames - * have been attempted to make the value meaningful: at least 6 failed - * tx attempts, or at least 8 successes. If not enough, don't try rate - * scaling yet. - * - * 2) Find available rates adjacent to current initial rate. Available means: - * a) supported by hardware && - * b) supported by association && - * c) within any constraints selected by user - * - * 3) Gather measured throughputs for adjacent rates. These might not have - * enough history to calculate a throughput. That's okay, we might try - * using one of them anyway! - * - * 4) Try decreasing rate if, for current rate: - * a) success ratio is < 15% || - * b) lower adjacent rate has better measured throughput || - * c) higher adjacent rate has worse throughput, and lower is unmeasured - * - * As a sanity check, if decrease was determined above, leave rate - * unchanged if: - * a) lower rate unavailable - * b) success ratio at current rate > 85% (very good) - * c) current measured throughput is better than expected throughput - * of lower rate (under perfect 100% tx conditions, see table below) - * - * 5) Try increasing rate if, for current rate: - * a) success ratio is < 15% || - * b) both adjacent rates' throughputs are unmeasured (try it!) || - * b) higher adjacent rate has better measured throughput || - * c) lower adjacent rate has worse throughput, and higher is unmeasured - * - * As a sanity check, if increase was determined above, leave rate - * unchanged if: - * a) success ratio at current rate < 70%. This is not particularly - * good performance; higher rate is sure to have poorer success. - * - * 6) Re-evaluate the rate after each tx frame. If working with block- - * acknowledge, history and statistics may be calculated for the entire - * block (including prior history that fits within the history windows), - * before re-evaluation. - * - * FINDING BEST STARTING MODULATION MODE: - * - * After working with a modulation mode for a "while" (and doing rate scaling), - * the driver searches for a new initial mode in an attempt to improve - * throughput. The "while" is measured by numbers of attempted frames: - * - * For legacy mode, search for new mode after: - * 480 successful frames, or 160 failed frames - * For high-throughput modes (SISO or MIMO), search for new mode after: - * 4500 successful frames, or 400 failed frames - * - * Mode switch possibilities are (3 for each mode): - * - * For legacy: - * Change antenna, try SISO (if HT association), try MIMO (if HT association) - * For SISO: - * Change antenna, try MIMO, try shortened guard interval (SGI) - * For MIMO: - * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI) - * - * When trying a new mode, use the same bit rate as the old/current mode when - * trying antenna switches and shortened guard interval. When switching to - * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate - * for which the expected throughput (under perfect conditions) is about the - * same or slightly better than the actual measured throughput delivered by - * the old/current mode. - * - * Actual throughput can be estimated by multiplying the expected throughput - * by the success ratio (successful / attempted tx frames). Frame size is - * not considered in this calculation; it assumes that frame size will average - * out to be fairly consistent over several samples. The following are - * metric values for expected throughput assuming 100% success ratio. - * Only G band has support for CCK rates: - * - * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60 - * - * G: 7 13 35 58 40 57 72 98 121 154 177 186 186 - * A: 0 0 0 0 40 57 72 98 121 154 177 186 186 - * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202 - * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211 - * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251 - * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257 - * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257 - * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264 - * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289 - * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293 - * - * After the new mode has been tried for a short while (minimum of 6 failed - * frames or 8 successful frames), compare success ratio and actual throughput - * estimate of the new mode with the old. If either is better with the new - * mode, continue to use the new mode. - * - * Continue comparing modes until all 3 possibilities have been tried. - * If moving from legacy to HT, try all 3 possibilities from the new HT - * mode. After trying all 3, a best mode is found. Continue to use this mode - * for the longer "while" described above (e.g. 480 successful frames for - * legacy), and then repeat the search process. - * - */ -struct iwl_link_quality_cmd { - - /* Index of destination/recipient station in uCode's station table */ - u8 sta_id; - u8 reserved1; - __le16 control; /* not used */ - struct iwl_link_qual_general_params general_params; - struct iwl_link_qual_agg_params agg_params; - - /* - * Rate info; when using rate-scaling, Tx command's initial_rate_index - * specifies 1st Tx rate attempted, via index into this table. - * agn devices works its way through table when retrying Tx. - */ - struct { - __le32 rate_n_flags; /* RATE_MCS_*, IWL_RATE_* */ - } rs_table[LINK_QUAL_MAX_RETRY_NUM]; - __le32 reserved2; -} __packed; - -/* - * BT configuration enable flags: - * bit 0 - 1: BT channel announcement enabled - * 0: disable - * bit 1 - 1: priority of BT device enabled - * 0: disable - * bit 2 - 1: BT 2 wire support enabled - * 0: disable - */ -#define BT_COEX_DISABLE (0x0) -#define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0) -#define BT_ENABLE_PRIORITY BIT(1) -#define BT_ENABLE_2_WIRE BIT(2) - -#define BT_COEX_DISABLE (0x0) -#define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY) - -#define BT_LEAD_TIME_MIN (0x0) -#define BT_LEAD_TIME_DEF (0x1E) -#define BT_LEAD_TIME_MAX (0xFF) - -#define BT_MAX_KILL_MIN (0x1) -#define BT_MAX_KILL_DEF (0x5) -#define BT_MAX_KILL_MAX (0xFF) - -#define BT_DURATION_LIMIT_DEF 625 -#define BT_DURATION_LIMIT_MAX 1250 -#define BT_DURATION_LIMIT_MIN 625 - -#define BT_ON_THRESHOLD_DEF 4 -#define BT_ON_THRESHOLD_MAX 1000 -#define BT_ON_THRESHOLD_MIN 1 - -#define BT_FRAG_THRESHOLD_DEF 0 -#define BT_FRAG_THRESHOLD_MAX 0 -#define BT_FRAG_THRESHOLD_MIN 0 - -#define BT_AGG_THRESHOLD_DEF 1200 -#define BT_AGG_THRESHOLD_MAX 8000 -#define BT_AGG_THRESHOLD_MIN 400 - -/* - * REPLY_BT_CONFIG = 0x9b (command, has simple generic response) - * - * agn devices support hardware handshake with Bluetooth device on - * same platform. Bluetooth device alerts wireless device when it will Tx; - * wireless device can delay or kill its own Tx to accommodate. - */ -struct iwl_bt_cmd { - u8 flags; - u8 lead_time; - u8 max_kill; - u8 reserved; - __le32 kill_ack_mask; - __le32 kill_cts_mask; -} __packed; - -#define IWLAGN_BT_FLAG_CHANNEL_INHIBITION BIT(0) - -#define IWLAGN_BT_FLAG_COEX_MODE_MASK (BIT(3)|BIT(4)|BIT(5)) -#define IWLAGN_BT_FLAG_COEX_MODE_SHIFT 3 -#define IWLAGN_BT_FLAG_COEX_MODE_DISABLED 0 -#define IWLAGN_BT_FLAG_COEX_MODE_LEGACY_2W 1 -#define IWLAGN_BT_FLAG_COEX_MODE_3W 2 -#define IWLAGN_BT_FLAG_COEX_MODE_4W 3 - -#define IWLAGN_BT_FLAG_UCODE_DEFAULT BIT(6) -/* Disable Sync PSPoll on SCO/eSCO */ -#define IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE BIT(7) - -#define IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD -75 /* dBm */ -#define IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD -65 /* dBm */ - -#define IWLAGN_BT_PRIO_BOOST_MAX 0xFF -#define IWLAGN_BT_PRIO_BOOST_MIN 0x00 -#define IWLAGN_BT_PRIO_BOOST_DEFAULT 0xF0 -#define IWLAGN_BT_PRIO_BOOST_DEFAULT32 0xF0F0F0F0 - -#define IWLAGN_BT_MAX_KILL_DEFAULT 5 - -#define IWLAGN_BT3_T7_DEFAULT 1 - -enum iwl_bt_kill_idx { - IWL_BT_KILL_DEFAULT = 0, - IWL_BT_KILL_OVERRIDE = 1, - IWL_BT_KILL_REDUCE = 2, -}; - -#define IWLAGN_BT_KILL_ACK_MASK_DEFAULT cpu_to_le32(0xffff0000) -#define IWLAGN_BT_KILL_CTS_MASK_DEFAULT cpu_to_le32(0xffff0000) -#define IWLAGN_BT_KILL_ACK_CTS_MASK_SCO cpu_to_le32(0xffffffff) -#define IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE cpu_to_le32(0) - -#define IWLAGN_BT3_PRIO_SAMPLE_DEFAULT 2 - -#define IWLAGN_BT3_T2_DEFAULT 0xc - -#define IWLAGN_BT_VALID_ENABLE_FLAGS cpu_to_le16(BIT(0)) -#define IWLAGN_BT_VALID_BOOST cpu_to_le16(BIT(1)) -#define IWLAGN_BT_VALID_MAX_KILL cpu_to_le16(BIT(2)) -#define IWLAGN_BT_VALID_3W_TIMERS cpu_to_le16(BIT(3)) -#define IWLAGN_BT_VALID_KILL_ACK_MASK cpu_to_le16(BIT(4)) -#define IWLAGN_BT_VALID_KILL_CTS_MASK cpu_to_le16(BIT(5)) -#define IWLAGN_BT_VALID_REDUCED_TX_PWR cpu_to_le16(BIT(6)) -#define IWLAGN_BT_VALID_3W_LUT cpu_to_le16(BIT(7)) - -#define IWLAGN_BT_ALL_VALID_MSK (IWLAGN_BT_VALID_ENABLE_FLAGS | \ - IWLAGN_BT_VALID_BOOST | \ - IWLAGN_BT_VALID_MAX_KILL | \ - IWLAGN_BT_VALID_3W_TIMERS | \ - IWLAGN_BT_VALID_KILL_ACK_MASK | \ - IWLAGN_BT_VALID_KILL_CTS_MASK | \ - IWLAGN_BT_VALID_REDUCED_TX_PWR | \ - IWLAGN_BT_VALID_3W_LUT) - -#define IWLAGN_BT_REDUCED_TX_PWR BIT(0) - -#define IWLAGN_BT_DECISION_LUT_SIZE 12 - -struct iwl_basic_bt_cmd { - u8 flags; - u8 ledtime; /* unused */ - u8 max_kill; - u8 bt3_timer_t7_value; - __le32 kill_ack_mask; - __le32 kill_cts_mask; - u8 bt3_prio_sample_time; - u8 bt3_timer_t2_value; - __le16 bt4_reaction_time; /* unused */ - __le32 bt3_lookup_table[IWLAGN_BT_DECISION_LUT_SIZE]; - /* - * bit 0: use reduced tx power for control frame - * bit 1 - 7: reserved - */ - u8 reduce_txpower; - u8 reserved; - __le16 valid; -}; - -struct iwl_bt_cmd_v1 { - struct iwl_basic_bt_cmd basic; - u8 prio_boost; - /* - * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask - * if configure the following patterns - */ - u8 tx_prio_boost; /* SW boost of WiFi tx priority */ - __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ -}; - -struct iwl_bt_cmd_v2 { - struct iwl_basic_bt_cmd basic; - __le32 prio_boost; - /* - * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask - * if configure the following patterns - */ - u8 reserved; - u8 tx_prio_boost; /* SW boost of WiFi tx priority */ - __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ -}; - -#define IWLAGN_BT_SCO_ACTIVE cpu_to_le32(BIT(0)) - -struct iwlagn_bt_sco_cmd { - __le32 flags; -}; - -/****************************************************************************** - * (6) - * Spectrum Management (802.11h) Commands, Responses, Notifications: - * - *****************************************************************************/ - -/* - * Spectrum Management - */ -#define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \ - RXON_FILTER_CTL2HOST_MSK | \ - RXON_FILTER_ACCEPT_GRP_MSK | \ - RXON_FILTER_DIS_DECRYPT_MSK | \ - RXON_FILTER_DIS_GRP_DECRYPT_MSK | \ - RXON_FILTER_ASSOC_MSK | \ - RXON_FILTER_BCON_AWARE_MSK) - -struct iwl_measure_channel { - __le32 duration; /* measurement duration in extended beacon - * format */ - u8 channel; /* channel to measure */ - u8 type; /* see enum iwl_measure_type */ - __le16 reserved; -} __packed; - -/* - * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command) - */ -struct iwl_spectrum_cmd { - __le16 len; /* number of bytes starting from token */ - u8 token; /* token id */ - u8 id; /* measurement id -- 0 or 1 */ - u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */ - u8 periodic; /* 1 = periodic */ - __le16 path_loss_timeout; - __le32 start_time; /* start time in extended beacon format */ - __le32 reserved2; - __le32 flags; /* rxon flags */ - __le32 filter_flags; /* rxon filter flags */ - __le16 channel_count; /* minimum 1, maximum 10 */ - __le16 reserved3; - struct iwl_measure_channel channels[10]; -} __packed; - -/* - * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response) - */ -struct iwl_spectrum_resp { - u8 token; - u8 id; /* id of the prior command replaced, or 0xff */ - __le16 status; /* 0 - command will be handled - * 1 - cannot handle (conflicts with another - * measurement) */ -} __packed; - -enum iwl_measurement_state { - IWL_MEASUREMENT_START = 0, - IWL_MEASUREMENT_STOP = 1, -}; - -enum iwl_measurement_status { - IWL_MEASUREMENT_OK = 0, - IWL_MEASUREMENT_CONCURRENT = 1, - IWL_MEASUREMENT_CSA_CONFLICT = 2, - IWL_MEASUREMENT_TGH_CONFLICT = 3, - /* 4-5 reserved */ - IWL_MEASUREMENT_STOPPED = 6, - IWL_MEASUREMENT_TIMEOUT = 7, - IWL_MEASUREMENT_PERIODIC_FAILED = 8, -}; - -#define NUM_ELEMENTS_IN_HISTOGRAM 8 - -struct iwl_measurement_histogram { - __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */ - __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */ -} __packed; - -/* clear channel availability counters */ -struct iwl_measurement_cca_counters { - __le32 ofdm; - __le32 cck; -} __packed; - -enum iwl_measure_type { - IWL_MEASURE_BASIC = (1 << 0), - IWL_MEASURE_CHANNEL_LOAD = (1 << 1), - IWL_MEASURE_HISTOGRAM_RPI = (1 << 2), - IWL_MEASURE_HISTOGRAM_NOISE = (1 << 3), - IWL_MEASURE_FRAME = (1 << 4), - /* bits 5:6 are reserved */ - IWL_MEASURE_IDLE = (1 << 7), -}; - -/* - * SPECTRUM_MEASURE_NOTIFICATION = 0x75 (notification only, not a command) - */ -struct iwl_spectrum_notification { - u8 id; /* measurement id -- 0 or 1 */ - u8 token; - u8 channel_index; /* index in measurement channel list */ - u8 state; /* 0 - start, 1 - stop */ - __le32 start_time; /* lower 32-bits of TSF */ - u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */ - u8 channel; - u8 type; /* see enum iwl_measurement_type */ - u8 reserved1; - /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only - * valid if applicable for measurement type requested. */ - __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */ - __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */ - __le32 cca_time; /* channel load time in usecs */ - u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 - - * unidentified */ - u8 reserved2[3]; - struct iwl_measurement_histogram histogram; - __le32 stop_time; /* lower 32-bits of TSF */ - __le32 status; /* see iwl_measurement_status */ -} __packed; - -/****************************************************************************** - * (7) - * Power Management Commands, Responses, Notifications: - * - *****************************************************************************/ - -/** - * struct iwl_powertable_cmd - Power Table Command - * @flags: See below: - * - * POWER_TABLE_CMD = 0x77 (command, has simple generic response) - * - * PM allow: - * bit 0 - '0' Driver not allow power management - * '1' Driver allow PM (use rest of parameters) - * - * uCode send sleep notifications: - * bit 1 - '0' Don't send sleep notification - * '1' send sleep notification (SEND_PM_NOTIFICATION) - * - * Sleep over DTIM - * bit 2 - '0' PM have to walk up every DTIM - * '1' PM could sleep over DTIM till listen Interval. - * - * PCI power managed - * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1) - * '1' !(PCI_CFG_LINK_CTRL & 0x1) - * - * Fast PD - * bit 4 - '1' Put radio to sleep when receiving frame for others - * - * Force sleep Modes - * bit 31/30- '00' use both mac/xtal sleeps - * '01' force Mac sleep - * '10' force xtal sleep - * '11' Illegal set - * - * NOTE: if sleep_interval[SLEEP_INTRVL_TABLE_SIZE-1] > DTIM period then - * ucode assume sleep over DTIM is allowed and we don't need to wake up - * for every DTIM. - */ -#define IWL_POWER_VEC_SIZE 5 - -#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) -#define IWL_POWER_POWER_SAVE_ENA_MSK cpu_to_le16(BIT(0)) -#define IWL_POWER_POWER_MANAGEMENT_ENA_MSK cpu_to_le16(BIT(1)) -#define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2)) -#define IWL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) -#define IWL_POWER_FAST_PD cpu_to_le16(BIT(4)) -#define IWL_POWER_BEACON_FILTERING cpu_to_le16(BIT(5)) -#define IWL_POWER_SHADOW_REG_ENA cpu_to_le16(BIT(6)) -#define IWL_POWER_CT_KILL_SET cpu_to_le16(BIT(7)) -#define IWL_POWER_BT_SCO_ENA cpu_to_le16(BIT(8)) -#define IWL_POWER_ADVANCE_PM_ENA_MSK cpu_to_le16(BIT(9)) - -struct iwl_powertable_cmd { - __le16 flags; - u8 keep_alive_seconds; - u8 debug_flags; - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 sleep_interval[IWL_POWER_VEC_SIZE]; - __le32 keep_alive_beacons; -} __packed; - -/* - * PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command) - * all devices identical. - */ -struct iwl_sleep_notification { - u8 pm_sleep_mode; - u8 pm_wakeup_src; - __le16 reserved; - __le32 sleep_time; - __le32 tsf_low; - __le32 bcon_timer; -} __packed; - -/* Sleep states. all devices identical. */ -enum { - IWL_PM_NO_SLEEP = 0, - IWL_PM_SLP_MAC = 1, - IWL_PM_SLP_FULL_MAC_UNASSOCIATE = 2, - IWL_PM_SLP_FULL_MAC_CARD_STATE = 3, - IWL_PM_SLP_PHY = 4, - IWL_PM_SLP_REPENT = 5, - IWL_PM_WAKEUP_BY_TIMER = 6, - IWL_PM_WAKEUP_BY_DRIVER = 7, - IWL_PM_WAKEUP_BY_RFKILL = 8, - /* 3 reserved */ - IWL_PM_NUM_OF_MODES = 12, -}; - -/* - * REPLY_CARD_STATE_CMD = 0xa0 (command, has simple generic response) - */ -#define CARD_STATE_CMD_DISABLE 0x00 /* Put card to sleep */ -#define CARD_STATE_CMD_ENABLE 0x01 /* Wake up card */ -#define CARD_STATE_CMD_HALT 0x02 /* Power down permanently */ -struct iwl_card_state_cmd { - __le32 status; /* CARD_STATE_CMD_* request new power state */ -} __packed; - -/* - * CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command) - */ -struct iwl_card_state_notif { - __le32 flags; -} __packed; - -#define HW_CARD_DISABLED 0x01 -#define SW_CARD_DISABLED 0x02 -#define CT_CARD_DISABLED 0x04 -#define RXON_CARD_DISABLED 0x10 - -struct iwl_ct_kill_config { - __le32 reserved; - __le32 critical_temperature_M; - __le32 critical_temperature_R; -} __packed; - -/* 1000, and 6x00 */ -struct iwl_ct_kill_throttling_config { - __le32 critical_temperature_exit; - __le32 reserved; - __le32 critical_temperature_enter; -} __packed; - -/****************************************************************************** - * (8) - * Scan Commands, Responses, Notifications: - * - *****************************************************************************/ - -#define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0) -#define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1) - -/** - * struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table - * - * One for each channel in the scan list. - * Each channel can independently select: - * 1) SSID for directed active scans - * 2) Txpower setting (for rate specified within Tx command) - * 3) How long to stay on-channel (behavior may be modified by quiet_time, - * quiet_plcp_th, good_CRC_th) - * - * To avoid uCode errors, make sure the following are true (see comments - * under struct iwl_scan_cmd about max_out_time and quiet_time): - * 1) If using passive_dwell (i.e. passive_dwell != 0): - * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0) - * 2) quiet_time <= active_dwell - * 3) If restricting off-channel time (i.e. max_out_time !=0): - * passive_dwell < max_out_time - * active_dwell < max_out_time - */ - -struct iwl_scan_channel { - /* - * type is defined as: - * 0:0 1 = active, 0 = passive - * 1:20 SSID direct bit map; if a bit is set, then corresponding - * SSID IE is transmitted in probe request. - * 21:31 reserved - */ - __le32 type; - __le16 channel; /* band is selected by iwl_scan_cmd "flags" field */ - u8 tx_gain; /* gain for analog radio */ - u8 dsp_atten; /* gain for DSP */ - __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ - __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ -} __packed; - -/* set number of direct probes __le32 type */ -#define IWL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1)))) - -/** - * struct iwl_ssid_ie - directed scan network information element - * - * Up to 20 of these may appear in REPLY_SCAN_CMD, - * selected by "type" bit field in struct iwl_scan_channel; - * each channel may select different ssids from among the 20 entries. - * SSID IEs get transmitted in reverse order of entry. - */ -struct iwl_ssid_ie { - u8 id; - u8 len; - u8 ssid[32]; -} __packed; - -#define PROBE_OPTION_MAX 20 -#define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF) -#define IWL_GOOD_CRC_TH_DISABLED 0 -#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) -#define IWL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff) -#define IWL_MAX_CMD_SIZE 4096 - -/* - * REPLY_SCAN_CMD = 0x80 (command) - * - * The hardware scan command is very powerful; the driver can set it up to - * maintain (relatively) normal network traffic while doing a scan in the - * background. The max_out_time and suspend_time control the ratio of how - * long the device stays on an associated network channel ("service channel") - * vs. how long it's away from the service channel, i.e. tuned to other channels - * for scanning. - * - * max_out_time is the max time off-channel (in usec), and suspend_time - * is how long (in "extended beacon" format) that the scan is "suspended" - * after returning to the service channel. That is, suspend_time is the - * time that we stay on the service channel, doing normal work, between - * scan segments. The driver may set these parameters differently to support - * scanning when associated vs. not associated, and light vs. heavy traffic - * loads when associated. - * - * After receiving this command, the device's scan engine does the following; - * - * 1) Sends SCAN_START notification to driver - * 2) Checks to see if it has time to do scan for one channel - * 3) Sends NULL packet, with power-save (PS) bit set to 1, - * to tell AP that we're going off-channel - * 4) Tunes to first channel in scan list, does active or passive scan - * 5) Sends SCAN_RESULT notification to driver - * 6) Checks to see if it has time to do scan on *next* channel in list - * 7) Repeats 4-6 until it no longer has time to scan the next channel - * before max_out_time expires - * 8) Returns to service channel - * 9) Sends NULL packet with PS=0 to tell AP that we're back - * 10) Stays on service channel until suspend_time expires - * 11) Repeats entire process 2-10 until list is complete - * 12) Sends SCAN_COMPLETE notification - * - * For fast, efficient scans, the scan command also has support for staying on - * a channel for just a short time, if doing active scanning and getting no - * responses to the transmitted probe request. This time is controlled by - * quiet_time, and the number of received packets below which a channel is - * considered "quiet" is controlled by quiet_plcp_threshold. - * - * For active scanning on channels that have regulatory restrictions against - * blindly transmitting, the scan can listen before transmitting, to make sure - * that there is already legitimate activity on the channel. If enough - * packets are cleanly received on the channel (controlled by good_CRC_th, - * typical value 1), the scan engine starts transmitting probe requests. - * - * Driver must use separate scan commands for 2.4 vs. 5 GHz bands. - * - * To avoid uCode errors, see timing restrictions described under - * struct iwl_scan_channel. - */ - -enum iwl_scan_flags { - /* BIT(0) currently unused */ - IWL_SCAN_FLAGS_ACTION_FRAME_TX = BIT(1), - /* bits 2-7 reserved */ -}; - -struct iwl_scan_cmd { - __le16 len; - u8 scan_flags; /* scan flags: see enum iwl_scan_flags */ - u8 channel_count; /* # channels in channel list */ - __le16 quiet_time; /* dwell only this # millisecs on quiet channel - * (only for active scan) */ - __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ - __le16 good_CRC_th; /* passive -> active promotion threshold */ - __le16 rx_chain; /* RXON_RX_CHAIN_* */ - __le32 max_out_time; /* max usec to be away from associated (service) - * channel */ - __le32 suspend_time; /* pause scan this long (in "extended beacon - * format") when returning to service chnl: - */ - __le32 flags; /* RXON_FLG_* */ - __le32 filter_flags; /* RXON_FILTER_* */ - - /* For active scans (set to all-0s for passive scans). - * Does not include payload. Must specify Tx rate; no rate scaling. */ - struct iwl_tx_cmd tx_cmd; - - /* For directed active scans (set to all-0s otherwise) */ - struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; - - /* - * Probe request frame, followed by channel list. - * - * Size of probe request frame is specified by byte count in tx_cmd. - * Channel list follows immediately after probe request frame. - * Number of channels in list is specified by channel_count. - * Each channel in list is of type: - * - * struct iwl_scan_channel channels[0]; - * - * NOTE: Only one band of channels can be scanned per pass. You - * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait - * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION) - * before requesting another scan. - */ - u8 data[0]; -} __packed; - -/* Can abort will notify by complete notification with abort status. */ -#define CAN_ABORT_STATUS cpu_to_le32(0x1) -/* complete notification statuses */ -#define ABORT_STATUS 0x2 - -/* - * REPLY_SCAN_CMD = 0x80 (response) - */ -struct iwl_scanreq_notification { - __le32 status; /* 1: okay, 2: cannot fulfill request */ -} __packed; - -/* - * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command) - */ -struct iwl_scanstart_notification { - __le32 tsf_low; - __le32 tsf_high; - __le32 beacon_timer; - u8 channel; - u8 band; - u8 reserved[2]; - __le32 status; -} __packed; - -#define SCAN_OWNER_STATUS 0x1 -#define MEASURE_OWNER_STATUS 0x2 - -#define IWL_PROBE_STATUS_OK 0 -#define IWL_PROBE_STATUS_TX_FAILED BIT(0) -/* error statuses combined with TX_FAILED */ -#define IWL_PROBE_STATUS_FAIL_TTL BIT(1) -#define IWL_PROBE_STATUS_FAIL_BT BIT(2) - -#define NUMBER_OF_STATISTICS 1 /* first __le32 is good CRC */ -/* - * SCAN_RESULTS_NOTIFICATION = 0x83 (notification only, not a command) - */ -struct iwl_scanresults_notification { - u8 channel; - u8 band; - u8 probe_status; - u8 num_probe_not_sent; /* not enough time to send */ - __le32 tsf_low; - __le32 tsf_high; - __le32 statistics[NUMBER_OF_STATISTICS]; -} __packed; - -/* - * SCAN_COMPLETE_NOTIFICATION = 0x84 (notification only, not a command) - */ -struct iwl_scancomplete_notification { - u8 scanned_channels; - u8 status; - u8 bt_status; /* BT On/Off status */ - u8 last_channel; - __le32 tsf_low; - __le32 tsf_high; -} __packed; - - -/****************************************************************************** - * (9) - * IBSS/AP Commands and Notifications: - * - *****************************************************************************/ - -enum iwl_ibss_manager { - IWL_NOT_IBSS_MANAGER = 0, - IWL_IBSS_MANAGER = 1, -}; - -/* - * BEACON_NOTIFICATION = 0x90 (notification only, not a command) - */ - -struct iwlagn_beacon_notif { - struct iwlagn_tx_resp beacon_notify_hdr; - __le32 low_tsf; - __le32 high_tsf; - __le32 ibss_mgr_status; -} __packed; - -/* - * REPLY_TX_BEACON = 0x91 (command, has simple generic response) - */ - -struct iwl_tx_beacon_cmd { - struct iwl_tx_cmd tx; - __le16 tim_idx; - u8 tim_size; - u8 reserved1; - struct ieee80211_hdr frame[0]; /* beacon frame */ -} __packed; - -/****************************************************************************** - * (10) - * Statistics Commands and Notifications: - * - *****************************************************************************/ - -#define IWL_TEMP_CONVERT 260 - -#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 -#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 -#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 - -/* Used for passing to driver number of successes and failures per rate */ -struct rate_histogram { - union { - __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; - __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; - __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; - } success; - union { - __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; - __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; - __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; - } failed; -} __packed; - -/* statistics command response */ - -struct statistics_dbg { - __le32 burst_check; - __le32 burst_count; - __le32 wait_for_silence_timeout_cnt; - __le32 reserved[3]; -} __packed; - -struct statistics_rx_phy { - __le32 ina_cnt; - __le32 fina_cnt; - __le32 plcp_err; - __le32 crc32_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 false_alarm_cnt; - __le32 fina_sync_err_cnt; - __le32 sfd_timeout; - __le32 fina_timeout; - __le32 unresponded_rts; - __le32 rxe_frame_limit_overrun; - __le32 sent_ack_cnt; - __le32 sent_cts_cnt; - __le32 sent_ba_rsp_cnt; - __le32 dsp_self_kill; - __le32 mh_format_err; - __le32 re_acq_main_rssi_sum; - __le32 reserved3; -} __packed; - -struct statistics_rx_ht_phy { - __le32 plcp_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 crc32_err; - __le32 mh_format_err; - __le32 agg_crc32_good; - __le32 agg_mpdu_cnt; - __le32 agg_cnt; - __le32 unsupport_mcs; -} __packed; - -#define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1) - -struct statistics_rx_non_phy { - __le32 bogus_cts; /* CTS received when not expecting CTS */ - __le32 bogus_ack; /* ACK received when not expecting ACK */ - __le32 non_bssid_frames; /* number of frames with BSSID that - * doesn't belong to the STA BSSID */ - __le32 filtered_frames; /* count frames that were dumped in the - * filtering process */ - __le32 non_channel_beacons; /* beacons with our bss id but not on - * our serving channel */ - __le32 channel_beacons; /* beacons with our bss id and in our - * serving channel */ - __le32 num_missed_bcon; /* number of missed beacons */ - __le32 adc_rx_saturation_time; /* count in 0.8us units the time the - * ADC was in saturation */ - __le32 ina_detection_search_time;/* total time (in 0.8us) searched - * for INA */ - __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ - __le32 interference_data_flag; /* flag for interference data - * availability. 1 when data is - * available. */ - __le32 channel_load; /* counts RX Enable time in uSec */ - __le32 dsp_false_alarms; /* DSP false alarm (both OFDM - * and CCK) counter */ - __le32 beacon_rssi_a; - __le32 beacon_rssi_b; - __le32 beacon_rssi_c; - __le32 beacon_energy_a; - __le32 beacon_energy_b; - __le32 beacon_energy_c; -} __packed; - -struct statistics_rx_non_phy_bt { - struct statistics_rx_non_phy common; - /* additional stats for bt */ - __le32 num_bt_kills; - __le32 reserved[2]; -} __packed; - -struct statistics_rx { - struct statistics_rx_phy ofdm; - struct statistics_rx_phy cck; - struct statistics_rx_non_phy general; - struct statistics_rx_ht_phy ofdm_ht; -} __packed; - -struct statistics_rx_bt { - struct statistics_rx_phy ofdm; - struct statistics_rx_phy cck; - struct statistics_rx_non_phy_bt general; - struct statistics_rx_ht_phy ofdm_ht; -} __packed; - -/** - * struct statistics_tx_power - current tx power - * - * @ant_a: current tx power on chain a in 1/2 dB step - * @ant_b: current tx power on chain b in 1/2 dB step - * @ant_c: current tx power on chain c in 1/2 dB step - */ -struct statistics_tx_power { - u8 ant_a; - u8 ant_b; - u8 ant_c; - u8 reserved; -} __packed; - -struct statistics_tx_non_phy_agg { - __le32 ba_timeout; - __le32 ba_reschedule_frames; - __le32 scd_query_agg_frame_cnt; - __le32 scd_query_no_agg; - __le32 scd_query_agg; - __le32 scd_query_mismatch; - __le32 frame_not_ready; - __le32 underrun; - __le32 bt_prio_kill; - __le32 rx_ba_rsp_cnt; -} __packed; - -struct statistics_tx { - __le32 preamble_cnt; - __le32 rx_detected_cnt; - __le32 bt_prio_defer_cnt; - __le32 bt_prio_kill_cnt; - __le32 few_bytes_cnt; - __le32 cts_timeout; - __le32 ack_timeout; - __le32 expected_ack_cnt; - __le32 actual_ack_cnt; - __le32 dump_msdu_cnt; - __le32 burst_abort_next_frame_mismatch_cnt; - __le32 burst_abort_missing_next_frame_cnt; - __le32 cts_timeout_collision; - __le32 ack_or_ba_timeout_collision; - struct statistics_tx_non_phy_agg agg; - /* - * "tx_power" are optional parameters provided by uCode, - * 6000 series is the only device provide the information, - * Those are reserved fields for all the other devices - */ - struct statistics_tx_power tx_power; - __le32 reserved1; -} __packed; - - -struct statistics_div { - __le32 tx_on_a; - __le32 tx_on_b; - __le32 exec_time; - __le32 probe_time; - __le32 reserved1; - __le32 reserved2; -} __packed; - -struct statistics_general_common { - __le32 temperature; /* radio temperature */ - __le32 temperature_m; /* radio voltage */ - struct statistics_dbg dbg; - __le32 sleep_time; - __le32 slots_out; - __le32 slots_idle; - __le32 ttl_timestamp; - struct statistics_div div; - __le32 rx_enable_counter; - /* - * num_of_sos_states: - * count the number of times we have to re-tune - * in order to get out of bad PHY status - */ - __le32 num_of_sos_states; -} __packed; - -struct statistics_bt_activity { - /* Tx statistics */ - __le32 hi_priority_tx_req_cnt; - __le32 hi_priority_tx_denied_cnt; - __le32 lo_priority_tx_req_cnt; - __le32 lo_priority_tx_denied_cnt; - /* Rx statistics */ - __le32 hi_priority_rx_req_cnt; - __le32 hi_priority_rx_denied_cnt; - __le32 lo_priority_rx_req_cnt; - __le32 lo_priority_rx_denied_cnt; -} __packed; - -struct statistics_general { - struct statistics_general_common common; - __le32 reserved2; - __le32 reserved3; -} __packed; - -struct statistics_general_bt { - struct statistics_general_common common; - struct statistics_bt_activity activity; - __le32 reserved2; - __le32 reserved3; -} __packed; - -#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0) -#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1) -#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2) - -/* - * REPLY_STATISTICS_CMD = 0x9c, - * all devices identical. - * - * This command triggers an immediate response containing uCode statistics. - * The response is in the same format as STATISTICS_NOTIFICATION 0x9d, below. - * - * If the CLEAR_STATS configuration flag is set, uCode will clear its - * internal copy of the statistics (counters) after issuing the response. - * This flag does not affect STATISTICS_NOTIFICATIONs after beacons (see below). - * - * If the DISABLE_NOTIF configuration flag is set, uCode will not issue - * STATISTICS_NOTIFICATIONs after received beacons (see below). This flag - * does not affect the response to the REPLY_STATISTICS_CMD 0x9c itself. - */ -#define IWL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */ -#define IWL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2)/* see above */ -struct iwl_statistics_cmd { - __le32 configuration_flags; /* IWL_STATS_CONF_* */ -} __packed; - -/* - * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) - * - * By default, uCode issues this notification after receiving a beacon - * while associated. To disable this behavior, set DISABLE_NOTIF flag in the - * REPLY_STATISTICS_CMD 0x9c, above. - * - * Statistics counters continue to increment beacon after beacon, but are - * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD - * 0x9c with CLEAR_STATS bit set (see above). - * - * uCode also issues this notification during scans. uCode clears statistics - * appropriately so that each notification contains statistics for only the - * one channel that has just been scanned. - */ -#define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2) -#define STATISTICS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8) - -struct iwl_notif_statistics { - __le32 flag; - struct statistics_rx rx; - struct statistics_tx tx; - struct statistics_general general; -} __packed; - -struct iwl_bt_notif_statistics { - __le32 flag; - struct statistics_rx_bt rx; - struct statistics_tx tx; - struct statistics_general_bt general; -} __packed; - -/* - * MISSED_BEACONS_NOTIFICATION = 0xa2 (notification only, not a command) - * - * uCode send MISSED_BEACONS_NOTIFICATION to driver when detect beacon missed - * in regardless of how many missed beacons, which mean when driver receive the - * notification, inside the command, it can find all the beacons information - * which include number of total missed beacons, number of consecutive missed - * beacons, number of beacons received and number of beacons expected to - * receive. - * - * If uCode detected consecutive_missed_beacons > 5, it will reset the radio - * in order to bring the radio/PHY back to working state; which has no relation - * to when driver will perform sensitivity calibration. - * - * Driver should set it own missed_beacon_threshold to decide when to perform - * sensitivity calibration based on number of consecutive missed beacons in - * order to improve overall performance, especially in noisy environment. - * - */ - -#define IWL_MISSED_BEACON_THRESHOLD_MIN (1) -#define IWL_MISSED_BEACON_THRESHOLD_DEF (5) -#define IWL_MISSED_BEACON_THRESHOLD_MAX IWL_MISSED_BEACON_THRESHOLD_DEF - -struct iwl_missed_beacon_notif { - __le32 consecutive_missed_beacons; - __le32 total_missed_becons; - __le32 num_expected_beacons; - __le32 num_recvd_beacons; -} __packed; - - -/****************************************************************************** - * (11) - * Rx Calibration Commands: - * - * With the uCode used for open source drivers, most Tx calibration (except - * for Tx Power) and most Rx calibration is done by uCode during the - * "initialize" phase of uCode boot. Driver must calibrate only: - * - * 1) Tx power (depends on temperature), described elsewhere - * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas) - * 3) Receiver sensitivity (to optimize signal detection) - * - *****************************************************************************/ - -/** - * SENSITIVITY_CMD = 0xa8 (command, has simple generic response) - * - * This command sets up the Rx signal detector for a sensitivity level that - * is high enough to lock onto all signals within the associated network, - * but low enough to ignore signals that are below a certain threshold, so as - * not to have too many "false alarms". False alarms are signals that the - * Rx DSP tries to lock onto, but then discards after determining that they - * are noise. - * - * The optimum number of false alarms is between 5 and 50 per 200 TUs - * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e. - * time listening, not transmitting). Driver must adjust sensitivity so that - * the ratio of actual false alarms to actual Rx time falls within this range. - * - * While associated, uCode delivers STATISTICS_NOTIFICATIONs after each - * received beacon. These provide information to the driver to analyze the - * sensitivity. Don't analyze statistics that come in from scanning, or any - * other non-associated-network source. Pertinent statistics include: - * - * From "general" statistics (struct statistics_rx_non_phy): - * - * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level) - * Measure of energy of desired signal. Used for establishing a level - * below which the device does not detect signals. - * - * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB) - * Measure of background noise in silent period after beacon. - * - * channel_load - * uSecs of actual Rx time during beacon period (varies according to - * how much time was spent transmitting). - * - * From "cck" and "ofdm" statistics (struct statistics_rx_phy), separately: - * - * false_alarm_cnt - * Signal locks abandoned early (before phy-level header). - * - * plcp_err - * Signal locks abandoned late (during phy-level header). - * - * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from - * beacon to beacon, i.e. each value is an accumulation of all errors - * before and including the latest beacon. Values will wrap around to 0 - * after counting up to 2^32 - 1. Driver must differentiate vs. - * previous beacon's values to determine # false alarms in the current - * beacon period. - * - * Total number of false alarms = false_alarms + plcp_errs - * - * For OFDM, adjust the following table entries in struct iwl_sensitivity_cmd - * (notice that the start points for OFDM are at or close to settings for - * maximum sensitivity): - * - * START / MIN / MAX - * HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX 90 / 85 / 120 - * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX 170 / 170 / 210 - * HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX 105 / 105 / 140 - * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX 220 / 220 / 270 - * - * If actual rate of OFDM false alarms (+ plcp_errors) is too high - * (greater than 50 for each 204.8 msecs listening), reduce sensitivity - * by *adding* 1 to all 4 of the table entries above, up to the max for - * each entry. Conversely, if false alarm rate is too low (less than 5 - * for each 204.8 msecs listening), *subtract* 1 from each entry to - * increase sensitivity. - * - * For CCK sensitivity, keep track of the following: - * - * 1). 20-beacon history of maximum background noise, indicated by - * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the - * 3 receivers. For any given beacon, the "silence reference" is - * the maximum of last 60 samples (20 beacons * 3 receivers). - * - * 2). 10-beacon history of strongest signal level, as indicated - * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers, - * i.e. the strength of the signal through the best receiver at the - * moment. These measurements are "upside down", with lower values - * for stronger signals, so max energy will be *minimum* value. - * - * Then for any given beacon, the driver must determine the *weakest* - * of the strongest signals; this is the minimum level that needs to be - * successfully detected, when using the best receiver at the moment. - * "Max cck energy" is the maximum (higher value means lower energy!) - * of the last 10 minima. Once this is determined, driver must add - * a little margin by adding "6" to it. - * - * 3). Number of consecutive beacon periods with too few false alarms. - * Reset this to 0 at the first beacon period that falls within the - * "good" range (5 to 50 false alarms per 204.8 milliseconds rx). - * - * Then, adjust the following CCK table entries in struct iwl_sensitivity_cmd - * (notice that the start points for CCK are at maximum sensitivity): - * - * START / MIN / MAX - * HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX 125 / 125 / 200 - * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX 200 / 200 / 400 - * HD_MIN_ENERGY_CCK_DET_INDEX 100 / 0 / 100 - * - * If actual rate of CCK false alarms (+ plcp_errors) is too high - * (greater than 50 for each 204.8 msecs listening), method for reducing - * sensitivity is: - * - * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, - * up to max 400. - * - * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is < 160, - * sensitivity has been reduced a significant amount; bring it up to - * a moderate 161. Otherwise, *add* 3, up to max 200. - * - * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is > 160, - * sensitivity has been reduced only a moderate or small amount; - * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_INDEX, - * down to min 0. Otherwise (if gain has been significantly reduced), - * don't change the HD_MIN_ENERGY_CCK_DET_INDEX value. - * - * b) Save a snapshot of the "silence reference". - * - * If actual rate of CCK false alarms (+ plcp_errors) is too low - * (less than 5 for each 204.8 msecs listening), method for increasing - * sensitivity is used only if: - * - * 1a) Previous beacon did not have too many false alarms - * 1b) AND difference between previous "silence reference" and current - * "silence reference" (prev - current) is 2 or more, - * OR 2) 100 or more consecutive beacon periods have had rate of - * less than 5 false alarms per 204.8 milliseconds rx time. - * - * Method for increasing sensitivity: - * - * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX, - * down to min 125. - * - * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, - * down to min 200. - * - * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_INDEX, up to max 100. - * - * If actual rate of CCK false alarms (+ plcp_errors) is within good range - * (between 5 and 50 for each 204.8 msecs listening): - * - * 1) Save a snapshot of the silence reference. - * - * 2) If previous beacon had too many CCK false alarms (+ plcp_errors), - * give some extra margin to energy threshold by *subtracting* 8 - * from value in HD_MIN_ENERGY_CCK_DET_INDEX. - * - * For all cases (too few, too many, good range), make sure that the CCK - * detection threshold (energy) is below the energy level for robust - * detection over the past 10 beacon periods, the "Max cck energy". - * Lower values mean higher energy; this means making sure that the value - * in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy". - * - */ - -/* - * Table entries in SENSITIVITY_CMD (struct iwl_sensitivity_cmd) - */ -#define HD_TABLE_SIZE (11) /* number of entries */ -#define HD_MIN_ENERGY_CCK_DET_INDEX (0) /* table indexes */ -#define HD_MIN_ENERGY_OFDM_DET_INDEX (1) -#define HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX (2) -#define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX (3) -#define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX (4) -#define HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX (5) -#define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX (6) -#define HD_BARKER_CORR_TH_ADD_MIN_INDEX (7) -#define HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX (8) -#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9) -#define HD_OFDM_ENERGY_TH_IN_INDEX (10) - -/* - * Additional table entries in enhance SENSITIVITY_CMD - */ -#define HD_INA_NON_SQUARE_DET_OFDM_INDEX (11) -#define HD_INA_NON_SQUARE_DET_CCK_INDEX (12) -#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX (13) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX (14) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (15) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX (16) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX (17) -#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX (18) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (19) -#define HD_CCK_NON_SQUARE_DET_SLOPE_INDEX (20) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX (21) -#define HD_RESERVED (22) - -/* number of entries for enhanced tbl */ -#define ENHANCE_HD_TABLE_SIZE (23) - -/* number of additional entries for enhanced tbl */ -#define ENHANCE_HD_TABLE_ENTRIES (ENHANCE_HD_TABLE_SIZE - HD_TABLE_SIZE) - -#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V1 cpu_to_le16(0) -#define HD_INA_NON_SQUARE_DET_CCK_DATA_V1 cpu_to_le16(0) -#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1 cpu_to_le16(0) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(668) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(486) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(37) -#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(853) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4) -#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(476) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(99) - -#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V2 cpu_to_le16(1) -#define HD_INA_NON_SQUARE_DET_CCK_DATA_V2 cpu_to_le16(1) -#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2 cpu_to_le16(1) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(600) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(40) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(486) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(45) -#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(853) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(60) -#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(476) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(99) - - -/* Control field in struct iwl_sensitivity_cmd */ -#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE cpu_to_le16(0) -#define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1) - -/** - * struct iwl_sensitivity_cmd - * @control: (1) updates working table, (0) updates default table - * @table: energy threshold values, use HD_* as index into table - * - * Always use "1" in "control" to update uCode's working table and DSP. - */ -struct iwl_sensitivity_cmd { - __le16 control; /* always use "1" */ - __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */ -} __packed; - -/* - * - */ -struct iwl_enhance_sensitivity_cmd { - __le16 control; /* always use "1" */ - __le16 enhance_table[ENHANCE_HD_TABLE_SIZE]; /* use HD_* as index */ -} __packed; - - -/** - * REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response) - * - * This command sets the relative gains of agn device's 3 radio receiver chains. - * - * After the first association, driver should accumulate signal and noise - * statistics from the STATISTICS_NOTIFICATIONs that follow the first 20 - * beacons from the associated network (don't collect statistics that come - * in from scanning, or any other non-network source). - * - * DISCONNECTED ANTENNA: - * - * Driver should determine which antennas are actually connected, by comparing - * average beacon signal levels for the 3 Rx chains. Accumulate (add) the - * following values over 20 beacons, one accumulator for each of the chains - * a/b/c, from struct statistics_rx_non_phy: - * - * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB) - * - * Find the strongest signal from among a/b/c. Compare the other two to the - * strongest. If any signal is more than 15 dB (times 20, unless you - * divide the accumulated values by 20) below the strongest, the driver - * considers that antenna to be disconnected, and should not try to use that - * antenna/chain for Rx or Tx. If both A and B seem to be disconnected, - * driver should declare the stronger one as connected, and attempt to use it - * (A and B are the only 2 Tx chains!). - * - * - * RX BALANCE: - * - * Driver should balance the 3 receivers (but just the ones that are connected - * to antennas, see above) for gain, by comparing the average signal levels - * detected during the silence after each beacon (background noise). - * Accumulate (add) the following values over 20 beacons, one accumulator for - * each of the chains a/b/c, from struct statistics_rx_non_phy: - * - * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB) - * - * Find the weakest background noise level from among a/b/c. This Rx chain - * will be the reference, with 0 gain adjustment. Attenuate other channels by - * finding noise difference: - * - * (accum_noise[i] - accum_noise[reference]) / 30 - * - * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB. - * For use in diff_gain_[abc] fields of struct iwl_calibration_cmd, the - * driver should limit the difference results to a range of 0-3 (0-4.5 dB), - * and set bit 2 to indicate "reduce gain". The value for the reference - * (weakest) chain should be "0". - * - * diff_gain_[abc] bit fields: - * 2: (1) reduce gain, (0) increase gain - * 1-0: amount of gain, units of 1.5 dB - */ - -/* Phy calibration command for series */ -enum { - IWL_PHY_CALIBRATE_DC_CMD = 8, - IWL_PHY_CALIBRATE_LO_CMD = 9, - IWL_PHY_CALIBRATE_TX_IQ_CMD = 11, - IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15, - IWL_PHY_CALIBRATE_BASE_BAND_CMD = 16, - IWL_PHY_CALIBRATE_TX_IQ_PERD_CMD = 17, - IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD = 18, -}; - -/* This enum defines the bitmap of various calibrations to enable in both - * init ucode and runtime ucode through CALIBRATION_CFG_CMD. - */ -enum iwl_ucode_calib_cfg { - IWL_CALIB_CFG_RX_BB_IDX = BIT(0), - IWL_CALIB_CFG_DC_IDX = BIT(1), - IWL_CALIB_CFG_LO_IDX = BIT(2), - IWL_CALIB_CFG_TX_IQ_IDX = BIT(3), - IWL_CALIB_CFG_RX_IQ_IDX = BIT(4), - IWL_CALIB_CFG_NOISE_IDX = BIT(5), - IWL_CALIB_CFG_CRYSTAL_IDX = BIT(6), - IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(7), - IWL_CALIB_CFG_PAPD_IDX = BIT(8), - IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(9), - IWL_CALIB_CFG_TX_PWR_IDX = BIT(10), -}; - -#define IWL_CALIB_INIT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \ - IWL_CALIB_CFG_DC_IDX | \ - IWL_CALIB_CFG_LO_IDX | \ - IWL_CALIB_CFG_TX_IQ_IDX | \ - IWL_CALIB_CFG_RX_IQ_IDX | \ - IWL_CALIB_CFG_CRYSTAL_IDX) - -#define IWL_CALIB_RT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \ - IWL_CALIB_CFG_DC_IDX | \ - IWL_CALIB_CFG_LO_IDX | \ - IWL_CALIB_CFG_TX_IQ_IDX | \ - IWL_CALIB_CFG_RX_IQ_IDX | \ - IWL_CALIB_CFG_TEMPERATURE_IDX | \ - IWL_CALIB_CFG_PAPD_IDX | \ - IWL_CALIB_CFG_TX_PWR_IDX | \ - IWL_CALIB_CFG_CRYSTAL_IDX) - -#define IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK cpu_to_le32(BIT(0)) - -struct iwl_calib_cfg_elmnt_s { - __le32 is_enable; - __le32 start; - __le32 send_res; - __le32 apply_res; - __le32 reserved; -} __packed; - -struct iwl_calib_cfg_status_s { - struct iwl_calib_cfg_elmnt_s once; - struct iwl_calib_cfg_elmnt_s perd; - __le32 flags; -} __packed; - -struct iwl_calib_cfg_cmd { - struct iwl_calib_cfg_status_s ucd_calib_cfg; - struct iwl_calib_cfg_status_s drv_calib_cfg; - __le32 reserved1; -} __packed; - -struct iwl_calib_hdr { - u8 op_code; - u8 first_group; - u8 groups_num; - u8 data_valid; -} __packed; - -struct iwl_calib_cmd { - struct iwl_calib_hdr hdr; - u8 data[0]; -} __packed; - -struct iwl_calib_xtal_freq_cmd { - struct iwl_calib_hdr hdr; - u8 cap_pin1; - u8 cap_pin2; - u8 pad[2]; -} __packed; - -#define DEFAULT_RADIO_SENSOR_OFFSET cpu_to_le16(2700) -struct iwl_calib_temperature_offset_cmd { - struct iwl_calib_hdr hdr; - __le16 radio_sensor_offset; - __le16 reserved; -} __packed; - -struct iwl_calib_temperature_offset_v2_cmd { - struct iwl_calib_hdr hdr; - __le16 radio_sensor_offset_high; - __le16 radio_sensor_offset_low; - __le16 burntVoltageRef; - __le16 reserved; -} __packed; - -/* IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */ -struct iwl_calib_chain_noise_reset_cmd { - struct iwl_calib_hdr hdr; - u8 data[0]; -}; - -/* IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD */ -struct iwl_calib_chain_noise_gain_cmd { - struct iwl_calib_hdr hdr; - u8 delta_gain_1; - u8 delta_gain_2; - u8 pad[2]; -} __packed; - -/****************************************************************************** - * (12) - * Miscellaneous Commands: - * - *****************************************************************************/ - -/* - * LEDs Command & Response - * REPLY_LEDS_CMD = 0x48 (command, has simple generic response) - * - * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field), - * this command turns it on or off, or sets up a periodic blinking cycle. - */ -struct iwl_led_cmd { - __le32 interval; /* "interval" in uSec */ - u8 id; /* 1: Activity, 2: Link, 3: Tech */ - u8 off; /* # intervals off while blinking; - * "0", with >0 "on" value, turns LED on */ - u8 on; /* # intervals on while blinking; - * "0", regardless of "off", turns LED off */ - u8 reserved; -} __packed; - -/* - * station priority table entries - * also used as potential "events" value for both - * COEX_MEDIUM_NOTIFICATION and COEX_EVENT_CMD - */ - -/* - * COEX events entry flag masks - * RP - Requested Priority - * WP - Win Medium Priority: priority assigned when the contention has been won - */ -#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG (0x1) -#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG (0x2) -#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG (0x4) - -#define COEX_CU_UNASSOC_IDLE_RP 4 -#define COEX_CU_UNASSOC_MANUAL_SCAN_RP 4 -#define COEX_CU_UNASSOC_AUTO_SCAN_RP 4 -#define COEX_CU_CALIBRATION_RP 4 -#define COEX_CU_PERIODIC_CALIBRATION_RP 4 -#define COEX_CU_CONNECTION_ESTAB_RP 4 -#define COEX_CU_ASSOCIATED_IDLE_RP 4 -#define COEX_CU_ASSOC_MANUAL_SCAN_RP 4 -#define COEX_CU_ASSOC_AUTO_SCAN_RP 4 -#define COEX_CU_ASSOC_ACTIVE_LEVEL_RP 4 -#define COEX_CU_RF_ON_RP 6 -#define COEX_CU_RF_OFF_RP 4 -#define COEX_CU_STAND_ALONE_DEBUG_RP 6 -#define COEX_CU_IPAN_ASSOC_LEVEL_RP 4 -#define COEX_CU_RSRVD1_RP 4 -#define COEX_CU_RSRVD2_RP 4 - -#define COEX_CU_UNASSOC_IDLE_WP 3 -#define COEX_CU_UNASSOC_MANUAL_SCAN_WP 3 -#define COEX_CU_UNASSOC_AUTO_SCAN_WP 3 -#define COEX_CU_CALIBRATION_WP 3 -#define COEX_CU_PERIODIC_CALIBRATION_WP 3 -#define COEX_CU_CONNECTION_ESTAB_WP 3 -#define COEX_CU_ASSOCIATED_IDLE_WP 3 -#define COEX_CU_ASSOC_MANUAL_SCAN_WP 3 -#define COEX_CU_ASSOC_AUTO_SCAN_WP 3 -#define COEX_CU_ASSOC_ACTIVE_LEVEL_WP 3 -#define COEX_CU_RF_ON_WP 3 -#define COEX_CU_RF_OFF_WP 3 -#define COEX_CU_STAND_ALONE_DEBUG_WP 6 -#define COEX_CU_IPAN_ASSOC_LEVEL_WP 3 -#define COEX_CU_RSRVD1_WP 3 -#define COEX_CU_RSRVD2_WP 3 - -#define COEX_UNASSOC_IDLE_FLAGS 0 -#define COEX_UNASSOC_MANUAL_SCAN_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_UNASSOC_AUTO_SCAN_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_CALIBRATION_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_PERIODIC_CALIBRATION_FLAGS 0 -/* - * COEX_CONNECTION_ESTAB: - * we need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. - */ -#define COEX_CONNECTION_ESTAB_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ - COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) -#define COEX_ASSOCIATED_IDLE_FLAGS 0 -#define COEX_ASSOC_MANUAL_SCAN_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_ASSOC_AUTO_SCAN_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0 -#define COEX_RF_ON_FLAGS 0 -#define COEX_RF_OFF_FLAGS 0 -#define COEX_STAND_ALONE_DEBUG_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_IPAN_ASSOC_LEVEL_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ - COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) -#define COEX_RSRVD1_FLAGS 0 -#define COEX_RSRVD2_FLAGS 0 -/* - * COEX_CU_RF_ON is the event wrapping all radio ownership. - * We need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. - */ -#define COEX_CU_RF_ON_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ - COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) - - -enum { - /* un-association part */ - COEX_UNASSOC_IDLE = 0, - COEX_UNASSOC_MANUAL_SCAN = 1, - COEX_UNASSOC_AUTO_SCAN = 2, - /* calibration */ - COEX_CALIBRATION = 3, - COEX_PERIODIC_CALIBRATION = 4, - /* connection */ - COEX_CONNECTION_ESTAB = 5, - /* association part */ - COEX_ASSOCIATED_IDLE = 6, - COEX_ASSOC_MANUAL_SCAN = 7, - COEX_ASSOC_AUTO_SCAN = 8, - COEX_ASSOC_ACTIVE_LEVEL = 9, - /* RF ON/OFF */ - COEX_RF_ON = 10, - COEX_RF_OFF = 11, - COEX_STAND_ALONE_DEBUG = 12, - /* IPAN */ - COEX_IPAN_ASSOC_LEVEL = 13, - /* reserved */ - COEX_RSRVD1 = 14, - COEX_RSRVD2 = 15, - COEX_NUM_OF_EVENTS = 16 -}; - -/* - * Coexistence WIFI/WIMAX Command - * COEX_PRIORITY_TABLE_CMD = 0x5a - * - */ -struct iwl_wimax_coex_event_entry { - u8 request_prio; - u8 win_medium_prio; - u8 reserved; - u8 flags; -} __packed; - -/* COEX flag masks */ - -/* Station table is valid */ -#define COEX_FLAGS_STA_TABLE_VALID_MSK (0x1) -/* UnMask wake up src at unassociated sleep */ -#define COEX_FLAGS_UNASSOC_WA_UNMASK_MSK (0x4) -/* UnMask wake up src at associated sleep */ -#define COEX_FLAGS_ASSOC_WA_UNMASK_MSK (0x8) -/* Enable CoEx feature. */ -#define COEX_FLAGS_COEX_ENABLE_MSK (0x80) - -struct iwl_wimax_coex_cmd { - u8 flags; - u8 reserved[3]; - struct iwl_wimax_coex_event_entry sta_prio[COEX_NUM_OF_EVENTS]; -} __packed; - -/* - * Coexistence MEDIUM NOTIFICATION - * COEX_MEDIUM_NOTIFICATION = 0x5b - * - * notification from uCode to host to indicate medium changes - * - */ -/* - * status field - * bit 0 - 2: medium status - * bit 3: medium change indication - * bit 4 - 31: reserved - */ -/* status option values, (0 - 2 bits) */ -#define COEX_MEDIUM_BUSY (0x0) /* radio belongs to WiMAX */ -#define COEX_MEDIUM_ACTIVE (0x1) /* radio belongs to WiFi */ -#define COEX_MEDIUM_PRE_RELEASE (0x2) /* received radio release */ -#define COEX_MEDIUM_MSK (0x7) - -/* send notification status (1 bit) */ -#define COEX_MEDIUM_CHANGED (0x8) -#define COEX_MEDIUM_CHANGED_MSK (0x8) -#define COEX_MEDIUM_SHIFT (3) - -struct iwl_coex_medium_notification { - __le32 status; - __le32 events; -} __packed; - -/* - * Coexistence EVENT Command - * COEX_EVENT_CMD = 0x5c - * - * send from host to uCode for coex event request. - */ -/* flags options */ -#define COEX_EVENT_REQUEST_MSK (0x1) - -struct iwl_coex_event_cmd { - u8 flags; - u8 event; - __le16 reserved; -} __packed; - -struct iwl_coex_event_resp { - __le32 status; -} __packed; - - -/****************************************************************************** - * Bluetooth Coexistence commands - * - *****************************************************************************/ - -/* - * BT Status notification - * REPLY_BT_COEX_PROFILE_NOTIF = 0xce - */ -enum iwl_bt_coex_profile_traffic_load { - IWL_BT_COEX_TRAFFIC_LOAD_NONE = 0, - IWL_BT_COEX_TRAFFIC_LOAD_LOW = 1, - IWL_BT_COEX_TRAFFIC_LOAD_HIGH = 2, - IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS = 3, -/* - * There are no more even though below is a u8, the - * indication from the BT device only has two bits. - */ -}; - -#define BT_SESSION_ACTIVITY_1_UART_MSG 0x1 -#define BT_SESSION_ACTIVITY_2_UART_MSG 0x2 - -/* BT UART message - Share Part (BT -> WiFi) */ -#define BT_UART_MSG_FRAME1MSGTYPE_POS (0) -#define BT_UART_MSG_FRAME1MSGTYPE_MSK \ - (0x7 << BT_UART_MSG_FRAME1MSGTYPE_POS) -#define BT_UART_MSG_FRAME1SSN_POS (3) -#define BT_UART_MSG_FRAME1SSN_MSK \ - (0x3 << BT_UART_MSG_FRAME1SSN_POS) -#define BT_UART_MSG_FRAME1UPDATEREQ_POS (5) -#define BT_UART_MSG_FRAME1UPDATEREQ_MSK \ - (0x1 << BT_UART_MSG_FRAME1UPDATEREQ_POS) -#define BT_UART_MSG_FRAME1RESERVED_POS (6) -#define BT_UART_MSG_FRAME1RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME1RESERVED_POS) - -#define BT_UART_MSG_FRAME2OPENCONNECTIONS_POS (0) -#define BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK \ - (0x3 << BT_UART_MSG_FRAME2OPENCONNECTIONS_POS) -#define BT_UART_MSG_FRAME2TRAFFICLOAD_POS (2) -#define BT_UART_MSG_FRAME2TRAFFICLOAD_MSK \ - (0x3 << BT_UART_MSG_FRAME2TRAFFICLOAD_POS) -#define BT_UART_MSG_FRAME2CHLSEQN_POS (4) -#define BT_UART_MSG_FRAME2CHLSEQN_MSK \ - (0x1 << BT_UART_MSG_FRAME2CHLSEQN_POS) -#define BT_UART_MSG_FRAME2INBAND_POS (5) -#define BT_UART_MSG_FRAME2INBAND_MSK \ - (0x1 << BT_UART_MSG_FRAME2INBAND_POS) -#define BT_UART_MSG_FRAME2RESERVED_POS (6) -#define BT_UART_MSG_FRAME2RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME2RESERVED_POS) - -#define BT_UART_MSG_FRAME3SCOESCO_POS (0) -#define BT_UART_MSG_FRAME3SCOESCO_MSK \ - (0x1 << BT_UART_MSG_FRAME3SCOESCO_POS) -#define BT_UART_MSG_FRAME3SNIFF_POS (1) -#define BT_UART_MSG_FRAME3SNIFF_MSK \ - (0x1 << BT_UART_MSG_FRAME3SNIFF_POS) -#define BT_UART_MSG_FRAME3A2DP_POS (2) -#define BT_UART_MSG_FRAME3A2DP_MSK \ - (0x1 << BT_UART_MSG_FRAME3A2DP_POS) -#define BT_UART_MSG_FRAME3ACL_POS (3) -#define BT_UART_MSG_FRAME3ACL_MSK \ - (0x1 << BT_UART_MSG_FRAME3ACL_POS) -#define BT_UART_MSG_FRAME3MASTER_POS (4) -#define BT_UART_MSG_FRAME3MASTER_MSK \ - (0x1 << BT_UART_MSG_FRAME3MASTER_POS) -#define BT_UART_MSG_FRAME3OBEX_POS (5) -#define BT_UART_MSG_FRAME3OBEX_MSK \ - (0x1 << BT_UART_MSG_FRAME3OBEX_POS) -#define BT_UART_MSG_FRAME3RESERVED_POS (6) -#define BT_UART_MSG_FRAME3RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME3RESERVED_POS) - -#define BT_UART_MSG_FRAME4IDLEDURATION_POS (0) -#define BT_UART_MSG_FRAME4IDLEDURATION_MSK \ - (0x3F << BT_UART_MSG_FRAME4IDLEDURATION_POS) -#define BT_UART_MSG_FRAME4RESERVED_POS (6) -#define BT_UART_MSG_FRAME4RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME4RESERVED_POS) - -#define BT_UART_MSG_FRAME5TXACTIVITY_POS (0) -#define BT_UART_MSG_FRAME5TXACTIVITY_MSK \ - (0x3 << BT_UART_MSG_FRAME5TXACTIVITY_POS) -#define BT_UART_MSG_FRAME5RXACTIVITY_POS (2) -#define BT_UART_MSG_FRAME5RXACTIVITY_MSK \ - (0x3 << BT_UART_MSG_FRAME5RXACTIVITY_POS) -#define BT_UART_MSG_FRAME5ESCORETRANSMIT_POS (4) -#define BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK \ - (0x3 << BT_UART_MSG_FRAME5ESCORETRANSMIT_POS) -#define BT_UART_MSG_FRAME5RESERVED_POS (6) -#define BT_UART_MSG_FRAME5RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME5RESERVED_POS) - -#define BT_UART_MSG_FRAME6SNIFFINTERVAL_POS (0) -#define BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK \ - (0x1F << BT_UART_MSG_FRAME6SNIFFINTERVAL_POS) -#define BT_UART_MSG_FRAME6DISCOVERABLE_POS (5) -#define BT_UART_MSG_FRAME6DISCOVERABLE_MSK \ - (0x1 << BT_UART_MSG_FRAME6DISCOVERABLE_POS) -#define BT_UART_MSG_FRAME6RESERVED_POS (6) -#define BT_UART_MSG_FRAME6RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME6RESERVED_POS) - -#define BT_UART_MSG_FRAME7SNIFFACTIVITY_POS (0) -#define BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK \ - (0x7 << BT_UART_MSG_FRAME7SNIFFACTIVITY_POS) -#define BT_UART_MSG_FRAME7PAGE_POS (3) -#define BT_UART_MSG_FRAME7PAGE_MSK \ - (0x1 << BT_UART_MSG_FRAME7PAGE_POS) -#define BT_UART_MSG_FRAME7INQUIRY_POS (4) -#define BT_UART_MSG_FRAME7INQUIRY_MSK \ - (0x1 << BT_UART_MSG_FRAME7INQUIRY_POS) -#define BT_UART_MSG_FRAME7CONNECTABLE_POS (5) -#define BT_UART_MSG_FRAME7CONNECTABLE_MSK \ - (0x1 << BT_UART_MSG_FRAME7CONNECTABLE_POS) -#define BT_UART_MSG_FRAME7RESERVED_POS (6) -#define BT_UART_MSG_FRAME7RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME7RESERVED_POS) - -/* BT Session Activity 2 UART message (BT -> WiFi) */ -#define BT_UART_MSG_2_FRAME1RESERVED1_POS (5) -#define BT_UART_MSG_2_FRAME1RESERVED1_MSK \ - (0x1< - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include "iwl-debug.h" -#include "iwl-io.h" -#include "dev.h" -#include "agn.h" - -/* create and remove of files */ -#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, priv, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ -} while (0) - -#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ - struct dentry *__tmp; \ - __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ - parent, ptr); \ - if (IS_ERR(__tmp) || !__tmp) \ - goto err; \ -} while (0) - -#define DEBUGFS_ADD_X32(name, parent, ptr) do { \ - struct dentry *__tmp; \ - __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \ - parent, ptr); \ - if (IS_ERR(__tmp) || !__tmp) \ - goto err; \ -} while (0) - -#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \ - struct dentry *__tmp; \ - __tmp = debugfs_create_u32(#name, mode, \ - parent, ptr); \ - if (IS_ERR(__tmp) || !__tmp) \ - goto err; \ -} while (0) - -/* file operation */ -#define DEBUGFS_READ_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define DEBUGFS_WRITE_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - - -#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -static ssize_t iwl_dbgfs_sram_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - u32 val = 0; - char *buf; - ssize_t ret; - int i = 0; - bool device_format = false; - int offset = 0; - int len = 0; - int pos = 0; - int sram; - struct iwl_priv *priv = file->private_data; - const struct fw_img *img; - size_t bufsz; - - if (!iwl_is_ready_rf(priv)) - return -EAGAIN; - - /* default is to dump the entire data segment */ - if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) { - priv->dbgfs_sram_offset = 0x800000; - if (!priv->ucode_loaded) - return -EINVAL; - img = &priv->fw->img[priv->cur_ucode]; - priv->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; - } - len = priv->dbgfs_sram_len; - - if (len == -4) { - device_format = true; - len = 4; - } - - bufsz = 50 + len * 4; - buf = kmalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", - len); - pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", - priv->dbgfs_sram_offset); - - /* adjust sram address since reads are only on even u32 boundaries */ - offset = priv->dbgfs_sram_offset & 0x3; - sram = priv->dbgfs_sram_offset & ~0x3; - - /* read the first u32 from sram */ - val = iwl_trans_read_mem32(priv->trans, sram); - - for (; len; len--) { - /* put the address at the start of every line */ - if (i == 0) - pos += scnprintf(buf + pos, bufsz - pos, - "%08X: ", sram + offset); - - if (device_format) - pos += scnprintf(buf + pos, bufsz - pos, - "%02x", (val >> (8 * (3 - offset))) & 0xff); - else - pos += scnprintf(buf + pos, bufsz - pos, - "%02x ", (val >> (8 * offset)) & 0xff); - - /* if all bytes processed, read the next u32 from sram */ - if (++offset == 4) { - sram += 4; - offset = 0; - val = iwl_trans_read_mem32(priv->trans, sram); - } - - /* put in extra spaces and split lines for human readability */ - if (++i == 16) { - i = 0; - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - } else if (!(i & 7)) { - pos += scnprintf(buf + pos, bufsz - pos, " "); - } else if (!(i & 3)) { - pos += scnprintf(buf + pos, bufsz - pos, " "); - } - } - if (i) - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_sram_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[64]; - int buf_size; - u32 offset, len; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (sscanf(buf, "%x,%x", &offset, &len) == 2) { - priv->dbgfs_sram_offset = offset; - priv->dbgfs_sram_len = len; - } else if (sscanf(buf, "%x", &offset) == 1) { - priv->dbgfs_sram_offset = offset; - priv->dbgfs_sram_len = -4; - } else { - priv->dbgfs_sram_offset = 0; - priv->dbgfs_sram_len = 0; - } - - return count; -} - -static ssize_t iwl_dbgfs_wowlan_sram_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - const struct fw_img *img = &priv->fw->img[IWL_UCODE_WOWLAN]; - - if (!priv->wowlan_sram) - return -ENODATA; - - return simple_read_from_buffer(user_buf, count, ppos, - priv->wowlan_sram, - img->sec[IWL_UCODE_SECTION_DATA].len); -} -static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - struct iwl_station_entry *station; - struct iwl_tid_data *tid_data; - char *buf; - int i, j, pos = 0; - ssize_t ret; - /* Add 30 for initial string */ - const size_t bufsz = 30 + sizeof(char) * 500 * (priv->num_stations); - - buf = kmalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n", - priv->num_stations); - - for (i = 0; i < IWLAGN_STATION_COUNT; i++) { - station = &priv->stations[i]; - if (!station->used) - continue; - pos += scnprintf(buf + pos, bufsz - pos, - "station %d - addr: %pM, flags: %#x\n", - i, station->sta.sta.addr, - station->sta.station_flags_msk); - pos += scnprintf(buf + pos, bufsz - pos, - "TID seqno next_rclmd " - "rate_n_flags state txq\n"); - - for (j = 0; j < IWL_MAX_TID_COUNT; j++) { - tid_data = &priv->tid_data[i][j]; - pos += scnprintf(buf + pos, bufsz - pos, - "%d: 0x%.4x 0x%.4x 0x%.8x " - "%d %.2d", - j, tid_data->seq_number, - tid_data->next_reclaimed, - tid_data->agg.rate_n_flags, - tid_data->agg.state, - tid_data->agg.txq_id); - - if (tid_data->agg.wait_for_ba) - pos += scnprintf(buf + pos, bufsz - pos, - " - waitforba"); - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - } - - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_nvm_read(struct file *file, - char __user *user_buf, - size_t count, - loff_t *ppos) -{ - ssize_t ret; - struct iwl_priv *priv = file->private_data; - int pos = 0, ofs = 0, buf_size = 0; - const u8 *ptr; - char *buf; - u16 nvm_ver; - size_t eeprom_len = priv->eeprom_blob_size; - buf_size = 4 * eeprom_len + 256; - - if (eeprom_len % 16) - return -ENODATA; - - ptr = priv->eeprom_blob; - if (!ptr) - return -ENOMEM; - - /* 4 characters for byte 0xYY */ - buf = kzalloc(buf_size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - nvm_ver = priv->nvm_data->nvm_version; - pos += scnprintf(buf + pos, buf_size - pos, - "NVM version: 0x%x\n", nvm_ver); - for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) { - pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x %16ph\n", - ofs, ptr + ofs); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - struct ieee80211_channel *channels = NULL; - const struct ieee80211_supported_band *supp_band = NULL; - int pos = 0, i, bufsz = PAGE_SIZE; - char *buf; - ssize_t ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ); - if (supp_band) { - channels = supp_band->channels; - - pos += scnprintf(buf + pos, bufsz - pos, - "Displaying %d channels in 2.4GHz band 802.11bg):\n", - supp_band->n_channels); - - for (i = 0; i < supp_band->n_channels; i++) - pos += scnprintf(buf + pos, bufsz - pos, - "%d: %ddBm: BSS%s%s, %s.\n", - channels[i].hw_value, - channels[i].max_power, - channels[i].flags & IEEE80211_CHAN_RADAR ? - " (IEEE 802.11h required)" : "", - ((channels[i].flags & IEEE80211_CHAN_NO_IR) - || (channels[i].flags & - IEEE80211_CHAN_RADAR)) ? "" : - ", IBSS", - channels[i].flags & - IEEE80211_CHAN_NO_IR ? - "passive only" : "active/passive"); - } - supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ); - if (supp_band) { - channels = supp_band->channels; - - pos += scnprintf(buf + pos, bufsz - pos, - "Displaying %d channels in 5.2GHz band (802.11a)\n", - supp_band->n_channels); - - for (i = 0; i < supp_band->n_channels; i++) - pos += scnprintf(buf + pos, bufsz - pos, - "%d: %ddBm: BSS%s%s, %s.\n", - channels[i].hw_value, - channels[i].max_power, - channels[i].flags & IEEE80211_CHAN_RADAR ? - " (IEEE 802.11h required)" : "", - ((channels[i].flags & IEEE80211_CHAN_NO_IR) - || (channels[i].flags & - IEEE80211_CHAN_RADAR)) ? "" : - ", IBSS", - channels[i].flags & - IEEE80211_CHAN_NO_IR ? - "passive only" : "active/passive"); - } - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_status_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - char buf[512]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n", - test_bit(STATUS_RF_KILL_HW, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n", - test_bit(STATUS_CT_KILL, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n", - test_bit(STATUS_ALIVE, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n", - test_bit(STATUS_READY, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n", - test_bit(STATUS_EXIT_PENDING, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n", - test_bit(STATUS_STATISTICS, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n", - test_bit(STATUS_SCANNING, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n", - test_bit(STATUS_SCAN_ABORTING, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n", - test_bit(STATUS_SCAN_HW, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n", - test_bit(STATUS_POWER_PMI, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n", - test_bit(STATUS_FW_ERROR, &priv->status)); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_rx_handlers_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - - int pos = 0; - int cnt = 0; - char *buf; - int bufsz = 24 * 64; /* 24 items * 64 char per item */ - ssize_t ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (cnt = 0; cnt < REPLY_MAX; cnt++) { - if (priv->rx_handlers_stats[cnt] > 0) - pos += scnprintf(buf + pos, bufsz - pos, - "\tRx handler[%36s]:\t\t %u\n", - iwl_dvm_get_cmd_string(cnt), - priv->rx_handlers_stats[cnt]); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_rx_handlers_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - - char buf[8]; - int buf_size; - u32 reset_flag; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x", &reset_flag) != 1) - return -EFAULT; - if (reset_flag == 0) - memset(&priv->rx_handlers_stats[0], 0, - sizeof(priv->rx_handlers_stats)); - - return count; -} - -static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - struct iwl_rxon_context *ctx; - int pos = 0, i; - char buf[256 * NUM_IWL_RXON_CTX]; - const size_t bufsz = sizeof(buf); - - for_each_context(priv, ctx) { - pos += scnprintf(buf + pos, bufsz - pos, "context %d:\n", - ctx->ctxid); - for (i = 0; i < AC_NUM; i++) { - pos += scnprintf(buf + pos, bufsz - pos, - "\tcw_min\tcw_max\taifsn\ttxop\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "AC[%d]\t%u\t%u\t%u\t%u\n", i, - ctx->qos_data.def_qos_parm.ac[i].cw_min, - ctx->qos_data.def_qos_parm.ac[i].cw_max, - ctx->qos_data.def_qos_parm.ac[i].aifsn, - ctx->qos_data.def_qos_parm.ac[i].edca_txop); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - } - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - struct iwl_tt_restriction *restriction; - char buf[100]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, - "Thermal Throttling Mode: %s\n", - tt->advanced_tt ? "Advance" : "Legacy"); - pos += scnprintf(buf + pos, bufsz - pos, - "Thermal Throttling State: %d\n", - tt->state); - if (tt->advanced_tt) { - restriction = tt->restriction + tt->state; - pos += scnprintf(buf + pos, bufsz - pos, - "Tx mode: %d\n", - restriction->tx_stream); - pos += scnprintf(buf + pos, bufsz - pos, - "Rx mode: %d\n", - restriction->rx_stream); - pos += scnprintf(buf + pos, bufsz - pos, - "HT mode: %d\n", - restriction->is_ht); - } - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_disable_ht40_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int ht40; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &ht40) != 1) - return -EFAULT; - if (!iwl_is_any_associated(priv)) - priv->disable_ht40 = ht40 ? true : false; - else - return -EINVAL; - - return count; -} - -static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[100]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, - "11n 40MHz Mode: %s\n", - priv->disable_ht40 ? "Disabled" : "Enabled"); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_temperature_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "%d\n", priv->temperature); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - - -static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int value; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (sscanf(buf, "%d", &value) != 1) - return -EINVAL; - - /* - * Our users expect 0 to be "CAM", but 0 isn't actually - * valid here. However, let's not confuse them and present - * IWL_POWER_INDEX_1 as "1", not "0". - */ - if (value == 0) - return -EINVAL; - else if (value > 0) - value -= 1; - - if (value != -1 && (value < 0 || value >= IWL_POWER_NUM)) - return -EINVAL; - - if (!iwl_is_ready_rf(priv)) - return -EAGAIN; - - priv->power_data.debug_sleep_level_override = value; - - mutex_lock(&priv->mutex); - iwl_power_update_mode(priv, true); - mutex_unlock(&priv->mutex); - - return count; -} - -static ssize_t iwl_dbgfs_sleep_level_override_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[10]; - int pos, value; - const size_t bufsz = sizeof(buf); - - /* see the write function */ - value = priv->power_data.debug_sleep_level_override; - if (value >= 0) - value += 1; - - pos = scnprintf(buf, bufsz, "%d\n", value); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[200]; - int pos = 0, i; - const size_t bufsz = sizeof(buf); - struct iwl_powertable_cmd *cmd = &priv->power_data.sleep_cmd; - - pos += scnprintf(buf + pos, bufsz - pos, - "flags: %#.2x\n", le16_to_cpu(cmd->flags)); - pos += scnprintf(buf + pos, bufsz - pos, - "RX/TX timeout: %d/%d usec\n", - le32_to_cpu(cmd->rx_data_timeout), - le32_to_cpu(cmd->tx_data_timeout)); - for (i = 0; i < IWL_POWER_VEC_SIZE; i++) - pos += scnprintf(buf + pos, bufsz - pos, - "sleep_interval[%d]: %d\n", i, - le32_to_cpu(cmd->sleep_interval[i])); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -DEBUGFS_READ_WRITE_FILE_OPS(sram); -DEBUGFS_READ_FILE_OPS(wowlan_sram); -DEBUGFS_READ_FILE_OPS(nvm); -DEBUGFS_READ_FILE_OPS(stations); -DEBUGFS_READ_FILE_OPS(channels); -DEBUGFS_READ_FILE_OPS(status); -DEBUGFS_READ_WRITE_FILE_OPS(rx_handlers); -DEBUGFS_READ_FILE_OPS(qos); -DEBUGFS_READ_FILE_OPS(thermal_throttling); -DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); -DEBUGFS_READ_FILE_OPS(temperature); -DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override); -DEBUGFS_READ_FILE_OPS(current_sleep_command); - -static const char *fmt_value = " %-30s %10u\n"; -static const char *fmt_hex = " %-30s 0x%02X\n"; -static const char *fmt_table = " %-30s %10u %10u %10u %10u\n"; -static const char *fmt_header = - "%-32s current cumulative delta max\n"; - -static int iwl_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz) -{ - int p = 0; - u32 flag; - - lockdep_assert_held(&priv->statistics.lock); - - flag = le32_to_cpu(priv->statistics.flag); - - p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", flag); - if (flag & UCODE_STATISTICS_CLEAR_MSK) - p += scnprintf(buf + p, bufsz - p, - "\tStatistics have been cleared\n"); - p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", - (flag & UCODE_STATISTICS_FREQUENCY_MSK) - ? "2.4 GHz" : "5.2 GHz"); - p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", - (flag & UCODE_STATISTICS_NARROW_BAND_MSK) - ? "enabled" : "disabled"); - - return p; -} - -static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - int pos = 0; - char *buf; - int bufsz = sizeof(struct statistics_rx_phy) * 40 + - sizeof(struct statistics_rx_non_phy) * 40 + - sizeof(struct statistics_rx_ht_phy) * 40 + 400; - ssize_t ret; - struct statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; - struct statistics_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; - struct statistics_rx_non_phy *general, *accum_general; - struct statistics_rx_non_phy *delta_general, *max_general; - struct statistics_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* - * the statistic information display here is based on - * the last statistics notification from uCode - * might not reflect the current uCode activity - */ - spin_lock_bh(&priv->statistics.lock); - ofdm = &priv->statistics.rx_ofdm; - cck = &priv->statistics.rx_cck; - general = &priv->statistics.rx_non_phy; - ht = &priv->statistics.rx_ofdm_ht; - accum_ofdm = &priv->accum_stats.rx_ofdm; - accum_cck = &priv->accum_stats.rx_cck; - accum_general = &priv->accum_stats.rx_non_phy; - accum_ht = &priv->accum_stats.rx_ofdm_ht; - delta_ofdm = &priv->delta_stats.rx_ofdm; - delta_cck = &priv->delta_stats.rx_cck; - delta_general = &priv->delta_stats.rx_non_phy; - delta_ht = &priv->delta_stats.rx_ofdm_ht; - max_ofdm = &priv->max_delta_stats.rx_ofdm; - max_cck = &priv->max_delta_stats.rx_cck; - max_general = &priv->max_delta_stats.rx_non_phy; - max_ht = &priv->max_delta_stats.rx_ofdm_ht; - - pos += iwl_statistics_flag(priv, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_Rx - OFDM:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "ina_cnt:", - le32_to_cpu(ofdm->ina_cnt), - accum_ofdm->ina_cnt, - delta_ofdm->ina_cnt, max_ofdm->ina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_cnt:", - le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, - delta_ofdm->fina_cnt, max_ofdm->fina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "plcp_err:", - le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, - delta_ofdm->plcp_err, max_ofdm->plcp_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_err:", - le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, - delta_ofdm->crc32_err, max_ofdm->crc32_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "overrun_err:", - le32_to_cpu(ofdm->overrun_err), - accum_ofdm->overrun_err, delta_ofdm->overrun_err, - max_ofdm->overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "early_overrun_err:", - le32_to_cpu(ofdm->early_overrun_err), - accum_ofdm->early_overrun_err, - delta_ofdm->early_overrun_err, - max_ofdm->early_overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_good:", - le32_to_cpu(ofdm->crc32_good), - accum_ofdm->crc32_good, delta_ofdm->crc32_good, - max_ofdm->crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "false_alarm_cnt:", - le32_to_cpu(ofdm->false_alarm_cnt), - accum_ofdm->false_alarm_cnt, - delta_ofdm->false_alarm_cnt, - max_ofdm->false_alarm_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_sync_err_cnt:", - le32_to_cpu(ofdm->fina_sync_err_cnt), - accum_ofdm->fina_sync_err_cnt, - delta_ofdm->fina_sync_err_cnt, - max_ofdm->fina_sync_err_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sfd_timeout:", - le32_to_cpu(ofdm->sfd_timeout), - accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout, - max_ofdm->sfd_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_timeout:", - le32_to_cpu(ofdm->fina_timeout), - accum_ofdm->fina_timeout, delta_ofdm->fina_timeout, - max_ofdm->fina_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "unresponded_rts:", - le32_to_cpu(ofdm->unresponded_rts), - accum_ofdm->unresponded_rts, - delta_ofdm->unresponded_rts, - max_ofdm->unresponded_rts); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "rxe_frame_lmt_ovrun:", - le32_to_cpu(ofdm->rxe_frame_limit_overrun), - accum_ofdm->rxe_frame_limit_overrun, - delta_ofdm->rxe_frame_limit_overrun, - max_ofdm->rxe_frame_limit_overrun); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_ack_cnt:", - le32_to_cpu(ofdm->sent_ack_cnt), - accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt, - max_ofdm->sent_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_cts_cnt:", - le32_to_cpu(ofdm->sent_cts_cnt), - accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt, - max_ofdm->sent_cts_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_ba_rsp_cnt:", - le32_to_cpu(ofdm->sent_ba_rsp_cnt), - accum_ofdm->sent_ba_rsp_cnt, - delta_ofdm->sent_ba_rsp_cnt, - max_ofdm->sent_ba_rsp_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "dsp_self_kill:", - le32_to_cpu(ofdm->dsp_self_kill), - accum_ofdm->dsp_self_kill, - delta_ofdm->dsp_self_kill, - max_ofdm->dsp_self_kill); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "mh_format_err:", - le32_to_cpu(ofdm->mh_format_err), - accum_ofdm->mh_format_err, - delta_ofdm->mh_format_err, - max_ofdm->mh_format_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "re_acq_main_rssi_sum:", - le32_to_cpu(ofdm->re_acq_main_rssi_sum), - accum_ofdm->re_acq_main_rssi_sum, - delta_ofdm->re_acq_main_rssi_sum, - max_ofdm->re_acq_main_rssi_sum); - - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_Rx - CCK:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "ina_cnt:", - le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, - delta_cck->ina_cnt, max_cck->ina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_cnt:", - le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, - delta_cck->fina_cnt, max_cck->fina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "plcp_err:", - le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, - delta_cck->plcp_err, max_cck->plcp_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_err:", - le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, - delta_cck->crc32_err, max_cck->crc32_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "overrun_err:", - le32_to_cpu(cck->overrun_err), - accum_cck->overrun_err, delta_cck->overrun_err, - max_cck->overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "early_overrun_err:", - le32_to_cpu(cck->early_overrun_err), - accum_cck->early_overrun_err, - delta_cck->early_overrun_err, - max_cck->early_overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_good:", - le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, - delta_cck->crc32_good, max_cck->crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "false_alarm_cnt:", - le32_to_cpu(cck->false_alarm_cnt), - accum_cck->false_alarm_cnt, - delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_sync_err_cnt:", - le32_to_cpu(cck->fina_sync_err_cnt), - accum_cck->fina_sync_err_cnt, - delta_cck->fina_sync_err_cnt, - max_cck->fina_sync_err_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sfd_timeout:", - le32_to_cpu(cck->sfd_timeout), - accum_cck->sfd_timeout, delta_cck->sfd_timeout, - max_cck->sfd_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_timeout:", - le32_to_cpu(cck->fina_timeout), - accum_cck->fina_timeout, delta_cck->fina_timeout, - max_cck->fina_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "unresponded_rts:", - le32_to_cpu(cck->unresponded_rts), - accum_cck->unresponded_rts, delta_cck->unresponded_rts, - max_cck->unresponded_rts); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "rxe_frame_lmt_ovrun:", - le32_to_cpu(cck->rxe_frame_limit_overrun), - accum_cck->rxe_frame_limit_overrun, - delta_cck->rxe_frame_limit_overrun, - max_cck->rxe_frame_limit_overrun); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_ack_cnt:", - le32_to_cpu(cck->sent_ack_cnt), - accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt, - max_cck->sent_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_cts_cnt:", - le32_to_cpu(cck->sent_cts_cnt), - accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt, - max_cck->sent_cts_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_ba_rsp_cnt:", - le32_to_cpu(cck->sent_ba_rsp_cnt), - accum_cck->sent_ba_rsp_cnt, - delta_cck->sent_ba_rsp_cnt, - max_cck->sent_ba_rsp_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "dsp_self_kill:", - le32_to_cpu(cck->dsp_self_kill), - accum_cck->dsp_self_kill, delta_cck->dsp_self_kill, - max_cck->dsp_self_kill); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "mh_format_err:", - le32_to_cpu(cck->mh_format_err), - accum_cck->mh_format_err, delta_cck->mh_format_err, - max_cck->mh_format_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "re_acq_main_rssi_sum:", - le32_to_cpu(cck->re_acq_main_rssi_sum), - accum_cck->re_acq_main_rssi_sum, - delta_cck->re_acq_main_rssi_sum, - max_cck->re_acq_main_rssi_sum); - - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_Rx - GENERAL:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "bogus_cts:", - le32_to_cpu(general->bogus_cts), - accum_general->bogus_cts, delta_general->bogus_cts, - max_general->bogus_cts); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "bogus_ack:", - le32_to_cpu(general->bogus_ack), - accum_general->bogus_ack, delta_general->bogus_ack, - max_general->bogus_ack); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "non_bssid_frames:", - le32_to_cpu(general->non_bssid_frames), - accum_general->non_bssid_frames, - delta_general->non_bssid_frames, - max_general->non_bssid_frames); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "filtered_frames:", - le32_to_cpu(general->filtered_frames), - accum_general->filtered_frames, - delta_general->filtered_frames, - max_general->filtered_frames); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "non_channel_beacons:", - le32_to_cpu(general->non_channel_beacons), - accum_general->non_channel_beacons, - delta_general->non_channel_beacons, - max_general->non_channel_beacons); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "channel_beacons:", - le32_to_cpu(general->channel_beacons), - accum_general->channel_beacons, - delta_general->channel_beacons, - max_general->channel_beacons); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "num_missed_bcon:", - le32_to_cpu(general->num_missed_bcon), - accum_general->num_missed_bcon, - delta_general->num_missed_bcon, - max_general->num_missed_bcon); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "adc_rx_saturation_time:", - le32_to_cpu(general->adc_rx_saturation_time), - accum_general->adc_rx_saturation_time, - delta_general->adc_rx_saturation_time, - max_general->adc_rx_saturation_time); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "ina_detect_search_tm:", - le32_to_cpu(general->ina_detection_search_time), - accum_general->ina_detection_search_time, - delta_general->ina_detection_search_time, - max_general->ina_detection_search_time); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_silence_rssi_a:", - le32_to_cpu(general->beacon_silence_rssi_a), - accum_general->beacon_silence_rssi_a, - delta_general->beacon_silence_rssi_a, - max_general->beacon_silence_rssi_a); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_silence_rssi_b:", - le32_to_cpu(general->beacon_silence_rssi_b), - accum_general->beacon_silence_rssi_b, - delta_general->beacon_silence_rssi_b, - max_general->beacon_silence_rssi_b); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_silence_rssi_c:", - le32_to_cpu(general->beacon_silence_rssi_c), - accum_general->beacon_silence_rssi_c, - delta_general->beacon_silence_rssi_c, - max_general->beacon_silence_rssi_c); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "interference_data_flag:", - le32_to_cpu(general->interference_data_flag), - accum_general->interference_data_flag, - delta_general->interference_data_flag, - max_general->interference_data_flag); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "channel_load:", - le32_to_cpu(general->channel_load), - accum_general->channel_load, - delta_general->channel_load, - max_general->channel_load); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "dsp_false_alarms:", - le32_to_cpu(general->dsp_false_alarms), - accum_general->dsp_false_alarms, - delta_general->dsp_false_alarms, - max_general->dsp_false_alarms); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_rssi_a:", - le32_to_cpu(general->beacon_rssi_a), - accum_general->beacon_rssi_a, - delta_general->beacon_rssi_a, - max_general->beacon_rssi_a); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_rssi_b:", - le32_to_cpu(general->beacon_rssi_b), - accum_general->beacon_rssi_b, - delta_general->beacon_rssi_b, - max_general->beacon_rssi_b); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_rssi_c:", - le32_to_cpu(general->beacon_rssi_c), - accum_general->beacon_rssi_c, - delta_general->beacon_rssi_c, - max_general->beacon_rssi_c); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_energy_a:", - le32_to_cpu(general->beacon_energy_a), - accum_general->beacon_energy_a, - delta_general->beacon_energy_a, - max_general->beacon_energy_a); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_energy_b:", - le32_to_cpu(general->beacon_energy_b), - accum_general->beacon_energy_b, - delta_general->beacon_energy_b, - max_general->beacon_energy_b); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_energy_c:", - le32_to_cpu(general->beacon_energy_c), - accum_general->beacon_energy_c, - delta_general->beacon_energy_c, - max_general->beacon_energy_c); - - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_Rx - OFDM_HT:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "plcp_err:", - le32_to_cpu(ht->plcp_err), accum_ht->plcp_err, - delta_ht->plcp_err, max_ht->plcp_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "overrun_err:", - le32_to_cpu(ht->overrun_err), accum_ht->overrun_err, - delta_ht->overrun_err, max_ht->overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "early_overrun_err:", - le32_to_cpu(ht->early_overrun_err), - accum_ht->early_overrun_err, - delta_ht->early_overrun_err, - max_ht->early_overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_good:", - le32_to_cpu(ht->crc32_good), accum_ht->crc32_good, - delta_ht->crc32_good, max_ht->crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_err:", - le32_to_cpu(ht->crc32_err), accum_ht->crc32_err, - delta_ht->crc32_err, max_ht->crc32_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "mh_format_err:", - le32_to_cpu(ht->mh_format_err), - accum_ht->mh_format_err, - delta_ht->mh_format_err, max_ht->mh_format_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg_crc32_good:", - le32_to_cpu(ht->agg_crc32_good), - accum_ht->agg_crc32_good, - delta_ht->agg_crc32_good, max_ht->agg_crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg_mpdu_cnt:", - le32_to_cpu(ht->agg_mpdu_cnt), - accum_ht->agg_mpdu_cnt, - delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg_cnt:", - le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt, - delta_ht->agg_cnt, max_ht->agg_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "unsupport_mcs:", - le32_to_cpu(ht->unsupport_mcs), - accum_ht->unsupport_mcs, - delta_ht->unsupport_mcs, max_ht->unsupport_mcs); - - spin_unlock_bh(&priv->statistics.lock); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - int pos = 0; - char *buf; - int bufsz = (sizeof(struct statistics_tx) * 48) + 250; - ssize_t ret; - struct statistics_tx *tx, *accum_tx, *delta_tx, *max_tx; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* the statistic information display here is based on - * the last statistics notification from uCode - * might not reflect the current uCode activity - */ - spin_lock_bh(&priv->statistics.lock); - - tx = &priv->statistics.tx; - accum_tx = &priv->accum_stats.tx; - delta_tx = &priv->delta_stats.tx; - max_tx = &priv->max_delta_stats.tx; - - pos += iwl_statistics_flag(priv, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_Tx:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "preamble:", - le32_to_cpu(tx->preamble_cnt), - accum_tx->preamble_cnt, - delta_tx->preamble_cnt, max_tx->preamble_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "rx_detected_cnt:", - le32_to_cpu(tx->rx_detected_cnt), - accum_tx->rx_detected_cnt, - delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "bt_prio_defer_cnt:", - le32_to_cpu(tx->bt_prio_defer_cnt), - accum_tx->bt_prio_defer_cnt, - delta_tx->bt_prio_defer_cnt, - max_tx->bt_prio_defer_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "bt_prio_kill_cnt:", - le32_to_cpu(tx->bt_prio_kill_cnt), - accum_tx->bt_prio_kill_cnt, - delta_tx->bt_prio_kill_cnt, - max_tx->bt_prio_kill_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "few_bytes_cnt:", - le32_to_cpu(tx->few_bytes_cnt), - accum_tx->few_bytes_cnt, - delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "cts_timeout:", - le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, - delta_tx->cts_timeout, max_tx->cts_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "ack_timeout:", - le32_to_cpu(tx->ack_timeout), - accum_tx->ack_timeout, - delta_tx->ack_timeout, max_tx->ack_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "expected_ack_cnt:", - le32_to_cpu(tx->expected_ack_cnt), - accum_tx->expected_ack_cnt, - delta_tx->expected_ack_cnt, - max_tx->expected_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "actual_ack_cnt:", - le32_to_cpu(tx->actual_ack_cnt), - accum_tx->actual_ack_cnt, - delta_tx->actual_ack_cnt, - max_tx->actual_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "dump_msdu_cnt:", - le32_to_cpu(tx->dump_msdu_cnt), - accum_tx->dump_msdu_cnt, - delta_tx->dump_msdu_cnt, - max_tx->dump_msdu_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "abort_nxt_frame_mismatch:", - le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt), - accum_tx->burst_abort_next_frame_mismatch_cnt, - delta_tx->burst_abort_next_frame_mismatch_cnt, - max_tx->burst_abort_next_frame_mismatch_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "abort_missing_nxt_frame:", - le32_to_cpu(tx->burst_abort_missing_next_frame_cnt), - accum_tx->burst_abort_missing_next_frame_cnt, - delta_tx->burst_abort_missing_next_frame_cnt, - max_tx->burst_abort_missing_next_frame_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "cts_timeout_collision:", - le32_to_cpu(tx->cts_timeout_collision), - accum_tx->cts_timeout_collision, - delta_tx->cts_timeout_collision, - max_tx->cts_timeout_collision); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "ack_ba_timeout_collision:", - le32_to_cpu(tx->ack_or_ba_timeout_collision), - accum_tx->ack_or_ba_timeout_collision, - delta_tx->ack_or_ba_timeout_collision, - max_tx->ack_or_ba_timeout_collision); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg ba_timeout:", - le32_to_cpu(tx->agg.ba_timeout), - accum_tx->agg.ba_timeout, - delta_tx->agg.ba_timeout, - max_tx->agg.ba_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg ba_resched_frames:", - le32_to_cpu(tx->agg.ba_reschedule_frames), - accum_tx->agg.ba_reschedule_frames, - delta_tx->agg.ba_reschedule_frames, - max_tx->agg.ba_reschedule_frames); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg scd_query_agg_frame:", - le32_to_cpu(tx->agg.scd_query_agg_frame_cnt), - accum_tx->agg.scd_query_agg_frame_cnt, - delta_tx->agg.scd_query_agg_frame_cnt, - max_tx->agg.scd_query_agg_frame_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg scd_query_no_agg:", - le32_to_cpu(tx->agg.scd_query_no_agg), - accum_tx->agg.scd_query_no_agg, - delta_tx->agg.scd_query_no_agg, - max_tx->agg.scd_query_no_agg); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg scd_query_agg:", - le32_to_cpu(tx->agg.scd_query_agg), - accum_tx->agg.scd_query_agg, - delta_tx->agg.scd_query_agg, - max_tx->agg.scd_query_agg); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg scd_query_mismatch:", - le32_to_cpu(tx->agg.scd_query_mismatch), - accum_tx->agg.scd_query_mismatch, - delta_tx->agg.scd_query_mismatch, - max_tx->agg.scd_query_mismatch); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg frame_not_ready:", - le32_to_cpu(tx->agg.frame_not_ready), - accum_tx->agg.frame_not_ready, - delta_tx->agg.frame_not_ready, - max_tx->agg.frame_not_ready); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg underrun:", - le32_to_cpu(tx->agg.underrun), - accum_tx->agg.underrun, - delta_tx->agg.underrun, max_tx->agg.underrun); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg bt_prio_kill:", - le32_to_cpu(tx->agg.bt_prio_kill), - accum_tx->agg.bt_prio_kill, - delta_tx->agg.bt_prio_kill, - max_tx->agg.bt_prio_kill); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg rx_ba_rsp_cnt:", - le32_to_cpu(tx->agg.rx_ba_rsp_cnt), - accum_tx->agg.rx_ba_rsp_cnt, - delta_tx->agg.rx_ba_rsp_cnt, - max_tx->agg.rx_ba_rsp_cnt); - - if (tx->tx_power.ant_a || tx->tx_power.ant_b || tx->tx_power.ant_c) { - pos += scnprintf(buf + pos, bufsz - pos, - "tx power: (1/2 dB step)\n"); - if ((priv->nvm_data->valid_tx_ant & ANT_A) && - tx->tx_power.ant_a) - pos += scnprintf(buf + pos, bufsz - pos, - fmt_hex, "antenna A:", - tx->tx_power.ant_a); - if ((priv->nvm_data->valid_tx_ant & ANT_B) && - tx->tx_power.ant_b) - pos += scnprintf(buf + pos, bufsz - pos, - fmt_hex, "antenna B:", - tx->tx_power.ant_b); - if ((priv->nvm_data->valid_tx_ant & ANT_C) && - tx->tx_power.ant_c) - pos += scnprintf(buf + pos, bufsz - pos, - fmt_hex, "antenna C:", - tx->tx_power.ant_c); - } - - spin_unlock_bh(&priv->statistics.lock); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - int pos = 0; - char *buf; - int bufsz = sizeof(struct statistics_general) * 10 + 300; - ssize_t ret; - struct statistics_general_common *general, *accum_general; - struct statistics_general_common *delta_general, *max_general; - struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; - struct statistics_div *div, *accum_div, *delta_div, *max_div; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* the statistic information display here is based on - * the last statistics notification from uCode - * might not reflect the current uCode activity - */ - - spin_lock_bh(&priv->statistics.lock); - - general = &priv->statistics.common; - dbg = &priv->statistics.common.dbg; - div = &priv->statistics.common.div; - accum_general = &priv->accum_stats.common; - accum_dbg = &priv->accum_stats.common.dbg; - accum_div = &priv->accum_stats.common.div; - delta_general = &priv->delta_stats.common; - max_general = &priv->max_delta_stats.common; - delta_dbg = &priv->delta_stats.common.dbg; - max_dbg = &priv->max_delta_stats.common.dbg; - delta_div = &priv->delta_stats.common.div; - max_div = &priv->max_delta_stats.common.div; - - pos += iwl_statistics_flag(priv, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_General:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_value, "temperature:", - le32_to_cpu(general->temperature)); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_value, "temperature_m:", - le32_to_cpu(general->temperature_m)); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_value, "ttl_timestamp:", - le32_to_cpu(general->ttl_timestamp)); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "burst_check:", - le32_to_cpu(dbg->burst_check), - accum_dbg->burst_check, - delta_dbg->burst_check, max_dbg->burst_check); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "burst_count:", - le32_to_cpu(dbg->burst_count), - accum_dbg->burst_count, - delta_dbg->burst_count, max_dbg->burst_count); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "wait_for_silence_timeout_count:", - le32_to_cpu(dbg->wait_for_silence_timeout_cnt), - accum_dbg->wait_for_silence_timeout_cnt, - delta_dbg->wait_for_silence_timeout_cnt, - max_dbg->wait_for_silence_timeout_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sleep_time:", - le32_to_cpu(general->sleep_time), - accum_general->sleep_time, - delta_general->sleep_time, max_general->sleep_time); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "slots_out:", - le32_to_cpu(general->slots_out), - accum_general->slots_out, - delta_general->slots_out, max_general->slots_out); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "slots_idle:", - le32_to_cpu(general->slots_idle), - accum_general->slots_idle, - delta_general->slots_idle, max_general->slots_idle); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "tx_on_a:", - le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, - delta_div->tx_on_a, max_div->tx_on_a); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "tx_on_b:", - le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, - delta_div->tx_on_b, max_div->tx_on_b); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "exec_time:", - le32_to_cpu(div->exec_time), accum_div->exec_time, - delta_div->exec_time, max_div->exec_time); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "probe_time:", - le32_to_cpu(div->probe_time), accum_div->probe_time, - delta_div->probe_time, max_div->probe_time); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "rx_enable_counter:", - le32_to_cpu(general->rx_enable_counter), - accum_general->rx_enable_counter, - delta_general->rx_enable_counter, - max_general->rx_enable_counter); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "num_of_sos_states:", - le32_to_cpu(general->num_of_sos_states), - accum_general->num_of_sos_states, - delta_general->num_of_sos_states, - max_general->num_of_sos_states); - - spin_unlock_bh(&priv->statistics.lock); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - int pos = 0; - char *buf; - int bufsz = (sizeof(struct statistics_bt_activity) * 24) + 200; - ssize_t ret; - struct statistics_bt_activity *bt, *accum_bt; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - if (!priv->bt_enable_flag) - return -EINVAL; - - /* make request to uCode to retrieve statistics information */ - mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, 0, false); - mutex_unlock(&priv->mutex); - - if (ret) - return -EAGAIN; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* - * the statistic information display here is based on - * the last statistics notification from uCode - * might not reflect the current uCode activity - */ - - spin_lock_bh(&priv->statistics.lock); - - bt = &priv->statistics.bt_activity; - accum_bt = &priv->accum_stats.bt_activity; - - pos += iwl_statistics_flag(priv, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, "Statistics_BT:\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "\t\t\tcurrent\t\t\taccumulative\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "hi_priority_tx_req_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(bt->hi_priority_tx_req_cnt), - accum_bt->hi_priority_tx_req_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "hi_priority_tx_denied_cnt:\t%u\t\t\t%u\n", - le32_to_cpu(bt->hi_priority_tx_denied_cnt), - accum_bt->hi_priority_tx_denied_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "lo_priority_tx_req_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(bt->lo_priority_tx_req_cnt), - accum_bt->lo_priority_tx_req_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "lo_priority_tx_denied_cnt:\t%u\t\t\t%u\n", - le32_to_cpu(bt->lo_priority_tx_denied_cnt), - accum_bt->lo_priority_tx_denied_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "hi_priority_rx_req_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(bt->hi_priority_rx_req_cnt), - accum_bt->hi_priority_rx_req_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "hi_priority_rx_denied_cnt:\t%u\t\t\t%u\n", - le32_to_cpu(bt->hi_priority_rx_denied_cnt), - accum_bt->hi_priority_rx_denied_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "lo_priority_rx_req_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(bt->lo_priority_rx_req_cnt), - accum_bt->lo_priority_rx_req_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "lo_priority_rx_denied_cnt:\t%u\t\t\t%u\n", - le32_to_cpu(bt->lo_priority_rx_denied_cnt), - accum_bt->lo_priority_rx_denied_cnt); - - pos += scnprintf(buf + pos, bufsz - pos, - "(rx)num_bt_kills:\t\t%u\t\t\t%u\n", - le32_to_cpu(priv->statistics.num_bt_kills), - priv->statistics.accum_num_bt_kills); - - spin_unlock_bh(&priv->statistics.lock); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_reply_tx_error_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - int pos = 0; - char *buf; - int bufsz = (sizeof(struct reply_tx_error_statistics) * 24) + - (sizeof(struct reply_agg_tx_error_statistics) * 24) + 200; - ssize_t ret; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, "Statistics_TX_Error:\n"); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_DELAY), - priv->reply_tx_stats.pp_delay); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_FEW_BYTES), - priv->reply_tx_stats.pp_few_bytes); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_BT_PRIO), - priv->reply_tx_stats.pp_bt_prio); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_QUIET_PERIOD), - priv->reply_tx_stats.pp_quiet_period); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_CALC_TTAK), - priv->reply_tx_stats.pp_calc_ttak); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_tx_fail_reason( - TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY), - priv->reply_tx_stats.int_crossed_retry); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_SHORT_LIMIT), - priv->reply_tx_stats.short_limit); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_LONG_LIMIT), - priv->reply_tx_stats.long_limit); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_UNDERRUN), - priv->reply_tx_stats.fifo_underrun); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_DRAIN_FLOW), - priv->reply_tx_stats.drain_flow); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_RFKILL_FLUSH), - priv->reply_tx_stats.rfkill_flush); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_LIFE_EXPIRE), - priv->reply_tx_stats.life_expire); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_DEST_PS), - priv->reply_tx_stats.dest_ps); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_HOST_ABORTED), - priv->reply_tx_stats.host_abort); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_BT_RETRY), - priv->reply_tx_stats.pp_delay); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_STA_INVALID), - priv->reply_tx_stats.sta_invalid); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_FRAG_DROPPED), - priv->reply_tx_stats.frag_drop); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_TID_DISABLE), - priv->reply_tx_stats.tid_disable); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_FLUSHED), - priv->reply_tx_stats.fifo_flush); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_tx_fail_reason( - TX_STATUS_FAIL_INSUFFICIENT_CF_POLL), - priv->reply_tx_stats.insuff_cf_poll); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_PASSIVE_NO_RX), - priv->reply_tx_stats.fail_hw_drop); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_tx_fail_reason( - TX_STATUS_FAIL_NO_BEACON_ON_RADAR), - priv->reply_tx_stats.sta_color_mismatch); - pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n", - priv->reply_tx_stats.unknown); - - pos += scnprintf(buf + pos, bufsz - pos, - "\nStatistics_Agg_TX_Error:\n"); - - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_UNDERRUN_MSK), - priv->reply_agg_tx_stats.underrun); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_BT_PRIO_MSK), - priv->reply_agg_tx_stats.bt_prio); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_FEW_BYTES_MSK), - priv->reply_agg_tx_stats.few_bytes); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_ABORT_MSK), - priv->reply_agg_tx_stats.abort); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_agg_tx_fail_reason( - AGG_TX_STATE_LAST_SENT_TTL_MSK), - priv->reply_agg_tx_stats.last_sent_ttl); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_agg_tx_fail_reason( - AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK), - priv->reply_agg_tx_stats.last_sent_try); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_agg_tx_fail_reason( - AGG_TX_STATE_LAST_SENT_BT_KILL_MSK), - priv->reply_agg_tx_stats.last_sent_bt_kill); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_SCD_QUERY_MSK), - priv->reply_agg_tx_stats.scd_query); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_agg_tx_fail_reason( - AGG_TX_STATE_TEST_BAD_CRC32_MSK), - priv->reply_agg_tx_stats.bad_crc32); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_RESPONSE_MSK), - priv->reply_agg_tx_stats.response); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DUMP_TX_MSK), - priv->reply_agg_tx_stats.dump_tx); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DELAY_TX_MSK), - priv->reply_agg_tx_stats.delay_tx); - pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n", - priv->reply_agg_tx_stats.unknown); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_sensitivity_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int pos = 0; - int cnt = 0; - char *buf; - int bufsz = sizeof(struct iwl_sensitivity_data) * 4 + 100; - ssize_t ret; - struct iwl_sensitivity_data *data; - - data = &priv->sensitivity_data; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n", - data->auto_corr_ofdm); - pos += scnprintf(buf + pos, bufsz - pos, - "auto_corr_ofdm_mrc:\t\t %u\n", - data->auto_corr_ofdm_mrc); - pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n", - data->auto_corr_ofdm_x1); - pos += scnprintf(buf + pos, bufsz - pos, - "auto_corr_ofdm_mrc_x1:\t\t %u\n", - data->auto_corr_ofdm_mrc_x1); - pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n", - data->auto_corr_cck); - pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n", - data->auto_corr_cck_mrc); - pos += scnprintf(buf + pos, bufsz - pos, - "last_bad_plcp_cnt_ofdm:\t\t %u\n", - data->last_bad_plcp_cnt_ofdm); - pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n", - data->last_fa_cnt_ofdm); - pos += scnprintf(buf + pos, bufsz - pos, - "last_bad_plcp_cnt_cck:\t\t %u\n", - data->last_bad_plcp_cnt_cck); - pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n", - data->last_fa_cnt_cck); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n", - data->nrg_curr_state); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n", - data->nrg_prev_state); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t"); - for (cnt = 0; cnt < 10; cnt++) { - pos += scnprintf(buf + pos, bufsz - pos, " %u", - data->nrg_value[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t"); - for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) { - pos += scnprintf(buf + pos, bufsz - pos, " %u", - data->nrg_silence_rssi[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n", - data->nrg_silence_ref); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n", - data->nrg_energy_idx); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n", - data->nrg_silence_idx); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n", - data->nrg_th_cck); - pos += scnprintf(buf + pos, bufsz - pos, - "nrg_auto_corr_silence_diff:\t %u\n", - data->nrg_auto_corr_silence_diff); - pos += scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n", - data->num_in_cck_no_fa); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n", - data->nrg_th_ofdm); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - - -static ssize_t iwl_dbgfs_chain_noise_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int pos = 0; - int cnt = 0; - char *buf; - int bufsz = sizeof(struct iwl_chain_noise_data) * 4 + 100; - ssize_t ret; - struct iwl_chain_noise_data *data; - - data = &priv->chain_noise_data; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n", - data->active_chains); - pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n", - data->chain_noise_a); - pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n", - data->chain_noise_b); - pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n", - data->chain_noise_c); - pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n", - data->chain_signal_a); - pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n", - data->chain_signal_b); - pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n", - data->chain_signal_c); - pos += scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n", - data->beacon_count); - - pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t"); - for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { - pos += scnprintf(buf + pos, bufsz - pos, " %u", - data->disconn_array[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t"); - for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { - pos += scnprintf(buf + pos, bufsz - pos, " %u", - data->delta_gain_code[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n", - data->radio_write); - pos += scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n", - data->state); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_power_save_status_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[60]; - int pos = 0; - const size_t bufsz = sizeof(buf); - u32 pwrsave_status; - - pwrsave_status = iwl_read32(priv->trans, CSR_GP_CNTRL) & - CSR_GP_REG_POWER_SAVE_STATUS_MSK; - - pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: "); - pos += scnprintf(buf + pos, bufsz - pos, "%s\n", - (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" : - (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" : - (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" : - "error"); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int clear; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &clear) != 1) - return -EFAULT; - - /* make request to uCode to retrieve statistics information */ - mutex_lock(&priv->mutex); - iwl_send_statistics_request(priv, 0, true); - mutex_unlock(&priv->mutex); - - return count; -} - -static ssize_t iwl_dbgfs_ucode_tracing_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int pos = 0; - char buf[128]; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "ucode trace timer is %s\n", - priv->event_log.ucode_trace ? "On" : "Off"); - pos += scnprintf(buf + pos, bufsz - pos, "non_wraps_count:\t\t %u\n", - priv->event_log.non_wraps_count); - pos += scnprintf(buf + pos, bufsz - pos, "wraps_once_count:\t\t %u\n", - priv->event_log.wraps_once_count); - pos += scnprintf(buf + pos, bufsz - pos, "wraps_more_count:\t\t %u\n", - priv->event_log.wraps_more_count); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int trace; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &trace) != 1) - return -EFAULT; - - if (trace) { - priv->event_log.ucode_trace = true; - if (iwl_is_alive(priv)) { - /* start collecting data now */ - mod_timer(&priv->ucode_trace, jiffies); - } - } else { - priv->event_log.ucode_trace = false; - del_timer_sync(&priv->ucode_trace); - } - - return count; -} - -static ssize_t iwl_dbgfs_rxon_flags_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int len = 0; - char buf[20]; - - len = sprintf(buf, "0x%04X\n", - le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.flags)); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t iwl_dbgfs_rxon_filter_flags_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int len = 0; - char buf[20]; - - len = sprintf(buf, "0x%04X\n", - le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags)); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t iwl_dbgfs_missed_beacon_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int pos = 0; - char buf[12]; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "%d\n", - priv->missed_beacon_threshold); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_missed_beacon_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int missed; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &missed) != 1) - return -EINVAL; - - if (missed < IWL_MISSED_BEACON_THRESHOLD_MIN || - missed > IWL_MISSED_BEACON_THRESHOLD_MAX) - priv->missed_beacon_threshold = - IWL_MISSED_BEACON_THRESHOLD_DEF; - else - priv->missed_beacon_threshold = missed; - - return count; -} - -static ssize_t iwl_dbgfs_plcp_delta_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int pos = 0; - char buf[12]; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "%u\n", - priv->plcp_delta_threshold); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int plcp; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &plcp) != 1) - return -EINVAL; - if ((plcp < IWL_MAX_PLCP_ERR_THRESHOLD_MIN) || - (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX)) - priv->plcp_delta_threshold = - IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE; - else - priv->plcp_delta_threshold = plcp; - return count; -} - -static ssize_t iwl_dbgfs_rf_reset_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - int pos = 0; - char buf[300]; - const size_t bufsz = sizeof(buf); - struct iwl_rf_reset *rf_reset = &priv->rf_reset; - - pos += scnprintf(buf + pos, bufsz - pos, - "RF reset statistics\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "\tnumber of reset request: %d\n", - rf_reset->reset_request_count); - pos += scnprintf(buf + pos, bufsz - pos, - "\tnumber of reset request success: %d\n", - rf_reset->reset_success_count); - pos += scnprintf(buf + pos, bufsz - pos, - "\tnumber of reset request reject: %d\n", - rf_reset->reset_reject_count); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_rf_reset_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int ret; - - ret = iwl_force_rf_reset(priv, true); - return ret ? ret : count; -} - -static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int flush; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &flush) != 1) - return -EINVAL; - - if (iwl_is_rfkill(priv)) - return -EFAULT; - - iwlagn_dev_txfifo_flush(priv); - - return count; -} - -static ssize_t iwl_dbgfs_bt_traffic_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - int pos = 0; - char buf[200]; - const size_t bufsz = sizeof(buf); - - if (!priv->bt_enable_flag) { - pos += scnprintf(buf + pos, bufsz - pos, "BT coex disabled\n"); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); - } - pos += scnprintf(buf + pos, bufsz - pos, "BT enable flag: 0x%x\n", - priv->bt_enable_flag); - pos += scnprintf(buf + pos, bufsz - pos, "BT in %s mode\n", - priv->bt_full_concurrent ? "full concurrency" : "3-wire"); - pos += scnprintf(buf + pos, bufsz - pos, "BT status: %s, " - "last traffic notif: %d\n", - priv->bt_status ? "On" : "Off", priv->last_bt_traffic_load); - pos += scnprintf(buf + pos, bufsz - pos, "ch_announcement: %d, " - "kill_ack_mask: %x, kill_cts_mask: %x\n", - priv->bt_ch_announce, priv->kill_ack_mask, - priv->kill_cts_mask); - - pos += scnprintf(buf + pos, bufsz - pos, "bluetooth traffic load: "); - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - pos += scnprintf(buf + pos, bufsz - pos, "Continuous\n"); - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - pos += scnprintf(buf + pos, bufsz - pos, "High\n"); - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - pos += scnprintf(buf + pos, bufsz - pos, "Low\n"); - break; - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - default: - pos += scnprintf(buf + pos, bufsz - pos, "None\n"); - break; - } - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_protection_mode_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - - int pos = 0; - char buf[40]; - const size_t bufsz = sizeof(buf); - - if (priv->cfg->ht_params) - pos += scnprintf(buf + pos, bufsz - pos, - "use %s for aggregation\n", - (priv->hw_params.use_rts_for_aggregation) ? - "rts/cts" : "cts-to-self"); - else - pos += scnprintf(buf + pos, bufsz - pos, "N/A"); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_protection_mode_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int rts; - - if (!priv->cfg->ht_params) - return -EINVAL; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &rts) != 1) - return -EINVAL; - if (rts) - priv->hw_params.use_rts_for_aggregation = true; - else - priv->hw_params.use_rts_for_aggregation = false; - return count; -} - -static int iwl_cmd_echo_test(struct iwl_priv *priv) -{ - int ret; - struct iwl_host_cmd cmd = { - .id = REPLY_ECHO, - .len = { 0 }, - }; - - ret = iwl_dvm_send_cmd(priv, &cmd); - if (ret) - IWL_ERR(priv, "echo testing fail: 0X%x\n", ret); - else - IWL_DEBUG_INFO(priv, "echo testing pass\n"); - return ret; -} - -static ssize_t iwl_dbgfs_echo_test_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - iwl_cmd_echo_test(priv); - return count; -} - -#ifdef CONFIG_IWLWIFI_DEBUG -static ssize_t iwl_dbgfs_log_event_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char *buf = NULL; - ssize_t ret; - - ret = iwl_dump_nic_event_log(priv, true, &buf); - if (ret > 0) - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_log_event_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - u32 event_log_flag; - char buf[8]; - int buf_size; - - /* check that the interface is up */ - if (!iwl_is_ready(priv)) - return -EAGAIN; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &event_log_flag) != 1) - return -EFAULT; - if (event_log_flag == 1) - iwl_dump_nic_event_log(priv, true, NULL); - - return count; -} -#endif - -static ssize_t iwl_dbgfs_calib_disabled_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[120]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, - "Sensitivity calibrations %s\n", - (priv->calib_disabled & - IWL_SENSITIVITY_CALIB_DISABLED) ? - "DISABLED" : "ENABLED"); - pos += scnprintf(buf + pos, bufsz - pos, - "Chain noise calibrations %s\n", - (priv->calib_disabled & - IWL_CHAIN_NOISE_CALIB_DISABLED) ? - "DISABLED" : "ENABLED"); - pos += scnprintf(buf + pos, bufsz - pos, - "Tx power calibrations %s\n", - (priv->calib_disabled & - IWL_TX_POWER_CALIB_DISABLED) ? - "DISABLED" : "ENABLED"); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_calib_disabled_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - u32 calib_disabled; - int buf_size; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x", &calib_disabled) != 1) - return -EFAULT; - - priv->calib_disabled = calib_disabled; - - return count; -} - -static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - bool restart_fw = iwlwifi_mod_params.restart_fw; - int ret; - - iwlwifi_mod_params.restart_fw = true; - - mutex_lock(&priv->mutex); - - /* take the return value to make compiler happy - it will fail anyway */ - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, 0, 0, NULL); - - mutex_unlock(&priv->mutex); - - iwlwifi_mod_params.restart_fw = restart_fw; - - return count; -} - -DEBUGFS_READ_FILE_OPS(ucode_rx_stats); -DEBUGFS_READ_FILE_OPS(ucode_tx_stats); -DEBUGFS_READ_FILE_OPS(ucode_general_stats); -DEBUGFS_READ_FILE_OPS(sensitivity); -DEBUGFS_READ_FILE_OPS(chain_noise); -DEBUGFS_READ_FILE_OPS(power_save_status); -DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics); -DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing); -DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon); -DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta); -DEBUGFS_READ_WRITE_FILE_OPS(rf_reset); -DEBUGFS_READ_FILE_OPS(rxon_flags); -DEBUGFS_READ_FILE_OPS(rxon_filter_flags); -DEBUGFS_WRITE_FILE_OPS(txfifo_flush); -DEBUGFS_READ_FILE_OPS(ucode_bt_stats); -DEBUGFS_READ_FILE_OPS(bt_traffic); -DEBUGFS_READ_WRITE_FILE_OPS(protection_mode); -DEBUGFS_READ_FILE_OPS(reply_tx_error); -DEBUGFS_WRITE_FILE_OPS(echo_test); -DEBUGFS_WRITE_FILE_OPS(fw_restart); -#ifdef CONFIG_IWLWIFI_DEBUG -DEBUGFS_READ_WRITE_FILE_OPS(log_event); -#endif -DEBUGFS_READ_WRITE_FILE_OPS(calib_disabled); - -/* - * Create the debugfs files and directories - * - */ -int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir) -{ - struct dentry *dir_data, *dir_rf, *dir_debug; - - priv->debugfs_dir = dbgfs_dir; - - dir_data = debugfs_create_dir("data", dbgfs_dir); - if (!dir_data) - goto err; - dir_rf = debugfs_create_dir("rf", dbgfs_dir); - if (!dir_rf) - goto err; - dir_debug = debugfs_create_dir("debug", dbgfs_dir); - if (!dir_debug) - goto err; - - DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(wowlan_sram, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(rx_handlers, dir_data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(sleep_level_override, dir_data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(current_sleep_command, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(thermal_throttling, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(temperature, dir_data, S_IRUSR); - - DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(rf_reset, dir_debug, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(ucode_bt_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(reply_tx_error, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(fw_restart, dir_debug, S_IWUSR); -#ifdef CONFIG_IWLWIFI_DEBUG - DEBUGFS_ADD_FILE(log_event, dir_debug, S_IWUSR | S_IRUSR); -#endif - - if (iwl_advanced_bt_coexist(priv)) - DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR); - - /* Calibrations disabled/enabled status*/ - DEBUGFS_ADD_FILE(calib_disabled, dir_rf, S_IWUSR | S_IRUSR); - - /* - * Create a symlink with mac80211. This is not very robust, as it does - * not remove the symlink created. The implicit assumption is that - * when the opmode exits, mac80211 will also exit, and will remove - * this symlink as part of its cleanup. - */ - if (priv->mac80211_registered) { - char buf[100]; - struct dentry *mac80211_dir, *dev_dir, *root_dir; - - dev_dir = dbgfs_dir->d_parent; - root_dir = dev_dir->d_parent; - mac80211_dir = priv->hw->wiphy->debugfsdir; - - snprintf(buf, 100, "../../%s/%s", root_dir->d_name.name, - dev_dir->d_name.name); - - if (!debugfs_create_symlink("iwlwifi", mac80211_dir, buf)) - goto err; - } - - return 0; - -err: - IWL_ERR(priv, "failed to create the dvm debugfs entries\n"); - return -ENOMEM; -} diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h deleted file mode 100644 index 0ba3e56d6015..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ /dev/null @@ -1,949 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -/* - * Please use this file (dev.h) for driver implementation definitions. - * Please use commands.h for uCode API definitions. - */ - -#ifndef __iwl_dev_h__ -#define __iwl_dev_h__ - -#include -#include -#include -#include -#include -#include - -#include "iwl-fw.h" -#include "iwl-eeprom-parse.h" -#include "iwl-csr.h" -#include "iwl-debug.h" -#include "iwl-agn-hw.h" -#include "iwl-op-mode.h" -#include "iwl-notif-wait.h" -#include "iwl-trans.h" - -#include "led.h" -#include "power.h" -#include "rs.h" -#include "tt.h" - -/* CT-KILL constants */ -#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ -#define CT_KILL_THRESHOLD 114 /* in Celsius */ -#define CT_KILL_EXIT_THRESHOLD 95 /* in Celsius */ - -/* Default noise level to report when noise measurement is not available. - * This may be because we're: - * 1) Not associated no beacon statistics being sent to driver) - * 2) Scanning (noise measurement does not apply to associated channel) - * Use default noise value of -127 ... this is below the range of measurable - * Rx dBm for all agn devices, so it can indicate "unmeasurable" to user. - * Also, -127 works better than 0 when averaging frames with/without - * noise info (e.g. averaging might be done in app); measured dBm values are - * always negative ... using a negative value as the default keeps all - * averages within an s8's (used in some apps) range of negative values. */ -#define IWL_NOISE_MEAS_NOT_AVAILABLE (-127) - -/* - * RTS threshold here is total size [2347] minus 4 FCS bytes - * Per spec: - * a value of 0 means RTS on all data/management packets - * a value > max MSDU size means no RTS - * else RTS for data/management frames where MPDU is larger - * than RTS value. - */ -#define DEFAULT_RTS_THRESHOLD 2347U -#define MIN_RTS_THRESHOLD 0U -#define MAX_RTS_THRESHOLD 2347U -#define MAX_MSDU_SIZE 2304U -#define MAX_MPDU_SIZE 2346U -#define DEFAULT_BEACON_INTERVAL 200U -#define DEFAULT_SHORT_RETRY_LIMIT 7U -#define DEFAULT_LONG_RETRY_LIMIT 4U - -#define IWL_NUM_SCAN_RATES (2) - - -#define IEEE80211_DATA_LEN 2304 -#define IEEE80211_4ADDR_LEN 30 -#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) -#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) - -#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 -#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 -#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 - -#define IWL_SUPPORTED_RATES_IE_LEN 8 - -#define IWL_INVALID_RATE 0xFF -#define IWL_INVALID_VALUE -1 - -union iwl_ht_rate_supp { - u16 rates; - struct { - u8 siso_rate; - u8 mimo_rate; - }; -}; - -struct iwl_ht_config { - bool single_chain_sufficient; - enum ieee80211_smps_mode smps; /* current smps mode */ -}; - -/* QoS structures */ -struct iwl_qos_info { - int qos_active; - struct iwl_qosparam_cmd def_qos_parm; -}; - -/** - * enum iwl_agg_state - * - * The state machine of the BA agreement establishment / tear down. - * These states relate to a specific RA / TID. - * - * @IWL_AGG_OFF: aggregation is not used - * @IWL_AGG_STARTING: aggregation are starting (between start and oper) - * @IWL_AGG_ON: aggregation session is up - * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the - * HW queue to be empty from packets for this RA /TID. - * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the - * HW queue to be empty from packets for this RA /TID. - */ -enum iwl_agg_state { - IWL_AGG_OFF = 0, - IWL_AGG_STARTING, - IWL_AGG_ON, - IWL_EMPTYING_HW_QUEUE_ADDBA, - IWL_EMPTYING_HW_QUEUE_DELBA, -}; - -/** - * struct iwl_ht_agg - aggregation state machine - - * This structs holds the states for the BA agreement establishment and tear - * down. It also holds the state during the BA session itself. This struct is - * duplicated for each RA / TID. - - * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the - * Tx response (REPLY_TX), and the block ack notification - * (REPLY_COMPRESSED_BA). - * @state: state of the BA agreement establishment / tear down. - * @txq_id: Tx queue used by the BA session - * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or - * the first packet to be sent in legacy HW queue in Tx AGG stop flow. - * Basically when next_reclaimed reaches ssn, we can tell mac80211 that - * we are ready to finish the Tx AGG stop / start flow. - * @wait_for_ba: Expect block-ack before next Tx reply - */ -struct iwl_ht_agg { - u32 rate_n_flags; - enum iwl_agg_state state; - u16 txq_id; - u16 ssn; - bool wait_for_ba; -}; - -/** - * struct iwl_tid_data - one for each RA / TID - - * This structs holds the states for each RA / TID. - - * @seq_number: the next WiFi sequence number to use - * @next_reclaimed: the WiFi sequence number of the next packet to be acked. - * This is basically (last acked packet++). - * @agg: aggregation state machine - */ -struct iwl_tid_data { - u16 seq_number; - u16 next_reclaimed; - struct iwl_ht_agg agg; -}; - -/* - * Structure should be accessed with sta_lock held. When station addition - * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only - * the commands (iwl_addsta_cmd and iwl_link_quality_cmd) without sta_lock - * held. - */ -struct iwl_station_entry { - struct iwl_addsta_cmd sta; - u8 used, ctxid; - struct iwl_link_quality_cmd *lq; -}; - -/* - * iwl_station_priv: Driver's private station information - * - * When mac80211 creates a station it reserves some space (hw->sta_data_size) - * in the structure for use by driver. This structure is places in that - * space. - */ -struct iwl_station_priv { - struct iwl_rxon_context *ctx; - struct iwl_lq_sta lq_sta; - atomic_t pending_frames; - bool client; - bool asleep; - u8 max_agg_bufsize; - u8 sta_id; -}; - -/** - * struct iwl_vif_priv - driver's private per-interface information - * - * When mac80211 allocates a virtual interface, it can allocate - * space for us to put data into. - */ -struct iwl_vif_priv { - struct iwl_rxon_context *ctx; - u8 ibss_bssid_sta_id; -}; - -struct iwl_sensitivity_ranges { - u16 min_nrg_cck; - - u16 nrg_th_cck; - u16 nrg_th_ofdm; - - u16 auto_corr_min_ofdm; - u16 auto_corr_min_ofdm_mrc; - u16 auto_corr_min_ofdm_x1; - u16 auto_corr_min_ofdm_mrc_x1; - - u16 auto_corr_max_ofdm; - u16 auto_corr_max_ofdm_mrc; - u16 auto_corr_max_ofdm_x1; - u16 auto_corr_max_ofdm_mrc_x1; - - u16 auto_corr_max_cck; - u16 auto_corr_max_cck_mrc; - u16 auto_corr_min_cck; - u16 auto_corr_min_cck_mrc; - - u16 barker_corr_th_min; - u16 barker_corr_th_min_mrc; - u16 nrg_th_cca; -}; - - -#define KELVIN_TO_CELSIUS(x) ((x)-273) -#define CELSIUS_TO_KELVIN(x) ((x)+273) - - -/****************************************************************************** - * - * Functions implemented in core module which are forward declared here - * for use by iwl-[4-5].c - * - * NOTE: The implementation of these functions are not hardware specific - * which is why they are in the core module files. - * - * Naming convention -- - * iwl_ <-- Is part of iwlwifi - * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) - * - ****************************************************************************/ -void iwl_update_chain_flags(struct iwl_priv *priv); -extern const u8 iwl_bcast_addr[ETH_ALEN]; - -#define IWL_OPERATION_MODE_AUTO 0 -#define IWL_OPERATION_MODE_HT_ONLY 1 -#define IWL_OPERATION_MODE_MIXED 2 -#define IWL_OPERATION_MODE_20MHZ 3 - -#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000 - -/* Sensitivity and chain noise calibration */ -#define INITIALIZATION_VALUE 0xFFFF -#define IWL_CAL_NUM_BEACONS 16 -#define MAXIMUM_ALLOWED_PATHLOSS 15 - -#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3 - -#define MAX_FA_OFDM 50 -#define MIN_FA_OFDM 5 -#define MAX_FA_CCK 50 -#define MIN_FA_CCK 5 - -#define AUTO_CORR_STEP_OFDM 1 - -#define AUTO_CORR_STEP_CCK 3 -#define AUTO_CORR_MAX_TH_CCK 160 - -#define NRG_DIFF 2 -#define NRG_STEP_CCK 2 -#define NRG_MARGIN 8 -#define MAX_NUMBER_CCK_NO_FA 100 - -#define AUTO_CORR_CCK_MIN_VAL_DEF (125) - -#define CHAIN_A 0 -#define CHAIN_B 1 -#define CHAIN_C 2 -#define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4 -#define ALL_BAND_FILTER 0xFF00 -#define IN_BAND_FILTER 0xFF -#define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF - -#define NRG_NUM_PREV_STAT_L 20 -#define NUM_RX_CHAINS 3 - -enum iwlagn_false_alarm_state { - IWL_FA_TOO_MANY = 0, - IWL_FA_TOO_FEW = 1, - IWL_FA_GOOD_RANGE = 2, -}; - -enum iwlagn_chain_noise_state { - IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */ - IWL_CHAIN_NOISE_ACCUMULATE, - IWL_CHAIN_NOISE_CALIBRATED, - IWL_CHAIN_NOISE_DONE, -}; - -/* Sensitivity calib data */ -struct iwl_sensitivity_data { - u32 auto_corr_ofdm; - u32 auto_corr_ofdm_mrc; - u32 auto_corr_ofdm_x1; - u32 auto_corr_ofdm_mrc_x1; - u32 auto_corr_cck; - u32 auto_corr_cck_mrc; - - u32 last_bad_plcp_cnt_ofdm; - u32 last_fa_cnt_ofdm; - u32 last_bad_plcp_cnt_cck; - u32 last_fa_cnt_cck; - - u32 nrg_curr_state; - u32 nrg_prev_state; - u32 nrg_value[10]; - u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L]; - u32 nrg_silence_ref; - u32 nrg_energy_idx; - u32 nrg_silence_idx; - u32 nrg_th_cck; - s32 nrg_auto_corr_silence_diff; - u32 num_in_cck_no_fa; - u32 nrg_th_ofdm; - - u16 barker_corr_th_min; - u16 barker_corr_th_min_mrc; - u16 nrg_th_cca; -}; - -/* Chain noise (differential Rx gain) calib data */ -struct iwl_chain_noise_data { - u32 active_chains; - u32 chain_noise_a; - u32 chain_noise_b; - u32 chain_noise_c; - u32 chain_signal_a; - u32 chain_signal_b; - u32 chain_signal_c; - u16 beacon_count; - u8 disconn_array[NUM_RX_CHAINS]; - u8 delta_gain_code[NUM_RX_CHAINS]; - u8 radio_write; - u8 state; -}; - -enum { - MEASUREMENT_READY = (1 << 0), - MEASUREMENT_ACTIVE = (1 << 1), -}; - -/* reply_tx_statistics (for _agn devices) */ -struct reply_tx_error_statistics { - u32 pp_delay; - u32 pp_few_bytes; - u32 pp_bt_prio; - u32 pp_quiet_period; - u32 pp_calc_ttak; - u32 int_crossed_retry; - u32 short_limit; - u32 long_limit; - u32 fifo_underrun; - u32 drain_flow; - u32 rfkill_flush; - u32 life_expire; - u32 dest_ps; - u32 host_abort; - u32 bt_retry; - u32 sta_invalid; - u32 frag_drop; - u32 tid_disable; - u32 fifo_flush; - u32 insuff_cf_poll; - u32 fail_hw_drop; - u32 sta_color_mismatch; - u32 unknown; -}; - -/* reply_agg_tx_statistics (for _agn devices) */ -struct reply_agg_tx_error_statistics { - u32 underrun; - u32 bt_prio; - u32 few_bytes; - u32 abort; - u32 last_sent_ttl; - u32 last_sent_try; - u32 last_sent_bt_kill; - u32 scd_query; - u32 bad_crc32; - u32 response; - u32 dump_tx; - u32 delay_tx; - u32 unknown; -}; - -/* - * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds - * to perform continuous uCode event logging operation if enabled - */ -#define UCODE_TRACE_PERIOD (10) - -/* - * iwl_event_log: current uCode event log position - * - * @ucode_trace: enable/disable ucode continuous trace timer - * @num_wraps: how many times the event buffer wraps - * @next_entry: the entry just before the next one that uCode would fill - * @non_wraps_count: counter for no wrap detected when dump ucode events - * @wraps_once_count: counter for wrap once detected when dump ucode events - * @wraps_more_count: counter for wrap more than once detected - * when dump ucode events - */ -struct iwl_event_log { - bool ucode_trace; - u32 num_wraps; - u32 next_entry; - int non_wraps_count; - int wraps_once_count; - int wraps_more_count; -}; - -#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3) - -/* BT Antenna Coupling Threshold (dB) */ -#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35) - -/* Firmware reload counter and Timestamp */ -#define IWL_MIN_RELOAD_DURATION 1000 /* 1000 ms */ -#define IWL_MAX_CONTINUE_RELOAD_CNT 4 - - -struct iwl_rf_reset { - int reset_request_count; - int reset_success_count; - int reset_reject_count; - unsigned long last_reset_jiffies; -}; - -enum iwl_rxon_context_id { - IWL_RXON_CTX_BSS, - IWL_RXON_CTX_PAN, - - NUM_IWL_RXON_CTX -}; - -/* extend beacon time format bit shifting */ -/* - * for _agn devices - * bits 31:22 - extended - * bits 21:0 - interval - */ -#define IWLAGN_EXT_BEACON_TIME_POS 22 - -struct iwl_rxon_context { - struct ieee80211_vif *vif; - - u8 mcast_queue; - u8 ac_to_queue[IEEE80211_NUM_ACS]; - u8 ac_to_fifo[IEEE80211_NUM_ACS]; - - /* - * We could use the vif to indicate active, but we - * also need it to be active during disabling when - * we already removed the vif for type setting. - */ - bool always_active, is_active; - - bool ht_need_multiple_chains; - - enum iwl_rxon_context_id ctxid; - - u32 interface_modes, exclusive_interface_modes; - u8 unused_devtype, ap_devtype, ibss_devtype, station_devtype; - - /* - * We declare this const so it can only be - * changed via explicit cast within the - * routines that actually update the physical - * hardware. - */ - const struct iwl_rxon_cmd active; - struct iwl_rxon_cmd staging; - - struct iwl_rxon_time_cmd timing; - - struct iwl_qos_info qos_data; - - u8 bcast_sta_id, ap_sta_id; - - u8 rxon_cmd, rxon_assoc_cmd, rxon_timing_cmd; - u8 qos_cmd; - u8 wep_key_cmd; - - struct iwl_wep_key wep_keys[WEP_KEYS_MAX]; - u8 key_mapping_keys; - - __le32 station_flags; - - int beacon_int; - - struct { - bool non_gf_sta_present; - u8 protection; - bool enabled, is_40mhz; - u8 extension_chan_offset; - } ht; -}; - -enum iwl_scan_type { - IWL_SCAN_NORMAL, - IWL_SCAN_RADIO_RESET, -}; - -/** - * struct iwl_hw_params - * - * Holds the module parameters - * - * @tx_chains_num: Number of TX chains - * @rx_chains_num: Number of RX chains - * @ct_kill_threshold: temperature threshold - in hw dependent unit - * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit - * relevant for 1000, 6000 and up - * @struct iwl_sensitivity_ranges: range of sensitivity values - * @use_rts_for_aggregation: use rts/cts protection for HT traffic - */ -struct iwl_hw_params { - u8 tx_chains_num; - u8 rx_chains_num; - bool use_rts_for_aggregation; - u32 ct_kill_threshold; - u32 ct_kill_exit_threshold; - - const struct iwl_sensitivity_ranges *sens; -}; - -/** - * struct iwl_dvm_bt_params - DVM specific BT (coex) parameters - * @advanced_bt_coexist: support advanced bt coexist - * @bt_init_traffic_load: specify initial bt traffic load - * @bt_prio_boost: default bt priority boost value - * @agg_time_limit: maximum number of uSec in aggregation - * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode - */ -struct iwl_dvm_bt_params { - bool advanced_bt_coexist; - u8 bt_init_traffic_load; - u32 bt_prio_boost; - u16 agg_time_limit; - bool bt_sco_disable; - bool bt_session_2; -}; - -/** - * struct iwl_dvm_cfg - DVM firmware specific device configuration - * @set_hw_params: set hardware parameters - * @set_channel_switch: send channel switch command - * @nic_config: apply device specific configuration - * @temperature: read temperature - * @adv_thermal_throttle: support advance thermal throttle - * @support_ct_kill_exit: support ct kill exit condition - * @plcp_delta_threshold: plcp error rate threshold used to trigger - * radio tuning when there is a high receiving plcp error rate - * @chain_noise_scale: default chain noise scale used for gain computation - * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up - * @no_idle_support: do not support idle mode - * @bt_params: pointer to BT parameters - * @need_temp_offset_calib: need to perform temperature offset calibration - * @no_xtal_calib: some devices do not need crystal calibration data, - * don't send it to those - * @temp_offset_v2: support v2 of temperature offset calibration - * @adv_pm: advanced power management - */ -struct iwl_dvm_cfg { - void (*set_hw_params)(struct iwl_priv *priv); - int (*set_channel_switch)(struct iwl_priv *priv, - struct ieee80211_channel_switch *ch_switch); - void (*nic_config)(struct iwl_priv *priv); - void (*temperature)(struct iwl_priv *priv); - - const struct iwl_dvm_bt_params *bt_params; - s32 chain_noise_scale; - u8 plcp_delta_threshold; - bool adv_thermal_throttle; - bool support_ct_kill_exit; - bool hd_v2; - bool no_idle_support; - bool need_temp_offset_calib; - bool no_xtal_calib; - bool temp_offset_v2; - bool adv_pm; -}; - -struct iwl_wipan_noa_data { - struct rcu_head rcu_head; - u32 length; - u8 data[]; -}; - -/* Calibration disabling bit mask */ -enum { - IWL_CALIB_ENABLE_ALL = 0, - - IWL_SENSITIVITY_CALIB_DISABLED = BIT(0), - IWL_CHAIN_NOISE_CALIB_DISABLED = BIT(1), - IWL_TX_POWER_CALIB_DISABLED = BIT(2), - - IWL_CALIB_DISABLE_ALL = 0xFFFFFFFF, -}; - -#define IWL_OP_MODE_GET_DVM(_iwl_op_mode) \ - ((struct iwl_priv *) ((_iwl_op_mode)->op_mode_specific)) - -#define IWL_MAC80211_GET_DVM(_hw) \ - ((struct iwl_priv *) ((struct iwl_op_mode *) \ - (_hw)->priv)->op_mode_specific) - -struct iwl_priv { - - struct iwl_trans *trans; - struct device *dev; /* for debug prints only */ - const struct iwl_cfg *cfg; - const struct iwl_fw *fw; - const struct iwl_dvm_cfg *lib; - unsigned long status; - - spinlock_t sta_lock; - struct mutex mutex; - - unsigned long transport_queue_stop; - bool passive_no_rx; -#define IWL_INVALID_MAC80211_QUEUE 0xff - u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; - atomic_t queue_stop_count[IWL_MAX_HW_QUEUES]; - - unsigned long agg_q_alloc[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; - - /* ieee device used by generic ieee processing code */ - struct ieee80211_hw *hw; - - struct napi_struct *napi; - - struct list_head calib_results; - - struct workqueue_struct *workqueue; - - struct iwl_hw_params hw_params; - - enum ieee80211_band band; - u8 valid_contexts; - - void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb); - - struct iwl_notif_wait_data notif_wait; - - /* spectrum measurement report caching */ - struct iwl_spectrum_notification measure_report; - u8 measurement_status; - - /* ucode beacon time */ - u32 ucode_beacon_time; - int missed_beacon_threshold; - - /* track IBSS manager (last beacon) status */ - u32 ibss_manager; - - /* jiffies when last recovery from statistics was performed */ - unsigned long rx_statistics_jiffies; - - /*counters */ - u32 rx_handlers_stats[REPLY_MAX]; - - /* rf reset */ - struct iwl_rf_reset rf_reset; - - /* firmware reload counter and timestamp */ - unsigned long reload_jiffies; - int reload_count; - bool ucode_loaded; - - u8 plcp_delta_threshold; - - /* thermal calibration */ - s32 temperature; /* Celsius */ - s32 last_temperature; - - struct iwl_wipan_noa_data __rcu *noa_data; - - /* Scan related variables */ - unsigned long scan_start; - unsigned long scan_start_tsf; - void *scan_cmd; - enum ieee80211_band scan_band; - struct cfg80211_scan_request *scan_request; - struct ieee80211_vif *scan_vif; - enum iwl_scan_type scan_type; - u8 scan_tx_ant[IEEE80211_NUM_BANDS]; - u8 mgmt_tx_ant; - - /* max number of station keys */ - u8 sta_key_max_num; - - bool new_scan_threshold_behaviour; - - bool wowlan; - - /* EEPROM MAC addresses */ - struct mac_address addresses[2]; - - struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX]; - - __le16 switch_channel; - - u8 start_calib; - struct iwl_sensitivity_data sensitivity_data; - struct iwl_chain_noise_data chain_noise_data; - __le16 sensitivity_tbl[HD_TABLE_SIZE]; - __le16 enhance_sensitivity_tbl[ENHANCE_HD_TABLE_ENTRIES]; - - struct iwl_ht_config current_ht_config; - - /* Rate scaling data */ - u8 retry_rate; - - int activity_timer_active; - - struct iwl_power_mgr power_data; - struct iwl_tt_mgmt thermal_throttle; - - /* station table variables */ - int num_stations; - struct iwl_station_entry stations[IWLAGN_STATION_COUNT]; - unsigned long ucode_key_table; - struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT]; - atomic_t num_aux_in_flight; - - u8 mac80211_registered; - - /* Indication if ieee80211_ops->open has been called */ - u8 is_open; - - enum nl80211_iftype iw_mode; - - /* Last Rx'd beacon timestamp */ - u64 timestamp; - - struct { - __le32 flag; - struct statistics_general_common common; - struct statistics_rx_non_phy rx_non_phy; - struct statistics_rx_phy rx_ofdm; - struct statistics_rx_ht_phy rx_ofdm_ht; - struct statistics_rx_phy rx_cck; - struct statistics_tx tx; -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct statistics_bt_activity bt_activity; - __le32 num_bt_kills, accum_num_bt_kills; -#endif - spinlock_t lock; - } statistics; -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct { - struct statistics_general_common common; - struct statistics_rx_non_phy rx_non_phy; - struct statistics_rx_phy rx_ofdm; - struct statistics_rx_ht_phy rx_ofdm_ht; - struct statistics_rx_phy rx_cck; - struct statistics_tx tx; - struct statistics_bt_activity bt_activity; - } accum_stats, delta_stats, max_delta_stats; -#endif - - /* - * reporting the number of tids has AGG on. 0 means - * no AGGREGATION - */ - u8 agg_tids_count; - - struct iwl_rx_phy_res last_phy_res; - u32 ampdu_ref; - bool last_phy_res_valid; - - /* - * chain noise reset and gain commands are the - * two extra calibration commands follows the standard - * phy calibration commands - */ - u8 phy_calib_chain_noise_reset_cmd; - u8 phy_calib_chain_noise_gain_cmd; - - /* counts reply_tx error */ - struct reply_tx_error_statistics reply_tx_stats; - struct reply_agg_tx_error_statistics reply_agg_tx_stats; - - /* bt coex */ - u8 bt_enable_flag; - u8 bt_status; - u8 bt_traffic_load, last_bt_traffic_load; - bool bt_ch_announce; - bool bt_full_concurrent; - bool bt_ant_couple_ok; - __le32 kill_ack_mask; - __le32 kill_cts_mask; - __le16 bt_valid; - bool reduced_txpower; - u16 bt_on_thresh; - u16 bt_duration; - u16 dynamic_frag_thresh; - u8 bt_ci_compliance; - struct work_struct bt_traffic_change_work; - bool bt_enable_pspoll; - struct iwl_rxon_context *cur_rssi_ctx; - bool bt_is_sco; - - struct work_struct restart; - struct work_struct scan_completed; - struct work_struct abort_scan; - - struct work_struct beacon_update; - struct iwl_rxon_context *beacon_ctx; - struct sk_buff *beacon_skb; - void *beacon_cmd; - - struct work_struct tt_work; - struct work_struct ct_enter; - struct work_struct ct_exit; - struct work_struct start_internal_scan; - struct work_struct tx_flush; - struct work_struct bt_full_concurrency; - struct work_struct bt_runtime_config; - - struct delayed_work scan_check; - - /* TX Power settings */ - s8 tx_power_user_lmt; - s8 tx_power_next; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - /* debugfs */ - struct dentry *debugfs_dir; - u32 dbgfs_sram_offset, dbgfs_sram_len; - bool disable_ht40; - void *wowlan_sram; -#endif /* CONFIG_IWLWIFI_DEBUGFS */ - - struct iwl_nvm_data *nvm_data; - /* eeprom blob for debugfs */ - u8 *eeprom_blob; - size_t eeprom_blob_size; - - struct work_struct txpower_work; - u32 calib_disabled; - struct work_struct run_time_calib_work; - struct timer_list statistics_periodic; - struct timer_list ucode_trace; - - struct iwl_event_log event_log; - -#ifdef CONFIG_IWLWIFI_LEDS - struct led_classdev led; - unsigned long blink_on, blink_off; - bool led_registered; -#endif - - /* WoWLAN GTK rekey data */ - u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; - __le64 replay_ctr; - __le16 last_seq_ctl; - bool have_rekey_data; -#ifdef CONFIG_PM_SLEEP - struct wiphy_wowlan_support wowlan_support; -#endif - - /* device_pointers: pointers to ucode event tables */ - struct { - u32 error_event_table; - u32 log_event_table; - } device_pointers; - - /* indicator of loaded ucode image */ - enum iwl_ucode_type cur_ucode; -}; /*iwl_priv */ - -static inline struct iwl_rxon_context * -iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif) -{ - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - - return vif_priv->ctx; -} - -#define for_each_context(priv, ctx) \ - for (ctx = &priv->contexts[IWL_RXON_CTX_BSS]; \ - ctx < &priv->contexts[NUM_IWL_RXON_CTX]; ctx++) \ - if (priv->valid_contexts & BIT(ctx->ctxid)) - -static inline int iwl_is_associated_ctx(struct iwl_rxon_context *ctx) -{ - return (ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; -} - -static inline int iwl_is_associated(struct iwl_priv *priv, - enum iwl_rxon_context_id ctxid) -{ - return iwl_is_associated_ctx(&priv->contexts[ctxid]); -} - -static inline int iwl_is_any_associated(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - for_each_context(priv, ctx) - if (iwl_is_associated_ctx(ctx)) - return true; - return false; -} - -#endif /* __iwl_dev_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c deleted file mode 100644 index 34b41e5f7cfc..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ /dev/null @@ -1,690 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -/* - * DVM device-specific data & functions - */ -#include "iwl-io.h" -#include "iwl-prph.h" -#include "iwl-eeprom-parse.h" - -#include "agn.h" -#include "dev.h" -#include "commands.h" - - -/* - * 1000 series - * =========== - */ - -/* - * For 1000, use advance thermal throttling critical temperature threshold, - * but legacy thermal management implementation for now. - * This is for the reason of 1000 uCode using advance thermal throttling API - * but not implement ct_kill_exit based on ct_kill exit temperature - * so the thermal throttling will still based on legacy thermal throttling - * management. - * The code here need to be modified once 1000 uCode has the advanced thermal - * throttling algorithm in place - */ -static void iwl1000_set_ct_threshold(struct iwl_priv *priv) -{ - /* want Celsius */ - priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; - priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; -} - -/* NIC configuration for 1000 series */ -static void iwl1000_nic_config(struct iwl_priv *priv) -{ - /* Setting digital SVR for 1000 card to 1.32V */ - /* locking is acquired in iwl_set_bits_mask_prph() function */ - iwl_set_bits_mask_prph(priv->trans, APMG_DIGITAL_SVR_REG, - APMG_SVR_DIGITAL_VOLTAGE_1_32, - ~APMG_SVR_VOLTAGE_CONFIG_BIT_MSK); -} - -/** - * iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time - * @priv -- pointer to iwl_priv data structure - * @tsf_bits -- number of bits need to shift for masking) - */ -static inline u32 iwl_beacon_time_mask_low(struct iwl_priv *priv, - u16 tsf_bits) -{ - return (1 << tsf_bits) - 1; -} - -/** - * iwl_beacon_time_mask_high - mask of higher 32 bit of beacon time - * @priv -- pointer to iwl_priv data structure - * @tsf_bits -- number of bits need to shift for masking) - */ -static inline u32 iwl_beacon_time_mask_high(struct iwl_priv *priv, - u16 tsf_bits) -{ - return ((1 << (32 - tsf_bits)) - 1) << tsf_bits; -} - -/* - * extended beacon time format - * time in usec will be changed into a 32-bit value in extended:internal format - * the extended part is the beacon counts - * the internal part is the time in usec within one beacon interval - */ -static u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, - u32 beacon_interval) -{ - u32 quot; - u32 rem; - u32 interval = beacon_interval * TIME_UNIT; - - if (!interval || !usec) - return 0; - - quot = (usec / interval) & - (iwl_beacon_time_mask_high(priv, IWLAGN_EXT_BEACON_TIME_POS) >> - IWLAGN_EXT_BEACON_TIME_POS); - rem = (usec % interval) & iwl_beacon_time_mask_low(priv, - IWLAGN_EXT_BEACON_TIME_POS); - - return (quot << IWLAGN_EXT_BEACON_TIME_POS) + rem; -} - -/* base is usually what we get from ucode with each received frame, - * the same as HW timer counter counting down - */ -static __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base, - u32 addon, u32 beacon_interval) -{ - u32 base_low = base & iwl_beacon_time_mask_low(priv, - IWLAGN_EXT_BEACON_TIME_POS); - u32 addon_low = addon & iwl_beacon_time_mask_low(priv, - IWLAGN_EXT_BEACON_TIME_POS); - u32 interval = beacon_interval * TIME_UNIT; - u32 res = (base & iwl_beacon_time_mask_high(priv, - IWLAGN_EXT_BEACON_TIME_POS)) + - (addon & iwl_beacon_time_mask_high(priv, - IWLAGN_EXT_BEACON_TIME_POS)); - - if (base_low > addon_low) - res += base_low - addon_low; - else if (base_low < addon_low) { - res += interval + base_low - addon_low; - res += (1 << IWLAGN_EXT_BEACON_TIME_POS); - } else - res += (1 << IWLAGN_EXT_BEACON_TIME_POS); - - return cpu_to_le32(res); -} - -static const struct iwl_sensitivity_ranges iwl1000_sensitivity = { - .min_nrg_cck = 95, - .auto_corr_min_ofdm = 90, - .auto_corr_min_ofdm_mrc = 170, - .auto_corr_min_ofdm_x1 = 120, - .auto_corr_min_ofdm_mrc_x1 = 240, - - .auto_corr_max_ofdm = 120, - .auto_corr_max_ofdm_mrc = 210, - .auto_corr_max_ofdm_x1 = 155, - .auto_corr_max_ofdm_mrc_x1 = 290, - - .auto_corr_min_cck = 125, - .auto_corr_max_cck = 200, - .auto_corr_min_cck_mrc = 170, - .auto_corr_max_cck_mrc = 400, - .nrg_th_cck = 95, - .nrg_th_ofdm = 95, - - .barker_corr_th_min = 190, - .barker_corr_th_min_mrc = 390, - .nrg_th_cca = 62, -}; - -static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) -{ - iwl1000_set_ct_threshold(priv); - - /* Set initial sensitivity parameters */ - priv->hw_params.sens = &iwl1000_sensitivity; -} - -const struct iwl_dvm_cfg iwl_dvm_1000_cfg = { - .set_hw_params = iwl1000_hw_set_hw_params, - .nic_config = iwl1000_nic_config, - .temperature = iwlagn_temperature, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, - .chain_noise_scale = 1000, -}; - - -/* - * 2000 series - * =========== - */ - -static void iwl2000_set_ct_threshold(struct iwl_priv *priv) -{ - /* want Celsius */ - priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; - priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; -} - -/* NIC configuration for 2000 series */ -static void iwl2000_nic_config(struct iwl_priv *priv) -{ - iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, - CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER); -} - -static const struct iwl_sensitivity_ranges iwl2000_sensitivity = { - .min_nrg_cck = 97, - .auto_corr_min_ofdm = 80, - .auto_corr_min_ofdm_mrc = 128, - .auto_corr_min_ofdm_x1 = 105, - .auto_corr_min_ofdm_mrc_x1 = 192, - - .auto_corr_max_ofdm = 145, - .auto_corr_max_ofdm_mrc = 232, - .auto_corr_max_ofdm_x1 = 110, - .auto_corr_max_ofdm_mrc_x1 = 232, - - .auto_corr_min_cck = 125, - .auto_corr_max_cck = 175, - .auto_corr_min_cck_mrc = 160, - .auto_corr_max_cck_mrc = 310, - .nrg_th_cck = 97, - .nrg_th_ofdm = 100, - - .barker_corr_th_min = 190, - .barker_corr_th_min_mrc = 390, - .nrg_th_cca = 62, -}; - -static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) -{ - iwl2000_set_ct_threshold(priv); - - /* Set initial sensitivity parameters */ - priv->hw_params.sens = &iwl2000_sensitivity; -} - -const struct iwl_dvm_cfg iwl_dvm_2000_cfg = { - .set_hw_params = iwl2000_hw_set_hw_params, - .nic_config = iwl2000_nic_config, - .temperature = iwlagn_temperature, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .hd_v2 = true, - .need_temp_offset_calib = true, - .temp_offset_v2 = true, -}; - -const struct iwl_dvm_cfg iwl_dvm_105_cfg = { - .set_hw_params = iwl2000_hw_set_hw_params, - .nic_config = iwl2000_nic_config, - .temperature = iwlagn_temperature, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .hd_v2 = true, - .need_temp_offset_calib = true, - .temp_offset_v2 = true, - .adv_pm = true, -}; - -static const struct iwl_dvm_bt_params iwl2030_bt_params = { - /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ - .advanced_bt_coexist = true, - .agg_time_limit = BT_AGG_THRESHOLD_DEF, - .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, - .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32, - .bt_sco_disable = true, - .bt_session_2 = true, -}; - -const struct iwl_dvm_cfg iwl_dvm_2030_cfg = { - .set_hw_params = iwl2000_hw_set_hw_params, - .nic_config = iwl2000_nic_config, - .temperature = iwlagn_temperature, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .hd_v2 = true, - .bt_params = &iwl2030_bt_params, - .need_temp_offset_calib = true, - .temp_offset_v2 = true, - .adv_pm = true, -}; - -/* - * 5000 series - * =========== - */ - -/* NIC configuration for 5000 series */ -static const struct iwl_sensitivity_ranges iwl5000_sensitivity = { - .min_nrg_cck = 100, - .auto_corr_min_ofdm = 90, - .auto_corr_min_ofdm_mrc = 170, - .auto_corr_min_ofdm_x1 = 105, - .auto_corr_min_ofdm_mrc_x1 = 220, - - .auto_corr_max_ofdm = 120, - .auto_corr_max_ofdm_mrc = 210, - .auto_corr_max_ofdm_x1 = 120, - .auto_corr_max_ofdm_mrc_x1 = 240, - - .auto_corr_min_cck = 125, - .auto_corr_max_cck = 200, - .auto_corr_min_cck_mrc = 200, - .auto_corr_max_cck_mrc = 400, - .nrg_th_cck = 100, - .nrg_th_ofdm = 100, - - .barker_corr_th_min = 190, - .barker_corr_th_min_mrc = 390, - .nrg_th_cca = 62, -}; - -static const struct iwl_sensitivity_ranges iwl5150_sensitivity = { - .min_nrg_cck = 95, - .auto_corr_min_ofdm = 90, - .auto_corr_min_ofdm_mrc = 170, - .auto_corr_min_ofdm_x1 = 105, - .auto_corr_min_ofdm_mrc_x1 = 220, - - .auto_corr_max_ofdm = 120, - .auto_corr_max_ofdm_mrc = 210, - /* max = min for performance bug in 5150 DSP */ - .auto_corr_max_ofdm_x1 = 105, - .auto_corr_max_ofdm_mrc_x1 = 220, - - .auto_corr_min_cck = 125, - .auto_corr_max_cck = 200, - .auto_corr_min_cck_mrc = 170, - .auto_corr_max_cck_mrc = 400, - .nrg_th_cck = 95, - .nrg_th_ofdm = 95, - - .barker_corr_th_min = 190, - .barker_corr_th_min_mrc = 390, - .nrg_th_cca = 62, -}; - -#define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF (-5) - -static s32 iwl_temp_calib_to_offset(struct iwl_priv *priv) -{ - u16 temperature, voltage; - - temperature = le16_to_cpu(priv->nvm_data->kelvin_temperature); - voltage = le16_to_cpu(priv->nvm_data->kelvin_voltage); - - /* offset = temp - volt / coeff */ - return (s32)(temperature - - voltage / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF); -} - -static void iwl5150_set_ct_threshold(struct iwl_priv *priv) -{ - const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF; - s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY) - - iwl_temp_calib_to_offset(priv); - - priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef; -} - -static void iwl5000_set_ct_threshold(struct iwl_priv *priv) -{ - /* want Celsius */ - priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; -} - -static void iwl5000_hw_set_hw_params(struct iwl_priv *priv) -{ - iwl5000_set_ct_threshold(priv); - - /* Set initial sensitivity parameters */ - priv->hw_params.sens = &iwl5000_sensitivity; -} - -static void iwl5150_hw_set_hw_params(struct iwl_priv *priv) -{ - iwl5150_set_ct_threshold(priv); - - /* Set initial sensitivity parameters */ - priv->hw_params.sens = &iwl5150_sensitivity; -} - -static void iwl5150_temperature(struct iwl_priv *priv) -{ - u32 vt = 0; - s32 offset = iwl_temp_calib_to_offset(priv); - - vt = le32_to_cpu(priv->statistics.common.temperature); - vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset; - /* now vt hold the temperature in Kelvin */ - priv->temperature = KELVIN_TO_CELSIUS(vt); - iwl_tt_handler(priv); -} - -static int iwl5000_hw_channel_switch(struct iwl_priv *priv, - struct ieee80211_channel_switch *ch_switch) -{ - /* - * MULTI-FIXME - * See iwlagn_mac_channel_switch. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl5000_channel_switch_cmd cmd; - u32 switch_time_in_usec, ucode_switch_time; - u16 ch; - u32 tsf_low; - u8 switch_count; - u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval); - struct ieee80211_vif *vif = ctx->vif; - struct iwl_host_cmd hcmd = { - .id = REPLY_CHANNEL_SWITCH, - .len = { sizeof(cmd), }, - .data = { &cmd, }, - }; - - cmd.band = priv->band == IEEE80211_BAND_2GHZ; - ch = ch_switch->chandef.chan->hw_value; - IWL_DEBUG_11H(priv, "channel switch from %d to %d\n", - ctx->active.channel, ch); - cmd.channel = cpu_to_le16(ch); - cmd.rxon_flags = ctx->staging.flags; - cmd.rxon_filter_flags = ctx->staging.filter_flags; - switch_count = ch_switch->count; - tsf_low = ch_switch->timestamp & 0x0ffffffff; - /* - * calculate the ucode channel switch time - * adding TSF as one of the factor for when to switch - */ - if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) { - if (switch_count > ((priv->ucode_beacon_time - tsf_low) / - beacon_interval)) { - switch_count -= (priv->ucode_beacon_time - - tsf_low) / beacon_interval; - } else - switch_count = 0; - } - if (switch_count <= 1) - cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time); - else { - switch_time_in_usec = - vif->bss_conf.beacon_int * switch_count * TIME_UNIT; - ucode_switch_time = iwl_usecs_to_beacons(priv, - switch_time_in_usec, - beacon_interval); - cmd.switch_time = iwl_add_beacon_time(priv, - priv->ucode_beacon_time, - ucode_switch_time, - beacon_interval); - } - IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", - cmd.switch_time); - cmd.expect_beacon = - ch_switch->chandef.chan->flags & IEEE80211_CHAN_RADAR; - - return iwl_dvm_send_cmd(priv, &hcmd); -} - -const struct iwl_dvm_cfg iwl_dvm_5000_cfg = { - .set_hw_params = iwl5000_hw_set_hw_params, - .set_channel_switch = iwl5000_hw_channel_switch, - .temperature = iwlagn_temperature, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .no_idle_support = true, -}; - -const struct iwl_dvm_cfg iwl_dvm_5150_cfg = { - .set_hw_params = iwl5150_hw_set_hw_params, - .set_channel_switch = iwl5000_hw_channel_switch, - .temperature = iwl5150_temperature, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .no_idle_support = true, - .no_xtal_calib = true, -}; - - - -/* - * 6000 series - * =========== - */ - -static void iwl6000_set_ct_threshold(struct iwl_priv *priv) -{ - /* want Celsius */ - priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; - priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; -} - -/* NIC configuration for 6000 series */ -static void iwl6000_nic_config(struct iwl_priv *priv) -{ - switch (priv->cfg->device_family) { - case IWL_DEVICE_FAMILY_6005: - case IWL_DEVICE_FAMILY_6030: - case IWL_DEVICE_FAMILY_6000: - break; - case IWL_DEVICE_FAMILY_6000i: - /* 2x2 IPA phy type */ - iwl_write32(priv->trans, CSR_GP_DRIVER_REG, - CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA); - break; - case IWL_DEVICE_FAMILY_6050: - /* Indicate calibration version to uCode. */ - if (priv->nvm_data->calib_version >= 6) - iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, - CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); - break; - case IWL_DEVICE_FAMILY_6150: - /* Indicate calibration version to uCode. */ - if (priv->nvm_data->calib_version >= 6) - iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, - CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); - iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, - CSR_GP_DRIVER_REG_BIT_6050_1x2); - break; - default: - WARN_ON(1); - } -} - -static const struct iwl_sensitivity_ranges iwl6000_sensitivity = { - .min_nrg_cck = 110, - .auto_corr_min_ofdm = 80, - .auto_corr_min_ofdm_mrc = 128, - .auto_corr_min_ofdm_x1 = 105, - .auto_corr_min_ofdm_mrc_x1 = 192, - - .auto_corr_max_ofdm = 145, - .auto_corr_max_ofdm_mrc = 232, - .auto_corr_max_ofdm_x1 = 110, - .auto_corr_max_ofdm_mrc_x1 = 232, - - .auto_corr_min_cck = 125, - .auto_corr_max_cck = 175, - .auto_corr_min_cck_mrc = 160, - .auto_corr_max_cck_mrc = 310, - .nrg_th_cck = 110, - .nrg_th_ofdm = 110, - - .barker_corr_th_min = 190, - .barker_corr_th_min_mrc = 336, - .nrg_th_cca = 62, -}; - -static void iwl6000_hw_set_hw_params(struct iwl_priv *priv) -{ - iwl6000_set_ct_threshold(priv); - - /* Set initial sensitivity parameters */ - priv->hw_params.sens = &iwl6000_sensitivity; - -} - -static int iwl6000_hw_channel_switch(struct iwl_priv *priv, - struct ieee80211_channel_switch *ch_switch) -{ - /* - * MULTI-FIXME - * See iwlagn_mac_channel_switch. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl6000_channel_switch_cmd *cmd; - u32 switch_time_in_usec, ucode_switch_time; - u16 ch; - u32 tsf_low; - u8 switch_count; - u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval); - struct ieee80211_vif *vif = ctx->vif; - struct iwl_host_cmd hcmd = { - .id = REPLY_CHANNEL_SWITCH, - .len = { sizeof(*cmd), }, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - }; - int err; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) - return -ENOMEM; - - hcmd.data[0] = cmd; - - cmd->band = priv->band == IEEE80211_BAND_2GHZ; - ch = ch_switch->chandef.chan->hw_value; - IWL_DEBUG_11H(priv, "channel switch from %u to %u\n", - ctx->active.channel, ch); - cmd->channel = cpu_to_le16(ch); - cmd->rxon_flags = ctx->staging.flags; - cmd->rxon_filter_flags = ctx->staging.filter_flags; - switch_count = ch_switch->count; - tsf_low = ch_switch->timestamp & 0x0ffffffff; - /* - * calculate the ucode channel switch time - * adding TSF as one of the factor for when to switch - */ - if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) { - if (switch_count > ((priv->ucode_beacon_time - tsf_low) / - beacon_interval)) { - switch_count -= (priv->ucode_beacon_time - - tsf_low) / beacon_interval; - } else - switch_count = 0; - } - if (switch_count <= 1) - cmd->switch_time = cpu_to_le32(priv->ucode_beacon_time); - else { - switch_time_in_usec = - vif->bss_conf.beacon_int * switch_count * TIME_UNIT; - ucode_switch_time = iwl_usecs_to_beacons(priv, - switch_time_in_usec, - beacon_interval); - cmd->switch_time = iwl_add_beacon_time(priv, - priv->ucode_beacon_time, - ucode_switch_time, - beacon_interval); - } - IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", - cmd->switch_time); - cmd->expect_beacon = - ch_switch->chandef.chan->flags & IEEE80211_CHAN_RADAR; - - err = iwl_dvm_send_cmd(priv, &hcmd); - kfree(cmd); - return err; -} - -const struct iwl_dvm_cfg iwl_dvm_6000_cfg = { - .set_hw_params = iwl6000_hw_set_hw_params, - .set_channel_switch = iwl6000_hw_channel_switch, - .nic_config = iwl6000_nic_config, - .temperature = iwlagn_temperature, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, -}; - -const struct iwl_dvm_cfg iwl_dvm_6005_cfg = { - .set_hw_params = iwl6000_hw_set_hw_params, - .set_channel_switch = iwl6000_hw_channel_switch, - .nic_config = iwl6000_nic_config, - .temperature = iwlagn_temperature, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .need_temp_offset_calib = true, -}; - -const struct iwl_dvm_cfg iwl_dvm_6050_cfg = { - .set_hw_params = iwl6000_hw_set_hw_params, - .set_channel_switch = iwl6000_hw_channel_switch, - .nic_config = iwl6000_nic_config, - .temperature = iwlagn_temperature, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1500, -}; - -static const struct iwl_dvm_bt_params iwl6000_bt_params = { - /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ - .advanced_bt_coexist = true, - .agg_time_limit = BT_AGG_THRESHOLD_DEF, - .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, - .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, - .bt_sco_disable = true, -}; - -const struct iwl_dvm_cfg iwl_dvm_6030_cfg = { - .set_hw_params = iwl6000_hw_set_hw_params, - .set_channel_switch = iwl6000_hw_channel_switch, - .nic_config = iwl6000_nic_config, - .temperature = iwlagn_temperature, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .bt_params = &iwl6000_bt_params, - .need_temp_offset_calib = true, - .adv_pm = true, -}; diff --git a/drivers/net/wireless/iwlwifi/dvm/led.c b/drivers/net/wireless/iwlwifi/dvm/led.c deleted file mode 100644 index ca4d6692cc4e..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/led.c +++ /dev/null @@ -1,223 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include "iwl-io.h" -#include "iwl-trans.h" -#include "iwl-modparams.h" -#include "dev.h" -#include "agn.h" - -/* Throughput OFF time(ms) ON time (ms) - * >300 25 25 - * >200 to 300 40 40 - * >100 to 200 55 55 - * >70 to 100 65 65 - * >50 to 70 75 75 - * >20 to 50 85 85 - * >10 to 20 95 95 - * >5 to 10 110 110 - * >1 to 5 130 130 - * >0 to 1 167 167 - * <=0 SOLID ON - */ -static const struct ieee80211_tpt_blink iwl_blink[] = { - { .throughput = 0, .blink_time = 334 }, - { .throughput = 1 * 1024 - 1, .blink_time = 260 }, - { .throughput = 5 * 1024 - 1, .blink_time = 220 }, - { .throughput = 10 * 1024 - 1, .blink_time = 190 }, - { .throughput = 20 * 1024 - 1, .blink_time = 170 }, - { .throughput = 50 * 1024 - 1, .blink_time = 150 }, - { .throughput = 70 * 1024 - 1, .blink_time = 130 }, - { .throughput = 100 * 1024 - 1, .blink_time = 110 }, - { .throughput = 200 * 1024 - 1, .blink_time = 80 }, - { .throughput = 300 * 1024 - 1, .blink_time = 50 }, -}; - -/* Set led register off */ -void iwlagn_led_enable(struct iwl_priv *priv) -{ - iwl_write32(priv->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON); -} - -/* - * Adjust led blink rate to compensate on a MAC Clock difference on every HW - * Led blink rate analysis showed an average deviation of 20% on 5000 series - * and up. - * Need to compensate on the led on/off time per HW according to the deviation - * to achieve the desired led frequency - * The calculation is: (100-averageDeviation)/100 * blinkTime - * For code efficiency the calculation will be: - * compensation = (100 - averageDeviation) * 64 / 100 - * NewBlinkTime = (compensation * BlinkTime) / 64 - */ -static inline u8 iwl_blink_compensation(struct iwl_priv *priv, - u8 time, u16 compensation) -{ - if (!compensation) { - IWL_ERR(priv, "undefined blink compensation: " - "use pre-defined blinking time\n"); - return time; - } - - return (u8)((time * compensation) >> 6); -} - -static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) -{ - struct iwl_host_cmd cmd = { - .id = REPLY_LEDS_CMD, - .len = { sizeof(struct iwl_led_cmd), }, - .data = { led_cmd, }, - .flags = CMD_ASYNC, - }; - u32 reg; - - reg = iwl_read32(priv->trans, CSR_LED_REG); - if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) - iwl_write32(priv->trans, CSR_LED_REG, - reg & CSR_LED_BSM_CTRL_MSK); - - return iwl_dvm_send_cmd(priv, &cmd); -} - -/* Set led pattern command */ -static int iwl_led_cmd(struct iwl_priv *priv, - unsigned long on, - unsigned long off) -{ - struct iwl_led_cmd led_cmd = { - .id = IWL_LED_LINK, - .interval = IWL_DEF_LED_INTRVL - }; - int ret; - - if (!test_bit(STATUS_READY, &priv->status)) - return -EBUSY; - - if (priv->blink_on == on && priv->blink_off == off) - return 0; - - if (off == 0) { - /* led is SOLID_ON */ - on = IWL_LED_SOLID; - } - - IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n", - priv->cfg->base_params->led_compensation); - led_cmd.on = iwl_blink_compensation(priv, on, - priv->cfg->base_params->led_compensation); - led_cmd.off = iwl_blink_compensation(priv, off, - priv->cfg->base_params->led_compensation); - - ret = iwl_send_led_cmd(priv, &led_cmd); - if (!ret) { - priv->blink_on = on; - priv->blink_off = off; - } - return ret; -} - -static void iwl_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); - unsigned long on = 0; - - if (brightness > 0) - on = IWL_LED_SOLID; - - iwl_led_cmd(priv, on, 0); -} - -static int iwl_led_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) -{ - struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); - - return iwl_led_cmd(priv, *delay_on, *delay_off); -} - -void iwl_leds_init(struct iwl_priv *priv) -{ - int mode = iwlwifi_mod_params.led_mode; - int ret; - - if (mode == IWL_LED_DISABLE) { - IWL_INFO(priv, "Led disabled\n"); - return; - } - if (mode == IWL_LED_DEFAULT) - mode = priv->cfg->led_mode; - - priv->led.name = kasprintf(GFP_KERNEL, "%s-led", - wiphy_name(priv->hw->wiphy)); - priv->led.brightness_set = iwl_led_brightness_set; - priv->led.blink_set = iwl_led_blink_set; - priv->led.max_brightness = 1; - - switch (mode) { - case IWL_LED_DEFAULT: - WARN_ON(1); - break; - case IWL_LED_BLINK: - priv->led.default_trigger = - ieee80211_create_tpt_led_trigger(priv->hw, - IEEE80211_TPT_LEDTRIG_FL_CONNECTED, - iwl_blink, ARRAY_SIZE(iwl_blink)); - break; - case IWL_LED_RF_STATE: - priv->led.default_trigger = - ieee80211_get_radio_led_name(priv->hw); - break; - } - - ret = led_classdev_register(priv->trans->dev, &priv->led); - if (ret) { - kfree(priv->led.name); - return; - } - - priv->led_registered = true; -} - -void iwl_leds_exit(struct iwl_priv *priv) -{ - if (!priv->led_registered) - return; - - led_classdev_unregister(&priv->led); - kfree(priv->led.name); -} diff --git a/drivers/net/wireless/iwlwifi/dvm/led.h b/drivers/net/wireless/iwlwifi/dvm/led.h deleted file mode 100644 index 1c6b2252d0f2..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/led.h +++ /dev/null @@ -1,55 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __iwl_leds_h__ -#define __iwl_leds_h__ - - -struct iwl_priv; - -#define IWL_LED_SOLID 11 -#define IWL_DEF_LED_INTRVL cpu_to_le32(1000) - -#define IWL_LED_ACTIVITY (0<<1) -#define IWL_LED_LINK (1<<1) - -#ifdef CONFIG_IWLWIFI_LEDS -void iwlagn_led_enable(struct iwl_priv *priv); -void iwl_leds_init(struct iwl_priv *priv); -void iwl_leds_exit(struct iwl_priv *priv); -#else -static inline void iwlagn_led_enable(struct iwl_priv *priv) -{ -} -static inline void iwl_leds_init(struct iwl_priv *priv) -{ -} -static inline void iwl_leds_exit(struct iwl_priv *priv) -{ -} -#endif - -#endif /* __iwl_leds_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c deleted file mode 100644 index e18629a16fb0..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ /dev/null @@ -1,1300 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include -#include -#include - -#include "iwl-io.h" -#include "iwl-agn-hw.h" -#include "iwl-trans.h" -#include "iwl-modparams.h" - -#include "dev.h" -#include "agn.h" - -int iwlagn_hw_valid_rtc_data_addr(u32 addr) -{ - return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) && - (addr < IWLAGN_RTC_DATA_UPPER_BOUND); -} - -int iwlagn_send_tx_power(struct iwl_priv *priv) -{ - struct iwlagn_tx_power_dbm_cmd tx_power_cmd; - u8 tx_ant_cfg_cmd; - - if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->status), - "TX Power requested while scanning!\n")) - return -EAGAIN; - - /* half dBm need to multiply */ - tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt); - - if (tx_power_cmd.global_lmt > priv->nvm_data->max_tx_pwr_half_dbm) { - /* - * For the newer devices which using enhanced/extend tx power - * table in EEPROM, the format is in half dBm. driver need to - * convert to dBm format before report to mac80211. - * By doing so, there is a possibility of 1/2 dBm resolution - * lost. driver will perform "round-up" operation before - * reporting, but it will cause 1/2 dBm tx power over the - * regulatory limit. Perform the checking here, if the - * "tx_power_user_lmt" is higher than EEPROM value (in - * half-dBm format), lower the tx power based on EEPROM - */ - tx_power_cmd.global_lmt = - priv->nvm_data->max_tx_pwr_half_dbm; - } - tx_power_cmd.flags = IWLAGN_TX_POWER_NO_CLOSED; - tx_power_cmd.srv_chan_lmt = IWLAGN_TX_POWER_AUTO; - - if (IWL_UCODE_API(priv->fw->ucode_ver) == 1) - tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD_V1; - else - tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD; - - return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, 0, - sizeof(tx_power_cmd), &tx_power_cmd); -} - -void iwlagn_temperature(struct iwl_priv *priv) -{ - lockdep_assert_held(&priv->statistics.lock); - - /* store temperature from correct statistics (in Celsius) */ - priv->temperature = le32_to_cpu(priv->statistics.common.temperature); - iwl_tt_handler(priv); -} - -int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band) -{ - int idx = 0; - int band_offset = 0; - - /* HT rate format: mac80211 wants an MCS number, which is just LSB */ - if (rate_n_flags & RATE_MCS_HT_MSK) { - idx = (rate_n_flags & 0xff); - return idx; - /* Legacy rate format, search for match in table */ - } else { - if (band == IEEE80211_BAND_5GHZ) - band_offset = IWL_FIRST_OFDM_RATE; - for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) - if (iwl_rates[idx].plcp == (rate_n_flags & 0xFF)) - return idx - band_offset; - } - - return -1; -} - -int iwlagn_manage_ibss_station(struct iwl_priv *priv, - struct ieee80211_vif *vif, bool add) -{ - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - - if (add) - return iwlagn_add_bssid_station(priv, vif_priv->ctx, - vif->bss_conf.bssid, - &vif_priv->ibss_bssid_sta_id); - return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id, - vif->bss_conf.bssid); -} - -/** - * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode - * - * pre-requirements: - * 1. acquire mutex before calling - * 2. make sure rf is on and not in exit state - */ -int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk) -{ - struct iwl_txfifo_flush_cmd_v3 flush_cmd_v3 = { - .flush_control = cpu_to_le16(IWL_DROP_ALL), - }; - struct iwl_txfifo_flush_cmd_v2 flush_cmd_v2 = { - .flush_control = cpu_to_le16(IWL_DROP_ALL), - }; - - u32 queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | - IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK; - - if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) - queue_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK | - IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK | - IWL_PAN_SCD_MGMT_MSK | - IWL_PAN_SCD_MULTICAST_MSK; - - if (priv->nvm_data->sku_cap_11n_enable) - queue_control |= IWL_AGG_TX_QUEUE_MSK; - - if (scd_q_msk) - queue_control = scd_q_msk; - - IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", queue_control); - flush_cmd_v3.queue_control = cpu_to_le32(queue_control); - flush_cmd_v2.queue_control = cpu_to_le16((u16)queue_control); - - if (IWL_UCODE_API(priv->fw->ucode_ver) > 2) - return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0, - sizeof(flush_cmd_v3), - &flush_cmd_v3); - return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0, - sizeof(flush_cmd_v2), &flush_cmd_v2); -} - -void iwlagn_dev_txfifo_flush(struct iwl_priv *priv) -{ - mutex_lock(&priv->mutex); - ieee80211_stop_queues(priv->hw); - if (iwlagn_txfifo_flush(priv, 0)) { - IWL_ERR(priv, "flush request fail\n"); - goto done; - } - IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n"); - iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff); -done: - ieee80211_wake_queues(priv->hw); - mutex_unlock(&priv->mutex); -} - -/* - * BT coex - */ -/* Notmal TDM */ -static const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaeaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xc0004000), - cpu_to_le32(0x00004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), -}; - - -/* Loose Coex */ -static const __le32 iwlagn_loose_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaeaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), -}; - -/* Full concurrency */ -static const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), -}; - -void iwlagn_send_advance_bt_config(struct iwl_priv *priv) -{ - struct iwl_basic_bt_cmd basic = { - .max_kill = IWLAGN_BT_MAX_KILL_DEFAULT, - .bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT, - .bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT, - .bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT, - }; - struct iwl_bt_cmd_v1 bt_cmd_v1; - struct iwl_bt_cmd_v2 bt_cmd_v2; - int ret; - - BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != - sizeof(basic.bt3_lookup_table)); - - if (priv->lib->bt_params) { - /* - * newer generation of devices (2000 series and newer) - * use the version 2 of the bt command - * we need to make sure sending the host command - * with correct data structure to avoid uCode assert - */ - if (priv->lib->bt_params->bt_session_2) { - bt_cmd_v2.prio_boost = cpu_to_le32( - priv->lib->bt_params->bt_prio_boost); - bt_cmd_v2.tx_prio_boost = 0; - bt_cmd_v2.rx_prio_boost = 0; - } else { - /* older version only has 8 bits */ - WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF); - bt_cmd_v1.prio_boost = - priv->lib->bt_params->bt_prio_boost; - bt_cmd_v1.tx_prio_boost = 0; - bt_cmd_v1.rx_prio_boost = 0; - } - } else { - IWL_ERR(priv, "failed to construct BT Coex Config\n"); - return; - } - - /* - * Possible situations when BT needs to take over for receive, - * at the same time where STA needs to response to AP's frame(s), - * reduce the tx power of the required response frames, by that, - * allow the concurrent BT receive & WiFi transmit - * (BT - ANT A, WiFi -ANT B), without interference to one another - * - * Reduced tx power apply to control frames only (ACK/Back/CTS) - * when indicated by the BT config command - */ - basic.kill_ack_mask = priv->kill_ack_mask; - basic.kill_cts_mask = priv->kill_cts_mask; - if (priv->reduced_txpower) - basic.reduce_txpower = IWLAGN_BT_REDUCED_TX_PWR; - basic.valid = priv->bt_valid; - - /* - * Configure BT coex mode to "no coexistence" when the - * user disabled BT coexistence, we have no interface - * (might be in monitor mode), or the interface is in - * IBSS mode (no proper uCode support for coex then). - */ - if (!iwlwifi_mod_params.bt_coex_active || - priv->iw_mode == NL80211_IFTYPE_ADHOC) { - basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED; - } else { - basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W << - IWLAGN_BT_FLAG_COEX_MODE_SHIFT; - - if (!priv->bt_enable_pspoll) - basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; - else - basic.flags &= ~IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; - - if (priv->bt_ch_announce) - basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION; - IWL_DEBUG_COEX(priv, "BT coex flag: 0X%x\n", basic.flags); - } - priv->bt_enable_flag = basic.flags; - if (priv->bt_full_concurrent) - memcpy(basic.bt3_lookup_table, iwlagn_concurrent_lookup, - sizeof(iwlagn_concurrent_lookup)); - else - memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup, - sizeof(iwlagn_def_3w_lookup)); - - IWL_DEBUG_COEX(priv, "BT coex %s in %s mode\n", - basic.flags ? "active" : "disabled", - priv->bt_full_concurrent ? - "full concurrency" : "3-wire"); - - if (priv->lib->bt_params->bt_session_2) { - memcpy(&bt_cmd_v2.basic, &basic, - sizeof(basic)); - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - 0, sizeof(bt_cmd_v2), &bt_cmd_v2); - } else { - memcpy(&bt_cmd_v1.basic, &basic, - sizeof(basic)); - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - 0, sizeof(bt_cmd_v1), &bt_cmd_v1); - } - if (ret) - IWL_ERR(priv, "failed to send BT Coex Config\n"); - -} - -void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena) -{ - struct iwl_rxon_context *ctx, *found_ctx = NULL; - bool found_ap = false; - - lockdep_assert_held(&priv->mutex); - - /* Check whether AP or GO mode is active. */ - if (rssi_ena) { - for_each_context(priv, ctx) { - if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_AP && - iwl_is_associated_ctx(ctx)) { - found_ap = true; - break; - } - } - } - - /* - * If disable was received or If GO/AP mode, disable RSSI - * measurements. - */ - if (!rssi_ena || found_ap) { - if (priv->cur_rssi_ctx) { - ctx = priv->cur_rssi_ctx; - ieee80211_disable_rssi_reports(ctx->vif); - priv->cur_rssi_ctx = NULL; - } - return; - } - - /* - * If rssi measurements need to be enabled, consider all cases now. - * Figure out how many contexts are active. - */ - for_each_context(priv, ctx) { - if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && - iwl_is_associated_ctx(ctx)) { - found_ctx = ctx; - break; - } - } - - /* - * rssi monitor already enabled for the correct interface...nothing - * to do. - */ - if (found_ctx == priv->cur_rssi_ctx) - return; - - /* - * Figure out if rssi monitor is currently enabled, and needs - * to be changed. If rssi monitor is already enabled, disable - * it first else just enable rssi measurements on the - * interface found above. - */ - if (priv->cur_rssi_ctx) { - ctx = priv->cur_rssi_ctx; - if (ctx->vif) - ieee80211_disable_rssi_reports(ctx->vif); - } - - priv->cur_rssi_ctx = found_ctx; - - if (!found_ctx) - return; - - ieee80211_enable_rssi_reports(found_ctx->vif, - IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD, - IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD); -} - -static bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg) -{ - return (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3SCOESCO_POS; -} - -static void iwlagn_bt_traffic_change_work(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, bt_traffic_change_work); - struct iwl_rxon_context *ctx; - int smps_request = -1; - - if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { - /* bt coex disabled */ - return; - } - - /* - * Note: bt_traffic_load can be overridden by scan complete and - * coex profile notifications. Ignore that since only bad consequence - * can be not matching debug print with actual state. - */ - IWL_DEBUG_COEX(priv, "BT traffic load changes: %d\n", - priv->bt_traffic_load); - - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - if (priv->bt_status) - smps_request = IEEE80211_SMPS_DYNAMIC; - else - smps_request = IEEE80211_SMPS_AUTOMATIC; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - smps_request = IEEE80211_SMPS_DYNAMIC; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - smps_request = IEEE80211_SMPS_STATIC; - break; - default: - IWL_ERR(priv, "Invalid BT traffic load: %d\n", - priv->bt_traffic_load); - break; - } - - mutex_lock(&priv->mutex); - - /* - * We can not send command to firmware while scanning. When the scan - * complete we will schedule this work again. We do check with mutex - * locked to prevent new scan request to arrive. We do not check - * STATUS_SCANNING to avoid race when queue_work two times from - * different notifications, but quit and not perform any work at all. - */ - if (test_bit(STATUS_SCAN_HW, &priv->status)) - goto out; - - iwl_update_chain_flags(priv); - - if (smps_request != -1) { - priv->current_ht_config.smps = smps_request; - for_each_context(priv, ctx) { - if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION) - ieee80211_request_smps(ctx->vif, smps_request); - } - } - - /* - * Dynamic PS poll related functionality. Adjust RSSI measurements if - * necessary. - */ - iwlagn_bt_coex_rssi_monitor(priv); -out: - mutex_unlock(&priv->mutex); -} - -/* - * If BT sco traffic, and RSSI monitor is enabled, move measurements to the - * correct interface or disable it if this is the last interface to be - * removed. - */ -void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv) -{ - if (priv->bt_is_sco && - priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS) - iwlagn_bt_adjust_rssi_monitor(priv, true); - else - iwlagn_bt_adjust_rssi_monitor(priv, false); -} - -static void iwlagn_print_uartmsg(struct iwl_priv *priv, - struct iwl_bt_uart_msg *uart_msg) -{ - IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, " - "Update Req = 0x%X\n", - (BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >> - BT_UART_MSG_FRAME1MSGTYPE_POS, - (BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >> - BT_UART_MSG_FRAME1SSN_POS, - (BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >> - BT_UART_MSG_FRAME1UPDATEREQ_POS); - - IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, " - "Chl_SeqN = 0x%X, In band = 0x%X\n", - (BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >> - BT_UART_MSG_FRAME2OPENCONNECTIONS_POS, - (BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >> - BT_UART_MSG_FRAME2TRAFFICLOAD_POS, - (BT_UART_MSG_FRAME2CHLSEQN_MSK & uart_msg->frame2) >> - BT_UART_MSG_FRAME2CHLSEQN_POS, - (BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >> - BT_UART_MSG_FRAME2INBAND_POS); - - IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, " - "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X\n", - (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3SCOESCO_POS, - (BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3SNIFF_POS, - (BT_UART_MSG_FRAME3A2DP_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3A2DP_POS, - (BT_UART_MSG_FRAME3ACL_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3ACL_POS, - (BT_UART_MSG_FRAME3MASTER_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3MASTER_POS, - (BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3OBEX_POS); - - IWL_DEBUG_COEX(priv, "Idle duration = 0x%X\n", - (BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >> - BT_UART_MSG_FRAME4IDLEDURATION_POS); - - IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, " - "eSCO Retransmissions = 0x%X\n", - (BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >> - BT_UART_MSG_FRAME5TXACTIVITY_POS, - (BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >> - BT_UART_MSG_FRAME5RXACTIVITY_POS, - (BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >> - BT_UART_MSG_FRAME5ESCORETRANSMIT_POS); - - IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X\n", - (BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >> - BT_UART_MSG_FRAME6SNIFFINTERVAL_POS, - (BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >> - BT_UART_MSG_FRAME6DISCOVERABLE_POS); - - IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = " - "0x%X, Inquiry = 0x%X, Connectable = 0x%X\n", - (BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >> - BT_UART_MSG_FRAME7SNIFFACTIVITY_POS, - (BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >> - BT_UART_MSG_FRAME7PAGE_POS, - (BT_UART_MSG_FRAME7INQUIRY_MSK & uart_msg->frame7) >> - BT_UART_MSG_FRAME7INQUIRY_POS, - (BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >> - BT_UART_MSG_FRAME7CONNECTABLE_POS); -} - -static bool iwlagn_set_kill_msk(struct iwl_priv *priv, - struct iwl_bt_uart_msg *uart_msg) -{ - bool need_update = false; - u8 kill_msk = IWL_BT_KILL_REDUCE; - static const __le32 bt_kill_ack_msg[3] = { - IWLAGN_BT_KILL_ACK_MASK_DEFAULT, - IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, - IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; - static const __le32 bt_kill_cts_msg[3] = { - IWLAGN_BT_KILL_CTS_MASK_DEFAULT, - IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, - IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; - - if (!priv->reduced_txpower) - kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) - ? IWL_BT_KILL_OVERRIDE : IWL_BT_KILL_DEFAULT; - if (priv->kill_ack_mask != bt_kill_ack_msg[kill_msk] || - priv->kill_cts_mask != bt_kill_cts_msg[kill_msk]) { - priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK; - priv->kill_ack_mask = bt_kill_ack_msg[kill_msk]; - priv->bt_valid |= IWLAGN_BT_VALID_KILL_CTS_MASK; - priv->kill_cts_mask = bt_kill_cts_msg[kill_msk]; - need_update = true; - } - return need_update; -} - -/* - * Upon RSSI changes, sends a bt config command with following changes - * 1. enable/disable "reduced control frames tx power - * 2. update the "kill)ack_mask" and "kill_cts_mask" - * - * If "reduced tx power" is enabled, uCode shall - * 1. ACK/Back/CTS rate shall reduced to 6Mbps - * 2. not use duplciate 20/40MHz mode - */ -static bool iwlagn_fill_txpower_mode(struct iwl_priv *priv, - struct iwl_bt_uart_msg *uart_msg) -{ - bool need_update = false; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - int ave_rssi; - - if (!ctx->vif || (ctx->vif->type != NL80211_IFTYPE_STATION)) { - IWL_DEBUG_INFO(priv, "BSS ctx not active or not in sta mode\n"); - return false; - } - - ave_rssi = ieee80211_ave_rssi(ctx->vif); - if (!ave_rssi) { - /* no rssi data, no changes to reduce tx power */ - IWL_DEBUG_COEX(priv, "no rssi data available\n"); - return need_update; - } - if (!priv->reduced_txpower && - !iwl_is_associated(priv, IWL_RXON_CTX_PAN) && - (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) && - (uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | - BT_UART_MSG_FRAME3OBEX_MSK)) && - !(uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | - BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK))) { - /* enabling reduced tx power */ - priv->reduced_txpower = true; - priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; - need_update = true; - } else if (priv->reduced_txpower && - (iwl_is_associated(priv, IWL_RXON_CTX_PAN) || - (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) || - (uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | - BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK)) || - !(uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | - BT_UART_MSG_FRAME3OBEX_MSK)))) { - /* disable reduced tx power */ - priv->reduced_txpower = false; - priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; - need_update = true; - } - - return need_update; -} - -static void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_bt_coex_profile_notif *coex = (void *)pkt->data; - struct iwl_bt_uart_msg *uart_msg = &coex->last_bt_uart_msg; - - if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { - /* bt coex disabled */ - return; - } - - IWL_DEBUG_COEX(priv, "BT Coex notification:\n"); - IWL_DEBUG_COEX(priv, " status: %d\n", coex->bt_status); - IWL_DEBUG_COEX(priv, " traffic load: %d\n", coex->bt_traffic_load); - IWL_DEBUG_COEX(priv, " CI compliance: %d\n", - coex->bt_ci_compliance); - iwlagn_print_uartmsg(priv, uart_msg); - - priv->last_bt_traffic_load = priv->bt_traffic_load; - priv->bt_is_sco = iwlagn_bt_traffic_is_sco(uart_msg); - - if (priv->iw_mode != NL80211_IFTYPE_ADHOC) { - if (priv->bt_status != coex->bt_status || - priv->last_bt_traffic_load != coex->bt_traffic_load) { - if (coex->bt_status) { - /* BT on */ - if (!priv->bt_ch_announce) - priv->bt_traffic_load = - IWL_BT_COEX_TRAFFIC_LOAD_HIGH; - else - priv->bt_traffic_load = - coex->bt_traffic_load; - } else { - /* BT off */ - priv->bt_traffic_load = - IWL_BT_COEX_TRAFFIC_LOAD_NONE; - } - priv->bt_status = coex->bt_status; - queue_work(priv->workqueue, - &priv->bt_traffic_change_work); - } - } - - /* schedule to send runtime bt_config */ - /* check reduce power before change ack/cts kill mask */ - if (iwlagn_fill_txpower_mode(priv, uart_msg) || - iwlagn_set_kill_msk(priv, uart_msg)) - queue_work(priv->workqueue, &priv->bt_runtime_config); - - - /* FIXME: based on notification, adjust the prio_boost */ - - priv->bt_ci_compliance = coex->bt_ci_compliance; -} - -void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv) -{ - priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] = - iwlagn_bt_coex_profile_notif; -} - -void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv) -{ - INIT_WORK(&priv->bt_traffic_change_work, - iwlagn_bt_traffic_change_work); -} - -void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv) -{ - cancel_work_sync(&priv->bt_traffic_change_work); -} - -static bool is_single_rx_stream(struct iwl_priv *priv) -{ - return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC || - priv->current_ht_config.single_chain_sufficient; -} - -#define IWL_NUM_RX_CHAINS_MULTIPLE 3 -#define IWL_NUM_RX_CHAINS_SINGLE 2 -#define IWL_NUM_IDLE_CHAINS_DUAL 2 -#define IWL_NUM_IDLE_CHAINS_SINGLE 1 - -/* - * Determine how many receiver/antenna chains to use. - * - * More provides better reception via diversity. Fewer saves power - * at the expense of throughput, but only when not in powersave to - * start with. - * - * MIMO (dual stream) requires at least 2, but works better with 3. - * This does not determine *which* chains to use, just how many. - */ -static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) -{ - if (priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist && - (priv->bt_full_concurrent || - priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { - /* - * only use chain 'A' in bt high traffic load or - * full concurrency mode - */ - return IWL_NUM_RX_CHAINS_SINGLE; - } - /* # of Rx chains to use when expecting MIMO. */ - if (is_single_rx_stream(priv)) - return IWL_NUM_RX_CHAINS_SINGLE; - else - return IWL_NUM_RX_CHAINS_MULTIPLE; -} - -/* - * When we are in power saving mode, unless device support spatial - * multiplexing power save, use the active count for rx chain count. - */ -static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) -{ - /* # Rx chains when idling, depending on SMPS mode */ - switch (priv->current_ht_config.smps) { - case IEEE80211_SMPS_STATIC: - case IEEE80211_SMPS_DYNAMIC: - return IWL_NUM_IDLE_CHAINS_SINGLE; - case IEEE80211_SMPS_AUTOMATIC: - case IEEE80211_SMPS_OFF: - return active_cnt; - default: - WARN(1, "invalid SMPS mode %d", - priv->current_ht_config.smps); - return active_cnt; - } -} - -/* up to 4 chains */ -static u8 iwl_count_chain_bitmap(u32 chain_bitmap) -{ - u8 res; - res = (chain_bitmap & BIT(0)) >> 0; - res += (chain_bitmap & BIT(1)) >> 1; - res += (chain_bitmap & BIT(2)) >> 2; - res += (chain_bitmap & BIT(3)) >> 3; - return res; -} - -/** - * iwlagn_set_rxon_chain - Set up Rx chain usage in "staging" RXON image - * - * Selects how many and which Rx receivers/antennas/chains to use. - * This should not be used for scan command ... it puts data in wrong place. - */ -void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) -{ - bool is_single = is_single_rx_stream(priv); - bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); - u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; - u32 active_chains; - u16 rx_chain; - - /* Tell uCode which antennas are actually connected. - * Before first association, we assume all antennas are connected. - * Just after first association, iwl_chain_noise_calibration() - * checks which antennas actually *are* connected. */ - if (priv->chain_noise_data.active_chains) - active_chains = priv->chain_noise_data.active_chains; - else - active_chains = priv->nvm_data->valid_rx_ant; - - if (priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist && - (priv->bt_full_concurrent || - priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { - /* - * only use chain 'A' in bt high traffic load or - * full concurrency mode - */ - active_chains = first_antenna(active_chains); - } - - rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; - - /* How many receivers should we use? */ - active_rx_cnt = iwl_get_active_rx_chain_count(priv); - idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt); - - - /* correct rx chain count according hw settings - * and chain noise calibration - */ - valid_rx_cnt = iwl_count_chain_bitmap(active_chains); - if (valid_rx_cnt < active_rx_cnt) - active_rx_cnt = valid_rx_cnt; - - if (valid_rx_cnt < idle_rx_cnt) - idle_rx_cnt = valid_rx_cnt; - - rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; - rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; - - ctx->staging.rx_chain = cpu_to_le16(rx_chain); - - if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam) - ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; - else - ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; - - IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n", - ctx->staging.rx_chain, - active_rx_cnt, idle_rx_cnt); - - WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || - active_rx_cnt < idle_rx_cnt); -} - -u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid) -{ - int i; - u8 ind = ant; - - if (priv->band == IEEE80211_BAND_2GHZ && - priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) - return 0; - - for (i = 0; i < RATE_ANT_NUM - 1; i++) { - ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; - if (valid & BIT(ind)) - return ind; - } - return ant; -} - -#ifdef CONFIG_PM_SLEEP -static void iwlagn_convert_p1k(u16 *p1k, __le16 *out) -{ - int i; - - for (i = 0; i < IWLAGN_P1K_SIZE; i++) - out[i] = cpu_to_le16(p1k[i]); -} - -struct wowlan_key_data { - struct iwl_rxon_context *ctx; - struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc; - struct iwlagn_wowlan_tkip_params_cmd *tkip; - const u8 *bssid; - bool error, use_rsc_tsc, use_tkip; -}; - - -static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key, - void *_data) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct wowlan_key_data *data = _data; - struct iwl_rxon_context *ctx = data->ctx; - struct aes_sc *aes_sc, *aes_tx_sc = NULL; - struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; - struct iwlagn_p1k_cache *rx_p1ks; - u8 *rx_mic_key; - struct ieee80211_key_seq seq; - u32 cur_rx_iv32 = 0; - u16 p1k[IWLAGN_P1K_SIZE]; - int ret, i; - - mutex_lock(&priv->mutex); - - if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || - key->cipher == WLAN_CIPHER_SUITE_WEP104) && - !sta && !ctx->key_mapping_keys) - ret = iwl_set_default_wep_key(priv, ctx, key); - else - ret = iwl_set_dynamic_key(priv, ctx, key, sta); - - if (ret) { - IWL_ERR(priv, "Error setting key during suspend!\n"); - data->error = true; - } - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - if (sta) { - tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; - tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; - - rx_p1ks = data->tkip->rx_uni; - - ieee80211_get_key_tx_seq(key, &seq); - tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); - - ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); - iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); - - memcpy(data->tkip->mic_keys.tx, - &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], - IWLAGN_MIC_KEY_SIZE); - - rx_mic_key = data->tkip->mic_keys.rx_unicast; - } else { - tkip_sc = - data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; - rx_p1ks = data->tkip->rx_multi; - rx_mic_key = data->tkip->mic_keys.rx_mcast; - } - - /* - * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 (as they need to to avoid replay attacks) - * for checking the IV in the frames. - */ - for (i = 0; i < IWLAGN_NUM_RSC; i++) { - ieee80211_get_key_rx_seq(key, i, &seq); - tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); - /* wrapping isn't allowed, AP must rekey */ - if (seq.tkip.iv32 > cur_rx_iv32) - cur_rx_iv32 = seq.tkip.iv32; - } - - ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k); - iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k); - ieee80211_get_tkip_rx_p1k(key, data->bssid, - cur_rx_iv32 + 1, p1k); - iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k); - - memcpy(rx_mic_key, - &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], - IWLAGN_MIC_KEY_SIZE); - - data->use_tkip = true; - data->use_rsc_tsc = true; - break; - case WLAN_CIPHER_SUITE_CCMP: - if (sta) { - u8 *pn = seq.ccmp.pn; - - aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; - aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; - - ieee80211_get_key_tx_seq(key, &seq); - aes_tx_sc->pn = cpu_to_le64( - (u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); - } else - aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; - - /* - * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 for checking the IV in the frames. - */ - for (i = 0; i < IWLAGN_NUM_RSC; i++) { - u8 *pn = seq.ccmp.pn; - - ieee80211_get_key_rx_seq(key, i, &seq); - aes_sc[i].pn = cpu_to_le64( - (u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); - } - data->use_rsc_tsc = true; - break; - } - - mutex_unlock(&priv->mutex); -} - -int iwlagn_send_patterns(struct iwl_priv *priv, - struct cfg80211_wowlan *wowlan) -{ - struct iwlagn_wowlan_patterns_cmd *pattern_cmd; - struct iwl_host_cmd cmd = { - .id = REPLY_WOWLAN_PATTERNS, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - }; - int i, err; - - if (!wowlan->n_patterns) - return 0; - - cmd.len[0] = sizeof(*pattern_cmd) + - wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern); - - pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); - if (!pattern_cmd) - return -ENOMEM; - - pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); - - for (i = 0; i < wowlan->n_patterns; i++) { - int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); - - memcpy(&pattern_cmd->patterns[i].mask, - wowlan->patterns[i].mask, mask_len); - memcpy(&pattern_cmd->patterns[i].pattern, - wowlan->patterns[i].pattern, - wowlan->patterns[i].pattern_len); - pattern_cmd->patterns[i].mask_size = mask_len; - pattern_cmd->patterns[i].pattern_size = - wowlan->patterns[i].pattern_len; - } - - cmd.data[0] = pattern_cmd; - err = iwl_dvm_send_cmd(priv, &cmd); - kfree(pattern_cmd); - return err; -} - -int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) -{ - struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd; - struct iwl_rxon_cmd rxon; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd; - struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {}; - struct iwlagn_d3_config_cmd d3_cfg_cmd = { - /* - * Program the minimum sleep time to 10 seconds, as many - * platforms have issues processing a wakeup signal while - * still being in the process of suspending. - */ - .min_sleep_time = cpu_to_le32(10 * 1000 * 1000), - }; - struct wowlan_key_data key_data = { - .ctx = ctx, - .bssid = ctx->active.bssid_addr, - .use_rsc_tsc = false, - .tkip = &tkip_cmd, - .use_tkip = false, - }; - int ret, i; - u16 seq; - - key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); - if (!key_data.rsc_tsc) - return -ENOMEM; - - memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd)); - - /* - * We know the last used seqno, and the uCode expects to know that - * one, it will increment before TX. - */ - seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ; - wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq); - - /* - * For QoS counters, we store the one to use next, so subtract 0x10 - * since the uCode will add 0x10 before using the value. - */ - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - seq = priv->tid_data[IWL_AP_ID][i].seq_number; - seq -= 0x10; - wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq); - } - - if (wowlan->disconnect) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | - IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE); - if (wowlan->magic_pkt) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET); - if (wowlan->gtk_rekey_failure) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL); - if (wowlan->eap_identity_req) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ); - if (wowlan->four_way_handshake) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); - if (wowlan->n_patterns) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); - - if (wowlan->rfkill_release) - d3_cfg_cmd.wakeup_flags |= - cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL); - - iwl_scan_cancel_timeout(priv, 200); - - memcpy(&rxon, &ctx->active, sizeof(rxon)); - - priv->ucode_loaded = false; - iwl_trans_stop_device(priv->trans); - - priv->wowlan = true; - - ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); - if (ret) - goto out; - - /* now configure WoWLAN ucode */ - ret = iwl_alive_start(priv); - if (ret) - goto out; - - memcpy(&ctx->staging, &rxon, sizeof(rxon)); - ret = iwlagn_commit_rxon(priv, ctx); - if (ret) - goto out; - - ret = iwl_power_update_mode(priv, true); - if (ret) - goto out; - - if (!iwlwifi_mod_params.sw_crypto) { - /* mark all keys clear */ - priv->ucode_key_table = 0; - ctx->key_mapping_keys = 0; - - /* - * This needs to be unlocked due to lock ordering - * constraints. Since we're in the suspend path - * that isn't really a problem though. - */ - mutex_unlock(&priv->mutex); - ieee80211_iter_keys(priv->hw, ctx->vif, - iwlagn_wowlan_program_keys, - &key_data); - mutex_lock(&priv->mutex); - if (key_data.error) { - ret = -EIO; - goto out; - } - - if (key_data.use_rsc_tsc) { - struct iwl_host_cmd rsc_tsc_cmd = { - .id = REPLY_WOWLAN_TSC_RSC_PARAMS, - .data[0] = key_data.rsc_tsc, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .len[0] = sizeof(*key_data.rsc_tsc), - }; - - ret = iwl_dvm_send_cmd(priv, &rsc_tsc_cmd); - if (ret) - goto out; - } - - if (key_data.use_tkip) { - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_WOWLAN_TKIP_PARAMS, - 0, sizeof(tkip_cmd), - &tkip_cmd); - if (ret) - goto out; - } - - if (priv->have_rekey_data) { - memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); - memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN); - kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); - memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN); - kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); - kek_kck_cmd.replay_ctr = priv->replay_ctr; - - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_WOWLAN_KEK_KCK_MATERIAL, - 0, sizeof(kek_kck_cmd), - &kek_kck_cmd); - if (ret) - goto out; - } - } - - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, 0, - sizeof(d3_cfg_cmd), &d3_cfg_cmd); - if (ret) - goto out; - - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER, - 0, sizeof(wakeup_filter_cmd), - &wakeup_filter_cmd); - if (ret) - goto out; - - ret = iwlagn_send_patterns(priv, wowlan); - out: - kfree(key_data.rsc_tsc); - return ret; -} -#endif - -int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) -{ - if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) { - IWL_WARN(priv, "Not sending command - %s KILL\n", - iwl_is_rfkill(priv) ? "RF" : "CT"); - return -EIO; - } - - if (test_bit(STATUS_FW_ERROR, &priv->status)) { - IWL_ERR(priv, "Command %s failed: FW Error\n", - iwl_dvm_get_cmd_string(cmd->id)); - return -EIO; - } - - /* - * This can happen upon FW ASSERT: we clear the STATUS_FW_ERROR flag - * in iwl_down but cancel the workers only later. - */ - if (!priv->ucode_loaded) { - IWL_ERR(priv, "Fw not loaded - dropping CMD: %x\n", cmd->id); - return -EIO; - } - - /* - * Synchronous commands from this op-mode must hold - * the mutex, this ensures we don't try to send two - * (or more) synchronous commands at a time. - */ - if (!(cmd->flags & CMD_ASYNC)) - lockdep_assert_held(&priv->mutex); - - return iwl_trans_send_cmd(priv->trans, cmd); -} - -int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, - u32 flags, u16 len, const void *data) -{ - struct iwl_host_cmd cmd = { - .id = id, - .len = { len, }, - .data = { data, }, - .flags = flags, - }; - - return iwl_dvm_send_cmd(priv, &cmd); -} diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c deleted file mode 100644 index b3ad34e8bf5a..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ /dev/null @@ -1,1655 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "iwl-io.h" -#include "iwl-trans.h" -#include "iwl-op-mode.h" -#include "iwl-modparams.h" - -#include "dev.h" -#include "calib.h" -#include "agn.h" - -/***************************************************************************** - * - * mac80211 entry point functions - * - *****************************************************************************/ - -static const struct ieee80211_iface_limit iwlagn_sta_ap_limits[] = { - { - .max = 1, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_AP), - }, -}; - -static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = { - { - .max = 2, - .types = BIT(NL80211_IFTYPE_STATION), - }, -}; - -static const struct ieee80211_iface_combination -iwlagn_iface_combinations_dualmode[] = { - { .num_different_channels = 1, - .max_interfaces = 2, - .beacon_int_infra_match = true, - .limits = iwlagn_sta_ap_limits, - .n_limits = ARRAY_SIZE(iwlagn_sta_ap_limits), - }, - { .num_different_channels = 1, - .max_interfaces = 2, - .limits = iwlagn_2sta_limits, - .n_limits = ARRAY_SIZE(iwlagn_2sta_limits), - }, -}; - -/* - * Not a mac80211 entry point function, but it fits in with all the - * other mac80211 functions grouped here. - */ -int iwlagn_mac_setup_register(struct iwl_priv *priv, - const struct iwl_ucode_capabilities *capa) -{ - int ret; - struct ieee80211_hw *hw = priv->hw; - struct iwl_rxon_context *ctx; - - hw->rate_control_algorithm = "iwl-agn-rs"; - - /* Tell mac80211 our characteristics */ - ieee80211_hw_set(hw, SIGNAL_DBM); - ieee80211_hw_set(hw, AMPDU_AGGREGATION); - ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC); - ieee80211_hw_set(hw, SPECTRUM_MGMT); - ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); - ieee80211_hw_set(hw, QUEUE_CONTROL); - ieee80211_hw_set(hw, SUPPORTS_PS); - ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); - ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); - ieee80211_hw_set(hw, WANT_MONITOR_VIF); - - hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE; - hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT; - - /* - * Including the following line will crash some AP's. This - * workaround removes the stimulus which causes the crash until - * the AP software can be fixed. - hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF; - */ - - if (priv->nvm_data->sku_cap_11n_enable) - hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS | - NL80211_FEATURE_STATIC_SMPS; - - /* - * Enable 11w if advertised by firmware and software crypto - * is not enabled (as the firmware will interpret some mgmt - * packets, so enabling it with software crypto isn't safe) - */ - if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP && - !iwlwifi_mod_params.sw_crypto) - ieee80211_hw_set(hw, MFP_CAPABLE); - - hw->sta_data_size = sizeof(struct iwl_station_priv); - hw->vif_data_size = sizeof(struct iwl_vif_priv); - - for_each_context(priv, ctx) { - hw->wiphy->interface_modes |= ctx->interface_modes; - hw->wiphy->interface_modes |= ctx->exclusive_interface_modes; - } - - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - - if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { - hw->wiphy->iface_combinations = - iwlagn_iface_combinations_dualmode; - hw->wiphy->n_iface_combinations = - ARRAY_SIZE(iwlagn_iface_combinations_dualmode); - } - - hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | - REGULATORY_DISABLE_BEACON_HINTS; - -#ifdef CONFIG_PM_SLEEP - if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len && - priv->trans->ops->d3_suspend && - priv->trans->ops->d3_resume && - device_can_wakeup(priv->trans->dev)) { - priv->wowlan_support.flags = WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_RFKILL_RELEASE; - if (!iwlwifi_mod_params.sw_crypto) - priv->wowlan_support.flags |= - WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | - WIPHY_WOWLAN_GTK_REKEY_FAILURE; - - priv->wowlan_support.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS; - priv->wowlan_support.pattern_min_len = - IWLAGN_WOWLAN_MIN_PATTERN_LEN; - priv->wowlan_support.pattern_max_len = - IWLAGN_WOWLAN_MAX_PATTERN_LEN; - hw->wiphy->wowlan = &priv->wowlan_support; - } -#endif - - if (iwlwifi_mod_params.power_save) - hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; - else - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; - /* we create the 802.11 header and a max-length SSID element */ - hw->wiphy->max_scan_ie_len = capa->max_probe_length - 24 - 34; - - /* - * We don't use all queues: 4 and 9 are unused and any - * aggregation queue gets mapped down to the AC queue. - */ - hw->queues = IWLAGN_FIRST_AMPDU_QUEUE; - - hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; - - if (priv->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels) - priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &priv->nvm_data->bands[IEEE80211_BAND_2GHZ]; - if (priv->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels) - priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &priv->nvm_data->bands[IEEE80211_BAND_5GHZ]; - - hw->wiphy->hw_version = priv->trans->hw_id; - - iwl_leds_init(priv); - - ret = ieee80211_register_hw(priv->hw); - if (ret) { - IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); - iwl_leds_exit(priv); - return ret; - } - priv->mac80211_registered = 1; - - return 0; -} - -void iwlagn_mac_unregister(struct iwl_priv *priv) -{ - if (!priv->mac80211_registered) - return; - iwl_leds_exit(priv); - ieee80211_unregister_hw(priv->hw); - priv->mac80211_registered = 0; -} - -static int __iwl_up(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - int ret; - - lockdep_assert_held(&priv->mutex); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { - IWL_WARN(priv, "Exit pending; will not bring the NIC up\n"); - return -EIO; - } - - for_each_context(priv, ctx) { - ret = iwlagn_alloc_bcast_station(priv, ctx); - if (ret) { - iwl_dealloc_bcast_stations(priv); - return ret; - } - } - - ret = iwl_trans_start_hw(priv->trans); - if (ret) { - IWL_ERR(priv, "Failed to start HW: %d\n", ret); - goto error; - } - - ret = iwl_run_init_ucode(priv); - if (ret) { - IWL_ERR(priv, "Failed to run INIT ucode: %d\n", ret); - goto error; - } - - ret = iwl_trans_start_hw(priv->trans); - if (ret) { - IWL_ERR(priv, "Failed to start HW: %d\n", ret); - goto error; - } - - ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR); - if (ret) { - IWL_ERR(priv, "Failed to start RT ucode: %d\n", ret); - goto error; - } - - ret = iwl_alive_start(priv); - if (ret) - goto error; - return 0; - - error: - set_bit(STATUS_EXIT_PENDING, &priv->status); - iwl_down(priv); - clear_bit(STATUS_EXIT_PENDING, &priv->status); - - IWL_ERR(priv, "Unable to initialize device.\n"); - return ret; -} - -static int iwlagn_mac_start(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int ret; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - /* we should be verifying the device is ready to be opened */ - mutex_lock(&priv->mutex); - ret = __iwl_up(priv); - mutex_unlock(&priv->mutex); - if (ret) - return ret; - - IWL_DEBUG_INFO(priv, "Start UP work done.\n"); - - /* Now we should be done, and the READY bit should be set. */ - if (WARN_ON(!test_bit(STATUS_READY, &priv->status))) - ret = -EIO; - - iwlagn_led_enable(priv); - - priv->is_open = 1; - IWL_DEBUG_MAC80211(priv, "leave\n"); - return 0; -} - -static void iwlagn_mac_stop(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (!priv->is_open) - return; - - priv->is_open = 0; - - mutex_lock(&priv->mutex); - iwl_down(priv); - mutex_unlock(&priv->mutex); - - iwl_cancel_deferred_work(priv); - - flush_workqueue(priv->workqueue); - - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_gtk_rekey_data *data) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - if (iwlwifi_mod_params.sw_crypto) - return; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - - if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif) - goto out; - - memcpy(priv->kek, data->kek, NL80211_KEK_LEN); - memcpy(priv->kck, data->kck, NL80211_KCK_LEN); - priv->replay_ctr = - cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr)); - priv->have_rekey_data = true; - - out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -#ifdef CONFIG_PM_SLEEP - -static int iwlagn_mac_suspend(struct ieee80211_hw *hw, - struct cfg80211_wowlan *wowlan) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - int ret; - - if (WARN_ON(!wowlan)) - return -EINVAL; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - - /* Don't attempt WoWLAN when not associated, tear down instead. */ - if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION || - !iwl_is_associated_ctx(ctx)) { - ret = 1; - goto out; - } - - ret = iwlagn_suspend(priv, wowlan); - if (ret) - goto error; - - /* let the ucode operate on its own */ - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - - iwl_trans_d3_suspend(priv->trans, false); - - goto out; - - error: - priv->wowlan = false; - iwlagn_prepare_restart(priv); - ieee80211_restart_hw(priv->hw); - out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -struct iwl_resume_data { - struct iwl_priv *priv; - struct iwlagn_wowlan_status *cmd; - bool valid; -}; - -static bool iwl_resume_status_fn(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_resume_data *resume_data = data; - struct iwl_priv *priv = resume_data->priv; - - if (iwl_rx_packet_payload_len(pkt) != sizeof(*resume_data->cmd)) { - IWL_ERR(priv, "rx wrong size data\n"); - return true; - } - memcpy(resume_data->cmd, pkt->data, sizeof(*resume_data->cmd)); - resume_data->valid = true; - - return true; -} - -static int iwlagn_mac_resume(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct ieee80211_vif *vif; - u32 base; - int ret; - enum iwl_d3_status d3_status; - struct error_table_start { - /* cf. struct iwl_error_event_table */ - u32 valid; - u32 error_id; - } err_info; - struct iwl_notification_wait status_wait; - static const u16 status_cmd[] = { - REPLY_WOWLAN_GET_STATUS, - }; - struct iwlagn_wowlan_status status_data = {}; - struct iwl_resume_data resume_data = { - .priv = priv, - .cmd = &status_data, - .valid = false, - }; - struct cfg80211_wowlan_wakeup wakeup = { - .pattern_idx = -1, - }; -#ifdef CONFIG_IWLWIFI_DEBUGFS - const struct fw_img *img; -#endif - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - - /* we'll clear ctx->vif during iwlagn_prepare_restart() */ - vif = ctx->vif; - - ret = iwl_trans_d3_resume(priv->trans, &d3_status, false); - if (ret) - goto out_unlock; - - if (d3_status != IWL_D3_STATUS_ALIVE) { - IWL_INFO(priv, "Device was reset during suspend\n"); - goto out_unlock; - } - - /* uCode is no longer operating by itself */ - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - - base = priv->device_pointers.error_event_table; - if (!iwlagn_hw_valid_rtc_data_addr(base)) { - IWL_WARN(priv, "Invalid error table during resume!\n"); - goto out_unlock; - } - - iwl_trans_read_mem_bytes(priv->trans, base, - &err_info, sizeof(err_info)); - - if (err_info.valid) { - IWL_INFO(priv, "error table is valid (%d, 0x%x)\n", - err_info.valid, err_info.error_id); - if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) { - wakeup.rfkill_release = true; - ieee80211_report_wowlan_wakeup(vif, &wakeup, - GFP_KERNEL); - } - goto out_unlock; - } - -#ifdef CONFIG_IWLWIFI_DEBUGFS - img = &priv->fw->img[IWL_UCODE_WOWLAN]; - if (!priv->wowlan_sram) - priv->wowlan_sram = - kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len, - GFP_KERNEL); - - if (priv->wowlan_sram) - iwl_trans_read_mem(priv->trans, 0x800000, - priv->wowlan_sram, - img->sec[IWL_UCODE_SECTION_DATA].len / 4); -#endif - - /* - * This is very strange. The GET_STATUS command is sent but the device - * doesn't reply properly, it seems it doesn't close the RBD so one is - * always left open ... As a result, we need to send another command - * and have to reset the driver afterwards. As we need to switch to - * runtime firmware again that'll happen. - */ - - iwl_init_notification_wait(&priv->notif_wait, &status_wait, status_cmd, - ARRAY_SIZE(status_cmd), iwl_resume_status_fn, - &resume_data); - - iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_GET_STATUS, CMD_ASYNC, 0, NULL); - iwl_dvm_send_cmd_pdu(priv, REPLY_ECHO, CMD_ASYNC, 0, NULL); - /* an RBD is left open in the firmware now! */ - - ret = iwl_wait_notification(&priv->notif_wait, &status_wait, HZ/5); - if (ret) - goto out_unlock; - - if (resume_data.valid && priv->contexts[IWL_RXON_CTX_BSS].vif) { - u32 reasons = le32_to_cpu(status_data.wakeup_reason); - struct cfg80211_wowlan_wakeup *wakeup_report; - - IWL_INFO(priv, "WoWLAN wakeup reason(s): 0x%.8x\n", reasons); - - if (reasons) { - if (reasons & IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET) - wakeup.magic_pkt = true; - if (reasons & IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH) - wakeup.pattern_idx = status_data.pattern_number; - if (reasons & (IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | - IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE)) - wakeup.disconnect = true; - if (reasons & IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL) - wakeup.gtk_rekey_failure = true; - if (reasons & IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ) - wakeup.eap_identity_req = true; - if (reasons & IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE) - wakeup.four_way_handshake = true; - wakeup_report = &wakeup; - } else { - wakeup_report = NULL; - } - - ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); - } - - priv->wowlan = false; - - iwlagn_prepare_restart(priv); - - memset((void *)&ctx->active, 0, sizeof(ctx->active)); - iwl_connection_init_rx_config(priv, ctx); - iwlagn_set_rxon_chain(priv, ctx); - - out_unlock: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - ieee80211_resume_disconnect(vif); - - return 1; -} - -static void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - device_set_wakeup_enable(priv->trans->dev, enabled); -} -#endif - -static void iwlagn_mac_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - if (iwlagn_tx_skb(priv, control->sta, skb)) - ieee80211_free_txskb(hw, skb); -} - -static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, - u32 iv32, u16 *phase1key) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key); -} - -static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - struct iwl_rxon_context *ctx = vif_priv->ctx; - int ret; - bool is_default_wep_key = false; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (iwlwifi_mod_params.sw_crypto) { - IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n"); - return -EOPNOTSUPP; - } - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - /* fall through */ - case WLAN_CIPHER_SUITE_CCMP: - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - break; - default: - break; - } - - /* - * We could program these keys into the hardware as well, but we - * don't expect much multicast traffic in IBSS and having keys - * for more stations is probably more useful. - * - * Mark key TX-only and return 0. - */ - if (vif->type == NL80211_IFTYPE_ADHOC && - !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { - key->hw_key_idx = WEP_INVALID_OFFSET; - return 0; - } - - /* If they key was TX-only, accept deletion */ - if (cmd == DISABLE_KEY && key->hw_key_idx == WEP_INVALID_OFFSET) - return 0; - - mutex_lock(&priv->mutex); - iwl_scan_cancel_timeout(priv, 100); - - BUILD_BUG_ON(WEP_INVALID_OFFSET == IWLAGN_HW_KEY_DEFAULT); - - /* - * If we are getting WEP group key and we didn't receive any key mapping - * so far, we are in legacy wep mode (group key only), otherwise we are - * in 1X mode. - * In legacy wep mode, we use another host command to the uCode. - */ - if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || - key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { - if (cmd == SET_KEY) - is_default_wep_key = !ctx->key_mapping_keys; - else - is_default_wep_key = - key->hw_key_idx == IWLAGN_HW_KEY_DEFAULT; - } - - - switch (cmd) { - case SET_KEY: - if (is_default_wep_key) { - ret = iwl_set_default_wep_key(priv, vif_priv->ctx, key); - break; - } - ret = iwl_set_dynamic_key(priv, vif_priv->ctx, key, sta); - if (ret) { - /* - * can't add key for RX, but we don't need it - * in the device for TX so still return 0 - */ - ret = 0; - key->hw_key_idx = WEP_INVALID_OFFSET; - } - - IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n"); - break; - case DISABLE_KEY: - if (is_default_wep_key) - ret = iwl_remove_default_wep_key(priv, ctx, key); - else - ret = iwl_remove_dynamic_key(priv, ctx, key, sta); - - IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n"); - break; - default: - ret = -EINVAL; - } - - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg) -{ - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) - return false; - return true; -} - -static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) -{ - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) - return false; - if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG) - return true; - - /* disabled by default */ - return false; -} - -static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int ret = -EINVAL; - struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; - - IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", - sta->addr, tid); - - if (!(priv->nvm_data->sku_cap_11n_enable)) - return -EACCES; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - - switch (action) { - case IEEE80211_AMPDU_RX_START: - if (!iwl_enable_rx_ampdu(priv->cfg)) - break; - IWL_DEBUG_HT(priv, "start Rx\n"); - ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn); - break; - case IEEE80211_AMPDU_RX_STOP: - IWL_DEBUG_HT(priv, "stop Rx\n"); - ret = iwl_sta_rx_agg_stop(priv, sta, tid); - break; - case IEEE80211_AMPDU_TX_START: - if (!priv->trans->ops->txq_enable) - break; - if (!iwl_enable_tx_ampdu(priv->cfg)) - break; - IWL_DEBUG_HT(priv, "start Tx\n"); - ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); - break; - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - IWL_DEBUG_HT(priv, "Flush Tx\n"); - ret = iwlagn_tx_agg_flush(priv, vif, sta, tid); - break; - case IEEE80211_AMPDU_TX_STOP_CONT: - IWL_DEBUG_HT(priv, "stop Tx\n"); - ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); - if ((ret == 0) && (priv->agg_tids_count > 0)) { - priv->agg_tids_count--; - IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", - priv->agg_tids_count); - } - if (!priv->agg_tids_count && - priv->hw_params.use_rts_for_aggregation) { - /* - * switch off RTS/CTS if it was previously enabled - */ - sta_priv->lq_sta.lq.general_params.flags &= - ~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; - iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif), - &sta_priv->lq_sta.lq, CMD_ASYNC, false); - } - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - ret = iwlagn_tx_agg_oper(priv, vif, sta, tid, buf_size); - break; - } - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - return ret; -} - -static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - bool is_ap = vif->type == NL80211_IFTYPE_STATION; - int ret; - u8 sta_id; - - IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n", - sta->addr); - sta_priv->sta_id = IWL_INVALID_STATION; - - atomic_set(&sta_priv->pending_frames, 0); - if (vif->type == NL80211_IFTYPE_AP) - sta_priv->client = true; - - ret = iwl_add_station_common(priv, vif_priv->ctx, sta->addr, - is_ap, sta, &sta_id); - if (ret) { - IWL_ERR(priv, "Unable to add station %pM (%d)\n", - sta->addr, ret); - /* Should we return success if return code is EEXIST ? */ - return ret; - } - - sta_priv->sta_id = sta_id; - - return 0; -} - -static int iwlagn_mac_sta_remove(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - int ret; - - IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", sta->addr); - - if (vif->type == NL80211_IFTYPE_STATION) { - /* - * Station will be removed from device when the RXON - * is set to unassociated -- just deactivate it here - * to avoid re-programming it. - */ - ret = 0; - iwl_deactivate_station(priv, sta_priv->sta_id, sta->addr); - } else { - ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr); - if (ret) - IWL_DEBUG_QUIET_RFKILL(priv, - "Error removing station %pM\n", sta->addr); - } - return ret; -} - -static int iwlagn_mac_sta_state(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - enum ieee80211_sta_state old_state, - enum ieee80211_sta_state new_state) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - enum { - NONE, ADD, REMOVE, HT_RATE_INIT, ADD_RATE_INIT, - } op = NONE; - int ret; - - IWL_DEBUG_MAC80211(priv, "station %pM state change %d->%d\n", - sta->addr, old_state, new_state); - - mutex_lock(&priv->mutex); - if (vif->type == NL80211_IFTYPE_STATION) { - if (old_state == IEEE80211_STA_NOTEXIST && - new_state == IEEE80211_STA_NONE) - op = ADD; - else if (old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_NOTEXIST) - op = REMOVE; - else if (old_state == IEEE80211_STA_AUTH && - new_state == IEEE80211_STA_ASSOC) - op = HT_RATE_INIT; - } else { - if (old_state == IEEE80211_STA_AUTH && - new_state == IEEE80211_STA_ASSOC) - op = ADD_RATE_INIT; - else if (old_state == IEEE80211_STA_ASSOC && - new_state == IEEE80211_STA_AUTH) - op = REMOVE; - } - - switch (op) { - case ADD: - ret = iwlagn_mac_sta_add(hw, vif, sta); - if (ret) - break; - /* - * Clear the in-progress flag, the AP station entry was added - * but we'll initialize LQ only when we've associated (which - * would also clear the in-progress flag). This is necessary - * in case we never initialize LQ because association fails. - */ - spin_lock_bh(&priv->sta_lock); - priv->stations[iwl_sta_id(sta)].used &= - ~IWL_STA_UCODE_INPROGRESS; - spin_unlock_bh(&priv->sta_lock); - break; - case REMOVE: - ret = iwlagn_mac_sta_remove(hw, vif, sta); - break; - case ADD_RATE_INIT: - ret = iwlagn_mac_sta_add(hw, vif, sta); - if (ret) - break; - /* Initialize rate scaling */ - IWL_DEBUG_INFO(priv, - "Initializing rate scaling for station %pM\n", - sta->addr); - iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); - ret = 0; - break; - case HT_RATE_INIT: - /* Initialize rate scaling */ - ret = iwl_sta_update_ht(priv, vif_priv->ctx, sta); - if (ret) - break; - IWL_DEBUG_INFO(priv, - "Initializing rate scaling for station %pM\n", - sta->addr); - iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); - ret = 0; - break; - default: - ret = 0; - break; - } - - /* - * mac80211 might WARN if we fail, but due the way we - * (badly) handle hard rfkill, we might fail here - */ - if (iwl_is_rfkill(priv)) - ret = 0; - - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_channel_switch *ch_switch) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_channel *channel = ch_switch->chandef.chan; - struct iwl_ht_config *ht_conf = &priv->current_ht_config; - /* - * MULTI-FIXME - * When we add support for multiple interfaces, we need to - * revisit this. The channel switch command in the device - * only affects the BSS context, but what does that really - * mean? And what if we get a CSA on the second interface? - * This needs a lot of work. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - u16 ch; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - mutex_lock(&priv->mutex); - - if (iwl_is_rfkill(priv)) - goto out; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status) || - test_bit(STATUS_SCANNING, &priv->status) || - test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) - goto out; - - if (!iwl_is_associated_ctx(ctx)) - goto out; - - if (!priv->lib->set_channel_switch) - goto out; - - ch = channel->hw_value; - if (le16_to_cpu(ctx->active.channel) == ch) - goto out; - - priv->current_ht_config.smps = conf->smps_mode; - - /* Configure HT40 channels */ - switch (cfg80211_get_chandef_type(&ch_switch->chandef)) { - case NL80211_CHAN_NO_HT: - case NL80211_CHAN_HT20: - ctx->ht.is_40mhz = false; - ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; - break; - case NL80211_CHAN_HT40MINUS: - ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; - ctx->ht.is_40mhz = true; - break; - case NL80211_CHAN_HT40PLUS: - ctx->ht.extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - ctx->ht.is_40mhz = true; - break; - } - - if ((le16_to_cpu(ctx->staging.channel) != ch)) - ctx->staging.flags = 0; - - iwl_set_rxon_channel(priv, channel, ctx); - iwl_set_rxon_ht(priv, ht_conf); - iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif); - - /* - * at this point, staging_rxon has the - * configuration for channel switch - */ - set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); - priv->switch_channel = cpu_to_le16(ch); - if (priv->lib->set_channel_switch(priv, ch_switch)) { - clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); - priv->switch_channel = 0; - ieee80211_chswitch_done(ctx->vif, false); - } - -out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) -{ - /* - * MULTI-FIXME - * See iwlagn_mac_channel_switch. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (!test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) - return; - - if (ctx->vif) - ieee80211_chswitch_done(ctx->vif, is_success); -} - -static void iwlagn_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - __le32 filter_or = 0, filter_nand = 0; - struct iwl_rxon_context *ctx; - -#define CHK(test, flag) do { \ - if (*total_flags & (test)) \ - filter_or |= (flag); \ - else \ - filter_nand |= (flag); \ - } while (0) - - IWL_DEBUG_MAC80211(priv, "Enter: changed: 0x%x, total: 0x%x\n", - changed_flags, *total_flags); - - CHK(FIF_OTHER_BSS, RXON_FILTER_PROMISC_MSK); - /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ - CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); - CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); - -#undef CHK - - mutex_lock(&priv->mutex); - - for_each_context(priv, ctx) { - ctx->staging.filter_flags &= ~filter_nand; - ctx->staging.filter_flags |= filter_or; - - /* - * Not committing directly because hardware can perform a scan, - * but we'll eventually commit the filter flags change anyway. - */ - } - - mutex_unlock(&priv->mutex); - - /* - * Receiving all multicast frames is always enabled by the - * default flags setup in iwl_connection_init_rx_config() - * since we currently do not support programming multicast - * filters into the device. - */ - *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | - FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; -} - -static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 queues, bool drop) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - u32 scd_queues; - - mutex_lock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { - IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n"); - goto done; - } - if (iwl_is_rfkill(priv)) { - IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n"); - goto done; - } - - scd_queues = BIT(priv->cfg->base_params->num_of_queues) - 1; - scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) | - BIT(IWL_DEFAULT_CMD_QUEUE_NUM)); - - if (drop) { - IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n", - scd_queues); - if (iwlagn_txfifo_flush(priv, scd_queues)) { - IWL_ERR(priv, "flush request fail\n"); - goto done; - } - } - - IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n"); - iwl_trans_wait_tx_queue_empty(priv->trans, scd_queues); -done: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -static void iwlagn_mac_event_callback(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const struct ieee80211_event *event) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - if (event->type != RSSI_EVENT) - return; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist) { - if (event->u.rssi.data == RSSI_EVENT_LOW) - priv->bt_enable_pspoll = true; - else if (event->u.rssi.data == RSSI_EVENT_HIGH) - priv->bt_enable_pspoll = false; - - queue_work(priv->workqueue, &priv->bt_runtime_config); - } else { - IWL_DEBUG_MAC80211(priv, "Advanced BT coex disabled," - "ignoring RSSI callback\n"); - } - - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -static int iwlagn_mac_set_tim(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, bool set) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - queue_work(priv->workqueue, &priv->beacon_update); - - return 0; -} - -static int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - struct iwl_rxon_context *ctx = vif_priv->ctx; - int q; - - if (WARN_ON(!ctx)) - return -EINVAL; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (!iwl_is_ready_rf(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); - return -EIO; - } - - if (queue >= AC_NUM) { - IWL_DEBUG_MAC80211(priv, "leave - queue >= AC_NUM %d\n", queue); - return 0; - } - - q = AC_NUM - 1 - queue; - - mutex_lock(&priv->mutex); - - ctx->qos_data.def_qos_parm.ac[q].cw_min = - cpu_to_le16(params->cw_min); - ctx->qos_data.def_qos_parm.ac[q].cw_max = - cpu_to_le16(params->cw_max); - ctx->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; - ctx->qos_data.def_qos_parm.ac[q].edca_txop = - cpu_to_le16((params->txop * 32)); - - ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0; - - mutex_unlock(&priv->mutex); - - IWL_DEBUG_MAC80211(priv, "leave\n"); - return 0; -} - -static int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - return priv->ibss_manager == IWL_IBSS_MANAGER; -} - -static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx) -{ - iwl_connection_init_rx_config(priv, ctx); - - iwlagn_set_rxon_chain(priv, ctx); - - return iwlagn_commit_rxon(priv, ctx); -} - -static int iwl_setup_interface(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - struct ieee80211_vif *vif = ctx->vif; - int err, ac; - - lockdep_assert_held(&priv->mutex); - - /* - * This variable will be correct only when there's just - * a single context, but all code using it is for hardware - * that supports only one context. - */ - priv->iw_mode = vif->type; - - ctx->is_active = true; - - err = iwl_set_mode(priv, ctx); - if (err) { - if (!ctx->always_active) - ctx->is_active = false; - return err; - } - - if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist && - vif->type == NL80211_IFTYPE_ADHOC) { - /* - * pretend to have high BT traffic as long as we - * are operating in IBSS mode, as this will cause - * the rate scaling etc. to behave as intended. - */ - priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; - } - - /* set up queue mappings */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - vif->hw_queue[ac] = ctx->ac_to_queue[ac]; - - if (vif->type == NL80211_IFTYPE_AP) - vif->cab_queue = ctx->mcast_queue; - else - vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; - - return 0; -} - -static int iwlagn_mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - struct iwl_rxon_context *tmp, *ctx = NULL; - int err; - enum nl80211_iftype viftype = ieee80211_vif_type_p2p(vif); - bool reset = false; - - IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", - viftype, vif->addr); - - mutex_lock(&priv->mutex); - - if (!iwl_is_ready_rf(priv)) { - IWL_WARN(priv, "Try to add interface when device not ready\n"); - err = -EINVAL; - goto out; - } - - for_each_context(priv, tmp) { - u32 possible_modes = - tmp->interface_modes | tmp->exclusive_interface_modes; - - if (tmp->vif) { - /* On reset we need to add the same interface again */ - if (tmp->vif == vif) { - reset = true; - ctx = tmp; - break; - } - - /* check if this busy context is exclusive */ - if (tmp->exclusive_interface_modes & - BIT(tmp->vif->type)) { - err = -EINVAL; - goto out; - } - continue; - } - - if (!(possible_modes & BIT(viftype))) - continue; - - /* have maybe usable context w/o interface */ - ctx = tmp; - break; - } - - if (!ctx) { - err = -EOPNOTSUPP; - goto out; - } - - vif_priv->ctx = ctx; - ctx->vif = vif; - - /* - * In SNIFFER device type, the firmware reports the FCS to - * the host, rather than snipping it off. Unfortunately, - * mac80211 doesn't (yet) provide a per-packet flag for - * this, so that we have to set the hardware flag based - * on the interfaces added. As the monitor interface can - * only be present by itself, and will be removed before - * other interfaces are added, this is safe. - */ - if (vif->type == NL80211_IFTYPE_MONITOR) - ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); - else - __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, priv->hw->flags); - - err = iwl_setup_interface(priv, ctx); - if (!err || reset) - goto out; - - ctx->vif = NULL; - priv->iw_mode = NL80211_IFTYPE_STATION; - out: - mutex_unlock(&priv->mutex); - - IWL_DEBUG_MAC80211(priv, "leave\n"); - return err; -} - -static void iwl_teardown_interface(struct iwl_priv *priv, - struct ieee80211_vif *vif, - bool mode_change) -{ - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - - lockdep_assert_held(&priv->mutex); - - if (priv->scan_vif == vif) { - iwl_scan_cancel_timeout(priv, 200); - iwl_force_scan_end(priv); - } - - if (!mode_change) { - iwl_set_mode(priv, ctx); - if (!ctx->always_active) - ctx->is_active = false; - } - - /* - * When removing the IBSS interface, overwrite the - * BT traffic load with the stored one from the last - * notification, if any. If this is a device that - * doesn't implement this, this has no effect since - * both values are the same and zero. - */ - if (vif->type == NL80211_IFTYPE_ADHOC) - priv->bt_traffic_load = priv->last_bt_traffic_load; -} - -static void iwlagn_mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - mutex_lock(&priv->mutex); - - if (WARN_ON(ctx->vif != vif)) { - struct iwl_rxon_context *tmp; - IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif); - for_each_context(priv, tmp) - IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n", - tmp->ctxid, tmp, tmp->vif); - } - ctx->vif = NULL; - - iwl_teardown_interface(priv, vif, false); - - mutex_unlock(&priv->mutex); - - IWL_DEBUG_MAC80211(priv, "leave\n"); - -} - -static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum nl80211_iftype newtype, bool newp2p) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx, *tmp; - enum nl80211_iftype newviftype = newtype; - u32 interface_modes; - int err; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - newtype = ieee80211_iftype_p2p(newtype, newp2p); - - mutex_lock(&priv->mutex); - - ctx = iwl_rxon_ctx_from_vif(vif); - - /* - * To simplify this code, only support changes on the - * BSS context. The PAN context is usually reassigned - * by creating/removing P2P interfaces anyway. - */ - if (ctx->ctxid != IWL_RXON_CTX_BSS) { - err = -EBUSY; - goto out; - } - - if (!ctx->vif || !iwl_is_ready_rf(priv)) { - /* - * Huh? But wait ... this can maybe happen when - * we're in the middle of a firmware restart! - */ - err = -EBUSY; - goto out; - } - - /* Check if the switch is supported in the same context */ - interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes; - if (!(interface_modes & BIT(newtype))) { - err = -EBUSY; - goto out; - } - - if (ctx->exclusive_interface_modes & BIT(newtype)) { - for_each_context(priv, tmp) { - if (ctx == tmp) - continue; - - if (!tmp->is_active) - continue; - - /* - * The current mode switch would be exclusive, but - * another context is active ... refuse the switch. - */ - err = -EBUSY; - goto out; - } - } - - /* success */ - iwl_teardown_interface(priv, vif, true); - vif->type = newviftype; - vif->p2p = newp2p; - err = iwl_setup_interface(priv, ctx); - WARN_ON(err); - /* - * We've switched internally, but submitting to the - * device may have failed for some reason. Mask this - * error, because otherwise mac80211 will not switch - * (and set the interface type back) and we'll be - * out of sync with it. - */ - err = 0; - - out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return err; -} - -static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct cfg80211_scan_request *req = &hw_req->req; - int ret; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (req->n_channels == 0) - return -EINVAL; - - mutex_lock(&priv->mutex); - - /* - * If an internal scan is in progress, just set - * up the scan_request as per above. - */ - if (priv->scan_type != IWL_SCAN_NORMAL) { - IWL_DEBUG_SCAN(priv, - "SCAN request during internal scan - defer\n"); - priv->scan_request = req; - priv->scan_vif = vif; - ret = 0; - } else { - priv->scan_request = req; - priv->scan_vif = vif; - /* - * mac80211 will only ask for one band at a time - * so using channels[0] here is ok - */ - ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL, - req->channels[0]->band); - if (ret) { - priv->scan_request = NULL; - priv->scan_vif = NULL; - } - } - - IWL_DEBUG_MAC80211(priv, "leave\n"); - - mutex_unlock(&priv->mutex); - - return ret; -} - -static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) -{ - struct iwl_addsta_cmd cmd = { - .mode = STA_CONTROL_MODIFY_MSK, - .station_flags_msk = STA_FLG_PWR_SAVE_MSK, - .sta.sta_id = sta_id, - }; - - iwl_send_add_sta(priv, &cmd, CMD_ASYNC); -} - -static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - int sta_id; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - switch (cmd) { - case STA_NOTIFY_SLEEP: - WARN_ON(!sta_priv->client); - sta_priv->asleep = true; - if (atomic_read(&sta_priv->pending_frames) > 0) - ieee80211_sta_block_awake(hw, sta, true); - break; - case STA_NOTIFY_AWAKE: - WARN_ON(!sta_priv->client); - if (!sta_priv->asleep) - break; - sta_priv->asleep = false; - sta_id = iwl_sta_id(sta); - if (sta_id != IWL_INVALID_STATION) - iwl_sta_modify_ps_wake(priv, sta_id); - break; - default: - break; - } - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -const struct ieee80211_ops iwlagn_hw_ops = { - .tx = iwlagn_mac_tx, - .start = iwlagn_mac_start, - .stop = iwlagn_mac_stop, -#ifdef CONFIG_PM_SLEEP - .suspend = iwlagn_mac_suspend, - .resume = iwlagn_mac_resume, - .set_wakeup = iwlagn_mac_set_wakeup, -#endif - .add_interface = iwlagn_mac_add_interface, - .remove_interface = iwlagn_mac_remove_interface, - .change_interface = iwlagn_mac_change_interface, - .config = iwlagn_mac_config, - .configure_filter = iwlagn_configure_filter, - .set_key = iwlagn_mac_set_key, - .update_tkip_key = iwlagn_mac_update_tkip_key, - .set_rekey_data = iwlagn_mac_set_rekey_data, - .conf_tx = iwlagn_mac_conf_tx, - .bss_info_changed = iwlagn_bss_info_changed, - .ampdu_action = iwlagn_mac_ampdu_action, - .hw_scan = iwlagn_mac_hw_scan, - .sta_notify = iwlagn_mac_sta_notify, - .sta_state = iwlagn_mac_sta_state, - .channel_switch = iwlagn_mac_channel_switch, - .flush = iwlagn_mac_flush, - .tx_last_beacon = iwlagn_mac_tx_last_beacon, - .event_callback = iwlagn_mac_event_callback, - .set_tim = iwlagn_mac_set_tim, -}; - -/* This function both allocates and initializes hw and priv. */ -struct ieee80211_hw *iwl_alloc_all(void) -{ - struct iwl_priv *priv; - struct iwl_op_mode *op_mode; - /* mac80211 allocates memory for this device instance, including - * space for this driver's private structure */ - struct ieee80211_hw *hw; - - hw = ieee80211_alloc_hw(sizeof(struct iwl_priv) + - sizeof(struct iwl_op_mode), &iwlagn_hw_ops); - if (!hw) - goto out; - - op_mode = hw->priv; - priv = IWL_OP_MODE_GET_DVM(op_mode); - priv->hw = hw; - -out: - return hw; -} diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c deleted file mode 100644 index e7616f0ee6e8..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ /dev/null @@ -1,2077 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "iwl-eeprom-read.h" -#include "iwl-eeprom-parse.h" -#include "iwl-io.h" -#include "iwl-trans.h" -#include "iwl-op-mode.h" -#include "iwl-drv.h" -#include "iwl-modparams.h" -#include "iwl-prph.h" - -#include "dev.h" -#include "calib.h" -#include "agn.h" - - -/****************************************************************************** - * - * module boiler plate - * - ******************************************************************************/ - -#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux" -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); -MODULE_LICENSE("GPL"); - -static const struct iwl_op_mode_ops iwl_dvm_ops; - -void iwl_update_chain_flags(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - - for_each_context(priv, ctx) { - iwlagn_set_rxon_chain(priv, ctx); - if (ctx->active.rx_chain != ctx->staging.rx_chain) - iwlagn_commit_rxon(priv, ctx); - } -} - -/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ -static void iwl_set_beacon_tim(struct iwl_priv *priv, - struct iwl_tx_beacon_cmd *tx_beacon_cmd, - u8 *beacon, u32 frame_size) -{ - u16 tim_idx; - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; - - /* - * The index is relative to frame start but we start looking at the - * variable-length part of the beacon. - */ - tim_idx = mgmt->u.beacon.variable - beacon; - - /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ - while ((tim_idx < (frame_size - 2)) && - (beacon[tim_idx] != WLAN_EID_TIM)) - tim_idx += beacon[tim_idx+1] + 2; - - /* If TIM field was found, set variables */ - if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { - tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); - tx_beacon_cmd->tim_size = beacon[tim_idx+1]; - } else - IWL_WARN(priv, "Unable to find TIM Element in beacon\n"); -} - -int iwlagn_send_beacon_cmd(struct iwl_priv *priv) -{ - struct iwl_tx_beacon_cmd *tx_beacon_cmd; - struct iwl_host_cmd cmd = { - .id = REPLY_TX_BEACON, - }; - struct ieee80211_tx_info *info; - u32 frame_size; - u32 rate_flags; - u32 rate; - - /* - * We have to set up the TX command, the TX Beacon command, and the - * beacon contents. - */ - - lockdep_assert_held(&priv->mutex); - - if (!priv->beacon_ctx) { - IWL_ERR(priv, "trying to build beacon w/o beacon context!\n"); - return 0; - } - - if (WARN_ON(!priv->beacon_skb)) - return -EINVAL; - - /* Allocate beacon command */ - if (!priv->beacon_cmd) - priv->beacon_cmd = kzalloc(sizeof(*tx_beacon_cmd), GFP_KERNEL); - tx_beacon_cmd = priv->beacon_cmd; - if (!tx_beacon_cmd) - return -ENOMEM; - - frame_size = priv->beacon_skb->len; - - /* Set up TX command fields */ - tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); - tx_beacon_cmd->tx.sta_id = priv->beacon_ctx->bcast_sta_id; - tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | - TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK; - - /* Set up TX beacon command fields */ - iwl_set_beacon_tim(priv, tx_beacon_cmd, priv->beacon_skb->data, - frame_size); - - /* Set up packet rate and flags */ - info = IEEE80211_SKB_CB(priv->beacon_skb); - - /* - * Let's set up the rate at least somewhat correctly; - * it will currently not actually be used by the uCode, - * it uses the broadcast station's rate instead. - */ - if (info->control.rates[0].idx < 0 || - info->control.rates[0].flags & IEEE80211_TX_RC_MCS) - rate = 0; - else - rate = info->control.rates[0].idx; - - priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, - priv->nvm_data->valid_tx_ant); - rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant); - - /* In mac80211, rates for 5 GHz start at 0 */ - if (info->band == IEEE80211_BAND_5GHZ) - rate += IWL_FIRST_OFDM_RATE; - else if (rate >= IWL_FIRST_CCK_RATE && rate <= IWL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; - - tx_beacon_cmd->tx.rate_n_flags = - iwl_hw_set_rate_n_flags(rate, rate_flags); - - /* Submit command */ - cmd.len[0] = sizeof(*tx_beacon_cmd); - cmd.data[0] = tx_beacon_cmd; - cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - cmd.len[1] = frame_size; - cmd.data[1] = priv->beacon_skb->data; - cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY; - - return iwl_dvm_send_cmd(priv, &cmd); -} - -static void iwl_bg_beacon_update(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, beacon_update); - struct sk_buff *beacon; - - mutex_lock(&priv->mutex); - if (!priv->beacon_ctx) { - IWL_ERR(priv, "updating beacon w/o beacon context!\n"); - goto out; - } - - if (priv->beacon_ctx->vif->type != NL80211_IFTYPE_AP) { - /* - * The ucode will send beacon notifications even in - * IBSS mode, but we don't want to process them. But - * we need to defer the type check to here due to - * requiring locking around the beacon_ctx access. - */ - goto out; - } - - /* Pull updated AP beacon from mac80211. will fail if not in AP mode */ - beacon = ieee80211_beacon_get(priv->hw, priv->beacon_ctx->vif); - if (!beacon) { - IWL_ERR(priv, "update beacon failed -- keeping old\n"); - goto out; - } - - /* new beacon skb is allocated every time; dispose previous.*/ - dev_kfree_skb(priv->beacon_skb); - - priv->beacon_skb = beacon; - - iwlagn_send_beacon_cmd(priv); - out: - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_bt_runtime_config(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, bt_runtime_config); - - mutex_lock(&priv->mutex); - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - goto out; - - /* dont send host command if rf-kill is on */ - if (!iwl_is_ready_rf(priv)) - goto out; - - iwlagn_send_advance_bt_config(priv); -out: - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_bt_full_concurrency(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, bt_full_concurrency); - struct iwl_rxon_context *ctx; - - mutex_lock(&priv->mutex); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - goto out; - - /* dont send host command if rf-kill is on */ - if (!iwl_is_ready_rf(priv)) - goto out; - - IWL_DEBUG_INFO(priv, "BT coex in %s mode\n", - priv->bt_full_concurrent ? - "full concurrency" : "3-wire"); - - /* - * LQ & RXON updated cmds must be sent before BT Config cmd - * to avoid 3-wire collisions - */ - for_each_context(priv, ctx) { - iwlagn_set_rxon_chain(priv, ctx); - iwlagn_commit_rxon(priv, ctx); - } - - iwlagn_send_advance_bt_config(priv); -out: - mutex_unlock(&priv->mutex); -} - -int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) -{ - struct iwl_statistics_cmd statistics_cmd = { - .configuration_flags = - clear ? IWL_STATS_CONF_CLEAR_STATS : 0, - }; - - if (flags & CMD_ASYNC) - return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, - CMD_ASYNC, - sizeof(struct iwl_statistics_cmd), - &statistics_cmd); - else - return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, 0, - sizeof(struct iwl_statistics_cmd), - &statistics_cmd); -} - -/** - * iwl_bg_statistics_periodic - Timer callback to queue statistics - * - * This callback is provided in order to send a statistics request. - * - * This timer function is continually reset to execute within - * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION - * was received. We need to ensure we receive the statistics in order - * to update the temperature used for calibrating the TXPOWER. - */ -static void iwl_bg_statistics_periodic(unsigned long data) -{ - struct iwl_priv *priv = (struct iwl_priv *)data; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - /* dont send host command if rf-kill is on */ - if (!iwl_is_ready_rf(priv)) - return; - - iwl_send_statistics_request(priv, CMD_ASYNC, false); -} - - -static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base, - u32 start_idx, u32 num_events, - u32 capacity, u32 mode) -{ - u32 i; - u32 ptr; /* SRAM byte address of log data */ - u32 ev, time, data; /* event log data */ - unsigned long reg_flags; - - if (mode == 0) - ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32)); - else - ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32)); - - /* Make sure device is powered up for SRAM reads */ - if (!iwl_trans_grab_nic_access(priv->trans, false, ®_flags)) - return; - - /* Set starting address; reads will auto-increment */ - iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, ptr); - - /* - * Refuse to read more than would have fit into the log from - * the current start_idx. This used to happen due to the race - * described below, but now WARN because the code below should - * prevent it from happening here. - */ - if (WARN_ON(num_events > capacity - start_idx)) - num_events = capacity - start_idx; - - /* - * "time" is actually "data" for mode 0 (no timestamp). - * place event id # at far right for easier visual parsing. - */ - for (i = 0; i < num_events; i++) { - ev = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); - time = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); - if (mode == 0) { - trace_iwlwifi_dev_ucode_cont_event( - priv->trans->dev, 0, time, ev); - } else { - data = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); - trace_iwlwifi_dev_ucode_cont_event( - priv->trans->dev, time, data, ev); - } - } - /* Allow device to power down */ - iwl_trans_release_nic_access(priv->trans, ®_flags); -} - -static void iwl_continuous_event_trace(struct iwl_priv *priv) -{ - u32 capacity; /* event log capacity in # entries */ - struct { - u32 capacity; - u32 mode; - u32 wrap_counter; - u32 write_counter; - } __packed read; - u32 base; /* SRAM byte address of event log header */ - u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ - u32 num_wraps; /* # times uCode wrapped to top of log */ - u32 next_entry; /* index of next entry to be written by uCode */ - - base = priv->device_pointers.log_event_table; - if (iwlagn_hw_valid_rtc_data_addr(base)) { - iwl_trans_read_mem_bytes(priv->trans, base, - &read, sizeof(read)); - capacity = read.capacity; - mode = read.mode; - num_wraps = read.wrap_counter; - next_entry = read.write_counter; - } else - return; - - /* - * Unfortunately, the uCode doesn't use temporary variables. - * Therefore, it can happen that we read next_entry == capacity, - * which really means next_entry == 0. - */ - if (unlikely(next_entry == capacity)) - next_entry = 0; - /* - * Additionally, the uCode increases the write pointer before - * the wraps counter, so if the write pointer is smaller than - * the old write pointer (wrap occurred) but we read that no - * wrap occurred, we actually read between the next_entry and - * num_wraps update (this does happen in practice!!) -- take - * that into account by increasing num_wraps. - */ - if (unlikely(next_entry < priv->event_log.next_entry && - num_wraps == priv->event_log.num_wraps)) - num_wraps++; - - if (num_wraps == priv->event_log.num_wraps) { - iwl_print_cont_event_trace( - priv, base, priv->event_log.next_entry, - next_entry - priv->event_log.next_entry, - capacity, mode); - - priv->event_log.non_wraps_count++; - } else { - if (num_wraps - priv->event_log.num_wraps > 1) - priv->event_log.wraps_more_count++; - else - priv->event_log.wraps_once_count++; - - trace_iwlwifi_dev_ucode_wrap_event(priv->trans->dev, - num_wraps - priv->event_log.num_wraps, - next_entry, priv->event_log.next_entry); - - if (next_entry < priv->event_log.next_entry) { - iwl_print_cont_event_trace( - priv, base, priv->event_log.next_entry, - capacity - priv->event_log.next_entry, - capacity, mode); - - iwl_print_cont_event_trace( - priv, base, 0, next_entry, capacity, mode); - } else { - iwl_print_cont_event_trace( - priv, base, next_entry, - capacity - next_entry, - capacity, mode); - - iwl_print_cont_event_trace( - priv, base, 0, next_entry, capacity, mode); - } - } - - priv->event_log.num_wraps = num_wraps; - priv->event_log.next_entry = next_entry; -} - -/** - * iwl_bg_ucode_trace - Timer callback to log ucode event - * - * The timer is continually set to execute every - * UCODE_TRACE_PERIOD milliseconds after the last timer expired - * this function is to perform continuous uCode event logging operation - * if enabled - */ -static void iwl_bg_ucode_trace(unsigned long data) -{ - struct iwl_priv *priv = (struct iwl_priv *)data; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (priv->event_log.ucode_trace) { - iwl_continuous_event_trace(priv); - /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */ - mod_timer(&priv->ucode_trace, - jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD)); - } -} - -static void iwl_bg_tx_flush(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, tx_flush); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - /* do nothing if rf-kill is on */ - if (!iwl_is_ready_rf(priv)) - return; - - IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n"); - iwlagn_dev_txfifo_flush(priv); -} - -/* - * queue/FIFO/AC mapping definitions - */ - -static const u8 iwlagn_bss_ac_to_fifo[] = { - IWL_TX_FIFO_VO, - IWL_TX_FIFO_VI, - IWL_TX_FIFO_BE, - IWL_TX_FIFO_BK, -}; - -static const u8 iwlagn_bss_ac_to_queue[] = { - 0, 1, 2, 3, -}; - -static const u8 iwlagn_pan_ac_to_fifo[] = { - IWL_TX_FIFO_VO_IPAN, - IWL_TX_FIFO_VI_IPAN, - IWL_TX_FIFO_BE_IPAN, - IWL_TX_FIFO_BK_IPAN, -}; - -static const u8 iwlagn_pan_ac_to_queue[] = { - 7, 6, 5, 4, -}; - -static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags) -{ - int i; - - /* - * The default context is always valid, - * the PAN context depends on uCode. - */ - priv->valid_contexts = BIT(IWL_RXON_CTX_BSS); - if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) - priv->valid_contexts |= BIT(IWL_RXON_CTX_PAN); - - for (i = 0; i < NUM_IWL_RXON_CTX; i++) - priv->contexts[i].ctxid = i; - - priv->contexts[IWL_RXON_CTX_BSS].always_active = true; - priv->contexts[IWL_RXON_CTX_BSS].is_active = true; - priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON; - priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING; - priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC; - priv->contexts[IWL_RXON_CTX_BSS].qos_cmd = REPLY_QOS_PARAM; - priv->contexts[IWL_RXON_CTX_BSS].ap_sta_id = IWL_AP_ID; - priv->contexts[IWL_RXON_CTX_BSS].wep_key_cmd = REPLY_WEPKEY; - priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID; - priv->contexts[IWL_RXON_CTX_BSS].exclusive_interface_modes = - BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MONITOR); - priv->contexts[IWL_RXON_CTX_BSS].interface_modes = - BIT(NL80211_IFTYPE_STATION); - priv->contexts[IWL_RXON_CTX_BSS].ap_devtype = RXON_DEV_TYPE_AP; - priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS; - priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS; - priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS; - memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_queue, - iwlagn_bss_ac_to_queue, sizeof(iwlagn_bss_ac_to_queue)); - memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_fifo, - iwlagn_bss_ac_to_fifo, sizeof(iwlagn_bss_ac_to_fifo)); - - priv->contexts[IWL_RXON_CTX_PAN].rxon_cmd = REPLY_WIPAN_RXON; - priv->contexts[IWL_RXON_CTX_PAN].rxon_timing_cmd = - REPLY_WIPAN_RXON_TIMING; - priv->contexts[IWL_RXON_CTX_PAN].rxon_assoc_cmd = - REPLY_WIPAN_RXON_ASSOC; - priv->contexts[IWL_RXON_CTX_PAN].qos_cmd = REPLY_WIPAN_QOS_PARAM; - priv->contexts[IWL_RXON_CTX_PAN].ap_sta_id = IWL_AP_ID_PAN; - priv->contexts[IWL_RXON_CTX_PAN].wep_key_cmd = REPLY_WIPAN_WEPKEY; - priv->contexts[IWL_RXON_CTX_PAN].bcast_sta_id = IWLAGN_PAN_BCAST_ID; - priv->contexts[IWL_RXON_CTX_PAN].station_flags = STA_FLG_PAN_STATION; - priv->contexts[IWL_RXON_CTX_PAN].interface_modes = - BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); - - priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP; - priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA; - priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P; - memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_queue, - iwlagn_pan_ac_to_queue, sizeof(iwlagn_pan_ac_to_queue)); - memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_fifo, - iwlagn_pan_ac_to_fifo, sizeof(iwlagn_pan_ac_to_fifo)); - priv->contexts[IWL_RXON_CTX_PAN].mcast_queue = IWL_IPAN_MCAST_QUEUE; - - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); -} - -static void iwl_rf_kill_ct_config(struct iwl_priv *priv) -{ - struct iwl_ct_kill_config cmd; - struct iwl_ct_kill_throttling_config adv_cmd; - int ret = 0; - - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); - - priv->thermal_throttle.ct_kill_toggle = false; - - if (priv->lib->support_ct_kill_exit) { - adv_cmd.critical_temperature_enter = - cpu_to_le32(priv->hw_params.ct_kill_threshold); - adv_cmd.critical_temperature_exit = - cpu_to_le32(priv->hw_params.ct_kill_exit_threshold); - - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_CT_KILL_CONFIG_CMD, - 0, sizeof(adv_cmd), &adv_cmd); - if (ret) - IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); - else - IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " - "succeeded, critical temperature enter is %d," - "exit is %d\n", - priv->hw_params.ct_kill_threshold, - priv->hw_params.ct_kill_exit_threshold); - } else { - cmd.critical_temperature_R = - cpu_to_le32(priv->hw_params.ct_kill_threshold); - - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_CT_KILL_CONFIG_CMD, - 0, sizeof(cmd), &cmd); - if (ret) - IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); - else - IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " - "succeeded, " - "critical temperature is %d\n", - priv->hw_params.ct_kill_threshold); - } -} - -static int iwlagn_send_calib_cfg_rt(struct iwl_priv *priv, u32 cfg) -{ - struct iwl_calib_cfg_cmd calib_cfg_cmd; - struct iwl_host_cmd cmd = { - .id = CALIBRATION_CFG_CMD, - .len = { sizeof(struct iwl_calib_cfg_cmd), }, - .data = { &calib_cfg_cmd, }, - }; - - memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd)); - calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_RT_CFG_ALL; - calib_cfg_cmd.ucd_calib_cfg.once.start = cpu_to_le32(cfg); - - return iwl_dvm_send_cmd(priv, &cmd); -} - - -static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant) -{ - struct iwl_tx_ant_config_cmd tx_ant_cmd = { - .valid = cpu_to_le32(valid_tx_ant), - }; - - if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) { - IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant); - return iwl_dvm_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, 0, - sizeof(struct iwl_tx_ant_config_cmd), - &tx_ant_cmd); - } else { - IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n"); - return -EOPNOTSUPP; - } -} - -static void iwl_send_bt_config(struct iwl_priv *priv) -{ - struct iwl_bt_cmd bt_cmd = { - .lead_time = BT_LEAD_TIME_DEF, - .max_kill = BT_MAX_KILL_DEF, - .kill_ack_mask = 0, - .kill_cts_mask = 0, - }; - - if (!iwlwifi_mod_params.bt_coex_active) - bt_cmd.flags = BT_COEX_DISABLE; - else - bt_cmd.flags = BT_COEX_ENABLE; - - priv->bt_enable_flag = bt_cmd.flags; - IWL_DEBUG_INFO(priv, "BT coex %s\n", - (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); - - if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - 0, sizeof(struct iwl_bt_cmd), &bt_cmd)) - IWL_ERR(priv, "failed to send BT Coex Config\n"); -} - -/** - * iwl_alive_start - called after REPLY_ALIVE notification received - * from protocol/runtime uCode (initialization uCode's - * Alive gets handled by iwl_init_alive_start()). - */ -int iwl_alive_start(struct iwl_priv *priv) -{ - int ret = 0; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - - IWL_DEBUG_INFO(priv, "Runtime Alive received.\n"); - - /* After the ALIVE response, we can send host commands to the uCode */ - set_bit(STATUS_ALIVE, &priv->status); - - if (iwl_is_rfkill(priv)) - return -ERFKILL; - - if (priv->event_log.ucode_trace) { - /* start collecting data now */ - mod_timer(&priv->ucode_trace, jiffies); - } - - /* download priority table before any calibration request */ - if (priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist) { - /* Configure Bluetooth device coexistence support */ - if (priv->lib->bt_params->bt_sco_disable) - priv->bt_enable_pspoll = false; - else - priv->bt_enable_pspoll = true; - - priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; - priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; - priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; - iwlagn_send_advance_bt_config(priv); - priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS; - priv->cur_rssi_ctx = NULL; - - iwl_send_prio_tbl(priv); - - /* FIXME: w/a to force change uCode BT state machine */ - ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; - ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; - } else if (priv->lib->bt_params) { - /* - * default is 2-wire BT coexexistence support - */ - iwl_send_bt_config(priv); - } - - /* - * Perform runtime calibrations, including DC calibration. - */ - iwlagn_send_calib_cfg_rt(priv, IWL_CALIB_CFG_DC_IDX); - - ieee80211_wake_queues(priv->hw); - - /* Configure Tx antenna selection based on H/W config */ - iwlagn_send_tx_ant_config(priv, priv->nvm_data->valid_tx_ant); - - if (iwl_is_associated_ctx(ctx) && !priv->wowlan) { - struct iwl_rxon_cmd *active_rxon = - (struct iwl_rxon_cmd *)&ctx->active; - /* apply any changes in staging */ - ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - } else { - struct iwl_rxon_context *tmp; - /* Initialize our rx_config data */ - for_each_context(priv, tmp) - iwl_connection_init_rx_config(priv, tmp); - - iwlagn_set_rxon_chain(priv, ctx); - } - - if (!priv->wowlan) { - /* WoWLAN ucode will not reply in the same way, skip it */ - iwl_reset_run_time_calib(priv); - } - - set_bit(STATUS_READY, &priv->status); - - /* Configure the adapter for unassociated operation */ - ret = iwlagn_commit_rxon(priv, ctx); - if (ret) - return ret; - - /* At this point, the NIC is initialized and operational */ - iwl_rf_kill_ct_config(priv); - - IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n"); - - return iwl_power_update_mode(priv, true); -} - -/** - * iwl_clear_driver_stations - clear knowledge of all stations from driver - * @priv: iwl priv struct - * - * This is called during iwl_down() to make sure that in the case - * we're coming there from a hardware restart mac80211 will be - * able to reconfigure stations -- if we're getting there in the - * normal down flow then the stations will already be cleared. - */ -static void iwl_clear_driver_stations(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - - spin_lock_bh(&priv->sta_lock); - memset(priv->stations, 0, sizeof(priv->stations)); - priv->num_stations = 0; - - priv->ucode_key_table = 0; - - for_each_context(priv, ctx) { - /* - * Remove all key information that is not stored as part - * of station information since mac80211 may not have had - * a chance to remove all the keys. When device is - * reconfigured by mac80211 after an error all keys will - * be reconfigured. - */ - memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys)); - ctx->key_mapping_keys = 0; - } - - spin_unlock_bh(&priv->sta_lock); -} - -void iwl_down(struct iwl_priv *priv) -{ - int exit_pending; - - IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n"); - - lockdep_assert_held(&priv->mutex); - - iwl_scan_cancel_timeout(priv, 200); - - exit_pending = - test_and_set_bit(STATUS_EXIT_PENDING, &priv->status); - - iwl_clear_ucode_stations(priv, NULL); - iwl_dealloc_bcast_stations(priv); - iwl_clear_driver_stations(priv); - - /* reset BT coex data */ - priv->bt_status = 0; - priv->cur_rssi_ctx = NULL; - priv->bt_is_sco = 0; - if (priv->lib->bt_params) - priv->bt_traffic_load = - priv->lib->bt_params->bt_init_traffic_load; - else - priv->bt_traffic_load = 0; - priv->bt_full_concurrent = false; - priv->bt_ci_compliance = 0; - - /* Wipe out the EXIT_PENDING status bit if we are not actually - * exiting the module */ - if (!exit_pending) - clear_bit(STATUS_EXIT_PENDING, &priv->status); - - if (priv->mac80211_registered) - ieee80211_stop_queues(priv->hw); - - priv->ucode_loaded = false; - iwl_trans_stop_device(priv->trans); - - /* Set num_aux_in_flight must be done after the transport is stopped */ - atomic_set(&priv->num_aux_in_flight, 0); - - /* Clear out all status bits but a few that are stable across reset */ - priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) << - STATUS_RF_KILL_HW | - test_bit(STATUS_FW_ERROR, &priv->status) << - STATUS_FW_ERROR | - test_bit(STATUS_EXIT_PENDING, &priv->status) << - STATUS_EXIT_PENDING; - - dev_kfree_skb(priv->beacon_skb); - priv->beacon_skb = NULL; -} - -/***************************************************************************** - * - * Workqueue callbacks - * - *****************************************************************************/ - -static void iwl_bg_run_time_calib_work(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, - run_time_calib_work); - - mutex_lock(&priv->mutex); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status) || - test_bit(STATUS_SCANNING, &priv->status)) { - mutex_unlock(&priv->mutex); - return; - } - - if (priv->start_calib) { - iwl_chain_noise_calibration(priv); - iwl_sensitivity_calibration(priv); - } - - mutex_unlock(&priv->mutex); -} - -void iwlagn_prepare_restart(struct iwl_priv *priv) -{ - bool bt_full_concurrent; - u8 bt_ci_compliance; - u8 bt_load; - u8 bt_status; - bool bt_is_sco; - int i; - - lockdep_assert_held(&priv->mutex); - - priv->is_open = 0; - - /* - * __iwl_down() will clear the BT status variables, - * which is correct, but when we restart we really - * want to keep them so restore them afterwards. - * - * The restart process will later pick them up and - * re-configure the hw when we reconfigure the BT - * command. - */ - bt_full_concurrent = priv->bt_full_concurrent; - bt_ci_compliance = priv->bt_ci_compliance; - bt_load = priv->bt_traffic_load; - bt_status = priv->bt_status; - bt_is_sco = priv->bt_is_sco; - - iwl_down(priv); - - priv->bt_full_concurrent = bt_full_concurrent; - priv->bt_ci_compliance = bt_ci_compliance; - priv->bt_traffic_load = bt_load; - priv->bt_status = bt_status; - priv->bt_is_sco = bt_is_sco; - - /* reset aggregation queues */ - for (i = IWLAGN_FIRST_AMPDU_QUEUE; i < IWL_MAX_HW_QUEUES; i++) - priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; - /* and stop counts */ - for (i = 0; i < IWL_MAX_HW_QUEUES; i++) - atomic_set(&priv->queue_stop_count[i], 0); - - memset(priv->agg_q_alloc, 0, sizeof(priv->agg_q_alloc)); -} - -static void iwl_bg_restart(struct work_struct *data) -{ - struct iwl_priv *priv = container_of(data, struct iwl_priv, restart); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) { - mutex_lock(&priv->mutex); - iwlagn_prepare_restart(priv); - mutex_unlock(&priv->mutex); - iwl_cancel_deferred_work(priv); - if (priv->mac80211_registered) - ieee80211_restart_hw(priv->hw); - else - IWL_ERR(priv, - "Cannot request restart before registrating with mac80211\n"); - } else { - WARN_ON(1); - } -} - -/***************************************************************************** - * - * driver setup and teardown - * - *****************************************************************************/ - -static void iwl_setup_deferred_work(struct iwl_priv *priv) -{ - priv->workqueue = create_singlethread_workqueue(DRV_NAME); - - INIT_WORK(&priv->restart, iwl_bg_restart); - INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update); - INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work); - INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush); - INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency); - INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config); - - iwl_setup_scan_deferred_work(priv); - - if (priv->lib->bt_params) - iwlagn_bt_setup_deferred_work(priv); - - setup_timer(&priv->statistics_periodic, iwl_bg_statistics_periodic, - (unsigned long)priv); - - setup_timer(&priv->ucode_trace, iwl_bg_ucode_trace, - (unsigned long)priv); -} - -void iwl_cancel_deferred_work(struct iwl_priv *priv) -{ - if (priv->lib->bt_params) - iwlagn_bt_cancel_deferred_work(priv); - - cancel_work_sync(&priv->run_time_calib_work); - cancel_work_sync(&priv->beacon_update); - - iwl_cancel_scan_deferred_work(priv); - - cancel_work_sync(&priv->bt_full_concurrency); - cancel_work_sync(&priv->bt_runtime_config); - - del_timer_sync(&priv->statistics_periodic); - del_timer_sync(&priv->ucode_trace); -} - -static int iwl_init_drv(struct iwl_priv *priv) -{ - spin_lock_init(&priv->sta_lock); - - mutex_init(&priv->mutex); - - INIT_LIST_HEAD(&priv->calib_results); - - priv->band = IEEE80211_BAND_2GHZ; - - priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold; - - priv->iw_mode = NL80211_IFTYPE_STATION; - priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; - priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; - priv->agg_tids_count = 0; - - priv->rx_statistics_jiffies = jiffies; - - /* Choose which receivers/antennas to use */ - iwlagn_set_rxon_chain(priv, &priv->contexts[IWL_RXON_CTX_BSS]); - - iwl_init_scan_params(priv); - - /* init bt coex */ - if (priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist) { - priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; - priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; - priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; - priv->bt_on_thresh = BT_ON_THRESHOLD_DEF; - priv->bt_duration = BT_DURATION_LIMIT_DEF; - priv->dynamic_frag_thresh = BT_FRAG_THRESHOLD_DEF; - } - - return 0; -} - -static void iwl_uninit_drv(struct iwl_priv *priv) -{ - kfree(priv->scan_cmd); - kfree(priv->beacon_cmd); - kfree(rcu_dereference_raw(priv->noa_data)); - iwl_calib_free_results(priv); -#ifdef CONFIG_IWLWIFI_DEBUGFS - kfree(priv->wowlan_sram); -#endif -} - -static void iwl_set_hw_params(struct iwl_priv *priv) -{ - if (priv->cfg->ht_params) - priv->hw_params.use_rts_for_aggregation = - priv->cfg->ht_params->use_rts_for_aggregation; - - /* Device-specific setup */ - priv->lib->set_hw_params(priv); -} - - - -/* show what optional capabilities we have */ -static void iwl_option_config(struct iwl_priv *priv) -{ -#ifdef CONFIG_IWLWIFI_DEBUG - IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG enabled\n"); -#else - IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG disabled\n"); -#endif - -#ifdef CONFIG_IWLWIFI_DEBUGFS - IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS enabled\n"); -#else - IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS disabled\n"); -#endif - -#ifdef CONFIG_IWLWIFI_DEVICE_TRACING - IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING enabled\n"); -#else - IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n"); -#endif -} - -static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) -{ - struct iwl_nvm_data *data = priv->nvm_data; - - if (data->sku_cap_11n_enable && - !priv->cfg->ht_params) { - IWL_ERR(priv, "Invalid 11n configuration\n"); - return -EINVAL; - } - - if (!data->sku_cap_11n_enable && !data->sku_cap_band_24GHz_enable && - !data->sku_cap_band_52GHz_enable) { - IWL_ERR(priv, "Invalid device sku\n"); - return -EINVAL; - } - - IWL_DEBUG_INFO(priv, - "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n", - data->sku_cap_band_24GHz_enable ? "" : "NOT", "enabled", - data->sku_cap_band_52GHz_enable ? "" : "NOT", "enabled", - data->sku_cap_11n_enable ? "" : "NOT", "enabled"); - - priv->hw_params.tx_chains_num = - num_of_ant(data->valid_tx_ant); - if (priv->cfg->rx_with_siso_diversity) - priv->hw_params.rx_chains_num = 1; - else - priv->hw_params.rx_chains_num = - num_of_ant(data->valid_rx_ant); - - IWL_DEBUG_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", - data->valid_tx_ant, - data->valid_rx_ant); - - return 0; -} - -static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, - const struct iwl_cfg *cfg, - const struct iwl_fw *fw, - struct dentry *dbgfs_dir) -{ - struct iwl_priv *priv; - struct ieee80211_hw *hw; - struct iwl_op_mode *op_mode; - u16 num_mac; - u32 ucode_flags; - struct iwl_trans_config trans_cfg = {}; - static const u8 no_reclaim_cmds[] = { - REPLY_RX_PHY_CMD, - REPLY_RX_MPDU_CMD, - REPLY_COMPRESSED_BA, - STATISTICS_NOTIFICATION, - REPLY_TX, - }; - int i; - - /************************ - * 1. Allocating HW data - ************************/ - hw = iwl_alloc_all(); - if (!hw) { - pr_err("%s: Cannot allocate network device\n", cfg->name); - goto out; - } - - op_mode = hw->priv; - op_mode->ops = &iwl_dvm_ops; - priv = IWL_OP_MODE_GET_DVM(op_mode); - priv->trans = trans; - priv->dev = trans->dev; - priv->cfg = cfg; - priv->fw = fw; - - switch (priv->cfg->device_family) { - case IWL_DEVICE_FAMILY_1000: - case IWL_DEVICE_FAMILY_100: - priv->lib = &iwl_dvm_1000_cfg; - break; - case IWL_DEVICE_FAMILY_2000: - priv->lib = &iwl_dvm_2000_cfg; - break; - case IWL_DEVICE_FAMILY_105: - priv->lib = &iwl_dvm_105_cfg; - break; - case IWL_DEVICE_FAMILY_2030: - case IWL_DEVICE_FAMILY_135: - priv->lib = &iwl_dvm_2030_cfg; - break; - case IWL_DEVICE_FAMILY_5000: - priv->lib = &iwl_dvm_5000_cfg; - break; - case IWL_DEVICE_FAMILY_5150: - priv->lib = &iwl_dvm_5150_cfg; - break; - case IWL_DEVICE_FAMILY_6000: - case IWL_DEVICE_FAMILY_6000i: - priv->lib = &iwl_dvm_6000_cfg; - break; - case IWL_DEVICE_FAMILY_6005: - priv->lib = &iwl_dvm_6005_cfg; - break; - case IWL_DEVICE_FAMILY_6050: - case IWL_DEVICE_FAMILY_6150: - priv->lib = &iwl_dvm_6050_cfg; - break; - case IWL_DEVICE_FAMILY_6030: - priv->lib = &iwl_dvm_6030_cfg; - break; - default: - break; - } - - if (WARN_ON(!priv->lib)) - goto out_free_hw; - - /* - * Populate the state variables that the transport layer needs - * to know about. - */ - trans_cfg.op_mode = op_mode; - trans_cfg.no_reclaim_cmds = no_reclaim_cmds; - trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); - trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; - trans_cfg.cmd_q_wdg_timeout = IWL_WATCHDOG_DISABLED; - - trans_cfg.command_names = iwl_dvm_cmd_strings; - trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM; - - WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE < - priv->cfg->base_params->num_of_queues); - - ucode_flags = fw->ucode_capa.flags; - - if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) { - priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN; - trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM; - } else { - priv->sta_key_max_num = STA_KEY_MAX_NUM; - trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; - } - - /* Configure transport layer */ - iwl_trans_configure(priv->trans, &trans_cfg); - - trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; - trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); - - /* At this point both hw and priv are allocated. */ - - SET_IEEE80211_DEV(priv->hw, priv->trans->dev); - - iwl_option_config(priv); - - IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n"); - - /* is antenna coupling more than 35dB ? */ - priv->bt_ant_couple_ok = - (iwlwifi_mod_params.ant_coupling > - IWL_BT_ANTENNA_COUPLING_THRESHOLD) ? - true : false; - - /* bt channel inhibition enabled*/ - priv->bt_ch_announce = true; - IWL_DEBUG_INFO(priv, "BT channel inhibition is %s\n", - (priv->bt_ch_announce) ? "On" : "Off"); - - /* these spin locks will be used in apm_ops.init and EEPROM access - * we should init now - */ - spin_lock_init(&priv->statistics.lock); - - /*********************** - * 2. Read REV register - ***********************/ - IWL_INFO(priv, "Detected %s, REV=0x%X\n", - priv->cfg->name, priv->trans->hw_rev); - - if (iwl_trans_start_hw(priv->trans)) - goto out_free_hw; - - /* Read the EEPROM */ - if (iwl_read_eeprom(priv->trans, &priv->eeprom_blob, - &priv->eeprom_blob_size)) { - IWL_ERR(priv, "Unable to init EEPROM\n"); - goto out_free_hw; - } - - /* Reset chip to save power until we load uCode during "up". */ - iwl_trans_stop_device(priv->trans); - - priv->nvm_data = iwl_parse_eeprom_data(priv->trans->dev, priv->cfg, - priv->eeprom_blob, - priv->eeprom_blob_size); - if (!priv->nvm_data) - goto out_free_eeprom_blob; - - if (iwl_nvm_check_version(priv->nvm_data, priv->trans)) - goto out_free_eeprom; - - if (iwl_eeprom_init_hw_params(priv)) - goto out_free_eeprom; - - /* extract MAC Address */ - memcpy(priv->addresses[0].addr, priv->nvm_data->hw_addr, ETH_ALEN); - IWL_DEBUG_INFO(priv, "MAC address: %pM\n", priv->addresses[0].addr); - priv->hw->wiphy->addresses = priv->addresses; - priv->hw->wiphy->n_addresses = 1; - num_mac = priv->nvm_data->n_hw_addrs; - if (num_mac > 1) { - memcpy(priv->addresses[1].addr, priv->addresses[0].addr, - ETH_ALEN); - priv->addresses[1].addr[5]++; - priv->hw->wiphy->n_addresses++; - } - - /************************ - * 4. Setup HW constants - ************************/ - iwl_set_hw_params(priv); - - if (!(priv->nvm_data->sku_cap_ipan_enable)) { - IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN\n"); - ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN; - /* - * if not PAN, then don't support P2P -- might be a uCode - * packaging bug or due to the eeprom check above - */ - priv->sta_key_max_num = STA_KEY_MAX_NUM; - trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; - - /* Configure transport layer again*/ - iwl_trans_configure(priv->trans, &trans_cfg); - } - - /******************* - * 5. Setup priv - *******************/ - for (i = 0; i < IWL_MAX_HW_QUEUES; i++) { - priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; - if (i < IWLAGN_FIRST_AMPDU_QUEUE && - i != IWL_DEFAULT_CMD_QUEUE_NUM && - i != IWL_IPAN_CMD_QUEUE_NUM) - priv->queue_to_mac80211[i] = i; - atomic_set(&priv->queue_stop_count[i], 0); - } - - if (iwl_init_drv(priv)) - goto out_free_eeprom; - - /* At this point both hw and priv are initialized. */ - - /******************** - * 6. Setup services - ********************/ - iwl_setup_deferred_work(priv); - iwl_setup_rx_handlers(priv); - - iwl_power_initialize(priv); - iwl_tt_initialize(priv); - - snprintf(priv->hw->wiphy->fw_version, - sizeof(priv->hw->wiphy->fw_version), - "%s", fw->fw_version); - - priv->new_scan_threshold_behaviour = - !!(ucode_flags & IWL_UCODE_TLV_FLAGS_NEWSCAN); - - priv->phy_calib_chain_noise_reset_cmd = - fw->ucode_capa.standard_phy_calibration_size; - priv->phy_calib_chain_noise_gain_cmd = - fw->ucode_capa.standard_phy_calibration_size + 1; - - /* initialize all valid contexts */ - iwl_init_context(priv, ucode_flags); - - /************************************************** - * This is still part of probe() in a sense... - * - * 7. Setup and register with mac80211 and debugfs - **************************************************/ - if (iwlagn_mac_setup_register(priv, &fw->ucode_capa)) - goto out_destroy_workqueue; - - if (iwl_dbgfs_register(priv, dbgfs_dir)) - goto out_mac80211_unregister; - - return op_mode; - -out_mac80211_unregister: - iwlagn_mac_unregister(priv); -out_destroy_workqueue: - iwl_tt_exit(priv); - iwl_cancel_deferred_work(priv); - destroy_workqueue(priv->workqueue); - priv->workqueue = NULL; - iwl_uninit_drv(priv); -out_free_eeprom_blob: - kfree(priv->eeprom_blob); -out_free_eeprom: - iwl_free_nvm_data(priv->nvm_data); -out_free_hw: - ieee80211_free_hw(priv->hw); -out: - op_mode = NULL; - return op_mode; -} - -static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n"); - - iwlagn_mac_unregister(priv); - - iwl_tt_exit(priv); - - kfree(priv->eeprom_blob); - iwl_free_nvm_data(priv->nvm_data); - - /*netif_stop_queue(dev); */ - flush_workqueue(priv->workqueue); - - /* ieee80211_unregister_hw calls iwlagn_mac_stop, which flushes - * priv->workqueue... so we can't take down the workqueue - * until now... */ - destroy_workqueue(priv->workqueue); - priv->workqueue = NULL; - - iwl_uninit_drv(priv); - - dev_kfree_skb(priv->beacon_skb); - - iwl_trans_op_mode_leave(priv->trans); - ieee80211_free_hw(priv->hw); -} - -static const char * const desc_lookup_text[] = { - "OK", - "FAIL", - "BAD_PARAM", - "BAD_CHECKSUM", - "NMI_INTERRUPT_WDG", - "SYSASSERT", - "FATAL_ERROR", - "BAD_COMMAND", - "HW_ERROR_TUNE_LOCK", - "HW_ERROR_TEMPERATURE", - "ILLEGAL_CHAN_FREQ", - "VCC_NOT_STABLE", - "FH_ERROR", - "NMI_INTERRUPT_HOST", - "NMI_INTERRUPT_ACTION_PT", - "NMI_INTERRUPT_UNKNOWN", - "UCODE_VERSION_MISMATCH", - "HW_ERROR_ABS_LOCK", - "HW_ERROR_CAL_LOCK_FAIL", - "NMI_INTERRUPT_INST_ACTION_PT", - "NMI_INTERRUPT_DATA_ACTION_PT", - "NMI_TRM_HW_ER", - "NMI_INTERRUPT_TRM", - "NMI_INTERRUPT_BREAK_POINT", - "DEBUG_0", - "DEBUG_1", - "DEBUG_2", - "DEBUG_3", -}; - -static struct { char *name; u8 num; } advanced_lookup[] = { - { "NMI_INTERRUPT_WDG", 0x34 }, - { "SYSASSERT", 0x35 }, - { "UCODE_VERSION_MISMATCH", 0x37 }, - { "BAD_COMMAND", 0x38 }, - { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, - { "FATAL_ERROR", 0x3D }, - { "NMI_TRM_HW_ERR", 0x46 }, - { "NMI_INTERRUPT_TRM", 0x4C }, - { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, - { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, - { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, - { "NMI_INTERRUPT_HOST", 0x66 }, - { "NMI_INTERRUPT_ACTION_PT", 0x7C }, - { "NMI_INTERRUPT_UNKNOWN", 0x84 }, - { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, - { "ADVANCED_SYSASSERT", 0 }, -}; - -static const char *desc_lookup(u32 num) -{ - int i; - int max = ARRAY_SIZE(desc_lookup_text); - - if (num < max) - return desc_lookup_text[num]; - - max = ARRAY_SIZE(advanced_lookup) - 1; - for (i = 0; i < max; i++) { - if (advanced_lookup[i].num == num) - break; - } - return advanced_lookup[i].name; -} - -#define ERROR_START_OFFSET (1 * sizeof(u32)) -#define ERROR_ELEM_SIZE (7 * sizeof(u32)) - -static void iwl_dump_nic_error_log(struct iwl_priv *priv) -{ - struct iwl_trans *trans = priv->trans; - u32 base; - struct iwl_error_event_table table; - - base = priv->device_pointers.error_event_table; - if (priv->cur_ucode == IWL_UCODE_INIT) { - if (!base) - base = priv->fw->init_errlog_ptr; - } else { - if (!base) - base = priv->fw->inst_errlog_ptr; - } - - if (!iwlagn_hw_valid_rtc_data_addr(base)) { - IWL_ERR(priv, - "Not valid error log pointer 0x%08X for %s uCode\n", - base, - (priv->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); - return; - } - - /*TODO: Update dbgfs with ISR error stats obtained below */ - iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); - - if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { - IWL_ERR(trans, "Start IWL Error Log Dump:\n"); - IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", - priv->status, table.valid); - } - - trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, - table.data1, table.data2, table.line, - table.blink1, table.blink2, table.ilink1, - table.ilink2, table.bcon_time, table.gp1, - table.gp2, table.gp3, table.ucode_ver, - table.hw_ver, 0, table.brd_ver); - IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id, - desc_lookup(table.error_id)); - IWL_ERR(priv, "0x%08X | uPc\n", table.pc); - IWL_ERR(priv, "0x%08X | branchlink1\n", table.blink1); - IWL_ERR(priv, "0x%08X | branchlink2\n", table.blink2); - IWL_ERR(priv, "0x%08X | interruptlink1\n", table.ilink1); - IWL_ERR(priv, "0x%08X | interruptlink2\n", table.ilink2); - IWL_ERR(priv, "0x%08X | data1\n", table.data1); - IWL_ERR(priv, "0x%08X | data2\n", table.data2); - IWL_ERR(priv, "0x%08X | line\n", table.line); - IWL_ERR(priv, "0x%08X | beacon time\n", table.bcon_time); - IWL_ERR(priv, "0x%08X | tsf low\n", table.tsf_low); - IWL_ERR(priv, "0x%08X | tsf hi\n", table.tsf_hi); - IWL_ERR(priv, "0x%08X | time gp1\n", table.gp1); - IWL_ERR(priv, "0x%08X | time gp2\n", table.gp2); - IWL_ERR(priv, "0x%08X | time gp3\n", table.gp3); - IWL_ERR(priv, "0x%08X | uCode version\n", table.ucode_ver); - IWL_ERR(priv, "0x%08X | hw version\n", table.hw_ver); - IWL_ERR(priv, "0x%08X | board version\n", table.brd_ver); - IWL_ERR(priv, "0x%08X | hcmd\n", table.hcmd); - IWL_ERR(priv, "0x%08X | isr0\n", table.isr0); - IWL_ERR(priv, "0x%08X | isr1\n", table.isr1); - IWL_ERR(priv, "0x%08X | isr2\n", table.isr2); - IWL_ERR(priv, "0x%08X | isr3\n", table.isr3); - IWL_ERR(priv, "0x%08X | isr4\n", table.isr4); - IWL_ERR(priv, "0x%08X | isr_pref\n", table.isr_pref); - IWL_ERR(priv, "0x%08X | wait_event\n", table.wait_event); - IWL_ERR(priv, "0x%08X | l2p_control\n", table.l2p_control); - IWL_ERR(priv, "0x%08X | l2p_duration\n", table.l2p_duration); - IWL_ERR(priv, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); - IWL_ERR(priv, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); - IWL_ERR(priv, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); - IWL_ERR(priv, "0x%08X | timestamp\n", table.u_timestamp); - IWL_ERR(priv, "0x%08X | flow_handler\n", table.flow_handler); -} - -#define EVENT_START_OFFSET (4 * sizeof(u32)) - -/** - * iwl_print_event_log - Dump error event log to syslog - * - */ -static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, - u32 num_events, u32 mode, - int pos, char **buf, size_t bufsz) -{ - u32 i; - u32 base; /* SRAM byte address of event log header */ - u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ - u32 ptr; /* SRAM byte address of log data */ - u32 ev, time, data; /* event log data */ - unsigned long reg_flags; - - struct iwl_trans *trans = priv->trans; - - if (num_events == 0) - return pos; - - base = priv->device_pointers.log_event_table; - if (priv->cur_ucode == IWL_UCODE_INIT) { - if (!base) - base = priv->fw->init_evtlog_ptr; - } else { - if (!base) - base = priv->fw->inst_evtlog_ptr; - } - - if (mode == 0) - event_size = 2 * sizeof(u32); - else - event_size = 3 * sizeof(u32); - - ptr = base + EVENT_START_OFFSET + (start_idx * event_size); - - /* Make sure device is powered up for SRAM reads */ - if (!iwl_trans_grab_nic_access(trans, false, ®_flags)) - return pos; - - /* Set starting address; reads will auto-increment */ - iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr); - - /* "time" is actually "data" for mode 0 (no timestamp). - * place event id # at far right for easier visual parsing. */ - for (i = 0; i < num_events; i++) { - ev = iwl_read32(trans, HBUS_TARG_MEM_RDAT); - time = iwl_read32(trans, HBUS_TARG_MEM_RDAT); - if (mode == 0) { - /* data, ev */ - if (bufsz) { - pos += scnprintf(*buf + pos, bufsz - pos, - "EVT_LOG:0x%08x:%04u\n", - time, ev); - } else { - trace_iwlwifi_dev_ucode_event(trans->dev, 0, - time, ev); - IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", - time, ev); - } - } else { - data = iwl_read32(trans, HBUS_TARG_MEM_RDAT); - if (bufsz) { - pos += scnprintf(*buf + pos, bufsz - pos, - "EVT_LOGT:%010u:0x%08x:%04u\n", - time, data, ev); - } else { - IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", - time, data, ev); - trace_iwlwifi_dev_ucode_event(trans->dev, time, - data, ev); - } - } - } - - /* Allow device to power down */ - iwl_trans_release_nic_access(trans, ®_flags); - return pos; -} - -/** - * iwl_print_last_event_logs - Dump the newest # of event log to syslog - */ -static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity, - u32 num_wraps, u32 next_entry, - u32 size, u32 mode, - int pos, char **buf, size_t bufsz) -{ - /* - * display the newest DEFAULT_LOG_ENTRIES entries - * i.e the entries just before the next ont that uCode would fill. - */ - if (num_wraps) { - if (next_entry < size) { - pos = iwl_print_event_log(priv, - capacity - (size - next_entry), - size - next_entry, mode, - pos, buf, bufsz); - pos = iwl_print_event_log(priv, 0, - next_entry, mode, - pos, buf, bufsz); - } else - pos = iwl_print_event_log(priv, next_entry - size, - size, mode, pos, buf, bufsz); - } else { - if (next_entry < size) { - pos = iwl_print_event_log(priv, 0, next_entry, - mode, pos, buf, bufsz); - } else { - pos = iwl_print_event_log(priv, next_entry - size, - size, mode, pos, buf, bufsz); - } - } - return pos; -} - -#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20) - -int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, - char **buf) -{ - u32 base; /* SRAM byte address of event log header */ - u32 capacity; /* event log capacity in # entries */ - u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ - u32 num_wraps; /* # times uCode wrapped to top of log */ - u32 next_entry; /* index of next entry to be written by uCode */ - u32 size; /* # entries that we'll print */ - u32 logsize; - int pos = 0; - size_t bufsz = 0; - struct iwl_trans *trans = priv->trans; - - base = priv->device_pointers.log_event_table; - if (priv->cur_ucode == IWL_UCODE_INIT) { - logsize = priv->fw->init_evtlog_size; - if (!base) - base = priv->fw->init_evtlog_ptr; - } else { - logsize = priv->fw->inst_evtlog_size; - if (!base) - base = priv->fw->inst_evtlog_ptr; - } - - if (!iwlagn_hw_valid_rtc_data_addr(base)) { - IWL_ERR(priv, - "Invalid event log pointer 0x%08X for %s uCode\n", - base, - (priv->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); - return -EINVAL; - } - - /* event log header */ - capacity = iwl_trans_read_mem32(trans, base); - mode = iwl_trans_read_mem32(trans, base + (1 * sizeof(u32))); - num_wraps = iwl_trans_read_mem32(trans, base + (2 * sizeof(u32))); - next_entry = iwl_trans_read_mem32(trans, base + (3 * sizeof(u32))); - - if (capacity > logsize) { - IWL_ERR(priv, "Log capacity %d is bogus, limit to %d " - "entries\n", capacity, logsize); - capacity = logsize; - } - - if (next_entry > logsize) { - IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n", - next_entry, logsize); - next_entry = logsize; - } - - size = num_wraps ? capacity : next_entry; - - /* bail out if nothing in log */ - if (size == 0) { - IWL_ERR(trans, "Start IWL Event Log Dump: nothing in log\n"); - return pos; - } - - if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log) - size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) - ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; - IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n", - size); - -#ifdef CONFIG_IWLWIFI_DEBUG - if (buf) { - if (full_log) - bufsz = capacity * 48; - else - bufsz = size * 48; - *buf = kmalloc(bufsz, GFP_KERNEL); - if (!*buf) - return -ENOMEM; - } - if (iwl_have_debug_level(IWL_DL_FW_ERRORS) || full_log) { - /* - * if uCode has wrapped back to top of log, - * start at the oldest entry, - * i.e the next one that uCode would fill. - */ - if (num_wraps) - pos = iwl_print_event_log(priv, next_entry, - capacity - next_entry, mode, - pos, buf, bufsz); - /* (then/else) start at top of log */ - pos = iwl_print_event_log(priv, 0, - next_entry, mode, pos, buf, bufsz); - } else - pos = iwl_print_last_event_logs(priv, capacity, num_wraps, - next_entry, size, mode, - pos, buf, bufsz); -#else - pos = iwl_print_last_event_logs(priv, capacity, num_wraps, - next_entry, size, mode, - pos, buf, bufsz); -#endif - return pos; -} - -static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) -{ - unsigned int reload_msec; - unsigned long reload_jiffies; - - if (iwl_have_debug_level(IWL_DL_FW_ERRORS)) - iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS); - - /* uCode is no longer loaded. */ - priv->ucode_loaded = false; - - /* Set the FW error flag -- cleared on iwl_down */ - set_bit(STATUS_FW_ERROR, &priv->status); - - iwl_abort_notification_waits(&priv->notif_wait); - - /* Keep the restart process from trying to send host - * commands by clearing the ready bit */ - clear_bit(STATUS_READY, &priv->status); - - if (!ondemand) { - /* - * If firmware keep reloading, then it indicate something - * serious wrong and firmware having problem to recover - * from it. Instead of keep trying which will fill the syslog - * and hang the system, let's just stop it - */ - reload_jiffies = jiffies; - reload_msec = jiffies_to_msecs((long) reload_jiffies - - (long) priv->reload_jiffies); - priv->reload_jiffies = reload_jiffies; - if (reload_msec <= IWL_MIN_RELOAD_DURATION) { - priv->reload_count++; - if (priv->reload_count >= IWL_MAX_CONTINUE_RELOAD_CNT) { - IWL_ERR(priv, "BUG_ON, Stop restarting\n"); - return; - } - } else - priv->reload_count = 0; - } - - if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) { - if (iwlwifi_mod_params.restart_fw) { - IWL_DEBUG_FW_ERRORS(priv, - "Restarting adapter due to uCode error.\n"); - queue_work(priv->workqueue, &priv->restart); - } else - IWL_DEBUG_FW_ERRORS(priv, - "Detected FW error, but not restarting\n"); - } -} - -static void iwl_nic_error(struct iwl_op_mode *op_mode) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - IWL_ERR(priv, "Loaded firmware version: %s\n", - priv->fw->fw_version); - - iwl_dump_nic_error_log(priv); - iwl_dump_nic_event_log(priv, false, NULL); - - iwlagn_fw_error(priv, false); -} - -static void iwl_cmd_queue_full(struct iwl_op_mode *op_mode) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - if (!iwl_check_for_ct_kill(priv)) { - IWL_ERR(priv, "Restarting adapter queue is full\n"); - iwlagn_fw_error(priv, false); - } -} - -#define EEPROM_RF_CONFIG_TYPE_MAX 0x3 - -static void iwl_nic_config(struct iwl_op_mode *op_mode) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - /* SKU Control */ - iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | - CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP, - (CSR_HW_REV_STEP(priv->trans->hw_rev) << - CSR_HW_IF_CONFIG_REG_POS_MAC_STEP) | - (CSR_HW_REV_DASH(priv->trans->hw_rev) << - CSR_HW_IF_CONFIG_REG_POS_MAC_DASH)); - - /* write radio config values to register */ - if (priv->nvm_data->radio_cfg_type <= EEPROM_RF_CONFIG_TYPE_MAX) { - u32 reg_val = - priv->nvm_data->radio_cfg_type << - CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE | - priv->nvm_data->radio_cfg_step << - CSR_HW_IF_CONFIG_REG_POS_PHY_STEP | - priv->nvm_data->radio_cfg_dash << - CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; - - iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | - CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | - CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH, - reg_val); - - IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n", - priv->nvm_data->radio_cfg_type, - priv->nvm_data->radio_cfg_step, - priv->nvm_data->radio_cfg_dash); - } else { - WARN_ON(1); - } - - /* set CSR_HW_CONFIG_REG for uCode use */ - iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | - CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); - - /* W/A : NIC is stuck in a reset state after Early PCIe power off - * (PCIe power is lost before PERST# is asserted), - * causing ME FW to lose ownership and not being able to obtain it back. - */ - iwl_set_bits_mask_prph(priv->trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, - ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); - - if (priv->lib->nic_config) - priv->lib->nic_config(priv); -} - -static void iwl_wimax_active(struct iwl_op_mode *op_mode) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - clear_bit(STATUS_READY, &priv->status); - IWL_ERR(priv, "RF is used by WiMAX\n"); -} - -static void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - int mq = priv->queue_to_mac80211[queue]; - - if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) - return; - - if (atomic_inc_return(&priv->queue_stop_count[mq]) > 1) { - IWL_DEBUG_TX_QUEUES(priv, - "queue %d (mac80211 %d) already stopped\n", - queue, mq); - return; - } - - set_bit(mq, &priv->transport_queue_stop); - ieee80211_stop_queue(priv->hw, mq); -} - -static void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - int mq = priv->queue_to_mac80211[queue]; - - if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) - return; - - if (atomic_dec_return(&priv->queue_stop_count[mq]) > 0) { - IWL_DEBUG_TX_QUEUES(priv, - "queue %d (mac80211 %d) already awake\n", - queue, mq); - return; - } - - clear_bit(mq, &priv->transport_queue_stop); - - if (!priv->passive_no_rx) - ieee80211_wake_queue(priv->hw, mq); -} - -void iwlagn_lift_passive_no_rx(struct iwl_priv *priv) -{ - int mq; - - if (!priv->passive_no_rx) - return; - - for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) { - if (!test_bit(mq, &priv->transport_queue_stop)) { - IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d\n", mq); - ieee80211_wake_queue(priv->hw, mq); - } else { - IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d\n", mq); - } - } - - priv->passive_no_rx = false; -} - -static void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - struct ieee80211_tx_info *info; - - info = IEEE80211_SKB_CB(skb); - iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); - ieee80211_free_txskb(priv->hw, skb); -} - -static bool iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - if (state) - set_bit(STATUS_RF_KILL_HW, &priv->status); - else - clear_bit(STATUS_RF_KILL_HW, &priv->status); - - wiphy_rfkill_set_hw_state(priv->hw->wiphy, state); - - return false; -} - -static const struct iwl_op_mode_ops iwl_dvm_ops = { - .start = iwl_op_mode_dvm_start, - .stop = iwl_op_mode_dvm_stop, - .rx = iwl_rx_dispatch, - .queue_full = iwl_stop_sw_queue, - .queue_not_full = iwl_wake_sw_queue, - .hw_rf_kill = iwl_set_hw_rfkill_state, - .free_skb = iwl_free_skb, - .nic_error = iwl_nic_error, - .cmd_queue_full = iwl_cmd_queue_full, - .nic_config = iwl_nic_config, - .wimax_active = iwl_wimax_active, -}; - -/***************************************************************************** - * - * driver and module entry point - * - *****************************************************************************/ -static int __init iwl_init(void) -{ - - int ret; - - ret = iwlagn_rate_control_register(); - if (ret) { - pr_err("Unable to register rate control algorithm: %d\n", ret); - return ret; - } - - ret = iwl_opmode_register("iwldvm", &iwl_dvm_ops); - if (ret) { - pr_err("Unable to register op_mode: %d\n", ret); - iwlagn_rate_control_unregister(); - } - - return ret; -} -module_init(iwl_init); - -static void __exit iwl_exit(void) -{ - iwl_opmode_deregister("iwldvm"); - iwlagn_rate_control_unregister(); -} -module_exit(iwl_exit); diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c deleted file mode 100644 index 1513dbc79c14..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/power.c +++ /dev/null @@ -1,395 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ - - -#include -#include -#include -#include -#include "iwl-io.h" -#include "iwl-debug.h" -#include "iwl-trans.h" -#include "iwl-modparams.h" -#include "dev.h" -#include "agn.h" -#include "commands.h" -#include "power.h" - -static bool force_cam = true; -module_param(force_cam, bool, 0644); -MODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)"); - -/* - * Setting power level allows the card to go to sleep when not busy. - * - * We calculate a sleep command based on the required latency, which - * we get from mac80211. In order to handle thermal throttling, we can - * also use pre-defined power levels. - */ - -/* - * This defines the old power levels. They are still used by default - * (level 1) and for thermal throttle (levels 3 through 5) - */ - -struct iwl_power_vec_entry { - struct iwl_powertable_cmd cmd; - u8 no_dtim; /* number of skip dtim */ -}; - -#define IWL_DTIM_RANGE_0_MAX 2 -#define IWL_DTIM_RANGE_1_MAX 10 - -#define NOSLP cpu_to_le16(0), 0, 0 -#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 -#define ASLP (IWL_POWER_POWER_SAVE_ENA_MSK | \ - IWL_POWER_POWER_MANAGEMENT_ENA_MSK | \ - IWL_POWER_ADVANCE_PM_ENA_MSK) -#define ASLP_TOUT(T) cpu_to_le32(T) -#define TU_TO_USEC 1024 -#define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC) -#define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \ - cpu_to_le32(X1), \ - cpu_to_le32(X2), \ - cpu_to_le32(X3), \ - cpu_to_le32(X4)} -/* default power management (not Tx power) table values */ -/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */ -/* DTIM 0 - 2 */ -static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { - {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0}, - {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1}, - {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2} -}; - - -/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ -/* DTIM 3 - 10 */ -static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { - {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, - {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1}, - {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2} -}; - -/* for DTIM period > IWL_DTIM_RANGE_1_MAX */ -/* DTIM 11 - */ -static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { - {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, - {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, - {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} -}; - -/* advance power management */ -/* DTIM 0 - 2 */ -static const struct iwl_power_vec_entry apm_range_0[IWL_POWER_NUM] = { - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} -}; - - -/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ -/* DTIM 3 - 10 */ -static const struct iwl_power_vec_entry apm_range_1[IWL_POWER_NUM] = { - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 6, 8, 0xFF), 0}, 2} -}; - -/* for DTIM period > IWL_DTIM_RANGE_1_MAX */ -/* DTIM 11 - */ -static const struct iwl_power_vec_entry apm_range_2[IWL_POWER_NUM] = { - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} -}; - -static void iwl_static_sleep_cmd(struct iwl_priv *priv, - struct iwl_powertable_cmd *cmd, - enum iwl_power_level lvl, int period) -{ - const struct iwl_power_vec_entry *table; - int max_sleep[IWL_POWER_VEC_SIZE] = { 0 }; - int i; - u8 skip; - u32 slp_itrvl; - - if (priv->lib->adv_pm) { - table = apm_range_2; - if (period <= IWL_DTIM_RANGE_1_MAX) - table = apm_range_1; - if (period <= IWL_DTIM_RANGE_0_MAX) - table = apm_range_0; - } else { - table = range_2; - if (period <= IWL_DTIM_RANGE_1_MAX) - table = range_1; - if (period <= IWL_DTIM_RANGE_0_MAX) - table = range_0; - } - - if (WARN_ON(lvl < 0 || lvl >= IWL_POWER_NUM)) - memset(cmd, 0, sizeof(*cmd)); - else - *cmd = table[lvl].cmd; - - if (period == 0) { - skip = 0; - period = 1; - for (i = 0; i < IWL_POWER_VEC_SIZE; i++) - max_sleep[i] = 1; - - } else { - skip = table[lvl].no_dtim; - for (i = 0; i < IWL_POWER_VEC_SIZE; i++) - max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]); - max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1; - } - - slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); - /* figure out the listen interval based on dtim period and skip */ - if (slp_itrvl == 0xFF) - cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = - cpu_to_le32(period * (skip + 1)); - - slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); - if (slp_itrvl > period) - cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = - cpu_to_le32((slp_itrvl / period) * period); - - if (skip) - cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; - else - cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; - - if (priv->cfg->base_params->shadow_reg_enable) - cmd->flags |= IWL_POWER_SHADOW_REG_ENA; - else - cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; - - if (iwl_advanced_bt_coexist(priv)) { - if (!priv->lib->bt_params->bt_sco_disable) - cmd->flags |= IWL_POWER_BT_SCO_ENA; - else - cmd->flags &= ~IWL_POWER_BT_SCO_ENA; - } - - - slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); - if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL) - cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = - cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL); - - /* enforce max sleep interval */ - for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) { - if (le32_to_cpu(cmd->sleep_interval[i]) > - (max_sleep[i] * period)) - cmd->sleep_interval[i] = - cpu_to_le32(max_sleep[i] * period); - if (i != (IWL_POWER_VEC_SIZE - 1)) { - if (le32_to_cpu(cmd->sleep_interval[i]) > - le32_to_cpu(cmd->sleep_interval[i+1])) - cmd->sleep_interval[i] = - cmd->sleep_interval[i+1]; - } - } - - if (priv->power_data.bus_pm) - cmd->flags |= IWL_POWER_PCI_PM_MSK; - else - cmd->flags &= ~IWL_POWER_PCI_PM_MSK; - - IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n", - skip, period); - /* The power level here is 0-4 (used as array index), but user expects - to see 1-5 (according to spec). */ - IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1); -} - -static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv, - struct iwl_powertable_cmd *cmd) -{ - memset(cmd, 0, sizeof(*cmd)); - - if (priv->power_data.bus_pm) - cmd->flags |= IWL_POWER_PCI_PM_MSK; - - IWL_DEBUG_POWER(priv, "Sleep command for CAM\n"); -} - -static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) -{ - IWL_DEBUG_POWER(priv, "Sending power/sleep command\n"); - IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags); - IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); - IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); - IWL_DEBUG_POWER(priv, "Sleep interval vector = { %d , %d , %d , %d , %d }\n", - le32_to_cpu(cmd->sleep_interval[0]), - le32_to_cpu(cmd->sleep_interval[1]), - le32_to_cpu(cmd->sleep_interval[2]), - le32_to_cpu(cmd->sleep_interval[3]), - le32_to_cpu(cmd->sleep_interval[4])); - - return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, 0, - sizeof(struct iwl_powertable_cmd), cmd); -} - -static void iwl_power_build_cmd(struct iwl_priv *priv, - struct iwl_powertable_cmd *cmd) -{ - bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS; - int dtimper; - - if (force_cam) { - iwl_power_sleep_cam_cmd(priv, cmd); - return; - } - - dtimper = priv->hw->conf.ps_dtim_period ?: 1; - - if (priv->wowlan) - iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); - else if (!priv->lib->no_idle_support && - priv->hw->conf.flags & IEEE80211_CONF_IDLE) - iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); - else if (iwl_tt_is_low_power_state(priv)) { - /* in thermal throttling low power state */ - iwl_static_sleep_cmd(priv, cmd, - iwl_tt_current_power_mode(priv), dtimper); - } else if (!enabled) - iwl_power_sleep_cam_cmd(priv, cmd); - else if (priv->power_data.debug_sleep_level_override >= 0) - iwl_static_sleep_cmd(priv, cmd, - priv->power_data.debug_sleep_level_override, - dtimper); - else { - /* Note that the user parameter is 1-5 (according to spec), - but we pass 0-4 because it acts as an array index. */ - if (iwlwifi_mod_params.power_level > IWL_POWER_INDEX_1 && - iwlwifi_mod_params.power_level <= IWL_POWER_NUM) - iwl_static_sleep_cmd(priv, cmd, - iwlwifi_mod_params.power_level - 1, dtimper); - else - iwl_static_sleep_cmd(priv, cmd, - IWL_POWER_INDEX_1, dtimper); - } -} - -int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, - bool force) -{ - int ret; - bool update_chains; - - lockdep_assert_held(&priv->mutex); - - /* Don't update the RX chain when chain noise calibration is running */ - update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE || - priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; - - if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) - return 0; - - if (!iwl_is_ready_rf(priv)) - return -EIO; - - /* scan complete use sleep_power_next, need to be updated */ - memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); - if (test_bit(STATUS_SCANNING, &priv->status) && !force) { - IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n"); - return 0; - } - - if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK) - iwl_dvm_set_pmi(priv, true); - - ret = iwl_set_power(priv, cmd); - if (!ret) { - if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)) - iwl_dvm_set_pmi(priv, false); - - if (update_chains) - iwl_update_chain_flags(priv); - else - IWL_DEBUG_POWER(priv, - "Cannot update the power, chain noise " - "calibration running: %d\n", - priv->chain_noise_data.state); - - memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)); - } else - IWL_ERR(priv, "set power fail, ret = %d\n", ret); - - return ret; -} - -int iwl_power_update_mode(struct iwl_priv *priv, bool force) -{ - struct iwl_powertable_cmd cmd; - - iwl_power_build_cmd(priv, &cmd); - return iwl_power_set_mode(priv, &cmd, force); -} - -/* initialize to default */ -void iwl_power_initialize(struct iwl_priv *priv) -{ - priv->power_data.bus_pm = priv->trans->pm_support; - - priv->power_data.debug_sleep_level_override = -1; - - memset(&priv->power_data.sleep_cmd, 0, - sizeof(priv->power_data.sleep_cmd)); -} diff --git a/drivers/net/wireless/iwlwifi/dvm/power.h b/drivers/net/wireless/iwlwifi/dvm/power.h deleted file mode 100644 index 570d3a5e4670..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/power.h +++ /dev/null @@ -1,47 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ -#ifndef __iwl_power_setting_h__ -#define __iwl_power_setting_h__ - -#include "commands.h" - -struct iwl_power_mgr { - struct iwl_powertable_cmd sleep_cmd; - struct iwl_powertable_cmd sleep_cmd_next; - int debug_sleep_level_override; - bool bus_pm; -}; - -int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, - bool force); -int iwl_power_update_mode(struct iwl_priv *priv, bool force); -void iwl_power_initialize(struct iwl_priv *priv); - -extern bool no_sleep_autoadjust; - -#endif /* __iwl_power_setting_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c deleted file mode 100644 index cef921c1a623..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ /dev/null @@ -1,3338 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "dev.h" -#include "agn.h" - -#define RS_NAME "iwl-agn-rs" - -#define NUM_TRY_BEFORE_ANT_TOGGLE 1 -#define IWL_NUMBER_TRY 1 -#define IWL_HT_NUMBER_TRY 3 - -#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ -#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ -#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ - -/* max allowed rate miss before sync LQ cmd */ -#define IWL_MISSED_RATE_MAX 15 -/* max time to accum history 2 seconds */ -#define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ) - -static u8 rs_ht_to_legacy[] = { - IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, - IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, - IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, - IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX -}; - -static const u8 ant_toggle_lookup[] = { - /*ANT_NONE -> */ ANT_NONE, - /*ANT_A -> */ ANT_B, - /*ANT_B -> */ ANT_C, - /*ANT_AB -> */ ANT_BC, - /*ANT_C -> */ ANT_A, - /*ANT_AC -> */ ANT_AB, - /*ANT_BC -> */ ANT_AC, - /*ANT_ABC -> */ ANT_ABC, -}; - -#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ - [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ - IWL_RATE_SISO_##s##M_PLCP, \ - IWL_RATE_MIMO2_##s##M_PLCP,\ - IWL_RATE_MIMO3_##s##M_PLCP,\ - IWL_RATE_##r##M_IEEE, \ - IWL_RATE_##ip##M_INDEX, \ - IWL_RATE_##in##M_INDEX, \ - IWL_RATE_##rp##M_INDEX, \ - IWL_RATE_##rn##M_INDEX, \ - IWL_RATE_##pp##M_INDEX, \ - IWL_RATE_##np##M_INDEX } - -/* - * Parameter order: - * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate - * - * If there isn't a valid next or previous rate then INV is used which - * maps to IWL_RATE_INVALID - * - */ -const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { - IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ - IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ - IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ - IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ - IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ - IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ - IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ - IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ - IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ - IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ - IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ - IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ - IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ - /* FIXME:RS: ^^ should be INV (legacy) */ -}; - -static inline u8 rs_extract_rate(u32 rate_n_flags) -{ - return (u8)(rate_n_flags & RATE_MCS_RATE_MSK); -} - -static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) -{ - int idx = 0; - - /* HT rate format */ - if (rate_n_flags & RATE_MCS_HT_MSK) { - idx = rs_extract_rate(rate_n_flags); - - if (idx >= IWL_RATE_MIMO3_6M_PLCP) - idx = idx - IWL_RATE_MIMO3_6M_PLCP; - else if (idx >= IWL_RATE_MIMO2_6M_PLCP) - idx = idx - IWL_RATE_MIMO2_6M_PLCP; - - idx += IWL_FIRST_OFDM_RATE; - /* skip 9M not supported in ht*/ - if (idx >= IWL_RATE_9M_INDEX) - idx += 1; - if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE)) - return idx; - - /* legacy rate format, search for match in table */ - } else { - for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) - if (iwl_rates[idx].plcp == - rs_extract_rate(rate_n_flags)) - return idx; - } - - return -1; -} - -static void rs_rate_scale_perform(struct iwl_priv *priv, - struct sk_buff *skb, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta); -static void rs_fill_link_cmd(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, u32 rate_n_flags); -static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); - - -#ifdef CONFIG_MAC80211_DEBUGFS -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags, int index); -#else -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags, int index) -{} -#endif - -/** - * The following tables contain the expected throughput metrics for all rates - * - * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits - * - * where invalid entries are zeros. - * - * CCK rates are only valid in legacy table and will only be used in G - * (2.4 GHz) band. - */ - -static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = { - 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 -}; - -static const u16 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */ - {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */ - {0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */ - {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */ -}; - -static const u16 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ - {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ - {0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */ - {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */ -}; - -static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */ - {0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */ - {0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */ - {0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/ -}; - -static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ - {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ - {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */ - {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */ -}; - -static const u16 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */ - {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */ - {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */ - {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */ -}; - -static const u16 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */ - {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */ - {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */ - {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */ -}; - -/* mbps, mcs */ -static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { - { "1", "BPSK DSSS"}, - { "2", "QPSK DSSS"}, - {"5.5", "BPSK CCK"}, - { "11", "QPSK CCK"}, - { "6", "BPSK 1/2"}, - { "9", "BPSK 1/2"}, - { "12", "QPSK 1/2"}, - { "18", "QPSK 3/4"}, - { "24", "16QAM 1/2"}, - { "36", "16QAM 3/4"}, - { "48", "64QAM 2/3"}, - { "54", "64QAM 3/4"}, - { "60", "64QAM 5/6"}, -}; - -#define MCS_INDEX_PER_STREAM (8) - -static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) -{ - window->data = 0; - window->success_counter = 0; - window->success_ratio = IWL_INVALID_VALUE; - window->counter = 0; - window->average_tpt = IWL_INVALID_VALUE; - window->stamp = 0; -} - -static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) -{ - return (ant_type & valid_antenna) == ant_type; -} - -/* - * removes the old data from the statistics. All data that is older than - * TID_MAX_TIME_DIFF, will be deleted. - */ -static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time) -{ - /* The oldest age we want to keep */ - u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; - - while (tl->queue_count && - (tl->time_stamp < oldest_time)) { - tl->total -= tl->packet_count[tl->head]; - tl->packet_count[tl->head] = 0; - tl->time_stamp += TID_QUEUE_CELL_SPACING; - tl->queue_count--; - tl->head++; - if (tl->head >= TID_QUEUE_MAX_SIZE) - tl->head = 0; - } -} - -/* - * increment traffic load value for tid and also remove - * any old values if passed the certain time period - */ -static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data, - struct ieee80211_hdr *hdr) -{ - u32 curr_time = jiffies_to_msecs(jiffies); - u32 time_diff; - s32 index; - struct iwl_traffic_load *tl = NULL; - u8 tid; - - if (ieee80211_is_data_qos(hdr->frame_control)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & 0xf; - } else - return IWL_MAX_TID_COUNT; - - if (unlikely(tid >= IWL_MAX_TID_COUNT)) - return IWL_MAX_TID_COUNT; - - tl = &lq_data->load[tid]; - - curr_time -= curr_time % TID_ROUND_VALUE; - - /* Happens only for the first packet. Initialize the data */ - if (!(tl->queue_count)) { - tl->total = 1; - tl->time_stamp = curr_time; - tl->queue_count = 1; - tl->head = 0; - tl->packet_count[0] = 1; - return IWL_MAX_TID_COUNT; - } - - time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); - index = time_diff / TID_QUEUE_CELL_SPACING; - - /* The history is too long: remove data that is older than */ - /* TID_MAX_TIME_DIFF */ - if (index >= TID_QUEUE_MAX_SIZE) - rs_tl_rm_old_stats(tl, curr_time); - - index = (tl->head + index) % TID_QUEUE_MAX_SIZE; - tl->packet_count[index] = tl->packet_count[index] + 1; - tl->total = tl->total + 1; - - if ((index + 1) > tl->queue_count) - tl->queue_count = index + 1; - - return tid; -} - -#ifdef CONFIG_MAC80211_DEBUGFS -/** - * Program the device to use fixed rate for frame transmit - * This is for debugging/testing only - * once the device start use fixed rate, we need to reload the module - * to being back the normal operation. - */ -static void rs_program_fix_rate(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta) -{ - struct iwl_station_priv *sta_priv = - container_of(lq_sta, struct iwl_station_priv, lq_sta); - struct iwl_rxon_context *ctx = sta_priv->ctx; - - lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ - lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - - IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n", - lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); - - if (lq_sta->dbg_fixed_rate) { - rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); - iwl_send_lq_cmd(lq_sta->drv, ctx, &lq_sta->lq, CMD_ASYNC, - false); - } -} -#endif - -/* - get the traffic load value for tid -*/ -static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid) -{ - u32 curr_time = jiffies_to_msecs(jiffies); - u32 time_diff; - s32 index; - struct iwl_traffic_load *tl = NULL; - - if (tid >= IWL_MAX_TID_COUNT) - return 0; - - tl = &(lq_data->load[tid]); - - curr_time -= curr_time % TID_ROUND_VALUE; - - if (!(tl->queue_count)) - return 0; - - time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); - index = time_diff / TID_QUEUE_CELL_SPACING; - - /* The history is too long: remove data that is older than */ - /* TID_MAX_TIME_DIFF */ - if (index >= TID_QUEUE_MAX_SIZE) - rs_tl_rm_old_stats(tl, curr_time); - - return tl->total; -} - -static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, - struct iwl_lq_sta *lq_data, u8 tid, - struct ieee80211_sta *sta) -{ - int ret = -EAGAIN; - u32 load; - - /* - * Don't create TX aggregation sessions when in high - * BT traffic, as they would just be disrupted by BT. - */ - if (priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) { - IWL_DEBUG_COEX(priv, - "BT traffic (%d), no aggregation allowed\n", - priv->bt_traffic_load); - return ret; - } - - load = rs_tl_get_load(lq_data, tid); - - IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", - sta->addr, tid); - ret = ieee80211_start_tx_ba_session(sta, tid, 5000); - if (ret == -EAGAIN) { - /* - * driver and mac80211 is out of sync - * this might be cause by reloading firmware - * stop the tx ba session here - */ - IWL_ERR(priv, "Fail start Tx agg on tid: %d\n", - tid); - ieee80211_stop_tx_ba_session(sta, tid); - } - return ret; -} - -static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, - struct iwl_lq_sta *lq_data, - struct ieee80211_sta *sta) -{ - if (tid < IWL_MAX_TID_COUNT) - rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); - else - IWL_ERR(priv, "tid exceeds max TID count: %d/%d\n", - tid, IWL_MAX_TID_COUNT); -} - -static inline int get_num_of_ant_from_rate(u32 rate_n_flags) -{ - return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_C_MSK); -} - -/* - * Static function to get the expected throughput from an iwl_scale_tbl_info - * that wraps a NULL pointer check - */ -static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) -{ - if (tbl->expected_tpt) - return tbl->expected_tpt[rs_index]; - return 0; -} - -/** - * rs_collect_tx_data - Update the success/failure sliding window - * - * We keep a sliding window of the last 62 packets transmitted - * at this rate. window->data contains the bitmask of successful - * packets. - */ -static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, - int scale_index, int attempts, int successes) -{ - struct iwl_rate_scale_data *window = NULL; - static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); - s32 fail_count, tpt; - - if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) - return -EINVAL; - - /* Select window for current tx bit rate */ - window = &(tbl->win[scale_index]); - - /* Get expected throughput */ - tpt = get_expected_tpt(tbl, scale_index); - - /* - * Keep track of only the latest 62 tx frame attempts in this rate's - * history window; anything older isn't really relevant any more. - * If we have filled up the sliding window, drop the oldest attempt; - * if the oldest attempt (highest bit in bitmap) shows "success", - * subtract "1" from the success counter (this is the main reason - * we keep these bitmaps!). - */ - while (attempts > 0) { - if (window->counter >= IWL_RATE_MAX_WINDOW) { - - /* remove earliest */ - window->counter = IWL_RATE_MAX_WINDOW - 1; - - if (window->data & mask) { - window->data &= ~mask; - window->success_counter--; - } - } - - /* Increment frames-attempted counter */ - window->counter++; - - /* Shift bitmap by one frame to throw away oldest history */ - window->data <<= 1; - - /* Mark the most recent #successes attempts as successful */ - if (successes > 0) { - window->success_counter++; - window->data |= 0x1; - successes--; - } - - attempts--; - } - - /* Calculate current success ratio, avoid divide-by-0! */ - if (window->counter > 0) - window->success_ratio = 128 * (100 * window->success_counter) - / window->counter; - else - window->success_ratio = IWL_INVALID_VALUE; - - fail_count = window->counter - window->success_counter; - - /* Calculate average throughput, if we have enough history. */ - if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || - (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) - window->average_tpt = (window->success_ratio * tpt + 64) / 128; - else - window->average_tpt = IWL_INVALID_VALUE; - - /* Tag this window as having been updated */ - window->stamp = jiffies; - - return 0; -} - -/* - * Fill uCode API rate_n_flags field, based on "search" or "active" table. - */ -/* FIXME:RS:remove this function and put the flags statically in the table */ -static u32 rate_n_flags_from_tbl(struct iwl_priv *priv, - struct iwl_scale_tbl_info *tbl, - int index, u8 use_green) -{ - u32 rate_n_flags = 0; - - if (is_legacy(tbl->lq_type)) { - rate_n_flags = iwl_rates[index].plcp; - if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) - rate_n_flags |= RATE_MCS_CCK_MSK; - - } else if (is_Ht(tbl->lq_type)) { - if (index > IWL_LAST_OFDM_RATE) { - IWL_ERR(priv, "Invalid HT rate index %d\n", index); - index = IWL_LAST_OFDM_RATE; - } - rate_n_flags = RATE_MCS_HT_MSK; - - if (is_siso(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_siso; - else if (is_mimo2(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_mimo2; - else - rate_n_flags |= iwl_rates[index].plcp_mimo3; - } else { - IWL_ERR(priv, "Invalid tbl->lq_type %d\n", tbl->lq_type); - } - - rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & - RATE_MCS_ANT_ABC_MSK); - - if (is_Ht(tbl->lq_type)) { - if (tbl->is_ht40) { - if (tbl->is_dup) - rate_n_flags |= RATE_MCS_DUP_MSK; - else - rate_n_flags |= RATE_MCS_HT40_MSK; - } - if (tbl->is_SGI) - rate_n_flags |= RATE_MCS_SGI_MSK; - - if (use_green) { - rate_n_flags |= RATE_MCS_GF_MSK; - if (is_siso(tbl->lq_type) && tbl->is_SGI) { - rate_n_flags &= ~RATE_MCS_SGI_MSK; - IWL_ERR(priv, "GF was set with SGI:SISO\n"); - } - } - } - return rate_n_flags; -} - -/* - * Interpret uCode API's rate_n_flags format, - * fill "search" or "active" tx mode table. - */ -static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, - enum ieee80211_band band, - struct iwl_scale_tbl_info *tbl, - int *rate_idx) -{ - u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); - u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); - u8 mcs; - - memset(tbl, 0, sizeof(struct iwl_scale_tbl_info)); - *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); - - if (*rate_idx == IWL_RATE_INVALID) { - *rate_idx = -1; - return -EINVAL; - } - tbl->is_SGI = 0; /* default legacy setup */ - tbl->is_ht40 = 0; - tbl->is_dup = 0; - tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); - tbl->lq_type = LQ_NONE; - tbl->max_search = IWL_MAX_SEARCH; - - /* legacy rate format */ - if (!(rate_n_flags & RATE_MCS_HT_MSK)) { - if (num_of_ant == 1) { - if (band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; - else - tbl->lq_type = LQ_G; - } - /* HT rate format */ - } else { - if (rate_n_flags & RATE_MCS_SGI_MSK) - tbl->is_SGI = 1; - - if ((rate_n_flags & RATE_MCS_HT40_MSK) || - (rate_n_flags & RATE_MCS_DUP_MSK)) - tbl->is_ht40 = 1; - - if (rate_n_flags & RATE_MCS_DUP_MSK) - tbl->is_dup = 1; - - mcs = rs_extract_rate(rate_n_flags); - - /* SISO */ - if (mcs <= IWL_RATE_SISO_60M_PLCP) { - if (num_of_ant == 1) - tbl->lq_type = LQ_SISO; /*else NONE*/ - /* MIMO2 */ - } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) { - if (num_of_ant == 2) - tbl->lq_type = LQ_MIMO2; - /* MIMO3 */ - } else { - if (num_of_ant == 3) { - tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; - tbl->lq_type = LQ_MIMO3; - } - } - } - return 0; -} - -/* switch to another antenna/antennas and return 1 */ -/* if no other valid antenna found, return 0 */ -static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, - struct iwl_scale_tbl_info *tbl) -{ - u8 new_ant_type; - - if (!tbl->ant_type || tbl->ant_type > ANT_ABC) - return 0; - - if (!rs_is_valid_ant(valid_ant, tbl->ant_type)) - return 0; - - new_ant_type = ant_toggle_lookup[tbl->ant_type]; - - while ((new_ant_type != tbl->ant_type) && - !rs_is_valid_ant(valid_ant, new_ant_type)) - new_ant_type = ant_toggle_lookup[new_ant_type]; - - if (new_ant_type == tbl->ant_type) - return 0; - - tbl->ant_type = new_ant_type; - *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; - *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; - return 1; -} - -/** - * Green-field mode is valid if the station supports it and - * there are no non-GF stations present in the BSS. - */ -static bool rs_use_green(struct ieee80211_sta *sta) -{ - /* - * There's a bug somewhere in this code that causes the - * scaling to get stuck because GF+SGI can't be combined - * in SISO rates. Until we find that bug, disable GF, it - * has only limited benefit and we still interoperate with - * GF APs since we can always receive GF transmissions. - */ - return false; -} - -/** - * rs_get_supported_rates - get the available rates - * - * if management frame or broadcast frame only return - * basic available rates. - * - */ -static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, - struct ieee80211_hdr *hdr, - enum iwl_table_type rate_type) -{ - if (is_legacy(rate_type)) { - return lq_sta->active_legacy_rate; - } else { - if (is_siso(rate_type)) - return lq_sta->active_siso_rate; - else if (is_mimo2(rate_type)) - return lq_sta->active_mimo2_rate; - else - return lq_sta->active_mimo3_rate; - } -} - -static u16 rs_get_adjacent_rate(struct iwl_priv *priv, u8 index, u16 rate_mask, - int rate_type) -{ - u8 high = IWL_RATE_INVALID; - u8 low = IWL_RATE_INVALID; - - /* 802.11A or ht walks to the next literal adjacent rate in - * the rate table */ - if (is_a_band(rate_type) || !is_legacy(rate_type)) { - int i; - u32 mask; - - /* Find the previous rate that is in the rate mask */ - i = index - 1; - for (mask = (1 << i); i >= 0; i--, mask >>= 1) { - if (rate_mask & mask) { - low = i; - break; - } - } - - /* Find the next rate that is in the rate mask */ - i = index + 1; - for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { - if (rate_mask & mask) { - high = i; - break; - } - } - - return (high << 8) | low; - } - - low = index; - while (low != IWL_RATE_INVALID) { - low = iwl_rates[low].prev_rs; - if (low == IWL_RATE_INVALID) - break; - if (rate_mask & (1 << low)) - break; - IWL_DEBUG_RATE(priv, "Skipping masked lower rate: %d\n", low); - } - - high = index; - while (high != IWL_RATE_INVALID) { - high = iwl_rates[high].next_rs; - if (high == IWL_RATE_INVALID) - break; - if (rate_mask & (1 << high)) - break; - IWL_DEBUG_RATE(priv, "Skipping masked higher rate: %d\n", high); - } - - return (high << 8) | low; -} - -static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - u8 scale_index, u8 ht_possible) -{ - s32 low; - u16 rate_mask; - u16 high_low; - u8 switch_to_legacy = 0; - u8 is_green = lq_sta->is_green; - struct iwl_priv *priv = lq_sta->drv; - - /* check if we need to switch from HT to legacy rates. - * assumption is that mandatory rates (1Mbps or 6Mbps) - * are always supported (spec demand) */ - if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { - switch_to_legacy = 1; - scale_index = rs_ht_to_legacy[scale_index]; - if (lq_sta->band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; - else - tbl->lq_type = LQ_G; - - if (num_of_ant(tbl->ant_type) > 1) - tbl->ant_type = - first_antenna(priv->nvm_data->valid_tx_ant); - - tbl->is_ht40 = 0; - tbl->is_SGI = 0; - tbl->max_search = IWL_MAX_SEARCH; - } - - rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); - - /* Mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { - /* supp_rates has no CCK bits in A mode */ - if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate_mask = (u16)(rate_mask & - (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); - else - rate_mask = (u16)(rate_mask & lq_sta->supp_rates); - } - - /* If we switched from HT to legacy, check current rate */ - if (switch_to_legacy && (rate_mask & (1 << scale_index))) { - low = scale_index; - goto out; - } - - high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask, - tbl->lq_type); - low = high_low & 0xff; - - if (low == IWL_RATE_INVALID) - low = scale_index; - -out: - return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); -} - -/* - * Simple function to compare two rate scale table types - */ -static bool table_type_matches(struct iwl_scale_tbl_info *a, - struct iwl_scale_tbl_info *b) -{ - return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) && - (a->is_SGI == b->is_SGI); -} - -static void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct iwl_lq_sta *lq_sta) -{ - struct iwl_scale_tbl_info *tbl; - bool full_concurrent = priv->bt_full_concurrent; - - if (priv->bt_ant_couple_ok) { - /* - * Is there a need to switch between - * full concurrency and 3-wire? - */ - if (priv->bt_ci_compliance && priv->bt_ant_couple_ok) - full_concurrent = true; - else - full_concurrent = false; - } - if ((priv->bt_traffic_load != priv->last_bt_traffic_load) || - (priv->bt_full_concurrent != full_concurrent)) { - priv->bt_full_concurrent = full_concurrent; - priv->last_bt_traffic_load = priv->bt_traffic_load; - - /* Update uCode's rate table. */ - tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); - - queue_work(priv->workqueue, &priv->bt_full_concurrency); - } -} - -/* - * mac80211 sends us Tx status - */ -static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta, - struct sk_buff *skb) -{ - int legacy_success; - int retries; - int rs_index, mac_index, i; - struct iwl_lq_sta *lq_sta = priv_sta; - struct iwl_link_quality_cmd *table; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct iwl_op_mode *op_mode = (struct iwl_op_mode *)priv_r; - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - enum mac80211_rate_control_flags mac_flags; - u32 tx_rate; - struct iwl_scale_tbl_info tbl_type; - struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n"); - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (!lq_sta) { - IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n"); - return; - } else if (!lq_sta->drv) { - IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); - return; - } - - if (!ieee80211_is_data(hdr->frame_control) || - info->flags & IEEE80211_TX_CTL_NO_ACK) - return; - - /* This packet was aggregated but doesn't carry status info */ - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && - !(info->flags & IEEE80211_TX_STAT_AMPDU)) - return; - - /* - * Ignore this Tx frame response if its initial rate doesn't match - * that of latest Link Quality command. There may be stragglers - * from a previous Link Quality command, but we're no longer interested - * in those; they're either from the "active" mode while we're trying - * to check "search" mode, or a prior "search" mode after we've moved - * to a new "search" mode (which might become the new "active" mode). - */ - table = &lq_sta->lq; - tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); - rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); - if (priv->band == IEEE80211_BAND_5GHZ) - rs_index -= IWL_FIRST_OFDM_RATE; - mac_flags = info->status.rates[0].flags; - mac_index = info->status.rates[0].idx; - /* For HT packets, map MCS to PLCP */ - if (mac_flags & IEEE80211_TX_RC_MCS) { - mac_index &= RATE_MCS_CODE_MSK; /* Remove # of streams */ - if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE)) - mac_index++; - /* - * mac80211 HT index is always zero-indexed; we need to move - * HT OFDM rates after CCK rates in 2.4 GHz band - */ - if (priv->band == IEEE80211_BAND_2GHZ) - mac_index += IWL_FIRST_OFDM_RATE; - } - /* Here we actually compare this rate to the latest LQ command */ - if ((mac_index < 0) || - (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || - (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || - (tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA)) || - (tbl_type.ant_type != info->status.antenna) || - (!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)) || - (!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || - (rs_index != mac_index)) { - IWL_DEBUG_RATE(priv, "initial rate %d does not match %d (0x%x)\n", mac_index, rs_index, tx_rate); - /* - * Since rates mis-match, the last LQ command may have failed. - * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with - * ... driver. - */ - lq_sta->missed_rate_counter++; - if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { - lq_sta->missed_rate_counter = 0; - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); - } - /* Regardless, ignore this status info for outdated rate */ - return; - } else - /* Rate did match, so reset the missed_rate_counter */ - lq_sta->missed_rate_counter = 0; - - /* Figure out if rate scale algorithm is in active or search table */ - if (table_type_matches(&tbl_type, - &(lq_sta->lq_info[lq_sta->active_tbl]))) { - curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - } else if (table_type_matches(&tbl_type, - &lq_sta->lq_info[1 - lq_sta->active_tbl])) { - curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - } else { - IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n"); - tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - IWL_DEBUG_RATE(priv, "active- lq:%x, ant:%x, SGI:%d\n", - tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); - tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - IWL_DEBUG_RATE(priv, "search- lq:%x, ant:%x, SGI:%d\n", - tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); - IWL_DEBUG_RATE(priv, "actual- lq:%x, ant:%x, SGI:%d\n", - tbl_type.lq_type, tbl_type.ant_type, tbl_type.is_SGI); - /* - * no matching table found, let's by-pass the data collection - * and continue to perform rate scale to find the rate table - */ - rs_stay_in_table(lq_sta, true); - goto done; - } - - /* - * Updating the frame history depends on whether packets were - * aggregated. - * - * For aggregation, all packets were transmitted at the same rate, the - * first index into rate scale table. - */ - if (info->flags & IEEE80211_TX_STAT_AMPDU) { - tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); - rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, - &rs_index); - rs_collect_tx_data(curr_tbl, rs_index, - info->status.ampdu_len, - info->status.ampdu_ack_len); - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->stay_in_tbl) { - lq_sta->total_success += info->status.ampdu_ack_len; - lq_sta->total_failed += (info->status.ampdu_len - - info->status.ampdu_ack_len); - } - } else { - /* - * For legacy, update frame history with for each Tx retry. - */ - retries = info->status.rates[0].count - 1; - /* HW doesn't send more than 15 retries */ - retries = min(retries, 15); - - /* The last transmission may have been successful */ - legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); - /* Collect data for each rate used during failed TX attempts */ - for (i = 0; i <= retries; ++i) { - tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags); - rs_get_tbl_info_from_mcs(tx_rate, priv->band, - &tbl_type, &rs_index); - /* - * Only collect stats if retried rate is in the same RS - * table as active/search. - */ - if (table_type_matches(&tbl_type, curr_tbl)) - tmp_tbl = curr_tbl; - else if (table_type_matches(&tbl_type, other_tbl)) - tmp_tbl = other_tbl; - else - continue; - rs_collect_tx_data(tmp_tbl, rs_index, 1, - i < retries ? 0 : legacy_success); - } - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->stay_in_tbl) { - lq_sta->total_success += legacy_success; - lq_sta->total_failed += retries + (1 - legacy_success); - } - } - /* The last TX rate is cached in lq_sta; it's set in if/else above */ - lq_sta->last_rate_n_flags = tx_rate; -done: - /* See if there's a better rate or modulation mode to try. */ - if (sta && sta->supp_rates[sband->band]) - rs_rate_scale_perform(priv, skb, sta, lq_sta); - - if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist) - rs_bt_update_lq(priv, ctx, lq_sta); -} - -/* - * Begin a period of staying with a selected modulation mode. - * Set "stay_in_tbl" flag to prevent any mode switches. - * Set frame tx success limits according to legacy vs. high-throughput, - * and reset overall (spanning all rates) tx success history statistics. - * These control how long we stay using same modulation mode before - * searching for a new mode. - */ -static void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy, - struct iwl_lq_sta *lq_sta) -{ - IWL_DEBUG_RATE(priv, "we are staying in the same table\n"); - lq_sta->stay_in_tbl = 1; /* only place this gets set */ - if (is_legacy) { - lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; - } else { - lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT; - } - lq_sta->table_count = 0; - lq_sta->total_failed = 0; - lq_sta->total_success = 0; - lq_sta->flush_timer = jiffies; - lq_sta->action_counter = 0; -} - -/* - * Find correct throughput table for given mode of modulation - */ -static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl) -{ - /* Used to choose among HT tables */ - const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT]; - - /* Check for invalid LQ type */ - if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { - tbl->expected_tpt = expected_tpt_legacy; - return; - } - - /* Legacy rates have only one table */ - if (is_legacy(tbl->lq_type)) { - tbl->expected_tpt = expected_tpt_legacy; - return; - } - - /* Choose among many HT tables depending on number of streams - * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation - * status */ - if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) - ht_tbl_pointer = expected_tpt_siso20MHz; - else if (is_siso(tbl->lq_type)) - ht_tbl_pointer = expected_tpt_siso40MHz; - else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) - ht_tbl_pointer = expected_tpt_mimo2_20MHz; - else if (is_mimo2(tbl->lq_type)) - ht_tbl_pointer = expected_tpt_mimo2_40MHz; - else if (is_mimo3(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) - ht_tbl_pointer = expected_tpt_mimo3_20MHz; - else /* if (is_mimo3(tbl->lq_type)) <-- must be true */ - ht_tbl_pointer = expected_tpt_mimo3_40MHz; - - if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ - tbl->expected_tpt = ht_tbl_pointer[0]; - else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ - tbl->expected_tpt = ht_tbl_pointer[1]; - else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ - tbl->expected_tpt = ht_tbl_pointer[2]; - else /* AGG+SGI */ - tbl->expected_tpt = ht_tbl_pointer[3]; -} - -/* - * Find starting rate for new "search" high-throughput mode of modulation. - * Goal is to find lowest expected rate (under perfect conditions) that is - * above the current measured throughput of "active" mode, to give new mode - * a fair chance to prove itself without too many challenges. - * - * This gets called when transitioning to more aggressive modulation - * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive - * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need - * to decrease to match "active" throughput. When moving from MIMO to SISO, - * bit rate will typically need to increase, but not if performance was bad. - */ -static s32 rs_get_best_rate(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, /* "search" */ - u16 rate_mask, s8 index) -{ - /* "active" values */ - struct iwl_scale_tbl_info *active_tbl = - &(lq_sta->lq_info[lq_sta->active_tbl]); - s32 active_sr = active_tbl->win[index].success_ratio; - s32 active_tpt = active_tbl->expected_tpt[index]; - /* expected "search" throughput */ - const u16 *tpt_tbl = tbl->expected_tpt; - - s32 new_rate, high, low, start_hi; - u16 high_low; - s8 rate = index; - - new_rate = high = low = start_hi = IWL_RATE_INVALID; - - for (; ;) { - high_low = rs_get_adjacent_rate(priv, rate, rate_mask, - tbl->lq_type); - - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - /* - * Lower the "search" bit rate, to give new "search" mode - * approximately the same throughput as "active" if: - * - * 1) "Active" mode has been working modestly well (but not - * great), and expected "search" throughput (under perfect - * conditions) at candidate rate is above the actual - * measured "active" throughput (but less than expected - * "active" throughput under perfect conditions). - * OR - * 2) "Active" mode has been working perfectly or very well - * and expected "search" throughput (under perfect - * conditions) at candidate rate is above expected - * "active" throughput (under perfect conditions). - */ - if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && - ((active_sr > IWL_RATE_DECREASE_TH) && - (active_sr <= IWL_RATE_HIGH_TH) && - (tpt_tbl[rate] <= active_tpt))) || - ((active_sr >= IWL_RATE_SCALE_SWITCH) && - (tpt_tbl[rate] > active_tpt))) { - - /* (2nd or later pass) - * If we've already tried to raise the rate, and are - * now trying to lower it, use the higher rate. */ - if (start_hi != IWL_RATE_INVALID) { - new_rate = start_hi; - break; - } - - new_rate = rate; - - /* Loop again with lower rate */ - if (low != IWL_RATE_INVALID) - rate = low; - - /* Lower rate not available, use the original */ - else - break; - - /* Else try to raise the "search" rate to match "active" */ - } else { - /* (2nd or later pass) - * If we've already tried to lower the rate, and are - * now trying to raise it, use the lower rate. */ - if (new_rate != IWL_RATE_INVALID) - break; - - /* Loop again with higher rate */ - else if (high != IWL_RATE_INVALID) { - start_hi = high; - rate = high; - - /* Higher rate not available, use the original */ - } else { - new_rate = rate; - break; - } - } - } - - return new_rate; -} - -/* - * Set up search table for MIMO2 - */ -static int rs_switch_to_mimo2(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - s32 rate; - s8 is_green = lq_sta->is_green; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) - return -1; - - if (sta->smps_mode == IEEE80211_SMPS_STATIC) - return -1; - - /* Need both Tx chains/antennas to support MIMO */ - if (priv->hw_params.tx_chains_num < 2) - return -1; - - IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO2\n"); - - tbl->lq_type = LQ_MIMO2; - tbl->is_dup = lq_sta->is_dup; - tbl->action = 0; - tbl->max_search = IWL_MAX_SEARCH; - rate_mask = lq_sta->active_mimo2_rate; - - if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - rs_set_expected_tpt_table(lq_sta, tbl); - - rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE(priv, "LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); - - IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); - return 0; -} - -/* - * Set up search table for MIMO3 - */ -static int rs_switch_to_mimo3(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - s32 rate; - s8 is_green = lq_sta->is_green; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) - return -1; - - if (sta->smps_mode == IEEE80211_SMPS_STATIC) - return -1; - - /* Need both Tx chains/antennas to support MIMO */ - if (priv->hw_params.tx_chains_num < 3) - return -1; - - IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO3\n"); - - tbl->lq_type = LQ_MIMO3; - tbl->is_dup = lq_sta->is_dup; - tbl->action = 0; - tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; - rate_mask = lq_sta->active_mimo3_rate; - - if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - rs_set_expected_tpt_table(lq_sta, tbl); - - rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE(priv, "LQ: MIMO3 best rate %d mask %X\n", - rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); - - IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); - return 0; -} - -/* - * Set up search table for SISO - */ -static int rs_switch_to_siso(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - u8 is_green = lq_sta->is_green; - s32 rate; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) - return -1; - - IWL_DEBUG_RATE(priv, "LQ: try to switch to SISO\n"); - - tbl->is_dup = lq_sta->is_dup; - tbl->lq_type = LQ_SISO; - tbl->action = 0; - tbl->max_search = IWL_MAX_SEARCH; - rate_mask = lq_sta->active_siso_rate; - - if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - if (is_green) - tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ - - rs_set_expected_tpt_table(lq_sta, tbl); - rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE(priv, "LQ: get best rate %d mask %X\n", rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE(priv, "can not switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); - IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); - return 0; -} - -/* - * Try to switch to new modulation mode from legacy - */ -static void rs_move_legacy_other(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, - int index) -{ - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; - u8 tx_chains_num = priv->hw_params.tx_chains_num; - int ret = 0; - u8 update_search_tbl_counter = 0; - - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2) - tbl->action = IWL_LEGACY_SWITCH_SISO; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - valid_tx_ant = - first_antenna(priv->nvm_data->valid_tx_ant); - if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2 && - tbl->action != IWL_LEGACY_SWITCH_SISO) - tbl->action = IWL_LEGACY_SWITCH_SISO; - break; - default: - IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); - break; - } - - if (!iwl_ht_enabled(priv)) - /* stay in Legacy */ - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; - else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && - tbl->action > IWL_LEGACY_SWITCH_SISO) - tbl->action = IWL_LEGACY_SWITCH_SISO; - - /* configure as 1x1 if bt full concurrency */ - if (priv->bt_full_concurrent) { - if (!iwl_ht_enabled(priv)) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; - else if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) - tbl->action = IWL_LEGACY_SWITCH_SISO; - valid_tx_ant = - first_antenna(priv->nvm_data->valid_tx_ant); - } - - start_action = tbl->action; - for (; ;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_LEGACY_SWITCH_ANTENNA1: - case IWL_LEGACY_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(priv, "LQ: Legacy toggle Antenna\n"); - - if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 && - tx_chains_num <= 1) || - (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 && - tx_chains_num <= 2)) - break; - - /* Don't change antenna if success has been great */ - if (window->success_ratio >= IWL_RS_GOOD_RATIO && - !priv->bt_full_concurrent && - priv->bt_traffic_load == - IWL_BT_COEX_TRAFFIC_LOAD_NONE) - break; - - /* Set up search table to try other antenna */ - memcpy(search_tbl, tbl, sz); - - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, search_tbl)) { - update_search_tbl_counter = 1; - rs_set_expected_tpt_table(lq_sta, search_tbl); - goto out; - } - break; - case IWL_LEGACY_SWITCH_SISO: - IWL_DEBUG_RATE(priv, "LQ: Legacy switch to SISO\n"); - - /* Set up search table to try SISO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - ret = rs_switch_to_siso(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - - break; - case IWL_LEGACY_SWITCH_MIMO2_AB: - case IWL_LEGACY_SWITCH_MIMO2_AC: - case IWL_LEGACY_SWITCH_MIMO2_BC: - IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO2\n"); - - /* Set up search table to try MIMO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB) - search_tbl->ant_type = ANT_AB; - else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC) - search_tbl->ant_type = ANT_AC; - else - search_tbl->ant_type = ANT_BC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - break; - - case IWL_LEGACY_SWITCH_MIMO3_ABC: - IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO3\n"); - - /* Set up search table to try MIMO3 */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - search_tbl->ant_type = ANT_ABC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - break; - } - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - - } - search_tbl->lq_type = LQ_NONE; - return; - -out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; -} - -/* - * Try to switch to new modulation mode from SISO - */ -static void rs_move_siso_to_other(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, int index) -{ - u8 is_green = lq_sta->is_green; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; - u8 tx_chains_num = priv->hw_params.tx_chains_num; - u8 update_search_tbl_counter = 0; - int ret; - - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_SISO_SWITCH_ANTENNA2) - tbl->action = IWL_SISO_SWITCH_MIMO2_AB; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - valid_tx_ant = - first_antenna(priv->nvm_data->valid_tx_ant); - if (tbl->action != IWL_SISO_SWITCH_ANTENNA1) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - break; - default: - IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); - break; - } - - if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && - tbl->action > IWL_SISO_SWITCH_ANTENNA2) { - /* stay in SISO */ - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - } - - /* configure as 1x1 if bt full concurrency */ - if (priv->bt_full_concurrent) { - valid_tx_ant = - first_antenna(priv->nvm_data->valid_tx_ant); - if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - } - - start_action = tbl->action; - for (;;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_SISO_SWITCH_ANTENNA1: - case IWL_SISO_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(priv, "LQ: SISO toggle Antenna\n"); - if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 && - tx_chains_num <= 1) || - (tbl->action == IWL_SISO_SWITCH_ANTENNA2 && - tx_chains_num <= 2)) - break; - - if (window->success_ratio >= IWL_RS_GOOD_RATIO && - !priv->bt_full_concurrent && - priv->bt_traffic_load == - IWL_BT_COEX_TRAFFIC_LOAD_NONE) - break; - - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, search_tbl)) { - update_search_tbl_counter = 1; - goto out; - } - break; - case IWL_SISO_SWITCH_MIMO2_AB: - case IWL_SISO_SWITCH_MIMO2_AC: - case IWL_SISO_SWITCH_MIMO2_BC: - IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO2\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB) - search_tbl->ant_type = ANT_AB; - else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC) - search_tbl->ant_type = ANT_AC; - else - search_tbl->ant_type = ANT_BC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - break; - case IWL_SISO_SWITCH_GI: - if (!tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - break; - - IWL_DEBUG_RATE(priv, "LQ: SISO toggle SGI/NGI\n"); - - memcpy(search_tbl, tbl, sz); - if (is_green) { - if (!tbl->is_SGI) - break; - else - IWL_ERR(priv, - "SGI was set in GF+SISO\n"); - } - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = - rate_n_flags_from_tbl(priv, search_tbl, - index, is_green); - update_search_tbl_counter = 1; - goto out; - case IWL_SISO_SWITCH_MIMO3_ABC: - IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO3\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - search_tbl->ant_type = ANT_ABC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - break; - } - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return; - - out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; -} - -/* - * Try to switch to new modulation mode from MIMO2 - */ -static void rs_move_mimo2_to_other(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, int index) -{ - s8 is_green = lq_sta->is_green; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; - u8 tx_chains_num = priv->hw_params.tx_chains_num; - u8 update_search_tbl_counter = 0; - int ret; - - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - if (tbl->action != IWL_MIMO2_SWITCH_SISO_A) - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_MIMO2_SWITCH_SISO_B || - tbl->action == IWL_MIMO2_SWITCH_SISO_C) - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - break; - default: - IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); - break; - } - - if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && - (tbl->action < IWL_MIMO2_SWITCH_SISO_A || - tbl->action > IWL_MIMO2_SWITCH_SISO_C)) { - /* switch in SISO */ - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - } - - /* configure as 1x1 if bt full concurrency */ - if (priv->bt_full_concurrent && - (tbl->action < IWL_MIMO2_SWITCH_SISO_A || - tbl->action > IWL_MIMO2_SWITCH_SISO_C)) - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - - start_action = tbl->action; - for (;;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_MIMO2_SWITCH_ANTENNA1: - case IWL_MIMO2_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle Antennas\n"); - - if (tx_chains_num <= 2) - break; - - if (window->success_ratio >= IWL_RS_GOOD_RATIO) - break; - - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, search_tbl)) { - update_search_tbl_counter = 1; - goto out; - } - break; - case IWL_MIMO2_SWITCH_SISO_A: - case IWL_MIMO2_SWITCH_SISO_B: - case IWL_MIMO2_SWITCH_SISO_C: - IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to SISO\n"); - - /* Set up new search table for SISO */ - memcpy(search_tbl, tbl, sz); - - if (tbl->action == IWL_MIMO2_SWITCH_SISO_A) - search_tbl->ant_type = ANT_A; - else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B) - search_tbl->ant_type = ANT_B; - else - search_tbl->ant_type = ANT_C; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_siso(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - - break; - - case IWL_MIMO2_SWITCH_GI: - if (!tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - break; - - IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle SGI/NGI\n"); - - /* Set up new search table for MIMO2 */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - /* - * If active table already uses the fastest possible - * modulation (dual stream with short guard interval), - * and it's working well, there's no need to look - * for a better type of modulation! - */ - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = - rate_n_flags_from_tbl(priv, search_tbl, - index, is_green); - update_search_tbl_counter = 1; - goto out; - - case IWL_MIMO2_SWITCH_MIMO3_ABC: - IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to MIMO3\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - search_tbl->ant_type = ANT_ABC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - - break; - } - tbl->action++; - if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) - tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return; - out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) - tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - -} - -/* - * Try to switch to new modulation mode from MIMO3 - */ -static void rs_move_mimo3_to_other(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, int index) -{ - s8 is_green = lq_sta->is_green; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = priv->nvm_data->valid_tx_ant; - u8 tx_chains_num = priv->hw_params.tx_chains_num; - int ret; - u8 update_search_tbl_counter = 0; - - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - if (tbl->action != IWL_MIMO3_SWITCH_SISO_A) - tbl->action = IWL_MIMO3_SWITCH_SISO_A; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_MIMO3_SWITCH_SISO_B || - tbl->action == IWL_MIMO3_SWITCH_SISO_C) - tbl->action = IWL_MIMO3_SWITCH_SISO_A; - break; - default: - IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); - break; - } - - if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && - (tbl->action < IWL_MIMO3_SWITCH_SISO_A || - tbl->action > IWL_MIMO3_SWITCH_SISO_C)) { - /* switch in SISO */ - tbl->action = IWL_MIMO3_SWITCH_SISO_A; - } - - /* configure as 1x1 if bt full concurrency */ - if (priv->bt_full_concurrent && - (tbl->action < IWL_MIMO3_SWITCH_SISO_A || - tbl->action > IWL_MIMO3_SWITCH_SISO_C)) - tbl->action = IWL_MIMO3_SWITCH_SISO_A; - - start_action = tbl->action; - for (;;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_MIMO3_SWITCH_ANTENNA1: - case IWL_MIMO3_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle Antennas\n"); - - if (tx_chains_num <= 3) - break; - - if (window->success_ratio >= IWL_RS_GOOD_RATIO) - break; - - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, search_tbl)) - goto out; - break; - case IWL_MIMO3_SWITCH_SISO_A: - case IWL_MIMO3_SWITCH_SISO_B: - case IWL_MIMO3_SWITCH_SISO_C: - IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to SISO\n"); - - /* Set up new search table for SISO */ - memcpy(search_tbl, tbl, sz); - - if (tbl->action == IWL_MIMO3_SWITCH_SISO_A) - search_tbl->ant_type = ANT_A; - else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B) - search_tbl->ant_type = ANT_B; - else - search_tbl->ant_type = ANT_C; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_siso(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - - break; - - case IWL_MIMO3_SWITCH_MIMO2_AB: - case IWL_MIMO3_SWITCH_MIMO2_AC: - case IWL_MIMO3_SWITCH_MIMO2_BC: - IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to MIMO2\n"); - - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB) - search_tbl->ant_type = ANT_AB; - else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC) - search_tbl->ant_type = ANT_AC; - else - search_tbl->ant_type = ANT_BC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - - break; - - case IWL_MIMO3_SWITCH_GI: - if (!tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - break; - - IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle SGI/NGI\n"); - - /* Set up new search table for MIMO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - /* - * If active table already uses the fastest possible - * modulation (dual stream with short guard interval), - * and it's working well, there's no need to look - * for a better type of modulation! - */ - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = - rate_n_flags_from_tbl(priv, search_tbl, - index, is_green); - update_search_tbl_counter = 1; - goto out; - } - tbl->action++; - if (tbl->action > IWL_MIMO3_SWITCH_GI) - tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return; - out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_MIMO3_SWITCH_GI) - tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; -} - -/* - * Check whether we should continue using same modulation mode, or - * begin search for a new mode, based on: - * 1) # tx successes or failures while using this mode - * 2) # times calling this function - * 3) elapsed time in this mode (not used, for now) - */ -static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) -{ - struct iwl_scale_tbl_info *tbl; - int i; - int active_tbl; - int flush_interval_passed = 0; - struct iwl_priv *priv; - - priv = lq_sta->drv; - active_tbl = lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - - /* If we've been disallowing search, see if we should now allow it */ - if (lq_sta->stay_in_tbl) { - - /* Elapsed time using current modulation mode */ - if (lq_sta->flush_timer) - flush_interval_passed = - time_after(jiffies, - (unsigned long)(lq_sta->flush_timer + - IWL_RATE_SCALE_FLUSH_INTVL)); - - /* - * Check if we should allow search for new modulation mode. - * If many frames have failed or succeeded, or we've used - * this same modulation for a long time, allow search, and - * reset history stats that keep track of whether we should - * allow a new search. Also (below) reset all bitmaps and - * stats in active history. - */ - if (force_search || - (lq_sta->total_failed > lq_sta->max_failure_limit) || - (lq_sta->total_success > lq_sta->max_success_limit) || - ((!lq_sta->search_better_tbl) && (lq_sta->flush_timer) - && (flush_interval_passed))) { - IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n", - lq_sta->total_failed, - lq_sta->total_success, - flush_interval_passed); - - /* Allow search for new mode */ - lq_sta->stay_in_tbl = 0; /* only place reset */ - lq_sta->total_failed = 0; - lq_sta->total_success = 0; - lq_sta->flush_timer = 0; - - /* - * Else if we've used this modulation mode enough repetitions - * (regardless of elapsed time or success/failure), reset - * history bitmaps and rate-specific stats for all rates in - * active table. - */ - } else { - lq_sta->table_count++; - if (lq_sta->table_count >= - lq_sta->table_count_limit) { - lq_sta->table_count = 0; - - IWL_DEBUG_RATE(priv, "LQ: stay in table clear win\n"); - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window( - &(tbl->win[i])); - } - } - - /* If transitioning to allow "search", reset all history - * bitmaps and stats in active table (this will become the new - * "search" table). */ - if (!lq_sta->stay_in_tbl) { - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(tbl->win[i])); - } - } -} - -/* - * setup rate table in uCode - */ -static void rs_update_rate_tbl(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - int index, u8 is_green) -{ - u32 rate; - - /* Update uCode's rate table. */ - rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); - rs_fill_link_cmd(priv, lq_sta, rate); - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); -} - -/* - * Do rate scaling and search for new modulation mode. - */ -static void rs_rate_scale_perform(struct iwl_priv *priv, - struct sk_buff *skb, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta) -{ - struct ieee80211_hw *hw = priv->hw; - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - int low = IWL_RATE_INVALID; - int high = IWL_RATE_INVALID; - int index; - int i; - struct iwl_rate_scale_data *window = NULL; - int current_tpt = IWL_INVALID_VALUE; - int low_tpt = IWL_INVALID_VALUE; - int high_tpt = IWL_INVALID_VALUE; - u32 fail_count; - s8 scale_action = 0; - u16 rate_mask; - u8 update_lq = 0; - struct iwl_scale_tbl_info *tbl, *tbl1; - u16 rate_scale_index_msk = 0; - u8 is_green = 0; - u8 active_tbl = 0; - u8 done_search = 0; - u16 high_low; - s32 sr; - u8 tid = IWL_MAX_TID_COUNT; - struct iwl_tid_data *tid_data; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n"); - - /* Send management frames and NO_ACK data using lowest rate. */ - /* TODO: this could probably be improved.. */ - if (!ieee80211_is_data(hdr->frame_control) || - info->flags & IEEE80211_TX_CTL_NO_ACK) - return; - - lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; - - tid = rs_tl_add_packet(lq_sta, hdr); - if ((tid != IWL_MAX_TID_COUNT) && - (lq_sta->tx_agg_tid_en & (1 << tid))) { - tid_data = &priv->tid_data[lq_sta->lq.sta_id][tid]; - if (tid_data->agg.state == IWL_AGG_OFF) - lq_sta->is_agg = 0; - else - lq_sta->is_agg = 1; - } else - lq_sta->is_agg = 0; - - /* - * Select rate-scale / modulation-mode table to work with in - * the rest of this function: "search" if searching for better - * modulation mode, or "active" if doing rate scaling within a mode. - */ - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - if (is_legacy(tbl->lq_type)) - lq_sta->is_green = 0; - else - lq_sta->is_green = rs_use_green(sta); - is_green = lq_sta->is_green; - - /* current tx rate */ - index = lq_sta->last_txrate_idx; - - IWL_DEBUG_RATE(priv, "Rate scale index %d for type %d\n", index, - tbl->lq_type); - - /* rates available for this association, and for modulation mode */ - rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); - - IWL_DEBUG_RATE(priv, "mask 0x%04X\n", rate_mask); - - /* mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { - if (lq_sta->band == IEEE80211_BAND_5GHZ) - /* supp_rates has no CCK bits in A mode */ - rate_scale_index_msk = (u16) (rate_mask & - (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); - else - rate_scale_index_msk = (u16) (rate_mask & - lq_sta->supp_rates); - - } else - rate_scale_index_msk = rate_mask; - - if (!rate_scale_index_msk) - rate_scale_index_msk = rate_mask; - - if (!((1 << index) & rate_scale_index_msk)) { - IWL_ERR(priv, "Current Rate is not valid\n"); - if (lq_sta->search_better_tbl) { - /* revert to active table if search table is not valid*/ - tbl->lq_type = LQ_NONE; - lq_sta->search_better_tbl = 0; - tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - /* get "active" rate info */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - rs_update_rate_tbl(priv, ctx, lq_sta, tbl, - index, is_green); - } - return; - } - - /* Get expected throughput table and history window for current rate */ - if (!tbl->expected_tpt) { - IWL_ERR(priv, "tbl->expected_tpt is NULL\n"); - return; - } - - /* force user max rate if set by user */ - if ((lq_sta->max_rate_idx != -1) && - (lq_sta->max_rate_idx < index)) { - index = lq_sta->max_rate_idx; - update_lq = 1; - window = &(tbl->win[index]); - goto lq_update; - } - - window = &(tbl->win[index]); - - /* - * If there is not enough history to calculate actual average - * throughput, keep analyzing results of more tx frames, without - * changing rate or mode (bypass most of the rest of this function). - * Set up new rate table in uCode only if old rate is not supported - * in current association (use new rate found above). - */ - fail_count = window->counter - window->success_counter; - if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && - (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { - IWL_DEBUG_RATE(priv, "LQ: still below TH. succ=%d total=%d " - "for index %d\n", - window->success_counter, window->counter, index); - - /* Can't calculate this yet; not enough history */ - window->average_tpt = IWL_INVALID_VALUE; - - /* Should we stay with this modulation mode, - * or search for a new one? */ - rs_stay_in_table(lq_sta, false); - - goto out; - } - /* Else we have enough samples; calculate estimate of - * actual average throughput */ - if (window->average_tpt != ((window->success_ratio * - tbl->expected_tpt[index] + 64) / 128)) { - IWL_ERR(priv, "expected_tpt should have been calculated by now\n"); - window->average_tpt = ((window->success_ratio * - tbl->expected_tpt[index] + 64) / 128); - } - - /* If we are searching for better modulation mode, check success. */ - if (lq_sta->search_better_tbl && - (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI)) { - /* If good success, continue using the "search" mode; - * no need to send new link quality command, since we're - * continuing to use the setup that we've been trying. */ - if (window->average_tpt > lq_sta->last_tpt) { - - IWL_DEBUG_RATE(priv, "LQ: SWITCHING TO NEW TABLE " - "suc=%d cur-tpt=%d old-tpt=%d\n", - window->success_ratio, - window->average_tpt, - lq_sta->last_tpt); - - if (!is_legacy(tbl->lq_type)) - lq_sta->enable_counter = 1; - - /* Swap tables; "search" becomes "active" */ - lq_sta->active_tbl = active_tbl; - current_tpt = window->average_tpt; - - /* Else poor success; go back to mode in "active" table */ - } else { - - IWL_DEBUG_RATE(priv, "LQ: GOING BACK TO THE OLD TABLE " - "suc=%d cur-tpt=%d old-tpt=%d\n", - window->success_ratio, - window->average_tpt, - lq_sta->last_tpt); - - /* Nullify "search" table */ - tbl->lq_type = LQ_NONE; - - /* Revert to "active" table */ - active_tbl = lq_sta->active_tbl; - tbl = &(lq_sta->lq_info[active_tbl]); - - /* Revert to "active" rate and throughput info */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - current_tpt = lq_sta->last_tpt; - - /* Need to set up a new rate table in uCode */ - update_lq = 1; - } - - /* Either way, we've made a decision; modulation mode - * search is done, allow rate adjustment next time. */ - lq_sta->search_better_tbl = 0; - done_search = 1; /* Don't switch modes below! */ - goto lq_update; - } - - /* (Else) not in search of better modulation mode, try for better - * starting rate, while staying in this mode. */ - high_low = rs_get_adjacent_rate(priv, index, rate_scale_index_msk, - tbl->lq_type); - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - /* If user set max rate, dont allow higher than user constrain */ - if ((lq_sta->max_rate_idx != -1) && - (lq_sta->max_rate_idx < high)) - high = IWL_RATE_INVALID; - - sr = window->success_ratio; - - /* Collect measured throughputs for current and adjacent rates */ - current_tpt = window->average_tpt; - if (low != IWL_RATE_INVALID) - low_tpt = tbl->win[low].average_tpt; - if (high != IWL_RATE_INVALID) - high_tpt = tbl->win[high].average_tpt; - - scale_action = 0; - - /* Too many failures, decrease rate */ - if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) { - IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n"); - scale_action = -1; - - /* No throughput measured yet for adjacent rates; try increase. */ - } else if ((low_tpt == IWL_INVALID_VALUE) && - (high_tpt == IWL_INVALID_VALUE)) { - - if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) - scale_action = 1; - else if (low != IWL_RATE_INVALID) - scale_action = 0; - } - - /* Both adjacent throughputs are measured, but neither one has better - * throughput; we're using the best rate, don't change it! */ - else if ((low_tpt != IWL_INVALID_VALUE) && - (high_tpt != IWL_INVALID_VALUE) && - (low_tpt < current_tpt) && - (high_tpt < current_tpt)) - scale_action = 0; - - /* At least one adjacent rate's throughput is measured, - * and may have better performance. */ - else { - /* Higher adjacent rate's throughput is measured */ - if (high_tpt != IWL_INVALID_VALUE) { - /* Higher rate has better throughput */ - if (high_tpt > current_tpt && - sr >= IWL_RATE_INCREASE_TH) { - scale_action = 1; - } else { - scale_action = 0; - } - - /* Lower adjacent rate's throughput is measured */ - } else if (low_tpt != IWL_INVALID_VALUE) { - /* Lower rate has better throughput */ - if (low_tpt > current_tpt) { - IWL_DEBUG_RATE(priv, - "decrease rate because of low tpt\n"); - scale_action = -1; - } else if (sr >= IWL_RATE_INCREASE_TH) { - scale_action = 1; - } - } - } - - /* Sanity check; asked for decrease, but success rate or throughput - * has been good at old rate. Don't change it. */ - if ((scale_action == -1) && (low != IWL_RATE_INVALID) && - ((sr > IWL_RATE_HIGH_TH) || - (current_tpt > (100 * tbl->expected_tpt[low])))) - scale_action = 0; - if (!iwl_ht_enabled(priv) && !is_legacy(tbl->lq_type)) - scale_action = -1; - if (iwl_tx_ant_restriction(priv) != IWL_ANT_OK_MULTI && - (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) - scale_action = -1; - - if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && - (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { - if (lq_sta->last_bt_traffic > priv->bt_traffic_load) { - /* - * don't set scale_action, don't want to scale up if - * the rate scale doesn't otherwise think that is a - * good idea. - */ - } else if (lq_sta->last_bt_traffic <= priv->bt_traffic_load) { - scale_action = -1; - } - } - lq_sta->last_bt_traffic = priv->bt_traffic_load; - - if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && - (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { - /* search for a new modulation */ - rs_stay_in_table(lq_sta, true); - goto lq_update; - } - - switch (scale_action) { - case -1: - /* Decrease starting rate, update uCode's rate table */ - if (low != IWL_RATE_INVALID) { - update_lq = 1; - index = low; - } - - break; - case 1: - /* Increase starting rate, update uCode's rate table */ - if (high != IWL_RATE_INVALID) { - update_lq = 1; - index = high; - } - - break; - case 0: - /* No change */ - default: - break; - } - - IWL_DEBUG_RATE(priv, "choose rate scale index %d action %d low %d " - "high %d type %d\n", - index, scale_action, low, high, tbl->lq_type); - -lq_update: - /* Replace uCode's rate table for the destination station. */ - if (update_lq) - rs_update_rate_tbl(priv, ctx, lq_sta, tbl, index, is_green); - - if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI) { - /* Should we stay with this modulation mode, - * or search for a new one? */ - rs_stay_in_table(lq_sta, false); - } - /* - * Search for new modulation mode if we're: - * 1) Not changing rates right now - * 2) Not just finishing up a search - * 3) Allowing a new search - */ - if (!update_lq && !done_search && !lq_sta->stay_in_tbl && window->counter) { - /* Save current throughput to compare with "search" throughput*/ - lq_sta->last_tpt = current_tpt; - - /* Select a new "search" modulation mode to try. - * If one is found, set up the new "search" table. */ - if (is_legacy(tbl->lq_type)) - rs_move_legacy_other(priv, lq_sta, conf, sta, index); - else if (is_siso(tbl->lq_type)) - rs_move_siso_to_other(priv, lq_sta, conf, sta, index); - else if (is_mimo2(tbl->lq_type)) - rs_move_mimo2_to_other(priv, lq_sta, conf, sta, index); - else - rs_move_mimo3_to_other(priv, lq_sta, conf, sta, index); - - /* If new "search" mode was selected, set up in uCode table */ - if (lq_sta->search_better_tbl) { - /* Access the "search" table, clear its history. */ - tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(tbl->win[i])); - - /* Use new "search" start rate */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - - IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n", - tbl->current_rate, index); - rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); - } else - done_search = 1; - } - - if (done_search && !lq_sta->stay_in_tbl) { - /* If the "active" (non-search) mode was legacy, - * and we've tried switching antennas, - * but we haven't been able to try HT modes (not available), - * stay with best antenna legacy modulation for a while - * before next round of mode comparisons. */ - tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); - if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) && - lq_sta->action_counter > tbl1->max_search) { - IWL_DEBUG_RATE(priv, "LQ: STAY in legacy table\n"); - rs_set_stay_in_table(priv, 1, lq_sta); - } - - /* If we're in an HT mode, and all 3 mode switch actions - * have been tried and compared, stay in this best modulation - * mode for a while before next round of mode comparisons. */ - if (lq_sta->enable_counter && - (lq_sta->action_counter >= tbl1->max_search) && - iwl_ht_enabled(priv)) { - if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && - (lq_sta->tx_agg_tid_en & (1 << tid)) && - (tid != IWL_MAX_TID_COUNT)) { - u8 sta_id = lq_sta->lq.sta_id; - tid_data = &priv->tid_data[sta_id][tid]; - if (tid_data->agg.state == IWL_AGG_OFF) { - IWL_DEBUG_RATE(priv, - "try to aggregate tid %d\n", - tid); - rs_tl_turn_on_agg(priv, tid, - lq_sta, sta); - } - } - rs_set_stay_in_table(priv, 0, lq_sta); - } - } - -out: - tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); - lq_sta->last_txrate_idx = index; -} - -/** - * rs_initialize_lq - Initialize a station's hardware rate table - * - * The uCode's station table contains a table of fallback rates - * for automatic fallback during transmission. - * - * NOTE: This sets up a default set of values. These will be replaced later - * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of - * rc80211_simple. - * - * NOTE: Run REPLY_ADD_STA command to set up station table entry, before - * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, - * which requires station table entry to exist). - */ -static void rs_initialize_lq(struct iwl_priv *priv, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta) -{ - struct iwl_scale_tbl_info *tbl; - int rate_idx; - int i; - u32 rate; - u8 use_green = rs_use_green(sta); - u8 active_tbl = 0; - u8 valid_tx_ant; - struct iwl_station_priv *sta_priv; - struct iwl_rxon_context *ctx; - - if (!sta || !lq_sta) - return; - - sta_priv = (void *)sta->drv_priv; - ctx = sta_priv->ctx; - - i = lq_sta->last_txrate_idx; - - valid_tx_ant = priv->nvm_data->valid_tx_ant; - - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - - if ((i < 0) || (i >= IWL_RATE_COUNT)) - i = 0; - - rate = iwl_rates[i].plcp; - tbl->ant_type = first_antenna(valid_tx_ant); - rate |= tbl->ant_type << RATE_MCS_ANT_POS; - - if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE) - rate |= RATE_MCS_CCK_MSK; - - rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx); - if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) - rs_toggle_antenna(valid_tx_ant, &rate, tbl); - - rate = rate_n_flags_from_tbl(priv, tbl, rate_idx, use_green); - tbl->current_rate = rate; - rs_set_expected_tpt_table(lq_sta, tbl); - rs_fill_link_cmd(NULL, lq_sta, rate); - priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, 0, true); -} - -static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, - struct ieee80211_tx_rate_control *txrc) -{ - - struct sk_buff *skb = txrc->skb; - struct ieee80211_supported_band *sband = txrc->sband; - struct iwl_op_mode *op_mode __maybe_unused = - (struct iwl_op_mode *)priv_r; - struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_lq_sta *lq_sta = priv_sta; - int rate_idx; - - IWL_DEBUG_RATE_LIMIT(priv, "rate scale calculate new rate for skb\n"); - - /* Get max rate if user set max rate */ - if (lq_sta) { - lq_sta->max_rate_idx = txrc->max_rate_idx; - if ((sband->band == IEEE80211_BAND_5GHZ) && - (lq_sta->max_rate_idx != -1)) - lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE; - if ((lq_sta->max_rate_idx < 0) || - (lq_sta->max_rate_idx >= IWL_RATE_COUNT)) - lq_sta->max_rate_idx = -1; - } - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (lq_sta && !lq_sta->drv) { - IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); - priv_sta = NULL; - } - - /* Send management frames and NO_ACK data using lowest rate. */ - if (rate_control_send_low(sta, priv_sta, txrc)) - return; - - rate_idx = lq_sta->last_txrate_idx; - - if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { - rate_idx -= IWL_FIRST_OFDM_RATE; - /* 6M and 9M shared same MCS index */ - rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; - if (rs_extract_rate(lq_sta->last_rate_n_flags) >= - IWL_RATE_MIMO3_6M_PLCP) - rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM); - else if (rs_extract_rate(lq_sta->last_rate_n_flags) >= - IWL_RATE_MIMO2_6M_PLCP) - rate_idx = rate_idx + MCS_INDEX_PER_STREAM; - info->control.rates[0].flags = IEEE80211_TX_RC_MCS; - if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI; - if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA; - if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD; - } else { - /* Check for invalid rates */ - if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) || - ((sband->band == IEEE80211_BAND_5GHZ) && - (rate_idx < IWL_FIRST_OFDM_RATE))) - rate_idx = rate_lowest_index(sband, sta); - /* On valid 5 GHz rate, adjust index */ - else if (sband->band == IEEE80211_BAND_5GHZ) - rate_idx -= IWL_FIRST_OFDM_RATE; - info->control.rates[0].flags = 0; - } - info->control.rates[0].idx = rate_idx; - info->control.rates[0].count = 1; -} - -static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta, - gfp_t gfp) -{ - struct iwl_station_priv *sta_priv = (struct iwl_station_priv *) sta->drv_priv; - struct iwl_op_mode *op_mode __maybe_unused = - (struct iwl_op_mode *)priv_rate; - struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); - - IWL_DEBUG_RATE(priv, "create station rate scale window\n"); - - return &sta_priv->lq_sta; -} - -/* - * Called after adding a new station to initialize rate scaling - */ -void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id) -{ - int i, j; - struct ieee80211_hw *hw = priv->hw; - struct ieee80211_conf *conf = &priv->hw->conf; - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct iwl_station_priv *sta_priv; - struct iwl_lq_sta *lq_sta; - struct ieee80211_supported_band *sband; - unsigned long supp; /* must be unsigned long for for_each_set_bit */ - - sta_priv = (struct iwl_station_priv *) sta->drv_priv; - lq_sta = &sta_priv->lq_sta; - sband = hw->wiphy->bands[conf->chandef.chan->band]; - - - lq_sta->lq.sta_id = sta_id; - - for (j = 0; j < LQ_SIZE; j++) - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); - - lq_sta->flush_timer = 0; - lq_sta->supp_rates = sta->supp_rates[sband->band]; - - IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n", - sta_id); - /* TODO: what is a good starting rate for STA? About middle? Maybe not - * the lowest or the highest rate.. Could consider using RSSI from - * previous packets? Need to have IEEE 802.1X auth succeed immediately - * after assoc.. */ - - lq_sta->is_dup = 0; - lq_sta->max_rate_idx = -1; - lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; - lq_sta->is_green = rs_use_green(sta); - lq_sta->band = sband->band; - /* - * active legacy rates as per supported rates bitmap - */ - supp = sta->supp_rates[sband->band]; - lq_sta->active_legacy_rate = 0; - for_each_set_bit(i, &supp, BITS_PER_LONG) - lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); - - /* - * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), - * supp_rates[] does not; shift to convert format, force 9 MBits off. - */ - lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; - lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; - lq_sta->active_siso_rate &= ~((u16)0x2); - lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; - - /* Same here */ - lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; - lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; - lq_sta->active_mimo2_rate &= ~((u16)0x2); - lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; - - lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1; - lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1; - lq_sta->active_mimo3_rate &= ~((u16)0x2); - lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE; - - IWL_DEBUG_RATE(priv, "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n", - lq_sta->active_siso_rate, - lq_sta->active_mimo2_rate, - lq_sta->active_mimo3_rate); - - /* These values will be overridden later */ - lq_sta->lq.general_params.single_stream_ant_msk = - first_antenna(priv->nvm_data->valid_tx_ant); - lq_sta->lq.general_params.dual_stream_ant_msk = - priv->nvm_data->valid_tx_ant & - ~first_antenna(priv->nvm_data->valid_tx_ant); - if (!lq_sta->lq.general_params.dual_stream_ant_msk) { - lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; - } else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) { - lq_sta->lq.general_params.dual_stream_ant_msk = - priv->nvm_data->valid_tx_ant; - } - - /* as default allow aggregation for all tids */ - lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; - lq_sta->drv = priv; - - /* Set last_txrate_idx to lowest rate */ - lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); - if (sband->band == IEEE80211_BAND_5GHZ) - lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; - lq_sta->is_agg = 0; -#ifdef CONFIG_MAC80211_DEBUGFS - lq_sta->dbg_fixed_rate = 0; -#endif - - rs_initialize_lq(priv, sta, lq_sta); -} - -static void rs_fill_link_cmd(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, u32 new_rate) -{ - struct iwl_scale_tbl_info tbl_type; - int index = 0; - int rate_idx; - int repeat_rate = 0; - u8 ant_toggle_cnt = 0; - u8 use_ht_possible = 1; - u8 valid_tx_ant = 0; - struct iwl_station_priv *sta_priv = - container_of(lq_sta, struct iwl_station_priv, lq_sta); - struct iwl_link_quality_cmd *lq_cmd = &lq_sta->lq; - - /* Override starting rate (index 0) if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); - - /* Interpret new_rate (rate_n_flags) */ - rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, - &tbl_type, &rate_idx); - - if (priv && priv->bt_full_concurrent) { - /* 1x1 only */ - tbl_type.ant_type = - first_antenna(priv->nvm_data->valid_tx_ant); - } - - /* How many times should we repeat the initial rate? */ - if (is_legacy(tbl_type.lq_type)) { - ant_toggle_cnt = 1; - repeat_rate = IWL_NUMBER_TRY; - } else { - repeat_rate = min(IWL_HT_NUMBER_TRY, - LINK_QUAL_AGG_DISABLE_START_DEF - 1); - } - - lq_cmd->general_params.mimo_delimiter = - is_mimo(tbl_type.lq_type) ? 1 : 0; - - /* Fill 1st table entry (index 0) */ - lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); - - if (num_of_ant(tbl_type.ant_type) == 1) { - lq_cmd->general_params.single_stream_ant_msk = - tbl_type.ant_type; - } else if (num_of_ant(tbl_type.ant_type) == 2) { - lq_cmd->general_params.dual_stream_ant_msk = - tbl_type.ant_type; - } /* otherwise we don't modify the existing value */ - - index++; - repeat_rate--; - if (priv) { - if (priv->bt_full_concurrent) - valid_tx_ant = ANT_A; - else - valid_tx_ant = priv->nvm_data->valid_tx_ant; - } - - /* Fill rest of rate table */ - while (index < LINK_QUAL_MAX_RETRY_NUM) { - /* Repeat initial/next rate. - * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. - * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ - while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { - if (is_legacy(tbl_type.lq_type)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (priv && - rs_toggle_antenna(valid_tx_ant, - &new_rate, &tbl_type)) - ant_toggle_cnt = 1; - } - - /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); - - /* Fill next table entry */ - lq_cmd->rs_table[index].rate_n_flags = - cpu_to_le32(new_rate); - repeat_rate--; - index++; - } - - rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, - &rate_idx); - - if (priv && priv->bt_full_concurrent) { - /* 1x1 only */ - tbl_type.ant_type = - first_antenna(priv->nvm_data->valid_tx_ant); - } - - /* Indicate to uCode which entries might be MIMO. - * If initial rate was MIMO, this will finally end up - * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ - if (is_mimo(tbl_type.lq_type)) - lq_cmd->general_params.mimo_delimiter = index; - - /* Get next rate */ - new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, - use_ht_possible); - - /* How many times should we repeat the next rate? */ - if (is_legacy(tbl_type.lq_type)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (priv && - rs_toggle_antenna(valid_tx_ant, - &new_rate, &tbl_type)) - ant_toggle_cnt = 1; - - repeat_rate = IWL_NUMBER_TRY; - } else { - repeat_rate = IWL_HT_NUMBER_TRY; - } - - /* Don't allow HT rates after next pass. - * rs_get_lower_rate() will change type to LQ_A or LQ_G. */ - use_ht_possible = 0; - - /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); - - /* Fill next table entry */ - lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); - - index++; - repeat_rate--; - } - - lq_cmd->agg_params.agg_frame_cnt_limit = - sta_priv->max_agg_bufsize ?: LINK_QUAL_AGG_FRAME_LIMIT_DEF; - lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; - - lq_cmd->agg_params.agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - /* - * overwrite if needed, pass aggregation time limit - * to uCode in uSec - */ - if (priv && priv->lib->bt_params && - priv->lib->bt_params->agg_time_limit && - priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) - lq_cmd->agg_params.agg_time_limit = - cpu_to_le16(priv->lib->bt_params->agg_time_limit); -} - -static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) -{ - return hw->priv; -} -/* rate scale requires free function to be implemented */ -static void rs_free(void *priv_rate) -{ - return; -} - -static void rs_free_sta(void *priv_r, struct ieee80211_sta *sta, - void *priv_sta) -{ - struct iwl_op_mode *op_mode __maybe_unused = priv_r; - struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); - - IWL_DEBUG_RATE(priv, "enter\n"); - IWL_DEBUG_RATE(priv, "leave\n"); -} - -#ifdef CONFIG_MAC80211_DEBUGFS -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags, int index) -{ - struct iwl_priv *priv; - u8 valid_tx_ant; - u8 ant_sel_tx; - - priv = lq_sta->drv; - valid_tx_ant = priv->nvm_data->valid_tx_ant; - if (lq_sta->dbg_fixed_rate) { - ant_sel_tx = - ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) - >> RATE_MCS_ANT_POS); - if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { - *rate_n_flags = lq_sta->dbg_fixed_rate; - IWL_DEBUG_RATE(priv, "Fixed rate ON\n"); - } else { - lq_sta->dbg_fixed_rate = 0; - IWL_ERR(priv, - "Invalid antenna selection 0x%X, Valid is 0x%X\n", - ant_sel_tx, valid_tx_ant); - IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); - } - } else { - IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); - } -} - -static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct iwl_lq_sta *lq_sta = file->private_data; - struct iwl_priv *priv; - char buf[64]; - size_t buf_size; - u32 parsed_rate; - - - priv = lq_sta->drv; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (sscanf(buf, "%x", &parsed_rate) == 1) - lq_sta->dbg_fixed_rate = parsed_rate; - else - lq_sta->dbg_fixed_rate = 0; - - rs_program_fix_rate(priv, lq_sta); - - return count; -} - -static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - char *buff; - int desc = 0; - int i = 0; - int index = 0; - ssize_t ret; - - struct iwl_lq_sta *lq_sta = file->private_data; - struct iwl_priv *priv; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - - priv = lq_sta->drv; - buff = kmalloc(1024, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); - desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n", - lq_sta->total_failed, lq_sta->total_success, - lq_sta->active_legacy_rate); - desc += sprintf(buff+desc, "fixed rate 0x%X\n", - lq_sta->dbg_fixed_rate); - desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", - (priv->nvm_data->valid_tx_ant & ANT_A) ? "ANT_A," : "", - (priv->nvm_data->valid_tx_ant & ANT_B) ? "ANT_B," : "", - (priv->nvm_data->valid_tx_ant & ANT_C) ? "ANT_C" : ""); - desc += sprintf(buff+desc, "lq type %s\n", - (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); - if (is_Ht(tbl->lq_type)) { - desc += sprintf(buff + desc, " %s", - (is_siso(tbl->lq_type)) ? "SISO" : - ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3")); - desc += sprintf(buff + desc, " %s", - (tbl->is_ht40) ? "40MHz" : "20MHz"); - desc += sprintf(buff + desc, " %s %s %s\n", - (tbl->is_SGI) ? "SGI" : "", - (lq_sta->is_green) ? "GF enabled" : "", - (lq_sta->is_agg) ? "AGG on" : ""); - } - desc += sprintf(buff+desc, "last tx rate=0x%X\n", - lq_sta->last_rate_n_flags); - desc += sprintf(buff+desc, "general:" - "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", - lq_sta->lq.general_params.flags, - lq_sta->lq.general_params.mimo_delimiter, - lq_sta->lq.general_params.single_stream_ant_msk, - lq_sta->lq.general_params.dual_stream_ant_msk); - - desc += sprintf(buff+desc, "agg:" - "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", - le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), - lq_sta->lq.agg_params.agg_dis_start_th, - lq_sta->lq.agg_params.agg_frame_cnt_limit); - - desc += sprintf(buff+desc, - "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", - lq_sta->lq.general_params.start_rate_index[0], - lq_sta->lq.general_params.start_rate_index[1], - lq_sta->lq.general_params.start_rate_index[2], - lq_sta->lq.general_params.start_rate_index[3]); - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - index = iwl_hwrate_to_plcp_idx( - le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags)); - if (is_legacy(tbl->lq_type)) { - desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n", - i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), - iwl_rate_mcs[index].mbps); - } else { - desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps (%s)\n", - i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), - iwl_rate_mcs[index].mbps, iwl_rate_mcs[index].mcs); - } - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); - kfree(buff); - return ret; -} - -static const struct file_operations rs_sta_dbgfs_scale_table_ops = { - .write = rs_sta_dbgfs_scale_table_write, - .read = rs_sta_dbgfs_scale_table_read, - .open = simple_open, - .llseek = default_llseek, -}; -static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - char *buff; - int desc = 0; - int i, j; - ssize_t ret; - - struct iwl_lq_sta *lq_sta = file->private_data; - - buff = kmalloc(1024, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - for (i = 0; i < LQ_SIZE; i++) { - desc += sprintf(buff+desc, - "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n" - "rate=0x%X\n", - lq_sta->active_tbl == i ? "*" : "x", - lq_sta->lq_info[i].lq_type, - lq_sta->lq_info[i].is_SGI, - lq_sta->lq_info[i].is_ht40, - lq_sta->lq_info[i].is_dup, - lq_sta->is_green, - lq_sta->lq_info[i].current_rate); - for (j = 0; j < IWL_RATE_COUNT; j++) { - desc += sprintf(buff+desc, - "counter=%d success=%d %%=%d\n", - lq_sta->lq_info[i].win[j].counter, - lq_sta->lq_info[i].win[j].success_counter, - lq_sta->lq_info[i].win[j].success_ratio); - } - } - ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); - kfree(buff); - return ret; -} - -static const struct file_operations rs_sta_dbgfs_stats_table_ops = { - .read = rs_sta_dbgfs_stats_table_read, - .open = simple_open, - .llseek = default_llseek, -}; - -static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - struct iwl_lq_sta *lq_sta = file->private_data; - struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; - char buff[120]; - int desc = 0; - - if (is_Ht(tbl->lq_type)) - desc += sprintf(buff+desc, - "Bit Rate= %d Mb/s\n", - tbl->expected_tpt[lq_sta->last_txrate_idx]); - else - desc += sprintf(buff+desc, - "Bit Rate= %d Mb/s\n", - iwl_rates[lq_sta->last_txrate_idx].ieee >> 1); - - return simple_read_from_buffer(user_buf, count, ppos, buff, desc); -} - -static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { - .read = rs_sta_dbgfs_rate_scale_data_read, - .open = simple_open, - .llseek = default_llseek, -}; - -static void rs_add_debugfs(void *priv, void *priv_sta, - struct dentry *dir) -{ - struct iwl_lq_sta *lq_sta = priv_sta; - lq_sta->rs_sta_dbgfs_scale_table_file = - debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, - lq_sta, &rs_sta_dbgfs_scale_table_ops); - lq_sta->rs_sta_dbgfs_stats_table_file = - debugfs_create_file("rate_stats_table", S_IRUSR, dir, - lq_sta, &rs_sta_dbgfs_stats_table_ops); - lq_sta->rs_sta_dbgfs_rate_scale_data_file = - debugfs_create_file("rate_scale_data", S_IRUSR, dir, - lq_sta, &rs_sta_dbgfs_rate_scale_data_ops); - lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = - debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, - &lq_sta->tx_agg_tid_en); - -} - -static void rs_remove_debugfs(void *priv, void *priv_sta) -{ - struct iwl_lq_sta *lq_sta = priv_sta; - debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); -} -#endif - -/* - * Initialization of rate scaling information is done by driver after - * the station is added. Since mac80211 calls this function before a - * station is added we ignore it. - */ -static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, - struct cfg80211_chan_def *chandef, - struct ieee80211_sta *sta, void *priv_sta) -{ -} - -static const struct rate_control_ops rs_ops = { - .name = RS_NAME, - .tx_status = rs_tx_status, - .get_rate = rs_get_rate, - .rate_init = rs_rate_init_stub, - .alloc = rs_alloc, - .free = rs_free, - .alloc_sta = rs_alloc_sta, - .free_sta = rs_free_sta, -#ifdef CONFIG_MAC80211_DEBUGFS - .add_sta_debugfs = rs_add_debugfs, - .remove_sta_debugfs = rs_remove_debugfs, -#endif -}; - -int iwlagn_rate_control_register(void) -{ - return ieee80211_rate_control_register(&rs_ops); -} - -void iwlagn_rate_control_unregister(void) -{ - ieee80211_rate_control_unregister(&rs_ops); -} - diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.h b/drivers/net/wireless/iwlwifi/dvm/rs.h deleted file mode 100644 index f6bd25cad203..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/rs.h +++ /dev/null @@ -1,426 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __iwl_agn_rs_h__ -#define __iwl_agn_rs_h__ - -#include - -#include "iwl-config.h" - -#include "commands.h" - -struct iwl_rate_info { - u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ - u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ - u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ - u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */ - u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ - u8 prev_ieee; /* previous rate in IEEE speeds */ - u8 next_ieee; /* next rate in IEEE speeds */ - u8 prev_rs; /* previous rate used in rs algo */ - u8 next_rs; /* next rate used in rs algo */ - u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ - u8 next_rs_tgg; /* next rate used in TGG rs algo */ -}; - -/* - * These serve as indexes into - * struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; - */ -enum { - IWL_RATE_1M_INDEX = 0, - IWL_RATE_2M_INDEX, - IWL_RATE_5M_INDEX, - IWL_RATE_11M_INDEX, - IWL_RATE_6M_INDEX, - IWL_RATE_9M_INDEX, - IWL_RATE_12M_INDEX, - IWL_RATE_18M_INDEX, - IWL_RATE_24M_INDEX, - IWL_RATE_36M_INDEX, - IWL_RATE_48M_INDEX, - IWL_RATE_54M_INDEX, - IWL_RATE_60M_INDEX, - IWL_RATE_COUNT, /*FIXME:RS:change to IWL_RATE_INDEX_COUNT,*/ - IWL_RATE_COUNT_LEGACY = IWL_RATE_COUNT - 1, /* Excluding 60M */ - IWL_RATE_INVM_INDEX = IWL_RATE_COUNT, - IWL_RATE_INVALID = IWL_RATE_COUNT, -}; - -enum { - IWL_RATE_6M_INDEX_TABLE = 0, - IWL_RATE_9M_INDEX_TABLE, - IWL_RATE_12M_INDEX_TABLE, - IWL_RATE_18M_INDEX_TABLE, - IWL_RATE_24M_INDEX_TABLE, - IWL_RATE_36M_INDEX_TABLE, - IWL_RATE_48M_INDEX_TABLE, - IWL_RATE_54M_INDEX_TABLE, - IWL_RATE_1M_INDEX_TABLE, - IWL_RATE_2M_INDEX_TABLE, - IWL_RATE_5M_INDEX_TABLE, - IWL_RATE_11M_INDEX_TABLE, - IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1, -}; - -enum { - IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, - IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX, - IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, - IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, -}; - -/* #define vs. enum to keep from defaulting to 'large integer' */ -#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX) -#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX) -#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX) -#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX) -#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX) -#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX) -#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX) -#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX) -#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX) -#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX) -#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX) -#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX) -#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) - -/* uCode API values for legacy bit rates, both OFDM and CCK */ -enum { - IWL_RATE_6M_PLCP = 13, - IWL_RATE_9M_PLCP = 15, - IWL_RATE_12M_PLCP = 5, - IWL_RATE_18M_PLCP = 7, - IWL_RATE_24M_PLCP = 9, - IWL_RATE_36M_PLCP = 11, - IWL_RATE_48M_PLCP = 1, - IWL_RATE_54M_PLCP = 3, - IWL_RATE_60M_PLCP = 3,/*FIXME:RS:should be removed*/ - IWL_RATE_1M_PLCP = 10, - IWL_RATE_2M_PLCP = 20, - IWL_RATE_5M_PLCP = 55, - IWL_RATE_11M_PLCP = 110, - /*FIXME:RS:change to IWL_RATE_LEGACY_??M_PLCP */ - /*FIXME:RS:add IWL_RATE_LEGACY_INVM_PLCP = 0,*/ -}; - -/* uCode API values for OFDM high-throughput (HT) bit rates */ -enum { - IWL_RATE_SISO_6M_PLCP = 0, - IWL_RATE_SISO_12M_PLCP = 1, - IWL_RATE_SISO_18M_PLCP = 2, - IWL_RATE_SISO_24M_PLCP = 3, - IWL_RATE_SISO_36M_PLCP = 4, - IWL_RATE_SISO_48M_PLCP = 5, - IWL_RATE_SISO_54M_PLCP = 6, - IWL_RATE_SISO_60M_PLCP = 7, - IWL_RATE_MIMO2_6M_PLCP = 0x8, - IWL_RATE_MIMO2_12M_PLCP = 0x9, - IWL_RATE_MIMO2_18M_PLCP = 0xa, - IWL_RATE_MIMO2_24M_PLCP = 0xb, - IWL_RATE_MIMO2_36M_PLCP = 0xc, - IWL_RATE_MIMO2_48M_PLCP = 0xd, - IWL_RATE_MIMO2_54M_PLCP = 0xe, - IWL_RATE_MIMO2_60M_PLCP = 0xf, - IWL_RATE_MIMO3_6M_PLCP = 0x10, - IWL_RATE_MIMO3_12M_PLCP = 0x11, - IWL_RATE_MIMO3_18M_PLCP = 0x12, - IWL_RATE_MIMO3_24M_PLCP = 0x13, - IWL_RATE_MIMO3_36M_PLCP = 0x14, - IWL_RATE_MIMO3_48M_PLCP = 0x15, - IWL_RATE_MIMO3_54M_PLCP = 0x16, - IWL_RATE_MIMO3_60M_PLCP = 0x17, - IWL_RATE_SISO_INVM_PLCP, - IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, - IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, -}; - -/* MAC header values for bit rates */ -enum { - IWL_RATE_6M_IEEE = 12, - IWL_RATE_9M_IEEE = 18, - IWL_RATE_12M_IEEE = 24, - IWL_RATE_18M_IEEE = 36, - IWL_RATE_24M_IEEE = 48, - IWL_RATE_36M_IEEE = 72, - IWL_RATE_48M_IEEE = 96, - IWL_RATE_54M_IEEE = 108, - IWL_RATE_60M_IEEE = 120, - IWL_RATE_1M_IEEE = 2, - IWL_RATE_2M_IEEE = 4, - IWL_RATE_5M_IEEE = 11, - IWL_RATE_11M_IEEE = 22, -}; - -#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) - -#define IWL_INVALID_VALUE -1 - -#define IWL_MIN_RSSI_VAL -100 -#define IWL_MAX_RSSI_VAL 0 - -/* These values specify how many Tx frame attempts before - * searching for a new modulation mode */ -#define IWL_LEGACY_FAILURE_LIMIT 160 -#define IWL_LEGACY_SUCCESS_LIMIT 480 -#define IWL_LEGACY_TABLE_COUNT 160 - -#define IWL_NONE_LEGACY_FAILURE_LIMIT 400 -#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500 -#define IWL_NONE_LEGACY_TABLE_COUNT 1500 - -/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ -#define IWL_RS_GOOD_RATIO 12800 /* 100% */ -#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ -#define IWL_RATE_HIGH_TH 10880 /* 85% */ -#define IWL_RATE_INCREASE_TH 6400 /* 50% */ -#define IWL_RATE_DECREASE_TH 1920 /* 15% */ - -/* possible actions when in legacy mode */ -#define IWL_LEGACY_SWITCH_ANTENNA1 0 -#define IWL_LEGACY_SWITCH_ANTENNA2 1 -#define IWL_LEGACY_SWITCH_SISO 2 -#define IWL_LEGACY_SWITCH_MIMO2_AB 3 -#define IWL_LEGACY_SWITCH_MIMO2_AC 4 -#define IWL_LEGACY_SWITCH_MIMO2_BC 5 -#define IWL_LEGACY_SWITCH_MIMO3_ABC 6 - -/* possible actions when in siso mode */ -#define IWL_SISO_SWITCH_ANTENNA1 0 -#define IWL_SISO_SWITCH_ANTENNA2 1 -#define IWL_SISO_SWITCH_MIMO2_AB 2 -#define IWL_SISO_SWITCH_MIMO2_AC 3 -#define IWL_SISO_SWITCH_MIMO2_BC 4 -#define IWL_SISO_SWITCH_GI 5 -#define IWL_SISO_SWITCH_MIMO3_ABC 6 - - -/* possible actions when in mimo mode */ -#define IWL_MIMO2_SWITCH_ANTENNA1 0 -#define IWL_MIMO2_SWITCH_ANTENNA2 1 -#define IWL_MIMO2_SWITCH_SISO_A 2 -#define IWL_MIMO2_SWITCH_SISO_B 3 -#define IWL_MIMO2_SWITCH_SISO_C 4 -#define IWL_MIMO2_SWITCH_GI 5 -#define IWL_MIMO2_SWITCH_MIMO3_ABC 6 - - -/* possible actions when in mimo3 mode */ -#define IWL_MIMO3_SWITCH_ANTENNA1 0 -#define IWL_MIMO3_SWITCH_ANTENNA2 1 -#define IWL_MIMO3_SWITCH_SISO_A 2 -#define IWL_MIMO3_SWITCH_SISO_B 3 -#define IWL_MIMO3_SWITCH_SISO_C 4 -#define IWL_MIMO3_SWITCH_MIMO2_AB 5 -#define IWL_MIMO3_SWITCH_MIMO2_AC 6 -#define IWL_MIMO3_SWITCH_MIMO2_BC 7 -#define IWL_MIMO3_SWITCH_GI 8 - - -#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI -#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC - -/*FIXME:RS:add possible actions for MIMO3*/ - -#define IWL_ACTION_LIMIT 3 /* # possible actions */ - -#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ - -/* load per tid defines for A-MPDU activation */ -#define IWL_AGG_TPT_THREHOLD 0 -#define IWL_AGG_LOAD_THRESHOLD 10 -#define IWL_AGG_ALL_TID 0xff -#define TID_QUEUE_CELL_SPACING 50 /*mS */ -#define TID_QUEUE_MAX_SIZE 20 -#define TID_ROUND_VALUE 5 /* mS */ - -#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) -#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) - -extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; - -enum iwl_table_type { - LQ_NONE, - LQ_G, /* legacy types */ - LQ_A, - LQ_SISO, /* high-throughput types */ - LQ_MIMO2, - LQ_MIMO3, - LQ_MAX, -}; - -#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A)) -#define is_siso(tbl) ((tbl) == LQ_SISO) -#define is_mimo2(tbl) ((tbl) == LQ_MIMO2) -#define is_mimo3(tbl) ((tbl) == LQ_MIMO3) -#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl)) -#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) -#define is_a_band(tbl) ((tbl) == LQ_A) -#define is_g_and(tbl) ((tbl) == LQ_G) - -#define IWL_MAX_MCS_DISPLAY_SIZE 12 - -struct iwl_rate_mcs_info { - char mbps[IWL_MAX_MCS_DISPLAY_SIZE]; - char mcs[IWL_MAX_MCS_DISPLAY_SIZE]; -}; - -/** - * struct iwl_rate_scale_data -- tx success history for one rate - */ -struct iwl_rate_scale_data { - u64 data; /* bitmap of successful frames */ - s32 success_counter; /* number of frames successful */ - s32 success_ratio; /* per-cent * 128 */ - s32 counter; /* number of frames attempted */ - s32 average_tpt; /* success ratio * expected throughput */ - unsigned long stamp; -}; - -/** - * struct iwl_scale_tbl_info -- tx params and success history for all rates - * - * There are two of these in struct iwl_lq_sta, - * one for "active", and one for "search". - */ -struct iwl_scale_tbl_info { - enum iwl_table_type lq_type; - u8 ant_type; - u8 is_SGI; /* 1 = short guard interval */ - u8 is_ht40; /* 1 = 40 MHz channel width */ - u8 is_dup; /* 1 = duplicated data streams */ - u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ - u8 max_search; /* maximun number of tables we can search */ - const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ - u32 current_rate; /* rate_n_flags, uCode API format */ - struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ -}; - -struct iwl_traffic_load { - unsigned long time_stamp; /* age of the oldest statistics */ - u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time - * slice */ - u32 total; /* total num of packets during the - * last TID_MAX_TIME_DIFF */ - u8 queue_count; /* number of queues that has - * been used since the last cleanup */ - u8 head; /* start of the circular buffer */ -}; - -/** - * struct iwl_lq_sta -- driver's rate scaling private structure - * - * Pointer to this gets passed back and forth between driver and mac80211. - */ -struct iwl_lq_sta { - u8 active_tbl; /* index of active table, range 0-1 */ - u8 enable_counter; /* indicates HT mode */ - u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ - u8 search_better_tbl; /* 1: currently trying alternate mode */ - s32 last_tpt; - - /* The following determine when to search for a new mode */ - u32 table_count_limit; - u32 max_failure_limit; /* # failed frames before new search */ - u32 max_success_limit; /* # successful frames before new search */ - u32 table_count; - u32 total_failed; /* total failed frames, any/all rates */ - u32 total_success; /* total successful frames, any/all rates */ - u64 flush_timer; /* time staying in mode before new search */ - - u8 action_counter; /* # mode-switch actions tried */ - u8 is_green; - u8 is_dup; - enum ieee80211_band band; - - /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ - u32 supp_rates; - u16 active_legacy_rate; - u16 active_siso_rate; - u16 active_mimo2_rate; - u16 active_mimo3_rate; - s8 max_rate_idx; /* Max rate set by user */ - u8 missed_rate_counter; - - struct iwl_link_quality_cmd lq; - struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ - struct iwl_traffic_load load[IWL_MAX_TID_COUNT]; - u8 tx_agg_tid_en; -#ifdef CONFIG_MAC80211_DEBUGFS - struct dentry *rs_sta_dbgfs_scale_table_file; - struct dentry *rs_sta_dbgfs_stats_table_file; - struct dentry *rs_sta_dbgfs_rate_scale_data_file; - struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; - u32 dbg_fixed_rate; -#endif - struct iwl_priv *drv; - - /* used to be in sta_info */ - int last_txrate_idx; - /* last tx rate_n_flags */ - u32 last_rate_n_flags; - /* packets destined for this STA are aggregated */ - u8 is_agg; - /* BT traffic this sta was last updated in */ - u8 last_bt_traffic; -}; - -static inline u8 first_antenna(u8 mask) -{ - if (mask & ANT_A) - return ANT_A; - if (mask & ANT_B) - return ANT_B; - return ANT_C; -} - - -/* Initialize station's rate scaling information after adding station */ -void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, - u8 sta_id); - -/** - * iwl_rate_control_register - Register the rate control algorithm callbacks - * - * Since the rate control algorithm is hardware specific, there is no need - * or reason to place it as a stand alone module. The driver can call - * iwl_rate_control_register in order to register the rate control callbacks - * with the mac80211 subsystem. This should be performed prior to calling - * ieee80211_register_hw - * - */ -int iwlagn_rate_control_register(void); - -/** - * iwl_rate_control_unregister - Unregister the rate control callbacks - * - * This should be called after calling ieee80211_unregister_hw, but before - * the driver is unloaded. - */ -void iwlagn_rate_control_unregister(void); - -#endif /* __iwl_agn__rs__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c deleted file mode 100644 index 4a45b0b594c7..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ /dev/null @@ -1,1101 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portionhelp of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include -#include -#include -#include "iwl-io.h" -#include "dev.h" -#include "calib.h" -#include "agn.h" - -#define IWL_CMD_ENTRY(x) [x] = #x - -const char *const iwl_dvm_cmd_strings[REPLY_MAX + 1] = { - IWL_CMD_ENTRY(REPLY_ALIVE), - IWL_CMD_ENTRY(REPLY_ERROR), - IWL_CMD_ENTRY(REPLY_ECHO), - IWL_CMD_ENTRY(REPLY_RXON), - IWL_CMD_ENTRY(REPLY_RXON_ASSOC), - IWL_CMD_ENTRY(REPLY_QOS_PARAM), - IWL_CMD_ENTRY(REPLY_RXON_TIMING), - IWL_CMD_ENTRY(REPLY_ADD_STA), - IWL_CMD_ENTRY(REPLY_REMOVE_STA), - IWL_CMD_ENTRY(REPLY_REMOVE_ALL_STA), - IWL_CMD_ENTRY(REPLY_TXFIFO_FLUSH), - IWL_CMD_ENTRY(REPLY_WEPKEY), - IWL_CMD_ENTRY(REPLY_TX), - IWL_CMD_ENTRY(REPLY_LEDS_CMD), - IWL_CMD_ENTRY(REPLY_TX_LINK_QUALITY_CMD), - IWL_CMD_ENTRY(COEX_PRIORITY_TABLE_CMD), - IWL_CMD_ENTRY(COEX_MEDIUM_NOTIFICATION), - IWL_CMD_ENTRY(COEX_EVENT_CMD), - IWL_CMD_ENTRY(REPLY_QUIET_CMD), - IWL_CMD_ENTRY(REPLY_CHANNEL_SWITCH), - IWL_CMD_ENTRY(CHANNEL_SWITCH_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_SPECTRUM_MEASUREMENT_CMD), - IWL_CMD_ENTRY(SPECTRUM_MEASURE_NOTIFICATION), - IWL_CMD_ENTRY(POWER_TABLE_CMD), - IWL_CMD_ENTRY(PM_SLEEP_NOTIFICATION), - IWL_CMD_ENTRY(PM_DEBUG_STATISTIC_NOTIFIC), - IWL_CMD_ENTRY(REPLY_SCAN_CMD), - IWL_CMD_ENTRY(REPLY_SCAN_ABORT_CMD), - IWL_CMD_ENTRY(SCAN_START_NOTIFICATION), - IWL_CMD_ENTRY(SCAN_RESULTS_NOTIFICATION), - IWL_CMD_ENTRY(SCAN_COMPLETE_NOTIFICATION), - IWL_CMD_ENTRY(BEACON_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_TX_BEACON), - IWL_CMD_ENTRY(WHO_IS_AWAKE_NOTIFICATION), - IWL_CMD_ENTRY(QUIET_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_TX_PWR_TABLE_CMD), - IWL_CMD_ENTRY(MEASURE_ABORT_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_BT_CONFIG), - IWL_CMD_ENTRY(REPLY_STATISTICS_CMD), - IWL_CMD_ENTRY(STATISTICS_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_CARD_STATE_CMD), - IWL_CMD_ENTRY(CARD_STATE_NOTIFICATION), - IWL_CMD_ENTRY(MISSED_BEACONS_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_CT_KILL_CONFIG_CMD), - IWL_CMD_ENTRY(SENSITIVITY_CMD), - IWL_CMD_ENTRY(REPLY_PHY_CALIBRATION_CMD), - IWL_CMD_ENTRY(REPLY_RX_PHY_CMD), - IWL_CMD_ENTRY(REPLY_RX_MPDU_CMD), - IWL_CMD_ENTRY(REPLY_COMPRESSED_BA), - IWL_CMD_ENTRY(CALIBRATION_CFG_CMD), - IWL_CMD_ENTRY(CALIBRATION_RES_NOTIFICATION), - IWL_CMD_ENTRY(CALIBRATION_COMPLETE_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_TX_POWER_DBM_CMD), - IWL_CMD_ENTRY(TEMPERATURE_NOTIFICATION), - IWL_CMD_ENTRY(TX_ANT_CONFIGURATION_CMD), - IWL_CMD_ENTRY(REPLY_BT_COEX_PROFILE_NOTIF), - IWL_CMD_ENTRY(REPLY_BT_COEX_PRIO_TABLE), - IWL_CMD_ENTRY(REPLY_BT_COEX_PROT_ENV), - IWL_CMD_ENTRY(REPLY_WIPAN_PARAMS), - IWL_CMD_ENTRY(REPLY_WIPAN_RXON), - IWL_CMD_ENTRY(REPLY_WIPAN_RXON_TIMING), - IWL_CMD_ENTRY(REPLY_WIPAN_RXON_ASSOC), - IWL_CMD_ENTRY(REPLY_WIPAN_QOS_PARAM), - IWL_CMD_ENTRY(REPLY_WIPAN_WEPKEY), - IWL_CMD_ENTRY(REPLY_WIPAN_P2P_CHANNEL_SWITCH), - IWL_CMD_ENTRY(REPLY_WIPAN_NOA_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_WIPAN_DEACTIVATION_COMPLETE), - IWL_CMD_ENTRY(REPLY_WOWLAN_PATTERNS), - IWL_CMD_ENTRY(REPLY_WOWLAN_WAKEUP_FILTER), - IWL_CMD_ENTRY(REPLY_WOWLAN_TSC_RSC_PARAMS), - IWL_CMD_ENTRY(REPLY_WOWLAN_TKIP_PARAMS), - IWL_CMD_ENTRY(REPLY_WOWLAN_KEK_KCK_MATERIAL), - IWL_CMD_ENTRY(REPLY_WOWLAN_GET_STATUS), - IWL_CMD_ENTRY(REPLY_D3_CONFIG), -}; -#undef IWL_CMD_ENTRY - -/****************************************************************************** - * - * Generic RX handler implementations - * - ******************************************************************************/ - -static void iwlagn_rx_reply_error(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_error_resp *err_resp = (void *)pkt->data; - - IWL_ERR(priv, "Error Reply type 0x%08X cmd REPLY_ERROR (0x%02X) " - "seq 0x%04X ser 0x%08X\n", - le32_to_cpu(err_resp->error_type), - err_resp->cmd_id, - le16_to_cpu(err_resp->bad_cmd_seq_num), - le32_to_cpu(err_resp->error_info)); -} - -static void iwlagn_rx_csa(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_csa_notification *csa = (void *)pkt->data; - /* - * MULTI-FIXME - * See iwlagn_mac_channel_switch. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl_rxon_cmd *rxon = (void *)&ctx->active; - - if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) - return; - - if (!le32_to_cpu(csa->status) && csa->channel == priv->switch_channel) { - rxon->channel = csa->channel; - ctx->staging.channel = csa->channel; - IWL_DEBUG_11H(priv, "CSA notif: channel %d\n", - le16_to_cpu(csa->channel)); - iwl_chswitch_done(priv, true); - } else { - IWL_ERR(priv, "CSA notif (fail) : channel %d\n", - le16_to_cpu(csa->channel)); - iwl_chswitch_done(priv, false); - } -} - - -static void iwlagn_rx_spectrum_measure_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_spectrum_notification *report = (void *)pkt->data; - - if (!report->state) { - IWL_DEBUG_11H(priv, - "Spectrum Measure Notification: Start\n"); - return; - } - - memcpy(&priv->measure_report, report, sizeof(*report)); - priv->measurement_status |= MEASUREMENT_READY; -} - -static void iwlagn_rx_pm_sleep_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ -#ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_sleep_notification *sleep = (void *)pkt->data; - IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n", - sleep->pm_sleep_mode, sleep->pm_wakeup_src); -#endif -} - -static void iwlagn_rx_pm_debug_statistics_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - 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); -} - -static void iwlagn_rx_beacon_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwlagn_beacon_notif *beacon = (void *)pkt->data; -#ifdef CONFIG_IWLWIFI_DEBUG - u16 status = le16_to_cpu(beacon->beacon_notify_hdr.status.status); - u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); - - IWL_DEBUG_RX(priv, "beacon status %#x, retries:%d ibssmgr:%d " - "tsf:0x%.8x%.8x rate:%d\n", - status & TX_STATUS_MSK, - beacon->beacon_notify_hdr.failure_frame, - le32_to_cpu(beacon->ibss_mgr_status), - le32_to_cpu(beacon->high_tsf), - le32_to_cpu(beacon->low_tsf), rate); -#endif - - priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); -} - -/** - * iwl_good_plcp_health - checks for plcp error. - * - * When the plcp error is exceeding the thresholds, reset the radio - * to improve the throughput. - */ -static bool iwlagn_good_plcp_health(struct iwl_priv *priv, - struct statistics_rx_phy *cur_ofdm, - struct statistics_rx_ht_phy *cur_ofdm_ht, - unsigned int msecs) -{ - int delta; - int threshold = priv->plcp_delta_threshold; - - if (threshold == IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) { - IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n"); - return true; - } - - delta = le32_to_cpu(cur_ofdm->plcp_err) - - le32_to_cpu(priv->statistics.rx_ofdm.plcp_err) + - le32_to_cpu(cur_ofdm_ht->plcp_err) - - le32_to_cpu(priv->statistics.rx_ofdm_ht.plcp_err); - - /* Can be negative if firmware reset statistics */ - if (delta <= 0) - return true; - - if ((delta * 100 / msecs) > threshold) { - IWL_DEBUG_RADIO(priv, - "plcp health threshold %u delta %d msecs %u\n", - threshold, delta, msecs); - return false; - } - - return true; -} - -int iwl_force_rf_reset(struct iwl_priv *priv, bool external) -{ - struct iwl_rf_reset *rf_reset; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return -EAGAIN; - - if (!iwl_is_any_associated(priv)) { - IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n"); - return -ENOLINK; - } - - rf_reset = &priv->rf_reset; - rf_reset->reset_request_count++; - if (!external && rf_reset->last_reset_jiffies && - time_after(rf_reset->last_reset_jiffies + - IWL_DELAY_NEXT_FORCE_RF_RESET, jiffies)) { - IWL_DEBUG_INFO(priv, "RF reset rejected\n"); - rf_reset->reset_reject_count++; - return -EAGAIN; - } - rf_reset->reset_success_count++; - rf_reset->last_reset_jiffies = jiffies; - - /* - * There is no easy and better way to force reset the radio, - * the only known method is switching channel which will force to - * reset and tune the radio. - * Use internal short scan (single channel) operation to should - * achieve this objective. - * Driver should reset the radio when number of consecutive missed - * beacon, or any other uCode error condition detected. - */ - IWL_DEBUG_INFO(priv, "perform radio reset.\n"); - iwl_internal_short_hw_scan(priv); - return 0; -} - - -static void iwlagn_recover_from_statistics(struct iwl_priv *priv, - struct statistics_rx_phy *cur_ofdm, - struct statistics_rx_ht_phy *cur_ofdm_ht, - struct statistics_tx *tx, - unsigned long stamp) -{ - unsigned int msecs; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - msecs = jiffies_to_msecs(stamp - priv->rx_statistics_jiffies); - - /* Only gather statistics and update time stamp when not associated */ - if (!iwl_is_any_associated(priv)) - return; - - /* Do not check/recover when do not have enough statistics data */ - if (msecs < 99) - return; - - if (!iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs)) - iwl_force_rf_reset(priv, false); -} - -/* Calculate noise level, based on measurements during network silence just - * before arriving beacon. This measurement can be done only if we know - * exactly when to expect beacons, therefore only when we're associated. */ -static void iwlagn_rx_calc_noise(struct iwl_priv *priv) -{ - struct statistics_rx_non_phy *rx_info; - int num_active_rx = 0; - int total_silence = 0; - int bcn_silence_a, bcn_silence_b, bcn_silence_c; - int last_rx_noise; - - rx_info = &priv->statistics.rx_non_phy; - - bcn_silence_a = - le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; - bcn_silence_b = - le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; - bcn_silence_c = - le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; - - if (bcn_silence_a) { - total_silence += bcn_silence_a; - num_active_rx++; - } - if (bcn_silence_b) { - total_silence += bcn_silence_b; - num_active_rx++; - } - if (bcn_silence_c) { - total_silence += bcn_silence_c; - num_active_rx++; - } - - /* Average among active antennas */ - if (num_active_rx) - last_rx_noise = (total_silence / num_active_rx) - 107; - else - last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; - - IWL_DEBUG_CALIB(priv, "inband silence a %u, b %u, c %u, dBm %d\n", - bcn_silence_a, bcn_silence_b, bcn_silence_c, - last_rx_noise); -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -/* - * based on the assumption of all statistics counter are in DWORD - * FIXME: This function is for debugging, do not deal with - * the case of counters roll-over. - */ -static void accum_stats(__le32 *prev, __le32 *cur, __le32 *delta, - __le32 *max_delta, __le32 *accum, int size) -{ - int i; - - for (i = 0; - i < size / sizeof(__le32); - i++, prev++, cur++, delta++, max_delta++, accum++) { - if (le32_to_cpu(*cur) > le32_to_cpu(*prev)) { - *delta = cpu_to_le32( - le32_to_cpu(*cur) - le32_to_cpu(*prev)); - le32_add_cpu(accum, le32_to_cpu(*delta)); - if (le32_to_cpu(*delta) > le32_to_cpu(*max_delta)) - *max_delta = *delta; - } - } -} - -static void -iwlagn_accumulative_statistics(struct iwl_priv *priv, - struct statistics_general_common *common, - struct statistics_rx_non_phy *rx_non_phy, - struct statistics_rx_phy *rx_ofdm, - struct statistics_rx_ht_phy *rx_ofdm_ht, - struct statistics_rx_phy *rx_cck, - struct statistics_tx *tx, - struct statistics_bt_activity *bt_activity) -{ -#define ACCUM(_name) \ - accum_stats((__le32 *)&priv->statistics._name, \ - (__le32 *)_name, \ - (__le32 *)&priv->delta_stats._name, \ - (__le32 *)&priv->max_delta_stats._name, \ - (__le32 *)&priv->accum_stats._name, \ - sizeof(*_name)); - - ACCUM(common); - ACCUM(rx_non_phy); - ACCUM(rx_ofdm); - ACCUM(rx_ofdm_ht); - ACCUM(rx_cck); - ACCUM(tx); - if (bt_activity) - ACCUM(bt_activity); -#undef ACCUM -} -#else -static inline void -iwlagn_accumulative_statistics(struct iwl_priv *priv, - struct statistics_general_common *common, - struct statistics_rx_non_phy *rx_non_phy, - struct statistics_rx_phy *rx_ofdm, - struct statistics_rx_ht_phy *rx_ofdm_ht, - struct statistics_rx_phy *rx_cck, - struct statistics_tx *tx, - struct statistics_bt_activity *bt_activity) -{ -} -#endif - -static void iwlagn_rx_statistics(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - unsigned long stamp = jiffies; - const int reg_recalib_period = 60; - int change; - struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 len = iwl_rx_packet_payload_len(pkt); - __le32 *flag; - struct statistics_general_common *common; - struct statistics_rx_non_phy *rx_non_phy; - struct statistics_rx_phy *rx_ofdm; - struct statistics_rx_ht_phy *rx_ofdm_ht; - struct statistics_rx_phy *rx_cck; - struct statistics_tx *tx; - struct statistics_bt_activity *bt_activity; - - IWL_DEBUG_RX(priv, "Statistics notification received (%d bytes).\n", - len); - - spin_lock(&priv->statistics.lock); - - if (len == sizeof(struct iwl_bt_notif_statistics)) { - struct iwl_bt_notif_statistics *stats; - stats = (void *)&pkt->data; - flag = &stats->flag; - common = &stats->general.common; - rx_non_phy = &stats->rx.general.common; - rx_ofdm = &stats->rx.ofdm; - rx_ofdm_ht = &stats->rx.ofdm_ht; - rx_cck = &stats->rx.cck; - tx = &stats->tx; - bt_activity = &stats->general.activity; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - /* handle this exception directly */ - priv->statistics.num_bt_kills = stats->rx.general.num_bt_kills; - le32_add_cpu(&priv->statistics.accum_num_bt_kills, - le32_to_cpu(stats->rx.general.num_bt_kills)); -#endif - } else if (len == sizeof(struct iwl_notif_statistics)) { - struct iwl_notif_statistics *stats; - stats = (void *)&pkt->data; - flag = &stats->flag; - common = &stats->general.common; - rx_non_phy = &stats->rx.general; - rx_ofdm = &stats->rx.ofdm; - rx_ofdm_ht = &stats->rx.ofdm_ht; - rx_cck = &stats->rx.cck; - tx = &stats->tx; - bt_activity = NULL; - } else { - WARN_ONCE(1, "len %d doesn't match BT (%zu) or normal (%zu)\n", - len, sizeof(struct iwl_bt_notif_statistics), - sizeof(struct iwl_notif_statistics)); - spin_unlock(&priv->statistics.lock); - return; - } - - change = common->temperature != priv->statistics.common.temperature || - (*flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK) != - (priv->statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK); - - iwlagn_accumulative_statistics(priv, common, rx_non_phy, rx_ofdm, - rx_ofdm_ht, rx_cck, tx, bt_activity); - - iwlagn_recover_from_statistics(priv, rx_ofdm, rx_ofdm_ht, tx, stamp); - - priv->statistics.flag = *flag; - memcpy(&priv->statistics.common, common, sizeof(*common)); - memcpy(&priv->statistics.rx_non_phy, rx_non_phy, sizeof(*rx_non_phy)); - memcpy(&priv->statistics.rx_ofdm, rx_ofdm, sizeof(*rx_ofdm)); - memcpy(&priv->statistics.rx_ofdm_ht, rx_ofdm_ht, sizeof(*rx_ofdm_ht)); - memcpy(&priv->statistics.rx_cck, rx_cck, sizeof(*rx_cck)); - memcpy(&priv->statistics.tx, tx, sizeof(*tx)); -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (bt_activity) - memcpy(&priv->statistics.bt_activity, bt_activity, - sizeof(*bt_activity)); -#endif - - priv->rx_statistics_jiffies = stamp; - - set_bit(STATUS_STATISTICS, &priv->status); - - /* Reschedule the statistics timer to occur in - * reg_recalib_period seconds to ensure we get a - * thermal update even if the uCode doesn't give - * us one */ - mod_timer(&priv->statistics_periodic, jiffies + - msecs_to_jiffies(reg_recalib_period * 1000)); - - if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) && - (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) { - iwlagn_rx_calc_noise(priv); - queue_work(priv->workqueue, &priv->run_time_calib_work); - } - if (priv->lib->temperature && change) - priv->lib->temperature(priv); - - spin_unlock(&priv->statistics.lock); -} - -static void iwlagn_rx_reply_statistics(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_notif_statistics *stats = (void *)pkt->data; - - if (le32_to_cpu(stats->flag) & UCODE_STATISTICS_CLEAR_MSK) { -#ifdef CONFIG_IWLWIFI_DEBUGFS - memset(&priv->accum_stats, 0, - sizeof(priv->accum_stats)); - memset(&priv->delta_stats, 0, - sizeof(priv->delta_stats)); - memset(&priv->max_delta_stats, 0, - sizeof(priv->max_delta_stats)); -#endif - IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); - } - - iwlagn_rx_statistics(priv, rxb); -} - -/* Handle notification from uCode that card's power state is changing - * due to software, hardware, or critical temperature RFKILL */ -static void iwlagn_rx_card_state_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_card_state_notif *card_state_notif = (void *)pkt->data; - u32 flags = le32_to_cpu(card_state_notif->flags); - unsigned long status = priv->status; - - IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n", - (flags & HW_CARD_DISABLED) ? "Kill" : "On", - (flags & SW_CARD_DISABLED) ? "Kill" : "On", - (flags & CT_CARD_DISABLED) ? - "Reached" : "Not reached"); - - if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | - CT_CARD_DISABLED)) { - - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C, - HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); - - if (!(flags & RXON_CARD_DISABLED)) { - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C, - HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); - } - if (flags & CT_CARD_DISABLED) - iwl_tt_enter_ct_kill(priv); - } - if (!(flags & CT_CARD_DISABLED)) - iwl_tt_exit_ct_kill(priv); - - if (flags & HW_CARD_DISABLED) - set_bit(STATUS_RF_KILL_HW, &priv->status); - else - clear_bit(STATUS_RF_KILL_HW, &priv->status); - - - if (!(flags & RXON_CARD_DISABLED)) - iwl_scan_cancel(priv); - - if ((test_bit(STATUS_RF_KILL_HW, &status) != - test_bit(STATUS_RF_KILL_HW, &priv->status))) - wiphy_rfkill_set_hw_state(priv->hw->wiphy, - test_bit(STATUS_RF_KILL_HW, &priv->status)); -} - -static void iwlagn_rx_missed_beacon_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) - -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_missed_beacon_notif *missed_beacon = (void *)pkt->data; - - if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > - priv->missed_beacon_threshold) { - IWL_DEBUG_CALIB(priv, - "missed bcn cnsq %d totl %d rcd %d expctd %d\n", - le32_to_cpu(missed_beacon->consecutive_missed_beacons), - le32_to_cpu(missed_beacon->total_missed_becons), - le32_to_cpu(missed_beacon->num_recvd_beacons), - le32_to_cpu(missed_beacon->num_expected_beacons)); - if (!test_bit(STATUS_SCANNING, &priv->status)) - iwl_init_sensitivity(priv); - } -} - -/* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD). - * This will be used later in iwl_rx_reply_rx() for REPLY_RX_MPDU_CMD. */ -static void iwlagn_rx_reply_rx_phy(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - - priv->last_phy_res_valid = true; - priv->ampdu_ref++; - memcpy(&priv->last_phy_res, pkt->data, - sizeof(struct iwl_rx_phy_res)); -} - -/* - * returns non-zero if packet should be dropped - */ -static int iwlagn_set_decrypted_flag(struct iwl_priv *priv, - struct ieee80211_hdr *hdr, - u32 decrypt_res, - struct ieee80211_rx_status *stats) -{ - u16 fc = le16_to_cpu(hdr->frame_control); - - /* - * All contexts have the same setting here due to it being - * a module parameter, so OK to check any context. - */ - if (priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags & - RXON_FILTER_DIS_DECRYPT_MSK) - return 0; - - if (!(fc & IEEE80211_FCTL_PROTECTED)) - return 0; - - IWL_DEBUG_RX(priv, "decrypt_res:0x%x\n", decrypt_res); - switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { - case RX_RES_STATUS_SEC_TYPE_TKIP: - /* The uCode has got a bad phase 1 Key, pushes the packet. - * Decryption will be done in SW. */ - if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == - RX_RES_STATUS_BAD_KEY_TTAK) - break; - - case RX_RES_STATUS_SEC_TYPE_WEP: - if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == - RX_RES_STATUS_BAD_ICV_MIC) { - /* bad ICV, the packet is destroyed since the - * decryption is inplace, drop it */ - IWL_DEBUG_RX(priv, "Packet destroyed\n"); - return -1; - } - case RX_RES_STATUS_SEC_TYPE_CCMP: - if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == - RX_RES_STATUS_DECRYPT_OK) { - IWL_DEBUG_RX(priv, "hw decrypt successfully!!!\n"); - stats->flag |= RX_FLAG_DECRYPTED; - } - break; - - default: - break; - } - return 0; -} - -static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, - struct ieee80211_hdr *hdr, - u16 len, - u32 ampdu_status, - struct iwl_rx_cmd_buffer *rxb, - struct ieee80211_rx_status *stats) -{ - struct sk_buff *skb; - __le16 fc = hdr->frame_control; - struct iwl_rxon_context *ctx; - unsigned int hdrlen, fraglen; - - /* We only process data packets if the interface is open */ - if (unlikely(!priv->is_open)) { - IWL_DEBUG_DROP_LIMIT(priv, - "Dropping packet while interface is not open.\n"); - return; - } - - /* In case of HW accelerated crypto and bad decryption, drop */ - if (!iwlwifi_mod_params.sw_crypto && - iwlagn_set_decrypted_flag(priv, hdr, ampdu_status, stats)) - return; - - /* Dont use dev_alloc_skb(), we'll have enough headroom once - * ieee80211_hdr pulled. - */ - skb = alloc_skb(128, GFP_ATOMIC); - if (!skb) { - IWL_ERR(priv, "alloc_skb failed\n"); - return; - } - /* If frame is small enough to fit in skb->head, pull it completely. - * If not, only pull ieee80211_hdr so that splice() or TCP coalesce - * are more efficient. - */ - hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr); - - memcpy(skb_put(skb, hdrlen), hdr, hdrlen); - fraglen = len - hdrlen; - - if (fraglen) { - int offset = (void *)hdr + hdrlen - - rxb_addr(rxb) + rxb_offset(rxb); - - skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, - fraglen, rxb->truesize); - } - - /* - * Wake any queues that were stopped due to a passive channel tx - * failure. This can happen because the regulatory enforcement in - * the device waits for a beacon before allowing transmission, - * sometimes even after already having transmitted frames for the - * association because the new RXON may reset the information. - */ - if (unlikely(ieee80211_is_beacon(fc) && priv->passive_no_rx)) { - for_each_context(priv, ctx) { - if (!ether_addr_equal(hdr->addr3, - ctx->active.bssid_addr)) - continue; - iwlagn_lift_passive_no_rx(priv); - } - } - - memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); - - ieee80211_rx_napi(priv->hw, skb, priv->napi); -} - -static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) -{ - u32 decrypt_out = 0; - - if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) == - RX_RES_STATUS_STATION_FOUND) - decrypt_out |= (RX_RES_STATUS_STATION_FOUND | - RX_RES_STATUS_NO_STATION_INFO_MISMATCH); - - decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK); - - /* packet was not encrypted */ - if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == - RX_RES_STATUS_SEC_TYPE_NONE) - return decrypt_out; - - /* packet was encrypted with unknown alg */ - if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == - RX_RES_STATUS_SEC_TYPE_ERR) - return decrypt_out; - - /* decryption was not done in HW */ - if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) != - RX_MPDU_RES_STATUS_DEC_DONE_MSK) - return decrypt_out; - - switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) { - - case RX_RES_STATUS_SEC_TYPE_CCMP: - /* alg is CCM: check MIC only */ - if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK)) - /* Bad MIC */ - decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; - else - decrypt_out |= RX_RES_STATUS_DECRYPT_OK; - - break; - - case RX_RES_STATUS_SEC_TYPE_TKIP: - if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) { - /* Bad TTAK */ - decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK; - break; - } - /* fall through if TTAK OK */ - default: - if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK)) - decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; - else - decrypt_out |= RX_RES_STATUS_DECRYPT_OK; - break; - } - - IWL_DEBUG_RX(priv, "decrypt_in:0x%x decrypt_out = 0x%x\n", - decrypt_in, decrypt_out); - - return decrypt_out; -} - -/* Calc max signal level (dBm) among 3 possible receivers */ -static int iwlagn_calc_rssi(struct iwl_priv *priv, - struct iwl_rx_phy_res *rx_resp) -{ - /* data from PHY/DSP regarding signal strength, etc., - * contents are always there, not configurable by host - */ - struct iwlagn_non_cfg_phy *ncphy = - (struct iwlagn_non_cfg_phy *)rx_resp->non_cfg_phy_buf; - u32 val, rssi_a, rssi_b, rssi_c, max_rssi; - u8 agc; - - val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_AGC_IDX]); - agc = (val & IWLAGN_OFDM_AGC_MSK) >> IWLAGN_OFDM_AGC_BIT_POS; - - /* Find max rssi among 3 possible receivers. - * These values are measured by the digital signal processor (DSP). - * They should stay fairly constant even as the signal strength varies, - * if the radio's automatic gain control (AGC) is working right. - * AGC value (see below) will provide the "interesting" info. - */ - val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_AB_IDX]); - rssi_a = (val & IWLAGN_OFDM_RSSI_INBAND_A_BITMSK) >> - IWLAGN_OFDM_RSSI_A_BIT_POS; - rssi_b = (val & IWLAGN_OFDM_RSSI_INBAND_B_BITMSK) >> - IWLAGN_OFDM_RSSI_B_BIT_POS; - val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_C_IDX]); - rssi_c = (val & IWLAGN_OFDM_RSSI_INBAND_C_BITMSK) >> - IWLAGN_OFDM_RSSI_C_BIT_POS; - - max_rssi = max_t(u32, rssi_a, rssi_b); - max_rssi = max_t(u32, max_rssi, rssi_c); - - IWL_DEBUG_STATS(priv, "Rssi In A %d B %d C %d Max %d AGC dB %d\n", - rssi_a, rssi_b, rssi_c, max_rssi, agc); - - /* dBm = max_rssi dB - agc dB - constant. - * Higher AGC (higher radio gain) means lower signal. */ - return max_rssi - agc - IWLAGN_RSSI_OFFSET; -} - -/* Called for REPLY_RX_MPDU_CMD */ -static void iwlagn_rx_reply_rx(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct ieee80211_hdr *header; - struct ieee80211_rx_status rx_status = {}; - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_rx_phy_res *phy_res; - __le32 rx_pkt_status; - struct iwl_rx_mpdu_res_start *amsdu; - u32 len; - u32 ampdu_status; - u32 rate_n_flags; - - if (!priv->last_phy_res_valid) { - IWL_ERR(priv, "MPDU frame without cached PHY data\n"); - return; - } - phy_res = &priv->last_phy_res; - amsdu = (struct iwl_rx_mpdu_res_start *)pkt->data; - header = (struct ieee80211_hdr *)(pkt->data + sizeof(*amsdu)); - len = le16_to_cpu(amsdu->byte_count); - rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*amsdu) + len); - ampdu_status = iwlagn_translate_rx_status(priv, - le32_to_cpu(rx_pkt_status)); - - if ((unlikely(phy_res->cfg_phy_cnt > 20))) { - IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d\n", - phy_res->cfg_phy_cnt); - return; - } - - if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || - !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { - IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n", - le32_to_cpu(rx_pkt_status)); - return; - } - - /* This will be used in several places later */ - rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); - - /* rx_status carries information about the packet to mac80211 */ - rx_status.mactime = le64_to_cpu(phy_res->timestamp); - rx_status.band = (phy_res->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? - IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - rx_status.freq = - ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel), - rx_status.band); - rx_status.rate_idx = - iwlagn_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band); - rx_status.flag = 0; - - /* TSF isn't reliable. In order to allow smooth user experience, - * this W/A doesn't propagate it to the mac80211 */ - /*rx_status.flag |= RX_FLAG_MACTIME_START;*/ - - priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); - - /* Find max signal strength (dBm) among 3 antenna/receiver chains */ - rx_status.signal = iwlagn_calc_rssi(priv, phy_res); - - IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, TSF %llu\n", - rx_status.signal, (unsigned long long)rx_status.mactime); - - /* - * "antenna number" - * - * It seems that the antenna field in the phy flags value - * is actually a bit field. This is undefined by radiotap, - * it wants an actual antenna number but I always get "7" - * for most legacy frames I receive indicating that the - * same frame was received on all three RX chains. - * - * I think this field should be removed in favor of a - * new 802.11n radiotap field "RX chains" that is defined - * as a bitmask. - */ - rx_status.antenna = - (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) - >> RX_RES_PHY_FLAGS_ANTENNA_POS; - - /* set the preamble flag if appropriate */ - if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) - rx_status.flag |= RX_FLAG_SHORTPRE; - - if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) { - /* - * We know which subframes of an A-MPDU belong - * together since we get a single PHY response - * from the firmware for all of them - */ - rx_status.flag |= RX_FLAG_AMPDU_DETAILS; - rx_status.ampdu_reference = priv->ampdu_ref; - } - - /* Set up the HT phy flags */ - if (rate_n_flags & RATE_MCS_HT_MSK) - rx_status.flag |= RX_FLAG_HT; - if (rate_n_flags & RATE_MCS_HT40_MSK) - rx_status.flag |= RX_FLAG_40MHZ; - if (rate_n_flags & RATE_MCS_SGI_MSK) - rx_status.flag |= RX_FLAG_SHORT_GI; - if (rate_n_flags & RATE_MCS_GF_MSK) - rx_status.flag |= RX_FLAG_HT_GF; - - iwlagn_pass_packet_to_mac80211(priv, header, len, ampdu_status, - rxb, &rx_status); -} - -static void iwlagn_rx_noa_notification(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_wipan_noa_data *new_data, *old_data; - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_wipan_noa_notification *noa_notif = (void *)pkt->data; - - /* no condition -- we're in softirq */ - old_data = rcu_dereference_protected(priv->noa_data, true); - - if (noa_notif->noa_active) { - u32 len = le16_to_cpu(noa_notif->noa_attribute.length); - u32 copylen = len; - - /* EID, len, OUI, subtype */ - len += 1 + 1 + 3 + 1; - /* P2P id, P2P length */ - len += 1 + 2; - copylen += 1 + 2; - - new_data = kmalloc(sizeof(*new_data) + len, GFP_ATOMIC); - if (new_data) { - new_data->length = len; - new_data->data[0] = WLAN_EID_VENDOR_SPECIFIC; - new_data->data[1] = len - 2; /* not counting EID, len */ - new_data->data[2] = (WLAN_OUI_WFA >> 16) & 0xff; - new_data->data[3] = (WLAN_OUI_WFA >> 8) & 0xff; - new_data->data[4] = (WLAN_OUI_WFA >> 0) & 0xff; - new_data->data[5] = WLAN_OUI_TYPE_WFA_P2P; - memcpy(&new_data->data[6], &noa_notif->noa_attribute, - copylen); - } - } else - new_data = NULL; - - rcu_assign_pointer(priv->noa_data, new_data); - - if (old_data) - kfree_rcu(old_data, rcu_head); -} - -/** - * iwl_setup_rx_handlers - Initialize Rx handler callbacks - * - * Setup the RX handlers for each of the reply types sent from the uCode - * to the host. - */ -void iwl_setup_rx_handlers(struct iwl_priv *priv) -{ - void (**handlers)(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb); - - handlers = priv->rx_handlers; - - handlers[REPLY_ERROR] = iwlagn_rx_reply_error; - handlers[CHANNEL_SWITCH_NOTIFICATION] = iwlagn_rx_csa; - handlers[SPECTRUM_MEASURE_NOTIFICATION] = - iwlagn_rx_spectrum_measure_notif; - handlers[PM_SLEEP_NOTIFICATION] = iwlagn_rx_pm_sleep_notif; - handlers[PM_DEBUG_STATISTIC_NOTIFIC] = - iwlagn_rx_pm_debug_statistics_notif; - handlers[BEACON_NOTIFICATION] = iwlagn_rx_beacon_notif; - handlers[REPLY_ADD_STA] = iwl_add_sta_callback; - - handlers[REPLY_WIPAN_NOA_NOTIFICATION] = iwlagn_rx_noa_notification; - - /* - * The same handler is used for both the REPLY to a discrete - * statistics request from the host as well as for the periodic - * statistics notifications (after received beacons) from the uCode. - */ - handlers[REPLY_STATISTICS_CMD] = iwlagn_rx_reply_statistics; - handlers[STATISTICS_NOTIFICATION] = iwlagn_rx_statistics; - - iwl_setup_rx_scan_handlers(priv); - - handlers[CARD_STATE_NOTIFICATION] = iwlagn_rx_card_state_notif; - handlers[MISSED_BEACONS_NOTIFICATION] = - iwlagn_rx_missed_beacon_notif; - - /* Rx handlers */ - handlers[REPLY_RX_PHY_CMD] = iwlagn_rx_reply_rx_phy; - handlers[REPLY_RX_MPDU_CMD] = iwlagn_rx_reply_rx; - - /* block ack */ - handlers[REPLY_COMPRESSED_BA] = - iwlagn_rx_reply_compressed_ba; - - priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx; - - /* set up notification wait support */ - iwl_notification_wait_init(&priv->notif_wait); - - /* Set up BT Rx handlers */ - if (priv->lib->bt_params) - iwlagn_bt_rx_handler_setup(priv); -} - -void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - /* - * Do the notification wait before RX handlers so - * even if the RX handler consumes the RXB we have - * access to it in the notification wait entry. - */ - iwl_notification_wait_notify(&priv->notif_wait, pkt); - - /* Based on type of command response or notification, - * handle those that need handling via function in - * rx_handlers table. See iwl_setup_rx_handlers() */ - if (priv->rx_handlers[pkt->hdr.cmd]) { - priv->rx_handlers_stats[pkt->hdr.cmd]++; - priv->rx_handlers[pkt->hdr.cmd](priv, rxb); - } else { - /* No handling needed */ - IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n", - iwl_dvm_get_cmd_string(pkt->hdr.cmd), - pkt->hdr.cmd); - } -} diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c deleted file mode 100644 index 85ceceb34fcc..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/rxon.c +++ /dev/null @@ -1,1572 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include "iwl-trans.h" -#include "iwl-modparams.h" -#include "dev.h" -#include "agn.h" -#include "calib.h" - -/* - * initialize rxon structure with default values from eeprom - */ -void iwl_connection_init_rx_config(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - memset(&ctx->staging, 0, sizeof(ctx->staging)); - - if (!ctx->vif) { - ctx->staging.dev_type = ctx->unused_devtype; - } else - switch (ctx->vif->type) { - case NL80211_IFTYPE_AP: - ctx->staging.dev_type = ctx->ap_devtype; - break; - - case NL80211_IFTYPE_STATION: - ctx->staging.dev_type = ctx->station_devtype; - ctx->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; - break; - - case NL80211_IFTYPE_ADHOC: - ctx->staging.dev_type = ctx->ibss_devtype; - ctx->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; - ctx->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK | - RXON_FILTER_ACCEPT_GRP_MSK; - break; - - case NL80211_IFTYPE_MONITOR: - ctx->staging.dev_type = RXON_DEV_TYPE_SNIFFER; - break; - - default: - IWL_ERR(priv, "Unsupported interface type %d\n", - ctx->vif->type); - break; - } - -#if 0 - /* TODO: Figure out when short_preamble would be set and cache from - * that */ - if (!hw_to_local(priv->hw)->short_preamble) - ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - else - ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; -#endif - - ctx->staging.channel = - cpu_to_le16(priv->hw->conf.chandef.chan->hw_value); - priv->band = priv->hw->conf.chandef.chan->band; - - iwl_set_flags_for_band(priv, ctx, priv->band, ctx->vif); - - /* clear both MIX and PURE40 mode flag */ - ctx->staging.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED | - RXON_FLG_CHANNEL_MODE_PURE_40); - if (ctx->vif) - memcpy(ctx->staging.node_addr, ctx->vif->addr, ETH_ALEN); - - ctx->staging.ofdm_ht_single_stream_basic_rates = 0xff; - ctx->staging.ofdm_ht_dual_stream_basic_rates = 0xff; - ctx->staging.ofdm_ht_triple_stream_basic_rates = 0xff; -} - -static int iwlagn_disable_bss(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct iwl_rxon_cmd *send) -{ - __le32 old_filter = send->filter_flags; - int ret; - - send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, - 0, sizeof(*send), send); - - send->filter_flags = old_filter; - - if (ret) - IWL_DEBUG_QUIET_RFKILL(priv, - "Error clearing ASSOC_MSK on BSS (%d)\n", ret); - - return ret; -} - -static int iwlagn_disable_pan(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct iwl_rxon_cmd *send) -{ - struct iwl_notification_wait disable_wait; - __le32 old_filter = send->filter_flags; - u8 old_dev_type = send->dev_type; - int ret; - static const u16 deactivate_cmd[] = { - REPLY_WIPAN_DEACTIVATION_COMPLETE - }; - - iwl_init_notification_wait(&priv->notif_wait, &disable_wait, - deactivate_cmd, ARRAY_SIZE(deactivate_cmd), - NULL, NULL); - - send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - send->dev_type = RXON_DEV_TYPE_P2P; - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, - 0, sizeof(*send), send); - - send->filter_flags = old_filter; - send->dev_type = old_dev_type; - - if (ret) { - IWL_ERR(priv, "Error disabling PAN (%d)\n", ret); - iwl_remove_notification(&priv->notif_wait, &disable_wait); - } else { - ret = iwl_wait_notification(&priv->notif_wait, - &disable_wait, HZ); - if (ret) - IWL_ERR(priv, "Timed out waiting for PAN disable\n"); - } - - return ret; -} - -static int iwlagn_disconn_pan(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct iwl_rxon_cmd *send) -{ - __le32 old_filter = send->filter_flags; - int ret; - - send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0, - sizeof(*send), send); - - send->filter_flags = old_filter; - - return ret; -} - -static void iwlagn_update_qos(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int ret; - - if (!ctx->is_active) - return; - - ctx->qos_data.def_qos_parm.qos_flags = 0; - - if (ctx->qos_data.qos_active) - ctx->qos_data.def_qos_parm.qos_flags |= - QOS_PARAM_FLG_UPDATE_EDCA_MSK; - - if (ctx->ht.enabled) - ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; - - IWL_DEBUG_INFO(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n", - ctx->qos_data.qos_active, - ctx->qos_data.def_qos_parm.qos_flags); - - ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, 0, - sizeof(struct iwl_qosparam_cmd), - &ctx->qos_data.def_qos_parm); - if (ret) - IWL_DEBUG_QUIET_RFKILL(priv, "Failed to update QoS\n"); -} - -static int iwlagn_update_beacon(struct iwl_priv *priv, - struct ieee80211_vif *vif) -{ - lockdep_assert_held(&priv->mutex); - - dev_kfree_skb(priv->beacon_skb); - priv->beacon_skb = ieee80211_beacon_get(priv->hw, vif); - if (!priv->beacon_skb) - return -ENOMEM; - return iwlagn_send_beacon_cmd(priv); -} - -static int iwlagn_send_rxon_assoc(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int ret = 0; - struct iwl_rxon_assoc_cmd rxon_assoc; - const struct iwl_rxon_cmd *rxon1 = &ctx->staging; - const struct iwl_rxon_cmd *rxon2 = &ctx->active; - - if ((rxon1->flags == rxon2->flags) && - (rxon1->filter_flags == rxon2->filter_flags) && - (rxon1->cck_basic_rates == rxon2->cck_basic_rates) && - (rxon1->ofdm_ht_single_stream_basic_rates == - rxon2->ofdm_ht_single_stream_basic_rates) && - (rxon1->ofdm_ht_dual_stream_basic_rates == - rxon2->ofdm_ht_dual_stream_basic_rates) && - (rxon1->ofdm_ht_triple_stream_basic_rates == - rxon2->ofdm_ht_triple_stream_basic_rates) && - (rxon1->acquisition_data == rxon2->acquisition_data) && - (rxon1->rx_chain == rxon2->rx_chain) && - (rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates)) { - IWL_DEBUG_INFO(priv, "Using current RXON_ASSOC. Not resending.\n"); - return 0; - } - - rxon_assoc.flags = ctx->staging.flags; - rxon_assoc.filter_flags = ctx->staging.filter_flags; - rxon_assoc.ofdm_basic_rates = ctx->staging.ofdm_basic_rates; - rxon_assoc.cck_basic_rates = ctx->staging.cck_basic_rates; - rxon_assoc.reserved1 = 0; - rxon_assoc.reserved2 = 0; - rxon_assoc.reserved3 = 0; - rxon_assoc.ofdm_ht_single_stream_basic_rates = - ctx->staging.ofdm_ht_single_stream_basic_rates; - rxon_assoc.ofdm_ht_dual_stream_basic_rates = - ctx->staging.ofdm_ht_dual_stream_basic_rates; - rxon_assoc.rx_chain_select_flags = ctx->staging.rx_chain; - rxon_assoc.ofdm_ht_triple_stream_basic_rates = - ctx->staging.ofdm_ht_triple_stream_basic_rates; - rxon_assoc.acquisition_data = ctx->staging.acquisition_data; - - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_assoc_cmd, - CMD_ASYNC, sizeof(rxon_assoc), &rxon_assoc); - return ret; -} - -static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) -{ - u16 new_val; - u16 beacon_factor; - - /* - * If mac80211 hasn't given us a beacon interval, program - * the default into the device (not checking this here - * would cause the adjustment below to return the maximum - * value, which may break PAN.) - */ - if (!beacon_val) - return DEFAULT_BEACON_INTERVAL; - - /* - * If the beacon interval we obtained from the peer - * is too large, we'll have to wake up more often - * (and in IBSS case, we'll beacon too much) - * - * For example, if max_beacon_val is 4096, and the - * requested beacon interval is 7000, we'll have to - * use 3500 to be able to wake up on the beacons. - * - * This could badly influence beacon detection stats. - */ - - beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; - new_val = beacon_val / beacon_factor; - - if (!new_val) - new_val = max_beacon_val; - - return new_val; -} - -static int iwl_send_rxon_timing(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - u64 tsf; - s32 interval_tm, rem; - struct ieee80211_conf *conf = NULL; - u16 beacon_int; - struct ieee80211_vif *vif = ctx->vif; - - conf = &priv->hw->conf; - - lockdep_assert_held(&priv->mutex); - - memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd)); - - ctx->timing.timestamp = cpu_to_le64(priv->timestamp); - ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval); - - beacon_int = vif ? vif->bss_conf.beacon_int : 0; - - /* - * TODO: For IBSS we need to get atim_window from mac80211, - * for now just always use 0 - */ - ctx->timing.atim_window = 0; - - if (ctx->ctxid == IWL_RXON_CTX_PAN && - (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) && - iwl_is_associated(priv, IWL_RXON_CTX_BSS) && - priv->contexts[IWL_RXON_CTX_BSS].vif && - priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) { - ctx->timing.beacon_interval = - priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval; - beacon_int = le16_to_cpu(ctx->timing.beacon_interval); - } else if (ctx->ctxid == IWL_RXON_CTX_BSS && - iwl_is_associated(priv, IWL_RXON_CTX_PAN) && - priv->contexts[IWL_RXON_CTX_PAN].vif && - priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int && - (!iwl_is_associated_ctx(ctx) || !ctx->vif || - !ctx->vif->bss_conf.beacon_int)) { - ctx->timing.beacon_interval = - priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval; - beacon_int = le16_to_cpu(ctx->timing.beacon_interval); - } else { - beacon_int = iwl_adjust_beacon_interval(beacon_int, - IWL_MAX_UCODE_BEACON_INTERVAL * TIME_UNIT); - ctx->timing.beacon_interval = cpu_to_le16(beacon_int); - } - - ctx->beacon_int = beacon_int; - - tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */ - interval_tm = beacon_int * TIME_UNIT; - rem = do_div(tsf, interval_tm); - ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); - - ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1; - - IWL_DEBUG_ASSOC(priv, - "beacon interval %d beacon timer %d beacon tim %d\n", - le16_to_cpu(ctx->timing.beacon_interval), - le32_to_cpu(ctx->timing.beacon_init_val), - le16_to_cpu(ctx->timing.atim_window)); - - return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd, - 0, sizeof(ctx->timing), &ctx->timing); -} - -static int iwlagn_rxon_disconn(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int ret; - struct iwl_rxon_cmd *active = (void *)&ctx->active; - - if (ctx->ctxid == IWL_RXON_CTX_BSS) { - ret = iwlagn_disable_bss(priv, ctx, &ctx->staging); - } else { - ret = iwlagn_disable_pan(priv, ctx, &ctx->staging); - if (ret) - return ret; - if (ctx->vif) { - ret = iwl_send_rxon_timing(priv, ctx); - if (ret) { - IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); - return ret; - } - ret = iwlagn_disconn_pan(priv, ctx, &ctx->staging); - } - } - if (ret) - return ret; - - /* - * Un-assoc RXON clears the station table and WEP - * keys, so we have to restore those afterwards. - */ - iwl_clear_ucode_stations(priv, ctx); - /* update -- might need P2P now */ - iwl_update_bcast_station(priv, ctx); - iwl_restore_stations(priv, ctx); - ret = iwl_restore_default_wep_keys(priv, ctx); - if (ret) { - IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret); - return ret; - } - - memcpy(active, &ctx->staging, sizeof(*active)); - return 0; -} - -static int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) -{ - int ret; - s8 prev_tx_power; - bool defer; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - - if (priv->calib_disabled & IWL_TX_POWER_CALIB_DISABLED) - return 0; - - lockdep_assert_held(&priv->mutex); - - if (priv->tx_power_user_lmt == tx_power && !force) - return 0; - - if (tx_power < IWLAGN_TX_POWER_TARGET_POWER_MIN) { - IWL_WARN(priv, - "Requested user TXPOWER %d below lower limit %d.\n", - tx_power, - IWLAGN_TX_POWER_TARGET_POWER_MIN); - return -EINVAL; - } - - if (tx_power > DIV_ROUND_UP(priv->nvm_data->max_tx_pwr_half_dbm, 2)) { - IWL_WARN(priv, - "Requested user TXPOWER %d above upper limit %d.\n", - tx_power, priv->nvm_data->max_tx_pwr_half_dbm); - return -EINVAL; - } - - if (!iwl_is_ready_rf(priv)) - return -EIO; - - /* scan complete and commit_rxon use tx_power_next value, - * it always need to be updated for newest request */ - priv->tx_power_next = tx_power; - - /* do not set tx power when scanning or channel changing */ - defer = test_bit(STATUS_SCANNING, &priv->status) || - memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging)); - if (defer && !force) { - IWL_DEBUG_INFO(priv, "Deferring tx power set\n"); - return 0; - } - - prev_tx_power = priv->tx_power_user_lmt; - priv->tx_power_user_lmt = tx_power; - - ret = iwlagn_send_tx_power(priv); - - /* if fail to set tx_power, restore the orig. tx power */ - if (ret) { - priv->tx_power_user_lmt = prev_tx_power; - priv->tx_power_next = prev_tx_power; - } - return ret; -} - -static int iwlagn_rxon_connect(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int ret; - struct iwl_rxon_cmd *active = (void *)&ctx->active; - - /* RXON timing must be before associated RXON */ - if (ctx->ctxid == IWL_RXON_CTX_BSS) { - ret = iwl_send_rxon_timing(priv, ctx); - if (ret) { - IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); - return ret; - } - } - /* QoS info may be cleared by previous un-assoc RXON */ - iwlagn_update_qos(priv, ctx); - - /* - * We'll run into this code path when beaconing is - * enabled, but then we also need to send the beacon - * to the device. - */ - if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_AP)) { - ret = iwlagn_update_beacon(priv, ctx->vif); - if (ret) { - IWL_ERR(priv, - "Error sending required beacon (%d)!\n", - ret); - return ret; - } - } - - priv->start_calib = 0; - /* - * Apply the new configuration. - * - * Associated RXON doesn't clear the station table in uCode, - * so we don't need to restore stations etc. after this. - */ - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0, - sizeof(struct iwl_rxon_cmd), &ctx->staging); - if (ret) { - IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); - return ret; - } - memcpy(active, &ctx->staging, sizeof(*active)); - - /* IBSS beacon needs to be sent after setting assoc */ - if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_ADHOC)) - if (iwlagn_update_beacon(priv, ctx->vif)) - IWL_ERR(priv, "Error sending IBSS beacon\n"); - iwl_init_sensitivity(priv); - - /* - * If we issue a new RXON command which required a tune then - * we must send a new TXPOWER command or we won't be able to - * Tx any frames. - * - * It's expected we set power here if channel is changing. - */ - ret = iwl_set_tx_power(priv, priv->tx_power_next, true); - if (ret) { - IWL_ERR(priv, "Error sending TX power (%d)\n", ret); - return ret; - } - - if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && - priv->cfg->ht_params && priv->cfg->ht_params->smps_mode) - ieee80211_request_smps(ctx->vif, - priv->cfg->ht_params->smps_mode); - - return 0; -} - -int iwlagn_set_pan_params(struct iwl_priv *priv) -{ - struct iwl_wipan_params_cmd cmd; - struct iwl_rxon_context *ctx_bss, *ctx_pan; - int slot0 = 300, slot1 = 0; - int ret; - - if (priv->valid_contexts == BIT(IWL_RXON_CTX_BSS)) - return 0; - - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - - lockdep_assert_held(&priv->mutex); - - ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS]; - ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN]; - - /* - * If the PAN context is inactive, then we don't need - * to update the PAN parameters, the last thing we'll - * have done before it goes inactive is making the PAN - * parameters be WLAN-only. - */ - if (!ctx_pan->is_active) - return 0; - - memset(&cmd, 0, sizeof(cmd)); - - /* only 2 slots are currently allowed */ - cmd.num_slots = 2; - - cmd.slots[0].type = 0; /* BSS */ - cmd.slots[1].type = 1; /* PAN */ - - if (ctx_bss->vif && ctx_pan->vif) { - int bcnint = ctx_pan->beacon_int; - int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1; - - /* should be set, but seems unused?? */ - cmd.flags |= cpu_to_le16(IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE); - - if (ctx_pan->vif->type == NL80211_IFTYPE_AP && - bcnint && - bcnint != ctx_bss->beacon_int) { - IWL_ERR(priv, - "beacon intervals don't match (%d, %d)\n", - ctx_bss->beacon_int, ctx_pan->beacon_int); - } else - bcnint = max_t(int, bcnint, - ctx_bss->beacon_int); - if (!bcnint) - bcnint = DEFAULT_BEACON_INTERVAL; - slot0 = bcnint / 2; - slot1 = bcnint - slot0; - - if (test_bit(STATUS_SCAN_HW, &priv->status) || - (!ctx_bss->vif->bss_conf.idle && - !ctx_bss->vif->bss_conf.assoc)) { - slot0 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME; - slot1 = IWL_MIN_SLOT_TIME; - } else if (!ctx_pan->vif->bss_conf.idle && - !ctx_pan->vif->bss_conf.assoc) { - slot1 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME; - slot0 = IWL_MIN_SLOT_TIME; - } - } else if (ctx_pan->vif) { - slot0 = 0; - slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) * - ctx_pan->beacon_int; - slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1); - - if (test_bit(STATUS_SCAN_HW, &priv->status)) { - slot0 = slot1 * 3 - IWL_MIN_SLOT_TIME; - slot1 = IWL_MIN_SLOT_TIME; - } - } - - cmd.slots[0].width = cpu_to_le16(slot0); - cmd.slots[1].width = cpu_to_le16(slot1); - - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, 0, - sizeof(cmd), &cmd); - if (ret) - IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret); - - return ret; -} - -static void _iwl_set_rxon_ht(struct iwl_priv *priv, - struct iwl_ht_config *ht_conf, - struct iwl_rxon_context *ctx) -{ - struct iwl_rxon_cmd *rxon = &ctx->staging; - - if (!ctx->ht.enabled) { - rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | - RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | - RXON_FLG_HT40_PROT_MSK | - RXON_FLG_HT_PROT_MSK); - return; - } - - /* FIXME: if the definition of ht.protection changed, the "translation" - * will be needed for rxon->flags - */ - rxon->flags |= cpu_to_le32(ctx->ht.protection << - RXON_FLG_HT_OPERATING_MODE_POS); - - /* Set up channel bandwidth: - * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ - /* clear the HT channel mode before set the mode */ - rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | - RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); - if (iwl_is_ht40_tx_allowed(priv, ctx, NULL)) { - /* pure ht40 */ - if (ctx->ht.protection == - IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { - rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; - /* - * Note: control channel is opposite of extension - * channel - */ - switch (ctx->ht.extension_chan_offset) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - rxon->flags &= - ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - rxon->flags |= - RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; - break; - } - } else { - /* - * Note: control channel is opposite of extension - * channel - */ - switch (ctx->ht.extension_chan_offset) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - rxon->flags &= - ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); - rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; - rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; - break; - case IEEE80211_HT_PARAM_CHA_SEC_NONE: - default: - /* - * channel location only valid if in Mixed - * mode - */ - IWL_ERR(priv, - "invalid extension channel offset\n"); - break; - } - } - } else { - rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY; - } - - iwlagn_set_rxon_chain(priv, ctx); - - IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X " - "extension channel offset 0x%x\n", - le32_to_cpu(rxon->flags), ctx->ht.protection, - ctx->ht.extension_chan_offset); -} - -void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) -{ - struct iwl_rxon_context *ctx; - - for_each_context(priv, ctx) - _iwl_set_rxon_ht(priv, ht_conf, ctx); -} - -/** - * iwl_set_rxon_channel - Set the band and channel values in staging RXON - * @ch: requested channel as a pointer to struct ieee80211_channel - - * NOTE: Does not commit to the hardware; it sets appropriate bit fields - * in the staging RXON flag structure based on the ch->band - */ -void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, - struct iwl_rxon_context *ctx) -{ - enum ieee80211_band band = ch->band; - u16 channel = ch->hw_value; - - if ((le16_to_cpu(ctx->staging.channel) == channel) && - (priv->band == band)) - return; - - ctx->staging.channel = cpu_to_le16(channel); - if (band == IEEE80211_BAND_5GHZ) - ctx->staging.flags &= ~RXON_FLG_BAND_24G_MSK; - else - ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; - - priv->band = band; - - IWL_DEBUG_INFO(priv, "Staging channel set to %d [%d]\n", channel, band); - -} - -void iwl_set_flags_for_band(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - enum ieee80211_band band, - struct ieee80211_vif *vif) -{ - if (band == IEEE80211_BAND_5GHZ) { - ctx->staging.flags &= - ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK - | RXON_FLG_CCK_MSK); - ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - } else { - /* Copied from iwl_post_associate() */ - if (vif && vif->bss_conf.use_short_slot) - ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - else - ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - - ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; - ctx->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; - ctx->staging.flags &= ~RXON_FLG_CCK_MSK; - } -} - -static void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, int hw_decrypt) -{ - struct iwl_rxon_cmd *rxon = &ctx->staging; - - if (hw_decrypt) - rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; - else - rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; - -} - -/* validate RXON structure is valid */ -static int iwl_check_rxon_cmd(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - struct iwl_rxon_cmd *rxon = &ctx->staging; - u32 errors = 0; - - if (rxon->flags & RXON_FLG_BAND_24G_MSK) { - if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) { - IWL_WARN(priv, "check 2.4G: wrong narrow\n"); - errors |= BIT(0); - } - if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) { - IWL_WARN(priv, "check 2.4G: wrong radar\n"); - errors |= BIT(1); - } - } else { - if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) { - IWL_WARN(priv, "check 5.2G: not short slot!\n"); - errors |= BIT(2); - } - if (rxon->flags & RXON_FLG_CCK_MSK) { - IWL_WARN(priv, "check 5.2G: CCK!\n"); - errors |= BIT(3); - } - } - if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) { - IWL_WARN(priv, "mac/bssid mcast!\n"); - errors |= BIT(4); - } - - /* make sure basic rates 6Mbps and 1Mbps are supported */ - if ((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0 && - (rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0) { - IWL_WARN(priv, "neither 1 nor 6 are basic\n"); - errors |= BIT(5); - } - - if (le16_to_cpu(rxon->assoc_id) > 2007) { - IWL_WARN(priv, "aid > 2007\n"); - errors |= BIT(6); - } - - if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) - == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) { - IWL_WARN(priv, "CCK and short slot\n"); - errors |= BIT(7); - } - - if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) - == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { - IWL_WARN(priv, "CCK and auto detect\n"); - errors |= BIT(8); - } - - if ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK | - RXON_FLG_TGG_PROTECT_MSK)) == - RXON_FLG_TGG_PROTECT_MSK) { - IWL_WARN(priv, "TGg but no auto-detect\n"); - errors |= BIT(9); - } - - if (rxon->channel == 0) { - IWL_WARN(priv, "zero channel is invalid\n"); - errors |= BIT(10); - } - - WARN(errors, "Invalid RXON (%#x), channel %d", - errors, le16_to_cpu(rxon->channel)); - - return errors ? -EINVAL : 0; -} - -/** - * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed - * @priv: staging_rxon is compared to active_rxon - * - * If the RXON structure is changing enough to require a new tune, - * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that - * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. - */ -static int iwl_full_rxon_required(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - const struct iwl_rxon_cmd *staging = &ctx->staging; - const struct iwl_rxon_cmd *active = &ctx->active; - -#define CHK(cond) \ - if ((cond)) { \ - IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n"); \ - return 1; \ - } - -#define CHK_NEQ(c1, c2) \ - if ((c1) != (c2)) { \ - IWL_DEBUG_INFO(priv, "need full RXON - " \ - #c1 " != " #c2 " - %d != %d\n", \ - (c1), (c2)); \ - return 1; \ - } - - /* These items are only settable from the full RXON command */ - CHK(!iwl_is_associated_ctx(ctx)); - CHK(!ether_addr_equal(staging->bssid_addr, active->bssid_addr)); - CHK(!ether_addr_equal(staging->node_addr, active->node_addr)); - CHK(!ether_addr_equal(staging->wlap_bssid_addr, - active->wlap_bssid_addr)); - CHK_NEQ(staging->dev_type, active->dev_type); - CHK_NEQ(staging->channel, active->channel); - CHK_NEQ(staging->air_propagation, active->air_propagation); - CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, - active->ofdm_ht_single_stream_basic_rates); - CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, - active->ofdm_ht_dual_stream_basic_rates); - CHK_NEQ(staging->ofdm_ht_triple_stream_basic_rates, - active->ofdm_ht_triple_stream_basic_rates); - CHK_NEQ(staging->assoc_id, active->assoc_id); - - /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can - * be updated with the RXON_ASSOC command -- however only some - * flag transitions are allowed using RXON_ASSOC */ - - /* Check if we are not switching bands */ - CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, - active->flags & RXON_FLG_BAND_24G_MSK); - - /* Check if we are switching association toggle */ - CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, - active->filter_flags & RXON_FILTER_ASSOC_MSK); - -#undef CHK -#undef CHK_NEQ - - return 0; -} - -#ifdef CONFIG_IWLWIFI_DEBUG -void iwl_print_rx_config_cmd(struct iwl_priv *priv, - enum iwl_rxon_context_id ctxid) -{ - struct iwl_rxon_context *ctx = &priv->contexts[ctxid]; - struct iwl_rxon_cmd *rxon = &ctx->staging; - - IWL_DEBUG_RADIO(priv, "RX CONFIG:\n"); - iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); - IWL_DEBUG_RADIO(priv, "u16 channel: 0x%x\n", - le16_to_cpu(rxon->channel)); - IWL_DEBUG_RADIO(priv, "u32 flags: 0x%08X\n", - le32_to_cpu(rxon->flags)); - IWL_DEBUG_RADIO(priv, "u32 filter_flags: 0x%08x\n", - le32_to_cpu(rxon->filter_flags)); - IWL_DEBUG_RADIO(priv, "u8 dev_type: 0x%x\n", rxon->dev_type); - IWL_DEBUG_RADIO(priv, "u8 ofdm_basic_rates: 0x%02x\n", - rxon->ofdm_basic_rates); - IWL_DEBUG_RADIO(priv, "u8 cck_basic_rates: 0x%02x\n", - rxon->cck_basic_rates); - IWL_DEBUG_RADIO(priv, "u8[6] node_addr: %pM\n", rxon->node_addr); - IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr); - IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", - le16_to_cpu(rxon->assoc_id)); -} -#endif - -static void iwl_calc_basic_rates(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int lowest_present_ofdm = 100; - int lowest_present_cck = 100; - u8 cck = 0; - u8 ofdm = 0; - - if (ctx->vif) { - struct ieee80211_supported_band *sband; - unsigned long basic = ctx->vif->bss_conf.basic_rates; - int i; - - sband = priv->hw->wiphy->bands[priv->hw->conf.chandef.chan->band]; - - for_each_set_bit(i, &basic, BITS_PER_LONG) { - int hw = sband->bitrates[i].hw_value; - if (hw >= IWL_FIRST_OFDM_RATE) { - ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE); - if (lowest_present_ofdm > hw) - lowest_present_ofdm = hw; - } else { - BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); - - cck |= BIT(hw); - if (lowest_present_cck > hw) - lowest_present_cck = hw; - } - } - } - - /* - * Now we've got the basic rates as bitmaps in the ofdm and cck - * variables. This isn't sufficient though, as there might not - * be all the right rates in the bitmap. E.g. if the only basic - * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps - * and 6 Mbps because the 802.11-2007 standard says in 9.6: - * - * [...] a STA responding to a received frame shall transmit - * its Control Response frame [...] at the highest rate in the - * BSSBasicRateSet parameter that is less than or equal to the - * rate of the immediately previous frame in the frame exchange - * sequence ([...]) and that is of the same modulation class - * ([...]) as the received frame. If no rate contained in the - * BSSBasicRateSet parameter meets these conditions, then the - * control frame sent in response to a received frame shall be - * transmitted at the highest mandatory rate of the PHY that is - * less than or equal to the rate of the received frame, and - * that is of the same modulation class as the received frame. - * - * As a consequence, we need to add all mandatory rates that are - * lower than all of the basic rates to these bitmaps. - */ - - if (IWL_RATE_24M_INDEX < lowest_present_ofdm) - ofdm |= IWL_RATE_24M_MASK >> IWL_FIRST_OFDM_RATE; - if (IWL_RATE_12M_INDEX < lowest_present_ofdm) - ofdm |= IWL_RATE_12M_MASK >> IWL_FIRST_OFDM_RATE; - /* 6M already there or needed so always add */ - ofdm |= IWL_RATE_6M_MASK >> IWL_FIRST_OFDM_RATE; - - /* - * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. - * Note, however: - * - if no CCK rates are basic, it must be ERP since there must - * be some basic rates at all, so they're OFDM => ERP PHY - * (or we're in 5 GHz, and the cck bitmap will never be used) - * - if 11M is a basic rate, it must be ERP as well, so add 5.5M - * - if 5.5M is basic, 1M and 2M are mandatory - * - if 2M is basic, 1M is mandatory - * - if 1M is basic, that's the only valid ACK rate. - * As a consequence, it's not as complicated as it sounds, just add - * any lower rates to the ACK rate bitmap. - */ - if (IWL_RATE_11M_INDEX < lowest_present_cck) - cck |= IWL_RATE_11M_MASK >> IWL_FIRST_CCK_RATE; - if (IWL_RATE_5M_INDEX < lowest_present_cck) - cck |= IWL_RATE_5M_MASK >> IWL_FIRST_CCK_RATE; - if (IWL_RATE_2M_INDEX < lowest_present_cck) - cck |= IWL_RATE_2M_MASK >> IWL_FIRST_CCK_RATE; - /* 1M already there or needed so always add */ - cck |= IWL_RATE_1M_MASK >> IWL_FIRST_CCK_RATE; - - IWL_DEBUG_RATE(priv, "Set basic rates cck:0x%.2x ofdm:0x%.2x\n", - cck, ofdm); - - /* "basic_rates" is a misnomer here -- should be called ACK rates */ - ctx->staging.cck_basic_rates = cck; - ctx->staging.ofdm_basic_rates = ofdm; -} - -/** - * iwlagn_commit_rxon - commit staging_rxon to hardware - * - * The RXON command in staging_rxon is committed to the hardware and - * the active_rxon structure is updated with the new data. This - * function correctly transitions out of the RXON_ASSOC_MSK state if - * a HW tune is required based on the RXON structure changes. - * - * The connect/disconnect flow should be as the following: - * - * 1. make sure send RXON command with association bit unset if not connect - * this should include the channel and the band for the candidate - * to be connected to - * 2. Add Station before RXON association with the AP - * 3. RXON_timing has to send before RXON for connection - * 4. full RXON command - associated bit set - * 5. use RXON_ASSOC command to update any flags changes - */ -int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) -{ - /* cast away the const for active_rxon in this function */ - struct iwl_rxon_cmd *active = (void *)&ctx->active; - bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK); - int ret; - - lockdep_assert_held(&priv->mutex); - - if (!iwl_is_alive(priv)) - return -EBUSY; - - /* This function hardcodes a bunch of dual-mode assumptions */ - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - - if (!ctx->is_active) - return 0; - - /* always get timestamp with Rx frame */ - ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK; - - /* recalculate basic rates */ - iwl_calc_basic_rates(priv, ctx); - - /* - * force CTS-to-self frames protection if RTS-CTS is not preferred - * one aggregation protection method - */ - if (!priv->hw_params.use_rts_for_aggregation) - ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; - - if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) || - !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK)) - ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - else - ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - - iwl_print_rx_config_cmd(priv, ctx->ctxid); - ret = iwl_check_rxon_cmd(priv, ctx); - if (ret) { - IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n"); - return -EINVAL; - } - - /* - * receive commit_rxon request - * abort any previous channel switch if still in process - */ - if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) && - (priv->switch_channel != ctx->staging.channel)) { - IWL_DEBUG_11H(priv, "abort channel switch on %d\n", - le16_to_cpu(priv->switch_channel)); - iwl_chswitch_done(priv, false); - } - - /* - * If we don't need to send a full RXON, we can use - * iwl_rxon_assoc_cmd which is used to reconfigure filter - * and other flags for the current radio configuration. - */ - if (!iwl_full_rxon_required(priv, ctx)) { - ret = iwlagn_send_rxon_assoc(priv, ctx); - if (ret) { - IWL_ERR(priv, "Error setting RXON_ASSOC (%d)\n", ret); - return ret; - } - - memcpy(active, &ctx->staging, sizeof(*active)); - /* - * We do not commit tx power settings while channel changing, - * do it now if after settings changed. - */ - iwl_set_tx_power(priv, priv->tx_power_next, false); - - /* make sure we are in the right PS state */ - iwl_power_update_mode(priv, true); - - return 0; - } - - iwl_set_rxon_hwcrypto(priv, ctx, !iwlwifi_mod_params.sw_crypto); - - IWL_DEBUG_INFO(priv, - "Going to commit RXON\n" - " * with%s RXON_FILTER_ASSOC_MSK\n" - " * channel = %d\n" - " * bssid = %pM\n", - (new_assoc ? "" : "out"), - le16_to_cpu(ctx->staging.channel), - ctx->staging.bssid_addr); - - /* - * Always clear associated first, but with the correct config. - * This is required as for example station addition for the - * AP station must be done after the BSSID is set to correctly - * set up filters in the device. - */ - ret = iwlagn_rxon_disconn(priv, ctx); - if (ret) - return ret; - - ret = iwlagn_set_pan_params(priv); - if (ret) - return ret; - - if (new_assoc) - return iwlagn_rxon_connect(priv, ctx); - - return 0; -} - -void iwlagn_config_ht40(struct ieee80211_conf *conf, - struct iwl_rxon_context *ctx) -{ - if (conf_is_ht40_minus(conf)) { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_BELOW; - ctx->ht.is_40mhz = true; - } else if (conf_is_ht40_plus(conf)) { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - ctx->ht.is_40mhz = true; - } else { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_NONE; - ctx->ht.is_40mhz = false; - } -} - -int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx; - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_channel *channel = conf->chandef.chan; - int ret = 0; - - IWL_DEBUG_MAC80211(priv, "enter: changed %#x\n", changed); - - mutex_lock(&priv->mutex); - - if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) { - IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); - goto out; - } - - if (!iwl_is_ready(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); - goto out; - } - - if (changed & (IEEE80211_CONF_CHANGE_SMPS | - IEEE80211_CONF_CHANGE_CHANNEL)) { - /* mac80211 uses static for non-HT which is what we want */ - priv->current_ht_config.smps = conf->smps_mode; - - /* - * Recalculate chain counts. - * - * If monitor mode is enabled then mac80211 will - * set up the SM PS mode to OFF if an HT channel is - * configured. - */ - for_each_context(priv, ctx) - iwlagn_set_rxon_chain(priv, ctx); - } - - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - for_each_context(priv, ctx) { - /* Configure HT40 channels */ - if (ctx->ht.enabled != conf_is_ht(conf)) - ctx->ht.enabled = conf_is_ht(conf); - - if (ctx->ht.enabled) { - /* if HT40 is used, it should not change - * after associated except channel switch */ - if (!ctx->ht.is_40mhz || - !iwl_is_associated_ctx(ctx)) - iwlagn_config_ht40(conf, ctx); - } else - ctx->ht.is_40mhz = false; - - /* - * Default to no protection. Protection mode will - * later be set from BSS config in iwl_ht_conf - */ - ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; - - /* if we are switching from ht to 2.4 clear flags - * from any ht related info since 2.4 does not - * support ht */ - if (le16_to_cpu(ctx->staging.channel) != - channel->hw_value) - ctx->staging.flags = 0; - - iwl_set_rxon_channel(priv, channel, ctx); - iwl_set_rxon_ht(priv, &priv->current_ht_config); - - iwl_set_flags_for_band(priv, ctx, channel->band, - ctx->vif); - } - - iwl_update_bcast_stations(priv); - } - - if (changed & (IEEE80211_CONF_CHANGE_PS | - IEEE80211_CONF_CHANGE_IDLE)) { - ret = iwl_power_update_mode(priv, false); - if (ret) - IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n"); - } - - if (changed & IEEE80211_CONF_CHANGE_POWER) { - IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n", - priv->tx_power_user_lmt, conf->power_level); - - iwl_set_tx_power(priv, conf->power_level, false); - } - - for_each_context(priv, ctx) { - if (!memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) - continue; - iwlagn_commit_rxon(priv, ctx); - } - out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -static void iwlagn_check_needed_chains(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_bss_conf *bss_conf) -{ - struct ieee80211_vif *vif = ctx->vif; - struct iwl_rxon_context *tmp; - struct ieee80211_sta *sta; - struct iwl_ht_config *ht_conf = &priv->current_ht_config; - struct ieee80211_sta_ht_cap *ht_cap; - bool need_multiple; - - lockdep_assert_held(&priv->mutex); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - rcu_read_lock(); - sta = ieee80211_find_sta(vif, bss_conf->bssid); - if (!sta) { - /* - * If at all, this can only happen through a race - * when the AP disconnects us while we're still - * setting up the connection, in that case mac80211 - * will soon tell us about that. - */ - need_multiple = false; - rcu_read_unlock(); - break; - } - - ht_cap = &sta->ht_cap; - - need_multiple = true; - - /* - * If the peer advertises no support for receiving 2 and 3 - * stream MCS rates, it can't be transmitting them either. - */ - if (ht_cap->mcs.rx_mask[1] == 0 && - ht_cap->mcs.rx_mask[2] == 0) { - need_multiple = false; - } else if (!(ht_cap->mcs.tx_params & - IEEE80211_HT_MCS_TX_DEFINED)) { - /* If it can't TX MCS at all ... */ - need_multiple = false; - } else if (ht_cap->mcs.tx_params & - IEEE80211_HT_MCS_TX_RX_DIFF) { - int maxstreams; - - /* - * But if it can receive them, it might still not - * be able to transmit them, which is what we need - * to check here -- so check the number of streams - * it advertises for TX (if different from RX). - */ - - maxstreams = (ht_cap->mcs.tx_params & - IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK); - maxstreams >>= - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; - maxstreams += 1; - - if (maxstreams <= 1) - need_multiple = false; - } - - rcu_read_unlock(); - break; - case NL80211_IFTYPE_ADHOC: - /* currently */ - need_multiple = false; - break; - default: - /* only AP really */ - need_multiple = true; - break; - } - - ctx->ht_need_multiple_chains = need_multiple; - - if (!need_multiple) { - /* check all contexts */ - for_each_context(priv, tmp) { - if (!tmp->vif) - continue; - if (tmp->ht_need_multiple_chains) { - need_multiple = true; - break; - } - } - } - - ht_conf->single_chain_sufficient = !need_multiple; -} - -static void iwlagn_chain_noise_reset(struct iwl_priv *priv) -{ - struct iwl_chain_noise_data *data = &priv->chain_noise_data; - int ret; - - if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED) - return; - - if ((data->state == IWL_CHAIN_NOISE_ALIVE) && - iwl_is_any_associated(priv)) { - struct iwl_calib_chain_noise_reset_cmd cmd; - - /* clear data for chain noise calibration algorithm */ - data->chain_noise_a = 0; - data->chain_noise_b = 0; - data->chain_noise_c = 0; - data->chain_signal_a = 0; - data->chain_signal_b = 0; - data->chain_signal_c = 0; - data->beacon_count = 0; - - memset(&cmd, 0, sizeof(cmd)); - iwl_set_calib_hdr(&cmd.hdr, - priv->phy_calib_chain_noise_reset_cmd); - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_PHY_CALIBRATION_CMD, - 0, sizeof(cmd), &cmd); - if (ret) - IWL_ERR(priv, - "Could not send REPLY_PHY_CALIBRATION_CMD\n"); - data->state = IWL_CHAIN_NOISE_ACCUMULATE; - IWL_DEBUG_CALIB(priv, "Run chain_noise_calibrate\n"); - } -} - -void iwlagn_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - int ret; - bool force = false; - - mutex_lock(&priv->mutex); - - if (changes & BSS_CHANGED_IDLE && bss_conf->idle) { - /* - * If we go idle, then clearly no "passive-no-rx" - * workaround is needed any more, this is a reset. - */ - iwlagn_lift_passive_no_rx(priv); - } - - if (unlikely(!iwl_is_ready(priv))) { - IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); - mutex_unlock(&priv->mutex); - return; - } - - if (unlikely(!ctx->vif)) { - IWL_DEBUG_MAC80211(priv, "leave - vif is NULL\n"); - mutex_unlock(&priv->mutex); - return; - } - - if (changes & BSS_CHANGED_BEACON_INT) - force = true; - - if (changes & BSS_CHANGED_QOS) { - ctx->qos_data.qos_active = bss_conf->qos; - iwlagn_update_qos(priv, ctx); - } - - ctx->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid); - if (vif->bss_conf.use_short_preamble) - ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; - else - ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - - if (changes & BSS_CHANGED_ASSOC) { - if (bss_conf->assoc) { - priv->timestamp = bss_conf->sync_tsf; - ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - } else { - ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - - if (ctx->ctxid == IWL_RXON_CTX_BSS) - priv->have_rekey_data = false; - } - - iwlagn_bt_coex_rssi_monitor(priv); - } - - if (ctx->ht.enabled) { - ctx->ht.protection = bss_conf->ht_operation_mode & - IEEE80211_HT_OP_MODE_PROTECTION; - ctx->ht.non_gf_sta_present = !!(bss_conf->ht_operation_mode & - IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - iwlagn_check_needed_chains(priv, ctx, bss_conf); - iwl_set_rxon_ht(priv, &priv->current_ht_config); - } - - iwlagn_set_rxon_chain(priv, ctx); - - if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ)) - ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; - else - ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; - - if (bss_conf->use_cts_prot) - ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; - else - ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN; - - memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); - - if (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_ADHOC) { - if (vif->bss_conf.enable_beacon) { - ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - priv->beacon_ctx = ctx; - } else { - ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - priv->beacon_ctx = NULL; - } - } - - /* - * If the ucode decides to do beacon filtering before - * association, it will lose beacons that are needed - * before sending frames out on passive channels. This - * causes association failures on those channels. Enable - * receiving beacons in such cases. - */ - - if (vif->type == NL80211_IFTYPE_STATION) { - if (!bss_conf->assoc) - ctx->staging.filter_flags |= RXON_FILTER_BCON_AWARE_MSK; - else - ctx->staging.filter_flags &= - ~RXON_FILTER_BCON_AWARE_MSK; - } - - if (force || memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) - iwlagn_commit_rxon(priv, ctx); - - if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) { - /* - * The chain noise calibration will enable PM upon - * completion. If calibration has already been run - * then we need to enable power management here. - */ - if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE) - iwl_power_update_mode(priv, false); - - /* Enable RX differential gain and sensitivity calibrations */ - iwlagn_chain_noise_reset(priv); - priv->start_calib = 1; - } - - if (changes & BSS_CHANGED_IBSS) { - ret = iwlagn_manage_ibss_station(priv, vif, - bss_conf->ibss_joined); - if (ret) - IWL_ERR(priv, "failed to %s IBSS station %pM\n", - bss_conf->ibss_joined ? "add" : "remove", - bss_conf->bssid); - } - - if (changes & BSS_CHANGED_BEACON && priv->beacon_ctx == ctx) { - if (iwlagn_update_beacon(priv, vif)) - IWL_ERR(priv, "Error updating beacon\n"); - } - - mutex_unlock(&priv->mutex); -} - -void iwlagn_post_scan(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - - /* - * We do not commit power settings while scan is pending, - * do it now if the settings changed. - */ - iwl_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false); - iwl_set_tx_power(priv, priv->tx_power_next, false); - - /* - * Since setting the RXON may have been deferred while - * performing the scan, fire one off if needed - */ - for_each_context(priv, ctx) - if (memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) - iwlagn_commit_rxon(priv, ctx); - - iwlagn_set_pan_params(priv); -} diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c deleted file mode 100644 index 648159495bbc..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ /dev/null @@ -1,1075 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ -#include -#include -#include -#include - -#include "dev.h" -#include "agn.h" - -/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after - * sending probe req. This should be set long enough to hear probe responses - * from more than one AP. */ -#define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ -#define IWL_ACTIVE_DWELL_TIME_52 (20) - -#define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3) -#define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2) - -/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. - * Must be set longer than active dwell time. - * For the most reliable scan, set > AP beacon interval (typically 100msec). */ -#define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ -#define IWL_PASSIVE_DWELL_TIME_52 (10) -#define IWL_PASSIVE_DWELL_BASE (100) -#define IWL_CHANNEL_TUNE_TIME 5 -#define MAX_SCAN_CHANNEL 50 - -/* For reset radio, need minimal dwell time only */ -#define IWL_RADIO_RESET_DWELL_TIME 5 - -static int iwl_send_scan_abort(struct iwl_priv *priv) -{ - int ret; - struct iwl_host_cmd cmd = { - .id = REPLY_SCAN_ABORT_CMD, - .flags = CMD_WANT_SKB, - }; - __le32 *status; - - /* Exit instantly with error when device is not ready - * to receive scan abort command or it does not perform - * hardware scan currently */ - if (!test_bit(STATUS_READY, &priv->status) || - !test_bit(STATUS_SCAN_HW, &priv->status) || - test_bit(STATUS_FW_ERROR, &priv->status)) - return -EIO; - - ret = iwl_dvm_send_cmd(priv, &cmd); - if (ret) - return ret; - - status = (void *)cmd.resp_pkt->data; - if (*status != CAN_ABORT_STATUS) { - /* The scan abort will return 1 for success or - * 2 for "failure". A failure condition can be - * due to simply not being in an active scan which - * can occur if we send the scan abort before we - * the microcode has notified us that a scan is - * completed. */ - IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n", - le32_to_cpu(*status)); - ret = -EIO; - } - - iwl_free_resp(&cmd); - return ret; -} - -static void iwl_complete_scan(struct iwl_priv *priv, bool aborted) -{ - /* check if scan was requested from mac80211 */ - if (priv->scan_request) { - IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n"); - ieee80211_scan_completed(priv->hw, aborted); - } - - priv->scan_type = IWL_SCAN_NORMAL; - priv->scan_vif = NULL; - priv->scan_request = NULL; -} - -static void iwl_process_scan_complete(struct iwl_priv *priv) -{ - bool aborted; - - lockdep_assert_held(&priv->mutex); - - if (!test_and_clear_bit(STATUS_SCAN_COMPLETE, &priv->status)) - return; - - IWL_DEBUG_SCAN(priv, "Completed scan.\n"); - - cancel_delayed_work(&priv->scan_check); - - aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status); - if (aborted) - IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n"); - - if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Scan already completed.\n"); - goto out_settings; - } - - if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) { - int err; - - /* Check if mac80211 requested scan during our internal scan */ - if (priv->scan_request == NULL) - goto out_complete; - - /* If so request a new scan */ - err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL, - priv->scan_request->channels[0]->band); - if (err) { - IWL_DEBUG_SCAN(priv, - "failed to initiate pending scan: %d\n", err); - aborted = true; - goto out_complete; - } - - return; - } - -out_complete: - iwl_complete_scan(priv, aborted); - -out_settings: - /* Can we still talk to firmware ? */ - if (!iwl_is_ready_rf(priv)) - return; - - iwlagn_post_scan(priv); -} - -void iwl_force_scan_end(struct iwl_priv *priv) -{ - lockdep_assert_held(&priv->mutex); - - if (!test_bit(STATUS_SCANNING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n"); - return; - } - - IWL_DEBUG_SCAN(priv, "Forcing scan end\n"); - clear_bit(STATUS_SCANNING, &priv->status); - clear_bit(STATUS_SCAN_HW, &priv->status); - clear_bit(STATUS_SCAN_ABORTING, &priv->status); - clear_bit(STATUS_SCAN_COMPLETE, &priv->status); - iwl_complete_scan(priv, true); -} - -static void iwl_do_scan_abort(struct iwl_priv *priv) -{ - int ret; - - lockdep_assert_held(&priv->mutex); - - if (!test_bit(STATUS_SCANNING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n"); - return; - } - - if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Scan abort in progress\n"); - return; - } - - ret = iwl_send_scan_abort(priv); - if (ret) { - IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret); - iwl_force_scan_end(priv); - } else - IWL_DEBUG_SCAN(priv, "Successfully send scan abort\n"); -} - -/** - * iwl_scan_cancel - Cancel any currently executing HW scan - */ -int iwl_scan_cancel(struct iwl_priv *priv) -{ - IWL_DEBUG_SCAN(priv, "Queuing abort scan\n"); - queue_work(priv->workqueue, &priv->abort_scan); - return 0; -} - -/** - * iwl_scan_cancel_timeout - Cancel any currently executing HW scan - * @ms: amount of time to wait (in milliseconds) for scan to abort - * - */ -void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(ms); - - lockdep_assert_held(&priv->mutex); - - IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n"); - - iwl_do_scan_abort(priv); - - while (time_before_eq(jiffies, timeout)) { - if (!test_bit(STATUS_SCAN_HW, &priv->status)) - goto finished; - msleep(20); - } - - return; - - finished: - /* - * Now STATUS_SCAN_HW is clear. This means that the - * device finished, but the background work is going - * to execute at best as soon as we release the mutex. - * Since we need to be able to issue a new scan right - * after this function returns, run the complete here. - * The STATUS_SCAN_COMPLETE bit will then be cleared - * and prevent the background work from "completing" - * a possible new scan. - */ - iwl_process_scan_complete(priv); -} - -/* Service response to REPLY_SCAN_CMD (0x80) */ -static void iwl_rx_reply_scan(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ -#ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_scanreq_notification *notif = (void *)pkt->data; - - IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status); -#endif -} - -/* Service SCAN_START_NOTIFICATION (0x82) */ -static void iwl_rx_scan_start_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_scanstart_notification *notif = (void *)pkt->data; - - priv->scan_start_tsf = le32_to_cpu(notif->tsf_low); - IWL_DEBUG_SCAN(priv, "Scan start: " - "%d [802.11%s] " - "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", - notif->channel, - notif->band ? "bg" : "a", - le32_to_cpu(notif->tsf_high), - le32_to_cpu(notif->tsf_low), - notif->status, notif->beacon_timer); -} - -/* Service SCAN_RESULTS_NOTIFICATION (0x83) */ -static void iwl_rx_scan_results_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ -#ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_scanresults_notification *notif = (void *)pkt->data; - - IWL_DEBUG_SCAN(priv, "Scan ch.res: " - "%d [802.11%s] " - "probe status: %u:%u " - "(TSF: 0x%08X:%08X) - %d " - "elapsed=%lu usec\n", - notif->channel, - notif->band ? "bg" : "a", - notif->probe_status, notif->num_probe_not_sent, - le32_to_cpu(notif->tsf_high), - le32_to_cpu(notif->tsf_low), - le32_to_cpu(notif->statistics[0]), - le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf); -#endif -} - -/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ -static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_scancomplete_notification *scan_notif = (void *)pkt->data; - - IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", - scan_notif->scanned_channels, - scan_notif->tsf_low, - scan_notif->tsf_high, scan_notif->status); - - IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", - (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", - jiffies_to_msecs(jiffies - priv->scan_start)); - - /* - * When aborting, we run the scan completed background work inline - * and the background work must then do nothing. The SCAN_COMPLETE - * bit helps implement that logic and thus needs to be set before - * queueing the work. Also, since the scan abort waits for SCAN_HW - * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW - * to avoid a race there. - */ - set_bit(STATUS_SCAN_COMPLETE, &priv->status); - clear_bit(STATUS_SCAN_HW, &priv->status); - queue_work(priv->workqueue, &priv->scan_completed); - - if (priv->iw_mode != NL80211_IFTYPE_ADHOC && - iwl_advanced_bt_coexist(priv) && - priv->bt_status != scan_notif->bt_status) { - if (scan_notif->bt_status) { - /* BT on */ - if (!priv->bt_ch_announce) - priv->bt_traffic_load = - IWL_BT_COEX_TRAFFIC_LOAD_HIGH; - /* - * otherwise, no traffic load information provided - * no changes made - */ - } else { - /* BT off */ - priv->bt_traffic_load = - IWL_BT_COEX_TRAFFIC_LOAD_NONE; - } - priv->bt_status = scan_notif->bt_status; - queue_work(priv->workqueue, - &priv->bt_traffic_change_work); - } -} - -void iwl_setup_rx_scan_handlers(struct iwl_priv *priv) -{ - /* scan handlers */ - priv->rx_handlers[REPLY_SCAN_CMD] = iwl_rx_reply_scan; - priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl_rx_scan_start_notif; - priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] = - iwl_rx_scan_results_notif; - priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] = - iwl_rx_scan_complete_notif; -} - -static u16 iwl_get_active_dwell_time(struct iwl_priv *priv, - enum ieee80211_band band, u8 n_probes) -{ - if (band == IEEE80211_BAND_5GHZ) - return IWL_ACTIVE_DWELL_TIME_52 + - IWL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); - else - return IWL_ACTIVE_DWELL_TIME_24 + - IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); -} - -static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time) -{ - struct iwl_rxon_context *ctx; - int limits[NUM_IWL_RXON_CTX] = {}; - int n_active = 0; - u16 limit; - - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - - /* - * If we're associated, we clamp the dwell time 98% - * of the beacon interval (minus 2 * channel tune time) - * If both contexts are active, we have to restrict to - * 1/2 of the minimum of them, because they might be in - * lock-step with the time inbetween only half of what - * time we'd have in each of them. - */ - for_each_context(priv, ctx) { - switch (ctx->staging.dev_type) { - case RXON_DEV_TYPE_P2P: - /* no timing constraints */ - continue; - case RXON_DEV_TYPE_ESS: - default: - /* timing constraints if associated */ - if (!iwl_is_associated_ctx(ctx)) - continue; - break; - case RXON_DEV_TYPE_CP: - case RXON_DEV_TYPE_2STA: - /* - * These seem to always have timers for TBTT - * active in uCode even when not associated yet. - */ - break; - } - - limits[n_active++] = ctx->beacon_int ?: IWL_PASSIVE_DWELL_BASE; - } - - switch (n_active) { - case 0: - return dwell_time; - case 2: - limit = (limits[1] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; - limit /= 2; - dwell_time = min(limit, dwell_time); - /* fall through to limit further */ - case 1: - limit = (limits[0] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; - limit /= n_active; - return min(limit, dwell_time); - default: - WARN_ON_ONCE(1); - return dwell_time; - } -} - -static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, - enum ieee80211_band band) -{ - u16 passive = (band == IEEE80211_BAND_2GHZ) ? - IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 : - IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52; - - return iwl_limit_dwell(priv, passive); -} - -/* Return valid, unused, channel for a passive scan to reset the RF */ -static u8 iwl_get_single_channel_number(struct iwl_priv *priv, - enum ieee80211_band band) -{ - struct ieee80211_supported_band *sband = priv->hw->wiphy->bands[band]; - struct iwl_rxon_context *ctx; - int i; - - for (i = 0; i < sband->n_channels; i++) { - bool busy = false; - - for_each_context(priv, ctx) { - busy = sband->channels[i].hw_value == - le16_to_cpu(ctx->staging.channel); - if (busy) - break; - } - - if (busy) - continue; - - if (!(sband->channels[i].flags & IEEE80211_CHAN_DISABLED)) - return sband->channels[i].hw_value; - } - - return 0; -} - -static int iwl_get_channel_for_reset_scan(struct iwl_priv *priv, - struct ieee80211_vif *vif, - enum ieee80211_band band, - struct iwl_scan_channel *scan_ch) -{ - const struct ieee80211_supported_band *sband; - u16 channel; - - sband = iwl_get_hw_mode(priv, band); - if (!sband) { - IWL_ERR(priv, "invalid band\n"); - return 0; - } - - channel = iwl_get_single_channel_number(priv, band); - if (channel) { - scan_ch->channel = cpu_to_le16(channel); - scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; - scan_ch->active_dwell = - cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); - scan_ch->passive_dwell = - cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); - /* Set txpower levels to defaults */ - scan_ch->dsp_atten = 110; - if (band == IEEE80211_BAND_5GHZ) - scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; - else - scan_ch->tx_gain = ((1 << 5) | (5 << 3)); - return 1; - } - - IWL_ERR(priv, "no valid channel found\n"); - return 0; -} - -static int iwl_get_channels_for_scan(struct iwl_priv *priv, - struct ieee80211_vif *vif, - enum ieee80211_band band, - u8 is_active, u8 n_probes, - struct iwl_scan_channel *scan_ch) -{ - struct ieee80211_channel *chan; - const struct ieee80211_supported_band *sband; - u16 passive_dwell = 0; - u16 active_dwell = 0; - int added, i; - u16 channel; - - sband = iwl_get_hw_mode(priv, band); - if (!sband) - return 0; - - active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); - passive_dwell = iwl_get_passive_dwell_time(priv, band); - - if (passive_dwell <= active_dwell) - passive_dwell = active_dwell + 1; - - for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { - chan = priv->scan_request->channels[i]; - - if (chan->band != band) - continue; - - channel = chan->hw_value; - scan_ch->channel = cpu_to_le16(channel); - - if (!is_active || (chan->flags & IEEE80211_CHAN_NO_IR)) - scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; - else - scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; - - if (n_probes) - scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes); - - scan_ch->active_dwell = cpu_to_le16(active_dwell); - scan_ch->passive_dwell = cpu_to_le16(passive_dwell); - - /* Set txpower levels to defaults */ - scan_ch->dsp_atten = 110; - - /* NOTE: if we were doing 6Mb OFDM for scans we'd use - * power level: - * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; - */ - if (band == IEEE80211_BAND_5GHZ) - scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; - else - scan_ch->tx_gain = ((1 << 5) | (5 << 3)); - - IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n", - channel, le32_to_cpu(scan_ch->type), - (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? - "ACTIVE" : "PASSIVE", - (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? - active_dwell : passive_dwell); - - scan_ch++; - added++; - } - - IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added); - return added; -} - -/** - * iwl_fill_probe_req - fill in all required fields and IE for probe request - */ - -static u16 iwl_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, - const u8 *ies, int ie_len, const u8 *ssid, - u8 ssid_len, int left) -{ - int len = 0; - u8 *pos = NULL; - - /* Make sure there is enough space for the probe request, - * two mandatory IEs and the data */ - left -= 24; - if (left < 0) - return 0; - - frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - eth_broadcast_addr(frame->da); - memcpy(frame->sa, ta, ETH_ALEN); - eth_broadcast_addr(frame->bssid); - frame->seq_ctrl = 0; - - len += 24; - - /* ...next IE... */ - pos = &frame->u.probe_req.variable[0]; - - /* fill in our SSID IE */ - left -= ssid_len + 2; - if (left < 0) - return 0; - *pos++ = WLAN_EID_SSID; - *pos++ = ssid_len; - if (ssid && ssid_len) { - memcpy(pos, ssid, ssid_len); - pos += ssid_len; - } - - len += ssid_len + 2; - - if (WARN_ON(left < ie_len)) - return len; - - if (ies && ie_len) { - memcpy(pos, ies, ie_len); - len += ie_len; - } - - return (u16)len; -} - -static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) -{ - struct iwl_host_cmd cmd = { - .id = REPLY_SCAN_CMD, - .len = { sizeof(struct iwl_scan_cmd), }, - }; - struct iwl_scan_cmd *scan; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - u32 rate_flags = 0; - u16 cmd_len = 0; - u16 rx_chain = 0; - enum ieee80211_band band; - u8 n_probes = 0; - u8 rx_ant = priv->nvm_data->valid_rx_ant; - u8 rate; - bool is_active = false; - int chan_mod; - u8 active_chains; - u8 scan_tx_antennas = priv->nvm_data->valid_tx_ant; - int ret; - int scan_cmd_size = sizeof(struct iwl_scan_cmd) + - MAX_SCAN_CHANNEL * sizeof(struct iwl_scan_channel) + - priv->fw->ucode_capa.max_probe_length; - const u8 *ssid = NULL; - u8 ssid_len = 0; - - if (WARN_ON(priv->scan_type == IWL_SCAN_NORMAL && - (!priv->scan_request || - priv->scan_request->n_channels > MAX_SCAN_CHANNEL))) - return -EINVAL; - - lockdep_assert_held(&priv->mutex); - - if (vif) - ctx = iwl_rxon_ctx_from_vif(vif); - - if (!priv->scan_cmd) { - priv->scan_cmd = kmalloc(scan_cmd_size, GFP_KERNEL); - if (!priv->scan_cmd) { - IWL_DEBUG_SCAN(priv, - "fail to allocate memory for scan\n"); - return -ENOMEM; - } - } - scan = priv->scan_cmd; - memset(scan, 0, scan_cmd_size); - - scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; - scan->quiet_time = IWL_ACTIVE_QUIET_TIME; - - if (iwl_is_any_associated(priv)) { - u16 interval = 0; - u32 extra; - u32 suspend_time = 100; - u32 scan_suspend_time = 100; - - IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); - switch (priv->scan_type) { - case IWL_SCAN_RADIO_RESET: - interval = 0; - break; - case IWL_SCAN_NORMAL: - interval = vif->bss_conf.beacon_int; - break; - } - - scan->suspend_time = 0; - scan->max_out_time = cpu_to_le32(200 * 1024); - if (!interval) - interval = suspend_time; - - extra = (suspend_time / interval) << 22; - scan_suspend_time = (extra | - ((suspend_time % interval) * 1024)); - scan->suspend_time = cpu_to_le32(scan_suspend_time); - IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", - scan_suspend_time, interval); - } - - switch (priv->scan_type) { - case IWL_SCAN_RADIO_RESET: - IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); - /* - * Override quiet time as firmware checks that active - * dwell is >= quiet; since we use passive scan it'll - * not actually be used. - */ - scan->quiet_time = cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); - break; - case IWL_SCAN_NORMAL: - if (priv->scan_request->n_ssids) { - int i, p = 0; - IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); - /* - * The highest priority SSID is inserted to the - * probe request template. - */ - ssid_len = priv->scan_request->ssids[0].ssid_len; - ssid = priv->scan_request->ssids[0].ssid; - - /* - * Invert the order of ssids, the firmware will invert - * it back. - */ - for (i = priv->scan_request->n_ssids - 1; i >= 1; i--) { - scan->direct_scan[p].id = WLAN_EID_SSID; - scan->direct_scan[p].len = - priv->scan_request->ssids[i].ssid_len; - memcpy(scan->direct_scan[p].ssid, - priv->scan_request->ssids[i].ssid, - priv->scan_request->ssids[i].ssid_len); - n_probes++; - p++; - } - is_active = true; - } else - IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); - break; - } - - scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; - scan->tx_cmd.sta_id = ctx->bcast_sta_id; - scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - - switch (priv->scan_band) { - case IEEE80211_BAND_2GHZ: - scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; - chan_mod = le32_to_cpu( - priv->contexts[IWL_RXON_CTX_BSS].active.flags & - RXON_FLG_CHANNEL_MODE_MSK) - >> RXON_FLG_CHANNEL_MODE_POS; - if ((priv->scan_request && priv->scan_request->no_cck) || - chan_mod == CHANNEL_MODE_PURE_40) { - rate = IWL_RATE_6M_PLCP; - } else { - rate = IWL_RATE_1M_PLCP; - rate_flags = RATE_MCS_CCK_MSK; - } - /* - * Internal scans are passive, so we can indiscriminately set - * the BT ignore flag on 2.4 GHz since it applies to TX only. - */ - if (priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist) - scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; - break; - case IEEE80211_BAND_5GHZ: - rate = IWL_RATE_6M_PLCP; - break; - default: - IWL_WARN(priv, "Invalid scan band\n"); - return -EIO; - } - - /* - * If active scanning is requested but a certain channel is - * marked passive, we can do active scanning if we detect - * transmissions. - * - * There is an issue with some firmware versions that triggers - * a sysassert on a "good CRC threshold" of zero (== disabled), - * on a radar channel even though this means that we should NOT - * send probes. - * - * The "good CRC threshold" is the number of frames that we - * need to receive during our dwell time on a channel before - * sending out probes -- setting this to a huge value will - * mean we never reach it, but at the same time work around - * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER - * here instead of IWL_GOOD_CRC_TH_DISABLED. - * - * This was fixed in later versions along with some other - * scan changes, and the threshold behaves as a flag in those - * versions. - */ - if (priv->new_scan_threshold_behaviour) - scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : - IWL_GOOD_CRC_TH_DISABLED; - else - scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : - IWL_GOOD_CRC_TH_NEVER; - - band = priv->scan_band; - - if (band == IEEE80211_BAND_2GHZ && - priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist) { - /* transmit 2.4 GHz probes only on first antenna */ - scan_tx_antennas = first_antenna(scan_tx_antennas); - } - - priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, - priv->scan_tx_ant[band], - scan_tx_antennas); - rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]); - scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags); - - /* - * In power save mode while associated use one chain, - * otherwise use all chains - */ - if (test_bit(STATUS_POWER_PMI, &priv->status) && - !(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { - /* rx_ant has been set to all valid chains previously */ - active_chains = rx_ant & - ((u8)(priv->chain_noise_data.active_chains)); - if (!active_chains) - active_chains = rx_ant; - - IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n", - priv->chain_noise_data.active_chains); - - rx_ant = first_antenna(active_chains); - } - if (priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist && - priv->bt_full_concurrent) { - /* operated as 1x1 in full concurrency mode */ - rx_ant = first_antenna(rx_ant); - } - - /* MIMO is not used here, but value is required */ - rx_chain |= - priv->nvm_data->valid_rx_ant << RXON_RX_CHAIN_VALID_POS; - rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; - rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; - rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; - scan->rx_chain = cpu_to_le16(rx_chain); - switch (priv->scan_type) { - case IWL_SCAN_NORMAL: - cmd_len = iwl_fill_probe_req( - (struct ieee80211_mgmt *)scan->data, - vif->addr, - priv->scan_request->ie, - priv->scan_request->ie_len, - ssid, ssid_len, - scan_cmd_size - sizeof(*scan)); - break; - case IWL_SCAN_RADIO_RESET: - /* use bcast addr, will not be transmitted but must be valid */ - cmd_len = iwl_fill_probe_req( - (struct ieee80211_mgmt *)scan->data, - iwl_bcast_addr, NULL, 0, - NULL, 0, - scan_cmd_size - sizeof(*scan)); - break; - default: - BUG(); - } - scan->tx_cmd.len = cpu_to_le16(cmd_len); - - scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | - RXON_FILTER_BCON_AWARE_MSK); - - switch (priv->scan_type) { - case IWL_SCAN_RADIO_RESET: - scan->channel_count = - iwl_get_channel_for_reset_scan(priv, vif, band, - (void *)&scan->data[cmd_len]); - break; - case IWL_SCAN_NORMAL: - scan->channel_count = - iwl_get_channels_for_scan(priv, vif, band, - is_active, n_probes, - (void *)&scan->data[cmd_len]); - break; - } - - if (scan->channel_count == 0) { - IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); - return -EIO; - } - - cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) + - scan->channel_count * sizeof(struct iwl_scan_channel); - cmd.data[0] = scan; - cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - scan->len = cpu_to_le16(cmd.len[0]); - - /* set scan bit here for PAN params */ - set_bit(STATUS_SCAN_HW, &priv->status); - - ret = iwlagn_set_pan_params(priv); - if (ret) { - clear_bit(STATUS_SCAN_HW, &priv->status); - return ret; - } - - ret = iwl_dvm_send_cmd(priv, &cmd); - if (ret) { - clear_bit(STATUS_SCAN_HW, &priv->status); - iwlagn_set_pan_params(priv); - } - - return ret; -} - -void iwl_init_scan_params(struct iwl_priv *priv) -{ - u8 ant_idx = fls(priv->nvm_data->valid_tx_ant) - 1; - if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ]) - priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx; - if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ]) - priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; -} - -int __must_check iwl_scan_initiate(struct iwl_priv *priv, - struct ieee80211_vif *vif, - enum iwl_scan_type scan_type, - enum ieee80211_band band) -{ - int ret; - - lockdep_assert_held(&priv->mutex); - - cancel_delayed_work(&priv->scan_check); - - if (!iwl_is_ready_rf(priv)) { - IWL_WARN(priv, "Request scan called when driver not ready.\n"); - return -EIO; - } - - if (test_bit(STATUS_SCAN_HW, &priv->status)) { - IWL_DEBUG_SCAN(priv, - "Multiple concurrent scan requests in parallel.\n"); - return -EBUSY; - } - - if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n"); - return -EBUSY; - } - - IWL_DEBUG_SCAN(priv, "Starting %sscan...\n", - scan_type == IWL_SCAN_NORMAL ? "" : - "internal short "); - - set_bit(STATUS_SCANNING, &priv->status); - priv->scan_type = scan_type; - priv->scan_start = jiffies; - priv->scan_band = band; - - ret = iwlagn_request_scan(priv, vif); - if (ret) { - clear_bit(STATUS_SCANNING, &priv->status); - priv->scan_type = IWL_SCAN_NORMAL; - return ret; - } - - queue_delayed_work(priv->workqueue, &priv->scan_check, - IWL_SCAN_CHECK_WATCHDOG); - - return 0; -} - - -/* - * internal short scan, this function should only been called while associated. - * It will reset and tune the radio to prevent possible RF related problem - */ -void iwl_internal_short_hw_scan(struct iwl_priv *priv) -{ - queue_work(priv->workqueue, &priv->start_internal_scan); -} - -static void iwl_bg_start_internal_scan(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, start_internal_scan); - - IWL_DEBUG_SCAN(priv, "Start internal scan\n"); - - mutex_lock(&priv->mutex); - - if (priv->scan_type == IWL_SCAN_RADIO_RESET) { - IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n"); - goto unlock; - } - - if (test_bit(STATUS_SCANNING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); - goto unlock; - } - - if (iwl_scan_initiate(priv, NULL, IWL_SCAN_RADIO_RESET, priv->band)) - IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n"); - unlock: - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_scan_check(struct work_struct *data) -{ - struct iwl_priv *priv = - container_of(data, struct iwl_priv, scan_check.work); - - IWL_DEBUG_SCAN(priv, "Scan check work\n"); - - /* Since we are here firmware does not finish scan and - * most likely is in bad shape, so we don't bother to - * send abort command, just force scan complete to mac80211 */ - mutex_lock(&priv->mutex); - iwl_force_scan_end(priv); - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_abort_scan(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan); - - IWL_DEBUG_SCAN(priv, "Abort scan work\n"); - - /* We keep scan_check work queued in case when firmware will not - * report back scan completed notification */ - mutex_lock(&priv->mutex); - iwl_scan_cancel_timeout(priv, 200); - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_scan_completed(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, scan_completed); - - mutex_lock(&priv->mutex); - iwl_process_scan_complete(priv); - mutex_unlock(&priv->mutex); -} - -void iwl_setup_scan_deferred_work(struct iwl_priv *priv) -{ - INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); - INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan); - INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan); - INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check); -} - -void iwl_cancel_scan_deferred_work(struct iwl_priv *priv) -{ - cancel_work_sync(&priv->start_internal_scan); - cancel_work_sync(&priv->abort_scan); - cancel_work_sync(&priv->scan_completed); - - if (cancel_delayed_work_sync(&priv->scan_check)) { - mutex_lock(&priv->mutex); - iwl_force_scan_end(priv); - mutex_unlock(&priv->mutex); - } -} diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c deleted file mode 100644 index 0fa67d3b7235..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ /dev/null @@ -1,1442 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include "iwl-trans.h" -#include "dev.h" -#include "agn.h" - -const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - -static int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) -{ - lockdep_assert_held(&priv->sta_lock); - - if (sta_id >= IWLAGN_STATION_COUNT) { - IWL_ERR(priv, "invalid sta_id %u\n", sta_id); - return -EINVAL; - } - if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) - IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u " - "addr %pM\n", - sta_id, priv->stations[sta_id].sta.sta.addr); - - if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) { - IWL_DEBUG_ASSOC(priv, - "STA id %u addr %pM already present in uCode " - "(according to driver)\n", - sta_id, priv->stations[sta_id].sta.sta.addr); - } else { - priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE; - IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n", - sta_id, priv->stations[sta_id].sta.sta.addr); - } - return 0; -} - -static void iwl_process_add_sta_resp(struct iwl_priv *priv, - struct iwl_rx_packet *pkt) -{ - struct iwl_add_sta_resp *add_sta_resp = (void *)pkt->data; - - IWL_DEBUG_INFO(priv, "Processing response for adding station\n"); - - spin_lock_bh(&priv->sta_lock); - - switch (add_sta_resp->status) { - case ADD_STA_SUCCESS_MSK: - IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); - break; - case ADD_STA_NO_ROOM_IN_TABLE: - IWL_ERR(priv, "Adding station failed, no room in table.\n"); - break; - case ADD_STA_NO_BLOCK_ACK_RESOURCE: - IWL_ERR(priv, - "Adding station failed, no block ack resource.\n"); - break; - case ADD_STA_MODIFY_NON_EXIST_STA: - IWL_ERR(priv, "Attempting to modify non-existing station\n"); - break; - default: - IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", - add_sta_resp->status); - break; - } - - spin_unlock_bh(&priv->sta_lock); -} - -void iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - - iwl_process_add_sta_resp(priv, pkt); -} - -int iwl_send_add_sta(struct iwl_priv *priv, - struct iwl_addsta_cmd *sta, u8 flags) -{ - int ret = 0; - struct iwl_host_cmd cmd = { - .id = REPLY_ADD_STA, - .flags = flags, - .data = { sta, }, - .len = { sizeof(*sta), }, - }; - u8 sta_id __maybe_unused = sta->sta.sta_id; - struct iwl_rx_packet *pkt; - struct iwl_add_sta_resp *add_sta_resp; - - IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", - sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); - - if (!(flags & CMD_ASYNC)) { - cmd.flags |= CMD_WANT_SKB; - might_sleep(); - } - - ret = iwl_dvm_send_cmd(priv, &cmd); - - if (ret || (flags & CMD_ASYNC)) - return ret; - - pkt = cmd.resp_pkt; - add_sta_resp = (void *)pkt->data; - - /* debug messages are printed in the handler */ - if (add_sta_resp->status == ADD_STA_SUCCESS_MSK) { - spin_lock_bh(&priv->sta_lock); - ret = iwl_sta_ucode_activate(priv, sta_id); - spin_unlock_bh(&priv->sta_lock); - } else { - ret = -EIO; - } - - iwl_free_resp(&cmd); - - return ret; -} - -bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_sta *sta) -{ - if (!ctx->ht.enabled || !ctx->ht.is_40mhz) - return false; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (priv->disable_ht40) - return false; -#endif - - /* special case for RXON */ - if (!sta) - return true; - - return sta->bandwidth >= IEEE80211_STA_RX_BW_40; -} - -static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, - struct ieee80211_sta *sta, - struct iwl_rxon_context *ctx, - __le32 *flags, __le32 *mask) -{ - struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; - - *mask = STA_FLG_RTS_MIMO_PROT_MSK | - STA_FLG_MIMO_DIS_MSK | - STA_FLG_HT40_EN_MSK | - STA_FLG_MAX_AGG_SIZE_MSK | - STA_FLG_AGG_MPDU_DENSITY_MSK; - *flags = 0; - - if (!sta || !sta_ht_inf->ht_supported) - return; - - IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n", - sta->addr, - (sta->smps_mode == IEEE80211_SMPS_STATIC) ? - "static" : - (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) ? - "dynamic" : "disabled"); - - switch (sta->smps_mode) { - case IEEE80211_SMPS_STATIC: - *flags |= STA_FLG_MIMO_DIS_MSK; - break; - case IEEE80211_SMPS_DYNAMIC: - *flags |= STA_FLG_RTS_MIMO_PROT_MSK; - break; - case IEEE80211_SMPS_OFF: - break; - default: - IWL_WARN(priv, "Invalid MIMO PS mode %d\n", sta->smps_mode); - break; - } - - *flags |= cpu_to_le32( - (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); - - *flags |= cpu_to_le32( - (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); - - if (iwl_is_ht40_tx_allowed(priv, ctx, sta)) - *flags |= STA_FLG_HT40_EN_MSK; -} - -int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct ieee80211_sta *sta) -{ - u8 sta_id = iwl_sta_id(sta); - __le32 flags, mask; - struct iwl_addsta_cmd cmd; - - if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) - return -EINVAL; - - iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].sta.station_flags &= ~mask; - priv->stations[sta_id].sta.station_flags |= flags; - spin_unlock_bh(&priv->sta_lock); - - memset(&cmd, 0, sizeof(cmd)); - cmd.mode = STA_CONTROL_MODIFY_MSK; - cmd.station_flags_msk = mask; - cmd.station_flags = flags; - cmd.sta.sta_id = sta_id; - - return iwl_send_add_sta(priv, &cmd, 0); -} - -static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, - struct ieee80211_sta *sta, - struct iwl_rxon_context *ctx) -{ - __le32 flags, mask; - - iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); - - lockdep_assert_held(&priv->sta_lock); - priv->stations[index].sta.station_flags &= ~mask; - priv->stations[index].sta.station_flags |= flags; -} - -/** - * iwl_prep_station - Prepare station information for addition - * - * should be called with sta_lock held - */ -u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - const u8 *addr, bool is_ap, struct ieee80211_sta *sta) -{ - struct iwl_station_entry *station; - int i; - u8 sta_id = IWL_INVALID_STATION; - - if (is_ap) - sta_id = ctx->ap_sta_id; - else if (is_broadcast_ether_addr(addr)) - sta_id = ctx->bcast_sta_id; - else - for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) { - if (ether_addr_equal(priv->stations[i].sta.sta.addr, - addr)) { - sta_id = i; - break; - } - - if (!priv->stations[i].used && - sta_id == IWL_INVALID_STATION) - sta_id = i; - } - - /* - * These two conditions have the same outcome, but keep them - * separate - */ - if (unlikely(sta_id == IWL_INVALID_STATION)) - return sta_id; - - /* - * uCode is not able to deal with multiple requests to add a - * station. Keep track if one is in progress so that we do not send - * another. - */ - if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { - IWL_DEBUG_INFO(priv, "STA %d already in process of being " - "added.\n", sta_id); - return sta_id; - } - - if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && - (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) && - ether_addr_equal(priv->stations[sta_id].sta.sta.addr, addr)) { - IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " - "adding again.\n", sta_id, addr); - return sta_id; - } - - station = &priv->stations[sta_id]; - station->used = IWL_STA_DRIVER_ACTIVE; - IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", - sta_id, addr); - priv->num_stations++; - - /* Set up the REPLY_ADD_STA command to send to device */ - memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd)); - memcpy(station->sta.sta.addr, addr, ETH_ALEN); - station->sta.mode = 0; - station->sta.sta.sta_id = sta_id; - station->sta.station_flags = ctx->station_flags; - station->ctxid = ctx->ctxid; - - if (sta) { - struct iwl_station_priv *sta_priv; - - sta_priv = (void *)sta->drv_priv; - sta_priv->ctx = ctx; - } - - /* - * OK to call unconditionally, since local stations (IBSS BSSID - * STA and broadcast STA) pass in a NULL sta, and mac80211 - * doesn't allow HT IBSS. - */ - iwl_set_ht_add_station(priv, sta_id, sta, ctx); - - return sta_id; - -} - -#define STA_WAIT_TIMEOUT (HZ/2) - -/** - * iwl_add_station_common - - */ -int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - const u8 *addr, bool is_ap, - struct ieee80211_sta *sta, u8 *sta_id_r) -{ - int ret = 0; - u8 sta_id; - struct iwl_addsta_cmd sta_cmd; - - *sta_id_r = 0; - spin_lock_bh(&priv->sta_lock); - sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta); - if (sta_id == IWL_INVALID_STATION) { - IWL_ERR(priv, "Unable to prepare station %pM for addition\n", - addr); - spin_unlock_bh(&priv->sta_lock); - return -EINVAL; - } - - /* - * uCode is not able to deal with multiple requests to add a - * station. Keep track if one is in progress so that we do not send - * another. - */ - if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { - IWL_DEBUG_INFO(priv, "STA %d already in process of being " - "added.\n", sta_id); - spin_unlock_bh(&priv->sta_lock); - return -EEXIST; - } - - if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && - (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { - IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " - "adding again.\n", sta_id, addr); - spin_unlock_bh(&priv->sta_lock); - return -EEXIST; - } - - priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; - memcpy(&sta_cmd, &priv->stations[sta_id].sta, - sizeof(struct iwl_addsta_cmd)); - spin_unlock_bh(&priv->sta_lock); - - /* Add station to device's station table */ - ret = iwl_send_add_sta(priv, &sta_cmd, 0); - if (ret) { - spin_lock_bh(&priv->sta_lock); - IWL_ERR(priv, "Adding station %pM failed.\n", - priv->stations[sta_id].sta.sta.addr); - priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; - priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; - spin_unlock_bh(&priv->sta_lock); - } - *sta_id_r = sta_id; - return ret; -} - -/** - * iwl_sta_ucode_deactivate - deactivate ucode status for a station - */ -static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id) -{ - lockdep_assert_held(&priv->sta_lock); - - /* Ucode must be active and driver must be non active */ - if ((priv->stations[sta_id].used & - (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) != - IWL_STA_UCODE_ACTIVE) - IWL_ERR(priv, "removed non active STA %u\n", sta_id); - - priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; - - memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); - IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); -} - -static int iwl_send_remove_station(struct iwl_priv *priv, - const u8 *addr, int sta_id, - bool temporary) -{ - struct iwl_rx_packet *pkt; - int ret; - struct iwl_rem_sta_cmd rm_sta_cmd; - struct iwl_rem_sta_resp *rem_sta_resp; - - struct iwl_host_cmd cmd = { - .id = REPLY_REMOVE_STA, - .len = { sizeof(struct iwl_rem_sta_cmd), }, - .data = { &rm_sta_cmd, }, - }; - - memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); - rm_sta_cmd.num_sta = 1; - memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); - - cmd.flags |= CMD_WANT_SKB; - - ret = iwl_dvm_send_cmd(priv, &cmd); - - if (ret) - return ret; - - pkt = cmd.resp_pkt; - rem_sta_resp = (void *)pkt->data; - - switch (rem_sta_resp->status) { - case REM_STA_SUCCESS_MSK: - if (!temporary) { - spin_lock_bh(&priv->sta_lock); - iwl_sta_ucode_deactivate(priv, sta_id); - spin_unlock_bh(&priv->sta_lock); - } - IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); - break; - default: - ret = -EIO; - IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); - break; - } - - iwl_free_resp(&cmd); - - return ret; -} - -/** - * iwl_remove_station - Remove driver's knowledge of station. - */ -int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, - const u8 *addr) -{ - u8 tid; - - if (!iwl_is_ready(priv)) { - IWL_DEBUG_INFO(priv, - "Unable to remove station %pM, device not ready.\n", - addr); - /* - * It is typical for stations to be removed when we are - * going down. Return success since device will be down - * soon anyway - */ - return 0; - } - - IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", - sta_id, addr); - - if (WARN_ON(sta_id == IWL_INVALID_STATION)) - return -EINVAL; - - spin_lock_bh(&priv->sta_lock); - - if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { - IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", - addr); - goto out_err; - } - - if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { - IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", - addr); - goto out_err; - } - - if (priv->stations[sta_id].used & IWL_STA_LOCAL) { - kfree(priv->stations[sta_id].lq); - priv->stations[sta_id].lq = NULL; - } - - for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) - memset(&priv->tid_data[sta_id][tid], 0, - sizeof(priv->tid_data[sta_id][tid])); - - priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; - - priv->num_stations--; - - if (WARN_ON(priv->num_stations < 0)) - priv->num_stations = 0; - - spin_unlock_bh(&priv->sta_lock); - - return iwl_send_remove_station(priv, addr, sta_id, false); -out_err: - spin_unlock_bh(&priv->sta_lock); - return -EINVAL; -} - -void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, - const u8 *addr) -{ - u8 tid; - - if (!iwl_is_ready(priv)) { - IWL_DEBUG_INFO(priv, - "Unable to remove station %pM, device not ready.\n", - addr); - return; - } - - IWL_DEBUG_ASSOC(priv, "Deactivating STA: %pM (%d)\n", addr, sta_id); - - if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) - return; - - spin_lock_bh(&priv->sta_lock); - - WARN_ON_ONCE(!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)); - - for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) - memset(&priv->tid_data[sta_id][tid], 0, - sizeof(priv->tid_data[sta_id][tid])); - - priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; - priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; - - priv->num_stations--; - - if (WARN_ON_ONCE(priv->num_stations < 0)) - priv->num_stations = 0; - - spin_unlock_bh(&priv->sta_lock); -} - -static void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - u8 sta_id, struct iwl_link_quality_cmd *link_cmd) -{ - int i, r; - u32 rate_flags = 0; - __le32 rate_n_flags; - - lockdep_assert_held(&priv->mutex); - - memset(link_cmd, 0, sizeof(*link_cmd)); - - /* Set up the rate scaling to start at selected rate, fall back - * all the way down to 1M in IEEE order, and then spin on 1M */ - if (priv->band == IEEE80211_BAND_5GHZ) - r = IWL_RATE_6M_INDEX; - else if (ctx && ctx->vif && ctx->vif->p2p) - r = IWL_RATE_6M_INDEX; - else - r = IWL_RATE_1M_INDEX; - - if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; - - rate_flags |= first_antenna(priv->nvm_data->valid_tx_ant) << - RATE_MCS_ANT_POS; - rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) - link_cmd->rs_table[i].rate_n_flags = rate_n_flags; - - link_cmd->general_params.single_stream_ant_msk = - first_antenna(priv->nvm_data->valid_tx_ant); - - link_cmd->general_params.dual_stream_ant_msk = - priv->nvm_data->valid_tx_ant & - ~first_antenna(priv->nvm_data->valid_tx_ant); - if (!link_cmd->general_params.dual_stream_ant_msk) { - link_cmd->general_params.dual_stream_ant_msk = ANT_AB; - } else if (num_of_ant(priv->nvm_data->valid_tx_ant) == 2) { - link_cmd->general_params.dual_stream_ant_msk = - priv->nvm_data->valid_tx_ant; - } - - link_cmd->agg_params.agg_dis_start_th = - LINK_QUAL_AGG_DISABLE_START_DEF; - link_cmd->agg_params.agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - - link_cmd->sta_id = sta_id; -} - -/** - * iwl_clear_ucode_stations - clear ucode station table bits - * - * This function clears all the bits in the driver indicating - * which stations are active in the ucode. Call when something - * other than explicit station management would cause this in - * the ucode, e.g. unassociated RXON. - */ -void iwl_clear_ucode_stations(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int i; - bool cleared = false; - - IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n"); - - spin_lock_bh(&priv->sta_lock); - for (i = 0; i < IWLAGN_STATION_COUNT; i++) { - if (ctx && ctx->ctxid != priv->stations[i].ctxid) - continue; - - if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) { - IWL_DEBUG_INFO(priv, - "Clearing ucode active for station %d\n", i); - priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; - cleared = true; - } - } - spin_unlock_bh(&priv->sta_lock); - - if (!cleared) - IWL_DEBUG_INFO(priv, - "No active stations found to be cleared\n"); -} - -/** - * iwl_restore_stations() - Restore driver known stations to device - * - * All stations considered active by driver, but not present in ucode, is - * restored. - * - * Function sleeps. - */ -void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) -{ - struct iwl_addsta_cmd sta_cmd; - static const struct iwl_link_quality_cmd zero_lq = {}; - struct iwl_link_quality_cmd lq; - int i; - bool found = false; - int ret; - bool send_lq; - - if (!iwl_is_ready(priv)) { - IWL_DEBUG_INFO(priv, - "Not ready yet, not restoring any stations.\n"); - return; - } - - IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); - spin_lock_bh(&priv->sta_lock); - for (i = 0; i < IWLAGN_STATION_COUNT; i++) { - if (ctx->ctxid != priv->stations[i].ctxid) - continue; - if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && - !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { - IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", - priv->stations[i].sta.sta.addr); - priv->stations[i].sta.mode = 0; - priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; - found = true; - } - } - - for (i = 0; i < IWLAGN_STATION_COUNT; i++) { - if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { - memcpy(&sta_cmd, &priv->stations[i].sta, - sizeof(struct iwl_addsta_cmd)); - send_lq = false; - if (priv->stations[i].lq) { - if (priv->wowlan) - iwl_sta_fill_lq(priv, ctx, i, &lq); - else - memcpy(&lq, priv->stations[i].lq, - sizeof(struct iwl_link_quality_cmd)); - - if (memcmp(&lq, &zero_lq, sizeof(lq))) - send_lq = true; - } - spin_unlock_bh(&priv->sta_lock); - ret = iwl_send_add_sta(priv, &sta_cmd, 0); - if (ret) { - spin_lock_bh(&priv->sta_lock); - IWL_ERR(priv, "Adding station %pM failed.\n", - priv->stations[i].sta.sta.addr); - priv->stations[i].used &= - ~IWL_STA_DRIVER_ACTIVE; - priv->stations[i].used &= - ~IWL_STA_UCODE_INPROGRESS; - continue; - } - /* - * Rate scaling has already been initialized, send - * current LQ command - */ - if (send_lq) - iwl_send_lq_cmd(priv, ctx, &lq, 0, true); - spin_lock_bh(&priv->sta_lock); - priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; - } - } - - spin_unlock_bh(&priv->sta_lock); - if (!found) - IWL_DEBUG_INFO(priv, "Restoring all known stations .... " - "no stations to be restored.\n"); - else - IWL_DEBUG_INFO(priv, "Restoring all known stations .... " - "complete.\n"); -} - -int iwl_get_free_ucode_key_offset(struct iwl_priv *priv) -{ - int i; - - for (i = 0; i < priv->sta_key_max_num; i++) - if (!test_and_set_bit(i, &priv->ucode_key_table)) - return i; - - return WEP_INVALID_OFFSET; -} - -void iwl_dealloc_bcast_stations(struct iwl_priv *priv) -{ - int i; - - spin_lock_bh(&priv->sta_lock); - for (i = 0; i < IWLAGN_STATION_COUNT; i++) { - if (!(priv->stations[i].used & IWL_STA_BCAST)) - continue; - - priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; - priv->num_stations--; - if (WARN_ON(priv->num_stations < 0)) - priv->num_stations = 0; - kfree(priv->stations[i].lq); - priv->stations[i].lq = NULL; - } - spin_unlock_bh(&priv->sta_lock); -} - -#ifdef CONFIG_IWLWIFI_DEBUG -static void iwl_dump_lq_cmd(struct iwl_priv *priv, - struct iwl_link_quality_cmd *lq) -{ - int i; - IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id); - IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n", - lq->general_params.single_stream_ant_msk, - lq->general_params.dual_stream_ant_msk); - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) - IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n", - i, lq->rs_table[i].rate_n_flags); -} -#else -static inline void iwl_dump_lq_cmd(struct iwl_priv *priv, - struct iwl_link_quality_cmd *lq) -{ -} -#endif - -/** - * is_lq_table_valid() - Test one aspect of LQ cmd for validity - * - * It sometimes happens when a HT rate has been in use and we - * loose connectivity with AP then mac80211 will first tell us that the - * current channel is not HT anymore before removing the station. In such a - * scenario the RXON flags will be updated to indicate we are not - * communicating HT anymore, but the LQ command may still contain HT rates. - * Test for this to prevent driver from sending LQ command between the time - * RXON flags are updated and when LQ command is updated. - */ -static bool is_lq_table_valid(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct iwl_link_quality_cmd *lq) -{ - int i; - - if (ctx->ht.enabled) - return true; - - IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n", - ctx->active.channel); - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & - RATE_MCS_HT_MSK) { - IWL_DEBUG_INFO(priv, - "index %d of LQ expects HT channel\n", - i); - return false; - } - } - return true; -} - -/** - * iwl_send_lq_cmd() - Send link quality command - * @init: This command is sent as part of station initialization right - * after station has been added. - * - * The link quality command is sent as the last step of station creation. - * This is the special case in which init is set and we call a callback in - * this case to clear the state indicating that station creation is in - * progress. - */ -int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct iwl_link_quality_cmd *lq, u8 flags, bool init) -{ - int ret = 0; - struct iwl_host_cmd cmd = { - .id = REPLY_TX_LINK_QUALITY_CMD, - .len = { sizeof(struct iwl_link_quality_cmd), }, - .flags = flags, - .data = { lq, }, - }; - - if (WARN_ON(lq->sta_id == IWL_INVALID_STATION)) - return -EINVAL; - - - spin_lock_bh(&priv->sta_lock); - if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) { - spin_unlock_bh(&priv->sta_lock); - return -EINVAL; - } - spin_unlock_bh(&priv->sta_lock); - - iwl_dump_lq_cmd(priv, lq); - if (WARN_ON(init && (cmd.flags & CMD_ASYNC))) - return -EINVAL; - - if (is_lq_table_valid(priv, ctx, lq)) - ret = iwl_dvm_send_cmd(priv, &cmd); - else - ret = -EINVAL; - - if (cmd.flags & CMD_ASYNC) - return ret; - - if (init) { - IWL_DEBUG_INFO(priv, "init LQ command complete, " - "clearing sta addition status for sta %d\n", - lq->sta_id); - spin_lock_bh(&priv->sta_lock); - priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; - spin_unlock_bh(&priv->sta_lock); - } - return ret; -} - - -static struct iwl_link_quality_cmd * -iwl_sta_alloc_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - u8 sta_id) -{ - struct iwl_link_quality_cmd *link_cmd; - - link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL); - if (!link_cmd) { - IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n"); - return NULL; - } - - iwl_sta_fill_lq(priv, ctx, sta_id, link_cmd); - - return link_cmd; -} - -/* - * iwlagn_add_bssid_station - Add the special IBSS BSSID station - * - * Function sleeps. - */ -int iwlagn_add_bssid_station(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - const u8 *addr, u8 *sta_id_r) -{ - int ret; - u8 sta_id; - struct iwl_link_quality_cmd *link_cmd; - - if (sta_id_r) - *sta_id_r = IWL_INVALID_STATION; - - ret = iwl_add_station_common(priv, ctx, addr, 0, NULL, &sta_id); - if (ret) { - IWL_ERR(priv, "Unable to add station %pM\n", addr); - return ret; - } - - if (sta_id_r) - *sta_id_r = sta_id; - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].used |= IWL_STA_LOCAL; - spin_unlock_bh(&priv->sta_lock); - - /* Set up default rate scaling table in device's station table */ - link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); - if (!link_cmd) { - IWL_ERR(priv, - "Unable to initialize rate scaling for station %pM.\n", - addr); - return -ENOMEM; - } - - ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true); - if (ret) - IWL_ERR(priv, "Link quality command failed (%d)\n", ret); - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].lq = link_cmd; - spin_unlock_bh(&priv->sta_lock); - - return 0; -} - -/* - * static WEP keys - * - * For each context, the device has a table of 4 static WEP keys - * (one for each key index) that is updated with the following - * commands. - */ - -static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - bool send_if_empty) -{ - int i, not_empty = 0; - u8 buff[sizeof(struct iwl_wep_cmd) + - sizeof(struct iwl_wep_key) * WEP_KEYS_MAX]; - struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff; - size_t cmd_size = sizeof(struct iwl_wep_cmd); - struct iwl_host_cmd cmd = { - .id = ctx->wep_key_cmd, - .data = { wep_cmd, }, - }; - - might_sleep(); - - memset(wep_cmd, 0, cmd_size + - (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX)); - - for (i = 0; i < WEP_KEYS_MAX ; i++) { - wep_cmd->key[i].key_index = i; - if (ctx->wep_keys[i].key_size) { - wep_cmd->key[i].key_offset = i; - not_empty = 1; - } else { - wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; - } - - wep_cmd->key[i].key_size = ctx->wep_keys[i].key_size; - memcpy(&wep_cmd->key[i].key[3], ctx->wep_keys[i].key, - ctx->wep_keys[i].key_size); - } - - wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; - wep_cmd->num_keys = WEP_KEYS_MAX; - - cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX; - - cmd.len[0] = cmd_size; - - if (not_empty || send_if_empty) - return iwl_dvm_send_cmd(priv, &cmd); - else - return 0; -} - -int iwl_restore_default_wep_keys(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - lockdep_assert_held(&priv->mutex); - - return iwl_send_static_wepkey_cmd(priv, ctx, false); -} - -int iwl_remove_default_wep_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *keyconf) -{ - int ret; - - lockdep_assert_held(&priv->mutex); - - IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n", - keyconf->keyidx); - - memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0])); - if (iwl_is_rfkill(priv)) { - IWL_DEBUG_WEP(priv, - "Not sending REPLY_WEPKEY command due to RFKILL.\n"); - /* but keys in device are clear anyway so return success */ - return 0; - } - ret = iwl_send_static_wepkey_cmd(priv, ctx, 1); - IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n", - keyconf->keyidx, ret); - - return ret; -} - -int iwl_set_default_wep_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *keyconf) -{ - int ret; - - lockdep_assert_held(&priv->mutex); - - if (keyconf->keylen != WEP_KEY_LEN_128 && - keyconf->keylen != WEP_KEY_LEN_64) { - IWL_DEBUG_WEP(priv, - "Bad WEP key length %d\n", keyconf->keylen); - return -EINVAL; - } - - keyconf->hw_key_idx = IWLAGN_HW_KEY_DEFAULT; - - ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; - memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key, - keyconf->keylen); - - ret = iwl_send_static_wepkey_cmd(priv, ctx, false); - IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n", - keyconf->keylen, keyconf->keyidx, ret); - - return ret; -} - -/* - * dynamic (per-station) keys - * - * The dynamic keys are a little more complicated. The device has - * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys. - * These are linked to stations by a table that contains an index - * into the key table for each station/key index/{mcast,unicast}, - * i.e. it's basically an array of pointers like this: - * key_offset_t key_mapping[NUM_STATIONS][4][2]; - * (it really works differently, but you can think of it as such) - * - * The key uploading and linking happens in the same command, the - * add station command with STA_MODIFY_KEY_MASK. - */ - -static u8 iwlagn_key_sta_id(struct iwl_priv *priv, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - - if (sta) - return iwl_sta_id(sta); - - /* - * The device expects GTKs for station interfaces to be - * installed as GTKs for the AP station. If we have no - * station ID, then use the ap_sta_id in that case. - */ - if (vif->type == NL80211_IFTYPE_STATION && vif_priv->ctx) - return vif_priv->ctx->ap_sta_id; - - return IWL_INVALID_STATION; -} - -static int iwlagn_send_sta_key(struct iwl_priv *priv, - struct ieee80211_key_conf *keyconf, - u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k, - u32 cmd_flags) -{ - __le16 key_flags; - struct iwl_addsta_cmd sta_cmd; - int i; - - spin_lock_bh(&priv->sta_lock); - memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); - spin_unlock_bh(&priv->sta_lock); - - key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); - key_flags |= STA_KEY_FLG_MAP_KEY_MSK; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - key_flags |= STA_KEY_FLG_CCMP; - memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); - break; - case WLAN_CIPHER_SUITE_TKIP: - key_flags |= STA_KEY_FLG_TKIP; - sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; - for (i = 0; i < 5; i++) - sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); - memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); - break; - case WLAN_CIPHER_SUITE_WEP104: - key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; - /* fall through */ - case WLAN_CIPHER_SUITE_WEP40: - key_flags |= STA_KEY_FLG_WEP; - memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen); - break; - default: - WARN_ON(1); - return -EINVAL; - } - - if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) - key_flags |= STA_KEY_MULTICAST_MSK; - - /* key pointer (offset) */ - sta_cmd.key.key_offset = keyconf->hw_key_idx; - - sta_cmd.key.key_flags = key_flags; - sta_cmd.mode = STA_CONTROL_MODIFY_MSK; - sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; - - return iwl_send_add_sta(priv, &sta_cmd, cmd_flags); -} - -void iwl_update_tkip_key(struct iwl_priv *priv, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) -{ - u8 sta_id = iwlagn_key_sta_id(priv, vif, sta); - - if (sta_id == IWL_INVALID_STATION) - return; - - if (iwl_scan_cancel(priv)) { - /* cancel scan failed, just live w/ bad key and rely - briefly on SW decryption */ - return; - } - - iwlagn_send_sta_key(priv, keyconf, sta_id, - iv32, phase1key, CMD_ASYNC); -} - -int iwl_remove_dynamic_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta) -{ - struct iwl_addsta_cmd sta_cmd; - u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); - __le16 key_flags; - - /* if station isn't there, neither is the key */ - if (sta_id == IWL_INVALID_STATION) - return -ENOENT; - - spin_lock_bh(&priv->sta_lock); - memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); - if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) - sta_id = IWL_INVALID_STATION; - spin_unlock_bh(&priv->sta_lock); - - if (sta_id == IWL_INVALID_STATION) - return 0; - - lockdep_assert_held(&priv->mutex); - - ctx->key_mapping_keys--; - - IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n", - keyconf->keyidx, sta_id); - - if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table)) - IWL_ERR(priv, "offset %d not used in uCode key table.\n", - keyconf->hw_key_idx); - - key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); - key_flags |= STA_KEY_FLG_MAP_KEY_MSK | STA_KEY_FLG_NO_ENC | - STA_KEY_FLG_INVALID; - - if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) - key_flags |= STA_KEY_MULTICAST_MSK; - - sta_cmd.key.key_flags = key_flags; - sta_cmd.key.key_offset = keyconf->hw_key_idx; - sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; - sta_cmd.mode = STA_CONTROL_MODIFY_MSK; - - return iwl_send_add_sta(priv, &sta_cmd, 0); -} - -int iwl_set_dynamic_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta) -{ - struct ieee80211_key_seq seq; - u16 p1k[5]; - int ret; - u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); - const u8 *addr; - - if (sta_id == IWL_INVALID_STATION) - return -EINVAL; - - lockdep_assert_held(&priv->mutex); - - keyconf->hw_key_idx = iwl_get_free_ucode_key_offset(priv); - if (keyconf->hw_key_idx == WEP_INVALID_OFFSET) - return -ENOSPC; - - ctx->key_mapping_keys++; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - if (sta) - addr = sta->addr; - else /* station mode case only */ - addr = ctx->active.bssid_addr; - - /* pre-fill phase 1 key into device cache */ - ieee80211_get_key_rx_seq(keyconf, 0, &seq); - ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); - ret = iwlagn_send_sta_key(priv, keyconf, sta_id, - seq.tkip.iv32, p1k, 0); - break; - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - ret = iwlagn_send_sta_key(priv, keyconf, sta_id, - 0, NULL, 0); - break; - default: - IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher); - ret = -EINVAL; - } - - if (ret) { - ctx->key_mapping_keys--; - clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table); - } - - IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", - keyconf->cipher, keyconf->keylen, keyconf->keyidx, - sta ? sta->addr : NULL, ret); - - return ret; -} - -/** - * iwlagn_alloc_bcast_station - add broadcast station into driver's station table. - * - * This adds the broadcast station into the driver's station table - * and marks it driver active, so that it will be restored to the - * device at the next best time. - */ -int iwlagn_alloc_bcast_station(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - struct iwl_link_quality_cmd *link_cmd; - u8 sta_id; - - spin_lock_bh(&priv->sta_lock); - sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL); - if (sta_id == IWL_INVALID_STATION) { - IWL_ERR(priv, "Unable to prepare broadcast station\n"); - spin_unlock_bh(&priv->sta_lock); - - return -EINVAL; - } - - priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE; - priv->stations[sta_id].used |= IWL_STA_BCAST; - spin_unlock_bh(&priv->sta_lock); - - link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); - if (!link_cmd) { - IWL_ERR(priv, - "Unable to initialize rate scaling for bcast station.\n"); - return -ENOMEM; - } - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].lq = link_cmd; - spin_unlock_bh(&priv->sta_lock); - - return 0; -} - -/** - * iwl_update_bcast_station - update broadcast station's LQ command - * - * Only used by iwlagn. Placed here to have all bcast station management - * code together. - */ -int iwl_update_bcast_station(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - struct iwl_link_quality_cmd *link_cmd; - u8 sta_id = ctx->bcast_sta_id; - - link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); - if (!link_cmd) { - IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n"); - return -ENOMEM; - } - - spin_lock_bh(&priv->sta_lock); - if (priv->stations[sta_id].lq) - kfree(priv->stations[sta_id].lq); - else - IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n"); - priv->stations[sta_id].lq = link_cmd; - spin_unlock_bh(&priv->sta_lock); - - return 0; -} - -int iwl_update_bcast_stations(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - int ret = 0; - - for_each_context(priv, ctx) { - ret = iwl_update_bcast_station(priv, ctx); - if (ret) - break; - } - - return ret; -} - -/** - * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table - */ -int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) -{ - struct iwl_addsta_cmd sta_cmd; - - lockdep_assert_held(&priv->mutex); - - /* Remove "disable" flag, to enable Tx for this TID */ - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; - priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); - priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); - spin_unlock_bh(&priv->sta_lock); - - return iwl_send_add_sta(priv, &sta_cmd, 0); -} - -int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, - int tid, u16 ssn) -{ - int sta_id; - struct iwl_addsta_cmd sta_cmd; - - lockdep_assert_held(&priv->mutex); - - sta_id = iwl_sta_id(sta); - if (sta_id == IWL_INVALID_STATION) - return -ENXIO; - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].sta.station_flags_msk = 0; - priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; - priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid; - priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); - priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); - spin_unlock_bh(&priv->sta_lock); - - return iwl_send_add_sta(priv, &sta_cmd, 0); -} - -int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, - int tid) -{ - int sta_id; - struct iwl_addsta_cmd sta_cmd; - - lockdep_assert_held(&priv->mutex); - - sta_id = iwl_sta_id(sta); - if (sta_id == IWL_INVALID_STATION) { - IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); - return -ENXIO; - } - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].sta.station_flags_msk = 0; - priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; - priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid; - priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); - spin_unlock_bh(&priv->sta_lock); - - return iwl_send_add_sta(priv, &sta_cmd, 0); -} - - - -void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) -{ - struct iwl_addsta_cmd cmd = { - .mode = STA_CONTROL_MODIFY_MSK, - .station_flags = STA_FLG_PWR_SAVE_MSK, - .station_flags_msk = STA_FLG_PWR_SAVE_MSK, - .sta.sta_id = sta_id, - .sta.modify_mask = STA_MODIFY_SLEEP_TX_COUNT_MSK, - .sleep_tx_count = cpu_to_le16(cnt), - }; - - iwl_send_add_sta(priv, &cmd, CMD_ASYNC); -} diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c deleted file mode 100644 index c4736c8834c5..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/tt.c +++ /dev/null @@ -1,685 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ - - -#include -#include -#include -#include -#include "iwl-io.h" -#include "iwl-modparams.h" -#include "iwl-debug.h" -#include "agn.h" -#include "dev.h" -#include "commands.h" -#include "tt.h" - -/* default Thermal Throttling transaction table - * Current state | Throttling Down | Throttling Up - *============================================================================= - * Condition Nxt State Condition Nxt State Condition Nxt State - *----------------------------------------------------------------------------- - * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A - * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 - * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 - * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 - *============================================================================= - */ -static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { - {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, - {IWL_TI_1, 105, CT_KILL_THRESHOLD - 1}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} -}; -static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { - {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, - {IWL_TI_2, 110, CT_KILL_THRESHOLD - 1}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} -}; -static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { - {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} -}; -static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { - {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, - {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, - {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX} -}; - -/* Advance Thermal Throttling default restriction table */ -static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = { - {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true }, - {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true }, - {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false }, - {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false } -}; - -bool iwl_tt_is_low_power_state(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - if (tt->state >= IWL_TI_1) - return true; - return false; -} - -u8 iwl_tt_current_power_mode(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - return tt->tt_power_mode; -} - -bool iwl_ht_enabled(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - struct iwl_tt_restriction *restriction; - - if (!priv->thermal_throttle.advanced_tt) - return true; - restriction = tt->restriction + tt->state; - return restriction->is_ht; -} - -static bool iwl_within_ct_kill_margin(struct iwl_priv *priv) -{ - s32 temp = priv->temperature; /* degrees CELSIUS except specified */ - bool within_margin = false; - - if (!priv->thermal_throttle.advanced_tt) - within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= - CT_KILL_THRESHOLD_LEGACY) ? true : false; - else - within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= - CT_KILL_THRESHOLD) ? true : false; - return within_margin; -} - -bool iwl_check_for_ct_kill(struct iwl_priv *priv) -{ - bool is_ct_kill = false; - - if (iwl_within_ct_kill_margin(priv)) { - iwl_tt_enter_ct_kill(priv); - is_ct_kill = true; - } - return is_ct_kill; -} - -enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - struct iwl_tt_restriction *restriction; - - if (!priv->thermal_throttle.advanced_tt) - return IWL_ANT_OK_MULTI; - restriction = tt->restriction + tt->state; - return restriction->tx_stream; -} - -enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - struct iwl_tt_restriction *restriction; - - if (!priv->thermal_throttle.advanced_tt) - return IWL_ANT_OK_MULTI; - restriction = tt->restriction + tt->state; - return restriction->rx_stream; -} - -#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ -#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */ - -/* - * toggle the bit to wake up uCode and check the temperature - * if the temperature is below CT, uCode will stay awake and send card - * state notification with CT_KILL bit clear to inform Thermal Throttling - * Management to change state. Otherwise, uCode will go back to sleep - * without doing anything, driver should continue the 5 seconds timer - * to wake up uCode for temperature check until temperature drop below CT - */ -static void iwl_tt_check_exit_ct_kill(unsigned long data) -{ - struct iwl_priv *priv = (struct iwl_priv *)data; - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - unsigned long flags; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (tt->state == IWL_TI_CT_KILL) { - if (priv->thermal_throttle.ct_kill_toggle) { - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); - priv->thermal_throttle.ct_kill_toggle = false; - } else { - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); - priv->thermal_throttle.ct_kill_toggle = true; - } - iwl_read32(priv->trans, CSR_UCODE_DRV_GP1); - if (iwl_trans_grab_nic_access(priv->trans, false, &flags)) - iwl_trans_release_nic_access(priv->trans, &flags); - - /* Reschedule the ct_kill timer to occur in - * CT_KILL_EXIT_DURATION seconds to ensure we get a - * thermal update */ - IWL_DEBUG_TEMP(priv, "schedule ct_kill exit timer\n"); - mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, - jiffies + CT_KILL_EXIT_DURATION * HZ); - } -} - -static void iwl_perform_ct_kill_task(struct iwl_priv *priv, - bool stop) -{ - if (stop) { - IWL_DEBUG_TEMP(priv, "Stop all queues\n"); - if (priv->mac80211_registered) - ieee80211_stop_queues(priv->hw); - IWL_DEBUG_TEMP(priv, - "Schedule 5 seconds CT_KILL Timer\n"); - mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, - jiffies + CT_KILL_EXIT_DURATION * HZ); - } else { - IWL_DEBUG_TEMP(priv, "Wake all queues\n"); - if (priv->mac80211_registered) - ieee80211_wake_queues(priv->hw); - } -} - -static void iwl_tt_ready_for_ct_kill(unsigned long data) -{ - struct iwl_priv *priv = (struct iwl_priv *)data; - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - /* temperature timer expired, ready to go into CT_KILL state */ - if (tt->state != IWL_TI_CT_KILL) { - IWL_DEBUG_TEMP(priv, "entering CT_KILL state when " - "temperature timer expired\n"); - tt->state = IWL_TI_CT_KILL; - set_bit(STATUS_CT_KILL, &priv->status); - iwl_perform_ct_kill_task(priv, true); - } -} - -static void iwl_prepare_ct_kill_task(struct iwl_priv *priv) -{ - IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n"); - /* make request to retrieve statistics information */ - iwl_send_statistics_request(priv, 0, false); - /* Reschedule the ct_kill wait timer */ - mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm, - jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION)); -} - -#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY) -#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100) -#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90) - -/* - * Legacy thermal throttling - * 1) Avoid NIC destruction due to high temperatures - * Chip will identify dangerously high temperatures that can - * harm the device and will power down - * 2) Avoid the NIC power down due to high temperature - * Throttle early enough to lower the power consumption before - * drastic steps are needed - */ -static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - enum iwl_tt_state old_state; - -#ifdef CONFIG_IWLWIFI_DEBUG - if ((tt->tt_previous_temp) && - (temp > tt->tt_previous_temp) && - ((temp - tt->tt_previous_temp) > - IWL_TT_INCREASE_MARGIN)) { - IWL_DEBUG_TEMP(priv, - "Temperature increase %d degree Celsius\n", - (temp - tt->tt_previous_temp)); - } -#endif - old_state = tt->state; - /* in Celsius */ - if (temp >= IWL_MINIMAL_POWER_THRESHOLD) - tt->state = IWL_TI_CT_KILL; - else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2) - tt->state = IWL_TI_2; - else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1) - tt->state = IWL_TI_1; - else - tt->state = IWL_TI_0; - -#ifdef CONFIG_IWLWIFI_DEBUG - tt->tt_previous_temp = temp; -#endif - /* stop ct_kill_waiting_tm timer */ - del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); - if (tt->state != old_state) { - switch (tt->state) { - case IWL_TI_0: - /* - * When the system is ready to go back to IWL_TI_0 - * we only have to call iwl_power_update_mode() to - * do so. - */ - break; - case IWL_TI_1: - tt->tt_power_mode = IWL_POWER_INDEX_3; - break; - case IWL_TI_2: - tt->tt_power_mode = IWL_POWER_INDEX_4; - break; - default: - tt->tt_power_mode = IWL_POWER_INDEX_5; - break; - } - mutex_lock(&priv->mutex); - if (old_state == IWL_TI_CT_KILL) - clear_bit(STATUS_CT_KILL, &priv->status); - if (tt->state != IWL_TI_CT_KILL && - iwl_power_update_mode(priv, true)) { - /* TT state not updated - * try again during next temperature read - */ - if (old_state == IWL_TI_CT_KILL) - set_bit(STATUS_CT_KILL, &priv->status); - tt->state = old_state; - IWL_ERR(priv, "Cannot update power mode, " - "TT state not updated\n"); - } else { - if (tt->state == IWL_TI_CT_KILL) { - if (force) { - set_bit(STATUS_CT_KILL, &priv->status); - iwl_perform_ct_kill_task(priv, true); - } else { - iwl_prepare_ct_kill_task(priv); - tt->state = old_state; - } - } else if (old_state == IWL_TI_CT_KILL && - tt->state != IWL_TI_CT_KILL) - iwl_perform_ct_kill_task(priv, false); - IWL_DEBUG_TEMP(priv, "Temperature state changed %u\n", - tt->state); - IWL_DEBUG_TEMP(priv, "Power Index change to %u\n", - tt->tt_power_mode); - } - mutex_unlock(&priv->mutex); - } -} - -/* - * Advance thermal throttling - * 1) Avoid NIC destruction due to high temperatures - * Chip will identify dangerously high temperatures that can - * harm the device and will power down - * 2) Avoid the NIC power down due to high temperature - * Throttle early enough to lower the power consumption before - * drastic steps are needed - * Actions include relaxing the power down sleep thresholds and - * decreasing the number of TX streams - * 3) Avoid throughput performance impact as much as possible - * - *============================================================================= - * Condition Nxt State Condition Nxt State Condition Nxt State - *----------------------------------------------------------------------------- - * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A - * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 - * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 - * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 - *============================================================================= - */ -static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - int i; - bool changed = false; - enum iwl_tt_state old_state; - struct iwl_tt_trans *transaction; - - old_state = tt->state; - for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) { - /* based on the current TT state, - * find the curresponding transaction table - * each table has (IWL_TI_STATE_MAX - 1) entries - * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) - * will advance to the correct table. - * then based on the current temperature - * find the next state need to transaction to - * go through all the possible (IWL_TI_STATE_MAX - 1) entries - * in the current table to see if transaction is needed - */ - transaction = tt->transaction + - ((old_state * (IWL_TI_STATE_MAX - 1)) + i); - if (temp >= transaction->tt_low && - temp <= transaction->tt_high) { -#ifdef CONFIG_IWLWIFI_DEBUG - if ((tt->tt_previous_temp) && - (temp > tt->tt_previous_temp) && - ((temp - tt->tt_previous_temp) > - IWL_TT_INCREASE_MARGIN)) { - IWL_DEBUG_TEMP(priv, - "Temperature increase %d " - "degree Celsius\n", - (temp - tt->tt_previous_temp)); - } - tt->tt_previous_temp = temp; -#endif - if (old_state != - transaction->next_state) { - changed = true; - tt->state = - transaction->next_state; - } - break; - } - } - /* stop ct_kill_waiting_tm timer */ - del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); - if (changed) { - if (tt->state >= IWL_TI_1) { - /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ - tt->tt_power_mode = IWL_POWER_INDEX_5; - - if (!iwl_ht_enabled(priv)) { - struct iwl_rxon_context *ctx; - - for_each_context(priv, ctx) { - struct iwl_rxon_cmd *rxon; - - rxon = &ctx->staging; - - /* disable HT */ - rxon->flags &= ~( - RXON_FLG_CHANNEL_MODE_MSK | - RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | - RXON_FLG_HT40_PROT_MSK | - RXON_FLG_HT_PROT_MSK); - } - } else { - /* check HT capability and set - * according to the system HT capability - * in case get disabled before */ - iwl_set_rxon_ht(priv, &priv->current_ht_config); - } - - } else { - /* - * restore system power setting -- it will be - * recalculated automatically. - */ - - /* check HT capability and set - * according to the system HT capability - * in case get disabled before */ - iwl_set_rxon_ht(priv, &priv->current_ht_config); - } - mutex_lock(&priv->mutex); - if (old_state == IWL_TI_CT_KILL) - clear_bit(STATUS_CT_KILL, &priv->status); - if (tt->state != IWL_TI_CT_KILL && - iwl_power_update_mode(priv, true)) { - /* TT state not updated - * try again during next temperature read - */ - IWL_ERR(priv, "Cannot update power mode, " - "TT state not updated\n"); - if (old_state == IWL_TI_CT_KILL) - set_bit(STATUS_CT_KILL, &priv->status); - tt->state = old_state; - } else { - IWL_DEBUG_TEMP(priv, - "Thermal Throttling to new state: %u\n", - tt->state); - if (old_state != IWL_TI_CT_KILL && - tt->state == IWL_TI_CT_KILL) { - if (force) { - IWL_DEBUG_TEMP(priv, - "Enter IWL_TI_CT_KILL\n"); - set_bit(STATUS_CT_KILL, &priv->status); - iwl_perform_ct_kill_task(priv, true); - } else { - tt->state = old_state; - iwl_prepare_ct_kill_task(priv); - } - } else if (old_state == IWL_TI_CT_KILL && - tt->state != IWL_TI_CT_KILL) { - IWL_DEBUG_TEMP(priv, "Exit IWL_TI_CT_KILL\n"); - iwl_perform_ct_kill_task(priv, false); - } - } - mutex_unlock(&priv->mutex); - } -} - -/* Card State Notification indicated reach critical temperature - * if PSP not enable, no Thermal Throttling function will be performed - * just set the GP1 bit to acknowledge the event - * otherwise, go into IWL_TI_CT_KILL state - * since Card State Notification will not provide any temperature reading - * for Legacy mode - * so just pass the CT_KILL temperature to iwl_legacy_tt_handler() - * for advance mode - * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state - */ -static void iwl_bg_ct_enter(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter); - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (!iwl_is_ready(priv)) - return; - - if (tt->state != IWL_TI_CT_KILL) { - IWL_ERR(priv, "Device reached critical temperature " - "- ucode going to sleep!\n"); - if (!priv->thermal_throttle.advanced_tt) - iwl_legacy_tt_handler(priv, - IWL_MINIMAL_POWER_THRESHOLD, - true); - else - iwl_advance_tt_handler(priv, - CT_KILL_THRESHOLD + 1, true); - } -} - -/* Card State Notification indicated out of critical temperature - * since Card State Notification will not provide any temperature reading - * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature - * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state - */ -static void iwl_bg_ct_exit(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit); - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (!iwl_is_ready(priv)) - return; - - /* stop ct_kill_exit_tm timer */ - del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); - - if (tt->state == IWL_TI_CT_KILL) { - IWL_ERR(priv, - "Device temperature below critical" - "- ucode awake!\n"); - /* - * exit from CT_KILL state - * reset the current temperature reading - */ - priv->temperature = 0; - if (!priv->thermal_throttle.advanced_tt) - iwl_legacy_tt_handler(priv, - IWL_REDUCED_PERFORMANCE_THRESHOLD_2, - true); - else - iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD, - true); - } -} - -void iwl_tt_enter_ct_kill(struct iwl_priv *priv) -{ - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - IWL_DEBUG_TEMP(priv, "Queueing critical temperature enter.\n"); - queue_work(priv->workqueue, &priv->ct_enter); -} - -void iwl_tt_exit_ct_kill(struct iwl_priv *priv) -{ - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - IWL_DEBUG_TEMP(priv, "Queueing critical temperature exit.\n"); - queue_work(priv->workqueue, &priv->ct_exit); -} - -static void iwl_bg_tt_work(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work); - s32 temp = priv->temperature; /* degrees CELSIUS except specified */ - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (!priv->thermal_throttle.advanced_tt) - iwl_legacy_tt_handler(priv, temp, false); - else - iwl_advance_tt_handler(priv, temp, false); -} - -void iwl_tt_handler(struct iwl_priv *priv) -{ - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - IWL_DEBUG_TEMP(priv, "Queueing thermal throttling work.\n"); - queue_work(priv->workqueue, &priv->tt_work); -} - -/* Thermal throttling initialization - * For advance thermal throttling: - * Initialize Thermal Index and temperature threshold table - * Initialize thermal throttling restriction table - */ -void iwl_tt_initialize(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1); - struct iwl_tt_trans *transaction; - - IWL_DEBUG_TEMP(priv, "Initialize Thermal Throttling\n"); - - memset(tt, 0, sizeof(struct iwl_tt_mgmt)); - - tt->state = IWL_TI_0; - setup_timer(&priv->thermal_throttle.ct_kill_exit_tm, - iwl_tt_check_exit_ct_kill, (unsigned long)priv); - setup_timer(&priv->thermal_throttle.ct_kill_waiting_tm, - iwl_tt_ready_for_ct_kill, (unsigned long)priv); - /* setup deferred ct kill work */ - INIT_WORK(&priv->tt_work, iwl_bg_tt_work); - INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); - INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); - - if (priv->lib->adv_thermal_throttle) { - IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); - tt->restriction = kcalloc(IWL_TI_STATE_MAX, - sizeof(struct iwl_tt_restriction), - GFP_KERNEL); - tt->transaction = kcalloc(IWL_TI_STATE_MAX * - (IWL_TI_STATE_MAX - 1), - sizeof(struct iwl_tt_trans), - GFP_KERNEL); - if (!tt->restriction || !tt->transaction) { - IWL_ERR(priv, "Fallback to Legacy Throttling\n"); - priv->thermal_throttle.advanced_tt = false; - kfree(tt->restriction); - tt->restriction = NULL; - kfree(tt->transaction); - tt->transaction = NULL; - } else { - transaction = tt->transaction + - (IWL_TI_0 * (IWL_TI_STATE_MAX - 1)); - memcpy(transaction, &tt_range_0[0], size); - transaction = tt->transaction + - (IWL_TI_1 * (IWL_TI_STATE_MAX - 1)); - memcpy(transaction, &tt_range_1[0], size); - transaction = tt->transaction + - (IWL_TI_2 * (IWL_TI_STATE_MAX - 1)); - memcpy(transaction, &tt_range_2[0], size); - transaction = tt->transaction + - (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1)); - memcpy(transaction, &tt_range_3[0], size); - size = sizeof(struct iwl_tt_restriction) * - IWL_TI_STATE_MAX; - memcpy(tt->restriction, - &restriction_range[0], size); - priv->thermal_throttle.advanced_tt = true; - } - } else { - IWL_DEBUG_TEMP(priv, "Legacy Thermal Throttling\n"); - priv->thermal_throttle.advanced_tt = false; - } -} - -/* cleanup thermal throttling management related memory and timer */ -void iwl_tt_exit(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - /* stop ct_kill_exit_tm timer if activated */ - del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); - /* stop ct_kill_waiting_tm timer if activated */ - del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); - cancel_work_sync(&priv->tt_work); - cancel_work_sync(&priv->ct_enter); - cancel_work_sync(&priv->ct_exit); - - if (priv->thermal_throttle.advanced_tt) { - /* free advance thermal throttling memory */ - kfree(tt->restriction); - tt->restriction = NULL; - kfree(tt->transaction); - tt->transaction = NULL; - } -} diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.h b/drivers/net/wireless/iwlwifi/dvm/tt.h deleted file mode 100644 index 507726534b84..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/tt.h +++ /dev/null @@ -1,128 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ -#ifndef __iwl_tt_setting_h__ -#define __iwl_tt_setting_h__ - -#include "commands.h" - -#define IWL_ABSOLUTE_ZERO 0 -#define IWL_ABSOLUTE_MAX 0xFFFFFFFF -#define IWL_TT_INCREASE_MARGIN 5 -#define IWL_TT_CT_KILL_MARGIN 3 - -enum iwl_antenna_ok { - IWL_ANT_OK_NONE, - IWL_ANT_OK_SINGLE, - IWL_ANT_OK_MULTI, -}; - -/* Thermal Throttling State Machine states */ -enum iwl_tt_state { - IWL_TI_0, /* normal temperature, system power state */ - IWL_TI_1, /* high temperature detect, low power state */ - IWL_TI_2, /* higher temperature detected, lower power state */ - IWL_TI_CT_KILL, /* critical temperature detected, lowest power state */ - IWL_TI_STATE_MAX -}; - -/** - * struct iwl_tt_restriction - Thermal Throttling restriction table - * @tx_stream: number of tx stream allowed - * @is_ht: ht enable/disable - * @rx_stream: number of rx stream allowed - * - * This table is used by advance thermal throttling management - * based on the current thermal throttling state, and determines - * the number of tx/rx streams and the status of HT operation. - */ -struct iwl_tt_restriction { - enum iwl_antenna_ok tx_stream; - enum iwl_antenna_ok rx_stream; - bool is_ht; -}; - -/** - * struct iwl_tt_trans - Thermal Throttling transaction table - * @next_state: next thermal throttling mode - * @tt_low: low temperature threshold to change state - * @tt_high: high temperature threshold to change state - * - * This is used by the advanced thermal throttling algorithm - * to determine the next thermal state to go based on the - * current temperature. - */ -struct iwl_tt_trans { - enum iwl_tt_state next_state; - u32 tt_low; - u32 tt_high; -}; - -/** - * struct iwl_tt_mgnt - Thermal Throttling Management structure - * @advanced_tt: advanced thermal throttle required - * @state: current Thermal Throttling state - * @tt_power_mode: Thermal Throttling power mode index - * being used to set power level when - * when thermal throttling state != IWL_TI_0 - * the tt_power_mode should set to different - * power mode based on the current tt state - * @tt_previous_temperature: last measured temperature - * @iwl_tt_restriction: ptr to restriction tbl, used by advance - * thermal throttling to determine how many tx/rx streams - * should be used in tt state; and can HT be enabled or not - * @iwl_tt_trans: ptr to adv trans table, used by advance thermal throttling - * state transaction - * @ct_kill_toggle: used to toggle the CSR bit when checking uCode temperature - * @ct_kill_exit_tm: timer to exit thermal kill - */ -struct iwl_tt_mgmt { - enum iwl_tt_state state; - bool advanced_tt; - u8 tt_power_mode; - bool ct_kill_toggle; -#ifdef CONFIG_IWLWIFI_DEBUG - s32 tt_previous_temp; -#endif - struct iwl_tt_restriction *restriction; - struct iwl_tt_trans *transaction; - struct timer_list ct_kill_exit_tm; - struct timer_list ct_kill_waiting_tm; -}; - -u8 iwl_tt_current_power_mode(struct iwl_priv *priv); -bool iwl_tt_is_low_power_state(struct iwl_priv *priv); -bool iwl_ht_enabled(struct iwl_priv *priv); -enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv); -enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv); -void iwl_tt_enter_ct_kill(struct iwl_priv *priv); -void iwl_tt_exit_ct_kill(struct iwl_priv *priv); -void iwl_tt_handler(struct iwl_priv *priv); -void iwl_tt_initialize(struct iwl_priv *priv); -void iwl_tt_exit(struct iwl_priv *priv); - -#endif /* __iwl_tt_setting_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c deleted file mode 100644 index bddd19769035..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ /dev/null @@ -1,1412 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include -#include -#include "iwl-io.h" -#include "iwl-trans.h" -#include "iwl-agn-hw.h" -#include "dev.h" -#include "agn.h" - -static const u8 tid_to_ac[] = { - IEEE80211_AC_BE, - IEEE80211_AC_BK, - IEEE80211_AC_BK, - IEEE80211_AC_BE, - IEEE80211_AC_VI, - IEEE80211_AC_VI, - IEEE80211_AC_VO, - IEEE80211_AC_VO, -}; - -static void iwlagn_tx_cmd_protection(struct iwl_priv *priv, - struct ieee80211_tx_info *info, - __le16 fc, __le32 *tx_flags) -{ - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS || - info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT || - info->flags & IEEE80211_TX_CTL_AMPDU) - *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK; -} - -/* - * handle build REPLY_TX command notification. - */ -static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, - struct sk_buff *skb, - struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, - struct ieee80211_hdr *hdr, u8 sta_id) -{ - __le16 fc = hdr->frame_control; - __le32 tx_flags = tx_cmd->tx_flags; - - tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) - tx_flags |= TX_CMD_FLG_ACK_MSK; - else - tx_flags &= ~TX_CMD_FLG_ACK_MSK; - - if (ieee80211_is_probe_resp(fc)) - tx_flags |= TX_CMD_FLG_TSF_MSK; - else if (ieee80211_is_back_req(fc)) - tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; - else if (info->band == IEEE80211_BAND_2GHZ && - priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist && - (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || - ieee80211_is_reassoc_req(fc) || - info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) - tx_flags |= TX_CMD_FLG_IGNORE_BT; - - - tx_cmd->sta_id = sta_id; - if (ieee80211_has_morefrags(fc)) - tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; - - if (ieee80211_is_data_qos(fc)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tx_cmd->tid_tspec = qc[0] & 0xf; - tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; - } else { - tx_cmd->tid_tspec = IWL_TID_NON_QOS; - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) - tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - else - tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; - } - - iwlagn_tx_cmd_protection(priv, info, fc, &tx_flags); - - tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); - if (ieee80211_is_mgmt(fc)) { - if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) - tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); - else - tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); - } else { - tx_cmd->timeout.pm_frame_timeout = 0; - } - - tx_cmd->driver_txop = 0; - tx_cmd->tx_flags = tx_flags; - tx_cmd->next_frame_len = 0; -} - -static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, - struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, - __le16 fc) -{ - u32 rate_flags; - int rate_idx; - u8 rts_retry_limit; - u8 data_retry_limit; - u8 rate_plcp; - - if (priv->wowlan) { - rts_retry_limit = IWLAGN_LOW_RETRY_LIMIT; - data_retry_limit = IWLAGN_LOW_RETRY_LIMIT; - } else { - /* Set retry limit on RTS packets */ - rts_retry_limit = IWLAGN_RTS_DFAULT_RETRY_LIMIT; - - /* Set retry limit on DATA packets and Probe Responses*/ - if (ieee80211_is_probe_resp(fc)) { - data_retry_limit = IWLAGN_MGMT_DFAULT_RETRY_LIMIT; - rts_retry_limit = - min(data_retry_limit, rts_retry_limit); - } else if (ieee80211_is_back_req(fc)) - data_retry_limit = IWLAGN_BAR_DFAULT_RETRY_LIMIT; - else - data_retry_limit = IWLAGN_DEFAULT_TX_RETRY; - } - - tx_cmd->data_retry_limit = data_retry_limit; - tx_cmd->rts_retry_limit = rts_retry_limit; - - /* DATA packets will use the uCode station table for rate/antenna - * selection */ - if (ieee80211_is_data(fc)) { - tx_cmd->initial_rate_index = 0; - tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; - return; - } else if (ieee80211_is_back_req(fc)) - tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; - - /** - * If the current TX rate stored in mac80211 has the MCS bit set, it's - * not really a TX rate. Thus, we use the lowest supported rate for - * this band. Also use the lowest supported rate if the stored rate - * index is invalid. - */ - rate_idx = info->control.rates[0].idx; - if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS || - (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY)) - rate_idx = rate_lowest_index( - &priv->nvm_data->bands[info->band], sta); - /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ - if (info->band == IEEE80211_BAND_5GHZ) - rate_idx += IWL_FIRST_OFDM_RATE; - /* Get PLCP rate for tx_cmd->rate_n_flags */ - rate_plcp = iwl_rates[rate_idx].plcp; - /* Zero out flags for this packet */ - rate_flags = 0; - - /* Set CCK flag as needed */ - if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) - rate_flags |= RATE_MCS_CCK_MSK; - - /* Set up antennas */ - if (priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist && - priv->bt_full_concurrent) { - /* operated as 1x1 in full concurrency mode */ - priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, - first_antenna(priv->nvm_data->valid_tx_ant)); - } else - priv->mgmt_tx_ant = iwl_toggle_tx_ant( - priv, priv->mgmt_tx_ant, - priv->nvm_data->valid_tx_ant); - rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant); - - /* Set the rate in the TX cmd */ - tx_cmd->rate_n_flags = iwl_hw_set_rate_n_flags(rate_plcp, rate_flags); -} - -static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv, - struct ieee80211_tx_info *info, - struct iwl_tx_cmd *tx_cmd, - struct sk_buff *skb_frag) -{ - struct ieee80211_key_conf *keyconf = info->control.hw_key; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - tx_cmd->sec_ctl = TX_CMD_SEC_CCM; - memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); - if (info->flags & IEEE80211_TX_CTL_AMPDU) - tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; - break; - - case WLAN_CIPHER_SUITE_TKIP: - tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; - ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); - break; - - case WLAN_CIPHER_SUITE_WEP104: - tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; - /* fall through */ - case WLAN_CIPHER_SUITE_WEP40: - tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP | - (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); - - memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); - - IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " - "with key %d\n", keyconf->keyidx); - break; - - default: - IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher); - break; - } -} - -/** - * iwl_sta_id_or_broadcast - return sta_id or broadcast sta - * @context: the current context - * @sta: mac80211 station - * - * In certain circumstances mac80211 passes a station pointer - * that may be %NULL, for example during TX or key setup. In - * that case, we need to use the broadcast station, so this - * inline wraps that pattern. - */ -static int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context, - struct ieee80211_sta *sta) -{ - int sta_id; - - if (!sta) - return context->bcast_sta_id; - - sta_id = iwl_sta_id(sta); - - /* - * mac80211 should not be passing a partially - * initialised station! - */ - WARN_ON(sta_id == IWL_INVALID_STATION); - - return sta_id; -} - -/* - * start REPLY_TX command process - */ -int iwlagn_tx_skb(struct iwl_priv *priv, - struct ieee80211_sta *sta, - struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_station_priv *sta_priv = NULL; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl_device_cmd *dev_cmd; - struct iwl_tx_cmd *tx_cmd; - __le16 fc; - u8 hdr_len; - u16 len, seq_number = 0; - u8 sta_id, tid = IWL_MAX_TID_COUNT; - bool is_agg = false, is_data_qos = false; - int txq_id; - - if (info->control.vif) - ctx = iwl_rxon_ctx_from_vif(info->control.vif); - - if (iwl_is_rfkill(priv)) { - IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n"); - goto drop_unlock_priv; - } - - fc = hdr->frame_control; - -#ifdef CONFIG_IWLWIFI_DEBUG - if (ieee80211_is_auth(fc)) - IWL_DEBUG_TX(priv, "Sending AUTH frame\n"); - else if (ieee80211_is_assoc_req(fc)) - IWL_DEBUG_TX(priv, "Sending ASSOC frame\n"); - else if (ieee80211_is_reassoc_req(fc)) - IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); -#endif - - if (unlikely(ieee80211_is_probe_resp(fc))) { - struct iwl_wipan_noa_data *noa_data = - rcu_dereference(priv->noa_data); - - if (noa_data && - pskb_expand_head(skb, 0, noa_data->length, - GFP_ATOMIC) == 0) { - memcpy(skb_put(skb, noa_data->length), - noa_data->data, noa_data->length); - hdr = (struct ieee80211_hdr *)skb->data; - } - } - - hdr_len = ieee80211_hdrlen(fc); - - /* For management frames use broadcast id to do not break aggregation */ - if (!ieee80211_is_data(fc)) - sta_id = ctx->bcast_sta_id; - else { - /* Find index into station table for destination station */ - sta_id = iwl_sta_id_or_broadcast(ctx, sta); - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", - hdr->addr1); - goto drop_unlock_priv; - } - } - - if (sta) - sta_priv = (void *)sta->drv_priv; - - if (sta_priv && sta_priv->asleep && - (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { - /* - * This sends an asynchronous command to the device, - * but we can rely on it being processed before the - * next frame is processed -- and the next frame to - * this station is the one that will consume this - * counter. - * For now set the counter to just 1 since we do not - * support uAPSD yet. - * - * FIXME: If we get two non-bufferable frames one - * after the other, we might only send out one of - * them because this is racy. - */ - iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); - } - - dev_cmd = iwl_trans_alloc_tx_cmd(priv->trans); - - if (unlikely(!dev_cmd)) - goto drop_unlock_priv; - - memset(dev_cmd, 0, sizeof(*dev_cmd)); - dev_cmd->hdr.cmd = REPLY_TX; - tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; - - /* Total # bytes to be transmitted */ - len = (u16)skb->len; - tx_cmd->len = cpu_to_le16(len); - - if (info->control.hw_key) - iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb); - - /* TODO need this for burst mode later on */ - iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id); - - iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, sta, fc); - - memset(&info->status, 0, sizeof(info->status)); - - info->driver_data[0] = ctx; - info->driver_data[1] = dev_cmd; - /* From now on, we cannot access info->control */ - - spin_lock(&priv->sta_lock); - - if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { - u8 *qc = NULL; - struct iwl_tid_data *tid_data; - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) - goto drop_unlock_sta; - tid_data = &priv->tid_data[sta_id][tid]; - - /* aggregation is on for this */ - if (info->flags & IEEE80211_TX_CTL_AMPDU && - tid_data->agg.state != IWL_AGG_ON) { - IWL_ERR(priv, - "TX_CTL_AMPDU while not in AGG: Tx flags = 0x%08x, agg.state = %d\n", - info->flags, tid_data->agg.state); - IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d\n", - sta_id, tid, - IEEE80211_SEQ_TO_SN(tid_data->seq_number)); - goto drop_unlock_sta; - } - - /* We can receive packets from the stack in IWL_AGG_{ON,OFF} - * only. Check this here. - */ - if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON && - tid_data->agg.state != IWL_AGG_OFF, - "Tx while agg.state = %d\n", tid_data->agg.state)) - goto drop_unlock_sta; - - seq_number = tid_data->seq_number; - 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; - - if (info->flags & IEEE80211_TX_CTL_AMPDU) - is_agg = true; - is_data_qos = true; - } - - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdr_len); - - txq_id = info->hw_queue; - - if (is_agg) - txq_id = priv->tid_data[sta_id][tid].agg.txq_id; - else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { - /* - * The microcode will clear the more data - * bit in the last frame it transmits. - */ - hdr->frame_control |= - cpu_to_le16(IEEE80211_FCTL_MOREDATA); - } - - WARN_ON_ONCE(is_agg && - priv->queue_to_mac80211[txq_id] != info->hw_queue); - - IWL_DEBUG_TX(priv, "TX to [%d|%d] Q:%d - seq: 0x%x\n", sta_id, tid, - txq_id, seq_number); - - if (iwl_trans_tx(priv->trans, skb, dev_cmd, txq_id)) - goto drop_unlock_sta; - - if (is_data_qos && !ieee80211_has_morefrags(fc)) - priv->tid_data[sta_id][tid].seq_number = seq_number; - - spin_unlock(&priv->sta_lock); - - /* - * Avoid atomic ops if it isn't an associated client. - * Also, if this is a packet for aggregation, don't - * increase the counter because the ucode will stop - * aggregation queues when their respective station - * goes to sleep. - */ - if (sta_priv && sta_priv->client && !is_agg) - atomic_inc(&sta_priv->pending_frames); - - return 0; - -drop_unlock_sta: - if (dev_cmd) - iwl_trans_free_tx_cmd(priv->trans, dev_cmd); - spin_unlock(&priv->sta_lock); -drop_unlock_priv: - return -1; -} - -static int iwlagn_alloc_agg_txq(struct iwl_priv *priv, int mq) -{ - int q; - - for (q = IWLAGN_FIRST_AMPDU_QUEUE; - q < priv->cfg->base_params->num_of_queues; q++) { - if (!test_and_set_bit(q, priv->agg_q_alloc)) { - priv->queue_to_mac80211[q] = mq; - return q; - } - } - - return -ENOSPC; -} - -static void iwlagn_dealloc_agg_txq(struct iwl_priv *priv, int q) -{ - clear_bit(q, priv->agg_q_alloc); - priv->queue_to_mac80211[q] = IWL_INVALID_MAC80211_QUEUE; -} - -int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid) -{ - struct iwl_tid_data *tid_data; - int sta_id, txq_id; - enum iwl_agg_state agg_state; - - sta_id = iwl_sta_id(sta); - - if (sta_id == IWL_INVALID_STATION) { - IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); - return -ENXIO; - } - - spin_lock_bh(&priv->sta_lock); - - tid_data = &priv->tid_data[sta_id][tid]; - txq_id = tid_data->agg.txq_id; - - switch (tid_data->agg.state) { - case IWL_EMPTYING_HW_QUEUE_ADDBA: - /* - * This can happen if the peer stops aggregation - * again before we've had a chance to drain the - * queue we selected previously, i.e. before the - * session was really started completely. - */ - IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); - goto turn_off; - case IWL_AGG_STARTING: - /* - * This can happen when the session is stopped before - * we receive ADDBA response - */ - IWL_DEBUG_HT(priv, "AGG stop before AGG became operational\n"); - goto turn_off; - case IWL_AGG_ON: - break; - default: - IWL_WARN(priv, - "Stopping AGG while state not ON or starting for %d on %d (%d)\n", - sta_id, tid, tid_data->agg.state); - spin_unlock_bh(&priv->sta_lock); - return 0; - } - - tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); - - /* There are still packets for this RA / TID in the HW */ - if (!test_bit(txq_id, priv->agg_q_alloc)) { - IWL_DEBUG_TX_QUEUES(priv, - "stopping AGG on STA/TID %d/%d but hwq %d not used\n", - sta_id, tid, txq_id); - } else if (tid_data->agg.ssn != tid_data->next_reclaimed) { - IWL_DEBUG_TX_QUEUES(priv, - "Can't proceed: ssn %d, next_recl = %d\n", - tid_data->agg.ssn, - tid_data->next_reclaimed); - tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_DELBA; - spin_unlock_bh(&priv->sta_lock); - return 0; - } - - IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", - tid_data->agg.ssn); -turn_off: - agg_state = tid_data->agg.state; - tid_data->agg.state = IWL_AGG_OFF; - - spin_unlock_bh(&priv->sta_lock); - - if (test_bit(txq_id, priv->agg_q_alloc)) { - /* - * If the transport didn't know that we wanted to start - * agreggation, don't tell it that we want to stop them. - * This can happen when we don't get the addBA response on - * time, or we hadn't time to drain the AC queues. - */ - if (agg_state == IWL_AGG_ON) - iwl_trans_txq_disable(priv->trans, txq_id, true); - else - IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", - agg_state); - iwlagn_dealloc_agg_txq(priv, txq_id); - } - - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - - return 0; -} - -int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 *ssn) -{ - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - struct iwl_tid_data *tid_data; - int sta_id, txq_id, ret; - - IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n", - sta->addr, tid); - - sta_id = iwl_sta_id(sta); - if (sta_id == IWL_INVALID_STATION) { - IWL_ERR(priv, "Start AGG on invalid station\n"); - return -ENXIO; - } - if (unlikely(tid >= IWL_MAX_TID_COUNT)) - return -EINVAL; - - if (priv->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) { - IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n"); - return -ENXIO; - } - - txq_id = iwlagn_alloc_agg_txq(priv, ctx->ac_to_queue[tid_to_ac[tid]]); - if (txq_id < 0) { - IWL_DEBUG_TX_QUEUES(priv, - "No free aggregation queue for %pM/%d\n", - sta->addr, tid); - return txq_id; - } - - ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); - if (ret) - return ret; - - spin_lock_bh(&priv->sta_lock); - tid_data = &priv->tid_data[sta_id][tid]; - tid_data->agg.ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); - tid_data->agg.txq_id = txq_id; - - *ssn = tid_data->agg.ssn; - - if (*ssn == tid_data->next_reclaimed) { - IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", - tid_data->agg.ssn); - tid_data->agg.state = IWL_AGG_STARTING; - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - } else { - IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " - "next_reclaimed = %d\n", - tid_data->agg.ssn, - tid_data->next_reclaimed); - tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; - } - spin_unlock_bh(&priv->sta_lock); - - return ret; -} - -int iwlagn_tx_agg_flush(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid) -{ - struct iwl_tid_data *tid_data; - enum iwl_agg_state agg_state; - int sta_id, txq_id; - sta_id = iwl_sta_id(sta); - - /* - * First set the agg state to OFF to avoid calling - * ieee80211_stop_tx_ba_cb in iwlagn_check_ratid_empty. - */ - spin_lock_bh(&priv->sta_lock); - - tid_data = &priv->tid_data[sta_id][tid]; - txq_id = tid_data->agg.txq_id; - agg_state = tid_data->agg.state; - IWL_DEBUG_TX_QUEUES(priv, "Flush AGG: sta %d tid %d q %d state %d\n", - sta_id, tid, txq_id, tid_data->agg.state); - - tid_data->agg.state = IWL_AGG_OFF; - - spin_unlock_bh(&priv->sta_lock); - - if (iwlagn_txfifo_flush(priv, BIT(txq_id))) - IWL_ERR(priv, "Couldn't flush the AGG queue\n"); - - if (test_bit(txq_id, priv->agg_q_alloc)) { - /* - * If the transport didn't know that we wanted to start - * agreggation, don't tell it that we want to stop them. - * This can happen when we don't get the addBA response on - * time, or we hadn't time to drain the AC queues. - */ - if (agg_state == IWL_AGG_ON) - iwl_trans_txq_disable(priv->trans, txq_id, true); - else - IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", - agg_state); - iwlagn_dealloc_agg_txq(priv, txq_id); - } - - return 0; -} - -int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u8 buf_size) -{ - struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - int q, fifo; - u16 ssn; - - buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); - - spin_lock_bh(&priv->sta_lock); - ssn = priv->tid_data[sta_priv->sta_id][tid].agg.ssn; - q = priv->tid_data[sta_priv->sta_id][tid].agg.txq_id; - priv->tid_data[sta_priv->sta_id][tid].agg.state = IWL_AGG_ON; - spin_unlock_bh(&priv->sta_lock); - - fifo = ctx->ac_to_fifo[tid_to_ac[tid]]; - - iwl_trans_txq_enable(priv->trans, q, fifo, sta_priv->sta_id, tid, - buf_size, ssn, 0); - - /* - * If the limit is 0, then it wasn't initialised yet, - * use the default. We can do that since we take the - * minimum below, and we don't want to go above our - * default due to hardware restrictions. - */ - if (sta_priv->max_agg_bufsize == 0) - sta_priv->max_agg_bufsize = - LINK_QUAL_AGG_FRAME_LIMIT_DEF; - - /* - * Even though in theory the peer could have different - * aggregation reorder buffer sizes for different sessions, - * our ucode doesn't allow for that and has a global limit - * for each station. Therefore, use the minimum of all the - * aggregation sessions and our default value. - */ - sta_priv->max_agg_bufsize = - min(sta_priv->max_agg_bufsize, buf_size); - - if (priv->hw_params.use_rts_for_aggregation) { - /* - * switch to RTS/CTS if it is the prefer protection - * method for HT traffic - */ - - sta_priv->lq_sta.lq.general_params.flags |= - LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; - } - priv->agg_tids_count++; - IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", - priv->agg_tids_count); - - sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit = - sta_priv->max_agg_bufsize; - - IWL_DEBUG_HT(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", - sta->addr, tid); - - return iwl_send_lq_cmd(priv, ctx, - &sta_priv->lq_sta.lq, CMD_ASYNC, false); -} - -static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) -{ - struct iwl_tid_data *tid_data = &priv->tid_data[sta_id][tid]; - enum iwl_rxon_context_id ctx; - struct ieee80211_vif *vif; - u8 *addr; - - lockdep_assert_held(&priv->sta_lock); - - addr = priv->stations[sta_id].sta.sta.addr; - ctx = priv->stations[sta_id].ctxid; - vif = priv->contexts[ctx].vif; - - switch (priv->tid_data[sta_id][tid].agg.state) { - case IWL_EMPTYING_HW_QUEUE_DELBA: - /* There are no packets for this RA / TID in the HW any more */ - if (tid_data->agg.ssn == tid_data->next_reclaimed) { - IWL_DEBUG_TX_QUEUES(priv, - "Can continue DELBA flow ssn = next_recl = %d\n", - tid_data->next_reclaimed); - iwl_trans_txq_disable(priv->trans, - tid_data->agg.txq_id, true); - iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id); - tid_data->agg.state = IWL_AGG_OFF; - ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); - } - break; - case IWL_EMPTYING_HW_QUEUE_ADDBA: - /* There are no packets for this RA / TID in the HW any more */ - if (tid_data->agg.ssn == tid_data->next_reclaimed) { - IWL_DEBUG_TX_QUEUES(priv, - "Can continue ADDBA flow ssn = next_recl = %d\n", - tid_data->next_reclaimed); - tid_data->agg.state = IWL_AGG_STARTING; - ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); - } - break; - default: - break; - } -} - -static void iwlagn_non_agg_tx_status(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - const u8 *addr1) -{ - struct ieee80211_sta *sta; - struct iwl_station_priv *sta_priv; - - rcu_read_lock(); - sta = ieee80211_find_sta(ctx->vif, addr1); - if (sta) { - sta_priv = (void *)sta->drv_priv; - /* avoid atomic ops if this isn't a client */ - if (sta_priv->client && - atomic_dec_return(&sta_priv->pending_frames) == 0) - ieee80211_sta_block_awake(priv->hw, sta, false); - } - rcu_read_unlock(); -} - -/** - * translate ucode response to mac80211 tx status control values - */ -static void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, - struct ieee80211_tx_info *info) -{ - struct ieee80211_tx_rate *r = &info->status.rates[0]; - - info->status.antenna = - ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); - if (rate_n_flags & RATE_MCS_HT_MSK) - r->flags |= IEEE80211_TX_RC_MCS; - if (rate_n_flags & RATE_MCS_GF_MSK) - r->flags |= IEEE80211_TX_RC_GREEN_FIELD; - if (rate_n_flags & RATE_MCS_HT40_MSK) - r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - if (rate_n_flags & RATE_MCS_DUP_MSK) - r->flags |= IEEE80211_TX_RC_DUP_DATA; - if (rate_n_flags & RATE_MCS_SGI_MSK) - r->flags |= IEEE80211_TX_RC_SHORT_GI; - r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band); -} - -#ifdef CONFIG_IWLWIFI_DEBUG -const char *iwl_get_tx_fail_reason(u32 status) -{ -#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x -#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x - - switch (status & TX_STATUS_MSK) { - case TX_STATUS_SUCCESS: - return "SUCCESS"; - TX_STATUS_POSTPONE(DELAY); - TX_STATUS_POSTPONE(FEW_BYTES); - TX_STATUS_POSTPONE(BT_PRIO); - TX_STATUS_POSTPONE(QUIET_PERIOD); - TX_STATUS_POSTPONE(CALC_TTAK); - TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); - TX_STATUS_FAIL(SHORT_LIMIT); - TX_STATUS_FAIL(LONG_LIMIT); - TX_STATUS_FAIL(FIFO_UNDERRUN); - TX_STATUS_FAIL(DRAIN_FLOW); - TX_STATUS_FAIL(RFKILL_FLUSH); - TX_STATUS_FAIL(LIFE_EXPIRE); - TX_STATUS_FAIL(DEST_PS); - TX_STATUS_FAIL(HOST_ABORTED); - TX_STATUS_FAIL(BT_RETRY); - TX_STATUS_FAIL(STA_INVALID); - TX_STATUS_FAIL(FRAG_DROPPED); - TX_STATUS_FAIL(TID_DISABLE); - TX_STATUS_FAIL(FIFO_FLUSHED); - TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); - TX_STATUS_FAIL(PASSIVE_NO_RX); - TX_STATUS_FAIL(NO_BEACON_ON_RADAR); - } - - return "UNKNOWN"; - -#undef TX_STATUS_FAIL -#undef TX_STATUS_POSTPONE -} -#endif /* CONFIG_IWLWIFI_DEBUG */ - -static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) -{ - status &= AGG_TX_STATUS_MSK; - - switch (status) { - case AGG_TX_STATE_UNDERRUN_MSK: - priv->reply_agg_tx_stats.underrun++; - break; - case AGG_TX_STATE_BT_PRIO_MSK: - priv->reply_agg_tx_stats.bt_prio++; - break; - case AGG_TX_STATE_FEW_BYTES_MSK: - priv->reply_agg_tx_stats.few_bytes++; - break; - case AGG_TX_STATE_ABORT_MSK: - priv->reply_agg_tx_stats.abort++; - break; - case AGG_TX_STATE_LAST_SENT_TTL_MSK: - priv->reply_agg_tx_stats.last_sent_ttl++; - break; - case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK: - priv->reply_agg_tx_stats.last_sent_try++; - break; - case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK: - priv->reply_agg_tx_stats.last_sent_bt_kill++; - break; - case AGG_TX_STATE_SCD_QUERY_MSK: - priv->reply_agg_tx_stats.scd_query++; - break; - case AGG_TX_STATE_TEST_BAD_CRC32_MSK: - priv->reply_agg_tx_stats.bad_crc32++; - break; - case AGG_TX_STATE_RESPONSE_MSK: - priv->reply_agg_tx_stats.response++; - break; - case AGG_TX_STATE_DUMP_TX_MSK: - priv->reply_agg_tx_stats.dump_tx++; - break; - case AGG_TX_STATE_DELAY_TX_MSK: - priv->reply_agg_tx_stats.delay_tx++; - break; - default: - priv->reply_agg_tx_stats.unknown++; - break; - } -} - -static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) -{ - return le32_to_cpup((__le32 *)&tx_resp->status + - tx_resp->frame_count) & IEEE80211_MAX_SN; -} - -static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, - struct iwlagn_tx_resp *tx_resp) -{ - struct agg_tx_status *frame_status = &tx_resp->status; - int tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> - IWLAGN_TX_RES_TID_POS; - int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> - IWLAGN_TX_RES_RA_POS; - struct iwl_ht_agg *agg = &priv->tid_data[sta_id][tid].agg; - u32 status = le16_to_cpu(tx_resp->status.status); - int i; - - WARN_ON(tid == IWL_TID_NON_QOS); - - if (agg->wait_for_ba) - IWL_DEBUG_TX_REPLY(priv, - "got tx response w/o block-ack\n"); - - agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); - agg->wait_for_ba = (tx_resp->frame_count > 1); - - /* - * If the BT kill count is non-zero, we'll get this - * notification again. - */ - if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && - priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist) { - IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); - } - - if (tx_resp->frame_count == 1) - return; - - IWL_DEBUG_TX_REPLY(priv, "TXQ %d initial_rate 0x%x ssn %d frm_cnt %d\n", - agg->txq_id, - le32_to_cpu(tx_resp->rate_n_flags), - iwlagn_get_scd_ssn(tx_resp), tx_resp->frame_count); - - /* Construct bit-map of pending frames within Tx window */ - for (i = 0; i < tx_resp->frame_count; i++) { - u16 fstatus = le16_to_cpu(frame_status[i].status); - u8 retry_cnt = (fstatus & AGG_TX_TRY_MSK) >> AGG_TX_TRY_POS; - - if (status & AGG_TX_STATUS_MSK) - iwlagn_count_agg_tx_err_status(priv, fstatus); - - if (status & (AGG_TX_STATE_FEW_BYTES_MSK | - AGG_TX_STATE_ABORT_MSK)) - continue; - - if (status & AGG_TX_STATUS_MSK || retry_cnt > 1) - IWL_DEBUG_TX_REPLY(priv, - "%d: status %s (0x%04x), try-count (0x%01x)\n", - i, - iwl_get_agg_tx_fail_reason(fstatus), - fstatus & AGG_TX_STATUS_MSK, - retry_cnt); - } -} - -#ifdef CONFIG_IWLWIFI_DEBUG -#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x - -const char *iwl_get_agg_tx_fail_reason(u16 status) -{ - status &= AGG_TX_STATUS_MSK; - switch (status) { - case AGG_TX_STATE_TRANSMITTED: - return "SUCCESS"; - AGG_TX_STATE_FAIL(UNDERRUN_MSK); - AGG_TX_STATE_FAIL(BT_PRIO_MSK); - AGG_TX_STATE_FAIL(FEW_BYTES_MSK); - AGG_TX_STATE_FAIL(ABORT_MSK); - AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK); - AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK); - AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK); - AGG_TX_STATE_FAIL(SCD_QUERY_MSK); - AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK); - AGG_TX_STATE_FAIL(RESPONSE_MSK); - AGG_TX_STATE_FAIL(DUMP_TX_MSK); - AGG_TX_STATE_FAIL(DELAY_TX_MSK); - } - - return "UNKNOWN"; -} -#endif /* CONFIG_IWLWIFI_DEBUG */ - -static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) -{ - status &= TX_STATUS_MSK; - - switch (status) { - case TX_STATUS_POSTPONE_DELAY: - priv->reply_tx_stats.pp_delay++; - break; - case TX_STATUS_POSTPONE_FEW_BYTES: - priv->reply_tx_stats.pp_few_bytes++; - break; - case TX_STATUS_POSTPONE_BT_PRIO: - priv->reply_tx_stats.pp_bt_prio++; - break; - case TX_STATUS_POSTPONE_QUIET_PERIOD: - priv->reply_tx_stats.pp_quiet_period++; - break; - case TX_STATUS_POSTPONE_CALC_TTAK: - priv->reply_tx_stats.pp_calc_ttak++; - break; - case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: - priv->reply_tx_stats.int_crossed_retry++; - break; - case TX_STATUS_FAIL_SHORT_LIMIT: - priv->reply_tx_stats.short_limit++; - break; - case TX_STATUS_FAIL_LONG_LIMIT: - priv->reply_tx_stats.long_limit++; - break; - case TX_STATUS_FAIL_FIFO_UNDERRUN: - priv->reply_tx_stats.fifo_underrun++; - break; - case TX_STATUS_FAIL_DRAIN_FLOW: - priv->reply_tx_stats.drain_flow++; - break; - case TX_STATUS_FAIL_RFKILL_FLUSH: - priv->reply_tx_stats.rfkill_flush++; - break; - case TX_STATUS_FAIL_LIFE_EXPIRE: - priv->reply_tx_stats.life_expire++; - break; - case TX_STATUS_FAIL_DEST_PS: - priv->reply_tx_stats.dest_ps++; - break; - case TX_STATUS_FAIL_HOST_ABORTED: - priv->reply_tx_stats.host_abort++; - break; - case TX_STATUS_FAIL_BT_RETRY: - priv->reply_tx_stats.bt_retry++; - break; - case TX_STATUS_FAIL_STA_INVALID: - priv->reply_tx_stats.sta_invalid++; - break; - case TX_STATUS_FAIL_FRAG_DROPPED: - priv->reply_tx_stats.frag_drop++; - break; - case TX_STATUS_FAIL_TID_DISABLE: - priv->reply_tx_stats.tid_disable++; - break; - case TX_STATUS_FAIL_FIFO_FLUSHED: - priv->reply_tx_stats.fifo_flush++; - break; - case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL: - priv->reply_tx_stats.insuff_cf_poll++; - break; - case TX_STATUS_FAIL_PASSIVE_NO_RX: - priv->reply_tx_stats.fail_hw_drop++; - break; - case TX_STATUS_FAIL_NO_BEACON_ON_RADAR: - priv->reply_tx_stats.sta_color_mismatch++; - break; - default: - priv->reply_tx_stats.unknown++; - break; - } -} - -static void iwlagn_set_tx_status(struct iwl_priv *priv, - struct ieee80211_tx_info *info, - struct iwlagn_tx_resp *tx_resp) -{ - u16 status = le16_to_cpu(tx_resp->status.status); - - info->flags &= ~IEEE80211_TX_CTL_AMPDU; - - info->status.rates[0].count = tx_resp->failure_frame + 1; - info->flags |= iwl_tx_status_to_mac80211(status); - iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), - info); - if (!iwl_is_tx_success(status)) - iwlagn_count_tx_err_status(priv, status); -} - -static void iwl_check_abort_status(struct iwl_priv *priv, - u8 frame_count, u32 status) -{ - if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { - IWL_ERR(priv, "Tx flush command to flush out all frames\n"); - if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) - queue_work(priv->workqueue, &priv->tx_flush); - } -} - -void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - u16 sequence = le16_to_cpu(pkt->hdr.sequence); - int txq_id = SEQ_TO_QUEUE(sequence); - int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence); - struct iwlagn_tx_resp *tx_resp = (void *)pkt->data; - struct ieee80211_hdr *hdr; - u32 status = le16_to_cpu(tx_resp->status.status); - u16 ssn = iwlagn_get_scd_ssn(tx_resp); - int tid; - int sta_id; - int freed; - struct ieee80211_tx_info *info; - struct sk_buff_head skbs; - struct sk_buff *skb; - struct iwl_rxon_context *ctx; - bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); - - tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> - IWLAGN_TX_RES_TID_POS; - sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> - IWLAGN_TX_RES_RA_POS; - - spin_lock_bh(&priv->sta_lock); - - if (is_agg) { - WARN_ON_ONCE(sta_id >= IWLAGN_STATION_COUNT || - tid >= IWL_MAX_TID_COUNT); - if (txq_id != priv->tid_data[sta_id][tid].agg.txq_id) - IWL_ERR(priv, "txq_id mismatch: %d %d\n", txq_id, - priv->tid_data[sta_id][tid].agg.txq_id); - iwl_rx_reply_tx_agg(priv, tx_resp); - } - - __skb_queue_head_init(&skbs); - - if (tx_resp->frame_count == 1) { - u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); - next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10); - - if (is_agg) { - /* If this is an aggregation queue, we can rely on the - * ssn since the wifi sequence number corresponds to - * the index in the TFD ring (%256). - * The seq_ctl is the sequence control of the packet - * to which this Tx response relates. But if there is a - * hole in the bitmap of the BA we received, this Tx - * response may allow to reclaim the hole and all the - * subsequent packets that were already acked. - * In that case, seq_ctl != ssn, and the next packet - * to be reclaimed will be ssn and not seq_ctl. - */ - next_reclaimed = ssn; - } - - if (tid != IWL_TID_NON_QOS) { - priv->tid_data[sta_id][tid].next_reclaimed = - next_reclaimed; - IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", - next_reclaimed); - } - - iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs); - - iwlagn_check_ratid_empty(priv, sta_id, tid); - freed = 0; - - /* process frames */ - skb_queue_walk(&skbs, skb) { - hdr = (struct ieee80211_hdr *)skb->data; - - if (!ieee80211_is_data_qos(hdr->frame_control)) - priv->last_seq_ctl = tx_resp->seq_ctl; - - info = IEEE80211_SKB_CB(skb); - ctx = info->driver_data[0]; - iwl_trans_free_tx_cmd(priv->trans, - info->driver_data[1]); - - memset(&info->status, 0, sizeof(info->status)); - - if (status == TX_STATUS_FAIL_PASSIVE_NO_RX && - ctx->vif && - ctx->vif->type == NL80211_IFTYPE_STATION) { - /* block and stop all queues */ - priv->passive_no_rx = true; - IWL_DEBUG_TX_QUEUES(priv, - "stop all queues: passive channel\n"); - ieee80211_stop_queues(priv->hw); - - IWL_DEBUG_TX_REPLY(priv, - "TXQ %d status %s (0x%08x) " - "rate_n_flags 0x%x retries %d\n", - txq_id, - iwl_get_tx_fail_reason(status), - status, - le32_to_cpu(tx_resp->rate_n_flags), - tx_resp->failure_frame); - - IWL_DEBUG_TX_REPLY(priv, - "FrameCnt = %d, idx=%d\n", - tx_resp->frame_count, cmd_index); - } - - /* check if BAR is needed */ - if (is_agg && !iwl_is_tx_success(status)) - info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; - iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb), - tx_resp); - if (!is_agg) - iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); - - freed++; - } - - if (tid != IWL_TID_NON_QOS) { - priv->tid_data[sta_id][tid].next_reclaimed = - next_reclaimed; - IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", - next_reclaimed); - } - - if (!is_agg && freed != 1) - IWL_ERR(priv, "Q: %d, freed %d\n", txq_id, freed); - - IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id, - iwl_get_tx_fail_reason(status), status); - - IWL_DEBUG_TX_REPLY(priv, - "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d seq_ctl=0x%x\n", - le32_to_cpu(tx_resp->rate_n_flags), - tx_resp->failure_frame, - SEQ_TO_INDEX(sequence), ssn, - le16_to_cpu(tx_resp->seq_ctl)); - } - - iwl_check_abort_status(priv, tx_resp->frame_count, status); - spin_unlock_bh(&priv->sta_lock); - - while (!skb_queue_empty(&skbs)) { - skb = __skb_dequeue(&skbs); - ieee80211_tx_status(priv->hw, skb); - } -} - -/** - * iwlagn_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA - * - * Handles block-acknowledge notification from device, which reports success - * of frames sent via aggregation. - */ -void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_compressed_ba_resp *ba_resp = (void *)pkt->data; - struct iwl_ht_agg *agg; - struct sk_buff_head reclaimed_skbs; - struct sk_buff *skb; - int sta_id; - int tid; - int freed; - - /* "flow" corresponds to Tx queue */ - u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); - - /* "ssn" is start of block-ack Tx window, corresponds to index - * (in Tx queue's circular buffer) of first TFD/frame in window */ - u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); - - if (scd_flow >= priv->cfg->base_params->num_of_queues) { - IWL_ERR(priv, - "BUG_ON scd_flow is bigger than number of queues\n"); - return; - } - - sta_id = ba_resp->sta_id; - tid = ba_resp->tid; - agg = &priv->tid_data[sta_id][tid].agg; - - spin_lock_bh(&priv->sta_lock); - - if (unlikely(!agg->wait_for_ba)) { - if (unlikely(ba_resp->bitmap)) - IWL_ERR(priv, "Received BA when not expected\n"); - spin_unlock_bh(&priv->sta_lock); - return; - } - - if (unlikely(scd_flow != agg->txq_id)) { - /* - * FIXME: this is a uCode bug which need to be addressed, - * log the information and return for now. - * Since it is can possibly happen very often and in order - * not to fill the syslog, don't use IWL_ERR or IWL_WARN - */ - IWL_DEBUG_TX_QUEUES(priv, - "Bad queue mapping txq_id=%d, agg_txq[sta:%d,tid:%d]=%d\n", - scd_flow, sta_id, tid, agg->txq_id); - spin_unlock_bh(&priv->sta_lock); - return; - } - - __skb_queue_head_init(&reclaimed_skbs); - - /* Release all TFDs before the SSN, i.e. all TFDs in front of - * block-ack window (we assume that they've been successfully - * transmitted ... if not, it's too late anyway). */ - iwl_trans_reclaim(priv->trans, scd_flow, ba_resp_scd_ssn, - &reclaimed_skbs); - - IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, " - "sta_id = %d\n", - agg->wait_for_ba, - (u8 *) &ba_resp->sta_addr_lo32, - ba_resp->sta_id); - IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, " - "scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n", - ba_resp->tid, le16_to_cpu(ba_resp->seq_ctl), - (unsigned long long)le64_to_cpu(ba_resp->bitmap), - scd_flow, ba_resp_scd_ssn, ba_resp->txed, - ba_resp->txed_2_done); - - /* Mark that the expected block-ack response arrived */ - agg->wait_for_ba = false; - - /* Sanity check values reported by uCode */ - if (ba_resp->txed_2_done > ba_resp->txed) { - IWL_DEBUG_TX_REPLY(priv, - "bogus sent(%d) and ack(%d) count\n", - ba_resp->txed, ba_resp->txed_2_done); - /* - * set txed_2_done = txed, - * so it won't impact rate scale - */ - ba_resp->txed = ba_resp->txed_2_done; - } - - priv->tid_data[sta_id][tid].next_reclaimed = ba_resp_scd_ssn; - - iwlagn_check_ratid_empty(priv, sta_id, tid); - freed = 0; - - skb_queue_walk(&reclaimed_skbs, skb) { - struct ieee80211_hdr *hdr = (void *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - if (ieee80211_is_data_qos(hdr->frame_control)) - freed++; - else - WARN_ON_ONCE(1); - - iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); - - memset(&info->status, 0, sizeof(info->status)); - /* Packet was transmitted successfully, failures come as single - * frames because before failing a frame the firmware transmits - * it without aggregation at least once. - */ - info->flags |= IEEE80211_TX_STAT_ACK; - - if (freed == 1) { - /* this is the first skb we deliver in this batch */ - /* put the rate scaling data there */ - info = IEEE80211_SKB_CB(skb); - memset(&info->status, 0, sizeof(info->status)); - info->flags |= IEEE80211_TX_STAT_AMPDU; - info->status.ampdu_ack_len = ba_resp->txed_2_done; - info->status.ampdu_len = ba_resp->txed; - iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, - info); - } - } - - spin_unlock_bh(&priv->sta_lock); - - while (!skb_queue_empty(&reclaimed_skbs)) { - skb = __skb_dequeue(&reclaimed_skbs); - ieee80211_tx_status(priv->hw, skb); - } -} diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c deleted file mode 100644 index 931a8e4269ef..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ /dev/null @@ -1,452 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include - -#include "iwl-io.h" -#include "iwl-agn-hw.h" -#include "iwl-trans.h" -#include "iwl-fh.h" -#include "iwl-op-mode.h" - -#include "dev.h" -#include "agn.h" -#include "calib.h" - -/****************************************************************************** - * - * uCode download functions - * - ******************************************************************************/ - -static inline const struct fw_img * -iwl_get_ucode_image(struct iwl_priv *priv, enum iwl_ucode_type ucode_type) -{ - if (ucode_type >= IWL_UCODE_TYPE_MAX) - return NULL; - - return &priv->fw->img[ucode_type]; -} - -/* - * Calibration - */ -static int iwl_set_Xtal_calib(struct iwl_priv *priv) -{ - struct iwl_calib_xtal_freq_cmd cmd; - __le16 *xtal_calib = priv->nvm_data->xtal_calib; - - iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD); - cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]); - cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]); - return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); -} - -static int iwl_set_temperature_offset_calib(struct iwl_priv *priv) -{ - struct iwl_calib_temperature_offset_cmd cmd; - - memset(&cmd, 0, sizeof(cmd)); - iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); - cmd.radio_sensor_offset = priv->nvm_data->raw_temperature; - if (!(cmd.radio_sensor_offset)) - cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET; - - IWL_DEBUG_CALIB(priv, "Radio sensor offset: %d\n", - le16_to_cpu(cmd.radio_sensor_offset)); - return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); -} - -static int iwl_set_temperature_offset_calib_v2(struct iwl_priv *priv) -{ - struct iwl_calib_temperature_offset_v2_cmd cmd; - - memset(&cmd, 0, sizeof(cmd)); - iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); - cmd.radio_sensor_offset_high = priv->nvm_data->kelvin_temperature; - cmd.radio_sensor_offset_low = priv->nvm_data->raw_temperature; - if (!cmd.radio_sensor_offset_low) { - IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n"); - cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET; - cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET; - } - cmd.burntVoltageRef = priv->nvm_data->calib_voltage; - - IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n", - le16_to_cpu(cmd.radio_sensor_offset_high)); - IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n", - le16_to_cpu(cmd.radio_sensor_offset_low)); - IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n", - le16_to_cpu(cmd.burntVoltageRef)); - - return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); -} - -static int iwl_send_calib_cfg(struct iwl_priv *priv) -{ - struct iwl_calib_cfg_cmd calib_cfg_cmd; - struct iwl_host_cmd cmd = { - .id = CALIBRATION_CFG_CMD, - .len = { sizeof(struct iwl_calib_cfg_cmd), }, - .data = { &calib_cfg_cmd, }, - }; - - memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd)); - calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL; - calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL; - calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL; - calib_cfg_cmd.ucd_calib_cfg.flags = - IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK; - - return iwl_dvm_send_cmd(priv, &cmd); -} - -int iwl_init_alive_start(struct iwl_priv *priv) -{ - int ret; - - if (priv->lib->bt_params && - priv->lib->bt_params->advanced_bt_coexist) { - /* - * Tell uCode we are ready to perform calibration - * need to perform this before any calibration - * no need to close the envlope since we are going - * to load the runtime uCode later. - */ - ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; - - } - - ret = iwl_send_calib_cfg(priv); - if (ret) - return ret; - - /** - * temperature offset calibration is only needed for runtime ucode, - * so prepare the value now. - */ - if (priv->lib->need_temp_offset_calib) { - if (priv->lib->temp_offset_v2) - return iwl_set_temperature_offset_calib_v2(priv); - else - return iwl_set_temperature_offset_calib(priv); - } - - return 0; -} - -static int iwl_send_wimax_coex(struct iwl_priv *priv) -{ - struct iwl_wimax_coex_cmd coex_cmd; - - /* coexistence is disabled */ - memset(&coex_cmd, 0, sizeof(coex_cmd)); - - return iwl_dvm_send_cmd_pdu(priv, - COEX_PRIORITY_TABLE_CMD, 0, - sizeof(coex_cmd), &coex_cmd); -} - -static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { - ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_COEX_OFF << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_COEX_ON << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - 0, 0, 0, 0, 0, 0, 0 -}; - -void iwl_send_prio_tbl(struct iwl_priv *priv) -{ - struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd; - - memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl, - sizeof(iwl_bt_prio_tbl)); - if (iwl_dvm_send_cmd_pdu(priv, - REPLY_BT_COEX_PRIO_TABLE, 0, - sizeof(prio_tbl_cmd), &prio_tbl_cmd)) - IWL_ERR(priv, "failed to send BT prio tbl command\n"); -} - -int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type) -{ - struct iwl_bt_coex_prot_env_cmd env_cmd; - int ret; - - env_cmd.action = action; - env_cmd.type = type; - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_BT_COEX_PROT_ENV, 0, - sizeof(env_cmd), &env_cmd); - if (ret) - IWL_ERR(priv, "failed to send BT env command\n"); - return ret; -} - -static const u8 iwlagn_default_queue_to_tx_fifo[] = { - IWL_TX_FIFO_VO, - IWL_TX_FIFO_VI, - IWL_TX_FIFO_BE, - IWL_TX_FIFO_BK, -}; - -static const u8 iwlagn_ipan_queue_to_tx_fifo[] = { - IWL_TX_FIFO_VO, - IWL_TX_FIFO_VI, - IWL_TX_FIFO_BE, - IWL_TX_FIFO_BK, - IWL_TX_FIFO_BK_IPAN, - IWL_TX_FIFO_BE_IPAN, - IWL_TX_FIFO_VI_IPAN, - IWL_TX_FIFO_VO_IPAN, - IWL_TX_FIFO_BE_IPAN, - IWL_TX_FIFO_UNUSED, - IWL_TX_FIFO_AUX, -}; - -static int iwl_alive_notify(struct iwl_priv *priv) -{ - const u8 *queue_to_txf; - u8 n_queues; - int ret; - int i; - - iwl_trans_fw_alive(priv->trans, 0); - - if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN && - priv->nvm_data->sku_cap_ipan_enable) { - n_queues = ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo); - queue_to_txf = iwlagn_ipan_queue_to_tx_fifo; - } else { - n_queues = ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo); - queue_to_txf = iwlagn_default_queue_to_tx_fifo; - } - - for (i = 0; i < n_queues; i++) - if (queue_to_txf[i] != IWL_TX_FIFO_UNUSED) - iwl_trans_ac_txq_enable(priv->trans, i, - queue_to_txf[i], 0); - - priv->passive_no_rx = false; - priv->transport_queue_stop = 0; - - ret = iwl_send_wimax_coex(priv); - if (ret) - return ret; - - if (!priv->lib->no_xtal_calib) { - ret = iwl_set_Xtal_calib(priv); - if (ret) - return ret; - } - - return iwl_send_calib_results(priv); -} - -struct iwl_alive_data { - bool valid; - u8 subtype; -}; - -static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_priv *priv = - container_of(notif_wait, struct iwl_priv, notif_wait); - struct iwl_alive_data *alive_data = data; - struct iwl_alive_resp *palive; - - palive = (void *)pkt->data; - - IWL_DEBUG_FW(priv, "Alive ucode status 0x%08X revision " - "0x%01X 0x%01X\n", - palive->is_valid, palive->ver_type, - palive->ver_subtype); - - priv->device_pointers.error_event_table = - le32_to_cpu(palive->error_event_table_ptr); - priv->device_pointers.log_event_table = - le32_to_cpu(palive->log_event_table_ptr); - - alive_data->subtype = palive->ver_subtype; - alive_data->valid = palive->is_valid == UCODE_VALID_OK; - - return true; -} - -#define UCODE_ALIVE_TIMEOUT HZ -#define UCODE_CALIB_TIMEOUT (2*HZ) - -int iwl_load_ucode_wait_alive(struct iwl_priv *priv, - enum iwl_ucode_type ucode_type) -{ - struct iwl_notification_wait alive_wait; - struct iwl_alive_data alive_data; - const struct fw_img *fw; - int ret; - enum iwl_ucode_type old_type; - static const u16 alive_cmd[] = { REPLY_ALIVE }; - - fw = iwl_get_ucode_image(priv, ucode_type); - if (WARN_ON(!fw)) - return -EINVAL; - - old_type = priv->cur_ucode; - priv->cur_ucode = ucode_type; - priv->ucode_loaded = false; - - iwl_init_notification_wait(&priv->notif_wait, &alive_wait, - alive_cmd, ARRAY_SIZE(alive_cmd), - iwl_alive_fn, &alive_data); - - ret = iwl_trans_start_fw(priv->trans, fw, false); - if (ret) { - priv->cur_ucode = old_type; - iwl_remove_notification(&priv->notif_wait, &alive_wait); - return ret; - } - - /* - * Some things may run in the background now, but we - * just wait for the ALIVE notification here. - */ - ret = iwl_wait_notification(&priv->notif_wait, &alive_wait, - UCODE_ALIVE_TIMEOUT); - if (ret) { - priv->cur_ucode = old_type; - return ret; - } - - if (!alive_data.valid) { - IWL_ERR(priv, "Loaded ucode is not valid!\n"); - priv->cur_ucode = old_type; - return -EIO; - } - - priv->ucode_loaded = true; - - if (ucode_type != IWL_UCODE_WOWLAN) { - /* delay a bit to give rfkill time to run */ - msleep(5); - } - - ret = iwl_alive_notify(priv); - if (ret) { - IWL_WARN(priv, - "Could not complete ALIVE transition: %d\n", ret); - priv->cur_ucode = old_type; - return ret; - } - - return 0; -} - -static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_priv *priv = data; - struct iwl_calib_hdr *hdr; - - if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) { - WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION); - return true; - } - - hdr = (struct iwl_calib_hdr *)pkt->data; - - 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); - - return false; -} - -int iwl_run_init_ucode(struct iwl_priv *priv) -{ - struct iwl_notification_wait calib_wait; - static const u16 calib_complete[] = { - CALIBRATION_RES_NOTIFICATION, - CALIBRATION_COMPLETE_NOTIFICATION - }; - int ret; - - lockdep_assert_held(&priv->mutex); - - /* No init ucode required? Curious, but maybe ok */ - if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len) - return 0; - - iwl_init_notification_wait(&priv->notif_wait, &calib_wait, - calib_complete, ARRAY_SIZE(calib_complete), - iwlagn_wait_calib, priv); - - /* Will also start the device */ - ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT); - if (ret) - goto error; - - ret = iwl_init_alive_start(priv); - if (ret) - goto error; - - /* - * Some things may run in the background now, but we - * just wait for the calibration complete notification. - */ - ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, - UCODE_CALIB_TIMEOUT); - - goto out; - - error: - iwl_remove_notification(&priv->notif_wait, &calib_wait); - out: - /* Whatever happened, stop the device */ - iwl_trans_stop_device(priv->trans); - priv->ucode_loaded = false; - - return ret; -} diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c deleted file mode 100644 index 06f6cc08f451..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ /dev/null @@ -1,140 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include "iwl-config.h" -#include "iwl-csr.h" -#include "iwl-agn-hw.h" - -/* Highest firmware API version supported */ -#define IWL1000_UCODE_API_MAX 5 -#define IWL100_UCODE_API_MAX 5 - -/* Oldest version we won't warn about */ -#define IWL1000_UCODE_API_OK 5 -#define IWL100_UCODE_API_OK 5 - -/* Lowest firmware API version supported */ -#define IWL1000_UCODE_API_MIN 1 -#define IWL100_UCODE_API_MIN 5 - -/* EEPROM version */ -#define EEPROM_1000_TX_POWER_VERSION (4) -#define EEPROM_1000_EEPROM_VERSION (0x15C) - -#define IWL1000_FW_PRE "iwlwifi-1000-" -#define IWL1000_MODULE_FIRMWARE(api) IWL1000_FW_PRE __stringify(api) ".ucode" - -#define IWL100_FW_PRE "iwlwifi-100-" -#define IWL100_MODULE_FIRMWARE(api) IWL100_FW_PRE __stringify(api) ".ucode" - - -static const struct iwl_base_params iwl1000_base_params = { - .num_of_queues = IWLAGN_NUM_QUEUES, - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, - .max_ll_items = OTP_MAX_LL_ITEMS_1000, - .shadow_ram_support = false, - .led_compensation = 51, - .wd_timeout = IWL_WATCHDOG_DISABLED, - .max_event_log_size = 128, - .scd_chain_ext_wa = true, -}; - -static const struct iwl_ht_params iwl1000_ht_params = { - .ht_greenfield_support = true, - .use_rts_for_aggregation = true, /* use rts/cts protection */ - .ht40_bands = BIT(IEEE80211_BAND_2GHZ), -}; - -static const struct iwl_eeprom_params iwl1000_eeprom_params = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_REG_BAND_24_HT40_CHANNELS, - EEPROM_REGULATORY_BAND_NO_HT40, - } -}; - -#define IWL_DEVICE_1000 \ - .fw_name_pre = IWL1000_FW_PRE, \ - .ucode_api_max = IWL1000_UCODE_API_MAX, \ - .ucode_api_ok = IWL1000_UCODE_API_OK, \ - .ucode_api_min = IWL1000_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_1000, \ - .max_inst_size = IWLAGN_RTC_INST_SIZE, \ - .max_data_size = IWLAGN_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_1000_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ - .base_params = &iwl1000_base_params, \ - .eeprom_params = &iwl1000_eeprom_params, \ - .led_mode = IWL_LED_BLINK, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl1000_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN", - IWL_DEVICE_1000, - .ht_params = &iwl1000_ht_params, -}; - -const struct iwl_cfg iwl1000_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1000 BG", - IWL_DEVICE_1000, -}; - -#define IWL_DEVICE_100 \ - .fw_name_pre = IWL100_FW_PRE, \ - .ucode_api_max = IWL100_UCODE_API_MAX, \ - .ucode_api_ok = IWL100_UCODE_API_OK, \ - .ucode_api_min = IWL100_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_100, \ - .max_inst_size = IWLAGN_RTC_INST_SIZE, \ - .max_data_size = IWLAGN_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_1000_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ - .base_params = &iwl1000_base_params, \ - .eeprom_params = &iwl1000_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE, \ - .rx_with_siso_diversity = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl100_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 100 BGN", - IWL_DEVICE_100, - .ht_params = &iwl1000_ht_params, -}; - -const struct iwl_cfg iwl100_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 100 BG", - IWL_DEVICE_100, -}; - -MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_OK)); -MODULE_FIRMWARE(IWL100_MODULE_FIRMWARE(IWL100_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c deleted file mode 100644 index 890b95f497d6..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ /dev/null @@ -1,216 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include "iwl-config.h" -#include "iwl-agn-hw.h" -#include "dvm/commands.h" /* needed for BT for now */ - -/* Highest firmware API version supported */ -#define IWL2030_UCODE_API_MAX 6 -#define IWL2000_UCODE_API_MAX 6 -#define IWL105_UCODE_API_MAX 6 -#define IWL135_UCODE_API_MAX 6 - -/* Oldest version we won't warn about */ -#define IWL2030_UCODE_API_OK 6 -#define IWL2000_UCODE_API_OK 6 -#define IWL105_UCODE_API_OK 6 -#define IWL135_UCODE_API_OK 6 - -/* Lowest firmware API version supported */ -#define IWL2030_UCODE_API_MIN 5 -#define IWL2000_UCODE_API_MIN 5 -#define IWL105_UCODE_API_MIN 5 -#define IWL135_UCODE_API_MIN 5 - -/* EEPROM version */ -#define EEPROM_2000_TX_POWER_VERSION (6) -#define EEPROM_2000_EEPROM_VERSION (0x805) - - -#define IWL2030_FW_PRE "iwlwifi-2030-" -#define IWL2030_MODULE_FIRMWARE(api) IWL2030_FW_PRE __stringify(api) ".ucode" - -#define IWL2000_FW_PRE "iwlwifi-2000-" -#define IWL2000_MODULE_FIRMWARE(api) IWL2000_FW_PRE __stringify(api) ".ucode" - -#define IWL105_FW_PRE "iwlwifi-105-" -#define IWL105_MODULE_FIRMWARE(api) IWL105_FW_PRE __stringify(api) ".ucode" - -#define IWL135_FW_PRE "iwlwifi-135-" -#define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE __stringify(api) ".ucode" - -static const struct iwl_base_params iwl2000_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = 0, - .max_ll_items = OTP_MAX_LL_ITEMS_2x00, - .shadow_ram_support = true, - .led_compensation = 51, - .wd_timeout = IWL_DEF_WD_TIMEOUT, - .max_event_log_size = 512, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .scd_chain_ext_wa = true, -}; - - -static const struct iwl_base_params iwl2030_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = 0, - .max_ll_items = OTP_MAX_LL_ITEMS_2x00, - .shadow_ram_support = true, - .led_compensation = 57, - .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .scd_chain_ext_wa = true, -}; - -static const struct iwl_ht_params iwl2000_ht_params = { - .ht_greenfield_support = true, - .use_rts_for_aggregation = true, /* use rts/cts protection */ - .ht40_bands = BIT(IEEE80211_BAND_2GHZ), -}; - -static const struct iwl_eeprom_params iwl20x0_eeprom_params = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_6000_REG_BAND_24_HT40_CHANNELS, - EEPROM_REGULATORY_BAND_NO_HT40, - }, - .enhanced_txpower = true, -}; - -#define IWL_DEVICE_2000 \ - .fw_name_pre = IWL2000_FW_PRE, \ - .ucode_api_max = IWL2000_UCODE_API_MAX, \ - .ucode_api_ok = IWL2000_UCODE_API_OK, \ - .ucode_api_min = IWL2000_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_2000, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .base_params = &iwl2000_base_params, \ - .eeprom_params = &iwl20x0_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - - -const struct iwl_cfg iwl2000_2bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN", - IWL_DEVICE_2000, - .ht_params = &iwl2000_ht_params, -}; - -const struct iwl_cfg iwl2000_2bgn_d_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 2200D BGN", - IWL_DEVICE_2000, - .ht_params = &iwl2000_ht_params, -}; - -#define IWL_DEVICE_2030 \ - .fw_name_pre = IWL2030_FW_PRE, \ - .ucode_api_max = IWL2030_UCODE_API_MAX, \ - .ucode_api_ok = IWL2030_UCODE_API_OK, \ - .ucode_api_min = IWL2030_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_2030, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .base_params = &iwl2030_base_params, \ - .eeprom_params = &iwl20x0_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl2030_2bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", - IWL_DEVICE_2030, - .ht_params = &iwl2000_ht_params, -}; - -#define IWL_DEVICE_105 \ - .fw_name_pre = IWL105_FW_PRE, \ - .ucode_api_max = IWL105_UCODE_API_MAX, \ - .ucode_api_ok = IWL105_UCODE_API_OK, \ - .ucode_api_min = IWL105_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_105, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .base_params = &iwl2000_base_params, \ - .eeprom_params = &iwl20x0_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE, \ - .rx_with_siso_diversity = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl105_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 105 BGN", - IWL_DEVICE_105, - .ht_params = &iwl2000_ht_params, -}; - -const struct iwl_cfg iwl105_bgn_d_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 105D BGN", - IWL_DEVICE_105, - .ht_params = &iwl2000_ht_params, -}; - -#define IWL_DEVICE_135 \ - .fw_name_pre = IWL135_FW_PRE, \ - .ucode_api_max = IWL135_UCODE_API_MAX, \ - .ucode_api_ok = IWL135_UCODE_API_OK, \ - .ucode_api_min = IWL135_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_135, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .base_params = &iwl2030_base_params, \ - .eeprom_params = &iwl20x0_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE, \ - .rx_with_siso_diversity = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl135_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 135 BGN", - IWL_DEVICE_135, - .ht_params = &iwl2000_ht_params, -}; - -MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_OK)); -MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_OK)); -MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_OK)); -MODULE_FIRMWARE(IWL135_MODULE_FIRMWARE(IWL135_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c deleted file mode 100644 index 724194e23414..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ /dev/null @@ -1,178 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include "iwl-config.h" -#include "iwl-agn-hw.h" -#include "iwl-csr.h" - -/* Highest firmware API version supported */ -#define IWL5000_UCODE_API_MAX 5 -#define IWL5150_UCODE_API_MAX 2 - -/* Oldest version we won't warn about */ -#define IWL5000_UCODE_API_OK 5 -#define IWL5150_UCODE_API_OK 2 - -/* Lowest firmware API version supported */ -#define IWL5000_UCODE_API_MIN 1 -#define IWL5150_UCODE_API_MIN 1 - -/* EEPROM versions */ -#define EEPROM_5000_TX_POWER_VERSION (4) -#define EEPROM_5000_EEPROM_VERSION (0x11A) -#define EEPROM_5050_TX_POWER_VERSION (4) -#define EEPROM_5050_EEPROM_VERSION (0x21E) - -#define IWL5000_FW_PRE "iwlwifi-5000-" -#define IWL5000_MODULE_FIRMWARE(api) IWL5000_FW_PRE __stringify(api) ".ucode" - -#define IWL5150_FW_PRE "iwlwifi-5150-" -#define IWL5150_MODULE_FIRMWARE(api) IWL5150_FW_PRE __stringify(api) ".ucode" - -static const struct iwl_base_params iwl5000_base_params = { - .eeprom_size = IWLAGN_EEPROM_IMG_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, - .led_compensation = 51, - .wd_timeout = IWL_WATCHDOG_DISABLED, - .max_event_log_size = 512, - .scd_chain_ext_wa = true, -}; - -static const struct iwl_ht_params iwl5000_ht_params = { - .ht_greenfield_support = true, - .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), -}; - -static const struct iwl_eeprom_params iwl5000_eeprom_params = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_REG_BAND_24_HT40_CHANNELS, - EEPROM_REG_BAND_52_HT40_CHANNELS - }, -}; - -#define IWL_DEVICE_5000 \ - .fw_name_pre = IWL5000_FW_PRE, \ - .ucode_api_max = IWL5000_UCODE_API_MAX, \ - .ucode_api_ok = IWL5000_UCODE_API_OK, \ - .ucode_api_min = IWL5000_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_5000, \ - .max_inst_size = IWLAGN_RTC_INST_SIZE, \ - .max_data_size = IWLAGN_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_5000_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_5000_TX_POWER_VERSION, \ - .base_params = &iwl5000_base_params, \ - .eeprom_params = &iwl5000_eeprom_params, \ - .led_mode = IWL_LED_BLINK, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl5300_agn_cfg = { - .name = "Intel(R) Ultimate N WiFi Link 5300 AGN", - IWL_DEVICE_5000, - /* at least EEPROM 0x11A has wrong info */ - .valid_tx_ant = ANT_ABC, /* .cfg overwrite */ - .valid_rx_ant = ANT_ABC, /* .cfg overwrite */ - .ht_params = &iwl5000_ht_params, -}; - -const struct iwl_cfg iwl5100_bgn_cfg = { - .name = "Intel(R) WiFi Link 5100 BGN", - IWL_DEVICE_5000, - .valid_tx_ant = ANT_B, /* .cfg overwrite */ - .valid_rx_ant = ANT_AB, /* .cfg overwrite */ - .ht_params = &iwl5000_ht_params, -}; - -const struct iwl_cfg iwl5100_abg_cfg = { - .name = "Intel(R) WiFi Link 5100 ABG", - IWL_DEVICE_5000, - .valid_tx_ant = ANT_B, /* .cfg overwrite */ - .valid_rx_ant = ANT_AB, /* .cfg overwrite */ -}; - -const struct iwl_cfg iwl5100_agn_cfg = { - .name = "Intel(R) WiFi Link 5100 AGN", - IWL_DEVICE_5000, - .valid_tx_ant = ANT_B, /* .cfg overwrite */ - .valid_rx_ant = ANT_AB, /* .cfg overwrite */ - .ht_params = &iwl5000_ht_params, -}; - -const struct iwl_cfg iwl5350_agn_cfg = { - .name = "Intel(R) WiMAX/WiFi Link 5350 AGN", - .fw_name_pre = IWL5000_FW_PRE, - .ucode_api_max = IWL5000_UCODE_API_MAX, - .ucode_api_ok = IWL5000_UCODE_API_OK, - .ucode_api_min = IWL5000_UCODE_API_MIN, - .device_family = IWL_DEVICE_FAMILY_5000, - .max_inst_size = IWLAGN_RTC_INST_SIZE, - .max_data_size = IWLAGN_RTC_DATA_SIZE, - .nvm_ver = EEPROM_5050_EEPROM_VERSION, - .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, - .base_params = &iwl5000_base_params, - .eeprom_params = &iwl5000_eeprom_params, - .ht_params = &iwl5000_ht_params, - .led_mode = IWL_LED_BLINK, - .internal_wimax_coex = true, -}; - -#define IWL_DEVICE_5150 \ - .fw_name_pre = IWL5150_FW_PRE, \ - .ucode_api_max = IWL5150_UCODE_API_MAX, \ - .ucode_api_ok = IWL5150_UCODE_API_OK, \ - .ucode_api_min = IWL5150_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_5150, \ - .max_inst_size = IWLAGN_RTC_INST_SIZE, \ - .max_data_size = IWLAGN_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_5050_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ - .base_params = &iwl5000_base_params, \ - .eeprom_params = &iwl5000_eeprom_params, \ - .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl5150_agn_cfg = { - .name = "Intel(R) WiMAX/WiFi Link 5150 AGN", - IWL_DEVICE_5150, - .ht_params = &iwl5000_ht_params, - -}; - -const struct iwl_cfg iwl5150_abg_cfg = { - .name = "Intel(R) WiMAX/WiFi Link 5150 ABG", - IWL_DEVICE_5150, -}; - -MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_OK)); -MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c deleted file mode 100644 index 21b2630763dc..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ /dev/null @@ -1,389 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include "iwl-config.h" -#include "iwl-agn-hw.h" -#include "dvm/commands.h" /* needed for BT for now */ - -/* Highest firmware API version supported */ -#define IWL6000_UCODE_API_MAX 6 -#define IWL6050_UCODE_API_MAX 5 -#define IWL6000G2_UCODE_API_MAX 6 -#define IWL6035_UCODE_API_MAX 6 - -/* Oldest version we won't warn about */ -#define IWL6000_UCODE_API_OK 4 -#define IWL6000G2_UCODE_API_OK 5 -#define IWL6050_UCODE_API_OK 5 -#define IWL6000G2B_UCODE_API_OK 6 -#define IWL6035_UCODE_API_OK 6 - -/* Lowest firmware API version supported */ -#define IWL6000_UCODE_API_MIN 4 -#define IWL6050_UCODE_API_MIN 4 -#define IWL6000G2_UCODE_API_MIN 5 -#define IWL6035_UCODE_API_MIN 6 - -/* EEPROM versions */ -#define EEPROM_6000_TX_POWER_VERSION (4) -#define EEPROM_6000_EEPROM_VERSION (0x423) -#define EEPROM_6050_TX_POWER_VERSION (4) -#define EEPROM_6050_EEPROM_VERSION (0x532) -#define EEPROM_6150_TX_POWER_VERSION (6) -#define EEPROM_6150_EEPROM_VERSION (0x553) -#define EEPROM_6005_TX_POWER_VERSION (6) -#define EEPROM_6005_EEPROM_VERSION (0x709) -#define EEPROM_6030_TX_POWER_VERSION (6) -#define EEPROM_6030_EEPROM_VERSION (0x709) -#define EEPROM_6035_TX_POWER_VERSION (6) -#define EEPROM_6035_EEPROM_VERSION (0x753) - -#define IWL6000_FW_PRE "iwlwifi-6000-" -#define IWL6000_MODULE_FIRMWARE(api) IWL6000_FW_PRE __stringify(api) ".ucode" - -#define IWL6050_FW_PRE "iwlwifi-6050-" -#define IWL6050_MODULE_FIRMWARE(api) IWL6050_FW_PRE __stringify(api) ".ucode" - -#define IWL6005_FW_PRE "iwlwifi-6000g2a-" -#define IWL6005_MODULE_FIRMWARE(api) IWL6005_FW_PRE __stringify(api) ".ucode" - -#define IWL6030_FW_PRE "iwlwifi-6000g2b-" -#define IWL6030_MODULE_FIRMWARE(api) IWL6030_FW_PRE __stringify(api) ".ucode" - -static const struct iwl_base_params iwl6000_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = 0, - .max_ll_items = OTP_MAX_LL_ITEMS_6x00, - .shadow_ram_support = true, - .led_compensation = 51, - .wd_timeout = IWL_DEF_WD_TIMEOUT, - .max_event_log_size = 512, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .scd_chain_ext_wa = true, -}; - -static const struct iwl_base_params iwl6050_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = 0, - .max_ll_items = OTP_MAX_LL_ITEMS_6x50, - .shadow_ram_support = true, - .led_compensation = 51, - .wd_timeout = IWL_DEF_WD_TIMEOUT, - .max_event_log_size = 1024, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .scd_chain_ext_wa = true, -}; - -static const struct iwl_base_params iwl6000_g2_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = 0, - .max_ll_items = OTP_MAX_LL_ITEMS_6x00, - .shadow_ram_support = true, - .led_compensation = 57, - .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .scd_chain_ext_wa = true, -}; - -static const struct iwl_ht_params iwl6000_ht_params = { - .ht_greenfield_support = true, - .use_rts_for_aggregation = true, /* use rts/cts protection */ - .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), -}; - -static const struct iwl_eeprom_params iwl6000_eeprom_params = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_6000_REG_BAND_24_HT40_CHANNELS, - EEPROM_REG_BAND_52_HT40_CHANNELS - }, - .enhanced_txpower = true, -}; - -#define IWL_DEVICE_6005 \ - .fw_name_pre = IWL6005_FW_PRE, \ - .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ - .ucode_api_ok = IWL6000G2_UCODE_API_OK, \ - .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_6005, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_6005_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ - .base_params = &iwl6000_g2_base_params, \ - .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl6005_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6005_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205 ABG", - IWL_DEVICE_6005, -}; - -const struct iwl_cfg iwl6005_2bg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205 BG", - IWL_DEVICE_6005, -}; - -const struct iwl_cfg iwl6005_2agn_sff_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205S AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6005_2agn_d_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205D AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6005_2agn_mow1_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6206 AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6005_2agn_mow2_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6207 AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; - -#define IWL_DEVICE_6030 \ - .fw_name_pre = IWL6030_FW_PRE, \ - .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ - .ucode_api_ok = IWL6000G2B_UCODE_API_OK, \ - .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_6030, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ - .base_params = &iwl6000_g2_base_params, \ - .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl6030_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", - IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6030_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 ABG", - IWL_DEVICE_6030, -}; - -const struct iwl_cfg iwl6030_2bgn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 BGN", - IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6030_2bg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 BG", - IWL_DEVICE_6030, -}; - -#define IWL_DEVICE_6035 \ - .fw_name_pre = IWL6030_FW_PRE, \ - .ucode_api_max = IWL6035_UCODE_API_MAX, \ - .ucode_api_ok = IWL6035_UCODE_API_OK, \ - .ucode_api_min = IWL6035_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_6030, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ - .base_params = &iwl6000_g2_base_params, \ - .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_RF_STATE, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl6035_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", - IWL_DEVICE_6035, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6035_2agn_sff_cfg = { - .name = "Intel(R) Centrino(R) Ultimate-N 6235 AGN", - IWL_DEVICE_6035, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl1030_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN", - IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl1030_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1030 BG", - IWL_DEVICE_6030, -}; - -const struct iwl_cfg iwl130_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 130 BGN", - IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, - .rx_with_siso_diversity = true, -}; - -const struct iwl_cfg iwl130_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 130 BG", - IWL_DEVICE_6030, - .rx_with_siso_diversity = true, -}; - -/* - * "i": Internal configuration, use internal Power Amplifier - */ -#define IWL_DEVICE_6000i \ - .fw_name_pre = IWL6000_FW_PRE, \ - .ucode_api_max = IWL6000_UCODE_API_MAX, \ - .ucode_api_ok = IWL6000_UCODE_API_OK, \ - .ucode_api_min = IWL6000_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_6000i, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .valid_tx_ant = ANT_BC, /* .cfg overwrite */ \ - .valid_rx_ant = ANT_BC, /* .cfg overwrite */ \ - .nvm_ver = EEPROM_6000_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, \ - .base_params = &iwl6000_base_params, \ - .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_BLINK, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl6000i_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN", - IWL_DEVICE_6000i, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6000i_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG", - IWL_DEVICE_6000i, -}; - -const struct iwl_cfg iwl6000i_2bg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6200 BG", - IWL_DEVICE_6000i, -}; - -#define IWL_DEVICE_6050 \ - .fw_name_pre = IWL6050_FW_PRE, \ - .ucode_api_max = IWL6050_UCODE_API_MAX, \ - .ucode_api_min = IWL6050_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_6050, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .valid_tx_ant = ANT_AB, /* .cfg overwrite */ \ - .valid_rx_ant = ANT_AB, /* .cfg overwrite */ \ - .nvm_ver = EEPROM_6050_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_6050_TX_POWER_VERSION, \ - .base_params = &iwl6050_base_params, \ - .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl6050_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN", - IWL_DEVICE_6050, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6050_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG", - IWL_DEVICE_6050, -}; - -#define IWL_DEVICE_6150 \ - .fw_name_pre = IWL6050_FW_PRE, \ - .ucode_api_max = IWL6050_UCODE_API_MAX, \ - .ucode_api_min = IWL6050_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_6150, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_6150_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_6150_TX_POWER_VERSION, \ - .base_params = &iwl6050_base_params, \ - .eeprom_params = &iwl6000_eeprom_params, \ - .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K - -const struct iwl_cfg iwl6150_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN", - IWL_DEVICE_6150, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6150_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG", - IWL_DEVICE_6150, -}; - -const struct iwl_cfg iwl6000_3agn_cfg = { - .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN", - .fw_name_pre = IWL6000_FW_PRE, - .ucode_api_max = IWL6000_UCODE_API_MAX, - .ucode_api_ok = IWL6000_UCODE_API_OK, - .ucode_api_min = IWL6000_UCODE_API_MIN, - .device_family = IWL_DEVICE_FAMILY_6000, - .max_inst_size = IWL60_RTC_INST_SIZE, - .max_data_size = IWL60_RTC_DATA_SIZE, - .nvm_ver = EEPROM_6000_EEPROM_VERSION, - .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, - .base_params = &iwl6000_base_params, - .eeprom_params = &iwl6000_eeprom_params, - .ht_params = &iwl6000_ht_params, - .led_mode = IWL_LED_BLINK, -}; - -MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_OK)); -MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_OK)); -MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_OK)); -MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2B_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c deleted file mode 100644 index 1a73c7a1da77..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ /dev/null @@ -1,346 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include -#include "iwl-config.h" -#include "iwl-agn-hw.h" - -/* Highest firmware API version supported */ -#define IWL7260_UCODE_API_MAX 17 - -/* Oldest version we won't warn about */ -#define IWL7260_UCODE_API_OK 13 - -/* Lowest firmware API version supported */ -#define IWL7260_UCODE_API_MIN 13 - -/* NVM versions */ -#define IWL7260_NVM_VERSION 0x0a1d -#define IWL7260_TX_POWER_VERSION 0xffff /* meaningless */ -#define IWL3160_NVM_VERSION 0x709 -#define IWL3160_TX_POWER_VERSION 0xffff /* meaningless */ -#define IWL3165_NVM_VERSION 0x709 -#define IWL3165_TX_POWER_VERSION 0xffff /* meaningless */ -#define IWL7265_NVM_VERSION 0x0a1d -#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */ -#define IWL7265D_NVM_VERSION 0x0c11 -#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */ - -/* DCCM offsets and lengths */ -#define IWL7000_DCCM_OFFSET 0x800000 -#define IWL7260_DCCM_LEN 0x14000 -#define IWL3160_DCCM_LEN 0x10000 -#define IWL7265_DCCM_LEN 0x17A00 - -#define IWL7260_FW_PRE "iwlwifi-7260-" -#define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode" - -#define IWL3160_FW_PRE "iwlwifi-3160-" -#define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode" - -#define IWL7265_FW_PRE "iwlwifi-7265-" -#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode" - -#define IWL7265D_FW_PRE "iwlwifi-7265D-" -#define IWL7265D_MODULE_FIRMWARE(api) IWL7265D_FW_PRE __stringify(api) ".ucode" - -#define NVM_HW_SECTION_NUM_FAMILY_7000 0 - -static const struct iwl_base_params iwl7000_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000, - .num_of_queues = 31, - .pll_cfg_val = 0, - .shadow_ram_support = true, - .led_compensation = 57, - .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, - .shadow_reg_enable = true, - .pcie_l1_allowed = true, - .apmg_wake_up_wa = true, -}; - -static const struct iwl_tt_params iwl7000_high_temp_tt_params = { - .ct_kill_entry = 118, - .ct_kill_exit = 96, - .ct_kill_duration = 5, - .dynamic_smps_entry = 114, - .dynamic_smps_exit = 110, - .tx_protection_entry = 114, - .tx_protection_exit = 108, - .tx_backoff = { - {.temperature = 112, .backoff = 300}, - {.temperature = 113, .backoff = 800}, - {.temperature = 114, .backoff = 1500}, - {.temperature = 115, .backoff = 3000}, - {.temperature = 116, .backoff = 5000}, - {.temperature = 117, .backoff = 10000}, - }, - .support_ct_kill = true, - .support_dynamic_smps = true, - .support_tx_protection = true, - .support_tx_backoff = true, -}; - -static const struct iwl_ht_params iwl7000_ht_params = { - .stbc = true, - .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), -}; - -#define IWL_DEVICE_7000 \ - .ucode_api_max = IWL7260_UCODE_API_MAX, \ - .ucode_api_ok = IWL7260_UCODE_API_OK, \ - .ucode_api_min = IWL7260_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_7000, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .base_params = &iwl7000_base_params, \ - .led_mode = IWL_LED_RF_STATE, \ - .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000, \ - .non_shared_ant = ANT_A, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ - .dccm_offset = IWL7000_DCCM_OFFSET - -const struct iwl_cfg iwl7260_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 7260", - .fw_name_pre = IWL7260_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL7260_NVM_VERSION, - .nvm_calib_ver = IWL7260_TX_POWER_VERSION, - .host_interrupt_operation_mode = true, - .lp_xtal_workaround = true, - .dccm_len = IWL7260_DCCM_LEN, -}; - -const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { - .name = "Intel(R) Dual Band Wireless AC 7260", - .fw_name_pre = IWL7260_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL7260_NVM_VERSION, - .nvm_calib_ver = IWL7260_TX_POWER_VERSION, - .high_temp = true, - .host_interrupt_operation_mode = true, - .lp_xtal_workaround = true, - .dccm_len = IWL7260_DCCM_LEN, - .thermal_params = &iwl7000_high_temp_tt_params, -}; - -const struct iwl_cfg iwl7260_2n_cfg = { - .name = "Intel(R) Dual Band Wireless N 7260", - .fw_name_pre = IWL7260_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL7260_NVM_VERSION, - .nvm_calib_ver = IWL7260_TX_POWER_VERSION, - .host_interrupt_operation_mode = true, - .lp_xtal_workaround = true, - .dccm_len = IWL7260_DCCM_LEN, -}; - -const struct iwl_cfg iwl7260_n_cfg = { - .name = "Intel(R) Wireless N 7260", - .fw_name_pre = IWL7260_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL7260_NVM_VERSION, - .nvm_calib_ver = IWL7260_TX_POWER_VERSION, - .host_interrupt_operation_mode = true, - .lp_xtal_workaround = true, - .dccm_len = IWL7260_DCCM_LEN, -}; - -const struct iwl_cfg iwl3160_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 3160", - .fw_name_pre = IWL3160_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL3160_NVM_VERSION, - .nvm_calib_ver = IWL3160_TX_POWER_VERSION, - .host_interrupt_operation_mode = true, - .dccm_len = IWL3160_DCCM_LEN, -}; - -const struct iwl_cfg iwl3160_2n_cfg = { - .name = "Intel(R) Dual Band Wireless N 3160", - .fw_name_pre = IWL3160_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL3160_NVM_VERSION, - .nvm_calib_ver = IWL3160_TX_POWER_VERSION, - .host_interrupt_operation_mode = true, - .dccm_len = IWL3160_DCCM_LEN, -}; - -const struct iwl_cfg iwl3160_n_cfg = { - .name = "Intel(R) Wireless N 3160", - .fw_name_pre = IWL3160_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL3160_NVM_VERSION, - .nvm_calib_ver = IWL3160_TX_POWER_VERSION, - .host_interrupt_operation_mode = true, - .dccm_len = IWL3160_DCCM_LEN, -}; - -static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = { - {.pwr = 1600, .backoff = 0}, - {.pwr = 1300, .backoff = 467}, - {.pwr = 900, .backoff = 1900}, - {.pwr = 800, .backoff = 2630}, - {.pwr = 700, .backoff = 3720}, - {.pwr = 600, .backoff = 5550}, - {.pwr = 500, .backoff = 9350}, - {0}, -}; - -static const struct iwl_ht_params iwl7265_ht_params = { - .stbc = true, - .ldpc = true, - .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), -}; - -const struct iwl_cfg iwl3165_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 3165", - .fw_name_pre = IWL7265D_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL3165_NVM_VERSION, - .nvm_calib_ver = IWL3165_TX_POWER_VERSION, - .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, - .dccm_len = IWL7265_DCCM_LEN, -}; - -const struct iwl_cfg iwl7265_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 7265", - .fw_name_pre = IWL7265_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7265_ht_params, - .nvm_ver = IWL7265_NVM_VERSION, - .nvm_calib_ver = IWL7265_TX_POWER_VERSION, - .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, - .dccm_len = IWL7265_DCCM_LEN, -}; - -const struct iwl_cfg iwl7265_2n_cfg = { - .name = "Intel(R) Dual Band Wireless N 7265", - .fw_name_pre = IWL7265_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7265_ht_params, - .nvm_ver = IWL7265_NVM_VERSION, - .nvm_calib_ver = IWL7265_TX_POWER_VERSION, - .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, - .dccm_len = IWL7265_DCCM_LEN, -}; - -const struct iwl_cfg iwl7265_n_cfg = { - .name = "Intel(R) Wireless N 7265", - .fw_name_pre = IWL7265_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7265_ht_params, - .nvm_ver = IWL7265_NVM_VERSION, - .nvm_calib_ver = IWL7265_TX_POWER_VERSION, - .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, - .dccm_len = IWL7265_DCCM_LEN, -}; - -const struct iwl_cfg iwl7265d_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 7265", - .fw_name_pre = IWL7265D_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7265_ht_params, - .nvm_ver = IWL7265D_NVM_VERSION, - .nvm_calib_ver = IWL7265_TX_POWER_VERSION, - .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, - .dccm_len = IWL7265_DCCM_LEN, -}; - -const struct iwl_cfg iwl7265d_2n_cfg = { - .name = "Intel(R) Dual Band Wireless N 7265", - .fw_name_pre = IWL7265D_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7265_ht_params, - .nvm_ver = IWL7265D_NVM_VERSION, - .nvm_calib_ver = IWL7265_TX_POWER_VERSION, - .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, - .dccm_len = IWL7265_DCCM_LEN, -}; - -const struct iwl_cfg iwl7265d_n_cfg = { - .name = "Intel(R) Wireless N 7265", - .fw_name_pre = IWL7265D_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7265_ht_params, - .nvm_ver = IWL7265D_NVM_VERSION, - .nvm_calib_ver = IWL7265_TX_POWER_VERSION, - .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, - .dccm_len = IWL7265_DCCM_LEN, -}; - -MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); -MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); -MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); -MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c deleted file mode 100644 index 0116e5a4c393..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ /dev/null @@ -1,229 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include -#include "iwl-config.h" -#include "iwl-agn-hw.h" - -/* Highest firmware API version supported */ -#define IWL8000_UCODE_API_MAX 17 - -/* Oldest version we won't warn about */ -#define IWL8000_UCODE_API_OK 13 - -/* Lowest firmware API version supported */ -#define IWL8000_UCODE_API_MIN 13 - -/* NVM versions */ -#define IWL8000_NVM_VERSION 0x0a1d -#define IWL8000_TX_POWER_VERSION 0xffff /* meaningless */ - -/* Memory offsets and lengths */ -#define IWL8260_DCCM_OFFSET 0x800000 -#define IWL8260_DCCM_LEN 0x18000 -#define IWL8260_DCCM2_OFFSET 0x880000 -#define IWL8260_DCCM2_LEN 0x8000 -#define IWL8260_SMEM_OFFSET 0x400000 -#define IWL8260_SMEM_LEN 0x68000 - -#define IWL8000_FW_PRE "iwlwifi-8000" -#define IWL8000_MODULE_FIRMWARE(api) \ - IWL8000_FW_PRE "-" __stringify(api) ".ucode" - -#define NVM_HW_SECTION_NUM_FAMILY_8000 10 -#define DEFAULT_NVM_FILE_FAMILY_8000B "nvmData-8000B" -#define DEFAULT_NVM_FILE_FAMILY_8000C "nvmData-8000C" - -/* Max SDIO RX/TX aggregation sizes of the ADDBA request/response */ -#define MAX_RX_AGG_SIZE_8260_SDIO 21 -#define MAX_TX_AGG_SIZE_8260_SDIO 40 - -/* Max A-MPDU exponent for HT and VHT */ -#define MAX_HT_AMPDU_EXPONENT_8260_SDIO IEEE80211_HT_MAX_AMPDU_32K -#define MAX_VHT_AMPDU_EXPONENT_8260_SDIO IEEE80211_VHT_MAX_AMPDU_32K - -static const struct iwl_base_params iwl8000_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000, - .num_of_queues = 31, - .pll_cfg_val = 0, - .shadow_ram_support = true, - .led_compensation = 57, - .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, - .shadow_reg_enable = true, - .pcie_l1_allowed = true, -}; - -static const struct iwl_ht_params iwl8000_ht_params = { - .stbc = true, - .ldpc = true, - .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), -}; - -static const struct iwl_tt_params iwl8000_tt_params = { - .ct_kill_entry = 115, - .ct_kill_exit = 93, - .ct_kill_duration = 5, - .dynamic_smps_entry = 111, - .dynamic_smps_exit = 107, - .tx_protection_entry = 112, - .tx_protection_exit = 105, - .tx_backoff = { - {.temperature = 110, .backoff = 200}, - {.temperature = 111, .backoff = 600}, - {.temperature = 112, .backoff = 1200}, - {.temperature = 113, .backoff = 2000}, - {.temperature = 114, .backoff = 4000}, - }, - .support_ct_kill = true, - .support_dynamic_smps = true, - .support_tx_protection = true, - .support_tx_backoff = true, -}; - -#define IWL_DEVICE_8000 \ - .ucode_api_max = IWL8000_UCODE_API_MAX, \ - .ucode_api_ok = IWL8000_UCODE_API_OK, \ - .ucode_api_min = IWL8000_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_8000, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .base_params = &iwl8000_base_params, \ - .led_mode = IWL_LED_RF_STATE, \ - .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000, \ - .d0i3 = true, \ - .features = NETIF_F_RXCSUM, \ - .non_shared_ant = ANT_A, \ - .dccm_offset = IWL8260_DCCM_OFFSET, \ - .dccm_len = IWL8260_DCCM_LEN, \ - .dccm2_offset = IWL8260_DCCM2_OFFSET, \ - .dccm2_len = IWL8260_DCCM2_LEN, \ - .smem_offset = IWL8260_SMEM_OFFSET, \ - .smem_len = IWL8260_SMEM_LEN, \ - .default_nvm_file_B_step = DEFAULT_NVM_FILE_FAMILY_8000B, \ - .default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C, \ - .thermal_params = &iwl8000_tt_params, \ - .apmg_not_supported = true - -const struct iwl_cfg iwl8260_2n_cfg = { - .name = "Intel(R) Dual Band Wireless N 8260", - .fw_name_pre = IWL8000_FW_PRE, - IWL_DEVICE_8000, - .ht_params = &iwl8000_ht_params, - .nvm_ver = IWL8000_NVM_VERSION, - .nvm_calib_ver = IWL8000_TX_POWER_VERSION, -}; - -const struct iwl_cfg iwl8260_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 8260", - .fw_name_pre = IWL8000_FW_PRE, - IWL_DEVICE_8000, - .ht_params = &iwl8000_ht_params, - .nvm_ver = IWL8000_NVM_VERSION, - .nvm_calib_ver = IWL8000_TX_POWER_VERSION, - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, -}; - -const struct iwl_cfg iwl4165_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 4165", - .fw_name_pre = IWL8000_FW_PRE, - IWL_DEVICE_8000, - .ht_params = &iwl8000_ht_params, - .nvm_ver = IWL8000_NVM_VERSION, - .nvm_calib_ver = IWL8000_TX_POWER_VERSION, - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, -}; - -const struct iwl_cfg iwl8260_2ac_sdio_cfg = { - .name = "Intel(R) Dual Band Wireless-AC 8260", - .fw_name_pre = IWL8000_FW_PRE, - IWL_DEVICE_8000, - .ht_params = &iwl8000_ht_params, - .nvm_ver = IWL8000_NVM_VERSION, - .nvm_calib_ver = IWL8000_TX_POWER_VERSION, - .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO, - .max_tx_agg_size = MAX_TX_AGG_SIZE_8260_SDIO, - .disable_dummy_notification = true, - .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO, - .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO, -}; - -const struct iwl_cfg iwl4165_2ac_sdio_cfg = { - .name = "Intel(R) Dual Band Wireless-AC 4165", - .fw_name_pre = IWL8000_FW_PRE, - IWL_DEVICE_8000, - .ht_params = &iwl8000_ht_params, - .nvm_ver = IWL8000_NVM_VERSION, - .nvm_calib_ver = IWL8000_TX_POWER_VERSION, - .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO, - .max_tx_agg_size = MAX_TX_AGG_SIZE_8260_SDIO, - .bt_shared_single_ant = true, - .disable_dummy_notification = true, - .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO, - .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO, -}; - -MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h deleted file mode 100644 index 04a483d38659..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h +++ /dev/null @@ -1,117 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -/* - * Please use this file (iwl-agn-hw.h) only for hardware-related definitions. - */ - -#ifndef __iwl_agn_hw_h__ -#define __iwl_agn_hw_h__ - -#define IWLAGN_RTC_INST_LOWER_BOUND (0x000000) -#define IWLAGN_RTC_INST_UPPER_BOUND (0x020000) - -#define IWLAGN_RTC_DATA_LOWER_BOUND (0x800000) -#define IWLAGN_RTC_DATA_UPPER_BOUND (0x80C000) - -#define IWLAGN_RTC_INST_SIZE (IWLAGN_RTC_INST_UPPER_BOUND - \ - IWLAGN_RTC_INST_LOWER_BOUND) -#define IWLAGN_RTC_DATA_SIZE (IWLAGN_RTC_DATA_UPPER_BOUND - \ - IWLAGN_RTC_DATA_LOWER_BOUND) - -#define IWL60_RTC_INST_LOWER_BOUND (0x000000) -#define IWL60_RTC_INST_UPPER_BOUND (0x040000) -#define IWL60_RTC_DATA_LOWER_BOUND (0x800000) -#define IWL60_RTC_DATA_UPPER_BOUND (0x814000) -#define IWL60_RTC_INST_SIZE \ - (IWL60_RTC_INST_UPPER_BOUND - IWL60_RTC_INST_LOWER_BOUND) -#define IWL60_RTC_DATA_SIZE \ - (IWL60_RTC_DATA_UPPER_BOUND - IWL60_RTC_DATA_LOWER_BOUND) - -/* RSSI to dBm */ -#define IWLAGN_RSSI_OFFSET 44 - -#define IWLAGN_DEFAULT_TX_RETRY 15 -#define IWLAGN_MGMT_DFAULT_RETRY_LIMIT 3 -#define IWLAGN_RTS_DFAULT_RETRY_LIMIT 60 -#define IWLAGN_BAR_DFAULT_RETRY_LIMIT 60 -#define IWLAGN_LOW_RETRY_LIMIT 7 - -/* Limit range of txpower output target to be between these values */ -#define IWLAGN_TX_POWER_TARGET_POWER_MIN (0) /* 0 dBm: 1 milliwatt */ -#define IWLAGN_TX_POWER_TARGET_POWER_MAX (16) /* 16 dBm */ - -/* EEPROM */ -#define IWLAGN_EEPROM_IMG_SIZE 2048 - -/* high blocks contain PAPD data */ -#define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */ -#define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */ -#define OTP_MAX_LL_ITEMS_1000 (3) /* OTP blocks for 1000 */ -#define OTP_MAX_LL_ITEMS_6x00 (4) /* OTP blocks for 6x00 */ -#define OTP_MAX_LL_ITEMS_6x50 (7) /* OTP blocks for 6x50 */ -#define OTP_MAX_LL_ITEMS_2x00 (4) /* OTP blocks for 2x00 */ - - -#define IWLAGN_NUM_QUEUES 20 - -#endif /* __iwl_agn_hw_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h deleted file mode 100644 index 910970858f98..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ /dev/null @@ -1,437 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __IWL_CONFIG_H__ -#define __IWL_CONFIG_H__ - -#include -#include - - -enum iwl_device_family { - IWL_DEVICE_FAMILY_UNDEFINED, - IWL_DEVICE_FAMILY_1000, - IWL_DEVICE_FAMILY_100, - IWL_DEVICE_FAMILY_2000, - IWL_DEVICE_FAMILY_2030, - IWL_DEVICE_FAMILY_105, - IWL_DEVICE_FAMILY_135, - IWL_DEVICE_FAMILY_5000, - IWL_DEVICE_FAMILY_5150, - IWL_DEVICE_FAMILY_6000, - IWL_DEVICE_FAMILY_6000i, - IWL_DEVICE_FAMILY_6005, - IWL_DEVICE_FAMILY_6030, - IWL_DEVICE_FAMILY_6050, - IWL_DEVICE_FAMILY_6150, - IWL_DEVICE_FAMILY_7000, - IWL_DEVICE_FAMILY_8000, -}; - -static inline bool iwl_has_secure_boot(u32 hw_rev, - enum iwl_device_family family) -{ - /* return 1 only for family 8000 B0 */ - if ((family == IWL_DEVICE_FAMILY_8000) && (hw_rev & 0xC)) - return true; - - return false; -} - -/* - * LED mode - * IWL_LED_DEFAULT: use device default - * IWL_LED_RF_STATE: turn LED on/off based on RF state - * LED ON = RF ON - * LED OFF = RF OFF - * IWL_LED_BLINK: adjust led blink rate based on blink table - * IWL_LED_DISABLE: led disabled - */ -enum iwl_led_mode { - IWL_LED_DEFAULT, - IWL_LED_RF_STATE, - IWL_LED_BLINK, - IWL_LED_DISABLE, -}; - -/* - * This is the threshold value of plcp error rate per 100mSecs. It is - * used to set and check for the validity of plcp_delta. - */ -#define IWL_MAX_PLCP_ERR_THRESHOLD_MIN 1 -#define IWL_MAX_PLCP_ERR_THRESHOLD_DEF 50 -#define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF 100 -#define IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF 200 -#define IWL_MAX_PLCP_ERR_THRESHOLD_MAX 255 -#define IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE 0 - -/* TX queue watchdog timeouts in mSecs */ -#define IWL_WATCHDOG_DISABLED 0 -#define IWL_DEF_WD_TIMEOUT 2500 -#define IWL_LONG_WD_TIMEOUT 10000 -#define IWL_MAX_WD_TIMEOUT 120000 - -#define IWL_DEFAULT_MAX_TX_POWER 22 - -/* Antenna presence definitions */ -#define ANT_NONE 0x0 -#define ANT_A BIT(0) -#define ANT_B BIT(1) -#define ANT_C BIT(2) -#define ANT_AB (ANT_A | ANT_B) -#define ANT_AC (ANT_A | ANT_C) -#define ANT_BC (ANT_B | ANT_C) -#define ANT_ABC (ANT_A | ANT_B | ANT_C) - -static inline u8 num_of_ant(u8 mask) -{ - return !!((mask) & ANT_A) + - !!((mask) & ANT_B) + - !!((mask) & ANT_C); -} - -/* - * @max_ll_items: max number of OTP blocks - * @shadow_ram_support: shadow support for OTP memory - * @led_compensation: compensate on the led on/off time per HW according - * to the deviation to achieve the desired led frequency. - * The detail algorithm is described in iwl-led.c - * @wd_timeout: TX queues watchdog timeout - * @max_event_log_size: size of event log buffer size for ucode event logging - * @shadow_reg_enable: HW shadow register support - * @apmg_wake_up_wa: should the MAC access REQ be asserted when a command - * is in flight. This is due to a HW bug in 7260, 3160 and 7265. - * @scd_chain_ext_wa: should the chain extension feature in SCD be disabled. - */ -struct iwl_base_params { - int eeprom_size; - int num_of_queues; /* def: HW dependent */ - /* for iwl_pcie_apm_init() */ - u32 pll_cfg_val; - - const u16 max_ll_items; - const bool shadow_ram_support; - u16 led_compensation; - unsigned int wd_timeout; - u32 max_event_log_size; - const bool shadow_reg_enable; - const bool pcie_l1_allowed; - const bool apmg_wake_up_wa; - const bool scd_chain_ext_wa; -}; - -/* - * @stbc: support Tx STBC and 1*SS Rx STBC - * @ldpc: support Tx/Rx with LDPC - * @use_rts_for_aggregation: use rts/cts protection for HT traffic - * @ht40_bands: bitmap of bands (using %IEEE80211_BAND_*) that support HT40 - */ -struct iwl_ht_params { - enum ieee80211_smps_mode smps_mode; - const bool ht_greenfield_support; /* if used set to true */ - const bool stbc; - const bool ldpc; - bool use_rts_for_aggregation; - u8 ht40_bands; -}; - -/* - * Tx-backoff threshold - * @temperature: The threshold in Celsius - * @backoff: The tx-backoff in uSec - */ -struct iwl_tt_tx_backoff { - s32 temperature; - u32 backoff; -}; - -#define TT_TX_BACKOFF_SIZE 6 - -/** - * struct iwl_tt_params - thermal throttling parameters - * @ct_kill_entry: CT Kill entry threshold - * @ct_kill_exit: CT Kill exit threshold - * @ct_kill_duration: The time intervals (in uSec) in which the driver needs - * to checks whether to exit CT Kill. - * @dynamic_smps_entry: Dynamic SMPS entry threshold - * @dynamic_smps_exit: Dynamic SMPS exit threshold - * @tx_protection_entry: TX protection entry threshold - * @tx_protection_exit: TX protection exit threshold - * @tx_backoff: Array of thresholds for tx-backoff , in ascending order. - * @support_ct_kill: Support CT Kill? - * @support_dynamic_smps: Support dynamic SMPS? - * @support_tx_protection: Support tx protection? - * @support_tx_backoff: Support tx-backoff? - */ -struct iwl_tt_params { - u32 ct_kill_entry; - u32 ct_kill_exit; - u32 ct_kill_duration; - u32 dynamic_smps_entry; - u32 dynamic_smps_exit; - u32 tx_protection_entry; - u32 tx_protection_exit; - struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE]; - bool support_ct_kill; - bool support_dynamic_smps; - bool support_tx_protection; - bool support_tx_backoff; -}; - -/* - * information on how to parse the EEPROM - */ -#define EEPROM_REG_BAND_1_CHANNELS 0x08 -#define EEPROM_REG_BAND_2_CHANNELS 0x26 -#define EEPROM_REG_BAND_3_CHANNELS 0x42 -#define EEPROM_REG_BAND_4_CHANNELS 0x5C -#define EEPROM_REG_BAND_5_CHANNELS 0x74 -#define EEPROM_REG_BAND_24_HT40_CHANNELS 0x82 -#define EEPROM_REG_BAND_52_HT40_CHANNELS 0x92 -#define EEPROM_6000_REG_BAND_24_HT40_CHANNELS 0x80 -#define EEPROM_REGULATORY_BAND_NO_HT40 0 - -/* lower blocks contain EEPROM image and calibration data */ -#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */ -#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */ -#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */ - -struct iwl_eeprom_params { - const u8 regulatory_bands[7]; - bool enhanced_txpower; -}; - -/* Tx-backoff power threshold - * @pwr: The power limit in mw - * @backoff: The tx-backoff in uSec - */ -struct iwl_pwr_tx_backoff { - u32 pwr; - u32 backoff; -}; - -/** - * struct iwl_cfg - * @name: Official name of the device - * @fw_name_pre: Firmware filename prefix. The api version and extension - * (.ucode) will be added to filename before loading from disk. The - * filename is constructed as fw_name_pre.ucode. - * @ucode_api_max: Highest version of uCode API supported by driver. - * @ucode_api_ok: oldest version of the uCode API that is OK to load - * without a warning, for use in transitions - * @ucode_api_min: Lowest version of uCode API supported by driver. - * @max_inst_size: The maximal length of the fw inst section - * @max_data_size: The maximal length of the fw data section - * @valid_tx_ant: valid transmit antenna - * @valid_rx_ant: valid receive antenna - * @non_shared_ant: the antenna that is for WiFi only - * @nvm_ver: NVM version - * @nvm_calib_ver: NVM calibration version - * @lib: pointer to the lib ops - * @base_params: pointer to basic parameters - * @ht_params: point to ht parameters - * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) - * @rx_with_siso_diversity: 1x1 device with rx antenna diversity - * @internal_wimax_coex: internal wifi/wimax combo device - * @high_temp: Is this NIC is designated to be in high temperature. - * @host_interrupt_operation_mode: device needs host interrupt operation - * mode set - * @d0i3: device uses d0i3 instead of d3 - * @nvm_hw_section_num: the ID of the HW NVM section - * @features: hw features, any combination of feature_whitelist - * @pwr_tx_backoffs: translation table between power limits and backoffs - * @max_rx_agg_size: max RX aggregation size of the ADDBA request/response - * @max_tx_agg_size: max TX aggregation size of the ADDBA request/response - * @max_ht_ampdu_factor: the exponent of the max length of A-MPDU that the - * station can receive in HT - * @max_vht_ampdu_exponent: the exponent of the max length of A-MPDU that the - * station can receive in VHT - * @dccm_offset: offset from which DCCM begins - * @dccm_len: length of DCCM (including runtime stack CCM) - * @dccm2_offset: offset from which the second DCCM begins - * @dccm2_len: length of the second DCCM - * @smem_offset: offset from which the SMEM begins - * @smem_len: the length of SMEM - * - * We enable the driver to be backward compatible wrt. hardware features. - * API differences in uCode shouldn't be handled here but through TLVs - * and/or the uCode API version instead. - */ -struct iwl_cfg { - /* params specific to an individual device within a device family */ - const char *name; - const char *fw_name_pre; - const unsigned int ucode_api_max; - const unsigned int ucode_api_ok; - const unsigned int ucode_api_min; - const enum iwl_device_family device_family; - const u32 max_data_size; - const u32 max_inst_size; - u8 valid_tx_ant; - u8 valid_rx_ant; - u8 non_shared_ant; - bool bt_shared_single_ant; - u16 nvm_ver; - u16 nvm_calib_ver; - /* params not likely to change within a device family */ - const struct iwl_base_params *base_params; - /* params likely to change within a device family */ - const struct iwl_ht_params *ht_params; - const struct iwl_eeprom_params *eeprom_params; - enum iwl_led_mode led_mode; - const bool rx_with_siso_diversity; - const bool internal_wimax_coex; - const bool host_interrupt_operation_mode; - bool high_temp; - bool d0i3; - u8 nvm_hw_section_num; - bool lp_xtal_workaround; - const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; - bool no_power_up_nic_in_init; - const char *default_nvm_file_B_step; - const char *default_nvm_file_C_step; - netdev_features_t features; - unsigned int max_rx_agg_size; - bool disable_dummy_notification; - unsigned int max_tx_agg_size; - unsigned int max_ht_ampdu_exponent; - unsigned int max_vht_ampdu_exponent; - const u32 dccm_offset; - const u32 dccm_len; - const u32 dccm2_offset; - const u32 dccm2_len; - const u32 smem_offset; - const u32 smem_len; - const struct iwl_tt_params *thermal_params; - bool apmg_not_supported; -}; - -/* - * This list declares the config structures for all devices. - */ -#if IS_ENABLED(CONFIG_IWLDVM) -extern const struct iwl_cfg iwl5300_agn_cfg; -extern const struct iwl_cfg iwl5100_agn_cfg; -extern const struct iwl_cfg iwl5350_agn_cfg; -extern const struct iwl_cfg iwl5100_bgn_cfg; -extern const struct iwl_cfg iwl5100_abg_cfg; -extern const struct iwl_cfg iwl5150_agn_cfg; -extern const struct iwl_cfg iwl5150_abg_cfg; -extern const struct iwl_cfg iwl6005_2agn_cfg; -extern const struct iwl_cfg iwl6005_2abg_cfg; -extern const struct iwl_cfg iwl6005_2bg_cfg; -extern const struct iwl_cfg iwl6005_2agn_sff_cfg; -extern const struct iwl_cfg iwl6005_2agn_d_cfg; -extern const struct iwl_cfg iwl6005_2agn_mow1_cfg; -extern const struct iwl_cfg iwl6005_2agn_mow2_cfg; -extern const struct iwl_cfg iwl1030_bgn_cfg; -extern const struct iwl_cfg iwl1030_bg_cfg; -extern const struct iwl_cfg iwl6030_2agn_cfg; -extern const struct iwl_cfg iwl6030_2abg_cfg; -extern const struct iwl_cfg iwl6030_2bgn_cfg; -extern const struct iwl_cfg iwl6030_2bg_cfg; -extern const struct iwl_cfg iwl6000i_2agn_cfg; -extern const struct iwl_cfg iwl6000i_2abg_cfg; -extern const struct iwl_cfg iwl6000i_2bg_cfg; -extern const struct iwl_cfg iwl6000_3agn_cfg; -extern const struct iwl_cfg iwl6050_2agn_cfg; -extern const struct iwl_cfg iwl6050_2abg_cfg; -extern const struct iwl_cfg iwl6150_bgn_cfg; -extern const struct iwl_cfg iwl6150_bg_cfg; -extern const struct iwl_cfg iwl1000_bgn_cfg; -extern const struct iwl_cfg iwl1000_bg_cfg; -extern const struct iwl_cfg iwl100_bgn_cfg; -extern const struct iwl_cfg iwl100_bg_cfg; -extern const struct iwl_cfg iwl130_bgn_cfg; -extern const struct iwl_cfg iwl130_bg_cfg; -extern const struct iwl_cfg iwl2000_2bgn_cfg; -extern const struct iwl_cfg iwl2000_2bgn_d_cfg; -extern const struct iwl_cfg iwl2030_2bgn_cfg; -extern const struct iwl_cfg iwl6035_2agn_cfg; -extern const struct iwl_cfg iwl6035_2agn_sff_cfg; -extern const struct iwl_cfg iwl105_bgn_cfg; -extern const struct iwl_cfg iwl105_bgn_d_cfg; -extern const struct iwl_cfg iwl135_bgn_cfg; -#endif /* CONFIG_IWLDVM */ -#if IS_ENABLED(CONFIG_IWLMVM) -extern const struct iwl_cfg iwl7260_2ac_cfg; -extern const struct iwl_cfg iwl7260_2ac_cfg_high_temp; -extern const struct iwl_cfg iwl7260_2n_cfg; -extern const struct iwl_cfg iwl7260_n_cfg; -extern const struct iwl_cfg iwl3160_2ac_cfg; -extern const struct iwl_cfg iwl3160_2n_cfg; -extern const struct iwl_cfg iwl3160_n_cfg; -extern const struct iwl_cfg iwl3165_2ac_cfg; -extern const struct iwl_cfg iwl7265_2ac_cfg; -extern const struct iwl_cfg iwl7265_2n_cfg; -extern const struct iwl_cfg iwl7265_n_cfg; -extern const struct iwl_cfg iwl7265d_2ac_cfg; -extern const struct iwl_cfg iwl7265d_2n_cfg; -extern const struct iwl_cfg iwl7265d_n_cfg; -extern const struct iwl_cfg iwl8260_2n_cfg; -extern const struct iwl_cfg iwl8260_2ac_cfg; -extern const struct iwl_cfg iwl4165_2ac_cfg; -extern const struct iwl_cfg iwl8260_2ac_sdio_cfg; -extern const struct iwl_cfg iwl4165_2ac_sdio_cfg; -#endif /* CONFIG_IWLMVM */ - -#endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h deleted file mode 100644 index 543abeaffcf0..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ /dev/null @@ -1,552 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __iwl_csr_h__ -#define __iwl_csr_h__ -/* - * CSR (control and status registers) - * - * CSR registers are mapped directly into PCI bus space, and are accessible - * whenever platform supplies power to device, even when device is in - * low power states due to driver-invoked device resets - * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes. - * - * Use iwl_write32() and iwl_read32() family to access these registers; - * these provide simple PCI bus access, without waking up the MAC. - * Do not use iwl_write_direct32() family for these registers; - * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ. - * The MAC (uCode processor, etc.) does not need to be powered up for accessing - * the CSR registers. - * - * NOTE: Device does need to be awake in order to read this memory - * via CSR_EEPROM and CSR_OTP registers - */ -#define CSR_BASE (0x000) - -#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */ -#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */ -#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */ -#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */ -#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/ -#define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */ -#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/ -#define CSR_GP_CNTRL (CSR_BASE+0x024) - -/* 2nd byte of CSR_INT_COALESCING, not accessible via iwl_write32()! */ -#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005) - -/* - * Hardware revision info - * Bit fields: - * 31-16: Reserved - * 15-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions - * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D - * 1-0: "Dash" (-) value, as in A-1, etc. - */ -#define CSR_HW_REV (CSR_BASE+0x028) - -/* - * EEPROM and OTP (one-time-programmable) memory reads - * - * NOTE: Device must be awake, initialized via apm_ops.init(), - * in order to read. - */ -#define CSR_EEPROM_REG (CSR_BASE+0x02c) -#define CSR_EEPROM_GP (CSR_BASE+0x030) -#define CSR_OTP_GP_REG (CSR_BASE+0x034) - -#define CSR_GIO_REG (CSR_BASE+0x03C) -#define CSR_GP_UCODE_REG (CSR_BASE+0x048) -#define CSR_GP_DRIVER_REG (CSR_BASE+0x050) - -/* - * UCODE-DRIVER GP (general purpose) mailbox registers. - * SET/CLR registers set/clear bit(s) if "1" is written. - */ -#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054) -#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058) -#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c) -#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060) - -#define CSR_MBOX_SET_REG (CSR_BASE + 0x88) - -#define CSR_LED_REG (CSR_BASE+0x094) -#define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0) -#define CSR_MAC_SHADOW_REG_CTRL (CSR_BASE+0x0A8) /* 6000 and up */ - - -/* GIO Chicken Bits (PCI Express bus link power management) */ -#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100) - -/* Analog phase-lock-loop configuration */ -#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c) - -/* - * CSR HW resources monitor registers - */ -#define CSR_MONITOR_CFG_REG (CSR_BASE+0x214) -#define CSR_MONITOR_STATUS_REG (CSR_BASE+0x228) -#define CSR_MONITOR_XTAL_RESOURCES (0x00000010) - -/* - * CSR Hardware Revision Workaround Register. Indicates hardware rev; - * "step" determines CCK backoff for txpower calculation. Used for 4965 only. - * See also CSR_HW_REV register. - * Bit fields: - * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step - * 1-0: "Dash" (-) value, as in C-1, etc. - */ -#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C) - -#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240) -#define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250) - -/* Bits for CSR_HW_IF_CONFIG_REG */ -#define CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH (0x00000003) -#define CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP (0x0000000C) -#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x000000C0) -#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100) -#define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200) -#define CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE (0x00000C00) -#define CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH (0x00003000) -#define CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP (0x0000C000) - -#define CSR_HW_IF_CONFIG_REG_POS_MAC_DASH (0) -#define CSR_HW_IF_CONFIG_REG_POS_MAC_STEP (2) -#define CSR_HW_IF_CONFIG_REG_POS_BOARD_VER (6) -#define CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE (10) -#define CSR_HW_IF_CONFIG_REG_POS_PHY_DASH (12) -#define CSR_HW_IF_CONFIG_REG_POS_PHY_STEP (14) - -#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) -#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) -#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ -#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ -#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ -#define CSR_HW_IF_CONFIG_REG_ENABLE_PME (0x10000000) -#define CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */ - -#define CSR_MBOX_SET_REG_OS_ALIVE BIT(5) - -#define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/ -#define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/ - -/* interrupt flags in INTA, set by uCode or hardware (e.g. dma), - * acknowledged (reset) by host writing "1" to flagged bits. */ -#define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */ -#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */ -#define CSR_INT_BIT_RX_PERIODIC (1 << 28) /* Rx periodic */ -#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */ -#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */ -#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */ -#define CSR_INT_BIT_PAGING (1 << 24) /* SDIO PAGING */ -#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */ -#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */ -#define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses */ -#define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */ -#define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */ - -#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ - CSR_INT_BIT_HW_ERR | \ - CSR_INT_BIT_FH_TX | \ - CSR_INT_BIT_SW_ERR | \ - CSR_INT_BIT_PAGING | \ - CSR_INT_BIT_RF_KILL | \ - CSR_INT_BIT_SW_RX | \ - CSR_INT_BIT_WAKEUP | \ - CSR_INT_BIT_ALIVE | \ - CSR_INT_BIT_RX_PERIODIC) - -/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ -#define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */ -#define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */ -#define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */ -#define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */ -#define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */ -#define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */ - -#define CSR_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ - CSR_FH_INT_BIT_RX_CHNL1 | \ - CSR_FH_INT_BIT_RX_CHNL0) - -#define CSR_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \ - CSR_FH_INT_BIT_TX_CHNL0) - -/* GPIO */ -#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200) -#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000) -#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC (0x00000200) - -/* RESET */ -#define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001) -#define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002) -#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080) -#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100) -#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200) -#define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000) - -/* - * GP (general purpose) CONTROL REGISTER - * Bit fields: - * 27: HW_RF_KILL_SW - * Indicates state of (platform's) hardware RF-Kill switch - * 26-24: POWER_SAVE_TYPE - * Indicates current power-saving mode: - * 000 -- No power saving - * 001 -- MAC power-down - * 010 -- PHY (radio) power-down - * 011 -- Error - * 10: XTAL ON request - * 9-6: SYS_CONFIG - * Indicates current system configuration, reflecting pins on chip - * as forced high/low by device circuit board. - * 4: GOING_TO_SLEEP - * Indicates MAC is entering a power-saving sleep power-down. - * Not a good time to access device-internal resources. - * 3: MAC_ACCESS_REQ - * Host sets this to request and maintain MAC wakeup, to allow host - * access to device-internal resources. Host must wait for - * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR - * device registers. - * 2: INIT_DONE - * Host sets this to put device into fully operational D0 power mode. - * Host resets this after SW_RESET to put device into low power mode. - * 0: MAC_CLOCK_READY - * Indicates MAC (ucode processor, etc.) is powered up and can run. - * Internal resources are accessible. - * NOTE: This does not indicate that the processor is actually running. - * NOTE: This does not indicate that device has completed - * init or post-power-down restore of internal SRAM memory. - * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that - * SRAM is restored and uCode is in normal operation mode. - * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and - * do not need to save/restore it. - * NOTE: After device reset, this bit remains "0" until host sets - * INIT_DONE - */ -#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001) -#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004) -#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008) -#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010) -#define CSR_GP_CNTRL_REG_FLAG_XTAL_ON (0x00000400) - -#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001) - -#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000) -#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000) -#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000) - - -/* HW REV */ -#define CSR_HW_REV_DASH(_val) (((_val) & 0x0000003) >> 0) -#define CSR_HW_REV_STEP(_val) (((_val) & 0x000000C) >> 2) - - -/** - * hw_rev values - */ -enum { - SILICON_A_STEP = 0, - SILICON_B_STEP, - SILICON_C_STEP, -}; - - -#define CSR_HW_REV_TYPE_MSK (0x000FFF0) -#define CSR_HW_REV_TYPE_5300 (0x0000020) -#define CSR_HW_REV_TYPE_5350 (0x0000030) -#define CSR_HW_REV_TYPE_5100 (0x0000050) -#define CSR_HW_REV_TYPE_5150 (0x0000040) -#define CSR_HW_REV_TYPE_1000 (0x0000060) -#define CSR_HW_REV_TYPE_6x00 (0x0000070) -#define CSR_HW_REV_TYPE_6x50 (0x0000080) -#define CSR_HW_REV_TYPE_6150 (0x0000084) -#define CSR_HW_REV_TYPE_6x05 (0x00000B0) -#define CSR_HW_REV_TYPE_6x30 CSR_HW_REV_TYPE_6x05 -#define CSR_HW_REV_TYPE_6x35 CSR_HW_REV_TYPE_6x05 -#define CSR_HW_REV_TYPE_2x30 (0x00000C0) -#define CSR_HW_REV_TYPE_2x00 (0x0000100) -#define CSR_HW_REV_TYPE_105 (0x0000110) -#define CSR_HW_REV_TYPE_135 (0x0000120) -#define CSR_HW_REV_TYPE_7265D (0x0000210) -#define CSR_HW_REV_TYPE_NONE (0x00001F0) - -/* EEPROM REG */ -#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) -#define CSR_EEPROM_REG_BIT_CMD (0x00000002) -#define CSR_EEPROM_REG_MSK_ADDR (0x0000FFFC) -#define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000) - -/* EEPROM GP */ -#define CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */ -#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) -#define CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP (0x00000000) -#define CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP (0x00000001) -#define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002) -#define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004) - -/* One-time-programmable memory general purpose reg */ -#define CSR_OTP_GP_REG_DEVICE_SELECT (0x00010000) /* 0 - EEPROM, 1 - OTP */ -#define CSR_OTP_GP_REG_OTP_ACCESS_MODE (0x00020000) /* 0 - absolute, 1 - relative */ -#define CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK (0x00100000) /* bit 20 */ -#define CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK (0x00200000) /* bit 21 */ - -/* GP REG */ -#define CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */ -#define CSR_GP_REG_NO_POWER_SAVE (0x00000000) -#define CSR_GP_REG_MAC_POWER_SAVE (0x01000000) -#define CSR_GP_REG_PHY_POWER_SAVE (0x02000000) -#define CSR_GP_REG_POWER_SAVE_ERROR (0x03000000) - - -/* CSR GIO */ -#define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002) - -/* - * UCODE-DRIVER GP (general purpose) mailbox register 1 - * Host driver and uCode write and/or read this register to communicate with - * each other. - * Bit fields: - * 4: UCODE_DISABLE - * Host sets this to request permanent halt of uCode, same as - * sending CARD_STATE command with "halt" bit set. - * 3: CT_KILL_EXIT - * Host sets this to request exit from CT_KILL state, i.e. host thinks - * device temperature is low enough to continue normal operation. - * 2: CMD_BLOCKED - * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL) - * to release uCode to clear all Tx and command queues, enter - * unassociated mode, and power down. - * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit. - * 1: SW_BIT_RFKILL - * Host sets this when issuing CARD_STATE command to request - * device sleep. - * 0: MAC_SLEEP - * uCode sets this when preparing a power-saving power-down. - * uCode resets this when power-up is complete and SRAM is sane. - * NOTE: device saves internal SRAM data to host when powering down, - * and must restore this data after powering back up. - * MAC_SLEEP is the best indication that restore is complete. - * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and - * do not need to save/restore it. - */ -#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001) -#define CSR_UCODE_SW_BIT_RFKILL (0x00000002) -#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) -#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008) -#define CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE (0x00000020) - -/* GP Driver */ -#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_MSK (0x00000003) -#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_3x3_HYB (0x00000000) -#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB (0x00000001) -#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA (0x00000002) -#define CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6 (0x00000004) -#define CSR_GP_DRIVER_REG_BIT_6050_1x2 (0x00000008) - -#define CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER (0x00000080) - -/* GIO Chicken Bits (PCI Express bus link power management) */ -#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) -#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) - -/* LED */ -#define CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF) -#define CSR_LED_REG_TURN_ON (0x60) -#define CSR_LED_REG_TURN_OFF (0x20) - -/* ANA_PLL */ -#define CSR50_ANA_PLL_CFG_VAL (0x00880300) - -/* HPET MEM debug */ -#define CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000) - -/* DRAM INT TABLE */ -#define CSR_DRAM_INT_TBL_ENABLE (1 << 31) -#define CSR_DRAM_INIT_TBL_WRITE_POINTER (1 << 28) -#define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) - -/* - * SHR target access (Shared block memory space) - * - * Shared internal registers can be accessed directly from PCI bus through SHR - * arbiter without need for the MAC HW to be powered up. This is possible due to - * indirect read/write via HEEP_CTRL_WRD_PCIEX_CTRL (0xEC) and - * HEEP_CTRL_WRD_PCIEX_DATA (0xF4) registers. - * - * Use iwl_write32()/iwl_read32() family to access these registers. The MAC HW - * need not be powered up so no "grab inc access" is required. - */ - -/* - * Registers for accessing shared registers (e.g. SHR_APMG_GP1, - * SHR_APMG_XTAL_CFG). For example, to read from SHR_APMG_GP1 register (0x1DC), - * first, write to the control register: - * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register) - * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 2 (read access) - * second, read from the data register HEEP_CTRL_WRD_PCIEX_DATA[31:0]. - * - * To write the register, first, write to the data register - * HEEP_CTRL_WRD_PCIEX_DATA[31:0] and then: - * HEEP_CTRL_WRD_PCIEX_CTRL[15:0] = 0x1DC (offset of the SHR_APMG_GP1 register) - * HEEP_CTRL_WRD_PCIEX_CTRL[29:28] = 3 (write access) - */ -#define HEEP_CTRL_WRD_PCIEX_CTRL_REG (CSR_BASE+0x0ec) -#define HEEP_CTRL_WRD_PCIEX_DATA_REG (CSR_BASE+0x0f4) - -/* - * HBUS (Host-side Bus) - * - * HBUS registers are mapped directly into PCI bus space, but are used - * to indirectly access device's internal memory or registers that - * may be powered-down. - * - * Use iwl_write_direct32()/iwl_read_direct32() family for these registers; - * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ - * to make sure the MAC (uCode processor, etc.) is powered up for accessing - * internal resources. - * - * Do not use iwl_write32()/iwl_read32() family to access these registers; - * these provide only simple PCI bus access, without waking up the MAC. - */ -#define HBUS_BASE (0x400) - -/* - * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM - * structures, error log, event log, verifying uCode load). - * First write to address register, then read from or write to data register - * to complete the job. Once the address register is set up, accesses to - * data registers auto-increment the address by one dword. - * Bit usage for address registers (read or write): - * 0-31: memory address within device - */ -#define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c) -#define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010) -#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018) -#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c) - -/* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */ -#define HBUS_TARG_MBX_C (HBUS_BASE+0x030) -#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004) - -/* - * Registers for accessing device's internal peripheral registers - * (e.g. SCD, BSM, etc.). First write to address register, - * then read from or write to data register to complete the job. - * Bit usage for address registers (read or write): - * 0-15: register address (offset) within device - * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword) - */ -#define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044) -#define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048) -#define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c) -#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050) - -/* Used to enable DBGM */ -#define HBUS_TARG_TEST_REG (HBUS_BASE+0x05c) - -/* - * Per-Tx-queue write pointer (index, really!) - * Indicates index to next TFD that driver will fill (1 past latest filled). - * Bit usage: - * 0-7: queue write index - * 11-8: queue selector - */ -#define HBUS_TARG_WRPTR (HBUS_BASE+0x060) - -/********************************************************** - * CSR values - **********************************************************/ - /* - * host interrupt timeout value - * used with setting interrupt coalescing timer - * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit - * - * default interrupt coalescing timer is 64 x 32 = 2048 usecs - */ -#define IWL_HOST_INT_TIMEOUT_MAX (0xFF) -#define IWL_HOST_INT_TIMEOUT_DEF (0x40) -#define IWL_HOST_INT_TIMEOUT_MIN (0x0) -#define IWL_HOST_INT_OPER_MODE BIT(31) - -/***************************************************************************** - * 7000/3000 series SHR DTS addresses * - *****************************************************************************/ - -/* Diode Results Register Structure: */ -enum dtd_diode_reg { - DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */ - DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */ - DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */ - DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */ - DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */ - DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */ -/* Those are the masks INSIDE the flags bit-field: */ - DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0, - DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */ - DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7, - DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */ -}; - -#endif /* !__iwl_csr_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.c b/drivers/net/wireless/iwlwifi/iwl-debug.c deleted file mode 100644 index 09feff4fa226..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-debug.c +++ /dev/null @@ -1,136 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include -#include -#include "iwl-drv.h" -#include "iwl-debug.h" -#include "iwl-devtrace.h" - -#define __iwl_fn(fn) \ -void __iwl_ ##fn(struct device *dev, const char *fmt, ...) \ -{ \ - struct va_format vaf = { \ - .fmt = fmt, \ - }; \ - va_list args; \ - \ - va_start(args, fmt); \ - vaf.va = &args; \ - dev_ ##fn(dev, "%pV", &vaf); \ - trace_iwlwifi_ ##fn(&vaf); \ - va_end(args); \ -} - -__iwl_fn(warn) -IWL_EXPORT_SYMBOL(__iwl_warn); -__iwl_fn(info) -IWL_EXPORT_SYMBOL(__iwl_info); -__iwl_fn(crit) -IWL_EXPORT_SYMBOL(__iwl_crit); - -void __iwl_err(struct device *dev, bool rfkill_prefix, bool trace_only, - const char *fmt, ...) -{ - struct va_format vaf = { - .fmt = fmt, - }; - va_list args; - - va_start(args, fmt); - vaf.va = &args; - if (!trace_only) { - if (rfkill_prefix) - dev_err(dev, "(RFKILL) %pV", &vaf); - else - dev_err(dev, "%pV", &vaf); - } - trace_iwlwifi_err(&vaf); - va_end(args); -} -IWL_EXPORT_SYMBOL(__iwl_err); - -#if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING) -void __iwl_dbg(struct device *dev, - u32 level, bool limit, const char *function, - const char *fmt, ...) -{ - struct va_format vaf = { - .fmt = fmt, - }; - va_list args; - - va_start(args, fmt); - vaf.va = &args; -#ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_have_debug_level(level) && - (!limit || net_ratelimit())) - dev_printk(KERN_DEBUG, dev, "%c %s %pV", - in_interrupt() ? 'I' : 'U', function, &vaf); -#endif - trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf); - va_end(args); -} -IWL_EXPORT_SYMBOL(__iwl_dbg); -#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h deleted file mode 100644 index 9bb36d79c2bd..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ /dev/null @@ -1,225 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __iwl_debug_h__ -#define __iwl_debug_h__ - -#include "iwl-modparams.h" - - -static inline bool iwl_have_debug_level(u32 level) -{ -#ifdef CONFIG_IWLWIFI_DEBUG - return iwlwifi_mod_params.debug_level & level; -#else - return false; -#endif -} - -void __iwl_err(struct device *dev, bool rfkill_prefix, bool only_trace, - const char *fmt, ...) __printf(4, 5); -void __iwl_warn(struct device *dev, const char *fmt, ...) __printf(2, 3); -void __iwl_info(struct device *dev, const char *fmt, ...) __printf(2, 3); -void __iwl_crit(struct device *dev, const char *fmt, ...) __printf(2, 3); - -/* not all compilers can evaluate strlen() at compile time, so use sizeof() */ -#define CHECK_FOR_NEWLINE(f) BUILD_BUG_ON(f[sizeof(f) - 2] != '\n') - -/* No matter what is m (priv, bus, trans), this will work */ -#define IWL_ERR_DEV(d, f, a...) \ - do { \ - CHECK_FOR_NEWLINE(f); \ - __iwl_err((d), false, false, f, ## a); \ - } while (0) -#define IWL_ERR(m, f, a...) \ - IWL_ERR_DEV((m)->dev, f, ## a) -#define IWL_WARN(m, f, a...) \ - do { \ - CHECK_FOR_NEWLINE(f); \ - __iwl_warn((m)->dev, f, ## a); \ - } while (0) -#define IWL_INFO(m, f, a...) \ - do { \ - CHECK_FOR_NEWLINE(f); \ - __iwl_info((m)->dev, f, ## a); \ - } while (0) -#define IWL_CRIT(m, f, a...) \ - do { \ - CHECK_FOR_NEWLINE(f); \ - __iwl_crit((m)->dev, f, ## a); \ - } while (0) - -#if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING) -void __iwl_dbg(struct device *dev, - u32 level, bool limit, const char *function, - const char *fmt, ...) __printf(5, 6); -#else -__printf(5, 6) static inline void -__iwl_dbg(struct device *dev, - u32 level, bool limit, const char *function, - const char *fmt, ...) -{} -#endif - -#define iwl_print_hex_error(m, p, len) \ -do { \ - print_hex_dump(KERN_ERR, "iwl data: ", \ - DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ -} while (0) - -#define __IWL_DEBUG_DEV(dev, level, limit, fmt, args...) \ - do { \ - CHECK_FOR_NEWLINE(fmt); \ - __iwl_dbg(dev, level, limit, __func__, fmt, ##args); \ - } while (0) -#define IWL_DEBUG(m, level, fmt, args...) \ - __IWL_DEBUG_DEV((m)->dev, level, false, fmt, ##args) -#define IWL_DEBUG_DEV(dev, level, fmt, args...) \ - __IWL_DEBUG_DEV(dev, level, false, fmt, ##args) -#define IWL_DEBUG_LIMIT(m, level, fmt, args...) \ - __IWL_DEBUG_DEV((m)->dev, level, true, fmt, ##args) - -#ifdef CONFIG_IWLWIFI_DEBUG -#define iwl_print_hex_dump(m, level, p, len) \ -do { \ - if (iwl_have_debug_level(level)) \ - print_hex_dump(KERN_DEBUG, "iwl data: ", \ - DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ -} while (0) -#else -#define iwl_print_hex_dump(m, level, p, len) -#endif /* CONFIG_IWLWIFI_DEBUG */ - -/* - * To use the debug system: - * - * If you are defining a new debug classification, simply add it to the #define - * list here in the form of - * - * #define IWL_DL_xxxx VALUE - * - * where xxxx should be the name of the classification (for example, WEP). - * - * You then need to either add a IWL_xxxx_DEBUG() macro definition for your - * classification, or use IWL_DEBUG(IWL_DL_xxxx, ...) whenever you want - * to send output to that classification. - * - * The active debug levels can be accessed via files - * - * /sys/module/iwlwifi/parameters/debug - * when CONFIG_IWLWIFI_DEBUG=y. - * - * /sys/kernel/debug/phy0/iwlwifi/debug/debug_level - * when CONFIG_IWLWIFI_DEBUGFS=y. - * - */ - -/* 0x0000000F - 0x00000001 */ -#define IWL_DL_INFO 0x00000001 -#define IWL_DL_MAC80211 0x00000002 -#define IWL_DL_HCMD 0x00000004 -#define IWL_DL_TDLS 0x00000008 -/* 0x000000F0 - 0x00000010 */ -#define IWL_DL_QUOTA 0x00000010 -#define IWL_DL_TE 0x00000020 -#define IWL_DL_EEPROM 0x00000040 -#define IWL_DL_RADIO 0x00000080 -/* 0x00000F00 - 0x00000100 */ -#define IWL_DL_POWER 0x00000100 -#define IWL_DL_TEMP 0x00000200 -#define IWL_DL_RPM 0x00000400 -#define IWL_DL_SCAN 0x00000800 -/* 0x0000F000 - 0x00001000 */ -#define IWL_DL_ASSOC 0x00001000 -#define IWL_DL_DROP 0x00002000 -#define IWL_DL_LAR 0x00004000 -#define IWL_DL_COEX 0x00008000 -/* 0x000F0000 - 0x00010000 */ -#define IWL_DL_FW 0x00010000 -#define IWL_DL_RF_KILL 0x00020000 -#define IWL_DL_FW_ERRORS 0x00040000 -#define IWL_DL_LED 0x00080000 -/* 0x00F00000 - 0x00100000 */ -#define IWL_DL_RATE 0x00100000 -#define IWL_DL_CALIB 0x00200000 -#define IWL_DL_WEP 0x00400000 -#define IWL_DL_TX 0x00800000 -/* 0x0F000000 - 0x01000000 */ -#define IWL_DL_RX 0x01000000 -#define IWL_DL_ISR 0x02000000 -#define IWL_DL_HT 0x04000000 -#define IWL_DL_EXTERNAL 0x08000000 -/* 0xF0000000 - 0x10000000 */ -#define IWL_DL_11H 0x10000000 -#define IWL_DL_STATS 0x20000000 -#define IWL_DL_TX_REPLY 0x40000000 -#define IWL_DL_TX_QUEUES 0x80000000 - -#define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a) -#define IWL_DEBUG_TDLS(p, f, a...) IWL_DEBUG(p, IWL_DL_TDLS, f, ## a) -#define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a) -#define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a) -#define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a) -#define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a) -#define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a) -#define IWL_DEBUG_TX(p, f, a...) IWL_DEBUG(p, IWL_DL_TX, f, ## a) -#define IWL_DEBUG_ISR(p, f, a...) IWL_DEBUG(p, IWL_DL_ISR, f, ## a) -#define IWL_DEBUG_LED(p, f, a...) IWL_DEBUG(p, IWL_DL_LED, f, ## a) -#define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a) -#define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a) -#define IWL_DEBUG_QUOTA(p, f, a...) IWL_DEBUG(p, IWL_DL_QUOTA, f, ## a) -#define IWL_DEBUG_TE(p, f, a...) IWL_DEBUG(p, IWL_DL_TE, f, ## a) -#define IWL_DEBUG_EEPROM(d, f, a...) IWL_DEBUG_DEV(d, IWL_DL_EEPROM, f, ## a) -#define IWL_DEBUG_CALIB(p, f, a...) IWL_DEBUG(p, IWL_DL_CALIB, f, ## a) -#define IWL_DEBUG_FW(p, f, a...) IWL_DEBUG(p, IWL_DL_FW, f, ## a) -#define IWL_DEBUG_RF_KILL(p, f, a...) IWL_DEBUG(p, IWL_DL_RF_KILL, f, ## a) -#define IWL_DEBUG_FW_ERRORS(p, f, a...) IWL_DEBUG(p, IWL_DL_FW_ERRORS, f, ## a) -#define IWL_DEBUG_DROP(p, f, a...) IWL_DEBUG(p, IWL_DL_DROP, f, ## a) -#define IWL_DEBUG_DROP_LIMIT(p, f, a...) \ - IWL_DEBUG_LIMIT(p, IWL_DL_DROP, f, ## a) -#define IWL_DEBUG_COEX(p, f, a...) IWL_DEBUG(p, IWL_DL_COEX, f, ## a) -#define IWL_DEBUG_RATE(p, f, a...) IWL_DEBUG(p, IWL_DL_RATE, f, ## a) -#define IWL_DEBUG_RATE_LIMIT(p, f, a...) \ - IWL_DEBUG_LIMIT(p, IWL_DL_RATE, f, ## a) -#define IWL_DEBUG_ASSOC(p, f, a...) \ - IWL_DEBUG(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a) -#define IWL_DEBUG_ASSOC_LIMIT(p, f, a...) \ - IWL_DEBUG_LIMIT(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a) -#define IWL_DEBUG_HT(p, f, a...) IWL_DEBUG(p, IWL_DL_HT, f, ## a) -#define IWL_DEBUG_STATS(p, f, a...) IWL_DEBUG(p, IWL_DL_STATS, f, ## a) -#define IWL_DEBUG_STATS_LIMIT(p, f, a...) \ - IWL_DEBUG_LIMIT(p, IWL_DL_STATS, f, ## a) -#define IWL_DEBUG_TX_REPLY(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_REPLY, f, ## a) -#define IWL_DEBUG_TX_QUEUES(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_QUEUES, f, ## a) -#define IWL_DEBUG_RADIO(p, f, a...) IWL_DEBUG(p, IWL_DL_RADIO, f, ## a) -#define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a) -#define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a) -#define IWL_DEBUG_RPM(p, f, a...) IWL_DEBUG(p, IWL_DL_RPM, f, ## a) -#define IWL_DEBUG_LAR(p, f, a...) IWL_DEBUG(p, IWL_DL_LAR, f, ## a) - -#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h deleted file mode 100644 index 71a78cede9b0..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h +++ /dev/null @@ -1,80 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#if !defined(__IWLWIFI_DEVICE_TRACE_DATA) || defined(TRACE_HEADER_MULTI_READ) -#define __IWLWIFI_DEVICE_TRACE_DATA - -#include - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM iwlwifi_data - -TRACE_EVENT(iwlwifi_dev_tx_data, - TP_PROTO(const struct device *dev, - struct sk_buff *skb, - u8 hdr_len, size_t data_len), - TP_ARGS(dev, skb, hdr_len, data_len), - TP_STRUCT__entry( - DEV_ENTRY - - __dynamic_array(u8, data, iwl_trace_data(skb) ? data_len : 0) - ), - TP_fast_assign( - DEV_ASSIGN; - if (iwl_trace_data(skb)) - skb_copy_bits(skb, hdr_len, - __get_dynamic_array(data), data_len); - ), - TP_printk("[%s] TX frame data", __get_str(dev)) -); - -TRACE_EVENT(iwlwifi_dev_rx_data, - TP_PROTO(const struct device *dev, - const struct iwl_trans *trans, - void *rxbuf, size_t len), - TP_ARGS(dev, trans, rxbuf, len), - TP_STRUCT__entry( - DEV_ENTRY - - __dynamic_array(u8, data, - len - iwl_rx_trace_len(trans, rxbuf, len)) - ), - TP_fast_assign( - size_t offs = iwl_rx_trace_len(trans, rxbuf, len); - DEV_ASSIGN; - if (offs < len) - memcpy(__get_dynamic_array(data), - ((u8 *)rxbuf) + offs, len - offs); - ), - TP_printk("[%s] RX frame data", __get_str(dev)) -); -#endif /* __IWLWIFI_DEVICE_TRACE_DATA */ - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE iwl-devtrace-data -#include diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h deleted file mode 100644 index f62c54485852..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h +++ /dev/null @@ -1,155 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#if !defined(__IWLWIFI_DEVICE_TRACE_IO) || defined(TRACE_HEADER_MULTI_READ) -#define __IWLWIFI_DEVICE_TRACE_IO - -#include - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM iwlwifi_io - -TRACE_EVENT(iwlwifi_dev_ioread32, - TP_PROTO(const struct device *dev, u32 offs, u32 val), - TP_ARGS(dev, offs, val), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, offs) - __field(u32, val) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->offs = offs; - __entry->val = val; - ), - TP_printk("[%s] read io[%#x] = %#x", - __get_str(dev), __entry->offs, __entry->val) -); - -TRACE_EVENT(iwlwifi_dev_iowrite8, - TP_PROTO(const struct device *dev, u32 offs, u8 val), - TP_ARGS(dev, offs, val), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, offs) - __field(u8, val) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->offs = offs; - __entry->val = val; - ), - TP_printk("[%s] write io[%#x] = %#x)", - __get_str(dev), __entry->offs, __entry->val) -); - -TRACE_EVENT(iwlwifi_dev_iowrite32, - TP_PROTO(const struct device *dev, u32 offs, u32 val), - TP_ARGS(dev, offs, val), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, offs) - __field(u32, val) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->offs = offs; - __entry->val = val; - ), - TP_printk("[%s] write io[%#x] = %#x)", - __get_str(dev), __entry->offs, __entry->val) -); - -TRACE_EVENT(iwlwifi_dev_iowrite_prph32, - TP_PROTO(const struct device *dev, u32 offs, u32 val), - TP_ARGS(dev, offs, val), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, offs) - __field(u32, val) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->offs = offs; - __entry->val = val; - ), - TP_printk("[%s] write PRPH[%#x] = %#x)", - __get_str(dev), __entry->offs, __entry->val) -); - -TRACE_EVENT(iwlwifi_dev_ioread_prph32, - TP_PROTO(const struct device *dev, u32 offs, u32 val), - TP_ARGS(dev, offs, val), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, offs) - __field(u32, val) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->offs = offs; - __entry->val = val; - ), - TP_printk("[%s] read PRPH[%#x] = %#x", - __get_str(dev), __entry->offs, __entry->val) -); - -TRACE_EVENT(iwlwifi_dev_irq, - TP_PROTO(const struct device *dev), - TP_ARGS(dev), - TP_STRUCT__entry( - DEV_ENTRY - ), - TP_fast_assign( - DEV_ASSIGN; - ), - /* TP_printk("") doesn't compile */ - TP_printk("%d", 0) -); - -TRACE_EVENT(iwlwifi_dev_ict_read, - TP_PROTO(const struct device *dev, u32 index, u32 value), - TP_ARGS(dev, index, value), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, index) - __field(u32, value) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->index = index; - __entry->value = value; - ), - TP_printk("[%s] read ict[%d] = %#.8x", - __get_str(dev), __entry->index, __entry->value) -); -#endif /* __IWLWIFI_DEVICE_TRACE_IO */ - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE iwl-devtrace-io -#include diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h deleted file mode 100644 index eb4b99a1c8cd..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h +++ /dev/null @@ -1,209 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#if !defined(__IWLWIFI_DEVICE_TRACE_IWLWIFI) || defined(TRACE_HEADER_MULTI_READ) -#define __IWLWIFI_DEVICE_TRACE_IWLWIFI - -#include - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM iwlwifi - -TRACE_EVENT(iwlwifi_dev_hcmd, - TP_PROTO(const struct device *dev, - struct iwl_host_cmd *cmd, u16 total_size, - struct iwl_cmd_header_wide *hdr), - TP_ARGS(dev, cmd, total_size, hdr), - TP_STRUCT__entry( - DEV_ENTRY - __dynamic_array(u8, hcmd, total_size) - __field(u32, flags) - ), - TP_fast_assign( - int i, offset = sizeof(struct iwl_cmd_header); - - if (hdr->group_id) - offset = sizeof(struct iwl_cmd_header_wide); - - DEV_ASSIGN; - __entry->flags = cmd->flags; - memcpy(__get_dynamic_array(hcmd), hdr, offset); - - for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { - if (!cmd->len[i]) - continue; - memcpy((u8 *)__get_dynamic_array(hcmd) + offset, - cmd->data[i], cmd->len[i]); - offset += cmd->len[i]; - } - ), - TP_printk("[%s] hcmd %#.2x.%#.2x (%ssync)", - __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[1], - ((u8 *)__get_dynamic_array(hcmd))[0], - __entry->flags & CMD_ASYNC ? "a" : "") -); - -TRACE_EVENT(iwlwifi_dev_rx, - TP_PROTO(const struct device *dev, const struct iwl_trans *trans, - struct iwl_rx_packet *pkt, size_t len), - TP_ARGS(dev, trans, pkt, len), - TP_STRUCT__entry( - DEV_ENTRY - __field(u8, cmd) - __dynamic_array(u8, rxbuf, iwl_rx_trace_len(trans, pkt, len)) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->cmd = pkt->hdr.cmd; - memcpy(__get_dynamic_array(rxbuf), pkt, - iwl_rx_trace_len(trans, pkt, len)); - ), - TP_printk("[%s] RX cmd %#.2x", - __get_str(dev), __entry->cmd) -); - -TRACE_EVENT(iwlwifi_dev_tx, - TP_PROTO(const struct device *dev, struct sk_buff *skb, - void *tfd, size_t tfdlen, - void *buf0, size_t buf0_len, - void *buf1, size_t buf1_len), - TP_ARGS(dev, skb, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len), - TP_STRUCT__entry( - DEV_ENTRY - - __field(size_t, framelen) - __dynamic_array(u8, tfd, tfdlen) - - /* - * Do not insert between or below these items, - * we want to keep the frame together (except - * for the possible padding). - */ - __dynamic_array(u8, buf0, buf0_len) - __dynamic_array(u8, buf1, iwl_trace_data(skb) ? 0 : buf1_len) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->framelen = buf0_len + buf1_len; - memcpy(__get_dynamic_array(tfd), tfd, tfdlen); - memcpy(__get_dynamic_array(buf0), buf0, buf0_len); - if (!iwl_trace_data(skb)) - memcpy(__get_dynamic_array(buf1), buf1, buf1_len); - ), - TP_printk("[%s] TX %.2x (%zu bytes)", - __get_str(dev), ((u8 *)__get_dynamic_array(buf0))[0], - __entry->framelen) -); - -TRACE_EVENT(iwlwifi_dev_ucode_error, - TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low, - u32 data1, u32 data2, u32 line, u32 blink1, - u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time, - u32 gp1, u32 gp2, u32 gp3, u32 major, u32 minor, u32 hw_ver, - u32 brd_ver), - TP_ARGS(dev, desc, tsf_low, data1, data2, line, - blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2, - gp3, major, minor, hw_ver, brd_ver), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, desc) - __field(u32, tsf_low) - __field(u32, data1) - __field(u32, data2) - __field(u32, line) - __field(u32, blink1) - __field(u32, blink2) - __field(u32, ilink1) - __field(u32, ilink2) - __field(u32, bcon_time) - __field(u32, gp1) - __field(u32, gp2) - __field(u32, gp3) - __field(u32, major) - __field(u32, minor) - __field(u32, hw_ver) - __field(u32, brd_ver) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->desc = desc; - __entry->tsf_low = tsf_low; - __entry->data1 = data1; - __entry->data2 = data2; - __entry->line = line; - __entry->blink1 = blink1; - __entry->blink2 = blink2; - __entry->ilink1 = ilink1; - __entry->ilink2 = ilink2; - __entry->bcon_time = bcon_time; - __entry->gp1 = gp1; - __entry->gp2 = gp2; - __entry->gp3 = gp3; - __entry->major = major; - __entry->minor = minor; - __entry->hw_ver = hw_ver; - __entry->brd_ver = brd_ver; - ), - TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, " - "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X " - "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X major 0x%08X " - "minor 0x%08X hw 0x%08X brd 0x%08X", - __get_str(dev), __entry->desc, __entry->tsf_low, - __entry->data1, - __entry->data2, __entry->line, __entry->blink1, - __entry->blink2, __entry->ilink1, __entry->ilink2, - __entry->bcon_time, __entry->gp1, __entry->gp2, - __entry->gp3, __entry->major, __entry->minor, - __entry->hw_ver, __entry->brd_ver) -); - -TRACE_EVENT(iwlwifi_dev_ucode_event, - TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev), - TP_ARGS(dev, time, data, ev), - TP_STRUCT__entry( - DEV_ENTRY - - __field(u32, time) - __field(u32, data) - __field(u32, ev) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->time = time; - __entry->data = data; - __entry->ev = ev; - ), - TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u", - __get_str(dev), __entry->time, __entry->data, __entry->ev) -); -#endif /* __IWLWIFI_DEVICE_TRACE_IWLWIFI */ - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE iwl-devtrace-iwlwifi -#include diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h deleted file mode 100644 index a3b3c2465f89..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h +++ /dev/null @@ -1,97 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#if !defined(__IWLWIFI_DEVICE_TRACE_MSG) || defined(TRACE_HEADER_MULTI_READ) -#define __IWLWIFI_DEVICE_TRACE_MSG - -#include - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM iwlwifi_msg - -#define MAX_MSG_LEN 110 - -DECLARE_EVENT_CLASS(iwlwifi_msg_event, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf), - TP_STRUCT__entry( - __dynamic_array(char, msg, MAX_MSG_LEN) - ), - TP_fast_assign( - WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), - MAX_MSG_LEN, vaf->fmt, - *vaf->va) >= MAX_MSG_LEN); - ), - TP_printk("%s", __get_str(msg)) -); - -DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_err, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_warn, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_info, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_crit, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -TRACE_EVENT(iwlwifi_dbg, - TP_PROTO(u32 level, bool in_interrupt, const char *function, - struct va_format *vaf), - TP_ARGS(level, in_interrupt, function, vaf), - TP_STRUCT__entry( - __field(u32, level) - __field(u8, in_interrupt) - __string(function, function) - __dynamic_array(char, msg, MAX_MSG_LEN) - ), - TP_fast_assign( - __entry->level = level; - __entry->in_interrupt = in_interrupt; - __assign_str(function, function); - WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), - MAX_MSG_LEN, vaf->fmt, - *vaf->va) >= MAX_MSG_LEN); - ), - TP_printk("%s", __get_str(msg)) -); -#endif /* __IWLWIFI_DEVICE_TRACE_MSG */ - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE iwl-devtrace-msg -#include diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h deleted file mode 100644 index 10839fae9cd9..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h +++ /dev/null @@ -1,81 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#if !defined(__IWLWIFI_DEVICE_TRACE_UCODE) || defined(TRACE_HEADER_MULTI_READ) -#define __IWLWIFI_DEVICE_TRACE_UCODE - -#include - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM iwlwifi_ucode - -TRACE_EVENT(iwlwifi_dev_ucode_cont_event, - TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev), - TP_ARGS(dev, time, data, ev), - TP_STRUCT__entry( - DEV_ENTRY - - __field(u32, time) - __field(u32, data) - __field(u32, ev) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->time = time; - __entry->data = data; - __entry->ev = ev; - ), - TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u", - __get_str(dev), __entry->time, __entry->data, __entry->ev) -); - -TRACE_EVENT(iwlwifi_dev_ucode_wrap_event, - TP_PROTO(const struct device *dev, u32 wraps, u32 n_entry, u32 p_entry), - TP_ARGS(dev, wraps, n_entry, p_entry), - TP_STRUCT__entry( - DEV_ENTRY - - __field(u32, wraps) - __field(u32, n_entry) - __field(u32, p_entry) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->wraps = wraps; - __entry->n_entry = n_entry; - __entry->p_entry = p_entry; - ), - TP_printk("[%s] wraps=#%02d n=0x%X p=0x%X", - __get_str(dev), __entry->wraps, __entry->n_entry, - __entry->p_entry) -); -#endif /* __IWLWIFI_DEVICE_TRACE_UCODE */ - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE iwl-devtrace-ucode -#include diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/iwlwifi/iwl-devtrace.c deleted file mode 100644 index 90987d6f348e..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.c +++ /dev/null @@ -1,43 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include - -/* sparse doesn't like tracepoint macros */ -#ifndef __CHECKER__ -#include "iwl-trans.h" - -#define CREATE_TRACE_POINTS -#include "iwl-devtrace.h" - -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite8); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ioread32); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_cont_event); -EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_wrap_event); -#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h deleted file mode 100644 index b87acd6a229b..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ /dev/null @@ -1,89 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __IWLWIFI_DEVICE_TRACE -#include -#include -#include -#include "iwl-trans.h" -#if !defined(__IWLWIFI_DEVICE_TRACE) -static inline bool iwl_trace_data(struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (void *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - if (!ieee80211_is_data(hdr->frame_control)) - return false; - return !(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO); -} - -static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans, - void *rxbuf, size_t len) -{ - struct iwl_cmd_header *cmd = (void *)((u8 *)rxbuf + sizeof(__le32)); - struct ieee80211_hdr *hdr; - - if (cmd->cmd != trans->rx_mpdu_cmd) - return len; - - hdr = (void *)((u8 *)cmd + sizeof(struct iwl_cmd_header) + - trans->rx_mpdu_cmd_hdr_size); - if (!ieee80211_is_data(hdr->frame_control)) - return len; - /* maybe try to identify EAPOL frames? */ - return sizeof(__le32) + sizeof(*cmd) + trans->rx_mpdu_cmd_hdr_size + - ieee80211_hdrlen(hdr->frame_control); -} -#endif - -#define __IWLWIFI_DEVICE_TRACE - -#include -#include -#include "iwl-trans.h" - - -#if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__) -#undef TRACE_EVENT -#define TRACE_EVENT(name, proto, ...) \ -static inline void trace_ ## name(proto) {} -#undef DECLARE_EVENT_CLASS -#define DECLARE_EVENT_CLASS(...) -#undef DEFINE_EVENT -#define DEFINE_EVENT(evt_class, name, proto, ...) \ -static inline void trace_ ## name(proto) {} -#endif - -#define DEV_ENTRY __string(dev, dev_name(dev)) -#define DEV_ASSIGN __assign_str(dev, dev_name(dev)) - -#include "iwl-devtrace-io.h" -#include "iwl-devtrace-ucode.h" -#include "iwl-devtrace-msg.h" -#include "iwl-devtrace-data.h" -#include "iwl-devtrace-iwlwifi.h" - -#endif /* __IWLWIFI_DEVICE_TRACE */ diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c deleted file mode 100644 index 463cadfbfccb..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ /dev/null @@ -1,1706 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include -#include -#include -#include -#include - -#include "iwl-drv.h" -#include "iwl-csr.h" -#include "iwl-debug.h" -#include "iwl-trans.h" -#include "iwl-op-mode.h" -#include "iwl-agn-hw.h" -#include "iwl-fw.h" -#include "iwl-config.h" -#include "iwl-modparams.h" - -/****************************************************************************** - * - * module boiler plate - * - ******************************************************************************/ - -#define DRV_DESCRIPTION "Intel(R) Wireless WiFi driver for Linux" -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); -MODULE_LICENSE("GPL"); - -#ifdef CONFIG_IWLWIFI_DEBUGFS -static struct dentry *iwl_dbgfs_root; -#endif - -/** - * struct iwl_drv - drv common data - * @list: list of drv structures using this opmode - * @fw: the iwl_fw structure - * @op_mode: the running op_mode - * @trans: transport layer - * @dev: for debug prints only - * @cfg: configuration struct - * @fw_index: firmware revision to try loading - * @firmware_name: composite filename of ucode file to load - * @request_firmware_complete: the firmware has been obtained from user space - */ -struct iwl_drv { - struct list_head list; - struct iwl_fw fw; - - struct iwl_op_mode *op_mode; - struct iwl_trans *trans; - struct device *dev; - const struct iwl_cfg *cfg; - - int fw_index; /* firmware we're trying to load */ - char firmware_name[32]; /* name of firmware file to load */ - - struct completion request_firmware_complete; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct dentry *dbgfs_drv; - struct dentry *dbgfs_trans; - struct dentry *dbgfs_op_mode; -#endif -}; - -enum { - DVM_OP_MODE = 0, - MVM_OP_MODE = 1, -}; - -/* Protects the table contents, i.e. the ops pointer & drv list */ -static struct mutex iwlwifi_opmode_table_mtx; -static struct iwlwifi_opmode_table { - const char *name; /* name: iwldvm, iwlmvm, etc */ - const struct iwl_op_mode_ops *ops; /* pointer to op_mode ops */ - struct list_head drv; /* list of devices using this op_mode */ -} iwlwifi_opmode_table[] = { /* ops set when driver is initialized */ - [DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL }, - [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL }, -}; - -#define IWL_DEFAULT_SCAN_CHANNELS 40 - -/* - * struct fw_sec: Just for the image parsing process. - * For the fw storage we are using struct fw_desc. - */ -struct fw_sec { - const void *data; /* the sec data */ - size_t size; /* section size */ - u32 offset; /* offset of writing in the device */ -}; - -static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc) -{ - vfree(desc->data); - desc->data = NULL; - desc->len = 0; -} - -static void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img) -{ - int i; - for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) - iwl_free_fw_desc(drv, &img->sec[i]); -} - -static void iwl_dealloc_ucode(struct iwl_drv *drv) -{ - int i; - - kfree(drv->fw.dbg_dest_tlv); - for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) - kfree(drv->fw.dbg_conf_tlv[i]); - for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) - kfree(drv->fw.dbg_trigger_tlv[i]); - - for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) - iwl_free_fw_img(drv, drv->fw.img + i); -} - -static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc, - struct fw_sec *sec) -{ - void *data; - - desc->data = NULL; - - if (!sec || !sec->size) - return -EINVAL; - - data = vmalloc(sec->size); - if (!data) - return -ENOMEM; - - desc->len = sec->size; - desc->offset = sec->offset; - memcpy(data, sec->data, desc->len); - desc->data = data; - - return 0; -} - -static void iwl_req_fw_callback(const struct firmware *ucode_raw, - void *context); - -#define UCODE_EXPERIMENTAL_INDEX 100 -#define UCODE_EXPERIMENTAL_TAG "exp" - -static int iwl_request_firmware(struct iwl_drv *drv, bool first) -{ - const char *name_pre = drv->cfg->fw_name_pre; - char tag[8]; - - if (first) { -#ifdef CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE - drv->fw_index = UCODE_EXPERIMENTAL_INDEX; - strcpy(tag, UCODE_EXPERIMENTAL_TAG); - } else if (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) { -#endif - drv->fw_index = drv->cfg->ucode_api_max; - sprintf(tag, "%d", drv->fw_index); - } else { - drv->fw_index--; - sprintf(tag, "%d", drv->fw_index); - } - - if (drv->fw_index < drv->cfg->ucode_api_min) { - IWL_ERR(drv, "no suitable firmware found!\n"); - return -ENOENT; - } - - snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode", - name_pre, tag); - - /* - * Starting 8000B - FW name format has changed. This overwrites the - * previous name and uses the new format. - */ - if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { - char rev_step = 'A' + CSR_HW_REV_STEP(drv->trans->hw_rev); - - snprintf(drv->firmware_name, sizeof(drv->firmware_name), - "%s%c-%s.ucode", name_pre, rev_step, tag); - } - - IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n", - (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) - ? "EXPERIMENTAL " : "", - drv->firmware_name); - - return request_firmware_nowait(THIS_MODULE, 1, drv->firmware_name, - drv->trans->dev, - GFP_KERNEL, drv, iwl_req_fw_callback); -} - -struct fw_img_parsing { - struct fw_sec sec[IWL_UCODE_SECTION_MAX]; - int sec_counter; -}; - -/* - * struct fw_sec_parsing: to extract fw section and it's offset from tlv - */ -struct fw_sec_parsing { - __le32 offset; - const u8 data[]; -} __packed; - -/** - * struct iwl_tlv_calib_data - parse the default calib data from TLV - * - * @ucode_type: the uCode to which the following default calib relates. - * @calib: default calibrations. - */ -struct iwl_tlv_calib_data { - __le32 ucode_type; - struct iwl_tlv_calib_ctrl calib; -} __packed; - -struct iwl_firmware_pieces { - struct fw_img_parsing img[IWL_UCODE_TYPE_MAX]; - - u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr; - u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr; - - /* FW debug data parsed for driver usage */ - struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; - struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX]; - size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX]; - struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX]; - size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX]; -}; - -/* - * These functions are just to extract uCode section data from the pieces - * structure. - */ -static struct fw_sec *get_sec(struct iwl_firmware_pieces *pieces, - enum iwl_ucode_type type, - int sec) -{ - return &pieces->img[type].sec[sec]; -} - -static void set_sec_data(struct iwl_firmware_pieces *pieces, - enum iwl_ucode_type type, - int sec, - const void *data) -{ - pieces->img[type].sec[sec].data = data; -} - -static void set_sec_size(struct iwl_firmware_pieces *pieces, - enum iwl_ucode_type type, - int sec, - size_t size) -{ - pieces->img[type].sec[sec].size = size; -} - -static size_t get_sec_size(struct iwl_firmware_pieces *pieces, - enum iwl_ucode_type type, - int sec) -{ - return pieces->img[type].sec[sec].size; -} - -static void set_sec_offset(struct iwl_firmware_pieces *pieces, - enum iwl_ucode_type type, - int sec, - u32 offset) -{ - pieces->img[type].sec[sec].offset = offset; -} - -static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len) -{ - int i, j; - struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data; - struct iwl_fw_cipher_scheme *fwcs; - struct ieee80211_cipher_scheme *cs; - u32 cipher; - - if (len < sizeof(*l) || - len < sizeof(l->size) + l->size * sizeof(l->cs[0])) - return -EINVAL; - - for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) { - fwcs = &l->cs[j]; - cipher = le32_to_cpu(fwcs->cipher); - - /* we skip schemes with zero cipher suite selector */ - if (!cipher) - continue; - - cs = &fw->cs[j++]; - cs->cipher = cipher; - cs->iftype = BIT(NL80211_IFTYPE_STATION); - cs->hdr_len = fwcs->hdr_len; - cs->pn_len = fwcs->pn_len; - cs->pn_off = fwcs->pn_off; - cs->key_idx_off = fwcs->key_idx_off; - cs->key_idx_mask = fwcs->key_idx_mask; - cs->key_idx_shift = fwcs->key_idx_shift; - cs->mic_len = fwcs->mic_len; - } - - return 0; -} - -static int iwl_store_gscan_capa(struct iwl_fw *fw, const u8 *data, - const u32 len) -{ - struct iwl_fw_gscan_capabilities *fw_capa = (void *)data; - struct iwl_gscan_capabilities *capa = &fw->gscan_capa; - - if (len < sizeof(*fw_capa)) - return -EINVAL; - - capa->max_scan_cache_size = le32_to_cpu(fw_capa->max_scan_cache_size); - capa->max_scan_buckets = le32_to_cpu(fw_capa->max_scan_buckets); - capa->max_ap_cache_per_scan = - le32_to_cpu(fw_capa->max_ap_cache_per_scan); - capa->max_rssi_sample_size = le32_to_cpu(fw_capa->max_rssi_sample_size); - capa->max_scan_reporting_threshold = - le32_to_cpu(fw_capa->max_scan_reporting_threshold); - capa->max_hotlist_aps = le32_to_cpu(fw_capa->max_hotlist_aps); - capa->max_significant_change_aps = - le32_to_cpu(fw_capa->max_significant_change_aps); - capa->max_bssid_history_entries = - le32_to_cpu(fw_capa->max_bssid_history_entries); - return 0; -} - -/* - * Gets uCode section from tlv. - */ -static int iwl_store_ucode_sec(struct iwl_firmware_pieces *pieces, - const void *data, enum iwl_ucode_type type, - int size) -{ - struct fw_img_parsing *img; - struct fw_sec *sec; - struct fw_sec_parsing *sec_parse; - - if (WARN_ON(!pieces || !data || type >= IWL_UCODE_TYPE_MAX)) - return -1; - - sec_parse = (struct fw_sec_parsing *)data; - - img = &pieces->img[type]; - sec = &img->sec[img->sec_counter]; - - sec->offset = le32_to_cpu(sec_parse->offset); - sec->data = sec_parse->data; - sec->size = size - sizeof(sec_parse->offset); - - ++img->sec_counter; - - return 0; -} - -static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data) -{ - struct iwl_tlv_calib_data *def_calib = - (struct iwl_tlv_calib_data *)data; - u32 ucode_type = le32_to_cpu(def_calib->ucode_type); - if (ucode_type >= IWL_UCODE_TYPE_MAX) { - IWL_ERR(drv, "Wrong ucode_type %u for default calibration.\n", - ucode_type); - return -EINVAL; - } - drv->fw.default_calib[ucode_type].flow_trigger = - def_calib->calib.flow_trigger; - drv->fw.default_calib[ucode_type].event_trigger = - def_calib->calib.event_trigger; - - return 0; -} - -static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, - struct iwl_ucode_capabilities *capa) -{ - const struct iwl_ucode_api *ucode_api = (void *)data; - u32 api_index = le32_to_cpu(ucode_api->api_index); - u32 api_flags = le32_to_cpu(ucode_api->api_flags); - int i; - - if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) { - IWL_ERR(drv, "api_index larger than supported by driver\n"); - /* don't return an error so we can load FW that has more bits */ - return 0; - } - - for (i = 0; i < 32; i++) { - if (api_flags & BIT(i)) - __set_bit(i + 32 * api_index, capa->_api); - } - - return 0; -} - -static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data, - struct iwl_ucode_capabilities *capa) -{ - const struct iwl_ucode_capa *ucode_capa = (void *)data; - u32 api_index = le32_to_cpu(ucode_capa->api_index); - u32 api_flags = le32_to_cpu(ucode_capa->api_capa); - int i; - - if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) { - IWL_ERR(drv, "api_index larger than supported by driver\n"); - /* don't return an error so we can load FW that has more bits */ - return 0; - } - - for (i = 0; i < 32; i++) { - if (api_flags & BIT(i)) - __set_bit(i + 32 * api_index, capa->_capa); - } - - return 0; -} - -static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv, - const struct firmware *ucode_raw, - struct iwl_firmware_pieces *pieces) -{ - struct iwl_ucode_header *ucode = (void *)ucode_raw->data; - u32 api_ver, hdr_size, build; - char buildstr[25]; - const u8 *src; - - drv->fw.ucode_ver = le32_to_cpu(ucode->ver); - api_ver = IWL_UCODE_API(drv->fw.ucode_ver); - - switch (api_ver) { - default: - hdr_size = 28; - if (ucode_raw->size < hdr_size) { - IWL_ERR(drv, "File size too small!\n"); - return -EINVAL; - } - build = le32_to_cpu(ucode->u.v2.build); - set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, - le32_to_cpu(ucode->u.v2.inst_size)); - set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, - le32_to_cpu(ucode->u.v2.data_size)); - set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, - le32_to_cpu(ucode->u.v2.init_size)); - set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, - le32_to_cpu(ucode->u.v2.init_data_size)); - src = ucode->u.v2.data; - break; - case 0: - case 1: - case 2: - hdr_size = 24; - if (ucode_raw->size < hdr_size) { - IWL_ERR(drv, "File size too small!\n"); - return -EINVAL; - } - build = 0; - set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, - le32_to_cpu(ucode->u.v1.inst_size)); - set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, - le32_to_cpu(ucode->u.v1.data_size)); - set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, - le32_to_cpu(ucode->u.v1.init_size)); - set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, - le32_to_cpu(ucode->u.v1.init_data_size)); - src = ucode->u.v1.data; - break; - } - - if (build) - sprintf(buildstr, " build %u%s", build, - (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) - ? " (EXP)" : ""); - else - buildstr[0] = '\0'; - - snprintf(drv->fw.fw_version, - sizeof(drv->fw.fw_version), - "%u.%u.%u.%u%s", - IWL_UCODE_MAJOR(drv->fw.ucode_ver), - IWL_UCODE_MINOR(drv->fw.ucode_ver), - IWL_UCODE_API(drv->fw.ucode_ver), - IWL_UCODE_SERIAL(drv->fw.ucode_ver), - buildstr); - - /* Verify size of file vs. image size info in file's header */ - - if (ucode_raw->size != hdr_size + - get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) + - get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) + - get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) + - get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)) { - - IWL_ERR(drv, - "uCode file size %d does not match expected size\n", - (int)ucode_raw->size); - return -EINVAL; - } - - - set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, src); - src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST); - set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, - IWLAGN_RTC_INST_LOWER_BOUND); - set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, src); - src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA); - set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, - IWLAGN_RTC_DATA_LOWER_BOUND); - set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, src); - src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST); - set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, - IWLAGN_RTC_INST_LOWER_BOUND); - set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, src); - src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA); - set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, - IWLAGN_RTC_DATA_LOWER_BOUND); - return 0; -} - -static int iwl_parse_tlv_firmware(struct iwl_drv *drv, - const struct firmware *ucode_raw, - struct iwl_firmware_pieces *pieces, - struct iwl_ucode_capabilities *capa) -{ - struct iwl_tlv_ucode_header *ucode = (void *)ucode_raw->data; - struct iwl_ucode_tlv *tlv; - size_t len = ucode_raw->size; - const u8 *data; - u32 tlv_len; - u32 usniffer_img; - enum iwl_ucode_tlv_type tlv_type; - const u8 *tlv_data; - char buildstr[25]; - u32 build, paging_mem_size; - int num_of_cpus; - bool usniffer_images = false; - bool usniffer_req = false; - bool gscan_capa = false; - - if (len < sizeof(*ucode)) { - IWL_ERR(drv, "uCode has invalid length: %zd\n", len); - return -EINVAL; - } - - if (ucode->magic != cpu_to_le32(IWL_TLV_UCODE_MAGIC)) { - IWL_ERR(drv, "invalid uCode magic: 0X%x\n", - le32_to_cpu(ucode->magic)); - return -EINVAL; - } - - drv->fw.ucode_ver = le32_to_cpu(ucode->ver); - memcpy(drv->fw.human_readable, ucode->human_readable, - sizeof(drv->fw.human_readable)); - build = le32_to_cpu(ucode->build); - - if (build) - sprintf(buildstr, " build %u%s", build, - (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) - ? " (EXP)" : ""); - else - buildstr[0] = '\0'; - - snprintf(drv->fw.fw_version, - sizeof(drv->fw.fw_version), - "%u.%u.%u.%u%s", - IWL_UCODE_MAJOR(drv->fw.ucode_ver), - IWL_UCODE_MINOR(drv->fw.ucode_ver), - IWL_UCODE_API(drv->fw.ucode_ver), - IWL_UCODE_SERIAL(drv->fw.ucode_ver), - buildstr); - - data = ucode->data; - - len -= sizeof(*ucode); - - while (len >= sizeof(*tlv)) { - len -= sizeof(*tlv); - tlv = (void *)data; - - tlv_len = le32_to_cpu(tlv->length); - tlv_type = le32_to_cpu(tlv->type); - tlv_data = tlv->data; - - if (len < tlv_len) { - IWL_ERR(drv, "invalid TLV len: %zd/%u\n", - len, tlv_len); - return -EINVAL; - } - len -= ALIGN(tlv_len, 4); - data += sizeof(*tlv) + ALIGN(tlv_len, 4); - - switch (tlv_type) { - case IWL_UCODE_TLV_INST: - set_sec_data(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_INST, tlv_data); - set_sec_size(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_INST, tlv_len); - set_sec_offset(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_INST, - IWLAGN_RTC_INST_LOWER_BOUND); - break; - case IWL_UCODE_TLV_DATA: - set_sec_data(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_DATA, tlv_data); - set_sec_size(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_DATA, tlv_len); - set_sec_offset(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_DATA, - IWLAGN_RTC_DATA_LOWER_BOUND); - break; - case IWL_UCODE_TLV_INIT: - set_sec_data(pieces, IWL_UCODE_INIT, - IWL_UCODE_SECTION_INST, tlv_data); - set_sec_size(pieces, IWL_UCODE_INIT, - IWL_UCODE_SECTION_INST, tlv_len); - set_sec_offset(pieces, IWL_UCODE_INIT, - IWL_UCODE_SECTION_INST, - IWLAGN_RTC_INST_LOWER_BOUND); - break; - case IWL_UCODE_TLV_INIT_DATA: - set_sec_data(pieces, IWL_UCODE_INIT, - IWL_UCODE_SECTION_DATA, tlv_data); - set_sec_size(pieces, IWL_UCODE_INIT, - IWL_UCODE_SECTION_DATA, tlv_len); - set_sec_offset(pieces, IWL_UCODE_INIT, - IWL_UCODE_SECTION_DATA, - IWLAGN_RTC_DATA_LOWER_BOUND); - break; - case IWL_UCODE_TLV_BOOT: - IWL_ERR(drv, "Found unexpected BOOT ucode\n"); - break; - case IWL_UCODE_TLV_PROBE_MAX_LEN: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - capa->max_probe_length = - le32_to_cpup((__le32 *)tlv_data); - break; - case IWL_UCODE_TLV_PAN: - if (tlv_len) - goto invalid_tlv_len; - capa->flags |= IWL_UCODE_TLV_FLAGS_PAN; - break; - case IWL_UCODE_TLV_FLAGS: - /* must be at least one u32 */ - if (tlv_len < sizeof(u32)) - goto invalid_tlv_len; - /* and a proper number of u32s */ - if (tlv_len % sizeof(u32)) - goto invalid_tlv_len; - /* - * This driver only reads the first u32 as - * right now no more features are defined, - * if that changes then either the driver - * will not work with the new firmware, or - * it'll not take advantage of new features. - */ - capa->flags = le32_to_cpup((__le32 *)tlv_data); - break; - case IWL_UCODE_TLV_API_CHANGES_SET: - if (tlv_len != sizeof(struct iwl_ucode_api)) - goto invalid_tlv_len; - if (iwl_set_ucode_api_flags(drv, tlv_data, capa)) - goto tlv_error; - break; - case IWL_UCODE_TLV_ENABLED_CAPABILITIES: - if (tlv_len != sizeof(struct iwl_ucode_capa)) - goto invalid_tlv_len; - if (iwl_set_ucode_capabilities(drv, tlv_data, capa)) - goto tlv_error; - break; - case IWL_UCODE_TLV_INIT_EVTLOG_PTR: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - pieces->init_evtlog_ptr = - le32_to_cpup((__le32 *)tlv_data); - break; - case IWL_UCODE_TLV_INIT_EVTLOG_SIZE: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - pieces->init_evtlog_size = - le32_to_cpup((__le32 *)tlv_data); - break; - case IWL_UCODE_TLV_INIT_ERRLOG_PTR: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - pieces->init_errlog_ptr = - le32_to_cpup((__le32 *)tlv_data); - break; - case IWL_UCODE_TLV_RUNT_EVTLOG_PTR: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - pieces->inst_evtlog_ptr = - le32_to_cpup((__le32 *)tlv_data); - break; - case IWL_UCODE_TLV_RUNT_EVTLOG_SIZE: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - pieces->inst_evtlog_size = - le32_to_cpup((__le32 *)tlv_data); - break; - case IWL_UCODE_TLV_RUNT_ERRLOG_PTR: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - pieces->inst_errlog_ptr = - le32_to_cpup((__le32 *)tlv_data); - break; - case IWL_UCODE_TLV_ENHANCE_SENS_TBL: - if (tlv_len) - goto invalid_tlv_len; - drv->fw.enhance_sensitivity_table = true; - break; - case IWL_UCODE_TLV_WOWLAN_INST: - set_sec_data(pieces, IWL_UCODE_WOWLAN, - IWL_UCODE_SECTION_INST, tlv_data); - set_sec_size(pieces, IWL_UCODE_WOWLAN, - IWL_UCODE_SECTION_INST, tlv_len); - set_sec_offset(pieces, IWL_UCODE_WOWLAN, - IWL_UCODE_SECTION_INST, - IWLAGN_RTC_INST_LOWER_BOUND); - break; - case IWL_UCODE_TLV_WOWLAN_DATA: - set_sec_data(pieces, IWL_UCODE_WOWLAN, - IWL_UCODE_SECTION_DATA, tlv_data); - set_sec_size(pieces, IWL_UCODE_WOWLAN, - IWL_UCODE_SECTION_DATA, tlv_len); - set_sec_offset(pieces, IWL_UCODE_WOWLAN, - IWL_UCODE_SECTION_DATA, - IWLAGN_RTC_DATA_LOWER_BOUND); - break; - case IWL_UCODE_TLV_PHY_CALIBRATION_SIZE: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - capa->standard_phy_calibration_size = - le32_to_cpup((__le32 *)tlv_data); - break; - case IWL_UCODE_TLV_SEC_RT: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, - tlv_len); - drv->fw.mvm_fw = true; - break; - case IWL_UCODE_TLV_SEC_INIT: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, - tlv_len); - drv->fw.mvm_fw = true; - break; - case IWL_UCODE_TLV_SEC_WOWLAN: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, - tlv_len); - drv->fw.mvm_fw = true; - break; - case IWL_UCODE_TLV_DEF_CALIB: - if (tlv_len != sizeof(struct iwl_tlv_calib_data)) - goto invalid_tlv_len; - if (iwl_set_default_calib(drv, tlv_data)) - goto tlv_error; - break; - case IWL_UCODE_TLV_PHY_SKU: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data); - drv->fw.valid_tx_ant = (drv->fw.phy_config & - FW_PHY_CFG_TX_CHAIN) >> - FW_PHY_CFG_TX_CHAIN_POS; - drv->fw.valid_rx_ant = (drv->fw.phy_config & - FW_PHY_CFG_RX_CHAIN) >> - FW_PHY_CFG_RX_CHAIN_POS; - break; - case IWL_UCODE_TLV_SECURE_SEC_RT: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, - tlv_len); - drv->fw.mvm_fw = true; - break; - case IWL_UCODE_TLV_SECURE_SEC_INIT: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, - tlv_len); - drv->fw.mvm_fw = true; - break; - case IWL_UCODE_TLV_SECURE_SEC_WOWLAN: - iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, - tlv_len); - drv->fw.mvm_fw = true; - break; - case IWL_UCODE_TLV_NUM_OF_CPU: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - num_of_cpus = - le32_to_cpup((__le32 *)tlv_data); - - if (num_of_cpus == 2) { - drv->fw.img[IWL_UCODE_REGULAR].is_dual_cpus = - true; - drv->fw.img[IWL_UCODE_INIT].is_dual_cpus = - true; - drv->fw.img[IWL_UCODE_WOWLAN].is_dual_cpus = - true; - } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) { - IWL_ERR(drv, "Driver support upto 2 CPUs\n"); - return -EINVAL; - } - break; - case IWL_UCODE_TLV_CSCHEME: - if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len)) - goto invalid_tlv_len; - break; - case IWL_UCODE_TLV_N_SCAN_CHANNELS: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - capa->n_scan_channels = - le32_to_cpup((__le32 *)tlv_data); - break; - case IWL_UCODE_TLV_FW_VERSION: { - __le32 *ptr = (void *)tlv_data; - u32 major, minor; - u8 local_comp; - - if (tlv_len != sizeof(u32) * 3) - goto invalid_tlv_len; - - major = le32_to_cpup(ptr++); - minor = le32_to_cpup(ptr++); - local_comp = le32_to_cpup(ptr); - - snprintf(drv->fw.fw_version, - sizeof(drv->fw.fw_version), "%u.%u.%u", - major, minor, local_comp); - break; - } - case IWL_UCODE_TLV_FW_DBG_DEST: { - struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data; - - if (pieces->dbg_dest_tlv) { - IWL_ERR(drv, - "dbg destination ignored, already exists\n"); - break; - } - - pieces->dbg_dest_tlv = dest; - IWL_INFO(drv, "Found debug destination: %s\n", - get_fw_dbg_mode_string(dest->monitor_mode)); - - drv->fw.dbg_dest_reg_num = - tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv, - reg_ops); - drv->fw.dbg_dest_reg_num /= - sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]); - - break; - } - case IWL_UCODE_TLV_FW_DBG_CONF: { - struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data; - - if (!pieces->dbg_dest_tlv) { - IWL_ERR(drv, - "Ignore dbg config %d - no destination configured\n", - conf->id); - break; - } - - if (conf->id >= ARRAY_SIZE(drv->fw.dbg_conf_tlv)) { - IWL_ERR(drv, - "Skip unknown configuration: %d\n", - conf->id); - break; - } - - if (pieces->dbg_conf_tlv[conf->id]) { - IWL_ERR(drv, - "Ignore duplicate dbg config %d\n", - conf->id); - break; - } - - if (conf->usniffer) - usniffer_req = true; - - IWL_INFO(drv, "Found debug configuration: %d\n", - conf->id); - - pieces->dbg_conf_tlv[conf->id] = conf; - pieces->dbg_conf_tlv_len[conf->id] = tlv_len; - break; - } - case IWL_UCODE_TLV_FW_DBG_TRIGGER: { - struct iwl_fw_dbg_trigger_tlv *trigger = - (void *)tlv_data; - u32 trigger_id = le32_to_cpu(trigger->id); - - if (trigger_id >= ARRAY_SIZE(drv->fw.dbg_trigger_tlv)) { - IWL_ERR(drv, - "Skip unknown trigger: %u\n", - trigger->id); - break; - } - - if (pieces->dbg_trigger_tlv[trigger_id]) { - IWL_ERR(drv, - "Ignore duplicate dbg trigger %u\n", - trigger->id); - break; - } - - IWL_INFO(drv, "Found debug trigger: %u\n", trigger->id); - - pieces->dbg_trigger_tlv[trigger_id] = trigger; - pieces->dbg_trigger_tlv_len[trigger_id] = tlv_len; - break; - } - case IWL_UCODE_TLV_SEC_RT_USNIFFER: - usniffer_images = true; - iwl_store_ucode_sec(pieces, tlv_data, - IWL_UCODE_REGULAR_USNIFFER, - tlv_len); - break; - case IWL_UCODE_TLV_PAGING: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - paging_mem_size = le32_to_cpup((__le32 *)tlv_data); - - IWL_DEBUG_FW(drv, - "Paging: paging enabled (size = %u bytes)\n", - paging_mem_size); - - if (paging_mem_size > MAX_PAGING_IMAGE_SIZE) { - IWL_ERR(drv, - "Paging: driver supports up to %lu bytes for paging image\n", - MAX_PAGING_IMAGE_SIZE); - return -EINVAL; - } - - if (paging_mem_size & (FW_PAGING_SIZE - 1)) { - IWL_ERR(drv, - "Paging: image isn't multiple %lu\n", - FW_PAGING_SIZE); - return -EINVAL; - } - - drv->fw.img[IWL_UCODE_REGULAR].paging_mem_size = - paging_mem_size; - usniffer_img = IWL_UCODE_REGULAR_USNIFFER; - drv->fw.img[usniffer_img].paging_mem_size = - paging_mem_size; - break; - case IWL_UCODE_TLV_SDIO_ADMA_ADDR: - if (tlv_len != sizeof(u32)) - goto invalid_tlv_len; - drv->fw.sdio_adma_addr = - le32_to_cpup((__le32 *)tlv_data); - break; - case IWL_UCODE_TLV_FW_GSCAN_CAPA: - if (iwl_store_gscan_capa(&drv->fw, tlv_data, tlv_len)) - goto invalid_tlv_len; - gscan_capa = true; - break; - default: - IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); - break; - } - } - - if (usniffer_req && !usniffer_images) { - IWL_ERR(drv, - "user selected to work with usniffer but usniffer image isn't available in ucode package\n"); - return -EINVAL; - } - - if (len) { - IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len); - iwl_print_hex_dump(drv, IWL_DL_FW, (u8 *)data, len); - return -EINVAL; - } - - /* - * If ucode advertises that it supports GSCAN but GSCAN - * capabilities TLV is not present, warn and continue without GSCAN. - */ - if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT) && - WARN(!gscan_capa, - "GSCAN is supported but capabilities TLV is unavailable\n")) - __clear_bit((__force long)IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT, - capa->_capa); - - return 0; - - invalid_tlv_len: - IWL_ERR(drv, "TLV %d has invalid size: %u\n", tlv_type, tlv_len); - tlv_error: - iwl_print_hex_dump(drv, IWL_DL_FW, tlv_data, tlv_len); - - return -EINVAL; -} - -static int iwl_alloc_ucode(struct iwl_drv *drv, - struct iwl_firmware_pieces *pieces, - enum iwl_ucode_type type) -{ - int i; - for (i = 0; - i < IWL_UCODE_SECTION_MAX && get_sec_size(pieces, type, i); - i++) - if (iwl_alloc_fw_desc(drv, &(drv->fw.img[type].sec[i]), - get_sec(pieces, type, i))) - return -ENOMEM; - return 0; -} - -static int validate_sec_sizes(struct iwl_drv *drv, - struct iwl_firmware_pieces *pieces, - const struct iwl_cfg *cfg) -{ - IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %Zd\n", - get_sec_size(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_INST)); - IWL_DEBUG_INFO(drv, "f/w package hdr runtime data size = %Zd\n", - get_sec_size(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_DATA)); - IWL_DEBUG_INFO(drv, "f/w package hdr init inst size = %Zd\n", - get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST)); - IWL_DEBUG_INFO(drv, "f/w package hdr init data size = %Zd\n", - get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)); - - /* Verify that uCode images will fit in card's SRAM. */ - if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) > - cfg->max_inst_size) { - IWL_ERR(drv, "uCode instr len %Zd too large to fit in\n", - get_sec_size(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_INST)); - return -1; - } - - if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) > - cfg->max_data_size) { - IWL_ERR(drv, "uCode data len %Zd too large to fit in\n", - get_sec_size(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_DATA)); - return -1; - } - - if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) > - cfg->max_inst_size) { - IWL_ERR(drv, "uCode init instr len %Zd too large to fit in\n", - get_sec_size(pieces, IWL_UCODE_INIT, - IWL_UCODE_SECTION_INST)); - return -1; - } - - if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) > - cfg->max_data_size) { - IWL_ERR(drv, "uCode init data len %Zd too large to fit in\n", - get_sec_size(pieces, IWL_UCODE_REGULAR, - IWL_UCODE_SECTION_DATA)); - return -1; - } - return 0; -} - -static struct iwl_op_mode * -_iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op) -{ - const struct iwl_op_mode_ops *ops = op->ops; - struct dentry *dbgfs_dir = NULL; - struct iwl_op_mode *op_mode = NULL; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - drv->dbgfs_op_mode = debugfs_create_dir(op->name, - drv->dbgfs_drv); - if (!drv->dbgfs_op_mode) { - IWL_ERR(drv, - "failed to create opmode debugfs directory\n"); - return op_mode; - } - dbgfs_dir = drv->dbgfs_op_mode; -#endif - - op_mode = ops->start(drv->trans, drv->cfg, &drv->fw, dbgfs_dir); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (!op_mode) { - debugfs_remove_recursive(drv->dbgfs_op_mode); - drv->dbgfs_op_mode = NULL; - } -#endif - - return op_mode; -} - -static void _iwl_op_mode_stop(struct iwl_drv *drv) -{ - /* op_mode can be NULL if its start failed */ - if (drv->op_mode) { - iwl_op_mode_stop(drv->op_mode); - drv->op_mode = NULL; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - debugfs_remove_recursive(drv->dbgfs_op_mode); - drv->dbgfs_op_mode = NULL; -#endif - } -} - -/** - * iwl_req_fw_callback - callback when firmware was loaded - * - * If loaded successfully, copies the firmware into buffers - * for the card to fetch (via DMA). - */ -static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) -{ - struct iwl_drv *drv = context; - struct iwl_fw *fw = &drv->fw; - struct iwl_ucode_header *ucode; - struct iwlwifi_opmode_table *op; - int err; - struct iwl_firmware_pieces *pieces; - const unsigned int api_max = drv->cfg->ucode_api_max; - unsigned int api_ok = drv->cfg->ucode_api_ok; - const unsigned int api_min = drv->cfg->ucode_api_min; - size_t trigger_tlv_sz[FW_DBG_TRIGGER_MAX]; - u32 api_ver; - int i; - bool load_module = false; - - fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH; - fw->ucode_capa.standard_phy_calibration_size = - IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; - fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS; - - if (!api_ok) - api_ok = api_max; - - pieces = kzalloc(sizeof(*pieces), GFP_KERNEL); - if (!pieces) - return; - - if (!ucode_raw) { - if (drv->fw_index <= api_ok) - IWL_ERR(drv, - "request for firmware file '%s' failed.\n", - drv->firmware_name); - goto try_again; - } - - IWL_DEBUG_INFO(drv, "Loaded firmware file '%s' (%zd bytes).\n", - drv->firmware_name, ucode_raw->size); - - /* Make sure that we got at least the API version number */ - if (ucode_raw->size < 4) { - IWL_ERR(drv, "File size way too small!\n"); - goto try_again; - } - - /* Data from ucode file: header followed by uCode images */ - ucode = (struct iwl_ucode_header *)ucode_raw->data; - - if (ucode->ver) - err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces); - else - err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces, - &fw->ucode_capa); - - if (err) - goto try_again; - - if (fw_has_api(&drv->fw.ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION)) - api_ver = drv->fw.ucode_ver; - else - api_ver = IWL_UCODE_API(drv->fw.ucode_ver); - - /* - * api_ver should match the api version forming part of the - * firmware filename ... but we don't check for that and only rely - * on the API version read from firmware header from here on forward - */ - /* no api version check required for experimental uCode */ - if (drv->fw_index != UCODE_EXPERIMENTAL_INDEX) { - if (api_ver < api_min || api_ver > api_max) { - IWL_ERR(drv, - "Driver unable to support your firmware API. " - "Driver supports v%u, firmware is v%u.\n", - api_max, api_ver); - goto try_again; - } - - if (api_ver < api_ok) { - if (api_ok != api_max) - IWL_ERR(drv, "Firmware has old API version, " - "expected v%u through v%u, got v%u.\n", - api_ok, api_max, api_ver); - else - IWL_ERR(drv, "Firmware has old API version, " - "expected v%u, got v%u.\n", - api_max, api_ver); - IWL_ERR(drv, "New firmware can be obtained from " - "http://www.intellinuxwireless.org/.\n"); - } - } - - /* - * In mvm uCode there is no difference between data and instructions - * sections. - */ - if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg)) - goto try_again; - - /* Allocate ucode buffers for card's bus-master loading ... */ - - /* Runtime instructions and 2 copies of data: - * 1) unmodified from disk - * 2) backup cache for save/restore during power-downs */ - for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) - if (iwl_alloc_ucode(drv, pieces, i)) - goto out_free_fw; - - if (pieces->dbg_dest_tlv) { - drv->fw.dbg_dest_tlv = - kmemdup(pieces->dbg_dest_tlv, - sizeof(*pieces->dbg_dest_tlv) + - sizeof(pieces->dbg_dest_tlv->reg_ops[0]) * - drv->fw.dbg_dest_reg_num, GFP_KERNEL); - - if (!drv->fw.dbg_dest_tlv) - goto out_free_fw; - } - - for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) { - if (pieces->dbg_conf_tlv[i]) { - drv->fw.dbg_conf_tlv_len[i] = - pieces->dbg_conf_tlv_len[i]; - drv->fw.dbg_conf_tlv[i] = - kmemdup(pieces->dbg_conf_tlv[i], - drv->fw.dbg_conf_tlv_len[i], - GFP_KERNEL); - if (!drv->fw.dbg_conf_tlv[i]) - goto out_free_fw; - } - } - - memset(&trigger_tlv_sz, 0xff, sizeof(trigger_tlv_sz)); - - trigger_tlv_sz[FW_DBG_TRIGGER_MISSED_BEACONS] = - sizeof(struct iwl_fw_dbg_trigger_missed_bcon); - trigger_tlv_sz[FW_DBG_TRIGGER_CHANNEL_SWITCH] = 0; - trigger_tlv_sz[FW_DBG_TRIGGER_FW_NOTIF] = - sizeof(struct iwl_fw_dbg_trigger_cmd); - trigger_tlv_sz[FW_DBG_TRIGGER_MLME] = - sizeof(struct iwl_fw_dbg_trigger_mlme); - trigger_tlv_sz[FW_DBG_TRIGGER_STATS] = - sizeof(struct iwl_fw_dbg_trigger_stats); - trigger_tlv_sz[FW_DBG_TRIGGER_RSSI] = - sizeof(struct iwl_fw_dbg_trigger_low_rssi); - trigger_tlv_sz[FW_DBG_TRIGGER_TXQ_TIMERS] = - sizeof(struct iwl_fw_dbg_trigger_txq_timer); - trigger_tlv_sz[FW_DBG_TRIGGER_TIME_EVENT] = - sizeof(struct iwl_fw_dbg_trigger_time_event); - trigger_tlv_sz[FW_DBG_TRIGGER_BA] = - sizeof(struct iwl_fw_dbg_trigger_ba); - - for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) { - if (pieces->dbg_trigger_tlv[i]) { - /* - * If the trigger isn't long enough, WARN and exit. - * Someone is trying to debug something and he won't - * be able to catch the bug he is trying to chase. - * We'd better be noisy to be sure he knows what's - * going on. - */ - if (WARN_ON(pieces->dbg_trigger_tlv_len[i] < - (trigger_tlv_sz[i] + - sizeof(struct iwl_fw_dbg_trigger_tlv)))) - goto out_free_fw; - drv->fw.dbg_trigger_tlv_len[i] = - pieces->dbg_trigger_tlv_len[i]; - drv->fw.dbg_trigger_tlv[i] = - kmemdup(pieces->dbg_trigger_tlv[i], - drv->fw.dbg_trigger_tlv_len[i], - GFP_KERNEL); - if (!drv->fw.dbg_trigger_tlv[i]) - goto out_free_fw; - } - } - - /* Now that we can no longer fail, copy information */ - - /* - * The (size - 16) / 12 formula is based on the information recorded - * for each event, which is of mode 1 (including timestamp) for all - * new microcodes that include this information. - */ - fw->init_evtlog_ptr = pieces->init_evtlog_ptr; - if (pieces->init_evtlog_size) - fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12; - else - fw->init_evtlog_size = - drv->cfg->base_params->max_event_log_size; - fw->init_errlog_ptr = pieces->init_errlog_ptr; - fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr; - if (pieces->inst_evtlog_size) - fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12; - else - fw->inst_evtlog_size = - drv->cfg->base_params->max_event_log_size; - fw->inst_errlog_ptr = pieces->inst_errlog_ptr; - - /* - * figure out the offset of chain noise reset and gain commands - * base on the size of standard phy calibration commands table size - */ - if (fw->ucode_capa.standard_phy_calibration_size > - IWL_MAX_PHY_CALIBRATE_TBL_SIZE) - fw->ucode_capa.standard_phy_calibration_size = - IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE; - - /* We have our copies now, allow OS release its copies */ - release_firmware(ucode_raw); - - mutex_lock(&iwlwifi_opmode_table_mtx); - if (fw->mvm_fw) - op = &iwlwifi_opmode_table[MVM_OP_MODE]; - else - op = &iwlwifi_opmode_table[DVM_OP_MODE]; - - IWL_INFO(drv, "loaded firmware version %s op_mode %s\n", - drv->fw.fw_version, op->name); - - /* add this device to the list of devices using this op_mode */ - list_add_tail(&drv->list, &op->drv); - - if (op->ops) { - drv->op_mode = _iwl_op_mode_start(drv, op); - - if (!drv->op_mode) { - mutex_unlock(&iwlwifi_opmode_table_mtx); - goto out_unbind; - } - } else { - load_module = true; - } - mutex_unlock(&iwlwifi_opmode_table_mtx); - - /* - * Complete the firmware request last so that - * a driver unbind (stop) doesn't run while we - * are doing the start() above. - */ - complete(&drv->request_firmware_complete); - - /* - * Load the module last so we don't block anything - * else from proceeding if the module fails to load - * or hangs loading. - */ - if (load_module) { - err = request_module("%s", op->name); -#ifdef CONFIG_IWLWIFI_OPMODE_MODULAR - if (err) - IWL_ERR(drv, - "failed to load module %s (error %d), is dynamic loading enabled?\n", - op->name, err); -#endif - } - kfree(pieces); - return; - - try_again: - /* try next, if any */ - release_firmware(ucode_raw); - if (iwl_request_firmware(drv, false)) - goto out_unbind; - kfree(pieces); - return; - - out_free_fw: - IWL_ERR(drv, "failed to allocate pci memory\n"); - iwl_dealloc_ucode(drv); - release_firmware(ucode_raw); - out_unbind: - kfree(pieces); - complete(&drv->request_firmware_complete); - device_release_driver(drv->trans->dev); -} - -struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, - const struct iwl_cfg *cfg) -{ - struct iwl_drv *drv; - int ret; - - drv = kzalloc(sizeof(*drv), GFP_KERNEL); - if (!drv) { - ret = -ENOMEM; - goto err; - } - - drv->trans = trans; - drv->dev = trans->dev; - drv->cfg = cfg; - - init_completion(&drv->request_firmware_complete); - INIT_LIST_HEAD(&drv->list); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - /* Create the device debugfs entries. */ - drv->dbgfs_drv = debugfs_create_dir(dev_name(trans->dev), - iwl_dbgfs_root); - - if (!drv->dbgfs_drv) { - IWL_ERR(drv, "failed to create debugfs directory\n"); - ret = -ENOMEM; - goto err_free_drv; - } - - /* Create transport layer debugfs dir */ - drv->trans->dbgfs_dir = debugfs_create_dir("trans", drv->dbgfs_drv); - - if (!drv->trans->dbgfs_dir) { - IWL_ERR(drv, "failed to create transport debugfs directory\n"); - ret = -ENOMEM; - goto err_free_dbgfs; - } -#endif - - ret = iwl_request_firmware(drv, true); - if (ret) { - IWL_ERR(trans, "Couldn't request the fw\n"); - goto err_fw; - } - - return drv; - -err_fw: -#ifdef CONFIG_IWLWIFI_DEBUGFS -err_free_dbgfs: - debugfs_remove_recursive(drv->dbgfs_drv); -err_free_drv: -#endif - kfree(drv); -err: - return ERR_PTR(ret); -} - -void iwl_drv_stop(struct iwl_drv *drv) -{ - wait_for_completion(&drv->request_firmware_complete); - - _iwl_op_mode_stop(drv); - - iwl_dealloc_ucode(drv); - - mutex_lock(&iwlwifi_opmode_table_mtx); - /* - * List is empty (this item wasn't added) - * when firmware loading failed -- in that - * case we can't remove it from any list. - */ - if (!list_empty(&drv->list)) - list_del(&drv->list); - mutex_unlock(&iwlwifi_opmode_table_mtx); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - debugfs_remove_recursive(drv->dbgfs_drv); -#endif - - kfree(drv); -} - - -/* shared module parameters */ -struct iwl_mod_params iwlwifi_mod_params = { - .restart_fw = true, - .bt_coex_active = true, - .power_level = IWL_POWER_INDEX_1, - .d0i3_disable = true, -#ifndef CONFIG_IWLWIFI_UAPSD - .uapsd_disable = true, -#endif /* CONFIG_IWLWIFI_UAPSD */ - /* the rest are 0 by default */ -}; -IWL_EXPORT_SYMBOL(iwlwifi_mod_params); - -int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops) -{ - int i; - struct iwl_drv *drv; - struct iwlwifi_opmode_table *op; - - mutex_lock(&iwlwifi_opmode_table_mtx); - for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { - op = &iwlwifi_opmode_table[i]; - if (strcmp(op->name, name)) - continue; - op->ops = ops; - /* TODO: need to handle exceptional case */ - list_for_each_entry(drv, &op->drv, list) - drv->op_mode = _iwl_op_mode_start(drv, op); - - mutex_unlock(&iwlwifi_opmode_table_mtx); - return 0; - } - mutex_unlock(&iwlwifi_opmode_table_mtx); - return -EIO; -} -IWL_EXPORT_SYMBOL(iwl_opmode_register); - -void iwl_opmode_deregister(const char *name) -{ - int i; - struct iwl_drv *drv; - - mutex_lock(&iwlwifi_opmode_table_mtx); - for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { - if (strcmp(iwlwifi_opmode_table[i].name, name)) - continue; - iwlwifi_opmode_table[i].ops = NULL; - - /* call the stop routine for all devices */ - list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list) - _iwl_op_mode_stop(drv); - - mutex_unlock(&iwlwifi_opmode_table_mtx); - return; - } - mutex_unlock(&iwlwifi_opmode_table_mtx); -} -IWL_EXPORT_SYMBOL(iwl_opmode_deregister); - -static int __init iwl_drv_init(void) -{ - int i; - - mutex_init(&iwlwifi_opmode_table_mtx); - - for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) - INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv); - - pr_info(DRV_DESCRIPTION "\n"); - pr_info(DRV_COPYRIGHT "\n"); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - /* Create the root of iwlwifi debugfs subsystem. */ - iwl_dbgfs_root = debugfs_create_dir(DRV_NAME, NULL); - - if (!iwl_dbgfs_root) - return -EFAULT; -#endif - - return iwl_pci_register_driver(); -} -module_init(iwl_drv_init); - -static void __exit iwl_drv_exit(void) -{ - iwl_pci_unregister_driver(); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - debugfs_remove_recursive(iwl_dbgfs_root); -#endif -} -module_exit(iwl_drv_exit); - -#ifdef CONFIG_IWLWIFI_DEBUG -module_param_named(debug, iwlwifi_mod_params.debug_level, uint, - S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "debug output mask"); -#endif - -module_param_named(swcrypto, iwlwifi_mod_params.sw_crypto, int, S_IRUGO); -MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); -module_param_named(11n_disable, iwlwifi_mod_params.disable_11n, uint, S_IRUGO); -MODULE_PARM_DESC(11n_disable, - "disable 11n functionality, bitmap: 1: full, 2: disable agg TX, 4: disable agg RX, 8 enable agg TX"); -module_param_named(amsdu_size_8K, iwlwifi_mod_params.amsdu_size_8K, - int, S_IRUGO); -MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0)"); -module_param_named(fw_restart, iwlwifi_mod_params.restart_fw, bool, S_IRUGO); -MODULE_PARM_DESC(fw_restart, "restart firmware in case of error (default true)"); - -module_param_named(antenna_coupling, iwlwifi_mod_params.ant_coupling, - int, S_IRUGO); -MODULE_PARM_DESC(antenna_coupling, - "specify antenna coupling in dB (default: 0 dB)"); - -module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO); -MODULE_PARM_DESC(nvm_file, "NVM file name"); - -module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable, - bool, S_IRUGO); -MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)"); - -module_param_named(lar_disable, iwlwifi_mod_params.lar_disable, - bool, S_IRUGO); -MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)"); - -module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, - bool, S_IRUGO | S_IWUSR); -#ifdef CONFIG_IWLWIFI_UAPSD -MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)"); -#else -MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: Y)"); -#endif - -/* - * set bt_coex_active to true, uCode will do kill/defer - * every time the priority line is asserted (BT is sending signals on the - * priority line in the PCIx). - * set bt_coex_active to false, uCode will ignore the BT activity and - * perform the normal operation - * - * User might experience transmit issue on some platform due to WiFi/BT - * co-exist problem. The possible behaviors are: - * Able to scan and finding all the available AP - * Not able to associate with any AP - * On those platforms, WiFi communication can be restored by set - * "bt_coex_active" module parameter to "false" - * - * default: bt_coex_active = true (BT_COEX_ENABLE) - */ -module_param_named(bt_coex_active, iwlwifi_mod_params.bt_coex_active, - bool, S_IRUGO); -MODULE_PARM_DESC(bt_coex_active, "enable wifi/bt co-exist (default: enable)"); - -module_param_named(led_mode, iwlwifi_mod_params.led_mode, int, S_IRUGO); -MODULE_PARM_DESC(led_mode, "0=system default, " - "1=On(RF On)/Off(RF Off), 2=blinking, 3=Off (default: 0)"); - -module_param_named(power_save, iwlwifi_mod_params.power_save, - bool, S_IRUGO); -MODULE_PARM_DESC(power_save, - "enable WiFi power management (default: disable)"); - -module_param_named(power_level, iwlwifi_mod_params.power_level, - int, S_IRUGO); -MODULE_PARM_DESC(power_level, - "default power save level (range from 1 - 5, default: 1)"); - -module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO); -MODULE_PARM_DESC(fw_monitor, - "firmware monitor - to debug FW (default: false - needs lots of memory)"); diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h deleted file mode 100644 index cda746b33db1..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-drv.h +++ /dev/null @@ -1,155 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __iwl_drv_h__ -#define __iwl_drv_h__ -#include - -/* for all modules */ -#define DRV_NAME "iwlwifi" -#define DRV_COPYRIGHT "Copyright(c) 2003- 2015 Intel Corporation" -#define DRV_AUTHOR "" - -/* radio config bits (actual values from NVM definition) */ -#define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ -#define NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ -#define NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ -#define NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ -#define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ -#define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ - -#define NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(x) (x & 0xF) -#define NVM_RF_CFG_DASH_MSK_FAMILY_8000(x) ((x >> 4) & 0xF) -#define NVM_RF_CFG_STEP_MSK_FAMILY_8000(x) ((x >> 8) & 0xF) -#define NVM_RF_CFG_TYPE_MSK_FAMILY_8000(x) ((x >> 12) & 0xFFF) -#define NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(x) ((x >> 24) & 0xF) -#define NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(x) ((x >> 28) & 0xF) - -/** - * DOC: Driver system flows - drv component - * - * This component implements the system flows such as bus enumeration, bus - * removal. Bus dependent parts of system flows (such as iwl_pci_probe) are in - * bus specific files (transport files). This is the code that is common among - * different buses. - * - * This component is also in charge of managing the several implementations of - * the wifi flows: it will allow to have several fw API implementation. These - * different implementations will differ in the way they implement mac80211's - * handlers too. - - * The init flow wrt to the drv component looks like this: - * 1) The bus specific component is called from module_init - * 2) The bus specific component registers the bus driver - * 3) The bus driver calls the probe function - * 4) The bus specific component configures the bus - * 5) The bus specific component calls to the drv bus agnostic part - * (iwl_drv_start) - * 6) iwl_drv_start fetches the fw ASYNC, iwl_req_fw_callback - * 7) iwl_req_fw_callback parses the fw file - * 8) iwl_req_fw_callback starts the wifi implementation to matches the fw - */ - -struct iwl_drv; -struct iwl_trans; -struct iwl_cfg; -/** - * iwl_drv_start - start the drv - * - * @trans_ops: the ops of the transport - * @cfg: device specific constants / virtual functions - * - * starts the driver: fetches the firmware. This should be called by bus - * specific system flows implementations. For example, the bus specific probe - * function should do bus related operations only, and then call to this - * function. It returns the driver object or %NULL if an error occurred. - */ -struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, - const struct iwl_cfg *cfg); - -/** - * iwl_drv_stop - stop the drv - * - * @drv: - * - * Stop the driver. This should be called by bus specific system flows - * implementations. For example, the bus specific remove function should first - * call this function and then do the bus related operations only. - */ -void iwl_drv_stop(struct iwl_drv *drv); - -/* - * exported symbol management - * - * The driver can be split into multiple modules, in which case some symbols - * must be exported for the sub-modules. However, if it's not split and - * everything is built-in, then we can avoid that. - */ -#ifdef CONFIG_IWLWIFI_OPMODE_MODULAR -#define IWL_EXPORT_SYMBOL(sym) EXPORT_SYMBOL_GPL(sym) -#else -#define IWL_EXPORT_SYMBOL(sym) -#endif - -#endif /* __iwl_drv_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c deleted file mode 100644 index acc3d186c5c1..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ /dev/null @@ -1,947 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ -#include -#include -#include -#include "iwl-drv.h" -#include "iwl-modparams.h" -#include "iwl-eeprom-parse.h" - -/* EEPROM offset definitions */ - -/* indirect access definitions */ -#define ADDRESS_MSK 0x0000FFFF -#define INDIRECT_TYPE_MSK 0x000F0000 -#define INDIRECT_HOST 0x00010000 -#define INDIRECT_GENERAL 0x00020000 -#define INDIRECT_REGULATORY 0x00030000 -#define INDIRECT_CALIBRATION 0x00040000 -#define INDIRECT_PROCESS_ADJST 0x00050000 -#define INDIRECT_OTHERS 0x00060000 -#define INDIRECT_TXP_LIMIT 0x00070000 -#define INDIRECT_TXP_LIMIT_SIZE 0x00080000 -#define INDIRECT_ADDRESS 0x00100000 - -/* corresponding link offsets in EEPROM */ -#define EEPROM_LINK_HOST (2*0x64) -#define EEPROM_LINK_GENERAL (2*0x65) -#define EEPROM_LINK_REGULATORY (2*0x66) -#define EEPROM_LINK_CALIBRATION (2*0x67) -#define EEPROM_LINK_PROCESS_ADJST (2*0x68) -#define EEPROM_LINK_OTHERS (2*0x69) -#define EEPROM_LINK_TXP_LIMIT (2*0x6a) -#define EEPROM_LINK_TXP_LIMIT_SIZE (2*0x6b) - -/* General */ -#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ -#define EEPROM_SUBSYSTEM_ID (2*0x0A) /* 2 bytes */ -#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ -#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ -#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ -#define EEPROM_VERSION (2*0x44) /* 2 bytes */ -#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ -#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ -#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ -#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ - -/* calibration */ -struct iwl_eeprom_calib_hdr { - u8 version; - u8 pa_type; - __le16 voltage; -} __packed; - -#define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION) -#define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL) - -/* temperature */ -#define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL) -#define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL) - -/* SKU Capabilities (actual values from EEPROM definition) */ -enum eeprom_sku_bits { - EEPROM_SKU_CAP_BAND_24GHZ = BIT(4), - EEPROM_SKU_CAP_BAND_52GHZ = BIT(5), - EEPROM_SKU_CAP_11N_ENABLE = BIT(6), - EEPROM_SKU_CAP_AMT_ENABLE = BIT(7), - EEPROM_SKU_CAP_IPAN_ENABLE = BIT(8) -}; - -/* radio config bits (actual values from EEPROM definition) */ -#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ -#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ -#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ -#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ -#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ -#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ - - -/* - * EEPROM bands - * These are the channel numbers from each band in the order - * that they are stored in the EEPROM band information. Note - * that EEPROM bands aren't the same as mac80211 bands, and - * there are even special "ht40 bands" in the EEPROM. - */ -static const u8 iwl_eeprom_band_1[14] = { /* 2.4 GHz */ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 -}; - -static const u8 iwl_eeprom_band_2[] = { /* 4915-5080MHz */ - 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 -}; - -static const u8 iwl_eeprom_band_3[] = { /* 5170-5320MHz */ - 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 -}; - -static const u8 iwl_eeprom_band_4[] = { /* 5500-5700MHz */ - 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 -}; - -static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */ - 145, 149, 153, 157, 161, 165 -}; - -static const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */ - 1, 2, 3, 4, 5, 6, 7 -}; - -static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ - 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 -}; - -#define IWL_NUM_CHANNELS (ARRAY_SIZE(iwl_eeprom_band_1) + \ - ARRAY_SIZE(iwl_eeprom_band_2) + \ - ARRAY_SIZE(iwl_eeprom_band_3) + \ - ARRAY_SIZE(iwl_eeprom_band_4) + \ - ARRAY_SIZE(iwl_eeprom_band_5)) - -/* rate data (static) */ -static struct ieee80211_rate iwl_cfg80211_rates[] = { - { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, }, - { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1, - .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, - { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2, - .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, - { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3, - .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, - { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, }, - { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, }, - { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, }, - { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, }, - { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, }, - { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, }, - { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, }, - { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, }, -}; -#define RATES_24_OFFS 0 -#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates) -#define RATES_52_OFFS 4 -#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS) - -/* EEPROM reading functions */ - -static u16 iwl_eeprom_query16(const u8 *eeprom, size_t eeprom_size, int offset) -{ - if (WARN_ON(offset + sizeof(u16) > eeprom_size)) - return 0; - return le16_to_cpup((__le16 *)(eeprom + offset)); -} - -static u32 eeprom_indirect_address(const u8 *eeprom, size_t eeprom_size, - u32 address) -{ - u16 offset = 0; - - if ((address & INDIRECT_ADDRESS) == 0) - return address; - - switch (address & INDIRECT_TYPE_MSK) { - case INDIRECT_HOST: - offset = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_LINK_HOST); - break; - case INDIRECT_GENERAL: - offset = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_LINK_GENERAL); - break; - case INDIRECT_REGULATORY: - offset = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_LINK_REGULATORY); - break; - case INDIRECT_TXP_LIMIT: - offset = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_LINK_TXP_LIMIT); - break; - case INDIRECT_TXP_LIMIT_SIZE: - offset = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_LINK_TXP_LIMIT_SIZE); - break; - case INDIRECT_CALIBRATION: - offset = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_LINK_CALIBRATION); - break; - case INDIRECT_PROCESS_ADJST: - offset = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_LINK_PROCESS_ADJST); - break; - case INDIRECT_OTHERS: - offset = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_LINK_OTHERS); - break; - default: - WARN_ON(1); - break; - } - - /* translate the offset from words to byte */ - return (address & ADDRESS_MSK) + (offset << 1); -} - -static const u8 *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size, - u32 offset) -{ - u32 address = eeprom_indirect_address(eeprom, eeprom_size, offset); - - if (WARN_ON(address >= eeprom_size)) - return NULL; - - return &eeprom[address]; -} - -static int iwl_eeprom_read_calib(const u8 *eeprom, size_t eeprom_size, - struct iwl_nvm_data *data) -{ - struct iwl_eeprom_calib_hdr *hdr; - - hdr = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, - EEPROM_CALIB_ALL); - if (!hdr) - return -ENODATA; - data->calib_version = hdr->version; - data->calib_voltage = hdr->voltage; - - return 0; -} - -/** - * enum iwl_eeprom_channel_flags - channel flags in EEPROM - * @EEPROM_CHANNEL_VALID: channel is usable for this SKU/geo - * @EEPROM_CHANNEL_IBSS: usable as an IBSS channel - * @EEPROM_CHANNEL_ACTIVE: active scanning allowed - * @EEPROM_CHANNEL_RADAR: radar detection required - * @EEPROM_CHANNEL_WIDE: 20 MHz channel okay (?) - * @EEPROM_CHANNEL_DFS: dynamic freq selection candidate - */ -enum iwl_eeprom_channel_flags { - EEPROM_CHANNEL_VALID = BIT(0), - EEPROM_CHANNEL_IBSS = BIT(1), - EEPROM_CHANNEL_ACTIVE = BIT(3), - EEPROM_CHANNEL_RADAR = BIT(4), - EEPROM_CHANNEL_WIDE = BIT(5), - EEPROM_CHANNEL_DFS = BIT(7), -}; - -/** - * struct iwl_eeprom_channel - EEPROM channel data - * @flags: %EEPROM_CHANNEL_* flags - * @max_power_avg: max power (in dBm) on this channel, at most 31 dBm - */ -struct iwl_eeprom_channel { - u8 flags; - s8 max_power_avg; -} __packed; - - -enum iwl_eeprom_enhanced_txpwr_flags { - IWL_EEPROM_ENH_TXP_FL_VALID = BIT(0), - IWL_EEPROM_ENH_TXP_FL_BAND_52G = BIT(1), - IWL_EEPROM_ENH_TXP_FL_OFDM = BIT(2), - IWL_EEPROM_ENH_TXP_FL_40MHZ = BIT(3), - IWL_EEPROM_ENH_TXP_FL_HT_AP = BIT(4), - IWL_EEPROM_ENH_TXP_FL_RES1 = BIT(5), - IWL_EEPROM_ENH_TXP_FL_RES2 = BIT(6), - IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE = BIT(7), -}; - -/** - * iwl_eeprom_enhanced_txpwr structure - * @flags: entry flags - * @channel: channel number - * @chain_a_max_pwr: chain a max power in 1/2 dBm - * @chain_b_max_pwr: chain b max power in 1/2 dBm - * @chain_c_max_pwr: chain c max power in 1/2 dBm - * @delta_20_in_40: 20-in-40 deltas (hi/lo) - * @mimo2_max_pwr: mimo2 max power in 1/2 dBm - * @mimo3_max_pwr: mimo3 max power in 1/2 dBm - * - * This structure presents the enhanced regulatory tx power limit layout - * in an EEPROM image. - */ -struct iwl_eeprom_enhanced_txpwr { - u8 flags; - u8 channel; - s8 chain_a_max; - s8 chain_b_max; - s8 chain_c_max; - u8 delta_20_in_40; - s8 mimo2_max; - s8 mimo3_max; -} __packed; - -static s8 iwl_get_max_txpwr_half_dbm(const struct iwl_nvm_data *data, - struct iwl_eeprom_enhanced_txpwr *txp) -{ - s8 result = 0; /* (.5 dBm) */ - - /* Take the highest tx power from any valid chains */ - if (data->valid_tx_ant & ANT_A && txp->chain_a_max > result) - result = txp->chain_a_max; - - if (data->valid_tx_ant & ANT_B && txp->chain_b_max > result) - result = txp->chain_b_max; - - if (data->valid_tx_ant & ANT_C && txp->chain_c_max > result) - result = txp->chain_c_max; - - if ((data->valid_tx_ant == ANT_AB || - data->valid_tx_ant == ANT_BC || - data->valid_tx_ant == ANT_AC) && txp->mimo2_max > result) - result = txp->mimo2_max; - - if (data->valid_tx_ant == ANT_ABC && txp->mimo3_max > result) - result = txp->mimo3_max; - - return result; -} - -#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT) -#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr) -#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE) - -#define TXP_CHECK_AND_PRINT(x) \ - ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) ? # x " " : "") - -static void -iwl_eeprom_enh_txp_read_element(struct iwl_nvm_data *data, - struct iwl_eeprom_enhanced_txpwr *txp, - int n_channels, s8 max_txpower_avg) -{ - int ch_idx; - enum ieee80211_band band; - - band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ? - IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; - - for (ch_idx = 0; ch_idx < n_channels; ch_idx++) { - struct ieee80211_channel *chan = &data->channels[ch_idx]; - - /* update matching channel or from common data only */ - if (txp->channel != 0 && chan->hw_value != txp->channel) - continue; - - /* update matching band only */ - if (band != chan->band) - continue; - - if (chan->max_power < max_txpower_avg && - !(txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ)) - chan->max_power = max_txpower_avg; - } -} - -static void iwl_eeprom_enhanced_txpower(struct device *dev, - struct iwl_nvm_data *data, - const u8 *eeprom, size_t eeprom_size, - int n_channels) -{ - struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; - int idx, entries; - __le16 *txp_len; - s8 max_txp_avg_halfdbm; - - BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); - - /* the length is in 16-bit words, but we want entries */ - txp_len = (__le16 *)iwl_eeprom_query_addr(eeprom, eeprom_size, - EEPROM_TXP_SZ_OFFS); - entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; - - txp_array = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, - EEPROM_TXP_OFFS); - - for (idx = 0; idx < entries; idx++) { - txp = &txp_array[idx]; - /* skip invalid entries */ - if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID)) - continue; - - IWL_DEBUG_EEPROM(dev, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n", - (txp->channel && (txp->flags & - IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ? - "Common " : (txp->channel) ? - "Channel" : "Common", - (txp->channel), - TXP_CHECK_AND_PRINT(VALID), - TXP_CHECK_AND_PRINT(BAND_52G), - TXP_CHECK_AND_PRINT(OFDM), - TXP_CHECK_AND_PRINT(40MHZ), - TXP_CHECK_AND_PRINT(HT_AP), - TXP_CHECK_AND_PRINT(RES1), - TXP_CHECK_AND_PRINT(RES2), - TXP_CHECK_AND_PRINT(COMMON_TYPE), - txp->flags); - IWL_DEBUG_EEPROM(dev, - "\t\t chain_A: 0x%02x chain_B: 0X%02x chain_C: 0X%02x\n", - txp->chain_a_max, txp->chain_b_max, - txp->chain_c_max); - IWL_DEBUG_EEPROM(dev, - "\t\t MIMO2: 0x%02x MIMO3: 0x%02x High 20_on_40: 0x%02x Low 20_on_40: 0x%02x\n", - txp->mimo2_max, txp->mimo3_max, - ((txp->delta_20_in_40 & 0xf0) >> 4), - (txp->delta_20_in_40 & 0x0f)); - - max_txp_avg_halfdbm = iwl_get_max_txpwr_half_dbm(data, txp); - - iwl_eeprom_enh_txp_read_element(data, txp, n_channels, - DIV_ROUND_UP(max_txp_avg_halfdbm, 2)); - - if (max_txp_avg_halfdbm > data->max_tx_pwr_half_dbm) - data->max_tx_pwr_half_dbm = max_txp_avg_halfdbm; - } -} - -static void iwl_init_band_reference(const struct iwl_cfg *cfg, - const u8 *eeprom, size_t eeprom_size, - int eeprom_band, int *eeprom_ch_count, - const struct iwl_eeprom_channel **ch_info, - const u8 **eeprom_ch_array) -{ - u32 offset = cfg->eeprom_params->regulatory_bands[eeprom_band - 1]; - - offset |= INDIRECT_ADDRESS | INDIRECT_REGULATORY; - - *ch_info = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, offset); - - switch (eeprom_band) { - case 1: /* 2.4GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1); - *eeprom_ch_array = iwl_eeprom_band_1; - break; - case 2: /* 4.9GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2); - *eeprom_ch_array = iwl_eeprom_band_2; - break; - case 3: /* 5.2GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3); - *eeprom_ch_array = iwl_eeprom_band_3; - break; - case 4: /* 5.5GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4); - *eeprom_ch_array = iwl_eeprom_band_4; - break; - case 5: /* 5.7GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5); - *eeprom_ch_array = iwl_eeprom_band_5; - break; - case 6: /* 2.4GHz ht40 channels */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6); - *eeprom_ch_array = iwl_eeprom_band_6; - break; - case 7: /* 5 GHz ht40 channels */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7); - *eeprom_ch_array = iwl_eeprom_band_7; - break; - default: - *eeprom_ch_count = 0; - *eeprom_ch_array = NULL; - WARN_ON(1); - } -} - -#define CHECK_AND_PRINT(x) \ - ((eeprom_ch->flags & EEPROM_CHANNEL_##x) ? # x " " : "") - -static void iwl_mod_ht40_chan_info(struct device *dev, - struct iwl_nvm_data *data, int n_channels, - enum ieee80211_band band, u16 channel, - const struct iwl_eeprom_channel *eeprom_ch, - u8 clear_ht40_extension_channel) -{ - struct ieee80211_channel *chan = NULL; - int i; - - for (i = 0; i < n_channels; i++) { - if (data->channels[i].band != band) - continue; - if (data->channels[i].hw_value != channel) - continue; - chan = &data->channels[i]; - break; - } - - if (!chan) - return; - - IWL_DEBUG_EEPROM(dev, - "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", - channel, - band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4", - CHECK_AND_PRINT(IBSS), - CHECK_AND_PRINT(ACTIVE), - CHECK_AND_PRINT(RADAR), - CHECK_AND_PRINT(WIDE), - CHECK_AND_PRINT(DFS), - eeprom_ch->flags, - eeprom_ch->max_power_avg, - ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) && - !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" - : "not "); - - if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) - chan->flags &= ~clear_ht40_extension_channel; -} - -#define CHECK_AND_PRINT_I(x) \ - ((eeprom_ch_info[ch_idx].flags & EEPROM_CHANNEL_##x) ? # x " " : "") - -static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - const u8 *eeprom, size_t eeprom_size) -{ - int band, ch_idx; - const struct iwl_eeprom_channel *eeprom_ch_info; - const u8 *eeprom_ch_array; - int eeprom_ch_count; - int n_channels = 0; - - /* - * Loop through the 5 EEPROM bands and add them to the parse list - */ - for (band = 1; band <= 5; band++) { - struct ieee80211_channel *channel; - - iwl_init_band_reference(cfg, eeprom, eeprom_size, band, - &eeprom_ch_count, &eeprom_ch_info, - &eeprom_ch_array); - - /* Loop through each band adding each of the channels */ - for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) { - const struct iwl_eeprom_channel *eeprom_ch; - - eeprom_ch = &eeprom_ch_info[ch_idx]; - - if (!(eeprom_ch->flags & EEPROM_CHANNEL_VALID)) { - IWL_DEBUG_EEPROM(dev, - "Ch. %d Flags %x [%sGHz] - No traffic\n", - eeprom_ch_array[ch_idx], - eeprom_ch_info[ch_idx].flags, - (band != 1) ? "5.2" : "2.4"); - continue; - } - - channel = &data->channels[n_channels]; - n_channels++; - - channel->hw_value = eeprom_ch_array[ch_idx]; - channel->band = (band == 1) ? IEEE80211_BAND_2GHZ - : IEEE80211_BAND_5GHZ; - channel->center_freq = - ieee80211_channel_to_frequency( - channel->hw_value, channel->band); - - /* set no-HT40, will enable as appropriate later */ - channel->flags = IEEE80211_CHAN_NO_HT40; - - if (!(eeprom_ch->flags & EEPROM_CHANNEL_IBSS)) - channel->flags |= IEEE80211_CHAN_NO_IR; - - if (!(eeprom_ch->flags & EEPROM_CHANNEL_ACTIVE)) - channel->flags |= IEEE80211_CHAN_NO_IR; - - if (eeprom_ch->flags & EEPROM_CHANNEL_RADAR) - channel->flags |= IEEE80211_CHAN_RADAR; - - /* Initialize regulatory-based run-time data */ - channel->max_power = - eeprom_ch_info[ch_idx].max_power_avg; - IWL_DEBUG_EEPROM(dev, - "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", - channel->hw_value, - (band != 1) ? "5.2" : "2.4", - CHECK_AND_PRINT_I(VALID), - CHECK_AND_PRINT_I(IBSS), - CHECK_AND_PRINT_I(ACTIVE), - CHECK_AND_PRINT_I(RADAR), - CHECK_AND_PRINT_I(WIDE), - CHECK_AND_PRINT_I(DFS), - eeprom_ch_info[ch_idx].flags, - eeprom_ch_info[ch_idx].max_power_avg, - ((eeprom_ch_info[ch_idx].flags & - EEPROM_CHANNEL_IBSS) && - !(eeprom_ch_info[ch_idx].flags & - EEPROM_CHANNEL_RADAR)) - ? "" : "not "); - } - } - - if (cfg->eeprom_params->enhanced_txpower) { - /* - * for newer device (6000 series and up) - * EEPROM contain enhanced tx power information - * driver need to process addition information - * to determine the max channel tx power limits - */ - iwl_eeprom_enhanced_txpower(dev, data, eeprom, eeprom_size, - n_channels); - } else { - /* All others use data from channel map */ - int i; - - data->max_tx_pwr_half_dbm = -128; - - for (i = 0; i < n_channels; i++) - data->max_tx_pwr_half_dbm = - max_t(s8, data->max_tx_pwr_half_dbm, - data->channels[i].max_power * 2); - } - - /* Check if we do have HT40 channels */ - if (cfg->eeprom_params->regulatory_bands[5] == - EEPROM_REGULATORY_BAND_NO_HT40 && - cfg->eeprom_params->regulatory_bands[6] == - EEPROM_REGULATORY_BAND_NO_HT40) - return n_channels; - - /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ - for (band = 6; band <= 7; band++) { - enum ieee80211_band ieeeband; - - iwl_init_band_reference(cfg, eeprom, eeprom_size, band, - &eeprom_ch_count, &eeprom_ch_info, - &eeprom_ch_array); - - /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ - ieeeband = (band == 6) ? IEEE80211_BAND_2GHZ - : IEEE80211_BAND_5GHZ; - - /* Loop through each band adding each of the channels */ - for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) { - /* Set up driver's info for lower half */ - iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband, - eeprom_ch_array[ch_idx], - &eeprom_ch_info[ch_idx], - IEEE80211_CHAN_NO_HT40PLUS); - - /* Set up driver's info for upper half */ - iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband, - eeprom_ch_array[ch_idx] + 4, - &eeprom_ch_info[ch_idx], - IEEE80211_CHAN_NO_HT40MINUS); - } - } - - return n_channels; -} - -int iwl_init_sband_channels(struct iwl_nvm_data *data, - struct ieee80211_supported_band *sband, - int n_channels, enum ieee80211_band band) -{ - struct ieee80211_channel *chan = &data->channels[0]; - int n = 0, idx = 0; - - while (idx < n_channels && chan->band != band) - chan = &data->channels[++idx]; - - sband->channels = &data->channels[idx]; - - while (idx < n_channels && chan->band == band) { - chan = &data->channels[++idx]; - n++; - } - - sband->n_channels = n; - - return n; -} - -#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ -#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ - -void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - struct ieee80211_sta_ht_cap *ht_info, - enum ieee80211_band band, - u8 tx_chains, u8 rx_chains) -{ - int max_bit_rate = 0; - - tx_chains = hweight8(tx_chains); - if (cfg->rx_with_siso_diversity) - rx_chains = 1; - else - rx_chains = hweight8(rx_chains); - - if (!(data->sku_cap_11n_enable) || !cfg->ht_params) { - ht_info->ht_supported = false; - return; - } - - if (data->sku_cap_mimo_disabled) - rx_chains = 1; - - ht_info->ht_supported = true; - ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40; - - if (cfg->ht_params->stbc) { - ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); - - if (tx_chains > 1) - ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; - } - - if (cfg->ht_params->ldpc) - ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; - - if (iwlwifi_mod_params.amsdu_size_8K) - ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; - - ht_info->ampdu_factor = cfg->max_ht_ampdu_exponent; - ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4; - - ht_info->mcs.rx_mask[0] = 0xFF; - if (rx_chains >= 2) - ht_info->mcs.rx_mask[1] = 0xFF; - if (rx_chains >= 3) - ht_info->mcs.rx_mask[2] = 0xFF; - - if (cfg->ht_params->ht_greenfield_support) - ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; - ht_info->cap |= IEEE80211_HT_CAP_SGI_20; - - max_bit_rate = MAX_BIT_RATE_20_MHZ; - - if (cfg->ht_params->ht40_bands & BIT(band)) { - ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - ht_info->cap |= IEEE80211_HT_CAP_SGI_40; - max_bit_rate = MAX_BIT_RATE_40_MHZ; - } - - /* Highest supported Rx data rate */ - max_bit_rate *= rx_chains; - WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); - ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); - - /* Tx MCS capabilities */ - ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - if (tx_chains != rx_chains) { - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; - ht_info->mcs.tx_params |= ((tx_chains - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - } -} - -static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - const u8 *eeprom, size_t eeprom_size) -{ - int n_channels = iwl_init_channel_map(dev, cfg, data, - eeprom, eeprom_size); - int n_used = 0; - struct ieee80211_supported_band *sband; - - sband = &data->bands[IEEE80211_BAND_2GHZ]; - sband->band = IEEE80211_BAND_2GHZ; - sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; - sband->n_bitrates = N_RATES_24; - n_used += iwl_init_sband_channels(data, sband, n_channels, - IEEE80211_BAND_2GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ, - data->valid_tx_ant, data->valid_rx_ant); - - sband = &data->bands[IEEE80211_BAND_5GHZ]; - sband->band = IEEE80211_BAND_5GHZ; - sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; - sband->n_bitrates = N_RATES_52; - n_used += iwl_init_sband_channels(data, sband, n_channels, - IEEE80211_BAND_5GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, - data->valid_tx_ant, data->valid_rx_ant); - - if (n_channels != n_used) - IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n", - n_used, n_channels); -} - -/* EEPROM data functions */ - -struct iwl_nvm_data * -iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg, - const u8 *eeprom, size_t eeprom_size) -{ - struct iwl_nvm_data *data; - const void *tmp; - u16 radio_cfg, sku; - - if (WARN_ON(!cfg || !cfg->eeprom_params)) - return NULL; - - data = kzalloc(sizeof(*data) + - sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS, - GFP_KERNEL); - if (!data) - return NULL; - - /* get MAC address(es) */ - tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_MAC_ADDRESS); - if (!tmp) - goto err_free; - memcpy(data->hw_addr, tmp, ETH_ALEN); - data->n_hw_addrs = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_NUM_MAC_ADDRESS); - - if (iwl_eeprom_read_calib(eeprom, eeprom_size, data)) - goto err_free; - - tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_XTAL); - if (!tmp) - goto err_free; - memcpy(data->xtal_calib, tmp, sizeof(data->xtal_calib)); - - tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, - EEPROM_RAW_TEMPERATURE); - if (!tmp) - goto err_free; - data->raw_temperature = *(__le16 *)tmp; - - tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, - EEPROM_KELVIN_TEMPERATURE); - if (!tmp) - goto err_free; - data->kelvin_temperature = *(__le16 *)tmp; - data->kelvin_voltage = *((__le16 *)tmp + 1); - - radio_cfg = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_RADIO_CONFIG); - data->radio_cfg_dash = EEPROM_RF_CFG_DASH_MSK(radio_cfg); - data->radio_cfg_pnum = EEPROM_RF_CFG_PNUM_MSK(radio_cfg); - data->radio_cfg_step = EEPROM_RF_CFG_STEP_MSK(radio_cfg); - data->radio_cfg_type = EEPROM_RF_CFG_TYPE_MSK(radio_cfg); - data->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg); - data->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg); - - sku = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_SKU_CAP); - data->sku_cap_11n_enable = sku & EEPROM_SKU_CAP_11N_ENABLE; - data->sku_cap_amt_enable = sku & EEPROM_SKU_CAP_AMT_ENABLE; - data->sku_cap_band_24GHz_enable = sku & EEPROM_SKU_CAP_BAND_24GHZ; - data->sku_cap_band_52GHz_enable = sku & EEPROM_SKU_CAP_BAND_52GHZ; - data->sku_cap_ipan_enable = sku & EEPROM_SKU_CAP_IPAN_ENABLE; - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) - data->sku_cap_11n_enable = false; - - data->nvm_version = iwl_eeprom_query16(eeprom, eeprom_size, - EEPROM_VERSION); - - /* check overrides (some devices have wrong EEPROM) */ - if (cfg->valid_tx_ant) - data->valid_tx_ant = cfg->valid_tx_ant; - if (cfg->valid_rx_ant) - data->valid_rx_ant = cfg->valid_rx_ant; - - if (!data->valid_tx_ant || !data->valid_rx_ant) { - IWL_ERR_DEV(dev, "invalid antennas (0x%x, 0x%x)\n", - data->valid_tx_ant, data->valid_rx_ant); - goto err_free; - } - - iwl_init_sbands(dev, cfg, data, eeprom, eeprom_size); - - return data; - err_free: - kfree(data); - return NULL; -} -IWL_EXPORT_SYMBOL(iwl_parse_eeprom_data); - -/* helper functions */ -int iwl_nvm_check_version(struct iwl_nvm_data *data, - struct iwl_trans *trans) -{ - if (data->nvm_version >= trans->cfg->nvm_ver || - data->calib_version >= trans->cfg->nvm_calib_ver) { - IWL_DEBUG_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n", - data->nvm_version, data->calib_version); - return 0; - } - - IWL_ERR(trans, - "Unsupported (too old) EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n", - data->nvm_version, trans->cfg->nvm_ver, - data->calib_version, trans->cfg->nvm_calib_ver); - return -EINVAL; -} -IWL_EXPORT_SYMBOL(iwl_nvm_check_version); diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h deleted file mode 100644 index 750c8c9ee70d..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h +++ /dev/null @@ -1,144 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ -#ifndef __iwl_eeprom_parse_h__ -#define __iwl_eeprom_parse_h__ - -#include -#include -#include "iwl-trans.h" - -struct iwl_nvm_data { - int n_hw_addrs; - u8 hw_addr[ETH_ALEN]; - - u8 calib_version; - __le16 calib_voltage; - - __le16 raw_temperature; - __le16 kelvin_temperature; - __le16 kelvin_voltage; - __le16 xtal_calib[2]; - - bool sku_cap_band_24GHz_enable; - bool sku_cap_band_52GHz_enable; - bool sku_cap_11n_enable; - bool sku_cap_11ac_enable; - bool sku_cap_amt_enable; - bool sku_cap_ipan_enable; - bool sku_cap_mimo_disabled; - - u16 radio_cfg_type; - u8 radio_cfg_step; - u8 radio_cfg_dash; - u8 radio_cfg_pnum; - u8 valid_tx_ant, valid_rx_ant; - - u32 nvm_version; - s8 max_tx_pwr_half_dbm; - - bool lar_enabled; - struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; - struct ieee80211_channel channels[]; -}; - -/** - * iwl_parse_eeprom_data - parse EEPROM data and return values - * - * @dev: device pointer we're parsing for, for debug only - * @cfg: device configuration for parsing and overrides - * @eeprom: the EEPROM data - * @eeprom_size: length of the EEPROM data - * - * This function parses all EEPROM values we need and then - * returns a (newly allocated) struct containing all the - * relevant values for driver use. The struct must be freed - * later with iwl_free_nvm_data(). - */ -struct iwl_nvm_data * -iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg, - const u8 *eeprom, size_t eeprom_size); - -/** - * iwl_free_nvm_data - free NVM data - * @data: the data to free - */ -static inline void iwl_free_nvm_data(struct iwl_nvm_data *data) -{ - kfree(data); -} - -int iwl_nvm_check_version(struct iwl_nvm_data *data, - struct iwl_trans *trans); - -int iwl_init_sband_channels(struct iwl_nvm_data *data, - struct ieee80211_supported_band *sband, - int n_channels, enum ieee80211_band band); - -void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - struct ieee80211_sta_ht_cap *ht_info, - enum ieee80211_band band, - u8 tx_chains, u8 rx_chains); - -#endif /* __iwl_eeprom_parse_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c deleted file mode 100644 index 219ca8acca62..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c +++ /dev/null @@ -1,464 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ -#include -#include -#include - -#include "iwl-drv.h" -#include "iwl-debug.h" -#include "iwl-eeprom-read.h" -#include "iwl-io.h" -#include "iwl-prph.h" -#include "iwl-csr.h" - -/* - * EEPROM access time values: - * - * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG. - * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). - * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. - * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. - */ -#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ - -#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ -#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ - - -/* - * The device's EEPROM semaphore prevents conflicts between driver and uCode - * when accessing the EEPROM; each access is a series of pulses to/from the - * EEPROM chip, not a single event, so even reads could conflict if they - * weren't arbitrated by the semaphore. - */ - -#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ -#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ - -static int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans) -{ - u16 count; - int ret; - - for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { - /* Request semaphore */ - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); - - /* See if we got it */ - ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, - EEPROM_SEM_TIMEOUT); - if (ret >= 0) { - IWL_DEBUG_EEPROM(trans->dev, - "Acquired semaphore after %d tries.\n", - count+1); - return ret; - } - } - - return ret; -} - -static void iwl_eeprom_release_semaphore(struct iwl_trans *trans) -{ - iwl_clear_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); -} - -static int iwl_eeprom_verify_signature(struct iwl_trans *trans, bool nvm_is_otp) -{ - u32 gp = iwl_read32(trans, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; - - IWL_DEBUG_EEPROM(trans->dev, "EEPROM signature=0x%08x\n", gp); - - switch (gp) { - case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP: - if (!nvm_is_otp) { - IWL_ERR(trans, "EEPROM with bad signature: 0x%08x\n", - gp); - return -ENOENT; - } - return 0; - case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: - case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: - if (nvm_is_otp) { - IWL_ERR(trans, "OTP with bad signature: 0x%08x\n", gp); - return -ENOENT; - } - return 0; - case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP: - default: - IWL_ERR(trans, - "bad EEPROM/OTP signature, type=%s, EEPROM_GP=0x%08x\n", - nvm_is_otp ? "OTP" : "EEPROM", gp); - return -ENOENT; - } -} - -/****************************************************************************** - * - * OTP related functions - * -******************************************************************************/ - -static void iwl_set_otp_access_absolute(struct iwl_trans *trans) -{ - iwl_read32(trans, CSR_OTP_GP_REG); - - iwl_clear_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_OTP_ACCESS_MODE); -} - -static int iwl_nvm_is_otp(struct iwl_trans *trans) -{ - u32 otpgp; - - /* OTP only valid for CP/PP and after */ - switch (trans->hw_rev & CSR_HW_REV_TYPE_MSK) { - case CSR_HW_REV_TYPE_NONE: - IWL_ERR(trans, "Unknown hardware type\n"); - return -EIO; - case CSR_HW_REV_TYPE_5300: - case CSR_HW_REV_TYPE_5350: - case CSR_HW_REV_TYPE_5100: - case CSR_HW_REV_TYPE_5150: - return 0; - default: - otpgp = iwl_read32(trans, CSR_OTP_GP_REG); - if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT) - return 1; - return 0; - } -} - -static int iwl_init_otp_access(struct iwl_trans *trans) -{ - int ret; - - /* Enable 40MHz radio clock */ - iwl_write32(trans, CSR_GP_CNTRL, - iwl_read32(trans, CSR_GP_CNTRL) | - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - /* wait for clock to be ready */ - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - 25000); - if (ret < 0) { - IWL_ERR(trans, "Time out access OTP\n"); - } else { - iwl_set_bits_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_RESET_REQ); - udelay(5); - iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_RESET_REQ); - - /* - * CSR auto clock gate disable bit - - * this is only applicable for HW with OTP shadow RAM - */ - if (trans->cfg->base_params->shadow_ram_support) - iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, - CSR_RESET_LINK_PWR_MGMT_DISABLED); - } - return ret; -} - -static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr, - __le16 *eeprom_data) -{ - int ret = 0; - u32 r; - u32 otpgp; - - iwl_write32(trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - ret = iwl_poll_bit(trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_READ_VALID_MSK, - CSR_EEPROM_REG_READ_VALID_MSK, - IWL_EEPROM_ACCESS_TIMEOUT); - if (ret < 0) { - IWL_ERR(trans, "Time out reading OTP[%d]\n", addr); - return ret; - } - r = iwl_read32(trans, CSR_EEPROM_REG); - /* check for ECC errors: */ - otpgp = iwl_read32(trans, CSR_OTP_GP_REG); - if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { - /* stop in this case */ - /* set the uncorrectable OTP ECC bit for acknowledgment */ - iwl_set_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); - IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n"); - return -EINVAL; - } - if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { - /* continue in this case */ - /* set the correctable OTP ECC bit for acknowledgment */ - iwl_set_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); - IWL_ERR(trans, "Correctable OTP ECC error, continue read\n"); - } - *eeprom_data = cpu_to_le16(r >> 16); - return 0; -} - -/* - * iwl_is_otp_empty: check for empty OTP - */ -static bool iwl_is_otp_empty(struct iwl_trans *trans) -{ - u16 next_link_addr = 0; - __le16 link_value; - bool is_empty = false; - - /* locate the beginning of OTP link list */ - if (!iwl_read_otp_word(trans, next_link_addr, &link_value)) { - if (!link_value) { - IWL_ERR(trans, "OTP is empty\n"); - is_empty = true; - } - } else { - IWL_ERR(trans, "Unable to read first block of OTP list.\n"); - is_empty = true; - } - - return is_empty; -} - - -/* - * iwl_find_otp_image: find EEPROM image in OTP - * finding the OTP block that contains the EEPROM image. - * the last valid block on the link list (the block _before_ the last block) - * is the block we should read and used to configure the device. - * If all the available OTP blocks are full, the last block will be the block - * we should read and used to configure the device. - * only perform this operation if shadow RAM is disabled - */ -static int iwl_find_otp_image(struct iwl_trans *trans, - u16 *validblockaddr) -{ - u16 next_link_addr = 0, valid_addr; - __le16 link_value = 0; - int usedblocks = 0; - - /* set addressing mode to absolute to traverse the link list */ - iwl_set_otp_access_absolute(trans); - - /* checking for empty OTP or error */ - if (iwl_is_otp_empty(trans)) - return -EINVAL; - - /* - * start traverse link list - * until reach the max number of OTP blocks - * different devices have different number of OTP blocks - */ - do { - /* save current valid block address - * check for more block on the link list - */ - valid_addr = next_link_addr; - next_link_addr = le16_to_cpu(link_value) * sizeof(u16); - IWL_DEBUG_EEPROM(trans->dev, "OTP blocks %d addr 0x%x\n", - usedblocks, next_link_addr); - if (iwl_read_otp_word(trans, next_link_addr, &link_value)) - return -EINVAL; - if (!link_value) { - /* - * reach the end of link list, return success and - * set address point to the starting address - * of the image - */ - *validblockaddr = valid_addr; - /* skip first 2 bytes (link list pointer) */ - *validblockaddr += 2; - return 0; - } - /* more in the link list, continue */ - usedblocks++; - } while (usedblocks <= trans->cfg->base_params->max_ll_items); - - /* OTP has no valid blocks */ - IWL_DEBUG_EEPROM(trans->dev, "OTP has no valid blocks\n"); - return -EINVAL; -} - -/** - * iwl_read_eeprom - read EEPROM contents - * - * Load the EEPROM contents from adapter and return it - * and its size. - * - * NOTE: This routine uses the non-debug IO access functions. - */ -int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size) -{ - __le16 *e; - u32 gp = iwl_read32(trans, CSR_EEPROM_GP); - int sz; - int ret; - u16 addr; - u16 validblockaddr = 0; - u16 cache_addr = 0; - int nvm_is_otp; - - if (!eeprom || !eeprom_size) - return -EINVAL; - - nvm_is_otp = iwl_nvm_is_otp(trans); - if (nvm_is_otp < 0) - return nvm_is_otp; - - sz = trans->cfg->base_params->eeprom_size; - IWL_DEBUG_EEPROM(trans->dev, "NVM size = %d\n", sz); - - e = kmalloc(sz, GFP_KERNEL); - if (!e) - return -ENOMEM; - - ret = iwl_eeprom_verify_signature(trans, nvm_is_otp); - if (ret < 0) { - IWL_ERR(trans, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); - goto err_free; - } - - /* Make sure driver (instead of uCode) is allowed to read EEPROM */ - ret = iwl_eeprom_acquire_semaphore(trans); - if (ret < 0) { - IWL_ERR(trans, "Failed to acquire EEPROM semaphore.\n"); - goto err_free; - } - - if (nvm_is_otp) { - ret = iwl_init_otp_access(trans); - if (ret) { - IWL_ERR(trans, "Failed to initialize OTP access.\n"); - goto err_unlock; - } - - iwl_write32(trans, CSR_EEPROM_GP, - iwl_read32(trans, CSR_EEPROM_GP) & - ~CSR_EEPROM_GP_IF_OWNER_MSK); - - iwl_set_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | - CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); - /* traversing the linked list if no shadow ram supported */ - if (!trans->cfg->base_params->shadow_ram_support) { - ret = iwl_find_otp_image(trans, &validblockaddr); - if (ret) - goto err_unlock; - } - for (addr = validblockaddr; addr < validblockaddr + sz; - addr += sizeof(u16)) { - __le16 eeprom_data; - - ret = iwl_read_otp_word(trans, addr, &eeprom_data); - if (ret) - goto err_unlock; - e[cache_addr / 2] = eeprom_data; - cache_addr += sizeof(u16); - } - } else { - /* eeprom is an array of 16bit values */ - for (addr = 0; addr < sz; addr += sizeof(u16)) { - u32 r; - - iwl_write32(trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - - ret = iwl_poll_bit(trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_READ_VALID_MSK, - CSR_EEPROM_REG_READ_VALID_MSK, - IWL_EEPROM_ACCESS_TIMEOUT); - if (ret < 0) { - IWL_ERR(trans, - "Time out reading EEPROM[%d]\n", addr); - goto err_unlock; - } - r = iwl_read32(trans, CSR_EEPROM_REG); - e[addr / 2] = cpu_to_le16(r >> 16); - } - } - - IWL_DEBUG_EEPROM(trans->dev, "NVM Type: %s\n", - nvm_is_otp ? "OTP" : "EEPROM"); - - iwl_eeprom_release_semaphore(trans); - - *eeprom_size = sz; - *eeprom = (u8 *)e; - return 0; - - err_unlock: - iwl_eeprom_release_semaphore(trans); - err_free: - kfree(e); - - return ret; -} -IWL_EXPORT_SYMBOL(iwl_read_eeprom); diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h deleted file mode 100644 index a6d3bdf82cdd..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h +++ /dev/null @@ -1,70 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __iwl_eeprom_h__ -#define __iwl_eeprom_h__ - -#include "iwl-trans.h" - -int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size); - -#endif /* __iwl_eeprom_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h deleted file mode 100644 index d56064861a9c..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-fh.h +++ /dev/null @@ -1,535 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __iwl_fh_h__ -#define __iwl_fh_h__ - -#include - -/****************************/ -/* Flow Handler Definitions */ -/****************************/ - -/** - * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) - * Addresses are offsets from device's PCI hardware base address. - */ -#define FH_MEM_LOWER_BOUND (0x1000) -#define FH_MEM_UPPER_BOUND (0x2000) - -/** - * Keep-Warm (KW) buffer base address. - * - * Driver must allocate a 4KByte buffer that is for keeping the - * host DRAM powered on (via dummy accesses to DRAM) to maintain low-latency - * DRAM access when doing Txing or Rxing. The dummy accesses prevent host - * from going into a power-savings mode that would cause higher DRAM latency, - * and possible data over/under-runs, before all Tx/Rx is complete. - * - * Driver loads FH_KW_MEM_ADDR_REG with the physical address (bits 35:4) - * of the buffer, which must be 4K aligned. Once this is set up, the device - * automatically invokes keep-warm accesses when normal accesses might not - * be sufficient to maintain fast DRAM response. - * - * Bit fields: - * 31-0: Keep-warm buffer physical base address [35:4], must be 4K aligned - */ -#define FH_KW_MEM_ADDR_REG (FH_MEM_LOWER_BOUND + 0x97C) - - -/** - * TFD Circular Buffers Base (CBBC) addresses - * - * Device has 16 base pointer registers, one for each of 16 host-DRAM-resident - * circular buffers (CBs/queues) containing Transmit Frame Descriptors (TFDs) - * (see struct iwl_tfd_frame). These 16 pointer registers are offset by 0x04 - * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte - * aligned (address bits 0-7 must be 0). - * Later devices have 20 (5000 series) or 30 (higher) queues, but the registers - * for them are in different places. - * - * Bit fields in each pointer register: - * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned - */ -#define FH_MEM_CBBC_0_15_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0) -#define FH_MEM_CBBC_0_15_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA10) -#define FH_MEM_CBBC_16_19_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBF0) -#define FH_MEM_CBBC_16_19_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) -#define FH_MEM_CBBC_20_31_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xB20) -#define FH_MEM_CBBC_20_31_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xB80) - -/* Find TFD CB base pointer for given queue */ -static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) -{ - if (chnl < 16) - return FH_MEM_CBBC_0_15_LOWER_BOUND + 4 * chnl; - if (chnl < 20) - return FH_MEM_CBBC_16_19_LOWER_BOUND + 4 * (chnl - 16); - WARN_ON_ONCE(chnl >= 32); - return FH_MEM_CBBC_20_31_LOWER_BOUND + 4 * (chnl - 20); -} - - -/** - * Rx SRAM Control and Status Registers (RSCSR) - * - * These registers provide handshake between driver and device for the Rx queue - * (this queue handles *all* command responses, notifications, Rx data, etc. - * sent from uCode to host driver). Unlike Tx, there is only one Rx - * queue, and only one Rx DMA/FIFO channel. Also unlike Tx, which can - * concatenate up to 20 DRAM buffers to form a Tx frame, each Receive Buffer - * Descriptor (RBD) points to only one Rx Buffer (RB); there is a 1:1 - * mapping between RBDs and RBs. - * - * Driver must allocate host DRAM memory for the following, and set the - * physical address of each into device registers: - * - * 1) Receive Buffer Descriptor (RBD) circular buffer (CB), typically with 256 - * entries (although any power of 2, up to 4096, is selectable by driver). - * Each entry (1 dword) points to a receive buffer (RB) of consistent size - * (typically 4K, although 8K or 16K are also selectable by driver). - * Driver sets up RB size and number of RBDs in the CB via Rx config - * register FH_MEM_RCSR_CHNL0_CONFIG_REG. - * - * Bit fields within one RBD: - * 27-0: Receive Buffer physical address bits [35:8], 256-byte aligned - * - * Driver sets physical address [35:8] of base of RBD circular buffer - * into FH_RSCSR_CHNL0_RBDCB_BASE_REG [27:0]. - * - * 2) Rx status buffer, 8 bytes, in which uCode indicates which Rx Buffers - * (RBs) have been filled, via a "write pointer", actually the index of - * the RB's corresponding RBD within the circular buffer. Driver sets - * physical address [35:4] into FH_RSCSR_CHNL0_STTS_WPTR_REG [31:0]. - * - * Bit fields in lower dword of Rx status buffer (upper dword not used - * by driver: - * 31-12: Not used by driver - * 11- 0: Index of last filled Rx buffer descriptor - * (device writes, driver reads this value) - * - * As the driver prepares Receive Buffers (RBs) for device to fill, driver must - * enter pointers to these RBs into contiguous RBD circular buffer entries, - * and update the device's "write" index register, - * FH_RSCSR_CHNL0_RBDCB_WPTR_REG. - * - * This "write" index corresponds to the *next* RBD that the driver will make - * available, i.e. one RBD past the tail of the ready-to-fill RBDs within - * the circular buffer. This value should initially be 0 (before preparing any - * RBs), should be 8 after preparing the first 8 RBs (for example), and must - * wrap back to 0 at the end of the circular buffer (but don't wrap before - * "read" index has advanced past 1! See below). - * NOTE: DEVICE EXPECTS THE WRITE INDEX TO BE INCREMENTED IN MULTIPLES OF 8. - * - * As the device fills RBs (referenced from contiguous RBDs within the circular - * buffer), it updates the Rx status buffer in host DRAM, 2) described above, - * to tell the driver the index of the latest filled RBD. The driver must - * read this "read" index from DRAM after receiving an Rx interrupt from device - * - * The driver must also internally keep track of a third index, which is the - * next RBD to process. When receiving an Rx interrupt, driver should process - * all filled but unprocessed RBs up to, but not including, the RB - * corresponding to the "read" index. For example, if "read" index becomes "1", - * driver may process the RB pointed to by RBD 0. Depending on volume of - * traffic, there may be many RBs to process. - * - * If read index == write index, device thinks there is no room to put new data. - * Due to this, the maximum number of filled RBs is 255, instead of 256. To - * be safe, make sure that there is a gap of at least 2 RBDs between "write" - * and "read" indexes; that is, make sure that there are no more than 254 - * buffers waiting to be filled. - */ -#define FH_MEM_RSCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBC0) -#define FH_MEM_RSCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) -#define FH_MEM_RSCSR_CHNL0 (FH_MEM_RSCSR_LOWER_BOUND) - -/** - * Physical base address of 8-byte Rx Status buffer. - * Bit fields: - * 31-0: Rx status buffer physical base address [35:4], must 16-byte aligned. - */ -#define FH_RSCSR_CHNL0_STTS_WPTR_REG (FH_MEM_RSCSR_CHNL0) - -/** - * Physical base address of Rx Buffer Descriptor Circular Buffer. - * Bit fields: - * 27-0: RBD CD physical base address [35:8], must be 256-byte aligned. - */ -#define FH_RSCSR_CHNL0_RBDCB_BASE_REG (FH_MEM_RSCSR_CHNL0 + 0x004) - -/** - * Rx write pointer (index, really!). - * Bit fields: - * 11-0: Index of driver's most recent prepared-to-be-filled RBD, + 1. - * NOTE: For 256-entry circular buffer, use only bits [7:0]. - */ -#define FH_RSCSR_CHNL0_RBDCB_WPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x008) -#define FH_RSCSR_CHNL0_WPTR (FH_RSCSR_CHNL0_RBDCB_WPTR_REG) - -#define FW_RSCSR_CHNL0_RXDCB_RDPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x00c) -#define FH_RSCSR_CHNL0_RDPTR FW_RSCSR_CHNL0_RXDCB_RDPTR_REG - -/** - * Rx Config/Status Registers (RCSR) - * Rx Config Reg for channel 0 (only channel used) - * - * Driver must initialize FH_MEM_RCSR_CHNL0_CONFIG_REG as follows for - * normal operation (see bit fields). - * - * Clearing FH_MEM_RCSR_CHNL0_CONFIG_REG to 0 turns off Rx DMA. - * Driver should poll FH_MEM_RSSR_RX_STATUS_REG for - * FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (bit 24) before continuing. - * - * Bit fields: - * 31-30: Rx DMA channel enable: '00' off/pause, '01' pause at end of frame, - * '10' operate normally - * 29-24: reserved - * 23-20: # RBDs in circular buffer = 2^value; use "8" for 256 RBDs (normal), - * min "5" for 32 RBDs, max "12" for 4096 RBDs. - * 19-18: reserved - * 17-16: size of each receive buffer; '00' 4K (normal), '01' 8K, - * '10' 12K, '11' 16K. - * 15-14: reserved - * 13-12: IRQ destination; '00' none, '01' host driver (normal operation) - * 11- 4: timeout for closing Rx buffer and interrupting host (units 32 usec) - * typical value 0x10 (about 1/2 msec) - * 3- 0: reserved - */ -#define FH_MEM_RCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) -#define FH_MEM_RCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xCC0) -#define FH_MEM_RCSR_CHNL0 (FH_MEM_RCSR_LOWER_BOUND) - -#define FH_MEM_RCSR_CHNL0_CONFIG_REG (FH_MEM_RCSR_CHNL0) -#define FH_MEM_RCSR_CHNL0_RBDCB_WPTR (FH_MEM_RCSR_CHNL0 + 0x8) -#define FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ (FH_MEM_RCSR_CHNL0 + 0x10) - -#define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */ -#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */ -#define FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK (0x00008000) /* bit 15 */ -#define FH_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MSK (0x00030000) /* bits 16-17 */ -#define FH_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MSK (0x00F00000) /* bits 20-23 */ -#define FH_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MSK (0xC0000000) /* bits 30-31*/ - -#define FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS (20) -#define FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS (4) -#define RX_RB_TIMEOUT (0x11) - -#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000) -#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000) -#define FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL (0x80000000) - -#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000) -#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000) -#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000) -#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000) - -#define FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY (0x00000004) -#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000) -#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000) - -/** - * Rx Shared Status Registers (RSSR) - * - * After stopping Rx DMA channel (writing 0 to - * FH_MEM_RCSR_CHNL0_CONFIG_REG), driver must poll - * FH_MEM_RSSR_RX_STATUS_REG until Rx channel is idle. - * - * Bit fields: - * 24: 1 = Channel 0 is idle - * - * FH_MEM_RSSR_SHARED_CTRL_REG and FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV - * contain default values that should not be altered by the driver. - */ -#define FH_MEM_RSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC40) -#define FH_MEM_RSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xD00) - -#define FH_MEM_RSSR_SHARED_CTRL_REG (FH_MEM_RSSR_LOWER_BOUND) -#define FH_MEM_RSSR_RX_STATUS_REG (FH_MEM_RSSR_LOWER_BOUND + 0x004) -#define FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV\ - (FH_MEM_RSSR_LOWER_BOUND + 0x008) - -#define FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) - -#define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT 28 -#define FH_MEM_TB_MAX_LENGTH (0x00020000) - -/* TFDB Area - TFDs buffer table */ -#define FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF) -#define FH_TFDIB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x900) -#define FH_TFDIB_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x958) -#define FH_TFDIB_CTRL0_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl)) -#define FH_TFDIB_CTRL1_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4) - -/** - * Transmit DMA Channel Control/Status Registers (TCSR) - * - * Device has one configuration register for each of 8 Tx DMA/FIFO channels - * supported in hardware (don't confuse these with the 16 Tx queues in DRAM, - * which feed the DMA/FIFO channels); config regs are separated by 0x20 bytes. - * - * To use a Tx DMA channel, driver must initialize its - * FH_TCSR_CHNL_TX_CONFIG_REG(chnl) with: - * - * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL - * - * All other bits should be 0. - * - * Bit fields: - * 31-30: Tx DMA channel enable: '00' off/pause, '01' pause at end of frame, - * '10' operate normally - * 29- 4: Reserved, set to "0" - * 3: Enable internal DMA requests (1, normal operation), disable (0) - * 2- 0: Reserved, set to "0" - */ -#define FH_TCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xD00) -#define FH_TCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xE60) - -/* Find Control/Status reg for given Tx DMA/FIFO channel */ -#define FH_TCSR_CHNL_NUM (8) - -/* TCSR: tx_config register values */ -#define FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \ - (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl)) -#define FH_TCSR_CHNL_TX_CREDIT_REG(_chnl) \ - (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x4) -#define FH_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \ - (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x8) - -#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000) -#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRV (0x00000001) - -#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE (0x00000000) -#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE (0x00000008) - -#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000) -#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000) -#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000) - -#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000) -#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000) -#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000) - -#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000) -#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000) -#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000) - -#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000) -#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000) -#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003) - -#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20) -#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12) - -/** - * Tx Shared Status Registers (TSSR) - * - * After stopping Tx DMA channel (writing 0 to - * FH_TCSR_CHNL_TX_CONFIG_REG(chnl)), driver must poll - * FH_TSSR_TX_STATUS_REG until selected Tx channel is idle - * (channel's buffers empty | no pending requests). - * - * Bit fields: - * 31-24: 1 = Channel buffers empty (channel 7:0) - * 23-16: 1 = No pending requests (channel 7:0) - */ -#define FH_TSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xEA0) -#define FH_TSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xEC0) - -#define FH_TSSR_TX_STATUS_REG (FH_TSSR_LOWER_BOUND + 0x010) - -/** - * Bit fields for TSSR(Tx Shared Status & Control) error status register: - * 31: Indicates an address error when accessed to internal memory - * uCode/driver must write "1" in order to clear this flag - * 30: Indicates that Host did not send the expected number of dwords to FH - * uCode/driver must write "1" in order to clear this flag - * 16-9:Each status bit is for one channel. Indicates that an (Error) ActDMA - * command was received from the scheduler while the TRB was already full - * with previous command - * uCode/driver must write "1" in order to clear this flag - * 7-0: Each status bit indicates a channel's TxCredit error. When an error - * bit is set, it indicates that the FH has received a full indication - * from the RTC TxFIFO and the current value of the TxCredit counter was - * not equal to zero. This mean that the credit mechanism was not - * synchronized to the TxFIFO status - * uCode/driver must write "1" in order to clear this flag - */ -#define FH_TSSR_TX_ERROR_REG (FH_TSSR_LOWER_BOUND + 0x018) -#define FH_TSSR_TX_MSG_CONFIG_REG (FH_TSSR_LOWER_BOUND + 0x008) - -#define FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16) - -/* Tx service channels */ -#define FH_SRVC_CHNL (9) -#define FH_SRVC_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9C8) -#define FH_SRVC_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0) -#define FH_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \ - (FH_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4) - -#define FH_TX_CHICKEN_BITS_REG (FH_MEM_LOWER_BOUND + 0xE98) -#define FH_TX_TRB_REG(_chan) (FH_MEM_LOWER_BOUND + 0x958 + (_chan) * 4) - -/* Instruct FH to increment the retry count of a packet when - * it is brought from the memory to TX-FIFO - */ -#define FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002) - -#define RX_QUEUE_SIZE 256 -#define RX_QUEUE_MASK 255 -#define RX_QUEUE_SIZE_LOG 8 - -/** - * struct iwl_rb_status - reserve buffer status - * host memory mapped FH registers - * @closed_rb_num [0:11] - Indicates the index of the RB which was closed - * @closed_fr_num [0:11] - Indicates the index of the RX Frame which was closed - * @finished_rb_num [0:11] - Indicates the index of the current RB - * in which the last frame was written to - * @finished_fr_num [0:11] - Indicates the index of the RX Frame - * which was transferred - */ -struct iwl_rb_status { - __le16 closed_rb_num; - __le16 closed_fr_num; - __le16 finished_rb_num; - __le16 finished_fr_nam; - __le32 __unused; -} __packed; - - -#define TFD_QUEUE_SIZE_MAX (256) -#define TFD_QUEUE_SIZE_BC_DUP (64) -#define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP) -#define IWL_TX_DMA_MASK DMA_BIT_MASK(36) -#define IWL_NUM_OF_TBS 20 - -static inline u8 iwl_get_dma_hi_addr(dma_addr_t addr) -{ - return (sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0) & 0xF; -} -/** - * struct iwl_tfd_tb transmit buffer descriptor within transmit frame descriptor - * - * This structure contains dma address and length of transmission address - * - * @lo: low [31:0] portion of the dma address of TX buffer - * every even is unaligned on 16 bit boundary - * @hi_n_len 0-3 [35:32] portion of dma - * 4-15 length of the tx buffer - */ -struct iwl_tfd_tb { - __le32 lo; - __le16 hi_n_len; -} __packed; - -/** - * struct iwl_tfd - * - * Transmit Frame Descriptor (TFD) - * - * @ __reserved1[3] reserved - * @ num_tbs 0-4 number of active tbs - * 5 reserved - * 6-7 padding (not used) - * @ tbs[20] transmit frame buffer descriptors - * @ __pad padding - * - * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM. - * Both driver and device share these circular buffers, each of which must be - * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes - * - * Driver must indicate the physical address of the base of each - * circular buffer via the FH_MEM_CBBC_QUEUE registers. - * - * Each TFD contains pointer/size information for up to 20 data buffers - * in host DRAM. These buffers collectively contain the (one) frame described - * by the TFD. Each buffer must be a single contiguous block of memory within - * itself, but buffers may be scattered in host DRAM. Each buffer has max size - * of (4K - 4). The concatenates all of a TFD's buffers into a single - * Tx frame, up to 8 KBytes in size. - * - * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx. - */ -struct iwl_tfd { - u8 __reserved1[3]; - u8 num_tbs; - struct iwl_tfd_tb tbs[IWL_NUM_OF_TBS]; - __le32 __pad; -} __packed; - -/* Keep Warm Size */ -#define IWL_KW_SIZE 0x1000 /* 4k */ - -/* Fixed (non-configurable) rx data from phy */ - -/** - * struct iwlagn_schedq_bc_tbl scheduler byte count table - * base physical address provided by SCD_DRAM_BASE_ADDR - * @tfd_offset 0-12 - tx command byte count - * 12-16 - station index - */ -struct iwlagn_scd_bc_tbl { - __le16 tfd_offset[TFD_QUEUE_BC_SIZE]; -} __packed; - -#endif /* !__iwl_fh_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h deleted file mode 100644 index 9dbe19cbb4dd..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ /dev/null @@ -1,320 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2014 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_error_dump_h__ -#define __fw_error_dump_h__ - -#include - -#define IWL_FW_ERROR_DUMP_BARKER 0x14789632 - -/** - * enum iwl_fw_error_dump_type - types of data in the dump file - * @IWL_FW_ERROR_DUMP_CSR: Control Status Registers - from offset 0 - * @IWL_FW_ERROR_DUMP_RXF: - * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as - * &struct iwl_fw_error_dump_txcmd packets - * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info - * info on the device / firmware. - * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor - * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several - * sections like this in a single file. - * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers - * @IWL_FW_ERROR_DUMP_MEM: chunk of memory - * @IWL_FW_ERROR_DUMP_ERROR_INFO: description of what triggered this dump. - * Structured as &struct iwl_fw_error_dump_trigger_desc. - * @IWL_FW_ERROR_DUMP_RB: the content of an RB structured as - * &struct iwl_fw_error_dump_rb - * @IWL_FW_ERROR_PAGING: UMAC's image memory segments which were - * paged to the DRAM. - */ -enum iwl_fw_error_dump_type { - /* 0 is deprecated */ - IWL_FW_ERROR_DUMP_CSR = 1, - IWL_FW_ERROR_DUMP_RXF = 2, - IWL_FW_ERROR_DUMP_TXCMD = 3, - IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4, - IWL_FW_ERROR_DUMP_FW_MONITOR = 5, - IWL_FW_ERROR_DUMP_PRPH = 6, - IWL_FW_ERROR_DUMP_TXF = 7, - IWL_FW_ERROR_DUMP_FH_REGS = 8, - IWL_FW_ERROR_DUMP_MEM = 9, - IWL_FW_ERROR_DUMP_ERROR_INFO = 10, - IWL_FW_ERROR_DUMP_RB = 11, - IWL_FW_ERROR_DUMP_PAGING = 12, - - IWL_FW_ERROR_DUMP_MAX, -}; - -/** - * struct iwl_fw_error_dump_data - data for one type - * @type: %enum iwl_fw_error_dump_type - * @len: the length starting from %data - * @data: the data itself - */ -struct iwl_fw_error_dump_data { - __le32 type; - __le32 len; - __u8 data[]; -} __packed; - -/** - * struct iwl_fw_error_dump_file - the layout of the header of the file - * @barker: must be %IWL_FW_ERROR_DUMP_BARKER - * @file_len: the length of all the file starting from %barker - * @data: array of %struct iwl_fw_error_dump_data - */ -struct iwl_fw_error_dump_file { - __le32 barker; - __le32 file_len; - u8 data[0]; -} __packed; - -/** - * struct iwl_fw_error_dump_txcmd - TX command data - * @cmdlen: original length of command - * @caplen: captured length of command (may be less) - * @data: captured command data, @caplen bytes - */ -struct iwl_fw_error_dump_txcmd { - __le32 cmdlen; - __le32 caplen; - u8 data[]; -} __packed; - -/** - * struct iwl_fw_error_dump_fifo - RX/TX FIFO data - * @fifo_num: number of FIFO (starting from 0) - * @available_bytes: num of bytes available in FIFO (may be less than FIFO size) - * @wr_ptr: position of write pointer - * @rd_ptr: position of read pointer - * @fence_ptr: position of fence pointer - * @fence_mode: the current mode of the fence (before locking) - - * 0=follow RD pointer ; 1 = freeze - * @data: all of the FIFO's data - */ -struct iwl_fw_error_dump_fifo { - __le32 fifo_num; - __le32 available_bytes; - __le32 wr_ptr; - __le32 rd_ptr; - __le32 fence_ptr; - __le32 fence_mode; - u8 data[]; -} __packed; - -enum iwl_fw_error_dump_family { - IWL_FW_ERROR_DUMP_FAMILY_7 = 7, - IWL_FW_ERROR_DUMP_FAMILY_8 = 8, -}; - -/** - * struct iwl_fw_error_dump_info - info on the device / firmware - * @device_family: the family of the device (7 / 8) - * @hw_step: the step of the device - * @fw_human_readable: human readable FW version - * @dev_human_readable: name of the device - * @bus_human_readable: name of the bus used - */ -struct iwl_fw_error_dump_info { - __le32 device_family; - __le32 hw_step; - u8 fw_human_readable[FW_VER_HUMAN_READABLE_SZ]; - u8 dev_human_readable[64]; - u8 bus_human_readable[8]; -} __packed; - -/** - * struct iwl_fw_error_dump_fw_mon - FW monitor data - * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer - * @fw_mon_base_ptr: base pointer of the data - * @fw_mon_cycle_cnt: number of wraparounds - * @reserved: for future use - * @data: captured data - */ -struct iwl_fw_error_dump_fw_mon { - __le32 fw_mon_wr_ptr; - __le32 fw_mon_base_ptr; - __le32 fw_mon_cycle_cnt; - __le32 reserved[3]; - u8 data[]; -} __packed; - -/** - * struct iwl_fw_error_dump_prph - periphery registers data - * @prph_start: address of the first register in this chunk - * @data: the content of the registers - */ -struct iwl_fw_error_dump_prph { - __le32 prph_start; - __le32 data[]; -}; - -enum iwl_fw_error_dump_mem_type { - IWL_FW_ERROR_DUMP_MEM_SRAM, - IWL_FW_ERROR_DUMP_MEM_SMEM, -}; - -/** - * struct iwl_fw_error_dump_mem - chunk of memory - * @type: %enum iwl_fw_error_dump_mem_type - * @offset: the offset from which the memory was read - * @data: the content of the memory - */ -struct iwl_fw_error_dump_mem { - __le32 type; - __le32 offset; - u8 data[]; -}; - -/** - * struct iwl_fw_error_dump_rb - content of an Receive Buffer - * @index: the index of the Receive Buffer in the Rx queue - * @rxq: the RB's Rx queue - * @reserved: - * @data: the content of the Receive Buffer - */ -struct iwl_fw_error_dump_rb { - __le32 index; - __le32 rxq; - __le32 reserved; - u8 data[]; -}; - -/** - * struct iwl_fw_error_dump_paging - content of the UMAC's image page - * block on DRAM - * @index: the index of the page block - * @reserved: - * @data: the content of the page block - */ -struct iwl_fw_error_dump_paging { - __le32 index; - __le32 reserved; - u8 data[]; -}; - -/** - * iwl_fw_error_next_data - advance fw error dump data pointer - * @data: previous data block - * Returns: next data block - */ -static inline struct iwl_fw_error_dump_data * -iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) -{ - return (void *)(data->data + le32_to_cpu(data->len)); -} - -/** - * enum iwl_fw_dbg_trigger - triggers available - * - * @FW_DBG_TRIGGER_USER: trigger log collection by user - * This should not be defined as a trigger to the driver, but a value the - * driver should set to indicate that the trigger was initiated by the - * user. - * @FW_DBG_TRIGGER_FW_ASSERT: trigger log collection when the firmware asserts - * @FW_DBG_TRIGGER_MISSED_BEACONS: trigger log collection when beacons are - * missed. - * @FW_DBG_TRIGGER_CHANNEL_SWITCH: trigger log collection upon channel switch. - * @FW_DBG_TRIGGER_FW_NOTIF: trigger log collection when the firmware sends a - * command response or a notification. - * @FW_DBG_TRIGGER_MLME: trigger log collection upon MLME event. - * @FW_DBG_TRIGGER_STATS: trigger log collection upon statistics threshold. - * @FW_DBG_TRIGGER_RSSI: trigger log collection when the rssi of the beacon - * goes below a threshold. - * @FW_DBG_TRIGGER_TXQ_TIMERS: configures the timers for the Tx queue hang - * detection. - * @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related - * events. - * @FW_DBG_TRIGGER_BA: trigger log collection upon BlockAck related events. - */ -enum iwl_fw_dbg_trigger { - FW_DBG_TRIGGER_INVALID = 0, - FW_DBG_TRIGGER_USER, - FW_DBG_TRIGGER_FW_ASSERT, - FW_DBG_TRIGGER_MISSED_BEACONS, - FW_DBG_TRIGGER_CHANNEL_SWITCH, - FW_DBG_TRIGGER_FW_NOTIF, - FW_DBG_TRIGGER_MLME, - FW_DBG_TRIGGER_STATS, - FW_DBG_TRIGGER_RSSI, - FW_DBG_TRIGGER_TXQ_TIMERS, - FW_DBG_TRIGGER_TIME_EVENT, - FW_DBG_TRIGGER_BA, - - /* must be last */ - FW_DBG_TRIGGER_MAX, -}; - -/** - * struct iwl_fw_error_dump_trigger_desc - describes the trigger condition - * @type: %enum iwl_fw_dbg_trigger - * @data: raw data about what happened - */ -struct iwl_fw_error_dump_trigger_desc { - __le32 type; - u8 data[]; -}; - -#endif /* __fw_error_dump_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h deleted file mode 100644 index 08303db0000f..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ /dev/null @@ -1,768 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __iwl_fw_file_h__ -#define __iwl_fw_file_h__ - -#include -#include - -/* v1/v2 uCode file layout */ -struct iwl_ucode_header { - __le32 ver; /* major/minor/API/serial */ - union { - struct { - __le32 inst_size; /* bytes of runtime code */ - __le32 data_size; /* bytes of runtime data */ - __le32 init_size; /* bytes of init code */ - __le32 init_data_size; /* bytes of init data */ - __le32 boot_size; /* bytes of bootstrap code */ - u8 data[0]; /* in same order as sizes */ - } v1; - struct { - __le32 build; /* build number */ - __le32 inst_size; /* bytes of runtime code */ - __le32 data_size; /* bytes of runtime data */ - __le32 init_size; /* bytes of init code */ - __le32 init_data_size; /* bytes of init data */ - __le32 boot_size; /* bytes of bootstrap code */ - u8 data[0]; /* in same order as sizes */ - } v2; - } u; -}; - -/* - * new TLV uCode file layout - * - * The new TLV file format contains TLVs, that each specify - * some piece of data. - */ - -enum iwl_ucode_tlv_type { - IWL_UCODE_TLV_INVALID = 0, /* unused */ - IWL_UCODE_TLV_INST = 1, - IWL_UCODE_TLV_DATA = 2, - IWL_UCODE_TLV_INIT = 3, - IWL_UCODE_TLV_INIT_DATA = 4, - IWL_UCODE_TLV_BOOT = 5, - IWL_UCODE_TLV_PROBE_MAX_LEN = 6, /* a u32 value */ - IWL_UCODE_TLV_PAN = 7, - IWL_UCODE_TLV_RUNT_EVTLOG_PTR = 8, - IWL_UCODE_TLV_RUNT_EVTLOG_SIZE = 9, - IWL_UCODE_TLV_RUNT_ERRLOG_PTR = 10, - IWL_UCODE_TLV_INIT_EVTLOG_PTR = 11, - IWL_UCODE_TLV_INIT_EVTLOG_SIZE = 12, - IWL_UCODE_TLV_INIT_ERRLOG_PTR = 13, - IWL_UCODE_TLV_ENHANCE_SENS_TBL = 14, - IWL_UCODE_TLV_PHY_CALIBRATION_SIZE = 15, - IWL_UCODE_TLV_WOWLAN_INST = 16, - IWL_UCODE_TLV_WOWLAN_DATA = 17, - IWL_UCODE_TLV_FLAGS = 18, - IWL_UCODE_TLV_SEC_RT = 19, - IWL_UCODE_TLV_SEC_INIT = 20, - IWL_UCODE_TLV_SEC_WOWLAN = 21, - IWL_UCODE_TLV_DEF_CALIB = 22, - IWL_UCODE_TLV_PHY_SKU = 23, - IWL_UCODE_TLV_SECURE_SEC_RT = 24, - IWL_UCODE_TLV_SECURE_SEC_INIT = 25, - IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26, - IWL_UCODE_TLV_NUM_OF_CPU = 27, - IWL_UCODE_TLV_CSCHEME = 28, - IWL_UCODE_TLV_API_CHANGES_SET = 29, - IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30, - IWL_UCODE_TLV_N_SCAN_CHANNELS = 31, - IWL_UCODE_TLV_PAGING = 32, - IWL_UCODE_TLV_SEC_RT_USNIFFER = 34, - IWL_UCODE_TLV_SDIO_ADMA_ADDR = 35, - IWL_UCODE_TLV_FW_VERSION = 36, - IWL_UCODE_TLV_FW_DBG_DEST = 38, - IWL_UCODE_TLV_FW_DBG_CONF = 39, - IWL_UCODE_TLV_FW_DBG_TRIGGER = 40, - IWL_UCODE_TLV_FW_GSCAN_CAPA = 50, -}; - -struct iwl_ucode_tlv { - __le32 type; /* see above */ - __le32 length; /* not including type/length fields */ - u8 data[0]; -}; - -#define IWL_TLV_UCODE_MAGIC 0x0a4c5749 -#define FW_VER_HUMAN_READABLE_SZ 64 - -struct iwl_tlv_ucode_header { - /* - * The TLV style ucode header is distinguished from - * the v1/v2 style header by first four bytes being - * zero, as such is an invalid combination of - * major/minor/API/serial versions. - */ - __le32 zero; - __le32 magic; - u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; - /* major/minor/API/serial or major in new format */ - __le32 ver; - __le32 build; - __le64 ignore; - /* - * The data contained herein has a TLV layout, - * see above for the TLV header and types. - * Note that each TLV is padded to a length - * that is a multiple of 4 for alignment. - */ - u8 data[0]; -}; - -/* - * ucode TLVs - * - * ability to get extension for: flags & capabilities from ucode binaries files - */ -struct iwl_ucode_api { - __le32 api_index; - __le32 api_flags; -} __packed; - -struct iwl_ucode_capa { - __le32 api_index; - __le32 api_capa; -} __packed; - -/** - * enum iwl_ucode_tlv_flag - ucode API flags - * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously - * was a separate TLV but moved here to save space. - * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behavior on hidden SSID, - * treats good CRC threshold as a boolean - * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w). - * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P. - * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS - * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD - * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan - * offload profile config command. - * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six - * (rather than two) IPv6 addresses - * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element - * from the probe request template. - * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version) - * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version) - * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC - * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and - * P2P client interfaces simultaneously if they are in different bindings. - * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and - * P2P client interfaces simultaneously if they are in same bindings. - * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD - * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save - * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering. - * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients - * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS. - */ -enum iwl_ucode_tlv_flag { - IWL_UCODE_TLV_FLAGS_PAN = BIT(0), - IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1), - IWL_UCODE_TLV_FLAGS_MFP = BIT(2), - IWL_UCODE_TLV_FLAGS_P2P = BIT(3), - IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4), - IWL_UCODE_TLV_FLAGS_SHORT_BL = BIT(7), - IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10), - IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12), - IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = BIT(15), - IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = BIT(16), - IWL_UCODE_TLV_FLAGS_P2P_PM = BIT(21), - IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM = BIT(22), - IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM = BIT(23), - IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24), - IWL_UCODE_TLV_FLAGS_EBS_SUPPORT = BIT(25), - IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26), - IWL_UCODE_TLV_FLAGS_BCAST_FILTERING = BIT(29), - IWL_UCODE_TLV_FLAGS_GO_UAPSD = BIT(30), -}; - -typedef unsigned int __bitwise__ iwl_ucode_tlv_api_t; - -/** - * enum iwl_ucode_tlv_api - ucode api - * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex - * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time - * longer than the passive one, which is essential for fragmented scan. - * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source. - * @IWL_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header - * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params - * @IWL_UCODE_TLV_API_NEW_VERSION: new versioning format - * @IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY: scan APIs use 8-level priority - * instead of 3. - * @IWL_UCODE_TLV_API_TX_POWER_CHAIN: TX power API has larger command size - * (command version 3) that supports per-chain limits - * - * @NUM_IWL_UCODE_TLV_API: number of bits used - */ -enum iwl_ucode_tlv_api { - IWL_UCODE_TLV_API_BT_COEX_SPLIT = (__force iwl_ucode_tlv_api_t)3, - IWL_UCODE_TLV_API_FRAGMENTED_SCAN = (__force iwl_ucode_tlv_api_t)8, - IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = (__force iwl_ucode_tlv_api_t)9, - IWL_UCODE_TLV_API_WIDE_CMD_HDR = (__force iwl_ucode_tlv_api_t)14, - IWL_UCODE_TLV_API_LQ_SS_PARAMS = (__force iwl_ucode_tlv_api_t)18, - IWL_UCODE_TLV_API_NEW_VERSION = (__force iwl_ucode_tlv_api_t)20, - IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY = (__force iwl_ucode_tlv_api_t)24, - IWL_UCODE_TLV_API_TX_POWER_CHAIN = (__force iwl_ucode_tlv_api_t)27, - - NUM_IWL_UCODE_TLV_API -#ifdef __CHECKER__ - /* sparse says it cannot increment the previous enum member */ - = 128 -#endif -}; - -typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; - -/** - * enum iwl_ucode_tlv_capa - ucode capabilities - * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3 - * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory - * @IWL_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan. - * @IWL_UCODE_TLV_CAPA_BEAMFORMER: supports Beamformer - * @IWL_UCODE_TLV_CAPA_TOF_SUPPORT: supports Time of Flight (802.11mc FTM) - * @IWL_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality - * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current - * tx power value into TPC Report action frame and Link Measurement Report - * action frame - * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports updating current - * channel in DS parameter set element in probe requests. - * @IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in - * probe requests. - * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests - * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA), - * which also implies support for the scheduler configuration command - * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching - * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command - * @IWL_UCODE_TLV_CAPA_DC2DC_SUPPORT: supports DC2DC Command - * @IWL_UCODE_TLV_CAPA_CSUM_SUPPORT: supports TCP Checksum Offload - * @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics - * @IWL_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running - * @IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC: ucode supports LAR updates with different - * sources for the MCC. This TLV bit is a future replacement to - * IWL_UCODE_TLV_API_WIFI_MCC_UPDATE. When either is set, multi-source LAR - * is supported. - * @IWL_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC - * @IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT: supports gscan - * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement - * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts - * - * @NUM_IWL_UCODE_TLV_CAPA: number of bits used - */ -enum iwl_ucode_tlv_capa { - IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = (__force iwl_ucode_tlv_capa_t)0, - IWL_UCODE_TLV_CAPA_LAR_SUPPORT = (__force iwl_ucode_tlv_capa_t)1, - IWL_UCODE_TLV_CAPA_UMAC_SCAN = (__force iwl_ucode_tlv_capa_t)2, - IWL_UCODE_TLV_CAPA_BEAMFORMER = (__force iwl_ucode_tlv_capa_t)3, - IWL_UCODE_TLV_CAPA_TOF_SUPPORT = (__force iwl_ucode_tlv_capa_t)5, - IWL_UCODE_TLV_CAPA_TDLS_SUPPORT = (__force iwl_ucode_tlv_capa_t)6, - IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT = (__force iwl_ucode_tlv_capa_t)8, - IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT = (__force iwl_ucode_tlv_capa_t)9, - IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT = (__force iwl_ucode_tlv_capa_t)10, - IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = (__force iwl_ucode_tlv_capa_t)11, - IWL_UCODE_TLV_CAPA_DQA_SUPPORT = (__force iwl_ucode_tlv_capa_t)12, - IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = (__force iwl_ucode_tlv_capa_t)13, - IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = (__force iwl_ucode_tlv_capa_t)18, - IWL_UCODE_TLV_CAPA_DC2DC_CONFIG_SUPPORT = (__force iwl_ucode_tlv_capa_t)19, - IWL_UCODE_TLV_CAPA_CSUM_SUPPORT = (__force iwl_ucode_tlv_capa_t)21, - IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS = (__force iwl_ucode_tlv_capa_t)22, - IWL_UCODE_TLV_CAPA_BT_COEX_PLCR = (__force iwl_ucode_tlv_capa_t)28, - IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC = (__force iwl_ucode_tlv_capa_t)29, - IWL_UCODE_TLV_CAPA_BT_COEX_RRC = (__force iwl_ucode_tlv_capa_t)30, - IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)31, - IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64, - IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, - - NUM_IWL_UCODE_TLV_CAPA -#ifdef __CHECKER__ - /* sparse says it cannot increment the previous enum member */ - = 128 -#endif -}; - -/* The default calibrate table size if not specified by firmware file */ -#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18 -#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19 -#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE 253 - -/* The default max probe length if not specified by the firmware file */ -#define IWL_DEFAULT_MAX_PROBE_LENGTH 200 - -/* - * For 16.0 uCode and above, there is no differentiation between sections, - * just an offset to the HW address. - */ -#define IWL_UCODE_SECTION_MAX 16 -#define CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC -#define PAGING_SEPARATOR_SECTION 0xAAAABBBB - -/* uCode version contains 4 values: Major/Minor/API/Serial */ -#define IWL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24) -#define IWL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16) -#define IWL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8) -#define IWL_UCODE_SERIAL(ver) ((ver) & 0x000000FF) - -/* - * Calibration control struct. - * Sent as part of the phy configuration command. - * @flow_trigger: bitmap for which calibrations to perform according to - * flow triggers. - * @event_trigger: bitmap for which calibrations to perform according to - * event triggers. - */ -struct iwl_tlv_calib_ctrl { - __le32 flow_trigger; - __le32 event_trigger; -} __packed; - -enum iwl_fw_phy_cfg { - FW_PHY_CFG_RADIO_TYPE_POS = 0, - FW_PHY_CFG_RADIO_TYPE = 0x3 << FW_PHY_CFG_RADIO_TYPE_POS, - FW_PHY_CFG_RADIO_STEP_POS = 2, - FW_PHY_CFG_RADIO_STEP = 0x3 << FW_PHY_CFG_RADIO_STEP_POS, - FW_PHY_CFG_RADIO_DASH_POS = 4, - FW_PHY_CFG_RADIO_DASH = 0x3 << FW_PHY_CFG_RADIO_DASH_POS, - FW_PHY_CFG_TX_CHAIN_POS = 16, - FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS, - FW_PHY_CFG_RX_CHAIN_POS = 20, - FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS, -}; - -#define IWL_UCODE_MAX_CS 1 - -/** - * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW. - * @cipher: a cipher suite selector - * @flags: cipher scheme flags (currently reserved for a future use) - * @hdr_len: a size of MPDU security header - * @pn_len: a size of PN - * @pn_off: an offset of pn from the beginning of the security header - * @key_idx_off: an offset of key index byte in the security header - * @key_idx_mask: a bit mask of key_idx bits - * @key_idx_shift: bit shift needed to get key_idx - * @mic_len: mic length in bytes - * @hw_cipher: a HW cipher index used in host commands - */ -struct iwl_fw_cipher_scheme { - __le32 cipher; - u8 flags; - u8 hdr_len; - u8 pn_len; - u8 pn_off; - u8 key_idx_off; - u8 key_idx_mask; - u8 key_idx_shift; - u8 mic_len; - u8 hw_cipher; -} __packed; - -enum iwl_fw_dbg_reg_operator { - CSR_ASSIGN, - CSR_SETBIT, - CSR_CLEARBIT, - - PRPH_ASSIGN, - PRPH_SETBIT, - PRPH_CLEARBIT, - - INDIRECT_ASSIGN, - INDIRECT_SETBIT, - INDIRECT_CLEARBIT, - - PRPH_BLOCKBIT, -}; - -/** - * struct iwl_fw_dbg_reg_op - an operation on a register - * - * @op: %enum iwl_fw_dbg_reg_operator - * @addr: offset of the register - * @val: value - */ -struct iwl_fw_dbg_reg_op { - u8 op; - u8 reserved[3]; - __le32 addr; - __le32 val; -} __packed; - -/** - * enum iwl_fw_dbg_monitor_mode - available monitor recording modes - * - * @SMEM_MODE: monitor stores the data in SMEM - * @EXTERNAL_MODE: monitor stores the data in allocated DRAM - * @MARBH_MODE: monitor stores the data in MARBH buffer - * @MIPI_MODE: monitor outputs the data through the MIPI interface - */ -enum iwl_fw_dbg_monitor_mode { - SMEM_MODE = 0, - EXTERNAL_MODE = 1, - MARBH_MODE = 2, - MIPI_MODE = 3, -}; - -/** - * struct iwl_fw_dbg_dest_tlv - configures the destination of the debug data - * - * @version: version of the TLV - currently 0 - * @monitor_mode: %enum iwl_fw_dbg_monitor_mode - * @size_power: buffer size will be 2^(size_power + 11) - * @base_reg: addr of the base addr register (PRPH) - * @end_reg: addr of the end addr register (PRPH) - * @write_ptr_reg: the addr of the reg of the write pointer - * @wrap_count: the addr of the reg of the wrap_count - * @base_shift: shift right of the base addr reg - * @end_shift: shift right of the end addr reg - * @reg_ops: array of registers operations - * - * This parses IWL_UCODE_TLV_FW_DBG_DEST - */ -struct iwl_fw_dbg_dest_tlv { - u8 version; - u8 monitor_mode; - u8 size_power; - u8 reserved; - __le32 base_reg; - __le32 end_reg; - __le32 write_ptr_reg; - __le32 wrap_count; - u8 base_shift; - u8 end_shift; - struct iwl_fw_dbg_reg_op reg_ops[0]; -} __packed; - -struct iwl_fw_dbg_conf_hcmd { - u8 id; - u8 reserved; - __le16 len; - u8 data[0]; -} __packed; - -/** - * enum iwl_fw_dbg_trigger_mode - triggers functionalities - * - * @IWL_FW_DBG_TRIGGER_START: when trigger occurs re-conf the dbg mechanism - * @IWL_FW_DBG_TRIGGER_STOP: when trigger occurs pull the dbg data - * @IWL_FW_DBG_TRIGGER_MONITOR_ONLY: when trigger occurs trigger is set to - * collect only monitor data - */ -enum iwl_fw_dbg_trigger_mode { - IWL_FW_DBG_TRIGGER_START = BIT(0), - IWL_FW_DBG_TRIGGER_STOP = BIT(1), - IWL_FW_DBG_TRIGGER_MONITOR_ONLY = BIT(2), -}; - -/** - * enum iwl_fw_dbg_trigger_vif_type - define the VIF type for a trigger - * @IWL_FW_DBG_CONF_VIF_ANY: any vif type - * @IWL_FW_DBG_CONF_VIF_IBSS: IBSS mode - * @IWL_FW_DBG_CONF_VIF_STATION: BSS mode - * @IWL_FW_DBG_CONF_VIF_AP: AP mode - * @IWL_FW_DBG_CONF_VIF_P2P_CLIENT: P2P Client mode - * @IWL_FW_DBG_CONF_VIF_P2P_GO: P2P GO mode - * @IWL_FW_DBG_CONF_VIF_P2P_DEVICE: P2P device - */ -enum iwl_fw_dbg_trigger_vif_type { - IWL_FW_DBG_CONF_VIF_ANY = NL80211_IFTYPE_UNSPECIFIED, - IWL_FW_DBG_CONF_VIF_IBSS = NL80211_IFTYPE_ADHOC, - IWL_FW_DBG_CONF_VIF_STATION = NL80211_IFTYPE_STATION, - IWL_FW_DBG_CONF_VIF_AP = NL80211_IFTYPE_AP, - IWL_FW_DBG_CONF_VIF_P2P_CLIENT = NL80211_IFTYPE_P2P_CLIENT, - IWL_FW_DBG_CONF_VIF_P2P_GO = NL80211_IFTYPE_P2P_GO, - IWL_FW_DBG_CONF_VIF_P2P_DEVICE = NL80211_IFTYPE_P2P_DEVICE, -}; - -/** - * struct iwl_fw_dbg_trigger_tlv - a TLV that describes the trigger - * @id: %enum iwl_fw_dbg_trigger - * @vif_type: %enum iwl_fw_dbg_trigger_vif_type - * @stop_conf_ids: bitmap of configurations this trigger relates to. - * if the mode is %IWL_FW_DBG_TRIGGER_STOP, then if the bit corresponding - * to the currently running configuration is set, the data should be - * collected. - * @stop_delay: how many milliseconds to wait before collecting the data - * after the STOP trigger fires. - * @mode: %enum iwl_fw_dbg_trigger_mode - can be stop / start of both - * @start_conf_id: if mode is %IWL_FW_DBG_TRIGGER_START, this defines what - * configuration should be applied when the triggers kicks in. - * @occurrences: number of occurrences. 0 means the trigger will never fire. - */ -struct iwl_fw_dbg_trigger_tlv { - __le32 id; - __le32 vif_type; - __le32 stop_conf_ids; - __le32 stop_delay; - u8 mode; - u8 start_conf_id; - __le16 occurrences; - __le32 reserved[2]; - - u8 data[0]; -} __packed; - -#define FW_DBG_START_FROM_ALIVE 0 -#define FW_DBG_CONF_MAX 32 -#define FW_DBG_INVALID 0xff - -/** - * struct iwl_fw_dbg_trigger_missed_bcon - configures trigger for missed beacons - * @stop_consec_missed_bcon: stop recording if threshold is crossed. - * @stop_consec_missed_bcon_since_rx: stop recording if threshold is crossed. - * @start_consec_missed_bcon: start recording if threshold is crossed. - * @start_consec_missed_bcon_since_rx: start recording if threshold is crossed. - * @reserved1: reserved - * @reserved2: reserved - */ -struct iwl_fw_dbg_trigger_missed_bcon { - __le32 stop_consec_missed_bcon; - __le32 stop_consec_missed_bcon_since_rx; - __le32 reserved2[2]; - __le32 start_consec_missed_bcon; - __le32 start_consec_missed_bcon_since_rx; - __le32 reserved1[2]; -} __packed; - -/** - * struct iwl_fw_dbg_trigger_cmd - configures trigger for messages from FW. - * cmds: the list of commands to trigger the collection on - */ -struct iwl_fw_dbg_trigger_cmd { - struct cmd { - u8 cmd_id; - u8 group_id; - } __packed cmds[16]; -} __packed; - -/** - * iwl_fw_dbg_trigger_stats - configures trigger for statistics - * @stop_offset: the offset of the value to be monitored - * @stop_threshold: the threshold above which to collect - * @start_offset: the offset of the value to be monitored - * @start_threshold: the threshold above which to start recording - */ -struct iwl_fw_dbg_trigger_stats { - __le32 stop_offset; - __le32 stop_threshold; - __le32 start_offset; - __le32 start_threshold; -} __packed; - -/** - * struct iwl_fw_dbg_trigger_low_rssi - trigger for low beacon RSSI - * @rssi: RSSI value to trigger at - */ -struct iwl_fw_dbg_trigger_low_rssi { - __le32 rssi; -} __packed; - -/** - * struct iwl_fw_dbg_trigger_mlme - configures trigger for mlme events - * @stop_auth_denied: number of denied authentication to collect - * @stop_auth_timeout: number of authentication timeout to collect - * @stop_rx_deauth: number of Rx deauth before to collect - * @stop_tx_deauth: number of Tx deauth before to collect - * @stop_assoc_denied: number of denied association to collect - * @stop_assoc_timeout: number of association timeout to collect - * @stop_connection_loss: number of connection loss to collect - * @start_auth_denied: number of denied authentication to start recording - * @start_auth_timeout: number of authentication timeout to start recording - * @start_rx_deauth: number of Rx deauth to start recording - * @start_tx_deauth: number of Tx deauth to start recording - * @start_assoc_denied: number of denied association to start recording - * @start_assoc_timeout: number of association timeout to start recording - * @start_connection_loss: number of connection loss to start recording - */ -struct iwl_fw_dbg_trigger_mlme { - u8 stop_auth_denied; - u8 stop_auth_timeout; - u8 stop_rx_deauth; - u8 stop_tx_deauth; - - u8 stop_assoc_denied; - u8 stop_assoc_timeout; - u8 stop_connection_loss; - u8 reserved; - - u8 start_auth_denied; - u8 start_auth_timeout; - u8 start_rx_deauth; - u8 start_tx_deauth; - - u8 start_assoc_denied; - u8 start_assoc_timeout; - u8 start_connection_loss; - u8 reserved2; -} __packed; - -/** - * struct iwl_fw_dbg_trigger_txq_timer - configures the Tx queue's timer - * @command_queue: timeout for the command queue in ms - * @bss: timeout for the queues of a BSS (except for TDLS queues) in ms - * @softap: timeout for the queues of a softAP in ms - * @p2p_go: timeout for the queues of a P2P GO in ms - * @p2p_client: timeout for the queues of a P2P client in ms - * @p2p_device: timeout for the queues of a P2P device in ms - * @ibss: timeout for the queues of an IBSS in ms - * @tdls: timeout for the queues of a TDLS station in ms - */ -struct iwl_fw_dbg_trigger_txq_timer { - __le32 command_queue; - __le32 bss; - __le32 softap; - __le32 p2p_go; - __le32 p2p_client; - __le32 p2p_device; - __le32 ibss; - __le32 tdls; - __le32 reserved[4]; -} __packed; - -/** - * struct iwl_fw_dbg_trigger_time_event - configures a time event trigger - * time_Events: a list of tuples . The driver will issue a - * trigger each time a time event notification that relates to time event - * id with one of the actions in the bitmap is received and - * BIT(notif->status) is set in status_bitmap. - * - */ -struct iwl_fw_dbg_trigger_time_event { - struct { - __le32 id; - __le32 action_bitmap; - __le32 status_bitmap; - } __packed time_events[16]; -} __packed; - -/** - * struct iwl_fw_dbg_trigger_ba - configures BlockAck related trigger - * rx_ba_start: tid bitmap to configure on what tid the trigger should occur - * when an Rx BlockAck session is started. - * rx_ba_stop: tid bitmap to configure on what tid the trigger should occur - * when an Rx BlockAck session is stopped. - * tx_ba_start: tid bitmap to configure on what tid the trigger should occur - * when a Tx BlockAck session is started. - * tx_ba_stop: tid bitmap to configure on what tid the trigger should occur - * when a Tx BlockAck session is stopped. - * rx_bar: tid bitmap to configure on what tid the trigger should occur - * when a BAR is received (for a Tx BlockAck session). - * tx_bar: tid bitmap to configure on what tid the trigger should occur - * when a BAR is send (for an Rx BlocAck session). - * frame_timeout: tid bitmap to configure on what tid the trigger should occur - * when a frame times out in the reodering buffer. - */ -struct iwl_fw_dbg_trigger_ba { - __le16 rx_ba_start; - __le16 rx_ba_stop; - __le16 tx_ba_start; - __le16 tx_ba_stop; - __le16 rx_bar; - __le16 tx_bar; - __le16 frame_timeout; -} __packed; - -/** - * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration. - * @id: conf id - * @usniffer: should the uSniffer image be used - * @num_of_hcmds: how many HCMDs to send are present here - * @hcmd: a variable length host command to be sent to apply the configuration. - * If there is more than one HCMD to send, they will appear one after the - * other and be sent in the order that they appear in. - * This parses IWL_UCODE_TLV_FW_DBG_CONF. The user can add up-to - * %FW_DBG_CONF_MAX configuration per run. - */ -struct iwl_fw_dbg_conf_tlv { - u8 id; - u8 usniffer; - u8 reserved; - u8 num_of_hcmds; - struct iwl_fw_dbg_conf_hcmd hcmd; -} __packed; - -/** - * struct iwl_fw_gscan_capabilities - gscan capabilities supported by FW - * @max_scan_cache_size: total space allocated for scan results (in bytes). - * @max_scan_buckets: maximum number of channel buckets. - * @max_ap_cache_per_scan: maximum number of APs that can be stored per scan. - * @max_rssi_sample_size: number of RSSI samples used for averaging RSSI. - * @max_scan_reporting_threshold: max possible report threshold. in percentage. - * @max_hotlist_aps: maximum number of entries for hotlist APs. - * @max_significant_change_aps: maximum number of entries for significant - * change APs. - * @max_bssid_history_entries: number of BSSID/RSSI entries that the device can - * hold. - */ -struct iwl_fw_gscan_capabilities { - __le32 max_scan_cache_size; - __le32 max_scan_buckets; - __le32 max_ap_cache_per_scan; - __le32 max_rssi_sample_size; - __le32 max_scan_reporting_threshold; - __le32 max_hotlist_aps; - __le32 max_significant_change_aps; - __le32 max_bssid_history_entries; -} __packed; - -#endif /* __iwl_fw_file_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h deleted file mode 100644 index 84ec0cefb62a..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ /dev/null @@ -1,322 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __iwl_fw_h__ -#define __iwl_fw_h__ -#include -#include - -#include "iwl-fw-file.h" -#include "iwl-fw-error-dump.h" - -/** - * enum iwl_ucode_type - * - * The type of ucode. - * - * @IWL_UCODE_REGULAR: Normal runtime ucode - * @IWL_UCODE_INIT: Initial ucode - * @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode - * @IWL_UCODE_REGULAR_USNIFFER: Normal runtime ucode when using usniffer image - */ -enum iwl_ucode_type { - IWL_UCODE_REGULAR, - IWL_UCODE_INIT, - IWL_UCODE_WOWLAN, - IWL_UCODE_REGULAR_USNIFFER, - IWL_UCODE_TYPE_MAX, -}; - -/* - * enumeration of ucode section. - * This enumeration is used directly for older firmware (before 16.0). - * For new firmware, there can be up to 4 sections (see below) but the - * first one packaged into the firmware file is the DATA section and - * some debugging code accesses that. - */ -enum iwl_ucode_sec { - IWL_UCODE_SECTION_DATA, - IWL_UCODE_SECTION_INST, -}; - -struct iwl_ucode_capabilities { - u32 max_probe_length; - u32 n_scan_channels; - u32 standard_phy_calibration_size; - u32 flags; - unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)]; - unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)]; -}; - -static inline bool -fw_has_api(const struct iwl_ucode_capabilities *capabilities, - iwl_ucode_tlv_api_t api) -{ - return test_bit((__force long)api, capabilities->_api); -} - -static inline bool -fw_has_capa(const struct iwl_ucode_capabilities *capabilities, - iwl_ucode_tlv_capa_t capa) -{ - return test_bit((__force long)capa, capabilities->_capa); -} - -/* one for each uCode image (inst/data, init/runtime/wowlan) */ -struct fw_desc { - const void *data; /* vmalloc'ed data */ - u32 len; /* size in bytes */ - u32 offset; /* offset in the device */ -}; - -struct fw_img { - struct fw_desc sec[IWL_UCODE_SECTION_MAX]; - bool is_dual_cpus; - u32 paging_mem_size; -}; - -struct iwl_sf_region { - u32 addr; - u32 size; -}; - -/* - * Block paging calculations - */ -#define PAGE_2_EXP_SIZE 12 /* 4K == 2^12 */ -#define FW_PAGING_SIZE BIT(PAGE_2_EXP_SIZE) /* page size is 4KB */ -#define PAGE_PER_GROUP_2_EXP_SIZE 3 -/* 8 pages per group */ -#define NUM_OF_PAGE_PER_GROUP BIT(PAGE_PER_GROUP_2_EXP_SIZE) -/* don't change, support only 32KB size */ -#define PAGING_BLOCK_SIZE (NUM_OF_PAGE_PER_GROUP * FW_PAGING_SIZE) -/* 32K == 2^15 */ -#define BLOCK_2_EXP_SIZE (PAGE_2_EXP_SIZE + PAGE_PER_GROUP_2_EXP_SIZE) - -/* - * Image paging calculations - */ -#define BLOCK_PER_IMAGE_2_EXP_SIZE 5 -/* 2^5 == 32 blocks per image */ -#define NUM_OF_BLOCK_PER_IMAGE BIT(BLOCK_PER_IMAGE_2_EXP_SIZE) -/* maximum image size 1024KB */ -#define MAX_PAGING_IMAGE_SIZE (NUM_OF_BLOCK_PER_IMAGE * PAGING_BLOCK_SIZE) - -/* Virtual address signature */ -#define PAGING_ADDR_SIG 0xAA000000 - -#define PAGING_CMD_IS_SECURED BIT(9) -#define PAGING_CMD_IS_ENABLED BIT(8) -#define PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS 0 -#define PAGING_TLV_SECURE_MASK 1 - -/** - * struct iwl_fw_paging - * @fw_paging_phys: page phy pointer - * @fw_paging_block: pointer to the allocated block - * @fw_paging_size: page size - */ -struct iwl_fw_paging { - dma_addr_t fw_paging_phys; - struct page *fw_paging_block; - u32 fw_paging_size; -}; - -/** - * struct iwl_fw_cscheme_list - a cipher scheme list - * @size: a number of entries - * @cs: cipher scheme entries - */ -struct iwl_fw_cscheme_list { - u8 size; - struct iwl_fw_cipher_scheme cs[]; -} __packed; - -/** - * struct iwl_gscan_capabilities - gscan capabilities supported by FW - * @max_scan_cache_size: total space allocated for scan results (in bytes). - * @max_scan_buckets: maximum number of channel buckets. - * @max_ap_cache_per_scan: maximum number of APs that can be stored per scan. - * @max_rssi_sample_size: number of RSSI samples used for averaging RSSI. - * @max_scan_reporting_threshold: max possible report threshold. in percentage. - * @max_hotlist_aps: maximum number of entries for hotlist APs. - * @max_significant_change_aps: maximum number of entries for significant - * change APs. - * @max_bssid_history_entries: number of BSSID/RSSI entries that the device can - * hold. - */ -struct iwl_gscan_capabilities { - u32 max_scan_cache_size; - u32 max_scan_buckets; - u32 max_ap_cache_per_scan; - u32 max_rssi_sample_size; - u32 max_scan_reporting_threshold; - u32 max_hotlist_aps; - u32 max_significant_change_aps; - u32 max_bssid_history_entries; -}; - -/** - * struct iwl_fw - variables associated with the firmware - * - * @ucode_ver: ucode version from the ucode file - * @fw_version: firmware version string - * @img: ucode image like ucode_rt, ucode_init, ucode_wowlan. - * @ucode_capa: capabilities parsed from the ucode file. - * @enhance_sensitivity_table: device can do enhanced sensitivity. - * @init_evtlog_ptr: event log offset for init ucode. - * @init_evtlog_size: event log size for init ucode. - * @init_errlog_ptr: error log offfset for init ucode. - * @inst_evtlog_ptr: event log offset for runtime ucode. - * @inst_evtlog_size: event log size for runtime ucode. - * @inst_errlog_ptr: error log offfset for runtime ucode. - * @mvm_fw: indicates this is MVM firmware - * @cipher_scheme: optional external cipher scheme. - * @human_readable: human readable version - * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until - * we get the ALIVE from the uCode - * @dbg_dest_tlv: points to the destination TLV for debug - * @dbg_conf_tlv: array of pointers to configuration TLVs for debug - * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries - * @dbg_trigger_tlv: array of pointers to triggers TLVs - * @dbg_trigger_tlv_len: lengths of the @dbg_trigger_tlv entries - * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv - */ -struct iwl_fw { - u32 ucode_ver; - - char fw_version[ETHTOOL_FWVERS_LEN]; - - /* ucode images */ - struct fw_img img[IWL_UCODE_TYPE_MAX]; - - struct iwl_ucode_capabilities ucode_capa; - bool enhance_sensitivity_table; - - u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr; - u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr; - - struct iwl_tlv_calib_ctrl default_calib[IWL_UCODE_TYPE_MAX]; - u32 phy_config; - u8 valid_tx_ant; - u8 valid_rx_ant; - - bool mvm_fw; - - struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; - u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; - - u32 sdio_adma_addr; - - struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; - struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX]; - size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX]; - struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX]; - size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX]; - u8 dbg_dest_reg_num; - struct iwl_gscan_capabilities gscan_capa; -}; - -static inline const char *get_fw_dbg_mode_string(int mode) -{ - switch (mode) { - case SMEM_MODE: - return "SMEM"; - case EXTERNAL_MODE: - return "EXTERNAL_DRAM"; - case MARBH_MODE: - return "MARBH"; - case MIPI_MODE: - return "MIPI"; - default: - return "UNKNOWN"; - } -} - -static inline bool -iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id) -{ - const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id]; - - if (!conf_tlv) - return false; - - return conf_tlv->usniffer; -} - -#define iwl_fw_dbg_trigger_enabled(fw, id) ({ \ - void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \ - unlikely(__dbg_trigger); \ -}) - -static inline struct iwl_fw_dbg_trigger_tlv* -iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, u8 id) -{ - if (WARN_ON(id >= ARRAY_SIZE(fw->dbg_trigger_tlv))) - return NULL; - - return fw->dbg_trigger_tlv[id]; -} - -#endif /* __iwl_fw_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c deleted file mode 100644 index 0bd9d4aad0c0..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ /dev/null @@ -1,289 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include - -#include "iwl-drv.h" -#include "iwl-io.h" -#include "iwl-csr.h" -#include "iwl-debug.h" -#include "iwl-prph.h" -#include "iwl-fh.h" - -void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val) -{ - trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val); - iwl_trans_write8(trans, ofs, val); -} -IWL_EXPORT_SYMBOL(iwl_write8); - -void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val) -{ - trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val); - iwl_trans_write32(trans, ofs, val); -} -IWL_EXPORT_SYMBOL(iwl_write32); - -u32 iwl_read32(struct iwl_trans *trans, u32 ofs) -{ - u32 val = iwl_trans_read32(trans, ofs); - - trace_iwlwifi_dev_ioread32(trans->dev, ofs, val); - return val; -} -IWL_EXPORT_SYMBOL(iwl_read32); - -#define IWL_POLL_INTERVAL 10 /* microseconds */ - -int iwl_poll_bit(struct iwl_trans *trans, u32 addr, - u32 bits, u32 mask, int timeout) -{ - int t = 0; - - do { - if ((iwl_read32(trans, addr) & mask) == (bits & mask)) - return t; - udelay(IWL_POLL_INTERVAL); - t += IWL_POLL_INTERVAL; - } while (t < timeout); - - return -ETIMEDOUT; -} -IWL_EXPORT_SYMBOL(iwl_poll_bit); - -u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) -{ - u32 value = 0x5a5a5a5a; - unsigned long flags; - if (iwl_trans_grab_nic_access(trans, false, &flags)) { - value = iwl_read32(trans, reg); - iwl_trans_release_nic_access(trans, &flags); - } - - return value; -} -IWL_EXPORT_SYMBOL(iwl_read_direct32); - -void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) -{ - unsigned long flags; - - if (iwl_trans_grab_nic_access(trans, false, &flags)) { - iwl_write32(trans, reg, value); - iwl_trans_release_nic_access(trans, &flags); - } -} -IWL_EXPORT_SYMBOL(iwl_write_direct32); - -int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, - int timeout) -{ - int t = 0; - - do { - if ((iwl_read_direct32(trans, addr) & mask) == mask) - return t; - udelay(IWL_POLL_INTERVAL); - t += IWL_POLL_INTERVAL; - } while (t < timeout); - - return -ETIMEDOUT; -} -IWL_EXPORT_SYMBOL(iwl_poll_direct_bit); - -u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs) -{ - u32 val = iwl_trans_read_prph(trans, ofs); - trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val); - return val; -} - -void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) -{ - trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val); - iwl_trans_write_prph(trans, ofs, val); -} - -u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs) -{ - unsigned long flags; - u32 val = 0x5a5a5a5a; - - if (iwl_trans_grab_nic_access(trans, false, &flags)) { - val = __iwl_read_prph(trans, ofs); - iwl_trans_release_nic_access(trans, &flags); - } - return val; -} -IWL_EXPORT_SYMBOL(iwl_read_prph); - -void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) -{ - unsigned long flags; - - if (iwl_trans_grab_nic_access(trans, false, &flags)) { - __iwl_write_prph(trans, ofs, val); - iwl_trans_release_nic_access(trans, &flags); - } -} -IWL_EXPORT_SYMBOL(iwl_write_prph); - -int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, - u32 bits, u32 mask, int timeout) -{ - int t = 0; - - do { - if ((iwl_read_prph(trans, addr) & mask) == (bits & mask)) - return t; - udelay(IWL_POLL_INTERVAL); - t += IWL_POLL_INTERVAL; - } while (t < timeout); - - return -ETIMEDOUT; -} - -void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) -{ - unsigned long flags; - - if (iwl_trans_grab_nic_access(trans, false, &flags)) { - __iwl_write_prph(trans, ofs, - __iwl_read_prph(trans, ofs) | mask); - iwl_trans_release_nic_access(trans, &flags); - } -} -IWL_EXPORT_SYMBOL(iwl_set_bits_prph); - -void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, - u32 bits, u32 mask) -{ - unsigned long flags; - - if (iwl_trans_grab_nic_access(trans, false, &flags)) { - __iwl_write_prph(trans, ofs, - (__iwl_read_prph(trans, ofs) & mask) | bits); - iwl_trans_release_nic_access(trans, &flags); - } -} -IWL_EXPORT_SYMBOL(iwl_set_bits_mask_prph); - -void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) -{ - unsigned long flags; - u32 val; - - if (iwl_trans_grab_nic_access(trans, false, &flags)) { - val = __iwl_read_prph(trans, ofs); - __iwl_write_prph(trans, ofs, (val & ~mask)); - iwl_trans_release_nic_access(trans, &flags); - } -} -IWL_EXPORT_SYMBOL(iwl_clear_bits_prph); - -void iwl_force_nmi(struct iwl_trans *trans) -{ - if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { - iwl_write_prph(trans, DEVICE_SET_NMI_REG, - DEVICE_SET_NMI_VAL_DRV); - iwl_write_prph(trans, DEVICE_SET_NMI_REG, - DEVICE_SET_NMI_VAL_HW); - } else { - iwl_write_prph(trans, DEVICE_SET_NMI_8000_REG, - DEVICE_SET_NMI_8000_VAL); - iwl_write_prph(trans, DEVICE_SET_NMI_REG, - DEVICE_SET_NMI_VAL_DRV); - } -} -IWL_EXPORT_SYMBOL(iwl_force_nmi); - -static const char *get_fh_string(int cmd) -{ -#define IWL_CMD(x) case x: return #x - switch (cmd) { - IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); - IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); - IWL_CMD(FH_RSCSR_CHNL0_WPTR); - IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); - IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); - IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); - IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); - IWL_CMD(FH_TSSR_TX_STATUS_REG); - IWL_CMD(FH_TSSR_TX_ERROR_REG); - default: - return "UNKNOWN"; - } -#undef IWL_CMD -} - -int iwl_dump_fh(struct iwl_trans *trans, char **buf) -{ - int i; - static const u32 fh_tbl[] = { - FH_RSCSR_CHNL0_STTS_WPTR_REG, - FH_RSCSR_CHNL0_RBDCB_BASE_REG, - FH_RSCSR_CHNL0_WPTR, - FH_MEM_RCSR_CHNL0_CONFIG_REG, - FH_MEM_RSSR_SHARED_CTRL_REG, - FH_MEM_RSSR_RX_STATUS_REG, - FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, - FH_TSSR_TX_STATUS_REG, - FH_TSSR_TX_ERROR_REG - }; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (buf) { - int pos = 0; - size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; - - *buf = kmalloc(bufsz, GFP_KERNEL); - if (!*buf) - return -ENOMEM; - - pos += scnprintf(*buf + pos, bufsz - pos, - "FH register values:\n"); - - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) - pos += scnprintf(*buf + pos, bufsz - pos, - " %34s: 0X%08x\n", - get_fh_string(fh_tbl[i]), - iwl_read_direct32(trans, fh_tbl[i])); - - return pos; - } -#endif - - IWL_ERR(trans, "FH register values:\n"); - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) - IWL_ERR(trans, " %34s: 0X%08x\n", - get_fh_string(fh_tbl[i]), - iwl_read_direct32(trans, fh_tbl[i])); - - return 0; -} diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h deleted file mode 100644 index 501d0560c061..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ /dev/null @@ -1,73 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __iwl_io_h__ -#define __iwl_io_h__ - -#include "iwl-devtrace.h" -#include "iwl-trans.h" - -void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val); -void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val); -u32 iwl_read32(struct iwl_trans *trans, u32 ofs); - -static inline void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask) -{ - iwl_trans_set_bits_mask(trans, reg, mask, mask); -} - -static inline void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask) -{ - iwl_trans_set_bits_mask(trans, reg, mask, 0); -} - -int iwl_poll_bit(struct iwl_trans *trans, u32 addr, - u32 bits, u32 mask, int timeout); -int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, - int timeout); - -u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg); -void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value); - - -u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs); -u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs); -void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); -void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); -int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, - u32 bits, u32 mask, int timeout); -void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); -void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, - u32 bits, u32 mask); -void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); -void iwl_force_nmi(struct iwl_trans *trans); - -/* Error handling */ -int iwl_dump_fh(struct iwl_trans *trans, char **buf); - -#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h deleted file mode 100644 index ac2b90df8413..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ /dev/null @@ -1,129 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __iwl_modparams_h__ -#define __iwl_modparams_h__ - -#include -#include -#include -#include - -extern struct iwl_mod_params iwlwifi_mod_params; - -enum iwl_power_level { - IWL_POWER_INDEX_1, - IWL_POWER_INDEX_2, - IWL_POWER_INDEX_3, - IWL_POWER_INDEX_4, - IWL_POWER_INDEX_5, - IWL_POWER_NUM -}; - -enum iwl_disable_11n { - IWL_DISABLE_HT_ALL = BIT(0), - IWL_DISABLE_HT_TXAGG = BIT(1), - IWL_DISABLE_HT_RXAGG = BIT(2), - IWL_ENABLE_HT_TXAGG = BIT(3), -}; - -/** - * struct iwl_mod_params - * - * Holds the module parameters - * - * @sw_crypto: using hardware encryption, default = 0 - * @disable_11n: disable 11n capabilities, default = 0, - * use IWL_[DIS,EN]ABLE_HT_* constants - * @amsdu_size_8K: enable 8K amsdu size, default = 0 - * @restart_fw: restart firmware, default = 1 - * @bt_coex_active: enable bt coex, default = true - * @led_mode: system default, default = 0 - * @power_save: enable power save, default = false - * @power_level: power level, default = 1 - * @debug_level: levels are IWL_DL_* - * @ant_coupling: antenna coupling in dB, default = 0 - * @d0i3_disable: disable d0i3, default = 1, - * @lar_disable: disable LAR (regulatory), default = 0 - * @fw_monitor: allow to use firmware monitor - */ -struct iwl_mod_params { - int sw_crypto; - unsigned int disable_11n; - int amsdu_size_8K; - bool restart_fw; - bool bt_coex_active; - int led_mode; - bool power_save; - int power_level; -#ifdef CONFIG_IWLWIFI_DEBUG - u32 debug_level; -#endif - int ant_coupling; - char *nvm_file; - bool uapsd_disable; - bool d0i3_disable; - bool lar_disable; - bool fw_monitor; -}; - -#endif /* #__iwl_modparams_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c deleted file mode 100644 index 6caf2affbbb5..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c +++ /dev/null @@ -1,193 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include -#include - -#include "iwl-drv.h" -#include "iwl-notif-wait.h" - - -void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait) -{ - spin_lock_init(¬if_wait->notif_wait_lock); - INIT_LIST_HEAD(¬if_wait->notif_waits); - init_waitqueue_head(¬if_wait->notif_waitq); -} -IWL_EXPORT_SYMBOL(iwl_notification_wait_init); - -void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt) -{ - bool triggered = false; - - if (!list_empty(¬if_wait->notif_waits)) { - struct iwl_notification_wait *w; - - spin_lock(¬if_wait->notif_wait_lock); - list_for_each_entry(w, ¬if_wait->notif_waits, list) { - int i; - bool found = false; - - /* - * If it already finished (triggered) or has been - * aborted then don't evaluate it again to avoid races, - * Otherwise the function could be called again even - * though it returned true before - */ - if (w->triggered || w->aborted) - continue; - - for (i = 0; i < w->n_cmds; i++) { - if (w->cmds[i] == - WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) { - found = true; - break; - } - } - if (!found) - continue; - - if (!w->fn || w->fn(notif_wait, pkt, w->fn_data)) { - w->triggered = true; - triggered = true; - } - } - spin_unlock(¬if_wait->notif_wait_lock); - - } - - if (triggered) - wake_up_all(¬if_wait->notif_waitq); -} -IWL_EXPORT_SYMBOL(iwl_notification_wait_notify); - -void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) -{ - struct iwl_notification_wait *wait_entry; - - spin_lock(¬if_wait->notif_wait_lock); - list_for_each_entry(wait_entry, ¬if_wait->notif_waits, list) - wait_entry->aborted = true; - spin_unlock(¬if_wait->notif_wait_lock); - - wake_up_all(¬if_wait->notif_waitq); -} -IWL_EXPORT_SYMBOL(iwl_abort_notification_waits); - -void -iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait, - struct iwl_notification_wait *wait_entry, - const u16 *cmds, int n_cmds, - bool (*fn)(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data), - void *fn_data) -{ - if (WARN_ON(n_cmds > MAX_NOTIF_CMDS)) - n_cmds = MAX_NOTIF_CMDS; - - wait_entry->fn = fn; - wait_entry->fn_data = fn_data; - wait_entry->n_cmds = n_cmds; - memcpy(wait_entry->cmds, cmds, n_cmds * sizeof(u16)); - wait_entry->triggered = false; - wait_entry->aborted = false; - - spin_lock_bh(¬if_wait->notif_wait_lock); - list_add(&wait_entry->list, ¬if_wait->notif_waits); - spin_unlock_bh(¬if_wait->notif_wait_lock); -} -IWL_EXPORT_SYMBOL(iwl_init_notification_wait); - -int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, - struct iwl_notification_wait *wait_entry, - unsigned long timeout) -{ - int ret; - - ret = wait_event_timeout(notif_wait->notif_waitq, - wait_entry->triggered || wait_entry->aborted, - timeout); - - spin_lock_bh(¬if_wait->notif_wait_lock); - list_del(&wait_entry->list); - spin_unlock_bh(¬if_wait->notif_wait_lock); - - if (wait_entry->aborted) - return -EIO; - - /* return value is always >= 0 */ - if (ret <= 0) - return -ETIMEDOUT; - return 0; -} -IWL_EXPORT_SYMBOL(iwl_wait_notification); - -void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, - struct iwl_notification_wait *wait_entry) -{ - spin_lock_bh(¬if_wait->notif_wait_lock); - list_del(&wait_entry->list); - spin_unlock_bh(¬if_wait->notif_wait_lock); -} -IWL_EXPORT_SYMBOL(iwl_remove_notification); diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/iwlwifi/iwl-notif-wait.h deleted file mode 100644 index dbe8234521de..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h +++ /dev/null @@ -1,139 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __iwl_notif_wait_h__ -#define __iwl_notif_wait_h__ - -#include - -#include "iwl-trans.h" - -struct iwl_notif_wait_data { - struct list_head notif_waits; - spinlock_t notif_wait_lock; - wait_queue_head_t notif_waitq; -}; - -#define MAX_NOTIF_CMDS 5 - -/** - * struct iwl_notification_wait - notification wait entry - * @list: list head for global list - * @fn: Function called with the notification. If the function - * returns true, the wait is over, if it returns false then - * the waiter stays blocked. If no function is given, any - * of the listed commands will unblock the waiter. - * @cmds: command IDs - * @n_cmds: number of command IDs - * @triggered: waiter should be woken up - * @aborted: wait was aborted - * - * This structure is not used directly, to wait for a - * notification declare it on the stack, and call - * iwlagn_init_notification_wait() with appropriate - * parameters. Then do whatever will cause the ucode - * to notify the driver, and to wait for that then - * call iwlagn_wait_notification(). - * - * Each notification is one-shot. If at some point we - * need to support multi-shot notifications (which - * can't be allocated on the stack) we need to modify - * the code for them. - */ -struct iwl_notification_wait { - struct list_head list; - - bool (*fn)(struct iwl_notif_wait_data *notif_data, - struct iwl_rx_packet *pkt, void *data); - void *fn_data; - - u16 cmds[MAX_NOTIF_CMDS]; - u8 n_cmds; - bool triggered, aborted; -}; - - -/* caller functions */ -void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_data); -void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_data, - struct iwl_rx_packet *pkt); -void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_data); - -/* user functions */ -void __acquires(wait_entry) -iwl_init_notification_wait(struct iwl_notif_wait_data *notif_data, - struct iwl_notification_wait *wait_entry, - const u16 *cmds, int n_cmds, - bool (*fn)(struct iwl_notif_wait_data *notif_data, - struct iwl_rx_packet *pkt, void *data), - void *fn_data); - -int __must_check __releases(wait_entry) -iwl_wait_notification(struct iwl_notif_wait_data *notif_data, - struct iwl_notification_wait *wait_entry, - unsigned long timeout); - -void __releases(wait_entry) -iwl_remove_notification(struct iwl_notif_wait_data *notif_data, - struct iwl_notification_wait *wait_entry); - -#endif /* __iwl_notif_wait_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c deleted file mode 100644 index d82984912e04..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ /dev/null @@ -1,844 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ -#include -#include -#include -#include -#include -#include "iwl-drv.h" -#include "iwl-modparams.h" -#include "iwl-nvm-parse.h" - -/* NVM offsets (in words) definitions */ -enum wkp_nvm_offsets { - /* NVM HW-Section offset (in words) definitions */ - HW_ADDR = 0x15, - - /* NVM SW-Section offset (in words) definitions */ - NVM_SW_SECTION = 0x1C0, - NVM_VERSION = 0, - RADIO_CFG = 1, - SKU = 2, - N_HW_ADDRS = 3, - NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION, - - /* NVM calibration section offset (in words) definitions */ - NVM_CALIB_SECTION = 0x2B8, - XTAL_CALIB = 0x316 - NVM_CALIB_SECTION -}; - -enum family_8000_nvm_offsets { - /* NVM HW-Section offset (in words) definitions */ - HW_ADDR0_WFPM_FAMILY_8000 = 0x12, - HW_ADDR1_WFPM_FAMILY_8000 = 0x16, - HW_ADDR0_PCIE_FAMILY_8000 = 0x8A, - HW_ADDR1_PCIE_FAMILY_8000 = 0x8E, - MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1, - - /* NVM SW-Section offset (in words) definitions */ - NVM_SW_SECTION_FAMILY_8000 = 0x1C0, - NVM_VERSION_FAMILY_8000 = 0, - RADIO_CFG_FAMILY_8000 = 0, - SKU_FAMILY_8000 = 2, - N_HW_ADDRS_FAMILY_8000 = 3, - - /* NVM REGULATORY -Section offset (in words) definitions */ - NVM_CHANNELS_FAMILY_8000 = 0, - NVM_LAR_OFFSET_FAMILY_8000_OLD = 0x4C7, - NVM_LAR_OFFSET_FAMILY_8000 = 0x507, - NVM_LAR_ENABLED_FAMILY_8000 = 0x7, - - /* NVM calibration section offset (in words) definitions */ - NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8, - XTAL_CALIB_FAMILY_8000 = 0x316 - NVM_CALIB_SECTION_FAMILY_8000 -}; - -/* SKU Capabilities (actual values from NVM definition) */ -enum nvm_sku_bits { - NVM_SKU_CAP_BAND_24GHZ = BIT(0), - NVM_SKU_CAP_BAND_52GHZ = BIT(1), - NVM_SKU_CAP_11N_ENABLE = BIT(2), - NVM_SKU_CAP_11AC_ENABLE = BIT(3), - NVM_SKU_CAP_MIMO_DISABLE = BIT(5), -}; - -/* - * These are the channel numbers in the order that they are stored in the NVM - */ -static const u8 iwl_nvm_channels[] = { - /* 2.4 GHz */ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - /* 5 GHz */ - 36, 40, 44 , 48, 52, 56, 60, 64, - 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, - 149, 153, 157, 161, 165 -}; - -static const u8 iwl_nvm_channels_family_8000[] = { - /* 2.4 GHz */ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - /* 5 GHz */ - 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, - 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, - 149, 153, 157, 161, 165, 169, 173, 177, 181 -}; - -#define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels) -#define IWL_NUM_CHANNELS_FAMILY_8000 ARRAY_SIZE(iwl_nvm_channels_family_8000) -#define NUM_2GHZ_CHANNELS 14 -#define NUM_2GHZ_CHANNELS_FAMILY_8000 14 -#define FIRST_2GHZ_HT_MINUS 5 -#define LAST_2GHZ_HT_PLUS 9 -#define LAST_5GHZ_HT 165 -#define LAST_5GHZ_HT_FAMILY_8000 181 -#define N_HW_ADDR_MASK 0xF - -/* rate data (static) */ -static struct ieee80211_rate iwl_cfg80211_rates[] = { - { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, }, - { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1, - .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, - { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2, - .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, - { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3, - .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, - { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, }, - { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, }, - { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, }, - { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, }, - { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, }, - { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, }, - { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, }, - { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, }, -}; -#define RATES_24_OFFS 0 -#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates) -#define RATES_52_OFFS 4 -#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS) - -/** - * enum iwl_nvm_channel_flags - channel flags in NVM - * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo - * @NVM_CHANNEL_IBSS: usable as an IBSS channel - * @NVM_CHANNEL_ACTIVE: active scanning allowed - * @NVM_CHANNEL_RADAR: radar detection required - * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed - * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS - * on same channel on 2.4 or same UNII band on 5.2 - * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?) - * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?) - * @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?) - * @NVM_CHANNEL_160MHZ: 160 MHz channel okay (?) - */ -enum iwl_nvm_channel_flags { - NVM_CHANNEL_VALID = BIT(0), - NVM_CHANNEL_IBSS = BIT(1), - NVM_CHANNEL_ACTIVE = BIT(3), - NVM_CHANNEL_RADAR = BIT(4), - NVM_CHANNEL_INDOOR_ONLY = BIT(5), - NVM_CHANNEL_GO_CONCURRENT = BIT(6), - NVM_CHANNEL_WIDE = BIT(8), - NVM_CHANNEL_40MHZ = BIT(9), - NVM_CHANNEL_80MHZ = BIT(10), - NVM_CHANNEL_160MHZ = BIT(11), -}; - -#define CHECK_AND_PRINT_I(x) \ - ((ch_flags & NVM_CHANNEL_##x) ? # x " " : "") - -static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz, - u16 nvm_flags, const struct iwl_cfg *cfg) -{ - u32 flags = IEEE80211_CHAN_NO_HT40; - u32 last_5ghz_ht = LAST_5GHZ_HT; - - if (cfg->device_family == IWL_DEVICE_FAMILY_8000) - last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000; - - if (!is_5ghz && (nvm_flags & NVM_CHANNEL_40MHZ)) { - if (ch_num <= LAST_2GHZ_HT_PLUS) - flags &= ~IEEE80211_CHAN_NO_HT40PLUS; - if (ch_num >= FIRST_2GHZ_HT_MINUS) - flags &= ~IEEE80211_CHAN_NO_HT40MINUS; - } else if (ch_num <= last_5ghz_ht && (nvm_flags & NVM_CHANNEL_40MHZ)) { - if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) - flags &= ~IEEE80211_CHAN_NO_HT40PLUS; - else - flags &= ~IEEE80211_CHAN_NO_HT40MINUS; - } - if (!(nvm_flags & NVM_CHANNEL_80MHZ)) - flags |= IEEE80211_CHAN_NO_80MHZ; - if (!(nvm_flags & NVM_CHANNEL_160MHZ)) - flags |= IEEE80211_CHAN_NO_160MHZ; - - if (!(nvm_flags & NVM_CHANNEL_IBSS)) - flags |= IEEE80211_CHAN_NO_IR; - - if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) - flags |= IEEE80211_CHAN_NO_IR; - - if (nvm_flags & NVM_CHANNEL_RADAR) - flags |= IEEE80211_CHAN_RADAR; - - if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) - flags |= IEEE80211_CHAN_INDOOR_ONLY; - - /* Set the GO concurrent flag only in case that NO_IR is set. - * Otherwise it is meaningless - */ - if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && - (flags & IEEE80211_CHAN_NO_IR)) - flags |= IEEE80211_CHAN_IR_CONCURRENT; - - return flags; -} - -static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - const __le16 * const nvm_ch_flags, - bool lar_supported) -{ - int ch_idx; - int n_channels = 0; - struct ieee80211_channel *channel; - u16 ch_flags; - bool is_5ghz; - int num_of_ch, num_2ghz_channels; - const u8 *nvm_chan; - - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { - num_of_ch = IWL_NUM_CHANNELS; - nvm_chan = &iwl_nvm_channels[0]; - num_2ghz_channels = NUM_2GHZ_CHANNELS; - } else { - num_of_ch = IWL_NUM_CHANNELS_FAMILY_8000; - nvm_chan = &iwl_nvm_channels_family_8000[0]; - num_2ghz_channels = NUM_2GHZ_CHANNELS_FAMILY_8000; - } - - for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { - ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx); - - if (ch_idx >= num_2ghz_channels && - !data->sku_cap_band_52GHz_enable) - continue; - - if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) { - /* - * Channels might become valid later if lar is - * supported, hence we still want to add them to - * the list of supported channels to cfg80211. - */ - IWL_DEBUG_EEPROM(dev, - "Ch. %d Flags %x [%sGHz] - No traffic\n", - nvm_chan[ch_idx], - ch_flags, - (ch_idx >= num_2ghz_channels) ? - "5.2" : "2.4"); - continue; - } - - channel = &data->channels[n_channels]; - n_channels++; - - channel->hw_value = nvm_chan[ch_idx]; - channel->band = (ch_idx < num_2ghz_channels) ? - IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - channel->center_freq = - ieee80211_channel_to_frequency( - channel->hw_value, channel->band); - - /* Initialize regulatory-based run-time data */ - - /* - * Default value - highest tx power value. max_power - * is not used in mvm, and is used for backwards compatibility - */ - channel->max_power = IWL_DEFAULT_MAX_TX_POWER; - is_5ghz = channel->band == IEEE80211_BAND_5GHZ; - - /* don't put limitations in case we're using LAR */ - if (!lar_supported) - channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx], - ch_idx, is_5ghz, - ch_flags, cfg); - else - channel->flags = 0; - - IWL_DEBUG_EEPROM(dev, - "Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", - channel->hw_value, - is_5ghz ? "5.2" : "2.4", - CHECK_AND_PRINT_I(VALID), - CHECK_AND_PRINT_I(IBSS), - CHECK_AND_PRINT_I(ACTIVE), - CHECK_AND_PRINT_I(RADAR), - CHECK_AND_PRINT_I(WIDE), - CHECK_AND_PRINT_I(INDOOR_ONLY), - CHECK_AND_PRINT_I(GO_CONCURRENT), - ch_flags, - channel->max_power, - ((ch_flags & NVM_CHANNEL_IBSS) && - !(ch_flags & NVM_CHANNEL_RADAR)) - ? "" : "not "); - } - - return n_channels; -} - -static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - struct ieee80211_sta_vht_cap *vht_cap, - u8 tx_chains, u8 rx_chains) -{ - int num_rx_ants = num_of_ant(rx_chains); - int num_tx_ants = num_of_ant(tx_chains); - unsigned int max_ampdu_exponent = (cfg->max_vht_ampdu_exponent ?: - IEEE80211_VHT_MAX_AMPDU_1024K); - - 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 | - 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | - max_ampdu_exponent << - IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; - - if (cfg->ht_params->ldpc) - vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC; - - if (data->sku_cap_mimo_disabled) { - num_rx_ants = 1; - num_tx_ants = 1; - } - - if (num_tx_ants > 1) - vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC; - else - vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; - - if (iwlwifi_mod_params.amsdu_size_8K) - vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; - - vht_cap->vht_mcs.rx_mcs_map = - cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); - - if (num_rx_ants == 1 || cfg->rx_with_siso_diversity) { - vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN; - /* this works because NOT_SUPPORTED == 3 */ - vht_cap->vht_mcs.rx_mcs_map |= - cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2); - } - - vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; -} - -static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - const __le16 *ch_section, - u8 tx_chains, u8 rx_chains, bool lar_supported) -{ - int n_channels; - int n_used = 0; - struct ieee80211_supported_band *sband; - - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) - n_channels = iwl_init_channel_map( - dev, cfg, data, - &ch_section[NVM_CHANNELS], lar_supported); - else - n_channels = iwl_init_channel_map( - dev, cfg, data, - &ch_section[NVM_CHANNELS_FAMILY_8000], - lar_supported); - - sband = &data->bands[IEEE80211_BAND_2GHZ]; - sband->band = IEEE80211_BAND_2GHZ; - sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; - sband->n_bitrates = N_RATES_24; - n_used += iwl_init_sband_channels(data, sband, n_channels, - IEEE80211_BAND_2GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ, - tx_chains, rx_chains); - - sband = &data->bands[IEEE80211_BAND_5GHZ]; - sband->band = IEEE80211_BAND_5GHZ; - sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; - sband->n_bitrates = N_RATES_52; - n_used += iwl_init_sband_channels(data, sband, n_channels, - IEEE80211_BAND_5GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, - tx_chains, rx_chains); - if (data->sku_cap_11ac_enable) - iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap, - tx_chains, rx_chains); - - if (n_channels != n_used) - IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", - n_used, n_channels); -} - -static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw, - const __le16 *phy_sku) -{ - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) - return le16_to_cpup(nvm_sw + SKU); - - return le32_to_cpup((__le32 *)(phy_sku + SKU_FAMILY_8000)); -} - -static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw) -{ - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) - return le16_to_cpup(nvm_sw + NVM_VERSION); - else - return le32_to_cpup((__le32 *)(nvm_sw + - NVM_VERSION_FAMILY_8000)); -} - -static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw, - const __le16 *phy_sku) -{ - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) - return le16_to_cpup(nvm_sw + RADIO_CFG); - - return le32_to_cpup((__le32 *)(phy_sku + RADIO_CFG_FAMILY_8000)); - -} - -static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw) -{ - int n_hw_addr; - - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) - return le16_to_cpup(nvm_sw + N_HW_ADDRS); - - n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000)); - - return n_hw_addr & N_HW_ADDR_MASK; -} - -static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - u32 radio_cfg) -{ - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { - data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg); - data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg); - data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg); - data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg); - return; - } - - /* set the radio configuration for family 8000 */ - data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK_FAMILY_8000(radio_cfg); - data->radio_cfg_step = NVM_RF_CFG_STEP_MSK_FAMILY_8000(radio_cfg); - data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK_FAMILY_8000(radio_cfg); - data->radio_cfg_pnum = NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(radio_cfg); - data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(radio_cfg); - data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(radio_cfg); -} - -static void iwl_set_hw_address(const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - const __le16 *nvm_sec) -{ - const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR); - - /* The byte order is little endian 16 bit, meaning 214365 */ - data->hw_addr[0] = hw_addr[1]; - data->hw_addr[1] = hw_addr[0]; - data->hw_addr[2] = hw_addr[3]; - data->hw_addr[3] = hw_addr[2]; - data->hw_addr[4] = hw_addr[5]; - data->hw_addr[5] = hw_addr[4]; -} - -static void iwl_set_hw_address_family_8000(struct device *dev, - const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - const __le16 *mac_override, - const __le16 *nvm_hw, - u32 mac_addr0, u32 mac_addr1) -{ - const u8 *hw_addr; - - if (mac_override) { - static const u8 reserved_mac[] = { - 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00 - }; - - hw_addr = (const u8 *)(mac_override + - MAC_ADDRESS_OVERRIDE_FAMILY_8000); - - /* - * Store the MAC address from MAO section. - * No byte swapping is required in MAO section - */ - memcpy(data->hw_addr, hw_addr, ETH_ALEN); - - /* - * Force the use of the OTP MAC address in case of reserved MAC - * address in the NVM, or if address is given but invalid. - */ - if (is_valid_ether_addr(data->hw_addr) && - memcmp(reserved_mac, hw_addr, ETH_ALEN) != 0) - return; - - IWL_ERR_DEV(dev, - "mac address from nvm override section is not valid\n"); - } - - if (nvm_hw) { - /* read the MAC address from HW resisters */ - hw_addr = (const u8 *)&mac_addr0; - data->hw_addr[0] = hw_addr[3]; - data->hw_addr[1] = hw_addr[2]; - data->hw_addr[2] = hw_addr[1]; - data->hw_addr[3] = hw_addr[0]; - - hw_addr = (const u8 *)&mac_addr1; - data->hw_addr[4] = hw_addr[1]; - data->hw_addr[5] = hw_addr[0]; - - if (!is_valid_ether_addr(data->hw_addr)) - IWL_ERR_DEV(dev, - "mac address from hw section is not valid\n"); - - return; - } - - IWL_ERR_DEV(dev, "mac address is not found\n"); -} - -#define IWL_4165_DEVICE_ID 0x5501 - -struct iwl_nvm_data * -iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, - const __le16 *nvm_hw, const __le16 *nvm_sw, - const __le16 *nvm_calib, const __le16 *regulatory, - const __le16 *mac_override, const __le16 *phy_sku, - u8 tx_chains, u8 rx_chains, bool lar_fw_supported, - u32 mac_addr0, u32 mac_addr1, u32 hw_id) -{ - struct iwl_nvm_data *data; - u32 sku; - u32 radio_cfg; - u16 lar_config; - - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) - data = kzalloc(sizeof(*data) + - sizeof(struct ieee80211_channel) * - IWL_NUM_CHANNELS, - GFP_KERNEL); - else - data = kzalloc(sizeof(*data) + - sizeof(struct ieee80211_channel) * - IWL_NUM_CHANNELS_FAMILY_8000, - GFP_KERNEL); - if (!data) - return NULL; - - data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw); - - radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw, phy_sku); - iwl_set_radio_cfg(cfg, data, radio_cfg); - if (data->valid_tx_ant) - tx_chains &= data->valid_tx_ant; - if (data->valid_rx_ant) - rx_chains &= data->valid_rx_ant; - - sku = iwl_get_sku(cfg, nvm_sw, phy_sku); - data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; - data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; - data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) - data->sku_cap_11n_enable = false; - data->sku_cap_11ac_enable = data->sku_cap_11n_enable && - (sku & NVM_SKU_CAP_11AC_ENABLE); - data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE; - - /* - * OTP 0x52 bug work around - * define antenna 1x1 according to MIMO disabled - */ - if (hw_id == IWL_4165_DEVICE_ID && data->sku_cap_mimo_disabled) { - data->valid_tx_ant = ANT_B; - data->valid_rx_ant = ANT_B; - tx_chains = ANT_B; - rx_chains = ANT_B; - } - - data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw); - - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { - /* Checking for required sections */ - if (!nvm_calib) { - IWL_ERR_DEV(dev, - "Can't parse empty Calib NVM sections\n"); - kfree(data); - return NULL; - } - /* in family 8000 Xtal calibration values moved to OTP */ - data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB); - data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1); - } - - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { - iwl_set_hw_address(cfg, data, nvm_hw); - - iwl_init_sbands(dev, cfg, data, nvm_sw, - tx_chains, rx_chains, lar_fw_supported); - } else { - u16 lar_offset = data->nvm_version < 0xE39 ? - NVM_LAR_OFFSET_FAMILY_8000_OLD : - NVM_LAR_OFFSET_FAMILY_8000; - - lar_config = le16_to_cpup(regulatory + lar_offset); - data->lar_enabled = !!(lar_config & - NVM_LAR_ENABLED_FAMILY_8000); - - /* MAC address in family 8000 */ - iwl_set_hw_address_family_8000(dev, cfg, data, mac_override, - nvm_hw, mac_addr0, mac_addr1); - - iwl_init_sbands(dev, cfg, data, regulatory, - tx_chains, rx_chains, - lar_fw_supported && data->lar_enabled); - } - - data->calib_version = 255; - - return data; -} -IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); - -static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan, - int ch_idx, u16 nvm_flags, - const struct iwl_cfg *cfg) -{ - u32 flags = NL80211_RRF_NO_HT40; - u32 last_5ghz_ht = LAST_5GHZ_HT; - - if (cfg->device_family == IWL_DEVICE_FAMILY_8000) - last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000; - - if (ch_idx < NUM_2GHZ_CHANNELS && - (nvm_flags & NVM_CHANNEL_40MHZ)) { - if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS) - flags &= ~NL80211_RRF_NO_HT40PLUS; - if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS) - flags &= ~NL80211_RRF_NO_HT40MINUS; - } else if (nvm_chan[ch_idx] <= last_5ghz_ht && - (nvm_flags & NVM_CHANNEL_40MHZ)) { - if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) - flags &= ~NL80211_RRF_NO_HT40PLUS; - else - flags &= ~NL80211_RRF_NO_HT40MINUS; - } - - if (!(nvm_flags & NVM_CHANNEL_80MHZ)) - flags |= NL80211_RRF_NO_80MHZ; - if (!(nvm_flags & NVM_CHANNEL_160MHZ)) - flags |= NL80211_RRF_NO_160MHZ; - - if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) - flags |= NL80211_RRF_NO_IR; - - if (nvm_flags & NVM_CHANNEL_RADAR) - flags |= NL80211_RRF_DFS; - - if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) - flags |= NL80211_RRF_NO_OUTDOOR; - - /* Set the GO concurrent flag only in case that NO_IR is set. - * Otherwise it is meaningless - */ - if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && - (flags & NL80211_RRF_NO_IR)) - flags |= NL80211_RRF_GO_CONCURRENT; - - return flags; -} - -struct ieee80211_regdomain * -iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, - int num_of_ch, __le32 *channels, u16 fw_mcc) -{ - int ch_idx; - u16 ch_flags, prev_ch_flags = 0; - const u8 *nvm_chan = cfg->device_family == IWL_DEVICE_FAMILY_8000 ? - iwl_nvm_channels_family_8000 : iwl_nvm_channels; - struct ieee80211_regdomain *regd; - int size_of_regd; - struct ieee80211_reg_rule *rule; - enum ieee80211_band band; - int center_freq, prev_center_freq = 0; - int valid_rules = 0; - bool new_rule; - int max_num_ch = cfg->device_family == IWL_DEVICE_FAMILY_8000 ? - IWL_NUM_CHANNELS_FAMILY_8000 : IWL_NUM_CHANNELS; - - if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES)) - return ERR_PTR(-EINVAL); - - if (WARN_ON(num_of_ch > max_num_ch)) - num_of_ch = max_num_ch; - - IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n", - num_of_ch); - - /* build a regdomain rule for every valid channel */ - size_of_regd = - sizeof(struct ieee80211_regdomain) + - num_of_ch * sizeof(struct ieee80211_reg_rule); - - regd = kzalloc(size_of_regd, GFP_KERNEL); - if (!regd) - return ERR_PTR(-ENOMEM); - - for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { - ch_flags = (u16)__le32_to_cpup(channels + ch_idx); - band = (ch_idx < NUM_2GHZ_CHANNELS) ? - IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx], - band); - new_rule = false; - - if (!(ch_flags & NVM_CHANNEL_VALID)) { - IWL_DEBUG_DEV(dev, IWL_DL_LAR, - "Ch. %d Flags %x [%sGHz] - No traffic\n", - nvm_chan[ch_idx], - ch_flags, - (ch_idx >= NUM_2GHZ_CHANNELS) ? - "5.2" : "2.4"); - continue; - } - - /* we can't continue the same rule */ - if (ch_idx == 0 || prev_ch_flags != ch_flags || - center_freq - prev_center_freq > 20) { - valid_rules++; - new_rule = true; - } - - rule = ®d->reg_rules[valid_rules - 1]; - - if (new_rule) - rule->freq_range.start_freq_khz = - MHZ_TO_KHZ(center_freq - 10); - - rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10); - - /* this doesn't matter - not used by FW */ - rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); - rule->power_rule.max_eirp = - DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER); - - rule->flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx, - ch_flags, cfg); - - /* rely on auto-calculation to merge BW of contiguous chans */ - rule->flags |= NL80211_RRF_AUTO_BW; - rule->freq_range.max_bandwidth_khz = 0; - - prev_ch_flags = ch_flags; - prev_center_freq = center_freq; - - IWL_DEBUG_DEV(dev, IWL_DL_LAR, - "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s(0x%02x): Ad-Hoc %ssupported\n", - center_freq, - band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4", - CHECK_AND_PRINT_I(VALID), - CHECK_AND_PRINT_I(ACTIVE), - CHECK_AND_PRINT_I(RADAR), - CHECK_AND_PRINT_I(WIDE), - CHECK_AND_PRINT_I(40MHZ), - CHECK_AND_PRINT_I(80MHZ), - CHECK_AND_PRINT_I(160MHZ), - CHECK_AND_PRINT_I(INDOOR_ONLY), - CHECK_AND_PRINT_I(GO_CONCURRENT), - ch_flags, - ((ch_flags & NVM_CHANNEL_ACTIVE) && - !(ch_flags & NVM_CHANNEL_RADAR)) - ? "" : "not "); - } - - regd->n_reg_rules = valid_rules; - - /* set alpha2 from FW. */ - regd->alpha2[0] = fw_mcc >> 8; - regd->alpha2[1] = fw_mcc & 0xff; - - return regd; -} -IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info); diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h deleted file mode 100644 index 9f44d8188c5c..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h +++ /dev/null @@ -1,97 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ -#ifndef __iwl_nvm_parse_h__ -#define __iwl_nvm_parse_h__ - -#include -#include "iwl-eeprom-parse.h" - -/** - * iwl_parse_nvm_data - parse NVM data and return values - * - * This function parses all NVM values we need and then - * returns a (newly allocated) struct containing all the - * relevant values for driver use. The struct must be freed - * later with iwl_free_nvm_data(). - */ -struct iwl_nvm_data * -iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, - const __le16 *nvm_hw, const __le16 *nvm_sw, - const __le16 *nvm_calib, const __le16 *regulatory, - const __le16 *mac_override, const __le16 *phy_sku, - u8 tx_chains, u8 rx_chains, bool lar_fw_supported, - u32 mac_addr0, u32 mac_addr1, u32 hw_id); - -/** - * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW - * - * This function parses the regulatory channel data received as a - * MCC_UPDATE_CMD command. It returns a newly allocation regulatory domain, - * to be fed into the regulatory core. An ERR_PTR is returned on error. - * If not given to the regulatory core, the user is responsible for freeing - * the regdomain returned here with kfree. - */ -struct ieee80211_regdomain * -iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, - int num_of_ch, __le32 *channels, u16 fw_mcc); - -#endif /* __iwl_nvm_parse_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h deleted file mode 100644 index 2a58d6833224..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ /dev/null @@ -1,271 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __iwl_op_mode_h__ -#define __iwl_op_mode_h__ - -#include -#include - -struct iwl_op_mode; -struct iwl_trans; -struct sk_buff; -struct iwl_device_cmd; -struct iwl_rx_cmd_buffer; -struct iwl_fw; -struct iwl_cfg; - -/** - * DOC: Operational mode - what is it ? - * - * The operational mode (a.k.a. op_mode) is the layer that implements - * mac80211's handlers. It knows two APIs: mac80211's and the fw's. It uses - * the transport API to access the HW. The op_mode doesn't need to know how the - * underlying HW works, since the transport layer takes care of that. - * - * There can be several op_mode: i.e. different fw APIs will require two - * different op_modes. This is why the op_mode is virtualized. - */ - -/** - * DOC: Life cycle of the Operational mode - * - * The operational mode has a very simple life cycle. - * - * 1) The driver layer (iwl-drv.c) chooses the op_mode based on the - * capabilities advertised by the fw file (in TLV format). - * 2) The driver layer starts the op_mode (ops->start) - * 3) The op_mode registers mac80211 - * 4) The op_mode is governed by mac80211 - * 5) The driver layer stops the op_mode - */ - -/** - * struct iwl_op_mode_ops - op_mode specific operations - * - * The op_mode exports its ops so that external components can start it and - * interact with it. The driver layer typically calls the start and stop - * handlers, the transport layer calls the others. - * - * All the handlers MUST be implemented, except @rx_rss which can be left - * out *iff* the opmode will never run on hardware with multi-queue capability. - * - * @start: start the op_mode. The transport layer is already allocated. - * May sleep - * @stop: stop the op_mode. Must free all the memory allocated. - * May sleep - * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the - * HCMD this Rx responds to. Can't sleep. - * @rx_rss: data queue RX notification to the op_mode, for (data) notifications - * received on the RSS queue(s). The queue parameter indicates which of the - * RSS queues received this frame; it will always be non-zero. - * This method must not sleep. - * @queue_full: notifies that a HW queue is full. - * Must be atomic and called with BH disabled. - * @queue_not_full: notifies that a HW queue is not full any more. - * Must be atomic and called with BH disabled. - * @hw_rf_kill:notifies of a change in the HW rf kill switch. True means that - * the radio is killed. Return %true if the device should be stopped by - * the transport immediately after the call. May sleep. - * @free_skb: allows the transport layer to free skbs that haven't been - * reclaimed by the op_mode. This can happen when the driver is freed and - * there are Tx packets pending in the transport layer. - * Must be atomic - * @nic_error: error notification. Must be atomic and must be called with BH - * disabled. - * @cmd_queue_full: Called when the command queue gets full. Must be atomic and - * called with BH disabled. - * @nic_config: configure NIC, called before firmware is started. - * May sleep - * @wimax_active: invoked when WiMax becomes active. May sleep - * @enter_d0i3: configure the fw to enter d0i3. return 1 to indicate d0i3 - * entrance is aborted (e.g. due to held reference). May sleep. - * @exit_d0i3: configure the fw to exit d0i3. May sleep. - */ -struct iwl_op_mode_ops { - struct iwl_op_mode *(*start)(struct iwl_trans *trans, - const struct iwl_cfg *cfg, - const struct iwl_fw *fw, - struct dentry *dbgfs_dir); - void (*stop)(struct iwl_op_mode *op_mode); - void (*rx)(struct iwl_op_mode *op_mode, struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb); - void (*rx_rss)(struct iwl_op_mode *op_mode, struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb, unsigned int queue); - void (*queue_full)(struct iwl_op_mode *op_mode, int queue); - void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue); - bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state); - void (*free_skb)(struct iwl_op_mode *op_mode, struct sk_buff *skb); - void (*nic_error)(struct iwl_op_mode *op_mode); - void (*cmd_queue_full)(struct iwl_op_mode *op_mode); - void (*nic_config)(struct iwl_op_mode *op_mode); - void (*wimax_active)(struct iwl_op_mode *op_mode); - int (*enter_d0i3)(struct iwl_op_mode *op_mode); - int (*exit_d0i3)(struct iwl_op_mode *op_mode); -}; - -int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops); -void iwl_opmode_deregister(const char *name); - -/** - * struct iwl_op_mode - operational mode - * @ops: pointer to its own ops - * - * This holds an implementation of the mac80211 / fw API. - */ -struct iwl_op_mode { - const struct iwl_op_mode_ops *ops; - - char op_mode_specific[0] __aligned(sizeof(void *)); -}; - -static inline void iwl_op_mode_stop(struct iwl_op_mode *op_mode) -{ - might_sleep(); - op_mode->ops->stop(op_mode); -} - -static inline void iwl_op_mode_rx(struct iwl_op_mode *op_mode, - struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb) -{ - return op_mode->ops->rx(op_mode, napi, rxb); -} - -static inline void iwl_op_mode_rx_rss(struct iwl_op_mode *op_mode, - struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb, - unsigned int queue) -{ - op_mode->ops->rx_rss(op_mode, napi, rxb, queue); -} - -static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode, - int queue) -{ - op_mode->ops->queue_full(op_mode, queue); -} - -static inline void iwl_op_mode_queue_not_full(struct iwl_op_mode *op_mode, - int queue) -{ - op_mode->ops->queue_not_full(op_mode, queue); -} - -static inline bool __must_check -iwl_op_mode_hw_rf_kill(struct iwl_op_mode *op_mode, bool state) -{ - might_sleep(); - return op_mode->ops->hw_rf_kill(op_mode, state); -} - -static inline void iwl_op_mode_free_skb(struct iwl_op_mode *op_mode, - struct sk_buff *skb) -{ - op_mode->ops->free_skb(op_mode, skb); -} - -static inline void iwl_op_mode_nic_error(struct iwl_op_mode *op_mode) -{ - op_mode->ops->nic_error(op_mode); -} - -static inline void iwl_op_mode_cmd_queue_full(struct iwl_op_mode *op_mode) -{ - op_mode->ops->cmd_queue_full(op_mode); -} - -static inline void iwl_op_mode_nic_config(struct iwl_op_mode *op_mode) -{ - might_sleep(); - op_mode->ops->nic_config(op_mode); -} - -static inline void iwl_op_mode_wimax_active(struct iwl_op_mode *op_mode) -{ - might_sleep(); - op_mode->ops->wimax_active(op_mode); -} - -static inline int iwl_op_mode_enter_d0i3(struct iwl_op_mode *op_mode) -{ - might_sleep(); - - if (!op_mode->ops->enter_d0i3) - return 0; - return op_mode->ops->enter_d0i3(op_mode); -} - -static inline int iwl_op_mode_exit_d0i3(struct iwl_op_mode *op_mode) -{ - might_sleep(); - - if (!op_mode->ops->exit_d0i3) - return 0; - return op_mode->ops->exit_d0i3(op_mode); -} - -#endif /* __iwl_op_mode_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c deleted file mode 100644 index a105455b6a24..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-phy-db.c +++ /dev/null @@ -1,471 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include -#include - -#include "iwl-drv.h" -#include "iwl-phy-db.h" -#include "iwl-debug.h" -#include "iwl-op-mode.h" -#include "iwl-trans.h" - -#define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */ -#define IWL_NUM_PAPD_CH_GROUPS 9 -#define IWL_NUM_TXP_CH_GROUPS 9 - -struct iwl_phy_db_entry { - u16 size; - u8 *data; -}; - -/** - * struct iwl_phy_db - stores phy configuration and calibration data. - * - * @cfg: phy configuration. - * @calib_nch: non channel specific calibration data. - * @calib_ch: channel specific calibration data. - * @calib_ch_group_papd: calibration data related to papd channel group. - * @calib_ch_group_txp: calibration data related to tx power chanel group. - */ -struct iwl_phy_db { - struct iwl_phy_db_entry cfg; - struct iwl_phy_db_entry calib_nch; - struct iwl_phy_db_entry calib_ch_group_papd[IWL_NUM_PAPD_CH_GROUPS]; - struct iwl_phy_db_entry calib_ch_group_txp[IWL_NUM_TXP_CH_GROUPS]; - - struct iwl_trans *trans; -}; - -enum iwl_phy_db_section_type { - IWL_PHY_DB_CFG = 1, - IWL_PHY_DB_CALIB_NCH, - IWL_PHY_DB_UNUSED, - IWL_PHY_DB_CALIB_CHG_PAPD, - IWL_PHY_DB_CALIB_CHG_TXP, - IWL_PHY_DB_MAX -}; - -#define PHY_DB_CMD 0x6c /* TEMP API - The actual is 0x8c */ - -/* - * phy db - configure operational ucode - */ -struct iwl_phy_db_cmd { - __le16 type; - __le16 length; - u8 data[]; -} __packed; - -/* for parsing of tx power channel group data that comes from the firmware*/ -struct iwl_phy_db_chg_txp { - __le32 space; - __le16 max_channel_idx; -} __packed; - -/* - * phy db - Receive phy db chunk after calibrations - */ -struct iwl_calib_res_notif_phy_db { - __le16 type; - __le16 length; - u8 data[]; -} __packed; - -struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans) -{ - struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db), - GFP_KERNEL); - - if (!phy_db) - return phy_db; - - phy_db->trans = trans; - - /* TODO: add default values of the phy db. */ - return phy_db; -} -IWL_EXPORT_SYMBOL(iwl_phy_db_init); - -/* - * get phy db section: returns a pointer to a phy db section specified by - * type and channel group id. - */ -static struct iwl_phy_db_entry * -iwl_phy_db_get_section(struct iwl_phy_db *phy_db, - enum iwl_phy_db_section_type type, - u16 chg_id) -{ - if (!phy_db || type >= IWL_PHY_DB_MAX) - return NULL; - - switch (type) { - case IWL_PHY_DB_CFG: - return &phy_db->cfg; - case IWL_PHY_DB_CALIB_NCH: - return &phy_db->calib_nch; - case IWL_PHY_DB_CALIB_CHG_PAPD: - if (chg_id >= IWL_NUM_PAPD_CH_GROUPS) - return NULL; - return &phy_db->calib_ch_group_papd[chg_id]; - case IWL_PHY_DB_CALIB_CHG_TXP: - if (chg_id >= IWL_NUM_TXP_CH_GROUPS) - return NULL; - return &phy_db->calib_ch_group_txp[chg_id]; - default: - return NULL; - } - return NULL; -} - -static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db, - enum iwl_phy_db_section_type type, - u16 chg_id) -{ - struct iwl_phy_db_entry *entry = - iwl_phy_db_get_section(phy_db, type, chg_id); - if (!entry) - return; - - kfree(entry->data); - entry->data = NULL; - entry->size = 0; -} - -void iwl_phy_db_free(struct iwl_phy_db *phy_db) -{ - int i; - - if (!phy_db) - return; - - iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0); - iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0); - for (i = 0; i < IWL_NUM_PAPD_CH_GROUPS; i++) - iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i); - for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++) - iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i); - - kfree(phy_db); -} -IWL_EXPORT_SYMBOL(iwl_phy_db_free); - -int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt, - gfp_t alloc_ctx) -{ - struct iwl_calib_res_notif_phy_db *phy_db_notif = - (struct iwl_calib_res_notif_phy_db *)pkt->data; - enum iwl_phy_db_section_type type = le16_to_cpu(phy_db_notif->type); - u16 size = le16_to_cpu(phy_db_notif->length); - struct iwl_phy_db_entry *entry; - u16 chg_id = 0; - - if (!phy_db) - return -EINVAL; - - if (type == IWL_PHY_DB_CALIB_CHG_PAPD || - type == IWL_PHY_DB_CALIB_CHG_TXP) - chg_id = le16_to_cpup((__le16 *)phy_db_notif->data); - - entry = iwl_phy_db_get_section(phy_db, type, chg_id); - if (!entry) - return -EINVAL; - - kfree(entry->data); - entry->data = kmemdup(phy_db_notif->data, size, alloc_ctx); - if (!entry->data) { - entry->size = 0; - return -ENOMEM; - } - - entry->size = size; - - IWL_DEBUG_INFO(phy_db->trans, - "%s(%d): [PHYDB]SET: Type %d , Size: %d\n", - __func__, __LINE__, type, size); - - return 0; -} -IWL_EXPORT_SYMBOL(iwl_phy_db_set_section); - -static int is_valid_channel(u16 ch_id) -{ - if (ch_id <= 14 || - (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) || - (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) || - (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1)) - return 1; - return 0; -} - -static u8 ch_id_to_ch_index(u16 ch_id) -{ - if (WARN_ON(!is_valid_channel(ch_id))) - return 0xff; - - if (ch_id <= 14) - return ch_id - 1; - if (ch_id <= 64) - return (ch_id + 20) / 4; - if (ch_id <= 140) - return (ch_id - 12) / 4; - return (ch_id - 13) / 4; -} - - -static u16 channel_id_to_papd(u16 ch_id) -{ - if (WARN_ON(!is_valid_channel(ch_id))) - return 0xff; - - if (1 <= ch_id && ch_id <= 14) - return 0; - if (36 <= ch_id && ch_id <= 64) - return 1; - if (100 <= ch_id && ch_id <= 140) - return 2; - return 3; -} - -static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id) -{ - struct iwl_phy_db_chg_txp *txp_chg; - int i; - u8 ch_index = ch_id_to_ch_index(ch_id); - if (ch_index == 0xff) - return 0xff; - - for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++) { - txp_chg = (void *)phy_db->calib_ch_group_txp[i].data; - if (!txp_chg) - return 0xff; - /* - * Looking for the first channel group that its max channel is - * higher then wanted channel. - */ - if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index) - return i; - } - return 0xff; -} -static -int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db, - u32 type, u8 **data, u16 *size, u16 ch_id) -{ - struct iwl_phy_db_entry *entry; - u16 ch_group_id = 0; - - if (!phy_db) - return -EINVAL; - - /* find wanted channel group */ - if (type == IWL_PHY_DB_CALIB_CHG_PAPD) - ch_group_id = channel_id_to_papd(ch_id); - else if (type == IWL_PHY_DB_CALIB_CHG_TXP) - ch_group_id = channel_id_to_txp(phy_db, ch_id); - - entry = iwl_phy_db_get_section(phy_db, type, ch_group_id); - if (!entry) - return -EINVAL; - - *data = entry->data; - *size = entry->size; - - IWL_DEBUG_INFO(phy_db->trans, - "%s(%d): [PHYDB] GET: Type %d , Size: %d\n", - __func__, __LINE__, type, *size); - - return 0; -} - -static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type, - u16 length, void *data) -{ - struct iwl_phy_db_cmd phy_db_cmd; - struct iwl_host_cmd cmd = { - .id = PHY_DB_CMD, - }; - - IWL_DEBUG_INFO(phy_db->trans, - "Sending PHY-DB hcmd of type %d, of length %d\n", - type, length); - - /* Set phy db cmd variables */ - phy_db_cmd.type = cpu_to_le16(type); - phy_db_cmd.length = cpu_to_le16(length); - - /* Set hcmd variables */ - cmd.data[0] = &phy_db_cmd; - cmd.len[0] = sizeof(struct iwl_phy_db_cmd); - cmd.data[1] = data; - cmd.len[1] = length; - cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY; - - return iwl_trans_send_cmd(phy_db->trans, &cmd); -} - -static int iwl_phy_db_send_all_channel_groups( - struct iwl_phy_db *phy_db, - enum iwl_phy_db_section_type type, - u8 max_ch_groups) -{ - u16 i; - int err; - struct iwl_phy_db_entry *entry; - - /* Send all the channel specific groups to operational fw */ - for (i = 0; i < max_ch_groups; i++) { - entry = iwl_phy_db_get_section(phy_db, - type, - i); - if (!entry) - return -EINVAL; - - if (!entry->size) - continue; - - /* Send the requested PHY DB section */ - err = iwl_send_phy_db_cmd(phy_db, - type, - entry->size, - entry->data); - if (err) { - IWL_ERR(phy_db->trans, - "Can't SEND phy_db section %d (%d), err %d\n", - type, i, err); - return err; - } - - IWL_DEBUG_INFO(phy_db->trans, - "Sent PHY_DB HCMD, type = %d num = %d\n", - type, i); - } - - return 0; -} - -int iwl_send_phy_db_data(struct iwl_phy_db *phy_db) -{ - u8 *data = NULL; - u16 size = 0; - int err; - - IWL_DEBUG_INFO(phy_db->trans, - "Sending phy db data and configuration to runtime image\n"); - - /* Send PHY DB CFG section */ - err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG, - &data, &size, 0); - if (err) { - IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n"); - return err; - } - - err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data); - if (err) { - IWL_ERR(phy_db->trans, - "Cannot send HCMD of Phy DB cfg section\n"); - return err; - } - - err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH, - &data, &size, 0); - if (err) { - IWL_ERR(phy_db->trans, - "Cannot get Phy DB non specific channel section\n"); - return err; - } - - err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data); - if (err) { - IWL_ERR(phy_db->trans, - "Cannot send HCMD of Phy DB non specific channel section\n"); - return err; - } - - /* Send all the TXP channel specific data */ - err = iwl_phy_db_send_all_channel_groups(phy_db, - IWL_PHY_DB_CALIB_CHG_PAPD, - IWL_NUM_PAPD_CH_GROUPS); - if (err) { - IWL_ERR(phy_db->trans, - "Cannot send channel specific PAPD groups\n"); - return err; - } - - /* Send all the TXP channel specific data */ - err = iwl_phy_db_send_all_channel_groups(phy_db, - IWL_PHY_DB_CALIB_CHG_TXP, - IWL_NUM_TXP_CH_GROUPS); - if (err) { - IWL_ERR(phy_db->trans, - "Cannot send channel specific TX power groups\n"); - return err; - } - - IWL_DEBUG_INFO(phy_db->trans, - "Finished sending phy db non channel data\n"); - return 0; -} -IWL_EXPORT_SYMBOL(iwl_send_phy_db_data); diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/iwlwifi/iwl-phy-db.h deleted file mode 100644 index 9ee18d0d2d01..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-phy-db.h +++ /dev/null @@ -1,82 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __IWL_PHYDB_H__ -#define __IWL_PHYDB_H__ - -#include - -#include "iwl-op-mode.h" -#include "iwl-trans.h" - -struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans); - -void iwl_phy_db_free(struct iwl_phy_db *phy_db); - -int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt, - gfp_t alloc_ctx); - - -int iwl_send_phy_db_data(struct iwl_phy_db *phy_db); - -#endif /* __IWL_PHYDB_H__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h deleted file mode 100644 index 3ab777f79e4f..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ /dev/null @@ -1,401 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __iwl_prph_h__ -#define __iwl_prph_h__ - -/* - * Registers in this file are internal, not PCI bus memory mapped. - * Driver accesses these via HBUS_TARG_PRPH_* registers. - */ -#define PRPH_BASE (0x00000) -#define PRPH_END (0xFFFFF) - -/* APMG (power management) constants */ -#define APMG_BASE (PRPH_BASE + 0x3000) -#define APMG_CLK_CTRL_REG (APMG_BASE + 0x0000) -#define APMG_CLK_EN_REG (APMG_BASE + 0x0004) -#define APMG_CLK_DIS_REG (APMG_BASE + 0x0008) -#define APMG_PS_CTRL_REG (APMG_BASE + 0x000c) -#define APMG_PCIDEV_STT_REG (APMG_BASE + 0x0010) -#define APMG_RFKILL_REG (APMG_BASE + 0x0014) -#define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c) -#define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020) -#define APMG_DIGITAL_SVR_REG (APMG_BASE + 0x0058) -#define APMG_ANALOG_SVR_REG (APMG_BASE + 0x006C) - -#define APMS_CLK_VAL_MRB_FUNC_MODE (0x00000001) -#define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) -#define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) - -#define APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS (0x00400000) -#define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000) -#define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000) -#define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) -#define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000) -#define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */ -#define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060) - -#define APMG_PCIDEV_STT_VAL_PERSIST_DIS (0x00000200) -#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) -#define APMG_PCIDEV_STT_VAL_WAKE_ME (0x00004000) - -#define APMG_RTC_INT_STT_RFKILL (0x10000000) - -/* Device system time */ -#define DEVICE_SYSTEM_TIME_REG 0xA0206C - -/* Device NMI register */ -#define DEVICE_SET_NMI_REG 0x00a01c30 -#define DEVICE_SET_NMI_VAL_HW BIT(0) -#define DEVICE_SET_NMI_VAL_DRV BIT(7) -#define DEVICE_SET_NMI_8000_REG 0x00a01c24 -#define DEVICE_SET_NMI_8000_VAL 0x1000000 - -/* Shared registers (0x0..0x3ff, via target indirect or periphery */ -#define SHR_BASE 0x00a10000 - -/* Shared GP1 register */ -#define SHR_APMG_GP1_REG 0x01dc -#define SHR_APMG_GP1_REG_PRPH (SHR_BASE + SHR_APMG_GP1_REG) -#define SHR_APMG_GP1_WF_XTAL_LP_EN 0x00000004 -#define SHR_APMG_GP1_CHICKEN_BIT_SELECT 0x80000000 - -/* Shared DL_CFG register */ -#define SHR_APMG_DL_CFG_REG 0x01c4 -#define SHR_APMG_DL_CFG_REG_PRPH (SHR_BASE + SHR_APMG_DL_CFG_REG) -#define SHR_APMG_DL_CFG_RTCS_CLK_SELECTOR_MSK 0x000000c0 -#define SHR_APMG_DL_CFG_RTCS_CLK_INTERNAL_XTAL 0x00000080 -#define SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP 0x00000100 - -/* Shared APMG_XTAL_CFG register */ -#define SHR_APMG_XTAL_CFG_REG 0x1c0 -#define SHR_APMG_XTAL_CFG_XTAL_ON_REQ 0x80000000 - -/* - * Device reset for family 8000 - * write to bit 24 in order to reset the CPU -*/ -#define RELEASE_CPU_RESET (0x300C) -#define RELEASE_CPU_RESET_BIT BIT(24) - -/***************************************************************************** - * 7000/3000 series SHR DTS addresses * - *****************************************************************************/ - -#define SHR_MISC_WFM_DTS_EN (0x00a10024) -#define DTSC_CFG_MODE (0x00a10604) -#define DTSC_VREF_AVG (0x00a10648) -#define DTSC_VREF5_AVG (0x00a1064c) -#define DTSC_CFG_MODE_PERIODIC (0x2) -#define DTSC_PTAT_AVG (0x00a10650) - - -/** - * Tx Scheduler - * - * The Tx Scheduler selects the next frame to be transmitted, choosing TFDs - * (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in - * host DRAM. It steers each frame's Tx command (which contains the frame - * data) into one of up to 7 prioritized Tx DMA FIFO channels within the - * device. A queue maps to only one (selectable by driver) Tx DMA channel, - * but one DMA channel may take input from several queues. - * - * Tx DMA FIFOs have dedicated purposes. - * - * For 5000 series and up, they are used differently - * (cf. iwl5000_default_queue_to_tx_fifo in iwl-5000.c): - * - * 0 -- EDCA BK (background) frames, lowest priority - * 1 -- EDCA BE (best effort) frames, normal priority - * 2 -- EDCA VI (video) frames, higher priority - * 3 -- EDCA VO (voice) and management frames, highest priority - * 4 -- unused - * 5 -- unused - * 6 -- unused - * 7 -- Commands - * - * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6. - * In addition, driver can map the remaining queues to Tx DMA/FIFO - * channels 0-3 to support 11n aggregation via EDCA DMA channels. - * - * The driver sets up each queue to work in one of two modes: - * - * 1) Scheduler-Ack, in which the scheduler automatically supports a - * block-ack (BA) window of up to 64 TFDs. In this mode, each queue - * contains TFDs for a unique combination of Recipient Address (RA) - * and Traffic Identifier (TID), that is, traffic of a given - * Quality-Of-Service (QOS) priority, destined for a single station. - * - * In scheduler-ack mode, the scheduler keeps track of the Tx status of - * each frame within the BA window, including whether it's been transmitted, - * and whether it's been acknowledged by the receiving station. The device - * automatically processes block-acks received from the receiving STA, - * and reschedules un-acked frames to be retransmitted (successful - * Tx completion may end up being out-of-order). - * - * The driver must maintain the queue's Byte Count table in host DRAM - * for this mode. - * This mode does not support fragmentation. - * - * 2) FIFO (a.k.a. non-Scheduler-ACK), in which each TFD is processed in order. - * The device may automatically retry Tx, but will retry only one frame - * at a time, until receiving ACK from receiving station, or reaching - * retry limit and giving up. - * - * The command queue (#4/#9) must use this mode! - * This mode does not require use of the Byte Count table in host DRAM. - * - * Driver controls scheduler operation via 3 means: - * 1) Scheduler registers - * 2) Shared scheduler data base in internal SRAM - * 3) Shared data in host DRAM - * - * Initialization: - * - * When loading, driver should allocate memory for: - * 1) 16 TFD circular buffers, each with space for (typically) 256 TFDs. - * 2) 16 Byte Count circular buffers in 16 KBytes contiguous memory - * (1024 bytes for each queue). - * - * After receiving "Alive" response from uCode, driver must initialize - * the scheduler (especially for queue #4/#9, the command queue, otherwise - * the driver can't issue commands!): - */ -#define SCD_MEM_LOWER_BOUND (0x0000) - -/** - * Max Tx window size is the max number of contiguous TFDs that the scheduler - * can keep track of at one time when creating block-ack chains of frames. - * Note that "64" matches the number of ack bits in a block-ack packet. - */ -#define SCD_WIN_SIZE 64 -#define SCD_FRAME_LIMIT 64 - -#define SCD_TXFIFO_POS_TID (0) -#define SCD_TXFIFO_POS_RA (4) -#define SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF) - -/* agn SCD */ -#define SCD_QUEUE_STTS_REG_POS_TXF (0) -#define SCD_QUEUE_STTS_REG_POS_ACTIVE (3) -#define SCD_QUEUE_STTS_REG_POS_WSL (4) -#define SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (19) -#define SCD_QUEUE_STTS_REG_MSK (0x017F0000) - -#define SCD_QUEUE_CTX_REG1_CREDIT_POS (8) -#define SCD_QUEUE_CTX_REG1_CREDIT_MSK (0x00FFFF00) -#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_POS (24) -#define SCD_QUEUE_CTX_REG1_SUPER_CREDIT_MSK (0xFF000000) -#define SCD_QUEUE_CTX_REG2_WIN_SIZE_POS (0) -#define SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK (0x0000007F) -#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16) -#define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000) -#define SCD_GP_CTRL_ENABLE_31_QUEUES BIT(0) -#define SCD_GP_CTRL_AUTO_ACTIVE_MODE BIT(18) - -/* Context Data */ -#define SCD_CONTEXT_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x600) -#define SCD_CONTEXT_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0) - -/* Tx status */ -#define SCD_TX_STTS_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x6A0) -#define SCD_TX_STTS_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0) - -/* Translation Data */ -#define SCD_TRANS_TBL_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x7E0) -#define SCD_TRANS_TBL_MEM_UPPER_BOUND (SCD_MEM_LOWER_BOUND + 0x808) - -#define SCD_CONTEXT_QUEUE_OFFSET(x)\ - (SCD_CONTEXT_MEM_LOWER_BOUND + ((x) * 8)) - -#define SCD_TX_STTS_QUEUE_OFFSET(x)\ - (SCD_TX_STTS_MEM_LOWER_BOUND + ((x) * 16)) - -#define SCD_TRANS_TBL_OFFSET_QUEUE(x) \ - ((SCD_TRANS_TBL_MEM_LOWER_BOUND + ((x) * 2)) & 0xfffc) - -#define SCD_BASE (PRPH_BASE + 0xa02c00) - -#define SCD_SRAM_BASE_ADDR (SCD_BASE + 0x0) -#define SCD_DRAM_BASE_ADDR (SCD_BASE + 0x8) -#define SCD_AIT (SCD_BASE + 0x0c) -#define SCD_TXFACT (SCD_BASE + 0x10) -#define SCD_ACTIVE (SCD_BASE + 0x14) -#define SCD_QUEUECHAIN_SEL (SCD_BASE + 0xe8) -#define SCD_CHAINEXT_EN (SCD_BASE + 0x244) -#define SCD_AGGR_SEL (SCD_BASE + 0x248) -#define SCD_INTERRUPT_MASK (SCD_BASE + 0x108) -#define SCD_GP_CTRL (SCD_BASE + 0x1a8) -#define SCD_EN_CTRL (SCD_BASE + 0x254) - -/*********************** END TX SCHEDULER *************************************/ - -/* tcp checksum offload */ -#define RX_EN_CSUM (0x00a00d88) - -/* Oscillator clock */ -#define OSC_CLK (0xa04068) -#define OSC_CLK_FORCE_CONTROL (0x8) - -#define FH_UCODE_LOAD_STATUS (0x1AF0) -#define CSR_UCODE_LOAD_STATUS_ADDR (0x1E70) -enum secure_load_status_reg { - LMPM_CPU_UCODE_LOADING_STARTED = 0x00000001, - LMPM_CPU_HDRS_LOADING_COMPLETED = 0x00000003, - LMPM_CPU_UCODE_LOADING_COMPLETED = 0x00000007, - LMPM_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8, - LMPM_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00, -}; - -#define LMPM_SECURE_INSPECTOR_CODE_ADDR (0x1E38) -#define LMPM_SECURE_INSPECTOR_DATA_ADDR (0x1E3C) -#define LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR (0x1E78) -#define LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR (0x1E7C) - -#define LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE (0x400000) -#define LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE (0x402000) -#define LMPM_SECURE_CPU1_HDR_MEM_SPACE (0x420000) -#define LMPM_SECURE_CPU2_HDR_MEM_SPACE (0x420400) - -/* Rx FIFO */ -#define RXF_SIZE_ADDR (0xa00c88) -#define RXF_RD_D_SPACE (0xa00c40) -#define RXF_RD_WR_PTR (0xa00c50) -#define RXF_RD_RD_PTR (0xa00c54) -#define RXF_RD_FENCE_PTR (0xa00c4c) -#define RXF_SET_FENCE_MODE (0xa00c14) -#define RXF_LD_WR2FENCE (0xa00c1c) -#define RXF_FIFO_RD_FENCE_INC (0xa00c68) -#define RXF_SIZE_BYTE_CND_POS (7) -#define RXF_SIZE_BYTE_CNT_MSK (0x3ff << RXF_SIZE_BYTE_CND_POS) -#define RXF_DIFF_FROM_PREV (0x200) - -#define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10) -#define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c) - -/* Tx FIFO */ -#define TXF_FIFO_ITEM_CNT (0xa00438) -#define TXF_WR_PTR (0xa00414) -#define TXF_RD_PTR (0xa00410) -#define TXF_FENCE_PTR (0xa00418) -#define TXF_LOCK_FENCE (0xa00424) -#define TXF_LARC_NUM (0xa0043c) -#define TXF_READ_MODIFY_DATA (0xa00448) -#define TXF_READ_MODIFY_ADDR (0xa0044c) - -/* FW monitor */ -#define MON_BUFF_SAMPLE_CTL (0xa03c00) -#define MON_BUFF_BASE_ADDR (0xa03c3c) -#define MON_BUFF_END_ADDR (0xa03c40) -#define MON_BUFF_WRPTR (0xa03c44) -#define MON_BUFF_CYCLE_CNT (0xa03c48) - -#define MON_DMARB_RD_CTL_ADDR (0xa03c60) -#define MON_DMARB_RD_DATA_ADDR (0xa03c5c) - -#define DBGC_IN_SAMPLE (0xa03c00) - -/* enable the ID buf for read */ -#define WFPM_PS_CTL_CLR 0xA0300C -#define WFMP_MAC_ADDR_0 0xA03080 -#define WFMP_MAC_ADDR_1 0xA03084 -#define LMPM_PMG_EN 0xA01CEC -#define RADIO_REG_SYS_MANUAL_DFT_0 0xAD4078 -#define RFIC_REG_RD 0xAD0470 -#define WFPM_CTRL_REG 0xA03030 -enum { - ENABLE_WFPM = BIT(31), - WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK = 0x80000000, -}; - -#define AUX_MISC_REG 0xA200B0 -enum { - HW_STEP_LOCATION_BITS = 24, -}; - -#define AUX_MISC_MASTER1_EN 0xA20818 -enum aux_misc_master1_en { - AUX_MISC_MASTER1_EN_SBE_MSK = 0x1, -}; - -#define AUX_MISC_MASTER1_SMPHR_STATUS 0xA20800 -#define RSA_ENABLE 0xA24B08 -#define PREG_AUX_BUS_WPROT_0 0xA04CC0 -#define SB_CPU_1_STATUS 0xA01E30 -#define SB_CPU_2_STATUS 0xA01E34 - -/* FW chicken bits */ -#define LMPM_CHICK 0xA01FF8 -enum { - LMPM_CHICK_EXTENDED_ADDR_SPACE = BIT(0), -}; - -/* FW chicken bits */ -#define LMPM_PAGE_PASS_NOTIF 0xA03824 -enum { - LMPM_PAGE_PASS_NOTIF_POS = BIT(20), -}; - -#endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-scd.h b/drivers/net/wireless/iwlwifi/iwl-scd.h deleted file mode 100644 index f2353ebf2666..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-scd.h +++ /dev/null @@ -1,143 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __iwl_scd_h__ -#define __iwl_scd_h__ - -#include "iwl-trans.h" -#include "iwl-io.h" -#include "iwl-prph.h" - - -static inline void iwl_scd_txq_set_chain(struct iwl_trans *trans, - u16 txq_id) -{ - iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id)); -} - -static inline void iwl_scd_txq_enable_agg(struct iwl_trans *trans, - u16 txq_id) -{ - iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); -} - -static inline void iwl_scd_txq_disable_agg(struct iwl_trans *trans, - u16 txq_id) -{ - iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); -} - -static inline void iwl_scd_disable_agg(struct iwl_trans *trans) -{ - iwl_set_bits_prph(trans, SCD_AGGR_SEL, 0); -} - -static inline void iwl_scd_activate_fifos(struct iwl_trans *trans) -{ - iwl_write_prph(trans, SCD_TXFACT, IWL_MASK(0, 7)); -} - -static inline void iwl_scd_deactivate_fifos(struct iwl_trans *trans) -{ - iwl_write_prph(trans, SCD_TXFACT, 0); -} - -static inline void iwl_scd_enable_set_active(struct iwl_trans *trans, - u32 value) -{ - iwl_write_prph(trans, SCD_EN_CTRL, value); -} - -static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl) -{ - if (chnl < 20) - return SCD_BASE + 0x18 + chnl * 4; - WARN_ON_ONCE(chnl >= 32); - return SCD_BASE + 0x284 + (chnl - 20) * 4; -} - -static inline unsigned int SCD_QUEUE_RDPTR(unsigned int chnl) -{ - if (chnl < 20) - return SCD_BASE + 0x68 + chnl * 4; - WARN_ON_ONCE(chnl >= 32); - return SCD_BASE + 0x2B4 + chnl * 4; -} - -static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl) -{ - if (chnl < 20) - return SCD_BASE + 0x10c + chnl * 4; - WARN_ON_ONCE(chnl >= 32); - return SCD_BASE + 0x334 + chnl * 4; -} - -static inline void iwl_scd_txq_set_inactive(struct iwl_trans *trans, - u16 txq_id) -{ - iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), - (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)| - (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); -} - -#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.c b/drivers/net/wireless/iwlwifi/iwl-trans.c deleted file mode 100644 index 71610968c365..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-trans.c +++ /dev/null @@ -1,114 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include -#include "iwl-trans.h" - -struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, - struct device *dev, - const struct iwl_cfg *cfg, - const struct iwl_trans_ops *ops, - size_t dev_cmd_headroom) -{ - struct iwl_trans *trans; -#ifdef CONFIG_LOCKDEP - static struct lock_class_key __key; -#endif - - trans = kzalloc(sizeof(*trans) + priv_size, GFP_KERNEL); - if (!trans) - return NULL; - -#ifdef CONFIG_LOCKDEP - lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map", - &__key, 0); -#endif - - trans->dev = dev; - trans->cfg = cfg; - trans->ops = ops; - trans->dev_cmd_headroom = dev_cmd_headroom; - trans->num_rx_queues = 1; - - snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), - "iwl_cmd_pool:%s", dev_name(trans->dev)); - trans->dev_cmd_pool = - kmem_cache_create(trans->dev_cmd_pool_name, - sizeof(struct iwl_device_cmd) - + trans->dev_cmd_headroom, - sizeof(void *), - SLAB_HWCACHE_ALIGN, - NULL); - if (!trans->dev_cmd_pool) - goto free; - - return trans; - free: - kfree(trans); - return NULL; -} - -void iwl_trans_free(struct iwl_trans *trans) -{ - kmem_cache_destroy(trans->dev_cmd_pool); - kfree(trans); -} diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h deleted file mode 100644 index 6f76525088f0..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ /dev/null @@ -1,1125 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __iwl_trans_h__ -#define __iwl_trans_h__ - -#include -#include /* for page_address */ -#include - -#include "iwl-debug.h" -#include "iwl-config.h" -#include "iwl-fw.h" -#include "iwl-op-mode.h" - -/** - * DOC: Transport layer - what is it ? - * - * The transport layer is the layer that deals with the HW directly. It provides - * an abstraction of the underlying HW to the upper layer. The transport layer - * doesn't provide any policy, algorithm or anything of this kind, but only - * mechanisms to make the HW do something. It is not completely stateless but - * close to it. - * We will have an implementation for each different supported bus. - */ - -/** - * DOC: Life cycle of the transport layer - * - * The transport layer has a very precise life cycle. - * - * 1) A helper function is called during the module initialization and - * registers the bus driver's ops with the transport's alloc function. - * 2) Bus's probe calls to the transport layer's allocation functions. - * Of course this function is bus specific. - * 3) This allocation functions will spawn the upper layer which will - * register mac80211. - * - * 4) At some point (i.e. mac80211's start call), the op_mode will call - * the following sequence: - * start_hw - * start_fw - * - * 5) Then when finished (or reset): - * stop_device - * - * 6) Eventually, the free function will be called. - */ - -/** - * DOC: Host command section - * - * A host command is a command issued by the upper layer to the fw. There are - * several versions of fw that have several APIs. The transport layer is - * completely agnostic to these differences. - * The transport does provide helper functionality (i.e. SYNC / ASYNC mode), - */ -#define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f) -#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8) -#define SEQ_TO_INDEX(s) ((s) & 0xff) -#define INDEX_TO_SEQ(i) ((i) & 0xff) -#define SEQ_RX_FRAME cpu_to_le16(0x8000) - -/* - * those functions retrieve specific information from - * the id field in the iwl_host_cmd struct which contains - * the command id, the group id and the version of the command - * and vice versa -*/ -static inline u8 iwl_cmd_opcode(u32 cmdid) -{ - return cmdid & 0xFF; -} - -static inline u8 iwl_cmd_groupid(u32 cmdid) -{ - return ((cmdid & 0xFF00) >> 8); -} - -static inline u8 iwl_cmd_version(u32 cmdid) -{ - return ((cmdid & 0xFF0000) >> 16); -} - -static inline u32 iwl_cmd_id(u8 opcode, u8 groupid, u8 version) -{ - return opcode + (groupid << 8) + (version << 16); -} - -/* make u16 wide id out of u8 group and opcode */ -#define WIDE_ID(grp, opcode) ((grp << 8) | opcode) - -/* due to the conversion, this group is special; new groups - * should be defined in the appropriate fw-api header files - */ -#define IWL_ALWAYS_LONG_GROUP 1 - -/** - * struct iwl_cmd_header - * - * This header format appears in the beginning of each command sent from the - * driver, and each response/notification received from uCode. - */ -struct iwl_cmd_header { - u8 cmd; /* Command ID: REPLY_RXON, etc. */ - u8 group_id; - /* - * The driver sets up the sequence number to values of its choosing. - * uCode does not use this value, but passes it back to the driver - * when sending the response to each driver-originated command, so - * the driver can match the response to the command. Since the values - * don't get used by uCode, the driver may set up an arbitrary format. - * - * There is one exception: uCode sets bit 15 when it originates - * the response/notification, i.e. when the response/notification - * is not a direct response to a command sent by the driver. For - * example, uCode issues REPLY_RX when it sends a received frame - * to the driver; it is not a direct response to any driver command. - * - * The Linux driver uses the following format: - * - * 0:7 tfd index - position within TX queue - * 8:12 TX queue id - * 13:14 reserved - * 15 unsolicited RX or uCode-originated notification - */ - __le16 sequence; -} __packed; - -/** - * struct iwl_cmd_header_wide - * - * This header format appears in the beginning of each command sent from the - * driver, and each response/notification received from uCode. - * this is the wide version that contains more information about the command - * like length, version and command type - */ -struct iwl_cmd_header_wide { - u8 cmd; - u8 group_id; - __le16 sequence; - __le16 length; - u8 reserved; - u8 version; -} __packed; - -#define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */ -#define FH_RSCSR_FRAME_INVALID 0x55550000 -#define FH_RSCSR_FRAME_ALIGN 0x40 - -struct iwl_rx_packet { - /* - * The first 4 bytes of the RX frame header contain both the RX frame - * size and some flags. - * Bit fields: - * 31: flag flush RB request - * 30: flag ignore TC (terminal counter) request - * 29: flag fast IRQ request - * 28-14: Reserved - * 13-00: RX frame size - */ - __le32 len_n_flags; - struct iwl_cmd_header hdr; - 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 ? - * - * @CMD_ASYNC: Return right away and don't wait for the response - * @CMD_WANT_SKB: Not valid with CMD_ASYNC. The caller needs the buffer of - * the response. The caller needs to call iwl_free_resp when done. - * @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the - * command queue, but after other high priority commands. Valid only - * with CMD_ASYNC. - * @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle. - * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle. - * @CMD_WAKE_UP_TRANS: The command response should wake up the trans - * (i.e. mark it as non-idle). - * @CMD_TB_BITMAP_POS: Position of the first bit for the TB bitmap. We need to - * check that we leave enough room for the TBs bitmap which needs 20 bits. - */ -enum CMD_MODE { - CMD_ASYNC = BIT(0), - CMD_WANT_SKB = BIT(1), - CMD_SEND_IN_RFKILL = BIT(2), - CMD_HIGH_PRIO = BIT(3), - CMD_SEND_IN_IDLE = BIT(4), - CMD_MAKE_TRANS_IDLE = BIT(5), - CMD_WAKE_UP_TRANS = BIT(6), - - CMD_TB_BITMAP_POS = 11, -}; - -#define DEF_CMD_PAYLOAD_SIZE 320 - -/** - * struct iwl_device_cmd - * - * For allocation of the command and tx queues, this establishes the overall - * size of the largest command we send to uCode, except for commands that - * aren't fully copied and use other TFD space. - */ -struct iwl_device_cmd { - union { - struct { - struct iwl_cmd_header hdr; /* uCode API */ - u8 payload[DEF_CMD_PAYLOAD_SIZE]; - }; - struct { - struct iwl_cmd_header_wide hdr_wide; - u8 payload_wide[DEF_CMD_PAYLOAD_SIZE - - sizeof(struct iwl_cmd_header_wide) + - sizeof(struct iwl_cmd_header)]; - }; - }; -} __packed; - -#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) - -/* - * number of transfer buffers (fragments) per transmit frame descriptor; - * this is just the driver's idea, the hardware supports 20 - */ -#define IWL_MAX_CMD_TBS_PER_TFD 2 - -/** - * struct iwl_hcmd_dataflag - flag for each one of the chunks of the command - * - * @IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's - * ring. The transport layer doesn't map the command's buffer to DMA, but - * rather copies it to a previously allocated DMA buffer. This flag tells - * the transport layer not to copy the command, but to map the existing - * buffer (that is passed in) instead. This saves the memcpy and allows - * commands that are bigger than the fixed buffer to be submitted. - * Note that a TFD entry after a NOCOPY one cannot be a normal copied one. - * @IWL_HCMD_DFL_DUP: Only valid without NOCOPY, duplicate the memory for this - * chunk internally and free it again after the command completes. This - * can (currently) be used only once per command. - * Note that a TFD entry after a DUP one cannot be a normal copied one. - */ -enum iwl_hcmd_dataflag { - IWL_HCMD_DFL_NOCOPY = BIT(0), - IWL_HCMD_DFL_DUP = BIT(1), -}; - -/** - * struct iwl_host_cmd - Host command to the uCode - * - * @data: array of chunks that composes the data of the host command - * @resp_pkt: response packet, if %CMD_WANT_SKB was set - * @_rx_page_order: (internally used to free response packet) - * @_rx_page_addr: (internally used to free response packet) - * @flags: can be CMD_* - * @len: array of the lengths of the chunks in data - * @dataflags: IWL_HCMD_DFL_* - * @id: command id of the host command, for wide commands encoding the - * version and group as well - */ -struct iwl_host_cmd { - const void *data[IWL_MAX_CMD_TBS_PER_TFD]; - struct iwl_rx_packet *resp_pkt; - unsigned long _rx_page_addr; - u32 _rx_page_order; - - u32 flags; - u32 id; - u16 len[IWL_MAX_CMD_TBS_PER_TFD]; - u8 dataflags[IWL_MAX_CMD_TBS_PER_TFD]; -}; - -static inline void iwl_free_resp(struct iwl_host_cmd *cmd) -{ - free_pages(cmd->_rx_page_addr, cmd->_rx_page_order); -} - -struct iwl_rx_cmd_buffer { - struct page *_page; - int _offset; - bool _page_stolen; - u32 _rx_page_order; - unsigned int truesize; -}; - -static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r) -{ - return (void *)((unsigned long)page_address(r->_page) + r->_offset); -} - -static inline int rxb_offset(struct iwl_rx_cmd_buffer *r) -{ - return r->_offset; -} - -static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r) -{ - r->_page_stolen = true; - get_page(r->_page); - return r->_page; -} - -static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r) -{ - __free_pages(r->_page, r->_rx_page_order); -} - -#define MAX_NO_RECLAIM_CMDS 6 - -#define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) - -/* - * Maximum number of HW queues the transport layer - * currently supports - */ -#define IWL_MAX_HW_QUEUES 32 -#define IWL_MAX_TID_COUNT 8 -#define IWL_FRAME_LIMIT 64 -#define IWL_MAX_RX_HW_QUEUES 16 - -/** - * enum iwl_wowlan_status - WoWLAN image/device status - * @IWL_D3_STATUS_ALIVE: firmware is still running after resume - * @IWL_D3_STATUS_RESET: device was reset while suspended - */ -enum iwl_d3_status { - IWL_D3_STATUS_ALIVE, - IWL_D3_STATUS_RESET, -}; - -/** - * enum iwl_trans_status: transport status flags - * @STATUS_SYNC_HCMD_ACTIVE: a SYNC command is being processed - * @STATUS_DEVICE_ENABLED: APM is enabled - * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up) - * @STATUS_INT_ENABLED: interrupts are enabled - * @STATUS_RFKILL: the HW RFkill switch is in KILL position - * @STATUS_FW_ERROR: the fw is in error state - * @STATUS_TRANS_GOING_IDLE: shutting down the trans, only special commands - * are sent - * @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent - * @STATUS_TRANS_DEAD: trans is dead - avoid any read/write operation - */ -enum iwl_trans_status { - STATUS_SYNC_HCMD_ACTIVE, - STATUS_DEVICE_ENABLED, - STATUS_TPOWER_PMI, - STATUS_INT_ENABLED, - STATUS_RFKILL, - STATUS_FW_ERROR, - STATUS_TRANS_GOING_IDLE, - STATUS_TRANS_IDLE, - STATUS_TRANS_DEAD, -}; - -/** - * struct iwl_trans_config - transport configuration - * - * @op_mode: pointer to the upper layer. - * @cmd_queue: the index of the command queue. - * Must be set before start_fw. - * @cmd_fifo: the fifo for host commands - * @cmd_q_wdg_timeout: the timeout of the watchdog timer for the command queue. - * @no_reclaim_cmds: Some devices erroneously don't set the - * SEQ_RX_FRAME bit on some notifications, this is the - * list of such notifications to filter. Max length is - * %MAX_NO_RECLAIM_CMDS. - * @n_no_reclaim_cmds: # of commands in list - * @rx_buf_size_8k: 8 kB RX buffer size needed for A-MSDUs, - * if unset 4k will be the RX buffer size - * @bc_table_dword: set to true if the BC table expects the byte count to be - * in DWORD (as opposed to bytes) - * @scd_set_active: should the transport configure the SCD for HCMD queue - * @wide_cmd_header: firmware supports wide host command header - * @command_names: array of command names, must be 256 entries - * (one for each command); for debugging only - * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until - * we get the ALIVE from the uCode - */ -struct iwl_trans_config { - struct iwl_op_mode *op_mode; - - u8 cmd_queue; - u8 cmd_fifo; - unsigned int cmd_q_wdg_timeout; - const u8 *no_reclaim_cmds; - unsigned int n_no_reclaim_cmds; - - bool rx_buf_size_8k; - bool bc_table_dword; - bool scd_set_active; - bool wide_cmd_header; - const char *const *command_names; - - u32 sdio_adma_addr; -}; - -struct iwl_trans_dump_data { - u32 len; - u8 data[]; -}; - -struct iwl_trans; - -struct iwl_trans_txq_scd_cfg { - u8 fifo; - s8 sta_id; - u8 tid; - bool aggregate; - int frame_limit; -}; - -/** - * struct iwl_trans_ops - transport specific operations - * - * All the handlers MUST be implemented - * - * @start_hw: starts the HW. If low_power is true, the NIC needs to be taken - * out of a low power state. From that point on, the HW can send - * interrupts. May sleep. - * @op_mode_leave: Turn off the HW RF kill indication if on - * May sleep - * @start_fw: allocates and inits all the resources for the transport - * layer. Also kick a fw image. - * May sleep - * @fw_alive: called when the fw sends alive notification. If the fw provides - * the SCD base address in SRAM, then provide it here, or 0 otherwise. - * May sleep - * @stop_device: stops the whole device (embedded CPU put to reset) and stops - * the HW. If low_power is true, the NIC will be put in low power state. - * From that point on, the HW will be stopped but will still issue an - * interrupt if the HW RF kill switch is triggered. - * This callback must do the right thing and not crash even if %start_hw() - * was called but not &start_fw(). May sleep. - * @d3_suspend: put the device into the correct mode for WoWLAN during - * suspend. This is optional, if not implemented WoWLAN will not be - * supported. This callback may sleep. - * @d3_resume: resume the device after WoWLAN, enabling the opmode to - * talk to the WoWLAN image to get its status. This is optional, if not - * implemented WoWLAN will not be supported. This callback may sleep. - * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted. - * If RFkill is asserted in the middle of a SYNC host command, it must - * return -ERFKILL straight away. - * May sleep only if CMD_ASYNC is not set - * @tx: send an skb - * Must be atomic - * @reclaim: free packet until ssn. Returns a list of freed packets. - * Must be atomic - * @txq_enable: setup a queue. To setup an AC queue, use the - * iwl_trans_ac_txq_enable wrapper. fw_alive must have been called before - * this one. The op_mode must not configure the HCMD queue. The scheduler - * configuration may be %NULL, in which case the hardware will not be - * configured. May sleep. - * @txq_disable: de-configure a Tx queue to send AMPDUs - * Must be atomic - * @wait_tx_queue_empty: wait until tx queues are empty. May sleep. - * @freeze_txq_timer: prevents the timer of the queue from firing until the - * queue is set to awake. Must be atomic. - * @dbgfs_register: add the dbgfs files under this directory. Files will be - * automatically deleted. - * @write8: write a u8 to a register at offset ofs from the BAR - * @write32: write a u32 to a register at offset ofs from the BAR - * @read32: read a u32 register at offset ofs from the BAR - * @read_prph: read a DWORD from a periphery register - * @write_prph: write a DWORD to a periphery register - * @read_mem: read device's SRAM in DWORD - * @write_mem: write device's SRAM in DWORD. If %buf is %NULL, then the memory - * will be zeroed. - * @configure: configure parameters required by the transport layer from - * the op_mode. May be called several times before start_fw, can't be - * called after that. - * @set_pmi: set the power pmi state - * @grab_nic_access: wake the NIC to be able to access non-HBUS regs. - * Sleeping is not allowed between grab_nic_access and - * release_nic_access. - * @release_nic_access: let the NIC go to sleep. The "flags" parameter - * must be the same one that was sent before to the grab_nic_access. - * @set_bits_mask - set SRAM register according to value and mask. - * @ref: grab a reference to the transport/FW layers, disallowing - * certain low power states - * @unref: release a reference previously taken with @ref. Note that - * initially the reference count is 1, making an initial @unref - * necessary to allow low power states. - * @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last - * TX'ed commands and similar. The buffer will be vfree'd by the caller. - * Note that the transport must fill in the proper file headers. - */ -struct iwl_trans_ops { - - int (*start_hw)(struct iwl_trans *iwl_trans, bool low_power); - void (*op_mode_leave)(struct iwl_trans *iwl_trans); - int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw, - bool run_in_rfkill); - int (*update_sf)(struct iwl_trans *trans, - struct iwl_sf_region *st_fwrd_space); - void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr); - void (*stop_device)(struct iwl_trans *trans, bool low_power); - - void (*d3_suspend)(struct iwl_trans *trans, bool test); - int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status, - bool test); - - int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd); - - int (*tx)(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_device_cmd *dev_cmd, int queue); - void (*reclaim)(struct iwl_trans *trans, int queue, int ssn, - struct sk_buff_head *skbs); - - void (*txq_enable)(struct iwl_trans *trans, int queue, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg, - unsigned int queue_wdg_timeout); - void (*txq_disable)(struct iwl_trans *trans, int queue, - bool configure_scd); - - int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); - int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm); - void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs, - bool freeze); - - void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val); - void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val); - u32 (*read32)(struct iwl_trans *trans, u32 ofs); - u32 (*read_prph)(struct iwl_trans *trans, u32 ofs); - void (*write_prph)(struct iwl_trans *trans, u32 ofs, u32 val); - int (*read_mem)(struct iwl_trans *trans, u32 addr, - void *buf, int dwords); - int (*write_mem)(struct iwl_trans *trans, u32 addr, - const void *buf, int dwords); - void (*configure)(struct iwl_trans *trans, - const struct iwl_trans_config *trans_cfg); - void (*set_pmi)(struct iwl_trans *trans, bool state); - bool (*grab_nic_access)(struct iwl_trans *trans, bool silent, - unsigned long *flags); - void (*release_nic_access)(struct iwl_trans *trans, - unsigned long *flags); - void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask, - u32 value); - void (*ref)(struct iwl_trans *trans); - void (*unref)(struct iwl_trans *trans); - int (*suspend)(struct iwl_trans *trans); - void (*resume)(struct iwl_trans *trans); - - struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans, - struct iwl_fw_dbg_trigger_tlv - *trigger); -}; - -/** - * enum iwl_trans_state - state of the transport layer - * - * @IWL_TRANS_NO_FW: no fw has sent an alive response - * @IWL_TRANS_FW_ALIVE: a fw has sent an alive response - */ -enum iwl_trans_state { - IWL_TRANS_NO_FW = 0, - IWL_TRANS_FW_ALIVE = 1, -}; - -/** - * enum iwl_d0i3_mode - d0i3 mode - * - * @IWL_D0I3_MODE_OFF - d0i3 is disabled - * @IWL_D0I3_MODE_ON_IDLE - enter d0i3 when device is idle - * (e.g. no active references) - * @IWL_D0I3_MODE_ON_SUSPEND - enter d0i3 only on suspend - * (in case of 'any' trigger) - */ -enum iwl_d0i3_mode { - IWL_D0I3_MODE_OFF = 0, - IWL_D0I3_MODE_ON_IDLE, - IWL_D0I3_MODE_ON_SUSPEND, -}; - -/** - * struct iwl_trans - transport common data - * - * @ops - pointer to iwl_trans_ops - * @op_mode - pointer to the op_mode - * @cfg - pointer to the configuration - * @status: a bit-mask of transport status flags - * @dev - pointer to struct device * that represents the device - * @max_skb_frags: maximum number of fragments an SKB can have when transmitted. - * 0 indicates that frag SKBs (NETIF_F_SG) aren't supported. - * @hw_id: a u32 with the ID of the device / sub-device. - * Set during transport allocation. - * @hw_id_str: a string with info about HW ID. Set during transport allocation. - * @pm_support: set to true in start_hw if link pm is supported - * @ltr_enabled: set to true if the LTR is enabled - * @num_rx_queues: number of RX queues allocated by the transport; - * the transport must set this before calling iwl_drv_start() - * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. - * The user should use iwl_trans_{alloc,free}_tx_cmd. - * @dev_cmd_headroom: room needed for the transport's private use before the - * device_cmd for Tx - for internal use only - * The user should use iwl_trans_{alloc,free}_tx_cmd. - * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before - * starting the firmware, used for tracing - * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the - * start of the 802.11 header in the @rx_mpdu_cmd - * @dflt_pwr_limit: default power limit fetched from the platform (ACPI) - * @dbg_dest_tlv: points to the destination TLV for debug - * @dbg_conf_tlv: array of pointers to configuration TLVs for debug - * @dbg_trigger_tlv: array of pointers to triggers TLVs for debug - * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv - * @paging_req_addr: The location were the FW will upload / download the pages - * from. The address is set by the opmode - * @paging_db: Pointer to the opmode paging data base, the pointer is set by - * the opmode. - * @paging_download_buf: Buffer used for copying all of the pages before - * downloading them to the FW. The buffer is allocated in the opmode - */ -struct iwl_trans { - const struct iwl_trans_ops *ops; - struct iwl_op_mode *op_mode; - const struct iwl_cfg *cfg; - enum iwl_trans_state state; - unsigned long status; - - struct device *dev; - u32 max_skb_frags; - u32 hw_rev; - u32 hw_id; - char hw_id_str[52]; - - u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size; - - bool pm_support; - bool ltr_enabled; - - u8 num_rx_queues; - - /* The following fields are internal only */ - struct kmem_cache *dev_cmd_pool; - size_t dev_cmd_headroom; - char dev_cmd_pool_name[50]; - - struct dentry *dbgfs_dir; - -#ifdef CONFIG_LOCKDEP - struct lockdep_map sync_cmd_lockdep_map; -#endif - - u64 dflt_pwr_limit; - - const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; - const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX]; - struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv; - u8 dbg_dest_reg_num; - - /* - * Paging parameters - All of the parameters should be set by the - * opmode when paging is enabled - */ - u32 paging_req_addr; - struct iwl_fw_paging *paging_db; - void *paging_download_buf; - - enum iwl_d0i3_mode d0i3_mode; - - bool wowlan_d0i3; - - /* pointer to trans specific struct */ - /*Ensure that this pointer will always be aligned to sizeof pointer */ - char trans_specific[0] __aligned(sizeof(void *)); -}; - -static inline void iwl_trans_configure(struct iwl_trans *trans, - const struct iwl_trans_config *trans_cfg) -{ - trans->op_mode = trans_cfg->op_mode; - - trans->ops->configure(trans, trans_cfg); -} - -static inline int _iwl_trans_start_hw(struct iwl_trans *trans, bool low_power) -{ - might_sleep(); - - return trans->ops->start_hw(trans, low_power); -} - -static inline int iwl_trans_start_hw(struct iwl_trans *trans) -{ - return trans->ops->start_hw(trans, true); -} - -static inline void iwl_trans_op_mode_leave(struct iwl_trans *trans) -{ - might_sleep(); - - if (trans->ops->op_mode_leave) - trans->ops->op_mode_leave(trans); - - trans->op_mode = NULL; - - trans->state = IWL_TRANS_NO_FW; -} - -static inline void iwl_trans_fw_alive(struct iwl_trans *trans, u32 scd_addr) -{ - might_sleep(); - - trans->state = IWL_TRANS_FW_ALIVE; - - trans->ops->fw_alive(trans, scd_addr); -} - -static inline int iwl_trans_start_fw(struct iwl_trans *trans, - const struct fw_img *fw, - bool run_in_rfkill) -{ - might_sleep(); - - WARN_ON_ONCE(!trans->rx_mpdu_cmd); - - clear_bit(STATUS_FW_ERROR, &trans->status); - return trans->ops->start_fw(trans, fw, run_in_rfkill); -} - -static inline int iwl_trans_update_sf(struct iwl_trans *trans, - struct iwl_sf_region *st_fwrd_space) -{ - might_sleep(); - - if (trans->ops->update_sf) - return trans->ops->update_sf(trans, st_fwrd_space); - - return 0; -} - -static inline void _iwl_trans_stop_device(struct iwl_trans *trans, - bool low_power) -{ - might_sleep(); - - trans->ops->stop_device(trans, low_power); - - trans->state = IWL_TRANS_NO_FW; -} - -static inline void iwl_trans_stop_device(struct iwl_trans *trans) -{ - _iwl_trans_stop_device(trans, true); -} - -static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test) -{ - might_sleep(); - if (trans->ops->d3_suspend) - trans->ops->d3_suspend(trans, test); -} - -static inline int iwl_trans_d3_resume(struct iwl_trans *trans, - enum iwl_d3_status *status, - bool test) -{ - might_sleep(); - if (!trans->ops->d3_resume) - return 0; - - return trans->ops->d3_resume(trans, status, test); -} - -static inline void iwl_trans_ref(struct iwl_trans *trans) -{ - if (trans->ops->ref) - trans->ops->ref(trans); -} - -static inline void iwl_trans_unref(struct iwl_trans *trans) -{ - if (trans->ops->unref) - trans->ops->unref(trans); -} - -static inline int iwl_trans_suspend(struct iwl_trans *trans) -{ - if (!trans->ops->suspend) - return 0; - - return trans->ops->suspend(trans); -} - -static inline void iwl_trans_resume(struct iwl_trans *trans) -{ - if (trans->ops->resume) - trans->ops->resume(trans); -} - -static inline struct iwl_trans_dump_data * -iwl_trans_dump_data(struct iwl_trans *trans, - struct iwl_fw_dbg_trigger_tlv *trigger) -{ - if (!trans->ops->dump_data) - return NULL; - return trans->ops->dump_data(trans, trigger); -} - -static inline int iwl_trans_send_cmd(struct iwl_trans *trans, - struct iwl_host_cmd *cmd) -{ - int ret; - - if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) && - test_bit(STATUS_RFKILL, &trans->status))) - return -ERFKILL; - - if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) - return -EIO; - - if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) { - IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); - return -EIO; - } - - if (!(cmd->flags & CMD_ASYNC)) - lock_map_acquire_read(&trans->sync_cmd_lockdep_map); - - ret = trans->ops->send_cmd(trans, cmd); - - if (!(cmd->flags & CMD_ASYNC)) - lock_map_release(&trans->sync_cmd_lockdep_map); - - return ret; -} - -static inline struct iwl_device_cmd * -iwl_trans_alloc_tx_cmd(struct iwl_trans *trans) -{ - u8 *dev_cmd_ptr = kmem_cache_alloc(trans->dev_cmd_pool, GFP_ATOMIC); - - if (unlikely(dev_cmd_ptr == NULL)) - return NULL; - - return (struct iwl_device_cmd *) - (dev_cmd_ptr + trans->dev_cmd_headroom); -} - -static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans, - struct iwl_device_cmd *dev_cmd) -{ - u8 *dev_cmd_ptr = (u8 *)dev_cmd - trans->dev_cmd_headroom; - - kmem_cache_free(trans->dev_cmd_pool, dev_cmd_ptr); -} - -static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_device_cmd *dev_cmd, int queue) -{ - if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) - return -EIO; - - if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) - IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); - - return trans->ops->tx(trans, skb, dev_cmd, queue); -} - -static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue, - int ssn, struct sk_buff_head *skbs) -{ - if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) - IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); - - trans->ops->reclaim(trans, queue, ssn, skbs); -} - -static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue, - bool configure_scd) -{ - trans->ops->txq_disable(trans, queue, configure_scd); -} - -static inline void -iwl_trans_txq_enable_cfg(struct iwl_trans *trans, int queue, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg, - unsigned int queue_wdg_timeout) -{ - might_sleep(); - - if (unlikely((trans->state != IWL_TRANS_FW_ALIVE))) - IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); - - trans->ops->txq_enable(trans, queue, ssn, cfg, queue_wdg_timeout); -} - -static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, - int fifo, int sta_id, int tid, - int frame_limit, u16 ssn, - unsigned int queue_wdg_timeout) -{ - struct iwl_trans_txq_scd_cfg cfg = { - .fifo = fifo, - .sta_id = sta_id, - .tid = tid, - .frame_limit = frame_limit, - .aggregate = sta_id >= 0, - }; - - iwl_trans_txq_enable_cfg(trans, queue, ssn, &cfg, queue_wdg_timeout); -} - -static inline -void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, int fifo, - unsigned int queue_wdg_timeout) -{ - struct iwl_trans_txq_scd_cfg cfg = { - .fifo = fifo, - .sta_id = -1, - .tid = IWL_MAX_TID_COUNT, - .frame_limit = IWL_FRAME_LIMIT, - .aggregate = false, - }; - - iwl_trans_txq_enable_cfg(trans, queue, 0, &cfg, queue_wdg_timeout); -} - -static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans, - unsigned long txqs, - bool freeze) -{ - if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) - IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); - - if (trans->ops->freeze_txq_timer) - trans->ops->freeze_txq_timer(trans, txqs, freeze); -} - -static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans, - u32 txqs) -{ - if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) - IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); - - return trans->ops->wait_tx_queue_empty(trans, txqs); -} - -static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) -{ - return trans->ops->dbgfs_register(trans, dir); -} - -static inline void iwl_trans_write8(struct iwl_trans *trans, u32 ofs, u8 val) -{ - trans->ops->write8(trans, ofs, val); -} - -static inline void iwl_trans_write32(struct iwl_trans *trans, u32 ofs, u32 val) -{ - trans->ops->write32(trans, ofs, val); -} - -static inline u32 iwl_trans_read32(struct iwl_trans *trans, u32 ofs) -{ - return trans->ops->read32(trans, ofs); -} - -static inline u32 iwl_trans_read_prph(struct iwl_trans *trans, u32 ofs) -{ - return trans->ops->read_prph(trans, ofs); -} - -static inline void iwl_trans_write_prph(struct iwl_trans *trans, u32 ofs, - u32 val) -{ - return trans->ops->write_prph(trans, ofs, val); -} - -static inline int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr, - void *buf, int dwords) -{ - return trans->ops->read_mem(trans, addr, buf, dwords); -} - -#define iwl_trans_read_mem_bytes(trans, addr, buf, bufsize) \ - do { \ - if (__builtin_constant_p(bufsize)) \ - BUILD_BUG_ON((bufsize) % sizeof(u32)); \ - iwl_trans_read_mem(trans, addr, buf, (bufsize) / sizeof(u32));\ - } while (0) - -static inline u32 iwl_trans_read_mem32(struct iwl_trans *trans, u32 addr) -{ - u32 value; - - if (WARN_ON(iwl_trans_read_mem(trans, addr, &value, 1))) - return 0xa5a5a5a5; - - return value; -} - -static inline int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr, - const void *buf, int dwords) -{ - return trans->ops->write_mem(trans, addr, buf, dwords); -} - -static inline u32 iwl_trans_write_mem32(struct iwl_trans *trans, u32 addr, - u32 val) -{ - return iwl_trans_write_mem(trans, addr, &val, 1); -} - -static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state) -{ - if (trans->ops->set_pmi) - trans->ops->set_pmi(trans, state); -} - -static inline void -iwl_trans_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value) -{ - trans->ops->set_bits_mask(trans, reg, mask, value); -} - -#define iwl_trans_grab_nic_access(trans, silent, flags) \ - __cond_lock(nic_access, \ - likely((trans)->ops->grab_nic_access(trans, silent, flags))) - -static inline void __releases(nic_access) -iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags) -{ - trans->ops->release_nic_access(trans, flags); - __release(nic_access); -} - -static inline void iwl_trans_fw_error(struct iwl_trans *trans) -{ - if (WARN_ON_ONCE(!trans->op_mode)) - return; - - /* prevent double restarts due to the same erroneous FW */ - if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status)) - iwl_op_mode_nic_error(trans->op_mode); -} - -/***************************************************** - * transport helper functions - *****************************************************/ -struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, - struct device *dev, - const struct iwl_cfg *cfg, - const struct iwl_trans_ops *ops, - size_t dev_cmd_headroom); -void iwl_trans_free(struct iwl_trans *trans); - -/***************************************************** -* driver (transport) register/unregister functions -******************************************************/ -int __must_check iwl_pci_register_driver(void); -void iwl_pci_unregister_driver(void); - -#endif /* __iwl_trans_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile deleted file mode 100644 index 8c2c3d13b092..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -obj-$(CONFIG_IWLMVM) += iwlmvm.o -iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o -iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o -iwlmvm-y += scan.o time-event.o rs.o -iwlmvm-y += power.o coex.o coex_legacy.o -iwlmvm-y += tt.o offloading.o tdls.o -iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o -iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o -iwlmvm-y += tof.o -iwlmvm-$(CONFIG_PM_SLEEP) += d3.o - -ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/iwlwifi/mvm/binding.c deleted file mode 100644 index a1376539d2dc..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/binding.c +++ /dev/null @@ -1,211 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include "fw-api.h" -#include "mvm.h" - -struct iwl_mvm_iface_iterator_data { - struct ieee80211_vif *ignore_vif; - int idx; - - struct iwl_mvm_phy_ctxt *phyctxt; - - u16 ids[MAX_MACS_IN_BINDING]; - u16 colors[MAX_MACS_IN_BINDING]; -}; - -static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action, - struct iwl_mvm_iface_iterator_data *data) -{ - struct iwl_binding_cmd cmd; - struct iwl_mvm_phy_ctxt *phyctxt = data->phyctxt; - int i, ret; - u32 status; - - memset(&cmd, 0, sizeof(cmd)); - - cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id, - phyctxt->color)); - cmd.action = cpu_to_le32(action); - cmd.phy = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id, - phyctxt->color)); - - for (i = 0; i < MAX_MACS_IN_BINDING; i++) - cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID); - for (i = 0; i < data->idx; i++) - cmd.macs[i] = cpu_to_le32(FW_CMD_ID_AND_COLOR(data->ids[i], - data->colors[i])); - - status = 0; - ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD, - sizeof(cmd), &cmd, &status); - if (ret) { - IWL_ERR(mvm, "Failed to send binding (action:%d): %d\n", - action, ret); - return ret; - } - - if (status) { - IWL_ERR(mvm, "Binding command failed: %u\n", status); - ret = -EIO; - } - - return ret; -} - -static void iwl_mvm_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_iface_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (vif == data->ignore_vif) - return; - - if (mvmvif->phy_ctxt != data->phyctxt) - return; - - if (WARN_ON_ONCE(data->idx >= MAX_MACS_IN_BINDING)) - return; - - data->ids[data->idx] = mvmvif->id; - data->colors[data->idx] = mvmvif->color; - data->idx++; -} - -static int iwl_mvm_binding_update(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mvm_phy_ctxt *phyctxt, - bool add) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_iface_iterator_data data = { - .ignore_vif = vif, - .phyctxt = phyctxt, - }; - u32 action = FW_CTXT_ACTION_MODIFY; - - lockdep_assert_held(&mvm->mutex); - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_iface_iterator, - &data); - - /* - * If there are no other interfaces yet we - * need to create a new binding. - */ - if (data.idx == 0) { - if (add) - action = FW_CTXT_ACTION_ADD; - else - action = FW_CTXT_ACTION_REMOVE; - } - - if (add) { - if (WARN_ON_ONCE(data.idx >= MAX_MACS_IN_BINDING)) - return -EINVAL; - - data.ids[data.idx] = mvmvif->id; - data.colors[data.idx] = mvmvif->color; - data.idx++; - } - - return iwl_mvm_binding_cmd(mvm, action, &data); -} - -int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) - return -EINVAL; - - /* - * Update SF - Disable if needed. if this fails, SF might still be on - * while many macs are bound, which is forbidden - so fail the binding. - */ - if (iwl_mvm_sf_update(mvm, vif, false)) - return -EINVAL; - - return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true); -} - -int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) - return -EINVAL; - - ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false); - - if (!ret) - if (iwl_mvm_sf_update(mvm, vif, true)) - IWL_ERR(mvm, "Failed to update SF state\n"); - - return ret; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c deleted file mode 100644 index e290ac67d975..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ /dev/null @@ -1,1005 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include -#include - -#include "fw-api-coex.h" -#include "iwl-modparams.h" -#include "mvm.h" -#include "iwl-debug.h" - -/* 20MHz / 40MHz below / 40Mhz above*/ -static const __le64 iwl_ci_mask[][3] = { - /* dummy entry for channel 0 */ - {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)}, - { - cpu_to_le64(0x0000001FFFULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x00007FFFFFULL), - }, - { - cpu_to_le64(0x000000FFFFULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x0003FFFFFFULL), - }, - { - cpu_to_le64(0x000003FFFCULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x000FFFFFFCULL), - }, - { - cpu_to_le64(0x00001FFFE0ULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x007FFFFFE0ULL), - }, - { - cpu_to_le64(0x00007FFF80ULL), - cpu_to_le64(0x00007FFFFFULL), - cpu_to_le64(0x01FFFFFF80ULL), - }, - { - cpu_to_le64(0x0003FFFC00ULL), - cpu_to_le64(0x0003FFFFFFULL), - cpu_to_le64(0x0FFFFFFC00ULL), - }, - { - cpu_to_le64(0x000FFFF000ULL), - cpu_to_le64(0x000FFFFFFCULL), - cpu_to_le64(0x3FFFFFF000ULL), - }, - { - cpu_to_le64(0x007FFF8000ULL), - cpu_to_le64(0x007FFFFFE0ULL), - cpu_to_le64(0xFFFFFF8000ULL), - }, - { - cpu_to_le64(0x01FFFE0000ULL), - cpu_to_le64(0x01FFFFFF80ULL), - cpu_to_le64(0xFFFFFE0000ULL), - }, - { - cpu_to_le64(0x0FFFF00000ULL), - cpu_to_le64(0x0FFFFFFC00ULL), - cpu_to_le64(0x0ULL), - }, - { - cpu_to_le64(0x3FFFC00000ULL), - cpu_to_le64(0x3FFFFFF000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFFE000000ULL), - cpu_to_le64(0xFFFFFF8000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFF8000000ULL), - cpu_to_le64(0xFFFFFE0000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFC0000000ULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x0ULL) - }, -}; - -struct corunning_block_luts { - u8 range; - __le32 lut20[BT_COEX_CORUN_LUT_SIZE]; -}; - -/* - * Ranges for the antenna coupling calibration / co-running block LUT: - * LUT0: [ 0, 12[ - * LUT1: [12, 20[ - * LUT2: [20, 21[ - * LUT3: [21, 23[ - * LUT4: [23, 27[ - * LUT5: [27, 30[ - * LUT6: [30, 32[ - * LUT7: [32, 33[ - * LUT8: [33, - [ - */ -static const struct corunning_block_luts antenna_coupling_ranges[] = { - { - .range = 0, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 12, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 20, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 21, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 23, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 27, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 30, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 32, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 33, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, -}; - -static enum iwl_bt_coex_lut_type -iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) -{ - struct ieee80211_chanctx_conf *chanctx_conf; - enum iwl_bt_coex_lut_type ret; - u16 phy_ctx_id; - u32 primary_ch_phy_id, secondary_ch_phy_id; - - /* - * Checking that we hold mvm->mutex is a good idea, but the rate - * control can't acquire the mutex since it runs in Tx path. - * So this is racy in that case, but in the worst case, the AMPDU - * size limit will be wrong for a short time which is not a big - * issue. - */ - - rcu_read_lock(); - - chanctx_conf = rcu_dereference(vif->chanctx_conf); - - if (!chanctx_conf || - chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { - rcu_read_unlock(); - return BT_COEX_INVALID_LUT; - } - - ret = BT_COEX_TX_DIS_LUT; - - if (mvm->cfg->bt_shared_single_ant) { - rcu_read_unlock(); - return ret; - } - - phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); - primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id); - secondary_ch_phy_id = - le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id); - - if (primary_ch_phy_id == phy_ctx_id) - ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut); - else if (secondary_ch_phy_id == phy_ctx_id) - ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut); - /* else - default = TX TX disallowed */ - - rcu_read_unlock(); - - return ret; -} - -int iwl_send_bt_init_conf(struct iwl_mvm *mvm) -{ - struct iwl_bt_coex_cmd bt_cmd = {}; - u32 mode; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_send_bt_init_conf_old(mvm); - - lockdep_assert_held(&mvm->mutex); - - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { - switch (mvm->bt_force_ant_mode) { - case BT_FORCE_ANT_BT: - mode = BT_COEX_BT; - break; - case BT_FORCE_ANT_WIFI: - mode = BT_COEX_WIFI; - break; - default: - WARN_ON(1); - mode = 0; - } - - bt_cmd.mode = cpu_to_le32(mode); - goto send_cmd; - } - - mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE; - bt_cmd.mode = cpu_to_le32(mode); - - if (IWL_MVM_BT_COEX_SYNC2SCO) - bt_cmd.enabled_modules |= - cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED); - - if (iwl_mvm_bt_is_plcr_supported(mvm)) - bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED); - - if (IWL_MVM_BT_COEX_MPLUT) { - bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED); - bt_cmd.enabled_modules |= - cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED); - } - - bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET); - -send_cmd: - memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); - memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); - - return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, 0, sizeof(bt_cmd), &bt_cmd); -} - -static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, - bool enable) -{ - struct iwl_bt_coex_reduced_txp_update_cmd cmd = {}; - struct iwl_mvm_sta *mvmsta; - u32 value; - int ret; - - mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); - if (!mvmsta) - return 0; - - /* nothing to do */ - if (mvmsta->bt_reduced_txpower == enable) - return 0; - - value = mvmsta->sta_id; - - if (enable) - value |= BT_REDUCED_TX_POWER_BIT; - - IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", - enable ? "en" : "dis", sta_id); - - cmd.reduced_txp = cpu_to_le32(value); - mvmsta->bt_reduced_txpower = enable; - - ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, CMD_ASYNC, - sizeof(cmd), &cmd); - - return ret; -} - -struct iwl_bt_iterator_data { - struct iwl_bt_coex_profile_notif *notif; - struct iwl_mvm *mvm; - struct ieee80211_chanctx_conf *primary; - struct ieee80211_chanctx_conf *secondary; - bool primary_ll; -}; - -static inline -void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool enable, int rssi) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->bf_data.last_bt_coex_event = rssi; - mvmvif->bf_data.bt_coex_max_thold = - enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0; - mvmvif->bf_data.bt_coex_min_thold = - enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; -} - -/* must be called under rcu_read_lock */ -static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_bt_iterator_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - struct ieee80211_chanctx_conf *chanctx_conf; - /* default smps_mode is AUTOMATIC - only used for client modes */ - enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; - u32 bt_activity_grading; - int ave_rssi; - - lockdep_assert_held(&mvm->mutex); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - break; - case NL80211_IFTYPE_AP: - if (!mvmvif->ap_ibss_active) - return; - break; - default: - return; - } - - chanctx_conf = rcu_dereference(vif->chanctx_conf); - - /* If channel context is invalid or not on 2.4GHz .. */ - if ((!chanctx_conf || - chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) { - if (vif->type == NL80211_IFTYPE_STATION) { - /* ... relax constraints and disable rssi events */ - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, - smps_mode); - iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, - false); - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); - } - return; - } - - bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading); - if (bt_activity_grading >= BT_HIGH_TRAFFIC) - smps_mode = IEEE80211_SMPS_STATIC; - else if (bt_activity_grading >= BT_LOW_TRAFFIC) - smps_mode = IEEE80211_SMPS_DYNAMIC; - - /* relax SMPS constraints for next association */ - if (!vif->bss_conf.assoc) - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - if (mvmvif->phy_ctxt && - IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status, - mvmvif->phy_ctxt->id)) - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - IWL_DEBUG_COEX(data->mvm, - "mac %d: bt_activity_grading %d smps_req %d\n", - mvmvif->id, bt_activity_grading, smps_mode); - - if (vif->type == NL80211_IFTYPE_STATION) - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, - smps_mode); - - /* low latency is always primary */ - if (iwl_mvm_vif_low_latency(mvmvif)) { - data->primary_ll = true; - - data->secondary = data->primary; - data->primary = chanctx_conf; - } - - if (vif->type == NL80211_IFTYPE_AP) { - if (!mvmvif->ap_ibss_active) - return; - - if (chanctx_conf == data->primary) - return; - - if (!data->primary_ll) { - /* - * downgrade the current primary no matter what its - * type is. - */ - data->secondary = data->primary; - data->primary = chanctx_conf; - } else { - /* there is low latency vif - we will be secondary */ - data->secondary = chanctx_conf; - } - return; - } - - /* - * STA / P2P Client, try to be primary if first vif. If we are in low - * latency mode, we are already in primary and just don't do much - */ - if (!data->primary || data->primary == chanctx_conf) - data->primary = chanctx_conf; - else if (!data->secondary) - /* if secondary is not NULL, it might be a GO */ - data->secondary = chanctx_conf; - - /* - * don't reduce the Tx power if one of these is true: - * we are in LOOSE - * single share antenna product - * BT is active - * we are associated - */ - if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || - mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || - le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) { - iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); - return; - } - - /* try to get the avg rssi from fw */ - ave_rssi = mvmvif->bf_data.ave_beacon_signal; - - /* if the RSSI isn't valid, fake it is very low */ - if (!ave_rssi) - ave_rssi = -100; - if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - } - - /* Begin to monitor the RSSI: it may influence the reduced Tx power */ - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi); -} - -static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) -{ - struct iwl_bt_iterator_data data = { - .mvm = mvm, - .notif = &mvm->last_bt_notif, - }; - struct iwl_bt_coex_ci_cmd cmd = {}; - u8 ci_bw_idx; - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - rcu_read_lock(); - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bt_notif_iterator, &data); - - if (data.primary) { - struct ieee80211_chanctx_conf *chan = data.primary; - if (WARN_ON(!chan->def.chan)) { - rcu_read_unlock(); - return; - } - - if (chan->def.width < NL80211_CHAN_WIDTH_40) { - ci_bw_idx = 0; - } else { - if (chan->def.center_freq1 > - chan->def.chan->center_freq) - ci_bw_idx = 2; - else - ci_bw_idx = 1; - } - - cmd.bt_primary_ci = - iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; - cmd.primary_ch_phy_id = - cpu_to_le32(*((u16 *)data.primary->drv_priv)); - } - - if (data.secondary) { - struct ieee80211_chanctx_conf *chan = data.secondary; - if (WARN_ON(!data.secondary->def.chan)) { - rcu_read_unlock(); - return; - } - - if (chan->def.width < NL80211_CHAN_WIDTH_40) { - ci_bw_idx = 0; - } else { - if (chan->def.center_freq1 > - chan->def.chan->center_freq) - ci_bw_idx = 2; - else - ci_bw_idx = 1; - } - - cmd.bt_secondary_ci = - iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; - cmd.secondary_ch_phy_id = - cpu_to_le32(*((u16 *)data.secondary->drv_priv)); - } - - rcu_read_unlock(); - - /* Don't spam the fw with the same command over and over */ - if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) { - if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, - sizeof(cmd), &cmd)) - IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); - memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd)); - } -} - -void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; - - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - iwl_mvm_rx_bt_coex_notif_old(mvm, rxb); - return; - } - - IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); - IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); - IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", - le32_to_cpu(notif->primary_ch_lut)); - IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", - le32_to_cpu(notif->secondary_ch_lut)); - IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", - le32_to_cpu(notif->bt_activity_grading)); - - /* remember this notification for future use: rssi fluctuations */ - memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); - - iwl_mvm_bt_coex_notif_handle(mvm); -} - -void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event_data rssi_event) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - iwl_mvm_bt_rssi_event_old(mvm, vif, rssi_event); - return; - } - - lockdep_assert_held(&mvm->mutex); - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - /* - * Rssi update while not associated - can happen since the statistics - * are handled asynchronously - */ - if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) - return; - - /* No BT - reports should be disabled */ - if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) - return; - - IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, - rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); - - /* - * Check if rssi is good enough for reduced Tx power, but not in loose - * scheme. - */ - if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant || - iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) - ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, - false); - else - ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true); - - if (ret) - IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); -} - -#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) -#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) - -u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; - enum iwl_bt_coex_lut_type lut_type; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_mvm_coex_agg_time_limit_old(mvm, sta); - - if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id)) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < - BT_HIGH_TRAFFIC) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - lut_type = iwl_get_coex_type(mvm, mvmsta->vif); - - if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - /* tight coex, high bt traffic, reduce AGG time limit */ - return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; -} - -bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; - enum iwl_bt_coex_lut_type lut_type; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_mvm_bt_coex_is_mimo_allowed_old(mvm, sta); - - if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id)) - return true; - - if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < - BT_HIGH_TRAFFIC) - return true; - - /* - * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas - * since BT is already killed. - * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while - * we Tx. - * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. - */ - lut_type = iwl_get_coex_type(mvm, mvmsta->vif); - return lut_type != BT_COEX_LOOSE_LUT; -} - -bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant) -{ - /* there is no other antenna, shared antenna is always available */ - if (mvm->cfg->bt_shared_single_ant) - return true; - - if (ant & mvm->cfg->non_shared_ant) - return true; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm); - - return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < - BT_HIGH_TRAFFIC; -} - -bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm) -{ - /* there is no other antenna, shared antenna is always available */ - if (mvm->cfg->bt_shared_single_ant) - return true; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm); - - return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC; -} - -bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, - enum ieee80211_band band) -{ - u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading); - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_BT_COEX_SPLIT)) - return iwl_mvm_bt_coex_is_tpc_allowed_old(mvm, band); - - if (band != IEEE80211_BAND_2GHZ) - return false; - - return bt_activity >= BT_LOW_TRAFFIC; -} - -u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, - struct ieee80211_tx_info *info, u8 ac) -{ - __le16 fc = hdr->frame_control; - - if (info->band != IEEE80211_BAND_2GHZ) - return 0; - - if (unlikely(mvm->bt_tx_prio)) - return mvm->bt_tx_prio - 1; - - /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */ - if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO || - is_multicast_ether_addr(hdr->addr1) || - ieee80211_is_ctl(fc) || ieee80211_is_mgmt(fc) || - ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) - return 3; - - switch (ac) { - case IEEE80211_AC_BE: - return 1; - case IEEE80211_AC_VO: - return 3; - case IEEE80211_AC_VI: - return 2; - default: - break; - } - - return 0; -} - -void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm) -{ - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - iwl_mvm_bt_coex_vif_change_old(mvm); - return; - } - - iwl_mvm_bt_coex_notif_handle(mvm); -} - -void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 ant_isolation = le32_to_cpup((void *)pkt->data); - struct iwl_bt_coex_corun_lut_update_cmd cmd = {}; - u8 __maybe_unused lower_bound, upper_bound; - u8 lut; - - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb); - return; - } - - if (!iwl_mvm_bt_is_plcr_supported(mvm)) - return; - - lockdep_assert_held(&mvm->mutex); - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - if (ant_isolation == mvm->last_ant_isol) - return; - - for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++) - if (ant_isolation < antenna_coupling_ranges[lut + 1].range) - break; - - lower_bound = antenna_coupling_ranges[lut].range; - - if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1) - upper_bound = antenna_coupling_ranges[lut + 1].range; - else - upper_bound = antenna_coupling_ranges[lut].range; - - IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n", - ant_isolation, lower_bound, upper_bound, lut); - - mvm->last_ant_isol = ant_isolation; - - if (mvm->last_corun_lut == lut) - return; - - mvm->last_corun_lut = lut; - - /* For the moment, use the same LUT for 20GHz and 40GHz */ - memcpy(&cmd.corun_lut20, antenna_coupling_ranges[lut].lut20, - sizeof(cmd.corun_lut20)); - - memcpy(&cmd.corun_lut40, antenna_coupling_ranges[lut].lut20, - sizeof(cmd.corun_lut40)); - - if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_CORUN_LUT, 0, - sizeof(cmd), &cmd)) - IWL_ERR(mvm, - "failed to send BT_COEX_UPDATE_CORUN_LUT command\n"); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c deleted file mode 100644 index 61c07b05fcaa..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c +++ /dev/null @@ -1,1315 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include -#include - -#include "fw-api-coex.h" -#include "iwl-modparams.h" -#include "mvm.h" -#include "iwl-debug.h" - -#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \ - [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \ - ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS)) - -static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1, - BT_COEX_PRIO_TBL_PRIO_BYPASS, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2, - BT_COEX_PRIO_TBL_PRIO_BYPASS, 1), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1, - BT_COEX_PRIO_TBL_PRIO_LOW, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2, - BT_COEX_PRIO_TBL_PRIO_LOW, 1), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1, - BT_COEX_PRIO_TBL_PRIO_HIGH, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2, - BT_COEX_PRIO_TBL_PRIO_HIGH, 1), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM, - BT_COEX_PRIO_TBL_DISABLED, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52, - BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24, - BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE, - BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0), - 0, 0, 0, 0, 0, 0, -}; - -#undef EVENT_PRIO_ANT - -static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) -{ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return 0; - - return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0, - sizeof(struct iwl_bt_coex_prio_tbl_cmd), - &iwl_bt_prio_tbl); -} - -static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = { - cpu_to_le32(0xf0f0f0f0), /* 50% */ - cpu_to_le32(0xc0c0c0c0), /* 25% */ - cpu_to_le32(0xfcfcfcfc), /* 75% */ - cpu_to_le32(0xfefefefe), /* 87.5% */ -}; - -static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { - { - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - }, - { - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - }, - { - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - }, -}; - -static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { - { - /* Tight */ - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaeaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xc0004000), - cpu_to_le32(0x00004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), - }, - { - /* Loose */ - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), - }, - { - /* Tx Tx disabled */ - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xeeaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xc0004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), - }, -}; - -/* 20MHz / 40MHz below / 40Mhz above*/ -static const __le64 iwl_ci_mask[][3] = { - /* dummy entry for channel 0 */ - {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)}, - { - cpu_to_le64(0x0000001FFFULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x00007FFFFFULL), - }, - { - cpu_to_le64(0x000000FFFFULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x0003FFFFFFULL), - }, - { - cpu_to_le64(0x000003FFFCULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x000FFFFFFCULL), - }, - { - cpu_to_le64(0x00001FFFE0ULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x007FFFFFE0ULL), - }, - { - cpu_to_le64(0x00007FFF80ULL), - cpu_to_le64(0x00007FFFFFULL), - cpu_to_le64(0x01FFFFFF80ULL), - }, - { - cpu_to_le64(0x0003FFFC00ULL), - cpu_to_le64(0x0003FFFFFFULL), - cpu_to_le64(0x0FFFFFFC00ULL), - }, - { - cpu_to_le64(0x000FFFF000ULL), - cpu_to_le64(0x000FFFFFFCULL), - cpu_to_le64(0x3FFFFFF000ULL), - }, - { - cpu_to_le64(0x007FFF8000ULL), - cpu_to_le64(0x007FFFFFE0ULL), - cpu_to_le64(0xFFFFFF8000ULL), - }, - { - cpu_to_le64(0x01FFFE0000ULL), - cpu_to_le64(0x01FFFFFF80ULL), - cpu_to_le64(0xFFFFFE0000ULL), - }, - { - cpu_to_le64(0x0FFFF00000ULL), - cpu_to_le64(0x0FFFFFFC00ULL), - cpu_to_le64(0x0ULL), - }, - { - cpu_to_le64(0x3FFFC00000ULL), - cpu_to_le64(0x3FFFFFF000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFFE000000ULL), - cpu_to_le64(0xFFFFFF8000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFF8000000ULL), - cpu_to_le64(0xFFFFFE0000ULL), - cpu_to_le64(0x0) - }, - { - cpu_to_le64(0xFFC0000000ULL), - cpu_to_le64(0x0ULL), - cpu_to_le64(0x0ULL) - }, -}; - -enum iwl_bt_kill_msk { - BT_KILL_MSK_DEFAULT, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_MAX, -}; - -static const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = { - [BT_KILL_MSK_DEFAULT] = 0xfffffc00, - [BT_KILL_MSK_NEVER] = 0xffffffff, - [BT_KILL_MSK_ALWAYS] = 0, -}; - -static const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = { - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - }, - { - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_NEVER, - }, - { - BT_KILL_MSK_DEFAULT, - BT_KILL_MSK_NEVER, - BT_KILL_MSK_DEFAULT, - }, -}; - -static const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = { - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_ALWAYS, - }, - { - BT_KILL_MSK_DEFAULT, - BT_KILL_MSK_ALWAYS, - BT_KILL_MSK_DEFAULT, - }, -}; - -struct corunning_block_luts { - u8 range; - __le32 lut20[BT_COEX_CORUN_LUT_SIZE]; -}; - -/* - * Ranges for the antenna coupling calibration / co-running block LUT: - * LUT0: [ 0, 12[ - * LUT1: [12, 20[ - * LUT2: [20, 21[ - * LUT3: [21, 23[ - * LUT4: [23, 27[ - * LUT5: [27, 30[ - * LUT6: [30, 32[ - * LUT7: [32, 33[ - * LUT8: [33, - [ - */ -static const struct corunning_block_luts antenna_coupling_ranges[] = { - { - .range = 0, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 12, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 20, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 21, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 23, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 27, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 30, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 32, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, - { - .range = 33, - .lut20 = { - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), - }, - }, -}; - -static enum iwl_bt_coex_lut_type -iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) -{ - struct ieee80211_chanctx_conf *chanctx_conf; - enum iwl_bt_coex_lut_type ret; - u16 phy_ctx_id; - - /* - * Checking that we hold mvm->mutex is a good idea, but the rate - * control can't acquire the mutex since it runs in Tx path. - * So this is racy in that case, but in the worst case, the AMPDU - * size limit will be wrong for a short time which is not a big - * issue. - */ - - rcu_read_lock(); - - chanctx_conf = rcu_dereference(vif->chanctx_conf); - - if (!chanctx_conf || - chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { - rcu_read_unlock(); - return BT_COEX_INVALID_LUT; - } - - ret = BT_COEX_TX_DIS_LUT; - - if (mvm->cfg->bt_shared_single_ant) { - rcu_read_unlock(); - return ret; - } - - phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); - - if (mvm->last_bt_ci_cmd_old.primary_ch_phy_id == phy_ctx_id) - ret = le32_to_cpu(mvm->last_bt_notif_old.primary_ch_lut); - else if (mvm->last_bt_ci_cmd_old.secondary_ch_phy_id == phy_ctx_id) - ret = le32_to_cpu(mvm->last_bt_notif_old.secondary_ch_lut); - /* else - default = TX TX disallowed */ - - rcu_read_unlock(); - - return ret; -} - -int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm) -{ - struct iwl_bt_coex_cmd_old *bt_cmd; - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - int ret; - u32 flags; - - ret = iwl_send_bt_prio_tbl(mvm); - if (ret) - return ret; - - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); - if (!bt_cmd) - return -ENOMEM; - cmd.data[0] = bt_cmd; - - lockdep_assert_held(&mvm->mutex); - - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) { - switch (mvm->bt_force_ant_mode) { - case BT_FORCE_ANT_AUTO: - flags = BT_COEX_AUTO_OLD; - break; - case BT_FORCE_ANT_BT: - flags = BT_COEX_BT_OLD; - break; - case BT_FORCE_ANT_WIFI: - flags = BT_COEX_WIFI_OLD; - break; - default: - WARN_ON(1); - flags = 0; - } - - bt_cmd->flags = cpu_to_le32(flags); - bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE); - goto send_cmd; - } - - bt_cmd->max_kill = 5; - bt_cmd->bt4_antenna_isolation_thr = - IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS; - bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling; - bt_cmd->bt4_tx_tx_delta_freq_thr = 15; - bt_cmd->bt4_tx_rx_max_freq0 = 15; - bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT; - bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT; - - flags = iwlwifi_mod_params.bt_coex_active ? - BT_COEX_NW_OLD : BT_COEX_DISABLE_OLD; - bt_cmd->flags = cpu_to_le32(flags); - - bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE | - BT_VALID_BT_PRIO_BOOST | - BT_VALID_MAX_KILL | - BT_VALID_3W_TMRS | - BT_VALID_KILL_ACK | - BT_VALID_KILL_CTS | - BT_VALID_REDUCED_TX_POWER | - BT_VALID_LUT | - BT_VALID_WIFI_RX_SW_PRIO_BOOST | - BT_VALID_WIFI_TX_SW_PRIO_BOOST | - BT_VALID_ANT_ISOLATION | - BT_VALID_ANT_ISOLATION_THRS | - BT_VALID_TXTX_DELTA_FREQ_THRS | - BT_VALID_TXRX_MAX_FREQ_0 | - BT_VALID_SYNC_TO_SCO | - BT_VALID_TTC | - BT_VALID_RRC); - - if (IWL_MVM_BT_COEX_SYNC2SCO) - bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO); - - if (iwl_mvm_bt_is_plcr_supported(mvm)) { - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 | - BT_VALID_CORUN_LUT_40); - bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING); - } - - if (IWL_MVM_BT_COEX_MPLUT) { - bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT); - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT); - } - - if (IWL_MVM_BT_COEX_TTC) - bt_cmd->flags |= cpu_to_le32(BT_COEX_TTC); - - if (iwl_mvm_bt_is_rrc_supported(mvm)) - bt_cmd->flags |= cpu_to_le32(BT_COEX_RRC); - - if (mvm->cfg->bt_shared_single_ant) - memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant, - sizeof(iwl_single_shared_ant)); - else - memcpy(&bt_cmd->decision_lut, iwl_combined_lookup, - sizeof(iwl_combined_lookup)); - - /* Take first Co-running block LUT to get started */ - memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20, - sizeof(bt_cmd->bt4_corun_lut20)); - memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20, - sizeof(bt_cmd->bt4_corun_lut40)); - - memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost, - sizeof(iwl_bt_prio_boost)); - bt_cmd->bt4_multiprio_lut[0] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG0); - bt_cmd->bt4_multiprio_lut[1] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG1); - -send_cmd: - memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old)); - memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old)); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(bt_cmd); - return ret; -} - -static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm) -{ - struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif_old; - u32 primary_lut = le32_to_cpu(notif->primary_ch_lut); - u32 ag = le32_to_cpu(notif->bt_activity_grading); - struct iwl_bt_coex_cmd_old *bt_cmd; - u8 ack_kill_msk, cts_kill_msk; - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .data[0] = &bt_cmd, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - int ret = 0; - - lockdep_assert_held(&mvm->mutex); - - ack_kill_msk = iwl_bt_ack_kill_msk[ag][primary_lut]; - cts_kill_msk = iwl_bt_cts_kill_msk[ag][primary_lut]; - - if (mvm->bt_ack_kill_msk[0] == ack_kill_msk && - mvm->bt_cts_kill_msk[0] == cts_kill_msk) - return 0; - - mvm->bt_ack_kill_msk[0] = ack_kill_msk; - mvm->bt_cts_kill_msk[0] = cts_kill_msk; - - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); - if (!bt_cmd) - return -ENOMEM; - cmd.data[0] = bt_cmd; - bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); - - bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk]); - bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk]); - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | - BT_VALID_KILL_ACK | - BT_VALID_KILL_CTS); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(bt_cmd); - return ret; -} - -static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, - bool enable) -{ - struct iwl_bt_coex_cmd_old *bt_cmd; - /* Send ASYNC since this can be sent from an atomic context */ - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_DUP, }, - .flags = CMD_ASYNC, - }; - struct iwl_mvm_sta *mvmsta; - int ret; - - mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); - if (!mvmsta) - return 0; - - /* nothing to do */ - if (mvmsta->bt_reduced_txpower == enable) - return 0; - - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC); - if (!bt_cmd) - return -ENOMEM; - cmd.data[0] = bt_cmd; - bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); - - bt_cmd->valid_bit_msk = - cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER); - bt_cmd->bt_reduced_tx_power = sta_id; - - if (enable) - bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT; - - IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", - enable ? "en" : "dis", sta_id); - - mvmsta->bt_reduced_txpower = enable; - - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(bt_cmd); - return ret; -} - -struct iwl_bt_iterator_data { - struct iwl_bt_coex_profile_notif_old *notif; - struct iwl_mvm *mvm; - struct ieee80211_chanctx_conf *primary; - struct ieee80211_chanctx_conf *secondary; - bool primary_ll; -}; - -static inline -void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool enable, int rssi) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->bf_data.last_bt_coex_event = rssi; - mvmvif->bf_data.bt_coex_max_thold = - enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0; - mvmvif->bf_data.bt_coex_min_thold = - enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0; -} - -/* must be called under rcu_read_lock */ -static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_bt_iterator_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - struct ieee80211_chanctx_conf *chanctx_conf; - enum ieee80211_smps_mode smps_mode; - u32 bt_activity_grading; - int ave_rssi; - - lockdep_assert_held(&mvm->mutex); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - /* default smps_mode for BSS / P2P client is AUTOMATIC */ - smps_mode = IEEE80211_SMPS_AUTOMATIC; - break; - case NL80211_IFTYPE_AP: - if (!mvmvif->ap_ibss_active) - return; - break; - default: - return; - } - - chanctx_conf = rcu_dereference(vif->chanctx_conf); - - /* If channel context is invalid or not on 2.4GHz .. */ - if ((!chanctx_conf || - chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) { - if (vif->type == NL80211_IFTYPE_STATION) { - /* ... relax constraints and disable rssi events */ - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, - smps_mode); - iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, - false); - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); - } - return; - } - - bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading); - if (bt_activity_grading >= BT_HIGH_TRAFFIC) - smps_mode = IEEE80211_SMPS_STATIC; - else if (bt_activity_grading >= BT_LOW_TRAFFIC) - smps_mode = vif->type == NL80211_IFTYPE_AP ? - IEEE80211_SMPS_OFF : - IEEE80211_SMPS_DYNAMIC; - - /* relax SMPS contraints for next association */ - if (!vif->bss_conf.assoc) - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - if (mvmvif->phy_ctxt && - data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id)) - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - IWL_DEBUG_COEX(data->mvm, - "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n", - mvmvif->id, data->notif->bt_status, bt_activity_grading, - smps_mode); - - if (vif->type == NL80211_IFTYPE_STATION) - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, - smps_mode); - - /* low latency is always primary */ - if (iwl_mvm_vif_low_latency(mvmvif)) { - data->primary_ll = true; - - data->secondary = data->primary; - data->primary = chanctx_conf; - } - - if (vif->type == NL80211_IFTYPE_AP) { - if (!mvmvif->ap_ibss_active) - return; - - if (chanctx_conf == data->primary) - return; - - if (!data->primary_ll) { - /* - * downgrade the current primary no matter what its - * type is. - */ - data->secondary = data->primary; - data->primary = chanctx_conf; - } else { - /* there is low latency vif - we will be secondary */ - data->secondary = chanctx_conf; - } - return; - } - - /* - * STA / P2P Client, try to be primary if first vif. If we are in low - * latency mode, we are already in primary and just don't do much - */ - if (!data->primary || data->primary == chanctx_conf) - data->primary = chanctx_conf; - else if (!data->secondary) - /* if secondary is not NULL, it might be a GO */ - data->secondary = chanctx_conf; - - /* - * don't reduce the Tx power if one of these is true: - * we are in LOOSE - * single share antenna product - * BT is active - * we are associated - */ - if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || - mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || - !data->notif->bt_status) { - iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); - return; - } - - /* try to get the avg rssi from fw */ - ave_rssi = mvmvif->bf_data.ave_beacon_signal; - - /* if the RSSI isn't valid, fake it is very low */ - if (!ave_rssi) - ave_rssi = -100; - if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) { - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) { - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - } - - /* Begin to monitor the RSSI: it may influence the reduced Tx power */ - iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi); -} - -static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) -{ - struct iwl_bt_iterator_data data = { - .mvm = mvm, - .notif = &mvm->last_bt_notif_old, - }; - struct iwl_bt_coex_ci_cmd_old cmd = {}; - u8 ci_bw_idx; - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - rcu_read_lock(); - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bt_notif_iterator, &data); - - if (data.primary) { - struct ieee80211_chanctx_conf *chan = data.primary; - - if (WARN_ON(!chan->def.chan)) { - rcu_read_unlock(); - return; - } - - if (chan->def.width < NL80211_CHAN_WIDTH_40) { - ci_bw_idx = 0; - cmd.co_run_bw_primary = 0; - } else { - cmd.co_run_bw_primary = 1; - if (chan->def.center_freq1 > - chan->def.chan->center_freq) - ci_bw_idx = 2; - else - ci_bw_idx = 1; - } - - cmd.bt_primary_ci = - iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; - cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv); - } - - if (data.secondary) { - struct ieee80211_chanctx_conf *chan = data.secondary; - - if (WARN_ON(!data.secondary->def.chan)) { - rcu_read_unlock(); - return; - } - - if (chan->def.width < NL80211_CHAN_WIDTH_40) { - ci_bw_idx = 0; - cmd.co_run_bw_secondary = 0; - } else { - cmd.co_run_bw_secondary = 1; - if (chan->def.center_freq1 > - chan->def.chan->center_freq) - ci_bw_idx = 2; - else - ci_bw_idx = 1; - } - - cmd.bt_secondary_ci = - iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; - cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv); - } - - rcu_read_unlock(); - - /* Don't spam the fw with the same command over and over */ - if (memcmp(&cmd, &mvm->last_bt_ci_cmd_old, sizeof(cmd))) { - if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, - sizeof(cmd), &cmd)) - IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); - memcpy(&mvm->last_bt_ci_cmd_old, &cmd, sizeof(cmd)); - } - - if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm)) - IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); -} - -void iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_bt_coex_profile_notif_old *notif = (void *)pkt->data; - - IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); - IWL_DEBUG_COEX(mvm, "\tBT status: %s\n", - notif->bt_status ? "ON" : "OFF"); - IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn); - IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); - IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", - le32_to_cpu(notif->primary_ch_lut)); - IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", - le32_to_cpu(notif->secondary_ch_lut)); - IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", - le32_to_cpu(notif->bt_activity_grading)); - IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n", - notif->bt_agg_traffic_load); - - /* remember this notification for future use: rssi fluctuations */ - memcpy(&mvm->last_bt_notif_old, notif, sizeof(mvm->last_bt_notif_old)); - - iwl_mvm_bt_coex_notif_handle(mvm); -} - -static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_bt_iterator_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - - struct ieee80211_chanctx_conf *chanctx_conf; - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - /* If channel context is invalid or not on 2.4GHz - don't count it */ - if (!chanctx_conf || - chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { - rcu_read_unlock(); - return; - } - rcu_read_unlock(); - - if (vif->type != NL80211_IFTYPE_STATION || - mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) - return; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], - lockdep_is_held(&mvm->mutex)); - - /* This can happen if the station has been removed right now */ - if (IS_ERR_OR_NULL(sta)) - return; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); -} - -void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event_data rssi_event) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_bt_iterator_data data = { - .mvm = mvm, - }; - int ret; - - lockdep_assert_held(&mvm->mutex); - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - /* - * Rssi update while not associated - can happen since the statistics - * are handled asynchronously - */ - if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) - return; - - /* No BT - reports should be disabled */ - if (!mvm->last_bt_notif_old.bt_status) - return; - - IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, - rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); - - /* - * Check if rssi is good enough for reduced Tx power, but not in loose - * scheme. - */ - if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant || - iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) - ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, - false); - else - ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true); - - if (ret) - IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bt_rssi_iterator, &data); - - if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm)) - IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); -} - -#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) -#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) - -u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - enum iwl_bt_coex_lut_type lut_type; - - if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) < - BT_HIGH_TRAFFIC) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - if (mvm->last_bt_notif_old.ttc_enabled) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - lut_type = iwl_get_coex_type(mvm, mvmsta->vif); - - if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) - return LINK_QUAL_AGG_TIME_LIMIT_DEF; - - /* tight coex, high bt traffic, reduce AGG time limit */ - return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; -} - -bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - enum iwl_bt_coex_lut_type lut_type; - - if (mvm->last_bt_notif_old.ttc_enabled) - return true; - - if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) < - BT_HIGH_TRAFFIC) - return true; - - /* - * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas - * since BT is already killed. - * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while - * we Tx. - * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. - */ - lut_type = iwl_get_coex_type(mvm, mvmsta->vif); - return lut_type != BT_COEX_LOOSE_LUT; -} - -bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm) -{ - u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading); - return ag < BT_HIGH_TRAFFIC; -} - -bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm, - enum ieee80211_band band) -{ - u32 bt_activity = - le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading); - - if (band != IEEE80211_BAND_2GHZ) - return false; - - return bt_activity >= BT_LOW_TRAFFIC; -} - -void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm) -{ - iwl_mvm_bt_coex_notif_handle(mvm); -} - -void iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 ant_isolation = le32_to_cpup((void *)pkt->data); - u8 __maybe_unused lower_bound, upper_bound; - u8 lut; - - struct iwl_bt_coex_cmd_old *bt_cmd; - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - - if (!iwl_mvm_bt_is_plcr_supported(mvm)) - return; - - lockdep_assert_held(&mvm->mutex); - - /* Ignore updates if we are in force mode */ - if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) - return; - - if (ant_isolation == mvm->last_ant_isol) - return; - - for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++) - if (ant_isolation < antenna_coupling_ranges[lut + 1].range) - break; - - lower_bound = antenna_coupling_ranges[lut].range; - - if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1) - upper_bound = antenna_coupling_ranges[lut + 1].range; - else - upper_bound = antenna_coupling_ranges[lut].range; - - IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n", - ant_isolation, lower_bound, upper_bound, lut); - - mvm->last_ant_isol = ant_isolation; - - if (mvm->last_corun_lut == lut) - return; - - mvm->last_corun_lut = lut; - - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); - if (!bt_cmd) - return; - cmd.data[0] = bt_cmd; - - bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD); - bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | - BT_VALID_CORUN_LUT_20 | - BT_VALID_CORUN_LUT_40); - - /* For the moment, use the same LUT for 20GHz and 40GHz */ - memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20, - sizeof(bt_cmd->bt4_corun_lut20)); - - memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20, - sizeof(bt_cmd->bt4_corun_lut40)); - - if (iwl_mvm_send_cmd(mvm, &cmd)) - IWL_ERR(mvm, "failed to send BT_CONFIG command\n"); - - kfree(bt_cmd); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h deleted file mode 100644 index 5c21231e195d..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/constants.h +++ /dev/null @@ -1,139 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __MVM_CONSTANTS_H -#define __MVM_CONSTANTS_H - -#include - -#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC) -#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC) -#define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC) -#define IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT (10 * USEC_PER_MSEC) -#define IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT (2 * 1024) /* defined in TU */ -#define IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT (40 * 1024) /* defined in TU */ -#define IWL_MVM_P2P_LOWLATENCY_PS_ENABLE 0 -#define IWL_MVM_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC) -#define IWL_MVM_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC) -#define IWL_MVM_UAPSD_QUEUES (IEEE80211_WMM_IE_STA_QOSINFO_AC_VO |\ - IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |\ - IEEE80211_WMM_IE_STA_QOSINFO_AC_BK |\ - IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) -#define IWL_MVM_PS_HEAVY_TX_THLD_PACKETS 20 -#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS 8 -#define IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS 30 -#define IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS 20 -#define IWL_MVM_PS_HEAVY_TX_THLD_PERCENT 50 -#define IWL_MVM_PS_HEAVY_RX_THLD_PERCENT 50 -#define IWL_MVM_PS_SNOOZE_INTERVAL 25 -#define IWL_MVM_PS_SNOOZE_WINDOW 50 -#define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25 -#define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64 -#define IWL_MVM_BT_COEX_EN_RED_TXP_THRESH 62 -#define IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH 65 -#define IWL_MVM_BT_COEX_SYNC2SCO 1 -#define IWL_MVM_BT_COEX_CORUNNING 0 -#define IWL_MVM_BT_COEX_MPLUT 1 -#define IWL_MVM_BT_COEX_RRC 1 -#define IWL_MVM_BT_COEX_TTC 1 -#define IWL_MVM_BT_COEX_MPLUT_REG0 0x22002200 -#define IWL_MVM_BT_COEX_MPLUT_REG1 0x11118451 -#define IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS 30 -#define IWL_MVM_FW_MCAST_FILTER_PASS_ALL 0 -#define IWL_MVM_FW_BCAST_FILTER_PASS_ALL 0 -#define IWL_MVM_QUOTA_THRESHOLD 4 -#define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0 -#define IWL_MVM_RS_80_20_FAR_RANGE_TWEAK 1 -#define IWL_MVM_TOF_IS_RESPONDER 0 -#define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1 -#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2 -#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1 -#define IWL_MVM_RS_INITIAL_MIMO_NUM_RATES 3 -#define IWL_MVM_RS_INITIAL_SISO_NUM_RATES 3 -#define IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES 2 -#define IWL_MVM_RS_INITIAL_LEGACY_RETRIES 2 -#define IWL_MVM_RS_SECONDARY_LEGACY_RETRIES 1 -#define IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES 16 -#define IWL_MVM_RS_SECONDARY_SISO_NUM_RATES 3 -#define IWL_MVM_RS_SECONDARY_SISO_RETRIES 1 -#define IWL_MVM_RS_RATE_MIN_FAILURE_TH 3 -#define IWL_MVM_RS_RATE_MIN_SUCCESS_TH 8 -#define IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT 5 /* Seconds */ -#define IWL_MVM_RS_IDLE_TIMEOUT 5 /* Seconds */ -#define IWL_MVM_RS_MISSED_RATE_MAX 15 -#define IWL_MVM_RS_LEGACY_FAILURE_LIMIT 160 -#define IWL_MVM_RS_LEGACY_SUCCESS_LIMIT 480 -#define IWL_MVM_RS_LEGACY_TABLE_COUNT 160 -#define IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT 400 -#define IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT 4500 -#define IWL_MVM_RS_NON_LEGACY_TABLE_COUNT 1500 -#define IWL_MVM_RS_SR_FORCE_DECREASE 15 /* percent */ -#define IWL_MVM_RS_SR_NO_DECREASE 85 /* percent */ -#define IWL_MVM_RS_AGG_TIME_LIMIT 4000 /* 4 msecs. valid 100-8000 */ -#define IWL_MVM_RS_AGG_DISABLE_START 3 -#define IWL_MVM_RS_TPC_SR_FORCE_INCREASE 75 /* percent */ -#define IWL_MVM_RS_TPC_SR_NO_INCREASE 85 /* percent */ -#define IWL_MVM_RS_TPC_TX_POWER_STEP 3 - -#endif /* __MVM_CONSTANTS_H */ diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c deleted file mode 100644 index 85ae902df7c0..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ /dev/null @@ -1,2104 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include "iwl-modparams.h" -#include "fw-api.h" -#include "mvm.h" - -void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_gtk_rekey_data *data) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (iwlwifi_mod_params.sw_crypto) - return; - - mutex_lock(&mvm->mutex); - - memcpy(mvmvif->rekey_data.kek, data->kek, NL80211_KEK_LEN); - memcpy(mvmvif->rekey_data.kck, data->kck, NL80211_KCK_LEN); - mvmvif->rekey_data.replay_ctr = - cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr)); - mvmvif->rekey_data.valid = true; - - mutex_unlock(&mvm->mutex); -} - -#if IS_ENABLED(CONFIG_IPV6) -void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct inet6_dev *idev) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct inet6_ifaddr *ifa; - int idx = 0; - - read_lock_bh(&idev->lock); - list_for_each_entry(ifa, &idev->addr_list, if_list) { - mvmvif->target_ipv6_addrs[idx] = ifa->addr; - idx++; - if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX) - break; - } - read_unlock_bh(&idev->lock); - - mvmvif->num_target_ipv6_addrs = idx; -} -#endif - -void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, int idx) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->tx_key_idx = idx; -} - -static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out) -{ - int i; - - for (i = 0; i < IWL_P1K_SIZE; i++) - out[i] = cpu_to_le16(p1k[i]); -} - -struct wowlan_key_data { - struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc; - struct iwl_wowlan_tkip_params_cmd *tkip; - bool error, use_rsc_tsc, use_tkip; - int wep_key_idx; -}; - -static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key, - void *_data) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct wowlan_key_data *data = _data; - struct aes_sc *aes_sc, *aes_tx_sc = NULL; - struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; - struct iwl_p1k_cache *rx_p1ks; - u8 *rx_mic_key; - struct ieee80211_key_seq seq; - u32 cur_rx_iv32 = 0; - u16 p1k[IWL_P1K_SIZE]; - int ret, i; - - mutex_lock(&mvm->mutex); - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: { /* hack it for now */ - struct { - struct iwl_mvm_wep_key_cmd wep_key_cmd; - struct iwl_mvm_wep_key wep_key; - } __packed wkc = { - .wep_key_cmd.mac_id_n_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)), - .wep_key_cmd.num_keys = 1, - /* firmware sets STA_KEY_FLG_WEP_13BYTES */ - .wep_key_cmd.decryption_type = STA_KEY_FLG_WEP, - .wep_key.key_index = key->keyidx, - .wep_key.key_size = key->keylen, - }; - - /* - * This will fail -- the key functions don't set support - * pairwise WEP keys. However, that's better than silently - * failing WoWLAN. Or maybe not? - */ - if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) - break; - - memcpy(&wkc.wep_key.key[3], key->key, key->keylen); - if (key->keyidx == mvmvif->tx_key_idx) { - /* TX key must be at offset 0 */ - wkc.wep_key.key_offset = 0; - } else { - /* others start at 1 */ - data->wep_key_idx++; - wkc.wep_key.key_offset = data->wep_key_idx; - } - - ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc); - data->error = ret != 0; - - mvm->ptk_ivlen = key->iv_len; - mvm->ptk_icvlen = key->icv_len; - mvm->gtk_ivlen = key->iv_len; - mvm->gtk_icvlen = key->icv_len; - - /* don't upload key again */ - goto out_unlock; - } - default: - data->error = true; - goto out_unlock; - case WLAN_CIPHER_SUITE_AES_CMAC: - /* - * Ignore CMAC keys -- the WoWLAN firmware doesn't support them - * but we also shouldn't abort suspend due to that. It does have - * support for the IGTK key renewal, but doesn't really use the - * IGTK for anything. This means we could spuriously wake up or - * be deauthenticated, but that was considered acceptable. - */ - goto out_unlock; - case WLAN_CIPHER_SUITE_TKIP: - if (sta) { - tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; - tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; - - rx_p1ks = data->tkip->rx_uni; - - ieee80211_get_key_tx_seq(key, &seq); - tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); - - ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); - iwl_mvm_convert_p1k(p1k, data->tkip->tx.p1k); - - memcpy(data->tkip->mic_keys.tx, - &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], - IWL_MIC_KEY_SIZE); - - rx_mic_key = data->tkip->mic_keys.rx_unicast; - } else { - tkip_sc = - data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; - rx_p1ks = data->tkip->rx_multi; - rx_mic_key = data->tkip->mic_keys.rx_mcast; - } - - /* - * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 (as they need to to avoid replay attacks) - * for checking the IV in the frames. - */ - for (i = 0; i < IWL_NUM_RSC; i++) { - ieee80211_get_key_rx_seq(key, i, &seq); - tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); - /* wrapping isn't allowed, AP must rekey */ - if (seq.tkip.iv32 > cur_rx_iv32) - cur_rx_iv32 = seq.tkip.iv32; - } - - ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid, - cur_rx_iv32, p1k); - iwl_mvm_convert_p1k(p1k, rx_p1ks[0].p1k); - ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid, - cur_rx_iv32 + 1, p1k); - iwl_mvm_convert_p1k(p1k, rx_p1ks[1].p1k); - - memcpy(rx_mic_key, - &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], - IWL_MIC_KEY_SIZE); - - data->use_tkip = true; - data->use_rsc_tsc = true; - break; - case WLAN_CIPHER_SUITE_CCMP: - if (sta) { - u64 pn64; - - aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; - aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; - - pn64 = atomic64_read(&key->tx_pn); - aes_tx_sc->pn = cpu_to_le64(pn64); - } else { - aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; - } - - /* - * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 for checking the IV in the frames. - */ - for (i = 0; i < IWL_NUM_RSC; i++) { - u8 *pn = seq.ccmp.pn; - - ieee80211_get_key_rx_seq(key, i, &seq); - aes_sc[i].pn = cpu_to_le64((u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); - } - data->use_rsc_tsc = true; - break; - } - - /* - * The D3 firmware hardcodes the key offset 0 as the key it uses - * to transmit packets to the AP, i.e. the PTK. - */ - if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { - key->hw_key_idx = 0; - mvm->ptk_ivlen = key->iv_len; - mvm->ptk_icvlen = key->icv_len; - } else { - /* - * firmware only supports TSC/RSC for a single key, - * so if there are multiple keep overwriting them - * with new ones -- this relies on mac80211 doing - * list_add_tail(). - */ - key->hw_key_idx = 1; - mvm->gtk_ivlen = key->iv_len; - mvm->gtk_icvlen = key->icv_len; - } - - ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, true); - data->error = ret != 0; -out_unlock: - mutex_unlock(&mvm->mutex); -} - -static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, - struct cfg80211_wowlan *wowlan) -{ - struct iwl_wowlan_patterns_cmd *pattern_cmd; - struct iwl_host_cmd cmd = { - .id = WOWLAN_PATTERNS, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - }; - int i, err; - - if (!wowlan->n_patterns) - return 0; - - cmd.len[0] = sizeof(*pattern_cmd) + - wowlan->n_patterns * sizeof(struct iwl_wowlan_pattern); - - pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); - if (!pattern_cmd) - return -ENOMEM; - - pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); - - for (i = 0; i < wowlan->n_patterns; i++) { - int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); - - memcpy(&pattern_cmd->patterns[i].mask, - wowlan->patterns[i].mask, mask_len); - memcpy(&pattern_cmd->patterns[i].pattern, - wowlan->patterns[i].pattern, - wowlan->patterns[i].pattern_len); - pattern_cmd->patterns[i].mask_size = mask_len; - pattern_cmd->patterns[i].pattern_size = - wowlan->patterns[i].pattern_len; - } - - cmd.data[0] = pattern_cmd; - err = iwl_mvm_send_cmd(mvm, &cmd); - kfree(pattern_cmd); - return err; -} - -enum iwl_mvm_tcp_packet_type { - MVM_TCP_TX_SYN, - MVM_TCP_RX_SYNACK, - MVM_TCP_TX_DATA, - MVM_TCP_RX_ACK, - MVM_TCP_RX_WAKE, - MVM_TCP_TX_FIN, -}; - -static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr) -{ - __sum16 check = tcp_v4_check(len, saddr, daddr, 0); - return cpu_to_le16(be16_to_cpu((__force __be16)check)); -} - -static void iwl_mvm_build_tcp_packet(struct ieee80211_vif *vif, - struct cfg80211_wowlan_tcp *tcp, - void *_pkt, u8 *mask, - __le16 *pseudo_hdr_csum, - enum iwl_mvm_tcp_packet_type ptype) -{ - struct { - struct ethhdr eth; - struct iphdr ip; - struct tcphdr tcp; - u8 data[]; - } __packed *pkt = _pkt; - u16 ip_tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); - int i; - - pkt->eth.h_proto = cpu_to_be16(ETH_P_IP), - pkt->ip.version = 4; - pkt->ip.ihl = 5; - pkt->ip.protocol = IPPROTO_TCP; - - switch (ptype) { - case MVM_TCP_TX_SYN: - case MVM_TCP_TX_DATA: - case MVM_TCP_TX_FIN: - memcpy(pkt->eth.h_dest, tcp->dst_mac, ETH_ALEN); - memcpy(pkt->eth.h_source, vif->addr, ETH_ALEN); - pkt->ip.ttl = 128; - pkt->ip.saddr = tcp->src; - pkt->ip.daddr = tcp->dst; - pkt->tcp.source = cpu_to_be16(tcp->src_port); - pkt->tcp.dest = cpu_to_be16(tcp->dst_port); - /* overwritten for TX SYN later */ - pkt->tcp.doff = sizeof(struct tcphdr) / 4; - pkt->tcp.window = cpu_to_be16(65000); - break; - case MVM_TCP_RX_SYNACK: - case MVM_TCP_RX_ACK: - case MVM_TCP_RX_WAKE: - memcpy(pkt->eth.h_dest, vif->addr, ETH_ALEN); - memcpy(pkt->eth.h_source, tcp->dst_mac, ETH_ALEN); - pkt->ip.saddr = tcp->dst; - pkt->ip.daddr = tcp->src; - pkt->tcp.source = cpu_to_be16(tcp->dst_port); - pkt->tcp.dest = cpu_to_be16(tcp->src_port); - break; - default: - WARN_ON(1); - return; - } - - switch (ptype) { - case MVM_TCP_TX_SYN: - /* firmware assumes 8 option bytes - 8 NOPs for now */ - memset(pkt->data, 0x01, 8); - ip_tot_len += 8; - pkt->tcp.doff = (sizeof(struct tcphdr) + 8) / 4; - pkt->tcp.syn = 1; - break; - case MVM_TCP_TX_DATA: - ip_tot_len += tcp->payload_len; - memcpy(pkt->data, tcp->payload, tcp->payload_len); - pkt->tcp.psh = 1; - pkt->tcp.ack = 1; - break; - case MVM_TCP_TX_FIN: - pkt->tcp.fin = 1; - pkt->tcp.ack = 1; - break; - case MVM_TCP_RX_SYNACK: - pkt->tcp.syn = 1; - pkt->tcp.ack = 1; - break; - case MVM_TCP_RX_ACK: - pkt->tcp.ack = 1; - break; - case MVM_TCP_RX_WAKE: - ip_tot_len += tcp->wake_len; - pkt->tcp.psh = 1; - pkt->tcp.ack = 1; - memcpy(pkt->data, tcp->wake_data, tcp->wake_len); - break; - } - - switch (ptype) { - case MVM_TCP_TX_SYN: - case MVM_TCP_TX_DATA: - case MVM_TCP_TX_FIN: - pkt->ip.tot_len = cpu_to_be16(ip_tot_len); - pkt->ip.check = ip_fast_csum(&pkt->ip, pkt->ip.ihl); - break; - case MVM_TCP_RX_WAKE: - for (i = 0; i < DIV_ROUND_UP(tcp->wake_len, 8); i++) { - u8 tmp = tcp->wake_mask[i]; - mask[i + 6] |= tmp << 6; - if (i + 1 < DIV_ROUND_UP(tcp->wake_len, 8)) - mask[i + 7] = tmp >> 2; - } - /* fall through for ethernet/IP/TCP headers mask */ - case MVM_TCP_RX_SYNACK: - case MVM_TCP_RX_ACK: - mask[0] = 0xff; /* match ethernet */ - /* - * match ethernet, ip.version, ip.ihl - * the ip.ihl half byte is really masked out by firmware - */ - mask[1] = 0x7f; - mask[2] = 0x80; /* match ip.protocol */ - mask[3] = 0xfc; /* match ip.saddr, ip.daddr */ - mask[4] = 0x3f; /* match ip.daddr, tcp.source, tcp.dest */ - mask[5] = 0x80; /* match tcp flags */ - /* leave rest (0 or set for MVM_TCP_RX_WAKE) */ - break; - }; - - *pseudo_hdr_csum = pseudo_hdr_check(ip_tot_len - sizeof(struct iphdr), - pkt->ip.saddr, pkt->ip.daddr); -} - -static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct cfg80211_wowlan_tcp *tcp) -{ - struct iwl_wowlan_remote_wake_config *cfg; - struct iwl_host_cmd cmd = { - .id = REMOTE_WAKE_CONFIG_CMD, - .len = { sizeof(*cfg), }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - int ret; - - if (!tcp) - return 0; - - cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); - if (!cfg) - return -ENOMEM; - cmd.data[0] = cfg; - - cfg->max_syn_retries = 10; - cfg->max_data_retries = 10; - cfg->tcp_syn_ack_timeout = 1; /* seconds */ - cfg->tcp_ack_timeout = 1; /* seconds */ - - /* SYN (TX) */ - iwl_mvm_build_tcp_packet( - vif, tcp, cfg->syn_tx.data, NULL, - &cfg->syn_tx.info.tcp_pseudo_header_checksum, - MVM_TCP_TX_SYN); - cfg->syn_tx.info.tcp_payload_length = 0; - - /* SYN/ACK (RX) */ - iwl_mvm_build_tcp_packet( - vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask, - &cfg->synack_rx.info.tcp_pseudo_header_checksum, - MVM_TCP_RX_SYNACK); - cfg->synack_rx.info.tcp_payload_length = 0; - - /* KEEPALIVE/ACK (TX) */ - iwl_mvm_build_tcp_packet( - vif, tcp, cfg->keepalive_tx.data, NULL, - &cfg->keepalive_tx.info.tcp_pseudo_header_checksum, - MVM_TCP_TX_DATA); - cfg->keepalive_tx.info.tcp_payload_length = - cpu_to_le16(tcp->payload_len); - cfg->sequence_number_offset = tcp->payload_seq.offset; - /* length must be 0..4, the field is little endian */ - cfg->sequence_number_length = tcp->payload_seq.len; - cfg->initial_sequence_number = cpu_to_le32(tcp->payload_seq.start); - cfg->keepalive_interval = cpu_to_le16(tcp->data_interval); - if (tcp->payload_tok.len) { - cfg->token_offset = tcp->payload_tok.offset; - cfg->token_length = tcp->payload_tok.len; - cfg->num_tokens = - cpu_to_le16(tcp->tokens_size % tcp->payload_tok.len); - memcpy(cfg->tokens, tcp->payload_tok.token_stream, - tcp->tokens_size); - } else { - /* set tokens to max value to almost never run out */ - cfg->num_tokens = cpu_to_le16(65535); - } - - /* ACK (RX) */ - iwl_mvm_build_tcp_packet( - vif, tcp, cfg->keepalive_ack_rx.data, - cfg->keepalive_ack_rx.rx_mask, - &cfg->keepalive_ack_rx.info.tcp_pseudo_header_checksum, - MVM_TCP_RX_ACK); - cfg->keepalive_ack_rx.info.tcp_payload_length = 0; - - /* WAKEUP (RX) */ - iwl_mvm_build_tcp_packet( - vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask, - &cfg->wake_rx.info.tcp_pseudo_header_checksum, - MVM_TCP_RX_WAKE); - cfg->wake_rx.info.tcp_payload_length = - cpu_to_le16(tcp->wake_len); - - /* FIN */ - iwl_mvm_build_tcp_packet( - vif, tcp, cfg->fin_tx.data, NULL, - &cfg->fin_tx.info.tcp_pseudo_header_checksum, - MVM_TCP_TX_FIN); - cfg->fin_tx.info.tcp_payload_length = 0; - - ret = iwl_mvm_send_cmd(mvm, &cmd); - kfree(cfg); - - return ret; -} - -static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *ap_sta) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_chanctx_conf *ctx; - u8 chains_static, chains_dynamic; - struct cfg80211_chan_def chandef; - int ret, i; - struct iwl_binding_cmd binding_cmd = {}; - struct iwl_time_quota_cmd quota_cmd = {}; - u32 status; - - /* add back the PHY */ - if (WARN_ON(!mvmvif->phy_ctxt)) - return -EINVAL; - - rcu_read_lock(); - ctx = rcu_dereference(vif->chanctx_conf); - if (WARN_ON(!ctx)) { - rcu_read_unlock(); - return -EINVAL; - } - chandef = ctx->def; - chains_static = ctx->rx_chains_static; - chains_dynamic = ctx->rx_chains_dynamic; - rcu_read_unlock(); - - ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, &chandef, - chains_static, chains_dynamic); - if (ret) - return ret; - - /* add back the MAC */ - mvmvif->uploaded = false; - - if (WARN_ON(!vif->bss_conf.assoc)) - return -EINVAL; - - ret = iwl_mvm_mac_ctxt_add(mvm, vif); - if (ret) - return ret; - - /* add back binding - XXX refactor? */ - binding_cmd.id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, - mvmvif->phy_ctxt->color)); - binding_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); - binding_cmd.phy = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, - mvmvif->phy_ctxt->color)); - binding_cmd.macs[0] = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)); - for (i = 1; i < MAX_MACS_IN_BINDING; i++) - binding_cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID); - - status = 0; - ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD, - sizeof(binding_cmd), &binding_cmd, - &status); - if (ret) { - IWL_ERR(mvm, "Failed to add binding: %d\n", ret); - return ret; - } - - if (status) { - IWL_ERR(mvm, "Binding command failed: %u\n", status); - return -EIO; - } - - ret = iwl_mvm_sta_send_to_fw(mvm, ap_sta, false); - if (ret) - return ret; - rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta); - - ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - if (ret) - return ret; - - /* and some quota */ - quota_cmd.quotas[0].id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id, - mvmvif->phy_ctxt->color)); - quota_cmd.quotas[0].quota = cpu_to_le32(IWL_MVM_MAX_QUOTA); - quota_cmd.quotas[0].max_duration = cpu_to_le32(IWL_MVM_MAX_QUOTA); - - for (i = 1; i < MAX_BINDINGS; i++) - quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID); - - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, - sizeof(quota_cmd), "a_cmd); - if (ret) - IWL_ERR(mvm, "Failed to send quota: %d\n", ret); - - if (iwl_mvm_is_lar_supported(mvm) && iwl_mvm_init_fw_regd(mvm)) - IWL_ERR(mvm, "Failed to initialize D3 LAR information\n"); - - return 0; -} - -static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_nonqos_seq_query_cmd query_cmd = { - .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_GET), - .mac_id_n_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)), - }; - struct iwl_host_cmd cmd = { - .id = NON_QOS_TX_COUNTER_CMD, - .flags = CMD_WANT_SKB, - }; - int err; - u32 size; - - cmd.data[0] = &query_cmd; - cmd.len[0] = sizeof(query_cmd); - - err = iwl_mvm_send_cmd(mvm, &cmd); - if (err) - return err; - - size = iwl_rx_packet_payload_len(cmd.resp_pkt); - if (size < sizeof(__le16)) { - err = -EINVAL; - } else { - err = le16_to_cpup((__le16 *)cmd.resp_pkt->data); - /* firmware returns next, not last-used seqno */ - err = (u16) (err - 0x10); - } - - iwl_free_resp(&cmd); - return err; -} - -void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_nonqos_seq_query_cmd query_cmd = { - .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_SET), - .mac_id_n_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)), - .value = cpu_to_le16(mvmvif->seqno), - }; - - /* return if called during restart, not resume from D3 */ - if (!mvmvif->seqno_valid) - return; - - mvmvif->seqno_valid = false; - - if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, 0, - sizeof(query_cmd), &query_cmd)) - IWL_ERR(mvm, "failed to set non-QoS seqno\n"); -} - -static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm) -{ - iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); - - iwl_trans_stop_device(mvm->trans); - - /* - * Set the HW restart bit -- this is mostly true as we're - * going to load new firmware and reprogram that, though - * the reprogramming is going to be manual to avoid adding - * all the MACs that aren't support. - * We don't have to clear up everything though because the - * reprogramming is manual. When we resume, we'll actually - * go through a proper restart sequence again to switch - * back to the runtime firmware image. - */ - set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - - /* We reprogram keys and shouldn't allocate new key indices */ - memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); - - mvm->ptk_ivlen = 0; - mvm->ptk_icvlen = 0; - mvm->ptk_ivlen = 0; - mvm->ptk_icvlen = 0; - - return iwl_mvm_load_d3_fw(mvm); -} - -static int -iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, - struct cfg80211_wowlan *wowlan, - struct iwl_wowlan_config_cmd *wowlan_config_cmd, - struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, - struct ieee80211_sta *ap_sta) -{ - int ret; - struct iwl_mvm_sta *mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); - - /* TODO: wowlan_config_cmd->wowlan_ba_teardown_tids */ - - wowlan_config_cmd->is_11n_connection = - ap_sta->ht_cap.ht_supported; - - /* Query the last used seqno and set it */ - ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); - if (ret < 0) - return ret; - - wowlan_config_cmd->non_qos_seq = cpu_to_le16(ret); - - iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, wowlan_config_cmd); - - if (wowlan->disconnect) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | - IWL_WOWLAN_WAKEUP_LINK_CHANGE); - if (wowlan->magic_pkt) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET); - if (wowlan->gtk_rekey_failure) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL); - if (wowlan->eap_identity_req) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ); - if (wowlan->four_way_handshake) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE); - if (wowlan->n_patterns) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH); - - if (wowlan->rfkill_release) - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); - - if (wowlan->tcp) { - /* - * Set the "link change" (really "link lost") flag as well - * since that implies losing the TCP connection. - */ - wowlan_config_cmd->wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS | - IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE | - IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET | - IWL_WOWLAN_WAKEUP_LINK_CHANGE); - } - - return 0; -} - -static int -iwl_mvm_wowlan_config(struct iwl_mvm *mvm, - struct cfg80211_wowlan *wowlan, - struct iwl_wowlan_config_cmd *wowlan_config_cmd, - struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, - struct ieee80211_sta *ap_sta) -{ - struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; - struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; - struct wowlan_key_data key_data = { - .use_rsc_tsc = false, - .tkip = &tkip_cmd, - .use_tkip = false, - }; - int ret; - - ret = iwl_mvm_switch_to_d3(mvm); - if (ret) - return ret; - - ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta); - if (ret) - return ret; - - key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); - if (!key_data.rsc_tsc) - return -ENOMEM; - - if (!iwlwifi_mod_params.sw_crypto) { - /* - * This needs to be unlocked due to lock ordering - * constraints. Since we're in the suspend path - * that isn't really a problem though. - */ - mutex_unlock(&mvm->mutex); - ieee80211_iter_keys(mvm->hw, vif, - iwl_mvm_wowlan_program_keys, - &key_data); - mutex_lock(&mvm->mutex); - if (key_data.error) { - ret = -EIO; - goto out; - } - - if (key_data.use_rsc_tsc) { - struct iwl_host_cmd rsc_tsc_cmd = { - .id = WOWLAN_TSC_RSC_PARAM, - .data[0] = key_data.rsc_tsc, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .len[0] = sizeof(*key_data.rsc_tsc), - }; - - ret = iwl_mvm_send_cmd(mvm, &rsc_tsc_cmd); - if (ret) - goto out; - } - - if (key_data.use_tkip) { - ret = iwl_mvm_send_cmd_pdu(mvm, - WOWLAN_TKIP_PARAM, - 0, sizeof(tkip_cmd), - &tkip_cmd); - if (ret) - goto out; - } - - if (mvmvif->rekey_data.valid) { - memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); - memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck, - NL80211_KCK_LEN); - kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); - memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek, - NL80211_KEK_LEN); - kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); - kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr; - - ret = iwl_mvm_send_cmd_pdu(mvm, - WOWLAN_KEK_KCK_MATERIAL, 0, - sizeof(kek_kck_cmd), - &kek_kck_cmd); - if (ret) - goto out; - } - } - - ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, - sizeof(*wowlan_config_cmd), - wowlan_config_cmd); - if (ret) - goto out; - - ret = iwl_mvm_send_patterns(mvm, wowlan); - if (ret) - goto out; - - ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0); - if (ret) - goto out; - - ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp); - -out: - kfree(key_data.rsc_tsc); - return ret; -} - -static int -iwl_mvm_netdetect_config(struct iwl_mvm *mvm, - struct cfg80211_wowlan *wowlan, - struct cfg80211_sched_scan_request *nd_config, - struct ieee80211_vif *vif) -{ - struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; - int ret; - - ret = iwl_mvm_switch_to_d3(mvm); - if (ret) - return ret; - - /* rfkill release can be either for wowlan or netdetect */ - if (wowlan->rfkill_release) - wowlan_config_cmd.wakeup_filter |= - cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); - - ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, - sizeof(wowlan_config_cmd), - &wowlan_config_cmd); - if (ret) - return ret; - - ret = iwl_mvm_sched_scan_start(mvm, vif, nd_config, &mvm->nd_ies, - IWL_MVM_SCAN_NETDETECT); - if (ret) - return ret; - - if (WARN_ON(mvm->nd_match_sets || mvm->nd_channels)) - return -EBUSY; - - /* save the sched scan matchsets... */ - if (nd_config->n_match_sets) { - mvm->nd_match_sets = kmemdup(nd_config->match_sets, - sizeof(*nd_config->match_sets) * - nd_config->n_match_sets, - GFP_KERNEL); - if (mvm->nd_match_sets) - mvm->n_nd_match_sets = nd_config->n_match_sets; - } - - /* ...and the sched scan channels for later reporting */ - mvm->nd_channels = kmemdup(nd_config->channels, - sizeof(*nd_config->channels) * - nd_config->n_channels, - GFP_KERNEL); - if (mvm->nd_channels) - mvm->n_nd_channels = nd_config->n_channels; - - return 0; -} - -static void iwl_mvm_free_nd(struct iwl_mvm *mvm) -{ - kfree(mvm->nd_match_sets); - mvm->nd_match_sets = NULL; - mvm->n_nd_match_sets = 0; - kfree(mvm->nd_channels); - mvm->nd_channels = NULL; - mvm->n_nd_channels = 0; -} - -static int __iwl_mvm_suspend(struct ieee80211_hw *hw, - struct cfg80211_wowlan *wowlan, - bool test) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct ieee80211_vif *vif = NULL; - struct iwl_mvm_vif *mvmvif = NULL; - struct ieee80211_sta *ap_sta = NULL; - struct iwl_d3_manager_config d3_cfg_cmd_data = { - /* - * Program the minimum sleep time to 10 seconds, as many - * platforms have issues processing a wakeup signal while - * still being in the process of suspending. - */ - .min_sleep_time = cpu_to_le32(10 * 1000 * 1000), - }; - struct iwl_host_cmd d3_cfg_cmd = { - .id = D3_CONFIG_CMD, - .flags = CMD_WANT_SKB, - .data[0] = &d3_cfg_cmd_data, - .len[0] = sizeof(d3_cfg_cmd_data), - }; - int ret; - int len __maybe_unused; - - if (!wowlan) { - /* - * mac80211 shouldn't get here, but for D3 test - * it doesn't warrant a warning - */ - WARN_ON(!test); - return -EINVAL; - } - - mutex_lock(&mvm->mutex); - - vif = iwl_mvm_get_bss_vif(mvm); - if (IS_ERR_OR_NULL(vif)) { - ret = 1; - goto out_noreset; - } - - mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) { - /* if we're not associated, this must be netdetect */ - if (!wowlan->nd_config && !mvm->nd_config) { - ret = 1; - goto out_noreset; - } - - ret = iwl_mvm_netdetect_config( - mvm, wowlan, wowlan->nd_config ?: mvm->nd_config, vif); - if (ret) - goto out; - - mvm->net_detect = true; - } else { - struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; - - ap_sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(ap_sta)) { - ret = -EINVAL; - goto out_noreset; - } - - ret = iwl_mvm_get_wowlan_config(mvm, wowlan, &wowlan_config_cmd, - vif, mvmvif, ap_sta); - if (ret) - goto out_noreset; - ret = iwl_mvm_wowlan_config(mvm, wowlan, &wowlan_config_cmd, - vif, mvmvif, ap_sta); - if (ret) - goto out; - - mvm->net_detect = false; - } - - ret = iwl_mvm_power_update_device(mvm); - if (ret) - goto out; - - ret = iwl_mvm_power_update_mac(mvm); - if (ret) - goto out; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvm->d3_wake_sysassert) - d3_cfg_cmd_data.wakeup_flags |= - cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR); -#endif - - /* must be last -- this switches firmware state */ - ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd); - if (ret) - goto out; -#ifdef CONFIG_IWLWIFI_DEBUGFS - 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); - } -#endif - iwl_free_resp(&d3_cfg_cmd); - - clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - - iwl_trans_d3_suspend(mvm->trans, test); - out: - if (ret < 0) { - iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); - ieee80211_restart_hw(mvm->hw); - iwl_mvm_free_nd(mvm); - } - out_noreset: - mutex_unlock(&mvm->mutex); - - return ret; -} - -static int iwl_mvm_enter_d0i3_sync(struct iwl_mvm *mvm) -{ - struct iwl_notification_wait wait_d3; - static const u16 d3_notif[] = { D3_CONFIG_CMD }; - int ret; - - iwl_init_notification_wait(&mvm->notif_wait, &wait_d3, - d3_notif, ARRAY_SIZE(d3_notif), - NULL, NULL); - - ret = iwl_mvm_enter_d0i3(mvm->hw->priv); - if (ret) - goto remove_notif; - - ret = iwl_wait_notification(&mvm->notif_wait, &wait_d3, HZ); - WARN_ON_ONCE(ret); - return ret; - -remove_notif: - iwl_remove_notification(&mvm->notif_wait, &wait_d3); - return ret; -} - -int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - /* make sure the d0i3 exit work is not pending */ - flush_work(&mvm->d0i3_exit_work); - - ret = iwl_trans_suspend(mvm->trans); - if (ret) - return ret; - - mvm->trans->wowlan_d0i3 = wowlan->any; - if (mvm->trans->wowlan_d0i3) { - /* 'any' trigger means d0i3 usage */ - if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { - ret = iwl_mvm_enter_d0i3_sync(mvm); - - if (ret) - return ret; - } - - mutex_lock(&mvm->d0i3_suspend_mutex); - __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); - mutex_unlock(&mvm->d0i3_suspend_mutex); - - iwl_trans_d3_suspend(mvm->trans, false); - - return 0; - } - - return __iwl_mvm_suspend(hw, wowlan, false); -} - -/* converted data from the different status responses */ -struct iwl_wowlan_status_data { - u16 pattern_number; - u16 qos_seq_ctr[8]; - u32 wakeup_reasons; - u32 wake_packet_length; - u32 wake_packet_bufsize; - const u8 *wake_packet; -}; - -static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_wowlan_status_data *status) -{ - struct sk_buff *pkt = NULL; - struct cfg80211_wowlan_wakeup wakeup = { - .pattern_idx = -1, - }; - struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; - u32 reasons = status->wakeup_reasons; - - if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { - wakeup_report = NULL; - goto report; - } - - if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) - wakeup.magic_pkt = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) - wakeup.pattern_idx = - status->pattern_number; - - if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) - wakeup.disconnect = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) - wakeup.gtk_rekey_failure = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) - wakeup.rfkill_release = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) - wakeup.eap_identity_req = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) - wakeup.four_way_handshake = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS) - wakeup.tcp_connlost = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE) - wakeup.tcp_nomoretokens = true; - - if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET) - wakeup.tcp_match = true; - - if (status->wake_packet_bufsize) { - int pktsize = status->wake_packet_bufsize; - int pktlen = status->wake_packet_length; - const u8 *pktdata = status->wake_packet; - struct ieee80211_hdr *hdr = (void *)pktdata; - int truncated = pktlen - pktsize; - - /* this would be a firmware bug */ - if (WARN_ON_ONCE(truncated < 0)) - truncated = 0; - - if (ieee80211_is_data(hdr->frame_control)) { - int hdrlen = ieee80211_hdrlen(hdr->frame_control); - int ivlen = 0, icvlen = 4; /* also FCS */ - - pkt = alloc_skb(pktsize, GFP_KERNEL); - if (!pkt) - goto report; - - memcpy(skb_put(pkt, hdrlen), pktdata, hdrlen); - pktdata += hdrlen; - pktsize -= hdrlen; - - if (ieee80211_has_protected(hdr->frame_control)) { - /* - * This is unlocked and using gtk_i(c)vlen, - * but since everything is under RTNL still - * that's not really a problem - changing - * it would be difficult. - */ - if (is_multicast_ether_addr(hdr->addr1)) { - ivlen = mvm->gtk_ivlen; - icvlen += mvm->gtk_icvlen; - } else { - ivlen = mvm->ptk_ivlen; - icvlen += mvm->ptk_icvlen; - } - } - - /* if truncated, FCS/ICV is (partially) gone */ - if (truncated >= icvlen) { - icvlen = 0; - truncated -= icvlen; - } else { - icvlen -= truncated; - truncated = 0; - } - - pktsize -= ivlen + icvlen; - pktdata += ivlen; - - memcpy(skb_put(pkt, pktsize), pktdata, pktsize); - - if (ieee80211_data_to_8023(pkt, vif->addr, vif->type)) - goto report; - wakeup.packet = pkt->data; - wakeup.packet_present_len = pkt->len; - wakeup.packet_len = pkt->len - truncated; - wakeup.packet_80211 = false; - } else { - int fcslen = 4; - - if (truncated >= 4) { - truncated -= 4; - fcslen = 0; - } else { - fcslen -= truncated; - truncated = 0; - } - pktsize -= fcslen; - wakeup.packet = status->wake_packet; - wakeup.packet_present_len = pktsize; - wakeup.packet_len = pktlen - truncated; - wakeup.packet_80211 = true; - } - } - - report: - ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); - kfree_skb(pkt); -} - -static void iwl_mvm_aes_sc_to_seq(struct aes_sc *sc, - struct ieee80211_key_seq *seq) -{ - u64 pn; - - pn = le64_to_cpu(sc->pn); - seq->ccmp.pn[0] = pn >> 40; - seq->ccmp.pn[1] = pn >> 32; - seq->ccmp.pn[2] = pn >> 24; - seq->ccmp.pn[3] = pn >> 16; - seq->ccmp.pn[4] = pn >> 8; - seq->ccmp.pn[5] = pn; -} - -static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc, - struct ieee80211_key_seq *seq) -{ - seq->tkip.iv32 = le32_to_cpu(sc->iv32); - seq->tkip.iv16 = le16_to_cpu(sc->iv16); -} - -static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs, - struct ieee80211_key_conf *key) -{ - int tid; - - BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); - - for (tid = 0; tid < IWL_NUM_RSC; tid++) { - struct ieee80211_key_seq seq = {}; - - iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); - ieee80211_set_key_rx_seq(key, tid, &seq); - } -} - -static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs, - struct ieee80211_key_conf *key) -{ - int tid; - - BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); - - for (tid = 0; tid < IWL_NUM_RSC; tid++) { - struct ieee80211_key_seq seq = {}; - - iwl_mvm_tkip_sc_to_seq(&scs[tid], &seq); - ieee80211_set_key_rx_seq(key, tid, &seq); - } -} - -static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key, - struct iwl_wowlan_status *status) -{ - union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc; - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key); - break; - case WLAN_CIPHER_SUITE_TKIP: - iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key); - break; - default: - WARN_ON(1); - } -} - -struct iwl_mvm_d3_gtk_iter_data { - struct iwl_wowlan_status *status; - void *last_gtk; - u32 cipher; - bool find_phase, unhandled_cipher; - int num_keys; -}; - -static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key, - void *_data) -{ - struct iwl_mvm_d3_gtk_iter_data *data = _data; - - if (data->unhandled_cipher) - return; - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - /* ignore WEP completely, nothing to do */ - return; - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_TKIP: - /* we support these */ - break; - default: - /* everything else (even CMAC for MFP) - disconnect from AP */ - data->unhandled_cipher = true; - return; - } - - data->num_keys++; - - /* - * pairwise key - update sequence counters only; - * note that this assumes no TDLS sessions are active - */ - if (sta) { - struct ieee80211_key_seq seq = {}; - union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc; - - if (data->find_phase) - return; - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key); - atomic64_set(&key->tx_pn, le64_to_cpu(sc->aes.tsc.pn)); - break; - case WLAN_CIPHER_SUITE_TKIP: - iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq); - iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key); - ieee80211_set_key_tx_seq(key, &seq); - break; - } - - /* that's it for this key */ - return; - } - - if (data->find_phase) { - data->last_gtk = key; - data->cipher = key->cipher; - return; - } - - if (data->status->num_of_gtk_rekeys) - ieee80211_remove_key(key); - else if (data->last_gtk == key) - iwl_mvm_set_key_rx_seq(key, data->status); -} - -static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_wowlan_status *status) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_d3_gtk_iter_data gtkdata = { - .status = status, - }; - u32 disconnection_reasons = - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; - - if (!status || !vif->bss_conf.bssid) - return false; - - if (le32_to_cpu(status->wakeup_reasons) & disconnection_reasons) - return false; - - /* find last GTK that we used initially, if any */ - gtkdata.find_phase = true; - ieee80211_iter_keys(mvm->hw, vif, - iwl_mvm_d3_update_gtks, >kdata); - /* not trying to keep connections with MFP/unhandled ciphers */ - if (gtkdata.unhandled_cipher) - return false; - if (!gtkdata.num_keys) - goto out; - if (!gtkdata.last_gtk) - return false; - - /* - * invalidate all other GTKs that might still exist and update - * the one that we used - */ - gtkdata.find_phase = false; - ieee80211_iter_keys(mvm->hw, vif, - iwl_mvm_d3_update_gtks, >kdata); - - if (status->num_of_gtk_rekeys) { - struct ieee80211_key_conf *key; - struct { - struct ieee80211_key_conf conf; - u8 key[32]; - } conf = { - .conf.cipher = gtkdata.cipher, - .conf.keyidx = status->gtk.key_index, - }; - - switch (gtkdata.cipher) { - case WLAN_CIPHER_SUITE_CCMP: - conf.conf.keylen = WLAN_KEY_LEN_CCMP; - memcpy(conf.conf.key, status->gtk.decrypt_key, - WLAN_KEY_LEN_CCMP); - break; - case WLAN_CIPHER_SUITE_TKIP: - conf.conf.keylen = WLAN_KEY_LEN_TKIP; - memcpy(conf.conf.key, status->gtk.decrypt_key, 16); - /* leave TX MIC key zeroed, we don't use it anyway */ - memcpy(conf.conf.key + - NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, - status->gtk.tkip_mic_key, 8); - break; - } - - key = ieee80211_gtk_rekey_add(vif, &conf.conf); - if (IS_ERR(key)) - return false; - iwl_mvm_set_key_rx_seq(key, status); - } - - if (status->num_of_gtk_rekeys) { - __be64 replay_ctr = - cpu_to_be64(le64_to_cpu(status->replay_ctr)); - ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid, - (void *)&replay_ctr, GFP_KERNEL); - } - -out: - mvmvif->seqno_valid = true; - /* +0x10 because the set API expects next-to-use, not last-used */ - mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10; - - return true; -} - -static struct iwl_wowlan_status * -iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - u32 base = mvm->error_event_table; - struct error_table_start { - /* cf. struct iwl_error_event_table */ - u32 valid; - u32 error_id; - } err_info; - struct iwl_host_cmd cmd = { - .id = WOWLAN_GET_STATUSES, - .flags = CMD_WANT_SKB, - }; - struct iwl_wowlan_status *status, *fw_status; - int ret, len, status_size; - - iwl_trans_read_mem_bytes(mvm->trans, base, - &err_info, sizeof(err_info)); - - if (err_info.valid) { - IWL_INFO(mvm, "error table is valid (%d) with error (%d)\n", - err_info.valid, err_info.error_id); - if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) { - struct cfg80211_wowlan_wakeup wakeup = { - .rfkill_release = true, - }; - ieee80211_report_wowlan_wakeup(vif, &wakeup, - GFP_KERNEL); - } - return ERR_PTR(-EIO); - } - - /* only for tracing for now */ - ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, 0, 0, NULL); - if (ret) - IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) { - IWL_ERR(mvm, "failed to query status (%d)\n", ret); - return ERR_PTR(ret); - } - - /* RF-kill already asserted again... */ - if (!cmd.resp_pkt) { - fw_status = ERR_PTR(-ERFKILL); - goto out_free_resp; - } - - status_size = sizeof(*fw_status); - - len = iwl_rx_packet_payload_len(cmd.resp_pkt); - if (len < status_size) { - IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); - fw_status = ERR_PTR(-EIO); - goto out_free_resp; - } - - status = (void *)cmd.resp_pkt->data; - if (len != (status_size + - ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) { - IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); - fw_status = ERR_PTR(-EIO); - goto out_free_resp; - } - - fw_status = kmemdup(status, len, GFP_KERNEL); - -out_free_resp: - iwl_free_resp(&cmd); - return fw_status; -} - -/* releases the MVM mutex */ -static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_wowlan_status_data status; - struct iwl_wowlan_status *fw_status; - int i; - bool keep; - struct ieee80211_sta *ap_sta; - struct iwl_mvm_sta *mvm_ap_sta; - - fw_status = iwl_mvm_get_wakeup_status(mvm, vif); - if (IS_ERR_OR_NULL(fw_status)) - goto out_unlock; - - status.pattern_number = le16_to_cpu(fw_status->pattern_number); - for (i = 0; i < 8; i++) - status.qos_seq_ctr[i] = - le16_to_cpu(fw_status->qos_seq_ctr[i]); - status.wakeup_reasons = le32_to_cpu(fw_status->wakeup_reasons); - status.wake_packet_length = - le32_to_cpu(fw_status->wake_packet_length); - status.wake_packet_bufsize = - le32_to_cpu(fw_status->wake_packet_bufsize); - status.wake_packet = fw_status->wake_packet; - - /* still at hard-coded place 0 for D3 image */ - ap_sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[0], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(ap_sta)) - goto out_free; - - mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - u16 seq = status.qos_seq_ctr[i]; - /* firmware stores last-used value, we store next value */ - seq += 0x10; - mvm_ap_sta->tid_data[i].seq_number = seq; - } - - /* now we have all the data we need, unlock to avoid mac80211 issues */ - mutex_unlock(&mvm->mutex); - - iwl_mvm_report_wakeup_reasons(mvm, vif, &status); - - keep = iwl_mvm_setup_connection_keep(mvm, vif, fw_status); - - kfree(fw_status); - return keep; - -out_free: - kfree(fw_status); -out_unlock: - mutex_unlock(&mvm->mutex); - return false; -} - -struct iwl_mvm_nd_query_results { - u32 matched_profiles; - struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES]; -}; - -static int -iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm, - struct iwl_mvm_nd_query_results *results) -{ - struct iwl_scan_offload_profiles_query *query; - struct iwl_host_cmd cmd = { - .id = SCAN_OFFLOAD_PROFILES_QUERY_CMD, - .flags = CMD_WANT_SKB, - }; - int ret, len; - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) { - IWL_ERR(mvm, "failed to query matched profiles (%d)\n", ret); - return ret; - } - - /* RF-kill already asserted again... */ - if (!cmd.resp_pkt) { - ret = -ERFKILL; - goto out_free_resp; - } - - len = iwl_rx_packet_payload_len(cmd.resp_pkt); - if (len < sizeof(*query)) { - IWL_ERR(mvm, "Invalid scan offload profiles query response!\n"); - ret = -EIO; - goto out_free_resp; - } - - query = (void *)cmd.resp_pkt->data; - - results->matched_profiles = le32_to_cpu(query->matched_profiles); - memcpy(results->matches, query->matches, sizeof(results->matches)); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - mvm->last_netdetect_scans = le32_to_cpu(query->n_scans_done); -#endif - -out_free_resp: - iwl_free_resp(&cmd); - return ret; -} - -static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct cfg80211_wowlan_nd_info *net_detect = NULL; - struct cfg80211_wowlan_wakeup wakeup = { - .pattern_idx = -1, - }; - struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; - struct iwl_mvm_nd_query_results query; - struct iwl_wowlan_status *fw_status; - unsigned long matched_profiles; - u32 reasons = 0; - int i, j, n_matches, ret; - - fw_status = iwl_mvm_get_wakeup_status(mvm, vif); - if (!IS_ERR_OR_NULL(fw_status)) { - reasons = le32_to_cpu(fw_status->wakeup_reasons); - kfree(fw_status); - } - - if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) - wakeup.rfkill_release = true; - - if (reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) - goto out; - - ret = iwl_mvm_netdetect_query_results(mvm, &query); - if (ret || !query.matched_profiles) { - wakeup_report = NULL; - goto out; - } - - matched_profiles = query.matched_profiles; - if (mvm->n_nd_match_sets) { - n_matches = hweight_long(matched_profiles); - } else { - IWL_ERR(mvm, "no net detect match information available\n"); - n_matches = 0; - } - - net_detect = kzalloc(sizeof(*net_detect) + - (n_matches * sizeof(net_detect->matches[0])), - GFP_KERNEL); - if (!net_detect || !n_matches) - goto out_report_nd; - - for_each_set_bit(i, &matched_profiles, mvm->n_nd_match_sets) { - struct iwl_scan_offload_profile_match *fw_match; - struct cfg80211_wowlan_nd_match *match; - int idx, n_channels = 0; - - fw_match = &query.matches[i]; - - for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; j++) - n_channels += hweight8(fw_match->matching_channels[j]); - - match = kzalloc(sizeof(*match) + - (n_channels * sizeof(*match->channels)), - GFP_KERNEL); - if (!match) - goto out_report_nd; - - net_detect->matches[net_detect->n_matches++] = match; - - /* We inverted the order of the SSIDs in the scan - * request, so invert the index here. - */ - idx = mvm->n_nd_match_sets - i - 1; - match->ssid.ssid_len = mvm->nd_match_sets[idx].ssid.ssid_len; - memcpy(match->ssid.ssid, mvm->nd_match_sets[idx].ssid.ssid, - match->ssid.ssid_len); - - if (mvm->n_nd_channels < n_channels) - continue; - - for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; j++) - if (fw_match->matching_channels[j / 8] & (BIT(j % 8))) - match->channels[match->n_channels++] = - mvm->nd_channels[j]->center_freq; - } - -out_report_nd: - wakeup.net_detect = net_detect; -out: - iwl_mvm_free_nd(mvm); - - mutex_unlock(&mvm->mutex); - ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); - - if (net_detect) { - for (i = 0; i < net_detect->n_matches; i++) - kfree(net_detect->matches[i]); - kfree(net_detect); - } -} - -static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) -{ -#ifdef CONFIG_IWLWIFI_DEBUGFS - const struct fw_img *img = &mvm->fw->img[IWL_UCODE_WOWLAN]; - u32 len = img->sec[IWL_UCODE_SECTION_DATA].len; - u32 offs = img->sec[IWL_UCODE_SECTION_DATA].offset; - - if (!mvm->store_d3_resume_sram) - return; - - if (!mvm->d3_resume_sram) { - mvm->d3_resume_sram = kzalloc(len, GFP_KERNEL); - if (!mvm->d3_resume_sram) - return; - } - - iwl_trans_read_mem_bytes(mvm->trans, offs, mvm->d3_resume_sram, len); -#endif -} - -static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - /* skip the one we keep connection on */ - if (data == vif) - return; - - if (vif->type == NL80211_IFTYPE_STATION) - ieee80211_resume_disconnect(vif); -} - -static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) -{ - struct ieee80211_vif *vif = NULL; - int ret; - enum iwl_d3_status d3_status; - bool keep = false; - - mutex_lock(&mvm->mutex); - - /* get the BSS vif pointer again */ - vif = iwl_mvm_get_bss_vif(mvm); - if (IS_ERR_OR_NULL(vif)) - goto err; - - ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test); - if (ret) - goto err; - - if (d3_status != IWL_D3_STATUS_ALIVE) { - IWL_INFO(mvm, "Device was reset during suspend\n"); - goto err; - } - - /* query SRAM first in case we want event logging */ - iwl_mvm_read_d3_sram(mvm); - - /* - * Query the current location and source from the D3 firmware so we - * can play it back when we re-intiailize the D0 firmware - */ - iwl_mvm_update_changed_regdom(mvm); - - if (mvm->net_detect) { - iwl_mvm_query_netdetect_reasons(mvm, vif); - /* has unlocked the mutex, so skip that */ - goto out; - } else { - keep = iwl_mvm_query_wakeup_reasons(mvm, vif); -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (keep) - mvm->keep_vif = vif; -#endif - /* has unlocked the mutex, so skip that */ - goto out_iterate; - } - -err: - iwl_mvm_free_nd(mvm); - mutex_unlock(&mvm->mutex); - -out_iterate: - if (!test) - ieee80211_iterate_active_interfaces_rtnl(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_d3_disconnect_iter, keep ? vif : NULL); - -out: - /* return 1 to reconfigure the device */ - set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status); - - /* We always return 1, which causes mac80211 to do a reconfig - * with IEEE80211_RECONFIG_TYPE_RESTART. This type of - * reconfig calls iwl_mvm_restart_complete(), where we unref - * the IWL_MVM_REF_UCODE_DOWN, so we need to take the - * reference here. - */ - iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); - return 1; -} - -static int iwl_mvm_resume_d3(struct iwl_mvm *mvm) -{ - iwl_trans_resume(mvm->trans); - - return __iwl_mvm_resume(mvm, false); -} - -static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm) -{ - bool exit_now; - enum iwl_d3_status d3_status; - - iwl_trans_d3_resume(mvm->trans, &d3_status, false); - - /* - * make sure to clear D0I3_DEFER_WAKEUP before - * calling iwl_trans_resume(), which might wait - * for d0i3 exit completion. - */ - mutex_lock(&mvm->d0i3_suspend_mutex); - __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); - exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP, - &mvm->d0i3_suspend_flags); - mutex_unlock(&mvm->d0i3_suspend_mutex); - if (exit_now) { - IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n"); - _iwl_mvm_exit_d0i3(mvm); - } - - iwl_trans_resume(mvm->trans); - - if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { - int ret = iwl_mvm_exit_d0i3(mvm->hw->priv); - - if (ret) - return ret; - /* - * d0i3 exit will be deferred until reconfig_complete. - * make sure there we are out of d0i3. - */ - } - return 0; -} - -int iwl_mvm_resume(struct ieee80211_hw *hw) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - /* 'any' trigger means d0i3 was used */ - if (hw->wiphy->wowlan_config->any) - return iwl_mvm_resume_d0i3(mvm); - else - return iwl_mvm_resume_d3(mvm); -} - -void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - device_set_wakeup_enable(mvm->trans->dev, enabled); -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file) -{ - struct iwl_mvm *mvm = inode->i_private; - int err; - - if (mvm->d3_test_active) - return -EBUSY; - - file->private_data = inode->i_private; - - ieee80211_stop_queues(mvm->hw); - synchronize_net(); - - /* start pseudo D3 */ - rtnl_lock(); - err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true); - rtnl_unlock(); - if (err > 0) - err = -EINVAL; - if (err) { - ieee80211_wake_queues(mvm->hw); - return err; - } - mvm->d3_test_active = true; - mvm->keep_vif = NULL; - return 0; -} - -static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - u32 pme_asserted; - - while (true) { - /* read pme_ptr if available */ - if (mvm->d3_test_pme_ptr) { - pme_asserted = iwl_trans_read_mem32(mvm->trans, - mvm->d3_test_pme_ptr); - if (pme_asserted) - break; - } - - if (msleep_interruptible(100)) - break; - } - - return 0; -} - -static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - /* skip the one we keep connection on */ - if (_data == vif) - return; - - if (vif->type == NL80211_IFTYPE_STATION) - ieee80211_connection_loss(vif); -} - -static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) -{ - struct iwl_mvm *mvm = inode->i_private; - int remaining_time = 10; - - mvm->d3_test_active = false; - rtnl_lock(); - __iwl_mvm_resume(mvm, true); - rtnl_unlock(); - iwl_abort_notification_waits(&mvm->notif_wait); - ieee80211_restart_hw(mvm->hw); - - /* wait for restart and disconnect all interfaces */ - while (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && - remaining_time > 0) { - remaining_time--; - msleep(1000); - } - - if (remaining_time == 0) - IWL_ERR(mvm, "Timed out waiting for HW restart to finish!\n"); - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_d3_test_disconn_work_iter, mvm->keep_vif); - - ieee80211_wake_queues(mvm->hw); - - return 0; -} - -const struct file_operations iwl_dbgfs_d3_test_ops = { - .llseek = no_llseek, - .open = iwl_mvm_d3_test_open, - .read = iwl_mvm_d3_test_read, - .release = iwl_mvm_d3_test_release, -}; -#endif diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c deleted file mode 100644 index 7904b41a04c6..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ /dev/null @@ -1,1483 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include "mvm.h" -#include "fw-api-tof.h" -#include "debugfs.h" - -static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - enum iwl_dbgfs_pm_mask param, int val) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; - - dbgfs_pm->mask |= param; - - switch (param) { - case MVM_DEBUGFS_PM_KEEP_ALIVE: { - int dtimper = vif->bss_conf.dtim_period ?: 1; - int dtimper_msec = dtimper * vif->bss_conf.beacon_int; - - IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); - if (val * MSEC_PER_SEC < 3 * dtimper_msec) - IWL_WARN(mvm, - "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", - val * MSEC_PER_SEC, 3 * dtimper_msec); - dbgfs_pm->keep_alive_seconds = val; - break; - } - case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: - IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", - val ? "enabled" : "disabled"); - dbgfs_pm->skip_over_dtim = val; - break; - case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: - IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); - dbgfs_pm->skip_dtim_periods = val; - break; - case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: - IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); - dbgfs_pm->rx_data_timeout = val; - break; - case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: - IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); - dbgfs_pm->tx_data_timeout = val; - break; - case MVM_DEBUGFS_PM_LPRX_ENA: - IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled"); - dbgfs_pm->lprx_ena = val; - break; - case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD: - IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val); - dbgfs_pm->lprx_rssi_threshold = val; - break; - case MVM_DEBUGFS_PM_SNOOZE_ENABLE: - IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val); - dbgfs_pm->snooze_ena = val; - break; - case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING: - IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val); - dbgfs_pm->uapsd_misbehaving = val; - break; - case MVM_DEBUGFS_PM_USE_PS_POLL: - IWL_DEBUG_POWER(mvm, "use_ps_poll=%d\n", val); - dbgfs_pm->use_ps_poll = val; - break; - } -} - -static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - enum iwl_dbgfs_pm_mask param; - int val, ret; - - if (!strncmp("keep_alive=", buf, 11)) { - if (sscanf(buf + 11, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_KEEP_ALIVE; - } else if (!strncmp("skip_over_dtim=", buf, 15)) { - if (sscanf(buf + 15, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; - } else if (!strncmp("skip_dtim_periods=", buf, 18)) { - if (sscanf(buf + 18, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; - } else if (!strncmp("rx_data_timeout=", buf, 16)) { - if (sscanf(buf + 16, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; - } else if (!strncmp("tx_data_timeout=", buf, 16)) { - if (sscanf(buf + 16, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; - } else if (!strncmp("lprx=", buf, 5)) { - if (sscanf(buf + 5, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_LPRX_ENA; - } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) { - if (sscanf(buf + 20, "%d", &val) != 1) - return -EINVAL; - if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val < - POWER_LPRX_RSSI_THRESHOLD_MIN) - return -EINVAL; - param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD; - } else if (!strncmp("snooze_enable=", buf, 14)) { - if (sscanf(buf + 14, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_SNOOZE_ENABLE; - } else if (!strncmp("uapsd_misbehaving=", buf, 18)) { - if (sscanf(buf + 18, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING; - } else if (!strncmp("use_ps_poll=", buf, 12)) { - if (sscanf(buf + 12, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_USE_PS_POLL; - } else { - return -EINVAL; - } - - mutex_lock(&mvm->mutex); - iwl_dbgfs_update_pm(mvm, vif, param, val); - ret = iwl_mvm_power_update_mac(mvm); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tx_pwr_lmt_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - char buf[64]; - int bufsz = sizeof(buf); - int pos; - - pos = scnprintf(buf, bufsz, "bss limit = %d\n", - vif->bss_conf.txpower); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_pm_params_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - char buf[512]; - int bufsz = sizeof(buf); - int pos; - - pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_mac_params_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u8 ap_sta_id; - struct ieee80211_chanctx_conf *chanctx_conf; - char buf[512]; - int bufsz = sizeof(buf); - int pos = 0; - int i; - - mutex_lock(&mvm->mutex); - - ap_sta_id = mvmvif->ap_sta_id; - - switch (ieee80211_vif_type_p2p(vif)) { - case NL80211_IFTYPE_ADHOC: - pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n"); - break; - case NL80211_IFTYPE_STATION: - pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n"); - break; - case NL80211_IFTYPE_AP: - pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n"); - break; - case NL80211_IFTYPE_P2P_CLIENT: - pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n"); - break; - case NL80211_IFTYPE_P2P_GO: - pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n"); - break; - case NL80211_IFTYPE_P2P_DEVICE: - pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n"); - break; - default: - break; - } - - pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n", - mvmvif->id, mvmvif->color); - pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n", - vif->bss_conf.bssid); - pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n"); - for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) - pos += scnprintf(buf+pos, bufsz-pos, - "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n", - i, mvmvif->queue_params[i].txop, - mvmvif->queue_params[i].cw_min, - mvmvif->queue_params[i].cw_max, - mvmvif->queue_params[i].aifs, - mvmvif->queue_params[i].uapsd); - - if (vif->type == NL80211_IFTYPE_STATION && - ap_sta_id != IWL_MVM_STATION_COUNT) { - struct ieee80211_sta *sta; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], - lockdep_is_held(&mvm->mutex)); - if (!IS_ERR_OR_NULL(sta)) { - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - - pos += scnprintf(buf+pos, bufsz-pos, - "ap_sta_id %d - reduced Tx power %d\n", - ap_sta_id, - mvm_sta->bt_reduced_txpower); - } - } - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - if (chanctx_conf) - pos += scnprintf(buf+pos, bufsz-pos, - "idle rx chains %d, active rx chains: %d\n", - chanctx_conf->rx_chains_static, - chanctx_conf->rx_chains_dynamic); - rcu_read_unlock(); - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, - enum iwl_dbgfs_bf_mask param, int value) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; - - dbgfs_bf->mask |= param; - - switch (param) { - case MVM_DEBUGFS_BF_ENERGY_DELTA: - dbgfs_bf->bf_energy_delta = value; - break; - case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA: - dbgfs_bf->bf_roaming_energy_delta = value; - break; - case MVM_DEBUGFS_BF_ROAMING_STATE: - dbgfs_bf->bf_roaming_state = value; - break; - case MVM_DEBUGFS_BF_TEMP_THRESHOLD: - dbgfs_bf->bf_temp_threshold = value; - break; - case MVM_DEBUGFS_BF_TEMP_FAST_FILTER: - dbgfs_bf->bf_temp_fast_filter = value; - break; - case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER: - dbgfs_bf->bf_temp_slow_filter = value; - break; - case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: - dbgfs_bf->bf_enable_beacon_filter = value; - break; - case MVM_DEBUGFS_BF_DEBUG_FLAG: - dbgfs_bf->bf_debug_flag = value; - break; - case MVM_DEBUGFS_BF_ESCAPE_TIMER: - dbgfs_bf->bf_escape_timer = value; - break; - case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT: - dbgfs_bf->ba_enable_beacon_abort = value; - break; - case MVM_DEBUGFS_BA_ESCAPE_TIMER: - dbgfs_bf->ba_escape_timer = value; - break; - } -} - -static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - enum iwl_dbgfs_bf_mask param; - int value, ret = 0; - - if (!strncmp("bf_energy_delta=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ENERGY_DELTA_MIN || - value > IWL_BF_ENERGY_DELTA_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ENERGY_DELTA; - } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) { - if (sscanf(buf+24, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN || - value > IWL_BF_ROAMING_ENERGY_DELTA_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA; - } else if (!strncmp("bf_roaming_state=", buf, 17)) { - if (sscanf(buf+17, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ROAMING_STATE_MIN || - value > IWL_BF_ROAMING_STATE_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ROAMING_STATE; - } else if (!strncmp("bf_temp_threshold=", buf, 18)) { - if (sscanf(buf+18, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_THRESHOLD_MIN || - value > IWL_BF_TEMP_THRESHOLD_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_THRESHOLD; - } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) { - if (sscanf(buf+20, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_FAST_FILTER_MIN || - value > IWL_BF_TEMP_FAST_FILTER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER; - } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) { - if (sscanf(buf+20, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_SLOW_FILTER_MIN || - value > IWL_BF_TEMP_SLOW_FILTER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER; - } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { - if (sscanf(buf+24, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER; - } else if (!strncmp("bf_debug_flag=", buf, 14)) { - if (sscanf(buf+14, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - param = MVM_DEBUGFS_BF_DEBUG_FLAG; - } else if (!strncmp("bf_escape_timer=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ESCAPE_TIMER_MIN || - value > IWL_BF_ESCAPE_TIMER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ESCAPE_TIMER; - } else if (!strncmp("ba_escape_timer=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BA_ESCAPE_TIMER_MIN || - value > IWL_BA_ESCAPE_TIMER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BA_ESCAPE_TIMER; - } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) { - if (sscanf(buf+23, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT; - } else { - return -EINVAL; - } - - mutex_lock(&mvm->mutex); - iwl_dbgfs_update_bf(vif, param, value); - if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) - ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); - else - ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_bf_params_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = - cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT), - .ba_enable_beacon_abort = - cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT), - }; - - iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); - if (mvmvif->bf_data.bf_enabled) - cmd.bf_enable_beacon_filter = cpu_to_le32(1); - else - cmd.bf_enable_beacon_filter = 0; - - pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", - le32_to_cpu(cmd.bf_energy_delta)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", - le32_to_cpu(cmd.bf_roaming_energy_delta)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", - le32_to_cpu(cmd.bf_roaming_state)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n", - le32_to_cpu(cmd.bf_temp_threshold)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n", - le32_to_cpu(cmd.bf_temp_fast_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n", - le32_to_cpu(cmd.bf_temp_slow_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", - le32_to_cpu(cmd.bf_enable_beacon_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", - le32_to_cpu(cmd.bf_debug_flag)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", - le32_to_cpu(cmd.bf_escape_timer)); - pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", - le32_to_cpu(cmd.ba_escape_timer)); - pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", - le32_to_cpu(cmd.ba_enable_beacon_abort)); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static inline char *iwl_dbgfs_is_match(char *name, char *buf) -{ - int len = strlen(name); - - return !strncmp(name, buf, len) ? buf + len : NULL; -} - -static ssize_t iwl_dbgfs_tof_enable_write(struct ieee80211_vif *vif, - char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u32 value; - int ret = -EINVAL; - char *data; - - mutex_lock(&mvm->mutex); - - data = iwl_dbgfs_is_match("tof_disabled=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.tof_cfg.tof_disabled = value; - goto out; - } - - data = iwl_dbgfs_is_match("one_sided_disabled=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.tof_cfg.one_sided_disabled = value; - goto out; - } - - data = iwl_dbgfs_is_match("is_debug_mode=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.tof_cfg.is_debug_mode = value; - goto out; - } - - data = iwl_dbgfs_is_match("is_buf=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.tof_cfg.is_buf_required = value; - goto out; - } - - data = iwl_dbgfs_is_match("send_tof_cfg=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) { - ret = iwl_mvm_tof_config_cmd(mvm); - goto out; - } - } - -out: - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tof_enable_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_tof_config_cmd *cmd; - - cmd = &mvm->tof_data.tof_cfg; - - mutex_lock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, "tof_disabled = %d\n", - cmd->tof_disabled); - pos += scnprintf(buf + pos, bufsz - pos, "one_sided_disabled = %d\n", - cmd->one_sided_disabled); - pos += scnprintf(buf + pos, bufsz - pos, "is_debug_mode = %d\n", - cmd->is_debug_mode); - pos += scnprintf(buf + pos, bufsz - pos, "is_buf_required = %d\n", - cmd->is_buf_required); - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_tof_responder_params_write(struct ieee80211_vif *vif, - char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u32 value; - int ret = 0; - char *data; - - mutex_lock(&mvm->mutex); - - data = iwl_dbgfs_is_match("burst_period=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (!ret) - mvm->tof_data.responder_cfg.burst_period = - cpu_to_le16(value); - goto out; - } - - data = iwl_dbgfs_is_match("min_delta_ftm=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.min_delta_ftm = value; - goto out; - } - - data = iwl_dbgfs_is_match("burst_duration=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.burst_duration = value; - goto out; - } - - data = iwl_dbgfs_is_match("num_of_burst_exp=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.num_of_burst_exp = value; - goto out; - } - - data = iwl_dbgfs_is_match("abort_responder=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.abort_responder = value; - goto out; - } - - data = iwl_dbgfs_is_match("get_ch_est=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.get_ch_est = value; - goto out; - } - - data = iwl_dbgfs_is_match("recv_sta_req_params=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.recv_sta_req_params = value; - goto out; - } - - data = iwl_dbgfs_is_match("channel_num=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.channel_num = value; - goto out; - } - - data = iwl_dbgfs_is_match("bandwidth=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.bandwidth = value; - goto out; - } - - data = iwl_dbgfs_is_match("rate=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.rate = value; - goto out; - } - - data = iwl_dbgfs_is_match("bssid=", buf); - if (data) { - u8 *mac = mvm->tof_data.responder_cfg.bssid; - - if (!mac_pton(data, mac)) { - ret = -EINVAL; - goto out; - } - } - - data = iwl_dbgfs_is_match("tsf_timer_offset_msecs=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.tsf_timer_offset_msecs = - cpu_to_le16(value); - goto out; - } - - data = iwl_dbgfs_is_match("toa_offset=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.toa_offset = - cpu_to_le16(value); - goto out; - } - - data = iwl_dbgfs_is_match("center_freq=", buf); - if (data) { - struct iwl_tof_responder_config_cmd *cmd = - &mvm->tof_data.responder_cfg; - - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) { - enum ieee80211_band band = (cmd->channel_num <= 14) ? - IEEE80211_BAND_2GHZ : - IEEE80211_BAND_5GHZ; - struct ieee80211_channel chn = { - .band = band, - .center_freq = ieee80211_channel_to_frequency( - cmd->channel_num, band), - }; - struct cfg80211_chan_def chandef = { - .chan = &chn, - .center_freq1 = - ieee80211_channel_to_frequency(value, - band), - }; - - cmd->ctrl_ch_position = iwl_mvm_get_ctrl_pos(&chandef); - } - goto out; - } - - data = iwl_dbgfs_is_match("ftm_per_burst=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.ftm_per_burst = value; - goto out; - } - - data = iwl_dbgfs_is_match("ftm_resp_ts_avail=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.ftm_resp_ts_avail = value; - goto out; - } - - data = iwl_dbgfs_is_match("asap_mode=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.responder_cfg.asap_mode = value; - goto out; - } - - data = iwl_dbgfs_is_match("send_responder_cfg=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) { - ret = iwl_mvm_tof_responder_cmd(mvm, vif); - goto out; - } - } - -out: - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tof_responder_params_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_tof_responder_config_cmd *cmd; - - cmd = &mvm->tof_data.responder_cfg; - - mutex_lock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, "burst_period = %d\n", - le16_to_cpu(cmd->burst_period)); - pos += scnprintf(buf + pos, bufsz - pos, "burst_duration = %d\n", - cmd->burst_duration); - pos += scnprintf(buf + pos, bufsz - pos, "bandwidth = %d\n", - cmd->bandwidth); - pos += scnprintf(buf + pos, bufsz - pos, "channel_num = %d\n", - cmd->channel_num); - pos += scnprintf(buf + pos, bufsz - pos, "ctrl_ch_position = 0x%x\n", - cmd->ctrl_ch_position); - pos += scnprintf(buf + pos, bufsz - pos, "bssid = %pM\n", - cmd->bssid); - pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %d\n", - cmd->min_delta_ftm); - pos += scnprintf(buf + pos, bufsz - pos, "num_of_burst_exp = %d\n", - cmd->num_of_burst_exp); - pos += scnprintf(buf + pos, bufsz - pos, "rate = %d\n", cmd->rate); - pos += scnprintf(buf + pos, bufsz - pos, "abort_responder = %d\n", - cmd->abort_responder); - pos += scnprintf(buf + pos, bufsz - pos, "get_ch_est = %d\n", - cmd->get_ch_est); - pos += scnprintf(buf + pos, bufsz - pos, "recv_sta_req_params = %d\n", - cmd->recv_sta_req_params); - pos += scnprintf(buf + pos, bufsz - pos, "ftm_per_burst = %d\n", - cmd->ftm_per_burst); - pos += scnprintf(buf + pos, bufsz - pos, "ftm_resp_ts_avail = %d\n", - cmd->ftm_resp_ts_avail); - pos += scnprintf(buf + pos, bufsz - pos, "asap_mode = %d\n", - cmd->asap_mode); - pos += scnprintf(buf + pos, bufsz - pos, - "tsf_timer_offset_msecs = %d\n", - le16_to_cpu(cmd->tsf_timer_offset_msecs)); - pos += scnprintf(buf + pos, bufsz - pos, "toa_offset = %d\n", - le16_to_cpu(cmd->toa_offset)); - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_tof_range_request_write(struct ieee80211_vif *vif, - char *buf, size_t count, - loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u32 value; - int ret = 0; - char *data; - - mutex_lock(&mvm->mutex); - - data = iwl_dbgfs_is_match("request_id=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.request_id = value; - goto out; - } - - data = iwl_dbgfs_is_match("initiator=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.initiator = value; - goto out; - } - - data = iwl_dbgfs_is_match("one_sided_los_disable=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.one_sided_los_disable = value; - goto out; - } - - data = iwl_dbgfs_is_match("req_timeout=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.req_timeout = value; - goto out; - } - - data = iwl_dbgfs_is_match("report_policy=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.report_policy = value; - goto out; - } - - data = iwl_dbgfs_is_match("macaddr_random=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.macaddr_random = value; - goto out; - } - - data = iwl_dbgfs_is_match("num_of_ap=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req.num_of_ap = value; - goto out; - } - - data = iwl_dbgfs_is_match("macaddr_template=", buf); - if (data) { - u8 mac[ETH_ALEN]; - - if (!mac_pton(data, mac)) { - ret = -EINVAL; - goto out; - } - memcpy(mvm->tof_data.range_req.macaddr_template, mac, ETH_ALEN); - goto out; - } - - data = iwl_dbgfs_is_match("macaddr_mask=", buf); - if (data) { - u8 mac[ETH_ALEN]; - - if (!mac_pton(data, mac)) { - ret = -EINVAL; - goto out; - } - memcpy(mvm->tof_data.range_req.macaddr_mask, mac, ETH_ALEN); - goto out; - } - - data = iwl_dbgfs_is_match("ap=", buf); - if (data) { - struct iwl_tof_range_req_ap_entry ap = {}; - int size = sizeof(struct iwl_tof_range_req_ap_entry); - u16 burst_period; - u8 *mac = ap.bssid; - unsigned int i; - - if (sscanf(data, "%u %hhd %hhd %hhd" - "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx" - "%hhd %hhd %hd" - "%hhd %hhd %d" - "%hhx %hhd %hhd %hhd", - &i, &ap.channel_num, &ap.bandwidth, - &ap.ctrl_ch_position, - mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5, - &ap.measure_type, &ap.num_of_bursts, - &burst_period, - &ap.samples_per_burst, &ap.retries_per_sample, - &ap.tsf_delta, &ap.location_req, &ap.asap_mode, - &ap.enable_dyn_ack, &ap.rssi) != 20) { - ret = -EINVAL; - goto out; - } - if (i >= IWL_MVM_TOF_MAX_APS) { - IWL_ERR(mvm, "Invalid AP index %d\n", i); - ret = -EINVAL; - goto out; - } - - ap.burst_period = cpu_to_le16(burst_period); - - memcpy(&mvm->tof_data.range_req.ap[i], &ap, size); - goto out; - } - - data = iwl_dbgfs_is_match("send_range_request=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) - ret = iwl_mvm_tof_range_request_cmd(mvm, vif); - goto out; - } - - ret = -EINVAL; -out: - mutex_unlock(&mvm->mutex); - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tof_range_request_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - char buf[512]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_tof_range_req_cmd *cmd; - int i; - - cmd = &mvm->tof_data.range_req; - - mutex_lock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, "request_id= %d\n", - cmd->request_id); - pos += scnprintf(buf + pos, bufsz - pos, "initiator= %d\n", - cmd->initiator); - pos += scnprintf(buf + pos, bufsz - pos, "one_sided_los_disable = %d\n", - cmd->one_sided_los_disable); - pos += scnprintf(buf + pos, bufsz - pos, "req_timeout= %d\n", - cmd->req_timeout); - pos += scnprintf(buf + pos, bufsz - pos, "report_policy= %d\n", - cmd->report_policy); - pos += scnprintf(buf + pos, bufsz - pos, "macaddr_random= %d\n", - cmd->macaddr_random); - pos += scnprintf(buf + pos, bufsz - pos, "macaddr_template= %pM\n", - cmd->macaddr_template); - pos += scnprintf(buf + pos, bufsz - pos, "macaddr_mask= %pM\n", - cmd->macaddr_mask); - pos += scnprintf(buf + pos, bufsz - pos, "num_of_ap= %d\n", - cmd->num_of_ap); - for (i = 0; i < cmd->num_of_ap; i++) { - struct iwl_tof_range_req_ap_entry *ap = &cmd->ap[i]; - - pos += scnprintf(buf + pos, bufsz - pos, - "ap %.2d: channel_num=%hhd bw=%hhd" - " control=%hhd bssid=%pM type=%hhd" - " num_of_bursts=%hhd burst_period=%hd ftm=%hhd" - " retries=%hhd tsf_delta=%d" - " tsf_delta_direction=%hhd location_req=0x%hhx " - " asap=%hhd enable=%hhd rssi=%hhd\n", - i, ap->channel_num, ap->bandwidth, - ap->ctrl_ch_position, ap->bssid, - ap->measure_type, ap->num_of_bursts, - ap->burst_period, ap->samples_per_burst, - ap->retries_per_sample, ap->tsf_delta, - ap->tsf_delta_direction, - ap->location_req, ap->asap_mode, - ap->enable_dyn_ack, ap->rssi); - } - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_tof_range_req_ext_write(struct ieee80211_vif *vif, - char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u32 value; - int ret = 0; - char *data; - - mutex_lock(&mvm->mutex); - - data = iwl_dbgfs_is_match("tsf_timer_offset_msec=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req_ext.tsf_timer_offset_msec = - cpu_to_le16(value); - goto out; - } - - data = iwl_dbgfs_is_match("min_delta_ftm=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req_ext.min_delta_ftm = value; - goto out; - } - - data = iwl_dbgfs_is_match("ftm_format_and_bw20M=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req_ext.ftm_format_and_bw20M = - value; - goto out; - } - - data = iwl_dbgfs_is_match("ftm_format_and_bw40M=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req_ext.ftm_format_and_bw40M = - value; - goto out; - } - - data = iwl_dbgfs_is_match("ftm_format_and_bw80M=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.range_req_ext.ftm_format_and_bw80M = - value; - goto out; - } - - data = iwl_dbgfs_is_match("send_range_req_ext=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) - ret = iwl_mvm_tof_range_request_ext_cmd(mvm, vif); - goto out; - } - - ret = -EINVAL; -out: - mutex_unlock(&mvm->mutex); - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tof_range_req_ext_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_tof_range_req_ext_cmd *cmd; - - cmd = &mvm->tof_data.range_req_ext; - - mutex_lock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, - "tsf_timer_offset_msec = %hd\n", - cmd->tsf_timer_offset_msec); - pos += scnprintf(buf + pos, bufsz - pos, "min_delta_ftm = %hhd\n", - cmd->min_delta_ftm); - pos += scnprintf(buf + pos, bufsz - pos, - "ftm_format_and_bw20M = %hhd\n", - cmd->ftm_format_and_bw20M); - pos += scnprintf(buf + pos, bufsz - pos, - "ftm_format_and_bw40M = %hhd\n", - cmd->ftm_format_and_bw40M); - pos += scnprintf(buf + pos, bufsz - pos, - "ftm_format_and_bw80M = %hhd\n", - cmd->ftm_format_and_bw80M); - - mutex_unlock(&mvm->mutex); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_tof_range_abort_write(struct ieee80211_vif *vif, - char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u32 value; - int abort_id, ret = 0; - char *data; - - mutex_lock(&mvm->mutex); - - data = iwl_dbgfs_is_match("abort_id=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0) - mvm->tof_data.last_abort_id = value; - goto out; - } - - data = iwl_dbgfs_is_match("send_range_abort=", buf); - if (data) { - ret = kstrtou32(data, 10, &value); - if (ret == 0 && value) { - abort_id = mvm->tof_data.last_abort_id; - ret = iwl_mvm_tof_range_abort_cmd(mvm, abort_id); - goto out; - } - } - -out: - mutex_unlock(&mvm->mutex); - return ret ?: count; -} - -static ssize_t iwl_dbgfs_tof_range_abort_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - char buf[32]; - int pos = 0; - const size_t bufsz = sizeof(buf); - int last_abort_id; - - mutex_lock(&mvm->mutex); - last_abort_id = mvm->tof_data.last_abort_id; - mutex_unlock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, "last_abort_id = %d\n", - last_abort_id); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_tof_range_response_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - char *buf; - int pos = 0; - const size_t bufsz = sizeof(struct iwl_tof_range_rsp_ntfy) + 256; - struct iwl_tof_range_rsp_ntfy *cmd; - int i, ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&mvm->mutex); - cmd = &mvm->tof_data.range_resp; - - pos += scnprintf(buf + pos, bufsz - pos, "request_id = %d\n", - cmd->request_id); - pos += scnprintf(buf + pos, bufsz - pos, "status = %d\n", - cmd->request_status); - pos += scnprintf(buf + pos, bufsz - pos, "last_in_batch = %d\n", - cmd->last_in_batch); - pos += scnprintf(buf + pos, bufsz - pos, "num_of_aps = %d\n", - cmd->num_of_aps); - for (i = 0; i < cmd->num_of_aps; i++) { - struct iwl_tof_range_rsp_ap_entry_ntfy *ap = &cmd->ap[i]; - - pos += scnprintf(buf + pos, bufsz - pos, - "ap %.2d: bssid=%pM status=%hhd bw=%hhd" - " rtt=%d rtt_var=%d rtt_spread=%d" - " rssi=%hhd rssi_spread=%hhd" - " range=%d range_var=%d" - " time_stamp=%d\n", - i, ap->bssid, ap->measure_status, - ap->measure_bw, - ap->rtt, ap->rtt_variance, ap->rtt_spread, - ap->rssi, ap->rssi_spread, ap->range, - ap->range_variance, ap->timestamp); - } - mutex_unlock(&mvm->mutex); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - u8 value; - int ret; - - ret = kstrtou8(buf, 0, &value); - if (ret) - return ret; - if (value > 1) - return -EINVAL; - - mutex_lock(&mvm->mutex); - iwl_mvm_update_low_latency(mvm, vif, value); - mutex_unlock(&mvm->mutex); - - return count; -} - -static ssize_t iwl_dbgfs_low_latency_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[2]; - - buf[0] = mvmvif->low_latency ? '1' : '0'; - buf[1] = '\n'; - return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); -} - -static ssize_t iwl_dbgfs_uapsd_misbehaving_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[20]; - int len; - - len = sprintf(buf, "%pM\n", mvmvif->uapsd_misbehaving_bssid); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t iwl_dbgfs_uapsd_misbehaving_write(struct ieee80211_vif *vif, - char *buf, size_t count, - loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - bool ret; - - mutex_lock(&mvm->mutex); - ret = mac_pton(buf, mvmvif->uapsd_misbehaving_bssid); - mutex_unlock(&mvm->mutex); - - return ret ? count : -EINVAL; -} - -static ssize_t iwl_dbgfs_rx_phyinfo_write(struct ieee80211_vif *vif, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - struct ieee80211_chanctx_conf *chanctx_conf; - struct iwl_mvm_phy_ctxt *phy_ctxt; - u16 value; - int ret; - - ret = kstrtou16(buf, 0, &value); - if (ret) - return ret; - - mutex_lock(&mvm->mutex); - rcu_read_lock(); - - chanctx_conf = rcu_dereference(vif->chanctx_conf); - /* make sure the channel context is assigned */ - if (!chanctx_conf) { - rcu_read_unlock(); - mutex_unlock(&mvm->mutex); - return -EINVAL; - } - - phy_ctxt = &mvm->phy_ctxts[*(u16 *)chanctx_conf->drv_priv]; - rcu_read_unlock(); - - mvm->dbgfs_rx_phyinfo = value; - - ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chanctx_conf->min_def, - chanctx_conf->rx_chains_static, - chanctx_conf->rx_chains_dynamic); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_rx_phyinfo_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[8]; - - snprintf(buf, sizeof(buf), "0x%04x\n", mvmvif->mvm->dbgfs_rx_phyinfo); - - return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); -} - -#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ - _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) -#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ - _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) -#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, vif, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ - } while (0) - -MVM_DEBUGFS_READ_FILE_OPS(mac_params); -MVM_DEBUGFS_READ_FILE_OPS(tx_pwr_lmt); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_enable, 32); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_request, 512); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_req_ext, 32); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_abort, 32); -MVM_DEBUGFS_READ_FILE_OPS(tof_range_response); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_responder_params, 32); - -void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct dentry *dbgfs_dir = vif->debugfs_dir; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[100]; - - /* - * Check if debugfs directory already exist before creating it. - * This may happen when, for example, resetting hw or suspend-resume - */ - if (!dbgfs_dir || mvmvif->dbgfs_dir) - return; - - mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir); - - if (!mvmvif->dbgfs_dir) { - IWL_ERR(mvm, "Failed to create debugfs directory under %s\n", - dbgfs_dir->d_name.name); - return; - } - - if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && - ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) || - (vif->type == NL80211_IFTYPE_STATION && vif->p2p && - mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM))) - MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR | - S_IRUSR); - - MVM_DEBUGFS_ADD_FILE_VIF(tx_pwr_lmt, mvmvif->dbgfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - - if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && - mvmvif == mvm->bf_allowed_vif) - MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT) && - !vif->p2p && (vif->type != NL80211_IFTYPE_P2P_DEVICE)) { - if (IWL_MVM_TOF_IS_RESPONDER && vif->type == NL80211_IFTYPE_AP) - MVM_DEBUGFS_ADD_FILE_VIF(tof_responder_params, - mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - - MVM_DEBUGFS_ADD_FILE_VIF(tof_range_request, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(tof_range_req_ext, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(tof_enable, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(tof_range_abort, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE_VIF(tof_range_response, mvmvif->dbgfs_dir, - S_IRUSR); - } - - /* - * Create symlink for convenience pointing to interface specific - * debugfs entries for the driver. For example, under - * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/ - * find - * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/ - */ - snprintf(buf, 100, "../../../%s/%s/%s/%s", - dbgfs_dir->d_parent->d_parent->d_name.name, - dbgfs_dir->d_parent->d_name.name, - dbgfs_dir->d_name.name, - mvmvif->dbgfs_dir->d_name.name); - - mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name, - mvm->debugfs_dir, buf); - if (!mvmvif->dbgfs_slink) - IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n", - dbgfs_dir->d_name.name); - return; -err: - IWL_ERR(mvm, "Can't create debugfs entity\n"); -} - -void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - debugfs_remove(mvmvif->dbgfs_slink); - mvmvif->dbgfs_slink = NULL; - - debugfs_remove_recursive(mvmvif->dbgfs_dir); - mvmvif->dbgfs_dir = NULL; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c deleted file mode 100644 index 05928fb4021d..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ /dev/null @@ -1,1516 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include - -#include "mvm.h" -#include "sta.h" -#include "iwl-io.h" -#include "debugfs.h" -#include "iwl-fw-error-dump.h" - -static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int ret; - u32 scd_q_msk; - - if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) - return -EIO; - - if (sscanf(buf, "%x", &scd_q_msk) != 1) - return -EINVAL; - - IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk); - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_flush_tx_path(mvm, scd_q_msk, 0) ? : count; - mutex_unlock(&mvm->mutex); - - return ret; -} - -static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm_sta *mvmsta; - int sta_id, drain, ret; - - if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) - return -EIO; - - if (sscanf(buf, "%d %d", &sta_id, &drain) != 2) - return -EINVAL; - if (sta_id < 0 || sta_id >= IWL_MVM_STATION_COUNT) - return -EINVAL; - if (drain < 0 || drain > 1) - return -EINVAL; - - mutex_lock(&mvm->mutex); - - mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); - - if (!mvmsta) - ret = -ENOENT; - else - ret = iwl_mvm_drain_sta(mvm, mvmsta, drain) ? : count; - - mutex_unlock(&mvm->mutex); - - return ret; -} - -static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - const struct fw_img *img; - unsigned int ofs, len; - size_t ret; - u8 *ptr; - - if (!mvm->ucode_loaded) - return -EINVAL; - - /* default is to dump the entire data segment */ - img = &mvm->fw->img[mvm->cur_ucode]; - ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - len = img->sec[IWL_UCODE_SECTION_DATA].len; - - if (mvm->dbgfs_sram_len) { - ofs = mvm->dbgfs_sram_offset; - len = mvm->dbgfs_sram_len; - } - - ptr = kzalloc(len, GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len); - - ret = simple_read_from_buffer(user_buf, count, ppos, ptr, len); - - kfree(ptr); - - return ret; -} - -static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - const struct fw_img *img; - u32 offset, len; - u32 img_offset, img_len; - - if (!mvm->ucode_loaded) - return -EINVAL; - - img = &mvm->fw->img[mvm->cur_ucode]; - img_offset = img->sec[IWL_UCODE_SECTION_DATA].offset; - img_len = img->sec[IWL_UCODE_SECTION_DATA].len; - - if (sscanf(buf, "%x,%x", &offset, &len) == 2) { - if ((offset & 0x3) || (len & 0x3)) - return -EINVAL; - - if (offset + len > img_offset + img_len) - return -EINVAL; - - mvm->dbgfs_sram_offset = offset; - mvm->dbgfs_sram_len = len; - } else { - mvm->dbgfs_sram_offset = 0; - mvm->dbgfs_sram_len = 0; - } - - return count; -} - -static ssize_t iwl_dbgfs_set_nic_temperature_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - char buf[16]; - int pos; - - if (!mvm->temperature_test) - pos = scnprintf(buf , sizeof(buf), "disabled\n"); - else - pos = scnprintf(buf , sizeof(buf), "%d\n", mvm->temperature); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -/* - * Set NIC Temperature - * Cause the driver to ignore the actual NIC temperature reported by the FW - * Enable: any value between IWL_MVM_DEBUG_SET_TEMPERATURE_MIN - - * IWL_MVM_DEBUG_SET_TEMPERATURE_MAX - * Disable: IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE - */ -static ssize_t iwl_dbgfs_set_nic_temperature_write(struct iwl_mvm *mvm, - char *buf, size_t count, - loff_t *ppos) -{ - int temperature; - - if (!mvm->ucode_loaded && !mvm->temperature_test) - return -EIO; - - if (kstrtoint(buf, 10, &temperature)) - return -EINVAL; - /* not a legal temperature */ - if ((temperature > IWL_MVM_DEBUG_SET_TEMPERATURE_MAX && - temperature != IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) || - temperature < IWL_MVM_DEBUG_SET_TEMPERATURE_MIN) - return -EINVAL; - - mutex_lock(&mvm->mutex); - if (temperature == IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) { - if (!mvm->temperature_test) - goto out; - - mvm->temperature_test = false; - /* Since we can't read the temp while awake, just set - * it to zero until we get the next RX stats from the - * firmware. - */ - mvm->temperature = 0; - } else { - mvm->temperature_test = true; - mvm->temperature = temperature; - } - IWL_DEBUG_TEMP(mvm, "%sabling debug set temperature (temp = %d)\n", - mvm->temperature_test ? "En" : "Dis" , - mvm->temperature); - /* handle the temperature change */ - iwl_mvm_tt_handler(mvm); - -out: - mutex_unlock(&mvm->mutex); - - return count; -} - -static ssize_t iwl_dbgfs_nic_temp_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - char buf[16]; - int pos, temp; - - if (!mvm->ucode_loaded) - return -EIO; - - mutex_lock(&mvm->mutex); - temp = iwl_mvm_get_temp(mvm); - mutex_unlock(&mvm->mutex); - - if (temp < 0) - return temp; - - pos = scnprintf(buf , sizeof(buf), "%d\n", temp); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - struct ieee80211_sta *sta; - char buf[400]; - int i, pos = 0, bufsz = sizeof(buf); - - mutex_lock(&mvm->mutex); - - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i); - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (!sta) - pos += scnprintf(buf + pos, bufsz - pos, "N/A\n"); - else if (IS_ERR(sta)) - pos += scnprintf(buf + pos, bufsz - pos, "%ld\n", - PTR_ERR(sta)); - else - pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", - sta->addr); - } - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - char buf[64]; - int bufsz = sizeof(buf); - int pos = 0; - - pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d0=%d\n", - mvm->disable_power_off); - pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d3=%d\n", - mvm->disable_power_off_d3); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int ret, val; - - if (!mvm->ucode_loaded) - return -EIO; - - if (!strncmp("disable_power_off_d0=", buf, 21)) { - if (sscanf(buf + 21, "%d", &val) != 1) - return -EINVAL; - mvm->disable_power_off = val; - } else if (!strncmp("disable_power_off_d3=", buf, 21)) { - if (sscanf(buf + 21, "%d", &val) != 1) - return -EINVAL; - mvm->disable_power_off_d3 = val; - } else { - return -EINVAL; - } - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_power_update_device(mvm); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -#define BT_MBOX_MSG(_notif, _num, _field) \ - ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ - >> BT_MBOX##_num##_##_field##_POS) - - -#define BT_MBOX_PRINT(_num, _field, _end) \ - pos += scnprintf(buf + pos, bufsz - pos, \ - "\t%s: %d%s", \ - #_field, \ - BT_MBOX_MSG(notif, _num, _field), \ - true ? "\n" : ", "); - -static -int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf, - int pos, int bufsz) -{ - pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n"); - - BT_MBOX_PRINT(0, LE_SLAVE_LAT, false); - BT_MBOX_PRINT(0, LE_PROF1, false); - BT_MBOX_PRINT(0, LE_PROF2, false); - BT_MBOX_PRINT(0, LE_PROF_OTHER, false); - BT_MBOX_PRINT(0, CHL_SEQ_N, false); - BT_MBOX_PRINT(0, INBAND_S, false); - BT_MBOX_PRINT(0, LE_MIN_RSSI, false); - BT_MBOX_PRINT(0, LE_SCAN, false); - BT_MBOX_PRINT(0, LE_ADV, false); - BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false); - BT_MBOX_PRINT(0, OPEN_CON_1, true); - - pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n"); - - BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false); - BT_MBOX_PRINT(1, IP_SR, false); - BT_MBOX_PRINT(1, LE_MSTR, false); - BT_MBOX_PRINT(1, AGGR_TRFC_LD, false); - BT_MBOX_PRINT(1, MSG_TYPE, false); - BT_MBOX_PRINT(1, SSN, true); - - pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n"); - - BT_MBOX_PRINT(2, SNIFF_ACT, false); - BT_MBOX_PRINT(2, PAG, false); - BT_MBOX_PRINT(2, INQUIRY, false); - BT_MBOX_PRINT(2, CONN, false); - BT_MBOX_PRINT(2, SNIFF_INTERVAL, false); - BT_MBOX_PRINT(2, DISC, false); - BT_MBOX_PRINT(2, SCO_TX_ACT, false); - BT_MBOX_PRINT(2, SCO_RX_ACT, false); - BT_MBOX_PRINT(2, ESCO_RE_TX, false); - BT_MBOX_PRINT(2, SCO_DURATION, true); - - pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n"); - - BT_MBOX_PRINT(3, SCO_STATE, false); - BT_MBOX_PRINT(3, SNIFF_STATE, false); - BT_MBOX_PRINT(3, A2DP_STATE, false); - BT_MBOX_PRINT(3, ACL_STATE, false); - BT_MBOX_PRINT(3, MSTR_STATE, false); - BT_MBOX_PRINT(3, OBX_STATE, false); - BT_MBOX_PRINT(3, OPEN_CON_2, false); - BT_MBOX_PRINT(3, TRAFFIC_LOAD, false); - BT_MBOX_PRINT(3, CHL_SEQN_LSB, false); - BT_MBOX_PRINT(3, INBAND_P, false); - BT_MBOX_PRINT(3, MSG_TYPE_2, false); - BT_MBOX_PRINT(3, SSN_2, false); - BT_MBOX_PRINT(3, UPDATE_REQUEST, true); - - return pos; -} - -static -int iwl_mvm_coex_dump_mbox_old(struct iwl_bt_coex_profile_notif_old *notif, - char *buf, int pos, int bufsz) -{ - pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n"); - - BT_MBOX_PRINT(0, LE_SLAVE_LAT, false); - BT_MBOX_PRINT(0, LE_PROF1, false); - BT_MBOX_PRINT(0, LE_PROF2, false); - BT_MBOX_PRINT(0, LE_PROF_OTHER, false); - BT_MBOX_PRINT(0, CHL_SEQ_N, false); - BT_MBOX_PRINT(0, INBAND_S, false); - BT_MBOX_PRINT(0, LE_MIN_RSSI, false); - BT_MBOX_PRINT(0, LE_SCAN, false); - BT_MBOX_PRINT(0, LE_ADV, false); - BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false); - BT_MBOX_PRINT(0, OPEN_CON_1, true); - - pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n"); - - BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false); - BT_MBOX_PRINT(1, IP_SR, false); - BT_MBOX_PRINT(1, LE_MSTR, false); - BT_MBOX_PRINT(1, AGGR_TRFC_LD, false); - BT_MBOX_PRINT(1, MSG_TYPE, false); - BT_MBOX_PRINT(1, SSN, true); - - pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n"); - - BT_MBOX_PRINT(2, SNIFF_ACT, false); - BT_MBOX_PRINT(2, PAG, false); - BT_MBOX_PRINT(2, INQUIRY, false); - BT_MBOX_PRINT(2, CONN, false); - BT_MBOX_PRINT(2, SNIFF_INTERVAL, false); - BT_MBOX_PRINT(2, DISC, false); - BT_MBOX_PRINT(2, SCO_TX_ACT, false); - BT_MBOX_PRINT(2, SCO_RX_ACT, false); - BT_MBOX_PRINT(2, ESCO_RE_TX, false); - BT_MBOX_PRINT(2, SCO_DURATION, true); - - pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n"); - - BT_MBOX_PRINT(3, SCO_STATE, false); - BT_MBOX_PRINT(3, SNIFF_STATE, false); - BT_MBOX_PRINT(3, A2DP_STATE, false); - BT_MBOX_PRINT(3, ACL_STATE, false); - BT_MBOX_PRINT(3, MSTR_STATE, false); - BT_MBOX_PRINT(3, OBX_STATE, false); - BT_MBOX_PRINT(3, OPEN_CON_2, false); - BT_MBOX_PRINT(3, TRAFFIC_LOAD, false); - BT_MBOX_PRINT(3, CHL_SEQN_LSB, false); - BT_MBOX_PRINT(3, INBAND_P, false); - BT_MBOX_PRINT(3, MSG_TYPE_2, false); - BT_MBOX_PRINT(3, SSN_2, false); - BT_MBOX_PRINT(3, UPDATE_REQUEST, true); - - return pos; -} - -static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - char *buf; - int ret, pos = 0, bufsz = sizeof(char) * 1024; - - buf = kmalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&mvm->mutex); - - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - struct iwl_bt_coex_profile_notif_old *notif = - &mvm->last_bt_notif_old; - - pos += iwl_mvm_coex_dump_mbox_old(notif, buf, pos, bufsz); - - pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", - notif->bt_ci_compliance); - pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", - le32_to_cpu(notif->primary_ch_lut)); - pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n", - le32_to_cpu(notif->secondary_ch_lut)); - pos += scnprintf(buf+pos, - bufsz-pos, "bt_activity_grading = %d\n", - le32_to_cpu(notif->bt_activity_grading)); - pos += scnprintf(buf+pos, bufsz-pos, - "antenna isolation = %d CORUN LUT index = %d\n", - mvm->last_ant_isol, mvm->last_corun_lut); - } else { - struct iwl_bt_coex_profile_notif *notif = - &mvm->last_bt_notif; - - pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz); - - pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", - notif->bt_ci_compliance); - pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", - le32_to_cpu(notif->primary_ch_lut)); - pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n", - le32_to_cpu(notif->secondary_ch_lut)); - pos += scnprintf(buf+pos, - bufsz-pos, "bt_activity_grading = %d\n", - le32_to_cpu(notif->bt_activity_grading)); - pos += scnprintf(buf+pos, bufsz-pos, - "antenna isolation = %d CORUN LUT index = %d\n", - mvm->last_ant_isol, mvm->last_corun_lut); - } - - mutex_unlock(&mvm->mutex); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - - return ret; -} -#undef BT_MBOX_PRINT - -static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - char buf[256]; - int bufsz = sizeof(buf); - int pos = 0; - - mutex_lock(&mvm->mutex); - - if (!fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_BT_COEX_SPLIT)) { - struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd_old; - - pos += scnprintf(buf+pos, bufsz-pos, - "Channel inhibition CMD\n"); - pos += scnprintf(buf+pos, bufsz-pos, - "\tPrimary Channel Bitmap 0x%016llx\n", - le64_to_cpu(cmd->bt_primary_ci)); - pos += scnprintf(buf+pos, bufsz-pos, - "\tSecondary Channel Bitmap 0x%016llx\n", - le64_to_cpu(cmd->bt_secondary_ci)); - - pos += scnprintf(buf+pos, bufsz-pos, - "BT Configuration CMD - 0=default, 1=never, 2=always\n"); - pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill msk idx %d\n", - mvm->bt_ack_kill_msk[0]); - pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill msk idx %d\n", - mvm->bt_cts_kill_msk[0]); - - } else { - struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd; - - pos += scnprintf(buf+pos, bufsz-pos, - "Channel inhibition CMD\n"); - pos += scnprintf(buf+pos, bufsz-pos, - "\tPrimary Channel Bitmap 0x%016llx\n", - le64_to_cpu(cmd->bt_primary_ci)); - pos += scnprintf(buf+pos, bufsz-pos, - "\tSecondary Channel Bitmap 0x%016llx\n", - le64_to_cpu(cmd->bt_secondary_ci)); - } - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - u32 bt_tx_prio; - - if (sscanf(buf, "%u", &bt_tx_prio) != 1) - return -EINVAL; - if (bt_tx_prio > 4) - return -EINVAL; - - mvm->bt_tx_prio = bt_tx_prio; - - return count; -} - -static ssize_t -iwl_dbgfs_bt_force_ant_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - static const char * const modes_str[BT_FORCE_ANT_MAX] = { - [BT_FORCE_ANT_DIS] = "dis", - [BT_FORCE_ANT_AUTO] = "auto", - [BT_FORCE_ANT_BT] = "bt", - [BT_FORCE_ANT_WIFI] = "wifi", - }; - int ret, bt_force_ant_mode; - - for (bt_force_ant_mode = 0; - bt_force_ant_mode < ARRAY_SIZE(modes_str); - bt_force_ant_mode++) { - if (!strcmp(buf, modes_str[bt_force_ant_mode])) - break; - } - - if (bt_force_ant_mode >= ARRAY_SIZE(modes_str)) - return -EINVAL; - - ret = 0; - mutex_lock(&mvm->mutex); - if (mvm->bt_force_ant_mode == bt_force_ant_mode) - goto out; - - mvm->bt_force_ant_mode = bt_force_ant_mode; - IWL_DEBUG_COEX(mvm, "Force mode: %s\n", - modes_str[mvm->bt_force_ant_mode]); - ret = iwl_send_bt_init_conf(mvm); - -out: - mutex_unlock(&mvm->mutex); - return ret ?: count; -} - -#define PRINT_STATS_LE32(_struct, _memb) \ - pos += scnprintf(buf + pos, bufsz - pos, \ - fmt_table, #_memb, \ - le32_to_cpu(_struct->_memb)) - -static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, - char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - static const char *fmt_table = "\t%-30s %10u\n"; - static const char *fmt_header = "%-32s\n"; - int pos = 0; - char *buf; - int ret; - /* 43 is the size of each data line, 33 is the size of each header */ - size_t bufsz = - ((sizeof(struct mvm_statistics_rx) / sizeof(__le32)) * 43) + - (4 * 33) + 1; - - struct mvm_statistics_rx_phy *ofdm; - struct mvm_statistics_rx_phy *cck; - struct mvm_statistics_rx_non_phy *general; - struct mvm_statistics_rx_ht_phy *ht; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&mvm->mutex); - - ofdm = &mvm->rx_stats.ofdm; - cck = &mvm->rx_stats.cck; - general = &mvm->rx_stats.general; - ht = &mvm->rx_stats.ofdm_ht; - - pos += scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - OFDM"); - PRINT_STATS_LE32(ofdm, ina_cnt); - PRINT_STATS_LE32(ofdm, fina_cnt); - PRINT_STATS_LE32(ofdm, plcp_err); - PRINT_STATS_LE32(ofdm, crc32_err); - PRINT_STATS_LE32(ofdm, overrun_err); - PRINT_STATS_LE32(ofdm, early_overrun_err); - PRINT_STATS_LE32(ofdm, crc32_good); - PRINT_STATS_LE32(ofdm, false_alarm_cnt); - PRINT_STATS_LE32(ofdm, fina_sync_err_cnt); - PRINT_STATS_LE32(ofdm, sfd_timeout); - PRINT_STATS_LE32(ofdm, fina_timeout); - PRINT_STATS_LE32(ofdm, unresponded_rts); - PRINT_STATS_LE32(ofdm, rxe_frame_lmt_overrun); - PRINT_STATS_LE32(ofdm, sent_ack_cnt); - PRINT_STATS_LE32(ofdm, sent_cts_cnt); - PRINT_STATS_LE32(ofdm, sent_ba_rsp_cnt); - PRINT_STATS_LE32(ofdm, dsp_self_kill); - PRINT_STATS_LE32(ofdm, mh_format_err); - PRINT_STATS_LE32(ofdm, re_acq_main_rssi_sum); - PRINT_STATS_LE32(ofdm, reserved); - - pos += scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - CCK"); - PRINT_STATS_LE32(cck, ina_cnt); - PRINT_STATS_LE32(cck, fina_cnt); - PRINT_STATS_LE32(cck, plcp_err); - PRINT_STATS_LE32(cck, crc32_err); - PRINT_STATS_LE32(cck, overrun_err); - PRINT_STATS_LE32(cck, early_overrun_err); - PRINT_STATS_LE32(cck, crc32_good); - PRINT_STATS_LE32(cck, false_alarm_cnt); - PRINT_STATS_LE32(cck, fina_sync_err_cnt); - PRINT_STATS_LE32(cck, sfd_timeout); - PRINT_STATS_LE32(cck, fina_timeout); - PRINT_STATS_LE32(cck, unresponded_rts); - PRINT_STATS_LE32(cck, rxe_frame_lmt_overrun); - PRINT_STATS_LE32(cck, sent_ack_cnt); - PRINT_STATS_LE32(cck, sent_cts_cnt); - PRINT_STATS_LE32(cck, sent_ba_rsp_cnt); - PRINT_STATS_LE32(cck, dsp_self_kill); - PRINT_STATS_LE32(cck, mh_format_err); - PRINT_STATS_LE32(cck, re_acq_main_rssi_sum); - PRINT_STATS_LE32(cck, reserved); - - pos += scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - GENERAL"); - PRINT_STATS_LE32(general, bogus_cts); - PRINT_STATS_LE32(general, bogus_ack); - PRINT_STATS_LE32(general, non_bssid_frames); - PRINT_STATS_LE32(general, filtered_frames); - PRINT_STATS_LE32(general, non_channel_beacons); - PRINT_STATS_LE32(general, channel_beacons); - PRINT_STATS_LE32(general, num_missed_bcon); - PRINT_STATS_LE32(general, adc_rx_saturation_time); - PRINT_STATS_LE32(general, ina_detection_search_time); - PRINT_STATS_LE32(general, beacon_silence_rssi_a); - PRINT_STATS_LE32(general, beacon_silence_rssi_b); - PRINT_STATS_LE32(general, beacon_silence_rssi_c); - PRINT_STATS_LE32(general, interference_data_flag); - PRINT_STATS_LE32(general, channel_load); - PRINT_STATS_LE32(general, dsp_false_alarms); - PRINT_STATS_LE32(general, beacon_rssi_a); - PRINT_STATS_LE32(general, beacon_rssi_b); - PRINT_STATS_LE32(general, beacon_rssi_c); - PRINT_STATS_LE32(general, beacon_energy_a); - PRINT_STATS_LE32(general, beacon_energy_b); - PRINT_STATS_LE32(general, beacon_energy_c); - PRINT_STATS_LE32(general, num_bt_kills); - PRINT_STATS_LE32(general, mac_id); - PRINT_STATS_LE32(general, directed_data_mpdu); - - pos += scnprintf(buf + pos, bufsz - pos, fmt_header, - "Statistics_Rx - HT"); - PRINT_STATS_LE32(ht, plcp_err); - PRINT_STATS_LE32(ht, overrun_err); - PRINT_STATS_LE32(ht, early_overrun_err); - PRINT_STATS_LE32(ht, crc32_good); - PRINT_STATS_LE32(ht, crc32_err); - PRINT_STATS_LE32(ht, mh_format_err); - PRINT_STATS_LE32(ht, agg_crc32_good); - PRINT_STATS_LE32(ht, agg_mpdu_cnt); - PRINT_STATS_LE32(ht, agg_cnt); - PRINT_STATS_LE32(ht, unsupport_mcs); - - mutex_unlock(&mvm->mutex); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - - return ret; -} -#undef PRINT_STAT_LE32 - -static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm, - char __user *user_buf, size_t count, - loff_t *ppos, - struct iwl_mvm_frame_stats *stats) -{ - char *buff, *pos, *endpos; - int idx, i; - int ret; - static const size_t bufsz = 1024; - - buff = kmalloc(bufsz, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - spin_lock_bh(&mvm->drv_stats_lock); - - pos = buff; - endpos = pos + bufsz; - - pos += scnprintf(pos, endpos - pos, - "Legacy/HT/VHT\t:\t%d/%d/%d\n", - stats->legacy_frames, - stats->ht_frames, - stats->vht_frames); - pos += scnprintf(pos, endpos - pos, "20/40/80\t:\t%d/%d/%d\n", - stats->bw_20_frames, - stats->bw_40_frames, - stats->bw_80_frames); - pos += scnprintf(pos, endpos - pos, "NGI/SGI\t\t:\t%d/%d\n", - stats->ngi_frames, - stats->sgi_frames); - pos += scnprintf(pos, endpos - pos, "SISO/MIMO2\t:\t%d/%d\n", - stats->siso_frames, - stats->mimo2_frames); - pos += scnprintf(pos, endpos - pos, "FAIL/SCSS\t:\t%d/%d\n", - stats->fail_frames, - stats->success_frames); - pos += scnprintf(pos, endpos - pos, "MPDUs agg\t:\t%d\n", - stats->agg_frames); - pos += scnprintf(pos, endpos - pos, "A-MPDUs\t\t:\t%d\n", - stats->ampdu_count); - pos += scnprintf(pos, endpos - pos, "Avg MPDUs/A-MPDU:\t%d\n", - stats->ampdu_count > 0 ? - (stats->agg_frames / stats->ampdu_count) : 0); - - pos += scnprintf(pos, endpos - pos, "Last Rates\n"); - - idx = stats->last_frame_idx - 1; - for (i = 0; i < ARRAY_SIZE(stats->last_rates); i++) { - idx = (idx + 1) % ARRAY_SIZE(stats->last_rates); - if (stats->last_rates[idx] == 0) - continue; - pos += scnprintf(pos, endpos - pos, "Rate[%d]: ", - (int)(ARRAY_SIZE(stats->last_rates) - i)); - pos += rs_pretty_print_rate(pos, stats->last_rates[idx]); - } - spin_unlock_bh(&mvm->drv_stats_lock); - - ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); - kfree(buff); - - return ret; -} - -static ssize_t iwl_dbgfs_drv_rx_stats_read(struct file *file, - char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - - return iwl_dbgfs_frame_stats_read(mvm, user_buf, count, ppos, - &mvm->drv_rx_stats); -} - -static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int ret; - - mutex_lock(&mvm->mutex); - - /* allow one more restart that we're provoking here */ - if (mvm->restart_fw >= 0) - mvm->restart_fw++; - - /* take the return value to make compiler happy - it will fail anyway */ - ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, 0, 0, NULL); - - mutex_unlock(&mvm->mutex); - - return count; -} - -static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_NMI); - if (ret) - return ret; - - iwl_force_nmi(mvm->trans); - - iwl_mvm_unref(mvm, IWL_MVM_REF_NMI); - - return count; -} - -static ssize_t -iwl_dbgfs_scan_ant_rxchain_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - int pos = 0; - char buf[32]; - const size_t bufsz = sizeof(buf); - - /* print which antennas were set for the scan command by the user */ - pos += scnprintf(buf + pos, bufsz - pos, "Antennas for scan: "); - if (mvm->scan_rx_ant & ANT_A) - pos += scnprintf(buf + pos, bufsz - pos, "A"); - if (mvm->scan_rx_ant & ANT_B) - pos += scnprintf(buf + pos, bufsz - pos, "B"); - if (mvm->scan_rx_ant & ANT_C) - pos += scnprintf(buf + pos, bufsz - pos, "C"); - pos += scnprintf(buf + pos, bufsz - pos, " (%hhx)\n", mvm->scan_rx_ant); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - u8 scan_rx_ant; - - if (sscanf(buf, "%hhx", &scan_rx_ant) != 1) - return -EINVAL; - if (scan_rx_ant > ANT_ABC) - return -EINVAL; - if (scan_rx_ant & ~(iwl_mvm_get_valid_rx_ant(mvm))) - return -EINVAL; - - if (mvm->scan_rx_ant != scan_rx_ant) { - mvm->scan_rx_ant = scan_rx_ant; - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_UMAC_SCAN)) - iwl_mvm_config_scan(mvm); - } - - return count; -} - -static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - int conf; - char buf[8]; - const size_t bufsz = sizeof(buf); - int pos = 0; - - mutex_lock(&mvm->mutex); - conf = mvm->fw_dbg_conf; - mutex_unlock(&mvm->mutex); - - pos += scnprintf(buf + pos, bufsz - pos, "%d\n", conf); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm, - char *buf, size_t count, - loff_t *ppos) -{ - unsigned int conf_id; - int ret; - - ret = kstrtouint(buf, 0, &conf_id); - if (ret) - return ret; - - if (WARN_ON(conf_id >= FW_DBG_CONF_MAX)) - return -EINVAL; - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_start_fw_dbg_conf(mvm, conf_id); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm, - char *buf, size_t count, - loff_t *ppos) -{ - int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE); - - if (ret) - return ret; - - iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, NULL, 0, NULL); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE); - - return count; -} - -#define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__) -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING -static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - struct iwl_bcast_filter_cmd cmd; - const struct iwl_fw_bcast_filter *filter; - char *buf; - int bufsz = 1024; - int i, j, pos = 0; - ssize_t ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&mvm->mutex); - if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) { - ADD_TEXT("None\n"); - mutex_unlock(&mvm->mutex); - goto out; - } - mutex_unlock(&mvm->mutex); - - for (i = 0; cmd.filters[i].attrs[0].mask; i++) { - filter = &cmd.filters[i]; - - ADD_TEXT("Filter [%d]:\n", i); - ADD_TEXT("\tDiscard=%d\n", filter->discard); - ADD_TEXT("\tFrame Type: %s\n", - filter->frame_type ? "IPv4" : "Generic"); - - for (j = 0; j < ARRAY_SIZE(filter->attrs); j++) { - const struct iwl_fw_bcast_filter_attr *attr; - - attr = &filter->attrs[j]; - if (!attr->mask) - break; - - ADD_TEXT("\tAttr [%d]: offset=%d (from %s), mask=0x%x, value=0x%x reserved=0x%x\n", - j, attr->offset, - attr->offset_type ? "IP End" : - "Payload Start", - be32_to_cpu(attr->mask), - be32_to_cpu(attr->val), - le16_to_cpu(attr->reserved1)); - } - } -out: - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int pos, next_pos; - struct iwl_fw_bcast_filter filter = {}; - struct iwl_bcast_filter_cmd cmd; - u32 filter_id, attr_id, mask, value; - int err = 0; - - if (sscanf(buf, "%d %hhi %hhi %n", &filter_id, &filter.discard, - &filter.frame_type, &pos) != 3) - return -EINVAL; - - if (filter_id >= ARRAY_SIZE(mvm->dbgfs_bcast_filtering.cmd.filters) || - filter.frame_type > BCAST_FILTER_FRAME_TYPE_IPV4) - return -EINVAL; - - for (attr_id = 0; attr_id < ARRAY_SIZE(filter.attrs); - attr_id++) { - struct iwl_fw_bcast_filter_attr *attr = - &filter.attrs[attr_id]; - - if (pos >= count) - break; - - if (sscanf(&buf[pos], "%hhi %hhi %i %i %n", - &attr->offset, &attr->offset_type, - &mask, &value, &next_pos) != 4) - return -EINVAL; - - attr->mask = cpu_to_be32(mask); - attr->val = cpu_to_be32(value); - if (mask) - filter.num_attrs++; - - pos += next_pos; - } - - mutex_lock(&mvm->mutex); - memcpy(&mvm->dbgfs_bcast_filtering.cmd.filters[filter_id], - &filter, sizeof(filter)); - - /* send updated bcast filtering configuration */ - if (mvm->dbgfs_bcast_filtering.override && - iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) - err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, - sizeof(cmd), &cmd); - mutex_unlock(&mvm->mutex); - - return err ?: count; -} - -static ssize_t iwl_dbgfs_bcast_filters_macs_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - struct iwl_bcast_filter_cmd cmd; - char *buf; - int bufsz = 1024; - int i, pos = 0; - ssize_t ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&mvm->mutex); - if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) { - ADD_TEXT("None\n"); - mutex_unlock(&mvm->mutex); - goto out; - } - mutex_unlock(&mvm->mutex); - - for (i = 0; i < ARRAY_SIZE(cmd.macs); i++) { - const struct iwl_fw_bcast_mac *mac = &cmd.macs[i]; - - ADD_TEXT("Mac [%d]: discard=%d attached_filters=0x%x\n", - i, mac->default_discard, mac->attached_filters); - } -out: - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm, - char *buf, size_t count, - loff_t *ppos) -{ - struct iwl_bcast_filter_cmd cmd; - struct iwl_fw_bcast_mac mac = {}; - u32 mac_id, attached_filters; - int err = 0; - - if (!mvm->bcast_filters) - return -ENOENT; - - if (sscanf(buf, "%d %hhi %i", &mac_id, &mac.default_discard, - &attached_filters) != 3) - return -EINVAL; - - if (mac_id >= ARRAY_SIZE(cmd.macs) || - mac.default_discard > 1 || - attached_filters >= BIT(ARRAY_SIZE(cmd.filters))) - return -EINVAL; - - mac.attached_filters = cpu_to_le16(attached_filters); - - mutex_lock(&mvm->mutex); - memcpy(&mvm->dbgfs_bcast_filtering.cmd.macs[mac_id], - &mac, sizeof(mac)); - - /* send updated bcast filtering configuration */ - if (mvm->dbgfs_bcast_filtering.override && - iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) - err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, - sizeof(cmd), &cmd); - mutex_unlock(&mvm->mutex); - - return err ?: count; -} -#endif - -#ifdef CONFIG_PM_SLEEP -static ssize_t iwl_dbgfs_d3_sram_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int store; - - if (sscanf(buf, "%d", &store) != 1) - return -EINVAL; - - mvm->store_d3_resume_sram = store; - - return count; -} - -static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - const struct fw_img *img; - int ofs, len, pos = 0; - size_t bufsz, ret; - char *buf; - u8 *ptr = mvm->d3_resume_sram; - - img = &mvm->fw->img[IWL_UCODE_WOWLAN]; - len = img->sec[IWL_UCODE_SECTION_DATA].len; - - bufsz = len * 4 + 256; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf, bufsz, "D3 SRAM capture: %sabled\n", - mvm->store_d3_resume_sram ? "en" : "dis"); - - if (ptr) { - for (ofs = 0; ofs < len; ofs += 16) { - pos += scnprintf(buf + pos, bufsz - pos, - "0x%.4x %16ph\n", ofs, ptr + ofs); - } - } else { - pos += scnprintf(buf + pos, bufsz - pos, - "(no data captured)\n"); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - - kfree(buf); - - return ret; -} -#endif - -#define PRINT_MVM_REF(ref) do { \ - if (mvm->refs[ref]) \ - pos += scnprintf(buf + pos, bufsz - pos, \ - "\t(0x%lx): %d %s\n", \ - BIT(ref), mvm->refs[ref], #ref); \ -} while (0) - -static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - int i, pos = 0; - char buf[256]; - const size_t bufsz = sizeof(buf); - u32 refs = 0; - - for (i = 0; i < IWL_MVM_REF_COUNT; i++) - if (mvm->refs[i]) - refs |= BIT(i); - - pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%x\n", - refs); - - PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN); - PRINT_MVM_REF(IWL_MVM_REF_SCAN); - PRINT_MVM_REF(IWL_MVM_REF_ROC); - PRINT_MVM_REF(IWL_MVM_REF_ROC_AUX); - PRINT_MVM_REF(IWL_MVM_REF_P2P_CLIENT); - PRINT_MVM_REF(IWL_MVM_REF_AP_IBSS); - PRINT_MVM_REF(IWL_MVM_REF_USER); - PRINT_MVM_REF(IWL_MVM_REF_TX); - PRINT_MVM_REF(IWL_MVM_REF_TX_AGG); - PRINT_MVM_REF(IWL_MVM_REF_ADD_IF); - PRINT_MVM_REF(IWL_MVM_REF_START_AP); - PRINT_MVM_REF(IWL_MVM_REF_BSS_CHANGED); - PRINT_MVM_REF(IWL_MVM_REF_PREPARE_TX); - PRINT_MVM_REF(IWL_MVM_REF_PROTECT_TDLS); - PRINT_MVM_REF(IWL_MVM_REF_CHECK_CTKILL); - PRINT_MVM_REF(IWL_MVM_REF_PRPH_READ); - PRINT_MVM_REF(IWL_MVM_REF_PRPH_WRITE); - PRINT_MVM_REF(IWL_MVM_REF_NMI); - PRINT_MVM_REF(IWL_MVM_REF_TM_CMD); - PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK); - PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA); - PRINT_MVM_REF(IWL_MVM_REF_FW_DBG_COLLECT); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - unsigned long value; - int ret; - bool taken; - - ret = kstrtoul(buf, 10, &value); - if (ret < 0) - return ret; - - mutex_lock(&mvm->mutex); - - taken = mvm->refs[IWL_MVM_REF_USER]; - if (value == 1 && !taken) - iwl_mvm_ref(mvm, IWL_MVM_REF_USER); - else if (value == 0 && taken) - iwl_mvm_unref(mvm, IWL_MVM_REF_USER); - else - ret = -EINVAL; - - mutex_unlock(&mvm->mutex); - - if (ret < 0) - return ret; - return count; -} - -#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ - _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm) -#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ - _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm) -#define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \ - if (!debugfs_create_file(alias, mode, parent, mvm, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ - } while (0) -#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \ - MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) - -static ssize_t -iwl_dbgfs_prph_reg_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - int pos = 0; - char buf[32]; - const size_t bufsz = sizeof(buf); - int ret; - - if (!mvm->dbgfs_prph_reg_addr) - return -EINVAL; - - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_READ); - if (ret) - return ret; - - pos += scnprintf(buf + pos, bufsz - pos, "Reg 0x%x: (0x%x)\n", - mvm->dbgfs_prph_reg_addr, - iwl_read_prph(mvm->trans, mvm->dbgfs_prph_reg_addr)); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_READ); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t -iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - u8 args; - u32 value; - int ret; - - args = sscanf(buf, "%i %i", &mvm->dbgfs_prph_reg_addr, &value); - /* if we only want to set the reg address - nothing more to do */ - if (args == 1) - goto out; - - /* otherwise, make sure we have both address and value */ - if (args != 2) - return -EINVAL; - - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE); - if (ret) - return ret; - - iwl_write_prph(mvm->trans, mvm->dbgfs_prph_reg_addr, value); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE); -out: - return count; -} - -static ssize_t -iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf, - size_t count, loff_t *ppos) -{ - int ret; - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_send_cmd_pdu(mvm, ECHO_CMD, 0, 0, NULL); - mutex_unlock(&mvm->mutex); - - return ret ?: count; -} - -MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); - -/* Device wide debugfs entries */ -MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16); -MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8); -MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(set_nic_temperature, 64); -MVM_DEBUGFS_READ_FILE_OPS(nic_temp); -MVM_DEBUGFS_READ_FILE_OPS(stations); -MVM_DEBUGFS_READ_FILE_OPS(bt_notif); -MVM_DEBUGFS_READ_FILE_OPS(bt_cmd); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64); -MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats); -MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats); -MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10); -MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10); -MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10); -MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8); -MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8); - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING -MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256); -#endif - -#ifdef CONFIG_PM_SLEEP -MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8); -#endif - -int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) -{ - struct dentry *bcast_dir __maybe_unused; - char buf[100]; - - spin_lock_init(&mvm->drv_stats_lock); - - mvm->debugfs_dir = dbgfs_dir; - - MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR); - MVM_DEBUGFS_ADD_FILE(set_nic_temperature, mvm->debugfs_dir, - S_IWUSR | S_IRUSR); - MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir, - S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, - S_IWUSR | S_IRUSR); - MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR); - MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR); - if (!debugfs_create_bool("enable_scan_iteration_notif", - S_IRUSR | S_IWUSR, - mvm->debugfs_dir, - &mvm->scan_iter_notif_enabled)) - goto err; - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) { - bcast_dir = debugfs_create_dir("bcast_filtering", - mvm->debugfs_dir); - if (!bcast_dir) - goto err; - - if (!debugfs_create_bool("override", S_IRUSR | S_IWUSR, - bcast_dir, - &mvm->dbgfs_bcast_filtering.override)) - goto err; - - MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters, - bcast_dir, S_IWUSR | S_IRUSR); - MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs, - bcast_dir, S_IWUSR | S_IRUSR); - } -#endif - -#ifdef CONFIG_PM_SLEEP - MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR); - MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR); - if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR, - mvm->debugfs_dir, &mvm->d3_wake_sysassert)) - goto err; - if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR, - mvm->debugfs_dir, &mvm->last_netdetect_scans)) - goto err; -#endif - - if (!debugfs_create_u8("low_latency_agg_frame_limit", S_IRUSR | S_IWUSR, - mvm->debugfs_dir, - &mvm->low_latency_agg_frame_limit)) - goto err; - if (!debugfs_create_u8("ps_disabled", S_IRUSR, - mvm->debugfs_dir, &mvm->ps_disabled)) - goto err; - if (!debugfs_create_blob("nvm_hw", S_IRUSR, - mvm->debugfs_dir, &mvm->nvm_hw_blob)) - goto err; - if (!debugfs_create_blob("nvm_sw", S_IRUSR, - mvm->debugfs_dir, &mvm->nvm_sw_blob)) - goto err; - if (!debugfs_create_blob("nvm_calib", S_IRUSR, - mvm->debugfs_dir, &mvm->nvm_calib_blob)) - goto err; - if (!debugfs_create_blob("nvm_prod", S_IRUSR, - mvm->debugfs_dir, &mvm->nvm_prod_blob)) - goto err; - if (!debugfs_create_blob("nvm_phy_sku", S_IRUSR, - mvm->debugfs_dir, &mvm->nvm_phy_sku_blob)) - goto err; - - /* - * Create a symlink with mac80211. It will be removed when mac80211 - * exists (before the opmode exists which removes the target.) - */ - snprintf(buf, 100, "../../%s/%s", - dbgfs_dir->d_parent->d_parent->d_name.name, - dbgfs_dir->d_parent->d_name.name); - if (!debugfs_create_symlink("iwlwifi", mvm->hw->wiphy->debugfsdir, buf)) - goto err; - - return 0; -err: - IWL_ERR(mvm, "Can't create the mvm debugfs directory\n"); - return -ENOMEM; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/iwlwifi/mvm/debugfs.h deleted file mode 100644 index 8c4190e7e027..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.h +++ /dev/null @@ -1,103 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#define MVM_DEBUGFS_READ_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -} - -#define MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ -static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \ - const char __user *user_buf, \ - size_t count, loff_t *ppos) \ -{ \ - argtype *arg = file->private_data; \ - char buf[buflen] = {}; \ - size_t buf_size = min(count, sizeof(buf) - 1); \ - \ - if (copy_from_user(buf, user_buf, buf_size)) \ - return -EFAULT; \ - \ - return iwl_dbgfs_##name##_write(arg, buf, buf_size, ppos); \ -} \ - -#define _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \ -MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = _iwl_dbgfs_##name##_write, \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define _MVM_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \ -MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = _iwl_dbgfs_##name##_write, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h deleted file mode 100644 index d398a6102805..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h +++ /dev/null @@ -1,476 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_bt_coex_h__ -#define __fw_api_bt_coex_h__ - -#include -#include - -#define BITS(nb) (BIT(nb) - 1) - -/** - * enum iwl_bt_coex_flags - flags for BT_COEX command - * @BT_COEX_MODE_POS: - * @BT_COEX_MODE_MSK: - * @BT_COEX_DISABLE_OLD: - * @BT_COEX_2W_OLD: - * @BT_COEX_3W_OLD: - * @BT_COEX_NW_OLD: - * @BT_COEX_AUTO_OLD: - * @BT_COEX_BT_OLD: Antenna is for BT (manufacuring tests) - * @BT_COEX_WIFI_OLD: Antenna is for BT (manufacuring tests) - * @BT_COEX_SYNC2SCO: - * @BT_COEX_CORUNNING: - * @BT_COEX_MPLUT: - * @BT_COEX_TTC: - * @BT_COEX_RRC: - * - * The COEX_MODE must be set for each command. Even if it is not changed. - */ -enum iwl_bt_coex_flags { - BT_COEX_MODE_POS = 3, - BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS, - BT_COEX_DISABLE_OLD = 0x0 << BT_COEX_MODE_POS, - BT_COEX_2W_OLD = 0x1 << BT_COEX_MODE_POS, - BT_COEX_3W_OLD = 0x2 << BT_COEX_MODE_POS, - BT_COEX_NW_OLD = 0x3 << BT_COEX_MODE_POS, - BT_COEX_AUTO_OLD = 0x5 << BT_COEX_MODE_POS, - BT_COEX_BT_OLD = 0x6 << BT_COEX_MODE_POS, - BT_COEX_WIFI_OLD = 0x7 << BT_COEX_MODE_POS, - BT_COEX_SYNC2SCO = BIT(7), - BT_COEX_CORUNNING = BIT(8), - BT_COEX_MPLUT = BIT(9), - BT_COEX_TTC = BIT(20), - BT_COEX_RRC = BIT(21), -}; - -/* - * indicates what has changed in the BT_COEX command. - * BT_VALID_ENABLE must be set for each command. Commands without this bit will - * discarded by the firmware - */ -enum iwl_bt_coex_valid_bit_msk { - BT_VALID_ENABLE = BIT(0), - BT_VALID_BT_PRIO_BOOST = BIT(1), - BT_VALID_MAX_KILL = BIT(2), - BT_VALID_3W_TMRS = BIT(3), - BT_VALID_KILL_ACK = BIT(4), - BT_VALID_KILL_CTS = BIT(5), - BT_VALID_REDUCED_TX_POWER = BIT(6), - BT_VALID_LUT = BIT(7), - BT_VALID_WIFI_RX_SW_PRIO_BOOST = BIT(8), - BT_VALID_WIFI_TX_SW_PRIO_BOOST = BIT(9), - BT_VALID_MULTI_PRIO_LUT = BIT(10), - BT_VALID_TRM_KICK_FILTER = BIT(11), - BT_VALID_CORUN_LUT_20 = BIT(12), - BT_VALID_CORUN_LUT_40 = BIT(13), - BT_VALID_ANT_ISOLATION = BIT(14), - BT_VALID_ANT_ISOLATION_THRS = BIT(15), - BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16), - BT_VALID_TXRX_MAX_FREQ_0 = BIT(17), - BT_VALID_SYNC_TO_SCO = BIT(18), - BT_VALID_TTC = BIT(20), - BT_VALID_RRC = BIT(21), -}; - -/** - * enum iwl_bt_reduced_tx_power - allows to reduce txpower for WiFi frames. - * @BT_REDUCED_TX_POWER_CTL: reduce Tx power for control frames - * @BT_REDUCED_TX_POWER_DATA: reduce Tx power for data frames - * - * This mechanism allows to have BT and WiFi run concurrently. Since WiFi - * reduces its Tx power, it can work along with BT, hence reducing the amount - * of WiFi frames being killed by BT. - */ -enum iwl_bt_reduced_tx_power { - BT_REDUCED_TX_POWER_CTL = BIT(0), - BT_REDUCED_TX_POWER_DATA = BIT(1), -}; - -enum iwl_bt_coex_lut_type { - BT_COEX_TIGHT_LUT = 0, - BT_COEX_LOOSE_LUT, - BT_COEX_TX_DIS_LUT, - - BT_COEX_MAX_LUT, - BT_COEX_INVALID_LUT = 0xff, -}; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */ - -#define BT_COEX_LUT_SIZE (12) -#define BT_COEX_CORUN_LUT_SIZE (32) -#define BT_COEX_MULTI_PRIO_LUT_SIZE (2) -#define BT_COEX_BOOST_SIZE (4) -#define BT_REDUCED_TX_POWER_BIT BIT(7) - -/** - * struct iwl_bt_coex_cmd_old - bt coex configuration command - * @flags:&enum iwl_bt_coex_flags - * @max_kill: - * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power - * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT - * should be set by default - * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT - * should be set by default - * @bt4_antenna_isolation: antenna isolation - * @bt4_antenna_isolation_thr: antenna threshold value - * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency - * @bt4_tx_rx_max_freq0: TxRx max frequency - * @bt_prio_boost: BT priority boost registers - * @wifi_tx_prio_boost: SW boost of wifi tx priority - * @wifi_rx_prio_boost: SW boost of wifi rx priority - * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK. - * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS. - * @decision_lut: PTA decision LUT, per Prio-Ch - * @bt4_multiprio_lut: multi priority LUT configuration - * @bt4_corun_lut20: co-running 20 MHz LUT configuration - * @bt4_corun_lut40: co-running 40 MHz LUT configuration - * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk - * - * The structure is used for the BT_COEX command. - */ -struct iwl_bt_coex_cmd_old { - __le32 flags; - u8 max_kill; - u8 bt_reduced_tx_power; - u8 override_primary_lut; - u8 override_secondary_lut; - - u8 bt4_antenna_isolation; - u8 bt4_antenna_isolation_thr; - u8 bt4_tx_tx_delta_freq_thr; - u8 bt4_tx_rx_max_freq0; - - __le32 bt_prio_boost[BT_COEX_BOOST_SIZE]; - __le32 wifi_tx_prio_boost; - __le32 wifi_rx_prio_boost; - __le32 kill_ack_msk; - __le32 kill_cts_msk; - - __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE]; - __le32 bt4_multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE]; - __le32 bt4_corun_lut20[BT_COEX_CORUN_LUT_SIZE]; - __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE]; - - __le32 valid_bit_msk; -} __packed; /* BT_COEX_CMD_API_S_VER_5 */ - -enum iwl_bt_coex_mode { - BT_COEX_DISABLE = 0x0, - BT_COEX_NW = 0x1, - BT_COEX_BT = 0x2, - BT_COEX_WIFI = 0x3, -}; /* BT_COEX_MODES_E */ - -enum iwl_bt_coex_enabled_modules { - BT_COEX_MPLUT_ENABLED = BIT(0), - BT_COEX_MPLUT_BOOST_ENABLED = BIT(1), - BT_COEX_SYNC2SCO_ENABLED = BIT(2), - BT_COEX_CORUN_ENABLED = BIT(3), - BT_COEX_HIGH_BAND_RET = BIT(4), -}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */ - -/** - * struct iwl_bt_coex_cmd - bt coex configuration command - * @mode: enum %iwl_bt_coex_mode - * @enabled_modules: enum %iwl_bt_coex_enabled_modules - * - * The structure is used for the BT_COEX command. - */ -struct iwl_bt_coex_cmd { - __le32 mode; - __le32 enabled_modules; -} __packed; /* BT_COEX_CMD_API_S_VER_6 */ - -/** - * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut - * @corun_lut20: co-running 20 MHz LUT configuration - * @corun_lut40: co-running 40 MHz LUT configuration - * - * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command. - */ -struct iwl_bt_coex_corun_lut_update_cmd { - __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE]; - __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE]; -} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */ - -/** - * struct iwl_bt_coex_reduced_txp_update_cmd - * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the - * bits are the sta_id (value) - */ -struct iwl_bt_coex_reduced_txp_update_cmd { - __le32 reduced_txp; -} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */ - -/** - * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command - * @bt_primary_ci: - * @primary_ch_phy_id: - * @bt_secondary_ci: - * @secondary_ch_phy_id: - * - * Used for BT_COEX_CI command - */ -struct iwl_bt_coex_ci_cmd { - __le64 bt_primary_ci; - __le32 primary_ch_phy_id; - - __le64 bt_secondary_ci; - __le32 secondary_ch_phy_id; -} __packed; /* BT_CI_MSG_API_S_VER_2 */ - -#define BT_MBOX(n_dw, _msg, _pos, _nbits) \ - BT_MBOX##n_dw##_##_msg##_POS = (_pos), \ - BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS - -enum iwl_bt_mxbox_dw0 { - BT_MBOX(0, LE_SLAVE_LAT, 0, 3), - BT_MBOX(0, LE_PROF1, 3, 1), - BT_MBOX(0, LE_PROF2, 4, 1), - BT_MBOX(0, LE_PROF_OTHER, 5, 1), - BT_MBOX(0, CHL_SEQ_N, 8, 4), - BT_MBOX(0, INBAND_S, 13, 1), - BT_MBOX(0, LE_MIN_RSSI, 16, 4), - BT_MBOX(0, LE_SCAN, 20, 1), - BT_MBOX(0, LE_ADV, 21, 1), - BT_MBOX(0, LE_MAX_TX_POWER, 24, 4), - BT_MBOX(0, OPEN_CON_1, 28, 2), -}; - -enum iwl_bt_mxbox_dw1 { - BT_MBOX(1, BR_MAX_TX_POWER, 0, 4), - BT_MBOX(1, IP_SR, 4, 1), - BT_MBOX(1, LE_MSTR, 5, 1), - BT_MBOX(1, AGGR_TRFC_LD, 8, 6), - BT_MBOX(1, MSG_TYPE, 16, 3), - BT_MBOX(1, SSN, 19, 2), -}; - -enum iwl_bt_mxbox_dw2 { - BT_MBOX(2, SNIFF_ACT, 0, 3), - BT_MBOX(2, PAG, 3, 1), - BT_MBOX(2, INQUIRY, 4, 1), - BT_MBOX(2, CONN, 5, 1), - BT_MBOX(2, SNIFF_INTERVAL, 8, 5), - BT_MBOX(2, DISC, 13, 1), - BT_MBOX(2, SCO_TX_ACT, 16, 2), - BT_MBOX(2, SCO_RX_ACT, 18, 2), - BT_MBOX(2, ESCO_RE_TX, 20, 2), - BT_MBOX(2, SCO_DURATION, 24, 6), -}; - -enum iwl_bt_mxbox_dw3 { - BT_MBOX(3, SCO_STATE, 0, 1), - BT_MBOX(3, SNIFF_STATE, 1, 1), - BT_MBOX(3, A2DP_STATE, 2, 1), - BT_MBOX(3, ACL_STATE, 3, 1), - BT_MBOX(3, MSTR_STATE, 4, 1), - BT_MBOX(3, OBX_STATE, 5, 1), - BT_MBOX(3, OPEN_CON_2, 8, 2), - BT_MBOX(3, TRAFFIC_LOAD, 10, 2), - BT_MBOX(3, CHL_SEQN_LSB, 12, 1), - BT_MBOX(3, INBAND_P, 13, 1), - BT_MBOX(3, MSG_TYPE_2, 16, 3), - BT_MBOX(3, SSN_2, 19, 2), - BT_MBOX(3, UPDATE_REQUEST, 21, 1), -}; - -#define BT_MBOX_MSG(_notif, _num, _field) \ - ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ - >> BT_MBOX##_num##_##_field##_POS) - -enum iwl_bt_activity_grading { - BT_OFF = 0, - BT_ON_NO_CONNECTION = 1, - BT_LOW_TRAFFIC = 2, - BT_HIGH_TRAFFIC = 3, - - BT_MAX_AG, -}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */ - -enum iwl_bt_ci_compliance { - BT_CI_COMPLIANCE_NONE = 0, - BT_CI_COMPLIANCE_PRIMARY = 1, - BT_CI_COMPLIANCE_SECONDARY = 2, - BT_CI_COMPLIANCE_BOTH = 3, -}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */ - -#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id) \ - (_ttc_rrc_status & BIT(_phy_id)) - -#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id) \ - ((_ttc_rrc_status >> 4) & BIT(_phy_id)) - -/** - * struct iwl_bt_coex_profile_notif - notification about BT coex - * @mbox_msg: message from BT to WiFi - * @msg_idx: the index of the message - * @bt_ci_compliance: enum %iwl_bt_ci_compliance - * @primary_ch_lut: LUT used for primary channel enum %iwl_bt_coex_lut_type - * @secondary_ch_lut: LUT used for secondary channel enume %iwl_bt_coex_lut_type - * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading - * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY - */ -struct iwl_bt_coex_profile_notif { - __le32 mbox_msg[4]; - __le32 msg_idx; - __le32 bt_ci_compliance; - - __le32 primary_ch_lut; - __le32 secondary_ch_lut; - __le32 bt_activity_grading; - u8 ttc_rrc_status; - u8 reserved[3]; -} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */ - -enum iwl_bt_coex_prio_table_event { - BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2 = 1, - BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1 = 2, - BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2 = 3, - BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1 = 4, - BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2 = 5, - BT_COEX_PRIO_TBL_EVT_DTIM = 6, - BT_COEX_PRIO_TBL_EVT_SCAN52 = 7, - BT_COEX_PRIO_TBL_EVT_SCAN24 = 8, - BT_COEX_PRIO_TBL_EVT_IDLE = 9, - BT_COEX_PRIO_TBL_EVT_MAX = 16, -}; /* BT_COEX_PRIO_TABLE_EVENTS_API_E_VER_1 */ - -enum iwl_bt_coex_prio_table_prio { - BT_COEX_PRIO_TBL_DISABLED = 0, - BT_COEX_PRIO_TBL_PRIO_LOW = 1, - BT_COEX_PRIO_TBL_PRIO_HIGH = 2, - BT_COEX_PRIO_TBL_PRIO_BYPASS = 3, - BT_COEX_PRIO_TBL_PRIO_COEX_OFF = 4, - BT_COEX_PRIO_TBL_PRIO_COEX_ON = 5, - BT_COEX_PRIO_TBL_PRIO_COEX_IDLE = 6, - BT_COEX_PRIO_TBL_MAX = 8, -}; /* BT_COEX_PRIO_TABLE_PRIORITIES_API_E_VER_1 */ - -#define BT_COEX_PRIO_TBL_SHRD_ANT_POS (0) -#define BT_COEX_PRIO_TBL_PRIO_POS (1) -#define BT_COEX_PRIO_TBL_RESERVED_POS (4) - -/** - * struct iwl_bt_coex_prio_tbl_cmd - priority table for BT coex - * @prio_tbl: - */ -struct iwl_bt_coex_prio_tbl_cmd { - u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX]; -} __packed; - -/** - * struct iwl_bt_coex_ci_cmd_old - bt coex channel inhibition command - * @bt_primary_ci: - * @bt_secondary_ci: - * @co_run_bw_primary: - * @co_run_bw_secondary: - * @primary_ch_phy_id: - * @secondary_ch_phy_id: - * - * Used for BT_COEX_CI command - */ -struct iwl_bt_coex_ci_cmd_old { - __le64 bt_primary_ci; - __le64 bt_secondary_ci; - - u8 co_run_bw_primary; - u8 co_run_bw_secondary; - u8 primary_ch_phy_id; - u8 secondary_ch_phy_id; -} __packed; /* BT_CI_MSG_API_S_VER_1 */ - -/** - * struct iwl_bt_coex_profile_notif_old - notification about BT coex - * @mbox_msg: message from BT to WiFi - * @msg_idx: the index of the message - * @bt_status: 0 - off, 1 - on - * @bt_open_conn: number of BT connections open - * @bt_traffic_load: load of BT traffic - * @bt_agg_traffic_load: aggregated load of BT traffic - * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant - * @primary_ch_lut: LUT used for primary channel - * @secondary_ch_lut: LUT used for secondary channel - * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading - */ -struct iwl_bt_coex_profile_notif_old { - __le32 mbox_msg[4]; - __le32 msg_idx; - u8 bt_status; - u8 bt_open_conn; - u8 bt_traffic_load; - u8 bt_agg_traffic_load; - u8 bt_ci_compliance; - u8 ttc_enabled; - u8 rrc_enabled; - u8 reserved; - - __le32 primary_ch_lut; - __le32 secondary_ch_lut; - __le32 bt_activity_grading; -} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */ - -#endif /* __fw_api_bt_coex_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h deleted file mode 100644 index 20521bebb0b1..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ /dev/null @@ -1,425 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_d3_h__ -#define __fw_api_d3_h__ - -/** - * enum iwl_d3_wakeup_flags - D3 manager wakeup flags - * @IWL_WAKEUP_D3_CONFIG_FW_ERROR: wake up on firmware sysassert - */ -enum iwl_d3_wakeup_flags { - IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0), -}; /* D3_MANAGER_WAKEUP_CONFIG_API_E_VER_3 */ - -/** - * struct iwl_d3_manager_config - D3 manager configuration command - * @min_sleep_time: minimum sleep time (in usec) - * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags - * @wakeup_host_timer: force wakeup after this many seconds - * - * The structure is used for the D3_CONFIG_CMD command. - */ -struct iwl_d3_manager_config { - __le32 min_sleep_time; - __le32 wakeup_flags; - __le32 wakeup_host_timer; -} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */ - - -/* TODO: OFFLOADS_QUERY_API_S_VER_1 */ - -/** - * enum iwl_d3_proto_offloads - enabled protocol offloads - * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled - * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled - */ -enum iwl_proto_offloads { - IWL_D3_PROTO_OFFLOAD_ARP = BIT(0), - IWL_D3_PROTO_OFFLOAD_NS = BIT(1), -}; - -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L 12 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S 4 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 12 - -#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L 4 -#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S 2 - -/** - * struct iwl_proto_offload_cmd_common - ARP/NS offload common part - * @enabled: enable flags - * @remote_ipv4_addr: remote address to answer to (or zero if all) - * @host_ipv4_addr: our IPv4 address to respond to queries for - * @arp_mac_addr: our MAC address for ARP responses - * @reserved: unused - */ -struct iwl_proto_offload_cmd_common { - __le32 enabled; - __be32 remote_ipv4_addr; - __be32 host_ipv4_addr; - u8 arp_mac_addr[ETH_ALEN]; - __le16 reserved; -} __packed; - -/** - * struct iwl_proto_offload_cmd_v1 - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @remote_ipv6_addr: remote address to answer to (or zero if all) - * @solicited_node_ipv6_addr: broken -- solicited node address exists - * for each target address - * @target_ipv6_addr: our target addresses - * @ndp_mac_addr: neighbor solicitation response MAC address - */ -struct iwl_proto_offload_cmd_v1 { - struct iwl_proto_offload_cmd_common common; - u8 remote_ipv6_addr[16]; - u8 solicited_node_ipv6_addr[16]; - u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1][16]; - u8 ndp_mac_addr[ETH_ALEN]; - __le16 reserved2; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */ - -/** - * struct iwl_proto_offload_cmd_v2 - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @remote_ipv6_addr: remote address to answer to (or zero if all) - * @solicited_node_ipv6_addr: broken -- solicited node address exists - * for each target address - * @target_ipv6_addr: our target addresses - * @ndp_mac_addr: neighbor solicitation response MAC address - */ -struct iwl_proto_offload_cmd_v2 { - struct iwl_proto_offload_cmd_common common; - u8 remote_ipv6_addr[16]; - u8 solicited_node_ipv6_addr[16]; - u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2][16]; - u8 ndp_mac_addr[ETH_ALEN]; - u8 numValidIPv6Addresses; - u8 reserved2[3]; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */ - -struct iwl_ns_config { - struct in6_addr source_ipv6_addr; - struct in6_addr dest_ipv6_addr; - u8 target_mac_addr[ETH_ALEN]; - __le16 reserved; -} __packed; /* NS_OFFLOAD_CONFIG */ - -struct iwl_targ_addr { - struct in6_addr addr; - __le32 config_num; -} __packed; /* TARGET_IPV6_ADDRESS */ - -/** - * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @target_ipv6_addr: target IPv6 addresses - * @ns_config: NS offload configurations - */ -struct iwl_proto_offload_cmd_v3_small { - struct iwl_proto_offload_cmd_common common; - __le32 num_valid_ipv6_addrs; - struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S]; - struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S]; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ - -/** - * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration - * @common: common/IPv4 configuration - * @target_ipv6_addr: target IPv6 addresses - * @ns_config: NS offload configurations - */ -struct iwl_proto_offload_cmd_v3_large { - struct iwl_proto_offload_cmd_common common; - __le32 num_valid_ipv6_addrs; - struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L]; - struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L]; -} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ - -/* - * WOWLAN_PATTERNS - */ -#define IWL_WOWLAN_MIN_PATTERN_LEN 16 -#define IWL_WOWLAN_MAX_PATTERN_LEN 128 - -struct iwl_wowlan_pattern { - u8 mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; - u8 pattern[IWL_WOWLAN_MAX_PATTERN_LEN]; - u8 mask_size; - u8 pattern_size; - __le16 reserved; -} __packed; /* WOWLAN_PATTERN_API_S_VER_1 */ - -#define IWL_WOWLAN_MAX_PATTERNS 20 - -struct iwl_wowlan_patterns_cmd { - __le32 n_patterns; - struct iwl_wowlan_pattern patterns[]; -} __packed; /* WOWLAN_PATTERN_ARRAY_API_S_VER_1 */ - -enum iwl_wowlan_wakeup_filters { - IWL_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0), - IWL_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1), - IWL_WOWLAN_WAKEUP_BEACON_MISS = BIT(2), - IWL_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3), - IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4), - IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5), - IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6), - IWL_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(7), - IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT = BIT(8), - IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS = BIT(9), - IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE = BIT(10), - IWL_WOWLAN_WAKEUP_REMOTE_TCP_EXTERNAL = BIT(11), - IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET = BIT(12), - IWL_WOWLAN_WAKEUP_IOAC_MAGIC_PACKET = BIT(13), - IWL_WOWLAN_WAKEUP_HOST_TIMER = BIT(14), - IWL_WOWLAN_WAKEUP_RX_FRAME = BIT(15), - IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16), -}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */ - -struct iwl_wowlan_config_cmd { - __le32 wakeup_filter; - __le16 non_qos_seq; - __le16 qos_seq[8]; - u8 wowlan_ba_teardown_tids; - u8 is_11n_connection; - u8 offloading_tid; - u8 reserved[3]; -} __packed; /* WOWLAN_CONFIG_API_S_VER_3 */ - -/* - * WOWLAN_TSC_RSC_PARAMS - */ -#define IWL_NUM_RSC 16 - -struct tkip_sc { - __le16 iv16; - __le16 pad; - __le32 iv32; -} __packed; /* TKIP_SC_API_U_VER_1 */ - -struct iwl_tkip_rsc_tsc { - struct tkip_sc unicast_rsc[IWL_NUM_RSC]; - struct tkip_sc multicast_rsc[IWL_NUM_RSC]; - struct tkip_sc tsc; -} __packed; /* TKIP_TSC_RSC_API_S_VER_1 */ - -struct aes_sc { - __le64 pn; -} __packed; /* TKIP_AES_SC_API_U_VER_1 */ - -struct iwl_aes_rsc_tsc { - struct aes_sc unicast_rsc[IWL_NUM_RSC]; - struct aes_sc multicast_rsc[IWL_NUM_RSC]; - struct aes_sc tsc; -} __packed; /* AES_TSC_RSC_API_S_VER_1 */ - -union iwl_all_tsc_rsc { - struct iwl_tkip_rsc_tsc tkip; - struct iwl_aes_rsc_tsc aes; -}; /* ALL_TSC_RSC_API_S_VER_2 */ - -struct iwl_wowlan_rsc_tsc_params_cmd { - union iwl_all_tsc_rsc all_tsc_rsc; -} __packed; /* ALL_TSC_RSC_API_S_VER_2 */ - -#define IWL_MIC_KEY_SIZE 8 -struct iwl_mic_keys { - u8 tx[IWL_MIC_KEY_SIZE]; - u8 rx_unicast[IWL_MIC_KEY_SIZE]; - u8 rx_mcast[IWL_MIC_KEY_SIZE]; -} __packed; /* MIC_KEYS_API_S_VER_1 */ - -#define IWL_P1K_SIZE 5 -struct iwl_p1k_cache { - __le16 p1k[IWL_P1K_SIZE]; -} __packed; - -#define IWL_NUM_RX_P1K_CACHE 2 - -struct iwl_wowlan_tkip_params_cmd { - struct iwl_mic_keys mic_keys; - struct iwl_p1k_cache tx; - struct iwl_p1k_cache rx_uni[IWL_NUM_RX_P1K_CACHE]; - struct iwl_p1k_cache rx_multi[IWL_NUM_RX_P1K_CACHE]; -} __packed; /* WOWLAN_TKIP_SETTING_API_S_VER_1 */ - -#define IWL_KCK_MAX_SIZE 32 -#define IWL_KEK_MAX_SIZE 32 - -struct iwl_wowlan_kek_kck_material_cmd { - u8 kck[IWL_KCK_MAX_SIZE]; - u8 kek[IWL_KEK_MAX_SIZE]; - __le16 kck_len; - __le16 kek_len; - __le64 replay_ctr; -} __packed; /* KEK_KCK_MATERIAL_API_S_VER_2 */ - -#define RF_KILL_INDICATOR_FOR_WOWLAN 0x87 - -enum iwl_wowlan_rekey_status { - IWL_WOWLAN_REKEY_POST_REKEY = 0, - IWL_WOWLAN_REKEY_WHILE_REKEY = 1, -}; /* WOWLAN_REKEY_STATUS_API_E_VER_1 */ - -enum iwl_wowlan_wakeup_reason { - IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS = 0, - IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET = BIT(0), - IWL_WOWLAN_WAKEUP_BY_PATTERN = BIT(1), - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON = BIT(2), - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH = BIT(3), - IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE = BIT(4), - IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED = BIT(5), - IWL_WOWLAN_WAKEUP_BY_UCODE_ERROR = BIT(6), - IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST = BIT(7), - IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE = BIT(8), - IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS = BIT(9), - IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE = BIT(10), - IWL_WOWLAN_WAKEUP_BY_REM_WAKE_TCP_EXTERNAL = BIT(11), - IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12), - IWL_WOWLAN_WAKEUP_BY_IOAC_MAGIC_PACKET = BIT(13), - IWL_WOWLAN_WAKEUP_BY_D3_WAKEUP_HOST_TIMER = BIT(14), - IWL_WOWLAN_WAKEUP_BY_RXFRAME_FILTERED_IN = BIT(15), - IWL_WOWLAN_WAKEUP_BY_BEACON_FILTERED_IN = BIT(16), - -}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */ - -struct iwl_wowlan_gtk_status { - u8 key_index; - u8 reserved[3]; - u8 decrypt_key[16]; - u8 tkip_mic_key[8]; - struct iwl_wowlan_rsc_tsc_params_cmd rsc; -} __packed; - -struct iwl_wowlan_status { - struct iwl_wowlan_gtk_status gtk; - __le64 replay_ctr; - __le16 pattern_number; - __le16 non_qos_seq_ctr; - __le16 qos_seq_ctr[8]; - __le32 wakeup_reasons; - __le32 num_of_gtk_rekeys; - __le32 transmitted_ndps; - __le32 received_beacons; - __le32 wake_packet_length; - __le32 wake_packet_bufsize; - u8 wake_packet[]; /* can be truncated from _length to _bufsize */ -} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */ - -#define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64 -#define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128 -#define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048 - -struct iwl_tcp_packet_info { - __le16 tcp_pseudo_header_checksum; - __le16 tcp_payload_length; -} __packed; /* TCP_PACKET_INFO_API_S_VER_2 */ - -struct iwl_tcp_packet { - struct iwl_tcp_packet_info info; - u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; - u8 data[IWL_WOWLAN_TCP_MAX_PACKET_LEN]; -} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */ - -struct iwl_remote_wake_packet { - struct iwl_tcp_packet_info info; - u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8]; - u8 data[IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN]; -} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */ - -struct iwl_wowlan_remote_wake_config { - __le32 connection_max_time; /* unused */ - /* TCP_PROTOCOL_CONFIG_API_S_VER_1 */ - u8 max_syn_retries; - u8 max_data_retries; - u8 tcp_syn_ack_timeout; - u8 tcp_ack_timeout; - - struct iwl_tcp_packet syn_tx; - struct iwl_tcp_packet synack_rx; - struct iwl_tcp_packet keepalive_ack_rx; - struct iwl_tcp_packet fin_tx; - - struct iwl_remote_wake_packet keepalive_tx; - struct iwl_remote_wake_packet wake_rx; - - /* REMOTE_WAKE_OFFSET_INFO_API_S_VER_1 */ - u8 sequence_number_offset; - u8 sequence_number_length; - u8 token_offset; - u8 token_length; - /* REMOTE_WAKE_PROTOCOL_PARAMS_API_S_VER_1 */ - __le32 initial_sequence_number; - __le16 keepalive_interval; - __le16 num_tokens; - u8 tokens[IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS]; -} __packed; /* REMOTE_WAKE_CONFIG_API_S_VER_2 */ - -/* TODO: NetDetect API */ - -#endif /* __fw_api_d3_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h deleted file mode 100644 index f3f3ee0a766b..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h +++ /dev/null @@ -1,387 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_mac_h__ -#define __fw_api_mac_h__ - -/* - * The first MAC indices (starting from 0) - * are available to the driver, AUX follows - */ -#define MAC_INDEX_AUX 4 -#define MAC_INDEX_MIN_DRIVER 0 -#define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX -#define NUM_MAC_INDEX (MAC_INDEX_AUX + 1) - -enum iwl_ac { - AC_BK, - AC_BE, - AC_VI, - AC_VO, - AC_NUM, -}; - -/** - * enum iwl_mac_protection_flags - MAC context flags - * @MAC_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames, - * this will require CCK RTS/CTS2self. - * RTS/CTS will protect full burst time. - * @MAC_PROT_FLG_HT_PROT: enable HT protection - * @MAC_PROT_FLG_FAT_PROT: protect 40 MHz transmissions - * @MAC_PROT_FLG_SELF_CTS_EN: allow CTS2self - */ -enum iwl_mac_protection_flags { - MAC_PROT_FLG_TGG_PROTECT = BIT(3), - MAC_PROT_FLG_HT_PROT = BIT(23), - MAC_PROT_FLG_FAT_PROT = BIT(24), - MAC_PROT_FLG_SELF_CTS_EN = BIT(30), -}; - -#define MAC_FLG_SHORT_SLOT BIT(4) -#define MAC_FLG_SHORT_PREAMBLE BIT(5) - -/** - * enum iwl_mac_types - Supported MAC types - * @FW_MAC_TYPE_FIRST: lowest supported MAC type - * @FW_MAC_TYPE_AUX: Auxiliary MAC (internal) - * @FW_MAC_TYPE_LISTENER: monitor MAC type (?) - * @FW_MAC_TYPE_PIBSS: Pseudo-IBSS - * @FW_MAC_TYPE_IBSS: IBSS - * @FW_MAC_TYPE_BSS_STA: BSS (managed) station - * @FW_MAC_TYPE_P2P_DEVICE: P2P Device - * @FW_MAC_TYPE_P2P_STA: P2P client - * @FW_MAC_TYPE_GO: P2P GO - * @FW_MAC_TYPE_TEST: ? - * @FW_MAC_TYPE_MAX: highest support MAC type - */ -enum iwl_mac_types { - FW_MAC_TYPE_FIRST = 1, - FW_MAC_TYPE_AUX = FW_MAC_TYPE_FIRST, - FW_MAC_TYPE_LISTENER, - FW_MAC_TYPE_PIBSS, - FW_MAC_TYPE_IBSS, - FW_MAC_TYPE_BSS_STA, - FW_MAC_TYPE_P2P_DEVICE, - FW_MAC_TYPE_P2P_STA, - FW_MAC_TYPE_GO, - FW_MAC_TYPE_TEST, - FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST -}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */ - -/** - * enum iwl_tsf_id - TSF hw timer ID - * @TSF_ID_A: use TSF A - * @TSF_ID_B: use TSF B - * @TSF_ID_C: use TSF C - * @TSF_ID_D: use TSF D - * @NUM_TSF_IDS: number of TSF timers available - */ -enum iwl_tsf_id { - TSF_ID_A = 0, - TSF_ID_B = 1, - TSF_ID_C = 2, - TSF_ID_D = 3, - NUM_TSF_IDS = 4, -}; /* TSF_ID_API_E_VER_1 */ - -/** - * struct iwl_mac_data_ap - configuration data for AP MAC context - * @beacon_time: beacon transmit time in system time - * @beacon_tsf: beacon transmit time in TSF - * @bi: beacon interval in TU - * @bi_reciprocal: 2^32 / bi - * @dtim_interval: dtim transmit time in TU - * @dtim_reciprocal: 2^32 / dtim_interval - * @mcast_qid: queue ID for multicast traffic - * @beacon_template: beacon template ID - */ -struct iwl_mac_data_ap { - __le32 beacon_time; - __le64 beacon_tsf; - __le32 bi; - __le32 bi_reciprocal; - __le32 dtim_interval; - __le32 dtim_reciprocal; - __le32 mcast_qid; - __le32 beacon_template; -} __packed; /* AP_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_ibss - configuration data for IBSS MAC context - * @beacon_time: beacon transmit time in system time - * @beacon_tsf: beacon transmit time in TSF - * @bi: beacon interval in TU - * @bi_reciprocal: 2^32 / bi - * @beacon_template: beacon template ID - */ -struct iwl_mac_data_ibss { - __le32 beacon_time; - __le64 beacon_tsf; - __le32 bi; - __le32 bi_reciprocal; - __le32 beacon_template; -} __packed; /* IBSS_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_sta - configuration data for station MAC context - * @is_assoc: 1 for associated state, 0 otherwise - * @dtim_time: DTIM arrival time in system time - * @dtim_tsf: DTIM arrival time in TSF - * @bi: beacon interval in TU, applicable only when associated - * @bi_reciprocal: 2^32 / bi , applicable only when associated - * @dtim_interval: DTIM interval in TU, applicable only when associated - * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated - * @listen_interval: in beacon intervals, applicable only when associated - * @assoc_id: unique ID assigned by the AP during association - */ -struct iwl_mac_data_sta { - __le32 is_assoc; - __le32 dtim_time; - __le64 dtim_tsf; - __le32 bi; - __le32 bi_reciprocal; - __le32 dtim_interval; - __le32 dtim_reciprocal; - __le32 listen_interval; - __le32 assoc_id; - __le32 assoc_beacon_arrive_time; -} __packed; /* STA_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_go - configuration data for P2P GO MAC context - * @ap: iwl_mac_data_ap struct with most config data - * @ctwin: client traffic window in TU (period after TBTT when GO is present). - * 0 indicates that there is no CT window. - * @opp_ps_enabled: indicate that opportunistic PS allowed - */ -struct iwl_mac_data_go { - struct iwl_mac_data_ap ap; - __le32 ctwin; - __le32 opp_ps_enabled; -} __packed; /* GO_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_p2p_sta - configuration data for P2P client MAC context - * @sta: iwl_mac_data_sta struct with most config data - * @ctwin: client traffic window in TU (period after TBTT when GO is present). - * 0 indicates that there is no CT window. - */ -struct iwl_mac_data_p2p_sta { - struct iwl_mac_data_sta sta; - __le32 ctwin; -} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */ - -/** - * struct iwl_mac_data_pibss - Pseudo IBSS config data - * @stats_interval: interval in TU between statistics notifications to host. - */ -struct iwl_mac_data_pibss { - __le32 stats_interval; -} __packed; /* PIBSS_MAC_DATA_API_S_VER_1 */ - -/* - * struct iwl_mac_data_p2p_dev - configuration data for the P2P Device MAC - * context. - * @is_disc_extended: if set to true, P2P Device discoverability is enabled on - * other channels as well. This should be to true only in case that the - * device is discoverable and there is an active GO. Note that setting this - * field when not needed, will increase the number of interrupts and have - * effect on the platform power, as this setting opens the Rx filters on - * all macs. - */ -struct iwl_mac_data_p2p_dev { - __le32 is_disc_extended; -} __packed; /* _P2P_DEV_MAC_DATA_API_S_VER_1 */ - -/** - * enum iwl_mac_filter_flags - MAC context filter flags - * @MAC_FILTER_IN_PROMISC: accept all data frames - * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all management and - * control frames to the host - * @MAC_FILTER_ACCEPT_GRP: accept multicast frames - * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames - * @MAC_FILTER_DIS_GRP_DECRYPT: don't decrypt multicast frames - * @MAC_FILTER_IN_BEACON: transfer foreign BSS's beacons to host - * (in station mode when associated) - * @MAC_FILTER_OUT_BCAST: filter out all broadcast frames - * @MAC_FILTER_IN_CRC32: extract FCS and append it to frames - * @MAC_FILTER_IN_PROBE_REQUEST: pass probe requests to host - */ -enum iwl_mac_filter_flags { - MAC_FILTER_IN_PROMISC = BIT(0), - MAC_FILTER_IN_CONTROL_AND_MGMT = BIT(1), - MAC_FILTER_ACCEPT_GRP = BIT(2), - MAC_FILTER_DIS_DECRYPT = BIT(3), - MAC_FILTER_DIS_GRP_DECRYPT = BIT(4), - MAC_FILTER_IN_BEACON = BIT(6), - MAC_FILTER_OUT_BCAST = BIT(8), - MAC_FILTER_IN_CRC32 = BIT(11), - MAC_FILTER_IN_PROBE_REQUEST = BIT(12), -}; - -/** - * enum iwl_mac_qos_flags - QoS flags - * @MAC_QOS_FLG_UPDATE_EDCA: ? - * @MAC_QOS_FLG_TGN: HT is enabled - * @MAC_QOS_FLG_TXOP_TYPE: ? - * - */ -enum iwl_mac_qos_flags { - MAC_QOS_FLG_UPDATE_EDCA = BIT(0), - MAC_QOS_FLG_TGN = BIT(1), - MAC_QOS_FLG_TXOP_TYPE = BIT(4), -}; - -/** - * struct iwl_ac_qos - QOS timing params for MAC_CONTEXT_CMD - * @cw_min: Contention window, start value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x0f. - * @cw_max: Contention window, max value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x3f. - * @aifsn: Number of slots in Arbitration Interframe Space (before - * performing random backoff timing prior to Tx). Device default 1. - * @fifos_mask: FIFOs used by this MAC for this AC - * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. - * - * One instance of this config struct for each of 4 EDCA access categories - * in struct iwl_qosparam_cmd. - * - * Device will automatically increase contention window by (2*CW) + 1 for each - * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW - * value, to cap the CW value. - */ -struct iwl_ac_qos { - __le16 cw_min; - __le16 cw_max; - u8 aifsn; - u8 fifos_mask; - __le16 edca_txop; -} __packed; /* AC_QOS_API_S_VER_2 */ - -/** - * struct iwl_mac_ctx_cmd - command structure to configure MAC contexts - * ( MAC_CONTEXT_CMD = 0x28 ) - * @id_and_color: ID and color of the MAC - * @action: action to perform, one of FW_CTXT_ACTION_* - * @mac_type: one of FW_MAC_TYPE_* - * @tsd_id: TSF HW timer, one of TSF_ID_* - * @node_addr: MAC address - * @bssid_addr: BSSID - * @cck_rates: basic rates available for CCK - * @ofdm_rates: basic rates available for OFDM - * @protection_flags: combination of MAC_PROT_FLG_FLAG_* - * @cck_short_preamble: 0x20 for enabling short preamble, 0 otherwise - * @short_slot: 0x10 for enabling short slots, 0 otherwise - * @filter_flags: combination of MAC_FILTER_* - * @qos_flags: from MAC_QOS_FLG_* - * @ac: one iwl_mac_qos configuration for each AC - * @mac_specific: one of struct iwl_mac_data_*, according to mac_type - */ -struct iwl_mac_ctx_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - /* MAC_CONTEXT_COMMON_DATA_API_S_VER_1 */ - __le32 mac_type; - __le32 tsf_id; - u8 node_addr[6]; - __le16 reserved_for_node_addr; - u8 bssid_addr[6]; - __le16 reserved_for_bssid_addr; - __le32 cck_rates; - __le32 ofdm_rates; - __le32 protection_flags; - __le32 cck_short_preamble; - __le32 short_slot; - __le32 filter_flags; - /* MAC_QOS_PARAM_API_S_VER_1 */ - __le32 qos_flags; - struct iwl_ac_qos ac[AC_NUM+1]; - /* MAC_CONTEXT_COMMON_DATA_API_S */ - union { - struct iwl_mac_data_ap ap; - struct iwl_mac_data_go go; - struct iwl_mac_data_sta sta; - struct iwl_mac_data_p2p_sta p2p_sta; - struct iwl_mac_data_p2p_dev p2p_dev; - struct iwl_mac_data_pibss pibss; - struct iwl_mac_data_ibss ibss; - }; -} __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */ - -static inline u32 iwl_mvm_reciprocal(u32 v) -{ - if (!v) - return 0; - return 0xFFFFFFFF / v; -} - -#define IWL_NONQOS_SEQ_GET 0x1 -#define IWL_NONQOS_SEQ_SET 0x2 -struct iwl_nonqos_seq_query_cmd { - __le32 get_set_flag; - __le32 mac_id_n_color; - __le16 value; - __le16 reserved; -} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */ - -#endif /* __fw_api_mac_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h deleted file mode 100644 index c8f3e2536cbb..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ /dev/null @@ -1,467 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __fw_api_power_h__ -#define __fw_api_power_h__ - -/* Power Management Commands, Responses, Notifications */ - -/** - * enum iwl_ltr_config_flags - masks for LTR config command flags - * @LTR_CFG_FLAG_FEATURE_ENABLE: Feature operational status - * @LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS: allow LTR change on shadow - * memory access - * @LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH: allow LTR msg send on ANY LTR - * reg change - * @LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3: allow LTR msg send on transition from - * D0 to D3 - * @LTR_CFG_FLAG_SW_SET_SHORT: fixed static short LTR register - * @LTR_CFG_FLAG_SW_SET_LONG: fixed static short LONG register - * @LTR_CFG_FLAG_DENIE_C10_ON_PD: allow going into C10 on PD - */ -enum iwl_ltr_config_flags { - LTR_CFG_FLAG_FEATURE_ENABLE = BIT(0), - LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS = BIT(1), - LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH = BIT(2), - LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3 = BIT(3), - LTR_CFG_FLAG_SW_SET_SHORT = BIT(4), - LTR_CFG_FLAG_SW_SET_LONG = BIT(5), - LTR_CFG_FLAG_DENIE_C10_ON_PD = BIT(6), -}; - -/** - * struct iwl_ltr_config_cmd_v1 - configures the LTR - * @flags: See %enum iwl_ltr_config_flags - */ -struct iwl_ltr_config_cmd_v1 { - __le32 flags; - __le32 static_long; - __le32 static_short; -} __packed; /* LTR_CAPABLE_API_S_VER_1 */ - -#define LTR_VALID_STATES_NUM 4 - -/** - * struct iwl_ltr_config_cmd - configures the LTR - * @flags: See %enum iwl_ltr_config_flags - * @static_long: - * @static_short: - * @ltr_cfg_values: - * @ltr_short_idle_timeout: - */ -struct iwl_ltr_config_cmd { - __le32 flags; - __le32 static_long; - __le32 static_short; - __le32 ltr_cfg_values[LTR_VALID_STATES_NUM]; - __le32 ltr_short_idle_timeout; -} __packed; /* LTR_CAPABLE_API_S_VER_2 */ - -/* Radio LP RX Energy Threshold measured in dBm */ -#define POWER_LPRX_RSSI_THRESHOLD 75 -#define POWER_LPRX_RSSI_THRESHOLD_MAX 94 -#define POWER_LPRX_RSSI_THRESHOLD_MIN 30 - -/** - * enum iwl_power_flags - masks for power table command flags - * @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off - * receiver and transmitter. '0' - does not allow. - * @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management, - * '1' Driver enables PM (use rest of parameters) - * @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM, - * '1' PM could sleep over DTIM till listen Interval. - * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all - * access categories are both delivery and trigger enabled. - * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and - * PBW Snoozing enabled - * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask - * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable. - * @POWER_FLAGS_AP_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving - * detection enablement -*/ -enum iwl_power_flags { - POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), - POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1), - POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2), - POWER_FLAGS_SNOOZE_ENA_MSK = BIT(5), - POWER_FLAGS_BT_SCO_ENA = BIT(8), - POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9), - POWER_FLAGS_LPRX_ENA_MSK = BIT(11), - POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12), -}; - -#define IWL_POWER_VEC_SIZE 5 - -/** - * struct iwl_powertable_cmd - legacy power command. Beside old API support this - * is used also with a new power API for device wide power settings. - * POWER_TABLE_CMD = 0x77 (command, has simple generic response) - * - * @flags: Power table command flags from POWER_FLAGS_* - * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. - * Minimum allowed:- 3 * DTIM. Keep alive period must be - * set regardless of power scheme or current power state. - * FW use this value also when PM is disabled. - * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to - * PSM transition - legacy PM - * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to - * PSM transition - legacy PM - * @sleep_interval: not in use - * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag - * is set. For example, if it is required to skip over - * one DTIM, this value need to be set to 2 (DTIM periods). - * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. - * Default: 80dbm - */ -struct iwl_powertable_cmd { - /* PM_POWER_TABLE_CMD_API_S_VER_6 */ - __le16 flags; - u8 keep_alive_seconds; - u8 debug_flags; - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 sleep_interval[IWL_POWER_VEC_SIZE]; - __le32 skip_dtim_periods; - __le32 lprx_rssi_threshold; -} __packed; - -/** - * enum iwl_device_power_flags - masks for device power command flags - * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off - * receiver and transmitter. '0' - does not allow. -*/ -enum iwl_device_power_flags { - DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), -}; - -/** - * struct iwl_device_power_cmd - device wide power command. - * DEVICE_POWER_CMD = 0x77 (command, has simple generic response) - * - * @flags: Power table command flags from DEVICE_POWER_FLAGS_* - */ -struct iwl_device_power_cmd { - /* PM_POWER_TABLE_CMD_API_S_VER_6 */ - __le16 flags; - __le16 reserved; -} __packed; - -/** - * struct iwl_mac_power_cmd - New power command containing uAPSD support - * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response) - * @id_and_color: MAC contex identifier - * @flags: Power table command flags from POWER_FLAGS_* - * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. - * Minimum allowed:- 3 * DTIM. Keep alive period must be - * set regardless of power scheme or current power state. - * FW use this value also when PM is disabled. - * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to - * PSM transition - legacy PM - * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to - * PSM transition - legacy PM - * @sleep_interval: not in use - * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag - * is set. For example, if it is required to skip over - * one DTIM, this value need to be set to 2 (DTIM periods). - * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to - * PSM transition - uAPSD - * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to - * PSM transition - uAPSD - * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. - * Default: 80dbm - * @num_skip_dtim: Number of DTIMs to skip if Skip over DTIM flag is set - * @snooze_interval: Maximum time between attempts to retrieve buffered data - * from the AP [msec] - * @snooze_window: A window of time in which PBW snoozing insures that all - * packets received. It is also the minimum time from last - * received unicast RX packet, before client stops snoozing - * for data. [msec] - * @snooze_step: TBD - * @qndp_tid: TID client shall use for uAPSD QNDP triggers - * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for - * each corresponding AC. - * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values. - * @uapsd_max_sp: Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct - * values. - * @heavy_tx_thld_packets: TX threshold measured in number of packets - * @heavy_rx_thld_packets: RX threshold measured in number of packets - * @heavy_tx_thld_percentage: TX threshold measured in load's percentage - * @heavy_rx_thld_percentage: RX threshold measured in load's percentage - * @limited_ps_threshold: -*/ -struct iwl_mac_power_cmd { - /* CONTEXT_DESC_API_T_VER_1 */ - __le32 id_and_color; - - /* CLIENT_PM_POWER_TABLE_S_VER_1 */ - __le16 flags; - __le16 keep_alive_seconds; - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 rx_data_timeout_uapsd; - __le32 tx_data_timeout_uapsd; - u8 lprx_rssi_threshold; - u8 skip_dtim_periods; - __le16 snooze_interval; - __le16 snooze_window; - u8 snooze_step; - u8 qndp_tid; - u8 uapsd_ac_flags; - u8 uapsd_max_sp; - u8 heavy_tx_thld_packets; - u8 heavy_rx_thld_packets; - u8 heavy_tx_thld_percentage; - u8 heavy_rx_thld_percentage; - u8 limited_ps_threshold; - u8 reserved; -} __packed; - -/* - * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when - * associated AP is identified as improperly implementing uAPSD protocol. - * PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78 - * @sta_id: index of station in uCode's station table - associated AP ID in - * this context. - */ -struct iwl_uapsd_misbehaving_ap_notif { - __le32 sta_id; - u8 mac_id; - u8 reserved[3]; -} __packed; - -/** - * struct iwl_reduce_tx_power_cmd - TX power reduction command - * REDUCE_TX_POWER_CMD = 0x9f - * @flags: (reserved for future implementation) - * @mac_context_id: id of the mac ctx for which we are reducing TX power. - * @pwr_restriction: TX power restriction in dBms. - */ -struct iwl_reduce_tx_power_cmd { - u8 flags; - u8 mac_context_id; - __le16 pwr_restriction; -} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */ - -enum iwl_dev_tx_power_cmd_mode { - IWL_TX_POWER_MODE_SET_MAC = 0, - IWL_TX_POWER_MODE_SET_DEVICE = 1, - IWL_TX_POWER_MODE_SET_CHAINS = 2, -}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_2 */; - -/** - * struct iwl_dev_tx_power_cmd_v2 - TX power reduction command - * @set_mode: see &enum iwl_dev_tx_power_cmd_mode - * @mac_context_id: id of the mac ctx for which we are reducing TX power. - * @pwr_restriction: TX power restriction in 1/8 dBms. - * @dev_24: device TX power restriction in 1/8 dBms - * @dev_52_low: device TX power restriction upper band - low - * @dev_52_high: device TX power restriction upper band - high - */ -struct iwl_dev_tx_power_cmd_v2 { - __le32 set_mode; - __le32 mac_context_id; - __le16 pwr_restriction; - __le16 dev_24; - __le16 dev_52_low; - __le16 dev_52_high; -} __packed; /* TX_REDUCED_POWER_API_S_VER_2 */ - -#define IWL_NUM_CHAIN_LIMITS 2 -#define IWL_NUM_SUB_BANDS 5 - -/** - * struct iwl_dev_tx_power_cmd - TX power reduction command - * @v2: version 2 of the command, embedded here for easier software handling - * @per_chain_restriction: per chain restrictions - */ -struct iwl_dev_tx_power_cmd { - /* v3 is just an extension of v2 - keep this here */ - struct iwl_dev_tx_power_cmd_v2 v2; - __le16 per_chain_restriction[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS]; -} __packed; /* TX_REDUCED_POWER_API_S_VER_3 */ - -#define IWL_DEV_MAX_TX_POWER 0x7FFF - -/** - * struct iwl_beacon_filter_cmd - * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) - * @id_and_color: MAC contex identifier - * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon - * to driver if delta in Energy values calculated for this and last - * passed beacon is greater than this threshold. Zero value means that - * the Energy change is ignored for beacon filtering, and beacon will - * not be forced to be sent to driver regardless of this delta. Typical - * energy delta 5dB. - * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state. - * Send beacon to driver if delta in Energy values calculated for this - * and last passed beacon is greater than this threshold. Zero value - * means that the Energy change is ignored for beacon filtering while in - * Roaming state, typical energy delta 1dB. - * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values - * calculated for current beacon is less than the threshold, use - * Roaming Energy Delta Threshold, otherwise use normal Energy Delta - * Threshold. Typical energy threshold is -72dBm. - * @bf_temp_threshold: This threshold determines the type of temperature - * filtering (Slow or Fast) that is selected (Units are in Celsuis): - * If the current temperature is above this threshold - Fast filter - * will be used, If the current temperature is below this threshold - - * Slow filter will be used. - * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values - * calculated for this and the last passed beacon is greater than this - * threshold. Zero value means that the temperature change is ignored for - * beacon filtering; beacons will not be forced to be sent to driver - * regardless of whether its temerature has been changed. - * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values - * calculated for this and the last passed beacon is greater than this - * threshold. Zero value means that the temperature change is ignored for - * beacon filtering; beacons will not be forced to be sent to driver - * regardless of whether its temerature has been changed. - * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled. - * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed - * for a specific period of time. Units: Beacons. - * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed - * for a longer period of time then this escape-timeout. Units: Beacons. - * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled. - */ -struct iwl_beacon_filter_cmd { - __le32 bf_energy_delta; - __le32 bf_roaming_energy_delta; - __le32 bf_roaming_state; - __le32 bf_temp_threshold; - __le32 bf_temp_fast_filter; - __le32 bf_temp_slow_filter; - __le32 bf_enable_beacon_filter; - __le32 bf_debug_flag; - __le32 bf_escape_timer; - __le32 ba_escape_timer; - __le32 ba_enable_beacon_abort; -} __packed; - -/* Beacon filtering and beacon abort */ -#define IWL_BF_ENERGY_DELTA_DEFAULT 5 -#define IWL_BF_ENERGY_DELTA_D0I3 20 -#define IWL_BF_ENERGY_DELTA_MAX 255 -#define IWL_BF_ENERGY_DELTA_MIN 0 - -#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1 -#define IWL_BF_ROAMING_ENERGY_DELTA_D0I3 20 -#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255 -#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0 - -#define IWL_BF_ROAMING_STATE_DEFAULT 72 -#define IWL_BF_ROAMING_STATE_D0I3 72 -#define IWL_BF_ROAMING_STATE_MAX 255 -#define IWL_BF_ROAMING_STATE_MIN 0 - -#define IWL_BF_TEMP_THRESHOLD_DEFAULT 112 -#define IWL_BF_TEMP_THRESHOLD_D0I3 112 -#define IWL_BF_TEMP_THRESHOLD_MAX 255 -#define IWL_BF_TEMP_THRESHOLD_MIN 0 - -#define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1 -#define IWL_BF_TEMP_FAST_FILTER_D0I3 1 -#define IWL_BF_TEMP_FAST_FILTER_MAX 255 -#define IWL_BF_TEMP_FAST_FILTER_MIN 0 - -#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5 -#define IWL_BF_TEMP_SLOW_FILTER_D0I3 20 -#define IWL_BF_TEMP_SLOW_FILTER_MAX 255 -#define IWL_BF_TEMP_SLOW_FILTER_MIN 0 - -#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1 - -#define IWL_BF_DEBUG_FLAG_DEFAULT 0 -#define IWL_BF_DEBUG_FLAG_D0I3 0 - -#define IWL_BF_ESCAPE_TIMER_DEFAULT 0 -#define IWL_BF_ESCAPE_TIMER_D0I3 0 -#define IWL_BF_ESCAPE_TIMER_MAX 1024 -#define IWL_BF_ESCAPE_TIMER_MIN 0 - -#define IWL_BA_ESCAPE_TIMER_DEFAULT 6 -#define IWL_BA_ESCAPE_TIMER_D0I3 6 -#define IWL_BA_ESCAPE_TIMER_D3 9 -#define IWL_BA_ESCAPE_TIMER_MAX 1024 -#define IWL_BA_ESCAPE_TIMER_MIN 0 - -#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1 - -#define IWL_BF_CMD_CONFIG(mode) \ - .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA ## mode), \ - .bf_roaming_energy_delta = \ - cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA ## mode), \ - .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE ## mode), \ - .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD ## mode), \ - .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER ## mode), \ - .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER ## mode), \ - .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG ## mode), \ - .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER ## mode), \ - .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER ## mode) - -#define IWL_BF_CMD_CONFIG_DEFAULTS IWL_BF_CMD_CONFIG(_DEFAULT) -#define IWL_BF_CMD_CONFIG_D0I3 IWL_BF_CMD_CONFIG(_D0I3) -#endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h deleted file mode 100644 index 0f1ea80a55ef..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h +++ /dev/null @@ -1,389 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_rs_h__ -#define __fw_api_rs_h__ - -#include "fw-api-mac.h" - -/* - * These serve as indexes into - * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT]; - * TODO: avoid overlap between legacy and HT rates - */ -enum { - IWL_RATE_1M_INDEX = 0, - IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, - IWL_RATE_2M_INDEX, - IWL_RATE_5M_INDEX, - IWL_RATE_11M_INDEX, - IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, - IWL_RATE_6M_INDEX, - IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, - IWL_RATE_MCS_0_INDEX = IWL_RATE_6M_INDEX, - IWL_FIRST_HT_RATE = IWL_RATE_MCS_0_INDEX, - IWL_FIRST_VHT_RATE = IWL_RATE_MCS_0_INDEX, - IWL_RATE_9M_INDEX, - IWL_RATE_12M_INDEX, - IWL_RATE_MCS_1_INDEX = IWL_RATE_12M_INDEX, - IWL_RATE_18M_INDEX, - IWL_RATE_MCS_2_INDEX = IWL_RATE_18M_INDEX, - IWL_RATE_24M_INDEX, - IWL_RATE_MCS_3_INDEX = IWL_RATE_24M_INDEX, - IWL_RATE_36M_INDEX, - IWL_RATE_MCS_4_INDEX = IWL_RATE_36M_INDEX, - IWL_RATE_48M_INDEX, - IWL_RATE_MCS_5_INDEX = IWL_RATE_48M_INDEX, - IWL_RATE_54M_INDEX, - IWL_RATE_MCS_6_INDEX = IWL_RATE_54M_INDEX, - IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX, - IWL_RATE_60M_INDEX, - IWL_RATE_MCS_7_INDEX = IWL_RATE_60M_INDEX, - IWL_LAST_HT_RATE = IWL_RATE_MCS_7_INDEX, - IWL_RATE_MCS_8_INDEX, - IWL_RATE_MCS_9_INDEX, - IWL_LAST_VHT_RATE = IWL_RATE_MCS_9_INDEX, - IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1, - IWL_RATE_COUNT = IWL_LAST_VHT_RATE + 1, -}; - -#define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX) - -/* fw API values for legacy bit rates, both OFDM and CCK */ -enum { - IWL_RATE_6M_PLCP = 13, - IWL_RATE_9M_PLCP = 15, - IWL_RATE_12M_PLCP = 5, - IWL_RATE_18M_PLCP = 7, - IWL_RATE_24M_PLCP = 9, - IWL_RATE_36M_PLCP = 11, - IWL_RATE_48M_PLCP = 1, - IWL_RATE_54M_PLCP = 3, - IWL_RATE_1M_PLCP = 10, - IWL_RATE_2M_PLCP = 20, - IWL_RATE_5M_PLCP = 55, - IWL_RATE_11M_PLCP = 110, - IWL_RATE_INVM_PLCP = -1, -}; - -/* - * rate_n_flags bit fields - * - * The 32-bit value has different layouts in the low 8 bites depending on the - * format. There are three formats, HT, VHT and legacy (11abg, with subformats - * for CCK and OFDM). - * - * High-throughput (HT) rate format - * bit 8 is 1, bit 26 is 0, bit 9 is 0 (OFDM) - * Very High-throughput (VHT) rate format - * bit 8 is 0, bit 26 is 1, bit 9 is 0 (OFDM) - * Legacy OFDM rate format for bits 7:0 - * bit 8 is 0, bit 26 is 0, bit 9 is 0 (OFDM) - * Legacy CCK rate format for bits 7:0: - * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK) - */ - -/* Bit 8: (1) HT format, (0) legacy or VHT format */ -#define RATE_MCS_HT_POS 8 -#define RATE_MCS_HT_MSK (1 << RATE_MCS_HT_POS) - -/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ -#define RATE_MCS_CCK_POS 9 -#define RATE_MCS_CCK_MSK (1 << RATE_MCS_CCK_POS) - -/* Bit 26: (1) VHT format, (0) legacy format in bits 8:0 */ -#define RATE_MCS_VHT_POS 26 -#define RATE_MCS_VHT_MSK (1 << RATE_MCS_VHT_POS) - - -/* - * High-throughput (HT) rate format for bits 7:0 - * - * 2-0: MCS rate base - * 0) 6 Mbps - * 1) 12 Mbps - * 2) 18 Mbps - * 3) 24 Mbps - * 4) 36 Mbps - * 5) 48 Mbps - * 6) 54 Mbps - * 7) 60 Mbps - * 4-3: 0) Single stream (SISO) - * 1) Dual stream (MIMO) - * 2) Triple stream (MIMO) - * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data - * (bits 7-6 are zero) - * - * Together the low 5 bits work out to the MCS index because we don't - * support MCSes above 15/23, and 0-7 have one stream, 8-15 have two - * streams and 16-23 have three streams. We could also support MCS 32 - * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.) - */ -#define RATE_HT_MCS_RATE_CODE_MSK 0x7 -#define RATE_HT_MCS_NSS_POS 3 -#define RATE_HT_MCS_NSS_MSK (3 << RATE_HT_MCS_NSS_POS) - -/* Bit 10: (1) Use Green Field preamble */ -#define RATE_HT_MCS_GF_POS 10 -#define RATE_HT_MCS_GF_MSK (1 << RATE_HT_MCS_GF_POS) - -#define RATE_HT_MCS_INDEX_MSK 0x3f - -/* - * Very High-throughput (VHT) rate format for bits 7:0 - * - * 3-0: VHT MCS (0-9) - * 5-4: number of streams - 1: - * 0) Single stream (SISO) - * 1) Dual stream (MIMO) - * 2) Triple stream (MIMO) - */ - -/* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */ -#define RATE_VHT_MCS_RATE_CODE_MSK 0xf -#define RATE_VHT_MCS_NSS_POS 4 -#define RATE_VHT_MCS_NSS_MSK (3 << RATE_VHT_MCS_NSS_POS) - -/* - * Legacy OFDM rate format for bits 7:0 - * - * 3-0: 0xD) 6 Mbps - * 0xF) 9 Mbps - * 0x5) 12 Mbps - * 0x7) 18 Mbps - * 0x9) 24 Mbps - * 0xB) 36 Mbps - * 0x1) 48 Mbps - * 0x3) 54 Mbps - * (bits 7-4 are 0) - * - * Legacy CCK rate format for bits 7:0: - * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK): - * - * 6-0: 10) 1 Mbps - * 20) 2 Mbps - * 55) 5.5 Mbps - * 110) 11 Mbps - * (bit 7 is 0) - */ -#define RATE_LEGACY_RATE_MSK 0xff - - -/* - * Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz - * 0 and 1 are valid for HT and VHT, 2 and 3 only for VHT - */ -#define RATE_MCS_CHAN_WIDTH_POS 11 -#define RATE_MCS_CHAN_WIDTH_MSK (3 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_20 (0 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_40 (1 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_80 (2 << RATE_MCS_CHAN_WIDTH_POS) -#define RATE_MCS_CHAN_WIDTH_160 (3 << RATE_MCS_CHAN_WIDTH_POS) - -/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ -#define RATE_MCS_SGI_POS 13 -#define RATE_MCS_SGI_MSK (1 << RATE_MCS_SGI_POS) - -/* Bit 14-16: Antenna selection (1) Ant A, (2) Ant B, (4) Ant C */ -#define RATE_MCS_ANT_POS 14 -#define RATE_MCS_ANT_A_MSK (1 << RATE_MCS_ANT_POS) -#define RATE_MCS_ANT_B_MSK (2 << RATE_MCS_ANT_POS) -#define RATE_MCS_ANT_C_MSK (4 << RATE_MCS_ANT_POS) -#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | \ - RATE_MCS_ANT_B_MSK) -#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | \ - RATE_MCS_ANT_C_MSK) -#define RATE_MCS_ANT_MSK RATE_MCS_ANT_ABC_MSK -#define RATE_MCS_ANT_NUM 3 - -/* Bit 17-18: (0) SS, (1) SS*2 */ -#define RATE_MCS_STBC_POS 17 -#define RATE_MCS_HT_STBC_MSK (3 << RATE_MCS_STBC_POS) -#define RATE_MCS_VHT_STBC_MSK (1 << RATE_MCS_STBC_POS) - -/* Bit 19: (0) Beamforming is off, (1) Beamforming is on */ -#define RATE_MCS_BF_POS 19 -#define RATE_MCS_BF_MSK (1 << RATE_MCS_BF_POS) - -/* Bit 20: (0) ZLF is off, (1) ZLF is on */ -#define RATE_MCS_ZLF_POS 20 -#define RATE_MCS_ZLF_MSK (1 << RATE_MCS_ZLF_POS) - -/* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */ -#define RATE_MCS_DUP_POS 24 -#define RATE_MCS_DUP_MSK (3 << RATE_MCS_DUP_POS) - -/* Bit 27: (1) LDPC enabled, (0) LDPC disabled */ -#define RATE_MCS_LDPC_POS 27 -#define RATE_MCS_LDPC_MSK (1 << RATE_MCS_LDPC_POS) - - -/* Link Quality definitions */ - -/* # entries in rate scale table to support Tx retries */ -#define LQ_MAX_RETRY_NUM 16 - -/* Link quality command flags bit fields */ - -/* Bit 0: (0) Don't use RTS (1) Use RTS */ -#define LQ_FLAG_USE_RTS_POS 0 -#define LQ_FLAG_USE_RTS_MSK (1 << LQ_FLAG_USE_RTS_POS) - -/* Bit 1-3: LQ command color. Used to match responses to LQ commands */ -#define LQ_FLAG_COLOR_POS 1 -#define LQ_FLAG_COLOR_MSK (7 << LQ_FLAG_COLOR_POS) - -/* Bit 4-5: Tx RTS BW Signalling - * (0) No RTS BW signalling - * (1) Static BW signalling - * (2) Dynamic BW signalling - */ -#define LQ_FLAG_RTS_BW_SIG_POS 4 -#define LQ_FLAG_RTS_BW_SIG_NONE (0 << LQ_FLAG_RTS_BW_SIG_POS) -#define LQ_FLAG_RTS_BW_SIG_STATIC (1 << LQ_FLAG_RTS_BW_SIG_POS) -#define LQ_FLAG_RTS_BW_SIG_DYNAMIC (2 << LQ_FLAG_RTS_BW_SIG_POS) - -/* Bit 6: (0) No dynamic BW selection (1) Allow dynamic BW selection - * Dyanmic BW selection allows Tx with narrower BW then requested in rates - */ -#define LQ_FLAG_DYNAMIC_BW_POS 6 -#define LQ_FLAG_DYNAMIC_BW_MSK (1 << LQ_FLAG_DYNAMIC_BW_POS) - -/* Single Stream Tx Parameters (lq_cmd->ss_params) - * Flags to control a smart FW decision about whether BFER/STBC/SISO will be - * used for single stream Tx. - */ - -/* Bit 0-1: Max STBC streams allowed. Can be 0-3. - * (0) - No STBC allowed - * (1) - 2x1 STBC allowed (HT/VHT) - * (2) - 4x2 STBC allowed (HT/VHT) - * (3) - 3x2 STBC allowed (HT only) - * All our chips are at most 2 antennas so only (1) is valid for now. - */ -#define LQ_SS_STBC_ALLOWED_POS 0 -#define LQ_SS_STBC_ALLOWED_MSK (3 << LQ_SS_STBC_ALLOWED_MSK) - -/* 2x1 STBC is allowed */ -#define LQ_SS_STBC_1SS_ALLOWED (1 << LQ_SS_STBC_ALLOWED_POS) - -/* Bit 2: Beamformer (VHT only) is allowed */ -#define LQ_SS_BFER_ALLOWED_POS 2 -#define LQ_SS_BFER_ALLOWED (1 << LQ_SS_BFER_ALLOWED_POS) - -/* Bit 3: Force BFER or STBC for testing - * If this is set: - * If BFER is allowed then force the ucode to choose BFER else - * If STBC is allowed then force the ucode to choose STBC over SISO - */ -#define LQ_SS_FORCE_POS 3 -#define LQ_SS_FORCE (1 << LQ_SS_FORCE_POS) - -/* Bit 31: ss_params field is valid. Used for FW backward compatibility - * with other drivers which don't support the ss_params API yet - */ -#define LQ_SS_PARAMS_VALID_POS 31 -#define LQ_SS_PARAMS_VALID (1 << LQ_SS_PARAMS_VALID_POS) - -/** - * struct iwl_lq_cmd - link quality command - * @sta_id: station to update - * @control: not used - * @flags: combination of LQ_FLAG_* - * @mimo_delim: the first SISO index in rs_table, which separates MIMO - * and SISO rates - * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD). - * Should be ANT_[ABC] - * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC] - * @initial_rate_index: first index from rs_table per AC category - * @agg_time_limit: aggregation max time threshold in usec/100, meaning - * value of 100 is one usec. Range is 100 to 8000 - * @agg_disable_start_th: try-count threshold for starting aggregation. - * If a frame has higher try-count, it should not be selected for - * starting an aggregation sequence. - * @agg_frame_cnt_limit: max frame count in an aggregation. - * 0: no limit - * 1: no aggregation (one frame per aggregation) - * 2 - 0x3f: maximal number of frames (up to 3f == 63) - * @rs_table: array of rates for each TX try, each is rate_n_flags, - * meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP - * @ss_params: single stream features. declare whether STBC or BFER are allowed. - */ -struct iwl_lq_cmd { - u8 sta_id; - u8 reduced_tpc; - u16 control; - /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */ - u8 flags; - u8 mimo_delim; - u8 single_stream_ant_msk; - u8 dual_stream_ant_msk; - u8 initial_rate_index[AC_NUM]; - /* LINK_QUAL_AGG_PARAMS_API_S_VER_1 */ - __le16 agg_time_limit; - u8 agg_disable_start_th; - u8 agg_frame_cnt_limit; - __le32 reserved2; - __le32 rs_table[LQ_MAX_RETRY_NUM]; - __le32 ss_params; -}; /* LINK_QUALITY_CMD_API_S_VER_1 */ -#endif /* __fw_api_rs_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h deleted file mode 100644 index 9b7e49d4620f..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h +++ /dev/null @@ -1,238 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __fw_api_rx_h__ -#define __fw_api_rx_h__ - -#define IWL_RX_INFO_PHY_CNT 8 -#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1 -#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff -#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00 -#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000 -#define IWL_RX_INFO_ENERGY_ANT_A_POS 0 -#define IWL_RX_INFO_ENERGY_ANT_B_POS 8 -#define IWL_RX_INFO_ENERGY_ANT_C_POS 16 - -/** - * struct iwl_rx_phy_info - phy info - * (REPLY_RX_PHY_CMD = 0xc0) - * @non_cfg_phy_cnt: non configurable DSP phy data byte count - * @cfg_phy_cnt: configurable DSP phy data byte count - * @stat_id: configurable DSP phy data set ID - * @reserved1: - * @system_timestamp: GP2 at on air rise - * @timestamp: TSF at on air rise - * @beacon_time_stamp: beacon at on-air rise - * @phy_flags: general phy flags: band, modulation, ... - * @channel: channel number - * @non_cfg_phy_buf: for various implementations of non_cfg_phy - * @rate_n_flags: RATE_MCS_* - * @byte_count: frame's byte-count - * @frame_time: frame's time on the air, based on byte count and frame rate - * calculation - * @mac_active_msk: what MACs were active when the frame was received - * - * Before each Rx, the device sends this data. It contains PHY information - * about the reception of the packet. - */ -struct iwl_rx_phy_info { - u8 non_cfg_phy_cnt; - u8 cfg_phy_cnt; - u8 stat_id; - u8 reserved1; - __le32 system_timestamp; - __le64 timestamp; - __le32 beacon_time_stamp; - __le16 phy_flags; - __le16 channel; - __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT]; - __le32 rate_n_flags; - __le32 byte_count; - __le16 mac_active_msk; - __le16 frame_time; -} __packed; - -/* - * TCP offload Rx assist info - * - * bits 0:3 - reserved - * bits 4:7 - MIC CRC length - * bits 8:12 - MAC header length - * bit 13 - Padding indication - * bit 14 - A-AMSDU indication - * bit 15 - Offload enabled - */ -enum iwl_csum_rx_assist_info { - CSUM_RXA_RESERVED_MASK = 0x000f, - CSUM_RXA_MICSIZE_MASK = 0x00f0, - CSUM_RXA_HEADERLEN_MASK = 0x1f00, - CSUM_RXA_PADD = BIT(13), - CSUM_RXA_AMSDU = BIT(14), - CSUM_RXA_ENA = BIT(15) -}; - -/** - * struct iwl_rx_mpdu_res_start - phy info - * @assist: see CSUM_RX_ASSIST_ above - */ -struct iwl_rx_mpdu_res_start { - __le16 byte_count; - __le16 assist; -} __packed; /* _RX_MPDU_RES_START_API_S_VER_2 */ - -/** - * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags - * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band - * @RX_RES_PHY_FLAGS_MOD_CCK: - * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short - * @RX_RES_PHY_FLAGS_NARROW_BAND: - * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received - * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU - * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame - * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble - * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame - */ -enum iwl_rx_phy_flags { - RX_RES_PHY_FLAGS_BAND_24 = BIT(0), - RX_RES_PHY_FLAGS_MOD_CCK = BIT(1), - RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2), - RX_RES_PHY_FLAGS_NARROW_BAND = BIT(3), - RX_RES_PHY_FLAGS_ANTENNA = (0x7 << 4), - RX_RES_PHY_FLAGS_ANTENNA_POS = 4, - RX_RES_PHY_FLAGS_AGG = BIT(7), - RX_RES_PHY_FLAGS_OFDM_HT = BIT(8), - RX_RES_PHY_FLAGS_OFDM_GF = BIT(9), - RX_RES_PHY_FLAGS_OFDM_VHT = BIT(10), -}; - -/** - * enum iwl_mvm_rx_status - written by fw for each Rx packet - * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine - * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow - * @RX_MPDU_RES_STATUS_SRC_STA_FOUND: - * @RX_MPDU_RES_STATUS_KEY_VALID: - * @RX_MPDU_RES_STATUS_KEY_PARAM_OK: - * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed - * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked - * in the driver. - * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine - * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR: valid for alg = CCM_CMAC or - * alg = CCM only. Checks replay attack for 11w frames. Relevant only if - * %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set. - * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted - * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP - * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM - * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP - * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC - * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted - * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm - * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted - * @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP: - * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP: - * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT: - * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame - * @RX_MPDU_RES_STATUS_CSUM_DONE: checksum was done by the hw - * @RX_MPDU_RES_STATUS_CSUM_OK: checksum found no errors - * @RX_MPDU_RES_STATUS_HASH_INDEX_MSK: - * @RX_MPDU_RES_STATUS_STA_ID_MSK: - * @RX_MPDU_RES_STATUS_RRF_KILL: - * @RX_MPDU_RES_STATUS_FILTERING_MSK: - * @RX_MPDU_RES_STATUS2_FILTERING_MSK: - */ -enum iwl_mvm_rx_status { - RX_MPDU_RES_STATUS_CRC_OK = BIT(0), - RX_MPDU_RES_STATUS_OVERRUN_OK = BIT(1), - RX_MPDU_RES_STATUS_SRC_STA_FOUND = BIT(2), - RX_MPDU_RES_STATUS_KEY_VALID = BIT(3), - RX_MPDU_RES_STATUS_KEY_PARAM_OK = BIT(4), - RX_MPDU_RES_STATUS_ICV_OK = BIT(5), - RX_MPDU_RES_STATUS_MIC_OK = BIT(6), - RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), - RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR = BIT(7), - RX_MPDU_RES_STATUS_SEC_NO_ENC = (0 << 8), - RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8), - RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8), - RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8), - RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8), - RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8), - RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8), - RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8), - RX_MPDU_RES_STATUS_DEC_DONE = BIT(11), - RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP = BIT(12), - RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP = BIT(13), - RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT = BIT(14), - RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME = BIT(15), - RX_MPDU_RES_STATUS_CSUM_DONE = BIT(16), - RX_MPDU_RES_STATUS_CSUM_OK = BIT(17), - RX_MPDU_RES_STATUS_HASH_INDEX_MSK = (0x3F0000), - RX_MPDU_RES_STATUS_STA_ID_MSK = (0x1f000000), - RX_MPDU_RES_STATUS_RRF_KILL = BIT(29), - RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000), - RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000), -}; - -#endif /* __fw_api_rx_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h deleted file mode 100644 index 3a657e4b60ac..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ /dev/null @@ -1,730 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __fw_api_scan_h__ -#define __fw_api_scan_h__ - -#include "fw-api.h" - -/* Scan Commands, Responses, Notifications */ - -/* Max number of IEs for direct SSID scans in a command */ -#define PROBE_OPTION_MAX 20 - -/** - * struct iwl_ssid_ie - directed scan network information element - * - * Up to 20 of these may appear in REPLY_SCAN_CMD, - * selected by "type" bit field in struct iwl_scan_channel; - * each channel may select different ssids from among the 20 entries. - * SSID IEs get transmitted in reverse order of entry. - */ -struct iwl_ssid_ie { - u8 id; - u8 len; - u8 ssid[IEEE80211_MAX_SSID_LEN]; -} __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */ - -/* scan offload */ -#define IWL_SCAN_MAX_BLACKLIST_LEN 64 -#define IWL_SCAN_SHORT_BLACKLIST_LEN 16 -#define IWL_SCAN_MAX_PROFILES 11 -#define SCAN_OFFLOAD_PROBE_REQ_SIZE 512 - -/* Default watchdog (in MS) for scheduled scan iteration */ -#define IWL_SCHED_SCAN_WATCHDOG cpu_to_le16(15000) - -#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) -#define CAN_ABORT_STATUS 1 - -#define IWL_FULL_SCAN_MULTIPLIER 5 -#define IWL_FAST_SCHED_SCAN_ITERATIONS 3 -#define IWL_MAX_SCHED_SCAN_PLANS 2 - -enum scan_framework_client { - SCAN_CLIENT_SCHED_SCAN = BIT(0), - SCAN_CLIENT_NETDETECT = BIT(1), - SCAN_CLIENT_ASSET_TRACKING = BIT(2), -}; - -/** - * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S - * @ssid: MAC address to filter out - * @reported_rssi: AP rssi reported to the host - * @client_bitmap: clients ignore this entry - enum scan_framework_client - */ -struct iwl_scan_offload_blacklist { - u8 ssid[ETH_ALEN]; - u8 reported_rssi; - u8 client_bitmap; -} __packed; - -enum iwl_scan_offload_network_type { - IWL_NETWORK_TYPE_BSS = 1, - IWL_NETWORK_TYPE_IBSS = 2, - IWL_NETWORK_TYPE_ANY = 3, -}; - -enum iwl_scan_offload_band_selection { - IWL_SCAN_OFFLOAD_SELECT_2_4 = 0x4, - IWL_SCAN_OFFLOAD_SELECT_5_2 = 0x8, - IWL_SCAN_OFFLOAD_SELECT_ANY = 0xc, -}; - -/** - * iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S - * @ssid_index: index to ssid list in fixed part - * @unicast_cipher: encryption algorithm to match - bitmap - * @aut_alg: authentication algorithm to match - bitmap - * @network_type: enum iwl_scan_offload_network_type - * @band_selection: enum iwl_scan_offload_band_selection - * @client_bitmap: clients waiting for match - enum scan_framework_client - */ -struct iwl_scan_offload_profile { - u8 ssid_index; - u8 unicast_cipher; - u8 auth_alg; - u8 network_type; - u8 band_selection; - u8 client_bitmap; - u8 reserved[2]; -} __packed; - -/** - * iwl_scan_offload_profile_cfg - SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1 - * @blaclist: AP list to filter off from scan results - * @profiles: profiles to search for match - * @blacklist_len: length of blacklist - * @num_profiles: num of profiles in the list - * @match_notify: clients waiting for match found notification - * @pass_match: clients waiting for the results - * @active_clients: active clients bitmap - enum scan_framework_client - * @any_beacon_notify: clients waiting for match notification without match - */ -struct iwl_scan_offload_profile_cfg { - struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES]; - u8 blacklist_len; - u8 num_profiles; - u8 match_notify; - u8 pass_match; - u8 active_clients; - u8 any_beacon_notify; - u8 reserved[2]; -} __packed; - -/** - * iwl_scan_schedule_lmac - schedule of scan offload - * @delay: delay between iterations, in seconds. - * @iterations: num of scan iterations - * @full_scan_mul: number of partial scans before each full scan - */ -struct iwl_scan_schedule_lmac { - __le16 delay; - u8 iterations; - u8 full_scan_mul; -} __packed; /* SCAN_SCHEDULE_API_S */ - -enum iwl_scan_offload_complete_status { - IWL_SCAN_OFFLOAD_COMPLETED = 1, - IWL_SCAN_OFFLOAD_ABORTED = 2, -}; - -enum iwl_scan_ebs_status { - IWL_SCAN_EBS_SUCCESS, - IWL_SCAN_EBS_FAILED, - IWL_SCAN_EBS_CHAN_NOT_FOUND, - IWL_SCAN_EBS_INACTIVE, -}; - -/** - * iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S - * @tx_flags: combination of TX_CMD_FLG_* - * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is - * cleared. Combination of RATE_MCS_* - * @sta_id: index of destination station in FW station table - * @reserved: for alignment and future use - */ -struct iwl_scan_req_tx_cmd { - __le32 tx_flags; - __le32 rate_n_flags; - u8 sta_id; - u8 reserved[3]; -} __packed; - -enum iwl_scan_channel_flags_lmac { - IWL_UNIFIED_SCAN_CHANNEL_FULL = BIT(27), - IWL_UNIFIED_SCAN_CHANNEL_PARTIAL = BIT(28), -}; - -/** - * iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2 - * @flags: bits 1-20: directed scan to i'th ssid - * other bits &enum iwl_scan_channel_flags_lmac - * @channel_number: channel number 1-13 etc - * @iter_count: scan iteration on this channel - * @iter_interval: interval in seconds between iterations on one channel - */ -struct iwl_scan_channel_cfg_lmac { - __le32 flags; - __le16 channel_num; - __le16 iter_count; - __le32 iter_interval; -} __packed; - -/* - * iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1 - * @offset: offset in the data block - * @len: length of the segment - */ -struct iwl_scan_probe_segment { - __le16 offset; - __le16 len; -} __packed; - -/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2 - * @mac_header: first (and common) part of the probe - * @band_data: band specific data - * @common_data: last (and common) part of the probe - * @buf: raw data block - */ -struct iwl_scan_probe_req { - struct iwl_scan_probe_segment mac_header; - struct iwl_scan_probe_segment band_data[2]; - struct iwl_scan_probe_segment common_data; - u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE]; -} __packed; - -enum iwl_scan_channel_flags { - IWL_SCAN_CHANNEL_FLAG_EBS = BIT(0), - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE = BIT(1), - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD = BIT(2), -}; - -/* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S - * @flags: enum iwl_scan_channel_flags - * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is - * involved. - * 1 - EBS is disabled. - * 2 - every second scan will be full scan(and so on). - */ -struct iwl_scan_channel_opt { - __le16 flags; - __le16 non_ebs_ratio; -} __packed; - -/** - * iwl_mvm_lmac_scan_flags - * @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses - * without filtering. - * @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels - * @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan - * @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification - * @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching - * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented - * @IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report - * and DS parameter set IEs into probe requests. - * @IWL_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches - */ -enum iwl_mvm_lmac_scan_flags { - IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL = BIT(0), - IWL_MVM_LMAC_SCAN_FLAG_PASSIVE = BIT(1), - IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = BIT(2), - IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = BIT(3), - IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4), - IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5), - IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = BIT(6), - IWL_MVM_LMAC_SCAN_FLAG_MATCH = BIT(9), -}; - -enum iwl_scan_priority { - IWL_SCAN_PRIORITY_LOW, - IWL_SCAN_PRIORITY_MEDIUM, - IWL_SCAN_PRIORITY_HIGH, -}; - -enum iwl_scan_priority_ext { - IWL_SCAN_PRIORITY_EXT_0_LOWEST, - IWL_SCAN_PRIORITY_EXT_1, - IWL_SCAN_PRIORITY_EXT_2, - IWL_SCAN_PRIORITY_EXT_3, - IWL_SCAN_PRIORITY_EXT_4, - IWL_SCAN_PRIORITY_EXT_5, - IWL_SCAN_PRIORITY_EXT_6, - IWL_SCAN_PRIORITY_EXT_7_HIGHEST, -}; - -/** - * iwl_scan_req_lmac - SCAN_REQUEST_CMD_API_S_VER_1 - * @reserved1: for alignment and future use - * @channel_num: num of channels to scan - * @active-dwell: dwell time for active channels - * @passive-dwell: dwell time for passive channels - * @fragmented-dwell: dwell time for fragmented passive scan - * @reserved2: for alignment and future use - * @rx_chain_selct: PHY_RX_CHAIN_* flags - * @scan_flags: &enum iwl_mvm_lmac_scan_flags - * @max_out_time: max time (in TU) to be out of associated channel - * @suspend_time: pause scan this long (TUs) when returning to service channel - * @flags: RXON flags - * @filter_flags: RXON filter - * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz - * @direct_scan: list of SSIDs for directed active scan - * @scan_prio: enum iwl_scan_priority - * @iter_num: number of scan iterations - * @delay: delay in seconds before first iteration - * @schedule: two scheduling plans. The first one is finite, the second one can - * be infinite. - * @channel_opt: channel optimization options, for full and partial scan - * @data: channel configuration and probe request packet. - */ -struct iwl_scan_req_lmac { - /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */ - __le32 reserved1; - u8 n_channels; - u8 active_dwell; - u8 passive_dwell; - u8 fragmented_dwell; - __le16 reserved2; - __le16 rx_chain_select; - __le32 scan_flags; - __le32 max_out_time; - __le32 suspend_time; - /* RX_ON_FLAGS_API_S_VER_1 */ - __le32 flags; - __le32 filter_flags; - struct iwl_scan_req_tx_cmd tx_cmd[2]; - struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; - __le32 scan_prio; - /* SCAN_REQ_PERIODIC_PARAMS_API_S */ - __le32 iter_num; - __le32 delay; - struct iwl_scan_schedule_lmac schedule[IWL_MAX_SCHED_SCAN_PLANS]; - struct iwl_scan_channel_opt channel_opt[2]; - u8 data[]; -} __packed; - -/** - * struct iwl_scan_results_notif - scan results for one channel - - * SCAN_RESULT_NTF_API_S_VER_3 - * @channel: which channel the results are from - * @band: 0 for 5.2 GHz, 1 for 2.4 GHz - * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request - * @num_probe_not_sent: # of request that weren't sent due to not enough time - * @duration: duration spent in channel, in usecs - */ -struct iwl_scan_results_notif { - u8 channel; - u8 band; - u8 probe_status; - u8 num_probe_not_sent; - __le32 duration; -} __packed; - -/** - * struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels) - * SCAN_COMPLETE_NTF_API_S_VER_3 - * @scanned_channels: number of channels scanned (and number of valid results) - * @status: one of SCAN_COMP_STATUS_* - * @bt_status: BT on/off status - * @last_channel: last channel that was scanned - * @tsf_low: TSF timer (lower half) in usecs - * @tsf_high: TSF timer (higher half) in usecs - * @results: an array of scan results, only "scanned_channels" of them are valid - */ -struct iwl_lmac_scan_complete_notif { - u8 scanned_channels; - u8 status; - u8 bt_status; - u8 last_channel; - __le32 tsf_low; - __le32 tsf_high; - struct iwl_scan_results_notif results[]; -} __packed; - -/** - * iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2 - * @last_schedule_line: last schedule line executed (fast or regular) - * @last_schedule_iteration: last scan iteration executed before scan abort - * @status: enum iwl_scan_offload_complete_status - * @ebs_status: EBS success status &enum iwl_scan_ebs_status - * @time_after_last_iter; time in seconds elapsed after last iteration - */ -struct iwl_periodic_scan_complete { - u8 last_schedule_line; - u8 last_schedule_iteration; - u8 status; - u8 ebs_status; - __le32 time_after_last_iter; - __le32 reserved; -} __packed; - -/* UMAC Scan API */ - -/* The maximum of either of these cannot exceed 8, because we use an - * 8-bit mask (see IWL_MVM_SCAN_MASK in mvm.h). - */ -#define IWL_MVM_MAX_UMAC_SCANS 8 -#define IWL_MVM_MAX_LMAC_SCANS 1 - -enum scan_config_flags { - SCAN_CONFIG_FLAG_ACTIVATE = BIT(0), - SCAN_CONFIG_FLAG_DEACTIVATE = BIT(1), - SCAN_CONFIG_FLAG_FORBID_CHUB_REQS = BIT(2), - SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS = BIT(3), - SCAN_CONFIG_FLAG_SET_TX_CHAINS = BIT(8), - SCAN_CONFIG_FLAG_SET_RX_CHAINS = BIT(9), - SCAN_CONFIG_FLAG_SET_AUX_STA_ID = BIT(10), - SCAN_CONFIG_FLAG_SET_ALL_TIMES = BIT(11), - SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES = BIT(12), - SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS = BIT(13), - SCAN_CONFIG_FLAG_SET_LEGACY_RATES = BIT(14), - SCAN_CONFIG_FLAG_SET_MAC_ADDR = BIT(15), - SCAN_CONFIG_FLAG_SET_FRAGMENTED = BIT(16), - SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED = BIT(17), - SCAN_CONFIG_FLAG_SET_CAM_MODE = BIT(18), - SCAN_CONFIG_FLAG_CLEAR_CAM_MODE = BIT(19), - SCAN_CONFIG_FLAG_SET_PROMISC_MODE = BIT(20), - SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE = BIT(21), - - /* Bits 26-31 are for num of channels in channel_array */ -#define SCAN_CONFIG_N_CHANNELS(n) ((n) << 26) -}; - -enum scan_config_rates { - /* OFDM basic rates */ - SCAN_CONFIG_RATE_6M = BIT(0), - SCAN_CONFIG_RATE_9M = BIT(1), - SCAN_CONFIG_RATE_12M = BIT(2), - SCAN_CONFIG_RATE_18M = BIT(3), - SCAN_CONFIG_RATE_24M = BIT(4), - SCAN_CONFIG_RATE_36M = BIT(5), - SCAN_CONFIG_RATE_48M = BIT(6), - SCAN_CONFIG_RATE_54M = BIT(7), - /* CCK basic rates */ - SCAN_CONFIG_RATE_1M = BIT(8), - SCAN_CONFIG_RATE_2M = BIT(9), - SCAN_CONFIG_RATE_5M = BIT(10), - SCAN_CONFIG_RATE_11M = BIT(11), - - /* Bits 16-27 are for supported rates */ -#define SCAN_CONFIG_SUPPORTED_RATE(rate) ((rate) << 16) -}; - -enum iwl_channel_flags { - IWL_CHANNEL_FLAG_EBS = BIT(0), - IWL_CHANNEL_FLAG_ACCURATE_EBS = BIT(1), - IWL_CHANNEL_FLAG_EBS_ADD = BIT(2), - IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE = BIT(3), -}; - -/** - * struct iwl_scan_config - * @flags: enum scan_config_flags - * @tx_chains: valid_tx antenna - ANT_* definitions - * @rx_chains: valid_rx antenna - ANT_* definitions - * @legacy_rates: default legacy rates - enum scan_config_rates - * @out_of_channel_time: default max out of serving channel time - * @suspend_time: default max suspend time - * @dwell_active: default dwell time for active scan - * @dwell_passive: default dwell time for passive scan - * @dwell_fragmented: default dwell time for fragmented scan - * @reserved: for future use and alignment - * @mac_addr: default mac address to be used in probes - * @bcast_sta_id: the index of the station in the fw - * @channel_flags: default channel flags - enum iwl_channel_flags - * scan_config_channel_flag - * @channel_array: default supported channels - */ -struct iwl_scan_config { - __le32 flags; - __le32 tx_chains; - __le32 rx_chains; - __le32 legacy_rates; - __le32 out_of_channel_time; - __le32 suspend_time; - u8 dwell_active; - u8 dwell_passive; - u8 dwell_fragmented; - u8 reserved; - u8 mac_addr[ETH_ALEN]; - u8 bcast_sta_id; - u8 channel_flags; - u8 channel_array[]; -} __packed; /* SCAN_CONFIG_DB_CMD_API_S */ - -/** - * iwl_umac_scan_flags - *@IWL_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request - * can be preempted by other scan requests with higher priority. - * The low priority scan will be resumed when the higher proirity scan is - * completed. - *@IWL_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver - * when scan starts. - */ -enum iwl_umac_scan_flags { - IWL_UMAC_SCAN_FLAG_PREEMPTIVE = BIT(0), - IWL_UMAC_SCAN_FLAG_START_NOTIF = BIT(1), -}; - -enum iwl_umac_scan_uid_offsets { - IWL_UMAC_SCAN_UID_TYPE_OFFSET = 0, - IWL_UMAC_SCAN_UID_SEQ_OFFSET = 8, -}; - -enum iwl_umac_scan_general_flags { - IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC = BIT(0), - IWL_UMAC_SCAN_GEN_FLAGS_OVER_BT = BIT(1), - IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL = BIT(2), - IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE = BIT(3), - IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT = BIT(4), - IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE = BIT(5), - IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = BIT(6), - IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = BIT(7), - IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = BIT(8), - IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9) -}; - -/** - * struct iwl_scan_channel_cfg_umac - * @flags: bitmap - 0-19: directed scan to i'th ssid. - * @channel_num: channel number 1-13 etc. - * @iter_count: repetition count for the channel. - * @iter_interval: interval between two scan iterations on one channel. - */ -struct iwl_scan_channel_cfg_umac { - __le32 flags; - u8 channel_num; - u8 iter_count; - __le16 iter_interval; -} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */ - -/** - * struct iwl_scan_umac_schedule - * @interval: interval in seconds between scan iterations - * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop - * @reserved: for alignment and future use - */ -struct iwl_scan_umac_schedule { - __le16 interval; - u8 iter_count; - u8 reserved; -} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */ - -/** - * struct iwl_scan_req_umac_tail - the rest of the UMAC scan request command - * parameters following channels configuration array. - * @schedule: two scheduling plans. - * @delay: delay in TUs before starting the first scan iteration - * @reserved: for future use and alignment - * @preq: probe request with IEs blocks - * @direct_scan: list of SSIDs for directed active scan - */ -struct iwl_scan_req_umac_tail { - /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */ - struct iwl_scan_umac_schedule schedule[IWL_MAX_SCHED_SCAN_PLANS]; - __le16 delay; - __le16 reserved; - /* SCAN_PROBE_PARAMS_API_S_VER_1 */ - struct iwl_scan_probe_req preq; - struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; -} __packed; - -/** - * struct iwl_scan_req_umac - * @flags: &enum iwl_umac_scan_flags - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @ooc_priority: out of channel priority - &enum iwl_scan_priority - * @general_flags: &enum iwl_umac_scan_general_flags - * @reserved1: for future use and alignment - * @active_dwell: dwell time for active scan - * @passive_dwell: dwell time for passive scan - * @fragmented_dwell: dwell time for fragmented passive scan - * @max_out_time: max out of serving channel time - * @suspend_time: max suspend time - * @scan_priority: scan internal prioritization &enum iwl_scan_priority - * @channel_flags: &enum iwl_scan_channel_flags - * @n_channels: num of channels in scan request - * @reserved2: for future use and alignment - * @data: &struct iwl_scan_channel_cfg_umac and - * &struct iwl_scan_req_umac_tail - */ -struct iwl_scan_req_umac { - __le32 flags; - __le32 uid; - __le32 ooc_priority; - /* SCAN_GENERAL_PARAMS_API_S_VER_1 */ - __le32 general_flags; - u8 reserved1; - u8 active_dwell; - u8 passive_dwell; - u8 fragmented_dwell; - __le32 max_out_time; - __le32 suspend_time; - __le32 scan_priority; - /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */ - u8 channel_flags; - u8 n_channels; - __le16 reserved2; - u8 data[]; -} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ - -/** - * struct iwl_umac_scan_abort - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @flags: reserved - */ -struct iwl_umac_scan_abort { - __le32 uid; - __le32 flags; -} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */ - -/** - * struct iwl_umac_scan_complete - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @last_schedule: last scheduling line - * @last_iter: last scan iteration number - * @scan status: &enum iwl_scan_offload_complete_status - * @ebs_status: &enum iwl_scan_ebs_status - * @time_from_last_iter: time elapsed from last iteration - * @reserved: for future use - */ -struct iwl_umac_scan_complete { - __le32 uid; - u8 last_schedule; - u8 last_iter; - u8 status; - u8 ebs_status; - __le32 time_from_last_iter; - __le32 reserved; -} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */ - -#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5 -/** - * struct iwl_scan_offload_profile_match - match information - * @bssid: matched bssid - * @channel: channel where the match occurred - * @energy: - * @matching_feature: - * @matching_channels: bitmap of channels that matched, referencing - * the channels passed in tue scan offload request - */ -struct iwl_scan_offload_profile_match { - u8 bssid[ETH_ALEN]; - __le16 reserved; - u8 channel; - u8 energy; - u8 matching_feature; - u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN]; -} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */ - -/** - * struct iwl_scan_offload_profiles_query - match results query response - * @matched_profiles: bitmap of matched profiles, referencing the - * matches passed in the scan offload request - * @last_scan_age: age of the last offloaded scan - * @n_scans_done: number of offloaded scans done - * @gp2_d0u: GP2 when D0U occurred - * @gp2_invoked: GP2 when scan offload was invoked - * @resume_while_scanning: not used - * @self_recovery: obsolete - * @reserved: reserved - * @matches: array of match information, one for each match - */ -struct iwl_scan_offload_profiles_query { - __le32 matched_profiles; - __le32 last_scan_age; - __le32 n_scans_done; - __le32 gp2_d0u; - __le32 gp2_invoked; - u8 resume_while_scanning; - u8 self_recovery; - __le16 reserved; - struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES]; -} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */ - -/** - * struct iwl_umac_scan_iter_complete_notif - notifies end of scanning iteration - * @uid: scan id, &enum iwl_umac_scan_uid_offsets - * @scanned_channels: number of channels scanned and number of valid elements in - * results array - * @status: one of SCAN_COMP_STATUS_* - * @bt_status: BT on/off status - * @last_channel: last channel that was scanned - * @tsf_low: TSF timer (lower half) in usecs - * @tsf_high: TSF timer (higher half) in usecs - * @results: array of scan results, only "scanned_channels" of them are valid - */ -struct iwl_umac_scan_iter_complete_notif { - __le32 uid; - u8 scanned_channels; - u8 status; - u8 bt_status; - u8 last_channel; - __le32 tsf_low; - __le32 tsf_high; - struct iwl_scan_results_notif results[]; -} __packed; /* SCAN_ITER_COMPLETE_NTF_UMAC_API_S_VER_1 */ - -#endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h deleted file mode 100644 index 493a8bdfbc9e..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ /dev/null @@ -1,414 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_sta_h__ -#define __fw_api_sta_h__ - -/** - * enum iwl_sta_flags - flags for the ADD_STA host command - * @STA_FLG_REDUCED_TX_PWR_CTRL: - * @STA_FLG_REDUCED_TX_PWR_DATA: - * @STA_FLG_DISABLE_TX: set if TX should be disabled - * @STA_FLG_PS: set if STA is in Power Save - * @STA_FLG_INVALID: set if STA is invalid - * @STA_FLG_DLP_EN: Direct Link Protocol is enabled - * @STA_FLG_SET_ALL_KEYS: the current key applies to all key IDs - * @STA_FLG_DRAIN_FLOW: drain flow - * @STA_FLG_PAN: STA is for PAN interface - * @STA_FLG_CLASS_AUTH: - * @STA_FLG_CLASS_ASSOC: - * @STA_FLG_CLASS_MIMO_PROT: - * @STA_FLG_MAX_AGG_SIZE_MSK: maximal size for A-MPDU - * @STA_FLG_AGG_MPDU_DENS_MSK: maximal MPDU density for Tx aggregation - * @STA_FLG_FAT_EN_MSK: support for channel width (for Tx). This flag is - * initialised by driver and can be updated by fw upon reception of - * action frames that can change the channel width. When cleared the fw - * will send all the frames in 20MHz even when FAT channel is requested. - * @STA_FLG_MIMO_EN_MSK: support for MIMO. This flag is initialised by the - * driver and can be updated by fw upon reception of action frames. - * @STA_FLG_MFP_EN: Management Frame Protection - */ -enum iwl_sta_flags { - STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3), - STA_FLG_REDUCED_TX_PWR_DATA = BIT(6), - - STA_FLG_DISABLE_TX = BIT(4), - - STA_FLG_PS = BIT(8), - STA_FLG_DRAIN_FLOW = BIT(12), - STA_FLG_PAN = BIT(13), - STA_FLG_CLASS_AUTH = BIT(14), - STA_FLG_CLASS_ASSOC = BIT(15), - STA_FLG_RTS_MIMO_PROT = BIT(17), - - STA_FLG_MAX_AGG_SIZE_SHIFT = 19, - STA_FLG_MAX_AGG_SIZE_8K = (0 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_16K = (1 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_32K = (2 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_64K = (3 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_128K = (4 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_256K = (5 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_512K = (6 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_1024K = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), - STA_FLG_MAX_AGG_SIZE_MSK = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT), - - STA_FLG_AGG_MPDU_DENS_SHIFT = 23, - STA_FLG_AGG_MPDU_DENS_2US = (4 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_4US = (5 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_8US = (6 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_16US = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT), - STA_FLG_AGG_MPDU_DENS_MSK = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT), - - STA_FLG_FAT_EN_20MHZ = (0 << 26), - STA_FLG_FAT_EN_40MHZ = (1 << 26), - STA_FLG_FAT_EN_80MHZ = (2 << 26), - STA_FLG_FAT_EN_160MHZ = (3 << 26), - STA_FLG_FAT_EN_MSK = (3 << 26), - - STA_FLG_MIMO_EN_SISO = (0 << 28), - STA_FLG_MIMO_EN_MIMO2 = (1 << 28), - STA_FLG_MIMO_EN_MIMO3 = (2 << 28), - STA_FLG_MIMO_EN_MSK = (3 << 28), -}; - -/** - * enum iwl_sta_key_flag - key flags for the ADD_STA host command - * @STA_KEY_FLG_NO_ENC: no encryption - * @STA_KEY_FLG_WEP: WEP encryption algorithm - * @STA_KEY_FLG_CCM: CCMP encryption algorithm - * @STA_KEY_FLG_TKIP: TKIP encryption algorithm - * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support) - * @STA_KEY_FLG_CMAC: CMAC encryption algorithm - * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm - * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value - * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from - * station info array (1 - n 1X mode) - * @STA_KEY_FLG_KEYID_MSK: the index of the key - * @STA_KEY_NOT_VALID: key is invalid - * @STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key - * @STA_KEY_MULTICAST: set for multical key - * @STA_KEY_MFP: key is used for Management Frame Protection - */ -enum iwl_sta_key_flag { - STA_KEY_FLG_NO_ENC = (0 << 0), - STA_KEY_FLG_WEP = (1 << 0), - STA_KEY_FLG_CCM = (2 << 0), - STA_KEY_FLG_TKIP = (3 << 0), - STA_KEY_FLG_EXT = (4 << 0), - STA_KEY_FLG_CMAC = (6 << 0), - STA_KEY_FLG_ENC_UNKNOWN = (7 << 0), - STA_KEY_FLG_EN_MSK = (7 << 0), - - STA_KEY_FLG_WEP_KEY_MAP = BIT(3), - STA_KEY_FLG_KEYID_POS = 8, - STA_KEY_FLG_KEYID_MSK = (3 << STA_KEY_FLG_KEYID_POS), - STA_KEY_NOT_VALID = BIT(11), - STA_KEY_FLG_WEP_13BYTES = BIT(12), - STA_KEY_MULTICAST = BIT(14), - STA_KEY_MFP = BIT(15), -}; - -/** - * enum iwl_sta_modify_flag - indicate to the fw what flag are being changed - * @STA_MODIFY_KEY: this command modifies %key - * @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx - * @STA_MODIFY_TX_RATE: unused - * @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid - * @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid - * @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count - * @STA_MODIFY_PROT_TH: - * @STA_MODIFY_QUEUES: modify the queues used by this station - */ -enum iwl_sta_modify_flag { - STA_MODIFY_KEY = BIT(0), - STA_MODIFY_TID_DISABLE_TX = BIT(1), - STA_MODIFY_TX_RATE = BIT(2), - STA_MODIFY_ADD_BA_TID = BIT(3), - STA_MODIFY_REMOVE_BA_TID = BIT(4), - STA_MODIFY_SLEEPING_STA_TX_COUNT = BIT(5), - STA_MODIFY_PROT_TH = BIT(6), - STA_MODIFY_QUEUES = BIT(7), -}; - -#define STA_MODE_MODIFY 1 - -/** - * enum iwl_sta_sleep_flag - type of sleep of the station - * @STA_SLEEP_STATE_AWAKE: - * @STA_SLEEP_STATE_PS_POLL: - * @STA_SLEEP_STATE_UAPSD: - * @STA_SLEEP_STATE_MOREDATA: set more-data bit on - * (last) released frame - */ -enum iwl_sta_sleep_flag { - STA_SLEEP_STATE_AWAKE = 0, - STA_SLEEP_STATE_PS_POLL = BIT(0), - STA_SLEEP_STATE_UAPSD = BIT(1), - STA_SLEEP_STATE_MOREDATA = BIT(2), -}; - -/* STA ID and color bits definitions */ -#define STA_ID_SEED (0x0f) -#define STA_ID_POS (0) -#define STA_ID_MSK (STA_ID_SEED << STA_ID_POS) - -#define STA_COLOR_SEED (0x7) -#define STA_COLOR_POS (4) -#define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS) - -#define STA_ID_N_COLOR_GET_COLOR(id_n_color) \ - (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS) -#define STA_ID_N_COLOR_GET_ID(id_n_color) \ - (((id_n_color) & STA_ID_MSK) >> STA_ID_POS) - -#define STA_KEY_MAX_NUM (16) -#define STA_KEY_IDX_INVALID (0xff) -#define STA_KEY_MAX_DATA_KEY_NUM (4) -#define IWL_MAX_GLOBAL_KEYS (4) -#define STA_KEY_LEN_WEP40 (5) -#define STA_KEY_LEN_WEP104 (13) - -/** - * struct iwl_mvm_keyinfo - key information - * @key_flags: type %iwl_sta_key_flag - * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection - * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx - * @key_offset: key offset in the fw's key table - * @key: 16-byte unicast decryption key - * @tx_secur_seq_cnt: initial RSC / PN needed for replay check - * @hw_tkip_mic_rx_key: byte: MIC Rx Key - used for TKIP only - * @hw_tkip_mic_tx_key: byte: MIC Tx Key - used for TKIP only - */ -struct iwl_mvm_keyinfo { - __le16 key_flags; - u8 tkip_rx_tsc_byte2; - u8 reserved1; - __le16 tkip_rx_ttak[5]; - u8 key_offset; - u8 reserved2; - u8 key[16]; - __le64 tx_secur_seq_cnt; - __le64 hw_tkip_mic_rx_key; - __le64 hw_tkip_mic_tx_key; -} __packed; - -/** - * struct iwl_mvm_add_sta_cmd - Add/modify a station in the fw's sta table. - * ( REPLY_ADD_STA = 0x18 ) - * @add_modify: 1: modify existing, 0: add new station - * @awake_acs: - * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable - * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field. - * @mac_id_n_color: the Mac context this station belongs to - * @addr[ETH_ALEN]: station's MAC address - * @sta_id: index of station in uCode's station table - * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave - * alone. 1 - modify, 0 - don't change. - * @station_flags: look at %iwl_sta_flags - * @station_flags_msk: what of %station_flags have changed - * @add_immediate_ba_tid: tid for which to add block-ack support (Rx) - * Set %STA_MODIFY_ADD_BA_TID to use this field, and also set - * add_immediate_ba_ssn. - * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx) - * Set %STA_MODIFY_REMOVE_BA_TID to use this field - * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with - * add_immediate_ba_tid. - * @sleep_tx_count: number of packets to transmit to station even though it is - * asleep. Used to synchronise PS-poll and u-APSD responses while ucode - * keeps track of STA sleep state. - * @sleep_state_flags: Look at %iwl_sta_sleep_flag. - * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP - * mac-addr. - * @beamform_flags: beam forming controls - * @tfd_queue_msk: tfd queues used by this station - * - * The device contains an internal table of per-station information, with info - * on security keys, aggregation parameters, and Tx rates for initial Tx - * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD). - * - * ADD_STA sets up the table entry for one station, either creating a new - * entry, or modifying a pre-existing one. - */ -struct iwl_mvm_add_sta_cmd { - u8 add_modify; - u8 awake_acs; - __le16 tid_disable_tx; - __le32 mac_id_n_color; - u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ - __le16 reserved2; - u8 sta_id; - u8 modify_mask; - __le16 reserved3; - __le32 station_flags; - __le32 station_flags_msk; - u8 add_immediate_ba_tid; - u8 remove_immediate_ba_tid; - __le16 add_immediate_ba_ssn; - __le16 sleep_tx_count; - __le16 sleep_state_flags; - __le16 assoc_id; - __le16 beamform_flags; - __le32 tfd_queue_msk; -} __packed; /* ADD_STA_CMD_API_S_VER_7 */ - -/** - * struct iwl_mvm_add_sta_key_cmd - add/modify sta key - * ( REPLY_ADD_STA_KEY = 0x17 ) - * @sta_id: index of station in uCode's station table - * @key_offset: key offset in key storage - * @key_flags: type %iwl_sta_key_flag - * @key: key material data - * @key2: key material data - * @rx_secur_seq_cnt: RX security sequence counter for the key - * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection - * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx - */ -struct iwl_mvm_add_sta_key_cmd { - u8 sta_id; - u8 key_offset; - __le16 key_flags; - u8 key[16]; - u8 key2[16]; - u8 rx_secur_seq_cnt[16]; - u8 tkip_rx_tsc_byte2; - u8 reserved; - __le16 tkip_rx_ttak[5]; -} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */ - -/** - * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command - * @ADD_STA_SUCCESS: operation was executed successfully - * @ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table - * @ADD_STA_IMMEDIATE_BA_FAILURE: can't add Rx block ack session - * @ADD_STA_MODIFY_NON_EXISTING_STA: driver requested to modify a station that - * doesn't exist. - */ -enum iwl_mvm_add_sta_rsp_status { - ADD_STA_SUCCESS = 0x1, - ADD_STA_STATIONS_OVERLOAD = 0x2, - ADD_STA_IMMEDIATE_BA_FAILURE = 0x4, - ADD_STA_MODIFY_NON_EXISTING_STA = 0x8, -}; - -/** - * struct iwl_mvm_rm_sta_cmd - Add / modify a station in the fw's station table - * ( REMOVE_STA = 0x19 ) - * @sta_id: the station id of the station to be removed - */ -struct iwl_mvm_rm_sta_cmd { - u8 sta_id; - u8 reserved[3]; -} __packed; /* REMOVE_STA_CMD_API_S_VER_2 */ - -/** - * struct iwl_mvm_mgmt_mcast_key_cmd - * ( MGMT_MCAST_KEY = 0x1f ) - * @ctrl_flags: %iwl_sta_key_flag - * @IGTK: - * @K1: unused - * @K2: unused - * @sta_id: station ID that support IGTK - * @key_id: - * @receive_seq_cnt: initial RSC/PN needed for replay check - */ -struct iwl_mvm_mgmt_mcast_key_cmd { - __le32 ctrl_flags; - u8 IGTK[16]; - u8 K1[16]; - u8 K2[16]; - __le32 key_id; - __le32 sta_id; - __le64 receive_seq_cnt; -} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */ - -struct iwl_mvm_wep_key { - u8 key_index; - u8 key_offset; - __le16 reserved1; - u8 key_size; - u8 reserved2[3]; - u8 key[16]; -} __packed; - -struct iwl_mvm_wep_key_cmd { - __le32 mac_id_n_color; - u8 num_keys; - u8 decryption_type; - u8 flags; - u8 reserved; - struct iwl_mvm_wep_key wep_key[0]; -} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */ - -/** - * struct iwl_mvm_eosp_notification - EOSP notification from firmware - * @remain_frame_count: # of frames remaining, non-zero if SP was cut - * short by GO absence - * @sta_id: station ID - */ -struct iwl_mvm_eosp_notification { - __le32 remain_frame_count; - __le32 sta_id; -} __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */ - -#endif /* __fw_api_sta_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h deleted file mode 100644 index 0c321f63ee42..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h +++ /dev/null @@ -1,284 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __fw_api_stats_h__ -#define __fw_api_stats_h__ -#include "fw-api-mac.h" - -struct mvm_statistics_dbg { - __le32 burst_check; - __le32 burst_count; - __le32 wait_for_silence_timeout_cnt; - __le32 reserved[3]; -} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */ - -struct mvm_statistics_div { - __le32 tx_on_a; - __le32 tx_on_b; - __le32 exec_time; - __le32 probe_time; - __le32 rssi_ant; - __le32 reserved2; -} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */ - -struct mvm_statistics_rx_non_phy { - __le32 bogus_cts; /* CTS received when not expecting CTS */ - __le32 bogus_ack; /* ACK received when not expecting ACK */ - __le32 non_bssid_frames; /* number of frames with BSSID that - * doesn't belong to the STA BSSID */ - __le32 filtered_frames; /* count frames that were dumped in the - * filtering process */ - __le32 non_channel_beacons; /* beacons with our bss id but not on - * our serving channel */ - __le32 channel_beacons; /* beacons with our bss id and in our - * serving channel */ - __le32 num_missed_bcon; /* number of missed beacons */ - __le32 adc_rx_saturation_time; /* count in 0.8us units the time the - * ADC was in saturation */ - __le32 ina_detection_search_time;/* total time (in 0.8us) searched - * for INA */ - __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ - __le32 interference_data_flag; /* flag for interference data - * availability. 1 when data is - * available. */ - __le32 channel_load; /* counts RX Enable time in uSec */ - __le32 dsp_false_alarms; /* DSP false alarm (both OFDM - * and CCK) counter */ - __le32 beacon_rssi_a; - __le32 beacon_rssi_b; - __le32 beacon_rssi_c; - __le32 beacon_energy_a; - __le32 beacon_energy_b; - __le32 beacon_energy_c; - __le32 num_bt_kills; - __le32 mac_id; - __le32 directed_data_mpdu; -} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */ - -struct mvm_statistics_rx_phy { - __le32 ina_cnt; - __le32 fina_cnt; - __le32 plcp_err; - __le32 crc32_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 false_alarm_cnt; - __le32 fina_sync_err_cnt; - __le32 sfd_timeout; - __le32 fina_timeout; - __le32 unresponded_rts; - __le32 rxe_frame_lmt_overrun; - __le32 sent_ack_cnt; - __le32 sent_cts_cnt; - __le32 sent_ba_rsp_cnt; - __le32 dsp_self_kill; - __le32 mh_format_err; - __le32 re_acq_main_rssi_sum; - __le32 reserved; -} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */ - -struct mvm_statistics_rx_ht_phy { - __le32 plcp_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 crc32_err; - __le32 mh_format_err; - __le32 agg_crc32_good; - __le32 agg_mpdu_cnt; - __le32 agg_cnt; - __le32 unsupport_mcs; -} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */ - -struct mvm_statistics_tx_non_phy { - __le32 preamble_cnt; - __le32 rx_detected_cnt; - __le32 bt_prio_defer_cnt; - __le32 bt_prio_kill_cnt; - __le32 few_bytes_cnt; - __le32 cts_timeout; - __le32 ack_timeout; - __le32 expected_ack_cnt; - __le32 actual_ack_cnt; - __le32 dump_msdu_cnt; - __le32 burst_abort_next_frame_mismatch_cnt; - __le32 burst_abort_missing_next_frame_cnt; - __le32 cts_timeout_collision; - __le32 ack_or_ba_timeout_collision; -} __packed; /* STATISTICS_TX_NON_PHY_API_S_VER_3 */ - -#define MAX_CHAINS 3 - -struct mvm_statistics_tx_non_phy_agg { - __le32 ba_timeout; - __le32 ba_reschedule_frames; - __le32 scd_query_agg_frame_cnt; - __le32 scd_query_no_agg; - __le32 scd_query_agg; - __le32 scd_query_mismatch; - __le32 frame_not_ready; - __le32 underrun; - __le32 bt_prio_kill; - __le32 rx_ba_rsp_cnt; - __s8 txpower[MAX_CHAINS]; - __s8 reserved; - __le32 reserved2; -} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */ - -struct mvm_statistics_tx_channel_width { - __le32 ext_cca_narrow_ch20[1]; - __le32 ext_cca_narrow_ch40[2]; - __le32 ext_cca_narrow_ch80[3]; - __le32 ext_cca_narrow_ch160[4]; - __le32 last_tx_ch_width_indx; - __le32 rx_detected_per_ch_width[4]; - __le32 success_per_ch_width[4]; - __le32 fail_per_ch_width[4]; -}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */ - -struct mvm_statistics_tx { - struct mvm_statistics_tx_non_phy general; - struct mvm_statistics_tx_non_phy_agg agg; - struct mvm_statistics_tx_channel_width channel_width; -} __packed; /* STATISTICS_TX_API_S_VER_4 */ - - -struct mvm_statistics_bt_activity { - __le32 hi_priority_tx_req_cnt; - __le32 hi_priority_tx_denied_cnt; - __le32 lo_priority_tx_req_cnt; - __le32 lo_priority_tx_denied_cnt; - __le32 hi_priority_rx_req_cnt; - __le32 hi_priority_rx_denied_cnt; - __le32 lo_priority_rx_req_cnt; - __le32 lo_priority_rx_denied_cnt; -} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ - -struct mvm_statistics_general_v8 { - __le32 radio_temperature; - __le32 radio_voltage; - struct mvm_statistics_dbg dbg; - __le32 sleep_time; - __le32 slots_out; - __le32 slots_idle; - __le32 ttl_timestamp; - struct mvm_statistics_div slow_div; - __le32 rx_enable_counter; - /* - * num_of_sos_states: - * count the number of times we have to re-tune - * in order to get out of bad PHY status - */ - __le32 num_of_sos_states; - __le32 beacon_filtered; - __le32 missed_beacons; - u8 beacon_filter_average_energy; - u8 beacon_filter_reason; - u8 beacon_filter_current_energy; - u8 beacon_filter_reserved; - __le32 beacon_filter_delta_time; - struct mvm_statistics_bt_activity bt_activity; - __le64 rx_time; - __le64 on_time_rf; - __le64 on_time_scan; - __le64 tx_time; - __le32 beacon_counter[NUM_MAC_INDEX]; - u8 beacon_average_energy[NUM_MAC_INDEX]; - u8 reserved[4 - (NUM_MAC_INDEX % 4)]; -} __packed; /* STATISTICS_GENERAL_API_S_VER_8 */ - -struct mvm_statistics_rx { - struct mvm_statistics_rx_phy ofdm; - struct mvm_statistics_rx_phy cck; - struct mvm_statistics_rx_non_phy general; - struct mvm_statistics_rx_ht_phy ofdm_ht; -} __packed; /* STATISTICS_RX_API_S_VER_3 */ - -/* - * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) - * - * By default, uCode issues this notification after receiving a beacon - * while associated. To disable this behavior, set DISABLE_NOTIF flag in the - * STATISTICS_CMD (0x9c), below. - */ - -struct iwl_notif_statistics_v10 { - __le32 flag; - struct mvm_statistics_rx rx; - struct mvm_statistics_tx tx; - struct mvm_statistics_general_v8 general; -} __packed; /* STATISTICS_NTFY_API_S_VER_10 */ - -#define IWL_STATISTICS_FLG_CLEAR 0x1 -#define IWL_STATISTICS_FLG_DISABLE_NOTIF 0x2 - -struct iwl_statistics_cmd { - __le32 flags; -} __packed; /* STATISTICS_CMD_API_S_VER_1 */ - -#endif /* __fw_api_stats_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h deleted file mode 100644 index eed6271d01a3..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h +++ /dev/null @@ -1,386 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2015 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __fw_api_tof_h__ -#define __fw_api_tof_h__ - -#include "fw-api.h" - -/* ToF sub-group command IDs */ -enum iwl_mvm_tof_sub_grp_ids { - TOF_RANGE_REQ_CMD = 0x1, - TOF_CONFIG_CMD = 0x2, - TOF_RANGE_ABORT_CMD = 0x3, - TOF_RANGE_REQ_EXT_CMD = 0x4, - TOF_RESPONDER_CONFIG_CMD = 0x5, - TOF_NW_INITIATED_RES_SEND_CMD = 0x6, - TOF_NEIGHBOR_REPORT_REQ_CMD = 0x7, - TOF_NEIGHBOR_REPORT_RSP_NOTIF = 0xFC, - TOF_NW_INITIATED_REQ_RCVD_NOTIF = 0xFD, - TOF_RANGE_RESPONSE_NOTIF = 0xFE, - TOF_MCSI_DEBUG_NOTIF = 0xFB, -}; - -/** - * struct iwl_tof_config_cmd - ToF configuration - * @tof_disabled: 0 enabled, 1 - disabled - * @one_sided_disabled: 0 enabled, 1 - disabled - * @is_debug_mode: 1 debug mode, 0 - otherwise - * @is_buf_required: 1 channel estimation buffer required, 0 - otherwise - */ -struct iwl_tof_config_cmd { - __le32 sub_grp_cmd_id; - u8 tof_disabled; - u8 one_sided_disabled; - u8 is_debug_mode; - u8 is_buf_required; -} __packed; - -/** - * struct iwl_tof_responder_config_cmd - ToF AP mode (for debug) - * @burst_period: future use: (currently hard coded in the LMAC) - * The interval between two sequential bursts. - * @min_delta_ftm: future use: (currently hard coded in the LMAC) - * The minimum delay between two sequential FTM Responses - * in the same burst. - * @burst_duration: future use: (currently hard coded in the LMAC) - * The total time for all FTMs handshake in the same burst. - * Affect the time events duration in the LMAC. - * @num_of_burst_exp: future use: (currently hard coded in the LMAC) - * The number of bursts for the current ToF request. Affect - * the number of events allocations in the current iteration. - * @get_ch_est: for xVT only, NA for driver - * @abort_responder: when set to '1' - Responder will terminate its activity - * (all other fields in the command are ignored) - * @recv_sta_req_params: 1 - Responder will ignore the other Responder's - * params and use the recomended Initiator params. - * 0 - otherwise - * @channel_num: current AP Channel - * @bandwidth: current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz - * @rate: current AP rate - * @ctrl_ch_position: coding of the control channel position relative to - * the center frequency. - * 40MHz 0 below center, 1 above center - * 80MHz bits [0..1]: 0 the near 20MHz to the center, - * 1 the far 20MHz to the center - * bit[2] as above 40MHz - * @ftm_per_burst: FTMs per Burst - * @ftm_resp_ts_avail: '0' - we don't measure over the Initial FTM Response, - * '1' - we measure over the Initial FTM Response - * @asap_mode: ASAP / Non ASAP mode for the current WLS station - * @sta_id: index of the AP STA when in AP mode - * @tsf_timer_offset_msecs: The dictated time offset (mSec) from the AP's TSF - * @toa_offset: Artificial addition [0.1nsec] for the ToA - to be used for debug - * purposes, simulating station movement by adding various values - * to this field - * @bssid: Current AP BSSID - */ -struct iwl_tof_responder_config_cmd { - __le32 sub_grp_cmd_id; - __le16 burst_period; - u8 min_delta_ftm; - u8 burst_duration; - u8 num_of_burst_exp; - u8 get_ch_est; - u8 abort_responder; - u8 recv_sta_req_params; - u8 channel_num; - u8 bandwidth; - u8 rate; - u8 ctrl_ch_position; - u8 ftm_per_burst; - u8 ftm_resp_ts_avail; - u8 asap_mode; - u8 sta_id; - __le16 tsf_timer_offset_msecs; - __le16 toa_offset; - u8 bssid[ETH_ALEN]; -} __packed; - -/** - * struct iwl_tof_range_request_ext_cmd - extended range req for WLS - * @tsf_timer_offset_msec: the recommended time offset (mSec) from the AP's TSF - * @min_delta_ftm: Minimal time between two consecutive measurements, - * in units of 100us. 0 means no preference by station - * @ftm_format_and_bw20M: FTM Channel Spacing/Format for 20MHz: recommended - * value be sent to the AP - * @ftm_format_and_bw40M: FTM Channel Spacing/Format for 40MHz: recommended - * value to be sent to the AP - * @ftm_format_and_bw80M: FTM Channel Spacing/Format for 80MHz: recommended - * value to be sent to the AP - */ -struct iwl_tof_range_req_ext_cmd { - __le32 sub_grp_cmd_id; - __le16 tsf_timer_offset_msec; - __le16 reserved; - u8 min_delta_ftm; - u8 ftm_format_and_bw20M; - u8 ftm_format_and_bw40M; - u8 ftm_format_and_bw80M; -} __packed; - -#define IWL_MVM_TOF_MAX_APS 21 - -/** - * struct iwl_tof_range_req_ap_entry - AP configuration parameters - * @channel_num: Current AP Channel - * @bandwidth: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz - * @tsf_delta_direction: TSF relatively to the subject AP - * @ctrl_ch_position: Coding of the control channel position relative to the - * center frequency. - * 40MHz 0 below center, 1 above center - * 80MHz bits [0..1]: 0 the near 20MHz to the center, - * 1 the far 20MHz to the center - * bit[2] as above 40MHz - * @bssid: AP's bss id - * @measure_type: Measurement type: 0 - two sided, 1 - One sided - * @num_of_bursts: Recommended value to be sent to the AP. 2s Exponent of the - * number of measurement iterations (min 2^0 = 1, max 2^14) - * @burst_period: Recommended value to be sent to the AP. Measurement - * periodicity In units of 100ms. ignored if num_of_bursts = 0 - * @samples_per_burst: 2-sided: the number of FTMs pairs in single Burst (1-31) - * 1-sided: how many rts/cts pairs should be used per burst. - * @retries_per_sample: Max number of retries that the LMAC should send - * in case of no replies by the AP. - * @tsf_delta: TSF Delta in units of microseconds. - * The difference between the AP TSF and the device local clock. - * @location_req: Location Request Bit[0] LCI should be sent in the FTMR - * Bit[1] Civic should be sent in the FTMR - * @asap_mode: 0 - non asap mode, 1 - asap mode (not relevant for one sided) - * @enable_dyn_ack: Enable Dynamic ACK BW. - * 0 Initiator interact with regular AP - * 1 Initiator interact with Responder machine: need to send the - * Initiator Acks with HT 40MHz / 80MHz, since the Responder should - * use it for its ch est measurement (this flag will be set when we - * configure the opposite machine to be Responder). - * @rssi: Last received value - * leagal values: -128-0 (0x7f). above 0x0 indicating an invalid value. - */ -struct iwl_tof_range_req_ap_entry { - u8 channel_num; - u8 bandwidth; - u8 tsf_delta_direction; - u8 ctrl_ch_position; - u8 bssid[ETH_ALEN]; - u8 measure_type; - u8 num_of_bursts; - __le16 burst_period; - u8 samples_per_burst; - u8 retries_per_sample; - __le32 tsf_delta; - u8 location_req; - u8 asap_mode; - u8 enable_dyn_ack; - s8 rssi; -} __packed; - -/** - * enum iwl_tof_response_mode - * @IWL_MVM_TOF_RESPOSE_ASAP: report each AP measurement separately as soon as - * possible (not supported for this release) - * @IWL_MVM_TOF_RESPOSE_TIMEOUT: report all AP measurements as a batch upon - * timeout expiration - * @IWL_MVM_TOF_RESPOSE_COMPLETE: report all AP measurements as a batch at the - * earlier of: measurements completion / timeout - * expiration. - */ -enum iwl_tof_response_mode { - IWL_MVM_TOF_RESPOSE_ASAP = 1, - IWL_MVM_TOF_RESPOSE_TIMEOUT, - IWL_MVM_TOF_RESPOSE_COMPLETE, -}; - -/** - * struct iwl_tof_range_req_cmd - start measurement cmd - * @request_id: A Token incremented per request. The same Token will be - * sent back in the range response - * @initiator: 0- NW initiated, 1 - Client Initiated - * @one_sided_los_disable: '0'- run ML-Algo for both ToF/OneSided, - * '1' - run ML-Algo for ToF only - * @req_timeout: Requested timeout of the response in units of 100ms. - * This is equivalent to the session time configured to the - * LMAC in Initiator Request - * @report_policy: Supported partially for this release: For current release - - * the range report will be uploaded as a batch when ready or - * when the session is done (successfully / partially). - * one of iwl_tof_response_mode. - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) - * @macaddr_random: '0' Use default source MAC address (i.e. p2_p), - * '1' Use MAC Address randomization according to the below - * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. - * Bits set to 1 shall be randomized by the UMAC - */ -struct iwl_tof_range_req_cmd { - __le32 sub_grp_cmd_id; - u8 request_id; - u8 initiator; - u8 one_sided_los_disable; - u8 req_timeout; - u8 report_policy; - u8 los_det_disable; - u8 num_of_ap; - u8 macaddr_random; - u8 macaddr_template[ETH_ALEN]; - u8 macaddr_mask[ETH_ALEN]; - struct iwl_tof_range_req_ap_entry ap[IWL_MVM_TOF_MAX_APS]; -} __packed; - -/** - * struct iwl_tof_gen_resp_cmd - generic ToF response - */ -struct iwl_tof_gen_resp_cmd { - __le32 sub_grp_cmd_id; - u8 data[]; -} __packed; - -/** - * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response) - * @measure_status: current APs measurement status - * @measure_bw: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz - * @rtt: The Round Trip Time that took for the last measurement for - * current AP [nSec] - * @rtt_variance: The Variance of the RTT values measured for current AP - * @rtt_spread: The Difference between the maximum and the minimum RTT - * values measured for current AP in the current session [nsec] - * @rssi: RSSI as uploaded in the Channel Estimation notification - * @rssi_spread: The Difference between the maximum and the minimum RSSI values - * measured for current AP in the current session - * @range: Measured range [cm] - * @range_variance: Measured range variance [cm] - * @timestamp: The GP2 Clock [usec] where Channel Estimation notification was - * uploaded by the LMAC - */ -struct iwl_tof_range_rsp_ap_entry_ntfy { - u8 bssid[ETH_ALEN]; - u8 measure_status; - u8 measure_bw; - __le32 rtt; - __le32 rtt_variance; - __le32 rtt_spread; - s8 rssi; - u8 rssi_spread; - __le16 reserved; - __le32 range; - __le32 range_variance; - __le32 timestamp; -} __packed; - -/** - * struct iwl_tof_range_rsp_ntfy - - * @request_id: A Token ID of the corresponding Range request - * @request_status: status of current measurement session - * @last_in_batch: reprot policy (when not all responses are uploaded at once) - * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) - */ -struct iwl_tof_range_rsp_ntfy { - u8 request_id; - u8 request_status; - u8 last_in_batch; - u8 num_of_aps; - struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_MVM_TOF_MAX_APS]; -} __packed; - -#define IWL_MVM_TOF_MCSI_BUF_SIZE (245) -/** - * struct iwl_tof_mcsi_notif - used for debug - * @token: token ID for the current session - * @role: '0' - initiator, '1' - responder - * @initiator_bssid: initiator machine - * @responder_bssid: responder machine - * @mcsi_buffer: debug data - */ -struct iwl_tof_mcsi_notif { - u8 token; - u8 role; - __le16 reserved; - u8 initiator_bssid[ETH_ALEN]; - u8 responder_bssid[ETH_ALEN]; - u8 mcsi_buffer[IWL_MVM_TOF_MCSI_BUF_SIZE * 4]; -} __packed; - -/** - * struct iwl_tof_neighbor_report_notif - * @bssid: BSSID of the AP which sent the report - * @request_token: same token as the corresponding request - * @status: - * @report_ie_len: the length of the response frame starting from the Element ID - * @data: the IEs - */ -struct iwl_tof_neighbor_report { - u8 bssid[ETH_ALEN]; - u8 request_token; - u8 status; - __le16 report_ie_len; - u8 data[]; -} __packed; - -/** - * struct iwl_tof_range_abort_cmd - * @request_id: corresponds to a range request - */ -struct iwl_tof_range_abort_cmd { - __le32 sub_grp_cmd_id; - u8 request_id; - u8 reserved[3]; -} __packed; - -#endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h deleted file mode 100644 index 853698ab8b05..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ /dev/null @@ -1,646 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __fw_api_tx_h__ -#define __fw_api_tx_h__ - -/** - * enum iwl_tx_flags - bitmasks for tx_flags in TX command - * @TX_CMD_FLG_PROT_REQUIRE: use RTS or CTS-to-self to protect the frame - * @TX_CMD_FLG_WRITE_TX_POWER: update current tx power value in the mgmt frame - * @TX_CMD_FLG_ACK: expect ACK from receiving station - * @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command. - * Otherwise, use rate_n_flags from the TX command - * @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected - * Must set TX_CMD_FLG_ACK with this flag. - * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence - * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence - * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC) - * @TX_CMD_FLG_BT_PRIO_POS: the position of the BT priority (bit 11 is ignored - * on old firmwares). - * @TX_CMD_FLG_BT_DIS: disable BT priority for this frame - * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control. - * Should be set for mgmt, non-QOS data, mcast, bcast and in scan command - * @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU - * @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame - * Should be set for beacons and probe responses - * @TX_CMD_FLG_CALIB: activate PA TX power calibrations - * @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count - * @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header. - * Should be set for 26/30 length MAC headers - * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW - * @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration - * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation - * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id - * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped - * @TX_CMD_FLG_EXEC_PAPD: execute PAPD - * @TX_CMD_FLG_PAPD_TYPE: 0 for reference power, 1 for nominal power - * @TX_CMD_FLG_HCCA_CHUNK: mark start of TSPEC chunk - */ -enum iwl_tx_flags { - TX_CMD_FLG_PROT_REQUIRE = BIT(0), - TX_CMD_FLG_WRITE_TX_POWER = BIT(1), - TX_CMD_FLG_ACK = BIT(3), - TX_CMD_FLG_STA_RATE = BIT(4), - TX_CMD_FLG_BAR = BIT(6), - TX_CMD_FLG_TXOP_PROT = BIT(7), - TX_CMD_FLG_VHT_NDPA = BIT(8), - TX_CMD_FLG_HT_NDPA = BIT(9), - TX_CMD_FLG_CSI_FDBK2HOST = BIT(10), - TX_CMD_FLG_BT_PRIO_POS = 11, - TX_CMD_FLG_BT_DIS = BIT(12), - TX_CMD_FLG_SEQ_CTL = BIT(13), - TX_CMD_FLG_MORE_FRAG = BIT(14), - TX_CMD_FLG_TSF = BIT(16), - TX_CMD_FLG_CALIB = BIT(17), - TX_CMD_FLG_KEEP_SEQ_CTL = BIT(18), - TX_CMD_FLG_MH_PAD = BIT(20), - TX_CMD_FLG_RESP_TO_DRV = BIT(21), - TX_CMD_FLG_CCMP_AGG = BIT(22), - TX_CMD_FLG_TKIP_MIC_DONE = BIT(23), - TX_CMD_FLG_DUR = BIT(25), - TX_CMD_FLG_FW_DROP = BIT(26), - TX_CMD_FLG_EXEC_PAPD = BIT(27), - TX_CMD_FLG_PAPD_TYPE = BIT(28), - TX_CMD_FLG_HCCA_CHUNK = BIT(31) -}; /* TX_FLAGS_BITS_API_S_VER_1 */ - -/** - * enum iwl_tx_pm_timeouts - pm timeout values in TX command - * @PM_FRAME_NONE: no need to suspend sleep mode - * @PM_FRAME_MGMT: fw suspend sleep mode for 100TU - * @PM_FRAME_ASSOC: fw suspend sleep mode for 10sec - */ -enum iwl_tx_pm_timeouts { - PM_FRAME_NONE = 0, - PM_FRAME_MGMT = 2, - PM_FRAME_ASSOC = 3, -}; - -/* - * TX command security control - */ -#define TX_CMD_SEC_WEP 0x01 -#define TX_CMD_SEC_CCM 0x02 -#define TX_CMD_SEC_TKIP 0x03 -#define TX_CMD_SEC_EXT 0x04 -#define TX_CMD_SEC_MSK 0x07 -#define TX_CMD_SEC_WEP_KEY_IDX_POS 6 -#define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 -#define TX_CMD_SEC_KEY128 0x08 - -/* TODO: how does these values are OK with only 16 bit variable??? */ -/* - * TX command next frame info - * - * bits 0:2 - security control (TX_CMD_SEC_*) - * bit 3 - immediate ACK required - * bit 4 - rate is taken from STA table - * bit 5 - frame belongs to BA stream - * bit 6 - immediate BA response expected - * bit 7 - unused - * bits 8:15 - Station ID - * bits 16:31 - rate - */ -#define TX_CMD_NEXT_FRAME_ACK_MSK (0x8) -#define TX_CMD_NEXT_FRAME_STA_RATE_MSK (0x10) -#define TX_CMD_NEXT_FRAME_BA_MSK (0x20) -#define TX_CMD_NEXT_FRAME_IMM_BA_RSP_MSK (0x40) -#define TX_CMD_NEXT_FRAME_FLAGS_MSK (0xf8) -#define TX_CMD_NEXT_FRAME_STA_ID_MSK (0xff00) -#define TX_CMD_NEXT_FRAME_STA_ID_POS (8) -#define TX_CMD_NEXT_FRAME_RATE_MSK (0xffff0000) -#define TX_CMD_NEXT_FRAME_RATE_POS (16) - -/* - * TX command Frame life time in us - to be written in pm_frame_timeout - */ -#define TX_CMD_LIFE_TIME_INFINITE 0xFFFFFFFF -#define TX_CMD_LIFE_TIME_DEFAULT 2000000 /* 2000 ms*/ -#define TX_CMD_LIFE_TIME_PROBE_RESP 40000 /* 40 ms */ -#define TX_CMD_LIFE_TIME_EXPIRED_FRAME 0 - -/* - * TID for non QoS frames - to be written in tid_tspec - */ -#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT - -/* - * Limits on the retransmissions - to be written in {data,rts}_retry_limit - */ -#define IWL_DEFAULT_TX_RETRY 15 -#define IWL_MGMT_DFAULT_RETRY_LIMIT 3 -#define IWL_RTS_DFAULT_RETRY_LIMIT 60 -#define IWL_BAR_DFAULT_RETRY_LIMIT 60 -#define IWL_LOW_RETRY_LIMIT 7 - -/* TODO: complete documentation for try_cnt and btkill_cnt */ -/** - * struct iwl_tx_cmd - TX command struct to FW - * ( TX_CMD = 0x1c ) - * @len: in bytes of the payload, see below for details - * @tx_flags: combination of TX_CMD_FLG_* - * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is - * cleared. Combination of RATE_MCS_* - * @sta_id: index of destination station in FW station table - * @sec_ctl: security control, TX_CMD_SEC_* - * @initial_rate_index: index into the the rate table for initial TX attempt. - * Applied if TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames. - * @key: security key - * @next_frame_flags: TX_CMD_SEC_* and TX_CMD_NEXT_FRAME_* - * @life_time: frame life time (usecs??) - * @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt + - * btkill_cnd + reserved), first 32 bits. "0" disables usage. - * @dram_msb_ptr: upper bits of the scratch physical address - * @rts_retry_limit: max attempts for RTS - * @data_retry_limit: max attempts to send the data packet - * @tid_spec: TID/tspec - * @pm_frame_timeout: PM TX frame timeout - * - * The byte count (both len and next_frame_len) includes MAC header - * (24/26/30/32 bytes) - * + 2 bytes pad if 26/30 header size - * + 8 byte IV for CCM or TKIP (not used for WEP) - * + Data payload - * + 8-byte MIC (not used for CCM/WEP) - * It does not include post-MAC padding, i.e., - * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes. - * Range of len: 14-2342 bytes. - * - * After the struct fields the MAC header is placed, plus any padding, - * and then the actial payload. - */ -struct iwl_tx_cmd { - __le16 len; - __le16 next_frame_len; - __le32 tx_flags; - struct { - u8 try_cnt; - u8 btkill_cnt; - __le16 reserved; - } scratch; /* DRAM_SCRATCH_API_U_VER_1 */ - __le32 rate_n_flags; - u8 sta_id; - u8 sec_ctl; - u8 initial_rate_index; - u8 reserved2; - u8 key[16]; - __le32 reserved3; - __le32 life_time; - __le32 dram_lsb_ptr; - u8 dram_msb_ptr; - u8 rts_retry_limit; - u8 data_retry_limit; - u8 tid_tspec; - __le16 pm_frame_timeout; - __le16 reserved4; - u8 payload[0]; - struct ieee80211_hdr hdr[0]; -} __packed; /* TX_CMD_API_S_VER_3 */ - -/* - * TX response related data - */ - -/* - * enum iwl_tx_status - status that is returned by the fw after attempts to Tx - * @TX_STATUS_SUCCESS: - * @TX_STATUS_DIRECT_DONE: - * @TX_STATUS_POSTPONE_DELAY: - * @TX_STATUS_POSTPONE_FEW_BYTES: - * @TX_STATUS_POSTPONE_BT_PRIO: - * @TX_STATUS_POSTPONE_QUIET_PERIOD: - * @TX_STATUS_POSTPONE_CALC_TTAK: - * @TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: - * @TX_STATUS_FAIL_SHORT_LIMIT: - * @TX_STATUS_FAIL_LONG_LIMIT: - * @TX_STATUS_FAIL_UNDERRUN: - * @TX_STATUS_FAIL_DRAIN_FLOW: - * @TX_STATUS_FAIL_RFKILL_FLUSH: - * @TX_STATUS_FAIL_LIFE_EXPIRE: - * @TX_STATUS_FAIL_DEST_PS: - * @TX_STATUS_FAIL_HOST_ABORTED: - * @TX_STATUS_FAIL_BT_RETRY: - * @TX_STATUS_FAIL_STA_INVALID: - * @TX_TATUS_FAIL_FRAG_DROPPED: - * @TX_STATUS_FAIL_TID_DISABLE: - * @TX_STATUS_FAIL_FIFO_FLUSHED: - * @TX_STATUS_FAIL_SMALL_CF_POLL: - * @TX_STATUS_FAIL_FW_DROP: - * @TX_STATUS_FAIL_STA_COLOR_MISMATCH: mismatch between color of Tx cmd and - * STA table - * @TX_FRAME_STATUS_INTERNAL_ABORT: - * @TX_MODE_MSK: - * @TX_MODE_NO_BURST: - * @TX_MODE_IN_BURST_SEQ: - * @TX_MODE_FIRST_IN_BURST: - * @TX_QUEUE_NUM_MSK: - * - * Valid only if frame_count =1 - * TODO: complete documentation - */ -enum iwl_tx_status { - TX_STATUS_MSK = 0x000000ff, - TX_STATUS_SUCCESS = 0x01, - TX_STATUS_DIRECT_DONE = 0x02, - /* postpone TX */ - TX_STATUS_POSTPONE_DELAY = 0x40, - TX_STATUS_POSTPONE_FEW_BYTES = 0x41, - TX_STATUS_POSTPONE_BT_PRIO = 0x42, - TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, - TX_STATUS_POSTPONE_CALC_TTAK = 0x44, - /* abort TX */ - TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, - TX_STATUS_FAIL_SHORT_LIMIT = 0x82, - TX_STATUS_FAIL_LONG_LIMIT = 0x83, - TX_STATUS_FAIL_UNDERRUN = 0x84, - TX_STATUS_FAIL_DRAIN_FLOW = 0x85, - TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, - TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, - TX_STATUS_FAIL_DEST_PS = 0x88, - TX_STATUS_FAIL_HOST_ABORTED = 0x89, - TX_STATUS_FAIL_BT_RETRY = 0x8a, - TX_STATUS_FAIL_STA_INVALID = 0x8b, - TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, - TX_STATUS_FAIL_TID_DISABLE = 0x8d, - TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, - TX_STATUS_FAIL_SMALL_CF_POLL = 0x8f, - TX_STATUS_FAIL_FW_DROP = 0x90, - TX_STATUS_FAIL_STA_COLOR_MISMATCH = 0x91, - TX_STATUS_INTERNAL_ABORT = 0x92, - TX_MODE_MSK = 0x00000f00, - TX_MODE_NO_BURST = 0x00000000, - TX_MODE_IN_BURST_SEQ = 0x00000100, - TX_MODE_FIRST_IN_BURST = 0x00000200, - TX_QUEUE_NUM_MSK = 0x0001f000, - TX_NARROW_BW_MSK = 0x00060000, - TX_NARROW_BW_1DIV2 = 0x00020000, - TX_NARROW_BW_1DIV4 = 0x00040000, - TX_NARROW_BW_1DIV8 = 0x00060000, -}; - -/* - * enum iwl_tx_agg_status - TX aggregation status - * @AGG_TX_STATE_STATUS_MSK: - * @AGG_TX_STATE_TRANSMITTED: - * @AGG_TX_STATE_UNDERRUN: - * @AGG_TX_STATE_BT_PRIO: - * @AGG_TX_STATE_FEW_BYTES: - * @AGG_TX_STATE_ABORT: - * @AGG_TX_STATE_LAST_SENT_TTL: - * @AGG_TX_STATE_LAST_SENT_TRY_CNT: - * @AGG_TX_STATE_LAST_SENT_BT_KILL: - * @AGG_TX_STATE_SCD_QUERY: - * @AGG_TX_STATE_TEST_BAD_CRC32: - * @AGG_TX_STATE_RESPONSE: - * @AGG_TX_STATE_DUMP_TX: - * @AGG_TX_STATE_DELAY_TX: - * @AGG_TX_STATE_TRY_CNT_MSK: Retry count for 1st frame in aggregation (retries - * occur if tx failed for this frame when it was a member of a previous - * aggregation block). If rate scaling is used, retry count indicates the - * rate table entry used for all frames in the new agg. - *@ AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for - * this frame - * - * TODO: complete documentation - */ -enum iwl_tx_agg_status { - AGG_TX_STATE_STATUS_MSK = 0x00fff, - AGG_TX_STATE_TRANSMITTED = 0x000, - AGG_TX_STATE_UNDERRUN = 0x001, - AGG_TX_STATE_BT_PRIO = 0x002, - AGG_TX_STATE_FEW_BYTES = 0x004, - AGG_TX_STATE_ABORT = 0x008, - AGG_TX_STATE_LAST_SENT_TTL = 0x010, - AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020, - AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040, - AGG_TX_STATE_SCD_QUERY = 0x080, - AGG_TX_STATE_TEST_BAD_CRC32 = 0x0100, - AGG_TX_STATE_RESPONSE = 0x1ff, - AGG_TX_STATE_DUMP_TX = 0x200, - AGG_TX_STATE_DELAY_TX = 0x400, - AGG_TX_STATE_TRY_CNT_POS = 12, - AGG_TX_STATE_TRY_CNT_MSK = 0xf << AGG_TX_STATE_TRY_CNT_POS, -}; - -#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL| \ - AGG_TX_STATE_LAST_SENT_TRY_CNT| \ - AGG_TX_STATE_LAST_SENT_BT_KILL) - -/* - * The mask below describes a status where we are absolutely sure that the MPDU - * wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've - * written the bytes to the TXE, but we know nothing about what the DSP did. - */ -#define AGG_TX_STAT_FRAME_NOT_SENT (AGG_TX_STATE_FEW_BYTES | \ - AGG_TX_STATE_ABORT | \ - AGG_TX_STATE_SCD_QUERY) - -/* - * REPLY_TX = 0x1c (response) - * - * This response may be in one of two slightly different formats, indicated - * by the frame_count field: - * - * 1) No aggregation (frame_count == 1). This reports Tx results for a single - * frame. Multiple attempts, at various bit rates, may have been made for - * this frame. - * - * 2) Aggregation (frame_count > 1). This reports Tx results for two or more - * frames that used block-acknowledge. All frames were transmitted at - * same rate. Rate scaling may have been used if first frame in this new - * agg block failed in previous agg block(s). - * - * Note that, for aggregation, ACK (block-ack) status is not delivered - * here; block-ack has not been received by the time the device records - * this status. - * This status relates to reasons the tx might have been blocked or aborted - * within the device, rather than whether it was received successfully by - * the destination station. - */ - -/** - * struct agg_tx_status - per packet TX aggregation status - * @status: enum iwl_tx_agg_status - * @sequence: Sequence # for this frame's Tx cmd (not SSN!) - */ -struct agg_tx_status { - __le16 status; - __le16 sequence; -} __packed; - -/* - * definitions for initial rate index field - * bits [3:0] initial rate index - * bits [6:4] rate table color, used for the initial rate - * bit-7 invalid rate indication - */ -#define TX_RES_INIT_RATE_INDEX_MSK 0x0f -#define TX_RES_RATE_TABLE_COLOR_MSK 0x70 -#define TX_RES_INV_RATE_INDEX_MSK 0x80 - -#define IWL_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f) -#define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4) - -/** - * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet - * ( REPLY_TX = 0x1c ) - * @frame_count: 1 no aggregation, >1 aggregation - * @bt_kill_count: num of times blocked by bluetooth (unused for agg) - * @failure_rts: num of failures due to unsuccessful RTS - * @failure_frame: num failures due to no ACK (unused for agg) - * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the - * Tx of all the batch. RATE_MCS_* - * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK. - * for agg: RTS + CTS + aggregation tx time + block-ack time. - * in usec. - * @pa_status: tx power info - * @pa_integ_res_a: tx power info - * @pa_integ_res_b: tx power info - * @pa_integ_res_c: tx power info - * @measurement_req_id: tx power info - * @tfd_info: TFD information set by the FH - * @seq_ctl: sequence control from the Tx cmd - * @byte_cnt: byte count from the Tx cmd - * @tlc_info: TLC rate info - * @ra_tid: bits [3:0] = ra, bits [7:4] = tid - * @frame_ctrl: frame control - * @status: for non-agg: frame status TX_STATUS_* - * for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields - * follow this one, up to frame_count. - * - * After the array of statuses comes the SSN of the SCD. Look at - * %iwl_mvm_get_scd_ssn for more details. - */ -struct iwl_mvm_tx_resp { - u8 frame_count; - u8 bt_kill_count; - u8 failure_rts; - u8 failure_frame; - __le32 initial_rate; - __le16 wireless_media_time; - - u8 pa_status; - u8 pa_integ_res_a[3]; - u8 pa_integ_res_b[3]; - u8 pa_integ_res_c[3]; - __le16 measurement_req_id; - u8 reduced_tpc; - u8 reserved; - - __le32 tfd_info; - __le16 seq_ctl; - __le16 byte_cnt; - u8 tlc_info; - u8 ra_tid; - __le16 frame_ctrl; - - struct agg_tx_status status; -} __packed; /* TX_RSP_API_S_VER_3 */ - -/** - * struct iwl_mvm_ba_notif - notifies about reception of BA - * ( BA_NOTIF = 0xc5 ) - * @sta_addr_lo32: lower 32 bits of the MAC address - * @sta_addr_hi16: upper 16 bits of the MAC address - * @sta_id: Index of recipient (BA-sending) station in fw's station table - * @tid: tid of the session - * @seq_ctl: - * @bitmap: the bitmap of the BA notification as seen in the air - * @scd_flow: the tx queue this BA relates to - * @scd_ssn: the index of the last contiguously sent packet - * @txed: number of Txed frames in this batch - * @txed_2_done: number of Acked frames in this batch - */ -struct iwl_mvm_ba_notif { - __le32 sta_addr_lo32; - __le16 sta_addr_hi16; - __le16 reserved; - - u8 sta_id; - u8 tid; - __le16 seq_ctl; - __le64 bitmap; - __le16 scd_flow; - __le16 scd_ssn; - u8 txed; - u8 txed_2_done; - __le16 reserved1; -} __packed; - -/* - * struct iwl_mac_beacon_cmd - beacon template command - * @tx: the tx commands associated with the beacon frame - * @template_id: currently equal to the mac context id of the coresponding - * mac. - * @tim_idx: the offset of the tim IE in the beacon - * @tim_size: the length of the tim IE - * @frame: the template of the beacon frame - */ -struct iwl_mac_beacon_cmd { - struct iwl_tx_cmd tx; - __le32 template_id; - __le32 tim_idx; - __le32 tim_size; - struct ieee80211_hdr frame[0]; -} __packed; - -struct iwl_beacon_notif { - struct iwl_mvm_tx_resp beacon_notify_hdr; - __le64 tsf; - __le32 ibss_mgr_status; -} __packed; - -/** - * struct iwl_extended_beacon_notif - notifies about beacon transmission - * @beacon_notify_hdr: tx response command associated with the beacon - * @tsf: last beacon tsf - * @ibss_mgr_status: whether IBSS is manager - * @gp2: last beacon time in gp2 - */ -struct iwl_extended_beacon_notif { - struct iwl_mvm_tx_resp beacon_notify_hdr; - __le64 tsf; - __le32 ibss_mgr_status; - __le32 gp2; -} __packed; /* BEACON_NTFY_API_S_VER_5 */ - -/** - * enum iwl_dump_control - dump (flush) control flags - * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty - * and the TFD queues are empty. - */ -enum iwl_dump_control { - DUMP_TX_FIFO_FLUSH = BIT(1), -}; - -/** - * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command - * @queues_ctl: bitmap of queues to flush - * @flush_ctl: control flags - * @reserved: reserved - */ -struct iwl_tx_path_flush_cmd { - __le32 queues_ctl; - __le16 flush_ctl; - __le16 reserved; -} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */ - -/** - * iwl_mvm_get_scd_ssn - returns the SSN of the SCD - * @tx_resp: the Tx response from the fw (agg or non-agg) - * - * When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since - * it can't know that everything will go well until the end of the AMPDU, it - * can't know in advance the number of MPDUs that will be sent in the current - * batch. This is why it writes the agg Tx response while it fetches the MPDUs. - * Hence, it can't know in advance what the SSN of the SCD will be at the end - * of the batch. This is why the SSN of the SCD is written at the end of the - * whole struct at a variable offset. This function knows how to cope with the - * variable offset and returns the SSN of the SCD. - */ -static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm_tx_resp *tx_resp) -{ - return le32_to_cpup((__le32 *)&tx_resp->status + - tx_resp->frame_count) & 0xfff; -} - -/** - * struct iwl_scd_txq_cfg_cmd - New txq hw scheduler config command - * @token: - * @sta_id: station id - * @tid: - * @scd_queue: scheduler queue to confiug - * @enable: 1 queue enable, 0 queue disable - * @aggregate: 1 aggregated queue, 0 otherwise - * @tx_fifo: %enum iwl_mvm_tx_fifo - * @window: BA window size - * @ssn: SSN for the BA agreement - */ -struct iwl_scd_txq_cfg_cmd { - u8 token; - u8 sta_id; - u8 tid; - u8 scd_queue; - u8 enable; - u8 aggregate; - u8 tx_fifo; - u8 window; - __le16 ssn; - __le16 reserved; -} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */ - -/** - * struct iwl_scd_txq_cfg_rsp - * @token: taken from the command - * @sta_id: station id from the command - * @tid: tid from the command - * @scd_queue: scd_queue from the command - */ -struct iwl_scd_txq_cfg_rsp { - u8 token; - u8 sta_id; - u8 tid; - u8 scd_queue; -} __packed; /* SCD_QUEUE_CFG_RSP_API_S_VER_1 */ - -#endif /* __fw_api_tx_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h deleted file mode 100644 index 181590fbd3b3..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ /dev/null @@ -1,1773 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __fw_api_h__ -#define __fw_api_h__ - -#include "fw-api-rs.h" -#include "fw-api-rx.h" -#include "fw-api-tx.h" -#include "fw-api-sta.h" -#include "fw-api-mac.h" -#include "fw-api-power.h" -#include "fw-api-d3.h" -#include "fw-api-coex.h" -#include "fw-api-scan.h" -#include "fw-api-stats.h" -#include "fw-api-tof.h" - -/* Tx queue numbers */ -enum { - IWL_MVM_OFFCHANNEL_QUEUE = 8, - IWL_MVM_CMD_QUEUE = 9, -}; - -enum iwl_mvm_tx_fifo { - IWL_MVM_TX_FIFO_BK = 0, - IWL_MVM_TX_FIFO_BE, - IWL_MVM_TX_FIFO_VI, - IWL_MVM_TX_FIFO_VO, - IWL_MVM_TX_FIFO_MCAST = 5, - IWL_MVM_TX_FIFO_CMD = 7, -}; - -#define IWL_MVM_STATION_COUNT 16 - -#define IWL_MVM_TDLS_STA_COUNT 4 - -/* commands */ -enum { - MVM_ALIVE = 0x1, - REPLY_ERROR = 0x2, - ECHO_CMD = 0x3, - - INIT_COMPLETE_NOTIF = 0x4, - - /* PHY context commands */ - PHY_CONTEXT_CMD = 0x8, - DBG_CFG = 0x9, - ANTENNA_COUPLING_NOTIFICATION = 0xa, - - /* UMAC scan commands */ - SCAN_ITERATION_COMPLETE_UMAC = 0xb5, - SCAN_CFG_CMD = 0xc, - SCAN_REQ_UMAC = 0xd, - SCAN_ABORT_UMAC = 0xe, - SCAN_COMPLETE_UMAC = 0xf, - - /* station table */ - ADD_STA_KEY = 0x17, - ADD_STA = 0x18, - REMOVE_STA = 0x19, - - /* paging get item */ - FW_GET_ITEM_CMD = 0x1a, - - /* TX */ - TX_CMD = 0x1c, - TXPATH_FLUSH = 0x1e, - MGMT_MCAST_KEY = 0x1f, - - /* scheduler config */ - SCD_QUEUE_CFG = 0x1d, - - /* global key */ - WEP_KEY = 0x20, - - /* Memory */ - SHARED_MEM_CFG = 0x25, - - /* TDLS */ - TDLS_CHANNEL_SWITCH_CMD = 0x27, - TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa, - TDLS_CONFIG_CMD = 0xa7, - - /* MAC and Binding commands */ - MAC_CONTEXT_CMD = 0x28, - TIME_EVENT_CMD = 0x29, /* both CMD and response */ - TIME_EVENT_NOTIFICATION = 0x2a, - BINDING_CONTEXT_CMD = 0x2b, - TIME_QUOTA_CMD = 0x2c, - NON_QOS_TX_COUNTER_CMD = 0x2d, - - LQ_CMD = 0x4e, - - /* paging block to FW cpu2 */ - FW_PAGING_BLOCK_CMD = 0x4f, - - /* Scan offload */ - SCAN_OFFLOAD_REQUEST_CMD = 0x51, - SCAN_OFFLOAD_ABORT_CMD = 0x52, - HOT_SPOT_CMD = 0x53, - SCAN_OFFLOAD_COMPLETE = 0x6D, - SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, - SCAN_OFFLOAD_CONFIG_CMD = 0x6f, - MATCH_FOUND_NOTIFICATION = 0xd9, - SCAN_ITERATION_COMPLETE = 0xe7, - - /* Phy */ - PHY_CONFIGURATION_CMD = 0x6a, - CALIB_RES_NOTIF_PHY_DB = 0x6b, - /* PHY_DB_CMD = 0x6c, */ - - /* ToF - 802.11mc FTM */ - TOF_CMD = 0x10, - TOF_NOTIFICATION = 0x11, - - /* Power - legacy power table command */ - POWER_TABLE_CMD = 0x77, - PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78, - LTR_CONFIG = 0xee, - - /* Thermal Throttling*/ - REPLY_THERMAL_MNG_BACKOFF = 0x7e, - - /* Set/Get DC2DC frequency tune */ - DC2DC_CONFIG_CMD = 0x83, - - /* NVM */ - NVM_ACCESS_CMD = 0x88, - - SET_CALIB_DEFAULT_CMD = 0x8e, - - BEACON_NOTIFICATION = 0x90, - BEACON_TEMPLATE_CMD = 0x91, - TX_ANT_CONFIGURATION_CMD = 0x98, - STATISTICS_CMD = 0x9c, - STATISTICS_NOTIFICATION = 0x9d, - EOSP_NOTIFICATION = 0x9e, - REDUCE_TX_POWER_CMD = 0x9f, - - /* RF-KILL commands and notifications */ - CARD_STATE_CMD = 0xa0, - CARD_STATE_NOTIFICATION = 0xa1, - - MISSED_BEACONS_NOTIFICATION = 0xa2, - - /* Power - new power table command */ - MAC_PM_POWER_TABLE = 0xa9, - - MFUART_LOAD_NOTIFICATION = 0xb1, - - REPLY_RX_PHY_CMD = 0xc0, - REPLY_RX_MPDU_CMD = 0xc1, - BA_NOTIF = 0xc5, - - /* Location Aware Regulatory */ - MCC_UPDATE_CMD = 0xc8, - MCC_CHUB_UPDATE_CMD = 0xc9, - - MARKER_CMD = 0xcb, - - /* BT Coex */ - BT_COEX_PRIO_TABLE = 0xcc, - BT_COEX_PROT_ENV = 0xcd, - BT_PROFILE_NOTIFICATION = 0xce, - BT_CONFIG = 0x9b, - BT_COEX_UPDATE_SW_BOOST = 0x5a, - BT_COEX_UPDATE_CORUN_LUT = 0x5b, - BT_COEX_UPDATE_REDUCED_TXP = 0x5c, - BT_COEX_CI = 0x5d, - - REPLY_SF_CFG_CMD = 0xd1, - REPLY_BEACON_FILTERING_CMD = 0xd2, - - /* DTS measurements */ - CMD_DTS_MEASUREMENT_TRIGGER = 0xdc, - DTS_MEASUREMENT_NOTIFICATION = 0xdd, - - REPLY_DEBUG_CMD = 0xf0, - DEBUG_LOG_MSG = 0xf7, - - BCAST_FILTER_CMD = 0xcf, - MCAST_FILTER_CMD = 0xd0, - - /* D3 commands/notifications */ - D3_CONFIG_CMD = 0xd3, - PROT_OFFLOAD_CONFIG_CMD = 0xd4, - OFFLOADS_QUERY_CMD = 0xd5, - REMOTE_WAKE_CONFIG_CMD = 0xd6, - D0I3_END_CMD = 0xed, - - /* for WoWLAN in particular */ - WOWLAN_PATTERNS = 0xe0, - WOWLAN_CONFIGURATION = 0xe1, - WOWLAN_TSC_RSC_PARAM = 0xe2, - WOWLAN_TKIP_PARAM = 0xe3, - WOWLAN_KEK_KCK_MATERIAL = 0xe4, - WOWLAN_GET_STATUSES = 0xe5, - WOWLAN_TX_POWER_PER_DB = 0xe6, - - /* and for NetDetect */ - SCAN_OFFLOAD_PROFILES_QUERY_CMD = 0x56, - SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD = 0x58, - SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD = 0x59, - - REPLY_MAX = 0xff, -}; - -enum iwl_phy_ops_subcmd_ids { - CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, - DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, -}; - -/* command groups */ -enum { - PHY_OPS_GROUP = 0x4, -}; - -/** - * struct iwl_cmd_response - generic response struct for most commands - * @status: status of the command asked, changes for each one - */ -struct iwl_cmd_response { - __le32 status; -}; - -/* - * struct iwl_tx_ant_cfg_cmd - * @valid: valid antenna configuration - */ -struct iwl_tx_ant_cfg_cmd { - __le32 valid; -} __packed; - -/* - * Calibration control struct. - * Sent as part of the phy configuration command. - * @flow_trigger: bitmap for which calibrations to perform according to - * flow triggers. - * @event_trigger: bitmap for which calibrations to perform according to - * event triggers. - */ -struct iwl_calib_ctrl { - __le32 flow_trigger; - __le32 event_trigger; -} __packed; - -/* This enum defines the bitmap of various calibrations to enable in both - * init ucode and runtime ucode through CALIBRATION_CFG_CMD. - */ -enum iwl_calib_cfg { - IWL_CALIB_CFG_XTAL_IDX = BIT(0), - IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(1), - IWL_CALIB_CFG_VOLTAGE_READ_IDX = BIT(2), - IWL_CALIB_CFG_PAPD_IDX = BIT(3), - IWL_CALIB_CFG_TX_PWR_IDX = BIT(4), - IWL_CALIB_CFG_DC_IDX = BIT(5), - IWL_CALIB_CFG_BB_FILTER_IDX = BIT(6), - IWL_CALIB_CFG_LO_LEAKAGE_IDX = BIT(7), - IWL_CALIB_CFG_TX_IQ_IDX = BIT(8), - IWL_CALIB_CFG_TX_IQ_SKEW_IDX = BIT(9), - IWL_CALIB_CFG_RX_IQ_IDX = BIT(10), - IWL_CALIB_CFG_RX_IQ_SKEW_IDX = BIT(11), - IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(12), - IWL_CALIB_CFG_CHAIN_NOISE_IDX = BIT(13), - IWL_CALIB_CFG_DISCONNECTED_ANT_IDX = BIT(14), - IWL_CALIB_CFG_ANT_COUPLING_IDX = BIT(15), - IWL_CALIB_CFG_DAC_IDX = BIT(16), - IWL_CALIB_CFG_ABS_IDX = BIT(17), - IWL_CALIB_CFG_AGC_IDX = BIT(18), -}; - -/* - * Phy configuration command. - */ -struct iwl_phy_cfg_cmd { - __le32 phy_cfg; - struct iwl_calib_ctrl calib_control; -} __packed; - -#define PHY_CFG_RADIO_TYPE (BIT(0) | BIT(1)) -#define PHY_CFG_RADIO_STEP (BIT(2) | BIT(3)) -#define PHY_CFG_RADIO_DASH (BIT(4) | BIT(5)) -#define PHY_CFG_PRODUCT_NUMBER (BIT(6) | BIT(7)) -#define PHY_CFG_TX_CHAIN_A BIT(8) -#define PHY_CFG_TX_CHAIN_B BIT(9) -#define PHY_CFG_TX_CHAIN_C BIT(10) -#define PHY_CFG_RX_CHAIN_A BIT(12) -#define PHY_CFG_RX_CHAIN_B BIT(13) -#define PHY_CFG_RX_CHAIN_C BIT(14) - - -/* Target of the NVM_ACCESS_CMD */ -enum { - NVM_ACCESS_TARGET_CACHE = 0, - NVM_ACCESS_TARGET_OTP = 1, - NVM_ACCESS_TARGET_EEPROM = 2, -}; - -/* Section types for NVM_ACCESS_CMD */ -enum { - NVM_SECTION_TYPE_SW = 1, - NVM_SECTION_TYPE_REGULATORY = 3, - NVM_SECTION_TYPE_CALIBRATION = 4, - NVM_SECTION_TYPE_PRODUCTION = 5, - NVM_SECTION_TYPE_MAC_OVERRIDE = 11, - NVM_SECTION_TYPE_PHY_SKU = 12, - NVM_MAX_NUM_SECTIONS = 13, -}; - -/** - * struct iwl_nvm_access_cmd_ver2 - Request the device to send an NVM section - * @op_code: 0 - read, 1 - write - * @target: NVM_ACCESS_TARGET_* - * @type: NVM_SECTION_TYPE_* - * @offset: offset in bytes into the section - * @length: in bytes, to read/write - * @data: if write operation, the data to write. On read its empty - */ -struct iwl_nvm_access_cmd { - u8 op_code; - u8 target; - __le16 type; - __le16 offset; - __le16 length; - u8 data[]; -} __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */ - -#define NUM_OF_FW_PAGING_BLOCKS 33 /* 32 for data and 1 block for CSS */ - -/* - * struct iwl_fw_paging_cmd - paging layout - * - * (FW_PAGING_BLOCK_CMD = 0x4f) - * - * Send to FW the paging layout in the driver. - * - * @flags: various flags for the command - * @block_size: the block size in powers of 2 - * @block_num: number of blocks specified in the command. - * @device_phy_addr: virtual addresses from device side -*/ -struct iwl_fw_paging_cmd { - __le32 flags; - __le32 block_size; - __le32 block_num; - __le32 device_phy_addr[NUM_OF_FW_PAGING_BLOCKS]; -} __packed; /* FW_PAGING_BLOCK_CMD_API_S_VER_1 */ - -/* - * Fw items ID's - * - * @IWL_FW_ITEM_ID_PAGING: Address of the pages that the FW will upload - * download - */ -enum iwl_fw_item_id { - IWL_FW_ITEM_ID_PAGING = 3, -}; - -/* - * struct iwl_fw_get_item_cmd - get an item from the fw - */ -struct iwl_fw_get_item_cmd { - __le32 item_id; -} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */ - -struct iwl_fw_get_item_resp { - __le32 item_id; - __le32 item_byte_cnt; - __le32 item_val; -} __packed; /* FW_GET_ITEM_RSP_S_VER_1 */ - -/** - * struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD - * @offset: offset in bytes into the section - * @length: in bytes, either how much was written or read - * @type: NVM_SECTION_TYPE_* - * @status: 0 for success, fail otherwise - * @data: if read operation, the data returned. Empty on write. - */ -struct iwl_nvm_access_resp { - __le16 offset; - __le16 length; - __le16 type; - __le16 status; - u8 data[]; -} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_2 */ - -/* MVM_ALIVE 0x1 */ - -/* alive response is_valid values */ -#define ALIVE_RESP_UCODE_OK BIT(0) -#define ALIVE_RESP_RFKILL BIT(1) - -/* alive response ver_type values */ -enum { - FW_TYPE_HW = 0, - FW_TYPE_PROT = 1, - FW_TYPE_AP = 2, - FW_TYPE_WOWLAN = 3, - FW_TYPE_TIMING = 4, - FW_TYPE_WIPAN = 5 -}; - -/* alive response ver_subtype values */ -enum { - FW_SUBTYPE_FULL_FEATURE = 0, - FW_SUBTYPE_BOOTSRAP = 1, /* Not valid */ - FW_SUBTYPE_REDUCED = 2, - FW_SUBTYPE_ALIVE_ONLY = 3, - FW_SUBTYPE_WOWLAN = 4, - FW_SUBTYPE_AP_SUBTYPE = 5, - FW_SUBTYPE_WIPAN = 6, - FW_SUBTYPE_INITIALIZE = 9 -}; - -#define IWL_ALIVE_STATUS_ERR 0xDEAD -#define IWL_ALIVE_STATUS_OK 0xCAFE - -#define IWL_ALIVE_FLG_RFKILL BIT(0) - -struct mvm_alive_resp_ver1 { - __le16 status; - __le16 flags; - u8 ucode_minor; - u8 ucode_major; - __le16 id; - u8 api_minor; - u8 api_major; - u8 ver_subtype; - u8 ver_type; - u8 mac; - u8 opt; - __le16 reserved2; - __le32 timestamp; - __le32 error_event_table_ptr; /* SRAM address for error log */ - __le32 log_event_table_ptr; /* SRAM address for event log */ - __le32 cpu_register_ptr; - __le32 dbgm_config_ptr; - __le32 alive_counter_ptr; - __le32 scd_base_ptr; /* SRAM address for SCD */ -} __packed; /* ALIVE_RES_API_S_VER_1 */ - -struct mvm_alive_resp_ver2 { - __le16 status; - __le16 flags; - u8 ucode_minor; - u8 ucode_major; - __le16 id; - u8 api_minor; - u8 api_major; - u8 ver_subtype; - u8 ver_type; - u8 mac; - u8 opt; - __le16 reserved2; - __le32 timestamp; - __le32 error_event_table_ptr; /* SRAM address for error log */ - __le32 log_event_table_ptr; /* SRAM address for LMAC event log */ - __le32 cpu_register_ptr; - __le32 dbgm_config_ptr; - __le32 alive_counter_ptr; - __le32 scd_base_ptr; /* SRAM address for SCD */ - __le32 st_fwrd_addr; /* pointer to Store and forward */ - __le32 st_fwrd_size; - u8 umac_minor; /* UMAC version: minor */ - u8 umac_major; /* UMAC version: major */ - __le16 umac_id; /* UMAC version: id */ - __le32 error_info_addr; /* SRAM address for UMAC error log */ - __le32 dbg_print_buff_addr; -} __packed; /* ALIVE_RES_API_S_VER_2 */ - -struct mvm_alive_resp { - __le16 status; - __le16 flags; - __le32 ucode_minor; - __le32 ucode_major; - u8 ver_subtype; - u8 ver_type; - u8 mac; - u8 opt; - __le32 timestamp; - __le32 error_event_table_ptr; /* SRAM address for error log */ - __le32 log_event_table_ptr; /* SRAM address for LMAC event log */ - __le32 cpu_register_ptr; - __le32 dbgm_config_ptr; - __le32 alive_counter_ptr; - __le32 scd_base_ptr; /* SRAM address for SCD */ - __le32 st_fwrd_addr; /* pointer to Store and forward */ - __le32 st_fwrd_size; - __le32 umac_minor; /* UMAC version: minor */ - __le32 umac_major; /* UMAC version: major */ - __le32 error_info_addr; /* SRAM address for UMAC error log */ - __le32 dbg_print_buff_addr; -} __packed; /* ALIVE_RES_API_S_VER_3 */ - -/* Error response/notification */ -enum { - FW_ERR_UNKNOWN_CMD = 0x0, - FW_ERR_INVALID_CMD_PARAM = 0x1, - FW_ERR_SERVICE = 0x2, - FW_ERR_ARC_MEMORY = 0x3, - FW_ERR_ARC_CODE = 0x4, - FW_ERR_WATCH_DOG = 0x5, - FW_ERR_WEP_GRP_KEY_INDX = 0x10, - FW_ERR_WEP_KEY_SIZE = 0x11, - FW_ERR_OBSOLETE_FUNC = 0x12, - FW_ERR_UNEXPECTED = 0xFE, - FW_ERR_FATAL = 0xFF -}; - -/** - * struct iwl_error_resp - FW error indication - * ( REPLY_ERROR = 0x2 ) - * @error_type: one of FW_ERR_* - * @cmd_id: the command ID for which the error occured - * @bad_cmd_seq_num: sequence number of the erroneous command - * @error_service: which service created the error, applicable only if - * error_type = 2, otherwise 0 - * @timestamp: TSF in usecs. - */ -struct iwl_error_resp { - __le32 error_type; - u8 cmd_id; - u8 reserved1; - __le16 bad_cmd_seq_num; - __le32 error_service; - __le64 timestamp; -} __packed; - - -/* Common PHY, MAC and Bindings definitions */ - -#define MAX_MACS_IN_BINDING (3) -#define MAX_BINDINGS (4) -#define AUX_BINDING_INDEX (3) -#define MAX_PHYS (4) - -/* Used to extract ID and color from the context dword */ -#define FW_CTXT_ID_POS (0) -#define FW_CTXT_ID_MSK (0xff << FW_CTXT_ID_POS) -#define FW_CTXT_COLOR_POS (8) -#define FW_CTXT_COLOR_MSK (0xff << FW_CTXT_COLOR_POS) -#define FW_CTXT_INVALID (0xffffffff) - -#define FW_CMD_ID_AND_COLOR(_id, _color) ((_id << FW_CTXT_ID_POS) |\ - (_color << FW_CTXT_COLOR_POS)) - -/* Possible actions on PHYs, MACs and Bindings */ -enum { - FW_CTXT_ACTION_STUB = 0, - FW_CTXT_ACTION_ADD, - FW_CTXT_ACTION_MODIFY, - FW_CTXT_ACTION_REMOVE, - FW_CTXT_ACTION_NUM -}; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */ - -/* Time Events */ - -/* Time Event types, according to MAC type */ -enum iwl_time_event_type { - /* BSS Station Events */ - TE_BSS_STA_AGGRESSIVE_ASSOC, - TE_BSS_STA_ASSOC, - TE_BSS_EAP_DHCP_PROT, - TE_BSS_QUIET_PERIOD, - - /* P2P Device Events */ - TE_P2P_DEVICE_DISCOVERABLE, - TE_P2P_DEVICE_LISTEN, - TE_P2P_DEVICE_ACTION_SCAN, - TE_P2P_DEVICE_FULL_SCAN, - - /* P2P Client Events */ - TE_P2P_CLIENT_AGGRESSIVE_ASSOC, - TE_P2P_CLIENT_ASSOC, - TE_P2P_CLIENT_QUIET_PERIOD, - - /* P2P GO Events */ - TE_P2P_GO_ASSOC_PROT, - TE_P2P_GO_REPETITIVE_NOA, - TE_P2P_GO_CT_WINDOW, - - /* WiDi Sync Events */ - TE_WIDI_TX_SYNC, - - /* Channel Switch NoA */ - TE_CHANNEL_SWITCH_PERIOD, - - TE_MAX -}; /* MAC_EVENT_TYPE_API_E_VER_1 */ - - - -/* Time event - defines for command API v1 */ - -/* - * @TE_V1_FRAG_NONE: fragmentation of the time event is NOT allowed. - * @TE_V1_FRAG_SINGLE: fragmentation of the time event is allowed, but only - * the first fragment is scheduled. - * @TE_V1_FRAG_DUAL: fragmentation of the time event is allowed, but only - * the first 2 fragments are scheduled. - * @TE_V1_FRAG_ENDLESS: fragmentation of the time event is allowed, and any - * number of fragments are valid. - * - * Other than the constant defined above, specifying a fragmentation value 'x' - * means that the event can be fragmented but only the first 'x' will be - * scheduled. - */ -enum { - TE_V1_FRAG_NONE = 0, - TE_V1_FRAG_SINGLE = 1, - TE_V1_FRAG_DUAL = 2, - TE_V1_FRAG_ENDLESS = 0xffffffff -}; - -/* If a Time Event can be fragmented, this is the max number of fragments */ -#define TE_V1_FRAG_MAX_MSK 0x0fffffff -/* Repeat the time event endlessly (until removed) */ -#define TE_V1_REPEAT_ENDLESS 0xffffffff -/* If a Time Event has bounded repetitions, this is the maximal value */ -#define TE_V1_REPEAT_MAX_MSK_V1 0x0fffffff - -/* Time Event dependencies: none, on another TE, or in a specific time */ -enum { - TE_V1_INDEPENDENT = 0, - TE_V1_DEP_OTHER = BIT(0), - TE_V1_DEP_TSF = BIT(1), - TE_V1_EVENT_SOCIOPATHIC = BIT(2), -}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */ - -/* - * @TE_V1_NOTIF_NONE: no notifications - * @TE_V1_NOTIF_HOST_EVENT_START: request/receive notification on event start - * @TE_V1_NOTIF_HOST_EVENT_END:request/receive notification on event end - * @TE_V1_NOTIF_INTERNAL_EVENT_START: internal FW use - * @TE_V1_NOTIF_INTERNAL_EVENT_END: internal FW use. - * @TE_V1_NOTIF_HOST_FRAG_START: request/receive notification on frag start - * @TE_V1_NOTIF_HOST_FRAG_END:request/receive notification on frag end - * @TE_V1_NOTIF_INTERNAL_FRAG_START: internal FW use. - * @TE_V1_NOTIF_INTERNAL_FRAG_END: internal FW use. - * - * Supported Time event notifications configuration. - * A notification (both event and fragment) includes a status indicating weather - * the FW was able to schedule the event or not. For fragment start/end - * notification the status is always success. There is no start/end fragment - * notification for monolithic events. - */ -enum { - TE_V1_NOTIF_NONE = 0, - TE_V1_NOTIF_HOST_EVENT_START = BIT(0), - TE_V1_NOTIF_HOST_EVENT_END = BIT(1), - TE_V1_NOTIF_INTERNAL_EVENT_START = BIT(2), - TE_V1_NOTIF_INTERNAL_EVENT_END = BIT(3), - TE_V1_NOTIF_HOST_FRAG_START = BIT(4), - TE_V1_NOTIF_HOST_FRAG_END = BIT(5), - TE_V1_NOTIF_INTERNAL_FRAG_START = BIT(6), - TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7), -}; /* MAC_EVENT_ACTION_API_E_VER_2 */ - -/* Time event - defines for command API */ - -/* - * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed. - * @TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only - * the first fragment is scheduled. - * @TE_V2_FRAG_DUAL: fragmentation of the time event is allowed, but only - * the first 2 fragments are scheduled. - * @TE_V2_FRAG_ENDLESS: fragmentation of the time event is allowed, and any - * number of fragments are valid. - * - * Other than the constant defined above, specifying a fragmentation value 'x' - * means that the event can be fragmented but only the first 'x' will be - * scheduled. - */ -enum { - TE_V2_FRAG_NONE = 0, - TE_V2_FRAG_SINGLE = 1, - TE_V2_FRAG_DUAL = 2, - TE_V2_FRAG_MAX = 0xfe, - TE_V2_FRAG_ENDLESS = 0xff -}; - -/* Repeat the time event endlessly (until removed) */ -#define TE_V2_REPEAT_ENDLESS 0xff -/* If a Time Event has bounded repetitions, this is the maximal value */ -#define TE_V2_REPEAT_MAX 0xfe - -#define TE_V2_PLACEMENT_POS 12 -#define TE_V2_ABSENCE_POS 15 - -/* Time event policy values - * A notification (both event and fragment) includes a status indicating weather - * the FW was able to schedule the event or not. For fragment start/end - * notification the status is always success. There is no start/end fragment - * notification for monolithic events. - * - * @TE_V2_DEFAULT_POLICY: independent, social, present, unoticable - * @TE_V2_NOTIF_HOST_EVENT_START: request/receive notification on event start - * @TE_V2_NOTIF_HOST_EVENT_END:request/receive notification on event end - * @TE_V2_NOTIF_INTERNAL_EVENT_START: internal FW use - * @TE_V2_NOTIF_INTERNAL_EVENT_END: internal FW use. - * @TE_V2_NOTIF_HOST_FRAG_START: request/receive notification on frag start - * @TE_V2_NOTIF_HOST_FRAG_END:request/receive notification on frag end - * @TE_V2_NOTIF_INTERNAL_FRAG_START: internal FW use. - * @TE_V2_NOTIF_INTERNAL_FRAG_END: internal FW use. - * @TE_V2_DEP_OTHER: depends on another time event - * @TE_V2_DEP_TSF: depends on a specific time - * @TE_V2_EVENT_SOCIOPATHIC: can't co-exist with other events of tha same MAC - * @TE_V2_ABSENCE: are we present or absent during the Time Event. - */ -enum { - TE_V2_DEFAULT_POLICY = 0x0, - - /* notifications (event start/stop, fragment start/stop) */ - TE_V2_NOTIF_HOST_EVENT_START = BIT(0), - TE_V2_NOTIF_HOST_EVENT_END = BIT(1), - TE_V2_NOTIF_INTERNAL_EVENT_START = BIT(2), - TE_V2_NOTIF_INTERNAL_EVENT_END = BIT(3), - - TE_V2_NOTIF_HOST_FRAG_START = BIT(4), - TE_V2_NOTIF_HOST_FRAG_END = BIT(5), - TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6), - TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7), - T2_V2_START_IMMEDIATELY = BIT(11), - - TE_V2_NOTIF_MSK = 0xff, - - /* placement characteristics */ - TE_V2_DEP_OTHER = BIT(TE_V2_PLACEMENT_POS), - TE_V2_DEP_TSF = BIT(TE_V2_PLACEMENT_POS + 1), - TE_V2_EVENT_SOCIOPATHIC = BIT(TE_V2_PLACEMENT_POS + 2), - - /* are we present or absent during the Time Event. */ - TE_V2_ABSENCE = BIT(TE_V2_ABSENCE_POS), -}; - -/** - * struct iwl_time_event_cmd_api - configuring Time Events - * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also - * with version 1. determined by IWL_UCODE_TLV_FLAGS) - * ( TIME_EVENT_CMD = 0x29 ) - * @id_and_color: ID and color of the relevant MAC - * @action: action to perform, one of FW_CTXT_ACTION_* - * @id: this field has two meanings, depending on the action: - * If the action is ADD, then it means the type of event to add. - * For all other actions it is the unique event ID assigned when the - * event was added by the FW. - * @apply_time: When to start the Time Event (in GP2) - * @max_delay: maximum delay to event's start (apply time), in TU - * @depends_on: the unique ID of the event we depend on (if any) - * @interval: interval between repetitions, in TU - * @duration: duration of event in TU - * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS - * @max_frags: maximal number of fragments the Time Event can be divided to - * @policy: defines whether uCode shall notify the host or other uCode modules - * on event and/or fragment start and/or end - * using one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF - * TE_EVENT_SOCIOPATHIC - * using TE_ABSENCE and using TE_NOTIF_* - */ -struct iwl_time_event_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - __le32 id; - /* MAC_TIME_EVENT_DATA_API_S_VER_2 */ - __le32 apply_time; - __le32 max_delay; - __le32 depends_on; - __le32 interval; - __le32 duration; - u8 repeat; - u8 max_frags; - __le16 policy; -} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_2 */ - -/** - * struct iwl_time_event_resp - response structure to iwl_time_event_cmd - * @status: bit 0 indicates success, all others specify errors - * @id: the Time Event type - * @unique_id: the unique ID assigned (in ADD) or given (others) to the TE - * @id_and_color: ID and color of the relevant MAC - */ -struct iwl_time_event_resp { - __le32 status; - __le32 id; - __le32 unique_id; - __le32 id_and_color; -} __packed; /* MAC_TIME_EVENT_RSP_API_S_VER_1 */ - -/** - * struct iwl_time_event_notif - notifications of time event start/stop - * ( TIME_EVENT_NOTIFICATION = 0x2a ) - * @timestamp: action timestamp in GP2 - * @session_id: session's unique id - * @unique_id: unique id of the Time Event itself - * @id_and_color: ID and color of the relevant MAC - * @action: one of TE_NOTIF_START or TE_NOTIF_END - * @status: true if scheduled, false otherwise (not executed) - */ -struct iwl_time_event_notif { - __le32 timestamp; - __le32 session_id; - __le32 unique_id; - __le32 id_and_color; - __le32 action; - __le32 status; -} __packed; /* MAC_TIME_EVENT_NTFY_API_S_VER_1 */ - - -/* Bindings and Time Quota */ - -/** - * struct iwl_binding_cmd - configuring bindings - * ( BINDING_CONTEXT_CMD = 0x2b ) - * @id_and_color: ID and color of the relevant Binding - * @action: action to perform, one of FW_CTXT_ACTION_* - * @macs: array of MAC id and colors which belong to the binding - * @phy: PHY id and color which belongs to the binding - */ -struct iwl_binding_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - /* BINDING_DATA_API_S_VER_1 */ - __le32 macs[MAX_MACS_IN_BINDING]; - __le32 phy; -} __packed; /* BINDING_CMD_API_S_VER_1 */ - -/* The maximal number of fragments in the FW's schedule session */ -#define IWL_MVM_MAX_QUOTA 128 - -/** - * struct iwl_time_quota_data - configuration of time quota per binding - * @id_and_color: ID and color of the relevant Binding - * @quota: absolute time quota in TU. The scheduler will try to divide the - * remainig quota (after Time Events) according to this quota. - * @max_duration: max uninterrupted context duration in TU - */ -struct iwl_time_quota_data { - __le32 id_and_color; - __le32 quota; - __le32 max_duration; -} __packed; /* TIME_QUOTA_DATA_API_S_VER_1 */ - -/** - * struct iwl_time_quota_cmd - configuration of time quota between bindings - * ( TIME_QUOTA_CMD = 0x2c ) - * @quotas: allocations per binding - */ -struct iwl_time_quota_cmd { - struct iwl_time_quota_data quotas[MAX_BINDINGS]; -} __packed; /* TIME_QUOTA_ALLOCATION_CMD_API_S_VER_1 */ - - -/* PHY context */ - -/* Supported bands */ -#define PHY_BAND_5 (0) -#define PHY_BAND_24 (1) - -/* Supported channel width, vary if there is VHT support */ -#define PHY_VHT_CHANNEL_MODE20 (0x0) -#define PHY_VHT_CHANNEL_MODE40 (0x1) -#define PHY_VHT_CHANNEL_MODE80 (0x2) -#define PHY_VHT_CHANNEL_MODE160 (0x3) - -/* - * Control channel position: - * For legacy set bit means upper channel, otherwise lower. - * For VHT - bit-2 marks if the control is lower/upper relative to center-freq - * bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0. - * center_freq - * | - * 40Mhz |_______|_______| - * 80Mhz |_______|_______|_______|_______| - * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______| - * code 011 010 001 000 | 100 101 110 111 - */ -#define PHY_VHT_CTRL_POS_1_BELOW (0x0) -#define PHY_VHT_CTRL_POS_2_BELOW (0x1) -#define PHY_VHT_CTRL_POS_3_BELOW (0x2) -#define PHY_VHT_CTRL_POS_4_BELOW (0x3) -#define PHY_VHT_CTRL_POS_1_ABOVE (0x4) -#define PHY_VHT_CTRL_POS_2_ABOVE (0x5) -#define PHY_VHT_CTRL_POS_3_ABOVE (0x6) -#define PHY_VHT_CTRL_POS_4_ABOVE (0x7) - -/* - * @band: PHY_BAND_* - * @channel: channel number - * @width: PHY_[VHT|LEGACY]_CHANNEL_* - * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_* - */ -struct iwl_fw_channel_info { - u8 band; - u8 channel; - u8 width; - u8 ctrl_pos; -} __packed; - -#define PHY_RX_CHAIN_DRIVER_FORCE_POS (0) -#define PHY_RX_CHAIN_DRIVER_FORCE_MSK \ - (0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS) -#define PHY_RX_CHAIN_VALID_POS (1) -#define PHY_RX_CHAIN_VALID_MSK \ - (0x7 << PHY_RX_CHAIN_VALID_POS) -#define PHY_RX_CHAIN_FORCE_SEL_POS (4) -#define PHY_RX_CHAIN_FORCE_SEL_MSK \ - (0x7 << PHY_RX_CHAIN_FORCE_SEL_POS) -#define PHY_RX_CHAIN_FORCE_MIMO_SEL_POS (7) -#define PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK \ - (0x7 << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS) -#define PHY_RX_CHAIN_CNT_POS (10) -#define PHY_RX_CHAIN_CNT_MSK \ - (0x3 << PHY_RX_CHAIN_CNT_POS) -#define PHY_RX_CHAIN_MIMO_CNT_POS (12) -#define PHY_RX_CHAIN_MIMO_CNT_MSK \ - (0x3 << PHY_RX_CHAIN_MIMO_CNT_POS) -#define PHY_RX_CHAIN_MIMO_FORCE_POS (14) -#define PHY_RX_CHAIN_MIMO_FORCE_MSK \ - (0x1 << PHY_RX_CHAIN_MIMO_FORCE_POS) - -/* TODO: fix the value, make it depend on firmware at runtime? */ -#define NUM_PHY_CTX 3 - -/* TODO: complete missing documentation */ -/** - * struct iwl_phy_context_cmd - config of the PHY context - * ( PHY_CONTEXT_CMD = 0x8 ) - * @id_and_color: ID and color of the relevant Binding - * @action: action to perform, one of FW_CTXT_ACTION_* - * @apply_time: 0 means immediate apply and context switch. - * other value means apply new params after X usecs - * @tx_param_color: ??? - * @channel_info: - * @txchain_info: ??? - * @rxchain_info: ??? - * @acquisition_data: ??? - * @dsp_cfg_flags: set to 0 - */ -struct iwl_phy_context_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - /* PHY_CONTEXT_DATA_API_S_VER_1 */ - __le32 apply_time; - __le32 tx_param_color; - struct iwl_fw_channel_info ci; - __le32 txchain_info; - __le32 rxchain_info; - __le32 acquisition_data; - __le32 dsp_cfg_flags; -} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */ - -/* - * Aux ROC command - * - * Command requests the firmware to create a time event for a certain duration - * and remain on the given channel. This is done by using the Aux framework in - * the FW. - * The command was first used for Hot Spot issues - but can be used regardless - * to Hot Spot. - * - * ( HOT_SPOT_CMD 0x53 ) - * - * @id_and_color: ID and color of the MAC - * @action: action to perform, one of FW_CTXT_ACTION_* - * @event_unique_id: If the action FW_CTXT_ACTION_REMOVE then the - * event_unique_id should be the id of the time event assigned by ucode. - * Otherwise ignore the event_unique_id. - * @sta_id_and_color: station id and color, resumed during "Remain On Channel" - * activity. - * @channel_info: channel info - * @node_addr: Our MAC Address - * @reserved: reserved for alignment - * @apply_time: GP2 value to start (should always be the current GP2 value) - * @apply_time_max_delay: Maximum apply time delay value in TU. Defines max - * time by which start of the event is allowed to be postponed. - * @duration: event duration in TU To calculate event duration: - * timeEventDuration = min(duration, remainingQuota) - */ -struct iwl_hs20_roc_req { - /* COMMON_INDEX_HDR_API_S_VER_1 hdr */ - __le32 id_and_color; - __le32 action; - __le32 event_unique_id; - __le32 sta_id_and_color; - struct iwl_fw_channel_info channel_info; - u8 node_addr[ETH_ALEN]; - __le16 reserved; - __le32 apply_time; - __le32 apply_time_max_delay; - __le32 duration; -} __packed; /* HOT_SPOT_CMD_API_S_VER_1 */ - -/* - * values for AUX ROC result values - */ -enum iwl_mvm_hot_spot { - HOT_SPOT_RSP_STATUS_OK, - HOT_SPOT_RSP_STATUS_TOO_MANY_EVENTS, - HOT_SPOT_MAX_NUM_OF_SESSIONS, -}; - -/* - * Aux ROC command response - * - * In response to iwl_hs20_roc_req the FW sends this command to notify the - * driver the uid of the timevent. - * - * ( HOT_SPOT_CMD 0x53 ) - * - * @event_unique_id: Unique ID of time event assigned by ucode - * @status: Return status 0 is success, all the rest used for specific errors - */ -struct iwl_hs20_roc_res { - __le32 event_unique_id; - __le32 status; -} __packed; /* HOT_SPOT_RSP_API_S_VER_1 */ - -/** - * struct iwl_radio_version_notif - information on the radio version - * ( RADIO_VERSION_NOTIFICATION = 0x68 ) - * @radio_flavor: - * @radio_step: - * @radio_dash: - */ -struct iwl_radio_version_notif { - __le32 radio_flavor; - __le32 radio_step; - __le32 radio_dash; -} __packed; /* RADIO_VERSION_NOTOFICATION_S_VER_1 */ - -enum iwl_card_state_flags { - CARD_ENABLED = 0x00, - HW_CARD_DISABLED = 0x01, - SW_CARD_DISABLED = 0x02, - CT_KILL_CARD_DISABLED = 0x04, - HALT_CARD_DISABLED = 0x08, - CARD_DISABLED_MSK = 0x0f, - CARD_IS_RX_ON = 0x10, -}; - -/** - * struct iwl_radio_version_notif - information on the radio version - * ( CARD_STATE_NOTIFICATION = 0xa1 ) - * @flags: %iwl_card_state_flags - */ -struct iwl_card_state_notif { - __le32 flags; -} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */ - -/** - * struct iwl_missed_beacons_notif - information on missed beacons - * ( MISSED_BEACONS_NOTIFICATION = 0xa2 ) - * @mac_id: interface ID - * @consec_missed_beacons_since_last_rx: number of consecutive missed - * beacons since last RX. - * @consec_missed_beacons: number of consecutive missed beacons - * @num_expected_beacons: - * @num_recvd_beacons: - */ -struct iwl_missed_beacons_notif { - __le32 mac_id; - __le32 consec_missed_beacons_since_last_rx; - __le32 consec_missed_beacons; - __le32 num_expected_beacons; - __le32 num_recvd_beacons; -} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */ - -/** - * struct iwl_mfuart_load_notif - mfuart image version & status - * ( MFUART_LOAD_NOTIFICATION = 0xb1 ) - * @installed_ver: installed image version - * @external_ver: external image version - * @status: MFUART loading status - * @duration: MFUART loading time -*/ -struct iwl_mfuart_load_notif { - __le32 installed_ver; - __le32 external_ver; - __le32 status; - __le32 duration; -} __packed; /*MFU_LOADER_NTFY_API_S_VER_1*/ - -/** - * struct iwl_set_calib_default_cmd - set default value for calibration. - * ( SET_CALIB_DEFAULT_CMD = 0x8e ) - * @calib_index: the calibration to set value for - * @length: of data - * @data: the value to set for the calibration result - */ -struct iwl_set_calib_default_cmd { - __le16 calib_index; - __le16 length; - u8 data[0]; -} __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */ - -#define MAX_PORT_ID_NUM 2 -#define MAX_MCAST_FILTERING_ADDRESSES 256 - -/** - * struct iwl_mcast_filter_cmd - configure multicast filter. - * @filter_own: Set 1 to filter out multicast packets sent by station itself - * @port_id: Multicast MAC addresses array specifier. This is a strange way - * to identify network interface adopted in host-device IF. - * It is used by FW as index in array of addresses. This array has - * MAX_PORT_ID_NUM members. - * @count: Number of MAC addresses in the array - * @pass_all: Set 1 to pass all multicast packets. - * @bssid: current association BSSID. - * @addr_list: Place holder for array of MAC addresses. - * IMPORTANT: add padding if necessary to ensure DWORD alignment. - */ -struct iwl_mcast_filter_cmd { - u8 filter_own; - u8 port_id; - u8 count; - u8 pass_all; - u8 bssid[6]; - u8 reserved[2]; - u8 addr_list[0]; -} __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */ - -#define MAX_BCAST_FILTERS 8 -#define MAX_BCAST_FILTER_ATTRS 2 - -/** - * enum iwl_mvm_bcast_filter_attr_offset - written by fw for each Rx packet - * @BCAST_FILTER_OFFSET_PAYLOAD_START: offset is from payload start. - * @BCAST_FILTER_OFFSET_IP_END: offset is from ip header end (i.e. - * start of ip payload). - */ -enum iwl_mvm_bcast_filter_attr_offset { - BCAST_FILTER_OFFSET_PAYLOAD_START = 0, - BCAST_FILTER_OFFSET_IP_END = 1, -}; - -/** - * struct iwl_fw_bcast_filter_attr - broadcast filter attribute - * @offset_type: &enum iwl_mvm_bcast_filter_attr_offset. - * @offset: starting offset of this pattern. - * @val: value to match - big endian (MSB is the first - * byte to match from offset pos). - * @mask: mask to match (big endian). - */ -struct iwl_fw_bcast_filter_attr { - u8 offset_type; - u8 offset; - __le16 reserved1; - __be32 val; - __be32 mask; -} __packed; /* BCAST_FILTER_ATT_S_VER_1 */ - -/** - * enum iwl_mvm_bcast_filter_frame_type - filter frame type - * @BCAST_FILTER_FRAME_TYPE_ALL: consider all frames. - * @BCAST_FILTER_FRAME_TYPE_IPV4: consider only ipv4 frames - */ -enum iwl_mvm_bcast_filter_frame_type { - BCAST_FILTER_FRAME_TYPE_ALL = 0, - BCAST_FILTER_FRAME_TYPE_IPV4 = 1, -}; - -/** - * struct iwl_fw_bcast_filter - broadcast filter - * @discard: discard frame (1) or let it pass (0). - * @frame_type: &enum iwl_mvm_bcast_filter_frame_type. - * @num_attrs: number of valid attributes in this filter. - * @attrs: attributes of this filter. a filter is considered matched - * only when all its attributes are matched (i.e. AND relationship) - */ -struct iwl_fw_bcast_filter { - u8 discard; - u8 frame_type; - u8 num_attrs; - u8 reserved1; - struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS]; -} __packed; /* BCAST_FILTER_S_VER_1 */ - -/** - * struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration. - * @default_discard: default action for this mac (discard (1) / pass (0)). - * @attached_filters: bitmap of relevant filters for this mac. - */ -struct iwl_fw_bcast_mac { - u8 default_discard; - u8 reserved1; - __le16 attached_filters; -} __packed; /* BCAST_MAC_CONTEXT_S_VER_1 */ - -/** - * struct iwl_bcast_filter_cmd - broadcast filtering configuration - * @disable: enable (0) / disable (1) - * @max_bcast_filters: max number of filters (MAX_BCAST_FILTERS) - * @max_macs: max number of macs (NUM_MAC_INDEX_DRIVER) - * @filters: broadcast filters - * @macs: broadcast filtering configuration per-mac - */ -struct iwl_bcast_filter_cmd { - u8 disable; - u8 max_bcast_filters; - u8 max_macs; - u8 reserved1; - struct iwl_fw_bcast_filter filters[MAX_BCAST_FILTERS]; - struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER]; -} __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */ - -/* - * enum iwl_mvm_marker_id - maker ids - * - * The ids for different type of markers to insert into the usniffer logs - */ -enum iwl_mvm_marker_id { - MARKER_ID_TX_FRAME_LATENCY = 1, -}; /* MARKER_ID_API_E_VER_1 */ - -/** - * struct iwl_mvm_marker - mark info into the usniffer logs - * - * (MARKER_CMD = 0xcb) - * - * Mark the UTC time stamp into the usniffer logs together with additional - * metadata, so the usniffer output can be parsed. - * In the command response the ucode will return the GP2 time. - * - * @dw_len: The amount of dwords following this byte including this byte. - * @marker_id: A unique marker id (iwl_mvm_marker_id). - * @reserved: reserved. - * @timestamp: in milliseconds since 1970-01-01 00:00:00 UTC - * @metadata: additional meta data that will be written to the unsiffer log - */ -struct iwl_mvm_marker { - u8 dwLen; - u8 markerId; - __le16 reserved; - __le64 timestamp; - __le32 metadata[0]; -} __packed; /* MARKER_API_S_VER_1 */ - -/* - * enum iwl_dc2dc_config_id - flag ids - * - * Ids of dc2dc configuration flags - */ -enum iwl_dc2dc_config_id { - DCDC_LOW_POWER_MODE_MSK_SET = 0x1, /* not used */ - DCDC_FREQ_TUNE_SET = 0x2, -}; /* MARKER_ID_API_E_VER_1 */ - -/** - * struct iwl_dc2dc_config_cmd - configure dc2dc values - * - * (DC2DC_CONFIG_CMD = 0x83) - * - * Set/Get & configure dc2dc values. - * The command always returns the current dc2dc values. - * - * @flags: set/get dc2dc - * @enable_low_power_mode: not used. - * @dc2dc_freq_tune0: frequency divider - digital domain - * @dc2dc_freq_tune1: frequency divider - analog domain - */ -struct iwl_dc2dc_config_cmd { - __le32 flags; - __le32 enable_low_power_mode; /* not used */ - __le32 dc2dc_freq_tune0; - __le32 dc2dc_freq_tune1; -} __packed; /* DC2DC_CONFIG_CMD_API_S_VER_1 */ - -/** - * struct iwl_dc2dc_config_resp - response for iwl_dc2dc_config_cmd - * - * Current dc2dc values returned by the FW. - * - * @dc2dc_freq_tune0: frequency divider - digital domain - * @dc2dc_freq_tune1: frequency divider - analog domain - */ -struct iwl_dc2dc_config_resp { - __le32 dc2dc_freq_tune0; - __le32 dc2dc_freq_tune1; -} __packed; /* DC2DC_CONFIG_RESP_API_S_VER_1 */ - -/*********************************** - * Smart Fifo API - ***********************************/ -/* Smart Fifo state */ -enum iwl_sf_state { - SF_LONG_DELAY_ON = 0, /* should never be called by driver */ - SF_FULL_ON, - SF_UNINIT, - SF_INIT_OFF, - SF_HW_NUM_STATES -}; - -/* Smart Fifo possible scenario */ -enum iwl_sf_scenario { - SF_SCENARIO_SINGLE_UNICAST, - SF_SCENARIO_AGG_UNICAST, - SF_SCENARIO_MULTICAST, - SF_SCENARIO_BA_RESP, - SF_SCENARIO_TX_RESP, - SF_NUM_SCENARIO -}; - -#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */ -#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */ - -/* smart FIFO default values */ -#define SF_W_MARK_SISO 6144 -#define SF_W_MARK_MIMO2 8192 -#define SF_W_MARK_MIMO3 6144 -#define SF_W_MARK_LEGACY 4096 -#define SF_W_MARK_SCAN 4096 - -/* SF Scenarios timers for default configuration (aligned to 32 uSec) */ -#define SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_SINGLE_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_AGG_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_AGG_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_MCAST_IDLE_TIMER_DEF 160 /* 150 mSec */ -#define SF_MCAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_BA_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_BA_AGING_TIMER_DEF 400 /* 0.4 mSec */ -#define SF_TX_RE_IDLE_TIMER_DEF 160 /* 150 uSec */ -#define SF_TX_RE_AGING_TIMER_DEF 400 /* 0.4 mSec */ - -/* SF Scenarios timers for BSS MAC configuration (aligned to 32 uSec) */ -#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */ -#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */ -#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */ -#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */ -#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */ -#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */ -#define SF_BA_IDLE_TIMER 320 /* 300 uSec */ -#define SF_BA_AGING_TIMER 2016 /* 2 mSec */ -#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */ -#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */ - -#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */ - -#define SF_CFG_DUMMY_NOTIF_OFF BIT(16) - -/** - * Smart Fifo configuration command. - * @state: smart fifo state, types listed in enum %iwl_sf_sate. - * @watermark: Minimum allowed availabe free space in RXF for transient state. - * @long_delay_timeouts: aging and idle timer values for each scenario - * in long delay state. - * @full_on_timeouts: timer values for each scenario in full on state. - */ -struct iwl_sf_cfg_cmd { - __le32 state; - __le32 watermark[SF_TRANSIENT_STATES_NUMBER]; - __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; - __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; -} __packed; /* SF_CFG_API_S_VER_2 */ - -/*********************************** - * Location Aware Regulatory (LAR) API - MCC updates - ***********************************/ - -/** - * struct iwl_mcc_update_cmd - Request the device to update geographic - * regulatory profile according to the given MCC (Mobile Country Code). - * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. - * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the - * MCC in the cmd response will be the relevant MCC in the NVM. - * @mcc: given mobile country code - * @source_id: the source from where we got the MCC, see iwl_mcc_source - * @reserved: reserved for alignment - */ -struct iwl_mcc_update_cmd { - __le16 mcc; - u8 source_id; - u8 reserved; -} __packed; /* LAR_UPDATE_MCC_CMD_API_S */ - -/** - * iwl_mcc_update_resp - response to MCC_UPDATE_CMD. - * Contains the new channel control profile map, if changed, and the new MCC - * (mobile country code). - * The new MCC may be different than what was requested in MCC_UPDATE_CMD. - * @status: see &enum iwl_mcc_update_status - * @mcc: the new applied MCC - * @cap: capabilities for all channels which matches the MCC - * @source_id: the MCC source, see iwl_mcc_source - * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 - * channels, depending on platform) - * @channels: channel control data map, DWORD for each channel. Only the first - * 16bits are used. - */ -struct iwl_mcc_update_resp { - __le32 status; - __le16 mcc; - u8 cap; - u8 source_id; - __le32 n_channels; - __le32 channels[0]; -} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S */ - -/** - * struct iwl_mcc_chub_notif - chub notifies of mcc change - * (MCC_CHUB_UPDATE_CMD = 0xc9) - * The Chub (Communication Hub, CommsHUB) is a HW component that connects to - * the cellular and connectivity cores that gets updates of the mcc, and - * notifies the ucode directly of any mcc change. - * The ucode requests the driver to request the device to update geographic - * regulatory profile according to the given MCC (Mobile Country Code). - * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. - * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the - * MCC in the cmd response will be the relevant MCC in the NVM. - * @mcc: given mobile country code - * @source_id: identity of the change originator, see iwl_mcc_source - * @reserved1: reserved for alignment - */ -struct iwl_mcc_chub_notif { - u16 mcc; - u8 source_id; - u8 reserved1; -} __packed; /* LAR_MCC_NOTIFY_S */ - -enum iwl_mcc_update_status { - MCC_RESP_NEW_CHAN_PROFILE, - MCC_RESP_SAME_CHAN_PROFILE, - MCC_RESP_INVALID, - MCC_RESP_NVM_DISABLED, - MCC_RESP_ILLEGAL, - MCC_RESP_LOW_PRIORITY, -}; - -enum iwl_mcc_source { - MCC_SOURCE_OLD_FW = 0, - MCC_SOURCE_ME = 1, - MCC_SOURCE_BIOS = 2, - MCC_SOURCE_3G_LTE_HOST = 3, - MCC_SOURCE_3G_LTE_DEVICE = 4, - MCC_SOURCE_WIFI = 5, - MCC_SOURCE_RESERVED = 6, - MCC_SOURCE_DEFAULT = 7, - MCC_SOURCE_UNINITIALIZED = 8, - MCC_SOURCE_GET_CURRENT = 0x10 -}; - -/* DTS measurements */ - -enum iwl_dts_measurement_flags { - DTS_TRIGGER_CMD_FLAGS_TEMP = BIT(0), - DTS_TRIGGER_CMD_FLAGS_VOLT = BIT(1), -}; - -/** - * iwl_dts_measurement_cmd - request DTS temperature and/or voltage measurements - * - * @flags: indicates which measurements we want as specified in &enum - * iwl_dts_measurement_flags - */ -struct iwl_dts_measurement_cmd { - __le32 flags; -} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_CMD_S */ - -/** -* enum iwl_dts_control_measurement_mode - DTS measurement type -* @DTS_AUTOMATIC: Automatic mode (full SW control). Provide temperature read -* back (latest value. Not waiting for new value). Use automatic -* SW DTS configuration. -* @DTS_REQUEST_READ: Request DTS read. Configure DTS with manual settings, -* trigger DTS reading and provide read back temperature read -* when available. -* @DTS_OVER_WRITE: over-write the DTS temperatures in the SW until next read -* @DTS_DIRECT_WITHOUT_MEASURE: DTS returns its latest temperature result, -* without measurement trigger. -*/ -enum iwl_dts_control_measurement_mode { - DTS_AUTOMATIC = 0, - DTS_REQUEST_READ = 1, - DTS_OVER_WRITE = 2, - DTS_DIRECT_WITHOUT_MEASURE = 3, -}; - -/** -* enum iwl_dts_used - DTS to use or used for measurement in the DTS request -* @DTS_USE_TOP: Top -* @DTS_USE_CHAIN_A: chain A -* @DTS_USE_CHAIN_B: chain B -* @DTS_USE_CHAIN_C: chain C -* @XTAL_TEMPERATURE - read temperature from xtal -*/ -enum iwl_dts_used { - DTS_USE_TOP = 0, - DTS_USE_CHAIN_A = 1, - DTS_USE_CHAIN_B = 2, - DTS_USE_CHAIN_C = 3, - XTAL_TEMPERATURE = 4, -}; - -/** -* enum iwl_dts_bit_mode - bit-mode to use in DTS request read mode -* @DTS_BIT6_MODE: bit 6 mode -* @DTS_BIT8_MODE: bit 8 mode -*/ -enum iwl_dts_bit_mode { - DTS_BIT6_MODE = 0, - DTS_BIT8_MODE = 1, -}; - -/** - * iwl_ext_dts_measurement_cmd - request extended DTS temperature measurements - * @control_mode: see &enum iwl_dts_control_measurement_mode - * @temperature: used when over write DTS mode is selected - * @sensor: set temperature sensor to use. See &enum iwl_dts_used - * @avg_factor: average factor to DTS in request DTS read mode - * @bit_mode: value defines the DTS bit mode to use. See &enum iwl_dts_bit_mode - * @step_duration: step duration for the DTS - */ -struct iwl_ext_dts_measurement_cmd { - __le32 control_mode; - __le32 temperature; - __le32 sensor; - __le32 avg_factor; - __le32 bit_mode; - __le32 step_duration; -} __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */ - -/** - * iwl_dts_measurement_notif - notification received with the measurements - * - * @temp: the measured temperature - * @voltage: the measured voltage - */ -struct iwl_dts_measurement_notif { - __le32 temp; - __le32 voltage; -} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S */ - -/*********************************** - * TDLS API - ***********************************/ - -/* Type of TDLS request */ -enum iwl_tdls_channel_switch_type { - TDLS_SEND_CHAN_SW_REQ = 0, - TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH, - TDLS_MOVE_CH, -}; /* TDLS_STA_CHANNEL_SWITCH_CMD_TYPE_API_E_VER_1 */ - -/** - * Switch timing sub-element in a TDLS channel-switch command - * @frame_timestamp: GP2 timestamp of channel-switch request/response packet - * received from peer - * @max_offchan_duration: What amount of microseconds out of a DTIM is given - * to the TDLS off-channel communication. For instance if the DTIM is - * 200TU and the TDLS peer is to be given 25% of the time, the value - * given will be 50TU, or 50 * 1024 if translated into microseconds. - * @switch_time: switch time the peer sent in its channel switch timing IE - * @switch_timout: switch timeout the peer sent in its channel switch timing IE - */ -struct iwl_tdls_channel_switch_timing { - __le32 frame_timestamp; /* GP2 time of peer packet Rx */ - __le32 max_offchan_duration; /* given in micro-seconds */ - __le32 switch_time; /* given in micro-seconds */ - __le32 switch_timeout; /* given in micro-seconds */ -} __packed; /* TDLS_STA_CHANNEL_SWITCH_TIMING_DATA_API_S_VER_1 */ - -#define IWL_TDLS_CH_SW_FRAME_MAX_SIZE 200 - -/** - * TDLS channel switch frame template - * - * A template representing a TDLS channel-switch request or response frame - * - * @switch_time_offset: offset to the channel switch timing IE in the template - * @tx_cmd: Tx parameters for the frame - * @data: frame data - */ -struct iwl_tdls_channel_switch_frame { - __le32 switch_time_offset; - struct iwl_tx_cmd tx_cmd; - u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE]; -} __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */ - -/** - * TDLS channel switch command - * - * The command is sent to initiate a channel switch and also in response to - * incoming TDLS channel-switch request/response packets from remote peers. - * - * @switch_type: see &enum iwl_tdls_channel_switch_type - * @peer_sta_id: station id of TDLS peer - * @ci: channel we switch to - * @timing: timing related data for command - * @frame: channel-switch request/response template, depending to switch_type - */ -struct iwl_tdls_channel_switch_cmd { - u8 switch_type; - __le32 peer_sta_id; - struct iwl_fw_channel_info ci; - struct iwl_tdls_channel_switch_timing timing; - struct iwl_tdls_channel_switch_frame frame; -} __packed; /* TDLS_STA_CHANNEL_SWITCH_CMD_API_S_VER_1 */ - -/** - * TDLS channel switch start notification - * - * @status: non-zero on success - * @offchannel_duration: duration given in microseconds - * @sta_id: peer currently performing the channel-switch with - */ -struct iwl_tdls_channel_switch_notif { - __le32 status; - __le32 offchannel_duration; - __le32 sta_id; -} __packed; /* TDLS_STA_CHANNEL_SWITCH_NTFY_API_S_VER_1 */ - -/** - * TDLS station info - * - * @sta_id: station id of the TDLS peer - * @tx_to_peer_tid: TID reserved vs. the peer for FW based Tx - * @tx_to_peer_ssn: initial SSN the FW should use for Tx on its TID vs the peer - * @is_initiator: 1 if the peer is the TDLS link initiator, 0 otherwise - */ -struct iwl_tdls_sta_info { - u8 sta_id; - u8 tx_to_peer_tid; - __le16 tx_to_peer_ssn; - __le32 is_initiator; -} __packed; /* TDLS_STA_INFO_VER_1 */ - -/** - * TDLS basic config command - * - * @id_and_color: MAC id and color being configured - * @tdls_peer_count: amount of currently connected TDLS peers - * @tx_to_ap_tid: TID reverved vs. the AP for FW based Tx - * @tx_to_ap_ssn: initial SSN the FW should use for Tx on its TID vs. the AP - * @sta_info: per-station info. Only the first tdls_peer_count entries are set - * @pti_req_data_offset: offset of network-level data for the PTI template - * @pti_req_tx_cmd: Tx parameters for PTI request template - * @pti_req_template: PTI request template data - */ -struct iwl_tdls_config_cmd { - __le32 id_and_color; /* mac id and color */ - u8 tdls_peer_count; - u8 tx_to_ap_tid; - __le16 tx_to_ap_ssn; - struct iwl_tdls_sta_info sta_info[IWL_MVM_TDLS_STA_COUNT]; - - __le32 pti_req_data_offset; - struct iwl_tx_cmd pti_req_tx_cmd; - u8 pti_req_template[0]; -} __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */ - -/** - * TDLS per-station config information from FW - * - * @sta_id: station id of the TDLS peer - * @tx_to_peer_last_seq: last sequence number used by FW during FW-based Tx to - * the peer - */ -struct iwl_tdls_config_sta_info_res { - __le16 sta_id; - __le16 tx_to_peer_last_seq; -} __packed; /* TDLS_STA_INFO_RSP_VER_1 */ - -/** - * TDLS config information from FW - * - * @tx_to_ap_last_seq: last sequence number used by FW during FW-based Tx to AP - * @sta_info: per-station TDLS config information - */ -struct iwl_tdls_config_res { - __le32 tx_to_ap_last_seq; - struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT]; -} __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */ - -#define TX_FIFO_MAX_NUM 8 -#define RX_FIFO_MAX_NUM 2 - -/** - * Shared memory configuration information from the FW - * - * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not - * accessible) - * @shared_mem_size: shared memory size - * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to - * 0x0 as accessible only via DBGM RDAT) - * @sample_buff_size: internal sample buff size - * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre - * 8000 HW set to 0x0 as not accessible) - * @txfifo_size: size of TXF0 ... TXF7 - * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0 - * @page_buff_addr: used by UMAC and performance debug (page miss analysis), - * when paging is not supported this should be 0 - * @page_buff_size: size of %page_buff_addr - */ -struct iwl_shared_mem_cfg { - __le32 shared_mem_addr; - __le32 shared_mem_size; - __le32 sample_buff_addr; - __le32 sample_buff_size; - __le32 txfifo_addr; - __le32 txfifo_size[TX_FIFO_MAX_NUM]; - __le32 rxfifo_size[RX_FIFO_MAX_NUM]; - __le32 page_buff_addr; - __le32 page_buff_size; -} __packed; /* SHARED_MEM_ALLOC_API_S_VER_1 */ - -#endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c deleted file mode 100644 index d906fa13ba97..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ /dev/null @@ -1,1166 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include - -#include "iwl-trans.h" -#include "iwl-op-mode.h" -#include "iwl-fw.h" -#include "iwl-debug.h" -#include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */ -#include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */ -#include "iwl-prph.h" -#include "iwl-eeprom-parse.h" - -#include "mvm.h" -#include "iwl-phy-db.h" - -#define MVM_UCODE_ALIVE_TIMEOUT HZ -#define MVM_UCODE_CALIB_TIMEOUT (2*HZ) - -#define UCODE_VALID_OK cpu_to_le32(0x1) - -struct iwl_mvm_alive_data { - bool valid; - u32 scd_base_addr; -}; - -static inline const struct fw_img * -iwl_get_ucode_image(struct iwl_mvm *mvm, enum iwl_ucode_type ucode_type) -{ - if (ucode_type >= IWL_UCODE_TYPE_MAX) - return NULL; - - return &mvm->fw->img[ucode_type]; -} - -static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant) -{ - struct iwl_tx_ant_cfg_cmd tx_ant_cmd = { - .valid = cpu_to_le32(valid_tx_ant), - }; - - IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant); - return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, 0, - sizeof(tx_ant_cmd), &tx_ant_cmd); -} - -static void iwl_free_fw_paging(struct iwl_mvm *mvm) -{ - int i; - - if (!mvm->fw_paging_db[0].fw_paging_block) - return; - - for (i = 0; i < NUM_OF_FW_PAGING_BLOCKS; i++) { - if (!mvm->fw_paging_db[i].fw_paging_block) { - IWL_DEBUG_FW(mvm, - "Paging: block %d already freed, continue to next page\n", - i); - - continue; - } - - __free_pages(mvm->fw_paging_db[i].fw_paging_block, - get_order(mvm->fw_paging_db[i].fw_paging_size)); - } - kfree(mvm->trans->paging_download_buf); - memset(mvm->fw_paging_db, 0, sizeof(mvm->fw_paging_db)); -} - -static int iwl_fill_paging_mem(struct iwl_mvm *mvm, const struct fw_img *image) -{ - int sec_idx, idx; - u32 offset = 0; - - /* - * find where is the paging image start point: - * if CPU2 exist and it's in paging format, then the image looks like: - * CPU1 sections (2 or more) - * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between CPU1 to CPU2 - * CPU2 sections (not paged) - * PAGING_SEPARATOR_SECTION delimiter - separate between CPU2 - * non paged to CPU2 paging sec - * CPU2 paging CSS - * CPU2 paging image (including instruction and data) - */ - for (sec_idx = 0; sec_idx < IWL_UCODE_SECTION_MAX; sec_idx++) { - if (image->sec[sec_idx].offset == PAGING_SEPARATOR_SECTION) { - sec_idx++; - break; - } - } - - if (sec_idx >= IWL_UCODE_SECTION_MAX) { - IWL_ERR(mvm, "driver didn't find paging image\n"); - iwl_free_fw_paging(mvm); - return -EINVAL; - } - - /* copy the CSS block to the dram */ - IWL_DEBUG_FW(mvm, "Paging: load paging CSS to FW, sec = %d\n", - sec_idx); - - memcpy(page_address(mvm->fw_paging_db[0].fw_paging_block), - image->sec[sec_idx].data, - mvm->fw_paging_db[0].fw_paging_size); - - IWL_DEBUG_FW(mvm, - "Paging: copied %d CSS bytes to first block\n", - mvm->fw_paging_db[0].fw_paging_size); - - sec_idx++; - - /* - * copy the paging blocks to the dram - * loop index start from 1 since that CSS block already copied to dram - * and CSS index is 0. - * loop stop at num_of_paging_blk since that last block is not full. - */ - for (idx = 1; idx < mvm->num_of_paging_blk; idx++) { - memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block), - image->sec[sec_idx].data + offset, - mvm->fw_paging_db[idx].fw_paging_size); - - IWL_DEBUG_FW(mvm, - "Paging: copied %d paging bytes to block %d\n", - mvm->fw_paging_db[idx].fw_paging_size, - idx); - - offset += mvm->fw_paging_db[idx].fw_paging_size; - } - - /* copy the last paging block */ - if (mvm->num_of_pages_in_last_blk > 0) { - memcpy(page_address(mvm->fw_paging_db[idx].fw_paging_block), - image->sec[sec_idx].data + offset, - FW_PAGING_SIZE * mvm->num_of_pages_in_last_blk); - - IWL_DEBUG_FW(mvm, - "Paging: copied %d pages in the last block %d\n", - mvm->num_of_pages_in_last_blk, idx); - } - - return 0; -} - -static int iwl_alloc_fw_paging_mem(struct iwl_mvm *mvm, - const struct fw_img *image) -{ - struct page *block; - dma_addr_t phys = 0; - int blk_idx = 0; - int order, num_of_pages; - int dma_enabled; - - if (mvm->fw_paging_db[0].fw_paging_block) - return 0; - - dma_enabled = is_device_dma_capable(mvm->trans->dev); - - /* ensure BLOCK_2_EXP_SIZE is power of 2 of PAGING_BLOCK_SIZE */ - BUILD_BUG_ON(BIT(BLOCK_2_EXP_SIZE) != PAGING_BLOCK_SIZE); - - num_of_pages = image->paging_mem_size / FW_PAGING_SIZE; - mvm->num_of_paging_blk = ((num_of_pages - 1) / - NUM_OF_PAGE_PER_GROUP) + 1; - - mvm->num_of_pages_in_last_blk = - num_of_pages - - NUM_OF_PAGE_PER_GROUP * (mvm->num_of_paging_blk - 1); - - IWL_DEBUG_FW(mvm, - "Paging: allocating mem for %d paging blocks, each block holds 8 pages, last block holds %d pages\n", - mvm->num_of_paging_blk, - mvm->num_of_pages_in_last_blk); - - /* allocate block of 4Kbytes for paging CSS */ - order = get_order(FW_PAGING_SIZE); - block = alloc_pages(GFP_KERNEL, order); - if (!block) { - /* free all the previous pages since we failed */ - iwl_free_fw_paging(mvm); - return -ENOMEM; - } - - mvm->fw_paging_db[blk_idx].fw_paging_block = block; - mvm->fw_paging_db[blk_idx].fw_paging_size = FW_PAGING_SIZE; - - if (dma_enabled) { - phys = dma_map_page(mvm->trans->dev, block, 0, - PAGE_SIZE << order, DMA_BIDIRECTIONAL); - if (dma_mapping_error(mvm->trans->dev, phys)) { - /* - * free the previous pages and the current one since - * we failed to map_page. - */ - iwl_free_fw_paging(mvm); - return -ENOMEM; - } - mvm->fw_paging_db[blk_idx].fw_paging_phys = phys; - } else { - mvm->fw_paging_db[blk_idx].fw_paging_phys = PAGING_ADDR_SIG | - blk_idx << BLOCK_2_EXP_SIZE; - } - - IWL_DEBUG_FW(mvm, - "Paging: allocated 4K(CSS) bytes (order %d) for firmware paging.\n", - order); - - /* - * allocate blocks in dram. - * since that CSS allocated in fw_paging_db[0] loop start from index 1 - */ - for (blk_idx = 1; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) { - /* allocate block of PAGING_BLOCK_SIZE (32K) */ - order = get_order(PAGING_BLOCK_SIZE); - block = alloc_pages(GFP_KERNEL, order); - if (!block) { - /* free all the previous pages since we failed */ - iwl_free_fw_paging(mvm); - return -ENOMEM; - } - - mvm->fw_paging_db[blk_idx].fw_paging_block = block; - mvm->fw_paging_db[blk_idx].fw_paging_size = PAGING_BLOCK_SIZE; - - if (dma_enabled) { - phys = dma_map_page(mvm->trans->dev, block, 0, - PAGE_SIZE << order, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(mvm->trans->dev, phys)) { - /* - * free the previous pages and the current one - * since we failed to map_page. - */ - iwl_free_fw_paging(mvm); - return -ENOMEM; - } - mvm->fw_paging_db[blk_idx].fw_paging_phys = phys; - } else { - mvm->fw_paging_db[blk_idx].fw_paging_phys = - PAGING_ADDR_SIG | - blk_idx << BLOCK_2_EXP_SIZE; - } - - IWL_DEBUG_FW(mvm, - "Paging: allocated 32K bytes (order %d) for firmware paging.\n", - order); - } - - return 0; -} - -static int iwl_save_fw_paging(struct iwl_mvm *mvm, - const struct fw_img *fw) -{ - int ret; - - ret = iwl_alloc_fw_paging_mem(mvm, fw); - if (ret) - return ret; - - return iwl_fill_paging_mem(mvm, fw); -} - -/* send paging cmd to FW in case CPU2 has paging image */ -static int iwl_send_paging_cmd(struct iwl_mvm *mvm, const struct fw_img *fw) -{ - int blk_idx; - __le32 dev_phy_addr; - struct iwl_fw_paging_cmd fw_paging_cmd = { - .flags = - cpu_to_le32(PAGING_CMD_IS_SECURED | - PAGING_CMD_IS_ENABLED | - (mvm->num_of_pages_in_last_blk << - PAGING_CMD_NUM_OF_PAGES_IN_LAST_GRP_POS)), - .block_size = cpu_to_le32(BLOCK_2_EXP_SIZE), - .block_num = cpu_to_le32(mvm->num_of_paging_blk), - }; - - /* loop for for all paging blocks + CSS block */ - for (blk_idx = 0; blk_idx < mvm->num_of_paging_blk + 1; blk_idx++) { - dev_phy_addr = - cpu_to_le32(mvm->fw_paging_db[blk_idx].fw_paging_phys >> - PAGE_2_EXP_SIZE); - fw_paging_cmd.device_phy_addr[blk_idx] = dev_phy_addr; - } - - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(FW_PAGING_BLOCK_CMD, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(fw_paging_cmd), &fw_paging_cmd); -} - -/* - * Send paging item cmd to FW in case CPU2 has paging image - */ -static int iwl_trans_get_paging_item(struct iwl_mvm *mvm) -{ - int ret; - struct iwl_fw_get_item_cmd fw_get_item_cmd = { - .item_id = cpu_to_le32(IWL_FW_ITEM_ID_PAGING), - }; - - struct iwl_fw_get_item_resp *item_resp; - struct iwl_host_cmd cmd = { - .id = iwl_cmd_id(FW_GET_ITEM_CMD, IWL_ALWAYS_LONG_GROUP, 0), - .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, - .data = { &fw_get_item_cmd, }, - }; - - cmd.len[0] = sizeof(struct iwl_fw_get_item_cmd); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) { - IWL_ERR(mvm, - "Paging: Failed to send FW_GET_ITEM_CMD cmd (err = %d)\n", - ret); - return ret; - } - - item_resp = (void *)((struct iwl_rx_packet *)cmd.resp_pkt)->data; - if (item_resp->item_id != cpu_to_le32(IWL_FW_ITEM_ID_PAGING)) { - IWL_ERR(mvm, - "Paging: got wrong item in FW_GET_ITEM_CMD resp (item_id = %u)\n", - le32_to_cpu(item_resp->item_id)); - ret = -EIO; - goto exit; - } - - mvm->trans->paging_download_buf = kzalloc(MAX_PAGING_IMAGE_SIZE, - GFP_KERNEL); - if (!mvm->trans->paging_download_buf) { - ret = -ENOMEM; - goto exit; - } - mvm->trans->paging_req_addr = le32_to_cpu(item_resp->item_val); - mvm->trans->paging_db = mvm->fw_paging_db; - IWL_DEBUG_FW(mvm, - "Paging: got paging request address (paging_req_addr 0x%08x)\n", - mvm->trans->paging_req_addr); - -exit: - iwl_free_resp(&cmd); - - return ret; -} - -static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_mvm *mvm = - container_of(notif_wait, struct iwl_mvm, notif_wait); - struct iwl_mvm_alive_data *alive_data = data; - struct mvm_alive_resp_ver1 *palive1; - struct mvm_alive_resp_ver2 *palive2; - struct mvm_alive_resp *palive; - - if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive1)) { - palive1 = (void *)pkt->data; - - mvm->support_umac_log = false; - mvm->error_event_table = - le32_to_cpu(palive1->error_event_table_ptr); - mvm->log_event_table = - le32_to_cpu(palive1->log_event_table_ptr); - alive_data->scd_base_addr = le32_to_cpu(palive1->scd_base_ptr); - - alive_data->valid = le16_to_cpu(palive1->status) == - IWL_ALIVE_STATUS_OK; - IWL_DEBUG_FW(mvm, - "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", - le16_to_cpu(palive1->status), palive1->ver_type, - palive1->ver_subtype, palive1->flags); - } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive2)) { - palive2 = (void *)pkt->data; - - mvm->error_event_table = - le32_to_cpu(palive2->error_event_table_ptr); - mvm->log_event_table = - le32_to_cpu(palive2->log_event_table_ptr); - alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr); - mvm->umac_error_event_table = - le32_to_cpu(palive2->error_info_addr); - mvm->sf_space.addr = le32_to_cpu(palive2->st_fwrd_addr); - mvm->sf_space.size = le32_to_cpu(palive2->st_fwrd_size); - - alive_data->valid = le16_to_cpu(palive2->status) == - IWL_ALIVE_STATUS_OK; - if (mvm->umac_error_event_table) - mvm->support_umac_log = true; - - IWL_DEBUG_FW(mvm, - "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", - le16_to_cpu(palive2->status), palive2->ver_type, - palive2->ver_subtype, palive2->flags); - - IWL_DEBUG_FW(mvm, - "UMAC version: Major - 0x%x, Minor - 0x%x\n", - palive2->umac_major, palive2->umac_minor); - } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) { - palive = (void *)pkt->data; - - mvm->error_event_table = - le32_to_cpu(palive->error_event_table_ptr); - mvm->log_event_table = - le32_to_cpu(palive->log_event_table_ptr); - alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr); - mvm->umac_error_event_table = - le32_to_cpu(palive->error_info_addr); - mvm->sf_space.addr = le32_to_cpu(palive->st_fwrd_addr); - mvm->sf_space.size = le32_to_cpu(palive->st_fwrd_size); - - alive_data->valid = le16_to_cpu(palive->status) == - IWL_ALIVE_STATUS_OK; - if (mvm->umac_error_event_table) - mvm->support_umac_log = true; - - IWL_DEBUG_FW(mvm, - "Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", - le16_to_cpu(palive->status), palive->ver_type, - palive->ver_subtype, palive->flags); - - IWL_DEBUG_FW(mvm, - "UMAC version: Major - 0x%x, Minor - 0x%x\n", - le32_to_cpu(palive->umac_major), - le32_to_cpu(palive->umac_minor)); - } - - return true; -} - -static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_phy_db *phy_db = data; - - if (pkt->hdr.cmd != CALIB_RES_NOTIF_PHY_DB) { - WARN_ON(pkt->hdr.cmd != INIT_COMPLETE_NOTIF); - return true; - } - - WARN_ON(iwl_phy_db_set_section(phy_db, pkt, GFP_ATOMIC)); - - return false; -} - -static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, - enum iwl_ucode_type ucode_type) -{ - struct iwl_notification_wait alive_wait; - struct iwl_mvm_alive_data alive_data; - const struct fw_img *fw; - int ret, i; - enum iwl_ucode_type old_type = mvm->cur_ucode; - static const u16 alive_cmd[] = { MVM_ALIVE }; - struct iwl_sf_region st_fwrd_space; - - if (ucode_type == IWL_UCODE_REGULAR && - iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE)) - fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER); - else - fw = iwl_get_ucode_image(mvm, ucode_type); - if (WARN_ON(!fw)) - return -EINVAL; - mvm->cur_ucode = ucode_type; - mvm->ucode_loaded = false; - - iwl_init_notification_wait(&mvm->notif_wait, &alive_wait, - alive_cmd, ARRAY_SIZE(alive_cmd), - iwl_alive_fn, &alive_data); - - ret = iwl_trans_start_fw(mvm->trans, fw, ucode_type == IWL_UCODE_INIT); - if (ret) { - mvm->cur_ucode = old_type; - iwl_remove_notification(&mvm->notif_wait, &alive_wait); - return ret; - } - - /* - * Some things may run in the background now, but we - * just wait for the ALIVE notification here. - */ - ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait, - MVM_UCODE_ALIVE_TIMEOUT); - if (ret) { - if (mvm->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) - IWL_ERR(mvm, - "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", - iwl_read_prph(mvm->trans, SB_CPU_1_STATUS), - iwl_read_prph(mvm->trans, SB_CPU_2_STATUS)); - mvm->cur_ucode = old_type; - return ret; - } - - if (!alive_data.valid) { - IWL_ERR(mvm, "Loaded ucode is not valid!\n"); - mvm->cur_ucode = old_type; - return -EIO; - } - - /* - * update the sdio allocation according to the pointer we get in the - * alive notification. - */ - st_fwrd_space.addr = mvm->sf_space.addr; - st_fwrd_space.size = mvm->sf_space.size; - ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space); - if (ret) { - IWL_ERR(mvm, "Failed to update SF size. ret %d\n", ret); - return ret; - } - - iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr); - - /* - * configure and operate fw paging mechanism. - * driver configures the paging flow only once, CPU2 paging image - * included in the IWL_UCODE_INIT image. - */ - if (fw->paging_mem_size) { - /* - * When dma is not enabled, the driver needs to copy / write - * the downloaded / uploaded page to / from the smem. - * This gets the location of the place were the pages are - * stored. - */ - if (!is_device_dma_capable(mvm->trans->dev)) { - ret = iwl_trans_get_paging_item(mvm); - if (ret) { - IWL_ERR(mvm, "failed to get FW paging item\n"); - return ret; - } - } - - ret = iwl_save_fw_paging(mvm, fw); - if (ret) { - IWL_ERR(mvm, "failed to save the FW paging image\n"); - return ret; - } - - ret = iwl_send_paging_cmd(mvm, fw); - if (ret) { - IWL_ERR(mvm, "failed to send the paging cmd\n"); - iwl_free_fw_paging(mvm); - return ret; - } - } - - /* - * Note: all the queues are enabled as part of the interface - * initialization, but in firmware restart scenarios they - * could be stopped, so wake them up. In firmware restart, - * mac80211 will have the queues stopped as well until the - * reconfiguration completes. During normal startup, they - * will be empty. - */ - - memset(&mvm->queue_info, 0, sizeof(mvm->queue_info)); - mvm->queue_info[IWL_MVM_CMD_QUEUE].hw_queue_refcount = 1; - - for (i = 0; i < IEEE80211_MAX_QUEUES; i++) - atomic_set(&mvm->mac80211_queue_stop_count[i], 0); - - mvm->ucode_loaded = true; - - return 0; -} - -static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) -{ - struct iwl_phy_cfg_cmd phy_cfg_cmd; - enum iwl_ucode_type ucode_type = mvm->cur_ucode; - - /* Set parameters */ - phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm)); - phy_cfg_cmd.calib_control.event_trigger = - mvm->fw->default_calib[ucode_type].event_trigger; - phy_cfg_cmd.calib_control.flow_trigger = - mvm->fw->default_calib[ucode_type].flow_trigger; - - IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n", - phy_cfg_cmd.phy_cfg); - - return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, 0, - sizeof(phy_cfg_cmd), &phy_cfg_cmd); -} - -int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) -{ - struct iwl_notification_wait calib_wait; - static const u16 init_complete[] = { - INIT_COMPLETE_NOTIF, - CALIB_RES_NOTIF_PHY_DB - }; - int ret; - - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON_ONCE(mvm->calibrating)) - return 0; - - iwl_init_notification_wait(&mvm->notif_wait, - &calib_wait, - init_complete, - ARRAY_SIZE(init_complete), - iwl_wait_phy_db_entry, - mvm->phy_db); - - /* Will also start the device */ - ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_INIT); - if (ret) { - IWL_ERR(mvm, "Failed to start INIT ucode: %d\n", ret); - goto error; - } - - ret = iwl_send_bt_init_conf(mvm); - if (ret) - goto error; - - /* Read the NVM only at driver load time, no need to do this twice */ - if (read_nvm) { - /* Read nvm */ - ret = iwl_nvm_init(mvm, true); - if (ret) { - IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); - goto error; - } - } - - /* In case we read the NVM from external file, load it to the NIC */ - if (mvm->nvm_file_name) - iwl_mvm_load_nvm_to_nic(mvm); - - ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); - WARN_ON(ret); - - /* - * abort after reading the nvm in case RF Kill is on, we will complete - * the init seq later when RF kill will switch to off - */ - if (iwl_mvm_is_radio_hw_killed(mvm)) { - IWL_DEBUG_RF_KILL(mvm, - "jump over all phy activities due to RF kill\n"); - iwl_remove_notification(&mvm->notif_wait, &calib_wait); - ret = 1; - goto out; - } - - mvm->calibrating = true; - - /* Send TX valid antennas before triggering calibrations */ - ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); - if (ret) - goto error; - - /* - * Send phy configurations command to init uCode - * to start the 16.0 uCode init image internal calibrations. - */ - ret = iwl_send_phy_cfg_cmd(mvm); - if (ret) { - IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n", - ret); - goto error; - } - - /* - * Some things may run in the background now, but we - * just wait for the calibration complete notification. - */ - ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait, - MVM_UCODE_CALIB_TIMEOUT); - - if (ret && iwl_mvm_is_radio_hw_killed(mvm)) { - IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n"); - ret = 1; - } - goto out; - -error: - iwl_remove_notification(&mvm->notif_wait, &calib_wait); -out: - mvm->calibrating = false; - if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) { - /* we want to debug INIT and we have no NVM - fake */ - mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) + - sizeof(struct ieee80211_channel) + - sizeof(struct ieee80211_rate), - GFP_KERNEL); - if (!mvm->nvm_data) - return -ENOMEM; - mvm->nvm_data->bands[0].channels = mvm->nvm_data->channels; - mvm->nvm_data->bands[0].n_channels = 1; - mvm->nvm_data->bands[0].n_bitrates = 1; - mvm->nvm_data->bands[0].bitrates = - (void *)mvm->nvm_data->channels + 1; - mvm->nvm_data->bands[0].bitrates->hw_value = 10; - } - - return ret; -} - -static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm) -{ - struct iwl_host_cmd cmd = { - .id = SHARED_MEM_CFG, - .flags = CMD_WANT_SKB, - .data = { NULL, }, - .len = { 0, }, - }; - struct iwl_rx_packet *pkt; - struct iwl_shared_mem_cfg *mem_cfg; - u32 i; - - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON(iwl_mvm_send_cmd(mvm, &cmd))) - return; - - pkt = cmd.resp_pkt; - mem_cfg = (void *)pkt->data; - - mvm->shared_mem_cfg.shared_mem_addr = - le32_to_cpu(mem_cfg->shared_mem_addr); - mvm->shared_mem_cfg.shared_mem_size = - le32_to_cpu(mem_cfg->shared_mem_size); - mvm->shared_mem_cfg.sample_buff_addr = - le32_to_cpu(mem_cfg->sample_buff_addr); - mvm->shared_mem_cfg.sample_buff_size = - le32_to_cpu(mem_cfg->sample_buff_size); - mvm->shared_mem_cfg.txfifo_addr = le32_to_cpu(mem_cfg->txfifo_addr); - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) - mvm->shared_mem_cfg.txfifo_size[i] = - le32_to_cpu(mem_cfg->txfifo_size[i]); - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) - mvm->shared_mem_cfg.rxfifo_size[i] = - le32_to_cpu(mem_cfg->rxfifo_size[i]); - mvm->shared_mem_cfg.page_buff_addr = - le32_to_cpu(mem_cfg->page_buff_addr); - mvm->shared_mem_cfg.page_buff_size = - le32_to_cpu(mem_cfg->page_buff_size); - IWL_DEBUG_INFO(mvm, "SHARED MEM CFG: got memory offsets/sizes\n"); - - iwl_free_resp(&cmd); -} - -int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - struct iwl_mvm_dump_desc *desc, - struct iwl_fw_dbg_trigger_tlv *trigger) -{ - unsigned int delay = 0; - - if (trigger) - delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); - - if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status)) - return -EBUSY; - - if (WARN_ON(mvm->fw_dump_desc)) - iwl_mvm_free_fw_dump_desc(mvm); - - IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", - le32_to_cpu(desc->trig_desc.type)); - - mvm->fw_dump_desc = desc; - mvm->fw_dump_trig = trigger; - - queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay); - - return 0; -} - -int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, - const char *str, size_t len, - struct iwl_fw_dbg_trigger_tlv *trigger) -{ - struct iwl_mvm_dump_desc *desc; - - desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); - if (!desc) - return -ENOMEM; - - desc->len = len; - desc->trig_desc.type = cpu_to_le32(trig); - memcpy(desc->trig_desc.data, str, len); - - return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger); -} - -int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trigger, - const char *fmt, ...) -{ - u16 occurrences = le16_to_cpu(trigger->occurrences); - int ret, len = 0; - char buf[64]; - - if (!occurrences) - return 0; - - if (fmt) { - va_list ap; - - buf[sizeof(buf) - 1] = '\0'; - - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - /* check for truncation */ - if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) - buf[sizeof(buf) - 1] = '\0'; - - len = strlen(buf) + 1; - } - - ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len, - trigger); - - if (ret) - return ret; - - trigger->occurrences = cpu_to_le16(occurrences - 1); - return 0; -} - -static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm) -{ - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) - iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); - else - iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1); -} - -int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id) -{ - u8 *ptr; - int ret; - int i; - - if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv), - "Invalid configuration %d\n", conf_id)) - return -EINVAL; - - /* EARLY START - firmware's configuration is hard coded */ - if ((!mvm->fw->dbg_conf_tlv[conf_id] || - !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) && - conf_id == FW_DBG_START_FROM_ALIVE) { - iwl_mvm_restart_early_start(mvm); - return 0; - } - - if (!mvm->fw->dbg_conf_tlv[conf_id]) - return -EINVAL; - - if (mvm->fw_dbg_conf != FW_DBG_INVALID) - IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n", - mvm->fw_dbg_conf); - - /* Send all HCMDs for configuring the FW debug */ - ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd; - for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) { - struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr; - - ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0, - le16_to_cpu(cmd->len), cmd->data); - if (ret) - return ret; - - ptr += sizeof(*cmd); - ptr += le16_to_cpu(cmd->len); - } - - mvm->fw_dbg_conf = conf_id; - return ret; -} - -static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) -{ - struct iwl_ltr_config_cmd cmd = { - .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE), - }; - - if (!mvm->trans->ltr_enabled) - return 0; - - return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0, - sizeof(cmd), &cmd); -} - -int iwl_mvm_up(struct iwl_mvm *mvm) -{ - int ret, i; - struct ieee80211_channel *chan; - struct cfg80211_chan_def chandef; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_trans_start_hw(mvm->trans); - if (ret) - return ret; - - /* - * If we haven't completed the run of the init ucode during - * module loading, load init ucode now - * (for example, if we were in RFKILL) - */ - ret = iwl_run_init_mvm_ucode(mvm, false); - if (ret && !iwlmvm_mod_params.init_dbg) { - IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret); - /* this can't happen */ - if (WARN_ON(ret > 0)) - ret = -ERFKILL; - goto error; - } - if (!iwlmvm_mod_params.init_dbg) { - /* - * Stop and start the transport without entering low power - * mode. This will save the state of other components on the - * device that are triggered by the INIT firwmare (MFUART). - */ - _iwl_trans_stop_device(mvm->trans, false); - ret = _iwl_trans_start_hw(mvm->trans, false); - if (ret) - goto error; - } - - if (iwlmvm_mod_params.init_dbg) - return 0; - - ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); - if (ret) { - IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); - goto error; - } - - iwl_mvm_get_shared_mem_conf(mvm); - - ret = iwl_mvm_sf_update(mvm, NULL, false); - if (ret) - IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); - - mvm->fw_dbg_conf = FW_DBG_INVALID; - /* if we have a destination, assume EARLY START */ - if (mvm->fw->dbg_dest_tlv) - mvm->fw_dbg_conf = FW_DBG_START_FROM_ALIVE; - iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_START_FROM_ALIVE); - - ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); - if (ret) - goto error; - - ret = iwl_send_bt_init_conf(mvm); - if (ret) - goto error; - - /* Send phy db control command and then phy db calibration*/ - ret = iwl_send_phy_db_data(mvm->phy_db); - if (ret) - goto error; - - ret = iwl_send_phy_cfg_cmd(mvm); - if (ret) - goto error; - - /* init the fw <-> mac80211 STA mapping */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); - - mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; - - /* reset quota debouncing buffer - 0xff will yield invalid data */ - memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd)); - - /* Add auxiliary station for scanning */ - ret = iwl_mvm_add_aux_sta(mvm); - if (ret) - goto error; - - /* Add all the PHY contexts */ - chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; - cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); - for (i = 0; i < NUM_PHY_CTX; i++) { - /* - * The channel used here isn't relevant as it's - * going to be overwritten in the other flows. - * For now use the first channel we have. - */ - ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i], - &chandef, 1, 1); - if (ret) - goto error; - } - - /* Initialize tx backoffs to the minimal possible */ - iwl_mvm_tt_tx_backoff(mvm, 0); - - WARN_ON(iwl_mvm_config_ltr(mvm)); - - ret = iwl_mvm_power_update_device(mvm); - if (ret) - goto error; - - /* - * RTNL is not taken during Ct-kill, but we don't need to scan/Tx - * anyway, so don't init MCC. - */ - if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) { - ret = iwl_mvm_init_mcc(mvm); - if (ret) - goto error; - } - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - ret = iwl_mvm_config_scan(mvm); - if (ret) - goto error; - } - - if (iwl_mvm_is_csum_supported(mvm) && - mvm->cfg->features & NETIF_F_RXCSUM) - iwl_trans_write_prph(mvm->trans, RX_EN_CSUM, 0x3); - - /* allow FW/transport low power modes if not during restart */ - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); - - IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); - return 0; - error: - iwl_trans_stop_device(mvm->trans); - return ret; -} - -int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm) -{ - int ret, i; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_trans_start_hw(mvm->trans); - if (ret) - return ret; - - ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_WOWLAN); - if (ret) { - IWL_ERR(mvm, "Failed to start WoWLAN firmware: %d\n", ret); - goto error; - } - - ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); - if (ret) - goto error; - - /* Send phy db control command and then phy db calibration*/ - ret = iwl_send_phy_db_data(mvm->phy_db); - if (ret) - goto error; - - ret = iwl_send_phy_cfg_cmd(mvm); - if (ret) - goto error; - - /* init the fw <-> mac80211 STA mapping */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); - - /* Add auxiliary station for scanning */ - ret = iwl_mvm_add_aux_sta(mvm); - if (ret) - goto error; - - return 0; - error: - iwl_trans_stop_device(mvm->trans); - return ret; -} - -void iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_card_state_notif *card_state_notif = (void *)pkt->data; - u32 flags = le32_to_cpu(card_state_notif->flags); - - IWL_DEBUG_RF_KILL(mvm, "Card state received: HW:%s SW:%s CT:%s\n", - (flags & HW_CARD_DISABLED) ? "Kill" : "On", - (flags & SW_CARD_DISABLED) ? "Kill" : "On", - (flags & CT_KILL_CARD_DISABLED) ? - "Reached" : "Not reached"); -} - -void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data; - - IWL_DEBUG_INFO(mvm, - "MFUART: installed ver: 0x%08x, external ver: 0x%08x, status: 0x%08x, duration: 0x%08x\n", - le32_to_cpu(mfuart_notif->installed_ver), - le32_to_cpu(mfuart_notif->external_ver), - le32_to_cpu(mfuart_notif->status), - le32_to_cpu(mfuart_notif->duration)); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/led.c b/drivers/net/wireless/iwlwifi/mvm/led.c deleted file mode 100644 index e3b3cf4dbd77..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/led.c +++ /dev/null @@ -1,136 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include "iwl-io.h" -#include "iwl-csr.h" -#include "mvm.h" - -/* Set led register on */ -static void iwl_mvm_led_enable(struct iwl_mvm *mvm) -{ - iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON); -} - -/* Set led register off */ -static void iwl_mvm_led_disable(struct iwl_mvm *mvm) -{ - iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_OFF); -} - -static void iwl_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct iwl_mvm *mvm = container_of(led_cdev, struct iwl_mvm, led); - if (brightness > 0) - iwl_mvm_led_enable(mvm); - else - iwl_mvm_led_disable(mvm); -} - -int iwl_mvm_leds_init(struct iwl_mvm *mvm) -{ - int mode = iwlwifi_mod_params.led_mode; - int ret; - - switch (mode) { - case IWL_LED_BLINK: - IWL_ERR(mvm, "Blink led mode not supported, used default\n"); - case IWL_LED_DEFAULT: - case IWL_LED_RF_STATE: - mode = IWL_LED_RF_STATE; - break; - case IWL_LED_DISABLE: - IWL_INFO(mvm, "Led disabled\n"); - return 0; - default: - return -EINVAL; - } - - mvm->led.name = kasprintf(GFP_KERNEL, "%s-led", - wiphy_name(mvm->hw->wiphy)); - mvm->led.brightness_set = iwl_led_brightness_set; - mvm->led.max_brightness = 1; - - if (mode == IWL_LED_RF_STATE) - mvm->led.default_trigger = - ieee80211_get_radio_led_name(mvm->hw); - - ret = led_classdev_register(mvm->trans->dev, &mvm->led); - if (ret) { - kfree(mvm->led.name); - IWL_INFO(mvm, "Failed to enable led\n"); - return ret; - } - - return 0; -} - -void iwl_mvm_leds_exit(struct iwl_mvm *mvm) -{ - if (iwlwifi_mod_params.led_mode == IWL_LED_DISABLE) - return; - - led_classdev_unregister(&mvm->led); - kfree(mvm->led.name); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c deleted file mode 100644 index ad7ad720d2e7..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ /dev/null @@ -1,1452 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include -#include "iwl-io.h" -#include "iwl-prph.h" -#include "fw-api.h" -#include "mvm.h" -#include "time-event.h" - -const u8 iwl_mvm_ac_to_tx_fifo[] = { - IWL_MVM_TX_FIFO_VO, - IWL_MVM_TX_FIFO_VI, - IWL_MVM_TX_FIFO_BE, - IWL_MVM_TX_FIFO_BK, -}; - -struct iwl_mvm_mac_iface_iterator_data { - struct iwl_mvm *mvm; - struct ieee80211_vif *vif; - unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)]; - unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)]; - enum iwl_tsf_id preferred_tsf; - bool found_vif; -}; - -struct iwl_mvm_hw_queues_iface_iterator_data { - struct ieee80211_vif *exclude_vif; - unsigned long used_hw_queues; -}; - -static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_mac_iface_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u16 min_bi; - - /* Skip the interface for which we are trying to assign a tsf_id */ - if (vif == data->vif) - return; - - /* - * The TSF is a hardware/firmware resource, there are 4 and - * the driver should assign and free them as needed. However, - * there are cases where 2 MACs should share the same TSF ID - * for the purpose of clock sync, an optimization to avoid - * clock drift causing overlapping TBTTs/DTIMs for a GO and - * client in the system. - * - * The firmware will decide according to the MAC type which - * will be the master and slave. Clients that need to sync - * with a remote station will be the master, and an AP or GO - * will be the slave. - * - * Depending on the new interface type it can be slaved to - * or become the master of an existing interface. - */ - switch (data->vif->type) { - case NL80211_IFTYPE_STATION: - /* - * The new interface is a client, so if the one we're iterating - * is an AP, and the beacon interval of the AP is a multiple or - * divisor of the beacon interval of the client, the same TSF - * should be used to avoid drift between the new client and - * existing AP. The existing AP will get drift updates from the - * new client context in this case. - */ - if (vif->type != NL80211_IFTYPE_AP || - data->preferred_tsf != NUM_TSF_IDS || - !test_bit(mvmvif->tsf_id, data->available_tsf_ids)) - break; - - min_bi = min(data->vif->bss_conf.beacon_int, - vif->bss_conf.beacon_int); - - if (!min_bi) - break; - - if ((data->vif->bss_conf.beacon_int - - vif->bss_conf.beacon_int) % min_bi == 0) { - data->preferred_tsf = mvmvif->tsf_id; - return; - } - break; - - case NL80211_IFTYPE_AP: - /* - * The new interface is AP/GO, so if its beacon interval is a - * multiple or a divisor of the beacon interval of an existing - * interface, it should get drift updates from an existing - * client or use the same TSF as an existing GO. There's no - * drift between TSFs internally but if they used different - * TSFs then a new client MAC could update one of them and - * cause drift that way. - */ - if ((vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_STATION) || - data->preferred_tsf != NUM_TSF_IDS || - !test_bit(mvmvif->tsf_id, data->available_tsf_ids)) - break; - - min_bi = min(data->vif->bss_conf.beacon_int, - vif->bss_conf.beacon_int); - - if (!min_bi) - break; - - if ((data->vif->bss_conf.beacon_int - - vif->bss_conf.beacon_int) % min_bi == 0) { - data->preferred_tsf = mvmvif->tsf_id; - return; - } - break; - default: - /* - * For all other interface types there's no need to - * take drift into account. Either they're exclusive - * like IBSS and monitor, or we don't care much about - * their TSF (like P2P Device), but we won't be able - * to share the TSF resource. - */ - break; - } - - /* - * Unless we exited above, we can't share the TSF resource - * that the virtual interface we're iterating over is using - * with the new one, so clear the available bit and if this - * was the preferred one, reset that as well. - */ - __clear_bit(mvmvif->tsf_id, data->available_tsf_ids); - - if (data->preferred_tsf == mvmvif->tsf_id) - data->preferred_tsf = NUM_TSF_IDS; -} - -/* - * Get the mask of the queues used by the vif - */ -u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif) -{ - u32 qmask = 0, ac; - - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) - return BIT(IWL_MVM_OFFCHANNEL_QUEUE); - - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) - qmask |= BIT(vif->hw_queue[ac]); - } - - if (vif->type == NL80211_IFTYPE_AP) - qmask |= BIT(vif->cab_queue); - - return qmask; -} - -static void iwl_mvm_iface_hw_queues_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_hw_queues_iface_iterator_data *data = _data; - - /* exclude the given vif */ - if (vif == data->exclude_vif) - return; - - data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif); -} - -static void iwl_mvm_mac_sta_hw_queues_iter(void *_data, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_hw_queues_iface_iterator_data *data = _data; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - - /* Mark the queues used by the sta */ - data->used_hw_queues |= mvmsta->tfd_queue_msk; -} - -unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, - struct ieee80211_vif *exclude_vif) -{ - u8 sta_id; - struct iwl_mvm_hw_queues_iface_iterator_data data = { - .exclude_vif = exclude_vif, - .used_hw_queues = - BIT(IWL_MVM_OFFCHANNEL_QUEUE) | - BIT(mvm->aux_queue) | - BIT(IWL_MVM_CMD_QUEUE), - }; - - lockdep_assert_held(&mvm->mutex); - - /* mark all VIF used hw queues */ - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - iwl_mvm_iface_hw_queues_iter, &data); - - /* don't assign the same hw queues as TDLS stations */ - ieee80211_iterate_stations_atomic(mvm->hw, - iwl_mvm_mac_sta_hw_queues_iter, - &data); - - /* - * Some TDLS stations may be removed but are in the process of being - * drained. Don't touch their queues. - */ - for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) - data.used_hw_queues |= mvm->tfd_drained[sta_id]; - - return data.used_hw_queues; -} - -static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_mac_iface_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - /* Iterator may already find the interface being added -- skip it */ - if (vif == data->vif) { - data->found_vif = true; - return; - } - - /* Mark MAC IDs as used by clearing the available bit, and - * (below) mark TSFs as used if their existing use is not - * compatible with the new interface type. - * No locking or atomic bit operations are needed since the - * data is on the stack of the caller function. - */ - __clear_bit(mvmvif->id, data->available_mac_ids); - - /* find a suitable tsf_id */ - iwl_mvm_mac_tsf_id_iter(_data, mac, vif); -} - -void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_mac_iface_iterator_data data = { - .mvm = mvm, - .vif = vif, - .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 }, - /* no preference yet */ - .preferred_tsf = NUM_TSF_IDS, - }; - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - iwl_mvm_mac_tsf_id_iter, &data); - - if (data.preferred_tsf != NUM_TSF_IDS) - mvmvif->tsf_id = data.preferred_tsf; - else if (!test_bit(mvmvif->tsf_id, data.available_tsf_ids)) - mvmvif->tsf_id = find_first_bit(data.available_tsf_ids, - NUM_TSF_IDS); -} - -static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_mac_iface_iterator_data data = { - .mvm = mvm, - .vif = vif, - .available_mac_ids = { (1 << NUM_MAC_INDEX_DRIVER) - 1 }, - .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 }, - /* no preference yet */ - .preferred_tsf = NUM_TSF_IDS, - .found_vif = false, - }; - u32 ac; - int ret, i; - unsigned long used_hw_queues; - - /* - * Allocate a MAC ID and a TSF for this MAC, along with the queues - * and other resources. - */ - - /* - * Before the iterator, we start with all MAC IDs and TSFs available. - * - * During iteration, all MAC IDs are cleared that are in use by other - * virtual interfaces, and all TSF IDs are cleared that can't be used - * by this new virtual interface because they're used by an interface - * that can't share it with the new one. - * At the same time, we check if there's a preferred TSF in the case - * that we should share it with another interface. - */ - - /* Currently, MAC ID 0 should be used only for the managed/IBSS vif */ - switch (vif->type) { - case NL80211_IFTYPE_ADHOC: - break; - case NL80211_IFTYPE_STATION: - if (!vif->p2p) - break; - /* fall through */ - default: - __clear_bit(0, data.available_mac_ids); - } - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - iwl_mvm_mac_iface_iterator, &data); - - used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, vif); - - /* - * In the case we're getting here during resume, it's similar to - * firmware restart, and with RESUME_ALL the iterator will find - * the vif being added already. - * We don't want to reassign any IDs in either case since doing - * so would probably assign different IDs (as interfaces aren't - * necessarily added in the same order), but the old IDs were - * preserved anyway, so skip ID assignment for both resume and - * recovery. - */ - if (data.found_vif) - return 0; - - /* Therefore, in recovery, we can't get here */ - if (WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))) - return -EBUSY; - - mvmvif->id = find_first_bit(data.available_mac_ids, - NUM_MAC_INDEX_DRIVER); - if (mvmvif->id == NUM_MAC_INDEX_DRIVER) { - IWL_ERR(mvm, "Failed to init MAC context - no free ID!\n"); - ret = -EIO; - goto exit_fail; - } - - if (data.preferred_tsf != NUM_TSF_IDS) - mvmvif->tsf_id = data.preferred_tsf; - else - mvmvif->tsf_id = find_first_bit(data.available_tsf_ids, - NUM_TSF_IDS); - if (mvmvif->tsf_id == NUM_TSF_IDS) { - IWL_ERR(mvm, "Failed to init MAC context - no free TSF!\n"); - ret = -EIO; - goto exit_fail; - } - - mvmvif->color = 0; - - INIT_LIST_HEAD(&mvmvif->time_event_data.list); - mvmvif->time_event_data.id = TE_MAX; - - /* No need to allocate data queues to P2P Device MAC.*/ - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE; - - return 0; - } - - /* Find available queues, and allocate them to the ACs */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - u8 queue = find_first_zero_bit(&used_hw_queues, - mvm->first_agg_queue); - - if (queue >= mvm->first_agg_queue) { - IWL_ERR(mvm, "Failed to allocate queue\n"); - ret = -EIO; - goto exit_fail; - } - - __set_bit(queue, &used_hw_queues); - vif->hw_queue[ac] = queue; - } - - /* Allocate the CAB queue for softAP and GO interfaces */ - if (vif->type == NL80211_IFTYPE_AP) { - u8 queue = find_first_zero_bit(&used_hw_queues, - mvm->first_agg_queue); - - if (queue >= mvm->first_agg_queue) { - IWL_ERR(mvm, "Failed to allocate cab queue\n"); - ret = -EIO; - goto exit_fail; - } - - vif->cab_queue = queue; - } else { - vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; - } - - mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT; - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - - for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) - mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC; - - return 0; - -exit_fail: - memset(mvmvif, 0, sizeof(struct iwl_mvm_vif)); - memset(vif->hw_queue, IEEE80211_INVAL_HW_QUEUE, sizeof(vif->hw_queue)); - vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; - return ret; -} - -int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, false, false); - u32 ac; - int ret; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_mac_ctxt_allocate_resources(mvm, vif); - if (ret) - return ret; - - switch (vif->type) { - case NL80211_IFTYPE_P2P_DEVICE: - iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_TX_FIFO_VO, 0, wdg_timeout); - break; - case NL80211_IFTYPE_AP: - iwl_mvm_enable_ac_txq(mvm, vif->cab_queue, vif->cab_queue, - IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); - /* fall through */ - default: - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac], - vif->hw_queue[ac], - iwl_mvm_ac_to_tx_fifo[ac], 0, - wdg_timeout); - break; - } - - return 0; -} - -void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - int ac; - - lockdep_assert_held(&mvm->mutex); - - switch (vif->type) { - case NL80211_IFTYPE_P2P_DEVICE: - iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_OFFCHANNEL_QUEUE, IWL_MAX_TID_COUNT, - 0); - break; - case NL80211_IFTYPE_AP: - iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue, - IWL_MAX_TID_COUNT, 0); - /* fall through */ - default: - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - iwl_mvm_disable_txq(mvm, vif->hw_queue[ac], - vif->hw_queue[ac], - IWL_MAX_TID_COUNT, 0); - } -} - -static void iwl_mvm_ack_rates(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - enum ieee80211_band band, - u8 *cck_rates, u8 *ofdm_rates) -{ - struct ieee80211_supported_band *sband; - unsigned long basic = vif->bss_conf.basic_rates; - int lowest_present_ofdm = 100; - int lowest_present_cck = 100; - u8 cck = 0; - u8 ofdm = 0; - int i; - - sband = mvm->hw->wiphy->bands[band]; - - for_each_set_bit(i, &basic, BITS_PER_LONG) { - int hw = sband->bitrates[i].hw_value; - if (hw >= IWL_FIRST_OFDM_RATE) { - ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE); - if (lowest_present_ofdm > hw) - lowest_present_ofdm = hw; - } else { - BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); - - cck |= BIT(hw); - if (lowest_present_cck > hw) - lowest_present_cck = hw; - } - } - - /* - * Now we've got the basic rates as bitmaps in the ofdm and cck - * variables. This isn't sufficient though, as there might not - * be all the right rates in the bitmap. E.g. if the only basic - * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps - * and 6 Mbps because the 802.11-2007 standard says in 9.6: - * - * [...] a STA responding to a received frame shall transmit - * its Control Response frame [...] at the highest rate in the - * BSSBasicRateSet parameter that is less than or equal to the - * rate of the immediately previous frame in the frame exchange - * sequence ([...]) and that is of the same modulation class - * ([...]) as the received frame. If no rate contained in the - * BSSBasicRateSet parameter meets these conditions, then the - * control frame sent in response to a received frame shall be - * transmitted at the highest mandatory rate of the PHY that is - * less than or equal to the rate of the received frame, and - * that is of the same modulation class as the received frame. - * - * As a consequence, we need to add all mandatory rates that are - * lower than all of the basic rates to these bitmaps. - */ - - if (IWL_RATE_24M_INDEX < lowest_present_ofdm) - ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE; - if (IWL_RATE_12M_INDEX < lowest_present_ofdm) - ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE; - /* 6M already there or needed so always add */ - ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE; - - /* - * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. - * Note, however: - * - if no CCK rates are basic, it must be ERP since there must - * be some basic rates at all, so they're OFDM => ERP PHY - * (or we're in 5 GHz, and the cck bitmap will never be used) - * - if 11M is a basic rate, it must be ERP as well, so add 5.5M - * - if 5.5M is basic, 1M and 2M are mandatory - * - if 2M is basic, 1M is mandatory - * - if 1M is basic, that's the only valid ACK rate. - * As a consequence, it's not as complicated as it sounds, just add - * any lower rates to the ACK rate bitmap. - */ - if (IWL_RATE_11M_INDEX < lowest_present_cck) - cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE; - if (IWL_RATE_5M_INDEX < lowest_present_cck) - cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE; - if (IWL_RATE_2M_INDEX < lowest_present_cck) - cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE; - /* 1M already there or needed so always add */ - cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE; - - *cck_rates = cck; - *ofdm_rates = ofdm; -} - -static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_ctx_cmd *cmd) -{ - /* for both sta and ap, ht_operation_mode hold the protection_mode */ - u8 protection_mode = vif->bss_conf.ht_operation_mode & - IEEE80211_HT_OP_MODE_PROTECTION; - /* The fw does not distinguish between ht and fat */ - u32 ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT; - - IWL_DEBUG_RATE(mvm, "protection mode set to %d\n", protection_mode); - /* - * See section 9.23.3.1 of IEEE 80211-2012. - * Nongreenfield HT STAs Present is not supported. - */ - switch (protection_mode) { - case IEEE80211_HT_OP_MODE_PROTECTION_NONE: - break; - case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: - case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: - cmd->protection_flags |= cpu_to_le32(ht_flag); - break; - case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: - /* Protect when channel wider than 20MHz */ - if (vif->bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) - cmd->protection_flags |= cpu_to_le32(ht_flag); - break; - default: - IWL_ERR(mvm, "Illegal protection mode %d\n", - protection_mode); - break; - } -} - -static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_ctx_cmd *cmd, - const u8 *bssid_override, - u32 action) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_chanctx_conf *chanctx; - bool ht_enabled = !!(vif->bss_conf.ht_operation_mode & - IEEE80211_HT_OP_MODE_PROTECTION); - u8 cck_ack_rates, ofdm_ack_rates; - const u8 *bssid = bssid_override ?: vif->bss_conf.bssid; - int i; - - cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)); - cmd->action = cpu_to_le32(action); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - if (vif->p2p) - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_STA); - else - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_BSS_STA); - break; - case NL80211_IFTYPE_AP: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_GO); - break; - case NL80211_IFTYPE_MONITOR: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_LISTENER); - break; - case NL80211_IFTYPE_P2P_DEVICE: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_DEVICE); - break; - case NL80211_IFTYPE_ADHOC: - cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_IBSS); - break; - default: - WARN_ON_ONCE(1); - } - - cmd->tsf_id = cpu_to_le32(mvmvif->tsf_id); - - memcpy(cmd->node_addr, vif->addr, ETH_ALEN); - - if (bssid) - memcpy(cmd->bssid_addr, bssid, ETH_ALEN); - else - eth_broadcast_addr(cmd->bssid_addr); - - rcu_read_lock(); - chanctx = rcu_dereference(vif->chanctx_conf); - iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band - : IEEE80211_BAND_2GHZ, - &cck_ack_rates, &ofdm_ack_rates); - rcu_read_unlock(); - - cmd->cck_rates = cpu_to_le32((u32)cck_ack_rates); - cmd->ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates); - - cmd->cck_short_preamble = - cpu_to_le32(vif->bss_conf.use_short_preamble ? - MAC_FLG_SHORT_PREAMBLE : 0); - cmd->short_slot = - cpu_to_le32(vif->bss_conf.use_short_slot ? - MAC_FLG_SHORT_SLOT : 0); - - for (i = 0; i < IEEE80211_NUM_ACS; i++) { - u8 txf = iwl_mvm_ac_to_tx_fifo[i]; - - cmd->ac[txf].cw_min = - cpu_to_le16(mvmvif->queue_params[i].cw_min); - cmd->ac[txf].cw_max = - cpu_to_le16(mvmvif->queue_params[i].cw_max); - cmd->ac[txf].edca_txop = - cpu_to_le16(mvmvif->queue_params[i].txop * 32); - cmd->ac[txf].aifsn = mvmvif->queue_params[i].aifs; - cmd->ac[txf].fifos_mask = BIT(txf); - } - - /* in AP mode, the MCAST FIFO takes the EDCA params from VO */ - if (vif->type == NL80211_IFTYPE_AP) - cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |= - BIT(IWL_MVM_TX_FIFO_MCAST); - - if (vif->bss_conf.qos) - cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); - - if (vif->bss_conf.use_cts_prot) - cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); - - IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n", - vif->bss_conf.use_cts_prot, - vif->bss_conf.ht_operation_mode); - if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) - cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); - if (ht_enabled) - iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd); - - cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP); -} - -static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, - struct iwl_mac_ctx_cmd *cmd) -{ - int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, - sizeof(*cmd), cmd); - if (ret) - IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n", - le32_to_cpu(cmd->action), ret); - return ret; -} - -static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action, bool force_assoc_off, - const u8 *bssid_override) -{ - struct iwl_mac_ctx_cmd cmd = {}; - struct iwl_mac_data_sta *ctxt_sta; - - WARN_ON(vif->type != NL80211_IFTYPE_STATION); - - /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, bssid_override, action); - - if (vif->p2p) { - struct ieee80211_p2p_noa_attr *noa = - &vif->bss_conf.p2p_noa_attr; - - cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow & - IEEE80211_P2P_OPPPS_CTWINDOW_MASK); - ctxt_sta = &cmd.p2p_sta.sta; - } else { - ctxt_sta = &cmd.sta; - } - - /* We need the dtim_period to set the MAC as associated */ - if (vif->bss_conf.assoc && vif->bss_conf.dtim_period && - !force_assoc_off) { - u32 dtim_offs; - - /* - * The DTIM count counts down, so when it is N that means N - * more beacon intervals happen until the DTIM TBTT. Therefore - * add this to the current time. If that ends up being in the - * future, the firmware will handle it. - * - * Also note that the system_timestamp (which we get here as - * "sync_device_ts") and TSF timestamp aren't at exactly the - * same offset in the frame -- the TSF is at the first symbol - * of the TSF, the system timestamp is at signal acquisition - * time. This means there's an offset between them of at most - * a few hundred microseconds (24 * 8 bits + PLCP time gives - * 384us in the longest case), this is currently not relevant - * as the firmware wakes up around 2ms before the TBTT. - */ - dtim_offs = vif->bss_conf.sync_dtim_count * - vif->bss_conf.beacon_int; - /* convert TU to usecs */ - dtim_offs *= 1024; - - ctxt_sta->dtim_tsf = - cpu_to_le64(vif->bss_conf.sync_tsf + dtim_offs); - ctxt_sta->dtim_time = - cpu_to_le32(vif->bss_conf.sync_device_ts + dtim_offs); - - IWL_DEBUG_INFO(mvm, "DTIM TBTT is 0x%llx/0x%x, offset %d\n", - le64_to_cpu(ctxt_sta->dtim_tsf), - le32_to_cpu(ctxt_sta->dtim_time), - dtim_offs); - - ctxt_sta->is_assoc = cpu_to_le32(1); - } else { - ctxt_sta->is_assoc = cpu_to_le32(0); - - /* Allow beacons to pass through as long as we are not - * associated, or we do not have dtim period information. - */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); - } - - ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int); - ctxt_sta->bi_reciprocal = - cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int)); - ctxt_sta->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * - vif->bss_conf.dtim_period); - ctxt_sta->dtim_reciprocal = - cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int * - vif->bss_conf.dtim_period)); - - ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval); - ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid); - - if (vif->probe_req_reg && vif->bss_conf.assoc && vif->p2p) - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); - - return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); -} - -static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mac_ctx_cmd cmd = {}; - - WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); - - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - - cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC | - MAC_FILTER_IN_CONTROL_AND_MGMT | - MAC_FILTER_IN_BEACON | - MAC_FILTER_IN_PROBE_REQUEST | - MAC_FILTER_IN_CRC32); - ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); - - return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); -} - -static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_ctx_cmd cmd = {}; - - WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); - - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - - cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_BEACON | - MAC_FILTER_IN_PROBE_REQUEST); - - /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */ - cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int); - cmd.ibss.bi_reciprocal = - cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int)); - - /* TODO: Assumes that the beacon id == mac context id */ - cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id); - - return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); -} - -struct iwl_mvm_go_iterator_data { - bool go_active; -}; - -static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) -{ - struct iwl_mvm_go_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (vif->type == NL80211_IFTYPE_AP && vif->p2p && - mvmvif->ap_ibss_active) - data->go_active = true; -} - -static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mac_ctx_cmd cmd = {}; - struct iwl_mvm_go_iterator_data data = {}; - - WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE); - - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - - cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); - - /* Override the filter flags to accept only probe requests */ - cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); - - /* - * This flag should be set to true when the P2P Device is - * discoverable and there is at least another active P2P GO. Settings - * this flag will allow the P2P Device to be discoverable on other - * channels in addition to its listen channel. - * Note that this flag should not be set in other cases as it opens the - * Rx filters on all MAC and increases the number of interrupts. - */ - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - iwl_mvm_go_iterator, &data); - - cmd.p2p_dev.is_disc_extended = cpu_to_le32(data.go_active ? 1 : 0); - return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); -} - -static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm, - struct iwl_mac_beacon_cmd *beacon_cmd, - u8 *beacon, u32 frame_size) -{ - u32 tim_idx; - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; - - /* The index is relative to frame start but we start looking at the - * variable-length part of the beacon. */ - tim_idx = mgmt->u.beacon.variable - beacon; - - /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ - while ((tim_idx < (frame_size - 2)) && - (beacon[tim_idx] != WLAN_EID_TIM)) - tim_idx += beacon[tim_idx+1] + 2; - - /* If TIM field was found, set variables */ - if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { - beacon_cmd->tim_idx = cpu_to_le32(tim_idx); - beacon_cmd->tim_size = cpu_to_le32((u32)beacon[tim_idx+1]); - } else { - IWL_WARN(mvm, "Unable to find TIM Element in beacon\n"); - } -} - -static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct sk_buff *beacon) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_host_cmd cmd = { - .id = BEACON_TEMPLATE_CMD, - .flags = CMD_ASYNC, - }; - struct iwl_mac_beacon_cmd beacon_cmd = {}; - struct ieee80211_tx_info *info; - u32 beacon_skb_len; - u32 rate, tx_flags; - - if (WARN_ON(!beacon)) - return -EINVAL; - - beacon_skb_len = beacon->len; - - /* TODO: for now the beacon template id is set to be the mac context id. - * Might be better to handle it as another resource ... */ - beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); - info = IEEE80211_SKB_CB(beacon); - - /* Set up TX command fields */ - beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len); - beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id; - beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); - tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF; - tx_flags |= - iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) << - TX_CMD_FLG_BT_PRIO_POS; - beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags); - - mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), - mvm->mgmt_last_antenna_idx); - - beacon_cmd.tx.rate_n_flags = - cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << - RATE_MCS_ANT_POS); - - if (info->band == IEEE80211_BAND_5GHZ || vif->p2p) { - rate = IWL_FIRST_OFDM_RATE; - } else { - rate = IWL_FIRST_CCK_RATE; - beacon_cmd.tx.rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK); - } - beacon_cmd.tx.rate_n_flags |= - cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate)); - - /* Set up TX beacon command fields */ - if (vif->type == NL80211_IFTYPE_AP) - iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd, - beacon->data, - beacon_skb_len); - - /* Submit command */ - cmd.len[0] = sizeof(beacon_cmd); - cmd.data[0] = &beacon_cmd; - cmd.dataflags[0] = 0; - cmd.len[1] = beacon_skb_len; - cmd.data[1] = beacon->data; - cmd.dataflags[1] = IWL_HCMD_DFL_DUP; - - return iwl_mvm_send_cmd(mvm, &cmd); -} - -/* The beacon template for the AP/GO/IBSS has changed and needs update */ -int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct sk_buff *beacon; - int ret; - - WARN_ON(vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_ADHOC); - - beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL); - if (!beacon) - return -ENOMEM; - - ret = iwl_mvm_mac_ctxt_send_beacon(mvm, vif, beacon); - dev_kfree_skb(beacon); - return ret; -} - -struct iwl_mvm_mac_ap_iterator_data { - struct iwl_mvm *mvm; - struct ieee80211_vif *vif; - u32 beacon_device_ts; - u16 beacon_int; -}; - -/* Find the beacon_device_ts and beacon_int for a managed interface */ -static void iwl_mvm_mac_ap_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_mac_ap_iterator_data *data = _data; - - if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) - return; - - /* Station client has higher priority over P2P client*/ - if (vif->p2p && data->beacon_device_ts) - return; - - data->beacon_device_ts = vif->bss_conf.sync_device_ts; - data->beacon_int = vif->bss_conf.beacon_int; -} - -/* - * Fill the specific data for mac context of type AP of P2P GO - */ -static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_data_ap *ctxt_ap, - bool add) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_mac_ap_iterator_data data = { - .mvm = mvm, - .vif = vif, - .beacon_device_ts = 0 - }; - - ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int); - ctxt_ap->bi_reciprocal = - cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int)); - ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int * - vif->bss_conf.dtim_period); - ctxt_ap->dtim_reciprocal = - cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int * - vif->bss_conf.dtim_period)); - - ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue); - - /* - * Only set the beacon time when the MAC is being added, when we - * just modify the MAC then we should keep the time -- the firmware - * can otherwise have a "jumping" TBTT. - */ - if (add) { - /* - * If there is a station/P2P client interface which is - * associated, set the AP's TBTT far enough from the station's - * TBTT. Otherwise, set it to the current system time - */ - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - iwl_mvm_mac_ap_iterator, &data); - - if (data.beacon_device_ts) { - u32 rand = (prandom_u32() % (64 - 36)) + 36; - mvmvif->ap_beacon_time = data.beacon_device_ts + - ieee80211_tu_to_usec(data.beacon_int * rand / - 100); - } else { - mvmvif->ap_beacon_time = - iwl_read_prph(mvm->trans, - DEVICE_SYSTEM_TIME_REG); - } - } - - ctxt_ap->beacon_time = cpu_to_le32(mvmvif->ap_beacon_time); - ctxt_ap->beacon_tsf = 0; /* unused */ - - /* TODO: Assume that the beacon id == mac context id */ - ctxt_ap->beacon_template = cpu_to_le32(mvmvif->id); -} - -static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_ctx_cmd cmd = {}; - - WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p); - - /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - - /* - * pass probe requests and beacons from other APs (needed - * for ht protection); when there're no any associated station - * don't ask FW to pass beacons to prevent unnecessary wake-ups. - */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); - if (mvmvif->ap_assoc_sta_count) { - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); - IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); - } else { - IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); - } - - /* Fill the data specific for ap mode */ - iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap, - action == FW_CTXT_ACTION_ADD); - - return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); -} - -static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mac_ctx_cmd cmd = {}; - struct ieee80211_p2p_noa_attr *noa = &vif->bss_conf.p2p_noa_attr; - - WARN_ON(vif->type != NL80211_IFTYPE_AP || !vif->p2p); - - /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - - /* - * pass probe requests and beacons from other APs (needed - * for ht protection) - */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST | - MAC_FILTER_IN_BEACON); - - /* Fill the data specific for GO mode */ - iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap, - action == FW_CTXT_ACTION_ADD); - - cmd.go.ctwin = cpu_to_le32(noa->oppps_ctwindow & - IEEE80211_P2P_OPPPS_CTWINDOW_MASK); - cmd.go.opp_ps_enabled = - cpu_to_le32(!!(noa->oppps_ctwindow & - IEEE80211_P2P_OPPPS_ENABLE_BIT)); - - return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); -} - -static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 action, bool force_assoc_off, - const u8 *bssid_override) -{ - switch (vif->type) { - case NL80211_IFTYPE_STATION: - return iwl_mvm_mac_ctxt_cmd_sta(mvm, vif, action, - force_assoc_off, - bssid_override); - break; - case NL80211_IFTYPE_AP: - if (!vif->p2p) - return iwl_mvm_mac_ctxt_cmd_ap(mvm, vif, action); - else - return iwl_mvm_mac_ctxt_cmd_go(mvm, vif, action); - break; - case NL80211_IFTYPE_MONITOR: - return iwl_mvm_mac_ctxt_cmd_listener(mvm, vif, action); - case NL80211_IFTYPE_P2P_DEVICE: - return iwl_mvm_mac_ctxt_cmd_p2p_device(mvm, vif, action); - case NL80211_IFTYPE_ADHOC: - return iwl_mvm_mac_ctxt_cmd_ibss(mvm, vif, action); - default: - break; - } - - return -EOPNOTSUPP; -} - -int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n", - vif->addr, ieee80211_vif_type_p2p(vif))) - return -EIO; - - ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, - true, NULL); - if (ret) - return ret; - - /* will only do anything at resume from D3 time */ - iwl_mvm_set_last_nonqos_seq(mvm, vif); - - mvmvif->uploaded = true; - return 0; -} - -int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool force_assoc_off, const u8 *bssid_override) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n", - vif->addr, ieee80211_vif_type_p2p(vif))) - return -EIO; - - return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, - force_assoc_off, bssid_override); -} - -int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_ctx_cmd cmd; - int ret; - - if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n", - vif->addr, ieee80211_vif_type_p2p(vif))) - return -EIO; - - memset(&cmd, 0, sizeof(cmd)); - - cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)); - cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); - - ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, - sizeof(cmd), &cmd); - if (ret) { - IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret); - return ret; - } - - mvmvif->uploaded = false; - - if (vif->type == NL80211_IFTYPE_MONITOR) - __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags); - - return 0; -} - -static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm, - struct ieee80211_vif *csa_vif, u32 gp2, - bool tx_success) -{ - struct iwl_mvm_vif *mvmvif = - iwl_mvm_vif_from_mac80211(csa_vif); - - /* Don't start to countdown from a failed beacon */ - if (!tx_success && !mvmvif->csa_countdown) - return; - - mvmvif->csa_countdown = true; - - if (!ieee80211_csa_is_complete(csa_vif)) { - int c = ieee80211_csa_update_counter(csa_vif); - - iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif); - if (csa_vif->p2p && - !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2 && - tx_success) { - u32 rel_time = (c + 1) * - csa_vif->bss_conf.beacon_int - - IWL_MVM_CHANNEL_SWITCH_TIME_GO; - u32 apply_time = gp2 + rel_time * 1024; - - iwl_mvm_schedule_csa_period(mvm, csa_vif, - IWL_MVM_CHANNEL_SWITCH_TIME_GO - - IWL_MVM_CHANNEL_SWITCH_MARGIN, - apply_time); - } - } else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) { - /* we don't have CSA NoA scheduled yet, switch now */ - ieee80211_csa_finish(csa_vif); - RCU_INIT_POINTER(mvm->csa_vif, NULL); - } -} - -void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_extended_beacon_notif *beacon = (void *)pkt->data; - struct iwl_mvm_tx_resp *beacon_notify_hdr; - struct ieee80211_vif *csa_vif; - struct ieee80211_vif *tx_blocked_vif; - u16 status; - - lockdep_assert_held(&mvm->mutex); - - beacon_notify_hdr = &beacon->beacon_notify_hdr; - mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2); - - status = le16_to_cpu(beacon_notify_hdr->status.status) & TX_STATUS_MSK; - IWL_DEBUG_RX(mvm, - "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n", - status, beacon_notify_hdr->failure_frame, - le64_to_cpu(beacon->tsf), - mvm->ap_last_beacon_gp2, - le32_to_cpu(beacon_notify_hdr->initial_rate)); - - csa_vif = rcu_dereference_protected(mvm->csa_vif, - lockdep_is_held(&mvm->mutex)); - if (unlikely(csa_vif && csa_vif->csa_active)) - iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2, - (status == TX_STATUS_SUCCESS)); - - tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif, - lockdep_is_held(&mvm->mutex)); - if (unlikely(tx_blocked_vif)) { - struct iwl_mvm_vif *mvmvif = - iwl_mvm_vif_from_mac80211(tx_blocked_vif); - - /* - * The channel switch is started and we have blocked the - * stations. If this is the first beacon (the timeout wasn't - * set), set the unblock timeout, otherwise countdown - */ - if (!mvm->csa_tx_block_bcn_timeout) - mvm->csa_tx_block_bcn_timeout = - IWL_MVM_CS_UNBLOCK_TX_TIMEOUT; - else - mvm->csa_tx_block_bcn_timeout--; - - /* Check if the timeout is expired, and unblock tx */ - if (mvm->csa_tx_block_bcn_timeout == 0) { - iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false); - RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL); - } - } -} - -static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_missed_beacons_notif *missed_beacons = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->mvm; - struct iwl_fw_dbg_trigger_missed_bcon *bcon_trig; - struct iwl_fw_dbg_trigger_tlv *trigger; - u32 stop_trig_missed_bcon, stop_trig_missed_bcon_since_rx; - u32 rx_missed_bcon, rx_missed_bcon_since_rx; - - if (mvmvif->id != (u16)le32_to_cpu(missed_beacons->mac_id)) - return; - - rx_missed_bcon = le32_to_cpu(missed_beacons->consec_missed_beacons); - rx_missed_bcon_since_rx = - le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx); - /* - * TODO: the threshold should be adjusted based on latency conditions, - * and/or in case of a CS flow on one of the other AP vifs. - */ - if (le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx) > - IWL_MVM_MISSED_BEACONS_THRESHOLD) - ieee80211_beacon_loss(vif); - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, - FW_DBG_TRIGGER_MISSED_BEACONS)) - return; - - trigger = iwl_fw_dbg_get_trigger(mvm->fw, - FW_DBG_TRIGGER_MISSED_BEACONS); - bcon_trig = (void *)trigger->data; - stop_trig_missed_bcon = le32_to_cpu(bcon_trig->stop_consec_missed_bcon); - stop_trig_missed_bcon_since_rx = - le32_to_cpu(bcon_trig->stop_consec_missed_bcon_since_rx); - - /* TODO: implement start trigger */ - - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) - return; - - if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx || - rx_missed_bcon >= stop_trig_missed_bcon) - iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); -} - -void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_missed_beacons_notif *mb = (void *)pkt->data; - - IWL_DEBUG_INFO(mvm, - "missed bcn mac_id=%u, consecutive=%u (%u, %u, %u)\n", - le32_to_cpu(mb->mac_id), - le32_to_cpu(mb->consec_missed_beacons), - le32_to_cpu(mb->consec_missed_beacons_since_last_rx), - le32_to_cpu(mb->num_recvd_beacons), - le32_to_cpu(mb->num_expected_beacons)); - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_beacon_loss_iterator, - mb); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c deleted file mode 100644 index 1fb684693040..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ /dev/null @@ -1,4260 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iwl-op-mode.h" -#include "iwl-io.h" -#include "mvm.h" -#include "sta.h" -#include "time-event.h" -#include "iwl-eeprom-parse.h" -#include "iwl-phy-db.h" -#include "testmode.h" -#include "iwl-fw-error-dump.h" -#include "iwl-prph.h" -#include "iwl-csr.h" -#include "iwl-nvm-parse.h" - -static const struct ieee80211_iface_limit iwl_mvm_limits[] = { - { - .max = 1, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_DEVICE), - }, -}; - -static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = { - { - .num_different_channels = 2, - .max_interfaces = 3, - .limits = iwl_mvm_limits, - .n_limits = ARRAY_SIZE(iwl_mvm_limits), - }, -}; - -#ifdef CONFIG_PM_SLEEP -static const struct nl80211_wowlan_tcp_data_token_feature -iwl_mvm_wowlan_tcp_token_feature = { - .min_len = 0, - .max_len = 255, - .bufsize = IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS, -}; - -static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = { - .tok = &iwl_mvm_wowlan_tcp_token_feature, - .data_payload_max = IWL_WOWLAN_TCP_MAX_PACKET_LEN - - sizeof(struct ethhdr) - - sizeof(struct iphdr) - - sizeof(struct tcphdr), - .data_interval_max = 65535, /* __le16 in API */ - .wake_payload_max = IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN - - sizeof(struct ethhdr) - - sizeof(struct iphdr) - - sizeof(struct tcphdr), - .seq = true, -}; -#endif - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING -/* - * Use the reserved field to indicate magic values. - * these values will only be used internally by the driver, - * and won't make it to the fw (reserved will be 0). - * BC_FILTER_MAGIC_IP - configure the val of this attribute to - * be the vif's ip address. in case there is not a single - * ip address (0, or more than 1), this attribute will - * be skipped. - * BC_FILTER_MAGIC_MAC - set the val of this attribute to - * the LSB bytes of the vif's mac address - */ -enum { - BC_FILTER_MAGIC_NONE = 0, - BC_FILTER_MAGIC_IP, - BC_FILTER_MAGIC_MAC, -}; - -static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = { - { - /* arp */ - .discard = 0, - .frame_type = BCAST_FILTER_FRAME_TYPE_ALL, - .attrs = { - { - /* frame type - arp, hw type - ethernet */ - .offset_type = - BCAST_FILTER_OFFSET_PAYLOAD_START, - .offset = sizeof(rfc1042_header), - .val = cpu_to_be32(0x08060001), - .mask = cpu_to_be32(0xffffffff), - }, - { - /* arp dest ip */ - .offset_type = - BCAST_FILTER_OFFSET_PAYLOAD_START, - .offset = sizeof(rfc1042_header) + 2 + - sizeof(struct arphdr) + - ETH_ALEN + sizeof(__be32) + - ETH_ALEN, - .mask = cpu_to_be32(0xffffffff), - /* mark it as special field */ - .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP), - }, - }, - }, - { - /* dhcp offer bcast */ - .discard = 0, - .frame_type = BCAST_FILTER_FRAME_TYPE_IPV4, - .attrs = { - { - /* udp dest port - 68 (bootp client)*/ - .offset_type = BCAST_FILTER_OFFSET_IP_END, - .offset = offsetof(struct udphdr, dest), - .val = cpu_to_be32(0x00440000), - .mask = cpu_to_be32(0xffff0000), - }, - { - /* dhcp - lsb bytes of client hw address */ - .offset_type = BCAST_FILTER_OFFSET_IP_END, - .offset = 38, - .mask = cpu_to_be32(0xffffffff), - /* mark it as special field */ - .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC), - }, - }, - }, - /* last filter must be empty */ - {}, -}; -#endif - -void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) -{ - if (!iwl_mvm_is_d0i3_supported(mvm)) - return; - - IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type); - spin_lock_bh(&mvm->refs_lock); - mvm->refs[ref_type]++; - spin_unlock_bh(&mvm->refs_lock); - iwl_trans_ref(mvm->trans); -} - -void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) -{ - if (!iwl_mvm_is_d0i3_supported(mvm)) - return; - - IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type); - spin_lock_bh(&mvm->refs_lock); - WARN_ON(!mvm->refs[ref_type]--); - spin_unlock_bh(&mvm->refs_lock); - iwl_trans_unref(mvm->trans); -} - -static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm, - enum iwl_mvm_ref_type except_ref) -{ - int i, j; - - if (!iwl_mvm_is_d0i3_supported(mvm)) - return; - - spin_lock_bh(&mvm->refs_lock); - for (i = 0; i < IWL_MVM_REF_COUNT; i++) { - if (except_ref == i || !mvm->refs[i]) - continue; - - IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d (%d)\n", - i, mvm->refs[i]); - for (j = 0; j < mvm->refs[i]; j++) - iwl_trans_unref(mvm->trans); - mvm->refs[i] = 0; - } - spin_unlock_bh(&mvm->refs_lock); -} - -bool iwl_mvm_ref_taken(struct iwl_mvm *mvm) -{ - int i; - bool taken = false; - - if (!iwl_mvm_is_d0i3_supported(mvm)) - return true; - - spin_lock_bh(&mvm->refs_lock); - for (i = 0; i < IWL_MVM_REF_COUNT; i++) { - if (mvm->refs[i]) { - taken = true; - break; - } - } - spin_unlock_bh(&mvm->refs_lock); - - return taken; -} - -int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) -{ - iwl_mvm_ref(mvm, ref_type); - - if (!wait_event_timeout(mvm->d0i3_exit_waitq, - !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), - HZ)) { - WARN_ON_ONCE(1); - iwl_mvm_unref(mvm, ref_type); - return -EIO; - } - - return 0; -} - -static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm) -{ - int i; - - memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts)); - for (i = 0; i < NUM_PHY_CTX; i++) { - mvm->phy_ctxts[i].id = i; - mvm->phy_ctxts[i].ref = 0; - } -} - -struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, - const char *alpha2, - enum iwl_mcc_source src_id, - bool *changed) -{ - struct ieee80211_regdomain *regd = NULL; - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mcc_update_resp *resp; - - IWL_DEBUG_LAR(mvm, "Getting regdomain data for %s from FW\n", alpha2); - - lockdep_assert_held(&mvm->mutex); - - resp = iwl_mvm_update_mcc(mvm, alpha2, src_id); - if (IS_ERR_OR_NULL(resp)) { - IWL_DEBUG_LAR(mvm, "Could not get update from FW %d\n", - PTR_ERR_OR_ZERO(resp)); - goto out; - } - - if (changed) - *changed = (resp->status == MCC_RESP_NEW_CHAN_PROFILE); - - regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg, - __le32_to_cpu(resp->n_channels), - resp->channels, - __le16_to_cpu(resp->mcc)); - /* Store the return source id */ - src_id = resp->source_id; - kfree(resp); - if (IS_ERR_OR_NULL(regd)) { - IWL_DEBUG_LAR(mvm, "Could not get parse update from FW %d\n", - PTR_ERR_OR_ZERO(regd)); - goto out; - } - - IWL_DEBUG_LAR(mvm, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n", - regd->alpha2, regd->alpha2[0], regd->alpha2[1], src_id); - mvm->lar_regdom_set = true; - mvm->mcc_src = src_id; - -out: - return regd; -} - -void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm) -{ - bool changed; - struct ieee80211_regdomain *regd; - - if (!iwl_mvm_is_lar_supported(mvm)) - return; - - regd = iwl_mvm_get_current_regdomain(mvm, &changed); - if (!IS_ERR_OR_NULL(regd)) { - /* only update the regulatory core if changed */ - if (changed) - regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); - - kfree(regd); - } -} - -struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm, - bool *changed) -{ - return iwl_mvm_get_regdomain(mvm->hw->wiphy, "ZZ", - iwl_mvm_is_wifi_mcc_supported(mvm) ? - MCC_SOURCE_GET_CURRENT : - MCC_SOURCE_OLD_FW, changed); -} - -int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm) -{ - enum iwl_mcc_source used_src; - struct ieee80211_regdomain *regd; - int ret; - bool changed; - const struct ieee80211_regdomain *r = - rtnl_dereference(mvm->hw->wiphy->regd); - - if (!r) - return -ENOENT; - - /* save the last source in case we overwrite it below */ - used_src = mvm->mcc_src; - if (iwl_mvm_is_wifi_mcc_supported(mvm)) { - /* Notify the firmware we support wifi location updates */ - regd = iwl_mvm_get_current_regdomain(mvm, NULL); - if (!IS_ERR_OR_NULL(regd)) - kfree(regd); - } - - /* Now set our last stored MCC and source */ - regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src, - &changed); - if (IS_ERR_OR_NULL(regd)) - return -EIO; - - /* update cfg80211 if the regdomain was changed */ - if (changed) - ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd); - else - ret = 0; - - kfree(regd); - return ret; -} - -int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) -{ - struct ieee80211_hw *hw = mvm->hw; - int num_mac, ret, i; - static const u32 mvm_ciphers[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, - }; - - /* Tell mac80211 our characteristics */ - ieee80211_hw_set(hw, SIGNAL_DBM); - ieee80211_hw_set(hw, SPECTRUM_MGMT); - ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); - ieee80211_hw_set(hw, QUEUE_CONTROL); - ieee80211_hw_set(hw, WANT_MONITOR_VIF); - ieee80211_hw_set(hw, SUPPORTS_PS); - ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); - ieee80211_hw_set(hw, AMPDU_AGGREGATION); - ieee80211_hw_set(hw, TIMING_BEACON_ONLY); - ieee80211_hw_set(hw, CONNECTION_MONITOR); - ieee80211_hw_set(hw, CHANCTX_STA_CSA); - ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); - ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); - - hw->queues = mvm->first_agg_queue; - hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; - hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | - IEEE80211_RADIOTAP_MCS_HAVE_STBC; - hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC | - IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED; - hw->rate_control_algorithm = "iwl-mvm-rs"; - hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES; - hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; - - BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 2); - memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers)); - hw->wiphy->n_cipher_suites = ARRAY_SIZE(mvm_ciphers); - hw->wiphy->cipher_suites = mvm->ciphers; - - /* - * Enable 11w if advertised by firmware and software crypto - * is not enabled (as the firmware will interpret some mgmt - * packets, so enabling it with software crypto isn't safe) - */ - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP && - !iwlwifi_mod_params.sw_crypto) { - ieee80211_hw_set(hw, MFP_CAPABLE); - mvm->ciphers[hw->wiphy->n_cipher_suites] = - WLAN_CIPHER_SUITE_AES_CMAC; - hw->wiphy->n_cipher_suites++; - } - - /* currently FW API supports only one optional cipher scheme */ - if (mvm->fw->cs[0].cipher) { - mvm->hw->n_cipher_schemes = 1; - mvm->hw->cipher_schemes = &mvm->fw->cs[0]; - mvm->ciphers[hw->wiphy->n_cipher_suites] = - mvm->fw->cs[0].cipher; - hw->wiphy->n_cipher_suites++; - } - - ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); - hw->wiphy->features |= - NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | - NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | - NL80211_FEATURE_ND_RANDOM_MAC_ADDR; - - hw->sta_data_size = sizeof(struct iwl_mvm_sta); - hw->vif_data_size = sizeof(struct iwl_mvm_vif); - hw->chanctx_data_size = sizeof(u16); - - hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_DEVICE) | - BIT(NL80211_IFTYPE_ADHOC); - - hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - hw->wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR; - if (iwl_mvm_is_lar_supported(mvm)) - hw->wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; - else - hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | - REGULATORY_DISABLE_BEACON_HINTS; - - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD) - hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; - - hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; - - hw->wiphy->iface_combinations = iwl_mvm_iface_combinations; - hw->wiphy->n_iface_combinations = - ARRAY_SIZE(iwl_mvm_iface_combinations); - - hw->wiphy->max_remain_on_channel_duration = 10000; - hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; - /* we can compensate an offset of up to 3 channels = 15 MHz */ - hw->wiphy->max_adj_channel_rssi_comp = 3 * 5; - - /* Extract MAC address */ - memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN); - hw->wiphy->addresses = mvm->addresses; - hw->wiphy->n_addresses = 1; - - /* Extract additional MAC addresses if available */ - num_mac = (mvm->nvm_data->n_hw_addrs > 1) ? - min(IWL_MVM_MAX_ADDRESSES, mvm->nvm_data->n_hw_addrs) : 1; - - for (i = 1; i < num_mac; i++) { - memcpy(mvm->addresses[i].addr, mvm->addresses[i-1].addr, - ETH_ALEN); - mvm->addresses[i].addr[5]++; - hw->wiphy->n_addresses++; - } - - iwl_mvm_reset_phy_ctxts(mvm); - - hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm); - - hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; - - BUILD_BUG_ON(IWL_MVM_SCAN_STOPPING_MASK & IWL_MVM_SCAN_MASK); - BUILD_BUG_ON(IWL_MVM_MAX_UMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK) || - IWL_MVM_MAX_LMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK)); - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) - mvm->max_scans = IWL_MVM_MAX_UMAC_SCANS; - else - mvm->max_scans = IWL_MVM_MAX_LMAC_SCANS; - - if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels) - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; - if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels) { - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_BEAMFORMER) && - fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_LQ_SS_PARAMS)) - hw->wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap |= - IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; - } - - hw->wiphy->hw_version = mvm->trans->hw_id; - - if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) - hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; - else - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; - hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; - hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES; - /* we create the 802.11 header and zero length SSID IE. */ - hw->wiphy->max_sched_scan_ie_len = - SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2; - hw->wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS; - hw->wiphy->max_sched_scan_plan_interval = U16_MAX; - - /* - * the firmware uses u8 for num of iterations, but 0xff is saved for - * infinite loop, so the maximum number of iterations is actually 254. - */ - hw->wiphy->max_sched_scan_plan_iterations = 254; - - hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN | - NL80211_FEATURE_LOW_PRIORITY_SCAN | - NL80211_FEATURE_P2P_GO_OPPPS | - NL80211_FEATURE_DYNAMIC_SMPS | - NL80211_FEATURE_STATIC_SMPS | - NL80211_FEATURE_SUPPORTS_WMM_ADMISSION; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT)) - hw->wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION; - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT)) - hw->wiphy->features |= NL80211_FEATURE_QUIET; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)) - hw->wiphy->features |= - NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT)) - hw->wiphy->features |= NL80211_FEATURE_WFA_TPC_IE_IN_PROBES; - - mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; - -#ifdef CONFIG_PM_SLEEP - if (iwl_mvm_is_d0i3_supported(mvm) && - device_can_wakeup(mvm->trans->dev)) { - mvm->wowlan.flags = WIPHY_WOWLAN_ANY; - hw->wiphy->wowlan = &mvm->wowlan; - } - - if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len && - mvm->trans->ops->d3_suspend && - mvm->trans->ops->d3_resume && - device_can_wakeup(mvm->trans->dev)) { - mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_RFKILL_RELEASE | - WIPHY_WOWLAN_NET_DETECT; - if (!iwlwifi_mod_params.sw_crypto) - mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | - WIPHY_WOWLAN_GTK_REKEY_FAILURE | - WIPHY_WOWLAN_4WAY_HANDSHAKE; - - mvm->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS; - mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN; - mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN; - mvm->wowlan.max_nd_match_sets = IWL_SCAN_MAX_PROFILES; - mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support; - hw->wiphy->wowlan = &mvm->wowlan; - } -#endif - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING - /* assign default bcast filtering configuration */ - mvm->bcast_filters = iwl_mvm_default_bcast_filters; -#endif - - ret = iwl_mvm_leds_init(mvm); - if (ret) - return ret; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_TDLS_SUPPORT)) { - IWL_DEBUG_TDLS(mvm, "TDLS supported\n"); - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; - ieee80211_hw_set(hw, TDLS_WIDER_BW); - } - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH)) { - IWL_DEBUG_TDLS(mvm, "TDLS channel switch supported\n"); - hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH; - } - - hw->netdev_features |= mvm->cfg->features; - if (!iwl_mvm_is_csum_supported(mvm)) - hw->netdev_features &= ~NETIF_F_RXCSUM; - - ret = ieee80211_register_hw(mvm->hw); - if (ret) - iwl_mvm_leds_exit(mvm); - - return ret; -} - -static bool iwl_mvm_defer_tx(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct sk_buff *skb) -{ - struct iwl_mvm_sta *mvmsta; - bool defer = false; - - /* - * double check the IN_D0I3 flag both before and after - * taking the spinlock, in order to prevent taking - * the spinlock when not needed. - */ - if (likely(!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status))) - return false; - - spin_lock(&mvm->d0i3_tx_lock); - /* - * testing the flag again ensures the skb dequeue - * loop (on d0i3 exit) hasn't run yet. - */ - if (!test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) - goto out; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (mvmsta->sta_id == IWL_MVM_STATION_COUNT || - mvmsta->sta_id != mvm->d0i3_ap_sta_id) - goto out; - - __skb_queue_tail(&mvm->d0i3_tx, skb); - ieee80211_stop_queues(mvm->hw); - - /* trigger wakeup */ - iwl_mvm_ref(mvm, IWL_MVM_REF_TX); - iwl_mvm_unref(mvm, IWL_MVM_REF_TX); - - defer = true; -out: - spin_unlock(&mvm->d0i3_tx_lock); - return defer; -} - -static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct ieee80211_sta *sta = control->sta; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (void *)skb->data; - - if (iwl_mvm_is_radio_killed(mvm)) { - IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n"); - goto drop; - } - - if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE && - !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) && - !test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) - goto drop; - - /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */ - if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER && - ieee80211_is_mgmt(hdr->frame_control) && - !ieee80211_is_deauth(hdr->frame_control) && - !ieee80211_is_disassoc(hdr->frame_control) && - !ieee80211_is_action(hdr->frame_control))) - sta = NULL; - - if (sta) { - if (iwl_mvm_defer_tx(mvm, sta, skb)) - return; - if (iwl_mvm_tx_skb(mvm, skb, sta)) - goto drop; - return; - } - - if (iwl_mvm_tx_skb_non_sta(mvm, skb)) - goto drop; - return; - drop: - ieee80211_free_txskb(hw, skb); -} - -static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg) -{ - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) - return false; - return true; -} - -static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) -{ - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) - return false; - if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG) - return true; - - /* enabled by default */ - return true; -} - -#define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \ - do { \ - if (!(le16_to_cpu(_tid_bm) & BIT(_tid))) \ - break; \ - iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt); \ - } while (0) - -static void -iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 rx_ba_ssn, - enum ieee80211_ampdu_mlme_action action) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_ba *ba_trig; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); - ba_trig = (void *)trig->data; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) - return; - - switch (action) { - case IEEE80211_AMPDU_TX_OPERATIONAL: { - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - - CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_start, tid, - "TX AGG START: MAC %pM tid %d ssn %d\n", - sta->addr, tid, tid_data->ssn); - break; - } - case IEEE80211_AMPDU_TX_STOP_CONT: - CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_stop, tid, - "TX AGG STOP: MAC %pM tid %d\n", - sta->addr, tid); - break; - case IEEE80211_AMPDU_RX_START: - CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_start, tid, - "RX AGG START: MAC %pM tid %d ssn %d\n", - sta->addr, tid, rx_ba_ssn); - break; - case IEEE80211_AMPDU_RX_STOP: - CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_stop, tid, - "RX AGG STOP: MAC %pM tid %d\n", - sta->addr, tid); - break; - default: - break; - } -} - -static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size, bool amsdu) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - bool tx_agg_ref = false; - - IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n", - sta->addr, tid, action); - - if (!(mvm->nvm_data->sku_cap_11n_enable)) - return -EACCES; - - /* return from D0i3 before starting a new Tx aggregation */ - switch (action) { - case IEEE80211_AMPDU_TX_START: - case IEEE80211_AMPDU_TX_STOP_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - case IEEE80211_AMPDU_TX_OPERATIONAL: - /* - * for tx start, wait synchronously until D0i3 exit to - * get the correct sequence number for the tid. - * additionally, some other ampdu actions use direct - * target access, which is not handled automatically - * by the trans layer (unlike commands), so wait for - * d0i3 exit in these cases as well. - */ - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_TX_AGG); - if (ret) - return ret; - - tx_agg_ref = true; - break; - default: - break; - } - - mutex_lock(&mvm->mutex); - - switch (action) { - case IEEE80211_AMPDU_RX_START: - if (!iwl_enable_rx_ampdu(mvm->cfg)) { - ret = -EINVAL; - break; - } - ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, *ssn, true); - break; - case IEEE80211_AMPDU_RX_STOP: - ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false); - break; - case IEEE80211_AMPDU_TX_START: - if (!iwl_enable_tx_ampdu(mvm->cfg)) { - ret = -EINVAL; - break; - } - ret = iwl_mvm_sta_tx_agg_start(mvm, vif, sta, tid, ssn); - break; - case IEEE80211_AMPDU_TX_STOP_CONT: - ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid); - break; - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size); - break; - default: - WARN_ON_ONCE(1); - ret = -EINVAL; - break; - } - - if (!ret) { - u16 rx_ba_ssn = 0; - - if (action == IEEE80211_AMPDU_RX_START) - rx_ba_ssn = *ssn; - - iwl_mvm_ampdu_check_trigger(mvm, vif, sta, tid, - rx_ba_ssn, action); - } - mutex_unlock(&mvm->mutex); - - /* - * If the tid is marked as started, we won't use it for offloaded - * traffic on the next D0i3 entry. It's safe to unref. - */ - if (tx_agg_ref) - iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG); - - return ret; -} - -static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->uploaded = false; - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - - spin_lock_bh(&mvm->time_event_lock); - iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data); - spin_unlock_bh(&mvm->time_event_lock); - - mvmvif->phy_ctxt = NULL; - memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data)); -} - -static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, - const void *data, size_t datalen) -{ - const struct iwl_mvm_dump_ptrs *dump_ptrs = data; - ssize_t bytes_read; - ssize_t bytes_read_trans; - - if (offset < dump_ptrs->op_mode_len) { - bytes_read = min_t(ssize_t, count, - dump_ptrs->op_mode_len - offset); - memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset, - bytes_read); - offset += bytes_read; - count -= bytes_read; - - if (count == 0) - return bytes_read; - } else { - bytes_read = 0; - } - - if (!dump_ptrs->trans_ptr) - return bytes_read; - - offset -= dump_ptrs->op_mode_len; - bytes_read_trans = min_t(ssize_t, count, - dump_ptrs->trans_ptr->len - offset); - memcpy(buffer + bytes_read, - (u8 *)dump_ptrs->trans_ptr->data + offset, - bytes_read_trans); - - return bytes_read + bytes_read_trans; -} - -static void iwl_mvm_free_coredump(const void *data) -{ - const struct iwl_mvm_dump_ptrs *fw_error_dump = data; - - vfree(fw_error_dump->op_mode_ptr); - vfree(fw_error_dump->trans_ptr); - kfree(fw_error_dump); -} - -static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, - struct iwl_fw_error_dump_data **dump_data) -{ - struct iwl_fw_error_dump_fifo *fifo_hdr; - u32 *fifo_data; - u32 fifo_len; - unsigned long flags; - int i, j; - - if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) - return; - - /* Pull RXF data from all RXFs */ - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) { - /* - * Keep aside the additional offset that might be needed for - * next RXF - */ - u32 offset_diff = RXF_DIFF_FROM_PREV * i; - - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = mvm->shared_mem_cfg.rxfifo_size[i]; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - continue; - - /* Add a TLV for the RXF */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(i); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_D_SPACE + - offset_diff)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_WR_PTR + - offset_diff)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_RD_PTR + - offset_diff)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_FENCE_PTR + - offset_diff)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_SET_FENCE_MODE + - offset_diff)); - - /* Lock fence */ - iwl_trans_write_prph(mvm->trans, - RXF_SET_FENCE_MODE + offset_diff, 0x1); - /* Set fence pointer to the same place like WR pointer */ - iwl_trans_write_prph(mvm->trans, - RXF_LD_WR2FENCE + offset_diff, 0x1); - /* Set fence offset */ - iwl_trans_write_prph(mvm->trans, - RXF_LD_FENCE_OFFSET_ADDR + offset_diff, - 0x0); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (j = 0; j < fifo_len; j++) - fifo_data[j] = iwl_trans_read_prph(mvm->trans, - RXF_FIFO_RD_FENCE_INC + - offset_diff); - *dump_data = iwl_fw_error_next_data(*dump_data); - } - - /* Pull TXF data from all TXFs */ - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) { - /* Mark the number of TXF we're pulling now */ - iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); - - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = mvm->shared_mem_cfg.txfifo_size[i]; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - continue; - - /* Add a TLV for the FIFO */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(i); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_FIFO_ITEM_CNT)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_WR_PTR)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_RD_PTR)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_FENCE_PTR)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_LOCK_FENCE)); - - /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ - iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR, - TXF_WR_PTR); - - /* Dummy-read to advance the read pointer to the head */ - iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (j = 0; j < fifo_len; j++) - fifo_data[j] = iwl_trans_read_prph(mvm->trans, - TXF_READ_MODIFY_DATA); - *dump_data = iwl_fw_error_next_data(*dump_data); - } - - iwl_trans_release_nic_access(mvm->trans, &flags); -} - -void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) -{ - if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert || - !mvm->fw_dump_desc) - return; - - kfree(mvm->fw_dump_desc); - mvm->fw_dump_desc = NULL; -} - -#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ -#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ - -void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) -{ - struct iwl_fw_error_dump_file *dump_file; - struct iwl_fw_error_dump_data *dump_data; - struct iwl_fw_error_dump_info *dump_info; - struct iwl_fw_error_dump_mem *dump_mem; - struct iwl_fw_error_dump_trigger_desc *dump_trig; - struct iwl_mvm_dump_ptrs *fw_error_dump; - u32 sram_len, sram_ofs; - u32 file_len, fifo_data_len = 0; - u32 smem_len = mvm->cfg->smem_len; - u32 sram2_len = mvm->cfg->dccm2_len; - bool monitor_dump_only = false; - - lockdep_assert_held(&mvm->mutex); - - /* there's no point in fw dump if the bus is dead */ - if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { - IWL_ERR(mvm, "Skip fw error dump since bus is dead\n"); - return; - } - - if (mvm->fw_dump_trig && - mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY) - monitor_dump_only = true; - - fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); - if (!fw_error_dump) - return; - - /* SRAM - include stack CCM if driver knows the values for it */ - if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { - const struct fw_img *img; - - img = &mvm->fw->img[mvm->cur_ucode]; - sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; - } else { - sram_ofs = mvm->cfg->dccm_offset; - sram_len = mvm->cfg->dccm_len; - } - - /* reading RXF/TXF sizes */ - if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { - struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg; - int i; - - fifo_data_len = 0; - - /* Count RXF size */ - for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) { - if (!mem_cfg->rxfifo_size[i]) - continue; - - /* Add header info */ - fifo_data_len += mem_cfg->rxfifo_size[i] + - sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - } - - for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) { - if (!mem_cfg->txfifo_size[i]) - continue; - - /* Add header info */ - fifo_data_len += mem_cfg->txfifo_size[i] + - sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - } - } - - file_len = sizeof(*dump_file) + - sizeof(*dump_data) * 2 + - sram_len + sizeof(*dump_mem) + - fifo_data_len + - sizeof(*dump_info); - - /* Make room for the SMEM, if it exists */ - if (smem_len) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; - - /* Make room for the secondary SRAM, if it exists */ - if (sram2_len) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; - - /* Make room for fw's virtual image pages, if it exists */ - if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) - file_len += mvm->num_of_paging_blk * - (sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_paging) + - PAGING_BLOCK_SIZE); - - /* If we only want a monitor dump, reset the file length */ - if (monitor_dump_only) { - file_len = sizeof(*dump_file) + sizeof(*dump_data) + - sizeof(*dump_info); - } - - /* - * In 8000 HW family B-step include the ICCM (which resides separately) - */ - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && - CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + - IWL8260_ICCM_LEN; - - if (mvm->fw_dump_desc) - file_len += sizeof(*dump_data) + sizeof(*dump_trig) + - mvm->fw_dump_desc->len; - - dump_file = vzalloc(file_len); - if (!dump_file) { - kfree(fw_error_dump); - iwl_mvm_free_fw_dump_desc(mvm); - return; - } - - fw_error_dump->op_mode_ptr = dump_file; - - dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); - dump_data = (void *)dump_file->data; - - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); - dump_data->len = cpu_to_le32(sizeof(*dump_info)); - dump_info = (void *) dump_data->data; - dump_info->device_family = - mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? - cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : - cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); - dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev)); - memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, - sizeof(dump_info->fw_human_readable)); - strncpy(dump_info->dev_human_readable, mvm->cfg->name, - sizeof(dump_info->dev_human_readable)); - strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, - sizeof(dump_info->bus_human_readable)); - - dump_data = iwl_fw_error_next_data(dump_data); - /* We only dump the FIFOs if the FW is in error state */ - if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) - iwl_mvm_dump_fifos(mvm, &dump_data); - - if (mvm->fw_dump_desc) { - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); - dump_data->len = cpu_to_le32(sizeof(*dump_trig) + - mvm->fw_dump_desc->len); - dump_trig = (void *)dump_data->data; - memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc, - sizeof(*dump_trig) + mvm->fw_dump_desc->len); - - /* now we can free this copy */ - iwl_mvm_free_fw_dump_desc(mvm); - dump_data = iwl_fw_error_next_data(dump_data); - } - - /* In case we only want monitor dump, skip to dump trasport data */ - if (monitor_dump_only) - goto dump_trans_data; - - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(sram_ofs); - iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data, - sram_len); - - if (smem_len) { - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); - dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset); - iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, - dump_mem->data, smem_len); - } - - if (sram2_len) { - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset); - iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset, - dump_mem->data, sram2_len); - } - - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && - CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) { - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN + - sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET); - iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET, - dump_mem->data, IWL8260_ICCM_LEN); - } - - /* Dump fw's virtual image */ - if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) { - u32 i; - - for (i = 1; i < mvm->num_of_paging_blk + 1; i++) { - struct iwl_fw_error_dump_paging *paging; - struct page *pages = - mvm->fw_paging_db[i].fw_paging_block; - - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); - dump_data->len = cpu_to_le32(sizeof(*paging) + - PAGING_BLOCK_SIZE); - paging = (void *)dump_data->data; - paging->index = cpu_to_le32(i); - memcpy(paging->data, page_address(pages), - PAGING_BLOCK_SIZE); - } - } - -dump_trans_data: - fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans, - mvm->fw_dump_trig); - fw_error_dump->op_mode_len = file_len; - if (fw_error_dump->trans_ptr) - file_len += fw_error_dump->trans_ptr->len; - dump_file->file_len = cpu_to_le32(file_len); - - dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, - GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); - - mvm->fw_dump_trig = NULL; - clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); -} - -struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { - .trig_desc = { - .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), - }, -}; - -static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) -{ - /* clear the D3 reconfig, we only need it to avoid dumping a - * firmware coredump on reconfiguration, we shouldn't do that - * on D3->D0 transition - */ - if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) { - mvm->fw_dump_desc = &iwl_mvm_dump_desc_assert; - iwl_mvm_fw_error_dump(mvm); - } - - /* cleanup all stale references (scan, roc), but keep the - * ucode_down ref until reconfig is complete - */ - iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN); - - iwl_trans_stop_device(mvm->trans); - - mvm->scan_status = 0; - mvm->ps_disabled = false; - mvm->calibrating = false; - - /* just in case one was running */ - ieee80211_remain_on_channel_expired(mvm->hw); - - /* - * cleanup all interfaces, even inactive ones, as some might have - * gone down during the HW restart - */ - ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_cleanup_iterator, mvm); - - mvm->p2p_device_vif = NULL; - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; - - iwl_mvm_reset_phy_ctxts(mvm); - memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained)); - memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained)); - memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); - memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old)); - memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); - memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old)); - memset(&mvm->bt_ack_kill_msk, 0, sizeof(mvm->bt_ack_kill_msk)); - memset(&mvm->bt_cts_kill_msk, 0, sizeof(mvm->bt_cts_kill_msk)); - - ieee80211_wake_queues(mvm->hw); - - /* clear any stale d0i3 state */ - clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); - - mvm->vif_count = 0; - mvm->rx_ba_sessions = 0; - mvm->fw_dbg_conf = FW_DBG_INVALID; - - /* keep statistics ticking */ - iwl_mvm_accu_radio_stats(mvm); -} - -int __iwl_mvm_mac_start(struct iwl_mvm *mvm) -{ - int ret; - - lockdep_assert_held(&mvm->mutex); - - /* Clean up some internal and mac80211 state on restart */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - iwl_mvm_restart_cleanup(mvm); - - ret = iwl_mvm_up(mvm); - - if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - /* Something went wrong - we need to finish some cleanup - * that normally iwl_mvm_mac_restart_complete() below - * would do. - */ - clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - iwl_mvm_d0i3_enable_tx(mvm, NULL); - } - - return ret; -} - -static int iwl_mvm_mac_start(struct ieee80211_hw *hw) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - /* Some hw restart cleanups must not hold the mutex */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - /* - * Make sure we are out of d0i3. This is needed - * to make sure the reference accounting is correct - * (and there is no stale d0i3_exit_work). - */ - wait_event_timeout(mvm->d0i3_exit_waitq, - !test_bit(IWL_MVM_STATUS_IN_D0I3, - &mvm->status), - HZ); - } - - mutex_lock(&mvm->mutex); - ret = __iwl_mvm_mac_start(mvm); - mutex_unlock(&mvm->mutex); - - return ret; -} - -static void iwl_mvm_restart_complete(struct iwl_mvm *mvm) -{ - int ret; - - mutex_lock(&mvm->mutex); - - clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - iwl_mvm_d0i3_enable_tx(mvm, NULL); - ret = iwl_mvm_update_quotas(mvm, true, NULL); - if (ret) - IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n", - ret); - - /* allow transport/FW low power modes */ - iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); - - /* - * If we have TDLS peers, remove them. We don't know the last seqno/PN - * of packets the FW sent out, so we must reconnect. - */ - iwl_mvm_teardown_tdls_peers(mvm); - - mutex_unlock(&mvm->mutex); -} - -static void iwl_mvm_resume_complete(struct iwl_mvm *mvm) -{ - if (!iwl_mvm_is_d0i3_supported(mvm)) - return; - - if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) - if (!wait_event_timeout(mvm->d0i3_exit_waitq, - !test_bit(IWL_MVM_STATUS_IN_D0I3, - &mvm->status), - HZ)) - WARN_ONCE(1, "D0i3 exit on resume timed out\n"); -} - -static void -iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw, - enum ieee80211_reconfig_type reconfig_type) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - switch (reconfig_type) { - case IEEE80211_RECONFIG_TYPE_RESTART: - iwl_mvm_restart_complete(mvm); - break; - case IEEE80211_RECONFIG_TYPE_SUSPEND: - iwl_mvm_resume_complete(mvm); - break; - } -} - -void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) -{ - lockdep_assert_held(&mvm->mutex); - - /* firmware counters are obviously reset now, but we shouldn't - * partially track so also clear the fw_reset_accu counters. - */ - memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats)); - - /* - * Disallow low power states when the FW is down by taking - * the UCODE_DOWN ref. in case of ongoing hw restart the - * ref is already taken, so don't take it again. - */ - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); - - /* async_handlers_wk is now blocked */ - - /* - * The work item could be running or queued if the - * ROC time event stops just as we get here. - */ - flush_work(&mvm->roc_done_wk); - - iwl_trans_stop_device(mvm->trans); - - iwl_mvm_async_handlers_purge(mvm); - /* async_handlers_list is empty and will stay empty: HW is stopped */ - - /* the fw is stopped, the aux sta is dead: clean up driver state */ - iwl_mvm_del_aux_sta(mvm); - - /* - * Clear IN_HW_RESTART flag when stopping the hw (as restart_complete() - * won't be called in this case). - * But make sure to cleanup interfaces that have gone down before/during - * HW restart was requested. - */ - if (test_and_clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - ieee80211_iterate_interfaces(mvm->hw, 0, - iwl_mvm_cleanup_iterator, mvm); - - /* We shouldn't have any UIDs still set. Loop over all the UIDs to - * make sure there's nothing left there and warn if any is found. - */ - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - int i; - - for (i = 0; i < mvm->max_scans; i++) { - if (WARN_ONCE(mvm->scan_uid_status[i], - "UMAC scan UID %d status was not cleaned\n", - i)) - mvm->scan_uid_status[i] = 0; - } - } - - mvm->ucode_loaded = false; -} - -static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - flush_work(&mvm->d0i3_exit_work); - flush_work(&mvm->async_handlers_wk); - cancel_delayed_work_sync(&mvm->fw_dump_wk); - iwl_mvm_free_fw_dump_desc(mvm); - - mutex_lock(&mvm->mutex); - __iwl_mvm_mac_stop(mvm); - mutex_unlock(&mvm->mutex); - - /* - * The worker might have been waiting for the mutex, let it run and - * discover that its list is now empty. - */ - cancel_work_sync(&mvm->async_handlers_wk); -} - -static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) -{ - u16 i; - - lockdep_assert_held(&mvm->mutex); - - for (i = 0; i < NUM_PHY_CTX; i++) - if (!mvm->phy_ctxts[i].ref) - return &mvm->phy_ctxts[i]; - - IWL_ERR(mvm, "No available PHY context\n"); - return NULL; -} - -static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - s16 tx_power) -{ - struct iwl_dev_tx_power_cmd cmd = { - .v2.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_MAC), - .v2.mac_context_id = - cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id), - .v2.pwr_restriction = cpu_to_le16(8 * tx_power), - }; - int len = sizeof(cmd); - - if (tx_power == IWL_DEFAULT_MAX_TX_POWER) - cmd.v2.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER); - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TX_POWER_CHAIN)) - len = sizeof(cmd.v2); - - return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); -} - -static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - mvmvif->mvm = mvm; - - /* - * make sure D0i3 exit is completed, otherwise a target access - * during tx queue configuration could be done when still in - * D0i3 state. - */ - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_ADD_IF); - if (ret) - return ret; - - /* - * Not much to do here. The stack will not allow interface - * types or combinations that we didn't advertise, so we - * don't really have to check the types. - */ - - mutex_lock(&mvm->mutex); - - /* make sure that beacon statistics don't go backwards with FW reset */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - mvmvif->beacon_stats.accu_num_beacons += - mvmvif->beacon_stats.num_beacons; - - /* Allocate resources for the MAC context, and add it to the fw */ - ret = iwl_mvm_mac_ctxt_init(mvm, vif); - if (ret) - goto out_unlock; - - /* Counting number of interfaces is needed for legacy PM */ - if (vif->type != NL80211_IFTYPE_P2P_DEVICE) - mvm->vif_count++; - - /* - * The AP binding flow can be done only after the beacon - * template is configured (which happens only in the mac80211 - * start_ap() flow), and adding the broadcast station can happen - * only after the binding. - * In addition, since modifying the MAC before adding a bcast - * station is not allowed by the FW, delay the adding of MAC context to - * the point where we can also add the bcast station. - * In short: there's not much we can do at this point, other than - * allocating resources :) - */ - if (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_ADHOC) { - ret = iwl_mvm_alloc_bcast_sta(mvm, vif); - if (ret) { - IWL_ERR(mvm, "Failed to allocate bcast sta\n"); - goto out_release; - } - - iwl_mvm_vif_dbgfs_register(mvm, vif); - goto out_unlock; - } - - mvmvif->features |= hw->netdev_features; - - ret = iwl_mvm_mac_ctxt_add(mvm, vif); - if (ret) - goto out_release; - - ret = iwl_mvm_power_update_mac(mvm); - if (ret) - goto out_remove_mac; - - /* beacon filtering */ - ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); - if (ret) - goto out_remove_mac; - - if (!mvm->bf_allowed_vif && - vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { - mvm->bf_allowed_vif = mvmvif; - vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | - IEEE80211_VIF_SUPPORTS_CQM_RSSI; - } - - /* - * P2P_DEVICE interface does not have a channel context assigned to it, - * so a dedicated PHY context is allocated to it and the corresponding - * MAC context is bound to it at this stage. - */ - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - - mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); - if (!mvmvif->phy_ctxt) { - ret = -ENOSPC; - goto out_free_bf; - } - - iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); - ret = iwl_mvm_binding_add_vif(mvm, vif); - if (ret) - goto out_unref_phy; - - ret = iwl_mvm_add_bcast_sta(mvm, vif); - if (ret) - goto out_unbind; - - /* Save a pointer to p2p device vif, so it can later be used to - * update the p2p device MAC when a GO is started/stopped */ - mvm->p2p_device_vif = vif; - } - - iwl_mvm_vif_dbgfs_register(mvm, vif); - goto out_unlock; - - out_unbind: - iwl_mvm_binding_remove_vif(mvm, vif); - out_unref_phy: - iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); - out_free_bf: - if (mvm->bf_allowed_vif == mvmvif) { - mvm->bf_allowed_vif = NULL; - vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | - IEEE80211_VIF_SUPPORTS_CQM_RSSI); - } - out_remove_mac: - mvmvif->phy_ctxt = NULL; - iwl_mvm_mac_ctxt_remove(mvm, vif); - out_release: - if (vif->type != NL80211_IFTYPE_P2P_DEVICE) - mvm->vif_count--; - - iwl_mvm_mac_ctxt_release(mvm, vif); - out_unlock: - mutex_unlock(&mvm->mutex); - - iwl_mvm_unref(mvm, IWL_MVM_REF_ADD_IF); - - return ret; -} - -static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif); - - if (tfd_msk) { - /* - * mac80211 first removes all the stations of the vif and - * then removes the vif. When it removes a station it also - * flushes the AMPDU session. So by now, all the AMPDU sessions - * of all the stations of this vif are closed, and the queues - * of these AMPDU sessions are properly closed. - * We still need to take care of the shared queues of the vif. - * Flush them here. - */ - mutex_lock(&mvm->mutex); - iwl_mvm_flush_tx_path(mvm, tfd_msk, 0); - mutex_unlock(&mvm->mutex); - - /* - * There are transports that buffer a few frames in the host. - * For these, the flush above isn't enough since while we were - * flushing, the transport might have sent more frames to the - * device. To solve this, wait here until the transport is - * empty. Technically, this could have replaced the flush - * above, but flush is much faster than draining. So flush - * first, and drain to make sure we have no frames in the - * transport anymore. - * If a station still had frames on the shared queues, it is - * already marked as draining, so to complete the draining, we - * just need to wait until the transport is empty. - */ - iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_msk); - } - - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - /* - * Flush the ROC worker which will flush the OFFCHANNEL queue. - * We assume here that all the packets sent to the OFFCHANNEL - * queue are sent in ROC session. - */ - flush_work(&mvm->roc_done_wk); - } else { - /* - * By now, all the AC queues are empty. The AGG queues are - * empty too. We already got all the Tx responses for all the - * packets in the queues. The drain work can have been - * triggered. Flush it. - */ - flush_work(&mvm->sta_drained_wk); - } -} - -static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - iwl_mvm_prepare_mac_removal(mvm, vif); - - mutex_lock(&mvm->mutex); - - if (mvm->bf_allowed_vif == mvmvif) { - mvm->bf_allowed_vif = NULL; - vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | - IEEE80211_VIF_SUPPORTS_CQM_RSSI); - } - - iwl_mvm_vif_dbgfs_clean(mvm, vif); - - /* - * For AP/GO interface, the tear down of the resources allocated to the - * interface is be handled as part of the stop_ap flow. - */ - if (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_ADHOC) { -#ifdef CONFIG_NL80211_TESTMODE - if (vif == mvm->noa_vif) { - mvm->noa_vif = NULL; - mvm->noa_duration = 0; - } -#endif - iwl_mvm_dealloc_bcast_sta(mvm, vif); - goto out_release; - } - - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - mvm->p2p_device_vif = NULL; - iwl_mvm_rm_bcast_sta(mvm, vif); - iwl_mvm_binding_remove_vif(mvm, vif); - iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); - mvmvif->phy_ctxt = NULL; - } - - if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE) - mvm->vif_count--; - - iwl_mvm_power_update_mac(mvm); - iwl_mvm_mac_ctxt_remove(mvm, vif); - -out_release: - iwl_mvm_mac_ctxt_release(mvm, vif); - mutex_unlock(&mvm->mutex); -} - -static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed) -{ - return 0; -} - -struct iwl_mvm_mc_iter_data { - struct iwl_mvm *mvm; - int port_id; -}; - -static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_mc_iter_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd; - int ret, len; - - /* if we don't have free ports, mcast frames will be dropped */ - if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM)) - return; - - if (vif->type != NL80211_IFTYPE_STATION || - !vif->bss_conf.assoc) - return; - - cmd->port_id = data->port_id++; - memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); - len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4); - - ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_ASYNC, len, cmd); - if (ret) - IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret); -} - -static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm) -{ - struct iwl_mvm_mc_iter_data iter_data = { - .mvm = mvm, - }; - - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON_ONCE(!mvm->mcast_filter_cmd)) - return; - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_mc_iface_iterator, &iter_data); -} - -static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw, - struct netdev_hw_addr_list *mc_list) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mcast_filter_cmd *cmd; - struct netdev_hw_addr *addr; - int addr_count; - bool pass_all; - int len; - - addr_count = netdev_hw_addr_list_count(mc_list); - pass_all = addr_count > MAX_MCAST_FILTERING_ADDRESSES || - IWL_MVM_FW_MCAST_FILTER_PASS_ALL; - if (pass_all) - addr_count = 0; - - len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4); - cmd = kzalloc(len, GFP_ATOMIC); - if (!cmd) - return 0; - - if (pass_all) { - cmd->pass_all = 1; - return (u64)(unsigned long)cmd; - } - - netdev_hw_addr_list_for_each(addr, mc_list) { - IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n", - cmd->count, addr->addr); - memcpy(&cmd->addr_list[cmd->count * ETH_ALEN], - addr->addr, ETH_ALEN); - cmd->count++; - } - - return (u64)(unsigned long)cmd; -} - -static void iwl_mvm_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast; - - mutex_lock(&mvm->mutex); - - /* replace previous configuration */ - kfree(mvm->mcast_filter_cmd); - mvm->mcast_filter_cmd = cmd; - - if (!cmd) - goto out; - - iwl_mvm_recalc_multicast(mvm); -out: - mutex_unlock(&mvm->mutex); - *total_flags = 0; -} - -static void iwl_mvm_config_iface_filter(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - unsigned int filter_flags, - unsigned int changed_flags) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - /* We support only filter for probe requests */ - if (!(changed_flags & FIF_PROBE_REQ)) - return; - - /* Supported only for p2p client interfaces */ - if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc || - !vif->p2p) - return; - - mutex_lock(&mvm->mutex); - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - mutex_unlock(&mvm->mutex); -} - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING -struct iwl_bcast_iter_data { - struct iwl_mvm *mvm; - struct iwl_bcast_filter_cmd *cmd; - u8 current_filter; -}; - -static void -iwl_mvm_set_bcast_filter(struct ieee80211_vif *vif, - const struct iwl_fw_bcast_filter *in_filter, - struct iwl_fw_bcast_filter *out_filter) -{ - struct iwl_fw_bcast_filter_attr *attr; - int i; - - memcpy(out_filter, in_filter, sizeof(*out_filter)); - - for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) { - attr = &out_filter->attrs[i]; - - if (!attr->mask) - break; - - switch (attr->reserved1) { - case cpu_to_le16(BC_FILTER_MAGIC_IP): - if (vif->bss_conf.arp_addr_cnt != 1) { - attr->mask = 0; - continue; - } - - attr->val = vif->bss_conf.arp_addr_list[0]; - break; - case cpu_to_le16(BC_FILTER_MAGIC_MAC): - attr->val = *(__be32 *)&vif->addr[2]; - break; - default: - break; - } - attr->reserved1 = 0; - out_filter->num_attrs++; - } -} - -static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_bcast_iter_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - struct iwl_bcast_filter_cmd *cmd = data->cmd; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_fw_bcast_mac *bcast_mac; - int i; - - if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs))) - return; - - bcast_mac = &cmd->macs[mvmvif->id]; - - /* - * enable filtering only for associated stations, but not for P2P - * Clients - */ - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p || - !vif->bss_conf.assoc) - return; - - bcast_mac->default_discard = 1; - - /* copy all configured filters */ - for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) { - /* - * Make sure we don't exceed our filters limit. - * if there is still a valid filter to be configured, - * be on the safe side and just allow bcast for this mac. - */ - if (WARN_ON_ONCE(data->current_filter >= - ARRAY_SIZE(cmd->filters))) { - bcast_mac->default_discard = 0; - bcast_mac->attached_filters = 0; - break; - } - - iwl_mvm_set_bcast_filter(vif, - &mvm->bcast_filters[i], - &cmd->filters[data->current_filter]); - - /* skip current filter if it contains no attributes */ - if (!cmd->filters[data->current_filter].num_attrs) - continue; - - /* attach the filter to current mac */ - bcast_mac->attached_filters |= - cpu_to_le16(BIT(data->current_filter)); - - data->current_filter++; - } -} - -bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, - struct iwl_bcast_filter_cmd *cmd) -{ - struct iwl_bcast_iter_data iter_data = { - .mvm = mvm, - .cmd = cmd, - }; - - if (IWL_MVM_FW_BCAST_FILTER_PASS_ALL) - return false; - - memset(cmd, 0, sizeof(*cmd)); - cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters); - cmd->max_macs = ARRAY_SIZE(cmd->macs); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - /* use debugfs filters/macs if override is configured */ - if (mvm->dbgfs_bcast_filtering.override) { - memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters, - sizeof(cmd->filters)); - memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs, - sizeof(cmd->macs)); - return true; - } -#endif - - /* if no filters are configured, do nothing */ - if (!mvm->bcast_filters) - return false; - - /* configure and attach these filters for each associated sta vif */ - ieee80211_iterate_active_interfaces( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bcast_filter_iterator, &iter_data); - - return true; -} -static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_bcast_filter_cmd cmd; - - if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING)) - return 0; - - if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) - return 0; - - return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, - sizeof(cmd), &cmd); -} -#else -static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - return 0; -} -#endif - -static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - /* - * Re-calculate the tsf id, as the master-slave relations depend on the - * beacon interval, which was not known when the station interface was - * added. - */ - if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) - iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); - - /* - * If we're not associated yet, take the (new) BSSID before associating - * so the firmware knows. If we're already associated, then use the old - * BSSID here, and we'll send a cleared one later in the CHANGED_ASSOC - * branch for disassociation below. - */ - if (changes & BSS_CHANGED_BSSID && !mvmvif->associated) - memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN); - - ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, mvmvif->bssid); - if (ret) - IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); - - /* after sending it once, adopt mac80211 data */ - memcpy(mvmvif->bssid, bss_conf->bssid, ETH_ALEN); - mvmvif->associated = bss_conf->assoc; - - if (changes & BSS_CHANGED_ASSOC) { - if (bss_conf->assoc) { - /* clear statistics to get clean beacon counter */ - iwl_mvm_request_statistics(mvm, true); - memset(&mvmvif->beacon_stats, 0, - sizeof(mvmvif->beacon_stats)); - - /* add quota for this interface */ - ret = iwl_mvm_update_quotas(mvm, true, NULL); - if (ret) { - IWL_ERR(mvm, "failed to update quotas\n"); - return; - } - - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, - &mvm->status)) { - /* - * If we're restarting then the firmware will - * obviously have lost synchronisation with - * the AP. It will attempt to synchronise by - * itself, but we can make it more reliable by - * scheduling a session protection time event. - * - * The firmware needs to receive a beacon to - * catch up with synchronisation, use 110% of - * the beacon interval. - * - * Set a large maximum delay to allow for more - * than a single interface. - */ - u32 dur = (11 * vif->bss_conf.beacon_int) / 10; - iwl_mvm_protect_session(mvm, vif, dur, dur, - 5 * dur, false); - } - - iwl_mvm_sf_update(mvm, vif, false); - iwl_mvm_power_vif_assoc(mvm, vif); - if (vif->p2p) { - iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT); - iwl_mvm_update_smps(mvm, vif, - IWL_MVM_SMPS_REQ_PROT, - IEEE80211_SMPS_DYNAMIC); - } - } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { - /* - * If update fails - SF might be running in associated - * mode while disassociated - which is forbidden. - */ - WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false), - "Failed to update SF upon disassociation\n"); - - /* remove AP station now that the MAC is unassoc */ - ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); - if (ret) - IWL_ERR(mvm, "failed to remove AP station\n"); - - if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - /* remove quota for this interface */ - ret = iwl_mvm_update_quotas(mvm, false, NULL); - if (ret) - IWL_ERR(mvm, "failed to update quotas\n"); - - if (vif->p2p) - iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT); - - /* this will take the cleared BSSID from bss_conf */ - ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - if (ret) - IWL_ERR(mvm, - "failed to update MAC %pM (clear after unassoc)\n", - vif->addr); - } - - iwl_mvm_recalc_multicast(mvm); - iwl_mvm_configure_bcast_filter(mvm, vif); - - /* reset rssi values */ - mvmvif->bf_data.ave_beacon_signal = 0; - - 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 - * remove the session protection. - */ - iwl_mvm_remove_time_event(mvm, mvmvif, - &mvmvif->time_event_data); - } - - if (changes & BSS_CHANGED_BEACON_INFO) { - iwl_mvm_sf_update(mvm, vif, false); - WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); - } - - if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS)) { - ret = iwl_mvm_power_update_mac(mvm); - if (ret) - IWL_ERR(mvm, "failed to update power mode\n"); - } - - if (changes & BSS_CHANGED_TXPOWER) { - IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n", - bss_conf->txpower); - iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower); - } - - if (changes & BSS_CHANGED_CQM) { - IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n"); - /* reset cqm events tracking */ - mvmvif->bf_data.last_cqm_event = 0; - if (mvmvif->bf_data.bf_enabled) { - ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); - if (ret) - IWL_ERR(mvm, - "failed to update CQM thresholds\n"); - } - } - - if (changes & BSS_CHANGED_ARP_FILTER) { - IWL_DEBUG_MAC80211(mvm, "arp filter changed\n"); - iwl_mvm_configure_bcast_filter(mvm, vif); - } -} - -static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - /* - * iwl_mvm_mac_ctxt_add() might read directly from the device - * (the system time), so make sure it is available. - */ - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_START_AP); - if (ret) - return ret; - - mutex_lock(&mvm->mutex); - - /* Send the beacon template */ - ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif); - if (ret) - goto out_unlock; - - /* - * Re-calculate the tsf id, as the master-slave relations depend on the - * beacon interval, which was not known when the AP interface was added. - */ - if (vif->type == NL80211_IFTYPE_AP) - iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); - - mvmvif->ap_assoc_sta_count = 0; - - /* Add the mac context */ - ret = iwl_mvm_mac_ctxt_add(mvm, vif); - if (ret) - goto out_unlock; - - /* Perform the binding */ - ret = iwl_mvm_binding_add_vif(mvm, vif); - if (ret) - goto out_remove; - - /* Send the bcast station. At this stage the TBTT and DTIM time events - * are added and applied to the scheduler */ - ret = iwl_mvm_send_add_bcast_sta(mvm, vif); - if (ret) - goto out_unbind; - - /* must be set before quota calculations */ - mvmvif->ap_ibss_active = true; - - /* power updated needs to be done before quotas */ - iwl_mvm_power_update_mac(mvm); - - ret = iwl_mvm_update_quotas(mvm, false, NULL); - if (ret) - goto out_quota_failed; - - /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ - if (vif->p2p && mvm->p2p_device_vif) - iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL); - - iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS); - - iwl_mvm_bt_coex_vif_change(mvm); - - /* we don't support TDLS during DCM */ - if (iwl_mvm_phy_ctx_count(mvm) > 1) - iwl_mvm_teardown_tdls_peers(mvm); - - goto out_unlock; - -out_quota_failed: - iwl_mvm_power_update_mac(mvm); - mvmvif->ap_ibss_active = false; - iwl_mvm_send_rm_bcast_sta(mvm, vif); -out_unbind: - iwl_mvm_binding_remove_vif(mvm, vif); -out_remove: - iwl_mvm_mac_ctxt_remove(mvm, vif); -out_unlock: - mutex_unlock(&mvm->mutex); - iwl_mvm_unref(mvm, IWL_MVM_REF_START_AP); - return ret; -} - -static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - iwl_mvm_prepare_mac_removal(mvm, vif); - - mutex_lock(&mvm->mutex); - - /* Handle AP stop while in CSA */ - if (rcu_access_pointer(mvm->csa_vif) == vif) { - iwl_mvm_remove_time_event(mvm, mvmvif, - &mvmvif->time_event_data); - RCU_INIT_POINTER(mvm->csa_vif, NULL); - mvmvif->csa_countdown = false; - } - - if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) { - RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL); - mvm->csa_tx_block_bcn_timeout = 0; - } - - mvmvif->ap_ibss_active = false; - mvm->ap_last_beacon_gp2 = 0; - - iwl_mvm_bt_coex_vif_change(mvm); - - iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS); - - /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ - if (vif->p2p && mvm->p2p_device_vif) - iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL); - - iwl_mvm_update_quotas(mvm, false, NULL); - iwl_mvm_send_rm_bcast_sta(mvm, vif); - iwl_mvm_binding_remove_vif(mvm, vif); - - iwl_mvm_power_update_mac(mvm); - - iwl_mvm_mac_ctxt_remove(mvm, vif); - - mutex_unlock(&mvm->mutex); -} - -static void -iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - /* Changes will be applied when the AP/IBSS is started */ - if (!mvmvif->ap_ibss_active) - return; - - if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT | - BSS_CHANGED_BANDWIDTH | BSS_CHANGED_QOS) && - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL)) - IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); - - /* Need to send a new beacon template to the FW */ - if (changes & BSS_CHANGED_BEACON && - iwl_mvm_mac_ctxt_beacon_changed(mvm, vif)) - IWL_WARN(mvm, "Failed updating beacon data\n"); - - if (changes & BSS_CHANGED_TXPOWER) { - IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n", - bss_conf->txpower); - iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower); - } - -} - -static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - /* - * iwl_mvm_bss_info_changed_station() might call - * iwl_mvm_protect_session(), which reads directly from - * the device (the system time), so make sure it is available. - */ - if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_BSS_CHANGED)) - return; - - mutex_lock(&mvm->mutex); - - if (changes & BSS_CHANGED_IDLE && !bss_conf->idle) - iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes); - break; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_ADHOC: - iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes); - break; - default: - /* shouldn't happen */ - WARN_ON_ONCE(1); - } - - mutex_unlock(&mvm->mutex); - iwl_mvm_unref(mvm, IWL_MVM_REF_BSS_CHANGED); -} - -static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - if (hw_req->req.n_channels == 0 || - hw_req->req.n_channels > mvm->fw->ucode_capa.n_scan_channels) - return -EINVAL; - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_reg_scan_start(mvm, vif, &hw_req->req, &hw_req->ies); - mutex_unlock(&mvm->mutex); - - return ret; -} - -static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - mutex_lock(&mvm->mutex); - - /* Due to a race condition, it's possible that mac80211 asks - * us to stop a hw_scan when it's already stopped. This can - * happen, for instance, if we stopped the scan ourselves, - * called ieee80211_scan_completed() and the userspace called - * cancel scan scan before ieee80211_scan_work() could run. - * To handle that, simply return if the scan is not running. - */ - if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) - iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); - - mutex_unlock(&mvm->mutex); -} - -static void -iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, u16 tids, - int num_frames, - enum ieee80211_frame_release_type reason, - bool more_data) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - /* Called when we need to transmit (a) frame(s) from mac80211 */ - - iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, - tids, more_data, false); -} - -static void -iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, u16 tids, - int num_frames, - enum ieee80211_frame_release_type reason, - bool more_data) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - /* Called when we need to transmit (a) frame(s) from agg queue */ - - iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, - tids, more_data, true); -} - -static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - unsigned long txqs = 0, tids = 0; - int tid; - - spin_lock_bh(&mvmsta->lock); - for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - - if (tid_data->state != IWL_AGG_ON && - tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA) - continue; - - __set_bit(tid_data->txq_id, &txqs); - - if (iwl_mvm_tid_queued(tid_data) == 0) - continue; - - __set_bit(tid, &tids); - } - - switch (cmd) { - case STA_NOTIFY_SLEEP: - if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0) - ieee80211_sta_block_awake(hw, sta, true); - - for_each_set_bit(tid, &tids, IWL_MAX_TID_COUNT) - ieee80211_sta_set_buffered(sta, tid, true); - - if (txqs) - iwl_trans_freeze_txq_timer(mvm->trans, txqs, true); - /* - * The fw updates the STA to be asleep. Tx packets on the Tx - * queues to this station will not be transmitted. The fw will - * send a Tx response with TX_STATUS_FAIL_DEST_PS. - */ - break; - case STA_NOTIFY_AWAKE: - if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) - break; - - if (txqs) - iwl_trans_freeze_txq_timer(mvm->trans, txqs, false); - iwl_mvm_sta_modify_ps_wake(mvm, sta); - break; - default: - break; - } - spin_unlock_bh(&mvmsta->lock); -} - -static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - - /* - * This is called before mac80211 does RCU synchronisation, - * so here we already invalidate our internal RCU-protected - * station pointer. The rest of the code will thus no longer - * be able to find the station this way, and we don't rely - * on further RCU synchronisation after the sta_state() - * callback deleted the station. - */ - mutex_lock(&mvm->mutex); - if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id])) - rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], - ERR_PTR(-ENOENT)); - - if (mvm_sta->vif->type == NL80211_IFTYPE_AP) { - mvmvif->ap_assoc_sta_count--; - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - } - - mutex_unlock(&mvm->mutex); -} - -static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - const u8 *bssid) -{ - if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT)) - return; - - if (iwlwifi_mod_params.uapsd_disable) { - vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; - return; - } - - vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; -} - -static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - enum ieee80211_sta_state old_state, - enum ieee80211_sta_state new_state) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n", - sta->addr, old_state, new_state); - - /* this would be a mac80211 bug ... but don't crash */ - if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) - return -EINVAL; - - /* if a STA is being removed, reuse its ID */ - flush_work(&mvm->sta_drained_wk); - - mutex_lock(&mvm->mutex); - if (old_state == IEEE80211_STA_NOTEXIST && - new_state == IEEE80211_STA_NONE) { - /* - * Firmware bug - it'll crash if the beacon interval is less - * than 16. We can't avoid connecting at all, so refuse the - * station state change, this will cause mac80211 to abandon - * attempts to connect to this AP, and eventually wpa_s will - * blacklist the AP... - */ - if (vif->type == NL80211_IFTYPE_STATION && - vif->bss_conf.beacon_int < 16) { - IWL_ERR(mvm, - "AP %pM beacon interval is %d, refusing due to firmware bug!\n", - sta->addr, vif->bss_conf.beacon_int); - ret = -EINVAL; - goto out_unlock; - } - - if (sta->tdls && - (vif->p2p || - iwl_mvm_tdls_sta_count(mvm, NULL) == - IWL_MVM_TDLS_STA_COUNT || - iwl_mvm_phy_ctx_count(mvm) > 1)) { - IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n"); - ret = -EBUSY; - goto out_unlock; - } - - ret = iwl_mvm_add_sta(mvm, vif, sta); - if (sta->tdls && ret == 0) - iwl_mvm_recalc_tdls_state(mvm, vif, true); - } else if (old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_AUTH) { - /* - * EBS may be disabled due to previous failures reported by FW. - * Reset EBS status here assuming environment has been changed. - */ - mvm->last_ebs_successful = true; - iwl_mvm_check_uapsd(mvm, vif, sta->addr); - ret = 0; - } else if (old_state == IEEE80211_STA_AUTH && - new_state == IEEE80211_STA_ASSOC) { - ret = iwl_mvm_update_sta(mvm, vif, sta); - if (ret == 0) - iwl_mvm_rs_rate_init(mvm, sta, - mvmvif->phy_ctxt->channel->band, - true); - } else if (old_state == IEEE80211_STA_ASSOC && - new_state == IEEE80211_STA_AUTHORIZED) { - - /* we don't support TDLS during DCM */ - if (iwl_mvm_phy_ctx_count(mvm) > 1) - iwl_mvm_teardown_tdls_peers(mvm); - - /* enable beacon filtering */ - WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); - ret = 0; - } else if (old_state == IEEE80211_STA_AUTHORIZED && - new_state == IEEE80211_STA_ASSOC) { - /* disable beacon filtering */ - WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, 0)); - ret = 0; - } else if (old_state == IEEE80211_STA_ASSOC && - new_state == IEEE80211_STA_AUTH) { - ret = 0; - } else if (old_state == IEEE80211_STA_AUTH && - new_state == IEEE80211_STA_NONE) { - ret = 0; - } else if (old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_NOTEXIST) { - ret = iwl_mvm_rm_sta(mvm, vif, sta); - if (sta->tdls) - iwl_mvm_recalc_tdls_state(mvm, vif, false); - } else { - ret = -EIO; - } - out_unlock: - mutex_unlock(&mvm->mutex); - - if (sta->tdls && ret == 0) { - if (old_state == IEEE80211_STA_NOTEXIST && - new_state == IEEE80211_STA_NONE) - ieee80211_reserve_tid(sta, IWL_MVM_TDLS_FW_TID); - else if (old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_NOTEXIST) - ieee80211_unreserve_tid(sta, IWL_MVM_TDLS_FW_TID); - } - - return ret; -} - -static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - mvm->rts_threshold = value; - - return 0; -} - -static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u32 changed) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - if (vif->type == NL80211_IFTYPE_STATION && - changed & IEEE80211_RC_NSS_CHANGED) - iwl_mvm_sf_update(mvm, vif, false); -} - -static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 ac, - const struct ieee80211_tx_queue_params *params) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->queue_params[ac] = *params; - - /* - * No need to update right away, we'll get BSS_CHANGED_QOS - * The exception is P2P_DEVICE interface which needs immediate update. - */ - if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - int ret; - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - mutex_unlock(&mvm->mutex); - return ret; - } - return 0; -} - -static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - u32 duration = min(IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS, - 200 + vif->bss_conf.beacon_int); - u32 min_duration = min(IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS, - 100 + vif->bss_conf.beacon_int); - - if (WARN_ON_ONCE(vif->bss_conf.assoc)) - return; - - /* - * iwl_mvm_protect_session() reads directly from the device - * (the system time), so make sure it is available. - */ - if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PREPARE_TX)) - return; - - mutex_lock(&mvm->mutex); - /* Try really hard to protect the session and hear a beacon */ - iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500, false); - mutex_unlock(&mvm->mutex); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PREPARE_TX); -} - -static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_sched_scan_request *req, - struct ieee80211_scan_ies *ies) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - int ret; - - mutex_lock(&mvm->mutex); - - if (!vif->bss_conf.idle) { - ret = -EBUSY; - goto out; - } - - ret = iwl_mvm_sched_scan_start(mvm, vif, req, ies, IWL_MVM_SCAN_SCHED); - -out: - mutex_unlock(&mvm->mutex); - return ret; -} - -static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - mutex_lock(&mvm->mutex); - - /* Due to a race condition, it's possible that mac80211 asks - * us to stop a sched_scan when it's already stopped. This - * can happen, for instance, if we stopped the scan ourselves, - * called ieee80211_sched_scan_stopped() and the userspace called - * stop sched scan scan before ieee80211_sched_scan_stopped_work() - * could run. To handle this, simply return if the scan is - * not running. - */ - if (!(mvm->scan_status & IWL_MVM_SCAN_SCHED)) { - mutex_unlock(&mvm->mutex); - return 0; - } - - ret = iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, false); - mutex_unlock(&mvm->mutex); - iwl_mvm_wait_for_async_handlers(mvm); - - return ret; -} - -static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, - enum set_key_cmd cmd, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - if (iwlwifi_mod_params.sw_crypto) { - IWL_DEBUG_MAC80211(mvm, "leave - hwcrypto disabled\n"); - return -EOPNOTSUPP; - } - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - break; - case WLAN_CIPHER_SUITE_CCMP: - key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - WARN_ON_ONCE(!ieee80211_hw_check(hw, MFP_CAPABLE)); - break; - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - /* For non-client mode, only use WEP keys for TX as we probably - * don't have a station yet anyway and would then have to keep - * track of the keys, linking them to each of the clients/peers - * as they appear. For now, don't do that, for performance WEP - * offload doesn't really matter much, but we need it for some - * other offload features in client mode. - */ - if (vif->type != NL80211_IFTYPE_STATION) - return 0; - break; - default: - /* currently FW supports only one optional cipher scheme */ - if (hw->n_cipher_schemes && - hw->cipher_schemes->cipher == key->cipher) - key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; - else - return -EOPNOTSUPP; - } - - mutex_lock(&mvm->mutex); - - switch (cmd) { - case SET_KEY: - if ((vif->type == NL80211_IFTYPE_ADHOC || - vif->type == NL80211_IFTYPE_AP) && !sta) { - /* - * GTK on AP interface is a TX-only key, return 0; - * on IBSS they're per-station and because we're lazy - * we don't support them for RX, so do the same. - */ - ret = 0; - key->hw_key_idx = STA_KEY_IDX_INVALID; - break; - } - - /* During FW restart, in order to restore the state as it was, - * don't try to reprogram keys we previously failed for. - */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && - key->hw_key_idx == STA_KEY_IDX_INVALID) { - IWL_DEBUG_MAC80211(mvm, - "skip invalid idx key programming during restart\n"); - ret = 0; - break; - } - - IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n"); - ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, - test_bit(IWL_MVM_STATUS_IN_HW_RESTART, - &mvm->status)); - if (ret) { - IWL_WARN(mvm, "set key failed\n"); - /* - * can't add key for RX, but we don't need it - * in the device for TX so still return 0 - */ - key->hw_key_idx = STA_KEY_IDX_INVALID; - ret = 0; - } - - break; - case DISABLE_KEY: - if (key->hw_key_idx == STA_KEY_IDX_INVALID) { - ret = 0; - break; - } - - IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n"); - ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key); - break; - default: - ret = -EINVAL; - } - - mutex_unlock(&mvm->mutex); - return ret; -} - -static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, - u32 iv32, u16 *phase1key) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID) - return; - - iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key); -} - - -static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_mvm *mvm = - container_of(notif_wait, struct iwl_mvm, notif_wait); - struct iwl_hs20_roc_res *resp; - int resp_len = iwl_rx_packet_payload_len(pkt); - struct iwl_mvm_time_event_data *te_data = data; - - if (WARN_ON(pkt->hdr.cmd != HOT_SPOT_CMD)) - return true; - - if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { - IWL_ERR(mvm, "Invalid HOT_SPOT_CMD response\n"); - return true; - } - - resp = (void *)pkt->data; - - IWL_DEBUG_TE(mvm, - "Aux ROC: Recieved response from ucode: status=%d uid=%d\n", - resp->status, resp->event_unique_id); - - te_data->uid = le32_to_cpu(resp->event_unique_id); - IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", - te_data->uid); - - spin_lock_bh(&mvm->time_event_lock); - list_add_tail(&te_data->list, &mvm->aux_roc_te_list); - spin_unlock_bh(&mvm->time_event_lock); - - return true; -} - -#define AUX_ROC_MAX_DELAY_ON_CHANNEL 200 -static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, - struct ieee80211_channel *channel, - struct ieee80211_vif *vif, - int duration) -{ - int res, time_reg = DEVICE_SYSTEM_TIME_REG; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data; - static const u16 time_event_response[] = { HOT_SPOT_CMD }; - struct iwl_notification_wait wait_time_event; - struct iwl_hs20_roc_req aux_roc_req = { - .action = cpu_to_le32(FW_CTXT_ACTION_ADD), - .id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(MAC_INDEX_AUX, 0)), - .sta_id_and_color = cpu_to_le32(mvm->aux_sta.sta_id), - /* Set the channel info data */ - .channel_info.band = (channel->band == IEEE80211_BAND_2GHZ) ? - PHY_BAND_24 : PHY_BAND_5, - .channel_info.channel = channel->hw_value, - .channel_info.width = PHY_VHT_CHANNEL_MODE20, - /* Set the time and duration */ - .apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)), - .apply_time_max_delay = - cpu_to_le32(MSEC_TO_TU(AUX_ROC_MAX_DELAY_ON_CHANNEL)), - .duration = cpu_to_le32(MSEC_TO_TU(duration)), - }; - - /* Set the node address */ - memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN); - - lockdep_assert_held(&mvm->mutex); - - spin_lock_bh(&mvm->time_event_lock); - - if (WARN_ON(te_data->id == HOT_SPOT_CMD)) { - spin_unlock_bh(&mvm->time_event_lock); - return -EIO; - } - - te_data->vif = vif; - te_data->duration = duration; - te_data->id = HOT_SPOT_CMD; - - spin_unlock_bh(&mvm->time_event_lock); - - /* - * Use a notification wait, which really just processes the - * command response and doesn't wait for anything, in order - * to be able to process the response and get the UID inside - * the RX path. Using CMD_WANT_SKB doesn't work because it - * stores the buffer and then wakes up this thread, by which - * time another notification (that the time event started) - * might already be processed unsuccessfully. - */ - iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, - time_event_response, - ARRAY_SIZE(time_event_response), - iwl_mvm_rx_aux_roc, te_data); - - res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, sizeof(aux_roc_req), - &aux_roc_req); - - if (res) { - IWL_ERR(mvm, "Couldn't send HOT_SPOT_CMD: %d\n", res); - iwl_remove_notification(&mvm->notif_wait, &wait_time_event); - goto out_clear_te; - } - - /* No need to wait for anything, so just pass 1 (0 isn't valid) */ - res = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1); - /* should never fail */ - WARN_ON_ONCE(res); - - if (res) { - out_clear_te: - spin_lock_bh(&mvm->time_event_lock); - iwl_mvm_te_clear_data(mvm, te_data); - spin_unlock_bh(&mvm->time_event_lock); - } - - return res; -} - -static int iwl_mvm_roc(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_channel *channel, - int duration, - enum ieee80211_roc_type type) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct cfg80211_chan_def chandef; - struct iwl_mvm_phy_ctxt *phy_ctxt; - int ret, i; - - IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, - duration, type); - - flush_work(&mvm->roc_done_wk); - - mutex_lock(&mvm->mutex); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) { - /* Use aux roc framework (HS20) */ - ret = iwl_mvm_send_aux_roc_cmd(mvm, channel, - vif, duration); - goto out_unlock; - } - IWL_ERR(mvm, "hotspot not supported\n"); - ret = -EINVAL; - goto out_unlock; - case NL80211_IFTYPE_P2P_DEVICE: - /* handle below */ - break; - default: - IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type); - ret = -EINVAL; - goto out_unlock; - } - - for (i = 0; i < NUM_PHY_CTX; i++) { - phy_ctxt = &mvm->phy_ctxts[i]; - if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt) - continue; - - if (phy_ctxt->ref && channel == phy_ctxt->channel) { - /* - * Unbind the P2P_DEVICE from the current PHY context, - * and if the PHY context is not used remove it. - */ - ret = iwl_mvm_binding_remove_vif(mvm, vif); - if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) - goto out_unlock; - - iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); - - /* Bind the P2P_DEVICE to the current PHY Context */ - mvmvif->phy_ctxt = phy_ctxt; - - ret = iwl_mvm_binding_add_vif(mvm, vif); - if (WARN(ret, "Failed binding P2P_DEVICE\n")) - goto out_unlock; - - iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); - goto schedule_time_event; - } - } - - /* Need to update the PHY context only if the ROC channel changed */ - if (channel == mvmvif->phy_ctxt->channel) - goto schedule_time_event; - - cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); - - /* - * Change the PHY context configuration as it is currently referenced - * only by the P2P Device MAC - */ - if (mvmvif->phy_ctxt->ref == 1) { - ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt, - &chandef, 1, 1); - if (ret) - goto out_unlock; - } else { - /* - * The PHY context is shared with other MACs. Need to remove the - * P2P Device from the binding, allocate an new PHY context and - * create a new binding - */ - phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); - if (!phy_ctxt) { - ret = -ENOSPC; - goto out_unlock; - } - - ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef, - 1, 1); - if (ret) { - IWL_ERR(mvm, "Failed to change PHY context\n"); - goto out_unlock; - } - - /* Unbind the P2P_DEVICE from the current PHY context */ - ret = iwl_mvm_binding_remove_vif(mvm, vif); - if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) - goto out_unlock; - - iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); - - /* Bind the P2P_DEVICE to the new allocated PHY context */ - mvmvif->phy_ctxt = phy_ctxt; - - ret = iwl_mvm_binding_add_vif(mvm, vif); - if (WARN(ret, "Failed binding P2P_DEVICE\n")) - goto out_unlock; - - iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); - } - -schedule_time_event: - /* Schedule the time events */ - ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type); - -out_unlock: - mutex_unlock(&mvm->mutex); - IWL_DEBUG_MAC80211(mvm, "leave\n"); - return ret; -} - -static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - IWL_DEBUG_MAC80211(mvm, "enter\n"); - - mutex_lock(&mvm->mutex); - iwl_mvm_stop_roc(mvm); - mutex_unlock(&mvm->mutex); - - IWL_DEBUG_MAC80211(mvm, "leave\n"); - return 0; -} - -static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm, - struct ieee80211_chanctx_conf *ctx) -{ - u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; - struct iwl_mvm_phy_ctxt *phy_ctxt; - int ret; - - lockdep_assert_held(&mvm->mutex); - - IWL_DEBUG_MAC80211(mvm, "Add channel context\n"); - - phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); - if (!phy_ctxt) { - ret = -ENOSPC; - goto out; - } - - ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def, - ctx->rx_chains_static, - ctx->rx_chains_dynamic); - if (ret) { - IWL_ERR(mvm, "Failed to add PHY context\n"); - goto out; - } - - iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt); - *phy_ctxt_id = phy_ctxt->id; -out: - return ret; -} - -static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - mutex_lock(&mvm->mutex); - ret = __iwl_mvm_add_chanctx(mvm, ctx); - mutex_unlock(&mvm->mutex); - - return ret; -} - -static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm, - struct ieee80211_chanctx_conf *ctx) -{ - u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; - struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; - - lockdep_assert_held(&mvm->mutex); - - iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt); -} - -static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - mutex_lock(&mvm->mutex); - __iwl_mvm_remove_chanctx(mvm, ctx); - mutex_unlock(&mvm->mutex); -} - -static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx, - u32 changed) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; - struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; - - if (WARN_ONCE((phy_ctxt->ref > 1) && - (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH | - IEEE80211_CHANCTX_CHANGE_RX_CHAINS | - IEEE80211_CHANCTX_CHANGE_RADAR | - IEEE80211_CHANCTX_CHANGE_MIN_WIDTH)), - "Cannot change PHY. Ref=%d, changed=0x%X\n", - phy_ctxt->ref, changed)) - return; - - mutex_lock(&mvm->mutex); - iwl_mvm_bt_coex_vif_change(mvm); - iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def, - ctx->rx_chains_static, - ctx->rx_chains_dynamic); - mutex_unlock(&mvm->mutex); -} - -static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx, - bool switching_chanctx) -{ - u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; - struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - lockdep_assert_held(&mvm->mutex); - - mvmvif->phy_ctxt = phy_ctxt; - - switch (vif->type) { - case NL80211_IFTYPE_AP: - /* only needed if we're switching chanctx (i.e. during CSA) */ - if (switching_chanctx) { - mvmvif->ap_ibss_active = true; - break; - } - case NL80211_IFTYPE_ADHOC: - /* - * The AP binding flow is handled as part of the start_ap flow - * (in bss_info_changed), similarly for IBSS. - */ - ret = 0; - goto out; - case NL80211_IFTYPE_STATION: - break; - case NL80211_IFTYPE_MONITOR: - /* always disable PS when a monitor interface is active */ - mvmvif->ps_disabled = true; - break; - default: - ret = -EINVAL; - goto out; - } - - ret = iwl_mvm_binding_add_vif(mvm, vif); - if (ret) - goto out; - - /* - * Power state must be updated before quotas, - * otherwise fw will complain. - */ - iwl_mvm_power_update_mac(mvm); - - /* Setting the quota at this stage is only required for monitor - * interfaces. For the other types, the bss_info changed flow - * will handle quota settings. - */ - if (vif->type == NL80211_IFTYPE_MONITOR) { - mvmvif->monitor_active = true; - ret = iwl_mvm_update_quotas(mvm, false, NULL); - if (ret) - goto out_remove_binding; - } - - /* Handle binding during CSA */ - if (vif->type == NL80211_IFTYPE_AP) { - iwl_mvm_update_quotas(mvm, false, NULL); - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - } - - if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) { - u32 duration = 2 * vif->bss_conf.beacon_int; - - /* iwl_mvm_protect_session() reads directly from the - * device (the system time), so make sure it is - * available. - */ - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA); - if (ret) - goto out_remove_binding; - - /* Protect the session to make sure we hear the first - * beacon on the new channel. - */ - iwl_mvm_protect_session(mvm, vif, duration, duration, - vif->bss_conf.beacon_int / 2, - true); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA); - - iwl_mvm_update_quotas(mvm, false, NULL); - } - - goto out; - -out_remove_binding: - iwl_mvm_binding_remove_vif(mvm, vif); - iwl_mvm_power_update_mac(mvm); -out: - if (ret) - mvmvif->phy_ctxt = NULL; - return ret; -} -static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - mutex_lock(&mvm->mutex); - ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false); - mutex_unlock(&mvm->mutex); - - return ret; -} - -static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx, - bool switching_chanctx) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_vif *disabled_vif = NULL; - - lockdep_assert_held(&mvm->mutex); - - iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); - - switch (vif->type) { - case NL80211_IFTYPE_ADHOC: - goto out; - case NL80211_IFTYPE_MONITOR: - mvmvif->monitor_active = false; - mvmvif->ps_disabled = false; - break; - case NL80211_IFTYPE_AP: - /* This part is triggered only during CSA */ - if (!switching_chanctx || !mvmvif->ap_ibss_active) - goto out; - - mvmvif->csa_countdown = false; - - /* Set CS bit on all the stations */ - iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true); - - /* Save blocked iface, the timeout is set on the next beacon */ - rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif); - - mvmvif->ap_ibss_active = false; - break; - case NL80211_IFTYPE_STATION: - if (!switching_chanctx) - break; - - disabled_vif = vif; - - iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL); - break; - default: - break; - } - - iwl_mvm_update_quotas(mvm, false, disabled_vif); - iwl_mvm_binding_remove_vif(mvm, vif); - -out: - mvmvif->phy_ctxt = NULL; - iwl_mvm_power_update_mac(mvm); -} - -static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_chanctx_conf *ctx) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - mutex_lock(&mvm->mutex); - __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false); - mutex_unlock(&mvm->mutex); -} - -static int -iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm, - struct ieee80211_vif_chanctx_switch *vifs) -{ - int ret; - - mutex_lock(&mvm->mutex); - __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true); - __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx); - - ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx); - if (ret) { - IWL_ERR(mvm, "failed to add new_ctx during channel switch\n"); - goto out_reassign; - } - - ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx, - true); - if (ret) { - IWL_ERR(mvm, - "failed to assign new_ctx during channel switch\n"); - goto out_remove; - } - - /* we don't support TDLS during DCM - can be caused by channel switch */ - if (iwl_mvm_phy_ctx_count(mvm) > 1) - iwl_mvm_teardown_tdls_peers(mvm); - - goto out; - -out_remove: - __iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx); - -out_reassign: - if (__iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx)) { - IWL_ERR(mvm, "failed to add old_ctx back after failure.\n"); - goto out_restart; - } - - if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, - true)) { - IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n"); - goto out_restart; - } - - goto out; - -out_restart: - /* things keep failing, better restart the hw */ - iwl_mvm_nic_restart(mvm, false); - -out: - mutex_unlock(&mvm->mutex); - - return ret; -} - -static int -iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm, - struct ieee80211_vif_chanctx_switch *vifs) -{ - int ret; - - mutex_lock(&mvm->mutex); - __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true); - - ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx, - true); - if (ret) { - IWL_ERR(mvm, - "failed to assign new_ctx during channel switch\n"); - goto out_reassign; - } - - goto out; - -out_reassign: - if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, - true)) { - IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n"); - goto out_restart; - } - - goto out; - -out_restart: - /* things keep failing, better restart the hw */ - iwl_mvm_nic_restart(mvm, false); - -out: - mutex_unlock(&mvm->mutex); - - return ret; -} - -static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw, - struct ieee80211_vif_chanctx_switch *vifs, - int n_vifs, - enum ieee80211_chanctx_switch_mode mode) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - /* we only support a single-vif right now */ - if (n_vifs > 1) - return -EOPNOTSUPP; - - switch (mode) { - case CHANCTX_SWMODE_SWAP_CONTEXTS: - ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs); - break; - case CHANCTX_SWMODE_REASSIGN_VIF: - ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs); - break; - default: - ret = -EOPNOTSUPP; - break; - } - - return ret; -} - -static int iwl_mvm_set_tim(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, - bool set) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - - if (!mvm_sta || !mvm_sta->vif) { - IWL_ERR(mvm, "Station is not associated to a vif\n"); - return -EINVAL; - } - - return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif); -} - -#ifdef CONFIG_NL80211_TESTMODE -static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = { - [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 }, - [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 }, - [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 }, -}; - -static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - void *data, int len) -{ - struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1]; - int err; - u32 noa_duration; - - err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy); - if (err) - return err; - - if (!tb[IWL_MVM_TM_ATTR_CMD]) - return -EINVAL; - - switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) { - case IWL_MVM_TM_CMD_SET_NOA: - if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p || - !vif->bss_conf.enable_beacon || - !tb[IWL_MVM_TM_ATTR_NOA_DURATION]) - return -EINVAL; - - noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]); - if (noa_duration >= vif->bss_conf.beacon_int) - return -EINVAL; - - mvm->noa_duration = noa_duration; - mvm->noa_vif = vif; - - return iwl_mvm_update_quotas(mvm, false, NULL); - case IWL_MVM_TM_CMD_SET_BEACON_FILTER: - /* must be associated client vif - ignore authorized */ - if (!vif || vif->type != NL80211_IFTYPE_STATION || - !vif->bss_conf.assoc || !vif->bss_conf.dtim_period || - !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]) - return -EINVAL; - - if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])) - return iwl_mvm_enable_beacon_filter(mvm, vif, 0); - return iwl_mvm_disable_beacon_filter(mvm, vif, 0); - } - - return -EOPNOTSUPP; -} - -static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - void *data, int len) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int err; - - mutex_lock(&mvm->mutex); - err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len); - mutex_unlock(&mvm->mutex); - - return err; -} -#endif - -static void iwl_mvm_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_channel_switch *chsw) -{ - /* By implementing this operation, we prevent mac80211 from - * starting its own channel switch timer, so that we can call - * ieee80211_chswitch_done() ourselves at the right time - * (which is when the absence time event starts). - */ - - IWL_DEBUG_MAC80211(IWL_MAC80211_GET_MVM(hw), - "dummy channel switch op\n"); -} - -static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_channel_switch *chsw) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct ieee80211_vif *csa_vif; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u32 apply_time; - int ret; - - mutex_lock(&mvm->mutex); - - mvmvif->csa_failed = false; - - IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n", - chsw->chandef.center_freq1); - - iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH); - - switch (vif->type) { - case NL80211_IFTYPE_AP: - csa_vif = - rcu_dereference_protected(mvm->csa_vif, - lockdep_is_held(&mvm->mutex)); - if (WARN_ONCE(csa_vif && csa_vif->csa_active, - "Another CSA is already in progress")) { - ret = -EBUSY; - goto out_unlock; - } - - rcu_assign_pointer(mvm->csa_vif, vif); - - if (WARN_ONCE(mvmvif->csa_countdown, - "Previous CSA countdown didn't complete")) { - ret = -EBUSY; - goto out_unlock; - } - - break; - case NL80211_IFTYPE_STATION: - /* Schedule the time event to a bit before beacon 1, - * to make sure we're in the new channel when the - * GO/AP arrives. - */ - apply_time = chsw->device_timestamp + - ((vif->bss_conf.beacon_int * (chsw->count - 1) - - IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024); - - if (chsw->block_tx) - iwl_mvm_csa_client_absent(mvm, vif); - - iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int, - apply_time); - if (mvmvif->bf_data.bf_enabled) { - ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); - if (ret) - goto out_unlock; - } - - break; - default: - break; - } - - mvmvif->ps_disabled = true; - - ret = iwl_mvm_power_update_ps(mvm); - if (ret) - goto out_unlock; - - /* we won't be on this channel any longer */ - iwl_mvm_teardown_tdls_peers(mvm); - -out_unlock: - mutex_unlock(&mvm->mutex); - - return ret; -} - -static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - mutex_lock(&mvm->mutex); - - if (mvmvif->csa_failed) { - mvmvif->csa_failed = false; - ret = -EIO; - goto out_unlock; - } - - if (vif->type == NL80211_IFTYPE_STATION) { - struct iwl_mvm_sta *mvmsta; - - mvmsta = iwl_mvm_sta_from_staid_protected(mvm, - mvmvif->ap_sta_id); - - if (WARN_ON(!mvmsta)) { - ret = -EIO; - goto out_unlock; - } - - iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false); - - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - - ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); - if (ret) - goto out_unlock; - - iwl_mvm_stop_session_protection(mvm, vif); - } - - mvmvif->ps_disabled = false; - - ret = iwl_mvm_power_update_ps(mvm); - -out_unlock: - mutex_unlock(&mvm->mutex); - - return ret; -} - -static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u32 queues, bool drop) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif; - struct iwl_mvm_sta *mvmsta; - struct ieee80211_sta *sta; - int i; - u32 msk = 0; - - if (!vif || vif->type != NL80211_IFTYPE_STATION) - return; - - mutex_lock(&mvm->mutex); - mvmvif = iwl_mvm_vif_from_mac80211(vif); - - /* flush the AP-station and all TDLS peers */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta)) - continue; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (mvmsta->vif != vif) - continue; - - /* make sure only TDLS peers or the AP are flushed */ - WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls); - - msk |= mvmsta->tfd_queue_msk; - } - - if (drop) { - if (iwl_mvm_flush_tx_path(mvm, msk, 0)) - IWL_ERR(mvm, "flush request fail\n"); - mutex_unlock(&mvm->mutex); - } else { - mutex_unlock(&mvm->mutex); - - /* this can take a while, and we may need/want other operations - * to succeed while doing this, so do it without the mutex held - */ - iwl_trans_wait_tx_queue_empty(mvm->trans, msk); - } -} - -static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx, - struct survey_info *survey) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - int ret; - - memset(survey, 0, sizeof(*survey)); - - /* only support global statistics right now */ - if (idx != 0) - return -ENOENT; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) - return -ENOENT; - - mutex_lock(&mvm->mutex); - - if (mvm->ucode_loaded) { - ret = iwl_mvm_request_statistics(mvm, false); - if (ret) - goto out; - } - - survey->filled = SURVEY_INFO_TIME | - SURVEY_INFO_TIME_RX | - SURVEY_INFO_TIME_TX | - SURVEY_INFO_TIME_SCAN; - survey->time = mvm->accu_radio_stats.on_time_rf + - mvm->radio_stats.on_time_rf; - do_div(survey->time, USEC_PER_MSEC); - - survey->time_rx = mvm->accu_radio_stats.rx_time + - mvm->radio_stats.rx_time; - do_div(survey->time_rx, USEC_PER_MSEC); - - survey->time_tx = mvm->accu_radio_stats.tx_time + - mvm->radio_stats.tx_time; - do_div(survey->time_tx, USEC_PER_MSEC); - - survey->time_scan = mvm->accu_radio_stats.on_time_scan + - mvm->radio_stats.on_time_scan; - do_div(survey->time_scan, USEC_PER_MSEC); - - ret = 0; - out: - mutex_unlock(&mvm->mutex); - return ret; -} - -static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct station_info *sinfo) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) - return; - - /* if beacon filtering isn't on mac80211 does it anyway */ - if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER)) - return; - - if (!vif->bss_conf.assoc) - return; - - mutex_lock(&mvm->mutex); - - if (mvmvif->ap_sta_id != mvmsta->sta_id) - goto unlock; - - if (iwl_mvm_request_statistics(mvm, false)) - goto unlock; - - sinfo->rx_beacon = mvmvif->beacon_stats.num_beacons + - mvmvif->beacon_stats.accu_num_beacons; - sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX); - if (mvmvif->beacon_stats.avg_signal) { - /* firmware only reports a value after RXing a few beacons */ - sinfo->rx_beacon_signal_avg = mvmvif->beacon_stats.avg_signal; - sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG); - } - unlock: - mutex_unlock(&mvm->mutex); -} - -static void iwl_mvm_event_mlme_callback(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - const struct ieee80211_event *event) -{ -#define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...) \ - do { \ - if ((_cnt) && --(_cnt)) \ - break; \ - iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\ - } while (0) - - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_mlme *trig_mlme; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME); - trig_mlme = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) - return; - - if (event->u.mlme.data == ASSOC_EVENT) { - if (event->u.mlme.status == MLME_DENIED) - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_assoc_denied, - "DENIED ASSOC: reason %d", - event->u.mlme.reason); - else if (event->u.mlme.status == MLME_TIMEOUT) - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_assoc_timeout, - "ASSOC TIMEOUT"); - } else if (event->u.mlme.data == AUTH_EVENT) { - if (event->u.mlme.status == MLME_DENIED) - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_auth_denied, - "DENIED AUTH: reason %d", - event->u.mlme.reason); - else if (event->u.mlme.status == MLME_TIMEOUT) - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_auth_timeout, - "AUTH TIMEOUT"); - } else if (event->u.mlme.data == DEAUTH_RX_EVENT) { - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_rx_deauth, - "DEAUTH RX %d", event->u.mlme.reason); - } else if (event->u.mlme.data == DEAUTH_TX_EVENT) { - CHECK_MLME_TRIGGER(mvm, trig, buf, - trig_mlme->stop_tx_deauth, - "DEAUTH TX %d", event->u.mlme.reason); - } -#undef CHECK_MLME_TRIGGER -} - -static void iwl_mvm_event_bar_rx_callback(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - const struct ieee80211_event *event) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_ba *ba_trig; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); - ba_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) - return; - - if (!(le16_to_cpu(ba_trig->rx_bar) & BIT(event->u.ba.tid))) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "BAR received from %pM, tid %d, ssn %d", - event->u.ba.sta->addr, event->u.ba.tid, - event->u.ba.ssn); -} - -static void -iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - const struct ieee80211_event *event) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_ba *ba_trig; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); - ba_trig = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) - return; - - if (!(le16_to_cpu(ba_trig->frame_timeout) & BIT(event->u.ba.tid))) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "Frame from %pM timed out, tid %d", - event->u.ba.sta->addr, event->u.ba.tid); -} - -static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const struct ieee80211_event *event) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - switch (event->type) { - case MLME_EVENT: - iwl_mvm_event_mlme_callback(mvm, vif, event); - break; - case BAR_RX_EVENT: - iwl_mvm_event_bar_rx_callback(mvm, vif, event); - break; - case BA_FRAME_TIMEOUT: - iwl_mvm_event_frame_timeout_callback(mvm, vif, event); - break; - default: - break; - } -} - -const struct ieee80211_ops iwl_mvm_hw_ops = { - .tx = iwl_mvm_mac_tx, - .ampdu_action = iwl_mvm_mac_ampdu_action, - .start = iwl_mvm_mac_start, - .reconfig_complete = iwl_mvm_mac_reconfig_complete, - .stop = iwl_mvm_mac_stop, - .add_interface = iwl_mvm_mac_add_interface, - .remove_interface = iwl_mvm_mac_remove_interface, - .config = iwl_mvm_mac_config, - .prepare_multicast = iwl_mvm_prepare_multicast, - .configure_filter = iwl_mvm_configure_filter, - .config_iface_filter = iwl_mvm_config_iface_filter, - .bss_info_changed = iwl_mvm_bss_info_changed, - .hw_scan = iwl_mvm_mac_hw_scan, - .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan, - .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove, - .sta_state = iwl_mvm_mac_sta_state, - .sta_notify = iwl_mvm_mac_sta_notify, - .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, - .release_buffered_frames = iwl_mvm_mac_release_buffered_frames, - .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, - .sta_rc_update = iwl_mvm_sta_rc_update, - .conf_tx = iwl_mvm_mac_conf_tx, - .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, - .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover, - .flush = iwl_mvm_mac_flush, - .sched_scan_start = iwl_mvm_mac_sched_scan_start, - .sched_scan_stop = iwl_mvm_mac_sched_scan_stop, - .set_key = iwl_mvm_mac_set_key, - .update_tkip_key = iwl_mvm_mac_update_tkip_key, - .remain_on_channel = iwl_mvm_roc, - .cancel_remain_on_channel = iwl_mvm_cancel_roc, - .add_chanctx = iwl_mvm_add_chanctx, - .remove_chanctx = iwl_mvm_remove_chanctx, - .change_chanctx = iwl_mvm_change_chanctx, - .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx, - .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx, - .switch_vif_chanctx = iwl_mvm_switch_vif_chanctx, - - .start_ap = iwl_mvm_start_ap_ibss, - .stop_ap = iwl_mvm_stop_ap_ibss, - .join_ibss = iwl_mvm_start_ap_ibss, - .leave_ibss = iwl_mvm_stop_ap_ibss, - - .set_tim = iwl_mvm_set_tim, - - .channel_switch = iwl_mvm_channel_switch, - .pre_channel_switch = iwl_mvm_pre_channel_switch, - .post_channel_switch = iwl_mvm_post_channel_switch, - - .tdls_channel_switch = iwl_mvm_tdls_channel_switch, - .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch, - .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch, - - .event_callback = iwl_mvm_mac_event_callback, - - CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) - -#ifdef CONFIG_PM_SLEEP - /* look at d3.c */ - .suspend = iwl_mvm_suspend, - .resume = iwl_mvm_resume, - .set_wakeup = iwl_mvm_set_wakeup, - .set_rekey_data = iwl_mvm_set_rekey_data, -#if IS_ENABLED(CONFIG_IPV6) - .ipv6_addr_change = iwl_mvm_ipv6_addr_change, -#endif - .set_default_unicast_key = iwl_mvm_set_default_unicast_key, -#endif - .get_survey = iwl_mvm_mac_get_survey, - .sta_statistics = iwl_mvm_mac_sta_statistics, -}; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h deleted file mode 100644 index 4bde2d027dcd..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ /dev/null @@ -1,1535 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __IWL_MVM_H__ -#define __IWL_MVM_H__ - -#include -#include -#include -#include - -#include "iwl-op-mode.h" -#include "iwl-trans.h" -#include "iwl-notif-wait.h" -#include "iwl-eeprom-parse.h" -#include "iwl-fw-file.h" -#include "iwl-config.h" -#include "sta.h" -#include "fw-api.h" -#include "constants.h" -#include "tof.h" - -#define IWL_MVM_MAX_ADDRESSES 5 -/* RSSI offset for WkP */ -#define IWL_RSSI_OFFSET 50 -#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8 -/* A TimeUnit is 1024 microsecond */ -#define MSEC_TO_TU(_msec) (_msec*1000/1024) - -/* For GO, this value represents the number of TUs before CSA "beacon - * 0" TBTT when the CSA time-event needs to be scheduled to start. It - * must be big enough to ensure that we switch in time. - */ -#define IWL_MVM_CHANNEL_SWITCH_TIME_GO 40 - -/* For client, this value represents the number of TUs before CSA - * "beacon 1" TBTT, instead. This is because we don't know when the - * GO/AP will be in the new channel, so we switch early enough. - */ -#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT 10 - -/* - * This value (in TUs) is used to fine tune the CSA NoA end time which should - * be just before "beacon 0" TBTT. - */ -#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4 - -/* - * Number of beacons to transmit on a new channel until we unblock tx to - * the stations, even if we didn't identify them on a new channel - */ -#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3 - -extern const struct ieee80211_ops iwl_mvm_hw_ops; - -/** - * struct iwl_mvm_mod_params - module parameters for iwlmvm - * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted. - * We will register to mac80211 to have testmode working. The NIC must not - * be up'ed after the INIT fw asserted. This is useful to be able to use - * proprietary tools over testmode to debug the INIT fw. - * @tfd_q_hang_detect: enabled the detection of hung transmit queues - * @power_scheme: one of enum iwl_power_scheme - */ -struct iwl_mvm_mod_params { - bool init_dbg; - bool tfd_q_hang_detect; - int power_scheme; -}; -extern struct iwl_mvm_mod_params iwlmvm_mod_params; - -/** - * struct iwl_mvm_dump_ptrs - set of pointers needed for the fw-error-dump - * - * @op_mode_ptr: pointer to the buffer coming from the mvm op_mode - * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the - * transport's data. - * @trans_len: length of the valid data in trans_ptr - * @op_mode_len: length of the valid data in op_mode_ptr - */ -struct iwl_mvm_dump_ptrs { - struct iwl_trans_dump_data *trans_ptr; - void *op_mode_ptr; - u32 op_mode_len; -}; - -/** - * struct iwl_mvm_dump_desc - describes the dump - * @len: length of trig_desc->data - * @trig_desc: the description of the dump - */ -struct iwl_mvm_dump_desc { - size_t len; - /* must be last */ - struct iwl_fw_error_dump_trigger_desc trig_desc; -}; - -extern struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert; - -struct iwl_mvm_phy_ctxt { - u16 id; - u16 color; - u32 ref; - - /* - * TODO: This should probably be removed. Currently here only for rate - * scaling algorithm - */ - struct ieee80211_channel *channel; -}; - -struct iwl_mvm_time_event_data { - struct ieee80211_vif *vif; - struct list_head list; - unsigned long end_jiffies; - u32 duration; - bool running; - u32 uid; - - /* - * The access to the 'id' field must be done when the - * mvm->time_event_lock is held, as it value is used to indicate - * if the te is in the time event list or not (when id == TE_MAX) - */ - u32 id; -}; - - /* Power management */ - -/** - * enum iwl_power_scheme - * @IWL_POWER_LEVEL_CAM - Continuously Active Mode - * @IWL_POWER_LEVEL_BPS - Balanced Power Save (default) - * @IWL_POWER_LEVEL_LP - Low Power - */ -enum iwl_power_scheme { - IWL_POWER_SCHEME_CAM = 1, - IWL_POWER_SCHEME_BPS, - IWL_POWER_SCHEME_LP -}; - -#define IWL_CONN_MAX_LISTEN_INTERVAL 10 -#define IWL_UAPSD_MAX_SP IEEE80211_WMM_IE_STA_QOSINFO_SP_2 - -#ifdef CONFIG_IWLWIFI_DEBUGFS -enum iwl_dbgfs_pm_mask { - MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0), - MVM_DEBUGFS_PM_SKIP_OVER_DTIM = BIT(1), - MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2), - MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3), - MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4), - MVM_DEBUGFS_PM_LPRX_ENA = BIT(6), - MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7), - MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8), - MVM_DEBUGFS_PM_UAPSD_MISBEHAVING = BIT(9), - MVM_DEBUGFS_PM_USE_PS_POLL = BIT(10), -}; - -struct iwl_dbgfs_pm { - u16 keep_alive_seconds; - u32 rx_data_timeout; - u32 tx_data_timeout; - bool skip_over_dtim; - u8 skip_dtim_periods; - bool lprx_ena; - u32 lprx_rssi_threshold; - bool snooze_ena; - bool uapsd_misbehaving; - bool use_ps_poll; - int mask; -}; - -/* beacon filtering */ - -enum iwl_dbgfs_bf_mask { - MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0), - MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1), - MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2), - MVM_DEBUGFS_BF_TEMP_THRESHOLD = BIT(3), - MVM_DEBUGFS_BF_TEMP_FAST_FILTER = BIT(4), - MVM_DEBUGFS_BF_TEMP_SLOW_FILTER = BIT(5), - MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(6), - MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(7), - MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(8), - MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(9), - MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(10), -}; - -struct iwl_dbgfs_bf { - u32 bf_energy_delta; - u32 bf_roaming_energy_delta; - u32 bf_roaming_state; - u32 bf_temp_threshold; - u32 bf_temp_fast_filter; - u32 bf_temp_slow_filter; - u32 bf_enable_beacon_filter; - u32 bf_debug_flag; - u32 bf_escape_timer; - u32 ba_escape_timer; - u32 ba_enable_beacon_abort; - int mask; -}; -#endif - -enum iwl_mvm_smps_type_request { - IWL_MVM_SMPS_REQ_BT_COEX, - IWL_MVM_SMPS_REQ_TT, - IWL_MVM_SMPS_REQ_PROT, - NUM_IWL_MVM_SMPS_REQ, -}; - -enum iwl_mvm_ref_type { - IWL_MVM_REF_UCODE_DOWN, - IWL_MVM_REF_SCAN, - IWL_MVM_REF_ROC, - IWL_MVM_REF_ROC_AUX, - IWL_MVM_REF_P2P_CLIENT, - IWL_MVM_REF_AP_IBSS, - IWL_MVM_REF_USER, - IWL_MVM_REF_TX, - IWL_MVM_REF_TX_AGG, - IWL_MVM_REF_ADD_IF, - IWL_MVM_REF_START_AP, - IWL_MVM_REF_BSS_CHANGED, - IWL_MVM_REF_PREPARE_TX, - IWL_MVM_REF_PROTECT_TDLS, - IWL_MVM_REF_CHECK_CTKILL, - IWL_MVM_REF_PRPH_READ, - IWL_MVM_REF_PRPH_WRITE, - IWL_MVM_REF_NMI, - IWL_MVM_REF_TM_CMD, - IWL_MVM_REF_EXIT_WORK, - IWL_MVM_REF_PROTECT_CSA, - IWL_MVM_REF_FW_DBG_COLLECT, - - /* update debugfs.c when changing this */ - - IWL_MVM_REF_COUNT, -}; - -enum iwl_bt_force_ant_mode { - BT_FORCE_ANT_DIS = 0, - BT_FORCE_ANT_AUTO, - BT_FORCE_ANT_BT, - BT_FORCE_ANT_WIFI, - - BT_FORCE_ANT_MAX, -}; - -/** -* struct iwl_mvm_vif_bf_data - beacon filtering related data -* @bf_enabled: indicates if beacon filtering is enabled -* @ba_enabled: indicated if beacon abort is enabled -* @ave_beacon_signal: average beacon signal -* @last_cqm_event: rssi of the last cqm event -* @bt_coex_min_thold: minimum threshold for BT coex -* @bt_coex_max_thold: maximum threshold for BT coex -* @last_bt_coex_event: rssi of the last BT coex event -*/ -struct iwl_mvm_vif_bf_data { - bool bf_enabled; - bool ba_enabled; - int ave_beacon_signal; - int last_cqm_event; - int bt_coex_min_thold; - int bt_coex_max_thold; - int last_bt_coex_event; -}; - -/** - * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context - * @id: between 0 and 3 - * @color: to solve races upon MAC addition and removal - * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA - * @bssid: BSSID for this (client) interface - * @associated: indicates that we're currently associated, used only for - * managing the firmware state in iwl_mvm_bss_info_changed_station() - * @ap_assoc_sta_count: count of stations associated to us - valid only - * if VIF type is AP - * @uploaded: indicates the MAC context has been added to the device - * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface - * should get quota etc. - * @pm_enabled - Indicate if MAC power management is allowed - * @monitor_active: indicates that monitor context is configured, and that the - * interface should get quota etc. - * @low_latency: indicates that this interface is in low-latency mode - * (VMACLowLatencyMode) - * @ps_disabled: indicates that this interface requires PS to be disabled - * @queue_params: QoS params for this MAC - * @bcast_sta: station used for broadcast packets. Used by the following - * vifs: P2P_DEVICE, GO and AP. - * @beacon_skb: the skb used to hold the AP/GO beacon template - * @smps_requests: the SMPS requests of different parts of the driver, - * combined on update to yield the overall request to mac80211. - * @beacon_stats: beacon statistics, containing the # of received beacons, - * # of received beacons accumulated over FW restart, and the current - * average signal of beacons retrieved from the firmware - * @csa_failed: CSA failed to schedule time event, report an error later - * @features: hw features active for this vif - */ -struct iwl_mvm_vif { - struct iwl_mvm *mvm; - u16 id; - u16 color; - u8 ap_sta_id; - - u8 bssid[ETH_ALEN]; - bool associated; - u8 ap_assoc_sta_count; - - bool uploaded; - bool ap_ibss_active; - bool pm_enabled; - bool monitor_active; - bool low_latency; - bool ps_disabled; - struct iwl_mvm_vif_bf_data bf_data; - - struct { - u32 num_beacons, accu_num_beacons; - u8 avg_signal; - } beacon_stats; - - u32 ap_beacon_time; - - enum iwl_tsf_id tsf_id; - - /* - * QoS data from mac80211, need to store this here - * as mac80211 has a separate callback but we need - * to have the data for the MAC context - */ - struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; - struct iwl_mvm_time_event_data time_event_data; - struct iwl_mvm_time_event_data hs_time_event_data; - - struct iwl_mvm_int_sta bcast_sta; - - /* - * Assigned while mac80211 has the interface in a channel context, - * or, for P2P Device, while it exists. - */ - struct iwl_mvm_phy_ctxt *phy_ctxt; - -#ifdef CONFIG_PM_SLEEP - /* WoWLAN GTK rekey data */ - struct { - u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; - __le64 replay_ctr; - bool valid; - } rekey_data; - - int tx_key_idx; - - bool seqno_valid; - u16 seqno; -#endif - -#if IS_ENABLED(CONFIG_IPV6) - /* IPv6 addresses for WoWLAN */ - struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; - int num_target_ipv6_addrs; -#endif - -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct dentry *dbgfs_dir; - struct dentry *dbgfs_slink; - struct iwl_dbgfs_pm dbgfs_pm; - struct iwl_dbgfs_bf dbgfs_bf; - struct iwl_mac_power_cmd mac_pwr_cmd; -#endif - - enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; - - /* FW identified misbehaving AP */ - u8 uapsd_misbehaving_bssid[ETH_ALEN]; - - /* Indicates that CSA countdown may be started */ - bool csa_countdown; - bool csa_failed; - - /* TCP Checksum Offload */ - netdev_features_t features; -}; - -static inline struct iwl_mvm_vif * -iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif) -{ - return (void *)vif->drv_priv; -} - -extern const u8 tid_to_mac80211_ac[]; - -#define IWL_MVM_SCAN_STOPPING_SHIFT 8 - -enum iwl_scan_status { - IWL_MVM_SCAN_REGULAR = BIT(0), - IWL_MVM_SCAN_SCHED = BIT(1), - IWL_MVM_SCAN_NETDETECT = BIT(2), - - IWL_MVM_SCAN_STOPPING_REGULAR = BIT(8), - IWL_MVM_SCAN_STOPPING_SCHED = BIT(9), - IWL_MVM_SCAN_STOPPING_NETDETECT = BIT(10), - - IWL_MVM_SCAN_REGULAR_MASK = IWL_MVM_SCAN_REGULAR | - IWL_MVM_SCAN_STOPPING_REGULAR, - IWL_MVM_SCAN_SCHED_MASK = IWL_MVM_SCAN_SCHED | - IWL_MVM_SCAN_STOPPING_SCHED, - IWL_MVM_SCAN_NETDETECT_MASK = IWL_MVM_SCAN_NETDETECT | - IWL_MVM_SCAN_STOPPING_NETDETECT, - - IWL_MVM_SCAN_STOPPING_MASK = 0xff << IWL_MVM_SCAN_STOPPING_SHIFT, - IWL_MVM_SCAN_MASK = 0xff, -}; - -/** - * struct iwl_nvm_section - describes an NVM section in memory. - * - * This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD, - * and saved for later use by the driver. Not all NVM sections are saved - * this way, only the needed ones. - */ -struct iwl_nvm_section { - u16 length; - const u8 *data; -}; - -/** - * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure - * @ct_kill_exit: worker to exit thermal kill - * @dynamic_smps: Is thermal throttling enabled dynamic_smps? - * @tx_backoff: The current thremal throttling tx backoff in uSec. - * @min_backoff: The minimal tx backoff due to power restrictions - * @params: Parameters to configure the thermal throttling algorithm. - * @throttle: Is thermal throttling is active? - */ -struct iwl_mvm_tt_mgmt { - struct delayed_work ct_kill_exit; - bool dynamic_smps; - u32 tx_backoff; - u32 min_backoff; - struct iwl_tt_params params; - bool throttle; -}; - -#define IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES 8 - -struct iwl_mvm_frame_stats { - u32 legacy_frames; - u32 ht_frames; - u32 vht_frames; - u32 bw_20_frames; - u32 bw_40_frames; - u32 bw_80_frames; - u32 bw_160_frames; - u32 sgi_frames; - u32 ngi_frames; - u32 siso_frames; - u32 mimo2_frames; - u32 agg_frames; - u32 ampdu_count; - u32 success_frames; - u32 fail_frames; - u32 last_rates[IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES]; - int last_frame_idx; -}; - -enum { - D0I3_DEFER_WAKEUP, - D0I3_PENDING_WAKEUP, -}; - -#define IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE 0xff -#define IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -100 -#define IWL_MVM_DEBUG_SET_TEMPERATURE_MAX 200 - -enum iwl_mvm_tdls_cs_state { - IWL_MVM_TDLS_SW_IDLE = 0, - IWL_MVM_TDLS_SW_REQ_SENT, - IWL_MVM_TDLS_SW_RESP_RCVD, - IWL_MVM_TDLS_SW_REQ_RCVD, - IWL_MVM_TDLS_SW_ACTIVE, -}; - -struct iwl_mvm_shared_mem_cfg { - u32 shared_mem_addr; - u32 shared_mem_size; - u32 sample_buff_addr; - u32 sample_buff_size; - u32 txfifo_addr; - u32 txfifo_size[TX_FIFO_MAX_NUM]; - u32 rxfifo_size[RX_FIFO_MAX_NUM]; - u32 page_buff_addr; - u32 page_buff_size; -}; - -struct iwl_mvm { - /* for logger access */ - struct device *dev; - - struct iwl_trans *trans; - const struct iwl_fw *fw; - const struct iwl_cfg *cfg; - struct iwl_phy_db *phy_db; - struct ieee80211_hw *hw; - - /* for protecting access to iwl_mvm */ - struct mutex mutex; - struct list_head async_handlers_list; - spinlock_t async_handlers_lock; - struct work_struct async_handlers_wk; - - struct work_struct roc_done_wk; - - unsigned long status; - - /* - * for beacon filtering - - * currently only one interface can be supported - */ - struct iwl_mvm_vif *bf_allowed_vif; - - enum iwl_ucode_type cur_ucode; - bool ucode_loaded; - bool calibrating; - u32 error_event_table; - u32 log_event_table; - u32 umac_error_event_table; - bool support_umac_log; - struct iwl_sf_region sf_space; - - u32 ampdu_ref; - - struct iwl_notif_wait_data notif_wait; - - struct mvm_statistics_rx rx_stats; - - struct { - u64 rx_time; - u64 tx_time; - u64 on_time_rf; - u64 on_time_scan; - } radio_stats, accu_radio_stats; - - struct { - /* Map to HW queue */ - u32 hw_queue_to_mac80211; - u8 hw_queue_refcount; - bool setup_reserved; - u16 tid_bitmap; /* Bitmap of the TIDs mapped to this queue */ - } queue_info[IWL_MAX_HW_QUEUES]; - spinlock_t queue_info_lock; /* For syncing queue mgmt operations */ - atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES]; - - const char *nvm_file_name; - struct iwl_nvm_data *nvm_data; - /* NVM sections */ - struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS]; - - /* Paging section */ - struct iwl_fw_paging fw_paging_db[NUM_OF_FW_PAGING_BLOCKS]; - u16 num_of_paging_blk; - u16 num_of_pages_in_last_blk; - - /* EEPROM MAC addresses */ - struct mac_address addresses[IWL_MVM_MAX_ADDRESSES]; - - /* data related to data path */ - struct iwl_rx_phy_info last_phy_info; - struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT]; - struct work_struct sta_drained_wk; - unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)]; - atomic_t pending_frames[IWL_MVM_STATION_COUNT]; - u32 tfd_drained[IWL_MVM_STATION_COUNT]; - u8 rx_ba_sessions; - - /* configured by mac80211 */ - u32 rts_threshold; - - /* Scan status, cmd (pre-allocated) and auxiliary station */ - unsigned int scan_status; - void *scan_cmd; - struct iwl_mcast_filter_cmd *mcast_filter_cmd; - - /* max number of simultaneous scans the FW supports */ - unsigned int max_scans; - - /* UMAC scan tracking */ - u32 scan_uid_status[IWL_MVM_MAX_UMAC_SCANS]; - - /* rx chain antennas set through debugfs for the scan command */ - u8 scan_rx_ant; - -#ifdef CONFIG_IWLWIFI_BCAST_FILTERING - /* broadcast filters to configure for each associated station */ - const struct iwl_fw_bcast_filter *bcast_filters; -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct { - bool override; - struct iwl_bcast_filter_cmd cmd; - } dbgfs_bcast_filtering; -#endif -#endif - - /* Internal station */ - struct iwl_mvm_int_sta aux_sta; - - bool last_ebs_successful; - - u8 scan_last_antenna_idx; /* to toggle TX between antennas */ - u8 mgmt_last_antenna_idx; - - /* last smart fifo state that was successfully sent to firmware */ - enum iwl_sf_state sf_state; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct dentry *debugfs_dir; - u32 dbgfs_sram_offset, dbgfs_sram_len; - u32 dbgfs_prph_reg_addr; - bool disable_power_off; - bool disable_power_off_d3; - - bool scan_iter_notif_enabled; - - struct debugfs_blob_wrapper nvm_hw_blob; - struct debugfs_blob_wrapper nvm_sw_blob; - struct debugfs_blob_wrapper nvm_calib_blob; - struct debugfs_blob_wrapper nvm_prod_blob; - struct debugfs_blob_wrapper nvm_phy_sku_blob; - - struct iwl_mvm_frame_stats drv_rx_stats; - spinlock_t drv_stats_lock; - u16 dbgfs_rx_phyinfo; -#endif - - struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX]; - - struct list_head time_event_list; - spinlock_t time_event_lock; - - /* - * A bitmap indicating the index of the key in use. The firmware - * can hold 16 keys at most. Reflect this fact. - */ - unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)]; - u8 fw_key_deleted[STA_KEY_MAX_NUM]; - - /* references taken by the driver and spinlock protecting them */ - spinlock_t refs_lock; - u8 refs[IWL_MVM_REF_COUNT]; - - u8 vif_count; - - /* -1 for always, 0 for never, >0 for that many times */ - s8 restart_fw; - u8 fw_dbg_conf; - struct delayed_work fw_dump_wk; - struct iwl_mvm_dump_desc *fw_dump_desc; - struct iwl_fw_dbg_trigger_tlv *fw_dump_trig; - -#ifdef CONFIG_IWLWIFI_LEDS - struct led_classdev led; -#endif - - struct ieee80211_vif *p2p_device_vif; - -#ifdef CONFIG_PM_SLEEP - struct wiphy_wowlan_support wowlan; - int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; - - /* sched scan settings for net detect */ - struct cfg80211_sched_scan_request *nd_config; - struct ieee80211_scan_ies nd_ies; - struct cfg80211_match_set *nd_match_sets; - int n_nd_match_sets; - struct ieee80211_channel **nd_channels; - int n_nd_channels; - bool net_detect; -#ifdef CONFIG_IWLWIFI_DEBUGFS - bool d3_wake_sysassert; - bool d3_test_active; - bool store_d3_resume_sram; - void *d3_resume_sram; - u32 d3_test_pme_ptr; - struct ieee80211_vif *keep_vif; - u32 last_netdetect_scans; /* no. of scans in the last net-detect wake */ -#endif -#endif - - /* d0i3 */ - u8 d0i3_ap_sta_id; - bool d0i3_offloading; - struct work_struct d0i3_exit_work; - struct sk_buff_head d0i3_tx; - /* protect d0i3_suspend_flags */ - struct mutex d0i3_suspend_mutex; - unsigned long d0i3_suspend_flags; - /* sync d0i3_tx queue and IWL_MVM_STATUS_IN_D0I3 status flag */ - spinlock_t d0i3_tx_lock; - wait_queue_head_t d0i3_exit_waitq; - - /* BT-Coex */ - u8 bt_ack_kill_msk[NUM_PHY_CTX]; - u8 bt_cts_kill_msk[NUM_PHY_CTX]; - - struct iwl_bt_coex_profile_notif_old last_bt_notif_old; - struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old; - struct iwl_bt_coex_profile_notif last_bt_notif; - struct iwl_bt_coex_ci_cmd last_bt_ci_cmd; - - u32 last_ant_isol; - u8 last_corun_lut; - u8 bt_tx_prio; - enum iwl_bt_force_ant_mode bt_force_ant_mode; - - /* Aux ROC */ - struct list_head aux_roc_te_list; - - /* Thermal Throttling and CTkill */ - struct iwl_mvm_tt_mgmt thermal_throttle; - s32 temperature; /* Celsius */ - /* - * Debug option to set the NIC temperature. This option makes the - * driver think this is the actual NIC temperature, and ignore the - * real temperature that is received from the fw - */ - bool temperature_test; /* Debug test temperature is enabled */ - - struct iwl_time_quota_cmd last_quota_cmd; - -#ifdef CONFIG_NL80211_TESTMODE - u32 noa_duration; - struct ieee80211_vif *noa_vif; -#endif - - /* Tx queues */ - u8 aux_queue; - u8 first_agg_queue; - u8 last_agg_queue; - - /* Indicate if device power save is allowed */ - u8 ps_disabled; /* u8 instead of bool to ease debugfs_create_* usage */ - - struct ieee80211_vif __rcu *csa_vif; - struct ieee80211_vif __rcu *csa_tx_blocked_vif; - u8 csa_tx_block_bcn_timeout; - - /* system time of last beacon (for AP/GO interface) */ - u32 ap_last_beacon_gp2; - - bool lar_regdom_set; - enum iwl_mcc_source mcc_src; - - u8 low_latency_agg_frame_limit; - - /* TDLS channel switch data */ - struct { - struct delayed_work dwork; - enum iwl_mvm_tdls_cs_state state; - - /* - * Current cs sta - might be different from periodic cs peer - * station. Value is meaningless when the cs-state is idle. - */ - u8 cur_sta_id; - - /* TDLS periodic channel-switch peer */ - struct { - u8 sta_id; - u8 op_class; - bool initiator; /* are we the link initiator */ - struct cfg80211_chan_def chandef; - struct sk_buff *skb; /* ch sw template */ - u32 ch_sw_tm_ie; - - /* timestamp of last ch-sw request sent (GP2 time) */ - u32 sent_timestamp; - } peer; - } tdls_cs; - - struct iwl_mvm_shared_mem_cfg shared_mem_cfg; - - u32 ciphers[6]; - struct iwl_mvm_tof_data tof_data; -}; - -/* Extract MVM priv from op_mode and _hw */ -#define IWL_OP_MODE_GET_MVM(_iwl_op_mode) \ - ((struct iwl_mvm *)(_iwl_op_mode)->op_mode_specific) - -#define IWL_MAC80211_GET_MVM(_hw) \ - IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv)) - -enum iwl_mvm_status { - IWL_MVM_STATUS_HW_RFKILL, - IWL_MVM_STATUS_HW_CTKILL, - IWL_MVM_STATUS_ROC_RUNNING, - IWL_MVM_STATUS_IN_HW_RESTART, - IWL_MVM_STATUS_IN_D0I3, - IWL_MVM_STATUS_ROC_AUX_RUNNING, - IWL_MVM_STATUS_D3_RECONFIG, - IWL_MVM_STATUS_DUMPING_FW_LOG, -}; - -static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) -{ - return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status) || - test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); -} - -static inline bool iwl_mvm_is_radio_hw_killed(struct iwl_mvm *mvm) -{ - return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); -} - -/* Must be called with rcu_read_lock() held and it can only be - * released when mvmsta is not needed anymore. - */ -static inline struct iwl_mvm_sta * -iwl_mvm_sta_from_staid_rcu(struct iwl_mvm *mvm, u8 sta_id) -{ - struct ieee80211_sta *sta; - - if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id)) - return NULL; - - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - - /* This can happen if the station has been removed right now */ - if (IS_ERR_OR_NULL(sta)) - return NULL; - - return iwl_mvm_sta_from_mac80211(sta); -} - -static inline struct iwl_mvm_sta * -iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id) -{ - struct ieee80211_sta *sta; - - if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id)) - return NULL; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - - /* This can happen if the station has been removed right now */ - if (IS_ERR_OR_NULL(sta)) - return NULL; - - return iwl_mvm_sta_from_mac80211(sta); -} - -static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) -{ - return mvm->trans->cfg->d0i3 && - mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF && - !iwlwifi_mod_params.d0i3_disable && - fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); -} - -static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm) -{ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_DQA_SUPPORT); -} - -static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm) -{ - bool nvm_lar = mvm->nvm_data->lar_enabled; - bool tlv_lar = fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_LAR_SUPPORT); - - if (iwlwifi_mod_params.lar_disable) - return false; - - /* - * Enable LAR only if it is supported by the FW (TLV) && - * enabled in the NVM - */ - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) - return nvm_lar && tlv_lar; - else - return tlv_lar; -} - -static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm) -{ - return fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_WIFI_MCC_UPDATE) || - fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC); -} - -static inline bool iwl_mvm_bt_is_plcr_supported(struct iwl_mvm *mvm) -{ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_BT_COEX_PLCR) && - IWL_MVM_BT_COEX_CORUNNING; -} - -static inline bool iwl_mvm_bt_is_rrc_supported(struct iwl_mvm *mvm) -{ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_BT_COEX_RRC) && - IWL_MVM_BT_COEX_RRC; -} - -static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm) -{ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_CSUM_SUPPORT); -} - -static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm) -{ - /* firmware flag isn't defined yet */ - return false; -} - -extern const u8 iwl_mvm_ac_to_tx_fifo[]; - -struct iwl_rate_info { - u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ - u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ - u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ - u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */ - u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ -}; - -void __iwl_mvm_mac_stop(struct iwl_mvm *mvm); -int __iwl_mvm_mac_start(struct iwl_mvm *mvm); - -/****************** - * MVM Methods - ******************/ -/* uCode */ -int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm); - -/* Utils */ -int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, - enum ieee80211_band band); -void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, - enum ieee80211_band band, - struct ieee80211_tx_rate *r); -u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx); -void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm); -u8 first_antenna(u8 mask); -u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx); - -/* Tx / Host Commands */ -int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm, - struct iwl_host_cmd *cmd); -int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, - u32 flags, u16 len, const void *data); -int __must_check iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, - struct iwl_host_cmd *cmd, - u32 *status); -int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, - u16 len, const void *data, - u32 *status); -int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, - struct ieee80211_sta *sta); -int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb); -void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, - struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, u8 sta_id); -void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, __le16 fc); -#ifdef CONFIG_IWLWIFI_DEBUG -const char *iwl_mvm_get_tx_fail_reason(u32 status); -#else -static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; } -#endif -int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags); -void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm); - -static inline void iwl_mvm_set_tx_cmd_ccmp(struct ieee80211_tx_info *info, - struct iwl_tx_cmd *tx_cmd) -{ - struct ieee80211_key_conf *keyconf = info->control.hw_key; - - tx_cmd->sec_ctl = TX_CMD_SEC_CCM; - memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); - if (info->flags & IEEE80211_TX_CTL_AMPDU) - tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_CCMP_AGG); -} - -static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm) -{ - flush_work(&mvm->async_handlers_wk); -} - -/* Statistics */ -void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt); -void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear); -void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm); - -/* NVM */ -int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic); -int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); - -static inline u8 iwl_mvm_get_valid_tx_ant(struct iwl_mvm *mvm) -{ - return mvm->nvm_data && mvm->nvm_data->valid_tx_ant ? - mvm->fw->valid_tx_ant & mvm->nvm_data->valid_tx_ant : - mvm->fw->valid_tx_ant; -} - -static inline u8 iwl_mvm_get_valid_rx_ant(struct iwl_mvm *mvm) -{ - return mvm->nvm_data && mvm->nvm_data->valid_rx_ant ? - mvm->fw->valid_rx_ant & mvm->nvm_data->valid_rx_ant : - mvm->fw->valid_rx_ant; -} - -static inline u32 iwl_mvm_get_phy_config(struct iwl_mvm *mvm) -{ - u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN | - FW_PHY_CFG_RX_CHAIN); - u32 valid_rx_ant = iwl_mvm_get_valid_rx_ant(mvm); - u32 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); - - phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS | - valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS; - - return mvm->fw->phy_config & phy_config; -} - -int iwl_mvm_up(struct iwl_mvm *mvm); -int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm); - -int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm); -bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, - struct iwl_bcast_filter_cmd *cmd); - -/* - * FW notifications / CMD responses handlers - * Convention: iwl_mvm_rx_ - */ -void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_shared_mem_cfg_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/* MVM PHY */ -int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic); -int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic); -void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, - struct iwl_mvm_phy_ctxt *ctxt); -void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, - struct iwl_mvm_phy_ctxt *ctxt); -int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm); -u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef); -u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef); - -/* MAC (virtual interface) programming */ -int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool force_assoc_off, const u8 *bssid_override); -int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif); -int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, - struct ieee80211_vif *exclude_vif); -/* Bindings */ -int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); - -/* Quota management */ -int iwl_mvm_update_quotas(struct iwl_mvm *mvm, bool force_upload, - struct ieee80211_vif *disabled_vif); - -/* Scanning */ -int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req, - struct ieee80211_scan_ies *ies); -int iwl_mvm_scan_size(struct iwl_mvm *mvm); -int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify); -int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm); -void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm); - -/* Scheduled scan */ -void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct cfg80211_sched_scan_request *req, - struct ieee80211_scan_ies *ies, - int type); -void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/* UMAC scan */ -int iwl_mvm_config_scan(struct iwl_mvm *mvm); -void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/* MVM debugfs */ -#ifdef CONFIG_IWLWIFI_DEBUGFS -int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir); -void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -#else -static inline int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, - struct dentry *dbgfs_dir) -{ - return 0; -} -static inline void -iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ -} -static inline void -iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ -} -#endif /* CONFIG_IWLWIFI_DEBUGFS */ - -/* rate scaling */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init); -void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg); -int rs_pretty_print_rate(char *buf, const u32 rate); -void rs_update_last_rssi(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_rx_status *rx_status); - -/* power management */ -int iwl_mvm_power_update_device(struct iwl_mvm *mvm); -int iwl_mvm_power_update_mac(struct iwl_mvm *mvm); -int iwl_mvm_power_update_ps(struct iwl_mvm *mvm); -int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - char *buf, int bufsz); - -void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -#ifdef CONFIG_IWLWIFI_LEDS -int iwl_mvm_leds_init(struct iwl_mvm *mvm); -void iwl_mvm_leds_exit(struct iwl_mvm *mvm); -#else -static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm) -{ - return 0; -} -static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm) -{ -} -#endif - -/* D3 (WoWLAN, NetDetect) */ -int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); -int iwl_mvm_resume(struct ieee80211_hw *hw); -void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled); -void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_gtk_rekey_data *data); -void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct inet6_dev *idev); -void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, int idx); -extern const struct file_operations iwl_dbgfs_d3_test_ops; -#ifdef CONFIG_PM_SLEEP -void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -#else -static inline void -iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ -} -#endif -void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, - struct iwl_wowlan_config_cmd *cmd); -int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool disable_offloading, - u32 cmd_flags); - -/* D0i3 */ -void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); -void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); -int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); -bool iwl_mvm_ref_taken(struct iwl_mvm *mvm); -void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq); -int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode); -int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode); -int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm); - -/* BT Coex */ -int iwl_send_bt_init_conf(struct iwl_mvm *mvm); -void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event_data); -void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm); -u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, - struct ieee80211_sta *sta); -bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, - struct ieee80211_sta *sta); -bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant); -bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm); -bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, - enum ieee80211_band band); -u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, - struct ieee80211_tx_info *info, u8 ac); - -bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm); -void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm); -int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm); -void iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event_data); -u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm, - struct ieee80211_sta *sta); -bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm, - struct ieee80211_sta *sta); -bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm, - enum ieee80211_band band); -void iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/* beacon filtering */ -#ifdef CONFIG_IWLWIFI_DEBUGFS -void -iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, - struct iwl_beacon_filter_cmd *cmd); -#else -static inline void -iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, - struct iwl_beacon_filter_cmd *cmd) -{} -#endif -int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool enable, u32 flags); -int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags); -int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags); -/* SMPS */ -void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum iwl_mvm_smps_type_request req_type, - enum ieee80211_smps_mode smps_request); -bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm); - -/* Low latency */ -int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool value); -/* get SystemLowLatencyMode - only needed for beacon threshold? */ -bool iwl_mvm_low_latency(struct iwl_mvm *mvm); -/* get VMACLowLatencyMode */ -static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif) -{ - /* - * should this consider associated/active/... state? - * - * Normally low-latency should only be active on interfaces - * that are active, but at least with debugfs it can also be - * enabled on interfaces that aren't active. However, when - * interface aren't active then they aren't added into the - * binding, so this has no real impact. For now, just return - * the current desired low-latency state. - */ - - return mvmvif->low_latency; -} - -/* hw scheduler queue config */ -void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg, - unsigned int wdg_timeout); -/* - * Disable a TXQ. - * Note that in non-DQA mode the %mac80211_queue and %tid params are ignored. - */ -void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u8 tid, u8 flags); -int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq); - -static inline -void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u8 fifo, u16 ssn, unsigned int wdg_timeout) -{ - struct iwl_trans_txq_scd_cfg cfg = { - .fifo = fifo, - .tid = IWL_MAX_TID_COUNT, - .aggregate = false, - .frame_limit = IWL_FRAME_LIMIT, - }; - - iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout); -} - -static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue, - int mac80211_queue, int fifo, - int sta_id, int tid, int frame_limit, - u16 ssn, unsigned int wdg_timeout) -{ - struct iwl_trans_txq_scd_cfg cfg = { - .fifo = fifo, - .sta_id = sta_id, - .tid = tid, - .frame_limit = frame_limit, - .aggregate = true, - }; - - iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout); -} - -/* Thermal management and CT-kill */ -void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff); -void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp); -void iwl_mvm_temp_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_tt_handler(struct iwl_mvm *mvm); -void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff); -void iwl_mvm_tt_exit(struct iwl_mvm *mvm); -void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); -int iwl_mvm_get_temp(struct iwl_mvm *mvm); - -/* Location Aware Regulatory */ -struct iwl_mcc_update_resp * -iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, - enum iwl_mcc_source src_id); -int iwl_mvm_init_mcc(struct iwl_mvm *mvm); -void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, - const char *alpha2, - enum iwl_mcc_source src_id, - bool *changed); -struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm, - bool *changed); -int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm); -void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm); - -/* smart fifo */ -int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool added_vif); - -/* TDLS */ - -/* - * We use TID 4 (VI) as a FW-used-only TID when TDLS connections are present. - * This TID is marked as used vs the AP and all connected TDLS peers. - */ -#define IWL_MVM_TDLS_FW_TID 4 - -int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm); -void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool sta_added); -void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); -int iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u8 oper_class, - struct cfg80211_chan_def *chandef, - struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie); -void iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_tdls_ch_sw_params *params); -void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_tdls_ch_switch_work(struct work_struct *work); - -struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); - -void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); -void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); - -int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id); -int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, - const char *str, size_t len, - struct iwl_fw_dbg_trigger_tlv *trigger); -int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - struct iwl_mvm_dump_desc *desc, - struct iwl_fw_dbg_trigger_tlv *trigger); -void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm); -int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trigger, - const char *fmt, ...) __printf(3, 4); -unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool tdls, bool cmd_q); -void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - const char *errmsg); -static inline bool -iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig, - struct ieee80211_vif *vif) -{ - u32 trig_vif = le32_to_cpu(trig->vif_type); - - return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif; -} - -static inline bool -iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trig) -{ - return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) && - (mvm->fw_dbg_conf == FW_DBG_INVALID || - (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids)))); -} - -static inline bool -iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_fw_dbg_trigger_tlv *trig) -{ - if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif)) - return false; - - return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig); -} - -static inline void -iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - enum iwl_fw_dbg_trigger trig) -{ - struct iwl_fw_dbg_trigger_tlv *trigger; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, trig)) - return; - - trigger = iwl_fw_dbg_get_trigger(mvm->fw, trig); - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); -} - -#endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c deleted file mode 100644 index 2ee0f6fe56a1..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ /dev/null @@ -1,864 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include -#include -#include -#include -#include "iwl-trans.h" -#include "iwl-csr.h" -#include "mvm.h" -#include "iwl-eeprom-parse.h" -#include "iwl-eeprom-read.h" -#include "iwl-nvm-parse.h" -#include "iwl-prph.h" - -/* Default NVM size to read */ -#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) -#define IWL_MAX_NVM_SECTION_SIZE 0x1b58 -#define IWL_MAX_NVM_8000_SECTION_SIZE 0x1ffc - -#define NVM_WRITE_OPCODE 1 -#define NVM_READ_OPCODE 0 - -/* load nvm chunk response */ -enum { - READ_NVM_CHUNK_SUCCEED = 0, - READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1 -}; - -/* - * prepare the NVM host command w/ the pointers to the nvm buffer - * and send it to fw - */ -static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, - u16 offset, u16 length, const u8 *data) -{ - struct iwl_nvm_access_cmd nvm_access_cmd = { - .offset = cpu_to_le16(offset), - .length = cpu_to_le16(length), - .type = cpu_to_le16(section), - .op_code = NVM_WRITE_OPCODE, - }; - struct iwl_host_cmd cmd = { - .id = NVM_ACCESS_CMD, - .len = { sizeof(struct iwl_nvm_access_cmd), length }, - .flags = CMD_SEND_IN_RFKILL, - .data = { &nvm_access_cmd, data }, - /* data may come from vmalloc, so use _DUP */ - .dataflags = { 0, IWL_HCMD_DFL_DUP }, - }; - - return iwl_mvm_send_cmd(mvm, &cmd); -} - -static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, - u16 offset, u16 length, u8 *data) -{ - struct iwl_nvm_access_cmd nvm_access_cmd = { - .offset = cpu_to_le16(offset), - .length = cpu_to_le16(length), - .type = cpu_to_le16(section), - .op_code = NVM_READ_OPCODE, - }; - struct iwl_nvm_access_resp *nvm_resp; - struct iwl_rx_packet *pkt; - struct iwl_host_cmd cmd = { - .id = NVM_ACCESS_CMD, - .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, - .data = { &nvm_access_cmd, }, - }; - int ret, bytes_read, offset_read; - u8 *resp_data; - - cmd.len[0] = sizeof(struct iwl_nvm_access_cmd); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) - return ret; - - pkt = cmd.resp_pkt; - - /* Extract NVM response */ - nvm_resp = (void *)pkt->data; - ret = le16_to_cpu(nvm_resp->status); - bytes_read = le16_to_cpu(nvm_resp->length); - offset_read = le16_to_cpu(nvm_resp->offset); - resp_data = nvm_resp->data; - if (ret) { - if ((offset != 0) && - (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) { - /* - * meaning of NOT_VALID_ADDRESS: - * driver try to read chunk from address that is - * multiple of 2K and got an error since addr is empty. - * meaning of (offset != 0): driver already - * read valid data from another chunk so this case - * is not an error. - */ - IWL_DEBUG_EEPROM(mvm->trans->dev, - "NVM access command failed on offset 0x%x since that section size is multiple 2K\n", - offset); - ret = 0; - } else { - IWL_DEBUG_EEPROM(mvm->trans->dev, - "NVM access command failed with status %d (device: %s)\n", - ret, mvm->cfg->name); - ret = -EIO; - } - goto exit; - } - - if (offset_read != offset) { - IWL_ERR(mvm, "NVM ACCESS response with invalid offset %d\n", - offset_read); - ret = -EINVAL; - goto exit; - } - - /* Write data to NVM */ - memcpy(data + offset, resp_data, bytes_read); - ret = bytes_read; - -exit: - iwl_free_resp(&cmd); - return ret; -} - -static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section, - const u8 *data, u16 length) -{ - int offset = 0; - - /* copy data in chunks of 2k (and remainder if any) */ - - while (offset < length) { - int chunk_size, ret; - - chunk_size = min(IWL_NVM_DEFAULT_CHUNK_SIZE, - length - offset); - - ret = iwl_nvm_write_chunk(mvm, section, offset, - chunk_size, data + offset); - if (ret < 0) - return ret; - - offset += chunk_size; - } - - return 0; -} - -/* - * Reads an NVM section completely. - * NICs prior to 7000 family doesn't have a real NVM, but just read - * section 0 which is the EEPROM. Because the EEPROM reading is unlimited - * by uCode, we need to manually check in this case that we don't - * overflow and try to read more than the EEPROM size. - * For 7000 family NICs, we supply the maximal size we can read, and - * the uCode fills the response with as much data as we can, - * without overflowing, so no check is needed. - */ -static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, - u8 *data, u32 size_read) -{ - u16 length, offset = 0; - int ret; - - /* Set nvm section read length */ - length = IWL_NVM_DEFAULT_CHUNK_SIZE; - - ret = length; - - /* Read the NVM until exhausted (reading less than requested) */ - while (ret == length) { - /* Check no memory assumptions fail and cause an overflow */ - if ((size_read + offset + length) > - mvm->cfg->base_params->eeprom_size) { - IWL_ERR(mvm, "EEPROM size is too small for NVM\n"); - return -ENOBUFS; - } - - ret = iwl_nvm_read_chunk(mvm, section, offset, length, data); - if (ret < 0) { - IWL_DEBUG_EEPROM(mvm->trans->dev, - "Cannot read NVM from section %d offset %d, length %d\n", - section, offset, length); - return ret; - } - offset += ret; - } - - IWL_DEBUG_EEPROM(mvm->trans->dev, - "NVM section %d read completed\n", section); - return offset; -} - -static struct iwl_nvm_data * -iwl_parse_nvm_sections(struct iwl_mvm *mvm) -{ - struct iwl_nvm_section *sections = mvm->nvm_sections; - const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; - bool lar_enabled; - u32 mac_addr0, mac_addr1; - - /* Checking for required sections */ - if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { - if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || - !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) { - IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n"); - return NULL; - } - } else { - /* SW and REGULATORY sections are mandatory */ - if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || - !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) { - IWL_ERR(mvm, - "Can't parse empty family 8000 OTP/NVM sections\n"); - return NULL; - } - /* MAC_OVERRIDE or at least HW section must exist */ - if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data && - !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) { - IWL_ERR(mvm, - "Can't parse mac_address, empty sections\n"); - return NULL; - } - - /* PHY_SKU section is mandatory in B0 */ - if (!mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) { - IWL_ERR(mvm, - "Can't parse phy_sku in B0, empty sections\n"); - return NULL; - } - } - - if (WARN_ON(!mvm->cfg)) - return NULL; - - /* read the mac address from WFMP registers */ - mac_addr0 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_0); - mac_addr1 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_1); - - hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; - sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; - calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; - regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data; - mac_override = - (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data; - phy_sku = (const __le16 *)sections[NVM_SECTION_TYPE_PHY_SKU].data; - - lar_enabled = !iwlwifi_mod_params.lar_disable && - fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_LAR_SUPPORT); - - return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, - regulatory, mac_override, phy_sku, - mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant, - lar_enabled, mac_addr0, mac_addr1, - mvm->trans->hw_id); -} - -#define MAX_NVM_FILE_LEN 16384 - -/* - * Reads external NVM from a file into mvm->nvm_sections - * - * HOW TO CREATE THE NVM FILE FORMAT: - * ------------------------------ - * 1. create hex file, format: - * 3800 -> header - * 0000 -> header - * 5a40 -> data - * - * rev - 6 bit (word1) - * len - 10 bit (word1) - * id - 4 bit (word2) - * rsv - 12 bit (word2) - * - * 2. flip 8bits with 8 bits per line to get the right NVM file format - * - * 3. create binary file from the hex file - * - * 4. save as "iNVM_xxx.bin" under /lib/firmware - */ -static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) -{ - int ret, section_size; - u16 section_id; - const struct firmware *fw_entry; - const struct { - __le16 word1; - __le16 word2; - u8 data[]; - } *file_sec; - const u8 *eof, *temp; - int max_section_size; - const __le32 *dword_buff; - -#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) -#define NVM_WORD2_ID(x) (x >> 12) -#define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8)) -#define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4) -#define NVM_HEADER_0 (0x2A504C54) -#define NVM_HEADER_1 (0x4E564D2A) -#define NVM_HEADER_SIZE (4 * sizeof(u32)) - - IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n"); - - /* Maximal size depends on HW family and step */ - if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) - max_section_size = IWL_MAX_NVM_SECTION_SIZE; - else - max_section_size = IWL_MAX_NVM_8000_SECTION_SIZE; - - /* - * Obtain NVM image via request_firmware. Since we already used - * request_firmware_nowait() for the firmware binary load and only - * get here after that we assume the NVM request can be satisfied - * synchronously. - */ - ret = request_firmware(&fw_entry, mvm->nvm_file_name, - mvm->trans->dev); - if (ret) { - IWL_ERR(mvm, "ERROR: %s isn't available %d\n", - mvm->nvm_file_name, ret); - return ret; - } - - IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", - mvm->nvm_file_name, fw_entry->size); - - if (fw_entry->size > MAX_NVM_FILE_LEN) { - IWL_ERR(mvm, "NVM file too large\n"); - ret = -EINVAL; - goto out; - } - - eof = fw_entry->data + fw_entry->size; - dword_buff = (__le32 *)fw_entry->data; - - /* some NVM file will contain a header. - * The header is identified by 2 dwords header as follow: - * dword[0] = 0x2A504C54 - * dword[1] = 0x4E564D2A - * - * This header must be skipped when providing the NVM data to the FW. - */ - if (fw_entry->size > NVM_HEADER_SIZE && - dword_buff[0] == cpu_to_le32(NVM_HEADER_0) && - dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) { - file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE); - IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2])); - IWL_INFO(mvm, "NVM Manufacturing date %08X\n", - le32_to_cpu(dword_buff[3])); - - /* nvm file validation, dword_buff[2] holds the file version */ - if ((CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_C_STEP && - le32_to_cpu(dword_buff[2]) < 0xE4A) || - (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP && - le32_to_cpu(dword_buff[2]) >= 0xE4A)) { - ret = -EFAULT; - goto out; - } - } else { - file_sec = (void *)fw_entry->data; - } - - while (true) { - if (file_sec->data > eof) { - IWL_ERR(mvm, - "ERROR - NVM file too short for section header\n"); - ret = -EINVAL; - break; - } - - /* check for EOF marker */ - if (!file_sec->word1 && !file_sec->word2) { - ret = 0; - break; - } - - if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { - section_size = - 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); - section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); - } else { - section_size = 2 * NVM_WORD2_LEN_FAMILY_8000( - le16_to_cpu(file_sec->word2)); - section_id = NVM_WORD1_ID_FAMILY_8000( - le16_to_cpu(file_sec->word1)); - } - - if (section_size > max_section_size) { - IWL_ERR(mvm, "ERROR - section too large (%d)\n", - section_size); - ret = -EINVAL; - break; - } - - if (!section_size) { - IWL_ERR(mvm, "ERROR - section empty\n"); - ret = -EINVAL; - break; - } - - if (file_sec->data + section_size > eof) { - IWL_ERR(mvm, - "ERROR - NVM file too short for section (%d bytes)\n", - section_size); - ret = -EINVAL; - break; - } - - if (WARN(section_id >= NVM_MAX_NUM_SECTIONS, - "Invalid NVM section ID %d\n", section_id)) { - ret = -EINVAL; - break; - } - - temp = kmemdup(file_sec->data, section_size, GFP_KERNEL); - if (!temp) { - ret = -ENOMEM; - break; - } - kfree(mvm->nvm_sections[section_id].data); - mvm->nvm_sections[section_id].data = temp; - mvm->nvm_sections[section_id].length = section_size; - - /* advance to the next section */ - file_sec = (void *)(file_sec->data + section_size); - } -out: - release_firmware(fw_entry); - return ret; -} - -/* 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 = 0; - struct iwl_nvm_section *sections = mvm->nvm_sections; - - IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n"); - - 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; - } - } - return ret; -} - -int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) -{ - int ret, section; - u32 size_read = 0; - u8 *nvm_buffer, *temp; - const char *nvm_file_B = mvm->cfg->default_nvm_file_B_step; - const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step; - - if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) - return -EINVAL; - - /* load NVM values from nic */ - if (read_nvm_from_nic) { - /* Read From FW NVM */ - IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); - - nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, - GFP_KERNEL); - if (!nvm_buffer) - return -ENOMEM; - for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) { - /* we override the constness for initial read */ - ret = iwl_nvm_read_section(mvm, section, nvm_buffer, - size_read); - if (ret < 0) - continue; - size_read += ret; - temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); - if (!temp) { - ret = -ENOMEM; - break; - } - mvm->nvm_sections[section].data = temp; - mvm->nvm_sections[section].length = ret; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - switch (section) { - case NVM_SECTION_TYPE_SW: - mvm->nvm_sw_blob.data = temp; - mvm->nvm_sw_blob.size = ret; - break; - case NVM_SECTION_TYPE_CALIBRATION: - mvm->nvm_calib_blob.data = temp; - mvm->nvm_calib_blob.size = ret; - break; - case NVM_SECTION_TYPE_PRODUCTION: - mvm->nvm_prod_blob.data = temp; - mvm->nvm_prod_blob.size = ret; - break; - case NVM_SECTION_TYPE_PHY_SKU: - mvm->nvm_phy_sku_blob.data = temp; - mvm->nvm_phy_sku_blob.size = ret; - break; - default: - if (section == mvm->cfg->nvm_hw_section_num) { - mvm->nvm_hw_blob.data = temp; - mvm->nvm_hw_blob.size = ret; - break; - } - } -#endif - } - if (!size_read) - IWL_ERR(mvm, "OTP is blank\n"); - kfree(nvm_buffer); - } - - /* Only if PNVM selected in the mod param - load external NVM */ - if (mvm->nvm_file_name) { - /* read External NVM file from the mod param */ - ret = iwl_mvm_read_external_nvm(mvm); - if (ret) { - /* choose the nvm_file name according to the - * HW step - */ - if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == - SILICON_B_STEP) - mvm->nvm_file_name = nvm_file_B; - else - mvm->nvm_file_name = nvm_file_C; - - if (ret == -EFAULT && mvm->nvm_file_name) { - /* in case nvm file was failed try again */ - ret = iwl_mvm_read_external_nvm(mvm); - if (ret) - return ret; - } else { - return ret; - } - } - } - - /* parse the relevant nvm sections */ - mvm->nvm_data = iwl_parse_nvm_sections(mvm); - if (!mvm->nvm_data) - return -ENODATA; - IWL_DEBUG_EEPROM(mvm->trans->dev, "nvm version = %x\n", - mvm->nvm_data->nvm_version); - - return 0; -} - -struct iwl_mcc_update_resp * -iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, - enum iwl_mcc_source src_id) -{ - struct iwl_mcc_update_cmd mcc_update_cmd = { - .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]), - .source_id = (u8)src_id, - }; - struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL; - struct iwl_rx_packet *pkt; - struct iwl_host_cmd cmd = { - .id = MCC_UPDATE_CMD, - .flags = CMD_WANT_SKB, - .data = { &mcc_update_cmd }, - }; - - int ret; - u32 status; - int resp_len, n_channels; - u16 mcc; - - if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) - return ERR_PTR(-EOPNOTSUPP); - - cmd.len[0] = sizeof(struct iwl_mcc_update_cmd); - - IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n", - alpha2[0], alpha2[1], src_id); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) - return ERR_PTR(ret); - - pkt = cmd.resp_pkt; - - /* Extract MCC response */ - mcc_resp = (void *)pkt->data; - status = le32_to_cpu(mcc_resp->status); - - mcc = le16_to_cpu(mcc_resp->mcc); - - /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ - if (mcc == 0) { - mcc = 0x3030; /* "00" - world */ - mcc_resp->mcc = cpu_to_le16(mcc); - } - - n_channels = __le32_to_cpu(mcc_resp->n_channels); - IWL_DEBUG_LAR(mvm, - "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n", - status, mcc, mcc >> 8, mcc & 0xff, - !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels); - - resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32); - resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL); - if (!resp_cp) { - ret = -ENOMEM; - goto exit; - } - - ret = 0; -exit: - iwl_free_resp(&cmd); - if (ret) - return ERR_PTR(ret); - return resp_cp; -} - -#ifdef CONFIG_ACPI -#define WRD_METHOD "WRDD" -#define WRDD_WIFI (0x07) -#define WRDD_WIGIG (0x10) - -static u32 iwl_mvm_wrdd_get_mcc(struct iwl_mvm *mvm, union acpi_object *wrdd) -{ - union acpi_object *mcc_pkg, *domain_type, *mcc_value; - u32 i; - - if (wrdd->type != ACPI_TYPE_PACKAGE || - wrdd->package.count < 2 || - wrdd->package.elements[0].type != ACPI_TYPE_INTEGER || - wrdd->package.elements[0].integer.value != 0) { - IWL_DEBUG_LAR(mvm, "Unsupported wrdd structure\n"); - return 0; - } - - for (i = 1 ; i < wrdd->package.count ; ++i) { - mcc_pkg = &wrdd->package.elements[i]; - - if (mcc_pkg->type != ACPI_TYPE_PACKAGE || - mcc_pkg->package.count < 2 || - mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER || - mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { - mcc_pkg = NULL; - continue; - } - - domain_type = &mcc_pkg->package.elements[0]; - if (domain_type->integer.value == WRDD_WIFI) - break; - - mcc_pkg = NULL; - } - - if (mcc_pkg) { - mcc_value = &mcc_pkg->package.elements[1]; - return mcc_value->integer.value; - } - - return 0; -} - -static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc) -{ - acpi_handle root_handle; - acpi_handle handle; - struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL}; - acpi_status status; - u32 mcc_val; - struct pci_dev *pdev = to_pci_dev(mvm->dev); - - root_handle = ACPI_HANDLE(&pdev->dev); - if (!root_handle) { - IWL_DEBUG_LAR(mvm, - "Could not retrieve root port ACPI handle\n"); - return -ENOENT; - } - - /* Get the method's handle */ - status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle); - if (ACPI_FAILURE(status)) { - IWL_DEBUG_LAR(mvm, "WRD method not found\n"); - return -ENOENT; - } - - /* Call WRDD with no arguments */ - status = acpi_evaluate_object(handle, NULL, NULL, &wrdd); - if (ACPI_FAILURE(status)) { - IWL_DEBUG_LAR(mvm, "WRDC invocation failed (0x%x)\n", status); - return -ENOENT; - } - - mcc_val = iwl_mvm_wrdd_get_mcc(mvm, wrdd.pointer); - kfree(wrdd.pointer); - if (!mcc_val) - return -ENOENT; - - mcc[0] = (mcc_val >> 8) & 0xff; - mcc[1] = mcc_val & 0xff; - mcc[2] = '\0'; - return 0; -} -#else /* CONFIG_ACPI */ -static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc) -{ - return -ENOENT; -} -#endif - -int iwl_mvm_init_mcc(struct iwl_mvm *mvm) -{ - bool tlv_lar; - bool nvm_lar; - int retval; - struct ieee80211_regdomain *regd; - char mcc[3]; - - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) { - tlv_lar = fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_LAR_SUPPORT); - nvm_lar = mvm->nvm_data->lar_enabled; - if (tlv_lar != nvm_lar) - IWL_INFO(mvm, - "Conflict between TLV & NVM regarding enabling LAR (TLV = %s NVM =%s)\n", - tlv_lar ? "enabled" : "disabled", - nvm_lar ? "enabled" : "disabled"); - } - - if (!iwl_mvm_is_lar_supported(mvm)) - return 0; - - /* - * try to replay the last set MCC to FW. If it doesn't exist, - * queue an update to cfg80211 to retrieve the default alpha2 from FW. - */ - retval = iwl_mvm_init_fw_regd(mvm); - if (retval != -ENOENT) - return retval; - - /* - * Driver regulatory hint for initial update, this also informs the - * firmware we support wifi location updates. - * Disallow scans that might crash the FW while the LAR regdomain - * is not set. - */ - mvm->lar_regdom_set = false; - - regd = iwl_mvm_get_current_regdomain(mvm, NULL); - if (IS_ERR_OR_NULL(regd)) - return -EIO; - - if (iwl_mvm_is_wifi_mcc_supported(mvm) && - !iwl_mvm_get_bios_mcc(mvm, mcc)) { - kfree(regd); - regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, - MCC_SOURCE_BIOS, NULL); - if (IS_ERR_OR_NULL(regd)) - return -EIO; - } - - retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd); - kfree(regd); - return retval; -} - -void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mcc_chub_notif *notif = (void *)pkt->data; - enum iwl_mcc_source src; - char mcc[3]; - struct ieee80211_regdomain *regd; - - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) - return; - - mcc[0] = notif->mcc >> 8; - mcc[1] = notif->mcc & 0xff; - mcc[2] = '\0'; - src = notif->source_id; - - IWL_DEBUG_LAR(mvm, - "RX: received chub update mcc cmd (mcc '%s' src %d)\n", - mcc, src); - regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL); - if (IS_ERR_OR_NULL(regd)) - return; - - regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); - kfree(regd); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/offloading.c b/drivers/net/wireless/iwlwifi/mvm/offloading.c deleted file mode 100644 index 68b0169c8892..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/offloading.c +++ /dev/null @@ -1,217 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include -#include -#include "mvm.h" - -void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, - struct iwl_wowlan_config_cmd *cmd) -{ - int i; - - /* - * For QoS counters, we store the one to use next, so subtract 0x10 - * since the uCode will add 0x10 *before* using the value while we - * increment after using the value (i.e. store the next value to use). - */ - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - u16 seq = mvm_ap_sta->tid_data[i].seq_number; - seq -= 0x10; - cmd->qos_seq[i] = cpu_to_le16(seq); - } -} - -int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool disable_offloading, - u32 cmd_flags) -{ - union { - struct iwl_proto_offload_cmd_v1 v1; - struct iwl_proto_offload_cmd_v2 v2; - struct iwl_proto_offload_cmd_v3_small v3s; - struct iwl_proto_offload_cmd_v3_large v3l; - } cmd = {}; - struct iwl_host_cmd hcmd = { - .id = PROT_OFFLOAD_CONFIG_CMD, - .flags = cmd_flags, - .data[0] = &cmd, - .dataflags[0] = IWL_HCMD_DFL_DUP, - }; - struct iwl_proto_offload_cmd_common *common; - u32 enabled = 0, size; - u32 capa_flags = mvm->fw->ucode_capa.flags; -#if IS_ENABLED(CONFIG_IPV6) - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int i; - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || - capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { - struct iwl_ns_config *nsc; - struct iwl_targ_addr *addrs; - int n_nsc, n_addrs; - int c; - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { - nsc = cmd.v3s.ns_config; - n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S; - addrs = cmd.v3s.targ_addrs; - n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S; - } else { - nsc = cmd.v3l.ns_config; - n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; - addrs = cmd.v3l.targ_addrs; - n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; - } - - if (mvmvif->num_target_ipv6_addrs) - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - - /* - * For each address we have (and that will fit) fill a target - * address struct and combine for NS offload structs with the - * solicited node addresses. - */ - for (i = 0, c = 0; - i < mvmvif->num_target_ipv6_addrs && - i < n_addrs && c < n_nsc; i++) { - struct in6_addr solicited_addr; - int j; - - addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], - &solicited_addr); - for (j = 0; j < c; j++) - if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, - &solicited_addr) == 0) - break; - if (j == c) - c++; - addrs[i].addr = mvmvif->target_ipv6_addrs[i]; - addrs[i].config_num = cpu_to_le32(j); - nsc[j].dest_ipv6_addr = solicited_addr; - memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); - } - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) - cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i); - else - cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i); - } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { - if (mvmvif->num_target_ipv6_addrs) { - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); - } - - BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != - sizeof(mvmvif->target_ipv6_addrs[0])); - - for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, - IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) - memcpy(cmd.v2.target_ipv6_addr[i], - &mvmvif->target_ipv6_addrs[i], - sizeof(cmd.v2.target_ipv6_addr[i])); - } else { - if (mvmvif->num_target_ipv6_addrs) { - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); - } - - BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != - sizeof(mvmvif->target_ipv6_addrs[0])); - - for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, - IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) - memcpy(cmd.v1.target_ipv6_addr[i], - &mvmvif->target_ipv6_addrs[i], - sizeof(cmd.v1.target_ipv6_addr[i])); - } -#endif - - if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { - common = &cmd.v3s.common; - size = sizeof(cmd.v3s); - } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { - common = &cmd.v3l.common; - size = sizeof(cmd.v3l); - } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { - common = &cmd.v2.common; - size = sizeof(cmd.v2); - } else { - common = &cmd.v1.common; - size = sizeof(cmd.v1); - } - - if (vif->bss_conf.arp_addr_cnt) { - enabled |= IWL_D3_PROTO_OFFLOAD_ARP; - common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; - memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); - } - - if (!disable_offloading) - common->enabled = cpu_to_le32(enabled); - - hcmd.len[0] = size; - return iwl_mvm_send_cmd(mvm, &hcmd); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c deleted file mode 100644 index 13c97f665ba8..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ /dev/null @@ -1,1434 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include -#include -#include - -#include "iwl-notif-wait.h" -#include "iwl-trans.h" -#include "iwl-op-mode.h" -#include "iwl-fw.h" -#include "iwl-debug.h" -#include "iwl-drv.h" -#include "iwl-modparams.h" -#include "mvm.h" -#include "iwl-phy-db.h" -#include "iwl-eeprom-parse.h" -#include "iwl-csr.h" -#include "iwl-io.h" -#include "iwl-prph.h" -#include "rs.h" -#include "fw-api-scan.h" -#include "time-event.h" - -#define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); -MODULE_LICENSE("GPL"); - -static const struct iwl_op_mode_ops iwl_mvm_ops; -static const struct iwl_op_mode_ops iwl_mvm_ops_mq; - -struct iwl_mvm_mod_params iwlmvm_mod_params = { - .power_scheme = IWL_POWER_SCHEME_BPS, - .tfd_q_hang_detect = true - /* rest of fields are 0 by default */ -}; - -module_param_named(init_dbg, iwlmvm_mod_params.init_dbg, bool, S_IRUGO); -MODULE_PARM_DESC(init_dbg, - "set to true to debug an ASSERT in INIT fw (default: false"); -module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO); -MODULE_PARM_DESC(power_scheme, - "power management scheme: 1-active, 2-balanced, 3-low power, default: 2"); -module_param_named(tfd_q_hang_detect, iwlmvm_mod_params.tfd_q_hang_detect, - bool, S_IRUGO); -MODULE_PARM_DESC(tfd_q_hang_detect, - "TFD queues hang detection (default: true"); - -/* - * module init and exit functions - */ -static int __init iwl_mvm_init(void) -{ - int ret; - - ret = iwl_mvm_rate_control_register(); - if (ret) { - pr_err("Unable to register rate control algorithm: %d\n", ret); - return ret; - } - - ret = iwl_opmode_register("iwlmvm", &iwl_mvm_ops); - - if (ret) { - pr_err("Unable to register MVM op_mode: %d\n", ret); - iwl_mvm_rate_control_unregister(); - } - - return ret; -} -module_init(iwl_mvm_init); - -static void __exit iwl_mvm_exit(void) -{ - iwl_opmode_deregister("iwlmvm"); - iwl_mvm_rate_control_unregister(); -} -module_exit(iwl_mvm_exit); - -static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash; - u32 reg_val = 0; - u32 phy_config = iwl_mvm_get_phy_config(mvm); - - radio_cfg_type = (phy_config & FW_PHY_CFG_RADIO_TYPE) >> - FW_PHY_CFG_RADIO_TYPE_POS; - radio_cfg_step = (phy_config & FW_PHY_CFG_RADIO_STEP) >> - FW_PHY_CFG_RADIO_STEP_POS; - radio_cfg_dash = (phy_config & FW_PHY_CFG_RADIO_DASH) >> - FW_PHY_CFG_RADIO_DASH_POS; - - /* SKU control */ - reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) << - CSR_HW_IF_CONFIG_REG_POS_MAC_STEP; - reg_val |= CSR_HW_REV_DASH(mvm->trans->hw_rev) << - CSR_HW_IF_CONFIG_REG_POS_MAC_DASH; - - /* radio configuration */ - reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; - reg_val |= radio_cfg_step << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP; - reg_val |= radio_cfg_dash << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; - - WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) & - ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE); - - /* - * TODO: Bits 7-8 of CSR in 8000 HW family set the ADC sampling, and - * shouldn't be set to any non-zero value. The same is supposed to be - * true of the other HW, but unsetting them (such as the 7260) causes - * automatic tests to fail on seemingly unrelated errors. Need to - * further investigate this, but for now we'll separate cases. - */ - if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) - reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI; - - iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | - CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP | - CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | - CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | - CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH | - CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | - CSR_HW_IF_CONFIG_REG_BIT_MAC_SI, - reg_val); - - IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, - radio_cfg_step, radio_cfg_dash); - - /* - * W/A : NIC is stuck in a reset state after Early PCIe power off - * (PCIe power is lost before PERST# is asserted), causing ME FW - * to lose ownership and not being able to obtain it back. - */ - if (!mvm->trans->cfg->apmg_not_supported) - iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, - ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); -} - -struct iwl_rx_handlers { - u16 cmd_id; - bool async; - void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -}; - -#define RX_HANDLER(_cmd_id, _fn, _async) \ - { .cmd_id = _cmd_id , .fn = _fn , .async = _async } -#define RX_HANDLER_GRP(_grp, _cmd, _fn, _async) \ - { .cmd_id = WIDE_ID(_grp, _cmd), .fn = _fn, .async = _async } - -/* - * Handlers for fw notifications - * Convention: RX_HANDLER(CMD_NAME, iwl_mvm_rx_CMD_NAME - * This list should be in order of frequency for performance purposes. - * - * The handler can be SYNC - this means that it will be called in the Rx path - * which can't acquire mvm->mutex. If the handler needs to hold mvm->mutex (and - * only in this case!), it should be set as ASYNC. In that case, it will be - * called from a worker with mvm->mutex held. - */ -static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { - RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false), - RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false), - - RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), - RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true), - RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), - RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION, - iwl_mvm_rx_ant_coupling_notif, true), - - RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false), - RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, true), - - RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false), - - RX_HANDLER(SCAN_ITERATION_COMPLETE, - iwl_mvm_rx_lmac_scan_iter_complete_notif, false), - RX_HANDLER(SCAN_OFFLOAD_COMPLETE, - iwl_mvm_rx_lmac_scan_complete_notif, true), - RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_match_found, - false), - RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif, - true), - RX_HANDLER(SCAN_ITERATION_COMPLETE_UMAC, - iwl_mvm_rx_umac_scan_iter_complete_notif, false), - - RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), - - RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif, - false), - - RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false), - RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION, - iwl_mvm_power_uapsd_misbehaving_ap_notif, false), - RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true), - RX_HANDLER_GRP(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE, - iwl_mvm_temp_notif, true), - - RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif, - true), - RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, false), - RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler, true), - -}; -#undef RX_HANDLER -#undef RX_HANDLER_GRP -#define CMD(x) [x] = #x - -static const char *const iwl_mvm_cmd_strings[REPLY_MAX + 1] = { - CMD(MVM_ALIVE), - CMD(REPLY_ERROR), - CMD(ECHO_CMD), - CMD(INIT_COMPLETE_NOTIF), - CMD(PHY_CONTEXT_CMD), - CMD(MGMT_MCAST_KEY), - CMD(TX_CMD), - CMD(TXPATH_FLUSH), - CMD(SHARED_MEM_CFG), - CMD(MAC_CONTEXT_CMD), - CMD(TIME_EVENT_CMD), - CMD(TIME_EVENT_NOTIFICATION), - CMD(BINDING_CONTEXT_CMD), - CMD(TIME_QUOTA_CMD), - CMD(NON_QOS_TX_COUNTER_CMD), - CMD(DC2DC_CONFIG_CMD), - CMD(NVM_ACCESS_CMD), - CMD(PHY_CONFIGURATION_CMD), - CMD(CALIB_RES_NOTIF_PHY_DB), - CMD(SET_CALIB_DEFAULT_CMD), - CMD(FW_PAGING_BLOCK_CMD), - CMD(ADD_STA_KEY), - CMD(ADD_STA), - CMD(FW_GET_ITEM_CMD), - CMD(REMOVE_STA), - CMD(LQ_CMD), - CMD(SCAN_OFFLOAD_CONFIG_CMD), - CMD(MATCH_FOUND_NOTIFICATION), - CMD(SCAN_OFFLOAD_REQUEST_CMD), - CMD(SCAN_OFFLOAD_ABORT_CMD), - CMD(HOT_SPOT_CMD), - CMD(SCAN_OFFLOAD_COMPLETE), - CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD), - CMD(SCAN_ITERATION_COMPLETE), - CMD(POWER_TABLE_CMD), - CMD(WEP_KEY), - CMD(REPLY_RX_PHY_CMD), - CMD(REPLY_RX_MPDU_CMD), - CMD(BEACON_NOTIFICATION), - CMD(BEACON_TEMPLATE_CMD), - CMD(STATISTICS_CMD), - CMD(STATISTICS_NOTIFICATION), - CMD(EOSP_NOTIFICATION), - CMD(REDUCE_TX_POWER_CMD), - CMD(TX_ANT_CONFIGURATION_CMD), - CMD(D3_CONFIG_CMD), - CMD(D0I3_END_CMD), - CMD(PROT_OFFLOAD_CONFIG_CMD), - CMD(OFFLOADS_QUERY_CMD), - CMD(REMOTE_WAKE_CONFIG_CMD), - CMD(WOWLAN_PATTERNS), - CMD(WOWLAN_CONFIGURATION), - CMD(WOWLAN_TSC_RSC_PARAM), - CMD(WOWLAN_TKIP_PARAM), - CMD(WOWLAN_KEK_KCK_MATERIAL), - CMD(WOWLAN_GET_STATUSES), - CMD(WOWLAN_TX_POWER_PER_DB), - CMD(SCAN_OFFLOAD_PROFILES_QUERY_CMD), - CMD(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD), - CMD(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD), - CMD(CARD_STATE_NOTIFICATION), - CMD(MISSED_BEACONS_NOTIFICATION), - CMD(BT_COEX_PRIO_TABLE), - CMD(BT_COEX_PROT_ENV), - CMD(BT_PROFILE_NOTIFICATION), - CMD(BT_CONFIG), - CMD(MCAST_FILTER_CMD), - CMD(BCAST_FILTER_CMD), - CMD(REPLY_SF_CFG_CMD), - CMD(REPLY_BEACON_FILTERING_CMD), - CMD(CMD_DTS_MEASUREMENT_TRIGGER), - CMD(DTS_MEASUREMENT_NOTIFICATION), - CMD(REPLY_THERMAL_MNG_BACKOFF), - CMD(MAC_PM_POWER_TABLE), - CMD(LTR_CONFIG), - CMD(BT_COEX_CI), - CMD(BT_COEX_UPDATE_SW_BOOST), - CMD(BT_COEX_UPDATE_CORUN_LUT), - CMD(BT_COEX_UPDATE_REDUCED_TXP), - CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), - CMD(ANTENNA_COUPLING_NOTIFICATION), - CMD(SCD_QUEUE_CFG), - CMD(SCAN_CFG_CMD), - CMD(SCAN_REQ_UMAC), - CMD(SCAN_ABORT_UMAC), - CMD(SCAN_COMPLETE_UMAC), - CMD(TDLS_CHANNEL_SWITCH_CMD), - CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION), - CMD(TDLS_CONFIG_CMD), - CMD(MCC_UPDATE_CMD), - CMD(SCAN_ITERATION_COMPLETE_UMAC), -}; -#undef CMD - -/* this forward declaration can avoid to export the function */ -static void iwl_mvm_async_handlers_wk(struct work_struct *wk); -static void iwl_mvm_d0i3_exit_work(struct work_struct *wk); - -static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg) -{ - const struct iwl_pwr_tx_backoff *pwr_tx_backoff = cfg->pwr_tx_backoffs; - - if (!pwr_tx_backoff) - return 0; - - while (pwr_tx_backoff->pwr) { - if (trans->dflt_pwr_limit >= pwr_tx_backoff->pwr) - return pwr_tx_backoff->backoff; - - pwr_tx_backoff++; - } - - return 0; -} - -static void iwl_mvm_fw_error_dump_wk(struct work_struct *work); - -static struct iwl_op_mode * -iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, - const struct iwl_fw *fw, struct dentry *dbgfs_dir) -{ - struct ieee80211_hw *hw; - struct iwl_op_mode *op_mode; - struct iwl_mvm *mvm; - struct iwl_trans_config trans_cfg = {}; - static const u8 no_reclaim_cmds[] = { - TX_CMD, - }; - int err, scan_size; - u32 min_backoff; - - /* - * We use IWL_MVM_STATION_COUNT to check the validity of the station - * index all over the driver - check that its value corresponds to the - * array size. - */ - BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT); - - /******************************** - * 1. Allocating and configuring HW data - ********************************/ - hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) + - sizeof(struct iwl_mvm), - &iwl_mvm_hw_ops); - if (!hw) - return NULL; - - if (cfg->max_rx_agg_size) - hw->max_rx_aggregation_subframes = cfg->max_rx_agg_size; - - if (cfg->max_tx_agg_size) - hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size; - - op_mode = hw->priv; - - mvm = IWL_OP_MODE_GET_MVM(op_mode); - mvm->dev = trans->dev; - mvm->trans = trans; - mvm->cfg = cfg; - mvm->fw = fw; - mvm->hw = hw; - - if (iwl_mvm_has_new_rx_api(mvm)) { - op_mode->ops = &iwl_mvm_ops_mq; - } else { - op_mode->ops = &iwl_mvm_ops; - - if (WARN_ON(trans->num_rx_queues > 1)) - goto out_free; - } - - mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0; - - mvm->aux_queue = 15; - mvm->first_agg_queue = 16; - mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1; - if (mvm->cfg->base_params->num_of_queues == 16) { - mvm->aux_queue = 11; - mvm->first_agg_queue = 12; - } - mvm->sf_state = SF_UNINIT; - mvm->low_latency_agg_frame_limit = 6; - mvm->cur_ucode = IWL_UCODE_INIT; - - mutex_init(&mvm->mutex); - mutex_init(&mvm->d0i3_suspend_mutex); - spin_lock_init(&mvm->async_handlers_lock); - INIT_LIST_HEAD(&mvm->time_event_list); - INIT_LIST_HEAD(&mvm->aux_roc_te_list); - INIT_LIST_HEAD(&mvm->async_handlers_list); - spin_lock_init(&mvm->time_event_lock); - spin_lock_init(&mvm->queue_info_lock); - - INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); - INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); - INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); - INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); - INIT_DELAYED_WORK(&mvm->fw_dump_wk, iwl_mvm_fw_error_dump_wk); - INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work); - - spin_lock_init(&mvm->d0i3_tx_lock); - spin_lock_init(&mvm->refs_lock); - skb_queue_head_init(&mvm->d0i3_tx); - init_waitqueue_head(&mvm->d0i3_exit_waitq); - - SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); - - /* - * Populate the state variables that the transport layer needs - * to know about. - */ - trans_cfg.op_mode = op_mode; - trans_cfg.no_reclaim_cmds = no_reclaim_cmds; - trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); - trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; - trans_cfg.wide_cmd_header = fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_WIDE_CMD_HDR); - - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) - trans_cfg.bc_table_dword = true; - - trans_cfg.command_names = iwl_mvm_cmd_strings; - - trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; - trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; - trans_cfg.scd_set_active = true; - - trans_cfg.sdio_adma_addr = fw->sdio_adma_addr; - - /* Set a short watchdog for the command queue */ - trans_cfg.cmd_q_wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, NULL, false, true); - - snprintf(mvm->hw->wiphy->fw_version, - sizeof(mvm->hw->wiphy->fw_version), - "%s", fw->fw_version); - - /* Configure transport layer */ - iwl_trans_configure(mvm->trans, &trans_cfg); - - trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; - trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); - trans->dbg_dest_tlv = mvm->fw->dbg_dest_tlv; - trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num; - memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv, - sizeof(trans->dbg_conf_tlv)); - trans->dbg_trigger_tlv = mvm->fw->dbg_trigger_tlv; - - /* set up notification wait support */ - iwl_notification_wait_init(&mvm->notif_wait); - - /* Init phy db */ - mvm->phy_db = iwl_phy_db_init(trans); - if (!mvm->phy_db) { - IWL_ERR(mvm, "Cannot init phy_db\n"); - goto out_free; - } - - IWL_INFO(mvm, "Detected %s, REV=0x%X\n", - mvm->cfg->name, mvm->trans->hw_rev); - - min_backoff = calc_min_backoff(trans, cfg); - iwl_mvm_tt_initialize(mvm, min_backoff); - - if (iwlwifi_mod_params.nvm_file) - mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; - else - IWL_DEBUG_EEPROM(mvm->trans->dev, - "working without external nvm file\n"); - - if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, - "not allowing power-up and not having nvm_file\n")) - goto out_free; - - /* - * Even if nvm exists in the nvm_file driver should read again the nvm - * from the nic because there might be entries that exist in the OTP - * and not in the file. - * for nics with no_power_up_nic_in_init: rely completley on nvm_file - */ - if (cfg->no_power_up_nic_in_init && mvm->nvm_file_name) { - err = iwl_nvm_init(mvm, false); - if (err) - goto out_free; - } else { - err = iwl_trans_start_hw(mvm->trans); - if (err) - goto out_free; - - mutex_lock(&mvm->mutex); - err = iwl_run_init_mvm_ucode(mvm, true); - if (!err || !iwlmvm_mod_params.init_dbg) - iwl_trans_stop_device(trans); - mutex_unlock(&mvm->mutex); - /* returns 0 if successful, 1 if success but in rfkill */ - if (err < 0 && !iwlmvm_mod_params.init_dbg) { - IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); - goto out_free; - } - } - - scan_size = iwl_mvm_scan_size(mvm); - - mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL); - if (!mvm->scan_cmd) - goto out_free; - - /* Set EBS as successful as long as not stated otherwise by the FW. */ - mvm->last_ebs_successful = true; - - err = iwl_mvm_mac_setup_register(mvm); - if (err) - goto out_free; - - err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir); - if (err) - goto out_unregister; - - memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); - - /* rpm starts with a taken ref. only set the appropriate bit here. */ - mvm->refs[IWL_MVM_REF_UCODE_DOWN] = 1; - - iwl_mvm_tof_init(mvm); - - return op_mode; - - out_unregister: - ieee80211_unregister_hw(mvm->hw); - iwl_mvm_leds_exit(mvm); - out_free: - flush_delayed_work(&mvm->fw_dump_wk); - iwl_phy_db_free(mvm->phy_db); - kfree(mvm->scan_cmd); - if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name) - iwl_trans_op_mode_leave(trans); - ieee80211_free_hw(mvm->hw); - return NULL; -} - -static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - int i; - - iwl_mvm_leds_exit(mvm); - - iwl_mvm_tt_exit(mvm); - - ieee80211_unregister_hw(mvm->hw); - - kfree(mvm->scan_cmd); - kfree(mvm->mcast_filter_cmd); - mvm->mcast_filter_cmd = NULL; - -#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS) - kfree(mvm->d3_resume_sram); - if (mvm->nd_config) { - kfree(mvm->nd_config->match_sets); - kfree(mvm->nd_config->scan_plans); - kfree(mvm->nd_config); - mvm->nd_config = NULL; - } -#endif - - iwl_trans_op_mode_leave(mvm->trans); - - iwl_phy_db_free(mvm->phy_db); - mvm->phy_db = NULL; - - iwl_free_nvm_data(mvm->nvm_data); - for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++) - kfree(mvm->nvm_sections[i].data); - - iwl_mvm_tof_clean(mvm); - - ieee80211_free_hw(mvm->hw); -} - -struct iwl_async_handler_entry { - struct list_head list; - struct iwl_rx_cmd_buffer rxb; - void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -}; - -void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm) -{ - struct iwl_async_handler_entry *entry, *tmp; - - spin_lock_bh(&mvm->async_handlers_lock); - list_for_each_entry_safe(entry, tmp, &mvm->async_handlers_list, list) { - iwl_free_rxb(&entry->rxb); - list_del(&entry->list); - kfree(entry); - } - spin_unlock_bh(&mvm->async_handlers_lock); -} - -static void iwl_mvm_async_handlers_wk(struct work_struct *wk) -{ - struct iwl_mvm *mvm = - container_of(wk, struct iwl_mvm, async_handlers_wk); - struct iwl_async_handler_entry *entry, *tmp; - struct list_head local_list; - - INIT_LIST_HEAD(&local_list); - - /* Ensure that we are not in stop flow (check iwl_mvm_mac_stop) */ - mutex_lock(&mvm->mutex); - - /* - * Sync with Rx path with a lock. Remove all the entries from this list, - * add them to a local one (lock free), and then handle them. - */ - spin_lock_bh(&mvm->async_handlers_lock); - list_splice_init(&mvm->async_handlers_list, &local_list); - spin_unlock_bh(&mvm->async_handlers_lock); - - list_for_each_entry_safe(entry, tmp, &local_list, list) { - entry->fn(mvm, &entry->rxb); - iwl_free_rxb(&entry->rxb); - list_del(&entry->list); - kfree(entry); - } - mutex_unlock(&mvm->mutex); -} - -static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_cmd *cmds_trig; - int i; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF); - cmds_trig = (void *)trig->data; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) - return; - - for (i = 0; i < ARRAY_SIZE(cmds_trig->cmds); i++) { - /* don't collect on CMD 0 */ - if (!cmds_trig->cmds[i].cmd_id) - break; - - if (cmds_trig->cmds[i].cmd_id != pkt->hdr.cmd || - cmds_trig->cmds[i].group_id != pkt->hdr.group_id) - continue; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "CMD 0x%02x.%02x received", - pkt->hdr.group_id, pkt->hdr.cmd); - break; - } -} - -static void iwl_mvm_rx_common(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_rx_packet *pkt) -{ - int i; - - iwl_mvm_rx_check_trigger(mvm, pkt); - - /* - * Do the notification wait before RX handlers so - * even if the RX handler consumes the RXB we have - * access to it in the notification wait entry. - */ - iwl_notification_wait_notify(&mvm->notif_wait, pkt); - - for (i = 0; i < ARRAY_SIZE(iwl_mvm_rx_handlers); i++) { - const struct iwl_rx_handlers *rx_h = &iwl_mvm_rx_handlers[i]; - struct iwl_async_handler_entry *entry; - - if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) - continue; - - if (!rx_h->async) { - rx_h->fn(mvm, rxb); - return; - } - - entry = kzalloc(sizeof(*entry), GFP_ATOMIC); - /* we can't do much... */ - if (!entry) - return; - - entry->rxb._page = rxb_steal_page(rxb); - entry->rxb._offset = rxb->_offset; - entry->rxb._rx_page_order = rxb->_rx_page_order; - entry->fn = rx_h->fn; - spin_lock(&mvm->async_handlers_lock); - list_add_tail(&entry->list, &mvm->async_handlers_list); - spin_unlock(&mvm->async_handlers_lock); - schedule_work(&mvm->async_handlers_wk); - break; - } -} - -static void iwl_mvm_rx(struct iwl_op_mode *op_mode, - struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) - iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); - else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) - iwl_mvm_rx_rx_phy_cmd(mvm, rxb); - else - iwl_mvm_rx_common(mvm, rxb, pkt); -} - -static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode, - struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) - iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); - else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) - iwl_mvm_rx_rx_phy_cmd(mvm, rxb); - else - iwl_mvm_rx_common(mvm, rxb, pkt); -} - -static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - unsigned long mq; - int q; - - spin_lock_bh(&mvm->queue_info_lock); - mq = mvm->queue_info[queue].hw_queue_to_mac80211; - spin_unlock_bh(&mvm->queue_info_lock); - - if (WARN_ON_ONCE(!mq)) - return; - - for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) { - if (atomic_inc_return(&mvm->mac80211_queue_stop_count[q]) > 1) { - IWL_DEBUG_TX_QUEUES(mvm, - "queue %d (mac80211 %d) already stopped\n", - queue, q); - continue; - } - - ieee80211_stop_queue(mvm->hw, q); - } -} - -static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - unsigned long mq; - int q; - - spin_lock_bh(&mvm->queue_info_lock); - mq = mvm->queue_info[queue].hw_queue_to_mac80211; - spin_unlock_bh(&mvm->queue_info_lock); - - if (WARN_ON_ONCE(!mq)) - return; - - for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) { - if (atomic_dec_return(&mvm->mac80211_queue_stop_count[q]) > 0) { - IWL_DEBUG_TX_QUEUES(mvm, - "queue %d (mac80211 %d) still stopped\n", - queue, q); - continue; - } - - ieee80211_wake_queue(mvm->hw, q); - } -} - -void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) -{ - if (state) - set_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); - else - clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); - - wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); -} - -static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - bool calibrating = ACCESS_ONCE(mvm->calibrating); - - if (state) - set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); - else - clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); - - wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); - - /* iwl_run_init_mvm_ucode is waiting for results, abort it */ - if (calibrating) - iwl_abort_notification_waits(&mvm->notif_wait); - - /* - * Stop the device if we run OPERATIONAL firmware or if we are in the - * middle of the calibrations. - */ - return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating); -} - -static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - struct ieee80211_tx_info *info; - - info = IEEE80211_SKB_CB(skb); - iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); - ieee80211_free_txskb(mvm->hw, skb); -} - -struct iwl_mvm_reprobe { - struct device *dev; - struct work_struct work; -}; - -static void iwl_mvm_reprobe_wk(struct work_struct *wk) -{ - struct iwl_mvm_reprobe *reprobe; - - reprobe = container_of(wk, struct iwl_mvm_reprobe, work); - if (device_reprobe(reprobe->dev)) - dev_err(reprobe->dev, "reprobe failed!\n"); - kfree(reprobe); - module_put(THIS_MODULE); -} - -static void iwl_mvm_fw_error_dump_wk(struct work_struct *work) -{ - struct iwl_mvm *mvm = - container_of(work, struct iwl_mvm, fw_dump_wk.work); - - if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT)) - return; - - mutex_lock(&mvm->mutex); - - /* stop recording */ - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { - iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); - } else { - iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0); - /* wait before we collect the data till the DBGC stop */ - udelay(100); - } - - iwl_mvm_fw_error_dump(mvm); - - /* start recording again if the firmware is not crashed */ - WARN_ON_ONCE((!test_bit(STATUS_FW_ERROR, &mvm->trans->status)) && - mvm->fw->dbg_dest_tlv && - iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf)); - - mutex_unlock(&mvm->mutex); - - iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT); -} - -void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) -{ - iwl_abort_notification_waits(&mvm->notif_wait); - - /* - * This is a bit racy, but worst case we tell mac80211 about - * a stopped/aborted scan when that was already done which - * is not a problem. It is necessary to abort any os scan - * here because mac80211 requires having the scan cleared - * before restarting. - * We'll reset the scan_status to NONE in restart cleanup in - * the next start() call from mac80211. If restart isn't called - * (no fw restart) scan status will stay busy. - */ - iwl_mvm_report_scan_aborted(mvm); - - /* - * If we're restarting already, don't cycle restarts. - * If INIT fw asserted, it will likely fail again. - * If WoWLAN fw asserted, don't restart either, mac80211 - * can't recover this since we're already half suspended. - */ - if (!mvm->restart_fw && fw_error) { - iwl_mvm_fw_dbg_collect_desc(mvm, &iwl_mvm_dump_desc_assert, - NULL); - } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, - &mvm->status)) { - struct iwl_mvm_reprobe *reprobe; - - IWL_ERR(mvm, - "Firmware error during reconfiguration - reprobe!\n"); - - /* - * get a module reference to avoid doing this while unloading - * anyway and to avoid scheduling a work with code that's - * being removed. - */ - if (!try_module_get(THIS_MODULE)) { - IWL_ERR(mvm, "Module is being unloaded - abort\n"); - return; - } - - reprobe = kzalloc(sizeof(*reprobe), GFP_ATOMIC); - if (!reprobe) { - module_put(THIS_MODULE); - return; - } - reprobe->dev = mvm->trans->dev; - INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); - schedule_work(&reprobe->work); - } else if (mvm->cur_ucode == IWL_UCODE_REGULAR) { - /* don't let the transport/FW power down */ - iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); - - if (fw_error && mvm->restart_fw > 0) - mvm->restart_fw--; - ieee80211_restart_hw(mvm->hw); - } -} - -static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - iwl_mvm_dump_nic_error_log(mvm); - - iwl_mvm_nic_restart(mvm, true); -} - -static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - WARN_ON(1); - iwl_mvm_nic_restart(mvm, true); -} - -struct iwl_d0i3_iter_data { - struct iwl_mvm *mvm; - u8 ap_sta_id; - u8 vif_count; - u8 offloading_tid; - bool disable_offloading; -}; - -static bool iwl_mvm_disallow_offloading(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_d0i3_iter_data *iter_data) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_sta *ap_sta; - struct iwl_mvm_sta *mvmsta; - u32 available_tids = 0; - u8 tid; - - if (WARN_ON(vif->type != NL80211_IFTYPE_STATION || - mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)) - return false; - - ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id]); - if (IS_ERR_OR_NULL(ap_sta)) - return false; - - mvmsta = iwl_mvm_sta_from_mac80211(ap_sta); - spin_lock_bh(&mvmsta->lock); - for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - - /* - * in case of pending tx packets, don't use this tid - * for offloading in order to prevent reuse of the same - * qos seq counters. - */ - if (iwl_mvm_tid_queued(tid_data)) - continue; - - if (tid_data->state != IWL_AGG_OFF) - continue; - - available_tids |= BIT(tid); - } - spin_unlock_bh(&mvmsta->lock); - - /* - * disallow protocol offloading if we have no available tid - * (with no pending frames and no active aggregation, - * as we don't handle "holes" properly - the scheduler needs the - * frame's seq number and TFD index to match) - */ - if (!available_tids) - return true; - - /* for simplicity, just use the first available tid */ - iter_data->offloading_tid = ffs(available_tids) - 1; - return false; -} - -static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_d0i3_iter_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; - - IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr); - if (vif->type != NL80211_IFTYPE_STATION || - !vif->bss_conf.assoc) - return; - - /* - * in case of pending tx packets or active aggregations, - * avoid offloading features in order to prevent reuse of - * the same qos seq counters. - */ - if (iwl_mvm_disallow_offloading(mvm, vif, data)) - data->disable_offloading = true; - - iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags); - iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading, flags); - - /* - * on init/association, mvm already configures POWER_TABLE_CMD - * and REPLY_MCAST_FILTER_CMD, so currently don't - * reconfigure them (we might want to use different - * params later on, though). - */ - data->ap_sta_id = mvmvif->ap_sta_id; - data->vif_count++; -} - -static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, - struct iwl_wowlan_config_cmd *cmd, - struct iwl_d0i3_iter_data *iter_data) -{ - struct ieee80211_sta *ap_sta; - struct iwl_mvm_sta *mvm_ap_sta; - - if (iter_data->ap_sta_id == IWL_MVM_STATION_COUNT) - return; - - rcu_read_lock(); - - ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[iter_data->ap_sta_id]); - if (IS_ERR_OR_NULL(ap_sta)) - goto out; - - mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); - cmd->is_11n_connection = ap_sta->ht_cap.ht_supported; - cmd->offloading_tid = iter_data->offloading_tid; - - /* - * The d0i3 uCode takes care of the nonqos counters, - * so configure only the qos seq ones. - */ - iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, cmd); -out: - rcu_read_unlock(); -} - -int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; - int ret; - struct iwl_d0i3_iter_data d0i3_iter_data = { - .mvm = mvm, - }; - struct iwl_wowlan_config_cmd wowlan_config_cmd = { - .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME | - IWL_WOWLAN_WAKEUP_BEACON_MISS | - IWL_WOWLAN_WAKEUP_LINK_CHANGE | - IWL_WOWLAN_WAKEUP_BCN_FILTERING), - }; - struct iwl_d3_manager_config d3_cfg_cmd = { - .min_sleep_time = cpu_to_le32(1000), - .wakeup_flags = cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR), - }; - - IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n"); - - set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); - - /* - * iwl_mvm_ref_sync takes a reference before checking the flag. - * so by checking there is no held reference we prevent a state - * in which iwl_mvm_ref_sync continues successfully while we - * configure the firmware to enter d0i3 - */ - if (iwl_mvm_ref_taken(mvm)) { - IWL_DEBUG_RPM(mvm->trans, "abort d0i3 due to taken ref\n"); - clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); - wake_up(&mvm->d0i3_exit_waitq); - return 1; - } - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_enter_d0i3_iterator, - &d0i3_iter_data); - if (d0i3_iter_data.vif_count == 1) { - mvm->d0i3_ap_sta_id = d0i3_iter_data.ap_sta_id; - mvm->d0i3_offloading = !d0i3_iter_data.disable_offloading; - } else { - WARN_ON_ONCE(d0i3_iter_data.vif_count > 1); - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; - mvm->d0i3_offloading = false; - } - - /* make sure we have no running tx while configuring the seqno */ - synchronize_net(); - - /* configure wowlan configuration only if needed */ - if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) { - iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, - &d0i3_iter_data); - - ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags, - sizeof(wowlan_config_cmd), - &wowlan_config_cmd); - if (ret) - return ret; - } - - return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, - flags | CMD_MAKE_TRANS_IDLE, - sizeof(d3_cfg_cmd), &d3_cfg_cmd); -} - -static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = _data; - u32 flags = CMD_ASYNC | CMD_HIGH_PRIO; - - IWL_DEBUG_RPM(mvm, "exiting D0i3 - vif %pM\n", vif->addr); - if (vif->type != NL80211_IFTYPE_STATION || - !vif->bss_conf.assoc) - return; - - iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags); -} - -struct iwl_mvm_wakeup_reason_iter_data { - struct iwl_mvm *mvm; - u32 wakeup_reasons; -}; - -static void iwl_mvm_d0i3_wakeup_reason_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_wakeup_reason_iter_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc && - data->mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) { - if (data->wakeup_reasons & - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH) - iwl_mvm_connection_loss(data->mvm, vif, "D0i3"); - else - ieee80211_beacon_loss(vif); - } -} - -void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq) -{ - struct ieee80211_sta *sta = NULL; - struct iwl_mvm_sta *mvm_ap_sta; - int i; - bool wake_queues = false; - - lockdep_assert_held(&mvm->mutex); - - spin_lock_bh(&mvm->d0i3_tx_lock); - - if (mvm->d0i3_ap_sta_id == IWL_MVM_STATION_COUNT) - goto out; - - IWL_DEBUG_RPM(mvm, "re-enqueue packets\n"); - - /* get the sta in order to update seq numbers and re-enqueue skbs */ - sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id], - lockdep_is_held(&mvm->mutex)); - - if (IS_ERR_OR_NULL(sta)) { - sta = NULL; - goto out; - } - - if (mvm->d0i3_offloading && qos_seq) { - /* update qos seq numbers if offloading was enabled */ - mvm_ap_sta = iwl_mvm_sta_from_mac80211(sta); - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - u16 seq = le16_to_cpu(qos_seq[i]); - /* firmware stores last-used one, we store next one */ - seq += 0x10; - mvm_ap_sta->tid_data[i].seq_number = seq; - } - } -out: - /* re-enqueue (or drop) all packets */ - while (!skb_queue_empty(&mvm->d0i3_tx)) { - struct sk_buff *skb = __skb_dequeue(&mvm->d0i3_tx); - - if (!sta || iwl_mvm_tx_skb(mvm, skb, sta)) - ieee80211_free_txskb(mvm->hw, skb); - - /* if the skb_queue is not empty, we need to wake queues */ - wake_queues = true; - } - clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); - wake_up(&mvm->d0i3_exit_waitq); - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; - if (wake_queues) - ieee80211_wake_queues(mvm->hw); - - spin_unlock_bh(&mvm->d0i3_tx_lock); -} - -static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) -{ - struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work); - struct iwl_host_cmd get_status_cmd = { - .id = WOWLAN_GET_STATUSES, - .flags = CMD_HIGH_PRIO | CMD_WANT_SKB, - }; - struct iwl_wowlan_status *status; - int ret; - u32 handled_reasons, wakeup_reasons = 0; - __le16 *qos_seq = NULL; - - mutex_lock(&mvm->mutex); - ret = iwl_mvm_send_cmd(mvm, &get_status_cmd); - if (ret) - goto out; - - if (!get_status_cmd.resp_pkt) - goto out; - - status = (void *)get_status_cmd.resp_pkt->data; - wakeup_reasons = le32_to_cpu(status->wakeup_reasons); - qos_seq = status->qos_seq_ctr; - - IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons); - - handled_reasons = IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; - if (wakeup_reasons & handled_reasons) { - struct iwl_mvm_wakeup_reason_iter_data data = { - .mvm = mvm, - .wakeup_reasons = wakeup_reasons, - }; - - ieee80211_iterate_active_interfaces( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_d0i3_wakeup_reason_iter, &data); - } -out: - iwl_mvm_d0i3_enable_tx(mvm, qos_seq); - - IWL_DEBUG_INFO(mvm, "d0i3 exit completed (wakeup reasons: 0x%x)\n", - wakeup_reasons); - - /* qos_seq might point inside resp_pkt, so free it only now */ - if (get_status_cmd.resp_pkt) - iwl_free_resp(&get_status_cmd); - - /* the FW might have updated the regdomain */ - iwl_mvm_update_changed_regdom(mvm); - - iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK); - mutex_unlock(&mvm->mutex); -} - -int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm) -{ - u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE | - CMD_WAKE_UP_TRANS; - int ret; - - IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n"); - - mutex_lock(&mvm->d0i3_suspend_mutex); - if (test_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags)) { - IWL_DEBUG_RPM(mvm, "Deferring d0i3 exit until resume\n"); - __set_bit(D0I3_PENDING_WAKEUP, &mvm->d0i3_suspend_flags); - mutex_unlock(&mvm->d0i3_suspend_mutex); - return 0; - } - mutex_unlock(&mvm->d0i3_suspend_mutex); - - ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL); - if (ret) - goto out; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_exit_d0i3_iterator, - mvm); -out: - schedule_work(&mvm->d0i3_exit_work); - return ret; -} - -int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - iwl_mvm_ref(mvm, IWL_MVM_REF_EXIT_WORK); - return _iwl_mvm_exit_d0i3(mvm); -} - -#define IWL_MVM_COMMON_OPS \ - /* these could be differentiated */ \ - .queue_full = iwl_mvm_stop_sw_queue, \ - .queue_not_full = iwl_mvm_wake_sw_queue, \ - .hw_rf_kill = iwl_mvm_set_hw_rfkill_state, \ - .free_skb = iwl_mvm_free_skb, \ - .nic_error = iwl_mvm_nic_error, \ - .cmd_queue_full = iwl_mvm_cmd_queue_full, \ - .nic_config = iwl_mvm_nic_config, \ - .enter_d0i3 = iwl_mvm_enter_d0i3, \ - .exit_d0i3 = iwl_mvm_exit_d0i3, \ - /* as we only register one, these MUST be common! */ \ - .start = iwl_op_mode_mvm_start, \ - .stop = iwl_op_mode_mvm_stop - -static const struct iwl_op_mode_ops iwl_mvm_ops = { - IWL_MVM_COMMON_OPS, - .rx = iwl_mvm_rx, -}; - -static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode, - struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb, - unsigned int queue) -{ - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); -} - -static const struct iwl_op_mode_ops iwl_mvm_ops_mq = { - IWL_MVM_COMMON_OPS, - .rx = iwl_mvm_rx_mq, - .rx_rss = iwl_mvm_rx_mq_rss, -}; diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c deleted file mode 100644 index e68a475e3071..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ /dev/null @@ -1,295 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include "fw-api.h" -#include "mvm.h" - -/* Maps the driver specific channel width definition to the fw values */ -u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef) -{ - switch (chandef->width) { - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - return PHY_VHT_CHANNEL_MODE20; - case NL80211_CHAN_WIDTH_40: - return PHY_VHT_CHANNEL_MODE40; - case NL80211_CHAN_WIDTH_80: - return PHY_VHT_CHANNEL_MODE80; - case NL80211_CHAN_WIDTH_160: - return PHY_VHT_CHANNEL_MODE160; - default: - WARN(1, "Invalid channel width=%u", chandef->width); - return PHY_VHT_CHANNEL_MODE20; - } -} - -/* - * Maps the driver specific control channel position (relative to the center - * freq) definitions to the the fw values - */ -u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef) -{ - switch (chandef->chan->center_freq - chandef->center_freq1) { - case -70: - return PHY_VHT_CTRL_POS_4_BELOW; - case -50: - return PHY_VHT_CTRL_POS_3_BELOW; - case -30: - return PHY_VHT_CTRL_POS_2_BELOW; - case -10: - return PHY_VHT_CTRL_POS_1_BELOW; - case 10: - return PHY_VHT_CTRL_POS_1_ABOVE; - case 30: - return PHY_VHT_CTRL_POS_2_ABOVE; - case 50: - return PHY_VHT_CTRL_POS_3_ABOVE; - case 70: - return PHY_VHT_CTRL_POS_4_ABOVE; - default: - WARN(1, "Invalid channel definition"); - case 0: - /* - * The FW is expected to check the control channel position only - * when in HT/VHT and the channel width is not 20MHz. Return - * this value as the default one. - */ - return PHY_VHT_CTRL_POS_1_BELOW; - } -} - -/* - * Construct the generic fields of the PHY context command - */ -static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt, - struct iwl_phy_context_cmd *cmd, - u32 action, u32 apply_time) -{ - memset(cmd, 0, sizeof(struct iwl_phy_context_cmd)); - - cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id, - ctxt->color)); - cmd->action = cpu_to_le32(action); - cmd->apply_time = cpu_to_le32(apply_time); -} - -/* - * Add the phy configuration to the PHY context command - */ -static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm, - struct iwl_phy_context_cmd *cmd, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic) -{ - u8 active_cnt, idle_cnt; - - /* Set the channel info data */ - cmd->ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ? - PHY_BAND_24 : PHY_BAND_5); - - cmd->ci.channel = chandef->chan->hw_value; - cmd->ci.width = iwl_mvm_get_channel_width(chandef); - cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef); - - /* Set rx the chains */ - idle_cnt = chains_static; - active_cnt = chains_dynamic; - - /* In scenarios where we only ever use a single-stream rates, - * i.e. legacy 11b/g/a associations, single-stream APs or even - * static SMPS, enable both chains to get diversity, improving - * the case where we're far enough from the AP that attenuation - * between the two antennas is sufficiently different to impact - * performance. - */ - if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) { - idle_cnt = 2; - active_cnt = 2; - } - - cmd->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) << - PHY_RX_CHAIN_VALID_POS); - cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS); - cmd->rxchain_info |= cpu_to_le32(active_cnt << - PHY_RX_CHAIN_MIMO_CNT_POS); -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (unlikely(mvm->dbgfs_rx_phyinfo)) - cmd->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo); -#endif - - cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); -} - -/* - * Send a command to apply the current phy configuration. The command is send - * only if something in the configuration changed: in case that this is the - * first time that the phy configuration is applied or in case that the phy - * configuration changed from the previous apply. - */ -static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, - struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic, - u32 action, u32 apply_time) -{ - struct iwl_phy_context_cmd cmd; - int ret; - - /* Set the command header fields */ - iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time); - - /* Set the command data */ - iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef, - chains_static, chains_dynamic); - - ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0, - sizeof(struct iwl_phy_context_cmd), - &cmd); - if (ret) - IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret); - return ret; -} - -/* - * Send a command to add a PHY context based on the current HW configuration. - */ -int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic) -{ - WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && - ctxt->ref); - lockdep_assert_held(&mvm->mutex); - - ctxt->channel = chandef->chan; - - return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, - chains_static, chains_dynamic, - FW_CTXT_ACTION_ADD, 0); -} - -/* - * Update the number of references to the given PHY context. This is valid only - * in case the PHY context was already created, i.e., its reference count > 0. - */ -void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) -{ - lockdep_assert_held(&mvm->mutex); - ctxt->ref++; -} - -/* - * Send a command to modify the PHY context based on the current HW - * configuration. Note that the function does not check that the configuration - * changed. - */ -int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, - struct cfg80211_chan_def *chandef, - u8 chains_static, u8 chains_dynamic) -{ - lockdep_assert_held(&mvm->mutex); - - ctxt->channel = chandef->chan; - return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, - chains_static, chains_dynamic, - FW_CTXT_ACTION_MODIFY, 0); -} - -void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) -{ - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON_ONCE(!ctxt)) - return; - - ctxt->ref--; -} - -static void iwl_mvm_binding_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - unsigned long *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (!mvmvif->phy_ctxt) - return; - - if (vif->type == NL80211_IFTYPE_STATION || - vif->type == NL80211_IFTYPE_AP) - __set_bit(mvmvif->phy_ctxt->id, data); -} - -int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm) -{ - unsigned long phy_ctxt_counter = 0; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_binding_iterator, - &phy_ctxt_counter); - - return hweight8(phy_ctxt_counter); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c deleted file mode 100644 index bed9696ee410..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ /dev/null @@ -1,1040 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include -#include -#include - -#include - -#include "iwl-debug.h" -#include "mvm.h" -#include "iwl-modparams.h" -#include "fw-api-power.h" - -#define POWER_KEEP_ALIVE_PERIOD_SEC 25 - -static -int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, - struct iwl_beacon_filter_cmd *cmd, - u32 flags) -{ - IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", - le32_to_cpu(cmd->ba_enable_beacon_abort)); - IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", - le32_to_cpu(cmd->ba_escape_timer)); - IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", - le32_to_cpu(cmd->bf_debug_flag)); - IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", - le32_to_cpu(cmd->bf_enable_beacon_filter)); - IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", - le32_to_cpu(cmd->bf_energy_delta)); - IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", - le32_to_cpu(cmd->bf_escape_timer)); - IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", - le32_to_cpu(cmd->bf_roaming_energy_delta)); - IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", - le32_to_cpu(cmd->bf_roaming_state)); - IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n", - le32_to_cpu(cmd->bf_temp_threshold)); - IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n", - le32_to_cpu(cmd->bf_temp_fast_filter)); - IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n", - le32_to_cpu(cmd->bf_temp_slow_filter)); - - return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags, - sizeof(struct iwl_beacon_filter_cmd), cmd); -} - -static -void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_beacon_filter_cmd *cmd, - bool d0i3) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (vif->bss_conf.cqm_rssi_thold && !d0i3) { - cmd->bf_energy_delta = - cpu_to_le32(vif->bss_conf.cqm_rssi_hyst); - /* fw uses an absolute value for this */ - cmd->bf_roaming_state = - cpu_to_le32(-vif->bss_conf.cqm_rssi_thold); - } - cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled); -} - -static void iwl_mvm_power_log(struct iwl_mvm *mvm, - struct iwl_mac_power_cmd *cmd) -{ - IWL_DEBUG_POWER(mvm, - "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n", - cmd->id_and_color, iwlmvm_mod_params.power_scheme, - le16_to_cpu(cmd->flags)); - IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", - le16_to_cpu(cmd->keep_alive_seconds)); - - if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) { - IWL_DEBUG_POWER(mvm, "Disable power management\n"); - return; - } - - IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n", - le32_to_cpu(cmd->rx_data_timeout)); - IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n", - le32_to_cpu(cmd->tx_data_timeout)); - if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) - IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", - cmd->skip_dtim_periods); - if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) - IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", - cmd->lprx_rssi_threshold); - if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { - IWL_DEBUG_POWER(mvm, "uAPSD enabled\n"); - IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n", - le32_to_cpu(cmd->rx_data_timeout_uapsd)); - IWL_DEBUG_POWER(mvm, "Tx timeout (uAPSD) = %u usec\n", - le32_to_cpu(cmd->tx_data_timeout_uapsd)); - IWL_DEBUG_POWER(mvm, "QNDP TID = %d\n", cmd->qndp_tid); - IWL_DEBUG_POWER(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags); - IWL_DEBUG_POWER(mvm, "Max SP = %d\n", cmd->uapsd_max_sp); - } -} - -static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_power_cmd *cmd) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - enum ieee80211_ac_numbers ac; - bool tid_found = false; - - for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) { - if (!mvmvif->queue_params[ac].uapsd) - continue; - - if (mvm->cur_ucode != IWL_UCODE_WOWLAN) - cmd->flags |= - cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); - - cmd->uapsd_ac_flags |= BIT(ac); - - /* QNDP TID - the highest TID with no admission control */ - if (!tid_found && !mvmvif->queue_params[ac].acm) { - tid_found = true; - switch (ac) { - case IEEE80211_AC_VO: - cmd->qndp_tid = 6; - break; - case IEEE80211_AC_VI: - cmd->qndp_tid = 5; - break; - case IEEE80211_AC_BE: - cmd->qndp_tid = 0; - break; - case IEEE80211_AC_BK: - cmd->qndp_tid = 1; - break; - } - } - } - - if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { -#ifdef CONFIG_IWLWIFI_DEBUGFS - /* set advanced pm flag with no uapsd ACs to enable ps-poll */ - if (mvmvif->dbgfs_pm.use_ps_poll) - cmd->flags |= - cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); -#endif - return; - } - - cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK); - - if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | - BIT(IEEE80211_AC_VI) | - BIT(IEEE80211_AC_BE) | - BIT(IEEE80211_AC_BK))) { - cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); - cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL); - cmd->snooze_window = (mvm->cur_ucode == IWL_UCODE_WOWLAN) ? - cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) : - cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW); - } - - cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP; - - if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags & - cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { - cmd->rx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); - cmd->tx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); - } else { - cmd->rx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT); - cmd->tx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT); - } - - if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { - cmd->heavy_tx_thld_packets = - IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS; - cmd->heavy_rx_thld_packets = - IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS; - } else { - cmd->heavy_tx_thld_packets = - IWL_MVM_PS_HEAVY_TX_THLD_PACKETS; - cmd->heavy_rx_thld_packets = - IWL_MVM_PS_HEAVY_RX_THLD_PACKETS; - } - cmd->heavy_tx_thld_percentage = - IWL_MVM_PS_HEAVY_TX_THLD_PERCENT; - cmd->heavy_rx_thld_percentage = - IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; -} - -static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, - ETH_ALEN)) - return false; - - if (vif->p2p && - !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD)) - return false; - /* - * Avoid using uAPSD if P2P client is associated to GO that uses - * opportunistic power save. This is due to current FW limitation. - */ - if (vif->p2p && - (vif->bss_conf.p2p_noa_attr.oppps_ctwindow & - IEEE80211_P2P_OPPPS_ENABLE_BIT)) - return false; - - /* - * Avoid using uAPSD if client is in DCM - - * low latency issue in Miracast - */ - if (iwl_mvm_phy_ctx_count(mvm) >= 2) - return false; - - return true; -} - -static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif) -{ - struct ieee80211_chanctx_conf *chanctx_conf; - struct ieee80211_channel *chan; - bool radar_detect = false; - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - WARN_ON(!chanctx_conf); - if (chanctx_conf) { - chan = chanctx_conf->def.chan; - radar_detect = chan->flags & IEEE80211_CHAN_RADAR; - } - rcu_read_unlock(); - - return radar_detect; -} - -static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_power_cmd *cmd, - bool host_awake) -{ - int dtimper = vif->bss_conf.dtim_period ?: 1; - int skip; - - /* disable, in case we're supposed to override */ - cmd->skip_dtim_periods = 0; - cmd->flags &= ~cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); - - if (iwl_mvm_power_is_radar(vif)) - return; - - if (dtimper >= 10) - return; - - /* TODO: check that multicast wake lock is off */ - - if (host_awake) { - if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_LP) - return; - skip = 2; - } else { - int dtimper_tu = dtimper * vif->bss_conf.beacon_int; - - if (WARN_ON(!dtimper_tu)) - return; - /* configure skip over dtim up to 306TU - 314 msec */ - skip = max_t(u8, 1, 306 / dtimper_tu); - } - - /* the firmware really expects "look at every X DTIMs", so add 1 */ - cmd->skip_dtim_periods = 1 + skip; - cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); -} - -static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_power_cmd *cmd, - bool host_awake) -{ - int dtimper, bi; - int keep_alive; - struct iwl_mvm_vif *mvmvif __maybe_unused = - iwl_mvm_vif_from_mac80211(vif); - - cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)); - dtimper = vif->bss_conf.dtim_period; - bi = vif->bss_conf.beacon_int; - - /* - * Regardless of power management state the driver must set - * keep alive period. FW will use it for sending keep alive NDPs - * immediately after association. Check that keep alive period - * is at least 3 * DTIM - */ - keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi), - USEC_PER_SEC); - keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC); - cmd->keep_alive_seconds = cpu_to_le16(keep_alive); - - if (mvm->ps_disabled) - return; - - cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); - - if (!vif->bss_conf.ps || !mvmvif->pm_enabled) - return; - - if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p && - (!fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS) || - !IWL_MVM_P2P_LOWLATENCY_PS_ENABLE)) - return; - - cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); - - if (vif->bss_conf.beacon_rate && - (vif->bss_conf.beacon_rate->bitrate == 10 || - vif->bss_conf.beacon_rate->bitrate == 60)) { - cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); - cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD; - } - - iwl_mvm_power_config_skip_dtim(mvm, vif, cmd, host_awake); - - if (!host_awake) { - cmd->rx_data_timeout = - cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); - cmd->tx_data_timeout = - cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); - } else if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p && - fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS)) { - cmd->tx_data_timeout = - cpu_to_le32(IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT); - cmd->rx_data_timeout = - cpu_to_le32(IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT); - } else { - cmd->rx_data_timeout = - cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT); - cmd->tx_data_timeout = - cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT); - } - - if (iwl_mvm_power_allow_uapsd(mvm, vif)) - iwl_mvm_power_configure_uapsd(mvm, vif, cmd); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) - cmd->keep_alive_seconds = - cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds); - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) { - if (mvmvif->dbgfs_pm.skip_over_dtim) - cmd->flags |= - cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); - else - cmd->flags &= - cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK); - } - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT) - cmd->rx_data_timeout = - cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout); - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT) - cmd->tx_data_timeout = - cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout); - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS) - cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods; - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) { - if (mvmvif->dbgfs_pm.lprx_ena) - cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); - else - cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK); - } - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD) - cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold; - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) { - if (mvmvif->dbgfs_pm.snooze_ena) - cmd->flags |= - cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); - else - cmd->flags &= - cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK); - } - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) { - u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK; - if (mvmvif->dbgfs_pm.uapsd_misbehaving) - cmd->flags |= cpu_to_le16(flag); - else - cmd->flags &= cpu_to_le16(flag); - } -#endif /* CONFIG_IWLWIFI_DEBUGFS */ -} - -static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mac_power_cmd cmd = {}; - - iwl_mvm_power_build_cmd(mvm, vif, &cmd, - mvm->cur_ucode != IWL_UCODE_WOWLAN); - iwl_mvm_power_log(mvm, &cmd); -#ifdef CONFIG_IWLWIFI_DEBUGFS - memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd)); -#endif - - return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0, - sizeof(cmd), &cmd); -} - -int iwl_mvm_power_update_device(struct iwl_mvm *mvm) -{ - struct iwl_device_power_cmd cmd = { - .flags = 0, - }; - - if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) - mvm->ps_disabled = true; - - if (!mvm->ps_disabled) - cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 : - mvm->disable_power_off) - cmd.flags &= - cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); -#endif - IWL_DEBUG_POWER(mvm, - "Sending device power command with flags = 0x%X\n", - cmd.flags); - - return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd), - &cmd); -} - -void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid, - ETH_ALEN)) - eth_zero_addr(mvmvif->uapsd_misbehaving_bssid); -} - -static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - u8 *ap_sta_id = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - /* The ap_sta_id is not expected to change during current association - * so no explicit protection is needed - */ - if (mvmvif->ap_sta_id == *ap_sta_id) - memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, - ETH_ALEN); -} - -void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data; - u8 ap_sta_id = le32_to_cpu(notif->sta_id); - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id); -} - -struct iwl_power_vifs { - struct iwl_mvm *mvm; - struct ieee80211_vif *bf_vif; - struct ieee80211_vif *bss_vif; - struct ieee80211_vif *p2p_vif; - struct ieee80211_vif *ap_vif; - struct ieee80211_vif *monitor_vif; - bool p2p_active; - bool bss_active; - bool ap_active; - bool monitor_active; -}; - -static void iwl_mvm_power_disable_pm_iterator(void *_data, u8* mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - mvmvif->pm_enabled = false; -} - -static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8* mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - bool *disable_ps = _data; - - if (mvmvif->phy_ctxt) - if (mvmvif->phy_ctxt->id < MAX_PHYS) - *disable_ps |= mvmvif->ps_disabled; -} - -static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_power_vifs *power_iterator = _data; - - switch (ieee80211_vif_type_p2p(vif)) { - case NL80211_IFTYPE_P2P_DEVICE: - break; - - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_AP: - /* only a single MAC of the same type */ - WARN_ON(power_iterator->ap_vif); - power_iterator->ap_vif = vif; - if (mvmvif->phy_ctxt) - if (mvmvif->phy_ctxt->id < MAX_PHYS) - power_iterator->ap_active = true; - break; - - case NL80211_IFTYPE_MONITOR: - /* only a single MAC of the same type */ - WARN_ON(power_iterator->monitor_vif); - power_iterator->monitor_vif = vif; - if (mvmvif->phy_ctxt) - if (mvmvif->phy_ctxt->id < MAX_PHYS) - power_iterator->monitor_active = true; - break; - - case NL80211_IFTYPE_P2P_CLIENT: - /* only a single MAC of the same type */ - WARN_ON(power_iterator->p2p_vif); - power_iterator->p2p_vif = vif; - if (mvmvif->phy_ctxt) - if (mvmvif->phy_ctxt->id < MAX_PHYS) - power_iterator->p2p_active = true; - break; - - case NL80211_IFTYPE_STATION: - /* only a single MAC of the same type */ - WARN_ON(power_iterator->bss_vif); - power_iterator->bss_vif = vif; - if (mvmvif->phy_ctxt) - if (mvmvif->phy_ctxt->id < MAX_PHYS) - power_iterator->bss_active = true; - - if (mvmvif->bf_data.bf_enabled && - !WARN_ON(power_iterator->bf_vif)) - power_iterator->bf_vif = vif; - - break; - - default: - break; - } -} - -static void iwl_mvm_power_set_pm(struct iwl_mvm *mvm, - struct iwl_power_vifs *vifs) -{ - struct iwl_mvm_vif *bss_mvmvif = NULL; - struct iwl_mvm_vif *p2p_mvmvif = NULL; - struct iwl_mvm_vif *ap_mvmvif = NULL; - bool client_same_channel = false; - bool ap_same_channel = false; - - lockdep_assert_held(&mvm->mutex); - - /* set pm_enable to false */ - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_disable_pm_iterator, - NULL); - - if (vifs->bss_vif) - bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif); - - if (vifs->p2p_vif) - p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif); - - if (vifs->ap_vif) - ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif); - - /* don't allow PM if any TDLS stations exist */ - if (iwl_mvm_tdls_sta_count(mvm, NULL)) - return; - - /* enable PM on bss if bss stand alone */ - if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) { - bss_mvmvif->pm_enabled = true; - return; - } - - /* enable PM on p2p if p2p stand alone */ - if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) { - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM) - p2p_mvmvif->pm_enabled = true; - return; - } - - if (vifs->bss_active && vifs->p2p_active) - client_same_channel = (bss_mvmvif->phy_ctxt->id == - p2p_mvmvif->phy_ctxt->id); - if (vifs->bss_active && vifs->ap_active) - ap_same_channel = (bss_mvmvif->phy_ctxt->id == - ap_mvmvif->phy_ctxt->id); - - /* clients are not stand alone: enable PM if DCM */ - if (!(client_same_channel || ap_same_channel) && - (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) { - if (vifs->bss_active) - bss_mvmvif->pm_enabled = true; - if (vifs->p2p_active && - (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)) - p2p_mvmvif->pm_enabled = true; - return; - } - - /* - * There is only one channel in the system and there are only - * bss and p2p clients that share it - */ - if (client_same_channel && !vifs->ap_active && - (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM)) { - /* share same channel*/ - bss_mvmvif->pm_enabled = true; - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM) - p2p_mvmvif->pm_enabled = true; - } -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, char *buf, - int bufsz) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_power_cmd cmd = {}; - int pos = 0; - - mutex_lock(&mvm->mutex); - memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd)); - mutex_unlock(&mvm->mutex); - - pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", - iwlmvm_mod_params.power_scheme); - pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n", - le16_to_cpu(cmd.flags)); - pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", - le16_to_cpu(cmd.keep_alive_seconds)); - - if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) - return pos; - - pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0); - pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", - cmd.skip_dtim_periods); - if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { - pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", - le32_to_cpu(cmd.rx_data_timeout)); - pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", - le32_to_cpu(cmd.tx_data_timeout)); - } - if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) - pos += scnprintf(buf+pos, bufsz-pos, - "lprx_rssi_threshold = %d\n", - cmd.lprx_rssi_threshold); - - if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) - return pos; - - pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n", - le32_to_cpu(cmd.rx_data_timeout_uapsd)); - pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n", - le32_to_cpu(cmd.tx_data_timeout_uapsd)); - pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid); - pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n", - cmd.uapsd_ac_flags); - pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n", - cmd.uapsd_max_sp); - pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n", - cmd.heavy_tx_thld_packets); - pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n", - cmd.heavy_rx_thld_packets); - pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n", - cmd.heavy_tx_thld_percentage); - pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n", - cmd.heavy_rx_thld_percentage); - pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ? - 1 : 0); - - if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK))) - return pos; - - pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n", - cmd.snooze_interval); - pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n", - cmd.snooze_window); - - return pos; -} - -void -iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, - struct iwl_beacon_filter_cmd *cmd) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; - - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA) - cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA) - cmd->bf_roaming_energy_delta = - cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE) - cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD) - cmd->bf_temp_threshold = - cpu_to_le32(dbgfs_bf->bf_temp_threshold); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER) - cmd->bf_temp_fast_filter = - cpu_to_le32(dbgfs_bf->bf_temp_fast_filter); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER) - cmd->bf_temp_slow_filter = - cpu_to_le32(dbgfs_bf->bf_temp_slow_filter); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG) - cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag); - if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER) - cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer); - if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER) - cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer); - if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT) - cmd->ba_enable_beacon_abort = - cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort); -} -#endif - -static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_beacon_filter_cmd *cmd, - u32 cmd_flags, - bool d0i3) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period || - vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return 0; - - iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd, d0i3); - if (!d0i3) - iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd); - ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags); - - /* don't change bf_enabled in case of temporary d0i3 configuration */ - if (!ret && !d0i3) - mvmvif->bf_data.bf_enabled = true; - - return ret; -} - -int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags) -{ - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = cpu_to_le32(1), - }; - - return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false); -} - -static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool enable) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = cpu_to_le32(1), - }; - - if (!mvmvif->bf_data.bf_enabled) - return 0; - - if (mvm->cur_ucode == IWL_UCODE_WOWLAN) - cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); - - mvmvif->bf_data.ba_enabled = enable; - return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false); -} - -int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags) -{ - struct iwl_beacon_filter_cmd cmd = {}; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return 0; - - ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags); - - if (!ret) - mvmvif->bf_data.bf_enabled = false; - - return ret; -} - -static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm) -{ - bool disable_ps; - int ret; - - /* disable PS if CAM */ - disable_ps = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM); - /* ...or if any of the vifs require PS to be off */ - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_ps_disabled_iterator, - &disable_ps); - - /* update device power state if it has changed */ - if (mvm->ps_disabled != disable_ps) { - bool old_ps_disabled = mvm->ps_disabled; - - mvm->ps_disabled = disable_ps; - ret = iwl_mvm_power_update_device(mvm); - if (ret) { - mvm->ps_disabled = old_ps_disabled; - return ret; - } - } - - return 0; -} - -static int iwl_mvm_power_set_ba(struct iwl_mvm *mvm, - struct iwl_power_vifs *vifs) -{ - struct iwl_mvm_vif *mvmvif; - bool ba_enable; - - if (!vifs->bf_vif) - return 0; - - mvmvif = iwl_mvm_vif_from_mac80211(vifs->bf_vif); - - ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled || - !vifs->bf_vif->bss_conf.ps || - iwl_mvm_vif_low_latency(mvmvif)); - - return iwl_mvm_update_beacon_abort(mvm, vifs->bf_vif, ba_enable); -} - -int iwl_mvm_power_update_ps(struct iwl_mvm *mvm) -{ - struct iwl_power_vifs vifs = { - .mvm = mvm, - }; - int ret; - - lockdep_assert_held(&mvm->mutex); - - /* get vifs info */ - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_get_vifs_iterator, &vifs); - - ret = iwl_mvm_power_set_ps(mvm); - if (ret) - return ret; - - return iwl_mvm_power_set_ba(mvm, &vifs); -} - -int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) -{ - struct iwl_power_vifs vifs = { - .mvm = mvm, - }; - int ret; - - lockdep_assert_held(&mvm->mutex); - - /* get vifs info */ - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_get_vifs_iterator, &vifs); - - iwl_mvm_power_set_pm(mvm, &vifs); - - ret = iwl_mvm_power_set_ps(mvm); - if (ret) - return ret; - - if (vifs.bss_vif) { - ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif); - if (ret) - return ret; - } - - if (vifs.p2p_vif) { - ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif); - if (ret) - return ret; - } - - return iwl_mvm_power_set_ba(mvm, &vifs); -} - -int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool enable, u32 flags) -{ - int ret; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mac_power_cmd cmd = {}; - - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return 0; - - if (!vif->bss_conf.assoc) - return 0; - - iwl_mvm_power_build_cmd(mvm, vif, &cmd, !enable); - - iwl_mvm_power_log(mvm, &cmd); -#ifdef CONFIG_IWLWIFI_DEBUGFS - memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd)); -#endif - ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, flags, - sizeof(cmd), &cmd); - if (ret) - return ret; - - /* configure beacon filtering */ - if (mvmvif != mvm->bf_allowed_vif) - return 0; - - if (enable) { - struct iwl_beacon_filter_cmd cmd_bf = { - IWL_BF_CMD_CONFIG_D0I3, - .bf_enable_beacon_filter = cpu_to_le32(1), - }; - ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf, - flags, true); - } else { - if (mvmvif->bf_data.bf_enabled) - ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags); - else - ret = iwl_mvm_disable_beacon_filter(mvm, vif, flags); - } - - return ret; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c deleted file mode 100644 index 509a66d05245..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ /dev/null @@ -1,328 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include "fw-api.h" -#include "mvm.h" - -#define QUOTA_100 IWL_MVM_MAX_QUOTA -#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100) - -struct iwl_mvm_quota_iterator_data { - int n_interfaces[MAX_BINDINGS]; - int colors[MAX_BINDINGS]; - int low_latency[MAX_BINDINGS]; - int n_low_latency_bindings; - struct ieee80211_vif *disabled_vif; -}; - -static void iwl_mvm_quota_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_quota_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u16 id; - - /* skip disabled interfaces here immediately */ - if (vif == data->disabled_vif) - return; - - if (!mvmvif->phy_ctxt) - return; - - /* currently, PHY ID == binding ID */ - id = mvmvif->phy_ctxt->id; - - /* need at least one binding per PHY */ - BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS); - - if (WARN_ON_ONCE(id >= MAX_BINDINGS)) - return; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - if (vif->bss_conf.assoc) - break; - return; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_ADHOC: - if (mvmvif->ap_ibss_active) - break; - return; - case NL80211_IFTYPE_MONITOR: - if (mvmvif->monitor_active) - break; - return; - case NL80211_IFTYPE_P2P_DEVICE: - return; - default: - WARN_ON_ONCE(1); - return; - } - - if (data->colors[id] < 0) - data->colors[id] = mvmvif->phy_ctxt->color; - else - WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color); - - data->n_interfaces[id]++; - - if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) { - data->n_low_latency_bindings++; - data->low_latency[id] = true; - } -} - -static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm, - struct iwl_time_quota_cmd *cmd) -{ -#ifdef CONFIG_NL80211_TESTMODE - struct iwl_mvm_vif *mvmvif; - int i, phy_id = -1, beacon_int = 0; - - if (!mvm->noa_duration || !mvm->noa_vif) - return; - - mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif); - if (!mvmvif->ap_ibss_active) - return; - - phy_id = mvmvif->phy_ctxt->id; - beacon_int = mvm->noa_vif->bss_conf.beacon_int; - - for (i = 0; i < MAX_BINDINGS; i++) { - u32 id_n_c = le32_to_cpu(cmd->quotas[i].id_and_color); - u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS; - u32 quota = le32_to_cpu(cmd->quotas[i].quota); - - if (id != phy_id) - continue; - - quota *= (beacon_int - mvm->noa_duration); - quota /= beacon_int; - - IWL_DEBUG_QUOTA(mvm, "quota: adjust for NoA from %d to %d\n", - le32_to_cpu(cmd->quotas[i].quota), quota); - - cmd->quotas[i].quota = cpu_to_le32(quota); - } -#endif -} - -int iwl_mvm_update_quotas(struct iwl_mvm *mvm, - bool force_update, - struct ieee80211_vif *disabled_vif) -{ - struct iwl_time_quota_cmd cmd = {}; - int i, idx, err, num_active_macs, quota, quota_rem, n_non_lowlat; - struct iwl_mvm_quota_iterator_data data = { - .n_interfaces = {}, - .colors = { -1, -1, -1, -1 }, - .disabled_vif = disabled_vif, - }; - struct iwl_time_quota_cmd *last = &mvm->last_quota_cmd; - bool send = false; - - lockdep_assert_held(&mvm->mutex); - - /* update all upon completion */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - return 0; - - /* iterator data above must match */ - BUILD_BUG_ON(MAX_BINDINGS != 4); - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_quota_iterator, &data); - - /* - * The FW's scheduling session consists of - * IWL_MVM_MAX_QUOTA fragments. Divide these fragments - * equally between all the bindings that require quota - */ - num_active_macs = 0; - for (i = 0; i < MAX_BINDINGS; i++) { - cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID); - num_active_macs += data.n_interfaces[i]; - } - - n_non_lowlat = num_active_macs; - - if (data.n_low_latency_bindings == 1) { - for (i = 0; i < MAX_BINDINGS; i++) { - if (data.low_latency[i]) { - n_non_lowlat -= data.n_interfaces[i]; - break; - } - } - } - - if (data.n_low_latency_bindings == 1 && n_non_lowlat) { - /* - * Reserve quota for the low latency binding in case that - * there are several data bindings but only a single - * low latency one. Split the rest of the quota equally - * between the other data interfaces. - */ - quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat; - quota_rem = QUOTA_100 - n_non_lowlat * quota - - QUOTA_LOWLAT_MIN; - IWL_DEBUG_QUOTA(mvm, - "quota: low-latency binding active, remaining quota per other binding: %d\n", - quota); - } else if (num_active_macs) { - /* - * There are 0 or more than 1 low latency bindings, or all the - * data interfaces belong to the single low latency binding. - * Split the quota equally between the data interfaces. - */ - quota = QUOTA_100 / num_active_macs; - quota_rem = QUOTA_100 % num_active_macs; - IWL_DEBUG_QUOTA(mvm, - "quota: splitting evenly per binding: %d\n", - quota); - } else { - /* values don't really matter - won't be used */ - quota = 0; - quota_rem = 0; - } - - for (idx = 0, i = 0; i < MAX_BINDINGS; i++) { - if (data.colors[i] < 0) - continue; - - cmd.quotas[idx].id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i])); - - if (data.n_interfaces[i] <= 0) - cmd.quotas[idx].quota = cpu_to_le32(0); - else if (data.n_low_latency_bindings == 1 && n_non_lowlat && - data.low_latency[i]) - /* - * There is more than one binding, but only one of the - * bindings is in low latency. For this case, allocate - * the minimal required quota for the low latency - * binding. - */ - cmd.quotas[idx].quota = cpu_to_le32(QUOTA_LOWLAT_MIN); - else - cmd.quotas[idx].quota = - cpu_to_le32(quota * data.n_interfaces[i]); - - WARN_ONCE(le32_to_cpu(cmd.quotas[idx].quota) > QUOTA_100, - "Binding=%d, quota=%u > max=%u\n", - idx, le32_to_cpu(cmd.quotas[idx].quota), QUOTA_100); - - cmd.quotas[idx].max_duration = cpu_to_le32(0); - - idx++; - } - - /* Give the remainder of the session to the first data binding */ - for (i = 0; i < MAX_BINDINGS; i++) { - if (le32_to_cpu(cmd.quotas[i].quota) != 0) { - le32_add_cpu(&cmd.quotas[i].quota, quota_rem); - IWL_DEBUG_QUOTA(mvm, - "quota: giving remainder of %d to binding %d\n", - quota_rem, i); - break; - } - } - - iwl_mvm_adjust_quota_for_noa(mvm, &cmd); - - /* check that we have non-zero quota for all valid bindings */ - for (i = 0; i < MAX_BINDINGS; i++) { - if (cmd.quotas[i].id_and_color != last->quotas[i].id_and_color) - send = true; - if (cmd.quotas[i].max_duration != last->quotas[i].max_duration) - send = true; - if (abs((int)le32_to_cpu(cmd.quotas[i].quota) - - (int)le32_to_cpu(last->quotas[i].quota)) - > IWL_MVM_QUOTA_THRESHOLD) - send = true; - if (cmd.quotas[i].id_and_color == cpu_to_le32(FW_CTXT_INVALID)) - continue; - WARN_ONCE(cmd.quotas[i].quota == 0, - "zero quota on binding %d\n", i); - } - - if (!send && !force_update) { - /* don't send a practically unchanged command, the firmware has - * to re-initialize a lot of state and that can have an adverse - * impact on it - */ - return 0; - } - - err = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, sizeof(cmd), &cmd); - - if (err) - IWL_ERR(mvm, "Failed to send quota: %d\n", err); - else - mvm->last_quota_cmd = cmd; - return err; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c deleted file mode 100644 index d1ad10391b47..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ /dev/null @@ -1,3983 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include -#include - -#include -#include -#include - -#include -#include "rs.h" -#include "fw-api.h" -#include "sta.h" -#include "iwl-op-mode.h" -#include "mvm.h" -#include "debugfs.h" - -#define RS_NAME "iwl-mvm-rs" - -#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ - -/* Calculations of success ratio are done in fixed point where 12800 is 100%. - * Use this macro when dealing with thresholds consts set as a percentage - */ -#define RS_PERCENT(x) (128 * x) - -static u8 rs_ht_to_legacy[] = { - [IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_MCS_1_INDEX] = IWL_RATE_9M_INDEX, - [IWL_RATE_MCS_2_INDEX] = IWL_RATE_12M_INDEX, - [IWL_RATE_MCS_3_INDEX] = IWL_RATE_18M_INDEX, - [IWL_RATE_MCS_4_INDEX] = IWL_RATE_24M_INDEX, - [IWL_RATE_MCS_5_INDEX] = IWL_RATE_36M_INDEX, - [IWL_RATE_MCS_6_INDEX] = IWL_RATE_48M_INDEX, - [IWL_RATE_MCS_7_INDEX] = IWL_RATE_54M_INDEX, - [IWL_RATE_MCS_8_INDEX] = IWL_RATE_54M_INDEX, - [IWL_RATE_MCS_9_INDEX] = IWL_RATE_54M_INDEX, -}; - -static const u8 ant_toggle_lookup[] = { - [ANT_NONE] = ANT_NONE, - [ANT_A] = ANT_B, - [ANT_B] = ANT_C, - [ANT_AB] = ANT_BC, - [ANT_C] = ANT_A, - [ANT_AC] = ANT_AB, - [ANT_BC] = ANT_AC, - [ANT_ABC] = ANT_ABC, -}; - -#define IWL_DECLARE_RATE_INFO(r, s, rp, rn) \ - [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ - IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ - IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ - IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ - IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP,\ - IWL_RATE_##rp##M_INDEX, \ - IWL_RATE_##rn##M_INDEX } - -#define IWL_DECLARE_MCS_RATE(s) \ - [IWL_RATE_MCS_##s##_INDEX] = { IWL_RATE_INVM_PLCP, \ - IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ - IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ - IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ - IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP, \ - IWL_RATE_INVM_INDEX, \ - IWL_RATE_INVM_INDEX } - -/* - * Parameter order: - * rate, ht rate, prev rate, next rate - * - * If there isn't a valid next or previous rate then INV is used which - * maps to IWL_RATE_INVALID - * - */ -static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = { - IWL_DECLARE_RATE_INFO(1, INV, INV, 2), /* 1mbps */ - IWL_DECLARE_RATE_INFO(2, INV, 1, 5), /* 2mbps */ - IWL_DECLARE_RATE_INFO(5, INV, 2, 11), /*5.5mbps */ - IWL_DECLARE_RATE_INFO(11, INV, 9, 12), /* 11mbps */ - IWL_DECLARE_RATE_INFO(6, 0, 5, 11), /* 6mbps ; MCS 0 */ - IWL_DECLARE_RATE_INFO(9, INV, 6, 11), /* 9mbps */ - IWL_DECLARE_RATE_INFO(12, 1, 11, 18), /* 12mbps ; MCS 1 */ - IWL_DECLARE_RATE_INFO(18, 2, 12, 24), /* 18mbps ; MCS 2 */ - IWL_DECLARE_RATE_INFO(24, 3, 18, 36), /* 24mbps ; MCS 3 */ - IWL_DECLARE_RATE_INFO(36, 4, 24, 48), /* 36mbps ; MCS 4 */ - IWL_DECLARE_RATE_INFO(48, 5, 36, 54), /* 48mbps ; MCS 5 */ - IWL_DECLARE_RATE_INFO(54, 6, 48, INV), /* 54mbps ; MCS 6 */ - IWL_DECLARE_MCS_RATE(7), /* MCS 7 */ - IWL_DECLARE_MCS_RATE(8), /* MCS 8 */ - IWL_DECLARE_MCS_RATE(9), /* MCS 9 */ -}; - -enum rs_action { - RS_ACTION_STAY = 0, - RS_ACTION_DOWNSCALE = -1, - RS_ACTION_UPSCALE = 1, -}; - -enum rs_column_mode { - RS_INVALID = 0, - RS_LEGACY, - RS_SISO, - RS_MIMO2, -}; - -#define MAX_NEXT_COLUMNS 7 -#define MAX_COLUMN_CHECKS 3 - -struct rs_tx_column; - -typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct rs_rate *rate, - const struct rs_tx_column *next_col); - -struct rs_tx_column { - enum rs_column_mode mode; - u8 ant; - bool sgi; - enum rs_column next_columns[MAX_NEXT_COLUMNS]; - allow_column_func_t checks[MAX_COLUMN_CHECKS]; -}; - -static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct rs_rate *rate, - const struct rs_tx_column *next_col) -{ - return iwl_mvm_bt_coex_is_ant_avail(mvm, next_col->ant); -} - -static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct rs_rate *rate, - const struct rs_tx_column *next_col) -{ - struct iwl_mvm_sta *mvmsta; - struct iwl_mvm_vif *mvmvif; - - if (!sta->ht_cap.ht_supported) - return false; - - if (sta->smps_mode == IEEE80211_SMPS_STATIC) - return false; - - if (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) < 2) - return false; - - if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) - return false; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - - if (mvm->nvm_data->sku_cap_mimo_disabled) - return false; - - return true; -} - -static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct rs_rate *rate, - const struct rs_tx_column *next_col) -{ - if (!sta->ht_cap.ht_supported) - return false; - - return true; -} - -static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct rs_rate *rate, - const struct rs_tx_column *next_col) -{ - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; - - if (is_ht20(rate) && (ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - return true; - if (is_ht40(rate) && (ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - return true; - if (is_ht80(rate) && (vht_cap->cap & - IEEE80211_VHT_CAP_SHORT_GI_80)) - return true; - - return false; -} - -static const struct rs_tx_column rs_tx_columns[] = { - [RS_COLUMN_LEGACY_ANT_A] = { - .mode = RS_LEGACY, - .ant = ANT_A, - .next_columns = { - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_MIMO2, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_ant_allow, - }, - }, - [RS_COLUMN_LEGACY_ANT_B] = { - .mode = RS_LEGACY, - .ant = ANT_B, - .next_columns = { - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_SISO_ANT_B, - RS_COLUMN_MIMO2, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_ant_allow, - }, - }, - [RS_COLUMN_SISO_ANT_A] = { - .mode = RS_SISO, - .ant = ANT_A, - .next_columns = { - RS_COLUMN_SISO_ANT_B, - RS_COLUMN_MIMO2, - RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_siso_allow, - rs_ant_allow, - }, - }, - [RS_COLUMN_SISO_ANT_B] = { - .mode = RS_SISO, - .ant = ANT_B, - .next_columns = { - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_MIMO2, - RS_COLUMN_SISO_ANT_B_SGI, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_siso_allow, - rs_ant_allow, - }, - }, - [RS_COLUMN_SISO_ANT_A_SGI] = { - .mode = RS_SISO, - .ant = ANT_A, - .sgi = true, - .next_columns = { - RS_COLUMN_SISO_ANT_B_SGI, - RS_COLUMN_MIMO2_SGI, - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_siso_allow, - rs_ant_allow, - rs_sgi_allow, - }, - }, - [RS_COLUMN_SISO_ANT_B_SGI] = { - .mode = RS_SISO, - .ant = ANT_B, - .sgi = true, - .next_columns = { - RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_MIMO2_SGI, - RS_COLUMN_SISO_ANT_B, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_siso_allow, - rs_ant_allow, - rs_sgi_allow, - }, - }, - [RS_COLUMN_MIMO2] = { - .mode = RS_MIMO2, - .ant = ANT_AB, - .next_columns = { - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_MIMO2_SGI, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_mimo_allow, - }, - }, - [RS_COLUMN_MIMO2_SGI] = { - .mode = RS_MIMO2, - .ant = ANT_AB, - .sgi = true, - .next_columns = { - RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_MIMO2, - RS_COLUMN_LEGACY_ANT_A, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - RS_COLUMN_INVALID, - }, - .checks = { - rs_mimo_allow, - rs_sgi_allow, - }, - }, -}; - -static inline u8 rs_extract_rate(u32 rate_n_flags) -{ - /* also works for HT because bits 7:6 are zero there */ - return (u8)(rate_n_flags & RATE_LEGACY_RATE_MSK); -} - -static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) -{ - int idx = 0; - - if (rate_n_flags & RATE_MCS_HT_MSK) { - idx = rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK; - idx += IWL_RATE_MCS_0_INDEX; - - /* skip 9M not supported in HT*/ - if (idx >= IWL_RATE_9M_INDEX) - idx += 1; - if ((idx >= IWL_FIRST_HT_RATE) && (idx <= IWL_LAST_HT_RATE)) - return idx; - } else if (rate_n_flags & RATE_MCS_VHT_MSK) { - idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; - idx += IWL_RATE_MCS_0_INDEX; - - /* skip 9M not supported in VHT*/ - if (idx >= IWL_RATE_9M_INDEX) - idx++; - if ((idx >= IWL_FIRST_VHT_RATE) && (idx <= IWL_LAST_VHT_RATE)) - return idx; - } else { - /* legacy rate format, search for match in table */ - - u8 legacy_rate = rs_extract_rate(rate_n_flags); - for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) - if (iwl_rates[idx].plcp == legacy_rate) - return idx; - } - - return IWL_RATE_INVALID; -} - -static void rs_rate_scale_perform(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - int tid); -static void rs_fill_lq_cmd(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - const struct rs_rate *initial_rate); -static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); - -/** - * The following tables contain the expected throughput metrics for all rates - * - * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits - * - * where invalid entries are zeros. - * - * CCK rates are only valid in legacy table and will only be used in G - * (2.4 GHz) band. - */ - -static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = { - 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0 -}; - -/* Expected TpT tables. 4 indexes: - * 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI - */ -static const u16 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202, 216, 0}, - {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210, 225, 0}, - {0, 0, 0, 0, 49, 0, 97, 145, 192, 285, 375, 420, 464, 551, 0}, - {0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0}, -}; - -static const u16 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257, 269, 275}, - {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264, 275, 280}, - {0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828, 911, 1070, 1173}, - {0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284}, -}; - -static const u16 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 130, 0, 191, 223, 244, 273, 288, 294, 298, 305, 308}, - {0, 0, 0, 0, 138, 0, 200, 231, 251, 279, 293, 298, 302, 308, 312}, - {0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466}, - {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691}, -}; - -static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250, 261, 0}, - {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256, 267, 0}, - {0, 0, 0, 0, 98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0}, - {0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0}, -}; - -static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289, 296, 300}, - {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293, 300, 303}, - {0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053}, - {0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221}, -}; - -static const u16 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 182, 0, 240, 264, 278, 299, 308, 311, 313, 317, 319}, - {0, 0, 0, 0, 190, 0, 247, 269, 282, 302, 310, 313, 315, 319, 320}, - {0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219}, - {0, 0, 0, 0, 474, 0, 920, 1338, 1732, 2464, 3116, 3418, 3705, 4225, 4545}, -}; - -/* mbps, mcs */ -static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { - { "1", "BPSK DSSS"}, - { "2", "QPSK DSSS"}, - {"5.5", "BPSK CCK"}, - { "11", "QPSK CCK"}, - { "6", "BPSK 1/2"}, - { "9", "BPSK 1/2"}, - { "12", "QPSK 1/2"}, - { "18", "QPSK 3/4"}, - { "24", "16QAM 1/2"}, - { "36", "16QAM 3/4"}, - { "48", "64QAM 2/3"}, - { "54", "64QAM 3/4"}, - { "60", "64QAM 5/6"}, -}; - -#define MCS_INDEX_PER_STREAM (8) - -static const char *rs_pretty_ant(u8 ant) -{ - static const char * const ant_name[] = { - [ANT_NONE] = "None", - [ANT_A] = "A", - [ANT_B] = "B", - [ANT_AB] = "AB", - [ANT_C] = "C", - [ANT_AC] = "AC", - [ANT_BC] = "BC", - [ANT_ABC] = "ABC", - }; - - if (ant > ANT_ABC) - return "UNKNOWN"; - - return ant_name[ant]; -} - -static const char *rs_pretty_lq_type(enum iwl_table_type type) -{ - static const char * const lq_types[] = { - [LQ_NONE] = "NONE", - [LQ_LEGACY_A] = "LEGACY_A", - [LQ_LEGACY_G] = "LEGACY_G", - [LQ_HT_SISO] = "HT SISO", - [LQ_HT_MIMO2] = "HT MIMO", - [LQ_VHT_SISO] = "VHT SISO", - [LQ_VHT_MIMO2] = "VHT MIMO", - }; - - if (type < LQ_NONE || type >= LQ_MAX) - return "UNKNOWN"; - - return lq_types[type]; -} - -static char *rs_pretty_rate(const struct rs_rate *rate) -{ - static char buf[40]; - static const char * const legacy_rates[] = { - [IWL_RATE_1M_INDEX] = "1M", - [IWL_RATE_2M_INDEX] = "2M", - [IWL_RATE_5M_INDEX] = "5.5M", - [IWL_RATE_11M_INDEX] = "11M", - [IWL_RATE_6M_INDEX] = "6M", - [IWL_RATE_9M_INDEX] = "9M", - [IWL_RATE_12M_INDEX] = "12M", - [IWL_RATE_18M_INDEX] = "18M", - [IWL_RATE_24M_INDEX] = "24M", - [IWL_RATE_36M_INDEX] = "36M", - [IWL_RATE_48M_INDEX] = "48M", - [IWL_RATE_54M_INDEX] = "54M", - }; - static const char *const ht_vht_rates[] = { - [IWL_RATE_MCS_0_INDEX] = "MCS0", - [IWL_RATE_MCS_1_INDEX] = "MCS1", - [IWL_RATE_MCS_2_INDEX] = "MCS2", - [IWL_RATE_MCS_3_INDEX] = "MCS3", - [IWL_RATE_MCS_4_INDEX] = "MCS4", - [IWL_RATE_MCS_5_INDEX] = "MCS5", - [IWL_RATE_MCS_6_INDEX] = "MCS6", - [IWL_RATE_MCS_7_INDEX] = "MCS7", - [IWL_RATE_MCS_8_INDEX] = "MCS8", - [IWL_RATE_MCS_9_INDEX] = "MCS9", - }; - const char *rate_str; - - if (is_type_legacy(rate->type)) - rate_str = legacy_rates[rate->index]; - else if (is_type_ht(rate->type) || is_type_vht(rate->type)) - rate_str = ht_vht_rates[rate->index]; - else - rate_str = "BAD_RATE"; - - sprintf(buf, "(%s|%s|%s)", rs_pretty_lq_type(rate->type), - rs_pretty_ant(rate->ant), rate_str); - return buf; -} - -static inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate, - const char *prefix) -{ - IWL_DEBUG_RATE(mvm, - "%s: %s BW: %d SGI: %d LDPC: %d STBC: %d\n", - prefix, rs_pretty_rate(rate), rate->bw, - rate->sgi, rate->ldpc, rate->stbc); -} - -static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) -{ - window->data = 0; - window->success_counter = 0; - window->success_ratio = IWL_INVALID_VALUE; - window->counter = 0; - window->average_tpt = IWL_INVALID_VALUE; -} - -static void rs_rate_scale_clear_tbl_windows(struct iwl_mvm *mvm, - struct iwl_scale_tbl_info *tbl) -{ - int i; - - IWL_DEBUG_RATE(mvm, "Clearing up window stats\n"); - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&tbl->win[i]); - - for (i = 0; i < ARRAY_SIZE(tbl->tpc_win); i++) - rs_rate_scale_clear_window(&tbl->tpc_win[i]); -} - -static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) -{ - return (ant_type & valid_antenna) == ant_type; -} - -static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_data, u8 tid, - struct ieee80211_sta *sta) -{ - int ret = -EAGAIN; - - IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n", - sta->addr, tid); - ret = ieee80211_start_tx_ba_session(sta, tid, 5000); - if (ret == -EAGAIN) { - /* - * driver and mac80211 is out of sync - * this might be cause by reloading firmware - * stop the tx ba session here - */ - IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n", - tid); - ieee80211_stop_tx_ba_session(sta, tid); - } - return ret; -} - -static void rs_tl_turn_on_agg(struct iwl_mvm *mvm, u8 tid, - struct iwl_lq_sta *lq_data, - struct ieee80211_sta *sta) -{ - if (tid < IWL_MAX_TID_COUNT) - rs_tl_turn_on_agg_for_tid(mvm, lq_data, tid, sta); - else - IWL_ERR(mvm, "tid exceeds max TID count: %d/%d\n", - tid, IWL_MAX_TID_COUNT); -} - -static inline int get_num_of_ant_from_rate(u32 rate_n_flags) -{ - return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_C_MSK); -} - -/* - * Static function to get the expected throughput from an iwl_scale_tbl_info - * that wraps a NULL pointer check - */ -static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) -{ - if (tbl->expected_tpt) - return tbl->expected_tpt[rs_index]; - return 0; -} - -/** - * rs_collect_tx_data - Update the success/failure sliding window - * - * We keep a sliding window of the last 62 packets transmitted - * at this rate. window->data contains the bitmask of successful - * packets. - */ -static int _rs_collect_tx_data(struct iwl_mvm *mvm, - struct iwl_scale_tbl_info *tbl, - int scale_index, int attempts, int successes, - struct iwl_rate_scale_data *window) -{ - static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); - s32 fail_count, tpt; - - /* Get expected throughput */ - tpt = get_expected_tpt(tbl, scale_index); - - /* - * Keep track of only the latest 62 tx frame attempts in this rate's - * history window; anything older isn't really relevant any more. - * If we have filled up the sliding window, drop the oldest attempt; - * if the oldest attempt (highest bit in bitmap) shows "success", - * subtract "1" from the success counter (this is the main reason - * we keep these bitmaps!). - */ - while (attempts > 0) { - if (window->counter >= IWL_RATE_MAX_WINDOW) { - /* remove earliest */ - window->counter = IWL_RATE_MAX_WINDOW - 1; - - if (window->data & mask) { - window->data &= ~mask; - window->success_counter--; - } - } - - /* Increment frames-attempted counter */ - window->counter++; - - /* Shift bitmap by one frame to throw away oldest history */ - window->data <<= 1; - - /* Mark the most recent #successes attempts as successful */ - if (successes > 0) { - window->success_counter++; - window->data |= 0x1; - successes--; - } - - attempts--; - } - - /* Calculate current success ratio, avoid divide-by-0! */ - if (window->counter > 0) - window->success_ratio = 128 * (100 * window->success_counter) - / window->counter; - else - window->success_ratio = IWL_INVALID_VALUE; - - fail_count = window->counter - window->success_counter; - - /* Calculate average throughput, if we have enough history. */ - if ((fail_count >= IWL_MVM_RS_RATE_MIN_FAILURE_TH) || - (window->success_counter >= IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) - window->average_tpt = (window->success_ratio * tpt + 64) / 128; - else - window->average_tpt = IWL_INVALID_VALUE; - - return 0; -} - -static int rs_collect_tx_data(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - int scale_index, int attempts, int successes, - u8 reduced_txp) -{ - struct iwl_rate_scale_data *window = NULL; - int ret; - - if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) - return -EINVAL; - - if (tbl->column != RS_COLUMN_INVALID) { - struct lq_sta_pers *pers = &lq_sta->pers; - - pers->tx_stats[tbl->column][scale_index].total += attempts; - pers->tx_stats[tbl->column][scale_index].success += successes; - } - - /* Select window for current tx bit rate */ - window = &(tbl->win[scale_index]); - - ret = _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, - window); - if (ret) - return ret; - - if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION)) - return -EINVAL; - - window = &tbl->tpc_win[reduced_txp]; - return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, - window); -} - -/* Convert rs_rate object into ucode rate bitmask */ -static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm, - struct rs_rate *rate) -{ - u32 ucode_rate = 0; - int index = rate->index; - - ucode_rate |= ((rate->ant << RATE_MCS_ANT_POS) & - RATE_MCS_ANT_ABC_MSK); - - if (is_legacy(rate)) { - ucode_rate |= iwl_rates[index].plcp; - if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) - ucode_rate |= RATE_MCS_CCK_MSK; - return ucode_rate; - } - - if (is_ht(rate)) { - if (index < IWL_FIRST_HT_RATE || index > IWL_LAST_HT_RATE) { - IWL_ERR(mvm, "Invalid HT rate index %d\n", index); - index = IWL_LAST_HT_RATE; - } - ucode_rate |= RATE_MCS_HT_MSK; - - if (is_ht_siso(rate)) - ucode_rate |= iwl_rates[index].plcp_ht_siso; - else if (is_ht_mimo2(rate)) - ucode_rate |= iwl_rates[index].plcp_ht_mimo2; - else - WARN_ON_ONCE(1); - } else if (is_vht(rate)) { - if (index < IWL_FIRST_VHT_RATE || index > IWL_LAST_VHT_RATE) { - IWL_ERR(mvm, "Invalid VHT rate index %d\n", index); - index = IWL_LAST_VHT_RATE; - } - ucode_rate |= RATE_MCS_VHT_MSK; - if (is_vht_siso(rate)) - ucode_rate |= iwl_rates[index].plcp_vht_siso; - else if (is_vht_mimo2(rate)) - ucode_rate |= iwl_rates[index].plcp_vht_mimo2; - else - WARN_ON_ONCE(1); - - } else { - IWL_ERR(mvm, "Invalid rate->type %d\n", rate->type); - } - - if (is_siso(rate) && rate->stbc) { - /* To enable STBC we need to set both a flag and ANT_AB */ - ucode_rate |= RATE_MCS_ANT_AB_MSK; - ucode_rate |= RATE_MCS_VHT_STBC_MSK; - } - - ucode_rate |= rate->bw; - if (rate->sgi) - ucode_rate |= RATE_MCS_SGI_MSK; - if (rate->ldpc) - ucode_rate |= RATE_MCS_LDPC_MSK; - - return ucode_rate; -} - -/* Convert a ucode rate into an rs_rate object */ -static int rs_rate_from_ucode_rate(const u32 ucode_rate, - enum ieee80211_band band, - struct rs_rate *rate) -{ - u32 ant_msk = ucode_rate & RATE_MCS_ANT_ABC_MSK; - u8 num_of_ant = get_num_of_ant_from_rate(ucode_rate); - u8 nss; - - memset(rate, 0, sizeof(*rate)); - rate->index = iwl_hwrate_to_plcp_idx(ucode_rate); - - if (rate->index == IWL_RATE_INVALID) - return -EINVAL; - - rate->ant = (ant_msk >> RATE_MCS_ANT_POS); - - /* Legacy */ - if (!(ucode_rate & RATE_MCS_HT_MSK) && - !(ucode_rate & RATE_MCS_VHT_MSK)) { - if (num_of_ant == 1) { - if (band == IEEE80211_BAND_5GHZ) - rate->type = LQ_LEGACY_A; - else - rate->type = LQ_LEGACY_G; - } - - return 0; - } - - /* HT or VHT */ - if (ucode_rate & RATE_MCS_SGI_MSK) - rate->sgi = true; - if (ucode_rate & RATE_MCS_LDPC_MSK) - rate->ldpc = true; - if (ucode_rate & RATE_MCS_VHT_STBC_MSK) - rate->stbc = true; - if (ucode_rate & RATE_MCS_BF_MSK) - rate->bfer = true; - - rate->bw = ucode_rate & RATE_MCS_CHAN_WIDTH_MSK; - - if (ucode_rate & RATE_MCS_HT_MSK) { - nss = ((ucode_rate & RATE_HT_MCS_NSS_MSK) >> - RATE_HT_MCS_NSS_POS) + 1; - - if (nss == 1) { - rate->type = LQ_HT_SISO; - WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1, - "stbc %d bfer %d", - rate->stbc, rate->bfer); - } else if (nss == 2) { - rate->type = LQ_HT_MIMO2; - WARN_ON_ONCE(num_of_ant != 2); - } else { - WARN_ON_ONCE(1); - } - } else if (ucode_rate & RATE_MCS_VHT_MSK) { - nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1; - - if (nss == 1) { - rate->type = LQ_VHT_SISO; - WARN_ONCE(!rate->stbc && !rate->bfer && num_of_ant != 1, - "stbc %d bfer %d", - rate->stbc, rate->bfer); - } else if (nss == 2) { - rate->type = LQ_VHT_MIMO2; - WARN_ON_ONCE(num_of_ant != 2); - } else { - WARN_ON_ONCE(1); - } - } - - WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_160); - WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_80 && - !is_vht(rate)); - - return 0; -} - -/* switch to another antenna/antennas and return 1 */ -/* if no other valid antenna found, return 0 */ -static int rs_toggle_antenna(u32 valid_ant, struct rs_rate *rate) -{ - u8 new_ant_type; - - if (!rate->ant || rate->ant > ANT_ABC) - return 0; - - if (!rs_is_valid_ant(valid_ant, rate->ant)) - return 0; - - new_ant_type = ant_toggle_lookup[rate->ant]; - - while ((new_ant_type != rate->ant) && - !rs_is_valid_ant(valid_ant, new_ant_type)) - new_ant_type = ant_toggle_lookup[new_ant_type]; - - if (new_ant_type == rate->ant) - return 0; - - rate->ant = new_ant_type; - - return 1; -} - -static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, - struct rs_rate *rate) -{ - if (is_legacy(rate)) - return lq_sta->active_legacy_rate; - else if (is_siso(rate)) - return lq_sta->active_siso_rate; - else if (is_mimo2(rate)) - return lq_sta->active_mimo2_rate; - - WARN_ON_ONCE(1); - return 0; -} - -static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, - int rate_type) -{ - u8 high = IWL_RATE_INVALID; - u8 low = IWL_RATE_INVALID; - - /* 802.11A or ht walks to the next literal adjacent rate in - * the rate table */ - if (is_type_a_band(rate_type) || !is_type_legacy(rate_type)) { - int i; - u32 mask; - - /* Find the previous rate that is in the rate mask */ - i = index - 1; - for (mask = (1 << i); i >= 0; i--, mask >>= 1) { - if (rate_mask & mask) { - low = i; - break; - } - } - - /* Find the next rate that is in the rate mask */ - i = index + 1; - for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { - if (rate_mask & mask) { - high = i; - break; - } - } - - return (high << 8) | low; - } - - low = index; - while (low != IWL_RATE_INVALID) { - low = iwl_rates[low].prev_rs; - if (low == IWL_RATE_INVALID) - break; - if (rate_mask & (1 << low)) - break; - } - - high = index; - while (high != IWL_RATE_INVALID) { - high = iwl_rates[high].next_rs; - if (high == IWL_RATE_INVALID) - break; - if (rate_mask & (1 << high)) - break; - } - - return (high << 8) | low; -} - -static inline bool rs_rate_supported(struct iwl_lq_sta *lq_sta, - struct rs_rate *rate) -{ - return BIT(rate->index) & rs_get_supported_rates(lq_sta, rate); -} - -/* Get the next supported lower rate in the current column. - * Return true if bottom rate in the current column was reached - */ -static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta, - struct rs_rate *rate) -{ - u8 low; - u16 high_low; - u16 rate_mask; - struct iwl_mvm *mvm = lq_sta->pers.drv; - - rate_mask = rs_get_supported_rates(lq_sta, rate); - high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask, - rate->type); - low = high_low & 0xff; - - /* Bottom rate of column reached */ - if (low == IWL_RATE_INVALID) - return true; - - rate->index = low; - return false; -} - -/* Get the next rate to use following a column downgrade */ -static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta, - struct rs_rate *rate) -{ - struct iwl_mvm *mvm = lq_sta->pers.drv; - - if (is_legacy(rate)) { - /* No column to downgrade from Legacy */ - return; - } else if (is_siso(rate)) { - /* Downgrade to Legacy if we were in SISO */ - if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate->type = LQ_LEGACY_A; - else - rate->type = LQ_LEGACY_G; - - rate->bw = RATE_MCS_CHAN_WIDTH_20; - - WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX || - rate->index > IWL_RATE_MCS_9_INDEX); - - rate->index = rs_ht_to_legacy[rate->index]; - rate->ldpc = false; - } else { - /* Downgrade to SISO with same MCS if in MIMO */ - rate->type = is_vht_mimo2(rate) ? - LQ_VHT_SISO : LQ_HT_SISO; - } - - if (num_of_ant(rate->ant) > 1) - rate->ant = first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); - - /* Relevant in both switching to SISO or Legacy */ - rate->sgi = false; - - if (!rs_rate_supported(lq_sta, rate)) - rs_get_lower_rate_in_column(lq_sta, rate); -} - -/* Check if both rates are identical - * allow_ant_mismatch enables matching a SISO rate on ANT_A or ANT_B - * with a rate indicating STBC/BFER and ANT_AB. - */ -static inline bool rs_rate_equal(struct rs_rate *a, - struct rs_rate *b, - bool allow_ant_mismatch) - -{ - bool ant_match = (a->ant == b->ant) && (a->stbc == b->stbc) && - (a->bfer == b->bfer); - - if (allow_ant_mismatch) { - if (a->stbc || a->bfer) { - WARN_ONCE(a->ant != ANT_AB, "stbc %d bfer %d ant %d", - a->stbc, a->bfer, a->ant); - ant_match |= (b->ant == ANT_A || b->ant == ANT_B); - } else if (b->stbc || b->bfer) { - WARN_ONCE(b->ant != ANT_AB, "stbc %d bfer %d ant %d", - b->stbc, b->bfer, b->ant); - ant_match |= (a->ant == ANT_A || a->ant == ANT_B); - } - } - - return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi) && - (a->ldpc == b->ldpc) && (a->index == b->index) && ant_match; -} - -/* Check if both rates share the same column */ -static inline bool rs_rate_column_match(struct rs_rate *a, - struct rs_rate *b) -{ - bool ant_match; - - if (a->stbc || a->bfer) - ant_match = (b->ant == ANT_A || b->ant == ANT_B); - else - ant_match = (a->ant == b->ant); - - return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi) - && ant_match; -} - -static inline enum rs_column rs_get_column_from_rate(struct rs_rate *rate) -{ - if (is_legacy(rate)) { - if (rate->ant == ANT_A) - return RS_COLUMN_LEGACY_ANT_A; - - if (rate->ant == ANT_B) - return RS_COLUMN_LEGACY_ANT_B; - - goto err; - } - - if (is_siso(rate)) { - if (rate->ant == ANT_A || rate->stbc || rate->bfer) - return rate->sgi ? RS_COLUMN_SISO_ANT_A_SGI : - RS_COLUMN_SISO_ANT_A; - - if (rate->ant == ANT_B) - return rate->sgi ? RS_COLUMN_SISO_ANT_B_SGI : - RS_COLUMN_SISO_ANT_B; - - goto err; - } - - if (is_mimo(rate)) - return rate->sgi ? RS_COLUMN_MIMO2_SGI : RS_COLUMN_MIMO2; - -err: - return RS_COLUMN_INVALID; -} - -static u8 rs_get_tid(struct ieee80211_hdr *hdr) -{ - u8 tid = IWL_MAX_TID_COUNT; - - if (ieee80211_is_data_qos(hdr->frame_control)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & 0xf; - } - - if (unlikely(tid > IWL_MAX_TID_COUNT)) - tid = IWL_MAX_TID_COUNT; - - return tid; -} - -void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, struct ieee80211_tx_info *info) -{ - int legacy_success; - int retries; - int i; - struct iwl_lq_cmd *table; - u32 lq_hwrate; - struct rs_rate lq_rate, tx_resp_rate; - struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; - u8 reduced_txp = (uintptr_t)info->status.status_driver_data[0]; - u32 tx_resp_hwrate = (uintptr_t)info->status.status_driver_data[1]; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_lq_sta *lq_sta = &mvmsta->lq_sta; - bool allow_ant_mismatch = fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_LQ_SS_PARAMS); - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (!lq_sta) { - IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n"); - return; - } else if (!lq_sta->pers.drv) { - IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); - return; - } - - /* This packet was aggregated but doesn't carry status info */ - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && - !(info->flags & IEEE80211_TX_STAT_AMPDU)) - return; - - rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, &tx_resp_rate); - -#ifdef CONFIG_MAC80211_DEBUGFS - /* Disable last tx check if we are debugging with fixed rate but - * update tx stats */ - if (lq_sta->pers.dbg_fixed_rate) { - int index = tx_resp_rate.index; - enum rs_column column; - int attempts, success; - - column = rs_get_column_from_rate(&tx_resp_rate); - if (WARN_ONCE(column == RS_COLUMN_INVALID, - "Can't map rate 0x%x to column", - tx_resp_hwrate)) - return; - - if (info->flags & IEEE80211_TX_STAT_AMPDU) { - attempts = info->status.ampdu_len; - success = info->status.ampdu_ack_len; - } else { - attempts = info->status.rates[0].count; - success = !!(info->flags & IEEE80211_TX_STAT_ACK); - } - - lq_sta->pers.tx_stats[column][index].total += attempts; - lq_sta->pers.tx_stats[column][index].success += success; - - IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n", - tx_resp_hwrate, success, attempts); - return; - } -#endif - - if (time_after(jiffies, - (unsigned long)(lq_sta->last_tx + - (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) { - int t; - - IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); - for (t = 0; t < IWL_MAX_TID_COUNT; t++) - ieee80211_stop_tx_ba_session(sta, t); - - iwl_mvm_rs_rate_init(mvm, sta, info->band, false); - return; - } - lq_sta->last_tx = jiffies; - - /* Ignore this Tx frame response if its initial rate doesn't match - * that of latest Link Quality command. There may be stragglers - * from a previous Link Quality command, but we're no longer interested - * in those; they're either from the "active" mode while we're trying - * to check "search" mode, or a prior "search" mode after we've moved - * to a new "search" mode (which might become the new "active" mode). - */ - table = &lq_sta->lq; - lq_hwrate = le32_to_cpu(table->rs_table[0]); - rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate); - - /* Here we actually compare this rate to the latest LQ command */ - if (!rs_rate_equal(&tx_resp_rate, &lq_rate, allow_ant_mismatch)) { - IWL_DEBUG_RATE(mvm, - "initial tx resp rate 0x%x does not match 0x%x\n", - tx_resp_hwrate, lq_hwrate); - - /* - * Since rates mis-match, the last LQ command may have failed. - * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with - * ... driver. - */ - lq_sta->missed_rate_counter++; - if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) { - lq_sta->missed_rate_counter = 0; - IWL_DEBUG_RATE(mvm, - "Too many rates mismatch. Send sync LQ. rs_state %d\n", - lq_sta->rs_state); - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); - } - /* Regardless, ignore this status info for outdated rate */ - return; - } else - /* Rate did match, so reset the missed_rate_counter */ - lq_sta->missed_rate_counter = 0; - - if (!lq_sta->search_better_tbl) { - curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - } else { - curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - } - - if (WARN_ON_ONCE(!rs_rate_column_match(&lq_rate, &curr_tbl->rate))) { - IWL_DEBUG_RATE(mvm, - "Neither active nor search matches tx rate\n"); - tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE"); - tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH"); - rs_dump_rate(mvm, &lq_rate, "ACTUAL"); - - /* - * no matching table found, let's by-pass the data collection - * and continue to perform rate scale to find the rate table - */ - rs_stay_in_table(lq_sta, true); - goto done; - } - - /* - * Updating the frame history depends on whether packets were - * aggregated. - * - * For aggregation, all packets were transmitted at the same rate, the - * first index into rate scale table. - */ - if (info->flags & IEEE80211_TX_STAT_AMPDU) { - /* ampdu_ack_len = 0 marks no BA was received. In this case - * treat it as a single frame loss as we don't want the success - * ratio to dip too quickly because a BA wasn't received - */ - if (info->status.ampdu_ack_len == 0) - info->status.ampdu_len = 1; - - rs_collect_tx_data(mvm, lq_sta, curr_tbl, lq_rate.index, - info->status.ampdu_len, - info->status.ampdu_ack_len, - reduced_txp); - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { - lq_sta->total_success += info->status.ampdu_ack_len; - lq_sta->total_failed += (info->status.ampdu_len - - info->status.ampdu_ack_len); - } - } else { - /* For legacy, update frame history with for each Tx retry. */ - retries = info->status.rates[0].count - 1; - /* HW doesn't send more than 15 retries */ - retries = min(retries, 15); - - /* The last transmission may have been successful */ - legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); - /* Collect data for each rate used during failed TX attempts */ - for (i = 0; i <= retries; ++i) { - lq_hwrate = le32_to_cpu(table->rs_table[i]); - rs_rate_from_ucode_rate(lq_hwrate, info->band, - &lq_rate); - /* - * Only collect stats if retried rate is in the same RS - * table as active/search. - */ - if (rs_rate_column_match(&lq_rate, &curr_tbl->rate)) - tmp_tbl = curr_tbl; - else if (rs_rate_column_match(&lq_rate, - &other_tbl->rate)) - tmp_tbl = other_tbl; - else - continue; - - rs_collect_tx_data(mvm, lq_sta, tmp_tbl, lq_rate.index, - 1, i < retries ? 0 : legacy_success, - reduced_txp); - } - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { - lq_sta->total_success += legacy_success; - lq_sta->total_failed += retries + (1 - legacy_success); - } - } - /* The last TX rate is cached in lq_sta; it's set in if/else above */ - lq_sta->last_rate_n_flags = lq_hwrate; - IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp); -done: - /* See if there's a better rate or modulation mode to try. */ - if (sta->supp_rates[info->band]) - rs_rate_scale_perform(mvm, sta, lq_sta, tid); -} - -/* - * mac80211 sends us Tx status - */ -static void rs_mac80211_tx_status(void *mvm_r, - struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta, - struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_r; - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - if (!iwl_mvm_sta_from_mac80211(sta)->vif) - return; - - if (!ieee80211_is_data(hdr->frame_control) || - info->flags & IEEE80211_TX_CTL_NO_ACK) - return; - - iwl_mvm_rs_tx_status(mvm, sta, rs_get_tid(hdr), info); -} - -/* - * Begin a period of staying with a selected modulation mode. - * Set "stay_in_tbl" flag to prevent any mode switches. - * Set frame tx success limits according to legacy vs. high-throughput, - * and reset overall (spanning all rates) tx success history statistics. - * These control how long we stay using same modulation mode before - * searching for a new mode. - */ -static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, - struct iwl_lq_sta *lq_sta) -{ - IWL_DEBUG_RATE(mvm, "Moving to RS_STATE_STAY_IN_COLUMN\n"); - lq_sta->rs_state = RS_STATE_STAY_IN_COLUMN; - if (is_legacy) { - lq_sta->table_count_limit = IWL_MVM_RS_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_MVM_RS_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_MVM_RS_LEGACY_SUCCESS_LIMIT; - } else { - lq_sta->table_count_limit = IWL_MVM_RS_NON_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT; - } - lq_sta->table_count = 0; - lq_sta->total_failed = 0; - lq_sta->total_success = 0; - lq_sta->flush_timer = jiffies; - lq_sta->visited_columns = 0; -} - -static inline int rs_get_max_rate_from_mask(unsigned long rate_mask) -{ - if (rate_mask) - return find_last_bit(&rate_mask, BITS_PER_LONG); - return IWL_RATE_INVALID; -} - -static int rs_get_max_allowed_rate(struct iwl_lq_sta *lq_sta, - const struct rs_tx_column *column) -{ - switch (column->mode) { - case RS_LEGACY: - return lq_sta->max_legacy_rate_idx; - case RS_SISO: - return lq_sta->max_siso_rate_idx; - case RS_MIMO2: - return lq_sta->max_mimo2_rate_idx; - default: - WARN_ON_ONCE(1); - } - - return lq_sta->max_legacy_rate_idx; -} - -static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta, - const struct rs_tx_column *column, - u32 bw) -{ - /* Used to choose among HT tables */ - const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT]; - - if (WARN_ON_ONCE(column->mode != RS_LEGACY && - column->mode != RS_SISO && - column->mode != RS_MIMO2)) - return expected_tpt_legacy; - - /* Legacy rates have only one table */ - if (column->mode == RS_LEGACY) - return expected_tpt_legacy; - - ht_tbl_pointer = expected_tpt_mimo2_20MHz; - /* Choose among many HT tables depending on number of streams - * (SISO/MIMO2), channel width (20/40/80), SGI, and aggregation - * status */ - if (column->mode == RS_SISO) { - switch (bw) { - case RATE_MCS_CHAN_WIDTH_20: - ht_tbl_pointer = expected_tpt_siso_20MHz; - break; - case RATE_MCS_CHAN_WIDTH_40: - ht_tbl_pointer = expected_tpt_siso_40MHz; - break; - case RATE_MCS_CHAN_WIDTH_80: - ht_tbl_pointer = expected_tpt_siso_80MHz; - break; - default: - WARN_ON_ONCE(1); - } - } else if (column->mode == RS_MIMO2) { - switch (bw) { - case RATE_MCS_CHAN_WIDTH_20: - ht_tbl_pointer = expected_tpt_mimo2_20MHz; - break; - case RATE_MCS_CHAN_WIDTH_40: - ht_tbl_pointer = expected_tpt_mimo2_40MHz; - break; - case RATE_MCS_CHAN_WIDTH_80: - ht_tbl_pointer = expected_tpt_mimo2_80MHz; - break; - default: - WARN_ON_ONCE(1); - } - } else { - WARN_ON_ONCE(1); - } - - if (!column->sgi && !lq_sta->is_agg) /* Normal */ - return ht_tbl_pointer[0]; - else if (column->sgi && !lq_sta->is_agg) /* SGI */ - return ht_tbl_pointer[1]; - else if (!column->sgi && lq_sta->is_agg) /* AGG */ - return ht_tbl_pointer[2]; - else /* AGG+SGI */ - return ht_tbl_pointer[3]; -} - -static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl) -{ - struct rs_rate *rate = &tbl->rate; - const struct rs_tx_column *column = &rs_tx_columns[tbl->column]; - - tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw); -} - -static s32 rs_get_best_rate(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, /* "search" */ - unsigned long rate_mask, s8 index) -{ - struct iwl_scale_tbl_info *active_tbl = - &(lq_sta->lq_info[lq_sta->active_tbl]); - s32 success_ratio = active_tbl->win[index].success_ratio; - u16 expected_current_tpt = active_tbl->expected_tpt[index]; - const u16 *tpt_tbl = tbl->expected_tpt; - u16 high_low; - u32 target_tpt; - int rate_idx; - - if (success_ratio >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) { - target_tpt = 100 * expected_current_tpt; - IWL_DEBUG_RATE(mvm, - "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n", - success_ratio, target_tpt); - } else { - target_tpt = lq_sta->last_tpt; - IWL_DEBUG_RATE(mvm, - "SR %d not that good. Find rate exceeding ACTUAL_TPT %d\n", - success_ratio, target_tpt); - } - - rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG); - - while (rate_idx != IWL_RATE_INVALID) { - if (target_tpt < (100 * tpt_tbl[rate_idx])) - break; - - high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask, - tbl->rate.type); - - rate_idx = (high_low >> 8) & 0xff; - } - - IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n", - rate_idx, target_tpt, - rate_idx != IWL_RATE_INVALID ? - 100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE); - - return rate_idx; -} - -static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta) -{ - if (sta->bandwidth >= IEEE80211_STA_RX_BW_80) - return RATE_MCS_CHAN_WIDTH_80; - else if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) - return RATE_MCS_CHAN_WIDTH_40; - - return RATE_MCS_CHAN_WIDTH_20; -} - -/* - * Check whether we should continue using same modulation mode, or - * begin search for a new mode, based on: - * 1) # tx successes or failures while using this mode - * 2) # times calling this function - * 3) elapsed time in this mode (not used, for now) - */ -static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) -{ - struct iwl_scale_tbl_info *tbl; - int active_tbl; - int flush_interval_passed = 0; - struct iwl_mvm *mvm; - - mvm = lq_sta->pers.drv; - active_tbl = lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - - /* If we've been disallowing search, see if we should now allow it */ - if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { - /* Elapsed time using current modulation mode */ - if (lq_sta->flush_timer) - flush_interval_passed = - time_after(jiffies, - (unsigned long)(lq_sta->flush_timer + - (IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT * HZ))); - - /* - * Check if we should allow search for new modulation mode. - * If many frames have failed or succeeded, or we've used - * this same modulation for a long time, allow search, and - * reset history stats that keep track of whether we should - * allow a new search. Also (below) reset all bitmaps and - * stats in active history. - */ - if (force_search || - (lq_sta->total_failed > lq_sta->max_failure_limit) || - (lq_sta->total_success > lq_sta->max_success_limit) || - ((!lq_sta->search_better_tbl) && - (lq_sta->flush_timer) && (flush_interval_passed))) { - IWL_DEBUG_RATE(mvm, - "LQ: stay is expired %d %d %d\n", - lq_sta->total_failed, - lq_sta->total_success, - flush_interval_passed); - - /* Allow search for new mode */ - lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_STARTED; - IWL_DEBUG_RATE(mvm, - "Moving to RS_STATE_SEARCH_CYCLE_STARTED\n"); - lq_sta->total_failed = 0; - lq_sta->total_success = 0; - lq_sta->flush_timer = 0; - /* mark the current column as visited */ - lq_sta->visited_columns = BIT(tbl->column); - /* - * Else if we've used this modulation mode enough repetitions - * (regardless of elapsed time or success/failure), reset - * history bitmaps and rate-specific stats for all rates in - * active table. - */ - } else { - lq_sta->table_count++; - if (lq_sta->table_count >= - lq_sta->table_count_limit) { - lq_sta->table_count = 0; - - IWL_DEBUG_RATE(mvm, - "LQ: stay in table clear win\n"); - rs_rate_scale_clear_tbl_windows(mvm, tbl); - } - } - - /* If transitioning to allow "search", reset all history - * bitmaps and stats in active table (this will become the new - * "search" table). */ - if (lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED) { - rs_rate_scale_clear_tbl_windows(mvm, tbl); - } - } -} - -/* - * setup rate table in uCode - */ -static void rs_update_rate_tbl(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl) -{ - rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate); - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); -} - -static bool rs_tweak_rate_tbl(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - enum rs_action scale_action) -{ - if (sta->bandwidth != IEEE80211_STA_RX_BW_80) - return false; - - if (!is_vht_siso(&tbl->rate)) - return false; - - if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_80) && - (tbl->rate.index == IWL_RATE_MCS_0_INDEX) && - (scale_action == RS_ACTION_DOWNSCALE)) { - tbl->rate.bw = RATE_MCS_CHAN_WIDTH_20; - tbl->rate.index = IWL_RATE_MCS_4_INDEX; - IWL_DEBUG_RATE(mvm, "Switch 80Mhz SISO MCS0 -> 20Mhz MCS4\n"); - goto tweaked; - } - - /* Go back to 80Mhz MCS1 only if we've established that 20Mhz MCS5 is - * sustainable, i.e. we're past the test window. We can't go back - * if MCS5 is just tested as this will happen always after switching - * to 20Mhz MCS4 because the rate stats are cleared. - */ - if ((tbl->rate.bw == RATE_MCS_CHAN_WIDTH_20) && - (((tbl->rate.index == IWL_RATE_MCS_5_INDEX) && - (scale_action == RS_ACTION_STAY)) || - ((tbl->rate.index > IWL_RATE_MCS_5_INDEX) && - (scale_action == RS_ACTION_UPSCALE)))) { - tbl->rate.bw = RATE_MCS_CHAN_WIDTH_80; - tbl->rate.index = IWL_RATE_MCS_1_INDEX; - IWL_DEBUG_RATE(mvm, "Switch 20Mhz SISO MCS5 -> 80Mhz MCS1\n"); - goto tweaked; - } - - return false; - -tweaked: - rs_set_expected_tpt_table(lq_sta, tbl); - rs_rate_scale_clear_tbl_windows(mvm, tbl); - return true; -} - -static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl) -{ - int i, j, max_rate; - enum rs_column next_col_id; - const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column]; - const struct rs_tx_column *next_col; - allow_column_func_t allow_func; - u8 valid_ants = iwl_mvm_get_valid_tx_ant(mvm); - const u16 *expected_tpt_tbl; - u16 tpt, max_expected_tpt; - - for (i = 0; i < MAX_NEXT_COLUMNS; i++) { - next_col_id = curr_col->next_columns[i]; - - if (next_col_id == RS_COLUMN_INVALID) - continue; - - if (lq_sta->visited_columns & BIT(next_col_id)) { - IWL_DEBUG_RATE(mvm, "Skip already visited column %d\n", - next_col_id); - continue; - } - - next_col = &rs_tx_columns[next_col_id]; - - if (!rs_is_valid_ant(valid_ants, next_col->ant)) { - IWL_DEBUG_RATE(mvm, - "Skip column %d as ANT config isn't supported by chip. valid_ants 0x%x column ant 0x%x\n", - next_col_id, valid_ants, next_col->ant); - continue; - } - - for (j = 0; j < MAX_COLUMN_CHECKS; j++) { - allow_func = next_col->checks[j]; - if (allow_func && !allow_func(mvm, sta, &tbl->rate, - next_col)) - break; - } - - if (j != MAX_COLUMN_CHECKS) { - IWL_DEBUG_RATE(mvm, - "Skip column %d: not allowed (check %d failed)\n", - next_col_id, j); - - continue; - } - - tpt = lq_sta->last_tpt / 100; - expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col, - rs_bw_from_sta_bw(sta)); - if (WARN_ON_ONCE(!expected_tpt_tbl)) - continue; - - max_rate = rs_get_max_allowed_rate(lq_sta, next_col); - if (max_rate == IWL_RATE_INVALID) { - IWL_DEBUG_RATE(mvm, - "Skip column %d: no rate is allowed in this column\n", - next_col_id); - continue; - } - - max_expected_tpt = expected_tpt_tbl[max_rate]; - if (tpt >= max_expected_tpt) { - IWL_DEBUG_RATE(mvm, - "Skip column %d: can't beat current TPT. Max expected %d current %d\n", - next_col_id, max_expected_tpt, tpt); - continue; - } - - IWL_DEBUG_RATE(mvm, - "Found potential column %d. Max expected %d current %d\n", - next_col_id, max_expected_tpt, tpt); - break; - } - - if (i == MAX_NEXT_COLUMNS) - return RS_COLUMN_INVALID; - - return next_col_id; -} - -static int rs_switch_to_column(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, - enum rs_column col_id) -{ - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct rs_rate *rate = &search_tbl->rate; - const struct rs_tx_column *column = &rs_tx_columns[col_id]; - const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column]; - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - unsigned long rate_mask = 0; - u32 rate_idx = 0; - - memcpy(search_tbl, tbl, sz); - - rate->sgi = column->sgi; - rate->ant = column->ant; - - if (column->mode == RS_LEGACY) { - if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate->type = LQ_LEGACY_A; - else - rate->type = LQ_LEGACY_G; - - rate->bw = RATE_MCS_CHAN_WIDTH_20; - rate->ldpc = false; - rate_mask = lq_sta->active_legacy_rate; - } else if (column->mode == RS_SISO) { - rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; - rate_mask = lq_sta->active_siso_rate; - } else if (column->mode == RS_MIMO2) { - rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; - rate_mask = lq_sta->active_mimo2_rate; - } else { - WARN_ON_ONCE("Bad column mode"); - } - - if (column->mode != RS_LEGACY) { - rate->bw = rs_bw_from_sta_bw(sta); - rate->ldpc = lq_sta->ldpc; - } - - 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 - */ - if (curr_column->mode != column->mode) { - rate_idx = rs_get_best_rate(mvm, lq_sta, search_tbl, - rate_mask, rate->index); - - if ((rate_idx == IWL_RATE_INVALID) || - !(BIT(rate_idx) & rate_mask)) { - IWL_DEBUG_RATE(mvm, - "can not switch with index %d" - " rate mask %lx\n", - rate_idx, rate_mask); - - goto err; - } - - rate->index = rate_idx; - } - - IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n", - col_id, rate->index); - - return 0; - -err: - rate->type = LQ_NONE; - return -1; -} - -static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, - struct iwl_scale_tbl_info *tbl, - s32 sr, int low, int high, - int current_tpt, - int low_tpt, int high_tpt) -{ - enum rs_action action = RS_ACTION_STAY; - - if ((sr <= RS_PERCENT(IWL_MVM_RS_SR_FORCE_DECREASE)) || - (current_tpt == 0)) { - IWL_DEBUG_RATE(mvm, - "Decrease rate because of low SR\n"); - return RS_ACTION_DOWNSCALE; - } - - if ((low_tpt == IWL_INVALID_VALUE) && - (high_tpt == IWL_INVALID_VALUE) && - (high != IWL_RATE_INVALID)) { - IWL_DEBUG_RATE(mvm, - "No data about high/low rates. Increase rate\n"); - return RS_ACTION_UPSCALE; - } - - if ((high_tpt == IWL_INVALID_VALUE) && - (high != IWL_RATE_INVALID) && - (low_tpt != IWL_INVALID_VALUE) && - (low_tpt < current_tpt)) { - IWL_DEBUG_RATE(mvm, - "No data about high rate and low rate is worse. Increase rate\n"); - return RS_ACTION_UPSCALE; - } - - if ((high_tpt != IWL_INVALID_VALUE) && - (high_tpt > current_tpt)) { - IWL_DEBUG_RATE(mvm, - "Higher rate is better. Increate rate\n"); - return RS_ACTION_UPSCALE; - } - - if ((low_tpt != IWL_INVALID_VALUE) && - (high_tpt != IWL_INVALID_VALUE) && - (low_tpt < current_tpt) && - (high_tpt < current_tpt)) { - IWL_DEBUG_RATE(mvm, - "Both high and low are worse. Maintain rate\n"); - return RS_ACTION_STAY; - } - - if ((low_tpt != IWL_INVALID_VALUE) && - (low_tpt > current_tpt)) { - IWL_DEBUG_RATE(mvm, - "Lower rate is better\n"); - action = RS_ACTION_DOWNSCALE; - goto out; - } - - if ((low_tpt == IWL_INVALID_VALUE) && - (low != IWL_RATE_INVALID)) { - IWL_DEBUG_RATE(mvm, - "No data about lower rate\n"); - action = RS_ACTION_DOWNSCALE; - goto out; - } - - IWL_DEBUG_RATE(mvm, "Maintain rate\n"); - -out: - if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID)) { - if (sr >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) { - IWL_DEBUG_RATE(mvm, - "SR is above NO DECREASE. Avoid downscale\n"); - action = RS_ACTION_STAY; - } else if (current_tpt > (100 * tbl->expected_tpt[low])) { - IWL_DEBUG_RATE(mvm, - "Current TPT is higher than max expected in low rate. Avoid downscale\n"); - action = RS_ACTION_STAY; - } else { - IWL_DEBUG_RATE(mvm, "Decrease rate\n"); - } - } - - return action; -} - -static bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta) -{ - /* Our chip supports Tx STBC and the peer is an HT/VHT STA which - * supports STBC of at least 1*SS - */ - if (!lq_sta->stbc_capable) - return false; - - if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) - return false; - - return true; -} - -static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index, - int *weaker, int *stronger) -{ - *weaker = index + IWL_MVM_RS_TPC_TX_POWER_STEP; - if (*weaker > TPC_MAX_REDUCTION) - *weaker = TPC_INVALID; - - *stronger = index - IWL_MVM_RS_TPC_TX_POWER_STEP; - if (*stronger < 0) - *stronger = TPC_INVALID; -} - -static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct rs_rate *rate, enum ieee80211_band band) -{ - int index = rate->index; - bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM); - bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION && - !vif->bss_conf.ps); - - IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n", - cam, sta_ps_disabled); - /* - * allow tpc only if power management is enabled, or bt coex - * activity grade allows it and we are on 2.4Ghz. - */ - if ((cam || sta_ps_disabled) && - !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band)) - return false; - - IWL_DEBUG_RATE(mvm, "check rate, table type: %d\n", rate->type); - if (is_legacy(rate)) - return index == IWL_RATE_54M_INDEX; - if (is_ht(rate)) - return index == IWL_RATE_MCS_7_INDEX; - if (is_vht(rate)) - return index == IWL_RATE_MCS_7_INDEX || - index == IWL_RATE_MCS_8_INDEX || - index == IWL_RATE_MCS_9_INDEX; - - WARN_ON_ONCE(1); - return false; -} - -enum tpc_action { - TPC_ACTION_STAY, - TPC_ACTION_DECREASE, - TPC_ACTION_INCREASE, - TPC_ACTION_NO_RESTIRCTION, -}; - -static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm, - s32 sr, int weak, int strong, - int current_tpt, - int weak_tpt, int strong_tpt) -{ - /* stay until we have valid tpt */ - if (current_tpt == IWL_INVALID_VALUE) { - IWL_DEBUG_RATE(mvm, "no current tpt. stay.\n"); - return TPC_ACTION_STAY; - } - - /* Too many failures, increase txp */ - if (sr <= RS_PERCENT(IWL_MVM_RS_TPC_SR_FORCE_INCREASE) || - current_tpt == 0) { - IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n"); - return TPC_ACTION_NO_RESTIRCTION; - } - - /* try decreasing first if applicable */ - if (weak != TPC_INVALID) { - if (weak_tpt == IWL_INVALID_VALUE && - (strong_tpt == IWL_INVALID_VALUE || - current_tpt >= strong_tpt)) { - IWL_DEBUG_RATE(mvm, - "no weak txp measurement. decrease txp\n"); - return TPC_ACTION_DECREASE; - } - - if (weak_tpt > current_tpt) { - IWL_DEBUG_RATE(mvm, - "lower txp has better tpt. decrease txp\n"); - return TPC_ACTION_DECREASE; - } - } - - /* next, increase if needed */ - if (sr < RS_PERCENT(IWL_MVM_RS_TPC_SR_NO_INCREASE) && - strong != TPC_INVALID) { - if (weak_tpt == IWL_INVALID_VALUE && - strong_tpt != IWL_INVALID_VALUE && - current_tpt < strong_tpt) { - IWL_DEBUG_RATE(mvm, - "higher txp has better tpt. increase txp\n"); - return TPC_ACTION_INCREASE; - } - - if (weak_tpt < current_tpt && - (strong_tpt == IWL_INVALID_VALUE || - strong_tpt > current_tpt)) { - IWL_DEBUG_RATE(mvm, - "lower txp has worse tpt. increase txp\n"); - return TPC_ACTION_INCREASE; - } - } - - IWL_DEBUG_RATE(mvm, "no need to increase or decrease txp - stay\n"); - return TPC_ACTION_STAY; -} - -static bool rs_tpc_perform(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - struct ieee80211_vif *vif = mvm_sta->vif; - struct ieee80211_chanctx_conf *chanctx_conf; - enum ieee80211_band band; - struct iwl_rate_scale_data *window; - struct rs_rate *rate = &tbl->rate; - enum tpc_action action; - s32 sr; - u8 cur = lq_sta->lq.reduced_tpc; - int current_tpt; - int weak, strong; - int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE; - -#ifdef CONFIG_MAC80211_DEBUGFS - if (lq_sta->pers.dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) { - IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n", - lq_sta->pers.dbg_fixed_txp_reduction); - lq_sta->lq.reduced_tpc = lq_sta->pers.dbg_fixed_txp_reduction; - return cur != lq_sta->pers.dbg_fixed_txp_reduction; - } -#endif - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - if (WARN_ON(!chanctx_conf)) - band = IEEE80211_NUM_BANDS; - else - band = chanctx_conf->def.chan->band; - rcu_read_unlock(); - - if (!rs_tpc_allowed(mvm, vif, rate, band)) { - IWL_DEBUG_RATE(mvm, - "tpc is not allowed. remove txp restrictions\n"); - lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; - return cur != TPC_NO_REDUCTION; - } - - rs_get_adjacent_txp(mvm, cur, &weak, &strong); - - /* Collect measured throughputs for current and adjacent rates */ - window = tbl->tpc_win; - sr = window[cur].success_ratio; - current_tpt = window[cur].average_tpt; - if (weak != TPC_INVALID) - weak_tpt = window[weak].average_tpt; - if (strong != TPC_INVALID) - strong_tpt = window[strong].average_tpt; - - IWL_DEBUG_RATE(mvm, - "(TPC: %d): cur_tpt %d SR %d weak %d strong %d weak_tpt %d strong_tpt %d\n", - cur, current_tpt, sr, weak, strong, - weak_tpt, strong_tpt); - - action = rs_get_tpc_action(mvm, sr, weak, strong, - current_tpt, weak_tpt, strong_tpt); - - /* override actions if we are on the edge */ - if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) { - IWL_DEBUG_RATE(mvm, "already in lowest txp, stay\n"); - action = TPC_ACTION_STAY; - } else if (strong == TPC_INVALID && - (action == TPC_ACTION_INCREASE || - action == TPC_ACTION_NO_RESTIRCTION)) { - IWL_DEBUG_RATE(mvm, "already in highest txp, stay\n"); - action = TPC_ACTION_STAY; - } - - switch (action) { - case TPC_ACTION_DECREASE: - lq_sta->lq.reduced_tpc = weak; - return true; - case TPC_ACTION_INCREASE: - lq_sta->lq.reduced_tpc = strong; - return true; - case TPC_ACTION_NO_RESTIRCTION: - lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; - return true; - case TPC_ACTION_STAY: - /* do nothing */ - break; - } - return false; -} - -/* - * Do rate scaling and search for new modulation mode. - */ -static void rs_rate_scale_perform(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - int tid) -{ - int low = IWL_RATE_INVALID; - int high = IWL_RATE_INVALID; - int index; - struct iwl_rate_scale_data *window = NULL; - int current_tpt = IWL_INVALID_VALUE; - int low_tpt = IWL_INVALID_VALUE; - int high_tpt = IWL_INVALID_VALUE; - u32 fail_count; - enum rs_action scale_action = RS_ACTION_STAY; - u16 rate_mask; - u8 update_lq = 0; - struct iwl_scale_tbl_info *tbl, *tbl1; - u8 active_tbl = 0; - u8 done_search = 0; - u16 high_low; - s32 sr; - u8 prev_agg = lq_sta->is_agg; - struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data; - struct rs_rate *rate; - - lq_sta->is_agg = !!sta_priv->agg_tids; - - /* - * Select rate-scale / modulation-mode table to work with in - * the rest of this function: "search" if searching for better - * modulation mode, or "active" if doing rate scaling within a mode. - */ - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - rate = &tbl->rate; - - if (prev_agg != lq_sta->is_agg) { - IWL_DEBUG_RATE(mvm, - "Aggregation changed: prev %d current %d. Update expected TPT table\n", - prev_agg, lq_sta->is_agg); - rs_set_expected_tpt_table(lq_sta, tbl); - rs_rate_scale_clear_tbl_windows(mvm, tbl); - } - - /* current tx rate */ - index = rate->index; - - /* rates available for this association, and for modulation mode */ - rate_mask = rs_get_supported_rates(lq_sta, rate); - - if (!(BIT(index) & rate_mask)) { - IWL_ERR(mvm, "Current Rate is not valid\n"); - if (lq_sta->search_better_tbl) { - /* revert to active table if search table is not valid*/ - rate->type = LQ_NONE; - lq_sta->search_better_tbl = 0; - tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - rs_update_rate_tbl(mvm, sta, lq_sta, tbl); - } - return; - } - - /* Get expected throughput table and history window for current rate */ - if (!tbl->expected_tpt) { - IWL_ERR(mvm, "tbl->expected_tpt is NULL\n"); - return; - } - - /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ - window = &(tbl->win[index]); - - /* - * If there is not enough history to calculate actual average - * throughput, keep analyzing results of more tx frames, without - * changing rate or mode (bypass most of the rest of this function). - * Set up new rate table in uCode only if old rate is not supported - * in current association (use new rate found above). - */ - fail_count = window->counter - window->success_counter; - if ((fail_count < IWL_MVM_RS_RATE_MIN_FAILURE_TH) && - (window->success_counter < IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) { - IWL_DEBUG_RATE(mvm, - "%s: Test Window: succ %d total %d\n", - rs_pretty_rate(rate), - window->success_counter, window->counter); - - /* Can't calculate this yet; not enough history */ - window->average_tpt = IWL_INVALID_VALUE; - - /* Should we stay with this modulation mode, - * or search for a new one? */ - rs_stay_in_table(lq_sta, false); - - return; - } - - /* If we are searching for better modulation mode, check success. */ - if (lq_sta->search_better_tbl) { - /* If good success, continue using the "search" mode; - * no need to send new link quality command, since we're - * continuing to use the setup that we've been trying. */ - if (window->average_tpt > lq_sta->last_tpt) { - IWL_DEBUG_RATE(mvm, - "SWITCHING TO NEW TABLE SR: %d " - "cur-tpt %d old-tpt %d\n", - window->success_ratio, - window->average_tpt, - lq_sta->last_tpt); - - /* Swap tables; "search" becomes "active" */ - lq_sta->active_tbl = active_tbl; - current_tpt = window->average_tpt; - /* Else poor success; go back to mode in "active" table */ - } else { - IWL_DEBUG_RATE(mvm, - "GOING BACK TO THE OLD TABLE: SR %d " - "cur-tpt %d old-tpt %d\n", - window->success_ratio, - window->average_tpt, - lq_sta->last_tpt); - - /* Nullify "search" table */ - rate->type = LQ_NONE; - - /* Revert to "active" table */ - active_tbl = lq_sta->active_tbl; - tbl = &(lq_sta->lq_info[active_tbl]); - - /* Revert to "active" rate and throughput info */ - index = tbl->rate.index; - current_tpt = lq_sta->last_tpt; - - /* Need to set up a new rate table in uCode */ - update_lq = 1; - } - - /* Either way, we've made a decision; modulation mode - * search is done, allow rate adjustment next time. */ - lq_sta->search_better_tbl = 0; - done_search = 1; /* Don't switch modes below! */ - goto lq_update; - } - - /* (Else) not in search of better modulation mode, try for better - * starting rate, while staying in this mode. */ - high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type); - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ - - sr = window->success_ratio; - - /* Collect measured throughputs for current and adjacent rates */ - current_tpt = window->average_tpt; - if (low != IWL_RATE_INVALID) - low_tpt = tbl->win[low].average_tpt; - if (high != IWL_RATE_INVALID) - high_tpt = tbl->win[high].average_tpt; - - IWL_DEBUG_RATE(mvm, - "%s: cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n", - rs_pretty_rate(rate), current_tpt, sr, - low, high, low_tpt, high_tpt); - - scale_action = rs_get_rate_action(mvm, tbl, sr, low, high, - current_tpt, low_tpt, high_tpt); - - /* Force a search in case BT doesn't like us being in MIMO */ - if (is_mimo(rate) && - !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) { - IWL_DEBUG_RATE(mvm, - "BT Coex forbids MIMO. Search for new config\n"); - rs_stay_in_table(lq_sta, true); - goto lq_update; - } - - switch (scale_action) { - case RS_ACTION_DOWNSCALE: - /* Decrease starting rate, update uCode's rate table */ - if (low != IWL_RATE_INVALID) { - update_lq = 1; - index = low; - } else { - IWL_DEBUG_RATE(mvm, - "At the bottom rate. Can't decrease\n"); - } - - break; - case RS_ACTION_UPSCALE: - /* Increase starting rate, update uCode's rate table */ - if (high != IWL_RATE_INVALID) { - update_lq = 1; - index = high; - } else { - IWL_DEBUG_RATE(mvm, - "At the top rate. Can't increase\n"); - } - - break; - case RS_ACTION_STAY: - /* No change */ - if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) - update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl); - break; - default: - break; - } - -lq_update: - /* Replace uCode's rate table for the destination station. */ - if (update_lq) { - tbl->rate.index = index; - if (IWL_MVM_RS_80_20_FAR_RANGE_TWEAK) - rs_tweak_rate_tbl(mvm, sta, lq_sta, tbl, scale_action); - rs_update_rate_tbl(mvm, sta, lq_sta, tbl); - } - - rs_stay_in_table(lq_sta, false); - - /* - * Search for new modulation mode if we're: - * 1) Not changing rates right now - * 2) Not just finishing up a search - * 3) Allowing a new search - */ - if (!update_lq && !done_search && - lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED - && window->counter) { - enum rs_column next_column; - - /* Save current throughput to compare with "search" throughput*/ - lq_sta->last_tpt = current_tpt; - - IWL_DEBUG_RATE(mvm, - "Start Search: update_lq %d done_search %d rs_state %d win->counter %d\n", - update_lq, done_search, lq_sta->rs_state, - window->counter); - - next_column = rs_get_next_column(mvm, lq_sta, sta, tbl); - if (next_column != RS_COLUMN_INVALID) { - int ret = rs_switch_to_column(mvm, lq_sta, sta, - next_column); - if (!ret) - lq_sta->search_better_tbl = 1; - } else { - IWL_DEBUG_RATE(mvm, - "No more columns to explore in search cycle. Go to RS_STATE_SEARCH_CYCLE_ENDED\n"); - lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_ENDED; - } - - /* If new "search" mode was selected, set up in uCode table */ - if (lq_sta->search_better_tbl) { - /* Access the "search" table, clear its history. */ - tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - rs_rate_scale_clear_tbl_windows(mvm, tbl); - - /* Use new "search" start rate */ - index = tbl->rate.index; - - rs_dump_rate(mvm, &tbl->rate, - "Switch to SEARCH TABLE:"); - rs_update_rate_tbl(mvm, sta, lq_sta, tbl); - } else { - done_search = 1; - } - } - - if (done_search && lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_ENDED) { - /* If the "active" (non-search) mode was legacy, - * and we've tried switching antennas, - * but we haven't been able to try HT modes (not available), - * stay with best antenna legacy modulation for a while - * before next round of mode comparisons. */ - tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); - if (is_legacy(&tbl1->rate)) { - IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n"); - - if (tid != IWL_MAX_TID_COUNT) { - tid_data = &sta_priv->tid_data[tid]; - if (tid_data->state != IWL_AGG_OFF) { - IWL_DEBUG_RATE(mvm, - "Stop aggregation on tid %d\n", - tid); - ieee80211_stop_tx_ba_session(sta, tid); - } - } - rs_set_stay_in_table(mvm, 1, lq_sta); - } else { - /* If we're in an HT mode, and all 3 mode switch actions - * have been tried and compared, stay in this best modulation - * mode for a while before next round of mode comparisons. */ - if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && - (lq_sta->tx_agg_tid_en & (1 << tid)) && - (tid != IWL_MAX_TID_COUNT)) { - tid_data = &sta_priv->tid_data[tid]; - if (tid_data->state == IWL_AGG_OFF) { - IWL_DEBUG_RATE(mvm, - "try to aggregate tid %d\n", - tid); - rs_tl_turn_on_agg(mvm, tid, - lq_sta, sta); - } - } - rs_set_stay_in_table(mvm, 0, lq_sta); - } - } -} - -struct rs_init_rate_info { - s8 rssi; - u8 rate_idx; -}; - -static const struct rs_init_rate_info rs_optimal_rates_24ghz_legacy[] = { - { -60, IWL_RATE_54M_INDEX }, - { -64, IWL_RATE_48M_INDEX }, - { -68, IWL_RATE_36M_INDEX }, - { -80, IWL_RATE_24M_INDEX }, - { -84, IWL_RATE_18M_INDEX }, - { -85, IWL_RATE_12M_INDEX }, - { -86, IWL_RATE_11M_INDEX }, - { -88, IWL_RATE_5M_INDEX }, - { -90, IWL_RATE_2M_INDEX }, - { S8_MIN, IWL_RATE_1M_INDEX }, -}; - -static const struct rs_init_rate_info rs_optimal_rates_5ghz_legacy[] = { - { -60, IWL_RATE_54M_INDEX }, - { -64, IWL_RATE_48M_INDEX }, - { -72, IWL_RATE_36M_INDEX }, - { -80, IWL_RATE_24M_INDEX }, - { -84, IWL_RATE_18M_INDEX }, - { -85, IWL_RATE_12M_INDEX }, - { -87, IWL_RATE_9M_INDEX }, - { S8_MIN, IWL_RATE_6M_INDEX }, -}; - -static const struct rs_init_rate_info rs_optimal_rates_ht[] = { - { -60, IWL_RATE_MCS_7_INDEX }, - { -64, IWL_RATE_MCS_6_INDEX }, - { -68, IWL_RATE_MCS_5_INDEX }, - { -72, IWL_RATE_MCS_4_INDEX }, - { -80, IWL_RATE_MCS_3_INDEX }, - { -84, IWL_RATE_MCS_2_INDEX }, - { -85, IWL_RATE_MCS_1_INDEX }, - { S8_MIN, IWL_RATE_MCS_0_INDEX}, -}; - -static const struct rs_init_rate_info rs_optimal_rates_vht_20mhz[] = { - { -60, IWL_RATE_MCS_8_INDEX }, - { -64, IWL_RATE_MCS_7_INDEX }, - { -68, IWL_RATE_MCS_6_INDEX }, - { -72, IWL_RATE_MCS_5_INDEX }, - { -80, IWL_RATE_MCS_4_INDEX }, - { -84, IWL_RATE_MCS_3_INDEX }, - { -85, IWL_RATE_MCS_2_INDEX }, - { -87, IWL_RATE_MCS_1_INDEX }, - { S8_MIN, IWL_RATE_MCS_0_INDEX}, -}; - -static const struct rs_init_rate_info rs_optimal_rates_vht_40_80mhz[] = { - { -60, IWL_RATE_MCS_9_INDEX }, - { -64, IWL_RATE_MCS_8_INDEX }, - { -68, IWL_RATE_MCS_7_INDEX }, - { -72, IWL_RATE_MCS_6_INDEX }, - { -80, IWL_RATE_MCS_5_INDEX }, - { -84, IWL_RATE_MCS_4_INDEX }, - { -85, IWL_RATE_MCS_3_INDEX }, - { -87, IWL_RATE_MCS_2_INDEX }, - { -88, IWL_RATE_MCS_1_INDEX }, - { S8_MIN, IWL_RATE_MCS_0_INDEX }, -}; - -/* Init the optimal rate based on STA caps - * This combined with rssi is used to report the last tx rate - * to userspace when we haven't transmitted enough frames. - */ -static void rs_init_optimal_rate(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta) -{ - struct rs_rate *rate = &lq_sta->optimal_rate; - - if (lq_sta->max_mimo2_rate_idx != IWL_RATE_INVALID) - rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; - else if (lq_sta->max_siso_rate_idx != IWL_RATE_INVALID) - rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; - else if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate->type = LQ_LEGACY_A; - else - rate->type = LQ_LEGACY_G; - - rate->bw = rs_bw_from_sta_bw(sta); - rate->sgi = rs_sgi_allow(mvm, sta, rate, NULL); - - /* ANT/LDPC/STBC aren't relevant for the rate reported to userspace */ - - if (is_mimo(rate)) { - lq_sta->optimal_rate_mask = lq_sta->active_mimo2_rate; - } else if (is_siso(rate)) { - lq_sta->optimal_rate_mask = lq_sta->active_siso_rate; - } else { - lq_sta->optimal_rate_mask = lq_sta->active_legacy_rate; - - if (lq_sta->band == IEEE80211_BAND_5GHZ) { - lq_sta->optimal_rates = rs_optimal_rates_5ghz_legacy; - lq_sta->optimal_nentries = - ARRAY_SIZE(rs_optimal_rates_5ghz_legacy); - } else { - lq_sta->optimal_rates = rs_optimal_rates_24ghz_legacy; - lq_sta->optimal_nentries = - ARRAY_SIZE(rs_optimal_rates_24ghz_legacy); - } - } - - if (is_vht(rate)) { - if (rate->bw == RATE_MCS_CHAN_WIDTH_20) { - lq_sta->optimal_rates = rs_optimal_rates_vht_20mhz; - lq_sta->optimal_nentries = - ARRAY_SIZE(rs_optimal_rates_vht_20mhz); - } else { - lq_sta->optimal_rates = rs_optimal_rates_vht_40_80mhz; - lq_sta->optimal_nentries = - ARRAY_SIZE(rs_optimal_rates_vht_40_80mhz); - } - } else if (is_ht(rate)) { - lq_sta->optimal_rates = rs_optimal_rates_ht; - lq_sta->optimal_nentries = ARRAY_SIZE(rs_optimal_rates_ht); - } -} - -/* Compute the optimal rate index based on RSSI */ -static struct rs_rate *rs_get_optimal_rate(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta) -{ - struct rs_rate *rate = &lq_sta->optimal_rate; - int i; - - rate->index = find_first_bit(&lq_sta->optimal_rate_mask, - BITS_PER_LONG); - - for (i = 0; i < lq_sta->optimal_nentries; i++) { - int rate_idx = lq_sta->optimal_rates[i].rate_idx; - - if ((lq_sta->pers.last_rssi >= lq_sta->optimal_rates[i].rssi) && - (BIT(rate_idx) & lq_sta->optimal_rate_mask)) { - rate->index = rate_idx; - break; - } - } - - return rate; -} - -/* Choose an initial legacy rate and antenna to use based on the RSSI - * of last Rx - */ -static void rs_get_initial_rate(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - enum ieee80211_band band, - struct rs_rate *rate) -{ - int i, nentries; - s8 best_rssi = S8_MIN; - u8 best_ant = ANT_NONE; - u8 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); - const struct rs_init_rate_info *initial_rates; - - for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) { - if (!(lq_sta->pers.chains & BIT(i))) - continue; - - if (lq_sta->pers.chain_signal[i] > best_rssi) { - best_rssi = lq_sta->pers.chain_signal[i]; - best_ant = BIT(i); - } - } - - IWL_DEBUG_RATE(mvm, "Best ANT: %s Best RSSI: %d\n", - rs_pretty_ant(best_ant), best_rssi); - - if (best_ant != ANT_A && best_ant != ANT_B) - rate->ant = first_antenna(valid_tx_ant); - else - rate->ant = best_ant; - - rate->sgi = false; - rate->ldpc = false; - rate->bw = RATE_MCS_CHAN_WIDTH_20; - - rate->index = find_first_bit(&lq_sta->active_legacy_rate, - BITS_PER_LONG); - - if (band == IEEE80211_BAND_5GHZ) { - rate->type = LQ_LEGACY_A; - initial_rates = rs_optimal_rates_5ghz_legacy; - nentries = ARRAY_SIZE(rs_optimal_rates_5ghz_legacy); - } else { - rate->type = LQ_LEGACY_G; - initial_rates = rs_optimal_rates_24ghz_legacy; - nentries = ARRAY_SIZE(rs_optimal_rates_24ghz_legacy); - } - - if (IWL_MVM_RS_RSSI_BASED_INIT_RATE) { - for (i = 0; i < nentries; i++) { - int rate_idx = initial_rates[i].rate_idx; - if ((best_rssi >= initial_rates[i].rssi) && - (BIT(rate_idx) & lq_sta->active_legacy_rate)) { - rate->index = rate_idx; - break; - } - } - } - - IWL_DEBUG_RATE(mvm, "rate_idx %d ANT %s\n", rate->index, - rs_pretty_ant(rate->ant)); -} - -/* Save info about RSSI of last Rx */ -void rs_update_last_rssi(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_rx_status *rx_status) -{ - int i; - - lq_sta->pers.chains = rx_status->chains; - lq_sta->pers.chain_signal[0] = rx_status->chain_signal[0]; - lq_sta->pers.chain_signal[1] = rx_status->chain_signal[1]; - lq_sta->pers.chain_signal[2] = rx_status->chain_signal[2]; - lq_sta->pers.last_rssi = S8_MIN; - - for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) { - if (!(lq_sta->pers.chains & BIT(i))) - continue; - - if (lq_sta->pers.chain_signal[i] > lq_sta->pers.last_rssi) - lq_sta->pers.last_rssi = lq_sta->pers.chain_signal[i]; - } -} - -/** - * rs_initialize_lq - Initialize a station's hardware rate table - * - * The uCode's station table contains a table of fallback rates - * for automatic fallback during transmission. - * - * NOTE: This sets up a default set of values. These will be replaced later - * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of - * rc80211_simple. - * - * NOTE: Run REPLY_ADD_STA command to set up station table entry, before - * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, - * which requires station table entry to exist). - */ -static void rs_initialize_lq(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - enum ieee80211_band band, - bool init) -{ - struct iwl_scale_tbl_info *tbl; - struct rs_rate *rate; - u8 active_tbl = 0; - - if (!sta || !lq_sta) - return; - - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - rate = &tbl->rate; - - rs_get_initial_rate(mvm, lq_sta, band, rate); - rs_init_optimal_rate(mvm, sta, lq_sta); - - WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B); - if (rate->ant == ANT_A) - tbl->column = RS_COLUMN_LEGACY_ANT_A; - else - tbl->column = RS_COLUMN_LEGACY_ANT_B; - - rs_set_expected_tpt_table(lq_sta, tbl); - rs_fill_lq_cmd(mvm, sta, lq_sta, rate); - /* TODO restore station should remember the lq cmd */ - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init); -} - -static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, - struct ieee80211_tx_rate_control *txrc) -{ - struct sk_buff *skb = txrc->skb; - struct iwl_op_mode *op_mode __maybe_unused = - (struct iwl_op_mode *)mvm_r; - struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_lq_sta *lq_sta = mvm_sta; - struct rs_rate *optimal_rate; - u32 last_ucode_rate; - - if (sta && !iwl_mvm_sta_from_mac80211(sta)->vif) { - /* if vif isn't initialized mvm doesn't know about - * this station, so don't do anything with the it - */ - sta = NULL; - mvm_sta = NULL; - } - - /* TODO: handle rate_idx_mask and rate_idx_mcs_mask */ - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (lq_sta && !lq_sta->pers.drv) { - IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n"); - mvm_sta = NULL; - } - - /* Send management frames and NO_ACK data using lowest rate. */ - if (rate_control_send_low(sta, mvm_sta, txrc)) - return; - - iwl_mvm_hwrate_to_tx_rate(lq_sta->last_rate_n_flags, - info->band, &info->control.rates[0]); - info->control.rates[0].count = 1; - - /* Report the optimal rate based on rssi and STA caps if we haven't - * converged yet (too little traffic) or exploring other modulations - */ - if (lq_sta->rs_state != RS_STATE_STAY_IN_COLUMN) { - optimal_rate = rs_get_optimal_rate(mvm, lq_sta); - last_ucode_rate = ucode_rate_from_rs_rate(mvm, - optimal_rate); - iwl_mvm_hwrate_to_tx_rate(last_ucode_rate, info->band, - &txrc->reported_rate); - } -} - -static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, - gfp_t gfp) -{ - struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); - struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate; - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; - - IWL_DEBUG_RATE(mvm, "create station rate scale window\n"); - - lq_sta->pers.drv = mvm; -#ifdef CONFIG_MAC80211_DEBUGFS - lq_sta->pers.dbg_fixed_rate = 0; - lq_sta->pers.dbg_fixed_txp_reduction = TPC_INVALID; - lq_sta->pers.ss_force = RS_SS_FORCE_NONE; -#endif - lq_sta->pers.chains = 0; - memset(lq_sta->pers.chain_signal, 0, sizeof(lq_sta->pers.chain_signal)); - lq_sta->pers.last_rssi = S8_MIN; - - return &sta_priv->lq_sta; -} - -static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap, - int nss) -{ - u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & - (0x3 << (2 * (nss - 1))); - rx_mcs >>= (2 * (nss - 1)); - - if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_7) - return IWL_RATE_MCS_7_INDEX; - else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_8) - return IWL_RATE_MCS_8_INDEX; - else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_9) - return IWL_RATE_MCS_9_INDEX; - - WARN_ON_ONCE(rx_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED); - return -1; -} - -static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, - struct ieee80211_sta_vht_cap *vht_cap, - struct iwl_lq_sta *lq_sta) -{ - int i; - int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1); - - if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { - for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { - if (i == IWL_RATE_9M_INDEX) - continue; - - /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ - if (i == IWL_RATE_MCS_9_INDEX && - sta->bandwidth == IEEE80211_STA_RX_BW_20) - continue; - - lq_sta->active_siso_rate |= BIT(i); - } - } - - if (sta->rx_nss < 2) - return; - - highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2); - if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { - for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { - if (i == IWL_RATE_9M_INDEX) - continue; - - /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ - if (i == IWL_RATE_MCS_9_INDEX && - sta->bandwidth == IEEE80211_STA_RX_BW_20) - continue; - - lq_sta->active_mimo2_rate |= BIT(i); - } - } -} - -static void rs_ht_init(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta_ht_cap *ht_cap) -{ - /* active_siso_rate mask includes 9 MBits (bit 5), - * and CCK (bits 0-3), supp_rates[] does not; - * shift to convert format, force 9 MBits off. - */ - lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; - lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; - lq_sta->active_siso_rate &= ~((u16)0x2); - lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; - - lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; - lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; - lq_sta->active_mimo2_rate &= ~((u16)0x2); - lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; - - if (mvm->cfg->ht_params->ldpc && - (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)) - lq_sta->ldpc = true; - - if (mvm->cfg->ht_params->stbc && - (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && - (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) - lq_sta->stbc_capable = true; - - lq_sta->is_vht = false; -} - -static void rs_vht_init(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta_vht_cap *vht_cap) -{ - rs_vht_set_enabled_rates(sta, vht_cap, lq_sta); - - if (mvm->cfg->ht_params->ldpc && - (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)) - lq_sta->ldpc = true; - - if (mvm->cfg->ht_params->stbc && - (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && - (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)) - lq_sta->stbc_capable = true; - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEAMFORMER) && - (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && - (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) - lq_sta->bfer_capable = true; - - lq_sta->is_vht = true; -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm) -{ - spin_lock_bh(&mvm->drv_stats_lock); - memset(&mvm->drv_rx_stats, 0, sizeof(mvm->drv_rx_stats)); - spin_unlock_bh(&mvm->drv_stats_lock); -} - -void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg) -{ - u8 nss = 0, mcs = 0; - - spin_lock(&mvm->drv_stats_lock); - - if (agg) - mvm->drv_rx_stats.agg_frames++; - - mvm->drv_rx_stats.success_frames++; - - switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { - case RATE_MCS_CHAN_WIDTH_20: - mvm->drv_rx_stats.bw_20_frames++; - break; - case RATE_MCS_CHAN_WIDTH_40: - mvm->drv_rx_stats.bw_40_frames++; - break; - case RATE_MCS_CHAN_WIDTH_80: - mvm->drv_rx_stats.bw_80_frames++; - break; - default: - WARN_ONCE(1, "bad BW. rate 0x%x", rate); - } - - if (rate & RATE_MCS_HT_MSK) { - mvm->drv_rx_stats.ht_frames++; - mcs = rate & RATE_HT_MCS_RATE_CODE_MSK; - nss = ((rate & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS) + 1; - } else if (rate & RATE_MCS_VHT_MSK) { - mvm->drv_rx_stats.vht_frames++; - mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; - nss = ((rate & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1; - } else { - mvm->drv_rx_stats.legacy_frames++; - } - - if (nss == 1) - mvm->drv_rx_stats.siso_frames++; - else if (nss == 2) - mvm->drv_rx_stats.mimo2_frames++; - - if (rate & RATE_MCS_SGI_MSK) - mvm->drv_rx_stats.sgi_frames++; - else - mvm->drv_rx_stats.ngi_frames++; - - mvm->drv_rx_stats.last_rates[mvm->drv_rx_stats.last_frame_idx] = rate; - mvm->drv_rx_stats.last_frame_idx = - (mvm->drv_rx_stats.last_frame_idx + 1) % - ARRAY_SIZE(mvm->drv_rx_stats.last_rates); - - spin_unlock(&mvm->drv_stats_lock); -} -#endif - -/* - * Called after adding a new station to initialize rate scaling - */ -void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum ieee80211_band band, bool init) -{ - int i, j; - struct ieee80211_hw *hw = mvm->hw; - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; - struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); - struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; - struct ieee80211_supported_band *sband; - unsigned long supp; /* must be unsigned long for for_each_set_bit */ - - /* clear all non-persistent lq data */ - memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); - - sband = hw->wiphy->bands[band]; - - lq_sta->lq.sta_id = sta_priv->sta_id; - - for (j = 0; j < LQ_SIZE; j++) - rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]); - - lq_sta->flush_timer = 0; - lq_sta->last_tx = jiffies; - - IWL_DEBUG_RATE(mvm, - "LQ: *** rate scale station global init for station %d ***\n", - sta_priv->sta_id); - /* TODO: what is a good starting rate for STA? About middle? Maybe not - * the lowest or the highest rate.. Could consider using RSSI from - * previous packets? Need to have IEEE 802.1X auth succeed immediately - * after assoc.. */ - - lq_sta->missed_rate_counter = IWL_MVM_RS_MISSED_RATE_MAX; - lq_sta->band = sband->band; - /* - * active legacy rates as per supported rates bitmap - */ - supp = sta->supp_rates[sband->band]; - lq_sta->active_legacy_rate = 0; - for_each_set_bit(i, &supp, BITS_PER_LONG) - lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); - - /* TODO: should probably account for rx_highest for both HT/VHT */ - if (!vht_cap || !vht_cap->vht_supported) - rs_ht_init(mvm, sta, lq_sta, ht_cap); - else - rs_vht_init(mvm, sta, lq_sta, vht_cap); - - lq_sta->max_legacy_rate_idx = - rs_get_max_rate_from_mask(lq_sta->active_legacy_rate); - lq_sta->max_siso_rate_idx = - rs_get_max_rate_from_mask(lq_sta->active_siso_rate); - lq_sta->max_mimo2_rate_idx = - rs_get_max_rate_from_mask(lq_sta->active_mimo2_rate); - - IWL_DEBUG_RATE(mvm, - "LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC=%d BFER=%d\n", - lq_sta->active_legacy_rate, - lq_sta->active_siso_rate, - lq_sta->active_mimo2_rate, - lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc_capable, - lq_sta->bfer_capable); - IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n", - lq_sta->max_legacy_rate_idx, - lq_sta->max_siso_rate_idx, - lq_sta->max_mimo2_rate_idx); - - /* These values will be overridden later */ - lq_sta->lq.single_stream_ant_msk = - first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); - lq_sta->lq.dual_stream_ant_msk = ANT_AB; - - /* as default allow aggregation for all tids */ - lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; - lq_sta->is_agg = 0; -#ifdef CONFIG_IWLWIFI_DEBUGFS - iwl_mvm_reset_frame_stats(mvm); -#endif - rs_initialize_lq(mvm, sta, lq_sta, band, init); -} - -static void rs_rate_update(void *mvm_r, - struct ieee80211_supported_band *sband, - struct cfg80211_chan_def *chandef, - struct ieee80211_sta *sta, void *priv_sta, - u32 changed) -{ - u8 tid; - struct iwl_op_mode *op_mode = - (struct iwl_op_mode *)mvm_r; - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - - if (!iwl_mvm_sta_from_mac80211(sta)->vif) - return; - - /* Stop any ongoing aggregations as rs starts off assuming no agg */ - for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) - ieee80211_stop_tx_ba_session(sta, tid); - - iwl_mvm_rs_rate_init(mvm, sta, sband->band, false); -} - -#ifdef CONFIG_MAC80211_DEBUGFS -static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm, - struct iwl_lq_cmd *lq_cmd, - enum ieee80211_band band, - u32 ucode_rate) -{ - struct rs_rate rate; - int i; - int num_rates = ARRAY_SIZE(lq_cmd->rs_table); - __le32 ucode_rate_le32 = cpu_to_le32(ucode_rate); - u8 ant = (ucode_rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; - - for (i = 0; i < num_rates; i++) - lq_cmd->rs_table[i] = ucode_rate_le32; - - rs_rate_from_ucode_rate(ucode_rate, band, &rate); - - if (is_mimo(&rate)) - lq_cmd->mimo_delim = num_rates - 1; - else - lq_cmd->mimo_delim = 0; - - lq_cmd->reduced_tpc = 0; - - if (num_of_ant(ant) == 1) - lq_cmd->single_stream_ant_msk = ant; - - lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; -} -#endif /* CONFIG_MAC80211_DEBUGFS */ - -static void rs_fill_rates_for_column(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct rs_rate *rate, - __le32 *rs_table, int *rs_table_index, - int num_rates, int num_retries, - u8 valid_tx_ant, bool toggle_ant) -{ - int i, j; - __le32 ucode_rate; - bool bottom_reached = false; - int prev_rate_idx = rate->index; - int end = LINK_QUAL_MAX_RETRY_NUM; - int index = *rs_table_index; - - for (i = 0; i < num_rates && index < end; i++) { - for (j = 0; j < num_retries && index < end; j++, index++) { - ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, - rate)); - rs_table[index] = ucode_rate; - if (toggle_ant) - rs_toggle_antenna(valid_tx_ant, rate); - } - - prev_rate_idx = rate->index; - bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate); - if (bottom_reached && !is_legacy(rate)) - break; - } - - if (!bottom_reached && !is_legacy(rate)) - rate->index = prev_rate_idx; - - *rs_table_index = index; -} - -/* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI - * column the rate table should look like this: - * - * rate[0] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI - * rate[1] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI - * rate[2] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI - * rate[3] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI - * rate[4] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI - * rate[5] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI - * rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI - * rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI - * rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI - * rate[9] 0x800B Legacy | ANT: B Rate: 36 Mbps - * rate[10] 0x4009 Legacy | ANT: A Rate: 24 Mbps - * rate[11] 0x8007 Legacy | ANT: B Rate: 18 Mbps - * rate[12] 0x4005 Legacy | ANT: A Rate: 12 Mbps - * rate[13] 0x800F Legacy | ANT: B Rate: 9 Mbps - * rate[14] 0x400D Legacy | ANT: A Rate: 6 Mbps - * rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps - */ -static void rs_build_rates_table(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - const struct rs_rate *initial_rate) -{ - struct rs_rate rate; - int num_rates, num_retries, index = 0; - u8 valid_tx_ant = 0; - struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; - bool toggle_ant = false; - - memcpy(&rate, initial_rate, sizeof(rate)); - - valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); - - /* TODO: remove old API when min FW API hits 14 */ - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS) && - rs_stbc_allow(mvm, sta, lq_sta)) - rate.stbc = true; - - if (is_siso(&rate)) { - num_rates = IWL_MVM_RS_INITIAL_SISO_NUM_RATES; - num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; - } else if (is_mimo(&rate)) { - num_rates = IWL_MVM_RS_INITIAL_MIMO_NUM_RATES; - num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; - } else { - num_rates = IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES; - num_retries = IWL_MVM_RS_INITIAL_LEGACY_RETRIES; - toggle_ant = true; - } - - rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, - num_rates, num_retries, valid_tx_ant, - toggle_ant); - - rs_get_lower_rate_down_column(lq_sta, &rate); - - if (is_siso(&rate)) { - num_rates = IWL_MVM_RS_SECONDARY_SISO_NUM_RATES; - num_retries = IWL_MVM_RS_SECONDARY_SISO_RETRIES; - lq_cmd->mimo_delim = index; - } else if (is_legacy(&rate)) { - num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; - num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES; - } else { - WARN_ON_ONCE(1); - } - - toggle_ant = true; - - rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, - num_rates, num_retries, valid_tx_ant, - toggle_ant); - - rs_get_lower_rate_down_column(lq_sta, &rate); - - num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; - num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES; - - rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, - num_rates, num_retries, valid_tx_ant, - toggle_ant); - -} - -struct rs_bfer_active_iter_data { - struct ieee80211_sta *exclude_sta; - struct iwl_mvm_sta *bfer_mvmsta; -}; - -static void rs_bfer_active_iter(void *_data, - struct ieee80211_sta *sta) -{ - struct rs_bfer_active_iter_data *data = _data; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.lq; - u32 ss_params = le32_to_cpu(lq_cmd->ss_params); - - if (sta == data->exclude_sta) - return; - - /* The current sta has BFER allowed */ - if (ss_params & LQ_SS_BFER_ALLOWED) { - WARN_ON_ONCE(data->bfer_mvmsta != NULL); - - data->bfer_mvmsta = mvmsta; - } -} - -static int rs_bfer_priority(struct iwl_mvm_sta *sta) -{ - int prio = -1; - enum nl80211_iftype viftype = ieee80211_vif_type_p2p(sta->vif); - - switch (viftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: - prio = 3; - break; - case NL80211_IFTYPE_P2P_CLIENT: - prio = 2; - break; - case NL80211_IFTYPE_STATION: - prio = 1; - break; - default: - WARN_ONCE(true, "viftype %d sta_id %d", viftype, sta->sta_id); - prio = -1; - } - - return prio; -} - -/* Returns >0 if sta1 has a higher BFER priority compared to sta2 */ -static int rs_bfer_priority_cmp(struct iwl_mvm_sta *sta1, - struct iwl_mvm_sta *sta2) -{ - int prio1 = rs_bfer_priority(sta1); - int prio2 = rs_bfer_priority(sta2); - - if (prio1 > prio2) - return 1; - if (prio1 < prio2) - return -1; - return 0; -} - -static void rs_set_lq_ss_params(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - const struct rs_rate *initial_rate) -{ - struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct rs_bfer_active_iter_data data = { - .exclude_sta = sta, - .bfer_mvmsta = NULL, - }; - struct iwl_mvm_sta *bfer_mvmsta = NULL; - u32 ss_params = LQ_SS_PARAMS_VALID; - - if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) - goto out; - -#ifdef CONFIG_MAC80211_DEBUGFS - /* Check if forcing the decision is configured. - * Note that SISO is forced by not allowing STBC or BFER - */ - if (lq_sta->pers.ss_force == RS_SS_FORCE_STBC) - ss_params |= (LQ_SS_STBC_1SS_ALLOWED | LQ_SS_FORCE); - else if (lq_sta->pers.ss_force == RS_SS_FORCE_BFER) - ss_params |= (LQ_SS_BFER_ALLOWED | LQ_SS_FORCE); - - if (lq_sta->pers.ss_force != RS_SS_FORCE_NONE) { - IWL_DEBUG_RATE(mvm, "Forcing single stream Tx decision %d\n", - lq_sta->pers.ss_force); - goto out; - } -#endif - - if (lq_sta->stbc_capable) - ss_params |= LQ_SS_STBC_1SS_ALLOWED; - - if (!lq_sta->bfer_capable) - goto out; - - ieee80211_iterate_stations_atomic(mvm->hw, - rs_bfer_active_iter, - &data); - bfer_mvmsta = data.bfer_mvmsta; - - /* This code is safe as it doesn't run concurrently for different - * stations. This is guaranteed by the fact that calls to - * ieee80211_tx_status wouldn't run concurrently for a single HW. - */ - if (!bfer_mvmsta) { - IWL_DEBUG_RATE(mvm, "No sta with BFER allowed found. Allow\n"); - - ss_params |= LQ_SS_BFER_ALLOWED; - goto out; - } - - IWL_DEBUG_RATE(mvm, "Found existing sta %d with BFER activated\n", - bfer_mvmsta->sta_id); - - /* Disallow BFER on another STA if active and we're a higher priority */ - if (rs_bfer_priority_cmp(mvmsta, bfer_mvmsta) > 0) { - struct iwl_lq_cmd *bfersta_lq_cmd = &bfer_mvmsta->lq_sta.lq; - u32 bfersta_ss_params = le32_to_cpu(bfersta_lq_cmd->ss_params); - - bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED; - bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params); - iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd, false); - - ss_params |= LQ_SS_BFER_ALLOWED; - IWL_DEBUG_RATE(mvm, - "Lower priority BFER sta found (%d). Switch BFER\n", - bfer_mvmsta->sta_id); - } -out: - lq_cmd->ss_params = cpu_to_le32(ss_params); -} - -static void rs_fill_lq_cmd(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, - const struct rs_rate *initial_rate) -{ - struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; - struct iwl_mvm_sta *mvmsta; - struct iwl_mvm_vif *mvmvif; - - lq_cmd->agg_disable_start_th = IWL_MVM_RS_AGG_DISABLE_START; - lq_cmd->agg_time_limit = - cpu_to_le16(IWL_MVM_RS_AGG_TIME_LIMIT); - -#ifdef CONFIG_MAC80211_DEBUGFS - if (lq_sta->pers.dbg_fixed_rate) { - rs_build_rates_table_from_fixed(mvm, lq_cmd, - lq_sta->band, - lq_sta->pers.dbg_fixed_rate); - return; - } -#endif - if (WARN_ON_ONCE(!sta || !initial_rate)) - return; - - rs_build_rates_table(mvm, sta, lq_sta, initial_rate); - - if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_LQ_SS_PARAMS)) - rs_set_lq_ss_params(mvm, sta, lq_sta, initial_rate); - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - - if (num_of_ant(initial_rate->ant) == 1) - lq_cmd->single_stream_ant_msk = initial_rate->ant; - - lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize; - - /* - * In case of low latency, tell the firmware to leave a frame in the - * Tx Fifo so that it can start a transaction in the same TxOP. This - * basically allows the firmware to send bursts. - */ - if (iwl_mvm_vif_low_latency(mvmvif)) { - lq_cmd->agg_frame_cnt_limit--; - - if (mvm->low_latency_agg_frame_limit) - lq_cmd->agg_frame_cnt_limit = - min(lq_cmd->agg_frame_cnt_limit, - mvm->low_latency_agg_frame_limit); - } - - if (mvmsta->vif->p2p) - lq_cmd->flags |= LQ_FLAG_USE_RTS_MSK; - - lq_cmd->agg_time_limit = - cpu_to_le16(iwl_mvm_coex_agg_time_limit(mvm, sta)); -} - -static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) -{ - return hw->priv; -} -/* rate scale requires free function to be implemented */ -static void rs_free(void *mvm_rate) -{ - return; -} - -static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta, - void *mvm_sta) -{ - struct iwl_op_mode *op_mode __maybe_unused = mvm_r; - struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); - - IWL_DEBUG_RATE(mvm, "enter\n"); - IWL_DEBUG_RATE(mvm, "leave\n"); -} - -#ifdef CONFIG_MAC80211_DEBUGFS -int rs_pretty_print_rate(char *buf, const u32 rate) -{ - - char *type, *bw; - u8 mcs = 0, nss = 0; - u8 ant = (rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; - - if (!(rate & RATE_MCS_HT_MSK) && - !(rate & RATE_MCS_VHT_MSK)) { - int index = iwl_hwrate_to_plcp_idx(rate); - - return sprintf(buf, "Legacy | ANT: %s Rate: %s Mbps\n", - rs_pretty_ant(ant), - index == IWL_RATE_INVALID ? "BAD" : - iwl_rate_mcs[index].mbps); - } - - if (rate & RATE_MCS_VHT_MSK) { - type = "VHT"; - mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; - nss = ((rate & RATE_VHT_MCS_NSS_MSK) - >> RATE_VHT_MCS_NSS_POS) + 1; - } else if (rate & RATE_MCS_HT_MSK) { - type = "HT"; - mcs = rate & RATE_HT_MCS_INDEX_MSK; - } else { - type = "Unknown"; /* shouldn't happen */ - } - - switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { - case RATE_MCS_CHAN_WIDTH_20: - bw = "20Mhz"; - break; - case RATE_MCS_CHAN_WIDTH_40: - bw = "40Mhz"; - break; - case RATE_MCS_CHAN_WIDTH_80: - bw = "80Mhz"; - break; - case RATE_MCS_CHAN_WIDTH_160: - bw = "160Mhz"; - break; - default: - bw = "BAD BW"; - } - - return sprintf(buf, "%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s\n", - type, rs_pretty_ant(ant), bw, mcs, nss, - (rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ", - (rate & RATE_MCS_HT_STBC_MSK) ? "STBC " : "", - (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "", - (rate & RATE_MCS_BF_MSK) ? "BF " : "", - (rate & RATE_MCS_ZLF_MSK) ? "ZLF " : ""); -} - -/** - * Program the device to use fixed rate for frame transmit - * This is for debugging/testing only - * once the device start use fixed rate, we need to reload the module - * to being back the normal operation. - */ -static void rs_program_fix_rate(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta) -{ - lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ - lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - - IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n", - lq_sta->lq.sta_id, lq_sta->pers.dbg_fixed_rate); - - if (lq_sta->pers.dbg_fixed_rate) { - rs_fill_lq_cmd(mvm, NULL, lq_sta, NULL); - iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false); - } -} - -static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct iwl_lq_sta *lq_sta = file->private_data; - struct iwl_mvm *mvm; - char buf[64]; - size_t buf_size; - u32 parsed_rate; - - mvm = lq_sta->pers.drv; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (sscanf(buf, "%x", &parsed_rate) == 1) - lq_sta->pers.dbg_fixed_rate = parsed_rate; - else - lq_sta->pers.dbg_fixed_rate = 0; - - rs_program_fix_rate(mvm, lq_sta); - - return count; -} - -static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - char *buff; - int desc = 0; - int i = 0; - ssize_t ret; - - struct iwl_lq_sta *lq_sta = file->private_data; - struct iwl_mvm *mvm; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct rs_rate *rate = &tbl->rate; - u32 ss_params; - mvm = lq_sta->pers.drv; - buff = kmalloc(2048, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); - desc += sprintf(buff+desc, "failed=%d success=%d rate=0%lX\n", - lq_sta->total_failed, lq_sta->total_success, - lq_sta->active_legacy_rate); - desc += sprintf(buff+desc, "fixed rate 0x%X\n", - lq_sta->pers.dbg_fixed_rate); - desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", - (iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "", - (iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "", - (iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : ""); - desc += sprintf(buff+desc, "lq type %s\n", - (is_legacy(rate)) ? "legacy" : - is_vht(rate) ? "VHT" : "HT"); - if (!is_legacy(rate)) { - desc += sprintf(buff + desc, " %s", - (is_siso(rate)) ? "SISO" : "MIMO2"); - desc += sprintf(buff + desc, " %s", - (is_ht20(rate)) ? "20MHz" : - (is_ht40(rate)) ? "40MHz" : - (is_ht80(rate)) ? "80Mhz" : "BAD BW"); - desc += sprintf(buff + desc, " %s %s %s\n", - (rate->sgi) ? "SGI" : "NGI", - (rate->ldpc) ? "LDPC" : "BCC", - (lq_sta->is_agg) ? "AGG on" : ""); - } - desc += sprintf(buff+desc, "last tx rate=0x%X\n", - lq_sta->last_rate_n_flags); - desc += sprintf(buff+desc, - "general: flags=0x%X mimo-d=%d s-ant=0x%x d-ant=0x%x\n", - lq_sta->lq.flags, - lq_sta->lq.mimo_delim, - lq_sta->lq.single_stream_ant_msk, - lq_sta->lq.dual_stream_ant_msk); - - desc += sprintf(buff+desc, - "agg: time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", - le16_to_cpu(lq_sta->lq.agg_time_limit), - lq_sta->lq.agg_disable_start_th, - lq_sta->lq.agg_frame_cnt_limit); - - desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc); - ss_params = le32_to_cpu(lq_sta->lq.ss_params); - desc += sprintf(buff+desc, "single stream params: %s%s%s%s\n", - (ss_params & LQ_SS_PARAMS_VALID) ? - "VALID" : "INVALID", - (ss_params & LQ_SS_BFER_ALLOWED) ? - ", BFER" : "", - (ss_params & LQ_SS_STBC_1SS_ALLOWED) ? - ", STBC" : "", - (ss_params & LQ_SS_FORCE) ? - ", FORCE" : ""); - desc += sprintf(buff+desc, - "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", - lq_sta->lq.initial_rate_index[0], - lq_sta->lq.initial_rate_index[1], - lq_sta->lq.initial_rate_index[2], - lq_sta->lq.initial_rate_index[3]); - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]); - - desc += sprintf(buff+desc, " rate[%d] 0x%X ", i, r); - desc += rs_pretty_print_rate(buff+desc, r); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); - kfree(buff); - return ret; -} - -static const struct file_operations rs_sta_dbgfs_scale_table_ops = { - .write = rs_sta_dbgfs_scale_table_write, - .read = rs_sta_dbgfs_scale_table_read, - .open = simple_open, - .llseek = default_llseek, -}; -static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - char *buff; - int desc = 0; - int i, j; - ssize_t ret; - struct iwl_scale_tbl_info *tbl; - struct rs_rate *rate; - struct iwl_lq_sta *lq_sta = file->private_data; - - buff = kmalloc(1024, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - for (i = 0; i < LQ_SIZE; i++) { - tbl = &(lq_sta->lq_info[i]); - rate = &tbl->rate; - desc += sprintf(buff+desc, - "%s type=%d SGI=%d BW=%s DUP=0\n" - "index=%d\n", - lq_sta->active_tbl == i ? "*" : "x", - rate->type, - rate->sgi, - is_ht20(rate) ? "20Mhz" : - is_ht40(rate) ? "40Mhz" : - is_ht80(rate) ? "80Mhz" : "ERR", - rate->index); - for (j = 0; j < IWL_RATE_COUNT; j++) { - desc += sprintf(buff+desc, - "counter=%d success=%d %%=%d\n", - tbl->win[j].counter, - tbl->win[j].success_counter, - tbl->win[j].success_ratio); - } - } - ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); - kfree(buff); - return ret; -} - -static const struct file_operations rs_sta_dbgfs_stats_table_ops = { - .read = rs_sta_dbgfs_stats_table_read, - .open = simple_open, - .llseek = default_llseek, -}; - -static ssize_t rs_sta_dbgfs_drv_tx_stats_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - static const char * const column_name[] = { - [RS_COLUMN_LEGACY_ANT_A] = "LEGACY_ANT_A", - [RS_COLUMN_LEGACY_ANT_B] = "LEGACY_ANT_B", - [RS_COLUMN_SISO_ANT_A] = "SISO_ANT_A", - [RS_COLUMN_SISO_ANT_B] = "SISO_ANT_B", - [RS_COLUMN_SISO_ANT_A_SGI] = "SISO_ANT_A_SGI", - [RS_COLUMN_SISO_ANT_B_SGI] = "SISO_ANT_B_SGI", - [RS_COLUMN_MIMO2] = "MIMO2", - [RS_COLUMN_MIMO2_SGI] = "MIMO2_SGI", - }; - - static const char * const rate_name[] = { - [IWL_RATE_1M_INDEX] = "1M", - [IWL_RATE_2M_INDEX] = "2M", - [IWL_RATE_5M_INDEX] = "5.5M", - [IWL_RATE_11M_INDEX] = "11M", - [IWL_RATE_6M_INDEX] = "6M|MCS0", - [IWL_RATE_9M_INDEX] = "9M", - [IWL_RATE_12M_INDEX] = "12M|MCS1", - [IWL_RATE_18M_INDEX] = "18M|MCS2", - [IWL_RATE_24M_INDEX] = "24M|MCS3", - [IWL_RATE_36M_INDEX] = "36M|MCS4", - [IWL_RATE_48M_INDEX] = "48M|MCS5", - [IWL_RATE_54M_INDEX] = "54M|MCS6", - [IWL_RATE_MCS_7_INDEX] = "MCS7", - [IWL_RATE_MCS_8_INDEX] = "MCS8", - [IWL_RATE_MCS_9_INDEX] = "MCS9", - }; - - char *buff, *pos, *endpos; - int col, rate; - ssize_t ret; - struct iwl_lq_sta *lq_sta = file->private_data; - struct rs_rate_stats *stats; - static const size_t bufsz = 1024; - - buff = kmalloc(bufsz, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - pos = buff; - endpos = pos + bufsz; - - pos += scnprintf(pos, endpos - pos, "COLUMN,"); - for (rate = 0; rate < IWL_RATE_COUNT; rate++) - pos += scnprintf(pos, endpos - pos, "%s,", rate_name[rate]); - pos += scnprintf(pos, endpos - pos, "\n"); - - for (col = 0; col < RS_COLUMN_COUNT; col++) { - pos += scnprintf(pos, endpos - pos, - "%s,", column_name[col]); - - for (rate = 0; rate < IWL_RATE_COUNT; rate++) { - stats = &(lq_sta->pers.tx_stats[col][rate]); - pos += scnprintf(pos, endpos - pos, - "%llu/%llu,", - stats->success, - stats->total); - } - pos += scnprintf(pos, endpos - pos, "\n"); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); - kfree(buff); - return ret; -} - -static ssize_t rs_sta_dbgfs_drv_tx_stats_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_lq_sta *lq_sta = file->private_data; - memset(lq_sta->pers.tx_stats, 0, sizeof(lq_sta->pers.tx_stats)); - - return count; -} - -static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = { - .read = rs_sta_dbgfs_drv_tx_stats_read, - .write = rs_sta_dbgfs_drv_tx_stats_write, - .open = simple_open, - .llseek = default_llseek, -}; - -static ssize_t iwl_dbgfs_ss_force_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_lq_sta *lq_sta = file->private_data; - char buf[12]; - int bufsz = sizeof(buf); - int pos = 0; - static const char * const ss_force_name[] = { - [RS_SS_FORCE_NONE] = "none", - [RS_SS_FORCE_STBC] = "stbc", - [RS_SS_FORCE_BFER] = "bfer", - [RS_SS_FORCE_SISO] = "siso", - }; - - pos += scnprintf(buf+pos, bufsz-pos, "%s\n", - ss_force_name[lq_sta->pers.ss_force]); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = lq_sta->pers.drv; - int ret = 0; - - if (!strncmp("none", buf, 4)) { - lq_sta->pers.ss_force = RS_SS_FORCE_NONE; - } else if (!strncmp("siso", buf, 4)) { - lq_sta->pers.ss_force = RS_SS_FORCE_SISO; - } else if (!strncmp("stbc", buf, 4)) { - if (lq_sta->stbc_capable) { - lq_sta->pers.ss_force = RS_SS_FORCE_STBC; - } else { - IWL_ERR(mvm, - "can't force STBC. peer doesn't support\n"); - ret = -EINVAL; - } - } else if (!strncmp("bfer", buf, 4)) { - if (lq_sta->bfer_capable) { - lq_sta->pers.ss_force = RS_SS_FORCE_BFER; - } else { - IWL_ERR(mvm, - "can't force BFER. peer doesn't support\n"); - ret = -EINVAL; - } - } else { - IWL_ERR(mvm, "valid values none|siso|stbc|bfer\n"); - ret = -EINVAL; - } - return ret ?: count; -} - -#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ - _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_lq_sta) -#define MVM_DEBUGFS_ADD_FILE_RS(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, lq_sta, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ - } while (0) - -MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32); - -static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir) -{ - struct iwl_lq_sta *lq_sta = priv_sta; - struct iwl_mvm_sta *mvmsta; - - mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta); - - if (!mvmsta->vif) - return; - - debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, - lq_sta, &rs_sta_dbgfs_scale_table_ops); - debugfs_create_file("rate_stats_table", S_IRUSR, dir, - lq_sta, &rs_sta_dbgfs_stats_table_ops); - debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir, - lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops); - debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, - &lq_sta->tx_agg_tid_en); - debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir, - &lq_sta->pers.dbg_fixed_txp_reduction); - - MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, S_IRUSR | S_IWUSR); - return; -err: - IWL_ERR((struct iwl_mvm *)mvm, "Can't create debugfs entity\n"); -} - -static void rs_remove_debugfs(void *mvm, void *mvm_sta) -{ -} -#endif - -/* - * Initialization of rate scaling information is done by driver after - * the station is added. Since mac80211 calls this function before a - * station is added we ignore it. - */ -static void rs_rate_init_stub(void *mvm_r, - struct ieee80211_supported_band *sband, - struct cfg80211_chan_def *chandef, - struct ieee80211_sta *sta, void *mvm_sta) -{ -} - -static const struct rate_control_ops rs_mvm_ops = { - .name = RS_NAME, - .tx_status = rs_mac80211_tx_status, - .get_rate = rs_get_rate, - .rate_init = rs_rate_init_stub, - .alloc = rs_alloc, - .free = rs_free, - .alloc_sta = rs_alloc_sta, - .free_sta = rs_free_sta, - .rate_update = rs_rate_update, -#ifdef CONFIG_MAC80211_DEBUGFS - .add_sta_debugfs = rs_add_debugfs, - .remove_sta_debugfs = rs_remove_debugfs, -#endif -}; - -int iwl_mvm_rate_control_register(void) -{ - return ieee80211_rate_control_register(&rs_mvm_ops); -} - -void iwl_mvm_rate_control_unregister(void) -{ - ieee80211_rate_control_unregister(&rs_mvm_ops); -} - -/** - * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable - * Tx protection, according to this request and previous requests, - * and send the LQ command. - * @mvmsta: The station - * @enable: Enable Tx protection? - */ -int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, - bool enable) -{ - struct iwl_lq_cmd *lq = &mvmsta->lq_sta.lq; - - lockdep_assert_held(&mvm->mutex); - - if (enable) { - if (mvmsta->tx_protection == 0) - lq->flags |= LQ_FLAG_USE_RTS_MSK; - mvmsta->tx_protection++; - } else { - mvmsta->tx_protection--; - if (mvmsta->tx_protection == 0) - lq->flags &= ~LQ_FLAG_USE_RTS_MSK; - } - - return iwl_mvm_send_lq_cmd(mvm, lq, false); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h deleted file mode 100644 index 81314ad9ebe0..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ /dev/null @@ -1,392 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __rs_h__ -#define __rs_h__ - -#include - -#include "iwl-config.h" - -#include "fw-api.h" -#include "iwl-trans.h" - -struct iwl_rs_rate_info { - u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ - u8 plcp_ht_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ - u8 plcp_ht_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ - u8 plcp_vht_siso; - u8 plcp_vht_mimo2; - u8 prev_rs; /* previous rate used in rs algo */ - u8 next_rs; /* next rate used in rs algo */ -}; - -#define IWL_RATE_60M_PLCP 3 - -enum { - IWL_RATE_INVM_INDEX = IWL_RATE_COUNT, - IWL_RATE_INVALID = IWL_RATE_COUNT, -}; - -#define LINK_QUAL_MAX_RETRY_NUM 16 - -enum { - IWL_RATE_6M_INDEX_TABLE = 0, - IWL_RATE_9M_INDEX_TABLE, - IWL_RATE_12M_INDEX_TABLE, - IWL_RATE_18M_INDEX_TABLE, - IWL_RATE_24M_INDEX_TABLE, - IWL_RATE_36M_INDEX_TABLE, - IWL_RATE_48M_INDEX_TABLE, - IWL_RATE_54M_INDEX_TABLE, - IWL_RATE_1M_INDEX_TABLE, - IWL_RATE_2M_INDEX_TABLE, - IWL_RATE_5M_INDEX_TABLE, - IWL_RATE_11M_INDEX_TABLE, - IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1, -}; - -/* #define vs. enum to keep from defaulting to 'large integer' */ -#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX) -#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX) -#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX) -#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX) -#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX) -#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX) -#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX) -#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX) -#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX) -#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX) -#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX) -#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX) -#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) - - -/* uCode API values for HT/VHT bit rates */ -enum { - IWL_RATE_HT_SISO_MCS_0_PLCP = 0, - IWL_RATE_HT_SISO_MCS_1_PLCP = 1, - IWL_RATE_HT_SISO_MCS_2_PLCP = 2, - IWL_RATE_HT_SISO_MCS_3_PLCP = 3, - IWL_RATE_HT_SISO_MCS_4_PLCP = 4, - IWL_RATE_HT_SISO_MCS_5_PLCP = 5, - IWL_RATE_HT_SISO_MCS_6_PLCP = 6, - IWL_RATE_HT_SISO_MCS_7_PLCP = 7, - IWL_RATE_HT_MIMO2_MCS_0_PLCP = 0x8, - IWL_RATE_HT_MIMO2_MCS_1_PLCP = 0x9, - IWL_RATE_HT_MIMO2_MCS_2_PLCP = 0xA, - IWL_RATE_HT_MIMO2_MCS_3_PLCP = 0xB, - IWL_RATE_HT_MIMO2_MCS_4_PLCP = 0xC, - IWL_RATE_HT_MIMO2_MCS_5_PLCP = 0xD, - IWL_RATE_HT_MIMO2_MCS_6_PLCP = 0xE, - IWL_RATE_HT_MIMO2_MCS_7_PLCP = 0xF, - IWL_RATE_VHT_SISO_MCS_0_PLCP = 0, - IWL_RATE_VHT_SISO_MCS_1_PLCP = 1, - IWL_RATE_VHT_SISO_MCS_2_PLCP = 2, - IWL_RATE_VHT_SISO_MCS_3_PLCP = 3, - IWL_RATE_VHT_SISO_MCS_4_PLCP = 4, - IWL_RATE_VHT_SISO_MCS_5_PLCP = 5, - IWL_RATE_VHT_SISO_MCS_6_PLCP = 6, - IWL_RATE_VHT_SISO_MCS_7_PLCP = 7, - IWL_RATE_VHT_SISO_MCS_8_PLCP = 8, - IWL_RATE_VHT_SISO_MCS_9_PLCP = 9, - IWL_RATE_VHT_MIMO2_MCS_0_PLCP = 0x10, - IWL_RATE_VHT_MIMO2_MCS_1_PLCP = 0x11, - IWL_RATE_VHT_MIMO2_MCS_2_PLCP = 0x12, - IWL_RATE_VHT_MIMO2_MCS_3_PLCP = 0x13, - IWL_RATE_VHT_MIMO2_MCS_4_PLCP = 0x14, - IWL_RATE_VHT_MIMO2_MCS_5_PLCP = 0x15, - IWL_RATE_VHT_MIMO2_MCS_6_PLCP = 0x16, - IWL_RATE_VHT_MIMO2_MCS_7_PLCP = 0x17, - IWL_RATE_VHT_MIMO2_MCS_8_PLCP = 0x18, - IWL_RATE_VHT_MIMO2_MCS_9_PLCP = 0x19, - IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_HT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_VHT_SISO_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_VHT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_HT_SISO_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_HT_SISO_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_HT_MIMO2_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, - IWL_RATE_HT_MIMO2_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, -}; - -#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) - -#define IWL_INVALID_VALUE -1 - -#define TPC_MAX_REDUCTION 15 -#define TPC_NO_REDUCTION 0 -#define TPC_INVALID 0xff - -#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) -#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) -#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) - -#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ - -/* load per tid defines for A-MPDU activation */ -#define IWL_AGG_TPT_THREHOLD 0 -#define IWL_AGG_ALL_TID 0xff - -enum iwl_table_type { - LQ_NONE, - LQ_LEGACY_G, /* legacy types */ - LQ_LEGACY_A, - LQ_HT_SISO, /* HT types */ - LQ_HT_MIMO2, - LQ_VHT_SISO, /* VHT types */ - LQ_VHT_MIMO2, - LQ_MAX, -}; - -struct rs_rate { - int index; - enum iwl_table_type type; - u8 ant; - u32 bw; - bool sgi; - bool ldpc; - bool stbc; - bool bfer; -}; - - -#define is_type_legacy(type) (((type) == LQ_LEGACY_G) || \ - ((type) == LQ_LEGACY_A)) -#define is_type_ht_siso(type) ((type) == LQ_HT_SISO) -#define is_type_ht_mimo2(type) ((type) == LQ_HT_MIMO2) -#define is_type_vht_siso(type) ((type) == LQ_VHT_SISO) -#define is_type_vht_mimo2(type) ((type) == LQ_VHT_MIMO2) -#define is_type_siso(type) (is_type_ht_siso(type) || is_type_vht_siso(type)) -#define is_type_mimo2(type) (is_type_ht_mimo2(type) || is_type_vht_mimo2(type)) -#define is_type_mimo(type) (is_type_mimo2(type)) -#define is_type_ht(type) (is_type_ht_siso(type) || is_type_ht_mimo2(type)) -#define is_type_vht(type) (is_type_vht_siso(type) || is_type_vht_mimo2(type)) -#define is_type_a_band(type) ((type) == LQ_LEGACY_A) -#define is_type_g_band(type) ((type) == LQ_LEGACY_G) - -#define is_legacy(rate) is_type_legacy((rate)->type) -#define is_ht_siso(rate) is_type_ht_siso((rate)->type) -#define is_ht_mimo2(rate) is_type_ht_mimo2((rate)->type) -#define is_vht_siso(rate) is_type_vht_siso((rate)->type) -#define is_vht_mimo2(rate) is_type_vht_mimo2((rate)->type) -#define is_siso(rate) is_type_siso((rate)->type) -#define is_mimo2(rate) is_type_mimo2((rate)->type) -#define is_mimo(rate) is_type_mimo((rate)->type) -#define is_ht(rate) is_type_ht((rate)->type) -#define is_vht(rate) is_type_vht((rate)->type) -#define is_a_band(rate) is_type_a_band((rate)->type) -#define is_g_band(rate) is_type_g_band((rate)->type) - -#define is_ht20(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_20) -#define is_ht40(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_40) -#define is_ht80(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_80) - -#define IWL_MAX_MCS_DISPLAY_SIZE 12 - -struct iwl_rate_mcs_info { - char mbps[IWL_MAX_MCS_DISPLAY_SIZE]; - char mcs[IWL_MAX_MCS_DISPLAY_SIZE]; -}; - -/** - * struct iwl_rate_scale_data -- tx success history for one rate - */ -struct iwl_rate_scale_data { - u64 data; /* bitmap of successful frames */ - s32 success_counter; /* number of frames successful */ - s32 success_ratio; /* per-cent * 128 */ - s32 counter; /* number of frames attempted */ - s32 average_tpt; /* success ratio * expected throughput */ -}; - -/* Possible Tx columns - * Tx Column = a combo of legacy/siso/mimo x antenna x SGI - */ -enum rs_column { - RS_COLUMN_LEGACY_ANT_A = 0, - RS_COLUMN_LEGACY_ANT_B, - RS_COLUMN_SISO_ANT_A, - RS_COLUMN_SISO_ANT_B, - RS_COLUMN_SISO_ANT_A_SGI, - RS_COLUMN_SISO_ANT_B_SGI, - RS_COLUMN_MIMO2, - RS_COLUMN_MIMO2_SGI, - - RS_COLUMN_LAST = RS_COLUMN_MIMO2_SGI, - RS_COLUMN_COUNT = RS_COLUMN_LAST + 1, - RS_COLUMN_INVALID, -}; - -enum rs_ss_force_opt { - RS_SS_FORCE_NONE = 0, - RS_SS_FORCE_STBC, - RS_SS_FORCE_BFER, - RS_SS_FORCE_SISO, -}; - -/* Packet stats per rate */ -struct rs_rate_stats { - u64 success; - u64 total; -}; - -/** - * struct iwl_scale_tbl_info -- tx params and success history for all rates - * - * There are two of these in struct iwl_lq_sta, - * one for "active", and one for "search". - */ -struct iwl_scale_tbl_info { - struct rs_rate rate; - enum rs_column column; - const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ - struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ - /* per txpower-reduction history */ - struct iwl_rate_scale_data tpc_win[TPC_MAX_REDUCTION + 1]; -}; - -enum { - RS_STATE_SEARCH_CYCLE_STARTED, - RS_STATE_SEARCH_CYCLE_ENDED, - RS_STATE_STAY_IN_COLUMN, -}; - -/** - * struct iwl_lq_sta -- driver's rate scaling private structure - * - * Pointer to this gets passed back and forth between driver and mac80211. - */ -struct iwl_lq_sta { - u8 active_tbl; /* index of active table, range 0-1 */ - u8 rs_state; /* RS_STATE_* */ - u8 search_better_tbl; /* 1: currently trying alternate mode */ - s32 last_tpt; - - /* The following determine when to search for a new mode */ - u32 table_count_limit; - u32 max_failure_limit; /* # failed frames before new search */ - u32 max_success_limit; /* # successful frames before new search */ - u32 table_count; - u32 total_failed; /* total failed frames, any/all rates */ - u32 total_success; /* total successful frames, any/all rates */ - u64 flush_timer; /* time staying in mode before new search */ - - u32 visited_columns; /* Bitmask marking which Tx columns were - * explored during a search cycle - */ - u64 last_tx; - bool is_vht; - bool ldpc; /* LDPC Rx is supported by the STA */ - bool stbc_capable; /* Tx STBC is supported by chip and Rx by STA */ - bool bfer_capable; /* Remote supports beamformee and we BFer */ - - enum ieee80211_band band; - - /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ - unsigned long active_legacy_rate; - unsigned long active_siso_rate; - unsigned long active_mimo2_rate; - - /* Highest rate per Tx mode */ - u8 max_legacy_rate_idx; - u8 max_siso_rate_idx; - u8 max_mimo2_rate_idx; - - /* Optimal rate based on RSSI and STA caps. - * Used only to reflect link speed to userspace. - */ - struct rs_rate optimal_rate; - unsigned long optimal_rate_mask; - const struct rs_init_rate_info *optimal_rates; - int optimal_nentries; - - u8 missed_rate_counter; - - struct iwl_lq_cmd lq; - struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ - u8 tx_agg_tid_en; - - /* last tx rate_n_flags */ - u32 last_rate_n_flags; - /* packets destined for this STA are aggregated */ - u8 is_agg; - - /* tx power reduce for this sta */ - int tpc_reduce; - - /* persistent fields - initialized only once - keep last! */ - struct lq_sta_pers { -#ifdef CONFIG_MAC80211_DEBUGFS - u32 dbg_fixed_rate; - u8 dbg_fixed_txp_reduction; - - /* force STBC/BFER/SISO for testing */ - enum rs_ss_force_opt ss_force; -#endif - u8 chains; - s8 chain_signal[IEEE80211_MAX_CHAINS]; - s8 last_rssi; - struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT]; - struct iwl_mvm *drv; - } pers; -}; - -/* Initialize station's rate scaling information after adding station */ -void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum ieee80211_band band, bool init); - -/* Notify RS about Tx status */ -void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, struct ieee80211_tx_info *info); - -/** - * iwl_rate_control_register - Register the rate control algorithm callbacks - * - * Since the rate control algorithm is hardware specific, there is no need - * or reason to place it as a stand alone module. The driver can call - * iwl_rate_control_register in order to register the rate control callbacks - * with the mac80211 subsystem. This should be performed prior to calling - * ieee80211_register_hw - * - */ -int iwl_mvm_rate_control_register(void); - -/** - * iwl_rate_control_unregister - Unregister the rate control callbacks - * - * This should be called after calling ieee80211_unregister_hw, but before - * the driver is unloaded. - */ -void iwl_mvm_rate_control_unregister(void); - -struct iwl_mvm_sta; - -int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, - bool enable); - -#endif /* __rs__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c deleted file mode 100644 index 5b58f5320e8d..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ /dev/null @@ -1,612 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ -#include -#include "iwl-trans.h" -#include "mvm.h" -#include "fw-api.h" - -/* - * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler - * - * Copies the phy information in mvm->last_phy_info, it will be used when the - * actual data will come from the fw in the next packet. - */ -void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - - memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info)); - mvm->ampdu_ref++; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { - spin_lock(&mvm->drv_stats_lock); - mvm->drv_rx_stats.ampdu_count++; - spin_unlock(&mvm->drv_stats_lock); - } -#endif -} - -/* - * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211 - * - * Adds the rxb to a new skb and give it to mac80211 - */ -static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, - struct napi_struct *napi, - struct sk_buff *skb, - struct ieee80211_hdr *hdr, u16 len, - u32 ampdu_status, u8 crypt_len, - struct iwl_rx_cmd_buffer *rxb) -{ - unsigned int hdrlen, fraglen; - - /* If frame is small enough to fit in skb->head, pull it completely. - * If not, only pull ieee80211_hdr (including crypto if present, and - * an additional 8 bytes for SNAP/ethertype, see below) so that - * splice() or TCP coalesce are more efficient. - * - * Since, in addition, ieee80211_data_to_8023() always pull in at - * least 8 bytes (possibly more for mesh) we can do the same here - * to save the cost of doing it later. That still doesn't pull in - * the actual IP header since the typical case has a SNAP header. - * If the latter changes (there are efforts in the standards group - * to do so) we should revisit this and ieee80211_data_to_8023(). - */ - hdrlen = (len <= skb_tailroom(skb)) ? len : - sizeof(*hdr) + crypt_len + 8; - - memcpy(skb_put(skb, hdrlen), hdr, hdrlen); - fraglen = len - hdrlen; - - if (fraglen) { - int offset = (void *)hdr + hdrlen - - rxb_addr(rxb) + rxb_offset(rxb); - - skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, - fraglen, rxb->truesize); - } - - ieee80211_rx_napi(mvm->hw, skb, napi); -} - -/* - * iwl_mvm_get_signal_strength - use new rx PHY INFO API - * values are reported by the fw as positive values - need to negate - * to obtain their dBM. Account for missing antennas by replacing 0 - * values by -256dBm: practically 0 power and a non-feasible 8 bit value. - */ -static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, - struct iwl_rx_phy_info *phy_info, - struct ieee80211_rx_status *rx_status) -{ - int energy_a, energy_b, energy_c, max_energy; - u32 val; - - val = - le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]); - energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >> - IWL_RX_INFO_ENERGY_ANT_A_POS; - energy_a = energy_a ? -energy_a : S8_MIN; - energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >> - IWL_RX_INFO_ENERGY_ANT_B_POS; - energy_b = energy_b ? -energy_b : S8_MIN; - energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >> - IWL_RX_INFO_ENERGY_ANT_C_POS; - energy_c = energy_c ? -energy_c : S8_MIN; - max_energy = max(energy_a, energy_b); - max_energy = max(max_energy, energy_c); - - IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n", - energy_a, energy_b, energy_c, max_energy); - - rx_status->signal = max_energy; - rx_status->chains = (le16_to_cpu(phy_info->phy_flags) & - RX_RES_PHY_FLAGS_ANTENNA) - >> RX_RES_PHY_FLAGS_ANTENNA_POS; - rx_status->chain_signal[0] = energy_a; - rx_status->chain_signal[1] = energy_b; - rx_status->chain_signal[2] = energy_c; -} - -/* - * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format - * @mvm: the mvm object - * @hdr: 80211 header - * @stats: status in mac80211's format - * @rx_pkt_status: status coming from fw - * - * returns non 0 value if the packet should be dropped - */ -static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, - struct ieee80211_hdr *hdr, - struct ieee80211_rx_status *stats, - u32 rx_pkt_status, - u8 *crypt_len) -{ - if (!ieee80211_has_protected(hdr->frame_control) || - (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == - RX_MPDU_RES_STATUS_SEC_NO_ENC) - return 0; - - /* packet was encrypted with unknown alg */ - if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == - RX_MPDU_RES_STATUS_SEC_ENC_ERR) - return 0; - - switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) { - case RX_MPDU_RES_STATUS_SEC_CCM_ENC: - /* alg is CCM: check MIC only */ - if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) - return -1; - - stats->flag |= RX_FLAG_DECRYPTED; - *crypt_len = IEEE80211_CCMP_HDR_LEN; - return 0; - - case RX_MPDU_RES_STATUS_SEC_TKIP_ENC: - /* Don't drop the frame and decrypt it in SW */ - if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK)) - return 0; - *crypt_len = IEEE80211_TKIP_IV_LEN; - /* fall through if TTAK OK */ - - case RX_MPDU_RES_STATUS_SEC_WEP_ENC: - if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK)) - return -1; - - stats->flag |= RX_FLAG_DECRYPTED; - if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) == - RX_MPDU_RES_STATUS_SEC_WEP_ENC) - *crypt_len = IEEE80211_WEP_IV_LEN; - return 0; - - case RX_MPDU_RES_STATUS_SEC_EXT_ENC: - if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) - return -1; - stats->flag |= RX_FLAG_DECRYPTED; - return 0; - - default: - IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status); - } - - return 0; -} - -static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, - struct sk_buff *skb, - u32 status) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); - - if (mvmvif->features & NETIF_F_RXCSUM && - status & RX_MPDU_RES_STATUS_CSUM_DONE && - status & RX_MPDU_RES_STATUS_CSUM_OK) - skb->ip_summed = CHECKSUM_UNNECESSARY; -} - -/* - * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler - * - * Handles the actual data of the Rx packet from the fw - */ -void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, - struct iwl_rx_cmd_buffer *rxb) -{ - struct ieee80211_hdr *hdr; - struct ieee80211_rx_status *rx_status; - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_rx_phy_info *phy_info; - struct iwl_rx_mpdu_res_start *rx_res; - struct ieee80211_sta *sta; - struct sk_buff *skb; - u32 len; - u32 ampdu_status; - u32 rate_n_flags; - u32 rx_pkt_status; - u8 crypt_len = 0; - - phy_info = &mvm->last_phy_info; - rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data; - hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res)); - len = le16_to_cpu(rx_res->byte_count); - rx_pkt_status = le32_to_cpup((__le32 *) - (pkt->data + sizeof(*rx_res) + len)); - - /* Dont use dev_alloc_skb(), we'll have enough headroom once - * ieee80211_hdr pulled. - */ - skb = alloc_skb(128, GFP_ATOMIC); - if (!skb) { - IWL_ERR(mvm, "alloc_skb failed\n"); - return; - } - - rx_status = IEEE80211_SKB_RXCB(skb); - - /* - * drop the packet if it has failed being decrypted by HW - */ - if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status, - &crypt_len)) { - IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n", - rx_pkt_status); - kfree_skb(skb); - return; - } - - /* - * Keep packets with CRC errors (and with overrun) for monitor mode - * (otherwise the firmware discards them) but mark them as bad. - */ - if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) || - !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) { - IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - } - - /* This will be used in several places later */ - rate_n_flags = le32_to_cpu(phy_info->rate_n_flags); - - /* rx_status carries information about the packet to mac80211 */ - rx_status->mactime = le64_to_cpu(phy_info->timestamp); - rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp); - rx_status->band = - (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ? - IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - rx_status->freq = - ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel), - rx_status->band); - /* - * TSF as indicated by the fw is at INA time, but mac80211 expects the - * TSF at the beginning of the MPDU. - */ - /*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/ - - iwl_mvm_get_signal_strength(mvm, phy_info, rx_status); - - IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal, - (unsigned long long)rx_status->mactime); - - rcu_read_lock(); - /* - * We have tx blocked stations (with CS bit). If we heard frames from - * a blocked station on a new channel we can TX to it again. - */ - if (unlikely(mvm->csa_tx_block_bcn_timeout)) { - sta = ieee80211_find_sta( - rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2); - if (sta) - iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); - } - - /* This is fine since we don't support multiple AP interfaces */ - sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); - if (sta) { - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - - rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); - - if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) && - ieee80211_is_beacon(hdr->frame_control)) { - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_low_rssi *rssi_trig; - bool trig_check; - s32 rssi; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, - FW_DBG_TRIGGER_RSSI); - rssi_trig = (void *)trig->data; - rssi = le32_to_cpu(rssi_trig->rssi); - - trig_check = - iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif, - trig); - if (trig_check && rx_status->signal < rssi) - iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); - } - } - - if (sta && ieee80211_is_data(hdr->frame_control)) - iwl_mvm_rx_csum(sta, skb, rx_pkt_status); - - rcu_read_unlock(); - - /* set the preamble flag if appropriate */ - if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE)) - rx_status->flag |= RX_FLAG_SHORTPRE; - - if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { - /* - * We know which subframes of an A-MPDU belong - * together since we get a single PHY response - * from the firmware for all of them - */ - rx_status->flag |= RX_FLAG_AMPDU_DETAILS; - rx_status->ampdu_reference = mvm->ampdu_ref; - } - - /* Set up the HT phy flags */ - switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { - case RATE_MCS_CHAN_WIDTH_20: - break; - case RATE_MCS_CHAN_WIDTH_40: - rx_status->flag |= RX_FLAG_40MHZ; - break; - case RATE_MCS_CHAN_WIDTH_80: - rx_status->vht_flag |= RX_VHT_FLAG_80MHZ; - break; - case RATE_MCS_CHAN_WIDTH_160: - rx_status->vht_flag |= RX_VHT_FLAG_160MHZ; - break; - } - if (rate_n_flags & RATE_MCS_SGI_MSK) - rx_status->flag |= RX_FLAG_SHORT_GI; - if (rate_n_flags & RATE_HT_MCS_GF_MSK) - rx_status->flag |= RX_FLAG_HT_GF; - if (rate_n_flags & RATE_MCS_LDPC_MSK) - rx_status->flag |= RX_FLAG_LDPC; - if (rate_n_flags & RATE_MCS_HT_MSK) { - u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> - RATE_MCS_STBC_POS; - rx_status->flag |= RX_FLAG_HT; - rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; - rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; - } else if (rate_n_flags & RATE_MCS_VHT_MSK) { - u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >> - RATE_MCS_STBC_POS; - rx_status->vht_nss = - ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1; - rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; - rx_status->flag |= RX_FLAG_VHT; - rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; - if (rate_n_flags & RATE_MCS_BF_MSK) - rx_status->vht_flag |= RX_VHT_FLAG_BF; - } else { - rx_status->rate_idx = - iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, - rx_status->band); - } - -#ifdef CONFIG_IWLWIFI_DEBUGFS - iwl_mvm_update_frame_stats(mvm, rate_n_flags, - rx_status->flag & RX_FLAG_AMPDU_DETAILS); -#endif - iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status, - crypt_len, rxb); -} - -static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm, - struct mvm_statistics_rx *rx_stats) -{ - lockdep_assert_held(&mvm->mutex); - - mvm->rx_stats = *rx_stats; -} - -struct iwl_mvm_stat_data { - struct iwl_mvm *mvm; - __le32 mac_id; - u8 beacon_filter_average_energy; - struct mvm_statistics_general_v8 *general; -}; - -static void iwl_mvm_stat_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_stat_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - int sig = -data->beacon_filter_average_energy; - int last_event; - int thold = vif->bss_conf.cqm_rssi_thold; - int hyst = vif->bss_conf.cqm_rssi_hyst; - u16 id = le32_to_cpu(data->mac_id); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - /* This doesn't need the MAC ID check since it's not taking the - * data copied into the "data" struct, but rather the data from - * the notification directly. - */ - if (data->general) { - mvmvif->beacon_stats.num_beacons = - le32_to_cpu(data->general->beacon_counter[mvmvif->id]); - mvmvif->beacon_stats.avg_signal = - -data->general->beacon_average_energy[mvmvif->id]; - } - - if (mvmvif->id != id) - return; - - if (vif->type != NL80211_IFTYPE_STATION) - return; - - if (sig == 0) { - IWL_DEBUG_RX(mvm, "RSSI is 0 - skip signal based decision\n"); - return; - } - - mvmvif->bf_data.ave_beacon_signal = sig; - - /* BT Coex */ - if (mvmvif->bf_data.bt_coex_min_thold != - mvmvif->bf_data.bt_coex_max_thold) { - last_event = mvmvif->bf_data.last_bt_coex_event; - if (sig > mvmvif->bf_data.bt_coex_max_thold && - (last_event <= mvmvif->bf_data.bt_coex_min_thold || - last_event == 0)) { - mvmvif->bf_data.last_bt_coex_event = sig; - IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n", - sig); - iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH); - } else if (sig < mvmvif->bf_data.bt_coex_min_thold && - (last_event >= mvmvif->bf_data.bt_coex_max_thold || - last_event == 0)) { - mvmvif->bf_data.last_bt_coex_event = sig; - IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n", - sig); - iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW); - } - } - - if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) - return; - - /* CQM Notification */ - last_event = mvmvif->bf_data.last_cqm_event; - if (thold && sig < thold && (last_event == 0 || - sig < last_event - hyst)) { - mvmvif->bf_data.last_cqm_event = sig; - IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n", - sig); - ieee80211_cqm_rssi_notify( - vif, - NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, - GFP_KERNEL); - } else if (sig > thold && - (last_event == 0 || sig > last_event + hyst)) { - mvmvif->bf_data.last_cqm_event = sig; - IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n", - sig); - ieee80211_cqm_rssi_notify( - vif, - NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, - GFP_KERNEL); - } -} - -static inline void -iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_stats *trig_stats; - u32 trig_offset, trig_thold; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_STATS)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_STATS); - trig_stats = (void *)trig->data; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) - return; - - trig_offset = le32_to_cpu(trig_stats->stop_offset); - trig_thold = le32_to_cpu(trig_stats->stop_threshold); - - if (WARN_ON_ONCE(trig_offset >= iwl_rx_packet_payload_len(pkt))) - return; - - if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); -} - -void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data; - struct iwl_mvm_stat_data data = { - .mvm = mvm, - }; - u32 temperature; - - if (iwl_rx_packet_payload_len(pkt) != sizeof(*stats)) - goto invalid; - - temperature = le32_to_cpu(stats->general.radio_temperature); - data.mac_id = stats->rx.general.mac_id; - data.beacon_filter_average_energy = - stats->general.beacon_filter_average_energy; - - iwl_mvm_update_rx_statistics(mvm, &stats->rx); - - mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time); - mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time); - mvm->radio_stats.on_time_rf = - le64_to_cpu(stats->general.on_time_rf); - mvm->radio_stats.on_time_scan = - le64_to_cpu(stats->general.on_time_scan); - - data.general = &stats->general; - - iwl_mvm_rx_stats_check_trigger(mvm, pkt); - - ieee80211_iterate_active_interfaces(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_stat_iterator, - &data); - return; - invalid: - IWL_ERR(mvm, "received invalid statistics size (%d)!\n", - iwl_rx_packet_payload_len(pkt)); -} - -void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb)); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c deleted file mode 100644 index d6e0c1b5c20c..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ /dev/null @@ -1,1552 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include - -#include "mvm.h" -#include "fw-api-scan.h" - -#define IWL_DENSE_EBS_SCAN_RATIO 5 -#define IWL_SPARSE_EBS_SCAN_RATIO 1 - -enum iwl_mvm_scan_type { - IWL_SCAN_TYPE_UNASSOC, - IWL_SCAN_TYPE_WILD, - IWL_SCAN_TYPE_MILD, - IWL_SCAN_TYPE_FRAGMENTED, -}; - -enum iwl_mvm_traffic_load { - IWL_MVM_TRAFFIC_LOW, - IWL_MVM_TRAFFIC_MEDIUM, - IWL_MVM_TRAFFIC_HIGH, -}; - -struct iwl_mvm_scan_timing_params { - u32 dwell_active; - u32 dwell_passive; - u32 dwell_fragmented; - u32 suspend_time; - u32 max_out_time; -}; - -static struct iwl_mvm_scan_timing_params scan_timing[] = { - [IWL_SCAN_TYPE_UNASSOC] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, - .suspend_time = 0, - .max_out_time = 0, - }, - [IWL_SCAN_TYPE_WILD] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, - .suspend_time = 30, - .max_out_time = 120, - }, - [IWL_SCAN_TYPE_MILD] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, - .suspend_time = 120, - .max_out_time = 120, - }, - [IWL_SCAN_TYPE_FRAGMENTED] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, - .suspend_time = 95, - .max_out_time = 44, - }, -}; - -struct iwl_mvm_scan_params { - enum iwl_mvm_scan_type type; - u32 n_channels; - u16 delay; - int n_ssids; - struct cfg80211_ssid *ssids; - struct ieee80211_channel **channels; - u32 flags; - u8 *mac_addr; - u8 *mac_addr_mask; - bool no_cck; - bool pass_all; - int n_match_sets; - struct iwl_scan_probe_req preq; - struct cfg80211_match_set *match_sets; - int n_scan_plans; - struct cfg80211_sched_scan_plan *scan_plans; -}; - -static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm) -{ - if (mvm->scan_rx_ant != ANT_NONE) - return mvm->scan_rx_ant; - return iwl_mvm_get_valid_rx_ant(mvm); -} - -static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) -{ - u16 rx_chain; - u8 rx_ant; - - rx_ant = iwl_mvm_scan_rx_ant(mvm); - rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS; - rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; - rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS; - rx_chain |= 0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS; - return cpu_to_le16(rx_chain); -} - -static __le32 iwl_mvm_scan_rxon_flags(enum ieee80211_band band) -{ - if (band == IEEE80211_BAND_2GHZ) - return cpu_to_le32(PHY_BAND_24); - else - return cpu_to_le32(PHY_BAND_5); -} - -static inline __le32 -iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band, - bool no_cck) -{ - u32 tx_ant; - - mvm->scan_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), - mvm->scan_last_antenna_idx); - tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS; - - if (band == IEEE80211_BAND_2GHZ && !no_cck) - return cpu_to_le32(IWL_RATE_1M_PLCP | RATE_MCS_CCK_MSK | - tx_ant); - else - return cpu_to_le32(IWL_RATE_6M_PLCP | tx_ant); -} - -static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int *global_cnt = data; - - if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt && - mvmvif->phy_ctxt->id < MAX_PHYS) - *global_cnt += 1; -} - -static enum iwl_mvm_traffic_load iwl_mvm_get_traffic_load(struct iwl_mvm *mvm) -{ - return IWL_MVM_TRAFFIC_LOW; -} - -static enum -iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mvm_scan_params *params) -{ - int global_cnt = 0; - enum iwl_mvm_traffic_load load; - bool low_latency; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_scan_condition_iterator, - &global_cnt); - if (!global_cnt) - return IWL_SCAN_TYPE_UNASSOC; - - load = iwl_mvm_get_traffic_load(mvm); - low_latency = iwl_mvm_low_latency(mvm); - - if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) && - vif->type != NL80211_IFTYPE_P2P_DEVICE && - fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAGMENTED_SCAN)) - return IWL_SCAN_TYPE_FRAGMENTED; - - if (load >= IWL_MVM_TRAFFIC_MEDIUM || low_latency) - return IWL_SCAN_TYPE_MILD; - - return IWL_SCAN_TYPE_WILD; -} - -static inline bool iwl_mvm_rrm_scan_needed(struct iwl_mvm *mvm) -{ - /* require rrm scan whenever the fw supports it */ - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT); -} - -static int iwl_mvm_max_scan_ie_fw_cmd_room(struct iwl_mvm *mvm) -{ - int max_probe_len; - - max_probe_len = SCAN_OFFLOAD_PROBE_REQ_SIZE; - - /* we create the 802.11 header and SSID element */ - max_probe_len -= 24 + 2; - - /* DS parameter set element is added on 2.4GHZ band if required */ - if (iwl_mvm_rrm_scan_needed(mvm)) - max_probe_len -= 3; - - return max_probe_len; -} - -int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm) -{ - int max_ie_len = iwl_mvm_max_scan_ie_fw_cmd_room(mvm); - - /* TODO: [BUG] This function should return the maximum allowed size of - * scan IEs, however the LMAC scan api contains both 2GHZ and 5GHZ IEs - * in the same command. So the correct implementation of this function - * is just iwl_mvm_max_scan_ie_fw_cmd_room() / 2. Currently the scan - * command has only 512 bytes and it would leave us with about 240 - * bytes for scan IEs, which is clearly not enough. So meanwhile - * we will report an incorrect value. This may result in a failure to - * issue a scan in unified_scan_lmac and unified_sched_scan_lmac - * functions with -ENOBUFS, if a large enough probe will be provided. - */ - return max_ie_len; -} - -static u8 *iwl_mvm_dump_channel_list(struct iwl_scan_results_notif *res, - int num_res, u8 *buf, size_t buf_size) -{ - int i; - u8 *pos = buf, *end = buf + buf_size; - - for (i = 0; pos < end && i < num_res; i++) - pos += snprintf(pos, end - pos, " %u", res[i].channel); - - /* terminate the string in case the buffer was too short */ - *(buf + buf_size - 1) = '\0'; - - return buf; -} - -void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_lmac_scan_complete_notif *notif = (void *)pkt->data; - u8 buf[256]; - - IWL_DEBUG_SCAN(mvm, - "Scan offload iteration complete: status=0x%x scanned channels=%d channels list: %s\n", - notif->status, notif->scanned_channels, - iwl_mvm_dump_channel_list(notif->results, - notif->scanned_channels, buf, - sizeof(buf))); -} - -void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n"); - ieee80211_sched_scan_results(mvm->hw); -} - -static const char *iwl_mvm_ebs_status_str(enum iwl_scan_ebs_status status) -{ - switch (status) { - case IWL_SCAN_EBS_SUCCESS: - return "successful"; - case IWL_SCAN_EBS_INACTIVE: - return "inactive"; - case IWL_SCAN_EBS_FAILED: - case IWL_SCAN_EBS_CHAN_NOT_FOUND: - default: - return "failed"; - } -} - -void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_periodic_scan_complete *scan_notif = (void *)pkt->data; - bool aborted = (scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED); - - /* scan status must be locked for proper checking */ - lockdep_assert_held(&mvm->mutex); - - /* We first check if we were stopping a scan, in which case we - * just clear the stopping flag. Then we check if it was a - * firmware initiated stop, in which case we need to inform - * mac80211. - * Note that we can have a stopping and a running scan - * simultaneously, but we can't have two different types of - * scans stopping or running at the same time (since LMAC - * doesn't support it). - */ - - if (mvm->scan_status & IWL_MVM_SCAN_STOPPING_SCHED) { - WARN_ON_ONCE(mvm->scan_status & IWL_MVM_SCAN_STOPPING_REGULAR); - - IWL_DEBUG_SCAN(mvm, "Scheduled scan %s, EBS status %s\n", - aborted ? "aborted" : "completed", - iwl_mvm_ebs_status_str(scan_notif->ebs_status)); - IWL_DEBUG_SCAN(mvm, - "Last line %d, Last iteration %d, Time after last iteration %d\n", - scan_notif->last_schedule_line, - scan_notif->last_schedule_iteration, - __le32_to_cpu(scan_notif->time_after_last_iter)); - - mvm->scan_status &= ~IWL_MVM_SCAN_STOPPING_SCHED; - } else if (mvm->scan_status & IWL_MVM_SCAN_STOPPING_REGULAR) { - IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s\n", - aborted ? "aborted" : "completed", - iwl_mvm_ebs_status_str(scan_notif->ebs_status)); - - mvm->scan_status &= ~IWL_MVM_SCAN_STOPPING_REGULAR; - } else if (mvm->scan_status & IWL_MVM_SCAN_SCHED) { - WARN_ON_ONCE(mvm->scan_status & IWL_MVM_SCAN_REGULAR); - - IWL_DEBUG_SCAN(mvm, "Scheduled scan %s, EBS status %s\n", - aborted ? "aborted" : "completed", - iwl_mvm_ebs_status_str(scan_notif->ebs_status)); - IWL_DEBUG_SCAN(mvm, - "Last line %d, Last iteration %d, Time after last iteration %d (FW)\n", - scan_notif->last_schedule_line, - scan_notif->last_schedule_iteration, - __le32_to_cpu(scan_notif->time_after_last_iter)); - - mvm->scan_status &= ~IWL_MVM_SCAN_SCHED; - ieee80211_sched_scan_stopped(mvm->hw); - } else if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) { - IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s (FW)\n", - aborted ? "aborted" : "completed", - iwl_mvm_ebs_status_str(scan_notif->ebs_status)); - - mvm->scan_status &= ~IWL_MVM_SCAN_REGULAR; - ieee80211_scan_completed(mvm->hw, - scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED); - iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); - } - - mvm->last_ebs_successful = - scan_notif->ebs_status == IWL_SCAN_EBS_SUCCESS || - scan_notif->ebs_status == IWL_SCAN_EBS_INACTIVE; -} - -static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list) -{ - int i; - - for (i = 0; i < PROBE_OPTION_MAX; i++) { - if (!ssid_list[i].len) - break; - if (ssid_list[i].len == ssid_len && - !memcmp(ssid_list->ssid, ssid, ssid_len)) - return i; - } - return -1; -} - -/* We insert the SSIDs in an inverted order, because the FW will - * invert it back. - */ -static void iwl_scan_build_ssids(struct iwl_mvm_scan_params *params, - struct iwl_ssid_ie *ssids, - u32 *ssid_bitmap) -{ - int i, j; - int index; - - /* - * copy SSIDs from match list. - * iwl_config_sched_scan_profiles() uses the order of these ssids to - * config match list. - */ - for (i = 0, j = params->n_match_sets - 1; - j >= 0 && i < PROBE_OPTION_MAX; - i++, j--) { - /* skip empty SSID matchsets */ - if (!params->match_sets[j].ssid.ssid_len) - continue; - ssids[i].id = WLAN_EID_SSID; - ssids[i].len = params->match_sets[j].ssid.ssid_len; - memcpy(ssids[i].ssid, params->match_sets[j].ssid.ssid, - ssids[i].len); - } - - /* add SSIDs from scan SSID list */ - *ssid_bitmap = 0; - for (j = params->n_ssids - 1; - j >= 0 && i < PROBE_OPTION_MAX; - i++, j--) { - index = iwl_ssid_exist(params->ssids[j].ssid, - params->ssids[j].ssid_len, - ssids); - if (index < 0) { - ssids[i].id = WLAN_EID_SSID; - ssids[i].len = params->ssids[j].ssid_len; - memcpy(ssids[i].ssid, params->ssids[j].ssid, - ssids[i].len); - *ssid_bitmap |= BIT(i); - } else { - *ssid_bitmap |= BIT(index); - } - } -} - -static int -iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, - struct cfg80211_sched_scan_request *req) -{ - struct iwl_scan_offload_profile *profile; - struct iwl_scan_offload_profile_cfg *profile_cfg; - struct iwl_scan_offload_blacklist *blacklist; - struct iwl_host_cmd cmd = { - .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD, - .len[1] = sizeof(*profile_cfg), - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .dataflags[1] = IWL_HCMD_DFL_NOCOPY, - }; - int blacklist_len; - int i; - int ret; - - if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES)) - return -EIO; - - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL) - blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN; - else - blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN; - - blacklist = kzalloc(sizeof(*blacklist) * blacklist_len, GFP_KERNEL); - if (!blacklist) - return -ENOMEM; - - profile_cfg = kzalloc(sizeof(*profile_cfg), GFP_KERNEL); - if (!profile_cfg) { - ret = -ENOMEM; - goto free_blacklist; - } - - cmd.data[0] = blacklist; - cmd.len[0] = sizeof(*blacklist) * blacklist_len; - cmd.data[1] = profile_cfg; - - /* No blacklist configuration */ - - profile_cfg->num_profiles = req->n_match_sets; - profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN; - profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN; - profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN; - if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len) - profile_cfg->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN; - - for (i = 0; i < req->n_match_sets; i++) { - profile = &profile_cfg->profiles[i]; - profile->ssid_index = i; - /* Support any cipher and auth algorithm */ - profile->unicast_cipher = 0xff; - profile->auth_alg = 0xff; - profile->network_type = IWL_NETWORK_TYPE_ANY; - profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY; - profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN; - } - - IWL_DEBUG_SCAN(mvm, "Sending scheduled scan profile config\n"); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - kfree(profile_cfg); -free_blacklist: - kfree(blacklist); - - return ret; -} - -static bool iwl_mvm_scan_pass_all(struct iwl_mvm *mvm, - struct cfg80211_sched_scan_request *req) -{ - if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { - IWL_DEBUG_SCAN(mvm, - "Sending scheduled scan with filtering, n_match_sets %d\n", - req->n_match_sets); - return false; - } - - IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n"); - return true; -} - -static int iwl_mvm_lmac_scan_abort(struct iwl_mvm *mvm) -{ - int ret; - struct iwl_host_cmd cmd = { - .id = SCAN_OFFLOAD_ABORT_CMD, - }; - u32 status; - - ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status); - if (ret) - return ret; - - if (status != CAN_ABORT_STATUS) { - /* - * The scan abort will return 1 for success or - * 2 for "failure". A failure condition can be - * due to simply not being in an active scan which - * can occur if we send the scan abort before the - * microcode has notified us that a scan is completed. - */ - IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status); - ret = -ENOENT; - } - - return ret; -} - -static void iwl_mvm_scan_fill_tx_cmd(struct iwl_mvm *mvm, - struct iwl_scan_req_tx_cmd *tx_cmd, - bool no_cck) -{ - tx_cmd[0].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | - TX_CMD_FLG_BT_DIS); - tx_cmd[0].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, - IEEE80211_BAND_2GHZ, - no_cck); - tx_cmd[0].sta_id = mvm->aux_sta.sta_id; - - tx_cmd[1].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | - TX_CMD_FLG_BT_DIS); - tx_cmd[1].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, - IEEE80211_BAND_5GHZ, - no_cck); - tx_cmd[1].sta_id = mvm->aux_sta.sta_id; -} - -static void -iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm, - struct ieee80211_channel **channels, - int n_channels, u32 ssid_bitmap, - struct iwl_scan_req_lmac *cmd) -{ - struct iwl_scan_channel_cfg_lmac *channel_cfg = (void *)&cmd->data; - int i; - - for (i = 0; i < n_channels; i++) { - channel_cfg[i].channel_num = - cpu_to_le16(channels[i]->hw_value); - channel_cfg[i].iter_count = cpu_to_le16(1); - channel_cfg[i].iter_interval = 0; - channel_cfg[i].flags = - cpu_to_le32(IWL_UNIFIED_SCAN_CHANNEL_PARTIAL | - ssid_bitmap); - } -} - -static u8 *iwl_mvm_copy_and_insert_ds_elem(struct iwl_mvm *mvm, const u8 *ies, - size_t len, u8 *const pos) -{ - static const u8 before_ds_params[] = { - WLAN_EID_SSID, - WLAN_EID_SUPP_RATES, - WLAN_EID_REQUEST, - WLAN_EID_EXT_SUPP_RATES, - }; - size_t offs; - u8 *newpos = pos; - - if (!iwl_mvm_rrm_scan_needed(mvm)) { - memcpy(newpos, ies, len); - return newpos + len; - } - - offs = ieee80211_ie_split(ies, len, - before_ds_params, - ARRAY_SIZE(before_ds_params), - 0); - - memcpy(newpos, ies, offs); - newpos += offs; - - /* Add a placeholder for DS Parameter Set element */ - *newpos++ = WLAN_EID_DS_PARAMS; - *newpos++ = 1; - *newpos++ = 0; - - memcpy(newpos, ies + offs, len - offs); - newpos += len - offs; - - return newpos; -} - -static void -iwl_mvm_build_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_scan_ies *ies, - struct iwl_mvm_scan_params *params) -{ - struct ieee80211_mgmt *frame = (void *)params->preq.buf; - u8 *pos, *newpos; - const u8 *mac_addr = params->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ? - params->mac_addr : NULL; - - /* - * Unfortunately, right now the offload scan doesn't support randomising - * within the firmware, so until the firmware API is ready we implement - * it in the driver. This means that the scan iterations won't really be - * random, only when it's restarted, but at least that helps a bit. - */ - if (mac_addr) - get_random_mask_addr(frame->sa, mac_addr, - params->mac_addr_mask); - else - memcpy(frame->sa, vif->addr, ETH_ALEN); - - frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - eth_broadcast_addr(frame->da); - eth_broadcast_addr(frame->bssid); - frame->seq_ctrl = 0; - - pos = frame->u.probe_req.variable; - *pos++ = WLAN_EID_SSID; - *pos++ = 0; - - params->preq.mac_header.offset = 0; - params->preq.mac_header.len = cpu_to_le16(24 + 2); - - /* Insert ds parameter set element on 2.4 GHz band */ - newpos = iwl_mvm_copy_and_insert_ds_elem(mvm, - ies->ies[IEEE80211_BAND_2GHZ], - ies->len[IEEE80211_BAND_2GHZ], - pos); - params->preq.band_data[0].offset = cpu_to_le16(pos - params->preq.buf); - params->preq.band_data[0].len = cpu_to_le16(newpos - pos); - pos = newpos; - - memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ], - ies->len[IEEE80211_BAND_5GHZ]); - params->preq.band_data[1].offset = cpu_to_le16(pos - params->preq.buf); - params->preq.band_data[1].len = - cpu_to_le16(ies->len[IEEE80211_BAND_5GHZ]); - pos += ies->len[IEEE80211_BAND_5GHZ]; - - memcpy(pos, ies->common_ies, ies->common_ie_len); - params->preq.common_data.offset = cpu_to_le16(pos - params->preq.buf); - params->preq.common_data.len = cpu_to_le16(ies->common_ie_len); -} - -static __le32 iwl_mvm_scan_priority(struct iwl_mvm *mvm, - enum iwl_scan_priority_ext prio) -{ - if (fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_EXT_SCAN_PRIORITY)) - return cpu_to_le32(prio); - - if (prio <= IWL_SCAN_PRIORITY_EXT_2) - return cpu_to_le32(IWL_SCAN_PRIORITY_LOW); - - if (prio <= IWL_SCAN_PRIORITY_EXT_4) - return cpu_to_le32(IWL_SCAN_PRIORITY_MEDIUM); - - return cpu_to_le32(IWL_SCAN_PRIORITY_HIGH); -} - -static void iwl_mvm_scan_lmac_dwell(struct iwl_mvm *mvm, - struct iwl_scan_req_lmac *cmd, - struct iwl_mvm_scan_params *params) -{ - cmd->active_dwell = scan_timing[params->type].dwell_active; - cmd->passive_dwell = scan_timing[params->type].dwell_passive; - cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented; - cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time); - cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time); - cmd->scan_prio = iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); -} - -static inline bool iwl_mvm_scan_fits(struct iwl_mvm *mvm, int n_ssids, - struct ieee80211_scan_ies *ies, - int n_channels) -{ - return ((n_ssids <= PROBE_OPTION_MAX) && - (n_channels <= mvm->fw->ucode_capa.n_scan_channels) & - (ies->common_ie_len + - ies->len[NL80211_BAND_2GHZ] + - ies->len[NL80211_BAND_5GHZ] <= - iwl_mvm_max_scan_ie_fw_cmd_room(mvm))); -} - -static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - const struct iwl_ucode_capabilities *capa = &mvm->fw->ucode_capa; - - /* We can only use EBS if: - * 1. the feature is supported; - * 2. the last EBS was successful; - * 3. if only single scan, the single scan EBS API is supported; - * 4. it's not a p2p find operation. - */ - return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) && - mvm->last_ebs_successful && - vif->type != NL80211_IFTYPE_P2P_DEVICE); -} - -static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm, - struct iwl_mvm_scan_params *params) -{ - int flags = 0; - - if (params->n_ssids == 0) - flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE; - - if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0) - flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION; - - if (params->type == IWL_SCAN_TYPE_FRAGMENTED) - flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED; - - if (iwl_mvm_rrm_scan_needed(mvm)) - flags |= IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED; - - if (params->pass_all) - flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL; - else - flags |= IWL_MVM_LMAC_SCAN_FLAG_MATCH; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvm->scan_iter_notif_enabled) - flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE; -#endif - - return flags; -} - -static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_scan_params *params) -{ - struct iwl_scan_req_lmac *cmd = mvm->scan_cmd; - struct iwl_scan_probe_req *preq = - (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) * - mvm->fw->ucode_capa.n_scan_channels); - u32 ssid_bitmap = 0; - int i; - - lockdep_assert_held(&mvm->mutex); - - memset(cmd, 0, ksize(cmd)); - - if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) - return -EINVAL; - - iwl_mvm_scan_lmac_dwell(mvm, cmd, params); - - cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm); - cmd->iter_num = cpu_to_le32(1); - cmd->n_channels = (u8)params->n_channels; - - cmd->delay = cpu_to_le32(params->delay); - - cmd->scan_flags = cpu_to_le32(iwl_mvm_scan_lmac_flags(mvm, params)); - - cmd->flags = iwl_mvm_scan_rxon_flags(params->channels[0]->band); - cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | - MAC_FILTER_IN_BEACON); - iwl_mvm_scan_fill_tx_cmd(mvm, cmd->tx_cmd, params->no_cck); - iwl_scan_build_ssids(params, cmd->direct_scan, &ssid_bitmap); - - /* this API uses bits 1-20 instead of 0-19 */ - ssid_bitmap <<= 1; - - for (i = 0; i < params->n_scan_plans; i++) { - struct cfg80211_sched_scan_plan *scan_plan = - ¶ms->scan_plans[i]; - - cmd->schedule[i].delay = - cpu_to_le16(scan_plan->interval); - cmd->schedule[i].iterations = scan_plan->iterations; - cmd->schedule[i].full_scan_mul = 1; - } - - /* - * If the number of iterations of the last scan plan is set to - * zero, it should run infinitely. However, this is not always the case. - * For example, when regular scan is requested the driver sets one scan - * plan with one iteration. - */ - if (!cmd->schedule[i - 1].iterations) - cmd->schedule[i - 1].iterations = 0xff; - - if (iwl_mvm_scan_use_ebs(mvm, vif)) { - cmd->channel_opt[0].flags = - cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); - cmd->channel_opt[0].non_ebs_ratio = - cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO); - cmd->channel_opt[1].flags = - cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); - cmd->channel_opt[1].non_ebs_ratio = - cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO); - } - - iwl_mvm_lmac_scan_cfg_channels(mvm, params->channels, - params->n_channels, ssid_bitmap, cmd); - - *preq = params->preq; - - return 0; -} - -static int rate_to_scan_rate_flag(unsigned int rate) -{ - static const int rate_to_scan_rate[IWL_RATE_COUNT] = { - [IWL_RATE_1M_INDEX] = SCAN_CONFIG_RATE_1M, - [IWL_RATE_2M_INDEX] = SCAN_CONFIG_RATE_2M, - [IWL_RATE_5M_INDEX] = SCAN_CONFIG_RATE_5M, - [IWL_RATE_11M_INDEX] = SCAN_CONFIG_RATE_11M, - [IWL_RATE_6M_INDEX] = SCAN_CONFIG_RATE_6M, - [IWL_RATE_9M_INDEX] = SCAN_CONFIG_RATE_9M, - [IWL_RATE_12M_INDEX] = SCAN_CONFIG_RATE_12M, - [IWL_RATE_18M_INDEX] = SCAN_CONFIG_RATE_18M, - [IWL_RATE_24M_INDEX] = SCAN_CONFIG_RATE_24M, - [IWL_RATE_36M_INDEX] = SCAN_CONFIG_RATE_36M, - [IWL_RATE_48M_INDEX] = SCAN_CONFIG_RATE_48M, - [IWL_RATE_54M_INDEX] = SCAN_CONFIG_RATE_54M, - }; - - return rate_to_scan_rate[rate]; -} - -static __le32 iwl_mvm_scan_config_rates(struct iwl_mvm *mvm) -{ - struct ieee80211_supported_band *band; - unsigned int rates = 0; - int i; - - band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; - for (i = 0; i < band->n_bitrates; i++) - rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value); - band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; - for (i = 0; i < band->n_bitrates; i++) - rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value); - - /* Set both basic rates and supported rates */ - rates |= SCAN_CONFIG_SUPPORTED_RATE(rates); - - return cpu_to_le32(rates); -} - -int iwl_mvm_config_scan(struct iwl_mvm *mvm) -{ - struct iwl_scan_config *scan_config; - struct ieee80211_supported_band *band; - int num_channels = - mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels + - mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; - int ret, i, j = 0, cmd_size; - struct iwl_host_cmd cmd = { - .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0), - }; - - if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels)) - return -ENOBUFS; - - cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels; - - scan_config = kzalloc(cmd_size, GFP_KERNEL); - if (!scan_config) - return -ENOMEM; - - scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE | - SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS | - SCAN_CONFIG_FLAG_SET_TX_CHAINS | - SCAN_CONFIG_FLAG_SET_RX_CHAINS | - SCAN_CONFIG_FLAG_SET_ALL_TIMES | - SCAN_CONFIG_FLAG_SET_LEGACY_RATES | - SCAN_CONFIG_FLAG_SET_MAC_ADDR | - SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS| - SCAN_CONFIG_N_CHANNELS(num_channels)); - scan_config->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); - scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm)); - scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm); - scan_config->out_of_channel_time = cpu_to_le32(170); - scan_config->suspend_time = cpu_to_le32(30); - scan_config->dwell_active = 20; - scan_config->dwell_passive = 110; - scan_config->dwell_fragmented = 20; - - memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); - - scan_config->bcast_sta_id = mvm->aux_sta.sta_id; - scan_config->channel_flags = IWL_CHANNEL_FLAG_EBS | - IWL_CHANNEL_FLAG_ACCURATE_EBS | - IWL_CHANNEL_FLAG_EBS_ADD | - IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE; - - band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; - for (i = 0; i < band->n_channels; i++, j++) - scan_config->channel_array[j] = band->channels[i].hw_value; - band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; - for (i = 0; i < band->n_channels; i++, j++) - scan_config->channel_array[j] = band->channels[i].hw_value; - - cmd.data[0] = scan_config; - cmd.len[0] = cmd_size; - cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - - IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n"); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(scan_config); - return ret; -} - -static int iwl_mvm_scan_uid_by_status(struct iwl_mvm *mvm, int status) -{ - int i; - - for (i = 0; i < mvm->max_scans; i++) - if (mvm->scan_uid_status[i] == status) - return i; - - return -ENOENT; -} - -static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params) -{ - return params->n_scan_plans == 1 && - params->scan_plans[0].iterations == 1; -} - -static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, - struct iwl_scan_req_umac *cmd, - struct iwl_mvm_scan_params *params) -{ - cmd->active_dwell = scan_timing[params->type].dwell_active; - cmd->passive_dwell = scan_timing[params->type].dwell_passive; - cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented; - cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time); - cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time); - cmd->scan_priority = - iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); - - if (iwl_mvm_is_regular_scan(params)) - cmd->ooc_priority = - iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); - else - cmd->ooc_priority = - iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_2); -} - -static void -iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm, - struct ieee80211_channel **channels, - int n_channels, u32 ssid_bitmap, - struct iwl_scan_req_umac *cmd) -{ - struct iwl_scan_channel_cfg_umac *channel_cfg = (void *)&cmd->data; - int i; - - for (i = 0; i < n_channels; i++) { - channel_cfg[i].flags = cpu_to_le32(ssid_bitmap); - channel_cfg[i].channel_num = channels[i]->hw_value; - channel_cfg[i].iter_count = 1; - channel_cfg[i].iter_interval = 0; - } -} - -static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm, - struct iwl_mvm_scan_params *params) -{ - int flags = 0; - - if (params->n_ssids == 0) - flags = IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE; - - if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT; - - if (params->type == IWL_SCAN_TYPE_FRAGMENTED) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED; - - if (iwl_mvm_rrm_scan_needed(mvm)) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED; - - if (params->pass_all) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL; - else - flags |= IWL_UMAC_SCAN_GEN_FLAGS_MATCH; - - if (!iwl_mvm_is_regular_scan(params)) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvm->scan_iter_notif_enabled) - flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE; -#endif - return flags; -} - -static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_scan_params *params, - int type) -{ - struct iwl_scan_req_umac *cmd = mvm->scan_cmd; - struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data + - sizeof(struct iwl_scan_channel_cfg_umac) * - mvm->fw->ucode_capa.n_scan_channels; - int uid, i; - u32 ssid_bitmap = 0; - - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) - return -EINVAL; - - uid = iwl_mvm_scan_uid_by_status(mvm, 0); - if (uid < 0) - return uid; - - memset(cmd, 0, ksize(cmd)); - - iwl_mvm_scan_umac_dwell(mvm, cmd, params); - - mvm->scan_uid_status[uid] = type; - - cmd->uid = cpu_to_le32(uid); - cmd->general_flags = cpu_to_le32(iwl_mvm_scan_umac_flags(mvm, params)); - - if (type == IWL_MVM_SCAN_SCHED) - cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE); - - if (iwl_mvm_scan_use_ebs(mvm, vif)) - cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS | - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; - - cmd->n_channels = params->n_channels; - - iwl_scan_build_ssids(params, sec_part->direct_scan, &ssid_bitmap); - - iwl_mvm_umac_scan_cfg_channels(mvm, params->channels, - params->n_channels, ssid_bitmap, cmd); - - for (i = 0; i < params->n_scan_plans; i++) { - struct cfg80211_sched_scan_plan *scan_plan = - ¶ms->scan_plans[i]; - - sec_part->schedule[i].iter_count = scan_plan->iterations; - sec_part->schedule[i].interval = - cpu_to_le16(scan_plan->interval); - } - - /* - * If the number of iterations of the last scan plan is set to - * zero, it should run infinitely. However, this is not always the case. - * For example, when regular scan is requested the driver sets one scan - * plan with one iteration. - */ - if (!sec_part->schedule[i - 1].iter_count) - sec_part->schedule[i - 1].iter_count = 0xff; - - sec_part->delay = cpu_to_le16(params->delay); - sec_part->preq = params->preq; - - return 0; -} - -static int iwl_mvm_num_scans(struct iwl_mvm *mvm) -{ - return hweight32(mvm->scan_status & IWL_MVM_SCAN_MASK); -} - -static int iwl_mvm_check_running_scans(struct iwl_mvm *mvm, int type) -{ - /* This looks a bit arbitrary, but the idea is that if we run - * out of possible simultaneous scans and the userspace is - * trying to run a scan type that is already running, we - * return -EBUSY. But if the userspace wants to start a - * different type of scan, we stop the opposite type to make - * space for the new request. The reason is backwards - * compatibility with old wpa_supplicant that wouldn't stop a - * scheduled scan before starting a normal scan. - */ - - if (iwl_mvm_num_scans(mvm) < mvm->max_scans) - return 0; - - /* Use a switch, even though this is a bitmask, so that more - * than one bits set will fall in default and we will warn. - */ - switch (type) { - case IWL_MVM_SCAN_REGULAR: - if (mvm->scan_status & IWL_MVM_SCAN_REGULAR_MASK) - return -EBUSY; - return iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true); - case IWL_MVM_SCAN_SCHED: - if (mvm->scan_status & IWL_MVM_SCAN_SCHED_MASK) - return -EBUSY; - iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); - case IWL_MVM_SCAN_NETDETECT: - /* No need to stop anything for net-detect since the - * firmware is restarted anyway. This way, any sched - * scans that were running will be restarted when we - * resume. - */ - return 0; - default: - WARN_ON(1); - break; - } - - return -EIO; -} - -int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct cfg80211_scan_request *req, - struct ieee80211_scan_ies *ies) -{ - struct iwl_host_cmd hcmd = { - .len = { iwl_mvm_scan_size(mvm), }, - .data = { mvm->scan_cmd, }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - struct iwl_mvm_scan_params params = {}; - int ret; - struct cfg80211_sched_scan_plan scan_plan = { .iterations = 1 }; - - lockdep_assert_held(&mvm->mutex); - - if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) { - IWL_ERR(mvm, "scan while LAR regdomain is not set\n"); - return -EBUSY; - } - - ret = iwl_mvm_check_running_scans(mvm, IWL_MVM_SCAN_REGULAR); - if (ret) - return ret; - - /* we should have failed registration if scan_cmd was NULL */ - if (WARN_ON(!mvm->scan_cmd)) - return -ENOMEM; - - if (!iwl_mvm_scan_fits(mvm, req->n_ssids, ies, req->n_channels)) - return -ENOBUFS; - - params.n_ssids = req->n_ssids; - params.flags = req->flags; - params.n_channels = req->n_channels; - params.delay = 0; - params.ssids = req->ssids; - params.channels = req->channels; - params.mac_addr = req->mac_addr; - params.mac_addr_mask = req->mac_addr_mask; - params.no_cck = req->no_cck; - params.pass_all = true; - params.n_match_sets = 0; - params.match_sets = NULL; - - params.scan_plans = &scan_plan; - params.n_scan_plans = 1; - - params.type = iwl_mvm_get_scan_type(mvm, vif, ¶ms); - - iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); - ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, - IWL_MVM_SCAN_REGULAR); - } else { - hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; - ret = iwl_mvm_scan_lmac(mvm, vif, ¶ms); - } - - if (ret) - return ret; - - ret = iwl_mvm_send_cmd(mvm, &hcmd); - if (ret) { - /* If the scan failed, it usually means that the FW was unable - * to allocate the time events. Warn on it, but maybe we - * should try to send the command again with different params. - */ - IWL_ERR(mvm, "Scan failed! ret %d\n", ret); - return ret; - } - - IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n"); - mvm->scan_status |= IWL_MVM_SCAN_REGULAR; - iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN); - - return 0; -} - -int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct cfg80211_sched_scan_request *req, - struct ieee80211_scan_ies *ies, - int type) -{ - struct iwl_host_cmd hcmd = { - .len = { iwl_mvm_scan_size(mvm), }, - .data = { mvm->scan_cmd, }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - struct iwl_mvm_scan_params params = {}; - int ret; - - lockdep_assert_held(&mvm->mutex); - - if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) { - IWL_ERR(mvm, "sched-scan while LAR regdomain is not set\n"); - return -EBUSY; - } - - ret = iwl_mvm_check_running_scans(mvm, type); - if (ret) - return ret; - - /* we should have failed registration if scan_cmd was NULL */ - if (WARN_ON(!mvm->scan_cmd)) - return -ENOMEM; - - if (!iwl_mvm_scan_fits(mvm, req->n_ssids, ies, req->n_channels)) - return -ENOBUFS; - - params.n_ssids = req->n_ssids; - params.flags = req->flags; - params.n_channels = req->n_channels; - params.ssids = req->ssids; - params.channels = req->channels; - params.mac_addr = req->mac_addr; - params.mac_addr_mask = req->mac_addr_mask; - params.no_cck = false; - params.pass_all = iwl_mvm_scan_pass_all(mvm, req); - params.n_match_sets = req->n_match_sets; - params.match_sets = req->match_sets; - if (!req->n_scan_plans) - return -EINVAL; - - params.n_scan_plans = req->n_scan_plans; - params.scan_plans = req->scan_plans; - - params.type = iwl_mvm_get_scan_type(mvm, vif, ¶ms); - - /* In theory, LMAC scans can handle a 32-bit delay, but since - * waiting for over 18 hours to start the scan is a bit silly - * and to keep it aligned with UMAC scans (which only support - * 16-bit delays), trim it down to 16-bits. - */ - if (req->delay > U16_MAX) { - IWL_DEBUG_SCAN(mvm, - "delay value is > 16-bits, set to max possible\n"); - params.delay = U16_MAX; - } else { - params.delay = req->delay; - } - - ret = iwl_mvm_config_sched_scan_profiles(mvm, req); - if (ret) - return ret; - - iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); - ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, IWL_MVM_SCAN_SCHED); - } else { - hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; - ret = iwl_mvm_scan_lmac(mvm, vif, ¶ms); - } - - if (ret) - return ret; - - ret = iwl_mvm_send_cmd(mvm, &hcmd); - if (!ret) { - IWL_DEBUG_SCAN(mvm, - "Sched scan request was sent successfully\n"); - mvm->scan_status |= type; - } else { - /* If the scan failed, it usually means that the FW was unable - * to allocate the time events. Warn on it, but maybe we - * should try to send the command again with different params. - */ - IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret); - } - - return ret; -} - -void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_umac_scan_complete *notif = (void *)pkt->data; - u32 uid = __le32_to_cpu(notif->uid); - bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED); - - if (WARN_ON(!(mvm->scan_uid_status[uid] & mvm->scan_status))) - return; - - /* if the scan is already stopping, we don't need to notify mac80211 */ - if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_REGULAR) { - ieee80211_scan_completed(mvm->hw, aborted); - iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); - } else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) { - ieee80211_sched_scan_stopped(mvm->hw); - } - - mvm->scan_status &= ~mvm->scan_uid_status[uid]; - IWL_DEBUG_SCAN(mvm, - "Scan completed, uid %u type %u, status %s, EBS status %s\n", - uid, mvm->scan_uid_status[uid], - notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? - "completed" : "aborted", - iwl_mvm_ebs_status_str(notif->ebs_status)); - IWL_DEBUG_SCAN(mvm, - "Last line %d, Last iteration %d, Time from last iteration %d\n", - notif->last_schedule, notif->last_iter, - __le32_to_cpu(notif->time_from_last_iter)); - - if (notif->ebs_status != IWL_SCAN_EBS_SUCCESS && - notif->ebs_status != IWL_SCAN_EBS_INACTIVE) - mvm->last_ebs_successful = false; - - mvm->scan_uid_status[uid] = 0; -} - -void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_umac_scan_iter_complete_notif *notif = (void *)pkt->data; - u8 buf[256]; - - IWL_DEBUG_SCAN(mvm, - "UMAC Scan iteration complete: status=0x%x scanned_channels=%d channels list: %s\n", - notif->status, notif->scanned_channels, - iwl_mvm_dump_channel_list(notif->results, - notif->scanned_channels, buf, - sizeof(buf))); -} - -static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type) -{ - struct iwl_umac_scan_abort cmd = {}; - int uid, ret; - - lockdep_assert_held(&mvm->mutex); - - /* We should always get a valid index here, because we already - * checked that this type of scan was running in the generic - * code. - */ - uid = iwl_mvm_scan_uid_by_status(mvm, type); - if (WARN_ON_ONCE(uid < 0)) - return uid; - - cmd.uid = cpu_to_le32(uid); - - IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid); - - ret = iwl_mvm_send_cmd_pdu(mvm, - iwl_cmd_id(SCAN_ABORT_UMAC, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(cmd), &cmd); - if (!ret) - mvm->scan_uid_status[uid] = type << IWL_MVM_SCAN_STOPPING_SHIFT; - - return ret; -} - -static int iwl_mvm_scan_stop_wait(struct iwl_mvm *mvm, int type) -{ - struct iwl_notification_wait wait_scan_done; - static const u16 scan_done_notif[] = { SCAN_COMPLETE_UMAC, - SCAN_OFFLOAD_COMPLETE, }; - int ret; - - lockdep_assert_held(&mvm->mutex); - - iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done, - scan_done_notif, - ARRAY_SIZE(scan_done_notif), - NULL, NULL); - - IWL_DEBUG_SCAN(mvm, "Preparing to stop scan, type %x\n", type); - - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) - ret = iwl_mvm_umac_scan_abort(mvm, type); - else - ret = iwl_mvm_lmac_scan_abort(mvm); - - if (ret) { - IWL_DEBUG_SCAN(mvm, "couldn't stop scan type %d\n", type); - iwl_remove_notification(&mvm->notif_wait, &wait_scan_done); - return ret; - } - - ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ); - - return ret; -} - -int iwl_mvm_scan_size(struct iwl_mvm *mvm) -{ - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) - return sizeof(struct iwl_scan_req_umac) + - sizeof(struct iwl_scan_channel_cfg_umac) * - mvm->fw->ucode_capa.n_scan_channels + - sizeof(struct iwl_scan_req_umac_tail); - - return sizeof(struct iwl_scan_req_lmac) + - sizeof(struct iwl_scan_channel_cfg_lmac) * - mvm->fw->ucode_capa.n_scan_channels + - sizeof(struct iwl_scan_probe_req); -} - -/* - * This function is used in nic restart flow, to inform mac80211 about scans - * that was aborted by restart flow or by an assert. - */ -void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm) -{ - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - int uid, i; - - uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_REGULAR); - if (uid >= 0) { - ieee80211_scan_completed(mvm->hw, true); - mvm->scan_uid_status[uid] = 0; - } - uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_SCHED); - if (uid >= 0 && !mvm->restart_fw) { - ieee80211_sched_scan_stopped(mvm->hw); - mvm->scan_uid_status[uid] = 0; - } - - /* We shouldn't have any UIDs still set. Loop over all the - * UIDs to make sure there's nothing left there and warn if - * any is found. - */ - for (i = 0; i < mvm->max_scans; i++) { - if (WARN_ONCE(mvm->scan_uid_status[i], - "UMAC scan UID %d status was not cleaned\n", - i)) - mvm->scan_uid_status[i] = 0; - } - } else { - if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) - ieee80211_scan_completed(mvm->hw, true); - - /* Sched scan will be restarted by mac80211 in - * restart_hw, so do not report if FW is about to be - * restarted. - */ - if ((mvm->scan_status & IWL_MVM_SCAN_SCHED) && !mvm->restart_fw) - ieee80211_sched_scan_stopped(mvm->hw); - } -} - -int iwl_mvm_scan_stop(struct iwl_mvm *mvm, int type, bool notify) -{ - int ret; - - if (!(mvm->scan_status & type)) - return 0; - - if (iwl_mvm_is_radio_killed(mvm)) { - ret = 0; - goto out; - } - - ret = iwl_mvm_scan_stop_wait(mvm, type); - if (!ret) - mvm->scan_status |= type << IWL_MVM_SCAN_STOPPING_SHIFT; -out: - /* Clear the scan status so the next scan requests will - * succeed and mark the scan as stopping, so that the Rx - * handler doesn't do anything, as the scan was stopped from - * above. - */ - mvm->scan_status &= ~type; - - if (type == IWL_MVM_SCAN_REGULAR) { - /* Since the rx handler won't do anything now, we have - * to release the scan reference here. - */ - iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); - if (notify) - ieee80211_scan_completed(mvm->hw, true); - } else if (notify) { - ieee80211_sched_scan_stopped(mvm->hw); - } - - return ret; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c deleted file mode 100644 index b0f59fdd287c..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/sf.c +++ /dev/null @@ -1,340 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include "mvm.h" - -/* For counting bound interfaces */ -struct iwl_mvm_active_iface_iterator_data { - struct ieee80211_vif *ignore_vif; - u8 sta_vif_ap_sta_id; - enum iwl_sf_state sta_vif_state; - int num_active_macs; -}; - -/* - * Count bound interfaces which are not p2p, besides data->ignore_vif. - * data->station_vif will point to one bound vif of type station, if exists. - */ -static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_active_iface_iterator_data *data = _data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (vif == data->ignore_vif || !mvmvif->phy_ctxt || - vif->type == NL80211_IFTYPE_P2P_DEVICE) - return; - - data->num_active_macs++; - - if (vif->type == NL80211_IFTYPE_STATION) { - data->sta_vif_ap_sta_id = mvmvif->ap_sta_id; - if (vif->bss_conf.assoc) - data->sta_vif_state = SF_FULL_ON; - else - data->sta_vif_state = SF_INIT_OFF; - } -} - -/* - * Aging and idle timeouts for the different possible scenarios - * in default configuration - */ -static const -__le32 sf_full_timeout_def[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = { - { - cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER_DEF), - cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER_DEF) - }, - { - cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER_DEF), - cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER_DEF) - }, - { - cpu_to_le32(SF_MCAST_AGING_TIMER_DEF), - cpu_to_le32(SF_MCAST_IDLE_TIMER_DEF) - }, - { - cpu_to_le32(SF_BA_AGING_TIMER_DEF), - cpu_to_le32(SF_BA_IDLE_TIMER_DEF) - }, - { - cpu_to_le32(SF_TX_RE_AGING_TIMER_DEF), - cpu_to_le32(SF_TX_RE_IDLE_TIMER_DEF) - }, -}; - -/* - * Aging and idle timeouts for the different possible scenarios - * in single BSS MAC configuration. - */ -static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = { - { - cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER), - cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER) - }, - { - cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER), - cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER) - }, - { - cpu_to_le32(SF_MCAST_AGING_TIMER), - cpu_to_le32(SF_MCAST_IDLE_TIMER) - }, - { - cpu_to_le32(SF_BA_AGING_TIMER), - cpu_to_le32(SF_BA_IDLE_TIMER) - }, - { - cpu_to_le32(SF_TX_RE_AGING_TIMER), - cpu_to_le32(SF_TX_RE_IDLE_TIMER) - }, -}; - -static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm, - struct iwl_sf_cfg_cmd *sf_cmd, - struct ieee80211_sta *sta) -{ - int i, j, watermark; - - sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN); - - /* - * If we are in association flow - check antenna configuration - * capabilities of the AP station, and choose the watermark accordingly. - */ - if (sta) { - if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) { - switch (sta->rx_nss) { - case 1: - watermark = SF_W_MARK_SISO; - break; - case 2: - watermark = SF_W_MARK_MIMO2; - break; - default: - watermark = SF_W_MARK_MIMO3; - break; - } - } else { - watermark = SF_W_MARK_LEGACY; - } - /* default watermark value for unassociated mode. */ - } else { - watermark = SF_W_MARK_MIMO2; - } - sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark); - - for (i = 0; i < SF_NUM_SCENARIO; i++) { - for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) { - sf_cmd->long_delay_timeouts[i][j] = - cpu_to_le32(SF_LONG_DELAY_AGING_TIMER); - } - } - - if (sta || IWL_UCODE_API(mvm->fw->ucode_ver) < 13) { - BUILD_BUG_ON(sizeof(sf_full_timeout) != - sizeof(__le32) * SF_NUM_SCENARIO * - SF_NUM_TIMEOUT_TYPES); - - memcpy(sf_cmd->full_on_timeouts, sf_full_timeout, - sizeof(sf_full_timeout)); - } else { - BUILD_BUG_ON(sizeof(sf_full_timeout_def) != - sizeof(__le32) * SF_NUM_SCENARIO * - SF_NUM_TIMEOUT_TYPES); - - memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def, - sizeof(sf_full_timeout_def)); - } - -} - -static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, - enum iwl_sf_state new_state) -{ - struct iwl_sf_cfg_cmd sf_cmd = { - .state = cpu_to_le32(SF_FULL_ON), - }; - struct ieee80211_sta *sta; - int ret = 0; - - if (IWL_UCODE_API(mvm->fw->ucode_ver) < 13) - sf_cmd.state = cpu_to_le32(new_state); - - if (mvm->cfg->disable_dummy_notification) - sf_cmd.state |= cpu_to_le32(SF_CFG_DUMMY_NOTIF_OFF); - - /* - * If an associated AP sta changed its antenna configuration, the state - * will remain FULL_ON but SF parameters need to be reconsidered. - */ - if (new_state != SF_FULL_ON && mvm->sf_state == new_state) - return 0; - - switch (new_state) { - case SF_UNINIT: - if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 13) - iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL); - break; - case SF_FULL_ON: - if (sta_id == IWL_MVM_STATION_COUNT) { - IWL_ERR(mvm, - "No station: Cannot switch SF to FULL_ON\n"); - return -EINVAL; - } - rcu_read_lock(); - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (IS_ERR_OR_NULL(sta)) { - IWL_ERR(mvm, "Invalid station id\n"); - rcu_read_unlock(); - return -EINVAL; - } - iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta); - rcu_read_unlock(); - break; - case SF_INIT_OFF: - iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL); - break; - default: - WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n", - new_state); - return -EINVAL; - } - - ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC, - sizeof(sf_cmd), &sf_cmd); - if (!ret) - mvm->sf_state = new_state; - - return ret; -} - -/* - * Update Smart fifo: - * Count bound interfaces that are not to be removed, ignoring p2p devices, - * and set new state accordingly. - */ -int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif, - bool remove_vif) -{ - enum iwl_sf_state new_state; - u8 sta_id = IWL_MVM_STATION_COUNT; - struct iwl_mvm_vif *mvmvif = NULL; - struct iwl_mvm_active_iface_iterator_data data = { - .ignore_vif = changed_vif, - .sta_vif_state = SF_UNINIT, - .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT, - }; - - /* - * Ignore the call if we are in HW Restart flow, or if the handled - * vif is a p2p device. - */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || - (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE)) - return 0; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bound_iface_iterator, - &data); - - /* If changed_vif exists and is not to be removed, add to the count */ - if (changed_vif && !remove_vif) - data.num_active_macs++; - - switch (data.num_active_macs) { - case 0: - /* If there are no active macs - change state to SF_INIT_OFF */ - new_state = SF_INIT_OFF; - break; - case 1: - if (remove_vif) { - /* The one active mac left is of type station - * and we filled the relevant data during iteration - */ - new_state = data.sta_vif_state; - sta_id = data.sta_vif_ap_sta_id; - } else { - if (WARN_ON(!changed_vif)) - return -EINVAL; - if (changed_vif->type != NL80211_IFTYPE_STATION) { - new_state = SF_UNINIT; - } else if (changed_vif->bss_conf.assoc && - changed_vif->bss_conf.dtim_period) { - mvmvif = iwl_mvm_vif_from_mac80211(changed_vif); - sta_id = mvmvif->ap_sta_id; - new_state = SF_FULL_ON; - } else { - new_state = SF_INIT_OFF; - } - } - break; - default: - /* If there are multiple active macs - change to SF_UNINIT */ - new_state = SF_UNINIT; - } - return iwl_mvm_sf_config(mvm, sta_id, new_state); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c deleted file mode 100644 index 300a249486e4..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ /dev/null @@ -1,1810 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include - -#include "mvm.h" -#include "sta.h" -#include "rs.h" - -static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, - enum nl80211_iftype iftype) -{ - int sta_id; - u32 reserved_ids = 0; - - BUILD_BUG_ON(IWL_MVM_STATION_COUNT > 32); - WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)); - - lockdep_assert_held(&mvm->mutex); - - /* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */ - if (iftype != NL80211_IFTYPE_STATION) - reserved_ids = BIT(0); - - /* Don't take rcu_read_lock() since we are protected by mvm->mutex */ - for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) { - if (BIT(sta_id) & reserved_ids) - continue; - - if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex))) - return sta_id; - } - return IWL_MVM_STATION_COUNT; -} - -/* send station add/update command to firmware */ -int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - bool update) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_add_sta_cmd add_sta_cmd = { - .sta_id = mvm_sta->sta_id, - .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color), - .add_modify = update ? 1 : 0, - .station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK | - STA_FLG_MIMO_EN_MSK), - }; - int ret; - u32 status; - u32 agg_size = 0, mpdu_dens = 0; - - if (!update) { - add_sta_cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk); - memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN); - } - - switch (sta->bandwidth) { - case IEEE80211_STA_RX_BW_160: - add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_160MHZ); - /* fall through */ - case IEEE80211_STA_RX_BW_80: - add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_80MHZ); - /* fall through */ - case IEEE80211_STA_RX_BW_40: - add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_FAT_EN_40MHZ); - /* fall through */ - case IEEE80211_STA_RX_BW_20: - if (sta->ht_cap.ht_supported) - add_sta_cmd.station_flags |= - cpu_to_le32(STA_FLG_FAT_EN_20MHZ); - break; - } - - switch (sta->rx_nss) { - case 1: - add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO); - break; - case 2: - add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO2); - break; - case 3 ... 8: - add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_MIMO3); - break; - } - - switch (sta->smps_mode) { - case IEEE80211_SMPS_AUTOMATIC: - case IEEE80211_SMPS_NUM_MODES: - WARN_ON(1); - break; - case IEEE80211_SMPS_STATIC: - /* override NSS */ - add_sta_cmd.station_flags &= ~cpu_to_le32(STA_FLG_MIMO_EN_MSK); - add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_MIMO_EN_SISO); - break; - case IEEE80211_SMPS_DYNAMIC: - add_sta_cmd.station_flags |= cpu_to_le32(STA_FLG_RTS_MIMO_PROT); - break; - case IEEE80211_SMPS_OFF: - /* nothing */ - break; - } - - if (sta->ht_cap.ht_supported) { - add_sta_cmd.station_flags_msk |= - cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK | - STA_FLG_AGG_MPDU_DENS_MSK); - - mpdu_dens = sta->ht_cap.ampdu_density; - } - - if (sta->vht_cap.vht_supported) { - agg_size = sta->vht_cap.cap & - IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; - agg_size >>= - IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; - } else if (sta->ht_cap.ht_supported) { - agg_size = sta->ht_cap.ampdu_factor; - } - - add_sta_cmd.station_flags |= - cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT); - add_sta_cmd.station_flags |= - cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT); - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd), - &add_sta_cmd, &status); - if (ret) - return ret; - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_ASSOC(mvm, "ADD_STA PASSED\n"); - break; - default: - ret = -EIO; - IWL_ERR(mvm, "ADD_STA failed\n"); - break; - } - - return ret; -} - -static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - unsigned long used_hw_queues; - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, NULL, true, false); - u32 ac; - - lockdep_assert_held(&mvm->mutex); - - used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, NULL); - - /* Find available queues, and allocate them to the ACs */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - u8 queue = find_first_zero_bit(&used_hw_queues, - mvm->first_agg_queue); - - if (queue >= mvm->first_agg_queue) { - IWL_ERR(mvm, "Failed to allocate STA queue\n"); - return -EBUSY; - } - - __set_bit(queue, &used_hw_queues); - mvmsta->hw_queue[ac] = queue; - } - - /* Found a place for all queues - enable them */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac], - mvmsta->hw_queue[ac], - iwl_mvm_ac_to_tx_fifo[ac], 0, - wdg_timeout); - mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]); - } - - return 0; -} - -static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - unsigned long sta_msk; - int i; - - lockdep_assert_held(&mvm->mutex); - - /* disable the TDLS STA-specific queues */ - sta_msk = mvmsta->tfd_queue_msk; - for_each_set_bit(i, &sta_msk, sizeof(sta_msk) * BITS_PER_BYTE) - iwl_mvm_disable_txq(mvm, i, i, IWL_MAX_TID_COUNT, 0); -} - -int iwl_mvm_add_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - int i, ret, sta_id; - - lockdep_assert_held(&mvm->mutex); - - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - sta_id = iwl_mvm_find_free_sta_id(mvm, - ieee80211_vif_type_p2p(vif)); - else - sta_id = mvm_sta->sta_id; - - if (sta_id == IWL_MVM_STATION_COUNT) - return -ENOSPC; - - if (vif->type == NL80211_IFTYPE_AP) { - mvmvif->ap_assoc_sta_count++; - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - } - - spin_lock_init(&mvm_sta->lock); - - mvm_sta->sta_id = sta_id; - mvm_sta->mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color); - mvm_sta->vif = vif; - mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; - mvm_sta->tx_protection = 0; - mvm_sta->tt_tx_protection = false; - - /* HW restart, don't assume the memory has been zeroed */ - atomic_set(&mvm->pending_frames[sta_id], 0); - mvm_sta->tid_disable_agg = 0xffff; /* No aggs at first */ - mvm_sta->tfd_queue_msk = 0; - - /* allocate new queues for a TDLS station */ - if (sta->tdls) { - ret = iwl_mvm_tdls_sta_init(mvm, sta); - if (ret) - return ret; - } else { - for (i = 0; i < IEEE80211_NUM_ACS; i++) - if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) - mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]); - } - - /* for HW restart - reset everything but the sequence number */ - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - u16 seq = mvm_sta->tid_data[i].seq_number; - memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i])); - mvm_sta->tid_data[i].seq_number = seq; - } - mvm_sta->agg_tids = 0; - - ret = iwl_mvm_sta_send_to_fw(mvm, sta, false); - if (ret) - goto err; - - if (vif->type == NL80211_IFTYPE_STATION) { - if (!sta->tdls) { - WARN_ON(mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT); - mvmvif->ap_sta_id = sta_id; - } else { - WARN_ON(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT); - } - } - - rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta); - - return 0; - -err: - iwl_mvm_tdls_sta_deinit(mvm, sta); - return ret; -} - -int iwl_mvm_update_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - return iwl_mvm_sta_send_to_fw(mvm, sta, true); -} - -int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, - bool drain) -{ - struct iwl_mvm_add_sta_cmd cmd = {}; - int ret; - u32 status; - - lockdep_assert_held(&mvm->mutex); - - cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color); - cmd.sta_id = mvmsta->sta_id; - cmd.add_modify = STA_MODE_MODIFY; - cmd.station_flags = drain ? cpu_to_le32(STA_FLG_DRAIN_FLOW) : 0; - cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW); - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); - if (ret) - return ret; - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_INFO(mvm, "Frames for staid %d will drained in fw\n", - mvmsta->sta_id); - break; - default: - ret = -EIO; - IWL_ERR(mvm, "Couldn't drain frames for staid %d\n", - mvmsta->sta_id); - break; - } - - return ret; -} - -/* - * Remove a station from the FW table. Before sending the command to remove - * the station validate that the station is indeed known to the driver (sanity - * only). - */ -static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_rm_sta_cmd rm_sta_cmd = { - .sta_id = sta_id, - }; - int ret; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - - /* Note: internal stations are marked as error values */ - if (!sta) { - IWL_ERR(mvm, "Invalid station id\n"); - return -EINVAL; - } - - ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, 0, - sizeof(rm_sta_cmd), &rm_sta_cmd); - if (ret) { - IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id); - return ret; - } - - return 0; -} - -void iwl_mvm_sta_drained_wk(struct work_struct *wk) -{ - struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, sta_drained_wk); - u8 sta_id; - - /* - * The mutex is needed because of the SYNC cmd, but not only: if the - * work would run concurrently with iwl_mvm_rm_sta, it would run before - * iwl_mvm_rm_sta sets the station as busy, and exit. Then - * iwl_mvm_rm_sta would set the station as busy, and nobody will clean - * that later. - */ - mutex_lock(&mvm->mutex); - - for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) { - int ret; - struct ieee80211_sta *sta = - rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - - /* - * This station is in use or RCU-removed; the latter happens in - * managed mode, where mac80211 removes the station before we - * can remove it from firmware (we can only do that after the - * MAC is marked unassociated), and possibly while the deauth - * frame to disconnect from the AP is still queued. Then, the - * station pointer is -ENOENT when the last skb is reclaimed. - */ - if (!IS_ERR(sta) || PTR_ERR(sta) == -ENOENT) - continue; - - if (PTR_ERR(sta) == -EINVAL) { - IWL_ERR(mvm, "Drained sta %d, but it is internal?\n", - sta_id); - continue; - } - - if (!sta) { - IWL_ERR(mvm, "Drained sta %d, but it was NULL?\n", - sta_id); - continue; - } - - WARN_ON(PTR_ERR(sta) != -EBUSY); - /* This station was removed and we waited until it got drained, - * we can now proceed and remove it. - */ - ret = iwl_mvm_rm_sta_common(mvm, sta_id); - if (ret) { - IWL_ERR(mvm, - "Couldn't remove sta %d after it was drained\n", - sta_id); - continue; - } - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL); - clear_bit(sta_id, mvm->sta_drained); - - if (mvm->tfd_drained[sta_id]) { - unsigned long i, msk = mvm->tfd_drained[sta_id]; - - for_each_set_bit(i, &msk, sizeof(msk) * BITS_PER_BYTE) - iwl_mvm_disable_txq(mvm, i, i, - IWL_MAX_TID_COUNT, 0); - - mvm->tfd_drained[sta_id] = 0; - IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n", - sta_id, msk); - } - } - - mutex_unlock(&mvm->mutex); -} - -int iwl_mvm_rm_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - int ret; - - lockdep_assert_held(&mvm->mutex); - - if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id == mvm_sta->sta_id) { - ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); - if (ret) - return ret; - /* flush its queues here since we are freeing mvm_sta */ - ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, 0); - if (ret) - return ret; - ret = iwl_trans_wait_tx_queue_empty(mvm->trans, - mvm_sta->tfd_queue_msk); - if (ret) - return ret; - ret = iwl_mvm_drain_sta(mvm, mvm_sta, false); - - /* if we are associated - we can't remove the AP STA now */ - if (vif->bss_conf.assoc) - return ret; - - /* unassoc - go ahead - remove the AP STA now */ - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - - /* clear d0i3_ap_sta_id if no longer relevant */ - if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id) - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; - } - - /* - * This shouldn't happen - the TDLS channel switch should be canceled - * before the STA is removed. - */ - if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == mvm_sta->sta_id)) { - mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; - cancel_delayed_work(&mvm->tdls_cs.dwork); - } - - /* - * Make sure that the tx response code sees the station as -EBUSY and - * calls the drain worker. - */ - spin_lock_bh(&mvm_sta->lock); - /* - * There are frames pending on the AC queues for this station. - * We need to wait until all the frames are drained... - */ - if (atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) { - rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], - ERR_PTR(-EBUSY)); - spin_unlock_bh(&mvm_sta->lock); - - /* disable TDLS sta queues on drain complete */ - if (sta->tdls) { - mvm->tfd_drained[mvm_sta->sta_id] = - mvm_sta->tfd_queue_msk; - IWL_DEBUG_TDLS(mvm, "Draining TDLS sta %d\n", - mvm_sta->sta_id); - } - - ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); - } else { - spin_unlock_bh(&mvm_sta->lock); - - if (sta->tdls) - iwl_mvm_tdls_sta_deinit(mvm, sta); - - ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id); - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL); - } - - return ret; -} - -int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u8 sta_id) -{ - int ret = iwl_mvm_rm_sta_common(mvm, sta_id); - - lockdep_assert_held(&mvm->mutex); - - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL); - return ret; -} - -static int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *sta, - u32 qmask, enum nl80211_iftype iftype) -{ - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype); - if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT)) - return -ENOSPC; - } - - sta->tfd_queue_msk = qmask; - - /* put a non-NULL value so iterating over the stations won't stop */ - rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], ERR_PTR(-EINVAL)); - return 0; -} - -static void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *sta) -{ - RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL); - memset(sta, 0, sizeof(struct iwl_mvm_int_sta)); - sta->sta_id = IWL_MVM_STATION_COUNT; -} - -static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *sta, - const u8 *addr, - u16 mac_id, u16 color) -{ - struct iwl_mvm_add_sta_cmd cmd; - int ret; - u32 status; - - lockdep_assert_held(&mvm->mutex); - - memset(&cmd, 0, sizeof(cmd)); - cmd.sta_id = sta->sta_id; - cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, - color)); - - cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk); - - if (addr) - memcpy(cmd.addr, addr, ETH_ALEN); - - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); - if (ret) - return ret; - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_INFO(mvm, "Internal station added.\n"); - return 0; - default: - ret = -EIO; - IWL_ERR(mvm, "Add internal station failed, status=0x%x\n", - status); - break; - } - return ret; -} - -int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) -{ - unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? - mvm->cfg->base_params->wd_timeout : - IWL_WATCHDOG_DISABLED; - int ret; - - lockdep_assert_held(&mvm->mutex); - - /* Map Aux queue to fifo - needs to happen before adding Aux station */ - iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue, - IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); - - /* Allocate aux station and assign to it the aux queue */ - ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue), - NL80211_IFTYPE_UNSPECIFIED); - if (ret) - return ret; - - ret = iwl_mvm_add_int_sta_common(mvm, &mvm->aux_sta, NULL, - MAC_INDEX_AUX, 0); - - if (ret) - iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta); - return ret; -} - -void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm) -{ - lockdep_assert_held(&mvm->mutex); - - iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta); -} - -/* - * Send the add station command for the vif's broadcast station. - * Assumes that the station was already allocated. - * - * @mvm: the mvm component - * @vif: the interface to which the broadcast station is added - * @bsta: the broadcast station to add. - */ -int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; - static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - const u8 *baddr = _baddr; - - lockdep_assert_held(&mvm->mutex); - - if (vif->type == NL80211_IFTYPE_ADHOC) - baddr = vif->bss_conf.bssid; - - if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT)) - return -ENOSPC; - - return iwl_mvm_add_int_sta_common(mvm, bsta, baddr, - mvmvif->id, mvmvif->color); -} - -/* Send the FW a request to remove the station from it's internal data - * structures, but DO NOT remove the entry from the local data structures. */ -int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int ret; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_rm_sta_common(mvm, mvmvif->bcast_sta.sta_id); - if (ret) - IWL_WARN(mvm, "Failed sending remove station\n"); - return ret; -} - -int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u32 qmask; - - lockdep_assert_held(&mvm->mutex); - - qmask = iwl_mvm_mac_get_queues_mask(vif); - - /* - * The firmware defines the TFD queue mask to only be relevant - * for *unicast* queues, so the multicast (CAB) queue shouldn't - * be included. - */ - if (vif->type == NL80211_IFTYPE_AP) - qmask &= ~BIT(vif->cab_queue); - - return iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, qmask, - ieee80211_vif_type_p2p(vif)); -} - -/* Allocate a new station entry for the broadcast station to the given vif, - * and send it to the FW. - * Note that each P2P mac should have its own broadcast station. - * - * @mvm: the mvm component - * @vif: the interface to which the broadcast station is added - * @bsta: the broadcast station to add. */ -int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; - int ret; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_alloc_bcast_sta(mvm, vif); - if (ret) - return ret; - - ret = iwl_mvm_send_add_bcast_sta(mvm, vif); - - if (ret) - iwl_mvm_dealloc_int_sta(mvm, bsta); - - return ret; -} - -void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta); -} - -/* - * Send the FW a request to remove the station from it's internal data - * structures, and in addition remove it from the local data structure. - */ -int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - int ret; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_send_rm_bcast_sta(mvm, vif); - - iwl_mvm_dealloc_bcast_sta(mvm, vif); - - return ret; -} - -#define IWL_MAX_RX_BA_SESSIONS 16 - -int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, u16 ssn, bool start) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_add_sta_cmd cmd = {}; - int ret; - u32 status; - - lockdep_assert_held(&mvm->mutex); - - if (start && mvm->rx_ba_sessions >= IWL_MAX_RX_BA_SESSIONS) { - IWL_WARN(mvm, "Not enough RX BA SESSIONS\n"); - return -ENOSPC; - } - - cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); - cmd.sta_id = mvm_sta->sta_id; - cmd.add_modify = STA_MODE_MODIFY; - if (start) { - cmd.add_immediate_ba_tid = (u8) tid; - cmd.add_immediate_ba_ssn = cpu_to_le16(ssn); - } else { - cmd.remove_immediate_ba_tid = (u8) tid; - } - cmd.modify_mask = start ? STA_MODIFY_ADD_BA_TID : - STA_MODIFY_REMOVE_BA_TID; - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); - if (ret) - return ret; - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_INFO(mvm, "RX BA Session %sed in fw\n", - start ? "start" : "stopp"); - break; - case ADD_STA_IMMEDIATE_BA_FAILURE: - IWL_WARN(mvm, "RX BA Session refused by fw\n"); - ret = -ENOSPC; - break; - default: - ret = -EIO; - IWL_ERR(mvm, "RX BA Session failed %sing, status 0x%x\n", - start ? "start" : "stopp", status); - break; - } - - if (!ret) { - if (start) - mvm->rx_ba_sessions++; - else if (mvm->rx_ba_sessions > 0) - /* check that restart flow didn't zero the counter */ - mvm->rx_ba_sessions--; - } - - return ret; -} - -static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, u8 queue, bool start) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_add_sta_cmd cmd = {}; - int ret; - u32 status; - - lockdep_assert_held(&mvm->mutex); - - if (start) { - mvm_sta->tfd_queue_msk |= BIT(queue); - mvm_sta->tid_disable_agg &= ~BIT(tid); - } else { - mvm_sta->tfd_queue_msk &= ~BIT(queue); - mvm_sta->tid_disable_agg |= BIT(tid); - } - - cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); - cmd.sta_id = mvm_sta->sta_id; - cmd.add_modify = STA_MODE_MODIFY; - cmd.modify_mask = STA_MODIFY_QUEUES | STA_MODIFY_TID_DISABLE_TX; - cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk); - cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg); - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); - if (ret) - return ret; - - switch (status) { - case ADD_STA_SUCCESS: - break; - default: - ret = -EIO; - IWL_ERR(mvm, "TX BA Session failed %sing, status 0x%x\n", - start ? "start" : "stopp", status); - break; - } - - return ret; -} - -const u8 tid_to_mac80211_ac[] = { - IEEE80211_AC_BE, - IEEE80211_AC_BK, - IEEE80211_AC_BK, - IEEE80211_AC_BE, - IEEE80211_AC_VI, - IEEE80211_AC_VI, - IEEE80211_AC_VO, - IEEE80211_AC_VO, -}; - -static const u8 tid_to_ucode_ac[] = { - AC_BE, - AC_BK, - AC_BK, - AC_BE, - AC_VI, - AC_VI, - AC_VO, - AC_VO, -}; - -int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 *ssn) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data; - int txq_id; - int ret; - - if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) - return -EINVAL; - - if (mvmsta->tid_data[tid].state != IWL_AGG_OFF) { - IWL_ERR(mvm, "Start AGG when state is not IWL_AGG_OFF %d!\n", - mvmsta->tid_data[tid].state); - return -ENXIO; - } - - lockdep_assert_held(&mvm->mutex); - - spin_lock_bh(&mvmsta->lock); - - /* possible race condition - we entered D0i3 while starting agg */ - if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) { - spin_unlock_bh(&mvmsta->lock); - IWL_ERR(mvm, "Entered D0i3 while starting Tx agg\n"); - return -EIO; - } - - spin_lock_bh(&mvm->queue_info_lock); - - txq_id = iwl_mvm_find_free_queue(mvm, mvm->first_agg_queue, - mvm->last_agg_queue); - if (txq_id < 0) { - ret = txq_id; - spin_unlock_bh(&mvm->queue_info_lock); - IWL_ERR(mvm, "Failed to allocate agg queue\n"); - goto release_locks; - } - mvm->queue_info[txq_id].setup_reserved = true; - spin_unlock_bh(&mvm->queue_info_lock); - - tid_data = &mvmsta->tid_data[tid]; - tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); - tid_data->txq_id = txq_id; - *ssn = tid_data->ssn; - - IWL_DEBUG_TX_QUEUES(mvm, - "Start AGG: sta %d tid %d queue %d - ssn = %d, next_recl = %d\n", - mvmsta->sta_id, tid, txq_id, tid_data->ssn, - tid_data->next_reclaimed); - - if (tid_data->ssn == tid_data->next_reclaimed) { - tid_data->state = IWL_AGG_STARTING; - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - } else { - tid_data->state = IWL_EMPTYING_HW_QUEUE_ADDBA; - } - - ret = 0; - -release_locks: - spin_unlock_bh(&mvmsta->lock); - - return ret; -} - -int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u8 buf_size) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false); - int queue, fifo, ret; - u16 ssn; - - BUILD_BUG_ON((sizeof(mvmsta->agg_tids) * BITS_PER_BYTE) - != IWL_MAX_TID_COUNT); - - buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); - - spin_lock_bh(&mvmsta->lock); - ssn = tid_data->ssn; - queue = tid_data->txq_id; - tid_data->state = IWL_AGG_ON; - mvmsta->agg_tids |= BIT(tid); - tid_data->ssn = 0xffff; - spin_unlock_bh(&mvmsta->lock); - - fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; - - iwl_mvm_enable_agg_txq(mvm, queue, - vif->hw_queue[tid_to_mac80211_ac[tid]], fifo, - mvmsta->sta_id, tid, buf_size, ssn, wdg_timeout); - - ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); - if (ret) - return -EIO; - - /* No need to mark as reserved */ - spin_lock_bh(&mvm->queue_info_lock); - mvm->queue_info[queue].setup_reserved = false; - spin_unlock_bh(&mvm->queue_info_lock); - - /* - * Even though in theory the peer could have different - * aggregation reorder buffer sizes for different sessions, - * our ucode doesn't allow for that and has a global limit - * for each station. Therefore, use the minimum of all the - * aggregation sessions and our default value. - */ - mvmsta->max_agg_bufsize = - min(mvmsta->max_agg_bufsize, buf_size); - mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize; - - IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", - sta->addr, tid); - - return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false); -} - -int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - u16 txq_id; - int err; - - - /* - * If mac80211 is cleaning its state, then say that we finished since - * our state has been cleared anyway. - */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - return 0; - } - - spin_lock_bh(&mvmsta->lock); - - txq_id = tid_data->txq_id; - - IWL_DEBUG_TX_QUEUES(mvm, "Stop AGG: sta %d tid %d q %d state %d\n", - mvmsta->sta_id, tid, txq_id, tid_data->state); - - mvmsta->agg_tids &= ~BIT(tid); - - /* No need to mark as reserved anymore */ - spin_lock_bh(&mvm->queue_info_lock); - mvm->queue_info[txq_id].setup_reserved = false; - spin_unlock_bh(&mvm->queue_info_lock); - - switch (tid_data->state) { - case IWL_AGG_ON: - tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); - - IWL_DEBUG_TX_QUEUES(mvm, - "ssn = %d, next_recl = %d\n", - tid_data->ssn, tid_data->next_reclaimed); - - /* There are still packets for this RA / TID in the HW */ - if (tid_data->ssn != tid_data->next_reclaimed) { - tid_data->state = IWL_EMPTYING_HW_QUEUE_DELBA; - err = 0; - break; - } - - tid_data->ssn = 0xffff; - tid_data->state = IWL_AGG_OFF; - spin_unlock_bh(&mvmsta->lock); - - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - - iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); - - iwl_mvm_disable_txq(mvm, txq_id, - vif->hw_queue[tid_to_mac80211_ac[tid]], tid, - 0); - return 0; - case IWL_AGG_STARTING: - case IWL_EMPTYING_HW_QUEUE_ADDBA: - /* - * The agg session has been stopped before it was set up. This - * can happen when the AddBA timer times out for example. - */ - - /* No barriers since we are under mutex */ - lockdep_assert_held(&mvm->mutex); - - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - tid_data->state = IWL_AGG_OFF; - err = 0; - break; - default: - IWL_ERR(mvm, - "Stopping AGG while state not ON or starting for %d on %d (%d)\n", - mvmsta->sta_id, tid, tid_data->state); - IWL_ERR(mvm, - "\ttid_data->txq_id = %d\n", tid_data->txq_id); - err = -EINVAL; - } - - spin_unlock_bh(&mvmsta->lock); - - return err; -} - -int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - u16 txq_id; - enum iwl_mvm_agg_state old_state; - - /* - * First set the agg state to OFF to avoid calling - * ieee80211_stop_tx_ba_cb in iwl_mvm_check_ratid_empty. - */ - spin_lock_bh(&mvmsta->lock); - txq_id = tid_data->txq_id; - IWL_DEBUG_TX_QUEUES(mvm, "Flush AGG: sta %d tid %d q %d state %d\n", - mvmsta->sta_id, tid, txq_id, tid_data->state); - old_state = tid_data->state; - tid_data->state = IWL_AGG_OFF; - mvmsta->agg_tids &= ~BIT(tid); - spin_unlock_bh(&mvmsta->lock); - - /* No need to mark as reserved */ - spin_lock_bh(&mvm->queue_info_lock); - mvm->queue_info[txq_id].setup_reserved = false; - spin_unlock_bh(&mvm->queue_info_lock); - - if (old_state >= IWL_AGG_ON) { - iwl_mvm_drain_sta(mvm, mvmsta, true); - if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0)) - IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); - iwl_trans_wait_tx_queue_empty(mvm->trans, - mvmsta->tfd_queue_msk); - iwl_mvm_drain_sta(mvm, mvmsta, false); - - iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); - - iwl_mvm_disable_txq(mvm, tid_data->txq_id, - vif->hw_queue[tid_to_mac80211_ac[tid]], tid, - 0); - } - - return 0; -} - -static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm) -{ - int i, max = -1, max_offs = -1; - - lockdep_assert_held(&mvm->mutex); - - /* Pick the unused key offset with the highest 'deleted' - * counter. Every time a key is deleted, all the counters - * are incremented and the one that was just deleted is - * reset to zero. Thus, the highest counter is the one - * that was deleted longest ago. Pick that one. - */ - for (i = 0; i < STA_KEY_MAX_NUM; i++) { - if (test_bit(i, mvm->fw_key_table)) - continue; - if (mvm->fw_key_deleted[i] > max) { - max = mvm->fw_key_deleted[i]; - max_offs = i; - } - } - - if (max_offs < 0) - return STA_KEY_IDX_INVALID; - - __set_bit(max_offs, mvm->fw_key_table); - - return max_offs; -} - -static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (sta) { - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - - return mvm_sta->sta_id; - } - - /* - * The device expects GTKs for station interfaces to be - * installed as GTKs for the AP station. If we have no - * station ID, then use AP's station ID. - */ - if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) - return mvmvif->ap_sta_id; - - return IWL_MVM_STATION_COUNT; -} - -static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, - struct iwl_mvm_sta *mvm_sta, - struct ieee80211_key_conf *keyconf, bool mcast, - u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags) -{ - struct iwl_mvm_add_sta_key_cmd cmd = {}; - __le16 key_flags; - int ret; - u32 status; - u16 keyidx; - int i; - u8 sta_id = mvm_sta->sta_id; - - keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & - STA_KEY_FLG_KEYID_MSK; - key_flags = cpu_to_le16(keyidx); - key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_KEY_MAP); - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP); - cmd.tkip_rx_tsc_byte2 = tkip_iv32; - for (i = 0; i < 5; i++) - cmd.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); - memcpy(cmd.key, keyconf->key, keyconf->keylen); - break; - case WLAN_CIPHER_SUITE_CCMP: - key_flags |= cpu_to_le16(STA_KEY_FLG_CCM); - memcpy(cmd.key, keyconf->key, keyconf->keylen); - break; - case WLAN_CIPHER_SUITE_WEP104: - key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES); - /* fall through */ - case WLAN_CIPHER_SUITE_WEP40: - key_flags |= cpu_to_le16(STA_KEY_FLG_WEP); - memcpy(cmd.key + 3, keyconf->key, keyconf->keylen); - break; - default: - key_flags |= cpu_to_le16(STA_KEY_FLG_EXT); - memcpy(cmd.key, keyconf->key, keyconf->keylen); - } - - if (mcast) - key_flags |= cpu_to_le16(STA_KEY_MULTICAST); - - cmd.key_offset = keyconf->hw_key_idx; - cmd.key_flags = key_flags; - cmd.sta_id = sta_id; - - status = ADD_STA_SUCCESS; - if (cmd_flags & CMD_ASYNC) - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC, - sizeof(cmd), &cmd); - else - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), - &cmd, &status); - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_WEP(mvm, "MODIFY_STA: set dynamic key passed\n"); - break; - default: - ret = -EIO; - IWL_ERR(mvm, "MODIFY_STA: set dynamic key failed\n"); - break; - } - - return ret; -} - -static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm, - struct ieee80211_key_conf *keyconf, - u8 sta_id, bool remove_key) -{ - struct iwl_mvm_mgmt_mcast_key_cmd igtk_cmd = {}; - - /* verify the key details match the required command's expectations */ - if (WARN_ON((keyconf->cipher != WLAN_CIPHER_SUITE_AES_CMAC) || - (keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE) || - (keyconf->keyidx != 4 && keyconf->keyidx != 5))) - return -EINVAL; - - igtk_cmd.key_id = cpu_to_le32(keyconf->keyidx); - igtk_cmd.sta_id = cpu_to_le32(sta_id); - - if (remove_key) { - igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_NOT_VALID); - } else { - struct ieee80211_key_seq seq; - const u8 *pn; - - memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen); - ieee80211_get_key_rx_seq(keyconf, 0, &seq); - pn = seq.aes_cmac.pn; - igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) | - ((u64) pn[4] << 8) | - ((u64) pn[3] << 16) | - ((u64) pn[2] << 24) | - ((u64) pn[1] << 32) | - ((u64) pn[0] << 40)); - } - - IWL_DEBUG_INFO(mvm, "%s igtk for sta %u\n", - remove_key ? "removing" : "installing", - igtk_cmd.sta_id); - - return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0, - sizeof(igtk_cmd), &igtk_cmd); -} - - -static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (sta) - return sta->addr; - - if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { - u8 sta_id = mvmvif->ap_sta_id; - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - return sta->addr; - } - - - return NULL; -} - -static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *keyconf, - bool mcast) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - int ret; - const u8 *addr; - struct ieee80211_key_seq seq; - u16 p1k[5]; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - addr = iwl_mvm_get_mac_addr(mvm, vif, sta); - /* get phase 1 key from mac80211 */ - ieee80211_get_key_rx_seq(keyconf, 0, &seq); - ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); - ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, - seq.tkip.iv32, p1k, 0); - break; - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, - 0, NULL, 0); - break; - default: - ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, - 0, NULL, 0); - } - - return ret; -} - -static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, - struct ieee80211_key_conf *keyconf, - bool mcast) -{ - struct iwl_mvm_add_sta_key_cmd cmd = {}; - __le16 key_flags; - int ret; - u32 status; - - key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & - STA_KEY_FLG_KEYID_MSK); - key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP); - key_flags |= cpu_to_le16(STA_KEY_NOT_VALID); - - if (mcast) - key_flags |= cpu_to_le16(STA_KEY_MULTICAST); - - cmd.key_flags = key_flags; - cmd.key_offset = keyconf->hw_key_idx; - cmd.sta_id = sta_id; - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), - &cmd, &status); - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n"); - break; - default: - ret = -EIO; - IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n"); - break; - } - - return ret; -} - -int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *keyconf, - bool have_key_offset) -{ - bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); - u8 sta_id; - int ret; - static const u8 __maybe_unused zero_addr[ETH_ALEN] = {0}; - - lockdep_assert_held(&mvm->mutex); - - /* Get the station id from the mvm local station table */ - sta_id = iwl_mvm_get_key_sta_id(vif, sta); - if (sta_id == IWL_MVM_STATION_COUNT) { - IWL_ERR(mvm, "Failed to find station id\n"); - return -EINVAL; - } - - if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { - ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false); - goto end; - } - - /* - * It is possible that the 'sta' parameter is NULL, and thus - * there is a need to retrieve the sta from the local station table. - */ - if (!sta) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta)) { - IWL_ERR(mvm, "Invalid station id\n"); - return -EINVAL; - } - } - - if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif)) - return -EINVAL; - - if (!have_key_offset) { - /* - * The D3 firmware hardcodes the PTK offset to 0, so we have to - * configure it there. As a result, this workaround exists to - * let the caller set the key offset (hw_key_idx), see d3.c. - */ - keyconf->hw_key_idx = iwl_mvm_set_fw_key_idx(mvm); - if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID) - return -ENOSPC; - } - - ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, mcast); - if (ret) { - __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table); - goto end; - } - - /* - * For WEP, the same key is used for multicast and unicast. Upload it - * again, using the same key offset, and now pointing the other one - * to the same key slot (offset). - * If this fails, remove the original as well. - */ - if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || - keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) { - ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, !mcast); - if (ret) { - __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table); - __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast); - } - } - -end: - IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", - keyconf->cipher, keyconf->keylen, keyconf->keyidx, - sta ? sta->addr : zero_addr, ret); - return ret; -} - -int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *keyconf) -{ - bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); - u8 sta_id; - int ret, i; - - lockdep_assert_held(&mvm->mutex); - - /* Get the station id from the mvm local station table */ - sta_id = iwl_mvm_get_key_sta_id(vif, sta); - - IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n", - keyconf->keyidx, sta_id); - - if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) - return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true); - - if (!__test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table)) { - IWL_ERR(mvm, "offset %d not used in fw key table.\n", - keyconf->hw_key_idx); - return -ENOENT; - } - - /* track which key was deleted last */ - for (i = 0; i < STA_KEY_MAX_NUM; i++) { - if (mvm->fw_key_deleted[i] < U8_MAX) - mvm->fw_key_deleted[i]++; - } - mvm->fw_key_deleted[keyconf->hw_key_idx] = 0; - - if (sta_id == IWL_MVM_STATION_COUNT) { - IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n"); - return 0; - } - - /* - * It is possible that the 'sta' parameter is NULL, and thus - * there is a need to retrieve the sta from the local station table, - * for example when a GTK is removed (where the sta_id will then be - * the AP ID, and no station was passed by mac80211.) - */ - if (!sta) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - if (!sta) { - IWL_ERR(mvm, "Invalid station id\n"); - return -EINVAL; - } - } - - if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif)) - return -EINVAL; - - ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast); - if (ret) - return ret; - - /* delete WEP key twice to get rid of (now useless) offset */ - if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || - keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) - ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, !mcast); - - return ret; -} - -void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, - u16 *phase1key) -{ - struct iwl_mvm_sta *mvm_sta; - u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta); - bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); - - if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT)) - return; - - rcu_read_lock(); - - if (!sta) { - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (WARN_ON(IS_ERR_OR_NULL(sta))) { - rcu_read_unlock(); - return; - } - } - - mvm_sta = iwl_mvm_sta_from_mac80211(sta); - iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, - iv32, phase1key, CMD_ASYNC); - rcu_read_unlock(); -} - -void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, - struct ieee80211_sta *sta) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_add_sta_cmd cmd = { - .add_modify = STA_MODE_MODIFY, - .sta_id = mvmsta->sta_id, - .station_flags_msk = cpu_to_le32(STA_FLG_PS), - .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), - }; - int ret; - - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); - if (ret) - IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); -} - -void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - enum ieee80211_frame_release_type reason, - u16 cnt, u16 tids, bool more_data, - bool agg) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_add_sta_cmd cmd = { - .add_modify = STA_MODE_MODIFY, - .sta_id = mvmsta->sta_id, - .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, - .sleep_tx_count = cpu_to_le16(cnt), - .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), - }; - int tid, ret; - unsigned long _tids = tids; - - /* convert TIDs to ACs - we don't support TSPEC so that's OK - * Note that this field is reserved and unused by firmware not - * supporting GO uAPSD, so it's safe to always do this. - */ - for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) - cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]); - - /* If we're releasing frames from aggregation queues then check if the - * all queues combined that we're releasing frames from have - * - more frames than the service period, in which case more_data - * needs to be set - * - fewer than 'cnt' frames, in which case we need to adjust the - * firmware command (but do that unconditionally) - */ - if (agg) { - int remaining = cnt; - - spin_lock_bh(&mvmsta->lock); - for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) { - struct iwl_mvm_tid_data *tid_data; - u16 n_queued; - - tid_data = &mvmsta->tid_data[tid]; - if (WARN(tid_data->state != IWL_AGG_ON && - tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA, - "TID %d state is %d\n", - tid, tid_data->state)) { - spin_unlock_bh(&mvmsta->lock); - ieee80211_sta_eosp(sta); - return; - } - - n_queued = iwl_mvm_tid_queued(tid_data); - if (n_queued > remaining) { - more_data = true; - remaining = 0; - break; - } - remaining -= n_queued; - } - spin_unlock_bh(&mvmsta->lock); - - cmd.sleep_tx_count = cpu_to_le16(cnt - remaining); - if (WARN_ON(cnt - remaining == 0)) { - ieee80211_sta_eosp(sta); - return; - } - } - - /* Note: this is ignored by firmware not supporting GO uAPSD */ - if (more_data) - cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA); - - if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) { - mvmsta->next_status_eosp = true; - cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL); - } else { - cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD); - } - - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); - if (ret) - IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); -} - -void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm_eosp_notification *notif = (void *)pkt->data; - struct ieee80211_sta *sta; - u32 sta_id = le32_to_cpu(notif->sta_id); - - if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT)) - return; - - rcu_read_lock(); - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (!IS_ERR_OR_NULL(sta)) - ieee80211_sta_eosp(sta); - rcu_read_unlock(); -} - -void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, - struct iwl_mvm_sta *mvmsta, bool disable) -{ - struct iwl_mvm_add_sta_cmd cmd = { - .add_modify = STA_MODE_MODIFY, - .sta_id = mvmsta->sta_id, - .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0, - .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX), - .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), - }; - int ret; - - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); - if (ret) - IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); -} - -void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - bool disable) -{ - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - - spin_lock_bh(&mvm_sta->lock); - - if (mvm_sta->disable_tx == disable) { - spin_unlock_bh(&mvm_sta->lock); - return; - } - - mvm_sta->disable_tx = disable; - - /* - * Tell mac80211 to start/stop queuing tx for this station, - * but don't stop queuing if there are still pending frames - * for this station. - */ - if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) - ieee80211_sta_block_awake(mvm->hw, sta, disable); - - iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable); - - spin_unlock_bh(&mvm_sta->lock); -} - -void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - bool disable) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvm_sta; - int i; - - lockdep_assert_held(&mvm->mutex); - - /* Block/unblock all the stations of the given mvmvif */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta)) - continue; - - mvm_sta = iwl_mvm_sta_from_mac80211(sta); - if (mvm_sta->mac_id_n_color != - FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)) - continue; - - iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable); - } -} - -void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvmsta; - - rcu_read_lock(); - - mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id); - - if (!WARN_ON(!mvmsta)) - iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true); - - rcu_read_unlock(); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h deleted file mode 100644 index eedb215eba3f..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ /dev/null @@ -1,426 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __sta_h__ -#define __sta_h__ - -#include -#include -#include - -#include "iwl-trans.h" /* for IWL_MAX_TID_COUNT */ -#include "fw-api.h" /* IWL_MVM_STATION_COUNT */ -#include "rs.h" - -struct iwl_mvm; -struct iwl_mvm_vif; - -/** - * DOC: station table - introduction - * - * The station table is a list of data structure that reprensent the stations. - * In STA/P2P client mode, the driver will hold one station for the AP/ GO. - * In GO/AP mode, the driver will have as many stations as associated clients. - * All these stations are reflected in the fw's station table. The driver - * keeps the fw's station table up to date with the ADD_STA command. Stations - * can be removed by the REMOVE_STA command. - * - * All the data related to a station is held in the structure %iwl_mvm_sta - * which is embed in the mac80211's %ieee80211_sta (in the drv_priv) area. - * This data includes the index of the station in the fw, per tid information - * (sequence numbers, Block-ack state machine, etc...). The stations are - * created and deleted by the %sta_state callback from %ieee80211_ops. - * - * The driver holds a map: %fw_id_to_mac_id that allows to fetch a - * %ieee80211_sta (and the %iwl_mvm_sta embedded into it) based on a fw - * station index. That way, the driver is able to get the tid related data in - * O(1) in time sensitive paths (Tx / Tx response / BA notification). These - * paths are triggered by the fw, and the driver needs to get a pointer to the - * %ieee80211 structure. This map helps to get that pointer quickly. - */ - -/** - * DOC: station table - locking - * - * As stated before, the station is created / deleted by mac80211's %sta_state - * callback from %ieee80211_ops which can sleep. The next paragraph explains - * the locking of a single stations, the next ones relates to the station - * table. - * - * The station holds the sequence number per tid. So this data needs to be - * accessed in the Tx path (which is softIRQ). It also holds the Block-Ack - * information (the state machine / and the logic that checks if the queues - * were drained), so it also needs to be accessible from the Tx response flow. - * In short, the station needs to be access from sleepable context as well as - * from tasklets, so the station itself needs a spinlock. - * - * The writers of %fw_id_to_mac_id map are serialized by the global mutex of - * the mvm op_mode. This is possible since %sta_state can sleep. - * The pointers in this map are RCU protected, hence we won't replace the - * station while we have Tx / Tx response / BA notification running. - * - * If a station is deleted while it still has packets in its A-MPDU queues, - * then the reclaim flow will notice that there is no station in the map for - * sta_id and it will dump the responses. - */ - -/** - * DOC: station table - internal stations - * - * The FW needs a few internal stations that are not reflected in - * mac80211, such as broadcast station in AP / GO mode, or AUX sta for - * scanning and P2P device (during the GO negotiation). - * For these kind of stations we have %iwl_mvm_int_sta struct which holds the - * data relevant for them from both %iwl_mvm_sta and %ieee80211_sta. - * Usually the data for these stations is static, so no locking is required, - * and no TID data as this is also not needed. - * One thing to note, is that these stations have an ID in the fw, but not - * in mac80211. In order to "reserve" them a sta_id in %fw_id_to_mac_id - * we fill ERR_PTR(EINVAL) in this mapping and all other dereferencing of - * pointers from this mapping need to check that the value is not error - * or NULL. - * - * Currently there is only one auxiliary station for scanning, initialized - * on init. - */ - -/** - * DOC: station table - AP Station in STA mode - * - * %iwl_mvm_vif includes the index of the AP station in the fw's STA table: - * %ap_sta_id. To get the point to the corresponding %ieee80211_sta, - * &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove - * the AP station from the fw before setting the MAC context as unassociated. - * Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is - * removed by mac80211, but the station won't be removed in the fw until the - * VIF is set as unassociated. Then, %ap_sta_id will be invalidated. - */ - -/** - * DOC: station table - Drain vs. Flush - * - * Flush means that all the frames in the SCD queue are dumped regardless the - * station to which they were sent. We do that when we disassociate and before - * we remove the STA of the AP. The flush can be done synchronously against the - * fw. - * Drain means that the fw will drop all the frames sent to a specific station. - * This is useful when a client (if we are IBSS / GO or AP) disassociates. In - * that case, we need to drain all the frames for that client from the AC queues - * that are shared with the other clients. Only then, we can remove the STA in - * the fw. In order to do so, we track the non-AMPDU packets for each station. - * If mac80211 removes a STA and if it still has non-AMPDU packets pending in - * the queues, we mark this station as %EBUSY in %fw_id_to_mac_id, and drop all - * the frames for this STA (%iwl_mvm_rm_sta). When the last frame is dropped - * (we know about it with its Tx response), we remove the station in fw and set - * it as %NULL in %fw_id_to_mac_id: this is the purpose of - * %iwl_mvm_sta_drained_wk. - */ - -/** - * DOC: station table - fw restart - * - * When the fw asserts, or we have any other issue that requires to reset the - * driver, we require mac80211 to reconfigure the driver. Since the private - * data of the stations is embed in mac80211's %ieee80211_sta, that data will - * not be zeroed and needs to be reinitialized manually. - * %IWL_MVM_STATUS_IN_HW_RESTART is set during restart and that will hint us - * that we must not allocate a new sta_id but reuse the previous one. This - * means that the stations being re-added after the reset will have the same - * place in the fw as before the reset. We do need to zero the %fw_id_to_mac_id - * map, since the stations aren't in the fw any more. Internal stations that - * are not added by mac80211 will be re-added in the init flow that is called - * after the restart: mac80211 call's %iwl_mvm_mac_start which calls to - * %iwl_mvm_up. - */ - -/** - * DOC: AP mode - PS - * - * When a station is asleep, the fw will set it as "asleep". All frames on - * shared queues (i.e. non-aggregation queues) to that station will be dropped - * by the fw (%TX_STATUS_FAIL_DEST_PS failure code). - * - * AMPDUs are in a separate queue that is stopped by the fw. We just need to - * let mac80211 know when there are frames in these queues so that it can - * properly handle trigger frames. - * - * When a trigger frame is received, mac80211 tells the driver to send frames - * from the AMPDU queues or sends frames to non-aggregation queues itself, - * depending on which ACs are delivery-enabled and what TID has frames to - * transmit. Note that mac80211 has all the knowledge since all the non-agg - * frames are buffered / filtered, and the driver tells mac80211 about agg - * frames). The driver needs to tell the fw to let frames out even if the - * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count. - * - * When we receive a frame from that station with PM bit unset, the driver - * needs to let the fw know that this station isn't asleep any more. This is - * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signaling the - * station's wakeup. - * - * For a GO, the Service Period might be cut short due to an absence period - * of the GO. In this (and all other cases) the firmware notifies us with the - * EOSP_NOTIFICATION, and we notify mac80211 of that. Further frames that we - * already sent to the device will be rejected again. - * - * See also "AP support for powersaving clients" in mac80211.h. - */ - -/** - * enum iwl_mvm_agg_state - * - * The state machine of the BA agreement establishment / tear down. - * These states relate to a specific RA / TID. - * - * @IWL_AGG_OFF: aggregation is not used - * @IWL_AGG_STARTING: aggregation are starting (between start and oper) - * @IWL_AGG_ON: aggregation session is up - * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the - * HW queue to be empty from packets for this RA /TID. - * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the - * HW queue to be empty from packets for this RA /TID. - */ -enum iwl_mvm_agg_state { - IWL_AGG_OFF = 0, - IWL_AGG_STARTING, - IWL_AGG_ON, - IWL_EMPTYING_HW_QUEUE_ADDBA, - IWL_EMPTYING_HW_QUEUE_DELBA, -}; - -/** - * struct iwl_mvm_tid_data - holds the states for each RA / TID - * @seq_number: the next WiFi sequence number to use - * @next_reclaimed: the WiFi sequence number of the next packet to be acked. - * This is basically (last acked packet++). - * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the - * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). - * @reduced_tpc: Reduced tx power. Holds the data between the - * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). - * @state: state of the BA agreement establishment / tear down. - * @txq_id: Tx queue used by the BA session - * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or - * the first packet to be sent in legacy HW queue in Tx AGG stop flow. - * Basically when next_reclaimed reaches ssn, we can tell mac80211 that - * we are ready to finish the Tx AGG stop / start flow. - * @tx_time: medium time consumed by this A-MPDU - */ -struct iwl_mvm_tid_data { - u16 seq_number; - u16 next_reclaimed; - /* The rest is Tx AGG related */ - u32 rate_n_flags; - u8 reduced_tpc; - enum iwl_mvm_agg_state state; - u16 txq_id; - u16 ssn; - u16 tx_time; -}; - -static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) -{ - return ieee80211_sn_sub(IEEE80211_SEQ_TO_SN(tid_data->seq_number), - tid_data->next_reclaimed); -} - -/** - * struct iwl_mvm_sta - representation of a station in the driver - * @sta_id: the index of the station in the fw (will be replaced by id_n_color) - * @tfd_queue_msk: the tfd queues used by the station - * @hw_queue: per-AC mapping of the TFD queues used by station - * @mac_id_n_color: the MAC context this station is linked to - * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for - * tid. - * @max_agg_bufsize: the maximal size of the AGG buffer for this station - * @bt_reduced_txpower: is reduced tx power enabled for this station - * @next_status_eosp: the next reclaimed packet is a PS-Poll response and - * we need to signal the EOSP - * @lock: lock to protect the whole struct. Since %tid_data is access from Tx - * and from Tx response flow, it needs a spinlock. - * @tid_data: per tid data. Look at %iwl_mvm_tid_data. - * @tx_protection: reference counter for controlling the Tx protection. - * @tt_tx_protection: is thermal throttling enable Tx protection? - * @disable_tx: is tx to this STA disabled? - * @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON) - * - * When mac80211 creates a station it reserves some space (hw->sta_data_size) - * in the structure for use by driver. This structure is placed in that - * space. - * - */ -struct iwl_mvm_sta { - u32 sta_id; - u32 tfd_queue_msk; - u8 hw_queue[IEEE80211_NUM_ACS]; - u32 mac_id_n_color; - u16 tid_disable_agg; - u8 max_agg_bufsize; - bool bt_reduced_txpower; - bool next_status_eosp; - spinlock_t lock; - struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT]; - struct iwl_lq_sta lq_sta; - struct ieee80211_vif *vif; - - /* Temporary, until the new TLC will control the Tx protection */ - s8 tx_protection; - bool tt_tx_protection; - - bool disable_tx; - u8 agg_tids; -}; - -static inline struct iwl_mvm_sta * -iwl_mvm_sta_from_mac80211(struct ieee80211_sta *sta) -{ - return (void *)sta->drv_priv; -} - -/** - * struct iwl_mvm_int_sta - representation of an internal station (auxiliary or - * broadcast) - * @sta_id: the index of the station in the fw (will be replaced by id_n_color) - * @tfd_queue_msk: the tfd queues used by the station - */ -struct iwl_mvm_int_sta { - u32 sta_id; - u32 tfd_queue_msk; -}; - -int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - bool update); -int iwl_mvm_add_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -int iwl_mvm_update_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -int iwl_mvm_rm_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u8 sta_id); -int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key, - bool have_key_offset); -int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *keyconf); - -void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, - u16 *phase1key); - -void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/* AMPDU */ -int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, u16 ssn, bool start); -int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 *ssn); -int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u8 buf_size); -int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid); -int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid); - -int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm); -void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm); - -int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); - -void iwl_mvm_sta_drained_wk(struct work_struct *wk); -void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, - struct ieee80211_sta *sta); -void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - enum ieee80211_frame_release_type reason, - u16 cnt, u16 tids, bool more_data, - bool agg); -int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, - bool drain); -void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, - struct iwl_mvm_sta *mvmsta, bool disable); -void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - bool disable); -void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - bool disable); -void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif); - -#endif /* __sta_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/tdls.c b/drivers/net/wireless/iwlwifi/mvm/tdls.c deleted file mode 100644 index fe2fa5650443..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/tdls.c +++ /dev/null @@ -1,732 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include "mvm.h" -#include "time-event.h" -#include "iwl-io.h" -#include "iwl-prph.h" - -#define TU_TO_US(x) (x * 1024) -#define TU_TO_MS(x) (TU_TO_US(x) / 1000) - -void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - int i; - - lockdep_assert_held(&mvm->mutex); - - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (!sta || IS_ERR(sta) || !sta->tdls) - continue; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - ieee80211_tdls_oper_request(mvmsta->vif, sta->addr, - NL80211_TDLS_TEARDOWN, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, - GFP_KERNEL); - } -} - -int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - int count = 0; - int i; - - lockdep_assert_held(&mvm->mutex); - - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (!sta || IS_ERR(sta) || !sta->tdls) - continue; - - if (vif) { - mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (mvmsta->vif != vif) - continue; - } - - count++; - } - - return count; -} - -static void iwl_mvm_tdls_config(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_rx_packet *pkt; - struct iwl_tdls_config_res *resp; - struct iwl_tdls_config_cmd tdls_cfg_cmd = {}; - struct iwl_host_cmd cmd = { - .id = TDLS_CONFIG_CMD, - .flags = CMD_WANT_SKB, - .data = { &tdls_cfg_cmd, }, - .len = { sizeof(struct iwl_tdls_config_cmd), }, - }; - struct ieee80211_sta *sta; - int ret, i, cnt; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - lockdep_assert_held(&mvm->mutex); - - tdls_cfg_cmd.id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); - tdls_cfg_cmd.tx_to_ap_tid = IWL_MVM_TDLS_FW_TID; - tdls_cfg_cmd.tx_to_ap_ssn = cpu_to_le16(0); /* not used for now */ - - /* for now the Tx cmd is empty and unused */ - - /* populate TDLS peer data */ - cnt = 0; - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta) || !sta->tdls) - continue; - - tdls_cfg_cmd.sta_info[cnt].sta_id = i; - tdls_cfg_cmd.sta_info[cnt].tx_to_peer_tid = - IWL_MVM_TDLS_FW_TID; - tdls_cfg_cmd.sta_info[cnt].tx_to_peer_ssn = cpu_to_le16(0); - tdls_cfg_cmd.sta_info[cnt].is_initiator = - cpu_to_le32(sta->tdls_initiator ? 1 : 0); - - cnt++; - } - - tdls_cfg_cmd.tdls_peer_count = cnt; - IWL_DEBUG_TDLS(mvm, "send TDLS config to FW for %d peers\n", cnt); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (WARN_ON_ONCE(ret)) - return; - - pkt = cmd.resp_pkt; - - WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) != sizeof(*resp)); - - /* we don't really care about the response at this point */ - - iwl_free_resp(&cmd); -} - -void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool sta_added) -{ - int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif); - - /* when the first peer joins, send a power update first */ - if (tdls_sta_cnt == 1 && sta_added) - iwl_mvm_power_update_mac(mvm); - - /* configure the FW with TDLS peer info */ - iwl_mvm_tdls_config(mvm, vif); - - /* when the last peer leaves, send a power update last */ - if (tdls_sta_cnt == 0 && !sta_added) - iwl_mvm_power_update_mac(mvm); -} - -void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int; - - /* - * iwl_mvm_protect_session() reads directly from the device - * (the system time), so make sure it is available. - */ - if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_TDLS)) - return; - - mutex_lock(&mvm->mutex); - /* Protect the session to hear the TDLS setup response on the channel */ - iwl_mvm_protect_session(mvm, vif, duration, duration, 100, true); - mutex_unlock(&mvm->mutex); - - iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS); -} - -static const char * -iwl_mvm_tdls_cs_state_str(enum iwl_mvm_tdls_cs_state state) -{ - switch (state) { - case IWL_MVM_TDLS_SW_IDLE: - return "IDLE"; - case IWL_MVM_TDLS_SW_REQ_SENT: - return "REQ SENT"; - case IWL_MVM_TDLS_SW_RESP_RCVD: - return "RESP RECEIVED"; - case IWL_MVM_TDLS_SW_REQ_RCVD: - return "REQ RECEIVED"; - case IWL_MVM_TDLS_SW_ACTIVE: - return "ACTIVE"; - } - - return NULL; -} - -static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm, - enum iwl_mvm_tdls_cs_state state) -{ - if (mvm->tdls_cs.state == state) - return; - - IWL_DEBUG_TDLS(mvm, "TDLS channel switch state: %s -> %s\n", - iwl_mvm_tdls_cs_state_str(mvm->tdls_cs.state), - iwl_mvm_tdls_cs_state_str(state)); - mvm->tdls_cs.state = state; - - /* we only send requests to our switching peer - update sent time */ - if (state == IWL_MVM_TDLS_SW_REQ_SENT) - mvm->tdls_cs.peer.sent_timestamp = - iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG); - - if (state == IWL_MVM_TDLS_SW_IDLE) - mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT; -} - -void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_tdls_channel_switch_notif *notif = (void *)pkt->data; - struct ieee80211_sta *sta; - unsigned int delay; - struct iwl_mvm_sta *mvmsta; - struct ieee80211_vif *vif; - u32 sta_id = le32_to_cpu(notif->sta_id); - - lockdep_assert_held(&mvm->mutex); - - /* can fail sometimes */ - if (!le32_to_cpu(notif->status)) { - iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); - return; - } - - if (WARN_ON(sta_id >= IWL_MVM_STATION_COUNT)) - return; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - /* the station may not be here, but if it is, it must be a TDLS peer */ - if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls)) - return; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - vif = mvmsta->vif; - - /* - * Update state and possibly switch again after this is over (DTIM). - * Also convert TU to msec. - */ - delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int); - mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, - msecs_to_jiffies(delay)); - - iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE); -} - -static int -iwl_mvm_tdls_check_action(struct iwl_mvm *mvm, - enum iwl_tdls_channel_switch_type type, - const u8 *peer, bool peer_initiator, u32 timestamp) -{ - bool same_peer = false; - int ret = 0; - - /* get the existing peer if it's there */ - if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE && - mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) { - struct ieee80211_sta *sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id], - lockdep_is_held(&mvm->mutex)); - if (!IS_ERR_OR_NULL(sta)) - same_peer = ether_addr_equal(peer, sta->addr); - } - - switch (mvm->tdls_cs.state) { - case IWL_MVM_TDLS_SW_IDLE: - /* - * might be spurious packet from the peer after the switch is - * already done - */ - if (type == TDLS_MOVE_CH) - ret = -EINVAL; - break; - case IWL_MVM_TDLS_SW_REQ_SENT: - /* only allow requests from the same peer */ - if (!same_peer) - ret = -EBUSY; - else if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH && - !peer_initiator) - /* - * We received a ch-switch request while an outgoing - * one is pending. Allow it if the peer is the link - * initiator. - */ - ret = -EBUSY; - else if (type == TDLS_SEND_CHAN_SW_REQ) - /* wait for idle before sending another request */ - ret = -EBUSY; - else if (timestamp <= mvm->tdls_cs.peer.sent_timestamp) - /* we got a stale response - ignore it */ - ret = -EINVAL; - break; - case IWL_MVM_TDLS_SW_RESP_RCVD: - /* - * we are waiting for the FW to give an "active" notification, - * so ignore requests in the meantime - */ - ret = -EBUSY; - break; - case IWL_MVM_TDLS_SW_REQ_RCVD: - /* as above, allow the link initiator to proceed */ - if (type == TDLS_SEND_CHAN_SW_REQ) { - if (!same_peer) - ret = -EBUSY; - else if (peer_initiator) /* they are the initiator */ - ret = -EBUSY; - } else if (type == TDLS_MOVE_CH) { - ret = -EINVAL; - } - break; - case IWL_MVM_TDLS_SW_ACTIVE: - /* - * the only valid request when active is a request to return - * to the base channel by the current off-channel peer - */ - if (type != TDLS_MOVE_CH || !same_peer) - ret = -EBUSY; - break; - } - - if (ret) - IWL_DEBUG_TDLS(mvm, - "Invalid TDLS action %d state %d peer %pM same_peer %d initiator %d\n", - type, mvm->tdls_cs.state, peer, same_peer, - peer_initiator); - - return ret; -} - -static int -iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - enum iwl_tdls_channel_switch_type type, - const u8 *peer, bool peer_initiator, - u8 oper_class, - struct cfg80211_chan_def *chandef, - u32 timestamp, u16 switch_time, - u16 switch_timeout, struct sk_buff *skb, - u32 ch_sw_tm_ie) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - struct ieee80211_tx_info *info; - struct ieee80211_hdr *hdr; - struct iwl_tdls_channel_switch_cmd cmd = {0}; - int ret; - - lockdep_assert_held(&mvm->mutex); - - ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator, - timestamp); - if (ret) - return ret; - - if (!skb || WARN_ON(skb->len > IWL_TDLS_CH_SW_FRAME_MAX_SIZE)) { - ret = -EINVAL; - goto out; - } - - cmd.switch_type = type; - cmd.timing.frame_timestamp = cpu_to_le32(timestamp); - cmd.timing.switch_time = cpu_to_le32(switch_time); - cmd.timing.switch_timeout = cpu_to_le32(switch_timeout); - - rcu_read_lock(); - sta = ieee80211_find_sta(vif, peer); - if (!sta) { - rcu_read_unlock(); - ret = -ENOENT; - goto out; - } - mvmsta = iwl_mvm_sta_from_mac80211(sta); - cmd.peer_sta_id = cpu_to_le32(mvmsta->sta_id); - - if (!chandef) { - if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT && - mvm->tdls_cs.peer.chandef.chan) { - /* actually moving to the channel */ - chandef = &mvm->tdls_cs.peer.chandef; - } else if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_ACTIVE && - type == TDLS_MOVE_CH) { - /* we need to return to base channel */ - struct ieee80211_chanctx_conf *chanctx = - rcu_dereference(vif->chanctx_conf); - - if (WARN_ON_ONCE(!chanctx)) { - rcu_read_unlock(); - goto out; - } - - chandef = &chanctx->def; - } - } - - if (chandef) { - cmd.ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ? - PHY_BAND_24 : PHY_BAND_5); - cmd.ci.channel = chandef->chan->hw_value; - cmd.ci.width = iwl_mvm_get_channel_width(chandef); - cmd.ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef); - } - - /* keep quota calculation simple for now - 50% of DTIM for TDLS */ - cmd.timing.max_offchan_duration = - cpu_to_le32(TU_TO_US(vif->bss_conf.dtim_period * - vif->bss_conf.beacon_int) / 2); - - /* Switch time is the first element in the switch-timing IE. */ - cmd.frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2); - - info = IEEE80211_SKB_CB(skb); - hdr = (void *)skb->data; - if (info->control.hw_key) { - if (info->control.hw_key->cipher != WLAN_CIPHER_SUITE_CCMP) { - rcu_read_unlock(); - ret = -EINVAL; - goto out; - } - iwl_mvm_set_tx_cmd_ccmp(info, &cmd.frame.tx_cmd); - } - - iwl_mvm_set_tx_cmd(mvm, skb, &cmd.frame.tx_cmd, info, - mvmsta->sta_id); - - iwl_mvm_set_tx_cmd_rate(mvm, &cmd.frame.tx_cmd, info, sta, - hdr->frame_control); - rcu_read_unlock(); - - memcpy(cmd.frame.data, skb->data, skb->len); - - ret = iwl_mvm_send_cmd_pdu(mvm, TDLS_CHANNEL_SWITCH_CMD, 0, - sizeof(cmd), &cmd); - if (ret) { - IWL_ERR(mvm, "Failed to send TDLS_CHANNEL_SWITCH cmd: %d\n", - ret); - goto out; - } - - /* channel switch has started, update state */ - if (type != TDLS_MOVE_CH) { - mvm->tdls_cs.cur_sta_id = mvmsta->sta_id; - iwl_mvm_tdls_update_cs_state(mvm, - type == TDLS_SEND_CHAN_SW_REQ ? - IWL_MVM_TDLS_SW_REQ_SENT : - IWL_MVM_TDLS_SW_REQ_RCVD); - } else { - iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_RESP_RCVD); - } - -out: - - /* channel switch failed - we are idle */ - if (ret) - iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); - - return ret; -} - -void iwl_mvm_tdls_ch_switch_work(struct work_struct *work) -{ - struct iwl_mvm *mvm; - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - struct ieee80211_vif *vif; - unsigned int delay; - int ret; - - mvm = container_of(work, struct iwl_mvm, tdls_cs.dwork.work); - mutex_lock(&mvm->mutex); - - /* called after an active channel switch has finished or timed-out */ - iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); - - /* station might be gone, in that case do nothing */ - if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) - goto out; - - sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id], - lockdep_is_held(&mvm->mutex)); - /* the station may not be here, but if it is, it must be a TDLS peer */ - if (!sta || IS_ERR(sta) || WARN_ON(!sta->tdls)) - goto out; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - vif = mvmsta->vif; - ret = iwl_mvm_tdls_config_channel_switch(mvm, vif, - TDLS_SEND_CHAN_SW_REQ, - sta->addr, - mvm->tdls_cs.peer.initiator, - mvm->tdls_cs.peer.op_class, - &mvm->tdls_cs.peer.chandef, - 0, 0, 0, - mvm->tdls_cs.peer.skb, - mvm->tdls_cs.peer.ch_sw_tm_ie); - if (ret) - IWL_ERR(mvm, "Not sending TDLS channel switch: %d\n", ret); - - /* retry after a DTIM if we failed sending now */ - delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int); - queue_delayed_work(system_wq, &mvm->tdls_cs.dwork, - msecs_to_jiffies(delay)); -out: - mutex_unlock(&mvm->mutex); -} - -int -iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u8 oper_class, - struct cfg80211_chan_def *chandef, - struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvmsta; - unsigned int delay; - int ret; - - mutex_lock(&mvm->mutex); - - IWL_DEBUG_TDLS(mvm, "TDLS channel switch with %pM ch %d width %d\n", - sta->addr, chandef->chan->center_freq, chandef->width); - - /* we only support a single peer for channel switching */ - if (mvm->tdls_cs.peer.sta_id != IWL_MVM_STATION_COUNT) { - IWL_DEBUG_TDLS(mvm, - "Existing peer. Can't start switch with %pM\n", - sta->addr); - ret = -EBUSY; - goto out; - } - - ret = iwl_mvm_tdls_config_channel_switch(mvm, vif, - TDLS_SEND_CHAN_SW_REQ, - sta->addr, sta->tdls_initiator, - oper_class, chandef, 0, 0, 0, - tmpl_skb, ch_sw_tm_ie); - if (ret) - goto out; - - /* - * Mark the peer as "in tdls switch" for this vif. We only allow a - * single such peer per vif. - */ - mvm->tdls_cs.peer.skb = skb_copy(tmpl_skb, GFP_KERNEL); - if (!mvm->tdls_cs.peer.skb) { - ret = -ENOMEM; - goto out; - } - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - mvm->tdls_cs.peer.sta_id = mvmsta->sta_id; - mvm->tdls_cs.peer.chandef = *chandef; - mvm->tdls_cs.peer.initiator = sta->tdls_initiator; - mvm->tdls_cs.peer.op_class = oper_class; - mvm->tdls_cs.peer.ch_sw_tm_ie = ch_sw_tm_ie; - - /* - * Wait for 2 DTIM periods before attempting the next switch. The next - * switch will be made sooner if the current one completes before that. - */ - delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period * - vif->bss_conf.beacon_int); - mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, - msecs_to_jiffies(delay)); - -out: - mutex_unlock(&mvm->mutex); - return ret; -} - -void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct ieee80211_sta *cur_sta; - bool wait_for_phy = false; - - mutex_lock(&mvm->mutex); - - IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr); - - /* we only support a single peer for channel switching */ - if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) { - IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr); - goto out; - } - - cur_sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id], - lockdep_is_held(&mvm->mutex)); - /* make sure it's the same peer */ - if (cur_sta != sta) - goto out; - - /* - * If we're currently in a switch because of the now canceled peer, - * wait a DTIM here to make sure the phy is back on the base channel. - * We can't otherwise force it. - */ - if (mvm->tdls_cs.cur_sta_id == mvm->tdls_cs.peer.sta_id && - mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE) - wait_for_phy = true; - - mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; - dev_kfree_skb(mvm->tdls_cs.peer.skb); - mvm->tdls_cs.peer.skb = NULL; - -out: - mutex_unlock(&mvm->mutex); - - /* make sure the phy is on the base channel */ - if (wait_for_phy) - msleep(TU_TO_MS(vif->bss_conf.dtim_period * - vif->bss_conf.beacon_int)); - - /* flush the channel switch state */ - flush_delayed_work(&mvm->tdls_cs.dwork); - - IWL_DEBUG_TDLS(mvm, "TDLS ending channel switch with %pM\n", sta->addr); -} - -void -iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_tdls_ch_sw_params *params) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - enum iwl_tdls_channel_switch_type type; - unsigned int delay; - const char *action_str = - params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ? - "REQ" : "RESP"; - - mutex_lock(&mvm->mutex); - - IWL_DEBUG_TDLS(mvm, - "Received TDLS ch switch action %s from %pM status %d\n", - action_str, params->sta->addr, params->status); - - /* - * we got a non-zero status from a peer we were switching to - move to - * the idle state and retry again later - */ - if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE && - params->status != 0 && - mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT && - mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) { - struct ieee80211_sta *cur_sta; - - /* make sure it's the same peer */ - cur_sta = rcu_dereference_protected( - mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id], - lockdep_is_held(&mvm->mutex)); - if (cur_sta == params->sta) { - iwl_mvm_tdls_update_cs_state(mvm, - IWL_MVM_TDLS_SW_IDLE); - goto retry; - } - } - - type = (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST) ? - TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH : TDLS_MOVE_CH; - - iwl_mvm_tdls_config_channel_switch(mvm, vif, type, params->sta->addr, - params->sta->tdls_initiator, 0, - params->chandef, params->timestamp, - params->switch_time, - params->switch_timeout, - params->tmpl_skb, - params->ch_sw_tm_ie); - -retry: - /* register a timeout in case we don't succeed in switching */ - delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int * - 1024 / 1000; - mod_delayed_work(system_wq, &mvm->tdls_cs.dwork, - msecs_to_jiffies(delay)); - mutex_unlock(&mvm->mutex); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/testmode.h b/drivers/net/wireless/iwlwifi/mvm/testmode.h deleted file mode 100644 index 79ab6beb6b26..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/testmode.h +++ /dev/null @@ -1,97 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __IWL_MVM_TESTMODE_H__ -#define __IWL_MVM_TESTMODE_H__ - -/** - * enum iwl_mvm_testmode_attrs - testmode attributes inside NL80211_ATTR_TESTDATA - * @IWL_MVM_TM_ATTR_UNSPEC: (invalid attribute) - * @IWL_MVM_TM_ATTR_CMD: sub command, see &enum iwl_mvm_testmode_commands (u32) - * @IWL_MVM_TM_ATTR_NOA_DURATION: requested NoA duration (u32) - * @IWL_MVM_TM_ATTR_BEACON_FILTER_STATE: beacon filter state (0 or 1, u32) - */ -enum iwl_mvm_testmode_attrs { - IWL_MVM_TM_ATTR_UNSPEC, - IWL_MVM_TM_ATTR_CMD, - IWL_MVM_TM_ATTR_NOA_DURATION, - IWL_MVM_TM_ATTR_BEACON_FILTER_STATE, - - /* keep last */ - NUM_IWL_MVM_TM_ATTRS, - IWL_MVM_TM_ATTR_MAX = NUM_IWL_MVM_TM_ATTRS - 1, -}; - -/** - * enum iwl_mvm_testmode_commands - MVM testmode commands - * @IWL_MVM_TM_CMD_SET_NOA: set NoA on GO vif for testing - * @IWL_MVM_TM_CMD_SET_BEACON_FILTER: turn beacon filtering off/on - */ -enum iwl_mvm_testmode_commands { - IWL_MVM_TM_CMD_SET_NOA, - IWL_MVM_TM_CMD_SET_BEACON_FILTER, -}; - -#endif /* __IWL_MVM_TESTMODE_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c deleted file mode 100644 index 7530eb23035d..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ /dev/null @@ -1,872 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include -#include - -#include "iwl-notif-wait.h" -#include "iwl-trans.h" -#include "fw-api.h" -#include "time-event.h" -#include "mvm.h" -#include "iwl-io.h" -#include "iwl-prph.h" - -/* - * For the high priority TE use a time event type that has similar priority to - * the FW's action scan priority. - */ -#define IWL_MVM_ROC_TE_TYPE_NORMAL TE_P2P_DEVICE_DISCOVERABLE -#define IWL_MVM_ROC_TE_TYPE_MGMT_TX TE_P2P_CLIENT_ASSOC - -void iwl_mvm_te_clear_data(struct iwl_mvm *mvm, - struct iwl_mvm_time_event_data *te_data) -{ - lockdep_assert_held(&mvm->time_event_lock); - - if (!te_data->vif) - return; - - list_del(&te_data->list); - te_data->running = false; - te_data->uid = 0; - te_data->id = TE_MAX; - te_data->vif = NULL; -} - -void iwl_mvm_roc_done_wk(struct work_struct *wk) -{ - struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk); - u32 queues = 0; - - /* - * Clear the ROC_RUNNING /ROC_AUX_RUNNING status bit. - * This will cause the TX path to drop offchannel transmissions. - * That would also be done by mac80211, but it is racy, in particular - * in the case that the time event actually completed in the firmware - * (which is handled in iwl_mvm_te_handle_notif). - */ - if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status)) { - queues |= BIT(IWL_MVM_OFFCHANNEL_QUEUE); - iwl_mvm_unref(mvm, IWL_MVM_REF_ROC); - } - if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status)) { - queues |= BIT(mvm->aux_queue); - iwl_mvm_unref(mvm, IWL_MVM_REF_ROC_AUX); - } - - synchronize_net(); - - /* - * Flush the offchannel queue -- this is called when the time - * event finishes or is canceled, so that frames queued for it - * won't get stuck on the queue and be transmitted in the next - * time event. - * We have to send the command asynchronously since this cannot - * be under the mutex for locking reasons, but that's not an - * issue as it will have to complete before the next command is - * executed, and a new time event means a new command. - */ - iwl_mvm_flush_tx_path(mvm, queues, CMD_ASYNC); -} - -static void iwl_mvm_roc_finished(struct iwl_mvm *mvm) -{ - /* - * Of course, our status bit is just as racy as mac80211, so in - * addition, fire off the work struct which will drop all frames - * from the hardware queues that made it through the race. First - * it will of course synchronize the TX path to make sure that - * any *new* TX will be rejected. - */ - schedule_work(&mvm->roc_done_wk); -} - -static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm) -{ - struct ieee80211_vif *csa_vif; - - rcu_read_lock(); - - csa_vif = rcu_dereference(mvm->csa_vif); - if (!csa_vif || !csa_vif->csa_active) - goto out_unlock; - - IWL_DEBUG_TE(mvm, "CSA NOA started\n"); - - /* - * CSA NoA is started but we still have beacons to - * transmit on the current channel. - * So we just do nothing here and the switch - * will be performed on the last TBTT. - */ - if (!ieee80211_csa_is_complete(csa_vif)) { - IWL_WARN(mvm, "CSA NOA started too early\n"); - goto out_unlock; - } - - ieee80211_csa_finish(csa_vif); - - rcu_read_unlock(); - - RCU_INIT_POINTER(mvm->csa_vif, NULL); - - return; - -out_unlock: - rcu_read_unlock(); -} - -static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - const char *errmsg) -{ - if (vif->type != NL80211_IFTYPE_STATION) - return false; - if (vif->bss_conf.assoc && vif->bss_conf.dtim_period) - return false; - if (errmsg) - IWL_ERR(mvm, "%s\n", errmsg); - - iwl_mvm_connection_loss(mvm, vif, errmsg); - return true; -} - -static void -iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm, - struct iwl_mvm_time_event_data *te_data, - struct iwl_time_event_notif *notif) -{ - struct ieee80211_vif *vif = te_data->vif; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - if (!notif->status) - IWL_DEBUG_TE(mvm, "CSA time event failed to start\n"); - - switch (te_data->vif->type) { - case NL80211_IFTYPE_AP: - if (!notif->status) - mvmvif->csa_failed = true; - iwl_mvm_csa_noa_start(mvm); - break; - case NL80211_IFTYPE_STATION: - if (!notif->status) { - iwl_mvm_connection_loss(mvm, vif, - "CSA TE failed to start"); - break; - } - iwl_mvm_csa_client_absent(mvm, te_data->vif); - ieee80211_chswitch_done(te_data->vif, true); - break; - default: - /* should never happen */ - WARN_ON_ONCE(1); - break; - } - - /* we don't need it anymore */ - iwl_mvm_te_clear_data(mvm, te_data); -} - -static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm, - struct iwl_time_event_notif *notif, - struct iwl_mvm_time_event_data *te_data) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_time_event *te_trig; - int i; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT); - te_trig = (void *)trig->data; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, te_data->vif, trig)) - return; - - for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) { - u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id); - u32 trig_action_bitmap = - le32_to_cpu(te_trig->time_events[i].action_bitmap); - u32 trig_status_bitmap = - le32_to_cpu(te_trig->time_events[i].status_bitmap); - - if (trig_te_id != te_data->id || - !(trig_action_bitmap & le32_to_cpu(notif->action)) || - !(trig_status_bitmap & BIT(le32_to_cpu(notif->status)))) - continue; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "Time event %d Action 0x%x received status: %d", - te_data->id, - le32_to_cpu(notif->action), - le32_to_cpu(notif->status)); - break; - } -} - -/* - * Handles a FW notification for an event that is known to the driver. - * - * @mvm: the mvm component - * @te_data: the time event data - * @notif: the notification data corresponding the time event data. - */ -static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, - struct iwl_mvm_time_event_data *te_data, - struct iwl_time_event_notif *notif) -{ - lockdep_assert_held(&mvm->time_event_lock); - - IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n", - le32_to_cpu(notif->unique_id), - le32_to_cpu(notif->action)); - - iwl_mvm_te_check_trigger(mvm, notif, te_data); - - /* - * The FW sends the start/end time event notifications even for events - * that it fails to schedule. This is indicated in the status field of - * the notification. This happens in cases that the scheduler cannot - * find a schedule that can handle the event (for example requesting a - * P2P Device discoveribility, while there are other higher priority - * events in the system). - */ - if (!le32_to_cpu(notif->status)) { - const char *msg; - - if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START)) - msg = "Time Event start notification failure"; - else - msg = "Time Event end notification failure"; - - IWL_DEBUG_TE(mvm, "%s\n", msg); - - if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) { - iwl_mvm_te_clear_data(mvm, te_data); - return; - } - } - - if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) { - IWL_DEBUG_TE(mvm, - "TE ended - current time %lu, estimated end %lu\n", - jiffies, te_data->end_jiffies); - - switch (te_data->vif->type) { - case NL80211_IFTYPE_P2P_DEVICE: - ieee80211_remain_on_channel_expired(mvm->hw); - iwl_mvm_roc_finished(mvm); - break; - case NL80211_IFTYPE_STATION: - /* - * By now, we should have finished association - * and know the dtim period. - */ - iwl_mvm_te_check_disconnect(mvm, te_data->vif, - "No association and the time event is over already..."); - break; - default: - break; - } - - iwl_mvm_te_clear_data(mvm, te_data); - } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) { - te_data->running = true; - te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration); - - if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { - set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); - iwl_mvm_ref(mvm, IWL_MVM_REF_ROC); - ieee80211_ready_on_channel(mvm->hw); - } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) { - iwl_mvm_te_handle_notify_csa(mvm, te_data, notif); - } - } else { - IWL_WARN(mvm, "Got TE with unknown action\n"); - } -} - -/* - * Handle A Aux ROC time event - */ -static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm, - struct iwl_time_event_notif *notif) -{ - struct iwl_mvm_time_event_data *te_data, *tmp; - bool aux_roc_te = false; - - list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) { - if (le32_to_cpu(notif->unique_id) == te_data->uid) { - aux_roc_te = true; - break; - } - } - if (!aux_roc_te) /* Not a Aux ROC time event */ - return -EINVAL; - - iwl_mvm_te_check_trigger(mvm, notif, te_data); - - if (!le32_to_cpu(notif->status)) { - IWL_DEBUG_TE(mvm, - "ERROR: Aux ROC Time Event %s notification failure\n", - (le32_to_cpu(notif->action) & - TE_V2_NOTIF_HOST_EVENT_START) ? "start" : "end"); - return -EINVAL; - } - - IWL_DEBUG_TE(mvm, - "Aux ROC time event notification - UID = 0x%x action %d\n", - le32_to_cpu(notif->unique_id), - le32_to_cpu(notif->action)); - - if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) { - /* End TE, notify mac80211 */ - ieee80211_remain_on_channel_expired(mvm->hw); - iwl_mvm_roc_finished(mvm); /* flush aux queue */ - list_del(&te_data->list); /* remove from list */ - te_data->running = false; - te_data->vif = NULL; - te_data->uid = 0; - te_data->id = TE_MAX; - } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) { - set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status); - te_data->running = true; - iwl_mvm_ref(mvm, IWL_MVM_REF_ROC_AUX); - ieee80211_ready_on_channel(mvm->hw); /* Start TE */ - } else { - IWL_DEBUG_TE(mvm, - "ERROR: Unknown Aux ROC Time Event (action = %d)\n", - le32_to_cpu(notif->action)); - return -EINVAL; - } - - return 0; -} - -/* - * The Rx handler for time event notifications - */ -void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_time_event_notif *notif = (void *)pkt->data; - struct iwl_mvm_time_event_data *te_data, *tmp; - - IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n", - le32_to_cpu(notif->unique_id), - le32_to_cpu(notif->action)); - - spin_lock_bh(&mvm->time_event_lock); - /* This time event is triggered for Aux ROC request */ - if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif)) - goto unlock; - - list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) { - if (le32_to_cpu(notif->unique_id) == te_data->uid) - iwl_mvm_te_handle_notif(mvm, te_data, notif); - } -unlock: - spin_unlock_bh(&mvm->time_event_lock); -} - -static bool iwl_mvm_te_notif(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_mvm *mvm = - container_of(notif_wait, struct iwl_mvm, notif_wait); - struct iwl_mvm_time_event_data *te_data = data; - struct iwl_time_event_notif *resp; - int resp_len = iwl_rx_packet_payload_len(pkt); - - if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_NOTIFICATION)) - return true; - - if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { - IWL_ERR(mvm, "Invalid TIME_EVENT_NOTIFICATION response\n"); - return true; - } - - resp = (void *)pkt->data; - - /* te_data->uid is already set in the TIME_EVENT_CMD response */ - if (le32_to_cpu(resp->unique_id) != te_data->uid) - return false; - - IWL_DEBUG_TE(mvm, "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n", - te_data->uid); - if (!resp->status) - IWL_ERR(mvm, - "TIME_EVENT_NOTIFICATION received but not executed\n"); - - return true; -} - -static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_mvm *mvm = - 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 = iwl_rx_packet_payload_len(pkt); - - if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD)) - return true; - - if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { - IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n"); - return true; - } - - resp = (void *)pkt->data; - - /* we should never get a response to another TIME_EVENT_CMD here */ - if (WARN_ON_ONCE(le32_to_cpu(resp->id) != te_data->id)) - return false; - - te_data->uid = le32_to_cpu(resp->unique_id); - IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", - te_data->uid); - return true; -} - -static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mvm_time_event_data *te_data, - struct iwl_time_event_cmd *te_cmd) -{ - static const u16 time_event_response[] = { TIME_EVENT_CMD }; - struct iwl_notification_wait wait_time_event; - int ret; - - lockdep_assert_held(&mvm->mutex); - - IWL_DEBUG_TE(mvm, "Add new TE, duration %d TU\n", - le32_to_cpu(te_cmd->duration)); - - spin_lock_bh(&mvm->time_event_lock); - if (WARN_ON(te_data->id != TE_MAX)) { - spin_unlock_bh(&mvm->time_event_lock); - return -EIO; - } - te_data->vif = vif; - te_data->duration = le32_to_cpu(te_cmd->duration); - te_data->id = le32_to_cpu(te_cmd->id); - list_add_tail(&te_data->list, &mvm->time_event_list); - spin_unlock_bh(&mvm->time_event_lock); - - /* - * Use a notification wait, which really just processes the - * command response and doesn't wait for anything, in order - * to be able to process the response and get the UID inside - * the RX path. Using CMD_WANT_SKB doesn't work because it - * stores the buffer and then wakes up this thread, by which - * time another notification (that the time event started) - * might already be processed unsuccessfully. - */ - iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, - time_event_response, - ARRAY_SIZE(time_event_response), - iwl_mvm_time_event_response, te_data); - - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, - sizeof(*te_cmd), te_cmd); - if (ret) { - IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); - iwl_remove_notification(&mvm->notif_wait, &wait_time_event); - goto out_clear_te; - } - - /* No need to wait for anything, so just pass 1 (0 isn't valid) */ - ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1); - /* should never fail */ - WARN_ON_ONCE(ret); - - if (ret) { - out_clear_te: - spin_lock_bh(&mvm->time_event_lock); - iwl_mvm_te_clear_data(mvm, te_data); - spin_unlock_bh(&mvm->time_event_lock); - } - return ret; -} - -void iwl_mvm_protect_session(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 duration, u32 min_duration, - u32 max_delay, bool wait_for_notif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - const u16 te_notif_response[] = { TIME_EVENT_NOTIFICATION }; - struct iwl_notification_wait wait_te_notif; - struct iwl_time_event_cmd time_cmd = {}; - - lockdep_assert_held(&mvm->mutex); - - if (te_data->running && - time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { - IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", - jiffies_to_msecs(te_data->end_jiffies - jiffies)); - return; - } - - if (te_data->running) { - IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n", - te_data->uid, - jiffies_to_msecs(te_data->end_jiffies - jiffies)); - /* - * we don't have enough time - * cancel the current TE and issue a new one - * Of course it would be better to remove the old one only - * when the new one is added, but we don't care if we are off - * channel for a bit. All we need to do, is not to return - * before we actually begin to be on the channel. - */ - iwl_mvm_stop_session_protection(mvm, vif); - } - - time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); - time_cmd.id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); - time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC); - - time_cmd.apply_time = cpu_to_le32(0); - - time_cmd.max_frags = TE_V2_FRAG_NONE; - time_cmd.max_delay = cpu_to_le32(max_delay); - /* TODO: why do we need to interval = bi if it is not periodic? */ - time_cmd.interval = cpu_to_le32(1); - time_cmd.duration = cpu_to_le32(duration); - time_cmd.repeat = 1; - time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | - TE_V2_NOTIF_HOST_EVENT_END | - T2_V2_START_IMMEDIATELY); - - if (!wait_for_notif) { - iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); - return; - } - - /* - * Create notification_wait for the TIME_EVENT_NOTIFICATION to use - * right after we send the time event - */ - iwl_init_notification_wait(&mvm->notif_wait, &wait_te_notif, - te_notif_response, - ARRAY_SIZE(te_notif_response), - iwl_mvm_te_notif, te_data); - - /* If TE was sent OK - wait for the notification that started */ - if (iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd)) { - IWL_ERR(mvm, "Failed to add TE to protect session\n"); - iwl_remove_notification(&mvm->notif_wait, &wait_te_notif); - } else if (iwl_wait_notification(&mvm->notif_wait, &wait_te_notif, - TU_TO_JIFFIES(max_delay))) { - IWL_ERR(mvm, "Failed to protect session until TE\n"); - } -} - -static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm, - struct iwl_mvm_time_event_data *te_data, - u32 *uid) -{ - u32 id; - - /* - * It is possible that by the time we got to this point the time - * event was already removed. - */ - spin_lock_bh(&mvm->time_event_lock); - - /* Save time event uid before clearing its data */ - *uid = te_data->uid; - id = te_data->id; - - /* - * The clear_data function handles time events that were already removed - */ - iwl_mvm_te_clear_data(mvm, te_data); - spin_unlock_bh(&mvm->time_event_lock); - - /* - * It is possible that by the time we try to remove it, the time event - * has already ended and removed. In such a case there is no need to - * send a removal command. - */ - if (id == TE_MAX) { - IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid); - return false; - } - - return true; -} - -/* - * Explicit request to remove a aux roc time event. The removal of a time - * event needs to be synchronized with the flow of a time event's end - * notification, which also removes the time event from the op mode - * data structures. - */ -static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - struct iwl_mvm_time_event_data *te_data) -{ - struct iwl_hs20_roc_req aux_cmd = {}; - u32 uid; - int ret; - - if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) - return; - - aux_cmd.event_unique_id = cpu_to_le32(uid); - aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); - aux_cmd.id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); - IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n", - le32_to_cpu(aux_cmd.event_unique_id)); - ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, - sizeof(aux_cmd), &aux_cmd); - - if (WARN_ON(ret)) - return; -} - -/* - * Explicit request to remove a time event. The removal of a time event needs to - * be synchronized with the flow of a time event's end notification, which also - * removes the time event from the op mode data structures. - */ -void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - struct iwl_mvm_time_event_data *te_data) -{ - struct iwl_time_event_cmd time_cmd = {}; - u32 uid; - int ret; - - if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid)) - return; - - /* When we remove a TE, the UID is to be set in the id field */ - time_cmd.id = cpu_to_le32(uid); - time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); - time_cmd.id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); - - IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id)); - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, - sizeof(time_cmd), &time_cmd); - if (WARN_ON(ret)) - return; -} - -void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - - lockdep_assert_held(&mvm->mutex); - iwl_mvm_remove_time_event(mvm, mvmvif, te_data); -} - -int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - int duration, enum ieee80211_roc_type type) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - struct iwl_time_event_cmd time_cmd = {}; - - lockdep_assert_held(&mvm->mutex); - if (te_data->running) { - IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n"); - return -EBUSY; - } - - /* - * Flush the done work, just in case it's still pending, so that - * the work it does can complete and we can accept new frames. - */ - flush_work(&mvm->roc_done_wk); - - time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); - time_cmd.id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); - - switch (type) { - case IEEE80211_ROC_TYPE_NORMAL: - time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_NORMAL); - break; - case IEEE80211_ROC_TYPE_MGMT_TX: - time_cmd.id = cpu_to_le32(IWL_MVM_ROC_TE_TYPE_MGMT_TX); - break; - default: - WARN_ONCE(1, "Got an invalid ROC type\n"); - return -EINVAL; - } - - time_cmd.apply_time = cpu_to_le32(0); - time_cmd.interval = cpu_to_le32(1); - - /* - * The P2P Device TEs can have lower priority than other events - * that are being scheduled by the driver/fw, and thus it might not be - * scheduled. To improve the chances of it being scheduled, allow them - * to be fragmented, and in addition allow them to be delayed. - */ - time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS); - time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2)); - time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); - time_cmd.repeat = 1; - time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | - TE_V2_NOTIF_HOST_EVENT_END | - T2_V2_START_IMMEDIATELY); - - return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); -} - -void iwl_mvm_stop_roc(struct iwl_mvm *mvm) -{ - struct iwl_mvm_vif *mvmvif = NULL; - struct iwl_mvm_time_event_data *te_data; - bool is_p2p = false; - - lockdep_assert_held(&mvm->mutex); - - spin_lock_bh(&mvm->time_event_lock); - - /* - * Iterate over the list of time events and find the time event that is - * associated with a P2P_DEVICE interface. - * This assumes that a P2P_DEVICE interface can have only a single time - * event at any given time and this time event coresponds to a ROC - * request - */ - list_for_each_entry(te_data, &mvm->time_event_list, list) { - if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { - mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); - is_p2p = true; - goto remove_te; - } - } - - /* There can only be at most one AUX ROC time event, we just use the - * list to simplify/unify code. Remove it if it exists. - */ - te_data = list_first_entry_or_null(&mvm->aux_roc_te_list, - struct iwl_mvm_time_event_data, - list); - if (te_data) - mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); - -remove_te: - spin_unlock_bh(&mvm->time_event_lock); - - if (!mvmvif) { - IWL_WARN(mvm, "No remain on channel event\n"); - return; - } - - if (is_p2p) - iwl_mvm_remove_time_event(mvm, mvmvif, te_data); - else - iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data); - - iwl_mvm_roc_finished(mvm); -} - -int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 duration, u32 apply_time) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - struct iwl_time_event_cmd time_cmd = {}; - - lockdep_assert_held(&mvm->mutex); - - if (te_data->running) { - IWL_DEBUG_TE(mvm, "CS period is already scheduled\n"); - return -EBUSY; - } - - time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD); - time_cmd.id_and_color = - cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); - time_cmd.id = cpu_to_le32(TE_CHANNEL_SWITCH_PERIOD); - time_cmd.apply_time = cpu_to_le32(apply_time); - time_cmd.max_frags = TE_V2_FRAG_NONE; - time_cmd.duration = cpu_to_le32(duration); - time_cmd.repeat = 1; - time_cmd.interval = cpu_to_le32(1); - time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | - TE_V2_ABSENCE); - - return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h deleted file mode 100644 index cbdf8e52a5f1..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.h +++ /dev/null @@ -1,249 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#ifndef __time_event_h__ -#define __time_event_h__ - -#include "fw-api.h" - -#include "mvm.h" - -/** - * DOC: Time Events - what is it? - * - * Time Events are a fw feature that allows the driver to control the presence - * of the device on the channel. Since the fw supports multiple channels - * concurrently, the fw may choose to jump to another channel at any time. - * In order to make sure that the fw is on a specific channel at a certain time - * and for a certain duration, the driver needs to issue a time event. - * - * The simplest example is for BSS association. The driver issues a time event, - * waits for it to start, and only then tells mac80211 that we can start the - * association. This way, we make sure that the association will be done - * smoothly and won't be interrupted by channel switch decided within the fw. - */ - - /** - * DOC: The flow against the fw - * - * When the driver needs to make sure we are in a certain channel, at a certain - * time and for a certain duration, it sends a Time Event. The flow against the - * fw goes like this: - * 1) Driver sends a TIME_EVENT_CMD to the fw - * 2) Driver gets the response for that command. This response contains the - * Unique ID (UID) of the event. - * 3) The fw sends notification when the event starts. - * - * Of course the API provides various options that allow to cover parameters - * of the flow. - * What is the duration of the event? - * What is the start time of the event? - * Is there an end-time for the event? - * How much can the event be delayed? - * Can the event be split? - * If yes what is the maximal number of chunks? - * etc... - */ - -/** - * DOC: Abstraction to the driver - * - * In order to simplify the use of time events to the rest of the driver, - * we abstract the use of time events. This component provides the functions - * needed by the driver. - */ - -#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500 -#define IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400 - -/** - * iwl_mvm_protect_session - start / extend the session protection. - * @mvm: the mvm component - * @vif: the virtual interface for which the session is issued - * @duration: the duration of the session in TU. - * @min_duration: will start a new session if the current session will end - * in less than min_duration. - * @max_delay: maximum delay before starting the time event (in TU) - * @wait_for_notif: true if it is required that a time event notification be - * waited for (that the time event has been scheduled before returning) - * - * This function can be used to start a session protection which means that the - * fw will stay on the channel for %duration_ms milliseconds. This function - * can block (sleep) until the session starts. This function can also be used - * to extend a currently running session. - * This function is meant to be used for BSS association for example, where we - * want to make sure that the fw stays on the channel during the association. - */ -void iwl_mvm_protect_session(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 duration, u32 min_duration, - u32 max_delay, bool wait_for_notif); - -/** - * iwl_mvm_stop_session_protection - cancel the session protection. - * @mvm: the mvm component - * @vif: the virtual interface for which the session is issued - * - * This functions cancels the session protection which is an act of good - * citizenship. If it is not needed any more it should be canceled because - * the other bindings wait for the medium during that time. - * This funtions doesn't sleep. - */ -void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); - -/* - * iwl_mvm_rx_time_event_notif - handles %TIME_EVENT_NOTIFICATION. - */ -void iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); - -/** - * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionality - * @mvm: the mvm component - * @vif: the virtual interface for which the roc is requested. It is assumed - * that the vif type is NL80211_IFTYPE_P2P_DEVICE - * @duration: the requested duration in millisecond for the fw to be on the - * channel that is bound to the vif. - * @type: the remain on channel request type - * - * This function can be used to issue a remain on channel session, - * which means that the fw will stay in the channel for the request %duration - * milliseconds. The function is async, meaning that it only issues the ROC - * request but does not wait for it to start. Once the FW is ready to serve the - * ROC request, it will issue a notification to the driver that it is on the - * requested channel. Once the FW completes the ROC request it will issue - * another notification to the driver. - */ -int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - int duration, enum ieee80211_roc_type type); - -/** - * iwl_mvm_stop_roc - stop remain on channel functionality - * @mvm: the mvm component - * - * This function can be used to cancel an ongoing ROC session. - * The function is async, it will instruct the FW to stop serving the ROC - * session, but will not wait for the actual stopping of the session. - */ -void iwl_mvm_stop_roc(struct iwl_mvm *mvm); - -/** - * iwl_mvm_remove_time_event - general function to clean up of time event - * @mvm: the mvm component - * @vif: the vif to which the time event belongs - * @te_data: the time event data that corresponds to that time event - * - * This function can be used to cancel a time event regardless its type. - * It is useful for cleaning up time events running before removing an - * interface. - */ -void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, - struct iwl_mvm_vif *mvmvif, - struct iwl_mvm_time_event_data *te_data); - -/** - * iwl_mvm_te_clear_data - remove time event from list - * @mvm: the mvm component - * @te_data: the time event data to remove - * - * This function is mostly internal, it is made available here only - * for firmware restart purposes. - */ -void iwl_mvm_te_clear_data(struct iwl_mvm *mvm, - struct iwl_mvm_time_event_data *te_data); - -void iwl_mvm_roc_done_wk(struct work_struct *wk); - -/** - * iwl_mvm_schedule_csa_period - request channel switch absence period - * @mvm: the mvm component - * @vif: the virtual interface for which the channel switch is issued - * @duration: the duration of the NoA in TU. - * @apply_time: NoA start time in GP2. - * - * This function is used to schedule NoA time event and is used to perform - * the channel switch flow. - */ -int iwl_mvm_schedule_csa_period(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 duration, u32 apply_time); - -/** - * iwl_mvm_te_scheduled - check if the fw received the TE cmd - * @te_data: the time event data that corresponds to that time event - * - * This function returns true iff this TE is added to the fw. - */ -static inline bool -iwl_mvm_te_scheduled(struct iwl_mvm_time_event_data *te_data) -{ - if (!te_data) - return false; - - return !!te_data->uid; -} - -#endif /* __time_event_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.c b/drivers/net/wireless/iwlwifi/mvm/tof.c deleted file mode 100644 index 4007f1d421dd..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/tof.c +++ /dev/null @@ -1,306 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2015 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include "mvm.h" -#include "fw-api-tof.h" - -#define IWL_MVM_TOF_RANGE_REQ_MAX_ID 256 - -void iwl_mvm_tof_init(struct iwl_mvm *mvm) -{ - struct iwl_mvm_tof_data *tof_data = &mvm->tof_data; - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return; - - memset(tof_data, 0, sizeof(*tof_data)); - - tof_data->tof_cfg.sub_grp_cmd_id = cpu_to_le32(TOF_CONFIG_CMD); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (IWL_MVM_TOF_IS_RESPONDER) { - tof_data->responder_cfg.sub_grp_cmd_id = - cpu_to_le32(TOF_RESPONDER_CONFIG_CMD); - tof_data->responder_cfg.sta_id = IWL_MVM_STATION_COUNT; - } -#endif - - tof_data->range_req.sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_REQ_CMD); - tof_data->range_req.req_timeout = 1; - tof_data->range_req.initiator = 1; - tof_data->range_req.report_policy = 3; - - tof_data->range_req_ext.sub_grp_cmd_id = - cpu_to_le32(TOF_RANGE_REQ_EXT_CMD); - - mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; -} - -void iwl_mvm_tof_clean(struct iwl_mvm *mvm) -{ - struct iwl_mvm_tof_data *tof_data = &mvm->tof_data; - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return; - - memset(tof_data, 0, sizeof(*tof_data)); - mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; -} - -static void iwl_tof_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - bool *enabled = _data; - - /* non bss vif exists */ - if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) - *enabled = false; -} - -int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm) -{ - struct iwl_tof_config_cmd *cmd = &mvm->tof_data.tof_cfg; - bool enabled; - - lockdep_assert_held(&mvm->mutex); - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return -EINVAL; - - ieee80211_iterate_active_interfaces_atomic(mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_tof_iterator, &enabled); - if (!enabled) { - IWL_DEBUG_INFO(mvm, "ToF is not supported (non bss vif)\n"); - return -EINVAL; - } - - mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(*cmd), cmd); -} - -int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id) -{ - struct iwl_tof_range_abort_cmd cmd = { - .sub_grp_cmd_id = cpu_to_le32(TOF_RANGE_ABORT_CMD), - .request_id = id, - }; - - lockdep_assert_held(&mvm->mutex); - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return -EINVAL; - - if (id != mvm->tof_data.active_range_request) { - IWL_ERR(mvm, "Invalid range request id %d (active %d)\n", - id, mvm->tof_data.active_range_request); - return -EINVAL; - } - - /* after abort is sent there's no active request anymore */ - mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; - - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(cmd), &cmd); -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_tof_responder_config_cmd *cmd = &mvm->tof_data.responder_cfg; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - lockdep_assert_held(&mvm->mutex); - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return -EINVAL; - - if (vif->p2p || vif->type != NL80211_IFTYPE_AP || - !mvmvif->ap_ibss_active) { - IWL_ERR(mvm, "Cannot start responder, not in AP mode\n"); - return -EIO; - } - - cmd->sta_id = mvmvif->bcast_sta.sta_id; - memcpy(cmd->bssid, vif->addr, ETH_ALEN); - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(*cmd), cmd); -} -#endif - -int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_host_cmd cmd = { - .id = iwl_cmd_id(TOF_CMD, IWL_ALWAYS_LONG_GROUP, 0), - .len = { sizeof(mvm->tof_data.range_req), }, - /* no copy because of the command size */ - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - }; - - lockdep_assert_held(&mvm->mutex); - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return -EINVAL; - - if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) { - IWL_ERR(mvm, "Cannot send range request, not STA mode\n"); - return -EIO; - } - - /* nesting of range requests is not supported in FW */ - if (mvm->tof_data.active_range_request != - IWL_MVM_TOF_RANGE_REQ_MAX_ID) { - IWL_ERR(mvm, "Cannot send range req, already active req %d\n", - mvm->tof_data.active_range_request); - return -EIO; - } - - mvm->tof_data.active_range_request = mvm->tof_data.range_req.request_id; - - cmd.data[0] = &mvm->tof_data.range_req; - return iwl_mvm_send_cmd(mvm, &cmd); -} - -int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - lockdep_assert_held(&mvm->mutex); - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TOF_SUPPORT)) - return -EINVAL; - - if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) { - IWL_ERR(mvm, "Cannot send ext range req, not in STA mode\n"); - return -EIO; - } - - return iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(TOF_CMD, - IWL_ALWAYS_LONG_GROUP, 0), - 0, sizeof(mvm->tof_data.range_req_ext), - &mvm->tof_data.range_req_ext); -} - -static int iwl_mvm_tof_range_resp(struct iwl_mvm *mvm, void *data) -{ - struct iwl_tof_range_rsp_ntfy *resp = (void *)data; - - if (resp->request_id != mvm->tof_data.active_range_request) { - IWL_ERR(mvm, "Request id mismatch, got %d, active %d\n", - resp->request_id, mvm->tof_data.active_range_request); - return -EIO; - } - - memcpy(&mvm->tof_data.range_resp, resp, - sizeof(struct iwl_tof_range_rsp_ntfy)); - mvm->tof_data.active_range_request = IWL_MVM_TOF_RANGE_REQ_MAX_ID; - - return 0; -} - -static int iwl_mvm_tof_mcsi_notif(struct iwl_mvm *mvm, void *data) -{ - struct iwl_tof_mcsi_notif *resp = (struct iwl_tof_mcsi_notif *)data; - - IWL_DEBUG_INFO(mvm, "MCSI notification, token %d\n", resp->token); - return 0; -} - -static int iwl_mvm_tof_nb_report_notif(struct iwl_mvm *mvm, void *data) -{ - struct iwl_tof_neighbor_report *report = - (struct iwl_tof_neighbor_report *)data; - - IWL_DEBUG_INFO(mvm, "NB report, bssid %pM, token %d, status 0x%x\n", - report->bssid, report->request_token, report->status); - return 0; -} - -void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_tof_gen_resp_cmd *resp = (void *)pkt->data; - - lockdep_assert_held(&mvm->mutex); - - switch (le32_to_cpu(resp->sub_grp_cmd_id)) { - case TOF_RANGE_RESPONSE_NOTIF: - iwl_mvm_tof_range_resp(mvm, resp->data); - break; - case TOF_MCSI_DEBUG_NOTIF: - iwl_mvm_tof_mcsi_notif(mvm, resp->data); - break; - case TOF_NEIGHBOR_REPORT_RSP_NOTIF: - iwl_mvm_tof_nb_report_notif(mvm, resp->data); - break; - default: - IWL_ERR(mvm, "Unknown sub-group command 0x%x\n", - resp->sub_grp_cmd_id); - break; - } -} diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.h b/drivers/net/wireless/iwlwifi/mvm/tof.h deleted file mode 100644 index 9beebc33cb8d..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/tof.h +++ /dev/null @@ -1,94 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2015 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __tof_h__ -#define __tof_h__ - -#include "fw-api-tof.h" - -struct iwl_mvm_tof_data { - struct iwl_tof_config_cmd tof_cfg; - struct iwl_tof_range_req_cmd range_req; - struct iwl_tof_range_req_ext_cmd range_req_ext; -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct iwl_tof_responder_config_cmd responder_cfg; -#endif - struct iwl_tof_range_rsp_ntfy range_resp; - u8 last_abort_id; - u16 active_range_request; -}; - -void iwl_mvm_tof_init(struct iwl_mvm *mvm); -void iwl_mvm_tof_clean(struct iwl_mvm *mvm); -int iwl_mvm_tof_config_cmd(struct iwl_mvm *mvm); -int iwl_mvm_tof_range_abort_cmd(struct iwl_mvm *mvm, u8 id); -int iwl_mvm_tof_range_request_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -void iwl_mvm_tof_resp_handler(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); -int iwl_mvm_tof_range_request_ext_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -#ifdef CONFIG_IWLWIFI_DEBUGFS -int iwl_mvm_tof_responder_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -#endif -#endif /* __tof_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c deleted file mode 100644 index cadfc0460597..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/tt.c +++ /dev/null @@ -1,460 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#include "mvm.h" - -#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ - -static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) -{ - struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; - u32 duration = tt->params.ct_kill_duration; - - if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) - return; - - IWL_ERR(mvm, "Enter CT Kill\n"); - iwl_mvm_set_hw_ctkill_state(mvm, true); - - tt->throttle = false; - tt->dynamic_smps = false; - - /* Don't schedule an exit work if we're in test mode, since - * the temperature will not change unless we manually set it - * again (or disable testing). - */ - if (!mvm->temperature_test) - schedule_delayed_work(&tt->ct_kill_exit, - round_jiffies_relative(duration * HZ)); -} - -static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm) -{ - if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) - return; - - IWL_ERR(mvm, "Exit CT Kill\n"); - iwl_mvm_set_hw_ctkill_state(mvm, false); -} - -void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp) -{ - /* ignore the notification if we are in test mode */ - if (mvm->temperature_test) - return; - - if (mvm->temperature == temp) - return; - - mvm->temperature = temp; - iwl_mvm_tt_handler(mvm); -} - -static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_dts_measurement_notif *notif; - int len = iwl_rx_packet_payload_len(pkt); - int temp; - - if (WARN_ON_ONCE(len != sizeof(*notif))) { - IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n"); - return -EINVAL; - } - - notif = (void *)pkt->data; - - temp = le32_to_cpu(notif->temp); - - /* shouldn't be negative, but since it's s32, make sure it isn't */ - if (WARN_ON_ONCE(temp < 0)) - temp = 0; - - IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp); - - return temp; -} - -static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_mvm *mvm = - container_of(notif_wait, struct iwl_mvm, notif_wait); - int *temp = data; - int ret; - - ret = iwl_mvm_temp_notif_parse(mvm, pkt); - if (ret < 0) - return true; - - *temp = ret; - - return true; -} - -void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - int temp; - - /* the notification is handled synchronously in ctkill, so skip here */ - if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) - return; - - temp = iwl_mvm_temp_notif_parse(mvm, pkt); - if (temp < 0) - return; - - iwl_mvm_tt_temp_changed(mvm, temp); -} - -static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm) -{ - struct iwl_dts_measurement_cmd cmd = { - .flags = cpu_to_le32(DTS_TRIGGER_CMD_FLAGS_TEMP), - }; - struct iwl_ext_dts_measurement_cmd extcmd = { - .control_mode = cpu_to_le32(DTS_AUTOMATIC), - }; - u32 cmdid; - - if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR)) - cmdid = iwl_cmd_id(CMD_DTS_MEASUREMENT_TRIGGER_WIDE, - PHY_OPS_GROUP, 0); - else - cmdid = CMD_DTS_MEASUREMENT_TRIGGER; - - if (!fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE)) - return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(cmd), &cmd); - - return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(extcmd), &extcmd); -} - -int iwl_mvm_get_temp(struct iwl_mvm *mvm) -{ - struct iwl_notification_wait wait_temp_notif; - static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP, - DTS_MEASUREMENT_NOTIF_WIDE) }; - int ret, temp; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR)) - temp_notif[0] = DTS_MEASUREMENT_NOTIFICATION; - - lockdep_assert_held(&mvm->mutex); - - iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif, - temp_notif, ARRAY_SIZE(temp_notif), - iwl_mvm_temp_notif_wait, &temp); - - ret = iwl_mvm_get_temp_cmd(mvm); - if (ret) { - IWL_ERR(mvm, "Failed to get the temperature (err=%d)\n", ret); - iwl_remove_notification(&mvm->notif_wait, &wait_temp_notif); - return ret; - } - - ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif, - IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT); - if (ret) { - IWL_ERR(mvm, "Getting the temperature timed out\n"); - return ret; - } - - return temp; -} - -static void check_exit_ctkill(struct work_struct *work) -{ - struct iwl_mvm_tt_mgmt *tt; - struct iwl_mvm *mvm; - u32 duration; - s32 temp; - - tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work); - mvm = container_of(tt, struct iwl_mvm, thermal_throttle); - - duration = tt->params.ct_kill_duration; - - mutex_lock(&mvm->mutex); - - if (__iwl_mvm_mac_start(mvm)) - goto reschedule; - - /* make sure the device is available for direct read/writes */ - if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL)) { - __iwl_mvm_mac_stop(mvm); - goto reschedule; - } - - temp = iwl_mvm_get_temp(mvm); - - iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL); - - __iwl_mvm_mac_stop(mvm); - - if (temp < 0) - goto reschedule; - - IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp); - - if (temp <= tt->params.ct_kill_exit) { - mutex_unlock(&mvm->mutex); - iwl_mvm_exit_ctkill(mvm); - return; - } - -reschedule: - mutex_unlock(&mvm->mutex); - schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, - round_jiffies(duration * HZ)); -} - -static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = _data; - enum ieee80211_smps_mode smps_mode; - - lockdep_assert_held(&mvm->mutex); - - if (mvm->thermal_throttle.dynamic_smps) - smps_mode = IEEE80211_SMPS_DYNAMIC; - else - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - if (vif->type != NL80211_IFTYPE_STATION) - return; - - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode); -} - -static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) -{ - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - int i, err; - - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta)) - continue; - mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (enable == mvmsta->tt_tx_protection) - continue; - err = iwl_mvm_tx_protection(mvm, mvmsta, enable); - if (err) { - IWL_ERR(mvm, "Failed to %s Tx protection\n", - enable ? "enable" : "disable"); - } else { - IWL_DEBUG_TEMP(mvm, "%s Tx protection\n", - enable ? "Enable" : "Disable"); - mvmsta->tt_tx_protection = enable; - } - } -} - -void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff) -{ - struct iwl_host_cmd cmd = { - .id = REPLY_THERMAL_MNG_BACKOFF, - .len = { sizeof(u32), }, - .data = { &backoff, }, - }; - - backoff = max(backoff, mvm->thermal_throttle.min_backoff); - - if (iwl_mvm_send_cmd(mvm, &cmd) == 0) { - IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n", - backoff); - mvm->thermal_throttle.tx_backoff = backoff; - } else { - IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n"); - } -} - -void iwl_mvm_tt_handler(struct iwl_mvm *mvm) -{ - struct iwl_tt_params *params = &mvm->thermal_throttle.params; - struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; - s32 temperature = mvm->temperature; - bool throttle_enable = false; - int i; - u32 tx_backoff; - - IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature); - - if (params->support_ct_kill && temperature >= params->ct_kill_entry) { - iwl_mvm_enter_ctkill(mvm); - return; - } - - if (params->support_ct_kill && - temperature <= params->ct_kill_exit) { - iwl_mvm_exit_ctkill(mvm); - return; - } - - if (params->support_dynamic_smps) { - if (!tt->dynamic_smps && - temperature >= params->dynamic_smps_entry) { - IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n"); - tt->dynamic_smps = true; - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_tt_smps_iterator, mvm); - throttle_enable = true; - } else if (tt->dynamic_smps && - temperature <= params->dynamic_smps_exit) { - IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n"); - tt->dynamic_smps = false; - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_tt_smps_iterator, mvm); - } - } - - if (params->support_tx_protection) { - if (temperature >= params->tx_protection_entry) { - iwl_mvm_tt_tx_protection(mvm, true); - throttle_enable = true; - } else if (temperature <= params->tx_protection_exit) { - iwl_mvm_tt_tx_protection(mvm, false); - } - } - - if (params->support_tx_backoff) { - tx_backoff = tt->min_backoff; - for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) { - if (temperature < params->tx_backoff[i].temperature) - break; - tx_backoff = max(tt->min_backoff, - params->tx_backoff[i].backoff); - } - if (tx_backoff != tt->min_backoff) - throttle_enable = true; - if (tt->tx_backoff != tx_backoff) - iwl_mvm_tt_tx_backoff(mvm, tx_backoff); - } - - if (!tt->throttle && throttle_enable) { - IWL_WARN(mvm, - "Due to high temperature thermal throttling initiated\n"); - tt->throttle = true; - } else if (tt->throttle && !tt->dynamic_smps && - tt->tx_backoff == tt->min_backoff && - temperature <= params->tx_protection_exit) { - IWL_WARN(mvm, - "Temperature is back to normal thermal throttling stopped\n"); - tt->throttle = false; - } -} - -static const struct iwl_tt_params iwl_mvm_default_tt_params = { - .ct_kill_entry = 118, - .ct_kill_exit = 96, - .ct_kill_duration = 5, - .dynamic_smps_entry = 114, - .dynamic_smps_exit = 110, - .tx_protection_entry = 114, - .tx_protection_exit = 108, - .tx_backoff = { - {.temperature = 112, .backoff = 200}, - {.temperature = 113, .backoff = 600}, - {.temperature = 114, .backoff = 1200}, - {.temperature = 115, .backoff = 2000}, - {.temperature = 116, .backoff = 4000}, - {.temperature = 117, .backoff = 10000}, - }, - .support_ct_kill = true, - .support_dynamic_smps = true, - .support_tx_protection = true, - .support_tx_backoff = true, -}; - -void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff) -{ - struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; - - IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n"); - - if (mvm->cfg->thermal_params) - tt->params = *mvm->cfg->thermal_params; - else - tt->params = iwl_mvm_default_tt_params; - - tt->throttle = false; - tt->dynamic_smps = false; - tt->min_backoff = min_backoff; - INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); -} - -void iwl_mvm_tt_exit(struct iwl_mvm *mvm) -{ - cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit); - IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n"); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c deleted file mode 100644 index c652a66be803..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ /dev/null @@ -1,1115 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include -#include - -#include "iwl-trans.h" -#include "iwl-eeprom-parse.h" -#include "mvm.h" -#include "sta.h" - -static void -iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr, - u16 tid, u16 ssn) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_ba *ba_trig; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA)) - return; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA); - ba_trig = (void *)trig->data; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) - return; - - if (!(le16_to_cpu(ba_trig->tx_bar) & BIT(tid))) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, - "BAR sent to %pM, tid %d, ssn %d", - addr, tid, ssn); -} - -/* - * Sets most of the Tx cmd's fields - */ -void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, - struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, u8 sta_id) -{ - struct ieee80211_hdr *hdr = (void *)skb->data; - __le16 fc = hdr->frame_control; - u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags); - u32 len = skb->len + FCS_LEN; - u8 ac; - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) - tx_flags |= TX_CMD_FLG_ACK; - else - tx_flags &= ~TX_CMD_FLG_ACK; - - if (ieee80211_is_probe_resp(fc)) - tx_flags |= TX_CMD_FLG_TSF; - - if (ieee80211_has_morefrags(fc)) - tx_flags |= TX_CMD_FLG_MORE_FRAG; - - if (ieee80211_is_data_qos(fc)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tx_cmd->tid_tspec = qc[0] & 0xf; - tx_flags &= ~TX_CMD_FLG_SEQ_CTL; - } else if (ieee80211_is_back_req(fc)) { - struct ieee80211_bar *bar = (void *)skb->data; - u16 control = le16_to_cpu(bar->control); - u16 ssn = le16_to_cpu(bar->start_seq_num); - - tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; - tx_cmd->tid_tspec = (control & - IEEE80211_BAR_CTRL_TID_INFO_MASK) >> - IEEE80211_BAR_CTRL_TID_INFO_SHIFT; - WARN_ON_ONCE(tx_cmd->tid_tspec >= IWL_MAX_TID_COUNT); - iwl_mvm_bar_check_trigger(mvm, bar->ra, tx_cmd->tid_tspec, - ssn); - } else { - tx_cmd->tid_tspec = IWL_TID_NON_QOS; - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) - tx_flags |= TX_CMD_FLG_SEQ_CTL; - else - tx_flags &= ~TX_CMD_FLG_SEQ_CTL; - } - - /* Default to 0 (BE) when tid_spec is set to IWL_TID_NON_QOS */ - if (tx_cmd->tid_tspec < IWL_MAX_TID_COUNT) - ac = tid_to_mac80211_ac[tx_cmd->tid_tspec]; - else - ac = tid_to_mac80211_ac[0]; - - tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, hdr, info, ac) << - TX_CMD_FLG_BT_PRIO_POS; - - if (ieee80211_is_mgmt(fc)) { - if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) - tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_ASSOC); - else if (ieee80211_is_action(fc)) - tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE); - else - tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT); - - /* The spec allows Action frames in A-MPDU, we don't support - * it - */ - WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU); - } else if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) { - tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_MGMT); - } else { - tx_cmd->pm_frame_timeout = cpu_to_le16(PM_FRAME_NONE); - } - - if (ieee80211_is_data(fc) && len > mvm->rts_threshold && - !is_multicast_ether_addr(ieee80211_get_DA(hdr))) - tx_flags |= TX_CMD_FLG_PROT_REQUIRE; - - if (fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT) && - ieee80211_action_contains_tpc(skb)) - tx_flags |= TX_CMD_FLG_WRITE_TX_POWER; - - tx_cmd->tx_flags = cpu_to_le32(tx_flags); - /* Total # bytes to be transmitted */ - tx_cmd->len = cpu_to_le16((u16)skb->len); - tx_cmd->next_frame_len = 0; - tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); - tx_cmd->sta_id = sta_id; -} - -/* - * Sets the fields in the Tx cmd that are rate related - */ -void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, __le16 fc) -{ - u32 rate_flags; - int rate_idx; - u8 rate_plcp; - - /* Set retry limit on RTS packets */ - tx_cmd->rts_retry_limit = IWL_RTS_DFAULT_RETRY_LIMIT; - - /* Set retry limit on DATA packets and Probe Responses*/ - if (ieee80211_is_probe_resp(fc)) { - tx_cmd->data_retry_limit = IWL_MGMT_DFAULT_RETRY_LIMIT; - tx_cmd->rts_retry_limit = - min(tx_cmd->data_retry_limit, tx_cmd->rts_retry_limit); - } else if (ieee80211_is_back_req(fc)) { - tx_cmd->data_retry_limit = IWL_BAR_DFAULT_RETRY_LIMIT; - } else { - tx_cmd->data_retry_limit = IWL_DEFAULT_TX_RETRY; - } - - /* - * for data packets, rate info comes from the table inside the fw. This - * table is controlled by LINK_QUALITY commands - */ - - if (ieee80211_is_data(fc) && sta) { - tx_cmd->initial_rate_index = 0; - tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE); - return; - } else if (ieee80211_is_back_req(fc)) { - tx_cmd->tx_flags |= - cpu_to_le32(TX_CMD_FLG_ACK | TX_CMD_FLG_BAR); - } - - /* HT rate doesn't make sense for a non data frame */ - WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS, - "Got an HT rate (flags:0x%x/mcs:%d) for a non data frame (fc:0x%x)\n", - info->control.rates[0].flags, - info->control.rates[0].idx, - le16_to_cpu(fc)); - - rate_idx = info->control.rates[0].idx; - /* if the rate isn't a well known legacy rate, take the lowest one */ - if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT_LEGACY) - rate_idx = rate_lowest_index( - &mvm->nvm_data->bands[info->band], sta); - - /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ - if (info->band == IEEE80211_BAND_5GHZ) - rate_idx += IWL_FIRST_OFDM_RATE; - - /* For 2.4 GHZ band, check that there is no need to remap */ - BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); - - /* Get PLCP rate for tx_cmd->rate_n_flags */ - rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx); - - mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), - mvm->mgmt_last_antenna_idx); - - if (info->band == IEEE80211_BAND_2GHZ && - !iwl_mvm_bt_coex_is_shared_ant_avail(mvm)) - rate_flags = mvm->cfg->non_shared_ant << RATE_MCS_ANT_POS; - else - rate_flags = - BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS; - - /* Set CCK flag as needed */ - if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) - rate_flags |= RATE_MCS_CCK_MSK; - - /* Set the rate in the TX cmd */ - tx_cmd->rate_n_flags = cpu_to_le32((u32)rate_plcp | rate_flags); -} - -/* - * Sets the fields in the Tx cmd that are crypto related - */ -static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, - struct ieee80211_tx_info *info, - struct iwl_tx_cmd *tx_cmd, - struct sk_buff *skb_frag, - int hdrlen) -{ - struct ieee80211_key_conf *keyconf = info->control.hw_key; - u8 *crypto_hdr = skb_frag->data + hdrlen; - u64 pn; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_CCMP_256: - iwl_mvm_set_tx_cmd_ccmp(info, tx_cmd); - pn = atomic64_inc_return(&keyconf->tx_pn); - crypto_hdr[0] = pn; - crypto_hdr[2] = 0; - crypto_hdr[3] = 0x20 | (keyconf->keyidx << 6); - crypto_hdr[1] = pn >> 8; - crypto_hdr[4] = pn >> 16; - crypto_hdr[5] = pn >> 24; - crypto_hdr[6] = pn >> 32; - crypto_hdr[7] = pn >> 40; - break; - - case WLAN_CIPHER_SUITE_TKIP: - tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; - ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); - break; - - case WLAN_CIPHER_SUITE_WEP104: - tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; - /* fall through */ - case WLAN_CIPHER_SUITE_WEP40: - tx_cmd->sec_ctl |= TX_CMD_SEC_WEP | - ((keyconf->keyidx << TX_CMD_SEC_WEP_KEY_IDX_POS) & - TX_CMD_SEC_WEP_KEY_IDX_MSK); - - memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); - break; - default: - tx_cmd->sec_ctl |= TX_CMD_SEC_EXT; - } -} - -/* - * Allocates and sets the Tx cmd the driver data pointers in the skb - */ -static struct iwl_device_cmd * -iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, - int hdrlen, struct ieee80211_sta *sta, u8 sta_id) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_device_cmd *dev_cmd; - struct iwl_tx_cmd *tx_cmd; - - dev_cmd = iwl_trans_alloc_tx_cmd(mvm->trans); - - if (unlikely(!dev_cmd)) - return NULL; - - memset(dev_cmd, 0, sizeof(*dev_cmd)); - dev_cmd->hdr.cmd = TX_CMD; - tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; - - if (info->control.hw_key) - iwl_mvm_set_tx_cmd_crypto(mvm, info, tx_cmd, skb, hdrlen); - - iwl_mvm_set_tx_cmd(mvm, skb, tx_cmd, info, sta_id); - - iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control); - - memset(&info->status, 0, sizeof(info->status)); - - info->driver_data[0] = NULL; - info->driver_data[1] = dev_cmd; - - return dev_cmd; -} - -int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_device_cmd *dev_cmd; - struct iwl_tx_cmd *tx_cmd; - u8 sta_id; - int hdrlen = ieee80211_hdrlen(hdr->frame_control); - - if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU)) - return -1; - - if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && - (!info->control.vif || - info->hw_queue != info->control.vif->cab_queue))) - return -1; - - /* - * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used - * in 2 different types of vifs, P2P & STATION. P2P uses the offchannel - * queue. STATION (HS2.0) uses the auxiliary context of the FW, - * and hence needs to be sent on the aux queue - */ - if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE && - info->control.vif->type == NL80211_IFTYPE_STATION) - IEEE80211_SKB_CB(skb)->hw_queue = mvm->aux_queue; - - /* - * If the interface on which the frame is sent is the P2P_DEVICE - * or an AP/GO interface use the broadcast station associated - * with it; otherwise if the interface is a managed interface - * use the AP station associated with it for multicast traffic - * (this is not possible for unicast packets as a TLDS discovery - * response are sent without a station entry); otherwise use the - * AUX station. - */ - sta_id = mvm->aux_sta.sta_id; - if (info->control.vif) { - struct iwl_mvm_vif *mvmvif = - iwl_mvm_vif_from_mac80211(info->control.vif); - - if (info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE || - info->control.vif->type == NL80211_IFTYPE_AP) - sta_id = mvmvif->bcast_sta.sta_id; - else if (info->control.vif->type == NL80211_IFTYPE_STATION && - is_multicast_ether_addr(hdr->addr1)) { - u8 ap_sta_id = ACCESS_ONCE(mvmvif->ap_sta_id); - - if (ap_sta_id != IWL_MVM_STATION_COUNT) - sta_id = ap_sta_id; - } - } - - IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue); - - dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, NULL, sta_id); - if (!dev_cmd) - return -1; - - /* From now on, we cannot access info->control */ - tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; - - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdrlen); - - if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) { - iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); - return -1; - } - - return 0; -} - -/* - * Sets the fields in the Tx cmd that are crypto related - */ -int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, - struct ieee80211_sta *sta) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_mvm_sta *mvmsta; - struct iwl_device_cmd *dev_cmd; - struct iwl_tx_cmd *tx_cmd; - __le16 fc; - u16 seq_number = 0; - u8 tid = IWL_MAX_TID_COUNT; - u8 txq_id = info->hw_queue; - bool is_data_qos = false, is_ampdu = false; - int hdrlen; - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - fc = hdr->frame_control; - hdrlen = ieee80211_hdrlen(fc); - - if (WARN_ON_ONCE(!mvmsta)) - return -1; - - if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) - return -1; - - dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, sta, mvmsta->sta_id); - if (!dev_cmd) - goto drop; - - tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; - /* From now on, we cannot access info->control */ - - /* - * we handle that entirely ourselves -- for uAPSD the firmware - * will always send a notification, and for PS-Poll responses - * we'll notify mac80211 when getting frame status - */ - info->flags &= ~IEEE80211_TX_STATUS_EOSP; - - spin_lock(&mvmsta->lock); - - if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { - u8 *qc = NULL; - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) - goto drop_unlock_sta; - - seq_number = mvmsta->tid_data[tid].seq_number; - seq_number &= IEEE80211_SCTL_SEQ; - hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(seq_number); - is_data_qos = true; - is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU; - } - - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdrlen); - - WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); - - if (sta->tdls) { - /* default to TID 0 for non-QoS packets */ - u8 tdls_tid = tid == IWL_MAX_TID_COUNT ? 0 : tid; - - txq_id = mvmsta->hw_queue[tid_to_mac80211_ac[tdls_tid]]; - } - - if (is_ampdu) { - if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON)) - goto drop_unlock_sta; - txq_id = mvmsta->tid_data[tid].txq_id; - } - - IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id, - 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 + 0x10; - - spin_unlock(&mvmsta->lock); - - if (txq_id < mvm->first_agg_queue) - atomic_inc(&mvm->pending_frames[mvmsta->sta_id]); - - return 0; - -drop_unlock_sta: - iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); - spin_unlock(&mvmsta->lock); -drop: - return -1; -} - -static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, u8 tid) -{ - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - struct ieee80211_vif *vif = mvmsta->vif; - - lockdep_assert_held(&mvmsta->lock); - - if ((tid_data->state == IWL_AGG_ON || - tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && - iwl_mvm_tid_queued(tid_data) == 0) { - /* - * Now that this aggregation queue is empty tell mac80211 so it - * knows we no longer have frames buffered for the station on - * this TID (for the TIM bitmap calculation.) - */ - ieee80211_sta_set_buffered(sta, tid, false); - } - - if (tid_data->ssn != tid_data->next_reclaimed) - return; - - switch (tid_data->state) { - case IWL_EMPTYING_HW_QUEUE_ADDBA: - IWL_DEBUG_TX_QUEUES(mvm, - "Can continue addBA flow ssn = next_recl = %d\n", - tid_data->next_reclaimed); - tid_data->state = IWL_AGG_STARTING; - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - - case IWL_EMPTYING_HW_QUEUE_DELBA: - IWL_DEBUG_TX_QUEUES(mvm, - "Can continue DELBA flow ssn = next_recl = %d\n", - tid_data->next_reclaimed); - iwl_mvm_disable_txq(mvm, tid_data->txq_id, - vif->hw_queue[tid_to_mac80211_ac[tid]], tid, - CMD_ASYNC); - tid_data->state = IWL_AGG_OFF; - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - - default: - break; - } -} - -#ifdef CONFIG_IWLWIFI_DEBUG -const char *iwl_mvm_get_tx_fail_reason(u32 status) -{ -#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x -#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x - - switch (status & TX_STATUS_MSK) { - case TX_STATUS_SUCCESS: - return "SUCCESS"; - TX_STATUS_POSTPONE(DELAY); - TX_STATUS_POSTPONE(FEW_BYTES); - TX_STATUS_POSTPONE(BT_PRIO); - TX_STATUS_POSTPONE(QUIET_PERIOD); - TX_STATUS_POSTPONE(CALC_TTAK); - TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); - TX_STATUS_FAIL(SHORT_LIMIT); - TX_STATUS_FAIL(LONG_LIMIT); - TX_STATUS_FAIL(UNDERRUN); - TX_STATUS_FAIL(DRAIN_FLOW); - TX_STATUS_FAIL(RFKILL_FLUSH); - TX_STATUS_FAIL(LIFE_EXPIRE); - TX_STATUS_FAIL(DEST_PS); - TX_STATUS_FAIL(HOST_ABORTED); - TX_STATUS_FAIL(BT_RETRY); - TX_STATUS_FAIL(STA_INVALID); - TX_STATUS_FAIL(FRAG_DROPPED); - TX_STATUS_FAIL(TID_DISABLE); - TX_STATUS_FAIL(FIFO_FLUSHED); - TX_STATUS_FAIL(SMALL_CF_POLL); - TX_STATUS_FAIL(FW_DROP); - TX_STATUS_FAIL(STA_COLOR_MISMATCH); - } - - return "UNKNOWN"; - -#undef TX_STATUS_FAIL -#undef TX_STATUS_POSTPONE -} -#endif /* CONFIG_IWLWIFI_DEBUG */ - -void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, - enum ieee80211_band band, - struct ieee80211_tx_rate *r) -{ - if (rate_n_flags & RATE_HT_MCS_GF_MSK) - r->flags |= IEEE80211_TX_RC_GREEN_FIELD; - switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { - case RATE_MCS_CHAN_WIDTH_20: - break; - case RATE_MCS_CHAN_WIDTH_40: - r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - break; - case RATE_MCS_CHAN_WIDTH_80: - r->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; - break; - case RATE_MCS_CHAN_WIDTH_160: - r->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH; - break; - } - if (rate_n_flags & RATE_MCS_SGI_MSK) - r->flags |= IEEE80211_TX_RC_SHORT_GI; - if (rate_n_flags & RATE_MCS_HT_MSK) { - r->flags |= IEEE80211_TX_RC_MCS; - r->idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; - } else if (rate_n_flags & RATE_MCS_VHT_MSK) { - ieee80211_rate_set_vht( - r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK, - ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> - RATE_VHT_MCS_NSS_POS) + 1); - r->flags |= IEEE80211_TX_RC_VHT_MCS; - } else { - r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, - band); - } -} - -/** - * translate ucode response to mac80211 tx status control values - */ -static void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags, - struct ieee80211_tx_info *info) -{ - struct ieee80211_tx_rate *r = &info->status.rates[0]; - - info->status.antenna = - ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); - iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r); -} - -static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct ieee80211_sta *sta; - u16 sequence = le16_to_cpu(pkt->hdr.sequence); - int txq_id = SEQ_TO_QUEUE(sequence); - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; - int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); - int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); - u32 status = le16_to_cpu(tx_resp->status.status); - u16 ssn = iwl_mvm_get_scd_ssn(tx_resp); - struct iwl_mvm_sta *mvmsta; - struct sk_buff_head skbs; - u8 skb_freed = 0; - u16 next_reclaimed, seq_ctl; - - __skb_queue_head_init(&skbs); - - seq_ctl = le16_to_cpu(tx_resp->seq_ctl); - - /* we can free until ssn % q.n_bd not inclusive */ - iwl_trans_reclaim(mvm->trans, txq_id, ssn, &skbs); - - while (!skb_queue_empty(&skbs)) { - struct sk_buff *skb = __skb_dequeue(&skbs); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - skb_freed++; - - iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); - - memset(&info->status, 0, sizeof(info->status)); - - info->flags &= ~IEEE80211_TX_CTL_AMPDU; - - /* inform mac80211 about what happened with the frame */ - switch (status & TX_STATUS_MSK) { - case TX_STATUS_SUCCESS: - case TX_STATUS_DIRECT_DONE: - info->flags |= IEEE80211_TX_STAT_ACK; - break; - case TX_STATUS_FAIL_DEST_PS: - info->flags |= IEEE80211_TX_STAT_TX_FILTERED; - break; - default: - break; - } - - info->status.rates[0].count = tx_resp->failure_frame + 1; - iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate), - info); - info->status.status_driver_data[1] = - (void *)(uintptr_t)le32_to_cpu(tx_resp->initial_rate); - - /* Single frame failure in an AMPDU queue => send BAR */ - if (txq_id >= mvm->first_agg_queue && - !(info->flags & IEEE80211_TX_STAT_ACK) && - !(info->flags & IEEE80211_TX_STAT_TX_FILTERED)) - info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; - - /* W/A FW bug: seq_ctl is wrong when the status isn't success */ - if (status != TX_STATUS_SUCCESS) { - struct ieee80211_hdr *hdr = (void *)skb->data; - seq_ctl = le16_to_cpu(hdr->seq_ctrl); - } - - /* - * TODO: this is not accurate if we are freeing more than one - * packet. - */ - info->status.tx_time = - le16_to_cpu(tx_resp->wireless_media_time); - BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1); - info->status.status_driver_data[0] = - (void *)(uintptr_t)tx_resp->reduced_tpc; - - ieee80211_tx_status(mvm->hw, skb); - } - - if (txq_id >= mvm->first_agg_queue) { - /* If this is an aggregation queue, we use the ssn since: - * ssn = wifi seq_num % 256. - * The seq_ctl is the sequence control of the packet to which - * this Tx response relates. But if there is a hole in the - * bitmap of the BA we received, this Tx response may allow to - * reclaim the hole and all the subsequent packets that were - * already acked. In that case, seq_ctl != ssn, and the next - * packet to be reclaimed will be ssn and not seq_ctl. In that - * case, several packets will be reclaimed even if - * frame_count = 1. - * - * The ssn is the index (% 256) of the latest packet that has - * treated (acked / dropped) + 1. - */ - next_reclaimed = ssn; - } else { - /* The next packet to be reclaimed is the one after this one */ - next_reclaimed = IEEE80211_SEQ_TO_SN(seq_ctl + 0x10); - } - - IWL_DEBUG_TX_REPLY(mvm, - "TXQ %d status %s (0x%08x)\n", - txq_id, iwl_mvm_get_tx_fail_reason(status), status); - - IWL_DEBUG_TX_REPLY(mvm, - "\t\t\t\tinitial_rate 0x%x retries %d, idx=%d ssn=%d next_reclaimed=0x%x seq_ctl=0x%x\n", - le32_to_cpu(tx_resp->initial_rate), - tx_resp->failure_frame, SEQ_TO_INDEX(sequence), - ssn, next_reclaimed, seq_ctl); - - rcu_read_lock(); - - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - /* - * sta can't be NULL otherwise it'd mean that the sta has been freed in - * the firmware while we still have packets for it in the Tx queues. - */ - if (WARN_ON_ONCE(!sta)) - goto out; - - if (!IS_ERR(sta)) { - mvmsta = iwl_mvm_sta_from_mac80211(sta); - - if (tid != IWL_TID_NON_QOS) { - struct iwl_mvm_tid_data *tid_data = - &mvmsta->tid_data[tid]; - - spin_lock_bh(&mvmsta->lock); - tid_data->next_reclaimed = next_reclaimed; - IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n", - next_reclaimed); - iwl_mvm_check_ratid_empty(mvm, sta, tid); - spin_unlock_bh(&mvmsta->lock); - } - - if (mvmsta->next_status_eosp) { - mvmsta->next_status_eosp = false; - ieee80211_sta_eosp(sta); - } - } else { - mvmsta = NULL; - } - - /* - * If the txq is not an AMPDU queue, there is no chance we freed - * several skbs. Check that out... - */ - if (txq_id >= mvm->first_agg_queue) - goto out; - - /* We can't free more than one frame at once on a shared queue */ - WARN_ON(skb_freed > 1); - - /* If we have still frames for this STA nothing to do here */ - if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) - goto out; - - if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) { - - /* - * If there are no pending frames for this STA and - * the tx to this station is not disabled, notify - * mac80211 that this station can now wake up in its - * STA table. - * If mvmsta is not NULL, sta is valid. - */ - - spin_lock_bh(&mvmsta->lock); - - if (!mvmsta->disable_tx) - ieee80211_sta_block_awake(mvm->hw, sta, false); - - spin_unlock_bh(&mvmsta->lock); - } - - if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) { - /* - * We are draining and this was the last packet - pre_rcu_remove - * has been called already. We might be after the - * synchronize_net already. - * Don't rely on iwl_mvm_rm_sta to see the empty Tx queues. - */ - set_bit(sta_id, mvm->sta_drained); - schedule_work(&mvm->sta_drained_wk); - } - -out: - rcu_read_unlock(); -} - -#ifdef CONFIG_IWLWIFI_DEBUG -#define AGG_TX_STATE_(x) case AGG_TX_STATE_ ## x: return #x -static const char *iwl_get_agg_tx_status(u16 status) -{ - switch (status & AGG_TX_STATE_STATUS_MSK) { - AGG_TX_STATE_(TRANSMITTED); - AGG_TX_STATE_(UNDERRUN); - AGG_TX_STATE_(BT_PRIO); - AGG_TX_STATE_(FEW_BYTES); - AGG_TX_STATE_(ABORT); - AGG_TX_STATE_(LAST_SENT_TTL); - AGG_TX_STATE_(LAST_SENT_TRY_CNT); - AGG_TX_STATE_(LAST_SENT_BT_KILL); - AGG_TX_STATE_(SCD_QUERY); - AGG_TX_STATE_(TEST_BAD_CRC32); - AGG_TX_STATE_(RESPONSE); - AGG_TX_STATE_(DUMP_TX); - AGG_TX_STATE_(DELAY_TX); - } - - return "UNKNOWN"; -} - -static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; - struct agg_tx_status *frame_status = &tx_resp->status; - int i; - - for (i = 0; i < tx_resp->frame_count; i++) { - u16 fstatus = le16_to_cpu(frame_status[i].status); - - IWL_DEBUG_TX_REPLY(mvm, - "status %s (0x%04x), try-count (%d) seq (0x%x)\n", - iwl_get_agg_tx_status(fstatus), - fstatus & AGG_TX_STATE_STATUS_MSK, - (fstatus & AGG_TX_STATE_TRY_CNT_MSK) >> - AGG_TX_STATE_TRY_CNT_POS, - le16_to_cpu(frame_status[i].sequence)); - } -} -#else -static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{} -#endif /* CONFIG_IWLWIFI_DEBUG */ - -static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, - struct iwl_rx_packet *pkt) -{ - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; - int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); - int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); - u16 sequence = le16_to_cpu(pkt->hdr.sequence); - struct ieee80211_sta *sta; - - if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < mvm->first_agg_queue)) - return; - - if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS)) - return; - - iwl_mvm_rx_tx_cmd_agg_dbg(mvm, pkt); - - rcu_read_lock(); - - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - - if (!WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - mvmsta->tid_data[tid].rate_n_flags = - le32_to_cpu(tx_resp->initial_rate); - mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc; - mvmsta->tid_data[tid].tx_time = - le16_to_cpu(tx_resp->wireless_media_time); - } - - rcu_read_unlock(); -} - -void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; - - if (tx_resp->frame_count == 1) - iwl_mvm_rx_tx_cmd_single(mvm, pkt); - else - iwl_mvm_rx_tx_cmd_agg(mvm, pkt); -} - -static void iwl_mvm_tx_info_from_ba_notif(struct ieee80211_tx_info *info, - struct iwl_mvm_ba_notif *ba_notif, - struct iwl_mvm_tid_data *tid_data) -{ - info->flags |= IEEE80211_TX_STAT_AMPDU; - info->status.ampdu_ack_len = ba_notif->txed_2_done; - info->status.ampdu_len = ba_notif->txed; - iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags, - info); - /* TODO: not accounted if the whole A-MPDU failed */ - info->status.tx_time = tid_data->tx_time; - info->status.status_driver_data[0] = - (void *)(uintptr_t)tid_data->reduced_tpc; - info->status.status_driver_data[1] = - (void *)(uintptr_t)tid_data->rate_n_flags; -} - -void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm_ba_notif *ba_notif = (void *)pkt->data; - struct sk_buff_head reclaimed_skbs; - struct iwl_mvm_tid_data *tid_data; - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - struct sk_buff *skb; - int sta_id, tid, freed; - /* "flow" corresponds to Tx queue */ - u16 scd_flow = le16_to_cpu(ba_notif->scd_flow); - /* "ssn" is start of block-ack Tx window, corresponds to index - * (in Tx queue's circular buffer) of first TFD/frame in window */ - u16 ba_resp_scd_ssn = le16_to_cpu(ba_notif->scd_ssn); - - sta_id = ba_notif->sta_id; - tid = ba_notif->tid; - - if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT || - tid >= IWL_MAX_TID_COUNT, - "sta_id %d tid %d", sta_id, tid)) - return; - - rcu_read_lock(); - - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - - /* Reclaiming frames for a station that has been deleted ? */ - if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { - rcu_read_unlock(); - return; - } - - mvmsta = iwl_mvm_sta_from_mac80211(sta); - tid_data = &mvmsta->tid_data[tid]; - - if (tid_data->txq_id != scd_flow) { - IWL_ERR(mvm, - "invalid BA notification: Q %d, tid %d, flow %d\n", - tid_data->txq_id, tid, scd_flow); - rcu_read_unlock(); - return; - } - - spin_lock_bh(&mvmsta->lock); - - __skb_queue_head_init(&reclaimed_skbs); - - /* - * Release all TFDs before the SSN, i.e. all TFDs in front of - * block-ack window (we assume that they've been successfully - * transmitted ... if not, it's too late anyway). - */ - iwl_trans_reclaim(mvm->trans, scd_flow, ba_resp_scd_ssn, - &reclaimed_skbs); - - IWL_DEBUG_TX_REPLY(mvm, - "BA_NOTIFICATION Received from %pM, sta_id = %d\n", - (u8 *)&ba_notif->sta_addr_lo32, - ba_notif->sta_id); - IWL_DEBUG_TX_REPLY(mvm, - "TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n", - ba_notif->tid, le16_to_cpu(ba_notif->seq_ctl), - (unsigned long long)le64_to_cpu(ba_notif->bitmap), - scd_flow, ba_resp_scd_ssn, ba_notif->txed, - ba_notif->txed_2_done); - - tid_data->next_reclaimed = ba_resp_scd_ssn; - - iwl_mvm_check_ratid_empty(mvm, sta, tid); - - freed = 0; - - skb_queue_walk(&reclaimed_skbs, skb) { - struct ieee80211_hdr *hdr = (void *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - if (ieee80211_is_data_qos(hdr->frame_control)) - freed++; - else - WARN_ON_ONCE(1); - - iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); - - memset(&info->status, 0, sizeof(info->status)); - /* Packet was transmitted successfully, failures come as single - * frames because before failing a frame the firmware transmits - * it without aggregation at least once. - */ - info->flags |= IEEE80211_TX_STAT_ACK; - - /* this is the first skb we deliver in this batch */ - /* put the rate scaling data there */ - if (freed == 1) - iwl_mvm_tx_info_from_ba_notif(info, ba_notif, tid_data); - } - - spin_unlock_bh(&mvmsta->lock); - - /* We got a BA notif with 0 acked or scd_ssn didn't progress which is - * possible (i.e. first MPDU in the aggregation wasn't acked) - * Still it's important to update RS about sent vs. acked. - */ - if (skb_queue_empty(&reclaimed_skbs)) { - struct ieee80211_tx_info ba_info = {}; - struct ieee80211_chanctx_conf *chanctx_conf = NULL; - - if (mvmsta->vif) - chanctx_conf = - rcu_dereference(mvmsta->vif->chanctx_conf); - - if (WARN_ON_ONCE(!chanctx_conf)) - goto out; - - ba_info.band = chanctx_conf->def.chan->band; - iwl_mvm_tx_info_from_ba_notif(&ba_info, ba_notif, tid_data); - - IWL_DEBUG_TX_REPLY(mvm, "No reclaim. Update rs directly\n"); - iwl_mvm_rs_tx_status(mvm, sta, tid, &ba_info); - } - -out: - rcu_read_unlock(); - - while (!skb_queue_empty(&reclaimed_skbs)) { - skb = __skb_dequeue(&reclaimed_skbs); - ieee80211_tx_status(mvm->hw, skb); - } -} - -/* - * Note that there are transports that buffer frames before they reach - * the firmware. This means that after flush_tx_path is called, the - * queue might not be empty. The race-free way to handle this is to: - * 1) set the station as draining - * 2) flush the Tx path - * 3) wait for the transport queues to be empty - */ -int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags) -{ - int ret; - struct iwl_tx_path_flush_cmd flush_cmd = { - .queues_ctl = cpu_to_le32(tfd_msk), - .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH), - }; - - ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags, - sizeof(flush_cmd), &flush_cmd); - if (ret) - IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret); - return ret; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c deleted file mode 100644 index ad0f16909e2e..000000000000 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ /dev/null @@ -1,1083 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright (C) 2015 Intel Deutschland GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include - -#include "iwl-debug.h" -#include "iwl-io.h" -#include "iwl-prph.h" - -#include "mvm.h" -#include "fw-api-rs.h" - -/* - * Will return 0 even if the cmd failed when RFKILL is asserted unless - * CMD_WANT_SKB is set in cmd->flags. - */ -int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd) -{ - int ret; - -#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) - if (WARN_ON(mvm->d3_test_active)) - return -EIO; -#endif - - /* - * Synchronous commands from this op-mode must hold - * the mutex, this ensures we don't try to send two - * (or more) synchronous commands at a time. - */ - if (!(cmd->flags & CMD_ASYNC)) - lockdep_assert_held(&mvm->mutex); - - ret = iwl_trans_send_cmd(mvm->trans, cmd); - - /* - * If the caller wants the SKB, then don't hide any problems, the - * caller might access the response buffer which will be NULL if - * the command failed. - */ - if (cmd->flags & CMD_WANT_SKB) - return ret; - - /* Silently ignore failures if RFKILL is asserted */ - if (!ret || ret == -ERFKILL) - return 0; - return ret; -} - -int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u32 id, - u32 flags, u16 len, const void *data) -{ - struct iwl_host_cmd cmd = { - .id = id, - .len = { len, }, - .data = { data, }, - .flags = flags, - }; - - return iwl_mvm_send_cmd(mvm, &cmd); -} - -/* - * We assume that the caller set the status to the success value - */ -int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, - u32 *status) -{ - struct iwl_rx_packet *pkt; - struct iwl_cmd_response *resp; - int ret, resp_len; - - lockdep_assert_held(&mvm->mutex); - -#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) - if (WARN_ON(mvm->d3_test_active)) - return -EIO; -#endif - - /* - * Only synchronous commands can wait for status, - * we use WANT_SKB so the caller can't. - */ - if (WARN_ONCE(cmd->flags & (CMD_ASYNC | CMD_WANT_SKB), - "cmd flags %x", cmd->flags)) - return -EINVAL; - - cmd->flags |= CMD_WANT_SKB; - - ret = iwl_trans_send_cmd(mvm->trans, cmd); - if (ret == -ERFKILL) { - /* - * The command failed because of RFKILL, don't update - * the status, leave it as success and return 0. - */ - return 0; - } else if (ret) { - return ret; - } - - pkt = cmd->resp_pkt; - /* Can happen if RFKILL is asserted */ - if (!pkt) { - ret = 0; - goto out_free_resp; - } - - resp_len = iwl_rx_packet_payload_len(pkt); - if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { - ret = -EIO; - goto out_free_resp; - } - - resp = (void *)pkt->data; - *status = le32_to_cpu(resp->status); - out_free_resp: - iwl_free_resp(cmd); - return ret; -} - -/* - * We assume that the caller set the status to the sucess value - */ -int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len, - const void *data, u32 *status) -{ - struct iwl_host_cmd cmd = { - .id = id, - .len = { len, }, - .data = { data, }, - }; - - return iwl_mvm_send_cmd_status(mvm, &cmd, status); -} - -#define IWL_DECLARE_RATE_INFO(r) \ - [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP - -/* - * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP - */ -static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = { - IWL_DECLARE_RATE_INFO(1), - IWL_DECLARE_RATE_INFO(2), - IWL_DECLARE_RATE_INFO(5), - IWL_DECLARE_RATE_INFO(11), - IWL_DECLARE_RATE_INFO(6), - IWL_DECLARE_RATE_INFO(9), - IWL_DECLARE_RATE_INFO(12), - IWL_DECLARE_RATE_INFO(18), - IWL_DECLARE_RATE_INFO(24), - IWL_DECLARE_RATE_INFO(36), - IWL_DECLARE_RATE_INFO(48), - IWL_DECLARE_RATE_INFO(54), -}; - -int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, - enum ieee80211_band band) -{ - int rate = rate_n_flags & RATE_LEGACY_RATE_MSK; - int idx; - int band_offset = 0; - - /* Legacy rate format, search for match in table */ - if (band == IEEE80211_BAND_5GHZ) - band_offset = IWL_FIRST_OFDM_RATE; - for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) - if (fw_rate_idx_to_plcp[idx] == rate) - return idx - band_offset; - - return -1; -} - -u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx) -{ - /* Get PLCP rate for tx_cmd->rate_n_flags */ - return fw_rate_idx_to_plcp[rate_idx]; -} - -void iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_error_resp *err_resp = (void *)pkt->data; - - IWL_ERR(mvm, "FW Error notification: type 0x%08X cmd_id 0x%02X\n", - le32_to_cpu(err_resp->error_type), err_resp->cmd_id); - IWL_ERR(mvm, "FW Error notification: seq 0x%04X service 0x%08X\n", - le16_to_cpu(err_resp->bad_cmd_seq_num), - le32_to_cpu(err_resp->error_service)); - IWL_ERR(mvm, "FW Error notification: timestamp 0x%16llX\n", - le64_to_cpu(err_resp->timestamp)); -} - -/* - * Returns the first antenna as ANT_[ABC], as defined in iwl-config.h. - * The parameter should also be a combination of ANT_[ABC]. - */ -u8 first_antenna(u8 mask) -{ - BUILD_BUG_ON(ANT_A != BIT(0)); /* using ffs is wrong if not */ - if (WARN_ON_ONCE(!mask)) /* ffs will return 0 if mask is zeroed */ - return BIT(0); - return BIT(ffs(mask) - 1); -} - -/* - * Toggles between TX antennas to send the probe request on. - * Receives the bitmask of valid TX antennas and the *index* used - * for the last TX, and returns the next valid *index* to use. - * In order to set it in the tx_cmd, must do BIT(idx). - */ -u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx) -{ - u8 ind = last_idx; - int i; - - for (i = 0; i < RATE_MCS_ANT_NUM; i++) { - ind = (ind + 1) % RATE_MCS_ANT_NUM; - if (valid & BIT(ind)) - return ind; - } - - WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid); - return last_idx; -} - -static const struct { - const char *name; - u8 num; -} advanced_lookup[] = { - { "NMI_INTERRUPT_WDG", 0x34 }, - { "SYSASSERT", 0x35 }, - { "UCODE_VERSION_MISMATCH", 0x37 }, - { "BAD_COMMAND", 0x38 }, - { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, - { "FATAL_ERROR", 0x3D }, - { "NMI_TRM_HW_ERR", 0x46 }, - { "NMI_INTERRUPT_TRM", 0x4C }, - { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, - { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, - { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, - { "NMI_INTERRUPT_HOST", 0x66 }, - { "NMI_INTERRUPT_ACTION_PT", 0x7C }, - { "NMI_INTERRUPT_UNKNOWN", 0x84 }, - { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, - { "ADVANCED_SYSASSERT", 0 }, -}; - -static const char *desc_lookup(u32 num) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(advanced_lookup) - 1; i++) - if (advanced_lookup[i].num == num) - return advanced_lookup[i].name; - - /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */ - return advanced_lookup[i].name; -} - -/* - * Note: This structure is read from the device with IO accesses, - * and the reading already does the endian conversion. As it is - * read with u32-sized accesses, any members with a different size - * need to be ordered correctly though! - */ -struct iwl_error_event_table_v1 { - u32 valid; /* (nonzero) valid, (0) log is empty */ - u32 error_id; /* type of error */ - u32 pc; /* program counter */ - u32 blink1; /* branch link */ - u32 blink2; /* branch link */ - u32 ilink1; /* interrupt link */ - u32 ilink2; /* interrupt link */ - u32 data1; /* error-specific data */ - u32 data2; /* error-specific data */ - u32 data3; /* error-specific data */ - u32 bcon_time; /* beacon timer */ - u32 tsf_low; /* network timestamp function timer */ - u32 tsf_hi; /* network timestamp function timer */ - u32 gp1; /* GP1 timer register */ - u32 gp2; /* GP2 timer register */ - u32 gp3; /* GP3 timer register */ - u32 ucode_ver; /* uCode version */ - u32 hw_ver; /* HW Silicon version */ - u32 brd_ver; /* HW board version */ - u32 log_pc; /* log program counter */ - u32 frame_ptr; /* frame pointer */ - u32 stack_ptr; /* stack pointer */ - u32 hcmd; /* last host command header */ - u32 isr0; /* isr status register LMPM_NIC_ISR0: - * rxtx_flag */ - u32 isr1; /* isr status register LMPM_NIC_ISR1: - * host_flag */ - u32 isr2; /* isr status register LMPM_NIC_ISR2: - * enc_flag */ - u32 isr3; /* isr status register LMPM_NIC_ISR3: - * time_flag */ - u32 isr4; /* isr status register LMPM_NIC_ISR4: - * wico interrupt */ - u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ - u32 wait_event; /* wait event() caller address */ - u32 l2p_control; /* L2pControlField */ - u32 l2p_duration; /* L2pDurationField */ - u32 l2p_mhvalid; /* L2pMhValidBits */ - u32 l2p_addr_match; /* L2pAddrMatchStat */ - u32 lmpm_pmg_sel; /* indicate which clocks are turned on - * (LMPM_PMG_SEL) */ - u32 u_timestamp; /* indicate when the date and time of the - * compilation */ - u32 flow_handler; /* FH read/write pointers, RX credit */ -} __packed /* LOG_ERROR_TABLE_API_S_VER_1 */; - -struct iwl_error_event_table { - u32 valid; /* (nonzero) valid, (0) log is empty */ - u32 error_id; /* type of error */ - u32 pc; /* program counter */ - u32 blink1; /* branch link */ - u32 blink2; /* branch link */ - u32 ilink1; /* interrupt link */ - u32 ilink2; /* interrupt link */ - u32 data1; /* error-specific data */ - u32 data2; /* error-specific data */ - u32 data3; /* error-specific data */ - u32 bcon_time; /* beacon timer */ - u32 tsf_low; /* network timestamp function timer */ - u32 tsf_hi; /* network timestamp function timer */ - u32 gp1; /* GP1 timer register */ - u32 gp2; /* GP2 timer register */ - u32 gp3; /* GP3 timer register */ - u32 major; /* uCode version major */ - u32 minor; /* uCode version minor */ - u32 hw_ver; /* HW Silicon version */ - u32 brd_ver; /* HW board version */ - u32 log_pc; /* log program counter */ - u32 frame_ptr; /* frame pointer */ - u32 stack_ptr; /* stack pointer */ - u32 hcmd; /* last host command header */ - u32 isr0; /* isr status register LMPM_NIC_ISR0: - * rxtx_flag */ - u32 isr1; /* isr status register LMPM_NIC_ISR1: - * host_flag */ - u32 isr2; /* isr status register LMPM_NIC_ISR2: - * enc_flag */ - u32 isr3; /* isr status register LMPM_NIC_ISR3: - * time_flag */ - u32 isr4; /* isr status register LMPM_NIC_ISR4: - * wico interrupt */ - u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ - u32 wait_event; /* wait event() caller address */ - u32 l2p_control; /* L2pControlField */ - u32 l2p_duration; /* L2pDurationField */ - u32 l2p_mhvalid; /* L2pMhValidBits */ - u32 l2p_addr_match; /* L2pAddrMatchStat */ - u32 lmpm_pmg_sel; /* indicate which clocks are turned on - * (LMPM_PMG_SEL) */ - u32 u_timestamp; /* indicate when the date and time of the - * compilation */ - u32 flow_handler; /* FH read/write pointers, RX credit */ -} __packed /* LOG_ERROR_TABLE_API_S_VER_2 */; - -/* - * UMAC error struct - relevant starting from family 8000 chip. - * Note: This structure is read from the device with IO accesses, - * and the reading already does the endian conversion. As it is - * read with u32-sized accesses, any members with a different size - * need to be ordered correctly though! - */ -struct iwl_umac_error_event_table { - u32 valid; /* (nonzero) valid, (0) log is empty */ - u32 error_id; /* type of error */ - u32 blink1; /* branch link */ - u32 blink2; /* branch link */ - u32 ilink1; /* interrupt link */ - u32 ilink2; /* interrupt link */ - u32 data1; /* error-specific data */ - u32 data2; /* error-specific data */ - u32 data3; /* error-specific data */ - u32 umac_major; - u32 umac_minor; - u32 frame_pointer; /* core register 27*/ - u32 stack_pointer; /* core register 28 */ - u32 cmd_header; /* latest host cmd sent to UMAC */ - u32 nic_isr_pref; /* ISR status register */ -} __packed; - -#define ERROR_START_OFFSET (1 * sizeof(u32)) -#define ERROR_ELEM_SIZE (7 * sizeof(u32)) - -static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm) -{ - struct iwl_trans *trans = mvm->trans; - struct iwl_umac_error_event_table table; - u32 base; - - base = mvm->umac_error_event_table; - - if (base < 0x800000) { - IWL_ERR(mvm, - "Not valid error log pointer 0x%08X for %s uCode\n", - base, - (mvm->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); - return; - } - - iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); - - if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { - IWL_ERR(trans, "Start IWL Error Log Dump:\n"); - IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", - mvm->status, table.valid); - } - - IWL_ERR(mvm, "0x%08X | %s\n", table.error_id, - desc_lookup(table.error_id)); - IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1); - IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2); - IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1); - IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2); - IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1); - IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2); - IWL_ERR(mvm, "0x%08X | umac data3\n", table.data3); - IWL_ERR(mvm, "0x%08X | umac major\n", table.umac_major); - IWL_ERR(mvm, "0x%08X | umac minor\n", table.umac_minor); - IWL_ERR(mvm, "0x%08X | frame pointer\n", table.frame_pointer); - IWL_ERR(mvm, "0x%08X | stack pointer\n", table.stack_pointer); - IWL_ERR(mvm, "0x%08X | last host cmd\n", table.cmd_header); - IWL_ERR(mvm, "0x%08X | isr status reg\n", table.nic_isr_pref); -} - -static void iwl_mvm_dump_nic_error_log_old(struct iwl_mvm *mvm) -{ - struct iwl_trans *trans = mvm->trans; - struct iwl_error_event_table_v1 table; - u32 base; - - base = mvm->error_event_table; - if (mvm->cur_ucode == IWL_UCODE_INIT) { - if (!base) - base = mvm->fw->init_errlog_ptr; - } else { - if (!base) - base = mvm->fw->inst_errlog_ptr; - } - - if (base < 0x800000) { - IWL_ERR(mvm, - "Not valid error log pointer 0x%08X for %s uCode\n", - base, - (mvm->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); - return; - } - - iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); - - if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { - IWL_ERR(trans, "Start IWL Error Log Dump:\n"); - IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", - mvm->status, table.valid); - } - - /* Do not change this output - scripts rely on it */ - - IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version); - - trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, - table.data1, table.data2, table.data3, - table.blink1, table.blink2, table.ilink1, - table.ilink2, table.bcon_time, table.gp1, - table.gp2, table.gp3, table.ucode_ver, 0, - table.hw_ver, table.brd_ver); - IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, - desc_lookup(table.error_id)); - IWL_ERR(mvm, "0x%08X | uPc\n", table.pc); - IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1); - IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2); - IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1); - IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2); - IWL_ERR(mvm, "0x%08X | data1\n", table.data1); - IWL_ERR(mvm, "0x%08X | data2\n", table.data2); - IWL_ERR(mvm, "0x%08X | data3\n", table.data3); - IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time); - IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low); - IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi); - IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1); - IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2); - IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3); - IWL_ERR(mvm, "0x%08X | uCode version\n", table.ucode_ver); - IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver); - IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver); - IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd); - IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0); - IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1); - IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2); - IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3); - IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4); - IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref); - IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event); - IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control); - IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration); - IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); - IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); - IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); - IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp); - IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler); - - if (mvm->support_umac_log) - iwl_mvm_dump_umac_error_log(mvm); -} - -void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) -{ - struct iwl_trans *trans = mvm->trans; - struct iwl_error_event_table table; - u32 base; - - if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION)) { - iwl_mvm_dump_nic_error_log_old(mvm); - return; - } - - base = mvm->error_event_table; - if (mvm->cur_ucode == IWL_UCODE_INIT) { - if (!base) - base = mvm->fw->init_errlog_ptr; - } else { - if (!base) - base = mvm->fw->inst_errlog_ptr; - } - - if (base < 0x800000) { - IWL_ERR(mvm, - "Not valid error log pointer 0x%08X for %s uCode\n", - base, - (mvm->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); - return; - } - - iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); - - if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { - IWL_ERR(trans, "Start IWL Error Log Dump:\n"); - IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", - mvm->status, table.valid); - } - - /* Do not change this output - scripts rely on it */ - - IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version); - - trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, - table.data1, table.data2, table.data3, - table.blink1, table.blink2, table.ilink1, - table.ilink2, table.bcon_time, table.gp1, - table.gp2, table.gp3, table.major, - table.minor, table.hw_ver, table.brd_ver); - IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, - desc_lookup(table.error_id)); - IWL_ERR(mvm, "0x%08X | uPc\n", table.pc); - IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1); - IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2); - IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1); - IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2); - IWL_ERR(mvm, "0x%08X | data1\n", table.data1); - IWL_ERR(mvm, "0x%08X | data2\n", table.data2); - IWL_ERR(mvm, "0x%08X | data3\n", table.data3); - IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time); - IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low); - IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi); - IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1); - IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2); - IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3); - IWL_ERR(mvm, "0x%08X | uCode version major\n", table.major); - IWL_ERR(mvm, "0x%08X | uCode version minor\n", table.minor); - IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver); - IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver); - IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd); - IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0); - IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1); - IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2); - IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3); - IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4); - IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref); - IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event); - IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control); - IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration); - IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); - IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); - IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); - IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp); - IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler); - - if (mvm->support_umac_log) - iwl_mvm_dump_umac_error_log(mvm); -} - -int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq) -{ - int i; - - lockdep_assert_held(&mvm->queue_info_lock); - - for (i = minq; i <= maxq; i++) - if (mvm->queue_info[i].hw_queue_refcount == 0 && - !mvm->queue_info[i].setup_reserved) - return i; - - return -ENOSPC; -} - -void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg, - unsigned int wdg_timeout) -{ - bool enable_queue = true; - - spin_lock_bh(&mvm->queue_info_lock); - - /* Make sure this TID isn't already enabled */ - if (mvm->queue_info[queue].tid_bitmap & BIT(cfg->tid)) { - spin_unlock_bh(&mvm->queue_info_lock); - IWL_ERR(mvm, "Trying to enable TXQ with existing TID %d\n", - cfg->tid); - return; - } - - /* Update mappings and refcounts */ - mvm->queue_info[queue].hw_queue_to_mac80211 |= BIT(mac80211_queue); - mvm->queue_info[queue].hw_queue_refcount++; - if (mvm->queue_info[queue].hw_queue_refcount > 1) - enable_queue = false; - mvm->queue_info[queue].tid_bitmap |= BIT(cfg->tid); - - IWL_DEBUG_TX_QUEUES(mvm, - "Enabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n", - queue, mvm->queue_info[queue].hw_queue_refcount, - mvm->queue_info[queue].hw_queue_to_mac80211); - - spin_unlock_bh(&mvm->queue_info_lock); - - /* Send the enabling command if we need to */ - if (enable_queue) { - struct iwl_scd_txq_cfg_cmd cmd = { - .scd_queue = queue, - .enable = 1, - .window = cfg->frame_limit, - .sta_id = cfg->sta_id, - .ssn = cpu_to_le16(ssn), - .tx_fifo = cfg->fifo, - .aggregate = cfg->aggregate, - .tid = cfg->tid, - }; - - iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, - wdg_timeout); - WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), - &cmd), - "Failed to configure queue %d on FIFO %d\n", queue, - cfg->fifo); - } -} - -void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, - u8 tid, u8 flags) -{ - struct iwl_scd_txq_cfg_cmd cmd = { - .scd_queue = queue, - .enable = 0, - }; - bool remove_mac_queue = true; - int ret; - - spin_lock_bh(&mvm->queue_info_lock); - - if (WARN_ON(mvm->queue_info[queue].hw_queue_refcount == 0)) { - spin_unlock_bh(&mvm->queue_info_lock); - return; - } - - mvm->queue_info[queue].tid_bitmap &= ~BIT(tid); - - /* - * If there is another TID with the same AC - don't remove the MAC queue - * from the mapping - */ - if (tid < IWL_MAX_TID_COUNT) { - unsigned long tid_bitmap = - mvm->queue_info[queue].tid_bitmap; - int ac = tid_to_mac80211_ac[tid]; - int i; - - for_each_set_bit(i, &tid_bitmap, IWL_MAX_TID_COUNT) { - if (tid_to_mac80211_ac[i] == ac) - remove_mac_queue = false; - } - } - - if (remove_mac_queue) - mvm->queue_info[queue].hw_queue_to_mac80211 &= - ~BIT(mac80211_queue); - mvm->queue_info[queue].hw_queue_refcount--; - - cmd.enable = mvm->queue_info[queue].hw_queue_refcount ? 1 : 0; - - IWL_DEBUG_TX_QUEUES(mvm, - "Disabling TXQ #%d refcount=%d (mac80211 map:0x%x)\n", - queue, - mvm->queue_info[queue].hw_queue_refcount, - mvm->queue_info[queue].hw_queue_to_mac80211); - - /* If the queue is still enabled - nothing left to do in this func */ - if (cmd.enable) { - spin_unlock_bh(&mvm->queue_info_lock); - return; - } - - /* Make sure queue info is correct even though we overwrite it */ - WARN(mvm->queue_info[queue].hw_queue_refcount || - mvm->queue_info[queue].tid_bitmap || - mvm->queue_info[queue].hw_queue_to_mac80211, - "TXQ #%d info out-of-sync - refcount=%d, mac map=0x%x, tid=0x%x\n", - queue, mvm->queue_info[queue].hw_queue_refcount, - mvm->queue_info[queue].hw_queue_to_mac80211, - mvm->queue_info[queue].tid_bitmap); - - /* If we are here - the queue is freed and we can zero out these vals */ - mvm->queue_info[queue].hw_queue_refcount = 0; - mvm->queue_info[queue].tid_bitmap = 0; - mvm->queue_info[queue].hw_queue_to_mac80211 = 0; - - spin_unlock_bh(&mvm->queue_info_lock); - - iwl_trans_txq_disable(mvm->trans, queue, false); - ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, - sizeof(cmd), &cmd); - if (ret) - IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n", - queue, ret); -} - -/** - * iwl_mvm_send_lq_cmd() - Send link quality command - * @init: This command is sent as part of station initialization right - * after station has been added. - * - * The link quality command is sent as the last step of station creation. - * This is the special case in which init is set and we call a callback in - * this case to clear the state indicating that station creation is in - * progress. - */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init) -{ - struct iwl_host_cmd cmd = { - .id = LQ_CMD, - .len = { sizeof(struct iwl_lq_cmd), }, - .flags = init ? 0 : CMD_ASYNC, - .data = { lq, }, - }; - - if (WARN_ON(lq->sta_id == IWL_MVM_STATION_COUNT)) - return -EINVAL; - - return iwl_mvm_send_cmd(mvm, &cmd); -} - -/** - * iwl_mvm_update_smps - Get a request to change the SMPS mode - * @req_type: The part of the driver who call for a change. - * @smps_requests: The request to change the SMPS mode. - * - * Get a requst to change the SMPS mode, - * and change it according to all other requests in the driver. - */ -void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum iwl_mvm_smps_type_request req_type, - enum ieee80211_smps_mode smps_request) -{ - struct iwl_mvm_vif *mvmvif; - enum ieee80211_smps_mode smps_mode; - int i; - - lockdep_assert_held(&mvm->mutex); - - /* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */ - if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1) - return; - - if (vif->type == NL80211_IFTYPE_AP) - smps_mode = IEEE80211_SMPS_OFF; - else - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - mvmvif = iwl_mvm_vif_from_mac80211(vif); - mvmvif->smps_requests[req_type] = smps_request; - for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { - if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) { - smps_mode = IEEE80211_SMPS_STATIC; - break; - } - if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) - smps_mode = IEEE80211_SMPS_DYNAMIC; - } - - ieee80211_request_smps(vif, smps_mode); -} - -int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear) -{ - struct iwl_statistics_cmd scmd = { - .flags = clear ? cpu_to_le32(IWL_STATISTICS_FLG_CLEAR) : 0, - }; - struct iwl_host_cmd cmd = { - .id = STATISTICS_CMD, - .len[0] = sizeof(scmd), - .data[0] = &scmd, - .flags = CMD_WANT_SKB, - }; - int ret; - - ret = iwl_mvm_send_cmd(mvm, &cmd); - if (ret) - return ret; - - iwl_mvm_handle_rx_statistics(mvm, cmd.resp_pkt); - iwl_free_resp(&cmd); - - if (clear) - iwl_mvm_accu_radio_stats(mvm); - - return 0; -} - -void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm) -{ - mvm->accu_radio_stats.rx_time += mvm->radio_stats.rx_time; - mvm->accu_radio_stats.tx_time += mvm->radio_stats.tx_time; - mvm->accu_radio_stats.on_time_rf += mvm->radio_stats.on_time_rf; - mvm->accu_radio_stats.on_time_scan += mvm->radio_stats.on_time_scan; -} - -static void iwl_mvm_diversity_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - bool *result = _data; - int i; - - for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { - if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC || - mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) - *result = false; - } -} - -bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm) -{ - bool result = true; - - lockdep_assert_held(&mvm->mutex); - - if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1) - return false; - - if (mvm->cfg->rx_with_siso_diversity) - return false; - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_diversity_iter, &result); - - return result; -} - -int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool value) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int res; - - lockdep_assert_held(&mvm->mutex); - - if (mvmvif->low_latency == value) - return 0; - - mvmvif->low_latency = value; - - res = iwl_mvm_update_quotas(mvm, false, NULL); - if (res) - return res; - - iwl_mvm_bt_coex_vif_change(mvm); - - return iwl_mvm_power_update_mac(mvm); -} - -static void iwl_mvm_ll_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) -{ - bool *result = _data; - - if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(vif))) - *result = true; -} - -bool iwl_mvm_low_latency(struct iwl_mvm *mvm) -{ - bool result = false; - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_ll_iter, &result); - - return result; -} - -struct iwl_bss_iter_data { - struct ieee80211_vif *vif; - bool error; -}; - -static void iwl_mvm_bss_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_bss_iter_data *data = _data; - - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return; - - if (data->vif) { - data->error = true; - return; - } - - data->vif = vif; -} - -struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm) -{ - struct iwl_bss_iter_data bss_iter_data = {}; - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bss_iface_iterator, &bss_iter_data); - - if (bss_iter_data.error) { - IWL_ERR(mvm, "More than one managed interface active!\n"); - return ERR_PTR(-EINVAL); - } - - return bss_iter_data.vif; -} - -unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool tdls, bool cmd_q) -{ - struct iwl_fw_dbg_trigger_tlv *trigger; - struct iwl_fw_dbg_trigger_txq_timer *txq_timer; - unsigned int default_timeout = - cmd_q ? IWL_DEF_WD_TIMEOUT : mvm->cfg->base_params->wd_timeout; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS)) - return iwlmvm_mod_params.tfd_q_hang_detect ? - default_timeout : IWL_WATCHDOG_DISABLED; - - trigger = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS); - txq_timer = (void *)trigger->data; - - if (tdls) - return le32_to_cpu(txq_timer->tdls); - - if (cmd_q) - return le32_to_cpu(txq_timer->command_queue); - - if (WARN_ON(!vif)) - return default_timeout; - - switch (ieee80211_vif_type_p2p(vif)) { - case NL80211_IFTYPE_ADHOC: - return le32_to_cpu(txq_timer->ibss); - case NL80211_IFTYPE_STATION: - return le32_to_cpu(txq_timer->bss); - case NL80211_IFTYPE_AP: - return le32_to_cpu(txq_timer->softap); - case NL80211_IFTYPE_P2P_CLIENT: - return le32_to_cpu(txq_timer->p2p_client); - case NL80211_IFTYPE_P2P_GO: - return le32_to_cpu(txq_timer->p2p_go); - case NL80211_IFTYPE_P2P_DEVICE: - return le32_to_cpu(txq_timer->p2p_device); - default: - WARN_ON(1); - return mvm->cfg->base_params->wd_timeout; - } -} - -void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - const char *errmsg) -{ - struct iwl_fw_dbg_trigger_tlv *trig; - struct iwl_fw_dbg_trigger_mlme *trig_mlme; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME)) - goto out; - - trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME); - trig_mlme = (void *)trig->data; - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) - goto out; - - if (trig_mlme->stop_connection_loss && - --trig_mlme->stop_connection_loss) - goto out; - - iwl_mvm_fw_dbg_collect_trig(mvm, trig, "%s", errmsg); - -out: - ieee80211_connection_loss(vif); -} diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c deleted file mode 100644 index 644b58bc5226..000000000000 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ /dev/null @@ -1,685 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include - -#include "iwl-trans.h" -#include "iwl-drv.h" -#include "internal.h" - -#define IWL_PCI_DEVICE(dev, subdev, cfg) \ - .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ - .subvendor = PCI_ANY_ID, .subdevice = (subdev), \ - .driver_data = (kernel_ulong_t)&(cfg) - -/* Hardware specific file defines the PCI IDs table for that hardware module */ -static const struct pci_device_id iwl_hw_card_ids[] = { -#if IS_ENABLED(CONFIG_IWLDVM) - {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */ - -/* 5300 Series WiFi */ - {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */ - -/* 5350 Series WiFi/WiMax */ - {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */ - -/* 5150 Series Wifi/WiMax */ - {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1326, iwl5150_abg_cfg)}, /* Half Mini Card */ - - {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */ - -/* 6x00 Series */ - {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422B, 0x1108, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422B, 0x1128, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)}, - {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x4238, 0x1118, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)}, - {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)}, - -/* 6x05 Series */ - {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1308, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1328, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0x1318, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0xC228, iwl6005_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */ - {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */ - -/* 6x30 Series */ - {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)}, - {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1030_bg_cfg)}, - {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1030_bgn_cfg)}, - {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1030_bg_cfg)}, - {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1030_bgn_cfg)}, - {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1030_bg_cfg)}, - {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_2bg_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_2abg_cfg)}, - -/* 6x50 WiFi/WiMax Series */ - {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)}, - -/* 6150 WiFi/WiMax Series */ - {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_bg_cfg)}, - {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_bg_cfg)}, - {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_bg_cfg)}, - -/* 1000 Series WiFi */ - {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)}, - -/* 100 Series WiFi */ - {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl100_bgn_cfg)}, - {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl100_bg_cfg)}, - {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl100_bgn_cfg)}, - {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl100_bg_cfg)}, - {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl100_bgn_cfg)}, - {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl100_bg_cfg)}, - -/* 130 Series WiFi */ - {IWL_PCI_DEVICE(0x0896, 0x5005, iwl130_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0896, 0x5007, iwl130_bg_cfg)}, - {IWL_PCI_DEVICE(0x0897, 0x5015, iwl130_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0897, 0x5017, iwl130_bg_cfg)}, - {IWL_PCI_DEVICE(0x0896, 0x5025, iwl130_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0896, 0x5027, iwl130_bg_cfg)}, - -/* 2x00 Series */ - {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_2bgn_d_cfg)}, - -/* 2x30 Series */ - {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)}, - -/* 6x35 Series */ - {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x406A, iwl6035_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088F, 0x426A, iwl6035_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6035_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6035_2agn_cfg)}, - -/* 105 Series */ - {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_bgn_d_cfg)}, - -/* 135 Series */ - {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)}, -#endif /* CONFIG_IWLDVM */ - -#if IS_ENABLED(CONFIG_IWLMVM) -/* 7260 Series */ - {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4C60, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4C70, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x406A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4272, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x426A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4472, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x446A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)}, - {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)}, - {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)}, - {IWL_PCI_DEVICE(0x08B1, 0x4570, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4560, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x5072, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x5170, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x5770, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC072, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC06A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC770, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC760, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xCC70, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xCC60, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC272, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC26A, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC472, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC570, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC560, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC370, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC360, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC02A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)}, - -/* 3160 Series */ - {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0072, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0172, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x0272, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0472, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x0370, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8072, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8172, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x8370, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x8272, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x1070, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x1170, iwl3160_2ac_cfg)}, - -/* 3165 Series */ - {IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3166, 0x4212, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3166, 0x4310, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3166, 0x4210, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x8110, iwl3165_2ac_cfg)}, - -/* 7265 Series */ - {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_n_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7265_n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5102, iwl7265_n_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7265_n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9012, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x900A, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5420, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5090, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5190, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5F10, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5212, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x520A, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9000, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9400, iwl7265_2ac_cfg)}, - -/* 8000 Series */ - {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F4, 0x1130, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xC110, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)}, - {IWL_PCI_DEVICE(0x24F5, 0x0010, iwl4165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F6, 0x0030, iwl4165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)}, -#endif /* CONFIG_IWLMVM */ - - {0} -}; -MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); - -#ifdef CONFIG_ACPI -#define SPL_METHOD "SPLC" -#define SPL_DOMAINTYPE_MODULE BIT(0) -#define SPL_DOMAINTYPE_WIFI BIT(1) -#define SPL_DOMAINTYPE_WIGIG BIT(2) -#define SPL_DOMAINTYPE_RFEM BIT(3) - -static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx) -{ - union acpi_object *limits, *domain_type, *power_limit; - - if (splx->type != ACPI_TYPE_PACKAGE || - splx->package.count != 2 || - splx->package.elements[0].type != ACPI_TYPE_INTEGER || - splx->package.elements[0].integer.value != 0) { - IWL_ERR(trans, "Unsupported splx structure\n"); - return 0; - } - - limits = &splx->package.elements[1]; - if (limits->type != ACPI_TYPE_PACKAGE || - limits->package.count < 2 || - limits->package.elements[0].type != ACPI_TYPE_INTEGER || - limits->package.elements[1].type != ACPI_TYPE_INTEGER) { - IWL_ERR(trans, "Invalid limits element\n"); - return 0; - } - - domain_type = &limits->package.elements[0]; - power_limit = &limits->package.elements[1]; - if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) { - IWL_DEBUG_INFO(trans, "WiFi power is not limited\n"); - return 0; - } - - return power_limit->integer.value; -} - -static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) -{ - acpi_handle pxsx_handle; - acpi_handle handle; - struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL}; - acpi_status status; - - pxsx_handle = ACPI_HANDLE(&pdev->dev); - if (!pxsx_handle) { - IWL_DEBUG_INFO(trans, - "Could not retrieve root port ACPI handle\n"); - return; - } - - /* Get the method's handle */ - status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle); - if (ACPI_FAILURE(status)) { - IWL_DEBUG_INFO(trans, "SPL method not found\n"); - return; - } - - /* Call SPLC with no arguments */ - status = acpi_evaluate_object(handle, NULL, NULL, &splx); - if (ACPI_FAILURE(status)) { - IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status); - return; - } - - trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer); - IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n", - trans->dflt_pwr_limit); - kfree(splx.pointer); -} - -#else /* CONFIG_ACPI */ -static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) {} -#endif - -/* PCI registers */ -#define PCI_CFG_RETRY_TIMEOUT 0x041 - -static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); - const struct iwl_cfg *cfg_7265d __maybe_unused = NULL; - struct iwl_trans *iwl_trans; - struct iwl_trans_pcie *trans_pcie; - int ret; - - iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg); - if (IS_ERR(iwl_trans)) - return PTR_ERR(iwl_trans); - -#if IS_ENABLED(CONFIG_IWLMVM) - /* - * special-case 7265D, it has the same PCI IDs. - * - * Note that because we already pass the cfg to the transport above, - * all the parameters that the transport uses must, until that is - * changed, be identical to the ones in the 7265D configuration. - */ - if (cfg == &iwl7265_2ac_cfg) - cfg_7265d = &iwl7265d_2ac_cfg; - else if (cfg == &iwl7265_2n_cfg) - cfg_7265d = &iwl7265d_2n_cfg; - else if (cfg == &iwl7265_n_cfg) - cfg_7265d = &iwl7265d_n_cfg; - if (cfg_7265d && - (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D) { - cfg = cfg_7265d; - iwl_trans->cfg = cfg_7265d; - } -#endif - - pci_set_drvdata(pdev, iwl_trans); - - trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans); - trans_pcie->drv = iwl_drv_start(iwl_trans, cfg); - - if (IS_ERR(trans_pcie->drv)) { - ret = PTR_ERR(trans_pcie->drv); - goto out_free_trans; - } - - set_dflt_pwr_limit(iwl_trans, pdev); - - /* register transport layer debugfs here */ - ret = iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir); - if (ret) - goto out_free_drv; - - return 0; - -out_free_drv: - iwl_drv_stop(trans_pcie->drv); -out_free_trans: - iwl_trans_pcie_free(iwl_trans); - return ret; -} - -static void iwl_pci_remove(struct pci_dev *pdev) -{ - struct iwl_trans *trans = pci_get_drvdata(pdev); - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - iwl_drv_stop(trans_pcie->drv); - iwl_trans_pcie_free(trans); -} - -#ifdef CONFIG_PM_SLEEP - -static int iwl_pci_suspend(struct device *device) -{ - /* Before you put code here, think about WoWLAN. You cannot check here - * whether WoWLAN is enabled or not, and your code will run even if - * WoWLAN is enabled - don't kill the NIC, someone may need it in Sx. - */ - - return 0; -} - -static int iwl_pci_resume(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - struct iwl_trans *trans = pci_get_drvdata(pdev); - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - bool hw_rfkill; - - /* Before you put code here, think about WoWLAN. You cannot check here - * whether WoWLAN is enabled or not, and your code will run even if - * WoWLAN is enabled - the NIC may be alive. - */ - - /* - * We disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state. - */ - pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); - - if (!trans->op_mode) - return 0; - - /* - * Enable rfkill interrupt (in order to keep track of - * the rfkill status) - */ - iwl_enable_rfkill_int(trans); - - hw_rfkill = iwl_is_rfkill_set(trans); - - mutex_lock(&trans_pcie->mutex); - iwl_trans_pcie_rf_kill(trans, hw_rfkill); - mutex_unlock(&trans_pcie->mutex); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume); - -#define IWL_PM_OPS (&iwl_dev_pm_ops) - -#else - -#define IWL_PM_OPS NULL - -#endif - -static struct pci_driver iwl_pci_driver = { - .name = DRV_NAME, - .id_table = iwl_hw_card_ids, - .probe = iwl_pci_probe, - .remove = iwl_pci_remove, - .driver.pm = IWL_PM_OPS, -}; - -int __must_check iwl_pci_register_driver(void) -{ - int ret; - ret = pci_register_driver(&iwl_pci_driver); - if (ret) - pr_err("Unable to initialize PCI module\n"); - - return ret; -} - -void iwl_pci_unregister_driver(void) -{ - pci_unregister_driver(&iwl_pci_driver); -} diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h deleted file mode 100644 index feb2f7e81134..000000000000 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ /dev/null @@ -1,569 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2015 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#ifndef __iwl_trans_int_pcie_h__ -#define __iwl_trans_int_pcie_h__ - -#include -#include -#include -#include -#include -#include - -#include "iwl-fh.h" -#include "iwl-csr.h" -#include "iwl-trans.h" -#include "iwl-debug.h" -#include "iwl-io.h" -#include "iwl-op-mode.h" - -/* We need 2 entries for the TX command and header, and another one might - * be needed for potential data in the SKB's head. The remaining ones can - * be used for frags. - */ -#define IWL_PCIE_MAX_FRAGS (IWL_NUM_OF_TBS - 3) - -/* - * RX related structures and functions - */ -#define RX_NUM_QUEUES 1 -#define RX_POST_REQ_ALLOC 2 -#define RX_CLAIM_REQ_ALLOC 8 -#define RX_POOL_SIZE ((RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC) * RX_NUM_QUEUES) -#define RX_LOW_WATERMARK 8 - -struct iwl_host_cmd; - -/*This file includes the declaration that are internal to the - * trans_pcie layer */ - -struct iwl_rx_mem_buffer { - dma_addr_t page_dma; - struct page *page; - struct list_head list; -}; - -/** - * struct isr_statistics - interrupt statistics - * - */ -struct isr_statistics { - u32 hw; - u32 sw; - u32 err_code; - u32 sch; - u32 alive; - u32 rfkill; - u32 ctkill; - u32 wakeup; - u32 rx; - u32 tx; - u32 unhandled; -}; - -/** - * struct iwl_rxq - Rx queue - * @bd: driver's pointer to buffer of receive buffer descriptors (rbd) - * @bd_dma: bus address of buffer of receive buffer descriptors (rbd) - * @read: Shared index to newest available Rx buffer - * @write: Shared index to oldest written Rx packet - * @free_count: Number of pre-allocated buffers in rx_free - * @used_count: Number of RBDs handled to allocator to use for allocation - * @write_actual: - * @rx_free: list of RBDs with allocated RB ready for use - * @rx_used: list of RBDs with no RB attached - * @need_update: flag to indicate we need to update read/write index - * @rb_stts: driver's pointer to receive buffer status - * @rb_stts_dma: bus address of receive buffer status - * @lock: - * @pool: initial pool of iwl_rx_mem_buffer for the queue - * @queue: actual rx queue - * - * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers - */ -struct iwl_rxq { - __le32 *bd; - dma_addr_t bd_dma; - u32 read; - u32 write; - u32 free_count; - u32 used_count; - u32 write_actual; - struct list_head rx_free; - struct list_head rx_used; - bool need_update; - struct iwl_rb_status *rb_stts; - dma_addr_t rb_stts_dma; - spinlock_t lock; - struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE]; - struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE]; -}; - -/** - * struct iwl_rb_allocator - Rx allocator - * @pool: initial pool of allocator - * @req_pending: number of requests the allcator had not processed yet - * @req_ready: number of requests honored and ready for claiming - * @rbd_allocated: RBDs with pages allocated and ready to be handled to - * the queue. This is a list of &struct iwl_rx_mem_buffer - * @rbd_empty: RBDs with no page attached for allocator use. This is a list - * of &struct iwl_rx_mem_buffer - * @lock: protects the rbd_allocated and rbd_empty lists - * @alloc_wq: work queue for background calls - * @rx_alloc: work struct for background calls - */ -struct iwl_rb_allocator { - struct iwl_rx_mem_buffer pool[RX_POOL_SIZE]; - atomic_t req_pending; - atomic_t req_ready; - struct list_head rbd_allocated; - struct list_head rbd_empty; - spinlock_t lock; - struct workqueue_struct *alloc_wq; - struct work_struct rx_alloc; -}; - -struct iwl_dma_ptr { - dma_addr_t dma; - void *addr; - size_t size; -}; - -/** - * iwl_queue_inc_wrap - increment queue index, wrap back to beginning - * @index -- current index - */ -static inline int iwl_queue_inc_wrap(int index) -{ - return ++index & (TFD_QUEUE_SIZE_MAX - 1); -} - -/** - * iwl_queue_dec_wrap - decrement queue index, wrap back to end - * @index -- current index - */ -static inline int iwl_queue_dec_wrap(int index) -{ - return --index & (TFD_QUEUE_SIZE_MAX - 1); -} - -struct iwl_cmd_meta { - /* only for SYNC commands, iff the reply skb is wanted */ - struct iwl_host_cmd *source; - u32 flags; -}; - -/* - * Generic queue structure - * - * Contains common data for Rx and Tx queues. - * - * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware - * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless - * there might be HW changes in the future). For the normal TX - * queues, n_window, which is the size of the software queue data - * is also 256; however, for the command queue, n_window is only - * 32 since we don't need so many commands pending. Since the HW - * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. As a result, - * the software buffers (in the variables @meta, @txb in struct - * iwl_txq) only have 32 entries, while the HW buffers (@tfds in - * the same struct) have 256. - * This means that we end up with the following: - * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 | - * SW entries: | 0 | ... | 31 | - * where N is a number between 0 and 7. This means that the SW - * data is a window overlayed over the HW queue. - */ -struct iwl_queue { - int write_ptr; /* 1-st empty entry (index) host_w*/ - int read_ptr; /* last used entry (index) host_r*/ - /* use for monitoring and recovering the stuck queue */ - dma_addr_t dma_addr; /* physical addr for BD's */ - int n_window; /* safe queue window */ - u32 id; - int low_mark; /* low watermark, resume queue if free - * space more than this */ - int high_mark; /* high watermark, stop queue if free - * space less than this */ -}; - -#define TFD_TX_CMD_SLOTS 256 -#define TFD_CMD_SLOTS 32 - -/* - * The FH will write back to the first TB only, so we need - * to copy some data into the buffer regardless of whether - * it should be mapped or not. This indicates how big the - * first TB must be to include the scratch buffer. Since - * the scratch is 4 bytes at offset 12, it's 16 now. If we - * make it bigger then allocations will be bigger and copy - * slower, so that's probably not useful. - */ -#define IWL_HCMD_SCRATCHBUF_SIZE 16 - -struct iwl_pcie_txq_entry { - struct iwl_device_cmd *cmd; - struct sk_buff *skb; - /* buffer to free after command completes */ - const void *free_buf; - struct iwl_cmd_meta meta; -}; - -struct iwl_pcie_txq_scratch_buf { - struct iwl_cmd_header hdr; - u8 buf[8]; - __le32 scratch; -}; - -/** - * struct iwl_txq - Tx Queue for DMA - * @q: generic Rx/Tx queue descriptor - * @tfds: transmit frame descriptors (DMA memory) - * @scratchbufs: start of command headers, including scratch buffers, for - * the writeback -- this is DMA memory and an array holding one buffer - * for each command on the queue - * @scratchbufs_dma: DMA address for the scratchbufs start - * @entries: transmit entries (driver state) - * @lock: queue lock - * @stuck_timer: timer that fires if queue gets stuck - * @trans_pcie: pointer back to transport (for timer) - * @need_update: indicates need to update read/write index - * @active: stores if queue is active - * @ampdu: true if this queue is an ampdu queue for an specific RA/TID - * @wd_timeout: queue watchdog timeout (jiffies) - per queue - * @frozen: tx stuck queue timer is frozen - * @frozen_expiry_remainder: remember how long until the timer fires - * - * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame - * descriptors) and required locking structures. - */ -struct iwl_txq { - struct iwl_queue q; - struct iwl_tfd *tfds; - struct iwl_pcie_txq_scratch_buf *scratchbufs; - dma_addr_t scratchbufs_dma; - struct iwl_pcie_txq_entry *entries; - spinlock_t lock; - unsigned long frozen_expiry_remainder; - struct timer_list stuck_timer; - struct iwl_trans_pcie *trans_pcie; - bool need_update; - bool frozen; - u8 active; - bool ampdu; - unsigned long wd_timeout; -}; - -static inline dma_addr_t -iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) -{ - return txq->scratchbufs_dma + - sizeof(struct iwl_pcie_txq_scratch_buf) * idx; -} - -/** - * struct iwl_trans_pcie - PCIe transport specific data - * @rxq: all the RX queue data - * @rba: allocator for RX replenishing - * @drv - pointer to iwl_drv - * @trans: pointer to the generic transport area - * @scd_base_addr: scheduler sram base address in SRAM - * @scd_bc_tbls: pointer to the byte count table of the scheduler - * @kw: keep warm address - * @pci_dev: basic pci-network driver stuff - * @hw_base: pci hardware address support - * @ucode_write_complete: indicates that the ucode has been copied. - * @ucode_write_waitq: wait queue for uCode load - * @cmd_queue - command queue number - * @rx_buf_size_8k: 8 kB RX buffer size - * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) - * @scd_set_active: should the transport configure the SCD for HCMD queue - * @wide_cmd_header: true when ucode supports wide command header format - * @rx_page_order: page order for receive buffer size - * @reg_lock: protect hw register access - * @mutex: to protect stop_device / start_fw / start_hw - * @cmd_in_flight: true when we have a host command in flight - * @fw_mon_phys: physical address of the buffer for the firmware monitor - * @fw_mon_page: points to the first page of the buffer for the firmware monitor - * @fw_mon_size: size of the buffer for the firmware monitor - */ -struct iwl_trans_pcie { - struct iwl_rxq rxq; - struct iwl_rb_allocator rba; - struct iwl_trans *trans; - struct iwl_drv *drv; - - struct net_device napi_dev; - struct napi_struct napi; - - /* INT ICT Table */ - __le32 *ict_tbl; - dma_addr_t ict_tbl_dma; - int ict_index; - bool use_ict; - bool is_down; - struct isr_statistics isr_stats; - - spinlock_t irq_lock; - struct mutex mutex; - u32 inta_mask; - u32 scd_base_addr; - struct iwl_dma_ptr scd_bc_tbls; - struct iwl_dma_ptr kw; - - struct iwl_txq *txq; - unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; - unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; - - /* PCI bus related data */ - struct pci_dev *pci_dev; - void __iomem *hw_base; - - bool ucode_write_complete; - wait_queue_head_t ucode_write_waitq; - wait_queue_head_t wait_command_queue; - - u8 cmd_queue; - u8 cmd_fifo; - unsigned int cmd_q_wdg_timeout; - u8 n_no_reclaim_cmds; - u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; - - bool rx_buf_size_8k; - bool bc_table_dword; - bool scd_set_active; - bool wide_cmd_header; - u32 rx_page_order; - - const char *const *command_names; - - /*protect hw register */ - spinlock_t reg_lock; - bool cmd_hold_nic_awake; - bool ref_cmd_in_flight; - - /* protect ref counter */ - spinlock_t ref_lock; - u32 ref_count; - - dma_addr_t fw_mon_phys; - struct page *fw_mon_page; - u32 fw_mon_size; -}; - -#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ - ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) - -static inline struct iwl_trans * -iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie) -{ - return container_of((void *)trans_pcie, struct iwl_trans, - trans_specific); -} - -/* - * Convention: trans API functions: iwl_trans_pcie_XXX - * Other functions: iwl_pcie_XXX - */ -struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, - const struct pci_device_id *ent, - const struct iwl_cfg *cfg); -void iwl_trans_pcie_free(struct iwl_trans *trans); - -/***************************************************** -* RX -******************************************************/ -int iwl_pcie_rx_init(struct iwl_trans *trans); -irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id); -int iwl_pcie_rx_stop(struct iwl_trans *trans); -void iwl_pcie_rx_free(struct iwl_trans *trans); - -/***************************************************** -* ICT - interrupt handling -******************************************************/ -irqreturn_t iwl_pcie_isr(int irq, void *data); -int iwl_pcie_alloc_ict(struct iwl_trans *trans); -void iwl_pcie_free_ict(struct iwl_trans *trans); -void iwl_pcie_reset_ict(struct iwl_trans *trans); -void iwl_pcie_disable_ict(struct iwl_trans *trans); - -/***************************************************** -* TX / HCMD -******************************************************/ -int iwl_pcie_tx_init(struct iwl_trans *trans); -void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr); -int iwl_pcie_tx_stop(struct iwl_trans *trans); -void iwl_pcie_tx_free(struct iwl_trans *trans); -void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int queue, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg, - unsigned int wdg_timeout); -void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue, - bool configure_scd); -int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_device_cmd *dev_cmd, int txq_id); -void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans); -int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); -void iwl_pcie_hcmd_complete(struct iwl_trans *trans, - struct iwl_rx_cmd_buffer *rxb); -void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, - struct sk_buff_head *skbs); -void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); - -void iwl_trans_pcie_ref(struct iwl_trans *trans); -void iwl_trans_pcie_unref(struct iwl_trans *trans); - -static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) -{ - struct iwl_tfd_tb *tb = &tfd->tbs[idx]; - - return le16_to_cpu(tb->hi_n_len) >> 4; -} - -/***************************************************** -* Error handling -******************************************************/ -void iwl_pcie_dump_csr(struct iwl_trans *trans); - -/***************************************************** -* Helpers -******************************************************/ -static inline void iwl_disable_interrupts(struct iwl_trans *trans) -{ - clear_bit(STATUS_INT_ENABLED, &trans->status); - - /* disable interrupts from uCode/NIC to host */ - iwl_write32(trans, CSR_INT_MASK, 0x00000000); - - /* acknowledge/clear/reset any interrupts still pending - * from uCode or flow handler (Rx/Tx DMA) */ - iwl_write32(trans, CSR_INT, 0xffffffff); - iwl_write32(trans, CSR_FH_INT_STATUS, 0xffffffff); - IWL_DEBUG_ISR(trans, "Disabled interrupts\n"); -} - -static inline void iwl_enable_interrupts(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - IWL_DEBUG_ISR(trans, "Enabling interrupts\n"); - set_bit(STATUS_INT_ENABLED, &trans->status); - trans_pcie->inta_mask = CSR_INI_SET_MASK; - iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); -} - -static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - IWL_DEBUG_ISR(trans, "Enabling rfkill interrupt\n"); - trans_pcie->inta_mask = CSR_INT_BIT_RF_KILL; - iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); -} - -static inline void iwl_wake_queue(struct iwl_trans *trans, - struct iwl_txq *txq) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (test_and_clear_bit(txq->q.id, trans_pcie->queue_stopped)) { - IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d\n", txq->q.id); - iwl_op_mode_queue_not_full(trans->op_mode, txq->q.id); - } -} - -static inline void iwl_stop_queue(struct iwl_trans *trans, - struct iwl_txq *txq) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (!test_and_set_bit(txq->q.id, trans_pcie->queue_stopped)) { - iwl_op_mode_queue_full(trans->op_mode, txq->q.id); - IWL_DEBUG_TX_QUEUES(trans, "Stop hwq %d\n", txq->q.id); - } else - IWL_DEBUG_TX_QUEUES(trans, "hwq %d already stopped\n", - txq->q.id); -} - -static inline bool iwl_queue_used(const struct iwl_queue *q, int i) -{ - return q->write_ptr >= q->read_ptr ? - (i >= q->read_ptr && i < q->write_ptr) : - !(i < q->read_ptr && i >= q->write_ptr); -} - -static inline u8 get_cmd_index(struct iwl_queue *q, u32 index) -{ - return index & (q->n_window - 1); -} - -static inline const char *get_cmd_string(struct iwl_trans_pcie *trans_pcie, - u8 cmd) -{ - if (!trans_pcie->command_names || !trans_pcie->command_names[cmd]) - return "UNKNOWN"; - return trans_pcie->command_names[cmd]; -} - -static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) -{ - return !(iwl_read32(trans, CSR_GP_CNTRL) & - CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); -} - -static inline void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, - u32 reg, u32 mask, u32 value) -{ - u32 v; - -#ifdef CONFIG_IWLWIFI_DEBUG - WARN_ON_ONCE(value & ~mask); -#endif - - v = iwl_read32(trans, reg); - v &= ~mask; - v |= value; - iwl_write32(trans, reg, v); -} - -static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans, - u32 reg, u32 mask) -{ - __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0); -} - -static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans, - u32 reg, u32 mask) -{ - __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask); -} - -void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state); - -#endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c deleted file mode 100644 index e06591f625c4..000000000000 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ /dev/null @@ -1,1548 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include - -#include "iwl-prph.h" -#include "iwl-io.h" -#include "internal.h" -#include "iwl-op-mode.h" - -/****************************************************************************** - * - * RX path functions - * - ******************************************************************************/ - -/* - * Rx theory of operation - * - * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), - * each of which point to Receive Buffers to be filled by the NIC. These get - * used not only for Rx frames, but for any command response or notification - * from the NIC. The driver and NIC manage the Rx buffers by means - * of indexes into the circular buffer. - * - * Rx Queue Indexes - * The host/firmware share two index registers for managing the Rx buffers. - * - * The READ index maps to the first position that the firmware may be writing - * to -- the driver can read up to (but not including) this position and get - * good data. - * The READ index is managed by the firmware once the card is enabled. - * - * The WRITE index maps to the last position the driver has read from -- the - * position preceding WRITE is the last slot the firmware can place a packet. - * - * The queue is empty (no good data) if WRITE = READ - 1, and is full if - * WRITE = READ. - * - * During initialization, the host sets up the READ queue position to the first - * INDEX position, and WRITE to the last (READ - 1 wrapped) - * - * When the firmware places a packet in a buffer, it will advance the READ index - * and fire the RX interrupt. The driver can then query the READ index and - * process as many packets as possible, moving the WRITE index forward as it - * resets the Rx queue buffers with new memory. - * - * The management in the driver is as follows: - * + A list of pre-allocated RBDs is stored in iwl->rxq->rx_free. - * When the interrupt handler is called, the request is processed. - * The page is either stolen - transferred to the upper layer - * or reused - added immediately to the iwl->rxq->rx_free list. - * + When the page is stolen - the driver updates the matching queue's used - * count, detaches the RBD and transfers it to the queue used list. - * When there are two used RBDs - they are transferred to the allocator empty - * list. Work is then scheduled for the allocator to start allocating - * eight buffers. - * When there are another 6 used RBDs - they are transferred to the allocator - * empty list and the driver tries to claim the pre-allocated buffers and - * add them to iwl->rxq->rx_free. If it fails - it continues to claim them - * until ready. - * When there are 8+ buffers in the free list - either from allocation or from - * 8 reused unstolen pages - restock is called to update the FW and indexes. - * + In order to make sure the allocator always has RBDs to use for allocation - * the allocator has initial pool in the size of num_queues*(8-2) - the - * maximum missing RBDs per allocation request (request posted with 2 - * empty RBDs, there is no guarantee when the other 6 RBDs are supplied). - * The queues supplies the recycle of the rest of the RBDs. - * + A received packet is processed and handed to the kernel network stack, - * detached from the iwl->rxq. The driver 'processed' index is updated. - * + If there are no allocated buffers in iwl->rxq->rx_free, - * the READ INDEX is not incremented and iwl->status(RX_STALLED) is set. - * If there were enough free buffers and RX_STALLED is set it is cleared. - * - * - * Driver sequence: - * - * iwl_rxq_alloc() Allocates rx_free - * iwl_pcie_rx_replenish() Replenishes rx_free list from rx_used, and calls - * iwl_pcie_rxq_restock. - * Used only during initialization. - * iwl_pcie_rxq_restock() Moves available buffers from rx_free into Rx - * queue, updates firmware pointers, and updates - * the WRITE index. - * iwl_pcie_rx_allocator() Background work for allocating pages. - * - * -- enable interrupts -- - * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the - * READ INDEX, detaching the SKB from the pool. - * Moves the packet buffer from queue to rx_used. - * Posts and claims requests to the allocator. - * Calls iwl_pcie_rxq_restock to refill any empty - * slots. - * - * RBD life-cycle: - * - * Init: - * rxq.pool -> rxq.rx_used -> rxq.rx_free -> rxq.queue - * - * Regular Receive interrupt: - * Page Stolen: - * rxq.queue -> rxq.rx_used -> allocator.rbd_empty -> - * allocator.rbd_allocated -> rxq.rx_free -> rxq.queue - * Page not Stolen: - * rxq.queue -> rxq.rx_free -> rxq.queue - * ... - * - */ - -/* - * iwl_rxq_space - Return number of free slots available in queue. - */ -static int iwl_rxq_space(const struct iwl_rxq *rxq) -{ - /* Make sure RX_QUEUE_SIZE is a power of 2 */ - BUILD_BUG_ON(RX_QUEUE_SIZE & (RX_QUEUE_SIZE - 1)); - - /* - * There can be up to (RX_QUEUE_SIZE - 1) free slots, to avoid ambiguity - * between empty and completely full queues. - * The following is equivalent to modulo by RX_QUEUE_SIZE and is well - * defined for negative dividends. - */ - return (rxq->read - rxq->write - 1) & (RX_QUEUE_SIZE - 1); -} - -/* - * iwl_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr - */ -static inline __le32 iwl_pcie_dma_addr2rbd_ptr(dma_addr_t dma_addr) -{ - return cpu_to_le32((u32)(dma_addr >> 8)); -} - -/* - * iwl_pcie_rx_stop - stops the Rx DMA - */ -int iwl_pcie_rx_stop(struct iwl_trans *trans) -{ - iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); - return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG, - FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); -} - -/* - * iwl_pcie_rxq_inc_wr_ptr - Update the write pointer for the RX queue - */ -static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - u32 reg; - - lockdep_assert_held(&rxq->lock); - - /* - * explicitly wake up the NIC if: - * 1. shadow registers aren't enabled - * 2. there is a chance that the NIC is asleep - */ - if (!trans->cfg->base_params->shadow_reg_enable && - test_bit(STATUS_TPOWER_PMI, &trans->status)) { - reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); - - if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { - IWL_DEBUG_INFO(trans, "Rx queue requesting wakeup, GP1 = 0x%x\n", - reg); - iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - rxq->need_update = true; - return; - } - } - - rxq->write_actual = round_down(rxq->write, 8); - iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); -} - -static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - - spin_lock(&rxq->lock); - - if (!rxq->need_update) - goto exit_unlock; - - iwl_pcie_rxq_inc_wr_ptr(trans); - rxq->need_update = false; - - exit_unlock: - spin_unlock(&rxq->lock); -} - -/* - * iwl_pcie_rxq_restock - refill RX queue from pre-allocated pool - * - * If there are slots in the RX queue that need to be restocked, - * and we have free pre-allocated buffers, fill the ranks as much - * as we can, pulling from rx_free. - * - * This moves the 'write' index forward to catch up with 'processed', and - * also updates the memory address in the firmware to reference the new - * target buffer. - */ -static void iwl_pcie_rxq_restock(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - struct iwl_rx_mem_buffer *rxb; - - /* - * If the device isn't enabled - not need to try to add buffers... - * This can happen when we stop the device and still have an interrupt - * pending. We stop the APM before we sync the interrupts because we - * have to (see comment there). On the other hand, since the APM is - * stopped, we cannot access the HW (in particular not prph). - * So don't try to restock if the APM has been already stopped. - */ - if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) - return; - - spin_lock(&rxq->lock); - while ((iwl_rxq_space(rxq) > 0) && (rxq->free_count)) { - /* The overwritten rxb must be a used one */ - rxb = rxq->queue[rxq->write]; - BUG_ON(rxb && rxb->page); - - /* Get next free Rx buffer, remove from free list */ - rxb = list_first_entry(&rxq->rx_free, struct iwl_rx_mem_buffer, - list); - list_del(&rxb->list); - - /* Point to Rx buffer via next RBD in circular buffer */ - rxq->bd[rxq->write] = iwl_pcie_dma_addr2rbd_ptr(rxb->page_dma); - rxq->queue[rxq->write] = rxb; - rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; - rxq->free_count--; - } - spin_unlock(&rxq->lock); - - /* If we've added more space for the firmware to place data, tell it. - * Increment device's write pointer in multiples of 8. */ - if (rxq->write_actual != (rxq->write & ~0x7)) { - spin_lock(&rxq->lock); - iwl_pcie_rxq_inc_wr_ptr(trans); - spin_unlock(&rxq->lock); - } -} - -/* - * iwl_pcie_rx_alloc_page - allocates and returns a page. - * - */ -static struct page *iwl_pcie_rx_alloc_page(struct iwl_trans *trans, - gfp_t priority) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - struct page *page; - gfp_t gfp_mask = priority; - - if (rxq->free_count > RX_LOW_WATERMARK) - gfp_mask |= __GFP_NOWARN; - - if (trans_pcie->rx_page_order > 0) - gfp_mask |= __GFP_COMP; - - /* Alloc a new receive buffer */ - page = alloc_pages(gfp_mask, trans_pcie->rx_page_order); - if (!page) { - if (net_ratelimit()) - IWL_DEBUG_INFO(trans, "alloc_pages failed, order: %d\n", - trans_pcie->rx_page_order); - /* Issue an error if the hardware has consumed more than half - * of its free buffer list and we don't have enough - * pre-allocated buffers. -` */ - if (rxq->free_count <= RX_LOW_WATERMARK && - iwl_rxq_space(rxq) > (RX_QUEUE_SIZE / 2) && - net_ratelimit()) - IWL_CRIT(trans, - "Failed to alloc_pages with GFP_KERNEL. Only %u free buffers remaining.\n", - rxq->free_count); - return NULL; - } - return page; -} - -/* - * iwl_pcie_rxq_alloc_rbs - allocate a page for each used RBD - * - * A used RBD is an Rx buffer that has been given to the stack. To use it again - * a page must be allocated and the RBD must point to the page. This function - * doesn't change the HW pointer but handles the list of pages that is used by - * iwl_pcie_rxq_restock. The latter function will update the HW to use the newly - * allocated buffers. - */ -static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - struct iwl_rx_mem_buffer *rxb; - struct page *page; - - while (1) { - spin_lock(&rxq->lock); - if (list_empty(&rxq->rx_used)) { - spin_unlock(&rxq->lock); - return; - } - spin_unlock(&rxq->lock); - - /* Alloc a new receive buffer */ - page = iwl_pcie_rx_alloc_page(trans, priority); - if (!page) - return; - - spin_lock(&rxq->lock); - - if (list_empty(&rxq->rx_used)) { - spin_unlock(&rxq->lock); - __free_pages(page, trans_pcie->rx_page_order); - return; - } - rxb = list_first_entry(&rxq->rx_used, struct iwl_rx_mem_buffer, - list); - list_del(&rxb->list); - spin_unlock(&rxq->lock); - - BUG_ON(rxb->page); - rxb->page = page; - /* Get physical address of the RB */ - rxb->page_dma = - dma_map_page(trans->dev, page, 0, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); - if (dma_mapping_error(trans->dev, rxb->page_dma)) { - rxb->page = NULL; - spin_lock(&rxq->lock); - list_add(&rxb->list, &rxq->rx_used); - spin_unlock(&rxq->lock); - __free_pages(page, trans_pcie->rx_page_order); - return; - } - /* dma address must be no more than 36 bits */ - BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); - /* and also 256 byte aligned! */ - BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); - - spin_lock(&rxq->lock); - - list_add_tail(&rxb->list, &rxq->rx_free); - rxq->free_count++; - - spin_unlock(&rxq->lock); - } -} - -static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - int i; - - lockdep_assert_held(&rxq->lock); - - for (i = 0; i < RX_QUEUE_SIZE; i++) { - if (!rxq->pool[i].page) - continue; - dma_unmap_page(trans->dev, rxq->pool[i].page_dma, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); - __free_pages(rxq->pool[i].page, trans_pcie->rx_page_order); - rxq->pool[i].page = NULL; - } -} - -/* - * iwl_pcie_rx_replenish - Move all used buffers from rx_used to rx_free - * - * When moving to rx_free an page is allocated for the slot. - * - * Also restock the Rx queue via iwl_pcie_rxq_restock. - * This is called only during initialization - */ -static void iwl_pcie_rx_replenish(struct iwl_trans *trans) -{ - iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL); - - iwl_pcie_rxq_restock(trans); -} - -/* - * iwl_pcie_rx_allocator - Allocates pages in the background for RX queues - * - * Allocates for each received request 8 pages - * Called as a scheduled work item. - */ -static void iwl_pcie_rx_allocator(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rb_allocator *rba = &trans_pcie->rba; - struct list_head local_empty; - int pending = atomic_xchg(&rba->req_pending, 0); - - IWL_DEBUG_RX(trans, "Pending allocation requests = %d\n", pending); - - /* If we were scheduled - there is at least one request */ - spin_lock(&rba->lock); - /* swap out the rba->rbd_empty to a local list */ - list_replace_init(&rba->rbd_empty, &local_empty); - spin_unlock(&rba->lock); - - while (pending) { - int i; - struct list_head local_allocated; - - INIT_LIST_HEAD(&local_allocated); - - for (i = 0; i < RX_CLAIM_REQ_ALLOC;) { - struct iwl_rx_mem_buffer *rxb; - struct page *page; - - /* List should never be empty - each reused RBD is - * returned to the list, and initial pool covers any - * possible gap between the time the page is allocated - * to the time the RBD is added. - */ - BUG_ON(list_empty(&local_empty)); - /* Get the first rxb from the rbd list */ - rxb = list_first_entry(&local_empty, - struct iwl_rx_mem_buffer, list); - BUG_ON(rxb->page); - - /* Alloc a new receive buffer */ - page = iwl_pcie_rx_alloc_page(trans, GFP_KERNEL); - if (!page) - continue; - rxb->page = page; - - /* Get physical address of the RB */ - rxb->page_dma = dma_map_page(trans->dev, page, 0, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); - if (dma_mapping_error(trans->dev, rxb->page_dma)) { - rxb->page = NULL; - __free_pages(page, trans_pcie->rx_page_order); - continue; - } - /* dma address must be no more than 36 bits */ - BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); - /* and also 256 byte aligned! */ - BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); - - /* move the allocated entry to the out list */ - list_move(&rxb->list, &local_allocated); - i++; - } - - pending--; - if (!pending) { - pending = atomic_xchg(&rba->req_pending, 0); - IWL_DEBUG_RX(trans, - "Pending allocation requests = %d\n", - pending); - } - - spin_lock(&rba->lock); - /* add the allocated rbds to the allocator allocated list */ - list_splice_tail(&local_allocated, &rba->rbd_allocated); - /* get more empty RBDs for current pending requests */ - list_splice_tail_init(&rba->rbd_empty, &local_empty); - spin_unlock(&rba->lock); - - atomic_inc(&rba->req_ready); - } - - spin_lock(&rba->lock); - /* return unused rbds to the allocator empty list */ - list_splice_tail(&local_empty, &rba->rbd_empty); - spin_unlock(&rba->lock); -} - -/* - * iwl_pcie_rx_allocator_get - Returns the pre-allocated pages -.* -.* Called by queue when the queue posted allocation request and - * has freed 8 RBDs in order to restock itself. - */ -static int iwl_pcie_rx_allocator_get(struct iwl_trans *trans, - struct iwl_rx_mem_buffer - *out[RX_CLAIM_REQ_ALLOC]) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rb_allocator *rba = &trans_pcie->rba; - int i; - - /* - * atomic_dec_if_positive returns req_ready - 1 for any scenario. - * If req_ready is 0 atomic_dec_if_positive will return -1 and this - * function will return -ENOMEM, as there are no ready requests. - * atomic_dec_if_positive will perofrm the *actual* decrement only if - * req_ready > 0, i.e. - there are ready requests and the function - * hands one request to the caller. - */ - if (atomic_dec_if_positive(&rba->req_ready) < 0) - return -ENOMEM; - - spin_lock(&rba->lock); - for (i = 0; i < RX_CLAIM_REQ_ALLOC; i++) { - /* Get next free Rx buffer, remove it from free list */ - out[i] = list_first_entry(&rba->rbd_allocated, - struct iwl_rx_mem_buffer, list); - list_del(&out[i]->list); - } - spin_unlock(&rba->lock); - - return 0; -} - -static void iwl_pcie_rx_allocator_work(struct work_struct *data) -{ - struct iwl_rb_allocator *rba_p = - container_of(data, struct iwl_rb_allocator, rx_alloc); - struct iwl_trans_pcie *trans_pcie = - container_of(rba_p, struct iwl_trans_pcie, rba); - - iwl_pcie_rx_allocator(trans_pcie->trans); -} - -static int iwl_pcie_rx_alloc(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - struct iwl_rb_allocator *rba = &trans_pcie->rba; - struct device *dev = trans->dev; - - memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq)); - - spin_lock_init(&rxq->lock); - spin_lock_init(&rba->lock); - - if (WARN_ON(rxq->bd || rxq->rb_stts)) - return -EINVAL; - - /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */ - rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - &rxq->bd_dma, GFP_KERNEL); - if (!rxq->bd) - goto err_bd; - - /*Allocate the driver's pointer to receive buffer status */ - rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts), - &rxq->rb_stts_dma, GFP_KERNEL); - if (!rxq->rb_stts) - goto err_rb_stts; - - return 0; - -err_rb_stts: - dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - rxq->bd, rxq->bd_dma); - rxq->bd_dma = 0; - rxq->bd = NULL; -err_bd: - return -ENOMEM; -} - -static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 rb_size; - const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ - - if (trans_pcie->rx_buf_size_8k) - rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; - else - rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; - - /* Stop Rx DMA */ - iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); - /* reset and flush pointers */ - iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0); - iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0); - iwl_write_direct32(trans, FH_RSCSR_CHNL0_RDPTR, 0); - - /* Reset driver's Rx queue write index */ - iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); - - /* Tell device where to find RBD circular buffer in DRAM */ - iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_BASE_REG, - (u32)(rxq->bd_dma >> 8)); - - /* Tell device where in DRAM to update its Rx status */ - iwl_write_direct32(trans, FH_RSCSR_CHNL0_STTS_WPTR_REG, - rxq->rb_stts_dma >> 4); - - /* Enable Rx DMA - * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in - * the credit mechanism in 5000 HW RX FIFO - * Direct rx interrupts to hosts - * Rx buffer size 4 or 8k - * RB timeout 0x10 - * 256 RBDs - */ - iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, - FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | - FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | - FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | - rb_size| - (RX_RB_TIMEOUT << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| - (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); - - /* Set interrupt coalescing timer to default (2048 usecs) */ - iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); - - /* W/A for interrupt coalescing bug in 7260 and 3160 */ - if (trans->cfg->host_interrupt_operation_mode) - iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE); -} - -static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq) -{ - int i; - - lockdep_assert_held(&rxq->lock); - - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - rxq->free_count = 0; - rxq->used_count = 0; - - for (i = 0; i < RX_QUEUE_SIZE; i++) - list_add(&rxq->pool[i].list, &rxq->rx_used); -} - -static void iwl_pcie_rx_init_rba(struct iwl_rb_allocator *rba) -{ - int i; - - lockdep_assert_held(&rba->lock); - - INIT_LIST_HEAD(&rba->rbd_allocated); - INIT_LIST_HEAD(&rba->rbd_empty); - - for (i = 0; i < RX_POOL_SIZE; i++) - list_add(&rba->pool[i].list, &rba->rbd_empty); -} - -static void iwl_pcie_rx_free_rba(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rb_allocator *rba = &trans_pcie->rba; - int i; - - lockdep_assert_held(&rba->lock); - - for (i = 0; i < RX_POOL_SIZE; i++) { - if (!rba->pool[i].page) - continue; - dma_unmap_page(trans->dev, rba->pool[i].page_dma, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); - __free_pages(rba->pool[i].page, trans_pcie->rx_page_order); - rba->pool[i].page = NULL; - } -} - -int iwl_pcie_rx_init(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - struct iwl_rb_allocator *rba = &trans_pcie->rba; - int i, err; - - if (!rxq->bd) { - err = iwl_pcie_rx_alloc(trans); - if (err) - return err; - } - if (!rba->alloc_wq) - rba->alloc_wq = alloc_workqueue("rb_allocator", - WQ_HIGHPRI | WQ_UNBOUND, 1); - INIT_WORK(&rba->rx_alloc, iwl_pcie_rx_allocator_work); - - spin_lock(&rba->lock); - atomic_set(&rba->req_pending, 0); - atomic_set(&rba->req_ready, 0); - /* free all first - we might be reconfigured for a different size */ - iwl_pcie_rx_free_rba(trans); - iwl_pcie_rx_init_rba(rba); - spin_unlock(&rba->lock); - - spin_lock(&rxq->lock); - - /* free all first - we might be reconfigured for a different size */ - iwl_pcie_rxq_free_rbs(trans); - iwl_pcie_rx_init_rxb_lists(rxq); - - for (i = 0; i < RX_QUEUE_SIZE; i++) - rxq->queue[i] = NULL; - - /* Set us so that we have processed and used all buffers, but have - * not restocked the Rx queue with fresh buffers */ - rxq->read = rxq->write = 0; - rxq->write_actual = 0; - memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts)); - spin_unlock(&rxq->lock); - - iwl_pcie_rx_replenish(trans); - - iwl_pcie_rx_hw_init(trans, rxq); - - spin_lock(&rxq->lock); - iwl_pcie_rxq_inc_wr_ptr(trans); - spin_unlock(&rxq->lock); - - return 0; -} - -void iwl_pcie_rx_free(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - struct iwl_rb_allocator *rba = &trans_pcie->rba; - - /*if rxq->bd is NULL, it means that nothing has been allocated, - * exit now */ - if (!rxq->bd) { - IWL_DEBUG_INFO(trans, "Free NULL rx context\n"); - return; - } - - cancel_work_sync(&rba->rx_alloc); - if (rba->alloc_wq) { - destroy_workqueue(rba->alloc_wq); - rba->alloc_wq = NULL; - } - - spin_lock(&rba->lock); - iwl_pcie_rx_free_rba(trans); - spin_unlock(&rba->lock); - - spin_lock(&rxq->lock); - iwl_pcie_rxq_free_rbs(trans); - spin_unlock(&rxq->lock); - - dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE, - rxq->bd, rxq->bd_dma); - rxq->bd_dma = 0; - rxq->bd = NULL; - - if (rxq->rb_stts) - dma_free_coherent(trans->dev, - sizeof(struct iwl_rb_status), - rxq->rb_stts, rxq->rb_stts_dma); - else - IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n"); - rxq->rb_stts_dma = 0; - rxq->rb_stts = NULL; -} - -/* - * iwl_pcie_rx_reuse_rbd - Recycle used RBDs - * - * Called when a RBD can be reused. The RBD is transferred to the allocator. - * When there are 2 empty RBDs - a request for allocation is posted - */ -static void iwl_pcie_rx_reuse_rbd(struct iwl_trans *trans, - struct iwl_rx_mem_buffer *rxb, - struct iwl_rxq *rxq, bool emergency) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rb_allocator *rba = &trans_pcie->rba; - - /* Move the RBD to the used list, will be moved to allocator in batches - * before claiming or posting a request*/ - list_add_tail(&rxb->list, &rxq->rx_used); - - if (unlikely(emergency)) - return; - - /* Count the allocator owned RBDs */ - rxq->used_count++; - - /* If we have RX_POST_REQ_ALLOC new released rx buffers - - * issue a request for allocator. Modulo RX_CLAIM_REQ_ALLOC is - * used for the case we failed to claim RX_CLAIM_REQ_ALLOC, - * after but we still need to post another request. - */ - if ((rxq->used_count % RX_CLAIM_REQ_ALLOC) == RX_POST_REQ_ALLOC) { - /* Move the 2 RBDs to the allocator ownership. - Allocator has another 6 from pool for the request completion*/ - spin_lock(&rba->lock); - list_splice_tail_init(&rxq->rx_used, &rba->rbd_empty); - spin_unlock(&rba->lock); - - atomic_inc(&rba->req_pending); - queue_work(rba->alloc_wq, &rba->rx_alloc); - } -} - -static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, - struct iwl_rx_mem_buffer *rxb, - bool emergency) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; - bool page_stolen = false; - int max_len = PAGE_SIZE << trans_pcie->rx_page_order; - u32 offset = 0; - - if (WARN_ON(!rxb)) - return; - - dma_unmap_page(trans->dev, rxb->page_dma, max_len, DMA_FROM_DEVICE); - - while (offset + sizeof(u32) + sizeof(struct iwl_cmd_header) < max_len) { - struct iwl_rx_packet *pkt; - u16 sequence; - bool reclaim; - int index, cmd_index, len; - struct iwl_rx_cmd_buffer rxcb = { - ._offset = offset, - ._rx_page_order = trans_pcie->rx_page_order, - ._page = rxb->page, - ._page_stolen = false, - .truesize = max_len, - }; - - pkt = rxb_addr(&rxcb); - - if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID)) - break; - - IWL_DEBUG_RX(trans, - "cmd at offset %d: %s (0x%.2x, seq 0x%x)\n", - rxcb._offset, - get_cmd_string(trans_pcie, pkt->hdr.cmd), - pkt->hdr.cmd, le16_to_cpu(pkt->hdr.sequence)); - - 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); - - /* Reclaim a command buffer only if this packet is a response - * to a (driver-originated) command. - * If the packet (e.g. Rx frame) originated from uCode, - * there is no command buffer to reclaim. - * Ucode should set SEQ_RX_FRAME bit if ucode-originated, - * but apparently a few don't get set; catch them here. */ - reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME); - if (reclaim) { - int i; - - for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) { - if (trans_pcie->no_reclaim_cmds[i] == - pkt->hdr.cmd) { - reclaim = false; - break; - } - } - } - - sequence = le16_to_cpu(pkt->hdr.sequence); - index = SEQ_TO_INDEX(sequence); - cmd_index = get_cmd_index(&txq->q, index); - - iwl_op_mode_rx(trans->op_mode, &trans_pcie->napi, &rxcb); - - if (reclaim) { - kzfree(txq->entries[cmd_index].free_buf); - txq->entries[cmd_index].free_buf = NULL; - } - - /* - * After here, we should always check rxcb._page_stolen, - * if it is true then one of the handlers took the page. - */ - - if (reclaim) { - /* Invoke any callbacks, transfer the buffer to caller, - * and fire off the (possibly) blocking - * iwl_trans_send_cmd() - * as we reclaim the driver command queue */ - if (!rxcb._page_stolen) - iwl_pcie_hcmd_complete(trans, &rxcb); - else - IWL_WARN(trans, "Claim null rxb?\n"); - } - - page_stolen |= rxcb._page_stolen; - offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN); - } - - /* page was stolen from us -- free our reference */ - if (page_stolen) { - __free_pages(rxb->page, trans_pcie->rx_page_order); - rxb->page = NULL; - } - - /* Reuse the page if possible. For notification packets and - * SKBs that fail to Rx correctly, add them back into the - * rx_free list for reuse later. */ - if (rxb->page != NULL) { - rxb->page_dma = - dma_map_page(trans->dev, rxb->page, 0, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); - if (dma_mapping_error(trans->dev, rxb->page_dma)) { - /* - * free the page(s) as well to not break - * the invariant that the items on the used - * list have no page(s) - */ - __free_pages(rxb->page, trans_pcie->rx_page_order); - rxb->page = NULL; - iwl_pcie_rx_reuse_rbd(trans, rxb, rxq, emergency); - } else { - list_add_tail(&rxb->list, &rxq->rx_free); - rxq->free_count++; - } - } else - iwl_pcie_rx_reuse_rbd(trans, rxb, rxq, emergency); -} - -/* - * iwl_pcie_rx_handle - Main entry function for receiving responses from fw - */ -static void iwl_pcie_rx_handle(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - u32 r, i, j, count = 0; - bool emergency = false; - -restart: - spin_lock(&rxq->lock); - /* uCode's read index (stored in shared DRAM) indicates the last Rx - * buffer that the driver may process (last buffer filled by ucode). */ - r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF; - i = rxq->read; - - /* Rx interrupt, but nothing sent from uCode */ - if (i == r) - IWL_DEBUG_RX(trans, "HW = SW = %d\n", r); - - while (i != r) { - struct iwl_rx_mem_buffer *rxb; - - if (unlikely(rxq->used_count == RX_QUEUE_SIZE / 2)) - emergency = true; - - rxb = rxq->queue[i]; - rxq->queue[i] = NULL; - - IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d (%p)\n", - r, i, rxb); - iwl_pcie_rx_handle_rb(trans, rxb, emergency); - - i = (i + 1) & RX_QUEUE_MASK; - - /* If we have RX_CLAIM_REQ_ALLOC released rx buffers - - * try to claim the pre-allocated buffers from the allocator */ - if (rxq->used_count >= RX_CLAIM_REQ_ALLOC) { - struct iwl_rb_allocator *rba = &trans_pcie->rba; - struct iwl_rx_mem_buffer *out[RX_CLAIM_REQ_ALLOC]; - - if (rxq->used_count % RX_CLAIM_REQ_ALLOC == 0 && - !emergency) { - /* Add the remaining 6 empty RBDs - * for allocator use - */ - spin_lock(&rba->lock); - list_splice_tail_init(&rxq->rx_used, - &rba->rbd_empty); - spin_unlock(&rba->lock); - } - - /* If not ready - continue, will try to reclaim later. - * No need to reschedule work - allocator exits only on - * success */ - if (!iwl_pcie_rx_allocator_get(trans, out)) { - /* If success - then RX_CLAIM_REQ_ALLOC - * buffers were retrieved and should be added - * to free list */ - rxq->used_count -= RX_CLAIM_REQ_ALLOC; - for (j = 0; j < RX_CLAIM_REQ_ALLOC; j++) { - list_add_tail(&out[j]->list, - &rxq->rx_free); - rxq->free_count++; - } - } - } - if (emergency) { - count++; - if (count == 8) { - count = 0; - if (rxq->used_count < RX_QUEUE_SIZE / 3) - emergency = false; - spin_unlock(&rxq->lock); - iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC); - spin_lock(&rxq->lock); - } - } - /* handle restock for three cases, can be all of them at once: - * - we just pulled buffers from the allocator - * - we have 8+ unstolen pages accumulated - * - we are in emergency and allocated buffers - */ - if (rxq->free_count >= RX_CLAIM_REQ_ALLOC) { - rxq->read = i; - spin_unlock(&rxq->lock); - iwl_pcie_rxq_restock(trans); - goto restart; - } - } - - /* Backtrack one entry */ - rxq->read = i; - spin_unlock(&rxq->lock); - - /* - * handle a case where in emergency there are some unallocated RBDs. - * those RBDs are in the used list, but are not tracked by the queue's - * used_count which counts allocator owned RBDs. - * unallocated emergency RBDs must be allocated on exit, otherwise - * when called again the function may not be in emergency mode and - * they will be handed to the allocator with no tracking in the RBD - * allocator counters, which will lead to them never being claimed back - * by the queue. - * by allocating them here, they are now in the queue free list, and - * will be restocked by the next call of iwl_pcie_rxq_restock. - */ - if (unlikely(emergency && count)) - iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC); - - if (trans_pcie->napi.poll) - napi_gro_flush(&trans_pcie->napi, false); -} - -/* - * iwl_pcie_irq_handle_error - called for HW or SW error interrupt from card - */ -static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int i; - - /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ - if (trans->cfg->internal_wimax_coex && - !trans->cfg->apmg_not_supported && - (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & - APMS_CLK_VAL_MRB_FUNC_MODE) || - (iwl_read_prph(trans, APMG_PS_CTRL_REG) & - APMG_PS_CTRL_VAL_RESET_REQ))) { - clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); - iwl_op_mode_wimax_active(trans->op_mode); - wake_up(&trans_pcie->wait_command_queue); - return; - } - - iwl_pcie_dump_csr(trans); - iwl_dump_fh(trans, NULL); - - local_bh_disable(); - /* The STATUS_FW_ERROR bit is set in this function. This must happen - * before we wake up the command caller, to ensure a proper cleanup. */ - iwl_trans_fw_error(trans); - local_bh_enable(); - - for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) - del_timer(&trans_pcie->txq[i].stuck_timer); - - clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); - wake_up(&trans_pcie->wait_command_queue); -} - -static u32 iwl_pcie_int_cause_non_ict(struct iwl_trans *trans) -{ - u32 inta; - - lockdep_assert_held(&IWL_TRANS_GET_PCIE_TRANS(trans)->irq_lock); - - trace_iwlwifi_dev_irq(trans->dev); - - /* Discover which interrupts are active/pending */ - inta = iwl_read32(trans, CSR_INT); - - /* the thread will service interrupts and re-enable them */ - return inta; -} - -/* a device (PCI-E) page is 4096 bytes long */ -#define ICT_SHIFT 12 -#define ICT_SIZE (1 << ICT_SHIFT) -#define ICT_COUNT (ICT_SIZE / sizeof(u32)) - -/* interrupt handler using ict table, with this interrupt driver will - * stop using INTA register to get device's interrupt, reading this register - * is expensive, device will write interrupts in ICT dram table, increment - * index then will fire interrupt to driver, driver will OR all ICT table - * entries from current index up to table entry with 0 value. the result is - * the interrupt we need to service, driver will set the entries back to 0 and - * set index. - */ -static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 inta; - u32 val = 0; - u32 read; - - trace_iwlwifi_dev_irq(trans->dev); - - /* Ignore interrupt if there's nothing in NIC to service. - * This may be due to IRQ shared with another device, - * or due to sporadic interrupts thrown from our NIC. */ - read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); - trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read); - if (!read) - return 0; - - /* - * Collect all entries up to the first 0, starting from ict_index; - * note we already read at ict_index. - */ - do { - val |= read; - IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n", - trans_pcie->ict_index, read); - trans_pcie->ict_tbl[trans_pcie->ict_index] = 0; - trans_pcie->ict_index = - ((trans_pcie->ict_index + 1) & (ICT_COUNT - 1)); - - read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); - trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, - read); - } while (read); - - /* We should not get this value, just ignore it. */ - if (val == 0xffffffff) - val = 0; - - /* - * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit - * (bit 15 before shifting it to 31) to clear when using interrupt - * coalescing. fortunately, bits 18 and 19 stay set when this happens - * so we use them to decide on the real state of the Rx bit. - * In order words, bit 15 is set if bit 18 or bit 19 are set. - */ - if (val & 0xC0000) - val |= 0x8000; - - inta = (0xff & val) | ((0xff00 & val) << 16); - return inta; -} - -irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) -{ - struct iwl_trans *trans = dev_id; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct isr_statistics *isr_stats = &trans_pcie->isr_stats; - u32 inta = 0; - u32 handled = 0; - - lock_map_acquire(&trans->sync_cmd_lockdep_map); - - spin_lock(&trans_pcie->irq_lock); - - /* dram interrupt table not set yet, - * use legacy interrupt. - */ - if (likely(trans_pcie->use_ict)) - inta = iwl_pcie_int_cause_ict(trans); - else - inta = iwl_pcie_int_cause_non_ict(trans); - - if (iwl_have_debug_level(IWL_DL_ISR)) { - IWL_DEBUG_ISR(trans, - "ISR inta 0x%08x, enabled 0x%08x(sw), enabled(hw) 0x%08x, fh 0x%08x\n", - inta, trans_pcie->inta_mask, - iwl_read32(trans, CSR_INT_MASK), - iwl_read32(trans, CSR_FH_INT_STATUS)); - if (inta & (~trans_pcie->inta_mask)) - IWL_DEBUG_ISR(trans, - "We got a masked interrupt (0x%08x)\n", - inta & (~trans_pcie->inta_mask)); - } - - inta &= trans_pcie->inta_mask; - - /* - * Ignore interrupt if there's nothing in NIC to service. - * This may be due to IRQ shared with another device, - * or due to sporadic interrupts thrown from our NIC. - */ - if (unlikely(!inta)) { - IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); - /* - * Re-enable interrupts here since we don't - * have anything to service - */ - if (test_bit(STATUS_INT_ENABLED, &trans->status)) - iwl_enable_interrupts(trans); - spin_unlock(&trans_pcie->irq_lock); - lock_map_release(&trans->sync_cmd_lockdep_map); - return IRQ_NONE; - } - - if (unlikely(inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { - /* - * Hardware disappeared. It might have - * already raised an interrupt. - */ - IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta); - spin_unlock(&trans_pcie->irq_lock); - goto out; - } - - /* Ack/clear/reset pending uCode interrupts. - * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, - */ - /* There is a hardware bug in the interrupt mask function that some - * interrupts (i.e. CSR_INT_BIT_SCD) can still be generated even if - * they are disabled in the CSR_INT_MASK register. Furthermore the - * ICT interrupt handling mechanism has another bug that might cause - * these unmasked interrupts fail to be detected. We workaround the - * hardware bugs here by ACKing all the possible interrupts so that - * interrupt coalescing can still be achieved. - */ - iwl_write32(trans, CSR_INT, inta | ~trans_pcie->inta_mask); - - if (iwl_have_debug_level(IWL_DL_ISR)) - IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n", - inta, iwl_read32(trans, CSR_INT_MASK)); - - spin_unlock(&trans_pcie->irq_lock); - - /* Now service all interrupt bits discovered above. */ - if (inta & CSR_INT_BIT_HW_ERR) { - IWL_ERR(trans, "Hardware error detected. Restarting.\n"); - - /* Tell the device to stop sending interrupts */ - iwl_disable_interrupts(trans); - - isr_stats->hw++; - iwl_pcie_irq_handle_error(trans); - - handled |= CSR_INT_BIT_HW_ERR; - - goto out; - } - - if (iwl_have_debug_level(IWL_DL_ISR)) { - /* NIC fires this, but we don't use it, redundant with WAKEUP */ - if (inta & CSR_INT_BIT_SCD) { - IWL_DEBUG_ISR(trans, - "Scheduler finished to transmit the frame/frames.\n"); - isr_stats->sch++; - } - - /* Alive notification via Rx interrupt will do the real work */ - if (inta & CSR_INT_BIT_ALIVE) { - IWL_DEBUG_ISR(trans, "Alive interrupt\n"); - isr_stats->alive++; - } - } - - /* Safely ignore these bits for debug checks below */ - inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); - - /* HW RF KILL switch toggled */ - if (inta & CSR_INT_BIT_RF_KILL) { - bool hw_rfkill; - - hw_rfkill = iwl_is_rfkill_set(trans); - IWL_WARN(trans, "RF_KILL bit toggled to %s.\n", - hw_rfkill ? "disable radio" : "enable radio"); - - isr_stats->rfkill++; - - mutex_lock(&trans_pcie->mutex); - iwl_trans_pcie_rf_kill(trans, hw_rfkill); - mutex_unlock(&trans_pcie->mutex); - if (hw_rfkill) { - set_bit(STATUS_RFKILL, &trans->status); - if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE, - &trans->status)) - IWL_DEBUG_RF_KILL(trans, - "Rfkill while SYNC HCMD in flight\n"); - wake_up(&trans_pcie->wait_command_queue); - } else { - clear_bit(STATUS_RFKILL, &trans->status); - } - - handled |= CSR_INT_BIT_RF_KILL; - } - - /* Chip got too hot and stopped itself */ - if (inta & CSR_INT_BIT_CT_KILL) { - IWL_ERR(trans, "Microcode CT kill error detected.\n"); - isr_stats->ctkill++; - handled |= CSR_INT_BIT_CT_KILL; - } - - /* Error detected by uCode */ - if (inta & CSR_INT_BIT_SW_ERR) { - IWL_ERR(trans, "Microcode SW error detected. " - " Restarting 0x%X.\n", inta); - isr_stats->sw++; - iwl_pcie_irq_handle_error(trans); - handled |= CSR_INT_BIT_SW_ERR; - } - - /* uCode wakes up after power-down sleep */ - if (inta & CSR_INT_BIT_WAKEUP) { - IWL_DEBUG_ISR(trans, "Wakeup interrupt\n"); - iwl_pcie_rxq_check_wrptr(trans); - iwl_pcie_txq_check_wrptrs(trans); - - isr_stats->wakeup++; - - handled |= CSR_INT_BIT_WAKEUP; - } - - /* All uCode command responses, including Tx command responses, - * Rx "responses" (frame-received notification), and other - * notifications from uCode come through here*/ - if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX | - CSR_INT_BIT_RX_PERIODIC)) { - IWL_DEBUG_ISR(trans, "Rx interrupt\n"); - if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { - handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); - iwl_write32(trans, CSR_FH_INT_STATUS, - CSR_FH_INT_RX_MASK); - } - if (inta & CSR_INT_BIT_RX_PERIODIC) { - handled |= CSR_INT_BIT_RX_PERIODIC; - iwl_write32(trans, - CSR_INT, CSR_INT_BIT_RX_PERIODIC); - } - /* Sending RX interrupt require many steps to be done in the - * the device: - * 1- write interrupt to current index in ICT table. - * 2- dma RX frame. - * 3- update RX shared data to indicate last write index. - * 4- send interrupt. - * This could lead to RX race, driver could receive RX interrupt - * but the shared data changes does not reflect this; - * periodic interrupt will detect any dangling Rx activity. - */ - - /* Disable periodic interrupt; we use it as just a one-shot. */ - iwl_write8(trans, CSR_INT_PERIODIC_REG, - CSR_INT_PERIODIC_DIS); - - /* - * Enable periodic interrupt in 8 msec only if we received - * real RX interrupt (instead of just periodic int), to catch - * any dangling Rx interrupt. If it was just the periodic - * interrupt, there was no dangling Rx activity, and no need - * to extend the periodic interrupt; one-shot is enough. - */ - if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) - iwl_write8(trans, CSR_INT_PERIODIC_REG, - CSR_INT_PERIODIC_ENA); - - isr_stats->rx++; - - local_bh_disable(); - iwl_pcie_rx_handle(trans); - local_bh_enable(); - } - - /* This "Tx" DMA channel is used only for loading uCode */ - if (inta & CSR_INT_BIT_FH_TX) { - iwl_write32(trans, CSR_FH_INT_STATUS, CSR_FH_INT_TX_MASK); - IWL_DEBUG_ISR(trans, "uCode load interrupt\n"); - isr_stats->tx++; - handled |= CSR_INT_BIT_FH_TX; - /* Wake up uCode load routine, now that load is complete */ - trans_pcie->ucode_write_complete = true; - wake_up(&trans_pcie->ucode_write_waitq); - } - - if (inta & ~handled) { - IWL_ERR(trans, "Unhandled INTA bits 0x%08x\n", inta & ~handled); - isr_stats->unhandled++; - } - - if (inta & ~(trans_pcie->inta_mask)) { - IWL_WARN(trans, "Disabled INTA bits 0x%08x were pending\n", - inta & ~trans_pcie->inta_mask); - } - - /* Re-enable all interrupts */ - /* only Re-enable if disabled by irq */ - if (test_bit(STATUS_INT_ENABLED, &trans->status)) - iwl_enable_interrupts(trans); - /* Re-enable RF_KILL if it occurred */ - else if (handled & CSR_INT_BIT_RF_KILL) - iwl_enable_rfkill_int(trans); - -out: - lock_map_release(&trans->sync_cmd_lockdep_map); - return IRQ_HANDLED; -} - -/****************************************************************************** - * - * ICT functions - * - ******************************************************************************/ - -/* Free dram table */ -void iwl_pcie_free_ict(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (trans_pcie->ict_tbl) { - dma_free_coherent(trans->dev, ICT_SIZE, - trans_pcie->ict_tbl, - trans_pcie->ict_tbl_dma); - trans_pcie->ict_tbl = NULL; - trans_pcie->ict_tbl_dma = 0; - } -} - -/* - * allocate dram shared table, it is an aligned memory - * block of ICT_SIZE. - * also reset all data related to ICT table interrupt. - */ -int iwl_pcie_alloc_ict(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - trans_pcie->ict_tbl = - dma_zalloc_coherent(trans->dev, ICT_SIZE, - &trans_pcie->ict_tbl_dma, - GFP_KERNEL); - if (!trans_pcie->ict_tbl) - return -ENOMEM; - - /* just an API sanity check ... it is guaranteed to be aligned */ - if (WARN_ON(trans_pcie->ict_tbl_dma & (ICT_SIZE - 1))) { - iwl_pcie_free_ict(trans); - return -EINVAL; - } - - IWL_DEBUG_ISR(trans, "ict dma addr %Lx ict vir addr %p\n", - (unsigned long long)trans_pcie->ict_tbl_dma, - trans_pcie->ict_tbl); - - return 0; -} - -/* Device is going up inform it about using ICT interrupt table, - * also we need to tell the driver to start using ICT interrupt. - */ -void iwl_pcie_reset_ict(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 val; - - if (!trans_pcie->ict_tbl) - return; - - spin_lock(&trans_pcie->irq_lock); - iwl_disable_interrupts(trans); - - memset(trans_pcie->ict_tbl, 0, ICT_SIZE); - - val = trans_pcie->ict_tbl_dma >> ICT_SHIFT; - - val |= CSR_DRAM_INT_TBL_ENABLE | - CSR_DRAM_INIT_TBL_WRAP_CHECK | - CSR_DRAM_INIT_TBL_WRITE_POINTER; - - IWL_DEBUG_ISR(trans, "CSR_DRAM_INT_TBL_REG =0x%x\n", val); - - iwl_write32(trans, CSR_DRAM_INT_TBL_REG, val); - trans_pcie->use_ict = true; - trans_pcie->ict_index = 0; - iwl_write32(trans, CSR_INT, trans_pcie->inta_mask); - iwl_enable_interrupts(trans); - spin_unlock(&trans_pcie->irq_lock); -} - -/* Device is going down disable ict interrupt usage */ -void iwl_pcie_disable_ict(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - spin_lock(&trans_pcie->irq_lock); - trans_pcie->use_ict = false; - spin_unlock(&trans_pcie->irq_lock); -} - -irqreturn_t iwl_pcie_isr(int irq, void *data) -{ - struct iwl_trans *trans = data; - - if (!trans) - return IRQ_NONE; - - /* Disable (but don't clear!) interrupts here to avoid - * back-to-back ISRs and sporadic interrupts from our NIC. - * If we have something to service, the tasklet will re-enable ints. - * If we *don't* have something, we'll re-enable before leaving here. - */ - iwl_write32(trans, CSR_INT_MASK, 0x00000000); - - return IRQ_WAKE_THREAD; -} diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c deleted file mode 100644 index 90283453073c..000000000000 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ /dev/null @@ -1,2825 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iwl-drv.h" -#include "iwl-trans.h" -#include "iwl-csr.h" -#include "iwl-prph.h" -#include "iwl-scd.h" -#include "iwl-agn-hw.h" -#include "iwl-fw-error-dump.h" -#include "internal.h" -#include "iwl-fh.h" - -/* extended range in FW SRAM */ -#define IWL_FW_MEM_EXTENDED_START 0x40000 -#define IWL_FW_MEM_EXTENDED_END 0x57FFF - -static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (!trans_pcie->fw_mon_page) - return; - - dma_unmap_page(trans->dev, trans_pcie->fw_mon_phys, - trans_pcie->fw_mon_size, DMA_FROM_DEVICE); - __free_pages(trans_pcie->fw_mon_page, - get_order(trans_pcie->fw_mon_size)); - trans_pcie->fw_mon_page = NULL; - trans_pcie->fw_mon_phys = 0; - trans_pcie->fw_mon_size = 0; -} - -static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct page *page = NULL; - dma_addr_t phys; - u32 size = 0; - u8 power; - - if (!max_power) { - /* default max_power is maximum */ - max_power = 26; - } else { - max_power += 11; - } - - if (WARN(max_power > 26, - "External buffer size for monitor is too big %d, check the FW TLV\n", - max_power)) - return; - - if (trans_pcie->fw_mon_page) { - dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys, - trans_pcie->fw_mon_size, - DMA_FROM_DEVICE); - return; - } - - phys = 0; - for (power = max_power; power >= 11; power--) { - int order; - - size = BIT(power); - order = get_order(size); - page = alloc_pages(__GFP_COMP | __GFP_NOWARN | __GFP_ZERO, - order); - if (!page) - continue; - - phys = dma_map_page(trans->dev, page, 0, PAGE_SIZE << order, - DMA_FROM_DEVICE); - if (dma_mapping_error(trans->dev, phys)) { - __free_pages(page, order); - page = NULL; - continue; - } - IWL_INFO(trans, - "Allocated 0x%08x bytes (order %d) for firmware monitor.\n", - size, order); - break; - } - - if (WARN_ON_ONCE(!page)) - return; - - if (power != max_power) - IWL_ERR(trans, - "Sorry - debug buffer is only %luK while you requested %luK\n", - (unsigned long)BIT(power - 10), - (unsigned long)BIT(max_power - 10)); - - trans_pcie->fw_mon_page = page; - trans_pcie->fw_mon_phys = phys; - trans_pcie->fw_mon_size = size; -} - -static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) -{ - iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, - ((reg & 0x0000ffff) | (2 << 28))); - return iwl_read32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG); -} - -static void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val) -{ - iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG, val); - iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, - ((reg & 0x0000ffff) | (3 << 28))); -} - -static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux) -{ - if (trans->cfg->apmg_not_supported) - return; - - if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold)) - iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VAUX, - ~APMG_PS_CTRL_MSK_PWR_SRC); - else - iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, - ~APMG_PS_CTRL_MSK_PWR_SRC); -} - -/* PCI registers */ -#define PCI_CFG_RETRY_TIMEOUT 0x041 - -static void iwl_pcie_apm_config(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u16 lctl; - u16 cap; - - /* - * HW bug W/A for instability in PCIe bus L0S->L1 transition. - * Check if BIOS (or OS) enabled L1-ASPM on this device. - * If so (likely), disable L0S, so device moves directly L0->L1; - * costs negligible amount of power savings. - * If not (unlikely), enable L0S, so there is at least some - * power savings, even without L1. - */ - pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl); - if (lctl & PCI_EXP_LNKCTL_ASPM_L1) - iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); - else - iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); - trans->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S); - - pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_DEVCTL2, &cap); - trans->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN; - dev_info(trans->dev, "L1 %sabled - LTR %sabled\n", - (lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis", - trans->ltr_enabled ? "En" : "Dis"); -} - -/* - * Start up NIC's basic functionality after it has been reset - * (e.g. after platform boot, or shutdown via iwl_pcie_apm_stop()) - * NOTE: This does not load uCode nor start the embedded processor - */ -static int iwl_pcie_apm_init(struct iwl_trans *trans) -{ - int ret = 0; - IWL_DEBUG_INFO(trans, "Init card's basic functions\n"); - - /* - * Use "set_bit" below rather than "write", to preserve any hardware - * bits already set by default after reset. - */ - - /* Disable L0S exit timer (platform NMI Work/Around) */ - if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) - iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); - - /* - * Disable L0s without affecting L1; - * don't wait for ICH L0s (ICH bug W/A) - */ - iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); - - /* Set FH wait threshold to maximum (HW error during stress W/A) */ - iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); - - /* - * Enable HAP INTA (interrupt from management bus) to - * wake device's PCI Express link L1a -> L0s - */ - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); - - iwl_pcie_apm_config(trans); - - /* Configure analog phase-lock-loop before activating to D0A */ - if (trans->cfg->base_params->pll_cfg_val) - iwl_set_bit(trans, CSR_ANA_PLL_CFG, - trans->cfg->base_params->pll_cfg_val); - - /* - * Set "initialization complete" bit to move adapter from - * D0U* --> D0A* (powered-up active) state. - */ - iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - /* - * Wait for clock stabilization; once stabilized, access to - * device-internal resources is supported, e.g. iwl_write_prph() - * and accesses to uCode SRAM. - */ - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); - if (ret < 0) { - IWL_DEBUG_INFO(trans, "Failed to init the card\n"); - 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. - * - * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" - * bits do not disable clocks. This preserves any hardware - * bits already set by default in "CLK_CTRL_REG" after reset. - */ - if (!trans->cfg->apmg_not_supported) { - iwl_write_prph(trans, APMG_CLK_EN_REG, - APMG_CLK_VAL_DMA_CLK_RQT); - udelay(20); - - /* Disable L1-Active */ - iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - - /* Clear the interrupt in APMG if the NIC is in RFKILL */ - iwl_write_prph(trans, APMG_RTC_INT_STT_REG, - APMG_RTC_INT_STT_RFKILL); - } - - set_bit(STATUS_DEVICE_ENABLED, &trans->status); - -out: - return ret; -} - -/* - * Enable LP XTAL to avoid HW bug where device may consume much power if - * FW is not loaded after device reset. LP XTAL is disabled by default - * after device HW reset. Do it only if XTAL is fed by internal source. - * Configure device's "persistence" mode to avoid resetting XTAL again when - * SHRD_HW_RST occurs in S3. - */ -static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans) -{ - int ret; - u32 apmg_gp1_reg; - u32 apmg_xtal_cfg_reg; - u32 dl_cfg_reg; - - /* Force XTAL ON */ - __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_XTAL_ON); - - /* Reset entire device - do controller reset (results in SHRD_HW_RST) */ - iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - - udelay(10); - - /* - * Set "initialization complete" bit to move adapter from - * D0U* --> D0A* (powered-up active) state. - */ - iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - /* - * Wait for clock stabilization; once stabilized, access to - * device-internal resources is possible. - */ - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - 25000); - if (WARN_ON(ret < 0)) { - IWL_ERR(trans, "Access time out - failed to enable LP XTAL\n"); - /* Release XTAL ON request */ - __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_XTAL_ON); - return; - } - - /* - * Clear "disable persistence" to avoid LP XTAL resetting when - * SHRD_HW_RST is applied in S3. - */ - iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_PERSIST_DIS); - - /* - * Force APMG XTAL to be active to prevent its disabling by HW - * caused by APMG idle state. - */ - apmg_xtal_cfg_reg = iwl_trans_pcie_read_shr(trans, - SHR_APMG_XTAL_CFG_REG); - iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, - apmg_xtal_cfg_reg | - SHR_APMG_XTAL_CFG_XTAL_ON_REQ); - - /* - * Reset entire device again - do controller reset (results in - * SHRD_HW_RST). Turn MAC off before proceeding. - */ - iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - - udelay(10); - - /* Enable LP XTAL by indirect access through CSR */ - apmg_gp1_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_GP1_REG); - iwl_trans_pcie_write_shr(trans, SHR_APMG_GP1_REG, apmg_gp1_reg | - SHR_APMG_GP1_WF_XTAL_LP_EN | - SHR_APMG_GP1_CHICKEN_BIT_SELECT); - - /* Clear delay line clock power up */ - dl_cfg_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_DL_CFG_REG); - iwl_trans_pcie_write_shr(trans, SHR_APMG_DL_CFG_REG, dl_cfg_reg & - ~SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP); - - /* - * Enable persistence mode to avoid LP XTAL resetting when - * SHRD_HW_RST is applied in S3. - */ - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PERSIST_MODE); - - /* - * Clear "initialization complete" bit to move adapter from - * D0A* (powered-up Active) --> D0U* (Uninitialized) state. - */ - iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - /* Activates XTAL resources monitor */ - __iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG, - CSR_MONITOR_XTAL_RESOURCES); - - /* Release XTAL ON request */ - __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_XTAL_ON); - udelay(10); - - /* Release APMG XTAL */ - iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, - apmg_xtal_cfg_reg & - ~SHR_APMG_XTAL_CFG_XTAL_ON_REQ); -} - -static int iwl_pcie_apm_stop_master(struct iwl_trans *trans) -{ - int ret = 0; - - /* stop device's busmaster DMA activity */ - iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); - - ret = iwl_poll_bit(trans, CSR_RESET, - CSR_RESET_REG_FLAG_MASTER_DISABLED, - CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); - if (ret < 0) - IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n"); - - IWL_DEBUG_INFO(trans, "stop master\n"); - - return ret; -} - -static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave) -{ - IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); - - if (op_mode_leave) { - if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) - iwl_pcie_apm_init(trans); - - /* inform ME that we are leaving */ - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) - iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_WAKE_ME); - else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { - iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, - CSR_RESET_LINK_PWR_MGMT_DISABLED); - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PREPARE | - CSR_HW_IF_CONFIG_REG_ENABLE_PME); - mdelay(1); - iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, - CSR_RESET_LINK_PWR_MGMT_DISABLED); - } - mdelay(5); - } - - clear_bit(STATUS_DEVICE_ENABLED, &trans->status); - - /* Stop device's DMA activity */ - iwl_pcie_apm_stop_master(trans); - - if (trans->cfg->lp_xtal_workaround) { - iwl_pcie_apm_lp_xtal_enable(trans); - return; - } - - /* Reset the entire device */ - iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - - udelay(10); - - /* - * Clear "initialization complete" bit to move adapter from - * D0A* (powered-up Active) --> D0U* (Uninitialized) state. - */ - iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); -} - -static int iwl_pcie_nic_init(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - /* nic_init */ - spin_lock(&trans_pcie->irq_lock); - iwl_pcie_apm_init(trans); - - spin_unlock(&trans_pcie->irq_lock); - - iwl_pcie_set_pwr(trans, false); - - iwl_op_mode_nic_config(trans->op_mode); - - /* Allocate the RX queue, or reset if it is already allocated */ - iwl_pcie_rx_init(trans); - - /* Allocate or reset and init all Tx and Command queues */ - if (iwl_pcie_tx_init(trans)) - return -ENOMEM; - - if (trans->cfg->base_params->shadow_reg_enable) { - /* enable shadow regs in HW */ - iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); - IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n"); - } - - return 0; -} - -#define HW_READY_TIMEOUT (50) - -/* Note: returns poll_bit return value, which is >= 0 if success */ -static int iwl_pcie_set_hw_ready(struct iwl_trans *trans) -{ - int ret; - - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); - - /* See if we got it */ - ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, - HW_READY_TIMEOUT); - - if (ret >= 0) - iwl_set_bit(trans, CSR_MBOX_SET_REG, CSR_MBOX_SET_REG_OS_ALIVE); - - IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : ""); - return ret; -} - -/* Note: returns standard 0/-ERROR code */ -static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) -{ - int ret; - int t = 0; - int iter; - - IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); - - ret = iwl_pcie_set_hw_ready(trans); - /* If the card is ready, exit 0 */ - if (ret >= 0) - return 0; - - iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, - CSR_RESET_LINK_PWR_MGMT_DISABLED); - msleep(1); - - for (iter = 0; iter < 10; iter++) { - /* If HW is not ready, prepare the conditions to check again */ - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PREPARE); - - do { - ret = iwl_pcie_set_hw_ready(trans); - if (ret >= 0) - return 0; - - usleep_range(200, 1000); - t += 200; - } while (t < 150000); - msleep(25); - } - - IWL_ERR(trans, "Couldn't prepare the card\n"); - - return ret; -} - -/* - * ucode - */ -static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr, - dma_addr_t phy_addr, u32 byte_cnt) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ret; - - trans_pcie->ucode_write_complete = false; - - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); - - iwl_write_direct32(trans, - FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), - dst_addr); - - iwl_write_direct32(trans, - FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), - phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); - - iwl_write_direct32(trans, - FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), - (iwl_get_dma_hi_addr(phy_addr) - << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); - - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), - 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | - 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | - FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); - - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | - FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); - - ret = wait_event_timeout(trans_pcie->ucode_write_waitq, - trans_pcie->ucode_write_complete, 5 * HZ); - if (!ret) { - IWL_ERR(trans, "Failed to load firmware chunk!\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, - const struct fw_desc *section) -{ - u8 *v_addr; - dma_addr_t p_addr; - u32 offset, chunk_sz = min_t(u32, FH_MEM_TB_MAX_LENGTH, section->len); - int ret = 0; - - IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", - section_num); - - v_addr = dma_alloc_coherent(trans->dev, chunk_sz, &p_addr, - GFP_KERNEL | __GFP_NOWARN); - if (!v_addr) { - IWL_DEBUG_INFO(trans, "Falling back to small chunks of DMA\n"); - chunk_sz = PAGE_SIZE; - v_addr = dma_alloc_coherent(trans->dev, chunk_sz, - &p_addr, GFP_KERNEL); - if (!v_addr) - return -ENOMEM; - } - - for (offset = 0; offset < section->len; offset += chunk_sz) { - u32 copy_size, dst_addr; - bool extended_addr = false; - - copy_size = min_t(u32, chunk_sz, section->len - offset); - dst_addr = section->offset + offset; - - if (dst_addr >= IWL_FW_MEM_EXTENDED_START && - dst_addr <= IWL_FW_MEM_EXTENDED_END) - extended_addr = true; - - if (extended_addr) - iwl_set_bits_prph(trans, LMPM_CHICK, - LMPM_CHICK_EXTENDED_ADDR_SPACE); - - memcpy(v_addr, (u8 *)section->data + offset, copy_size); - ret = iwl_pcie_load_firmware_chunk(trans, dst_addr, p_addr, - copy_size); - - if (extended_addr) - iwl_clear_bits_prph(trans, LMPM_CHICK, - LMPM_CHICK_EXTENDED_ADDR_SPACE); - - if (ret) { - IWL_ERR(trans, - "Could not load the [%d] uCode section\n", - section_num); - break; - } - } - - dma_free_coherent(trans->dev, chunk_sz, v_addr, p_addr); - return ret; -} - -/* - * Driver Takes the ownership on secure machine before FW load - * and prevent race with the BT load. - * W/A for ROM bug. (should be remove in the next Si step) - */ -static int iwl_pcie_rsa_race_bug_wa(struct iwl_trans *trans) -{ - u32 val, loop = 1000; - - /* - * Check the RSA semaphore is accessible. - * If the HW isn't locked and the rsa semaphore isn't accessible, - * we are in trouble. - */ - val = iwl_read_prph(trans, PREG_AUX_BUS_WPROT_0); - if (val & (BIT(1) | BIT(17))) { - IWL_INFO(trans, - "can't access the RSA semaphore it is write protected\n"); - return 0; - } - - /* take ownership on the AUX IF */ - iwl_write_prph(trans, WFPM_CTRL_REG, WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK); - iwl_write_prph(trans, AUX_MISC_MASTER1_EN, AUX_MISC_MASTER1_EN_SBE_MSK); - - do { - iwl_write_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS, 0x1); - val = iwl_read_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS); - if (val == 0x1) { - iwl_write_prph(trans, RSA_ENABLE, 0); - return 0; - } - - udelay(10); - loop--; - } while (loop > 0); - - IWL_ERR(trans, "Failed to take ownership on secure machine\n"); - return -EIO; -} - -static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans, - const struct fw_img *image, - int cpu, - int *first_ucode_section) -{ - int shift_param; - int i, ret = 0, sec_num = 0x1; - u32 val, last_read_idx = 0; - - if (cpu == 1) { - shift_param = 0; - *first_ucode_section = 0; - } else { - shift_param = 16; - (*first_ucode_section)++; - } - - for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) { - last_read_idx = i; - - /* - * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between - * CPU1 to CPU2. - * PAGING_SEPARATOR_SECTION delimiter - separate between - * CPU2 non paged to CPU2 paging sec. - */ - if (!image->sec[i].data || - image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION || - image->sec[i].offset == PAGING_SEPARATOR_SECTION) { - IWL_DEBUG_FW(trans, - "Break since Data not valid or Empty section, sec = %d\n", - i); - break; - } - - ret = iwl_pcie_load_section(trans, i, &image->sec[i]); - if (ret) - return ret; - - /* Notify the ucode of the loaded section number and status */ - val = iwl_read_direct32(trans, FH_UCODE_LOAD_STATUS); - val = val | (sec_num << shift_param); - iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, val); - sec_num = (sec_num << 1) | 0x1; - } - - *first_ucode_section = last_read_idx; - - if (cpu == 1) - iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFF); - else - iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF); - - return 0; -} - -static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans, - const struct fw_img *image, - int cpu, - int *first_ucode_section) -{ - int shift_param; - int i, ret = 0; - u32 last_read_idx = 0; - - if (cpu == 1) { - shift_param = 0; - *first_ucode_section = 0; - } else { - shift_param = 16; - (*first_ucode_section)++; - } - - for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) { - last_read_idx = i; - - /* - * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between - * CPU1 to CPU2. - * PAGING_SEPARATOR_SECTION delimiter - separate between - * CPU2 non paged to CPU2 paging sec. - */ - if (!image->sec[i].data || - image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION || - image->sec[i].offset == PAGING_SEPARATOR_SECTION) { - IWL_DEBUG_FW(trans, - "Break since Data not valid or Empty section, sec = %d\n", - i); - break; - } - - ret = iwl_pcie_load_section(trans, i, &image->sec[i]); - if (ret) - return ret; - } - - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) - iwl_set_bits_prph(trans, - CSR_UCODE_LOAD_STATUS_ADDR, - (LMPM_CPU_UCODE_LOADING_COMPLETED | - LMPM_CPU_HDRS_LOADING_COMPLETED | - LMPM_CPU_UCODE_LOADING_STARTED) << - shift_param); - - *first_ucode_section = last_read_idx; - - return 0; -} - -static void iwl_pcie_apply_destination(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv; - int i; - - if (dest->version) - IWL_ERR(trans, - "DBG DEST version is %d - expect issues\n", - dest->version); - - IWL_INFO(trans, "Applying debug destination %s\n", - get_fw_dbg_mode_string(dest->monitor_mode)); - - if (dest->monitor_mode == EXTERNAL_MODE) - iwl_pcie_alloc_fw_monitor(trans, dest->size_power); - else - IWL_WARN(trans, "PCI should have external buffer debug\n"); - - for (i = 0; i < trans->dbg_dest_reg_num; i++) { - u32 addr = le32_to_cpu(dest->reg_ops[i].addr); - u32 val = le32_to_cpu(dest->reg_ops[i].val); - - switch (dest->reg_ops[i].op) { - case CSR_ASSIGN: - iwl_write32(trans, addr, val); - break; - case CSR_SETBIT: - iwl_set_bit(trans, addr, BIT(val)); - break; - case CSR_CLEARBIT: - iwl_clear_bit(trans, addr, BIT(val)); - break; - case PRPH_ASSIGN: - iwl_write_prph(trans, addr, val); - break; - case PRPH_SETBIT: - iwl_set_bits_prph(trans, addr, BIT(val)); - break; - case PRPH_CLEARBIT: - iwl_clear_bits_prph(trans, addr, BIT(val)); - break; - case PRPH_BLOCKBIT: - if (iwl_read_prph(trans, addr) & BIT(val)) { - IWL_ERR(trans, - "BIT(%u) in address 0x%x is 1, stopping FW configuration\n", - val, addr); - goto monitor; - } - break; - default: - IWL_ERR(trans, "FW debug - unknown OP %d\n", - dest->reg_ops[i].op); - break; - } - } - -monitor: - if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) { - iwl_write_prph(trans, le32_to_cpu(dest->base_reg), - trans_pcie->fw_mon_phys >> dest->base_shift); - iwl_write_prph(trans, le32_to_cpu(dest->end_reg), - (trans_pcie->fw_mon_phys + - trans_pcie->fw_mon_size) >> dest->end_shift); - } -} - -static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, - const struct fw_img *image) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ret = 0; - int first_ucode_section; - - IWL_DEBUG_FW(trans, "working with %s CPU\n", - image->is_dual_cpus ? "Dual" : "Single"); - - /* load to FW the binary non secured sections of CPU1 */ - ret = iwl_pcie_load_cpu_sections(trans, image, 1, &first_ucode_section); - if (ret) - return ret; - - if (image->is_dual_cpus) { - /* set CPU2 header address */ - iwl_write_prph(trans, - LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR, - LMPM_SECURE_CPU2_HDR_MEM_SPACE); - - /* load to FW the binary sections of CPU2 */ - ret = iwl_pcie_load_cpu_sections(trans, image, 2, - &first_ucode_section); - if (ret) - return ret; - } - - /* supported for 7000 only for the moment */ - if (iwlwifi_mod_params.fw_monitor && - trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) { - iwl_pcie_alloc_fw_monitor(trans, 0); - - if (trans_pcie->fw_mon_size) { - iwl_write_prph(trans, MON_BUFF_BASE_ADDR, - trans_pcie->fw_mon_phys >> 4); - iwl_write_prph(trans, MON_BUFF_END_ADDR, - (trans_pcie->fw_mon_phys + - trans_pcie->fw_mon_size) >> 4); - } - } else if (trans->dbg_dest_tlv) { - iwl_pcie_apply_destination(trans); - } - - /* release CPU reset */ - iwl_write32(trans, CSR_RESET, 0); - - return 0; -} - -static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans, - const struct fw_img *image) -{ - int ret = 0; - int first_ucode_section; - - IWL_DEBUG_FW(trans, "working with %s CPU\n", - image->is_dual_cpus ? "Dual" : "Single"); - - if (trans->dbg_dest_tlv) - iwl_pcie_apply_destination(trans); - - /* TODO: remove in the next Si step */ - ret = iwl_pcie_rsa_race_bug_wa(trans); - if (ret) - return ret; - - /* configure the ucode to be ready to get the secured image */ - /* release CPU reset */ - iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); - - /* load to FW the binary Secured sections of CPU1 */ - ret = iwl_pcie_load_cpu_sections_8000(trans, image, 1, - &first_ucode_section); - if (ret) - return ret; - - /* load to FW the binary sections of CPU2 */ - return iwl_pcie_load_cpu_sections_8000(trans, image, 2, - &first_ucode_section); -} - -static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, - const struct fw_img *fw, bool run_in_rfkill) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - bool hw_rfkill; - int ret; - - mutex_lock(&trans_pcie->mutex); - - /* Someone called stop_device, don't try to start_fw */ - if (trans_pcie->is_down) { - IWL_WARN(trans, - "Can't start_fw since the HW hasn't been started\n"); - ret = EIO; - goto out; - } - - /* This may fail if AMT took ownership of the device */ - if (iwl_pcie_prepare_card_hw(trans)) { - IWL_WARN(trans, "Exit HW not ready\n"); - ret = -EIO; - goto out; - } - - iwl_enable_rfkill_int(trans); - - /* If platform's RF_KILL switch is NOT set to KILL */ - hw_rfkill = iwl_is_rfkill_set(trans); - if (hw_rfkill) - set_bit(STATUS_RFKILL, &trans->status); - else - clear_bit(STATUS_RFKILL, &trans->status); - iwl_trans_pcie_rf_kill(trans, hw_rfkill); - if (hw_rfkill && !run_in_rfkill) { - ret = -ERFKILL; - goto out; - } - - iwl_write32(trans, CSR_INT, 0xFFFFFFFF); - - ret = iwl_pcie_nic_init(trans); - if (ret) { - IWL_ERR(trans, "Unable to init nic\n"); - goto out; - } - - /* make sure rfkill handshake bits are cleared */ - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - /* clear (again), then enable host interrupts */ - iwl_write32(trans, CSR_INT, 0xFFFFFFFF); - iwl_enable_interrupts(trans); - - /* really make sure rfkill handshake bits are cleared */ - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - - /* Load the given image to the HW */ - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) - ret = iwl_pcie_load_given_ucode_8000(trans, fw); - else - ret = iwl_pcie_load_given_ucode(trans, fw); - -out: - mutex_unlock(&trans_pcie->mutex); - return ret; -} - -static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) -{ - iwl_pcie_reset_ict(trans); - iwl_pcie_tx_start(trans, scd_addr); -} - -static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - bool hw_rfkill, was_hw_rfkill; - - lockdep_assert_held(&trans_pcie->mutex); - - if (trans_pcie->is_down) - return; - - trans_pcie->is_down = true; - - was_hw_rfkill = iwl_is_rfkill_set(trans); - - /* tell the device to stop sending interrupts */ - spin_lock(&trans_pcie->irq_lock); - iwl_disable_interrupts(trans); - spin_unlock(&trans_pcie->irq_lock); - - /* device going down, Stop using ICT table */ - iwl_pcie_disable_ict(trans); - - /* - * If a HW restart happens during firmware loading, - * then the firmware loading might call this function - * and later it might be called again due to the - * restart. So don't process again if the device is - * already dead. - */ - if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) { - IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n"); - iwl_pcie_tx_stop(trans); - iwl_pcie_rx_stop(trans); - - /* Power-down device's busmaster DMA clocks */ - if (!trans->cfg->apmg_not_supported) { - iwl_write_prph(trans, APMG_CLK_DIS_REG, - APMG_CLK_VAL_DMA_CLK_RQT); - udelay(5); - } - } - - /* Make sure (redundant) we've released our request to stay awake */ - iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - - /* Stop the device, and put it in low power state */ - iwl_pcie_apm_stop(trans, false); - - /* stop and reset the on-board processor */ - iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - udelay(20); - - /* - * Upon stop, the APM issues an interrupt if HW RF kill is set. - * This is a bug in certain verions of the hardware. - * Certain devices also keep sending HW RF kill interrupt all - * the time, unless the interrupt is ACKed even if the interrupt - * should be masked. Re-ACK all the interrupts here. - */ - spin_lock(&trans_pcie->irq_lock); - iwl_disable_interrupts(trans); - spin_unlock(&trans_pcie->irq_lock); - - - /* clear all status bits */ - clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); - clear_bit(STATUS_INT_ENABLED, &trans->status); - clear_bit(STATUS_TPOWER_PMI, &trans->status); - clear_bit(STATUS_RFKILL, &trans->status); - - /* - * Even if we stop the HW, we still want the RF kill - * interrupt - */ - iwl_enable_rfkill_int(trans); - - /* - * Check again since the RF kill state may have changed while - * all the interrupts were disabled, in this case we couldn't - * receive the RF kill interrupt and update the state in the - * op_mode. - * Don't call the op_mode if the rkfill state hasn't changed. - * This allows the op_mode to call stop_device from the rfkill - * notification without endless recursion. Under very rare - * circumstances, we might have a small recursion if the rfkill - * state changed exactly now while we were called from stop_device. - * This is very unlikely but can happen and is supported. - */ - hw_rfkill = iwl_is_rfkill_set(trans); - if (hw_rfkill) - set_bit(STATUS_RFKILL, &trans->status); - else - clear_bit(STATUS_RFKILL, &trans->status); - if (hw_rfkill != was_hw_rfkill) - iwl_trans_pcie_rf_kill(trans, hw_rfkill); - - /* re-take ownership to prevent other users from stealing the deivce */ - iwl_pcie_prepare_card_hw(trans); -} - -static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - mutex_lock(&trans_pcie->mutex); - _iwl_trans_pcie_stop_device(trans, low_power); - mutex_unlock(&trans_pcie->mutex); -} - -void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state) -{ - struct iwl_trans_pcie __maybe_unused *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - - lockdep_assert_held(&trans_pcie->mutex); - - if (iwl_op_mode_hw_rf_kill(trans->op_mode, state)) - _iwl_trans_pcie_stop_device(trans, true); -} - -static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (trans->wowlan_d0i3) { - /* Enable persistence mode to avoid reset */ - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PERSIST_MODE); - } - - iwl_disable_interrupts(trans); - - /* - * in testing mode, the host stays awake and the - * hardware won't be reset (not even partially) - */ - if (test) - return; - - iwl_pcie_disable_ict(trans); - - synchronize_irq(trans_pcie->pci_dev->irq); - - iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - if (!trans->wowlan_d0i3) { - /* - * reset TX queues -- some of their registers reset during S3 - * so if we don't reset everything here the D3 image would try - * to execute some invalid memory upon resume - */ - iwl_trans_pcie_tx_reset(trans); - } - - iwl_pcie_set_pwr(trans, true); -} - -static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, - enum iwl_d3_status *status, - bool test) -{ - u32 val; - int ret; - - if (test) { - iwl_enable_interrupts(trans); - *status = IWL_D3_STATUS_ALIVE; - return 0; - } - - /* - * Also enables interrupts - none will happen as the device doesn't - * know we're waking it up, only when the opmode actually tells it - * after this call. - */ - iwl_pcie_reset_ict(trans); - - iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) - udelay(2); - - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - 25000); - if (ret < 0) { - IWL_ERR(trans, "Failed to resume the device (mac ready)\n"); - return ret; - } - - iwl_pcie_set_pwr(trans, false); - - if (trans->wowlan_d0i3) { - iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - } else { - iwl_trans_pcie_tx_reset(trans); - - ret = iwl_pcie_rx_init(trans); - if (ret) { - IWL_ERR(trans, - "Failed to resume the device (RX reset)\n"); - return ret; - } - } - - val = iwl_read32(trans, CSR_RESET); - if (val & CSR_RESET_REG_FLAG_NEVO_RESET) - *status = IWL_D3_STATUS_RESET; - else - *status = IWL_D3_STATUS_ALIVE; - - return 0; -} - -static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - bool hw_rfkill; - int err; - - lockdep_assert_held(&trans_pcie->mutex); - - err = iwl_pcie_prepare_card_hw(trans); - if (err) { - IWL_ERR(trans, "Error while preparing HW: %d\n", err); - return err; - } - - /* Reset the entire device */ - iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - - usleep_range(10, 15); - - iwl_pcie_apm_init(trans); - - /* From now on, the op_mode will be kept updated about RF kill state */ - iwl_enable_rfkill_int(trans); - - /* Set is_down to false here so that...*/ - trans_pcie->is_down = false; - - hw_rfkill = iwl_is_rfkill_set(trans); - if (hw_rfkill) - set_bit(STATUS_RFKILL, &trans->status); - else - clear_bit(STATUS_RFKILL, &trans->status); - /* ... rfkill can call stop_device and set it false if needed */ - iwl_trans_pcie_rf_kill(trans, hw_rfkill); - - return 0; -} - -static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ret; - - mutex_lock(&trans_pcie->mutex); - ret = _iwl_trans_pcie_start_hw(trans, low_power); - mutex_unlock(&trans_pcie->mutex); - - return ret; -} - -static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - mutex_lock(&trans_pcie->mutex); - - /* disable interrupts - don't enable HW RF kill interrupt */ - spin_lock(&trans_pcie->irq_lock); - iwl_disable_interrupts(trans); - spin_unlock(&trans_pcie->irq_lock); - - iwl_pcie_apm_stop(trans, true); - - spin_lock(&trans_pcie->irq_lock); - iwl_disable_interrupts(trans); - spin_unlock(&trans_pcie->irq_lock); - - iwl_pcie_disable_ict(trans); - - mutex_unlock(&trans_pcie->mutex); - - synchronize_irq(trans_pcie->pci_dev->irq); -} - -static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) -{ - writeb(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); -} - -static void iwl_trans_pcie_write32(struct iwl_trans *trans, u32 ofs, u32 val) -{ - writel(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); -} - -static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs) -{ - return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); -} - -static u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg) -{ - iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_RADDR, - ((reg & 0x000FFFFF) | (3 << 24))); - return iwl_trans_pcie_read32(trans, HBUS_TARG_PRPH_RDAT); -} - -static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, - u32 val) -{ - iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WADDR, - ((addr & 0x000FFFFF) | (3 << 24))); - iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val); -} - -static int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget) -{ - WARN_ON(1); - return 0; -} - -static void iwl_trans_pcie_configure(struct iwl_trans *trans, - const struct iwl_trans_config *trans_cfg) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - trans_pcie->cmd_queue = trans_cfg->cmd_queue; - trans_pcie->cmd_fifo = trans_cfg->cmd_fifo; - trans_pcie->cmd_q_wdg_timeout = trans_cfg->cmd_q_wdg_timeout; - if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) - trans_pcie->n_no_reclaim_cmds = 0; - else - trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds; - if (trans_pcie->n_no_reclaim_cmds) - memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds, - trans_pcie->n_no_reclaim_cmds * sizeof(u8)); - - trans_pcie->rx_buf_size_8k = trans_cfg->rx_buf_size_8k; - if (trans_pcie->rx_buf_size_8k) - trans_pcie->rx_page_order = get_order(8 * 1024); - else - trans_pcie->rx_page_order = get_order(4 * 1024); - - trans_pcie->wide_cmd_header = trans_cfg->wide_cmd_header; - trans_pcie->command_names = trans_cfg->command_names; - trans_pcie->bc_table_dword = trans_cfg->bc_table_dword; - trans_pcie->scd_set_active = trans_cfg->scd_set_active; - - /* init ref_count to 1 (should be cleared when ucode is loaded) */ - trans_pcie->ref_count = 1; - - /* Initialize NAPI here - it should be before registering to mac80211 - * in the opmode but after the HW struct is allocated. - * As this function may be called again in some corner cases don't - * do anything if NAPI was already initialized. - */ - if (!trans_pcie->napi.poll) { - init_dummy_netdev(&trans_pcie->napi_dev); - netif_napi_add(&trans_pcie->napi_dev, &trans_pcie->napi, - iwl_pcie_dummy_napi_poll, 64); - } -} - -void iwl_trans_pcie_free(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - synchronize_irq(trans_pcie->pci_dev->irq); - - iwl_pcie_tx_free(trans); - iwl_pcie_rx_free(trans); - - free_irq(trans_pcie->pci_dev->irq, trans); - iwl_pcie_free_ict(trans); - - pci_disable_msi(trans_pcie->pci_dev); - iounmap(trans_pcie->hw_base); - pci_release_regions(trans_pcie->pci_dev); - pci_disable_device(trans_pcie->pci_dev); - - if (trans_pcie->napi.poll) - netif_napi_del(&trans_pcie->napi); - - iwl_pcie_free_fw_monitor(trans); - - iwl_trans_free(trans); -} - -static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) -{ - if (state) - set_bit(STATUS_TPOWER_PMI, &trans->status); - else - clear_bit(STATUS_TPOWER_PMI, &trans->status); -} - -static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, - unsigned long *flags) -{ - int ret; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - spin_lock_irqsave(&trans_pcie->reg_lock, *flags); - - if (trans_pcie->cmd_hold_nic_awake) - goto out; - - /* this bit wakes up the NIC */ - __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) - udelay(2); - - /* - * These bits say the device is running, and should keep running for - * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), - * but they do not indicate that embedded SRAM is restored yet; - * 3945 and 4965 have volatile SRAM, and must save/restore contents - * to/from host DRAM when sleeping/waking for power-saving. - * Each direction takes approximately 1/4 millisecond; with this - * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a - * series of register accesses are expected (e.g. reading Event Log), - * to keep device from sleeping. - * - * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that - * SRAM is okay/restored. We don't check that here because this call - * is just for hardware register access; but GP1 MAC_SLEEP check is a - * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). - * - * 5000 series and later (including 1000 series) have non-volatile SRAM, - * and do not save/restore SRAM when power cycling. - */ - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, - (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | - CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); - if (unlikely(ret < 0)) { - iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); - if (!silent) { - u32 val = iwl_read32(trans, CSR_GP_CNTRL); - WARN_ONCE(1, - "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n", - val); - spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags); - return false; - } - } - -out: - /* - * Fool sparse by faking we release the lock - sparse will - * track nic_access anyway. - */ - __release(&trans_pcie->reg_lock); - return true; -} - -static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans, - unsigned long *flags) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - lockdep_assert_held(&trans_pcie->reg_lock); - - /* - * Fool sparse by faking we acquiring the lock - sparse will - * track nic_access anyway. - */ - __acquire(&trans_pcie->reg_lock); - - if (trans_pcie->cmd_hold_nic_awake) - goto out; - - __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - /* - * Above we read the CSR_GP_CNTRL register, which will flush - * any previous writes, but we need the write that clears the - * MAC_ACCESS_REQ bit to be performed before any other writes - * scheduled on different CPUs (after we drop reg_lock). - */ - mmiowb(); -out: - spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags); -} - -static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, - void *buf, int dwords) -{ - unsigned long flags; - int offs, ret = 0; - u32 *vals = buf; - - if (iwl_trans_grab_nic_access(trans, false, &flags)) { - iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr); - for (offs = 0; offs < dwords; offs++) - vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT); - iwl_trans_release_nic_access(trans, &flags); - } else { - ret = -EBUSY; - } - return ret; -} - -static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, - const void *buf, int dwords) -{ - unsigned long flags; - int offs, ret = 0; - const u32 *vals = buf; - - if (iwl_trans_grab_nic_access(trans, false, &flags)) { - iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr); - for (offs = 0; offs < dwords; offs++) - iwl_write32(trans, HBUS_TARG_MEM_WDAT, - vals ? vals[offs] : 0); - iwl_trans_release_nic_access(trans, &flags); - } else { - ret = -EBUSY; - } - return ret; -} - -static void iwl_trans_pcie_freeze_txq_timer(struct iwl_trans *trans, - unsigned long txqs, - bool freeze) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int queue; - - for_each_set_bit(queue, &txqs, BITS_PER_LONG) { - struct iwl_txq *txq = &trans_pcie->txq[queue]; - unsigned long now; - - spin_lock_bh(&txq->lock); - - now = jiffies; - - if (txq->frozen == freeze) - goto next_queue; - - IWL_DEBUG_TX_QUEUES(trans, "%s TXQ %d\n", - freeze ? "Freezing" : "Waking", queue); - - txq->frozen = freeze; - - if (txq->q.read_ptr == txq->q.write_ptr) - goto next_queue; - - if (freeze) { - if (unlikely(time_after(now, - txq->stuck_timer.expires))) { - /* - * The timer should have fired, maybe it is - * spinning right now on the lock. - */ - goto next_queue; - } - /* remember how long until the timer fires */ - txq->frozen_expiry_remainder = - txq->stuck_timer.expires - now; - del_timer(&txq->stuck_timer); - goto next_queue; - } - - /* - * Wake a non-empty queue -> arm timer with the - * remainder before it froze - */ - mod_timer(&txq->stuck_timer, - now + txq->frozen_expiry_remainder); - -next_queue: - spin_unlock_bh(&txq->lock); - } -} - -#define IWL_FLUSH_WAIT_MS 2000 - -static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq; - struct iwl_queue *q; - int cnt; - unsigned long now = jiffies; - u32 scd_sram_addr; - u8 buf[16]; - int ret = 0; - - /* waiting for all the tx frames complete might take a while */ - for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { - u8 wr_ptr; - - if (cnt == trans_pcie->cmd_queue) - continue; - if (!test_bit(cnt, trans_pcie->queue_used)) - continue; - if (!(BIT(cnt) & txq_bm)) - continue; - - IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", cnt); - txq = &trans_pcie->txq[cnt]; - q = &txq->q; - wr_ptr = ACCESS_ONCE(q->write_ptr); - - while (q->read_ptr != ACCESS_ONCE(q->write_ptr) && - !time_after(jiffies, - now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) { - u8 write_ptr = ACCESS_ONCE(q->write_ptr); - - if (WARN_ONCE(wr_ptr != write_ptr, - "WR pointer moved while flushing %d -> %d\n", - wr_ptr, write_ptr)) - return -ETIMEDOUT; - msleep(1); - } - - if (q->read_ptr != q->write_ptr) { - IWL_ERR(trans, - "fail to flush all tx fifo queues Q %d\n", cnt); - ret = -ETIMEDOUT; - break; - } - IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", cnt); - } - - if (!ret) - return 0; - - IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", - txq->q.read_ptr, txq->q.write_ptr); - - scd_sram_addr = trans_pcie->scd_base_addr + - SCD_TX_STTS_QUEUE_OFFSET(txq->q.id); - iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); - - iwl_print_hex_error(trans, buf, sizeof(buf)); - - for (cnt = 0; cnt < FH_TCSR_CHNL_NUM; cnt++) - IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", cnt, - iwl_read_direct32(trans, FH_TX_TRB_REG(cnt))); - - for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { - u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(cnt)); - u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; - bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); - u32 tbl_dw = - iwl_trans_read_mem32(trans, trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE(cnt)); - - if (cnt & 0x1) - tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; - else - tbl_dw = tbl_dw & 0x0000FFFF; - - IWL_ERR(trans, - "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", - cnt, active ? "" : "in", fifo, tbl_dw, - iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) & - (TFD_QUEUE_SIZE_MAX - 1), - iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt))); - } - - return ret; -} - -static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, - u32 mask, u32 value) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - - spin_lock_irqsave(&trans_pcie->reg_lock, flags); - __iwl_trans_pcie_set_bits_mask(trans, reg, mask, value); - spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); -} - -void iwl_trans_pcie_ref(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - - if (iwlwifi_mod_params.d0i3_disable) - return; - - spin_lock_irqsave(&trans_pcie->ref_lock, flags); - IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); - trans_pcie->ref_count++; - spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); -} - -void iwl_trans_pcie_unref(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - - if (iwlwifi_mod_params.d0i3_disable) - return; - - spin_lock_irqsave(&trans_pcie->ref_lock, flags); - IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); - if (WARN_ON_ONCE(trans_pcie->ref_count == 0)) { - spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); - return; - } - trans_pcie->ref_count--; - spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); -} - -static const char *get_csr_string(int cmd) -{ -#define IWL_CMD(x) case x: return #x - switch (cmd) { - IWL_CMD(CSR_HW_IF_CONFIG_REG); - IWL_CMD(CSR_INT_COALESCING); - IWL_CMD(CSR_INT); - IWL_CMD(CSR_INT_MASK); - IWL_CMD(CSR_FH_INT_STATUS); - IWL_CMD(CSR_GPIO_IN); - IWL_CMD(CSR_RESET); - IWL_CMD(CSR_GP_CNTRL); - IWL_CMD(CSR_HW_REV); - IWL_CMD(CSR_EEPROM_REG); - IWL_CMD(CSR_EEPROM_GP); - IWL_CMD(CSR_OTP_GP_REG); - IWL_CMD(CSR_GIO_REG); - IWL_CMD(CSR_GP_UCODE_REG); - IWL_CMD(CSR_GP_DRIVER_REG); - IWL_CMD(CSR_UCODE_DRV_GP1); - IWL_CMD(CSR_UCODE_DRV_GP2); - IWL_CMD(CSR_LED_REG); - IWL_CMD(CSR_DRAM_INT_TBL_REG); - IWL_CMD(CSR_GIO_CHICKEN_BITS); - IWL_CMD(CSR_ANA_PLL_CFG); - IWL_CMD(CSR_HW_REV_WA_REG); - IWL_CMD(CSR_MONITOR_STATUS_REG); - IWL_CMD(CSR_DBG_HPET_MEM_REG); - default: - return "UNKNOWN"; - } -#undef IWL_CMD -} - -void iwl_pcie_dump_csr(struct iwl_trans *trans) -{ - int i; - static const u32 csr_tbl[] = { - CSR_HW_IF_CONFIG_REG, - CSR_INT_COALESCING, - CSR_INT, - CSR_INT_MASK, - CSR_FH_INT_STATUS, - CSR_GPIO_IN, - CSR_RESET, - CSR_GP_CNTRL, - CSR_HW_REV, - CSR_EEPROM_REG, - CSR_EEPROM_GP, - CSR_OTP_GP_REG, - CSR_GIO_REG, - CSR_GP_UCODE_REG, - CSR_GP_DRIVER_REG, - CSR_UCODE_DRV_GP1, - CSR_UCODE_DRV_GP2, - CSR_LED_REG, - CSR_DRAM_INT_TBL_REG, - CSR_GIO_CHICKEN_BITS, - CSR_ANA_PLL_CFG, - CSR_MONITOR_STATUS_REG, - CSR_HW_REV_WA_REG, - CSR_DBG_HPET_MEM_REG - }; - IWL_ERR(trans, "CSR values:\n"); - IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is " - "CSR_INT_PERIODIC_REG)\n"); - for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) { - IWL_ERR(trans, " %25s: 0X%08x\n", - get_csr_string(csr_tbl[i]), - iwl_read32(trans, csr_tbl[i])); - } -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -/* create and remove of files */ -#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, trans, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ -} while (0) - -/* file operation */ -#define DEBUGFS_READ_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define DEBUGFS_WRITE_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq; - struct iwl_queue *q; - char *buf; - int pos = 0; - int cnt; - int ret; - size_t bufsz; - - bufsz = sizeof(char) * 75 * trans->cfg->base_params->num_of_queues; - - if (!trans_pcie->txq) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { - txq = &trans_pcie->txq[cnt]; - q = &txq->q; - pos += scnprintf(buf + pos, bufsz - pos, - "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d frozen=%d%s\n", - cnt, q->read_ptr, q->write_ptr, - !!test_bit(cnt, trans_pcie->queue_used), - !!test_bit(cnt, trans_pcie->queue_stopped), - txq->need_update, txq->frozen, - (cnt == trans_pcie->cmd_queue ? " HCMD" : "")); - } - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", - rxq->read); - pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", - rxq->write); - pos += scnprintf(buf + pos, bufsz - pos, "write_actual: %u\n", - rxq->write_actual); - pos += scnprintf(buf + pos, bufsz - pos, "need_update: %d\n", - rxq->need_update); - pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", - rxq->free_count); - if (rxq->rb_stts) { - pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", - le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF); - } else { - pos += scnprintf(buf + pos, bufsz - pos, - "closed_rb_num: Not Allocated\n"); - } - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_interrupt_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct isr_statistics *isr_stats = &trans_pcie->isr_stats; - - int pos = 0; - char *buf; - int bufsz = 24 * 64; /* 24 items * 64 char per item */ - ssize_t ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, - "Interrupt Statistics Report:\n"); - - pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", - isr_stats->hw); - pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", - isr_stats->sw); - if (isr_stats->sw || isr_stats->hw) { - pos += scnprintf(buf + pos, bufsz - pos, - "\tLast Restarting Code: 0x%X\n", - isr_stats->err_code); - } -#ifdef CONFIG_IWLWIFI_DEBUG - pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", - isr_stats->sch); - pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", - isr_stats->alive); -#endif - pos += scnprintf(buf + pos, bufsz - pos, - "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill); - - pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", - isr_stats->ctkill); - - pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", - isr_stats->wakeup); - - pos += scnprintf(buf + pos, bufsz - pos, - "Rx command responses:\t\t %u\n", isr_stats->rx); - - pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", - isr_stats->tx); - - pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", - isr_stats->unhandled); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_interrupt_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct isr_statistics *isr_stats = &trans_pcie->isr_stats; - - char buf[8]; - int buf_size; - u32 reset_flag; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x", &reset_flag) != 1) - return -EFAULT; - if (reset_flag == 0) - memset(isr_stats, 0, sizeof(*isr_stats)); - - return count; -} - -static ssize_t iwl_dbgfs_csr_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - char buf[8]; - int buf_size; - int csr; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &csr) != 1) - return -EFAULT; - - iwl_pcie_dump_csr(trans); - - return count; -} - -static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - char *buf = NULL; - ssize_t ret; - - ret = iwl_dump_fh(trans, &buf); - if (ret < 0) - return ret; - if (!buf) - return -EINVAL; - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); - kfree(buf); - return ret; -} - -DEBUGFS_READ_WRITE_FILE_OPS(interrupt); -DEBUGFS_READ_FILE_OPS(fh_reg); -DEBUGFS_READ_FILE_OPS(rx_queue); -DEBUGFS_READ_FILE_OPS(tx_queue); -DEBUGFS_WRITE_FILE_OPS(csr); - -/* - * Create the debugfs files and directories - * - */ -static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) -{ - DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR); - DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR); - DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(csr, dir, S_IWUSR); - DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR); - return 0; - -err: - IWL_ERR(trans, "failed to create the trans debugfs entry\n"); - return -ENOMEM; -} -#else -static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) -{ - return 0; -} -#endif /*CONFIG_IWLWIFI_DEBUGFS */ - -static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd) -{ - u32 cmdlen = 0; - int i; - - for (i = 0; i < IWL_NUM_OF_TBS; i++) - cmdlen += iwl_pcie_tfd_tb_get_len(tfd, i); - - return cmdlen; -} - -static const struct { - u32 start, end; -} iwl_prph_dump_addr[] = { - { .start = 0x00a00000, .end = 0x00a00000 }, - { .start = 0x00a0000c, .end = 0x00a00024 }, - { .start = 0x00a0002c, .end = 0x00a0003c }, - { .start = 0x00a00410, .end = 0x00a00418 }, - { .start = 0x00a00420, .end = 0x00a00420 }, - { .start = 0x00a00428, .end = 0x00a00428 }, - { .start = 0x00a00430, .end = 0x00a0043c }, - { .start = 0x00a00444, .end = 0x00a00444 }, - { .start = 0x00a004c0, .end = 0x00a004cc }, - { .start = 0x00a004d8, .end = 0x00a004d8 }, - { .start = 0x00a004e0, .end = 0x00a004f0 }, - { .start = 0x00a00840, .end = 0x00a00840 }, - { .start = 0x00a00850, .end = 0x00a00858 }, - { .start = 0x00a01004, .end = 0x00a01008 }, - { .start = 0x00a01010, .end = 0x00a01010 }, - { .start = 0x00a01018, .end = 0x00a01018 }, - { .start = 0x00a01024, .end = 0x00a01024 }, - { .start = 0x00a0102c, .end = 0x00a01034 }, - { .start = 0x00a0103c, .end = 0x00a01040 }, - { .start = 0x00a01048, .end = 0x00a01094 }, - { .start = 0x00a01c00, .end = 0x00a01c20 }, - { .start = 0x00a01c58, .end = 0x00a01c58 }, - { .start = 0x00a01c7c, .end = 0x00a01c7c }, - { .start = 0x00a01c28, .end = 0x00a01c54 }, - { .start = 0x00a01c5c, .end = 0x00a01c5c }, - { .start = 0x00a01c60, .end = 0x00a01cdc }, - { .start = 0x00a01ce0, .end = 0x00a01d0c }, - { .start = 0x00a01d18, .end = 0x00a01d20 }, - { .start = 0x00a01d2c, .end = 0x00a01d30 }, - { .start = 0x00a01d40, .end = 0x00a01d5c }, - { .start = 0x00a01d80, .end = 0x00a01d80 }, - { .start = 0x00a01d98, .end = 0x00a01d9c }, - { .start = 0x00a01da8, .end = 0x00a01da8 }, - { .start = 0x00a01db8, .end = 0x00a01df4 }, - { .start = 0x00a01dc0, .end = 0x00a01dfc }, - { .start = 0x00a01e00, .end = 0x00a01e2c }, - { .start = 0x00a01e40, .end = 0x00a01e60 }, - { .start = 0x00a01e68, .end = 0x00a01e6c }, - { .start = 0x00a01e74, .end = 0x00a01e74 }, - { .start = 0x00a01e84, .end = 0x00a01e90 }, - { .start = 0x00a01e9c, .end = 0x00a01ec4 }, - { .start = 0x00a01ed0, .end = 0x00a01ee0 }, - { .start = 0x00a01f00, .end = 0x00a01f1c }, - { .start = 0x00a01f44, .end = 0x00a01ffc }, - { .start = 0x00a02000, .end = 0x00a02048 }, - { .start = 0x00a02068, .end = 0x00a020f0 }, - { .start = 0x00a02100, .end = 0x00a02118 }, - { .start = 0x00a02140, .end = 0x00a0214c }, - { .start = 0x00a02168, .end = 0x00a0218c }, - { .start = 0x00a021c0, .end = 0x00a021c0 }, - { .start = 0x00a02400, .end = 0x00a02410 }, - { .start = 0x00a02418, .end = 0x00a02420 }, - { .start = 0x00a02428, .end = 0x00a0242c }, - { .start = 0x00a02434, .end = 0x00a02434 }, - { .start = 0x00a02440, .end = 0x00a02460 }, - { .start = 0x00a02468, .end = 0x00a024b0 }, - { .start = 0x00a024c8, .end = 0x00a024cc }, - { .start = 0x00a02500, .end = 0x00a02504 }, - { .start = 0x00a0250c, .end = 0x00a02510 }, - { .start = 0x00a02540, .end = 0x00a02554 }, - { .start = 0x00a02580, .end = 0x00a025f4 }, - { .start = 0x00a02600, .end = 0x00a0260c }, - { .start = 0x00a02648, .end = 0x00a02650 }, - { .start = 0x00a02680, .end = 0x00a02680 }, - { .start = 0x00a026c0, .end = 0x00a026d0 }, - { .start = 0x00a02700, .end = 0x00a0270c }, - { .start = 0x00a02804, .end = 0x00a02804 }, - { .start = 0x00a02818, .end = 0x00a0281c }, - { .start = 0x00a02c00, .end = 0x00a02db4 }, - { .start = 0x00a02df4, .end = 0x00a02fb0 }, - { .start = 0x00a03000, .end = 0x00a03014 }, - { .start = 0x00a0301c, .end = 0x00a0302c }, - { .start = 0x00a03034, .end = 0x00a03038 }, - { .start = 0x00a03040, .end = 0x00a03048 }, - { .start = 0x00a03060, .end = 0x00a03068 }, - { .start = 0x00a03070, .end = 0x00a03074 }, - { .start = 0x00a0307c, .end = 0x00a0307c }, - { .start = 0x00a03080, .end = 0x00a03084 }, - { .start = 0x00a0308c, .end = 0x00a03090 }, - { .start = 0x00a03098, .end = 0x00a03098 }, - { .start = 0x00a030a0, .end = 0x00a030a0 }, - { .start = 0x00a030a8, .end = 0x00a030b4 }, - { .start = 0x00a030bc, .end = 0x00a030bc }, - { .start = 0x00a030c0, .end = 0x00a0312c }, - { .start = 0x00a03c00, .end = 0x00a03c5c }, - { .start = 0x00a04400, .end = 0x00a04454 }, - { .start = 0x00a04460, .end = 0x00a04474 }, - { .start = 0x00a044c0, .end = 0x00a044ec }, - { .start = 0x00a04500, .end = 0x00a04504 }, - { .start = 0x00a04510, .end = 0x00a04538 }, - { .start = 0x00a04540, .end = 0x00a04548 }, - { .start = 0x00a04560, .end = 0x00a0457c }, - { .start = 0x00a04590, .end = 0x00a04598 }, - { .start = 0x00a045c0, .end = 0x00a045f4 }, -}; - -static u32 iwl_trans_pcie_dump_prph(struct iwl_trans *trans, - struct iwl_fw_error_dump_data **data) -{ - struct iwl_fw_error_dump_prph *prph; - unsigned long flags; - u32 prph_len = 0, i; - - if (!iwl_trans_grab_nic_access(trans, false, &flags)) - return 0; - - for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4; - int reg; - __le32 *val; - - prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk; - - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); - (*data)->len = cpu_to_le32(sizeof(*prph) + - num_bytes_in_chunk); - prph = (void *)(*data)->data; - prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); - val = (void *)prph->data; - - for (reg = iwl_prph_dump_addr[i].start; - reg <= iwl_prph_dump_addr[i].end; - reg += 4) - *val++ = cpu_to_le32(iwl_trans_pcie_read_prph(trans, - reg)); - *data = iwl_fw_error_next_data(*data); - } - - iwl_trans_release_nic_access(trans, &flags); - - return prph_len; -} - -static u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans, - struct iwl_fw_error_dump_data **data, - int allocated_rb_nums) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int max_len = PAGE_SIZE << trans_pcie->rx_page_order; - struct iwl_rxq *rxq = &trans_pcie->rxq; - u32 i, r, j, rb_len = 0; - - spin_lock(&rxq->lock); - - r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF; - - for (i = rxq->read, j = 0; - i != r && j < allocated_rb_nums; - i = (i + 1) & RX_QUEUE_MASK, j++) { - struct iwl_rx_mem_buffer *rxb = rxq->queue[i]; - struct iwl_fw_error_dump_rb *rb; - - dma_unmap_page(trans->dev, rxb->page_dma, max_len, - DMA_FROM_DEVICE); - - rb_len += sizeof(**data) + sizeof(*rb) + max_len; - - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RB); - (*data)->len = cpu_to_le32(sizeof(*rb) + max_len); - rb = (void *)(*data)->data; - rb->index = cpu_to_le32(i); - memcpy(rb->data, page_address(rxb->page), max_len); - /* remap the page for the free benefit */ - rxb->page_dma = dma_map_page(trans->dev, rxb->page, 0, - max_len, - DMA_FROM_DEVICE); - - *data = iwl_fw_error_next_data(*data); - } - - spin_unlock(&rxq->lock); - - return rb_len; -} -#define IWL_CSR_TO_DUMP (0x250) - -static u32 iwl_trans_pcie_dump_csr(struct iwl_trans *trans, - struct iwl_fw_error_dump_data **data) -{ - u32 csr_len = sizeof(**data) + IWL_CSR_TO_DUMP; - __le32 *val; - int i; - - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_CSR); - (*data)->len = cpu_to_le32(IWL_CSR_TO_DUMP); - val = (void *)(*data)->data; - - for (i = 0; i < IWL_CSR_TO_DUMP; i += 4) - *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i)); - - *data = iwl_fw_error_next_data(*data); - - return csr_len; -} - -static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans, - struct iwl_fw_error_dump_data **data) -{ - u32 fh_regs_len = FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND; - unsigned long flags; - __le32 *val; - int i; - - if (!iwl_trans_grab_nic_access(trans, false, &flags)) - return 0; - - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FH_REGS); - (*data)->len = cpu_to_le32(fh_regs_len); - val = (void *)(*data)->data; - - for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; i += sizeof(u32)) - *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i)); - - iwl_trans_release_nic_access(trans, &flags); - - *data = iwl_fw_error_next_data(*data); - - return sizeof(**data) + fh_regs_len; -} - -static u32 -iwl_trans_pci_dump_marbh_monitor(struct iwl_trans *trans, - struct iwl_fw_error_dump_fw_mon *fw_mon_data, - u32 monitor_len) -{ - u32 buf_size_in_dwords = (monitor_len >> 2); - u32 *buffer = (u32 *)fw_mon_data->data; - unsigned long flags; - u32 i; - - if (!iwl_trans_grab_nic_access(trans, false, &flags)) - return 0; - - __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x1); - for (i = 0; i < buf_size_in_dwords; i++) - buffer[i] = __iwl_read_prph(trans, MON_DMARB_RD_DATA_ADDR); - __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x0); - - iwl_trans_release_nic_access(trans, &flags); - - return monitor_len; -} - -static u32 -iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, - struct iwl_fw_error_dump_data **data, - u32 monitor_len) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 len = 0; - - if ((trans_pcie->fw_mon_page && - trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) || - trans->dbg_dest_tlv) { - struct iwl_fw_error_dump_fw_mon *fw_mon_data; - u32 base, write_ptr, wrap_cnt; - - /* If there was a dest TLV - use the values from there */ - if (trans->dbg_dest_tlv) { - write_ptr = - le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg); - wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count); - base = le32_to_cpu(trans->dbg_dest_tlv->base_reg); - } else { - base = MON_BUFF_BASE_ADDR; - write_ptr = MON_BUFF_WRPTR; - wrap_cnt = MON_BUFF_CYCLE_CNT; - } - - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR); - fw_mon_data = (void *)(*data)->data; - fw_mon_data->fw_mon_wr_ptr = - cpu_to_le32(iwl_read_prph(trans, write_ptr)); - fw_mon_data->fw_mon_cycle_cnt = - cpu_to_le32(iwl_read_prph(trans, wrap_cnt)); - fw_mon_data->fw_mon_base_ptr = - cpu_to_le32(iwl_read_prph(trans, base)); - - len += sizeof(**data) + sizeof(*fw_mon_data); - if (trans_pcie->fw_mon_page) { - /* - * The firmware is now asserted, it won't write anything - * to the buffer. CPU can take ownership to fetch the - * data. The buffer will be handed back to the device - * before the firmware will be restarted. - */ - dma_sync_single_for_cpu(trans->dev, - trans_pcie->fw_mon_phys, - trans_pcie->fw_mon_size, - DMA_FROM_DEVICE); - memcpy(fw_mon_data->data, - page_address(trans_pcie->fw_mon_page), - trans_pcie->fw_mon_size); - - monitor_len = trans_pcie->fw_mon_size; - } else if (trans->dbg_dest_tlv->monitor_mode == SMEM_MODE) { - /* - * Update pointers to reflect actual values after - * shifting - */ - base = iwl_read_prph(trans, base) << - trans->dbg_dest_tlv->base_shift; - iwl_trans_read_mem(trans, base, fw_mon_data->data, - monitor_len / sizeof(u32)); - } else if (trans->dbg_dest_tlv->monitor_mode == MARBH_MODE) { - monitor_len = - iwl_trans_pci_dump_marbh_monitor(trans, - fw_mon_data, - monitor_len); - } else { - /* Didn't match anything - output no monitor data */ - monitor_len = 0; - } - - len += monitor_len; - (*data)->len = cpu_to_le32(monitor_len + sizeof(*fw_mon_data)); - } - - return len; -} - -static struct iwl_trans_dump_data -*iwl_trans_pcie_dump_data(struct iwl_trans *trans, - struct iwl_fw_dbg_trigger_tlv *trigger) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_fw_error_dump_data *data; - struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue]; - struct iwl_fw_error_dump_txcmd *txcmd; - struct iwl_trans_dump_data *dump_data; - u32 len, num_rbs; - u32 monitor_len; - int i, ptr; - bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status); - - /* transport dump header */ - len = sizeof(*dump_data); - - /* host commands */ - len += sizeof(*data) + - cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE); - - /* FW monitor */ - if (trans_pcie->fw_mon_page) { - len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) + - trans_pcie->fw_mon_size; - monitor_len = trans_pcie->fw_mon_size; - } else if (trans->dbg_dest_tlv) { - u32 base, end; - - base = le32_to_cpu(trans->dbg_dest_tlv->base_reg); - end = le32_to_cpu(trans->dbg_dest_tlv->end_reg); - - base = iwl_read_prph(trans, base) << - trans->dbg_dest_tlv->base_shift; - end = iwl_read_prph(trans, end) << - trans->dbg_dest_tlv->end_shift; - - /* Make "end" point to the actual end */ - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 || - trans->dbg_dest_tlv->monitor_mode == MARBH_MODE) - end += (1 << trans->dbg_dest_tlv->end_shift); - monitor_len = end - base; - len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) + - monitor_len; - } else { - monitor_len = 0; - } - - if (trigger && (trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)) { - dump_data = vzalloc(len); - if (!dump_data) - return NULL; - - data = (void *)dump_data->data; - len = iwl_trans_pcie_dump_monitor(trans, &data, monitor_len); - dump_data->len = len; - - return dump_data; - } - - /* CSR registers */ - len += sizeof(*data) + IWL_CSR_TO_DUMP; - - /* PRPH registers */ - for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4; - - len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_prph) + - num_bytes_in_chunk; - } - - /* FH registers */ - len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND); - - if (dump_rbs) { - /* RBs */ - num_rbs = le16_to_cpu(ACCESS_ONCE( - trans_pcie->rxq.rb_stts->closed_rb_num)) - & 0x0FFF; - num_rbs = (num_rbs - trans_pcie->rxq.read) & RX_QUEUE_MASK; - len += num_rbs * (sizeof(*data) + - sizeof(struct iwl_fw_error_dump_rb) + - (PAGE_SIZE << trans_pcie->rx_page_order)); - } - - dump_data = vzalloc(len); - if (!dump_data) - return NULL; - - len = 0; - data = (void *)dump_data->data; - data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD); - txcmd = (void *)data->data; - spin_lock_bh(&cmdq->lock); - ptr = cmdq->q.write_ptr; - for (i = 0; i < cmdq->q.n_window; i++) { - u8 idx = get_cmd_index(&cmdq->q, ptr); - u32 caplen, cmdlen; - - cmdlen = iwl_trans_pcie_get_cmdlen(&cmdq->tfds[ptr]); - caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen); - - if (cmdlen) { - len += sizeof(*txcmd) + caplen; - txcmd->cmdlen = cpu_to_le32(cmdlen); - txcmd->caplen = cpu_to_le32(caplen); - memcpy(txcmd->data, cmdq->entries[idx].cmd, caplen); - txcmd = (void *)((u8 *)txcmd->data + caplen); - } - - ptr = iwl_queue_dec_wrap(ptr); - } - spin_unlock_bh(&cmdq->lock); - - data->len = cpu_to_le32(len); - len += sizeof(*data); - data = iwl_fw_error_next_data(data); - - len += iwl_trans_pcie_dump_prph(trans, &data); - len += iwl_trans_pcie_dump_csr(trans, &data); - len += iwl_trans_pcie_fh_regs_dump(trans, &data); - if (dump_rbs) - len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs); - - len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len); - - dump_data->len = len; - - return dump_data; -} - -static const struct iwl_trans_ops trans_ops_pcie = { - .start_hw = iwl_trans_pcie_start_hw, - .op_mode_leave = iwl_trans_pcie_op_mode_leave, - .fw_alive = iwl_trans_pcie_fw_alive, - .start_fw = iwl_trans_pcie_start_fw, - .stop_device = iwl_trans_pcie_stop_device, - - .d3_suspend = iwl_trans_pcie_d3_suspend, - .d3_resume = iwl_trans_pcie_d3_resume, - - .send_cmd = iwl_trans_pcie_send_hcmd, - - .tx = iwl_trans_pcie_tx, - .reclaim = iwl_trans_pcie_reclaim, - - .txq_disable = iwl_trans_pcie_txq_disable, - .txq_enable = iwl_trans_pcie_txq_enable, - - .dbgfs_register = iwl_trans_pcie_dbgfs_register, - - .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, - .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, - - .write8 = iwl_trans_pcie_write8, - .write32 = iwl_trans_pcie_write32, - .read32 = iwl_trans_pcie_read32, - .read_prph = iwl_trans_pcie_read_prph, - .write_prph = iwl_trans_pcie_write_prph, - .read_mem = iwl_trans_pcie_read_mem, - .write_mem = iwl_trans_pcie_write_mem, - .configure = iwl_trans_pcie_configure, - .set_pmi = iwl_trans_pcie_set_pmi, - .grab_nic_access = iwl_trans_pcie_grab_nic_access, - .release_nic_access = iwl_trans_pcie_release_nic_access, - .set_bits_mask = iwl_trans_pcie_set_bits_mask, - - .ref = iwl_trans_pcie_ref, - .unref = iwl_trans_pcie_unref, - - .dump_data = iwl_trans_pcie_dump_data, -}; - -struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, - const struct pci_device_id *ent, - const struct iwl_cfg *cfg) -{ - struct iwl_trans_pcie *trans_pcie; - struct iwl_trans *trans; - u16 pci_cmd; - int ret; - - trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), - &pdev->dev, cfg, &trans_ops_pcie, 0); - if (!trans) - return ERR_PTR(-ENOMEM); - - trans->max_skb_frags = IWL_PCIE_MAX_FRAGS; - - trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - trans_pcie->trans = trans; - spin_lock_init(&trans_pcie->irq_lock); - spin_lock_init(&trans_pcie->reg_lock); - spin_lock_init(&trans_pcie->ref_lock); - mutex_init(&trans_pcie->mutex); - init_waitqueue_head(&trans_pcie->ucode_write_waitq); - - ret = pci_enable_device(pdev); - if (ret) - goto out_no_pci; - - if (!cfg->base_params->pcie_l1_allowed) { - /* - * W/A - seems to solve weird behavior. We need to remove this - * if we don't want to stay in L1 all the time. This wastes a - * lot of power. - */ - pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | - PCIE_LINK_STATE_L1 | - PCIE_LINK_STATE_CLKPM); - } - - pci_set_master(pdev); - - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); - if (!ret) - ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36)); - if (ret) { - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (!ret) - ret = pci_set_consistent_dma_mask(pdev, - DMA_BIT_MASK(32)); - /* both attempts failed: */ - if (ret) { - dev_err(&pdev->dev, "No suitable DMA available\n"); - goto out_pci_disable_device; - } - } - - ret = pci_request_regions(pdev, DRV_NAME); - if (ret) { - dev_err(&pdev->dev, "pci_request_regions failed\n"); - goto out_pci_disable_device; - } - - trans_pcie->hw_base = pci_ioremap_bar(pdev, 0); - if (!trans_pcie->hw_base) { - dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); - ret = -ENODEV; - goto out_pci_release_regions; - } - - /* We disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state */ - pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); - - trans->dev = &pdev->dev; - trans_pcie->pci_dev = pdev; - iwl_disable_interrupts(trans); - - ret = pci_enable_msi(pdev); - if (ret) { - dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", ret); - /* enable rfkill interrupt: hw bug w/a */ - pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); - if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { - pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; - pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); - } - } - - trans->hw_rev = iwl_read32(trans, CSR_HW_REV); - /* - * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have - * changed, and now the revision step also includes bit 0-1 (no more - * "dash" value). To keep hw_rev backwards compatible - we'll store it - * in the old format. - */ - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { - unsigned long flags; - - trans->hw_rev = (trans->hw_rev & 0xfff0) | - (CSR_HW_REV_STEP(trans->hw_rev << 2) << 2); - - ret = iwl_pcie_prepare_card_hw(trans); - if (ret) { - IWL_WARN(trans, "Exit HW not ready\n"); - goto out_pci_disable_msi; - } - - /* - * in-order to recognize C step driver should read chip version - * id located at the AUX bus MISC address space. - */ - iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - udelay(2); - - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - 25000); - if (ret < 0) { - IWL_DEBUG_INFO(trans, "Failed to wake up the nic\n"); - goto out_pci_disable_msi; - } - - if (iwl_trans_grab_nic_access(trans, false, &flags)) { - u32 hw_step; - - hw_step = __iwl_read_prph(trans, WFPM_CTRL_REG); - hw_step |= ENABLE_WFPM; - __iwl_write_prph(trans, WFPM_CTRL_REG, hw_step); - hw_step = __iwl_read_prph(trans, AUX_MISC_REG); - hw_step = (hw_step >> HW_STEP_LOCATION_BITS) & 0xF; - if (hw_step == 0x3) - trans->hw_rev = (trans->hw_rev & 0xFFFFFFF3) | - (SILICON_C_STEP << 2); - iwl_trans_release_nic_access(trans, &flags); - } - } - - trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; - snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), - "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); - - /* Initialize the wait queue for commands */ - init_waitqueue_head(&trans_pcie->wait_command_queue); - - ret = iwl_pcie_alloc_ict(trans); - if (ret) - goto out_pci_disable_msi; - - ret = request_threaded_irq(pdev->irq, iwl_pcie_isr, - iwl_pcie_irq_handler, - IRQF_SHARED, DRV_NAME, trans); - if (ret) { - IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq); - goto out_free_ict; - } - - trans_pcie->inta_mask = CSR_INI_SET_MASK; - trans->d0i3_mode = IWL_D0I3_MODE_ON_SUSPEND; - - return trans; - -out_free_ict: - iwl_pcie_free_ict(trans); -out_pci_disable_msi: - pci_disable_msi(pdev); -out_pci_release_regions: - pci_release_regions(pdev); -out_pci_disable_device: - pci_disable_device(pdev); -out_no_pci: - iwl_trans_free(trans); - return ERR_PTR(ret); -} diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c deleted file mode 100644 index a8c8a4a7420b..000000000000 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ /dev/null @@ -1,1988 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include - -#include "iwl-debug.h" -#include "iwl-csr.h" -#include "iwl-prph.h" -#include "iwl-io.h" -#include "iwl-scd.h" -#include "iwl-op-mode.h" -#include "internal.h" -/* FIXME: need to abstract out TX command (once we know what it looks like) */ -#include "dvm/commands.h" - -#define IWL_TX_CRC_SIZE 4 -#define IWL_TX_DELIMITER_SIZE 4 - -/*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** - * DMA services - * - * Theory of operation - * - * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer - * of buffer descriptors, each of which points to one or more data buffers for - * the device to read from or fill. Driver and device exchange status of each - * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty - * entries in each circular buffer, to protect against confusing empty and full - * queue states. - * - * The device reads or writes the data in the queues via the device's several - * DMA/FIFO channels. Each queue is mapped to a single DMA channel. - * - * For Tx queue, there are low mark and high mark limits. If, after queuing - * the packet for Tx, free space become < low mark, Tx queue stopped. When - * reclaiming packets (on 'tx done IRQ), if free space become > high mark, - * Tx queue resumed. - * - ***************************************************/ -static int iwl_queue_space(const struct iwl_queue *q) -{ - unsigned int max; - unsigned int used; - - /* - * To avoid ambiguity between empty and completely full queues, there - * should always be less than TFD_QUEUE_SIZE_MAX elements in the queue. - * If q->n_window is smaller than TFD_QUEUE_SIZE_MAX, there is no need - * to reserve any queue entries for this purpose. - */ - if (q->n_window < TFD_QUEUE_SIZE_MAX) - max = q->n_window; - else - max = TFD_QUEUE_SIZE_MAX - 1; - - /* - * TFD_QUEUE_SIZE_MAX is a power of 2, so the following is equivalent to - * modulo by TFD_QUEUE_SIZE_MAX and is well defined. - */ - used = (q->write_ptr - q->read_ptr) & (TFD_QUEUE_SIZE_MAX - 1); - - if (WARN_ON(used > max)) - return 0; - - return max - used; -} - -/* - * iwl_queue_init - Initialize queue's high/low-water and read/write indexes - */ -static int iwl_queue_init(struct iwl_queue *q, int slots_num, u32 id) -{ - q->n_window = slots_num; - q->id = id; - - /* slots_num must be power-of-two size, otherwise - * get_cmd_index is broken. */ - if (WARN_ON(!is_power_of_2(slots_num))) - return -EINVAL; - - q->low_mark = q->n_window / 4; - if (q->low_mark < 4) - q->low_mark = 4; - - q->high_mark = q->n_window / 8; - if (q->high_mark < 2) - q->high_mark = 2; - - q->write_ptr = 0; - q->read_ptr = 0; - - return 0; -} - -static int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr, size_t size) -{ - if (WARN_ON(ptr->addr)) - return -EINVAL; - - ptr->addr = dma_alloc_coherent(trans->dev, size, - &ptr->dma, GFP_KERNEL); - if (!ptr->addr) - return -ENOMEM; - ptr->size = size; - return 0; -} - -static void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr) -{ - if (unlikely(!ptr->addr)) - return; - - dma_free_coherent(trans->dev, ptr->size, ptr->addr, ptr->dma); - memset(ptr, 0, sizeof(*ptr)); -} - -static void iwl_pcie_txq_stuck_timer(unsigned long data) -{ - struct iwl_txq *txq = (void *)data; - struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; - struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); - u32 scd_sram_addr = trans_pcie->scd_base_addr + - SCD_TX_STTS_QUEUE_OFFSET(txq->q.id); - u8 buf[16]; - int i; - - spin_lock(&txq->lock); - /* check if triggered erroneously */ - if (txq->q.read_ptr == txq->q.write_ptr) { - spin_unlock(&txq->lock); - return; - } - spin_unlock(&txq->lock); - - IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id, - jiffies_to_msecs(txq->wd_timeout)); - IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", - txq->q.read_ptr, txq->q.write_ptr); - - iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); - - iwl_print_hex_error(trans, buf, sizeof(buf)); - - for (i = 0; i < FH_TCSR_CHNL_NUM; i++) - IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i, - iwl_read_direct32(trans, FH_TX_TRB_REG(i))); - - for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { - u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(i)); - u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; - bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); - u32 tbl_dw = - iwl_trans_read_mem32(trans, - trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE(i)); - - if (i & 0x1) - tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; - else - tbl_dw = tbl_dw & 0x0000FFFF; - - IWL_ERR(trans, - "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", - i, active ? "" : "in", fifo, tbl_dw, - iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) & - (TFD_QUEUE_SIZE_MAX - 1), - iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); - } - - iwl_force_nmi(trans); -} - -/* - * iwl_pcie_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array - */ -static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans, - struct iwl_txq *txq, u16 byte_cnt) -{ - struct iwlagn_scd_bc_tbl *scd_bc_tbl; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int write_ptr = txq->q.write_ptr; - int txq_id = txq->q.id; - u8 sec_ctl = 0; - u8 sta_id = 0; - u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE; - __le16 bc_ent; - struct iwl_tx_cmd *tx_cmd = - (void *) txq->entries[txq->q.write_ptr].cmd->payload; - - scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; - - sta_id = tx_cmd->sta_id; - sec_ctl = tx_cmd->sec_ctl; - - switch (sec_ctl & TX_CMD_SEC_MSK) { - case TX_CMD_SEC_CCM: - len += IEEE80211_CCMP_MIC_LEN; - break; - case TX_CMD_SEC_TKIP: - len += IEEE80211_TKIP_ICV_LEN; - break; - case TX_CMD_SEC_WEP: - len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN; - break; - } - - if (trans_pcie->bc_table_dword) - len = DIV_ROUND_UP(len, 4); - - if (WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX)) - return; - - bc_ent = cpu_to_le16(len | (sta_id << 12)); - - scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; - - if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) - scd_bc_tbl[txq_id]. - tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; -} - -static void iwl_pcie_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, - struct iwl_txq *txq) -{ - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; - int txq_id = txq->q.id; - int read_ptr = txq->q.read_ptr; - u8 sta_id = 0; - __le16 bc_ent; - struct iwl_tx_cmd *tx_cmd = - (void *)txq->entries[txq->q.read_ptr].cmd->payload; - - WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX); - - if (txq_id != trans_pcie->cmd_queue) - sta_id = tx_cmd->sta_id; - - bc_ent = cpu_to_le16(1 | (sta_id << 12)); - scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent; - - if (read_ptr < TFD_QUEUE_SIZE_BC_DUP) - scd_bc_tbl[txq_id]. - tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; -} - -/* - * iwl_pcie_txq_inc_wr_ptr - Send new write index to hardware - */ -static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, - struct iwl_txq *txq) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 reg = 0; - int txq_id = txq->q.id; - - lockdep_assert_held(&txq->lock); - - /* - * explicitly wake up the NIC if: - * 1. shadow registers aren't enabled - * 2. NIC is woken up for CMD regardless of shadow outside this function - * 3. there is a chance that the NIC is asleep - */ - if (!trans->cfg->base_params->shadow_reg_enable && - txq_id != trans_pcie->cmd_queue && - test_bit(STATUS_TPOWER_PMI, &trans->status)) { - /* - * wake up nic if it's powered down ... - * uCode will wake up, and interrupt us again, so next - * time we'll skip this part. - */ - reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); - - if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { - IWL_DEBUG_INFO(trans, "Tx queue %d requesting wakeup, GP1 = 0x%x\n", - txq_id, reg); - iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - txq->need_update = true; - return; - } - } - - /* - * if not in power-save mode, uCode will never sleep when we're - * trying to tx (during RFKILL, we're not trying to tx). - */ - IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->q.write_ptr); - iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); -} - -void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int i; - - for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { - struct iwl_txq *txq = &trans_pcie->txq[i]; - - spin_lock_bh(&txq->lock); - if (trans_pcie->txq[i].need_update) { - iwl_pcie_txq_inc_wr_ptr(trans, txq); - trans_pcie->txq[i].need_update = false; - } - spin_unlock_bh(&txq->lock); - } -} - -static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) -{ - struct iwl_tfd_tb *tb = &tfd->tbs[idx]; - - dma_addr_t addr = get_unaligned_le32(&tb->lo); - if (sizeof(dma_addr_t) > sizeof(u32)) - addr |= - ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16; - - return addr; -} - -static inline void iwl_pcie_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, - dma_addr_t addr, u16 len) -{ - struct iwl_tfd_tb *tb = &tfd->tbs[idx]; - u16 hi_n_len = len << 4; - - put_unaligned_le32(addr, &tb->lo); - if (sizeof(dma_addr_t) > sizeof(u32)) - hi_n_len |= ((addr >> 16) >> 16) & 0xF; - - tb->hi_n_len = cpu_to_le16(hi_n_len); - - tfd->num_tbs = idx + 1; -} - -static inline u8 iwl_pcie_tfd_get_num_tbs(struct iwl_tfd *tfd) -{ - return tfd->num_tbs & 0x1f; -} - -static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, - struct iwl_cmd_meta *meta, - struct iwl_tfd *tfd) -{ - int i; - int num_tbs; - - /* Sanity check on number of chunks */ - num_tbs = iwl_pcie_tfd_get_num_tbs(tfd); - - if (num_tbs >= IWL_NUM_OF_TBS) { - IWL_ERR(trans, "Too many chunks: %i\n", num_tbs); - /* @todo issue fatal error, it is quite serious situation */ - return; - } - - /* first TB is never freed - it's the scratchbuf data */ - - for (i = 1; i < num_tbs; i++) { - if (meta->flags & BIT(i + CMD_TB_BITMAP_POS)) - dma_unmap_page(trans->dev, - iwl_pcie_tfd_tb_get_addr(tfd, i), - iwl_pcie_tfd_tb_get_len(tfd, i), - DMA_TO_DEVICE); - else - dma_unmap_single(trans->dev, - iwl_pcie_tfd_tb_get_addr(tfd, i), - iwl_pcie_tfd_tb_get_len(tfd, i), - DMA_TO_DEVICE); - } - tfd->num_tbs = 0; -} - -/* - * iwl_pcie_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] - * @trans - transport private data - * @txq - tx queue - * @dma_dir - the direction of the DMA mapping - * - * Does NOT advance any TFD circular buffer read/write indexes - * Does NOT free the TFD itself (which is within circular buffer) - */ -static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) -{ - struct iwl_tfd *tfd_tmp = txq->tfds; - - /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and - * idx is bounded by n_window - */ - int rd_ptr = txq->q.read_ptr; - int idx = get_cmd_index(&txq->q, rd_ptr); - - lockdep_assert_held(&txq->lock); - - /* We have only q->n_window txq->entries, but we use - * TFD_QUEUE_SIZE_MAX tfds - */ - iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]); - - /* free SKB */ - if (txq->entries) { - struct sk_buff *skb; - - skb = txq->entries[idx].skb; - - /* Can be called from irqs-disabled context - * If skb is not NULL, it means that the whole queue is being - * freed and that the queue is not empty - free the skb - */ - if (skb) { - iwl_op_mode_free_skb(trans->op_mode, skb); - txq->entries[idx].skb = NULL; - } - } -} - -static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, - dma_addr_t addr, u16 len, bool reset) -{ - struct iwl_queue *q; - struct iwl_tfd *tfd, *tfd_tmp; - u32 num_tbs; - - q = &txq->q; - tfd_tmp = txq->tfds; - tfd = &tfd_tmp[q->write_ptr]; - - if (reset) - memset(tfd, 0, sizeof(*tfd)); - - num_tbs = iwl_pcie_tfd_get_num_tbs(tfd); - - /* Each TFD can point to a maximum 20 Tx buffers */ - if (num_tbs >= IWL_NUM_OF_TBS) { - IWL_ERR(trans, "Error can not send more than %d chunks\n", - IWL_NUM_OF_TBS); - return -EINVAL; - } - - if (WARN(addr & ~IWL_TX_DMA_MASK, - "Unaligned address = %llx\n", (unsigned long long)addr)) - return -EINVAL; - - iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len); - - return num_tbs; -} - -static int iwl_pcie_txq_alloc(struct iwl_trans *trans, - struct iwl_txq *txq, int slots_num, - u32 txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; - size_t scratchbuf_sz; - int i; - - if (WARN_ON(txq->entries || txq->tfds)) - return -EINVAL; - - setup_timer(&txq->stuck_timer, iwl_pcie_txq_stuck_timer, - (unsigned long)txq); - txq->trans_pcie = trans_pcie; - - txq->q.n_window = slots_num; - - txq->entries = kcalloc(slots_num, - sizeof(struct iwl_pcie_txq_entry), - GFP_KERNEL); - - if (!txq->entries) - goto error; - - if (txq_id == trans_pcie->cmd_queue) - for (i = 0; i < slots_num; i++) { - txq->entries[i].cmd = - kmalloc(sizeof(struct iwl_device_cmd), - GFP_KERNEL); - if (!txq->entries[i].cmd) - goto error; - } - - /* Circular buffer of transmit frame descriptors (TFDs), - * shared with device */ - txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz, - &txq->q.dma_addr, GFP_KERNEL); - if (!txq->tfds) - goto error; - - BUILD_BUG_ON(IWL_HCMD_SCRATCHBUF_SIZE != sizeof(*txq->scratchbufs)); - BUILD_BUG_ON(offsetof(struct iwl_pcie_txq_scratch_buf, scratch) != - sizeof(struct iwl_cmd_header) + - offsetof(struct iwl_tx_cmd, scratch)); - - scratchbuf_sz = sizeof(*txq->scratchbufs) * slots_num; - - txq->scratchbufs = dma_alloc_coherent(trans->dev, scratchbuf_sz, - &txq->scratchbufs_dma, - GFP_KERNEL); - if (!txq->scratchbufs) - goto err_free_tfds; - - txq->q.id = txq_id; - - return 0; -err_free_tfds: - dma_free_coherent(trans->dev, tfd_sz, txq->tfds, txq->q.dma_addr); -error: - if (txq->entries && txq_id == trans_pcie->cmd_queue) - for (i = 0; i < slots_num; i++) - kfree(txq->entries[i].cmd); - kfree(txq->entries); - txq->entries = NULL; - - return -ENOMEM; - -} - -static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, - int slots_num, u32 txq_id) -{ - int ret; - - txq->need_update = false; - - /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise - * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ - BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); - - /* Initialize queue's high/low-water marks, and head/tail indexes */ - ret = iwl_queue_init(&txq->q, slots_num, txq_id); - if (ret) - return ret; - - spin_lock_init(&txq->lock); - - /* - * Tell nic where to find circular buffer of Tx Frame Descriptors for - * given Tx queue, and enable the DMA channel used for that queue. - * Circular buffer (TFD queue in DRAM) physical base address */ - iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), - txq->q.dma_addr >> 8); - - return 0; -} - -/* - * iwl_pcie_txq_unmap - Unmap any remaining DMA mappings and free skb's - */ -static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; - struct iwl_queue *q = &txq->q; - - spin_lock_bh(&txq->lock); - while (q->write_ptr != q->read_ptr) { - IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", - txq_id, q->read_ptr); - iwl_pcie_txq_free_tfd(trans, txq); - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr); - } - txq->active = false; - spin_unlock_bh(&txq->lock); - - /* just in case - this queue may have been stopped */ - iwl_wake_queue(trans, txq); -} - -/* - * iwl_pcie_txq_free - Deallocate DMA queue. - * @txq: Transmit queue to deallocate. - * - * Empty queue by removing and destroying all BD's. - * Free all buffers. - * 0-fill, but do not free "txq" descriptor structure. - */ -static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; - struct device *dev = trans->dev; - int i; - - if (WARN_ON(!txq)) - return; - - iwl_pcie_txq_unmap(trans, txq_id); - - /* De-alloc array of command/tx buffers */ - if (txq_id == trans_pcie->cmd_queue) - for (i = 0; i < txq->q.n_window; i++) { - kzfree(txq->entries[i].cmd); - kzfree(txq->entries[i].free_buf); - } - - /* De-alloc circular buffer of TFDs */ - if (txq->tfds) { - dma_free_coherent(dev, - sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX, - txq->tfds, txq->q.dma_addr); - txq->q.dma_addr = 0; - txq->tfds = NULL; - - dma_free_coherent(dev, - sizeof(*txq->scratchbufs) * txq->q.n_window, - txq->scratchbufs, txq->scratchbufs_dma); - } - - kfree(txq->entries); - txq->entries = NULL; - - del_timer_sync(&txq->stuck_timer); - - /* 0-fill queue descriptor structure */ - memset(txq, 0, sizeof(*txq)); -} - -void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int nq = trans->cfg->base_params->num_of_queues; - int chan; - u32 reg_val; - int clear_dwords = (SCD_TRANS_TBL_OFFSET_QUEUE(nq) - - SCD_CONTEXT_MEM_LOWER_BOUND) / sizeof(u32); - - /* make sure all queue are not stopped/used */ - memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); - memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); - - trans_pcie->scd_base_addr = - iwl_read_prph(trans, SCD_SRAM_BASE_ADDR); - - WARN_ON(scd_base_addr != 0 && - scd_base_addr != trans_pcie->scd_base_addr); - - /* reset context data, TX status and translation data */ - iwl_trans_write_mem(trans, trans_pcie->scd_base_addr + - SCD_CONTEXT_MEM_LOWER_BOUND, - NULL, clear_dwords); - - iwl_write_prph(trans, SCD_DRAM_BASE_ADDR, - trans_pcie->scd_bc_tbls.dma >> 10); - - /* The chain extension of the SCD doesn't work well. This feature is - * enabled by default by the HW, so we need to disable it manually. - */ - if (trans->cfg->base_params->scd_chain_ext_wa) - iwl_write_prph(trans, SCD_CHAINEXT_EN, 0); - - iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue, - trans_pcie->cmd_fifo, - trans_pcie->cmd_q_wdg_timeout); - - /* Activate all Tx DMA/FIFO channels */ - iwl_scd_activate_fifos(trans); - - /* Enable DMA channel */ - for (chan = 0; chan < FH_TCSR_CHNL_NUM; chan++) - iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(chan), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); - - /* Update FH chicken bits */ - reg_val = iwl_read_direct32(trans, FH_TX_CHICKEN_BITS_REG); - iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG, - reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); - - /* Enable L1-Active */ - if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) - iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); -} - -void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int txq_id; - - for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; - txq_id++) { - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; - - iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), - txq->q.dma_addr >> 8); - iwl_pcie_txq_unmap(trans, txq_id); - txq->q.read_ptr = 0; - txq->q.write_ptr = 0; - } - - /* Tell NIC where to find the "keep warm" buffer */ - iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, - trans_pcie->kw.dma >> 4); - - /* - * Send 0 as the scd_base_addr since the device may have be reset - * while we were in WoWLAN in which case SCD_SRAM_BASE_ADDR will - * contain garbage. - */ - iwl_pcie_tx_start(trans, 0); -} - -static void iwl_pcie_tx_stop_fh(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - int ch, ret; - u32 mask = 0; - - spin_lock(&trans_pcie->irq_lock); - - if (!iwl_trans_grab_nic_access(trans, false, &flags)) - goto out; - - /* Stop each Tx DMA channel */ - for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { - iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); - mask |= FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch); - } - - /* Wait for DMA channels to be idle */ - ret = iwl_poll_bit(trans, FH_TSSR_TX_STATUS_REG, mask, mask, 5000); - if (ret < 0) - IWL_ERR(trans, - "Failing on timeout while stopping DMA channel %d [0x%08x]\n", - ch, iwl_read32(trans, FH_TSSR_TX_STATUS_REG)); - - iwl_trans_release_nic_access(trans, &flags); - -out: - spin_unlock(&trans_pcie->irq_lock); -} - -/* - * iwl_pcie_tx_stop - Stop all Tx DMA channels - */ -int iwl_pcie_tx_stop(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int txq_id; - - /* Turn off all Tx DMA fifos */ - iwl_scd_deactivate_fifos(trans); - - /* Turn off all Tx DMA channels */ - iwl_pcie_tx_stop_fh(trans); - - /* - * This function can be called before the op_mode disabled the - * queues. This happens when we have an rfkill interrupt. - * Since we stop Tx altogether - mark the queues as stopped. - */ - memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); - memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); - - /* This can happen: start_hw, stop_device */ - if (!trans_pcie->txq) - return 0; - - /* Unmap DMA from host system and free skb's */ - for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; - txq_id++) - iwl_pcie_txq_unmap(trans, txq_id); - - return 0; -} - -/* - * iwl_trans_tx_free - Free TXQ Context - * - * Destroy all TX DMA queues and structures - */ -void iwl_pcie_tx_free(struct iwl_trans *trans) -{ - int txq_id; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - /* Tx queues */ - if (trans_pcie->txq) { - for (txq_id = 0; - txq_id < trans->cfg->base_params->num_of_queues; txq_id++) - iwl_pcie_txq_free(trans, txq_id); - } - - kfree(trans_pcie->txq); - trans_pcie->txq = NULL; - - iwl_pcie_free_dma_ptr(trans, &trans_pcie->kw); - - iwl_pcie_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls); -} - -/* - * iwl_pcie_tx_alloc - allocate TX context - * Allocate all Tx DMA structures and initialize them - */ -static int iwl_pcie_tx_alloc(struct iwl_trans *trans) -{ - int ret; - int txq_id, slots_num; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - u16 scd_bc_tbls_size = trans->cfg->base_params->num_of_queues * - sizeof(struct iwlagn_scd_bc_tbl); - - /*It is not allowed to alloc twice, so warn when this happens. - * We cannot rely on the previous allocation, so free and fail */ - if (WARN_ON(trans_pcie->txq)) { - ret = -EINVAL; - goto error; - } - - ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls, - scd_bc_tbls_size); - if (ret) { - IWL_ERR(trans, "Scheduler BC Table allocation failed\n"); - goto error; - } - - /* Alloc keep-warm buffer */ - ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE); - if (ret) { - IWL_ERR(trans, "Keep Warm allocation failed\n"); - goto error; - } - - trans_pcie->txq = kcalloc(trans->cfg->base_params->num_of_queues, - sizeof(struct iwl_txq), GFP_KERNEL); - if (!trans_pcie->txq) { - IWL_ERR(trans, "Not enough memory for txq\n"); - ret = -ENOMEM; - goto error; - } - - /* Alloc and init all Tx queues, including the command queue (#4/#9) */ - for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; - txq_id++) { - slots_num = (txq_id == trans_pcie->cmd_queue) ? - TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_pcie_txq_alloc(trans, &trans_pcie->txq[txq_id], - slots_num, txq_id); - if (ret) { - IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); - goto error; - } - } - - return 0; - -error: - iwl_pcie_tx_free(trans); - - return ret; -} -int iwl_pcie_tx_init(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ret; - int txq_id, slots_num; - bool alloc = false; - - if (!trans_pcie->txq) { - ret = iwl_pcie_tx_alloc(trans); - if (ret) - goto error; - alloc = true; - } - - spin_lock(&trans_pcie->irq_lock); - - /* Turn off all Tx DMA fifos */ - iwl_scd_deactivate_fifos(trans); - - /* Tell NIC where to find the "keep warm" buffer */ - iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, - trans_pcie->kw.dma >> 4); - - spin_unlock(&trans_pcie->irq_lock); - - /* Alloc and init all Tx queues, including the command queue (#4/#9) */ - for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; - txq_id++) { - slots_num = (txq_id == trans_pcie->cmd_queue) ? - TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_pcie_txq_init(trans, &trans_pcie->txq[txq_id], - slots_num, txq_id); - if (ret) { - IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); - goto error; - } - } - - iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_AUTO_ACTIVE_MODE); - if (trans->cfg->base_params->num_of_queues > 20) - iwl_set_bits_prph(trans, SCD_GP_CTRL, - SCD_GP_CTRL_ENABLE_31_QUEUES); - - return 0; -error: - /*Upon error, free only if we allocated something */ - if (alloc) - iwl_pcie_tx_free(trans); - return ret; -} - -static inline void iwl_pcie_txq_progress(struct iwl_txq *txq) -{ - lockdep_assert_held(&txq->lock); - - if (!txq->wd_timeout) - return; - - /* - * station is asleep and we send data - that must - * be uAPSD or PS-Poll. Don't rearm the timer. - */ - if (txq->frozen) - return; - - /* - * if empty delete timer, otherwise move timer forward - * since we're making progress on this queue - */ - if (txq->q.read_ptr == txq->q.write_ptr) - del_timer(&txq->stuck_timer); - else - mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); -} - -/* Frees buffers until index _not_ inclusive */ -void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, - struct sk_buff_head *skbs) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; - int tfd_num = ssn & (TFD_QUEUE_SIZE_MAX - 1); - struct iwl_queue *q = &txq->q; - int last_to_free; - - /* This function is not meant to release cmd queue*/ - if (WARN_ON(txq_id == trans_pcie->cmd_queue)) - return; - - spin_lock_bh(&txq->lock); - - if (!txq->active) { - IWL_DEBUG_TX_QUEUES(trans, "Q %d inactive - ignoring idx %d\n", - txq_id, ssn); - goto out; - } - - if (txq->q.read_ptr == tfd_num) - goto out; - - IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n", - txq_id, txq->q.read_ptr, tfd_num, ssn); - - /*Since we free until index _not_ inclusive, the one before index is - * the last we will free. This one must be used */ - last_to_free = iwl_queue_dec_wrap(tfd_num); - - if (!iwl_queue_used(q, last_to_free)) { - IWL_ERR(trans, - "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", - __func__, txq_id, last_to_free, TFD_QUEUE_SIZE_MAX, - q->write_ptr, q->read_ptr); - goto out; - } - - if (WARN_ON(!skb_queue_empty(skbs))) - goto out; - - for (; - q->read_ptr != tfd_num; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) { - - if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL)) - continue; - - __skb_queue_tail(skbs, txq->entries[txq->q.read_ptr].skb); - - txq->entries[txq->q.read_ptr].skb = NULL; - - iwl_pcie_txq_inval_byte_cnt_tbl(trans, txq); - - iwl_pcie_txq_free_tfd(trans, txq); - } - - iwl_pcie_txq_progress(txq); - - if (iwl_queue_space(&txq->q) > txq->q.low_mark) - iwl_wake_queue(trans, txq); - - if (q->read_ptr == q->write_ptr) { - IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id); - iwl_trans_pcie_unref(trans); - } - -out: - spin_unlock_bh(&txq->lock); -} - -static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, - const struct iwl_host_cmd *cmd) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ret; - - lockdep_assert_held(&trans_pcie->reg_lock); - - if (!(cmd->flags & CMD_SEND_IN_IDLE) && - !trans_pcie->ref_cmd_in_flight) { - trans_pcie->ref_cmd_in_flight = true; - IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n"); - iwl_trans_pcie_ref(trans); - } - - /* - * wake up the NIC to make sure that the firmware will see the host - * command - we will let the NIC sleep once all the host commands - * returned. This needs to be done only on NICs that have - * apmg_wake_up_wa set. - */ - if (trans->cfg->base_params->apmg_wake_up_wa && - !trans_pcie->cmd_hold_nic_awake) { - __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, - (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | - CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), - 15000); - if (ret < 0) { - __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - IWL_ERR(trans, "Failed to wake NIC for hcmd\n"); - return -EIO; - } - trans_pcie->cmd_hold_nic_awake = true; - } - - return 0; -} - -static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - lockdep_assert_held(&trans_pcie->reg_lock); - - if (trans_pcie->ref_cmd_in_flight) { - trans_pcie->ref_cmd_in_flight = false; - IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n"); - iwl_trans_pcie_unref(trans); - } - - if (trans->cfg->base_params->apmg_wake_up_wa) { - if (WARN_ON(!trans_pcie->cmd_hold_nic_awake)) - return 0; - - trans_pcie->cmd_hold_nic_awake = false; - __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - } - return 0; -} - -/* - * iwl_pcie_cmdq_reclaim - Reclaim TX command queue entries already Tx'd - * - * When FW advances 'R' index, all entries between old and new 'R' index - * need to be reclaimed. As result, some free space forms. If there is - * enough free space (> low mark), wake the stack that feeds us. - */ -static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; - struct iwl_queue *q = &txq->q; - unsigned long flags; - int nfreed = 0; - - lockdep_assert_held(&txq->lock); - - if ((idx >= TFD_QUEUE_SIZE_MAX) || (!iwl_queue_used(q, idx))) { - IWL_ERR(trans, - "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", - __func__, txq_id, idx, TFD_QUEUE_SIZE_MAX, - q->write_ptr, q->read_ptr); - return; - } - - for (idx = iwl_queue_inc_wrap(idx); q->read_ptr != idx; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) { - - if (nfreed++ > 0) { - IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", - idx, q->write_ptr, q->read_ptr); - iwl_force_nmi(trans); - } - } - - if (q->read_ptr == q->write_ptr) { - spin_lock_irqsave(&trans_pcie->reg_lock, flags); - iwl_pcie_clear_cmd_in_flight(trans); - spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); - } - - iwl_pcie_txq_progress(txq); -} - -static int iwl_pcie_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid, - u16 txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 tbl_dw_addr; - u32 tbl_dw; - u16 scd_q2ratid; - - scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK; - - tbl_dw_addr = trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE(txq_id); - - tbl_dw = iwl_trans_read_mem32(trans, tbl_dw_addr); - - if (txq_id & 0x1) - tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); - else - tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); - - iwl_trans_write_mem32(trans, tbl_dw_addr, tbl_dw); - - return 0; -} - -/* Receiver address (actually, Rx station's index into station table), - * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ -#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) - -void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg, - unsigned int wdg_timeout) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; - int fifo = -1; - - if (test_and_set_bit(txq_id, trans_pcie->queue_used)) - WARN_ONCE(1, "queue %d already used - expect issues", txq_id); - - txq->wd_timeout = msecs_to_jiffies(wdg_timeout); - - if (cfg) { - fifo = cfg->fifo; - - /* Disable the scheduler prior configuring the cmd queue */ - if (txq_id == trans_pcie->cmd_queue && - trans_pcie->scd_set_active) - iwl_scd_enable_set_active(trans, 0); - - /* Stop this Tx queue before configuring it */ - iwl_scd_txq_set_inactive(trans, txq_id); - - /* Set this queue as a chain-building queue unless it is CMD */ - if (txq_id != trans_pcie->cmd_queue) - iwl_scd_txq_set_chain(trans, txq_id); - - if (cfg->aggregate) { - u16 ra_tid = BUILD_RAxTID(cfg->sta_id, cfg->tid); - - /* Map receiver-address / traffic-ID to this queue */ - iwl_pcie_txq_set_ratid_map(trans, ra_tid, txq_id); - - /* enable aggregations for the queue */ - iwl_scd_txq_enable_agg(trans, txq_id); - txq->ampdu = true; - } else { - /* - * disable aggregations for the queue, this will also - * make the ra_tid mapping configuration irrelevant - * since it is now a non-AGG queue. - */ - iwl_scd_txq_disable_agg(trans, txq_id); - - ssn = txq->q.read_ptr; - } - } - - /* Place first TFD at index corresponding to start sequence number. - * Assumes that ssn_idx is valid (!= 0xFFF) */ - txq->q.read_ptr = (ssn & 0xff); - txq->q.write_ptr = (ssn & 0xff); - iwl_write_direct32(trans, HBUS_TARG_WRPTR, - (ssn & 0xff) | (txq_id << 8)); - - if (cfg) { - u8 frame_limit = cfg->frame_limit; - - iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn); - - /* Set up Tx window size and frame limit for this queue */ - iwl_trans_write_mem32(trans, trans_pcie->scd_base_addr + - SCD_CONTEXT_QUEUE_OFFSET(txq_id), 0); - iwl_trans_write_mem32(trans, - trans_pcie->scd_base_addr + - SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), - ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & - SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | - ((frame_limit << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); - - /* Set up status area in SRAM, map to Tx DMA/FIFO, activate */ - iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), - (1 << SCD_QUEUE_STTS_REG_POS_ACTIVE) | - (cfg->fifo << SCD_QUEUE_STTS_REG_POS_TXF) | - (1 << SCD_QUEUE_STTS_REG_POS_WSL) | - SCD_QUEUE_STTS_REG_MSK); - - /* enable the scheduler for this queue (only) */ - if (txq_id == trans_pcie->cmd_queue && - trans_pcie->scd_set_active) - iwl_scd_enable_set_active(trans, BIT(txq_id)); - - IWL_DEBUG_TX_QUEUES(trans, - "Activate queue %d on FIFO %d WrPtr: %d\n", - txq_id, fifo, ssn & 0xff); - } else { - IWL_DEBUG_TX_QUEUES(trans, - "Activate queue %d WrPtr: %d\n", - txq_id, ssn & 0xff); - } - - txq->active = true; -} - -void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, - bool configure_scd) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 stts_addr = trans_pcie->scd_base_addr + - SCD_TX_STTS_QUEUE_OFFSET(txq_id); - static const u32 zero_val[4] = {}; - - trans_pcie->txq[txq_id].frozen_expiry_remainder = 0; - trans_pcie->txq[txq_id].frozen = false; - - /* - * Upon HW Rfkill - we stop the device, and then stop the queues - * in the op_mode. Just for the sake of the simplicity of the op_mode, - * allow the op_mode to call txq_disable after it already called - * stop_device. - */ - if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) { - WARN_ONCE(test_bit(STATUS_DEVICE_ENABLED, &trans->status), - "queue %d not used", txq_id); - return; - } - - if (configure_scd) { - iwl_scd_txq_set_inactive(trans, txq_id); - - iwl_trans_write_mem(trans, stts_addr, (void *)zero_val, - ARRAY_SIZE(zero_val)); - } - - iwl_pcie_txq_unmap(trans, txq_id); - trans_pcie->txq[txq_id].ampdu = false; - - IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); -} - -/*************** HOST COMMAND QUEUE FUNCTIONS *****/ - -/* - * iwl_pcie_enqueue_hcmd - enqueue a uCode command - * @priv: device private data point - * @cmd: a pointer to the ucode command structure - * - * The function returns < 0 values to indicate the operation - * failed. On success, it returns the index (>= 0) of command in the - * command queue. - */ -static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, - struct iwl_host_cmd *cmd) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; - struct iwl_queue *q = &txq->q; - struct iwl_device_cmd *out_cmd; - struct iwl_cmd_meta *out_meta; - unsigned long flags; - void *dup_buf = NULL; - dma_addr_t phys_addr; - int idx; - u16 copy_size, cmd_size, scratch_size; - bool had_nocopy = false; - u8 group_id = iwl_cmd_groupid(cmd->id); - int i, ret; - u32 cmd_pos; - const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; - u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; - - if (WARN(!trans_pcie->wide_cmd_header && - group_id > IWL_ALWAYS_LONG_GROUP, - "unsupported wide command %#x\n", cmd->id)) - return -EINVAL; - - if (group_id != 0) { - copy_size = sizeof(struct iwl_cmd_header_wide); - cmd_size = sizeof(struct iwl_cmd_header_wide); - } else { - copy_size = sizeof(struct iwl_cmd_header); - cmd_size = sizeof(struct iwl_cmd_header); - } - - /* need one for the header if the first is NOCOPY */ - BUILD_BUG_ON(IWL_MAX_CMD_TBS_PER_TFD > IWL_NUM_OF_TBS - 1); - - for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { - cmddata[i] = cmd->data[i]; - cmdlen[i] = cmd->len[i]; - - if (!cmd->len[i]) - continue; - - /* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */ - if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { - int copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; - - if (copy > cmdlen[i]) - copy = cmdlen[i]; - cmdlen[i] -= copy; - cmddata[i] += copy; - copy_size += copy; - } - - if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { - had_nocopy = true; - if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) { - idx = -EINVAL; - goto free_dup_buf; - } - } else if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) { - /* - * This is also a chunk that isn't copied - * to the static buffer so set had_nocopy. - */ - had_nocopy = true; - - /* only allowed once */ - if (WARN_ON(dup_buf)) { - idx = -EINVAL; - goto free_dup_buf; - } - - dup_buf = kmemdup(cmddata[i], cmdlen[i], - GFP_ATOMIC); - if (!dup_buf) - return -ENOMEM; - } else { - /* NOCOPY must not be followed by normal! */ - if (WARN_ON(had_nocopy)) { - idx = -EINVAL; - goto free_dup_buf; - } - copy_size += cmdlen[i]; - } - cmd_size += cmd->len[i]; - } - - /* - * If any of the command structures end up being larger than - * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically - * allocated into separate TFDs, then we will need to - * increase the size of the buffers. - */ - if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE, - "Command %s (%#x) is too large (%d bytes)\n", - get_cmd_string(trans_pcie, cmd->id), cmd->id, copy_size)) { - idx = -EINVAL; - goto free_dup_buf; - } - - spin_lock_bh(&txq->lock); - - if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { - spin_unlock_bh(&txq->lock); - - IWL_ERR(trans, "No space in command queue\n"); - iwl_op_mode_cmd_queue_full(trans->op_mode); - idx = -ENOSPC; - goto free_dup_buf; - } - - idx = get_cmd_index(q, q->write_ptr); - out_cmd = txq->entries[idx].cmd; - out_meta = &txq->entries[idx].meta; - - memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ - if (cmd->flags & CMD_WANT_SKB) - out_meta->source = cmd; - - /* set up the header */ - if (group_id != 0) { - out_cmd->hdr_wide.cmd = iwl_cmd_opcode(cmd->id); - out_cmd->hdr_wide.group_id = group_id; - out_cmd->hdr_wide.version = iwl_cmd_version(cmd->id); - out_cmd->hdr_wide.length = - cpu_to_le16(cmd_size - - sizeof(struct iwl_cmd_header_wide)); - out_cmd->hdr_wide.reserved = 0; - out_cmd->hdr_wide.sequence = - cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | - INDEX_TO_SEQ(q->write_ptr)); - - cmd_pos = sizeof(struct iwl_cmd_header_wide); - copy_size = sizeof(struct iwl_cmd_header_wide); - } else { - out_cmd->hdr.cmd = iwl_cmd_opcode(cmd->id); - out_cmd->hdr.sequence = - cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | - INDEX_TO_SEQ(q->write_ptr)); - out_cmd->hdr.group_id = 0; - - cmd_pos = sizeof(struct iwl_cmd_header); - copy_size = sizeof(struct iwl_cmd_header); - } - - /* and copy the data that needs to be copied */ - for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { - int copy; - - if (!cmd->len[i]) - continue; - - /* copy everything if not nocopy/dup */ - if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | - IWL_HCMD_DFL_DUP))) { - copy = cmd->len[i]; - - memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); - cmd_pos += copy; - copy_size += copy; - continue; - } - - /* - * Otherwise we need at least IWL_HCMD_SCRATCHBUF_SIZE copied - * in total (for the scratchbuf handling), but copy up to what - * we can fit into the payload for debug dump purposes. - */ - copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]); - - memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); - cmd_pos += copy; - - /* However, treat copy_size the proper way, we need it below */ - if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { - copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; - - if (copy > cmd->len[i]) - copy = cmd->len[i]; - copy_size += copy; - } - } - - IWL_DEBUG_HC(trans, - "Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", - get_cmd_string(trans_pcie, out_cmd->hdr.cmd), - group_id, out_cmd->hdr.cmd, - le16_to_cpu(out_cmd->hdr.sequence), - cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); - - /* start the TFD with the scratchbuf */ - scratch_size = min_t(int, copy_size, IWL_HCMD_SCRATCHBUF_SIZE); - memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size); - iwl_pcie_txq_build_tfd(trans, txq, - iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr), - scratch_size, true); - - /* map first command fragment, if any remains */ - if (copy_size > scratch_size) { - phys_addr = dma_map_single(trans->dev, - ((u8 *)&out_cmd->hdr) + scratch_size, - copy_size - scratch_size, - DMA_TO_DEVICE); - if (dma_mapping_error(trans->dev, phys_addr)) { - iwl_pcie_tfd_unmap(trans, out_meta, - &txq->tfds[q->write_ptr]); - idx = -ENOMEM; - goto out; - } - - iwl_pcie_txq_build_tfd(trans, txq, phys_addr, - copy_size - scratch_size, false); - } - - /* map the remaining (adjusted) nocopy/dup fragments */ - for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { - const void *data = cmddata[i]; - - if (!cmdlen[i]) - continue; - if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | - IWL_HCMD_DFL_DUP))) - continue; - if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) - data = dup_buf; - phys_addr = dma_map_single(trans->dev, (void *)data, - cmdlen[i], DMA_TO_DEVICE); - if (dma_mapping_error(trans->dev, phys_addr)) { - iwl_pcie_tfd_unmap(trans, out_meta, - &txq->tfds[q->write_ptr]); - idx = -ENOMEM; - goto out; - } - - iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false); - } - - BUILD_BUG_ON(IWL_NUM_OF_TBS + CMD_TB_BITMAP_POS > - sizeof(out_meta->flags) * BITS_PER_BYTE); - out_meta->flags = cmd->flags; - if (WARN_ON_ONCE(txq->entries[idx].free_buf)) - kzfree(txq->entries[idx].free_buf); - txq->entries[idx].free_buf = dup_buf; - - trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr_wide); - - /* start timer if queue currently empty */ - if (q->read_ptr == q->write_ptr && txq->wd_timeout) - mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); - - spin_lock_irqsave(&trans_pcie->reg_lock, flags); - ret = iwl_pcie_set_cmd_in_flight(trans, cmd); - if (ret < 0) { - idx = ret; - spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); - goto out; - } - - /* Increment and update queue's write index */ - q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); - iwl_pcie_txq_inc_wr_ptr(trans, txq); - - spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); - - out: - spin_unlock_bh(&txq->lock); - free_dup_buf: - if (idx < 0) - kfree(dup_buf); - return idx; -} - -/* - * iwl_pcie_hcmd_complete - Pull unused buffers off the queue and reclaim them - * @rxb: Rx buffer to reclaim - * - * If an Rx buffer has an async callback associated with it the callback - * will be executed. The attached skb (if present) will only be freed - * if the callback returns 1 - */ -void iwl_pcie_hcmd_complete(struct iwl_trans *trans, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - u16 sequence = le16_to_cpu(pkt->hdr.sequence); - int txq_id = SEQ_TO_QUEUE(sequence); - int index = SEQ_TO_INDEX(sequence); - int cmd_index; - struct iwl_device_cmd *cmd; - struct iwl_cmd_meta *meta; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; - - /* If a Tx command is being handled and it isn't in the actual - * command queue then there a command routing bug has been introduced - * in the queue management code. */ - if (WARN(txq_id != trans_pcie->cmd_queue, - "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", - txq_id, trans_pcie->cmd_queue, sequence, - trans_pcie->txq[trans_pcie->cmd_queue].q.read_ptr, - trans_pcie->txq[trans_pcie->cmd_queue].q.write_ptr)) { - iwl_print_hex_error(trans, pkt, 32); - return; - } - - spin_lock_bh(&txq->lock); - - cmd_index = get_cmd_index(&txq->q, index); - cmd = txq->entries[cmd_index].cmd; - meta = &txq->entries[cmd_index].meta; - - iwl_pcie_tfd_unmap(trans, meta, &txq->tfds[index]); - - /* Input error checking is done when commands are added to queue. */ - if (meta->flags & CMD_WANT_SKB) { - struct page *p = rxb_steal_page(rxb); - - meta->source->resp_pkt = pkt; - meta->source->_rx_page_addr = (unsigned long)page_address(p); - meta->source->_rx_page_order = trans_pcie->rx_page_order; - } - - iwl_pcie_cmdq_reclaim(trans, txq_id, index); - - if (!(meta->flags & CMD_ASYNC)) { - if (!test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status)) { - IWL_WARN(trans, - "HCMD_ACTIVE already clear for command %s\n", - get_cmd_string(trans_pcie, cmd->hdr.cmd)); - } - clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); - IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", - get_cmd_string(trans_pcie, cmd->hdr.cmd)); - wake_up(&trans_pcie->wait_command_queue); - } - - meta->flags = 0; - - spin_unlock_bh(&txq->lock); -} - -#define HOST_COMPLETE_TIMEOUT (2 * HZ) - -static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans, - struct iwl_host_cmd *cmd) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ret; - - /* An asynchronous command can not expect an SKB to be set. */ - if (WARN_ON(cmd->flags & CMD_WANT_SKB)) - return -EINVAL; - - ret = iwl_pcie_enqueue_hcmd(trans, cmd); - if (ret < 0) { - IWL_ERR(trans, - "Error sending %s: enqueue_hcmd failed: %d\n", - get_cmd_string(trans_pcie, cmd->id), ret); - return ret; - } - return 0; -} - -static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, - struct iwl_host_cmd *cmd) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int cmd_idx; - int ret; - - IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", - get_cmd_string(trans_pcie, cmd->id)); - - if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE, - &trans->status), - "Command %s: a command is already active!\n", - get_cmd_string(trans_pcie, cmd->id))) - return -EIO; - - IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", - get_cmd_string(trans_pcie, cmd->id)); - - cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd); - if (cmd_idx < 0) { - ret = cmd_idx; - clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); - IWL_ERR(trans, - "Error sending %s: enqueue_hcmd failed: %d\n", - get_cmd_string(trans_pcie, cmd->id), ret); - return ret; - } - - ret = wait_event_timeout(trans_pcie->wait_command_queue, - !test_bit(STATUS_SYNC_HCMD_ACTIVE, - &trans->status), - HOST_COMPLETE_TIMEOUT); - if (!ret) { - struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; - struct iwl_queue *q = &txq->q; - - IWL_ERR(trans, "Error sending %s: time out after %dms.\n", - get_cmd_string(trans_pcie, cmd->id), - jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); - - IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n", - q->read_ptr, q->write_ptr); - - clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); - IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", - get_cmd_string(trans_pcie, cmd->id)); - ret = -ETIMEDOUT; - - iwl_force_nmi(trans); - iwl_trans_fw_error(trans); - - goto cancel; - } - - if (test_bit(STATUS_FW_ERROR, &trans->status)) { - IWL_ERR(trans, "FW error in SYNC CMD %s\n", - get_cmd_string(trans_pcie, cmd->id)); - dump_stack(); - ret = -EIO; - goto cancel; - } - - if (!(cmd->flags & CMD_SEND_IN_RFKILL) && - test_bit(STATUS_RFKILL, &trans->status)) { - IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n"); - ret = -ERFKILL; - goto cancel; - } - - if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { - IWL_ERR(trans, "Error: Response NULL in '%s'\n", - get_cmd_string(trans_pcie, cmd->id)); - ret = -EIO; - goto cancel; - } - - return 0; - -cancel: - if (cmd->flags & CMD_WANT_SKB) { - /* - * Cancel the CMD_WANT_SKB flag for the cmd in the - * TX cmd queue. Otherwise in case the cmd comes - * in later, it will possibly set an invalid - * address (cmd->meta.source). - */ - trans_pcie->txq[trans_pcie->cmd_queue]. - entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB; - } - - if (cmd->resp_pkt) { - iwl_free_resp(cmd); - cmd->resp_pkt = NULL; - } - - return ret; -} - -int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) -{ - if (!(cmd->flags & CMD_SEND_IN_RFKILL) && - test_bit(STATUS_RFKILL, &trans->status)) { - IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n", - cmd->id); - return -ERFKILL; - } - - if (cmd->flags & CMD_ASYNC) - return iwl_pcie_send_hcmd_async(trans, cmd); - - /* We still can fail on RFKILL that can be asserted while we wait */ - return iwl_pcie_send_hcmd_sync(trans, cmd); -} - -int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_device_cmd *dev_cmd, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct ieee80211_hdr *hdr; - struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; - struct iwl_cmd_meta *out_meta; - struct iwl_txq *txq; - struct iwl_queue *q; - dma_addr_t tb0_phys, tb1_phys, scratch_phys; - void *tb1_addr; - u16 len, tb1_len, tb2_len; - bool wait_write_ptr; - __le16 fc; - u8 hdr_len; - u16 wifi_seq; - int i; - - txq = &trans_pcie->txq[txq_id]; - q = &txq->q; - - if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used), - "TX on unused queue %d\n", txq_id)) - return -EINVAL; - - if (skb_is_nonlinear(skb) && - skb_shinfo(skb)->nr_frags > IWL_PCIE_MAX_FRAGS && - __skb_linearize(skb)) - return -ENOMEM; - - /* mac80211 always puts the full header into the SKB's head, - * so there's no need to check if it's readable there - */ - hdr = (struct ieee80211_hdr *)skb->data; - fc = hdr->frame_control; - hdr_len = ieee80211_hdrlen(fc); - - spin_lock(&txq->lock); - - /* In AGG mode, the index in the ring must correspond to the WiFi - * sequence number. This is a HW requirements to help the SCD to parse - * the BA. - * Check here that the packets are in the right place on the ring. - */ - wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); - WARN_ONCE(txq->ampdu && - (wifi_seq & 0xff) != q->write_ptr, - "Q: %d WiFi Seq %d tfdNum %d", - txq_id, wifi_seq, q->write_ptr); - - /* Set up driver data for this TFD */ - txq->entries[q->write_ptr].skb = skb; - txq->entries[q->write_ptr].cmd = dev_cmd; - - dev_cmd->hdr.sequence = - cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | - INDEX_TO_SEQ(q->write_ptr))); - - tb0_phys = iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr); - scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) + - offsetof(struct iwl_tx_cmd, scratch); - - tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); - tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); - - /* Set up first empty entry in queue's array of Tx/cmd buffers */ - out_meta = &txq->entries[q->write_ptr].meta; - out_meta->flags = 0; - - /* - * The second TB (tb1) points to the remainder of the TX command - * and the 802.11 header - dword aligned size - * (This calculation modifies the TX command, so do it before the - * setup of the first TB) - */ - len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + - hdr_len - IWL_HCMD_SCRATCHBUF_SIZE; - tb1_len = ALIGN(len, 4); - - /* Tell NIC about any 2-byte padding after MAC header */ - if (tb1_len != len) - tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; - - /* The first TB points to the scratchbuf data - min_copy bytes */ - memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr, - IWL_HCMD_SCRATCHBUF_SIZE); - iwl_pcie_txq_build_tfd(trans, txq, tb0_phys, - IWL_HCMD_SCRATCHBUF_SIZE, true); - - /* there must be data left over for TB1 or this code must be changed */ - BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE); - - /* map the data for TB1 */ - tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_HCMD_SCRATCHBUF_SIZE; - tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(trans->dev, tb1_phys))) - goto out_err; - iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false); - - /* - * Set up TFD's third entry to point directly to remainder - * of skb's head, if any - */ - tb2_len = skb_headlen(skb) - hdr_len; - if (tb2_len > 0) { - dma_addr_t tb2_phys = dma_map_single(trans->dev, - skb->data + hdr_len, - tb2_len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) { - iwl_pcie_tfd_unmap(trans, out_meta, - &txq->tfds[q->write_ptr]); - goto out_err; - } - iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false); - } - - /* set up the remaining entries to point to the data */ - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - dma_addr_t tb_phys; - int tb_idx; - - if (!skb_frag_size(frag)) - continue; - - tb_phys = skb_frag_dma_map(trans->dev, frag, 0, - skb_frag_size(frag), DMA_TO_DEVICE); - - if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { - iwl_pcie_tfd_unmap(trans, out_meta, - &txq->tfds[q->write_ptr]); - goto out_err; - } - tb_idx = iwl_pcie_txq_build_tfd(trans, txq, tb_phys, - skb_frag_size(frag), false); - - out_meta->flags |= BIT(tb_idx + CMD_TB_BITMAP_POS); - } - - /* Set up entry for this TFD in Tx byte-count array */ - iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); - - trace_iwlwifi_dev_tx(trans->dev, skb, - &txq->tfds[txq->q.write_ptr], - sizeof(struct iwl_tfd), - &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len, - skb->data + hdr_len, tb2_len); - trace_iwlwifi_dev_tx_data(trans->dev, skb, - hdr_len, skb->len - hdr_len); - - wait_write_ptr = ieee80211_has_morefrags(fc); - - /* start timer if queue currently empty */ - if (q->read_ptr == q->write_ptr) { - if (txq->wd_timeout) { - /* - * If the TXQ is active, then set the timer, if not, - * set the timer in remainder so that the timer will - * be armed with the right value when the station will - * wake up. - */ - if (!txq->frozen) - mod_timer(&txq->stuck_timer, - jiffies + txq->wd_timeout); - else - txq->frozen_expiry_remainder = txq->wd_timeout; - } - IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id); - iwl_trans_pcie_ref(trans); - } - - /* Tell device the write index *just past* this latest filled TFD */ - q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); - if (!wait_write_ptr) - iwl_pcie_txq_inc_wr_ptr(trans, txq); - - /* - * At this point the frame is "transmitted" successfully - * and we will get a TX status notification eventually. - */ - if (iwl_queue_space(q) < q->high_mark) { - if (wait_write_ptr) - iwl_pcie_txq_inc_wr_ptr(trans, txq); - else - iwl_stop_queue(trans, txq); - } - spin_unlock(&txq->lock); - return 0; -out_err: - spin_unlock(&txq->lock); - return -1; -} -- cgit v1.2.3-59-g8ed1b From f988d64010ec058a206dce083b5c791ce09caa48 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 21:07:19 +0200 Subject: libertas: move under marvell vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 2 +- drivers/net/wireless/Makefile | 2 +- drivers/net/wireless/libertas/Kconfig | 45 - drivers/net/wireless/libertas/LICENSE | 16 - drivers/net/wireless/libertas/Makefile | 21 - drivers/net/wireless/libertas/README | 239 --- drivers/net/wireless/libertas/cfg.c | 2215 ---------------------- drivers/net/wireless/libertas/cfg.h | 21 - drivers/net/wireless/libertas/cmd.c | 1725 ----------------- drivers/net/wireless/libertas/cmd.h | 141 -- drivers/net/wireless/libertas/cmdresp.c | 353 ---- drivers/net/wireless/libertas/debugfs.c | 989 ---------- drivers/net/wireless/libertas/debugfs.h | 10 - drivers/net/wireless/libertas/decl.h | 82 - drivers/net/wireless/libertas/defs.h | 394 ---- drivers/net/wireless/libertas/dev.h | 211 --- drivers/net/wireless/libertas/ethtool.c | 120 -- drivers/net/wireless/libertas/firmware.c | 227 --- drivers/net/wireless/libertas/host.h | 978 ---------- drivers/net/wireless/libertas/if_cs.c | 1006 ---------- drivers/net/wireless/libertas/if_sdio.c | 1453 -------------- drivers/net/wireless/libertas/if_sdio.h | 52 - drivers/net/wireless/libertas/if_spi.c | 1318 ------------- drivers/net/wireless/libertas/if_spi.h | 206 -- drivers/net/wireless/libertas/if_usb.c | 1018 ---------- drivers/net/wireless/libertas/if_usb.h | 106 -- drivers/net/wireless/libertas/main.c | 1225 ------------ drivers/net/wireless/libertas/mesh.c | 1187 ------------ drivers/net/wireless/libertas/mesh.h | 77 - drivers/net/wireless/libertas/radiotap.h | 44 - drivers/net/wireless/libertas/rx.c | 286 --- drivers/net/wireless/libertas/tx.c | 207 -- drivers/net/wireless/libertas/types.h | 268 --- drivers/net/wireless/marvell/Kconfig | 16 + drivers/net/wireless/marvell/Makefile | 1 + drivers/net/wireless/marvell/libertas/Kconfig | 45 + drivers/net/wireless/marvell/libertas/LICENSE | 16 + drivers/net/wireless/marvell/libertas/Makefile | 21 + drivers/net/wireless/marvell/libertas/README | 239 +++ drivers/net/wireless/marvell/libertas/cfg.c | 2215 ++++++++++++++++++++++ drivers/net/wireless/marvell/libertas/cfg.h | 21 + drivers/net/wireless/marvell/libertas/cmd.c | 1725 +++++++++++++++++ drivers/net/wireless/marvell/libertas/cmd.h | 141 ++ drivers/net/wireless/marvell/libertas/cmdresp.c | 353 ++++ drivers/net/wireless/marvell/libertas/debugfs.c | 989 ++++++++++ drivers/net/wireless/marvell/libertas/debugfs.h | 10 + drivers/net/wireless/marvell/libertas/decl.h | 82 + drivers/net/wireless/marvell/libertas/defs.h | 394 ++++ drivers/net/wireless/marvell/libertas/dev.h | 211 +++ drivers/net/wireless/marvell/libertas/ethtool.c | 120 ++ drivers/net/wireless/marvell/libertas/firmware.c | 227 +++ drivers/net/wireless/marvell/libertas/host.h | 978 ++++++++++ drivers/net/wireless/marvell/libertas/if_cs.c | 1006 ++++++++++ drivers/net/wireless/marvell/libertas/if_sdio.c | 1453 ++++++++++++++ drivers/net/wireless/marvell/libertas/if_sdio.h | 52 + drivers/net/wireless/marvell/libertas/if_spi.c | 1318 +++++++++++++ drivers/net/wireless/marvell/libertas/if_spi.h | 206 ++ drivers/net/wireless/marvell/libertas/if_usb.c | 1018 ++++++++++ drivers/net/wireless/marvell/libertas/if_usb.h | 106 ++ drivers/net/wireless/marvell/libertas/main.c | 1225 ++++++++++++ drivers/net/wireless/marvell/libertas/mesh.c | 1187 ++++++++++++ drivers/net/wireless/marvell/libertas/mesh.h | 77 + drivers/net/wireless/marvell/libertas/radiotap.h | 44 + drivers/net/wireless/marvell/libertas/rx.c | 286 +++ drivers/net/wireless/marvell/libertas/tx.c | 207 ++ drivers/net/wireless/marvell/libertas/types.h | 268 +++ 67 files changed, 16260 insertions(+), 16243 deletions(-) delete mode 100644 drivers/net/wireless/libertas/Kconfig delete mode 100644 drivers/net/wireless/libertas/LICENSE delete mode 100644 drivers/net/wireless/libertas/Makefile delete mode 100644 drivers/net/wireless/libertas/README delete mode 100644 drivers/net/wireless/libertas/cfg.c delete mode 100644 drivers/net/wireless/libertas/cfg.h delete mode 100644 drivers/net/wireless/libertas/cmd.c delete mode 100644 drivers/net/wireless/libertas/cmd.h delete mode 100644 drivers/net/wireless/libertas/cmdresp.c delete mode 100644 drivers/net/wireless/libertas/debugfs.c delete mode 100644 drivers/net/wireless/libertas/debugfs.h delete mode 100644 drivers/net/wireless/libertas/decl.h delete mode 100644 drivers/net/wireless/libertas/defs.h delete mode 100644 drivers/net/wireless/libertas/dev.h delete mode 100644 drivers/net/wireless/libertas/ethtool.c delete mode 100644 drivers/net/wireless/libertas/firmware.c delete mode 100644 drivers/net/wireless/libertas/host.h delete mode 100644 drivers/net/wireless/libertas/if_cs.c delete mode 100644 drivers/net/wireless/libertas/if_sdio.c delete mode 100644 drivers/net/wireless/libertas/if_sdio.h delete mode 100644 drivers/net/wireless/libertas/if_spi.c delete mode 100644 drivers/net/wireless/libertas/if_spi.h delete mode 100644 drivers/net/wireless/libertas/if_usb.c delete mode 100644 drivers/net/wireless/libertas/if_usb.h delete mode 100644 drivers/net/wireless/libertas/main.c delete mode 100644 drivers/net/wireless/libertas/mesh.c delete mode 100644 drivers/net/wireless/libertas/mesh.h delete mode 100644 drivers/net/wireless/libertas/radiotap.h delete mode 100644 drivers/net/wireless/libertas/rx.c delete mode 100644 drivers/net/wireless/libertas/tx.c delete mode 100644 drivers/net/wireless/libertas/types.h create mode 100644 drivers/net/wireless/marvell/Kconfig create mode 100644 drivers/net/wireless/marvell/Makefile create mode 100644 drivers/net/wireless/marvell/libertas/Kconfig create mode 100644 drivers/net/wireless/marvell/libertas/LICENSE create mode 100644 drivers/net/wireless/marvell/libertas/Makefile create mode 100644 drivers/net/wireless/marvell/libertas/README create mode 100644 drivers/net/wireless/marvell/libertas/cfg.c create mode 100644 drivers/net/wireless/marvell/libertas/cfg.h create mode 100644 drivers/net/wireless/marvell/libertas/cmd.c create mode 100644 drivers/net/wireless/marvell/libertas/cmd.h create mode 100644 drivers/net/wireless/marvell/libertas/cmdresp.c create mode 100644 drivers/net/wireless/marvell/libertas/debugfs.c create mode 100644 drivers/net/wireless/marvell/libertas/debugfs.h create mode 100644 drivers/net/wireless/marvell/libertas/decl.h create mode 100644 drivers/net/wireless/marvell/libertas/defs.h create mode 100644 drivers/net/wireless/marvell/libertas/dev.h create mode 100644 drivers/net/wireless/marvell/libertas/ethtool.c create mode 100644 drivers/net/wireless/marvell/libertas/firmware.c create mode 100644 drivers/net/wireless/marvell/libertas/host.h create mode 100644 drivers/net/wireless/marvell/libertas/if_cs.c create mode 100644 drivers/net/wireless/marvell/libertas/if_sdio.c create mode 100644 drivers/net/wireless/marvell/libertas/if_sdio.h create mode 100644 drivers/net/wireless/marvell/libertas/if_spi.c create mode 100644 drivers/net/wireless/marvell/libertas/if_spi.h create mode 100644 drivers/net/wireless/marvell/libertas/if_usb.c create mode 100644 drivers/net/wireless/marvell/libertas/if_usb.h create mode 100644 drivers/net/wireless/marvell/libertas/main.c create mode 100644 drivers/net/wireless/marvell/libertas/mesh.c create mode 100644 drivers/net/wireless/marvell/libertas/mesh.h create mode 100644 drivers/net/wireless/marvell/libertas/radiotap.h create mode 100644 drivers/net/wireless/marvell/libertas/rx.c create mode 100644 drivers/net/wireless/marvell/libertas/tx.c create mode 100644 drivers/net/wireless/marvell/libertas/types.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index d72be8c88138..75ff7434db0e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6708,7 +6708,7 @@ F: drivers/net/ethernet/marvell/sk* MARVELL LIBERTAS WIRELESS DRIVER L: libertas-dev@lists.infradead.org S: Orphan -F: drivers/net/wireless/libertas/ +F: drivers/net/wireless/marvell/libertas/ MARVELL MV643XX ETHERNET DRIVER M: Sebastian Hesselbarth diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index d71efe89970b..b11a2d364200 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -22,6 +22,7 @@ source "drivers/net/wireless/atmel/Kconfig" source "drivers/net/wireless/broadcom/Kconfig" source "drivers/net/wireless/cisco/Kconfig" source "drivers/net/wireless/intel/Kconfig" +source "drivers/net/wireless/marvell/Kconfig" source "drivers/net/wireless/st/Kconfig" config PCMCIA_RAYCS @@ -162,7 +163,6 @@ config MWL8K source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/hostap/Kconfig" -source "drivers/net/wireless/libertas/Kconfig" source "drivers/net/wireless/orinoco/Kconfig" source "drivers/net/wireless/p54/Kconfig" source "drivers/net/wireless/rt2x00/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 6bcb2925a6ce..a974a6edb4b6 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/ obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/ +obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ obj-$(CONFIG_HERMES) += orinoco/ @@ -24,7 +25,6 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o obj-$(CONFIG_USB_ZD1201) += zd1201.o -obj-$(CONFIG_LIBERTAS) += libertas/ obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ diff --git a/drivers/net/wireless/libertas/Kconfig b/drivers/net/wireless/libertas/Kconfig deleted file mode 100644 index e6268ceacbf1..000000000000 --- a/drivers/net/wireless/libertas/Kconfig +++ /dev/null @@ -1,45 +0,0 @@ -config LIBERTAS - tristate "Marvell 8xxx Libertas WLAN driver support" - depends on CFG80211 - select WIRELESS_EXT - select WEXT_SPY - select LIB80211 - select FW_LOADER - ---help--- - A library for Marvell Libertas 8xxx devices. - -config LIBERTAS_USB - tristate "Marvell Libertas 8388 USB 802.11b/g cards" - depends on LIBERTAS && USB - ---help--- - A driver for Marvell Libertas 8388 USB devices. - -config LIBERTAS_CS - tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards" - depends on LIBERTAS && PCMCIA && HAS_IOPORT_MAP - ---help--- - A driver for Marvell Libertas 8385 CompactFlash devices. - -config LIBERTAS_SDIO - tristate "Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards" - depends on LIBERTAS && MMC - ---help--- - A driver for Marvell Libertas 8385/8686/8688 SDIO devices. - -config LIBERTAS_SPI - tristate "Marvell Libertas 8686 SPI 802.11b/g cards" - depends on LIBERTAS && SPI - ---help--- - A driver for Marvell Libertas 8686 SPI devices. - -config LIBERTAS_DEBUG - bool "Enable full debugging output in the Libertas module." - depends on LIBERTAS - ---help--- - Debugging support. - -config LIBERTAS_MESH - bool "Enable mesh support" - depends on LIBERTAS - help - This enables Libertas' MESH support, used by e.g. the OLPC people. diff --git a/drivers/net/wireless/libertas/LICENSE b/drivers/net/wireless/libertas/LICENSE deleted file mode 100644 index 8862742213b9..000000000000 --- a/drivers/net/wireless/libertas/LICENSE +++ /dev/null @@ -1,16 +0,0 @@ - Copyright (c) 2003-2006, Marvell International Ltd. - All Rights Reserved - - This program is free software; you can redistribute it and/or modify it - under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place - Suite 330, Boston, MA 02111-1307, USA. - diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile deleted file mode 100644 index eac72f7bd341..000000000000 --- a/drivers/net/wireless/libertas/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -libertas-y += cfg.o -libertas-y += cmd.o -libertas-y += cmdresp.o -libertas-y += debugfs.o -libertas-y += ethtool.o -libertas-y += main.o -libertas-y += rx.o -libertas-y += tx.o -libertas-y += firmware.o -libertas-$(CONFIG_LIBERTAS_MESH) += mesh.o - -usb8xxx-objs += if_usb.o -libertas_cs-objs += if_cs.o -libertas_sdio-objs += if_sdio.o -libertas_spi-objs += if_spi.o - -obj-$(CONFIG_LIBERTAS) += libertas.o -obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o -obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o -obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o -obj-$(CONFIG_LIBERTAS_SPI) += libertas_spi.o diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README deleted file mode 100644 index 1a554a685e91..000000000000 --- a/drivers/net/wireless/libertas/README +++ /dev/null @@ -1,239 +0,0 @@ -================================================================================ - README for Libertas - - (c) Copyright © 2003-2006, Marvell International Ltd. - All Rights Reserved - - 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 along with the File in the license.txt file or on the worldwide - web at http://www.gnu.org/licenses/gpl.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. -================================================================================ - -===================== -DRIVER LOADING -===================== - - o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/ - - o. Load driver by using the following command: - - insmod usb8388.ko [fw_name=usb8388.bin] - -========================= -ETHTOOL -========================= - - -Use the -i option to retrieve version information from the driver. - -# ethtool -i eth0 -driver: libertas -version: COMM-USB8388-318.p4 -firmware-version: 5.110.7 -bus-info: - -Use the -e option to read the EEPROM contents of the card. - - Usage: - ethtool -e ethX [raw on|off] [offset N] [length N] - - -e retrieves and prints an EEPROM dump for the specified ethernet - device. When raw is enabled, then it dumps the raw EEPROM data - to stdout. The length and offset parameters allow dumping cer- - tain portions of the EEPROM. Default is to dump the entire EEP- - ROM. - -# ethtool -e eth0 offset 0 length 16 -Offset Values ------- ------ -0x0000 38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00 - -======================== -DEBUGFS COMMANDS -======================== - -those commands are used via debugfs interface - -=========== -rdmac -rdbbp -rdrf - These commands are used to read the MAC, BBP and RF registers from the - card. These commands take one parameter that specifies the offset - location that is to be read. This parameter must be specified in - hexadecimal (its possible to precede preceding the number with a "0x"). - - Path: /sys/kernel/debug/libertas_wireless/ethX/registers/ - - Usage: - echo "0xa123" > rdmac ; cat rdmac - echo "0xa123" > rdbbp ; cat rdbbp - echo "0xa123" > rdrf ; cat rdrf -wrmac -wrbbp -wrrf - These commands are used to write the MAC, BBP and RF registers in the - card. These commands take two parameters that specify the offset - location and the value that is to be written. This parameters must - be specified in hexadecimal (its possible to precede the number - with a "0x"). - - Usage: - echo "0xa123 0xaa" > wrmac - echo "0xa123 0xaa" > wrbbp - echo "0xa123 0xaa" > wrrf - -sleepparams - This command is used to set the sleepclock configurations - - Path: /sys/kernel/debug/libertas_wireless/ethX/ - - Usage: - cat sleepparams: reads the current sleepclock configuration - - echo "p1 p2 p3 p4 p5 p6" > sleepparams: writes the sleepclock configuration. - - where: - p1 is Sleep clock error in ppm (0-65535) - p2 is Wakeup offset in usec (0-65535) - p3 is Clock stabilization time in usec (0-65535) - p4 is Control periodic calibration (0-2) - p5 is Control the use of external sleep clock (0-2) - p6 is reserved for debug (0-65535) - -subscribed_events - - The subscribed_events directory contains the interface for the - subscribed events API. - - Path: /sys/kernel/debug/libertas_wireless/ethX/subscribed_events/ - - Each event is represented by a filename. Each filename consists of the - following three fields: - Value Frequency Subscribed - - To read the current values for a given event, do: - cat event - To set the current values, do: - echo "60 2 1" > event - - Frequency field specifies the reporting frequency for this event. - If it is set to 0, then the event is reported only once, and then - automatically unsubscribed. If it is set to 1, then the event is - reported every time it occurs. If it is set to N, then the event is - reported every Nth time it occurs. - - beacon_missed - Value field specifies the number of consecutive missing beacons which - triggers the LINK_LOSS event. This event is generated only once after - which the firmware resets its state. At initialization, the LINK_LOSS - event is subscribed by default. The default value of MissedBeacons is - 60. - - failure_count - Value field specifies the consecutive failure count threshold which - triggers the generation of the MAX_FAIL event. Once this event is - generated, the consecutive failure count is reset to 0. - At initialization, the MAX_FAIL event is NOT subscribed by - default. - - high_rssi - This event is generated when the average received RSSI in beacons goes - above a threshold, specified by Value. - - low_rssi - This event is generated when the average received RSSI in beacons goes - below a threshold, specified by Value. - - high_snr - This event is generated when the average received SNR in beacons goes - above a threshold, specified by Value. - - low_snr - This event is generated when the average received SNR in beacons goes - below a threshold, specified by Value. - -extscan - This command is used to do a specific scan. - - Path: /sys/kernel/debug/libertas_wireless/ethX/ - - Usage: echo "SSID" > extscan - - Example: - echo "LINKSYS-AP" > extscan - - To see the results of use getscantable command. - -getscantable - - Display the current contents of the driver scan table (ie. get the - scan results). - - Path: /sys/kernel/debug/libertas_wireless/ethX/ - - Usage: - cat getscantable - -setuserscan - Initiate a customized scan and retrieve the results - - - Path: /sys/kernel/debug/libertas_wireless/ethX/ - - Usage: - echo "[ARGS]" > setuserscan - - where [ARGS]: - - bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan - ssid="[SSID]" specify a SSID filter for the scan - keep=[0 or 1] keep the previous scan results (1), discard (0) - dur=[scan time] time to scan for each channel in milliseconds - type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) - - Any combination of the above arguments can be supplied on the command - line. If dur tokens are absent, the driver default setting will be used. - The bssid and ssid fields, if blank, will produce an unfiltered scan. - The type field will default to 3 (Any) and the keep field will default - to 0 (Discard). - - Examples: - 1) Perform a passive scan on all channels for 20 ms per channel: - echo "dur=20" > setuserscan - - 2) Perform an active scan for a specific SSID: - echo "ssid="TestAP"" > setuserscan - - 3) Scan all available channels (B/G, A bands) for a specific BSSID, keep - the current scan table intact, update existing or append new scan data: - echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan - - 4) Scan for all infrastructure networks. - Keep the previous scan table intact. Update any duplicate BSSID/SSID - matches with the new scan data: - echo "type=1 keep=1" > setuserscan - - All entries in the scan table (not just the new scan data when keep=1) - will be displayed upon completion by use of the getscantable ioctl. - -hostsleep - This command is used to enable/disable host sleep. - Note: Host sleep parameters should be configured using - "ethtool -s ethX wol X" command before enabling host sleep. - - Path: /sys/kernel/debug/libertas_wireless/ethX/ - - Usage: - cat hostsleep: reads the current hostsleep state - echo "1" > hostsleep : enable host sleep. - echo "0" > hostsleep : disable host sleep - diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c deleted file mode 100644 index 8317afd065b4..000000000000 --- a/drivers/net/wireless/libertas/cfg.c +++ /dev/null @@ -1,2215 +0,0 @@ -/* - * Implement cfg80211 ("iw") support. - * - * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany - * Holger Schurig - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -#include "decl.h" -#include "cfg.h" -#include "cmd.h" -#include "mesh.h" - - -#define CHAN2G(_channel, _freq, _flags) { \ - .band = IEEE80211_BAND_2GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_channel), \ - .flags = (_flags), \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} - -static struct ieee80211_channel lbs_2ghz_channels[] = { - CHAN2G(1, 2412, 0), - CHAN2G(2, 2417, 0), - CHAN2G(3, 2422, 0), - CHAN2G(4, 2427, 0), - CHAN2G(5, 2432, 0), - CHAN2G(6, 2437, 0), - CHAN2G(7, 2442, 0), - CHAN2G(8, 2447, 0), - CHAN2G(9, 2452, 0), - CHAN2G(10, 2457, 0), - CHAN2G(11, 2462, 0), - CHAN2G(12, 2467, 0), - CHAN2G(13, 2472, 0), - CHAN2G(14, 2484, 0), -}; - -#define RATETAB_ENT(_rate, _hw_value, _flags) { \ - .bitrate = (_rate), \ - .hw_value = (_hw_value), \ - .flags = (_flags), \ -} - - -/* Table 6 in section 3.2.1.1 */ -static struct ieee80211_rate lbs_rates[] = { - RATETAB_ENT(10, 0, 0), - RATETAB_ENT(20, 1, 0), - RATETAB_ENT(55, 2, 0), - RATETAB_ENT(110, 3, 0), - RATETAB_ENT(60, 9, 0), - RATETAB_ENT(90, 6, 0), - RATETAB_ENT(120, 7, 0), - RATETAB_ENT(180, 8, 0), - RATETAB_ENT(240, 9, 0), - RATETAB_ENT(360, 10, 0), - RATETAB_ENT(480, 11, 0), - RATETAB_ENT(540, 12, 0), -}; - -static struct ieee80211_supported_band lbs_band_2ghz = { - .channels = lbs_2ghz_channels, - .n_channels = ARRAY_SIZE(lbs_2ghz_channels), - .bitrates = lbs_rates, - .n_bitrates = ARRAY_SIZE(lbs_rates), -}; - - -static const u32 cipher_suites[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, -}; - -/* Time to stay on the channel */ -#define LBS_DWELL_PASSIVE 100 -#define LBS_DWELL_ACTIVE 40 - - -/*************************************************************************** - * Misc utility functions - * - * TLVs are Marvell specific. They are very similar to IEs, they have the - * same structure: type, length, data*. The only difference: for IEs, the - * type and length are u8, but for TLVs they're __le16. - */ - -/* - * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1 - * in the firmware spec - */ -static int lbs_auth_to_authtype(enum nl80211_auth_type auth_type) -{ - int ret = -ENOTSUPP; - - switch (auth_type) { - case NL80211_AUTHTYPE_OPEN_SYSTEM: - case NL80211_AUTHTYPE_SHARED_KEY: - ret = auth_type; - break; - case NL80211_AUTHTYPE_AUTOMATIC: - ret = NL80211_AUTHTYPE_OPEN_SYSTEM; - break; - case NL80211_AUTHTYPE_NETWORK_EAP: - ret = 0x80; - break; - default: - /* silence compiler */ - break; - } - return ret; -} - - -/* - * Various firmware commands need the list of supported rates, but with - * the hight-bit set for basic rates - */ -static int lbs_add_rates(u8 *rates) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { - u8 rate = lbs_rates[i].bitrate / 5; - if (rate == 0x02 || rate == 0x04 || - rate == 0x0b || rate == 0x16) - rate |= 0x80; - rates[i] = rate; - } - return ARRAY_SIZE(lbs_rates); -} - - -/*************************************************************************** - * TLV utility functions - * - * TLVs are Marvell specific. They are very similar to IEs, they have the - * same structure: type, length, data*. The only difference: for IEs, the - * type and length are u8, but for TLVs they're __le16. - */ - - -/* - * Add ssid TLV - */ -#define LBS_MAX_SSID_TLV_SIZE \ - (sizeof(struct mrvl_ie_header) \ - + IEEE80211_MAX_SSID_LEN) - -static int lbs_add_ssid_tlv(u8 *tlv, const u8 *ssid, int ssid_len) -{ - struct mrvl_ie_ssid_param_set *ssid_tlv = (void *)tlv; - - /* - * TLV-ID SSID 00 00 - * length 06 00 - * ssid 4d 4e 54 45 53 54 - */ - ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID); - ssid_tlv->header.len = cpu_to_le16(ssid_len); - memcpy(ssid_tlv->ssid, ssid, ssid_len); - return sizeof(ssid_tlv->header) + ssid_len; -} - - -/* - * Add channel list TLV (section 8.4.2) - * - * Actual channel data comes from priv->wdev->wiphy->channels. - */ -#define LBS_MAX_CHANNEL_LIST_TLV_SIZE \ - (sizeof(struct mrvl_ie_header) \ - + (LBS_SCAN_BEFORE_NAP * sizeof(struct chanscanparamset))) - -static int lbs_add_channel_list_tlv(struct lbs_private *priv, u8 *tlv, - int last_channel, int active_scan) -{ - int chanscanparamsize = sizeof(struct chanscanparamset) * - (last_channel - priv->scan_channel); - - struct mrvl_ie_header *header = (void *) tlv; - - /* - * TLV-ID CHANLIST 01 01 - * length 0e 00 - * channel 00 01 00 00 00 64 00 - * radio type 00 - * channel 01 - * scan type 00 - * min scan time 00 00 - * max scan time 64 00 - * channel 2 00 02 00 00 00 64 00 - * - */ - - header->type = cpu_to_le16(TLV_TYPE_CHANLIST); - header->len = cpu_to_le16(chanscanparamsize); - tlv += sizeof(struct mrvl_ie_header); - - /* lbs_deb_scan("scan: channels %d to %d\n", priv->scan_channel, - last_channel); */ - memset(tlv, 0, chanscanparamsize); - - while (priv->scan_channel < last_channel) { - struct chanscanparamset *param = (void *) tlv; - - param->radiotype = CMD_SCAN_RADIO_TYPE_BG; - param->channumber = - priv->scan_req->channels[priv->scan_channel]->hw_value; - if (active_scan) { - param->maxscantime = cpu_to_le16(LBS_DWELL_ACTIVE); - } else { - param->chanscanmode.passivescan = 1; - param->maxscantime = cpu_to_le16(LBS_DWELL_PASSIVE); - } - tlv += sizeof(struct chanscanparamset); - priv->scan_channel++; - } - return sizeof(struct mrvl_ie_header) + chanscanparamsize; -} - - -/* - * Add rates TLV - * - * The rates are in lbs_bg_rates[], but for the 802.11b - * rates the high bit is set. We add this TLV only because - * there's a firmware which otherwise doesn't report all - * APs in range. - */ -#define LBS_MAX_RATES_TLV_SIZE \ - (sizeof(struct mrvl_ie_header) \ - + (ARRAY_SIZE(lbs_rates))) - -/* Adds a TLV with all rates the hardware supports */ -static int lbs_add_supported_rates_tlv(u8 *tlv) -{ - size_t i; - struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; - - /* - * TLV-ID RATES 01 00 - * length 0e 00 - * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c - */ - rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); - tlv += sizeof(rate_tlv->header); - i = lbs_add_rates(tlv); - tlv += i; - rate_tlv->header.len = cpu_to_le16(i); - return sizeof(rate_tlv->header) + i; -} - -/* Add common rates from a TLV and return the new end of the TLV */ -static u8 * -add_ie_rates(u8 *tlv, const u8 *ie, int *nrates) -{ - int hw, ap, ap_max = ie[1]; - u8 hw_rate; - - /* Advance past IE header */ - ie += 2; - - lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max); - - for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { - hw_rate = lbs_rates[hw].bitrate / 5; - for (ap = 0; ap < ap_max; ap++) { - if (hw_rate == (ie[ap] & 0x7f)) { - *tlv++ = ie[ap]; - *nrates = *nrates + 1; - } - } - } - return tlv; -} - -/* - * Adds a TLV with all rates the hardware *and* BSS supports. - */ -static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) -{ - struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; - const u8 *rates_eid, *ext_rates_eid; - int n = 0; - - rcu_read_lock(); - rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); - ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); - - /* - * 01 00 TLV_TYPE_RATES - * 04 00 len - * 82 84 8b 96 rates - */ - rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); - tlv += sizeof(rate_tlv->header); - - /* Add basic rates */ - if (rates_eid) { - tlv = add_ie_rates(tlv, rates_eid, &n); - - /* Add extended rates, if any */ - if (ext_rates_eid) - tlv = add_ie_rates(tlv, ext_rates_eid, &n); - } else { - lbs_deb_assoc("assoc: bss had no basic rate IE\n"); - /* Fallback: add basic 802.11b rates */ - *tlv++ = 0x82; - *tlv++ = 0x84; - *tlv++ = 0x8b; - *tlv++ = 0x96; - n = 4; - } - rcu_read_unlock(); - - rate_tlv->header.len = cpu_to_le16(n); - return sizeof(rate_tlv->header) + n; -} - - -/* - * Add auth type TLV. - * - * This is only needed for newer firmware (V9 and up). - */ -#define LBS_MAX_AUTH_TYPE_TLV_SIZE \ - sizeof(struct mrvl_ie_auth_type) - -static int lbs_add_auth_type_tlv(u8 *tlv, enum nl80211_auth_type auth_type) -{ - struct mrvl_ie_auth_type *auth = (void *) tlv; - - /* - * 1f 01 TLV_TYPE_AUTH_TYPE - * 01 00 len - * 01 auth type - */ - auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); - auth->header.len = cpu_to_le16(sizeof(*auth)-sizeof(auth->header)); - auth->auth = cpu_to_le16(lbs_auth_to_authtype(auth_type)); - return sizeof(*auth); -} - - -/* - * Add channel (phy ds) TLV - */ -#define LBS_MAX_CHANNEL_TLV_SIZE \ - sizeof(struct mrvl_ie_header) - -static int lbs_add_channel_tlv(u8 *tlv, u8 channel) -{ - struct mrvl_ie_ds_param_set *ds = (void *) tlv; - - /* - * 03 00 TLV_TYPE_PHY_DS - * 01 00 len - * 06 channel - */ - ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); - ds->header.len = cpu_to_le16(sizeof(*ds)-sizeof(ds->header)); - ds->channel = channel; - return sizeof(*ds); -} - - -/* - * Add (empty) CF param TLV of the form: - */ -#define LBS_MAX_CF_PARAM_TLV_SIZE \ - sizeof(struct mrvl_ie_header) - -static int lbs_add_cf_param_tlv(u8 *tlv) -{ - struct mrvl_ie_cf_param_set *cf = (void *)tlv; - - /* - * 04 00 TLV_TYPE_CF - * 06 00 len - * 00 cfpcnt - * 00 cfpperiod - * 00 00 cfpmaxduration - * 00 00 cfpdurationremaining - */ - cf->header.type = cpu_to_le16(TLV_TYPE_CF); - cf->header.len = cpu_to_le16(sizeof(*cf)-sizeof(cf->header)); - return sizeof(*cf); -} - -/* - * Add WPA TLV - */ -#define LBS_MAX_WPA_TLV_SIZE \ - (sizeof(struct mrvl_ie_header) \ - + 128 /* TODO: I guessed the size */) - -static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len) -{ - size_t tlv_len; - - /* - * We need just convert an IE to an TLV. IEs use u8 for the header, - * u8 type - * u8 len - * u8[] data - * but TLVs use __le16 instead: - * __le16 type - * __le16 len - * u8[] data - */ - *tlv++ = *ie++; - *tlv++ = 0; - tlv_len = *tlv++ = *ie++; - *tlv++ = 0; - while (tlv_len--) - *tlv++ = *ie++; - /* the TLV is two bytes larger than the IE */ - return ie_len + 2; -} - -/* - * Set Channel - */ - -static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, - struct cfg80211_chan_def *chandef) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret = -ENOTSUPP; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d", - chandef->chan->center_freq, - cfg80211_get_chandef_type(chandef)); - - if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) - goto out; - - ret = lbs_set_channel(priv, chandef->chan->hw_value); - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - -static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy, - struct net_device *netdev, - struct ieee80211_channel *channel) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret = -ENOTSUPP; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d", - netdev_name(netdev), channel->center_freq); - - if (netdev != priv->mesh_dev) - goto out; - - ret = lbs_mesh_set_channel(priv, channel->hw_value); - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - - -/* - * Scanning - */ - -/* - * When scanning, the firmware doesn't send a nul packet with the power-safe - * bit to the AP. So we cannot stay away from our current channel too long, - * otherwise we loose data. So take a "nap" while scanning every other - * while. - */ -#define LBS_SCAN_BEFORE_NAP 4 - - -/* - * When the firmware reports back a scan-result, it gives us an "u8 rssi", - * which isn't really an RSSI, as it becomes larger when moving away from - * the AP. Anyway, we need to convert that into mBm. - */ -#define LBS_SCAN_RSSI_TO_MBM(rssi) \ - ((-(int)rssi + 3)*100) - -static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, - struct cmd_header *resp) -{ - struct cfg80211_bss *bss; - struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp; - int bsssize; - const u8 *pos; - const u8 *tsfdesc; - int tsfsize; - int i; - int ret = -EILSEQ; - - lbs_deb_enter(LBS_DEB_CFG80211); - - bsssize = get_unaligned_le16(&scanresp->bssdescriptsize); - - lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n", - scanresp->nr_sets, bsssize, le16_to_cpu(resp->size)); - - if (scanresp->nr_sets == 0) { - ret = 0; - goto done; - } - - /* - * The general layout of the scan response is described in chapter - * 5.7.1. Basically we have a common part, then any number of BSS - * descriptor sections. Finally we have section with the same number - * of TSFs. - * - * cmd_ds_802_11_scan_rsp - * cmd_header - * pos_size - * nr_sets - * bssdesc 1 - * bssid - * rssi - * timestamp - * intvl - * capa - * IEs - * bssdesc 2 - * bssdesc n - * MrvlIEtypes_TsfFimestamp_t - * TSF for BSS 1 - * TSF for BSS 2 - * TSF for BSS n - */ - - pos = scanresp->bssdesc_and_tlvbuffer; - - lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer, - scanresp->bssdescriptsize); - - tsfdesc = pos + bsssize; - tsfsize = 4 + 8 * scanresp->nr_sets; - lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TSF", (u8 *) tsfdesc, tsfsize); - - /* Validity check: we expect a Marvell-Local TLV */ - i = get_unaligned_le16(tsfdesc); - tsfdesc += 2; - if (i != TLV_TYPE_TSFTIMESTAMP) { - lbs_deb_scan("scan response: invalid TSF Timestamp %d\n", i); - goto done; - } - - /* - * Validity check: the TLV holds TSF values with 8 bytes each, so - * the size in the TLV must match the nr_sets value - */ - i = get_unaligned_le16(tsfdesc); - tsfdesc += 2; - if (i / 8 != scanresp->nr_sets) { - lbs_deb_scan("scan response: invalid number of TSF timestamp " - "sets (expected %d got %d)\n", scanresp->nr_sets, - i / 8); - goto done; - } - - for (i = 0; i < scanresp->nr_sets; i++) { - const u8 *bssid; - const u8 *ie; - int left; - int ielen; - int rssi; - u16 intvl; - u16 capa; - int chan_no = -1; - const u8 *ssid = NULL; - u8 ssid_len = 0; - - int len = get_unaligned_le16(pos); - pos += 2; - - /* BSSID */ - bssid = pos; - pos += ETH_ALEN; - /* RSSI */ - rssi = *pos++; - /* Packet time stamp */ - pos += 8; - /* Beacon interval */ - intvl = get_unaligned_le16(pos); - pos += 2; - /* Capabilities */ - capa = get_unaligned_le16(pos); - pos += 2; - - /* To find out the channel, we must parse the IEs */ - ie = pos; - /* - * 6+1+8+2+2: size of BSSID, RSSI, time stamp, beacon - * interval, capabilities - */ - ielen = left = len - (6 + 1 + 8 + 2 + 2); - while (left >= 2) { - u8 id, elen; - id = *pos++; - elen = *pos++; - left -= 2; - if (elen > left) { - lbs_deb_scan("scan response: invalid IE fmt\n"); - goto done; - } - - if (id == WLAN_EID_DS_PARAMS) - chan_no = *pos; - if (id == WLAN_EID_SSID) { - ssid = pos; - ssid_len = elen; - } - left -= elen; - pos += elen; - } - - /* No channel, no luck */ - if (chan_no != -1) { - struct wiphy *wiphy = priv->wdev->wiphy; - int freq = ieee80211_channel_to_frequency(chan_no, - IEEE80211_BAND_2GHZ); - struct ieee80211_channel *channel = - ieee80211_get_channel(wiphy, freq); - - lbs_deb_scan("scan: %pM, capa %04x, chan %2d, %*pE, %d dBm\n", - bssid, capa, chan_no, ssid_len, ssid, - LBS_SCAN_RSSI_TO_MBM(rssi)/100); - - if (channel && - !(channel->flags & IEEE80211_CHAN_DISABLED)) { - bss = cfg80211_inform_bss(wiphy, channel, - CFG80211_BSS_FTYPE_UNKNOWN, - bssid, get_unaligned_le64(tsfdesc), - capa, intvl, ie, ielen, - LBS_SCAN_RSSI_TO_MBM(rssi), - GFP_KERNEL); - cfg80211_put_bss(wiphy, bss); - } - } else - lbs_deb_scan("scan response: missing BSS channel IE\n"); - - tsfdesc += 8; - } - ret = 0; - - done: - lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); - return ret; -} - - -/* - * Our scan command contains a TLV, consting of a SSID TLV, a channel list - * TLV and a rates TLV. Determine the maximum size of them: - */ -#define LBS_SCAN_MAX_CMD_SIZE \ - (sizeof(struct cmd_ds_802_11_scan) \ - + LBS_MAX_SSID_TLV_SIZE \ - + LBS_MAX_CHANNEL_LIST_TLV_SIZE \ - + LBS_MAX_RATES_TLV_SIZE) - -/* - * Assumes priv->scan_req is initialized and valid - * Assumes priv->scan_channel is initialized - */ -static void lbs_scan_worker(struct work_struct *work) -{ - struct lbs_private *priv = - container_of(work, struct lbs_private, scan_work.work); - struct cmd_ds_802_11_scan *scan_cmd; - u8 *tlv; /* pointer into our current, growing TLV storage area */ - int last_channel; - int running, carrier; - - lbs_deb_enter(LBS_DEB_SCAN); - - scan_cmd = kzalloc(LBS_SCAN_MAX_CMD_SIZE, GFP_KERNEL); - if (scan_cmd == NULL) - goto out_no_scan_cmd; - - /* prepare fixed part of scan command */ - scan_cmd->bsstype = CMD_BSS_TYPE_ANY; - - /* stop network while we're away from our main channel */ - running = !netif_queue_stopped(priv->dev); - carrier = netif_carrier_ok(priv->dev); - if (running) - netif_stop_queue(priv->dev); - if (carrier) - netif_carrier_off(priv->dev); - - /* prepare fixed part of scan command */ - tlv = scan_cmd->tlvbuffer; - - /* add SSID TLV */ - if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0) - tlv += lbs_add_ssid_tlv(tlv, - priv->scan_req->ssids[0].ssid, - priv->scan_req->ssids[0].ssid_len); - - /* add channel TLVs */ - last_channel = priv->scan_channel + LBS_SCAN_BEFORE_NAP; - if (last_channel > priv->scan_req->n_channels) - last_channel = priv->scan_req->n_channels; - tlv += lbs_add_channel_list_tlv(priv, tlv, last_channel, - priv->scan_req->n_ssids); - - /* add rates TLV */ - tlv += lbs_add_supported_rates_tlv(tlv); - - if (priv->scan_channel < priv->scan_req->n_channels) { - cancel_delayed_work(&priv->scan_work); - if (netif_running(priv->dev)) - queue_delayed_work(priv->work_thread, &priv->scan_work, - msecs_to_jiffies(300)); - } - - /* This is the final data we are about to send */ - scan_cmd->hdr.size = cpu_to_le16(tlv - (u8 *)scan_cmd); - lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd, - sizeof(*scan_cmd)); - lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer, - tlv - scan_cmd->tlvbuffer); - - __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr, - le16_to_cpu(scan_cmd->hdr.size), - lbs_ret_scan, 0); - - if (priv->scan_channel >= priv->scan_req->n_channels) { - /* Mark scan done */ - cancel_delayed_work(&priv->scan_work); - lbs_scan_done(priv); - } - - /* Restart network */ - if (carrier) - netif_carrier_on(priv->dev); - if (running && !priv->tx_pending_len) - netif_wake_queue(priv->dev); - - kfree(scan_cmd); - - /* Wake up anything waiting on scan completion */ - if (priv->scan_req == NULL) { - lbs_deb_scan("scan: waking up waiters\n"); - wake_up_all(&priv->scan_q); - } - - out_no_scan_cmd: - lbs_deb_leave(LBS_DEB_SCAN); -} - -static void _internal_start_scan(struct lbs_private *priv, bool internal, - struct cfg80211_scan_request *request) -{ - lbs_deb_enter(LBS_DEB_CFG80211); - - lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n", - request->n_ssids, request->n_channels, request->ie_len); - - priv->scan_channel = 0; - priv->scan_req = request; - priv->internal_scan = internal; - - queue_delayed_work(priv->work_thread, &priv->scan_work, - msecs_to_jiffies(50)); - - lbs_deb_leave(LBS_DEB_CFG80211); -} - -/* - * Clean up priv->scan_req. Should be used to handle the allocation details. - */ -void lbs_scan_done(struct lbs_private *priv) -{ - WARN_ON(!priv->scan_req); - - if (priv->internal_scan) - kfree(priv->scan_req); - else - cfg80211_scan_done(priv->scan_req, false); - - priv->scan_req = NULL; -} - -static int lbs_cfg_scan(struct wiphy *wiphy, - struct cfg80211_scan_request *request) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret = 0; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (priv->scan_req || delayed_work_pending(&priv->scan_work)) { - /* old scan request not yet processed */ - ret = -EAGAIN; - goto out; - } - - _internal_start_scan(priv, false, request); - - if (priv->surpriseremoved) - ret = -EIO; - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - - - -/* - * Events - */ - -void lbs_send_disconnect_notification(struct lbs_private *priv, - bool locally_generated) -{ - lbs_deb_enter(LBS_DEB_CFG80211); - - cfg80211_disconnected(priv->dev, 0, NULL, 0, locally_generated, - GFP_KERNEL); - - lbs_deb_leave(LBS_DEB_CFG80211); -} - -void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event) -{ - lbs_deb_enter(LBS_DEB_CFG80211); - - cfg80211_michael_mic_failure(priv->dev, - priv->assoc_bss, - event == MACREG_INT_CODE_MIC_ERR_MULTICAST ? - NL80211_KEYTYPE_GROUP : - NL80211_KEYTYPE_PAIRWISE, - -1, - NULL, - GFP_KERNEL); - - lbs_deb_leave(LBS_DEB_CFG80211); -} - - - - -/* - * Connect/disconnect - */ - - -/* - * This removes all WEP keys - */ -static int lbs_remove_wep_keys(struct lbs_private *priv) -{ - struct cmd_ds_802_11_set_wep cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CFG80211); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.keyindex = cpu_to_le16(priv->wep_tx_key); - cmd.action = cpu_to_le16(CMD_ACT_REMOVE); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); - - lbs_deb_leave(LBS_DEB_CFG80211); - return ret; -} - -/* - * Set WEP keys - */ -static int lbs_set_wep_keys(struct lbs_private *priv) -{ - struct cmd_ds_802_11_set_wep cmd; - int i; - int ret; - - lbs_deb_enter(LBS_DEB_CFG80211); - - /* - * command 13 00 - * size 50 00 - * sequence xx xx - * result 00 00 - * action 02 00 ACT_ADD - * transmit key 00 00 - * type for key 1 01 WEP40 - * type for key 2 00 - * type for key 3 00 - * type for key 4 00 - * key 1 39 39 39 39 39 00 00 00 - * 00 00 00 00 00 00 00 00 - * key 2 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * key 3 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * key 4 00 00 00 00 00 00 00 00 - */ - if (priv->wep_key_len[0] || priv->wep_key_len[1] || - priv->wep_key_len[2] || priv->wep_key_len[3]) { - /* Only set wep keys if we have at least one of them */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.keyindex = cpu_to_le16(priv->wep_tx_key); - cmd.action = cpu_to_le16(CMD_ACT_ADD); - - for (i = 0; i < 4; i++) { - switch (priv->wep_key_len[i]) { - case WLAN_KEY_LEN_WEP40: - cmd.keytype[i] = CMD_TYPE_WEP_40_BIT; - break; - case WLAN_KEY_LEN_WEP104: - cmd.keytype[i] = CMD_TYPE_WEP_104_BIT; - break; - default: - cmd.keytype[i] = 0; - break; - } - memcpy(cmd.keymaterial[i], priv->wep_key[i], - priv->wep_key_len[i]); - } - - ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); - } else { - /* Otherwise remove all wep keys */ - ret = lbs_remove_wep_keys(priv); - } - - lbs_deb_leave(LBS_DEB_CFG80211); - return ret; -} - - -/* - * Enable/Disable RSN status - */ -static int lbs_enable_rsn(struct lbs_private *priv, int enable) -{ - struct cmd_ds_802_11_enable_rsn cmd; - int ret; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", enable); - - /* - * cmd 2f 00 - * size 0c 00 - * sequence xx xx - * result 00 00 - * action 01 00 ACT_SET - * enable 01 00 - */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.enable = cpu_to_le16(enable); - - ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd); - - lbs_deb_leave(LBS_DEB_CFG80211); - return ret; -} - - -/* - * Set WPA/WPA key material - */ - -/* - * like "struct cmd_ds_802_11_key_material", but with cmd_header. Once we - * get rid of WEXT, this should go into host.h - */ - -struct cmd_key_material { - struct cmd_header hdr; - - __le16 action; - struct MrvlIEtype_keyParamSet param; -} __packed; - -static int lbs_set_key_material(struct lbs_private *priv, - int key_type, int key_info, - const u8 *key, u16 key_len) -{ - struct cmd_key_material cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CFG80211); - - /* - * Example for WPA (TKIP): - * - * cmd 5e 00 - * size 34 00 - * sequence xx xx - * result 00 00 - * action 01 00 - * TLV type 00 01 key param - * length 00 26 - * key type 01 00 TKIP - * key info 06 00 UNICAST | ENABLED - * key len 20 00 - * key 32 bytes - */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.param.type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); - cmd.param.length = cpu_to_le16(sizeof(cmd.param) - 4); - cmd.param.keytypeid = cpu_to_le16(key_type); - cmd.param.keyinfo = cpu_to_le16(key_info); - cmd.param.keylen = cpu_to_le16(key_len); - if (key && key_len) - memcpy(cmd.param.key, key, key_len); - - ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd); - - lbs_deb_leave(LBS_DEB_CFG80211); - return ret; -} - - -/* - * Sets the auth type (open, shared, etc) in the firmware. That - * we use CMD_802_11_AUTHENTICATE is misleading, this firmware - * command doesn't send an authentication frame at all, it just - * stores the auth_type. - */ -static int lbs_set_authtype(struct lbs_private *priv, - struct cfg80211_connect_params *sme) -{ - struct cmd_ds_802_11_authenticate cmd; - int ret; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", sme->auth_type); - - /* - * cmd 11 00 - * size 19 00 - * sequence xx xx - * result 00 00 - * BSS id 00 13 19 80 da 30 - * auth type 00 - * reserved 00 00 00 00 00 00 00 00 00 00 - */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - if (sme->bssid) - memcpy(cmd.bssid, sme->bssid, ETH_ALEN); - /* convert auth_type */ - ret = lbs_auth_to_authtype(sme->auth_type); - if (ret < 0) - goto done; - - cmd.authtype = ret; - ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd); - - done: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - -/* - * Create association request - */ -#define LBS_ASSOC_MAX_CMD_SIZE \ - (sizeof(struct cmd_ds_802_11_associate) \ - - 512 /* cmd_ds_802_11_associate.iebuf */ \ - + LBS_MAX_SSID_TLV_SIZE \ - + LBS_MAX_CHANNEL_TLV_SIZE \ - + LBS_MAX_CF_PARAM_TLV_SIZE \ - + LBS_MAX_AUTH_TYPE_TLV_SIZE \ - + LBS_MAX_WPA_TLV_SIZE) - -static int lbs_associate(struct lbs_private *priv, - struct cfg80211_bss *bss, - struct cfg80211_connect_params *sme) -{ - struct cmd_ds_802_11_associate_response *resp; - struct cmd_ds_802_11_associate *cmd = kzalloc(LBS_ASSOC_MAX_CMD_SIZE, - GFP_KERNEL); - const u8 *ssid_eid; - size_t len, resp_ie_len; - int status; - int ret; - u8 *pos = &(cmd->iebuf[0]); - u8 *tmp; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (!cmd) { - ret = -ENOMEM; - goto done; - } - - /* - * cmd 50 00 - * length 34 00 - * sequence xx xx - * result 00 00 - * BSS id 00 13 19 80 da 30 - * capabilities 11 00 - * listen interval 0a 00 - * beacon interval 00 00 - * DTIM period 00 - * TLVs xx (up to 512 bytes) - */ - cmd->hdr.command = cpu_to_le16(CMD_802_11_ASSOCIATE); - - /* Fill in static fields */ - memcpy(cmd->bssid, bss->bssid, ETH_ALEN); - cmd->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL); - cmd->capability = cpu_to_le16(bss->capability); - - /* add SSID TLV */ - rcu_read_lock(); - ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); - if (ssid_eid) - pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); - else - lbs_deb_assoc("no SSID\n"); - rcu_read_unlock(); - - /* add DS param TLV */ - if (bss->channel) - pos += lbs_add_channel_tlv(pos, bss->channel->hw_value); - else - lbs_deb_assoc("no channel\n"); - - /* add (empty) CF param TLV */ - pos += lbs_add_cf_param_tlv(pos); - - /* add rates TLV */ - tmp = pos + 4; /* skip Marvell IE header */ - pos += lbs_add_common_rates_tlv(pos, bss); - lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp); - - /* add auth type TLV */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) >= 9) - pos += lbs_add_auth_type_tlv(pos, sme->auth_type); - - /* add WPA/WPA2 TLV */ - if (sme->ie && sme->ie_len) - pos += lbs_add_wpa_tlv(pos, sme->ie, sme->ie_len); - - len = (sizeof(*cmd) - sizeof(cmd->iebuf)) + - (u16)(pos - (u8 *) &cmd->iebuf); - cmd->hdr.size = cpu_to_le16(len); - - lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_CMD", (u8 *) cmd, - le16_to_cpu(cmd->hdr.size)); - - /* store for later use */ - memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN); - - ret = lbs_cmd_with_response(priv, CMD_802_11_ASSOCIATE, cmd); - if (ret) - goto done; - - /* generate connect message to cfg80211 */ - - resp = (void *) cmd; /* recast for easier field access */ - status = le16_to_cpu(resp->statuscode); - - /* Older FW versions map the IEEE 802.11 Status Code in the association - * response to the following values returned in resp->statuscode: - * - * IEEE Status Code Marvell Status Code - * 0 -> 0x0000 ASSOC_RESULT_SUCCESS - * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED - * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED - * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED - * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED - * others -> 0x0003 ASSOC_RESULT_REFUSED - * - * Other response codes: - * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused) - * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for - * association response from the AP) - */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { - switch (status) { - case 0: - break; - case 1: - lbs_deb_assoc("invalid association parameters\n"); - status = WLAN_STATUS_CAPS_UNSUPPORTED; - break; - case 2: - lbs_deb_assoc("timer expired while waiting for AP\n"); - status = WLAN_STATUS_AUTH_TIMEOUT; - break; - case 3: - lbs_deb_assoc("association refused by AP\n"); - status = WLAN_STATUS_ASSOC_DENIED_UNSPEC; - break; - case 4: - lbs_deb_assoc("authentication refused by AP\n"); - status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; - break; - default: - lbs_deb_assoc("association failure %d\n", status); - /* v5 OLPC firmware does return the AP status code if - * it's not one of the values above. Let that through. - */ - break; - } - } - - lbs_deb_assoc("status %d, statuscode 0x%04x, capability 0x%04x, " - "aid 0x%04x\n", status, le16_to_cpu(resp->statuscode), - le16_to_cpu(resp->capability), le16_to_cpu(resp->aid)); - - resp_ie_len = le16_to_cpu(resp->hdr.size) - - sizeof(resp->hdr) - - 6; - cfg80211_connect_result(priv->dev, - priv->assoc_bss, - sme->ie, sme->ie_len, - resp->iebuf, resp_ie_len, - status, - GFP_KERNEL); - - if (status == 0) { - /* TODO: get rid of priv->connect_status */ - priv->connect_status = LBS_CONNECTED; - netif_carrier_on(priv->dev); - if (!priv->tx_pending_len) - netif_tx_wake_all_queues(priv->dev); - } - - kfree(cmd); -done: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - -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 = ieee80211_get_num_supported_channels(wiphy); - enum ieee80211_band band; - - creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + - n_channels * sizeof(void *), - GFP_ATOMIC); - if (!creq) - return NULL; - - /* SSIDs come after channels */ - creq->ssids = (void *)&creq->channels[n_channels]; - creq->n_channels = n_channels; - creq->n_ssids = 1; - - /* Scan all available channels */ - i = 0; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - int j; - - if (!wiphy->bands[band]) - continue; - - for (j = 0; j < wiphy->bands[band]->n_channels; j++) { - /* ignore disabled channels */ - if (wiphy->bands[band]->channels[j].flags & - IEEE80211_CHAN_DISABLED) - continue; - - creq->channels[i] = &wiphy->bands[band]->channels[j]; - i++; - } - } - if (i) { - /* Set real number of channels specified in creq->channels[] */ - creq->n_channels = i; - - /* Scan for the SSID we're going to connect to */ - memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len); - creq->ssids[0].ssid_len = sme->ssid_len; - } else { - /* No channels found... */ - kfree(creq); - creq = NULL; - } - - return creq; -} - -static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_connect_params *sme) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - struct cfg80211_bss *bss = NULL; - int ret = 0; - u8 preamble = RADIO_PREAMBLE_SHORT; - - if (dev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (!sme->bssid) { - struct cfg80211_scan_request *creq; - - /* - * Scan for the requested network after waiting for existing - * scans to finish. - */ - lbs_deb_assoc("assoc: waiting for existing scans\n"); - wait_event_interruptible_timeout(priv->scan_q, - (priv->scan_req == NULL), - (15 * HZ)); - - creq = _new_connect_scan_req(wiphy, sme); - if (!creq) { - ret = -EINVAL; - goto done; - } - - lbs_deb_assoc("assoc: scanning for compatible AP\n"); - _internal_start_scan(priv, true, creq); - - lbs_deb_assoc("assoc: waiting for scan to complete\n"); - wait_event_interruptible_timeout(priv->scan_q, - (priv->scan_req == NULL), - (15 * HZ)); - lbs_deb_assoc("assoc: scanning completed\n"); - } - - /* Find the BSS we want using available scan results */ - bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, - sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS, - IEEE80211_PRIVACY_ANY); - if (!bss) { - wiphy_err(wiphy, "assoc: bss %pM not in scan results\n", - sme->bssid); - ret = -ENOENT; - goto done; - } - lbs_deb_assoc("trying %pM\n", bss->bssid); - lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n", - sme->crypto.cipher_group, - sme->key_idx, sme->key_len); - - /* As this is a new connection, clear locally stored WEP keys */ - priv->wep_tx_key = 0; - memset(priv->wep_key, 0, sizeof(priv->wep_key)); - memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); - - /* set/remove WEP keys */ - switch (sme->crypto.cipher_group) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - /* Store provided WEP keys in priv-> */ - priv->wep_tx_key = sme->key_idx; - priv->wep_key_len[sme->key_idx] = sme->key_len; - memcpy(priv->wep_key[sme->key_idx], sme->key, sme->key_len); - /* Set WEP keys and WEP mode */ - lbs_set_wep_keys(priv); - priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE; - lbs_set_mac_control(priv); - /* No RSN mode for WEP */ - lbs_enable_rsn(priv, 0); - break; - case 0: /* there's no WLAN_CIPHER_SUITE_NONE definition */ - /* - * If we don't have no WEP, no WPA and no WPA2, - * we remove all keys like in the WPA/WPA2 setup, - * we just don't set RSN. - * - * Therefore: fall-through - */ - case WLAN_CIPHER_SUITE_TKIP: - case WLAN_CIPHER_SUITE_CCMP: - /* Remove WEP keys and WEP mode */ - lbs_remove_wep_keys(priv); - priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE; - lbs_set_mac_control(priv); - - /* clear the WPA/WPA2 keys */ - lbs_set_key_material(priv, - KEY_TYPE_ID_WEP, /* doesn't matter */ - KEY_INFO_WPA_UNICAST, - NULL, 0); - lbs_set_key_material(priv, - KEY_TYPE_ID_WEP, /* doesn't matter */ - KEY_INFO_WPA_MCAST, - NULL, 0); - /* RSN mode for WPA/WPA2 */ - lbs_enable_rsn(priv, sme->crypto.cipher_group != 0); - break; - default: - wiphy_err(wiphy, "unsupported cipher group 0x%x\n", - sme->crypto.cipher_group); - ret = -ENOTSUPP; - goto done; - } - - ret = lbs_set_authtype(priv, sme); - if (ret == -ENOTSUPP) { - wiphy_err(wiphy, "unsupported authtype 0x%x\n", sme->auth_type); - goto done; - } - - lbs_set_radio(priv, preamble, 1); - - /* Do the actual association */ - ret = lbs_associate(priv, bss, sme); - - done: - if (bss) - cfg80211_put_bss(wiphy, bss); - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - -int lbs_disconnect(struct lbs_private *priv, u16 reason) -{ - struct cmd_ds_802_11_deauthenticate cmd; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - /* Mildly ugly to use a locally store my own BSSID ... */ - memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN); - cmd.reasoncode = cpu_to_le16(reason); - - ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd); - if (ret) - return ret; - - cfg80211_disconnected(priv->dev, - reason, - NULL, 0, true, - GFP_KERNEL); - priv->connect_status = LBS_DISCONNECTED; - - return 0; -} - -static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, - u16 reason_code) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - - if (dev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code); - - /* store for lbs_cfg_ret_disconnect() */ - priv->disassoc_reason = reason_code; - - return lbs_disconnect(priv, reason_code); -} - -static int lbs_cfg_set_default_key(struct wiphy *wiphy, - struct net_device *netdev, - u8 key_index, bool unicast, - bool multicast) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - - if (netdev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (key_index != priv->wep_tx_key) { - lbs_deb_assoc("set_default_key: to %d\n", key_index); - priv->wep_tx_key = key_index; - lbs_set_wep_keys(priv); - } - - return 0; -} - - -static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev, - u8 idx, bool pairwise, const u8 *mac_addr, - struct key_params *params) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - u16 key_info; - u16 key_type; - int ret = 0; - - if (netdev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter(LBS_DEB_CFG80211); - - lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n", - params->cipher, mac_addr); - lbs_deb_assoc("add_key: key index %d, key len %d\n", - idx, params->key_len); - if (params->key_len) - lbs_deb_hex(LBS_DEB_CFG80211, "KEY", - params->key, params->key_len); - - lbs_deb_assoc("add_key: seq len %d\n", params->seq_len); - if (params->seq_len) - lbs_deb_hex(LBS_DEB_CFG80211, "SEQ", - params->seq, params->seq_len); - - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - /* actually compare if something has changed ... */ - if ((priv->wep_key_len[idx] != params->key_len) || - memcmp(priv->wep_key[idx], - params->key, params->key_len) != 0) { - priv->wep_key_len[idx] = params->key_len; - memcpy(priv->wep_key[idx], - params->key, params->key_len); - lbs_set_wep_keys(priv); - } - break; - case WLAN_CIPHER_SUITE_TKIP: - case WLAN_CIPHER_SUITE_CCMP: - key_info = KEY_INFO_WPA_ENABLED | ((idx == 0) - ? KEY_INFO_WPA_UNICAST - : KEY_INFO_WPA_MCAST); - key_type = (params->cipher == WLAN_CIPHER_SUITE_TKIP) - ? KEY_TYPE_ID_TKIP - : KEY_TYPE_ID_AES; - lbs_set_key_material(priv, - key_type, - key_info, - params->key, params->key_len); - break; - default: - wiphy_err(wiphy, "unhandled cipher 0x%x\n", params->cipher); - ret = -ENOTSUPP; - break; - } - - return ret; -} - - -static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev, - u8 key_index, bool pairwise, const u8 *mac_addr) -{ - - lbs_deb_enter(LBS_DEB_CFG80211); - - lbs_deb_assoc("del_key: key_idx %d, mac_addr %pM\n", - key_index, mac_addr); - -#ifdef TODO - struct lbs_private *priv = wiphy_priv(wiphy); - /* - * I think can keep this a NO-OP, because: - - * - we clear all keys whenever we do lbs_cfg_connect() anyway - * - neither "iw" nor "wpa_supplicant" won't call this during - * an ongoing connection - * - TODO: but I have to check if this is still true when - * I set the AP to periodic re-keying - * - we've not kzallec() something when we've added a key at - * lbs_cfg_connect() or lbs_cfg_add_key(). - * - * This causes lbs_cfg_del_key() only called at disconnect time, - * where we'd just waste time deleting a key that is not going - * to be used anyway. - */ - if (key_index < 3 && priv->wep_key_len[key_index]) { - priv->wep_key_len[key_index] = 0; - lbs_set_wep_keys(priv); - } -#endif - - return 0; -} - - -/* - * Get station - */ - -static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac, struct station_info *sinfo) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - s8 signal, noise; - int ret; - size_t i; - - lbs_deb_enter(LBS_DEB_CFG80211); - - sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES) | - BIT(NL80211_STA_INFO_TX_PACKETS) | - BIT(NL80211_STA_INFO_RX_BYTES) | - BIT(NL80211_STA_INFO_RX_PACKETS); - sinfo->tx_bytes = priv->dev->stats.tx_bytes; - sinfo->tx_packets = priv->dev->stats.tx_packets; - sinfo->rx_bytes = priv->dev->stats.rx_bytes; - sinfo->rx_packets = priv->dev->stats.rx_packets; - - /* Get current RSSI */ - ret = lbs_get_rssi(priv, &signal, &noise); - if (ret == 0) { - sinfo->signal = signal; - sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); - } - - /* Convert priv->cur_rate from hw_value to NL80211 value */ - for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { - if (priv->cur_rate == lbs_rates[i].hw_value) { - sinfo->txrate.legacy = lbs_rates[i].bitrate; - sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); - break; - } - } - - return 0; -} - - - - -/* - * Change interface - */ - -static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret = 0; - - if (dev == priv->mesh_dev) - return -EOPNOTSUPP; - - switch (type) { - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - break; - default: - return -EOPNOTSUPP; - } - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (priv->iface_running) - ret = lbs_set_iface_type(priv, type); - - if (!ret) - priv->wdev->iftype = type; - - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - - -/* - * IBSS (Ad-Hoc) - */ - -/* - * The firmware needs the following bits masked out of the beacon-derived - * capability field when associating/joining to a BSS: - * 9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused) - */ -#define CAPINFO_MASK (~(0xda00)) - - -static void lbs_join_post(struct lbs_private *priv, - struct cfg80211_ibss_params *params, - u8 *bssid, u16 capability) -{ - u8 fake_ie[2 + IEEE80211_MAX_SSID_LEN + /* ssid */ - 2 + 4 + /* basic rates */ - 2 + 1 + /* DS parameter */ - 2 + 2 + /* atim */ - 2 + 8]; /* extended rates */ - u8 *fake = fake_ie; - struct cfg80211_bss *bss; - - lbs_deb_enter(LBS_DEB_CFG80211); - - /* - * For cfg80211_inform_bss, we'll need a fake IE, as we can't get - * the real IE from the firmware. So we fabricate a fake IE based on - * what the firmware actually sends (sniffed with wireshark). - */ - /* Fake SSID IE */ - *fake++ = WLAN_EID_SSID; - *fake++ = params->ssid_len; - memcpy(fake, params->ssid, params->ssid_len); - fake += params->ssid_len; - /* Fake supported basic rates IE */ - *fake++ = WLAN_EID_SUPP_RATES; - *fake++ = 4; - *fake++ = 0x82; - *fake++ = 0x84; - *fake++ = 0x8b; - *fake++ = 0x96; - /* Fake DS channel IE */ - *fake++ = WLAN_EID_DS_PARAMS; - *fake++ = 1; - *fake++ = params->chandef.chan->hw_value; - /* Fake IBSS params IE */ - *fake++ = WLAN_EID_IBSS_PARAMS; - *fake++ = 2; - *fake++ = 0; /* ATIM=0 */ - *fake++ = 0; - /* Fake extended rates IE, TODO: don't add this for 802.11b only, - * but I don't know how this could be checked */ - *fake++ = WLAN_EID_EXT_SUPP_RATES; - *fake++ = 8; - *fake++ = 0x0c; - *fake++ = 0x12; - *fake++ = 0x18; - *fake++ = 0x24; - *fake++ = 0x30; - *fake++ = 0x48; - *fake++ = 0x60; - *fake++ = 0x6c; - lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie); - - bss = cfg80211_inform_bss(priv->wdev->wiphy, - params->chandef.chan, - CFG80211_BSS_FTYPE_UNKNOWN, - bssid, - 0, - capability, - params->beacon_interval, - fake_ie, fake - fake_ie, - 0, GFP_KERNEL); - cfg80211_put_bss(priv->wdev->wiphy, bss); - - memcpy(priv->wdev->ssid, params->ssid, params->ssid_len); - priv->wdev->ssid_len = params->ssid_len; - - cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan, - GFP_KERNEL); - - /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */ - priv->connect_status = LBS_CONNECTED; - netif_carrier_on(priv->dev); - if (!priv->tx_pending_len) - netif_wake_queue(priv->dev); - - lbs_deb_leave(LBS_DEB_CFG80211); -} - -static int lbs_ibss_join_existing(struct lbs_private *priv, - struct cfg80211_ibss_params *params, - struct cfg80211_bss *bss) -{ - const u8 *rates_eid; - struct cmd_ds_802_11_ad_hoc_join cmd; - u8 preamble = RADIO_PREAMBLE_SHORT; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CFG80211); - - /* TODO: set preamble based on scan result */ - ret = lbs_set_radio(priv, preamble, 1); - if (ret) - goto out; - - /* - * Example CMD_802_11_AD_HOC_JOIN command: - * - * command 2c 00 CMD_802_11_AD_HOC_JOIN - * size 65 00 - * sequence xx xx - * result 00 00 - * bssid 02 27 27 97 2f 96 - * ssid 49 42 53 53 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * type 02 CMD_BSS_TYPE_IBSS - * beacon period 64 00 - * dtim period 00 - * timestamp 00 00 00 00 00 00 00 00 - * localtime 00 00 00 00 00 00 00 00 - * IE DS 03 - * IE DS len 01 - * IE DS channel 01 - * reserveed 00 00 00 00 - * IE IBSS 06 - * IE IBSS len 02 - * IE IBSS atim 00 00 - * reserved 00 00 00 00 - * capability 02 00 - * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c 00 - * fail timeout ff 00 - * probe delay 00 00 - */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - - memcpy(cmd.bss.bssid, bss->bssid, ETH_ALEN); - memcpy(cmd.bss.ssid, params->ssid, params->ssid_len); - cmd.bss.type = CMD_BSS_TYPE_IBSS; - cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval); - cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS; - cmd.bss.ds.header.len = 1; - cmd.bss.ds.channel = params->chandef.chan->hw_value; - cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS; - cmd.bss.ibss.header.len = 2; - cmd.bss.ibss.atimwindow = 0; - cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK); - - /* set rates to the intersection of our rates and the rates in the - bss */ - rcu_read_lock(); - rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); - if (!rates_eid) { - lbs_add_rates(cmd.bss.rates); - } else { - int hw, i; - u8 rates_max = rates_eid[1]; - u8 *rates = cmd.bss.rates; - for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { - u8 hw_rate = lbs_rates[hw].bitrate / 5; - for (i = 0; i < rates_max; i++) { - if (hw_rate == (rates_eid[i+2] & 0x7f)) { - u8 rate = rates_eid[i+2]; - if (rate == 0x02 || rate == 0x04 || - rate == 0x0b || rate == 0x16) - rate |= 0x80; - *rates++ = rate; - } - } - } - } - rcu_read_unlock(); - - /* Only v8 and below support setting this */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { - cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); - cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); - } - ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd); - if (ret) - goto out; - - /* - * This is a sample response to CMD_802_11_AD_HOC_JOIN: - * - * response 2c 80 - * size 09 00 - * sequence xx xx - * result 00 00 - * reserved 00 - */ - lbs_join_post(priv, params, bss->bssid, bss->capability); - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - - -static int lbs_ibss_start_new(struct lbs_private *priv, - struct cfg80211_ibss_params *params) -{ - struct cmd_ds_802_11_ad_hoc_start cmd; - struct cmd_ds_802_11_ad_hoc_result *resp = - (struct cmd_ds_802_11_ad_hoc_result *) &cmd; - u8 preamble = RADIO_PREAMBLE_SHORT; - int ret = 0; - u16 capability; - - lbs_deb_enter(LBS_DEB_CFG80211); - - ret = lbs_set_radio(priv, preamble, 1); - if (ret) - goto out; - - /* - * Example CMD_802_11_AD_HOC_START command: - * - * command 2b 00 CMD_802_11_AD_HOC_START - * size b1 00 - * sequence xx xx - * result 00 00 - * ssid 54 45 53 54 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * bss type 02 - * beacon period 64 00 - * dtim period 00 - * IE IBSS 06 - * IE IBSS len 02 - * IE IBSS atim 00 00 - * reserved 00 00 00 00 - * IE DS 03 - * IE DS len 01 - * IE DS channel 01 - * reserved 00 00 00 00 - * probe delay 00 00 - * capability 02 00 - * rates 82 84 8b 96 (basic rates with have bit 7 set) - * 0c 12 18 24 30 48 60 6c - * padding 100 bytes - */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - memcpy(cmd.ssid, params->ssid, params->ssid_len); - cmd.bsstype = CMD_BSS_TYPE_IBSS; - cmd.beaconperiod = cpu_to_le16(params->beacon_interval); - cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS; - cmd.ibss.header.len = 2; - cmd.ibss.atimwindow = 0; - cmd.ds.header.id = WLAN_EID_DS_PARAMS; - cmd.ds.header.len = 1; - cmd.ds.channel = params->chandef.chan->hw_value; - /* Only v8 and below support setting probe delay */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) - cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); - /* TODO: mix in WLAN_CAPABILITY_PRIVACY */ - capability = WLAN_CAPABILITY_IBSS; - cmd.capability = cpu_to_le16(capability); - lbs_add_rates(cmd.rates); - - - ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd); - if (ret) - goto out; - - /* - * This is a sample response to CMD_802_11_AD_HOC_JOIN: - * - * response 2b 80 - * size 14 00 - * sequence xx xx - * result 00 00 - * reserved 00 - * bssid 02 2b 7b 0f 86 0e - */ - lbs_join_post(priv, params, resp->bssid, capability); - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - -static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_ibss_params *params) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret = 0; - struct cfg80211_bss *bss; - - if (dev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (!params->chandef.chan) { - ret = -ENOTSUPP; - goto out; - } - - ret = lbs_set_channel(priv, params->chandef.chan->hw_value); - if (ret) - goto out; - - /* Search if someone is beaconing. This assumes that the - * bss list is populated already */ - bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid, - params->ssid, params->ssid_len, - IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY); - - if (bss) { - ret = lbs_ibss_join_existing(priv, params, bss); - cfg80211_put_bss(wiphy, bss); - } else - ret = lbs_ibss_start_new(priv, params); - - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - -static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - struct cmd_ds_802_11_ad_hoc_stop cmd; - int ret = 0; - - if (dev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter(LBS_DEB_CFG80211); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd); - - /* TODO: consider doing this at MACREG_INT_CODE_ADHOC_BCN_LOST time */ - lbs_mac_event_disconnected(priv, true); - - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - - - -/* - * Initialization - */ - -static struct cfg80211_ops lbs_cfg80211_ops = { - .set_monitor_channel = lbs_cfg_set_monitor_channel, - .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel, - .scan = lbs_cfg_scan, - .connect = lbs_cfg_connect, - .disconnect = lbs_cfg_disconnect, - .add_key = lbs_cfg_add_key, - .del_key = lbs_cfg_del_key, - .set_default_key = lbs_cfg_set_default_key, - .get_station = lbs_cfg_get_station, - .change_virtual_intf = lbs_change_intf, - .join_ibss = lbs_join_ibss, - .leave_ibss = lbs_leave_ibss, -}; - - -/* - * At this time lbs_private *priv doesn't even exist, so we just allocate - * memory and don't initialize the wiphy further. This is postponed until we - * can talk to the firmware and happens at registration time in - * lbs_cfg_wiphy_register(). - */ -struct wireless_dev *lbs_cfg_alloc(struct device *dev) -{ - int ret = 0; - struct wireless_dev *wdev; - - lbs_deb_enter(LBS_DEB_CFG80211); - - wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); - if (!wdev) - return ERR_PTR(-ENOMEM); - - wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private)); - if (!wdev->wiphy) { - dev_err(dev, "cannot allocate wiphy\n"); - ret = -ENOMEM; - goto err_wiphy_new; - } - - lbs_deb_leave(LBS_DEB_CFG80211); - return wdev; - - err_wiphy_new: - kfree(wdev); - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ERR_PTR(ret); -} - - -static void lbs_cfg_set_regulatory_hint(struct lbs_private *priv) -{ - struct region_code_mapping { - const char *cn; - int code; - }; - - /* Section 5.17.2 */ - static const struct region_code_mapping regmap[] = { - {"US ", 0x10}, /* US FCC */ - {"CA ", 0x20}, /* Canada */ - {"EU ", 0x30}, /* ETSI */ - {"ES ", 0x31}, /* Spain */ - {"FR ", 0x32}, /* France */ - {"JP ", 0x40}, /* Japan */ - }; - size_t i; - - lbs_deb_enter(LBS_DEB_CFG80211); - - for (i = 0; i < ARRAY_SIZE(regmap); i++) - if (regmap[i].code == priv->regioncode) { - regulatory_hint(priv->wdev->wiphy, regmap[i].cn); - break; - } - - lbs_deb_leave(LBS_DEB_CFG80211); -} - -static void lbs_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - - lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain " - "callback for domain %c%c\n", request->alpha2[0], - request->alpha2[1]); - - memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); - if (lbs_iface_active(priv)) - lbs_set_11d_domain_info(priv); - - lbs_deb_leave(LBS_DEB_CFG80211); -} - -/* - * This function get's called after lbs_setup_firmware() determined the - * firmware capabities. So we can setup the wiphy according to our - * hardware/firmware. - */ -int lbs_cfg_register(struct lbs_private *priv) -{ - struct wireless_dev *wdev = priv->wdev; - int ret; - - lbs_deb_enter(LBS_DEB_CFG80211); - - wdev->wiphy->max_scan_ssids = 1; - wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - - wdev->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC); - if (lbs_rtap_supported(priv)) - wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); - if (lbs_mesh_activated(priv)) - wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT); - - wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz; - - /* - * We could check priv->fwcapinfo && FW_CAPINFO_WPA, but I have - * never seen a firmware without WPA - */ - wdev->wiphy->cipher_suites = cipher_suites; - wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); - wdev->wiphy->reg_notifier = lbs_reg_notifier; - - ret = wiphy_register(wdev->wiphy); - if (ret < 0) - pr_err("cannot register wiphy device\n"); - - priv->wiphy_registered = true; - - ret = register_netdev(priv->dev); - if (ret) - pr_err("cannot register network device\n"); - - INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker); - - lbs_cfg_set_regulatory_hint(priv); - - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - -void lbs_scan_deinit(struct lbs_private *priv) -{ - lbs_deb_enter(LBS_DEB_CFG80211); - cancel_delayed_work_sync(&priv->scan_work); -} - - -void lbs_cfg_free(struct lbs_private *priv) -{ - struct wireless_dev *wdev = priv->wdev; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (!wdev) - return; - - if (priv->wiphy_registered) - wiphy_unregister(wdev->wiphy); - - if (wdev->wiphy) - wiphy_free(wdev->wiphy); - - kfree(wdev); -} diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h deleted file mode 100644 index acccc2922401..000000000000 --- a/drivers/net/wireless/libertas/cfg.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __LBS_CFG80211_H__ -#define __LBS_CFG80211_H__ - -struct device; -struct lbs_private; -struct regulatory_request; -struct wiphy; - -struct wireless_dev *lbs_cfg_alloc(struct device *dev); -int lbs_cfg_register(struct lbs_private *priv); -void lbs_cfg_free(struct lbs_private *priv); - -void lbs_send_disconnect_notification(struct lbs_private *priv, - bool locally_generated); -void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event); - -void lbs_scan_done(struct lbs_private *priv); -void lbs_scan_deinit(struct lbs_private *priv); -int lbs_disconnect(struct lbs_private *priv, u16 reason); - -#endif diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c deleted file mode 100644 index 0387a5b380c8..000000000000 --- a/drivers/net/wireless/libertas/cmd.c +++ /dev/null @@ -1,1725 +0,0 @@ -/* - * This file contains the handling of command. - * It prepares command and sends it to firmware when it is ready. - */ - -#include -#include -#include -#include -#include -#include - -#include "decl.h" -#include "cfg.h" -#include "cmd.h" - -#define CAL_NF(nf) ((s32)(-(s32)(nf))) -#define CAL_RSSI(snr, nf) ((s32)((s32)(snr) + CAL_NF(nf))) - -/** - * lbs_cmd_copyback - Simple callback that copies response back into command - * - * @priv: A pointer to &struct lbs_private structure - * @extra: A pointer to the original command structure for which - * 'resp' is a response - * @resp: A pointer to the command response - * - * returns: 0 on success, error on failure - */ -int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, - struct cmd_header *resp) -{ - struct cmd_header *buf = (void *)extra; - uint16_t copy_len; - - copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); - memcpy(buf, resp, copy_len); - return 0; -} -EXPORT_SYMBOL_GPL(lbs_cmd_copyback); - -/** - * lbs_cmd_async_callback - Simple callback that ignores the result. - * Use this if you just want to send a command to the hardware, but don't - * care for the result. - * - * @priv: ignored - * @extra: ignored - * @resp: ignored - * - * returns: 0 for success - */ -static int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra, - struct cmd_header *resp) -{ - return 0; -} - - -/** - * is_command_allowed_in_ps - tests if a command is allowed in Power Save mode - * - * @cmd: the command ID - * - * returns: 1 if allowed, 0 if not allowed - */ -static u8 is_command_allowed_in_ps(u16 cmd) -{ - switch (cmd) { - case CMD_802_11_RSSI: - return 1; - case CMD_802_11_HOST_SLEEP_CFG: - return 1; - default: - break; - } - return 0; -} - -/** - * lbs_update_hw_spec - Updates the hardware details like MAC address - * and regulatory region - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: 0 on success, error on failure - */ -int lbs_update_hw_spec(struct lbs_private *priv) -{ - struct cmd_ds_get_hw_spec cmd; - int ret = -1; - u32 i; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); - ret = lbs_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); - if (ret) - goto out; - - priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); - - /* The firmware release is in an interesting format: the patch - * level is in the most significant nibble ... so fix that: */ - priv->fwrelease = le32_to_cpu(cmd.fwrelease); - priv->fwrelease = (priv->fwrelease << 8) | - (priv->fwrelease >> 24 & 0xff); - - /* Some firmware capabilities: - * CF card firmware 5.0.16p0: cap 0x00000303 - * USB dongle firmware 5.110.17p2: cap 0x00000303 - */ - netdev_info(priv->dev, "%pM, fw %u.%u.%up%u, cap 0x%08x\n", - cmd.permanentaddr, - priv->fwrelease >> 24 & 0xff, - priv->fwrelease >> 16 & 0xff, - priv->fwrelease >> 8 & 0xff, - priv->fwrelease & 0xff, - priv->fwcapinfo); - lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", - cmd.hwifversion, cmd.version); - - /* Clamp region code to 8-bit since FW spec indicates that it should - * only ever be 8-bit, even though the field size is 16-bit. Some firmware - * returns non-zero high 8 bits here. - * - * Firmware version 4.0.102 used in CF8381 has region code shifted. We - * need to check for this problem and handle it properly. - */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4) - priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF; - else - priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; - - for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { - /* use the region code to search for the index */ - if (priv->regioncode == lbs_region_code_to_index[i]) - break; - } - - /* if it's unidentified region code, use the default (USA) */ - if (i >= MRVDRV_MAX_REGION_CODE) { - priv->regioncode = 0x10; - netdev_info(priv->dev, - "unidentified region code; using the default (USA)\n"); - } - - if (priv->current_addr[0] == 0xff) - memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); - - if (!priv->copied_hwaddr) { - memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN); - if (priv->mesh_dev) - memcpy(priv->mesh_dev->dev_addr, - priv->current_addr, ETH_ALEN); - priv->copied_hwaddr = 1; - } - -out: - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy, - struct cmd_header *resp) -{ - lbs_deb_enter(LBS_DEB_CMD); - if (priv->is_host_sleep_activated) { - priv->is_host_sleep_configured = 0; - if (priv->psstate == PS_STATE_FULL_POWER) { - priv->is_host_sleep_activated = 0; - wake_up_interruptible(&priv->host_sleep_q); - } - } else { - priv->is_host_sleep_configured = 1; - } - lbs_deb_leave(LBS_DEB_CMD); - return 0; -} - -int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, - struct wol_config *p_wol_config) -{ - struct cmd_ds_host_sleep cmd_config; - int ret; - - /* - * Certain firmware versions do not support EHS_REMOVE_WAKEUP command - * and the card will return a failure. Since we need to be - * able to reset the mask, in those cases we set a 0 mask instead. - */ - if (criteria == EHS_REMOVE_WAKEUP && !priv->ehs_remove_supported) - criteria = 0; - - cmd_config.hdr.size = cpu_to_le16(sizeof(cmd_config)); - cmd_config.criteria = cpu_to_le32(criteria); - cmd_config.gpio = priv->wol_gpio; - cmd_config.gap = priv->wol_gap; - - if (p_wol_config != NULL) - memcpy((uint8_t *)&cmd_config.wol_conf, (uint8_t *)p_wol_config, - sizeof(struct wol_config)); - else - cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE; - - ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr, - le16_to_cpu(cmd_config.hdr.size), - lbs_ret_host_sleep_cfg, 0); - if (!ret) { - if (p_wol_config) - memcpy((uint8_t *) p_wol_config, - (uint8_t *)&cmd_config.wol_conf, - sizeof(struct wol_config)); - } else { - netdev_info(priv->dev, "HOST_SLEEP_CFG failed %d\n", ret); - } - - return ret; -} -EXPORT_SYMBOL_GPL(lbs_host_sleep_cfg); - -/** - * lbs_set_ps_mode - Sets the Power Save mode - * - * @priv: A pointer to &struct lbs_private structure - * @cmd_action: The Power Save operation (PS_MODE_ACTION_ENTER_PS or - * PS_MODE_ACTION_EXIT_PS) - * @block: Whether to block on a response or not - * - * returns: 0 on success, error on failure - */ -int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block) -{ - struct cmd_ds_802_11_ps_mode cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(cmd_action); - - if (cmd_action == PS_MODE_ACTION_ENTER_PS) { - lbs_deb_cmd("PS_MODE: action ENTER_PS\n"); - cmd.multipledtim = cpu_to_le16(1); /* Default DTIM multiple */ - } else if (cmd_action == PS_MODE_ACTION_EXIT_PS) { - lbs_deb_cmd("PS_MODE: action EXIT_PS\n"); - } else { - /* We don't handle CONFIRM_SLEEP here because it needs to - * be fastpathed to the firmware. - */ - lbs_deb_cmd("PS_MODE: unknown action 0x%X\n", cmd_action); - ret = -EOPNOTSUPP; - goto out; - } - - if (block) - ret = lbs_cmd_with_response(priv, CMD_802_11_PS_MODE, &cmd); - else - lbs_cmd_async(priv, CMD_802_11_PS_MODE, &cmd.hdr, sizeof (cmd)); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, - struct sleep_params *sp) -{ - struct cmd_ds_802_11_sleep_params cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - if (cmd_action == CMD_ACT_GET) { - memset(&cmd, 0, sizeof(cmd)); - } else { - cmd.error = cpu_to_le16(sp->sp_error); - cmd.offset = cpu_to_le16(sp->sp_offset); - cmd.stabletime = cpu_to_le16(sp->sp_stabletime); - cmd.calcontrol = sp->sp_calcontrol; - cmd.externalsleepclk = sp->sp_extsleepclk; - cmd.reserved = cpu_to_le16(sp->sp_reserved); - } - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(cmd_action); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SLEEP_PARAMS, &cmd); - - if (!ret) { - lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, " - "calcontrol 0x%x extsleepclk 0x%x\n", - le16_to_cpu(cmd.error), le16_to_cpu(cmd.offset), - le16_to_cpu(cmd.stabletime), cmd.calcontrol, - cmd.externalsleepclk); - - sp->sp_error = le16_to_cpu(cmd.error); - sp->sp_offset = le16_to_cpu(cmd.offset); - sp->sp_stabletime = le16_to_cpu(cmd.stabletime); - sp->sp_calcontrol = cmd.calcontrol; - sp->sp_extsleepclk = cmd.externalsleepclk; - sp->sp_reserved = le16_to_cpu(cmd.reserved); - } - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return 0; -} - -static int lbs_wait_for_ds_awake(struct lbs_private *priv) -{ - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - if (priv->is_deep_sleep) { - if (!wait_event_interruptible_timeout(priv->ds_awake_q, - !priv->is_deep_sleep, (10 * HZ))) { - netdev_err(priv->dev, "ds_awake_q: timer expired\n"); - ret = -1; - } - } - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep) -{ - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - if (deep_sleep) { - if (priv->is_deep_sleep != 1) { - lbs_deb_cmd("deep sleep: sleep\n"); - BUG_ON(!priv->enter_deep_sleep); - ret = priv->enter_deep_sleep(priv); - if (!ret) { - netif_stop_queue(priv->dev); - netif_carrier_off(priv->dev); - } - } else { - netdev_err(priv->dev, "deep sleep: already enabled\n"); - } - } else { - if (priv->is_deep_sleep) { - lbs_deb_cmd("deep sleep: wakeup\n"); - BUG_ON(!priv->exit_deep_sleep); - ret = priv->exit_deep_sleep(priv); - if (!ret) { - ret = lbs_wait_for_ds_awake(priv); - if (ret) - netdev_err(priv->dev, - "deep sleep: wakeup failed\n"); - } - } - } - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -static int lbs_ret_host_sleep_activate(struct lbs_private *priv, - unsigned long dummy, - struct cmd_header *cmd) -{ - lbs_deb_enter(LBS_DEB_FW); - priv->is_host_sleep_activated = 1; - wake_up_interruptible(&priv->host_sleep_q); - lbs_deb_leave(LBS_DEB_FW); - return 0; -} - -int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep) -{ - struct cmd_header cmd; - int ret = 0; - uint32_t criteria = EHS_REMOVE_WAKEUP; - - lbs_deb_enter(LBS_DEB_CMD); - - if (host_sleep) { - if (priv->is_host_sleep_activated != 1) { - memset(&cmd, 0, sizeof(cmd)); - ret = lbs_host_sleep_cfg(priv, priv->wol_criteria, - (struct wol_config *)NULL); - if (ret) { - netdev_info(priv->dev, - "Host sleep configuration failed: %d\n", - ret); - return ret; - } - if (priv->psstate == PS_STATE_FULL_POWER) { - ret = __lbs_cmd(priv, - CMD_802_11_HOST_SLEEP_ACTIVATE, - &cmd, - sizeof(cmd), - lbs_ret_host_sleep_activate, 0); - if (ret) - netdev_info(priv->dev, - "HOST_SLEEP_ACTIVATE failed: %d\n", - ret); - } - - if (!wait_event_interruptible_timeout( - priv->host_sleep_q, - priv->is_host_sleep_activated, - (10 * HZ))) { - netdev_err(priv->dev, - "host_sleep_q: timer expired\n"); - ret = -1; - } - } else { - netdev_err(priv->dev, "host sleep: already enabled\n"); - } - } else { - if (priv->is_host_sleep_activated) - ret = lbs_host_sleep_cfg(priv, criteria, - (struct wol_config *)NULL); - } - - return ret; -} - -/** - * lbs_set_snmp_mib - Set an SNMP MIB value - * - * @priv: A pointer to &struct lbs_private structure - * @oid: The OID to set in the firmware - * @val: Value to set the OID to - * - * returns: 0 on success, error on failure - */ -int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val) -{ - struct cmd_ds_802_11_snmp_mib cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof (cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.oid = cpu_to_le16((u16) oid); - - switch (oid) { - case SNMP_MIB_OID_BSS_TYPE: - cmd.bufsize = cpu_to_le16(sizeof(u8)); - cmd.value[0] = val; - break; - case SNMP_MIB_OID_11D_ENABLE: - case SNMP_MIB_OID_FRAG_THRESHOLD: - case SNMP_MIB_OID_RTS_THRESHOLD: - case SNMP_MIB_OID_SHORT_RETRY_LIMIT: - case SNMP_MIB_OID_LONG_RETRY_LIMIT: - cmd.bufsize = cpu_to_le16(sizeof(u16)); - *((__le16 *)(&cmd.value)) = cpu_to_le16(val); - break; - default: - lbs_deb_cmd("SNMP_CMD: (set) unhandled OID 0x%x\n", oid); - ret = -EINVAL; - goto out; - } - - lbs_deb_cmd("SNMP_CMD: (set) oid 0x%x, oid size 0x%x, value 0x%x\n", - le16_to_cpu(cmd.oid), le16_to_cpu(cmd.bufsize), val); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_get_snmp_mib - Get an SNMP MIB value - * - * @priv: A pointer to &struct lbs_private structure - * @oid: The OID to retrieve from the firmware - * @out_val: Location for the returned value - * - * returns: 0 on success, error on failure - */ -int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val) -{ - struct cmd_ds_802_11_snmp_mib cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof (cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_GET); - cmd.oid = cpu_to_le16(oid); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); - if (ret) - goto out; - - switch (le16_to_cpu(cmd.bufsize)) { - case sizeof(u8): - *out_val = cmd.value[0]; - break; - case sizeof(u16): - *out_val = le16_to_cpu(*((__le16 *)(&cmd.value))); - break; - default: - lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n", - oid, le16_to_cpu(cmd.bufsize)); - break; - } - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_get_tx_power - Get the min, max, and current TX power - * - * @priv: A pointer to &struct lbs_private structure - * @curlevel: Current power level in dBm - * @minlevel: Minimum supported power level in dBm (optional) - * @maxlevel: Maximum supported power level in dBm (optional) - * - * returns: 0 on success, error on failure - */ -int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, - s16 *maxlevel) -{ - struct cmd_ds_802_11_rf_tx_power cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_GET); - - ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); - if (ret == 0) { - *curlevel = le16_to_cpu(cmd.curlevel); - if (minlevel) - *minlevel = cmd.minlevel; - if (maxlevel) - *maxlevel = cmd.maxlevel; - } - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -/** - * lbs_set_tx_power - Set the TX power - * - * @priv: A pointer to &struct lbs_private structure - * @dbm: The desired power level in dBm - * - * returns: 0 on success, error on failure - */ -int lbs_set_tx_power(struct lbs_private *priv, s16 dbm) -{ - struct cmd_ds_802_11_rf_tx_power cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.curlevel = cpu_to_le16(dbm); - - lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm); - - ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -/** - * lbs_set_monitor_mode - Enable or disable monitor mode - * (only implemented on OLPC usb8388 FW) - * - * @priv: A pointer to &struct lbs_private structure - * @enable: 1 to enable monitor mode, 0 to disable - * - * returns: 0 on success, error on failure - */ -int lbs_set_monitor_mode(struct lbs_private *priv, int enable) -{ - struct cmd_ds_802_11_monitor_mode cmd; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - if (enable) - cmd.mode = cpu_to_le16(0x1); - - lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable); - - ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd); - if (ret == 0) { - priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP : - ARPHRD_ETHER; - } - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -/** - * lbs_get_channel - Get the radio channel - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: The channel on success, error on failure - */ -static int lbs_get_channel(struct lbs_private *priv) -{ - struct cmd_ds_802_11_rf_channel cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_GET); - - ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); - if (ret) - goto out; - - ret = le16_to_cpu(cmd.channel); - lbs_deb_cmd("current radio channel is %d\n", ret); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -int lbs_update_channel(struct lbs_private *priv) -{ - int ret; - - /* the channel in f/w could be out of sync; get the current channel */ - lbs_deb_enter(LBS_DEB_ASSOC); - - ret = lbs_get_channel(priv); - if (ret > 0) { - priv->channel = ret; - ret = 0; - } - lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); - return ret; -} - -/** - * lbs_set_channel - Set the radio channel - * - * @priv: A pointer to &struct lbs_private structure - * @channel: The desired channel, or 0 to clear a locked channel - * - * returns: 0 on success, error on failure - */ -int lbs_set_channel(struct lbs_private *priv, u8 channel) -{ - struct cmd_ds_802_11_rf_channel cmd; -#ifdef DEBUG - u8 old_channel = priv->channel; -#endif - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); - cmd.channel = cpu_to_le16(channel); - - ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); - if (ret) - goto out; - - priv->channel = (uint8_t) le16_to_cpu(cmd.channel); - lbs_deb_cmd("channel switch from %d to %d\n", old_channel, - priv->channel); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_get_rssi - Get current RSSI and noise floor - * - * @priv: A pointer to &struct lbs_private structure - * @rssi: On successful return, signal level in mBm - * @nf: On successful return, Noise floor - * - * returns: The channel on success, error on failure - */ -int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf) -{ - struct cmd_ds_802_11_rssi cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - BUG_ON(rssi == NULL); - BUG_ON(nf == NULL); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - /* Average SNR over last 8 beacons */ - cmd.n_or_snr = cpu_to_le16(8); - - ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd); - if (ret == 0) { - *nf = CAL_NF(le16_to_cpu(cmd.nf)); - *rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf)); - } - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_set_11d_domain_info - Send regulatory and 802.11d domain information - * to the firmware - * - * @priv: pointer to &struct lbs_private - * - * returns: 0 on success, error code on failure -*/ -int lbs_set_11d_domain_info(struct lbs_private *priv) -{ - struct wiphy *wiphy = priv->wdev->wiphy; - struct ieee80211_supported_band **bands = wiphy->bands; - struct cmd_ds_802_11d_domain_info cmd; - struct mrvl_ie_domain_param_set *domain = &cmd.domain; - struct ieee80211_country_ie_triplet *t; - enum ieee80211_band band; - struct ieee80211_channel *ch; - u8 num_triplet = 0; - u8 num_parsed_chan = 0; - u8 first_channel = 0, next_chan = 0, max_pwr = 0; - u8 i, flag = 0; - size_t triplet_size; - int ret = 0; - - lbs_deb_enter(LBS_DEB_11D); - if (!priv->country_code[0]) - goto out; - - memset(&cmd, 0, sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - - lbs_deb_11d("Setting country code '%c%c'\n", - priv->country_code[0], priv->country_code[1]); - - domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); - - /* Set country code */ - domain->country_code[0] = priv->country_code[0]; - domain->country_code[1] = priv->country_code[1]; - domain->country_code[2] = ' '; - - /* Now set up the channel triplets; firmware is somewhat picky here - * and doesn't validate channel numbers and spans; hence it would - * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39. Since - * the last 3 aren't valid channels, the driver is responsible for - * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20) - * etc. - */ - for (band = 0; - (band < IEEE80211_NUM_BANDS) && (num_triplet < MAX_11D_TRIPLETS); - band++) { - - if (!bands[band]) - continue; - - for (i = 0; - (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS); - i++) { - ch = &bands[band]->channels[i]; - if (ch->flags & IEEE80211_CHAN_DISABLED) - continue; - - if (!flag) { - flag = 1; - next_chan = first_channel = (u32) ch->hw_value; - max_pwr = ch->max_power; - num_parsed_chan = 1; - continue; - } - - if ((ch->hw_value == next_chan + 1) && - (ch->max_power == max_pwr)) { - /* Consolidate adjacent channels */ - next_chan++; - num_parsed_chan++; - } else { - /* Add this triplet */ - lbs_deb_11d("11D triplet (%d, %d, %d)\n", - first_channel, num_parsed_chan, - max_pwr); - t = &domain->triplet[num_triplet]; - t->chans.first_channel = first_channel; - t->chans.num_channels = num_parsed_chan; - t->chans.max_power = max_pwr; - num_triplet++; - flag = 0; - } - } - - if (flag) { - /* Add last triplet */ - lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel, - num_parsed_chan, max_pwr); - t = &domain->triplet[num_triplet]; - t->chans.first_channel = first_channel; - t->chans.num_channels = num_parsed_chan; - t->chans.max_power = max_pwr; - num_triplet++; - } - } - - lbs_deb_11d("# triplets %d\n", num_triplet); - - /* Set command header sizes */ - triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet); - domain->header.len = cpu_to_le16(sizeof(domain->country_code) + - triplet_size); - - lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set", - (u8 *) &cmd.domain.country_code, - le16_to_cpu(domain->header.len)); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) + - sizeof(cmd.action) + - sizeof(cmd.domain.header) + - sizeof(cmd.domain.country_code) + - triplet_size); - - ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd); - -out: - lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); - return ret; -} - -/** - * lbs_get_reg - Read a MAC, Baseband, or RF register - * - * @priv: pointer to &struct lbs_private - * @reg: register command, one of CMD_MAC_REG_ACCESS, - * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS - * @offset: byte offset of the register to get - * @value: on success, the value of the register at 'offset' - * - * returns: 0 on success, error code on failure -*/ -int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) -{ - struct cmd_ds_reg_access cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - BUG_ON(value == NULL); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_GET); - cmd.offset = cpu_to_le16(offset); - - if (reg != CMD_MAC_REG_ACCESS && - reg != CMD_BBP_REG_ACCESS && - reg != CMD_RF_REG_ACCESS) { - ret = -EINVAL; - goto out; - } - - ret = lbs_cmd_with_response(priv, reg, &cmd); - if (!ret) { - if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) - *value = cmd.value.bbp_rf; - else if (reg == CMD_MAC_REG_ACCESS) - *value = le32_to_cpu(cmd.value.mac); - } - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_set_reg - Write a MAC, Baseband, or RF register - * - * @priv: pointer to &struct lbs_private - * @reg: register command, one of CMD_MAC_REG_ACCESS, - * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS - * @offset: byte offset of the register to set - * @value: the value to write to the register at 'offset' - * - * returns: 0 on success, error code on failure -*/ -int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value) -{ - struct cmd_ds_reg_access cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.offset = cpu_to_le16(offset); - - if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) - cmd.value.bbp_rf = (u8) (value & 0xFF); - else if (reg == CMD_MAC_REG_ACCESS) - cmd.value.mac = cpu_to_le32(value); - else { - ret = -EINVAL; - goto out; - } - - ret = lbs_cmd_with_response(priv, reg, &cmd); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -static void lbs_queue_cmd(struct lbs_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - unsigned long flags; - int addtail = 1; - - lbs_deb_enter(LBS_DEB_HOST); - - if (!cmdnode) { - lbs_deb_host("QUEUE_CMD: cmdnode is NULL\n"); - goto done; - } - if (!cmdnode->cmdbuf->size) { - lbs_deb_host("DNLD_CMD: cmd size is zero\n"); - goto done; - } - cmdnode->result = 0; - - /* Exit_PS command needs to be queued in the header always. */ - if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) { - struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf; - - if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { - if (priv->psstate != PS_STATE_FULL_POWER) - addtail = 0; - } - } - - if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_WAKEUP_CONFIRM) - addtail = 0; - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (addtail) - list_add_tail(&cmdnode->list, &priv->cmdpendingq); - else - list_add(&cmdnode->list, &priv->cmdpendingq); - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", - le16_to_cpu(cmdnode->cmdbuf->command)); - -done: - lbs_deb_leave(LBS_DEB_HOST); -} - -static void lbs_submit_command(struct lbs_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - unsigned long flags; - struct cmd_header *cmd; - uint16_t cmdsize; - uint16_t command; - int timeo = 3 * HZ; - int ret; - - lbs_deb_enter(LBS_DEB_HOST); - - cmd = cmdnode->cmdbuf; - - spin_lock_irqsave(&priv->driver_lock, flags); - priv->seqnum++; - cmd->seqnum = cpu_to_le16(priv->seqnum); - priv->cur_cmd = cmdnode; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - cmdsize = le16_to_cpu(cmd->size); - command = le16_to_cpu(cmd->command); - - /* These commands take longer */ - if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE) - timeo = 5 * HZ; - - lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", - command, le16_to_cpu(cmd->seqnum), cmdsize); - lbs_deb_hex(LBS_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); - - ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); - - if (ret) { - netdev_info(priv->dev, "DNLD_CMD: hw_host_to_card failed: %d\n", - ret); - /* Reset dnld state machine, report failure */ - priv->dnld_sent = DNLD_RES_RECEIVED; - lbs_complete_command(priv, cmdnode, ret); - } - - if (command == CMD_802_11_DEEP_SLEEP) { - if (priv->is_auto_deep_sleep_enabled) { - priv->wakeup_dev_required = 1; - priv->dnld_sent = 0; - } - priv->is_deep_sleep = 1; - lbs_complete_command(priv, cmdnode, 0); - } else { - /* Setup the timer after transmit command */ - mod_timer(&priv->command_timer, jiffies + timeo); - } - - lbs_deb_leave(LBS_DEB_HOST); -} - -/* - * This function inserts command node to cmdfreeq - * after cleans it. Requires priv->driver_lock held. - */ -static void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - lbs_deb_enter(LBS_DEB_HOST); - - if (!cmdnode) - goto out; - - cmdnode->callback = NULL; - cmdnode->callback_arg = 0; - - memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); - - list_add_tail(&cmdnode->list, &priv->cmdfreeq); - out: - lbs_deb_leave(LBS_DEB_HOST); -} - -static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv, - struct cmd_ctrl_node *ptempcmd) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->driver_lock, flags); - __lbs_cleanup_and_insert_cmd(priv, ptempcmd); - spin_unlock_irqrestore(&priv->driver_lock, flags); -} - -void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, - int result) -{ - /* - * Normally, commands are removed from cmdpendingq before being - * submitted. However, we can arrive here on alternative codepaths - * where the command is still pending. Make sure the command really - * isn't part of a list at this point. - */ - list_del_init(&cmd->list); - - cmd->result = result; - cmd->cmdwaitqwoken = 1; - wake_up(&cmd->cmdwait_q); - - if (!cmd->callback || cmd->callback == lbs_cmd_async_callback) - __lbs_cleanup_and_insert_cmd(priv, cmd); - priv->cur_cmd = NULL; - wake_up(&priv->waitq); -} - -void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, - int result) -{ - unsigned long flags; - spin_lock_irqsave(&priv->driver_lock, flags); - __lbs_complete_command(priv, cmd, result); - spin_unlock_irqrestore(&priv->driver_lock, flags); -} - -int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) -{ - struct cmd_ds_802_11_radio_control cmd; - int ret = -EINVAL; - - lbs_deb_enter(LBS_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.control = 0; - - /* Only v8 and below support setting the preamble */ - if (priv->fwrelease < 0x09000000) { - switch (preamble) { - case RADIO_PREAMBLE_SHORT: - case RADIO_PREAMBLE_AUTO: - case RADIO_PREAMBLE_LONG: - cmd.control = cpu_to_le16(preamble); - break; - default: - goto out; - } - } - - if (radio_on) - cmd.control |= cpu_to_le16(0x1); - else { - cmd.control &= cpu_to_le16(~0x1); - priv->txpower_cur = 0; - } - - lbs_deb_cmd("RADIO_CONTROL: radio %s, preamble %d\n", - radio_on ? "ON" : "OFF", preamble); - - priv->radio_on = radio_on; - - ret = lbs_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -void lbs_set_mac_control(struct lbs_private *priv) -{ - struct cmd_ds_mac_control cmd; - - lbs_deb_enter(LBS_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(priv->mac_control); - cmd.reserved = 0; - - lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd)); - - lbs_deb_leave(LBS_DEB_CMD); -} - -int lbs_set_mac_control_sync(struct lbs_private *priv) -{ - struct cmd_ds_mac_control cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(priv->mac_control); - cmd.reserved = 0; - ret = lbs_cmd_with_response(priv, CMD_MAC_CONTROL, &cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -/** - * lbs_allocate_cmd_buffer - allocates the command buffer and links - * it to command free queue - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: 0 for success or -1 on error - */ -int lbs_allocate_cmd_buffer(struct lbs_private *priv) -{ - int ret = 0; - u32 bufsize; - u32 i; - struct cmd_ctrl_node *cmdarray; - - lbs_deb_enter(LBS_DEB_HOST); - - /* Allocate and initialize the command array */ - bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; - if (!(cmdarray = kzalloc(bufsize, GFP_KERNEL))) { - lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); - ret = -1; - goto done; - } - priv->cmd_array = cmdarray; - - /* Allocate and initialize each command buffer in the command array */ - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); - if (!cmdarray[i].cmdbuf) { - lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); - ret = -1; - goto done; - } - } - - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - init_waitqueue_head(&cmdarray[i].cmdwait_q); - lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]); - } - ret = 0; - -done: - lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); - return ret; -} - -/** - * lbs_free_cmd_buffer - free the command buffer - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: 0 for success - */ -int lbs_free_cmd_buffer(struct lbs_private *priv) -{ - struct cmd_ctrl_node *cmdarray; - unsigned int i; - - lbs_deb_enter(LBS_DEB_HOST); - - /* need to check if cmd array is allocated or not */ - if (priv->cmd_array == NULL) { - lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); - goto done; - } - - cmdarray = priv->cmd_array; - - /* Release shared memory buffers */ - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - if (cmdarray[i].cmdbuf) { - kfree(cmdarray[i].cmdbuf); - cmdarray[i].cmdbuf = NULL; - } - } - - /* Release cmd_ctrl_node */ - if (priv->cmd_array) { - kfree(priv->cmd_array); - priv->cmd_array = NULL; - } - -done: - lbs_deb_leave(LBS_DEB_HOST); - return 0; -} - -/** - * lbs_get_free_cmd_node - gets a free command node if available in - * command free queue - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: A pointer to &cmd_ctrl_node structure on success - * or %NULL on error - */ -static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv) -{ - struct cmd_ctrl_node *tempnode; - unsigned long flags; - - lbs_deb_enter(LBS_DEB_HOST); - - if (!priv) - return NULL; - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (!list_empty(&priv->cmdfreeq)) { - tempnode = list_first_entry(&priv->cmdfreeq, - struct cmd_ctrl_node, list); - list_del_init(&tempnode->list); - } else { - lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); - tempnode = NULL; - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbs_deb_leave(LBS_DEB_HOST); - return tempnode; -} - -/** - * lbs_execute_next_command - execute next command in command - * pending queue. Will put firmware back to PS mode if applicable. - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: 0 on success or -1 on error - */ -int lbs_execute_next_command(struct lbs_private *priv) -{ - struct cmd_ctrl_node *cmdnode = NULL; - struct cmd_header *cmd; - unsigned long flags; - int ret = 0; - - /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the - * only caller to us is lbs_thread() and we get even when a - * data packet is received */ - lbs_deb_enter(LBS_DEB_THREAD); - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->cur_cmd) { - netdev_alert(priv->dev, - "EXEC_NEXT_CMD: already processing command!\n"); - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - if (!list_empty(&priv->cmdpendingq)) { - cmdnode = list_first_entry(&priv->cmdpendingq, - struct cmd_ctrl_node, list); - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - if (cmdnode) { - cmd = cmdnode->cmdbuf; - - if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) { - if ((priv->psstate == PS_STATE_SLEEP) || - (priv->psstate == PS_STATE_PRE_SLEEP)) { - lbs_deb_host( - "EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n", - le16_to_cpu(cmd->command), - priv->psstate); - ret = -1; - goto done; - } - lbs_deb_host("EXEC_NEXT_CMD: OK to send command " - "0x%04x in psstate %d\n", - le16_to_cpu(cmd->command), priv->psstate); - } else if (priv->psstate != PS_STATE_FULL_POWER) { - /* - * 1. Non-PS command: - * Queue it. set needtowakeup to TRUE if current state - * is SLEEP, otherwise call send EXIT_PS. - * 2. PS command but not EXIT_PS: - * Ignore it. - * 3. PS command EXIT_PS: - * Set needtowakeup to TRUE if current state is SLEEP, - * otherwise send this command down to firmware - * immediately. - */ - if (cmd->command != cpu_to_le16(CMD_802_11_PS_MODE)) { - /* Prepare to send Exit PS, - * this non PS command will be sent later */ - if ((priv->psstate == PS_STATE_SLEEP) - || (priv->psstate == PS_STATE_PRE_SLEEP) - ) { - /* w/ new scheme, it will not reach here. - since it is blocked in main_thread. */ - priv->needtowakeup = 1; - } else { - lbs_set_ps_mode(priv, - PS_MODE_ACTION_EXIT_PS, - false); - } - - ret = 0; - goto done; - } else { - /* - * PS command. Ignore it if it is not Exit_PS. - * otherwise send it down immediately. - */ - struct cmd_ds_802_11_ps_mode *psm = (void *)&cmd[1]; - - lbs_deb_host( - "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n", - psm->action); - if (psm->action != - cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { - lbs_deb_host( - "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n"); - lbs_complete_command(priv, cmdnode, 0); - - ret = 0; - goto done; - } - - if ((priv->psstate == PS_STATE_SLEEP) || - (priv->psstate == PS_STATE_PRE_SLEEP)) { - lbs_deb_host( - "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n"); - lbs_complete_command(priv, cmdnode, 0); - priv->needtowakeup = 1; - - ret = 0; - goto done; - } - - lbs_deb_host( - "EXEC_NEXT_CMD: sending EXIT_PS\n"); - } - } - spin_lock_irqsave(&priv->driver_lock, flags); - list_del_init(&cmdnode->list); - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", - le16_to_cpu(cmd->command)); - lbs_submit_command(priv, cmdnode); - } else { - /* - * check if in power save mode, if yes, put the device back - * to PS mode - */ -#ifdef TODO - /* - * This was the old code for libertas+wext. Someone that - * understands this beast should re-code it in a sane way. - * - * I actually don't understand why this is related to WPA - * and to connection status, shouldn't powering should be - * independ of such things? - */ - if ((priv->psmode != LBS802_11POWERMODECAM) && - (priv->psstate == PS_STATE_FULL_POWER) && - ((priv->connect_status == LBS_CONNECTED) || - lbs_mesh_connected(priv))) { - if (priv->secinfo.WPAenabled || - priv->secinfo.WPA2enabled) { - /* check for valid WPA group keys */ - if (priv->wpa_mcast_key.len || - priv->wpa_unicast_key.len) { - lbs_deb_host( - "EXEC_NEXT_CMD: WPA enabled and GTK_SET" - " go back to PS_SLEEP"); - lbs_set_ps_mode(priv, - PS_MODE_ACTION_ENTER_PS, - false); - } - } else { - lbs_deb_host( - "EXEC_NEXT_CMD: cmdpendingq empty, " - "go back to PS_SLEEP"); - lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, - false); - } - } -#endif - } - - ret = 0; -done: - lbs_deb_leave(LBS_DEB_THREAD); - return ret; -} - -static void lbs_send_confirmsleep(struct lbs_private *priv) -{ - unsigned long flags; - int ret; - - lbs_deb_enter(LBS_DEB_HOST); - lbs_deb_hex(LBS_DEB_HOST, "sleep confirm", (u8 *) &confirm_sleep, - sizeof(confirm_sleep)); - - ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &confirm_sleep, - sizeof(confirm_sleep)); - if (ret) { - netdev_alert(priv->dev, "confirm_sleep failed\n"); - goto out; - } - - spin_lock_irqsave(&priv->driver_lock, flags); - - /* We don't get a response on the sleep-confirmation */ - priv->dnld_sent = DNLD_RES_RECEIVED; - - if (priv->is_host_sleep_configured) { - priv->is_host_sleep_activated = 1; - wake_up_interruptible(&priv->host_sleep_q); - } - - /* If nothing to do, go back to sleep (?) */ - if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx]) - priv->psstate = PS_STATE_SLEEP; - - spin_unlock_irqrestore(&priv->driver_lock, flags); - -out: - lbs_deb_leave(LBS_DEB_HOST); -} - -/** - * lbs_ps_confirm_sleep - checks condition and prepares to - * send sleep confirm command to firmware if ok - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: n/a - */ -void lbs_ps_confirm_sleep(struct lbs_private *priv) -{ - unsigned long flags =0; - int allowed = 1; - - lbs_deb_enter(LBS_DEB_HOST); - - spin_lock_irqsave(&priv->driver_lock, flags); - if (priv->dnld_sent) { - allowed = 0; - lbs_deb_host("dnld_sent was set\n"); - } - - /* In-progress command? */ - if (priv->cur_cmd) { - allowed = 0; - lbs_deb_host("cur_cmd was set\n"); - } - - /* Pending events or command responses? */ - if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) { - allowed = 0; - lbs_deb_host("pending events or command responses\n"); - } - spin_unlock_irqrestore(&priv->driver_lock, flags); - - if (allowed) { - lbs_deb_host("sending lbs_ps_confirm_sleep\n"); - lbs_send_confirmsleep(priv); - } else { - lbs_deb_host("sleep confirm has been delayed\n"); - } - - lbs_deb_leave(LBS_DEB_HOST); -} - - -/** - * lbs_set_tpc_cfg - Configures the transmission power control functionality - * - * @priv: A pointer to &struct lbs_private structure - * @enable: Transmission power control enable - * @p0: Power level when link quality is good (dBm). - * @p1: Power level when link quality is fair (dBm). - * @p2: Power level when link quality is poor (dBm). - * @usesnr: Use Signal to Noise Ratio in TPC - * - * returns: 0 on success - */ -int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, - int8_t p2, int usesnr) -{ - struct cmd_ds_802_11_tpc_cfg cmd; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.enable = !!enable; - cmd.usesnr = !!usesnr; - cmd.P0 = p0; - cmd.P1 = p1; - cmd.P2 = p2; - - ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd); - - return ret; -} - -/** - * lbs_set_power_adapt_cfg - Configures the power adaptation settings - * - * @priv: A pointer to &struct lbs_private structure - * @enable: Power adaptation enable - * @p0: Power level for 1, 2, 5.5 and 11 Mbps (dBm). - * @p1: Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm). - * @p2: Power level for 48 and 54 Mbps (dBm). - * - * returns: 0 on Success - */ - -int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, - int8_t p1, int8_t p2) -{ - struct cmd_ds_802_11_pa_cfg cmd; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.enable = !!enable; - cmd.P0 = p0; - cmd.P1 = p1; - cmd.P2 = p2; - - ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd); - - return ret; -} - - -struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, - uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), - unsigned long callback_arg) -{ - struct cmd_ctrl_node *cmdnode; - - lbs_deb_enter(LBS_DEB_HOST); - - if (priv->surpriseremoved) { - lbs_deb_host("PREP_CMD: card removed\n"); - cmdnode = ERR_PTR(-ENOENT); - goto done; - } - - /* No commands are allowed in Deep Sleep until we toggle the GPIO - * to wake up the card and it has signaled that it's ready. - */ - if (!priv->is_auto_deep_sleep_enabled) { - if (priv->is_deep_sleep) { - lbs_deb_cmd("command not allowed in deep sleep\n"); - cmdnode = ERR_PTR(-EBUSY); - goto done; - } - } - - cmdnode = lbs_get_free_cmd_node(priv); - if (cmdnode == NULL) { - lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); - - /* Wake up main thread to execute next command */ - wake_up(&priv->waitq); - cmdnode = ERR_PTR(-ENOBUFS); - goto done; - } - - cmdnode->callback = callback; - cmdnode->callback_arg = callback_arg; - - /* Copy the incoming command to the buffer */ - memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); - - /* Set command, clean result, move to buffer */ - cmdnode->cmdbuf->command = cpu_to_le16(command); - cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); - cmdnode->cmdbuf->result = 0; - - lbs_deb_host("PREP_CMD: command 0x%04x\n", command); - - cmdnode->cmdwaitqwoken = 0; - lbs_queue_cmd(priv, cmdnode); - wake_up(&priv->waitq); - - done: - lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode); - return cmdnode; -} - -void lbs_cmd_async(struct lbs_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size) -{ - lbs_deb_enter(LBS_DEB_CMD); - __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, - lbs_cmd_async_callback, 0); - lbs_deb_leave(LBS_DEB_CMD); -} - -int __lbs_cmd(struct lbs_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), - unsigned long callback_arg) -{ - struct cmd_ctrl_node *cmdnode; - unsigned long flags; - int ret = 0; - - lbs_deb_enter(LBS_DEB_HOST); - - cmdnode = __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, - callback, callback_arg); - if (IS_ERR(cmdnode)) { - ret = PTR_ERR(cmdnode); - goto done; - } - - might_sleep(); - - /* - * Be careful with signals here. A signal may be received as the system - * goes into suspend or resume. We do not want this to interrupt the - * command, so we perform an uninterruptible sleep. - */ - wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); - - spin_lock_irqsave(&priv->driver_lock, flags); - ret = cmdnode->result; - if (ret) - netdev_info(priv->dev, "PREP_CMD: command 0x%04x failed: %d\n", - command, ret); - - __lbs_cleanup_and_insert_cmd(priv, cmdnode); - spin_unlock_irqrestore(&priv->driver_lock, flags); - -done: - lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(__lbs_cmd); diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h deleted file mode 100644 index 0c5444b02c64..000000000000 --- a/drivers/net/wireless/libertas/cmd.h +++ /dev/null @@ -1,141 +0,0 @@ -/* Copyright (C) 2007, Red Hat, Inc. */ - -#ifndef _LBS_CMD_H_ -#define _LBS_CMD_H_ - -#include - -#include "host.h" -#include "dev.h" - - -/* Command & response transfer between host and card */ - -struct cmd_ctrl_node { - struct list_head list; - int result; - /* command response */ - int (*callback)(struct lbs_private *, - unsigned long, - struct cmd_header *); - unsigned long callback_arg; - /* command data */ - struct cmd_header *cmdbuf; - /* wait queue */ - u16 cmdwaitqwoken; - wait_queue_head_t cmdwait_q; -}; - - -/* lbs_cmd() infers the size of the buffer to copy data back into, from - the size of the target of the pointer. Since the command to be sent - may often be smaller, that size is set in cmd->size by the caller.*/ -#define lbs_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \ - uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \ - (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \ - __lbs_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \ -}) - -#define lbs_cmd_with_response(priv, cmdnr, cmd) \ - lbs_cmd(priv, cmdnr, cmd, lbs_cmd_copyback, (unsigned long) (cmd)) - -void lbs_cmd_async(struct lbs_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size); - -int __lbs_cmd(struct lbs_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), - unsigned long callback_arg); - -struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, - uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), - unsigned long callback_arg); - -int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, - struct cmd_header *resp); - -int lbs_allocate_cmd_buffer(struct lbs_private *priv); -int lbs_free_cmd_buffer(struct lbs_private *priv); - -int lbs_execute_next_command(struct lbs_private *priv); -void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, - int result); -void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, - int result); -int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len); - - -/* From cmdresp.c */ - -void lbs_mac_event_disconnected(struct lbs_private *priv, - bool locally_generated); - - - -/* Events */ - -int lbs_process_event(struct lbs_private *priv, u32 event); - - -/* Actual commands */ - -int lbs_update_hw_spec(struct lbs_private *priv); - -int lbs_set_channel(struct lbs_private *priv, u8 channel); - -int lbs_update_channel(struct lbs_private *priv); - -int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, - struct wol_config *p_wol_config); - -int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, - struct sleep_params *sp); - -void lbs_ps_confirm_sleep(struct lbs_private *priv); - -int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on); - -void lbs_set_mac_control(struct lbs_private *priv); -int lbs_set_mac_control_sync(struct lbs_private *priv); - -int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, - s16 *maxlevel); - -int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val); - -int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val); - - -/* Commands only used in wext.c, assoc. and scan.c */ - -int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, - int8_t p1, int8_t p2); - -int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, - int8_t p2, int usesnr); - -int lbs_set_data_rate(struct lbs_private *priv, u8 rate); - -int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv, - uint16_t cmd_action); - -int lbs_set_tx_power(struct lbs_private *priv, s16 dbm); - -int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep); - -int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep); - -int lbs_set_monitor_mode(struct lbs_private *priv, int enable); - -int lbs_get_rssi(struct lbs_private *priv, s8 *snr, s8 *nf); - -int lbs_set_11d_domain_info(struct lbs_private *priv); - -int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value); - -int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value); - -int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block); - -#endif /* _LBS_CMD_H */ diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c deleted file mode 100644 index e5442e8956f7..000000000000 --- a/drivers/net/wireless/libertas/cmdresp.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * This file contains the handling of command - * responses as well as events generated by firmware. - */ - -#include -#include -#include -#include -#include -#include - -#include "cfg.h" -#include "cmd.h" - -/** - * lbs_mac_event_disconnected - handles disconnect event. It - * reports disconnect to upper layer, clean tx/rx packets, - * reset link state etc. - * - * @priv: A pointer to struct lbs_private structure - * @locally_generated: indicates disconnect was requested locally - * (usually by userspace) - * - * returns: n/a - */ -void lbs_mac_event_disconnected(struct lbs_private *priv, - bool locally_generated) -{ - if (priv->connect_status != LBS_CONNECTED) - return; - - lbs_deb_enter(LBS_DEB_ASSOC); - - /* - * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. - * It causes problem in the Supplicant - */ - msleep_interruptible(1000); - - if (priv->wdev->iftype == NL80211_IFTYPE_STATION) - lbs_send_disconnect_notification(priv, locally_generated); - - /* report disconnect to upper layer */ - netif_stop_queue(priv->dev); - netif_carrier_off(priv->dev); - - /* Free Tx and Rx packets */ - kfree_skb(priv->currenttxskb); - priv->currenttxskb = NULL; - priv->tx_pending_len = 0; - - priv->connect_status = LBS_DISCONNECTED; - - if (priv->psstate != PS_STATE_FULL_POWER) { - /* make firmware to exit PS mode */ - lbs_deb_cmd("disconnected, so exit PS mode\n"); - lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); - } - lbs_deb_leave(LBS_DEB_ASSOC); -} - -int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) -{ - uint16_t respcmd, curcmd; - struct cmd_header *resp; - int ret = 0; - unsigned long flags; - uint16_t result; - - lbs_deb_enter(LBS_DEB_HOST); - - mutex_lock(&priv->lock); - spin_lock_irqsave(&priv->driver_lock, flags); - - if (!priv->cur_cmd) { - lbs_deb_host("CMD_RESP: cur_cmd is NULL\n"); - ret = -1; - spin_unlock_irqrestore(&priv->driver_lock, flags); - goto done; - } - - resp = (void *)data; - curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); - respcmd = le16_to_cpu(resp->command); - result = le16_to_cpu(resp->result); - - lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n", - respcmd, le16_to_cpu(resp->seqnum), len); - lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len); - - if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { - netdev_info(priv->dev, - "Received CMD_RESP with invalid sequence %d (expected %d)\n", - le16_to_cpu(resp->seqnum), - le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum)); - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - if (respcmd != CMD_RET(curcmd) && - respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) { - netdev_info(priv->dev, "Invalid CMD_RESP %x to command %x!\n", - respcmd, curcmd); - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - if (resp->result == cpu_to_le16(0x0004)) { - /* 0x0004 means -EAGAIN. Drop the response, let it time out - and be resubmitted */ - netdev_info(priv->dev, - "Firmware returns DEFER to command %x. Will let it time out...\n", - le16_to_cpu(resp->command)); - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - /* Now we got response from FW, cancel the command timer */ - del_timer(&priv->command_timer); - priv->cmd_timed_out = 0; - - if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) { - struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1]; - u16 action = le16_to_cpu(psmode->action); - - lbs_deb_host( - "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n", - result, action); - - if (result) { - lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n", - result); - /* - * We should not re-try enter-ps command in - * ad-hoc mode. It takes place in - * lbs_execute_next_command(). - */ - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR && - action == PS_MODE_ACTION_ENTER_PS) - priv->psmode = LBS802_11POWERMODECAM; - } else if (action == PS_MODE_ACTION_ENTER_PS) { - priv->needtowakeup = 0; - priv->psstate = PS_STATE_AWAKE; - - lbs_deb_host("CMD_RESP: ENTER_PS command response\n"); - if (priv->connect_status != LBS_CONNECTED) { - /* - * When Deauth Event received before Enter_PS command - * response, We need to wake up the firmware. - */ - lbs_deb_host( - "disconnected, invoking lbs_ps_wakeup\n"); - - spin_unlock_irqrestore(&priv->driver_lock, flags); - mutex_unlock(&priv->lock); - lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, - false); - mutex_lock(&priv->lock); - spin_lock_irqsave(&priv->driver_lock, flags); - } - } else if (action == PS_MODE_ACTION_EXIT_PS) { - priv->needtowakeup = 0; - priv->psstate = PS_STATE_FULL_POWER; - lbs_deb_host("CMD_RESP: EXIT_PS command response\n"); - } else { - lbs_deb_host("CMD_RESP: PS action 0x%X\n", action); - } - - __lbs_complete_command(priv, priv->cur_cmd, result); - spin_unlock_irqrestore(&priv->driver_lock, flags); - - ret = 0; - goto done; - } - - /* If the command is not successful, cleanup and return failure */ - if ((result != 0 || !(respcmd & 0x8000))) { - lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n", - result, respcmd); - /* - * Handling errors here - */ - switch (respcmd) { - case CMD_RET(CMD_GET_HW_SPEC): - case CMD_RET(CMD_802_11_RESET): - lbs_deb_host("CMD_RESP: reset failed\n"); - break; - - } - __lbs_complete_command(priv, priv->cur_cmd, result); - spin_unlock_irqrestore(&priv->driver_lock, flags); - - ret = -1; - goto done; - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - if (priv->cur_cmd && priv->cur_cmd->callback) { - ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, - resp); - } - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->cur_cmd) { - /* Clean up and Put current command back to cmdfreeq */ - __lbs_complete_command(priv, priv->cur_cmd, result); - } - spin_unlock_irqrestore(&priv->driver_lock, flags); - -done: - mutex_unlock(&priv->lock); - lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); - return ret; -} - -int lbs_process_event(struct lbs_private *priv, u32 event) -{ - int ret = 0; - struct cmd_header cmd; - - lbs_deb_enter(LBS_DEB_CMD); - - switch (event) { - case MACREG_INT_CODE_LINK_SENSED: - lbs_deb_cmd("EVENT: link sensed\n"); - break; - - case MACREG_INT_CODE_DEAUTHENTICATED: - lbs_deb_cmd("EVENT: deauthenticated\n"); - lbs_mac_event_disconnected(priv, false); - break; - - case MACREG_INT_CODE_DISASSOCIATED: - lbs_deb_cmd("EVENT: disassociated\n"); - lbs_mac_event_disconnected(priv, false); - break; - - case MACREG_INT_CODE_LINK_LOST_NO_SCAN: - lbs_deb_cmd("EVENT: link lost\n"); - lbs_mac_event_disconnected(priv, true); - break; - - case MACREG_INT_CODE_PS_SLEEP: - lbs_deb_cmd("EVENT: ps sleep\n"); - - /* handle unexpected PS SLEEP event */ - if (priv->psstate == PS_STATE_FULL_POWER) { - lbs_deb_cmd( - "EVENT: in FULL POWER mode, ignoring PS_SLEEP\n"); - break; - } - priv->psstate = PS_STATE_PRE_SLEEP; - - lbs_ps_confirm_sleep(priv); - - break; - - case MACREG_INT_CODE_HOST_AWAKE: - lbs_deb_cmd("EVENT: host awake\n"); - if (priv->reset_deep_sleep_wakeup) - priv->reset_deep_sleep_wakeup(priv); - priv->is_deep_sleep = 0; - lbs_cmd_async(priv, CMD_802_11_WAKEUP_CONFIRM, &cmd, - sizeof(cmd)); - priv->is_host_sleep_activated = 0; - wake_up_interruptible(&priv->host_sleep_q); - break; - - case MACREG_INT_CODE_DEEP_SLEEP_AWAKE: - if (priv->reset_deep_sleep_wakeup) - priv->reset_deep_sleep_wakeup(priv); - lbs_deb_cmd("EVENT: ds awake\n"); - priv->is_deep_sleep = 0; - priv->wakeup_dev_required = 0; - wake_up_interruptible(&priv->ds_awake_q); - break; - - case MACREG_INT_CODE_PS_AWAKE: - lbs_deb_cmd("EVENT: ps awake\n"); - /* handle unexpected PS AWAKE event */ - if (priv->psstate == PS_STATE_FULL_POWER) { - lbs_deb_cmd( - "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); - break; - } - - priv->psstate = PS_STATE_AWAKE; - - if (priv->needtowakeup) { - /* - * wait for the command processing to finish - * before resuming sending - * priv->needtowakeup will be set to FALSE - * in lbs_ps_wakeup() - */ - lbs_deb_cmd("waking up ...\n"); - lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); - } - break; - - case MACREG_INT_CODE_MIC_ERR_UNICAST: - lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n"); - lbs_send_mic_failureevent(priv, event); - break; - - case MACREG_INT_CODE_MIC_ERR_MULTICAST: - lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n"); - lbs_send_mic_failureevent(priv, event); - break; - - case MACREG_INT_CODE_MIB_CHANGED: - lbs_deb_cmd("EVENT: MIB CHANGED\n"); - break; - case MACREG_INT_CODE_INIT_DONE: - lbs_deb_cmd("EVENT: INIT DONE\n"); - break; - case MACREG_INT_CODE_ADHOC_BCN_LOST: - lbs_deb_cmd("EVENT: ADHOC beacon lost\n"); - break; - case MACREG_INT_CODE_RSSI_LOW: - netdev_alert(priv->dev, "EVENT: rssi low\n"); - break; - case MACREG_INT_CODE_SNR_LOW: - netdev_alert(priv->dev, "EVENT: snr low\n"); - break; - case MACREG_INT_CODE_MAX_FAIL: - netdev_alert(priv->dev, "EVENT: max fail\n"); - break; - case MACREG_INT_CODE_RSSI_HIGH: - netdev_alert(priv->dev, "EVENT: rssi high\n"); - break; - case MACREG_INT_CODE_SNR_HIGH: - netdev_alert(priv->dev, "EVENT: snr high\n"); - break; - - case MACREG_INT_CODE_MESH_AUTO_STARTED: - /* Ignore spurious autostart events */ - netdev_info(priv->dev, "EVENT: MESH_AUTO_STARTED (ignoring)\n"); - break; - - default: - netdev_alert(priv->dev, "EVENT: unknown event id %d\n", event); - break; - } - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c deleted file mode 100644 index 26cbf1dcc662..000000000000 --- a/drivers/net/wireless/libertas/debugfs.c +++ /dev/null @@ -1,989 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "decl.h" -#include "cmd.h" -#include "debugfs.h" - -static struct dentry *lbs_dir; -static char *szStates[] = { - "Connected", - "Disconnected" -}; - -#ifdef PROC_DEBUG -static void lbs_debug_init(struct lbs_private *priv); -#endif - -static ssize_t write_file_dummy(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -static const size_t len = PAGE_SIZE; - -static ssize_t lbs_dev_info(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - size_t pos = 0; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - ssize_t res; - if (!buf) - return -ENOMEM; - - pos += snprintf(buf+pos, len-pos, "state = %s\n", - szStates[priv->connect_status]); - pos += snprintf(buf+pos, len-pos, "region_code = %02x\n", - (u32) priv->regioncode); - - res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - - free_page(addr); - return res; -} - -static ssize_t lbs_sleepparams_write(struct file *file, - const char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t buf_size, ret; - struct sleep_params sp; - int p1, p2, p3, p4, p5, p6; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, user_buf, buf_size)) { - ret = -EFAULT; - goto out_unlock; - } - ret = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6); - if (ret != 6) { - ret = -EINVAL; - goto out_unlock; - } - sp.sp_error = p1; - sp.sp_offset = p2; - sp.sp_stabletime = p3; - sp.sp_calcontrol = p4; - sp.sp_extsleepclk = p5; - sp.sp_reserved = p6; - - ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_SET, &sp); - if (!ret) - ret = count; - else if (ret > 0) - ret = -EINVAL; - -out_unlock: - free_page(addr); - return ret; -} - -static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t ret; - size_t pos = 0; - struct sleep_params sp; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp); - if (ret) - goto out_unlock; - - pos += snprintf(buf, len, "%d %d %d %d %d %d\n", sp.sp_error, - sp.sp_offset, sp.sp_stabletime, - sp.sp_calcontrol, sp.sp_extsleepclk, - sp.sp_reserved); - - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - -out_unlock: - free_page(addr); - return ret; -} - -static ssize_t lbs_host_sleep_write(struct file *file, - const char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t buf_size, ret; - int host_sleep; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, user_buf, buf_size)) { - ret = -EFAULT; - goto out_unlock; - } - ret = sscanf(buf, "%d", &host_sleep); - if (ret != 1) { - ret = -EINVAL; - goto out_unlock; - } - - if (host_sleep == 0) - ret = lbs_set_host_sleep(priv, 0); - else if (host_sleep == 1) { - if (priv->wol_criteria == EHS_REMOVE_WAKEUP) { - netdev_info(priv->dev, - "wake parameters not configured\n"); - ret = -EINVAL; - goto out_unlock; - } - ret = lbs_set_host_sleep(priv, 1); - } else { - netdev_err(priv->dev, "invalid option\n"); - ret = -EINVAL; - } - - if (!ret) - ret = count; - -out_unlock: - free_page(addr); - return ret; -} - -static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t ret; - size_t pos = 0; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated); - - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - - free_page(addr); - return ret; -} - -/* - * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might - * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the - * firmware. Here's an example: - * 04 01 02 00 00 00 05 01 02 00 00 00 06 01 02 00 - * 00 00 07 01 02 00 3c 00 00 00 00 00 00 00 03 03 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * - * The 04 01 is the TLV type (here TLV_TYPE_RSSI_LOW), 02 00 is the length, - * 00 00 are the data bytes of this TLV. For this TLV, their meaning is - * defined in mrvlietypes_thresholds - * - * This function searches in this TLV data chunk for a given TLV type - * and returns a pointer to the first data byte of the TLV, or to NULL - * if the TLV hasn't been found. - */ -static void *lbs_tlv_find(uint16_t tlv_type, const uint8_t *tlv, uint16_t size) -{ - struct mrvl_ie_header *tlv_h; - uint16_t length; - ssize_t pos = 0; - - while (pos < size) { - tlv_h = (struct mrvl_ie_header *) tlv; - if (!tlv_h->len) - return NULL; - if (tlv_h->type == cpu_to_le16(tlv_type)) - return tlv_h; - length = le16_to_cpu(tlv_h->len) + sizeof(*tlv_h); - pos += length; - tlv += length; - } - return NULL; -} - - -static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask, - struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct cmd_ds_802_11_subscribe_event *subscribed; - struct mrvl_ie_thresholds *got; - struct lbs_private *priv = file->private_data; - ssize_t ret = 0; - size_t pos = 0; - char *buf; - u8 value; - u8 freq; - int events = 0; - - buf = (char *)get_zeroed_page(GFP_KERNEL); - if (!buf) - return -ENOMEM; - - subscribed = kzalloc(sizeof(*subscribed), GFP_KERNEL); - if (!subscribed) { - ret = -ENOMEM; - goto out_page; - } - - subscribed->hdr.size = cpu_to_le16(sizeof(*subscribed)); - subscribed->action = cpu_to_le16(CMD_ACT_GET); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, subscribed); - if (ret) - goto out_cmd; - - got = lbs_tlv_find(tlv_type, subscribed->tlv, sizeof(subscribed->tlv)); - if (got) { - value = got->value; - freq = got->freq; - events = le16_to_cpu(subscribed->events); - - pos += snprintf(buf, len, "%d %d %d\n", value, freq, - !!(events & event_mask)); - } - - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - - out_cmd: - kfree(subscribed); - - out_page: - free_page((unsigned long)buf); - return ret; -} - - -static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask, - struct file *file, - const char __user *userbuf, size_t count, - loff_t *ppos) -{ - struct cmd_ds_802_11_subscribe_event *events; - struct mrvl_ie_thresholds *tlv; - struct lbs_private *priv = file->private_data; - ssize_t buf_size; - int value, freq, new_mask; - uint16_t curr_mask; - char *buf; - int ret; - - buf = (char *)get_zeroed_page(GFP_KERNEL); - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - ret = -EFAULT; - goto out_page; - } - ret = sscanf(buf, "%d %d %d", &value, &freq, &new_mask); - if (ret != 3) { - ret = -EINVAL; - goto out_page; - } - events = kzalloc(sizeof(*events), GFP_KERNEL); - if (!events) { - ret = -ENOMEM; - goto out_page; - } - - events->hdr.size = cpu_to_le16(sizeof(*events)); - events->action = cpu_to_le16(CMD_ACT_GET); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events); - if (ret) - goto out_events; - - curr_mask = le16_to_cpu(events->events); - - if (new_mask) - new_mask = curr_mask | event_mask; - else - new_mask = curr_mask & ~event_mask; - - /* Now everything is set and we can send stuff down to the firmware */ - - tlv = (void *)events->tlv; - - events->action = cpu_to_le16(CMD_ACT_SET); - events->events = cpu_to_le16(new_mask); - tlv->header.type = cpu_to_le16(tlv_type); - tlv->header.len = cpu_to_le16(sizeof(*tlv) - sizeof(tlv->header)); - tlv->value = value; - if (tlv_type != TLV_TYPE_BCNMISS) - tlv->freq = freq; - - /* The command header, the action, the event mask, and one TLV */ - events->hdr.size = cpu_to_le16(sizeof(events->hdr) + 4 + sizeof(*tlv)); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events); - - if (!ret) - ret = count; - out_events: - kfree(events); - out_page: - free_page((unsigned long)buf); - return ret; -} - - -static ssize_t lbs_lowrssi_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_lowrssi_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_lowsnr_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_lowsnr_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_failcount_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_failcount_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_highrssi_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_highrssi_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_highsnr_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_highsnr_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, - file, userbuf, count, ppos); -} - -static ssize_t lbs_bcnmiss_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_bcnmiss_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t pos = 0; - int ret; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - u32 val = 0; - - if (!buf) - return -ENOMEM; - - ret = lbs_get_reg(priv, CMD_MAC_REG_ACCESS, priv->mac_offset, &val); - mdelay(10); - if (!ret) { - pos = snprintf(buf, len, "MAC[0x%x] = 0x%08x\n", - priv->mac_offset, val); - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - } - free_page(addr); - return ret; -} - -static ssize_t lbs_rdmac_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - priv->mac_offset = simple_strtoul(buf, NULL, 16); - res = count; -out_unlock: - free_page(addr); - return res; -} - -static ssize_t lbs_wrmac_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - u32 offset, value; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - res = sscanf(buf, "%x %x", &offset, &value); - if (res != 2) { - res = -EFAULT; - goto out_unlock; - } - - res = lbs_set_reg(priv, CMD_MAC_REG_ACCESS, offset, value); - mdelay(10); - - if (!res) - res = count; -out_unlock: - free_page(addr); - return res; -} - -static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t pos = 0; - int ret; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - u32 val; - - if (!buf) - return -ENOMEM; - - ret = lbs_get_reg(priv, CMD_BBP_REG_ACCESS, priv->bbp_offset, &val); - mdelay(10); - if (!ret) { - pos = snprintf(buf, len, "BBP[0x%x] = 0x%08x\n", - priv->bbp_offset, val); - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - } - free_page(addr); - - return ret; -} - -static ssize_t lbs_rdbbp_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - priv->bbp_offset = simple_strtoul(buf, NULL, 16); - res = count; -out_unlock: - free_page(addr); - return res; -} - -static ssize_t lbs_wrbbp_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - u32 offset, value; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - res = sscanf(buf, "%x %x", &offset, &value); - if (res != 2) { - res = -EFAULT; - goto out_unlock; - } - - res = lbs_set_reg(priv, CMD_BBP_REG_ACCESS, offset, value); - mdelay(10); - - if (!res) - res = count; -out_unlock: - free_page(addr); - return res; -} - -static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t pos = 0; - int ret; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - u32 val; - - if (!buf) - return -ENOMEM; - - ret = lbs_get_reg(priv, CMD_RF_REG_ACCESS, priv->rf_offset, &val); - mdelay(10); - if (!ret) { - pos = snprintf(buf, len, "RF[0x%x] = 0x%08x\n", - priv->rf_offset, val); - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - } - free_page(addr); - - return ret; -} - -static ssize_t lbs_rdrf_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - priv->rf_offset = simple_strtoul(buf, NULL, 16); - res = count; -out_unlock: - free_page(addr); - return res; -} - -static ssize_t lbs_wrrf_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - u32 offset, value; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - res = sscanf(buf, "%x %x", &offset, &value); - if (res != 2) { - res = -EFAULT; - goto out_unlock; - } - - res = lbs_set_reg(priv, CMD_RF_REG_ACCESS, offset, value); - mdelay(10); - - if (!res) - res = count; -out_unlock: - free_page(addr); - return res; -} - -#define FOPS(fread, fwrite) { \ - .owner = THIS_MODULE, \ - .open = simple_open, \ - .read = (fread), \ - .write = (fwrite), \ - .llseek = generic_file_llseek, \ -} - -struct lbs_debugfs_files { - const char *name; - umode_t perm; - struct file_operations fops; -}; - -static const struct lbs_debugfs_files debugfs_files[] = { - { "info", 0444, FOPS(lbs_dev_info, write_file_dummy), }, - { "sleepparams", 0644, FOPS(lbs_sleepparams_read, - lbs_sleepparams_write), }, - { "hostsleep", 0644, FOPS(lbs_host_sleep_read, - lbs_host_sleep_write), }, -}; - -static const struct lbs_debugfs_files debugfs_events_files[] = { - {"low_rssi", 0644, FOPS(lbs_lowrssi_read, - lbs_lowrssi_write), }, - {"low_snr", 0644, FOPS(lbs_lowsnr_read, - lbs_lowsnr_write), }, - {"failure_count", 0644, FOPS(lbs_failcount_read, - lbs_failcount_write), }, - {"beacon_missed", 0644, FOPS(lbs_bcnmiss_read, - lbs_bcnmiss_write), }, - {"high_rssi", 0644, FOPS(lbs_highrssi_read, - lbs_highrssi_write), }, - {"high_snr", 0644, FOPS(lbs_highsnr_read, - lbs_highsnr_write), }, -}; - -static const struct lbs_debugfs_files debugfs_regs_files[] = { - {"rdmac", 0644, FOPS(lbs_rdmac_read, lbs_rdmac_write), }, - {"wrmac", 0600, FOPS(NULL, lbs_wrmac_write), }, - {"rdbbp", 0644, FOPS(lbs_rdbbp_read, lbs_rdbbp_write), }, - {"wrbbp", 0600, FOPS(NULL, lbs_wrbbp_write), }, - {"rdrf", 0644, FOPS(lbs_rdrf_read, lbs_rdrf_write), }, - {"wrrf", 0600, FOPS(NULL, lbs_wrrf_write), }, -}; - -void lbs_debugfs_init(void) -{ - if (!lbs_dir) - lbs_dir = debugfs_create_dir("lbs_wireless", NULL); -} - -void lbs_debugfs_remove(void) -{ - debugfs_remove(lbs_dir); -} - -void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev) -{ - int i; - const struct lbs_debugfs_files *files; - if (!lbs_dir) - goto exit; - - priv->debugfs_dir = debugfs_create_dir(dev->name, lbs_dir); - if (!priv->debugfs_dir) - goto exit; - - for (i=0; idebugfs_files[i] = debugfs_create_file(files->name, - files->perm, - priv->debugfs_dir, - priv, - &files->fops); - } - - priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir); - if (!priv->events_dir) - goto exit; - - for (i=0; idebugfs_events_files[i] = debugfs_create_file(files->name, - files->perm, - priv->events_dir, - priv, - &files->fops); - } - - priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir); - if (!priv->regs_dir) - goto exit; - - for (i=0; idebugfs_regs_files[i] = debugfs_create_file(files->name, - files->perm, - priv->regs_dir, - priv, - &files->fops); - } - -#ifdef PROC_DEBUG - lbs_debug_init(priv); -#endif -exit: - return; -} - -void lbs_debugfs_remove_one(struct lbs_private *priv) -{ - int i; - - for(i=0; idebugfs_regs_files[i]); - - debugfs_remove(priv->regs_dir); - - for(i=0; idebugfs_events_files[i]); - - debugfs_remove(priv->events_dir); -#ifdef PROC_DEBUG - debugfs_remove(priv->debugfs_debug); -#endif - for(i=0; idebugfs_files[i]); - debugfs_remove(priv->debugfs_dir); -} - - - -/* debug entry */ - -#ifdef PROC_DEBUG - -#define item_size(n) (FIELD_SIZEOF(struct lbs_private, n)) -#define item_addr(n) (offsetof(struct lbs_private, n)) - - -struct debug_data { - char name[32]; - u32 size; - size_t addr; -}; - -/* To debug any member of struct lbs_private, simply add one line here. - */ -static struct debug_data items[] = { - {"psmode", item_size(psmode), item_addr(psmode)}, - {"psstate", item_size(psstate), item_addr(psstate)}, -}; - -static int num_of_items = ARRAY_SIZE(items); - -/** - * lbs_debugfs_read - proc read function - * - * @file: file to read - * @userbuf: pointer to buffer - * @count: number of bytes to read - * @ppos: read data starting position - * - * returns: amount of data read or negative error code - */ -static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - int val = 0; - size_t pos = 0; - ssize_t res; - char *p; - int i; - struct debug_data *d; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - p = buf; - - d = file->private_data; - - for (i = 0; i < num_of_items; i++) { - if (d[i].size == 1) - val = *((u8 *) d[i].addr); - else if (d[i].size == 2) - val = *((u16 *) d[i].addr); - else if (d[i].size == 4) - val = *((u32 *) d[i].addr); - else if (d[i].size == 8) - val = *((u64 *) d[i].addr); - - pos += sprintf(p + pos, "%s=%d\n", d[i].name, val); - } - - res = simple_read_from_buffer(userbuf, count, ppos, p, pos); - - free_page(addr); - return res; -} - -/** - * lbs_debugfs_write - proc write function - * - * @f: file pointer - * @buf: pointer to data buffer - * @cnt: data number to write - * @ppos: file position - * - * returns: amount of data written - */ -static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, - size_t cnt, loff_t *ppos) -{ - int r, i; - char *pdata; - char *p; - char *p0; - char *p1; - char *p2; - struct debug_data *d = f->private_data; - - if (cnt == 0) - return 0; - - pdata = kmalloc(cnt + 1, GFP_KERNEL); - if (pdata == NULL) - return 0; - - if (copy_from_user(pdata, buf, cnt)) { - lbs_deb_debugfs("Copy from user failed\n"); - kfree(pdata); - return 0; - } - pdata[cnt] = '\0'; - - p0 = pdata; - for (i = 0; i < num_of_items; i++) { - do { - p = strstr(p0, d[i].name); - if (p == NULL) - break; - p1 = strchr(p, '\n'); - if (p1 == NULL) - break; - p0 = p1++; - p2 = strchr(p, '='); - if (!p2) - break; - p2++; - r = simple_strtoul(p2, NULL, 0); - if (d[i].size == 1) - *((u8 *) d[i].addr) = (u8) r; - else if (d[i].size == 2) - *((u16 *) d[i].addr) = (u16) r; - else if (d[i].size == 4) - *((u32 *) d[i].addr) = (u32) r; - else if (d[i].size == 8) - *((u64 *) d[i].addr) = (u64) r; - break; - } while (1); - } - kfree(pdata); - - return (ssize_t)cnt; -} - -static const struct file_operations lbs_debug_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .write = lbs_debugfs_write, - .read = lbs_debugfs_read, - .llseek = default_llseek, -}; - -/** - * lbs_debug_init - create debug proc file - * - * @priv: pointer to &struct lbs_private - * - * returns: N/A - */ -static void lbs_debug_init(struct lbs_private *priv) -{ - int i; - - if (!priv->debugfs_dir) - return; - - for (i = 0; i < num_of_items; i++) - items[i].addr += (size_t) priv; - - priv->debugfs_debug = debugfs_create_file("debug", 0644, - priv->debugfs_dir, &items[0], - &lbs_debug_fops); -} -#endif diff --git a/drivers/net/wireless/libertas/debugfs.h b/drivers/net/wireless/libertas/debugfs.h deleted file mode 100644 index f2b9c7ffe0fd..000000000000 --- a/drivers/net/wireless/libertas/debugfs.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _LBS_DEBUGFS_H_ -#define _LBS_DEBUGFS_H_ - -void lbs_debugfs_init(void); -void lbs_debugfs_remove(void); - -void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev); -void lbs_debugfs_remove_one(struct lbs_private *priv); - -#endif diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h deleted file mode 100644 index 84a3aa7ac570..000000000000 --- a/drivers/net/wireless/libertas/decl.h +++ /dev/null @@ -1,82 +0,0 @@ - -/* - * This file contains declaration referring to - * functions defined in other source files - */ - -#ifndef _LBS_DECL_H_ -#define _LBS_DECL_H_ - -#include -#include -#include - -/* Should be terminated by a NULL entry */ -struct lbs_fw_table { - int model; - const char *helper; - const char *fwname; -}; - -struct lbs_private; -typedef void (*lbs_fw_cb)(struct lbs_private *priv, int ret, - const struct firmware *helper, const struct firmware *mainfw); - -struct lbs_private; -struct sk_buff; -struct net_device; -struct cmd_ds_command; - - -/* ethtool.c */ -extern const struct ethtool_ops lbs_ethtool_ops; - - -/* tx.c */ -void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count); -netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev); - -/* rx.c */ -int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *); - - -/* main.c */ -struct lbs_private *lbs_add_card(void *card, struct device *dmdev); -void lbs_remove_card(struct lbs_private *priv); -int lbs_start_card(struct lbs_private *priv); -void lbs_stop_card(struct lbs_private *priv); -void lbs_host_to_card_done(struct lbs_private *priv); - -int lbs_start_iface(struct lbs_private *priv); -int lbs_stop_iface(struct lbs_private *priv); -int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type); - -int lbs_rtap_supported(struct lbs_private *priv); - -int lbs_set_mac_address(struct net_device *dev, void *addr); -void lbs_set_multicast_list(struct net_device *dev); -void lbs_update_mcast(struct lbs_private *priv); - -int lbs_suspend(struct lbs_private *priv); -int lbs_resume(struct lbs_private *priv); - -void lbs_queue_event(struct lbs_private *priv, u32 event); -void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx); - -int lbs_enter_auto_deep_sleep(struct lbs_private *priv); -int lbs_exit_auto_deep_sleep(struct lbs_private *priv); - -u32 lbs_fw_index_to_data_rate(u8 index); -u8 lbs_data_rate_to_fw_index(u32 rate); - -int lbs_get_firmware(struct device *dev, u32 card_model, - const struct lbs_fw_table *fw_table, - const struct firmware **helper, - const struct firmware **mainfw); -int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, - u32 card_model, const struct lbs_fw_table *fw_table, - lbs_fw_cb callback); -void lbs_wait_for_firmware_load(struct lbs_private *priv); - -#endif diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h deleted file mode 100644 index 407784aca627..000000000000 --- a/drivers/net/wireless/libertas/defs.h +++ /dev/null @@ -1,394 +0,0 @@ -/* - * This header file contains global constant/enum definitions, - * global variable declaration. - */ -#ifndef _LBS_DEFS_H_ -#define _LBS_DEFS_H_ - -#include - -#ifdef CONFIG_LIBERTAS_DEBUG -#define DEBUG -#define PROC_DEBUG -#endif - -#ifndef DRV_NAME -#define DRV_NAME "libertas" -#endif - - -#define LBS_DEB_ENTER 0x00000001 -#define LBS_DEB_LEAVE 0x00000002 -#define LBS_DEB_MAIN 0x00000004 -#define LBS_DEB_NET 0x00000008 -#define LBS_DEB_MESH 0x00000010 -#define LBS_DEB_WEXT 0x00000020 -#define LBS_DEB_IOCTL 0x00000040 -#define LBS_DEB_SCAN 0x00000080 -#define LBS_DEB_ASSOC 0x00000100 -#define LBS_DEB_JOIN 0x00000200 -#define LBS_DEB_11D 0x00000400 -#define LBS_DEB_DEBUGFS 0x00000800 -#define LBS_DEB_ETHTOOL 0x00001000 -#define LBS_DEB_HOST 0x00002000 -#define LBS_DEB_CMD 0x00004000 -#define LBS_DEB_RX 0x00008000 -#define LBS_DEB_TX 0x00010000 -#define LBS_DEB_USB 0x00020000 -#define LBS_DEB_CS 0x00040000 -#define LBS_DEB_FW 0x00080000 -#define LBS_DEB_THREAD 0x00100000 -#define LBS_DEB_HEX 0x00200000 -#define LBS_DEB_SDIO 0x00400000 -#define LBS_DEB_SYSFS 0x00800000 -#define LBS_DEB_SPI 0x01000000 -#define LBS_DEB_CFG80211 0x02000000 - -extern unsigned int lbs_debug; - -#ifdef DEBUG -#define LBS_DEB_LL(grp, grpnam, fmt, args...) \ -do { if ((lbs_debug & (grp)) == (grp)) \ - printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \ - in_interrupt() ? " (INT)" : "", ## args); } while (0) -#else -#define LBS_DEB_LL(grp, grpnam, fmt, args...) do {} while (0) -#endif - -#define lbs_deb_enter(grp) \ - LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s()\n", __func__); -#define lbs_deb_enter_args(grp, fmt, args...) \ - LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s(" fmt ")\n", __func__, ## args); -#define lbs_deb_leave(grp) \ - LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s()\n", __func__); -#define lbs_deb_leave_args(grp, fmt, args...) \ - LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s(), " fmt "\n", \ - __func__, ##args); -#define lbs_deb_main(fmt, args...) LBS_DEB_LL(LBS_DEB_MAIN, " main", fmt, ##args) -#define lbs_deb_net(fmt, args...) LBS_DEB_LL(LBS_DEB_NET, " net", fmt, ##args) -#define lbs_deb_mesh(fmt, args...) LBS_DEB_LL(LBS_DEB_MESH, " mesh", fmt, ##args) -#define lbs_deb_wext(fmt, args...) LBS_DEB_LL(LBS_DEB_WEXT, " wext", fmt, ##args) -#define lbs_deb_ioctl(fmt, args...) LBS_DEB_LL(LBS_DEB_IOCTL, " ioctl", fmt, ##args) -#define lbs_deb_scan(fmt, args...) LBS_DEB_LL(LBS_DEB_SCAN, " scan", fmt, ##args) -#define lbs_deb_assoc(fmt, args...) LBS_DEB_LL(LBS_DEB_ASSOC, " assoc", fmt, ##args) -#define lbs_deb_join(fmt, args...) LBS_DEB_LL(LBS_DEB_JOIN, " join", fmt, ##args) -#define lbs_deb_11d(fmt, args...) LBS_DEB_LL(LBS_DEB_11D, " 11d", fmt, ##args) -#define lbs_deb_debugfs(fmt, args...) LBS_DEB_LL(LBS_DEB_DEBUGFS, " debugfs", fmt, ##args) -#define lbs_deb_ethtool(fmt, args...) LBS_DEB_LL(LBS_DEB_ETHTOOL, " ethtool", fmt, ##args) -#define lbs_deb_host(fmt, args...) LBS_DEB_LL(LBS_DEB_HOST, " host", fmt, ##args) -#define lbs_deb_cmd(fmt, args...) LBS_DEB_LL(LBS_DEB_CMD, " cmd", fmt, ##args) -#define lbs_deb_rx(fmt, args...) LBS_DEB_LL(LBS_DEB_RX, " rx", fmt, ##args) -#define lbs_deb_tx(fmt, args...) LBS_DEB_LL(LBS_DEB_TX, " tx", fmt, ##args) -#define lbs_deb_fw(fmt, args...) LBS_DEB_LL(LBS_DEB_FW, " fw", fmt, ##args) -#define lbs_deb_usb(fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usb", fmt, ##args) -#define lbs_deb_usbd(dev, fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args) -#define lbs_deb_cs(fmt, args...) LBS_DEB_LL(LBS_DEB_CS, " cs", fmt, ##args) -#define lbs_deb_thread(fmt, args...) LBS_DEB_LL(LBS_DEB_THREAD, " thread", fmt, ##args) -#define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " sdio", fmt, ##args) -#define lbs_deb_sysfs(fmt, args...) LBS_DEB_LL(LBS_DEB_SYSFS, " sysfs", fmt, ##args) -#define lbs_deb_spi(fmt, args...) LBS_DEB_LL(LBS_DEB_SPI, " spi", fmt, ##args) -#define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args) - -#ifdef DEBUG -static inline void lbs_deb_hex(unsigned int grp, const char *prompt, - const u8 *buf, int len) -{ - int i = 0; - - if (len && - (lbs_debug & LBS_DEB_HEX) && - (lbs_debug & grp)) - { - for (i = 1; i <= len; i++) { - if ((i & 0xf) == 1) { - if (i != 1) - printk("\n"); - printk(DRV_NAME " %s: ", prompt); - } - printk("%02x ", (u8) * buf); - buf++; - } - printk("\n"); - } -} -#else -#define lbs_deb_hex(grp,prompt,buf,len) do {} while (0) -#endif - - - -/* Buffer Constants */ - -/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical - * addresses of TxPD buffers. Station has only 8 TxPD available, Whereas - * driver has more local TxPDs. Each TxPD on the host memory is associated - * with a Tx control node. The driver maintains 8 RxPD descriptors for - * station firmware to store Rx packet information. - * - * Current version of MAC has a 32x6 multicast address buffer. - * - * 802.11b can have up to 14 channels, the driver keeps the - * BSSID(MAC address) of each APs or Ad hoc stations it has sensed. - */ - -#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 -#define LBS_NUM_CMD_BUFFERS 10 -#define LBS_CMD_BUFFER_SIZE (2 * 1024) -#define MRVDRV_MAX_CHANNEL_SIZE 14 -#define MRVDRV_ASSOCIATION_TIME_OUT 255 -#define MRVDRV_SNAP_HEADER_LEN 8 - -#define LBS_UPLD_SIZE 2312 -#define DEV_NAME_LEN 32 - -/* Wake criteria for HOST_SLEEP_CFG command */ -#define EHS_WAKE_ON_BROADCAST_DATA 0x0001 -#define EHS_WAKE_ON_UNICAST_DATA 0x0002 -#define EHS_WAKE_ON_MAC_EVENT 0x0004 -#define EHS_WAKE_ON_MULTICAST_DATA 0x0008 -#define EHS_REMOVE_WAKEUP 0xFFFFFFFF -/* Wake rules for Host_Sleep_CFG command */ -#define WOL_RULE_NET_TYPE_INFRA_OR_IBSS 0x00 -#define WOL_RULE_NET_TYPE_MESH 0x10 -#define WOL_RULE_ADDR_TYPE_BCAST 0x01 -#define WOL_RULE_ADDR_TYPE_MCAST 0x08 -#define WOL_RULE_ADDR_TYPE_UCAST 0x02 -#define WOL_RULE_OP_AND 0x01 -#define WOL_RULE_OP_OR 0x02 -#define WOL_RULE_OP_INVALID 0xFF -#define WOL_RESULT_VALID_CMD 0 -#define WOL_RESULT_NOSPC_ERR 1 -#define WOL_RESULT_EEXIST_ERR 2 - -/* Misc constants */ -/* This section defines 802.11 specific contants */ - -#define MRVDRV_MAX_BSS_DESCRIPTS 16 -#define MRVDRV_MAX_REGION_CODE 6 - -#define MRVDRV_DEFAULT_LISTEN_INTERVAL 10 - -#define MRVDRV_CHANNELS_PER_SCAN 4 -#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 - -#define MRVDRV_MIN_BEACON_INTERVAL 20 -#define MRVDRV_MAX_BEACON_INTERVAL 1000 -#define MRVDRV_BEACON_INTERVAL 100 - -#define MARVELL_MESH_IE_LENGTH 9 - -/* - * Values used to populate the struct mrvl_mesh_ie. The only time you need this - * is when enabling the mesh using CMD_MESH_CONFIG. - */ -#define MARVELL_MESH_IE_TYPE 4 -#define MARVELL_MESH_IE_SUBTYPE 0 -#define MARVELL_MESH_IE_VERSION 0 -#define MARVELL_MESH_PROTO_ID_HWMP 0 -#define MARVELL_MESH_METRIC_ID 0 -#define MARVELL_MESH_CAPABILITY 0 - -/* INT status Bit Definition */ -#define MRVDRV_TX_DNLD_RDY 0x0001 -#define MRVDRV_RX_UPLD_RDY 0x0002 -#define MRVDRV_CMD_DNLD_RDY 0x0004 -#define MRVDRV_CMD_UPLD_RDY 0x0008 -#define MRVDRV_CARDEVENT 0x0010 - -/* Automatic TX control default levels */ -#define POW_ADAPT_DEFAULT_P0 13 -#define POW_ADAPT_DEFAULT_P1 15 -#define POW_ADAPT_DEFAULT_P2 18 -#define TPC_DEFAULT_P0 5 -#define TPC_DEFAULT_P1 10 -#define TPC_DEFAULT_P2 13 - -/* TxPD status */ - -/* - * Station firmware use TxPD status field to report final Tx transmit - * result, Bit masks are used to present combined situations. - */ - -#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 -#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 - -/* Tx mesh flag */ -/* - * Currently we are using normal WDS flag as mesh flag. - * TODO: change to proper mesh flag when MAC understands it. - */ -#define TxPD_CONTROL_WDS_FRAME (1<<17) -#define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME - -/* Mesh interface ID */ -#define MESH_IFACE_ID 0x0001 -/* Mesh id should be in bits 14-13-12 */ -#define MESH_IFACE_BIT_OFFSET 0x000c -/* Mesh enable bit in FW capability */ -#define MESH_CAPINFO_ENABLE_MASK (1<<16) - -/* FW definition from Marvell v4 */ -#define MRVL_FW_V4 (0x04) -/* FW definition from Marvell v5 */ -#define MRVL_FW_V5 (0x05) -/* FW definition from Marvell v10 */ -#define MRVL_FW_V10 (0x0a) -/* FW major revision definition */ -#define MRVL_FW_MAJOR_REV(x) ((x)>>24) - -/* RxPD status */ - -#define MRVDRV_RXPD_STATUS_OK 0x0001 - -/* RxPD status - Received packet types */ -/* Rx mesh flag */ -/* - * Currently we are using normal WDS flag as mesh flag. - * TODO: change to proper mesh flag when MAC understands it. - */ -#define RxPD_CONTROL_WDS_FRAME (0x40) -#define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME - -/* RSSI-related defines */ -/* - * RSSI constants are used to implement 802.11 RSSI threshold - * indication. if the Rx packet signal got too weak for 5 consecutive - * times, miniport driver (driver) will report this event to wrapper - */ - -#define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) - -/* RTS/FRAG related defines */ -#define MRVDRV_RTS_MIN_VALUE 0 -#define MRVDRV_RTS_MAX_VALUE 2347 -#define MRVDRV_FRAG_MIN_VALUE 256 -#define MRVDRV_FRAG_MAX_VALUE 2346 - -/* This is for firmware specific length */ -#define EXTRA_LEN 36 - -#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ - (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) - -#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ - (ETH_FRAME_LEN + sizeof(struct rxpd) \ - + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) - -#define CMD_F_HOSTCMD (1 << 0) -#define FW_CAPINFO_WPA (1 << 0) -#define FW_CAPINFO_PS (1 << 1) -#define FW_CAPINFO_FIRMWARE_UPGRADE (1 << 13) -#define FW_CAPINFO_BOOT2_UPGRADE (1<<14) -#define FW_CAPINFO_PERSISTENT_CONFIG (1<<15) - -#define KEY_LEN_WPA_AES 16 -#define KEY_LEN_WPA_TKIP 32 -#define KEY_LEN_WEP_104 13 -#define KEY_LEN_WEP_40 5 - -#define RF_ANTENNA_1 0x1 -#define RF_ANTENNA_2 0x2 -#define RF_ANTENNA_AUTO 0xFFFF - -#define BAND_B (0x01) -#define BAND_G (0x02) -#define ALL_802_11_BANDS (BAND_B | BAND_G) - -#define MAX_RATES 14 - -#define MAX_LEDS 8 - -/* Global Variable Declaration */ -extern const char lbs_driver_version[]; -extern u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE]; - - -/* ENUM definition */ -/* SNRNF_TYPE */ -enum SNRNF_TYPE { - TYPE_BEACON = 0, - TYPE_RXPD, - MAX_TYPE_B -}; - -/* SNRNF_DATA */ -enum SNRNF_DATA { - TYPE_NOAVG = 0, - TYPE_AVG, - MAX_TYPE_AVG -}; - -/* LBS_802_11_POWER_MODE */ -enum LBS_802_11_POWER_MODE { - LBS802_11POWERMODECAM, - LBS802_11POWERMODEMAX_PSP, - LBS802_11POWERMODEFAST_PSP, - /* not a real mode, defined as an upper bound */ - LBS802_11POWEMODEMAX -}; - -/* PS_STATE */ -enum PS_STATE { - PS_STATE_FULL_POWER, - PS_STATE_AWAKE, - PS_STATE_PRE_SLEEP, - PS_STATE_SLEEP -}; - -/* DNLD_STATE */ -enum DNLD_STATE { - DNLD_RES_RECEIVED, - DNLD_DATA_SENT, - DNLD_CMD_SENT, - DNLD_BOOTCMD_SENT, -}; - -/* LBS_MEDIA_STATE */ -enum LBS_MEDIA_STATE { - LBS_CONNECTED, - LBS_DISCONNECTED -}; - -/* LBS_802_11_PRIVACY_FILTER */ -enum LBS_802_11_PRIVACY_FILTER { - LBS802_11PRIVFILTERACCEPTALL, - LBS802_11PRIVFILTER8021XWEP -}; - -/* mv_ms_type */ -enum mv_ms_type { - MVMS_DAT = 0, - MVMS_CMD = 1, - MVMS_TXDONE = 2, - MVMS_EVENT -}; - -/* KEY_TYPE_ID */ -enum KEY_TYPE_ID { - KEY_TYPE_ID_WEP = 0, - KEY_TYPE_ID_TKIP, - KEY_TYPE_ID_AES -}; - -/* KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */ -enum KEY_INFO_WPA { - KEY_INFO_WPA_MCAST = 0x01, - KEY_INFO_WPA_UNICAST = 0x02, - KEY_INFO_WPA_ENABLED = 0x04 -}; - -/* Default values for fwt commands. */ -#define FWT_DEFAULT_METRIC 0 -#define FWT_DEFAULT_DIR 1 -/* Default Rate, 11Mbps */ -#define FWT_DEFAULT_RATE 3 -#define FWT_DEFAULT_SSN 0xffffffff -#define FWT_DEFAULT_DSN 0 -#define FWT_DEFAULT_HOPCOUNT 0 -#define FWT_DEFAULT_TTL 0 -#define FWT_DEFAULT_EXPIRATION 0 -#define FWT_DEFAULT_SLEEPMODE 0 -#define FWT_DEFAULT_SNR 0 - -#endif diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h deleted file mode 100644 index 6bd1608992b0..000000000000 --- a/drivers/net/wireless/libertas/dev.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * This file contains definitions and data structures specific - * to Marvell 802.11 NIC. It contains the Device Information - * structure struct lbs_private.. - */ -#ifndef _LBS_DEV_H_ -#define _LBS_DEV_H_ - -#include "defs.h" -#include "decl.h" -#include "host.h" - -#include - -/* sleep_params */ -struct sleep_params { - uint16_t sp_error; - uint16_t sp_offset; - uint16_t sp_stabletime; - uint8_t sp_calcontrol; - uint8_t sp_extsleepclk; - uint16_t sp_reserved; -}; - -/* Mesh statistics */ -struct lbs_mesh_stats { - u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ - u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ - u32 fwd_drop_ttl; /* Fwd: TTL zero */ - u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ - u32 fwd_drop_noroute; /* Fwd: No route to Destination */ - u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ - u32 drop_blind; /* Rx: Dropped by blinding table */ - u32 tx_failed_cnt; /* Tx: Failed transmissions */ -}; - -/* Private structure for the MV device */ -struct lbs_private { - - /* Basic networking */ - struct net_device *dev; - u32 connect_status; - struct work_struct mcast_work; - u32 nr_of_multicastmacaddr; - u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; - - /* CFG80211 */ - struct wireless_dev *wdev; - bool wiphy_registered; - struct cfg80211_scan_request *scan_req; - u8 assoc_bss[ETH_ALEN]; - u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - u8 disassoc_reason; - - /* Mesh */ - struct net_device *mesh_dev; /* Virtual device */ -#ifdef CONFIG_LIBERTAS_MESH - struct lbs_mesh_stats mstats; - uint16_t mesh_tlv; - u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1]; - u8 mesh_ssid_len; - u8 mesh_channel; -#endif - - /* Debugfs */ - struct dentry *debugfs_dir; - struct dentry *debugfs_debug; - struct dentry *debugfs_files[6]; - struct dentry *events_dir; - struct dentry *debugfs_events_files[6]; - struct dentry *regs_dir; - struct dentry *debugfs_regs_files[6]; - - /* Hardware debugging */ - u32 mac_offset; - u32 bbp_offset; - u32 rf_offset; - - /* Power management */ - u16 psmode; - u32 psstate; - u8 needtowakeup; - - /* Deep sleep */ - int is_deep_sleep; - int deep_sleep_required; - int is_auto_deep_sleep_enabled; - int wakeup_dev_required; - int is_activity_detected; - int auto_deep_sleep_timeout; /* in ms */ - wait_queue_head_t ds_awake_q; - struct timer_list auto_deepsleep_timer; - - /* Host sleep*/ - int is_host_sleep_configured; - int is_host_sleep_activated; - wait_queue_head_t host_sleep_q; - - /* Hardware access */ - void *card; - bool iface_running; - u8 fw_ready; - u8 surpriseremoved; - u8 setup_fw_on_resume; - int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb); - void (*reset_card) (struct lbs_private *priv); - int (*power_save) (struct lbs_private *priv); - int (*power_restore) (struct lbs_private *priv); - int (*enter_deep_sleep) (struct lbs_private *priv); - int (*exit_deep_sleep) (struct lbs_private *priv); - int (*reset_deep_sleep_wakeup) (struct lbs_private *priv); - - /* Adapter info (from EEPROM) */ - u32 fwrelease; - u32 fwcapinfo; - u16 regioncode; - u8 current_addr[ETH_ALEN]; - u8 copied_hwaddr; - - /* Command download */ - u8 dnld_sent; - /* bit0 1/0=data_sent/data_tx_done, - bit1 1/0=cmd_sent/cmd_tx_done, - all other bits reserved 0 */ - u16 seqnum; - struct cmd_ctrl_node *cmd_array; - struct cmd_ctrl_node *cur_cmd; - struct list_head cmdfreeq; /* free command buffers */ - struct list_head cmdpendingq; /* pending command buffers */ - struct timer_list command_timer; - int cmd_timed_out; - - /* Command responses sent from the hardware to the driver */ - u8 resp_idx; - u8 resp_buf[2][LBS_UPLD_SIZE]; - u32 resp_len[2]; - - /* Events sent from hardware to driver */ - struct kfifo event_fifo; - - /* thread to service interrupts */ - struct task_struct *main_thread; - wait_queue_head_t waitq; - struct workqueue_struct *work_thread; - - /* Encryption stuff */ - u8 authtype_auto; - u8 wep_tx_key; - u8 wep_key[4][WLAN_KEY_LEN_WEP104]; - u8 wep_key_len[4]; - - /* Wake On LAN */ - uint32_t wol_criteria; - uint8_t wol_gpio; - uint8_t wol_gap; - bool ehs_remove_supported; - - /* Transmitting */ - int tx_pending_len; /* -1 while building packet */ - u8 tx_pending_buf[LBS_UPLD_SIZE]; - /* protected by hard_start_xmit serialization */ - u8 txretrycount; - struct sk_buff *currenttxskb; - struct timer_list tx_lockup_timer; - - /* Locks */ - struct mutex lock; - spinlock_t driver_lock; - - /* NIC/link operation characteristics */ - u16 mac_control; - u8 radio_on; - u8 cur_rate; - u8 channel; - s16 txpower_cur; - s16 txpower_min; - s16 txpower_max; - - /* Scanning */ - struct delayed_work scan_work; - int scan_channel; - /* Queue of things waiting for scan completion */ - wait_queue_head_t scan_q; - /* Whether the scan was initiated internally and not by cfg80211 */ - bool internal_scan; - - /* Firmware load */ - u32 fw_model; - wait_queue_head_t fw_waitq; - struct device *fw_device; - const struct firmware *helper_fw; - const struct lbs_fw_table *fw_table; - const struct lbs_fw_table *fw_iter; - lbs_fw_cb fw_callback; -}; - -extern struct cmd_confirm_sleep confirm_sleep; - -/* Check if there is an interface active. */ -static inline int lbs_iface_active(struct lbs_private *priv) -{ - int r; - - r = netif_running(priv->dev); - if (priv->mesh_dev) - r |= netif_running(priv->mesh_dev); - - return r; -} - -#endif diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c deleted file mode 100644 index f955b2d66ed6..000000000000 --- a/drivers/net/wireless/libertas/ethtool.c +++ /dev/null @@ -1,120 +0,0 @@ -#include -#include -#include -#include - -#include "decl.h" -#include "cmd.h" -#include "mesh.h" - - -static void lbs_ethtool_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - struct lbs_private *priv = dev->ml_priv; - - snprintf(info->fw_version, sizeof(info->fw_version), - "%u.%u.%u.p%u", - priv->fwrelease >> 24 & 0xff, - priv->fwrelease >> 16 & 0xff, - priv->fwrelease >> 8 & 0xff, - priv->fwrelease & 0xff); - strlcpy(info->driver, "libertas", sizeof(info->driver)); - strlcpy(info->version, lbs_driver_version, sizeof(info->version)); -} - -/* - * All 8388 parts have 16KiB EEPROM size at the time of writing. - * In case that changes this needs fixing. - */ -#define LBS_EEPROM_LEN 16384 - -static int lbs_ethtool_get_eeprom_len(struct net_device *dev) -{ - return LBS_EEPROM_LEN; -} - -static int lbs_ethtool_get_eeprom(struct net_device *dev, - struct ethtool_eeprom *eeprom, u8 * bytes) -{ - struct lbs_private *priv = dev->ml_priv; - struct cmd_ds_802_11_eeprom_access cmd; - int ret; - - lbs_deb_enter(LBS_DEB_ETHTOOL); - - if (eeprom->offset + eeprom->len > LBS_EEPROM_LEN || - eeprom->len > LBS_EEPROM_READ_LEN) { - ret = -EINVAL; - goto out; - } - - cmd.hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) - - LBS_EEPROM_READ_LEN + eeprom->len); - cmd.action = cpu_to_le16(CMD_ACT_GET); - cmd.offset = cpu_to_le16(eeprom->offset); - cmd.len = cpu_to_le16(eeprom->len); - ret = lbs_cmd_with_response(priv, CMD_802_11_EEPROM_ACCESS, &cmd); - if (!ret) - memcpy(bytes, cmd.value, eeprom->len); - -out: - lbs_deb_leave_args(LBS_DEB_ETHTOOL, "ret %d", ret); - return ret; -} - -static void lbs_ethtool_get_wol(struct net_device *dev, - struct ethtool_wolinfo *wol) -{ - struct lbs_private *priv = dev->ml_priv; - - wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY; - - if (priv->wol_criteria == EHS_REMOVE_WAKEUP) - return; - - if (priv->wol_criteria & EHS_WAKE_ON_UNICAST_DATA) - wol->wolopts |= WAKE_UCAST; - if (priv->wol_criteria & EHS_WAKE_ON_MULTICAST_DATA) - wol->wolopts |= WAKE_MCAST; - if (priv->wol_criteria & EHS_WAKE_ON_BROADCAST_DATA) - wol->wolopts |= WAKE_BCAST; - if (priv->wol_criteria & EHS_WAKE_ON_MAC_EVENT) - wol->wolopts |= WAKE_PHY; -} - -static int lbs_ethtool_set_wol(struct net_device *dev, - struct ethtool_wolinfo *wol) -{ - struct lbs_private *priv = dev->ml_priv; - - if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY)) - return -EOPNOTSUPP; - - priv->wol_criteria = 0; - if (wol->wolopts & WAKE_UCAST) - priv->wol_criteria |= EHS_WAKE_ON_UNICAST_DATA; - if (wol->wolopts & WAKE_MCAST) - priv->wol_criteria |= EHS_WAKE_ON_MULTICAST_DATA; - if (wol->wolopts & WAKE_BCAST) - priv->wol_criteria |= EHS_WAKE_ON_BROADCAST_DATA; - if (wol->wolopts & WAKE_PHY) - priv->wol_criteria |= EHS_WAKE_ON_MAC_EVENT; - if (wol->wolopts == 0) - priv->wol_criteria |= EHS_REMOVE_WAKEUP; - return 0; -} - -const struct ethtool_ops lbs_ethtool_ops = { - .get_drvinfo = lbs_ethtool_get_drvinfo, - .get_eeprom = lbs_ethtool_get_eeprom, - .get_eeprom_len = lbs_ethtool_get_eeprom_len, -#ifdef CONFIG_LIBERTAS_MESH - .get_sset_count = lbs_mesh_ethtool_get_sset_count, - .get_ethtool_stats = lbs_mesh_ethtool_get_stats, - .get_strings = lbs_mesh_ethtool_get_strings, -#endif - .get_wol = lbs_ethtool_get_wol, - .set_wol = lbs_ethtool_set_wol, -}; - diff --git a/drivers/net/wireless/libertas/firmware.c b/drivers/net/wireless/libertas/firmware.c deleted file mode 100644 index 51b92b5df119..000000000000 --- a/drivers/net/wireless/libertas/firmware.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Firmware loading and handling functions. - */ - -#include -#include -#include - -#include "dev.h" -#include "decl.h" - -static void load_next_firmware_from_table(struct lbs_private *private); - -static void lbs_fw_loaded(struct lbs_private *priv, int ret, - const struct firmware *helper, const struct firmware *mainfw) -{ - unsigned long flags; - - lbs_deb_fw("firmware load complete, code %d\n", ret); - - /* User must free helper/mainfw */ - priv->fw_callback(priv, ret, helper, mainfw); - - spin_lock_irqsave(&priv->driver_lock, flags); - priv->fw_callback = NULL; - wake_up(&priv->fw_waitq); - spin_unlock_irqrestore(&priv->driver_lock, flags); -} - -static void do_load_firmware(struct lbs_private *priv, const char *name, - void (*cb)(const struct firmware *fw, void *context)) -{ - int ret; - - lbs_deb_fw("Requesting %s\n", name); - ret = request_firmware_nowait(THIS_MODULE, true, name, - priv->fw_device, GFP_KERNEL, priv, cb); - if (ret) { - lbs_deb_fw("request_firmware_nowait error %d\n", ret); - lbs_fw_loaded(priv, ret, NULL, NULL); - } -} - -static void main_firmware_cb(const struct firmware *firmware, void *context) -{ - struct lbs_private *priv = context; - - if (!firmware) { - /* Failed to find firmware: try next table entry */ - load_next_firmware_from_table(priv); - return; - } - - /* Firmware found! */ - lbs_fw_loaded(priv, 0, priv->helper_fw, firmware); - if (priv->helper_fw) { - release_firmware (priv->helper_fw); - priv->helper_fw = NULL; - } - release_firmware (firmware); -} - -static void helper_firmware_cb(const struct firmware *firmware, void *context) -{ - struct lbs_private *priv = context; - - if (!firmware) { - /* Failed to find firmware: try next table entry */ - load_next_firmware_from_table(priv); - return; - } - - /* Firmware found! */ - if (priv->fw_iter->fwname) { - priv->helper_fw = firmware; - do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb); - } else { - /* No main firmware needed for this helper --> success! */ - lbs_fw_loaded(priv, 0, firmware, NULL); - } -} - -static void load_next_firmware_from_table(struct lbs_private *priv) -{ - const struct lbs_fw_table *iter; - - if (!priv->fw_iter) - iter = priv->fw_table; - else - iter = ++priv->fw_iter; - - if (priv->helper_fw) { - release_firmware(priv->helper_fw); - priv->helper_fw = NULL; - } - -next: - if (!iter->helper) { - /* End of table hit. */ - lbs_fw_loaded(priv, -ENOENT, NULL, NULL); - return; - } - - if (iter->model != priv->fw_model) { - iter++; - goto next; - } - - priv->fw_iter = iter; - do_load_firmware(priv, iter->helper, helper_firmware_cb); -} - -void lbs_wait_for_firmware_load(struct lbs_private *priv) -{ - wait_event(priv->fw_waitq, priv->fw_callback == NULL); -} - -/** - * lbs_get_firmware_async - Retrieves firmware asynchronously. Can load - * either a helper firmware and a main firmware (2-stage), or just the helper. - * - * @priv: Pointer to lbs_private instance - * @dev: A pointer to &device structure - * @card_model: Bus-specific card model ID used to filter firmware table - * elements - * @fw_table: Table of firmware file names and device model numbers - * terminated by an entry with a NULL helper name - * @callback: User callback to invoke when firmware load succeeds or fails. - */ -int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, - u32 card_model, const struct lbs_fw_table *fw_table, - lbs_fw_cb callback) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->driver_lock, flags); - if (priv->fw_callback) { - lbs_deb_fw("firmware load already in progress\n"); - spin_unlock_irqrestore(&priv->driver_lock, flags); - return -EBUSY; - } - - priv->fw_device = device; - priv->fw_callback = callback; - priv->fw_table = fw_table; - priv->fw_iter = NULL; - priv->fw_model = card_model; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbs_deb_fw("Starting async firmware load\n"); - load_next_firmware_from_table(priv); - return 0; -} -EXPORT_SYMBOL_GPL(lbs_get_firmware_async); - -/** - * lbs_get_firmware - Retrieves two-stage firmware - * - * @dev: A pointer to &device structure - * @card_model: Bus-specific card model ID used to filter firmware table - * elements - * @fw_table: Table of firmware file names and device model numbers - * terminated by an entry with a NULL helper name - * @helper: On success, the helper firmware; caller must free - * @mainfw: On success, the main firmware; caller must free - * - * Deprecated: use lbs_get_firmware_async() instead. - * - * returns: 0 on success, non-zero on failure - */ -int lbs_get_firmware(struct device *dev, u32 card_model, - const struct lbs_fw_table *fw_table, - const struct firmware **helper, - const struct firmware **mainfw) -{ - const struct lbs_fw_table *iter; - int ret; - - BUG_ON(helper == NULL); - BUG_ON(mainfw == NULL); - - /* Search for firmware to use from the table. */ - iter = fw_table; - while (iter && iter->helper) { - if (iter->model != card_model) - goto next; - - if (*helper == NULL) { - ret = request_firmware(helper, iter->helper, dev); - if (ret) - goto next; - - /* If the device has one-stage firmware (ie cf8305) and - * we've got it then we don't need to bother with the - * main firmware. - */ - if (iter->fwname == NULL) - return 0; - } - - if (*mainfw == NULL) { - ret = request_firmware(mainfw, iter->fwname, dev); - if (ret) { - /* Clear the helper to ensure we don't have - * mismatched firmware pairs. - */ - release_firmware(*helper); - *helper = NULL; - } - } - - if (*helper && *mainfw) - return 0; - - next: - iter++; - } - - /* Failed */ - release_firmware(*helper); - *helper = NULL; - release_firmware(*mainfw); - *mainfw = NULL; - - return -ENOENT; -} -EXPORT_SYMBOL_GPL(lbs_get_firmware); diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h deleted file mode 100644 index 96726f79a1dd..000000000000 --- a/drivers/net/wireless/libertas/host.h +++ /dev/null @@ -1,978 +0,0 @@ -/* - * This file function prototypes, data structure - * and definitions for all the host/station commands - */ - -#ifndef _LBS_HOST_H_ -#define _LBS_HOST_H_ - -#include "types.h" -#include "defs.h" - -#define DEFAULT_AD_HOC_CHANNEL 6 - -#define CMD_OPTION_WAITFORRSP 0x0002 - -/* Host command IDs */ - -/* - * Return command are almost always the same as the host command, but with - * bit 15 set high. There are a few exceptions, though... - */ -#define CMD_RET(cmd) (0x8000 | cmd) - -/* Return command convention exceptions: */ -#define CMD_RET_802_11_ASSOCIATE 0x8012 - -/* Command codes */ -#define CMD_GET_HW_SPEC 0x0003 -#define CMD_EEPROM_UPDATE 0x0004 -#define CMD_802_11_RESET 0x0005 -#define CMD_802_11_SCAN 0x0006 -#define CMD_802_11_GET_LOG 0x000b -#define CMD_MAC_MULTICAST_ADR 0x0010 -#define CMD_802_11_AUTHENTICATE 0x0011 -#define CMD_802_11_EEPROM_ACCESS 0x0059 -#define CMD_802_11_ASSOCIATE 0x0050 -#define CMD_802_11_SET_WEP 0x0013 -#define CMD_802_11_GET_STAT 0x0014 -#define CMD_802_3_GET_STAT 0x0015 -#define CMD_802_11_SNMP_MIB 0x0016 -#define CMD_MAC_REG_MAP 0x0017 -#define CMD_BBP_REG_MAP 0x0018 -#define CMD_MAC_REG_ACCESS 0x0019 -#define CMD_BBP_REG_ACCESS 0x001a -#define CMD_RF_REG_ACCESS 0x001b -#define CMD_802_11_RADIO_CONTROL 0x001c -#define CMD_802_11_RF_CHANNEL 0x001d -#define CMD_802_11_RF_TX_POWER 0x001e -#define CMD_802_11_RSSI 0x001f -#define CMD_802_11_RF_ANTENNA 0x0020 -#define CMD_802_11_PS_MODE 0x0021 -#define CMD_802_11_DATA_RATE 0x0022 -#define CMD_RF_REG_MAP 0x0023 -#define CMD_802_11_DEAUTHENTICATE 0x0024 -#define CMD_802_11_REASSOCIATE 0x0025 -#define CMD_MAC_CONTROL 0x0028 -#define CMD_802_11_AD_HOC_START 0x002b -#define CMD_802_11_AD_HOC_JOIN 0x002c -#define CMD_802_11_QUERY_TKIP_REPLY_CNTRS 0x002e -#define CMD_802_11_ENABLE_RSN 0x002f -#define CMD_802_11_SET_AFC 0x003c -#define CMD_802_11_GET_AFC 0x003d -#define CMD_802_11_DEEP_SLEEP 0x003e -#define CMD_802_11_AD_HOC_STOP 0x0040 -#define CMD_802_11_HOST_SLEEP_CFG 0x0043 -#define CMD_802_11_WAKEUP_CONFIRM 0x0044 -#define CMD_802_11_HOST_SLEEP_ACTIVATE 0x0045 -#define CMD_802_11_BEACON_STOP 0x0049 -#define CMD_802_11_MAC_ADDRESS 0x004d -#define CMD_802_11_LED_GPIO_CTRL 0x004e -#define CMD_802_11_BAND_CONFIG 0x0058 -#define CMD_GSPI_BUS_CONFIG 0x005a -#define CMD_802_11D_DOMAIN_INFO 0x005b -#define CMD_802_11_KEY_MATERIAL 0x005e -#define CMD_802_11_SLEEP_PARAMS 0x0066 -#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067 -#define CMD_802_11_SLEEP_PERIOD 0x0068 -#define CMD_802_11_TPC_CFG 0x0072 -#define CMD_802_11_PA_CFG 0x0073 -#define CMD_802_11_FW_WAKE_METHOD 0x0074 -#define CMD_802_11_SUBSCRIBE_EVENT 0x0075 -#define CMD_802_11_RATE_ADAPT_RATESET 0x0076 -#define CMD_802_11_TX_RATE_QUERY 0x007f -#define CMD_GET_TSF 0x0080 -#define CMD_BT_ACCESS 0x0087 -#define CMD_FWT_ACCESS 0x0095 -#define CMD_802_11_MONITOR_MODE 0x0098 -#define CMD_MESH_ACCESS 0x009b -#define CMD_MESH_CONFIG_OLD 0x00a3 -#define CMD_MESH_CONFIG 0x00ac -#define CMD_SET_BOOT2_VER 0x00a5 -#define CMD_FUNC_INIT 0x00a9 -#define CMD_FUNC_SHUTDOWN 0x00aa -#define CMD_802_11_BEACON_CTRL 0x00b0 - -/* For the IEEE Power Save */ -#define PS_MODE_ACTION_ENTER_PS 0x0030 -#define PS_MODE_ACTION_EXIT_PS 0x0031 -#define PS_MODE_ACTION_SLEEP_CONFIRMED 0x0034 - -#define CMD_ENABLE_RSN 0x0001 -#define CMD_DISABLE_RSN 0x0000 - -#define CMD_ACT_GET 0x0000 -#define CMD_ACT_SET 0x0001 - -/* Define action or option for CMD_802_11_SET_WEP */ -#define CMD_ACT_ADD 0x0002 -#define CMD_ACT_REMOVE 0x0004 - -#define CMD_TYPE_WEP_40_BIT 0x01 -#define CMD_TYPE_WEP_104_BIT 0x02 - -#define CMD_NUM_OF_WEP_KEYS 4 - -#define CMD_WEP_KEY_INDEX_MASK 0x3fff - -/* Define action or option for CMD_802_11_SCAN */ -#define CMD_BSS_TYPE_BSS 0x0001 -#define CMD_BSS_TYPE_IBSS 0x0002 -#define CMD_BSS_TYPE_ANY 0x0003 - -/* Define action or option for CMD_802_11_SCAN */ -#define CMD_SCAN_TYPE_ACTIVE 0x0000 -#define CMD_SCAN_TYPE_PASSIVE 0x0001 - -#define CMD_SCAN_RADIO_TYPE_BG 0 - -#define CMD_SCAN_PROBE_DELAY_TIME 0 - -/* Define action or option for CMD_MAC_CONTROL */ -#define CMD_ACT_MAC_RX_ON 0x0001 -#define CMD_ACT_MAC_TX_ON 0x0002 -#define CMD_ACT_MAC_LOOPBACK_ON 0x0004 -#define CMD_ACT_MAC_WEP_ENABLE 0x0008 -#define CMD_ACT_MAC_INT_ENABLE 0x0010 -#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 -#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 -#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 -#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 -#define CMD_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400 - -/* Event flags for CMD_802_11_SUBSCRIBE_EVENT */ -#define CMD_SUBSCRIBE_RSSI_LOW 0x0001 -#define CMD_SUBSCRIBE_SNR_LOW 0x0002 -#define CMD_SUBSCRIBE_FAILCOUNT 0x0004 -#define CMD_SUBSCRIBE_BCNMISS 0x0008 -#define CMD_SUBSCRIBE_RSSI_HIGH 0x0010 -#define CMD_SUBSCRIBE_SNR_HIGH 0x0020 - -#define RADIO_PREAMBLE_LONG 0x00 -#define RADIO_PREAMBLE_SHORT 0x02 -#define RADIO_PREAMBLE_AUTO 0x04 - -/* Define action or option for CMD_802_11_RF_CHANNEL */ -#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 -#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 - -/* Define action or option for CMD_802_11_DATA_RATE */ -#define CMD_ACT_SET_TX_AUTO 0x0000 -#define CMD_ACT_SET_TX_FIX_RATE 0x0001 -#define CMD_ACT_GET_TX_RATE 0x0002 - -/* Options for CMD_802_11_FW_WAKE_METHOD */ -#define CMD_WAKE_METHOD_UNCHANGED 0x0000 -#define CMD_WAKE_METHOD_COMMAND_INT 0x0001 -#define CMD_WAKE_METHOD_GPIO 0x0002 - -/* Object IDs for CMD_802_11_SNMP_MIB */ -#define SNMP_MIB_OID_BSS_TYPE 0x0000 -#define SNMP_MIB_OID_OP_RATE_SET 0x0001 -#define SNMP_MIB_OID_BEACON_PERIOD 0x0002 /* Reserved on v9+ */ -#define SNMP_MIB_OID_DTIM_PERIOD 0x0003 /* Reserved on v9+ */ -#define SNMP_MIB_OID_ASSOC_TIMEOUT 0x0004 /* Reserved on v9+ */ -#define SNMP_MIB_OID_RTS_THRESHOLD 0x0005 -#define SNMP_MIB_OID_SHORT_RETRY_LIMIT 0x0006 -#define SNMP_MIB_OID_LONG_RETRY_LIMIT 0x0007 -#define SNMP_MIB_OID_FRAG_THRESHOLD 0x0008 -#define SNMP_MIB_OID_11D_ENABLE 0x0009 -#define SNMP_MIB_OID_11H_ENABLE 0x000A - -/* Define action or option for CMD_BT_ACCESS */ -enum cmd_bt_access_opts { - /* The bt commands start at 5 instead of 1 because the old dft commands - * are mapped to 1-4. These old commands are no longer maintained and - * should not be called. - */ - CMD_ACT_BT_ACCESS_ADD = 5, - CMD_ACT_BT_ACCESS_DEL, - CMD_ACT_BT_ACCESS_LIST, - CMD_ACT_BT_ACCESS_RESET, - CMD_ACT_BT_ACCESS_SET_INVERT, - CMD_ACT_BT_ACCESS_GET_INVERT -}; - -/* Define action or option for CMD_FWT_ACCESS */ -enum cmd_fwt_access_opts { - CMD_ACT_FWT_ACCESS_ADD = 1, - CMD_ACT_FWT_ACCESS_DEL, - CMD_ACT_FWT_ACCESS_LOOKUP, - CMD_ACT_FWT_ACCESS_LIST, - CMD_ACT_FWT_ACCESS_LIST_ROUTE, - CMD_ACT_FWT_ACCESS_LIST_NEIGHBOR, - CMD_ACT_FWT_ACCESS_RESET, - CMD_ACT_FWT_ACCESS_CLEANUP, - CMD_ACT_FWT_ACCESS_TIME, -}; - -/* Define action or option for CMD_802_11_HOST_SLEEP_CFG */ -enum cmd_wol_cfg_opts { - CMD_ACT_ACTION_NONE = 0, - CMD_ACT_SET_WOL_RULE, - CMD_ACT_GET_WOL_RULE, - CMD_ACT_RESET_WOL_RULE, -}; - -/* Define action or option for CMD_MESH_ACCESS */ -enum cmd_mesh_access_opts { - CMD_ACT_MESH_GET_TTL = 1, - CMD_ACT_MESH_SET_TTL, - CMD_ACT_MESH_GET_STATS, - CMD_ACT_MESH_GET_ANYCAST, - CMD_ACT_MESH_SET_ANYCAST, - CMD_ACT_MESH_SET_LINK_COSTS, - CMD_ACT_MESH_GET_LINK_COSTS, - CMD_ACT_MESH_SET_BCAST_RATE, - CMD_ACT_MESH_GET_BCAST_RATE, - CMD_ACT_MESH_SET_RREQ_DELAY, - CMD_ACT_MESH_GET_RREQ_DELAY, - CMD_ACT_MESH_SET_ROUTE_EXP, - CMD_ACT_MESH_GET_ROUTE_EXP, - CMD_ACT_MESH_SET_AUTOSTART_ENABLED, - CMD_ACT_MESH_GET_AUTOSTART_ENABLED, - CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT = 17, -}; - -/* Define actions and types for CMD_MESH_CONFIG */ -enum cmd_mesh_config_actions { - CMD_ACT_MESH_CONFIG_STOP = 0, - CMD_ACT_MESH_CONFIG_START, - CMD_ACT_MESH_CONFIG_SET, - CMD_ACT_MESH_CONFIG_GET, -}; - -enum cmd_mesh_config_types { - CMD_TYPE_MESH_SET_BOOTFLAG = 1, - CMD_TYPE_MESH_SET_BOOTTIME, - CMD_TYPE_MESH_SET_DEF_CHANNEL, - CMD_TYPE_MESH_SET_MESH_IE, - CMD_TYPE_MESH_GET_DEFAULTS, - CMD_TYPE_MESH_GET_MESH_IE, /* GET_DEFAULTS is superset of GET_MESHIE */ -}; - -/* Card Event definition */ -#define MACREG_INT_CODE_TX_PPA_FREE 0 -#define MACREG_INT_CODE_TX_DMA_DONE 1 -#define MACREG_INT_CODE_LINK_LOST_W_SCAN 2 -#define MACREG_INT_CODE_LINK_LOST_NO_SCAN 3 -#define MACREG_INT_CODE_LINK_SENSED 4 -#define MACREG_INT_CODE_CMD_FINISHED 5 -#define MACREG_INT_CODE_MIB_CHANGED 6 -#define MACREG_INT_CODE_INIT_DONE 7 -#define MACREG_INT_CODE_DEAUTHENTICATED 8 -#define MACREG_INT_CODE_DISASSOCIATED 9 -#define MACREG_INT_CODE_PS_AWAKE 10 -#define MACREG_INT_CODE_PS_SLEEP 11 -#define MACREG_INT_CODE_MIC_ERR_MULTICAST 13 -#define MACREG_INT_CODE_MIC_ERR_UNICAST 14 -#define MACREG_INT_CODE_WM_AWAKE 15 -#define MACREG_INT_CODE_DEEP_SLEEP_AWAKE 16 -#define MACREG_INT_CODE_ADHOC_BCN_LOST 17 -#define MACREG_INT_CODE_HOST_AWAKE 18 -#define MACREG_INT_CODE_STOP_TX 19 -#define MACREG_INT_CODE_START_TX 20 -#define MACREG_INT_CODE_CHANNEL_SWITCH 21 -#define MACREG_INT_CODE_MEASUREMENT_RDY 22 -#define MACREG_INT_CODE_WMM_CHANGE 23 -#define MACREG_INT_CODE_BG_SCAN_REPORT 24 -#define MACREG_INT_CODE_RSSI_LOW 25 -#define MACREG_INT_CODE_SNR_LOW 26 -#define MACREG_INT_CODE_MAX_FAIL 27 -#define MACREG_INT_CODE_RSSI_HIGH 28 -#define MACREG_INT_CODE_SNR_HIGH 29 -#define MACREG_INT_CODE_MESH_AUTO_STARTED 35 -#define MACREG_INT_CODE_FIRMWARE_READY 48 - - -/* 802.11-related definitions */ - -/* TxPD descriptor */ -struct txpd { - /* union to cope up with later FW revisions */ - union { - /* Current Tx packet status */ - __le32 tx_status; - struct { - /* BSS type: client, AP, etc. */ - u8 bss_type; - /* BSS number */ - u8 bss_num; - /* Reserved */ - __le16 reserved; - } bss; - } u; - /* Tx control */ - __le32 tx_control; - __le32 tx_packet_location; - /* Tx packet length */ - __le16 tx_packet_length; - /* First 2 byte of destination MAC address */ - u8 tx_dest_addr_high[2]; - /* Last 4 byte of destination MAC address */ - u8 tx_dest_addr_low[4]; - /* Pkt Priority */ - u8 priority; - /* Pkt Trasnit Power control */ - u8 powermgmt; - /* Amount of time the packet has been queued (units = 2ms) */ - u8 pktdelay_2ms; - /* reserved */ - u8 reserved1; -} __packed; - -/* RxPD Descriptor */ -struct rxpd { - /* union to cope up with later FW revisions */ - union { - /* Current Rx packet status */ - __le16 status; - struct { - /* BSS type: client, AP, etc. */ - u8 bss_type; - /* BSS number */ - u8 bss_num; - } __packed bss; - } __packed u; - - /* SNR */ - u8 snr; - - /* Tx control */ - u8 rx_control; - - /* Pkt length */ - __le16 pkt_len; - - /* Noise Floor */ - u8 nf; - - /* Rx Packet Rate */ - u8 rx_rate; - - /* Pkt addr */ - __le32 pkt_ptr; - - /* Next Rx RxPD addr */ - __le32 next_rxpd_ptr; - - /* Pkt Priority */ - u8 priority; - u8 reserved[3]; -} __packed; - -struct cmd_header { - __le16 command; - __le16 size; - __le16 seqnum; - __le16 result; -} __packed; - -/* Generic structure to hold all key types. */ -struct enc_key { - u16 len; - u16 flags; /* KEY_INFO_* from defs.h */ - u16 type; /* KEY_TYPE_* from defs.h */ - u8 key[32]; -}; - -/* lbs_offset_value */ -struct lbs_offset_value { - u32 offset; - u32 value; -} __packed; - -#define MAX_11D_TRIPLETS 83 - -struct mrvl_ie_domain_param_set { - struct mrvl_ie_header header; - - u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - struct ieee80211_country_ie_triplet triplet[MAX_11D_TRIPLETS]; -} __packed; - -struct cmd_ds_802_11d_domain_info { - struct cmd_header hdr; - - __le16 action; - struct mrvl_ie_domain_param_set domain; -} __packed; - -/* - * Define data structure for CMD_GET_HW_SPEC - * This structure defines the response for the GET_HW_SPEC command - */ -struct cmd_ds_get_hw_spec { - struct cmd_header hdr; - - /* HW Interface version number */ - __le16 hwifversion; - /* HW version number */ - __le16 version; - /* Max number of TxPD FW can handle */ - __le16 nr_txpd; - /* Max no of Multicast address */ - __le16 nr_mcast_adr; - /* MAC address */ - u8 permanentaddr[6]; - - /* region Code */ - __le16 regioncode; - - /* Number of antenna used */ - __le16 nr_antenna; - - /* FW release number, example 0x01030304 = 2.3.4p1 */ - __le32 fwrelease; - - /* Base Address of TxPD queue */ - __le32 wcb_base; - /* Read Pointer of RxPd queue */ - __le32 rxpd_rdptr; - - /* Write Pointer of RxPd queue */ - __le32 rxpd_wrptr; - - /*FW/HW capability */ - __le32 fwcapinfo; -} __packed; - -struct cmd_ds_802_11_subscribe_event { - struct cmd_header hdr; - - __le16 action; - __le16 events; - - /* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a - * number of TLVs. From the v5.1 manual, those TLVs would add up to - * 40 bytes. However, future firmware might add additional TLVs, so I - * bump this up a bit. - */ - uint8_t tlv[128]; -} __packed; - -/* - * This scan handle Country Information IE(802.11d compliant) - * Define data structure for CMD_802_11_SCAN - */ -struct cmd_ds_802_11_scan { - struct cmd_header hdr; - - uint8_t bsstype; - uint8_t bssid[ETH_ALEN]; - uint8_t tlvbuffer[0]; -} __packed; - -struct cmd_ds_802_11_scan_rsp { - struct cmd_header hdr; - - __le16 bssdescriptsize; - uint8_t nr_sets; - uint8_t bssdesc_and_tlvbuffer[0]; -} __packed; - -struct cmd_ds_802_11_get_log { - struct cmd_header hdr; - - __le32 mcasttxframe; - __le32 failed; - __le32 retry; - __le32 multiretry; - __le32 framedup; - __le32 rtssuccess; - __le32 rtsfailure; - __le32 ackfailure; - __le32 rxfrag; - __le32 mcastrxframe; - __le32 fcserror; - __le32 txframe; - __le32 wepundecryptable; -} __packed; - -struct cmd_ds_mac_control { - struct cmd_header hdr; - __le16 action; - u16 reserved; -} __packed; - -struct cmd_ds_mac_multicast_adr { - struct cmd_header hdr; - __le16 action; - __le16 nr_of_adrs; - u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; -} __packed; - -struct cmd_ds_802_11_authenticate { - struct cmd_header hdr; - - u8 bssid[ETH_ALEN]; - u8 authtype; - u8 reserved[10]; -} __packed; - -struct cmd_ds_802_11_deauthenticate { - struct cmd_header hdr; - - u8 macaddr[ETH_ALEN]; - __le16 reasoncode; -} __packed; - -struct cmd_ds_802_11_associate { - struct cmd_header hdr; - - u8 bssid[6]; - __le16 capability; - __le16 listeninterval; - __le16 bcnperiod; - u8 dtimperiod; - u8 iebuf[512]; /* Enough for required and most optional IEs */ -} __packed; - -struct cmd_ds_802_11_associate_response { - struct cmd_header hdr; - - __le16 capability; - __le16 statuscode; - __le16 aid; - u8 iebuf[512]; -} __packed; - -struct cmd_ds_802_11_set_wep { - struct cmd_header hdr; - - /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ - __le16 action; - - /* key Index selected for Tx */ - __le16 keyindex; - - /* 40, 128bit or TXWEP */ - uint8_t keytype[4]; - uint8_t keymaterial[4][16]; -} __packed; - -struct cmd_ds_802_11_snmp_mib { - struct cmd_header hdr; - - __le16 action; - __le16 oid; - __le16 bufsize; - u8 value[128]; -} __packed; - -struct cmd_ds_reg_access { - struct cmd_header hdr; - - __le16 action; - __le16 offset; - union { - u8 bbp_rf; /* for BBP and RF registers */ - __le32 mac; /* for MAC registers */ - } value; -} __packed; - -struct cmd_ds_802_11_radio_control { - struct cmd_header hdr; - - __le16 action; - __le16 control; -} __packed; - -struct cmd_ds_802_11_beacon_control { - struct cmd_header hdr; - - __le16 action; - __le16 beacon_enable; - __le16 beacon_period; -} __packed; - -struct cmd_ds_802_11_sleep_params { - struct cmd_header hdr; - - /* ACT_GET/ACT_SET */ - __le16 action; - - /* Sleep clock error in ppm */ - __le16 error; - - /* Wakeup offset in usec */ - __le16 offset; - - /* Clock stabilization time in usec */ - __le16 stabletime; - - /* control periodic calibration */ - uint8_t calcontrol; - - /* control the use of external sleep clock */ - uint8_t externalsleepclk; - - /* reserved field, should be set to zero */ - __le16 reserved; -} __packed; - -struct cmd_ds_802_11_rf_channel { - struct cmd_header hdr; - - __le16 action; - __le16 channel; - __le16 rftype; /* unused */ - __le16 reserved; /* unused */ - u8 channellist[32]; /* unused */ -} __packed; - -struct cmd_ds_802_11_rssi { - struct cmd_header hdr; - - /* - * request: number of beacons (N) to average the SNR and NF over - * response: SNR of most recent beacon - */ - __le16 n_or_snr; - - /* - * The following fields are only set in the response. - * In the request these are reserved and should be set to 0. - */ - __le16 nf; /* most recent beacon noise floor */ - __le16 avg_snr; /* average SNR weighted by N from request */ - __le16 avg_nf; /* average noise floor weighted by N from request */ -} __packed; - -struct cmd_ds_802_11_mac_address { - struct cmd_header hdr; - - __le16 action; - u8 macadd[ETH_ALEN]; -} __packed; - -struct cmd_ds_802_11_rf_tx_power { - struct cmd_header hdr; - - __le16 action; - __le16 curlevel; - s8 maxlevel; - s8 minlevel; -} __packed; - -/* MONITOR_MODE only exists in OLPC v5 firmware */ -struct cmd_ds_802_11_monitor_mode { - struct cmd_header hdr; - - __le16 action; - __le16 mode; -} __packed; - -struct cmd_ds_set_boot2_ver { - struct cmd_header hdr; - - __le16 action; - __le16 version; -} __packed; - -struct cmd_ds_802_11_fw_wake_method { - struct cmd_header hdr; - - __le16 action; - __le16 method; -} __packed; - -struct cmd_ds_802_11_ps_mode { - struct cmd_header hdr; - - __le16 action; - - /* - * Interval for keepalive in PS mode: - * 0x0000 = don't change - * 0x001E = firmware default - * 0xFFFF = disable - */ - __le16 nullpktinterval; - - /* - * Number of DTIM intervals to wake up for: - * 0 = don't change - * 1 = firmware default - * 5 = max - */ - __le16 multipledtim; - - __le16 reserved; - __le16 locallisteninterval; - - /* - * AdHoc awake period (FW v9+ only): - * 0 = don't change - * 1 = always awake (IEEE standard behavior) - * 2 - 31 = sleep for (n - 1) periods and awake for 1 period - * 32 - 254 = invalid - * 255 = sleep at each ATIM - */ - __le16 adhoc_awake_period; -} __packed; - -struct cmd_confirm_sleep { - struct cmd_header hdr; - - __le16 action; - __le16 nullpktinterval; - __le16 multipledtim; - __le16 reserved; - __le16 locallisteninterval; -} __packed; - -struct cmd_ds_802_11_data_rate { - struct cmd_header hdr; - - __le16 action; - __le16 reserved; - u8 rates[MAX_RATES]; -} __packed; - -struct cmd_ds_802_11_rate_adapt_rateset { - struct cmd_header hdr; - __le16 action; - __le16 enablehwauto; - __le16 bitmap; -} __packed; - -struct cmd_ds_802_11_ad_hoc_start { - struct cmd_header hdr; - - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 bsstype; - __le16 beaconperiod; - u8 dtimperiod; /* Reserved on v9 and later */ - struct ieee_ie_ibss_param_set ibss; - u8 reserved1[4]; - struct ieee_ie_ds_param_set ds; - u8 reserved2[4]; - __le16 probedelay; /* Reserved on v9 and later */ - __le16 capability; - u8 rates[MAX_RATES]; - u8 tlv_memory_size_pad[100]; -} __packed; - -struct cmd_ds_802_11_ad_hoc_result { - struct cmd_header hdr; - - u8 pad[3]; - u8 bssid[ETH_ALEN]; -} __packed; - -struct adhoc_bssdesc { - u8 bssid[ETH_ALEN]; - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 type; - __le16 beaconperiod; - u8 dtimperiod; - __le64 timestamp; - __le64 localtime; - struct ieee_ie_ds_param_set ds; - u8 reserved1[4]; - struct ieee_ie_ibss_param_set ibss; - u8 reserved2[4]; - __le16 capability; - u8 rates[MAX_RATES]; - - /* - * DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the - * Adhoc join command and will cause a binary layout mismatch with - * the firmware - */ -} __packed; - -struct cmd_ds_802_11_ad_hoc_join { - struct cmd_header hdr; - - struct adhoc_bssdesc bss; - __le16 failtimeout; /* Reserved on v9 and later */ - __le16 probedelay; /* Reserved on v9 and later */ -} __packed; - -struct cmd_ds_802_11_ad_hoc_stop { - struct cmd_header hdr; -} __packed; - -struct cmd_ds_802_11_enable_rsn { - struct cmd_header hdr; - - __le16 action; - __le16 enable; -} __packed; - -struct MrvlIEtype_keyParamSet { - /* type ID */ - __le16 type; - - /* length of Payload */ - __le16 length; - - /* type of key: WEP=0, TKIP=1, AES=2 */ - __le16 keytypeid; - - /* key control Info specific to a keytypeid */ - __le16 keyinfo; - - /* length of key */ - __le16 keylen; - - /* key material of size keylen */ - u8 key[32]; -} __packed; - -#define MAX_WOL_RULES 16 - -struct host_wol_rule { - uint8_t rule_no; - uint8_t rule_ops; - __le16 sig_offset; - __le16 sig_length; - __le16 reserve; - __be32 sig_mask; - __be32 signature; -} __packed; - -struct wol_config { - uint8_t action; - uint8_t pattern; - uint8_t no_rules_in_cmd; - uint8_t result; - struct host_wol_rule rule[MAX_WOL_RULES]; -} __packed; - -struct cmd_ds_host_sleep { - struct cmd_header hdr; - __le32 criteria; - uint8_t gpio; - uint16_t gap; - struct wol_config wol_conf; -} __packed; - - - -struct cmd_ds_802_11_key_material { - struct cmd_header hdr; - - __le16 action; - struct MrvlIEtype_keyParamSet keyParamSet[2]; -} __packed; - -struct cmd_ds_802_11_eeprom_access { - struct cmd_header hdr; - __le16 action; - __le16 offset; - __le16 len; - /* firmware says it returns a maximum of 20 bytes */ -#define LBS_EEPROM_READ_LEN 20 - u8 value[LBS_EEPROM_READ_LEN]; -} __packed; - -struct cmd_ds_802_11_tpc_cfg { - struct cmd_header hdr; - - __le16 action; - uint8_t enable; - int8_t P0; - int8_t P1; - int8_t P2; - uint8_t usesnr; -} __packed; - - -struct cmd_ds_802_11_pa_cfg { - struct cmd_header hdr; - - __le16 action; - uint8_t enable; - int8_t P0; - int8_t P1; - int8_t P2; -} __packed; - - -struct cmd_ds_802_11_led_ctrl { - struct cmd_header hdr; - - __le16 action; - __le16 numled; - u8 data[256]; -} __packed; - -/* Automatic Frequency Control */ -struct cmd_ds_802_11_afc { - struct cmd_header hdr; - - __le16 afc_auto; - union { - struct { - __le16 threshold; - __le16 period; - }; - struct { - __le16 timing_offset; /* signed */ - __le16 carrier_offset; /* signed */ - }; - }; -} __packed; - -struct cmd_tx_rate_query { - __le16 txrate; -} __packed; - -struct cmd_ds_get_tsf { - __le64 tsfvalue; -} __packed; - -struct cmd_ds_bt_access { - struct cmd_header hdr; - - __le16 action; - __le32 id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; -} __packed; - -struct cmd_ds_fwt_access { - struct cmd_header hdr; - - __le16 action; - __le32 id; - u8 valid; - u8 da[ETH_ALEN]; - u8 dir; - u8 ra[ETH_ALEN]; - __le32 ssn; - __le32 dsn; - __le32 metric; - u8 rate; - u8 hopcount; - u8 ttl; - __le32 expiration; - u8 sleepmode; - __le32 snr; - __le32 references; - u8 prec[ETH_ALEN]; -} __packed; - -struct cmd_ds_mesh_config { - struct cmd_header hdr; - - __le16 action; - __le16 channel; - __le16 type; - __le16 length; - u8 data[128]; /* last position reserved */ -} __packed; - -struct cmd_ds_mesh_access { - struct cmd_header hdr; - - __le16 action; - __le32 data[32]; /* last position reserved */ -} __packed; - -/* Number of stats counters returned by the firmware */ -#define MESH_STATS_NUM 8 -#endif diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c deleted file mode 100644 index f499efc6abcf..000000000000 --- a/drivers/net/wireless/libertas/if_cs.c +++ /dev/null @@ -1,1006 +0,0 @@ -/* - - Driver for the Marvell 8385 based compact flash WLAN cards. - - (C) 2007 by Holger Schurig - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#define DRV_NAME "libertas_cs" - -#include "decl.h" -#include "defs.h" -#include "dev.h" - - -/********************************************************************/ -/* Module stuff */ -/********************************************************************/ - -MODULE_AUTHOR("Holger Schurig "); -MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards"); -MODULE_LICENSE("GPL"); - - - -/********************************************************************/ -/* Data structures */ -/********************************************************************/ - -struct if_cs_card { - struct pcmcia_device *p_dev; - struct lbs_private *priv; - void __iomem *iobase; - bool align_regs; - u32 model; -}; - - -enum { - MODEL_UNKNOWN = 0x00, - MODEL_8305 = 0x01, - MODEL_8381 = 0x02, - MODEL_8385 = 0x03 -}; - -static const struct lbs_fw_table fw_table[] = { - { MODEL_8305, "libertas/cf8305.bin", NULL }, - { MODEL_8305, "libertas_cs_helper.fw", NULL }, - { MODEL_8381, "libertas/cf8381_helper.bin", "libertas/cf8381.bin" }, - { MODEL_8381, "libertas_cs_helper.fw", "libertas_cs.fw" }, - { MODEL_8385, "libertas/cf8385_helper.bin", "libertas/cf8385.bin" }, - { MODEL_8385, "libertas_cs_helper.fw", "libertas_cs.fw" }, - { 0, NULL, NULL } -}; -MODULE_FIRMWARE("libertas/cf8305.bin"); -MODULE_FIRMWARE("libertas/cf8381_helper.bin"); -MODULE_FIRMWARE("libertas/cf8381.bin"); -MODULE_FIRMWARE("libertas/cf8385_helper.bin"); -MODULE_FIRMWARE("libertas/cf8385.bin"); -MODULE_FIRMWARE("libertas_cs_helper.fw"); -MODULE_FIRMWARE("libertas_cs.fw"); - - -/********************************************************************/ -/* Hardware access */ -/********************************************************************/ - -/* This define enables wrapper functions which allow you - to dump all register accesses. You normally won't this, - except for development */ -/* #define DEBUG_IO */ - -#ifdef DEBUG_IO -static int debug_output = 0; -#else -/* This way the compiler optimizes the printk's away */ -#define debug_output 0 -#endif - -static inline unsigned int if_cs_read8(struct if_cs_card *card, uint reg) -{ - unsigned int val = ioread8(card->iobase + reg); - if (debug_output) - printk(KERN_INFO "inb %08x<%02x\n", reg, val); - return val; -} -static inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg) -{ - unsigned int val = ioread16(card->iobase + reg); - if (debug_output) - printk(KERN_INFO "inw %08x<%04x\n", reg, val); - return val; -} -static inline void if_cs_read16_rep( - struct if_cs_card *card, - uint reg, - void *buf, - unsigned long count) -{ - if (debug_output) - printk(KERN_INFO "insw %08x<(0x%lx words)\n", - reg, count); - ioread16_rep(card->iobase + reg, buf, count); -} - -static inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val) -{ - if (debug_output) - printk(KERN_INFO "outb %08x>%02x\n", reg, val); - iowrite8(val, card->iobase + reg); -} - -static inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val) -{ - if (debug_output) - printk(KERN_INFO "outw %08x>%04x\n", reg, val); - iowrite16(val, card->iobase + reg); -} - -static inline void if_cs_write16_rep( - struct if_cs_card *card, - uint reg, - const void *buf, - unsigned long count) -{ - if (debug_output) - printk(KERN_INFO "outsw %08x>(0x%lx words)\n", - reg, count); - iowrite16_rep(card->iobase + reg, buf, count); -} - - -/* - * I know that polling/delaying is frowned upon. However, this procedure - * with polling is needed while downloading the firmware. At this stage, - * the hardware does unfortunately not create any interrupts. - * - * Fortunately, this function is never used once the firmware is in - * the card. :-) - * - * As a reference, see the "Firmware Specification v5.1", page 18 - * and 19. I did not follow their suggested timing to the word, - * but this works nice & fast anyway. - */ -static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 reg) -{ - int i; - - for (i = 0; i < 100000; i++) { - u8 val = if_cs_read8(card, addr); - if (val == reg) - return 0; - udelay(5); - } - return -ETIME; -} - - - -/* - * First the bitmasks for the host/card interrupt/status registers: - */ -#define IF_CS_BIT_TX 0x0001 -#define IF_CS_BIT_RX 0x0002 -#define IF_CS_BIT_COMMAND 0x0004 -#define IF_CS_BIT_RESP 0x0008 -#define IF_CS_BIT_EVENT 0x0010 -#define IF_CS_BIT_MASK 0x001f - - - -/* - * It's not really clear to me what the host status register is for. It - * needs to be set almost in union with "host int cause". The following - * bits from above are used: - * - * IF_CS_BIT_TX driver downloaded a data packet - * IF_CS_BIT_RX driver got a data packet - * IF_CS_BIT_COMMAND driver downloaded a command - * IF_CS_BIT_RESP not used (has some meaning with powerdown) - * IF_CS_BIT_EVENT driver read a host event - */ -#define IF_CS_HOST_STATUS 0x00000000 - -/* - * With the host int cause register can the host (that is, Linux) cause - * an interrupt in the firmware, to tell the firmware about those events: - * - * IF_CS_BIT_TX a data packet has been downloaded - * IF_CS_BIT_RX a received data packet has retrieved - * IF_CS_BIT_COMMAND a firmware block or a command has been downloaded - * IF_CS_BIT_RESP not used (has some meaning with powerdown) - * IF_CS_BIT_EVENT a host event (link lost etc) has been retrieved - */ -#define IF_CS_HOST_INT_CAUSE 0x00000002 - -/* - * The host int mask register is used to enable/disable interrupt. However, - * I have the suspicion that disabled interrupts are lost. - */ -#define IF_CS_HOST_INT_MASK 0x00000004 - -/* - * Used to send or receive data packets: - */ -#define IF_CS_WRITE 0x00000016 -#define IF_CS_WRITE_LEN 0x00000014 -#define IF_CS_READ 0x00000010 -#define IF_CS_READ_LEN 0x00000024 - -/* - * Used to send commands (and to send firmware block) and to - * receive command responses: - */ -#define IF_CS_CMD 0x0000001A -#define IF_CS_CMD_LEN 0x00000018 -#define IF_CS_RESP 0x00000012 -#define IF_CS_RESP_LEN 0x00000030 - -/* - * The card status registers shows what the card/firmware actually - * accepts: - * - * IF_CS_BIT_TX you may send a data packet - * IF_CS_BIT_RX you may retrieve a data packet - * IF_CS_BIT_COMMAND you may send a command - * IF_CS_BIT_RESP you may retrieve a command response - * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) - * - * When reading this register several times, you will get back the same - * results --- with one exception: the IF_CS_BIT_EVENT clear itself - * automatically. - * - * Not that we don't rely on BIT_RX,_BIT_RESP or BIT_EVENT because - * we handle this via the card int cause register. - */ -#define IF_CS_CARD_STATUS 0x00000020 -#define IF_CS_CARD_STATUS_MASK 0x7f00 - -/* - * The card int cause register is used by the card/firmware to notify us - * about the following events: - * - * IF_CS_BIT_TX a data packet has successfully been sentx - * IF_CS_BIT_RX a data packet has been received and can be retrieved - * IF_CS_BIT_COMMAND not used - * IF_CS_BIT_RESP the firmware has a command response for us - * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) - */ -#define IF_CS_CARD_INT_CAUSE 0x00000022 - -/* - * This is used to for handshaking with the card's bootloader/helper image - * to synchronize downloading of firmware blocks. - */ -#define IF_CS_SQ_READ_LOW 0x00000028 -#define IF_CS_SQ_HELPER_OK 0x10 - -/* - * The scratch register tells us ... - * - * IF_CS_SCRATCH_BOOT_OK the bootloader runs - * IF_CS_SCRATCH_HELPER_OK the helper firmware already runs - */ -#define IF_CS_SCRATCH 0x0000003F -#define IF_CS_SCRATCH_BOOT_OK 0x00 -#define IF_CS_SCRATCH_HELPER_OK 0x5a - -/* - * Used to detect ancient chips: - */ -#define IF_CS_PRODUCT_ID 0x0000001C -#define IF_CS_CF8385_B1_REV 0x12 -#define IF_CS_CF8381_B3_REV 0x04 -#define IF_CS_CF8305_B1_REV 0x03 - -/* - * Used to detect other cards than CF8385 since their revisions of silicon - * doesn't match those from CF8385, eg. CF8381 B3 works with this driver. - */ -#define CF8305_MANFID 0x02db -#define CF8305_CARDID 0x8103 -#define CF8381_MANFID 0x02db -#define CF8381_CARDID 0x6064 -#define CF8385_MANFID 0x02df -#define CF8385_CARDID 0x8103 - -/* - * FIXME: just use the 'driver_info' field of 'struct pcmcia_device_id' when - * that gets fixed. Currently there's no way to access it from the probe hook. - */ -static inline u32 get_model(u16 manf_id, u16 card_id) -{ - /* NOTE: keep in sync with if_cs_ids */ - if (manf_id == CF8305_MANFID && card_id == CF8305_CARDID) - return MODEL_8305; - else if (manf_id == CF8381_MANFID && card_id == CF8381_CARDID) - return MODEL_8381; - else if (manf_id == CF8385_MANFID && card_id == CF8385_CARDID) - return MODEL_8385; - return MODEL_UNKNOWN; -} - -/********************************************************************/ -/* I/O and interrupt handling */ -/********************************************************************/ - -static inline void if_cs_enable_ints(struct if_cs_card *card) -{ - lbs_deb_enter(LBS_DEB_CS); - if_cs_write16(card, IF_CS_HOST_INT_MASK, 0); -} - -static inline void if_cs_disable_ints(struct if_cs_card *card) -{ - lbs_deb_enter(LBS_DEB_CS); - if_cs_write16(card, IF_CS_HOST_INT_MASK, IF_CS_BIT_MASK); -} - -/* - * Called from if_cs_host_to_card to send a command to the hardware - */ -static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb) -{ - struct if_cs_card *card = (struct if_cs_card *)priv->card; - int ret = -1; - int loops = 0; - - lbs_deb_enter(LBS_DEB_CS); - if_cs_disable_ints(card); - - /* Is hardware ready? */ - while (1) { - u16 status = if_cs_read16(card, IF_CS_CARD_STATUS); - if (status & IF_CS_BIT_COMMAND) - break; - if (++loops > 100) { - netdev_err(priv->dev, "card not ready for commands\n"); - goto done; - } - mdelay(1); - } - - if_cs_write16(card, IF_CS_CMD_LEN, nb); - - if_cs_write16_rep(card, IF_CS_CMD, buf, nb / 2); - /* Are we supposed to transfer an odd amount of bytes? */ - if (nb & 1) - if_cs_write8(card, IF_CS_CMD, buf[nb-1]); - - /* "Assert the download over interrupt command in the Host - * status register" */ - if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); - - /* "Assert the download over interrupt command in the Card - * interrupt case register" */ - if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); - ret = 0; - -done: - if_cs_enable_ints(card); - lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); - return ret; -} - -/* - * Called from if_cs_host_to_card to send a data to the hardware - */ -static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb) -{ - struct if_cs_card *card = (struct if_cs_card *)priv->card; - u16 status; - - lbs_deb_enter(LBS_DEB_CS); - if_cs_disable_ints(card); - - status = if_cs_read16(card, IF_CS_CARD_STATUS); - BUG_ON((status & IF_CS_BIT_TX) == 0); - - if_cs_write16(card, IF_CS_WRITE_LEN, nb); - - /* write even number of bytes, then odd byte if necessary */ - if_cs_write16_rep(card, IF_CS_WRITE, buf, nb / 2); - if (nb & 1) - if_cs_write8(card, IF_CS_WRITE, buf[nb-1]); - - if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_TX); - if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_TX); - if_cs_enable_ints(card); - - lbs_deb_leave(LBS_DEB_CS); -} - -/* - * Get the command result out of the card. - */ -static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len) -{ - unsigned long flags; - int ret = -1; - u16 status; - - lbs_deb_enter(LBS_DEB_CS); - - /* is hardware ready? */ - status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); - if ((status & IF_CS_BIT_RESP) == 0) { - netdev_err(priv->dev, "no cmd response in card\n"); - *len = 0; - goto out; - } - - *len = if_cs_read16(priv->card, IF_CS_RESP_LEN); - if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) { - netdev_err(priv->dev, - "card cmd buffer has invalid # of bytes (%d)\n", - *len); - goto out; - } - - /* read even number of bytes, then odd byte if necessary */ - if_cs_read16_rep(priv->card, IF_CS_RESP, data, *len/sizeof(u16)); - if (*len & 1) - data[*len-1] = if_cs_read8(priv->card, IF_CS_RESP); - - /* This is a workaround for a firmware that reports too much - * bytes */ - *len -= 8; - ret = 0; - - /* Clear this flag again */ - spin_lock_irqsave(&priv->driver_lock, flags); - priv->dnld_sent = DNLD_RES_RECEIVED; - spin_unlock_irqrestore(&priv->driver_lock, flags); - -out: - lbs_deb_leave_args(LBS_DEB_CS, "ret %d, len %d", ret, *len); - return ret; -} - -static struct sk_buff *if_cs_receive_data(struct lbs_private *priv) -{ - struct sk_buff *skb = NULL; - u16 len; - u8 *data; - - lbs_deb_enter(LBS_DEB_CS); - - len = if_cs_read16(priv->card, IF_CS_READ_LEN); - if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { - netdev_err(priv->dev, - "card data buffer has invalid # of bytes (%d)\n", - len); - priv->dev->stats.rx_dropped++; - goto dat_err; - } - - skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2); - if (!skb) - goto out; - skb_put(skb, len); - skb_reserve(skb, 2);/* 16 byte align */ - data = skb->data; - - /* read even number of bytes, then odd byte if necessary */ - if_cs_read16_rep(priv->card, IF_CS_READ, data, len/sizeof(u16)); - if (len & 1) - data[len-1] = if_cs_read8(priv->card, IF_CS_READ); - -dat_err: - if_cs_write16(priv->card, IF_CS_HOST_STATUS, IF_CS_BIT_RX); - if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_RX); - -out: - lbs_deb_leave_args(LBS_DEB_CS, "ret %p", skb); - return skb; -} - -static irqreturn_t if_cs_interrupt(int irq, void *data) -{ - struct if_cs_card *card = data; - struct lbs_private *priv = card->priv; - u16 cause; - - lbs_deb_enter(LBS_DEB_CS); - - /* Ask card interrupt cause register if there is something for us */ - cause = if_cs_read16(card, IF_CS_CARD_INT_CAUSE); - lbs_deb_cs("cause 0x%04x\n", cause); - - if (cause == 0) { - /* Not for us */ - return IRQ_NONE; - } - - if (cause == 0xffff) { - /* Read in junk, the card has probably been removed */ - card->priv->surpriseremoved = 1; - return IRQ_HANDLED; - } - - if (cause & IF_CS_BIT_RX) { - struct sk_buff *skb; - lbs_deb_cs("rx packet\n"); - skb = if_cs_receive_data(priv); - if (skb) - lbs_process_rxed_packet(priv, skb); - } - - if (cause & IF_CS_BIT_TX) { - lbs_deb_cs("tx done\n"); - lbs_host_to_card_done(priv); - } - - if (cause & IF_CS_BIT_RESP) { - unsigned long flags; - u8 i; - - lbs_deb_cs("cmd resp\n"); - spin_lock_irqsave(&priv->driver_lock, flags); - i = (priv->resp_idx == 0) ? 1 : 0; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - BUG_ON(priv->resp_len[i]); - if_cs_receive_cmdres(priv, priv->resp_buf[i], - &priv->resp_len[i]); - - spin_lock_irqsave(&priv->driver_lock, flags); - lbs_notify_command_response(priv, i); - spin_unlock_irqrestore(&priv->driver_lock, flags); - } - - if (cause & IF_CS_BIT_EVENT) { - u16 status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); - if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, - IF_CS_BIT_EVENT); - lbs_queue_event(priv, (status & IF_CS_CARD_STATUS_MASK) >> 8); - } - - /* Clear interrupt cause */ - if_cs_write16(card, IF_CS_CARD_INT_CAUSE, cause & IF_CS_BIT_MASK); - - lbs_deb_leave(LBS_DEB_CS); - return IRQ_HANDLED; -} - - - - -/********************************************************************/ -/* Firmware */ -/********************************************************************/ - -/* - * Tries to program the helper firmware. - * - * Return 0 on success - */ -static int if_cs_prog_helper(struct if_cs_card *card, const struct firmware *fw) -{ - int ret = 0; - int sent = 0; - u8 scratch; - - lbs_deb_enter(LBS_DEB_CS); - - /* - * This is the only place where an unaligned register access happens on - * the CF8305 card, therefore for the sake of speed of the driver, we do - * the alignment correction here. - */ - if (card->align_regs) - scratch = if_cs_read16(card, IF_CS_SCRATCH) >> 8; - else - scratch = if_cs_read8(card, IF_CS_SCRATCH); - - /* "If the value is 0x5a, the firmware is already - * downloaded successfully" - */ - if (scratch == IF_CS_SCRATCH_HELPER_OK) - goto done; - - /* "If the value is != 00, it is invalid value of register */ - if (scratch != IF_CS_SCRATCH_BOOT_OK) { - ret = -ENODEV; - goto done; - } - - lbs_deb_cs("helper size %td\n", fw->size); - - /* "Set the 5 bytes of the helper image to 0" */ - /* Not needed, this contains an ARM branch instruction */ - - for (;;) { - /* "the number of bytes to send is 256" */ - int count = 256; - int remain = fw->size - sent; - - if (remain < count) - count = remain; - - /* - * "write the number of bytes to be sent to the I/O Command - * write length register" - */ - if_cs_write16(card, IF_CS_CMD_LEN, count); - - /* "write this to I/O Command port register as 16 bit writes */ - if (count) - if_cs_write16_rep(card, IF_CS_CMD, - &fw->data[sent], - count >> 1); - - /* - * "Assert the download over interrupt command in the Host - * status register" - */ - if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); - - /* - * "Assert the download over interrupt command in the Card - * interrupt case register" - */ - if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); - - /* - * "The host polls the Card Status register ... for 50 ms before - * declaring a failure" - */ - ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, - IF_CS_BIT_COMMAND); - if (ret < 0) { - pr_err("can't download helper at 0x%x, ret %d\n", - sent, ret); - goto done; - } - - if (count == 0) - break; - - sent += count; - } - -done: - lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); - return ret; -} - - -static int if_cs_prog_real(struct if_cs_card *card, const struct firmware *fw) -{ - int ret = 0; - int retry = 0; - int len = 0; - int sent; - - lbs_deb_enter(LBS_DEB_CS); - - lbs_deb_cs("fw size %td\n", fw->size); - - ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW, - IF_CS_SQ_HELPER_OK); - if (ret < 0) { - pr_err("helper firmware doesn't answer\n"); - goto done; - } - - for (sent = 0; sent < fw->size; sent += len) { - len = if_cs_read16(card, IF_CS_SQ_READ_LOW); - if (len & 1) { - retry++; - pr_info("odd, need to retry this firmware block\n"); - } else { - retry = 0; - } - - if (retry > 20) { - pr_err("could not download firmware\n"); - ret = -ENODEV; - goto done; - } - if (retry) { - sent -= len; - } - - - if_cs_write16(card, IF_CS_CMD_LEN, len); - - if_cs_write16_rep(card, IF_CS_CMD, - &fw->data[sent], - (len+1) >> 1); - if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); - if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); - - ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, - IF_CS_BIT_COMMAND); - if (ret < 0) { - pr_err("can't download firmware at 0x%x\n", sent); - goto done; - } - } - - ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a); - if (ret < 0) - pr_err("firmware download failed\n"); - -done: - lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); - return ret; -} - -static void if_cs_prog_firmware(struct lbs_private *priv, int ret, - const struct firmware *helper, - const struct firmware *mainfw) -{ - struct if_cs_card *card = priv->card; - - if (ret) { - pr_err("failed to find firmware (%d)\n", ret); - return; - } - - /* Load the firmware */ - ret = if_cs_prog_helper(card, helper); - if (ret == 0 && (card->model != MODEL_8305)) - ret = if_cs_prog_real(card, mainfw); - if (ret) - return; - - /* Now actually get the IRQ */ - ret = request_irq(card->p_dev->irq, if_cs_interrupt, - IRQF_SHARED, DRV_NAME, card); - if (ret) { - pr_err("error in request_irq\n"); - return; - } - - /* - * Clear any interrupt cause that happened while sending - * firmware/initializing card - */ - if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK); - if_cs_enable_ints(card); - - /* And finally bring the card up */ - priv->fw_ready = 1; - if (lbs_start_card(priv) != 0) { - pr_err("could not activate card\n"); - free_irq(card->p_dev->irq, card); - } -} - - -/********************************************************************/ -/* Callback functions for libertas.ko */ -/********************************************************************/ - -/* Send commands or data packets to the card */ -static int if_cs_host_to_card(struct lbs_private *priv, - u8 type, - u8 *buf, - u16 nb) -{ - int ret = -1; - - lbs_deb_enter_args(LBS_DEB_CS, "type %d, bytes %d", type, nb); - - switch (type) { - case MVMS_DAT: - priv->dnld_sent = DNLD_DATA_SENT; - if_cs_send_data(priv, buf, nb); - ret = 0; - break; - case MVMS_CMD: - priv->dnld_sent = DNLD_CMD_SENT; - ret = if_cs_send_cmd(priv, buf, nb); - break; - default: - netdev_err(priv->dev, "%s: unsupported type %d\n", - __func__, type); - } - - lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); - return ret; -} - - -static void if_cs_release(struct pcmcia_device *p_dev) -{ - struct if_cs_card *card = p_dev->priv; - - lbs_deb_enter(LBS_DEB_CS); - - free_irq(p_dev->irq, card); - pcmcia_disable_device(p_dev); - if (card->iobase) - ioport_unmap(card->iobase); - - lbs_deb_leave(LBS_DEB_CS); -} - - -static int if_cs_ioprobe(struct pcmcia_device *p_dev, void *priv_data) -{ - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - - if (p_dev->resource[1]->end) { - pr_err("wrong CIS (check number of IO windows)\n"); - return -ENODEV; - } - - /* This reserves IO space but doesn't actually enable it */ - return pcmcia_request_io(p_dev); -} - -static int if_cs_probe(struct pcmcia_device *p_dev) -{ - int ret = -ENOMEM; - unsigned int prod_id; - struct lbs_private *priv; - struct if_cs_card *card; - - lbs_deb_enter(LBS_DEB_CS); - - card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL); - if (!card) - goto out; - - card->p_dev = p_dev; - p_dev->priv = card; - - p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - - if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) { - pr_err("error in pcmcia_loop_config\n"); - goto out1; - } - - /* - * Allocate an interrupt line. Note that this does not assign - * a handler to the interrupt, unless the 'Handler' member of - * the irq structure is initialized. - */ - if (!p_dev->irq) - goto out1; - - /* Initialize io access */ - card->iobase = ioport_map(p_dev->resource[0]->start, - resource_size(p_dev->resource[0])); - if (!card->iobase) { - pr_err("error in ioport_map\n"); - ret = -EIO; - goto out1; - } - - ret = pcmcia_enable_device(p_dev); - if (ret) { - pr_err("error in pcmcia_enable_device\n"); - goto out2; - } - - /* Finally, report what we've done */ - lbs_deb_cs("irq %d, io %pR", p_dev->irq, p_dev->resource[0]); - - /* - * Most of the libertas cards can do unaligned register access, but some - * weird ones cannot. That's especially true for the CF8305 card. - */ - card->align_regs = false; - - card->model = get_model(p_dev->manf_id, p_dev->card_id); - if (card->model == MODEL_UNKNOWN) { - pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n", - p_dev->manf_id, p_dev->card_id); - ret = -ENODEV; - goto out2; - } - - /* Check if we have a current silicon */ - prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID); - if (card->model == MODEL_8305) { - card->align_regs = true; - if (prod_id < IF_CS_CF8305_B1_REV) { - pr_err("8305 rev B0 and older are not supported\n"); - ret = -ENODEV; - goto out2; - } - } - - if ((card->model == MODEL_8381) && prod_id < IF_CS_CF8381_B3_REV) { - pr_err("8381 rev B2 and older are not supported\n"); - ret = -ENODEV; - goto out2; - } - - if ((card->model == MODEL_8385) && prod_id < IF_CS_CF8385_B1_REV) { - pr_err("8385 rev B0 and older are not supported\n"); - ret = -ENODEV; - goto out2; - } - - /* Make this card known to the libertas driver */ - priv = lbs_add_card(card, &p_dev->dev); - if (!priv) { - ret = -ENOMEM; - goto out2; - } - - /* Set up fields in lbs_private */ - card->priv = priv; - priv->card = card; - priv->hw_host_to_card = if_cs_host_to_card; - priv->enter_deep_sleep = NULL; - priv->exit_deep_sleep = NULL; - priv->reset_deep_sleep_wakeup = NULL; - - /* Get firmware */ - ret = lbs_get_firmware_async(priv, &p_dev->dev, card->model, fw_table, - if_cs_prog_firmware); - if (ret) { - pr_err("failed to find firmware (%d)\n", ret); - goto out3; - } - - goto out; - -out3: - lbs_remove_card(priv); -out2: - ioport_unmap(card->iobase); -out1: - pcmcia_disable_device(p_dev); -out: - lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); - return ret; -} - - -static void if_cs_detach(struct pcmcia_device *p_dev) -{ - struct if_cs_card *card = p_dev->priv; - - lbs_deb_enter(LBS_DEB_CS); - - lbs_stop_card(card->priv); - lbs_remove_card(card->priv); - if_cs_disable_ints(card); - if_cs_release(p_dev); - kfree(card); - - lbs_deb_leave(LBS_DEB_CS); -} - - - -/********************************************************************/ -/* Module initialization */ -/********************************************************************/ - -static const struct pcmcia_device_id if_cs_ids[] = { - PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID), - PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID), - PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID), - /* NOTE: keep in sync with get_model() */ - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); - -static struct pcmcia_driver lbs_driver = { - .owner = THIS_MODULE, - .name = DRV_NAME, - .probe = if_cs_probe, - .remove = if_cs_detach, - .id_table = if_cs_ids, -}; -module_pcmcia_driver(lbs_driver); diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c deleted file mode 100644 index 33ceda296c9c..000000000000 --- a/drivers/net/wireless/libertas/if_sdio.c +++ /dev/null @@ -1,1453 +0,0 @@ -/* - * linux/drivers/net/wireless/libertas/if_sdio.c - * - * Copyright 2007-2008 Pierre Ossman - * - * Inspired by if_cs.c, Copyright 2007 Holger Schurig - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This hardware has more or less no CMD53 support, so all registers - * must be accessed using sdio_readb()/sdio_writeb(). - * - * Transfers must be in one transaction or the firmware goes bonkers. - * This means that the transfer must either be small enough to do a - * byte based transfer or it must be padded to a multiple of the - * current block size. - * - * As SDIO is still new to the kernel, it is unfortunately common with - * bugs in the host controllers related to that. One such bug is that - * controllers cannot do transfers that aren't a multiple of 4 bytes. - * If you don't have time to fix the host controller driver, you can - * work around the problem by modifying if_sdio_host_to_card() and - * if_sdio_card_to_host() to pad the data. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "host.h" -#include "decl.h" -#include "defs.h" -#include "dev.h" -#include "cmd.h" -#include "if_sdio.h" - -static void if_sdio_interrupt(struct sdio_func *func); - -/* The if_sdio_remove() callback function is called when - * user removes this module from kernel space or ejects - * the card from the slot. The driver handles these 2 cases - * differently for SD8688 combo chip. - * If the user is removing the module, the FUNC_SHUTDOWN - * command for SD8688 is sent to the firmware. - * If the card is removed, there is no need to send this command. - * - * The variable 'user_rmmod' is used to distinguish these two - * scenarios. This flag is initialized as FALSE in case the card - * is removed, and will be set to TRUE for module removal when - * module_exit function is called. - */ -static u8 user_rmmod; - -static const struct sdio_device_id if_sdio_ids[] = { - { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, - SDIO_DEVICE_ID_MARVELL_LIBERTAS) }, - { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, - SDIO_DEVICE_ID_MARVELL_8688WLAN) }, - { /* end: all zeroes */ }, -}; - -MODULE_DEVICE_TABLE(sdio, if_sdio_ids); - -#define MODEL_8385 0x04 -#define MODEL_8686 0x0b -#define MODEL_8688 0x10 - -static const struct lbs_fw_table fw_table[] = { - { MODEL_8385, "libertas/sd8385_helper.bin", "libertas/sd8385.bin" }, - { MODEL_8385, "sd8385_helper.bin", "sd8385.bin" }, - { MODEL_8686, "libertas/sd8686_v9_helper.bin", "libertas/sd8686_v9.bin" }, - { MODEL_8686, "libertas/sd8686_v8_helper.bin", "libertas/sd8686_v8.bin" }, - { MODEL_8686, "sd8686_helper.bin", "sd8686.bin" }, - { MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" }, - { MODEL_8688, "sd8688_helper.bin", "sd8688.bin" }, - { 0, NULL, NULL } -}; -MODULE_FIRMWARE("libertas/sd8385_helper.bin"); -MODULE_FIRMWARE("libertas/sd8385.bin"); -MODULE_FIRMWARE("sd8385_helper.bin"); -MODULE_FIRMWARE("sd8385.bin"); -MODULE_FIRMWARE("libertas/sd8686_v9_helper.bin"); -MODULE_FIRMWARE("libertas/sd8686_v9.bin"); -MODULE_FIRMWARE("libertas/sd8686_v8_helper.bin"); -MODULE_FIRMWARE("libertas/sd8686_v8.bin"); -MODULE_FIRMWARE("sd8686_helper.bin"); -MODULE_FIRMWARE("sd8686.bin"); -MODULE_FIRMWARE("libertas/sd8688_helper.bin"); -MODULE_FIRMWARE("libertas/sd8688.bin"); -MODULE_FIRMWARE("sd8688_helper.bin"); -MODULE_FIRMWARE("sd8688.bin"); - -struct if_sdio_packet { - struct if_sdio_packet *next; - u16 nb; - u8 buffer[0] __attribute__((aligned(4))); -}; - -struct if_sdio_card { - struct sdio_func *func; - struct lbs_private *priv; - - int model; - unsigned long ioport; - unsigned int scratch_reg; - bool started; - wait_queue_head_t pwron_waitq; - - u8 buffer[65536] __attribute__((aligned(4))); - - spinlock_t lock; - struct if_sdio_packet *packets; - - struct workqueue_struct *workqueue; - struct work_struct packet_worker; - - u8 rx_unit; -}; - -static void if_sdio_finish_power_on(struct if_sdio_card *card); -static int if_sdio_power_off(struct if_sdio_card *card); - -/********************************************************************/ -/* I/O */ -/********************************************************************/ - -/* - * For SD8385/SD8686, this function reads firmware status after - * the image is downloaded, or reads RX packet length when - * interrupt (with IF_SDIO_H_INT_UPLD bit set) is received. - * For SD8688, this function reads firmware status only. - */ -static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err) -{ - int ret; - u16 scratch; - - scratch = sdio_readb(card->func, card->scratch_reg, &ret); - if (!ret) - scratch |= sdio_readb(card->func, card->scratch_reg + 1, - &ret) << 8; - - if (err) - *err = ret; - - if (ret) - return 0xffff; - - return scratch; -} - -static u8 if_sdio_read_rx_unit(struct if_sdio_card *card) -{ - int ret; - u8 rx_unit; - - rx_unit = sdio_readb(card->func, IF_SDIO_RX_UNIT, &ret); - - if (ret) - rx_unit = 0; - - return rx_unit; -} - -static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err) -{ - int ret; - u16 rx_len; - - switch (card->model) { - case MODEL_8385: - case MODEL_8686: - rx_len = if_sdio_read_scratch(card, &ret); - break; - case MODEL_8688: - default: /* for newer chipsets */ - rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret); - if (!ret) - rx_len <<= card->rx_unit; - else - rx_len = 0xffff; /* invalid length */ - - break; - } - - if (err) - *err = ret; - - return rx_len; -} - -static int if_sdio_handle_cmd(struct if_sdio_card *card, - u8 *buffer, unsigned size) -{ - struct lbs_private *priv = card->priv; - int ret; - unsigned long flags; - u8 i; - - lbs_deb_enter(LBS_DEB_SDIO); - - if (size > LBS_CMD_BUFFER_SIZE) { - lbs_deb_sdio("response packet too large (%d bytes)\n", - (int)size); - ret = -E2BIG; - goto out; - } - - spin_lock_irqsave(&priv->driver_lock, flags); - - i = (priv->resp_idx == 0) ? 1 : 0; - BUG_ON(priv->resp_len[i]); - priv->resp_len[i] = size; - memcpy(priv->resp_buf[i], buffer, size); - lbs_notify_command_response(priv, i); - - spin_unlock_irqrestore(&card->priv->driver_lock, flags); - - ret = 0; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; -} - -static int if_sdio_handle_data(struct if_sdio_card *card, - u8 *buffer, unsigned size) -{ - int ret; - struct sk_buff *skb; - char *data; - - lbs_deb_enter(LBS_DEB_SDIO); - - if (size > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { - lbs_deb_sdio("response packet too large (%d bytes)\n", - (int)size); - ret = -E2BIG; - goto out; - } - - skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + NET_IP_ALIGN); - if (!skb) { - ret = -ENOMEM; - goto out; - } - - skb_reserve(skb, NET_IP_ALIGN); - - data = skb_put(skb, size); - - memcpy(data, buffer, size); - - lbs_process_rxed_packet(card->priv, skb); - - ret = 0; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; -} - -static int if_sdio_handle_event(struct if_sdio_card *card, - u8 *buffer, unsigned size) -{ - int ret; - u32 event; - - lbs_deb_enter(LBS_DEB_SDIO); - - if (card->model == MODEL_8385) { - event = sdio_readb(card->func, IF_SDIO_EVENT, &ret); - if (ret) - goto out; - - /* right shift 3 bits to get the event id */ - event >>= 3; - } else { - if (size < 4) { - lbs_deb_sdio("event packet too small (%d bytes)\n", - (int)size); - ret = -EINVAL; - goto out; - } - event = buffer[3] << 24; - event |= buffer[2] << 16; - event |= buffer[1] << 8; - event |= buffer[0] << 0; - } - - lbs_queue_event(card->priv, event & 0xFF); - ret = 0; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; -} - -static int if_sdio_wait_status(struct if_sdio_card *card, const u8 condition) -{ - u8 status; - unsigned long timeout; - int ret = 0; - - timeout = jiffies + HZ; - while (1) { - status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); - if (ret) - return ret; - if ((status & condition) == condition) - break; - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - mdelay(1); - } - return ret; -} - -static int if_sdio_card_to_host(struct if_sdio_card *card) -{ - int ret; - u16 size, type, chunk; - - lbs_deb_enter(LBS_DEB_SDIO); - - size = if_sdio_read_rx_len(card, &ret); - if (ret) - goto out; - - if (size < 4) { - lbs_deb_sdio("invalid packet size (%d bytes) from firmware\n", - (int)size); - ret = -EINVAL; - goto out; - } - - ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); - if (ret) - goto out; - - /* - * The transfer must be in one transaction or the firmware - * goes suicidal. There's no way to guarantee that for all - * controllers, but we can at least try. - */ - chunk = sdio_align_size(card->func, size); - - ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk); - if (ret) - goto out; - - chunk = card->buffer[0] | (card->buffer[1] << 8); - type = card->buffer[2] | (card->buffer[3] << 8); - - lbs_deb_sdio("packet of type %d and size %d bytes\n", - (int)type, (int)chunk); - - if (chunk > size) { - lbs_deb_sdio("packet fragment (%d > %d)\n", - (int)chunk, (int)size); - ret = -EINVAL; - goto out; - } - - if (chunk < size) { - lbs_deb_sdio("packet fragment (%d < %d)\n", - (int)chunk, (int)size); - } - - switch (type) { - case MVMS_CMD: - ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4); - if (ret) - goto out; - break; - case MVMS_DAT: - ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4); - if (ret) - goto out; - break; - case MVMS_EVENT: - ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4); - if (ret) - goto out; - break; - default: - lbs_deb_sdio("invalid type (%d) from firmware\n", - (int)type); - ret = -EINVAL; - goto out; - } - -out: - if (ret) - pr_err("problem fetching packet from firmware\n"); - - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; -} - -static void if_sdio_host_to_card_worker(struct work_struct *work) -{ - struct if_sdio_card *card; - struct if_sdio_packet *packet; - int ret; - unsigned long flags; - - lbs_deb_enter(LBS_DEB_SDIO); - - card = container_of(work, struct if_sdio_card, packet_worker); - - while (1) { - spin_lock_irqsave(&card->lock, flags); - packet = card->packets; - if (packet) - card->packets = packet->next; - spin_unlock_irqrestore(&card->lock, flags); - - if (!packet) - break; - - sdio_claim_host(card->func); - - ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); - if (ret == 0) { - ret = sdio_writesb(card->func, card->ioport, - packet->buffer, packet->nb); - } - - if (ret) - pr_err("error %d sending packet to firmware\n", ret); - - sdio_release_host(card->func); - - kfree(packet); - } - - lbs_deb_leave(LBS_DEB_SDIO); -} - -/********************************************************************/ -/* Firmware */ -/********************************************************************/ - -#define FW_DL_READY_STATUS (IF_SDIO_IO_RDY | IF_SDIO_DL_RDY) - -static int if_sdio_prog_helper(struct if_sdio_card *card, - const struct firmware *fw) -{ - int ret; - unsigned long timeout; - u8 *chunk_buffer; - u32 chunk_size; - const u8 *firmware; - size_t size; - - lbs_deb_enter(LBS_DEB_SDIO); - - chunk_buffer = kzalloc(64, GFP_KERNEL); - if (!chunk_buffer) { - ret = -ENOMEM; - goto out; - } - - sdio_claim_host(card->func); - - ret = sdio_set_block_size(card->func, 32); - if (ret) - goto release; - - firmware = fw->data; - size = fw->size; - - while (size) { - ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); - if (ret) - goto release; - - /* On some platforms (like Davinci) the chip needs more time - * between helper blocks. - */ - mdelay(2); - - chunk_size = min_t(size_t, size, 60); - - *((__le32*)chunk_buffer) = cpu_to_le32(chunk_size); - memcpy(chunk_buffer + 4, firmware, chunk_size); -/* - lbs_deb_sdio("sending %d bytes chunk\n", chunk_size); -*/ - ret = sdio_writesb(card->func, card->ioport, - chunk_buffer, 64); - if (ret) - goto release; - - firmware += chunk_size; - size -= chunk_size; - } - - /* an empty block marks the end of the transfer */ - memset(chunk_buffer, 0, 4); - ret = sdio_writesb(card->func, card->ioport, chunk_buffer, 64); - if (ret) - goto release; - - lbs_deb_sdio("waiting for helper to boot...\n"); - - /* wait for the helper to boot by looking at the size register */ - timeout = jiffies + HZ; - while (1) { - u16 req_size; - - req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret); - if (ret) - goto release; - - req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8; - if (ret) - goto release; - - if (req_size != 0) - break; - - if (time_after(jiffies, timeout)) { - ret = -ETIMEDOUT; - goto release; - } - - msleep(10); - } - - ret = 0; - -release: - sdio_release_host(card->func); - kfree(chunk_buffer); - -out: - if (ret) - pr_err("failed to load helper firmware\n"); - - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; -} - -static int if_sdio_prog_real(struct if_sdio_card *card, - const struct firmware *fw) -{ - int ret; - unsigned long timeout; - u8 *chunk_buffer; - u32 chunk_size; - const u8 *firmware; - size_t size, req_size; - - lbs_deb_enter(LBS_DEB_SDIO); - - chunk_buffer = kzalloc(512, GFP_KERNEL); - if (!chunk_buffer) { - ret = -ENOMEM; - goto out; - } - - sdio_claim_host(card->func); - - ret = sdio_set_block_size(card->func, 32); - if (ret) - goto release; - - firmware = fw->data; - size = fw->size; - - while (size) { - timeout = jiffies + HZ; - while (1) { - ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); - if (ret) - goto release; - - req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, - &ret); - if (ret) - goto release; - - req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, - &ret) << 8; - if (ret) - goto release; - - /* - * For SD8688 wait until the length is not 0, 1 or 2 - * before downloading the first FW block, - * since BOOT code writes the register to indicate the - * helper/FW download winner, - * the value could be 1 or 2 (Func1 or Func2). - */ - if ((size != fw->size) || (req_size > 2)) - break; - if (time_after(jiffies, timeout)) { - ret = -ETIMEDOUT; - goto release; - } - mdelay(1); - } - -/* - lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size); -*/ - if (req_size == 0) { - lbs_deb_sdio("firmware helper gave up early\n"); - ret = -EIO; - goto release; - } - - if (req_size & 0x01) { - lbs_deb_sdio("firmware helper signalled error\n"); - ret = -EIO; - goto release; - } - - if (req_size > size) - req_size = size; - - while (req_size) { - chunk_size = min_t(size_t, req_size, 512); - - memcpy(chunk_buffer, firmware, chunk_size); -/* - lbs_deb_sdio("sending %d bytes (%d bytes) chunk\n", - chunk_size, (chunk_size + 31) / 32 * 32); -*/ - ret = sdio_writesb(card->func, card->ioport, - chunk_buffer, roundup(chunk_size, 32)); - if (ret) - goto release; - - firmware += chunk_size; - size -= chunk_size; - req_size -= chunk_size; - } - } - - ret = 0; - - lbs_deb_sdio("waiting for firmware to boot...\n"); - - /* wait for the firmware to boot */ - timeout = jiffies + HZ; - while (1) { - u16 scratch; - - scratch = if_sdio_read_scratch(card, &ret); - if (ret) - goto release; - - if (scratch == IF_SDIO_FIRMWARE_OK) - break; - - if (time_after(jiffies, timeout)) { - ret = -ETIMEDOUT; - goto release; - } - - msleep(10); - } - - ret = 0; - -release: - sdio_release_host(card->func); - kfree(chunk_buffer); - -out: - if (ret) - pr_err("failed to load firmware\n"); - - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; -} - -static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret, - const struct firmware *helper, - const struct firmware *mainfw) -{ - struct if_sdio_card *card = priv->card; - - if (ret) { - pr_err("failed to find firmware (%d)\n", ret); - return; - } - - ret = if_sdio_prog_helper(card, helper); - if (ret) - return; - - lbs_deb_sdio("Helper firmware loaded\n"); - - ret = if_sdio_prog_real(card, mainfw); - if (ret) - return; - - lbs_deb_sdio("Firmware loaded\n"); - if_sdio_finish_power_on(card); -} - -static int if_sdio_prog_firmware(struct if_sdio_card *card) -{ - int ret; - u16 scratch; - - lbs_deb_enter(LBS_DEB_SDIO); - - /* - * Disable interrupts - */ - sdio_claim_host(card->func); - sdio_writeb(card->func, 0x00, IF_SDIO_H_INT_MASK, &ret); - sdio_release_host(card->func); - - sdio_claim_host(card->func); - scratch = if_sdio_read_scratch(card, &ret); - sdio_release_host(card->func); - - lbs_deb_sdio("firmware status = %#x\n", scratch); - lbs_deb_sdio("scratch ret = %d\n", ret); - - if (ret) - goto out; - - - /* - * The manual clearly describes that FEDC is the right code to use - * to detect firmware presence, but for SD8686 it is not that simple. - * Scratch is also used to store the RX packet length, so we lose - * the FEDC value early on. So we use a non-zero check in order - * to validate firmware presence. - * Additionally, the SD8686 in the Gumstix always has the high scratch - * bit set, even when the firmware is not loaded. So we have to - * exclude that from the test. - */ - if (scratch == IF_SDIO_FIRMWARE_OK) { - lbs_deb_sdio("firmware already loaded\n"); - if_sdio_finish_power_on(card); - return 0; - } else if ((card->model == MODEL_8686) && (scratch & 0x7fff)) { - lbs_deb_sdio("firmware may be running\n"); - if_sdio_finish_power_on(card); - return 0; - } - - ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model, - fw_table, if_sdio_do_prog_firmware); - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; -} - -/********************************************************************/ -/* Power management */ -/********************************************************************/ - -/* Finish power on sequence (after firmware is loaded) */ -static void if_sdio_finish_power_on(struct if_sdio_card *card) -{ - struct sdio_func *func = card->func; - struct lbs_private *priv = card->priv; - int ret; - - sdio_claim_host(func); - sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); - - /* - * Get rx_unit if the chip is SD8688 or newer. - * SD8385 & SD8686 do not have rx_unit. - */ - if ((card->model != MODEL_8385) - && (card->model != MODEL_8686)) - card->rx_unit = if_sdio_read_rx_unit(card); - else - card->rx_unit = 0; - - /* - * Set up the interrupt handler late. - * - * If we set it up earlier, the (buggy) hardware generates a spurious - * interrupt, even before the interrupt has been enabled, with - * CCCR_INTx = 0. - * - * We register the interrupt handler late so that we can handle any - * spurious interrupts, and also to avoid generation of that known - * spurious interrupt in the first place. - */ - ret = sdio_claim_irq(func, if_sdio_interrupt); - if (ret) - goto release; - - /* - * Enable interrupts now that everything is set up - */ - sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); - if (ret) - goto release_irq; - - sdio_release_host(func); - - /* Set fw_ready before queuing any commands so that - * lbs_thread won't block from sending them to firmware. - */ - priv->fw_ready = 1; - - /* - * FUNC_INIT is required for SD8688 WLAN/BT multiple functions - */ - if (card->model == MODEL_8688) { - struct cmd_header cmd; - - memset(&cmd, 0, sizeof(cmd)); - - lbs_deb_sdio("send function INIT command\n"); - if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd), - lbs_cmd_copyback, (unsigned long) &cmd)) - netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n"); - } - - wake_up(&card->pwron_waitq); - - if (!card->started) { - ret = lbs_start_card(priv); - if_sdio_power_off(card); - if (ret == 0) { - card->started = true; - /* Tell PM core that we don't need the card to be - * powered now */ - pm_runtime_put(&func->dev); - } - } - - return; - -release_irq: - sdio_release_irq(func); -release: - sdio_release_host(func); -} - -static int if_sdio_power_on(struct if_sdio_card *card) -{ - struct sdio_func *func = card->func; - struct mmc_host *host = func->card->host; - int ret; - - sdio_claim_host(func); - - ret = sdio_enable_func(func); - if (ret) - goto release; - - /* For 1-bit transfers to the 8686 model, we need to enable the - * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0 - * bit to allow access to non-vendor registers. */ - if ((card->model == MODEL_8686) && - (host->caps & MMC_CAP_SDIO_IRQ) && - (host->ios.bus_width == MMC_BUS_WIDTH_1)) { - u8 reg; - - func->card->quirks |= MMC_QUIRK_LENIENT_FN0; - reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret); - if (ret) - goto disable; - - reg |= SDIO_BUS_ECSI; - sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret); - if (ret) - goto disable; - } - - card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); - if (ret) - goto disable; - - card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8; - if (ret) - goto disable; - - card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16; - if (ret) - goto disable; - - sdio_release_host(func); - ret = if_sdio_prog_firmware(card); - if (ret) { - sdio_claim_host(func); - goto disable; - } - - return 0; - -disable: - sdio_disable_func(func); -release: - sdio_release_host(func); - return ret; -} - -static int if_sdio_power_off(struct if_sdio_card *card) -{ - struct sdio_func *func = card->func; - struct lbs_private *priv = card->priv; - - priv->fw_ready = 0; - - sdio_claim_host(func); - sdio_release_irq(func); - sdio_disable_func(func); - sdio_release_host(func); - return 0; -} - - -/*******************************************************************/ -/* Libertas callbacks */ -/*******************************************************************/ - -static int if_sdio_host_to_card(struct lbs_private *priv, - u8 type, u8 *buf, u16 nb) -{ - int ret; - struct if_sdio_card *card; - struct if_sdio_packet *packet, *cur; - u16 size; - unsigned long flags; - - lbs_deb_enter_args(LBS_DEB_SDIO, "type %d, bytes %d", type, nb); - - card = priv->card; - - if (nb > (65536 - sizeof(struct if_sdio_packet) - 4)) { - ret = -EINVAL; - goto out; - } - - /* - * The transfer must be in one transaction or the firmware - * goes suicidal. There's no way to guarantee that for all - * controllers, but we can at least try. - */ - size = sdio_align_size(card->func, nb + 4); - - packet = kzalloc(sizeof(struct if_sdio_packet) + size, - GFP_ATOMIC); - if (!packet) { - ret = -ENOMEM; - goto out; - } - - packet->next = NULL; - packet->nb = size; - - /* - * SDIO specific header. - */ - packet->buffer[0] = (nb + 4) & 0xff; - packet->buffer[1] = ((nb + 4) >> 8) & 0xff; - packet->buffer[2] = type; - packet->buffer[3] = 0; - - memcpy(packet->buffer + 4, buf, nb); - - spin_lock_irqsave(&card->lock, flags); - - if (!card->packets) - card->packets = packet; - else { - cur = card->packets; - while (cur->next) - cur = cur->next; - cur->next = packet; - } - - switch (type) { - case MVMS_CMD: - priv->dnld_sent = DNLD_CMD_SENT; - break; - case MVMS_DAT: - priv->dnld_sent = DNLD_DATA_SENT; - break; - default: - lbs_deb_sdio("unknown packet type %d\n", (int)type); - } - - spin_unlock_irqrestore(&card->lock, flags); - - queue_work(card->workqueue, &card->packet_worker); - - ret = 0; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; -} - -static int if_sdio_enter_deep_sleep(struct lbs_private *priv) -{ - int ret = -1; - struct cmd_header cmd; - - memset(&cmd, 0, sizeof(cmd)); - - lbs_deb_sdio("send DEEP_SLEEP command\n"); - ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd), - lbs_cmd_copyback, (unsigned long) &cmd); - if (ret) - netdev_err(priv->dev, "DEEP_SLEEP cmd failed\n"); - - mdelay(200); - return ret; -} - -static int if_sdio_exit_deep_sleep(struct lbs_private *priv) -{ - struct if_sdio_card *card = priv->card; - int ret = -1; - - lbs_deb_enter(LBS_DEB_SDIO); - sdio_claim_host(card->func); - - sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); - if (ret) - netdev_err(priv->dev, "sdio_writeb failed!\n"); - - sdio_release_host(card->func); - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; -} - -static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv) -{ - struct if_sdio_card *card = priv->card; - int ret = -1; - - lbs_deb_enter(LBS_DEB_SDIO); - sdio_claim_host(card->func); - - sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret); - if (ret) - netdev_err(priv->dev, "sdio_writeb failed!\n"); - - sdio_release_host(card->func); - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; - -} - -static struct mmc_host *reset_host; - -static void if_sdio_reset_card_worker(struct work_struct *work) -{ - /* - * The actual reset operation must be run outside of lbs_thread. This - * is because mmc_remove_host() will cause the device to be instantly - * destroyed, and the libertas driver then needs to end lbs_thread, - * leading to a deadlock. - * - * We run it in a workqueue totally independent from the if_sdio_card - * instance for that reason. - */ - - pr_info("Resetting card..."); - mmc_remove_host(reset_host); - mmc_add_host(reset_host); -} -static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker); - -static void if_sdio_reset_card(struct lbs_private *priv) -{ - struct if_sdio_card *card = priv->card; - - if (work_pending(&card_reset_work)) - return; - - reset_host = card->func->card->host; - schedule_work(&card_reset_work); -} - -static int if_sdio_power_save(struct lbs_private *priv) -{ - struct if_sdio_card *card = priv->card; - int ret; - - flush_workqueue(card->workqueue); - - ret = if_sdio_power_off(card); - - /* Let runtime PM know the card is powered off */ - pm_runtime_put_sync(&card->func->dev); - - return ret; -} - -static int if_sdio_power_restore(struct lbs_private *priv) -{ - struct if_sdio_card *card = priv->card; - int r; - - /* Make sure the card will not be powered off by runtime PM */ - pm_runtime_get_sync(&card->func->dev); - - r = if_sdio_power_on(card); - if (r) - return r; - - wait_event(card->pwron_waitq, priv->fw_ready); - return 0; -} - - -/*******************************************************************/ -/* SDIO callbacks */ -/*******************************************************************/ - -static void if_sdio_interrupt(struct sdio_func *func) -{ - int ret; - struct if_sdio_card *card; - u8 cause; - - lbs_deb_enter(LBS_DEB_SDIO); - - card = sdio_get_drvdata(func); - - cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret); - if (ret || !cause) - goto out; - - lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause); - - sdio_writeb(card->func, ~cause, IF_SDIO_H_INT_STATUS, &ret); - if (ret) - goto out; - - /* - * Ignore the define name, this really means the card has - * successfully received the command. - */ - card->priv->is_activity_detected = 1; - if (cause & IF_SDIO_H_INT_DNLD) - lbs_host_to_card_done(card->priv); - - - if (cause & IF_SDIO_H_INT_UPLD) { - ret = if_sdio_card_to_host(card); - if (ret) - goto out; - } - - ret = 0; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); -} - -static int if_sdio_probe(struct sdio_func *func, - const struct sdio_device_id *id) -{ - struct if_sdio_card *card; - struct lbs_private *priv; - int ret, i; - unsigned int model; - struct if_sdio_packet *packet; - - lbs_deb_enter(LBS_DEB_SDIO); - - for (i = 0;i < func->card->num_info;i++) { - if (sscanf(func->card->info[i], - "802.11 SDIO ID: %x", &model) == 1) - break; - if (sscanf(func->card->info[i], - "ID: %x", &model) == 1) - break; - if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) { - model = MODEL_8385; - break; - } - } - - if (i == func->card->num_info) { - pr_err("unable to identify card model\n"); - return -ENODEV; - } - - card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL); - if (!card) - return -ENOMEM; - - card->func = func; - card->model = model; - - switch (card->model) { - case MODEL_8385: - card->scratch_reg = IF_SDIO_SCRATCH_OLD; - break; - case MODEL_8686: - card->scratch_reg = IF_SDIO_SCRATCH; - break; - case MODEL_8688: - default: /* for newer chipsets */ - card->scratch_reg = IF_SDIO_FW_STATUS; - break; - } - - spin_lock_init(&card->lock); - card->workqueue = create_workqueue("libertas_sdio"); - INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); - init_waitqueue_head(&card->pwron_waitq); - - /* Check if we support this card */ - for (i = 0; i < ARRAY_SIZE(fw_table); i++) { - if (card->model == fw_table[i].model) - break; - } - if (i == ARRAY_SIZE(fw_table)) { - pr_err("unknown card model 0x%x\n", card->model); - ret = -ENODEV; - goto free; - } - - sdio_set_drvdata(func, card); - - lbs_deb_sdio("class = 0x%X, vendor = 0x%X, " - "device = 0x%X, model = 0x%X, ioport = 0x%X\n", - func->class, func->vendor, func->device, - model, (unsigned)card->ioport); - - - priv = lbs_add_card(card, &func->dev); - if (!priv) { - ret = -ENOMEM; - goto free; - } - - card->priv = priv; - - priv->card = card; - priv->hw_host_to_card = if_sdio_host_to_card; - priv->enter_deep_sleep = if_sdio_enter_deep_sleep; - priv->exit_deep_sleep = if_sdio_exit_deep_sleep; - priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; - priv->reset_card = if_sdio_reset_card; - priv->power_save = if_sdio_power_save; - priv->power_restore = if_sdio_power_restore; - - ret = if_sdio_power_on(card); - if (ret) - goto err_activate_card; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; - -err_activate_card: - flush_workqueue(card->workqueue); - lbs_remove_card(priv); -free: - destroy_workqueue(card->workqueue); - while (card->packets) { - packet = card->packets; - card->packets = card->packets->next; - kfree(packet); - } - - kfree(card); - - goto out; -} - -static void if_sdio_remove(struct sdio_func *func) -{ - struct if_sdio_card *card; - struct if_sdio_packet *packet; - - lbs_deb_enter(LBS_DEB_SDIO); - - card = sdio_get_drvdata(func); - - /* Undo decrement done above in if_sdio_probe */ - pm_runtime_get_noresume(&func->dev); - - if (user_rmmod && (card->model == MODEL_8688)) { - /* - * FUNC_SHUTDOWN is required for SD8688 WLAN/BT - * multiple functions - */ - struct cmd_header cmd; - - memset(&cmd, 0, sizeof(cmd)); - - lbs_deb_sdio("send function SHUTDOWN command\n"); - if (__lbs_cmd(card->priv, CMD_FUNC_SHUTDOWN, - &cmd, sizeof(cmd), lbs_cmd_copyback, - (unsigned long) &cmd)) - pr_alert("CMD_FUNC_SHUTDOWN cmd failed\n"); - } - - - lbs_deb_sdio("call remove card\n"); - lbs_stop_card(card->priv); - lbs_remove_card(card->priv); - - flush_workqueue(card->workqueue); - destroy_workqueue(card->workqueue); - - while (card->packets) { - packet = card->packets; - card->packets = card->packets->next; - kfree(packet); - } - - kfree(card); - lbs_deb_leave(LBS_DEB_SDIO); -} - -static int if_sdio_suspend(struct device *dev) -{ - struct sdio_func *func = dev_to_sdio_func(dev); - int ret; - struct if_sdio_card *card = sdio_get_drvdata(func); - - mmc_pm_flag_t flags = sdio_get_host_pm_caps(func); - - /* If we're powered off anyway, just let the mmc layer remove the - * card. */ - if (!lbs_iface_active(card->priv)) - return -ENOSYS; - - dev_info(dev, "%s: suspend: PM flags = 0x%x\n", - sdio_func_id(func), flags); - - /* If we aren't being asked to wake on anything, we should bail out - * and let the SD stack power down the card. - */ - if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) { - dev_info(dev, "Suspend without wake params -- powering down card\n"); - return -ENOSYS; - } - - if (!(flags & MMC_PM_KEEP_POWER)) { - dev_err(dev, "%s: cannot remain alive while host is suspended\n", - sdio_func_id(func)); - return -ENOSYS; - } - - ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); - if (ret) - return ret; - - ret = lbs_suspend(card->priv); - if (ret) - return ret; - - return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); -} - -static int if_sdio_resume(struct device *dev) -{ - struct sdio_func *func = dev_to_sdio_func(dev); - struct if_sdio_card *card = sdio_get_drvdata(func); - int ret; - - dev_info(dev, "%s: resume: we're back\n", sdio_func_id(func)); - - ret = lbs_resume(card->priv); - - return ret; -} - -static const struct dev_pm_ops if_sdio_pm_ops = { - .suspend = if_sdio_suspend, - .resume = if_sdio_resume, -}; - -static struct sdio_driver if_sdio_driver = { - .name = "libertas_sdio", - .id_table = if_sdio_ids, - .probe = if_sdio_probe, - .remove = if_sdio_remove, - .drv = { - .pm = &if_sdio_pm_ops, - }, -}; - -/*******************************************************************/ -/* Module functions */ -/*******************************************************************/ - -static int __init if_sdio_init_module(void) -{ - int ret = 0; - - lbs_deb_enter(LBS_DEB_SDIO); - - printk(KERN_INFO "libertas_sdio: Libertas SDIO driver\n"); - printk(KERN_INFO "libertas_sdio: Copyright Pierre Ossman\n"); - - ret = sdio_register_driver(&if_sdio_driver); - - /* Clear the flag in case user removes the card. */ - user_rmmod = 0; - - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; -} - -static void __exit if_sdio_exit_module(void) -{ - lbs_deb_enter(LBS_DEB_SDIO); - - /* Set the flag as user is removing this module. */ - user_rmmod = 1; - - cancel_work_sync(&card_reset_work); - - sdio_unregister_driver(&if_sdio_driver); - - lbs_deb_leave(LBS_DEB_SDIO); -} - -module_init(if_sdio_init_module); -module_exit(if_sdio_exit_module); - -MODULE_DESCRIPTION("Libertas SDIO WLAN Driver"); -MODULE_AUTHOR("Pierre Ossman"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h deleted file mode 100644 index 62fda3592f67..000000000000 --- a/drivers/net/wireless/libertas/if_sdio.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * linux/drivers/net/wireless/libertas/if_sdio.h - * - * Copyright 2007 Pierre Ossman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ - -#ifndef _LBS_IF_SDIO_H -#define _LBS_IF_SDIO_H - -#define IF_SDIO_IOPORT 0x00 - -#define IF_SDIO_H_INT_MASK 0x04 -#define IF_SDIO_H_INT_OFLOW 0x08 -#define IF_SDIO_H_INT_UFLOW 0x04 -#define IF_SDIO_H_INT_DNLD 0x02 -#define IF_SDIO_H_INT_UPLD 0x01 - -#define IF_SDIO_H_INT_STATUS 0x05 -#define IF_SDIO_H_INT_RSR 0x06 -#define IF_SDIO_H_INT_STATUS2 0x07 - -#define IF_SDIO_RD_BASE 0x10 - -#define IF_SDIO_STATUS 0x20 -#define IF_SDIO_IO_RDY 0x08 -#define IF_SDIO_CIS_RDY 0x04 -#define IF_SDIO_UL_RDY 0x02 -#define IF_SDIO_DL_RDY 0x01 - -#define IF_SDIO_C_INT_MASK 0x24 -#define IF_SDIO_C_INT_STATUS 0x28 -#define IF_SDIO_C_INT_RSR 0x2C - -#define IF_SDIO_SCRATCH 0x34 -#define IF_SDIO_SCRATCH_OLD 0x80fe -#define IF_SDIO_FW_STATUS 0x40 -#define IF_SDIO_FIRMWARE_OK 0xfedc - -#define IF_SDIO_RX_LEN 0x42 -#define IF_SDIO_RX_UNIT 0x43 - -#define IF_SDIO_EVENT 0x80fc - -#define IF_SDIO_BLOCK_SIZE 256 -#define CONFIGURATION_REG 0x03 -#define HOST_POWER_UP (0x1U << 1) -#endif diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c deleted file mode 100644 index 82c0796377aa..000000000000 --- a/drivers/net/wireless/libertas/if_spi.c +++ /dev/null @@ -1,1318 +0,0 @@ -/* - * linux/drivers/net/wireless/libertas/if_spi.c - * - * Driver for Marvell SPI WLAN cards. - * - * Copyright 2008 Analog Devices Inc. - * - * Authors: - * Andrey Yurovsky - * Colin McCabe - * - * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "host.h" -#include "decl.h" -#include "defs.h" -#include "dev.h" -#include "if_spi.h" - -struct if_spi_packet { - struct list_head list; - u16 blen; - u8 buffer[0] __attribute__((aligned(4))); -}; - -struct if_spi_card { - struct spi_device *spi; - struct lbs_private *priv; - struct libertas_spi_platform_data *pdata; - - /* The card ID and card revision, as reported by the hardware. */ - u16 card_id; - u8 card_rev; - - /* The last time that we initiated an SPU operation */ - unsigned long prev_xfer_time; - - int use_dummy_writes; - unsigned long spu_port_delay; - unsigned long spu_reg_delay; - - /* Handles all SPI communication (except for FW load) */ - struct workqueue_struct *workqueue; - struct work_struct packet_work; - struct work_struct resume_work; - - u8 cmd_buffer[IF_SPI_CMD_BUF_SIZE]; - - /* A buffer of incoming packets from libertas core. - * Since we can't sleep in hw_host_to_card, we have to buffer - * them. */ - struct list_head cmd_packet_list; - struct list_head data_packet_list; - - /* Protects cmd_packet_list and data_packet_list */ - spinlock_t buffer_lock; - - /* True is card suspended */ - u8 suspended; -}; - -static void free_if_spi_card(struct if_spi_card *card) -{ - struct list_head *cursor, *next; - struct if_spi_packet *packet; - - list_for_each_safe(cursor, next, &card->cmd_packet_list) { - packet = container_of(cursor, struct if_spi_packet, list); - list_del(&packet->list); - kfree(packet); - } - list_for_each_safe(cursor, next, &card->data_packet_list) { - packet = container_of(cursor, struct if_spi_packet, list); - list_del(&packet->list); - kfree(packet); - } - kfree(card); -} - -#define MODEL_8385 0x04 -#define MODEL_8686 0x0b -#define MODEL_8688 0x10 - -static const struct lbs_fw_table fw_table[] = { - { MODEL_8385, "libertas/gspi8385_helper.bin", "libertas/gspi8385.bin" }, - { MODEL_8385, "libertas/gspi8385_hlp.bin", "libertas/gspi8385.bin" }, - { MODEL_8686, "libertas/gspi8686_v9_helper.bin", "libertas/gspi8686_v9.bin" }, - { MODEL_8686, "libertas/gspi8686_hlp.bin", "libertas/gspi8686.bin" }, - { MODEL_8688, "libertas/gspi8688_helper.bin", "libertas/gspi8688.bin" }, - { 0, NULL, NULL } -}; -MODULE_FIRMWARE("libertas/gspi8385_helper.bin"); -MODULE_FIRMWARE("libertas/gspi8385_hlp.bin"); -MODULE_FIRMWARE("libertas/gspi8385.bin"); -MODULE_FIRMWARE("libertas/gspi8686_v9_helper.bin"); -MODULE_FIRMWARE("libertas/gspi8686_v9.bin"); -MODULE_FIRMWARE("libertas/gspi8686_hlp.bin"); -MODULE_FIRMWARE("libertas/gspi8686.bin"); -MODULE_FIRMWARE("libertas/gspi8688_helper.bin"); -MODULE_FIRMWARE("libertas/gspi8688.bin"); - - -/* - * SPI Interface Unit Routines - * - * The SPU sits between the host and the WLAN module. - * All communication with the firmware is through SPU transactions. - * - * First we have to put a SPU register name on the bus. Then we can - * either read from or write to that register. - * - */ - -static void spu_transaction_init(struct if_spi_card *card) -{ - if (!time_after(jiffies, card->prev_xfer_time + 1)) { - /* Unfortunately, the SPU requires a delay between successive - * transactions. If our last transaction was more than a jiffy - * ago, we have obviously already delayed enough. - * If not, we have to busy-wait to be on the safe side. */ - ndelay(400); - } -} - -static void spu_transaction_finish(struct if_spi_card *card) -{ - card->prev_xfer_time = jiffies; -} - -/* - * Write out a byte buffer to an SPI register, - * using a series of 16-bit transfers. - */ -static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len) -{ - int err = 0; - __le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK); - struct spi_message m; - struct spi_transfer reg_trans; - struct spi_transfer data_trans; - - spi_message_init(&m); - memset(®_trans, 0, sizeof(reg_trans)); - memset(&data_trans, 0, sizeof(data_trans)); - - /* You must give an even number of bytes to the SPU, even if it - * doesn't care about the last one. */ - BUG_ON(len & 0x1); - - spu_transaction_init(card); - - /* write SPU register index */ - reg_trans.tx_buf = ®_out; - reg_trans.len = sizeof(reg_out); - - data_trans.tx_buf = buf; - data_trans.len = len; - - spi_message_add_tail(®_trans, &m); - spi_message_add_tail(&data_trans, &m); - - err = spi_sync(card->spi, &m); - spu_transaction_finish(card); - return err; -} - -static inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val) -{ - __le16 buff; - - buff = cpu_to_le16(val); - return spu_write(card, reg, (u8 *)&buff, sizeof(u16)); -} - -static inline int spu_reg_is_port_reg(u16 reg) -{ - switch (reg) { - case IF_SPI_IO_RDWRPORT_REG: - case IF_SPI_CMD_RDWRPORT_REG: - case IF_SPI_DATA_RDWRPORT_REG: - return 1; - default: - return 0; - } -} - -static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len) -{ - unsigned int delay; - int err = 0; - __le16 reg_out = cpu_to_le16(reg | IF_SPI_READ_OPERATION_MASK); - struct spi_message m; - struct spi_transfer reg_trans; - struct spi_transfer dummy_trans; - struct spi_transfer data_trans; - - /* - * You must take an even number of bytes from the SPU, even if you - * don't care about the last one. - */ - BUG_ON(len & 0x1); - - spu_transaction_init(card); - - spi_message_init(&m); - memset(®_trans, 0, sizeof(reg_trans)); - memset(&dummy_trans, 0, sizeof(dummy_trans)); - memset(&data_trans, 0, sizeof(data_trans)); - - /* write SPU register index */ - reg_trans.tx_buf = ®_out; - reg_trans.len = sizeof(reg_out); - spi_message_add_tail(®_trans, &m); - - delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay : - card->spu_reg_delay; - if (card->use_dummy_writes) { - /* Clock in dummy cycles while the SPU fills the FIFO */ - dummy_trans.len = delay / 8; - spi_message_add_tail(&dummy_trans, &m); - } else { - /* Busy-wait while the SPU fills the FIFO */ - reg_trans.delay_usecs = - DIV_ROUND_UP((100 + (delay * 10)), 1000); - } - - /* read in data */ - data_trans.rx_buf = buf; - data_trans.len = len; - spi_message_add_tail(&data_trans, &m); - - err = spi_sync(card->spi, &m); - spu_transaction_finish(card); - return err; -} - -/* Read 16 bits from an SPI register */ -static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val) -{ - __le16 buf; - int ret; - - ret = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); - if (ret == 0) - *val = le16_to_cpup(&buf); - return ret; -} - -/* - * Read 32 bits from an SPI register. - * The low 16 bits are read first. - */ -static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val) -{ - __le32 buf; - int err; - - err = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); - if (!err) - *val = le32_to_cpup(&buf); - return err; -} - -/* - * Keep reading 16 bits from an SPI register until you get the correct result. - * - * If mask = 0, the correct result is any non-zero number. - * If mask != 0, the correct result is any number where - * number & target_mask == target - * - * Returns -ETIMEDOUT if a second passes without the correct result. - */ -static int spu_wait_for_u16(struct if_spi_card *card, u16 reg, - u16 target_mask, u16 target) -{ - int err; - unsigned long timeout = jiffies + 5*HZ; - while (1) { - u16 val; - err = spu_read_u16(card, reg, &val); - if (err) - return err; - if (target_mask) { - if ((val & target_mask) == target) - return 0; - } else { - if (val) - return 0; - } - udelay(100); - if (time_after(jiffies, timeout)) { - pr_err("%s: timeout with val=%02x, target_mask=%02x, target=%02x\n", - __func__, val, target_mask, target); - return -ETIMEDOUT; - } - } -} - -/* - * Read 16 bits from an SPI register until you receive a specific value. - * Returns -ETIMEDOUT if a 4 tries pass without success. - */ -static int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target) -{ - int err, try; - for (try = 0; try < 4; ++try) { - u32 val = 0; - err = spu_read_u32(card, reg, &val); - if (err) - return err; - if (val == target) - return 0; - mdelay(100); - } - return -ETIMEDOUT; -} - -static int spu_set_interrupt_mode(struct if_spi_card *card, - int suppress_host_int, - int auto_int) -{ - int err = 0; - - /* - * We can suppress a host interrupt by clearing the appropriate - * bit in the "host interrupt status mask" register - */ - if (suppress_host_int) { - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); - if (err) - return err; - } else { - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, - IF_SPI_HISM_TX_DOWNLOAD_RDY | - IF_SPI_HISM_RX_UPLOAD_RDY | - IF_SPI_HISM_CMD_DOWNLOAD_RDY | - IF_SPI_HISM_CARDEVENT | - IF_SPI_HISM_CMD_UPLOAD_RDY); - if (err) - return err; - } - - /* - * If auto-interrupts are on, the completion of certain transactions - * will trigger an interrupt automatically. If auto-interrupts - * are off, we need to set the "Card Interrupt Cause" register to - * trigger a card interrupt. - */ - if (auto_int) { - err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG, - IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO | - IF_SPI_HICT_RX_UPLOAD_OVER_AUTO | - IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO | - IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO); - if (err) - return err; - } else { - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); - if (err) - return err; - } - return err; -} - -static int spu_get_chip_revision(struct if_spi_card *card, - u16 *card_id, u8 *card_rev) -{ - int err = 0; - u32 dev_ctrl; - err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl); - if (err) - return err; - *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl); - *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl); - return err; -} - -static int spu_set_bus_mode(struct if_spi_card *card, u16 mode) -{ - int err = 0; - u16 rval; - /* set bus mode */ - err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode); - if (err) - return err; - /* Check that we were able to read back what we just wrote. */ - err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval); - if (err) - return err; - if ((rval & 0xF) != mode) { - pr_err("Can't read bus mode register\n"); - return -EIO; - } - return 0; -} - -static int spu_init(struct if_spi_card *card, int use_dummy_writes) -{ - int err = 0; - u32 delay; - - /* - * We have to start up in timed delay mode so that we can safely - * read the Delay Read Register. - */ - card->use_dummy_writes = 0; - err = spu_set_bus_mode(card, - IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | - IF_SPI_BUS_MODE_DELAY_METHOD_TIMED | - IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); - if (err) - return err; - card->spu_port_delay = 1000; - card->spu_reg_delay = 1000; - err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay); - if (err) - return err; - card->spu_port_delay = delay & 0x0000ffff; - card->spu_reg_delay = (delay & 0xffff0000) >> 16; - - /* If dummy clock delay mode has been requested, switch to it now */ - if (use_dummy_writes) { - card->use_dummy_writes = 1; - err = spu_set_bus_mode(card, - IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | - IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK | - IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); - if (err) - return err; - } - - lbs_deb_spi("Initialized SPU unit. " - "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n", - card->spu_port_delay, card->spu_reg_delay); - return err; -} - -/* - * Firmware Loading - */ - -static int if_spi_prog_helper_firmware(struct if_spi_card *card, - const struct firmware *firmware) -{ - int err = 0; - int bytes_remaining; - const u8 *fw; - u8 temp[HELPER_FW_LOAD_CHUNK_SZ]; - - lbs_deb_enter(LBS_DEB_SPI); - - err = spu_set_interrupt_mode(card, 1, 0); - if (err) - goto out; - - bytes_remaining = firmware->size; - fw = firmware->data; - - /* Load helper firmware image */ - while (bytes_remaining > 0) { - /* - * Scratch pad 1 should contain the number of bytes we - * want to download to the firmware - */ - err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, - HELPER_FW_LOAD_CHUNK_SZ); - if (err) - goto out; - - err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, - IF_SPI_HIST_CMD_DOWNLOAD_RDY, - IF_SPI_HIST_CMD_DOWNLOAD_RDY); - if (err) - goto out; - - /* - * Feed the data into the command read/write port reg - * in chunks of 64 bytes - */ - memset(temp, 0, sizeof(temp)); - memcpy(temp, fw, - min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ)); - mdelay(10); - err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, - temp, HELPER_FW_LOAD_CHUNK_SZ); - if (err) - goto out; - - /* Interrupt the boot code */ - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); - if (err) - goto out; - err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, - IF_SPI_CIC_CMD_DOWNLOAD_OVER); - if (err) - goto out; - bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ; - fw += HELPER_FW_LOAD_CHUNK_SZ; - } - - /* - * Once the helper / single stage firmware download is complete, - * write 0 to scratch pad 1 and interrupt the - * bootloader. This completes the helper download. - */ - err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK); - if (err) - goto out; - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); - if (err) - goto out; - err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, - IF_SPI_CIC_CMD_DOWNLOAD_OVER); -out: - if (err) - pr_err("failed to load helper firmware (err=%d)\n", err); - lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); - return err; -} - -/* - * Returns the length of the next packet the firmware expects us to send. - * Sets crc_err if the previous transfer had a CRC error. - */ -static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card, - int *crc_err) -{ - u16 len; - int err = 0; - - /* - * wait until the host interrupt status register indicates - * that we are ready to download - */ - err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, - IF_SPI_HIST_CMD_DOWNLOAD_RDY, - IF_SPI_HIST_CMD_DOWNLOAD_RDY); - if (err) { - pr_err("timed out waiting for host_int_status\n"); - return err; - } - - /* Ask the device how many bytes of firmware it wants. */ - err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); - if (err) - return err; - - if (len > IF_SPI_CMD_BUF_SIZE) { - pr_err("firmware load device requested a larger transfer than we are prepared to handle (len = %d)\n", - len); - return -EIO; - } - if (len & 0x1) { - lbs_deb_spi("%s: crc error\n", __func__); - len &= ~0x1; - *crc_err = 1; - } else - *crc_err = 0; - - return len; -} - -static int if_spi_prog_main_firmware(struct if_spi_card *card, - const struct firmware *firmware) -{ - struct lbs_private *priv = card->priv; - int len, prev_len; - int bytes, crc_err = 0, err = 0; - const u8 *fw; - u16 num_crc_errs; - - lbs_deb_enter(LBS_DEB_SPI); - - err = spu_set_interrupt_mode(card, 1, 0); - if (err) - goto out; - - err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0); - if (err) { - netdev_err(priv->dev, - "%s: timed out waiting for initial scratch reg = 0\n", - __func__); - goto out; - } - - num_crc_errs = 0; - prev_len = 0; - bytes = firmware->size; - fw = firmware->data; - while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) { - if (len < 0) { - err = len; - goto out; - } - if (bytes < 0) { - /* - * If there are no more bytes left, we would normally - * expect to have terminated with len = 0 - */ - netdev_err(priv->dev, - "Firmware load wants more bytes than we have to offer.\n"); - break; - } - if (crc_err) { - /* Previous transfer failed. */ - if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) { - pr_err("Too many CRC errors encountered in firmware load.\n"); - err = -EIO; - goto out; - } - } else { - /* Previous transfer succeeded. Advance counters. */ - bytes -= prev_len; - fw += prev_len; - } - if (bytes < len) { - memset(card->cmd_buffer, 0, len); - memcpy(card->cmd_buffer, fw, bytes); - } else - memcpy(card->cmd_buffer, fw, len); - - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); - if (err) - goto out; - err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, - card->cmd_buffer, len); - if (err) - goto out; - err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG , - IF_SPI_CIC_CMD_DOWNLOAD_OVER); - if (err) - goto out; - prev_len = len; - } - if (bytes > prev_len) { - pr_err("firmware load wants fewer bytes than we have to offer\n"); - } - - /* Confirm firmware download */ - err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG, - SUCCESSFUL_FW_DOWNLOAD_MAGIC); - if (err) { - pr_err("failed to confirm the firmware download\n"); - goto out; - } - -out: - if (err) - pr_err("failed to load firmware (err=%d)\n", err); - lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); - return err; -} - -/* - * SPI Transfer Thread - * - * The SPI worker handles all SPI transfers, so there is no need for a lock. - */ - -/* Move a command from the card to the host */ -static int if_spi_c2h_cmd(struct if_spi_card *card) -{ - struct lbs_private *priv = card->priv; - unsigned long flags; - int err = 0; - u16 len; - u8 i; - - /* - * We need a buffer big enough to handle whatever people send to - * hw_host_to_card - */ - BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE); - BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE); - - /* - * It's just annoying if the buffer size isn't a multiple of 4, because - * then we might have len < IF_SPI_CMD_BUF_SIZE but - * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE - */ - BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0); - - lbs_deb_enter(LBS_DEB_SPI); - - /* How many bytes are there to read? */ - err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len); - if (err) - goto out; - if (!len) { - netdev_err(priv->dev, "%s: error: card has no data for host\n", - __func__); - err = -EINVAL; - goto out; - } else if (len > IF_SPI_CMD_BUF_SIZE) { - netdev_err(priv->dev, - "%s: error: response packet too large: %d bytes, but maximum is %d\n", - __func__, len, IF_SPI_CMD_BUF_SIZE); - err = -EINVAL; - goto out; - } - - /* Read the data from the WLAN module into our command buffer */ - err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG, - card->cmd_buffer, ALIGN(len, 4)); - if (err) - goto out; - - spin_lock_irqsave(&priv->driver_lock, flags); - i = (priv->resp_idx == 0) ? 1 : 0; - BUG_ON(priv->resp_len[i]); - priv->resp_len[i] = len; - memcpy(priv->resp_buf[i], card->cmd_buffer, len); - lbs_notify_command_response(priv, i); - spin_unlock_irqrestore(&priv->driver_lock, flags); - -out: - if (err) - netdev_err(priv->dev, "%s: err=%d\n", __func__, err); - lbs_deb_leave(LBS_DEB_SPI); - return err; -} - -/* Move data from the card to the host */ -static int if_spi_c2h_data(struct if_spi_card *card) -{ - struct lbs_private *priv = card->priv; - struct sk_buff *skb; - char *data; - u16 len; - int err = 0; - - lbs_deb_enter(LBS_DEB_SPI); - - /* How many bytes are there to read? */ - err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); - if (err) - goto out; - if (!len) { - netdev_err(priv->dev, "%s: error: card has no data for host\n", - __func__); - err = -EINVAL; - goto out; - } else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { - netdev_err(priv->dev, - "%s: error: card has %d bytes of data, but our maximum skb size is %zu\n", - __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); - err = -EINVAL; - goto out; - } - - /* TODO: should we allocate a smaller skb if we have less data? */ - skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); - if (!skb) { - err = -ENOBUFS; - goto out; - } - skb_reserve(skb, IPFIELD_ALIGN_OFFSET); - data = skb_put(skb, len); - - /* Read the data from the WLAN module into our skb... */ - err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 4)); - if (err) - goto free_skb; - - /* pass the SKB to libertas */ - err = lbs_process_rxed_packet(card->priv, skb); - if (err) - goto free_skb; - - /* success */ - goto out; - -free_skb: - dev_kfree_skb(skb); -out: - if (err) - netdev_err(priv->dev, "%s: err=%d\n", __func__, err); - lbs_deb_leave(LBS_DEB_SPI); - return err; -} - -/* Move data or a command from the host to the card. */ -static void if_spi_h2c(struct if_spi_card *card, - struct if_spi_packet *packet, int type) -{ - struct lbs_private *priv = card->priv; - int err = 0; - u16 int_type, port_reg; - - switch (type) { - case MVMS_DAT: - int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER; - port_reg = IF_SPI_DATA_RDWRPORT_REG; - break; - case MVMS_CMD: - int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER; - port_reg = IF_SPI_CMD_RDWRPORT_REG; - break; - default: - netdev_err(priv->dev, "can't transfer buffer of type %d\n", - type); - err = -EINVAL; - goto out; - } - - /* Write the data to the card */ - err = spu_write(card, port_reg, packet->buffer, packet->blen); - if (err) - goto out; - -out: - kfree(packet); - - if (err) - netdev_err(priv->dev, "%s: error %d\n", __func__, err); -} - -/* Inform the host about a card event */ -static void if_spi_e2h(struct if_spi_card *card) -{ - int err = 0; - u32 cause; - struct lbs_private *priv = card->priv; - - err = spu_read_u32(card, IF_SPI_SCRATCH_3_REG, &cause); - if (err) - goto out; - - /* re-enable the card event interrupt */ - spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, - ~IF_SPI_HICU_CARD_EVENT); - - /* generate a card interrupt */ - spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_HOST_EVENT); - - lbs_queue_event(priv, cause & 0xff); -out: - if (err) - netdev_err(priv->dev, "%s: error %d\n", __func__, err); -} - -static void if_spi_host_to_card_worker(struct work_struct *work) -{ - int err; - struct if_spi_card *card; - u16 hiStatus; - unsigned long flags; - struct if_spi_packet *packet; - struct lbs_private *priv; - - card = container_of(work, struct if_spi_card, packet_work); - priv = card->priv; - - lbs_deb_enter(LBS_DEB_SPI); - - /* - * Read the host interrupt status register to see what we - * can do. - */ - err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG, - &hiStatus); - if (err) { - netdev_err(priv->dev, "I/O error\n"); - goto err; - } - - if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) { - err = if_spi_c2h_cmd(card); - if (err) - goto err; - } - if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) { - err = if_spi_c2h_data(card); - if (err) - goto err; - } - - /* - * workaround: in PS mode, the card does not set the Command - * Download Ready bit, but it sets TX Download Ready. - */ - if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY || - (card->priv->psstate != PS_STATE_FULL_POWER && - (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) { - /* - * This means two things. First of all, - * if there was a previous command sent, the card has - * successfully received it. - * Secondly, it is now ready to download another - * command. - */ - lbs_host_to_card_done(card->priv); - - /* Do we have any command packets from the host to send? */ - packet = NULL; - spin_lock_irqsave(&card->buffer_lock, flags); - if (!list_empty(&card->cmd_packet_list)) { - packet = (struct if_spi_packet *)(card-> - cmd_packet_list.next); - list_del(&packet->list); - } - spin_unlock_irqrestore(&card->buffer_lock, flags); - - if (packet) - if_spi_h2c(card, packet, MVMS_CMD); - } - if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) { - /* Do we have any data packets from the host to send? */ - packet = NULL; - spin_lock_irqsave(&card->buffer_lock, flags); - if (!list_empty(&card->data_packet_list)) { - packet = (struct if_spi_packet *)(card-> - data_packet_list.next); - list_del(&packet->list); - } - spin_unlock_irqrestore(&card->buffer_lock, flags); - - if (packet) - if_spi_h2c(card, packet, MVMS_DAT); - } - if (hiStatus & IF_SPI_HIST_CARD_EVENT) - if_spi_e2h(card); - -err: - if (err) - netdev_err(priv->dev, "%s: got error %d\n", __func__, err); - - lbs_deb_leave(LBS_DEB_SPI); -} - -/* - * Host to Card - * - * Called from Libertas to transfer some data to the WLAN device - * We can't sleep here. - */ -static int if_spi_host_to_card(struct lbs_private *priv, - u8 type, u8 *buf, u16 nb) -{ - int err = 0; - unsigned long flags; - struct if_spi_card *card = priv->card; - struct if_spi_packet *packet; - u16 blen; - - lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb); - - if (nb == 0) { - netdev_err(priv->dev, "%s: invalid size requested: %d\n", - __func__, nb); - err = -EINVAL; - goto out; - } - blen = ALIGN(nb, 4); - packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC); - if (!packet) { - err = -ENOMEM; - goto out; - } - packet->blen = blen; - memcpy(packet->buffer, buf, nb); - memset(packet->buffer + nb, 0, blen - nb); - - switch (type) { - case MVMS_CMD: - priv->dnld_sent = DNLD_CMD_SENT; - spin_lock_irqsave(&card->buffer_lock, flags); - list_add_tail(&packet->list, &card->cmd_packet_list); - spin_unlock_irqrestore(&card->buffer_lock, flags); - break; - case MVMS_DAT: - priv->dnld_sent = DNLD_DATA_SENT; - spin_lock_irqsave(&card->buffer_lock, flags); - list_add_tail(&packet->list, &card->data_packet_list); - spin_unlock_irqrestore(&card->buffer_lock, flags); - break; - default: - kfree(packet); - netdev_err(priv->dev, "can't transfer buffer of type %d\n", - type); - err = -EINVAL; - break; - } - - /* Queue spi xfer work */ - queue_work(card->workqueue, &card->packet_work); -out: - lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err); - return err; -} - -/* - * Host Interrupts - * - * Service incoming interrupts from the WLAN device. We can't sleep here, so - * don't try to talk on the SPI bus, just queue the SPI xfer work. - */ -static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id) -{ - struct if_spi_card *card = dev_id; - - queue_work(card->workqueue, &card->packet_work); - - return IRQ_HANDLED; -} - -/* - * SPI callbacks - */ - -static int if_spi_init_card(struct if_spi_card *card) -{ - struct lbs_private *priv = card->priv; - int err, i; - u32 scratch; - const struct firmware *helper = NULL; - const struct firmware *mainfw = NULL; - - lbs_deb_enter(LBS_DEB_SPI); - - err = spu_init(card, card->pdata->use_dummy_writes); - if (err) - goto out; - err = spu_get_chip_revision(card, &card->card_id, &card->card_rev); - if (err) - goto out; - - err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch); - if (err) - goto out; - if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC) - lbs_deb_spi("Firmware is already loaded for " - "Marvell WLAN 802.11 adapter\n"); - else { - /* Check if we support this card */ - for (i = 0; i < ARRAY_SIZE(fw_table); i++) { - if (card->card_id == fw_table[i].model) - break; - } - if (i == ARRAY_SIZE(fw_table)) { - netdev_err(priv->dev, "Unsupported chip_id: 0x%02x\n", - card->card_id); - err = -ENODEV; - goto out; - } - - err = lbs_get_firmware(&card->spi->dev, card->card_id, - &fw_table[0], &helper, &mainfw); - if (err) { - netdev_err(priv->dev, "failed to find firmware (%d)\n", - err); - goto out; - } - - lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter " - "(chip_id = 0x%04x, chip_rev = 0x%02x) " - "attached to SPI bus_num %d, chip_select %d. " - "spi->max_speed_hz=%d\n", - card->card_id, card->card_rev, - card->spi->master->bus_num, - card->spi->chip_select, - card->spi->max_speed_hz); - err = if_spi_prog_helper_firmware(card, helper); - if (err) - goto out; - err = if_spi_prog_main_firmware(card, mainfw); - if (err) - goto out; - lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n"); - } - - err = spu_set_interrupt_mode(card, 0, 1); - if (err) - goto out; - -out: - lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); - return err; -} - -static void if_spi_resume_worker(struct work_struct *work) -{ - struct if_spi_card *card; - - card = container_of(work, struct if_spi_card, resume_work); - - if (card->suspended) { - if (card->pdata->setup) - card->pdata->setup(card->spi); - - /* Init card ... */ - if_spi_init_card(card); - - enable_irq(card->spi->irq); - - /* And resume it ... */ - lbs_resume(card->priv); - - card->suspended = 0; - } -} - -static int if_spi_probe(struct spi_device *spi) -{ - struct if_spi_card *card; - struct lbs_private *priv = NULL; - struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev); - int err = 0; - - lbs_deb_enter(LBS_DEB_SPI); - - if (!pdata) { - err = -EINVAL; - goto out; - } - - if (pdata->setup) { - err = pdata->setup(spi); - if (err) - goto out; - } - - /* Allocate card structure to represent this specific device */ - card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL); - if (!card) { - err = -ENOMEM; - goto teardown; - } - spi_set_drvdata(spi, card); - card->pdata = pdata; - card->spi = spi; - card->prev_xfer_time = jiffies; - - INIT_LIST_HEAD(&card->cmd_packet_list); - INIT_LIST_HEAD(&card->data_packet_list); - spin_lock_init(&card->buffer_lock); - - /* Initialize the SPI Interface Unit */ - - /* Firmware load */ - err = if_spi_init_card(card); - if (err) - goto free_card; - - /* - * Register our card with libertas. - * This will call alloc_etherdev. - */ - priv = lbs_add_card(card, &spi->dev); - if (!priv) { - err = -ENOMEM; - goto free_card; - } - card->priv = priv; - priv->setup_fw_on_resume = 1; - priv->card = card; - priv->hw_host_to_card = if_spi_host_to_card; - priv->enter_deep_sleep = NULL; - priv->exit_deep_sleep = NULL; - priv->reset_deep_sleep_wakeup = NULL; - priv->fw_ready = 1; - - /* Initialize interrupt handling stuff. */ - card->workqueue = create_workqueue("libertas_spi"); - INIT_WORK(&card->packet_work, if_spi_host_to_card_worker); - INIT_WORK(&card->resume_work, if_spi_resume_worker); - - err = request_irq(spi->irq, if_spi_host_interrupt, - IRQF_TRIGGER_FALLING, "libertas_spi", card); - if (err) { - pr_err("can't get host irq line-- request_irq failed\n"); - goto terminate_workqueue; - } - - /* - * Start the card. - * This will call register_netdev, and we'll start - * getting interrupts... - */ - err = lbs_start_card(priv); - if (err) - goto release_irq; - - lbs_deb_spi("Finished initializing WLAN module.\n"); - - /* successful exit */ - goto out; - -release_irq: - free_irq(spi->irq, card); -terminate_workqueue: - flush_workqueue(card->workqueue); - destroy_workqueue(card->workqueue); - lbs_remove_card(priv); /* will call free_netdev */ -free_card: - free_if_spi_card(card); -teardown: - if (pdata->teardown) - pdata->teardown(spi); -out: - lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); - return err; -} - -static int libertas_spi_remove(struct spi_device *spi) -{ - struct if_spi_card *card = spi_get_drvdata(spi); - struct lbs_private *priv = card->priv; - - lbs_deb_spi("libertas_spi_remove\n"); - lbs_deb_enter(LBS_DEB_SPI); - - cancel_work_sync(&card->resume_work); - - lbs_stop_card(priv); - lbs_remove_card(priv); /* will call free_netdev */ - - free_irq(spi->irq, card); - flush_workqueue(card->workqueue); - destroy_workqueue(card->workqueue); - if (card->pdata->teardown) - card->pdata->teardown(spi); - free_if_spi_card(card); - lbs_deb_leave(LBS_DEB_SPI); - return 0; -} - -static int if_spi_suspend(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - struct if_spi_card *card = spi_get_drvdata(spi); - - if (!card->suspended) { - lbs_suspend(card->priv); - flush_workqueue(card->workqueue); - disable_irq(spi->irq); - - if (card->pdata->teardown) - card->pdata->teardown(spi); - card->suspended = 1; - } - - return 0; -} - -static int if_spi_resume(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - struct if_spi_card *card = spi_get_drvdata(spi); - - /* Schedule delayed work */ - schedule_work(&card->resume_work); - - return 0; -} - -static const struct dev_pm_ops if_spi_pm_ops = { - .suspend = if_spi_suspend, - .resume = if_spi_resume, -}; - -static struct spi_driver libertas_spi_driver = { - .probe = if_spi_probe, - .remove = libertas_spi_remove, - .driver = { - .name = "libertas_spi", - .pm = &if_spi_pm_ops, - }, -}; - -/* - * Module functions - */ - -static int __init if_spi_init_module(void) -{ - int ret = 0; - lbs_deb_enter(LBS_DEB_SPI); - printk(KERN_INFO "libertas_spi: Libertas SPI driver\n"); - ret = spi_register_driver(&libertas_spi_driver); - lbs_deb_leave(LBS_DEB_SPI); - return ret; -} - -static void __exit if_spi_exit_module(void) -{ - lbs_deb_enter(LBS_DEB_SPI); - spi_unregister_driver(&libertas_spi_driver); - lbs_deb_leave(LBS_DEB_SPI); -} - -module_init(if_spi_init_module); -module_exit(if_spi_exit_module); - -MODULE_DESCRIPTION("Libertas SPI WLAN Driver"); -MODULE_AUTHOR("Andrey Yurovsky , " - "Colin McCabe "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:libertas_spi"); diff --git a/drivers/net/wireless/libertas/if_spi.h b/drivers/net/wireless/libertas/if_spi.h deleted file mode 100644 index e450e31fd11d..000000000000 --- a/drivers/net/wireless/libertas/if_spi.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * linux/drivers/net/wireless/libertas/if_spi.c - * - * Driver for Marvell SPI WLAN cards. - * - * Copyright 2008 Analog Devices Inc. - * - * Authors: - * Andrey Yurovsky - * Colin McCabe - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ - -#ifndef _LBS_IF_SPI_H_ -#define _LBS_IF_SPI_H_ - -#define IPFIELD_ALIGN_OFFSET 2 -#define IF_SPI_CMD_BUF_SIZE 2400 - -/***************** Firmware *****************/ - -#define IF_SPI_FW_NAME_MAX 30 - -#define MAX_MAIN_FW_LOAD_CRC_ERR 10 - -/* Chunk size when loading the helper firmware */ -#define HELPER_FW_LOAD_CHUNK_SZ 64 - -/* Value to write to indicate end of helper firmware dnld */ -#define FIRMWARE_DNLD_OK 0x0000 - -/* Value to check once the main firmware is downloaded */ -#define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888 - -/***************** SPI Interface Unit *****************/ -/* Masks used in SPI register read/write operations */ -#define IF_SPI_READ_OPERATION_MASK 0x0 -#define IF_SPI_WRITE_OPERATION_MASK 0x8000 - -/* SPI register offsets. 4-byte aligned. */ -#define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */ -#define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */ -#define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */ -#define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */ - -#define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */ -#define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */ -#define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */ - -#define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */ -#define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */ -#define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */ - -#define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */ -#define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */ -#define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */ -#define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */ - -#define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number reg */ -#define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */ - -#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */ - -#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */ -#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interrupt status reg */ -#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */ -#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */ - -#define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset select */ - -#define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */ -#define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */ -#define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event mask */ -#define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status mask */ -#define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset select */ - -#define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */ -#define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */ - -/***************** IF_SPI_DEVICEID_CTRL_REG *****************/ -#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16) -#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff) - -/***************** IF_SPI_HOST_INT_CTRL_REG *****************/ -/* Host Interrupt Control bit : Wake up */ -#define IF_SPI_HICT_WAKE_UP (1<<0) -/* Host Interrupt Control bit : WLAN ready */ -#define IF_SPI_HICT_WLAN_READY (1<<1) -/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */ -/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */ -/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */ -/* Host Interrupt Control bit : Tx auto download */ -#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5) -/* Host Interrupt Control bit : Rx auto upload */ -#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6) -/* Host Interrupt Control bit : Command auto download */ -#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7) -/* Host Interrupt Control bit : Command auto upload */ -#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8) - -/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/ -/* Card Interrupt Case bit : Tx download over */ -#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0) -/* Card Interrupt Case bit : Rx upload over */ -#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1) -/* Card Interrupt Case bit : Command download over */ -#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2) -/* Card Interrupt Case bit : Host event */ -#define IF_SPI_CIC_HOST_EVENT (1<<3) -/* Card Interrupt Case bit : Command upload over */ -#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4) -/* Card Interrupt Case bit : Power down */ -#define IF_SPI_CIC_POWER_DOWN (1<<5) - -/***************** IF_SPI_CARD_INT_STATUS_REG *****************/ -#define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0) -#define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1) -#define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2) -#define IF_SPI_CIS_HOST_EVENT (1<<3) -#define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4) -#define IF_SPI_CIS_POWER_DOWN (1<<5) - -/***************** IF_SPI_HOST_INT_CAUSE_REG *****************/ -#define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0) -#define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1) -#define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2) -#define IF_SPI_HICU_CARD_EVENT (1<<3) -#define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4) -#define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5) -#define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6) -#define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7) -#define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8) -#define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9) -#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10) - -/***************** IF_SPI_HOST_INT_STATUS_REG *****************/ -/* Host Interrupt Status bit : Tx download ready */ -#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0) -/* Host Interrupt Status bit : Rx upload ready */ -#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1) -/* Host Interrupt Status bit : Command download ready */ -#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2) -/* Host Interrupt Status bit : Card event */ -#define IF_SPI_HIST_CARD_EVENT (1<<3) -/* Host Interrupt Status bit : Command upload ready */ -#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4) -/* Host Interrupt Status bit : I/O write FIFO overflow */ -#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5) -/* Host Interrupt Status bit : I/O read FIFO underflow */ -#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6) -/* Host Interrupt Status bit : Data write FIFO overflow */ -#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7) -/* Host Interrupt Status bit : Data read FIFO underflow */ -#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8) -/* Host Interrupt Status bit : Command write FIFO overflow */ -#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9) -/* Host Interrupt Status bit : Command read FIFO underflow */ -#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10) - -/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/ -/* Host Interrupt Status Mask bit : Tx download ready */ -#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0) -/* Host Interrupt Status Mask bit : Rx upload ready */ -#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1) -/* Host Interrupt Status Mask bit : Command download ready */ -#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2) -/* Host Interrupt Status Mask bit : Card event */ -#define IF_SPI_HISM_CARDEVENT (1<<3) -/* Host Interrupt Status Mask bit : Command upload ready */ -#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4) -/* Host Interrupt Status Mask bit : I/O write FIFO overflow */ -#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5) -/* Host Interrupt Status Mask bit : I/O read FIFO underflow */ -#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6) -/* Host Interrupt Status Mask bit : Data write FIFO overflow */ -#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7) -/* Host Interrupt Status Mask bit : Data write FIFO underflow */ -#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8) -/* Host Interrupt Status Mask bit : Command write FIFO overflow */ -#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9) -/* Host Interrupt Status Mask bit : Command write FIFO underflow */ -#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10) - -/***************** IF_SPI_SPU_BUS_MODE_REG *****************/ -/* SCK edge on which the WLAN module outputs data on MISO */ -#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8 -#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0 - -/* In a SPU read operation, there is a delay between writing the SPU - * register name and getting back data from the WLAN module. - * This can be specified in terms of nanoseconds or in terms of dummy - * clock cycles which the master must output before receiving a response. */ -#define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4 -#define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0 - -/* Some different modes of SPI operation */ -#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00 -#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01 -#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02 -#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03 - -#endif diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c deleted file mode 100644 index dff08a2896a3..000000000000 --- a/drivers/net/wireless/libertas/if_usb.c +++ /dev/null @@ -1,1018 +0,0 @@ -/* - * This file contains functions used in USB interface module. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_OLPC -#include -#endif - -#define DRV_NAME "usb8xxx" - -#include "host.h" -#include "decl.h" -#include "defs.h" -#include "dev.h" -#include "cmd.h" -#include "if_usb.h" - -#define INSANEDEBUG 0 -#define lbs_deb_usb2(...) do { if (INSANEDEBUG) lbs_deb_usbd(__VA_ARGS__); } while (0) - -#define MESSAGE_HEADER_LEN 4 - -MODULE_FIRMWARE("libertas/usb8388_v9.bin"); -MODULE_FIRMWARE("libertas/usb8388_v5.bin"); -MODULE_FIRMWARE("libertas/usb8388.bin"); -MODULE_FIRMWARE("libertas/usb8682.bin"); -MODULE_FIRMWARE("usb8388.bin"); - -enum { - MODEL_UNKNOWN = 0x0, - MODEL_8388 = 0x1, - MODEL_8682 = 0x2 -}; - -/* table of firmware file names */ -static const struct lbs_fw_table fw_table[] = { - { MODEL_8388, "libertas/usb8388_olpc.bin", NULL }, - { MODEL_8388, "libertas/usb8388_v9.bin", NULL }, - { MODEL_8388, "libertas/usb8388_v5.bin", NULL }, - { MODEL_8388, "libertas/usb8388.bin", NULL }, - { MODEL_8388, "usb8388.bin", NULL }, - { MODEL_8682, "libertas/usb8682.bin", NULL } -}; - -static struct usb_device_id if_usb_table[] = { - /* Enter the device signature inside */ - { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 }, - { USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 }, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, if_usb_table); - -static void if_usb_receive(struct urb *urb); -static void if_usb_receive_fwload(struct urb *urb); -static void if_usb_prog_firmware(struct lbs_private *priv, int ret, - const struct firmware *fw, - const struct firmware *unused); -static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, - uint8_t *payload, uint16_t nb); -static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, - uint16_t nb); -static void if_usb_free(struct if_usb_card *cardp); -static int if_usb_submit_rx_urb(struct if_usb_card *cardp); -static int if_usb_reset_device(struct if_usb_card *cardp); - -/** - * if_usb_write_bulk_callback - callback function to handle the status - * of the URB - * @urb: pointer to &urb structure - * returns: N/A - */ -static void if_usb_write_bulk_callback(struct urb *urb) -{ - struct if_usb_card *cardp = (struct if_usb_card *) urb->context; - - /* handle the transmission complete validations */ - - if (urb->status == 0) { - struct lbs_private *priv = cardp->priv; - - lbs_deb_usb2(&urb->dev->dev, "URB status is successful\n"); - lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", - urb->actual_length); - - /* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not - * passed up to the lbs level. - */ - if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT) - lbs_host_to_card_done(priv); - } else { - /* print the failure status number for debug */ - pr_info("URB in failure status: %d\n", urb->status); - } -} - -/** - * if_usb_free - free tx/rx urb, skb and rx buffer - * @cardp: pointer to &if_usb_card - * returns: N/A - */ -static void if_usb_free(struct if_usb_card *cardp) -{ - lbs_deb_enter(LBS_DEB_USB); - - /* Unlink tx & rx urb */ - usb_kill_urb(cardp->tx_urb); - usb_kill_urb(cardp->rx_urb); - - usb_free_urb(cardp->tx_urb); - cardp->tx_urb = NULL; - - usb_free_urb(cardp->rx_urb); - cardp->rx_urb = NULL; - - kfree(cardp->ep_out_buf); - cardp->ep_out_buf = NULL; - - lbs_deb_leave(LBS_DEB_USB); -} - -static void if_usb_setup_firmware(struct lbs_private *priv) -{ - struct if_usb_card *cardp = priv->card; - struct cmd_ds_set_boot2_ver b2_cmd; - struct cmd_ds_802_11_fw_wake_method wake_method; - - b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); - b2_cmd.action = 0; - b2_cmd.version = cardp->boot2_version; - - if (lbs_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) - lbs_deb_usb("Setting boot2 version failed\n"); - - priv->wol_gpio = 2; /* Wake via GPIO2... */ - priv->wol_gap = 20; /* ... after 20ms */ - lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA, - (struct wol_config *) NULL); - - wake_method.hdr.size = cpu_to_le16(sizeof(wake_method)); - wake_method.action = cpu_to_le16(CMD_ACT_GET); - if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) { - netdev_info(priv->dev, "Firmware does not seem to support PS mode\n"); - priv->fwcapinfo &= ~FW_CAPINFO_PS; - } else { - if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) { - lbs_deb_usb("Firmware seems to support PS with wake-via-command\n"); - } else { - /* The versions which boot up this way don't seem to - work even if we set it to the command interrupt */ - priv->fwcapinfo &= ~FW_CAPINFO_PS; - netdev_info(priv->dev, - "Firmware doesn't wake via command interrupt; disabling PS mode\n"); - } - } -} - -static void if_usb_fw_timeo(unsigned long priv) -{ - struct if_usb_card *cardp = (void *)priv; - - if (cardp->fwdnldover) { - lbs_deb_usb("Download complete, no event. Assuming success\n"); - } else { - pr_err("Download timed out\n"); - cardp->surprise_removed = 1; - } - wake_up(&cardp->fw_wq); -} - -#ifdef CONFIG_OLPC -static void if_usb_reset_olpc_card(struct lbs_private *priv) -{ - printk(KERN_CRIT "Resetting OLPC wireless via EC...\n"); - olpc_ec_cmd(0x25, NULL, 0, NULL, 0); -} -#endif - -/** - * if_usb_probe - sets the configuration values - * @intf: &usb_interface pointer - * @id: pointer to usb_device_id - * returns: 0 on success, error code on failure - */ -static int if_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - struct lbs_private *priv; - struct if_usb_card *cardp; - int r = -ENOMEM; - int i; - - udev = interface_to_usbdev(intf); - - cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); - if (!cardp) - goto error; - - setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp); - init_waitqueue_head(&cardp->fw_wq); - - cardp->udev = udev; - cardp->model = (uint32_t) id->driver_info; - iface_desc = intf->cur_altsetting; - - lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" - " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", - le16_to_cpu(udev->descriptor.bcdUSB), - udev->descriptor.bDeviceClass, - udev->descriptor.bDeviceSubClass, - udev->descriptor.bDeviceProtocol); - - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_bulk_in(endpoint)) { - cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); - cardp->ep_in = usb_endpoint_num(endpoint); - - lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); - lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); - - } else if (usb_endpoint_is_bulk_out(endpoint)) { - cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); - cardp->ep_out = usb_endpoint_num(endpoint); - - lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out); - lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size); - } - } - if (!cardp->ep_out_size || !cardp->ep_in_size) { - lbs_deb_usbd(&udev->dev, "Endpoints not found\n"); - goto dealloc; - } - if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) { - lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n"); - goto dealloc; - } - if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) { - lbs_deb_usbd(&udev->dev, "Tx URB allocation failed\n"); - goto dealloc; - } - cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL); - if (!cardp->ep_out_buf) { - lbs_deb_usbd(&udev->dev, "Could not allocate buffer\n"); - goto dealloc; - } - - if (!(priv = lbs_add_card(cardp, &intf->dev))) - goto err_add_card; - - cardp->priv = priv; - - priv->hw_host_to_card = if_usb_host_to_card; - priv->enter_deep_sleep = NULL; - priv->exit_deep_sleep = NULL; - priv->reset_deep_sleep_wakeup = NULL; -#ifdef CONFIG_OLPC - if (machine_is_olpc()) - priv->reset_card = if_usb_reset_olpc_card; -#endif - - cardp->boot2_version = udev->descriptor.bcdDevice; - - usb_get_dev(udev); - usb_set_intfdata(intf, cardp); - - r = lbs_get_firmware_async(priv, &udev->dev, cardp->model, - fw_table, if_usb_prog_firmware); - if (r) - goto err_get_fw; - - return 0; - -err_get_fw: - lbs_remove_card(priv); -err_add_card: - if_usb_reset_device(cardp); -dealloc: - if_usb_free(cardp); - -error: - return r; -} - -/** - * if_usb_disconnect - free resource and cleanup - * @intf: USB interface structure - * returns: N/A - */ -static void if_usb_disconnect(struct usb_interface *intf) -{ - struct if_usb_card *cardp = usb_get_intfdata(intf); - struct lbs_private *priv = cardp->priv; - - lbs_deb_enter(LBS_DEB_MAIN); - - cardp->surprise_removed = 1; - - if (priv) { - lbs_stop_card(priv); - lbs_remove_card(priv); - } - - /* Unlink and free urb */ - if_usb_free(cardp); - - usb_set_intfdata(intf, NULL); - usb_put_dev(interface_to_usbdev(intf)); - - lbs_deb_leave(LBS_DEB_MAIN); -} - -/** - * if_usb_send_fw_pkt - download FW - * @cardp: pointer to &struct if_usb_card - * returns: 0 - */ -static int if_usb_send_fw_pkt(struct if_usb_card *cardp) -{ - struct fwdata *fwdata = cardp->ep_out_buf; - const uint8_t *firmware = cardp->fw->data; - - /* If we got a CRC failure on the last block, back - up and retry it */ - if (!cardp->CRC_OK) { - cardp->totalbytes = cardp->fwlastblksent; - cardp->fwseqnum--; - } - - lbs_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n", - cardp->totalbytes); - - /* struct fwdata (which we sent to the card) has an - extra __le32 field in between the header and the data, - which is not in the struct fwheader in the actual - firmware binary. Insert the seqnum in the middle... */ - memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], - sizeof(struct fwheader)); - - cardp->fwlastblksent = cardp->totalbytes; - cardp->totalbytes += sizeof(struct fwheader); - - memcpy(fwdata->data, &firmware[cardp->totalbytes], - le32_to_cpu(fwdata->hdr.datalength)); - - lbs_deb_usb2(&cardp->udev->dev, "Data length = %d\n", - le32_to_cpu(fwdata->hdr.datalength)); - - fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); - cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); - - usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + - le32_to_cpu(fwdata->hdr.datalength)); - - if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) { - lbs_deb_usb2(&cardp->udev->dev, "There are data to follow\n"); - lbs_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n", - cardp->fwseqnum, cardp->totalbytes); - } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) { - lbs_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n"); - lbs_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n"); - - cardp->fwfinalblk = 1; - } - - lbs_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n", - cardp->totalbytes); - - return 0; -} - -static int if_usb_reset_device(struct if_usb_card *cardp) -{ - struct cmd_header *cmd = cardp->ep_out_buf + 4; - int ret; - - lbs_deb_enter(LBS_DEB_USB); - - *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); - - cmd->command = cpu_to_le16(CMD_802_11_RESET); - cmd->size = cpu_to_le16(sizeof(cmd)); - cmd->result = cpu_to_le16(0); - cmd->seqnum = cpu_to_le16(0x5a5a); - usb_tx_block(cardp, cardp->ep_out_buf, 4 + sizeof(struct cmd_header)); - - msleep(100); - ret = usb_reset_device(cardp->udev); - msleep(100); - -#ifdef CONFIG_OLPC - if (ret && machine_is_olpc()) - if_usb_reset_olpc_card(NULL); -#endif - - lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret); - - return ret; -} - -/** - * usb_tx_block - transfer the data to the device - * @cardp: pointer to &struct if_usb_card - * @payload: pointer to payload data - * @nb: data length - * returns: 0 for success or negative error code - */ -static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb) -{ - int ret; - - /* check if device is removed */ - if (cardp->surprise_removed) { - lbs_deb_usbd(&cardp->udev->dev, "Device removed\n"); - ret = -ENODEV; - goto tx_ret; - } - - usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, - usb_sndbulkpipe(cardp->udev, - cardp->ep_out), - payload, nb, if_usb_write_bulk_callback, cardp); - - cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; - - if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { - lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret); - } else { - lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n"); - ret = 0; - } - -tx_ret: - return ret; -} - -static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, - void (*callbackfn)(struct urb *urb)) -{ - struct sk_buff *skb; - int ret = -1; - - if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { - pr_err("No free skb\n"); - goto rx_ret; - } - - cardp->rx_skb = skb; - - /* Fill the receive configuration URB and initialise the Rx call back */ - usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, - usb_rcvbulkpipe(cardp->udev, cardp->ep_in), - skb->data + IPFIELD_ALIGN_OFFSET, - MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, - cardp); - - cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; - - lbs_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); - if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { - lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret); - kfree_skb(skb); - cardp->rx_skb = NULL; - ret = -1; - } else { - lbs_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n"); - ret = 0; - } - -rx_ret: - return ret; -} - -static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) -{ - return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); -} - -static int if_usb_submit_rx_urb(struct if_usb_card *cardp) -{ - return __if_usb_submit_rx_urb(cardp, &if_usb_receive); -} - -static void if_usb_receive_fwload(struct urb *urb) -{ - struct if_usb_card *cardp = urb->context; - struct sk_buff *skb = cardp->rx_skb; - struct fwsyncheader *syncfwheader; - struct bootcmdresp bootcmdresp; - - if (urb->status) { - lbs_deb_usbd(&cardp->udev->dev, - "URB status is failed during fw load\n"); - kfree_skb(skb); - return; - } - - if (cardp->fwdnldover) { - __le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); - - if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && - tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) { - pr_info("Firmware ready event received\n"); - wake_up(&cardp->fw_wq); - } else { - lbs_deb_usb("Waiting for confirmation; got %x %x\n", - le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1])); - if_usb_submit_rx_urb_fwload(cardp); - } - kfree_skb(skb); - return; - } - if (cardp->bootcmdresp <= 0) { - memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, - sizeof(bootcmdresp)); - - if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { - kfree_skb(skb); - if_usb_submit_rx_urb_fwload(cardp); - cardp->bootcmdresp = BOOT_CMD_RESP_OK; - lbs_deb_usbd(&cardp->udev->dev, - "Received valid boot command response\n"); - return; - } - if (bootcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { - if (bootcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || - bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || - bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) { - if (!cardp->bootcmdresp) - pr_info("Firmware already seems alive; resetting\n"); - cardp->bootcmdresp = -1; - } else { - pr_info("boot cmd response wrong magic number (0x%x)\n", - le32_to_cpu(bootcmdresp.magic)); - } - } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) && - (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) && - (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) { - pr_info("boot cmd response cmd_tag error (%d)\n", - bootcmdresp.cmd); - } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) { - pr_info("boot cmd response result error (%d)\n", - bootcmdresp.result); - } else { - cardp->bootcmdresp = 1; - lbs_deb_usbd(&cardp->udev->dev, - "Received valid boot command response\n"); - } - kfree_skb(skb); - if_usb_submit_rx_urb_fwload(cardp); - return; - } - - syncfwheader = kmemdup(skb->data + IPFIELD_ALIGN_OFFSET, - sizeof(struct fwsyncheader), GFP_ATOMIC); - if (!syncfwheader) { - lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n"); - kfree_skb(skb); - return; - } - - if (!syncfwheader->cmd) { - lbs_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n"); - lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n", - le32_to_cpu(syncfwheader->seqnum)); - cardp->CRC_OK = 1; - } else { - lbs_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n"); - cardp->CRC_OK = 0; - } - - kfree_skb(skb); - - /* Give device 5s to either write firmware to its RAM or eeprom */ - mod_timer(&cardp->fw_timeout, jiffies + (HZ*5)); - - if (cardp->fwfinalblk) { - cardp->fwdnldover = 1; - goto exit; - } - - if_usb_send_fw_pkt(cardp); - - exit: - if_usb_submit_rx_urb_fwload(cardp); - - kfree(syncfwheader); -} - -#define MRVDRV_MIN_PKT_LEN 30 - -static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, - struct if_usb_card *cardp, - struct lbs_private *priv) -{ - if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN - || recvlength < MRVDRV_MIN_PKT_LEN) { - lbs_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n"); - kfree_skb(skb); - return; - } - - skb_reserve(skb, IPFIELD_ALIGN_OFFSET); - skb_put(skb, recvlength); - skb_pull(skb, MESSAGE_HEADER_LEN); - - lbs_process_rxed_packet(priv, skb); -} - -static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, - struct sk_buff *skb, - struct if_usb_card *cardp, - struct lbs_private *priv) -{ - u8 i; - - if (recvlength > LBS_CMD_BUFFER_SIZE) { - lbs_deb_usbd(&cardp->udev->dev, - "The receive buffer is too large\n"); - kfree_skb(skb); - return; - } - - BUG_ON(!in_interrupt()); - - spin_lock(&priv->driver_lock); - - i = (priv->resp_idx == 0) ? 1 : 0; - BUG_ON(priv->resp_len[i]); - priv->resp_len[i] = (recvlength - MESSAGE_HEADER_LEN); - memcpy(priv->resp_buf[i], recvbuff + MESSAGE_HEADER_LEN, - priv->resp_len[i]); - kfree_skb(skb); - lbs_notify_command_response(priv, i); - - spin_unlock(&priv->driver_lock); - - lbs_deb_usbd(&cardp->udev->dev, - "Wake up main thread to handle cmd response\n"); -} - -/** - * if_usb_receive - read the packet into the upload buffer, - * wake up the main thread and initialise the Rx callack - * - * @urb: pointer to &struct urb - * returns: N/A - */ -static void if_usb_receive(struct urb *urb) -{ - struct if_usb_card *cardp = urb->context; - struct sk_buff *skb = cardp->rx_skb; - struct lbs_private *priv = cardp->priv; - int recvlength = urb->actual_length; - uint8_t *recvbuff = NULL; - uint32_t recvtype = 0; - __le32 *pkt = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); - uint32_t event; - - lbs_deb_enter(LBS_DEB_USB); - - if (recvlength) { - if (urb->status) { - lbs_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n", - urb->status); - kfree_skb(skb); - goto setup_for_next; - } - - recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; - recvtype = le32_to_cpu(pkt[0]); - lbs_deb_usbd(&cardp->udev->dev, - "Recv length = 0x%x, Recv type = 0x%X\n", - recvlength, recvtype); - } else if (urb->status) { - kfree_skb(skb); - goto rx_exit; - } - - switch (recvtype) { - case CMD_TYPE_DATA: - process_cmdtypedata(recvlength, skb, cardp, priv); - break; - - case CMD_TYPE_REQUEST: - process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); - break; - - case CMD_TYPE_INDICATION: - /* Event handling */ - event = le32_to_cpu(pkt[1]); - lbs_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event); - kfree_skb(skb); - - /* Icky undocumented magic special case */ - if (event & 0xffff0000) { - u32 trycount = (event & 0xffff0000) >> 16; - - lbs_send_tx_feedback(priv, trycount); - } else - lbs_queue_event(priv, event & 0xFF); - break; - - default: - lbs_deb_usbd(&cardp->udev->dev, "Unknown command type 0x%X\n", - recvtype); - kfree_skb(skb); - break; - } - -setup_for_next: - if_usb_submit_rx_urb(cardp); -rx_exit: - lbs_deb_leave(LBS_DEB_USB); -} - -/** - * if_usb_host_to_card - downloads data to FW - * @priv: pointer to &struct lbs_private structure - * @type: type of data - * @payload: pointer to data buffer - * @nb: number of bytes - * returns: 0 for success or negative error code - */ -static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, - uint8_t *payload, uint16_t nb) -{ - struct if_usb_card *cardp = priv->card; - - lbs_deb_usbd(&cardp->udev->dev,"*** type = %u\n", type); - lbs_deb_usbd(&cardp->udev->dev,"size after = %d\n", nb); - - if (type == MVMS_CMD) { - *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); - priv->dnld_sent = DNLD_CMD_SENT; - } else { - *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); - priv->dnld_sent = DNLD_DATA_SENT; - } - - memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); - - return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN); -} - -/** - * if_usb_issue_boot_command - issues Boot command to the Boot2 code - * @cardp: pointer to &if_usb_card - * @ivalue: 1:Boot from FW by USB-Download - * 2:Boot from FW in EEPROM - * returns: 0 for success or negative error code - */ -static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) -{ - struct bootcmd *bootcmd = cardp->ep_out_buf; - - /* Prepare command */ - bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); - bootcmd->cmd = ivalue; - memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); - - /* Issue command */ - usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd)); - - return 0; -} - - -/** - * check_fwfile_format - check the validity of Boot2/FW image - * - * @data: pointer to image - * @totlen: image length - * returns: 0 (good) or 1 (failure) - */ -static int check_fwfile_format(const uint8_t *data, uint32_t totlen) -{ - uint32_t bincmd, exit; - uint32_t blksize, offset, len; - int ret; - - ret = 1; - exit = len = 0; - - do { - struct fwheader *fwh = (void *)data; - - bincmd = le32_to_cpu(fwh->dnldcmd); - blksize = le32_to_cpu(fwh->datalength); - switch (bincmd) { - case FW_HAS_DATA_TO_RECV: - offset = sizeof(struct fwheader) + blksize; - data += offset; - len += offset; - if (len >= totlen) - exit = 1; - break; - case FW_HAS_LAST_BLOCK: - exit = 1; - ret = 0; - break; - default: - exit = 1; - break; - } - } while (!exit); - - if (ret) - pr_err("firmware file format check FAIL\n"); - else - lbs_deb_fw("firmware file format check PASS\n"); - - return ret; -} - -static void if_usb_prog_firmware(struct lbs_private *priv, int ret, - const struct firmware *fw, - const struct firmware *unused) -{ - struct if_usb_card *cardp = priv->card; - int i = 0; - static int reset_count = 10; - - lbs_deb_enter(LBS_DEB_USB); - - if (ret) { - pr_err("failed to find firmware (%d)\n", ret); - goto done; - } - - cardp->fw = fw; - if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { - ret = -EINVAL; - goto done; - } - - /* Cancel any pending usb business */ - usb_kill_urb(cardp->rx_urb); - usb_kill_urb(cardp->tx_urb); - - cardp->fwlastblksent = 0; - cardp->fwdnldover = 0; - cardp->totalbytes = 0; - cardp->fwfinalblk = 0; - cardp->bootcmdresp = 0; - -restart: - if (if_usb_submit_rx_urb_fwload(cardp) < 0) { - lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); - ret = -EIO; - goto done; - } - - cardp->bootcmdresp = 0; - do { - int j = 0; - i++; - if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); - /* wait for command response */ - do { - j++; - msleep_interruptible(100); - } while (cardp->bootcmdresp == 0 && j < 10); - } while (cardp->bootcmdresp == 0 && i < 5); - - if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) { - /* Return to normal operation */ - ret = -EOPNOTSUPP; - usb_kill_urb(cardp->rx_urb); - usb_kill_urb(cardp->tx_urb); - if (if_usb_submit_rx_urb(cardp) < 0) - ret = -EIO; - goto done; - } else if (cardp->bootcmdresp <= 0) { - if (--reset_count >= 0) { - if_usb_reset_device(cardp); - goto restart; - } - ret = -EIO; - goto done; - } - - i = 0; - - cardp->totalbytes = 0; - cardp->fwlastblksent = 0; - cardp->CRC_OK = 1; - cardp->fwdnldover = 0; - cardp->fwseqnum = -1; - cardp->totalbytes = 0; - cardp->fwfinalblk = 0; - - /* Send the first firmware packet... */ - if_usb_send_fw_pkt(cardp); - - /* ... and wait for the process to complete */ - wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover); - - del_timer_sync(&cardp->fw_timeout); - usb_kill_urb(cardp->rx_urb); - - if (!cardp->fwdnldover) { - pr_info("failed to load fw, resetting device!\n"); - if (--reset_count >= 0) { - if_usb_reset_device(cardp); - goto restart; - } - - pr_info("FW download failure, time = %d ms\n", i * 100); - ret = -EIO; - goto done; - } - - cardp->priv->fw_ready = 1; - if_usb_submit_rx_urb(cardp); - - if (lbs_start_card(priv)) - goto done; - - if_usb_setup_firmware(priv); - - /* - * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware. - */ - priv->wol_criteria = EHS_REMOVE_WAKEUP; - if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL)) - priv->ehs_remove_supported = false; - - done: - cardp->fw = NULL; - lbs_deb_leave(LBS_DEB_USB); -} - - -#ifdef CONFIG_PM -static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct if_usb_card *cardp = usb_get_intfdata(intf); - struct lbs_private *priv = cardp->priv; - int ret; - - lbs_deb_enter(LBS_DEB_USB); - - if (priv->psstate != PS_STATE_FULL_POWER) { - ret = -1; - goto out; - } - -#ifdef CONFIG_OLPC - if (machine_is_olpc()) { - if (priv->wol_criteria == EHS_REMOVE_WAKEUP) - olpc_ec_wakeup_clear(EC_SCI_SRC_WLAN); - else - olpc_ec_wakeup_set(EC_SCI_SRC_WLAN); - } -#endif - - ret = lbs_suspend(priv); - if (ret) - goto out; - - /* Unlink tx & rx urb */ - usb_kill_urb(cardp->tx_urb); - usb_kill_urb(cardp->rx_urb); - - out: - lbs_deb_leave(LBS_DEB_USB); - return ret; -} - -static int if_usb_resume(struct usb_interface *intf) -{ - struct if_usb_card *cardp = usb_get_intfdata(intf); - struct lbs_private *priv = cardp->priv; - - lbs_deb_enter(LBS_DEB_USB); - - if_usb_submit_rx_urb(cardp); - - lbs_resume(priv); - - lbs_deb_leave(LBS_DEB_USB); - return 0; -} -#else -#define if_usb_suspend NULL -#define if_usb_resume NULL -#endif - -static struct usb_driver if_usb_driver = { - .name = DRV_NAME, - .probe = if_usb_probe, - .disconnect = if_usb_disconnect, - .id_table = if_usb_table, - .suspend = if_usb_suspend, - .resume = if_usb_resume, - .reset_resume = if_usb_resume, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(if_usb_driver); - -MODULE_DESCRIPTION("8388 USB WLAN Driver"); -MODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc."); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h deleted file mode 100644 index 6e42eac331de..000000000000 --- a/drivers/net/wireless/libertas/if_usb.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef _LBS_IF_USB_H -#define _LBS_IF_USB_H - -#include -#include - -struct lbs_private; - -/* - * This file contains definition for USB interface. - */ -#define CMD_TYPE_REQUEST 0xF00DFACE -#define CMD_TYPE_DATA 0xBEADC0DE -#define CMD_TYPE_INDICATION 0xBEEFFACE - -#define IPFIELD_ALIGN_OFFSET 2 - -#define BOOT_CMD_FW_BY_USB 0x01 -#define BOOT_CMD_FW_IN_EEPROM 0x02 -#define BOOT_CMD_UPDATE_BOOT2 0x03 -#define BOOT_CMD_UPDATE_FW 0x04 -#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */ - -struct bootcmd -{ - __le32 magic; - uint8_t cmd; - uint8_t pad[11]; -}; - -#define BOOT_CMD_RESP_OK 0x0001 -#define BOOT_CMD_RESP_FAIL 0x0000 -#define BOOT_CMD_RESP_NOT_SUPPORTED 0x0002 - -struct bootcmdresp -{ - __le32 magic; - uint8_t cmd; - uint8_t result; - uint8_t pad[2]; -}; - -/* USB card description structure*/ -struct if_usb_card { - struct usb_device *udev; - uint32_t model; /* MODEL_* */ - struct urb *rx_urb, *tx_urb; - struct lbs_private *priv; - - struct sk_buff *rx_skb; - - uint8_t ep_in; - uint8_t ep_out; - - /* bootcmdresp == 0 means command is pending - * bootcmdresp < 0 means error - * bootcmdresp > 0 is a BOOT_CMD_RESP_* from firmware - */ - int8_t bootcmdresp; - - int ep_in_size; - - void *ep_out_buf; - int ep_out_size; - - const struct firmware *fw; - struct timer_list fw_timeout; - wait_queue_head_t fw_wq; - uint32_t fwseqnum; - uint32_t totalbytes; - uint32_t fwlastblksent; - uint8_t CRC_OK; - uint8_t fwdnldover; - uint8_t fwfinalblk; - uint8_t surprise_removed; - - __le16 boot2_version; -}; - -/* fwheader */ -struct fwheader { - __le32 dnldcmd; - __le32 baseaddr; - __le32 datalength; - __le32 CRC; -}; - -#define FW_MAX_DATA_BLK_SIZE 600 -/* FWData */ -struct fwdata { - struct fwheader hdr; - __le32 seqnum; - uint8_t data[0]; -}; - -/* fwsyncheader */ -struct fwsyncheader { - __le32 cmd; - __le32 seqnum; -}; - -#define FW_HAS_DATA_TO_RECV 0x00000001 -#define FW_HAS_LAST_BLOCK 0x00000004 - - -#endif diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c deleted file mode 100644 index 8079560f4965..000000000000 --- a/drivers/net/wireless/libertas/main.c +++ /dev/null @@ -1,1225 +0,0 @@ -/* - * This file contains the major functions in WLAN - * driver. It includes init, exit, open, close and main - * thread etc.. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "host.h" -#include "decl.h" -#include "dev.h" -#include "cfg.h" -#include "debugfs.h" -#include "cmd.h" -#include "mesh.h" - -#define DRIVER_RELEASE_VERSION "323.p0" -const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION -#ifdef DEBUG - "-dbg" -#endif - ""; - - -/* Module parameters */ -unsigned int lbs_debug; -EXPORT_SYMBOL_GPL(lbs_debug); -module_param_named(libertas_debug, lbs_debug, int, 0644); - -unsigned int lbs_disablemesh; -EXPORT_SYMBOL_GPL(lbs_disablemesh); -module_param_named(libertas_disablemesh, lbs_disablemesh, int, 0644); - - -/* - * This global structure is used to send the confirm_sleep command as - * fast as possible down to the firmware. - */ -struct cmd_confirm_sleep confirm_sleep; - - -/* - * the table to keep region code - */ -u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE] = - { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; - -/* - * FW rate table. FW refers to rates by their index in this table, not by the - * rate value itself. Values of 0x00 are - * reserved positions. - */ -static u8 fw_data_rates[MAX_RATES] = - { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, - 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 -}; - -/** - * lbs_fw_index_to_data_rate - use index to get the data rate - * - * @idx: The index of data rate - * returns: data rate or 0 - */ -u32 lbs_fw_index_to_data_rate(u8 idx) -{ - if (idx >= sizeof(fw_data_rates)) - idx = 0; - return fw_data_rates[idx]; -} - -/** - * lbs_data_rate_to_fw_index - use rate to get the index - * - * @rate: data rate - * returns: index or 0 - */ -u8 lbs_data_rate_to_fw_index(u32 rate) -{ - u8 i; - - if (!rate) - return 0; - - for (i = 0; i < sizeof(fw_data_rates); i++) { - if (rate == fw_data_rates[i]) - return i; - } - return 0; -} - -int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type) -{ - int ret = 0; - - switch (type) { - case NL80211_IFTYPE_MONITOR: - ret = lbs_set_monitor_mode(priv, 1); - break; - case NL80211_IFTYPE_STATION: - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) - ret = lbs_set_monitor_mode(priv, 0); - if (!ret) - ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1); - break; - case NL80211_IFTYPE_ADHOC: - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) - ret = lbs_set_monitor_mode(priv, 0); - if (!ret) - ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2); - break; - default: - ret = -ENOTSUPP; - } - return ret; -} - -int lbs_start_iface(struct lbs_private *priv) -{ - struct cmd_ds_802_11_mac_address cmd; - int ret; - - if (priv->power_restore) { - ret = priv->power_restore(priv); - if (ret) - return ret; - } - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - memcpy(cmd.macadd, priv->current_addr, ETH_ALEN); - - ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); - if (ret) { - lbs_deb_net("set MAC address failed\n"); - goto err; - } - - ret = lbs_set_iface_type(priv, priv->wdev->iftype); - if (ret) { - lbs_deb_net("set iface type failed\n"); - goto err; - } - - ret = lbs_set_11d_domain_info(priv); - if (ret) { - lbs_deb_net("set 11d domain info failed\n"); - goto err; - } - - lbs_update_channel(priv); - - priv->iface_running = true; - return 0; - -err: - if (priv->power_save) - priv->power_save(priv); - return ret; -} - -/** - * lbs_dev_open - open the ethX interface - * - * @dev: A pointer to &net_device structure - * returns: 0 or -EBUSY if monitor mode active - */ -static int lbs_dev_open(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - int ret = 0; - - lbs_deb_enter(LBS_DEB_NET); - if (!priv->iface_running) { - ret = lbs_start_iface(priv); - if (ret) - goto out; - } - - spin_lock_irq(&priv->driver_lock); - - netif_carrier_off(dev); - - if (!priv->tx_pending_len) - netif_wake_queue(dev); - - spin_unlock_irq(&priv->driver_lock); - -out: - lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); - return ret; -} - -static bool lbs_command_queue_empty(struct lbs_private *priv) -{ - unsigned long flags; - bool ret; - spin_lock_irqsave(&priv->driver_lock, flags); - ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq); - spin_unlock_irqrestore(&priv->driver_lock, flags); - return ret; -} - -int lbs_stop_iface(struct lbs_private *priv) -{ - unsigned long flags; - int ret = 0; - - lbs_deb_enter(LBS_DEB_MAIN); - - spin_lock_irqsave(&priv->driver_lock, flags); - priv->iface_running = false; - kfree_skb(priv->currenttxskb); - priv->currenttxskb = NULL; - priv->tx_pending_len = 0; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - cancel_work_sync(&priv->mcast_work); - del_timer_sync(&priv->tx_lockup_timer); - - /* Disable command processing, and wait for all commands to complete */ - lbs_deb_main("waiting for commands to complete\n"); - wait_event(priv->waitq, lbs_command_queue_empty(priv)); - lbs_deb_main("all commands completed\n"); - - if (priv->power_save) - ret = priv->power_save(priv); - - lbs_deb_leave(LBS_DEB_MAIN); - return ret; -} - -/** - * lbs_eth_stop - close the ethX interface - * - * @dev: A pointer to &net_device structure - * returns: 0 - */ -static int lbs_eth_stop(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - - lbs_deb_enter(LBS_DEB_NET); - - if (priv->connect_status == LBS_CONNECTED) - lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING); - - spin_lock_irq(&priv->driver_lock); - netif_stop_queue(dev); - spin_unlock_irq(&priv->driver_lock); - - lbs_update_mcast(priv); - cancel_delayed_work_sync(&priv->scan_work); - if (priv->scan_req) - lbs_scan_done(priv); - - netif_carrier_off(priv->dev); - - if (!lbs_iface_active(priv)) - lbs_stop_iface(priv); - - lbs_deb_leave(LBS_DEB_NET); - return 0; -} - -void lbs_host_to_card_done(struct lbs_private *priv) -{ - unsigned long flags; - - lbs_deb_enter(LBS_DEB_THREAD); - - spin_lock_irqsave(&priv->driver_lock, flags); - del_timer(&priv->tx_lockup_timer); - - priv->dnld_sent = DNLD_RES_RECEIVED; - - /* Wake main thread if commands are pending */ - if (!priv->cur_cmd || priv->tx_pending_len > 0) { - if (!priv->wakeup_dev_required) - wake_up(&priv->waitq); - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbs_deb_leave(LBS_DEB_THREAD); -} -EXPORT_SYMBOL_GPL(lbs_host_to_card_done); - -int lbs_set_mac_address(struct net_device *dev, void *addr) -{ - int ret = 0; - struct lbs_private *priv = dev->ml_priv; - struct sockaddr *phwaddr = addr; - - lbs_deb_enter(LBS_DEB_NET); - - /* - * Can only set MAC address when all interfaces are down, to be written - * to the hardware when one of them is brought up. - */ - if (lbs_iface_active(priv)) - return -EBUSY; - - /* In case it was called from the mesh device */ - dev = priv->dev; - - memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN); - memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN); - if (priv->mesh_dev) - memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN); - - lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); - return ret; -} - - -static inline int mac_in_list(unsigned char *list, int list_len, - unsigned char *mac) -{ - while (list_len) { - if (!memcmp(list, mac, ETH_ALEN)) - return 1; - list += ETH_ALEN; - list_len--; - } - return 0; -} - - -static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, - struct net_device *dev, int nr_addrs) -{ - int i = nr_addrs; - struct netdev_hw_addr *ha; - int cnt; - - if ((dev->flags & (IFF_UP|IFF_MULTICAST)) != (IFF_UP|IFF_MULTICAST)) - return nr_addrs; - - netif_addr_lock_bh(dev); - cnt = netdev_mc_count(dev); - netdev_for_each_mc_addr(ha, dev) { - if (mac_in_list(cmd->maclist, nr_addrs, ha->addr)) { - lbs_deb_net("mcast address %s:%pM skipped\n", dev->name, - ha->addr); - cnt--; - continue; - } - - if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE) - break; - memcpy(&cmd->maclist[6*i], ha->addr, ETH_ALEN); - lbs_deb_net("mcast address %s:%pM added to filter\n", dev->name, - ha->addr); - i++; - cnt--; - } - netif_addr_unlock_bh(dev); - if (cnt) - return -EOVERFLOW; - - return i; -} - -void lbs_update_mcast(struct lbs_private *priv) -{ - struct cmd_ds_mac_multicast_adr mcast_cmd; - int dev_flags = 0; - int nr_addrs; - int old_mac_control = priv->mac_control; - - lbs_deb_enter(LBS_DEB_NET); - - if (netif_running(priv->dev)) - dev_flags |= priv->dev->flags; - if (priv->mesh_dev && netif_running(priv->mesh_dev)) - dev_flags |= priv->mesh_dev->flags; - - if (dev_flags & IFF_PROMISC) { - priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE; - priv->mac_control &= ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE | - CMD_ACT_MAC_MULTICAST_ENABLE); - goto out_set_mac_control; - } else if (dev_flags & IFF_ALLMULTI) { - do_allmulti: - priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; - priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | - CMD_ACT_MAC_MULTICAST_ENABLE); - goto out_set_mac_control; - } - - /* Once for priv->dev, again for priv->mesh_dev if it exists */ - nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->dev, 0); - if (nr_addrs >= 0 && priv->mesh_dev) - nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->mesh_dev, nr_addrs); - if (nr_addrs < 0) - goto do_allmulti; - - if (nr_addrs) { - int size = offsetof(struct cmd_ds_mac_multicast_adr, - maclist[6*nr_addrs]); - - mcast_cmd.action = cpu_to_le16(CMD_ACT_SET); - mcast_cmd.hdr.size = cpu_to_le16(size); - mcast_cmd.nr_of_adrs = cpu_to_le16(nr_addrs); - - lbs_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &mcast_cmd.hdr, size); - - priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; - } else - priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; - - priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | - CMD_ACT_MAC_ALL_MULTICAST_ENABLE); - out_set_mac_control: - if (priv->mac_control != old_mac_control) - lbs_set_mac_control(priv); - - lbs_deb_leave(LBS_DEB_NET); -} - -static void lbs_set_mcast_worker(struct work_struct *work) -{ - struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work); - lbs_update_mcast(priv); -} - -void lbs_set_multicast_list(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - - schedule_work(&priv->mcast_work); -} - -/** - * lbs_thread - handles the major jobs in the LBS driver. - * It handles all events generated by firmware, RX data received - * from firmware and TX data sent from kernel. - * - * @data: A pointer to &lbs_thread structure - * returns: 0 - */ -static int lbs_thread(void *data) -{ - struct net_device *dev = data; - struct lbs_private *priv = dev->ml_priv; - wait_queue_t wait; - - lbs_deb_enter(LBS_DEB_THREAD); - - init_waitqueue_entry(&wait, current); - - for (;;) { - int shouldsleep; - u8 resp_idx; - - lbs_deb_thread("1: currenttxskb %p, dnld_sent %d\n", - priv->currenttxskb, priv->dnld_sent); - - add_wait_queue(&priv->waitq, &wait); - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irq(&priv->driver_lock); - - if (kthread_should_stop()) - shouldsleep = 0; /* Bye */ - else if (priv->surpriseremoved) - shouldsleep = 1; /* We need to wait until we're _told_ to die */ - else if (priv->psstate == PS_STATE_SLEEP) - shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ - else if (priv->cmd_timed_out) - shouldsleep = 0; /* Command timed out. Recover */ - else if (!priv->fw_ready) - shouldsleep = 1; /* Firmware not ready. We're waiting for it */ - else if (priv->dnld_sent) - shouldsleep = 1; /* Something is en route to the device already */ - else if (priv->tx_pending_len > 0) - shouldsleep = 0; /* We've a packet to send */ - else if (priv->resp_len[priv->resp_idx]) - shouldsleep = 0; /* We have a command response */ - else if (priv->cur_cmd) - shouldsleep = 1; /* Can't send a command; one already running */ - else if (!list_empty(&priv->cmdpendingq) && - !(priv->wakeup_dev_required)) - shouldsleep = 0; /* We have a command to send */ - else if (kfifo_len(&priv->event_fifo)) - shouldsleep = 0; /* We have an event to process */ - else - shouldsleep = 1; /* No command */ - - if (shouldsleep) { - lbs_deb_thread("sleeping, connect_status %d, " - "psmode %d, psstate %d\n", - priv->connect_status, - priv->psmode, priv->psstate); - spin_unlock_irq(&priv->driver_lock); - schedule(); - } else - spin_unlock_irq(&priv->driver_lock); - - lbs_deb_thread("2: currenttxskb %p, dnld_send %d\n", - priv->currenttxskb, priv->dnld_sent); - - set_current_state(TASK_RUNNING); - remove_wait_queue(&priv->waitq, &wait); - - lbs_deb_thread("3: currenttxskb %p, dnld_sent %d\n", - priv->currenttxskb, priv->dnld_sent); - - if (kthread_should_stop()) { - lbs_deb_thread("break from main thread\n"); - break; - } - - if (priv->surpriseremoved) { - lbs_deb_thread("adapter removed; waiting to die...\n"); - continue; - } - - lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n", - priv->currenttxskb, priv->dnld_sent); - - /* Process any pending command response */ - spin_lock_irq(&priv->driver_lock); - resp_idx = priv->resp_idx; - if (priv->resp_len[resp_idx]) { - spin_unlock_irq(&priv->driver_lock); - lbs_process_command_response(priv, - priv->resp_buf[resp_idx], - priv->resp_len[resp_idx]); - spin_lock_irq(&priv->driver_lock); - priv->resp_len[resp_idx] = 0; - } - spin_unlock_irq(&priv->driver_lock); - - /* Process hardware events, e.g. card removed, link lost */ - spin_lock_irq(&priv->driver_lock); - while (kfifo_len(&priv->event_fifo)) { - u32 event; - - if (kfifo_out(&priv->event_fifo, - (unsigned char *) &event, sizeof(event)) != - sizeof(event)) - break; - spin_unlock_irq(&priv->driver_lock); - lbs_process_event(priv, event); - spin_lock_irq(&priv->driver_lock); - } - spin_unlock_irq(&priv->driver_lock); - - if (priv->wakeup_dev_required) { - lbs_deb_thread("Waking up device...\n"); - /* Wake up device */ - if (priv->exit_deep_sleep(priv)) - lbs_deb_thread("Wakeup device failed\n"); - continue; - } - - /* command timeout stuff */ - if (priv->cmd_timed_out && priv->cur_cmd) { - struct cmd_ctrl_node *cmdnode = priv->cur_cmd; - - netdev_info(dev, "Timeout submitting command 0x%04x\n", - le16_to_cpu(cmdnode->cmdbuf->command)); - lbs_complete_command(priv, cmdnode, -ETIMEDOUT); - - /* Reset card, but only when it isn't in the process - * of being shutdown anyway. */ - if (!dev->dismantle && priv->reset_card) - priv->reset_card(priv); - } - priv->cmd_timed_out = 0; - - if (!priv->fw_ready) - continue; - - /* Check if we need to confirm Sleep Request received previously */ - if (priv->psstate == PS_STATE_PRE_SLEEP && - !priv->dnld_sent && !priv->cur_cmd) { - if (priv->connect_status == LBS_CONNECTED) { - lbs_deb_thread("pre-sleep, currenttxskb %p, " - "dnld_sent %d, cur_cmd %p\n", - priv->currenttxskb, priv->dnld_sent, - priv->cur_cmd); - - lbs_ps_confirm_sleep(priv); - } else { - /* workaround for firmware sending - * deauth/linkloss event immediately - * after sleep request; remove this - * after firmware fixes it - */ - priv->psstate = PS_STATE_AWAKE; - netdev_alert(dev, - "ignore PS_SleepConfirm in non-connected state\n"); - } - } - - /* The PS state is changed during processing of Sleep Request - * event above - */ - if ((priv->psstate == PS_STATE_SLEEP) || - (priv->psstate == PS_STATE_PRE_SLEEP)) - continue; - - if (priv->is_deep_sleep) - continue; - - /* Execute the next command */ - if (!priv->dnld_sent && !priv->cur_cmd) - lbs_execute_next_command(priv); - - spin_lock_irq(&priv->driver_lock); - if (!priv->dnld_sent && priv->tx_pending_len > 0) { - int ret = priv->hw_host_to_card(priv, MVMS_DAT, - priv->tx_pending_buf, - priv->tx_pending_len); - if (ret) { - lbs_deb_tx("host_to_card failed %d\n", ret); - priv->dnld_sent = DNLD_RES_RECEIVED; - } else { - mod_timer(&priv->tx_lockup_timer, - jiffies + (HZ * 5)); - } - priv->tx_pending_len = 0; - if (!priv->currenttxskb) { - /* We can wake the queues immediately if we aren't - waiting for TX feedback */ - if (priv->connect_status == LBS_CONNECTED) - netif_wake_queue(priv->dev); - if (priv->mesh_dev && - netif_running(priv->mesh_dev)) - netif_wake_queue(priv->mesh_dev); - } - } - spin_unlock_irq(&priv->driver_lock); - } - - del_timer(&priv->command_timer); - del_timer(&priv->tx_lockup_timer); - del_timer(&priv->auto_deepsleep_timer); - - lbs_deb_leave(LBS_DEB_THREAD); - return 0; -} - -/** - * lbs_setup_firmware - gets the HW spec from the firmware and sets - * some basic parameters - * - * @priv: A pointer to &struct lbs_private structure - * returns: 0 or -1 - */ -static int lbs_setup_firmware(struct lbs_private *priv) -{ - int ret = -1; - s16 curlevel = 0, minlevel = 0, maxlevel = 0; - - lbs_deb_enter(LBS_DEB_FW); - - /* Read MAC address from firmware */ - eth_broadcast_addr(priv->current_addr); - ret = lbs_update_hw_spec(priv); - if (ret) - goto done; - - /* Read power levels if available */ - ret = lbs_get_tx_power(priv, &curlevel, &minlevel, &maxlevel); - if (ret == 0) { - priv->txpower_cur = curlevel; - priv->txpower_min = minlevel; - priv->txpower_max = maxlevel; - } - - /* Send cmd to FW to enable 11D function */ - ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1); - if (ret) - goto done; - - ret = lbs_set_mac_control_sync(priv); -done: - lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); - return ret; -} - -int lbs_suspend(struct lbs_private *priv) -{ - int ret; - - lbs_deb_enter(LBS_DEB_FW); - - if (priv->is_deep_sleep) { - ret = lbs_set_deep_sleep(priv, 0); - if (ret) { - netdev_err(priv->dev, - "deep sleep cancellation failed: %d\n", ret); - return ret; - } - priv->deep_sleep_required = 1; - } - - ret = lbs_set_host_sleep(priv, 1); - - netif_device_detach(priv->dev); - if (priv->mesh_dev) - netif_device_detach(priv->mesh_dev); - - lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(lbs_suspend); - -int lbs_resume(struct lbs_private *priv) -{ - int ret; - - lbs_deb_enter(LBS_DEB_FW); - - ret = lbs_set_host_sleep(priv, 0); - - netif_device_attach(priv->dev); - if (priv->mesh_dev) - netif_device_attach(priv->mesh_dev); - - if (priv->deep_sleep_required) { - priv->deep_sleep_required = 0; - ret = lbs_set_deep_sleep(priv, 1); - if (ret) - netdev_err(priv->dev, - "deep sleep activation failed: %d\n", ret); - } - - if (priv->setup_fw_on_resume) - ret = lbs_setup_firmware(priv); - - lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(lbs_resume); - -/** - * lbs_cmd_timeout_handler - handles the timeout of command sending. - * It will re-send the same command again. - * - * @data: &struct lbs_private pointer - */ -static void lbs_cmd_timeout_handler(unsigned long data) -{ - struct lbs_private *priv = (struct lbs_private *)data; - unsigned long flags; - - lbs_deb_enter(LBS_DEB_CMD); - spin_lock_irqsave(&priv->driver_lock, flags); - - if (!priv->cur_cmd) - goto out; - - netdev_info(priv->dev, "command 0x%04x timed out\n", - le16_to_cpu(priv->cur_cmd->cmdbuf->command)); - - priv->cmd_timed_out = 1; - - /* - * If the device didn't even acknowledge the command, reset the state - * so that we don't block all future commands due to this one timeout. - */ - if (priv->dnld_sent == DNLD_CMD_SENT) - priv->dnld_sent = DNLD_RES_RECEIVED; - - wake_up(&priv->waitq); -out: - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbs_deb_leave(LBS_DEB_CMD); -} - -/** - * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames - * to the hardware. This is known to frequently happen with SD8686 when - * waking up after a Wake-on-WLAN-triggered resume. - * - * @data: &struct lbs_private pointer - */ -static void lbs_tx_lockup_handler(unsigned long data) -{ - struct lbs_private *priv = (struct lbs_private *)data; - unsigned long flags; - - lbs_deb_enter(LBS_DEB_TX); - spin_lock_irqsave(&priv->driver_lock, flags); - - netdev_info(priv->dev, "TX lockup detected\n"); - if (priv->reset_card) - priv->reset_card(priv); - - priv->dnld_sent = DNLD_RES_RECEIVED; - wake_up_interruptible(&priv->waitq); - - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbs_deb_leave(LBS_DEB_TX); -} - -/** - * auto_deepsleep_timer_fn - put the device back to deep sleep mode when - * timer expires and no activity (command, event, data etc.) is detected. - * @data: &struct lbs_private pointer - * returns: N/A - */ -static void auto_deepsleep_timer_fn(unsigned long data) -{ - struct lbs_private *priv = (struct lbs_private *)data; - - lbs_deb_enter(LBS_DEB_CMD); - - if (priv->is_activity_detected) { - priv->is_activity_detected = 0; - } else { - if (priv->is_auto_deep_sleep_enabled && - (!priv->wakeup_dev_required) && - (priv->connect_status != LBS_CONNECTED)) { - struct cmd_header cmd; - - lbs_deb_main("Entering auto deep sleep mode...\n"); - memset(&cmd, 0, sizeof(cmd)); - cmd.size = cpu_to_le16(sizeof(cmd)); - lbs_cmd_async(priv, CMD_802_11_DEEP_SLEEP, &cmd, - sizeof(cmd)); - } - } - mod_timer(&priv->auto_deepsleep_timer , jiffies + - (priv->auto_deep_sleep_timeout * HZ)/1000); - lbs_deb_leave(LBS_DEB_CMD); -} - -int lbs_enter_auto_deep_sleep(struct lbs_private *priv) -{ - lbs_deb_enter(LBS_DEB_SDIO); - - priv->is_auto_deep_sleep_enabled = 1; - if (priv->is_deep_sleep) - priv->wakeup_dev_required = 1; - mod_timer(&priv->auto_deepsleep_timer , - jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000); - - lbs_deb_leave(LBS_DEB_SDIO); - return 0; -} - -int lbs_exit_auto_deep_sleep(struct lbs_private *priv) -{ - lbs_deb_enter(LBS_DEB_SDIO); - - priv->is_auto_deep_sleep_enabled = 0; - priv->auto_deep_sleep_timeout = 0; - del_timer(&priv->auto_deepsleep_timer); - - lbs_deb_leave(LBS_DEB_SDIO); - return 0; -} - -static int lbs_init_adapter(struct lbs_private *priv) -{ - int ret; - - lbs_deb_enter(LBS_DEB_MAIN); - - eth_broadcast_addr(priv->current_addr); - - priv->connect_status = LBS_DISCONNECTED; - priv->channel = DEFAULT_AD_HOC_CHANNEL; - priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; - priv->radio_on = 1; - priv->psmode = LBS802_11POWERMODECAM; - priv->psstate = PS_STATE_FULL_POWER; - priv->is_deep_sleep = 0; - priv->is_auto_deep_sleep_enabled = 0; - priv->deep_sleep_required = 0; - priv->wakeup_dev_required = 0; - init_waitqueue_head(&priv->ds_awake_q); - init_waitqueue_head(&priv->scan_q); - priv->authtype_auto = 1; - priv->is_host_sleep_configured = 0; - priv->is_host_sleep_activated = 0; - init_waitqueue_head(&priv->host_sleep_q); - init_waitqueue_head(&priv->fw_waitq); - mutex_init(&priv->lock); - - setup_timer(&priv->command_timer, lbs_cmd_timeout_handler, - (unsigned long)priv); - setup_timer(&priv->tx_lockup_timer, lbs_tx_lockup_handler, - (unsigned long)priv); - setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, - (unsigned long)priv); - - INIT_LIST_HEAD(&priv->cmdfreeq); - INIT_LIST_HEAD(&priv->cmdpendingq); - - spin_lock_init(&priv->driver_lock); - - /* Allocate the command buffers */ - if (lbs_allocate_cmd_buffer(priv)) { - pr_err("Out of memory allocating command buffers\n"); - ret = -ENOMEM; - goto out; - } - priv->resp_idx = 0; - priv->resp_len[0] = priv->resp_len[1] = 0; - - /* Create the event FIFO */ - ret = kfifo_alloc(&priv->event_fifo, sizeof(u32) * 16, GFP_KERNEL); - if (ret) { - pr_err("Out of memory allocating event FIFO buffer\n"); - goto out; - } - -out: - lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); - - return ret; -} - -static void lbs_free_adapter(struct lbs_private *priv) -{ - lbs_deb_enter(LBS_DEB_MAIN); - - lbs_free_cmd_buffer(priv); - kfifo_free(&priv->event_fifo); - del_timer(&priv->command_timer); - del_timer(&priv->tx_lockup_timer); - del_timer(&priv->auto_deepsleep_timer); - - lbs_deb_leave(LBS_DEB_MAIN); -} - -static const struct net_device_ops lbs_netdev_ops = { - .ndo_open = lbs_dev_open, - .ndo_stop = lbs_eth_stop, - .ndo_start_xmit = lbs_hard_start_xmit, - .ndo_set_mac_address = lbs_set_mac_address, - .ndo_set_rx_mode = lbs_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, - .ndo_validate_addr = eth_validate_addr, -}; - -/** - * lbs_add_card - adds the card. It will probe the - * card, allocate the lbs_priv and initialize the device. - * - * @card: A pointer to card - * @dmdev: A pointer to &struct device - * returns: A pointer to &struct lbs_private structure - */ -struct lbs_private *lbs_add_card(void *card, struct device *dmdev) -{ - struct net_device *dev; - struct wireless_dev *wdev; - struct lbs_private *priv = NULL; - - lbs_deb_enter(LBS_DEB_MAIN); - - /* Allocate an Ethernet device and register it */ - wdev = lbs_cfg_alloc(dmdev); - if (IS_ERR(wdev)) { - pr_err("cfg80211 init failed\n"); - goto done; - } - - wdev->iftype = NL80211_IFTYPE_STATION; - priv = wdev_priv(wdev); - priv->wdev = wdev; - - if (lbs_init_adapter(priv)) { - pr_err("failed to initialize adapter structure\n"); - goto err_wdev; - } - - dev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup); - if (!dev) { - dev_err(dmdev, "no memory for network device instance\n"); - goto err_adapter; - } - - dev->ieee80211_ptr = wdev; - dev->ml_priv = priv; - SET_NETDEV_DEV(dev, dmdev); - wdev->netdev = dev; - priv->dev = dev; - - dev->netdev_ops = &lbs_netdev_ops; - dev->watchdog_timeo = 5 * HZ; - dev->ethtool_ops = &lbs_ethtool_ops; - dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - - priv->card = card; - - strcpy(dev->name, "wlan%d"); - - lbs_deb_thread("Starting main thread...\n"); - init_waitqueue_head(&priv->waitq); - priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main"); - if (IS_ERR(priv->main_thread)) { - lbs_deb_thread("Error creating main thread.\n"); - goto err_ndev; - } - - priv->work_thread = create_singlethread_workqueue("lbs_worker"); - INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker); - - priv->wol_criteria = EHS_REMOVE_WAKEUP; - priv->wol_gpio = 0xff; - priv->wol_gap = 20; - priv->ehs_remove_supported = true; - - goto done; - - err_ndev: - free_netdev(dev); - - err_adapter: - lbs_free_adapter(priv); - - err_wdev: - lbs_cfg_free(priv); - - priv = NULL; - -done: - lbs_deb_leave_args(LBS_DEB_MAIN, "priv %p", priv); - return priv; -} -EXPORT_SYMBOL_GPL(lbs_add_card); - - -void lbs_remove_card(struct lbs_private *priv) -{ - struct net_device *dev = priv->dev; - - lbs_deb_enter(LBS_DEB_MAIN); - - lbs_remove_mesh(priv); - - if (priv->wiphy_registered) - lbs_scan_deinit(priv); - - lbs_wait_for_firmware_load(priv); - - /* worker thread destruction blocks on the in-flight command which - * should have been cleared already in lbs_stop_card(). - */ - lbs_deb_main("destroying worker thread\n"); - destroy_workqueue(priv->work_thread); - lbs_deb_main("done destroying worker thread\n"); - - if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { - priv->psmode = LBS802_11POWERMODECAM; - lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, true); - } - - if (priv->is_deep_sleep) { - priv->is_deep_sleep = 0; - wake_up_interruptible(&priv->ds_awake_q); - } - - priv->is_host_sleep_configured = 0; - priv->is_host_sleep_activated = 0; - wake_up_interruptible(&priv->host_sleep_q); - - /* Stop the thread servicing the interrupts */ - priv->surpriseremoved = 1; - kthread_stop(priv->main_thread); - - lbs_free_adapter(priv); - lbs_cfg_free(priv); - free_netdev(dev); - - lbs_deb_leave(LBS_DEB_MAIN); -} -EXPORT_SYMBOL_GPL(lbs_remove_card); - - -int lbs_rtap_supported(struct lbs_private *priv) -{ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) - return 1; - - /* newer firmware use a capability mask */ - return ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && - (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)); -} - - -int lbs_start_card(struct lbs_private *priv) -{ - struct net_device *dev = priv->dev; - int ret = -1; - - lbs_deb_enter(LBS_DEB_MAIN); - - /* poke the firmware */ - ret = lbs_setup_firmware(priv); - if (ret) - goto done; - - if (!lbs_disablemesh) - lbs_init_mesh(priv); - else - pr_info("%s: mesh disabled\n", dev->name); - - if (lbs_cfg_register(priv)) { - pr_err("cannot register device\n"); - goto done; - } - - if (lbs_mesh_activated(priv)) - lbs_start_mesh(priv); - - lbs_debugfs_init_one(priv, dev); - - netdev_info(dev, "Marvell WLAN 802.11 adapter\n"); - - ret = 0; - -done: - lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(lbs_start_card); - - -void lbs_stop_card(struct lbs_private *priv) -{ - struct net_device *dev; - - lbs_deb_enter(LBS_DEB_MAIN); - - if (!priv) - goto out; - dev = priv->dev; - - /* If the netdev isn't registered, it means that lbs_start_card() was - * never called so we have nothing to do here. */ - if (dev->reg_state != NETREG_REGISTERED) - goto out; - - netif_stop_queue(dev); - netif_carrier_off(dev); - - lbs_debugfs_remove_one(priv); - lbs_deinit_mesh(priv); - unregister_netdev(dev); - -out: - lbs_deb_leave(LBS_DEB_MAIN); -} -EXPORT_SYMBOL_GPL(lbs_stop_card); - - -void lbs_queue_event(struct lbs_private *priv, u32 event) -{ - unsigned long flags; - - lbs_deb_enter(LBS_DEB_THREAD); - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->psstate == PS_STATE_SLEEP) - priv->psstate = PS_STATE_AWAKE; - - kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32)); - - wake_up(&priv->waitq); - - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbs_deb_leave(LBS_DEB_THREAD); -} -EXPORT_SYMBOL_GPL(lbs_queue_event); - -void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx) -{ - lbs_deb_enter(LBS_DEB_THREAD); - - if (priv->psstate == PS_STATE_SLEEP) - priv->psstate = PS_STATE_AWAKE; - - /* Swap buffers by flipping the response index */ - BUG_ON(resp_idx > 1); - priv->resp_idx = resp_idx; - - wake_up(&priv->waitq); - - lbs_deb_leave(LBS_DEB_THREAD); -} -EXPORT_SYMBOL_GPL(lbs_notify_command_response); - -static int __init lbs_init_module(void) -{ - lbs_deb_enter(LBS_DEB_MAIN); - memset(&confirm_sleep, 0, sizeof(confirm_sleep)); - confirm_sleep.hdr.command = cpu_to_le16(CMD_802_11_PS_MODE); - confirm_sleep.hdr.size = cpu_to_le16(sizeof(confirm_sleep)); - confirm_sleep.action = cpu_to_le16(PS_MODE_ACTION_SLEEP_CONFIRMED); - lbs_debugfs_init(); - lbs_deb_leave(LBS_DEB_MAIN); - return 0; -} - -static void __exit lbs_exit_module(void) -{ - lbs_deb_enter(LBS_DEB_MAIN); - lbs_debugfs_remove(); - lbs_deb_leave(LBS_DEB_MAIN); -} - -module_init(lbs_init_module); -module_exit(lbs_exit_module); - -MODULE_DESCRIPTION("Libertas WLAN Driver Library"); -MODULE_AUTHOR("Marvell International Ltd."); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c deleted file mode 100644 index d0c881dd5846..000000000000 --- a/drivers/net/wireless/libertas/mesh.c +++ /dev/null @@ -1,1187 +0,0 @@ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mesh.h" -#include "decl.h" -#include "cmd.h" - - -static int lbs_add_mesh(struct lbs_private *priv); - -/*************************************************************************** - * Mesh command handling - */ - -static int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, - struct cmd_ds_mesh_access *cmd) -{ - int ret; - - lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); - - cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS); - cmd->hdr.size = cpu_to_le16(sizeof(*cmd)); - cmd->hdr.result = 0; - - cmd->action = cpu_to_le16(cmd_action); - - ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -static int __lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type) -{ - int ret; - u16 command = CMD_MESH_CONFIG_OLD; - - lbs_deb_enter(LBS_DEB_CMD); - - /* - * Command id is 0xac for v10 FW along with mesh interface - * id in bits 14-13-12. - */ - if (priv->mesh_tlv == TLV_TYPE_MESH_ID) - command = CMD_MESH_CONFIG | - (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET); - - cmd->hdr.command = cpu_to_le16(command); - cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config)); - cmd->hdr.result = 0; - - cmd->type = cpu_to_le16(type); - cmd->action = cpu_to_le16(action); - - ret = lbs_cmd_with_response(priv, command, cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -static int lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type) -{ - int ret; - - if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) - return -EOPNOTSUPP; - - ret = __lbs_mesh_config_send(priv, cmd, action, type); - return ret; -} - -/* This function is the CMD_MESH_CONFIG legacy function. It only handles the - * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG - * are all handled by preparing a struct cmd_ds_mesh_config and passing it to - * lbs_mesh_config_send. - */ -static int lbs_mesh_config(struct lbs_private *priv, uint16_t action, - uint16_t chan) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_meshie *ie; - - memset(&cmd, 0, sizeof(cmd)); - cmd.channel = cpu_to_le16(chan); - ie = (struct mrvl_meshie *)cmd.data; - - switch (action) { - case CMD_ACT_MESH_CONFIG_START: - ie->id = WLAN_EID_VENDOR_SPECIFIC; - ie->val.oui[0] = 0x00; - ie->val.oui[1] = 0x50; - ie->val.oui[2] = 0x43; - ie->val.type = MARVELL_MESH_IE_TYPE; - ie->val.subtype = MARVELL_MESH_IE_SUBTYPE; - ie->val.version = MARVELL_MESH_IE_VERSION; - ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP; - ie->val.active_metric_id = MARVELL_MESH_METRIC_ID; - ie->val.mesh_capability = MARVELL_MESH_CAPABILITY; - ie->val.mesh_id_len = priv->mesh_ssid_len; - memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len); - ie->len = sizeof(struct mrvl_meshie_val) - - IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len; - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val)); - break; - case CMD_ACT_MESH_CONFIG_STOP: - break; - default: - return -1; - } - lbs_deb_cmd("mesh config action %d type %x channel %d SSID %*pE\n", - action, priv->mesh_tlv, chan, priv->mesh_ssid_len, - priv->mesh_ssid); - - return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); -} - -int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel) -{ - priv->mesh_channel = channel; - return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel); -} - -static uint16_t lbs_mesh_get_channel(struct lbs_private *priv) -{ - return priv->mesh_channel ?: 1; -} - -/*************************************************************************** - * Mesh sysfs support - */ - -/* - * Attributes exported through sysfs - */ - -/** - * lbs_anycast_get - Get function for sysfs attribute anycast_mask - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t lbs_anycast_get(struct device *dev, - struct device_attribute *attr, char * buf) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - - memset(&mesh_access, 0, sizeof(mesh_access)); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access); - if (ret) - return ret; - - return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0])); -} - -/** - * lbs_anycast_set - Set function for sysfs attribute anycast_mask - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t lbs_anycast_set(struct device *dev, - struct device_attribute *attr, const char * buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - uint32_t datum; - int ret; - - memset(&mesh_access, 0, sizeof(mesh_access)); - sscanf(buf, "%x", &datum); - mesh_access.data[0] = cpu_to_le32(datum); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * lbs_prb_rsp_limit_get - Get function for sysfs attribute prb_rsp_limit - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t lbs_prb_rsp_limit_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - u32 retry_limit; - - memset(&mesh_access, 0, sizeof(mesh_access)); - mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, - &mesh_access); - if (ret) - return ret; - - retry_limit = le32_to_cpu(mesh_access.data[1]); - return snprintf(buf, 10, "%d\n", retry_limit); -} - -/** - * lbs_prb_rsp_limit_set - Set function for sysfs attribute prb_rsp_limit - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t lbs_prb_rsp_limit_set(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - unsigned long retry_limit; - - memset(&mesh_access, 0, sizeof(mesh_access)); - mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET); - - if (!kstrtoul(buf, 10, &retry_limit)) - return -ENOTSUPP; - if (retry_limit > 15) - return -ENOTSUPP; - - mesh_access.data[1] = cpu_to_le32(retry_limit); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, - &mesh_access); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * lbs_mesh_get - Get function for sysfs attribute mesh - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t lbs_mesh_get(struct device *dev, - struct device_attribute *attr, char * buf) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev); -} - -/** - * lbs_mesh_set - Set function for sysfs attribute mesh - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t lbs_mesh_set(struct device *dev, - struct device_attribute *attr, const char * buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - int enable; - - sscanf(buf, "%x", &enable); - enable = !!enable; - if (enable == !!priv->mesh_dev) - return count; - - if (enable) - lbs_add_mesh(priv); - else - lbs_remove_mesh(priv); - - return count; -} - -/* - * lbs_mesh attribute to be exported per ethX interface - * through sysfs (/sys/class/net/ethX/lbs_mesh) - */ -static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set); - -/* - * anycast_mask attribute to be exported per mshX interface - * through sysfs (/sys/class/net/mshX/anycast_mask) - */ -static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set); - -/* - * prb_rsp_limit attribute to be exported per mshX interface - * through sysfs (/sys/class/net/mshX/prb_rsp_limit) - */ -static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get, - lbs_prb_rsp_limit_set); - -static struct attribute *lbs_mesh_sysfs_entries[] = { - &dev_attr_anycast_mask.attr, - &dev_attr_prb_rsp_limit.attr, - NULL, -}; - -static const struct attribute_group lbs_mesh_attr_group = { - .attrs = lbs_mesh_sysfs_entries, -}; - - -/*************************************************************************** - * Persistent configuration support - */ - -static int mesh_get_default_parameters(struct device *dev, - struct mrvl_mesh_defaults *defs) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - int ret; - - memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET, - CMD_TYPE_MESH_GET_DEFAULTS); - - if (ret) - return -EOPNOTSUPP; - - memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults)); - - return 0; -} - -/** - * bootflag_get - Get function for sysfs attribute bootflag - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t bootflag_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag)); -} - -/** - * bootflag_set - Set function for sysfs attribute bootflag - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 1)) - return -EINVAL; - - *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum); - cmd.length = cpu_to_le16(sizeof(uint32_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_BOOTFLAG); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * boottime_get - Get function for sysfs attribute boottime - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t boottime_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 12, "%d\n", defs.boottime); -} - -/** - * boottime_set - Set function for sysfs attribute boottime - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t boottime_set(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* A too small boot time will result in the device booting into - * standalone (no-host) mode before the host can take control of it, - * so the change will be hard to revert. This may be a desired - * feature (e.g to configure a very fast boot time for devices that - * will not be attached to a host), but dangerous. So I'm enforcing a - * lower limit of 20 seconds: remove and recompile the driver if this - * does not work for you. - */ - datum = (datum < 20) ? 20 : datum; - cmd.data[0] = datum; - cmd.length = cpu_to_le16(sizeof(uint8_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_BOOTTIME); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * channel_get - Get function for sysfs attribute channel - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t channel_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel)); -} - -/** - * channel_set - Set function for sysfs attribute channel - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t channel_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if (ret != 1 || datum < 1 || datum > 11) - return -EINVAL; - - *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum); - cmd.length = cpu_to_le16(sizeof(uint16_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_DEF_CHANNEL); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * mesh_id_get - Get function for sysfs attribute mesh_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) { - dev_err(dev, "inconsistent mesh ID length\n"); - defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN; - } - - memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len); - buf[defs.meshie.val.mesh_id_len] = '\n'; - buf[defs.meshie.val.mesh_id_len + 1] = '\0'; - - return defs.meshie.val.mesh_id_len + 1; -} - -/** - * mesh_id_set - Set function for sysfs attribute mesh_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - int len; - int ret; - - if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1) - return -EINVAL; - - memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); - ie = (struct mrvl_meshie *) &cmd.data[0]; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - - len = count - 1; - memcpy(ie->val.mesh_id, buf, len); - /* SSID len */ - ie->val.mesh_id_len = len; - /* IE len */ - ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * protocol_id_get - Get function for sysfs attribute protocol_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t protocol_id_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id); -} - -/** - * protocol_id_set - Set function for sysfs attribute protocol_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t protocol_id_set(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update protocol id */ - ie->val.active_protocol_id = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * metric_id_get - Get function for sysfs attribute metric_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t metric_id_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id); -} - -/** - * metric_id_set - Set function for sysfs attribute metric_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update metric id */ - ie->val.active_metric_id = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * capability_get - Get function for sysfs attribute capability - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t capability_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability); -} - -/** - * capability_set - Set function for sysfs attribute capability - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t capability_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update value */ - ie->val.mesh_capability = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - - -static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set); -static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set); -static DEVICE_ATTR(channel, 0644, channel_get, channel_set); -static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set); -static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set); -static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set); -static DEVICE_ATTR(capability, 0644, capability_get, capability_set); - -static struct attribute *boot_opts_attrs[] = { - &dev_attr_bootflag.attr, - &dev_attr_boottime.attr, - &dev_attr_channel.attr, - NULL -}; - -static const struct attribute_group boot_opts_group = { - .name = "boot_options", - .attrs = boot_opts_attrs, -}; - -static struct attribute *mesh_ie_attrs[] = { - &dev_attr_mesh_id.attr, - &dev_attr_protocol_id.attr, - &dev_attr_metric_id.attr, - &dev_attr_capability.attr, - NULL -}; - -static const struct attribute_group mesh_ie_group = { - .name = "mesh_ie", - .attrs = mesh_ie_attrs, -}; - -static void lbs_persist_config_init(struct net_device *dev) -{ - int ret; - ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group); - ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group); -} - -static void lbs_persist_config_remove(struct net_device *dev) -{ - sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group); - sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group); -} - - -/*************************************************************************** - * Initializing and starting, stopping mesh - */ - -/* - * Check mesh FW version and appropriately send the mesh start - * command - */ -int lbs_init_mesh(struct lbs_private *priv) -{ - int ret = 0; - - lbs_deb_enter(LBS_DEB_MESH); - - /* Determine mesh_fw_ver from fwrelease and fwcapinfo */ - /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */ - /* 5.110.22 have mesh command with 0xa3 command id */ - /* 10.0.0.p0 FW brings in mesh config command with different id */ - /* Check FW version MSB and initialize mesh_fw_ver */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) { - /* Enable mesh, if supported, and work out which TLV it uses. - 0x100 + 291 is an unofficial value used in 5.110.20.pXX - 0x100 + 37 is the official value used in 5.110.21.pXX - but we check them in that order because 20.pXX doesn't - give an error -- it just silently fails. */ - - /* 5.110.20.pXX firmware will fail the command if the channel - doesn't match the existing channel. But only if the TLV - is correct. If the channel is wrong, _BOTH_ versions will - give an error to 0x100+291, and allow 0x100+37 to succeed. - It's just that 5.110.20.pXX will not have done anything - useful */ - - priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID; - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) { - priv->mesh_tlv = TLV_TYPE_MESH_ID; - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) - priv->mesh_tlv = 0; - } - } else - if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && - (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) { - /* 10.0.0.pXX new firmwares should succeed with TLV - * 0x100+37; Do not invoke command with old TLV. - */ - priv->mesh_tlv = TLV_TYPE_MESH_ID; - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) - priv->mesh_tlv = 0; - } - - /* Stop meshing until interface is brought up */ - lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1); - - if (priv->mesh_tlv) { - sprintf(priv->mesh_ssid, "mesh"); - priv->mesh_ssid_len = 4; - ret = 1; - } - - lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); - return ret; -} - -void lbs_start_mesh(struct lbs_private *priv) -{ - lbs_add_mesh(priv); - - if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh)) - netdev_err(priv->dev, "cannot register lbs_mesh attribute\n"); -} - -int lbs_deinit_mesh(struct lbs_private *priv) -{ - struct net_device *dev = priv->dev; - int ret = 0; - - lbs_deb_enter(LBS_DEB_MESH); - - if (priv->mesh_tlv) { - device_remove_file(&dev->dev, &dev_attr_lbs_mesh); - ret = 1; - } - - lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); - return ret; -} - - -/** - * lbs_mesh_stop - close the mshX interface - * - * @dev: A pointer to &net_device structure - * returns: 0 - */ -static int lbs_mesh_stop(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - - lbs_deb_enter(LBS_DEB_MESH); - lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, - lbs_mesh_get_channel(priv)); - - spin_lock_irq(&priv->driver_lock); - - netif_stop_queue(dev); - netif_carrier_off(dev); - - spin_unlock_irq(&priv->driver_lock); - - lbs_update_mcast(priv); - if (!lbs_iface_active(priv)) - lbs_stop_iface(priv); - - lbs_deb_leave(LBS_DEB_MESH); - return 0; -} - -/** - * lbs_mesh_dev_open - open the mshX interface - * - * @dev: A pointer to &net_device structure - * returns: 0 or -EBUSY if monitor mode active - */ -static int lbs_mesh_dev_open(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - int ret = 0; - - lbs_deb_enter(LBS_DEB_NET); - if (!priv->iface_running) { - ret = lbs_start_iface(priv); - if (ret) - goto out; - } - - spin_lock_irq(&priv->driver_lock); - - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { - ret = -EBUSY; - spin_unlock_irq(&priv->driver_lock); - goto out; - } - - netif_carrier_on(dev); - - if (!priv->tx_pending_len) - netif_wake_queue(dev); - - spin_unlock_irq(&priv->driver_lock); - - ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, - lbs_mesh_get_channel(priv)); - -out: - lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); - return ret; -} - -static const struct net_device_ops mesh_netdev_ops = { - .ndo_open = lbs_mesh_dev_open, - .ndo_stop = lbs_mesh_stop, - .ndo_start_xmit = lbs_hard_start_xmit, - .ndo_set_mac_address = lbs_set_mac_address, - .ndo_set_rx_mode = lbs_set_multicast_list, -}; - -/** - * lbs_add_mesh - add mshX interface - * - * @priv: A pointer to the &struct lbs_private structure - * returns: 0 if successful, -X otherwise - */ -static int lbs_add_mesh(struct lbs_private *priv) -{ - struct net_device *mesh_dev = NULL; - struct wireless_dev *mesh_wdev; - int ret = 0; - - lbs_deb_enter(LBS_DEB_MESH); - - /* Allocate a virtual mesh device */ - mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); - if (!mesh_wdev) { - lbs_deb_mesh("init mshX wireless device failed\n"); - ret = -ENOMEM; - goto done; - } - - mesh_dev = alloc_netdev(0, "msh%d", NET_NAME_UNKNOWN, ether_setup); - if (!mesh_dev) { - lbs_deb_mesh("init mshX device failed\n"); - ret = -ENOMEM; - goto err_free_wdev; - } - - mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT; - mesh_wdev->wiphy = priv->wdev->wiphy; - mesh_wdev->netdev = mesh_dev; - - mesh_dev->ml_priv = priv; - mesh_dev->ieee80211_ptr = mesh_wdev; - priv->mesh_dev = mesh_dev; - - mesh_dev->netdev_ops = &mesh_netdev_ops; - mesh_dev->ethtool_ops = &lbs_ethtool_ops; - eth_hw_addr_inherit(mesh_dev, priv->dev); - - SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent); - - mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - /* Register virtual mesh interface */ - ret = register_netdev(mesh_dev); - if (ret) { - pr_err("cannot register mshX virtual interface\n"); - goto err_free_netdev; - } - - ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); - if (ret) - goto err_unregister; - - lbs_persist_config_init(mesh_dev); - - /* Everything successful */ - ret = 0; - goto done; - -err_unregister: - unregister_netdev(mesh_dev); - -err_free_netdev: - free_netdev(mesh_dev); - -err_free_wdev: - kfree(mesh_wdev); - -done: - lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); - return ret; -} - -void lbs_remove_mesh(struct lbs_private *priv) -{ - struct net_device *mesh_dev; - - mesh_dev = priv->mesh_dev; - if (!mesh_dev) - return; - - lbs_deb_enter(LBS_DEB_MESH); - netif_stop_queue(mesh_dev); - netif_carrier_off(mesh_dev); - sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); - lbs_persist_config_remove(mesh_dev); - unregister_netdev(mesh_dev); - priv->mesh_dev = NULL; - kfree(mesh_dev->ieee80211_ptr); - free_netdev(mesh_dev); - lbs_deb_leave(LBS_DEB_MESH); -} - - -/*************************************************************************** - * Sending and receiving - */ -struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, - struct net_device *dev, struct rxpd *rxpd) -{ - if (priv->mesh_dev) { - if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) { - if (rxpd->rx_control & RxPD_MESH_FRAME) - dev = priv->mesh_dev; - } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) { - if (rxpd->u.bss.bss_num == MESH_IFACE_ID) - dev = priv->mesh_dev; - } - } - return dev; -} - - -void lbs_mesh_set_txpd(struct lbs_private *priv, - struct net_device *dev, struct txpd *txpd) -{ - if (dev == priv->mesh_dev) { - if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) - txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME); - else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) - txpd->u.bss.bss_num = MESH_IFACE_ID; - } -} - - -/*************************************************************************** - * Ethtool related - */ - -static const char * const mesh_stat_strings[] = { - "drop_duplicate_bcast", - "drop_ttl_zero", - "drop_no_fwd_route", - "drop_no_buffers", - "fwded_unicast_cnt", - "fwded_bcast_cnt", - "drop_blind_table", - "tx_failed_cnt" -}; - -void lbs_mesh_ethtool_get_stats(struct net_device *dev, - struct ethtool_stats *stats, uint64_t *data) -{ - struct lbs_private *priv = dev->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - - lbs_deb_enter(LBS_DEB_ETHTOOL); - - /* Get Mesh Statistics */ - ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access); - - if (ret) { - memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t))); - return; - } - - priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]); - priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]); - priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]); - priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]); - priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]); - priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]); - priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]); - priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]); - - data[0] = priv->mstats.fwd_drop_rbt; - data[1] = priv->mstats.fwd_drop_ttl; - data[2] = priv->mstats.fwd_drop_noroute; - data[3] = priv->mstats.fwd_drop_nobuf; - data[4] = priv->mstats.fwd_unicast_cnt; - data[5] = priv->mstats.fwd_bcast_cnt; - data[6] = priv->mstats.drop_blind; - data[7] = priv->mstats.tx_failed_cnt; - - lbs_deb_enter(LBS_DEB_ETHTOOL); -} - -int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset) -{ - struct lbs_private *priv = dev->ml_priv; - - if (sset == ETH_SS_STATS && dev == priv->mesh_dev) - return MESH_STATS_NUM; - - return -EOPNOTSUPP; -} - -void lbs_mesh_ethtool_get_strings(struct net_device *dev, - uint32_t stringset, uint8_t *s) -{ - int i; - - lbs_deb_enter(LBS_DEB_ETHTOOL); - - switch (stringset) { - case ETH_SS_STATS: - for (i = 0; i < MESH_STATS_NUM; i++) { - memcpy(s + i * ETH_GSTRING_LEN, - mesh_stat_strings[i], - ETH_GSTRING_LEN); - } - break; - } - lbs_deb_enter(LBS_DEB_ETHTOOL); -} diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/libertas/mesh.h deleted file mode 100644 index 6603f341c874..000000000000 --- a/drivers/net/wireless/libertas/mesh.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Contains all definitions needed for the Libertas' MESH implementation. - */ -#ifndef _LBS_MESH_H_ -#define _LBS_MESH_H_ - - -#include -#include - -#include "host.h" -#include "dev.h" - -#ifdef CONFIG_LIBERTAS_MESH - -struct net_device; - -int lbs_init_mesh(struct lbs_private *priv); -void lbs_start_mesh(struct lbs_private *priv); -int lbs_deinit_mesh(struct lbs_private *priv); - -void lbs_remove_mesh(struct lbs_private *priv); - -static inline bool lbs_mesh_activated(struct lbs_private *priv) -{ - /* Mesh SSID is only programmed after successful init */ - return priv->mesh_ssid_len != 0; -} - -int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel); - -/* Sending / Receiving */ - -struct rxpd; -struct txpd; - -struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, - struct net_device *dev, struct rxpd *rxpd); -void lbs_mesh_set_txpd(struct lbs_private *priv, - struct net_device *dev, struct txpd *txpd); - - -/* Command handling */ - -struct cmd_ds_command; -struct cmd_ds_mesh_access; -struct cmd_ds_mesh_config; - - -/* Ethtool statistics */ - -struct ethtool_stats; - -void lbs_mesh_ethtool_get_stats(struct net_device *dev, - struct ethtool_stats *stats, uint64_t *data); -int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset); -void lbs_mesh_ethtool_get_strings(struct net_device *dev, - uint32_t stringset, uint8_t *s); - - -#else - -#define lbs_init_mesh(priv) -#define lbs_deinit_mesh(priv) -#define lbs_start_mesh(priv) -#define lbs_add_mesh(priv) -#define lbs_remove_mesh(priv) -#define lbs_mesh_set_dev(priv, dev, rxpd) (dev) -#define lbs_mesh_set_txpd(priv, dev, txpd) -#define lbs_mesh_set_channel(priv, channel) (0) -#define lbs_mesh_activated(priv) (false) - -#endif - - - -#endif diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h deleted file mode 100644 index b3c8ea6d610e..000000000000 --- a/drivers/net/wireless/libertas/radiotap.h +++ /dev/null @@ -1,44 +0,0 @@ -#include - -struct tx_radiotap_hdr { - struct ieee80211_radiotap_header hdr; - u8 rate; - u8 txpower; - u8 rts_retries; - u8 data_retries; -} __packed; - -#define TX_RADIOTAP_PRESENT ( \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ - (1 << IEEE80211_RADIOTAP_RTS_RETRIES) | \ - (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | \ - 0) - -#define IEEE80211_FC_VERSION_MASK 0x0003 -#define IEEE80211_FC_TYPE_MASK 0x000c -#define IEEE80211_FC_TYPE_MGT 0x0000 -#define IEEE80211_FC_TYPE_CTL 0x0004 -#define IEEE80211_FC_TYPE_DATA 0x0008 -#define IEEE80211_FC_SUBTYPE_MASK 0x00f0 -#define IEEE80211_FC_TOFROMDS_MASK 0x0300 -#define IEEE80211_FC_TODS_MASK 0x0100 -#define IEEE80211_FC_FROMDS_MASK 0x0200 -#define IEEE80211_FC_NODS 0x0000 -#define IEEE80211_FC_TODS 0x0100 -#define IEEE80211_FC_FROMDS 0x0200 -#define IEEE80211_FC_DSTODS 0x0300 - -struct rx_radiotap_hdr { - struct ieee80211_radiotap_header hdr; - u8 flags; - u8 rate; - u8 antsignal; -} __packed; - -#define RX_RADIOTAP_PRESENT ( \ - (1 << IEEE80211_RADIOTAP_FLAGS) | \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ - 0) - diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c deleted file mode 100644 index e446fed7b345..000000000000 --- a/drivers/net/wireless/libertas/rx.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * This file contains the handling of RX in wlan driver. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include "defs.h" -#include "host.h" -#include "radiotap.h" -#include "decl.h" -#include "dev.h" -#include "mesh.h" - -struct eth803hdr { - u8 dest_addr[6]; - u8 src_addr[6]; - u16 h803_len; -} __packed; - -struct rfc1042hdr { - u8 llc_dsap; - u8 llc_ssap; - u8 llc_ctrl; - u8 snap_oui[3]; - u16 snap_type; -} __packed; - -struct rxpackethdr { - struct eth803hdr eth803_hdr; - struct rfc1042hdr rfc1042_hdr; -} __packed; - -struct rx80211packethdr { - struct rxpd rx_pd; - void *eth80211_hdr; -} __packed; - -static int process_rxed_802_11_packet(struct lbs_private *priv, - struct sk_buff *skb); - -/** - * lbs_process_rxed_packet - processes received packet and forwards it - * to kernel/upper layer - * - * @priv: A pointer to &struct lbs_private - * @skb: A pointer to skb which includes the received packet - * returns: 0 or -1 - */ -int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) -{ - int ret = 0; - struct net_device *dev = priv->dev; - struct rxpackethdr *p_rx_pkt; - struct rxpd *p_rx_pd; - int hdrchop; - struct ethhdr *p_ethhdr; - static const u8 rfc1042_eth_hdr[] = { - 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 - }; - - lbs_deb_enter(LBS_DEB_RX); - - BUG_ON(!skb); - - skb->ip_summed = CHECKSUM_NONE; - - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { - ret = process_rxed_802_11_packet(priv, skb); - goto done; - } - - p_rx_pd = (struct rxpd *) skb->data; - p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd + - le32_to_cpu(p_rx_pd->pkt_ptr)); - - dev = lbs_mesh_set_dev(priv, dev, p_rx_pd); - - lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, - min_t(unsigned int, skb->len, 100)); - - if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { - lbs_deb_rx("rx err: frame received with bad length\n"); - dev->stats.rx_length_errors++; - ret = -EINVAL; - dev_kfree_skb(skb); - goto done; - } - - lbs_deb_rx("rx data: skb->len - pkt_ptr = %d-%zd = %zd\n", - skb->len, (size_t)le32_to_cpu(p_rx_pd->pkt_ptr), - skb->len - (size_t)le32_to_cpu(p_rx_pd->pkt_ptr)); - - lbs_deb_hex(LBS_DEB_RX, "RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr, - sizeof(p_rx_pkt->eth803_hdr.dest_addr)); - lbs_deb_hex(LBS_DEB_RX, "RX Data: Src", p_rx_pkt->eth803_hdr.src_addr, - sizeof(p_rx_pkt->eth803_hdr.src_addr)); - - if (memcmp(&p_rx_pkt->rfc1042_hdr, - rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { - /* - * Replace the 803 header and rfc1042 header (llc/snap) with an - * EthernetII header, keep the src/dst and snap_type (ethertype) - * - * The firmware only passes up SNAP frames converting - * all RX Data from 802.11 to 802.2/LLC/SNAP frames. - * - * To create the Ethernet II, just move the src, dst address right - * before the snap_type. - */ - p_ethhdr = (struct ethhdr *) - ((u8 *) &p_rx_pkt->eth803_hdr - + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr) - - sizeof(p_rx_pkt->eth803_hdr.dest_addr) - - sizeof(p_rx_pkt->eth803_hdr.src_addr) - - sizeof(p_rx_pkt->rfc1042_hdr.snap_type)); - - memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr, - sizeof(p_ethhdr->h_source)); - memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr, - sizeof(p_ethhdr->h_dest)); - - /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header - * that was removed - */ - hdrchop = (u8 *)p_ethhdr - (u8 *)p_rx_pd; - } else { - lbs_deb_hex(LBS_DEB_RX, "RX Data: LLC/SNAP", - (u8 *) &p_rx_pkt->rfc1042_hdr, - sizeof(p_rx_pkt->rfc1042_hdr)); - - /* Chop off the rxpd */ - hdrchop = (u8 *)&p_rx_pkt->eth803_hdr - (u8 *)p_rx_pd; - } - - /* Chop off the leading header bytes so the skb points to the start of - * either the reconstructed EthII frame or the 802.2/llc/snap frame - */ - skb_pull(skb, hdrchop); - - priv->cur_rate = lbs_fw_index_to_data_rate(p_rx_pd->rx_rate); - - lbs_deb_rx("rx data: size of actual packet %d\n", skb->len); - dev->stats.rx_bytes += skb->len; - dev->stats.rx_packets++; - - skb->protocol = eth_type_trans(skb, dev); - if (in_interrupt()) - netif_rx(skb); - else - netif_rx_ni(skb); - - ret = 0; -done: - lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(lbs_process_rxed_packet); - -/** - * convert_mv_rate_to_radiotap - converts Tx/Rx rates from Marvell WLAN format - * (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s) - * - * @rate: Input rate - * returns: Output Rate (0 if invalid) - */ -static u8 convert_mv_rate_to_radiotap(u8 rate) -{ - switch (rate) { - case 0: /* 1 Mbps */ - return 2; - case 1: /* 2 Mbps */ - return 4; - case 2: /* 5.5 Mbps */ - return 11; - case 3: /* 11 Mbps */ - return 22; - /* case 4: reserved */ - case 5: /* 6 Mbps */ - return 12; - case 6: /* 9 Mbps */ - return 18; - case 7: /* 12 Mbps */ - return 24; - case 8: /* 18 Mbps */ - return 36; - case 9: /* 24 Mbps */ - return 48; - case 10: /* 36 Mbps */ - return 72; - case 11: /* 48 Mbps */ - return 96; - case 12: /* 54 Mbps */ - return 108; - } - pr_alert("Invalid Marvell WLAN rate %i\n", rate); - return 0; -} - -/** - * process_rxed_802_11_packet - processes a received 802.11 packet and forwards - * it to kernel/upper layer - * - * @priv: A pointer to &struct lbs_private - * @skb: A pointer to skb which includes the received packet - * returns: 0 or -1 - */ -static int process_rxed_802_11_packet(struct lbs_private *priv, - struct sk_buff *skb) -{ - int ret = 0; - struct net_device *dev = priv->dev; - struct rx80211packethdr *p_rx_pkt; - struct rxpd *prxpd; - struct rx_radiotap_hdr radiotap_hdr; - struct rx_radiotap_hdr *pradiotap_hdr; - - lbs_deb_enter(LBS_DEB_RX); - - p_rx_pkt = (struct rx80211packethdr *) skb->data; - prxpd = &p_rx_pkt->rx_pd; - - /* lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); */ - - if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { - lbs_deb_rx("rx err: frame received with bad length\n"); - dev->stats.rx_length_errors++; - ret = -EINVAL; - kfree_skb(skb); - goto done; - } - - lbs_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n", - skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); - - /* create the exported radio header */ - - /* radiotap header */ - memset(&radiotap_hdr, 0, sizeof(radiotap_hdr)); - /* XXX must check radiotap_hdr.hdr.it_pad for pad */ - radiotap_hdr.hdr.it_len = cpu_to_le16 (sizeof(struct rx_radiotap_hdr)); - radiotap_hdr.hdr.it_present = cpu_to_le32 (RX_RADIOTAP_PRESENT); - radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); - /* XXX must check no carryout */ - radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; - - /* chop the rxpd */ - skb_pull(skb, sizeof(struct rxpd)); - - /* add space for the new radio header */ - if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) && - pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, GFP_ATOMIC)) { - netdev_alert(dev, "%s: couldn't pskb_expand_head\n", __func__); - ret = -ENOMEM; - kfree_skb(skb); - goto done; - } - - pradiotap_hdr = (void *)skb_push(skb, sizeof(struct rx_radiotap_hdr)); - memcpy(pradiotap_hdr, &radiotap_hdr, sizeof(struct rx_radiotap_hdr)); - - priv->cur_rate = lbs_fw_index_to_data_rate(prxpd->rx_rate); - - lbs_deb_rx("rx data: size of actual packet %d\n", skb->len); - dev->stats.rx_bytes += skb->len; - dev->stats.rx_packets++; - - skb->protocol = eth_type_trans(skb, priv->dev); - - if (in_interrupt()) - netif_rx(skb); - else - netif_rx_ni(skb); - - ret = 0; - -done: - lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret); - return ret; -} diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c deleted file mode 100644 index c025f9c18282..000000000000 --- a/drivers/net/wireless/libertas/tx.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * This file contains the handling of TX in wlan driver. - */ -#include -#include -#include -#include -#include -#include - -#include "host.h" -#include "radiotap.h" -#include "decl.h" -#include "defs.h" -#include "dev.h" -#include "mesh.h" - -/** - * convert_radiotap_rate_to_mv - converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE - * units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1) - * - * @rate: Input rate - * returns: Output Rate (0 if invalid) - */ -static u32 convert_radiotap_rate_to_mv(u8 rate) -{ - switch (rate) { - case 2: /* 1 Mbps */ - return 0 | (1 << 4); - case 4: /* 2 Mbps */ - return 1 | (1 << 4); - case 11: /* 5.5 Mbps */ - return 2 | (1 << 4); - case 22: /* 11 Mbps */ - return 3 | (1 << 4); - case 12: /* 6 Mbps */ - return 4 | (1 << 4); - case 18: /* 9 Mbps */ - return 5 | (1 << 4); - case 24: /* 12 Mbps */ - return 6 | (1 << 4); - case 36: /* 18 Mbps */ - return 7 | (1 << 4); - case 48: /* 24 Mbps */ - return 8 | (1 << 4); - case 72: /* 36 Mbps */ - return 9 | (1 << 4); - case 96: /* 48 Mbps */ - return 10 | (1 << 4); - case 108: /* 54 Mbps */ - return 11 | (1 << 4); - } - return 0; -} - -/** - * lbs_hard_start_xmit - checks the conditions and sends packet to IF - * layer if everything is ok - * - * @skb: A pointer to skb which includes TX packet - * @dev: A pointer to the &struct net_device - * returns: 0 or -1 - */ -netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned long flags; - struct lbs_private *priv = dev->ml_priv; - struct txpd *txpd; - char *p802x_hdr; - uint16_t pkt_len; - netdev_tx_t ret = NETDEV_TX_OK; - - lbs_deb_enter(LBS_DEB_TX); - - /* We need to protect against the queues being restarted before - we get round to stopping them */ - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->surpriseremoved) - goto free; - - if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { - lbs_deb_tx("tx err: skb length %d 0 or > %zd\n", - skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); - /* We'll never manage to send this one; drop it and return 'OK' */ - - dev->stats.tx_dropped++; - dev->stats.tx_errors++; - goto free; - } - - - netif_stop_queue(priv->dev); - if (priv->mesh_dev) - netif_stop_queue(priv->mesh_dev); - - if (priv->tx_pending_len) { - /* This can happen if packets come in on the mesh and eth - device simultaneously -- there's no mutual exclusion on - hard_start_xmit() calls between devices. */ - lbs_deb_tx("Packet on %s while busy\n", dev->name); - ret = NETDEV_TX_BUSY; - goto unlock; - } - - priv->tx_pending_len = -1; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbs_deb_hex(LBS_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); - - txpd = (void *)priv->tx_pending_buf; - memset(txpd, 0, sizeof(struct txpd)); - - p802x_hdr = skb->data; - pkt_len = skb->len; - - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { - struct tx_radiotap_hdr *rtap_hdr = (void *)skb->data; - - /* set txpd fields from the radiotap header */ - txpd->tx_control = cpu_to_le32(convert_radiotap_rate_to_mv(rtap_hdr->rate)); - - /* skip the radiotap header */ - p802x_hdr += sizeof(*rtap_hdr); - pkt_len -= sizeof(*rtap_hdr); - - /* copy destination address from 802.11 header */ - memcpy(txpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN); - } else { - /* copy destination address from 802.3 header */ - memcpy(txpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN); - } - - txpd->tx_packet_length = cpu_to_le16(pkt_len); - txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); - - lbs_mesh_set_txpd(priv, dev, txpd); - - lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) &txpd, sizeof(struct txpd)); - - lbs_deb_hex(LBS_DEB_TX, "Tx Data", (u8 *) p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); - - memcpy(&txpd[1], p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); - - spin_lock_irqsave(&priv->driver_lock, flags); - priv->tx_pending_len = pkt_len + sizeof(struct txpd); - - lbs_deb_tx("%s lined up packet\n", __func__); - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { - /* Keep the skb to echo it back once Tx feedback is - received from FW */ - skb_orphan(skb); - - /* Keep the skb around for when we get feedback */ - priv->currenttxskb = skb; - } else { - free: - dev_kfree_skb_any(skb); - } - - unlock: - spin_unlock_irqrestore(&priv->driver_lock, flags); - wake_up(&priv->waitq); - - lbs_deb_leave_args(LBS_DEB_TX, "ret %d", ret); - return ret; -} - -/** - * lbs_send_tx_feedback - sends to the host the last transmitted packet, - * filling the radiotap headers with transmission information. - * - * @priv: A pointer to &struct lbs_private structure - * @try_count: A 32-bit value containing transmission retry status. - * - * returns: void - */ -void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count) -{ - struct tx_radiotap_hdr *radiotap_hdr; - - if (priv->wdev->iftype != NL80211_IFTYPE_MONITOR || - priv->currenttxskb == NULL) - return; - - radiotap_hdr = (struct tx_radiotap_hdr *)priv->currenttxskb->data; - - radiotap_hdr->data_retries = try_count ? - (1 + priv->txretrycount - try_count) : 0; - - priv->currenttxskb->protocol = eth_type_trans(priv->currenttxskb, - priv->dev); - netif_rx(priv->currenttxskb); - - priv->currenttxskb = NULL; - - if (priv->connect_status == LBS_CONNECTED) - netif_wake_queue(priv->dev); - - if (priv->mesh_dev && netif_running(priv->mesh_dev)) - netif_wake_queue(priv->mesh_dev); -} -EXPORT_SYMBOL_GPL(lbs_send_tx_feedback); diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h deleted file mode 100644 index cf1d9b047ee6..000000000000 --- a/drivers/net/wireless/libertas/types.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - * This header file contains definition for global types - */ -#ifndef _LBS_TYPES_H_ -#define _LBS_TYPES_H_ - -#include -#include -#include - -struct ieee_ie_header { - u8 id; - u8 len; -} __packed; - -struct ieee_ie_cf_param_set { - struct ieee_ie_header header; - - u8 cfpcnt; - u8 cfpperiod; - __le16 cfpmaxduration; - __le16 cfpdurationremaining; -} __packed; - - -struct ieee_ie_ibss_param_set { - struct ieee_ie_header header; - - __le16 atimwindow; -} __packed; - -union ieee_ss_param_set { - struct ieee_ie_cf_param_set cf; - struct ieee_ie_ibss_param_set ibss; -} __packed; - -struct ieee_ie_fh_param_set { - struct ieee_ie_header header; - - __le16 dwelltime; - u8 hopset; - u8 hoppattern; - u8 hopindex; -} __packed; - -struct ieee_ie_ds_param_set { - struct ieee_ie_header header; - - u8 channel; -} __packed; - -union ieee_phy_param_set { - struct ieee_ie_fh_param_set fh; - struct ieee_ie_ds_param_set ds; -} __packed; - -/* TLV type ID definition */ -#define PROPRIETARY_TLV_BASE_ID 0x0100 - -/* Terminating TLV type */ -#define MRVL_TERMINATE_TLV_ID 0xffff - -#define TLV_TYPE_SSID 0x0000 -#define TLV_TYPE_RATES 0x0001 -#define TLV_TYPE_PHY_FH 0x0002 -#define TLV_TYPE_PHY_DS 0x0003 -#define TLV_TYPE_CF 0x0004 -#define TLV_TYPE_IBSS 0x0006 - -#define TLV_TYPE_DOMAIN 0x0007 - -#define TLV_TYPE_POWER_CAPABILITY 0x0021 - -#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) -#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) -#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) -#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) -#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 5) -#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 6) -#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 7) -#define TLV_TYPE_LED_GPIO (PROPRIETARY_TLV_BASE_ID + 8) -#define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 9) -#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) -#define TLV_TYPE_REASSOCAP (PROPRIETARY_TLV_BASE_ID + 11) -#define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 12) -#define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 13) -#define TLV_TYPE_BCASTPROBE (PROPRIETARY_TLV_BASE_ID + 14) -#define TLV_TYPE_NUMSSID_PROBE (PROPRIETARY_TLV_BASE_ID + 15) -#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) -#define TLV_TYPE_CRYPTO_DATA (PROPRIETARY_TLV_BASE_ID + 17) -#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) -#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) -#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) -#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 23) -#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) -#define TLV_TYPE_MESH_ID (PROPRIETARY_TLV_BASE_ID + 37) -#define TLV_TYPE_OLD_MESH_ID (PROPRIETARY_TLV_BASE_ID + 291) - -/* TLV related data structures */ -struct mrvl_ie_header { - __le16 type; - __le16 len; -} __packed; - -struct mrvl_ie_data { - struct mrvl_ie_header header; - u8 Data[1]; -} __packed; - -struct mrvl_ie_rates_param_set { - struct mrvl_ie_header header; - u8 rates[1]; -} __packed; - -struct mrvl_ie_ssid_param_set { - struct mrvl_ie_header header; - u8 ssid[1]; -} __packed; - -struct mrvl_ie_wildcard_ssid_param_set { - struct mrvl_ie_header header; - u8 MaxSsidlength; - u8 ssid[1]; -} __packed; - -struct chanscanmode { -#ifdef __BIG_ENDIAN_BITFIELD - u8 reserved_2_7:6; - u8 disablechanfilt:1; - u8 passivescan:1; -#else - u8 passivescan:1; - u8 disablechanfilt:1; - u8 reserved_2_7:6; -#endif -} __packed; - -struct chanscanparamset { - u8 radiotype; - u8 channumber; - struct chanscanmode chanscanmode; - __le16 minscantime; - __le16 maxscantime; -} __packed; - -struct mrvl_ie_chanlist_param_set { - struct mrvl_ie_header header; - struct chanscanparamset chanscanparam[1]; -} __packed; - -struct mrvl_ie_cf_param_set { - struct mrvl_ie_header header; - u8 cfpcnt; - u8 cfpperiod; - __le16 cfpmaxduration; - __le16 cfpdurationremaining; -} __packed; - -struct mrvl_ie_ds_param_set { - struct mrvl_ie_header header; - u8 channel; -} __packed; - -struct mrvl_ie_rsn_param_set { - struct mrvl_ie_header header; - u8 rsnie[1]; -} __packed; - -struct mrvl_ie_tsf_timestamp { - struct mrvl_ie_header header; - __le64 tsftable[1]; -} __packed; - -/* v9 and later firmware only */ -struct mrvl_ie_auth_type { - struct mrvl_ie_header header; - __le16 auth; -} __packed; - -/* Local Power capability */ -struct mrvl_ie_power_capability { - struct mrvl_ie_header header; - s8 minpower; - s8 maxpower; -} __packed; - -/* used in CMD_802_11_SUBSCRIBE_EVENT for SNR, RSSI and Failure */ -struct mrvl_ie_thresholds { - struct mrvl_ie_header header; - u8 value; - u8 freq; -} __packed; - -struct mrvl_ie_beacons_missed { - struct mrvl_ie_header header; - u8 beaconmissed; - u8 reserved; -} __packed; - -struct mrvl_ie_num_probes { - struct mrvl_ie_header header; - __le16 numprobes; -} __packed; - -struct mrvl_ie_bcast_probe { - struct mrvl_ie_header header; - __le16 bcastprobe; -} __packed; - -struct mrvl_ie_num_ssid_probe { - struct mrvl_ie_header header; - __le16 numssidprobe; -} __packed; - -struct led_pin { - u8 led; - u8 pin; -} __packed; - -struct mrvl_ie_ledgpio { - struct mrvl_ie_header header; - struct led_pin ledpin[1]; -} __packed; - -struct led_bhv { - uint8_t firmwarestate; - uint8_t led; - uint8_t ledstate; - uint8_t ledarg; -} __packed; - - -struct mrvl_ie_ledbhv { - struct mrvl_ie_header header; - struct led_bhv ledbhv[1]; -} __packed; - -/* - * Meant to be packed as the value member of a struct ieee80211_info_element. - * Note that the len member of the ieee80211_info_element varies depending on - * the mesh_id_len - */ -struct mrvl_meshie_val { - uint8_t oui[3]; - uint8_t type; - uint8_t subtype; - uint8_t version; - uint8_t active_protocol_id; - uint8_t active_metric_id; - uint8_t mesh_capability; - uint8_t mesh_id_len; - uint8_t mesh_id[IEEE80211_MAX_SSID_LEN]; -} __packed; - -struct mrvl_meshie { - u8 id, len; - struct mrvl_meshie_val val; -} __packed; - -struct mrvl_mesh_defaults { - __le32 bootflag; - uint8_t boottime; - uint8_t reserved; - __le16 channel; - struct mrvl_meshie meshie; -} __packed; - -#endif diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig new file mode 100644 index 000000000000..7842096bea53 --- /dev/null +++ b/drivers/net/wireless/marvell/Kconfig @@ -0,0 +1,16 @@ +config WLAN_VENDOR_MARVELL + bool "Marvell devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_MARVELL + +source "drivers/net/wireless/marvell/libertas/Kconfig" + +endif # WLAN_VENDOR_MARVELL diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile new file mode 100644 index 000000000000..6f7ac46ded33 --- /dev/null +++ b/drivers/net/wireless/marvell/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_LIBERTAS) += libertas/ diff --git a/drivers/net/wireless/marvell/libertas/Kconfig b/drivers/net/wireless/marvell/libertas/Kconfig new file mode 100644 index 000000000000..e6268ceacbf1 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/Kconfig @@ -0,0 +1,45 @@ +config LIBERTAS + tristate "Marvell 8xxx Libertas WLAN driver support" + depends on CFG80211 + select WIRELESS_EXT + select WEXT_SPY + select LIB80211 + select FW_LOADER + ---help--- + A library for Marvell Libertas 8xxx devices. + +config LIBERTAS_USB + tristate "Marvell Libertas 8388 USB 802.11b/g cards" + depends on LIBERTAS && USB + ---help--- + A driver for Marvell Libertas 8388 USB devices. + +config LIBERTAS_CS + tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards" + depends on LIBERTAS && PCMCIA && HAS_IOPORT_MAP + ---help--- + A driver for Marvell Libertas 8385 CompactFlash devices. + +config LIBERTAS_SDIO + tristate "Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards" + depends on LIBERTAS && MMC + ---help--- + A driver for Marvell Libertas 8385/8686/8688 SDIO devices. + +config LIBERTAS_SPI + tristate "Marvell Libertas 8686 SPI 802.11b/g cards" + depends on LIBERTAS && SPI + ---help--- + A driver for Marvell Libertas 8686 SPI devices. + +config LIBERTAS_DEBUG + bool "Enable full debugging output in the Libertas module." + depends on LIBERTAS + ---help--- + Debugging support. + +config LIBERTAS_MESH + bool "Enable mesh support" + depends on LIBERTAS + help + This enables Libertas' MESH support, used by e.g. the OLPC people. diff --git a/drivers/net/wireless/marvell/libertas/LICENSE b/drivers/net/wireless/marvell/libertas/LICENSE new file mode 100644 index 000000000000..8862742213b9 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/LICENSE @@ -0,0 +1,16 @@ + Copyright (c) 2003-2006, Marvell International Ltd. + All Rights Reserved + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + diff --git a/drivers/net/wireless/marvell/libertas/Makefile b/drivers/net/wireless/marvell/libertas/Makefile new file mode 100644 index 000000000000..eac72f7bd341 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/Makefile @@ -0,0 +1,21 @@ +libertas-y += cfg.o +libertas-y += cmd.o +libertas-y += cmdresp.o +libertas-y += debugfs.o +libertas-y += ethtool.o +libertas-y += main.o +libertas-y += rx.o +libertas-y += tx.o +libertas-y += firmware.o +libertas-$(CONFIG_LIBERTAS_MESH) += mesh.o + +usb8xxx-objs += if_usb.o +libertas_cs-objs += if_cs.o +libertas_sdio-objs += if_sdio.o +libertas_spi-objs += if_spi.o + +obj-$(CONFIG_LIBERTAS) += libertas.o +obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o +obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o +obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o +obj-$(CONFIG_LIBERTAS_SPI) += libertas_spi.o diff --git a/drivers/net/wireless/marvell/libertas/README b/drivers/net/wireless/marvell/libertas/README new file mode 100644 index 000000000000..1a554a685e91 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/README @@ -0,0 +1,239 @@ +================================================================================ + README for Libertas + + (c) Copyright © 2003-2006, Marvell International Ltd. + All Rights Reserved + + 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 along with the File in the license.txt file or on the worldwide + web at http://www.gnu.org/licenses/gpl.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. +================================================================================ + +===================== +DRIVER LOADING +===================== + + o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/ + + o. Load driver by using the following command: + + insmod usb8388.ko [fw_name=usb8388.bin] + +========================= +ETHTOOL +========================= + + +Use the -i option to retrieve version information from the driver. + +# ethtool -i eth0 +driver: libertas +version: COMM-USB8388-318.p4 +firmware-version: 5.110.7 +bus-info: + +Use the -e option to read the EEPROM contents of the card. + + Usage: + ethtool -e ethX [raw on|off] [offset N] [length N] + + -e retrieves and prints an EEPROM dump for the specified ethernet + device. When raw is enabled, then it dumps the raw EEPROM data + to stdout. The length and offset parameters allow dumping cer- + tain portions of the EEPROM. Default is to dump the entire EEP- + ROM. + +# ethtool -e eth0 offset 0 length 16 +Offset Values +------ ------ +0x0000 38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00 + +======================== +DEBUGFS COMMANDS +======================== + +those commands are used via debugfs interface + +=========== +rdmac +rdbbp +rdrf + These commands are used to read the MAC, BBP and RF registers from the + card. These commands take one parameter that specifies the offset + location that is to be read. This parameter must be specified in + hexadecimal (its possible to precede preceding the number with a "0x"). + + Path: /sys/kernel/debug/libertas_wireless/ethX/registers/ + + Usage: + echo "0xa123" > rdmac ; cat rdmac + echo "0xa123" > rdbbp ; cat rdbbp + echo "0xa123" > rdrf ; cat rdrf +wrmac +wrbbp +wrrf + These commands are used to write the MAC, BBP and RF registers in the + card. These commands take two parameters that specify the offset + location and the value that is to be written. This parameters must + be specified in hexadecimal (its possible to precede the number + with a "0x"). + + Usage: + echo "0xa123 0xaa" > wrmac + echo "0xa123 0xaa" > wrbbp + echo "0xa123 0xaa" > wrrf + +sleepparams + This command is used to set the sleepclock configurations + + Path: /sys/kernel/debug/libertas_wireless/ethX/ + + Usage: + cat sleepparams: reads the current sleepclock configuration + + echo "p1 p2 p3 p4 p5 p6" > sleepparams: writes the sleepclock configuration. + + where: + p1 is Sleep clock error in ppm (0-65535) + p2 is Wakeup offset in usec (0-65535) + p3 is Clock stabilization time in usec (0-65535) + p4 is Control periodic calibration (0-2) + p5 is Control the use of external sleep clock (0-2) + p6 is reserved for debug (0-65535) + +subscribed_events + + The subscribed_events directory contains the interface for the + subscribed events API. + + Path: /sys/kernel/debug/libertas_wireless/ethX/subscribed_events/ + + Each event is represented by a filename. Each filename consists of the + following three fields: + Value Frequency Subscribed + + To read the current values for a given event, do: + cat event + To set the current values, do: + echo "60 2 1" > event + + Frequency field specifies the reporting frequency for this event. + If it is set to 0, then the event is reported only once, and then + automatically unsubscribed. If it is set to 1, then the event is + reported every time it occurs. If it is set to N, then the event is + reported every Nth time it occurs. + + beacon_missed + Value field specifies the number of consecutive missing beacons which + triggers the LINK_LOSS event. This event is generated only once after + which the firmware resets its state. At initialization, the LINK_LOSS + event is subscribed by default. The default value of MissedBeacons is + 60. + + failure_count + Value field specifies the consecutive failure count threshold which + triggers the generation of the MAX_FAIL event. Once this event is + generated, the consecutive failure count is reset to 0. + At initialization, the MAX_FAIL event is NOT subscribed by + default. + + high_rssi + This event is generated when the average received RSSI in beacons goes + above a threshold, specified by Value. + + low_rssi + This event is generated when the average received RSSI in beacons goes + below a threshold, specified by Value. + + high_snr + This event is generated when the average received SNR in beacons goes + above a threshold, specified by Value. + + low_snr + This event is generated when the average received SNR in beacons goes + below a threshold, specified by Value. + +extscan + This command is used to do a specific scan. + + Path: /sys/kernel/debug/libertas_wireless/ethX/ + + Usage: echo "SSID" > extscan + + Example: + echo "LINKSYS-AP" > extscan + + To see the results of use getscantable command. + +getscantable + + Display the current contents of the driver scan table (ie. get the + scan results). + + Path: /sys/kernel/debug/libertas_wireless/ethX/ + + Usage: + cat getscantable + +setuserscan + Initiate a customized scan and retrieve the results + + + Path: /sys/kernel/debug/libertas_wireless/ethX/ + + Usage: + echo "[ARGS]" > setuserscan + + where [ARGS]: + + bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan + ssid="[SSID]" specify a SSID filter for the scan + keep=[0 or 1] keep the previous scan results (1), discard (0) + dur=[scan time] time to scan for each channel in milliseconds + type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) + + Any combination of the above arguments can be supplied on the command + line. If dur tokens are absent, the driver default setting will be used. + The bssid and ssid fields, if blank, will produce an unfiltered scan. + The type field will default to 3 (Any) and the keep field will default + to 0 (Discard). + + Examples: + 1) Perform a passive scan on all channels for 20 ms per channel: + echo "dur=20" > setuserscan + + 2) Perform an active scan for a specific SSID: + echo "ssid="TestAP"" > setuserscan + + 3) Scan all available channels (B/G, A bands) for a specific BSSID, keep + the current scan table intact, update existing or append new scan data: + echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan + + 4) Scan for all infrastructure networks. + Keep the previous scan table intact. Update any duplicate BSSID/SSID + matches with the new scan data: + echo "type=1 keep=1" > setuserscan + + All entries in the scan table (not just the new scan data when keep=1) + will be displayed upon completion by use of the getscantable ioctl. + +hostsleep + This command is used to enable/disable host sleep. + Note: Host sleep parameters should be configured using + "ethtool -s ethX wol X" command before enabling host sleep. + + Path: /sys/kernel/debug/libertas_wireless/ethX/ + + Usage: + cat hostsleep: reads the current hostsleep state + echo "1" > hostsleep : enable host sleep. + echo "0" > hostsleep : disable host sleep + diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c new file mode 100644 index 000000000000..8317afd065b4 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/cfg.c @@ -0,0 +1,2215 @@ +/* + * Implement cfg80211 ("iw") support. + * + * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany + * Holger Schurig + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "decl.h" +#include "cfg.h" +#include "cmd.h" +#include "mesh.h" + + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel lbs_2ghz_channels[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +#define RATETAB_ENT(_rate, _hw_value, _flags) { \ + .bitrate = (_rate), \ + .hw_value = (_hw_value), \ + .flags = (_flags), \ +} + + +/* Table 6 in section 3.2.1.1 */ +static struct ieee80211_rate lbs_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, 0), + RATETAB_ENT(55, 2, 0), + RATETAB_ENT(110, 3, 0), + RATETAB_ENT(60, 9, 0), + RATETAB_ENT(90, 6, 0), + RATETAB_ENT(120, 7, 0), + RATETAB_ENT(180, 8, 0), + RATETAB_ENT(240, 9, 0), + RATETAB_ENT(360, 10, 0), + RATETAB_ENT(480, 11, 0), + RATETAB_ENT(540, 12, 0), +}; + +static struct ieee80211_supported_band lbs_band_2ghz = { + .channels = lbs_2ghz_channels, + .n_channels = ARRAY_SIZE(lbs_2ghz_channels), + .bitrates = lbs_rates, + .n_bitrates = ARRAY_SIZE(lbs_rates), +}; + + +static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, +}; + +/* Time to stay on the channel */ +#define LBS_DWELL_PASSIVE 100 +#define LBS_DWELL_ACTIVE 40 + + +/*************************************************************************** + * Misc utility functions + * + * TLVs are Marvell specific. They are very similar to IEs, they have the + * same structure: type, length, data*. The only difference: for IEs, the + * type and length are u8, but for TLVs they're __le16. + */ + +/* + * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1 + * in the firmware spec + */ +static int lbs_auth_to_authtype(enum nl80211_auth_type auth_type) +{ + int ret = -ENOTSUPP; + + switch (auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + case NL80211_AUTHTYPE_SHARED_KEY: + ret = auth_type; + break; + case NL80211_AUTHTYPE_AUTOMATIC: + ret = NL80211_AUTHTYPE_OPEN_SYSTEM; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + ret = 0x80; + break; + default: + /* silence compiler */ + break; + } + return ret; +} + + +/* + * Various firmware commands need the list of supported rates, but with + * the hight-bit set for basic rates + */ +static int lbs_add_rates(u8 *rates) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { + u8 rate = lbs_rates[i].bitrate / 5; + if (rate == 0x02 || rate == 0x04 || + rate == 0x0b || rate == 0x16) + rate |= 0x80; + rates[i] = rate; + } + return ARRAY_SIZE(lbs_rates); +} + + +/*************************************************************************** + * TLV utility functions + * + * TLVs are Marvell specific. They are very similar to IEs, they have the + * same structure: type, length, data*. The only difference: for IEs, the + * type and length are u8, but for TLVs they're __le16. + */ + + +/* + * Add ssid TLV + */ +#define LBS_MAX_SSID_TLV_SIZE \ + (sizeof(struct mrvl_ie_header) \ + + IEEE80211_MAX_SSID_LEN) + +static int lbs_add_ssid_tlv(u8 *tlv, const u8 *ssid, int ssid_len) +{ + struct mrvl_ie_ssid_param_set *ssid_tlv = (void *)tlv; + + /* + * TLV-ID SSID 00 00 + * length 06 00 + * ssid 4d 4e 54 45 53 54 + */ + ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID); + ssid_tlv->header.len = cpu_to_le16(ssid_len); + memcpy(ssid_tlv->ssid, ssid, ssid_len); + return sizeof(ssid_tlv->header) + ssid_len; +} + + +/* + * Add channel list TLV (section 8.4.2) + * + * Actual channel data comes from priv->wdev->wiphy->channels. + */ +#define LBS_MAX_CHANNEL_LIST_TLV_SIZE \ + (sizeof(struct mrvl_ie_header) \ + + (LBS_SCAN_BEFORE_NAP * sizeof(struct chanscanparamset))) + +static int lbs_add_channel_list_tlv(struct lbs_private *priv, u8 *tlv, + int last_channel, int active_scan) +{ + int chanscanparamsize = sizeof(struct chanscanparamset) * + (last_channel - priv->scan_channel); + + struct mrvl_ie_header *header = (void *) tlv; + + /* + * TLV-ID CHANLIST 01 01 + * length 0e 00 + * channel 00 01 00 00 00 64 00 + * radio type 00 + * channel 01 + * scan type 00 + * min scan time 00 00 + * max scan time 64 00 + * channel 2 00 02 00 00 00 64 00 + * + */ + + header->type = cpu_to_le16(TLV_TYPE_CHANLIST); + header->len = cpu_to_le16(chanscanparamsize); + tlv += sizeof(struct mrvl_ie_header); + + /* lbs_deb_scan("scan: channels %d to %d\n", priv->scan_channel, + last_channel); */ + memset(tlv, 0, chanscanparamsize); + + while (priv->scan_channel < last_channel) { + struct chanscanparamset *param = (void *) tlv; + + param->radiotype = CMD_SCAN_RADIO_TYPE_BG; + param->channumber = + priv->scan_req->channels[priv->scan_channel]->hw_value; + if (active_scan) { + param->maxscantime = cpu_to_le16(LBS_DWELL_ACTIVE); + } else { + param->chanscanmode.passivescan = 1; + param->maxscantime = cpu_to_le16(LBS_DWELL_PASSIVE); + } + tlv += sizeof(struct chanscanparamset); + priv->scan_channel++; + } + return sizeof(struct mrvl_ie_header) + chanscanparamsize; +} + + +/* + * Add rates TLV + * + * The rates are in lbs_bg_rates[], but for the 802.11b + * rates the high bit is set. We add this TLV only because + * there's a firmware which otherwise doesn't report all + * APs in range. + */ +#define LBS_MAX_RATES_TLV_SIZE \ + (sizeof(struct mrvl_ie_header) \ + + (ARRAY_SIZE(lbs_rates))) + +/* Adds a TLV with all rates the hardware supports */ +static int lbs_add_supported_rates_tlv(u8 *tlv) +{ + size_t i; + struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; + + /* + * TLV-ID RATES 01 00 + * length 0e 00 + * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c + */ + rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); + tlv += sizeof(rate_tlv->header); + i = lbs_add_rates(tlv); + tlv += i; + rate_tlv->header.len = cpu_to_le16(i); + return sizeof(rate_tlv->header) + i; +} + +/* Add common rates from a TLV and return the new end of the TLV */ +static u8 * +add_ie_rates(u8 *tlv, const u8 *ie, int *nrates) +{ + int hw, ap, ap_max = ie[1]; + u8 hw_rate; + + /* Advance past IE header */ + ie += 2; + + lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max); + + for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { + hw_rate = lbs_rates[hw].bitrate / 5; + for (ap = 0; ap < ap_max; ap++) { + if (hw_rate == (ie[ap] & 0x7f)) { + *tlv++ = ie[ap]; + *nrates = *nrates + 1; + } + } + } + return tlv; +} + +/* + * Adds a TLV with all rates the hardware *and* BSS supports. + */ +static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) +{ + struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; + const u8 *rates_eid, *ext_rates_eid; + int n = 0; + + rcu_read_lock(); + rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); + ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); + + /* + * 01 00 TLV_TYPE_RATES + * 04 00 len + * 82 84 8b 96 rates + */ + rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); + tlv += sizeof(rate_tlv->header); + + /* Add basic rates */ + if (rates_eid) { + tlv = add_ie_rates(tlv, rates_eid, &n); + + /* Add extended rates, if any */ + if (ext_rates_eid) + tlv = add_ie_rates(tlv, ext_rates_eid, &n); + } else { + lbs_deb_assoc("assoc: bss had no basic rate IE\n"); + /* Fallback: add basic 802.11b rates */ + *tlv++ = 0x82; + *tlv++ = 0x84; + *tlv++ = 0x8b; + *tlv++ = 0x96; + n = 4; + } + rcu_read_unlock(); + + rate_tlv->header.len = cpu_to_le16(n); + return sizeof(rate_tlv->header) + n; +} + + +/* + * Add auth type TLV. + * + * This is only needed for newer firmware (V9 and up). + */ +#define LBS_MAX_AUTH_TYPE_TLV_SIZE \ + sizeof(struct mrvl_ie_auth_type) + +static int lbs_add_auth_type_tlv(u8 *tlv, enum nl80211_auth_type auth_type) +{ + struct mrvl_ie_auth_type *auth = (void *) tlv; + + /* + * 1f 01 TLV_TYPE_AUTH_TYPE + * 01 00 len + * 01 auth type + */ + auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); + auth->header.len = cpu_to_le16(sizeof(*auth)-sizeof(auth->header)); + auth->auth = cpu_to_le16(lbs_auth_to_authtype(auth_type)); + return sizeof(*auth); +} + + +/* + * Add channel (phy ds) TLV + */ +#define LBS_MAX_CHANNEL_TLV_SIZE \ + sizeof(struct mrvl_ie_header) + +static int lbs_add_channel_tlv(u8 *tlv, u8 channel) +{ + struct mrvl_ie_ds_param_set *ds = (void *) tlv; + + /* + * 03 00 TLV_TYPE_PHY_DS + * 01 00 len + * 06 channel + */ + ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); + ds->header.len = cpu_to_le16(sizeof(*ds)-sizeof(ds->header)); + ds->channel = channel; + return sizeof(*ds); +} + + +/* + * Add (empty) CF param TLV of the form: + */ +#define LBS_MAX_CF_PARAM_TLV_SIZE \ + sizeof(struct mrvl_ie_header) + +static int lbs_add_cf_param_tlv(u8 *tlv) +{ + struct mrvl_ie_cf_param_set *cf = (void *)tlv; + + /* + * 04 00 TLV_TYPE_CF + * 06 00 len + * 00 cfpcnt + * 00 cfpperiod + * 00 00 cfpmaxduration + * 00 00 cfpdurationremaining + */ + cf->header.type = cpu_to_le16(TLV_TYPE_CF); + cf->header.len = cpu_to_le16(sizeof(*cf)-sizeof(cf->header)); + return sizeof(*cf); +} + +/* + * Add WPA TLV + */ +#define LBS_MAX_WPA_TLV_SIZE \ + (sizeof(struct mrvl_ie_header) \ + + 128 /* TODO: I guessed the size */) + +static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len) +{ + size_t tlv_len; + + /* + * We need just convert an IE to an TLV. IEs use u8 for the header, + * u8 type + * u8 len + * u8[] data + * but TLVs use __le16 instead: + * __le16 type + * __le16 len + * u8[] data + */ + *tlv++ = *ie++; + *tlv++ = 0; + tlv_len = *tlv++ = *ie++; + *tlv++ = 0; + while (tlv_len--) + *tlv++ = *ie++; + /* the TLV is two bytes larger than the IE */ + return ie_len + 2; +} + +/* + * Set Channel + */ + +static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = -ENOTSUPP; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d", + chandef->chan->center_freq, + cfg80211_get_chandef_type(chandef)); + + if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) + goto out; + + ret = lbs_set_channel(priv, chandef->chan->hw_value); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + +static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy, + struct net_device *netdev, + struct ieee80211_channel *channel) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = -ENOTSUPP; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d", + netdev_name(netdev), channel->center_freq); + + if (netdev != priv->mesh_dev) + goto out; + + ret = lbs_mesh_set_channel(priv, channel->hw_value); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + + +/* + * Scanning + */ + +/* + * When scanning, the firmware doesn't send a nul packet with the power-safe + * bit to the AP. So we cannot stay away from our current channel too long, + * otherwise we loose data. So take a "nap" while scanning every other + * while. + */ +#define LBS_SCAN_BEFORE_NAP 4 + + +/* + * When the firmware reports back a scan-result, it gives us an "u8 rssi", + * which isn't really an RSSI, as it becomes larger when moving away from + * the AP. Anyway, we need to convert that into mBm. + */ +#define LBS_SCAN_RSSI_TO_MBM(rssi) \ + ((-(int)rssi + 3)*100) + +static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, + struct cmd_header *resp) +{ + struct cfg80211_bss *bss; + struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp; + int bsssize; + const u8 *pos; + const u8 *tsfdesc; + int tsfsize; + int i; + int ret = -EILSEQ; + + lbs_deb_enter(LBS_DEB_CFG80211); + + bsssize = get_unaligned_le16(&scanresp->bssdescriptsize); + + lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n", + scanresp->nr_sets, bsssize, le16_to_cpu(resp->size)); + + if (scanresp->nr_sets == 0) { + ret = 0; + goto done; + } + + /* + * The general layout of the scan response is described in chapter + * 5.7.1. Basically we have a common part, then any number of BSS + * descriptor sections. Finally we have section with the same number + * of TSFs. + * + * cmd_ds_802_11_scan_rsp + * cmd_header + * pos_size + * nr_sets + * bssdesc 1 + * bssid + * rssi + * timestamp + * intvl + * capa + * IEs + * bssdesc 2 + * bssdesc n + * MrvlIEtypes_TsfFimestamp_t + * TSF for BSS 1 + * TSF for BSS 2 + * TSF for BSS n + */ + + pos = scanresp->bssdesc_and_tlvbuffer; + + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer, + scanresp->bssdescriptsize); + + tsfdesc = pos + bsssize; + tsfsize = 4 + 8 * scanresp->nr_sets; + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TSF", (u8 *) tsfdesc, tsfsize); + + /* Validity check: we expect a Marvell-Local TLV */ + i = get_unaligned_le16(tsfdesc); + tsfdesc += 2; + if (i != TLV_TYPE_TSFTIMESTAMP) { + lbs_deb_scan("scan response: invalid TSF Timestamp %d\n", i); + goto done; + } + + /* + * Validity check: the TLV holds TSF values with 8 bytes each, so + * the size in the TLV must match the nr_sets value + */ + i = get_unaligned_le16(tsfdesc); + tsfdesc += 2; + if (i / 8 != scanresp->nr_sets) { + lbs_deb_scan("scan response: invalid number of TSF timestamp " + "sets (expected %d got %d)\n", scanresp->nr_sets, + i / 8); + goto done; + } + + for (i = 0; i < scanresp->nr_sets; i++) { + const u8 *bssid; + const u8 *ie; + int left; + int ielen; + int rssi; + u16 intvl; + u16 capa; + int chan_no = -1; + const u8 *ssid = NULL; + u8 ssid_len = 0; + + int len = get_unaligned_le16(pos); + pos += 2; + + /* BSSID */ + bssid = pos; + pos += ETH_ALEN; + /* RSSI */ + rssi = *pos++; + /* Packet time stamp */ + pos += 8; + /* Beacon interval */ + intvl = get_unaligned_le16(pos); + pos += 2; + /* Capabilities */ + capa = get_unaligned_le16(pos); + pos += 2; + + /* To find out the channel, we must parse the IEs */ + ie = pos; + /* + * 6+1+8+2+2: size of BSSID, RSSI, time stamp, beacon + * interval, capabilities + */ + ielen = left = len - (6 + 1 + 8 + 2 + 2); + while (left >= 2) { + u8 id, elen; + id = *pos++; + elen = *pos++; + left -= 2; + if (elen > left) { + lbs_deb_scan("scan response: invalid IE fmt\n"); + goto done; + } + + if (id == WLAN_EID_DS_PARAMS) + chan_no = *pos; + if (id == WLAN_EID_SSID) { + ssid = pos; + ssid_len = elen; + } + left -= elen; + pos += elen; + } + + /* No channel, no luck */ + if (chan_no != -1) { + struct wiphy *wiphy = priv->wdev->wiphy; + int freq = ieee80211_channel_to_frequency(chan_no, + IEEE80211_BAND_2GHZ); + struct ieee80211_channel *channel = + ieee80211_get_channel(wiphy, freq); + + lbs_deb_scan("scan: %pM, capa %04x, chan %2d, %*pE, %d dBm\n", + bssid, capa, chan_no, ssid_len, ssid, + LBS_SCAN_RSSI_TO_MBM(rssi)/100); + + if (channel && + !(channel->flags & IEEE80211_CHAN_DISABLED)) { + bss = cfg80211_inform_bss(wiphy, channel, + CFG80211_BSS_FTYPE_UNKNOWN, + bssid, get_unaligned_le64(tsfdesc), + capa, intvl, ie, ielen, + LBS_SCAN_RSSI_TO_MBM(rssi), + GFP_KERNEL); + cfg80211_put_bss(wiphy, bss); + } + } else + lbs_deb_scan("scan response: missing BSS channel IE\n"); + + tsfdesc += 8; + } + ret = 0; + + done: + lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); + return ret; +} + + +/* + * Our scan command contains a TLV, consting of a SSID TLV, a channel list + * TLV and a rates TLV. Determine the maximum size of them: + */ +#define LBS_SCAN_MAX_CMD_SIZE \ + (sizeof(struct cmd_ds_802_11_scan) \ + + LBS_MAX_SSID_TLV_SIZE \ + + LBS_MAX_CHANNEL_LIST_TLV_SIZE \ + + LBS_MAX_RATES_TLV_SIZE) + +/* + * Assumes priv->scan_req is initialized and valid + * Assumes priv->scan_channel is initialized + */ +static void lbs_scan_worker(struct work_struct *work) +{ + struct lbs_private *priv = + container_of(work, struct lbs_private, scan_work.work); + struct cmd_ds_802_11_scan *scan_cmd; + u8 *tlv; /* pointer into our current, growing TLV storage area */ + int last_channel; + int running, carrier; + + lbs_deb_enter(LBS_DEB_SCAN); + + scan_cmd = kzalloc(LBS_SCAN_MAX_CMD_SIZE, GFP_KERNEL); + if (scan_cmd == NULL) + goto out_no_scan_cmd; + + /* prepare fixed part of scan command */ + scan_cmd->bsstype = CMD_BSS_TYPE_ANY; + + /* stop network while we're away from our main channel */ + running = !netif_queue_stopped(priv->dev); + carrier = netif_carrier_ok(priv->dev); + if (running) + netif_stop_queue(priv->dev); + if (carrier) + netif_carrier_off(priv->dev); + + /* prepare fixed part of scan command */ + tlv = scan_cmd->tlvbuffer; + + /* add SSID TLV */ + if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0) + tlv += lbs_add_ssid_tlv(tlv, + priv->scan_req->ssids[0].ssid, + priv->scan_req->ssids[0].ssid_len); + + /* add channel TLVs */ + last_channel = priv->scan_channel + LBS_SCAN_BEFORE_NAP; + if (last_channel > priv->scan_req->n_channels) + last_channel = priv->scan_req->n_channels; + tlv += lbs_add_channel_list_tlv(priv, tlv, last_channel, + priv->scan_req->n_ssids); + + /* add rates TLV */ + tlv += lbs_add_supported_rates_tlv(tlv); + + if (priv->scan_channel < priv->scan_req->n_channels) { + cancel_delayed_work(&priv->scan_work); + if (netif_running(priv->dev)) + queue_delayed_work(priv->work_thread, &priv->scan_work, + msecs_to_jiffies(300)); + } + + /* This is the final data we are about to send */ + scan_cmd->hdr.size = cpu_to_le16(tlv - (u8 *)scan_cmd); + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd, + sizeof(*scan_cmd)); + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer, + tlv - scan_cmd->tlvbuffer); + + __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr, + le16_to_cpu(scan_cmd->hdr.size), + lbs_ret_scan, 0); + + if (priv->scan_channel >= priv->scan_req->n_channels) { + /* Mark scan done */ + cancel_delayed_work(&priv->scan_work); + lbs_scan_done(priv); + } + + /* Restart network */ + if (carrier) + netif_carrier_on(priv->dev); + if (running && !priv->tx_pending_len) + netif_wake_queue(priv->dev); + + kfree(scan_cmd); + + /* Wake up anything waiting on scan completion */ + if (priv->scan_req == NULL) { + lbs_deb_scan("scan: waking up waiters\n"); + wake_up_all(&priv->scan_q); + } + + out_no_scan_cmd: + lbs_deb_leave(LBS_DEB_SCAN); +} + +static void _internal_start_scan(struct lbs_private *priv, bool internal, + struct cfg80211_scan_request *request) +{ + lbs_deb_enter(LBS_DEB_CFG80211); + + lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n", + request->n_ssids, request->n_channels, request->ie_len); + + priv->scan_channel = 0; + priv->scan_req = request; + priv->internal_scan = internal; + + queue_delayed_work(priv->work_thread, &priv->scan_work, + msecs_to_jiffies(50)); + + lbs_deb_leave(LBS_DEB_CFG80211); +} + +/* + * Clean up priv->scan_req. Should be used to handle the allocation details. + */ +void lbs_scan_done(struct lbs_private *priv) +{ + WARN_ON(!priv->scan_req); + + if (priv->internal_scan) + kfree(priv->scan_req); + else + cfg80211_scan_done(priv->scan_req, false); + + priv->scan_req = NULL; +} + +static int lbs_cfg_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = 0; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (priv->scan_req || delayed_work_pending(&priv->scan_work)) { + /* old scan request not yet processed */ + ret = -EAGAIN; + goto out; + } + + _internal_start_scan(priv, false, request); + + if (priv->surpriseremoved) + ret = -EIO; + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + + + +/* + * Events + */ + +void lbs_send_disconnect_notification(struct lbs_private *priv, + bool locally_generated) +{ + lbs_deb_enter(LBS_DEB_CFG80211); + + cfg80211_disconnected(priv->dev, 0, NULL, 0, locally_generated, + GFP_KERNEL); + + lbs_deb_leave(LBS_DEB_CFG80211); +} + +void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event) +{ + lbs_deb_enter(LBS_DEB_CFG80211); + + cfg80211_michael_mic_failure(priv->dev, + priv->assoc_bss, + event == MACREG_INT_CODE_MIC_ERR_MULTICAST ? + NL80211_KEYTYPE_GROUP : + NL80211_KEYTYPE_PAIRWISE, + -1, + NULL, + GFP_KERNEL); + + lbs_deb_leave(LBS_DEB_CFG80211); +} + + + + +/* + * Connect/disconnect + */ + + +/* + * This removes all WEP keys + */ +static int lbs_remove_wep_keys(struct lbs_private *priv) +{ + struct cmd_ds_802_11_set_wep cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CFG80211); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.keyindex = cpu_to_le16(priv->wep_tx_key); + cmd.action = cpu_to_le16(CMD_ACT_REMOVE); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); + + lbs_deb_leave(LBS_DEB_CFG80211); + return ret; +} + +/* + * Set WEP keys + */ +static int lbs_set_wep_keys(struct lbs_private *priv) +{ + struct cmd_ds_802_11_set_wep cmd; + int i; + int ret; + + lbs_deb_enter(LBS_DEB_CFG80211); + + /* + * command 13 00 + * size 50 00 + * sequence xx xx + * result 00 00 + * action 02 00 ACT_ADD + * transmit key 00 00 + * type for key 1 01 WEP40 + * type for key 2 00 + * type for key 3 00 + * type for key 4 00 + * key 1 39 39 39 39 39 00 00 00 + * 00 00 00 00 00 00 00 00 + * key 2 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * key 3 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * key 4 00 00 00 00 00 00 00 00 + */ + if (priv->wep_key_len[0] || priv->wep_key_len[1] || + priv->wep_key_len[2] || priv->wep_key_len[3]) { + /* Only set wep keys if we have at least one of them */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.keyindex = cpu_to_le16(priv->wep_tx_key); + cmd.action = cpu_to_le16(CMD_ACT_ADD); + + for (i = 0; i < 4; i++) { + switch (priv->wep_key_len[i]) { + case WLAN_KEY_LEN_WEP40: + cmd.keytype[i] = CMD_TYPE_WEP_40_BIT; + break; + case WLAN_KEY_LEN_WEP104: + cmd.keytype[i] = CMD_TYPE_WEP_104_BIT; + break; + default: + cmd.keytype[i] = 0; + break; + } + memcpy(cmd.keymaterial[i], priv->wep_key[i], + priv->wep_key_len[i]); + } + + ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); + } else { + /* Otherwise remove all wep keys */ + ret = lbs_remove_wep_keys(priv); + } + + lbs_deb_leave(LBS_DEB_CFG80211); + return ret; +} + + +/* + * Enable/Disable RSN status + */ +static int lbs_enable_rsn(struct lbs_private *priv, int enable) +{ + struct cmd_ds_802_11_enable_rsn cmd; + int ret; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", enable); + + /* + * cmd 2f 00 + * size 0c 00 + * sequence xx xx + * result 00 00 + * action 01 00 ACT_SET + * enable 01 00 + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.enable = cpu_to_le16(enable); + + ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd); + + lbs_deb_leave(LBS_DEB_CFG80211); + return ret; +} + + +/* + * Set WPA/WPA key material + */ + +/* + * like "struct cmd_ds_802_11_key_material", but with cmd_header. Once we + * get rid of WEXT, this should go into host.h + */ + +struct cmd_key_material { + struct cmd_header hdr; + + __le16 action; + struct MrvlIEtype_keyParamSet param; +} __packed; + +static int lbs_set_key_material(struct lbs_private *priv, + int key_type, int key_info, + const u8 *key, u16 key_len) +{ + struct cmd_key_material cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CFG80211); + + /* + * Example for WPA (TKIP): + * + * cmd 5e 00 + * size 34 00 + * sequence xx xx + * result 00 00 + * action 01 00 + * TLV type 00 01 key param + * length 00 26 + * key type 01 00 TKIP + * key info 06 00 UNICAST | ENABLED + * key len 20 00 + * key 32 bytes + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.param.type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + cmd.param.length = cpu_to_le16(sizeof(cmd.param) - 4); + cmd.param.keytypeid = cpu_to_le16(key_type); + cmd.param.keyinfo = cpu_to_le16(key_info); + cmd.param.keylen = cpu_to_le16(key_len); + if (key && key_len) + memcpy(cmd.param.key, key, key_len); + + ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd); + + lbs_deb_leave(LBS_DEB_CFG80211); + return ret; +} + + +/* + * Sets the auth type (open, shared, etc) in the firmware. That + * we use CMD_802_11_AUTHENTICATE is misleading, this firmware + * command doesn't send an authentication frame at all, it just + * stores the auth_type. + */ +static int lbs_set_authtype(struct lbs_private *priv, + struct cfg80211_connect_params *sme) +{ + struct cmd_ds_802_11_authenticate cmd; + int ret; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", sme->auth_type); + + /* + * cmd 11 00 + * size 19 00 + * sequence xx xx + * result 00 00 + * BSS id 00 13 19 80 da 30 + * auth type 00 + * reserved 00 00 00 00 00 00 00 00 00 00 + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + if (sme->bssid) + memcpy(cmd.bssid, sme->bssid, ETH_ALEN); + /* convert auth_type */ + ret = lbs_auth_to_authtype(sme->auth_type); + if (ret < 0) + goto done; + + cmd.authtype = ret; + ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd); + + done: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + +/* + * Create association request + */ +#define LBS_ASSOC_MAX_CMD_SIZE \ + (sizeof(struct cmd_ds_802_11_associate) \ + - 512 /* cmd_ds_802_11_associate.iebuf */ \ + + LBS_MAX_SSID_TLV_SIZE \ + + LBS_MAX_CHANNEL_TLV_SIZE \ + + LBS_MAX_CF_PARAM_TLV_SIZE \ + + LBS_MAX_AUTH_TYPE_TLV_SIZE \ + + LBS_MAX_WPA_TLV_SIZE) + +static int lbs_associate(struct lbs_private *priv, + struct cfg80211_bss *bss, + struct cfg80211_connect_params *sme) +{ + struct cmd_ds_802_11_associate_response *resp; + struct cmd_ds_802_11_associate *cmd = kzalloc(LBS_ASSOC_MAX_CMD_SIZE, + GFP_KERNEL); + const u8 *ssid_eid; + size_t len, resp_ie_len; + int status; + int ret; + u8 *pos = &(cmd->iebuf[0]); + u8 *tmp; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (!cmd) { + ret = -ENOMEM; + goto done; + } + + /* + * cmd 50 00 + * length 34 00 + * sequence xx xx + * result 00 00 + * BSS id 00 13 19 80 da 30 + * capabilities 11 00 + * listen interval 0a 00 + * beacon interval 00 00 + * DTIM period 00 + * TLVs xx (up to 512 bytes) + */ + cmd->hdr.command = cpu_to_le16(CMD_802_11_ASSOCIATE); + + /* Fill in static fields */ + memcpy(cmd->bssid, bss->bssid, ETH_ALEN); + cmd->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL); + cmd->capability = cpu_to_le16(bss->capability); + + /* add SSID TLV */ + rcu_read_lock(); + ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (ssid_eid) + pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); + else + lbs_deb_assoc("no SSID\n"); + rcu_read_unlock(); + + /* add DS param TLV */ + if (bss->channel) + pos += lbs_add_channel_tlv(pos, bss->channel->hw_value); + else + lbs_deb_assoc("no channel\n"); + + /* add (empty) CF param TLV */ + pos += lbs_add_cf_param_tlv(pos); + + /* add rates TLV */ + tmp = pos + 4; /* skip Marvell IE header */ + pos += lbs_add_common_rates_tlv(pos, bss); + lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp); + + /* add auth type TLV */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) >= 9) + pos += lbs_add_auth_type_tlv(pos, sme->auth_type); + + /* add WPA/WPA2 TLV */ + if (sme->ie && sme->ie_len) + pos += lbs_add_wpa_tlv(pos, sme->ie, sme->ie_len); + + len = (sizeof(*cmd) - sizeof(cmd->iebuf)) + + (u16)(pos - (u8 *) &cmd->iebuf); + cmd->hdr.size = cpu_to_le16(len); + + lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_CMD", (u8 *) cmd, + le16_to_cpu(cmd->hdr.size)); + + /* store for later use */ + memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN); + + ret = lbs_cmd_with_response(priv, CMD_802_11_ASSOCIATE, cmd); + if (ret) + goto done; + + /* generate connect message to cfg80211 */ + + resp = (void *) cmd; /* recast for easier field access */ + status = le16_to_cpu(resp->statuscode); + + /* Older FW versions map the IEEE 802.11 Status Code in the association + * response to the following values returned in resp->statuscode: + * + * IEEE Status Code Marvell Status Code + * 0 -> 0x0000 ASSOC_RESULT_SUCCESS + * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED + * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED + * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED + * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED + * others -> 0x0003 ASSOC_RESULT_REFUSED + * + * Other response codes: + * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused) + * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for + * association response from the AP) + */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { + switch (status) { + case 0: + break; + case 1: + lbs_deb_assoc("invalid association parameters\n"); + status = WLAN_STATUS_CAPS_UNSUPPORTED; + break; + case 2: + lbs_deb_assoc("timer expired while waiting for AP\n"); + status = WLAN_STATUS_AUTH_TIMEOUT; + break; + case 3: + lbs_deb_assoc("association refused by AP\n"); + status = WLAN_STATUS_ASSOC_DENIED_UNSPEC; + break; + case 4: + lbs_deb_assoc("authentication refused by AP\n"); + status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + break; + default: + lbs_deb_assoc("association failure %d\n", status); + /* v5 OLPC firmware does return the AP status code if + * it's not one of the values above. Let that through. + */ + break; + } + } + + lbs_deb_assoc("status %d, statuscode 0x%04x, capability 0x%04x, " + "aid 0x%04x\n", status, le16_to_cpu(resp->statuscode), + le16_to_cpu(resp->capability), le16_to_cpu(resp->aid)); + + resp_ie_len = le16_to_cpu(resp->hdr.size) + - sizeof(resp->hdr) + - 6; + cfg80211_connect_result(priv->dev, + priv->assoc_bss, + sme->ie, sme->ie_len, + resp->iebuf, resp_ie_len, + status, + GFP_KERNEL); + + if (status == 0) { + /* TODO: get rid of priv->connect_status */ + priv->connect_status = LBS_CONNECTED; + netif_carrier_on(priv->dev); + if (!priv->tx_pending_len) + netif_tx_wake_all_queues(priv->dev); + } + + kfree(cmd); +done: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + +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 = ieee80211_get_num_supported_channels(wiphy); + enum ieee80211_band band; + + creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + + n_channels * sizeof(void *), + GFP_ATOMIC); + if (!creq) + return NULL; + + /* SSIDs come after channels */ + creq->ssids = (void *)&creq->channels[n_channels]; + creq->n_channels = n_channels; + creq->n_ssids = 1; + + /* Scan all available channels */ + i = 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + + if (!wiphy->bands[band]) + continue; + + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + /* ignore disabled channels */ + if (wiphy->bands[band]->channels[j].flags & + IEEE80211_CHAN_DISABLED) + continue; + + creq->channels[i] = &wiphy->bands[band]->channels[j]; + i++; + } + } + if (i) { + /* Set real number of channels specified in creq->channels[] */ + creq->n_channels = i; + + /* Scan for the SSID we're going to connect to */ + memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len); + creq->ssids[0].ssid_len = sme->ssid_len; + } else { + /* No channels found... */ + kfree(creq); + creq = NULL; + } + + return creq; +} + +static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + struct cfg80211_bss *bss = NULL; + int ret = 0; + u8 preamble = RADIO_PREAMBLE_SHORT; + + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (!sme->bssid) { + struct cfg80211_scan_request *creq; + + /* + * Scan for the requested network after waiting for existing + * scans to finish. + */ + lbs_deb_assoc("assoc: waiting for existing scans\n"); + wait_event_interruptible_timeout(priv->scan_q, + (priv->scan_req == NULL), + (15 * HZ)); + + creq = _new_connect_scan_req(wiphy, sme); + if (!creq) { + ret = -EINVAL; + goto done; + } + + lbs_deb_assoc("assoc: scanning for compatible AP\n"); + _internal_start_scan(priv, true, creq); + + lbs_deb_assoc("assoc: waiting for scan to complete\n"); + wait_event_interruptible_timeout(priv->scan_q, + (priv->scan_req == NULL), + (15 * HZ)); + lbs_deb_assoc("assoc: scanning completed\n"); + } + + /* Find the BSS we want using available scan results */ + bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, + sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY_ANY); + if (!bss) { + wiphy_err(wiphy, "assoc: bss %pM not in scan results\n", + sme->bssid); + ret = -ENOENT; + goto done; + } + lbs_deb_assoc("trying %pM\n", bss->bssid); + lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n", + sme->crypto.cipher_group, + sme->key_idx, sme->key_len); + + /* As this is a new connection, clear locally stored WEP keys */ + priv->wep_tx_key = 0; + memset(priv->wep_key, 0, sizeof(priv->wep_key)); + memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); + + /* set/remove WEP keys */ + switch (sme->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* Store provided WEP keys in priv-> */ + priv->wep_tx_key = sme->key_idx; + priv->wep_key_len[sme->key_idx] = sme->key_len; + memcpy(priv->wep_key[sme->key_idx], sme->key, sme->key_len); + /* Set WEP keys and WEP mode */ + lbs_set_wep_keys(priv); + priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE; + lbs_set_mac_control(priv); + /* No RSN mode for WEP */ + lbs_enable_rsn(priv, 0); + break; + case 0: /* there's no WLAN_CIPHER_SUITE_NONE definition */ + /* + * If we don't have no WEP, no WPA and no WPA2, + * we remove all keys like in the WPA/WPA2 setup, + * we just don't set RSN. + * + * Therefore: fall-through + */ + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + /* Remove WEP keys and WEP mode */ + lbs_remove_wep_keys(priv); + priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE; + lbs_set_mac_control(priv); + + /* clear the WPA/WPA2 keys */ + lbs_set_key_material(priv, + KEY_TYPE_ID_WEP, /* doesn't matter */ + KEY_INFO_WPA_UNICAST, + NULL, 0); + lbs_set_key_material(priv, + KEY_TYPE_ID_WEP, /* doesn't matter */ + KEY_INFO_WPA_MCAST, + NULL, 0); + /* RSN mode for WPA/WPA2 */ + lbs_enable_rsn(priv, sme->crypto.cipher_group != 0); + break; + default: + wiphy_err(wiphy, "unsupported cipher group 0x%x\n", + sme->crypto.cipher_group); + ret = -ENOTSUPP; + goto done; + } + + ret = lbs_set_authtype(priv, sme); + if (ret == -ENOTSUPP) { + wiphy_err(wiphy, "unsupported authtype 0x%x\n", sme->auth_type); + goto done; + } + + lbs_set_radio(priv, preamble, 1); + + /* Do the actual association */ + ret = lbs_associate(priv, bss, sme); + + done: + if (bss) + cfg80211_put_bss(wiphy, bss); + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + +int lbs_disconnect(struct lbs_private *priv, u16 reason) +{ + struct cmd_ds_802_11_deauthenticate cmd; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + /* Mildly ugly to use a locally store my own BSSID ... */ + memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN); + cmd.reasoncode = cpu_to_le16(reason); + + ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd); + if (ret) + return ret; + + cfg80211_disconnected(priv->dev, + reason, + NULL, 0, true, + GFP_KERNEL); + priv->connect_status = LBS_DISCONNECTED; + + return 0; +} + +static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code); + + /* store for lbs_cfg_ret_disconnect() */ + priv->disassoc_reason = reason_code; + + return lbs_disconnect(priv, reason_code); +} + +static int lbs_cfg_set_default_key(struct wiphy *wiphy, + struct net_device *netdev, + u8 key_index, bool unicast, + bool multicast) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + + if (netdev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (key_index != priv->wep_tx_key) { + lbs_deb_assoc("set_default_key: to %d\n", key_index); + priv->wep_tx_key = key_index; + lbs_set_wep_keys(priv); + } + + return 0; +} + + +static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev, + u8 idx, bool pairwise, const u8 *mac_addr, + struct key_params *params) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + u16 key_info; + u16 key_type; + int ret = 0; + + if (netdev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter(LBS_DEB_CFG80211); + + lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n", + params->cipher, mac_addr); + lbs_deb_assoc("add_key: key index %d, key len %d\n", + idx, params->key_len); + if (params->key_len) + lbs_deb_hex(LBS_DEB_CFG80211, "KEY", + params->key, params->key_len); + + lbs_deb_assoc("add_key: seq len %d\n", params->seq_len); + if (params->seq_len) + lbs_deb_hex(LBS_DEB_CFG80211, "SEQ", + params->seq, params->seq_len); + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* actually compare if something has changed ... */ + if ((priv->wep_key_len[idx] != params->key_len) || + memcmp(priv->wep_key[idx], + params->key, params->key_len) != 0) { + priv->wep_key_len[idx] = params->key_len; + memcpy(priv->wep_key[idx], + params->key, params->key_len); + lbs_set_wep_keys(priv); + } + break; + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + key_info = KEY_INFO_WPA_ENABLED | ((idx == 0) + ? KEY_INFO_WPA_UNICAST + : KEY_INFO_WPA_MCAST); + key_type = (params->cipher == WLAN_CIPHER_SUITE_TKIP) + ? KEY_TYPE_ID_TKIP + : KEY_TYPE_ID_AES; + lbs_set_key_material(priv, + key_type, + key_info, + params->key, params->key_len); + break; + default: + wiphy_err(wiphy, "unhandled cipher 0x%x\n", params->cipher); + ret = -ENOTSUPP; + break; + } + + return ret; +} + + +static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, bool pairwise, const u8 *mac_addr) +{ + + lbs_deb_enter(LBS_DEB_CFG80211); + + lbs_deb_assoc("del_key: key_idx %d, mac_addr %pM\n", + key_index, mac_addr); + +#ifdef TODO + struct lbs_private *priv = wiphy_priv(wiphy); + /* + * I think can keep this a NO-OP, because: + + * - we clear all keys whenever we do lbs_cfg_connect() anyway + * - neither "iw" nor "wpa_supplicant" won't call this during + * an ongoing connection + * - TODO: but I have to check if this is still true when + * I set the AP to periodic re-keying + * - we've not kzallec() something when we've added a key at + * lbs_cfg_connect() or lbs_cfg_add_key(). + * + * This causes lbs_cfg_del_key() only called at disconnect time, + * where we'd just waste time deleting a key that is not going + * to be used anyway. + */ + if (key_index < 3 && priv->wep_key_len[key_index]) { + priv->wep_key_len[key_index] = 0; + lbs_set_wep_keys(priv); + } +#endif + + return 0; +} + + +/* + * Get station + */ + +static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + s8 signal, noise; + int ret; + size_t i; + + lbs_deb_enter(LBS_DEB_CFG80211); + + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_RX_BYTES) | + BIT(NL80211_STA_INFO_RX_PACKETS); + sinfo->tx_bytes = priv->dev->stats.tx_bytes; + sinfo->tx_packets = priv->dev->stats.tx_packets; + sinfo->rx_bytes = priv->dev->stats.rx_bytes; + sinfo->rx_packets = priv->dev->stats.rx_packets; + + /* Get current RSSI */ + ret = lbs_get_rssi(priv, &signal, &noise); + if (ret == 0) { + sinfo->signal = signal; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); + } + + /* Convert priv->cur_rate from hw_value to NL80211 value */ + for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { + if (priv->cur_rate == lbs_rates[i].hw_value) { + sinfo->txrate.legacy = lbs_rates[i].bitrate; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); + break; + } + } + + return 0; +} + + + + +/* + * Change interface + */ + +static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = 0; + + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; + + switch (type) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + break; + default: + return -EOPNOTSUPP; + } + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (priv->iface_running) + ret = lbs_set_iface_type(priv, type); + + if (!ret) + priv->wdev->iftype = type; + + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + + +/* + * IBSS (Ad-Hoc) + */ + +/* + * The firmware needs the following bits masked out of the beacon-derived + * capability field when associating/joining to a BSS: + * 9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused) + */ +#define CAPINFO_MASK (~(0xda00)) + + +static void lbs_join_post(struct lbs_private *priv, + struct cfg80211_ibss_params *params, + u8 *bssid, u16 capability) +{ + u8 fake_ie[2 + IEEE80211_MAX_SSID_LEN + /* ssid */ + 2 + 4 + /* basic rates */ + 2 + 1 + /* DS parameter */ + 2 + 2 + /* atim */ + 2 + 8]; /* extended rates */ + u8 *fake = fake_ie; + struct cfg80211_bss *bss; + + lbs_deb_enter(LBS_DEB_CFG80211); + + /* + * For cfg80211_inform_bss, we'll need a fake IE, as we can't get + * the real IE from the firmware. So we fabricate a fake IE based on + * what the firmware actually sends (sniffed with wireshark). + */ + /* Fake SSID IE */ + *fake++ = WLAN_EID_SSID; + *fake++ = params->ssid_len; + memcpy(fake, params->ssid, params->ssid_len); + fake += params->ssid_len; + /* Fake supported basic rates IE */ + *fake++ = WLAN_EID_SUPP_RATES; + *fake++ = 4; + *fake++ = 0x82; + *fake++ = 0x84; + *fake++ = 0x8b; + *fake++ = 0x96; + /* Fake DS channel IE */ + *fake++ = WLAN_EID_DS_PARAMS; + *fake++ = 1; + *fake++ = params->chandef.chan->hw_value; + /* Fake IBSS params IE */ + *fake++ = WLAN_EID_IBSS_PARAMS; + *fake++ = 2; + *fake++ = 0; /* ATIM=0 */ + *fake++ = 0; + /* Fake extended rates IE, TODO: don't add this for 802.11b only, + * but I don't know how this could be checked */ + *fake++ = WLAN_EID_EXT_SUPP_RATES; + *fake++ = 8; + *fake++ = 0x0c; + *fake++ = 0x12; + *fake++ = 0x18; + *fake++ = 0x24; + *fake++ = 0x30; + *fake++ = 0x48; + *fake++ = 0x60; + *fake++ = 0x6c; + lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie); + + bss = cfg80211_inform_bss(priv->wdev->wiphy, + params->chandef.chan, + CFG80211_BSS_FTYPE_UNKNOWN, + bssid, + 0, + capability, + params->beacon_interval, + fake_ie, fake - fake_ie, + 0, GFP_KERNEL); + cfg80211_put_bss(priv->wdev->wiphy, bss); + + memcpy(priv->wdev->ssid, params->ssid, params->ssid_len); + priv->wdev->ssid_len = params->ssid_len; + + cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan, + GFP_KERNEL); + + /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */ + priv->connect_status = LBS_CONNECTED; + netif_carrier_on(priv->dev); + if (!priv->tx_pending_len) + netif_wake_queue(priv->dev); + + lbs_deb_leave(LBS_DEB_CFG80211); +} + +static int lbs_ibss_join_existing(struct lbs_private *priv, + struct cfg80211_ibss_params *params, + struct cfg80211_bss *bss) +{ + const u8 *rates_eid; + struct cmd_ds_802_11_ad_hoc_join cmd; + u8 preamble = RADIO_PREAMBLE_SHORT; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CFG80211); + + /* TODO: set preamble based on scan result */ + ret = lbs_set_radio(priv, preamble, 1); + if (ret) + goto out; + + /* + * Example CMD_802_11_AD_HOC_JOIN command: + * + * command 2c 00 CMD_802_11_AD_HOC_JOIN + * size 65 00 + * sequence xx xx + * result 00 00 + * bssid 02 27 27 97 2f 96 + * ssid 49 42 53 53 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * type 02 CMD_BSS_TYPE_IBSS + * beacon period 64 00 + * dtim period 00 + * timestamp 00 00 00 00 00 00 00 00 + * localtime 00 00 00 00 00 00 00 00 + * IE DS 03 + * IE DS len 01 + * IE DS channel 01 + * reserveed 00 00 00 00 + * IE IBSS 06 + * IE IBSS len 02 + * IE IBSS atim 00 00 + * reserved 00 00 00 00 + * capability 02 00 + * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c 00 + * fail timeout ff 00 + * probe delay 00 00 + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + + memcpy(cmd.bss.bssid, bss->bssid, ETH_ALEN); + memcpy(cmd.bss.ssid, params->ssid, params->ssid_len); + cmd.bss.type = CMD_BSS_TYPE_IBSS; + cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval); + cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS; + cmd.bss.ds.header.len = 1; + cmd.bss.ds.channel = params->chandef.chan->hw_value; + cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS; + cmd.bss.ibss.header.len = 2; + cmd.bss.ibss.atimwindow = 0; + cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK); + + /* set rates to the intersection of our rates and the rates in the + bss */ + rcu_read_lock(); + rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); + if (!rates_eid) { + lbs_add_rates(cmd.bss.rates); + } else { + int hw, i; + u8 rates_max = rates_eid[1]; + u8 *rates = cmd.bss.rates; + for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { + u8 hw_rate = lbs_rates[hw].bitrate / 5; + for (i = 0; i < rates_max; i++) { + if (hw_rate == (rates_eid[i+2] & 0x7f)) { + u8 rate = rates_eid[i+2]; + if (rate == 0x02 || rate == 0x04 || + rate == 0x0b || rate == 0x16) + rate |= 0x80; + *rates++ = rate; + } + } + } + } + rcu_read_unlock(); + + /* Only v8 and below support setting this */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { + cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); + cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); + } + ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd); + if (ret) + goto out; + + /* + * This is a sample response to CMD_802_11_AD_HOC_JOIN: + * + * response 2c 80 + * size 09 00 + * sequence xx xx + * result 00 00 + * reserved 00 + */ + lbs_join_post(priv, params, bss->bssid, bss->capability); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + + +static int lbs_ibss_start_new(struct lbs_private *priv, + struct cfg80211_ibss_params *params) +{ + struct cmd_ds_802_11_ad_hoc_start cmd; + struct cmd_ds_802_11_ad_hoc_result *resp = + (struct cmd_ds_802_11_ad_hoc_result *) &cmd; + u8 preamble = RADIO_PREAMBLE_SHORT; + int ret = 0; + u16 capability; + + lbs_deb_enter(LBS_DEB_CFG80211); + + ret = lbs_set_radio(priv, preamble, 1); + if (ret) + goto out; + + /* + * Example CMD_802_11_AD_HOC_START command: + * + * command 2b 00 CMD_802_11_AD_HOC_START + * size b1 00 + * sequence xx xx + * result 00 00 + * ssid 54 45 53 54 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * bss type 02 + * beacon period 64 00 + * dtim period 00 + * IE IBSS 06 + * IE IBSS len 02 + * IE IBSS atim 00 00 + * reserved 00 00 00 00 + * IE DS 03 + * IE DS len 01 + * IE DS channel 01 + * reserved 00 00 00 00 + * probe delay 00 00 + * capability 02 00 + * rates 82 84 8b 96 (basic rates with have bit 7 set) + * 0c 12 18 24 30 48 60 6c + * padding 100 bytes + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + memcpy(cmd.ssid, params->ssid, params->ssid_len); + cmd.bsstype = CMD_BSS_TYPE_IBSS; + cmd.beaconperiod = cpu_to_le16(params->beacon_interval); + cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS; + cmd.ibss.header.len = 2; + cmd.ibss.atimwindow = 0; + cmd.ds.header.id = WLAN_EID_DS_PARAMS; + cmd.ds.header.len = 1; + cmd.ds.channel = params->chandef.chan->hw_value; + /* Only v8 and below support setting probe delay */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) + cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); + /* TODO: mix in WLAN_CAPABILITY_PRIVACY */ + capability = WLAN_CAPABILITY_IBSS; + cmd.capability = cpu_to_le16(capability); + lbs_add_rates(cmd.rates); + + + ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd); + if (ret) + goto out; + + /* + * This is a sample response to CMD_802_11_AD_HOC_JOIN: + * + * response 2b 80 + * size 14 00 + * sequence xx xx + * result 00 00 + * reserved 00 + * bssid 02 2b 7b 0f 86 0e + */ + lbs_join_post(priv, params, resp->bssid, capability); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + +static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = 0; + struct cfg80211_bss *bss; + + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (!params->chandef.chan) { + ret = -ENOTSUPP; + goto out; + } + + ret = lbs_set_channel(priv, params->chandef.chan->hw_value); + if (ret) + goto out; + + /* Search if someone is beaconing. This assumes that the + * bss list is populated already */ + bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid, + params->ssid, params->ssid_len, + IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY); + + if (bss) { + ret = lbs_ibss_join_existing(priv, params, bss); + cfg80211_put_bss(wiphy, bss); + } else + ret = lbs_ibss_start_new(priv, params); + + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + +static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + struct cmd_ds_802_11_ad_hoc_stop cmd; + int ret = 0; + + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter(LBS_DEB_CFG80211); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd); + + /* TODO: consider doing this at MACREG_INT_CODE_ADHOC_BCN_LOST time */ + lbs_mac_event_disconnected(priv, true); + + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + + + +/* + * Initialization + */ + +static struct cfg80211_ops lbs_cfg80211_ops = { + .set_monitor_channel = lbs_cfg_set_monitor_channel, + .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel, + .scan = lbs_cfg_scan, + .connect = lbs_cfg_connect, + .disconnect = lbs_cfg_disconnect, + .add_key = lbs_cfg_add_key, + .del_key = lbs_cfg_del_key, + .set_default_key = lbs_cfg_set_default_key, + .get_station = lbs_cfg_get_station, + .change_virtual_intf = lbs_change_intf, + .join_ibss = lbs_join_ibss, + .leave_ibss = lbs_leave_ibss, +}; + + +/* + * At this time lbs_private *priv doesn't even exist, so we just allocate + * memory and don't initialize the wiphy further. This is postponed until we + * can talk to the firmware and happens at registration time in + * lbs_cfg_wiphy_register(). + */ +struct wireless_dev *lbs_cfg_alloc(struct device *dev) +{ + int ret = 0; + struct wireless_dev *wdev; + + lbs_deb_enter(LBS_DEB_CFG80211); + + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) + return ERR_PTR(-ENOMEM); + + wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private)); + if (!wdev->wiphy) { + dev_err(dev, "cannot allocate wiphy\n"); + ret = -ENOMEM; + goto err_wiphy_new; + } + + lbs_deb_leave(LBS_DEB_CFG80211); + return wdev; + + err_wiphy_new: + kfree(wdev); + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ERR_PTR(ret); +} + + +static void lbs_cfg_set_regulatory_hint(struct lbs_private *priv) +{ + struct region_code_mapping { + const char *cn; + int code; + }; + + /* Section 5.17.2 */ + static const struct region_code_mapping regmap[] = { + {"US ", 0x10}, /* US FCC */ + {"CA ", 0x20}, /* Canada */ + {"EU ", 0x30}, /* ETSI */ + {"ES ", 0x31}, /* Spain */ + {"FR ", 0x32}, /* France */ + {"JP ", 0x40}, /* Japan */ + }; + size_t i; + + lbs_deb_enter(LBS_DEB_CFG80211); + + for (i = 0; i < ARRAY_SIZE(regmap); i++) + if (regmap[i].code == priv->regioncode) { + regulatory_hint(priv->wdev->wiphy, regmap[i].cn); + break; + } + + lbs_deb_leave(LBS_DEB_CFG80211); +} + +static void lbs_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + + lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain " + "callback for domain %c%c\n", request->alpha2[0], + request->alpha2[1]); + + memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); + if (lbs_iface_active(priv)) + lbs_set_11d_domain_info(priv); + + lbs_deb_leave(LBS_DEB_CFG80211); +} + +/* + * This function get's called after lbs_setup_firmware() determined the + * firmware capabities. So we can setup the wiphy according to our + * hardware/firmware. + */ +int lbs_cfg_register(struct lbs_private *priv) +{ + struct wireless_dev *wdev = priv->wdev; + int ret; + + lbs_deb_enter(LBS_DEB_CFG80211); + + wdev->wiphy->max_scan_ssids = 1; + wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wdev->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + if (lbs_rtap_supported(priv)) + wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); + if (lbs_mesh_activated(priv)) + wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT); + + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz; + + /* + * We could check priv->fwcapinfo && FW_CAPINFO_WPA, but I have + * never seen a firmware without WPA + */ + wdev->wiphy->cipher_suites = cipher_suites; + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + wdev->wiphy->reg_notifier = lbs_reg_notifier; + + ret = wiphy_register(wdev->wiphy); + if (ret < 0) + pr_err("cannot register wiphy device\n"); + + priv->wiphy_registered = true; + + ret = register_netdev(priv->dev); + if (ret) + pr_err("cannot register network device\n"); + + INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker); + + lbs_cfg_set_regulatory_hint(priv); + + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + +void lbs_scan_deinit(struct lbs_private *priv) +{ + lbs_deb_enter(LBS_DEB_CFG80211); + cancel_delayed_work_sync(&priv->scan_work); +} + + +void lbs_cfg_free(struct lbs_private *priv) +{ + struct wireless_dev *wdev = priv->wdev; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (!wdev) + return; + + if (priv->wiphy_registered) + wiphy_unregister(wdev->wiphy); + + if (wdev->wiphy) + wiphy_free(wdev->wiphy); + + kfree(wdev); +} diff --git a/drivers/net/wireless/marvell/libertas/cfg.h b/drivers/net/wireless/marvell/libertas/cfg.h new file mode 100644 index 000000000000..acccc2922401 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/cfg.h @@ -0,0 +1,21 @@ +#ifndef __LBS_CFG80211_H__ +#define __LBS_CFG80211_H__ + +struct device; +struct lbs_private; +struct regulatory_request; +struct wiphy; + +struct wireless_dev *lbs_cfg_alloc(struct device *dev); +int lbs_cfg_register(struct lbs_private *priv); +void lbs_cfg_free(struct lbs_private *priv); + +void lbs_send_disconnect_notification(struct lbs_private *priv, + bool locally_generated); +void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event); + +void lbs_scan_done(struct lbs_private *priv); +void lbs_scan_deinit(struct lbs_private *priv); +int lbs_disconnect(struct lbs_private *priv, u16 reason); + +#endif diff --git a/drivers/net/wireless/marvell/libertas/cmd.c b/drivers/net/wireless/marvell/libertas/cmd.c new file mode 100644 index 000000000000..0387a5b380c8 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/cmd.c @@ -0,0 +1,1725 @@ +/* + * This file contains the handling of command. + * It prepares command and sends it to firmware when it is ready. + */ + +#include +#include +#include +#include +#include +#include + +#include "decl.h" +#include "cfg.h" +#include "cmd.h" + +#define CAL_NF(nf) ((s32)(-(s32)(nf))) +#define CAL_RSSI(snr, nf) ((s32)((s32)(snr) + CAL_NF(nf))) + +/** + * lbs_cmd_copyback - Simple callback that copies response back into command + * + * @priv: A pointer to &struct lbs_private structure + * @extra: A pointer to the original command structure for which + * 'resp' is a response + * @resp: A pointer to the command response + * + * returns: 0 on success, error on failure + */ +int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, + struct cmd_header *resp) +{ + struct cmd_header *buf = (void *)extra; + uint16_t copy_len; + + copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); + memcpy(buf, resp, copy_len); + return 0; +} +EXPORT_SYMBOL_GPL(lbs_cmd_copyback); + +/** + * lbs_cmd_async_callback - Simple callback that ignores the result. + * Use this if you just want to send a command to the hardware, but don't + * care for the result. + * + * @priv: ignored + * @extra: ignored + * @resp: ignored + * + * returns: 0 for success + */ +static int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra, + struct cmd_header *resp) +{ + return 0; +} + + +/** + * is_command_allowed_in_ps - tests if a command is allowed in Power Save mode + * + * @cmd: the command ID + * + * returns: 1 if allowed, 0 if not allowed + */ +static u8 is_command_allowed_in_ps(u16 cmd) +{ + switch (cmd) { + case CMD_802_11_RSSI: + return 1; + case CMD_802_11_HOST_SLEEP_CFG: + return 1; + default: + break; + } + return 0; +} + +/** + * lbs_update_hw_spec - Updates the hardware details like MAC address + * and regulatory region + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: 0 on success, error on failure + */ +int lbs_update_hw_spec(struct lbs_private *priv) +{ + struct cmd_ds_get_hw_spec cmd; + int ret = -1; + u32 i; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); + ret = lbs_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); + if (ret) + goto out; + + priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); + + /* The firmware release is in an interesting format: the patch + * level is in the most significant nibble ... so fix that: */ + priv->fwrelease = le32_to_cpu(cmd.fwrelease); + priv->fwrelease = (priv->fwrelease << 8) | + (priv->fwrelease >> 24 & 0xff); + + /* Some firmware capabilities: + * CF card firmware 5.0.16p0: cap 0x00000303 + * USB dongle firmware 5.110.17p2: cap 0x00000303 + */ + netdev_info(priv->dev, "%pM, fw %u.%u.%up%u, cap 0x%08x\n", + cmd.permanentaddr, + priv->fwrelease >> 24 & 0xff, + priv->fwrelease >> 16 & 0xff, + priv->fwrelease >> 8 & 0xff, + priv->fwrelease & 0xff, + priv->fwcapinfo); + lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", + cmd.hwifversion, cmd.version); + + /* Clamp region code to 8-bit since FW spec indicates that it should + * only ever be 8-bit, even though the field size is 16-bit. Some firmware + * returns non-zero high 8 bits here. + * + * Firmware version 4.0.102 used in CF8381 has region code shifted. We + * need to check for this problem and handle it properly. + */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4) + priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF; + else + priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* use the region code to search for the index */ + if (priv->regioncode == lbs_region_code_to_index[i]) + break; + } + + /* if it's unidentified region code, use the default (USA) */ + if (i >= MRVDRV_MAX_REGION_CODE) { + priv->regioncode = 0x10; + netdev_info(priv->dev, + "unidentified region code; using the default (USA)\n"); + } + + if (priv->current_addr[0] == 0xff) + memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); + + if (!priv->copied_hwaddr) { + memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN); + if (priv->mesh_dev) + memcpy(priv->mesh_dev->dev_addr, + priv->current_addr, ETH_ALEN); + priv->copied_hwaddr = 1; + } + +out: + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy, + struct cmd_header *resp) +{ + lbs_deb_enter(LBS_DEB_CMD); + if (priv->is_host_sleep_activated) { + priv->is_host_sleep_configured = 0; + if (priv->psstate == PS_STATE_FULL_POWER) { + priv->is_host_sleep_activated = 0; + wake_up_interruptible(&priv->host_sleep_q); + } + } else { + priv->is_host_sleep_configured = 1; + } + lbs_deb_leave(LBS_DEB_CMD); + return 0; +} + +int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, + struct wol_config *p_wol_config) +{ + struct cmd_ds_host_sleep cmd_config; + int ret; + + /* + * Certain firmware versions do not support EHS_REMOVE_WAKEUP command + * and the card will return a failure. Since we need to be + * able to reset the mask, in those cases we set a 0 mask instead. + */ + if (criteria == EHS_REMOVE_WAKEUP && !priv->ehs_remove_supported) + criteria = 0; + + cmd_config.hdr.size = cpu_to_le16(sizeof(cmd_config)); + cmd_config.criteria = cpu_to_le32(criteria); + cmd_config.gpio = priv->wol_gpio; + cmd_config.gap = priv->wol_gap; + + if (p_wol_config != NULL) + memcpy((uint8_t *)&cmd_config.wol_conf, (uint8_t *)p_wol_config, + sizeof(struct wol_config)); + else + cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE; + + ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr, + le16_to_cpu(cmd_config.hdr.size), + lbs_ret_host_sleep_cfg, 0); + if (!ret) { + if (p_wol_config) + memcpy((uint8_t *) p_wol_config, + (uint8_t *)&cmd_config.wol_conf, + sizeof(struct wol_config)); + } else { + netdev_info(priv->dev, "HOST_SLEEP_CFG failed %d\n", ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(lbs_host_sleep_cfg); + +/** + * lbs_set_ps_mode - Sets the Power Save mode + * + * @priv: A pointer to &struct lbs_private structure + * @cmd_action: The Power Save operation (PS_MODE_ACTION_ENTER_PS or + * PS_MODE_ACTION_EXIT_PS) + * @block: Whether to block on a response or not + * + * returns: 0 on success, error on failure + */ +int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block) +{ + struct cmd_ds_802_11_ps_mode cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(cmd_action); + + if (cmd_action == PS_MODE_ACTION_ENTER_PS) { + lbs_deb_cmd("PS_MODE: action ENTER_PS\n"); + cmd.multipledtim = cpu_to_le16(1); /* Default DTIM multiple */ + } else if (cmd_action == PS_MODE_ACTION_EXIT_PS) { + lbs_deb_cmd("PS_MODE: action EXIT_PS\n"); + } else { + /* We don't handle CONFIRM_SLEEP here because it needs to + * be fastpathed to the firmware. + */ + lbs_deb_cmd("PS_MODE: unknown action 0x%X\n", cmd_action); + ret = -EOPNOTSUPP; + goto out; + } + + if (block) + ret = lbs_cmd_with_response(priv, CMD_802_11_PS_MODE, &cmd); + else + lbs_cmd_async(priv, CMD_802_11_PS_MODE, &cmd.hdr, sizeof (cmd)); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, + struct sleep_params *sp) +{ + struct cmd_ds_802_11_sleep_params cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + if (cmd_action == CMD_ACT_GET) { + memset(&cmd, 0, sizeof(cmd)); + } else { + cmd.error = cpu_to_le16(sp->sp_error); + cmd.offset = cpu_to_le16(sp->sp_offset); + cmd.stabletime = cpu_to_le16(sp->sp_stabletime); + cmd.calcontrol = sp->sp_calcontrol; + cmd.externalsleepclk = sp->sp_extsleepclk; + cmd.reserved = cpu_to_le16(sp->sp_reserved); + } + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(cmd_action); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SLEEP_PARAMS, &cmd); + + if (!ret) { + lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, " + "calcontrol 0x%x extsleepclk 0x%x\n", + le16_to_cpu(cmd.error), le16_to_cpu(cmd.offset), + le16_to_cpu(cmd.stabletime), cmd.calcontrol, + cmd.externalsleepclk); + + sp->sp_error = le16_to_cpu(cmd.error); + sp->sp_offset = le16_to_cpu(cmd.offset); + sp->sp_stabletime = le16_to_cpu(cmd.stabletime); + sp->sp_calcontrol = cmd.calcontrol; + sp->sp_extsleepclk = cmd.externalsleepclk; + sp->sp_reserved = le16_to_cpu(cmd.reserved); + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return 0; +} + +static int lbs_wait_for_ds_awake(struct lbs_private *priv) +{ + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + if (priv->is_deep_sleep) { + if (!wait_event_interruptible_timeout(priv->ds_awake_q, + !priv->is_deep_sleep, (10 * HZ))) { + netdev_err(priv->dev, "ds_awake_q: timer expired\n"); + ret = -1; + } + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep) +{ + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + if (deep_sleep) { + if (priv->is_deep_sleep != 1) { + lbs_deb_cmd("deep sleep: sleep\n"); + BUG_ON(!priv->enter_deep_sleep); + ret = priv->enter_deep_sleep(priv); + if (!ret) { + netif_stop_queue(priv->dev); + netif_carrier_off(priv->dev); + } + } else { + netdev_err(priv->dev, "deep sleep: already enabled\n"); + } + } else { + if (priv->is_deep_sleep) { + lbs_deb_cmd("deep sleep: wakeup\n"); + BUG_ON(!priv->exit_deep_sleep); + ret = priv->exit_deep_sleep(priv); + if (!ret) { + ret = lbs_wait_for_ds_awake(priv); + if (ret) + netdev_err(priv->dev, + "deep sleep: wakeup failed\n"); + } + } + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +static int lbs_ret_host_sleep_activate(struct lbs_private *priv, + unsigned long dummy, + struct cmd_header *cmd) +{ + lbs_deb_enter(LBS_DEB_FW); + priv->is_host_sleep_activated = 1; + wake_up_interruptible(&priv->host_sleep_q); + lbs_deb_leave(LBS_DEB_FW); + return 0; +} + +int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep) +{ + struct cmd_header cmd; + int ret = 0; + uint32_t criteria = EHS_REMOVE_WAKEUP; + + lbs_deb_enter(LBS_DEB_CMD); + + if (host_sleep) { + if (priv->is_host_sleep_activated != 1) { + memset(&cmd, 0, sizeof(cmd)); + ret = lbs_host_sleep_cfg(priv, priv->wol_criteria, + (struct wol_config *)NULL); + if (ret) { + netdev_info(priv->dev, + "Host sleep configuration failed: %d\n", + ret); + return ret; + } + if (priv->psstate == PS_STATE_FULL_POWER) { + ret = __lbs_cmd(priv, + CMD_802_11_HOST_SLEEP_ACTIVATE, + &cmd, + sizeof(cmd), + lbs_ret_host_sleep_activate, 0); + if (ret) + netdev_info(priv->dev, + "HOST_SLEEP_ACTIVATE failed: %d\n", + ret); + } + + if (!wait_event_interruptible_timeout( + priv->host_sleep_q, + priv->is_host_sleep_activated, + (10 * HZ))) { + netdev_err(priv->dev, + "host_sleep_q: timer expired\n"); + ret = -1; + } + } else { + netdev_err(priv->dev, "host sleep: already enabled\n"); + } + } else { + if (priv->is_host_sleep_activated) + ret = lbs_host_sleep_cfg(priv, criteria, + (struct wol_config *)NULL); + } + + return ret; +} + +/** + * lbs_set_snmp_mib - Set an SNMP MIB value + * + * @priv: A pointer to &struct lbs_private structure + * @oid: The OID to set in the firmware + * @val: Value to set the OID to + * + * returns: 0 on success, error on failure + */ +int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val) +{ + struct cmd_ds_802_11_snmp_mib cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof (cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.oid = cpu_to_le16((u16) oid); + + switch (oid) { + case SNMP_MIB_OID_BSS_TYPE: + cmd.bufsize = cpu_to_le16(sizeof(u8)); + cmd.value[0] = val; + break; + case SNMP_MIB_OID_11D_ENABLE: + case SNMP_MIB_OID_FRAG_THRESHOLD: + case SNMP_MIB_OID_RTS_THRESHOLD: + case SNMP_MIB_OID_SHORT_RETRY_LIMIT: + case SNMP_MIB_OID_LONG_RETRY_LIMIT: + cmd.bufsize = cpu_to_le16(sizeof(u16)); + *((__le16 *)(&cmd.value)) = cpu_to_le16(val); + break; + default: + lbs_deb_cmd("SNMP_CMD: (set) unhandled OID 0x%x\n", oid); + ret = -EINVAL; + goto out; + } + + lbs_deb_cmd("SNMP_CMD: (set) oid 0x%x, oid size 0x%x, value 0x%x\n", + le16_to_cpu(cmd.oid), le16_to_cpu(cmd.bufsize), val); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +/** + * lbs_get_snmp_mib - Get an SNMP MIB value + * + * @priv: A pointer to &struct lbs_private structure + * @oid: The OID to retrieve from the firmware + * @out_val: Location for the returned value + * + * returns: 0 on success, error on failure + */ +int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val) +{ + struct cmd_ds_802_11_snmp_mib cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof (cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_GET); + cmd.oid = cpu_to_le16(oid); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); + if (ret) + goto out; + + switch (le16_to_cpu(cmd.bufsize)) { + case sizeof(u8): + *out_val = cmd.value[0]; + break; + case sizeof(u16): + *out_val = le16_to_cpu(*((__le16 *)(&cmd.value))); + break; + default: + lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n", + oid, le16_to_cpu(cmd.bufsize)); + break; + } + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +/** + * lbs_get_tx_power - Get the min, max, and current TX power + * + * @priv: A pointer to &struct lbs_private structure + * @curlevel: Current power level in dBm + * @minlevel: Minimum supported power level in dBm (optional) + * @maxlevel: Maximum supported power level in dBm (optional) + * + * returns: 0 on success, error on failure + */ +int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, + s16 *maxlevel) +{ + struct cmd_ds_802_11_rf_tx_power cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_GET); + + ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); + if (ret == 0) { + *curlevel = le16_to_cpu(cmd.curlevel); + if (minlevel) + *minlevel = cmd.minlevel; + if (maxlevel) + *maxlevel = cmd.maxlevel; + } + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +/** + * lbs_set_tx_power - Set the TX power + * + * @priv: A pointer to &struct lbs_private structure + * @dbm: The desired power level in dBm + * + * returns: 0 on success, error on failure + */ +int lbs_set_tx_power(struct lbs_private *priv, s16 dbm) +{ + struct cmd_ds_802_11_rf_tx_power cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.curlevel = cpu_to_le16(dbm); + + lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm); + + ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +/** + * lbs_set_monitor_mode - Enable or disable monitor mode + * (only implemented on OLPC usb8388 FW) + * + * @priv: A pointer to &struct lbs_private structure + * @enable: 1 to enable monitor mode, 0 to disable + * + * returns: 0 on success, error on failure + */ +int lbs_set_monitor_mode(struct lbs_private *priv, int enable) +{ + struct cmd_ds_802_11_monitor_mode cmd; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + if (enable) + cmd.mode = cpu_to_le16(0x1); + + lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable); + + ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd); + if (ret == 0) { + priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP : + ARPHRD_ETHER; + } + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +/** + * lbs_get_channel - Get the radio channel + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: The channel on success, error on failure + */ +static int lbs_get_channel(struct lbs_private *priv) +{ + struct cmd_ds_802_11_rf_channel cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_GET); + + ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); + if (ret) + goto out; + + ret = le16_to_cpu(cmd.channel); + lbs_deb_cmd("current radio channel is %d\n", ret); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +int lbs_update_channel(struct lbs_private *priv) +{ + int ret; + + /* the channel in f/w could be out of sync; get the current channel */ + lbs_deb_enter(LBS_DEB_ASSOC); + + ret = lbs_get_channel(priv); + if (ret > 0) { + priv->channel = ret; + ret = 0; + } + lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); + return ret; +} + +/** + * lbs_set_channel - Set the radio channel + * + * @priv: A pointer to &struct lbs_private structure + * @channel: The desired channel, or 0 to clear a locked channel + * + * returns: 0 on success, error on failure + */ +int lbs_set_channel(struct lbs_private *priv, u8 channel) +{ + struct cmd_ds_802_11_rf_channel cmd; +#ifdef DEBUG + u8 old_channel = priv->channel; +#endif + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); + cmd.channel = cpu_to_le16(channel); + + ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); + if (ret) + goto out; + + priv->channel = (uint8_t) le16_to_cpu(cmd.channel); + lbs_deb_cmd("channel switch from %d to %d\n", old_channel, + priv->channel); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +/** + * lbs_get_rssi - Get current RSSI and noise floor + * + * @priv: A pointer to &struct lbs_private structure + * @rssi: On successful return, signal level in mBm + * @nf: On successful return, Noise floor + * + * returns: The channel on success, error on failure + */ +int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf) +{ + struct cmd_ds_802_11_rssi cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + BUG_ON(rssi == NULL); + BUG_ON(nf == NULL); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + /* Average SNR over last 8 beacons */ + cmd.n_or_snr = cpu_to_le16(8); + + ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd); + if (ret == 0) { + *nf = CAL_NF(le16_to_cpu(cmd.nf)); + *rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf)); + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +/** + * lbs_set_11d_domain_info - Send regulatory and 802.11d domain information + * to the firmware + * + * @priv: pointer to &struct lbs_private + * + * returns: 0 on success, error code on failure +*/ +int lbs_set_11d_domain_info(struct lbs_private *priv) +{ + struct wiphy *wiphy = priv->wdev->wiphy; + struct ieee80211_supported_band **bands = wiphy->bands; + struct cmd_ds_802_11d_domain_info cmd; + struct mrvl_ie_domain_param_set *domain = &cmd.domain; + struct ieee80211_country_ie_triplet *t; + enum ieee80211_band band; + struct ieee80211_channel *ch; + u8 num_triplet = 0; + u8 num_parsed_chan = 0; + u8 first_channel = 0, next_chan = 0, max_pwr = 0; + u8 i, flag = 0; + size_t triplet_size; + int ret = 0; + + lbs_deb_enter(LBS_DEB_11D); + if (!priv->country_code[0]) + goto out; + + memset(&cmd, 0, sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + + lbs_deb_11d("Setting country code '%c%c'\n", + priv->country_code[0], priv->country_code[1]); + + domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); + + /* Set country code */ + domain->country_code[0] = priv->country_code[0]; + domain->country_code[1] = priv->country_code[1]; + domain->country_code[2] = ' '; + + /* Now set up the channel triplets; firmware is somewhat picky here + * and doesn't validate channel numbers and spans; hence it would + * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39. Since + * the last 3 aren't valid channels, the driver is responsible for + * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20) + * etc. + */ + for (band = 0; + (band < IEEE80211_NUM_BANDS) && (num_triplet < MAX_11D_TRIPLETS); + band++) { + + if (!bands[band]) + continue; + + for (i = 0; + (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS); + i++) { + ch = &bands[band]->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (!flag) { + flag = 1; + next_chan = first_channel = (u32) ch->hw_value; + max_pwr = ch->max_power; + num_parsed_chan = 1; + continue; + } + + if ((ch->hw_value == next_chan + 1) && + (ch->max_power == max_pwr)) { + /* Consolidate adjacent channels */ + next_chan++; + num_parsed_chan++; + } else { + /* Add this triplet */ + lbs_deb_11d("11D triplet (%d, %d, %d)\n", + first_channel, num_parsed_chan, + max_pwr); + t = &domain->triplet[num_triplet]; + t->chans.first_channel = first_channel; + t->chans.num_channels = num_parsed_chan; + t->chans.max_power = max_pwr; + num_triplet++; + flag = 0; + } + } + + if (flag) { + /* Add last triplet */ + lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel, + num_parsed_chan, max_pwr); + t = &domain->triplet[num_triplet]; + t->chans.first_channel = first_channel; + t->chans.num_channels = num_parsed_chan; + t->chans.max_power = max_pwr; + num_triplet++; + } + } + + lbs_deb_11d("# triplets %d\n", num_triplet); + + /* Set command header sizes */ + triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet); + domain->header.len = cpu_to_le16(sizeof(domain->country_code) + + triplet_size); + + lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set", + (u8 *) &cmd.domain.country_code, + le16_to_cpu(domain->header.len)); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) + + sizeof(cmd.action) + + sizeof(cmd.domain.header) + + sizeof(cmd.domain.country_code) + + triplet_size); + + ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd); + +out: + lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); + return ret; +} + +/** + * lbs_get_reg - Read a MAC, Baseband, or RF register + * + * @priv: pointer to &struct lbs_private + * @reg: register command, one of CMD_MAC_REG_ACCESS, + * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS + * @offset: byte offset of the register to get + * @value: on success, the value of the register at 'offset' + * + * returns: 0 on success, error code on failure +*/ +int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) +{ + struct cmd_ds_reg_access cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + BUG_ON(value == NULL); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_GET); + cmd.offset = cpu_to_le16(offset); + + if (reg != CMD_MAC_REG_ACCESS && + reg != CMD_BBP_REG_ACCESS && + reg != CMD_RF_REG_ACCESS) { + ret = -EINVAL; + goto out; + } + + ret = lbs_cmd_with_response(priv, reg, &cmd); + if (!ret) { + if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) + *value = cmd.value.bbp_rf; + else if (reg == CMD_MAC_REG_ACCESS) + *value = le32_to_cpu(cmd.value.mac); + } + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +/** + * lbs_set_reg - Write a MAC, Baseband, or RF register + * + * @priv: pointer to &struct lbs_private + * @reg: register command, one of CMD_MAC_REG_ACCESS, + * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS + * @offset: byte offset of the register to set + * @value: the value to write to the register at 'offset' + * + * returns: 0 on success, error code on failure +*/ +int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value) +{ + struct cmd_ds_reg_access cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.offset = cpu_to_le16(offset); + + if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) + cmd.value.bbp_rf = (u8) (value & 0xFF); + else if (reg == CMD_MAC_REG_ACCESS) + cmd.value.mac = cpu_to_le32(value); + else { + ret = -EINVAL; + goto out; + } + + ret = lbs_cmd_with_response(priv, reg, &cmd); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +static void lbs_queue_cmd(struct lbs_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + int addtail = 1; + + lbs_deb_enter(LBS_DEB_HOST); + + if (!cmdnode) { + lbs_deb_host("QUEUE_CMD: cmdnode is NULL\n"); + goto done; + } + if (!cmdnode->cmdbuf->size) { + lbs_deb_host("DNLD_CMD: cmd size is zero\n"); + goto done; + } + cmdnode->result = 0; + + /* Exit_PS command needs to be queued in the header always. */ + if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) { + struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf; + + if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { + if (priv->psstate != PS_STATE_FULL_POWER) + addtail = 0; + } + } + + if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_WAKEUP_CONFIRM) + addtail = 0; + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (addtail) + list_add_tail(&cmdnode->list, &priv->cmdpendingq); + else + list_add(&cmdnode->list, &priv->cmdpendingq); + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", + le16_to_cpu(cmdnode->cmdbuf->command)); + +done: + lbs_deb_leave(LBS_DEB_HOST); +} + +static void lbs_submit_command(struct lbs_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + struct cmd_header *cmd; + uint16_t cmdsize; + uint16_t command; + int timeo = 3 * HZ; + int ret; + + lbs_deb_enter(LBS_DEB_HOST); + + cmd = cmdnode->cmdbuf; + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->seqnum++; + cmd->seqnum = cpu_to_le16(priv->seqnum); + priv->cur_cmd = cmdnode; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + cmdsize = le16_to_cpu(cmd->size); + command = le16_to_cpu(cmd->command); + + /* These commands take longer */ + if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE) + timeo = 5 * HZ; + + lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", + command, le16_to_cpu(cmd->seqnum), cmdsize); + lbs_deb_hex(LBS_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); + + ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); + + if (ret) { + netdev_info(priv->dev, "DNLD_CMD: hw_host_to_card failed: %d\n", + ret); + /* Reset dnld state machine, report failure */ + priv->dnld_sent = DNLD_RES_RECEIVED; + lbs_complete_command(priv, cmdnode, ret); + } + + if (command == CMD_802_11_DEEP_SLEEP) { + if (priv->is_auto_deep_sleep_enabled) { + priv->wakeup_dev_required = 1; + priv->dnld_sent = 0; + } + priv->is_deep_sleep = 1; + lbs_complete_command(priv, cmdnode, 0); + } else { + /* Setup the timer after transmit command */ + mod_timer(&priv->command_timer, jiffies + timeo); + } + + lbs_deb_leave(LBS_DEB_HOST); +} + +/* + * This function inserts command node to cmdfreeq + * after cleans it. Requires priv->driver_lock held. + */ +static void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + lbs_deb_enter(LBS_DEB_HOST); + + if (!cmdnode) + goto out; + + cmdnode->callback = NULL; + cmdnode->callback_arg = 0; + + memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); + + list_add_tail(&cmdnode->list, &priv->cmdfreeq); + out: + lbs_deb_leave(LBS_DEB_HOST); +} + +static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv, + struct cmd_ctrl_node *ptempcmd) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->driver_lock, flags); + __lbs_cleanup_and_insert_cmd(priv, ptempcmd); + spin_unlock_irqrestore(&priv->driver_lock, flags); +} + +void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result) +{ + /* + * Normally, commands are removed from cmdpendingq before being + * submitted. However, we can arrive here on alternative codepaths + * where the command is still pending. Make sure the command really + * isn't part of a list at this point. + */ + list_del_init(&cmd->list); + + cmd->result = result; + cmd->cmdwaitqwoken = 1; + wake_up(&cmd->cmdwait_q); + + if (!cmd->callback || cmd->callback == lbs_cmd_async_callback) + __lbs_cleanup_and_insert_cmd(priv, cmd); + priv->cur_cmd = NULL; + wake_up(&priv->waitq); +} + +void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result) +{ + unsigned long flags; + spin_lock_irqsave(&priv->driver_lock, flags); + __lbs_complete_command(priv, cmd, result); + spin_unlock_irqrestore(&priv->driver_lock, flags); +} + +int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) +{ + struct cmd_ds_802_11_radio_control cmd; + int ret = -EINVAL; + + lbs_deb_enter(LBS_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.control = 0; + + /* Only v8 and below support setting the preamble */ + if (priv->fwrelease < 0x09000000) { + switch (preamble) { + case RADIO_PREAMBLE_SHORT: + case RADIO_PREAMBLE_AUTO: + case RADIO_PREAMBLE_LONG: + cmd.control = cpu_to_le16(preamble); + break; + default: + goto out; + } + } + + if (radio_on) + cmd.control |= cpu_to_le16(0x1); + else { + cmd.control &= cpu_to_le16(~0x1); + priv->txpower_cur = 0; + } + + lbs_deb_cmd("RADIO_CONTROL: radio %s, preamble %d\n", + radio_on ? "ON" : "OFF", preamble); + + priv->radio_on = radio_on; + + ret = lbs_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +void lbs_set_mac_control(struct lbs_private *priv) +{ + struct cmd_ds_mac_control cmd; + + lbs_deb_enter(LBS_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(priv->mac_control); + cmd.reserved = 0; + + lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd)); + + lbs_deb_leave(LBS_DEB_CMD); +} + +int lbs_set_mac_control_sync(struct lbs_private *priv) +{ + struct cmd_ds_mac_control cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(priv->mac_control); + cmd.reserved = 0; + ret = lbs_cmd_with_response(priv, CMD_MAC_CONTROL, &cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +/** + * lbs_allocate_cmd_buffer - allocates the command buffer and links + * it to command free queue + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: 0 for success or -1 on error + */ +int lbs_allocate_cmd_buffer(struct lbs_private *priv) +{ + int ret = 0; + u32 bufsize; + u32 i; + struct cmd_ctrl_node *cmdarray; + + lbs_deb_enter(LBS_DEB_HOST); + + /* Allocate and initialize the command array */ + bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; + if (!(cmdarray = kzalloc(bufsize, GFP_KERNEL))) { + lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); + ret = -1; + goto done; + } + priv->cmd_array = cmdarray; + + /* Allocate and initialize each command buffer in the command array */ + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); + if (!cmdarray[i].cmdbuf) { + lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); + ret = -1; + goto done; + } + } + + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + init_waitqueue_head(&cmdarray[i].cmdwait_q); + lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]); + } + ret = 0; + +done: + lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); + return ret; +} + +/** + * lbs_free_cmd_buffer - free the command buffer + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: 0 for success + */ +int lbs_free_cmd_buffer(struct lbs_private *priv) +{ + struct cmd_ctrl_node *cmdarray; + unsigned int i; + + lbs_deb_enter(LBS_DEB_HOST); + + /* need to check if cmd array is allocated or not */ + if (priv->cmd_array == NULL) { + lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); + goto done; + } + + cmdarray = priv->cmd_array; + + /* Release shared memory buffers */ + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + if (cmdarray[i].cmdbuf) { + kfree(cmdarray[i].cmdbuf); + cmdarray[i].cmdbuf = NULL; + } + } + + /* Release cmd_ctrl_node */ + if (priv->cmd_array) { + kfree(priv->cmd_array); + priv->cmd_array = NULL; + } + +done: + lbs_deb_leave(LBS_DEB_HOST); + return 0; +} + +/** + * lbs_get_free_cmd_node - gets a free command node if available in + * command free queue + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: A pointer to &cmd_ctrl_node structure on success + * or %NULL on error + */ +static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv) +{ + struct cmd_ctrl_node *tempnode; + unsigned long flags; + + lbs_deb_enter(LBS_DEB_HOST); + + if (!priv) + return NULL; + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!list_empty(&priv->cmdfreeq)) { + tempnode = list_first_entry(&priv->cmdfreeq, + struct cmd_ctrl_node, list); + list_del_init(&tempnode->list); + } else { + lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); + tempnode = NULL; + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + lbs_deb_leave(LBS_DEB_HOST); + return tempnode; +} + +/** + * lbs_execute_next_command - execute next command in command + * pending queue. Will put firmware back to PS mode if applicable. + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: 0 on success or -1 on error + */ +int lbs_execute_next_command(struct lbs_private *priv) +{ + struct cmd_ctrl_node *cmdnode = NULL; + struct cmd_header *cmd; + unsigned long flags; + int ret = 0; + + /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the + * only caller to us is lbs_thread() and we get even when a + * data packet is received */ + lbs_deb_enter(LBS_DEB_THREAD); + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->cur_cmd) { + netdev_alert(priv->dev, + "EXEC_NEXT_CMD: already processing command!\n"); + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + + if (!list_empty(&priv->cmdpendingq)) { + cmdnode = list_first_entry(&priv->cmdpendingq, + struct cmd_ctrl_node, list); + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + if (cmdnode) { + cmd = cmdnode->cmdbuf; + + if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) { + if ((priv->psstate == PS_STATE_SLEEP) || + (priv->psstate == PS_STATE_PRE_SLEEP)) { + lbs_deb_host( + "EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n", + le16_to_cpu(cmd->command), + priv->psstate); + ret = -1; + goto done; + } + lbs_deb_host("EXEC_NEXT_CMD: OK to send command " + "0x%04x in psstate %d\n", + le16_to_cpu(cmd->command), priv->psstate); + } else if (priv->psstate != PS_STATE_FULL_POWER) { + /* + * 1. Non-PS command: + * Queue it. set needtowakeup to TRUE if current state + * is SLEEP, otherwise call send EXIT_PS. + * 2. PS command but not EXIT_PS: + * Ignore it. + * 3. PS command EXIT_PS: + * Set needtowakeup to TRUE if current state is SLEEP, + * otherwise send this command down to firmware + * immediately. + */ + if (cmd->command != cpu_to_le16(CMD_802_11_PS_MODE)) { + /* Prepare to send Exit PS, + * this non PS command will be sent later */ + if ((priv->psstate == PS_STATE_SLEEP) + || (priv->psstate == PS_STATE_PRE_SLEEP) + ) { + /* w/ new scheme, it will not reach here. + since it is blocked in main_thread. */ + priv->needtowakeup = 1; + } else { + lbs_set_ps_mode(priv, + PS_MODE_ACTION_EXIT_PS, + false); + } + + ret = 0; + goto done; + } else { + /* + * PS command. Ignore it if it is not Exit_PS. + * otherwise send it down immediately. + */ + struct cmd_ds_802_11_ps_mode *psm = (void *)&cmd[1]; + + lbs_deb_host( + "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n", + psm->action); + if (psm->action != + cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { + lbs_deb_host( + "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n"); + lbs_complete_command(priv, cmdnode, 0); + + ret = 0; + goto done; + } + + if ((priv->psstate == PS_STATE_SLEEP) || + (priv->psstate == PS_STATE_PRE_SLEEP)) { + lbs_deb_host( + "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n"); + lbs_complete_command(priv, cmdnode, 0); + priv->needtowakeup = 1; + + ret = 0; + goto done; + } + + lbs_deb_host( + "EXEC_NEXT_CMD: sending EXIT_PS\n"); + } + } + spin_lock_irqsave(&priv->driver_lock, flags); + list_del_init(&cmdnode->list); + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", + le16_to_cpu(cmd->command)); + lbs_submit_command(priv, cmdnode); + } else { + /* + * check if in power save mode, if yes, put the device back + * to PS mode + */ +#ifdef TODO + /* + * This was the old code for libertas+wext. Someone that + * understands this beast should re-code it in a sane way. + * + * I actually don't understand why this is related to WPA + * and to connection status, shouldn't powering should be + * independ of such things? + */ + if ((priv->psmode != LBS802_11POWERMODECAM) && + (priv->psstate == PS_STATE_FULL_POWER) && + ((priv->connect_status == LBS_CONNECTED) || + lbs_mesh_connected(priv))) { + if (priv->secinfo.WPAenabled || + priv->secinfo.WPA2enabled) { + /* check for valid WPA group keys */ + if (priv->wpa_mcast_key.len || + priv->wpa_unicast_key.len) { + lbs_deb_host( + "EXEC_NEXT_CMD: WPA enabled and GTK_SET" + " go back to PS_SLEEP"); + lbs_set_ps_mode(priv, + PS_MODE_ACTION_ENTER_PS, + false); + } + } else { + lbs_deb_host( + "EXEC_NEXT_CMD: cmdpendingq empty, " + "go back to PS_SLEEP"); + lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, + false); + } + } +#endif + } + + ret = 0; +done: + lbs_deb_leave(LBS_DEB_THREAD); + return ret; +} + +static void lbs_send_confirmsleep(struct lbs_private *priv) +{ + unsigned long flags; + int ret; + + lbs_deb_enter(LBS_DEB_HOST); + lbs_deb_hex(LBS_DEB_HOST, "sleep confirm", (u8 *) &confirm_sleep, + sizeof(confirm_sleep)); + + ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &confirm_sleep, + sizeof(confirm_sleep)); + if (ret) { + netdev_alert(priv->dev, "confirm_sleep failed\n"); + goto out; + } + + spin_lock_irqsave(&priv->driver_lock, flags); + + /* We don't get a response on the sleep-confirmation */ + priv->dnld_sent = DNLD_RES_RECEIVED; + + if (priv->is_host_sleep_configured) { + priv->is_host_sleep_activated = 1; + wake_up_interruptible(&priv->host_sleep_q); + } + + /* If nothing to do, go back to sleep (?) */ + if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx]) + priv->psstate = PS_STATE_SLEEP; + + spin_unlock_irqrestore(&priv->driver_lock, flags); + +out: + lbs_deb_leave(LBS_DEB_HOST); +} + +/** + * lbs_ps_confirm_sleep - checks condition and prepares to + * send sleep confirm command to firmware if ok + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: n/a + */ +void lbs_ps_confirm_sleep(struct lbs_private *priv) +{ + unsigned long flags =0; + int allowed = 1; + + lbs_deb_enter(LBS_DEB_HOST); + + spin_lock_irqsave(&priv->driver_lock, flags); + if (priv->dnld_sent) { + allowed = 0; + lbs_deb_host("dnld_sent was set\n"); + } + + /* In-progress command? */ + if (priv->cur_cmd) { + allowed = 0; + lbs_deb_host("cur_cmd was set\n"); + } + + /* Pending events or command responses? */ + if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) { + allowed = 0; + lbs_deb_host("pending events or command responses\n"); + } + spin_unlock_irqrestore(&priv->driver_lock, flags); + + if (allowed) { + lbs_deb_host("sending lbs_ps_confirm_sleep\n"); + lbs_send_confirmsleep(priv); + } else { + lbs_deb_host("sleep confirm has been delayed\n"); + } + + lbs_deb_leave(LBS_DEB_HOST); +} + + +/** + * lbs_set_tpc_cfg - Configures the transmission power control functionality + * + * @priv: A pointer to &struct lbs_private structure + * @enable: Transmission power control enable + * @p0: Power level when link quality is good (dBm). + * @p1: Power level when link quality is fair (dBm). + * @p2: Power level when link quality is poor (dBm). + * @usesnr: Use Signal to Noise Ratio in TPC + * + * returns: 0 on success + */ +int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, + int8_t p2, int usesnr) +{ + struct cmd_ds_802_11_tpc_cfg cmd; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.enable = !!enable; + cmd.usesnr = !!usesnr; + cmd.P0 = p0; + cmd.P1 = p1; + cmd.P2 = p2; + + ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd); + + return ret; +} + +/** + * lbs_set_power_adapt_cfg - Configures the power adaptation settings + * + * @priv: A pointer to &struct lbs_private structure + * @enable: Power adaptation enable + * @p0: Power level for 1, 2, 5.5 and 11 Mbps (dBm). + * @p1: Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm). + * @p2: Power level for 48 and 54 Mbps (dBm). + * + * returns: 0 on Success + */ + +int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, + int8_t p1, int8_t p2) +{ + struct cmd_ds_802_11_pa_cfg cmd; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.enable = !!enable; + cmd.P0 = p0; + cmd.P1 = p1; + cmd.P2 = p2; + + ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd); + + return ret; +} + + +struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, + uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), + unsigned long callback_arg) +{ + struct cmd_ctrl_node *cmdnode; + + lbs_deb_enter(LBS_DEB_HOST); + + if (priv->surpriseremoved) { + lbs_deb_host("PREP_CMD: card removed\n"); + cmdnode = ERR_PTR(-ENOENT); + goto done; + } + + /* No commands are allowed in Deep Sleep until we toggle the GPIO + * to wake up the card and it has signaled that it's ready. + */ + if (!priv->is_auto_deep_sleep_enabled) { + if (priv->is_deep_sleep) { + lbs_deb_cmd("command not allowed in deep sleep\n"); + cmdnode = ERR_PTR(-EBUSY); + goto done; + } + } + + cmdnode = lbs_get_free_cmd_node(priv); + if (cmdnode == NULL) { + lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); + + /* Wake up main thread to execute next command */ + wake_up(&priv->waitq); + cmdnode = ERR_PTR(-ENOBUFS); + goto done; + } + + cmdnode->callback = callback; + cmdnode->callback_arg = callback_arg; + + /* Copy the incoming command to the buffer */ + memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); + + /* Set command, clean result, move to buffer */ + cmdnode->cmdbuf->command = cpu_to_le16(command); + cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); + cmdnode->cmdbuf->result = 0; + + lbs_deb_host("PREP_CMD: command 0x%04x\n", command); + + cmdnode->cmdwaitqwoken = 0; + lbs_queue_cmd(priv, cmdnode); + wake_up(&priv->waitq); + + done: + lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode); + return cmdnode; +} + +void lbs_cmd_async(struct lbs_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size) +{ + lbs_deb_enter(LBS_DEB_CMD); + __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, + lbs_cmd_async_callback, 0); + lbs_deb_leave(LBS_DEB_CMD); +} + +int __lbs_cmd(struct lbs_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), + unsigned long callback_arg) +{ + struct cmd_ctrl_node *cmdnode; + unsigned long flags; + int ret = 0; + + lbs_deb_enter(LBS_DEB_HOST); + + cmdnode = __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, + callback, callback_arg); + if (IS_ERR(cmdnode)) { + ret = PTR_ERR(cmdnode); + goto done; + } + + might_sleep(); + + /* + * Be careful with signals here. A signal may be received as the system + * goes into suspend or resume. We do not want this to interrupt the + * command, so we perform an uninterruptible sleep. + */ + wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); + + spin_lock_irqsave(&priv->driver_lock, flags); + ret = cmdnode->result; + if (ret) + netdev_info(priv->dev, "PREP_CMD: command 0x%04x failed: %d\n", + command, ret); + + __lbs_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&priv->driver_lock, flags); + +done: + lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(__lbs_cmd); diff --git a/drivers/net/wireless/marvell/libertas/cmd.h b/drivers/net/wireless/marvell/libertas/cmd.h new file mode 100644 index 000000000000..0c5444b02c64 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/cmd.h @@ -0,0 +1,141 @@ +/* Copyright (C) 2007, Red Hat, Inc. */ + +#ifndef _LBS_CMD_H_ +#define _LBS_CMD_H_ + +#include + +#include "host.h" +#include "dev.h" + + +/* Command & response transfer between host and card */ + +struct cmd_ctrl_node { + struct list_head list; + int result; + /* command response */ + int (*callback)(struct lbs_private *, + unsigned long, + struct cmd_header *); + unsigned long callback_arg; + /* command data */ + struct cmd_header *cmdbuf; + /* wait queue */ + u16 cmdwaitqwoken; + wait_queue_head_t cmdwait_q; +}; + + +/* lbs_cmd() infers the size of the buffer to copy data back into, from + the size of the target of the pointer. Since the command to be sent + may often be smaller, that size is set in cmd->size by the caller.*/ +#define lbs_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \ + uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \ + (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \ + __lbs_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \ +}) + +#define lbs_cmd_with_response(priv, cmdnr, cmd) \ + lbs_cmd(priv, cmdnr, cmd, lbs_cmd_copyback, (unsigned long) (cmd)) + +void lbs_cmd_async(struct lbs_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size); + +int __lbs_cmd(struct lbs_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), + unsigned long callback_arg); + +struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, + uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), + unsigned long callback_arg); + +int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, + struct cmd_header *resp); + +int lbs_allocate_cmd_buffer(struct lbs_private *priv); +int lbs_free_cmd_buffer(struct lbs_private *priv); + +int lbs_execute_next_command(struct lbs_private *priv); +void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result); +void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result); +int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len); + + +/* From cmdresp.c */ + +void lbs_mac_event_disconnected(struct lbs_private *priv, + bool locally_generated); + + + +/* Events */ + +int lbs_process_event(struct lbs_private *priv, u32 event); + + +/* Actual commands */ + +int lbs_update_hw_spec(struct lbs_private *priv); + +int lbs_set_channel(struct lbs_private *priv, u8 channel); + +int lbs_update_channel(struct lbs_private *priv); + +int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, + struct wol_config *p_wol_config); + +int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, + struct sleep_params *sp); + +void lbs_ps_confirm_sleep(struct lbs_private *priv); + +int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on); + +void lbs_set_mac_control(struct lbs_private *priv); +int lbs_set_mac_control_sync(struct lbs_private *priv); + +int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, + s16 *maxlevel); + +int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val); + +int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val); + + +/* Commands only used in wext.c, assoc. and scan.c */ + +int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, + int8_t p1, int8_t p2); + +int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, + int8_t p2, int usesnr); + +int lbs_set_data_rate(struct lbs_private *priv, u8 rate); + +int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv, + uint16_t cmd_action); + +int lbs_set_tx_power(struct lbs_private *priv, s16 dbm); + +int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep); + +int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep); + +int lbs_set_monitor_mode(struct lbs_private *priv, int enable); + +int lbs_get_rssi(struct lbs_private *priv, s8 *snr, s8 *nf); + +int lbs_set_11d_domain_info(struct lbs_private *priv); + +int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value); + +int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value); + +int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block); + +#endif /* _LBS_CMD_H */ diff --git a/drivers/net/wireless/marvell/libertas/cmdresp.c b/drivers/net/wireless/marvell/libertas/cmdresp.c new file mode 100644 index 000000000000..e5442e8956f7 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/cmdresp.c @@ -0,0 +1,353 @@ +/* + * This file contains the handling of command + * responses as well as events generated by firmware. + */ + +#include +#include +#include +#include +#include +#include + +#include "cfg.h" +#include "cmd.h" + +/** + * lbs_mac_event_disconnected - handles disconnect event. It + * reports disconnect to upper layer, clean tx/rx packets, + * reset link state etc. + * + * @priv: A pointer to struct lbs_private structure + * @locally_generated: indicates disconnect was requested locally + * (usually by userspace) + * + * returns: n/a + */ +void lbs_mac_event_disconnected(struct lbs_private *priv, + bool locally_generated) +{ + if (priv->connect_status != LBS_CONNECTED) + return; + + lbs_deb_enter(LBS_DEB_ASSOC); + + /* + * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. + * It causes problem in the Supplicant + */ + msleep_interruptible(1000); + + if (priv->wdev->iftype == NL80211_IFTYPE_STATION) + lbs_send_disconnect_notification(priv, locally_generated); + + /* report disconnect to upper layer */ + netif_stop_queue(priv->dev); + netif_carrier_off(priv->dev); + + /* Free Tx and Rx packets */ + kfree_skb(priv->currenttxskb); + priv->currenttxskb = NULL; + priv->tx_pending_len = 0; + + priv->connect_status = LBS_DISCONNECTED; + + if (priv->psstate != PS_STATE_FULL_POWER) { + /* make firmware to exit PS mode */ + lbs_deb_cmd("disconnected, so exit PS mode\n"); + lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); + } + lbs_deb_leave(LBS_DEB_ASSOC); +} + +int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) +{ + uint16_t respcmd, curcmd; + struct cmd_header *resp; + int ret = 0; + unsigned long flags; + uint16_t result; + + lbs_deb_enter(LBS_DEB_HOST); + + mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!priv->cur_cmd) { + lbs_deb_host("CMD_RESP: cur_cmd is NULL\n"); + ret = -1; + spin_unlock_irqrestore(&priv->driver_lock, flags); + goto done; + } + + resp = (void *)data; + curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); + respcmd = le16_to_cpu(resp->command); + result = le16_to_cpu(resp->result); + + lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n", + respcmd, le16_to_cpu(resp->seqnum), len); + lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len); + + if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { + netdev_info(priv->dev, + "Received CMD_RESP with invalid sequence %d (expected %d)\n", + le16_to_cpu(resp->seqnum), + le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum)); + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + if (respcmd != CMD_RET(curcmd) && + respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) { + netdev_info(priv->dev, "Invalid CMD_RESP %x to command %x!\n", + respcmd, curcmd); + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + + if (resp->result == cpu_to_le16(0x0004)) { + /* 0x0004 means -EAGAIN. Drop the response, let it time out + and be resubmitted */ + netdev_info(priv->dev, + "Firmware returns DEFER to command %x. Will let it time out...\n", + le16_to_cpu(resp->command)); + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + + /* Now we got response from FW, cancel the command timer */ + del_timer(&priv->command_timer); + priv->cmd_timed_out = 0; + + if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) { + struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1]; + u16 action = le16_to_cpu(psmode->action); + + lbs_deb_host( + "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n", + result, action); + + if (result) { + lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n", + result); + /* + * We should not re-try enter-ps command in + * ad-hoc mode. It takes place in + * lbs_execute_next_command(). + */ + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR && + action == PS_MODE_ACTION_ENTER_PS) + priv->psmode = LBS802_11POWERMODECAM; + } else if (action == PS_MODE_ACTION_ENTER_PS) { + priv->needtowakeup = 0; + priv->psstate = PS_STATE_AWAKE; + + lbs_deb_host("CMD_RESP: ENTER_PS command response\n"); + if (priv->connect_status != LBS_CONNECTED) { + /* + * When Deauth Event received before Enter_PS command + * response, We need to wake up the firmware. + */ + lbs_deb_host( + "disconnected, invoking lbs_ps_wakeup\n"); + + spin_unlock_irqrestore(&priv->driver_lock, flags); + mutex_unlock(&priv->lock); + lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, + false); + mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->driver_lock, flags); + } + } else if (action == PS_MODE_ACTION_EXIT_PS) { + priv->needtowakeup = 0; + priv->psstate = PS_STATE_FULL_POWER; + lbs_deb_host("CMD_RESP: EXIT_PS command response\n"); + } else { + lbs_deb_host("CMD_RESP: PS action 0x%X\n", action); + } + + __lbs_complete_command(priv, priv->cur_cmd, result); + spin_unlock_irqrestore(&priv->driver_lock, flags); + + ret = 0; + goto done; + } + + /* If the command is not successful, cleanup and return failure */ + if ((result != 0 || !(respcmd & 0x8000))) { + lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n", + result, respcmd); + /* + * Handling errors here + */ + switch (respcmd) { + case CMD_RET(CMD_GET_HW_SPEC): + case CMD_RET(CMD_802_11_RESET): + lbs_deb_host("CMD_RESP: reset failed\n"); + break; + + } + __lbs_complete_command(priv, priv->cur_cmd, result); + spin_unlock_irqrestore(&priv->driver_lock, flags); + + ret = -1; + goto done; + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + if (priv->cur_cmd && priv->cur_cmd->callback) { + ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, + resp); + } + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->cur_cmd) { + /* Clean up and Put current command back to cmdfreeq */ + __lbs_complete_command(priv, priv->cur_cmd, result); + } + spin_unlock_irqrestore(&priv->driver_lock, flags); + +done: + mutex_unlock(&priv->lock); + lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); + return ret; +} + +int lbs_process_event(struct lbs_private *priv, u32 event) +{ + int ret = 0; + struct cmd_header cmd; + + lbs_deb_enter(LBS_DEB_CMD); + + switch (event) { + case MACREG_INT_CODE_LINK_SENSED: + lbs_deb_cmd("EVENT: link sensed\n"); + break; + + case MACREG_INT_CODE_DEAUTHENTICATED: + lbs_deb_cmd("EVENT: deauthenticated\n"); + lbs_mac_event_disconnected(priv, false); + break; + + case MACREG_INT_CODE_DISASSOCIATED: + lbs_deb_cmd("EVENT: disassociated\n"); + lbs_mac_event_disconnected(priv, false); + break; + + case MACREG_INT_CODE_LINK_LOST_NO_SCAN: + lbs_deb_cmd("EVENT: link lost\n"); + lbs_mac_event_disconnected(priv, true); + break; + + case MACREG_INT_CODE_PS_SLEEP: + lbs_deb_cmd("EVENT: ps sleep\n"); + + /* handle unexpected PS SLEEP event */ + if (priv->psstate == PS_STATE_FULL_POWER) { + lbs_deb_cmd( + "EVENT: in FULL POWER mode, ignoring PS_SLEEP\n"); + break; + } + priv->psstate = PS_STATE_PRE_SLEEP; + + lbs_ps_confirm_sleep(priv); + + break; + + case MACREG_INT_CODE_HOST_AWAKE: + lbs_deb_cmd("EVENT: host awake\n"); + if (priv->reset_deep_sleep_wakeup) + priv->reset_deep_sleep_wakeup(priv); + priv->is_deep_sleep = 0; + lbs_cmd_async(priv, CMD_802_11_WAKEUP_CONFIRM, &cmd, + sizeof(cmd)); + priv->is_host_sleep_activated = 0; + wake_up_interruptible(&priv->host_sleep_q); + break; + + case MACREG_INT_CODE_DEEP_SLEEP_AWAKE: + if (priv->reset_deep_sleep_wakeup) + priv->reset_deep_sleep_wakeup(priv); + lbs_deb_cmd("EVENT: ds awake\n"); + priv->is_deep_sleep = 0; + priv->wakeup_dev_required = 0; + wake_up_interruptible(&priv->ds_awake_q); + break; + + case MACREG_INT_CODE_PS_AWAKE: + lbs_deb_cmd("EVENT: ps awake\n"); + /* handle unexpected PS AWAKE event */ + if (priv->psstate == PS_STATE_FULL_POWER) { + lbs_deb_cmd( + "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); + break; + } + + priv->psstate = PS_STATE_AWAKE; + + if (priv->needtowakeup) { + /* + * wait for the command processing to finish + * before resuming sending + * priv->needtowakeup will be set to FALSE + * in lbs_ps_wakeup() + */ + lbs_deb_cmd("waking up ...\n"); + lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); + } + break; + + case MACREG_INT_CODE_MIC_ERR_UNICAST: + lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n"); + lbs_send_mic_failureevent(priv, event); + break; + + case MACREG_INT_CODE_MIC_ERR_MULTICAST: + lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n"); + lbs_send_mic_failureevent(priv, event); + break; + + case MACREG_INT_CODE_MIB_CHANGED: + lbs_deb_cmd("EVENT: MIB CHANGED\n"); + break; + case MACREG_INT_CODE_INIT_DONE: + lbs_deb_cmd("EVENT: INIT DONE\n"); + break; + case MACREG_INT_CODE_ADHOC_BCN_LOST: + lbs_deb_cmd("EVENT: ADHOC beacon lost\n"); + break; + case MACREG_INT_CODE_RSSI_LOW: + netdev_alert(priv->dev, "EVENT: rssi low\n"); + break; + case MACREG_INT_CODE_SNR_LOW: + netdev_alert(priv->dev, "EVENT: snr low\n"); + break; + case MACREG_INT_CODE_MAX_FAIL: + netdev_alert(priv->dev, "EVENT: max fail\n"); + break; + case MACREG_INT_CODE_RSSI_HIGH: + netdev_alert(priv->dev, "EVENT: rssi high\n"); + break; + case MACREG_INT_CODE_SNR_HIGH: + netdev_alert(priv->dev, "EVENT: snr high\n"); + break; + + case MACREG_INT_CODE_MESH_AUTO_STARTED: + /* Ignore spurious autostart events */ + netdev_info(priv->dev, "EVENT: MESH_AUTO_STARTED (ignoring)\n"); + break; + + default: + netdev_alert(priv->dev, "EVENT: unknown event id %d\n", event); + break; + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} diff --git a/drivers/net/wireless/marvell/libertas/debugfs.c b/drivers/net/wireless/marvell/libertas/debugfs.c new file mode 100644 index 000000000000..26cbf1dcc662 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/debugfs.c @@ -0,0 +1,989 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "decl.h" +#include "cmd.h" +#include "debugfs.h" + +static struct dentry *lbs_dir; +static char *szStates[] = { + "Connected", + "Disconnected" +}; + +#ifdef PROC_DEBUG +static void lbs_debug_init(struct lbs_private *priv); +#endif + +static ssize_t write_file_dummy(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static const size_t len = PAGE_SIZE; + +static ssize_t lbs_dev_info(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + ssize_t res; + if (!buf) + return -ENOMEM; + + pos += snprintf(buf+pos, len-pos, "state = %s\n", + szStates[priv->connect_status]); + pos += snprintf(buf+pos, len-pos, "region_code = %02x\n", + (u32) priv->regioncode); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return res; +} + +static ssize_t lbs_sleepparams_write(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t buf_size, ret; + struct sleep_params sp; + int p1, p2, p3, p4, p5, p6; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, user_buf, buf_size)) { + ret = -EFAULT; + goto out_unlock; + } + ret = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6); + if (ret != 6) { + ret = -EINVAL; + goto out_unlock; + } + sp.sp_error = p1; + sp.sp_offset = p2; + sp.sp_stabletime = p3; + sp.sp_calcontrol = p4; + sp.sp_extsleepclk = p5; + sp.sp_reserved = p6; + + ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_SET, &sp); + if (!ret) + ret = count; + else if (ret > 0) + ret = -EINVAL; + +out_unlock: + free_page(addr); + return ret; +} + +static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t ret; + size_t pos = 0; + struct sleep_params sp; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp); + if (ret) + goto out_unlock; + + pos += snprintf(buf, len, "%d %d %d %d %d %d\n", sp.sp_error, + sp.sp_offset, sp.sp_stabletime, + sp.sp_calcontrol, sp.sp_extsleepclk, + sp.sp_reserved); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + +out_unlock: + free_page(addr); + return ret; +} + +static ssize_t lbs_host_sleep_write(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t buf_size, ret; + int host_sleep; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, user_buf, buf_size)) { + ret = -EFAULT; + goto out_unlock; + } + ret = sscanf(buf, "%d", &host_sleep); + if (ret != 1) { + ret = -EINVAL; + goto out_unlock; + } + + if (host_sleep == 0) + ret = lbs_set_host_sleep(priv, 0); + else if (host_sleep == 1) { + if (priv->wol_criteria == EHS_REMOVE_WAKEUP) { + netdev_info(priv->dev, + "wake parameters not configured\n"); + ret = -EINVAL; + goto out_unlock; + } + ret = lbs_set_host_sleep(priv, 1); + } else { + netdev_err(priv->dev, "invalid option\n"); + ret = -EINVAL; + } + + if (!ret) + ret = count; + +out_unlock: + free_page(addr); + return ret; +} + +static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t ret; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return ret; +} + +/* + * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might + * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the + * firmware. Here's an example: + * 04 01 02 00 00 00 05 01 02 00 00 00 06 01 02 00 + * 00 00 07 01 02 00 3c 00 00 00 00 00 00 00 03 03 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * + * The 04 01 is the TLV type (here TLV_TYPE_RSSI_LOW), 02 00 is the length, + * 00 00 are the data bytes of this TLV. For this TLV, their meaning is + * defined in mrvlietypes_thresholds + * + * This function searches in this TLV data chunk for a given TLV type + * and returns a pointer to the first data byte of the TLV, or to NULL + * if the TLV hasn't been found. + */ +static void *lbs_tlv_find(uint16_t tlv_type, const uint8_t *tlv, uint16_t size) +{ + struct mrvl_ie_header *tlv_h; + uint16_t length; + ssize_t pos = 0; + + while (pos < size) { + tlv_h = (struct mrvl_ie_header *) tlv; + if (!tlv_h->len) + return NULL; + if (tlv_h->type == cpu_to_le16(tlv_type)) + return tlv_h; + length = le16_to_cpu(tlv_h->len) + sizeof(*tlv_h); + pos += length; + tlv += length; + } + return NULL; +} + + +static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask, + struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct cmd_ds_802_11_subscribe_event *subscribed; + struct mrvl_ie_thresholds *got; + struct lbs_private *priv = file->private_data; + ssize_t ret = 0; + size_t pos = 0; + char *buf; + u8 value; + u8 freq; + int events = 0; + + buf = (char *)get_zeroed_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + subscribed = kzalloc(sizeof(*subscribed), GFP_KERNEL); + if (!subscribed) { + ret = -ENOMEM; + goto out_page; + } + + subscribed->hdr.size = cpu_to_le16(sizeof(*subscribed)); + subscribed->action = cpu_to_le16(CMD_ACT_GET); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, subscribed); + if (ret) + goto out_cmd; + + got = lbs_tlv_find(tlv_type, subscribed->tlv, sizeof(subscribed->tlv)); + if (got) { + value = got->value; + freq = got->freq; + events = le16_to_cpu(subscribed->events); + + pos += snprintf(buf, len, "%d %d %d\n", value, freq, + !!(events & event_mask)); + } + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + out_cmd: + kfree(subscribed); + + out_page: + free_page((unsigned long)buf); + return ret; +} + + +static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask, + struct file *file, + const char __user *userbuf, size_t count, + loff_t *ppos) +{ + struct cmd_ds_802_11_subscribe_event *events; + struct mrvl_ie_thresholds *tlv; + struct lbs_private *priv = file->private_data; + ssize_t buf_size; + int value, freq, new_mask; + uint16_t curr_mask; + char *buf; + int ret; + + buf = (char *)get_zeroed_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + ret = -EFAULT; + goto out_page; + } + ret = sscanf(buf, "%d %d %d", &value, &freq, &new_mask); + if (ret != 3) { + ret = -EINVAL; + goto out_page; + } + events = kzalloc(sizeof(*events), GFP_KERNEL); + if (!events) { + ret = -ENOMEM; + goto out_page; + } + + events->hdr.size = cpu_to_le16(sizeof(*events)); + events->action = cpu_to_le16(CMD_ACT_GET); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events); + if (ret) + goto out_events; + + curr_mask = le16_to_cpu(events->events); + + if (new_mask) + new_mask = curr_mask | event_mask; + else + new_mask = curr_mask & ~event_mask; + + /* Now everything is set and we can send stuff down to the firmware */ + + tlv = (void *)events->tlv; + + events->action = cpu_to_le16(CMD_ACT_SET); + events->events = cpu_to_le16(new_mask); + tlv->header.type = cpu_to_le16(tlv_type); + tlv->header.len = cpu_to_le16(sizeof(*tlv) - sizeof(tlv->header)); + tlv->value = value; + if (tlv_type != TLV_TYPE_BCNMISS) + tlv->freq = freq; + + /* The command header, the action, the event mask, and one TLV */ + events->hdr.size = cpu_to_le16(sizeof(events->hdr) + 4 + sizeof(*tlv)); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events); + + if (!ret) + ret = count; + out_events: + kfree(events); + out_page: + free_page((unsigned long)buf); + return ret; +} + + +static ssize_t lbs_lowrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_lowrssi_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_lowsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_lowsnr_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_failcount_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_failcount_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_highrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_highrssi_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_highsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_highsnr_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, + file, userbuf, count, ppos); +} + +static ssize_t lbs_bcnmiss_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_bcnmiss_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + u32 val = 0; + + if (!buf) + return -ENOMEM; + + ret = lbs_get_reg(priv, CMD_MAC_REG_ACCESS, priv->mac_offset, &val); + mdelay(10); + if (!ret) { + pos = snprintf(buf, len, "MAC[0x%x] = 0x%08x\n", + priv->mac_offset, val); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + } + free_page(addr); + return ret; +} + +static ssize_t lbs_rdmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->mac_offset = simple_strtoul(buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t lbs_wrmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + res = lbs_set_reg(priv, CMD_MAC_REG_ACCESS, offset, value); + mdelay(10); + + if (!res) + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + u32 val; + + if (!buf) + return -ENOMEM; + + ret = lbs_get_reg(priv, CMD_BBP_REG_ACCESS, priv->bbp_offset, &val); + mdelay(10); + if (!ret) { + pos = snprintf(buf, len, "BBP[0x%x] = 0x%08x\n", + priv->bbp_offset, val); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + } + free_page(addr); + + return ret; +} + +static ssize_t lbs_rdbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->bbp_offset = simple_strtoul(buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t lbs_wrbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + res = lbs_set_reg(priv, CMD_BBP_REG_ACCESS, offset, value); + mdelay(10); + + if (!res) + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + u32 val; + + if (!buf) + return -ENOMEM; + + ret = lbs_get_reg(priv, CMD_RF_REG_ACCESS, priv->rf_offset, &val); + mdelay(10); + if (!ret) { + pos = snprintf(buf, len, "RF[0x%x] = 0x%08x\n", + priv->rf_offset, val); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + } + free_page(addr); + + return ret; +} + +static ssize_t lbs_rdrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->rf_offset = simple_strtoul(buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t lbs_wrrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + res = lbs_set_reg(priv, CMD_RF_REG_ACCESS, offset, value); + mdelay(10); + + if (!res) + res = count; +out_unlock: + free_page(addr); + return res; +} + +#define FOPS(fread, fwrite) { \ + .owner = THIS_MODULE, \ + .open = simple_open, \ + .read = (fread), \ + .write = (fwrite), \ + .llseek = generic_file_llseek, \ +} + +struct lbs_debugfs_files { + const char *name; + umode_t perm; + struct file_operations fops; +}; + +static const struct lbs_debugfs_files debugfs_files[] = { + { "info", 0444, FOPS(lbs_dev_info, write_file_dummy), }, + { "sleepparams", 0644, FOPS(lbs_sleepparams_read, + lbs_sleepparams_write), }, + { "hostsleep", 0644, FOPS(lbs_host_sleep_read, + lbs_host_sleep_write), }, +}; + +static const struct lbs_debugfs_files debugfs_events_files[] = { + {"low_rssi", 0644, FOPS(lbs_lowrssi_read, + lbs_lowrssi_write), }, + {"low_snr", 0644, FOPS(lbs_lowsnr_read, + lbs_lowsnr_write), }, + {"failure_count", 0644, FOPS(lbs_failcount_read, + lbs_failcount_write), }, + {"beacon_missed", 0644, FOPS(lbs_bcnmiss_read, + lbs_bcnmiss_write), }, + {"high_rssi", 0644, FOPS(lbs_highrssi_read, + lbs_highrssi_write), }, + {"high_snr", 0644, FOPS(lbs_highsnr_read, + lbs_highsnr_write), }, +}; + +static const struct lbs_debugfs_files debugfs_regs_files[] = { + {"rdmac", 0644, FOPS(lbs_rdmac_read, lbs_rdmac_write), }, + {"wrmac", 0600, FOPS(NULL, lbs_wrmac_write), }, + {"rdbbp", 0644, FOPS(lbs_rdbbp_read, lbs_rdbbp_write), }, + {"wrbbp", 0600, FOPS(NULL, lbs_wrbbp_write), }, + {"rdrf", 0644, FOPS(lbs_rdrf_read, lbs_rdrf_write), }, + {"wrrf", 0600, FOPS(NULL, lbs_wrrf_write), }, +}; + +void lbs_debugfs_init(void) +{ + if (!lbs_dir) + lbs_dir = debugfs_create_dir("lbs_wireless", NULL); +} + +void lbs_debugfs_remove(void) +{ + debugfs_remove(lbs_dir); +} + +void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev) +{ + int i; + const struct lbs_debugfs_files *files; + if (!lbs_dir) + goto exit; + + priv->debugfs_dir = debugfs_create_dir(dev->name, lbs_dir); + if (!priv->debugfs_dir) + goto exit; + + for (i=0; idebugfs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->debugfs_dir, + priv, + &files->fops); + } + + priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir); + if (!priv->events_dir) + goto exit; + + for (i=0; idebugfs_events_files[i] = debugfs_create_file(files->name, + files->perm, + priv->events_dir, + priv, + &files->fops); + } + + priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir); + if (!priv->regs_dir) + goto exit; + + for (i=0; idebugfs_regs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->regs_dir, + priv, + &files->fops); + } + +#ifdef PROC_DEBUG + lbs_debug_init(priv); +#endif +exit: + return; +} + +void lbs_debugfs_remove_one(struct lbs_private *priv) +{ + int i; + + for(i=0; idebugfs_regs_files[i]); + + debugfs_remove(priv->regs_dir); + + for(i=0; idebugfs_events_files[i]); + + debugfs_remove(priv->events_dir); +#ifdef PROC_DEBUG + debugfs_remove(priv->debugfs_debug); +#endif + for(i=0; idebugfs_files[i]); + debugfs_remove(priv->debugfs_dir); +} + + + +/* debug entry */ + +#ifdef PROC_DEBUG + +#define item_size(n) (FIELD_SIZEOF(struct lbs_private, n)) +#define item_addr(n) (offsetof(struct lbs_private, n)) + + +struct debug_data { + char name[32]; + u32 size; + size_t addr; +}; + +/* To debug any member of struct lbs_private, simply add one line here. + */ +static struct debug_data items[] = { + {"psmode", item_size(psmode), item_addr(psmode)}, + {"psstate", item_size(psstate), item_addr(psstate)}, +}; + +static int num_of_items = ARRAY_SIZE(items); + +/** + * lbs_debugfs_read - proc read function + * + * @file: file to read + * @userbuf: pointer to buffer + * @count: number of bytes to read + * @ppos: read data starting position + * + * returns: amount of data read or negative error code + */ +static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + int val = 0; + size_t pos = 0; + ssize_t res; + char *p; + int i; + struct debug_data *d; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + p = buf; + + d = file->private_data; + + for (i = 0; i < num_of_items; i++) { + if (d[i].size == 1) + val = *((u8 *) d[i].addr); + else if (d[i].size == 2) + val = *((u16 *) d[i].addr); + else if (d[i].size == 4) + val = *((u32 *) d[i].addr); + else if (d[i].size == 8) + val = *((u64 *) d[i].addr); + + pos += sprintf(p + pos, "%s=%d\n", d[i].name, val); + } + + res = simple_read_from_buffer(userbuf, count, ppos, p, pos); + + free_page(addr); + return res; +} + +/** + * lbs_debugfs_write - proc write function + * + * @f: file pointer + * @buf: pointer to data buffer + * @cnt: data number to write + * @ppos: file position + * + * returns: amount of data written + */ +static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, + size_t cnt, loff_t *ppos) +{ + int r, i; + char *pdata; + char *p; + char *p0; + char *p1; + char *p2; + struct debug_data *d = f->private_data; + + if (cnt == 0) + return 0; + + pdata = kmalloc(cnt + 1, GFP_KERNEL); + if (pdata == NULL) + return 0; + + if (copy_from_user(pdata, buf, cnt)) { + lbs_deb_debugfs("Copy from user failed\n"); + kfree(pdata); + return 0; + } + pdata[cnt] = '\0'; + + p0 = pdata; + for (i = 0; i < num_of_items; i++) { + do { + p = strstr(p0, d[i].name); + if (p == NULL) + break; + p1 = strchr(p, '\n'); + if (p1 == NULL) + break; + p0 = p1++; + p2 = strchr(p, '='); + if (!p2) + break; + p2++; + r = simple_strtoul(p2, NULL, 0); + if (d[i].size == 1) + *((u8 *) d[i].addr) = (u8) r; + else if (d[i].size == 2) + *((u16 *) d[i].addr) = (u16) r; + else if (d[i].size == 4) + *((u32 *) d[i].addr) = (u32) r; + else if (d[i].size == 8) + *((u64 *) d[i].addr) = (u64) r; + break; + } while (1); + } + kfree(pdata); + + return (ssize_t)cnt; +} + +static const struct file_operations lbs_debug_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = lbs_debugfs_write, + .read = lbs_debugfs_read, + .llseek = default_llseek, +}; + +/** + * lbs_debug_init - create debug proc file + * + * @priv: pointer to &struct lbs_private + * + * returns: N/A + */ +static void lbs_debug_init(struct lbs_private *priv) +{ + int i; + + if (!priv->debugfs_dir) + return; + + for (i = 0; i < num_of_items; i++) + items[i].addr += (size_t) priv; + + priv->debugfs_debug = debugfs_create_file("debug", 0644, + priv->debugfs_dir, &items[0], + &lbs_debug_fops); +} +#endif diff --git a/drivers/net/wireless/marvell/libertas/debugfs.h b/drivers/net/wireless/marvell/libertas/debugfs.h new file mode 100644 index 000000000000..f2b9c7ffe0fd --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/debugfs.h @@ -0,0 +1,10 @@ +#ifndef _LBS_DEBUGFS_H_ +#define _LBS_DEBUGFS_H_ + +void lbs_debugfs_init(void); +void lbs_debugfs_remove(void); + +void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev); +void lbs_debugfs_remove_one(struct lbs_private *priv); + +#endif diff --git a/drivers/net/wireless/marvell/libertas/decl.h b/drivers/net/wireless/marvell/libertas/decl.h new file mode 100644 index 000000000000..84a3aa7ac570 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/decl.h @@ -0,0 +1,82 @@ + +/* + * This file contains declaration referring to + * functions defined in other source files + */ + +#ifndef _LBS_DECL_H_ +#define _LBS_DECL_H_ + +#include +#include +#include + +/* Should be terminated by a NULL entry */ +struct lbs_fw_table { + int model; + const char *helper; + const char *fwname; +}; + +struct lbs_private; +typedef void (*lbs_fw_cb)(struct lbs_private *priv, int ret, + const struct firmware *helper, const struct firmware *mainfw); + +struct lbs_private; +struct sk_buff; +struct net_device; +struct cmd_ds_command; + + +/* ethtool.c */ +extern const struct ethtool_ops lbs_ethtool_ops; + + +/* tx.c */ +void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count); +netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev); + +/* rx.c */ +int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *); + + +/* main.c */ +struct lbs_private *lbs_add_card(void *card, struct device *dmdev); +void lbs_remove_card(struct lbs_private *priv); +int lbs_start_card(struct lbs_private *priv); +void lbs_stop_card(struct lbs_private *priv); +void lbs_host_to_card_done(struct lbs_private *priv); + +int lbs_start_iface(struct lbs_private *priv); +int lbs_stop_iface(struct lbs_private *priv); +int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type); + +int lbs_rtap_supported(struct lbs_private *priv); + +int lbs_set_mac_address(struct net_device *dev, void *addr); +void lbs_set_multicast_list(struct net_device *dev); +void lbs_update_mcast(struct lbs_private *priv); + +int lbs_suspend(struct lbs_private *priv); +int lbs_resume(struct lbs_private *priv); + +void lbs_queue_event(struct lbs_private *priv, u32 event); +void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx); + +int lbs_enter_auto_deep_sleep(struct lbs_private *priv); +int lbs_exit_auto_deep_sleep(struct lbs_private *priv); + +u32 lbs_fw_index_to_data_rate(u8 index); +u8 lbs_data_rate_to_fw_index(u32 rate); + +int lbs_get_firmware(struct device *dev, u32 card_model, + const struct lbs_fw_table *fw_table, + const struct firmware **helper, + const struct firmware **mainfw); +int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, + u32 card_model, const struct lbs_fw_table *fw_table, + lbs_fw_cb callback); +void lbs_wait_for_firmware_load(struct lbs_private *priv); + +#endif diff --git a/drivers/net/wireless/marvell/libertas/defs.h b/drivers/net/wireless/marvell/libertas/defs.h new file mode 100644 index 000000000000..407784aca627 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/defs.h @@ -0,0 +1,394 @@ +/* + * This header file contains global constant/enum definitions, + * global variable declaration. + */ +#ifndef _LBS_DEFS_H_ +#define _LBS_DEFS_H_ + +#include + +#ifdef CONFIG_LIBERTAS_DEBUG +#define DEBUG +#define PROC_DEBUG +#endif + +#ifndef DRV_NAME +#define DRV_NAME "libertas" +#endif + + +#define LBS_DEB_ENTER 0x00000001 +#define LBS_DEB_LEAVE 0x00000002 +#define LBS_DEB_MAIN 0x00000004 +#define LBS_DEB_NET 0x00000008 +#define LBS_DEB_MESH 0x00000010 +#define LBS_DEB_WEXT 0x00000020 +#define LBS_DEB_IOCTL 0x00000040 +#define LBS_DEB_SCAN 0x00000080 +#define LBS_DEB_ASSOC 0x00000100 +#define LBS_DEB_JOIN 0x00000200 +#define LBS_DEB_11D 0x00000400 +#define LBS_DEB_DEBUGFS 0x00000800 +#define LBS_DEB_ETHTOOL 0x00001000 +#define LBS_DEB_HOST 0x00002000 +#define LBS_DEB_CMD 0x00004000 +#define LBS_DEB_RX 0x00008000 +#define LBS_DEB_TX 0x00010000 +#define LBS_DEB_USB 0x00020000 +#define LBS_DEB_CS 0x00040000 +#define LBS_DEB_FW 0x00080000 +#define LBS_DEB_THREAD 0x00100000 +#define LBS_DEB_HEX 0x00200000 +#define LBS_DEB_SDIO 0x00400000 +#define LBS_DEB_SYSFS 0x00800000 +#define LBS_DEB_SPI 0x01000000 +#define LBS_DEB_CFG80211 0x02000000 + +extern unsigned int lbs_debug; + +#ifdef DEBUG +#define LBS_DEB_LL(grp, grpnam, fmt, args...) \ +do { if ((lbs_debug & (grp)) == (grp)) \ + printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \ + in_interrupt() ? " (INT)" : "", ## args); } while (0) +#else +#define LBS_DEB_LL(grp, grpnam, fmt, args...) do {} while (0) +#endif + +#define lbs_deb_enter(grp) \ + LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s()\n", __func__); +#define lbs_deb_enter_args(grp, fmt, args...) \ + LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s(" fmt ")\n", __func__, ## args); +#define lbs_deb_leave(grp) \ + LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s()\n", __func__); +#define lbs_deb_leave_args(grp, fmt, args...) \ + LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s(), " fmt "\n", \ + __func__, ##args); +#define lbs_deb_main(fmt, args...) LBS_DEB_LL(LBS_DEB_MAIN, " main", fmt, ##args) +#define lbs_deb_net(fmt, args...) LBS_DEB_LL(LBS_DEB_NET, " net", fmt, ##args) +#define lbs_deb_mesh(fmt, args...) LBS_DEB_LL(LBS_DEB_MESH, " mesh", fmt, ##args) +#define lbs_deb_wext(fmt, args...) LBS_DEB_LL(LBS_DEB_WEXT, " wext", fmt, ##args) +#define lbs_deb_ioctl(fmt, args...) LBS_DEB_LL(LBS_DEB_IOCTL, " ioctl", fmt, ##args) +#define lbs_deb_scan(fmt, args...) LBS_DEB_LL(LBS_DEB_SCAN, " scan", fmt, ##args) +#define lbs_deb_assoc(fmt, args...) LBS_DEB_LL(LBS_DEB_ASSOC, " assoc", fmt, ##args) +#define lbs_deb_join(fmt, args...) LBS_DEB_LL(LBS_DEB_JOIN, " join", fmt, ##args) +#define lbs_deb_11d(fmt, args...) LBS_DEB_LL(LBS_DEB_11D, " 11d", fmt, ##args) +#define lbs_deb_debugfs(fmt, args...) LBS_DEB_LL(LBS_DEB_DEBUGFS, " debugfs", fmt, ##args) +#define lbs_deb_ethtool(fmt, args...) LBS_DEB_LL(LBS_DEB_ETHTOOL, " ethtool", fmt, ##args) +#define lbs_deb_host(fmt, args...) LBS_DEB_LL(LBS_DEB_HOST, " host", fmt, ##args) +#define lbs_deb_cmd(fmt, args...) LBS_DEB_LL(LBS_DEB_CMD, " cmd", fmt, ##args) +#define lbs_deb_rx(fmt, args...) LBS_DEB_LL(LBS_DEB_RX, " rx", fmt, ##args) +#define lbs_deb_tx(fmt, args...) LBS_DEB_LL(LBS_DEB_TX, " tx", fmt, ##args) +#define lbs_deb_fw(fmt, args...) LBS_DEB_LL(LBS_DEB_FW, " fw", fmt, ##args) +#define lbs_deb_usb(fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usb", fmt, ##args) +#define lbs_deb_usbd(dev, fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args) +#define lbs_deb_cs(fmt, args...) LBS_DEB_LL(LBS_DEB_CS, " cs", fmt, ##args) +#define lbs_deb_thread(fmt, args...) LBS_DEB_LL(LBS_DEB_THREAD, " thread", fmt, ##args) +#define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " sdio", fmt, ##args) +#define lbs_deb_sysfs(fmt, args...) LBS_DEB_LL(LBS_DEB_SYSFS, " sysfs", fmt, ##args) +#define lbs_deb_spi(fmt, args...) LBS_DEB_LL(LBS_DEB_SPI, " spi", fmt, ##args) +#define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args) + +#ifdef DEBUG +static inline void lbs_deb_hex(unsigned int grp, const char *prompt, + const u8 *buf, int len) +{ + int i = 0; + + if (len && + (lbs_debug & LBS_DEB_HEX) && + (lbs_debug & grp)) + { + for (i = 1; i <= len; i++) { + if ((i & 0xf) == 1) { + if (i != 1) + printk("\n"); + printk(DRV_NAME " %s: ", prompt); + } + printk("%02x ", (u8) * buf); + buf++; + } + printk("\n"); + } +} +#else +#define lbs_deb_hex(grp,prompt,buf,len) do {} while (0) +#endif + + + +/* Buffer Constants */ + +/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical + * addresses of TxPD buffers. Station has only 8 TxPD available, Whereas + * driver has more local TxPDs. Each TxPD on the host memory is associated + * with a Tx control node. The driver maintains 8 RxPD descriptors for + * station firmware to store Rx packet information. + * + * Current version of MAC has a 32x6 multicast address buffer. + * + * 802.11b can have up to 14 channels, the driver keeps the + * BSSID(MAC address) of each APs or Ad hoc stations it has sensed. + */ + +#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 +#define LBS_NUM_CMD_BUFFERS 10 +#define LBS_CMD_BUFFER_SIZE (2 * 1024) +#define MRVDRV_MAX_CHANNEL_SIZE 14 +#define MRVDRV_ASSOCIATION_TIME_OUT 255 +#define MRVDRV_SNAP_HEADER_LEN 8 + +#define LBS_UPLD_SIZE 2312 +#define DEV_NAME_LEN 32 + +/* Wake criteria for HOST_SLEEP_CFG command */ +#define EHS_WAKE_ON_BROADCAST_DATA 0x0001 +#define EHS_WAKE_ON_UNICAST_DATA 0x0002 +#define EHS_WAKE_ON_MAC_EVENT 0x0004 +#define EHS_WAKE_ON_MULTICAST_DATA 0x0008 +#define EHS_REMOVE_WAKEUP 0xFFFFFFFF +/* Wake rules for Host_Sleep_CFG command */ +#define WOL_RULE_NET_TYPE_INFRA_OR_IBSS 0x00 +#define WOL_RULE_NET_TYPE_MESH 0x10 +#define WOL_RULE_ADDR_TYPE_BCAST 0x01 +#define WOL_RULE_ADDR_TYPE_MCAST 0x08 +#define WOL_RULE_ADDR_TYPE_UCAST 0x02 +#define WOL_RULE_OP_AND 0x01 +#define WOL_RULE_OP_OR 0x02 +#define WOL_RULE_OP_INVALID 0xFF +#define WOL_RESULT_VALID_CMD 0 +#define WOL_RESULT_NOSPC_ERR 1 +#define WOL_RESULT_EEXIST_ERR 2 + +/* Misc constants */ +/* This section defines 802.11 specific contants */ + +#define MRVDRV_MAX_BSS_DESCRIPTS 16 +#define MRVDRV_MAX_REGION_CODE 6 + +#define MRVDRV_DEFAULT_LISTEN_INTERVAL 10 + +#define MRVDRV_CHANNELS_PER_SCAN 4 +#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 + +#define MRVDRV_MIN_BEACON_INTERVAL 20 +#define MRVDRV_MAX_BEACON_INTERVAL 1000 +#define MRVDRV_BEACON_INTERVAL 100 + +#define MARVELL_MESH_IE_LENGTH 9 + +/* + * Values used to populate the struct mrvl_mesh_ie. The only time you need this + * is when enabling the mesh using CMD_MESH_CONFIG. + */ +#define MARVELL_MESH_IE_TYPE 4 +#define MARVELL_MESH_IE_SUBTYPE 0 +#define MARVELL_MESH_IE_VERSION 0 +#define MARVELL_MESH_PROTO_ID_HWMP 0 +#define MARVELL_MESH_METRIC_ID 0 +#define MARVELL_MESH_CAPABILITY 0 + +/* INT status Bit Definition */ +#define MRVDRV_TX_DNLD_RDY 0x0001 +#define MRVDRV_RX_UPLD_RDY 0x0002 +#define MRVDRV_CMD_DNLD_RDY 0x0004 +#define MRVDRV_CMD_UPLD_RDY 0x0008 +#define MRVDRV_CARDEVENT 0x0010 + +/* Automatic TX control default levels */ +#define POW_ADAPT_DEFAULT_P0 13 +#define POW_ADAPT_DEFAULT_P1 15 +#define POW_ADAPT_DEFAULT_P2 18 +#define TPC_DEFAULT_P0 5 +#define TPC_DEFAULT_P1 10 +#define TPC_DEFAULT_P2 13 + +/* TxPD status */ + +/* + * Station firmware use TxPD status field to report final Tx transmit + * result, Bit masks are used to present combined situations. + */ + +#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 +#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 + +/* Tx mesh flag */ +/* + * Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define TxPD_CONTROL_WDS_FRAME (1<<17) +#define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME + +/* Mesh interface ID */ +#define MESH_IFACE_ID 0x0001 +/* Mesh id should be in bits 14-13-12 */ +#define MESH_IFACE_BIT_OFFSET 0x000c +/* Mesh enable bit in FW capability */ +#define MESH_CAPINFO_ENABLE_MASK (1<<16) + +/* FW definition from Marvell v4 */ +#define MRVL_FW_V4 (0x04) +/* FW definition from Marvell v5 */ +#define MRVL_FW_V5 (0x05) +/* FW definition from Marvell v10 */ +#define MRVL_FW_V10 (0x0a) +/* FW major revision definition */ +#define MRVL_FW_MAJOR_REV(x) ((x)>>24) + +/* RxPD status */ + +#define MRVDRV_RXPD_STATUS_OK 0x0001 + +/* RxPD status - Received packet types */ +/* Rx mesh flag */ +/* + * Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define RxPD_CONTROL_WDS_FRAME (0x40) +#define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME + +/* RSSI-related defines */ +/* + * RSSI constants are used to implement 802.11 RSSI threshold + * indication. if the Rx packet signal got too weak for 5 consecutive + * times, miniport driver (driver) will report this event to wrapper + */ + +#define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) + +/* RTS/FRAG related defines */ +#define MRVDRV_RTS_MIN_VALUE 0 +#define MRVDRV_RTS_MAX_VALUE 2347 +#define MRVDRV_FRAG_MIN_VALUE 256 +#define MRVDRV_FRAG_MAX_VALUE 2346 + +/* This is for firmware specific length */ +#define EXTRA_LEN 36 + +#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) + +#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct rxpd) \ + + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) + +#define CMD_F_HOSTCMD (1 << 0) +#define FW_CAPINFO_WPA (1 << 0) +#define FW_CAPINFO_PS (1 << 1) +#define FW_CAPINFO_FIRMWARE_UPGRADE (1 << 13) +#define FW_CAPINFO_BOOT2_UPGRADE (1<<14) +#define FW_CAPINFO_PERSISTENT_CONFIG (1<<15) + +#define KEY_LEN_WPA_AES 16 +#define KEY_LEN_WPA_TKIP 32 +#define KEY_LEN_WEP_104 13 +#define KEY_LEN_WEP_40 5 + +#define RF_ANTENNA_1 0x1 +#define RF_ANTENNA_2 0x2 +#define RF_ANTENNA_AUTO 0xFFFF + +#define BAND_B (0x01) +#define BAND_G (0x02) +#define ALL_802_11_BANDS (BAND_B | BAND_G) + +#define MAX_RATES 14 + +#define MAX_LEDS 8 + +/* Global Variable Declaration */ +extern const char lbs_driver_version[]; +extern u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE]; + + +/* ENUM definition */ +/* SNRNF_TYPE */ +enum SNRNF_TYPE { + TYPE_BEACON = 0, + TYPE_RXPD, + MAX_TYPE_B +}; + +/* SNRNF_DATA */ +enum SNRNF_DATA { + TYPE_NOAVG = 0, + TYPE_AVG, + MAX_TYPE_AVG +}; + +/* LBS_802_11_POWER_MODE */ +enum LBS_802_11_POWER_MODE { + LBS802_11POWERMODECAM, + LBS802_11POWERMODEMAX_PSP, + LBS802_11POWERMODEFAST_PSP, + /* not a real mode, defined as an upper bound */ + LBS802_11POWEMODEMAX +}; + +/* PS_STATE */ +enum PS_STATE { + PS_STATE_FULL_POWER, + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP +}; + +/* DNLD_STATE */ +enum DNLD_STATE { + DNLD_RES_RECEIVED, + DNLD_DATA_SENT, + DNLD_CMD_SENT, + DNLD_BOOTCMD_SENT, +}; + +/* LBS_MEDIA_STATE */ +enum LBS_MEDIA_STATE { + LBS_CONNECTED, + LBS_DISCONNECTED +}; + +/* LBS_802_11_PRIVACY_FILTER */ +enum LBS_802_11_PRIVACY_FILTER { + LBS802_11PRIVFILTERACCEPTALL, + LBS802_11PRIVFILTER8021XWEP +}; + +/* mv_ms_type */ +enum mv_ms_type { + MVMS_DAT = 0, + MVMS_CMD = 1, + MVMS_TXDONE = 2, + MVMS_EVENT +}; + +/* KEY_TYPE_ID */ +enum KEY_TYPE_ID { + KEY_TYPE_ID_WEP = 0, + KEY_TYPE_ID_TKIP, + KEY_TYPE_ID_AES +}; + +/* KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */ +enum KEY_INFO_WPA { + KEY_INFO_WPA_MCAST = 0x01, + KEY_INFO_WPA_UNICAST = 0x02, + KEY_INFO_WPA_ENABLED = 0x04 +}; + +/* Default values for fwt commands. */ +#define FWT_DEFAULT_METRIC 0 +#define FWT_DEFAULT_DIR 1 +/* Default Rate, 11Mbps */ +#define FWT_DEFAULT_RATE 3 +#define FWT_DEFAULT_SSN 0xffffffff +#define FWT_DEFAULT_DSN 0 +#define FWT_DEFAULT_HOPCOUNT 0 +#define FWT_DEFAULT_TTL 0 +#define FWT_DEFAULT_EXPIRATION 0 +#define FWT_DEFAULT_SLEEPMODE 0 +#define FWT_DEFAULT_SNR 0 + +#endif diff --git a/drivers/net/wireless/marvell/libertas/dev.h b/drivers/net/wireless/marvell/libertas/dev.h new file mode 100644 index 000000000000..6bd1608992b0 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/dev.h @@ -0,0 +1,211 @@ +/* + * This file contains definitions and data structures specific + * to Marvell 802.11 NIC. It contains the Device Information + * structure struct lbs_private.. + */ +#ifndef _LBS_DEV_H_ +#define _LBS_DEV_H_ + +#include "defs.h" +#include "decl.h" +#include "host.h" + +#include + +/* sleep_params */ +struct sleep_params { + uint16_t sp_error; + uint16_t sp_offset; + uint16_t sp_stabletime; + uint8_t sp_calcontrol; + uint8_t sp_extsleepclk; + uint16_t sp_reserved; +}; + +/* Mesh statistics */ +struct lbs_mesh_stats { + u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ + u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ + u32 fwd_drop_ttl; /* Fwd: TTL zero */ + u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ + u32 fwd_drop_noroute; /* Fwd: No route to Destination */ + u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ + u32 drop_blind; /* Rx: Dropped by blinding table */ + u32 tx_failed_cnt; /* Tx: Failed transmissions */ +}; + +/* Private structure for the MV device */ +struct lbs_private { + + /* Basic networking */ + struct net_device *dev; + u32 connect_status; + struct work_struct mcast_work; + u32 nr_of_multicastmacaddr; + u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; + + /* CFG80211 */ + struct wireless_dev *wdev; + bool wiphy_registered; + struct cfg80211_scan_request *scan_req; + u8 assoc_bss[ETH_ALEN]; + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + u8 disassoc_reason; + + /* Mesh */ + struct net_device *mesh_dev; /* Virtual device */ +#ifdef CONFIG_LIBERTAS_MESH + struct lbs_mesh_stats mstats; + uint16_t mesh_tlv; + u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1]; + u8 mesh_ssid_len; + u8 mesh_channel; +#endif + + /* Debugfs */ + struct dentry *debugfs_dir; + struct dentry *debugfs_debug; + struct dentry *debugfs_files[6]; + struct dentry *events_dir; + struct dentry *debugfs_events_files[6]; + struct dentry *regs_dir; + struct dentry *debugfs_regs_files[6]; + + /* Hardware debugging */ + u32 mac_offset; + u32 bbp_offset; + u32 rf_offset; + + /* Power management */ + u16 psmode; + u32 psstate; + u8 needtowakeup; + + /* Deep sleep */ + int is_deep_sleep; + int deep_sleep_required; + int is_auto_deep_sleep_enabled; + int wakeup_dev_required; + int is_activity_detected; + int auto_deep_sleep_timeout; /* in ms */ + wait_queue_head_t ds_awake_q; + struct timer_list auto_deepsleep_timer; + + /* Host sleep*/ + int is_host_sleep_configured; + int is_host_sleep_activated; + wait_queue_head_t host_sleep_q; + + /* Hardware access */ + void *card; + bool iface_running; + u8 fw_ready; + u8 surpriseremoved; + u8 setup_fw_on_resume; + int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb); + void (*reset_card) (struct lbs_private *priv); + int (*power_save) (struct lbs_private *priv); + int (*power_restore) (struct lbs_private *priv); + int (*enter_deep_sleep) (struct lbs_private *priv); + int (*exit_deep_sleep) (struct lbs_private *priv); + int (*reset_deep_sleep_wakeup) (struct lbs_private *priv); + + /* Adapter info (from EEPROM) */ + u32 fwrelease; + u32 fwcapinfo; + u16 regioncode; + u8 current_addr[ETH_ALEN]; + u8 copied_hwaddr; + + /* Command download */ + u8 dnld_sent; + /* bit0 1/0=data_sent/data_tx_done, + bit1 1/0=cmd_sent/cmd_tx_done, + all other bits reserved 0 */ + u16 seqnum; + struct cmd_ctrl_node *cmd_array; + struct cmd_ctrl_node *cur_cmd; + struct list_head cmdfreeq; /* free command buffers */ + struct list_head cmdpendingq; /* pending command buffers */ + struct timer_list command_timer; + int cmd_timed_out; + + /* Command responses sent from the hardware to the driver */ + u8 resp_idx; + u8 resp_buf[2][LBS_UPLD_SIZE]; + u32 resp_len[2]; + + /* Events sent from hardware to driver */ + struct kfifo event_fifo; + + /* thread to service interrupts */ + struct task_struct *main_thread; + wait_queue_head_t waitq; + struct workqueue_struct *work_thread; + + /* Encryption stuff */ + u8 authtype_auto; + u8 wep_tx_key; + u8 wep_key[4][WLAN_KEY_LEN_WEP104]; + u8 wep_key_len[4]; + + /* Wake On LAN */ + uint32_t wol_criteria; + uint8_t wol_gpio; + uint8_t wol_gap; + bool ehs_remove_supported; + + /* Transmitting */ + int tx_pending_len; /* -1 while building packet */ + u8 tx_pending_buf[LBS_UPLD_SIZE]; + /* protected by hard_start_xmit serialization */ + u8 txretrycount; + struct sk_buff *currenttxskb; + struct timer_list tx_lockup_timer; + + /* Locks */ + struct mutex lock; + spinlock_t driver_lock; + + /* NIC/link operation characteristics */ + u16 mac_control; + u8 radio_on; + u8 cur_rate; + u8 channel; + s16 txpower_cur; + s16 txpower_min; + s16 txpower_max; + + /* Scanning */ + struct delayed_work scan_work; + int scan_channel; + /* Queue of things waiting for scan completion */ + wait_queue_head_t scan_q; + /* Whether the scan was initiated internally and not by cfg80211 */ + bool internal_scan; + + /* Firmware load */ + u32 fw_model; + wait_queue_head_t fw_waitq; + struct device *fw_device; + const struct firmware *helper_fw; + const struct lbs_fw_table *fw_table; + const struct lbs_fw_table *fw_iter; + lbs_fw_cb fw_callback; +}; + +extern struct cmd_confirm_sleep confirm_sleep; + +/* Check if there is an interface active. */ +static inline int lbs_iface_active(struct lbs_private *priv) +{ + int r; + + r = netif_running(priv->dev); + if (priv->mesh_dev) + r |= netif_running(priv->mesh_dev); + + return r; +} + +#endif diff --git a/drivers/net/wireless/marvell/libertas/ethtool.c b/drivers/net/wireless/marvell/libertas/ethtool.c new file mode 100644 index 000000000000..f955b2d66ed6 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/ethtool.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include + +#include "decl.h" +#include "cmd.h" +#include "mesh.h" + + +static void lbs_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct lbs_private *priv = dev->ml_priv; + + snprintf(info->fw_version, sizeof(info->fw_version), + "%u.%u.%u.p%u", + priv->fwrelease >> 24 & 0xff, + priv->fwrelease >> 16 & 0xff, + priv->fwrelease >> 8 & 0xff, + priv->fwrelease & 0xff); + strlcpy(info->driver, "libertas", sizeof(info->driver)); + strlcpy(info->version, lbs_driver_version, sizeof(info->version)); +} + +/* + * All 8388 parts have 16KiB EEPROM size at the time of writing. + * In case that changes this needs fixing. + */ +#define LBS_EEPROM_LEN 16384 + +static int lbs_ethtool_get_eeprom_len(struct net_device *dev) +{ + return LBS_EEPROM_LEN; +} + +static int lbs_ethtool_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 * bytes) +{ + struct lbs_private *priv = dev->ml_priv; + struct cmd_ds_802_11_eeprom_access cmd; + int ret; + + lbs_deb_enter(LBS_DEB_ETHTOOL); + + if (eeprom->offset + eeprom->len > LBS_EEPROM_LEN || + eeprom->len > LBS_EEPROM_READ_LEN) { + ret = -EINVAL; + goto out; + } + + cmd.hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) - + LBS_EEPROM_READ_LEN + eeprom->len); + cmd.action = cpu_to_le16(CMD_ACT_GET); + cmd.offset = cpu_to_le16(eeprom->offset); + cmd.len = cpu_to_le16(eeprom->len); + ret = lbs_cmd_with_response(priv, CMD_802_11_EEPROM_ACCESS, &cmd); + if (!ret) + memcpy(bytes, cmd.value, eeprom->len); + +out: + lbs_deb_leave_args(LBS_DEB_ETHTOOL, "ret %d", ret); + return ret; +} + +static void lbs_ethtool_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct lbs_private *priv = dev->ml_priv; + + wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY; + + if (priv->wol_criteria == EHS_REMOVE_WAKEUP) + return; + + if (priv->wol_criteria & EHS_WAKE_ON_UNICAST_DATA) + wol->wolopts |= WAKE_UCAST; + if (priv->wol_criteria & EHS_WAKE_ON_MULTICAST_DATA) + wol->wolopts |= WAKE_MCAST; + if (priv->wol_criteria & EHS_WAKE_ON_BROADCAST_DATA) + wol->wolopts |= WAKE_BCAST; + if (priv->wol_criteria & EHS_WAKE_ON_MAC_EVENT) + wol->wolopts |= WAKE_PHY; +} + +static int lbs_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct lbs_private *priv = dev->ml_priv; + + if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY)) + return -EOPNOTSUPP; + + priv->wol_criteria = 0; + if (wol->wolopts & WAKE_UCAST) + priv->wol_criteria |= EHS_WAKE_ON_UNICAST_DATA; + if (wol->wolopts & WAKE_MCAST) + priv->wol_criteria |= EHS_WAKE_ON_MULTICAST_DATA; + if (wol->wolopts & WAKE_BCAST) + priv->wol_criteria |= EHS_WAKE_ON_BROADCAST_DATA; + if (wol->wolopts & WAKE_PHY) + priv->wol_criteria |= EHS_WAKE_ON_MAC_EVENT; + if (wol->wolopts == 0) + priv->wol_criteria |= EHS_REMOVE_WAKEUP; + return 0; +} + +const struct ethtool_ops lbs_ethtool_ops = { + .get_drvinfo = lbs_ethtool_get_drvinfo, + .get_eeprom = lbs_ethtool_get_eeprom, + .get_eeprom_len = lbs_ethtool_get_eeprom_len, +#ifdef CONFIG_LIBERTAS_MESH + .get_sset_count = lbs_mesh_ethtool_get_sset_count, + .get_ethtool_stats = lbs_mesh_ethtool_get_stats, + .get_strings = lbs_mesh_ethtool_get_strings, +#endif + .get_wol = lbs_ethtool_get_wol, + .set_wol = lbs_ethtool_set_wol, +}; + diff --git a/drivers/net/wireless/marvell/libertas/firmware.c b/drivers/net/wireless/marvell/libertas/firmware.c new file mode 100644 index 000000000000..51b92b5df119 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/firmware.c @@ -0,0 +1,227 @@ +/* + * Firmware loading and handling functions. + */ + +#include +#include +#include + +#include "dev.h" +#include "decl.h" + +static void load_next_firmware_from_table(struct lbs_private *private); + +static void lbs_fw_loaded(struct lbs_private *priv, int ret, + const struct firmware *helper, const struct firmware *mainfw) +{ + unsigned long flags; + + lbs_deb_fw("firmware load complete, code %d\n", ret); + + /* User must free helper/mainfw */ + priv->fw_callback(priv, ret, helper, mainfw); + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->fw_callback = NULL; + wake_up(&priv->fw_waitq); + spin_unlock_irqrestore(&priv->driver_lock, flags); +} + +static void do_load_firmware(struct lbs_private *priv, const char *name, + void (*cb)(const struct firmware *fw, void *context)) +{ + int ret; + + lbs_deb_fw("Requesting %s\n", name); + ret = request_firmware_nowait(THIS_MODULE, true, name, + priv->fw_device, GFP_KERNEL, priv, cb); + if (ret) { + lbs_deb_fw("request_firmware_nowait error %d\n", ret); + lbs_fw_loaded(priv, ret, NULL, NULL); + } +} + +static void main_firmware_cb(const struct firmware *firmware, void *context) +{ + struct lbs_private *priv = context; + + if (!firmware) { + /* Failed to find firmware: try next table entry */ + load_next_firmware_from_table(priv); + return; + } + + /* Firmware found! */ + lbs_fw_loaded(priv, 0, priv->helper_fw, firmware); + if (priv->helper_fw) { + release_firmware (priv->helper_fw); + priv->helper_fw = NULL; + } + release_firmware (firmware); +} + +static void helper_firmware_cb(const struct firmware *firmware, void *context) +{ + struct lbs_private *priv = context; + + if (!firmware) { + /* Failed to find firmware: try next table entry */ + load_next_firmware_from_table(priv); + return; + } + + /* Firmware found! */ + if (priv->fw_iter->fwname) { + priv->helper_fw = firmware; + do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb); + } else { + /* No main firmware needed for this helper --> success! */ + lbs_fw_loaded(priv, 0, firmware, NULL); + } +} + +static void load_next_firmware_from_table(struct lbs_private *priv) +{ + const struct lbs_fw_table *iter; + + if (!priv->fw_iter) + iter = priv->fw_table; + else + iter = ++priv->fw_iter; + + if (priv->helper_fw) { + release_firmware(priv->helper_fw); + priv->helper_fw = NULL; + } + +next: + if (!iter->helper) { + /* End of table hit. */ + lbs_fw_loaded(priv, -ENOENT, NULL, NULL); + return; + } + + if (iter->model != priv->fw_model) { + iter++; + goto next; + } + + priv->fw_iter = iter; + do_load_firmware(priv, iter->helper, helper_firmware_cb); +} + +void lbs_wait_for_firmware_load(struct lbs_private *priv) +{ + wait_event(priv->fw_waitq, priv->fw_callback == NULL); +} + +/** + * lbs_get_firmware_async - Retrieves firmware asynchronously. Can load + * either a helper firmware and a main firmware (2-stage), or just the helper. + * + * @priv: Pointer to lbs_private instance + * @dev: A pointer to &device structure + * @card_model: Bus-specific card model ID used to filter firmware table + * elements + * @fw_table: Table of firmware file names and device model numbers + * terminated by an entry with a NULL helper name + * @callback: User callback to invoke when firmware load succeeds or fails. + */ +int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, + u32 card_model, const struct lbs_fw_table *fw_table, + lbs_fw_cb callback) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->driver_lock, flags); + if (priv->fw_callback) { + lbs_deb_fw("firmware load already in progress\n"); + spin_unlock_irqrestore(&priv->driver_lock, flags); + return -EBUSY; + } + + priv->fw_device = device; + priv->fw_callback = callback; + priv->fw_table = fw_table; + priv->fw_iter = NULL; + priv->fw_model = card_model; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + lbs_deb_fw("Starting async firmware load\n"); + load_next_firmware_from_table(priv); + return 0; +} +EXPORT_SYMBOL_GPL(lbs_get_firmware_async); + +/** + * lbs_get_firmware - Retrieves two-stage firmware + * + * @dev: A pointer to &device structure + * @card_model: Bus-specific card model ID used to filter firmware table + * elements + * @fw_table: Table of firmware file names and device model numbers + * terminated by an entry with a NULL helper name + * @helper: On success, the helper firmware; caller must free + * @mainfw: On success, the main firmware; caller must free + * + * Deprecated: use lbs_get_firmware_async() instead. + * + * returns: 0 on success, non-zero on failure + */ +int lbs_get_firmware(struct device *dev, u32 card_model, + const struct lbs_fw_table *fw_table, + const struct firmware **helper, + const struct firmware **mainfw) +{ + const struct lbs_fw_table *iter; + int ret; + + BUG_ON(helper == NULL); + BUG_ON(mainfw == NULL); + + /* Search for firmware to use from the table. */ + iter = fw_table; + while (iter && iter->helper) { + if (iter->model != card_model) + goto next; + + if (*helper == NULL) { + ret = request_firmware(helper, iter->helper, dev); + if (ret) + goto next; + + /* If the device has one-stage firmware (ie cf8305) and + * we've got it then we don't need to bother with the + * main firmware. + */ + if (iter->fwname == NULL) + return 0; + } + + if (*mainfw == NULL) { + ret = request_firmware(mainfw, iter->fwname, dev); + if (ret) { + /* Clear the helper to ensure we don't have + * mismatched firmware pairs. + */ + release_firmware(*helper); + *helper = NULL; + } + } + + if (*helper && *mainfw) + return 0; + + next: + iter++; + } + + /* Failed */ + release_firmware(*helper); + *helper = NULL; + release_firmware(*mainfw); + *mainfw = NULL; + + return -ENOENT; +} +EXPORT_SYMBOL_GPL(lbs_get_firmware); diff --git a/drivers/net/wireless/marvell/libertas/host.h b/drivers/net/wireless/marvell/libertas/host.h new file mode 100644 index 000000000000..96726f79a1dd --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/host.h @@ -0,0 +1,978 @@ +/* + * This file function prototypes, data structure + * and definitions for all the host/station commands + */ + +#ifndef _LBS_HOST_H_ +#define _LBS_HOST_H_ + +#include "types.h" +#include "defs.h" + +#define DEFAULT_AD_HOC_CHANNEL 6 + +#define CMD_OPTION_WAITFORRSP 0x0002 + +/* Host command IDs */ + +/* + * Return command are almost always the same as the host command, but with + * bit 15 set high. There are a few exceptions, though... + */ +#define CMD_RET(cmd) (0x8000 | cmd) + +/* Return command convention exceptions: */ +#define CMD_RET_802_11_ASSOCIATE 0x8012 + +/* Command codes */ +#define CMD_GET_HW_SPEC 0x0003 +#define CMD_EEPROM_UPDATE 0x0004 +#define CMD_802_11_RESET 0x0005 +#define CMD_802_11_SCAN 0x0006 +#define CMD_802_11_GET_LOG 0x000b +#define CMD_MAC_MULTICAST_ADR 0x0010 +#define CMD_802_11_AUTHENTICATE 0x0011 +#define CMD_802_11_EEPROM_ACCESS 0x0059 +#define CMD_802_11_ASSOCIATE 0x0050 +#define CMD_802_11_SET_WEP 0x0013 +#define CMD_802_11_GET_STAT 0x0014 +#define CMD_802_3_GET_STAT 0x0015 +#define CMD_802_11_SNMP_MIB 0x0016 +#define CMD_MAC_REG_MAP 0x0017 +#define CMD_BBP_REG_MAP 0x0018 +#define CMD_MAC_REG_ACCESS 0x0019 +#define CMD_BBP_REG_ACCESS 0x001a +#define CMD_RF_REG_ACCESS 0x001b +#define CMD_802_11_RADIO_CONTROL 0x001c +#define CMD_802_11_RF_CHANNEL 0x001d +#define CMD_802_11_RF_TX_POWER 0x001e +#define CMD_802_11_RSSI 0x001f +#define CMD_802_11_RF_ANTENNA 0x0020 +#define CMD_802_11_PS_MODE 0x0021 +#define CMD_802_11_DATA_RATE 0x0022 +#define CMD_RF_REG_MAP 0x0023 +#define CMD_802_11_DEAUTHENTICATE 0x0024 +#define CMD_802_11_REASSOCIATE 0x0025 +#define CMD_MAC_CONTROL 0x0028 +#define CMD_802_11_AD_HOC_START 0x002b +#define CMD_802_11_AD_HOC_JOIN 0x002c +#define CMD_802_11_QUERY_TKIP_REPLY_CNTRS 0x002e +#define CMD_802_11_ENABLE_RSN 0x002f +#define CMD_802_11_SET_AFC 0x003c +#define CMD_802_11_GET_AFC 0x003d +#define CMD_802_11_DEEP_SLEEP 0x003e +#define CMD_802_11_AD_HOC_STOP 0x0040 +#define CMD_802_11_HOST_SLEEP_CFG 0x0043 +#define CMD_802_11_WAKEUP_CONFIRM 0x0044 +#define CMD_802_11_HOST_SLEEP_ACTIVATE 0x0045 +#define CMD_802_11_BEACON_STOP 0x0049 +#define CMD_802_11_MAC_ADDRESS 0x004d +#define CMD_802_11_LED_GPIO_CTRL 0x004e +#define CMD_802_11_BAND_CONFIG 0x0058 +#define CMD_GSPI_BUS_CONFIG 0x005a +#define CMD_802_11D_DOMAIN_INFO 0x005b +#define CMD_802_11_KEY_MATERIAL 0x005e +#define CMD_802_11_SLEEP_PARAMS 0x0066 +#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067 +#define CMD_802_11_SLEEP_PERIOD 0x0068 +#define CMD_802_11_TPC_CFG 0x0072 +#define CMD_802_11_PA_CFG 0x0073 +#define CMD_802_11_FW_WAKE_METHOD 0x0074 +#define CMD_802_11_SUBSCRIBE_EVENT 0x0075 +#define CMD_802_11_RATE_ADAPT_RATESET 0x0076 +#define CMD_802_11_TX_RATE_QUERY 0x007f +#define CMD_GET_TSF 0x0080 +#define CMD_BT_ACCESS 0x0087 +#define CMD_FWT_ACCESS 0x0095 +#define CMD_802_11_MONITOR_MODE 0x0098 +#define CMD_MESH_ACCESS 0x009b +#define CMD_MESH_CONFIG_OLD 0x00a3 +#define CMD_MESH_CONFIG 0x00ac +#define CMD_SET_BOOT2_VER 0x00a5 +#define CMD_FUNC_INIT 0x00a9 +#define CMD_FUNC_SHUTDOWN 0x00aa +#define CMD_802_11_BEACON_CTRL 0x00b0 + +/* For the IEEE Power Save */ +#define PS_MODE_ACTION_ENTER_PS 0x0030 +#define PS_MODE_ACTION_EXIT_PS 0x0031 +#define PS_MODE_ACTION_SLEEP_CONFIRMED 0x0034 + +#define CMD_ENABLE_RSN 0x0001 +#define CMD_DISABLE_RSN 0x0000 + +#define CMD_ACT_GET 0x0000 +#define CMD_ACT_SET 0x0001 + +/* Define action or option for CMD_802_11_SET_WEP */ +#define CMD_ACT_ADD 0x0002 +#define CMD_ACT_REMOVE 0x0004 + +#define CMD_TYPE_WEP_40_BIT 0x01 +#define CMD_TYPE_WEP_104_BIT 0x02 + +#define CMD_NUM_OF_WEP_KEYS 4 + +#define CMD_WEP_KEY_INDEX_MASK 0x3fff + +/* Define action or option for CMD_802_11_SCAN */ +#define CMD_BSS_TYPE_BSS 0x0001 +#define CMD_BSS_TYPE_IBSS 0x0002 +#define CMD_BSS_TYPE_ANY 0x0003 + +/* Define action or option for CMD_802_11_SCAN */ +#define CMD_SCAN_TYPE_ACTIVE 0x0000 +#define CMD_SCAN_TYPE_PASSIVE 0x0001 + +#define CMD_SCAN_RADIO_TYPE_BG 0 + +#define CMD_SCAN_PROBE_DELAY_TIME 0 + +/* Define action or option for CMD_MAC_CONTROL */ +#define CMD_ACT_MAC_RX_ON 0x0001 +#define CMD_ACT_MAC_TX_ON 0x0002 +#define CMD_ACT_MAC_LOOPBACK_ON 0x0004 +#define CMD_ACT_MAC_WEP_ENABLE 0x0008 +#define CMD_ACT_MAC_INT_ENABLE 0x0010 +#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 +#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 +#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 +#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 +#define CMD_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400 + +/* Event flags for CMD_802_11_SUBSCRIBE_EVENT */ +#define CMD_SUBSCRIBE_RSSI_LOW 0x0001 +#define CMD_SUBSCRIBE_SNR_LOW 0x0002 +#define CMD_SUBSCRIBE_FAILCOUNT 0x0004 +#define CMD_SUBSCRIBE_BCNMISS 0x0008 +#define CMD_SUBSCRIBE_RSSI_HIGH 0x0010 +#define CMD_SUBSCRIBE_SNR_HIGH 0x0020 + +#define RADIO_PREAMBLE_LONG 0x00 +#define RADIO_PREAMBLE_SHORT 0x02 +#define RADIO_PREAMBLE_AUTO 0x04 + +/* Define action or option for CMD_802_11_RF_CHANNEL */ +#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 +#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 + +/* Define action or option for CMD_802_11_DATA_RATE */ +#define CMD_ACT_SET_TX_AUTO 0x0000 +#define CMD_ACT_SET_TX_FIX_RATE 0x0001 +#define CMD_ACT_GET_TX_RATE 0x0002 + +/* Options for CMD_802_11_FW_WAKE_METHOD */ +#define CMD_WAKE_METHOD_UNCHANGED 0x0000 +#define CMD_WAKE_METHOD_COMMAND_INT 0x0001 +#define CMD_WAKE_METHOD_GPIO 0x0002 + +/* Object IDs for CMD_802_11_SNMP_MIB */ +#define SNMP_MIB_OID_BSS_TYPE 0x0000 +#define SNMP_MIB_OID_OP_RATE_SET 0x0001 +#define SNMP_MIB_OID_BEACON_PERIOD 0x0002 /* Reserved on v9+ */ +#define SNMP_MIB_OID_DTIM_PERIOD 0x0003 /* Reserved on v9+ */ +#define SNMP_MIB_OID_ASSOC_TIMEOUT 0x0004 /* Reserved on v9+ */ +#define SNMP_MIB_OID_RTS_THRESHOLD 0x0005 +#define SNMP_MIB_OID_SHORT_RETRY_LIMIT 0x0006 +#define SNMP_MIB_OID_LONG_RETRY_LIMIT 0x0007 +#define SNMP_MIB_OID_FRAG_THRESHOLD 0x0008 +#define SNMP_MIB_OID_11D_ENABLE 0x0009 +#define SNMP_MIB_OID_11H_ENABLE 0x000A + +/* Define action or option for CMD_BT_ACCESS */ +enum cmd_bt_access_opts { + /* The bt commands start at 5 instead of 1 because the old dft commands + * are mapped to 1-4. These old commands are no longer maintained and + * should not be called. + */ + CMD_ACT_BT_ACCESS_ADD = 5, + CMD_ACT_BT_ACCESS_DEL, + CMD_ACT_BT_ACCESS_LIST, + CMD_ACT_BT_ACCESS_RESET, + CMD_ACT_BT_ACCESS_SET_INVERT, + CMD_ACT_BT_ACCESS_GET_INVERT +}; + +/* Define action or option for CMD_FWT_ACCESS */ +enum cmd_fwt_access_opts { + CMD_ACT_FWT_ACCESS_ADD = 1, + CMD_ACT_FWT_ACCESS_DEL, + CMD_ACT_FWT_ACCESS_LOOKUP, + CMD_ACT_FWT_ACCESS_LIST, + CMD_ACT_FWT_ACCESS_LIST_ROUTE, + CMD_ACT_FWT_ACCESS_LIST_NEIGHBOR, + CMD_ACT_FWT_ACCESS_RESET, + CMD_ACT_FWT_ACCESS_CLEANUP, + CMD_ACT_FWT_ACCESS_TIME, +}; + +/* Define action or option for CMD_802_11_HOST_SLEEP_CFG */ +enum cmd_wol_cfg_opts { + CMD_ACT_ACTION_NONE = 0, + CMD_ACT_SET_WOL_RULE, + CMD_ACT_GET_WOL_RULE, + CMD_ACT_RESET_WOL_RULE, +}; + +/* Define action or option for CMD_MESH_ACCESS */ +enum cmd_mesh_access_opts { + CMD_ACT_MESH_GET_TTL = 1, + CMD_ACT_MESH_SET_TTL, + CMD_ACT_MESH_GET_STATS, + CMD_ACT_MESH_GET_ANYCAST, + CMD_ACT_MESH_SET_ANYCAST, + CMD_ACT_MESH_SET_LINK_COSTS, + CMD_ACT_MESH_GET_LINK_COSTS, + CMD_ACT_MESH_SET_BCAST_RATE, + CMD_ACT_MESH_GET_BCAST_RATE, + CMD_ACT_MESH_SET_RREQ_DELAY, + CMD_ACT_MESH_GET_RREQ_DELAY, + CMD_ACT_MESH_SET_ROUTE_EXP, + CMD_ACT_MESH_GET_ROUTE_EXP, + CMD_ACT_MESH_SET_AUTOSTART_ENABLED, + CMD_ACT_MESH_GET_AUTOSTART_ENABLED, + CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT = 17, +}; + +/* Define actions and types for CMD_MESH_CONFIG */ +enum cmd_mesh_config_actions { + CMD_ACT_MESH_CONFIG_STOP = 0, + CMD_ACT_MESH_CONFIG_START, + CMD_ACT_MESH_CONFIG_SET, + CMD_ACT_MESH_CONFIG_GET, +}; + +enum cmd_mesh_config_types { + CMD_TYPE_MESH_SET_BOOTFLAG = 1, + CMD_TYPE_MESH_SET_BOOTTIME, + CMD_TYPE_MESH_SET_DEF_CHANNEL, + CMD_TYPE_MESH_SET_MESH_IE, + CMD_TYPE_MESH_GET_DEFAULTS, + CMD_TYPE_MESH_GET_MESH_IE, /* GET_DEFAULTS is superset of GET_MESHIE */ +}; + +/* Card Event definition */ +#define MACREG_INT_CODE_TX_PPA_FREE 0 +#define MACREG_INT_CODE_TX_DMA_DONE 1 +#define MACREG_INT_CODE_LINK_LOST_W_SCAN 2 +#define MACREG_INT_CODE_LINK_LOST_NO_SCAN 3 +#define MACREG_INT_CODE_LINK_SENSED 4 +#define MACREG_INT_CODE_CMD_FINISHED 5 +#define MACREG_INT_CODE_MIB_CHANGED 6 +#define MACREG_INT_CODE_INIT_DONE 7 +#define MACREG_INT_CODE_DEAUTHENTICATED 8 +#define MACREG_INT_CODE_DISASSOCIATED 9 +#define MACREG_INT_CODE_PS_AWAKE 10 +#define MACREG_INT_CODE_PS_SLEEP 11 +#define MACREG_INT_CODE_MIC_ERR_MULTICAST 13 +#define MACREG_INT_CODE_MIC_ERR_UNICAST 14 +#define MACREG_INT_CODE_WM_AWAKE 15 +#define MACREG_INT_CODE_DEEP_SLEEP_AWAKE 16 +#define MACREG_INT_CODE_ADHOC_BCN_LOST 17 +#define MACREG_INT_CODE_HOST_AWAKE 18 +#define MACREG_INT_CODE_STOP_TX 19 +#define MACREG_INT_CODE_START_TX 20 +#define MACREG_INT_CODE_CHANNEL_SWITCH 21 +#define MACREG_INT_CODE_MEASUREMENT_RDY 22 +#define MACREG_INT_CODE_WMM_CHANGE 23 +#define MACREG_INT_CODE_BG_SCAN_REPORT 24 +#define MACREG_INT_CODE_RSSI_LOW 25 +#define MACREG_INT_CODE_SNR_LOW 26 +#define MACREG_INT_CODE_MAX_FAIL 27 +#define MACREG_INT_CODE_RSSI_HIGH 28 +#define MACREG_INT_CODE_SNR_HIGH 29 +#define MACREG_INT_CODE_MESH_AUTO_STARTED 35 +#define MACREG_INT_CODE_FIRMWARE_READY 48 + + +/* 802.11-related definitions */ + +/* TxPD descriptor */ +struct txpd { + /* union to cope up with later FW revisions */ + union { + /* Current Tx packet status */ + __le32 tx_status; + struct { + /* BSS type: client, AP, etc. */ + u8 bss_type; + /* BSS number */ + u8 bss_num; + /* Reserved */ + __le16 reserved; + } bss; + } u; + /* Tx control */ + __le32 tx_control; + __le32 tx_packet_location; + /* Tx packet length */ + __le16 tx_packet_length; + /* First 2 byte of destination MAC address */ + u8 tx_dest_addr_high[2]; + /* Last 4 byte of destination MAC address */ + u8 tx_dest_addr_low[4]; + /* Pkt Priority */ + u8 priority; + /* Pkt Trasnit Power control */ + u8 powermgmt; + /* Amount of time the packet has been queued (units = 2ms) */ + u8 pktdelay_2ms; + /* reserved */ + u8 reserved1; +} __packed; + +/* RxPD Descriptor */ +struct rxpd { + /* union to cope up with later FW revisions */ + union { + /* Current Rx packet status */ + __le16 status; + struct { + /* BSS type: client, AP, etc. */ + u8 bss_type; + /* BSS number */ + u8 bss_num; + } __packed bss; + } __packed u; + + /* SNR */ + u8 snr; + + /* Tx control */ + u8 rx_control; + + /* Pkt length */ + __le16 pkt_len; + + /* Noise Floor */ + u8 nf; + + /* Rx Packet Rate */ + u8 rx_rate; + + /* Pkt addr */ + __le32 pkt_ptr; + + /* Next Rx RxPD addr */ + __le32 next_rxpd_ptr; + + /* Pkt Priority */ + u8 priority; + u8 reserved[3]; +} __packed; + +struct cmd_header { + __le16 command; + __le16 size; + __le16 seqnum; + __le16 result; +} __packed; + +/* Generic structure to hold all key types. */ +struct enc_key { + u16 len; + u16 flags; /* KEY_INFO_* from defs.h */ + u16 type; /* KEY_TYPE_* from defs.h */ + u8 key[32]; +}; + +/* lbs_offset_value */ +struct lbs_offset_value { + u32 offset; + u32 value; +} __packed; + +#define MAX_11D_TRIPLETS 83 + +struct mrvl_ie_domain_param_set { + struct mrvl_ie_header header; + + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + struct ieee80211_country_ie_triplet triplet[MAX_11D_TRIPLETS]; +} __packed; + +struct cmd_ds_802_11d_domain_info { + struct cmd_header hdr; + + __le16 action; + struct mrvl_ie_domain_param_set domain; +} __packed; + +/* + * Define data structure for CMD_GET_HW_SPEC + * This structure defines the response for the GET_HW_SPEC command + */ +struct cmd_ds_get_hw_spec { + struct cmd_header hdr; + + /* HW Interface version number */ + __le16 hwifversion; + /* HW version number */ + __le16 version; + /* Max number of TxPD FW can handle */ + __le16 nr_txpd; + /* Max no of Multicast address */ + __le16 nr_mcast_adr; + /* MAC address */ + u8 permanentaddr[6]; + + /* region Code */ + __le16 regioncode; + + /* Number of antenna used */ + __le16 nr_antenna; + + /* FW release number, example 0x01030304 = 2.3.4p1 */ + __le32 fwrelease; + + /* Base Address of TxPD queue */ + __le32 wcb_base; + /* Read Pointer of RxPd queue */ + __le32 rxpd_rdptr; + + /* Write Pointer of RxPd queue */ + __le32 rxpd_wrptr; + + /*FW/HW capability */ + __le32 fwcapinfo; +} __packed; + +struct cmd_ds_802_11_subscribe_event { + struct cmd_header hdr; + + __le16 action; + __le16 events; + + /* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a + * number of TLVs. From the v5.1 manual, those TLVs would add up to + * 40 bytes. However, future firmware might add additional TLVs, so I + * bump this up a bit. + */ + uint8_t tlv[128]; +} __packed; + +/* + * This scan handle Country Information IE(802.11d compliant) + * Define data structure for CMD_802_11_SCAN + */ +struct cmd_ds_802_11_scan { + struct cmd_header hdr; + + uint8_t bsstype; + uint8_t bssid[ETH_ALEN]; + uint8_t tlvbuffer[0]; +} __packed; + +struct cmd_ds_802_11_scan_rsp { + struct cmd_header hdr; + + __le16 bssdescriptsize; + uint8_t nr_sets; + uint8_t bssdesc_and_tlvbuffer[0]; +} __packed; + +struct cmd_ds_802_11_get_log { + struct cmd_header hdr; + + __le32 mcasttxframe; + __le32 failed; + __le32 retry; + __le32 multiretry; + __le32 framedup; + __le32 rtssuccess; + __le32 rtsfailure; + __le32 ackfailure; + __le32 rxfrag; + __le32 mcastrxframe; + __le32 fcserror; + __le32 txframe; + __le32 wepundecryptable; +} __packed; + +struct cmd_ds_mac_control { + struct cmd_header hdr; + __le16 action; + u16 reserved; +} __packed; + +struct cmd_ds_mac_multicast_adr { + struct cmd_header hdr; + __le16 action; + __le16 nr_of_adrs; + u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; +} __packed; + +struct cmd_ds_802_11_authenticate { + struct cmd_header hdr; + + u8 bssid[ETH_ALEN]; + u8 authtype; + u8 reserved[10]; +} __packed; + +struct cmd_ds_802_11_deauthenticate { + struct cmd_header hdr; + + u8 macaddr[ETH_ALEN]; + __le16 reasoncode; +} __packed; + +struct cmd_ds_802_11_associate { + struct cmd_header hdr; + + u8 bssid[6]; + __le16 capability; + __le16 listeninterval; + __le16 bcnperiod; + u8 dtimperiod; + u8 iebuf[512]; /* Enough for required and most optional IEs */ +} __packed; + +struct cmd_ds_802_11_associate_response { + struct cmd_header hdr; + + __le16 capability; + __le16 statuscode; + __le16 aid; + u8 iebuf[512]; +} __packed; + +struct cmd_ds_802_11_set_wep { + struct cmd_header hdr; + + /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ + __le16 action; + + /* key Index selected for Tx */ + __le16 keyindex; + + /* 40, 128bit or TXWEP */ + uint8_t keytype[4]; + uint8_t keymaterial[4][16]; +} __packed; + +struct cmd_ds_802_11_snmp_mib { + struct cmd_header hdr; + + __le16 action; + __le16 oid; + __le16 bufsize; + u8 value[128]; +} __packed; + +struct cmd_ds_reg_access { + struct cmd_header hdr; + + __le16 action; + __le16 offset; + union { + u8 bbp_rf; /* for BBP and RF registers */ + __le32 mac; /* for MAC registers */ + } value; +} __packed; + +struct cmd_ds_802_11_radio_control { + struct cmd_header hdr; + + __le16 action; + __le16 control; +} __packed; + +struct cmd_ds_802_11_beacon_control { + struct cmd_header hdr; + + __le16 action; + __le16 beacon_enable; + __le16 beacon_period; +} __packed; + +struct cmd_ds_802_11_sleep_params { + struct cmd_header hdr; + + /* ACT_GET/ACT_SET */ + __le16 action; + + /* Sleep clock error in ppm */ + __le16 error; + + /* Wakeup offset in usec */ + __le16 offset; + + /* Clock stabilization time in usec */ + __le16 stabletime; + + /* control periodic calibration */ + uint8_t calcontrol; + + /* control the use of external sleep clock */ + uint8_t externalsleepclk; + + /* reserved field, should be set to zero */ + __le16 reserved; +} __packed; + +struct cmd_ds_802_11_rf_channel { + struct cmd_header hdr; + + __le16 action; + __le16 channel; + __le16 rftype; /* unused */ + __le16 reserved; /* unused */ + u8 channellist[32]; /* unused */ +} __packed; + +struct cmd_ds_802_11_rssi { + struct cmd_header hdr; + + /* + * request: number of beacons (N) to average the SNR and NF over + * response: SNR of most recent beacon + */ + __le16 n_or_snr; + + /* + * The following fields are only set in the response. + * In the request these are reserved and should be set to 0. + */ + __le16 nf; /* most recent beacon noise floor */ + __le16 avg_snr; /* average SNR weighted by N from request */ + __le16 avg_nf; /* average noise floor weighted by N from request */ +} __packed; + +struct cmd_ds_802_11_mac_address { + struct cmd_header hdr; + + __le16 action; + u8 macadd[ETH_ALEN]; +} __packed; + +struct cmd_ds_802_11_rf_tx_power { + struct cmd_header hdr; + + __le16 action; + __le16 curlevel; + s8 maxlevel; + s8 minlevel; +} __packed; + +/* MONITOR_MODE only exists in OLPC v5 firmware */ +struct cmd_ds_802_11_monitor_mode { + struct cmd_header hdr; + + __le16 action; + __le16 mode; +} __packed; + +struct cmd_ds_set_boot2_ver { + struct cmd_header hdr; + + __le16 action; + __le16 version; +} __packed; + +struct cmd_ds_802_11_fw_wake_method { + struct cmd_header hdr; + + __le16 action; + __le16 method; +} __packed; + +struct cmd_ds_802_11_ps_mode { + struct cmd_header hdr; + + __le16 action; + + /* + * Interval for keepalive in PS mode: + * 0x0000 = don't change + * 0x001E = firmware default + * 0xFFFF = disable + */ + __le16 nullpktinterval; + + /* + * Number of DTIM intervals to wake up for: + * 0 = don't change + * 1 = firmware default + * 5 = max + */ + __le16 multipledtim; + + __le16 reserved; + __le16 locallisteninterval; + + /* + * AdHoc awake period (FW v9+ only): + * 0 = don't change + * 1 = always awake (IEEE standard behavior) + * 2 - 31 = sleep for (n - 1) periods and awake for 1 period + * 32 - 254 = invalid + * 255 = sleep at each ATIM + */ + __le16 adhoc_awake_period; +} __packed; + +struct cmd_confirm_sleep { + struct cmd_header hdr; + + __le16 action; + __le16 nullpktinterval; + __le16 multipledtim; + __le16 reserved; + __le16 locallisteninterval; +} __packed; + +struct cmd_ds_802_11_data_rate { + struct cmd_header hdr; + + __le16 action; + __le16 reserved; + u8 rates[MAX_RATES]; +} __packed; + +struct cmd_ds_802_11_rate_adapt_rateset { + struct cmd_header hdr; + __le16 action; + __le16 enablehwauto; + __le16 bitmap; +} __packed; + +struct cmd_ds_802_11_ad_hoc_start { + struct cmd_header hdr; + + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 bsstype; + __le16 beaconperiod; + u8 dtimperiod; /* Reserved on v9 and later */ + struct ieee_ie_ibss_param_set ibss; + u8 reserved1[4]; + struct ieee_ie_ds_param_set ds; + u8 reserved2[4]; + __le16 probedelay; /* Reserved on v9 and later */ + __le16 capability; + u8 rates[MAX_RATES]; + u8 tlv_memory_size_pad[100]; +} __packed; + +struct cmd_ds_802_11_ad_hoc_result { + struct cmd_header hdr; + + u8 pad[3]; + u8 bssid[ETH_ALEN]; +} __packed; + +struct adhoc_bssdesc { + u8 bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 type; + __le16 beaconperiod; + u8 dtimperiod; + __le64 timestamp; + __le64 localtime; + struct ieee_ie_ds_param_set ds; + u8 reserved1[4]; + struct ieee_ie_ibss_param_set ibss; + u8 reserved2[4]; + __le16 capability; + u8 rates[MAX_RATES]; + + /* + * DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the + * Adhoc join command and will cause a binary layout mismatch with + * the firmware + */ +} __packed; + +struct cmd_ds_802_11_ad_hoc_join { + struct cmd_header hdr; + + struct adhoc_bssdesc bss; + __le16 failtimeout; /* Reserved on v9 and later */ + __le16 probedelay; /* Reserved on v9 and later */ +} __packed; + +struct cmd_ds_802_11_ad_hoc_stop { + struct cmd_header hdr; +} __packed; + +struct cmd_ds_802_11_enable_rsn { + struct cmd_header hdr; + + __le16 action; + __le16 enable; +} __packed; + +struct MrvlIEtype_keyParamSet { + /* type ID */ + __le16 type; + + /* length of Payload */ + __le16 length; + + /* type of key: WEP=0, TKIP=1, AES=2 */ + __le16 keytypeid; + + /* key control Info specific to a keytypeid */ + __le16 keyinfo; + + /* length of key */ + __le16 keylen; + + /* key material of size keylen */ + u8 key[32]; +} __packed; + +#define MAX_WOL_RULES 16 + +struct host_wol_rule { + uint8_t rule_no; + uint8_t rule_ops; + __le16 sig_offset; + __le16 sig_length; + __le16 reserve; + __be32 sig_mask; + __be32 signature; +} __packed; + +struct wol_config { + uint8_t action; + uint8_t pattern; + uint8_t no_rules_in_cmd; + uint8_t result; + struct host_wol_rule rule[MAX_WOL_RULES]; +} __packed; + +struct cmd_ds_host_sleep { + struct cmd_header hdr; + __le32 criteria; + uint8_t gpio; + uint16_t gap; + struct wol_config wol_conf; +} __packed; + + + +struct cmd_ds_802_11_key_material { + struct cmd_header hdr; + + __le16 action; + struct MrvlIEtype_keyParamSet keyParamSet[2]; +} __packed; + +struct cmd_ds_802_11_eeprom_access { + struct cmd_header hdr; + __le16 action; + __le16 offset; + __le16 len; + /* firmware says it returns a maximum of 20 bytes */ +#define LBS_EEPROM_READ_LEN 20 + u8 value[LBS_EEPROM_READ_LEN]; +} __packed; + +struct cmd_ds_802_11_tpc_cfg { + struct cmd_header hdr; + + __le16 action; + uint8_t enable; + int8_t P0; + int8_t P1; + int8_t P2; + uint8_t usesnr; +} __packed; + + +struct cmd_ds_802_11_pa_cfg { + struct cmd_header hdr; + + __le16 action; + uint8_t enable; + int8_t P0; + int8_t P1; + int8_t P2; +} __packed; + + +struct cmd_ds_802_11_led_ctrl { + struct cmd_header hdr; + + __le16 action; + __le16 numled; + u8 data[256]; +} __packed; + +/* Automatic Frequency Control */ +struct cmd_ds_802_11_afc { + struct cmd_header hdr; + + __le16 afc_auto; + union { + struct { + __le16 threshold; + __le16 period; + }; + struct { + __le16 timing_offset; /* signed */ + __le16 carrier_offset; /* signed */ + }; + }; +} __packed; + +struct cmd_tx_rate_query { + __le16 txrate; +} __packed; + +struct cmd_ds_get_tsf { + __le64 tsfvalue; +} __packed; + +struct cmd_ds_bt_access { + struct cmd_header hdr; + + __le16 action; + __le32 id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; +} __packed; + +struct cmd_ds_fwt_access { + struct cmd_header hdr; + + __le16 action; + __le32 id; + u8 valid; + u8 da[ETH_ALEN]; + u8 dir; + u8 ra[ETH_ALEN]; + __le32 ssn; + __le32 dsn; + __le32 metric; + u8 rate; + u8 hopcount; + u8 ttl; + __le32 expiration; + u8 sleepmode; + __le32 snr; + __le32 references; + u8 prec[ETH_ALEN]; +} __packed; + +struct cmd_ds_mesh_config { + struct cmd_header hdr; + + __le16 action; + __le16 channel; + __le16 type; + __le16 length; + u8 data[128]; /* last position reserved */ +} __packed; + +struct cmd_ds_mesh_access { + struct cmd_header hdr; + + __le16 action; + __le32 data[32]; /* last position reserved */ +} __packed; + +/* Number of stats counters returned by the firmware */ +#define MESH_STATS_NUM 8 +#endif diff --git a/drivers/net/wireless/marvell/libertas/if_cs.c b/drivers/net/wireless/marvell/libertas/if_cs.c new file mode 100644 index 000000000000..f499efc6abcf --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_cs.c @@ -0,0 +1,1006 @@ +/* + + Driver for the Marvell 8385 based compact flash WLAN cards. + + (C) 2007 by Holger Schurig + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define DRV_NAME "libertas_cs" + +#include "decl.h" +#include "defs.h" +#include "dev.h" + + +/********************************************************************/ +/* Module stuff */ +/********************************************************************/ + +MODULE_AUTHOR("Holger Schurig "); +MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards"); +MODULE_LICENSE("GPL"); + + + +/********************************************************************/ +/* Data structures */ +/********************************************************************/ + +struct if_cs_card { + struct pcmcia_device *p_dev; + struct lbs_private *priv; + void __iomem *iobase; + bool align_regs; + u32 model; +}; + + +enum { + MODEL_UNKNOWN = 0x00, + MODEL_8305 = 0x01, + MODEL_8381 = 0x02, + MODEL_8385 = 0x03 +}; + +static const struct lbs_fw_table fw_table[] = { + { MODEL_8305, "libertas/cf8305.bin", NULL }, + { MODEL_8305, "libertas_cs_helper.fw", NULL }, + { MODEL_8381, "libertas/cf8381_helper.bin", "libertas/cf8381.bin" }, + { MODEL_8381, "libertas_cs_helper.fw", "libertas_cs.fw" }, + { MODEL_8385, "libertas/cf8385_helper.bin", "libertas/cf8385.bin" }, + { MODEL_8385, "libertas_cs_helper.fw", "libertas_cs.fw" }, + { 0, NULL, NULL } +}; +MODULE_FIRMWARE("libertas/cf8305.bin"); +MODULE_FIRMWARE("libertas/cf8381_helper.bin"); +MODULE_FIRMWARE("libertas/cf8381.bin"); +MODULE_FIRMWARE("libertas/cf8385_helper.bin"); +MODULE_FIRMWARE("libertas/cf8385.bin"); +MODULE_FIRMWARE("libertas_cs_helper.fw"); +MODULE_FIRMWARE("libertas_cs.fw"); + + +/********************************************************************/ +/* Hardware access */ +/********************************************************************/ + +/* This define enables wrapper functions which allow you + to dump all register accesses. You normally won't this, + except for development */ +/* #define DEBUG_IO */ + +#ifdef DEBUG_IO +static int debug_output = 0; +#else +/* This way the compiler optimizes the printk's away */ +#define debug_output 0 +#endif + +static inline unsigned int if_cs_read8(struct if_cs_card *card, uint reg) +{ + unsigned int val = ioread8(card->iobase + reg); + if (debug_output) + printk(KERN_INFO "inb %08x<%02x\n", reg, val); + return val; +} +static inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg) +{ + unsigned int val = ioread16(card->iobase + reg); + if (debug_output) + printk(KERN_INFO "inw %08x<%04x\n", reg, val); + return val; +} +static inline void if_cs_read16_rep( + struct if_cs_card *card, + uint reg, + void *buf, + unsigned long count) +{ + if (debug_output) + printk(KERN_INFO "insw %08x<(0x%lx words)\n", + reg, count); + ioread16_rep(card->iobase + reg, buf, count); +} + +static inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val) +{ + if (debug_output) + printk(KERN_INFO "outb %08x>%02x\n", reg, val); + iowrite8(val, card->iobase + reg); +} + +static inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val) +{ + if (debug_output) + printk(KERN_INFO "outw %08x>%04x\n", reg, val); + iowrite16(val, card->iobase + reg); +} + +static inline void if_cs_write16_rep( + struct if_cs_card *card, + uint reg, + const void *buf, + unsigned long count) +{ + if (debug_output) + printk(KERN_INFO "outsw %08x>(0x%lx words)\n", + reg, count); + iowrite16_rep(card->iobase + reg, buf, count); +} + + +/* + * I know that polling/delaying is frowned upon. However, this procedure + * with polling is needed while downloading the firmware. At this stage, + * the hardware does unfortunately not create any interrupts. + * + * Fortunately, this function is never used once the firmware is in + * the card. :-) + * + * As a reference, see the "Firmware Specification v5.1", page 18 + * and 19. I did not follow their suggested timing to the word, + * but this works nice & fast anyway. + */ +static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 reg) +{ + int i; + + for (i = 0; i < 100000; i++) { + u8 val = if_cs_read8(card, addr); + if (val == reg) + return 0; + udelay(5); + } + return -ETIME; +} + + + +/* + * First the bitmasks for the host/card interrupt/status registers: + */ +#define IF_CS_BIT_TX 0x0001 +#define IF_CS_BIT_RX 0x0002 +#define IF_CS_BIT_COMMAND 0x0004 +#define IF_CS_BIT_RESP 0x0008 +#define IF_CS_BIT_EVENT 0x0010 +#define IF_CS_BIT_MASK 0x001f + + + +/* + * It's not really clear to me what the host status register is for. It + * needs to be set almost in union with "host int cause". The following + * bits from above are used: + * + * IF_CS_BIT_TX driver downloaded a data packet + * IF_CS_BIT_RX driver got a data packet + * IF_CS_BIT_COMMAND driver downloaded a command + * IF_CS_BIT_RESP not used (has some meaning with powerdown) + * IF_CS_BIT_EVENT driver read a host event + */ +#define IF_CS_HOST_STATUS 0x00000000 + +/* + * With the host int cause register can the host (that is, Linux) cause + * an interrupt in the firmware, to tell the firmware about those events: + * + * IF_CS_BIT_TX a data packet has been downloaded + * IF_CS_BIT_RX a received data packet has retrieved + * IF_CS_BIT_COMMAND a firmware block or a command has been downloaded + * IF_CS_BIT_RESP not used (has some meaning with powerdown) + * IF_CS_BIT_EVENT a host event (link lost etc) has been retrieved + */ +#define IF_CS_HOST_INT_CAUSE 0x00000002 + +/* + * The host int mask register is used to enable/disable interrupt. However, + * I have the suspicion that disabled interrupts are lost. + */ +#define IF_CS_HOST_INT_MASK 0x00000004 + +/* + * Used to send or receive data packets: + */ +#define IF_CS_WRITE 0x00000016 +#define IF_CS_WRITE_LEN 0x00000014 +#define IF_CS_READ 0x00000010 +#define IF_CS_READ_LEN 0x00000024 + +/* + * Used to send commands (and to send firmware block) and to + * receive command responses: + */ +#define IF_CS_CMD 0x0000001A +#define IF_CS_CMD_LEN 0x00000018 +#define IF_CS_RESP 0x00000012 +#define IF_CS_RESP_LEN 0x00000030 + +/* + * The card status registers shows what the card/firmware actually + * accepts: + * + * IF_CS_BIT_TX you may send a data packet + * IF_CS_BIT_RX you may retrieve a data packet + * IF_CS_BIT_COMMAND you may send a command + * IF_CS_BIT_RESP you may retrieve a command response + * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) + * + * When reading this register several times, you will get back the same + * results --- with one exception: the IF_CS_BIT_EVENT clear itself + * automatically. + * + * Not that we don't rely on BIT_RX,_BIT_RESP or BIT_EVENT because + * we handle this via the card int cause register. + */ +#define IF_CS_CARD_STATUS 0x00000020 +#define IF_CS_CARD_STATUS_MASK 0x7f00 + +/* + * The card int cause register is used by the card/firmware to notify us + * about the following events: + * + * IF_CS_BIT_TX a data packet has successfully been sentx + * IF_CS_BIT_RX a data packet has been received and can be retrieved + * IF_CS_BIT_COMMAND not used + * IF_CS_BIT_RESP the firmware has a command response for us + * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) + */ +#define IF_CS_CARD_INT_CAUSE 0x00000022 + +/* + * This is used to for handshaking with the card's bootloader/helper image + * to synchronize downloading of firmware blocks. + */ +#define IF_CS_SQ_READ_LOW 0x00000028 +#define IF_CS_SQ_HELPER_OK 0x10 + +/* + * The scratch register tells us ... + * + * IF_CS_SCRATCH_BOOT_OK the bootloader runs + * IF_CS_SCRATCH_HELPER_OK the helper firmware already runs + */ +#define IF_CS_SCRATCH 0x0000003F +#define IF_CS_SCRATCH_BOOT_OK 0x00 +#define IF_CS_SCRATCH_HELPER_OK 0x5a + +/* + * Used to detect ancient chips: + */ +#define IF_CS_PRODUCT_ID 0x0000001C +#define IF_CS_CF8385_B1_REV 0x12 +#define IF_CS_CF8381_B3_REV 0x04 +#define IF_CS_CF8305_B1_REV 0x03 + +/* + * Used to detect other cards than CF8385 since their revisions of silicon + * doesn't match those from CF8385, eg. CF8381 B3 works with this driver. + */ +#define CF8305_MANFID 0x02db +#define CF8305_CARDID 0x8103 +#define CF8381_MANFID 0x02db +#define CF8381_CARDID 0x6064 +#define CF8385_MANFID 0x02df +#define CF8385_CARDID 0x8103 + +/* + * FIXME: just use the 'driver_info' field of 'struct pcmcia_device_id' when + * that gets fixed. Currently there's no way to access it from the probe hook. + */ +static inline u32 get_model(u16 manf_id, u16 card_id) +{ + /* NOTE: keep in sync with if_cs_ids */ + if (manf_id == CF8305_MANFID && card_id == CF8305_CARDID) + return MODEL_8305; + else if (manf_id == CF8381_MANFID && card_id == CF8381_CARDID) + return MODEL_8381; + else if (manf_id == CF8385_MANFID && card_id == CF8385_CARDID) + return MODEL_8385; + return MODEL_UNKNOWN; +} + +/********************************************************************/ +/* I/O and interrupt handling */ +/********************************************************************/ + +static inline void if_cs_enable_ints(struct if_cs_card *card) +{ + lbs_deb_enter(LBS_DEB_CS); + if_cs_write16(card, IF_CS_HOST_INT_MASK, 0); +} + +static inline void if_cs_disable_ints(struct if_cs_card *card) +{ + lbs_deb_enter(LBS_DEB_CS); + if_cs_write16(card, IF_CS_HOST_INT_MASK, IF_CS_BIT_MASK); +} + +/* + * Called from if_cs_host_to_card to send a command to the hardware + */ +static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb) +{ + struct if_cs_card *card = (struct if_cs_card *)priv->card; + int ret = -1; + int loops = 0; + + lbs_deb_enter(LBS_DEB_CS); + if_cs_disable_ints(card); + + /* Is hardware ready? */ + while (1) { + u16 status = if_cs_read16(card, IF_CS_CARD_STATUS); + if (status & IF_CS_BIT_COMMAND) + break; + if (++loops > 100) { + netdev_err(priv->dev, "card not ready for commands\n"); + goto done; + } + mdelay(1); + } + + if_cs_write16(card, IF_CS_CMD_LEN, nb); + + if_cs_write16_rep(card, IF_CS_CMD, buf, nb / 2); + /* Are we supposed to transfer an odd amount of bytes? */ + if (nb & 1) + if_cs_write8(card, IF_CS_CMD, buf[nb-1]); + + /* "Assert the download over interrupt command in the Host + * status register" */ + if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); + + /* "Assert the download over interrupt command in the Card + * interrupt case register" */ + if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); + ret = 0; + +done: + if_cs_enable_ints(card); + lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); + return ret; +} + +/* + * Called from if_cs_host_to_card to send a data to the hardware + */ +static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb) +{ + struct if_cs_card *card = (struct if_cs_card *)priv->card; + u16 status; + + lbs_deb_enter(LBS_DEB_CS); + if_cs_disable_ints(card); + + status = if_cs_read16(card, IF_CS_CARD_STATUS); + BUG_ON((status & IF_CS_BIT_TX) == 0); + + if_cs_write16(card, IF_CS_WRITE_LEN, nb); + + /* write even number of bytes, then odd byte if necessary */ + if_cs_write16_rep(card, IF_CS_WRITE, buf, nb / 2); + if (nb & 1) + if_cs_write8(card, IF_CS_WRITE, buf[nb-1]); + + if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_TX); + if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_TX); + if_cs_enable_ints(card); + + lbs_deb_leave(LBS_DEB_CS); +} + +/* + * Get the command result out of the card. + */ +static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len) +{ + unsigned long flags; + int ret = -1; + u16 status; + + lbs_deb_enter(LBS_DEB_CS); + + /* is hardware ready? */ + status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); + if ((status & IF_CS_BIT_RESP) == 0) { + netdev_err(priv->dev, "no cmd response in card\n"); + *len = 0; + goto out; + } + + *len = if_cs_read16(priv->card, IF_CS_RESP_LEN); + if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) { + netdev_err(priv->dev, + "card cmd buffer has invalid # of bytes (%d)\n", + *len); + goto out; + } + + /* read even number of bytes, then odd byte if necessary */ + if_cs_read16_rep(priv->card, IF_CS_RESP, data, *len/sizeof(u16)); + if (*len & 1) + data[*len-1] = if_cs_read8(priv->card, IF_CS_RESP); + + /* This is a workaround for a firmware that reports too much + * bytes */ + *len -= 8; + ret = 0; + + /* Clear this flag again */ + spin_lock_irqsave(&priv->driver_lock, flags); + priv->dnld_sent = DNLD_RES_RECEIVED; + spin_unlock_irqrestore(&priv->driver_lock, flags); + +out: + lbs_deb_leave_args(LBS_DEB_CS, "ret %d, len %d", ret, *len); + return ret; +} + +static struct sk_buff *if_cs_receive_data(struct lbs_private *priv) +{ + struct sk_buff *skb = NULL; + u16 len; + u8 *data; + + lbs_deb_enter(LBS_DEB_CS); + + len = if_cs_read16(priv->card, IF_CS_READ_LEN); + if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { + netdev_err(priv->dev, + "card data buffer has invalid # of bytes (%d)\n", + len); + priv->dev->stats.rx_dropped++; + goto dat_err; + } + + skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2); + if (!skb) + goto out; + skb_put(skb, len); + skb_reserve(skb, 2);/* 16 byte align */ + data = skb->data; + + /* read even number of bytes, then odd byte if necessary */ + if_cs_read16_rep(priv->card, IF_CS_READ, data, len/sizeof(u16)); + if (len & 1) + data[len-1] = if_cs_read8(priv->card, IF_CS_READ); + +dat_err: + if_cs_write16(priv->card, IF_CS_HOST_STATUS, IF_CS_BIT_RX); + if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_RX); + +out: + lbs_deb_leave_args(LBS_DEB_CS, "ret %p", skb); + return skb; +} + +static irqreturn_t if_cs_interrupt(int irq, void *data) +{ + struct if_cs_card *card = data; + struct lbs_private *priv = card->priv; + u16 cause; + + lbs_deb_enter(LBS_DEB_CS); + + /* Ask card interrupt cause register if there is something for us */ + cause = if_cs_read16(card, IF_CS_CARD_INT_CAUSE); + lbs_deb_cs("cause 0x%04x\n", cause); + + if (cause == 0) { + /* Not for us */ + return IRQ_NONE; + } + + if (cause == 0xffff) { + /* Read in junk, the card has probably been removed */ + card->priv->surpriseremoved = 1; + return IRQ_HANDLED; + } + + if (cause & IF_CS_BIT_RX) { + struct sk_buff *skb; + lbs_deb_cs("rx packet\n"); + skb = if_cs_receive_data(priv); + if (skb) + lbs_process_rxed_packet(priv, skb); + } + + if (cause & IF_CS_BIT_TX) { + lbs_deb_cs("tx done\n"); + lbs_host_to_card_done(priv); + } + + if (cause & IF_CS_BIT_RESP) { + unsigned long flags; + u8 i; + + lbs_deb_cs("cmd resp\n"); + spin_lock_irqsave(&priv->driver_lock, flags); + i = (priv->resp_idx == 0) ? 1 : 0; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + BUG_ON(priv->resp_len[i]); + if_cs_receive_cmdres(priv, priv->resp_buf[i], + &priv->resp_len[i]); + + spin_lock_irqsave(&priv->driver_lock, flags); + lbs_notify_command_response(priv, i); + spin_unlock_irqrestore(&priv->driver_lock, flags); + } + + if (cause & IF_CS_BIT_EVENT) { + u16 status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); + if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, + IF_CS_BIT_EVENT); + lbs_queue_event(priv, (status & IF_CS_CARD_STATUS_MASK) >> 8); + } + + /* Clear interrupt cause */ + if_cs_write16(card, IF_CS_CARD_INT_CAUSE, cause & IF_CS_BIT_MASK); + + lbs_deb_leave(LBS_DEB_CS); + return IRQ_HANDLED; +} + + + + +/********************************************************************/ +/* Firmware */ +/********************************************************************/ + +/* + * Tries to program the helper firmware. + * + * Return 0 on success + */ +static int if_cs_prog_helper(struct if_cs_card *card, const struct firmware *fw) +{ + int ret = 0; + int sent = 0; + u8 scratch; + + lbs_deb_enter(LBS_DEB_CS); + + /* + * This is the only place where an unaligned register access happens on + * the CF8305 card, therefore for the sake of speed of the driver, we do + * the alignment correction here. + */ + if (card->align_regs) + scratch = if_cs_read16(card, IF_CS_SCRATCH) >> 8; + else + scratch = if_cs_read8(card, IF_CS_SCRATCH); + + /* "If the value is 0x5a, the firmware is already + * downloaded successfully" + */ + if (scratch == IF_CS_SCRATCH_HELPER_OK) + goto done; + + /* "If the value is != 00, it is invalid value of register */ + if (scratch != IF_CS_SCRATCH_BOOT_OK) { + ret = -ENODEV; + goto done; + } + + lbs_deb_cs("helper size %td\n", fw->size); + + /* "Set the 5 bytes of the helper image to 0" */ + /* Not needed, this contains an ARM branch instruction */ + + for (;;) { + /* "the number of bytes to send is 256" */ + int count = 256; + int remain = fw->size - sent; + + if (remain < count) + count = remain; + + /* + * "write the number of bytes to be sent to the I/O Command + * write length register" + */ + if_cs_write16(card, IF_CS_CMD_LEN, count); + + /* "write this to I/O Command port register as 16 bit writes */ + if (count) + if_cs_write16_rep(card, IF_CS_CMD, + &fw->data[sent], + count >> 1); + + /* + * "Assert the download over interrupt command in the Host + * status register" + */ + if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); + + /* + * "Assert the download over interrupt command in the Card + * interrupt case register" + */ + if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); + + /* + * "The host polls the Card Status register ... for 50 ms before + * declaring a failure" + */ + ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, + IF_CS_BIT_COMMAND); + if (ret < 0) { + pr_err("can't download helper at 0x%x, ret %d\n", + sent, ret); + goto done; + } + + if (count == 0) + break; + + sent += count; + } + +done: + lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); + return ret; +} + + +static int if_cs_prog_real(struct if_cs_card *card, const struct firmware *fw) +{ + int ret = 0; + int retry = 0; + int len = 0; + int sent; + + lbs_deb_enter(LBS_DEB_CS); + + lbs_deb_cs("fw size %td\n", fw->size); + + ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW, + IF_CS_SQ_HELPER_OK); + if (ret < 0) { + pr_err("helper firmware doesn't answer\n"); + goto done; + } + + for (sent = 0; sent < fw->size; sent += len) { + len = if_cs_read16(card, IF_CS_SQ_READ_LOW); + if (len & 1) { + retry++; + pr_info("odd, need to retry this firmware block\n"); + } else { + retry = 0; + } + + if (retry > 20) { + pr_err("could not download firmware\n"); + ret = -ENODEV; + goto done; + } + if (retry) { + sent -= len; + } + + + if_cs_write16(card, IF_CS_CMD_LEN, len); + + if_cs_write16_rep(card, IF_CS_CMD, + &fw->data[sent], + (len+1) >> 1); + if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); + if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); + + ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, + IF_CS_BIT_COMMAND); + if (ret < 0) { + pr_err("can't download firmware at 0x%x\n", sent); + goto done; + } + } + + ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a); + if (ret < 0) + pr_err("firmware download failed\n"); + +done: + lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); + return ret; +} + +static void if_cs_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *helper, + const struct firmware *mainfw) +{ + struct if_cs_card *card = priv->card; + + if (ret) { + pr_err("failed to find firmware (%d)\n", ret); + return; + } + + /* Load the firmware */ + ret = if_cs_prog_helper(card, helper); + if (ret == 0 && (card->model != MODEL_8305)) + ret = if_cs_prog_real(card, mainfw); + if (ret) + return; + + /* Now actually get the IRQ */ + ret = request_irq(card->p_dev->irq, if_cs_interrupt, + IRQF_SHARED, DRV_NAME, card); + if (ret) { + pr_err("error in request_irq\n"); + return; + } + + /* + * Clear any interrupt cause that happened while sending + * firmware/initializing card + */ + if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK); + if_cs_enable_ints(card); + + /* And finally bring the card up */ + priv->fw_ready = 1; + if (lbs_start_card(priv) != 0) { + pr_err("could not activate card\n"); + free_irq(card->p_dev->irq, card); + } +} + + +/********************************************************************/ +/* Callback functions for libertas.ko */ +/********************************************************************/ + +/* Send commands or data packets to the card */ +static int if_cs_host_to_card(struct lbs_private *priv, + u8 type, + u8 *buf, + u16 nb) +{ + int ret = -1; + + lbs_deb_enter_args(LBS_DEB_CS, "type %d, bytes %d", type, nb); + + switch (type) { + case MVMS_DAT: + priv->dnld_sent = DNLD_DATA_SENT; + if_cs_send_data(priv, buf, nb); + ret = 0; + break; + case MVMS_CMD: + priv->dnld_sent = DNLD_CMD_SENT; + ret = if_cs_send_cmd(priv, buf, nb); + break; + default: + netdev_err(priv->dev, "%s: unsupported type %d\n", + __func__, type); + } + + lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); + return ret; +} + + +static void if_cs_release(struct pcmcia_device *p_dev) +{ + struct if_cs_card *card = p_dev->priv; + + lbs_deb_enter(LBS_DEB_CS); + + free_irq(p_dev->irq, card); + pcmcia_disable_device(p_dev); + if (card->iobase) + ioport_unmap(card->iobase); + + lbs_deb_leave(LBS_DEB_CS); +} + + +static int if_cs_ioprobe(struct pcmcia_device *p_dev, void *priv_data) +{ + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + + if (p_dev->resource[1]->end) { + pr_err("wrong CIS (check number of IO windows)\n"); + return -ENODEV; + } + + /* This reserves IO space but doesn't actually enable it */ + return pcmcia_request_io(p_dev); +} + +static int if_cs_probe(struct pcmcia_device *p_dev) +{ + int ret = -ENOMEM; + unsigned int prod_id; + struct lbs_private *priv; + struct if_cs_card *card; + + lbs_deb_enter(LBS_DEB_CS); + + card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL); + if (!card) + goto out; + + card->p_dev = p_dev; + p_dev->priv = card; + + p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + + if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) { + pr_err("error in pcmcia_loop_config\n"); + goto out1; + } + + /* + * Allocate an interrupt line. Note that this does not assign + * a handler to the interrupt, unless the 'Handler' member of + * the irq structure is initialized. + */ + if (!p_dev->irq) + goto out1; + + /* Initialize io access */ + card->iobase = ioport_map(p_dev->resource[0]->start, + resource_size(p_dev->resource[0])); + if (!card->iobase) { + pr_err("error in ioport_map\n"); + ret = -EIO; + goto out1; + } + + ret = pcmcia_enable_device(p_dev); + if (ret) { + pr_err("error in pcmcia_enable_device\n"); + goto out2; + } + + /* Finally, report what we've done */ + lbs_deb_cs("irq %d, io %pR", p_dev->irq, p_dev->resource[0]); + + /* + * Most of the libertas cards can do unaligned register access, but some + * weird ones cannot. That's especially true for the CF8305 card. + */ + card->align_regs = false; + + card->model = get_model(p_dev->manf_id, p_dev->card_id); + if (card->model == MODEL_UNKNOWN) { + pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n", + p_dev->manf_id, p_dev->card_id); + ret = -ENODEV; + goto out2; + } + + /* Check if we have a current silicon */ + prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID); + if (card->model == MODEL_8305) { + card->align_regs = true; + if (prod_id < IF_CS_CF8305_B1_REV) { + pr_err("8305 rev B0 and older are not supported\n"); + ret = -ENODEV; + goto out2; + } + } + + if ((card->model == MODEL_8381) && prod_id < IF_CS_CF8381_B3_REV) { + pr_err("8381 rev B2 and older are not supported\n"); + ret = -ENODEV; + goto out2; + } + + if ((card->model == MODEL_8385) && prod_id < IF_CS_CF8385_B1_REV) { + pr_err("8385 rev B0 and older are not supported\n"); + ret = -ENODEV; + goto out2; + } + + /* Make this card known to the libertas driver */ + priv = lbs_add_card(card, &p_dev->dev); + if (!priv) { + ret = -ENOMEM; + goto out2; + } + + /* Set up fields in lbs_private */ + card->priv = priv; + priv->card = card; + priv->hw_host_to_card = if_cs_host_to_card; + priv->enter_deep_sleep = NULL; + priv->exit_deep_sleep = NULL; + priv->reset_deep_sleep_wakeup = NULL; + + /* Get firmware */ + ret = lbs_get_firmware_async(priv, &p_dev->dev, card->model, fw_table, + if_cs_prog_firmware); + if (ret) { + pr_err("failed to find firmware (%d)\n", ret); + goto out3; + } + + goto out; + +out3: + lbs_remove_card(priv); +out2: + ioport_unmap(card->iobase); +out1: + pcmcia_disable_device(p_dev); +out: + lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); + return ret; +} + + +static void if_cs_detach(struct pcmcia_device *p_dev) +{ + struct if_cs_card *card = p_dev->priv; + + lbs_deb_enter(LBS_DEB_CS); + + lbs_stop_card(card->priv); + lbs_remove_card(card->priv); + if_cs_disable_ints(card); + if_cs_release(p_dev); + kfree(card); + + lbs_deb_leave(LBS_DEB_CS); +} + + + +/********************************************************************/ +/* Module initialization */ +/********************************************************************/ + +static const struct pcmcia_device_id if_cs_ids[] = { + PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID), + PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID), + PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID), + /* NOTE: keep in sync with get_model() */ + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); + +static struct pcmcia_driver lbs_driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .probe = if_cs_probe, + .remove = if_cs_detach, + .id_table = if_cs_ids, +}; +module_pcmcia_driver(lbs_driver); diff --git a/drivers/net/wireless/marvell/libertas/if_sdio.c b/drivers/net/wireless/marvell/libertas/if_sdio.c new file mode 100644 index 000000000000..33ceda296c9c --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_sdio.c @@ -0,0 +1,1453 @@ +/* + * linux/drivers/net/wireless/libertas/if_sdio.c + * + * Copyright 2007-2008 Pierre Ossman + * + * Inspired by if_cs.c, Copyright 2007 Holger Schurig + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This hardware has more or less no CMD53 support, so all registers + * must be accessed using sdio_readb()/sdio_writeb(). + * + * Transfers must be in one transaction or the firmware goes bonkers. + * This means that the transfer must either be small enough to do a + * byte based transfer or it must be padded to a multiple of the + * current block size. + * + * As SDIO is still new to the kernel, it is unfortunately common with + * bugs in the host controllers related to that. One such bug is that + * controllers cannot do transfers that aren't a multiple of 4 bytes. + * If you don't have time to fix the host controller driver, you can + * work around the problem by modifying if_sdio_host_to_card() and + * if_sdio_card_to_host() to pad the data. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "host.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "cmd.h" +#include "if_sdio.h" + +static void if_sdio_interrupt(struct sdio_func *func); + +/* The if_sdio_remove() callback function is called when + * user removes this module from kernel space or ejects + * the card from the slot. The driver handles these 2 cases + * differently for SD8688 combo chip. + * If the user is removing the module, the FUNC_SHUTDOWN + * command for SD8688 is sent to the firmware. + * If the card is removed, there is no need to send this command. + * + * The variable 'user_rmmod' is used to distinguish these two + * scenarios. This flag is initialized as FALSE in case the card + * is removed, and will be set to TRUE for module removal when + * module_exit function is called. + */ +static u8 user_rmmod; + +static const struct sdio_device_id if_sdio_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, + SDIO_DEVICE_ID_MARVELL_LIBERTAS) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, + SDIO_DEVICE_ID_MARVELL_8688WLAN) }, + { /* end: all zeroes */ }, +}; + +MODULE_DEVICE_TABLE(sdio, if_sdio_ids); + +#define MODEL_8385 0x04 +#define MODEL_8686 0x0b +#define MODEL_8688 0x10 + +static const struct lbs_fw_table fw_table[] = { + { MODEL_8385, "libertas/sd8385_helper.bin", "libertas/sd8385.bin" }, + { MODEL_8385, "sd8385_helper.bin", "sd8385.bin" }, + { MODEL_8686, "libertas/sd8686_v9_helper.bin", "libertas/sd8686_v9.bin" }, + { MODEL_8686, "libertas/sd8686_v8_helper.bin", "libertas/sd8686_v8.bin" }, + { MODEL_8686, "sd8686_helper.bin", "sd8686.bin" }, + { MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" }, + { MODEL_8688, "sd8688_helper.bin", "sd8688.bin" }, + { 0, NULL, NULL } +}; +MODULE_FIRMWARE("libertas/sd8385_helper.bin"); +MODULE_FIRMWARE("libertas/sd8385.bin"); +MODULE_FIRMWARE("sd8385_helper.bin"); +MODULE_FIRMWARE("sd8385.bin"); +MODULE_FIRMWARE("libertas/sd8686_v9_helper.bin"); +MODULE_FIRMWARE("libertas/sd8686_v9.bin"); +MODULE_FIRMWARE("libertas/sd8686_v8_helper.bin"); +MODULE_FIRMWARE("libertas/sd8686_v8.bin"); +MODULE_FIRMWARE("sd8686_helper.bin"); +MODULE_FIRMWARE("sd8686.bin"); +MODULE_FIRMWARE("libertas/sd8688_helper.bin"); +MODULE_FIRMWARE("libertas/sd8688.bin"); +MODULE_FIRMWARE("sd8688_helper.bin"); +MODULE_FIRMWARE("sd8688.bin"); + +struct if_sdio_packet { + struct if_sdio_packet *next; + u16 nb; + u8 buffer[0] __attribute__((aligned(4))); +}; + +struct if_sdio_card { + struct sdio_func *func; + struct lbs_private *priv; + + int model; + unsigned long ioport; + unsigned int scratch_reg; + bool started; + wait_queue_head_t pwron_waitq; + + u8 buffer[65536] __attribute__((aligned(4))); + + spinlock_t lock; + struct if_sdio_packet *packets; + + struct workqueue_struct *workqueue; + struct work_struct packet_worker; + + u8 rx_unit; +}; + +static void if_sdio_finish_power_on(struct if_sdio_card *card); +static int if_sdio_power_off(struct if_sdio_card *card); + +/********************************************************************/ +/* I/O */ +/********************************************************************/ + +/* + * For SD8385/SD8686, this function reads firmware status after + * the image is downloaded, or reads RX packet length when + * interrupt (with IF_SDIO_H_INT_UPLD bit set) is received. + * For SD8688, this function reads firmware status only. + */ +static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err) +{ + int ret; + u16 scratch; + + scratch = sdio_readb(card->func, card->scratch_reg, &ret); + if (!ret) + scratch |= sdio_readb(card->func, card->scratch_reg + 1, + &ret) << 8; + + if (err) + *err = ret; + + if (ret) + return 0xffff; + + return scratch; +} + +static u8 if_sdio_read_rx_unit(struct if_sdio_card *card) +{ + int ret; + u8 rx_unit; + + rx_unit = sdio_readb(card->func, IF_SDIO_RX_UNIT, &ret); + + if (ret) + rx_unit = 0; + + return rx_unit; +} + +static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err) +{ + int ret; + u16 rx_len; + + switch (card->model) { + case MODEL_8385: + case MODEL_8686: + rx_len = if_sdio_read_scratch(card, &ret); + break; + case MODEL_8688: + default: /* for newer chipsets */ + rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret); + if (!ret) + rx_len <<= card->rx_unit; + else + rx_len = 0xffff; /* invalid length */ + + break; + } + + if (err) + *err = ret; + + return rx_len; +} + +static int if_sdio_handle_cmd(struct if_sdio_card *card, + u8 *buffer, unsigned size) +{ + struct lbs_private *priv = card->priv; + int ret; + unsigned long flags; + u8 i; + + lbs_deb_enter(LBS_DEB_SDIO); + + if (size > LBS_CMD_BUFFER_SIZE) { + lbs_deb_sdio("response packet too large (%d bytes)\n", + (int)size); + ret = -E2BIG; + goto out; + } + + spin_lock_irqsave(&priv->driver_lock, flags); + + i = (priv->resp_idx == 0) ? 1 : 0; + BUG_ON(priv->resp_len[i]); + priv->resp_len[i] = size; + memcpy(priv->resp_buf[i], buffer, size); + lbs_notify_command_response(priv, i); + + spin_unlock_irqrestore(&card->priv->driver_lock, flags); + + ret = 0; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +static int if_sdio_handle_data(struct if_sdio_card *card, + u8 *buffer, unsigned size) +{ + int ret; + struct sk_buff *skb; + char *data; + + lbs_deb_enter(LBS_DEB_SDIO); + + if (size > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { + lbs_deb_sdio("response packet too large (%d bytes)\n", + (int)size); + ret = -E2BIG; + goto out; + } + + skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + NET_IP_ALIGN); + if (!skb) { + ret = -ENOMEM; + goto out; + } + + skb_reserve(skb, NET_IP_ALIGN); + + data = skb_put(skb, size); + + memcpy(data, buffer, size); + + lbs_process_rxed_packet(card->priv, skb); + + ret = 0; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; +} + +static int if_sdio_handle_event(struct if_sdio_card *card, + u8 *buffer, unsigned size) +{ + int ret; + u32 event; + + lbs_deb_enter(LBS_DEB_SDIO); + + if (card->model == MODEL_8385) { + event = sdio_readb(card->func, IF_SDIO_EVENT, &ret); + if (ret) + goto out; + + /* right shift 3 bits to get the event id */ + event >>= 3; + } else { + if (size < 4) { + lbs_deb_sdio("event packet too small (%d bytes)\n", + (int)size); + ret = -EINVAL; + goto out; + } + event = buffer[3] << 24; + event |= buffer[2] << 16; + event |= buffer[1] << 8; + event |= buffer[0] << 0; + } + + lbs_queue_event(card->priv, event & 0xFF); + ret = 0; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; +} + +static int if_sdio_wait_status(struct if_sdio_card *card, const u8 condition) +{ + u8 status; + unsigned long timeout; + int ret = 0; + + timeout = jiffies + HZ; + while (1) { + status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); + if (ret) + return ret; + if ((status & condition) == condition) + break; + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + mdelay(1); + } + return ret; +} + +static int if_sdio_card_to_host(struct if_sdio_card *card) +{ + int ret; + u16 size, type, chunk; + + lbs_deb_enter(LBS_DEB_SDIO); + + size = if_sdio_read_rx_len(card, &ret); + if (ret) + goto out; + + if (size < 4) { + lbs_deb_sdio("invalid packet size (%d bytes) from firmware\n", + (int)size); + ret = -EINVAL; + goto out; + } + + ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); + if (ret) + goto out; + + /* + * The transfer must be in one transaction or the firmware + * goes suicidal. There's no way to guarantee that for all + * controllers, but we can at least try. + */ + chunk = sdio_align_size(card->func, size); + + ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk); + if (ret) + goto out; + + chunk = card->buffer[0] | (card->buffer[1] << 8); + type = card->buffer[2] | (card->buffer[3] << 8); + + lbs_deb_sdio("packet of type %d and size %d bytes\n", + (int)type, (int)chunk); + + if (chunk > size) { + lbs_deb_sdio("packet fragment (%d > %d)\n", + (int)chunk, (int)size); + ret = -EINVAL; + goto out; + } + + if (chunk < size) { + lbs_deb_sdio("packet fragment (%d < %d)\n", + (int)chunk, (int)size); + } + + switch (type) { + case MVMS_CMD: + ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4); + if (ret) + goto out; + break; + case MVMS_DAT: + ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4); + if (ret) + goto out; + break; + case MVMS_EVENT: + ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4); + if (ret) + goto out; + break; + default: + lbs_deb_sdio("invalid type (%d) from firmware\n", + (int)type); + ret = -EINVAL; + goto out; + } + +out: + if (ret) + pr_err("problem fetching packet from firmware\n"); + + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; +} + +static void if_sdio_host_to_card_worker(struct work_struct *work) +{ + struct if_sdio_card *card; + struct if_sdio_packet *packet; + int ret; + unsigned long flags; + + lbs_deb_enter(LBS_DEB_SDIO); + + card = container_of(work, struct if_sdio_card, packet_worker); + + while (1) { + spin_lock_irqsave(&card->lock, flags); + packet = card->packets; + if (packet) + card->packets = packet->next; + spin_unlock_irqrestore(&card->lock, flags); + + if (!packet) + break; + + sdio_claim_host(card->func); + + ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); + if (ret == 0) { + ret = sdio_writesb(card->func, card->ioport, + packet->buffer, packet->nb); + } + + if (ret) + pr_err("error %d sending packet to firmware\n", ret); + + sdio_release_host(card->func); + + kfree(packet); + } + + lbs_deb_leave(LBS_DEB_SDIO); +} + +/********************************************************************/ +/* Firmware */ +/********************************************************************/ + +#define FW_DL_READY_STATUS (IF_SDIO_IO_RDY | IF_SDIO_DL_RDY) + +static int if_sdio_prog_helper(struct if_sdio_card *card, + const struct firmware *fw) +{ + int ret; + unsigned long timeout; + u8 *chunk_buffer; + u32 chunk_size; + const u8 *firmware; + size_t size; + + lbs_deb_enter(LBS_DEB_SDIO); + + chunk_buffer = kzalloc(64, GFP_KERNEL); + if (!chunk_buffer) { + ret = -ENOMEM; + goto out; + } + + sdio_claim_host(card->func); + + ret = sdio_set_block_size(card->func, 32); + if (ret) + goto release; + + firmware = fw->data; + size = fw->size; + + while (size) { + ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); + if (ret) + goto release; + + /* On some platforms (like Davinci) the chip needs more time + * between helper blocks. + */ + mdelay(2); + + chunk_size = min_t(size_t, size, 60); + + *((__le32*)chunk_buffer) = cpu_to_le32(chunk_size); + memcpy(chunk_buffer + 4, firmware, chunk_size); +/* + lbs_deb_sdio("sending %d bytes chunk\n", chunk_size); +*/ + ret = sdio_writesb(card->func, card->ioport, + chunk_buffer, 64); + if (ret) + goto release; + + firmware += chunk_size; + size -= chunk_size; + } + + /* an empty block marks the end of the transfer */ + memset(chunk_buffer, 0, 4); + ret = sdio_writesb(card->func, card->ioport, chunk_buffer, 64); + if (ret) + goto release; + + lbs_deb_sdio("waiting for helper to boot...\n"); + + /* wait for the helper to boot by looking at the size register */ + timeout = jiffies + HZ; + while (1) { + u16 req_size; + + req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret); + if (ret) + goto release; + + req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8; + if (ret) + goto release; + + if (req_size != 0) + break; + + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + goto release; + } + + msleep(10); + } + + ret = 0; + +release: + sdio_release_host(card->func); + kfree(chunk_buffer); + +out: + if (ret) + pr_err("failed to load helper firmware\n"); + + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +static int if_sdio_prog_real(struct if_sdio_card *card, + const struct firmware *fw) +{ + int ret; + unsigned long timeout; + u8 *chunk_buffer; + u32 chunk_size; + const u8 *firmware; + size_t size, req_size; + + lbs_deb_enter(LBS_DEB_SDIO); + + chunk_buffer = kzalloc(512, GFP_KERNEL); + if (!chunk_buffer) { + ret = -ENOMEM; + goto out; + } + + sdio_claim_host(card->func); + + ret = sdio_set_block_size(card->func, 32); + if (ret) + goto release; + + firmware = fw->data; + size = fw->size; + + while (size) { + timeout = jiffies + HZ; + while (1) { + ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); + if (ret) + goto release; + + req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, + &ret); + if (ret) + goto release; + + req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, + &ret) << 8; + if (ret) + goto release; + + /* + * For SD8688 wait until the length is not 0, 1 or 2 + * before downloading the first FW block, + * since BOOT code writes the register to indicate the + * helper/FW download winner, + * the value could be 1 or 2 (Func1 or Func2). + */ + if ((size != fw->size) || (req_size > 2)) + break; + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + goto release; + } + mdelay(1); + } + +/* + lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size); +*/ + if (req_size == 0) { + lbs_deb_sdio("firmware helper gave up early\n"); + ret = -EIO; + goto release; + } + + if (req_size & 0x01) { + lbs_deb_sdio("firmware helper signalled error\n"); + ret = -EIO; + goto release; + } + + if (req_size > size) + req_size = size; + + while (req_size) { + chunk_size = min_t(size_t, req_size, 512); + + memcpy(chunk_buffer, firmware, chunk_size); +/* + lbs_deb_sdio("sending %d bytes (%d bytes) chunk\n", + chunk_size, (chunk_size + 31) / 32 * 32); +*/ + ret = sdio_writesb(card->func, card->ioport, + chunk_buffer, roundup(chunk_size, 32)); + if (ret) + goto release; + + firmware += chunk_size; + size -= chunk_size; + req_size -= chunk_size; + } + } + + ret = 0; + + lbs_deb_sdio("waiting for firmware to boot...\n"); + + /* wait for the firmware to boot */ + timeout = jiffies + HZ; + while (1) { + u16 scratch; + + scratch = if_sdio_read_scratch(card, &ret); + if (ret) + goto release; + + if (scratch == IF_SDIO_FIRMWARE_OK) + break; + + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + goto release; + } + + msleep(10); + } + + ret = 0; + +release: + sdio_release_host(card->func); + kfree(chunk_buffer); + +out: + if (ret) + pr_err("failed to load firmware\n"); + + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *helper, + const struct firmware *mainfw) +{ + struct if_sdio_card *card = priv->card; + + if (ret) { + pr_err("failed to find firmware (%d)\n", ret); + return; + } + + ret = if_sdio_prog_helper(card, helper); + if (ret) + return; + + lbs_deb_sdio("Helper firmware loaded\n"); + + ret = if_sdio_prog_real(card, mainfw); + if (ret) + return; + + lbs_deb_sdio("Firmware loaded\n"); + if_sdio_finish_power_on(card); +} + +static int if_sdio_prog_firmware(struct if_sdio_card *card) +{ + int ret; + u16 scratch; + + lbs_deb_enter(LBS_DEB_SDIO); + + /* + * Disable interrupts + */ + sdio_claim_host(card->func); + sdio_writeb(card->func, 0x00, IF_SDIO_H_INT_MASK, &ret); + sdio_release_host(card->func); + + sdio_claim_host(card->func); + scratch = if_sdio_read_scratch(card, &ret); + sdio_release_host(card->func); + + lbs_deb_sdio("firmware status = %#x\n", scratch); + lbs_deb_sdio("scratch ret = %d\n", ret); + + if (ret) + goto out; + + + /* + * The manual clearly describes that FEDC is the right code to use + * to detect firmware presence, but for SD8686 it is not that simple. + * Scratch is also used to store the RX packet length, so we lose + * the FEDC value early on. So we use a non-zero check in order + * to validate firmware presence. + * Additionally, the SD8686 in the Gumstix always has the high scratch + * bit set, even when the firmware is not loaded. So we have to + * exclude that from the test. + */ + if (scratch == IF_SDIO_FIRMWARE_OK) { + lbs_deb_sdio("firmware already loaded\n"); + if_sdio_finish_power_on(card); + return 0; + } else if ((card->model == MODEL_8686) && (scratch & 0x7fff)) { + lbs_deb_sdio("firmware may be running\n"); + if_sdio_finish_power_on(card); + return 0; + } + + ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model, + fw_table, if_sdio_do_prog_firmware); + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +/********************************************************************/ +/* Power management */ +/********************************************************************/ + +/* Finish power on sequence (after firmware is loaded) */ +static void if_sdio_finish_power_on(struct if_sdio_card *card) +{ + struct sdio_func *func = card->func; + struct lbs_private *priv = card->priv; + int ret; + + sdio_claim_host(func); + sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); + + /* + * Get rx_unit if the chip is SD8688 or newer. + * SD8385 & SD8686 do not have rx_unit. + */ + if ((card->model != MODEL_8385) + && (card->model != MODEL_8686)) + card->rx_unit = if_sdio_read_rx_unit(card); + else + card->rx_unit = 0; + + /* + * Set up the interrupt handler late. + * + * If we set it up earlier, the (buggy) hardware generates a spurious + * interrupt, even before the interrupt has been enabled, with + * CCCR_INTx = 0. + * + * We register the interrupt handler late so that we can handle any + * spurious interrupts, and also to avoid generation of that known + * spurious interrupt in the first place. + */ + ret = sdio_claim_irq(func, if_sdio_interrupt); + if (ret) + goto release; + + /* + * Enable interrupts now that everything is set up + */ + sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); + if (ret) + goto release_irq; + + sdio_release_host(func); + + /* Set fw_ready before queuing any commands so that + * lbs_thread won't block from sending them to firmware. + */ + priv->fw_ready = 1; + + /* + * FUNC_INIT is required for SD8688 WLAN/BT multiple functions + */ + if (card->model == MODEL_8688) { + struct cmd_header cmd; + + memset(&cmd, 0, sizeof(cmd)); + + lbs_deb_sdio("send function INIT command\n"); + if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd), + lbs_cmd_copyback, (unsigned long) &cmd)) + netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n"); + } + + wake_up(&card->pwron_waitq); + + if (!card->started) { + ret = lbs_start_card(priv); + if_sdio_power_off(card); + if (ret == 0) { + card->started = true; + /* Tell PM core that we don't need the card to be + * powered now */ + pm_runtime_put(&func->dev); + } + } + + return; + +release_irq: + sdio_release_irq(func); +release: + sdio_release_host(func); +} + +static int if_sdio_power_on(struct if_sdio_card *card) +{ + struct sdio_func *func = card->func; + struct mmc_host *host = func->card->host; + int ret; + + sdio_claim_host(func); + + ret = sdio_enable_func(func); + if (ret) + goto release; + + /* For 1-bit transfers to the 8686 model, we need to enable the + * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0 + * bit to allow access to non-vendor registers. */ + if ((card->model == MODEL_8686) && + (host->caps & MMC_CAP_SDIO_IRQ) && + (host->ios.bus_width == MMC_BUS_WIDTH_1)) { + u8 reg; + + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; + reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret); + if (ret) + goto disable; + + reg |= SDIO_BUS_ECSI; + sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret); + if (ret) + goto disable; + } + + card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); + if (ret) + goto disable; + + card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8; + if (ret) + goto disable; + + card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16; + if (ret) + goto disable; + + sdio_release_host(func); + ret = if_sdio_prog_firmware(card); + if (ret) { + sdio_claim_host(func); + goto disable; + } + + return 0; + +disable: + sdio_disable_func(func); +release: + sdio_release_host(func); + return ret; +} + +static int if_sdio_power_off(struct if_sdio_card *card) +{ + struct sdio_func *func = card->func; + struct lbs_private *priv = card->priv; + + priv->fw_ready = 0; + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_disable_func(func); + sdio_release_host(func); + return 0; +} + + +/*******************************************************************/ +/* Libertas callbacks */ +/*******************************************************************/ + +static int if_sdio_host_to_card(struct lbs_private *priv, + u8 type, u8 *buf, u16 nb) +{ + int ret; + struct if_sdio_card *card; + struct if_sdio_packet *packet, *cur; + u16 size; + unsigned long flags; + + lbs_deb_enter_args(LBS_DEB_SDIO, "type %d, bytes %d", type, nb); + + card = priv->card; + + if (nb > (65536 - sizeof(struct if_sdio_packet) - 4)) { + ret = -EINVAL; + goto out; + } + + /* + * The transfer must be in one transaction or the firmware + * goes suicidal. There's no way to guarantee that for all + * controllers, but we can at least try. + */ + size = sdio_align_size(card->func, nb + 4); + + packet = kzalloc(sizeof(struct if_sdio_packet) + size, + GFP_ATOMIC); + if (!packet) { + ret = -ENOMEM; + goto out; + } + + packet->next = NULL; + packet->nb = size; + + /* + * SDIO specific header. + */ + packet->buffer[0] = (nb + 4) & 0xff; + packet->buffer[1] = ((nb + 4) >> 8) & 0xff; + packet->buffer[2] = type; + packet->buffer[3] = 0; + + memcpy(packet->buffer + 4, buf, nb); + + spin_lock_irqsave(&card->lock, flags); + + if (!card->packets) + card->packets = packet; + else { + cur = card->packets; + while (cur->next) + cur = cur->next; + cur->next = packet; + } + + switch (type) { + case MVMS_CMD: + priv->dnld_sent = DNLD_CMD_SENT; + break; + case MVMS_DAT: + priv->dnld_sent = DNLD_DATA_SENT; + break; + default: + lbs_deb_sdio("unknown packet type %d\n", (int)type); + } + + spin_unlock_irqrestore(&card->lock, flags); + + queue_work(card->workqueue, &card->packet_worker); + + ret = 0; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; +} + +static int if_sdio_enter_deep_sleep(struct lbs_private *priv) +{ + int ret = -1; + struct cmd_header cmd; + + memset(&cmd, 0, sizeof(cmd)); + + lbs_deb_sdio("send DEEP_SLEEP command\n"); + ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd), + lbs_cmd_copyback, (unsigned long) &cmd); + if (ret) + netdev_err(priv->dev, "DEEP_SLEEP cmd failed\n"); + + mdelay(200); + return ret; +} + +static int if_sdio_exit_deep_sleep(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int ret = -1; + + lbs_deb_enter(LBS_DEB_SDIO); + sdio_claim_host(card->func); + + sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); + if (ret) + netdev_err(priv->dev, "sdio_writeb failed!\n"); + + sdio_release_host(card->func); + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int ret = -1; + + lbs_deb_enter(LBS_DEB_SDIO); + sdio_claim_host(card->func); + + sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret); + if (ret) + netdev_err(priv->dev, "sdio_writeb failed!\n"); + + sdio_release_host(card->func); + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; + +} + +static struct mmc_host *reset_host; + +static void if_sdio_reset_card_worker(struct work_struct *work) +{ + /* + * The actual reset operation must be run outside of lbs_thread. This + * is because mmc_remove_host() will cause the device to be instantly + * destroyed, and the libertas driver then needs to end lbs_thread, + * leading to a deadlock. + * + * We run it in a workqueue totally independent from the if_sdio_card + * instance for that reason. + */ + + pr_info("Resetting card..."); + mmc_remove_host(reset_host); + mmc_add_host(reset_host); +} +static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker); + +static void if_sdio_reset_card(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + + if (work_pending(&card_reset_work)) + return; + + reset_host = card->func->card->host; + schedule_work(&card_reset_work); +} + +static int if_sdio_power_save(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int ret; + + flush_workqueue(card->workqueue); + + ret = if_sdio_power_off(card); + + /* Let runtime PM know the card is powered off */ + pm_runtime_put_sync(&card->func->dev); + + return ret; +} + +static int if_sdio_power_restore(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int r; + + /* Make sure the card will not be powered off by runtime PM */ + pm_runtime_get_sync(&card->func->dev); + + r = if_sdio_power_on(card); + if (r) + return r; + + wait_event(card->pwron_waitq, priv->fw_ready); + return 0; +} + + +/*******************************************************************/ +/* SDIO callbacks */ +/*******************************************************************/ + +static void if_sdio_interrupt(struct sdio_func *func) +{ + int ret; + struct if_sdio_card *card; + u8 cause; + + lbs_deb_enter(LBS_DEB_SDIO); + + card = sdio_get_drvdata(func); + + cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret); + if (ret || !cause) + goto out; + + lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause); + + sdio_writeb(card->func, ~cause, IF_SDIO_H_INT_STATUS, &ret); + if (ret) + goto out; + + /* + * Ignore the define name, this really means the card has + * successfully received the command. + */ + card->priv->is_activity_detected = 1; + if (cause & IF_SDIO_H_INT_DNLD) + lbs_host_to_card_done(card->priv); + + + if (cause & IF_SDIO_H_INT_UPLD) { + ret = if_sdio_card_to_host(card); + if (ret) + goto out; + } + + ret = 0; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); +} + +static int if_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct if_sdio_card *card; + struct lbs_private *priv; + int ret, i; + unsigned int model; + struct if_sdio_packet *packet; + + lbs_deb_enter(LBS_DEB_SDIO); + + for (i = 0;i < func->card->num_info;i++) { + if (sscanf(func->card->info[i], + "802.11 SDIO ID: %x", &model) == 1) + break; + if (sscanf(func->card->info[i], + "ID: %x", &model) == 1) + break; + if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) { + model = MODEL_8385; + break; + } + } + + if (i == func->card->num_info) { + pr_err("unable to identify card model\n"); + return -ENODEV; + } + + card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->func = func; + card->model = model; + + switch (card->model) { + case MODEL_8385: + card->scratch_reg = IF_SDIO_SCRATCH_OLD; + break; + case MODEL_8686: + card->scratch_reg = IF_SDIO_SCRATCH; + break; + case MODEL_8688: + default: /* for newer chipsets */ + card->scratch_reg = IF_SDIO_FW_STATUS; + break; + } + + spin_lock_init(&card->lock); + card->workqueue = create_workqueue("libertas_sdio"); + INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); + init_waitqueue_head(&card->pwron_waitq); + + /* Check if we support this card */ + for (i = 0; i < ARRAY_SIZE(fw_table); i++) { + if (card->model == fw_table[i].model) + break; + } + if (i == ARRAY_SIZE(fw_table)) { + pr_err("unknown card model 0x%x\n", card->model); + ret = -ENODEV; + goto free; + } + + sdio_set_drvdata(func, card); + + lbs_deb_sdio("class = 0x%X, vendor = 0x%X, " + "device = 0x%X, model = 0x%X, ioport = 0x%X\n", + func->class, func->vendor, func->device, + model, (unsigned)card->ioport); + + + priv = lbs_add_card(card, &func->dev); + if (!priv) { + ret = -ENOMEM; + goto free; + } + + card->priv = priv; + + priv->card = card; + priv->hw_host_to_card = if_sdio_host_to_card; + priv->enter_deep_sleep = if_sdio_enter_deep_sleep; + priv->exit_deep_sleep = if_sdio_exit_deep_sleep; + priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; + priv->reset_card = if_sdio_reset_card; + priv->power_save = if_sdio_power_save; + priv->power_restore = if_sdio_power_restore; + + ret = if_sdio_power_on(card); + if (ret) + goto err_activate_card; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; + +err_activate_card: + flush_workqueue(card->workqueue); + lbs_remove_card(priv); +free: + destroy_workqueue(card->workqueue); + while (card->packets) { + packet = card->packets; + card->packets = card->packets->next; + kfree(packet); + } + + kfree(card); + + goto out; +} + +static void if_sdio_remove(struct sdio_func *func) +{ + struct if_sdio_card *card; + struct if_sdio_packet *packet; + + lbs_deb_enter(LBS_DEB_SDIO); + + card = sdio_get_drvdata(func); + + /* Undo decrement done above in if_sdio_probe */ + pm_runtime_get_noresume(&func->dev); + + if (user_rmmod && (card->model == MODEL_8688)) { + /* + * FUNC_SHUTDOWN is required for SD8688 WLAN/BT + * multiple functions + */ + struct cmd_header cmd; + + memset(&cmd, 0, sizeof(cmd)); + + lbs_deb_sdio("send function SHUTDOWN command\n"); + if (__lbs_cmd(card->priv, CMD_FUNC_SHUTDOWN, + &cmd, sizeof(cmd), lbs_cmd_copyback, + (unsigned long) &cmd)) + pr_alert("CMD_FUNC_SHUTDOWN cmd failed\n"); + } + + + lbs_deb_sdio("call remove card\n"); + lbs_stop_card(card->priv); + lbs_remove_card(card->priv); + + flush_workqueue(card->workqueue); + destroy_workqueue(card->workqueue); + + while (card->packets) { + packet = card->packets; + card->packets = card->packets->next; + kfree(packet); + } + + kfree(card); + lbs_deb_leave(LBS_DEB_SDIO); +} + +static int if_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + int ret; + struct if_sdio_card *card = sdio_get_drvdata(func); + + mmc_pm_flag_t flags = sdio_get_host_pm_caps(func); + + /* If we're powered off anyway, just let the mmc layer remove the + * card. */ + if (!lbs_iface_active(card->priv)) + return -ENOSYS; + + dev_info(dev, "%s: suspend: PM flags = 0x%x\n", + sdio_func_id(func), flags); + + /* If we aren't being asked to wake on anything, we should bail out + * and let the SD stack power down the card. + */ + if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) { + dev_info(dev, "Suspend without wake params -- powering down card\n"); + return -ENOSYS; + } + + if (!(flags & MMC_PM_KEEP_POWER)) { + dev_err(dev, "%s: cannot remain alive while host is suspended\n", + sdio_func_id(func)); + return -ENOSYS; + } + + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) + return ret; + + ret = lbs_suspend(card->priv); + if (ret) + return ret; + + return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); +} + +static int if_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct if_sdio_card *card = sdio_get_drvdata(func); + int ret; + + dev_info(dev, "%s: resume: we're back\n", sdio_func_id(func)); + + ret = lbs_resume(card->priv); + + return ret; +} + +static const struct dev_pm_ops if_sdio_pm_ops = { + .suspend = if_sdio_suspend, + .resume = if_sdio_resume, +}; + +static struct sdio_driver if_sdio_driver = { + .name = "libertas_sdio", + .id_table = if_sdio_ids, + .probe = if_sdio_probe, + .remove = if_sdio_remove, + .drv = { + .pm = &if_sdio_pm_ops, + }, +}; + +/*******************************************************************/ +/* Module functions */ +/*******************************************************************/ + +static int __init if_sdio_init_module(void) +{ + int ret = 0; + + lbs_deb_enter(LBS_DEB_SDIO); + + printk(KERN_INFO "libertas_sdio: Libertas SDIO driver\n"); + printk(KERN_INFO "libertas_sdio: Copyright Pierre Ossman\n"); + + ret = sdio_register_driver(&if_sdio_driver); + + /* Clear the flag in case user removes the card. */ + user_rmmod = 0; + + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; +} + +static void __exit if_sdio_exit_module(void) +{ + lbs_deb_enter(LBS_DEB_SDIO); + + /* Set the flag as user is removing this module. */ + user_rmmod = 1; + + cancel_work_sync(&card_reset_work); + + sdio_unregister_driver(&if_sdio_driver); + + lbs_deb_leave(LBS_DEB_SDIO); +} + +module_init(if_sdio_init_module); +module_exit(if_sdio_exit_module); + +MODULE_DESCRIPTION("Libertas SDIO WLAN Driver"); +MODULE_AUTHOR("Pierre Ossman"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/marvell/libertas/if_sdio.h b/drivers/net/wireless/marvell/libertas/if_sdio.h new file mode 100644 index 000000000000..62fda3592f67 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_sdio.h @@ -0,0 +1,52 @@ +/* + * linux/drivers/net/wireless/libertas/if_sdio.h + * + * Copyright 2007 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef _LBS_IF_SDIO_H +#define _LBS_IF_SDIO_H + +#define IF_SDIO_IOPORT 0x00 + +#define IF_SDIO_H_INT_MASK 0x04 +#define IF_SDIO_H_INT_OFLOW 0x08 +#define IF_SDIO_H_INT_UFLOW 0x04 +#define IF_SDIO_H_INT_DNLD 0x02 +#define IF_SDIO_H_INT_UPLD 0x01 + +#define IF_SDIO_H_INT_STATUS 0x05 +#define IF_SDIO_H_INT_RSR 0x06 +#define IF_SDIO_H_INT_STATUS2 0x07 + +#define IF_SDIO_RD_BASE 0x10 + +#define IF_SDIO_STATUS 0x20 +#define IF_SDIO_IO_RDY 0x08 +#define IF_SDIO_CIS_RDY 0x04 +#define IF_SDIO_UL_RDY 0x02 +#define IF_SDIO_DL_RDY 0x01 + +#define IF_SDIO_C_INT_MASK 0x24 +#define IF_SDIO_C_INT_STATUS 0x28 +#define IF_SDIO_C_INT_RSR 0x2C + +#define IF_SDIO_SCRATCH 0x34 +#define IF_SDIO_SCRATCH_OLD 0x80fe +#define IF_SDIO_FW_STATUS 0x40 +#define IF_SDIO_FIRMWARE_OK 0xfedc + +#define IF_SDIO_RX_LEN 0x42 +#define IF_SDIO_RX_UNIT 0x43 + +#define IF_SDIO_EVENT 0x80fc + +#define IF_SDIO_BLOCK_SIZE 256 +#define CONFIGURATION_REG 0x03 +#define HOST_POWER_UP (0x1U << 1) +#endif diff --git a/drivers/net/wireless/marvell/libertas/if_spi.c b/drivers/net/wireless/marvell/libertas/if_spi.c new file mode 100644 index 000000000000..82c0796377aa --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_spi.c @@ -0,0 +1,1318 @@ +/* + * linux/drivers/net/wireless/libertas/if_spi.c + * + * Driver for Marvell SPI WLAN cards. + * + * Copyright 2008 Analog Devices Inc. + * + * Authors: + * Andrey Yurovsky + * Colin McCabe + * + * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "host.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "if_spi.h" + +struct if_spi_packet { + struct list_head list; + u16 blen; + u8 buffer[0] __attribute__((aligned(4))); +}; + +struct if_spi_card { + struct spi_device *spi; + struct lbs_private *priv; + struct libertas_spi_platform_data *pdata; + + /* The card ID and card revision, as reported by the hardware. */ + u16 card_id; + u8 card_rev; + + /* The last time that we initiated an SPU operation */ + unsigned long prev_xfer_time; + + int use_dummy_writes; + unsigned long spu_port_delay; + unsigned long spu_reg_delay; + + /* Handles all SPI communication (except for FW load) */ + struct workqueue_struct *workqueue; + struct work_struct packet_work; + struct work_struct resume_work; + + u8 cmd_buffer[IF_SPI_CMD_BUF_SIZE]; + + /* A buffer of incoming packets from libertas core. + * Since we can't sleep in hw_host_to_card, we have to buffer + * them. */ + struct list_head cmd_packet_list; + struct list_head data_packet_list; + + /* Protects cmd_packet_list and data_packet_list */ + spinlock_t buffer_lock; + + /* True is card suspended */ + u8 suspended; +}; + +static void free_if_spi_card(struct if_spi_card *card) +{ + struct list_head *cursor, *next; + struct if_spi_packet *packet; + + list_for_each_safe(cursor, next, &card->cmd_packet_list) { + packet = container_of(cursor, struct if_spi_packet, list); + list_del(&packet->list); + kfree(packet); + } + list_for_each_safe(cursor, next, &card->data_packet_list) { + packet = container_of(cursor, struct if_spi_packet, list); + list_del(&packet->list); + kfree(packet); + } + kfree(card); +} + +#define MODEL_8385 0x04 +#define MODEL_8686 0x0b +#define MODEL_8688 0x10 + +static const struct lbs_fw_table fw_table[] = { + { MODEL_8385, "libertas/gspi8385_helper.bin", "libertas/gspi8385.bin" }, + { MODEL_8385, "libertas/gspi8385_hlp.bin", "libertas/gspi8385.bin" }, + { MODEL_8686, "libertas/gspi8686_v9_helper.bin", "libertas/gspi8686_v9.bin" }, + { MODEL_8686, "libertas/gspi8686_hlp.bin", "libertas/gspi8686.bin" }, + { MODEL_8688, "libertas/gspi8688_helper.bin", "libertas/gspi8688.bin" }, + { 0, NULL, NULL } +}; +MODULE_FIRMWARE("libertas/gspi8385_helper.bin"); +MODULE_FIRMWARE("libertas/gspi8385_hlp.bin"); +MODULE_FIRMWARE("libertas/gspi8385.bin"); +MODULE_FIRMWARE("libertas/gspi8686_v9_helper.bin"); +MODULE_FIRMWARE("libertas/gspi8686_v9.bin"); +MODULE_FIRMWARE("libertas/gspi8686_hlp.bin"); +MODULE_FIRMWARE("libertas/gspi8686.bin"); +MODULE_FIRMWARE("libertas/gspi8688_helper.bin"); +MODULE_FIRMWARE("libertas/gspi8688.bin"); + + +/* + * SPI Interface Unit Routines + * + * The SPU sits between the host and the WLAN module. + * All communication with the firmware is through SPU transactions. + * + * First we have to put a SPU register name on the bus. Then we can + * either read from or write to that register. + * + */ + +static void spu_transaction_init(struct if_spi_card *card) +{ + if (!time_after(jiffies, card->prev_xfer_time + 1)) { + /* Unfortunately, the SPU requires a delay between successive + * transactions. If our last transaction was more than a jiffy + * ago, we have obviously already delayed enough. + * If not, we have to busy-wait to be on the safe side. */ + ndelay(400); + } +} + +static void spu_transaction_finish(struct if_spi_card *card) +{ + card->prev_xfer_time = jiffies; +} + +/* + * Write out a byte buffer to an SPI register, + * using a series of 16-bit transfers. + */ +static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len) +{ + int err = 0; + __le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK); + struct spi_message m; + struct spi_transfer reg_trans; + struct spi_transfer data_trans; + + spi_message_init(&m); + memset(®_trans, 0, sizeof(reg_trans)); + memset(&data_trans, 0, sizeof(data_trans)); + + /* You must give an even number of bytes to the SPU, even if it + * doesn't care about the last one. */ + BUG_ON(len & 0x1); + + spu_transaction_init(card); + + /* write SPU register index */ + reg_trans.tx_buf = ®_out; + reg_trans.len = sizeof(reg_out); + + data_trans.tx_buf = buf; + data_trans.len = len; + + spi_message_add_tail(®_trans, &m); + spi_message_add_tail(&data_trans, &m); + + err = spi_sync(card->spi, &m); + spu_transaction_finish(card); + return err; +} + +static inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val) +{ + __le16 buff; + + buff = cpu_to_le16(val); + return spu_write(card, reg, (u8 *)&buff, sizeof(u16)); +} + +static inline int spu_reg_is_port_reg(u16 reg) +{ + switch (reg) { + case IF_SPI_IO_RDWRPORT_REG: + case IF_SPI_CMD_RDWRPORT_REG: + case IF_SPI_DATA_RDWRPORT_REG: + return 1; + default: + return 0; + } +} + +static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len) +{ + unsigned int delay; + int err = 0; + __le16 reg_out = cpu_to_le16(reg | IF_SPI_READ_OPERATION_MASK); + struct spi_message m; + struct spi_transfer reg_trans; + struct spi_transfer dummy_trans; + struct spi_transfer data_trans; + + /* + * You must take an even number of bytes from the SPU, even if you + * don't care about the last one. + */ + BUG_ON(len & 0x1); + + spu_transaction_init(card); + + spi_message_init(&m); + memset(®_trans, 0, sizeof(reg_trans)); + memset(&dummy_trans, 0, sizeof(dummy_trans)); + memset(&data_trans, 0, sizeof(data_trans)); + + /* write SPU register index */ + reg_trans.tx_buf = ®_out; + reg_trans.len = sizeof(reg_out); + spi_message_add_tail(®_trans, &m); + + delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay : + card->spu_reg_delay; + if (card->use_dummy_writes) { + /* Clock in dummy cycles while the SPU fills the FIFO */ + dummy_trans.len = delay / 8; + spi_message_add_tail(&dummy_trans, &m); + } else { + /* Busy-wait while the SPU fills the FIFO */ + reg_trans.delay_usecs = + DIV_ROUND_UP((100 + (delay * 10)), 1000); + } + + /* read in data */ + data_trans.rx_buf = buf; + data_trans.len = len; + spi_message_add_tail(&data_trans, &m); + + err = spi_sync(card->spi, &m); + spu_transaction_finish(card); + return err; +} + +/* Read 16 bits from an SPI register */ +static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val) +{ + __le16 buf; + int ret; + + ret = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); + if (ret == 0) + *val = le16_to_cpup(&buf); + return ret; +} + +/* + * Read 32 bits from an SPI register. + * The low 16 bits are read first. + */ +static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val) +{ + __le32 buf; + int err; + + err = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); + if (!err) + *val = le32_to_cpup(&buf); + return err; +} + +/* + * Keep reading 16 bits from an SPI register until you get the correct result. + * + * If mask = 0, the correct result is any non-zero number. + * If mask != 0, the correct result is any number where + * number & target_mask == target + * + * Returns -ETIMEDOUT if a second passes without the correct result. + */ +static int spu_wait_for_u16(struct if_spi_card *card, u16 reg, + u16 target_mask, u16 target) +{ + int err; + unsigned long timeout = jiffies + 5*HZ; + while (1) { + u16 val; + err = spu_read_u16(card, reg, &val); + if (err) + return err; + if (target_mask) { + if ((val & target_mask) == target) + return 0; + } else { + if (val) + return 0; + } + udelay(100); + if (time_after(jiffies, timeout)) { + pr_err("%s: timeout with val=%02x, target_mask=%02x, target=%02x\n", + __func__, val, target_mask, target); + return -ETIMEDOUT; + } + } +} + +/* + * Read 16 bits from an SPI register until you receive a specific value. + * Returns -ETIMEDOUT if a 4 tries pass without success. + */ +static int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target) +{ + int err, try; + for (try = 0; try < 4; ++try) { + u32 val = 0; + err = spu_read_u32(card, reg, &val); + if (err) + return err; + if (val == target) + return 0; + mdelay(100); + } + return -ETIMEDOUT; +} + +static int spu_set_interrupt_mode(struct if_spi_card *card, + int suppress_host_int, + int auto_int) +{ + int err = 0; + + /* + * We can suppress a host interrupt by clearing the appropriate + * bit in the "host interrupt status mask" register + */ + if (suppress_host_int) { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); + if (err) + return err; + } else { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, + IF_SPI_HISM_TX_DOWNLOAD_RDY | + IF_SPI_HISM_RX_UPLOAD_RDY | + IF_SPI_HISM_CMD_DOWNLOAD_RDY | + IF_SPI_HISM_CARDEVENT | + IF_SPI_HISM_CMD_UPLOAD_RDY); + if (err) + return err; + } + + /* + * If auto-interrupts are on, the completion of certain transactions + * will trigger an interrupt automatically. If auto-interrupts + * are off, we need to set the "Card Interrupt Cause" register to + * trigger a card interrupt. + */ + if (auto_int) { + err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG, + IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO | + IF_SPI_HICT_RX_UPLOAD_OVER_AUTO | + IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO | + IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO); + if (err) + return err; + } else { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); + if (err) + return err; + } + return err; +} + +static int spu_get_chip_revision(struct if_spi_card *card, + u16 *card_id, u8 *card_rev) +{ + int err = 0; + u32 dev_ctrl; + err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl); + if (err) + return err; + *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl); + *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl); + return err; +} + +static int spu_set_bus_mode(struct if_spi_card *card, u16 mode) +{ + int err = 0; + u16 rval; + /* set bus mode */ + err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode); + if (err) + return err; + /* Check that we were able to read back what we just wrote. */ + err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval); + if (err) + return err; + if ((rval & 0xF) != mode) { + pr_err("Can't read bus mode register\n"); + return -EIO; + } + return 0; +} + +static int spu_init(struct if_spi_card *card, int use_dummy_writes) +{ + int err = 0; + u32 delay; + + /* + * We have to start up in timed delay mode so that we can safely + * read the Delay Read Register. + */ + card->use_dummy_writes = 0; + err = spu_set_bus_mode(card, + IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | + IF_SPI_BUS_MODE_DELAY_METHOD_TIMED | + IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); + if (err) + return err; + card->spu_port_delay = 1000; + card->spu_reg_delay = 1000; + err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay); + if (err) + return err; + card->spu_port_delay = delay & 0x0000ffff; + card->spu_reg_delay = (delay & 0xffff0000) >> 16; + + /* If dummy clock delay mode has been requested, switch to it now */ + if (use_dummy_writes) { + card->use_dummy_writes = 1; + err = spu_set_bus_mode(card, + IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | + IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK | + IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); + if (err) + return err; + } + + lbs_deb_spi("Initialized SPU unit. " + "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n", + card->spu_port_delay, card->spu_reg_delay); + return err; +} + +/* + * Firmware Loading + */ + +static int if_spi_prog_helper_firmware(struct if_spi_card *card, + const struct firmware *firmware) +{ + int err = 0; + int bytes_remaining; + const u8 *fw; + u8 temp[HELPER_FW_LOAD_CHUNK_SZ]; + + lbs_deb_enter(LBS_DEB_SPI); + + err = spu_set_interrupt_mode(card, 1, 0); + if (err) + goto out; + + bytes_remaining = firmware->size; + fw = firmware->data; + + /* Load helper firmware image */ + while (bytes_remaining > 0) { + /* + * Scratch pad 1 should contain the number of bytes we + * want to download to the firmware + */ + err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, + HELPER_FW_LOAD_CHUNK_SZ); + if (err) + goto out; + + err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, + IF_SPI_HIST_CMD_DOWNLOAD_RDY, + IF_SPI_HIST_CMD_DOWNLOAD_RDY); + if (err) + goto out; + + /* + * Feed the data into the command read/write port reg + * in chunks of 64 bytes + */ + memset(temp, 0, sizeof(temp)); + memcpy(temp, fw, + min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ)); + mdelay(10); + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, + temp, HELPER_FW_LOAD_CHUNK_SZ); + if (err) + goto out; + + /* Interrupt the boot code */ + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, + IF_SPI_CIC_CMD_DOWNLOAD_OVER); + if (err) + goto out; + bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ; + fw += HELPER_FW_LOAD_CHUNK_SZ; + } + + /* + * Once the helper / single stage firmware download is complete, + * write 0 to scratch pad 1 and interrupt the + * bootloader. This completes the helper download. + */ + err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, + IF_SPI_CIC_CMD_DOWNLOAD_OVER); +out: + if (err) + pr_err("failed to load helper firmware (err=%d)\n", err); + lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); + return err; +} + +/* + * Returns the length of the next packet the firmware expects us to send. + * Sets crc_err if the previous transfer had a CRC error. + */ +static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card, + int *crc_err) +{ + u16 len; + int err = 0; + + /* + * wait until the host interrupt status register indicates + * that we are ready to download + */ + err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, + IF_SPI_HIST_CMD_DOWNLOAD_RDY, + IF_SPI_HIST_CMD_DOWNLOAD_RDY); + if (err) { + pr_err("timed out waiting for host_int_status\n"); + return err; + } + + /* Ask the device how many bytes of firmware it wants. */ + err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); + if (err) + return err; + + if (len > IF_SPI_CMD_BUF_SIZE) { + pr_err("firmware load device requested a larger transfer than we are prepared to handle (len = %d)\n", + len); + return -EIO; + } + if (len & 0x1) { + lbs_deb_spi("%s: crc error\n", __func__); + len &= ~0x1; + *crc_err = 1; + } else + *crc_err = 0; + + return len; +} + +static int if_spi_prog_main_firmware(struct if_spi_card *card, + const struct firmware *firmware) +{ + struct lbs_private *priv = card->priv; + int len, prev_len; + int bytes, crc_err = 0, err = 0; + const u8 *fw; + u16 num_crc_errs; + + lbs_deb_enter(LBS_DEB_SPI); + + err = spu_set_interrupt_mode(card, 1, 0); + if (err) + goto out; + + err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0); + if (err) { + netdev_err(priv->dev, + "%s: timed out waiting for initial scratch reg = 0\n", + __func__); + goto out; + } + + num_crc_errs = 0; + prev_len = 0; + bytes = firmware->size; + fw = firmware->data; + while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) { + if (len < 0) { + err = len; + goto out; + } + if (bytes < 0) { + /* + * If there are no more bytes left, we would normally + * expect to have terminated with len = 0 + */ + netdev_err(priv->dev, + "Firmware load wants more bytes than we have to offer.\n"); + break; + } + if (crc_err) { + /* Previous transfer failed. */ + if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) { + pr_err("Too many CRC errors encountered in firmware load.\n"); + err = -EIO; + goto out; + } + } else { + /* Previous transfer succeeded. Advance counters. */ + bytes -= prev_len; + fw += prev_len; + } + if (bytes < len) { + memset(card->cmd_buffer, 0, len); + memcpy(card->cmd_buffer, fw, bytes); + } else + memcpy(card->cmd_buffer, fw, len); + + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto out; + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, + card->cmd_buffer, len); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG , + IF_SPI_CIC_CMD_DOWNLOAD_OVER); + if (err) + goto out; + prev_len = len; + } + if (bytes > prev_len) { + pr_err("firmware load wants fewer bytes than we have to offer\n"); + } + + /* Confirm firmware download */ + err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG, + SUCCESSFUL_FW_DOWNLOAD_MAGIC); + if (err) { + pr_err("failed to confirm the firmware download\n"); + goto out; + } + +out: + if (err) + pr_err("failed to load firmware (err=%d)\n", err); + lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); + return err; +} + +/* + * SPI Transfer Thread + * + * The SPI worker handles all SPI transfers, so there is no need for a lock. + */ + +/* Move a command from the card to the host */ +static int if_spi_c2h_cmd(struct if_spi_card *card) +{ + struct lbs_private *priv = card->priv; + unsigned long flags; + int err = 0; + u16 len; + u8 i; + + /* + * We need a buffer big enough to handle whatever people send to + * hw_host_to_card + */ + BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE); + BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE); + + /* + * It's just annoying if the buffer size isn't a multiple of 4, because + * then we might have len < IF_SPI_CMD_BUF_SIZE but + * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE + */ + BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0); + + lbs_deb_enter(LBS_DEB_SPI); + + /* How many bytes are there to read? */ + err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len); + if (err) + goto out; + if (!len) { + netdev_err(priv->dev, "%s: error: card has no data for host\n", + __func__); + err = -EINVAL; + goto out; + } else if (len > IF_SPI_CMD_BUF_SIZE) { + netdev_err(priv->dev, + "%s: error: response packet too large: %d bytes, but maximum is %d\n", + __func__, len, IF_SPI_CMD_BUF_SIZE); + err = -EINVAL; + goto out; + } + + /* Read the data from the WLAN module into our command buffer */ + err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG, + card->cmd_buffer, ALIGN(len, 4)); + if (err) + goto out; + + spin_lock_irqsave(&priv->driver_lock, flags); + i = (priv->resp_idx == 0) ? 1 : 0; + BUG_ON(priv->resp_len[i]); + priv->resp_len[i] = len; + memcpy(priv->resp_buf[i], card->cmd_buffer, len); + lbs_notify_command_response(priv, i); + spin_unlock_irqrestore(&priv->driver_lock, flags); + +out: + if (err) + netdev_err(priv->dev, "%s: err=%d\n", __func__, err); + lbs_deb_leave(LBS_DEB_SPI); + return err; +} + +/* Move data from the card to the host */ +static int if_spi_c2h_data(struct if_spi_card *card) +{ + struct lbs_private *priv = card->priv; + struct sk_buff *skb; + char *data; + u16 len; + int err = 0; + + lbs_deb_enter(LBS_DEB_SPI); + + /* How many bytes are there to read? */ + err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); + if (err) + goto out; + if (!len) { + netdev_err(priv->dev, "%s: error: card has no data for host\n", + __func__); + err = -EINVAL; + goto out; + } else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { + netdev_err(priv->dev, + "%s: error: card has %d bytes of data, but our maximum skb size is %zu\n", + __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); + err = -EINVAL; + goto out; + } + + /* TODO: should we allocate a smaller skb if we have less data? */ + skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); + if (!skb) { + err = -ENOBUFS; + goto out; + } + skb_reserve(skb, IPFIELD_ALIGN_OFFSET); + data = skb_put(skb, len); + + /* Read the data from the WLAN module into our skb... */ + err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 4)); + if (err) + goto free_skb; + + /* pass the SKB to libertas */ + err = lbs_process_rxed_packet(card->priv, skb); + if (err) + goto free_skb; + + /* success */ + goto out; + +free_skb: + dev_kfree_skb(skb); +out: + if (err) + netdev_err(priv->dev, "%s: err=%d\n", __func__, err); + lbs_deb_leave(LBS_DEB_SPI); + return err; +} + +/* Move data or a command from the host to the card. */ +static void if_spi_h2c(struct if_spi_card *card, + struct if_spi_packet *packet, int type) +{ + struct lbs_private *priv = card->priv; + int err = 0; + u16 int_type, port_reg; + + switch (type) { + case MVMS_DAT: + int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER; + port_reg = IF_SPI_DATA_RDWRPORT_REG; + break; + case MVMS_CMD: + int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER; + port_reg = IF_SPI_CMD_RDWRPORT_REG; + break; + default: + netdev_err(priv->dev, "can't transfer buffer of type %d\n", + type); + err = -EINVAL; + goto out; + } + + /* Write the data to the card */ + err = spu_write(card, port_reg, packet->buffer, packet->blen); + if (err) + goto out; + +out: + kfree(packet); + + if (err) + netdev_err(priv->dev, "%s: error %d\n", __func__, err); +} + +/* Inform the host about a card event */ +static void if_spi_e2h(struct if_spi_card *card) +{ + int err = 0; + u32 cause; + struct lbs_private *priv = card->priv; + + err = spu_read_u32(card, IF_SPI_SCRATCH_3_REG, &cause); + if (err) + goto out; + + /* re-enable the card event interrupt */ + spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, + ~IF_SPI_HICU_CARD_EVENT); + + /* generate a card interrupt */ + spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_HOST_EVENT); + + lbs_queue_event(priv, cause & 0xff); +out: + if (err) + netdev_err(priv->dev, "%s: error %d\n", __func__, err); +} + +static void if_spi_host_to_card_worker(struct work_struct *work) +{ + int err; + struct if_spi_card *card; + u16 hiStatus; + unsigned long flags; + struct if_spi_packet *packet; + struct lbs_private *priv; + + card = container_of(work, struct if_spi_card, packet_work); + priv = card->priv; + + lbs_deb_enter(LBS_DEB_SPI); + + /* + * Read the host interrupt status register to see what we + * can do. + */ + err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG, + &hiStatus); + if (err) { + netdev_err(priv->dev, "I/O error\n"); + goto err; + } + + if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) { + err = if_spi_c2h_cmd(card); + if (err) + goto err; + } + if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) { + err = if_spi_c2h_data(card); + if (err) + goto err; + } + + /* + * workaround: in PS mode, the card does not set the Command + * Download Ready bit, but it sets TX Download Ready. + */ + if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY || + (card->priv->psstate != PS_STATE_FULL_POWER && + (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) { + /* + * This means two things. First of all, + * if there was a previous command sent, the card has + * successfully received it. + * Secondly, it is now ready to download another + * command. + */ + lbs_host_to_card_done(card->priv); + + /* Do we have any command packets from the host to send? */ + packet = NULL; + spin_lock_irqsave(&card->buffer_lock, flags); + if (!list_empty(&card->cmd_packet_list)) { + packet = (struct if_spi_packet *)(card-> + cmd_packet_list.next); + list_del(&packet->list); + } + spin_unlock_irqrestore(&card->buffer_lock, flags); + + if (packet) + if_spi_h2c(card, packet, MVMS_CMD); + } + if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) { + /* Do we have any data packets from the host to send? */ + packet = NULL; + spin_lock_irqsave(&card->buffer_lock, flags); + if (!list_empty(&card->data_packet_list)) { + packet = (struct if_spi_packet *)(card-> + data_packet_list.next); + list_del(&packet->list); + } + spin_unlock_irqrestore(&card->buffer_lock, flags); + + if (packet) + if_spi_h2c(card, packet, MVMS_DAT); + } + if (hiStatus & IF_SPI_HIST_CARD_EVENT) + if_spi_e2h(card); + +err: + if (err) + netdev_err(priv->dev, "%s: got error %d\n", __func__, err); + + lbs_deb_leave(LBS_DEB_SPI); +} + +/* + * Host to Card + * + * Called from Libertas to transfer some data to the WLAN device + * We can't sleep here. + */ +static int if_spi_host_to_card(struct lbs_private *priv, + u8 type, u8 *buf, u16 nb) +{ + int err = 0; + unsigned long flags; + struct if_spi_card *card = priv->card; + struct if_spi_packet *packet; + u16 blen; + + lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb); + + if (nb == 0) { + netdev_err(priv->dev, "%s: invalid size requested: %d\n", + __func__, nb); + err = -EINVAL; + goto out; + } + blen = ALIGN(nb, 4); + packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC); + if (!packet) { + err = -ENOMEM; + goto out; + } + packet->blen = blen; + memcpy(packet->buffer, buf, nb); + memset(packet->buffer + nb, 0, blen - nb); + + switch (type) { + case MVMS_CMD: + priv->dnld_sent = DNLD_CMD_SENT; + spin_lock_irqsave(&card->buffer_lock, flags); + list_add_tail(&packet->list, &card->cmd_packet_list); + spin_unlock_irqrestore(&card->buffer_lock, flags); + break; + case MVMS_DAT: + priv->dnld_sent = DNLD_DATA_SENT; + spin_lock_irqsave(&card->buffer_lock, flags); + list_add_tail(&packet->list, &card->data_packet_list); + spin_unlock_irqrestore(&card->buffer_lock, flags); + break; + default: + kfree(packet); + netdev_err(priv->dev, "can't transfer buffer of type %d\n", + type); + err = -EINVAL; + break; + } + + /* Queue spi xfer work */ + queue_work(card->workqueue, &card->packet_work); +out: + lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err); + return err; +} + +/* + * Host Interrupts + * + * Service incoming interrupts from the WLAN device. We can't sleep here, so + * don't try to talk on the SPI bus, just queue the SPI xfer work. + */ +static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id) +{ + struct if_spi_card *card = dev_id; + + queue_work(card->workqueue, &card->packet_work); + + return IRQ_HANDLED; +} + +/* + * SPI callbacks + */ + +static int if_spi_init_card(struct if_spi_card *card) +{ + struct lbs_private *priv = card->priv; + int err, i; + u32 scratch; + const struct firmware *helper = NULL; + const struct firmware *mainfw = NULL; + + lbs_deb_enter(LBS_DEB_SPI); + + err = spu_init(card, card->pdata->use_dummy_writes); + if (err) + goto out; + err = spu_get_chip_revision(card, &card->card_id, &card->card_rev); + if (err) + goto out; + + err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch); + if (err) + goto out; + if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC) + lbs_deb_spi("Firmware is already loaded for " + "Marvell WLAN 802.11 adapter\n"); + else { + /* Check if we support this card */ + for (i = 0; i < ARRAY_SIZE(fw_table); i++) { + if (card->card_id == fw_table[i].model) + break; + } + if (i == ARRAY_SIZE(fw_table)) { + netdev_err(priv->dev, "Unsupported chip_id: 0x%02x\n", + card->card_id); + err = -ENODEV; + goto out; + } + + err = lbs_get_firmware(&card->spi->dev, card->card_id, + &fw_table[0], &helper, &mainfw); + if (err) { + netdev_err(priv->dev, "failed to find firmware (%d)\n", + err); + goto out; + } + + lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter " + "(chip_id = 0x%04x, chip_rev = 0x%02x) " + "attached to SPI bus_num %d, chip_select %d. " + "spi->max_speed_hz=%d\n", + card->card_id, card->card_rev, + card->spi->master->bus_num, + card->spi->chip_select, + card->spi->max_speed_hz); + err = if_spi_prog_helper_firmware(card, helper); + if (err) + goto out; + err = if_spi_prog_main_firmware(card, mainfw); + if (err) + goto out; + lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n"); + } + + err = spu_set_interrupt_mode(card, 0, 1); + if (err) + goto out; + +out: + lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); + return err; +} + +static void if_spi_resume_worker(struct work_struct *work) +{ + struct if_spi_card *card; + + card = container_of(work, struct if_spi_card, resume_work); + + if (card->suspended) { + if (card->pdata->setup) + card->pdata->setup(card->spi); + + /* Init card ... */ + if_spi_init_card(card); + + enable_irq(card->spi->irq); + + /* And resume it ... */ + lbs_resume(card->priv); + + card->suspended = 0; + } +} + +static int if_spi_probe(struct spi_device *spi) +{ + struct if_spi_card *card; + struct lbs_private *priv = NULL; + struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev); + int err = 0; + + lbs_deb_enter(LBS_DEB_SPI); + + if (!pdata) { + err = -EINVAL; + goto out; + } + + if (pdata->setup) { + err = pdata->setup(spi); + if (err) + goto out; + } + + /* Allocate card structure to represent this specific device */ + card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL); + if (!card) { + err = -ENOMEM; + goto teardown; + } + spi_set_drvdata(spi, card); + card->pdata = pdata; + card->spi = spi; + card->prev_xfer_time = jiffies; + + INIT_LIST_HEAD(&card->cmd_packet_list); + INIT_LIST_HEAD(&card->data_packet_list); + spin_lock_init(&card->buffer_lock); + + /* Initialize the SPI Interface Unit */ + + /* Firmware load */ + err = if_spi_init_card(card); + if (err) + goto free_card; + + /* + * Register our card with libertas. + * This will call alloc_etherdev. + */ + priv = lbs_add_card(card, &spi->dev); + if (!priv) { + err = -ENOMEM; + goto free_card; + } + card->priv = priv; + priv->setup_fw_on_resume = 1; + priv->card = card; + priv->hw_host_to_card = if_spi_host_to_card; + priv->enter_deep_sleep = NULL; + priv->exit_deep_sleep = NULL; + priv->reset_deep_sleep_wakeup = NULL; + priv->fw_ready = 1; + + /* Initialize interrupt handling stuff. */ + card->workqueue = create_workqueue("libertas_spi"); + INIT_WORK(&card->packet_work, if_spi_host_to_card_worker); + INIT_WORK(&card->resume_work, if_spi_resume_worker); + + err = request_irq(spi->irq, if_spi_host_interrupt, + IRQF_TRIGGER_FALLING, "libertas_spi", card); + if (err) { + pr_err("can't get host irq line-- request_irq failed\n"); + goto terminate_workqueue; + } + + /* + * Start the card. + * This will call register_netdev, and we'll start + * getting interrupts... + */ + err = lbs_start_card(priv); + if (err) + goto release_irq; + + lbs_deb_spi("Finished initializing WLAN module.\n"); + + /* successful exit */ + goto out; + +release_irq: + free_irq(spi->irq, card); +terminate_workqueue: + flush_workqueue(card->workqueue); + destroy_workqueue(card->workqueue); + lbs_remove_card(priv); /* will call free_netdev */ +free_card: + free_if_spi_card(card); +teardown: + if (pdata->teardown) + pdata->teardown(spi); +out: + lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); + return err; +} + +static int libertas_spi_remove(struct spi_device *spi) +{ + struct if_spi_card *card = spi_get_drvdata(spi); + struct lbs_private *priv = card->priv; + + lbs_deb_spi("libertas_spi_remove\n"); + lbs_deb_enter(LBS_DEB_SPI); + + cancel_work_sync(&card->resume_work); + + lbs_stop_card(priv); + lbs_remove_card(priv); /* will call free_netdev */ + + free_irq(spi->irq, card); + flush_workqueue(card->workqueue); + destroy_workqueue(card->workqueue); + if (card->pdata->teardown) + card->pdata->teardown(spi); + free_if_spi_card(card); + lbs_deb_leave(LBS_DEB_SPI); + return 0; +} + +static int if_spi_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct if_spi_card *card = spi_get_drvdata(spi); + + if (!card->suspended) { + lbs_suspend(card->priv); + flush_workqueue(card->workqueue); + disable_irq(spi->irq); + + if (card->pdata->teardown) + card->pdata->teardown(spi); + card->suspended = 1; + } + + return 0; +} + +static int if_spi_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct if_spi_card *card = spi_get_drvdata(spi); + + /* Schedule delayed work */ + schedule_work(&card->resume_work); + + return 0; +} + +static const struct dev_pm_ops if_spi_pm_ops = { + .suspend = if_spi_suspend, + .resume = if_spi_resume, +}; + +static struct spi_driver libertas_spi_driver = { + .probe = if_spi_probe, + .remove = libertas_spi_remove, + .driver = { + .name = "libertas_spi", + .pm = &if_spi_pm_ops, + }, +}; + +/* + * Module functions + */ + +static int __init if_spi_init_module(void) +{ + int ret = 0; + lbs_deb_enter(LBS_DEB_SPI); + printk(KERN_INFO "libertas_spi: Libertas SPI driver\n"); + ret = spi_register_driver(&libertas_spi_driver); + lbs_deb_leave(LBS_DEB_SPI); + return ret; +} + +static void __exit if_spi_exit_module(void) +{ + lbs_deb_enter(LBS_DEB_SPI); + spi_unregister_driver(&libertas_spi_driver); + lbs_deb_leave(LBS_DEB_SPI); +} + +module_init(if_spi_init_module); +module_exit(if_spi_exit_module); + +MODULE_DESCRIPTION("Libertas SPI WLAN Driver"); +MODULE_AUTHOR("Andrey Yurovsky , " + "Colin McCabe "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:libertas_spi"); diff --git a/drivers/net/wireless/marvell/libertas/if_spi.h b/drivers/net/wireless/marvell/libertas/if_spi.h new file mode 100644 index 000000000000..e450e31fd11d --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_spi.h @@ -0,0 +1,206 @@ +/* + * linux/drivers/net/wireless/libertas/if_spi.c + * + * Driver for Marvell SPI WLAN cards. + * + * Copyright 2008 Analog Devices Inc. + * + * Authors: + * Andrey Yurovsky + * Colin McCabe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef _LBS_IF_SPI_H_ +#define _LBS_IF_SPI_H_ + +#define IPFIELD_ALIGN_OFFSET 2 +#define IF_SPI_CMD_BUF_SIZE 2400 + +/***************** Firmware *****************/ + +#define IF_SPI_FW_NAME_MAX 30 + +#define MAX_MAIN_FW_LOAD_CRC_ERR 10 + +/* Chunk size when loading the helper firmware */ +#define HELPER_FW_LOAD_CHUNK_SZ 64 + +/* Value to write to indicate end of helper firmware dnld */ +#define FIRMWARE_DNLD_OK 0x0000 + +/* Value to check once the main firmware is downloaded */ +#define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888 + +/***************** SPI Interface Unit *****************/ +/* Masks used in SPI register read/write operations */ +#define IF_SPI_READ_OPERATION_MASK 0x0 +#define IF_SPI_WRITE_OPERATION_MASK 0x8000 + +/* SPI register offsets. 4-byte aligned. */ +#define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */ +#define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */ +#define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */ +#define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */ + +#define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */ +#define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */ +#define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */ + +#define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */ +#define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */ +#define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */ + +#define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */ +#define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */ +#define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */ +#define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */ + +#define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number reg */ +#define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */ + +#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */ + +#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */ +#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interrupt status reg */ +#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */ +#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */ + +#define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset select */ + +#define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */ +#define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */ +#define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event mask */ +#define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status mask */ +#define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset select */ + +#define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */ +#define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */ + +/***************** IF_SPI_DEVICEID_CTRL_REG *****************/ +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16) +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff) + +/***************** IF_SPI_HOST_INT_CTRL_REG *****************/ +/* Host Interrupt Control bit : Wake up */ +#define IF_SPI_HICT_WAKE_UP (1<<0) +/* Host Interrupt Control bit : WLAN ready */ +#define IF_SPI_HICT_WLAN_READY (1<<1) +/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */ +/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */ +/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */ +/* Host Interrupt Control bit : Tx auto download */ +#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5) +/* Host Interrupt Control bit : Rx auto upload */ +#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6) +/* Host Interrupt Control bit : Command auto download */ +#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7) +/* Host Interrupt Control bit : Command auto upload */ +#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8) + +/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/ +/* Card Interrupt Case bit : Tx download over */ +#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0) +/* Card Interrupt Case bit : Rx upload over */ +#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1) +/* Card Interrupt Case bit : Command download over */ +#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2) +/* Card Interrupt Case bit : Host event */ +#define IF_SPI_CIC_HOST_EVENT (1<<3) +/* Card Interrupt Case bit : Command upload over */ +#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4) +/* Card Interrupt Case bit : Power down */ +#define IF_SPI_CIC_POWER_DOWN (1<<5) + +/***************** IF_SPI_CARD_INT_STATUS_REG *****************/ +#define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0) +#define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1) +#define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2) +#define IF_SPI_CIS_HOST_EVENT (1<<3) +#define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4) +#define IF_SPI_CIS_POWER_DOWN (1<<5) + +/***************** IF_SPI_HOST_INT_CAUSE_REG *****************/ +#define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0) +#define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1) +#define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2) +#define IF_SPI_HICU_CARD_EVENT (1<<3) +#define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4) +#define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5) +#define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6) +#define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7) +#define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8) +#define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9) +#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_HOST_INT_STATUS_REG *****************/ +/* Host Interrupt Status bit : Tx download ready */ +#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0) +/* Host Interrupt Status bit : Rx upload ready */ +#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1) +/* Host Interrupt Status bit : Command download ready */ +#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2) +/* Host Interrupt Status bit : Card event */ +#define IF_SPI_HIST_CARD_EVENT (1<<3) +/* Host Interrupt Status bit : Command upload ready */ +#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4) +/* Host Interrupt Status bit : I/O write FIFO overflow */ +#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5) +/* Host Interrupt Status bit : I/O read FIFO underflow */ +#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6) +/* Host Interrupt Status bit : Data write FIFO overflow */ +#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7) +/* Host Interrupt Status bit : Data read FIFO underflow */ +#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8) +/* Host Interrupt Status bit : Command write FIFO overflow */ +#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9) +/* Host Interrupt Status bit : Command read FIFO underflow */ +#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/ +/* Host Interrupt Status Mask bit : Tx download ready */ +#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0) +/* Host Interrupt Status Mask bit : Rx upload ready */ +#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1) +/* Host Interrupt Status Mask bit : Command download ready */ +#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2) +/* Host Interrupt Status Mask bit : Card event */ +#define IF_SPI_HISM_CARDEVENT (1<<3) +/* Host Interrupt Status Mask bit : Command upload ready */ +#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4) +/* Host Interrupt Status Mask bit : I/O write FIFO overflow */ +#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5) +/* Host Interrupt Status Mask bit : I/O read FIFO underflow */ +#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6) +/* Host Interrupt Status Mask bit : Data write FIFO overflow */ +#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7) +/* Host Interrupt Status Mask bit : Data write FIFO underflow */ +#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8) +/* Host Interrupt Status Mask bit : Command write FIFO overflow */ +#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9) +/* Host Interrupt Status Mask bit : Command write FIFO underflow */ +#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_SPU_BUS_MODE_REG *****************/ +/* SCK edge on which the WLAN module outputs data on MISO */ +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8 +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0 + +/* In a SPU read operation, there is a delay between writing the SPU + * register name and getting back data from the WLAN module. + * This can be specified in terms of nanoseconds or in terms of dummy + * clock cycles which the master must output before receiving a response. */ +#define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4 +#define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0 + +/* Some different modes of SPI operation */ +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00 +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01 +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02 +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03 + +#endif diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c new file mode 100644 index 000000000000..dff08a2896a3 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_usb.c @@ -0,0 +1,1018 @@ +/* + * This file contains functions used in USB interface module. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OLPC +#include +#endif + +#define DRV_NAME "usb8xxx" + +#include "host.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "cmd.h" +#include "if_usb.h" + +#define INSANEDEBUG 0 +#define lbs_deb_usb2(...) do { if (INSANEDEBUG) lbs_deb_usbd(__VA_ARGS__); } while (0) + +#define MESSAGE_HEADER_LEN 4 + +MODULE_FIRMWARE("libertas/usb8388_v9.bin"); +MODULE_FIRMWARE("libertas/usb8388_v5.bin"); +MODULE_FIRMWARE("libertas/usb8388.bin"); +MODULE_FIRMWARE("libertas/usb8682.bin"); +MODULE_FIRMWARE("usb8388.bin"); + +enum { + MODEL_UNKNOWN = 0x0, + MODEL_8388 = 0x1, + MODEL_8682 = 0x2 +}; + +/* table of firmware file names */ +static const struct lbs_fw_table fw_table[] = { + { MODEL_8388, "libertas/usb8388_olpc.bin", NULL }, + { MODEL_8388, "libertas/usb8388_v9.bin", NULL }, + { MODEL_8388, "libertas/usb8388_v5.bin", NULL }, + { MODEL_8388, "libertas/usb8388.bin", NULL }, + { MODEL_8388, "usb8388.bin", NULL }, + { MODEL_8682, "libertas/usb8682.bin", NULL } +}; + +static struct usb_device_id if_usb_table[] = { + /* Enter the device signature inside */ + { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 }, + { USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, if_usb_table); + +static void if_usb_receive(struct urb *urb); +static void if_usb_receive_fwload(struct urb *urb); +static void if_usb_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *fw, + const struct firmware *unused); +static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, + uint8_t *payload, uint16_t nb); +static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, + uint16_t nb); +static void if_usb_free(struct if_usb_card *cardp); +static int if_usb_submit_rx_urb(struct if_usb_card *cardp); +static int if_usb_reset_device(struct if_usb_card *cardp); + +/** + * if_usb_write_bulk_callback - callback function to handle the status + * of the URB + * @urb: pointer to &urb structure + * returns: N/A + */ +static void if_usb_write_bulk_callback(struct urb *urb) +{ + struct if_usb_card *cardp = (struct if_usb_card *) urb->context; + + /* handle the transmission complete validations */ + + if (urb->status == 0) { + struct lbs_private *priv = cardp->priv; + + lbs_deb_usb2(&urb->dev->dev, "URB status is successful\n"); + lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", + urb->actual_length); + + /* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not + * passed up to the lbs level. + */ + if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT) + lbs_host_to_card_done(priv); + } else { + /* print the failure status number for debug */ + pr_info("URB in failure status: %d\n", urb->status); + } +} + +/** + * if_usb_free - free tx/rx urb, skb and rx buffer + * @cardp: pointer to &if_usb_card + * returns: N/A + */ +static void if_usb_free(struct if_usb_card *cardp) +{ + lbs_deb_enter(LBS_DEB_USB); + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + usb_free_urb(cardp->tx_urb); + cardp->tx_urb = NULL; + + usb_free_urb(cardp->rx_urb); + cardp->rx_urb = NULL; + + kfree(cardp->ep_out_buf); + cardp->ep_out_buf = NULL; + + lbs_deb_leave(LBS_DEB_USB); +} + +static void if_usb_setup_firmware(struct lbs_private *priv) +{ + struct if_usb_card *cardp = priv->card; + struct cmd_ds_set_boot2_ver b2_cmd; + struct cmd_ds_802_11_fw_wake_method wake_method; + + b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); + b2_cmd.action = 0; + b2_cmd.version = cardp->boot2_version; + + if (lbs_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) + lbs_deb_usb("Setting boot2 version failed\n"); + + priv->wol_gpio = 2; /* Wake via GPIO2... */ + priv->wol_gap = 20; /* ... after 20ms */ + lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA, + (struct wol_config *) NULL); + + wake_method.hdr.size = cpu_to_le16(sizeof(wake_method)); + wake_method.action = cpu_to_le16(CMD_ACT_GET); + if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) { + netdev_info(priv->dev, "Firmware does not seem to support PS mode\n"); + priv->fwcapinfo &= ~FW_CAPINFO_PS; + } else { + if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) { + lbs_deb_usb("Firmware seems to support PS with wake-via-command\n"); + } else { + /* The versions which boot up this way don't seem to + work even if we set it to the command interrupt */ + priv->fwcapinfo &= ~FW_CAPINFO_PS; + netdev_info(priv->dev, + "Firmware doesn't wake via command interrupt; disabling PS mode\n"); + } + } +} + +static void if_usb_fw_timeo(unsigned long priv) +{ + struct if_usb_card *cardp = (void *)priv; + + if (cardp->fwdnldover) { + lbs_deb_usb("Download complete, no event. Assuming success\n"); + } else { + pr_err("Download timed out\n"); + cardp->surprise_removed = 1; + } + wake_up(&cardp->fw_wq); +} + +#ifdef CONFIG_OLPC +static void if_usb_reset_olpc_card(struct lbs_private *priv) +{ + printk(KERN_CRIT "Resetting OLPC wireless via EC...\n"); + olpc_ec_cmd(0x25, NULL, 0, NULL, 0); +} +#endif + +/** + * if_usb_probe - sets the configuration values + * @intf: &usb_interface pointer + * @id: pointer to usb_device_id + * returns: 0 on success, error code on failure + */ +static int if_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct lbs_private *priv; + struct if_usb_card *cardp; + int r = -ENOMEM; + int i; + + udev = interface_to_usbdev(intf); + + cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); + if (!cardp) + goto error; + + setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp); + init_waitqueue_head(&cardp->fw_wq); + + cardp->udev = udev; + cardp->model = (uint32_t) id->driver_info; + iface_desc = intf->cur_altsetting; + + lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" + " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", + le16_to_cpu(udev->descriptor.bcdUSB), + udev->descriptor.bDeviceClass, + udev->descriptor.bDeviceSubClass, + udev->descriptor.bDeviceProtocol); + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (usb_endpoint_is_bulk_in(endpoint)) { + cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); + cardp->ep_in = usb_endpoint_num(endpoint); + + lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); + lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); + + } else if (usb_endpoint_is_bulk_out(endpoint)) { + cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); + cardp->ep_out = usb_endpoint_num(endpoint); + + lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out); + lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size); + } + } + if (!cardp->ep_out_size || !cardp->ep_in_size) { + lbs_deb_usbd(&udev->dev, "Endpoints not found\n"); + goto dealloc; + } + if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) { + lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n"); + goto dealloc; + } + if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) { + lbs_deb_usbd(&udev->dev, "Tx URB allocation failed\n"); + goto dealloc; + } + cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL); + if (!cardp->ep_out_buf) { + lbs_deb_usbd(&udev->dev, "Could not allocate buffer\n"); + goto dealloc; + } + + if (!(priv = lbs_add_card(cardp, &intf->dev))) + goto err_add_card; + + cardp->priv = priv; + + priv->hw_host_to_card = if_usb_host_to_card; + priv->enter_deep_sleep = NULL; + priv->exit_deep_sleep = NULL; + priv->reset_deep_sleep_wakeup = NULL; +#ifdef CONFIG_OLPC + if (machine_is_olpc()) + priv->reset_card = if_usb_reset_olpc_card; +#endif + + cardp->boot2_version = udev->descriptor.bcdDevice; + + usb_get_dev(udev); + usb_set_intfdata(intf, cardp); + + r = lbs_get_firmware_async(priv, &udev->dev, cardp->model, + fw_table, if_usb_prog_firmware); + if (r) + goto err_get_fw; + + return 0; + +err_get_fw: + lbs_remove_card(priv); +err_add_card: + if_usb_reset_device(cardp); +dealloc: + if_usb_free(cardp); + +error: + return r; +} + +/** + * if_usb_disconnect - free resource and cleanup + * @intf: USB interface structure + * returns: N/A + */ +static void if_usb_disconnect(struct usb_interface *intf) +{ + struct if_usb_card *cardp = usb_get_intfdata(intf); + struct lbs_private *priv = cardp->priv; + + lbs_deb_enter(LBS_DEB_MAIN); + + cardp->surprise_removed = 1; + + if (priv) { + lbs_stop_card(priv); + lbs_remove_card(priv); + } + + /* Unlink and free urb */ + if_usb_free(cardp); + + usb_set_intfdata(intf, NULL); + usb_put_dev(interface_to_usbdev(intf)); + + lbs_deb_leave(LBS_DEB_MAIN); +} + +/** + * if_usb_send_fw_pkt - download FW + * @cardp: pointer to &struct if_usb_card + * returns: 0 + */ +static int if_usb_send_fw_pkt(struct if_usb_card *cardp) +{ + struct fwdata *fwdata = cardp->ep_out_buf; + const uint8_t *firmware = cardp->fw->data; + + /* If we got a CRC failure on the last block, back + up and retry it */ + if (!cardp->CRC_OK) { + cardp->totalbytes = cardp->fwlastblksent; + cardp->fwseqnum--; + } + + lbs_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n", + cardp->totalbytes); + + /* struct fwdata (which we sent to the card) has an + extra __le32 field in between the header and the data, + which is not in the struct fwheader in the actual + firmware binary. Insert the seqnum in the middle... */ + memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], + sizeof(struct fwheader)); + + cardp->fwlastblksent = cardp->totalbytes; + cardp->totalbytes += sizeof(struct fwheader); + + memcpy(fwdata->data, &firmware[cardp->totalbytes], + le32_to_cpu(fwdata->hdr.datalength)); + + lbs_deb_usb2(&cardp->udev->dev, "Data length = %d\n", + le32_to_cpu(fwdata->hdr.datalength)); + + fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); + cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); + + usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + + le32_to_cpu(fwdata->hdr.datalength)); + + if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) { + lbs_deb_usb2(&cardp->udev->dev, "There are data to follow\n"); + lbs_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n", + cardp->fwseqnum, cardp->totalbytes); + } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) { + lbs_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n"); + lbs_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n"); + + cardp->fwfinalblk = 1; + } + + lbs_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n", + cardp->totalbytes); + + return 0; +} + +static int if_usb_reset_device(struct if_usb_card *cardp) +{ + struct cmd_header *cmd = cardp->ep_out_buf + 4; + int ret; + + lbs_deb_enter(LBS_DEB_USB); + + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); + + cmd->command = cpu_to_le16(CMD_802_11_RESET); + cmd->size = cpu_to_le16(sizeof(cmd)); + cmd->result = cpu_to_le16(0); + cmd->seqnum = cpu_to_le16(0x5a5a); + usb_tx_block(cardp, cardp->ep_out_buf, 4 + sizeof(struct cmd_header)); + + msleep(100); + ret = usb_reset_device(cardp->udev); + msleep(100); + +#ifdef CONFIG_OLPC + if (ret && machine_is_olpc()) + if_usb_reset_olpc_card(NULL); +#endif + + lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret); + + return ret; +} + +/** + * usb_tx_block - transfer the data to the device + * @cardp: pointer to &struct if_usb_card + * @payload: pointer to payload data + * @nb: data length + * returns: 0 for success or negative error code + */ +static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb) +{ + int ret; + + /* check if device is removed */ + if (cardp->surprise_removed) { + lbs_deb_usbd(&cardp->udev->dev, "Device removed\n"); + ret = -ENODEV; + goto tx_ret; + } + + usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, + usb_sndbulkpipe(cardp->udev, + cardp->ep_out), + payload, nb, if_usb_write_bulk_callback, cardp); + + cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; + + if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { + lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret); + } else { + lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n"); + ret = 0; + } + +tx_ret: + return ret; +} + +static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, + void (*callbackfn)(struct urb *urb)) +{ + struct sk_buff *skb; + int ret = -1; + + if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { + pr_err("No free skb\n"); + goto rx_ret; + } + + cardp->rx_skb = skb; + + /* Fill the receive configuration URB and initialise the Rx call back */ + usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, + usb_rcvbulkpipe(cardp->udev, cardp->ep_in), + skb->data + IPFIELD_ALIGN_OFFSET, + MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, + cardp); + + cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; + + lbs_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); + if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { + lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret); + kfree_skb(skb); + cardp->rx_skb = NULL; + ret = -1; + } else { + lbs_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n"); + ret = 0; + } + +rx_ret: + return ret; +} + +static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) +{ + return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); +} + +static int if_usb_submit_rx_urb(struct if_usb_card *cardp) +{ + return __if_usb_submit_rx_urb(cardp, &if_usb_receive); +} + +static void if_usb_receive_fwload(struct urb *urb) +{ + struct if_usb_card *cardp = urb->context; + struct sk_buff *skb = cardp->rx_skb; + struct fwsyncheader *syncfwheader; + struct bootcmdresp bootcmdresp; + + if (urb->status) { + lbs_deb_usbd(&cardp->udev->dev, + "URB status is failed during fw load\n"); + kfree_skb(skb); + return; + } + + if (cardp->fwdnldover) { + __le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); + + if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && + tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) { + pr_info("Firmware ready event received\n"); + wake_up(&cardp->fw_wq); + } else { + lbs_deb_usb("Waiting for confirmation; got %x %x\n", + le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1])); + if_usb_submit_rx_urb_fwload(cardp); + } + kfree_skb(skb); + return; + } + if (cardp->bootcmdresp <= 0) { + memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(bootcmdresp)); + + if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(cardp); + cardp->bootcmdresp = BOOT_CMD_RESP_OK; + lbs_deb_usbd(&cardp->udev->dev, + "Received valid boot command response\n"); + return; + } + if (bootcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { + if (bootcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || + bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || + bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) { + if (!cardp->bootcmdresp) + pr_info("Firmware already seems alive; resetting\n"); + cardp->bootcmdresp = -1; + } else { + pr_info("boot cmd response wrong magic number (0x%x)\n", + le32_to_cpu(bootcmdresp.magic)); + } + } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) && + (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) && + (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) { + pr_info("boot cmd response cmd_tag error (%d)\n", + bootcmdresp.cmd); + } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) { + pr_info("boot cmd response result error (%d)\n", + bootcmdresp.result); + } else { + cardp->bootcmdresp = 1; + lbs_deb_usbd(&cardp->udev->dev, + "Received valid boot command response\n"); + } + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(cardp); + return; + } + + syncfwheader = kmemdup(skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(struct fwsyncheader), GFP_ATOMIC); + if (!syncfwheader) { + lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n"); + kfree_skb(skb); + return; + } + + if (!syncfwheader->cmd) { + lbs_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n"); + lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n", + le32_to_cpu(syncfwheader->seqnum)); + cardp->CRC_OK = 1; + } else { + lbs_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n"); + cardp->CRC_OK = 0; + } + + kfree_skb(skb); + + /* Give device 5s to either write firmware to its RAM or eeprom */ + mod_timer(&cardp->fw_timeout, jiffies + (HZ*5)); + + if (cardp->fwfinalblk) { + cardp->fwdnldover = 1; + goto exit; + } + + if_usb_send_fw_pkt(cardp); + + exit: + if_usb_submit_rx_urb_fwload(cardp); + + kfree(syncfwheader); +} + +#define MRVDRV_MIN_PKT_LEN 30 + +static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, + struct if_usb_card *cardp, + struct lbs_private *priv) +{ + if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN + || recvlength < MRVDRV_MIN_PKT_LEN) { + lbs_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n"); + kfree_skb(skb); + return; + } + + skb_reserve(skb, IPFIELD_ALIGN_OFFSET); + skb_put(skb, recvlength); + skb_pull(skb, MESSAGE_HEADER_LEN); + + lbs_process_rxed_packet(priv, skb); +} + +static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, + struct sk_buff *skb, + struct if_usb_card *cardp, + struct lbs_private *priv) +{ + u8 i; + + if (recvlength > LBS_CMD_BUFFER_SIZE) { + lbs_deb_usbd(&cardp->udev->dev, + "The receive buffer is too large\n"); + kfree_skb(skb); + return; + } + + BUG_ON(!in_interrupt()); + + spin_lock(&priv->driver_lock); + + i = (priv->resp_idx == 0) ? 1 : 0; + BUG_ON(priv->resp_len[i]); + priv->resp_len[i] = (recvlength - MESSAGE_HEADER_LEN); + memcpy(priv->resp_buf[i], recvbuff + MESSAGE_HEADER_LEN, + priv->resp_len[i]); + kfree_skb(skb); + lbs_notify_command_response(priv, i); + + spin_unlock(&priv->driver_lock); + + lbs_deb_usbd(&cardp->udev->dev, + "Wake up main thread to handle cmd response\n"); +} + +/** + * if_usb_receive - read the packet into the upload buffer, + * wake up the main thread and initialise the Rx callack + * + * @urb: pointer to &struct urb + * returns: N/A + */ +static void if_usb_receive(struct urb *urb) +{ + struct if_usb_card *cardp = urb->context; + struct sk_buff *skb = cardp->rx_skb; + struct lbs_private *priv = cardp->priv; + int recvlength = urb->actual_length; + uint8_t *recvbuff = NULL; + uint32_t recvtype = 0; + __le32 *pkt = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); + uint32_t event; + + lbs_deb_enter(LBS_DEB_USB); + + if (recvlength) { + if (urb->status) { + lbs_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n", + urb->status); + kfree_skb(skb); + goto setup_for_next; + } + + recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; + recvtype = le32_to_cpu(pkt[0]); + lbs_deb_usbd(&cardp->udev->dev, + "Recv length = 0x%x, Recv type = 0x%X\n", + recvlength, recvtype); + } else if (urb->status) { + kfree_skb(skb); + goto rx_exit; + } + + switch (recvtype) { + case CMD_TYPE_DATA: + process_cmdtypedata(recvlength, skb, cardp, priv); + break; + + case CMD_TYPE_REQUEST: + process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); + break; + + case CMD_TYPE_INDICATION: + /* Event handling */ + event = le32_to_cpu(pkt[1]); + lbs_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event); + kfree_skb(skb); + + /* Icky undocumented magic special case */ + if (event & 0xffff0000) { + u32 trycount = (event & 0xffff0000) >> 16; + + lbs_send_tx_feedback(priv, trycount); + } else + lbs_queue_event(priv, event & 0xFF); + break; + + default: + lbs_deb_usbd(&cardp->udev->dev, "Unknown command type 0x%X\n", + recvtype); + kfree_skb(skb); + break; + } + +setup_for_next: + if_usb_submit_rx_urb(cardp); +rx_exit: + lbs_deb_leave(LBS_DEB_USB); +} + +/** + * if_usb_host_to_card - downloads data to FW + * @priv: pointer to &struct lbs_private structure + * @type: type of data + * @payload: pointer to data buffer + * @nb: number of bytes + * returns: 0 for success or negative error code + */ +static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, + uint8_t *payload, uint16_t nb) +{ + struct if_usb_card *cardp = priv->card; + + lbs_deb_usbd(&cardp->udev->dev,"*** type = %u\n", type); + lbs_deb_usbd(&cardp->udev->dev,"size after = %d\n", nb); + + if (type == MVMS_CMD) { + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); + priv->dnld_sent = DNLD_CMD_SENT; + } else { + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); + priv->dnld_sent = DNLD_DATA_SENT; + } + + memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); + + return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN); +} + +/** + * if_usb_issue_boot_command - issues Boot command to the Boot2 code + * @cardp: pointer to &if_usb_card + * @ivalue: 1:Boot from FW by USB-Download + * 2:Boot from FW in EEPROM + * returns: 0 for success or negative error code + */ +static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) +{ + struct bootcmd *bootcmd = cardp->ep_out_buf; + + /* Prepare command */ + bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); + bootcmd->cmd = ivalue; + memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); + + /* Issue command */ + usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd)); + + return 0; +} + + +/** + * check_fwfile_format - check the validity of Boot2/FW image + * + * @data: pointer to image + * @totlen: image length + * returns: 0 (good) or 1 (failure) + */ +static int check_fwfile_format(const uint8_t *data, uint32_t totlen) +{ + uint32_t bincmd, exit; + uint32_t blksize, offset, len; + int ret; + + ret = 1; + exit = len = 0; + + do { + struct fwheader *fwh = (void *)data; + + bincmd = le32_to_cpu(fwh->dnldcmd); + blksize = le32_to_cpu(fwh->datalength); + switch (bincmd) { + case FW_HAS_DATA_TO_RECV: + offset = sizeof(struct fwheader) + blksize; + data += offset; + len += offset; + if (len >= totlen) + exit = 1; + break; + case FW_HAS_LAST_BLOCK: + exit = 1; + ret = 0; + break; + default: + exit = 1; + break; + } + } while (!exit); + + if (ret) + pr_err("firmware file format check FAIL\n"); + else + lbs_deb_fw("firmware file format check PASS\n"); + + return ret; +} + +static void if_usb_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *fw, + const struct firmware *unused) +{ + struct if_usb_card *cardp = priv->card; + int i = 0; + static int reset_count = 10; + + lbs_deb_enter(LBS_DEB_USB); + + if (ret) { + pr_err("failed to find firmware (%d)\n", ret); + goto done; + } + + cardp->fw = fw; + if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { + ret = -EINVAL; + goto done; + } + + /* Cancel any pending usb business */ + usb_kill_urb(cardp->rx_urb); + usb_kill_urb(cardp->tx_urb); + + cardp->fwlastblksent = 0; + cardp->fwdnldover = 0; + cardp->totalbytes = 0; + cardp->fwfinalblk = 0; + cardp->bootcmdresp = 0; + +restart: + if (if_usb_submit_rx_urb_fwload(cardp) < 0) { + lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); + ret = -EIO; + goto done; + } + + cardp->bootcmdresp = 0; + do { + int j = 0; + i++; + if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); + /* wait for command response */ + do { + j++; + msleep_interruptible(100); + } while (cardp->bootcmdresp == 0 && j < 10); + } while (cardp->bootcmdresp == 0 && i < 5); + + if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) { + /* Return to normal operation */ + ret = -EOPNOTSUPP; + usb_kill_urb(cardp->rx_urb); + usb_kill_urb(cardp->tx_urb); + if (if_usb_submit_rx_urb(cardp) < 0) + ret = -EIO; + goto done; + } else if (cardp->bootcmdresp <= 0) { + if (--reset_count >= 0) { + if_usb_reset_device(cardp); + goto restart; + } + ret = -EIO; + goto done; + } + + i = 0; + + cardp->totalbytes = 0; + cardp->fwlastblksent = 0; + cardp->CRC_OK = 1; + cardp->fwdnldover = 0; + cardp->fwseqnum = -1; + cardp->totalbytes = 0; + cardp->fwfinalblk = 0; + + /* Send the first firmware packet... */ + if_usb_send_fw_pkt(cardp); + + /* ... and wait for the process to complete */ + wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover); + + del_timer_sync(&cardp->fw_timeout); + usb_kill_urb(cardp->rx_urb); + + if (!cardp->fwdnldover) { + pr_info("failed to load fw, resetting device!\n"); + if (--reset_count >= 0) { + if_usb_reset_device(cardp); + goto restart; + } + + pr_info("FW download failure, time = %d ms\n", i * 100); + ret = -EIO; + goto done; + } + + cardp->priv->fw_ready = 1; + if_usb_submit_rx_urb(cardp); + + if (lbs_start_card(priv)) + goto done; + + if_usb_setup_firmware(priv); + + /* + * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware. + */ + priv->wol_criteria = EHS_REMOVE_WAKEUP; + if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL)) + priv->ehs_remove_supported = false; + + done: + cardp->fw = NULL; + lbs_deb_leave(LBS_DEB_USB); +} + + +#ifdef CONFIG_PM +static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct if_usb_card *cardp = usb_get_intfdata(intf); + struct lbs_private *priv = cardp->priv; + int ret; + + lbs_deb_enter(LBS_DEB_USB); + + if (priv->psstate != PS_STATE_FULL_POWER) { + ret = -1; + goto out; + } + +#ifdef CONFIG_OLPC + if (machine_is_olpc()) { + if (priv->wol_criteria == EHS_REMOVE_WAKEUP) + olpc_ec_wakeup_clear(EC_SCI_SRC_WLAN); + else + olpc_ec_wakeup_set(EC_SCI_SRC_WLAN); + } +#endif + + ret = lbs_suspend(priv); + if (ret) + goto out; + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + out: + lbs_deb_leave(LBS_DEB_USB); + return ret; +} + +static int if_usb_resume(struct usb_interface *intf) +{ + struct if_usb_card *cardp = usb_get_intfdata(intf); + struct lbs_private *priv = cardp->priv; + + lbs_deb_enter(LBS_DEB_USB); + + if_usb_submit_rx_urb(cardp); + + lbs_resume(priv); + + lbs_deb_leave(LBS_DEB_USB); + return 0; +} +#else +#define if_usb_suspend NULL +#define if_usb_resume NULL +#endif + +static struct usb_driver if_usb_driver = { + .name = DRV_NAME, + .probe = if_usb_probe, + .disconnect = if_usb_disconnect, + .id_table = if_usb_table, + .suspend = if_usb_suspend, + .resume = if_usb_resume, + .reset_resume = if_usb_resume, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(if_usb_driver); + +MODULE_DESCRIPTION("8388 USB WLAN Driver"); +MODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/marvell/libertas/if_usb.h b/drivers/net/wireless/marvell/libertas/if_usb.h new file mode 100644 index 000000000000..6e42eac331de --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_usb.h @@ -0,0 +1,106 @@ +#ifndef _LBS_IF_USB_H +#define _LBS_IF_USB_H + +#include +#include + +struct lbs_private; + +/* + * This file contains definition for USB interface. + */ +#define CMD_TYPE_REQUEST 0xF00DFACE +#define CMD_TYPE_DATA 0xBEADC0DE +#define CMD_TYPE_INDICATION 0xBEEFFACE + +#define IPFIELD_ALIGN_OFFSET 2 + +#define BOOT_CMD_FW_BY_USB 0x01 +#define BOOT_CMD_FW_IN_EEPROM 0x02 +#define BOOT_CMD_UPDATE_BOOT2 0x03 +#define BOOT_CMD_UPDATE_FW 0x04 +#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */ + +struct bootcmd +{ + __le32 magic; + uint8_t cmd; + uint8_t pad[11]; +}; + +#define BOOT_CMD_RESP_OK 0x0001 +#define BOOT_CMD_RESP_FAIL 0x0000 +#define BOOT_CMD_RESP_NOT_SUPPORTED 0x0002 + +struct bootcmdresp +{ + __le32 magic; + uint8_t cmd; + uint8_t result; + uint8_t pad[2]; +}; + +/* USB card description structure*/ +struct if_usb_card { + struct usb_device *udev; + uint32_t model; /* MODEL_* */ + struct urb *rx_urb, *tx_urb; + struct lbs_private *priv; + + struct sk_buff *rx_skb; + + uint8_t ep_in; + uint8_t ep_out; + + /* bootcmdresp == 0 means command is pending + * bootcmdresp < 0 means error + * bootcmdresp > 0 is a BOOT_CMD_RESP_* from firmware + */ + int8_t bootcmdresp; + + int ep_in_size; + + void *ep_out_buf; + int ep_out_size; + + const struct firmware *fw; + struct timer_list fw_timeout; + wait_queue_head_t fw_wq; + uint32_t fwseqnum; + uint32_t totalbytes; + uint32_t fwlastblksent; + uint8_t CRC_OK; + uint8_t fwdnldover; + uint8_t fwfinalblk; + uint8_t surprise_removed; + + __le16 boot2_version; +}; + +/* fwheader */ +struct fwheader { + __le32 dnldcmd; + __le32 baseaddr; + __le32 datalength; + __le32 CRC; +}; + +#define FW_MAX_DATA_BLK_SIZE 600 +/* FWData */ +struct fwdata { + struct fwheader hdr; + __le32 seqnum; + uint8_t data[0]; +}; + +/* fwsyncheader */ +struct fwsyncheader { + __le32 cmd; + __le32 seqnum; +}; + +#define FW_HAS_DATA_TO_RECV 0x00000001 +#define FW_HAS_LAST_BLOCK 0x00000004 + + +#endif diff --git a/drivers/net/wireless/marvell/libertas/main.c b/drivers/net/wireless/marvell/libertas/main.c new file mode 100644 index 000000000000..8079560f4965 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/main.c @@ -0,0 +1,1225 @@ +/* + * This file contains the major functions in WLAN + * driver. It includes init, exit, open, close and main + * thread etc.. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "host.h" +#include "decl.h" +#include "dev.h" +#include "cfg.h" +#include "debugfs.h" +#include "cmd.h" +#include "mesh.h" + +#define DRIVER_RELEASE_VERSION "323.p0" +const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION +#ifdef DEBUG + "-dbg" +#endif + ""; + + +/* Module parameters */ +unsigned int lbs_debug; +EXPORT_SYMBOL_GPL(lbs_debug); +module_param_named(libertas_debug, lbs_debug, int, 0644); + +unsigned int lbs_disablemesh; +EXPORT_SYMBOL_GPL(lbs_disablemesh); +module_param_named(libertas_disablemesh, lbs_disablemesh, int, 0644); + + +/* + * This global structure is used to send the confirm_sleep command as + * fast as possible down to the firmware. + */ +struct cmd_confirm_sleep confirm_sleep; + + +/* + * the table to keep region code + */ +u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE] = + { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; + +/* + * FW rate table. FW refers to rates by their index in this table, not by the + * rate value itself. Values of 0x00 are + * reserved positions. + */ +static u8 fw_data_rates[MAX_RATES] = + { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, + 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 +}; + +/** + * lbs_fw_index_to_data_rate - use index to get the data rate + * + * @idx: The index of data rate + * returns: data rate or 0 + */ +u32 lbs_fw_index_to_data_rate(u8 idx) +{ + if (idx >= sizeof(fw_data_rates)) + idx = 0; + return fw_data_rates[idx]; +} + +/** + * lbs_data_rate_to_fw_index - use rate to get the index + * + * @rate: data rate + * returns: index or 0 + */ +u8 lbs_data_rate_to_fw_index(u32 rate) +{ + u8 i; + + if (!rate) + return 0; + + for (i = 0; i < sizeof(fw_data_rates); i++) { + if (rate == fw_data_rates[i]) + return i; + } + return 0; +} + +int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type) +{ + int ret = 0; + + switch (type) { + case NL80211_IFTYPE_MONITOR: + ret = lbs_set_monitor_mode(priv, 1); + break; + case NL80211_IFTYPE_STATION: + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) + ret = lbs_set_monitor_mode(priv, 0); + if (!ret) + ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1); + break; + case NL80211_IFTYPE_ADHOC: + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) + ret = lbs_set_monitor_mode(priv, 0); + if (!ret) + ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2); + break; + default: + ret = -ENOTSUPP; + } + return ret; +} + +int lbs_start_iface(struct lbs_private *priv) +{ + struct cmd_ds_802_11_mac_address cmd; + int ret; + + if (priv->power_restore) { + ret = priv->power_restore(priv); + if (ret) + return ret; + } + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + memcpy(cmd.macadd, priv->current_addr, ETH_ALEN); + + ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); + if (ret) { + lbs_deb_net("set MAC address failed\n"); + goto err; + } + + ret = lbs_set_iface_type(priv, priv->wdev->iftype); + if (ret) { + lbs_deb_net("set iface type failed\n"); + goto err; + } + + ret = lbs_set_11d_domain_info(priv); + if (ret) { + lbs_deb_net("set 11d domain info failed\n"); + goto err; + } + + lbs_update_channel(priv); + + priv->iface_running = true; + return 0; + +err: + if (priv->power_save) + priv->power_save(priv); + return ret; +} + +/** + * lbs_dev_open - open the ethX interface + * + * @dev: A pointer to &net_device structure + * returns: 0 or -EBUSY if monitor mode active + */ +static int lbs_dev_open(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + int ret = 0; + + lbs_deb_enter(LBS_DEB_NET); + if (!priv->iface_running) { + ret = lbs_start_iface(priv); + if (ret) + goto out; + } + + spin_lock_irq(&priv->driver_lock); + + netif_carrier_off(dev); + + if (!priv->tx_pending_len) + netif_wake_queue(dev); + + spin_unlock_irq(&priv->driver_lock); + +out: + lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); + return ret; +} + +static bool lbs_command_queue_empty(struct lbs_private *priv) +{ + unsigned long flags; + bool ret; + spin_lock_irqsave(&priv->driver_lock, flags); + ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq); + spin_unlock_irqrestore(&priv->driver_lock, flags); + return ret; +} + +int lbs_stop_iface(struct lbs_private *priv) +{ + unsigned long flags; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MAIN); + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->iface_running = false; + kfree_skb(priv->currenttxskb); + priv->currenttxskb = NULL; + priv->tx_pending_len = 0; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + cancel_work_sync(&priv->mcast_work); + del_timer_sync(&priv->tx_lockup_timer); + + /* Disable command processing, and wait for all commands to complete */ + lbs_deb_main("waiting for commands to complete\n"); + wait_event(priv->waitq, lbs_command_queue_empty(priv)); + lbs_deb_main("all commands completed\n"); + + if (priv->power_save) + ret = priv->power_save(priv); + + lbs_deb_leave(LBS_DEB_MAIN); + return ret; +} + +/** + * lbs_eth_stop - close the ethX interface + * + * @dev: A pointer to &net_device structure + * returns: 0 + */ +static int lbs_eth_stop(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + + lbs_deb_enter(LBS_DEB_NET); + + if (priv->connect_status == LBS_CONNECTED) + lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING); + + spin_lock_irq(&priv->driver_lock); + netif_stop_queue(dev); + spin_unlock_irq(&priv->driver_lock); + + lbs_update_mcast(priv); + cancel_delayed_work_sync(&priv->scan_work); + if (priv->scan_req) + lbs_scan_done(priv); + + netif_carrier_off(priv->dev); + + if (!lbs_iface_active(priv)) + lbs_stop_iface(priv); + + lbs_deb_leave(LBS_DEB_NET); + return 0; +} + +void lbs_host_to_card_done(struct lbs_private *priv) +{ + unsigned long flags; + + lbs_deb_enter(LBS_DEB_THREAD); + + spin_lock_irqsave(&priv->driver_lock, flags); + del_timer(&priv->tx_lockup_timer); + + priv->dnld_sent = DNLD_RES_RECEIVED; + + /* Wake main thread if commands are pending */ + if (!priv->cur_cmd || priv->tx_pending_len > 0) { + if (!priv->wakeup_dev_required) + wake_up(&priv->waitq); + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_THREAD); +} +EXPORT_SYMBOL_GPL(lbs_host_to_card_done); + +int lbs_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = 0; + struct lbs_private *priv = dev->ml_priv; + struct sockaddr *phwaddr = addr; + + lbs_deb_enter(LBS_DEB_NET); + + /* + * Can only set MAC address when all interfaces are down, to be written + * to the hardware when one of them is brought up. + */ + if (lbs_iface_active(priv)) + return -EBUSY; + + /* In case it was called from the mesh device */ + dev = priv->dev; + + memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN); + memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN); + if (priv->mesh_dev) + memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN); + + lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); + return ret; +} + + +static inline int mac_in_list(unsigned char *list, int list_len, + unsigned char *mac) +{ + while (list_len) { + if (!memcmp(list, mac, ETH_ALEN)) + return 1; + list += ETH_ALEN; + list_len--; + } + return 0; +} + + +static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, + struct net_device *dev, int nr_addrs) +{ + int i = nr_addrs; + struct netdev_hw_addr *ha; + int cnt; + + if ((dev->flags & (IFF_UP|IFF_MULTICAST)) != (IFF_UP|IFF_MULTICAST)) + return nr_addrs; + + netif_addr_lock_bh(dev); + cnt = netdev_mc_count(dev); + netdev_for_each_mc_addr(ha, dev) { + if (mac_in_list(cmd->maclist, nr_addrs, ha->addr)) { + lbs_deb_net("mcast address %s:%pM skipped\n", dev->name, + ha->addr); + cnt--; + continue; + } + + if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE) + break; + memcpy(&cmd->maclist[6*i], ha->addr, ETH_ALEN); + lbs_deb_net("mcast address %s:%pM added to filter\n", dev->name, + ha->addr); + i++; + cnt--; + } + netif_addr_unlock_bh(dev); + if (cnt) + return -EOVERFLOW; + + return i; +} + +void lbs_update_mcast(struct lbs_private *priv) +{ + struct cmd_ds_mac_multicast_adr mcast_cmd; + int dev_flags = 0; + int nr_addrs; + int old_mac_control = priv->mac_control; + + lbs_deb_enter(LBS_DEB_NET); + + if (netif_running(priv->dev)) + dev_flags |= priv->dev->flags; + if (priv->mesh_dev && netif_running(priv->mesh_dev)) + dev_flags |= priv->mesh_dev->flags; + + if (dev_flags & IFF_PROMISC) { + priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE; + priv->mac_control &= ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE | + CMD_ACT_MAC_MULTICAST_ENABLE); + goto out_set_mac_control; + } else if (dev_flags & IFF_ALLMULTI) { + do_allmulti: + priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; + priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | + CMD_ACT_MAC_MULTICAST_ENABLE); + goto out_set_mac_control; + } + + /* Once for priv->dev, again for priv->mesh_dev if it exists */ + nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->dev, 0); + if (nr_addrs >= 0 && priv->mesh_dev) + nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->mesh_dev, nr_addrs); + if (nr_addrs < 0) + goto do_allmulti; + + if (nr_addrs) { + int size = offsetof(struct cmd_ds_mac_multicast_adr, + maclist[6*nr_addrs]); + + mcast_cmd.action = cpu_to_le16(CMD_ACT_SET); + mcast_cmd.hdr.size = cpu_to_le16(size); + mcast_cmd.nr_of_adrs = cpu_to_le16(nr_addrs); + + lbs_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &mcast_cmd.hdr, size); + + priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; + } else + priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; + + priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | + CMD_ACT_MAC_ALL_MULTICAST_ENABLE); + out_set_mac_control: + if (priv->mac_control != old_mac_control) + lbs_set_mac_control(priv); + + lbs_deb_leave(LBS_DEB_NET); +} + +static void lbs_set_mcast_worker(struct work_struct *work) +{ + struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work); + lbs_update_mcast(priv); +} + +void lbs_set_multicast_list(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + + schedule_work(&priv->mcast_work); +} + +/** + * lbs_thread - handles the major jobs in the LBS driver. + * It handles all events generated by firmware, RX data received + * from firmware and TX data sent from kernel. + * + * @data: A pointer to &lbs_thread structure + * returns: 0 + */ +static int lbs_thread(void *data) +{ + struct net_device *dev = data; + struct lbs_private *priv = dev->ml_priv; + wait_queue_t wait; + + lbs_deb_enter(LBS_DEB_THREAD); + + init_waitqueue_entry(&wait, current); + + for (;;) { + int shouldsleep; + u8 resp_idx; + + lbs_deb_thread("1: currenttxskb %p, dnld_sent %d\n", + priv->currenttxskb, priv->dnld_sent); + + add_wait_queue(&priv->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irq(&priv->driver_lock); + + if (kthread_should_stop()) + shouldsleep = 0; /* Bye */ + else if (priv->surpriseremoved) + shouldsleep = 1; /* We need to wait until we're _told_ to die */ + else if (priv->psstate == PS_STATE_SLEEP) + shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ + else if (priv->cmd_timed_out) + shouldsleep = 0; /* Command timed out. Recover */ + else if (!priv->fw_ready) + shouldsleep = 1; /* Firmware not ready. We're waiting for it */ + else if (priv->dnld_sent) + shouldsleep = 1; /* Something is en route to the device already */ + else if (priv->tx_pending_len > 0) + shouldsleep = 0; /* We've a packet to send */ + else if (priv->resp_len[priv->resp_idx]) + shouldsleep = 0; /* We have a command response */ + else if (priv->cur_cmd) + shouldsleep = 1; /* Can't send a command; one already running */ + else if (!list_empty(&priv->cmdpendingq) && + !(priv->wakeup_dev_required)) + shouldsleep = 0; /* We have a command to send */ + else if (kfifo_len(&priv->event_fifo)) + shouldsleep = 0; /* We have an event to process */ + else + shouldsleep = 1; /* No command */ + + if (shouldsleep) { + lbs_deb_thread("sleeping, connect_status %d, " + "psmode %d, psstate %d\n", + priv->connect_status, + priv->psmode, priv->psstate); + spin_unlock_irq(&priv->driver_lock); + schedule(); + } else + spin_unlock_irq(&priv->driver_lock); + + lbs_deb_thread("2: currenttxskb %p, dnld_send %d\n", + priv->currenttxskb, priv->dnld_sent); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&priv->waitq, &wait); + + lbs_deb_thread("3: currenttxskb %p, dnld_sent %d\n", + priv->currenttxskb, priv->dnld_sent); + + if (kthread_should_stop()) { + lbs_deb_thread("break from main thread\n"); + break; + } + + if (priv->surpriseremoved) { + lbs_deb_thread("adapter removed; waiting to die...\n"); + continue; + } + + lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n", + priv->currenttxskb, priv->dnld_sent); + + /* Process any pending command response */ + spin_lock_irq(&priv->driver_lock); + resp_idx = priv->resp_idx; + if (priv->resp_len[resp_idx]) { + spin_unlock_irq(&priv->driver_lock); + lbs_process_command_response(priv, + priv->resp_buf[resp_idx], + priv->resp_len[resp_idx]); + spin_lock_irq(&priv->driver_lock); + priv->resp_len[resp_idx] = 0; + } + spin_unlock_irq(&priv->driver_lock); + + /* Process hardware events, e.g. card removed, link lost */ + spin_lock_irq(&priv->driver_lock); + while (kfifo_len(&priv->event_fifo)) { + u32 event; + + if (kfifo_out(&priv->event_fifo, + (unsigned char *) &event, sizeof(event)) != + sizeof(event)) + break; + spin_unlock_irq(&priv->driver_lock); + lbs_process_event(priv, event); + spin_lock_irq(&priv->driver_lock); + } + spin_unlock_irq(&priv->driver_lock); + + if (priv->wakeup_dev_required) { + lbs_deb_thread("Waking up device...\n"); + /* Wake up device */ + if (priv->exit_deep_sleep(priv)) + lbs_deb_thread("Wakeup device failed\n"); + continue; + } + + /* command timeout stuff */ + if (priv->cmd_timed_out && priv->cur_cmd) { + struct cmd_ctrl_node *cmdnode = priv->cur_cmd; + + netdev_info(dev, "Timeout submitting command 0x%04x\n", + le16_to_cpu(cmdnode->cmdbuf->command)); + lbs_complete_command(priv, cmdnode, -ETIMEDOUT); + + /* Reset card, but only when it isn't in the process + * of being shutdown anyway. */ + if (!dev->dismantle && priv->reset_card) + priv->reset_card(priv); + } + priv->cmd_timed_out = 0; + + if (!priv->fw_ready) + continue; + + /* Check if we need to confirm Sleep Request received previously */ + if (priv->psstate == PS_STATE_PRE_SLEEP && + !priv->dnld_sent && !priv->cur_cmd) { + if (priv->connect_status == LBS_CONNECTED) { + lbs_deb_thread("pre-sleep, currenttxskb %p, " + "dnld_sent %d, cur_cmd %p\n", + priv->currenttxskb, priv->dnld_sent, + priv->cur_cmd); + + lbs_ps_confirm_sleep(priv); + } else { + /* workaround for firmware sending + * deauth/linkloss event immediately + * after sleep request; remove this + * after firmware fixes it + */ + priv->psstate = PS_STATE_AWAKE; + netdev_alert(dev, + "ignore PS_SleepConfirm in non-connected state\n"); + } + } + + /* The PS state is changed during processing of Sleep Request + * event above + */ + if ((priv->psstate == PS_STATE_SLEEP) || + (priv->psstate == PS_STATE_PRE_SLEEP)) + continue; + + if (priv->is_deep_sleep) + continue; + + /* Execute the next command */ + if (!priv->dnld_sent && !priv->cur_cmd) + lbs_execute_next_command(priv); + + spin_lock_irq(&priv->driver_lock); + if (!priv->dnld_sent && priv->tx_pending_len > 0) { + int ret = priv->hw_host_to_card(priv, MVMS_DAT, + priv->tx_pending_buf, + priv->tx_pending_len); + if (ret) { + lbs_deb_tx("host_to_card failed %d\n", ret); + priv->dnld_sent = DNLD_RES_RECEIVED; + } else { + mod_timer(&priv->tx_lockup_timer, + jiffies + (HZ * 5)); + } + priv->tx_pending_len = 0; + if (!priv->currenttxskb) { + /* We can wake the queues immediately if we aren't + waiting for TX feedback */ + if (priv->connect_status == LBS_CONNECTED) + netif_wake_queue(priv->dev); + if (priv->mesh_dev && + netif_running(priv->mesh_dev)) + netif_wake_queue(priv->mesh_dev); + } + } + spin_unlock_irq(&priv->driver_lock); + } + + del_timer(&priv->command_timer); + del_timer(&priv->tx_lockup_timer); + del_timer(&priv->auto_deepsleep_timer); + + lbs_deb_leave(LBS_DEB_THREAD); + return 0; +} + +/** + * lbs_setup_firmware - gets the HW spec from the firmware and sets + * some basic parameters + * + * @priv: A pointer to &struct lbs_private structure + * returns: 0 or -1 + */ +static int lbs_setup_firmware(struct lbs_private *priv) +{ + int ret = -1; + s16 curlevel = 0, minlevel = 0, maxlevel = 0; + + lbs_deb_enter(LBS_DEB_FW); + + /* Read MAC address from firmware */ + eth_broadcast_addr(priv->current_addr); + ret = lbs_update_hw_spec(priv); + if (ret) + goto done; + + /* Read power levels if available */ + ret = lbs_get_tx_power(priv, &curlevel, &minlevel, &maxlevel); + if (ret == 0) { + priv->txpower_cur = curlevel; + priv->txpower_min = minlevel; + priv->txpower_max = maxlevel; + } + + /* Send cmd to FW to enable 11D function */ + ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1); + if (ret) + goto done; + + ret = lbs_set_mac_control_sync(priv); +done: + lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); + return ret; +} + +int lbs_suspend(struct lbs_private *priv) +{ + int ret; + + lbs_deb_enter(LBS_DEB_FW); + + if (priv->is_deep_sleep) { + ret = lbs_set_deep_sleep(priv, 0); + if (ret) { + netdev_err(priv->dev, + "deep sleep cancellation failed: %d\n", ret); + return ret; + } + priv->deep_sleep_required = 1; + } + + ret = lbs_set_host_sleep(priv, 1); + + netif_device_detach(priv->dev); + if (priv->mesh_dev) + netif_device_detach(priv->mesh_dev); + + lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(lbs_suspend); + +int lbs_resume(struct lbs_private *priv) +{ + int ret; + + lbs_deb_enter(LBS_DEB_FW); + + ret = lbs_set_host_sleep(priv, 0); + + netif_device_attach(priv->dev); + if (priv->mesh_dev) + netif_device_attach(priv->mesh_dev); + + if (priv->deep_sleep_required) { + priv->deep_sleep_required = 0; + ret = lbs_set_deep_sleep(priv, 1); + if (ret) + netdev_err(priv->dev, + "deep sleep activation failed: %d\n", ret); + } + + if (priv->setup_fw_on_resume) + ret = lbs_setup_firmware(priv); + + lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(lbs_resume); + +/** + * lbs_cmd_timeout_handler - handles the timeout of command sending. + * It will re-send the same command again. + * + * @data: &struct lbs_private pointer + */ +static void lbs_cmd_timeout_handler(unsigned long data) +{ + struct lbs_private *priv = (struct lbs_private *)data; + unsigned long flags; + + lbs_deb_enter(LBS_DEB_CMD); + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!priv->cur_cmd) + goto out; + + netdev_info(priv->dev, "command 0x%04x timed out\n", + le16_to_cpu(priv->cur_cmd->cmdbuf->command)); + + priv->cmd_timed_out = 1; + + /* + * If the device didn't even acknowledge the command, reset the state + * so that we don't block all future commands due to this one timeout. + */ + if (priv->dnld_sent == DNLD_CMD_SENT) + priv->dnld_sent = DNLD_RES_RECEIVED; + + wake_up(&priv->waitq); +out: + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_CMD); +} + +/** + * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames + * to the hardware. This is known to frequently happen with SD8686 when + * waking up after a Wake-on-WLAN-triggered resume. + * + * @data: &struct lbs_private pointer + */ +static void lbs_tx_lockup_handler(unsigned long data) +{ + struct lbs_private *priv = (struct lbs_private *)data; + unsigned long flags; + + lbs_deb_enter(LBS_DEB_TX); + spin_lock_irqsave(&priv->driver_lock, flags); + + netdev_info(priv->dev, "TX lockup detected\n"); + if (priv->reset_card) + priv->reset_card(priv); + + priv->dnld_sent = DNLD_RES_RECEIVED; + wake_up_interruptible(&priv->waitq); + + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_TX); +} + +/** + * auto_deepsleep_timer_fn - put the device back to deep sleep mode when + * timer expires and no activity (command, event, data etc.) is detected. + * @data: &struct lbs_private pointer + * returns: N/A + */ +static void auto_deepsleep_timer_fn(unsigned long data) +{ + struct lbs_private *priv = (struct lbs_private *)data; + + lbs_deb_enter(LBS_DEB_CMD); + + if (priv->is_activity_detected) { + priv->is_activity_detected = 0; + } else { + if (priv->is_auto_deep_sleep_enabled && + (!priv->wakeup_dev_required) && + (priv->connect_status != LBS_CONNECTED)) { + struct cmd_header cmd; + + lbs_deb_main("Entering auto deep sleep mode...\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.size = cpu_to_le16(sizeof(cmd)); + lbs_cmd_async(priv, CMD_802_11_DEEP_SLEEP, &cmd, + sizeof(cmd)); + } + } + mod_timer(&priv->auto_deepsleep_timer , jiffies + + (priv->auto_deep_sleep_timeout * HZ)/1000); + lbs_deb_leave(LBS_DEB_CMD); +} + +int lbs_enter_auto_deep_sleep(struct lbs_private *priv) +{ + lbs_deb_enter(LBS_DEB_SDIO); + + priv->is_auto_deep_sleep_enabled = 1; + if (priv->is_deep_sleep) + priv->wakeup_dev_required = 1; + mod_timer(&priv->auto_deepsleep_timer , + jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000); + + lbs_deb_leave(LBS_DEB_SDIO); + return 0; +} + +int lbs_exit_auto_deep_sleep(struct lbs_private *priv) +{ + lbs_deb_enter(LBS_DEB_SDIO); + + priv->is_auto_deep_sleep_enabled = 0; + priv->auto_deep_sleep_timeout = 0; + del_timer(&priv->auto_deepsleep_timer); + + lbs_deb_leave(LBS_DEB_SDIO); + return 0; +} + +static int lbs_init_adapter(struct lbs_private *priv) +{ + int ret; + + lbs_deb_enter(LBS_DEB_MAIN); + + eth_broadcast_addr(priv->current_addr); + + priv->connect_status = LBS_DISCONNECTED; + priv->channel = DEFAULT_AD_HOC_CHANNEL; + priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; + priv->radio_on = 1; + priv->psmode = LBS802_11POWERMODECAM; + priv->psstate = PS_STATE_FULL_POWER; + priv->is_deep_sleep = 0; + priv->is_auto_deep_sleep_enabled = 0; + priv->deep_sleep_required = 0; + priv->wakeup_dev_required = 0; + init_waitqueue_head(&priv->ds_awake_q); + init_waitqueue_head(&priv->scan_q); + priv->authtype_auto = 1; + priv->is_host_sleep_configured = 0; + priv->is_host_sleep_activated = 0; + init_waitqueue_head(&priv->host_sleep_q); + init_waitqueue_head(&priv->fw_waitq); + mutex_init(&priv->lock); + + setup_timer(&priv->command_timer, lbs_cmd_timeout_handler, + (unsigned long)priv); + setup_timer(&priv->tx_lockup_timer, lbs_tx_lockup_handler, + (unsigned long)priv); + setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, + (unsigned long)priv); + + INIT_LIST_HEAD(&priv->cmdfreeq); + INIT_LIST_HEAD(&priv->cmdpendingq); + + spin_lock_init(&priv->driver_lock); + + /* Allocate the command buffers */ + if (lbs_allocate_cmd_buffer(priv)) { + pr_err("Out of memory allocating command buffers\n"); + ret = -ENOMEM; + goto out; + } + priv->resp_idx = 0; + priv->resp_len[0] = priv->resp_len[1] = 0; + + /* Create the event FIFO */ + ret = kfifo_alloc(&priv->event_fifo, sizeof(u32) * 16, GFP_KERNEL); + if (ret) { + pr_err("Out of memory allocating event FIFO buffer\n"); + goto out; + } + +out: + lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); + + return ret; +} + +static void lbs_free_adapter(struct lbs_private *priv) +{ + lbs_deb_enter(LBS_DEB_MAIN); + + lbs_free_cmd_buffer(priv); + kfifo_free(&priv->event_fifo); + del_timer(&priv->command_timer); + del_timer(&priv->tx_lockup_timer); + del_timer(&priv->auto_deepsleep_timer); + + lbs_deb_leave(LBS_DEB_MAIN); +} + +static const struct net_device_ops lbs_netdev_ops = { + .ndo_open = lbs_dev_open, + .ndo_stop = lbs_eth_stop, + .ndo_start_xmit = lbs_hard_start_xmit, + .ndo_set_mac_address = lbs_set_mac_address, + .ndo_set_rx_mode = lbs_set_multicast_list, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +/** + * lbs_add_card - adds the card. It will probe the + * card, allocate the lbs_priv and initialize the device. + * + * @card: A pointer to card + * @dmdev: A pointer to &struct device + * returns: A pointer to &struct lbs_private structure + */ +struct lbs_private *lbs_add_card(void *card, struct device *dmdev) +{ + struct net_device *dev; + struct wireless_dev *wdev; + struct lbs_private *priv = NULL; + + lbs_deb_enter(LBS_DEB_MAIN); + + /* Allocate an Ethernet device and register it */ + wdev = lbs_cfg_alloc(dmdev); + if (IS_ERR(wdev)) { + pr_err("cfg80211 init failed\n"); + goto done; + } + + wdev->iftype = NL80211_IFTYPE_STATION; + priv = wdev_priv(wdev); + priv->wdev = wdev; + + if (lbs_init_adapter(priv)) { + pr_err("failed to initialize adapter structure\n"); + goto err_wdev; + } + + dev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup); + if (!dev) { + dev_err(dmdev, "no memory for network device instance\n"); + goto err_adapter; + } + + dev->ieee80211_ptr = wdev; + dev->ml_priv = priv; + SET_NETDEV_DEV(dev, dmdev); + wdev->netdev = dev; + priv->dev = dev; + + dev->netdev_ops = &lbs_netdev_ops; + dev->watchdog_timeo = 5 * HZ; + dev->ethtool_ops = &lbs_ethtool_ops; + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + + priv->card = card; + + strcpy(dev->name, "wlan%d"); + + lbs_deb_thread("Starting main thread...\n"); + init_waitqueue_head(&priv->waitq); + priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main"); + if (IS_ERR(priv->main_thread)) { + lbs_deb_thread("Error creating main thread.\n"); + goto err_ndev; + } + + priv->work_thread = create_singlethread_workqueue("lbs_worker"); + INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker); + + priv->wol_criteria = EHS_REMOVE_WAKEUP; + priv->wol_gpio = 0xff; + priv->wol_gap = 20; + priv->ehs_remove_supported = true; + + goto done; + + err_ndev: + free_netdev(dev); + + err_adapter: + lbs_free_adapter(priv); + + err_wdev: + lbs_cfg_free(priv); + + priv = NULL; + +done: + lbs_deb_leave_args(LBS_DEB_MAIN, "priv %p", priv); + return priv; +} +EXPORT_SYMBOL_GPL(lbs_add_card); + + +void lbs_remove_card(struct lbs_private *priv) +{ + struct net_device *dev = priv->dev; + + lbs_deb_enter(LBS_DEB_MAIN); + + lbs_remove_mesh(priv); + + if (priv->wiphy_registered) + lbs_scan_deinit(priv); + + lbs_wait_for_firmware_load(priv); + + /* worker thread destruction blocks on the in-flight command which + * should have been cleared already in lbs_stop_card(). + */ + lbs_deb_main("destroying worker thread\n"); + destroy_workqueue(priv->work_thread); + lbs_deb_main("done destroying worker thread\n"); + + if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { + priv->psmode = LBS802_11POWERMODECAM; + lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, true); + } + + if (priv->is_deep_sleep) { + priv->is_deep_sleep = 0; + wake_up_interruptible(&priv->ds_awake_q); + } + + priv->is_host_sleep_configured = 0; + priv->is_host_sleep_activated = 0; + wake_up_interruptible(&priv->host_sleep_q); + + /* Stop the thread servicing the interrupts */ + priv->surpriseremoved = 1; + kthread_stop(priv->main_thread); + + lbs_free_adapter(priv); + lbs_cfg_free(priv); + free_netdev(dev); + + lbs_deb_leave(LBS_DEB_MAIN); +} +EXPORT_SYMBOL_GPL(lbs_remove_card); + + +int lbs_rtap_supported(struct lbs_private *priv) +{ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) + return 1; + + /* newer firmware use a capability mask */ + return ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && + (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)); +} + + +int lbs_start_card(struct lbs_private *priv) +{ + struct net_device *dev = priv->dev; + int ret = -1; + + lbs_deb_enter(LBS_DEB_MAIN); + + /* poke the firmware */ + ret = lbs_setup_firmware(priv); + if (ret) + goto done; + + if (!lbs_disablemesh) + lbs_init_mesh(priv); + else + pr_info("%s: mesh disabled\n", dev->name); + + if (lbs_cfg_register(priv)) { + pr_err("cannot register device\n"); + goto done; + } + + if (lbs_mesh_activated(priv)) + lbs_start_mesh(priv); + + lbs_debugfs_init_one(priv, dev); + + netdev_info(dev, "Marvell WLAN 802.11 adapter\n"); + + ret = 0; + +done: + lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(lbs_start_card); + + +void lbs_stop_card(struct lbs_private *priv) +{ + struct net_device *dev; + + lbs_deb_enter(LBS_DEB_MAIN); + + if (!priv) + goto out; + dev = priv->dev; + + /* If the netdev isn't registered, it means that lbs_start_card() was + * never called so we have nothing to do here. */ + if (dev->reg_state != NETREG_REGISTERED) + goto out; + + netif_stop_queue(dev); + netif_carrier_off(dev); + + lbs_debugfs_remove_one(priv); + lbs_deinit_mesh(priv); + unregister_netdev(dev); + +out: + lbs_deb_leave(LBS_DEB_MAIN); +} +EXPORT_SYMBOL_GPL(lbs_stop_card); + + +void lbs_queue_event(struct lbs_private *priv, u32 event) +{ + unsigned long flags; + + lbs_deb_enter(LBS_DEB_THREAD); + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->psstate == PS_STATE_SLEEP) + priv->psstate = PS_STATE_AWAKE; + + kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32)); + + wake_up(&priv->waitq); + + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_THREAD); +} +EXPORT_SYMBOL_GPL(lbs_queue_event); + +void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx) +{ + lbs_deb_enter(LBS_DEB_THREAD); + + if (priv->psstate == PS_STATE_SLEEP) + priv->psstate = PS_STATE_AWAKE; + + /* Swap buffers by flipping the response index */ + BUG_ON(resp_idx > 1); + priv->resp_idx = resp_idx; + + wake_up(&priv->waitq); + + lbs_deb_leave(LBS_DEB_THREAD); +} +EXPORT_SYMBOL_GPL(lbs_notify_command_response); + +static int __init lbs_init_module(void) +{ + lbs_deb_enter(LBS_DEB_MAIN); + memset(&confirm_sleep, 0, sizeof(confirm_sleep)); + confirm_sleep.hdr.command = cpu_to_le16(CMD_802_11_PS_MODE); + confirm_sleep.hdr.size = cpu_to_le16(sizeof(confirm_sleep)); + confirm_sleep.action = cpu_to_le16(PS_MODE_ACTION_SLEEP_CONFIRMED); + lbs_debugfs_init(); + lbs_deb_leave(LBS_DEB_MAIN); + return 0; +} + +static void __exit lbs_exit_module(void) +{ + lbs_deb_enter(LBS_DEB_MAIN); + lbs_debugfs_remove(); + lbs_deb_leave(LBS_DEB_MAIN); +} + +module_init(lbs_init_module); +module_exit(lbs_exit_module); + +MODULE_DESCRIPTION("Libertas WLAN Driver Library"); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/marvell/libertas/mesh.c b/drivers/net/wireless/marvell/libertas/mesh.c new file mode 100644 index 000000000000..d0c881dd5846 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/mesh.c @@ -0,0 +1,1187 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mesh.h" +#include "decl.h" +#include "cmd.h" + + +static int lbs_add_mesh(struct lbs_private *priv); + +/*************************************************************************** + * Mesh command handling + */ + +static int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, + struct cmd_ds_mesh_access *cmd) +{ + int ret; + + lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); + + cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS); + cmd->hdr.size = cpu_to_le16(sizeof(*cmd)); + cmd->hdr.result = 0; + + cmd->action = cpu_to_le16(cmd_action); + + ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +static int __lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type) +{ + int ret; + u16 command = CMD_MESH_CONFIG_OLD; + + lbs_deb_enter(LBS_DEB_CMD); + + /* + * Command id is 0xac for v10 FW along with mesh interface + * id in bits 14-13-12. + */ + if (priv->mesh_tlv == TLV_TYPE_MESH_ID) + command = CMD_MESH_CONFIG | + (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET); + + cmd->hdr.command = cpu_to_le16(command); + cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config)); + cmd->hdr.result = 0; + + cmd->type = cpu_to_le16(type); + cmd->action = cpu_to_le16(action); + + ret = lbs_cmd_with_response(priv, command, cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +static int lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type) +{ + int ret; + + if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) + return -EOPNOTSUPP; + + ret = __lbs_mesh_config_send(priv, cmd, action, type); + return ret; +} + +/* This function is the CMD_MESH_CONFIG legacy function. It only handles the + * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG + * are all handled by preparing a struct cmd_ds_mesh_config and passing it to + * lbs_mesh_config_send. + */ +static int lbs_mesh_config(struct lbs_private *priv, uint16_t action, + uint16_t chan) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_meshie *ie; + + memset(&cmd, 0, sizeof(cmd)); + cmd.channel = cpu_to_le16(chan); + ie = (struct mrvl_meshie *)cmd.data; + + switch (action) { + case CMD_ACT_MESH_CONFIG_START: + ie->id = WLAN_EID_VENDOR_SPECIFIC; + ie->val.oui[0] = 0x00; + ie->val.oui[1] = 0x50; + ie->val.oui[2] = 0x43; + ie->val.type = MARVELL_MESH_IE_TYPE; + ie->val.subtype = MARVELL_MESH_IE_SUBTYPE; + ie->val.version = MARVELL_MESH_IE_VERSION; + ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP; + ie->val.active_metric_id = MARVELL_MESH_METRIC_ID; + ie->val.mesh_capability = MARVELL_MESH_CAPABILITY; + ie->val.mesh_id_len = priv->mesh_ssid_len; + memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len); + ie->len = sizeof(struct mrvl_meshie_val) - + IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len; + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val)); + break; + case CMD_ACT_MESH_CONFIG_STOP: + break; + default: + return -1; + } + lbs_deb_cmd("mesh config action %d type %x channel %d SSID %*pE\n", + action, priv->mesh_tlv, chan, priv->mesh_ssid_len, + priv->mesh_ssid); + + return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); +} + +int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel) +{ + priv->mesh_channel = channel; + return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel); +} + +static uint16_t lbs_mesh_get_channel(struct lbs_private *priv) +{ + return priv->mesh_channel ?: 1; +} + +/*************************************************************************** + * Mesh sysfs support + */ + +/* + * Attributes exported through sysfs + */ + +/** + * lbs_anycast_get - Get function for sysfs attribute anycast_mask + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t lbs_anycast_get(struct device *dev, + struct device_attribute *attr, char * buf) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + + memset(&mesh_access, 0, sizeof(mesh_access)); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access); + if (ret) + return ret; + + return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0])); +} + +/** + * lbs_anycast_set - Set function for sysfs attribute anycast_mask + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t lbs_anycast_set(struct device *dev, + struct device_attribute *attr, const char * buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + uint32_t datum; + int ret; + + memset(&mesh_access, 0, sizeof(mesh_access)); + sscanf(buf, "%x", &datum); + mesh_access.data[0] = cpu_to_le32(datum); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * lbs_prb_rsp_limit_get - Get function for sysfs attribute prb_rsp_limit + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t lbs_prb_rsp_limit_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + u32 retry_limit; + + memset(&mesh_access, 0, sizeof(mesh_access)); + mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, + &mesh_access); + if (ret) + return ret; + + retry_limit = le32_to_cpu(mesh_access.data[1]); + return snprintf(buf, 10, "%d\n", retry_limit); +} + +/** + * lbs_prb_rsp_limit_set - Set function for sysfs attribute prb_rsp_limit + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t lbs_prb_rsp_limit_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + unsigned long retry_limit; + + memset(&mesh_access, 0, sizeof(mesh_access)); + mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET); + + if (!kstrtoul(buf, 10, &retry_limit)) + return -ENOTSUPP; + if (retry_limit > 15) + return -ENOTSUPP; + + mesh_access.data[1] = cpu_to_le32(retry_limit); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, + &mesh_access); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * lbs_mesh_get - Get function for sysfs attribute mesh + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t lbs_mesh_get(struct device *dev, + struct device_attribute *attr, char * buf) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev); +} + +/** + * lbs_mesh_set - Set function for sysfs attribute mesh + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t lbs_mesh_set(struct device *dev, + struct device_attribute *attr, const char * buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + int enable; + + sscanf(buf, "%x", &enable); + enable = !!enable; + if (enable == !!priv->mesh_dev) + return count; + + if (enable) + lbs_add_mesh(priv); + else + lbs_remove_mesh(priv); + + return count; +} + +/* + * lbs_mesh attribute to be exported per ethX interface + * through sysfs (/sys/class/net/ethX/lbs_mesh) + */ +static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set); + +/* + * anycast_mask attribute to be exported per mshX interface + * through sysfs (/sys/class/net/mshX/anycast_mask) + */ +static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set); + +/* + * prb_rsp_limit attribute to be exported per mshX interface + * through sysfs (/sys/class/net/mshX/prb_rsp_limit) + */ +static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get, + lbs_prb_rsp_limit_set); + +static struct attribute *lbs_mesh_sysfs_entries[] = { + &dev_attr_anycast_mask.attr, + &dev_attr_prb_rsp_limit.attr, + NULL, +}; + +static const struct attribute_group lbs_mesh_attr_group = { + .attrs = lbs_mesh_sysfs_entries, +}; + + +/*************************************************************************** + * Persistent configuration support + */ + +static int mesh_get_default_parameters(struct device *dev, + struct mrvl_mesh_defaults *defs) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + int ret; + + memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET, + CMD_TYPE_MESH_GET_DEFAULTS); + + if (ret) + return -EOPNOTSUPP; + + memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults)); + + return 0; +} + +/** + * bootflag_get - Get function for sysfs attribute bootflag + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t bootflag_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag)); +} + +/** + * bootflag_set - Set function for sysfs attribute bootflag + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 1)) + return -EINVAL; + + *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum); + cmd.length = cpu_to_le16(sizeof(uint32_t)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_BOOTFLAG); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * boottime_get - Get function for sysfs attribute boottime + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t boottime_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 12, "%d\n", defs.boottime); +} + +/** + * boottime_set - Set function for sysfs attribute boottime + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t boottime_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* A too small boot time will result in the device booting into + * standalone (no-host) mode before the host can take control of it, + * so the change will be hard to revert. This may be a desired + * feature (e.g to configure a very fast boot time for devices that + * will not be attached to a host), but dangerous. So I'm enforcing a + * lower limit of 20 seconds: remove and recompile the driver if this + * does not work for you. + */ + datum = (datum < 20) ? 20 : datum; + cmd.data[0] = datum; + cmd.length = cpu_to_le16(sizeof(uint8_t)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_BOOTTIME); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * channel_get - Get function for sysfs attribute channel + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t channel_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel)); +} + +/** + * channel_set - Set function for sysfs attribute channel + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t channel_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if (ret != 1 || datum < 1 || datum > 11) + return -EINVAL; + + *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum); + cmd.length = cpu_to_le16(sizeof(uint16_t)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_DEF_CHANNEL); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * mesh_id_get - Get function for sysfs attribute mesh_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) { + dev_err(dev, "inconsistent mesh ID length\n"); + defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN; + } + + memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len); + buf[defs.meshie.val.mesh_id_len] = '\n'; + buf[defs.meshie.val.mesh_id_len + 1] = '\0'; + + return defs.meshie.val.mesh_id_len + 1; +} + +/** + * mesh_id_set - Set function for sysfs attribute mesh_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + int len; + int ret; + + if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1) + return -EINVAL; + + memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); + ie = (struct mrvl_meshie *) &cmd.data[0]; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + + len = count - 1; + memcpy(ie->val.mesh_id, buf, len); + /* SSID len */ + ie->val.mesh_id_len = len; + /* IE len */ + ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * protocol_id_get - Get function for sysfs attribute protocol_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t protocol_id_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id); +} + +/** + * protocol_id_set - Set function for sysfs attribute protocol_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t protocol_id_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + ie = (struct mrvl_meshie *) &cmd.data[0]; + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + /* update protocol id */ + ie->val.active_protocol_id = datum; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * metric_id_get - Get function for sysfs attribute metric_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t metric_id_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id); +} + +/** + * metric_id_set - Set function for sysfs attribute metric_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + ie = (struct mrvl_meshie *) &cmd.data[0]; + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + /* update metric id */ + ie->val.active_metric_id = datum; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * capability_get - Get function for sysfs attribute capability + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t capability_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability); +} + +/** + * capability_set - Set function for sysfs attribute capability + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t capability_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + ie = (struct mrvl_meshie *) &cmd.data[0]; + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + /* update value */ + ie->val.mesh_capability = datum; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + + +static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set); +static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set); +static DEVICE_ATTR(channel, 0644, channel_get, channel_set); +static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set); +static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set); +static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set); +static DEVICE_ATTR(capability, 0644, capability_get, capability_set); + +static struct attribute *boot_opts_attrs[] = { + &dev_attr_bootflag.attr, + &dev_attr_boottime.attr, + &dev_attr_channel.attr, + NULL +}; + +static const struct attribute_group boot_opts_group = { + .name = "boot_options", + .attrs = boot_opts_attrs, +}; + +static struct attribute *mesh_ie_attrs[] = { + &dev_attr_mesh_id.attr, + &dev_attr_protocol_id.attr, + &dev_attr_metric_id.attr, + &dev_attr_capability.attr, + NULL +}; + +static const struct attribute_group mesh_ie_group = { + .name = "mesh_ie", + .attrs = mesh_ie_attrs, +}; + +static void lbs_persist_config_init(struct net_device *dev) +{ + int ret; + ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group); + ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group); +} + +static void lbs_persist_config_remove(struct net_device *dev) +{ + sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group); + sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group); +} + + +/*************************************************************************** + * Initializing and starting, stopping mesh + */ + +/* + * Check mesh FW version and appropriately send the mesh start + * command + */ +int lbs_init_mesh(struct lbs_private *priv) +{ + int ret = 0; + + lbs_deb_enter(LBS_DEB_MESH); + + /* Determine mesh_fw_ver from fwrelease and fwcapinfo */ + /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */ + /* 5.110.22 have mesh command with 0xa3 command id */ + /* 10.0.0.p0 FW brings in mesh config command with different id */ + /* Check FW version MSB and initialize mesh_fw_ver */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) { + /* Enable mesh, if supported, and work out which TLV it uses. + 0x100 + 291 is an unofficial value used in 5.110.20.pXX + 0x100 + 37 is the official value used in 5.110.21.pXX + but we check them in that order because 20.pXX doesn't + give an error -- it just silently fails. */ + + /* 5.110.20.pXX firmware will fail the command if the channel + doesn't match the existing channel. But only if the TLV + is correct. If the channel is wrong, _BOTH_ versions will + give an error to 0x100+291, and allow 0x100+37 to succeed. + It's just that 5.110.20.pXX will not have done anything + useful */ + + priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID; + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) { + priv->mesh_tlv = TLV_TYPE_MESH_ID; + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) + priv->mesh_tlv = 0; + } + } else + if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && + (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) { + /* 10.0.0.pXX new firmwares should succeed with TLV + * 0x100+37; Do not invoke command with old TLV. + */ + priv->mesh_tlv = TLV_TYPE_MESH_ID; + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) + priv->mesh_tlv = 0; + } + + /* Stop meshing until interface is brought up */ + lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1); + + if (priv->mesh_tlv) { + sprintf(priv->mesh_ssid, "mesh"); + priv->mesh_ssid_len = 4; + ret = 1; + } + + lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); + return ret; +} + +void lbs_start_mesh(struct lbs_private *priv) +{ + lbs_add_mesh(priv); + + if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh)) + netdev_err(priv->dev, "cannot register lbs_mesh attribute\n"); +} + +int lbs_deinit_mesh(struct lbs_private *priv) +{ + struct net_device *dev = priv->dev; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MESH); + + if (priv->mesh_tlv) { + device_remove_file(&dev->dev, &dev_attr_lbs_mesh); + ret = 1; + } + + lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); + return ret; +} + + +/** + * lbs_mesh_stop - close the mshX interface + * + * @dev: A pointer to &net_device structure + * returns: 0 + */ +static int lbs_mesh_stop(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + + lbs_deb_enter(LBS_DEB_MESH); + lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, + lbs_mesh_get_channel(priv)); + + spin_lock_irq(&priv->driver_lock); + + netif_stop_queue(dev); + netif_carrier_off(dev); + + spin_unlock_irq(&priv->driver_lock); + + lbs_update_mcast(priv); + if (!lbs_iface_active(priv)) + lbs_stop_iface(priv); + + lbs_deb_leave(LBS_DEB_MESH); + return 0; +} + +/** + * lbs_mesh_dev_open - open the mshX interface + * + * @dev: A pointer to &net_device structure + * returns: 0 or -EBUSY if monitor mode active + */ +static int lbs_mesh_dev_open(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + int ret = 0; + + lbs_deb_enter(LBS_DEB_NET); + if (!priv->iface_running) { + ret = lbs_start_iface(priv); + if (ret) + goto out; + } + + spin_lock_irq(&priv->driver_lock); + + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + ret = -EBUSY; + spin_unlock_irq(&priv->driver_lock); + goto out; + } + + netif_carrier_on(dev); + + if (!priv->tx_pending_len) + netif_wake_queue(dev); + + spin_unlock_irq(&priv->driver_lock); + + ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, + lbs_mesh_get_channel(priv)); + +out: + lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); + return ret; +} + +static const struct net_device_ops mesh_netdev_ops = { + .ndo_open = lbs_mesh_dev_open, + .ndo_stop = lbs_mesh_stop, + .ndo_start_xmit = lbs_hard_start_xmit, + .ndo_set_mac_address = lbs_set_mac_address, + .ndo_set_rx_mode = lbs_set_multicast_list, +}; + +/** + * lbs_add_mesh - add mshX interface + * + * @priv: A pointer to the &struct lbs_private structure + * returns: 0 if successful, -X otherwise + */ +static int lbs_add_mesh(struct lbs_private *priv) +{ + struct net_device *mesh_dev = NULL; + struct wireless_dev *mesh_wdev; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MESH); + + /* Allocate a virtual mesh device */ + mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!mesh_wdev) { + lbs_deb_mesh("init mshX wireless device failed\n"); + ret = -ENOMEM; + goto done; + } + + mesh_dev = alloc_netdev(0, "msh%d", NET_NAME_UNKNOWN, ether_setup); + if (!mesh_dev) { + lbs_deb_mesh("init mshX device failed\n"); + ret = -ENOMEM; + goto err_free_wdev; + } + + mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT; + mesh_wdev->wiphy = priv->wdev->wiphy; + mesh_wdev->netdev = mesh_dev; + + mesh_dev->ml_priv = priv; + mesh_dev->ieee80211_ptr = mesh_wdev; + priv->mesh_dev = mesh_dev; + + mesh_dev->netdev_ops = &mesh_netdev_ops; + mesh_dev->ethtool_ops = &lbs_ethtool_ops; + eth_hw_addr_inherit(mesh_dev, priv->dev); + + SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent); + + mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + /* Register virtual mesh interface */ + ret = register_netdev(mesh_dev); + if (ret) { + pr_err("cannot register mshX virtual interface\n"); + goto err_free_netdev; + } + + ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); + if (ret) + goto err_unregister; + + lbs_persist_config_init(mesh_dev); + + /* Everything successful */ + ret = 0; + goto done; + +err_unregister: + unregister_netdev(mesh_dev); + +err_free_netdev: + free_netdev(mesh_dev); + +err_free_wdev: + kfree(mesh_wdev); + +done: + lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); + return ret; +} + +void lbs_remove_mesh(struct lbs_private *priv) +{ + struct net_device *mesh_dev; + + mesh_dev = priv->mesh_dev; + if (!mesh_dev) + return; + + lbs_deb_enter(LBS_DEB_MESH); + netif_stop_queue(mesh_dev); + netif_carrier_off(mesh_dev); + sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); + lbs_persist_config_remove(mesh_dev); + unregister_netdev(mesh_dev); + priv->mesh_dev = NULL; + kfree(mesh_dev->ieee80211_ptr); + free_netdev(mesh_dev); + lbs_deb_leave(LBS_DEB_MESH); +} + + +/*************************************************************************** + * Sending and receiving + */ +struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, + struct net_device *dev, struct rxpd *rxpd) +{ + if (priv->mesh_dev) { + if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) { + if (rxpd->rx_control & RxPD_MESH_FRAME) + dev = priv->mesh_dev; + } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) { + if (rxpd->u.bss.bss_num == MESH_IFACE_ID) + dev = priv->mesh_dev; + } + } + return dev; +} + + +void lbs_mesh_set_txpd(struct lbs_private *priv, + struct net_device *dev, struct txpd *txpd) +{ + if (dev == priv->mesh_dev) { + if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) + txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME); + else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) + txpd->u.bss.bss_num = MESH_IFACE_ID; + } +} + + +/*************************************************************************** + * Ethtool related + */ + +static const char * const mesh_stat_strings[] = { + "drop_duplicate_bcast", + "drop_ttl_zero", + "drop_no_fwd_route", + "drop_no_buffers", + "fwded_unicast_cnt", + "fwded_bcast_cnt", + "drop_blind_table", + "tx_failed_cnt" +}; + +void lbs_mesh_ethtool_get_stats(struct net_device *dev, + struct ethtool_stats *stats, uint64_t *data) +{ + struct lbs_private *priv = dev->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + + lbs_deb_enter(LBS_DEB_ETHTOOL); + + /* Get Mesh Statistics */ + ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access); + + if (ret) { + memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t))); + return; + } + + priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]); + priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]); + priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]); + priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]); + priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]); + priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]); + priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]); + priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]); + + data[0] = priv->mstats.fwd_drop_rbt; + data[1] = priv->mstats.fwd_drop_ttl; + data[2] = priv->mstats.fwd_drop_noroute; + data[3] = priv->mstats.fwd_drop_nobuf; + data[4] = priv->mstats.fwd_unicast_cnt; + data[5] = priv->mstats.fwd_bcast_cnt; + data[6] = priv->mstats.drop_blind; + data[7] = priv->mstats.tx_failed_cnt; + + lbs_deb_enter(LBS_DEB_ETHTOOL); +} + +int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset) +{ + struct lbs_private *priv = dev->ml_priv; + + if (sset == ETH_SS_STATS && dev == priv->mesh_dev) + return MESH_STATS_NUM; + + return -EOPNOTSUPP; +} + +void lbs_mesh_ethtool_get_strings(struct net_device *dev, + uint32_t stringset, uint8_t *s) +{ + int i; + + lbs_deb_enter(LBS_DEB_ETHTOOL); + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < MESH_STATS_NUM; i++) { + memcpy(s + i * ETH_GSTRING_LEN, + mesh_stat_strings[i], + ETH_GSTRING_LEN); + } + break; + } + lbs_deb_enter(LBS_DEB_ETHTOOL); +} diff --git a/drivers/net/wireless/marvell/libertas/mesh.h b/drivers/net/wireless/marvell/libertas/mesh.h new file mode 100644 index 000000000000..6603f341c874 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/mesh.h @@ -0,0 +1,77 @@ +/* + * Contains all definitions needed for the Libertas' MESH implementation. + */ +#ifndef _LBS_MESH_H_ +#define _LBS_MESH_H_ + + +#include +#include + +#include "host.h" +#include "dev.h" + +#ifdef CONFIG_LIBERTAS_MESH + +struct net_device; + +int lbs_init_mesh(struct lbs_private *priv); +void lbs_start_mesh(struct lbs_private *priv); +int lbs_deinit_mesh(struct lbs_private *priv); + +void lbs_remove_mesh(struct lbs_private *priv); + +static inline bool lbs_mesh_activated(struct lbs_private *priv) +{ + /* Mesh SSID is only programmed after successful init */ + return priv->mesh_ssid_len != 0; +} + +int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel); + +/* Sending / Receiving */ + +struct rxpd; +struct txpd; + +struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, + struct net_device *dev, struct rxpd *rxpd); +void lbs_mesh_set_txpd(struct lbs_private *priv, + struct net_device *dev, struct txpd *txpd); + + +/* Command handling */ + +struct cmd_ds_command; +struct cmd_ds_mesh_access; +struct cmd_ds_mesh_config; + + +/* Ethtool statistics */ + +struct ethtool_stats; + +void lbs_mesh_ethtool_get_stats(struct net_device *dev, + struct ethtool_stats *stats, uint64_t *data); +int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset); +void lbs_mesh_ethtool_get_strings(struct net_device *dev, + uint32_t stringset, uint8_t *s); + + +#else + +#define lbs_init_mesh(priv) +#define lbs_deinit_mesh(priv) +#define lbs_start_mesh(priv) +#define lbs_add_mesh(priv) +#define lbs_remove_mesh(priv) +#define lbs_mesh_set_dev(priv, dev, rxpd) (dev) +#define lbs_mesh_set_txpd(priv, dev, txpd) +#define lbs_mesh_set_channel(priv, channel) (0) +#define lbs_mesh_activated(priv) (false) + +#endif + + + +#endif diff --git a/drivers/net/wireless/marvell/libertas/radiotap.h b/drivers/net/wireless/marvell/libertas/radiotap.h new file mode 100644 index 000000000000..b3c8ea6d610e --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/radiotap.h @@ -0,0 +1,44 @@ +#include + +struct tx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 rate; + u8 txpower; + u8 rts_retries; + u8 data_retries; +} __packed; + +#define TX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ + (1 << IEEE80211_RADIOTAP_RTS_RETRIES) | \ + (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | \ + 0) + +#define IEEE80211_FC_VERSION_MASK 0x0003 +#define IEEE80211_FC_TYPE_MASK 0x000c +#define IEEE80211_FC_TYPE_MGT 0x0000 +#define IEEE80211_FC_TYPE_CTL 0x0004 +#define IEEE80211_FC_TYPE_DATA 0x0008 +#define IEEE80211_FC_SUBTYPE_MASK 0x00f0 +#define IEEE80211_FC_TOFROMDS_MASK 0x0300 +#define IEEE80211_FC_TODS_MASK 0x0100 +#define IEEE80211_FC_FROMDS_MASK 0x0200 +#define IEEE80211_FC_NODS 0x0000 +#define IEEE80211_FC_TODS 0x0100 +#define IEEE80211_FC_FROMDS 0x0200 +#define IEEE80211_FC_DSTODS 0x0300 + +struct rx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 flags; + u8 rate; + u8 antsignal; +} __packed; + +#define RX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ + 0) + diff --git a/drivers/net/wireless/marvell/libertas/rx.c b/drivers/net/wireless/marvell/libertas/rx.c new file mode 100644 index 000000000000..e446fed7b345 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/rx.c @@ -0,0 +1,286 @@ +/* + * This file contains the handling of RX in wlan driver. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "dev.h" +#include "mesh.h" + +struct eth803hdr { + u8 dest_addr[6]; + u8 src_addr[6]; + u16 h803_len; +} __packed; + +struct rfc1042hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + u16 snap_type; +} __packed; + +struct rxpackethdr { + struct eth803hdr eth803_hdr; + struct rfc1042hdr rfc1042_hdr; +} __packed; + +struct rx80211packethdr { + struct rxpd rx_pd; + void *eth80211_hdr; +} __packed; + +static int process_rxed_802_11_packet(struct lbs_private *priv, + struct sk_buff *skb); + +/** + * lbs_process_rxed_packet - processes received packet and forwards it + * to kernel/upper layer + * + * @priv: A pointer to &struct lbs_private + * @skb: A pointer to skb which includes the received packet + * returns: 0 or -1 + */ +int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) +{ + int ret = 0; + struct net_device *dev = priv->dev; + struct rxpackethdr *p_rx_pkt; + struct rxpd *p_rx_pd; + int hdrchop; + struct ethhdr *p_ethhdr; + static const u8 rfc1042_eth_hdr[] = { + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 + }; + + lbs_deb_enter(LBS_DEB_RX); + + BUG_ON(!skb); + + skb->ip_summed = CHECKSUM_NONE; + + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + ret = process_rxed_802_11_packet(priv, skb); + goto done; + } + + p_rx_pd = (struct rxpd *) skb->data; + p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd + + le32_to_cpu(p_rx_pd->pkt_ptr)); + + dev = lbs_mesh_set_dev(priv, dev, p_rx_pd); + + lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_deb_rx("rx err: frame received with bad length\n"); + dev->stats.rx_length_errors++; + ret = -EINVAL; + dev_kfree_skb(skb); + goto done; + } + + lbs_deb_rx("rx data: skb->len - pkt_ptr = %d-%zd = %zd\n", + skb->len, (size_t)le32_to_cpu(p_rx_pd->pkt_ptr), + skb->len - (size_t)le32_to_cpu(p_rx_pd->pkt_ptr)); + + lbs_deb_hex(LBS_DEB_RX, "RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_rx_pkt->eth803_hdr.dest_addr)); + lbs_deb_hex(LBS_DEB_RX, "RX Data: Src", p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_rx_pkt->eth803_hdr.src_addr)); + + if (memcmp(&p_rx_pkt->rfc1042_hdr, + rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { + /* + * Replace the 803 header and rfc1042 header (llc/snap) with an + * EthernetII header, keep the src/dst and snap_type (ethertype) + * + * The firmware only passes up SNAP frames converting + * all RX Data from 802.11 to 802.2/LLC/SNAP frames. + * + * To create the Ethernet II, just move the src, dst address right + * before the snap_type. + */ + p_ethhdr = (struct ethhdr *) + ((u8 *) &p_rx_pkt->eth803_hdr + + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr) + - sizeof(p_rx_pkt->eth803_hdr.dest_addr) + - sizeof(p_rx_pkt->eth803_hdr.src_addr) + - sizeof(p_rx_pkt->rfc1042_hdr.snap_type)); + + memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_ethhdr->h_source)); + memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_ethhdr->h_dest)); + + /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header + * that was removed + */ + hdrchop = (u8 *)p_ethhdr - (u8 *)p_rx_pd; + } else { + lbs_deb_hex(LBS_DEB_RX, "RX Data: LLC/SNAP", + (u8 *) &p_rx_pkt->rfc1042_hdr, + sizeof(p_rx_pkt->rfc1042_hdr)); + + /* Chop off the rxpd */ + hdrchop = (u8 *)&p_rx_pkt->eth803_hdr - (u8 *)p_rx_pd; + } + + /* Chop off the leading header bytes so the skb points to the start of + * either the reconstructed EthII frame or the 802.2/llc/snap frame + */ + skb_pull(skb, hdrchop); + + priv->cur_rate = lbs_fw_index_to_data_rate(p_rx_pd->rx_rate); + + lbs_deb_rx("rx data: size of actual packet %d\n", skb->len); + dev->stats.rx_bytes += skb->len; + dev->stats.rx_packets++; + + skb->protocol = eth_type_trans(skb, dev); + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + + ret = 0; +done: + lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(lbs_process_rxed_packet); + +/** + * convert_mv_rate_to_radiotap - converts Tx/Rx rates from Marvell WLAN format + * (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s) + * + * @rate: Input rate + * returns: Output Rate (0 if invalid) + */ +static u8 convert_mv_rate_to_radiotap(u8 rate) +{ + switch (rate) { + case 0: /* 1 Mbps */ + return 2; + case 1: /* 2 Mbps */ + return 4; + case 2: /* 5.5 Mbps */ + return 11; + case 3: /* 11 Mbps */ + return 22; + /* case 4: reserved */ + case 5: /* 6 Mbps */ + return 12; + case 6: /* 9 Mbps */ + return 18; + case 7: /* 12 Mbps */ + return 24; + case 8: /* 18 Mbps */ + return 36; + case 9: /* 24 Mbps */ + return 48; + case 10: /* 36 Mbps */ + return 72; + case 11: /* 48 Mbps */ + return 96; + case 12: /* 54 Mbps */ + return 108; + } + pr_alert("Invalid Marvell WLAN rate %i\n", rate); + return 0; +} + +/** + * process_rxed_802_11_packet - processes a received 802.11 packet and forwards + * it to kernel/upper layer + * + * @priv: A pointer to &struct lbs_private + * @skb: A pointer to skb which includes the received packet + * returns: 0 or -1 + */ +static int process_rxed_802_11_packet(struct lbs_private *priv, + struct sk_buff *skb) +{ + int ret = 0; + struct net_device *dev = priv->dev; + struct rx80211packethdr *p_rx_pkt; + struct rxpd *prxpd; + struct rx_radiotap_hdr radiotap_hdr; + struct rx_radiotap_hdr *pradiotap_hdr; + + lbs_deb_enter(LBS_DEB_RX); + + p_rx_pkt = (struct rx80211packethdr *) skb->data; + prxpd = &p_rx_pkt->rx_pd; + + /* lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); */ + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_deb_rx("rx err: frame received with bad length\n"); + dev->stats.rx_length_errors++; + ret = -EINVAL; + kfree_skb(skb); + goto done; + } + + lbs_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n", + skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); + + /* create the exported radio header */ + + /* radiotap header */ + memset(&radiotap_hdr, 0, sizeof(radiotap_hdr)); + /* XXX must check radiotap_hdr.hdr.it_pad for pad */ + radiotap_hdr.hdr.it_len = cpu_to_le16 (sizeof(struct rx_radiotap_hdr)); + radiotap_hdr.hdr.it_present = cpu_to_le32 (RX_RADIOTAP_PRESENT); + radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); + /* XXX must check no carryout */ + radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; + + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + + /* add space for the new radio header */ + if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) && + pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, GFP_ATOMIC)) { + netdev_alert(dev, "%s: couldn't pskb_expand_head\n", __func__); + ret = -ENOMEM; + kfree_skb(skb); + goto done; + } + + pradiotap_hdr = (void *)skb_push(skb, sizeof(struct rx_radiotap_hdr)); + memcpy(pradiotap_hdr, &radiotap_hdr, sizeof(struct rx_radiotap_hdr)); + + priv->cur_rate = lbs_fw_index_to_data_rate(prxpd->rx_rate); + + lbs_deb_rx("rx data: size of actual packet %d\n", skb->len); + dev->stats.rx_bytes += skb->len; + dev->stats.rx_packets++; + + skb->protocol = eth_type_trans(skb, priv->dev); + + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + + ret = 0; + +done: + lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret); + return ret; +} diff --git a/drivers/net/wireless/marvell/libertas/tx.c b/drivers/net/wireless/marvell/libertas/tx.c new file mode 100644 index 000000000000..c025f9c18282 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/tx.c @@ -0,0 +1,207 @@ +/* + * This file contains the handling of TX in wlan driver. + */ +#include +#include +#include +#include +#include +#include + +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "mesh.h" + +/** + * convert_radiotap_rate_to_mv - converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE + * units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1) + * + * @rate: Input rate + * returns: Output Rate (0 if invalid) + */ +static u32 convert_radiotap_rate_to_mv(u8 rate) +{ + switch (rate) { + case 2: /* 1 Mbps */ + return 0 | (1 << 4); + case 4: /* 2 Mbps */ + return 1 | (1 << 4); + case 11: /* 5.5 Mbps */ + return 2 | (1 << 4); + case 22: /* 11 Mbps */ + return 3 | (1 << 4); + case 12: /* 6 Mbps */ + return 4 | (1 << 4); + case 18: /* 9 Mbps */ + return 5 | (1 << 4); + case 24: /* 12 Mbps */ + return 6 | (1 << 4); + case 36: /* 18 Mbps */ + return 7 | (1 << 4); + case 48: /* 24 Mbps */ + return 8 | (1 << 4); + case 72: /* 36 Mbps */ + return 9 | (1 << 4); + case 96: /* 48 Mbps */ + return 10 | (1 << 4); + case 108: /* 54 Mbps */ + return 11 | (1 << 4); + } + return 0; +} + +/** + * lbs_hard_start_xmit - checks the conditions and sends packet to IF + * layer if everything is ok + * + * @skb: A pointer to skb which includes TX packet + * @dev: A pointer to the &struct net_device + * returns: 0 or -1 + */ +netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned long flags; + struct lbs_private *priv = dev->ml_priv; + struct txpd *txpd; + char *p802x_hdr; + uint16_t pkt_len; + netdev_tx_t ret = NETDEV_TX_OK; + + lbs_deb_enter(LBS_DEB_TX); + + /* We need to protect against the queues being restarted before + we get round to stopping them */ + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->surpriseremoved) + goto free; + + if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { + lbs_deb_tx("tx err: skb length %d 0 or > %zd\n", + skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); + /* We'll never manage to send this one; drop it and return 'OK' */ + + dev->stats.tx_dropped++; + dev->stats.tx_errors++; + goto free; + } + + + netif_stop_queue(priv->dev); + if (priv->mesh_dev) + netif_stop_queue(priv->mesh_dev); + + if (priv->tx_pending_len) { + /* This can happen if packets come in on the mesh and eth + device simultaneously -- there's no mutual exclusion on + hard_start_xmit() calls between devices. */ + lbs_deb_tx("Packet on %s while busy\n", dev->name); + ret = NETDEV_TX_BUSY; + goto unlock; + } + + priv->tx_pending_len = -1; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + lbs_deb_hex(LBS_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); + + txpd = (void *)priv->tx_pending_buf; + memset(txpd, 0, sizeof(struct txpd)); + + p802x_hdr = skb->data; + pkt_len = skb->len; + + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + struct tx_radiotap_hdr *rtap_hdr = (void *)skb->data; + + /* set txpd fields from the radiotap header */ + txpd->tx_control = cpu_to_le32(convert_radiotap_rate_to_mv(rtap_hdr->rate)); + + /* skip the radiotap header */ + p802x_hdr += sizeof(*rtap_hdr); + pkt_len -= sizeof(*rtap_hdr); + + /* copy destination address from 802.11 header */ + memcpy(txpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN); + } else { + /* copy destination address from 802.3 header */ + memcpy(txpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN); + } + + txpd->tx_packet_length = cpu_to_le16(pkt_len); + txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); + + lbs_mesh_set_txpd(priv, dev, txpd); + + lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) &txpd, sizeof(struct txpd)); + + lbs_deb_hex(LBS_DEB_TX, "Tx Data", (u8 *) p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); + + memcpy(&txpd[1], p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->tx_pending_len = pkt_len + sizeof(struct txpd); + + lbs_deb_tx("%s lined up packet\n", __func__); + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + /* Keep the skb to echo it back once Tx feedback is + received from FW */ + skb_orphan(skb); + + /* Keep the skb around for when we get feedback */ + priv->currenttxskb = skb; + } else { + free: + dev_kfree_skb_any(skb); + } + + unlock: + spin_unlock_irqrestore(&priv->driver_lock, flags); + wake_up(&priv->waitq); + + lbs_deb_leave_args(LBS_DEB_TX, "ret %d", ret); + return ret; +} + +/** + * lbs_send_tx_feedback - sends to the host the last transmitted packet, + * filling the radiotap headers with transmission information. + * + * @priv: A pointer to &struct lbs_private structure + * @try_count: A 32-bit value containing transmission retry status. + * + * returns: void + */ +void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count) +{ + struct tx_radiotap_hdr *radiotap_hdr; + + if (priv->wdev->iftype != NL80211_IFTYPE_MONITOR || + priv->currenttxskb == NULL) + return; + + radiotap_hdr = (struct tx_radiotap_hdr *)priv->currenttxskb->data; + + radiotap_hdr->data_retries = try_count ? + (1 + priv->txretrycount - try_count) : 0; + + priv->currenttxskb->protocol = eth_type_trans(priv->currenttxskb, + priv->dev); + netif_rx(priv->currenttxskb); + + priv->currenttxskb = NULL; + + if (priv->connect_status == LBS_CONNECTED) + netif_wake_queue(priv->dev); + + if (priv->mesh_dev && netif_running(priv->mesh_dev)) + netif_wake_queue(priv->mesh_dev); +} +EXPORT_SYMBOL_GPL(lbs_send_tx_feedback); diff --git a/drivers/net/wireless/marvell/libertas/types.h b/drivers/net/wireless/marvell/libertas/types.h new file mode 100644 index 000000000000..cf1d9b047ee6 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/types.h @@ -0,0 +1,268 @@ +/* + * This header file contains definition for global types + */ +#ifndef _LBS_TYPES_H_ +#define _LBS_TYPES_H_ + +#include +#include +#include + +struct ieee_ie_header { + u8 id; + u8 len; +} __packed; + +struct ieee_ie_cf_param_set { + struct ieee_ie_header header; + + u8 cfpcnt; + u8 cfpperiod; + __le16 cfpmaxduration; + __le16 cfpdurationremaining; +} __packed; + + +struct ieee_ie_ibss_param_set { + struct ieee_ie_header header; + + __le16 atimwindow; +} __packed; + +union ieee_ss_param_set { + struct ieee_ie_cf_param_set cf; + struct ieee_ie_ibss_param_set ibss; +} __packed; + +struct ieee_ie_fh_param_set { + struct ieee_ie_header header; + + __le16 dwelltime; + u8 hopset; + u8 hoppattern; + u8 hopindex; +} __packed; + +struct ieee_ie_ds_param_set { + struct ieee_ie_header header; + + u8 channel; +} __packed; + +union ieee_phy_param_set { + struct ieee_ie_fh_param_set fh; + struct ieee_ie_ds_param_set ds; +} __packed; + +/* TLV type ID definition */ +#define PROPRIETARY_TLV_BASE_ID 0x0100 + +/* Terminating TLV type */ +#define MRVL_TERMINATE_TLV_ID 0xffff + +#define TLV_TYPE_SSID 0x0000 +#define TLV_TYPE_RATES 0x0001 +#define TLV_TYPE_PHY_FH 0x0002 +#define TLV_TYPE_PHY_DS 0x0003 +#define TLV_TYPE_CF 0x0004 +#define TLV_TYPE_IBSS 0x0006 + +#define TLV_TYPE_DOMAIN 0x0007 + +#define TLV_TYPE_POWER_CAPABILITY 0x0021 + +#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) +#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) +#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) +#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 5) +#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 6) +#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 7) +#define TLV_TYPE_LED_GPIO (PROPRIETARY_TLV_BASE_ID + 8) +#define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 9) +#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) +#define TLV_TYPE_REASSOCAP (PROPRIETARY_TLV_BASE_ID + 11) +#define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 12) +#define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 13) +#define TLV_TYPE_BCASTPROBE (PROPRIETARY_TLV_BASE_ID + 14) +#define TLV_TYPE_NUMSSID_PROBE (PROPRIETARY_TLV_BASE_ID + 15) +#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) +#define TLV_TYPE_CRYPTO_DATA (PROPRIETARY_TLV_BASE_ID + 17) +#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) +#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) +#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 23) +#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) +#define TLV_TYPE_MESH_ID (PROPRIETARY_TLV_BASE_ID + 37) +#define TLV_TYPE_OLD_MESH_ID (PROPRIETARY_TLV_BASE_ID + 291) + +/* TLV related data structures */ +struct mrvl_ie_header { + __le16 type; + __le16 len; +} __packed; + +struct mrvl_ie_data { + struct mrvl_ie_header header; + u8 Data[1]; +} __packed; + +struct mrvl_ie_rates_param_set { + struct mrvl_ie_header header; + u8 rates[1]; +} __packed; + +struct mrvl_ie_ssid_param_set { + struct mrvl_ie_header header; + u8 ssid[1]; +} __packed; + +struct mrvl_ie_wildcard_ssid_param_set { + struct mrvl_ie_header header; + u8 MaxSsidlength; + u8 ssid[1]; +} __packed; + +struct chanscanmode { +#ifdef __BIG_ENDIAN_BITFIELD + u8 reserved_2_7:6; + u8 disablechanfilt:1; + u8 passivescan:1; +#else + u8 passivescan:1; + u8 disablechanfilt:1; + u8 reserved_2_7:6; +#endif +} __packed; + +struct chanscanparamset { + u8 radiotype; + u8 channumber; + struct chanscanmode chanscanmode; + __le16 minscantime; + __le16 maxscantime; +} __packed; + +struct mrvl_ie_chanlist_param_set { + struct mrvl_ie_header header; + struct chanscanparamset chanscanparam[1]; +} __packed; + +struct mrvl_ie_cf_param_set { + struct mrvl_ie_header header; + u8 cfpcnt; + u8 cfpperiod; + __le16 cfpmaxduration; + __le16 cfpdurationremaining; +} __packed; + +struct mrvl_ie_ds_param_set { + struct mrvl_ie_header header; + u8 channel; +} __packed; + +struct mrvl_ie_rsn_param_set { + struct mrvl_ie_header header; + u8 rsnie[1]; +} __packed; + +struct mrvl_ie_tsf_timestamp { + struct mrvl_ie_header header; + __le64 tsftable[1]; +} __packed; + +/* v9 and later firmware only */ +struct mrvl_ie_auth_type { + struct mrvl_ie_header header; + __le16 auth; +} __packed; + +/* Local Power capability */ +struct mrvl_ie_power_capability { + struct mrvl_ie_header header; + s8 minpower; + s8 maxpower; +} __packed; + +/* used in CMD_802_11_SUBSCRIBE_EVENT for SNR, RSSI and Failure */ +struct mrvl_ie_thresholds { + struct mrvl_ie_header header; + u8 value; + u8 freq; +} __packed; + +struct mrvl_ie_beacons_missed { + struct mrvl_ie_header header; + u8 beaconmissed; + u8 reserved; +} __packed; + +struct mrvl_ie_num_probes { + struct mrvl_ie_header header; + __le16 numprobes; +} __packed; + +struct mrvl_ie_bcast_probe { + struct mrvl_ie_header header; + __le16 bcastprobe; +} __packed; + +struct mrvl_ie_num_ssid_probe { + struct mrvl_ie_header header; + __le16 numssidprobe; +} __packed; + +struct led_pin { + u8 led; + u8 pin; +} __packed; + +struct mrvl_ie_ledgpio { + struct mrvl_ie_header header; + struct led_pin ledpin[1]; +} __packed; + +struct led_bhv { + uint8_t firmwarestate; + uint8_t led; + uint8_t ledstate; + uint8_t ledarg; +} __packed; + + +struct mrvl_ie_ledbhv { + struct mrvl_ie_header header; + struct led_bhv ledbhv[1]; +} __packed; + +/* + * Meant to be packed as the value member of a struct ieee80211_info_element. + * Note that the len member of the ieee80211_info_element varies depending on + * the mesh_id_len + */ +struct mrvl_meshie_val { + uint8_t oui[3]; + uint8_t type; + uint8_t subtype; + uint8_t version; + uint8_t active_protocol_id; + uint8_t active_metric_id; + uint8_t mesh_capability; + uint8_t mesh_id_len; + uint8_t mesh_id[IEEE80211_MAX_SSID_LEN]; +} __packed; + +struct mrvl_meshie { + u8 id, len; + struct mrvl_meshie_val val; +} __packed; + +struct mrvl_mesh_defaults { + __le32 bootflag; + uint8_t boottime; + uint8_t reserved; + __le16 channel; + struct mrvl_meshie meshie; +} __packed; + +#endif -- cgit v1.2.3-59-g8ed1b From dd3f92dea849d96272642579003754df9367c160 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 21:11:21 +0200 Subject: libertas_tf: move under marvell vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- drivers/net/wireless/Kconfig | 19 - drivers/net/wireless/Makefile | 2 - drivers/net/wireless/libertas_tf/Makefile | 6 - drivers/net/wireless/libertas_tf/cmd.c | 807 ------------------ drivers/net/wireless/libertas_tf/deb_defs.h | 104 --- drivers/net/wireless/libertas_tf/if_usb.c | 928 --------------------- drivers/net/wireless/libertas_tf/if_usb.h | 98 --- drivers/net/wireless/libertas_tf/libertas_tf.h | 519 ------------ drivers/net/wireless/libertas_tf/main.c | 766 ----------------- drivers/net/wireless/marvell/Kconfig | 1 + drivers/net/wireless/marvell/Makefile | 2 + drivers/net/wireless/marvell/libertas_tf/Kconfig | 18 + drivers/net/wireless/marvell/libertas_tf/Makefile | 6 + drivers/net/wireless/marvell/libertas_tf/cmd.c | 807 ++++++++++++++++++ .../net/wireless/marvell/libertas_tf/deb_defs.h | 104 +++ drivers/net/wireless/marvell/libertas_tf/if_usb.c | 928 +++++++++++++++++++++ drivers/net/wireless/marvell/libertas_tf/if_usb.h | 98 +++ .../net/wireless/marvell/libertas_tf/libertas_tf.h | 519 ++++++++++++ drivers/net/wireless/marvell/libertas_tf/main.c | 766 +++++++++++++++++ 19 files changed, 3249 insertions(+), 3249 deletions(-) delete mode 100644 drivers/net/wireless/libertas_tf/Makefile delete mode 100644 drivers/net/wireless/libertas_tf/cmd.c delete mode 100644 drivers/net/wireless/libertas_tf/deb_defs.h delete mode 100644 drivers/net/wireless/libertas_tf/if_usb.c delete mode 100644 drivers/net/wireless/libertas_tf/if_usb.h delete mode 100644 drivers/net/wireless/libertas_tf/libertas_tf.h delete mode 100644 drivers/net/wireless/libertas_tf/main.c create mode 100644 drivers/net/wireless/marvell/libertas_tf/Kconfig create mode 100644 drivers/net/wireless/marvell/libertas_tf/Makefile create mode 100644 drivers/net/wireless/marvell/libertas_tf/cmd.c create mode 100644 drivers/net/wireless/marvell/libertas_tf/deb_defs.h create mode 100644 drivers/net/wireless/marvell/libertas_tf/if_usb.c create mode 100644 drivers/net/wireless/marvell/libertas_tf/if_usb.h create mode 100644 drivers/net/wireless/marvell/libertas_tf/libertas_tf.h create mode 100644 drivers/net/wireless/marvell/libertas_tf/main.c (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index b11a2d364200..25f52b32d725 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -40,25 +40,6 @@ config PCMCIA_RAYCS To compile this driver as a module, choose M here: the module will be called ray_cs. If unsure, say N. -config LIBERTAS_THINFIRM - tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware" - depends on MAC80211 - select FW_LOADER - ---help--- - A library for Marvell Libertas 8xxx devices using thinfirm. - -config LIBERTAS_THINFIRM_DEBUG - bool "Enable full debugging output in the Libertas thin firmware module." - depends on LIBERTAS_THINFIRM - ---help--- - Debugging support. - -config LIBERTAS_THINFIRM_USB - tristate "Marvell Libertas 8388 USB 802.11b/g cards with thin firmware" - depends on LIBERTAS_THINFIRM && USB - ---help--- - A driver for Marvell Libertas 8388 USB devices using thinfirm. - config PCMCIA_WL3501 tristate "Planet WL3501 PCMCIA cards" depends on CFG80211 && PCMCIA diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index a974a6edb4b6..72b167742ac7 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -26,8 +26,6 @@ obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o obj-$(CONFIG_USB_ZD1201) += zd1201.o -obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ - obj-$(CONFIG_MWL8K) += mwl8k.o obj-$(CONFIG_RT2X00) += rt2x00/ diff --git a/drivers/net/wireless/libertas_tf/Makefile b/drivers/net/wireless/libertas_tf/Makefile deleted file mode 100644 index ff5544d6ac9d..000000000000 --- a/drivers/net/wireless/libertas_tf/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -libertas_tf-objs := main.o cmd.o - -libertas_tf_usb-objs += if_usb.o - -obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf.o -obj-$(CONFIG_LIBERTAS_THINFIRM_USB) += libertas_tf_usb.o diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/libertas_tf/cmd.c deleted file mode 100644 index 909ac3685010..000000000000 --- a/drivers/net/wireless/libertas_tf/cmd.c +++ /dev/null @@ -1,807 +0,0 @@ -/* - * Copyright (C) 2008, cozybit Inc. - * Copyright (C) 2003-2006, Marvell International Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include - -#include "libertas_tf.h" - -static const struct channel_range channel_ranges[] = { - { LBTF_REGDOMAIN_US, 1, 12 }, - { LBTF_REGDOMAIN_CA, 1, 12 }, - { LBTF_REGDOMAIN_EU, 1, 14 }, - { LBTF_REGDOMAIN_JP, 1, 14 }, - { LBTF_REGDOMAIN_SP, 1, 14 }, - { LBTF_REGDOMAIN_FR, 1, 14 }, -}; - -static u16 lbtf_region_code_to_index[MRVDRV_MAX_REGION_CODE] = -{ - LBTF_REGDOMAIN_US, LBTF_REGDOMAIN_CA, LBTF_REGDOMAIN_EU, - LBTF_REGDOMAIN_SP, LBTF_REGDOMAIN_FR, LBTF_REGDOMAIN_JP, -}; - -static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv); - - -/** - * lbtf_cmd_copyback - Simple callback that copies response back into command - * - * @priv A pointer to struct lbtf_private structure - * @extra A pointer to the original command structure for which - * 'resp' is a response - * @resp A pointer to the command response - * - * Returns: 0 on success, error on failure - */ -int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, - struct cmd_header *resp) -{ - struct cmd_header *buf = (void *)extra; - uint16_t copy_len; - - copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); - memcpy(buf, resp, copy_len); - return 0; -} -EXPORT_SYMBOL_GPL(lbtf_cmd_copyback); - -#define CHAN_TO_IDX(chan) ((chan) - 1) - -static void lbtf_geo_init(struct lbtf_private *priv) -{ - const struct channel_range *range = channel_ranges; - u8 ch; - int i; - - for (i = 0; i < ARRAY_SIZE(channel_ranges); i++) - if (channel_ranges[i].regdomain == priv->regioncode) { - range = &channel_ranges[i]; - break; - } - - for (ch = priv->range.start; ch < priv->range.end; ch++) - priv->channels[CHAN_TO_IDX(ch)].flags = 0; -} - -/** - * lbtf_update_hw_spec: Updates the hardware details. - * - * @priv A pointer to struct lbtf_private structure - * - * Returns: 0 on success, error on failure - */ -int lbtf_update_hw_spec(struct lbtf_private *priv) -{ - struct cmd_ds_get_hw_spec cmd; - int ret = -1; - u32 i; - - lbtf_deb_enter(LBTF_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); - ret = lbtf_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); - if (ret) - goto out; - - priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); - - /* The firmware release is in an interesting format: the patch - * level is in the most significant nibble ... so fix that: */ - priv->fwrelease = le32_to_cpu(cmd.fwrelease); - priv->fwrelease = (priv->fwrelease << 8) | - (priv->fwrelease >> 24 & 0xff); - - printk(KERN_INFO "libertastf: %pM, fw %u.%u.%up%u, cap 0x%08x\n", - cmd.permanentaddr, - priv->fwrelease >> 24 & 0xff, - priv->fwrelease >> 16 & 0xff, - priv->fwrelease >> 8 & 0xff, - priv->fwrelease & 0xff, - priv->fwcapinfo); - lbtf_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", - cmd.hwifversion, cmd.version); - - /* Clamp region code to 8-bit since FW spec indicates that it should - * only ever be 8-bit, even though the field size is 16-bit. Some - * firmware returns non-zero high 8 bits here. - */ - priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; - - for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { - /* use the region code to search for the index */ - if (priv->regioncode == lbtf_region_code_to_index[i]) - break; - } - - /* if it's unidentified region code, use the default (USA) */ - if (i >= MRVDRV_MAX_REGION_CODE) { - priv->regioncode = 0x10; - pr_info("unidentified region code; using the default (USA)\n"); - } - - if (priv->current_addr[0] == 0xff) - memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); - - SET_IEEE80211_PERM_ADDR(priv->hw, priv->current_addr); - - lbtf_geo_init(priv); -out: - lbtf_deb_leave(LBTF_DEB_CMD); - return ret; -} - -/** - * lbtf_set_channel: Set the radio channel - * - * @priv A pointer to struct lbtf_private structure - * @channel The desired channel, or 0 to clear a locked channel - * - * Returns: 0 on success, error on failure - */ -int lbtf_set_channel(struct lbtf_private *priv, u8 channel) -{ - int ret = 0; - struct cmd_ds_802_11_rf_channel cmd; - - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); - cmd.channel = cpu_to_le16(channel); - - ret = lbtf_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); - lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); - return ret; -} - -int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon) -{ - struct cmd_ds_802_11_beacon_set cmd; - int size; - - lbtf_deb_enter(LBTF_DEB_CMD); - - if (beacon->len > MRVL_MAX_BCN_SIZE) { - lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", -1); - return -1; - } - size = sizeof(cmd) - sizeof(cmd.beacon) + beacon->len; - cmd.hdr.size = cpu_to_le16(size); - cmd.len = cpu_to_le16(beacon->len); - memcpy(cmd.beacon, (u8 *) beacon->data, beacon->len); - - lbtf_cmd_async(priv, CMD_802_11_BEACON_SET, &cmd.hdr, size); - - lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", 0); - return 0; -} - -int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, - int beacon_int) -{ - struct cmd_ds_802_11_beacon_control cmd; - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.beacon_enable = cpu_to_le16(beacon_enable); - cmd.beacon_period = cpu_to_le16(beacon_int); - - lbtf_cmd_async(priv, CMD_802_11_BEACON_CTRL, &cmd.hdr, sizeof(cmd)); - - lbtf_deb_leave(LBTF_DEB_CMD); - return 0; -} - -static void lbtf_queue_cmd(struct lbtf_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - unsigned long flags; - lbtf_deb_enter(LBTF_DEB_HOST); - - if (!cmdnode) { - lbtf_deb_host("QUEUE_CMD: cmdnode is NULL\n"); - goto qcmd_done; - } - - if (!cmdnode->cmdbuf->size) { - lbtf_deb_host("DNLD_CMD: cmd size is zero\n"); - goto qcmd_done; - } - - cmdnode->result = 0; - spin_lock_irqsave(&priv->driver_lock, flags); - list_add_tail(&cmdnode->list, &priv->cmdpendingq); - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbtf_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", - le16_to_cpu(cmdnode->cmdbuf->command)); - -qcmd_done: - lbtf_deb_leave(LBTF_DEB_HOST); -} - -static void lbtf_submit_command(struct lbtf_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - unsigned long flags; - struct cmd_header *cmd; - uint16_t cmdsize; - uint16_t command; - int timeo = 5 * HZ; - int ret; - - lbtf_deb_enter(LBTF_DEB_HOST); - - cmd = cmdnode->cmdbuf; - - spin_lock_irqsave(&priv->driver_lock, flags); - priv->cur_cmd = cmdnode; - cmdsize = le16_to_cpu(cmd->size); - command = le16_to_cpu(cmd->command); - - lbtf_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", - command, le16_to_cpu(cmd->seqnum), cmdsize); - lbtf_deb_hex(LBTF_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); - - ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); - spin_unlock_irqrestore(&priv->driver_lock, flags); - - if (ret) { - pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret); - /* Let the timer kick in and retry, and potentially reset - the whole thing if the condition persists */ - timeo = HZ; - } - - /* Setup the timer after transmit command */ - mod_timer(&priv->command_timer, jiffies + timeo); - - lbtf_deb_leave(LBTF_DEB_HOST); -} - -/** - * This function inserts command node to cmdfreeq - * after cleans it. Requires priv->driver_lock held. - */ -static void __lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - lbtf_deb_enter(LBTF_DEB_HOST); - - if (!cmdnode) - goto cl_ins_out; - - cmdnode->callback = NULL; - cmdnode->callback_arg = 0; - - memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); - - list_add_tail(&cmdnode->list, &priv->cmdfreeq); - -cl_ins_out: - lbtf_deb_leave(LBTF_DEB_HOST); -} - -static void lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, - struct cmd_ctrl_node *ptempcmd) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->driver_lock, flags); - __lbtf_cleanup_and_insert_cmd(priv, ptempcmd); - spin_unlock_irqrestore(&priv->driver_lock, flags); -} - -void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, - int result) -{ - cmd->result = result; - cmd->cmdwaitqwoken = 1; - wake_up_interruptible(&cmd->cmdwait_q); - - if (!cmd->callback) - __lbtf_cleanup_and_insert_cmd(priv, cmd); - priv->cur_cmd = NULL; -} - -int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv) -{ - struct cmd_ds_mac_multicast_addr cmd; - - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - - cmd.nr_of_adrs = cpu_to_le16((u16) priv->nr_of_multicastmacaddr); - - lbtf_deb_cmd("MULTICAST_ADR: setting %d addresses\n", cmd.nr_of_adrs); - - memcpy(cmd.maclist, priv->multicastlist, - priv->nr_of_multicastmacaddr * ETH_ALEN); - - lbtf_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &cmd.hdr, sizeof(cmd)); - - lbtf_deb_leave(LBTF_DEB_CMD); - return 0; -} - -void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode) -{ - struct cmd_ds_set_mode cmd; - lbtf_deb_enter(LBTF_DEB_WEXT); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.mode = cpu_to_le16(mode); - lbtf_deb_wext("Switching to mode: 0x%x\n", mode); - lbtf_cmd_async(priv, CMD_802_11_SET_MODE, &cmd.hdr, sizeof(cmd)); - - lbtf_deb_leave(LBTF_DEB_WEXT); -} - -void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid) -{ - struct cmd_ds_set_bssid cmd; - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.activate = activate ? 1 : 0; - if (activate) - memcpy(cmd.bssid, bssid, ETH_ALEN); - - lbtf_cmd_async(priv, CMD_802_11_SET_BSSID, &cmd.hdr, sizeof(cmd)); - lbtf_deb_leave(LBTF_DEB_CMD); -} - -int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr) -{ - struct cmd_ds_802_11_mac_address cmd; - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - - memcpy(cmd.macadd, mac_addr, ETH_ALEN); - - lbtf_cmd_async(priv, CMD_802_11_MAC_ADDRESS, &cmd.hdr, sizeof(cmd)); - lbtf_deb_leave(LBTF_DEB_CMD); - return 0; -} - -int lbtf_set_radio_control(struct lbtf_private *priv) -{ - int ret = 0; - struct cmd_ds_802_11_radio_control cmd; - - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - - switch (priv->preamble) { - case CMD_TYPE_SHORT_PREAMBLE: - cmd.control = cpu_to_le16(SET_SHORT_PREAMBLE); - break; - - case CMD_TYPE_LONG_PREAMBLE: - cmd.control = cpu_to_le16(SET_LONG_PREAMBLE); - break; - - case CMD_TYPE_AUTO_PREAMBLE: - default: - cmd.control = cpu_to_le16(SET_AUTO_PREAMBLE); - break; - } - - if (priv->radioon) - cmd.control |= cpu_to_le16(TURN_ON_RF); - else - cmd.control &= cpu_to_le16(~TURN_ON_RF); - - lbtf_deb_cmd("RADIO_SET: radio %d, preamble %d\n", priv->radioon, - priv->preamble); - - ret = lbtf_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); - - lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); - return ret; -} - -void lbtf_set_mac_control(struct lbtf_private *priv) -{ - struct cmd_ds_mac_control cmd; - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(priv->mac_control); - cmd.reserved = 0; - - lbtf_cmd_async(priv, CMD_MAC_CONTROL, - &cmd.hdr, sizeof(cmd)); - - lbtf_deb_leave(LBTF_DEB_CMD); -} - -/** - * lbtf_allocate_cmd_buffer - Allocates cmd buffer, links it to free cmd queue - * - * @priv A pointer to struct lbtf_private structure - * - * Returns: 0 on success. - */ -int lbtf_allocate_cmd_buffer(struct lbtf_private *priv) -{ - int ret = 0; - u32 bufsize; - u32 i; - struct cmd_ctrl_node *cmdarray; - - lbtf_deb_enter(LBTF_DEB_HOST); - - /* Allocate and initialize the command array */ - bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; - cmdarray = kzalloc(bufsize, GFP_KERNEL); - if (!cmdarray) { - lbtf_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); - ret = -1; - goto done; - } - priv->cmd_array = cmdarray; - - /* Allocate and initialize each command buffer in the command array */ - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); - if (!cmdarray[i].cmdbuf) { - lbtf_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); - ret = -1; - goto done; - } - } - - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - init_waitqueue_head(&cmdarray[i].cmdwait_q); - lbtf_cleanup_and_insert_cmd(priv, &cmdarray[i]); - } - - ret = 0; - -done: - lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret); - return ret; -} - -/** - * lbtf_free_cmd_buffer - Frees the cmd buffer. - * - * @priv A pointer to struct lbtf_private structure - * - * Returns: 0 - */ -int lbtf_free_cmd_buffer(struct lbtf_private *priv) -{ - struct cmd_ctrl_node *cmdarray; - unsigned int i; - - lbtf_deb_enter(LBTF_DEB_HOST); - - /* need to check if cmd array is allocated or not */ - if (priv->cmd_array == NULL) { - lbtf_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); - goto done; - } - - cmdarray = priv->cmd_array; - - /* Release shared memory buffers */ - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - kfree(cmdarray[i].cmdbuf); - cmdarray[i].cmdbuf = NULL; - } - - /* Release cmd_ctrl_node */ - kfree(priv->cmd_array); - priv->cmd_array = NULL; - -done: - lbtf_deb_leave(LBTF_DEB_HOST); - return 0; -} - -/** - * lbtf_get_cmd_ctrl_node - Gets free cmd node from free cmd queue. - * - * @priv A pointer to struct lbtf_private structure - * - * Returns: pointer to a struct cmd_ctrl_node or NULL if none available. - */ -static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv) -{ - struct cmd_ctrl_node *tempnode; - unsigned long flags; - - lbtf_deb_enter(LBTF_DEB_HOST); - - if (!priv) - return NULL; - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (!list_empty(&priv->cmdfreeq)) { - tempnode = list_first_entry(&priv->cmdfreeq, - struct cmd_ctrl_node, list); - list_del(&tempnode->list); - } else { - lbtf_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); - tempnode = NULL; - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbtf_deb_leave(LBTF_DEB_HOST); - return tempnode; -} - -/** - * lbtf_execute_next_command: execute next command in cmd pending queue. - * - * @priv A pointer to struct lbtf_private structure - * - * Returns: 0 on success. - */ -int lbtf_execute_next_command(struct lbtf_private *priv) -{ - struct cmd_ctrl_node *cmdnode = NULL; - struct cmd_header *cmd; - unsigned long flags; - int ret = 0; - - /* Debug group is lbtf_deb_THREAD and not lbtf_deb_HOST, because the - * only caller to us is lbtf_thread() and we get even when a - * data packet is received */ - lbtf_deb_enter(LBTF_DEB_THREAD); - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->cur_cmd) { - pr_alert("EXEC_NEXT_CMD: already processing command!\n"); - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - if (!list_empty(&priv->cmdpendingq)) { - cmdnode = list_first_entry(&priv->cmdpendingq, - struct cmd_ctrl_node, list); - } - - if (cmdnode) { - cmd = cmdnode->cmdbuf; - - list_del(&cmdnode->list); - lbtf_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", - le16_to_cpu(cmd->command)); - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbtf_submit_command(priv, cmdnode); - } else - spin_unlock_irqrestore(&priv->driver_lock, flags); - - ret = 0; -done: - lbtf_deb_leave(LBTF_DEB_THREAD); - return ret; -} - -static struct cmd_ctrl_node *__lbtf_cmd_async(struct lbtf_private *priv, - uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbtf_private *, unsigned long, - struct cmd_header *), - unsigned long callback_arg) -{ - struct cmd_ctrl_node *cmdnode; - - lbtf_deb_enter(LBTF_DEB_HOST); - - if (priv->surpriseremoved) { - lbtf_deb_host("PREP_CMD: card removed\n"); - cmdnode = ERR_PTR(-ENOENT); - goto done; - } - - cmdnode = lbtf_get_cmd_ctrl_node(priv); - if (cmdnode == NULL) { - lbtf_deb_host("PREP_CMD: cmdnode is NULL\n"); - - /* Wake up main thread to execute next command */ - queue_work(lbtf_wq, &priv->cmd_work); - cmdnode = ERR_PTR(-ENOBUFS); - goto done; - } - - cmdnode->callback = callback; - cmdnode->callback_arg = callback_arg; - - /* Copy the incoming command to the buffer */ - memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); - - /* Set sequence number, clean result, move to buffer */ - priv->seqnum++; - cmdnode->cmdbuf->command = cpu_to_le16(command); - cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); - cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum); - cmdnode->cmdbuf->result = 0; - - lbtf_deb_host("PREP_CMD: command 0x%04x\n", command); - - cmdnode->cmdwaitqwoken = 0; - lbtf_queue_cmd(priv, cmdnode); - queue_work(lbtf_wq, &priv->cmd_work); - - done: - lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %p", cmdnode); - return cmdnode; -} - -void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size) -{ - lbtf_deb_enter(LBTF_DEB_CMD); - __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, NULL, 0); - lbtf_deb_leave(LBTF_DEB_CMD); -} - -int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbtf_private *, - unsigned long, struct cmd_header *), - unsigned long callback_arg) -{ - struct cmd_ctrl_node *cmdnode; - unsigned long flags; - int ret = 0; - - lbtf_deb_enter(LBTF_DEB_HOST); - - cmdnode = __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, - callback, callback_arg); - if (IS_ERR(cmdnode)) { - ret = PTR_ERR(cmdnode); - goto done; - } - - might_sleep(); - ret = wait_event_interruptible(cmdnode->cmdwait_q, - cmdnode->cmdwaitqwoken); - if (ret) { - pr_info("PREP_CMD: command 0x%04x interrupted by signal: %d\n", - command, ret); - goto done; - } - - spin_lock_irqsave(&priv->driver_lock, flags); - ret = cmdnode->result; - if (ret) - pr_info("PREP_CMD: command 0x%04x failed: %d\n", - command, ret); - - __lbtf_cleanup_and_insert_cmd(priv, cmdnode); - spin_unlock_irqrestore(&priv->driver_lock, flags); - -done: - lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(__lbtf_cmd); - -/* Call holding driver_lock */ -void lbtf_cmd_response_rx(struct lbtf_private *priv) -{ - priv->cmd_response_rxed = 1; - queue_work(lbtf_wq, &priv->cmd_work); -} -EXPORT_SYMBOL_GPL(lbtf_cmd_response_rx); - -int lbtf_process_rx_command(struct lbtf_private *priv) -{ - uint16_t respcmd, curcmd; - struct cmd_header *resp; - int ret = 0; - unsigned long flags; - uint16_t result; - - lbtf_deb_enter(LBTF_DEB_CMD); - - mutex_lock(&priv->lock); - spin_lock_irqsave(&priv->driver_lock, flags); - - if (!priv->cur_cmd) { - ret = -1; - spin_unlock_irqrestore(&priv->driver_lock, flags); - goto done; - } - - resp = (void *)priv->cmd_resp_buff; - curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); - respcmd = le16_to_cpu(resp->command); - result = le16_to_cpu(resp->result); - - if (net_ratelimit()) - pr_info("libertastf: cmd response 0x%04x, seq %d, size %d\n", - respcmd, le16_to_cpu(resp->seqnum), - le16_to_cpu(resp->size)); - - if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - if (respcmd != CMD_RET(curcmd)) { - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - if (resp->result == cpu_to_le16(0x0004)) { - /* 0x0004 means -EAGAIN. Drop the response, let it time out - and be resubmitted */ - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - /* Now we got response from FW, cancel the command timer */ - del_timer(&priv->command_timer); - priv->cmd_timed_out = 0; - if (priv->nr_retries) - priv->nr_retries = 0; - - /* If the command is not successful, cleanup and return failure */ - if ((result != 0 || !(respcmd & 0x8000))) { - /* - * Handling errors here - */ - switch (respcmd) { - case CMD_RET(CMD_GET_HW_SPEC): - case CMD_RET(CMD_802_11_RESET): - pr_info("libertastf: reset failed\n"); - break; - - } - lbtf_complete_command(priv, priv->cur_cmd, result); - spin_unlock_irqrestore(&priv->driver_lock, flags); - - ret = -1; - goto done; - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - if (priv->cur_cmd && priv->cur_cmd->callback) { - ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, - resp); - } - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->cur_cmd) { - /* Clean up and Put current command back to cmdfreeq */ - lbtf_complete_command(priv, priv->cur_cmd, result); - } - spin_unlock_irqrestore(&priv->driver_lock, flags); - -done: - mutex_unlock(&priv->lock); - lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); - return ret; -} diff --git a/drivers/net/wireless/libertas_tf/deb_defs.h b/drivers/net/wireless/libertas_tf/deb_defs.h deleted file mode 100644 index 4bd3dc5adf7c..000000000000 --- a/drivers/net/wireless/libertas_tf/deb_defs.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * This header file contains global constant/enum definitions, - * global variable declaration. - */ -#ifndef _LBS_DEB_DEFS_H_ -#define _LBS_DEB_DEFS_H_ - -#ifndef DRV_NAME -#define DRV_NAME "libertas_tf" -#endif - -#include - -#ifdef CONFIG_LIBERTAS_THINFIRM_DEBUG -#define DEBUG -#define PROC_DEBUG -#endif - -#define LBTF_DEB_ENTER 0x00000001 -#define LBTF_DEB_LEAVE 0x00000002 -#define LBTF_DEB_MAIN 0x00000004 -#define LBTF_DEB_NET 0x00000008 -#define LBTF_DEB_MESH 0x00000010 -#define LBTF_DEB_WEXT 0x00000020 -#define LBTF_DEB_IOCTL 0x00000040 -#define LBTF_DEB_SCAN 0x00000080 -#define LBTF_DEB_ASSOC 0x00000100 -#define LBTF_DEB_JOIN 0x00000200 -#define LBTF_DEB_11D 0x00000400 -#define LBTF_DEB_DEBUGFS 0x00000800 -#define LBTF_DEB_ETHTOOL 0x00001000 -#define LBTF_DEB_HOST 0x00002000 -#define LBTF_DEB_CMD 0x00004000 -#define LBTF_DEB_RX 0x00008000 -#define LBTF_DEB_TX 0x00010000 -#define LBTF_DEB_USB 0x00020000 -#define LBTF_DEB_CS 0x00040000 -#define LBTF_DEB_FW 0x00080000 -#define LBTF_DEB_THREAD 0x00100000 -#define LBTF_DEB_HEX 0x00200000 -#define LBTF_DEB_SDIO 0x00400000 -#define LBTF_DEB_MACOPS 0x00800000 - -extern unsigned int lbtf_debug; - - -#ifdef DEBUG -#define LBTF_DEB_LL(grp, grpnam, fmt, args...) \ -do { if ((lbtf_debug & (grp)) == (grp)) \ - printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \ - in_interrupt() ? " (INT)" : "", ## args); } while (0) -#else -#define LBTF_DEB_LL(grp, grpnam, fmt, args...) do {} while (0) -#endif - -#define lbtf_deb_enter(grp) \ - LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s()\n", __func__); -#define lbtf_deb_enter_args(grp, fmt, args...) \ - LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s(" fmt ")\n", __func__, ## args); -#define lbtf_deb_leave(grp) \ - LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s()\n", __func__); -#define lbtf_deb_leave_args(grp, fmt, args...) \ - LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s(), " fmt "\n", \ - __func__, ##args); -#define lbtf_deb_main(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MAIN, " main", fmt, ##args) -#define lbtf_deb_net(fmt, args...) LBTF_DEB_LL(LBTF_DEB_NET, " net", fmt, ##args) -#define lbtf_deb_mesh(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MESH, " mesh", fmt, ##args) -#define lbtf_deb_wext(fmt, args...) LBTF_DEB_LL(LBTF_DEB_WEXT, " wext", fmt, ##args) -#define lbtf_deb_ioctl(fmt, args...) LBTF_DEB_LL(LBTF_DEB_IOCTL, " ioctl", fmt, ##args) -#define lbtf_deb_scan(fmt, args...) LBTF_DEB_LL(LBTF_DEB_SCAN, " scan", fmt, ##args) -#define lbtf_deb_assoc(fmt, args...) LBTF_DEB_LL(LBTF_DEB_ASSOC, " assoc", fmt, ##args) -#define lbtf_deb_join(fmt, args...) LBTF_DEB_LL(LBTF_DEB_JOIN, " join", fmt, ##args) -#define lbtf_deb_11d(fmt, args...) LBTF_DEB_LL(LBTF_DEB_11D, " 11d", fmt, ##args) -#define lbtf_deb_debugfs(fmt, args...) LBTF_DEB_LL(LBTF_DEB_DEBUGFS, " debugfs", fmt, ##args) -#define lbtf_deb_ethtool(fmt, args...) LBTF_DEB_LL(LBTF_DEB_ETHTOOL, " ethtool", fmt, ##args) -#define lbtf_deb_host(fmt, args...) LBTF_DEB_LL(LBTF_DEB_HOST, " host", fmt, ##args) -#define lbtf_deb_cmd(fmt, args...) LBTF_DEB_LL(LBTF_DEB_CMD, " cmd", fmt, ##args) -#define lbtf_deb_rx(fmt, args...) LBTF_DEB_LL(LBTF_DEB_RX, " rx", fmt, ##args) -#define lbtf_deb_tx(fmt, args...) LBTF_DEB_LL(LBTF_DEB_TX, " tx", fmt, ##args) -#define lbtf_deb_fw(fmt, args...) LBTF_DEB_LL(LBTF_DEB_FW, " fw", fmt, ##args) -#define lbtf_deb_usb(fmt, args...) LBTF_DEB_LL(LBTF_DEB_USB, " usb", fmt, ##args) -#define lbtf_deb_usbd(dev, fmt, args...) LBTF_DEB_LL(LBTF_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args) -#define lbtf_deb_cs(fmt, args...) LBTF_DEB_LL(LBTF_DEB_CS, " cs", fmt, ##args) -#define lbtf_deb_thread(fmt, args...) LBTF_DEB_LL(LBTF_DEB_THREAD, " thread", fmt, ##args) -#define lbtf_deb_sdio(fmt, args...) LBTF_DEB_LL(LBTF_DEB_SDIO, " thread", fmt, ##args) -#define lbtf_deb_macops(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MACOPS, " thread", fmt, ##args) - -#ifdef DEBUG -static inline void lbtf_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len) -{ - char newprompt[32]; - - if (len && - (lbtf_debug & LBTF_DEB_HEX) && - (lbtf_debug & grp)) { - snprintf(newprompt, sizeof(newprompt), DRV_NAME " %s: ", prompt); - print_hex_dump_bytes(prompt, DUMP_PREFIX_NONE, buf, len); - } -} -#else -#define lbtf_deb_hex(grp, prompt, buf, len) do {} while (0) -#endif - -#endif diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c deleted file mode 100644 index 799a2efe5793..000000000000 --- a/drivers/net/wireless/libertas_tf/if_usb.c +++ /dev/null @@ -1,928 +0,0 @@ -/* - * Copyright (C) 2008, cozybit Inc. - * Copyright (C) 2003-2006, Marvell International Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ -#define DRV_NAME "lbtf_usb" - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "libertas_tf.h" -#include "if_usb.h" - -#include -#include -#include -#include -#include -#include - -#define INSANEDEBUG 0 -#define lbtf_deb_usb2(...) do { if (INSANEDEBUG) lbtf_deb_usbd(__VA_ARGS__); } while (0) - -#define MESSAGE_HEADER_LEN 4 - -static char *lbtf_fw_name = "lbtf_usb.bin"; -module_param_named(fw_name, lbtf_fw_name, charp, 0644); - -MODULE_FIRMWARE("lbtf_usb.bin"); - -static struct usb_device_id if_usb_table[] = { - /* Enter the device signature inside */ - { USB_DEVICE(0x1286, 0x2001) }, - { USB_DEVICE(0x05a3, 0x8388) }, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, if_usb_table); - -static void if_usb_receive(struct urb *urb); -static void if_usb_receive_fwload(struct urb *urb); -static int if_usb_prog_firmware(struct if_usb_card *cardp); -static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type, - uint8_t *payload, uint16_t nb); -static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, - uint16_t nb, u8 data); -static void if_usb_free(struct if_usb_card *cardp); -static int if_usb_submit_rx_urb(struct if_usb_card *cardp); -static int if_usb_reset_device(struct if_usb_card *cardp); - -/** - * if_usb_wrike_bulk_callback - call back to handle URB status - * - * @param urb pointer to urb structure - */ -static void if_usb_write_bulk_callback(struct urb *urb) -{ - if (urb->status != 0) { - /* print the failure status number for debug */ - pr_info("URB in failure status: %d\n", urb->status); - } else { - lbtf_deb_usb2(&urb->dev->dev, "URB status is successful\n"); - lbtf_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", - urb->actual_length); - } -} - -/** - * if_usb_free - free tx/rx urb, skb and rx buffer - * - * @param cardp pointer if_usb_card - */ -static void if_usb_free(struct if_usb_card *cardp) -{ - lbtf_deb_enter(LBTF_DEB_USB); - - /* Unlink tx & rx urb */ - usb_kill_urb(cardp->tx_urb); - usb_kill_urb(cardp->rx_urb); - usb_kill_urb(cardp->cmd_urb); - - usb_free_urb(cardp->tx_urb); - cardp->tx_urb = NULL; - - usb_free_urb(cardp->rx_urb); - cardp->rx_urb = NULL; - - usb_free_urb(cardp->cmd_urb); - cardp->cmd_urb = NULL; - - kfree(cardp->ep_out_buf); - cardp->ep_out_buf = NULL; - - lbtf_deb_leave(LBTF_DEB_USB); -} - -static void if_usb_setup_firmware(struct lbtf_private *priv) -{ - struct if_usb_card *cardp = priv->card; - struct cmd_ds_set_boot2_ver b2_cmd; - - lbtf_deb_enter(LBTF_DEB_USB); - - if_usb_submit_rx_urb(cardp); - b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); - b2_cmd.action = 0; - b2_cmd.version = cardp->boot2_version; - - if (lbtf_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) - lbtf_deb_usb("Setting boot2 version failed\n"); - - lbtf_deb_leave(LBTF_DEB_USB); -} - -static void if_usb_fw_timeo(unsigned long priv) -{ - struct if_usb_card *cardp = (void *)priv; - - lbtf_deb_enter(LBTF_DEB_USB); - if (!cardp->fwdnldover) { - /* Download timed out */ - cardp->priv->surpriseremoved = 1; - pr_err("Download timed out\n"); - } else { - lbtf_deb_usb("Download complete, no event. Assuming success\n"); - } - wake_up(&cardp->fw_wq); - lbtf_deb_leave(LBTF_DEB_USB); -} - -/** - * if_usb_probe - sets the configuration values - * - * @ifnum interface number - * @id pointer to usb_device_id - * - * Returns: 0 on success, error code on failure - */ -static int if_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - struct lbtf_private *priv; - struct if_usb_card *cardp; - int i; - - lbtf_deb_enter(LBTF_DEB_USB); - udev = interface_to_usbdev(intf); - - cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); - if (!cardp) - goto error; - - setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp); - init_waitqueue_head(&cardp->fw_wq); - - cardp->udev = udev; - iface_desc = intf->cur_altsetting; - - lbtf_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" - " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", - le16_to_cpu(udev->descriptor.bcdUSB), - udev->descriptor.bDeviceClass, - udev->descriptor.bDeviceSubClass, - udev->descriptor.bDeviceProtocol); - - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_bulk_in(endpoint)) { - cardp->ep_in_size = - le16_to_cpu(endpoint->wMaxPacketSize); - cardp->ep_in = usb_endpoint_num(endpoint); - - lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n", - cardp->ep_in); - lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n", - cardp->ep_in_size); - } else if (usb_endpoint_is_bulk_out(endpoint)) { - cardp->ep_out_size = - le16_to_cpu(endpoint->wMaxPacketSize); - cardp->ep_out = usb_endpoint_num(endpoint); - - lbtf_deb_usbd(&udev->dev, "out_endpoint = %d\n", - cardp->ep_out); - lbtf_deb_usbd(&udev->dev, "Bulk out size is %d\n", - cardp->ep_out_size); - } - } - if (!cardp->ep_out_size || !cardp->ep_in_size) { - lbtf_deb_usbd(&udev->dev, "Endpoints not found\n"); - /* Endpoints not found */ - goto dealloc; - } - - cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!cardp->rx_urb) { - lbtf_deb_usbd(&udev->dev, "Rx URB allocation failed\n"); - goto dealloc; - } - - cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!cardp->tx_urb) { - lbtf_deb_usbd(&udev->dev, "Tx URB allocation failed\n"); - goto dealloc; - } - - cardp->cmd_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!cardp->cmd_urb) { - lbtf_deb_usbd(&udev->dev, "Cmd URB allocation failed\n"); - goto dealloc; - } - - cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, - GFP_KERNEL); - if (!cardp->ep_out_buf) { - lbtf_deb_usbd(&udev->dev, "Could not allocate buffer\n"); - goto dealloc; - } - - priv = lbtf_add_card(cardp, &udev->dev); - if (!priv) - goto dealloc; - - cardp->priv = priv; - - priv->hw_host_to_card = if_usb_host_to_card; - priv->hw_prog_firmware = if_usb_prog_firmware; - priv->hw_reset_device = if_usb_reset_device; - cardp->boot2_version = udev->descriptor.bcdDevice; - - usb_get_dev(udev); - usb_set_intfdata(intf, cardp); - - return 0; - -dealloc: - if_usb_free(cardp); -error: -lbtf_deb_leave(LBTF_DEB_MAIN); - return -ENOMEM; -} - -/** - * if_usb_disconnect - free resource and cleanup - * - * @intf USB interface structure - */ -static void if_usb_disconnect(struct usb_interface *intf) -{ - struct if_usb_card *cardp = usb_get_intfdata(intf); - struct lbtf_private *priv = cardp->priv; - - lbtf_deb_enter(LBTF_DEB_MAIN); - - if_usb_reset_device(cardp); - - if (priv) - lbtf_remove_card(priv); - - /* Unlink and free urb */ - if_usb_free(cardp); - - usb_set_intfdata(intf, NULL); - usb_put_dev(interface_to_usbdev(intf)); - - lbtf_deb_leave(LBTF_DEB_MAIN); -} - -/** - * if_usb_send_fw_pkt - This function downloads the FW - * - * @priv pointer to struct lbtf_private - * - * Returns: 0 - */ -static int if_usb_send_fw_pkt(struct if_usb_card *cardp) -{ - struct fwdata *fwdata = cardp->ep_out_buf; - u8 *firmware = (u8 *) cardp->fw->data; - - lbtf_deb_enter(LBTF_DEB_FW); - - /* If we got a CRC failure on the last block, back - up and retry it */ - if (!cardp->CRC_OK) { - cardp->totalbytes = cardp->fwlastblksent; - cardp->fwseqnum--; - } - - lbtf_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n", - cardp->totalbytes); - - /* struct fwdata (which we sent to the card) has an - extra __le32 field in between the header and the data, - which is not in the struct fwheader in the actual - firmware binary. Insert the seqnum in the middle... */ - memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], - sizeof(struct fwheader)); - - cardp->fwlastblksent = cardp->totalbytes; - cardp->totalbytes += sizeof(struct fwheader); - - memcpy(fwdata->data, &firmware[cardp->totalbytes], - le32_to_cpu(fwdata->hdr.datalength)); - - lbtf_deb_usb2(&cardp->udev->dev, "Data length = %d\n", - le32_to_cpu(fwdata->hdr.datalength)); - - fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); - cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); - - usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + - le32_to_cpu(fwdata->hdr.datalength), 0); - - if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) { - lbtf_deb_usb2(&cardp->udev->dev, "There are data to follow\n"); - lbtf_deb_usb2(&cardp->udev->dev, - "seqnum = %d totalbytes = %d\n", - cardp->fwseqnum, cardp->totalbytes); - } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) { - lbtf_deb_usb2(&cardp->udev->dev, - "Host has finished FW downloading\n"); - lbtf_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n"); - - /* Host has finished FW downloading - * Donwloading FW JUMP BLOCK - */ - cardp->fwfinalblk = 1; - } - - lbtf_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n", - cardp->totalbytes); - - lbtf_deb_leave(LBTF_DEB_FW); - return 0; -} - -static int if_usb_reset_device(struct if_usb_card *cardp) -{ - struct cmd_ds_802_11_reset *cmd = cardp->ep_out_buf + 4; - int ret; - - lbtf_deb_enter(LBTF_DEB_USB); - - *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); - - cmd->hdr.command = cpu_to_le16(CMD_802_11_RESET); - cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset)); - cmd->hdr.result = cpu_to_le16(0); - cmd->hdr.seqnum = cpu_to_le16(0x5a5a); - cmd->action = cpu_to_le16(CMD_ACT_HALT); - usb_tx_block(cardp, cardp->ep_out_buf, - 4 + sizeof(struct cmd_ds_802_11_reset), 0); - - msleep(100); - ret = usb_reset_device(cardp->udev); - msleep(100); - - lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret); - - return ret; -} - -/** - * usb_tx_block - transfer data to the device - * - * @priv pointer to struct lbtf_private - * @payload pointer to payload data - * @nb data length - * @data non-zero for data, zero for commands - * - * Returns: 0 on success, nonzero otherwise. - */ -static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, - uint16_t nb, u8 data) -{ - int ret = -1; - struct urb *urb; - - lbtf_deb_enter(LBTF_DEB_USB); - /* check if device is removed */ - if (cardp->priv->surpriseremoved) { - lbtf_deb_usbd(&cardp->udev->dev, "Device removed\n"); - goto tx_ret; - } - - if (data) - urb = cardp->tx_urb; - else - urb = cardp->cmd_urb; - - usb_fill_bulk_urb(urb, cardp->udev, - usb_sndbulkpipe(cardp->udev, - cardp->ep_out), - payload, nb, if_usb_write_bulk_callback, cardp); - - urb->transfer_flags |= URB_ZERO_PACKET; - - if (usb_submit_urb(urb, GFP_ATOMIC)) { - lbtf_deb_usbd(&cardp->udev->dev, - "usb_submit_urb failed: %d\n", ret); - goto tx_ret; - } - - lbtf_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n"); - - ret = 0; - -tx_ret: - lbtf_deb_leave(LBTF_DEB_USB); - return ret; -} - -static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, - void (*callbackfn)(struct urb *urb)) -{ - struct sk_buff *skb; - int ret = -1; - - lbtf_deb_enter(LBTF_DEB_USB); - - skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); - if (!skb) { - pr_err("No free skb\n"); - lbtf_deb_leave(LBTF_DEB_USB); - return -1; - } - - cardp->rx_skb = skb; - - /* Fill the receive configuration URB and initialise the Rx call back */ - usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, - usb_rcvbulkpipe(cardp->udev, cardp->ep_in), - skb_tail_pointer(skb), - MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, cardp); - - cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; - - lbtf_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", - cardp->rx_urb); - ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC); - if (ret) { - lbtf_deb_usbd(&cardp->udev->dev, - "Submit Rx URB failed: %d\n", ret); - kfree_skb(skb); - cardp->rx_skb = NULL; - lbtf_deb_leave(LBTF_DEB_USB); - return -1; - } else { - lbtf_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n"); - lbtf_deb_leave(LBTF_DEB_USB); - return 0; - } -} - -static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) -{ - return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); -} - -static int if_usb_submit_rx_urb(struct if_usb_card *cardp) -{ - return __if_usb_submit_rx_urb(cardp, &if_usb_receive); -} - -static void if_usb_receive_fwload(struct urb *urb) -{ - struct if_usb_card *cardp = urb->context; - struct sk_buff *skb = cardp->rx_skb; - struct fwsyncheader *syncfwheader; - struct bootcmdresp bcmdresp; - - lbtf_deb_enter(LBTF_DEB_USB); - if (urb->status) { - lbtf_deb_usbd(&cardp->udev->dev, - "URB status is failed during fw load\n"); - kfree_skb(skb); - lbtf_deb_leave(LBTF_DEB_USB); - return; - } - - if (cardp->fwdnldover) { - __le32 *tmp = (__le32 *)(skb->data); - - if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && - tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) { - /* Firmware ready event received */ - pr_info("Firmware ready event received\n"); - wake_up(&cardp->fw_wq); - } else { - lbtf_deb_usb("Waiting for confirmation; got %x %x\n", - le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1])); - if_usb_submit_rx_urb_fwload(cardp); - } - kfree_skb(skb); - lbtf_deb_leave(LBTF_DEB_USB); - return; - } - if (cardp->bootcmdresp <= 0) { - memcpy(&bcmdresp, skb->data, sizeof(bcmdresp)); - - if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { - kfree_skb(skb); - if_usb_submit_rx_urb_fwload(cardp); - cardp->bootcmdresp = 1; - /* Received valid boot command response */ - lbtf_deb_usbd(&cardp->udev->dev, - "Received valid boot command response\n"); - lbtf_deb_leave(LBTF_DEB_USB); - return; - } - if (bcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { - if (bcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || - bcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || - bcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) { - if (!cardp->bootcmdresp) - pr_info("Firmware already seems alive; resetting\n"); - cardp->bootcmdresp = -1; - } else { - pr_info("boot cmd response wrong magic number (0x%x)\n", - le32_to_cpu(bcmdresp.magic)); - } - } else if (bcmdresp.cmd != BOOT_CMD_FW_BY_USB) { - pr_info("boot cmd response cmd_tag error (%d)\n", - bcmdresp.cmd); - } else if (bcmdresp.result != BOOT_CMD_RESP_OK) { - pr_info("boot cmd response result error (%d)\n", - bcmdresp.result); - } else { - cardp->bootcmdresp = 1; - lbtf_deb_usbd(&cardp->udev->dev, - "Received valid boot command response\n"); - } - - kfree_skb(skb); - if_usb_submit_rx_urb_fwload(cardp); - lbtf_deb_leave(LBTF_DEB_USB); - return; - } - - syncfwheader = kmemdup(skb->data, sizeof(struct fwsyncheader), - GFP_ATOMIC); - if (!syncfwheader) { - lbtf_deb_usbd(&cardp->udev->dev, - "Failure to allocate syncfwheader\n"); - kfree_skb(skb); - lbtf_deb_leave(LBTF_DEB_USB); - return; - } - - if (!syncfwheader->cmd) { - lbtf_deb_usb2(&cardp->udev->dev, - "FW received Blk with correct CRC\n"); - lbtf_deb_usb2(&cardp->udev->dev, - "FW received Blk seqnum = %d\n", - le32_to_cpu(syncfwheader->seqnum)); - cardp->CRC_OK = 1; - } else { - lbtf_deb_usbd(&cardp->udev->dev, - "FW received Blk with CRC error\n"); - cardp->CRC_OK = 0; - } - - kfree_skb(skb); - - /* reschedule timer for 200ms hence */ - mod_timer(&cardp->fw_timeout, jiffies + (HZ/5)); - - if (cardp->fwfinalblk) { - cardp->fwdnldover = 1; - goto exit; - } - - if_usb_send_fw_pkt(cardp); - - exit: - if_usb_submit_rx_urb_fwload(cardp); - - kfree(syncfwheader); - - lbtf_deb_leave(LBTF_DEB_USB); -} - -#define MRVDRV_MIN_PKT_LEN 30 - -static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, - struct if_usb_card *cardp, - struct lbtf_private *priv) -{ - if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN - || recvlength < MRVDRV_MIN_PKT_LEN) { - lbtf_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n"); - kfree_skb(skb); - return; - } - - skb_put(skb, recvlength); - skb_pull(skb, MESSAGE_HEADER_LEN); - lbtf_rx(priv, skb); -} - -static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, - struct sk_buff *skb, - struct if_usb_card *cardp, - struct lbtf_private *priv) -{ - if (recvlength > LBS_CMD_BUFFER_SIZE) { - lbtf_deb_usbd(&cardp->udev->dev, - "The receive buffer is too large\n"); - kfree_skb(skb); - return; - } - - BUG_ON(!in_interrupt()); - - spin_lock(&priv->driver_lock); - memcpy(priv->cmd_resp_buff, recvbuff + MESSAGE_HEADER_LEN, - recvlength - MESSAGE_HEADER_LEN); - kfree_skb(skb); - lbtf_cmd_response_rx(priv); - spin_unlock(&priv->driver_lock); -} - -/** - * if_usb_receive - read data received from the device. - * - * @urb pointer to struct urb - */ -static void if_usb_receive(struct urb *urb) -{ - struct if_usb_card *cardp = urb->context; - struct sk_buff *skb = cardp->rx_skb; - struct lbtf_private *priv = cardp->priv; - int recvlength = urb->actual_length; - uint8_t *recvbuff = NULL; - uint32_t recvtype = 0; - __le32 *pkt = (__le32 *) skb->data; - - lbtf_deb_enter(LBTF_DEB_USB); - - if (recvlength) { - if (urb->status) { - lbtf_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n", - urb->status); - kfree_skb(skb); - goto setup_for_next; - } - - recvbuff = skb->data; - recvtype = le32_to_cpu(pkt[0]); - lbtf_deb_usbd(&cardp->udev->dev, - "Recv length = 0x%x, Recv type = 0x%X\n", - recvlength, recvtype); - } else if (urb->status) { - kfree_skb(skb); - lbtf_deb_leave(LBTF_DEB_USB); - return; - } - - switch (recvtype) { - case CMD_TYPE_DATA: - process_cmdtypedata(recvlength, skb, cardp, priv); - break; - - case CMD_TYPE_REQUEST: - process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); - break; - - case CMD_TYPE_INDICATION: - { - /* Event cause handling */ - u32 event_cause = le32_to_cpu(pkt[1]); - lbtf_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", - event_cause); - - /* Icky undocumented magic special case */ - if (event_cause & 0xffff0000) { - u16 tmp; - u8 retrycnt; - u8 failure; - - tmp = event_cause >> 16; - retrycnt = tmp & 0x00ff; - failure = (tmp & 0xff00) >> 8; - lbtf_send_tx_feedback(priv, retrycnt, failure); - } else if (event_cause == LBTF_EVENT_BCN_SENT) - lbtf_bcn_sent(priv); - else - lbtf_deb_usbd(&cardp->udev->dev, - "Unsupported notification %d received\n", - event_cause); - kfree_skb(skb); - break; - } - default: - lbtf_deb_usbd(&cardp->udev->dev, - "libertastf: unknown command type 0x%X\n", recvtype); - kfree_skb(skb); - break; - } - -setup_for_next: - if_usb_submit_rx_urb(cardp); - lbtf_deb_leave(LBTF_DEB_USB); -} - -/** - * if_usb_host_to_card - Download data to the device - * - * @priv pointer to struct lbtf_private structure - * @type type of data - * @buf pointer to data buffer - * @len number of bytes - * - * Returns: 0 on success, nonzero otherwise - */ -static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type, - uint8_t *payload, uint16_t nb) -{ - struct if_usb_card *cardp = priv->card; - u8 data = 0; - - lbtf_deb_usbd(&cardp->udev->dev, "*** type = %u\n", type); - lbtf_deb_usbd(&cardp->udev->dev, "size after = %d\n", nb); - - if (type == MVMS_CMD) { - *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); - } else { - *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); - data = 1; - } - - memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); - - return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN, - data); -} - -/** - * if_usb_issue_boot_command - Issue boot command to Boot2. - * - * @ivalue 1 boots from FW by USB-Download, 2 boots from FW in EEPROM. - * - * Returns: 0 - */ -static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) -{ - struct bootcmd *bootcmd = cardp->ep_out_buf; - - /* Prepare command */ - bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); - bootcmd->cmd = ivalue; - memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); - - /* Issue command */ - usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd), 0); - - return 0; -} - - -/** - * check_fwfile_format - Check the validity of Boot2/FW image. - * - * @data pointer to image - * @totlen image length - * - * Returns: 0 if the image is valid, nonzero otherwise. - */ -static int check_fwfile_format(const u8 *data, u32 totlen) -{ - u32 bincmd, exit; - u32 blksize, offset, len; - int ret; - - ret = 1; - exit = len = 0; - - do { - struct fwheader *fwh = (void *) data; - - bincmd = le32_to_cpu(fwh->dnldcmd); - blksize = le32_to_cpu(fwh->datalength); - switch (bincmd) { - case FW_HAS_DATA_TO_RECV: - offset = sizeof(struct fwheader) + blksize; - data += offset; - len += offset; - if (len >= totlen) - exit = 1; - break; - case FW_HAS_LAST_BLOCK: - exit = 1; - ret = 0; - break; - default: - exit = 1; - break; - } - } while (!exit); - - if (ret) - pr_err("firmware file format check FAIL\n"); - else - lbtf_deb_fw("firmware file format check PASS\n"); - - return ret; -} - - -static int if_usb_prog_firmware(struct if_usb_card *cardp) -{ - int i = 0; - static int reset_count = 10; - int ret = 0; - - lbtf_deb_enter(LBTF_DEB_USB); - - kernel_param_lock(THIS_MODULE); - ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev); - if (ret < 0) { - pr_err("request_firmware() failed with %#x\n", ret); - pr_err("firmware %s not found\n", lbtf_fw_name); - kernel_param_unlock(THIS_MODULE); - goto done; - } - kernel_param_unlock(THIS_MODULE); - - if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) - goto release_fw; - -restart: - if (if_usb_submit_rx_urb_fwload(cardp) < 0) { - lbtf_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); - ret = -1; - goto release_fw; - } - - cardp->bootcmdresp = 0; - do { - int j = 0; - i++; - /* Issue Boot command = 1, Boot from Download-FW */ - if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); - /* wait for command response */ - do { - j++; - msleep_interruptible(100); - } while (cardp->bootcmdresp == 0 && j < 10); - } while (cardp->bootcmdresp == 0 && i < 5); - - if (cardp->bootcmdresp <= 0) { - if (--reset_count >= 0) { - if_usb_reset_device(cardp); - goto restart; - } - return -1; - } - - i = 0; - - cardp->totalbytes = 0; - cardp->fwlastblksent = 0; - cardp->CRC_OK = 1; - cardp->fwdnldover = 0; - cardp->fwseqnum = -1; - cardp->totalbytes = 0; - cardp->fwfinalblk = 0; - - /* Send the first firmware packet... */ - if_usb_send_fw_pkt(cardp); - - /* ... and wait for the process to complete */ - wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved || - cardp->fwdnldover); - - del_timer_sync(&cardp->fw_timeout); - usb_kill_urb(cardp->rx_urb); - - if (!cardp->fwdnldover) { - pr_info("failed to load fw, resetting device!\n"); - if (--reset_count >= 0) { - if_usb_reset_device(cardp); - goto restart; - } - - pr_info("FW download failure, time = %d ms\n", i * 100); - ret = -1; - goto release_fw; - } - - cardp->priv->fw_ready = 1; - - release_fw: - release_firmware(cardp->fw); - cardp->fw = NULL; - - if_usb_setup_firmware(cardp->priv); - - done: - lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret); - return ret; -} - - -#define if_usb_suspend NULL -#define if_usb_resume NULL - -static struct usb_driver if_usb_driver = { - .name = DRV_NAME, - .probe = if_usb_probe, - .disconnect = if_usb_disconnect, - .id_table = if_usb_table, - .suspend = if_usb_suspend, - .resume = if_usb_resume, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(if_usb_driver); - -MODULE_DESCRIPTION("8388 USB WLAN Thinfirm Driver"); -MODULE_AUTHOR("Cozybit Inc."); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas_tf/if_usb.h b/drivers/net/wireless/libertas_tf/if_usb.h deleted file mode 100644 index 6fa5b3f59efe..000000000000 --- a/drivers/net/wireless/libertas_tf/if_usb.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2008, cozybit Inc. - * Copyright (C) 2003-2006, Marvell International Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ -#include -#include - -struct lbtf_private; - -/** - * This file contains definition for USB interface. - */ -#define CMD_TYPE_REQUEST 0xF00DFACE -#define CMD_TYPE_DATA 0xBEADC0DE -#define CMD_TYPE_INDICATION 0xBEEFFACE - -#define BOOT_CMD_FW_BY_USB 0x01 -#define BOOT_CMD_FW_IN_EEPROM 0x02 -#define BOOT_CMD_UPDATE_BOOT2 0x03 -#define BOOT_CMD_UPDATE_FW 0x04 -#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */ - -struct bootcmd { - __le32 magic; - uint8_t cmd; - uint8_t pad[11]; -}; - -#define BOOT_CMD_RESP_OK 0x0001 -#define BOOT_CMD_RESP_FAIL 0x0000 - -struct bootcmdresp { - __le32 magic; - uint8_t cmd; - uint8_t result; - uint8_t pad[2]; -}; - -/** USB card description structure*/ -struct if_usb_card { - struct usb_device *udev; - struct urb *rx_urb, *tx_urb, *cmd_urb; - struct lbtf_private *priv; - - struct sk_buff *rx_skb; - - uint8_t ep_in; - uint8_t ep_out; - - int8_t bootcmdresp; - - int ep_in_size; - - void *ep_out_buf; - int ep_out_size; - - const struct firmware *fw; - struct timer_list fw_timeout; - wait_queue_head_t fw_wq; - uint32_t fwseqnum; - uint32_t totalbytes; - uint32_t fwlastblksent; - uint8_t CRC_OK; - uint8_t fwdnldover; - uint8_t fwfinalblk; - - __le16 boot2_version; -}; - -/** fwheader */ -struct fwheader { - __le32 dnldcmd; - __le32 baseaddr; - __le32 datalength; - __le32 CRC; -}; - -#define FW_MAX_DATA_BLK_SIZE 600 -/** FWData */ -struct fwdata { - struct fwheader hdr; - __le32 seqnum; - uint8_t data[0]; -}; - -/** fwsyncheader */ -struct fwsyncheader { - __le32 cmd; - __le32 seqnum; -}; - -#define FW_HAS_DATA_TO_RECV 0x00000001 -#define FW_HAS_LAST_BLOCK 0x00000004 diff --git a/drivers/net/wireless/libertas_tf/libertas_tf.h b/drivers/net/wireless/libertas_tf/libertas_tf.h deleted file mode 100644 index ad77b92d0b41..000000000000 --- a/drivers/net/wireless/libertas_tf/libertas_tf.h +++ /dev/null @@ -1,519 +0,0 @@ -/* - * Copyright (C) 2008, cozybit Inc. - * Copyright (C) 2007, Red Hat, Inc. - * Copyright (C) 2003-2006, Marvell International Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ -#include -#include -#include -#include - -#include "deb_defs.h" - -#ifndef DRV_NAME -#define DRV_NAME "libertas_tf" -#endif - -#define MRVL_DEFAULT_RETRIES 9 -#define MRVL_PER_PACKET_RATE 0x10 -#define MRVL_MAX_BCN_SIZE 440 -#define CMD_OPTION_WAITFORRSP 0x0002 - -/* Return command are almost always the same as the host command, but with - * bit 15 set high. There are a few exceptions, though... - */ -#define CMD_RET(cmd) (0x8000 | cmd) - -/* Command codes */ -#define CMD_GET_HW_SPEC 0x0003 -#define CMD_802_11_RESET 0x0005 -#define CMD_MAC_MULTICAST_ADR 0x0010 -#define CMD_802_11_RADIO_CONTROL 0x001c -#define CMD_802_11_RF_CHANNEL 0x001d -#define CMD_802_11_RF_TX_POWER 0x001e -#define CMD_MAC_CONTROL 0x0028 -#define CMD_802_11_MAC_ADDRESS 0x004d -#define CMD_SET_BOOT2_VER 0x00a5 -#define CMD_802_11_BEACON_CTRL 0x00b0 -#define CMD_802_11_BEACON_SET 0x00cb -#define CMD_802_11_SET_MODE 0x00cc -#define CMD_802_11_SET_BSSID 0x00cd - -#define CMD_ACT_GET 0x0000 -#define CMD_ACT_SET 0x0001 - -/* Define action or option for CMD_802_11_RESET */ -#define CMD_ACT_HALT 0x0003 - -/* Define action or option for CMD_MAC_CONTROL */ -#define CMD_ACT_MAC_RX_ON 0x0001 -#define CMD_ACT_MAC_TX_ON 0x0002 -#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 -#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 -#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 -#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 - -/* Define action or option for CMD_802_11_RADIO_CONTROL */ -#define CMD_TYPE_AUTO_PREAMBLE 0x0001 -#define CMD_TYPE_SHORT_PREAMBLE 0x0002 -#define CMD_TYPE_LONG_PREAMBLE 0x0003 - -#define TURN_ON_RF 0x01 -#define RADIO_ON 0x01 -#define RADIO_OFF 0x00 - -#define SET_AUTO_PREAMBLE 0x05 -#define SET_SHORT_PREAMBLE 0x03 -#define SET_LONG_PREAMBLE 0x01 - -/* Define action or option for CMD_802_11_RF_CHANNEL */ -#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 -#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 - -/* Codes for CMD_802_11_SET_MODE */ -enum lbtf_mode { - LBTF_PASSIVE_MODE, - LBTF_STA_MODE, - LBTF_AP_MODE, -}; - -/** Card Event definition */ -#define MACREG_INT_CODE_FIRMWARE_READY 48 -/** Buffer Constants */ - -/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical -* addresses of TxPD buffers. Station has only 8 TxPD available, Whereas -* driver has more local TxPDs. Each TxPD on the host memory is associated -* with a Tx control node. The driver maintains 8 RxPD descriptors for -* station firmware to store Rx packet information. -* -* Current version of MAC has a 32x6 multicast address buffer. -* -* 802.11b can have up to 14 channels, the driver keeps the -* BSSID(MAC address) of each APs or Ad hoc stations it has sensed. -*/ - -#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 -#define LBS_NUM_CMD_BUFFERS 10 -#define LBS_CMD_BUFFER_SIZE (2 * 1024) -#define MRVDRV_MAX_CHANNEL_SIZE 14 -#define MRVDRV_SNAP_HEADER_LEN 8 - -#define LBS_UPLD_SIZE 2312 -#define DEV_NAME_LEN 32 - -/** Misc constants */ -/* This section defines 802.11 specific contants */ - -#define MRVDRV_MAX_REGION_CODE 6 -/** - * the table to keep region code - */ -#define LBTF_REGDOMAIN_US 0x10 -#define LBTF_REGDOMAIN_CA 0x20 -#define LBTF_REGDOMAIN_EU 0x30 -#define LBTF_REGDOMAIN_SP 0x31 -#define LBTF_REGDOMAIN_FR 0x32 -#define LBTF_REGDOMAIN_JP 0x40 - -#define SBI_EVENT_CAUSE_SHIFT 3 - -/** RxPD status */ - -#define MRVDRV_RXPD_STATUS_OK 0x0001 - - -/* This is for firmware specific length */ -#define EXTRA_LEN 36 - -#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ - (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) - -#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ - (ETH_FRAME_LEN + sizeof(struct rxpd) \ - + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) - -#define CMD_F_HOSTCMD (1 << 0) -#define FW_CAPINFO_WPA (1 << 0) - -#define RF_ANTENNA_1 0x1 -#define RF_ANTENNA_2 0x2 -#define RF_ANTENNA_AUTO 0xFFFF - -#define LBTF_EVENT_BCN_SENT 55 - -/** Global Variable Declaration */ -/** mv_ms_type */ -enum mv_ms_type { - MVMS_DAT = 0, - MVMS_CMD = 1, - MVMS_TXDONE = 2, - MVMS_EVENT -}; - -extern struct workqueue_struct *lbtf_wq; - -struct lbtf_private; - -struct lbtf_offset_value { - u32 offset; - u32 value; -}; - -struct channel_range { - u8 regdomain; - u8 start; - u8 end; /* exclusive (channel must be less than end) */ -}; - -struct if_usb_card; - -/** Private structure for the MV device */ -struct lbtf_private { - void *card; - struct ieee80211_hw *hw; - - /* Command response buffer */ - u8 cmd_resp_buff[LBS_UPLD_SIZE]; - /* Download sent: - bit0 1/0=data_sent/data_tx_done, - bit1 1/0=cmd_sent/cmd_tx_done, - all other bits reserved 0 */ - struct ieee80211_vif *vif; - - struct work_struct cmd_work; - struct work_struct tx_work; - /** Hardware access */ - int (*hw_host_to_card) (struct lbtf_private *priv, u8 type, u8 *payload, u16 nb); - int (*hw_prog_firmware) (struct if_usb_card *cardp); - int (*hw_reset_device) (struct if_usb_card *cardp); - - - /** Wlan adapter data structure*/ - /** STATUS variables */ - u32 fwrelease; - u32 fwcapinfo; - /* protected with big lock */ - - struct mutex lock; - - /** command-related variables */ - u16 seqnum; - /* protected by big lock */ - - struct cmd_ctrl_node *cmd_array; - /** Current command */ - struct cmd_ctrl_node *cur_cmd; - /** command Queues */ - /** Free command buffers */ - struct list_head cmdfreeq; - /** Pending command buffers */ - struct list_head cmdpendingq; - - /** spin locks */ - spinlock_t driver_lock; - - /** Timers */ - struct timer_list command_timer; - int nr_retries; - int cmd_timed_out; - - u8 cmd_response_rxed; - - /** capability Info used in Association, start, join */ - u16 capability; - - /** MAC address information */ - u8 current_addr[ETH_ALEN]; - u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; - u32 nr_of_multicastmacaddr; - int cur_freq; - - struct sk_buff *skb_to_tx; - struct sk_buff *tx_skb; - - /** NIC Operation characteristics */ - u16 mac_control; - u16 regioncode; - struct channel_range range; - - u8 radioon; - u32 preamble; - - struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[12]; - struct ieee80211_supported_band band; - struct lbtf_offset_value offsetvalue; - - u8 fw_ready; - u8 surpriseremoved; - struct sk_buff_head bc_ps_buf; - - /* Most recently reported noise in dBm */ - s8 noise; -}; - -/* 802.11-related definitions */ - -/* TxPD descriptor */ -struct txpd { - /* Current Tx packet status */ - __le32 tx_status; - /* Tx control */ - __le32 tx_control; - __le32 tx_packet_location; - /* Tx packet length */ - __le16 tx_packet_length; - /* First 2 byte of destination MAC address */ - u8 tx_dest_addr_high[2]; - /* Last 4 byte of destination MAC address */ - u8 tx_dest_addr_low[4]; - /* Pkt Priority */ - u8 priority; - /* Pkt Trasnit Power control */ - u8 powermgmt; - /* Time the packet has been queued in the driver (units = 2ms) */ - u8 pktdelay_2ms; - /* reserved */ - u8 reserved1; -}; - -/* RxPD Descriptor */ -struct rxpd { - /* Current Rx packet status */ - __le16 status; - - /* SNR */ - u8 snr; - - /* Tx control */ - u8 rx_control; - - /* Pkt length */ - __le16 pkt_len; - - /* Noise Floor */ - u8 nf; - - /* Rx Packet Rate */ - u8 rx_rate; - - /* Pkt addr */ - __le32 pkt_ptr; - - /* Next Rx RxPD addr */ - __le32 next_rxpd_ptr; - - /* Pkt Priority */ - u8 priority; - u8 reserved[3]; -}; - -struct cmd_header { - __le16 command; - __le16 size; - __le16 seqnum; - __le16 result; -} __packed; - -struct cmd_ctrl_node { - struct list_head list; - int result; - /* command response */ - int (*callback)(struct lbtf_private *, - unsigned long, struct cmd_header *); - unsigned long callback_arg; - /* command data */ - struct cmd_header *cmdbuf; - /* wait queue */ - u16 cmdwaitqwoken; - wait_queue_head_t cmdwait_q; -}; - -/* - * Define data structure for CMD_GET_HW_SPEC - * This structure defines the response for the GET_HW_SPEC command - */ -struct cmd_ds_get_hw_spec { - struct cmd_header hdr; - - /* HW Interface version number */ - __le16 hwifversion; - /* HW version number */ - __le16 version; - /* Max number of TxPD FW can handle */ - __le16 nr_txpd; - /* Max no of Multicast address */ - __le16 nr_mcast_adr; - /* MAC address */ - u8 permanentaddr[6]; - - /* region Code */ - __le16 regioncode; - - /* Number of antenna used */ - __le16 nr_antenna; - - /* FW release number, example 0x01030304 = 2.3.4p1 */ - __le32 fwrelease; - - /* Base Address of TxPD queue */ - __le32 wcb_base; - /* Read Pointer of RxPd queue */ - __le32 rxpd_rdptr; - - /* Write Pointer of RxPd queue */ - __le32 rxpd_wrptr; - - /*FW/HW capability */ - __le32 fwcapinfo; -} __packed; - -struct cmd_ds_mac_control { - struct cmd_header hdr; - __le16 action; - u16 reserved; -}; - -struct cmd_ds_802_11_mac_address { - struct cmd_header hdr; - - __le16 action; - uint8_t macadd[ETH_ALEN]; -}; - -struct cmd_ds_mac_multicast_addr { - struct cmd_header hdr; - - __le16 action; - __le16 nr_of_adrs; - u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; -}; - -struct cmd_ds_set_mode { - struct cmd_header hdr; - - __le16 mode; -}; - -struct cmd_ds_set_bssid { - struct cmd_header hdr; - - u8 bssid[6]; - u8 activate; -}; - -struct cmd_ds_802_11_radio_control { - struct cmd_header hdr; - - __le16 action; - __le16 control; -}; - - -struct cmd_ds_802_11_rf_channel { - struct cmd_header hdr; - - __le16 action; - __le16 channel; - __le16 rftype; /* unused */ - __le16 reserved; /* unused */ - u8 channellist[32]; /* unused */ -}; - -struct cmd_ds_set_boot2_ver { - struct cmd_header hdr; - - __le16 action; - __le16 version; -}; - -struct cmd_ds_802_11_reset { - struct cmd_header hdr; - - __le16 action; -}; - -struct cmd_ds_802_11_beacon_control { - struct cmd_header hdr; - - __le16 action; - __le16 beacon_enable; - __le16 beacon_period; -}; - -struct cmd_ds_802_11_beacon_set { - struct cmd_header hdr; - - __le16 len; - u8 beacon[MRVL_MAX_BCN_SIZE]; -}; - -struct lbtf_private; -struct cmd_ctrl_node; - -/** Function Prototype Declaration */ -void lbtf_set_mac_control(struct lbtf_private *priv); - -int lbtf_free_cmd_buffer(struct lbtf_private *priv); - -int lbtf_allocate_cmd_buffer(struct lbtf_private *priv); -int lbtf_execute_next_command(struct lbtf_private *priv); -int lbtf_set_radio_control(struct lbtf_private *priv); -int lbtf_update_hw_spec(struct lbtf_private *priv); -int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv); -void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode); -void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid); -int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr); - -int lbtf_set_channel(struct lbtf_private *priv, u8 channel); - -int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon); -int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, - int beacon_int); - - -int lbtf_process_rx_command(struct lbtf_private *priv); -void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, - int result); -void lbtf_cmd_response_rx(struct lbtf_private *priv); - -/* main.c */ -struct chan_freq_power *lbtf_get_region_cfp_table(u8 region, - int *cfp_no); -struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev); -int lbtf_remove_card(struct lbtf_private *priv); -int lbtf_start_card(struct lbtf_private *priv); -int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb); -void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail); -void lbtf_bcn_sent(struct lbtf_private *priv); - -/* support functions for cmd.c */ -/* lbtf_cmd() infers the size of the buffer to copy data back into, from - the size of the target of the pointer. Since the command to be sent - may often be smaller, that size is set in cmd->size by the caller.*/ -#define lbtf_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \ - uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \ - (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \ - __lbtf_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \ -}) - -#define lbtf_cmd_with_response(priv, cmdnr, cmd) \ - lbtf_cmd(priv, cmdnr, cmd, lbtf_cmd_copyback, (unsigned long) (cmd)) - -void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size); - -int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbtf_private *, unsigned long, - struct cmd_header *), - unsigned long callback_arg); - -int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, - struct cmd_header *resp); diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c deleted file mode 100644 index a47f0acc099a..000000000000 --- a/drivers/net/wireless/libertas_tf/main.c +++ /dev/null @@ -1,766 +0,0 @@ -/* - * Copyright (C) 2008, cozybit Inc. - * Copyright (C) 2003-2006, Marvell International Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include - -#include -#include -#include "libertas_tf.h" - -#define DRIVER_RELEASE_VERSION "004.p0" -/* thinfirm version: 5.132.X.pX */ -#define LBTF_FW_VER_MIN 0x05840300 -#define LBTF_FW_VER_MAX 0x0584ffff -#define QOS_CONTROL_LEN 2 - -/* Module parameters */ -unsigned int lbtf_debug; -EXPORT_SYMBOL_GPL(lbtf_debug); -module_param_named(libertas_tf_debug, lbtf_debug, int, 0644); - -static const char lbtf_driver_version[] = "THINFIRM-USB8388-" DRIVER_RELEASE_VERSION -#ifdef DEBUG - "-dbg" -#endif - ""; - -struct workqueue_struct *lbtf_wq; - -static const struct ieee80211_channel lbtf_channels[] = { - { .center_freq = 2412, .hw_value = 1 }, - { .center_freq = 2417, .hw_value = 2 }, - { .center_freq = 2422, .hw_value = 3 }, - { .center_freq = 2427, .hw_value = 4 }, - { .center_freq = 2432, .hw_value = 5 }, - { .center_freq = 2437, .hw_value = 6 }, - { .center_freq = 2442, .hw_value = 7 }, - { .center_freq = 2447, .hw_value = 8 }, - { .center_freq = 2452, .hw_value = 9 }, - { .center_freq = 2457, .hw_value = 10 }, - { .center_freq = 2462, .hw_value = 11 }, - { .center_freq = 2467, .hw_value = 12 }, - { .center_freq = 2472, .hw_value = 13 }, - { .center_freq = 2484, .hw_value = 14 }, -}; - -/* This table contains the hardware specific values for the modulation rates. */ -static const struct ieee80211_rate lbtf_rates[] = { - { .bitrate = 10, - .hw_value = 0, }, - { .bitrate = 20, - .hw_value = 1, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, - .hw_value = 2, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, - .hw_value = 3, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 60, - .hw_value = 5, - .flags = 0 }, - { .bitrate = 90, - .hw_value = 6, - .flags = 0 }, - { .bitrate = 120, - .hw_value = 7, - .flags = 0 }, - { .bitrate = 180, - .hw_value = 8, - .flags = 0 }, - { .bitrate = 240, - .hw_value = 9, - .flags = 0 }, - { .bitrate = 360, - .hw_value = 10, - .flags = 0 }, - { .bitrate = 480, - .hw_value = 11, - .flags = 0 }, - { .bitrate = 540, - .hw_value = 12, - .flags = 0 }, -}; - -static void lbtf_cmd_work(struct work_struct *work) -{ - struct lbtf_private *priv = container_of(work, struct lbtf_private, - cmd_work); - - lbtf_deb_enter(LBTF_DEB_CMD); - - spin_lock_irq(&priv->driver_lock); - /* command response? */ - if (priv->cmd_response_rxed) { - priv->cmd_response_rxed = 0; - spin_unlock_irq(&priv->driver_lock); - lbtf_process_rx_command(priv); - spin_lock_irq(&priv->driver_lock); - } - - if (priv->cmd_timed_out && priv->cur_cmd) { - struct cmd_ctrl_node *cmdnode = priv->cur_cmd; - - if (++priv->nr_retries > 10) { - lbtf_complete_command(priv, cmdnode, - -ETIMEDOUT); - priv->nr_retries = 0; - } else { - priv->cur_cmd = NULL; - - /* Stick it back at the _top_ of the pending - * queue for immediate resubmission */ - list_add(&cmdnode->list, &priv->cmdpendingq); - } - } - priv->cmd_timed_out = 0; - spin_unlock_irq(&priv->driver_lock); - - if (!priv->fw_ready) { - lbtf_deb_leave_args(LBTF_DEB_CMD, "fw not ready"); - return; - } - - /* Execute the next command */ - if (!priv->cur_cmd) - lbtf_execute_next_command(priv); - - lbtf_deb_leave(LBTF_DEB_CMD); -} - -/** - * lbtf_setup_firmware: initialize firmware. - * - * @priv A pointer to struct lbtf_private structure - * - * Returns: 0 on success. - */ -static int lbtf_setup_firmware(struct lbtf_private *priv) -{ - int ret = -1; - - lbtf_deb_enter(LBTF_DEB_FW); - /* - * Read priv address from HW - */ - eth_broadcast_addr(priv->current_addr); - ret = lbtf_update_hw_spec(priv); - if (ret) { - ret = -1; - goto done; - } - - lbtf_set_mac_control(priv); - lbtf_set_radio_control(priv); - - ret = 0; -done: - lbtf_deb_leave_args(LBTF_DEB_FW, "ret: %d", ret); - return ret; -} - -/** - * This function handles the timeout of command sending. - * It will re-send the same command again. - */ -static void command_timer_fn(unsigned long data) -{ - struct lbtf_private *priv = (struct lbtf_private *)data; - unsigned long flags; - lbtf_deb_enter(LBTF_DEB_CMD); - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (!priv->cur_cmd) { - printk(KERN_DEBUG "libertastf: command timer expired; " - "no pending command\n"); - goto out; - } - - printk(KERN_DEBUG "libertas: command %x timed out\n", - le16_to_cpu(priv->cur_cmd->cmdbuf->command)); - - priv->cmd_timed_out = 1; - queue_work(lbtf_wq, &priv->cmd_work); -out: - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbtf_deb_leave(LBTF_DEB_CMD); -} - -static int lbtf_init_adapter(struct lbtf_private *priv) -{ - lbtf_deb_enter(LBTF_DEB_MAIN); - eth_broadcast_addr(priv->current_addr); - mutex_init(&priv->lock); - - priv->vif = NULL; - setup_timer(&priv->command_timer, command_timer_fn, - (unsigned long)priv); - - INIT_LIST_HEAD(&priv->cmdfreeq); - INIT_LIST_HEAD(&priv->cmdpendingq); - - spin_lock_init(&priv->driver_lock); - - /* Allocate the command buffers */ - if (lbtf_allocate_cmd_buffer(priv)) - return -1; - - lbtf_deb_leave(LBTF_DEB_MAIN); - return 0; -} - -static void lbtf_free_adapter(struct lbtf_private *priv) -{ - lbtf_deb_enter(LBTF_DEB_MAIN); - lbtf_free_cmd_buffer(priv); - del_timer(&priv->command_timer); - lbtf_deb_leave(LBTF_DEB_MAIN); -} - -static void lbtf_op_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct lbtf_private *priv = hw->priv; - - priv->skb_to_tx = skb; - queue_work(lbtf_wq, &priv->tx_work); - /* - * queue will be restarted when we receive transmission feedback if - * there are no buffered multicast frames to send - */ - ieee80211_stop_queues(priv->hw); -} - -static void lbtf_tx_work(struct work_struct *work) -{ - struct lbtf_private *priv = container_of(work, struct lbtf_private, - tx_work); - unsigned int len; - struct ieee80211_tx_info *info; - struct txpd *txpd; - struct sk_buff *skb = NULL; - int err; - - lbtf_deb_enter(LBTF_DEB_MACOPS | LBTF_DEB_TX); - - if ((priv->vif->type == NL80211_IFTYPE_AP) && - (!skb_queue_empty(&priv->bc_ps_buf))) - skb = skb_dequeue(&priv->bc_ps_buf); - else if (priv->skb_to_tx) { - skb = priv->skb_to_tx; - priv->skb_to_tx = NULL; - } else { - lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); - return; - } - - len = skb->len; - info = IEEE80211_SKB_CB(skb); - txpd = (struct txpd *) skb_push(skb, sizeof(struct txpd)); - - if (priv->surpriseremoved) { - dev_kfree_skb_any(skb); - lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); - return; - } - - memset(txpd, 0, sizeof(struct txpd)); - /* Activate per-packet rate selection */ - txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE | - ieee80211_get_tx_rate(priv->hw, info)->hw_value); - - /* copy destination address from 802.11 header */ - memcpy(txpd->tx_dest_addr_high, skb->data + sizeof(struct txpd) + 4, - ETH_ALEN); - txpd->tx_packet_length = cpu_to_le16(len); - txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); - lbtf_deb_hex(LBTF_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); - BUG_ON(priv->tx_skb); - spin_lock_irq(&priv->driver_lock); - priv->tx_skb = skb; - err = priv->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len); - spin_unlock_irq(&priv->driver_lock); - if (err) { - dev_kfree_skb_any(skb); - priv->tx_skb = NULL; - pr_err("TX error: %d", err); - } - lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); -} - -static int lbtf_op_start(struct ieee80211_hw *hw) -{ - struct lbtf_private *priv = hw->priv; - void *card = priv->card; - int ret = -1; - - lbtf_deb_enter(LBTF_DEB_MACOPS); - - if (!priv->fw_ready) - /* Upload firmware */ - if (priv->hw_prog_firmware(card)) - goto err_prog_firmware; - - /* poke the firmware */ - priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE; - priv->radioon = RADIO_ON; - priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; - ret = lbtf_setup_firmware(priv); - if (ret) - goto err_prog_firmware; - - if ((priv->fwrelease < LBTF_FW_VER_MIN) || - (priv->fwrelease > LBTF_FW_VER_MAX)) { - ret = -1; - goto err_prog_firmware; - } - - printk(KERN_INFO "libertastf: Marvell WLAN 802.11 thinfirm adapter\n"); - lbtf_deb_leave(LBTF_DEB_MACOPS); - return 0; - -err_prog_firmware: - priv->hw_reset_device(card); - lbtf_deb_leave_args(LBTF_DEB_MACOPS, "error programming fw; ret=%d", ret); - return ret; -} - -static void lbtf_op_stop(struct ieee80211_hw *hw) -{ - struct lbtf_private *priv = hw->priv; - unsigned long flags; - struct sk_buff *skb; - - struct cmd_ctrl_node *cmdnode; - - lbtf_deb_enter(LBTF_DEB_MACOPS); - - /* Flush pending command nodes */ - spin_lock_irqsave(&priv->driver_lock, flags); - list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { - cmdnode->result = -ENOENT; - cmdnode->cmdwaitqwoken = 1; - wake_up_interruptible(&cmdnode->cmdwait_q); - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - cancel_work_sync(&priv->cmd_work); - cancel_work_sync(&priv->tx_work); - while ((skb = skb_dequeue(&priv->bc_ps_buf))) - dev_kfree_skb_any(skb); - priv->radioon = RADIO_OFF; - lbtf_set_radio_control(priv); - - lbtf_deb_leave(LBTF_DEB_MACOPS); -} - -static int lbtf_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct lbtf_private *priv = hw->priv; - lbtf_deb_enter(LBTF_DEB_MACOPS); - if (priv->vif != NULL) - return -EOPNOTSUPP; - - priv->vif = vif; - switch (vif->type) { - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_AP: - lbtf_set_mode(priv, LBTF_AP_MODE); - break; - case NL80211_IFTYPE_STATION: - lbtf_set_mode(priv, LBTF_STA_MODE); - break; - default: - priv->vif = NULL; - return -EOPNOTSUPP; - } - lbtf_set_mac_address(priv, (u8 *) vif->addr); - lbtf_deb_leave(LBTF_DEB_MACOPS); - return 0; -} - -static void lbtf_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct lbtf_private *priv = hw->priv; - lbtf_deb_enter(LBTF_DEB_MACOPS); - - if (priv->vif->type == NL80211_IFTYPE_AP || - priv->vif->type == NL80211_IFTYPE_MESH_POINT) - lbtf_beacon_ctrl(priv, 0, 0); - lbtf_set_mode(priv, LBTF_PASSIVE_MODE); - lbtf_set_bssid(priv, 0, NULL); - priv->vif = NULL; - lbtf_deb_leave(LBTF_DEB_MACOPS); -} - -static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed) -{ - struct lbtf_private *priv = hw->priv; - struct ieee80211_conf *conf = &hw->conf; - lbtf_deb_enter(LBTF_DEB_MACOPS); - - if (conf->chandef.chan->center_freq != priv->cur_freq) { - priv->cur_freq = conf->chandef.chan->center_freq; - lbtf_set_channel(priv, conf->chandef.chan->hw_value); - } - lbtf_deb_leave(LBTF_DEB_MACOPS); - return 0; -} - -static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw, - struct netdev_hw_addr_list *mc_list) -{ - struct lbtf_private *priv = hw->priv; - int i; - struct netdev_hw_addr *ha; - int mc_count = netdev_hw_addr_list_count(mc_list); - - if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) - return mc_count; - - priv->nr_of_multicastmacaddr = mc_count; - i = 0; - netdev_hw_addr_list_for_each(ha, mc_list) - memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN); - - return mc_count; -} - -#define SUPPORTED_FIF_FLAGS FIF_ALLMULTI -static void lbtf_op_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *new_flags, - u64 multicast) -{ - struct lbtf_private *priv = hw->priv; - int old_mac_control = priv->mac_control; - - lbtf_deb_enter(LBTF_DEB_MACOPS); - - changed_flags &= SUPPORTED_FIF_FLAGS; - *new_flags &= SUPPORTED_FIF_FLAGS; - - if (!changed_flags) { - lbtf_deb_leave(LBTF_DEB_MACOPS); - return; - } - - priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; - if (*new_flags & (FIF_ALLMULTI) || - multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) { - priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; - priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; - } else if (multicast) { - priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; - priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE; - lbtf_cmd_set_mac_multicast_addr(priv); - } else { - priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE | - CMD_ACT_MAC_ALL_MULTICAST_ENABLE); - if (priv->nr_of_multicastmacaddr) { - priv->nr_of_multicastmacaddr = 0; - lbtf_cmd_set_mac_multicast_addr(priv); - } - } - - - if (priv->mac_control != old_mac_control) - lbtf_set_mac_control(priv); - - lbtf_deb_leave(LBTF_DEB_MACOPS); -} - -static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct lbtf_private *priv = hw->priv; - struct sk_buff *beacon; - lbtf_deb_enter(LBTF_DEB_MACOPS); - - if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) { - switch (priv->vif->type) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_MESH_POINT: - beacon = ieee80211_beacon_get(hw, vif); - if (beacon) { - lbtf_beacon_set(priv, beacon); - kfree_skb(beacon); - lbtf_beacon_ctrl(priv, 1, - bss_conf->beacon_int); - } - break; - default: - break; - } - } - - if (changes & BSS_CHANGED_BSSID) { - bool activate = !is_zero_ether_addr(bss_conf->bssid); - lbtf_set_bssid(priv, activate, bss_conf->bssid); - } - - if (changes & BSS_CHANGED_ERP_PREAMBLE) { - if (bss_conf->use_short_preamble) - priv->preamble = CMD_TYPE_SHORT_PREAMBLE; - else - priv->preamble = CMD_TYPE_LONG_PREAMBLE; - lbtf_set_radio_control(priv); - } - - lbtf_deb_leave(LBTF_DEB_MACOPS); -} - -static int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx, - struct survey_info *survey) -{ - struct lbtf_private *priv = hw->priv; - struct ieee80211_conf *conf = &hw->conf; - - if (idx != 0) - return -ENOENT; - - survey->channel = conf->chandef.chan; - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = priv->noise; - - return 0; -} - -static const struct ieee80211_ops lbtf_ops = { - .tx = lbtf_op_tx, - .start = lbtf_op_start, - .stop = lbtf_op_stop, - .add_interface = lbtf_op_add_interface, - .remove_interface = lbtf_op_remove_interface, - .config = lbtf_op_config, - .prepare_multicast = lbtf_op_prepare_multicast, - .configure_filter = lbtf_op_configure_filter, - .bss_info_changed = lbtf_op_bss_info_changed, - .get_survey = lbtf_op_get_survey, -}; - -int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb) -{ - struct ieee80211_rx_status stats; - struct rxpd *prxpd; - int need_padding; - unsigned int flags; - struct ieee80211_hdr *hdr; - - lbtf_deb_enter(LBTF_DEB_RX); - - prxpd = (struct rxpd *) skb->data; - - memset(&stats, 0, sizeof(stats)); - if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) - stats.flag |= RX_FLAG_FAILED_FCS_CRC; - stats.freq = priv->cur_freq; - stats.band = IEEE80211_BAND_2GHZ; - stats.signal = prxpd->snr; - priv->noise = prxpd->nf; - /* Marvell rate index has a hole at value 4 */ - if (prxpd->rx_rate > 4) - --prxpd->rx_rate; - stats.rate_idx = prxpd->rx_rate; - skb_pull(skb, sizeof(struct rxpd)); - - hdr = (struct ieee80211_hdr *)skb->data; - flags = le32_to_cpu(*(__le32 *)(skb->data + 4)); - - need_padding = ieee80211_is_data_qos(hdr->frame_control); - need_padding ^= ieee80211_has_a4(hdr->frame_control); - need_padding ^= ieee80211_is_data_qos(hdr->frame_control) && - (*ieee80211_get_qos_ctl(hdr) & - IEEE80211_QOS_CTL_A_MSDU_PRESENT); - - if (need_padding) { - memmove(skb->data + 2, skb->data, skb->len); - skb_reserve(skb, 2); - } - - memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats)); - - lbtf_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n", - skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); - lbtf_deb_hex(LBTF_DEB_RX, "RX Data", skb->data, - min_t(unsigned int, skb->len, 100)); - - ieee80211_rx_irqsafe(priv->hw, skb); - - lbtf_deb_leave(LBTF_DEB_RX); - return 0; -} -EXPORT_SYMBOL_GPL(lbtf_rx); - -/** - * lbtf_add_card: Add and initialize the card, no fw upload yet. - * - * @card A pointer to card - * - * Returns: pointer to struct lbtf_priv. - */ -struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev) -{ - struct ieee80211_hw *hw; - struct lbtf_private *priv = NULL; - - lbtf_deb_enter(LBTF_DEB_MAIN); - - hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops); - if (!hw) - goto done; - - priv = hw->priv; - if (lbtf_init_adapter(priv)) - goto err_init_adapter; - - priv->hw = hw; - priv->card = card; - priv->tx_skb = NULL; - - hw->queues = 1; - ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); - hw->extra_tx_headroom = sizeof(struct txpd); - memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels)); - memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates)); - priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates); - priv->band.bitrates = priv->rates; - priv->band.n_channels = ARRAY_SIZE(lbtf_channels); - priv->band.channels = priv->channels; - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC); - skb_queue_head_init(&priv->bc_ps_buf); - - SET_IEEE80211_DEV(hw, dmdev); - - INIT_WORK(&priv->cmd_work, lbtf_cmd_work); - INIT_WORK(&priv->tx_work, lbtf_tx_work); - if (ieee80211_register_hw(hw)) - goto err_init_adapter; - - goto done; - -err_init_adapter: - lbtf_free_adapter(priv); - ieee80211_free_hw(hw); - priv = NULL; - -done: - lbtf_deb_leave_args(LBTF_DEB_MAIN, "priv %p", priv); - return priv; -} -EXPORT_SYMBOL_GPL(lbtf_add_card); - - -int lbtf_remove_card(struct lbtf_private *priv) -{ - struct ieee80211_hw *hw = priv->hw; - - lbtf_deb_enter(LBTF_DEB_MAIN); - - priv->surpriseremoved = 1; - del_timer(&priv->command_timer); - lbtf_free_adapter(priv); - priv->hw = NULL; - ieee80211_unregister_hw(hw); - ieee80211_free_hw(hw); - - lbtf_deb_leave(LBTF_DEB_MAIN); - return 0; -} -EXPORT_SYMBOL_GPL(lbtf_remove_card); - -void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); - - ieee80211_tx_info_clear_status(info); - /* - * Commented out, otherwise we never go beyond 1Mbit/s using mac80211 - * default pid rc algorithm. - * - * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt; - */ - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail) - info->flags |= IEEE80211_TX_STAT_ACK; - skb_pull(priv->tx_skb, sizeof(struct txpd)); - ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); - priv->tx_skb = NULL; - if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf)) - ieee80211_wake_queues(priv->hw); - else - queue_work(lbtf_wq, &priv->tx_work); -} -EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback); - -void lbtf_bcn_sent(struct lbtf_private *priv) -{ - struct sk_buff *skb = NULL; - - if (priv->vif->type != NL80211_IFTYPE_AP) - return; - - if (skb_queue_empty(&priv->bc_ps_buf)) { - bool tx_buff_bc = false; - - while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) { - skb_queue_tail(&priv->bc_ps_buf, skb); - tx_buff_bc = true; - } - if (tx_buff_bc) { - ieee80211_stop_queues(priv->hw); - queue_work(lbtf_wq, &priv->tx_work); - } - } - - skb = ieee80211_beacon_get(priv->hw, priv->vif); - - if (skb) { - lbtf_beacon_set(priv, skb); - kfree_skb(skb); - } -} -EXPORT_SYMBOL_GPL(lbtf_bcn_sent); - -static int __init lbtf_init_module(void) -{ - lbtf_deb_enter(LBTF_DEB_MAIN); - lbtf_wq = create_workqueue("libertastf"); - if (lbtf_wq == NULL) { - printk(KERN_ERR "libertastf: couldn't create workqueue\n"); - return -ENOMEM; - } - lbtf_deb_leave(LBTF_DEB_MAIN); - return 0; -} - -static void __exit lbtf_exit_module(void) -{ - lbtf_deb_enter(LBTF_DEB_MAIN); - destroy_workqueue(lbtf_wq); - lbtf_deb_leave(LBTF_DEB_MAIN); -} - -module_init(lbtf_init_module); -module_exit(lbtf_exit_module); - -MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library"); -MODULE_AUTHOR("Cozybit Inc."); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig index 7842096bea53..97ec8f35745e 100644 --- a/drivers/net/wireless/marvell/Kconfig +++ b/drivers/net/wireless/marvell/Kconfig @@ -12,5 +12,6 @@ config WLAN_VENDOR_MARVELL if WLAN_VENDOR_MARVELL source "drivers/net/wireless/marvell/libertas/Kconfig" +source "drivers/net/wireless/marvell/libertas_tf/Kconfig" endif # WLAN_VENDOR_MARVELL diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile index 6f7ac46ded33..8f5eb423b71a 100644 --- a/drivers/net/wireless/marvell/Makefile +++ b/drivers/net/wireless/marvell/Makefile @@ -1 +1,3 @@ obj-$(CONFIG_LIBERTAS) += libertas/ + +obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ diff --git a/drivers/net/wireless/marvell/libertas_tf/Kconfig b/drivers/net/wireless/marvell/libertas_tf/Kconfig new file mode 100644 index 000000000000..b5557af90048 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas_tf/Kconfig @@ -0,0 +1,18 @@ +config LIBERTAS_THINFIRM + tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware" + depends on MAC80211 + select FW_LOADER + ---help--- + A library for Marvell Libertas 8xxx devices using thinfirm. + +config LIBERTAS_THINFIRM_DEBUG + bool "Enable full debugging output in the Libertas thin firmware module." + depends on LIBERTAS_THINFIRM + ---help--- + Debugging support. + +config LIBERTAS_THINFIRM_USB + tristate "Marvell Libertas 8388 USB 802.11b/g cards with thin firmware" + depends on LIBERTAS_THINFIRM && USB + ---help--- + A driver for Marvell Libertas 8388 USB devices using thinfirm. diff --git a/drivers/net/wireless/marvell/libertas_tf/Makefile b/drivers/net/wireless/marvell/libertas_tf/Makefile new file mode 100644 index 000000000000..ff5544d6ac9d --- /dev/null +++ b/drivers/net/wireless/marvell/libertas_tf/Makefile @@ -0,0 +1,6 @@ +libertas_tf-objs := main.o cmd.o + +libertas_tf_usb-objs += if_usb.o + +obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf.o +obj-$(CONFIG_LIBERTAS_THINFIRM_USB) += libertas_tf_usb.o diff --git a/drivers/net/wireless/marvell/libertas_tf/cmd.c b/drivers/net/wireless/marvell/libertas_tf/cmd.c new file mode 100644 index 000000000000..909ac3685010 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas_tf/cmd.c @@ -0,0 +1,807 @@ +/* + * Copyright (C) 2008, cozybit Inc. + * Copyright (C) 2003-2006, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +#include "libertas_tf.h" + +static const struct channel_range channel_ranges[] = { + { LBTF_REGDOMAIN_US, 1, 12 }, + { LBTF_REGDOMAIN_CA, 1, 12 }, + { LBTF_REGDOMAIN_EU, 1, 14 }, + { LBTF_REGDOMAIN_JP, 1, 14 }, + { LBTF_REGDOMAIN_SP, 1, 14 }, + { LBTF_REGDOMAIN_FR, 1, 14 }, +}; + +static u16 lbtf_region_code_to_index[MRVDRV_MAX_REGION_CODE] = +{ + LBTF_REGDOMAIN_US, LBTF_REGDOMAIN_CA, LBTF_REGDOMAIN_EU, + LBTF_REGDOMAIN_SP, LBTF_REGDOMAIN_FR, LBTF_REGDOMAIN_JP, +}; + +static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv); + + +/** + * lbtf_cmd_copyback - Simple callback that copies response back into command + * + * @priv A pointer to struct lbtf_private structure + * @extra A pointer to the original command structure for which + * 'resp' is a response + * @resp A pointer to the command response + * + * Returns: 0 on success, error on failure + */ +int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, + struct cmd_header *resp) +{ + struct cmd_header *buf = (void *)extra; + uint16_t copy_len; + + copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); + memcpy(buf, resp, copy_len); + return 0; +} +EXPORT_SYMBOL_GPL(lbtf_cmd_copyback); + +#define CHAN_TO_IDX(chan) ((chan) - 1) + +static void lbtf_geo_init(struct lbtf_private *priv) +{ + const struct channel_range *range = channel_ranges; + u8 ch; + int i; + + for (i = 0; i < ARRAY_SIZE(channel_ranges); i++) + if (channel_ranges[i].regdomain == priv->regioncode) { + range = &channel_ranges[i]; + break; + } + + for (ch = priv->range.start; ch < priv->range.end; ch++) + priv->channels[CHAN_TO_IDX(ch)].flags = 0; +} + +/** + * lbtf_update_hw_spec: Updates the hardware details. + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: 0 on success, error on failure + */ +int lbtf_update_hw_spec(struct lbtf_private *priv) +{ + struct cmd_ds_get_hw_spec cmd; + int ret = -1; + u32 i; + + lbtf_deb_enter(LBTF_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); + ret = lbtf_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); + if (ret) + goto out; + + priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); + + /* The firmware release is in an interesting format: the patch + * level is in the most significant nibble ... so fix that: */ + priv->fwrelease = le32_to_cpu(cmd.fwrelease); + priv->fwrelease = (priv->fwrelease << 8) | + (priv->fwrelease >> 24 & 0xff); + + printk(KERN_INFO "libertastf: %pM, fw %u.%u.%up%u, cap 0x%08x\n", + cmd.permanentaddr, + priv->fwrelease >> 24 & 0xff, + priv->fwrelease >> 16 & 0xff, + priv->fwrelease >> 8 & 0xff, + priv->fwrelease & 0xff, + priv->fwcapinfo); + lbtf_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", + cmd.hwifversion, cmd.version); + + /* Clamp region code to 8-bit since FW spec indicates that it should + * only ever be 8-bit, even though the field size is 16-bit. Some + * firmware returns non-zero high 8 bits here. + */ + priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* use the region code to search for the index */ + if (priv->regioncode == lbtf_region_code_to_index[i]) + break; + } + + /* if it's unidentified region code, use the default (USA) */ + if (i >= MRVDRV_MAX_REGION_CODE) { + priv->regioncode = 0x10; + pr_info("unidentified region code; using the default (USA)\n"); + } + + if (priv->current_addr[0] == 0xff) + memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); + + SET_IEEE80211_PERM_ADDR(priv->hw, priv->current_addr); + + lbtf_geo_init(priv); +out: + lbtf_deb_leave(LBTF_DEB_CMD); + return ret; +} + +/** + * lbtf_set_channel: Set the radio channel + * + * @priv A pointer to struct lbtf_private structure + * @channel The desired channel, or 0 to clear a locked channel + * + * Returns: 0 on success, error on failure + */ +int lbtf_set_channel(struct lbtf_private *priv, u8 channel) +{ + int ret = 0; + struct cmd_ds_802_11_rf_channel cmd; + + lbtf_deb_enter(LBTF_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); + cmd.channel = cpu_to_le16(channel); + + ret = lbtf_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); + lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); + return ret; +} + +int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon) +{ + struct cmd_ds_802_11_beacon_set cmd; + int size; + + lbtf_deb_enter(LBTF_DEB_CMD); + + if (beacon->len > MRVL_MAX_BCN_SIZE) { + lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", -1); + return -1; + } + size = sizeof(cmd) - sizeof(cmd.beacon) + beacon->len; + cmd.hdr.size = cpu_to_le16(size); + cmd.len = cpu_to_le16(beacon->len); + memcpy(cmd.beacon, (u8 *) beacon->data, beacon->len); + + lbtf_cmd_async(priv, CMD_802_11_BEACON_SET, &cmd.hdr, size); + + lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", 0); + return 0; +} + +int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, + int beacon_int) +{ + struct cmd_ds_802_11_beacon_control cmd; + lbtf_deb_enter(LBTF_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.beacon_enable = cpu_to_le16(beacon_enable); + cmd.beacon_period = cpu_to_le16(beacon_int); + + lbtf_cmd_async(priv, CMD_802_11_BEACON_CTRL, &cmd.hdr, sizeof(cmd)); + + lbtf_deb_leave(LBTF_DEB_CMD); + return 0; +} + +static void lbtf_queue_cmd(struct lbtf_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + lbtf_deb_enter(LBTF_DEB_HOST); + + if (!cmdnode) { + lbtf_deb_host("QUEUE_CMD: cmdnode is NULL\n"); + goto qcmd_done; + } + + if (!cmdnode->cmdbuf->size) { + lbtf_deb_host("DNLD_CMD: cmd size is zero\n"); + goto qcmd_done; + } + + cmdnode->result = 0; + spin_lock_irqsave(&priv->driver_lock, flags); + list_add_tail(&cmdnode->list, &priv->cmdpendingq); + spin_unlock_irqrestore(&priv->driver_lock, flags); + + lbtf_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", + le16_to_cpu(cmdnode->cmdbuf->command)); + +qcmd_done: + lbtf_deb_leave(LBTF_DEB_HOST); +} + +static void lbtf_submit_command(struct lbtf_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + struct cmd_header *cmd; + uint16_t cmdsize; + uint16_t command; + int timeo = 5 * HZ; + int ret; + + lbtf_deb_enter(LBTF_DEB_HOST); + + cmd = cmdnode->cmdbuf; + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->cur_cmd = cmdnode; + cmdsize = le16_to_cpu(cmd->size); + command = le16_to_cpu(cmd->command); + + lbtf_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", + command, le16_to_cpu(cmd->seqnum), cmdsize); + lbtf_deb_hex(LBTF_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); + + ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); + spin_unlock_irqrestore(&priv->driver_lock, flags); + + if (ret) { + pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret); + /* Let the timer kick in and retry, and potentially reset + the whole thing if the condition persists */ + timeo = HZ; + } + + /* Setup the timer after transmit command */ + mod_timer(&priv->command_timer, jiffies + timeo); + + lbtf_deb_leave(LBTF_DEB_HOST); +} + +/** + * This function inserts command node to cmdfreeq + * after cleans it. Requires priv->driver_lock held. + */ +static void __lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + lbtf_deb_enter(LBTF_DEB_HOST); + + if (!cmdnode) + goto cl_ins_out; + + cmdnode->callback = NULL; + cmdnode->callback_arg = 0; + + memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); + + list_add_tail(&cmdnode->list, &priv->cmdfreeq); + +cl_ins_out: + lbtf_deb_leave(LBTF_DEB_HOST); +} + +static void lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, + struct cmd_ctrl_node *ptempcmd) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->driver_lock, flags); + __lbtf_cleanup_and_insert_cmd(priv, ptempcmd); + spin_unlock_irqrestore(&priv->driver_lock, flags); +} + +void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, + int result) +{ + cmd->result = result; + cmd->cmdwaitqwoken = 1; + wake_up_interruptible(&cmd->cmdwait_q); + + if (!cmd->callback) + __lbtf_cleanup_and_insert_cmd(priv, cmd); + priv->cur_cmd = NULL; +} + +int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv) +{ + struct cmd_ds_mac_multicast_addr cmd; + + lbtf_deb_enter(LBTF_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + + cmd.nr_of_adrs = cpu_to_le16((u16) priv->nr_of_multicastmacaddr); + + lbtf_deb_cmd("MULTICAST_ADR: setting %d addresses\n", cmd.nr_of_adrs); + + memcpy(cmd.maclist, priv->multicastlist, + priv->nr_of_multicastmacaddr * ETH_ALEN); + + lbtf_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &cmd.hdr, sizeof(cmd)); + + lbtf_deb_leave(LBTF_DEB_CMD); + return 0; +} + +void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode) +{ + struct cmd_ds_set_mode cmd; + lbtf_deb_enter(LBTF_DEB_WEXT); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.mode = cpu_to_le16(mode); + lbtf_deb_wext("Switching to mode: 0x%x\n", mode); + lbtf_cmd_async(priv, CMD_802_11_SET_MODE, &cmd.hdr, sizeof(cmd)); + + lbtf_deb_leave(LBTF_DEB_WEXT); +} + +void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid) +{ + struct cmd_ds_set_bssid cmd; + lbtf_deb_enter(LBTF_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.activate = activate ? 1 : 0; + if (activate) + memcpy(cmd.bssid, bssid, ETH_ALEN); + + lbtf_cmd_async(priv, CMD_802_11_SET_BSSID, &cmd.hdr, sizeof(cmd)); + lbtf_deb_leave(LBTF_DEB_CMD); +} + +int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr) +{ + struct cmd_ds_802_11_mac_address cmd; + lbtf_deb_enter(LBTF_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + + memcpy(cmd.macadd, mac_addr, ETH_ALEN); + + lbtf_cmd_async(priv, CMD_802_11_MAC_ADDRESS, &cmd.hdr, sizeof(cmd)); + lbtf_deb_leave(LBTF_DEB_CMD); + return 0; +} + +int lbtf_set_radio_control(struct lbtf_private *priv) +{ + int ret = 0; + struct cmd_ds_802_11_radio_control cmd; + + lbtf_deb_enter(LBTF_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + + switch (priv->preamble) { + case CMD_TYPE_SHORT_PREAMBLE: + cmd.control = cpu_to_le16(SET_SHORT_PREAMBLE); + break; + + case CMD_TYPE_LONG_PREAMBLE: + cmd.control = cpu_to_le16(SET_LONG_PREAMBLE); + break; + + case CMD_TYPE_AUTO_PREAMBLE: + default: + cmd.control = cpu_to_le16(SET_AUTO_PREAMBLE); + break; + } + + if (priv->radioon) + cmd.control |= cpu_to_le16(TURN_ON_RF); + else + cmd.control &= cpu_to_le16(~TURN_ON_RF); + + lbtf_deb_cmd("RADIO_SET: radio %d, preamble %d\n", priv->radioon, + priv->preamble); + + ret = lbtf_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); + + lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); + return ret; +} + +void lbtf_set_mac_control(struct lbtf_private *priv) +{ + struct cmd_ds_mac_control cmd; + lbtf_deb_enter(LBTF_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(priv->mac_control); + cmd.reserved = 0; + + lbtf_cmd_async(priv, CMD_MAC_CONTROL, + &cmd.hdr, sizeof(cmd)); + + lbtf_deb_leave(LBTF_DEB_CMD); +} + +/** + * lbtf_allocate_cmd_buffer - Allocates cmd buffer, links it to free cmd queue + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: 0 on success. + */ +int lbtf_allocate_cmd_buffer(struct lbtf_private *priv) +{ + int ret = 0; + u32 bufsize; + u32 i; + struct cmd_ctrl_node *cmdarray; + + lbtf_deb_enter(LBTF_DEB_HOST); + + /* Allocate and initialize the command array */ + bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; + cmdarray = kzalloc(bufsize, GFP_KERNEL); + if (!cmdarray) { + lbtf_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); + ret = -1; + goto done; + } + priv->cmd_array = cmdarray; + + /* Allocate and initialize each command buffer in the command array */ + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); + if (!cmdarray[i].cmdbuf) { + lbtf_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); + ret = -1; + goto done; + } + } + + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + init_waitqueue_head(&cmdarray[i].cmdwait_q); + lbtf_cleanup_and_insert_cmd(priv, &cmdarray[i]); + } + + ret = 0; + +done: + lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret); + return ret; +} + +/** + * lbtf_free_cmd_buffer - Frees the cmd buffer. + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: 0 + */ +int lbtf_free_cmd_buffer(struct lbtf_private *priv) +{ + struct cmd_ctrl_node *cmdarray; + unsigned int i; + + lbtf_deb_enter(LBTF_DEB_HOST); + + /* need to check if cmd array is allocated or not */ + if (priv->cmd_array == NULL) { + lbtf_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); + goto done; + } + + cmdarray = priv->cmd_array; + + /* Release shared memory buffers */ + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + kfree(cmdarray[i].cmdbuf); + cmdarray[i].cmdbuf = NULL; + } + + /* Release cmd_ctrl_node */ + kfree(priv->cmd_array); + priv->cmd_array = NULL; + +done: + lbtf_deb_leave(LBTF_DEB_HOST); + return 0; +} + +/** + * lbtf_get_cmd_ctrl_node - Gets free cmd node from free cmd queue. + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: pointer to a struct cmd_ctrl_node or NULL if none available. + */ +static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv) +{ + struct cmd_ctrl_node *tempnode; + unsigned long flags; + + lbtf_deb_enter(LBTF_DEB_HOST); + + if (!priv) + return NULL; + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!list_empty(&priv->cmdfreeq)) { + tempnode = list_first_entry(&priv->cmdfreeq, + struct cmd_ctrl_node, list); + list_del(&tempnode->list); + } else { + lbtf_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); + tempnode = NULL; + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + lbtf_deb_leave(LBTF_DEB_HOST); + return tempnode; +} + +/** + * lbtf_execute_next_command: execute next command in cmd pending queue. + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: 0 on success. + */ +int lbtf_execute_next_command(struct lbtf_private *priv) +{ + struct cmd_ctrl_node *cmdnode = NULL; + struct cmd_header *cmd; + unsigned long flags; + int ret = 0; + + /* Debug group is lbtf_deb_THREAD and not lbtf_deb_HOST, because the + * only caller to us is lbtf_thread() and we get even when a + * data packet is received */ + lbtf_deb_enter(LBTF_DEB_THREAD); + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->cur_cmd) { + pr_alert("EXEC_NEXT_CMD: already processing command!\n"); + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + + if (!list_empty(&priv->cmdpendingq)) { + cmdnode = list_first_entry(&priv->cmdpendingq, + struct cmd_ctrl_node, list); + } + + if (cmdnode) { + cmd = cmdnode->cmdbuf; + + list_del(&cmdnode->list); + lbtf_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", + le16_to_cpu(cmd->command)); + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbtf_submit_command(priv, cmdnode); + } else + spin_unlock_irqrestore(&priv->driver_lock, flags); + + ret = 0; +done: + lbtf_deb_leave(LBTF_DEB_THREAD); + return ret; +} + +static struct cmd_ctrl_node *__lbtf_cmd_async(struct lbtf_private *priv, + uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbtf_private *, unsigned long, + struct cmd_header *), + unsigned long callback_arg) +{ + struct cmd_ctrl_node *cmdnode; + + lbtf_deb_enter(LBTF_DEB_HOST); + + if (priv->surpriseremoved) { + lbtf_deb_host("PREP_CMD: card removed\n"); + cmdnode = ERR_PTR(-ENOENT); + goto done; + } + + cmdnode = lbtf_get_cmd_ctrl_node(priv); + if (cmdnode == NULL) { + lbtf_deb_host("PREP_CMD: cmdnode is NULL\n"); + + /* Wake up main thread to execute next command */ + queue_work(lbtf_wq, &priv->cmd_work); + cmdnode = ERR_PTR(-ENOBUFS); + goto done; + } + + cmdnode->callback = callback; + cmdnode->callback_arg = callback_arg; + + /* Copy the incoming command to the buffer */ + memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); + + /* Set sequence number, clean result, move to buffer */ + priv->seqnum++; + cmdnode->cmdbuf->command = cpu_to_le16(command); + cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); + cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum); + cmdnode->cmdbuf->result = 0; + + lbtf_deb_host("PREP_CMD: command 0x%04x\n", command); + + cmdnode->cmdwaitqwoken = 0; + lbtf_queue_cmd(priv, cmdnode); + queue_work(lbtf_wq, &priv->cmd_work); + + done: + lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %p", cmdnode); + return cmdnode; +} + +void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size) +{ + lbtf_deb_enter(LBTF_DEB_CMD); + __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, NULL, 0); + lbtf_deb_leave(LBTF_DEB_CMD); +} + +int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbtf_private *, + unsigned long, struct cmd_header *), + unsigned long callback_arg) +{ + struct cmd_ctrl_node *cmdnode; + unsigned long flags; + int ret = 0; + + lbtf_deb_enter(LBTF_DEB_HOST); + + cmdnode = __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, + callback, callback_arg); + if (IS_ERR(cmdnode)) { + ret = PTR_ERR(cmdnode); + goto done; + } + + might_sleep(); + ret = wait_event_interruptible(cmdnode->cmdwait_q, + cmdnode->cmdwaitqwoken); + if (ret) { + pr_info("PREP_CMD: command 0x%04x interrupted by signal: %d\n", + command, ret); + goto done; + } + + spin_lock_irqsave(&priv->driver_lock, flags); + ret = cmdnode->result; + if (ret) + pr_info("PREP_CMD: command 0x%04x failed: %d\n", + command, ret); + + __lbtf_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&priv->driver_lock, flags); + +done: + lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(__lbtf_cmd); + +/* Call holding driver_lock */ +void lbtf_cmd_response_rx(struct lbtf_private *priv) +{ + priv->cmd_response_rxed = 1; + queue_work(lbtf_wq, &priv->cmd_work); +} +EXPORT_SYMBOL_GPL(lbtf_cmd_response_rx); + +int lbtf_process_rx_command(struct lbtf_private *priv) +{ + uint16_t respcmd, curcmd; + struct cmd_header *resp; + int ret = 0; + unsigned long flags; + uint16_t result; + + lbtf_deb_enter(LBTF_DEB_CMD); + + mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!priv->cur_cmd) { + ret = -1; + spin_unlock_irqrestore(&priv->driver_lock, flags); + goto done; + } + + resp = (void *)priv->cmd_resp_buff; + curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); + respcmd = le16_to_cpu(resp->command); + result = le16_to_cpu(resp->result); + + if (net_ratelimit()) + pr_info("libertastf: cmd response 0x%04x, seq %d, size %d\n", + respcmd, le16_to_cpu(resp->seqnum), + le16_to_cpu(resp->size)); + + if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + if (respcmd != CMD_RET(curcmd)) { + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + + if (resp->result == cpu_to_le16(0x0004)) { + /* 0x0004 means -EAGAIN. Drop the response, let it time out + and be resubmitted */ + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + + /* Now we got response from FW, cancel the command timer */ + del_timer(&priv->command_timer); + priv->cmd_timed_out = 0; + if (priv->nr_retries) + priv->nr_retries = 0; + + /* If the command is not successful, cleanup and return failure */ + if ((result != 0 || !(respcmd & 0x8000))) { + /* + * Handling errors here + */ + switch (respcmd) { + case CMD_RET(CMD_GET_HW_SPEC): + case CMD_RET(CMD_802_11_RESET): + pr_info("libertastf: reset failed\n"); + break; + + } + lbtf_complete_command(priv, priv->cur_cmd, result); + spin_unlock_irqrestore(&priv->driver_lock, flags); + + ret = -1; + goto done; + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + if (priv->cur_cmd && priv->cur_cmd->callback) { + ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, + resp); + } + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->cur_cmd) { + /* Clean up and Put current command back to cmdfreeq */ + lbtf_complete_command(priv, priv->cur_cmd, result); + } + spin_unlock_irqrestore(&priv->driver_lock, flags); + +done: + mutex_unlock(&priv->lock); + lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); + return ret; +} diff --git a/drivers/net/wireless/marvell/libertas_tf/deb_defs.h b/drivers/net/wireless/marvell/libertas_tf/deb_defs.h new file mode 100644 index 000000000000..4bd3dc5adf7c --- /dev/null +++ b/drivers/net/wireless/marvell/libertas_tf/deb_defs.h @@ -0,0 +1,104 @@ +/** + * This header file contains global constant/enum definitions, + * global variable declaration. + */ +#ifndef _LBS_DEB_DEFS_H_ +#define _LBS_DEB_DEFS_H_ + +#ifndef DRV_NAME +#define DRV_NAME "libertas_tf" +#endif + +#include + +#ifdef CONFIG_LIBERTAS_THINFIRM_DEBUG +#define DEBUG +#define PROC_DEBUG +#endif + +#define LBTF_DEB_ENTER 0x00000001 +#define LBTF_DEB_LEAVE 0x00000002 +#define LBTF_DEB_MAIN 0x00000004 +#define LBTF_DEB_NET 0x00000008 +#define LBTF_DEB_MESH 0x00000010 +#define LBTF_DEB_WEXT 0x00000020 +#define LBTF_DEB_IOCTL 0x00000040 +#define LBTF_DEB_SCAN 0x00000080 +#define LBTF_DEB_ASSOC 0x00000100 +#define LBTF_DEB_JOIN 0x00000200 +#define LBTF_DEB_11D 0x00000400 +#define LBTF_DEB_DEBUGFS 0x00000800 +#define LBTF_DEB_ETHTOOL 0x00001000 +#define LBTF_DEB_HOST 0x00002000 +#define LBTF_DEB_CMD 0x00004000 +#define LBTF_DEB_RX 0x00008000 +#define LBTF_DEB_TX 0x00010000 +#define LBTF_DEB_USB 0x00020000 +#define LBTF_DEB_CS 0x00040000 +#define LBTF_DEB_FW 0x00080000 +#define LBTF_DEB_THREAD 0x00100000 +#define LBTF_DEB_HEX 0x00200000 +#define LBTF_DEB_SDIO 0x00400000 +#define LBTF_DEB_MACOPS 0x00800000 + +extern unsigned int lbtf_debug; + + +#ifdef DEBUG +#define LBTF_DEB_LL(grp, grpnam, fmt, args...) \ +do { if ((lbtf_debug & (grp)) == (grp)) \ + printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \ + in_interrupt() ? " (INT)" : "", ## args); } while (0) +#else +#define LBTF_DEB_LL(grp, grpnam, fmt, args...) do {} while (0) +#endif + +#define lbtf_deb_enter(grp) \ + LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s()\n", __func__); +#define lbtf_deb_enter_args(grp, fmt, args...) \ + LBTF_DEB_LL(grp | LBTF_DEB_ENTER, " enter", "%s(" fmt ")\n", __func__, ## args); +#define lbtf_deb_leave(grp) \ + LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s()\n", __func__); +#define lbtf_deb_leave_args(grp, fmt, args...) \ + LBTF_DEB_LL(grp | LBTF_DEB_LEAVE, " leave", "%s(), " fmt "\n", \ + __func__, ##args); +#define lbtf_deb_main(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MAIN, " main", fmt, ##args) +#define lbtf_deb_net(fmt, args...) LBTF_DEB_LL(LBTF_DEB_NET, " net", fmt, ##args) +#define lbtf_deb_mesh(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MESH, " mesh", fmt, ##args) +#define lbtf_deb_wext(fmt, args...) LBTF_DEB_LL(LBTF_DEB_WEXT, " wext", fmt, ##args) +#define lbtf_deb_ioctl(fmt, args...) LBTF_DEB_LL(LBTF_DEB_IOCTL, " ioctl", fmt, ##args) +#define lbtf_deb_scan(fmt, args...) LBTF_DEB_LL(LBTF_DEB_SCAN, " scan", fmt, ##args) +#define lbtf_deb_assoc(fmt, args...) LBTF_DEB_LL(LBTF_DEB_ASSOC, " assoc", fmt, ##args) +#define lbtf_deb_join(fmt, args...) LBTF_DEB_LL(LBTF_DEB_JOIN, " join", fmt, ##args) +#define lbtf_deb_11d(fmt, args...) LBTF_DEB_LL(LBTF_DEB_11D, " 11d", fmt, ##args) +#define lbtf_deb_debugfs(fmt, args...) LBTF_DEB_LL(LBTF_DEB_DEBUGFS, " debugfs", fmt, ##args) +#define lbtf_deb_ethtool(fmt, args...) LBTF_DEB_LL(LBTF_DEB_ETHTOOL, " ethtool", fmt, ##args) +#define lbtf_deb_host(fmt, args...) LBTF_DEB_LL(LBTF_DEB_HOST, " host", fmt, ##args) +#define lbtf_deb_cmd(fmt, args...) LBTF_DEB_LL(LBTF_DEB_CMD, " cmd", fmt, ##args) +#define lbtf_deb_rx(fmt, args...) LBTF_DEB_LL(LBTF_DEB_RX, " rx", fmt, ##args) +#define lbtf_deb_tx(fmt, args...) LBTF_DEB_LL(LBTF_DEB_TX, " tx", fmt, ##args) +#define lbtf_deb_fw(fmt, args...) LBTF_DEB_LL(LBTF_DEB_FW, " fw", fmt, ##args) +#define lbtf_deb_usb(fmt, args...) LBTF_DEB_LL(LBTF_DEB_USB, " usb", fmt, ##args) +#define lbtf_deb_usbd(dev, fmt, args...) LBTF_DEB_LL(LBTF_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args) +#define lbtf_deb_cs(fmt, args...) LBTF_DEB_LL(LBTF_DEB_CS, " cs", fmt, ##args) +#define lbtf_deb_thread(fmt, args...) LBTF_DEB_LL(LBTF_DEB_THREAD, " thread", fmt, ##args) +#define lbtf_deb_sdio(fmt, args...) LBTF_DEB_LL(LBTF_DEB_SDIO, " thread", fmt, ##args) +#define lbtf_deb_macops(fmt, args...) LBTF_DEB_LL(LBTF_DEB_MACOPS, " thread", fmt, ##args) + +#ifdef DEBUG +static inline void lbtf_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len) +{ + char newprompt[32]; + + if (len && + (lbtf_debug & LBTF_DEB_HEX) && + (lbtf_debug & grp)) { + snprintf(newprompt, sizeof(newprompt), DRV_NAME " %s: ", prompt); + print_hex_dump_bytes(prompt, DUMP_PREFIX_NONE, buf, len); + } +} +#else +#define lbtf_deb_hex(grp, prompt, buf, len) do {} while (0) +#endif + +#endif diff --git a/drivers/net/wireless/marvell/libertas_tf/if_usb.c b/drivers/net/wireless/marvell/libertas_tf/if_usb.c new file mode 100644 index 000000000000..799a2efe5793 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.c @@ -0,0 +1,928 @@ +/* + * Copyright (C) 2008, cozybit Inc. + * Copyright (C) 2003-2006, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#define DRV_NAME "lbtf_usb" + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "libertas_tf.h" +#include "if_usb.h" + +#include +#include +#include +#include +#include +#include + +#define INSANEDEBUG 0 +#define lbtf_deb_usb2(...) do { if (INSANEDEBUG) lbtf_deb_usbd(__VA_ARGS__); } while (0) + +#define MESSAGE_HEADER_LEN 4 + +static char *lbtf_fw_name = "lbtf_usb.bin"; +module_param_named(fw_name, lbtf_fw_name, charp, 0644); + +MODULE_FIRMWARE("lbtf_usb.bin"); + +static struct usb_device_id if_usb_table[] = { + /* Enter the device signature inside */ + { USB_DEVICE(0x1286, 0x2001) }, + { USB_DEVICE(0x05a3, 0x8388) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, if_usb_table); + +static void if_usb_receive(struct urb *urb); +static void if_usb_receive_fwload(struct urb *urb); +static int if_usb_prog_firmware(struct if_usb_card *cardp); +static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type, + uint8_t *payload, uint16_t nb); +static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, + uint16_t nb, u8 data); +static void if_usb_free(struct if_usb_card *cardp); +static int if_usb_submit_rx_urb(struct if_usb_card *cardp); +static int if_usb_reset_device(struct if_usb_card *cardp); + +/** + * if_usb_wrike_bulk_callback - call back to handle URB status + * + * @param urb pointer to urb structure + */ +static void if_usb_write_bulk_callback(struct urb *urb) +{ + if (urb->status != 0) { + /* print the failure status number for debug */ + pr_info("URB in failure status: %d\n", urb->status); + } else { + lbtf_deb_usb2(&urb->dev->dev, "URB status is successful\n"); + lbtf_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", + urb->actual_length); + } +} + +/** + * if_usb_free - free tx/rx urb, skb and rx buffer + * + * @param cardp pointer if_usb_card + */ +static void if_usb_free(struct if_usb_card *cardp) +{ + lbtf_deb_enter(LBTF_DEB_USB); + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + usb_kill_urb(cardp->cmd_urb); + + usb_free_urb(cardp->tx_urb); + cardp->tx_urb = NULL; + + usb_free_urb(cardp->rx_urb); + cardp->rx_urb = NULL; + + usb_free_urb(cardp->cmd_urb); + cardp->cmd_urb = NULL; + + kfree(cardp->ep_out_buf); + cardp->ep_out_buf = NULL; + + lbtf_deb_leave(LBTF_DEB_USB); +} + +static void if_usb_setup_firmware(struct lbtf_private *priv) +{ + struct if_usb_card *cardp = priv->card; + struct cmd_ds_set_boot2_ver b2_cmd; + + lbtf_deb_enter(LBTF_DEB_USB); + + if_usb_submit_rx_urb(cardp); + b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); + b2_cmd.action = 0; + b2_cmd.version = cardp->boot2_version; + + if (lbtf_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) + lbtf_deb_usb("Setting boot2 version failed\n"); + + lbtf_deb_leave(LBTF_DEB_USB); +} + +static void if_usb_fw_timeo(unsigned long priv) +{ + struct if_usb_card *cardp = (void *)priv; + + lbtf_deb_enter(LBTF_DEB_USB); + if (!cardp->fwdnldover) { + /* Download timed out */ + cardp->priv->surpriseremoved = 1; + pr_err("Download timed out\n"); + } else { + lbtf_deb_usb("Download complete, no event. Assuming success\n"); + } + wake_up(&cardp->fw_wq); + lbtf_deb_leave(LBTF_DEB_USB); +} + +/** + * if_usb_probe - sets the configuration values + * + * @ifnum interface number + * @id pointer to usb_device_id + * + * Returns: 0 on success, error code on failure + */ +static int if_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct lbtf_private *priv; + struct if_usb_card *cardp; + int i; + + lbtf_deb_enter(LBTF_DEB_USB); + udev = interface_to_usbdev(intf); + + cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); + if (!cardp) + goto error; + + setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp); + init_waitqueue_head(&cardp->fw_wq); + + cardp->udev = udev; + iface_desc = intf->cur_altsetting; + + lbtf_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" + " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", + le16_to_cpu(udev->descriptor.bcdUSB), + udev->descriptor.bDeviceClass, + udev->descriptor.bDeviceSubClass, + udev->descriptor.bDeviceProtocol); + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (usb_endpoint_is_bulk_in(endpoint)) { + cardp->ep_in_size = + le16_to_cpu(endpoint->wMaxPacketSize); + cardp->ep_in = usb_endpoint_num(endpoint); + + lbtf_deb_usbd(&udev->dev, "in_endpoint = %d\n", + cardp->ep_in); + lbtf_deb_usbd(&udev->dev, "Bulk in size is %d\n", + cardp->ep_in_size); + } else if (usb_endpoint_is_bulk_out(endpoint)) { + cardp->ep_out_size = + le16_to_cpu(endpoint->wMaxPacketSize); + cardp->ep_out = usb_endpoint_num(endpoint); + + lbtf_deb_usbd(&udev->dev, "out_endpoint = %d\n", + cardp->ep_out); + lbtf_deb_usbd(&udev->dev, "Bulk out size is %d\n", + cardp->ep_out_size); + } + } + if (!cardp->ep_out_size || !cardp->ep_in_size) { + lbtf_deb_usbd(&udev->dev, "Endpoints not found\n"); + /* Endpoints not found */ + goto dealloc; + } + + cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cardp->rx_urb) { + lbtf_deb_usbd(&udev->dev, "Rx URB allocation failed\n"); + goto dealloc; + } + + cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cardp->tx_urb) { + lbtf_deb_usbd(&udev->dev, "Tx URB allocation failed\n"); + goto dealloc; + } + + cardp->cmd_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cardp->cmd_urb) { + lbtf_deb_usbd(&udev->dev, "Cmd URB allocation failed\n"); + goto dealloc; + } + + cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, + GFP_KERNEL); + if (!cardp->ep_out_buf) { + lbtf_deb_usbd(&udev->dev, "Could not allocate buffer\n"); + goto dealloc; + } + + priv = lbtf_add_card(cardp, &udev->dev); + if (!priv) + goto dealloc; + + cardp->priv = priv; + + priv->hw_host_to_card = if_usb_host_to_card; + priv->hw_prog_firmware = if_usb_prog_firmware; + priv->hw_reset_device = if_usb_reset_device; + cardp->boot2_version = udev->descriptor.bcdDevice; + + usb_get_dev(udev); + usb_set_intfdata(intf, cardp); + + return 0; + +dealloc: + if_usb_free(cardp); +error: +lbtf_deb_leave(LBTF_DEB_MAIN); + return -ENOMEM; +} + +/** + * if_usb_disconnect - free resource and cleanup + * + * @intf USB interface structure + */ +static void if_usb_disconnect(struct usb_interface *intf) +{ + struct if_usb_card *cardp = usb_get_intfdata(intf); + struct lbtf_private *priv = cardp->priv; + + lbtf_deb_enter(LBTF_DEB_MAIN); + + if_usb_reset_device(cardp); + + if (priv) + lbtf_remove_card(priv); + + /* Unlink and free urb */ + if_usb_free(cardp); + + usb_set_intfdata(intf, NULL); + usb_put_dev(interface_to_usbdev(intf)); + + lbtf_deb_leave(LBTF_DEB_MAIN); +} + +/** + * if_usb_send_fw_pkt - This function downloads the FW + * + * @priv pointer to struct lbtf_private + * + * Returns: 0 + */ +static int if_usb_send_fw_pkt(struct if_usb_card *cardp) +{ + struct fwdata *fwdata = cardp->ep_out_buf; + u8 *firmware = (u8 *) cardp->fw->data; + + lbtf_deb_enter(LBTF_DEB_FW); + + /* If we got a CRC failure on the last block, back + up and retry it */ + if (!cardp->CRC_OK) { + cardp->totalbytes = cardp->fwlastblksent; + cardp->fwseqnum--; + } + + lbtf_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n", + cardp->totalbytes); + + /* struct fwdata (which we sent to the card) has an + extra __le32 field in between the header and the data, + which is not in the struct fwheader in the actual + firmware binary. Insert the seqnum in the middle... */ + memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], + sizeof(struct fwheader)); + + cardp->fwlastblksent = cardp->totalbytes; + cardp->totalbytes += sizeof(struct fwheader); + + memcpy(fwdata->data, &firmware[cardp->totalbytes], + le32_to_cpu(fwdata->hdr.datalength)); + + lbtf_deb_usb2(&cardp->udev->dev, "Data length = %d\n", + le32_to_cpu(fwdata->hdr.datalength)); + + fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); + cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); + + usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + + le32_to_cpu(fwdata->hdr.datalength), 0); + + if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) { + lbtf_deb_usb2(&cardp->udev->dev, "There are data to follow\n"); + lbtf_deb_usb2(&cardp->udev->dev, + "seqnum = %d totalbytes = %d\n", + cardp->fwseqnum, cardp->totalbytes); + } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) { + lbtf_deb_usb2(&cardp->udev->dev, + "Host has finished FW downloading\n"); + lbtf_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n"); + + /* Host has finished FW downloading + * Donwloading FW JUMP BLOCK + */ + cardp->fwfinalblk = 1; + } + + lbtf_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n", + cardp->totalbytes); + + lbtf_deb_leave(LBTF_DEB_FW); + return 0; +} + +static int if_usb_reset_device(struct if_usb_card *cardp) +{ + struct cmd_ds_802_11_reset *cmd = cardp->ep_out_buf + 4; + int ret; + + lbtf_deb_enter(LBTF_DEB_USB); + + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); + + cmd->hdr.command = cpu_to_le16(CMD_802_11_RESET); + cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset)); + cmd->hdr.result = cpu_to_le16(0); + cmd->hdr.seqnum = cpu_to_le16(0x5a5a); + cmd->action = cpu_to_le16(CMD_ACT_HALT); + usb_tx_block(cardp, cardp->ep_out_buf, + 4 + sizeof(struct cmd_ds_802_11_reset), 0); + + msleep(100); + ret = usb_reset_device(cardp->udev); + msleep(100); + + lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret); + + return ret; +} + +/** + * usb_tx_block - transfer data to the device + * + * @priv pointer to struct lbtf_private + * @payload pointer to payload data + * @nb data length + * @data non-zero for data, zero for commands + * + * Returns: 0 on success, nonzero otherwise. + */ +static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, + uint16_t nb, u8 data) +{ + int ret = -1; + struct urb *urb; + + lbtf_deb_enter(LBTF_DEB_USB); + /* check if device is removed */ + if (cardp->priv->surpriseremoved) { + lbtf_deb_usbd(&cardp->udev->dev, "Device removed\n"); + goto tx_ret; + } + + if (data) + urb = cardp->tx_urb; + else + urb = cardp->cmd_urb; + + usb_fill_bulk_urb(urb, cardp->udev, + usb_sndbulkpipe(cardp->udev, + cardp->ep_out), + payload, nb, if_usb_write_bulk_callback, cardp); + + urb->transfer_flags |= URB_ZERO_PACKET; + + if (usb_submit_urb(urb, GFP_ATOMIC)) { + lbtf_deb_usbd(&cardp->udev->dev, + "usb_submit_urb failed: %d\n", ret); + goto tx_ret; + } + + lbtf_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n"); + + ret = 0; + +tx_ret: + lbtf_deb_leave(LBTF_DEB_USB); + return ret; +} + +static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, + void (*callbackfn)(struct urb *urb)) +{ + struct sk_buff *skb; + int ret = -1; + + lbtf_deb_enter(LBTF_DEB_USB); + + skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); + if (!skb) { + pr_err("No free skb\n"); + lbtf_deb_leave(LBTF_DEB_USB); + return -1; + } + + cardp->rx_skb = skb; + + /* Fill the receive configuration URB and initialise the Rx call back */ + usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, + usb_rcvbulkpipe(cardp->udev, cardp->ep_in), + skb_tail_pointer(skb), + MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, cardp); + + cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; + + lbtf_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", + cardp->rx_urb); + ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC); + if (ret) { + lbtf_deb_usbd(&cardp->udev->dev, + "Submit Rx URB failed: %d\n", ret); + kfree_skb(skb); + cardp->rx_skb = NULL; + lbtf_deb_leave(LBTF_DEB_USB); + return -1; + } else { + lbtf_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n"); + lbtf_deb_leave(LBTF_DEB_USB); + return 0; + } +} + +static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) +{ + return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); +} + +static int if_usb_submit_rx_urb(struct if_usb_card *cardp) +{ + return __if_usb_submit_rx_urb(cardp, &if_usb_receive); +} + +static void if_usb_receive_fwload(struct urb *urb) +{ + struct if_usb_card *cardp = urb->context; + struct sk_buff *skb = cardp->rx_skb; + struct fwsyncheader *syncfwheader; + struct bootcmdresp bcmdresp; + + lbtf_deb_enter(LBTF_DEB_USB); + if (urb->status) { + lbtf_deb_usbd(&cardp->udev->dev, + "URB status is failed during fw load\n"); + kfree_skb(skb); + lbtf_deb_leave(LBTF_DEB_USB); + return; + } + + if (cardp->fwdnldover) { + __le32 *tmp = (__le32 *)(skb->data); + + if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && + tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) { + /* Firmware ready event received */ + pr_info("Firmware ready event received\n"); + wake_up(&cardp->fw_wq); + } else { + lbtf_deb_usb("Waiting for confirmation; got %x %x\n", + le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1])); + if_usb_submit_rx_urb_fwload(cardp); + } + kfree_skb(skb); + lbtf_deb_leave(LBTF_DEB_USB); + return; + } + if (cardp->bootcmdresp <= 0) { + memcpy(&bcmdresp, skb->data, sizeof(bcmdresp)); + + if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(cardp); + cardp->bootcmdresp = 1; + /* Received valid boot command response */ + lbtf_deb_usbd(&cardp->udev->dev, + "Received valid boot command response\n"); + lbtf_deb_leave(LBTF_DEB_USB); + return; + } + if (bcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { + if (bcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || + bcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || + bcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) { + if (!cardp->bootcmdresp) + pr_info("Firmware already seems alive; resetting\n"); + cardp->bootcmdresp = -1; + } else { + pr_info("boot cmd response wrong magic number (0x%x)\n", + le32_to_cpu(bcmdresp.magic)); + } + } else if (bcmdresp.cmd != BOOT_CMD_FW_BY_USB) { + pr_info("boot cmd response cmd_tag error (%d)\n", + bcmdresp.cmd); + } else if (bcmdresp.result != BOOT_CMD_RESP_OK) { + pr_info("boot cmd response result error (%d)\n", + bcmdresp.result); + } else { + cardp->bootcmdresp = 1; + lbtf_deb_usbd(&cardp->udev->dev, + "Received valid boot command response\n"); + } + + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(cardp); + lbtf_deb_leave(LBTF_DEB_USB); + return; + } + + syncfwheader = kmemdup(skb->data, sizeof(struct fwsyncheader), + GFP_ATOMIC); + if (!syncfwheader) { + lbtf_deb_usbd(&cardp->udev->dev, + "Failure to allocate syncfwheader\n"); + kfree_skb(skb); + lbtf_deb_leave(LBTF_DEB_USB); + return; + } + + if (!syncfwheader->cmd) { + lbtf_deb_usb2(&cardp->udev->dev, + "FW received Blk with correct CRC\n"); + lbtf_deb_usb2(&cardp->udev->dev, + "FW received Blk seqnum = %d\n", + le32_to_cpu(syncfwheader->seqnum)); + cardp->CRC_OK = 1; + } else { + lbtf_deb_usbd(&cardp->udev->dev, + "FW received Blk with CRC error\n"); + cardp->CRC_OK = 0; + } + + kfree_skb(skb); + + /* reschedule timer for 200ms hence */ + mod_timer(&cardp->fw_timeout, jiffies + (HZ/5)); + + if (cardp->fwfinalblk) { + cardp->fwdnldover = 1; + goto exit; + } + + if_usb_send_fw_pkt(cardp); + + exit: + if_usb_submit_rx_urb_fwload(cardp); + + kfree(syncfwheader); + + lbtf_deb_leave(LBTF_DEB_USB); +} + +#define MRVDRV_MIN_PKT_LEN 30 + +static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, + struct if_usb_card *cardp, + struct lbtf_private *priv) +{ + if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN + || recvlength < MRVDRV_MIN_PKT_LEN) { + lbtf_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n"); + kfree_skb(skb); + return; + } + + skb_put(skb, recvlength); + skb_pull(skb, MESSAGE_HEADER_LEN); + lbtf_rx(priv, skb); +} + +static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, + struct sk_buff *skb, + struct if_usb_card *cardp, + struct lbtf_private *priv) +{ + if (recvlength > LBS_CMD_BUFFER_SIZE) { + lbtf_deb_usbd(&cardp->udev->dev, + "The receive buffer is too large\n"); + kfree_skb(skb); + return; + } + + BUG_ON(!in_interrupt()); + + spin_lock(&priv->driver_lock); + memcpy(priv->cmd_resp_buff, recvbuff + MESSAGE_HEADER_LEN, + recvlength - MESSAGE_HEADER_LEN); + kfree_skb(skb); + lbtf_cmd_response_rx(priv); + spin_unlock(&priv->driver_lock); +} + +/** + * if_usb_receive - read data received from the device. + * + * @urb pointer to struct urb + */ +static void if_usb_receive(struct urb *urb) +{ + struct if_usb_card *cardp = urb->context; + struct sk_buff *skb = cardp->rx_skb; + struct lbtf_private *priv = cardp->priv; + int recvlength = urb->actual_length; + uint8_t *recvbuff = NULL; + uint32_t recvtype = 0; + __le32 *pkt = (__le32 *) skb->data; + + lbtf_deb_enter(LBTF_DEB_USB); + + if (recvlength) { + if (urb->status) { + lbtf_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n", + urb->status); + kfree_skb(skb); + goto setup_for_next; + } + + recvbuff = skb->data; + recvtype = le32_to_cpu(pkt[0]); + lbtf_deb_usbd(&cardp->udev->dev, + "Recv length = 0x%x, Recv type = 0x%X\n", + recvlength, recvtype); + } else if (urb->status) { + kfree_skb(skb); + lbtf_deb_leave(LBTF_DEB_USB); + return; + } + + switch (recvtype) { + case CMD_TYPE_DATA: + process_cmdtypedata(recvlength, skb, cardp, priv); + break; + + case CMD_TYPE_REQUEST: + process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); + break; + + case CMD_TYPE_INDICATION: + { + /* Event cause handling */ + u32 event_cause = le32_to_cpu(pkt[1]); + lbtf_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", + event_cause); + + /* Icky undocumented magic special case */ + if (event_cause & 0xffff0000) { + u16 tmp; + u8 retrycnt; + u8 failure; + + tmp = event_cause >> 16; + retrycnt = tmp & 0x00ff; + failure = (tmp & 0xff00) >> 8; + lbtf_send_tx_feedback(priv, retrycnt, failure); + } else if (event_cause == LBTF_EVENT_BCN_SENT) + lbtf_bcn_sent(priv); + else + lbtf_deb_usbd(&cardp->udev->dev, + "Unsupported notification %d received\n", + event_cause); + kfree_skb(skb); + break; + } + default: + lbtf_deb_usbd(&cardp->udev->dev, + "libertastf: unknown command type 0x%X\n", recvtype); + kfree_skb(skb); + break; + } + +setup_for_next: + if_usb_submit_rx_urb(cardp); + lbtf_deb_leave(LBTF_DEB_USB); +} + +/** + * if_usb_host_to_card - Download data to the device + * + * @priv pointer to struct lbtf_private structure + * @type type of data + * @buf pointer to data buffer + * @len number of bytes + * + * Returns: 0 on success, nonzero otherwise + */ +static int if_usb_host_to_card(struct lbtf_private *priv, uint8_t type, + uint8_t *payload, uint16_t nb) +{ + struct if_usb_card *cardp = priv->card; + u8 data = 0; + + lbtf_deb_usbd(&cardp->udev->dev, "*** type = %u\n", type); + lbtf_deb_usbd(&cardp->udev->dev, "size after = %d\n", nb); + + if (type == MVMS_CMD) { + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); + } else { + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); + data = 1; + } + + memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); + + return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN, + data); +} + +/** + * if_usb_issue_boot_command - Issue boot command to Boot2. + * + * @ivalue 1 boots from FW by USB-Download, 2 boots from FW in EEPROM. + * + * Returns: 0 + */ +static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) +{ + struct bootcmd *bootcmd = cardp->ep_out_buf; + + /* Prepare command */ + bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); + bootcmd->cmd = ivalue; + memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); + + /* Issue command */ + usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd), 0); + + return 0; +} + + +/** + * check_fwfile_format - Check the validity of Boot2/FW image. + * + * @data pointer to image + * @totlen image length + * + * Returns: 0 if the image is valid, nonzero otherwise. + */ +static int check_fwfile_format(const u8 *data, u32 totlen) +{ + u32 bincmd, exit; + u32 blksize, offset, len; + int ret; + + ret = 1; + exit = len = 0; + + do { + struct fwheader *fwh = (void *) data; + + bincmd = le32_to_cpu(fwh->dnldcmd); + blksize = le32_to_cpu(fwh->datalength); + switch (bincmd) { + case FW_HAS_DATA_TO_RECV: + offset = sizeof(struct fwheader) + blksize; + data += offset; + len += offset; + if (len >= totlen) + exit = 1; + break; + case FW_HAS_LAST_BLOCK: + exit = 1; + ret = 0; + break; + default: + exit = 1; + break; + } + } while (!exit); + + if (ret) + pr_err("firmware file format check FAIL\n"); + else + lbtf_deb_fw("firmware file format check PASS\n"); + + return ret; +} + + +static int if_usb_prog_firmware(struct if_usb_card *cardp) +{ + int i = 0; + static int reset_count = 10; + int ret = 0; + + lbtf_deb_enter(LBTF_DEB_USB); + + kernel_param_lock(THIS_MODULE); + ret = request_firmware(&cardp->fw, lbtf_fw_name, &cardp->udev->dev); + if (ret < 0) { + pr_err("request_firmware() failed with %#x\n", ret); + pr_err("firmware %s not found\n", lbtf_fw_name); + kernel_param_unlock(THIS_MODULE); + goto done; + } + kernel_param_unlock(THIS_MODULE); + + if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) + goto release_fw; + +restart: + if (if_usb_submit_rx_urb_fwload(cardp) < 0) { + lbtf_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); + ret = -1; + goto release_fw; + } + + cardp->bootcmdresp = 0; + do { + int j = 0; + i++; + /* Issue Boot command = 1, Boot from Download-FW */ + if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); + /* wait for command response */ + do { + j++; + msleep_interruptible(100); + } while (cardp->bootcmdresp == 0 && j < 10); + } while (cardp->bootcmdresp == 0 && i < 5); + + if (cardp->bootcmdresp <= 0) { + if (--reset_count >= 0) { + if_usb_reset_device(cardp); + goto restart; + } + return -1; + } + + i = 0; + + cardp->totalbytes = 0; + cardp->fwlastblksent = 0; + cardp->CRC_OK = 1; + cardp->fwdnldover = 0; + cardp->fwseqnum = -1; + cardp->totalbytes = 0; + cardp->fwfinalblk = 0; + + /* Send the first firmware packet... */ + if_usb_send_fw_pkt(cardp); + + /* ... and wait for the process to complete */ + wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved || + cardp->fwdnldover); + + del_timer_sync(&cardp->fw_timeout); + usb_kill_urb(cardp->rx_urb); + + if (!cardp->fwdnldover) { + pr_info("failed to load fw, resetting device!\n"); + if (--reset_count >= 0) { + if_usb_reset_device(cardp); + goto restart; + } + + pr_info("FW download failure, time = %d ms\n", i * 100); + ret = -1; + goto release_fw; + } + + cardp->priv->fw_ready = 1; + + release_fw: + release_firmware(cardp->fw); + cardp->fw = NULL; + + if_usb_setup_firmware(cardp->priv); + + done: + lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret); + return ret; +} + + +#define if_usb_suspend NULL +#define if_usb_resume NULL + +static struct usb_driver if_usb_driver = { + .name = DRV_NAME, + .probe = if_usb_probe, + .disconnect = if_usb_disconnect, + .id_table = if_usb_table, + .suspend = if_usb_suspend, + .resume = if_usb_resume, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(if_usb_driver); + +MODULE_DESCRIPTION("8388 USB WLAN Thinfirm Driver"); +MODULE_AUTHOR("Cozybit Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/marvell/libertas_tf/if_usb.h b/drivers/net/wireless/marvell/libertas_tf/if_usb.h new file mode 100644 index 000000000000..6fa5b3f59efe --- /dev/null +++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2008, cozybit Inc. + * Copyright (C) 2003-2006, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#include +#include + +struct lbtf_private; + +/** + * This file contains definition for USB interface. + */ +#define CMD_TYPE_REQUEST 0xF00DFACE +#define CMD_TYPE_DATA 0xBEADC0DE +#define CMD_TYPE_INDICATION 0xBEEFFACE + +#define BOOT_CMD_FW_BY_USB 0x01 +#define BOOT_CMD_FW_IN_EEPROM 0x02 +#define BOOT_CMD_UPDATE_BOOT2 0x03 +#define BOOT_CMD_UPDATE_FW 0x04 +#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */ + +struct bootcmd { + __le32 magic; + uint8_t cmd; + uint8_t pad[11]; +}; + +#define BOOT_CMD_RESP_OK 0x0001 +#define BOOT_CMD_RESP_FAIL 0x0000 + +struct bootcmdresp { + __le32 magic; + uint8_t cmd; + uint8_t result; + uint8_t pad[2]; +}; + +/** USB card description structure*/ +struct if_usb_card { + struct usb_device *udev; + struct urb *rx_urb, *tx_urb, *cmd_urb; + struct lbtf_private *priv; + + struct sk_buff *rx_skb; + + uint8_t ep_in; + uint8_t ep_out; + + int8_t bootcmdresp; + + int ep_in_size; + + void *ep_out_buf; + int ep_out_size; + + const struct firmware *fw; + struct timer_list fw_timeout; + wait_queue_head_t fw_wq; + uint32_t fwseqnum; + uint32_t totalbytes; + uint32_t fwlastblksent; + uint8_t CRC_OK; + uint8_t fwdnldover; + uint8_t fwfinalblk; + + __le16 boot2_version; +}; + +/** fwheader */ +struct fwheader { + __le32 dnldcmd; + __le32 baseaddr; + __le32 datalength; + __le32 CRC; +}; + +#define FW_MAX_DATA_BLK_SIZE 600 +/** FWData */ +struct fwdata { + struct fwheader hdr; + __le32 seqnum; + uint8_t data[0]; +}; + +/** fwsyncheader */ +struct fwsyncheader { + __le32 cmd; + __le32 seqnum; +}; + +#define FW_HAS_DATA_TO_RECV 0x00000001 +#define FW_HAS_LAST_BLOCK 0x00000004 diff --git a/drivers/net/wireless/marvell/libertas_tf/libertas_tf.h b/drivers/net/wireless/marvell/libertas_tf/libertas_tf.h new file mode 100644 index 000000000000..ad77b92d0b41 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas_tf/libertas_tf.h @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2008, cozybit Inc. + * Copyright (C) 2007, Red Hat, Inc. + * Copyright (C) 2003-2006, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#include +#include +#include +#include + +#include "deb_defs.h" + +#ifndef DRV_NAME +#define DRV_NAME "libertas_tf" +#endif + +#define MRVL_DEFAULT_RETRIES 9 +#define MRVL_PER_PACKET_RATE 0x10 +#define MRVL_MAX_BCN_SIZE 440 +#define CMD_OPTION_WAITFORRSP 0x0002 + +/* Return command are almost always the same as the host command, but with + * bit 15 set high. There are a few exceptions, though... + */ +#define CMD_RET(cmd) (0x8000 | cmd) + +/* Command codes */ +#define CMD_GET_HW_SPEC 0x0003 +#define CMD_802_11_RESET 0x0005 +#define CMD_MAC_MULTICAST_ADR 0x0010 +#define CMD_802_11_RADIO_CONTROL 0x001c +#define CMD_802_11_RF_CHANNEL 0x001d +#define CMD_802_11_RF_TX_POWER 0x001e +#define CMD_MAC_CONTROL 0x0028 +#define CMD_802_11_MAC_ADDRESS 0x004d +#define CMD_SET_BOOT2_VER 0x00a5 +#define CMD_802_11_BEACON_CTRL 0x00b0 +#define CMD_802_11_BEACON_SET 0x00cb +#define CMD_802_11_SET_MODE 0x00cc +#define CMD_802_11_SET_BSSID 0x00cd + +#define CMD_ACT_GET 0x0000 +#define CMD_ACT_SET 0x0001 + +/* Define action or option for CMD_802_11_RESET */ +#define CMD_ACT_HALT 0x0003 + +/* Define action or option for CMD_MAC_CONTROL */ +#define CMD_ACT_MAC_RX_ON 0x0001 +#define CMD_ACT_MAC_TX_ON 0x0002 +#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 +#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 +#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 +#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 + +/* Define action or option for CMD_802_11_RADIO_CONTROL */ +#define CMD_TYPE_AUTO_PREAMBLE 0x0001 +#define CMD_TYPE_SHORT_PREAMBLE 0x0002 +#define CMD_TYPE_LONG_PREAMBLE 0x0003 + +#define TURN_ON_RF 0x01 +#define RADIO_ON 0x01 +#define RADIO_OFF 0x00 + +#define SET_AUTO_PREAMBLE 0x05 +#define SET_SHORT_PREAMBLE 0x03 +#define SET_LONG_PREAMBLE 0x01 + +/* Define action or option for CMD_802_11_RF_CHANNEL */ +#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 +#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 + +/* Codes for CMD_802_11_SET_MODE */ +enum lbtf_mode { + LBTF_PASSIVE_MODE, + LBTF_STA_MODE, + LBTF_AP_MODE, +}; + +/** Card Event definition */ +#define MACREG_INT_CODE_FIRMWARE_READY 48 +/** Buffer Constants */ + +/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical +* addresses of TxPD buffers. Station has only 8 TxPD available, Whereas +* driver has more local TxPDs. Each TxPD on the host memory is associated +* with a Tx control node. The driver maintains 8 RxPD descriptors for +* station firmware to store Rx packet information. +* +* Current version of MAC has a 32x6 multicast address buffer. +* +* 802.11b can have up to 14 channels, the driver keeps the +* BSSID(MAC address) of each APs or Ad hoc stations it has sensed. +*/ + +#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 +#define LBS_NUM_CMD_BUFFERS 10 +#define LBS_CMD_BUFFER_SIZE (2 * 1024) +#define MRVDRV_MAX_CHANNEL_SIZE 14 +#define MRVDRV_SNAP_HEADER_LEN 8 + +#define LBS_UPLD_SIZE 2312 +#define DEV_NAME_LEN 32 + +/** Misc constants */ +/* This section defines 802.11 specific contants */ + +#define MRVDRV_MAX_REGION_CODE 6 +/** + * the table to keep region code + */ +#define LBTF_REGDOMAIN_US 0x10 +#define LBTF_REGDOMAIN_CA 0x20 +#define LBTF_REGDOMAIN_EU 0x30 +#define LBTF_REGDOMAIN_SP 0x31 +#define LBTF_REGDOMAIN_FR 0x32 +#define LBTF_REGDOMAIN_JP 0x40 + +#define SBI_EVENT_CAUSE_SHIFT 3 + +/** RxPD status */ + +#define MRVDRV_RXPD_STATUS_OK 0x0001 + + +/* This is for firmware specific length */ +#define EXTRA_LEN 36 + +#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) + +#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct rxpd) \ + + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) + +#define CMD_F_HOSTCMD (1 << 0) +#define FW_CAPINFO_WPA (1 << 0) + +#define RF_ANTENNA_1 0x1 +#define RF_ANTENNA_2 0x2 +#define RF_ANTENNA_AUTO 0xFFFF + +#define LBTF_EVENT_BCN_SENT 55 + +/** Global Variable Declaration */ +/** mv_ms_type */ +enum mv_ms_type { + MVMS_DAT = 0, + MVMS_CMD = 1, + MVMS_TXDONE = 2, + MVMS_EVENT +}; + +extern struct workqueue_struct *lbtf_wq; + +struct lbtf_private; + +struct lbtf_offset_value { + u32 offset; + u32 value; +}; + +struct channel_range { + u8 regdomain; + u8 start; + u8 end; /* exclusive (channel must be less than end) */ +}; + +struct if_usb_card; + +/** Private structure for the MV device */ +struct lbtf_private { + void *card; + struct ieee80211_hw *hw; + + /* Command response buffer */ + u8 cmd_resp_buff[LBS_UPLD_SIZE]; + /* Download sent: + bit0 1/0=data_sent/data_tx_done, + bit1 1/0=cmd_sent/cmd_tx_done, + all other bits reserved 0 */ + struct ieee80211_vif *vif; + + struct work_struct cmd_work; + struct work_struct tx_work; + /** Hardware access */ + int (*hw_host_to_card) (struct lbtf_private *priv, u8 type, u8 *payload, u16 nb); + int (*hw_prog_firmware) (struct if_usb_card *cardp); + int (*hw_reset_device) (struct if_usb_card *cardp); + + + /** Wlan adapter data structure*/ + /** STATUS variables */ + u32 fwrelease; + u32 fwcapinfo; + /* protected with big lock */ + + struct mutex lock; + + /** command-related variables */ + u16 seqnum; + /* protected by big lock */ + + struct cmd_ctrl_node *cmd_array; + /** Current command */ + struct cmd_ctrl_node *cur_cmd; + /** command Queues */ + /** Free command buffers */ + struct list_head cmdfreeq; + /** Pending command buffers */ + struct list_head cmdpendingq; + + /** spin locks */ + spinlock_t driver_lock; + + /** Timers */ + struct timer_list command_timer; + int nr_retries; + int cmd_timed_out; + + u8 cmd_response_rxed; + + /** capability Info used in Association, start, join */ + u16 capability; + + /** MAC address information */ + u8 current_addr[ETH_ALEN]; + u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; + u32 nr_of_multicastmacaddr; + int cur_freq; + + struct sk_buff *skb_to_tx; + struct sk_buff *tx_skb; + + /** NIC Operation characteristics */ + u16 mac_control; + u16 regioncode; + struct channel_range range; + + u8 radioon; + u32 preamble; + + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_supported_band band; + struct lbtf_offset_value offsetvalue; + + u8 fw_ready; + u8 surpriseremoved; + struct sk_buff_head bc_ps_buf; + + /* Most recently reported noise in dBm */ + s8 noise; +}; + +/* 802.11-related definitions */ + +/* TxPD descriptor */ +struct txpd { + /* Current Tx packet status */ + __le32 tx_status; + /* Tx control */ + __le32 tx_control; + __le32 tx_packet_location; + /* Tx packet length */ + __le16 tx_packet_length; + /* First 2 byte of destination MAC address */ + u8 tx_dest_addr_high[2]; + /* Last 4 byte of destination MAC address */ + u8 tx_dest_addr_low[4]; + /* Pkt Priority */ + u8 priority; + /* Pkt Trasnit Power control */ + u8 powermgmt; + /* Time the packet has been queued in the driver (units = 2ms) */ + u8 pktdelay_2ms; + /* reserved */ + u8 reserved1; +}; + +/* RxPD Descriptor */ +struct rxpd { + /* Current Rx packet status */ + __le16 status; + + /* SNR */ + u8 snr; + + /* Tx control */ + u8 rx_control; + + /* Pkt length */ + __le16 pkt_len; + + /* Noise Floor */ + u8 nf; + + /* Rx Packet Rate */ + u8 rx_rate; + + /* Pkt addr */ + __le32 pkt_ptr; + + /* Next Rx RxPD addr */ + __le32 next_rxpd_ptr; + + /* Pkt Priority */ + u8 priority; + u8 reserved[3]; +}; + +struct cmd_header { + __le16 command; + __le16 size; + __le16 seqnum; + __le16 result; +} __packed; + +struct cmd_ctrl_node { + struct list_head list; + int result; + /* command response */ + int (*callback)(struct lbtf_private *, + unsigned long, struct cmd_header *); + unsigned long callback_arg; + /* command data */ + struct cmd_header *cmdbuf; + /* wait queue */ + u16 cmdwaitqwoken; + wait_queue_head_t cmdwait_q; +}; + +/* + * Define data structure for CMD_GET_HW_SPEC + * This structure defines the response for the GET_HW_SPEC command + */ +struct cmd_ds_get_hw_spec { + struct cmd_header hdr; + + /* HW Interface version number */ + __le16 hwifversion; + /* HW version number */ + __le16 version; + /* Max number of TxPD FW can handle */ + __le16 nr_txpd; + /* Max no of Multicast address */ + __le16 nr_mcast_adr; + /* MAC address */ + u8 permanentaddr[6]; + + /* region Code */ + __le16 regioncode; + + /* Number of antenna used */ + __le16 nr_antenna; + + /* FW release number, example 0x01030304 = 2.3.4p1 */ + __le32 fwrelease; + + /* Base Address of TxPD queue */ + __le32 wcb_base; + /* Read Pointer of RxPd queue */ + __le32 rxpd_rdptr; + + /* Write Pointer of RxPd queue */ + __le32 rxpd_wrptr; + + /*FW/HW capability */ + __le32 fwcapinfo; +} __packed; + +struct cmd_ds_mac_control { + struct cmd_header hdr; + __le16 action; + u16 reserved; +}; + +struct cmd_ds_802_11_mac_address { + struct cmd_header hdr; + + __le16 action; + uint8_t macadd[ETH_ALEN]; +}; + +struct cmd_ds_mac_multicast_addr { + struct cmd_header hdr; + + __le16 action; + __le16 nr_of_adrs; + u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; +}; + +struct cmd_ds_set_mode { + struct cmd_header hdr; + + __le16 mode; +}; + +struct cmd_ds_set_bssid { + struct cmd_header hdr; + + u8 bssid[6]; + u8 activate; +}; + +struct cmd_ds_802_11_radio_control { + struct cmd_header hdr; + + __le16 action; + __le16 control; +}; + + +struct cmd_ds_802_11_rf_channel { + struct cmd_header hdr; + + __le16 action; + __le16 channel; + __le16 rftype; /* unused */ + __le16 reserved; /* unused */ + u8 channellist[32]; /* unused */ +}; + +struct cmd_ds_set_boot2_ver { + struct cmd_header hdr; + + __le16 action; + __le16 version; +}; + +struct cmd_ds_802_11_reset { + struct cmd_header hdr; + + __le16 action; +}; + +struct cmd_ds_802_11_beacon_control { + struct cmd_header hdr; + + __le16 action; + __le16 beacon_enable; + __le16 beacon_period; +}; + +struct cmd_ds_802_11_beacon_set { + struct cmd_header hdr; + + __le16 len; + u8 beacon[MRVL_MAX_BCN_SIZE]; +}; + +struct lbtf_private; +struct cmd_ctrl_node; + +/** Function Prototype Declaration */ +void lbtf_set_mac_control(struct lbtf_private *priv); + +int lbtf_free_cmd_buffer(struct lbtf_private *priv); + +int lbtf_allocate_cmd_buffer(struct lbtf_private *priv); +int lbtf_execute_next_command(struct lbtf_private *priv); +int lbtf_set_radio_control(struct lbtf_private *priv); +int lbtf_update_hw_spec(struct lbtf_private *priv); +int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv); +void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode); +void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid); +int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr); + +int lbtf_set_channel(struct lbtf_private *priv, u8 channel); + +int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon); +int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, + int beacon_int); + + +int lbtf_process_rx_command(struct lbtf_private *priv); +void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, + int result); +void lbtf_cmd_response_rx(struct lbtf_private *priv); + +/* main.c */ +struct chan_freq_power *lbtf_get_region_cfp_table(u8 region, + int *cfp_no); +struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev); +int lbtf_remove_card(struct lbtf_private *priv); +int lbtf_start_card(struct lbtf_private *priv); +int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb); +void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail); +void lbtf_bcn_sent(struct lbtf_private *priv); + +/* support functions for cmd.c */ +/* lbtf_cmd() infers the size of the buffer to copy data back into, from + the size of the target of the pointer. Since the command to be sent + may often be smaller, that size is set in cmd->size by the caller.*/ +#define lbtf_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \ + uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \ + (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \ + __lbtf_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \ +}) + +#define lbtf_cmd_with_response(priv, cmdnr, cmd) \ + lbtf_cmd(priv, cmdnr, cmd, lbtf_cmd_copyback, (unsigned long) (cmd)) + +void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size); + +int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbtf_private *, unsigned long, + struct cmd_header *), + unsigned long callback_arg); + +int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, + struct cmd_header *resp); diff --git a/drivers/net/wireless/marvell/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c new file mode 100644 index 000000000000..a47f0acc099a --- /dev/null +++ b/drivers/net/wireless/marvell/libertas_tf/main.c @@ -0,0 +1,766 @@ +/* + * Copyright (C) 2008, cozybit Inc. + * Copyright (C) 2003-2006, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +#include +#include +#include "libertas_tf.h" + +#define DRIVER_RELEASE_VERSION "004.p0" +/* thinfirm version: 5.132.X.pX */ +#define LBTF_FW_VER_MIN 0x05840300 +#define LBTF_FW_VER_MAX 0x0584ffff +#define QOS_CONTROL_LEN 2 + +/* Module parameters */ +unsigned int lbtf_debug; +EXPORT_SYMBOL_GPL(lbtf_debug); +module_param_named(libertas_tf_debug, lbtf_debug, int, 0644); + +static const char lbtf_driver_version[] = "THINFIRM-USB8388-" DRIVER_RELEASE_VERSION +#ifdef DEBUG + "-dbg" +#endif + ""; + +struct workqueue_struct *lbtf_wq; + +static const struct ieee80211_channel lbtf_channels[] = { + { .center_freq = 2412, .hw_value = 1 }, + { .center_freq = 2417, .hw_value = 2 }, + { .center_freq = 2422, .hw_value = 3 }, + { .center_freq = 2427, .hw_value = 4 }, + { .center_freq = 2432, .hw_value = 5 }, + { .center_freq = 2437, .hw_value = 6 }, + { .center_freq = 2442, .hw_value = 7 }, + { .center_freq = 2447, .hw_value = 8 }, + { .center_freq = 2452, .hw_value = 9 }, + { .center_freq = 2457, .hw_value = 10 }, + { .center_freq = 2462, .hw_value = 11 }, + { .center_freq = 2467, .hw_value = 12 }, + { .center_freq = 2472, .hw_value = 13 }, + { .center_freq = 2484, .hw_value = 14 }, +}; + +/* This table contains the hardware specific values for the modulation rates. */ +static const struct ieee80211_rate lbtf_rates[] = { + { .bitrate = 10, + .hw_value = 0, }, + { .bitrate = 20, + .hw_value = 1, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, + .hw_value = 2, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, + .hw_value = 3, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60, + .hw_value = 5, + .flags = 0 }, + { .bitrate = 90, + .hw_value = 6, + .flags = 0 }, + { .bitrate = 120, + .hw_value = 7, + .flags = 0 }, + { .bitrate = 180, + .hw_value = 8, + .flags = 0 }, + { .bitrate = 240, + .hw_value = 9, + .flags = 0 }, + { .bitrate = 360, + .hw_value = 10, + .flags = 0 }, + { .bitrate = 480, + .hw_value = 11, + .flags = 0 }, + { .bitrate = 540, + .hw_value = 12, + .flags = 0 }, +}; + +static void lbtf_cmd_work(struct work_struct *work) +{ + struct lbtf_private *priv = container_of(work, struct lbtf_private, + cmd_work); + + lbtf_deb_enter(LBTF_DEB_CMD); + + spin_lock_irq(&priv->driver_lock); + /* command response? */ + if (priv->cmd_response_rxed) { + priv->cmd_response_rxed = 0; + spin_unlock_irq(&priv->driver_lock); + lbtf_process_rx_command(priv); + spin_lock_irq(&priv->driver_lock); + } + + if (priv->cmd_timed_out && priv->cur_cmd) { + struct cmd_ctrl_node *cmdnode = priv->cur_cmd; + + if (++priv->nr_retries > 10) { + lbtf_complete_command(priv, cmdnode, + -ETIMEDOUT); + priv->nr_retries = 0; + } else { + priv->cur_cmd = NULL; + + /* Stick it back at the _top_ of the pending + * queue for immediate resubmission */ + list_add(&cmdnode->list, &priv->cmdpendingq); + } + } + priv->cmd_timed_out = 0; + spin_unlock_irq(&priv->driver_lock); + + if (!priv->fw_ready) { + lbtf_deb_leave_args(LBTF_DEB_CMD, "fw not ready"); + return; + } + + /* Execute the next command */ + if (!priv->cur_cmd) + lbtf_execute_next_command(priv); + + lbtf_deb_leave(LBTF_DEB_CMD); +} + +/** + * lbtf_setup_firmware: initialize firmware. + * + * @priv A pointer to struct lbtf_private structure + * + * Returns: 0 on success. + */ +static int lbtf_setup_firmware(struct lbtf_private *priv) +{ + int ret = -1; + + lbtf_deb_enter(LBTF_DEB_FW); + /* + * Read priv address from HW + */ + eth_broadcast_addr(priv->current_addr); + ret = lbtf_update_hw_spec(priv); + if (ret) { + ret = -1; + goto done; + } + + lbtf_set_mac_control(priv); + lbtf_set_radio_control(priv); + + ret = 0; +done: + lbtf_deb_leave_args(LBTF_DEB_FW, "ret: %d", ret); + return ret; +} + +/** + * This function handles the timeout of command sending. + * It will re-send the same command again. + */ +static void command_timer_fn(unsigned long data) +{ + struct lbtf_private *priv = (struct lbtf_private *)data; + unsigned long flags; + lbtf_deb_enter(LBTF_DEB_CMD); + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!priv->cur_cmd) { + printk(KERN_DEBUG "libertastf: command timer expired; " + "no pending command\n"); + goto out; + } + + printk(KERN_DEBUG "libertas: command %x timed out\n", + le16_to_cpu(priv->cur_cmd->cmdbuf->command)); + + priv->cmd_timed_out = 1; + queue_work(lbtf_wq, &priv->cmd_work); +out: + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbtf_deb_leave(LBTF_DEB_CMD); +} + +static int lbtf_init_adapter(struct lbtf_private *priv) +{ + lbtf_deb_enter(LBTF_DEB_MAIN); + eth_broadcast_addr(priv->current_addr); + mutex_init(&priv->lock); + + priv->vif = NULL; + setup_timer(&priv->command_timer, command_timer_fn, + (unsigned long)priv); + + INIT_LIST_HEAD(&priv->cmdfreeq); + INIT_LIST_HEAD(&priv->cmdpendingq); + + spin_lock_init(&priv->driver_lock); + + /* Allocate the command buffers */ + if (lbtf_allocate_cmd_buffer(priv)) + return -1; + + lbtf_deb_leave(LBTF_DEB_MAIN); + return 0; +} + +static void lbtf_free_adapter(struct lbtf_private *priv) +{ + lbtf_deb_enter(LBTF_DEB_MAIN); + lbtf_free_cmd_buffer(priv); + del_timer(&priv->command_timer); + lbtf_deb_leave(LBTF_DEB_MAIN); +} + +static void lbtf_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct lbtf_private *priv = hw->priv; + + priv->skb_to_tx = skb; + queue_work(lbtf_wq, &priv->tx_work); + /* + * queue will be restarted when we receive transmission feedback if + * there are no buffered multicast frames to send + */ + ieee80211_stop_queues(priv->hw); +} + +static void lbtf_tx_work(struct work_struct *work) +{ + struct lbtf_private *priv = container_of(work, struct lbtf_private, + tx_work); + unsigned int len; + struct ieee80211_tx_info *info; + struct txpd *txpd; + struct sk_buff *skb = NULL; + int err; + + lbtf_deb_enter(LBTF_DEB_MACOPS | LBTF_DEB_TX); + + if ((priv->vif->type == NL80211_IFTYPE_AP) && + (!skb_queue_empty(&priv->bc_ps_buf))) + skb = skb_dequeue(&priv->bc_ps_buf); + else if (priv->skb_to_tx) { + skb = priv->skb_to_tx; + priv->skb_to_tx = NULL; + } else { + lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); + return; + } + + len = skb->len; + info = IEEE80211_SKB_CB(skb); + txpd = (struct txpd *) skb_push(skb, sizeof(struct txpd)); + + if (priv->surpriseremoved) { + dev_kfree_skb_any(skb); + lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); + return; + } + + memset(txpd, 0, sizeof(struct txpd)); + /* Activate per-packet rate selection */ + txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE | + ieee80211_get_tx_rate(priv->hw, info)->hw_value); + + /* copy destination address from 802.11 header */ + memcpy(txpd->tx_dest_addr_high, skb->data + sizeof(struct txpd) + 4, + ETH_ALEN); + txpd->tx_packet_length = cpu_to_le16(len); + txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); + lbtf_deb_hex(LBTF_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); + BUG_ON(priv->tx_skb); + spin_lock_irq(&priv->driver_lock); + priv->tx_skb = skb; + err = priv->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len); + spin_unlock_irq(&priv->driver_lock); + if (err) { + dev_kfree_skb_any(skb); + priv->tx_skb = NULL; + pr_err("TX error: %d", err); + } + lbtf_deb_leave(LBTF_DEB_MACOPS | LBTF_DEB_TX); +} + +static int lbtf_op_start(struct ieee80211_hw *hw) +{ + struct lbtf_private *priv = hw->priv; + void *card = priv->card; + int ret = -1; + + lbtf_deb_enter(LBTF_DEB_MACOPS); + + if (!priv->fw_ready) + /* Upload firmware */ + if (priv->hw_prog_firmware(card)) + goto err_prog_firmware; + + /* poke the firmware */ + priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE; + priv->radioon = RADIO_ON; + priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; + ret = lbtf_setup_firmware(priv); + if (ret) + goto err_prog_firmware; + + if ((priv->fwrelease < LBTF_FW_VER_MIN) || + (priv->fwrelease > LBTF_FW_VER_MAX)) { + ret = -1; + goto err_prog_firmware; + } + + printk(KERN_INFO "libertastf: Marvell WLAN 802.11 thinfirm adapter\n"); + lbtf_deb_leave(LBTF_DEB_MACOPS); + return 0; + +err_prog_firmware: + priv->hw_reset_device(card); + lbtf_deb_leave_args(LBTF_DEB_MACOPS, "error programming fw; ret=%d", ret); + return ret; +} + +static void lbtf_op_stop(struct ieee80211_hw *hw) +{ + struct lbtf_private *priv = hw->priv; + unsigned long flags; + struct sk_buff *skb; + + struct cmd_ctrl_node *cmdnode; + + lbtf_deb_enter(LBTF_DEB_MACOPS); + + /* Flush pending command nodes */ + spin_lock_irqsave(&priv->driver_lock, flags); + list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { + cmdnode->result = -ENOENT; + cmdnode->cmdwaitqwoken = 1; + wake_up_interruptible(&cmdnode->cmdwait_q); + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + cancel_work_sync(&priv->cmd_work); + cancel_work_sync(&priv->tx_work); + while ((skb = skb_dequeue(&priv->bc_ps_buf))) + dev_kfree_skb_any(skb); + priv->radioon = RADIO_OFF; + lbtf_set_radio_control(priv); + + lbtf_deb_leave(LBTF_DEB_MACOPS); +} + +static int lbtf_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct lbtf_private *priv = hw->priv; + lbtf_deb_enter(LBTF_DEB_MACOPS); + if (priv->vif != NULL) + return -EOPNOTSUPP; + + priv->vif = vif; + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + lbtf_set_mode(priv, LBTF_AP_MODE); + break; + case NL80211_IFTYPE_STATION: + lbtf_set_mode(priv, LBTF_STA_MODE); + break; + default: + priv->vif = NULL; + return -EOPNOTSUPP; + } + lbtf_set_mac_address(priv, (u8 *) vif->addr); + lbtf_deb_leave(LBTF_DEB_MACOPS); + return 0; +} + +static void lbtf_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct lbtf_private *priv = hw->priv; + lbtf_deb_enter(LBTF_DEB_MACOPS); + + if (priv->vif->type == NL80211_IFTYPE_AP || + priv->vif->type == NL80211_IFTYPE_MESH_POINT) + lbtf_beacon_ctrl(priv, 0, 0); + lbtf_set_mode(priv, LBTF_PASSIVE_MODE); + lbtf_set_bssid(priv, 0, NULL); + priv->vif = NULL; + lbtf_deb_leave(LBTF_DEB_MACOPS); +} + +static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed) +{ + struct lbtf_private *priv = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + lbtf_deb_enter(LBTF_DEB_MACOPS); + + if (conf->chandef.chan->center_freq != priv->cur_freq) { + priv->cur_freq = conf->chandef.chan->center_freq; + lbtf_set_channel(priv, conf->chandef.chan->hw_value); + } + lbtf_deb_leave(LBTF_DEB_MACOPS); + return 0; +} + +static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct lbtf_private *priv = hw->priv; + int i; + struct netdev_hw_addr *ha; + int mc_count = netdev_hw_addr_list_count(mc_list); + + if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) + return mc_count; + + priv->nr_of_multicastmacaddr = mc_count; + i = 0; + netdev_hw_addr_list_for_each(ha, mc_list) + memcpy(&priv->multicastlist[i++], ha->addr, ETH_ALEN); + + return mc_count; +} + +#define SUPPORTED_FIF_FLAGS FIF_ALLMULTI +static void lbtf_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *new_flags, + u64 multicast) +{ + struct lbtf_private *priv = hw->priv; + int old_mac_control = priv->mac_control; + + lbtf_deb_enter(LBTF_DEB_MACOPS); + + changed_flags &= SUPPORTED_FIF_FLAGS; + *new_flags &= SUPPORTED_FIF_FLAGS; + + if (!changed_flags) { + lbtf_deb_leave(LBTF_DEB_MACOPS); + return; + } + + priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; + if (*new_flags & (FIF_ALLMULTI) || + multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) { + priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; + priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; + } else if (multicast) { + priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; + priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE; + lbtf_cmd_set_mac_multicast_addr(priv); + } else { + priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE | + CMD_ACT_MAC_ALL_MULTICAST_ENABLE); + if (priv->nr_of_multicastmacaddr) { + priv->nr_of_multicastmacaddr = 0; + lbtf_cmd_set_mac_multicast_addr(priv); + } + } + + + if (priv->mac_control != old_mac_control) + lbtf_set_mac_control(priv); + + lbtf_deb_leave(LBTF_DEB_MACOPS); +} + +static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct lbtf_private *priv = hw->priv; + struct sk_buff *beacon; + lbtf_deb_enter(LBTF_DEB_MACOPS); + + if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) { + switch (priv->vif->type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + beacon = ieee80211_beacon_get(hw, vif); + if (beacon) { + lbtf_beacon_set(priv, beacon); + kfree_skb(beacon); + lbtf_beacon_ctrl(priv, 1, + bss_conf->beacon_int); + } + break; + default: + break; + } + } + + if (changes & BSS_CHANGED_BSSID) { + bool activate = !is_zero_ether_addr(bss_conf->bssid); + lbtf_set_bssid(priv, activate, bss_conf->bssid); + } + + if (changes & BSS_CHANGED_ERP_PREAMBLE) { + if (bss_conf->use_short_preamble) + priv->preamble = CMD_TYPE_SHORT_PREAMBLE; + else + priv->preamble = CMD_TYPE_LONG_PREAMBLE; + lbtf_set_radio_control(priv); + } + + lbtf_deb_leave(LBTF_DEB_MACOPS); +} + +static int lbtf_op_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct lbtf_private *priv = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + + if (idx != 0) + return -ENOENT; + + survey->channel = conf->chandef.chan; + survey->filled = SURVEY_INFO_NOISE_DBM; + survey->noise = priv->noise; + + return 0; +} + +static const struct ieee80211_ops lbtf_ops = { + .tx = lbtf_op_tx, + .start = lbtf_op_start, + .stop = lbtf_op_stop, + .add_interface = lbtf_op_add_interface, + .remove_interface = lbtf_op_remove_interface, + .config = lbtf_op_config, + .prepare_multicast = lbtf_op_prepare_multicast, + .configure_filter = lbtf_op_configure_filter, + .bss_info_changed = lbtf_op_bss_info_changed, + .get_survey = lbtf_op_get_survey, +}; + +int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb) +{ + struct ieee80211_rx_status stats; + struct rxpd *prxpd; + int need_padding; + unsigned int flags; + struct ieee80211_hdr *hdr; + + lbtf_deb_enter(LBTF_DEB_RX); + + prxpd = (struct rxpd *) skb->data; + + memset(&stats, 0, sizeof(stats)); + if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) + stats.flag |= RX_FLAG_FAILED_FCS_CRC; + stats.freq = priv->cur_freq; + stats.band = IEEE80211_BAND_2GHZ; + stats.signal = prxpd->snr; + priv->noise = prxpd->nf; + /* Marvell rate index has a hole at value 4 */ + if (prxpd->rx_rate > 4) + --prxpd->rx_rate; + stats.rate_idx = prxpd->rx_rate; + skb_pull(skb, sizeof(struct rxpd)); + + hdr = (struct ieee80211_hdr *)skb->data; + flags = le32_to_cpu(*(__le32 *)(skb->data + 4)); + + need_padding = ieee80211_is_data_qos(hdr->frame_control); + need_padding ^= ieee80211_has_a4(hdr->frame_control); + need_padding ^= ieee80211_is_data_qos(hdr->frame_control) && + (*ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_A_MSDU_PRESENT); + + if (need_padding) { + memmove(skb->data + 2, skb->data, skb->len); + skb_reserve(skb, 2); + } + + memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats)); + + lbtf_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n", + skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); + lbtf_deb_hex(LBTF_DEB_RX, "RX Data", skb->data, + min_t(unsigned int, skb->len, 100)); + + ieee80211_rx_irqsafe(priv->hw, skb); + + lbtf_deb_leave(LBTF_DEB_RX); + return 0; +} +EXPORT_SYMBOL_GPL(lbtf_rx); + +/** + * lbtf_add_card: Add and initialize the card, no fw upload yet. + * + * @card A pointer to card + * + * Returns: pointer to struct lbtf_priv. + */ +struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev) +{ + struct ieee80211_hw *hw; + struct lbtf_private *priv = NULL; + + lbtf_deb_enter(LBTF_DEB_MAIN); + + hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops); + if (!hw) + goto done; + + priv = hw->priv; + if (lbtf_init_adapter(priv)) + goto err_init_adapter; + + priv->hw = hw; + priv->card = card; + priv->tx_skb = NULL; + + hw->queues = 1; + ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); + hw->extra_tx_headroom = sizeof(struct txpd); + memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels)); + memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates)); + priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates); + priv->band.bitrates = priv->rates; + priv->band.n_channels = ARRAY_SIZE(lbtf_channels); + priv->band.channels = priv->channels; + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + skb_queue_head_init(&priv->bc_ps_buf); + + SET_IEEE80211_DEV(hw, dmdev); + + INIT_WORK(&priv->cmd_work, lbtf_cmd_work); + INIT_WORK(&priv->tx_work, lbtf_tx_work); + if (ieee80211_register_hw(hw)) + goto err_init_adapter; + + goto done; + +err_init_adapter: + lbtf_free_adapter(priv); + ieee80211_free_hw(hw); + priv = NULL; + +done: + lbtf_deb_leave_args(LBTF_DEB_MAIN, "priv %p", priv); + return priv; +} +EXPORT_SYMBOL_GPL(lbtf_add_card); + + +int lbtf_remove_card(struct lbtf_private *priv) +{ + struct ieee80211_hw *hw = priv->hw; + + lbtf_deb_enter(LBTF_DEB_MAIN); + + priv->surpriseremoved = 1; + del_timer(&priv->command_timer); + lbtf_free_adapter(priv); + priv->hw = NULL; + ieee80211_unregister_hw(hw); + ieee80211_free_hw(hw); + + lbtf_deb_leave(LBTF_DEB_MAIN); + return 0; +} +EXPORT_SYMBOL_GPL(lbtf_remove_card); + +void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); + + ieee80211_tx_info_clear_status(info); + /* + * Commented out, otherwise we never go beyond 1Mbit/s using mac80211 + * default pid rc algorithm. + * + * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt; + */ + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail) + info->flags |= IEEE80211_TX_STAT_ACK; + skb_pull(priv->tx_skb, sizeof(struct txpd)); + ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); + priv->tx_skb = NULL; + if (!priv->skb_to_tx && skb_queue_empty(&priv->bc_ps_buf)) + ieee80211_wake_queues(priv->hw); + else + queue_work(lbtf_wq, &priv->tx_work); +} +EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback); + +void lbtf_bcn_sent(struct lbtf_private *priv) +{ + struct sk_buff *skb = NULL; + + if (priv->vif->type != NL80211_IFTYPE_AP) + return; + + if (skb_queue_empty(&priv->bc_ps_buf)) { + bool tx_buff_bc = false; + + while ((skb = ieee80211_get_buffered_bc(priv->hw, priv->vif))) { + skb_queue_tail(&priv->bc_ps_buf, skb); + tx_buff_bc = true; + } + if (tx_buff_bc) { + ieee80211_stop_queues(priv->hw); + queue_work(lbtf_wq, &priv->tx_work); + } + } + + skb = ieee80211_beacon_get(priv->hw, priv->vif); + + if (skb) { + lbtf_beacon_set(priv, skb); + kfree_skb(skb); + } +} +EXPORT_SYMBOL_GPL(lbtf_bcn_sent); + +static int __init lbtf_init_module(void) +{ + lbtf_deb_enter(LBTF_DEB_MAIN); + lbtf_wq = create_workqueue("libertastf"); + if (lbtf_wq == NULL) { + printk(KERN_ERR "libertastf: couldn't create workqueue\n"); + return -ENOMEM; + } + lbtf_deb_leave(LBTF_DEB_MAIN); + return 0; +} + +static void __exit lbtf_exit_module(void) +{ + lbtf_deb_enter(LBTF_DEB_MAIN); + destroy_workqueue(lbtf_wq); + lbtf_deb_leave(LBTF_DEB_MAIN); +} + +module_init(lbtf_init_module); +module_exit(lbtf_exit_module); + +MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library"); +MODULE_AUTHOR("Cozybit Inc."); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 277b024e5e3d4af4c219c0b9bd541ca4398e0b69 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 21:14:51 +0200 Subject: mwifiex: move under marvell vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 1 - drivers/net/wireless/Makefile | 2 - drivers/net/wireless/marvell/Kconfig | 1 + drivers/net/wireless/marvell/Makefile | 1 + drivers/net/wireless/marvell/mwifiex/11ac.c | 382 ++ drivers/net/wireless/marvell/mwifiex/11ac.h | 45 + drivers/net/wireless/marvell/mwifiex/11h.c | 319 ++ drivers/net/wireless/marvell/mwifiex/11n.c | 914 +++++ drivers/net/wireless/marvell/mwifiex/11n.h | 191 + drivers/net/wireless/marvell/mwifiex/11n_aggr.c | 318 ++ drivers/net/wireless/marvell/mwifiex/11n_aggr.h | 33 + .../net/wireless/marvell/mwifiex/11n_rxreorder.c | 910 +++++ .../net/wireless/marvell/mwifiex/11n_rxreorder.h | 85 + drivers/net/wireless/marvell/mwifiex/Kconfig | 44 + drivers/net/wireless/marvell/mwifiex/Makefile | 57 + drivers/net/wireless/marvell/mwifiex/README | 240 ++ drivers/net/wireless/marvell/mwifiex/cfg80211.c | 3887 ++++++++++++++++++++ drivers/net/wireless/marvell/mwifiex/cfg80211.h | 29 + drivers/net/wireless/marvell/mwifiex/cfp.c | 537 +++ drivers/net/wireless/marvell/mwifiex/cmdevt.c | 1659 +++++++++ drivers/net/wireless/marvell/mwifiex/debugfs.c | 1005 +++++ drivers/net/wireless/marvell/mwifiex/decl.h | 273 ++ drivers/net/wireless/marvell/mwifiex/ethtool.c | 70 + drivers/net/wireless/marvell/mwifiex/fw.h | 2177 +++++++++++ drivers/net/wireless/marvell/mwifiex/ie.c | 488 +++ drivers/net/wireless/marvell/mwifiex/init.c | 782 ++++ drivers/net/wireless/marvell/mwifiex/ioctl.h | 470 +++ drivers/net/wireless/marvell/mwifiex/join.c | 1525 ++++++++ drivers/net/wireless/marvell/mwifiex/main.c | 1552 ++++++++ drivers/net/wireless/marvell/mwifiex/main.h | 1579 ++++++++ drivers/net/wireless/marvell/mwifiex/pcie.c | 2742 ++++++++++++++ drivers/net/wireless/marvell/mwifiex/pcie.h | 382 ++ drivers/net/wireless/marvell/mwifiex/scan.c | 2639 +++++++++++++ drivers/net/wireless/marvell/mwifiex/sdio.c | 2684 ++++++++++++++ drivers/net/wireless/marvell/mwifiex/sdio.h | 672 ++++ drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 2282 ++++++++++++ drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c | 1249 +++++++ drivers/net/wireless/marvell/mwifiex/sta_event.c | 864 +++++ drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 1421 +++++++ drivers/net/wireless/marvell/mwifiex/sta_rx.c | 267 ++ drivers/net/wireless/marvell/mwifiex/sta_tx.c | 244 ++ drivers/net/wireless/marvell/mwifiex/tdls.c | 1500 ++++++++ drivers/net/wireless/marvell/mwifiex/txrx.c | 386 ++ drivers/net/wireless/marvell/mwifiex/uap_cmd.c | 885 +++++ drivers/net/wireless/marvell/mwifiex/uap_event.c | 333 ++ drivers/net/wireless/marvell/mwifiex/uap_txrx.c | 437 +++ drivers/net/wireless/marvell/mwifiex/usb.c | 1267 +++++++ drivers/net/wireless/marvell/mwifiex/usb.h | 110 + drivers/net/wireless/marvell/mwifiex/util.c | 751 ++++ drivers/net/wireless/marvell/mwifiex/util.h | 96 + drivers/net/wireless/marvell/mwifiex/wmm.c | 1531 ++++++++ drivers/net/wireless/marvell/mwifiex/wmm.h | 140 + drivers/net/wireless/mwifiex/11ac.c | 382 -- drivers/net/wireless/mwifiex/11ac.h | 45 - drivers/net/wireless/mwifiex/11h.c | 319 -- drivers/net/wireless/mwifiex/11n.c | 914 ----- drivers/net/wireless/mwifiex/11n.h | 191 - drivers/net/wireless/mwifiex/11n_aggr.c | 318 -- drivers/net/wireless/mwifiex/11n_aggr.h | 33 - drivers/net/wireless/mwifiex/11n_rxreorder.c | 910 ----- drivers/net/wireless/mwifiex/11n_rxreorder.h | 85 - drivers/net/wireless/mwifiex/Kconfig | 44 - drivers/net/wireless/mwifiex/Makefile | 57 - drivers/net/wireless/mwifiex/README | 240 -- drivers/net/wireless/mwifiex/cfg80211.c | 3887 -------------------- drivers/net/wireless/mwifiex/cfg80211.h | 29 - drivers/net/wireless/mwifiex/cfp.c | 537 --- drivers/net/wireless/mwifiex/cmdevt.c | 1659 --------- drivers/net/wireless/mwifiex/debugfs.c | 1005 ----- drivers/net/wireless/mwifiex/decl.h | 273 -- drivers/net/wireless/mwifiex/ethtool.c | 70 - drivers/net/wireless/mwifiex/fw.h | 2177 ----------- drivers/net/wireless/mwifiex/ie.c | 488 --- drivers/net/wireless/mwifiex/init.c | 782 ---- drivers/net/wireless/mwifiex/ioctl.h | 470 --- drivers/net/wireless/mwifiex/join.c | 1525 -------- drivers/net/wireless/mwifiex/main.c | 1552 -------- drivers/net/wireless/mwifiex/main.h | 1579 -------- drivers/net/wireless/mwifiex/pcie.c | 2742 -------------- drivers/net/wireless/mwifiex/pcie.h | 382 -- drivers/net/wireless/mwifiex/scan.c | 2639 ------------- drivers/net/wireless/mwifiex/sdio.c | 2684 -------------- drivers/net/wireless/mwifiex/sdio.h | 672 ---- drivers/net/wireless/mwifiex/sta_cmd.c | 2282 ------------ drivers/net/wireless/mwifiex/sta_cmdresp.c | 1249 ------- drivers/net/wireless/mwifiex/sta_event.c | 864 ----- drivers/net/wireless/mwifiex/sta_ioctl.c | 1421 ------- drivers/net/wireless/mwifiex/sta_rx.c | 267 -- drivers/net/wireless/mwifiex/sta_tx.c | 244 -- drivers/net/wireless/mwifiex/tdls.c | 1500 -------- drivers/net/wireless/mwifiex/txrx.c | 386 -- drivers/net/wireless/mwifiex/uap_cmd.c | 885 ----- drivers/net/wireless/mwifiex/uap_event.c | 333 -- drivers/net/wireless/mwifiex/uap_txrx.c | 437 --- drivers/net/wireless/mwifiex/usb.c | 1267 ------- drivers/net/wireless/mwifiex/usb.h | 110 - drivers/net/wireless/mwifiex/util.c | 751 ---- drivers/net/wireless/mwifiex/util.h | 96 - drivers/net/wireless/mwifiex/wmm.c | 1531 -------- drivers/net/wireless/mwifiex/wmm.h | 140 - 101 files changed, 42456 insertions(+), 42457 deletions(-) create mode 100644 drivers/net/wireless/marvell/mwifiex/11ac.c create mode 100644 drivers/net/wireless/marvell/mwifiex/11ac.h create mode 100644 drivers/net/wireless/marvell/mwifiex/11h.c create mode 100644 drivers/net/wireless/marvell/mwifiex/11n.c create mode 100644 drivers/net/wireless/marvell/mwifiex/11n.h create mode 100644 drivers/net/wireless/marvell/mwifiex/11n_aggr.c create mode 100644 drivers/net/wireless/marvell/mwifiex/11n_aggr.h create mode 100644 drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c create mode 100644 drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h create mode 100644 drivers/net/wireless/marvell/mwifiex/Kconfig create mode 100644 drivers/net/wireless/marvell/mwifiex/Makefile create mode 100644 drivers/net/wireless/marvell/mwifiex/README create mode 100644 drivers/net/wireless/marvell/mwifiex/cfg80211.c create mode 100644 drivers/net/wireless/marvell/mwifiex/cfg80211.h create mode 100644 drivers/net/wireless/marvell/mwifiex/cfp.c create mode 100644 drivers/net/wireless/marvell/mwifiex/cmdevt.c create mode 100644 drivers/net/wireless/marvell/mwifiex/debugfs.c create mode 100644 drivers/net/wireless/marvell/mwifiex/decl.h create mode 100644 drivers/net/wireless/marvell/mwifiex/ethtool.c create mode 100644 drivers/net/wireless/marvell/mwifiex/fw.h create mode 100644 drivers/net/wireless/marvell/mwifiex/ie.c create mode 100644 drivers/net/wireless/marvell/mwifiex/init.c create mode 100644 drivers/net/wireless/marvell/mwifiex/ioctl.h create mode 100644 drivers/net/wireless/marvell/mwifiex/join.c create mode 100644 drivers/net/wireless/marvell/mwifiex/main.c create mode 100644 drivers/net/wireless/marvell/mwifiex/main.h create mode 100644 drivers/net/wireless/marvell/mwifiex/pcie.c create mode 100644 drivers/net/wireless/marvell/mwifiex/pcie.h create mode 100644 drivers/net/wireless/marvell/mwifiex/scan.c create mode 100644 drivers/net/wireless/marvell/mwifiex/sdio.c create mode 100644 drivers/net/wireless/marvell/mwifiex/sdio.h create mode 100644 drivers/net/wireless/marvell/mwifiex/sta_cmd.c create mode 100644 drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c create mode 100644 drivers/net/wireless/marvell/mwifiex/sta_event.c create mode 100644 drivers/net/wireless/marvell/mwifiex/sta_ioctl.c create mode 100644 drivers/net/wireless/marvell/mwifiex/sta_rx.c create mode 100644 drivers/net/wireless/marvell/mwifiex/sta_tx.c create mode 100644 drivers/net/wireless/marvell/mwifiex/tdls.c create mode 100644 drivers/net/wireless/marvell/mwifiex/txrx.c create mode 100644 drivers/net/wireless/marvell/mwifiex/uap_cmd.c create mode 100644 drivers/net/wireless/marvell/mwifiex/uap_event.c create mode 100644 drivers/net/wireless/marvell/mwifiex/uap_txrx.c create mode 100644 drivers/net/wireless/marvell/mwifiex/usb.c create mode 100644 drivers/net/wireless/marvell/mwifiex/usb.h create mode 100644 drivers/net/wireless/marvell/mwifiex/util.c create mode 100644 drivers/net/wireless/marvell/mwifiex/util.h create mode 100644 drivers/net/wireless/marvell/mwifiex/wmm.c create mode 100644 drivers/net/wireless/marvell/mwifiex/wmm.h delete mode 100644 drivers/net/wireless/mwifiex/11ac.c delete mode 100644 drivers/net/wireless/mwifiex/11ac.h delete mode 100644 drivers/net/wireless/mwifiex/11h.c delete mode 100644 drivers/net/wireless/mwifiex/11n.c delete mode 100644 drivers/net/wireless/mwifiex/11n.h delete mode 100644 drivers/net/wireless/mwifiex/11n_aggr.c delete mode 100644 drivers/net/wireless/mwifiex/11n_aggr.h delete mode 100644 drivers/net/wireless/mwifiex/11n_rxreorder.c delete mode 100644 drivers/net/wireless/mwifiex/11n_rxreorder.h delete mode 100644 drivers/net/wireless/mwifiex/Kconfig delete mode 100644 drivers/net/wireless/mwifiex/Makefile delete mode 100644 drivers/net/wireless/mwifiex/README delete mode 100644 drivers/net/wireless/mwifiex/cfg80211.c delete mode 100644 drivers/net/wireless/mwifiex/cfg80211.h delete mode 100644 drivers/net/wireless/mwifiex/cfp.c delete mode 100644 drivers/net/wireless/mwifiex/cmdevt.c delete mode 100644 drivers/net/wireless/mwifiex/debugfs.c delete mode 100644 drivers/net/wireless/mwifiex/decl.h delete mode 100644 drivers/net/wireless/mwifiex/ethtool.c delete mode 100644 drivers/net/wireless/mwifiex/fw.h delete mode 100644 drivers/net/wireless/mwifiex/ie.c delete mode 100644 drivers/net/wireless/mwifiex/init.c delete mode 100644 drivers/net/wireless/mwifiex/ioctl.h delete mode 100644 drivers/net/wireless/mwifiex/join.c delete mode 100644 drivers/net/wireless/mwifiex/main.c delete mode 100644 drivers/net/wireless/mwifiex/main.h delete mode 100644 drivers/net/wireless/mwifiex/pcie.c delete mode 100644 drivers/net/wireless/mwifiex/pcie.h delete mode 100644 drivers/net/wireless/mwifiex/scan.c delete mode 100644 drivers/net/wireless/mwifiex/sdio.c delete mode 100644 drivers/net/wireless/mwifiex/sdio.h delete mode 100644 drivers/net/wireless/mwifiex/sta_cmd.c delete mode 100644 drivers/net/wireless/mwifiex/sta_cmdresp.c delete mode 100644 drivers/net/wireless/mwifiex/sta_event.c delete mode 100644 drivers/net/wireless/mwifiex/sta_ioctl.c delete mode 100644 drivers/net/wireless/mwifiex/sta_rx.c delete mode 100644 drivers/net/wireless/mwifiex/sta_tx.c delete mode 100644 drivers/net/wireless/mwifiex/tdls.c delete mode 100644 drivers/net/wireless/mwifiex/txrx.c delete mode 100644 drivers/net/wireless/mwifiex/uap_cmd.c delete mode 100644 drivers/net/wireless/mwifiex/uap_event.c delete mode 100644 drivers/net/wireless/mwifiex/uap_txrx.c delete mode 100644 drivers/net/wireless/mwifiex/usb.c delete mode 100644 drivers/net/wireless/mwifiex/usb.h delete mode 100644 drivers/net/wireless/mwifiex/util.c delete mode 100644 drivers/net/wireless/mwifiex/util.h delete mode 100644 drivers/net/wireless/mwifiex/wmm.c delete mode 100644 drivers/net/wireless/mwifiex/wmm.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 75ff7434db0e..5b889f6972c0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6728,7 +6728,7 @@ M: Amitkumar Karwar M: Nishant Sarmukadam L: linux-wireless@vger.kernel.org S: Maintained -F: drivers/net/wireless/mwifiex/ +F: drivers/net/wireless/marvell/mwifiex/ MARVELL MWL8K WIRELESS DRIVER M: Lennert Buytenhek diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 25f52b32d725..465665237308 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -152,7 +152,6 @@ source "drivers/net/wireless/realtek/rtlwifi/Kconfig" source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig" source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig" -source "drivers/net/wireless/mwifiex/Kconfig" source "drivers/net/wireless/rsi/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 72b167742ac7..7a95b58a5c76 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -40,6 +40,4 @@ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o obj-$(CONFIG_WL_TI) += ti/ -obj-$(CONFIG_MWIFIEX) += mwifiex/ - obj-$(CONFIG_RSI_91X) += rsi/ diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig index 97ec8f35745e..bbdbac9a2a45 100644 --- a/drivers/net/wireless/marvell/Kconfig +++ b/drivers/net/wireless/marvell/Kconfig @@ -13,5 +13,6 @@ if WLAN_VENDOR_MARVELL source "drivers/net/wireless/marvell/libertas/Kconfig" source "drivers/net/wireless/marvell/libertas_tf/Kconfig" +source "drivers/net/wireless/marvell/mwifiex/Kconfig" endif # WLAN_VENDOR_MARVELL diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile index 8f5eb423b71a..f4ab48aaff3c 100644 --- a/drivers/net/wireless/marvell/Makefile +++ b/drivers/net/wireless/marvell/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_LIBERTAS) += libertas/ obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ +obj-$(CONFIG_MWIFIEX) += mwifiex/ diff --git a/drivers/net/wireless/marvell/mwifiex/11ac.c b/drivers/net/wireless/marvell/mwifiex/11ac.c new file mode 100644 index 000000000000..59d23fb2365f --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11ac.c @@ -0,0 +1,382 @@ +/* + * Marvell Wireless LAN device driver: 802.11ac + * + * Copyright (C) 2013-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "fw.h" +#include "main.h" +#include "11ac.h" + +/* Tables of the MCS map to the highest data rate (in Mbps) supported + * for long GI. + */ +static const u16 max_rate_lgi_80MHZ[8][3] = { + {0x124, 0x15F, 0x186}, /* NSS = 1 */ + {0x249, 0x2BE, 0x30C}, /* NSS = 2 */ + {0x36D, 0x41D, 0x492}, /* NSS = 3 */ + {0x492, 0x57C, 0x618}, /* NSS = 4 */ + {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 6 */ + {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */ + {0x924, 0xAF8, 0xC30} /* NSS = 8 */ +}; + +static const u16 max_rate_lgi_160MHZ[8][3] = { + {0x249, 0x2BE, 0x30C}, /* NSS = 1 */ + {0x492, 0x57C, 0x618}, /* NSS = 2 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 3 */ + {0x924, 0xAF8, 0xC30}, /* NSS = 4 */ + {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */ + {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */ + {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */ + {0x1248, 0x15F0, 0x1860} /* NSS = 8 */ +}; + +/* This function converts the 2-bit MCS map to the highest long GI + * VHT data rate. + */ +static u16 +mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, + u8 bands, u16 mcs_map) +{ + u8 i, nss, mcs; + u16 max_rate = 0; + u32 usr_vht_cap_info = 0; + struct mwifiex_adapter *adapter = priv->adapter; + + if (bands & BAND_AAC) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; + + /* find the max NSS supported */ + nss = 1; + for (i = 1; i <= 8; i++) { + mcs = GET_VHTNSSMCS(mcs_map, i); + if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED) + nss = i; + } + mcs = GET_VHTNSSMCS(mcs_map, nss); + + /* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */ + if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED) + mcs = IEEE80211_VHT_MCS_SUPPORT_0_9; + + if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) { + /* support 160 MHz */ + max_rate = max_rate_lgi_160MHZ[nss - 1][mcs]; + if (!max_rate) + /* MCS9 is not supported in NSS6 */ + max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1]; + } else { + max_rate = max_rate_lgi_80MHZ[nss - 1][mcs]; + if (!max_rate) + /* MCS9 is not supported in NSS3 */ + max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1]; + } + + return max_rate; +} + +static void +mwifiex_fill_vht_cap_info(struct mwifiex_private *priv, + struct ieee80211_vht_cap *vht_cap, u8 bands) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (bands & BAND_A) + vht_cap->vht_cap_info = + cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a); + else + vht_cap->vht_cap_info = + cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg); +} + +void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, + struct ieee80211_vht_cap *vht_cap, u8 bands) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u16 mcs_map_user, mcs_map_resp, mcs_map_result; + u16 mcs_user, mcs_resp, nss, tmp; + + /* Fill VHT cap info */ + mwifiex_fill_vht_cap_info(priv, vht_cap, bands); + + /* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */ + mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); + mcs_map_result = 0; + + for (nss = 1; nss <= 8; nss++) { + mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); + + if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min(mcs_user, mcs_resp)); + } + + vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result); + + tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp); + + /* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */ + mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); + mcs_map_result = 0; + + for (nss = 1; nss <= 8; nss++) { + mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); + if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min(mcs_user, mcs_resp)); + } + + vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result); + + tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp); + + return; +} + +int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, + u8 **buffer) +{ + struct mwifiex_ie_types_vhtcap *vht_cap; + struct mwifiex_ie_types_oper_mode_ntf *oper_ntf; + struct ieee_types_oper_mode_ntf *ieee_oper_ntf; + struct mwifiex_ie_types_vht_oper *vht_op; + struct mwifiex_adapter *adapter = priv->adapter; + u8 supp_chwd_set; + u32 usr_vht_cap_info; + int ret_len = 0; + + if (bss_desc->bss_band & BAND_A) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; + + /* VHT Capabilities IE */ + if (bss_desc->bcn_vht_cap) { + vht_cap = (struct mwifiex_ie_types_vhtcap *)*buffer; + memset(vht_cap, 0, sizeof(*vht_cap)); + vht_cap->header.type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY); + vht_cap->header.len = + cpu_to_le16(sizeof(struct ieee80211_vht_cap)); + memcpy((u8 *)vht_cap + sizeof(struct mwifiex_ie_types_header), + (u8 *)bss_desc->bcn_vht_cap, + le16_to_cpu(vht_cap->header.len)); + + mwifiex_fill_vht_cap_tlv(priv, &vht_cap->vht_cap, + bss_desc->bss_band); + *buffer += sizeof(*vht_cap); + ret_len += sizeof(*vht_cap); + } + + /* VHT Operation IE */ + if (bss_desc->bcn_vht_oper) { + if (priv->bss_mode == NL80211_IFTYPE_STATION) { + vht_op = (struct mwifiex_ie_types_vht_oper *)*buffer; + memset(vht_op, 0, sizeof(*vht_op)); + vht_op->header.type = + cpu_to_le16(WLAN_EID_VHT_OPERATION); + vht_op->header.len = cpu_to_le16(sizeof(*vht_op) - + sizeof(struct mwifiex_ie_types_header)); + memcpy((u8 *)vht_op + + sizeof(struct mwifiex_ie_types_header), + (u8 *)bss_desc->bcn_vht_oper, + le16_to_cpu(vht_op->header.len)); + + /* negotiate the channel width and central freq + * and keep the central freq as the peer suggests + */ + supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); + + switch (supp_chwd_set) { + case 0: + vht_op->chan_width = + min_t(u8, IEEE80211_VHT_CHANWIDTH_80MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + case 1: + vht_op->chan_width = + min_t(u8, IEEE80211_VHT_CHANWIDTH_160MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + case 2: + vht_op->chan_width = + min_t(u8, IEEE80211_VHT_CHANWIDTH_80P80MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + default: + vht_op->chan_width = + IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + + *buffer += sizeof(*vht_op); + ret_len += sizeof(*vht_op); + } + } + + /* Operating Mode Notification IE */ + if (bss_desc->oper_mode) { + ieee_oper_ntf = bss_desc->oper_mode; + oper_ntf = (void *)*buffer; + memset(oper_ntf, 0, sizeof(*oper_ntf)); + oper_ntf->header.type = cpu_to_le16(WLAN_EID_OPMODE_NOTIF); + oper_ntf->header.len = cpu_to_le16(sizeof(u8)); + oper_ntf->oper_mode = ieee_oper_ntf->oper_mode; + *buffer += sizeof(*oper_ntf); + ret_len += sizeof(*oper_ntf); + } + + return ret_len; +} + +int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct mwifiex_11ac_vht_cfg *cfg) +{ + struct host_cmd_11ac_vht_cfg *vhtcfg = &cmd->params.vht_cfg; + + cmd->command = cpu_to_le16(HostCmd_CMD_11AC_CFG); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_11ac_vht_cfg) + + S_DS_GEN); + vhtcfg->action = cpu_to_le16(cmd_action); + vhtcfg->band_config = cfg->band_config; + vhtcfg->misc_config = cfg->misc_config; + vhtcfg->cap_info = cpu_to_le32(cfg->cap_info); + vhtcfg->mcs_tx_set = cpu_to_le32(cfg->mcs_tx_set); + vhtcfg->mcs_rx_set = cpu_to_le32(cfg->mcs_rx_set); + + return 0; +} + +/* This function initializes the BlockACK setup information for given + * mwifiex_private structure for 11ac enabled networks. + */ +void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv) +{ + priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + priv->add_ba_param.tx_win_size = + MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE; + } else { + priv->add_ba_param.tx_win_size = + MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; + } + + return; +} + +bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv) +{ + struct mwifiex_bssdescriptor *bss_desc; + struct ieee80211_vht_operation *vht_oper; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + vht_oper = bss_desc->bcn_vht_oper; + + if (!bss_desc->bcn_vht_cap || !vht_oper) + return false; + + if (vht_oper->chan_width == IEEE80211_VHT_CHANWIDTH_USE_HT) + return false; + + return true; +} + +u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, + u32 pri_chan, u8 chan_bw) +{ + u8 center_freq_idx = 0; + + if (band & BAND_AAC) { + switch (pri_chan) { + case 36: + case 40: + case 44: + case 48: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 42; + break; + case 52: + case 56: + case 60: + case 64: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 58; + else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) + center_freq_idx = 50; + break; + case 100: + case 104: + case 108: + case 112: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 106; + break; + case 116: + case 120: + case 124: + case 128: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 122; + else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) + center_freq_idx = 114; + break; + case 132: + case 136: + case 140: + case 144: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 138; + break; + case 149: + case 153: + case 157: + case 161: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 155; + break; + default: + center_freq_idx = 42; + } + } + + return center_freq_idx; +} diff --git a/drivers/net/wireless/marvell/mwifiex/11ac.h b/drivers/net/wireless/marvell/mwifiex/11ac.h new file mode 100644 index 000000000000..1ca92c7a8a4a --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11ac.h @@ -0,0 +1,45 @@ +/* + * Marvell Wireless LAN device driver: 802.11ac + * + * Copyright (C) 2013-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_11AC_H_ +#define _MWIFIEX_11AC_H_ + +#define VHT_CFG_2GHZ BIT(0) +#define VHT_CFG_5GHZ BIT(1) + +enum vht_cfg_misc_config { + VHT_CAP_TX_OPERATION = 1, + VHT_CAP_ASSOCIATION, + VHT_CAP_UAP_ONLY +}; + +#define DEFAULT_VHT_MCS_SET 0xfffa +#define DISABLE_VHT_MCS_SET 0xffff + +#define VHT_BW_80_160_80P80 BIT(2) + +int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, + u8 **buffer); +int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct mwifiex_11ac_vht_cfg *cfg); +void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, + struct ieee80211_vht_cap *vht_cap, u8 bands); +#endif /* _MWIFIEX_11AC_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/11h.c b/drivers/net/wireless/marvell/mwifiex/11h.c new file mode 100644 index 000000000000..71a1b580796f --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11h.c @@ -0,0 +1,319 @@ +/* + * Marvell Wireless LAN device driver: 802.11h + * + * Copyright (C) 2013-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" +#include "fw.h" + + +void mwifiex_init_11h_params(struct mwifiex_private *priv) +{ + priv->state_11h.is_11h_enabled = true; + priv->state_11h.is_11h_active = false; +} + +inline int mwifiex_is_11h_active(struct mwifiex_private *priv) +{ + return priv->state_11h.is_11h_active; +} +/* This function appends 11h info to a buffer while joining an + * infrastructure BSS + */ +static void +mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer, + struct mwifiex_bssdescriptor *bss_desc) +{ + struct mwifiex_ie_types_header *ie_header; + struct mwifiex_ie_types_pwr_capability *cap; + struct mwifiex_ie_types_local_pwr_constraint *constraint; + struct ieee80211_supported_band *sband; + u8 radio_type; + int i; + + if (!buffer || !(*buffer)) + return; + + radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); + sband = priv->wdev.wiphy->bands[radio_type]; + + cap = (struct mwifiex_ie_types_pwr_capability *)*buffer; + cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY); + cap->header.len = cpu_to_le16(2); + cap->min_pwr = 0; + cap->max_pwr = 0; + *buffer += sizeof(*cap); + + constraint = (struct mwifiex_ie_types_local_pwr_constraint *)*buffer; + constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT); + constraint->header.len = cpu_to_le16(2); + constraint->chan = bss_desc->channel; + constraint->constraint = bss_desc->local_constraint; + *buffer += sizeof(*constraint); + + ie_header = (struct mwifiex_ie_types_header *)*buffer; + ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header->len = cpu_to_le16(2 * sband->n_channels + 2); + *buffer += sizeof(*ie_header); + *(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS; + *(*buffer)++ = 2 * sband->n_channels; + for (i = 0; i < sband->n_channels; i++) { + *(*buffer)++ = ieee80211_frequency_to_channel( + sband->channels[i].center_freq); + *(*buffer)++ = 1; /* one channel in the subband */ + } +} + +/* Enable or disable the 11h extensions in the firmware */ +int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag) +{ + u32 enable = flag; + + /* enable master mode radar detection on AP interface */ + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && enable) + enable |= MWIFIEX_MASTER_RADAR_DET_MASK; + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, DOT11H_I, &enable, true); +} + +/* This functions processes TLV buffer for a pending BSS Join command. + * + * Activate 11h functionality in the firmware if the spectrum management + * capability bit is found in the network we are joining. Also, necessary + * TLVs are set based on requested network's 11h capability. + */ +void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (bss_desc->sensed_11h) { + /* Activate 11h functions in firmware, turns on capability + * bit + */ + mwifiex_11h_activate(priv, true); + priv->state_11h.is_11h_active = true; + bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT; + mwifiex_11h_process_infra_join(priv, buffer, bss_desc); + } else { + /* Deactivate 11h functions in the firmware */ + mwifiex_11h_activate(priv, false); + priv->state_11h.is_11h_active = false; + bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT; + } +} + +/* This is DFS CAC work queue function. + * This delayed work emits CAC finished event for cfg80211 if + * CAC was started earlier. + */ +void mwifiex_dfs_cac_work_queue(struct work_struct *work) +{ + struct cfg80211_chan_def chandef; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct mwifiex_private *priv = + container_of(delayed_work, struct mwifiex_private, + dfs_cac_work); + + if (WARN_ON(!priv)) + return; + + chandef = priv->dfs_chandef; + if (priv->wdev.cac_started) { + mwifiex_dbg(priv->adapter, MSG, + "CAC timer finished; No radar detected\n"); + cfg80211_cac_event(priv->netdev, &chandef, + NL80211_RADAR_CAC_FINISHED, + GFP_KERNEL); + } +} + +/* This function prepares channel report request command to FW for + * starting radar detection. + */ +int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req; + struct mwifiex_radar_params *radar_params = (void *)data_buf; + + cmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST); + cmd->size = cpu_to_le16(S_DS_GEN); + le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_chan_rpt_req)); + + cr_req->chan_desc.start_freq = cpu_to_le16(MWIFIEX_A_BAND_START_FREQ); + cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value; + cr_req->chan_desc.chan_width = radar_params->chandef->width; + cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms); + + if (radar_params->cac_time_ms) + mwifiex_dbg(priv->adapter, MSG, + "11h: issuing DFS Radar check for channel=%d\n", + radar_params->chandef->chan->hw_value); + else + mwifiex_dbg(priv->adapter, MSG, "cancelling CAC\n"); + + return 0; +} + +int mwifiex_stop_radar_detection(struct mwifiex_private *priv, + struct cfg80211_chan_def *chandef) +{ + struct mwifiex_radar_params radar_params; + + memset(&radar_params, 0, sizeof(struct mwifiex_radar_params)); + radar_params.chandef = chandef; + radar_params.cac_time_ms = 0; + + return mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, &radar_params, true); +} + +/* This function is to abort ongoing CAC upon stopping AP operations + * or during unload. + */ +void mwifiex_abort_cac(struct mwifiex_private *priv) +{ + if (priv->wdev.cac_started) { + if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) + mwifiex_dbg(priv->adapter, ERROR, + "failed to stop CAC in FW\n"); + mwifiex_dbg(priv->adapter, MSG, + "Aborting delayed work for CAC.\n"); + cancel_delayed_work_sync(&priv->dfs_cac_work); + cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); + } +} + +/* This function handles channel report event from FW during CAC period. + * If radar is detected during CAC, driver indicates the same to cfg80211 + * and also cancels ongoing delayed work. + */ +int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct host_cmd_ds_chan_rpt_event *rpt_event; + struct mwifiex_ie_types_chan_rpt_data *rpt; + u8 *evt_buf; + u16 event_len, tlv_len; + + rpt_event = (void *)(skb->data + sizeof(u32)); + event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event)+ + sizeof(u32)); + + if (le32_to_cpu(rpt_event->result) != HostCmd_RESULT_OK) { + mwifiex_dbg(priv->adapter, ERROR, + "Error in channel report event\n"); + return -1; + } + + evt_buf = (void *)&rpt_event->tlvbuf; + + while (event_len >= sizeof(struct mwifiex_ie_types_header)) { + rpt = (void *)&rpt_event->tlvbuf; + tlv_len = le16_to_cpu(rpt->header.len); + + switch (le16_to_cpu(rpt->header.type)) { + case TLV_TYPE_CHANRPT_11H_BASIC: + if (rpt->map.radar) { + mwifiex_dbg(priv->adapter, MSG, + "RADAR Detected on channel %d!\n", + priv->dfs_chandef.chan->hw_value); + cancel_delayed_work_sync(&priv->dfs_cac_work); + cfg80211_cac_event(priv->netdev, + &priv->dfs_chandef, + NL80211_RADAR_DETECTED, + GFP_KERNEL); + } + break; + default: + break; + } + + evt_buf += (tlv_len + sizeof(rpt->header)); + event_len -= (tlv_len + sizeof(rpt->header)); + } + + return 0; +} + +/* Handler for radar detected event from FW.*/ +int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_radar_det_event *rdr_event; + + rdr_event = (void *)(skb->data + sizeof(u32)); + + if (le32_to_cpu(rdr_event->passed)) { + mwifiex_dbg(priv->adapter, MSG, + "radar detected; indicating kernel\n"); + if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) + mwifiex_dbg(priv->adapter, ERROR, + "Failed to stop CAC in FW\n"); + cfg80211_radar_event(priv->adapter->wiphy, &priv->dfs_chandef, + GFP_KERNEL); + mwifiex_dbg(priv->adapter, MSG, "regdomain: %d\n", + rdr_event->reg_domain); + mwifiex_dbg(priv->adapter, MSG, "radar detection type: %d\n", + rdr_event->det_type); + } else { + mwifiex_dbg(priv->adapter, MSG, + "false radar detection event!\n"); + } + + return 0; +} + +/* This is work queue function for channel switch handling. + * This function takes care of updating new channel definitin to + * bss config structure, restart AP and indicate channel switch success + * to cfg80211. + */ +void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work) +{ + struct mwifiex_uap_bss_param *bss_cfg; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct mwifiex_private *priv = + container_of(delayed_work, struct mwifiex_private, + dfs_chan_sw_work); + + if (WARN_ON(!priv)) + return; + + bss_cfg = &priv->bss_cfg; + if (!bss_cfg->beacon_period) { + mwifiex_dbg(priv->adapter, ERROR, + "channel switch: AP already stopped\n"); + return; + } + + mwifiex_uap_set_channel(priv, bss_cfg, priv->dfs_chandef); + + if (mwifiex_config_start_uap(priv, bss_cfg)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to start AP after channel switch\n"); + return; + } + + mwifiex_dbg(priv->adapter, MSG, + "indicating channel switch completion to kernel\n"); + cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef); +} diff --git a/drivers/net/wireless/marvell/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c new file mode 100644 index 000000000000..c174e79e6df2 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n.c @@ -0,0 +1,914 @@ +/* + * Marvell Wireless LAN device driver: 802.11n + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" + +/* + * Fills HT capability information field, AMPDU Parameters field, HT extended + * capability field, and supported MCS set fields. + * + * HT capability information field, AMPDU Parameters field, supported MCS set + * fields are retrieved from cfg80211 stack + * + * RD responder bit to set to clear in the extended capability header. + */ +int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, + struct ieee80211_ht_cap *ht_cap) +{ + uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info); + struct ieee80211_supported_band *sband = + priv->wdev.wiphy->bands[radio_type]; + + if (WARN_ON_ONCE(!sband)) { + mwifiex_dbg(priv->adapter, ERROR, "Invalid radio type!\n"); + return -EINVAL; + } + + ht_cap->ampdu_params_info = + (sband->ht_cap.ampdu_factor & + IEEE80211_HT_AMPDU_PARM_FACTOR) | + ((sband->ht_cap.ampdu_density << + IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) & + IEEE80211_HT_AMPDU_PARM_DENSITY); + + memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs, + sizeof(sband->ht_cap.mcs)); + + if (priv->bss_mode == NL80211_IFTYPE_STATION || + (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && + (priv->adapter->sec_chan_offset != + IEEE80211_HT_PARAM_CHA_SEC_NONE))) + /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ + SETHT_MCS32(ht_cap->mcs.rx_mask); + + /* Clear RD responder bit */ + ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER; + + ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap); + ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap); + + if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap)) + ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP); + + return 0; +} + +/* + * This function returns the pointer to an entry in BA Stream + * table which matches the requested BA status. + */ +static struct mwifiex_tx_ba_stream_tbl * +mwifiex_get_ba_status(struct mwifiex_private *priv, + enum mwifiex_ba_status ba_status) +{ + struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; + unsigned long flags; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (tx_ba_tsr_tbl->ba_status == ba_status) { + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, + flags); + return tx_ba_tsr_tbl; + } + } + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + return NULL; +} + +/* + * This function handles the command response of delete a block + * ack request. + * + * The function checks the response success status and takes action + * accordingly (send an add BA request in case of success, or recreate + * the deleted stream in case of failure, if the add BA was also + * initiated by us). + */ +int mwifiex_ret_11n_delba(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + int tid; + struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; + struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba; + uint16_t del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set); + + tid = del_ba_param_set >> DELBA_TID_POS; + if (del_ba->del_result == BA_RESULT_SUCCESS) { + mwifiex_del_ba_tbl(priv, tid, del_ba->peer_mac_addr, + TYPE_DELBA_SENT, + INITIATOR_BIT(del_ba_param_set)); + + tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS); + if (tx_ba_tbl) + mwifiex_send_addba(priv, tx_ba_tbl->tid, + tx_ba_tbl->ra); + } else { /* + * In case of failure, recreate the deleted stream in case + * we initiated the ADDBA + */ + if (!INITIATOR_BIT(del_ba_param_set)) + return 0; + + mwifiex_create_ba_tbl(priv, del_ba->peer_mac_addr, tid, + BA_SETUP_INPROGRESS); + + tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS); + + if (tx_ba_tbl) + mwifiex_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra, + TYPE_DELBA_SENT, true); + } + + return 0; +} + +/* + * This function handles the command response of add a block + * ack request. + * + * Handling includes changing the header fields to CPU formats, checking + * the response success status and taking actions accordingly (delete the + * BA stream table in case of failure). + */ +int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + int tid, tid_down; + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; + struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; + struct mwifiex_ra_list_tbl *ra_list; + u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); + + add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) + & SSN_MASK); + + tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + + tid_down = mwifiex_wmm_downgrade_tid(priv, tid); + ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, add_ba_rsp-> + peer_mac_addr); + if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { + if (ra_list) { + ra_list->ba_status = BA_SETUP_NONE; + ra_list->amsdu_in_ampdu = false; + } + mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr, + TYPE_DELBA_SENT, true); + if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) + priv->aggr_prio_tbl[tid].ampdu_ap = + BA_STREAM_NOT_ALLOWED; + return 0; + } + + tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr); + if (tx_ba_tbl) { + mwifiex_dbg(priv->adapter, EVENT, "info: BA stream complete\n"); + tx_ba_tbl->ba_status = BA_SETUP_COMPLETE; + if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.tx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + tx_ba_tbl->amsdu = true; + else + tx_ba_tbl->amsdu = false; + if (ra_list) { + ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu; + ra_list->ba_status = BA_SETUP_COMPLETE; + } + } else { + mwifiex_dbg(priv->adapter, ERROR, "BA stream not created\n"); + } + + return 0; +} + +/* + * This function prepares command of reconfigure Tx buffer. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting Tx buffer size (for SET only) + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, int cmd_action, + u16 *buf_size) +{ + struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf; + u16 action = (u16) cmd_action; + + cmd->command = cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN); + tx_buf->action = cpu_to_le16(action); + switch (action) { + case HostCmd_ACT_GEN_SET: + mwifiex_dbg(priv->adapter, CMD, + "cmd: set tx_buf=%d\n", *buf_size); + tx_buf->buff_size = cpu_to_le16(*buf_size); + break; + case HostCmd_ACT_GEN_GET: + default: + tx_buf->buff_size = 0; + break; + } + return 0; +} + +/* + * This function prepares command of AMSDU aggregation control. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting AMSDU control parameters (for SET only) + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, + int cmd_action, + struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl) +{ + struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl = + &cmd->params.amsdu_aggr_ctrl; + u16 action = (u16) cmd_action; + + cmd->command = cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl) + + S_DS_GEN); + amsdu_ctrl->action = cpu_to_le16(action); + switch (action) { + case HostCmd_ACT_GEN_SET: + amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable); + amsdu_ctrl->curr_buf_size = 0; + break; + case HostCmd_ACT_GEN_GET: + default: + amsdu_ctrl->curr_buf_size = 0; + break; + } + return 0; +} + +/* + * This function prepares 11n configuration command. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting HT Tx capability and HT Tx information fields + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct mwifiex_ds_11n_tx_cfg *txcfg) +{ + struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg; + + cmd->command = cpu_to_le16(HostCmd_CMD_11N_CFG); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN); + htcfg->action = cpu_to_le16(cmd_action); + htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap); + htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo); + + if (priv->adapter->is_hw_11ac_capable) + htcfg->misc_config = cpu_to_le16(txcfg->misc_config); + + return 0; +} + +/* + * This function appends an 11n TLV to a buffer. + * + * Buffer allocation is responsibility of the calling + * function. No size validation is made here. + * + * The function fills up the following sections, if applicable - + * - HT capability IE + * - HT information IE (with channel list) + * - 20/40 BSS Coexistence IE + * - HT Extended Capabilities IE + */ +int +mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, + u8 **buffer) +{ + struct mwifiex_ie_types_htcap *ht_cap; + struct mwifiex_ie_types_htinfo *ht_info; + struct mwifiex_ie_types_chan_list_param_set *chan_list; + struct mwifiex_ie_types_2040bssco *bss_co_2040; + struct mwifiex_ie_types_extcap *ext_cap; + int ret_len = 0; + struct ieee80211_supported_band *sband; + struct ieee_types_header *hdr; + u8 radio_type; + + if (!buffer || !*buffer) + return ret_len; + + radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); + sband = priv->wdev.wiphy->bands[radio_type]; + + if (bss_desc->bcn_ht_cap) { + ht_cap = (struct mwifiex_ie_types_htcap *) *buffer; + memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); + ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_cap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header), + (u8 *)bss_desc->bcn_ht_cap, + le16_to_cpu(ht_cap->header.len)); + + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); + + *buffer += sizeof(struct mwifiex_ie_types_htcap); + ret_len += sizeof(struct mwifiex_ie_types_htcap); + } + + if (bss_desc->bcn_ht_oper) { + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { + ht_info = (struct mwifiex_ie_types_htinfo *) *buffer; + memset(ht_info, 0, + sizeof(struct mwifiex_ie_types_htinfo)); + ht_info->header.type = + cpu_to_le16(WLAN_EID_HT_OPERATION); + ht_info->header.len = + cpu_to_le16( + sizeof(struct ieee80211_ht_operation)); + + memcpy((u8 *) ht_info + + sizeof(struct mwifiex_ie_types_header), + (u8 *)bss_desc->bcn_ht_oper, + le16_to_cpu(ht_info->header.len)); + + if (!(sband->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + ht_info->ht_oper.ht_param &= + ~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY | + IEEE80211_HT_PARAM_CHA_SEC_OFFSET); + + *buffer += sizeof(struct mwifiex_ie_types_htinfo); + ret_len += sizeof(struct mwifiex_ie_types_htinfo); + } + + chan_list = + (struct mwifiex_ie_types_chan_list_param_set *) *buffer; + memset(chan_list, 0, + sizeof(struct mwifiex_ie_types_chan_list_param_set)); + chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + chan_list->header.len = cpu_to_le16( + sizeof(struct mwifiex_ie_types_chan_list_param_set) - + sizeof(struct mwifiex_ie_types_header)); + chan_list->chan_scan_param[0].chan_number = + bss_desc->bcn_ht_oper->primary_chan; + chan_list->chan_scan_param[0].radio_type = + mwifiex_band_to_radio_type((u8) bss_desc->bss_band); + + if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && + bss_desc->bcn_ht_oper->ht_param & + IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) + SET_SECONDARYCHAN(chan_list->chan_scan_param[0]. + radio_type, + (bss_desc->bcn_ht_oper->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); + + *buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set); + ret_len += sizeof(struct mwifiex_ie_types_chan_list_param_set); + } + + if (bss_desc->bcn_bss_co_2040) { + bss_co_2040 = (struct mwifiex_ie_types_2040bssco *) *buffer; + memset(bss_co_2040, 0, + sizeof(struct mwifiex_ie_types_2040bssco)); + bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040); + bss_co_2040->header.len = + cpu_to_le16(sizeof(bss_co_2040->bss_co_2040)); + + memcpy((u8 *) bss_co_2040 + + sizeof(struct mwifiex_ie_types_header), + bss_desc->bcn_bss_co_2040 + + sizeof(struct ieee_types_header), + le16_to_cpu(bss_co_2040->header.len)); + + *buffer += sizeof(struct mwifiex_ie_types_2040bssco); + ret_len += sizeof(struct mwifiex_ie_types_2040bssco); + } + + if (bss_desc->bcn_ext_cap) { + hdr = (void *)bss_desc->bcn_ext_cap; + ext_cap = (struct mwifiex_ie_types_extcap *) *buffer; + memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap)); + ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY); + ext_cap->header.len = cpu_to_le16(hdr->len); + + memcpy((u8 *)ext_cap->ext_capab, + bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header), + le16_to_cpu(ext_cap->header.len)); + + if (hdr->len > 3 && + ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED) + priv->hs2_enabled = true; + else + priv->hs2_enabled = false; + + *buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len; + ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len; + } + + return ret_len; +} + +/* + * This function checks if the given pointer is valid entry of + * Tx BA Stream table. + */ +static int mwifiex_is_tx_ba_stream_ptr_valid(struct mwifiex_private *priv, + struct mwifiex_tx_ba_stream_tbl *tx_tbl_ptr) +{ + struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; + + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (tx_ba_tsr_tbl == tx_tbl_ptr) + return true; + } + + return false; +} + +/* + * This function deletes the given entry in Tx BA Stream table. + * + * The function also performs a validity check on the supplied + * pointer before trying to delete. + */ +void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, + struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl) +{ + if (!tx_ba_tsr_tbl && + mwifiex_is_tx_ba_stream_ptr_valid(priv, tx_ba_tsr_tbl)) + return; + + mwifiex_dbg(priv->adapter, INFO, + "info: tx_ba_tsr_tbl %p\n", tx_ba_tsr_tbl); + + list_del(&tx_ba_tsr_tbl->list); + + kfree(tx_ba_tsr_tbl); +} + +/* + * This function deletes all the entries in Tx BA Stream table. + */ +void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv) +{ + int i; + struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node; + unsigned long flags; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry_safe(del_tbl_ptr, tmp_node, + &priv->tx_ba_stream_tbl_ptr, list) + mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr); + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + + INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); + + for (i = 0; i < MAX_NUM_TID; ++i) + priv->aggr_prio_tbl[i].ampdu_ap = + priv->aggr_prio_tbl[i].ampdu_user; +} + +/* + * This function returns the pointer to an entry in BA Stream + * table which matches the given RA/TID pair. + */ +struct mwifiex_tx_ba_stream_tbl * +mwifiex_get_ba_tbl(struct mwifiex_private *priv, int tid, u8 *ra) +{ + struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; + unsigned long flags; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) && + tx_ba_tsr_tbl->tid == tid) { + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, + flags); + return tx_ba_tsr_tbl; + } + } + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + return NULL; +} + +/* + * This function creates an entry in Tx BA stream table for the + * given RA/TID pair. + */ +void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, + enum mwifiex_ba_status ba_status) +{ + struct mwifiex_tx_ba_stream_tbl *new_node; + struct mwifiex_ra_list_tbl *ra_list; + unsigned long flags; + int tid_down; + + if (!mwifiex_get_ba_tbl(priv, tid, ra)) { + new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl), + GFP_ATOMIC); + if (!new_node) + return; + + tid_down = mwifiex_wmm_downgrade_tid(priv, tid); + ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, ra); + if (ra_list) { + ra_list->ba_status = ba_status; + ra_list->amsdu_in_ampdu = false; + } + INIT_LIST_HEAD(&new_node->list); + + new_node->tid = tid; + new_node->ba_status = ba_status; + memcpy(new_node->ra, ra, ETH_ALEN); + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr); + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + } +} + +/* + * This function sends an add BA request to the given TID/RA pair. + */ +int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) +{ + struct host_cmd_ds_11n_addba_req add_ba_req; + u32 tx_win_size = priv->add_ba_param.tx_win_size; + static u8 dialog_tok; + int ret; + unsigned long flags; + u16 block_ack_param_set; + + mwifiex_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid); + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->is_hw_11ac_capable && + memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) { + struct mwifiex_sta_node *sta_ptr; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, peer_mac); + if (!sta_ptr) { + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + mwifiex_dbg(priv->adapter, ERROR, + "BA setup with unknown TDLS peer %pM!\n", + peer_mac); + return -1; + } + if (sta_ptr->is_11ac_enabled) + tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + } + + block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) | + tx_win_size << BLOCKACKPARAM_WINSIZE_POS | + IMMEDIATE_BLOCK_ACK); + + /* enable AMSDU inside AMPDU */ + if (priv->add_ba_param.tx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK; + + add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set); + add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout); + + ++dialog_tok; + + if (dialog_tok == 0) + dialog_tok = 1; + + add_ba_req.dialog_token = dialog_tok; + memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN); + + /* We don't wait for the response of this command */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ, + 0, 0, &add_ba_req, false); + + return ret; +} + +/* + * This function sends a delete BA request to the given TID/RA pair. + */ +int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, + int initiator) +{ + struct host_cmd_ds_11n_delba delba; + int ret; + uint16_t del_ba_param_set; + + memset(&delba, 0, sizeof(delba)); + delba.del_ba_param_set = cpu_to_le16(tid << DELBA_TID_POS); + + del_ba_param_set = le16_to_cpu(delba.del_ba_param_set); + if (initiator) + del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK; + else + del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK; + + memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN); + + /* We don't wait for the response of this command */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, + HostCmd_ACT_GEN_SET, 0, &delba, false); + + return ret; +} + +/* + * This function sends delba to specific tid + */ +void mwifiex_11n_delba(struct mwifiex_private *priv, int tid) +{ + struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; + + if (list_empty(&priv->rx_reorder_tbl_ptr)) { + dev_dbg(priv->adapter->dev, + "mwifiex_11n_delba: rx_reorder_tbl_ptr empty\n"); + return; + } + + list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) { + if (rx_reor_tbl_ptr->tid == tid) { + dev_dbg(priv->adapter->dev, + "Send delba to tid=%d, %pM\n", + tid, rx_reor_tbl_ptr->ta); + mwifiex_send_delba(priv, tid, rx_reor_tbl_ptr->ta, 0); + return; + } + } +} + +/* + * This function handles the command response of a delete BA request. + */ +void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba) +{ + struct host_cmd_ds_11n_delba *cmd_del_ba = + (struct host_cmd_ds_11n_delba *) del_ba; + uint16_t del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set); + int tid; + + tid = del_ba_param_set >> DELBA_TID_POS; + + mwifiex_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr, + TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set)); +} + +/* + * This function retrieves the Rx reordering table. + */ +int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, + struct mwifiex_ds_rx_reorder_tbl *buf) +{ + int i; + struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf; + struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr; + int count = 0; + unsigned long flags; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr, + list) { + rx_reo_tbl->tid = (u16) rx_reorder_tbl_ptr->tid; + memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN); + rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win; + rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size; + for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) + rx_reo_tbl->buffer[i] = true; + else + rx_reo_tbl->buffer[i] = false; + } + rx_reo_tbl++; + count++; + + if (count >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED) + break; + } + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + return count; +} + +/* + * This function retrieves the Tx BA stream table. + */ +int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, + struct mwifiex_ds_tx_ba_stream_tbl *buf) +{ + struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; + struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf; + int count = 0; + unsigned long flags; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + rx_reo_tbl->tid = (u16) tx_ba_tsr_tbl->tid; + mwifiex_dbg(priv->adapter, DATA, "data: %s tid=%d\n", + __func__, rx_reo_tbl->tid); + memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN); + rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu; + rx_reo_tbl++; + count++; + if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) + break; + } + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + + return count; +} + +/* + * This function retrieves the entry for specific tx BA stream table by RA and + * deletes it. + */ +void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra) +{ + struct mwifiex_tx_ba_stream_tbl *tbl, *tmp; + unsigned long flags; + + if (!ra) + return; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list) { + if (!memcmp(tbl->ra, ra, ETH_ALEN)) { + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, + flags); + mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, tbl); + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + } + } + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + + return; +} + +/* This function initializes the BlockACK setup information for given + * mwifiex_private structure. + */ +void mwifiex_set_ba_params(struct mwifiex_private *priv) +{ + priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + priv->add_ba_param.tx_win_size = + MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE; + } else { + priv->add_ba_param.tx_win_size = + MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; + } + + priv->add_ba_param.tx_amsdu = true; + priv->add_ba_param.rx_amsdu = true; + + return; +} + +u8 mwifiex_get_sec_chan_offset(int chan) +{ + u8 sec_offset; + + switch (chan) { + case 36: + case 44: + case 52: + case 60: + case 100: + case 108: + case 116: + case 124: + case 132: + case 140: + case 149: + case 157: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + break; + case 40: + case 48: + case 56: + case 64: + case 104: + case 112: + case 120: + case 128: + case 136: + case 144: + case 153: + case 161: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + break; + case 165: + default: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + break; + } + + return sec_offset; +} + +/* This function will send DELBA to entries in the priv's + * Tx BA stream table + */ +static void +mwifiex_send_delba_txbastream_tbl(struct mwifiex_private *priv, u8 tid) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_tx_ba_stream_tbl *tx_ba_stream_tbl_ptr; + + if (list_empty(&priv->tx_ba_stream_tbl_ptr)) + return; + + list_for_each_entry(tx_ba_stream_tbl_ptr, + &priv->tx_ba_stream_tbl_ptr, list) { + if (tx_ba_stream_tbl_ptr->ba_status == BA_SETUP_COMPLETE) { + if (tid == tx_ba_stream_tbl_ptr->tid) { + dev_dbg(adapter->dev, + "Tx:Send delba to tid=%d, %pM\n", tid, + tx_ba_stream_tbl_ptr->ra); + mwifiex_send_delba(priv, + tx_ba_stream_tbl_ptr->tid, + tx_ba_stream_tbl_ptr->ra, 1); + return; + } + } + } +} + +/* This function updates all the tx_win_size + */ +void mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *adapter) +{ + u8 i; + u32 tx_win_size; + struct mwifiex_private *priv; + + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i]) + continue; + priv = adapter->priv[i]; + tx_win_size = priv->add_ba_param.tx_win_size; + + if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) + priv->add_ba_param.tx_win_size = + MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; + + if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) + priv->add_ba_param.tx_win_size = + MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; + + if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) + priv->add_ba_param.tx_win_size = + MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE; + + if (adapter->coex_win_size) { + if (adapter->coex_tx_win_size) + priv->add_ba_param.tx_win_size = + adapter->coex_tx_win_size; + } + + if (tx_win_size != priv->add_ba_param.tx_win_size) { + if (!priv->media_connected) + continue; + for (i = 0; i < MAX_NUM_TID; i++) + mwifiex_send_delba_txbastream_tbl(priv, i); + } + } +} diff --git a/drivers/net/wireless/marvell/mwifiex/11n.h b/drivers/net/wireless/marvell/mwifiex/11n.h new file mode 100644 index 000000000000..afdd58aa90de --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n.h @@ -0,0 +1,191 @@ +/* + * Marvell Wireless LAN device driver: 802.11n + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_11N_H_ +#define _MWIFIEX_11N_H_ + +#include "11n_aggr.h" +#include "11n_rxreorder.h" +#include "wmm.h" + +int mwifiex_ret_11n_delba(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct mwifiex_ds_11n_tx_cfg *txcfg); +int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, + u8 **buffer); +int mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type, + struct ieee80211_ht_cap *); +int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv, + u16 action, int *htcap_cfg); +void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, + struct mwifiex_tx_ba_stream_tbl + *tx_tbl); +void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv); +struct mwifiex_tx_ba_stream_tbl *mwifiex_get_ba_tbl(struct + mwifiex_private + *priv, int tid, + u8 *ra); +void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, + enum mwifiex_ba_status ba_status); +int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac); +int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, + int initiator); +void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba); +int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, + struct mwifiex_ds_rx_reorder_tbl *buf); +int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, + struct mwifiex_ds_tx_ba_stream_tbl *buf); +int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + int cmd_action, u16 *buf_size); +int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, + int cmd_action, + struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl); +void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra); +u8 mwifiex_get_sec_chan_offset(int chan); + +static inline u8 +mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int tid) +{ + struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ptr->ra); + + if (unlikely(!node)) + return false; + + return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false; +} + +/* This function checks whether AMPDU is allowed or not for a particular TID. */ +static inline u8 +mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int tid) +{ + if (is_broadcast_ether_addr(ptr->ra)) + return false; + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); + } else { + if (ptr->tdls_link) + return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); + + return (priv->aggr_prio_tbl[tid].ampdu_ap != + BA_STREAM_NOT_ALLOWED) ? true : false; + } +} + +/* + * This function checks whether AMSDU is allowed or not for a particular TID. + */ +static inline u8 +mwifiex_is_amsdu_allowed(struct mwifiex_private *priv, int tid) +{ + return (((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) && + (priv->is_data_rate_auto || !(priv->bitmap_rates[2] & 0x03))) + ? true : false); +} + +/* + * This function checks whether a space is available for new BA stream or not. + */ +static inline u8 mwifiex_space_avail_for_new_ba_stream( + struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + u8 i; + u32 ba_stream_num = 0, ba_stream_max; + + ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + ba_stream_num += mwifiex_wmm_list_len( + &priv->tx_ba_stream_tbl_ptr); + } + + if (adapter->fw_api_ver == MWIFIEX_FW_V15) { + ba_stream_max = + GETSUPP_TXBASTREAMS(adapter->hw_dot_11n_dev_cap); + if (!ba_stream_max) + ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; + } + + return ((ba_stream_num < ba_stream_max) ? true : false); +} + +/* + * This function finds the correct Tx BA stream to delete. + * + * Upon successfully locating, both the TID and the RA are returned. + */ +static inline u8 +mwifiex_find_stream_to_delete(struct mwifiex_private *priv, int ptr_tid, + int *ptid, u8 *ra) +{ + int tid; + u8 ret = false; + struct mwifiex_tx_ba_stream_tbl *tx_tbl; + unsigned long flags; + + tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (tid > priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user) { + tid = priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user; + *ptid = tx_tbl->tid; + memcpy(ra, tx_tbl->ra, ETH_ALEN); + ret = true; + } + } + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + + return ret; +} + +/* + * This function checks whether associated station is 11n enabled + */ +static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv, + struct mwifiex_sta_node *node) +{ + + if (!node || (priv->bss_role != MWIFIEX_BSS_ROLE_UAP) || + !priv->ap_11n_enabled) + return 0; + + return node->is_11n_enabled; +} + +static inline u8 +mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, const u8 *ra) +{ + struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra); + if (node) + return node->is_11n_enabled; + + return false; +} +#endif /* !_MWIFIEX_11N_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c new file mode 100644 index 000000000000..aa498e0d2204 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c @@ -0,0 +1,318 @@ +/* + * Marvell Wireless LAN device driver: 802.11n Aggregation + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11n_aggr.h" + +/* + * Creates an AMSDU subframe for aggregation into one AMSDU packet. + * + * The resultant AMSDU subframe format is - + * + * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ + * | DA | SA | Length | SNAP header | MSDU | + * | data[0..5] | data[6..11] | | | data[14..] | + * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ + * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes--> + * + * This function also computes the amount of padding required to make the + * buffer length multiple of 4 bytes. + * + * Data => |DA|SA|SNAP-TYPE|........ .| + * MSDU => |DA|SA|Length|SNAP|...... ..| + */ +static int +mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr, + struct sk_buff *skb_src, int *pad) + +{ + int dt_offset; + struct rfc_1042_hdr snap = { + 0xaa, /* LLC DSAP */ + 0xaa, /* LLC SSAP */ + 0x03, /* LLC CTRL */ + {0x00, 0x00, 0x00}, /* SNAP OUI */ + 0x0000 /* SNAP type */ + /* + * This field will be overwritten + * later with ethertype + */ + }; + struct tx_packet_hdr *tx_header; + + tx_header = (void *)skb_put(skb_aggr, sizeof(*tx_header)); + + /* Copy DA and SA */ + dt_offset = 2 * ETH_ALEN; + memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); + + /* Copy SNAP header */ + snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto; + + dt_offset += sizeof(__be16); + + memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr)); + + skb_pull(skb_src, dt_offset); + + /* Update Length field */ + tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN); + + /* Add payload */ + memcpy(skb_put(skb_aggr, skb_src->len), skb_src->data, skb_src->len); + + /* Add padding for new MSDU to start from 4 byte boundary */ + *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4; + + return skb_aggr->len + *pad; +} + +/* + * Adds TxPD to AMSDU header. + * + * Each AMSDU packet will contain one TxPD at the beginning, + * followed by multiple AMSDU subframes. + */ +static void +mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct txpd *local_tx_pd; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + unsigned int pad; + int headroom = (priv->adapter->iface_type == + MWIFIEX_USB) ? 0 : INTF_HEADER_LEN; + + pad = ((void *)skb->data - sizeof(*local_tx_pd) - + headroom - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); + skb_push(skb, pad); + + skb_push(skb, sizeof(*local_tx_pd)); + + local_tx_pd = (struct txpd *) skb->data; + memset(local_tx_pd, 0, sizeof(struct txpd)); + + /* Original priority has been overwritten */ + local_tx_pd->priority = (u8) skb->priority; + local_tx_pd->pkt_delay_2ms = + mwifiex_wmm_compute_drv_pkt_delay(priv, skb); + local_tx_pd->bss_num = priv->bss_num; + local_tx_pd->bss_type = priv->bss_type; + /* Always zero as the data is followed by struct txpd */ + local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) + + pad); + local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU); + local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - + sizeof(*local_tx_pd) - + pad); + + if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; + + if (local_tx_pd->tx_control == 0) + /* TxCtrl set by user or default */ + local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && + priv->adapter->pps_uapsd_mode) { + if (true == mwifiex_check_last_packet_indication(priv)) { + priv->adapter->tx_lock_flag = true; + local_tx_pd->flags = + MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; + } + } +} + +/* + * Create aggregated packet. + * + * This function creates an aggregated MSDU packet, by combining buffers + * from the RA list. Each individual buffer is encapsulated as an AMSDU + * subframe and all such subframes are concatenated together to form the + * AMSDU packet. + * + * A TxPD is also added to the front of the resultant AMSDU packets for + * transmission. The resultant packets format is - + * + * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+ + * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame| + * | | 1 | 2 | .. | n | + * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+ + */ +int +mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *pra_list, + int ptrindex, unsigned long ra_list_flags) + __releases(&priv->wmm.ra_list_spinlock) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct sk_buff *skb_aggr, *skb_src; + struct mwifiex_txinfo *tx_info_aggr, *tx_info_src; + int pad = 0, aggr_num = 0, ret; + struct mwifiex_tx_param tx_param; + struct txpd *ptx_pd = NULL; + int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN; + + skb_src = skb_peek(&pra_list->skb_head); + if (!skb_src) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + return 0; + } + + tx_info_src = MWIFIEX_SKB_TXCB(skb_src); + skb_aggr = mwifiex_alloc_dma_align_buf(adapter->tx_buf_size, + GFP_ATOMIC | GFP_DMA); + if (!skb_aggr) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + return -1; + } + skb_reserve(skb_aggr, MWIFIEX_MIN_DATA_HEADER_LEN); + tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); + + memset(tx_info_aggr, 0, sizeof(*tx_info_aggr)); + tx_info_aggr->bss_type = tx_info_src->bss_type; + tx_info_aggr->bss_num = tx_info_src->bss_num; + + if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT; + skb_aggr->priority = skb_src->priority; + skb_aggr->tstamp = skb_src->tstamp; + + skb_aggr->tstamp = ktime_get_real(); + + do { + /* Check if AMSDU can accommodate this MSDU */ + if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN)) + break; + + skb_src = skb_dequeue(&pra_list->skb_head); + pra_list->total_pkt_count--; + atomic_dec(&priv->wmm.tx_pkts_queued); + aggr_num++; + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad); + + mwifiex_write_data_complete(adapter, skb_src, 0, 0); + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + + if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + return -1; + } + + if (skb_tailroom(skb_aggr) < pad) { + pad = 0; + break; + } + skb_put(skb_aggr, pad); + + skb_src = skb_peek(&pra_list->skb_head); + + } while (skb_src); + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); + + /* Last AMSDU packet does not need padding */ + skb_trim(skb_aggr, skb_aggr->len - pad); + + /* Form AMSDU */ + mwifiex_11n_form_amsdu_txpd(priv, skb_aggr); + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) + ptx_pd = (struct txpd *)skb_aggr->data; + + skb_push(skb_aggr, headroom); + tx_info_aggr->aggr_num = aggr_num * 2; + if (adapter->data_sent || adapter->tx_lock_flag) { + atomic_add(aggr_num * 2, &adapter->tx_queued); + skb_queue_tail(&adapter->tx_data_q, skb_aggr); + return 0; + } + + if (adapter->iface_type == MWIFIEX_USB) { + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, + skb_aggr, NULL); + } else { + if (skb_src) + tx_param.next_pkt_len = + skb_src->len + sizeof(struct txpd); + else + tx_param.next_pkt_len = 0; + + ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, + skb_aggr, &tx_param); + } + switch (ret) { + case -EBUSY: + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_write_data_complete(adapter, skb_aggr, 1, -1); + return -1; + } + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && + adapter->pps_uapsd_mode && adapter->tx_lock_flag) { + priv->adapter->tx_lock_flag = false; + if (ptx_pd) + ptx_pd->flags = 0; + } + + skb_queue_tail(&pra_list->skb_head, skb_aggr); + + pra_list->total_pkt_count++; + + atomic_inc(&priv->wmm.tx_pkts_queued); + + tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + break; + case -1: + mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n", + __func__, ret); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); + return 0; + case -EINPROGRESS: + break; + case 0: + mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); + break; + default: + break; + } + if (ret != -EBUSY) { + mwifiex_rotate_priolists(priv, pra_list, ptrindex); + } + + return 0; +} diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.h b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h new file mode 100644 index 000000000000..0cd2a3eb6c17 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h @@ -0,0 +1,33 @@ +/* + * Marvell Wireless LAN device driver: 802.11n Aggregation + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_11N_AGGR_H_ +#define _MWIFIEX_11N_AGGR_H_ + +#define PKT_TYPE_AMSDU 0xE6 +#define MIN_NUM_AMSDU 2 + +int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv, + struct sk_buff *skb); +int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, + int ptr_index, unsigned long flags) + __releases(&priv->wmm.ra_list_spinlock); + +#endif /* !_MWIFIEX_11N_AGGR_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c new file mode 100644 index 000000000000..b3970a8c9e48 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c @@ -0,0 +1,910 @@ +/* + * Marvell Wireless LAN device driver: 802.11n RX Re-ordering + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11n_rxreorder.h" + +/* This function will dispatch amsdu packet and forward it to kernel/upper + * layer. + */ +static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct rxpd *local_rx_pd = (struct rxpd *)(skb->data); + int ret; + + if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) { + struct sk_buff_head list; + struct sk_buff *rx_skb; + + __skb_queue_head_init(&list); + + skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset)); + skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length)); + + ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, + priv->wdev.iftype, 0, false); + + while (!skb_queue_empty(&list)) { + rx_skb = __skb_dequeue(&list); + ret = mwifiex_recv_packet(priv, rx_skb); + if (ret == -1) + mwifiex_dbg(priv->adapter, ERROR, + "Rx of A-MSDU failed"); + } + return 0; + } + + return -1; +} + +/* This function will process the rx packet and forward it to kernel/upper + * layer. + */ +static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload) +{ + int ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload); + + if (!ret) + return 0; + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) + return mwifiex_handle_uap_rx_forward(priv, payload); + + return mwifiex_process_rx_packet(priv, payload); +} + +/* + * This function dispatches all packets in the Rx reorder table until the + * start window. + * + * There could be holes in the buffer, which are skipped by the function. + * Since the buffer is linear, the function uses rotation to simulate + * circular buffer. + */ +static void +mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, + struct mwifiex_rx_reorder_tbl *tbl, + int start_win) +{ + int pkt_to_send, i; + void *rx_tmp_ptr; + unsigned long flags; + + pkt_to_send = (start_win > tbl->start_win) ? + min((start_win - tbl->start_win), tbl->win_size) : + tbl->win_size; + + for (i = 0; i < pkt_to_send; ++i) { + spin_lock_irqsave(&priv->rx_pkt_lock, flags); + rx_tmp_ptr = NULL; + if (tbl->rx_reorder_ptr[i]) { + rx_tmp_ptr = tbl->rx_reorder_ptr[i]; + tbl->rx_reorder_ptr[i] = NULL; + } + spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); + if (rx_tmp_ptr) + mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); + } + + spin_lock_irqsave(&priv->rx_pkt_lock, flags); + /* + * We don't have a circular buffer, hence use rotation to simulate + * circular buffer + */ + for (i = 0; i < tbl->win_size - pkt_to_send; ++i) { + tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i]; + tbl->rx_reorder_ptr[pkt_to_send + i] = NULL; + } + + tbl->start_win = start_win; + spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); +} + +/* + * This function dispatches all packets in the Rx reorder table until + * a hole is found. + * + * The start window is adjusted automatically when a hole is located. + * Since the buffer is linear, the function uses rotation to simulate + * circular buffer. + */ +static void +mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, + struct mwifiex_rx_reorder_tbl *tbl) +{ + int i, j, xchg; + void *rx_tmp_ptr; + unsigned long flags; + + for (i = 0; i < tbl->win_size; ++i) { + spin_lock_irqsave(&priv->rx_pkt_lock, flags); + if (!tbl->rx_reorder_ptr[i]) { + spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); + break; + } + rx_tmp_ptr = tbl->rx_reorder_ptr[i]; + tbl->rx_reorder_ptr[i] = NULL; + spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); + mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); + } + + spin_lock_irqsave(&priv->rx_pkt_lock, flags); + /* + * We don't have a circular buffer, hence use rotation to simulate + * circular buffer + */ + if (i > 0) { + xchg = tbl->win_size - i; + for (j = 0; j < xchg; ++j) { + tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j]; + tbl->rx_reorder_ptr[i + j] = NULL; + } + } + tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1); + spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); +} + +/* + * This function deletes the Rx reorder table and frees the memory. + * + * The function stops the associated timer and dispatches all the + * pending packets in the Rx reorder table before deletion. + */ +static void +mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, + struct mwifiex_rx_reorder_tbl *tbl) +{ + unsigned long flags; + int start_win; + + if (!tbl) + return; + + spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); + priv->adapter->rx_locked = true; + if (priv->adapter->rx_processing) { + spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); + flush_workqueue(priv->adapter->rx_workqueue); + } else { + spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); + } + + start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); + + del_timer_sync(&tbl->timer_context.timer); + tbl->timer_context.timer_is_set = false; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_del(&tbl->list); + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + kfree(tbl->rx_reorder_ptr); + kfree(tbl); + + spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); + priv->adapter->rx_locked = false; + spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); + +} + +/* + * This function returns the pointer to an entry in Rx reordering + * table which matches the given TA/TID pair. + */ +struct mwifiex_rx_reorder_tbl * +mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) +{ + struct mwifiex_rx_reorder_tbl *tbl; + unsigned long flags; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) { + if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, + flags); + return tbl; + } + } + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + return NULL; +} + +/* This function retrieves the pointer to an entry in Rx reordering + * table which matches the given TA and deletes it. + */ +void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta) +{ + struct mwifiex_rx_reorder_tbl *tbl, *tmp; + unsigned long flags; + + if (!ta) + return; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) { + if (!memcmp(tbl->ta, ta, ETH_ALEN)) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, + flags); + mwifiex_del_rx_reorder_entry(priv, tbl); + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + } + } + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + return; +} + +/* + * This function finds the last sequence number used in the packets + * buffered in Rx reordering table. + */ +static int +mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx) +{ + struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr; + struct mwifiex_private *priv = ctx->priv; + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, + flags); + return i; + } + } + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + return -1; +} + +/* + * This function flushes all the packets in Rx reordering table. + * + * The function checks if any packets are currently buffered in the + * table or not. In case there are packets available, it dispatches + * them and then dumps the Rx reordering table. + */ +static void +mwifiex_flush_data(unsigned long context) +{ + struct reorder_tmr_cnxt *ctx = + (struct reorder_tmr_cnxt *) context; + int start_win, seq_num; + + ctx->timer_is_set = false; + seq_num = mwifiex_11n_find_last_seq_num(ctx); + + if (seq_num < 0) + return; + + mwifiex_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num); + start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1); + mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr, + start_win); +} + +/* + * This function creates an entry in Rx reordering table for the + * given TA/TID. + * + * The function also initializes the entry with sequence number, window + * size as well as initializes the timer. + * + * If the received TA/TID pair is already present, all the packets are + * dispatched and the window size is moved until the SSN. + */ +static void +mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, + int tid, int win_size, int seq_num) +{ + int i; + struct mwifiex_rx_reorder_tbl *tbl, *new_node; + u16 last_seq = 0; + unsigned long flags; + struct mwifiex_sta_node *node; + + /* + * If we get a TID, ta pair which is already present dispatch all the + * the packets and move the window size until the ssn + */ + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); + if (tbl) { + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num); + return; + } + /* if !tbl then create one */ + new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL); + if (!new_node) + return; + + INIT_LIST_HEAD(&new_node->list); + new_node->tid = tid; + memcpy(new_node->ta, ta, ETH_ALEN); + new_node->start_win = seq_num; + new_node->init_win = seq_num; + new_node->flags = 0; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + if (mwifiex_queuing_ra_based(priv)) { + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { + node = mwifiex_get_sta_entry(priv, ta); + if (node) + last_seq = node->rx_seq[tid]; + } + } else { + node = mwifiex_get_sta_entry(priv, ta); + if (node) + last_seq = node->rx_seq[tid]; + else + last_seq = priv->rx_seq[tid]; + } + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + mwifiex_dbg(priv->adapter, INFO, + "info: last_seq=%d start_win=%d\n", + last_seq, new_node->start_win); + + if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && + last_seq >= new_node->start_win) { + new_node->start_win = last_seq + 1; + new_node->flags |= RXREOR_INIT_WINDOW_SHIFT; + } + + new_node->win_size = win_size; + + new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size, + GFP_KERNEL); + if (!new_node->rx_reorder_ptr) { + kfree((u8 *) new_node); + mwifiex_dbg(priv->adapter, ERROR, + "%s: failed to alloc reorder_ptr\n", __func__); + return; + } + + new_node->timer_context.ptr = new_node; + new_node->timer_context.priv = priv; + new_node->timer_context.timer_is_set = false; + + setup_timer(&new_node->timer_context.timer, mwifiex_flush_data, + (unsigned long)&new_node->timer_context); + + for (i = 0; i < win_size; ++i) + new_node->rx_reorder_ptr[i] = NULL; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); +} + +static void +mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl) +{ + u32 min_flush_time; + + if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32) + min_flush_time = MIN_FLUSH_TIMER_15_MS; + else + min_flush_time = MIN_FLUSH_TIMER_MS; + + mod_timer(&tbl->timer_context.timer, + jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size)); + + tbl->timer_context.timer_is_set = true; +} + +/* + * This function prepares command for adding a BA request. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting add BA request buffer + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf) +{ + struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req; + + cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); + cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); + memcpy(add_ba_req, data_buf, sizeof(*add_ba_req)); + + return 0; +} + +/* + * This function prepares command for adding a BA response. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting add BA response buffer + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct host_cmd_ds_11n_addba_req + *cmd_addba_req) +{ + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; + struct mwifiex_sta_node *sta_ptr; + u32 rx_win_size = priv->add_ba_param.rx_win_size; + u8 tid; + int win_size; + unsigned long flags; + uint16_t block_ack_param_set; + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->is_hw_11ac_capable && + memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) { + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, + cmd_addba_req->peer_mac_addr); + if (!sta_ptr) { + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + mwifiex_dbg(priv->adapter, ERROR, + "BA setup with unknown TDLS peer %pM!\n", + cmd_addba_req->peer_mac_addr); + return -1; + } + if (sta_ptr->is_11ac_enabled) + rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + } + + cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); + cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); + + memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr, + ETH_ALEN); + add_ba_rsp->dialog_token = cmd_addba_req->dialog_token; + add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo; + add_ba_rsp->ssn = cmd_addba_req->ssn; + + block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set); + tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); + block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; + + /* If we don't support AMSDU inside AMPDU, reset the bit */ + if (!priv->add_ba_param.rx_amsdu || + (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)) + block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; + block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS; + add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); + win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) + & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) + >> BLOCKACKPARAM_WINSIZE_POS; + cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set); + + mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr, + tid, win_size, + le16_to_cpu(cmd_addba_req->ssn)); + return 0; +} + +/* + * This function prepares command for deleting a BA request. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting del BA request buffer + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) +{ + struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba; + + cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA); + cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); + memcpy(del_ba, data_buf, sizeof(*del_ba)); + + return 0; +} + +/* + * This function identifies if Rx reordering is needed for a received packet. + * + * In case reordering is required, the function will do the reordering + * before sending it to kernel. + * + * The Rx reorder table is checked first with the received TID/TA pair. If + * not found, the received packet is dispatched immediately. But if found, + * the packet is reordered and all the packets in the updated Rx reordering + * table is dispatched until a hole is found. + * + * For sequence number less than the starting window, the packet is dropped. + */ +int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, + u16 seq_num, u16 tid, + u8 *ta, u8 pkt_type, void *payload) +{ + struct mwifiex_rx_reorder_tbl *tbl; + int prev_start_win, start_win, end_win, win_size; + u16 pkt_index; + bool init_window_shift = false; + int ret = 0; + + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); + if (!tbl) { + if (pkt_type != PKT_TYPE_BAR) + mwifiex_11n_dispatch_pkt(priv, payload); + return ret; + } + + if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) { + mwifiex_11n_dispatch_pkt(priv, payload); + return ret; + } + + start_win = tbl->start_win; + prev_start_win = start_win; + win_size = tbl->win_size; + end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); + if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) { + init_window_shift = true; + tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT; + } + + if (tbl->flags & RXREOR_FORCE_NO_DROP) { + mwifiex_dbg(priv->adapter, INFO, + "RXREOR_FORCE_NO_DROP when HS is activated\n"); + tbl->flags &= ~RXREOR_FORCE_NO_DROP; + } else if (init_window_shift && seq_num < start_win && + seq_num >= tbl->init_win) { + mwifiex_dbg(priv->adapter, INFO, + "Sender TID sequence number reset %d->%d for SSN %d\n", + start_win, seq_num, tbl->init_win); + tbl->start_win = start_win = seq_num; + end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); + } else { + /* + * If seq_num is less then starting win then ignore and drop + * the packet + */ + if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { + if (seq_num >= ((start_win + TWOPOW11) & + (MAX_TID_VALUE - 1)) && + seq_num < start_win) { + ret = -1; + goto done; + } + } else if ((seq_num < start_win) || + (seq_num >= (start_win + TWOPOW11))) { + ret = -1; + goto done; + } + } + + /* + * If this packet is a BAR we adjust seq_num as + * WinStart = seq_num + */ + if (pkt_type == PKT_TYPE_BAR) + seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1); + + if (((end_win < start_win) && + (seq_num < start_win) && (seq_num > end_win)) || + ((end_win > start_win) && ((seq_num > end_win) || + (seq_num < start_win)))) { + end_win = seq_num; + if (((end_win - win_size) + 1) >= 0) + start_win = (end_win - win_size) + 1; + else + start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1; + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); + } + + if (pkt_type != PKT_TYPE_BAR) { + if (seq_num >= start_win) + pkt_index = seq_num - start_win; + else + pkt_index = (seq_num+MAX_TID_VALUE) - start_win; + + if (tbl->rx_reorder_ptr[pkt_index]) { + ret = -1; + goto done; + } + + tbl->rx_reorder_ptr[pkt_index] = payload; + } + + /* + * Dispatch all packets sequentially from start_win until a + * hole is found and adjust the start_win appropriately + */ + mwifiex_11n_scan_and_dispatch(priv, tbl); + +done: + if (!tbl->timer_context.timer_is_set || + prev_start_win != tbl->start_win) + mwifiex_11n_rxreorder_timer_restart(tbl); + return ret; +} + +/* + * This function deletes an entry for a given TID/TA pair. + * + * The TID/TA are taken from del BA event body. + */ +void +mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, + u8 type, int initiator) +{ + struct mwifiex_rx_reorder_tbl *tbl; + struct mwifiex_tx_ba_stream_tbl *ptx_tbl; + struct mwifiex_ra_list_tbl *ra_list; + u8 cleanup_rx_reorder_tbl; + unsigned long flags; + int tid_down; + + if (type == TYPE_DELBA_RECEIVE) + cleanup_rx_reorder_tbl = (initiator) ? true : false; + else + cleanup_rx_reorder_tbl = (initiator) ? false : true; + + mwifiex_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n", + peer_mac, tid, initiator); + + if (cleanup_rx_reorder_tbl) { + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, + peer_mac); + if (!tbl) { + mwifiex_dbg(priv->adapter, EVENT, + "event: TID, TA not found in table\n"); + return; + } + mwifiex_del_rx_reorder_entry(priv, tbl); + } else { + ptx_tbl = mwifiex_get_ba_tbl(priv, tid, peer_mac); + if (!ptx_tbl) { + mwifiex_dbg(priv->adapter, EVENT, + "event: TID, RA not found in table\n"); + return; + } + + tid_down = mwifiex_wmm_downgrade_tid(priv, tid); + ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, peer_mac); + if (ra_list) { + ra_list->amsdu_in_ampdu = false; + ra_list->ba_status = BA_SETUP_NONE; + } + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + } +} + +/* + * This function handles the command response of an add BA response. + * + * Handling includes changing the header fields into CPU format and + * creating the stream, provided the add BA is accepted. + */ +int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; + int tid, win_size; + struct mwifiex_rx_reorder_tbl *tbl; + uint16_t block_ack_param_set; + + block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); + + tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + /* + * Check if we had rejected the ADDBA, if yes then do not create + * the stream + */ + if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { + mwifiex_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n", + add_ba_rsp->peer_mac_addr, tid); + + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, + add_ba_rsp->peer_mac_addr); + if (tbl) + mwifiex_del_rx_reorder_entry(priv, tbl); + + return 0; + } + + win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) + >> BLOCKACKPARAM_WINSIZE_POS; + + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, + add_ba_rsp->peer_mac_addr); + if (tbl) { + if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.rx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + tbl->amsdu = true; + else + tbl->amsdu = false; + } + + mwifiex_dbg(priv->adapter, CMD, + "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", + add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size); + + return 0; +} + +/* + * This function handles BA stream timeout event by preparing and sending + * a command to the firmware. + */ +void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, + struct host_cmd_ds_11n_batimeout *event) +{ + struct host_cmd_ds_11n_delba delba; + + memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba)); + memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN); + + delba.del_ba_param_set |= + cpu_to_le16((u16) event->tid << DELBA_TID_POS); + delba.del_ba_param_set |= cpu_to_le16( + (u16) event->origninator << DELBA_INITIATOR_POS); + delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); + mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false); +} + +/* + * This function cleans up the Rx reorder table by deleting all the entries + * and re-initializing. + */ +void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) +{ + struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node; + unsigned long flags; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_for_each_entry_safe(del_tbl_ptr, tmp_node, + &priv->rx_reorder_tbl_ptr, list) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr); + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + } + INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + mwifiex_reset_11n_rx_seq_num(priv); +} + +/* + * This function updates all rx_reorder_tbl's flags. + */ +void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags) +{ + struct mwifiex_private *priv; + struct mwifiex_rx_reorder_tbl *tbl; + unsigned long lock_flags; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags); + if (list_empty(&priv->rx_reorder_tbl_ptr)) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, + lock_flags); + continue; + } + + list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) + tbl->flags = flags; + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags); + } + + return; +} + +/* This function update all the rx_win_size based on coex flag + */ +static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter, + bool coex_flag) +{ + u8 i; + u32 rx_win_size; + struct mwifiex_private *priv; + + dev_dbg(adapter->dev, "Update rxwinsize %d\n", coex_flag); + + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i]) + continue; + priv = adapter->priv[i]; + rx_win_size = priv->add_ba_param.rx_win_size; + if (coex_flag) { + if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) + priv->add_ba_param.rx_win_size = + MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) + priv->add_ba_param.rx_win_size = + MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) + priv->add_ba_param.rx_win_size = + MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE; + } else { + if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) + priv->add_ba_param.rx_win_size = + MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) + priv->add_ba_param.rx_win_size = + MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) + priv->add_ba_param.rx_win_size = + MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE; + } + + if (adapter->coex_win_size && adapter->coex_rx_win_size) + priv->add_ba_param.rx_win_size = + adapter->coex_rx_win_size; + + if (rx_win_size != priv->add_ba_param.rx_win_size) { + if (!priv->media_connected) + continue; + for (i = 0; i < MAX_NUM_TID; i++) + mwifiex_11n_delba(priv, i); + } + } +} + +/* This function check coex for RX BA + */ +void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter) +{ + u8 i; + struct mwifiex_private *priv; + u8 count = 0; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + priv = adapter->priv[i]; + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { + if (priv->media_connected) + count++; + } + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + if (priv->bss_started) + count++; + } + } + if (count >= MWIFIEX_BSS_COEX_COUNT) + break; + } + if (count >= MWIFIEX_BSS_COEX_COUNT) + mwifiex_update_ampdu_rxwinsize(adapter, true); + else + mwifiex_update_ampdu_rxwinsize(adapter, false); +} diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h new file mode 100644 index 000000000000..63ecea89b4ab --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h @@ -0,0 +1,85 @@ +/* + * Marvell Wireless LAN device driver: 802.11n RX Re-ordering + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_11N_RXREORDER_H_ +#define _MWIFIEX_11N_RXREORDER_H_ + +#define MIN_FLUSH_TIMER_MS 50 +#define MIN_FLUSH_TIMER_15_MS 15 +#define MWIFIEX_BA_WIN_SIZE_32 32 + +#define PKT_TYPE_BAR 0xE7 +#define MAX_TID_VALUE (2 << 11) +#define TWOPOW11 (2 << 10) + +#define BLOCKACKPARAM_TID_POS 2 +#define BLOCKACKPARAM_AMSDU_SUPP_MASK 0x1 +#define BLOCKACKPARAM_WINSIZE_POS 6 +#define DELBA_TID_POS 12 +#define DELBA_INITIATOR_POS 11 +#define TYPE_DELBA_SENT 1 +#define TYPE_DELBA_RECEIVE 2 +#define IMMEDIATE_BLOCK_ACK 0x2 + +#define ADDBA_RSP_STATUS_ACCEPT 0 + +#define MWIFIEX_DEF_11N_RX_SEQ_NUM 0xffff +#define BA_SETUP_MAX_PACKET_THRESHOLD 16 +#define BA_SETUP_PACKET_OFFSET 16 + +enum mwifiex_rxreor_flags { + RXREOR_FORCE_NO_DROP = 1<<0, + RXREOR_INIT_WINDOW_SHIFT = 1<<1, +}; + +static inline void mwifiex_reset_11n_rx_seq_num(struct mwifiex_private *priv) +{ + memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq)); +} + +int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *, + u16 seqNum, + u16 tid, u8 *ta, + u8 pkttype, void *payload); +void mwifiex_del_ba_tbl(struct mwifiex_private *priv, int Tid, + u8 *PeerMACAddr, u8 type, int initiator); +void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, + struct host_cmd_ds_11n_batimeout *event); +int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, + struct host_cmd_ds_command + *resp); +int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, + void *data_buf); +int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct host_cmd_ds_11n_addba_req + *cmd_addba_req); +int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, + void *data_buf); +void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv); +struct mwifiex_rx_reorder_tbl *mwifiex_11n_get_rxreorder_tbl(struct + mwifiex_private + *priv, int tid, + u8 *ta); +struct mwifiex_rx_reorder_tbl * +mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta); +void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta); +void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags); + +#endif /* _MWIFIEX_11N_RXREORDER_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/Kconfig b/drivers/net/wireless/marvell/mwifiex/Kconfig new file mode 100644 index 000000000000..279167ddd293 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/Kconfig @@ -0,0 +1,44 @@ +config MWIFIEX + tristate "Marvell WiFi-Ex Driver" + depends on CFG80211 + ---help--- + This adds support for wireless adapters based on Marvell + 802.11n/ac chipsets. + + If you choose to build it as a module, it will be called + mwifiex. + +config MWIFIEX_SDIO + tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897/SD8997" + depends on MWIFIEX && MMC + select FW_LOADER + select WANT_DEV_COREDUMP + ---help--- + This adds support for wireless adapters based on Marvell + 8786/8787/8797/8887/8897/8997 chipsets with SDIO interface. + + If you choose to build it as a module, it will be called + mwifiex_sdio. + +config MWIFIEX_PCIE + tristate "Marvell WiFi-Ex Driver for PCIE 8766/8897/8997" + depends on MWIFIEX && PCI + select FW_LOADER + select WANT_DEV_COREDUMP + ---help--- + This adds support for wireless adapters based on Marvell + 8766/8897/8997 chipsets with PCIe interface. + + If you choose to build it as a module, it will be called + mwifiex_pcie. + +config MWIFIEX_USB + tristate "Marvell WiFi-Ex Driver for USB8766/8797/8997" + depends on MWIFIEX && USB + select FW_LOADER + ---help--- + This adds support for wireless adapters based on Marvell + 8797/8997 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/marvell/mwifiex/Makefile b/drivers/net/wireless/marvell/mwifiex/Makefile new file mode 100644 index 000000000000..fdfd9bf15ed4 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/Makefile @@ -0,0 +1,57 @@ +# +# Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + + +mwifiex-y += main.o +mwifiex-y += init.o +mwifiex-y += cfp.o +mwifiex-y += cmdevt.o +mwifiex-y += util.o +mwifiex-y += txrx.o +mwifiex-y += wmm.o +mwifiex-y += 11n.o +mwifiex-y += 11ac.o +mwifiex-y += 11n_aggr.o +mwifiex-y += 11n_rxreorder.o +mwifiex-y += scan.o +mwifiex-y += join.o +mwifiex-y += sta_ioctl.o +mwifiex-y += sta_cmd.o +mwifiex-y += uap_cmd.o +mwifiex-y += ie.o +mwifiex-y += sta_cmdresp.o +mwifiex-y += sta_event.o +mwifiex-y += uap_event.o +mwifiex-y += sta_tx.o +mwifiex-y += sta_rx.o +mwifiex-y += uap_txrx.o +mwifiex-y += cfg80211.o +mwifiex-y += ethtool.o +mwifiex-y += 11h.o +mwifiex-y += tdls.o +mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o +obj-$(CONFIG_MWIFIEX) += mwifiex.o + +mwifiex_sdio-y += sdio.o +obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o + +mwifiex_pcie-y += pcie.o +obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o + +mwifiex_usb-y += usb.o +obj-$(CONFIG_MWIFIEX_USB) += mwifiex_usb.o + +ccflags-y += -D__CHECK_ENDIAN diff --git a/drivers/net/wireless/marvell/mwifiex/README b/drivers/net/wireless/marvell/mwifiex/README new file mode 100644 index 000000000000..2f0f9b5609d0 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/README @@ -0,0 +1,240 @@ +# Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + + +=============================================================================== + U S E R M A N U A L + +1) FOR DRIVER INSTALL + + a) Copy sd8787.bin to /lib/firmware/mrvl/ directory, + create the directory if it doesn't exist. + b) Install WLAN driver, + insmod mwifiex.ko + c) Uninstall WLAN driver, + ifconfig mlanX down + rmmod mwifiex + + +2) FOR DRIVER CONFIGURATION AND INFO + The configurations can be done either using the 'iw' user space + utility or debugfs. + + a) 'iw' utility commands + + Following are some useful iw commands:- + +iw dev mlan0 scan + + This command will trigger a scan. + The command will then display the scan table entries + +iw dev mlan0 connect -w [] [] [key 0:abcde d:1123456789a] + The above command can be used to connect to an AP with a particular SSID. + Ap's operating frequency can be specified or even the bssid. If the AP is using + WEP encryption, wep keys can be specified in the command. + Note: Every time before connecting to an AP scan command (iw dev mlan0 scan) should be used by user. + +iw dev mlan0 disconnect + This command will be used to disconnect from an AP. + + +iw dev mlan0 ibss join [fixed-freq] [fixed-bssid] [key 0:abcde] + The command will be used to join or create an ibss. Optionally, operating frequency, + bssid and the security related parameters can be specified while joining/creating + and ibss. + +iw dev mlan0 ibss leave + The command will be used to leave an ibss network. + +iw dev mlan0 link + The command will be used to get the connection status. The command will return parameters + such as SSID, operating frequency, rx/tx packets, signal strength, tx bitrate. + + Apart from the iw utility all standard configurations using the 'iwconfig' utility are also supported. + + b) Debugfs interface + + The debugfs interface can be used for configurations and for getting + some useful information from the driver. + The section below explains the configurations that can be + done. + + Mount debugfs to /debugfs mount point: + + mkdir /debugfs + mount -t debugfs debugfs /debugfs + + The information is provided in /debugfs/mwifiex/mlanX/: + +iw reg set + The command will be used to change the regulatory domain. + +iw reg get + The command will be used to get current regulatory domain. + +info + This command is used to get driver info. + + Usage: + cat info + + driver_name = "mwifiex" + driver_version = + interface_name = "mlanX" + bss_mode = "Ad-hoc" | "Managed" | "Auto" | "Unknown" + media_state = "Disconnected" | "Connected" + mac_address = <6-byte adapter MAC address> + multicase_count = + essid = + bssid = + channel = + region_code = + multicasr_address[n] = + num_tx_bytes = + num_rx_bytes = + num_tx_pkts = + num_rx_pkts = + num_tx_pkts_dropped = + num_rx_pkts_dropped = + num_tx_pkts_err = + num_rx_pkts_err = + carrier "on" | "off" + tx queue "stopped" | "started" + + The following debug info are provided in /debugfs/mwifiex/mlanX/debug: + + int_counter = + wmm_ac_vo = + wmm_ac_vi = + wmm_ac_be = + wmm_ac_bk = + tx_buf_size = + curr_tx_buf_size = + ps_mode = <0/1, CAM mode/PS mode> + ps_state = <0/1/2/3, full power state/awake state/pre-sleep state/sleep state> + is_deep_sleep = <0/1, not deep sleep state/deep sleep state> + wakeup_dev_req = <0/1, wakeup device not required/required> + wakeup_tries = + hs_configured = <0/1, host sleep not configured/configured> + hs_activated = <0/1, extended host sleep not activated/activated> + num_tx_timeout = + is_cmd_timedout = <0/1 command timeout not occurred/occurred> + timeout_cmd_id = + timeout_cmd_act = + last_cmd_id = + last_cmd_act = + last_cmd_index = <0 based last command index> + last_cmd_resp_id = + last_cmd_resp_index = <0 based last command response index> + last_event = + last_event_index = <0 based last event index> + num_cmd_h2c_fail = + num_cmd_sleep_cfm_fail = + num_tx_h2c_fail = + num_evt_deauth = + num_evt_disassoc = + num_evt_link_lost = + num_cmd_deauth = + num_cmd_assoc_ok = + num_cmd_assoc_fail = + cmd_sent = <0/1, send command resources available/sending command to device> + data_sent = <0/1, send data resources available/sending data to device> + mp_rd_bitmap = + mp_wr_bitmap = + cmd_resp_received = <0/1, no cmd response to process/response received and yet to process> + event_received = <0/1, no event to process/event received and yet to process> + cmd_pending = + tx_pending = + rx_pending = + + +3) FOR DRIVER CONFIGURATION + +regrdwr + This command is used to read/write the adapter register. + + Usage: + echo " [value]" > regrdwr + cat regrdwr + + where the parameters are, + : 1:MAC/SOC, 2:BBP, 3:RF, 4:PMIC, 5:CAU + : offset of register + [value]: value to be written + + Examples: + echo "1 0xa060" > regrdwr : Read the MAC register + echo "1 0xa060 0x12" > regrdwr : Write the MAC register + echo "1 0xa794 0x80000000" > regrdwr + : Write 0x80000000 to MAC register +rdeeprom + This command is used to read the EEPROM contents of the card. + + Usage: + echo " " > rdeeprom + cat rdeeprom + + where the parameters are, + : multiples of 4 + : 4-20, multiples of 4 + + Example: + echo "0 20" > rdeeprom : Read 20 bytes of EEPROM data from offset 0 + +hscfg + This command is used to debug/simulate host sleep feature using + different configuration parameters. + + Usage: + echo " [GPIO# [gap]]]" > hscfg + cat hscfg + + where the parameters are, + : bit 0 = 1 -- broadcast data + bit 1 = 1 -- unicast data + bit 2 = 1 -- mac event + bit 3 = 1 -- multicast data + [GPIO#]: pin number of GPIO used to wakeup the host. + GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO + will be used instead). + [gap]: the gap in milliseconds between wakeup signal and + wakeup event or 0xff for special setting (host + acknowledge required) when GPIO is used to wakeup host. + + Examples: + echo "-1" > hscfg : Cancel host sleep mode + echo "3" > hscfg : Broadcast and unicast data; + Use GPIO and gap set previously + echo "2 3" > hscfg : Unicast data and GPIO 3; + Use gap set previously + echo "2 1 160" > hscfg : Unicast data, GPIO 1 and gap 160 ms + echo "2 1 0xff" > hscfg : Unicast data, GPIO 1; Wait for host + to ack before sending wakeup event + +getlog + This command is used to get the statistics available in the station. + Usage: + + cat getlog + +device_dump + This command is used to dump driver information and firmware memory + segments. + Usage: + + cat fw_dump + +=============================================================================== diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c new file mode 100644 index 000000000000..4073116e6e9f --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -0,0 +1,3887 @@ +/* + * Marvell Wireless LAN device driver: CFG80211 + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "cfg80211.h" +#include "main.h" +#include "11n.h" + +static char *reg_alpha2; +module_param(reg_alpha2, charp, 0); + +static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { + { + .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT), + }, + { + .max = 1, .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_combination +mwifiex_iface_comb_ap_sta = { + .limits = mwifiex_ap_sta_limits, + .num_different_channels = 1, + .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), + .max_interfaces = MWIFIEX_MAX_BSS_NUM, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40), +}; + +static const struct ieee80211_iface_combination +mwifiex_iface_comb_ap_sta_vht = { + .limits = mwifiex_ap_sta_limits, + .num_different_channels = 1, + .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), + .max_interfaces = MWIFIEX_MAX_BSS_NUM, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), +}; + +static const struct +ieee80211_iface_combination mwifiex_iface_comb_ap_sta_drcs = { + .limits = mwifiex_ap_sta_limits, + .num_different_channels = 2, + .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), + .max_interfaces = MWIFIEX_MAX_BSS_NUM, + .beacon_int_infra_match = true, +}; + +/* + * This function maps the nl802.11 channel type into driver channel type. + * + * The mapping is as follows - + * NL80211_CHAN_NO_HT -> IEEE80211_HT_PARAM_CHA_SEC_NONE + * NL80211_CHAN_HT20 -> IEEE80211_HT_PARAM_CHA_SEC_NONE + * NL80211_CHAN_HT40PLUS -> IEEE80211_HT_PARAM_CHA_SEC_ABOVE + * NL80211_CHAN_HT40MINUS -> IEEE80211_HT_PARAM_CHA_SEC_BELOW + * Others -> IEEE80211_HT_PARAM_CHA_SEC_NONE + */ +u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type) +{ + switch (chan_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + return IEEE80211_HT_PARAM_CHA_SEC_NONE; + case NL80211_CHAN_HT40PLUS: + return IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + case NL80211_CHAN_HT40MINUS: + return IEEE80211_HT_PARAM_CHA_SEC_BELOW; + default: + return IEEE80211_HT_PARAM_CHA_SEC_NONE; + } +} + +/* This function maps IEEE HT secondary channel type to NL80211 channel type + */ +u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset) +{ + switch (second_chan_offset) { + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + return NL80211_CHAN_HT20; + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + return NL80211_CHAN_HT40PLUS; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + return NL80211_CHAN_HT40MINUS; + default: + return NL80211_CHAN_HT20; + } +} + +/* + * This function checks whether WEP is set. + */ +static int +mwifiex_is_alg_wep(u32 cipher) +{ + switch (cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + return 1; + default: + break; + } + + return 0; +} + +/* + * This function retrieves the private structure from kernel wiphy structure. + */ +static void *mwifiex_cfg80211_get_adapter(struct wiphy *wiphy) +{ + return (void *) (*(unsigned long *) wiphy_priv(wiphy)); +} + +/* + * CFG802.11 operation handler to delete a network key. + */ +static int +mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, bool pairwise, const u8 *mac_addr) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); + const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const u8 *peer_mac = pairwise ? mac_addr : bc_mac; + + if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1)) { + mwifiex_dbg(priv->adapter, ERROR, "deleting the crypto keys\n"); + return -EFAULT; + } + + mwifiex_dbg(priv->adapter, INFO, "info: crypto keys deleted\n"); + return 0; +} + +/* + * This function forms an skb for management frame. + */ +static int +mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) +{ + u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + u16 pkt_len; + u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT; + + pkt_len = len + ETH_ALEN; + + skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len)); + + memcpy(skb_push(skb, sizeof(tx_control)), + &tx_control, sizeof(tx_control)); + + memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type)); + + /* Add packet data and address4 */ + memcpy(skb_put(skb, sizeof(struct ieee80211_hdr_3addr)), buf, + sizeof(struct ieee80211_hdr_3addr)); + memcpy(skb_put(skb, ETH_ALEN), addr, ETH_ALEN); + memcpy(skb_put(skb, len - sizeof(struct ieee80211_hdr_3addr)), + buf + sizeof(struct ieee80211_hdr_3addr), + len - sizeof(struct ieee80211_hdr_3addr)); + + skb->priority = LOW_PRIO_TID; + __net_timestamp(skb); + + return 0; +} + +/* + * CFG802.11 operation handler to transmit a management frame. + */ +static int +mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, u64 *cookie) +{ + const u8 *buf = params->buf; + size_t len = params->len; + struct sk_buff *skb; + u16 pkt_len; + const struct ieee80211_mgmt *mgmt; + struct mwifiex_txinfo *tx_info; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + + if (!buf || !len) { + mwifiex_dbg(priv->adapter, ERROR, "invalid buffer and length\n"); + return -EFAULT; + } + + mgmt = (const struct ieee80211_mgmt *)buf; + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA && + ieee80211_is_probe_resp(mgmt->frame_control)) { + /* Since we support offload probe resp, we need to skip probe + * resp in AP or GO mode */ + mwifiex_dbg(priv->adapter, INFO, + "info: skip to send probe resp in AP or GO mode\n"); + return 0; + } + + pkt_len = len + ETH_ALEN; + skb = dev_alloc_skb(MWIFIEX_MIN_DATA_HEADER_LEN + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + pkt_len + sizeof(pkt_len)); + + if (!skb) { + mwifiex_dbg(priv->adapter, ERROR, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = pkt_len; + + mwifiex_form_mgmt_frame(skb, buf, len); + *cookie = prandom_u32() | 1; + + if (ieee80211_is_action(mgmt->frame_control)) + skb = mwifiex_clone_skb_for_tx_status(priv, + skb, + MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, cookie); + else + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, + GFP_ATOMIC); + + mwifiex_queue_tx_pkt(priv, skb); + + mwifiex_dbg(priv->adapter, INFO, "info: management frame transmitted\n"); + return 0; +} + +/* + * CFG802.11 operation handler to register a mgmt frame. + */ +static void +mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, + u16 frame_type, bool reg) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + u32 mask; + + if (reg) + mask = priv->mgmt_frame_mask | BIT(frame_type >> 4); + else + mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4); + + if (mask != priv->mgmt_frame_mask) { + priv->mgmt_frame_mask = mask; + mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, + HostCmd_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask, false); + mwifiex_dbg(priv->adapter, INFO, "info: mgmt frame registered\n"); + } +} + +/* + * CFG802.11 operation handler to remain on channel. + */ +static int +mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + unsigned int duration, u64 *cookie) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + int ret; + + if (!chan || !cookie) { + mwifiex_dbg(priv->adapter, ERROR, "Invalid parameter for ROC\n"); + return -EINVAL; + } + + if (priv->roc_cfg.cookie) { + mwifiex_dbg(priv->adapter, INFO, + "info: ongoing ROC, cookie = 0x%llx\n", + priv->roc_cfg.cookie); + return -EBUSY; + } + + ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, chan, + duration); + + if (!ret) { + *cookie = prandom_u32() | 1; + priv->roc_cfg.cookie = *cookie; + priv->roc_cfg.chan = *chan; + + cfg80211_ready_on_channel(wdev, *cookie, chan, + duration, GFP_ATOMIC); + + mwifiex_dbg(priv->adapter, INFO, + "info: ROC, cookie = 0x%llx\n", *cookie); + } + + return ret; +} + +/* + * CFG802.11 operation handler to cancel remain on channel. + */ +static int +mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, u64 cookie) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + int ret; + + if (cookie != priv->roc_cfg.cookie) + return -ENOENT; + + ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_REMOVE, + &priv->roc_cfg.chan, 0); + + if (!ret) { + cfg80211_remain_on_channel_expired(wdev, cookie, + &priv->roc_cfg.chan, + GFP_ATOMIC); + + memset(&priv->roc_cfg, 0, sizeof(struct mwifiex_roc_cfg)); + + mwifiex_dbg(priv->adapter, INFO, + "info: cancel ROC, cookie = 0x%llx\n", cookie); + } + + return ret; +} + +/* + * CFG802.11 operation handler to set Tx power. + */ +static int +mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, + int mbm) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; + struct mwifiex_power_cfg power_cfg; + int dbm = MBM_TO_DBM(mbm); + + if (type == NL80211_TX_POWER_FIXED) { + power_cfg.is_power_auto = 0; + power_cfg.power_level = dbm; + } else { + power_cfg.is_power_auto = 1; + } + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + return mwifiex_set_tx_power(priv, &power_cfg); +} + +/* + * CFG802.11 operation handler to set Power Save option. + * + * The timeout value, if provided, is currently ignored. + */ +static int +mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, + bool enabled, int timeout) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + u32 ps_mode; + + if (timeout) + mwifiex_dbg(priv->adapter, INFO, + "info: ignore timeout value for IEEE Power Save\n"); + + ps_mode = enabled; + + return mwifiex_drv_set_power(priv, &ps_mode); +} + +/* + * CFG802.11 operation handler to set the default network key. + */ +static int +mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, bool unicast, + bool multicast) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); + + /* Return if WEP key not configured */ + if (!priv->sec_info.wep_enabled) + return 0; + + if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) { + priv->wep_key_curr_index = key_index; + } else if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, + NULL, 0)) { + mwifiex_dbg(priv->adapter, ERROR, "set default Tx key index\n"); + return -EFAULT; + } + + return 0; +} + +/* + * CFG802.11 operation handler to add a network key. + */ +static int +mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, bool pairwise, const u8 *mac_addr, + struct key_params *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); + struct mwifiex_wep_key *wep_key; + const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const u8 *peer_mac = pairwise ? mac_addr : bc_mac; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP && + (params->cipher == WLAN_CIPHER_SUITE_WEP40 || + params->cipher == WLAN_CIPHER_SUITE_WEP104)) { + if (params->key && params->key_len) { + wep_key = &priv->wep_key[key_index]; + memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); + memcpy(wep_key->key_material, params->key, + params->key_len); + wep_key->key_index = key_index; + wep_key->key_length = params->key_len; + priv->sec_info.wep_enabled = 1; + } + return 0; + } + + if (mwifiex_set_encode(priv, params, params->key, params->key_len, + key_index, peer_mac, 0)) { + mwifiex_dbg(priv->adapter, ERROR, "crypto keys added\n"); + return -EFAULT; + } + + return 0; +} + +/* + * This function sends domain information to the firmware. + * + * The following information are passed to the firmware - + * - Country codes + * - Sub bands (first channel, number of channels, maximum Tx power) + */ +int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) +{ + u8 no_of_triplet = 0; + struct ieee80211_country_ie_triplet *t; + u8 no_of_parsed_chan = 0; + u8 first_chan = 0, next_chan = 0, max_pwr = 0; + u8 i, flag = 0; + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; + struct mwifiex_802_11d_domain_reg *domain_info = &adapter->domain_reg; + + /* Set country code */ + domain_info->country_code[0] = adapter->country_code[0]; + domain_info->country_code[1] = adapter->country_code[1]; + domain_info->country_code[2] = ' '; + + band = mwifiex_band_to_radio_type(adapter->config_bands); + if (!wiphy->bands[band]) { + mwifiex_dbg(adapter, ERROR, + "11D: setting domain info in FW\n"); + return -1; + } + + sband = wiphy->bands[band]; + + for (i = 0; i < sband->n_channels ; i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (!flag) { + flag = 1; + first_chan = (u32) ch->hw_value; + next_chan = first_chan; + max_pwr = ch->max_power; + no_of_parsed_chan = 1; + continue; + } + + if (ch->hw_value == next_chan + 1 && + ch->max_power == max_pwr) { + next_chan++; + no_of_parsed_chan++; + } else { + t = &domain_info->triplet[no_of_triplet]; + t->chans.first_channel = first_chan; + t->chans.num_channels = no_of_parsed_chan; + t->chans.max_power = max_pwr; + no_of_triplet++; + first_chan = (u32) ch->hw_value; + next_chan = first_chan; + max_pwr = ch->max_power; + no_of_parsed_chan = 1; + } + } + + if (flag) { + t = &domain_info->triplet[no_of_triplet]; + t->chans.first_channel = first_chan; + t->chans.num_channels = no_of_parsed_chan; + t->chans.max_power = max_pwr; + no_of_triplet++; + } + + domain_info->no_of_triplet = no_of_triplet; + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { + mwifiex_dbg(adapter, INFO, + "11D: setting domain info in FW\n"); + return -1; + } + + return 0; +} + +/* + * CFG802.11 regulatory domain callback function. + * + * This function is called when the regulatory domain is changed due to the + * following reasons - + * - Set by driver + * - Set by system core + * - Set by user + * - Set bt Country IE + */ +static void mwifiex_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv = mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY); + mwifiex_dbg(adapter, INFO, + "info: cfg80211 regulatory domain callback for %c%c\n", + request->alpha2[0], request->alpha2[1]); + + switch (request->initiator) { + case NL80211_REGDOM_SET_BY_DRIVER: + case NL80211_REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_USER: + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + break; + default: + mwifiex_dbg(adapter, ERROR, + "unknown regdom initiator: %d\n", + request->initiator); + return; + } + + /* Don't send world or same regdom info to firmware */ + if (strncmp(request->alpha2, "00", 2) && + strncmp(request->alpha2, adapter->country_code, + sizeof(request->alpha2))) { + memcpy(adapter->country_code, request->alpha2, + sizeof(request->alpha2)); + mwifiex_send_domain_info_cmd_fw(wiphy); + mwifiex_dnld_txpwr_table(priv); + } +} + +/* + * This function sets the fragmentation threshold. + * + * The fragmentation threshold value must lie between MWIFIEX_FRAG_MIN_VALUE + * and MWIFIEX_FRAG_MAX_VALUE. + */ +static int +mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr) +{ + if (frag_thr < MWIFIEX_FRAG_MIN_VALUE || + frag_thr > MWIFIEX_FRAG_MAX_VALUE) + frag_thr = MWIFIEX_FRAG_MAX_VALUE; + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, FRAG_THRESH_I, + &frag_thr, true); +} + +/* + * This function sets the RTS threshold. + + * The rts value must lie between MWIFIEX_RTS_MIN_VALUE + * and MWIFIEX_RTS_MAX_VALUE. + */ +static int +mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr) +{ + if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE) + rts_thr = MWIFIEX_RTS_MAX_VALUE; + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, RTS_THRESH_I, + &rts_thr, true); +} + +/* + * CFG802.11 operation handler to set wiphy parameters. + * + * This function can be used to set the RTS threshold and the + * Fragmentation threshold of the driver. + */ +static int +mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; + struct mwifiex_uap_bss_param *bss_cfg; + int ret; + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + switch (priv->bss_role) { + case MWIFIEX_BSS_ROLE_UAP: + if (priv->bss_started) { + mwifiex_dbg(adapter, ERROR, + "cannot change wiphy params when bss started"); + return -EINVAL; + } + + bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL); + if (!bss_cfg) + return -ENOMEM; + + mwifiex_set_sys_config_invalid_data(bss_cfg); + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) + bss_cfg->rts_threshold = wiphy->rts_threshold; + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) + bss_cfg->frag_threshold = wiphy->frag_threshold; + if (changed & WIPHY_PARAM_RETRY_LONG) + bss_cfg->retry_limit = wiphy->retry_long; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, + false); + + kfree(bss_cfg); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "Failed to set wiphy phy params\n"); + return ret; + } + break; + + case MWIFIEX_BSS_ROLE_STA: + if (priv->media_connected) { + mwifiex_dbg(adapter, ERROR, + "cannot change wiphy params when connected"); + return -EINVAL; + } + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + ret = mwifiex_set_rts(priv, + wiphy->rts_threshold); + if (ret) + return ret; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + ret = mwifiex_set_frag(priv, + wiphy->frag_threshold); + if (ret) + return ret; + } + break; + } + + return 0; +} + +static int +mwifiex_cfg80211_deinit_p2p(struct mwifiex_private *priv) +{ + u16 mode = P2P_MODE_DISABLE; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + return 0; +} + +/* + * This function initializes the functionalities for P2P client. + * The P2P client initialization sequence is: + * disable -> device -> client + */ +static int +mwifiex_cfg80211_init_p2p_client(struct mwifiex_private *priv) +{ + u16 mode; + + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -1; + + mode = P2P_MODE_DEVICE; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + mode = P2P_MODE_CLIENT; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + return 0; +} + +/* + * This function initializes the functionalities for P2P GO. + * The P2P GO initialization sequence is: + * disable -> device -> GO + */ +static int +mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv) +{ + u16 mode; + + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -1; + + mode = P2P_MODE_DEVICE; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + mode = P2P_MODE_GO; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + return 0; +} + +static int mwifiex_deinit_priv_params(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + unsigned long flags; + + priv->mgmt_frame_mask = 0; + if (mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, + HostCmd_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask, false)) { + mwifiex_dbg(adapter, ERROR, + "could not unregister mgmt frame rx\n"); + return -1; + } + + mwifiex_deauthenticate(priv, NULL); + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + adapter->main_locked = true; + if (adapter->mwifiex_processing) { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + flush_workqueue(adapter->workqueue); + } else { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + } + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + adapter->rx_locked = true; + if (adapter->rx_processing) { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + flush_workqueue(adapter->rx_workqueue); + } else { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + } + + mwifiex_free_priv(priv); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; + + return 0; +} + +static int +mwifiex_init_new_priv_params(struct mwifiex_private *priv, + struct net_device *dev, + enum nl80211_iftype type) +{ + struct mwifiex_adapter *adapter = priv->adapter; + unsigned long flags; + + mwifiex_init_priv(priv); + + priv->bss_mode = type; + priv->wdev.iftype = type; + + mwifiex_init_priv_params(priv, priv->netdev); + priv->bss_started = 0; + + switch (type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_type = MWIFIEX_BSS_TYPE_STA; + break; + case NL80211_IFTYPE_P2P_CLIENT: + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_type = MWIFIEX_BSS_TYPE_P2P; + break; + case NL80211_IFTYPE_P2P_GO: + priv->bss_role = MWIFIEX_BSS_ROLE_UAP; + priv->bss_type = MWIFIEX_BSS_TYPE_P2P; + break; + case NL80211_IFTYPE_AP: + priv->bss_type = MWIFIEX_BSS_TYPE_UAP; + priv->bss_role = MWIFIEX_BSS_ROLE_UAP; + break; + default: + mwifiex_dbg(adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + adapter->main_locked = false; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + adapter->rx_locked = false; + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + + return 0; +} + +static int +mwifiex_change_vif_to_p2p(struct net_device *dev, + enum nl80211_iftype curr_iftype, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv; + struct mwifiex_adapter *adapter; + + priv = mwifiex_netdev_get_priv(dev); + + if (!priv) + return -1; + + adapter = priv->adapter; + + if (adapter->curr_iface_comb.p2p_intf == + adapter->iface_limit.p2p_intf) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple P2P ifaces\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "%s: changing role to p2p\n", dev->name); + + if (mwifiex_deinit_priv_params(priv)) + return -1; + if (mwifiex_init_new_priv_params(priv, dev, type)) + return -1; + + switch (type) { + case NL80211_IFTYPE_P2P_CLIENT: + if (mwifiex_cfg80211_init_p2p_client(priv)) + return -EFAULT; + break; + case NL80211_IFTYPE_P2P_GO: + if (mwifiex_cfg80211_init_p2p_go(priv)) + return -EFAULT; + break; + default: + mwifiex_dbg(adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + + if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true)) + return -1; + + if (mwifiex_sta_init_cmd(priv, false, false)) + return -1; + + switch (curr_iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf--; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf--; + break; + default: + break; + } + + adapter->curr_iface_comb.p2p_intf++; + dev->ieee80211_ptr->iftype = type; + + return 0; +} + +static int +mwifiex_change_vif_to_sta_adhoc(struct net_device *dev, + enum nl80211_iftype curr_iftype, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv; + struct mwifiex_adapter *adapter; + + priv = mwifiex_netdev_get_priv(dev); + + if (!priv) + return -1; + + adapter = priv->adapter; + + if ((curr_iftype != NL80211_IFTYPE_P2P_CLIENT && + curr_iftype != NL80211_IFTYPE_P2P_GO) && + (adapter->curr_iface_comb.sta_intf == + adapter->iface_limit.sta_intf)) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple station/adhoc ifaces\n"); + return -1; + } + + if (type == NL80211_IFTYPE_STATION) + mwifiex_dbg(adapter, INFO, + "%s: changing role to station\n", dev->name); + else + mwifiex_dbg(adapter, INFO, + "%s: changing role to adhoc\n", dev->name); + + if (mwifiex_deinit_priv_params(priv)) + return -1; + if (mwifiex_init_new_priv_params(priv, dev, type)) + return -1; + if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true)) + return -1; + if (mwifiex_sta_init_cmd(priv, false, false)) + return -1; + + switch (curr_iftype) { + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + adapter->curr_iface_comb.p2p_intf--; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf--; + break; + default: + break; + } + + adapter->curr_iface_comb.sta_intf++; + dev->ieee80211_ptr->iftype = type; + return 0; +} + +static int +mwifiex_change_vif_to_ap(struct net_device *dev, + enum nl80211_iftype curr_iftype, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv; + struct mwifiex_adapter *adapter; + + priv = mwifiex_netdev_get_priv(dev); + + if (!priv) + return -1; + + adapter = priv->adapter; + + if (adapter->curr_iface_comb.uap_intf == + adapter->iface_limit.uap_intf) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple AP ifaces\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "%s: changing role to AP\n", dev->name); + + if (mwifiex_deinit_priv_params(priv)) + return -1; + if (mwifiex_init_new_priv_params(priv, dev, type)) + return -1; + if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true)) + return -1; + if (mwifiex_sta_init_cmd(priv, false, false)) + return -1; + + switch (curr_iftype) { + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + adapter->curr_iface_comb.p2p_intf--; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf--; + break; + default: + break; + } + + adapter->curr_iface_comb.uap_intf++; + dev->ieee80211_ptr->iftype = type; + return 0; +} +/* + * CFG802.11 operation handler to change interface type. + */ +static int +mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype; + + switch (curr_iftype) { + case NL80211_IFTYPE_ADHOC: + switch (type) { + case NL80211_IFTYPE_STATION: + priv->bss_mode = type; + priv->sec_info.authentication_mode = + NL80211_AUTHTYPE_OPEN_SYSTEM; + dev->ieee80211_ptr->iftype = type; + mwifiex_deauthenticate(priv, NULL); + return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, + true); + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + return mwifiex_change_vif_to_p2p(dev, curr_iftype, + type, flags, params); + case NL80211_IFTYPE_AP: + return mwifiex_change_vif_to_ap(dev, curr_iftype, type, + flags, params); + case NL80211_IFTYPE_UNSPECIFIED: + mwifiex_dbg(priv->adapter, INFO, + "%s: kept type as IBSS\n", dev->name); + case NL80211_IFTYPE_ADHOC: /* This shouldn't happen */ + return 0; + default: + mwifiex_dbg(priv->adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + break; + case NL80211_IFTYPE_STATION: + switch (type) { + case NL80211_IFTYPE_ADHOC: + priv->bss_mode = type; + priv->sec_info.authentication_mode = + NL80211_AUTHTYPE_OPEN_SYSTEM; + dev->ieee80211_ptr->iftype = type; + mwifiex_deauthenticate(priv, NULL); + return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, + true); + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + return mwifiex_change_vif_to_p2p(dev, curr_iftype, + type, flags, params); + case NL80211_IFTYPE_AP: + return mwifiex_change_vif_to_ap(dev, curr_iftype, type, + flags, params); + case NL80211_IFTYPE_UNSPECIFIED: + mwifiex_dbg(priv->adapter, INFO, + "%s: kept type as STA\n", dev->name); + case NL80211_IFTYPE_STATION: /* This shouldn't happen */ + return 0; + default: + mwifiex_dbg(priv->adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + break; + case NL80211_IFTYPE_AP: + switch (type) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, + type, flags, + params); + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + return mwifiex_change_vif_to_p2p(dev, curr_iftype, + type, flags, params); + case NL80211_IFTYPE_UNSPECIFIED: + mwifiex_dbg(priv->adapter, INFO, + "%s: kept type as AP\n", dev->name); + case NL80211_IFTYPE_AP: /* This shouldn't happen */ + return 0; + default: + mwifiex_dbg(priv->adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + switch (type) { + case NL80211_IFTYPE_STATION: + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -EFAULT; + priv->adapter->curr_iface_comb.p2p_intf--; + priv->adapter->curr_iface_comb.sta_intf++; + dev->ieee80211_ptr->iftype = type; + break; + case NL80211_IFTYPE_ADHOC: + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -EFAULT; + return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, + type, flags, + params); + break; + case NL80211_IFTYPE_AP: + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -EFAULT; + return mwifiex_change_vif_to_ap(dev, curr_iftype, type, + flags, params); + case NL80211_IFTYPE_UNSPECIFIED: + mwifiex_dbg(priv->adapter, INFO, + "%s: kept type as P2P\n", dev->name); + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + return 0; + default: + mwifiex_dbg(priv->adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "%s: unknown iftype: %d\n", + dev->name, dev->ieee80211_ptr->iftype); + return -EOPNOTSUPP; + } + + + return 0; +} + +static void +mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo, + struct rate_info *rate) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (adapter->is_hw_11ac_capable) { + /* bit[1-0]: 00=LG 01=HT 10=VHT */ + if (tx_htinfo & BIT(0)) { + /* HT */ + rate->mcs = priv->tx_rate; + rate->flags |= RATE_INFO_FLAGS_MCS; + } + if (tx_htinfo & BIT(1)) { + /* VHT */ + rate->mcs = priv->tx_rate & 0x0F; + rate->flags |= RATE_INFO_FLAGS_VHT_MCS; + } + + if (tx_htinfo & (BIT(1) | BIT(0))) { + /* HT or VHT */ + switch (tx_htinfo & (BIT(3) | BIT(2))) { + case 0: + rate->bw = RATE_INFO_BW_20; + break; + case (BIT(2)): + rate->bw = RATE_INFO_BW_40; + break; + case (BIT(3)): + rate->bw = RATE_INFO_BW_80; + break; + case (BIT(3) | BIT(2)): + rate->bw = RATE_INFO_BW_160; + break; + } + + if (tx_htinfo & BIT(4)) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + + if ((priv->tx_rate >> 4) == 1) + rate->nss = 2; + else + rate->nss = 1; + } + } else { + /* + * Bit 0 in tx_htinfo indicates that current Tx rate + * is 11n rate. Valid MCS index values for us are 0 to 15. + */ + if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) { + rate->mcs = priv->tx_rate; + rate->flags |= RATE_INFO_FLAGS_MCS; + rate->bw = RATE_INFO_BW_20; + if (tx_htinfo & BIT(1)) + rate->bw = RATE_INFO_BW_40; + if (tx_htinfo & BIT(2)) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + } + } +} + +/* + * This function dumps the station information on a buffer. + * + * The following information are shown - + * - Total bytes transmitted + * - Total bytes received + * - Total packets transmitted + * - Total packets received + * - Signal quality level + * - Transmission rate + */ +static int +mwifiex_dump_station_info(struct mwifiex_private *priv, + struct mwifiex_sta_node *node, + struct station_info *sinfo) +{ + u32 rate; + + sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_RX_PACKETS) | BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_TX_BITRATE) | + BIT(NL80211_STA_INFO_SIGNAL) | BIT(NL80211_STA_INFO_SIGNAL_AVG); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + if (!node) + return -ENOENT; + + sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME) | + BIT(NL80211_STA_INFO_TX_FAILED); + sinfo->inactive_time = + jiffies_to_msecs(jiffies - node->stats.last_rx); + + sinfo->signal = node->stats.rssi; + sinfo->signal_avg = node->stats.rssi; + sinfo->rx_bytes = node->stats.rx_bytes; + sinfo->tx_bytes = node->stats.tx_bytes; + sinfo->rx_packets = node->stats.rx_packets; + sinfo->tx_packets = node->stats.tx_packets; + sinfo->tx_failed = node->stats.tx_failed; + + mwifiex_parse_htinfo(priv, node->stats.last_tx_htinfo, + &sinfo->txrate); + sinfo->txrate.legacy = node->stats.last_tx_rate * 5; + + return 0; + } + + /* Get signal information from the firmware */ + if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, 0, NULL, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "failed to get signal information\n"); + return -EFAULT; + } + + if (mwifiex_drv_get_data_rate(priv, &rate)) { + mwifiex_dbg(priv->adapter, ERROR, + "getting data rate error\n"); + return -EFAULT; + } + + /* Get DTIM period information from firmware */ + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, DTIM_PERIOD_I, + &priv->dtim_period, true); + + mwifiex_parse_htinfo(priv, priv->tx_htinfo, &sinfo->txrate); + + sinfo->signal_avg = priv->bcn_rssi_avg; + sinfo->rx_bytes = priv->stats.rx_bytes; + sinfo->tx_bytes = priv->stats.tx_bytes; + sinfo->rx_packets = priv->stats.rx_packets; + sinfo->tx_packets = priv->stats.tx_packets; + sinfo->signal = priv->bcn_rssi_avg; + /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */ + sinfo->txrate.legacy = rate * 5; + + if (priv->bss_mode == NL80211_IFTYPE_STATION) { + sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); + sinfo->bss_param.flags = 0; + if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & + WLAN_CAPABILITY_SHORT_PREAMBLE) + sinfo->bss_param.flags |= + BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & + WLAN_CAPABILITY_SHORT_SLOT_TIME) + sinfo->bss_param.flags |= + BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.dtim_period = priv->dtim_period; + sinfo->bss_param.beacon_interval = + priv->curr_bss_params.bss_descriptor.beacon_period; + } + + return 0; +} + +/* + * CFG802.11 operation handler to get station information. + * + * This function only works in connected mode, and dumps the + * requested station information, if available. + */ +static int +mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!priv->media_connected) + return -ENOENT; + if (memcmp(mac, priv->cfg_bssid, ETH_ALEN)) + return -ENOENT; + + return mwifiex_dump_station_info(priv, NULL, sinfo); +} + +/* + * CFG802.11 operation handler to dump station information. + */ +static int +mwifiex_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *mac, struct station_info *sinfo) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + static struct mwifiex_sta_node *node; + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + priv->media_connected && idx == 0) { + ether_addr_copy(mac, priv->cfg_bssid); + return mwifiex_dump_station_info(priv, NULL, sinfo); + } else if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + mwifiex_send_cmd(priv, HOST_CMD_APCMD_STA_LIST, + HostCmd_ACT_GEN_GET, 0, NULL, true); + + if (node && (&node->list == &priv->sta_list)) { + node = NULL; + return -ENOENT; + } + + node = list_prepare_entry(node, &priv->sta_list, list); + list_for_each_entry_continue(node, &priv->sta_list, list) { + ether_addr_copy(mac, node->mac_addr); + return mwifiex_dump_station_info(priv, node, sinfo); + } + } + + return -ENOENT; +} + +static int +mwifiex_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev, + int idx, struct survey_info *survey) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_chan_stats *pchan_stats = priv->adapter->chan_stats; + enum ieee80211_band band; + + mwifiex_dbg(priv->adapter, DUMP, "dump_survey idx=%d\n", idx); + + memset(survey, 0, sizeof(struct survey_info)); + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + priv->media_connected && idx == 0) { + u8 curr_bss_band = priv->curr_bss_params.band; + u32 chan = priv->curr_bss_params.bss_descriptor.channel; + + band = mwifiex_band_to_radio_type(curr_bss_band); + survey->channel = ieee80211_get_channel(wiphy, + ieee80211_channel_to_frequency(chan, band)); + + if (priv->bcn_nf_last) { + survey->filled = SURVEY_INFO_NOISE_DBM; + survey->noise = priv->bcn_nf_last; + } + return 0; + } + + if (idx >= priv->adapter->num_in_chan_stats) + return -ENOENT; + + if (!pchan_stats[idx].cca_scan_dur) + return 0; + + band = pchan_stats[idx].bandcfg; + survey->channel = ieee80211_get_channel(wiphy, + ieee80211_channel_to_frequency(pchan_stats[idx].chan_num, band)); + survey->filled = SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; + survey->noise = pchan_stats[idx].noise; + survey->time = pchan_stats[idx].cca_scan_dur; + survey->time_busy = pchan_stats[idx].cca_busy_dur; + + return 0; +} + +/* Supported rates to be advertised to the cfg80211 */ +static struct ieee80211_rate mwifiex_rates[] = { + {.bitrate = 10, .hw_value = 2, }, + {.bitrate = 20, .hw_value = 4, }, + {.bitrate = 55, .hw_value = 11, }, + {.bitrate = 110, .hw_value = 22, }, + {.bitrate = 60, .hw_value = 12, }, + {.bitrate = 90, .hw_value = 18, }, + {.bitrate = 120, .hw_value = 24, }, + {.bitrate = 180, .hw_value = 36, }, + {.bitrate = 240, .hw_value = 48, }, + {.bitrate = 360, .hw_value = 72, }, + {.bitrate = 480, .hw_value = 96, }, + {.bitrate = 540, .hw_value = 108, }, +}; + +/* Channel definitions to be advertised to cfg80211 */ +static struct ieee80211_channel mwifiex_channels_2ghz[] = { + {.center_freq = 2412, .hw_value = 1, }, + {.center_freq = 2417, .hw_value = 2, }, + {.center_freq = 2422, .hw_value = 3, }, + {.center_freq = 2427, .hw_value = 4, }, + {.center_freq = 2432, .hw_value = 5, }, + {.center_freq = 2437, .hw_value = 6, }, + {.center_freq = 2442, .hw_value = 7, }, + {.center_freq = 2447, .hw_value = 8, }, + {.center_freq = 2452, .hw_value = 9, }, + {.center_freq = 2457, .hw_value = 10, }, + {.center_freq = 2462, .hw_value = 11, }, + {.center_freq = 2467, .hw_value = 12, }, + {.center_freq = 2472, .hw_value = 13, }, + {.center_freq = 2484, .hw_value = 14, }, +}; + +static struct ieee80211_supported_band mwifiex_band_2ghz = { + .channels = mwifiex_channels_2ghz, + .n_channels = ARRAY_SIZE(mwifiex_channels_2ghz), + .bitrates = mwifiex_rates, + .n_bitrates = ARRAY_SIZE(mwifiex_rates), +}; + +static struct ieee80211_channel mwifiex_channels_5ghz[] = { + {.center_freq = 5040, .hw_value = 8, }, + {.center_freq = 5060, .hw_value = 12, }, + {.center_freq = 5080, .hw_value = 16, }, + {.center_freq = 5170, .hw_value = 34, }, + {.center_freq = 5190, .hw_value = 38, }, + {.center_freq = 5210, .hw_value = 42, }, + {.center_freq = 5230, .hw_value = 46, }, + {.center_freq = 5180, .hw_value = 36, }, + {.center_freq = 5200, .hw_value = 40, }, + {.center_freq = 5220, .hw_value = 44, }, + {.center_freq = 5240, .hw_value = 48, }, + {.center_freq = 5260, .hw_value = 52, }, + {.center_freq = 5280, .hw_value = 56, }, + {.center_freq = 5300, .hw_value = 60, }, + {.center_freq = 5320, .hw_value = 64, }, + {.center_freq = 5500, .hw_value = 100, }, + {.center_freq = 5520, .hw_value = 104, }, + {.center_freq = 5540, .hw_value = 108, }, + {.center_freq = 5560, .hw_value = 112, }, + {.center_freq = 5580, .hw_value = 116, }, + {.center_freq = 5600, .hw_value = 120, }, + {.center_freq = 5620, .hw_value = 124, }, + {.center_freq = 5640, .hw_value = 128, }, + {.center_freq = 5660, .hw_value = 132, }, + {.center_freq = 5680, .hw_value = 136, }, + {.center_freq = 5700, .hw_value = 140, }, + {.center_freq = 5745, .hw_value = 149, }, + {.center_freq = 5765, .hw_value = 153, }, + {.center_freq = 5785, .hw_value = 157, }, + {.center_freq = 5805, .hw_value = 161, }, + {.center_freq = 5825, .hw_value = 165, }, +}; + +static struct ieee80211_supported_band mwifiex_band_5ghz = { + .channels = mwifiex_channels_5ghz, + .n_channels = ARRAY_SIZE(mwifiex_channels_5ghz), + .bitrates = mwifiex_rates + 4, + .n_bitrates = ARRAY_SIZE(mwifiex_rates) - 4, +}; + + +/* Supported crypto cipher suits to be advertised to cfg80211 */ +static const u32 mwifiex_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + +/* Supported mgmt frame types to be advertised to cfg80211 */ +static const struct ieee80211_txrx_stypes +mwifiex_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_AP] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, +}; + +/* + * CFG802.11 operation handler for setting bit rates. + * + * Function configures data rates to firmware using bitrate mask + * provided by cfg80211. + */ +static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, + const struct cfg80211_bitrate_mask *mask) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + enum ieee80211_band band; + struct mwifiex_adapter *adapter = priv->adapter; + + if (!priv->media_connected) { + mwifiex_dbg(adapter, ERROR, + "Can not set Tx data rate in disconnected state\n"); + return -EINVAL; + } + + band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + + memset(bitmap_rates, 0, sizeof(bitmap_rates)); + + /* Fill HR/DSSS rates. */ + if (band == IEEE80211_BAND_2GHZ) + bitmap_rates[0] = mask->control[band].legacy & 0x000f; + + /* Fill OFDM rates */ + if (band == IEEE80211_BAND_2GHZ) + bitmap_rates[1] = (mask->control[band].legacy & 0x0ff0) >> 4; + else + bitmap_rates[1] = mask->control[band].legacy; + + /* Fill HT MCS rates */ + bitmap_rates[2] = mask->control[band].ht_mcs[0]; + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) + bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8; + + /* Fill VHT MCS rates */ + if (adapter->fw_api_ver == MWIFIEX_FW_V15) { + bitmap_rates[10] = mask->control[band].vht_mcs[0]; + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) + bitmap_rates[11] = mask->control[band].vht_mcs[1]; + } + + return mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_SET, 0, bitmap_rates, true); +} + +/* + * CFG802.11 operation handler for connection quality monitoring. + * + * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI + * events to FW. + */ +static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_ds_misc_subsc_evt subsc_evt; + + priv->cqm_rssi_thold = rssi_thold; + priv->cqm_rssi_hyst = rssi_hyst; + + memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); + subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; + + /* Subscribe/unsubscribe low and high rssi events */ + if (rssi_thold && rssi_hyst) { + subsc_evt.action = HostCmd_ACT_BITWISE_SET; + subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold); + subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold); + subsc_evt.bcn_l_rssi_cfg.evt_freq = 1; + subsc_evt.bcn_h_rssi_cfg.evt_freq = 1; + return mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, &subsc_evt, true); + } else { + subsc_evt.action = HostCmd_ACT_BITWISE_CLR; + return mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, &subsc_evt, true); + } + + return 0; +} + +/* cfg80211 operation handler for change_beacon. + * Function retrieves and sets modified management IEs to FW. + */ +static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_beacon_data *data) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: bss_type mismatched\n", __func__); + return -EINVAL; + } + + if (!priv->bss_started) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: bss not started\n", __func__); + return -EINVAL; + } + + if (mwifiex_set_mgmt_ies(priv, data)) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: setting mgmt ies failed\n", __func__); + return -EFAULT; + } + + return 0; +} + +/* cfg80211 operation handler for del_station. + * Function deauthenticates station which value is provided in mac parameter. + * If mac is NULL/broadcast, all stations in associated station list are + * deauthenticated. If bss is not started or there are no stations in + * associated stations list, no action is taken. + */ +static int +mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, + struct station_del_parameters *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_sta_node *sta_node; + u8 deauth_mac[ETH_ALEN]; + unsigned long flags; + + if (list_empty(&priv->sta_list) || !priv->bss_started) + return 0; + + if (!params->mac || is_broadcast_ether_addr(params->mac)) + return 0; + + mwifiex_dbg(priv->adapter, INFO, "%s: mac address %pM\n", + __func__, params->mac); + + eth_zero_addr(deauth_mac); + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_node = mwifiex_get_sta_entry(priv, params->mac); + if (sta_node) + ether_addr_copy(deauth_mac, params->mac); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + if (is_valid_ether_addr(deauth_mac)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH, + HostCmd_ACT_GEN_SET, 0, + deauth_mac, true)) + return -1; + } + + return 0; +} + +static int +mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv = mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY); + struct mwifiex_ds_ant_cfg ant_cfg; + + if (!tx_ant || !rx_ant) + return -EOPNOTSUPP; + + if (adapter->hw_dev_mcs_support != HT_STREAM_2X2) { + /* Not a MIMO chip. User should provide specific antenna number + * for Tx/Rx path or enable all antennas for diversity + */ + if (tx_ant != rx_ant) + return -EOPNOTSUPP; + + if ((tx_ant & (tx_ant - 1)) && + (tx_ant != BIT(adapter->number_of_antenna) - 1)) + return -EOPNOTSUPP; + + if ((tx_ant == BIT(adapter->number_of_antenna) - 1) && + (priv->adapter->number_of_antenna > 1)) { + tx_ant = RF_ANTENNA_AUTO; + rx_ant = RF_ANTENNA_AUTO; + } + } else { + struct ieee80211_sta_ht_cap *ht_info; + int rx_mcs_supp; + enum ieee80211_band band; + + if ((tx_ant == 0x1 && rx_ant == 0x1)) { + adapter->user_dev_mcs_support = HT_STREAM_1X1; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support = + MWIFIEX_11AC_MCS_MAP_1X1; + } else { + adapter->user_dev_mcs_support = HT_STREAM_2X2; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support = + MWIFIEX_11AC_MCS_MAP_2X2; + } + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!adapter->wiphy->bands[band]) + continue; + + ht_info = &adapter->wiphy->bands[band]->ht_cap; + rx_mcs_supp = + GET_RXMCSSUPP(adapter->user_dev_mcs_support); + memset(&ht_info->mcs, 0, adapter->number_of_antenna); + memset(&ht_info->mcs, 0xff, rx_mcs_supp); + } + } + + ant_cfg.tx_ant = tx_ant; + ant_cfg.rx_ant = rx_ant; + + return mwifiex_send_cmd(priv, HostCmd_CMD_RF_ANTENNA, + HostCmd_ACT_GEN_SET, 0, &ant_cfg, true); +} + +/* cfg80211 operation handler for stop ap. + * Function stops BSS running at uAP interface. + */ +static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + mwifiex_abort_cac(priv); + + if (mwifiex_del_mgmt_ies(priv)) + mwifiex_dbg(priv->adapter, ERROR, + "Failed to delete mgmt IEs!\n"); + + priv->ap_11n_enabled = 0; + memset(&priv->bss_cfg, 0, sizeof(priv->bss_cfg)); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to stop the BSS\n"); + return -1; + } + + if (mwifiex_send_cmd(priv, HOST_CMD_APCMD_SYS_RESET, + HostCmd_ACT_GEN_SET, 0, NULL, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to reset BSS\n"); + return -1; + } + + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); + + return 0; +} + +/* cfg80211 operation handler for start_ap. + * Function sets beacon period, DTIM period, SSID and security into + * AP config structure. + * AP is configured with these settings and BSS is started. + */ +static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_settings *params) +{ + struct mwifiex_uap_bss_param *bss_cfg; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) + return -1; + + bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL); + if (!bss_cfg) + return -ENOMEM; + + mwifiex_set_sys_config_invalid_data(bss_cfg); + + if (params->beacon_interval) + bss_cfg->beacon_period = params->beacon_interval; + if (params->dtim_period) + bss_cfg->dtim_period = params->dtim_period; + + if (params->ssid && params->ssid_len) { + memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len); + bss_cfg->ssid.ssid_len = params->ssid_len; + } + if (params->inactivity_timeout > 0) { + /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */ + bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout; + bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout; + } + + switch (params->hidden_ssid) { + case NL80211_HIDDEN_SSID_NOT_IN_USE: + bss_cfg->bcast_ssid_ctl = 1; + break; + case NL80211_HIDDEN_SSID_ZERO_LEN: + bss_cfg->bcast_ssid_ctl = 0; + break; + case NL80211_HIDDEN_SSID_ZERO_CONTENTS: + /* firmware doesn't support this type of hidden SSID */ + default: + kfree(bss_cfg); + return -EINVAL; + } + + mwifiex_uap_set_channel(priv, bss_cfg, params->chandef); + mwifiex_set_uap_rates(bss_cfg, params); + + if (mwifiex_set_secure_params(priv, bss_cfg, params)) { + kfree(bss_cfg); + mwifiex_dbg(priv->adapter, ERROR, + "Failed to parse secuirty parameters!\n"); + return -1; + } + + mwifiex_set_ht_params(priv, bss_cfg, params); + + if (priv->adapter->is_hw_11ac_capable) { + mwifiex_set_vht_params(priv, bss_cfg, params); + mwifiex_set_vht_width(priv, params->chandef.width, + priv->ap_11ac_enabled); + } + + if (priv->ap_11ac_enabled) + mwifiex_set_11ac_ba_params(priv); + else + mwifiex_set_ba_params(priv); + + mwifiex_set_wmm_params(priv, bss_cfg, params); + + if (mwifiex_is_11h_active(priv)) + mwifiex_set_tpc_params(priv, bss_cfg, params); + + if (mwifiex_is_11h_active(priv) && + !cfg80211_chandef_dfs_required(wiphy, ¶ms->chandef, + priv->bss_mode)) { + mwifiex_dbg(priv->adapter, INFO, + "Disable 11h extensions in FW\n"); + if (mwifiex_11h_activate(priv, false)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to disable 11h extensions!!"); + return -1; + } + priv->state_11h.is_11h_active = false; + } + + if (mwifiex_config_start_uap(priv, bss_cfg)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to start AP\n"); + kfree(bss_cfg); + return -1; + } + + if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon)) + return -1; + + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + mwifiex_wake_up_net_dev_queue(priv->netdev, priv->adapter); + + memcpy(&priv->bss_cfg, bss_cfg, sizeof(priv->bss_cfg)); + kfree(bss_cfg); + return 0; +} + +/* + * CFG802.11 operation handler for disconnection request. + * + * This function does not work when there is already a disconnection + * procedure going on. + */ +static int +mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (mwifiex_deauthenticate(priv, NULL)) + return -EFAULT; + + mwifiex_dbg(priv->adapter, MSG, + "info: successfully disconnected from %pM:\t" + "reason code %d\n", priv->cfg_bssid, reason_code); + + eth_zero_addr(priv->cfg_bssid); + priv->hs2_enabled = false; + + return 0; +} + +/* + * This function informs the CFG802.11 subsystem of a new IBSS. + * + * The following information are sent to the CFG802.11 subsystem + * to register the new IBSS. If we do not register the new IBSS, + * a kernel panic will result. + * - SSID + * - SSID length + * - BSSID + * - Channel + */ +static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) +{ + struct ieee80211_channel *chan; + struct mwifiex_bss_info bss_info; + struct cfg80211_bss *bss; + int ie_len; + u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)]; + enum ieee80211_band band; + + if (mwifiex_get_bss_info(priv, &bss_info)) + return -1; + + ie_buf[0] = WLAN_EID_SSID; + ie_buf[1] = bss_info.ssid.ssid_len; + + memcpy(&ie_buf[sizeof(struct ieee_types_header)], + &bss_info.ssid.ssid, bss_info.ssid.ssid_len); + ie_len = ie_buf[1] + sizeof(struct ieee_types_header); + + band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + chan = __ieee80211_get_channel(priv->wdev.wiphy, + ieee80211_channel_to_frequency(bss_info.bss_chan, + band)); + + bss = cfg80211_inform_bss(priv->wdev.wiphy, chan, + CFG80211_BSS_FTYPE_UNKNOWN, + bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, + 0, ie_buf, ie_len, 0, GFP_KERNEL); + if (bss) { + cfg80211_put_bss(priv->wdev.wiphy, bss); + ether_addr_copy(priv->cfg_bssid, bss_info.bssid); + } + + return 0; +} + +/* + * This function connects with a BSS. + * + * This function handles both Infra and Ad-Hoc modes. It also performs + * validity checking on the provided parameters, disconnects from the + * current BSS (if any), sets up the association/scan parameters, + * including security settings, and performs specific SSID scan before + * trying to connect. + * + * For Infra mode, the function returns failure if the specified SSID + * is not found in scan table. However, for Ad-Hoc mode, it can create + * the IBSS if it does not exist. On successful completion in either case, + * the function notifies the CFG802.11 subsystem of the new BSS connection. + */ +static int +mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, + const u8 *ssid, const u8 *bssid, int mode, + struct ieee80211_channel *channel, + struct cfg80211_connect_params *sme, bool privacy) +{ + struct cfg80211_ssid req_ssid; + int ret, auth_type = 0; + struct cfg80211_bss *bss = NULL; + u8 is_scanning_required = 0; + + memset(&req_ssid, 0, sizeof(struct cfg80211_ssid)); + + req_ssid.ssid_len = ssid_len; + if (ssid_len > IEEE80211_MAX_SSID_LEN) { + mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n"); + return -EINVAL; + } + + memcpy(req_ssid.ssid, ssid, ssid_len); + if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { + mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n"); + return -EINVAL; + } + + /* As this is new association, clear locally stored + * keys and security related flags */ + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + priv->wep_key_curr_index = 0; + priv->sec_info.encryption_mode = 0; + priv->sec_info.is_authtype_auto = 0; + ret = mwifiex_set_encode(priv, NULL, NULL, 0, 0, NULL, 1); + + if (mode == NL80211_IFTYPE_ADHOC) { + /* "privacy" is set only for ad-hoc mode */ + if (privacy) { + /* + * Keep WLAN_CIPHER_SUITE_WEP104 for now so that + * the firmware can find a matching network from the + * scan. The cfg80211 does not give us the encryption + * mode at this stage so just setting it to WEP here. + */ + priv->sec_info.encryption_mode = + WLAN_CIPHER_SUITE_WEP104; + priv->sec_info.authentication_mode = + NL80211_AUTHTYPE_OPEN_SYSTEM; + } + + goto done; + } + + /* Now handle infra mode. "sme" is valid for infra mode only */ + if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { + auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + priv->sec_info.is_authtype_auto = 1; + } else { + auth_type = sme->auth_type; + } + + if (sme->crypto.n_ciphers_pairwise) { + priv->sec_info.encryption_mode = + sme->crypto.ciphers_pairwise[0]; + priv->sec_info.authentication_mode = auth_type; + } + + if (sme->crypto.cipher_group) { + priv->sec_info.encryption_mode = sme->crypto.cipher_group; + priv->sec_info.authentication_mode = auth_type; + } + if (sme->ie) + ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len); + + if (sme->key) { + if (mwifiex_is_alg_wep(priv->sec_info.encryption_mode)) { + mwifiex_dbg(priv->adapter, INFO, + "info: setting wep encryption\t" + "with key len %d\n", sme->key_len); + priv->wep_key_curr_index = sme->key_idx; + ret = mwifiex_set_encode(priv, NULL, sme->key, + sme->key_len, sme->key_idx, + NULL, 0); + } + } +done: + /* + * Scan entries are valid for some time (15 sec). So we can save one + * active scan time if we just try cfg80211_get_bss first. If it fails + * then request scan and cfg80211_get_bss() again for final output. + */ + while (1) { + if (is_scanning_required) { + /* Do specific SSID scanning */ + if (mwifiex_request_scan(priv, &req_ssid)) { + mwifiex_dbg(priv->adapter, ERROR, "scan error\n"); + return -EFAULT; + } + } + + /* Find the BSS we want using available scan results */ + if (mode == NL80211_IFTYPE_ADHOC) + bss = cfg80211_get_bss(priv->wdev.wiphy, channel, + bssid, ssid, ssid_len, + IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY_ANY); + else + bss = cfg80211_get_bss(priv->wdev.wiphy, channel, + bssid, ssid, ssid_len, + IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY_ANY); + + if (!bss) { + if (is_scanning_required) { + mwifiex_dbg(priv->adapter, WARN, + "assoc: requested bss not found in scan results\n"); + break; + } + is_scanning_required = 1; + } else { + mwifiex_dbg(priv->adapter, MSG, + "info: trying to associate to '%s' bssid %pM\n", + (char *)req_ssid.ssid, bss->bssid); + memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN); + break; + } + } + + ret = mwifiex_bss_start(priv, bss, &req_ssid); + if (ret) + return ret; + + if (mode == NL80211_IFTYPE_ADHOC) { + /* Inform the BSS information to kernel, otherwise + * kernel will give a panic after successful assoc */ + if (mwifiex_cfg80211_inform_ibss_bss(priv)) + return -EFAULT; + } + + return ret; +} + +/* + * CFG802.11 operation handler for association request. + * + * This function does not work when the current mode is set to Ad-Hoc, or + * when there is already an association procedure going on. The given BSS + * information is used to associate. + */ +static int +mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) { + mwifiex_dbg(adapter, ERROR, + "%s: reject infra assoc request in non-STA role\n", + dev->name); + return -EINVAL; + } + + if (priv->wdev.current_bss) { + mwifiex_dbg(adapter, ERROR, + "%s: already connected\n", dev->name); + return -EALREADY; + } + + if (adapter->surprise_removed || adapter->is_cmd_timedout) { + mwifiex_dbg(adapter, ERROR, + "%s: Ignore connection.\t" + "Card removed or FW in bad state\n", + dev->name); + return -EFAULT; + } + + mwifiex_dbg(adapter, INFO, + "info: Trying to associate to %s and bssid %pM\n", + (char *)sme->ssid, sme->bssid); + + ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, + priv->bss_mode, sme->channel, sme, 0); + if (!ret) { + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, + NULL, 0, WLAN_STATUS_SUCCESS, + GFP_KERNEL); + mwifiex_dbg(priv->adapter, MSG, + "info: associated to bssid %pM successfully\n", + priv->cfg_bssid); + if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->auto_tdls && + priv->bss_type == MWIFIEX_BSS_TYPE_STA) + mwifiex_setup_auto_tdls_timer(priv); + } else { + mwifiex_dbg(priv->adapter, ERROR, + "info: association to bssid %pM failed\n", + priv->cfg_bssid); + eth_zero_addr(priv->cfg_bssid); + + if (ret > 0) + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, NULL, 0, ret, + GFP_KERNEL); + else + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + } + + return 0; +} + +/* + * This function sets following parameters for ibss network. + * - channel + * - start band + * - 11n flag + * - secondary channel offset + */ +static int mwifiex_set_ibss_params(struct mwifiex_private *priv, + struct cfg80211_ibss_params *params) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int index = 0, i; + u8 config_bands = 0; + + if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) { + if (!params->basic_rates) { + config_bands = BAND_B | BAND_G; + } else { + for (i = 0; i < mwifiex_band_2ghz.n_bitrates; i++) { + /* + * Rates below 6 Mbps in the table are CCK + * rates; 802.11b and from 6 they are OFDM; + * 802.11G + */ + if (mwifiex_rates[i].bitrate == 60) { + index = 1 << i; + break; + } + } + + if (params->basic_rates < index) { + config_bands = BAND_B; + } else { + config_bands = BAND_G; + if (params->basic_rates % index) + config_bands |= BAND_B; + } + } + + if (cfg80211_get_chandef_type(¶ms->chandef) != + NL80211_CHAN_NO_HT) + config_bands |= BAND_G | BAND_GN; + } else { + if (cfg80211_get_chandef_type(¶ms->chandef) == + NL80211_CHAN_NO_HT) + config_bands = BAND_A; + else + config_bands = BAND_AN | BAND_A; + } + + if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) { + adapter->config_bands = config_bands; + adapter->adhoc_start_band = config_bands; + + if ((config_bands & BAND_GN) || (config_bands & BAND_AN)) + adapter->adhoc_11n_enabled = true; + else + adapter->adhoc_11n_enabled = false; + } + + adapter->sec_chan_offset = + mwifiex_chan_type_to_sec_chan_offset( + cfg80211_get_chandef_type(¶ms->chandef)); + priv->adhoc_channel = ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); + + mwifiex_dbg(adapter, INFO, + "info: set ibss band %d, chan %d, chan offset %d\n", + config_bands, priv->adhoc_channel, + adapter->sec_chan_offset); + + return 0; +} + +/* + * CFG802.11 operation handler to join an IBSS. + * + * This function does not work in any mode other than Ad-Hoc, or if + * a join operation is already in progress. + */ +static int +mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + int ret = 0; + + if (priv->bss_mode != NL80211_IFTYPE_ADHOC) { + mwifiex_dbg(priv->adapter, ERROR, + "request to join ibss received\t" + "when station is not in ibss mode\n"); + goto done; + } + + mwifiex_dbg(priv->adapter, MSG, + "info: trying to join to %s and bssid %pM\n", + (char *)params->ssid, params->bssid); + + mwifiex_set_ibss_params(priv, params); + + ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, + params->bssid, priv->bss_mode, + params->chandef.chan, NULL, + params->privacy); +done: + if (!ret) { + cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, + params->chandef.chan, GFP_KERNEL); + mwifiex_dbg(priv->adapter, MSG, + "info: joined/created adhoc network with bssid\t" + "%pM successfully\n", priv->cfg_bssid); + } else { + mwifiex_dbg(priv->adapter, ERROR, + "info: failed creating/joining adhoc network\n"); + } + + return ret; +} + +/* + * CFG802.11 operation handler to leave an IBSS. + * + * This function does not work if a leave operation is + * already in progress. + */ +static int +mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + mwifiex_dbg(priv->adapter, MSG, "info: disconnecting from essid %pM\n", + priv->cfg_bssid); + if (mwifiex_deauthenticate(priv, NULL)) + return -EFAULT; + + eth_zero_addr(priv->cfg_bssid); + + return 0; +} + +/* + * CFG802.11 operation handler for scan request. + * + * This function issues a scan request to the firmware based upon + * the user specified scan configuration. On successful completion, + * it also informs the results. + */ +static int +mwifiex_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct net_device *dev = request->wdev->netdev; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + int i, offset, ret; + struct ieee80211_channel *chan; + struct ieee_types_header *ie; + struct mwifiex_user_scan_cfg *user_scan_cfg; + + mwifiex_dbg(priv->adapter, CMD, + "info: received scan request on %s\n", dev->name); + + /* Block scan request if scan operation or scan cleanup when interface + * is disabled is in process + */ + if (priv->scan_request || priv->scan_aborting) { + mwifiex_dbg(priv->adapter, WARN, + "cmd: Scan already in process..\n"); + return -EBUSY; + } + + user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); + if (!user_scan_cfg) + return -ENOMEM; + + priv->scan_request = request; + + user_scan_cfg->num_ssids = request->n_ssids; + user_scan_cfg->ssid_list = request->ssids; + + if (request->ie && request->ie_len) { + offset = 0; + for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR) + continue; + priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_SCAN; + ie = (struct ieee_types_header *)(request->ie + offset); + memcpy(&priv->vs_ie[i].ie, ie, sizeof(*ie) + ie->len); + offset += sizeof(*ie) + ie->len; + + if (offset >= request->ie_len) + break; + } + } + + for (i = 0; i < min_t(u32, request->n_channels, + MWIFIEX_USER_SCAN_CHAN_MAX); i++) { + chan = request->channels[i]; + user_scan_cfg->chan_list[i].chan_number = chan->hw_value; + user_scan_cfg->chan_list[i].radio_type = chan->band; + + if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids) + user_scan_cfg->chan_list[i].scan_type = + MWIFIEX_SCAN_TYPE_PASSIVE; + else + user_scan_cfg->chan_list[i].scan_type = + MWIFIEX_SCAN_TYPE_ACTIVE; + + user_scan_cfg->chan_list[i].scan_time = 0; + } + + if (priv->adapter->scan_chan_gap_enabled && + mwifiex_is_any_intf_active(priv)) + user_scan_cfg->scan_chan_gap = + priv->adapter->scan_chan_gap_time; + + ret = mwifiex_scan_networks(priv, user_scan_cfg); + kfree(user_scan_cfg); + if (ret) { + mwifiex_dbg(priv->adapter, ERROR, + "scan failed: %d\n", ret); + priv->scan_aborting = false; + priv->scan_request = NULL; + return ret; + } + + if (request->ie && request->ie_len) { + for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask == MWIFIEX_VSIE_MASK_SCAN) { + priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_CLEAR; + memset(&priv->vs_ie[i].ie, 0, + MWIFIEX_MAX_VSIE_LEN); + } + } + } + return 0; +} + +static void mwifiex_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info, + struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + vht_info->vht_supported = true; + + vht_info->cap = adapter->hw_dot_11ac_dev_cap; + /* Update MCS support for VHT */ + vht_info->vht_mcs.rx_mcs_map = cpu_to_le16( + adapter->hw_dot_11ac_mcs_support & 0xFFFF); + vht_info->vht_mcs.rx_highest = 0; + vht_info->vht_mcs.tx_mcs_map = cpu_to_le16( + adapter->hw_dot_11ac_mcs_support >> 16); + vht_info->vht_mcs.tx_highest = 0; +} + +/* + * This function sets up the CFG802.11 specific HT capability fields + * with default values. + * + * The following default values are set - + * - HT Supported = True + * - Maximum AMPDU length factor = IEEE80211_HT_MAX_AMPDU_64K + * - Minimum AMPDU spacing = IEEE80211_HT_MPDU_DENSITY_NONE + * - HT Capabilities supported by firmware + * - MCS information, Rx mask = 0xff + * - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01) + */ +static void +mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, + struct mwifiex_private *priv) +{ + int rx_mcs_supp; + struct ieee80211_mcs_info mcs_set; + u8 *mcs = (u8 *)&mcs_set; + struct mwifiex_adapter *adapter = priv->adapter; + + ht_info->ht_supported = true; + ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; + + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + + /* Fill HT capability information */ + if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + else + ht_info->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + else + ht_info->cap &= ~IEEE80211_HT_CAP_SGI_20; + + if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + else + ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40; + + if (adapter->user_dev_mcs_support == HT_STREAM_2X2) + ht_info->cap |= 3 << IEEE80211_HT_CAP_RX_STBC_SHIFT; + else + ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; + + if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + else + ht_info->cap &= ~IEEE80211_HT_CAP_TX_STBC; + + if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + else + ht_info->cap &= ~IEEE80211_HT_CAP_GRN_FLD; + + if (ISENABLED_40MHZ_INTOLERANT(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT; + else + ht_info->cap &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT; + + if (ISSUPP_RXLDPC(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; + else + ht_info->cap &= ~IEEE80211_HT_CAP_LDPC_CODING; + + ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU; + ht_info->cap |= IEEE80211_HT_CAP_SM_PS; + + rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support); + /* Set MCS for 1x1/2x2 */ + memset(mcs, 0xff, rx_mcs_supp); + /* Clear all the other values */ + memset(&mcs[rx_mcs_supp], 0, + sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); + if (priv->bss_mode == NL80211_IFTYPE_STATION || + ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) + /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ + SETHT_MCS32(mcs_set.rx_mask); + + memcpy((u8 *) &ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info)); + + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; +} + +/* + * create a new virtual interface with the given name and name assign type + */ +struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; + struct net_device *dev; + void *mdev_priv; + + if (!adapter) + return ERR_PTR(-EFAULT); + + switch (type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + if (adapter->curr_iface_comb.sta_intf == + adapter->iface_limit.sta_intf) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple sta/adhoc ifaces\n"); + return ERR_PTR(-EINVAL); + } + + priv = mwifiex_get_unused_priv(adapter); + if (!priv) { + mwifiex_dbg(adapter, ERROR, + "could not get free private struct\n"); + return ERR_PTR(-EFAULT); + } + + priv->wdev.wiphy = wiphy; + priv->wdev.iftype = NL80211_IFTYPE_STATION; + + if (type == NL80211_IFTYPE_UNSPECIFIED) + priv->bss_mode = NL80211_IFTYPE_STATION; + else + priv->bss_mode = type; + + priv->bss_type = MWIFIEX_BSS_TYPE_STA; + priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority = 0; + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_num = adapter->curr_iface_comb.sta_intf; + + break; + case NL80211_IFTYPE_AP: + if (adapter->curr_iface_comb.uap_intf == + adapter->iface_limit.uap_intf) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple AP ifaces\n"); + return ERR_PTR(-EINVAL); + } + + priv = mwifiex_get_unused_priv(adapter); + if (!priv) { + mwifiex_dbg(adapter, ERROR, + "could not get free private struct\n"); + return ERR_PTR(-EFAULT); + } + + priv->wdev.wiphy = wiphy; + priv->wdev.iftype = NL80211_IFTYPE_AP; + + priv->bss_type = MWIFIEX_BSS_TYPE_UAP; + priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority = 0; + priv->bss_role = MWIFIEX_BSS_ROLE_UAP; + priv->bss_started = 0; + priv->bss_num = adapter->curr_iface_comb.uap_intf; + priv->bss_mode = type; + + break; + case NL80211_IFTYPE_P2P_CLIENT: + if (adapter->curr_iface_comb.p2p_intf == + adapter->iface_limit.p2p_intf) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple P2P ifaces\n"); + return ERR_PTR(-EINVAL); + } + + priv = mwifiex_get_unused_priv(adapter); + if (!priv) { + mwifiex_dbg(adapter, ERROR, + "could not get free private struct\n"); + return ERR_PTR(-EFAULT); + } + + priv->wdev.wiphy = wiphy; + /* At start-up, wpa_supplicant tries to change the interface + * to NL80211_IFTYPE_STATION if it is not managed mode. + */ + priv->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT; + priv->bss_mode = NL80211_IFTYPE_P2P_CLIENT; + + /* Setting bss_type to P2P tells firmware that this interface + * is receiving P2P peers found during find phase and doing + * action frame handshake. + */ + priv->bss_type = MWIFIEX_BSS_TYPE_P2P; + + priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority = MWIFIEX_BSS_ROLE_STA; + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_started = 0; + priv->bss_num = adapter->curr_iface_comb.p2p_intf; + + if (mwifiex_cfg80211_init_p2p_client(priv)) { + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-EFAULT); + } + + break; + default: + mwifiex_dbg(adapter, ERROR, "type not supported\n"); + return ERR_PTR(-EINVAL); + } + + dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name, + name_assign_type, ether_setup, + IEEE80211_NUM_ACS, 1); + if (!dev) { + mwifiex_dbg(adapter, ERROR, + "no memory available for netdevice\n"); + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-ENOMEM); + } + + mwifiex_init_priv_params(priv, dev); + priv->netdev = dev; + + mwifiex_setup_ht_caps(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv); + if (adapter->is_hw_11ac_capable) + mwifiex_setup_vht_caps( + &wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap, priv); + + if (adapter->config_bands & BAND_A) + mwifiex_setup_ht_caps( + &wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv); + + if ((adapter->config_bands & BAND_A) && adapter->is_hw_11ac_capable) + mwifiex_setup_vht_caps( + &wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap, priv); + + dev_net_set(dev, wiphy_net(wiphy)); + dev->ieee80211_ptr = &priv->wdev; + dev->ieee80211_ptr->iftype = priv->bss_mode; + memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); + SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); + + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT; + dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN; + dev->ethtool_ops = &mwifiex_ethtool_ops; + + mdev_priv = netdev_priv(dev); + *((unsigned long *) mdev_priv) = (unsigned long) priv; + + SET_NETDEV_DEV(dev, adapter->dev); + + /* Register network device */ + if (register_netdevice(dev)) { + mwifiex_dbg(adapter, ERROR, + "cannot register virtual network device\n"); + free_netdev(dev); + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->netdev = NULL; + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-EFAULT); + } + + priv->dfs_cac_workqueue = alloc_workqueue("MWIFIEX_DFS_CAC%s", + WQ_HIGHPRI | + WQ_MEM_RECLAIM | + WQ_UNBOUND, 1, name); + if (!priv->dfs_cac_workqueue) { + mwifiex_dbg(adapter, ERROR, + "cannot register virtual network device\n"); + free_netdev(dev); + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->netdev = NULL; + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-ENOMEM); + } + + INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue); + + priv->dfs_chan_sw_workqueue = alloc_workqueue("MWIFIEX_DFS_CHSW%s", + WQ_HIGHPRI | WQ_UNBOUND | + WQ_MEM_RECLAIM, 1, name); + if (!priv->dfs_chan_sw_workqueue) { + mwifiex_dbg(adapter, ERROR, + "cannot register virtual network device\n"); + free_netdev(dev); + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->netdev = NULL; + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-ENOMEM); + } + + INIT_DELAYED_WORK(&priv->dfs_chan_sw_work, + mwifiex_dfs_chan_sw_work_queue); + + sema_init(&priv->async_sem, 1); + + mwifiex_dbg(adapter, INFO, + "info: %s: Marvell 802.11 Adapter\n", dev->name); + +#ifdef CONFIG_DEBUG_FS + mwifiex_dev_debugfs_init(priv); +#endif + + switch (type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf++; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf++; + break; + case NL80211_IFTYPE_P2P_CLIENT: + adapter->curr_iface_comb.p2p_intf++; + break; + default: + mwifiex_dbg(adapter, ERROR, "type not supported\n"); + return ERR_PTR(-EINVAL); + } + + return &priv->wdev; +} +EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf); + +/* + * del_virtual_intf: remove the virtual interface determined by dev + */ +int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + struct mwifiex_adapter *adapter = priv->adapter; + struct sk_buff *skb, *tmp; + +#ifdef CONFIG_DEBUG_FS + mwifiex_dev_debugfs_remove(priv); +#endif + + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + + skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + + if (wdev->netdev->reg_state == NETREG_REGISTERED) + unregister_netdevice(wdev->netdev); + + if (priv->dfs_cac_workqueue) { + flush_workqueue(priv->dfs_cac_workqueue); + destroy_workqueue(priv->dfs_cac_workqueue); + priv->dfs_cac_workqueue = NULL; + } + + if (priv->dfs_chan_sw_workqueue) { + flush_workqueue(priv->dfs_chan_sw_workqueue); + destroy_workqueue(priv->dfs_chan_sw_workqueue); + priv->dfs_chan_sw_workqueue = NULL; + } + /* Clear the priv in adapter */ + priv->netdev->ieee80211_ptr = NULL; + priv->netdev = NULL; + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + + priv->media_connected = false; + + switch (priv->bss_mode) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf--; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf--; + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + adapter->curr_iface_comb.p2p_intf--; + break; + default: + mwifiex_dbg(adapter, ERROR, + "del_virtual_intf: type not supported\n"); + break; + } + + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + kfree(priv->hist_data); + + return 0; +} +EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); + +static bool +mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq, + u8 max_byte_seq) +{ + int j, k, valid_byte_cnt = 0; + bool dont_care_byte = false; + + for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { + for (k = 0; k < 8; k++) { + if (pat->mask[j] & 1 << k) { + memcpy(byte_seq + valid_byte_cnt, + &pat->pattern[j * 8 + k], 1); + valid_byte_cnt++; + if (dont_care_byte) + return false; + } else { + if (valid_byte_cnt) + dont_care_byte = true; + } + + if (valid_byte_cnt > max_byte_seq) + return false; + } + } + + byte_seq[max_byte_seq] = valid_byte_cnt; + + return true; +} + +#ifdef CONFIG_PM +static void mwifiex_set_auto_arp_mef_entry(struct mwifiex_private *priv, + struct mwifiex_mef_entry *mef_entry) +{ + int i, filt_num = 0, num_ipv4 = 0; + struct in_device *in_dev; + struct in_ifaddr *ifa; + __be32 ips[MWIFIEX_MAX_SUPPORTED_IPADDR]; + struct mwifiex_adapter *adapter = priv->adapter; + + mef_entry->mode = MEF_MODE_HOST_SLEEP; + mef_entry->action = MEF_ACTION_AUTO_ARP; + + /* Enable ARP offload feature */ + memset(ips, 0, sizeof(ips)); + for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) { + if (adapter->priv[i]->netdev) { + in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev); + if (!in_dev) + continue; + ifa = in_dev->ifa_list; + if (!ifa || !ifa->ifa_local) + continue; + ips[i] = ifa->ifa_local; + num_ipv4++; + } + } + + for (i = 0; i < num_ipv4; i++) { + if (!ips[i]) + continue; + mef_entry->filter[filt_num].repeat = 1; + memcpy(mef_entry->filter[filt_num].byte_seq, + (u8 *)&ips[i], sizeof(ips[i])); + mef_entry->filter[filt_num]. + byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = + sizeof(ips[i]); + mef_entry->filter[filt_num].offset = 46; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + if (filt_num) { + mef_entry->filter[filt_num].filt_action = + TYPE_OR; + } + filt_num++; + } + + mef_entry->filter[filt_num].repeat = 1; + mef_entry->filter[filt_num].byte_seq[0] = 0x08; + mef_entry->filter[filt_num].byte_seq[1] = 0x06; + mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = 2; + mef_entry->filter[filt_num].offset = 20; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + mef_entry->filter[filt_num].filt_action = TYPE_AND; +} + +static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv, + struct mwifiex_ds_mef_cfg *mef_cfg, + struct mwifiex_mef_entry *mef_entry, + struct cfg80211_wowlan *wowlan) +{ + int i, filt_num = 0, ret = 0; + bool first_pat = true; + u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; + const u8 ipv4_mc_mac[] = {0x33, 0x33}; + const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; + + mef_entry->mode = MEF_MODE_HOST_SLEEP; + mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST; + + for (i = 0; i < wowlan->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!mwifiex_is_pattern_supported(&wowlan->patterns[i], + byte_seq, + MWIFIEX_MEF_MAX_BYTESEQ)) { + mwifiex_dbg(priv->adapter, ERROR, + "Pattern not supported\n"); + return -EOPNOTSUPP; + } + + if (!wowlan->patterns[i].pkt_offset) { + if (!(byte_seq[0] & 0x01) && + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) { + mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST; + continue; + } else if (is_broadcast_ether_addr(byte_seq)) { + mef_cfg->criteria |= MWIFIEX_CRITERIA_BROADCAST; + continue; + } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) { + mef_cfg->criteria |= MWIFIEX_CRITERIA_MULTICAST; + continue; + } + } + mef_entry->filter[filt_num].repeat = 1; + mef_entry->filter[filt_num].offset = + wowlan->patterns[i].pkt_offset; + memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq, + sizeof(byte_seq)); + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + + if (first_pat) + first_pat = false; + else + mef_entry->filter[filt_num].filt_action = TYPE_AND; + + filt_num++; + } + + if (wowlan->magic_pkt) { + mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST; + mef_entry->filter[filt_num].repeat = 16; + memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, + ETH_ALEN); + mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = + ETH_ALEN; + mef_entry->filter[filt_num].offset = 28; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + if (filt_num) + mef_entry->filter[filt_num].filt_action = TYPE_OR; + + filt_num++; + mef_entry->filter[filt_num].repeat = 16; + memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, + ETH_ALEN); + mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = + ETH_ALEN; + mef_entry->filter[filt_num].offset = 56; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + mef_entry->filter[filt_num].filt_action = TYPE_OR; + } + return ret; +} + +static int mwifiex_set_mef_filter(struct mwifiex_private *priv, + struct cfg80211_wowlan *wowlan) +{ + int ret = 0, num_entries = 1; + struct mwifiex_ds_mef_cfg mef_cfg; + struct mwifiex_mef_entry *mef_entry; + + if (wowlan->n_patterns || wowlan->magic_pkt) + num_entries++; + + mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL); + if (!mef_entry) + return -ENOMEM; + + memset(&mef_cfg, 0, sizeof(mef_cfg)); + mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST | + MWIFIEX_CRITERIA_UNICAST; + mef_cfg.num_entries = num_entries; + mef_cfg.mef_entry = mef_entry; + + mwifiex_set_auto_arp_mef_entry(priv, &mef_entry[0]); + + if (wowlan->n_patterns || wowlan->magic_pkt) { + ret = mwifiex_set_wowlan_mef_entry(priv, &mef_cfg, + &mef_entry[1], wowlan); + if (ret) + goto err; + } + + if (!mef_cfg.criteria) + mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST | + MWIFIEX_CRITERIA_UNICAST | + MWIFIEX_CRITERIA_MULTICAST; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG, + HostCmd_ACT_GEN_SET, 0, + &mef_cfg, true); + +err: + kfree(mef_entry); + return ret; +} + +static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wowlan) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_ds_hs_cfg hs_cfg; + int i, ret = 0; + struct mwifiex_private *priv; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + mwifiex_abort_cac(priv); + } + + mwifiex_cancel_all_pending_cmd(adapter); + + if (!wowlan) { + mwifiex_dbg(adapter, ERROR, + "None of the WOWLAN triggers enabled\n"); + return 0; + } + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + + if (!priv->media_connected) { + mwifiex_dbg(adapter, ERROR, + "Can not configure WOWLAN in disconnected state\n"); + return 0; + } + + ret = mwifiex_set_mef_filter(priv, wowlan); + if (ret) { + mwifiex_dbg(adapter, ERROR, "Failed to set MEF filter\n"); + return ret; + } + + if (wowlan->disconnect) { + memset(&hs_cfg, 0, sizeof(hs_cfg)); + hs_cfg.is_invoke_hostcmd = false; + hs_cfg.conditions = HS_CFG_COND_MAC_EVENT; + hs_cfg.gpio = HS_CFG_GPIO_DEF; + hs_cfg.gap = HS_CFG_GAP_DEF; + ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, + MWIFIEX_SYNC_CMD, &hs_cfg); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "Failed to set HS params\n"); + return ret; + } + } + + return ret; +} + +static int mwifiex_cfg80211_resume(struct wiphy *wiphy) +{ + return 0; +} + +static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy, + bool enabled) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + + device_set_wakeup_enable(adapter->dev, enabled); +} +#endif + +static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq) +{ + const u8 ipv4_mc_mac[] = {0x33, 0x33}; + const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; + const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff}; + + if ((byte_seq[0] & 0x01) && + (byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 1)) + return PACKET_TYPE_UNICAST; + else if (!memcmp(byte_seq, bc_mac, 4)) + return PACKET_TYPE_BROADCAST; + else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3)) + return PACKET_TYPE_MULTICAST; + + return 0; +} + +static int +mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv, + struct cfg80211_coalesce_rules *crule, + struct mwifiex_coalesce_rule *mrule) +{ + u8 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ + 1]; + struct filt_field_param *param; + int i; + + mrule->max_coalescing_delay = crule->delay; + + param = mrule->params; + + for (i = 0; i < crule->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!mwifiex_is_pattern_supported(&crule->patterns[i], + byte_seq, + MWIFIEX_COALESCE_MAX_BYTESEQ)) { + mwifiex_dbg(priv->adapter, ERROR, + "Pattern not supported\n"); + return -EOPNOTSUPP; + } + + if (!crule->patterns[i].pkt_offset) { + u8 pkt_type; + + pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq); + if (pkt_type && mrule->pkt_type) { + mwifiex_dbg(priv->adapter, ERROR, + "Multiple packet types not allowed\n"); + return -EOPNOTSUPP; + } else if (pkt_type) { + mrule->pkt_type = pkt_type; + continue; + } + } + + if (crule->condition == NL80211_COALESCE_CONDITION_MATCH) + param->operation = RECV_FILTER_MATCH_TYPE_EQ; + else + param->operation = RECV_FILTER_MATCH_TYPE_NE; + + param->operand_len = byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ]; + memcpy(param->operand_byte_stream, byte_seq, + param->operand_len); + param->offset = crule->patterns[i].pkt_offset; + param++; + + mrule->num_of_fields++; + } + + if (!mrule->pkt_type) { + mwifiex_dbg(priv->adapter, ERROR, + "Packet type can not be determined\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, + struct cfg80211_coalesce *coalesce) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + int i, ret; + struct mwifiex_ds_coalesce_cfg coalesce_cfg; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + + memset(&coalesce_cfg, 0, sizeof(coalesce_cfg)); + if (!coalesce) { + mwifiex_dbg(adapter, WARN, + "Disable coalesce and reset all previous rules\n"); + return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, + HostCmd_ACT_GEN_SET, 0, + &coalesce_cfg, true); + } + + coalesce_cfg.num_of_rules = coalesce->n_rules; + for (i = 0; i < coalesce->n_rules; i++) { + ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i], + &coalesce_cfg.rule[i]); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "Recheck the patterns provided for rule %d\n", + i + 1); + return ret; + } + } + + return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, + HostCmd_ACT_GEN_SET, 0, &coalesce_cfg, true); +} + +/* cfg80211 ops handler for tdls_mgmt. + * Function prepares TDLS action frame packets and forwards them to FW + */ +static int +mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + bool initiator, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + int ret; + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return -ENOTSUPP; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Setup Request to %pM status_code=%d\n", + peer, status_code); + mwifiex_add_auto_tdls_peer(priv, peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_SETUP_RESPONSE: + mwifiex_add_auto_tdls_peer(priv, peer); + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Setup Response to %pM status_code=%d\n", + peer, status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_SETUP_CONFIRM: + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Confirm to %pM status_code=%d\n", peer, + status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_TEARDOWN: + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Tear down to %pM\n", peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Discovery Request to %pM\n", peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Discovery Response to %pM\n", peer); + ret = mwifiex_send_tdls_action_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "Unknown TDLS mgmt/action frame %pM\n", peer); + ret = -EINVAL; + break; + } + + return ret; +} + +static int +mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, enum nl80211_tdls_operation action) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return -ENOTSUPP; + + mwifiex_dbg(priv->adapter, MSG, + "TDLS peer=%pM, oper=%d\n", peer, action); + + switch (action) { + case NL80211_TDLS_ENABLE_LINK: + action = MWIFIEX_TDLS_ENABLE_LINK; + break; + case NL80211_TDLS_DISABLE_LINK: + action = MWIFIEX_TDLS_DISABLE_LINK; + break; + case NL80211_TDLS_TEARDOWN: + /* shouldn't happen!*/ + mwifiex_dbg(priv->adapter, ERROR, + "tdls_oper: teardown from driver not supported\n"); + return -EINVAL; + case NL80211_TDLS_SETUP: + /* shouldn't happen!*/ + mwifiex_dbg(priv->adapter, ERROR, + "tdls_oper: setup from driver not supported\n"); + return -EINVAL; + case NL80211_TDLS_DISCOVERY_REQ: + /* shouldn't happen!*/ + mwifiex_dbg(priv->adapter, ERROR, + "tdls_oper: discovery from driver not supported\n"); + return -EINVAL; + default: + mwifiex_dbg(priv->adapter, ERROR, + "tdls_oper: operation not supported\n"); + return -ENOTSUPP; + } + + return mwifiex_tdls_oper(priv, peer, action); +} + +static int +mwifiex_cfg80211_tdls_chan_switch(struct wiphy *wiphy, struct net_device *dev, + const u8 *addr, u8 oper_class, + struct cfg80211_chan_def *chandef) +{ + struct mwifiex_sta_node *sta_ptr; + unsigned long flags; + u16 chan; + u8 second_chan_offset, band; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, addr); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + if (!sta_ptr) { + wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n", + __func__, addr); + return -ENOENT; + } + + if (!(sta_ptr->tdls_cap.extcap.ext_capab[3] & + WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)) { + wiphy_err(wiphy, "%pM do not support tdls cs\n", addr); + return -ENOENT; + } + + if (sta_ptr->tdls_status == TDLS_CHAN_SWITCHING || + sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) { + wiphy_err(wiphy, "channel switch is running, abort request\n"); + return -EALREADY; + } + + chan = chandef->chan->hw_value; + second_chan_offset = mwifiex_get_sec_chan_offset(chan); + band = chandef->chan->band; + mwifiex_start_tdls_cs(priv, addr, chan, second_chan_offset, band); + + return 0; +} + +static void +mwifiex_cfg80211_tdls_cancel_chan_switch(struct wiphy *wiphy, + struct net_device *dev, + const u8 *addr) +{ + struct mwifiex_sta_node *sta_ptr; + unsigned long flags; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, addr); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + if (!sta_ptr) { + wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n", + __func__, addr); + } else if (!(sta_ptr->tdls_status == TDLS_CHAN_SWITCHING || + sta_ptr->tdls_status == TDLS_IN_BASE_CHAN || + sta_ptr->tdls_status == TDLS_IN_OFF_CHAN)) { + wiphy_err(wiphy, "tdls chan switch not initialize by %pM\n", + addr); + } else + mwifiex_stop_tdls_cs(priv, addr); +} + +static int +mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_parameters *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) + return -ENOTSUPP; + + return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK); +} + +static int +mwifiex_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + struct ieee_types_header *chsw_ie; + struct ieee80211_channel_sw_ie *channel_sw; + int chsw_msec; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (priv->adapter->scan_processing) { + mwifiex_dbg(priv->adapter, ERROR, + "radar detection: scan in process...\n"); + return -EBUSY; + } + + if (priv->wdev.cac_started) + return -EBUSY; + + if (cfg80211_chandef_identical(¶ms->chandef, + &priv->dfs_chandef)) + return -EINVAL; + + chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, + params->beacon_csa.tail, + params->beacon_csa.tail_len); + if (!chsw_ie) { + mwifiex_dbg(priv->adapter, ERROR, + "Could not parse channel switch announcement IE\n"); + return -EINVAL; + } + + channel_sw = (void *)(chsw_ie + 1); + if (channel_sw->mode) { + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); + } + + if (mwifiex_del_mgmt_ies(priv)) + mwifiex_dbg(priv->adapter, ERROR, + "Failed to delete mgmt IEs!\n"); + + if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon_csa)) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: setting mgmt ies failed\n", __func__); + return -EFAULT; + } + + memcpy(&priv->dfs_chandef, ¶ms->chandef, sizeof(priv->dfs_chandef)); + memcpy(&priv->beacon_after, ¶ms->beacon_after, + sizeof(priv->beacon_after)); + + chsw_msec = max(channel_sw->count * priv->bss_cfg.beacon_period, 100); + queue_delayed_work(priv->dfs_chan_sw_workqueue, &priv->dfs_chan_sw_work, + msecs_to_jiffies(chsw_msec)); + return 0; +} + +static int mwifiex_cfg80211_get_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + struct mwifiex_bssdescriptor *curr_bss; + struct ieee80211_channel *chan; + u8 second_chan_offset; + enum nl80211_channel_type chan_type; + enum ieee80211_band band; + int freq; + int ret = -ENODATA; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP && + cfg80211_chandef_valid(&priv->bss_chandef)) { + *chandef = priv->bss_chandef; + ret = 0; + } else if (priv->media_connected) { + curr_bss = &priv->curr_bss_params.bss_descriptor; + band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + freq = ieee80211_channel_to_frequency(curr_bss->channel, band); + chan = ieee80211_get_channel(wiphy, freq); + + if (curr_bss->bcn_ht_oper) { + second_chan_offset = curr_bss->bcn_ht_oper->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_OFFSET; + chan_type = mwifiex_sec_chan_offset_to_chan_type + (second_chan_offset); + cfg80211_chandef_create(chandef, chan, chan_type); + } else { + cfg80211_chandef_create(chandef, chan, + NL80211_CHAN_NO_HT); + } + ret = 0; + } + + return ret; +} + +static int +mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + u32 cac_time_ms) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_radar_params radar_params; + + if (priv->adapter->scan_processing) { + mwifiex_dbg(priv->adapter, ERROR, + "radar detection: scan already in process...\n"); + return -EBUSY; + } + + if (!mwifiex_is_11h_active(priv)) { + mwifiex_dbg(priv->adapter, INFO, + "Enable 11h extensions in FW\n"); + if (mwifiex_11h_activate(priv, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to activate 11h extensions!!"); + return -1; + } + priv->state_11h.is_11h_active = true; + } + + memset(&radar_params, 0, sizeof(struct mwifiex_radar_params)); + radar_params.chandef = chandef; + radar_params.cac_time_ms = cac_time_ms; + + memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef)); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, &radar_params, true)) + return -1; + + queue_delayed_work(priv->dfs_cac_workqueue, &priv->dfs_cac_work, + msecs_to_jiffies(cac_time_ms)); + return 0; +} + +static int +mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, + struct station_parameters *params) +{ + int ret; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + /* we support change_station handler only for TDLS peers*/ + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) + return -ENOTSUPP; + + priv->sta_params = params; + + ret = mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CONFIG_LINK); + priv->sta_params = NULL; + + return ret; +} + +/* station cfg80211 operations */ +static struct cfg80211_ops mwifiex_cfg80211_ops = { + .add_virtual_intf = mwifiex_add_virtual_intf, + .del_virtual_intf = mwifiex_del_virtual_intf, + .change_virtual_intf = mwifiex_cfg80211_change_virtual_intf, + .scan = mwifiex_cfg80211_scan, + .connect = mwifiex_cfg80211_connect, + .disconnect = mwifiex_cfg80211_disconnect, + .get_station = mwifiex_cfg80211_get_station, + .dump_station = mwifiex_cfg80211_dump_station, + .dump_survey = mwifiex_cfg80211_dump_survey, + .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params, + .join_ibss = mwifiex_cfg80211_join_ibss, + .leave_ibss = mwifiex_cfg80211_leave_ibss, + .add_key = mwifiex_cfg80211_add_key, + .del_key = mwifiex_cfg80211_del_key, + .mgmt_tx = mwifiex_cfg80211_mgmt_tx, + .mgmt_frame_register = mwifiex_cfg80211_mgmt_frame_register, + .remain_on_channel = mwifiex_cfg80211_remain_on_channel, + .cancel_remain_on_channel = mwifiex_cfg80211_cancel_remain_on_channel, + .set_default_key = mwifiex_cfg80211_set_default_key, + .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt, + .set_tx_power = mwifiex_cfg80211_set_tx_power, + .set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask, + .start_ap = mwifiex_cfg80211_start_ap, + .stop_ap = mwifiex_cfg80211_stop_ap, + .change_beacon = mwifiex_cfg80211_change_beacon, + .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config, + .set_antenna = mwifiex_cfg80211_set_antenna, + .del_station = mwifiex_cfg80211_del_station, +#ifdef CONFIG_PM + .suspend = mwifiex_cfg80211_suspend, + .resume = mwifiex_cfg80211_resume, + .set_wakeup = mwifiex_cfg80211_set_wakeup, +#endif + .set_coalesce = mwifiex_cfg80211_set_coalesce, + .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt, + .tdls_oper = mwifiex_cfg80211_tdls_oper, + .tdls_channel_switch = mwifiex_cfg80211_tdls_chan_switch, + .tdls_cancel_channel_switch = mwifiex_cfg80211_tdls_cancel_chan_switch, + .add_station = mwifiex_cfg80211_add_station, + .change_station = mwifiex_cfg80211_change_station, + .get_channel = mwifiex_cfg80211_get_channel, + .start_radar_detection = mwifiex_cfg80211_start_radar_detection, + .channel_switch = mwifiex_cfg80211_channel_switch, +}; + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support mwifiex_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = MWIFIEX_MEF_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, + .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, +}; +#endif + +static bool mwifiex_is_valid_alpha2(const char *alpha2) +{ + if (!alpha2 || strlen(alpha2) != 2) + return false; + + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return true; + + return false; +} + +static const struct wiphy_coalesce_support mwifiex_coalesce_support = { + .n_rules = MWIFIEX_COALESCE_MAX_RULES, + .max_delay = MWIFIEX_MAX_COALESCING_DELAY, + .n_patterns = MWIFIEX_COALESCE_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, + .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, +}; + +int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter) +{ + u32 n_channels_bg, n_channels_a = 0; + + n_channels_bg = mwifiex_band_2ghz.n_channels; + + if (adapter->config_bands & BAND_A) + n_channels_a = mwifiex_band_5ghz.n_channels; + + adapter->num_in_chan_stats = max_t(u32, n_channels_bg, n_channels_a); + adapter->chan_stats = vmalloc(sizeof(*adapter->chan_stats) * + adapter->num_in_chan_stats); + + if (!adapter->chan_stats) + return -ENOMEM; + + return 0; +} + +/* + * This function registers the device with CFG802.11 subsystem. + * + * The function creates the wireless device/wiphy, populates it with + * default parameters and handler function pointers, and finally + * registers the device. + */ + +int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) +{ + int ret; + void *wdev_priv; + 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, + sizeof(struct mwifiex_adapter *)); + if (!wiphy) { + mwifiex_dbg(adapter, ERROR, + "%s: creating new wiphy\n", __func__); + return -ENOMEM; + } + wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; + wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; + wiphy->mgmt_stypes = mwifiex_mgmt_stypes; + wiphy->max_remain_on_channel_duration = 5000; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_AP); + + wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz; + if (adapter->config_bands & BAND_A) + wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz; + else + wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; + + if (adapter->drcs_enabled && ISSUPP_DRCS_ENABLED(adapter->fw_cap_info)) + wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_drcs; + else if (adapter->is_hw_11ac_capable) + wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_vht; + else + wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta; + wiphy->n_iface_combinations = 1; + + /* Initialize cipher suits */ + wiphy->cipher_suites = mwifiex_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); + + ether_addr_copy(wiphy->perm_addr, adapter->perm_addr); + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | + WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_HAS_CHANNEL_SWITCH | + WIPHY_FLAG_PS_ON_BY_DEFAULT; + + if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_TDLS_EXTERNAL_SETUP; + +#ifdef CONFIG_PM + wiphy->wowlan = &mwifiex_wowlan_support; +#endif + + wiphy->coalesce = &mwifiex_coalesce_support; + + wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + + wiphy->available_antennas_tx = BIT(adapter->number_of_antenna) - 1; + wiphy->available_antennas_rx = BIT(adapter->number_of_antenna) - 1; + + wiphy->features |= NL80211_FEATURE_HT_IBSS | + NL80211_FEATURE_INACTIVITY_TIMER | + NL80211_FEATURE_NEED_OBSS_SCAN; + + if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) + wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH; + + if (adapter->fw_api_ver == MWIFIEX_FW_V15) + wiphy->features |= NL80211_FEATURE_SK_TX_STATUS; + + /* Reserve space for mwifiex specific private data for BSS */ + wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv); + + wiphy->reg_notifier = mwifiex_reg_notifier; + + /* Set struct mwifiex_adapter pointer in wiphy_priv */ + wdev_priv = wiphy_priv(wiphy); + *(unsigned long *)wdev_priv = (unsigned long)adapter; + + set_wiphy_dev(wiphy, priv->adapter->dev); + + ret = wiphy_register(wiphy); + if (ret < 0) { + mwifiex_dbg(adapter, ERROR, + "%s: wiphy_register failed: %d\n", __func__, ret); + wiphy_free(wiphy); + return ret; + } + + if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) { + mwifiex_dbg(adapter, INFO, + "driver hint alpha2: %2.2s\n", reg_alpha2); + regulatory_hint(wiphy, reg_alpha2); + } else { + country_code = mwifiex_11d_code_2_region(adapter->region_code); + if (country_code) + mwifiex_dbg(adapter, WARN, + "ignoring F/W country code %2.2s\n", + country_code); + } + + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, FRAG_THRESH_I, &thr, true); + wiphy->frag_threshold = thr; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, RTS_THRESH_I, &thr, true); + wiphy->rts_threshold = thr; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true); + wiphy->retry_short = (u8) retry; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry, true); + wiphy->retry_long = (u8) retry; + + adapter->wiphy = wiphy; + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.h b/drivers/net/wireless/marvell/mwifiex/cfg80211.h new file mode 100644 index 000000000000..908367857d58 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.h @@ -0,0 +1,29 @@ +/* + * Marvell Wireless LAN device driver: CFG80211 + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef __MWIFIEX_CFG80211__ +#define __MWIFIEX_CFG80211__ + +#include + +#include "main.h" + +int mwifiex_register_cfg80211(struct mwifiex_adapter *); + +#endif diff --git a/drivers/net/wireless/marvell/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c new file mode 100644 index 000000000000..3ddb8ec676ed --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/cfp.c @@ -0,0 +1,537 @@ +/* + * Marvell Wireless LAN device driver: Channel, Frequence and Power + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cfg80211.h" + +/* 100mW */ +#define MWIFIEX_TX_PWR_DEFAULT 20 +/* 100mW */ +#define MWIFIEX_TX_PWR_US_DEFAULT 20 +/* 50mW */ +#define MWIFIEX_TX_PWR_JP_DEFAULT 16 +/* 100mW */ +#define MWIFIEX_TX_PWR_FR_100MW 20 +/* 10mW */ +#define MWIFIEX_TX_PWR_FR_10MW 10 +/* 100mW */ +#define MWIFIEX_TX_PWR_EMEA_DEFAULT 20 + +static u8 adhoc_rates_b[B_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, 0 }; + +static u8 adhoc_rates_g[G_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, + 0xb0, 0x48, 0x60, 0x6c, 0 }; + +static u8 adhoc_rates_bg[BG_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, + 0x0c, 0x12, 0x18, 0x24, + 0x30, 0x48, 0x60, 0x6c, 0 }; + +static u8 adhoc_rates_a[A_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, + 0xb0, 0x48, 0x60, 0x6c, 0 }; +static u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, + 0xb0, 0x48, 0x60, 0x6c, 0 }; +static u16 mwifiex_data_rates[MWIFIEX_SUPPORTED_RATES_EXT] = { 0x02, 0x04, + 0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18, + 0x24, 0x30, 0x48, 0x60, 0x6C, 0x90, + 0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68, + 0x75, 0x82, 0x0C, 0x1B, 0x36, 0x51, + 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00 }; + +static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 }; + +static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, + 0x30, 0x48, 0x60, 0x6c, 0 }; + +static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c, + 0x12, 0x16, 0x18, 0x24, 0x30, 0x48, + 0x60, 0x6c, 0 }; + +u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30, + 0x32, 0x40, 0x41, 0xff }; + +static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; + +/* For every mcs_rate line, the first 8 bytes are for stream 1x1, + * and all 16 bytes are for stream 2x2. + */ +static const u16 mcs_rate[4][16] = { + /* LGI 40M */ + { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, + 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, + + /* SGI 40M */ + { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, + 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, + + /* LGI 20M */ + { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, + 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, + + /* SGI 20M */ + { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, + 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } +}; + +/* AC rates */ +static const u16 ac_mcs_rate_nss1[8][10] = { + /* LG 160M */ + { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, + 0x492, 0x57C, 0x618 }, + + /* SG 160M */ + { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, + 0x514, 0x618, 0x6C6 }, + + /* LG 80M */ + { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, + 0x249, 0x2BE, 0x30C }, + + /* SG 80M */ + { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, + 0x28A, 0x30C, 0x363 }, + + /* LG 40M */ + { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, + 0x10E, 0x144, 0x168 }, + + /* SG 40M */ + { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, + 0x12C, 0x168, 0x190 }, + + /* LG 20M */ + { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 }, + + /* SG 20M */ + { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 }, +}; + +/* NSS2 note: the value in the table is 2 multiplier of the actual rate */ +static const u16 ac_mcs_rate_nss2[8][10] = { + /* LG 160M */ + { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, + 0x924, 0xAF8, 0xC30 }, + + /* SG 160M */ + { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, + 0xA28, 0xC30, 0xD8B }, + + /* LG 80M */ + { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, + 0x492, 0x57C, 0x618 }, + + /* SG 80M */ + { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, + 0x514, 0x618, 0x6C6 }, + + /* LG 40M */ + { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, + 0x21C, 0x288, 0x2D0 }, + + /* SG 40M */ + { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, + 0x258, 0x2D0, 0x320 }, + + /* LG 20M */ + { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, + 0x138, 0x00 }, + + /* SG 20M */ + { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, + 0x15B, 0x00 }, +}; + +struct region_code_mapping { + u8 code; + u8 region[IEEE80211_COUNTRY_STRING_LEN]; +}; + +static struct region_code_mapping region_code_mapping_t[] = { + { 0x10, "US " }, /* US FCC */ + { 0x20, "CA " }, /* IC Canada */ + { 0x30, "EU " }, /* ETSI */ + { 0x31, "ES " }, /* Spain */ + { 0x32, "FR " }, /* France */ + { 0x40, "JP " }, /* Japan */ + { 0x41, "JP " }, /* Japan */ + { 0x50, "CN " }, /* China */ +}; + +/* This function converts integer code to region string */ +u8 *mwifiex_11d_code_2_region(u8 code) +{ + u8 i; + u8 size = sizeof(region_code_mapping_t)/ + sizeof(struct region_code_mapping); + + /* Look for code in mapping table */ + for (i = 0; i < size; i++) + if (region_code_mapping_t[i].code == code) + return region_code_mapping_t[i].region; + + return NULL; +} + +/* + * This function maps an index in supported rates table into + * the corresponding data rate. + */ +u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, + u8 index, u8 ht_info) +{ + u32 rate = 0; + u8 mcs_index = 0; + u8 bw = 0; + u8 gi = 0; + + if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_VHT) { + mcs_index = min(index & 0xF, 9); + + /* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */ + bw = (ht_info & 0xC) >> 2; + + /* LGI: gi =0, SGI: gi = 1 */ + gi = (ht_info & 0x10) >> 4; + + if ((index >> 4) == 1) /* NSS = 2 */ + rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index]; + else /* NSS = 1 */ + rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index]; + } else if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_HT) { + /* 20M: bw=0, 40M: bw=1 */ + bw = (ht_info & 0xC) >> 2; + + /* LGI: gi =0, SGI: gi = 1 */ + gi = (ht_info & 0x10) >> 4; + + if (index == MWIFIEX_RATE_BITMAP_MCS0) { + if (gi == 1) + rate = 0x0D; /* MCS 32 SGI rate */ + else + rate = 0x0C; /* MCS 32 LGI rate */ + } else if (index < 16) { + if ((bw == 1) || (bw == 0)) + rate = mcs_rate[2 * (1 - bw) + gi][index]; + else + rate = mwifiex_data_rates[0]; + } else { + rate = mwifiex_data_rates[0]; + } + } else { + /* 11n non-HT rates */ + if (index >= MWIFIEX_SUPPORTED_RATES_EXT) + index = 0; + rate = mwifiex_data_rates[index]; + } + + return rate; +} + +/* This function maps an index in supported rates table into + * the corresponding data rate. + */ +u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, + u8 index, u8 ht_info) +{ + u32 mcs_num_supp = + (priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8; + u32 rate; + + if (priv->adapter->is_hw_11ac_capable) + return mwifiex_index_to_acs_data_rate(priv, index, ht_info); + + if (ht_info & BIT(0)) { + if (index == MWIFIEX_RATE_BITMAP_MCS0) { + if (ht_info & BIT(2)) + rate = 0x0D; /* MCS 32 SGI rate */ + else + rate = 0x0C; /* MCS 32 LGI rate */ + } else if (index < mcs_num_supp) { + if (ht_info & BIT(1)) { + if (ht_info & BIT(2)) + /* SGI, 40M */ + rate = mcs_rate[1][index]; + else + /* LGI, 40M */ + rate = mcs_rate[0][index]; + } else { + if (ht_info & BIT(2)) + /* SGI, 20M */ + rate = mcs_rate[3][index]; + else + /* LGI, 20M */ + rate = mcs_rate[2][index]; + } + } else + rate = mwifiex_data_rates[0]; + } else { + if (index >= MWIFIEX_SUPPORTED_RATES_EXT) + index = 0; + rate = mwifiex_data_rates[index]; + } + return rate; +} + +/* + * This function returns the current active data rates. + * + * The result may vary depending upon connection status. + */ +u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates) +{ + if (!priv->media_connected) + return mwifiex_get_supported_rates(priv, rates); + else + return mwifiex_copy_rates(rates, 0, + priv->curr_bss_params.data_rates, + priv->curr_bss_params.num_of_rates); +} + +/* + * This function locates the Channel-Frequency-Power triplet based upon + * band and channel/frequency parameters. + */ +struct mwifiex_chan_freq_power * +mwifiex_get_cfp(struct mwifiex_private *priv, u8 band, u16 channel, u32 freq) +{ + struct mwifiex_chan_freq_power *cfp = NULL; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch = NULL; + int i; + + if (!channel && !freq) + return cfp; + + if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG) + sband = priv->wdev.wiphy->bands[IEEE80211_BAND_2GHZ]; + else + sband = priv->wdev.wiphy->bands[IEEE80211_BAND_5GHZ]; + + if (!sband) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: cannot find cfp by band %d\n", + __func__, band); + return cfp; + } + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (freq) { + if (ch->center_freq == freq) + break; + } else { + /* find by valid channel*/ + if (ch->hw_value == channel || + channel == FIRST_VALID_CHANNEL) + break; + } + } + if (i == sband->n_channels) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: cannot find cfp by band %d\t" + "& channel=%d freq=%d\n", + __func__, band, channel, freq); + } else { + if (!ch) + return cfp; + + priv->cfp.channel = ch->hw_value; + priv->cfp.freq = ch->center_freq; + priv->cfp.max_tx_power = ch->max_power; + cfp = &priv->cfp; + } + + return cfp; +} + +/* + * This function checks if the data rate is set to auto. + */ +u8 +mwifiex_is_rate_auto(struct mwifiex_private *priv) +{ + u32 i; + int rate_num = 0; + + for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++) + if (priv->bitmap_rates[i]) + rate_num++; + + if (rate_num > 1) + return true; + else + return false; +} + +/* This function gets the supported data rates from bitmask inside + * cfg80211_scan_request. + */ +u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv, + u8 *rates, u8 radio_type) +{ + struct wiphy *wiphy = priv->adapter->wiphy; + struct cfg80211_scan_request *request = priv->scan_request; + u32 num_rates, rate_mask; + struct ieee80211_supported_band *sband; + int i; + + if (radio_type) { + sband = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (WARN_ON_ONCE(!sband)) + return 0; + rate_mask = request->rates[IEEE80211_BAND_5GHZ]; + } else { + sband = wiphy->bands[IEEE80211_BAND_2GHZ]; + if (WARN_ON_ONCE(!sband)) + return 0; + rate_mask = request->rates[IEEE80211_BAND_2GHZ]; + } + + num_rates = 0; + for (i = 0; i < sband->n_bitrates; i++) { + if ((BIT(i) & rate_mask) == 0) + continue; /* skip rate */ + rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5); + } + + return num_rates; +} + +/* This function gets the supported data rates. The function works in + * both Ad-Hoc and infra mode by printing the band and returning the + * data rates. + */ +u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) +{ + u32 k = 0; + struct mwifiex_adapter *adapter = priv->adapter; + + if (priv->bss_mode == NL80211_IFTYPE_STATION || + priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { + switch (adapter->config_bands) { + case BAND_B: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_b\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_b, + sizeof(supported_rates_b)); + break; + case BAND_G: + case BAND_G | BAND_GN: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_g\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_g, + sizeof(supported_rates_g)); + break; + case BAND_B | BAND_G: + case BAND_A | BAND_B | BAND_G: + case BAND_A | BAND_B: + case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN: + case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC: + case BAND_B | BAND_G | BAND_GN: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_bg\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_bg, + sizeof(supported_rates_bg)); + break; + case BAND_A: + case BAND_A | BAND_G: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_a\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_a, + sizeof(supported_rates_a)); + break; + case BAND_AN: + case BAND_A | BAND_AN: + case BAND_A | BAND_AN | BAND_AAC: + case BAND_A | BAND_G | BAND_AN | BAND_GN: + case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_a\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_a, + sizeof(supported_rates_a)); + break; + case BAND_GN: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_n\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_n, + sizeof(supported_rates_n)); + break; + } + } else { + /* Ad-hoc mode */ + switch (adapter->adhoc_start_band) { + case BAND_B: + mwifiex_dbg(adapter, INFO, "info: adhoc B\n"); + k = mwifiex_copy_rates(rates, k, adhoc_rates_b, + sizeof(adhoc_rates_b)); + break; + case BAND_G: + case BAND_G | BAND_GN: + mwifiex_dbg(adapter, INFO, "info: adhoc G only\n"); + k = mwifiex_copy_rates(rates, k, adhoc_rates_g, + sizeof(adhoc_rates_g)); + break; + case BAND_B | BAND_G: + case BAND_B | BAND_G | BAND_GN: + mwifiex_dbg(adapter, INFO, "info: adhoc BG\n"); + k = mwifiex_copy_rates(rates, k, adhoc_rates_bg, + sizeof(adhoc_rates_bg)); + break; + case BAND_A: + case BAND_A | BAND_AN: + mwifiex_dbg(adapter, INFO, "info: adhoc A\n"); + k = mwifiex_copy_rates(rates, k, adhoc_rates_a, + sizeof(adhoc_rates_a)); + break; + } + } + + return k; +} + +u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, + u8 rx_rate, u8 rate_info) +{ + u8 rate_index = 0; + + /* HT40 */ + if ((rate_info & BIT(0)) && (rate_info & BIT(1))) + rate_index = MWIFIEX_RATE_INDEX_MCS0 + + MWIFIEX_BW20_MCS_NUM + rx_rate; + else if (rate_info & BIT(0)) /* HT20 */ + rate_index = MWIFIEX_RATE_INDEX_MCS0 + rx_rate; + else + rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ? + rx_rate - 1 : rx_rate; + + return rate_index; +} diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c new file mode 100644 index 000000000000..45ae38e32621 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -0,0 +1,1659 @@ +/* + * Marvell Wireless LAN device driver: commands and events + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11ac.h" + +/* + * This function initializes a command node. + * + * The actual allocation of the node is not done by this function. It only + * initiates a node by filling it with default parameters. Similarly, + * allocation of the different buffers used (IOCTL buffer, data buffer) are + * not done by this function either. + */ +static void +mwifiex_init_cmd_node(struct mwifiex_private *priv, + struct cmd_ctrl_node *cmd_node, + u32 cmd_oid, void *data_buf, bool sync) +{ + cmd_node->priv = priv; + cmd_node->cmd_oid = cmd_oid; + if (sync) { + cmd_node->wait_q_enabled = true; + cmd_node->cmd_wait_q_woken = false; + cmd_node->condition = &cmd_node->cmd_wait_q_woken; + } + cmd_node->data_buf = data_buf; + cmd_node->cmd_skb = cmd_node->skb; +} + +/* + * This function returns a command node from the free queue depending upon + * availability. + */ +static struct cmd_ctrl_node * +mwifiex_get_cmd_node(struct mwifiex_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node; + unsigned long flags; + + spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); + if (list_empty(&adapter->cmd_free_q)) { + mwifiex_dbg(adapter, ERROR, + "GET_CMD_NODE: cmd node not available\n"); + spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); + return NULL; + } + cmd_node = list_first_entry(&adapter->cmd_free_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); + + return cmd_node; +} + +/* + * This function cleans up a command node. + * + * The function resets the fields including the buffer pointers. + * This function does not try to free the buffers. They must be + * freed before calling this function. + * + * This function will however call the receive completion callback + * in case a response buffer is still available before resetting + * the pointer. + */ +static void +mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + cmd_node->cmd_oid = 0; + cmd_node->cmd_flag = 0; + cmd_node->data_buf = NULL; + cmd_node->wait_q_enabled = false; + + if (cmd_node->cmd_skb) + skb_trim(cmd_node->cmd_skb, 0); + + if (cmd_node->resp_skb) { + adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb); + cmd_node->resp_skb = NULL; + } +} + +/* + * This function sends a host command to the firmware. + * + * The function copies the host command into the driver command + * buffer, which will be transferred to the firmware later by the + * main thread. + */ +static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_ds_misc_cmd *pcmd_ptr) +{ + /* Copy the HOST command to command buffer */ + memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len); + mwifiex_dbg(priv->adapter, CMD, + "cmd: host cmd size = %d\n", pcmd_ptr->len); + return 0; +} + +/* + * This function downloads a command to the firmware. + * + * The function performs sanity tests, sets the command sequence + * number and size, converts the header fields to CPU format before + * sending. Afterwards, it logs the command ID and action for debugging + * and sets up the command timeout timer. + */ +static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, + struct cmd_ctrl_node *cmd_node) +{ + + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + struct host_cmd_ds_command *host_cmd; + uint16_t cmd_code; + uint16_t cmd_size; + unsigned long flags; + __le32 tmp; + + if (!adapter || !cmd_node) + return -1; + + host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); + + /* Sanity test */ + if (host_cmd == NULL || host_cmd->size == 0) { + mwifiex_dbg(adapter, ERROR, + "DNLD_CMD: host_cmd is null\t" + "or cmd size is 0, not sending\n"); + if (cmd_node->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + mwifiex_recycle_cmd_node(adapter, cmd_node); + return -1; + } + + cmd_code = le16_to_cpu(host_cmd->command); + cmd_size = le16_to_cpu(host_cmd->size); + + if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET && + cmd_code != HostCmd_CMD_FUNC_SHUTDOWN && + cmd_code != HostCmd_CMD_FUNC_INIT) { + mwifiex_dbg(adapter, ERROR, + "DNLD_CMD: FW in reset state, ignore cmd %#x\n", + cmd_code); + mwifiex_recycle_cmd_node(adapter, cmd_node); + queue_work(adapter->workqueue, &adapter->main_work); + return -1; + } + + /* Set command sequence number */ + adapter->seq_num++; + host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO + (adapter->seq_num, + cmd_node->priv->bss_num, + cmd_node->priv->bss_type)); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->curr_cmd = cmd_node; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + /* Adjust skb length */ + if (cmd_node->cmd_skb->len > cmd_size) + /* + * cmd_size is less than sizeof(struct host_cmd_ds_command). + * Trim off the unused portion. + */ + skb_trim(cmd_node->cmd_skb, cmd_size); + else if (cmd_node->cmd_skb->len < cmd_size) + /* + * cmd_size is larger than sizeof(struct host_cmd_ds_command) + * because we have appended custom IE TLV. Increase skb length + * accordingly. + */ + skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len); + + mwifiex_dbg(adapter, CMD, + "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", + cmd_code, + le16_to_cpu(*(__le16 *)((u8 *)host_cmd + S_DS_GEN)), + cmd_size, le16_to_cpu(host_cmd->seq_num)); + mwifiex_dbg_dump(adapter, CMD_D, "cmd buffer:", host_cmd, cmd_size); + + if (adapter->iface_type == MWIFIEX_USB) { + tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD); + skb_push(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN); + memcpy(cmd_node->cmd_skb->data, &tmp, MWIFIEX_TYPE_LEN); + adapter->cmd_sent = true; + ret = adapter->if_ops.host_to_card(adapter, + MWIFIEX_USB_EP_CMD_EVENT, + cmd_node->cmd_skb, NULL); + skb_pull(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN); + if (ret == -EBUSY) + cmd_node->cmd_skb = NULL; + } else { + skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN); + ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, + cmd_node->cmd_skb, NULL); + skb_pull(cmd_node->cmd_skb, INTF_HEADER_LEN); + } + + if (ret == -1) { + mwifiex_dbg(adapter, ERROR, + "DNLD_CMD: host to card failed\n"); + if (adapter->iface_type == MWIFIEX_USB) + adapter->cmd_sent = false; + if (cmd_node->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->curr_cmd = NULL; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + adapter->dbg.num_cmd_host_to_card_failure++; + return -1; + } + + /* Save the last command id and action to debug log */ + adapter->dbg.last_cmd_index = + (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code; + adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] = + le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)); + + /* Clear BSS_NO_BITS from HostCmd */ + cmd_code &= HostCmd_CMD_ID_MASK; + + /* Setup the timer after transmit command */ + mod_timer(&adapter->cmd_timer, + jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); + + return 0; +} + +/* + * This function downloads a sleep confirm command to the firmware. + * + * The function performs sanity tests, sets the command sequence + * number and size, converts the header fields to CPU format before + * sending. + * + * No responses are needed for sleep confirm command. + */ +static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) +{ + int ret; + struct mwifiex_private *priv; + struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = + (struct mwifiex_opt_sleep_confirm *) + adapter->sleep_cfm->data; + struct sk_buff *sleep_cfm_tmp; + __le32 tmp; + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + adapter->seq_num++; + sleep_cfm_buf->seq_num = + cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO + (adapter->seq_num, priv->bss_num, + priv->bss_type))); + + mwifiex_dbg(adapter, CMD, + "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", + le16_to_cpu(sleep_cfm_buf->command), + le16_to_cpu(sleep_cfm_buf->action), + le16_to_cpu(sleep_cfm_buf->size), + le16_to_cpu(sleep_cfm_buf->seq_num)); + mwifiex_dbg_dump(adapter, CMD_D, "SLEEP_CFM buffer: ", sleep_cfm_buf, + le16_to_cpu(sleep_cfm_buf->size)); + + if (adapter->iface_type == MWIFIEX_USB) { + sleep_cfm_tmp = + dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) + + MWIFIEX_TYPE_LEN); + skb_put(sleep_cfm_tmp, sizeof(struct mwifiex_opt_sleep_confirm) + + MWIFIEX_TYPE_LEN); + tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD); + memcpy(sleep_cfm_tmp->data, &tmp, MWIFIEX_TYPE_LEN); + memcpy(sleep_cfm_tmp->data + MWIFIEX_TYPE_LEN, + adapter->sleep_cfm->data, + sizeof(struct mwifiex_opt_sleep_confirm)); + ret = adapter->if_ops.host_to_card(adapter, + MWIFIEX_USB_EP_CMD_EVENT, + sleep_cfm_tmp, NULL); + if (ret != -EBUSY) + dev_kfree_skb_any(sleep_cfm_tmp); + } else { + skb_push(adapter->sleep_cfm, INTF_HEADER_LEN); + ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, + adapter->sleep_cfm, NULL); + skb_pull(adapter->sleep_cfm, INTF_HEADER_LEN); + } + + if (ret == -1) { + mwifiex_dbg(adapter, ERROR, "SLEEP_CFM: failed\n"); + adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; + return -1; + } + + if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl)) + /* Response is not needed for sleep confirm command */ + adapter->ps_state = PS_STATE_SLEEP; + else + adapter->ps_state = PS_STATE_SLEEP_CFM; + + if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) && + (adapter->is_hs_configured && + !adapter->sleep_period.period)) { + adapter->pm_wakeup_card_req = true; + mwifiex_hs_activated_event(mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), true); + } + + return ret; +} + +/* + * This function allocates the command buffers and links them to + * the command free queue. + * + * The driver uses a pre allocated number of command buffers, which + * are created at driver initializations and freed at driver cleanup. + * Every command needs to obtain a command buffer from this pool before + * it can be issued. The command free queue lists the command buffers + * currently free to use, while the command pending queue lists the + * command buffers already in use and awaiting handling. Command buffers + * are returned to the free queue after use. + */ +int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_array; + u32 i; + + /* Allocate and initialize struct cmd_ctrl_node */ + cmd_array = kcalloc(MWIFIEX_NUM_OF_CMD_BUFFER, + sizeof(struct cmd_ctrl_node), GFP_KERNEL); + if (!cmd_array) + return -ENOMEM; + + adapter->cmd_pool = cmd_array; + + /* Allocate and initialize command buffers */ + for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { + cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER); + if (!cmd_array[i].skb) { + mwifiex_dbg(adapter, ERROR, + "unable to allocate command buffer\n"); + return -ENOMEM; + } + } + + for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) + mwifiex_insert_cmd_to_free_q(adapter, &cmd_array[i]); + + return 0; +} + +/* + * This function frees the command buffers. + * + * The function calls the completion callback for all the command + * buffers that still have response buffers associated with them. + */ +int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_array; + u32 i; + + /* Need to check if cmd pool is allocated or not */ + if (!adapter->cmd_pool) { + mwifiex_dbg(adapter, FATAL, + "info: FREE_CMD_BUF: cmd_pool is null\n"); + return 0; + } + + cmd_array = adapter->cmd_pool; + + /* Release shared memory buffers */ + for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { + if (cmd_array[i].skb) { + mwifiex_dbg(adapter, CMD, + "cmd: free cmd buffer %d\n", i); + dev_kfree_skb_any(cmd_array[i].skb); + } + if (!cmd_array[i].resp_skb) + continue; + + if (adapter->iface_type == MWIFIEX_USB) + adapter->if_ops.cmdrsp_complete(adapter, + cmd_array[i].resp_skb); + else + dev_kfree_skb_any(cmd_array[i].resp_skb); + } + /* Release struct cmd_ctrl_node */ + if (adapter->cmd_pool) { + mwifiex_dbg(adapter, CMD, + "cmd: free cmd pool\n"); + kfree(adapter->cmd_pool); + adapter->cmd_pool = NULL; + } + + return 0; +} + +/* + * This function handles events generated by firmware. + * + * Event body of events received from firmware are not used (though they are + * saved), only the event ID is used. Some events are re-invoked by + * the driver, with a new event body. + * + * After processing, the function calls the completion callback + * for cleanup. + */ +int mwifiex_process_event(struct mwifiex_adapter *adapter) +{ + int ret; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + struct sk_buff *skb = adapter->event_skb; + u32 eventcause = adapter->event_cause; + struct mwifiex_rxinfo *rx_info; + + /* Save the last event to debug log */ + adapter->dbg.last_event_index = + (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_event[adapter->dbg.last_event_index] = + (u16) eventcause; + + /* Get BSS number and corresponding priv */ + priv = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause), + EVENT_GET_BSS_TYPE(eventcause)); + if (!priv) + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + /* Clear BSS_NO_BITS from event */ + eventcause &= EVENT_ID_MASK; + adapter->event_cause = eventcause; + + if (skb) { + rx_info = MWIFIEX_SKB_RXCB(skb); + memset(rx_info, 0, sizeof(*rx_info)); + rx_info->bss_num = priv->bss_num; + rx_info->bss_type = priv->bss_type; + mwifiex_dbg_dump(adapter, EVT_D, "Event Buf:", + skb->data, skb->len); + } + + mwifiex_dbg(adapter, EVENT, "EVENT: cause: %#x\n", eventcause); + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) + ret = mwifiex_process_uap_event(priv); + else + ret = mwifiex_process_sta_event(priv); + + adapter->event_cause = 0; + adapter->event_skb = NULL; + adapter->if_ops.event_complete(adapter, skb); + + return ret; +} + +/* + * This function prepares a command and send it to the firmware. + * + * Preparation includes - + * - Sanity tests to make sure the card is still present or the FW + * is not reset + * - Getting a new command node from the command free queue + * - Initializing the command node for default parameters + * - Fill up the non-default parameters and buffer pointers + * - Add the command to pending queue + */ +int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, + u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync) +{ + int ret; + struct mwifiex_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node; + struct host_cmd_ds_command *cmd_ptr; + + if (!adapter) { + pr_err("PREP_CMD: adapter is NULL\n"); + return -1; + } + + if (adapter->is_suspended) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: device in suspended state\n"); + return -1; + } + + if (adapter->hs_enabling && cmd_no != HostCmd_CMD_802_11_HS_CFG_ENH) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: host entering sleep state\n"); + return -1; + } + + if (adapter->surprise_removed) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: card is removed\n"); + return -1; + } + + if (adapter->is_cmd_timedout) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: FW is in bad state\n"); + return -1; + } + + if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) { + if (cmd_no != HostCmd_CMD_FUNC_INIT) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: FW in reset state\n"); + return -1; + } + } + + /* Get a new command node */ + cmd_node = mwifiex_get_cmd_node(adapter); + + if (!cmd_node) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: no free cmd node\n"); + return -1; + } + + /* Initialize the command node */ + mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf, sync); + + if (!cmd_node->cmd_skb) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: no free cmd buf\n"); + return -1; + } + + memset(skb_put(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command)), + 0, sizeof(struct host_cmd_ds_command)); + + cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->result = 0; + + /* Prepare command */ + if (cmd_no) { + switch (cmd_no) { + case HostCmd_CMD_UAP_SYS_CONFIG: + case HostCmd_CMD_UAP_BSS_START: + case HostCmd_CMD_UAP_BSS_STOP: + case HostCmd_CMD_UAP_STA_DEAUTH: + case HOST_CMD_APCMD_SYS_RESET: + case HOST_CMD_APCMD_STA_LIST: + ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action, + cmd_oid, data_buf, + cmd_ptr); + break; + default: + ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action, + cmd_oid, data_buf, + cmd_ptr); + break; + } + } else { + ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf); + cmd_node->cmd_flag |= CMD_F_HOSTCMD; + } + + /* Return error, since the command preparation failed */ + if (ret) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: cmd %#x preparation failed\n", + cmd_no); + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + return -1; + } + + /* Send command */ + if (cmd_no == HostCmd_CMD_802_11_SCAN || + cmd_no == HostCmd_CMD_802_11_SCAN_EXT) { + mwifiex_queue_scan_cmd(priv, cmd_node); + } else { + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + queue_work(adapter->workqueue, &adapter->main_work); + if (cmd_node->wait_q_enabled) + ret = mwifiex_wait_queue_complete(adapter, cmd_node); + } + + return ret; +} + +/* + * This function returns a command to the command free queue. + * + * The function also calls the completion callback if required, before + * cleaning the command node and re-inserting it into the free queue. + */ +void +mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + unsigned long flags; + + if (!cmd_node) + return; + + if (cmd_node->wait_q_enabled) + mwifiex_complete_cmd(adapter, cmd_node); + /* Clean the node */ + mwifiex_clean_cmd_node(adapter, cmd_node); + + /* Insert node into cmd_free_q */ + spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); + list_add_tail(&cmd_node->list, &adapter->cmd_free_q); + spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); +} + +/* This function reuses a command node. */ +void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + struct host_cmd_ds_command *host_cmd = (void *)cmd_node->cmd_skb->data; + + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + + atomic_dec(&adapter->cmd_pending); + mwifiex_dbg(adapter, CMD, + "cmd: FREE_CMD: cmd=%#x, cmd_pending=%d\n", + le16_to_cpu(host_cmd->command), + atomic_read(&adapter->cmd_pending)); +} + +/* + * This function queues a command to the command pending queue. + * + * This in effect adds the command to the command list to be executed. + * Exit PS command is handled specially, by placing it always to the + * front of the command queue. + */ +void +mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node, u32 add_tail) +{ + struct host_cmd_ds_command *host_cmd = NULL; + u16 command; + unsigned long flags; + + host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); + if (!host_cmd) { + mwifiex_dbg(adapter, ERROR, "QUEUE_CMD: host_cmd is NULL\n"); + return; + } + + command = le16_to_cpu(host_cmd->command); + + /* Exit_PS command needs to be queued in the header always. */ + if (command == HostCmd_CMD_802_11_PS_MODE_ENH) { + struct host_cmd_ds_802_11_ps_mode_enh *pm = + &host_cmd->params.psmode_enh; + if ((le16_to_cpu(pm->action) == DIS_PS) || + (le16_to_cpu(pm->action) == DIS_AUTO_PS)) { + if (adapter->ps_state != PS_STATE_AWAKE) + add_tail = false; + } + } + + spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + if (add_tail) + list_add_tail(&cmd_node->list, &adapter->cmd_pending_q); + else + list_add(&cmd_node->list, &adapter->cmd_pending_q); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + + atomic_inc(&adapter->cmd_pending); + mwifiex_dbg(adapter, CMD, + "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n", + command, atomic_read(&adapter->cmd_pending)); +} + +/* + * This function executes the next command in command pending queue. + * + * This function will fail if a command is already in processing stage, + * otherwise it will dequeue the first command from the command pending + * queue and send to the firmware. + * + * If the device is currently in host sleep mode, any commands, except the + * host sleep configuration command will de-activate the host sleep. For PS + * mode, the function will put the firmware back to sleep if applicable. + */ +int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + struct cmd_ctrl_node *cmd_node; + int ret = 0; + struct host_cmd_ds_command *host_cmd; + unsigned long cmd_flags; + unsigned long cmd_pending_q_flags; + + /* Check if already in processing */ + if (adapter->curr_cmd) { + mwifiex_dbg(adapter, FATAL, + "EXEC_NEXT_CMD: cmd in processing\n"); + return -1; + } + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + /* Check if any command is pending */ + spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); + if (list_empty(&adapter->cmd_pending_q)) { + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, + cmd_pending_q_flags); + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + return 0; + } + cmd_node = list_first_entry(&adapter->cmd_pending_q, + struct cmd_ctrl_node, list); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, + cmd_pending_q_flags); + + host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); + priv = cmd_node->priv; + + if (adapter->ps_state != PS_STATE_AWAKE) { + mwifiex_dbg(adapter, ERROR, + "%s: cannot send cmd in sleep state,\t" + "this should not happen\n", __func__); + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + return ret; + } + + spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, + cmd_pending_q_flags); + + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node); + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + /* Any command sent to the firmware when host is in sleep + * mode should de-configure host sleep. We should skip the + * host sleep configuration command itself though + */ + if (priv && (host_cmd->command != + cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) { + if (adapter->hs_activated) { + adapter->is_hs_configured = false; + mwifiex_hs_activated_event(priv, false); + } + } + + return ret; +} + +/* + * This function handles the command response. + * + * After processing, the function cleans the command node and puts + * it back to the command free queue. + */ +int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) +{ + struct host_cmd_ds_command *resp; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + int ret = 0; + uint16_t orig_cmdresp_no; + uint16_t cmdresp_no; + uint16_t cmdresp_result; + unsigned long flags; + + /* Now we got response from FW, cancel the command timer */ + del_timer_sync(&adapter->cmd_timer); + + if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) { + resp = (struct host_cmd_ds_command *) adapter->upld_buf; + mwifiex_dbg(adapter, ERROR, + "CMD_RESP: NULL curr_cmd, %#x\n", + le16_to_cpu(resp->command)); + return -1; + } + + adapter->is_cmd_timedout = 0; + + resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data; + if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { + /* Copy original response back to response buffer */ + struct mwifiex_ds_misc_cmd *hostcmd; + uint16_t size = le16_to_cpu(resp->size); + mwifiex_dbg(adapter, INFO, + "info: host cmd resp size = %d\n", size); + size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER); + if (adapter->curr_cmd->data_buf) { + hostcmd = adapter->curr_cmd->data_buf; + hostcmd->len = size; + memcpy(hostcmd->cmd, resp, size); + } + } + orig_cmdresp_no = le16_to_cpu(resp->command); + + /* Get BSS number and corresponding priv */ + priv = mwifiex_get_priv_by_id(adapter, + HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)), + HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num))); + if (!priv) + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + /* Clear RET_BIT from HostCmd */ + resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_CMD_ID_MASK); + + cmdresp_no = le16_to_cpu(resp->command); + cmdresp_result = le16_to_cpu(resp->result); + + /* Save the last command response to debug log */ + adapter->dbg.last_cmd_resp_index = + (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] = + orig_cmdresp_no; + + mwifiex_dbg(adapter, CMD, + "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", + orig_cmdresp_no, cmdresp_result, + le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num)); + mwifiex_dbg_dump(adapter, CMD_D, "CMD_RESP buffer:", resp, + le16_to_cpu(resp->size)); + + if (!(orig_cmdresp_no & HostCmd_RET_BIT)) { + mwifiex_dbg(adapter, ERROR, "CMD_RESP: invalid cmd resp\n"); + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + + mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->curr_cmd = NULL; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + return -1; + } + + if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { + adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD; + if ((cmdresp_result == HostCmd_RESULT_OK) && + (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH)) + ret = mwifiex_ret_802_11_hs_cfg(priv, resp); + } else { + /* handle response */ + ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp); + } + + /* Check init command response */ + if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { + if (ret) { + mwifiex_dbg(adapter, ERROR, + "%s: cmd %#x failed during\t" + "initialization\n", __func__, cmdresp_no); + mwifiex_init_fw_complete(adapter); + return -1; + } else if (adapter->last_init_cmd == cmdresp_no) + adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE; + } + + if (adapter->curr_cmd) { + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status = ret; + + mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->curr_cmd = NULL; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + } + + return ret; +} + +/* + * This function handles the timeout of command sending. + * + * It will re-send the same command again. + */ +void +mwifiex_cmd_timeout_func(unsigned long function_context) +{ + struct mwifiex_adapter *adapter = + (struct mwifiex_adapter *) function_context; + struct cmd_ctrl_node *cmd_node; + + adapter->is_cmd_timedout = 1; + if (!adapter->curr_cmd) { + mwifiex_dbg(adapter, ERROR, + "cmd: empty curr_cmd\n"); + return; + } + cmd_node = adapter->curr_cmd; + if (cmd_node) { + adapter->dbg.timeout_cmd_id = + adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index]; + adapter->dbg.timeout_cmd_act = + adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index]; + mwifiex_dbg(adapter, MSG, + "%s: Timeout cmd id = %#x, act = %#x\n", __func__, + adapter->dbg.timeout_cmd_id, + adapter->dbg.timeout_cmd_act); + + mwifiex_dbg(adapter, MSG, + "num_data_h2c_failure = %d\n", + adapter->dbg.num_tx_host_to_card_failure); + mwifiex_dbg(adapter, MSG, + "num_cmd_h2c_failure = %d\n", + adapter->dbg.num_cmd_host_to_card_failure); + + mwifiex_dbg(adapter, MSG, + "is_cmd_timedout = %d\n", + adapter->is_cmd_timedout); + mwifiex_dbg(adapter, MSG, + "num_tx_timeout = %d\n", + adapter->dbg.num_tx_timeout); + + mwifiex_dbg(adapter, MSG, + "last_cmd_index = %d\n", + adapter->dbg.last_cmd_index); + mwifiex_dbg(adapter, MSG, + "last_cmd_id: %*ph\n", + (int)sizeof(adapter->dbg.last_cmd_id), + adapter->dbg.last_cmd_id); + mwifiex_dbg(adapter, MSG, + "last_cmd_act: %*ph\n", + (int)sizeof(adapter->dbg.last_cmd_act), + adapter->dbg.last_cmd_act); + + mwifiex_dbg(adapter, MSG, + "last_cmd_resp_index = %d\n", + adapter->dbg.last_cmd_resp_index); + mwifiex_dbg(adapter, MSG, + "last_cmd_resp_id: %*ph\n", + (int)sizeof(adapter->dbg.last_cmd_resp_id), + adapter->dbg.last_cmd_resp_id); + + mwifiex_dbg(adapter, MSG, + "last_event_index = %d\n", + adapter->dbg.last_event_index); + mwifiex_dbg(adapter, MSG, + "last_event: %*ph\n", + (int)sizeof(adapter->dbg.last_event), + adapter->dbg.last_event); + + mwifiex_dbg(adapter, MSG, + "data_sent=%d cmd_sent=%d\n", + adapter->data_sent, adapter->cmd_sent); + + mwifiex_dbg(adapter, MSG, + "ps_mode=%d ps_state=%d\n", + adapter->ps_mode, adapter->ps_state); + + if (cmd_node->wait_q_enabled) { + adapter->cmd_wait_q.status = -ETIMEDOUT; + mwifiex_cancel_pending_ioctl(adapter); + } + } + if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { + mwifiex_init_fw_complete(adapter); + return; + } + + if (adapter->if_ops.device_dump) + adapter->if_ops.device_dump(adapter); + + if (adapter->if_ops.card_reset) + adapter->if_ops.card_reset(adapter); +} + +/* + * This function cancels all the pending commands. + * + * The current command, all commands in command pending queue and all scan + * commands in scan pending queue are cancelled. All the completion callbacks + * are called with failure status to ensure cleanup. + */ +void +mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; + unsigned long flags, cmd_flags; + struct mwifiex_private *priv; + int i; + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + /* Cancel current cmd */ + if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { + adapter->curr_cmd->wait_q_enabled = false; + adapter->cmd_wait_q.status = -1; + mwifiex_complete_cmd(adapter, adapter->curr_cmd); + /* no recycle probably wait for response */ + } + /* Cancel all pending command */ + spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->cmd_pending_q, list) { + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + + if (cmd_node->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + mwifiex_recycle_cmd_node(adapter, cmd_node); + spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + } + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + + /* Cancel all pending scan command */ + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, list) { + list_del(&cmd_node->list); + + cmd_node->wait_q_enabled = false; + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + + if (adapter->scan_processing) { + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (priv->scan_request) { + mwifiex_dbg(adapter, WARN, "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } + } + } +} + +/* + * This function cancels all pending commands that matches with + * the given IOCTL request. + * + * Both the current command buffer and the pending command queue are + * searched for matching IOCTL request. The completion callback of + * the matched command is called with failure status to ensure cleanup. + * In case of scan commands, all pending commands in scan pending queue + * are cancelled. + */ +void +mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; + unsigned long cmd_flags; + unsigned long scan_pending_q_flags; + struct mwifiex_private *priv; + int i; + + if ((adapter->curr_cmd) && + (adapter->curr_cmd->wait_q_enabled)) { + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + cmd_node = adapter->curr_cmd; + /* setting curr_cmd to NULL is quite dangerous, because + * mwifiex_process_cmdresp checks curr_cmd to be != NULL + * at the beginning then relies on it and dereferences + * it at will + * this probably works since mwifiex_cmd_timeout_func + * is the only caller of this function and responses + * at that point + */ + adapter->curr_cmd = NULL; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + + mwifiex_recycle_cmd_node(adapter, cmd_node); + } + + /* Cancel all pending scan command */ + spin_lock_irqsave(&adapter->scan_pending_q_lock, + scan_pending_q_flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, list) { + list_del(&cmd_node->list); + cmd_node->wait_q_enabled = false; + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + scan_pending_q_flags); + + if (adapter->scan_processing) { + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (priv->scan_request) { + mwifiex_dbg(adapter, WARN, "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } + } + } +} + +/* + * This function sends the sleep confirm command to firmware, if + * possible. + * + * The sleep confirm command cannot be issued if command response, + * data response or event response is awaiting handling, or if we + * are in the middle of sending a command, or expecting a command + * response. + */ +void +mwifiex_check_ps_cond(struct mwifiex_adapter *adapter) +{ + if (!adapter->cmd_sent && + !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter)) + mwifiex_dnld_sleep_confirm_cmd(adapter); + else + mwifiex_dbg(adapter, CMD, + "cmd: Delay Sleep Confirm (%s%s%s)\n", + (adapter->cmd_sent) ? "D" : "", + (adapter->curr_cmd) ? "C" : "", + (IS_CARD_RX_RCVD(adapter)) ? "R" : ""); +} + +/* + * This function sends a Host Sleep activated event to applications. + * + * This event is generated by the driver, with a blank event body. + */ +void +mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated) +{ + if (activated) { + if (priv->adapter->is_hs_configured) { + priv->adapter->hs_activated = true; + mwifiex_update_rxreor_flags(priv->adapter, + RXREOR_FORCE_NO_DROP); + mwifiex_dbg(priv->adapter, EVENT, + "event: hs_activated\n"); + priv->adapter->hs_activate_wait_q_woken = true; + wake_up_interruptible( + &priv->adapter->hs_activate_wait_q); + } else { + mwifiex_dbg(priv->adapter, EVENT, + "event: HS not configured\n"); + } + } else { + mwifiex_dbg(priv->adapter, EVENT, + "event: hs_deactivated\n"); + priv->adapter->hs_activated = false; + } +} + +/* + * This function handles the command response of a Host Sleep configuration + * command. + * + * Handling includes changing the header fields into CPU format + * and setting the current host sleep activation status in driver. + * + * In case host sleep status change, the function generates an event to + * notify the applications. + */ +int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg = + &resp->params.opt_hs_cfg; + uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions); + + if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) && + adapter->iface_type != MWIFIEX_USB) { + mwifiex_hs_activated_event(priv, true); + return 0; + } else { + mwifiex_dbg(adapter, CMD, + "cmd: CMD_RESP: HS_CFG cmd reply\t" + " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n", + resp->result, conditions, + phs_cfg->params.hs_config.gpio, + phs_cfg->params.hs_config.gap); + } + if (conditions != HS_CFG_CANCEL) { + adapter->is_hs_configured = true; + if (adapter->iface_type == MWIFIEX_USB) + mwifiex_hs_activated_event(priv, true); + } else { + adapter->is_hs_configured = false; + if (adapter->hs_activated) + mwifiex_hs_activated_event(priv, false); + } + + return 0; +} + +/* + * This function wakes up the adapter and generates a Host Sleep + * cancel event on receiving the power up interrupt. + */ +void +mwifiex_process_hs_config(struct mwifiex_adapter *adapter) +{ + mwifiex_dbg(adapter, INFO, + "info: %s: auto cancelling host sleep\t" + "since there is interrupt from the firmware\n", + __func__); + + adapter->if_ops.wakeup(adapter); + adapter->hs_activated = false; + adapter->is_hs_configured = false; + adapter->is_suspended = false; + mwifiex_hs_activated_event(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY), + false); +} +EXPORT_SYMBOL_GPL(mwifiex_process_hs_config); + +/* + * This function handles the command response of a sleep confirm command. + * + * The function sets the card state to SLEEP if the response indicates success. + */ +void +mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter, + u8 *pbuf, u32 upld_len) +{ + struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + uint16_t result = le16_to_cpu(cmd->result); + uint16_t command = le16_to_cpu(cmd->command); + uint16_t seq_num = le16_to_cpu(cmd->seq_num); + + if (!upld_len) { + mwifiex_dbg(adapter, ERROR, + "%s: cmd size is 0\n", __func__); + return; + } + + mwifiex_dbg(adapter, CMD, + "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", + command, result, le16_to_cpu(cmd->size), seq_num); + + /* Get BSS number and corresponding priv */ + priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num), + HostCmd_GET_BSS_TYPE(seq_num)); + if (!priv) + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + /* Update sequence number */ + seq_num = HostCmd_GET_SEQ_NO(seq_num); + /* Clear RET_BIT from HostCmd */ + command &= HostCmd_CMD_ID_MASK; + + if (command != HostCmd_CMD_802_11_PS_MODE_ENH) { + mwifiex_dbg(adapter, ERROR, + "%s: rcvd unexpected resp for cmd %#x, result = %x\n", + __func__, command, result); + return; + } + + if (result) { + mwifiex_dbg(adapter, ERROR, + "%s: sleep confirm cmd failed\n", + __func__); + adapter->pm_wakeup_card_req = false; + adapter->ps_state = PS_STATE_AWAKE; + return; + } + adapter->pm_wakeup_card_req = true; + if (adapter->is_hs_configured) + mwifiex_hs_activated_event(mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), + true); + adapter->ps_state = PS_STATE_SLEEP; + cmd->command = cpu_to_le16(command); + cmd->seq_num = cpu_to_le16(seq_num); +} +EXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp); + +/* + * This function prepares an enhanced power mode command. + * + * This function can be used to disable power save or to configure + * power save with auto PS or STA PS or auto deep sleep. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting Power Save bitmap, PS parameters TLV, PS mode TLV, + * auto deep sleep TLV (as required) + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, uint16_t ps_bitmap, + struct mwifiex_ds_auto_ds *auto_ds) +{ + struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh = + &cmd->params.psmode_enh; + u8 *tlv; + u16 cmd_size = 0; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); + if (cmd_action == DIS_AUTO_PS) { + psmode_enh->action = cpu_to_le16(DIS_AUTO_PS); + psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap)); + } else if (cmd_action == GET_PS) { + psmode_enh->action = cpu_to_le16(GET_PS); + psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap)); + } else if (cmd_action == EN_AUTO_PS) { + psmode_enh->action = cpu_to_le16(EN_AUTO_PS); + psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); + cmd_size = S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap); + tlv = (u8 *) cmd + cmd_size; + if (ps_bitmap & BITMAP_STA_PS) { + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_ps_param *ps_tlv = + (struct mwifiex_ie_types_ps_param *) tlv; + struct mwifiex_ps_param *ps_mode = &ps_tlv->param; + ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM); + ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) - + sizeof(struct mwifiex_ie_types_header)); + cmd_size += sizeof(*ps_tlv); + tlv += sizeof(*ps_tlv); + mwifiex_dbg(priv->adapter, CMD, + "cmd: PS Command: Enter PS\n"); + ps_mode->null_pkt_interval = + cpu_to_le16(adapter->null_pkt_interval); + ps_mode->multiple_dtims = + cpu_to_le16(adapter->multiple_dtim); + ps_mode->bcn_miss_timeout = + cpu_to_le16(adapter->bcn_miss_time_out); + ps_mode->local_listen_interval = + cpu_to_le16(adapter->local_listen_interval); + ps_mode->adhoc_wake_period = + cpu_to_le16(adapter->adhoc_awake_period); + ps_mode->delay_to_ps = + cpu_to_le16(adapter->delay_to_ps); + ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode); + + } + if (ps_bitmap & BITMAP_AUTO_DS) { + struct mwifiex_ie_types_auto_ds_param *auto_ds_tlv = + (struct mwifiex_ie_types_auto_ds_param *) tlv; + u16 idletime = 0; + + auto_ds_tlv->header.type = + cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); + auto_ds_tlv->header.len = + cpu_to_le16(sizeof(*auto_ds_tlv) - + sizeof(struct mwifiex_ie_types_header)); + cmd_size += sizeof(*auto_ds_tlv); + tlv += sizeof(*auto_ds_tlv); + if (auto_ds) + idletime = auto_ds->idle_time; + mwifiex_dbg(priv->adapter, CMD, + "cmd: PS Command: Enter Auto Deep Sleep\n"); + auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime); + } + cmd->size = cpu_to_le16(cmd_size); + } + return 0; +} + +/* + * This function handles the command response of an enhanced power mode + * command. + * + * Handling includes changing the header fields into CPU format + * and setting the current enhanced power mode in driver. + */ +int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct mwifiex_ds_pm_cfg *pm_cfg) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_ps_mode_enh *ps_mode = + &resp->params.psmode_enh; + uint16_t action = le16_to_cpu(ps_mode->action); + uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap); + uint16_t auto_ps_bitmap = + le16_to_cpu(ps_mode->params.ps_bitmap); + + mwifiex_dbg(adapter, INFO, + "info: %s: PS_MODE cmd reply result=%#x action=%#X\n", + __func__, resp->result, action); + if (action == EN_AUTO_PS) { + if (auto_ps_bitmap & BITMAP_AUTO_DS) { + mwifiex_dbg(adapter, CMD, + "cmd: Enabled auto deep sleep\n"); + priv->adapter->is_deep_sleep = true; + } + if (auto_ps_bitmap & BITMAP_STA_PS) { + mwifiex_dbg(adapter, CMD, + "cmd: Enabled STA power save\n"); + if (adapter->sleep_period.period) + mwifiex_dbg(adapter, CMD, + "cmd: set to uapsd/pps mode\n"); + } + } else if (action == DIS_AUTO_PS) { + if (ps_bitmap & BITMAP_AUTO_DS) { + priv->adapter->is_deep_sleep = false; + mwifiex_dbg(adapter, CMD, + "cmd: Disabled auto deep sleep\n"); + } + if (ps_bitmap & BITMAP_STA_PS) { + mwifiex_dbg(adapter, CMD, + "cmd: Disabled STA power save\n"); + if (adapter->sleep_period.period) { + adapter->delay_null_pkt = false; + adapter->tx_lock_flag = false; + adapter->pps_uapsd_mode = false; + } + } + } else if (action == GET_PS) { + if (ps_bitmap & BITMAP_STA_PS) + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; + else + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; + + mwifiex_dbg(adapter, CMD, + "cmd: ps_bitmap=%#x\n", ps_bitmap); + + if (pm_cfg) { + /* This section is for get power save mode */ + if (ps_bitmap & BITMAP_STA_PS) + pm_cfg->param.ps_mode = 1; + else + pm_cfg->param.ps_mode = 0; + } + } + return 0; +} + +/* + * This function prepares command to get hardware specifications. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting permanent address parameter + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd) +{ + struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec; + + cmd->command = cpu_to_le16(HostCmd_CMD_GET_HW_SPEC); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN); + memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN); + + return 0; +} + +/* + * This function handles the command response of get hardware + * specifications. + * + * Handling includes changing the header fields into CPU format + * and saving/updating the following parameters in driver - + * - Firmware capability information + * - Firmware band settings + * - Ad-hoc start band and channel + * - Ad-hoc 11n activation status + * - Firmware release number + * - Number of antennas + * - Hardware address + * - Hardware interface version + * - Firmware version + * - Region code + * - 11n capabilities + * - MCS support fields + * - MP end port + */ +int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_header *tlv; + struct hw_spec_api_rev *api_rev; + u16 resp_size, api_id; + int i, left_len, parsed_len = 0; + + adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info); + + if (IS_SUPPORT_MULTI_BANDS(adapter)) + adapter->fw_bands = (u8) GET_FW_DEFAULT_BANDS(adapter); + else + adapter->fw_bands = BAND_B; + + adapter->config_bands = adapter->fw_bands; + + if (adapter->fw_bands & BAND_A) { + if (adapter->fw_bands & BAND_GN) { + adapter->config_bands |= BAND_AN; + adapter->fw_bands |= BAND_AN; + } + if (adapter->fw_bands & BAND_AN) { + adapter->adhoc_start_band = BAND_A | BAND_AN; + adapter->adhoc_11n_enabled = true; + } else { + adapter->adhoc_start_band = BAND_A; + } + priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A; + } else if (adapter->fw_bands & BAND_GN) { + adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; + priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + adapter->adhoc_11n_enabled = true; + } else if (adapter->fw_bands & BAND_G) { + adapter->adhoc_start_band = BAND_G | BAND_B; + priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + } else if (adapter->fw_bands & BAND_B) { + adapter->adhoc_start_band = BAND_B; + priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + } + + adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number); + adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff; + adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna); + + if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) { + adapter->is_hw_11ac_capable = true; + + /* Copy 11AC cap */ + adapter->hw_dot_11ac_dev_cap = + le32_to_cpu(hw_spec->dot_11ac_dev_cap); + adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap + & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; + adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap + & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; + + /* Copy 11AC mcs */ + adapter->hw_dot_11ac_mcs_support = + le32_to_cpu(hw_spec->dot_11ac_mcs_support); + adapter->usr_dot_11ac_mcs_support = + adapter->hw_dot_11ac_mcs_support; + } else { + adapter->is_hw_11ac_capable = false; + } + + resp_size = le16_to_cpu(resp->size) - S_DS_GEN; + if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) { + /* we have variable HW SPEC information */ + left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec); + while (left_len > sizeof(struct mwifiex_ie_types_header)) { + tlv = (void *)&hw_spec->tlvs + parsed_len; + switch (le16_to_cpu(tlv->type)) { + case TLV_TYPE_API_REV: + api_rev = (struct hw_spec_api_rev *)tlv; + api_id = le16_to_cpu(api_rev->api_id); + switch (api_id) { + case KEY_API_VER_ID: + adapter->key_api_major_ver = + api_rev->major_ver; + adapter->key_api_minor_ver = + api_rev->minor_ver; + mwifiex_dbg(adapter, INFO, + "key_api v%d.%d\n", + adapter->key_api_major_ver, + adapter->key_api_minor_ver); + break; + case FW_API_VER_ID: + adapter->fw_api_ver = + api_rev->major_ver; + mwifiex_dbg(adapter, INFO, + "Firmware api version %d\n", + adapter->fw_api_ver); + break; + default: + mwifiex_dbg(adapter, FATAL, + "Unknown api_id: %d\n", + api_id); + break; + } + break; + default: + mwifiex_dbg(adapter, FATAL, + "Unknown GET_HW_SPEC TLV type: %#x\n", + le16_to_cpu(tlv->type)); + break; + } + parsed_len += le16_to_cpu(tlv->len) + + sizeof(struct mwifiex_ie_types_header); + left_len -= le16_to_cpu(tlv->len) + + sizeof(struct mwifiex_ie_types_header); + } + } + + mwifiex_dbg(adapter, INFO, + "info: GET_HW_SPEC: fw_release_number- %#x\n", + adapter->fw_release_number); + mwifiex_dbg(adapter, INFO, + "info: GET_HW_SPEC: permanent addr: %pM\n", + hw_spec->permanent_addr); + mwifiex_dbg(adapter, INFO, + "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n", + le16_to_cpu(hw_spec->hw_if_version), + le16_to_cpu(hw_spec->version)); + + ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr); + adapter->region_code = le16_to_cpu(hw_spec->region_code); + + for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++) + /* Use the region code to search for the index */ + if (adapter->region_code == region_code_index[i]) + break; + + /* If it's unidentified region code, use the default (USA) */ + if (i >= MWIFIEX_MAX_REGION_CODE) { + adapter->region_code = 0x10; + mwifiex_dbg(adapter, WARN, + "cmd: unknown region code, use default (USA)\n"); + } + + adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap); + adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; + adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support; + + if (adapter->if_ops.update_mp_end_port) + adapter->if_ops.update_mp_end_port(adapter, + le16_to_cpu(hw_spec->mp_end_port)); + + if (adapter->fw_api_ver == MWIFIEX_FW_V15) + adapter->scan_chan_gap_enabled = true; + + return 0; +} diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c new file mode 100644 index 000000000000..9824d8dd2b44 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c @@ -0,0 +1,1005 @@ +/* + * Marvell Wireless LAN device driver: debugfs + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" +#include "11n.h" + + +static struct dentry *mwifiex_dfs_dir; + +static char *bss_modes[] = { + "UNSPECIFIED", + "ADHOC", + "STATION", + "AP", + "AP_VLAN", + "WDS", + "MONITOR", + "MESH_POINT", + "P2P_CLIENT", + "P2P_GO", + "P2P_DEVICE", +}; + +/* + * Proc info file read handler. + * + * This function is called when the 'info' file is opened for reading. + * It prints the following driver related information - + * - Driver name + * - Driver version + * - Driver extended version + * - Interface name + * - BSS mode + * - Media state (connected or disconnected) + * - MAC address + * - Total number of Tx bytes + * - Total number of Rx bytes + * - Total number of Tx packets + * - Total number of Rx packets + * - Total number of dropped Tx packets + * - Total number of dropped Rx packets + * - Total number of corrupted Tx packets + * - Total number of corrupted Rx packets + * - Carrier status (on or off) + * - Tx queue status (started or stopped) + * + * For STA mode drivers, it also prints the following extra - + * - ESSID + * - BSSID + * - Channel + * - Region code + * - Multicast count + * - Multicast addresses + */ +static ssize_t +mwifiex_info_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *) file->private_data; + struct net_device *netdev = priv->netdev; + struct netdev_hw_addr *ha; + struct netdev_queue *txq; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *) page, fmt[64]; + struct mwifiex_bss_info info; + ssize_t ret; + int i = 0; + + if (!p) + return -ENOMEM; + + memset(&info, 0, sizeof(info)); + ret = mwifiex_get_bss_info(priv, &info); + if (ret) + goto free_and_exit; + + mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1); + + if (!priv->version_str[0]) + mwifiex_get_ver_ext(priv); + + p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); + p += sprintf(p, "driver_version = %s", fmt); + p += sprintf(p, "\nverext = %s", priv->version_str); + p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name); + + if (info.bss_mode >= ARRAY_SIZE(bss_modes)) + p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode); + else + p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]); + + p += sprintf(p, "media_state=\"%s\"\n", + (!priv->media_connected ? "Disconnected" : "Connected")); + p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { + p += sprintf(p, "multicast_count=\"%d\"\n", + netdev_mc_count(netdev)); + p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid); + p += sprintf(p, "bssid=\"%pM\"\n", info.bssid); + p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan); + p += sprintf(p, "country_code = \"%s\"\n", info.country_code); + + netdev_for_each_mc_addr(ha, netdev) + p += sprintf(p, "multicast_address[%d]=\"%pM\"\n", + i++, ha->addr); + } + + p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); + p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); + p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); + p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); + p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); + p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); + p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); + p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); + p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) + ? "on" : "off")); + p += sprintf(p, "tx queue"); + for (i = 0; i < netdev->num_tx_queues; i++) { + txq = netdev_get_tx_queue(netdev, i); + p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ? + "stopped" : "started"); + } + p += sprintf(p, "\n"); + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, + (unsigned long) p - page); + +free_and_exit: + free_page(page); + return ret; +} + +/* + * Proc device dump read handler. + * + * This function is called when the 'device_dump' file is opened for + * reading. + * This function dumps driver information and firmware memory segments + * (ex. DTCM, ITCM, SQRAM etc.) for + * debugging. + */ +static ssize_t +mwifiex_device_dump_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = file->private_data; + + if (!priv->adapter->if_ops.device_dump) + return -EIO; + + priv->adapter->if_ops.device_dump(priv->adapter); + + return 0; +} + +/* + * Proc getlog file read handler. + * + * This function is called when the 'getlog' file is opened for reading + * It prints the following log information - + * - Number of multicast Tx frames + * - Number of failed packets + * - Number of Tx retries + * - Number of multicast Tx retries + * - Number of duplicate frames + * - Number of RTS successes + * - Number of RTS failures + * - Number of ACK failures + * - Number of fragmented Rx frames + * - Number of multicast Rx frames + * - Number of FCS errors + * - Number of Tx frames + * - WEP ICV error counts + * - Number of received beacons + * - Number of missed beacons + */ +static ssize_t +mwifiex_getlog_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *) file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *) page; + ssize_t ret; + struct mwifiex_ds_get_stats stats; + + if (!p) + return -ENOMEM; + + memset(&stats, 0, sizeof(stats)); + ret = mwifiex_get_stats_info(priv, &stats); + if (ret) + goto free_and_exit; + + p += sprintf(p, "\n" + "mcasttxframe %u\n" + "failed %u\n" + "retry %u\n" + "multiretry %u\n" + "framedup %u\n" + "rtssuccess %u\n" + "rtsfailure %u\n" + "ackfailure %u\n" + "rxfrag %u\n" + "mcastrxframe %u\n" + "fcserror %u\n" + "txframe %u\n" + "wepicverrcnt-1 %u\n" + "wepicverrcnt-2 %u\n" + "wepicverrcnt-3 %u\n" + "wepicverrcnt-4 %u\n" + "bcn_rcv_cnt %u\n" + "bcn_miss_cnt %u\n", + stats.mcast_tx_frame, + stats.failed, + stats.retry, + stats.multi_retry, + stats.frame_dup, + stats.rts_success, + stats.rts_failure, + stats.ack_failure, + stats.rx_frag, + stats.mcast_rx_frame, + stats.fcs_error, + stats.tx_frame, + stats.wep_icv_error[0], + stats.wep_icv_error[1], + stats.wep_icv_error[2], + stats.wep_icv_error[3], + stats.bcn_rcv_cnt, + stats.bcn_miss_cnt); + + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, + (unsigned long) p - page); + +free_and_exit: + free_page(page); + return ret; +} + +/* Sysfs histogram file read handler. + * + * This function is called when the 'histogram' file is opened for reading + * It prints the following histogram information - + * - Number of histogram samples + * - Receive packet number of each rx_rate + * - Receive packet number of each snr + * - Receive packet number of each nosie_flr + * - Receive packet number of each signal streath + */ +static ssize_t +mwifiex_histogram_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *)file->private_data; + ssize_t ret; + struct mwifiex_histogram_data *phist_data; + int i, value; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + + if (!p) + return -ENOMEM; + + if (!priv || !priv->hist_data) + return -EFAULT; + phist_data = priv->hist_data; + + p += sprintf(p, "\n" + "total samples = %d\n", + atomic_read(&phist_data->num_samples)); + + p += sprintf(p, "rx rates (in Mbps): 0=1M 1=2M"); + p += sprintf(p, "2=5.5M 3=11M 4=6M 5=9M 6=12M\n"); + p += sprintf(p, "7=18M 8=24M 9=36M 10=48M 11=54M"); + p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n"); + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + p += sprintf(p, "44-53=MCS0-9(VHT:BW20)"); + p += sprintf(p, "54-63=MCS0-9(VHT:BW40)"); + p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n"); + } else { + p += sprintf(p, "\n"); + } + + for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) { + value = atomic_read(&phist_data->rx_rate[i]); + if (value) + p += sprintf(p, "rx_rate[%02d] = %d\n", i, value); + } + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES; + i++) { + value = atomic_read(&phist_data->rx_rate[i]); + if (value) + p += sprintf(p, "rx_rate[%02d] = %d\n", + i, value); + } + } + + for (i = 0; i < MWIFIEX_MAX_SNR; i++) { + value = atomic_read(&phist_data->snr[i]); + if (value) + p += sprintf(p, "snr[%02ddB] = %d\n", i, value); + } + for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) { + value = atomic_read(&phist_data->noise_flr[i]); + if (value) + p += sprintf(p, "noise_flr[-%02ddBm] = %d\n", + (int)(i-128), value); + } + for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) { + value = atomic_read(&phist_data->sig_str[i]); + if (value) + p += sprintf(p, "sig_strength[-%02ddBm] = %d\n", + i, value); + } + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, + (unsigned long)p - page); + + return ret; +} + +static ssize_t +mwifiex_histogram_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + + if (priv && priv->hist_data) + mwifiex_hist_data_reset(priv); + return 0; +} + +static struct mwifiex_debug_info info; + +/* + * Proc debug file read handler. + * + * This function is called when the 'debug' file is opened for reading + * It prints the following log information - + * - Interrupt count + * - WMM AC VO packets count + * - WMM AC VI packets count + * - WMM AC BE packets count + * - WMM AC BK packets count + * - Maximum Tx buffer size + * - Tx buffer size + * - Current Tx buffer size + * - Power Save mode + * - Power Save state + * - Deep Sleep status + * - Device wakeup required status + * - Number of wakeup tries + * - Host Sleep configured status + * - Host Sleep activated status + * - Number of Tx timeouts + * - Number of command timeouts + * - Last timed out command ID + * - Last timed out command action + * - Last command ID + * - Last command action + * - Last command index + * - Last command response ID + * - Last command response index + * - Last event + * - Last event index + * - Number of host to card command failures + * - Number of sleep confirm command failures + * - Number of host to card data failure + * - Number of deauthentication events + * - Number of disassociation events + * - Number of link lost events + * - Number of deauthentication commands + * - Number of association success commands + * - Number of association failure commands + * - Number of commands sent + * - Number of data packets sent + * - Number of command responses received + * - Number of events received + * - Tx BA stream table (TID, RA) + * - Rx reorder table (TID, TA, Start window, Window size, Buffer) + */ +static ssize_t +mwifiex_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *) file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *) page; + ssize_t ret; + + if (!p) + return -ENOMEM; + + ret = mwifiex_get_debug_info(priv, &info); + if (ret) + goto free_and_exit; + + p += mwifiex_debug_info_to_buffer(priv, p, &info); + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, + (unsigned long) p - page); + +free_and_exit: + free_page(page); + return ret; +} + +static u32 saved_reg_type, saved_reg_offset, saved_reg_value; + +/* + * Proc regrdwr file write handler. + * + * This function is called when the 'regrdwr' file is opened for writing + * + * This function can be used to write to a register. + */ +static ssize_t +mwifiex_regrdwr_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *) addr; + size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); + int ret; + u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; + + if (!buf) + return -ENOMEM; + + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); + + if (reg_type == 0 || reg_offset == 0) { + ret = -EINVAL; + goto done; + } else { + saved_reg_type = reg_type; + saved_reg_offset = reg_offset; + saved_reg_value = reg_value; + ret = count; + } +done: + free_page(addr); + return ret; +} + +/* + * Proc regrdwr file read handler. + * + * This function is called when the 'regrdwr' file is opened for reading + * + * This function can be used to read from a register. + */ +static ssize_t +mwifiex_regrdwr_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *) file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *) addr; + int pos = 0, ret = 0; + u32 reg_value; + + if (!buf) + return -ENOMEM; + + if (!saved_reg_type) { + /* No command has been given */ + pos += snprintf(buf, PAGE_SIZE, "0"); + goto done; + } + /* Set command has been given */ + if (saved_reg_value != UINT_MAX) { + ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset, + saved_reg_value); + + pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", + saved_reg_type, saved_reg_offset, + saved_reg_value); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + goto done; + } + /* Get command has been given */ + ret = mwifiex_reg_read(priv, saved_reg_type, + saved_reg_offset, ®_value); + if (ret) { + ret = -EINVAL; + goto done; + } + + pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, + saved_reg_offset, reg_value); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + +done: + free_page(addr); + return ret; +} + +/* Proc debug_mask file read handler. + * This function is called when the 'debug_mask' file is opened for reading + * This function can be used read driver debugging mask value. + */ +static ssize_t +mwifiex_debug_mask_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *)file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)page; + size_t ret = 0; + int pos = 0; + + if (!buf) + return -ENOMEM; + + pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n", + priv->adapter->debug_mask); + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(page); + return ret; +} + +/* Proc debug_mask file read handler. + * This function is called when the 'debug_mask' file is opened for reading + * This function can be used read driver debugging mask value. + */ +static ssize_t +mwifiex_debug_mask_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret; + unsigned long debug_mask; + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (void *)addr; + size_t buf_size = min(count, (size_t)(PAGE_SIZE - 1)); + + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + if (kstrtoul(buf, 0, &debug_mask)) { + ret = -EINVAL; + goto done; + } + + priv->adapter->debug_mask = debug_mask; + ret = count; +done: + free_page(addr); + return ret; +} + +/* Proc memrw file write handler. + * This function is called when the 'memrw' file is opened for writing + * This function can be used to write to a memory location. + */ +static ssize_t +mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count, + loff_t *ppos) +{ + int ret; + char cmd; + struct mwifiex_ds_mem_rw mem_rw; + u16 cmd_action; + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (void *)addr; + size_t buf_size = min(count, (size_t)(PAGE_SIZE - 1)); + + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value); + if (ret != 3) { + ret = -EINVAL; + goto done; + } + + if ((cmd == 'r') || (cmd == 'R')) { + cmd_action = HostCmd_ACT_GEN_GET; + mem_rw.value = 0; + } else if ((cmd == 'w') || (cmd == 'W')) { + cmd_action = HostCmd_ACT_GEN_SET; + } else { + ret = -EINVAL; + goto done; + } + + memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw)); + if (mwifiex_send_cmd(priv, HostCmd_CMD_MEM_ACCESS, cmd_action, 0, + &mem_rw, true)) + ret = -1; + else + ret = count; + +done: + free_page(addr); + return ret; +} + +/* Proc memrw file read handler. + * This function is called when the 'memrw' file is opened for reading + * This function can be used to read from a memory location. + */ +static ssize_t +mwifiex_memrw_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + int ret, pos = 0; + + if (!buf) + return -ENOMEM; + + pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr, + priv->mem_rw.value); + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(addr); + return ret; +} + +static u32 saved_offset = -1, saved_bytes = -1; + +/* + * Proc rdeeprom file write handler. + * + * This function is called when the 'rdeeprom' file is opened for writing + * + * This function can be used to write to a RDEEPROM location. + */ +static ssize_t +mwifiex_rdeeprom_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *) addr; + size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); + int ret = 0; + int offset = -1, bytes = -1; + + if (!buf) + return -ENOMEM; + + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + sscanf(buf, "%d %d", &offset, &bytes); + + if (offset == -1 || bytes == -1) { + ret = -EINVAL; + goto done; + } else { + saved_offset = offset; + saved_bytes = bytes; + ret = count; + } +done: + free_page(addr); + return ret; +} + +/* + * Proc rdeeprom read write handler. + * + * This function is called when the 'rdeeprom' file is opened for reading + * + * This function can be used to read from a RDEEPROM location. + */ +static ssize_t +mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *) file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *) addr; + int pos, ret, i; + u8 value[MAX_EEPROM_DATA]; + + if (!buf) + return -ENOMEM; + + if (saved_offset == -1) { + /* No command has been given */ + pos = snprintf(buf, PAGE_SIZE, "0"); + goto done; + } + + /* Get command has been given */ + ret = mwifiex_eeprom_read(priv, (u16) saved_offset, + (u16) saved_bytes, value); + if (ret) { + ret = -EINVAL; + goto out_free; + } + + pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); + + for (i = 0; i < saved_bytes; i++) + pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]); + +done: + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); +out_free: + free_page(addr); + return ret; +} + +/* Proc hscfg file write handler + * This function can be used to configure the host sleep parameters. + */ +static ssize_t +mwifiex_hscfg_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); + int ret, arg_num; + struct mwifiex_ds_hs_cfg hscfg; + int conditions = HS_CFG_COND_DEF; + u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF; + + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap); + + memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); + + if (arg_num > 3) { + mwifiex_dbg(priv->adapter, ERROR, + "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (arg_num >= 1 && arg_num < 3) + mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, + MWIFIEX_SYNC_CMD, &hscfg); + + if (arg_num) { + if (conditions == HS_CFG_CANCEL) { + mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD); + ret = count; + goto done; + } + hscfg.conditions = conditions; + } + if (arg_num >= 2) + hscfg.gpio = gpio; + if (arg_num == 3) + hscfg.gap = gap; + + hscfg.is_invoke_hostcmd = false; + mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, + MWIFIEX_SYNC_CMD, &hscfg); + + mwifiex_enable_hs(priv->adapter); + priv->adapter->hs_enabling = false; + ret = count; +done: + free_page(addr); + return ret; +} + +/* Proc hscfg file read handler + * This function can be used to read host sleep configuration + * parameters from driver. + */ +static ssize_t +mwifiex_hscfg_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + int pos, ret; + struct mwifiex_ds_hs_cfg hscfg; + + if (!buf) + return -ENOMEM; + + mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, + MWIFIEX_SYNC_CMD, &hscfg); + + pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions, + hscfg.gpio, hscfg.gap); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(addr); + return ret; +} + +static ssize_t +mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = file->private_data; + char buf[3]; + bool timeshare_coex; + int ret; + unsigned int len; + + if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) + return -EOPNOTSUPP; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, + HostCmd_ACT_GEN_GET, 0, ×hare_coex, true); + if (ret) + return ret; + + len = sprintf(buf, "%d\n", timeshare_coex); + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static ssize_t +mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + bool timeshare_coex; + struct mwifiex_private *priv = file->private_data; + char kbuf[16]; + int ret; + + if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) + return -EOPNOTSUPP; + + memset(kbuf, 0, sizeof(kbuf)); + + if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count))) + return -EFAULT; + + if (strtobool(kbuf, ×hare_coex)) + return -EINVAL; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, + HostCmd_ACT_GEN_SET, 0, ×hare_coex, true); + if (ret) + return ret; + else + return count; +} + +#define MWIFIEX_DFS_ADD_FILE(name) do { \ + if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \ + priv, &mwifiex_dfs_##name##_fops)) \ + return; \ +} while (0); + +#define MWIFIEX_DFS_FILE_OPS(name) \ +static const struct file_operations mwifiex_dfs_##name##_fops = { \ + .read = mwifiex_##name##_read, \ + .write = mwifiex_##name##_write, \ + .open = simple_open, \ +}; + +#define MWIFIEX_DFS_FILE_READ_OPS(name) \ +static const struct file_operations mwifiex_dfs_##name##_fops = { \ + .read = mwifiex_##name##_read, \ + .open = simple_open, \ +}; + +#define MWIFIEX_DFS_FILE_WRITE_OPS(name) \ +static const struct file_operations mwifiex_dfs_##name##_fops = { \ + .write = mwifiex_##name##_write, \ + .open = simple_open, \ +}; + + +MWIFIEX_DFS_FILE_READ_OPS(info); +MWIFIEX_DFS_FILE_READ_OPS(debug); +MWIFIEX_DFS_FILE_READ_OPS(getlog); +MWIFIEX_DFS_FILE_READ_OPS(device_dump); +MWIFIEX_DFS_FILE_OPS(regrdwr); +MWIFIEX_DFS_FILE_OPS(rdeeprom); +MWIFIEX_DFS_FILE_OPS(memrw); +MWIFIEX_DFS_FILE_OPS(hscfg); +MWIFIEX_DFS_FILE_OPS(histogram); +MWIFIEX_DFS_FILE_OPS(debug_mask); +MWIFIEX_DFS_FILE_OPS(timeshare_coex); + +/* + * This function creates the debug FS directory structure and the files. + */ +void +mwifiex_dev_debugfs_init(struct mwifiex_private *priv) +{ + if (!mwifiex_dfs_dir || !priv) + return; + + priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name, + mwifiex_dfs_dir); + + if (!priv->dfs_dev_dir) + return; + + MWIFIEX_DFS_ADD_FILE(info); + MWIFIEX_DFS_ADD_FILE(debug); + MWIFIEX_DFS_ADD_FILE(getlog); + MWIFIEX_DFS_ADD_FILE(regrdwr); + MWIFIEX_DFS_ADD_FILE(rdeeprom); + MWIFIEX_DFS_ADD_FILE(device_dump); + MWIFIEX_DFS_ADD_FILE(memrw); + MWIFIEX_DFS_ADD_FILE(hscfg); + MWIFIEX_DFS_ADD_FILE(histogram); + MWIFIEX_DFS_ADD_FILE(debug_mask); + MWIFIEX_DFS_ADD_FILE(timeshare_coex); +} + +/* + * This function removes the debug FS directory structure and the files. + */ +void +mwifiex_dev_debugfs_remove(struct mwifiex_private *priv) +{ + if (!priv) + return; + + debugfs_remove_recursive(priv->dfs_dev_dir); +} + +/* + * This function creates the top level proc directory. + */ +void +mwifiex_debugfs_init(void) +{ + if (!mwifiex_dfs_dir) + mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL); +} + +/* + * This function removes the top level proc directory. + */ +void +mwifiex_debugfs_remove(void) +{ + if (mwifiex_dfs_dir) + debugfs_remove(mwifiex_dfs_dir); +} diff --git a/drivers/net/wireless/marvell/mwifiex/decl.h b/drivers/net/wireless/marvell/mwifiex/decl.h new file mode 100644 index 000000000000..098e1f14dc9a --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/decl.h @@ -0,0 +1,273 @@ +/* + * Marvell Wireless LAN device driver: generic data structures and APIs + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_DECL_H_ +#define _MWIFIEX_DECL_H_ + +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#define MWIFIEX_BSS_COEX_COUNT 2 +#define MWIFIEX_MAX_BSS_NUM (3) + +#define MWIFIEX_DMA_ALIGN_SZ 64 +#define MWIFIEX_RX_HEADROOM 64 +#define MAX_TXPD_SZ 32 +#define INTF_HDR_ALIGN 4 + +#define MWIFIEX_MIN_DATA_HEADER_LEN (MWIFIEX_DMA_ALIGN_SZ + INTF_HDR_ALIGN + \ + MAX_TXPD_SZ) +#define MWIFIEX_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type) + * + sizeof(tx_control) + */ + +#define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2 +#define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16 +#define MWIFIEX_MAX_TDLS_PEER_SUPPORTED 8 + +#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 64 +#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 64 +#define MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE 16 + +#define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE 32 + +#define MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE 16 + +#define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE 16 +#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE 64 +#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE 64 +#define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE 64 +#define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE 64 + +#define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff + +#define MWIFIEX_RATE_BITMAP_MCS0 32 + +#define MWIFIEX_RX_DATA_BUF_SIZE (4 * 1024) +#define MWIFIEX_RX_CMD_BUF_SIZE (2 * 1024) + +#define MAX_BEACON_PERIOD (4000) +#define MIN_BEACON_PERIOD (50) +#define MAX_DTIM_PERIOD (100) +#define MIN_DTIM_PERIOD (1) + +#define MWIFIEX_RTS_MIN_VALUE (0) +#define MWIFIEX_RTS_MAX_VALUE (2347) +#define MWIFIEX_FRAG_MIN_VALUE (256) +#define MWIFIEX_FRAG_MAX_VALUE (2346) +#define MWIFIEX_WMM_VERSION 0x01 +#define MWIFIEX_WMM_SUBTYPE 0x01 + +#define MWIFIEX_RETRY_LIMIT 14 +#define MWIFIEX_SDIO_BLOCK_SIZE 256 + +#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0) +#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1) +#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2) +#define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS BIT(3) +#define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS BIT(4) +#define MWIFIEX_BUF_FLAG_AGGR_PKT BIT(5) + +#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024 +#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128 + +#define MWIFIEX_TDLS_DISABLE_LINK 0x00 +#define MWIFIEX_TDLS_ENABLE_LINK 0x01 +#define MWIFIEX_TDLS_CREATE_LINK 0x02 +#define MWIFIEX_TDLS_CONFIG_LINK 0x03 + +#define MWIFIEX_TDLS_RSSI_HIGH 50 +#define MWIFIEX_TDLS_RSSI_LOW 55 +#define MWIFIEX_TDLS_MAX_FAIL_COUNT 4 +#define MWIFIEX_AUTO_TDLS_IDLE_TIME 10 + +/* 54M rates, index from 0 to 11 */ +#define MWIFIEX_RATE_INDEX_MCS0 12 +/* 12-27=MCS0-15(BW20) */ +#define MWIFIEX_BW20_MCS_NUM 15 + +/* Rate index for OFDM 0 */ +#define MWIFIEX_RATE_INDEX_OFDM0 4 + +#define MWIFIEX_MAX_STA_NUM 1 +#define MWIFIEX_MAX_UAP_NUM 1 +#define MWIFIEX_MAX_P2P_NUM 1 + +#define MWIFIEX_A_BAND_START_FREQ 5000 + +/* SDIO Aggr data packet special info */ +#define SDIO_MAX_AGGR_BUF_SIZE (256 * 255) +#define BLOCK_NUMBER_OFFSET 15 +#define SDIO_HEADER_OFFSET 28 + +enum mwifiex_bss_type { + MWIFIEX_BSS_TYPE_STA = 0, + MWIFIEX_BSS_TYPE_UAP = 1, + MWIFIEX_BSS_TYPE_P2P = 2, + MWIFIEX_BSS_TYPE_ANY = 0xff, +}; + +enum mwifiex_bss_role { + MWIFIEX_BSS_ROLE_STA = 0, + MWIFIEX_BSS_ROLE_UAP = 1, + MWIFIEX_BSS_ROLE_ANY = 0xff, +}; + +enum mwifiex_tdls_status { + TDLS_NOT_SETUP = 0, + TDLS_SETUP_INPROGRESS, + TDLS_SETUP_COMPLETE, + TDLS_SETUP_FAILURE, + TDLS_LINK_TEARDOWN, + TDLS_CHAN_SWITCHING, + TDLS_IN_BASE_CHAN, + TDLS_IN_OFF_CHAN, +}; + +enum mwifiex_tdls_error_code { + TDLS_ERR_NO_ERROR = 0, + TDLS_ERR_INTERNAL_ERROR, + TDLS_ERR_MAX_LINKS_EST, + TDLS_ERR_LINK_EXISTS, + TDLS_ERR_LINK_NONEXISTENT, + TDLS_ERR_PEER_STA_UNREACHABLE = 25, +}; + +#define BSS_ROLE_BIT_MASK BIT(0) + +#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) + +enum mwifiex_data_frame_type { + MWIFIEX_DATA_FRAME_TYPE_ETH_II = 0, + MWIFIEX_DATA_FRAME_TYPE_802_11, +}; + +struct mwifiex_fw_image { + u8 *helper_buf; + u32 helper_len; + u8 *fw_buf; + u32 fw_len; +}; + +struct mwifiex_802_11_ssid { + u32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; +}; + +struct mwifiex_wait_queue { + wait_queue_head_t wait; + int status; +}; + +struct mwifiex_rxinfo { + struct sk_buff *parent; + u8 bss_num; + u8 bss_type; + u8 use_count; + u8 buf_type; +}; + +struct mwifiex_txinfo { + u32 status_code; + u8 flags; + u8 bss_num; + u8 bss_type; + u8 aggr_num; + u32 pkt_len; + u8 ack_frame_id; + u64 cookie; +}; + +enum mwifiex_wmm_ac_e { + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VO +} __packed; + +struct ieee_types_wmm_ac_parameters { + u8 aci_aifsn_bitmap; + u8 ecw_bitmap; + __le16 tx_op_limit; +} __packed; + +struct mwifiex_types_wmm_info { + u8 oui[4]; + u8 subtype; + u8 version; + u8 qos_info; + u8 reserved; + struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS]; +} __packed; + +struct mwifiex_arp_eth_header { + struct arphdr hdr; + u8 ar_sha[ETH_ALEN]; + u8 ar_sip[4]; + u8 ar_tha[ETH_ALEN]; + u8 ar_tip[4]; +} __packed; + +struct mwifiex_chan_stats { + u8 chan_num; + u8 bandcfg; + u8 flags; + s8 noise; + u16 total_bss; + u16 cca_scan_dur; + u16 cca_busy_dur; +} __packed; + +#define MWIFIEX_HIST_MAX_SAMPLES 1048576 +#define MWIFIEX_MAX_RX_RATES 44 +#define MWIFIEX_MAX_AC_RX_RATES 74 +#define MWIFIEX_MAX_SNR 256 +#define MWIFIEX_MAX_NOISE_FLR 256 +#define MWIFIEX_MAX_SIG_STRENGTH 256 + +struct mwifiex_histogram_data { + atomic_t rx_rate[MWIFIEX_MAX_AC_RX_RATES]; + atomic_t snr[MWIFIEX_MAX_SNR]; + atomic_t noise_flr[MWIFIEX_MAX_NOISE_FLR]; + atomic_t sig_str[MWIFIEX_MAX_SIG_STRENGTH]; + atomic_t num_samples; +}; + +struct mwifiex_iface_comb { + u8 sta_intf; + u8 uap_intf; + u8 p2p_intf; +}; + +struct mwifiex_radar_params { + struct cfg80211_chan_def *chandef; + u32 cac_time_ms; +} __packed; + +struct mwifiex_11h_intf_state { + bool is_11h_enabled; + bool is_11h_active; +} __packed; +#endif /* !_MWIFIEX_DECL_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/ethtool.c b/drivers/net/wireless/marvell/mwifiex/ethtool.c new file mode 100644 index 000000000000..58400c69ab26 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/ethtool.c @@ -0,0 +1,70 @@ +/* + * Marvell Wireless LAN device driver: ethtool + * + * Copyright (C) 2013-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" + +static void mwifiex_ethtool_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + u32 conditions = le32_to_cpu(priv->adapter->hs_cfg.conditions); + + wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY; + + if (conditions == HS_CFG_COND_DEF) + return; + + if (conditions & HS_CFG_COND_UNICAST_DATA) + wol->wolopts |= WAKE_UCAST; + if (conditions & HS_CFG_COND_MULTICAST_DATA) + wol->wolopts |= WAKE_MCAST; + if (conditions & HS_CFG_COND_BROADCAST_DATA) + wol->wolopts |= WAKE_BCAST; + if (conditions & HS_CFG_COND_MAC_EVENT) + wol->wolopts |= WAKE_PHY; +} + +static int mwifiex_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + u32 conditions = 0; + + if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY)) + return -EOPNOTSUPP; + + if (wol->wolopts & WAKE_UCAST) + conditions |= HS_CFG_COND_UNICAST_DATA; + if (wol->wolopts & WAKE_MCAST) + conditions |= HS_CFG_COND_MULTICAST_DATA; + if (wol->wolopts & WAKE_BCAST) + conditions |= HS_CFG_COND_BROADCAST_DATA; + if (wol->wolopts & WAKE_PHY) + conditions |= HS_CFG_COND_MAC_EVENT; + if (wol->wolopts == 0) + conditions |= HS_CFG_COND_DEF; + priv->adapter->hs_cfg.conditions = cpu_to_le32(conditions); + + return 0; +} + +const struct ethtool_ops mwifiex_ethtool_ops = { + .get_wol = mwifiex_ethtool_get_wol, + .set_wol = mwifiex_ethtool_set_wol, +}; diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h new file mode 100644 index 000000000000..1e1e81a0a8d4 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -0,0 +1,2177 @@ +/* + * Marvell Wireless LAN device driver: Firmware specific macros & structures + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_FW_H_ +#define _MWIFIEX_FW_H_ + +#include + + +#define INTF_HEADER_LEN 4 + +struct rfc_1042_hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + __be16 snap_type; +}; + +struct rx_packet_hdr { + struct ethhdr eth803_hdr; + struct rfc_1042_hdr rfc1042_hdr; +}; + +struct tx_packet_hdr { + struct ethhdr eth803_hdr; + struct rfc_1042_hdr rfc1042_hdr; +}; + +#define B_SUPPORTED_RATES 5 +#define G_SUPPORTED_RATES 9 +#define BG_SUPPORTED_RATES 13 +#define A_SUPPORTED_RATES 9 +#define HOSTCMD_SUPPORTED_RATES 14 +#define N_SUPPORTED_RATES 3 +#define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN | \ + BAND_AN | BAND_AAC) + +#define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \ + BIT(13)) +#define IS_SUPPORT_MULTI_BANDS(adapter) \ + (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT) + +/* bit 13: 11ac BAND_AAC + * bit 12: reserved for lab testing, will be reused for BAND_AN + * bit 11: 11n BAND_GN + * bit 10: 11a BAND_A + * bit 9: 11g BAND_G + * bit 8: 11b BAND_B + * Map these bits to band capability by right shifting 8 bits. + */ +#define GET_FW_DEFAULT_BANDS(adapter) \ + (((adapter->fw_cap_info & 0x2f00) >> 8) & \ + ALL_802_11_BANDS) + +#define HostCmd_WEP_KEY_INDEX_MASK 0x3fff + +#define KEY_INFO_ENABLED 0x01 +enum KEY_TYPE_ID { + KEY_TYPE_ID_WEP = 0, + KEY_TYPE_ID_TKIP, + KEY_TYPE_ID_AES, + KEY_TYPE_ID_WAPI, + KEY_TYPE_ID_AES_CMAC, +}; + +#define WPA_PN_SIZE 8 +#define KEY_PARAMS_FIXED_LEN 10 +#define KEY_INDEX_MASK 0xf +#define KEY_API_VER_MAJOR_V2 2 + +#define KEY_MCAST BIT(0) +#define KEY_UNICAST BIT(1) +#define KEY_ENABLED BIT(2) +#define KEY_DEFAULT BIT(3) +#define KEY_TX_KEY BIT(4) +#define KEY_RX_KEY BIT(5) +#define KEY_IGTK BIT(10) + +#define WAPI_KEY_LEN (WLAN_KEY_LEN_SMS4 + PN_LEN + 2) + +#define MAX_POLL_TRIES 100 +#define MAX_FIRMWARE_POLL_TRIES 100 + +#define FIRMWARE_READY_SDIO 0xfedc +#define FIRMWARE_READY_PCIE 0xfedcba00 + +#define MWIFIEX_COEX_MODE_TIMESHARE 0x01 +#define MWIFIEX_COEX_MODE_SPATIAL 0x82 + +enum mwifiex_usb_ep { + MWIFIEX_USB_EP_CMD_EVENT = 1, + MWIFIEX_USB_EP_DATA = 2, + MWIFIEX_USB_EP_DATA_CH2 = 3, +}; + +enum MWIFIEX_802_11_PRIVACY_FILTER { + MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL, + MWIFIEX_802_11_PRIV_FILTER_8021X_WEP +}; + +#define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI)-(s16)(NF))) +#define CAL_RSSI(SNR, NF) ((s16)((s16)(SNR)+(s16)(NF))) + +#define UAP_BSS_PARAMS_I 0 +#define UAP_CUSTOM_IE_I 1 +#define MWIFIEX_AUTO_IDX_MASK 0xffff +#define MWIFIEX_DELETE_MASK 0x0000 +#define MGMT_MASK_ASSOC_REQ 0x01 +#define MGMT_MASK_REASSOC_REQ 0x04 +#define MGMT_MASK_ASSOC_RESP 0x02 +#define MGMT_MASK_REASSOC_RESP 0x08 +#define MGMT_MASK_PROBE_REQ 0x10 +#define MGMT_MASK_PROBE_RESP 0x20 +#define MGMT_MASK_BEACON 0x100 + +#define TLV_TYPE_UAP_SSID 0x0000 +#define TLV_TYPE_UAP_RATES 0x0001 +#define TLV_TYPE_PWR_CONSTRAINT 0x0020 + +#define PROPRIETARY_TLV_BASE_ID 0x0100 +#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) +#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) +#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) +#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) +#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) +#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) +#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) +#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) +#define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32) +#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35) +#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42) +#define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44) +#define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45) +#define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48) +#define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51) +#define TLV_TYPE_UAP_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 57) +#define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 59) +#define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60) +#define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64) +#define TLV_TYPE_UAP_AKMP (PROPRIETARY_TLV_BASE_ID + 65) +#define TLV_TYPE_UAP_FRAG_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 70) +#define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82) +#define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83) +#define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84) +#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86) +#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87) +#define TLV_TYPE_CHANRPT_11H_BASIC (PROPRIETARY_TLV_BASE_ID + 91) +#define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93) +#define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94) +#define TLV_TYPE_ROBUST_COEX (PROPRIETARY_TLV_BASE_ID + 96) +#define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104) +#define TLV_TYPE_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 105) +#define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113) +#define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114) +#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123) +#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) +#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) +#define TLV_TYPE_TX_PAUSE (PROPRIETARY_TLV_BASE_ID + 148) +#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) +#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) +#define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183) +#define TLV_TYPE_MC_GROUP_INFO (PROPRIETARY_TLV_BASE_ID + 184) +#define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194) +#define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197) +#define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199) +#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 198) +#define TLV_BTCOEX_WL_AGGR_WINSIZE (PROPRIETARY_TLV_BASE_ID + 202) +#define TLV_BTCOEX_WL_SCANTIME (PROPRIETARY_TLV_BASE_ID + 203) +#define TLV_TYPE_BSS_MODE (PROPRIETARY_TLV_BASE_ID + 206) + +#define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 + +#define SSN_MASK 0xfff0 + +#define BA_RESULT_SUCCESS 0x0 +#define BA_RESULT_TIMEOUT 0x2 + +#define IS_BASTREAM_SETUP(ptr) (ptr->ba_status) + +#define BA_STREAM_NOT_ALLOWED 0xff + +#define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN || \ + priv->adapter->config_bands & BAND_AN) && \ + priv->curr_bss_params.bss_descriptor.bcn_ht_cap) +#define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) &\ + BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS) + +#define MWIFIEX_TX_DATA_BUF_SIZE_4K 4096 +#define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192 + +#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) +#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14)) +#define ISSUPP_DRCS_ENABLED(FwCapInfo) (FwCapInfo & BIT(15)) +#define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16)) + +#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ + IEEE80211_HT_CAP_SM_PS) + +#define MWIFIEX_DEF_11N_TX_BF_CAP 0x09E1E008 + +#define MWIFIEX_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR + +#define GET_RXSTBC(x) (x & IEEE80211_HT_CAP_RX_STBC) +#define MWIFIEX_RX_STBC1 0x0100 +#define MWIFIEX_RX_STBC12 0x0200 +#define MWIFIEX_RX_STBC123 0x0300 + +/* dev_cap bitmap + * BIT + * 0-16 reserved + * 17 IEEE80211_HT_CAP_SUP_WIDTH_20_40 + * 18-22 reserved + * 23 IEEE80211_HT_CAP_SGI_20 + * 24 IEEE80211_HT_CAP_SGI_40 + * 25 IEEE80211_HT_CAP_TX_STBC + * 26 IEEE80211_HT_CAP_RX_STBC + * 27-28 reserved + * 29 IEEE80211_HT_CAP_GRN_FLD + * 30-31 reserved + */ +#define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & BIT(17)) +#define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & BIT(23)) +#define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & BIT(24)) +#define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(25)) +#define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(26)) +#define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29)) +#define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8)) +#define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22)) +#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30)) +#define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2)) +#define GETSUPP_TXBASTREAMS(Dot11nDevCap) ((Dot11nDevCap >> 18) & 0xF) + +/* httxcfg bitmap + * 0 reserved + * 1 20/40 Mhz enable(1)/disable(0) + * 2-3 reserved + * 4 green field enable(1)/disable(0) + * 5 short GI in 20 Mhz enable(1)/disable(0) + * 6 short GI in 40 Mhz enable(1)/disable(0) + * 7-15 reserved + */ +#define MWIFIEX_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6)) + +/* 11AC Tx and Rx MCS map for 1x1 mode: + * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 + * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 7 streams + */ +#define MWIFIEX_11AC_MCS_MAP_1X1 0xfffefffe + +/* 11AC Tx and Rx MCS map for 2x2 mode: + * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 and 2 + * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 6 streams + */ +#define MWIFIEX_11AC_MCS_MAP_2X2 0xfffafffa + +#define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f) +#define SETHT_MCS32(x) (x[4] |= 1) +#define HT_STREAM_1X1 0x11 +#define HT_STREAM_2X2 0x22 + +#define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4)) + +#define LLC_SNAP_LEN 8 + +/* HW_SPEC fw_cap_info */ + +#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & 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) +#define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \ + (2 * (nss - 1))) +#define GET_DEVTXMCSMAP(dev_mcs_map) (dev_mcs_map >> 16) +#define GET_DEVRXMCSMAP(dev_mcs_map) (dev_mcs_map & 0xFFFF) + +/* Clear SU Beanformer, MU beanformer, MU beanformee and + * sounding dimensions bits + */ +#define MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK \ + (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \ + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \ + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \ + IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK) + +#define MOD_CLASS_HR_DSSS 0x03 +#define MOD_CLASS_OFDM 0x07 +#define MOD_CLASS_HT 0x08 +#define HT_BW_20 0 +#define HT_BW_40 1 + +#define DFS_CHAN_MOVE_TIME 10000 + +#define HostCmd_CMD_GET_HW_SPEC 0x0003 +#define HostCmd_CMD_802_11_SCAN 0x0006 +#define HostCmd_CMD_802_11_GET_LOG 0x000b +#define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010 +#define HostCmd_CMD_802_11_EEPROM_ACCESS 0x0059 +#define HostCmd_CMD_802_11_ASSOCIATE 0x0012 +#define HostCmd_CMD_802_11_SNMP_MIB 0x0016 +#define HostCmd_CMD_MAC_REG_ACCESS 0x0019 +#define HostCmd_CMD_BBP_REG_ACCESS 0x001a +#define HostCmd_CMD_RF_REG_ACCESS 0x001b +#define HostCmd_CMD_PMIC_REG_ACCESS 0x00ad +#define HostCmd_CMD_RF_TX_PWR 0x001e +#define HostCmd_CMD_RF_ANTENNA 0x0020 +#define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024 +#define HostCmd_CMD_MAC_CONTROL 0x0028 +#define HostCmd_CMD_802_11_AD_HOC_START 0x002b +#define HostCmd_CMD_802_11_AD_HOC_JOIN 0x002c +#define HostCmd_CMD_802_11_AD_HOC_STOP 0x0040 +#define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D +#define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b +#define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e +#define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c +#define HostCmd_CMD_WMM_GET_STATUS 0x0071 +#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 +#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f +#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083 +#define HostCmd_CMD_MEM_ACCESS 0x0086 +#define HostCmd_CMD_CFG_DATA 0x008f +#define HostCmd_CMD_VERSION_EXT 0x0097 +#define HostCmd_CMD_MEF_CFG 0x009a +#define HostCmd_CMD_RSSI_INFO 0x00a4 +#define HostCmd_CMD_FUNC_INIT 0x00a9 +#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa +#define HOST_CMD_APCMD_SYS_RESET 0x00af +#define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0 +#define HostCmd_CMD_UAP_BSS_START 0x00b1 +#define HostCmd_CMD_UAP_BSS_STOP 0x00b2 +#define HOST_CMD_APCMD_STA_LIST 0x00b3 +#define HostCmd_CMD_UAP_STA_DEAUTH 0x00b5 +#define HostCmd_CMD_11N_CFG 0x00cd +#define HostCmd_CMD_11N_ADDBA_REQ 0x00ce +#define HostCmd_CMD_11N_ADDBA_RSP 0x00cf +#define HostCmd_CMD_11N_DELBA 0x00d0 +#define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9 +#define HostCmd_CMD_CHAN_REPORT_REQUEST 0x00dd +#define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df +#define HostCmd_CMD_TXPWR_CFG 0x00d1 +#define HostCmd_CMD_TX_RATE_CFG 0x00d6 +#define HostCmd_CMD_ROBUST_COEX 0x00e0 +#define HostCmd_CMD_802_11_PS_MODE_ENH 0x00e4 +#define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5 +#define HostCmd_CMD_P2P_MODE_CFG 0x00eb +#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed +#define HostCmd_CMD_SET_BSS_MODE 0x00f7 +#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa +#define HostCmd_CMD_802_11_SCAN_EXT 0x0107 +#define HostCmd_CMD_COALESCE_CFG 0x010a +#define HostCmd_CMD_MGMT_FRAME_REG 0x010c +#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d +#define HostCmd_CMD_11AC_CFG 0x0112 +#define HostCmd_CMD_TDLS_CONFIG 0x0100 +#define HostCmd_CMD_MC_POLICY 0x0121 +#define HostCmd_CMD_TDLS_OPER 0x0122 +#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223 + +#define PROTOCOL_NO_SECURITY 0x01 +#define PROTOCOL_STATIC_WEP 0x02 +#define PROTOCOL_WPA 0x08 +#define PROTOCOL_WPA2 0x20 +#define PROTOCOL_WPA2_MIXED 0x28 +#define PROTOCOL_EAP 0x40 +#define KEY_MGMT_NONE 0x04 +#define KEY_MGMT_PSK 0x02 +#define KEY_MGMT_EAP 0x01 +#define CIPHER_TKIP 0x04 +#define CIPHER_AES_CCMP 0x08 +#define VALID_CIPHER_BITMAP 0x0c + +enum ENH_PS_MODES { + EN_PS = 1, + DIS_PS = 2, + EN_AUTO_DS = 3, + DIS_AUTO_DS = 4, + SLEEP_CONFIRM = 5, + GET_PS = 0, + EN_AUTO_PS = 0xff, + DIS_AUTO_PS = 0xfe, +}; + +enum P2P_MODES { + P2P_MODE_DISABLE = 0, + P2P_MODE_DEVICE = 1, + P2P_MODE_GO = 2, + P2P_MODE_CLIENT = 3, +}; + +#define HostCmd_RET_BIT 0x8000 +#define HostCmd_ACT_GEN_GET 0x0000 +#define HostCmd_ACT_GEN_SET 0x0001 +#define HostCmd_ACT_GEN_REMOVE 0x0004 +#define HostCmd_ACT_BITWISE_SET 0x0002 +#define HostCmd_ACT_BITWISE_CLR 0x0003 +#define HostCmd_RESULT_OK 0x0000 + +#define HostCmd_ACT_MAC_RX_ON 0x0001 +#define HostCmd_ACT_MAC_TX_ON 0x0002 +#define HostCmd_ACT_MAC_WEP_ENABLE 0x0008 +#define HostCmd_ACT_MAC_ETHERNETII_ENABLE 0x0010 +#define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 +#define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 +#define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON 0x2000 + +#define HostCmd_BSS_MODE_IBSS 0x0002 +#define HostCmd_BSS_MODE_ANY 0x0003 + +#define HostCmd_SCAN_RADIO_TYPE_BG 0 +#define HostCmd_SCAN_RADIO_TYPE_A 1 + +#define HS_CFG_CANCEL 0xffffffff +#define HS_CFG_COND_DEF 0x00000000 +#define HS_CFG_GPIO_DEF 0xff +#define HS_CFG_GAP_DEF 0xff +#define HS_CFG_COND_BROADCAST_DATA 0x00000001 +#define HS_CFG_COND_UNICAST_DATA 0x00000002 +#define HS_CFG_COND_MAC_EVENT 0x00000004 +#define HS_CFG_COND_MULTICAST_DATA 0x00000008 + +#define CONNECT_ERR_AUTH_ERR_STA_FAILURE 0xFFFB +#define CONNECT_ERR_ASSOC_ERR_TIMEOUT 0xFFFC +#define CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED 0xFFFD +#define CONNECT_ERR_AUTH_MSG_UNHANDLED 0xFFFE +#define CONNECT_ERR_STA_FAILURE 0xFFFF + + +#define CMD_F_HOSTCMD (1 << 0) + +#define HostCmd_CMD_ID_MASK 0x0fff + +#define HostCmd_SEQ_NUM_MASK 0x00ff + +#define HostCmd_BSS_NUM_MASK 0x0f00 + +#define HostCmd_BSS_TYPE_MASK 0xf000 + +#define HostCmd_ACT_SET_RX 0x0001 +#define HostCmd_ACT_SET_TX 0x0002 +#define HostCmd_ACT_SET_BOTH 0x0003 + +#define RF_ANTENNA_AUTO 0xFFFF + +#define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) { \ + (((seq) & 0x00ff) | \ + (((num) & 0x000f) << 8)) | \ + (((type) & 0x000f) << 12); } + +#define HostCmd_GET_SEQ_NO(seq) \ + ((seq) & HostCmd_SEQ_NUM_MASK) + +#define HostCmd_GET_BSS_NO(seq) \ + (((seq) & HostCmd_BSS_NUM_MASK) >> 8) + +#define HostCmd_GET_BSS_TYPE(seq) \ + (((seq) & HostCmd_BSS_TYPE_MASK) >> 12) + +#define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001 +#define EVENT_LINK_LOST 0x00000003 +#define EVENT_LINK_SENSED 0x00000004 +#define EVENT_MIB_CHANGED 0x00000006 +#define EVENT_INIT_DONE 0x00000007 +#define EVENT_DEAUTHENTICATED 0x00000008 +#define EVENT_DISASSOCIATED 0x00000009 +#define EVENT_PS_AWAKE 0x0000000a +#define EVENT_PS_SLEEP 0x0000000b +#define EVENT_MIC_ERR_MULTICAST 0x0000000d +#define EVENT_MIC_ERR_UNICAST 0x0000000e +#define EVENT_DEEP_SLEEP_AWAKE 0x00000010 +#define EVENT_ADHOC_BCN_LOST 0x00000011 + +#define EVENT_WMM_STATUS_CHANGE 0x00000017 +#define EVENT_BG_SCAN_REPORT 0x00000018 +#define EVENT_RSSI_LOW 0x00000019 +#define EVENT_SNR_LOW 0x0000001a +#define EVENT_MAX_FAIL 0x0000001b +#define EVENT_RSSI_HIGH 0x0000001c +#define EVENT_SNR_HIGH 0x0000001d +#define EVENT_IBSS_COALESCED 0x0000001e +#define EVENT_DATA_RSSI_LOW 0x00000024 +#define EVENT_DATA_SNR_LOW 0x00000025 +#define EVENT_DATA_RSSI_HIGH 0x00000026 +#define EVENT_DATA_SNR_HIGH 0x00000027 +#define EVENT_LINK_QUALITY 0x00000028 +#define EVENT_PORT_RELEASE 0x0000002b +#define EVENT_UAP_STA_DEAUTH 0x0000002c +#define EVENT_UAP_STA_ASSOC 0x0000002d +#define EVENT_UAP_BSS_START 0x0000002e +#define EVENT_PRE_BEACON_LOST 0x00000031 +#define EVENT_ADDBA 0x00000033 +#define EVENT_DELBA 0x00000034 +#define EVENT_BA_STREAM_TIEMOUT 0x00000037 +#define EVENT_AMSDU_AGGR_CTRL 0x00000042 +#define EVENT_UAP_BSS_IDLE 0x00000043 +#define EVENT_UAP_BSS_ACTIVE 0x00000044 +#define EVENT_WEP_ICV_ERR 0x00000046 +#define EVENT_HS_ACT_REQ 0x00000047 +#define EVENT_BW_CHANGE 0x00000048 +#define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c +#define EVENT_HOSTWAKE_STAIE 0x0000004d +#define EVENT_CHANNEL_SWITCH_ANN 0x00000050 +#define EVENT_TDLS_GENERIC_EVENT 0x00000052 +#define EVENT_RADAR_DETECTED 0x00000053 +#define EVENT_CHANNEL_REPORT_RDY 0x00000054 +#define EVENT_TX_DATA_PAUSE 0x00000055 +#define EVENT_EXT_SCAN_REPORT 0x00000058 +#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f +#define EVENT_MULTI_CHAN_INFO 0x0000006a +#define EVENT_TX_STATUS_REPORT 0x00000074 +#define EVENT_BT_COEX_WLAN_PARA_CHANGE 0X00000076 + +#define EVENT_ID_MASK 0xffff +#define BSS_NUM_MASK 0xf + +#define EVENT_GET_BSS_NUM(event_cause) \ + (((event_cause) >> 16) & BSS_NUM_MASK) + +#define EVENT_GET_BSS_TYPE(event_cause) \ + (((event_cause) >> 24) & 0x00ff) + +#define MWIFIEX_MAX_PATTERN_LEN 20 +#define MWIFIEX_MAX_OFFSET_LEN 100 +#define STACK_NBYTES 100 +#define TYPE_DNUM 1 +#define TYPE_BYTESEQ 2 +#define MAX_OPERAND 0x40 +#define TYPE_EQ (MAX_OPERAND+1) +#define TYPE_EQ_DNUM (MAX_OPERAND+2) +#define TYPE_EQ_BIT (MAX_OPERAND+3) +#define TYPE_AND (MAX_OPERAND+4) +#define TYPE_OR (MAX_OPERAND+5) +#define MEF_MODE_HOST_SLEEP 1 +#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3 +#define MEF_ACTION_AUTO_ARP 0x10 +#define MWIFIEX_CRITERIA_BROADCAST BIT(0) +#define MWIFIEX_CRITERIA_UNICAST BIT(1) +#define MWIFIEX_CRITERIA_MULTICAST BIT(3) +#define MWIFIEX_MAX_SUPPORTED_IPADDR 4 + +#define ACT_TDLS_DELETE 0x00 +#define ACT_TDLS_CREATE 0x01 +#define ACT_TDLS_CONFIG 0x02 + +#define TDLS_EVENT_LINK_TEAR_DOWN 3 +#define TDLS_EVENT_CHAN_SWITCH_RESULT 7 +#define TDLS_EVENT_START_CHAN_SWITCH 8 +#define TDLS_EVENT_CHAN_SWITCH_STOPPED 9 + +#define TDLS_BASE_CHANNEL 0 +#define TDLS_OFF_CHANNEL 1 + +#define ACT_TDLS_CS_ENABLE_CONFIG 0x00 +#define ACT_TDLS_CS_INIT 0x06 +#define ACT_TDLS_CS_STOP 0x07 +#define ACT_TDLS_CS_PARAMS 0x08 + +#define MWIFIEX_DEF_CS_UNIT_TIME 2 +#define MWIFIEX_DEF_CS_THR_OTHERLINK 10 +#define MWIFIEX_DEF_THR_DIRECTLINK 0 +#define MWIFIEX_DEF_CS_TIME 10 +#define MWIFIEX_DEF_CS_TIMEOUT 16 +#define MWIFIEX_DEF_CS_REG_CLASS 12 +#define MWIFIEX_DEF_CS_PERIODICITY 1 + +#define MWIFIEX_FW_V15 15 + +#define MWIFIEX_MASTER_RADAR_DET_MASK BIT(1) + +struct mwifiex_ie_types_header { + __le16 type; + __le16 len; +} __packed; + +struct mwifiex_ie_types_data { + struct mwifiex_ie_types_header header; + u8 data[1]; +} __packed; + +#define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01 +#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08 +#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10 +#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET 0x01 +#define MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS 0x20 + +struct txpd { + u8 bss_type; + u8 bss_num; + __le16 tx_pkt_length; + __le16 tx_pkt_offset; + __le16 tx_pkt_type; + __le32 tx_control; + u8 priority; + u8 flags; + u8 pkt_delay_2ms; + u8 reserved1[2]; + u8 tx_token_id; + u8 reserved[2]; +} __packed; + +struct rxpd { + u8 bss_type; + u8 bss_num; + __le16 rx_pkt_length; + __le16 rx_pkt_offset; + __le16 rx_pkt_type; + __le16 seq_num; + u8 priority; + u8 rx_rate; + s8 snr; + s8 nf; + + /* For: Non-802.11 AC cards + * + * Ht Info [Bit 0] RxRate format: LG=0, HT=1 + * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 + * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 + * + * For: 802.11 AC cards + * [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10 + * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01 + * BW80 = 10 BW160 = 11 + * [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1 + * [Bit 5] STBC support Enabled = 1 + * [Bit 6] LDPC support Enabled = 1 + * [Bit 7] Reserved + */ + u8 ht_info; + u8 reserved[3]; + u8 flags; +} __packed; + +struct uap_txpd { + u8 bss_type; + u8 bss_num; + __le16 tx_pkt_length; + __le16 tx_pkt_offset; + __le16 tx_pkt_type; + __le32 tx_control; + u8 priority; + u8 flags; + u8 pkt_delay_2ms; + u8 reserved1[2]; + u8 tx_token_id; + u8 reserved[2]; +}; + +struct uap_rxpd { + u8 bss_type; + u8 bss_num; + __le16 rx_pkt_length; + __le16 rx_pkt_offset; + __le16 rx_pkt_type; + __le16 seq_num; + u8 priority; + u8 rx_rate; + s8 snr; + s8 nf; + u8 ht_info; + u8 reserved[3]; + u8 flags; +}; + +struct mwifiex_fw_chan_stats { + u8 chan_num; + u8 bandcfg; + u8 flags; + s8 noise; + __le16 total_bss; + __le16 cca_scan_dur; + __le16 cca_busy_dur; +} __packed; + +enum mwifiex_chan_scan_mode_bitmasks { + MWIFIEX_PASSIVE_SCAN = BIT(0), + MWIFIEX_DISABLE_CHAN_FILT = BIT(1), + MWIFIEX_HIDDEN_SSID_REPORT = BIT(4), +}; + +struct mwifiex_chan_scan_param_set { + u8 radio_type; + u8 chan_number; + u8 chan_scan_mode_bitmap; + __le16 min_scan_time; + __le16 max_scan_time; +} __packed; + +struct mwifiex_ie_types_chan_list_param_set { + struct mwifiex_ie_types_header header; + struct mwifiex_chan_scan_param_set chan_scan_param[1]; +} __packed; + +struct chan_band_param_set { + u8 radio_type; + u8 chan_number; +}; + +struct mwifiex_ie_types_chan_band_list_param_set { + struct mwifiex_ie_types_header header; + struct chan_band_param_set chan_band_param[1]; +} __packed; + +struct mwifiex_ie_types_rates_param_set { + struct mwifiex_ie_types_header header; + u8 rates[1]; +} __packed; + +struct mwifiex_ie_types_ssid_param_set { + struct mwifiex_ie_types_header header; + u8 ssid[1]; +} __packed; + +struct mwifiex_ie_types_num_probes { + struct mwifiex_ie_types_header header; + __le16 num_probes; +} __packed; + +struct mwifiex_ie_types_scan_chan_gap { + struct mwifiex_ie_types_header header; + /* time gap in TUs to be used between two consecutive channels scan */ + __le16 chan_gap; +} __packed; + +struct mwifiex_ietypes_chanstats { + struct mwifiex_ie_types_header header; + struct mwifiex_fw_chan_stats chanstats[0]; +} __packed; + +struct mwifiex_ie_types_wildcard_ssid_params { + struct mwifiex_ie_types_header header; + u8 max_ssid_length; + u8 ssid[1]; +} __packed; + +#define TSF_DATA_SIZE 8 +struct mwifiex_ie_types_tsf_timestamp { + struct mwifiex_ie_types_header header; + u8 tsf_data[1]; +} __packed; + +struct mwifiex_cf_param_set { + u8 cfp_cnt; + u8 cfp_period; + __le16 cfp_max_duration; + __le16 cfp_duration_remaining; +} __packed; + +struct mwifiex_ibss_param_set { + __le16 atim_window; +} __packed; + +struct mwifiex_ie_types_ss_param_set { + struct mwifiex_ie_types_header header; + union { + struct mwifiex_cf_param_set cf_param_set[1]; + struct mwifiex_ibss_param_set ibss_param_set[1]; + } cf_ibss; +} __packed; + +struct mwifiex_fh_param_set { + __le16 dwell_time; + u8 hop_set; + u8 hop_pattern; + u8 hop_index; +} __packed; + +struct mwifiex_ds_param_set { + u8 current_chan; +} __packed; + +struct mwifiex_ie_types_phy_param_set { + struct mwifiex_ie_types_header header; + union { + struct mwifiex_fh_param_set fh_param_set[1]; + struct mwifiex_ds_param_set ds_param_set[1]; + } fh_ds; +} __packed; + +struct mwifiex_ie_types_auth_type { + struct mwifiex_ie_types_header header; + __le16 auth_type; +} __packed; + +struct mwifiex_ie_types_vendor_param_set { + struct mwifiex_ie_types_header header; + u8 ie[MWIFIEX_MAX_VSIE_LEN]; +}; + +#define MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC 60 + +struct mwifiex_ie_types_tdls_idle_timeout { + struct mwifiex_ie_types_header header; + __le16 value; +} __packed; + +struct mwifiex_ie_types_rsn_param_set { + struct mwifiex_ie_types_header header; + u8 rsn_ie[1]; +} __packed; + +#define KEYPARAMSET_FIXED_LEN 6 + +struct mwifiex_ie_type_key_param_set { + __le16 type; + __le16 length; + __le16 key_type_id; + __le16 key_info; + __le16 key_len; + u8 key[50]; +} __packed; + +#define IGTK_PN_LEN 8 + +struct mwifiex_cmac_param { + u8 ipn[IGTK_PN_LEN]; + u8 key[WLAN_KEY_LEN_AES_CMAC]; +} __packed; + +struct mwifiex_wep_param { + __le16 key_len; + u8 key[WLAN_KEY_LEN_WEP104]; +} __packed; + +struct mwifiex_tkip_param { + u8 pn[WPA_PN_SIZE]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_TKIP]; +} __packed; + +struct mwifiex_aes_param { + u8 pn[WPA_PN_SIZE]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_CCMP]; +} __packed; + +struct mwifiex_wapi_param { + u8 pn[PN_LEN]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_SMS4]; +} __packed; + +struct mwifiex_cmac_aes_param { + u8 ipn[IGTK_PN_LEN]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_AES_CMAC]; +} __packed; + +struct mwifiex_ie_type_key_param_set_v2 { + __le16 type; + __le16 len; + u8 mac_addr[ETH_ALEN]; + u8 key_idx; + u8 key_type; + __le16 key_info; + union { + struct mwifiex_wep_param wep; + struct mwifiex_tkip_param tkip; + struct mwifiex_aes_param aes; + struct mwifiex_wapi_param wapi; + struct mwifiex_cmac_aes_param cmac_aes; + } key_params; +} __packed; + +struct host_cmd_ds_802_11_key_material_v2 { + __le16 action; + struct mwifiex_ie_type_key_param_set_v2 key_param_set; +} __packed; + +struct host_cmd_ds_802_11_key_material { + __le16 action; + struct mwifiex_ie_type_key_param_set key_param_set; +} __packed; + +struct host_cmd_ds_gen { + __le16 command; + __le16 size; + __le16 seq_num; + __le16 result; +}; + +#define S_DS_GEN sizeof(struct host_cmd_ds_gen) + +enum sleep_resp_ctrl { + RESP_NOT_NEEDED = 0, + RESP_NEEDED, +}; + +struct mwifiex_ps_param { + __le16 null_pkt_interval; + __le16 multiple_dtims; + __le16 bcn_miss_timeout; + __le16 local_listen_interval; + __le16 adhoc_wake_period; + __le16 mode; + __le16 delay_to_ps; +}; + +#define BITMAP_AUTO_DS 0x01 +#define BITMAP_STA_PS 0x10 + +struct mwifiex_ie_types_auto_ds_param { + struct mwifiex_ie_types_header header; + __le16 deep_sleep_timeout; +} __packed; + +struct mwifiex_ie_types_ps_param { + struct mwifiex_ie_types_header header; + struct mwifiex_ps_param param; +} __packed; + +struct host_cmd_ds_802_11_ps_mode_enh { + __le16 action; + + union { + struct mwifiex_ps_param opt_ps; + __le16 ps_bitmap; + } params; +} __packed; + +enum API_VER_ID { + KEY_API_VER_ID = 1, + FW_API_VER_ID = 2, +}; + +struct hw_spec_api_rev { + struct mwifiex_ie_types_header header; + __le16 api_id; + u8 major_ver; + u8 minor_ver; +} __packed; + +struct host_cmd_ds_get_hw_spec { + __le16 hw_if_version; + __le16 version; + __le16 reserved; + __le16 num_of_mcast_adr; + u8 permanent_addr[ETH_ALEN]; + __le16 region_code; + __le16 number_of_antenna; + __le32 fw_release_number; + __le32 reserved_1; + __le32 reserved_2; + __le32 reserved_3; + __le32 fw_cap_info; + __le32 dot_11n_dev_cap; + u8 dev_mcs_support; + __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ + __le16 mgmt_buf_count; /* mgmt IE buffer count */ + __le32 reserved_5; + __le32 reserved_6; + __le32 dot_11ac_dev_cap; + __le32 dot_11ac_mcs_support; + u8 tlvs[0]; +} __packed; + +struct host_cmd_ds_802_11_rssi_info { + __le16 action; + __le16 ndata; + __le16 nbcn; + __le16 reserved[9]; + long long reserved_1; +}; + +struct host_cmd_ds_802_11_rssi_info_rsp { + __le16 action; + __le16 ndata; + __le16 nbcn; + __le16 data_rssi_last; + __le16 data_nf_last; + __le16 data_rssi_avg; + __le16 data_nf_avg; + __le16 bcn_rssi_last; + __le16 bcn_nf_last; + __le16 bcn_rssi_avg; + __le16 bcn_nf_avg; + long long tsf_bcn; +}; + +struct host_cmd_ds_802_11_mac_address { + __le16 action; + u8 mac_addr[ETH_ALEN]; +}; + +struct host_cmd_ds_mac_control { + __le16 action; + __le16 reserved; +}; + +struct host_cmd_ds_mac_multicast_adr { + __le16 action; + __le16 num_of_adrs; + u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; +} __packed; + +struct host_cmd_ds_802_11_deauthenticate { + u8 mac_addr[ETH_ALEN]; + __le16 reason_code; +} __packed; + +struct host_cmd_ds_802_11_associate { + u8 peer_sta_addr[ETH_ALEN]; + __le16 cap_info_bitmap; + __le16 listen_interval; + __le16 beacon_period; + u8 dtim_period; +} __packed; + +struct ieee_types_assoc_rsp { + __le16 cap_info_bitmap; + __le16 status_code; + __le16 a_id; + u8 ie_buffer[1]; +} __packed; + +struct host_cmd_ds_802_11_associate_rsp { + struct ieee_types_assoc_rsp assoc_rsp; +} __packed; + +struct ieee_types_cf_param_set { + u8 element_id; + u8 len; + u8 cfp_cnt; + u8 cfp_period; + __le16 cfp_max_duration; + __le16 cfp_duration_remaining; +} __packed; + +struct ieee_types_ibss_param_set { + u8 element_id; + u8 len; + __le16 atim_window; +} __packed; + +union ieee_types_ss_param_set { + struct ieee_types_cf_param_set cf_param_set; + struct ieee_types_ibss_param_set ibss_param_set; +} __packed; + +struct ieee_types_fh_param_set { + u8 element_id; + u8 len; + __le16 dwell_time; + u8 hop_set; + u8 hop_pattern; + u8 hop_index; +} __packed; + +struct ieee_types_ds_param_set { + u8 element_id; + u8 len; + u8 current_chan; +} __packed; + +union ieee_types_phy_param_set { + struct ieee_types_fh_param_set fh_param_set; + struct ieee_types_ds_param_set ds_param_set; +} __packed; + +struct ieee_types_oper_mode_ntf { + u8 element_id; + u8 len; + u8 oper_mode; +} __packed; + +struct host_cmd_ds_802_11_ad_hoc_start { + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 bss_mode; + __le16 beacon_period; + u8 dtim_period; + union ieee_types_ss_param_set ss_param_set; + union ieee_types_phy_param_set phy_param_set; + u16 reserved1; + __le16 cap_info_bitmap; + u8 data_rate[HOSTCMD_SUPPORTED_RATES]; +} __packed; + +struct host_cmd_ds_802_11_ad_hoc_result { + u8 pad[3]; + u8 bssid[ETH_ALEN]; +} __packed; + +struct adhoc_bss_desc { + u8 bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 bss_mode; + __le16 beacon_period; + u8 dtim_period; + u8 time_stamp[8]; + u8 local_time[8]; + union ieee_types_phy_param_set phy_param_set; + union ieee_types_ss_param_set ss_param_set; + __le16 cap_info_bitmap; + u8 data_rates[HOSTCMD_SUPPORTED_RATES]; + + /* + * DO NOT ADD ANY FIELDS TO THIS STRUCTURE. + * It is used in the Adhoc join command and will cause a + * binary layout mismatch with the firmware + */ +} __packed; + +struct host_cmd_ds_802_11_ad_hoc_join { + struct adhoc_bss_desc bss_descriptor; + u16 reserved1; + u16 reserved2; +} __packed; + +struct host_cmd_ds_802_11_get_log { + __le32 mcast_tx_frame; + __le32 failed; + __le32 retry; + __le32 multi_retry; + __le32 frame_dup; + __le32 rts_success; + __le32 rts_failure; + __le32 ack_failure; + __le32 rx_frag; + __le32 mcast_rx_frame; + __le32 fcs_error; + __le32 tx_frame; + __le32 reserved; + __le32 wep_icv_err_cnt[4]; + __le32 bcn_rcv_cnt; + __le32 bcn_miss_cnt; +}; + +/* Enumeration for rate format */ +enum _mwifiex_rate_format { + MWIFIEX_RATE_FORMAT_LG = 0, + MWIFIEX_RATE_FORMAT_HT, + MWIFIEX_RATE_FORMAT_VHT, + MWIFIEX_RATE_FORMAT_AUTO = 0xFF, +}; + +struct host_cmd_ds_tx_rate_query { + u8 tx_rate; + /* Tx Rate Info: For 802.11 AC cards + * + * [Bit 0-1] tx rate formate: LG = 0, HT = 1, VHT = 2 + * [Bit 2-3] HT/VHT Bandwidth: BW20 = 0, BW40 = 1, BW80 = 2, BW160 = 3 + * [Bit 4] HT/VHT Guard Interval: LGI = 0, SGI = 1 + * + * For non-802.11 AC cards + * Ht Info [Bit 0] RxRate format: LG=0, HT=1 + * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 + * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 + */ + u8 ht_info; +} __packed; + +struct mwifiex_tx_pause_tlv { + struct mwifiex_ie_types_header header; + u8 peermac[ETH_ALEN]; + u8 tx_pause; + u8 pkt_cnt; +} __packed; + +enum Host_Sleep_Action { + HS_CONFIGURE = 0x0001, + HS_ACTIVATE = 0x0002, +}; + +struct mwifiex_hs_config_param { + __le32 conditions; + u8 gpio; + u8 gap; +} __packed; + +struct hs_activate_param { + __le16 resp_ctrl; +} __packed; + +struct host_cmd_ds_802_11_hs_cfg_enh { + __le16 action; + + union { + struct mwifiex_hs_config_param hs_config; + struct hs_activate_param hs_activate; + } params; +} __packed; + +enum SNMP_MIB_INDEX { + OP_RATE_SET_I = 1, + DTIM_PERIOD_I = 3, + RTS_THRESH_I = 5, + SHORT_RETRY_LIM_I = 6, + LONG_RETRY_LIM_I = 7, + FRAG_THRESH_I = 8, + DOT11D_I = 9, + DOT11H_I = 10, +}; + +enum mwifiex_assocmd_failurepoint { + MWIFIEX_ASSOC_CMD_SUCCESS = 0, + MWIFIEX_ASSOC_CMD_FAILURE_ASSOC, + MWIFIEX_ASSOC_CMD_FAILURE_AUTH, + MWIFIEX_ASSOC_CMD_FAILURE_JOIN +}; + +#define MAX_SNMP_BUF_SIZE 128 + +struct host_cmd_ds_802_11_snmp_mib { + __le16 query_type; + __le16 oid; + __le16 buf_size; + u8 value[1]; +} __packed; + +struct mwifiex_rate_scope { + __le16 type; + __le16 length; + __le16 hr_dsss_rate_bitmap; + __le16 ofdm_rate_bitmap; + __le16 ht_mcs_rate_bitmap[8]; + __le16 vht_mcs_rate_bitmap[8]; +} __packed; + +struct mwifiex_rate_drop_pattern { + __le16 type; + __le16 length; + __le32 rate_drop_mode; +} __packed; + +struct host_cmd_ds_tx_rate_cfg { + __le16 action; + __le16 cfg_index; +} __packed; + +struct mwifiex_power_group { + u8 modulation_class; + u8 first_rate_code; + u8 last_rate_code; + s8 power_step; + s8 power_min; + s8 power_max; + u8 ht_bandwidth; + u8 reserved; +} __packed; + +struct mwifiex_types_power_group { + __le16 type; + __le16 length; +} __packed; + +struct host_cmd_ds_txpwr_cfg { + __le16 action; + __le16 cfg_index; + __le32 mode; +} __packed; + +struct host_cmd_ds_rf_tx_pwr { + __le16 action; + __le16 cur_level; + u8 max_power; + u8 min_power; +} __packed; + +struct host_cmd_ds_rf_ant_mimo { + __le16 action_tx; + __le16 tx_ant_mode; + __le16 action_rx; + __le16 rx_ant_mode; +}; + +struct host_cmd_ds_rf_ant_siso { + __le16 action; + __le16 ant_mode; +}; + +struct host_cmd_ds_tdls_oper { + __le16 tdls_action; + __le16 reason; + u8 peer_mac[ETH_ALEN]; +} __packed; + +struct mwifiex_tdls_config { + __le16 enable; +}; + +struct mwifiex_tdls_config_cs_params { + u8 unit_time; + u8 thr_otherlink; + u8 thr_directlink; +}; + +struct mwifiex_tdls_init_cs_params { + u8 peer_mac[ETH_ALEN]; + u8 primary_chan; + u8 second_chan_offset; + u8 band; + __le16 switch_time; + __le16 switch_timeout; + u8 reg_class; + u8 periodicity; +} __packed; + +struct mwifiex_tdls_stop_cs_params { + u8 peer_mac[ETH_ALEN]; +}; + +struct host_cmd_ds_tdls_config { + __le16 tdls_action; + u8 tdls_data[1]; +} __packed; + +struct mwifiex_chan_desc { + __le16 start_freq; + u8 chan_width; + u8 chan_num; +} __packed; + +struct host_cmd_ds_chan_rpt_req { + struct mwifiex_chan_desc chan_desc; + __le32 msec_dwell_time; +} __packed; + +struct host_cmd_ds_chan_rpt_event { + __le32 result; + __le64 start_tsf; + __le32 duration; + u8 tlvbuf[0]; +} __packed; + +struct host_cmd_sdio_sp_rx_aggr_cfg { + u8 action; + u8 enable; + __le16 block_size; +} __packed; + +struct mwifiex_fixed_bcn_param { + __le64 timestamp; + __le16 beacon_period; + __le16 cap_info_bitmap; +} __packed; + +struct mwifiex_event_scan_result { + __le16 event_id; + u8 bss_index; + u8 bss_type; + u8 more_event; + u8 reserved[3]; + __le16 buf_size; + u8 num_of_set; +} __packed; + +struct tx_status_event { + u8 packet_type; + u8 tx_token_id; + u8 status; +} __packed; + +#define MWIFIEX_USER_SCAN_CHAN_MAX 50 + +#define MWIFIEX_MAX_SSID_LIST_LENGTH 10 + +struct mwifiex_scan_cmd_config { + /* + * BSS mode to be sent in the firmware command + */ + u8 bss_mode; + + /* Specific BSSID used to filter scan results in the firmware */ + u8 specific_bssid[ETH_ALEN]; + + /* Length of TLVs sent in command starting at tlvBuffer */ + u32 tlv_buf_len; + + /* + * SSID TLV(s) and ChanList TLVs to be sent in the firmware command + * + * TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set + * WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set + */ + u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored + here */ +} __packed; + +struct mwifiex_user_scan_chan { + u8 chan_number; + u8 radio_type; + u8 scan_type; + u8 reserved; + u32 scan_time; +} __packed; + +struct mwifiex_user_scan_cfg { + /* + * BSS mode to be sent in the firmware command + */ + u8 bss_mode; + /* Configure the number of probe requests for active chan scans */ + u8 num_probes; + u8 reserved; + /* BSSID filter sent in the firmware command to limit the results */ + u8 specific_bssid[ETH_ALEN]; + /* SSID filter list used in the firmware to limit the scan results */ + struct cfg80211_ssid *ssid_list; + u8 num_ssids; + /* Variable number (fixed maximum) of channels to scan up */ + struct mwifiex_user_scan_chan chan_list[MWIFIEX_USER_SCAN_CHAN_MAX]; + u16 scan_chan_gap; +} __packed; + +struct ie_body { + u8 grp_key_oui[4]; + u8 ptk_cnt[2]; + u8 ptk_body[4]; +} __packed; + +struct host_cmd_ds_802_11_scan { + u8 bss_mode; + u8 bssid[ETH_ALEN]; + u8 tlv_buffer[1]; +} __packed; + +struct host_cmd_ds_802_11_scan_rsp { + __le16 bss_descript_size; + u8 number_of_sets; + u8 bss_desc_and_tlv_buffer[1]; +} __packed; + +struct host_cmd_ds_802_11_scan_ext { + u32 reserved; + u8 tlv_buffer[1]; +} __packed; + +struct mwifiex_ie_types_bss_mode { + struct mwifiex_ie_types_header header; + u8 bss_mode; +} __packed; + +struct mwifiex_ie_types_bss_scan_rsp { + struct mwifiex_ie_types_header header; + u8 bssid[ETH_ALEN]; + u8 frame_body[1]; +} __packed; + +struct mwifiex_ie_types_bss_scan_info { + struct mwifiex_ie_types_header header; + __le16 rssi; + __le16 anpi; + u8 cca_busy_fraction; + u8 radio_type; + u8 channel; + u8 reserved; + __le64 tsf; +} __packed; + +struct host_cmd_ds_802_11_bg_scan_query { + u8 flush; +} __packed; + +struct host_cmd_ds_802_11_bg_scan_query_rsp { + __le32 report_condition; + struct host_cmd_ds_802_11_scan_rsp scan_resp; +} __packed; + +struct mwifiex_ietypes_domain_param_set { + struct mwifiex_ie_types_header header; + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + struct ieee80211_country_ie_triplet triplet[1]; +} __packed; + +struct host_cmd_ds_802_11d_domain_info { + __le16 action; + struct mwifiex_ietypes_domain_param_set domain; +} __packed; + +struct host_cmd_ds_802_11d_domain_info_rsp { + __le16 action; + struct mwifiex_ietypes_domain_param_set domain; +} __packed; + +struct host_cmd_ds_11n_addba_req { + u8 add_req_result; + u8 peer_mac_addr[ETH_ALEN]; + u8 dialog_token; + __le16 block_ack_param_set; + __le16 block_ack_tmo; + __le16 ssn; +} __packed; + +struct host_cmd_ds_11n_addba_rsp { + u8 add_rsp_result; + u8 peer_mac_addr[ETH_ALEN]; + u8 dialog_token; + __le16 status_code; + __le16 block_ack_param_set; + __le16 block_ack_tmo; + __le16 ssn; +} __packed; + +struct host_cmd_ds_11n_delba { + u8 del_result; + u8 peer_mac_addr[ETH_ALEN]; + __le16 del_ba_param_set; + __le16 reason_code; + u8 reserved; +} __packed; + +struct host_cmd_ds_11n_batimeout { + u8 tid; + u8 peer_mac_addr[ETH_ALEN]; + u8 origninator; +} __packed; + +struct host_cmd_ds_11n_cfg { + __le16 action; + __le16 ht_tx_cap; + __le16 ht_tx_info; + __le16 misc_config; /* Needed for 802.11AC cards only */ +} __packed; + +struct host_cmd_ds_txbuf_cfg { + __le16 action; + __le16 buff_size; + __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ + __le16 reserved3; +} __packed; + +struct host_cmd_ds_amsdu_aggr_ctrl { + __le16 action; + __le16 enable; + __le16 curr_buf_size; +} __packed; + +struct host_cmd_ds_sta_deauth { + u8 mac[ETH_ALEN]; + __le16 reason; +} __packed; + +struct mwifiex_ie_types_sta_info { + struct mwifiex_ie_types_header header; + u8 mac[ETH_ALEN]; + u8 power_mfg_status; + s8 rssi; +}; + +struct host_cmd_ds_sta_list { + u16 sta_count; + u8 tlv[0]; +} __packed; + +struct mwifiex_ie_types_pwr_capability { + struct mwifiex_ie_types_header header; + s8 min_pwr; + s8 max_pwr; +}; + +struct mwifiex_ie_types_local_pwr_constraint { + struct mwifiex_ie_types_header header; + u8 chan; + u8 constraint; +}; + +struct mwifiex_ie_types_wmm_param_set { + struct mwifiex_ie_types_header header; + u8 wmm_ie[1]; +}; + +struct mwifiex_ie_types_wmm_queue_status { + struct mwifiex_ie_types_header header; + u8 queue_index; + u8 disabled; + __le16 medium_time; + u8 flow_required; + u8 flow_created; + u32 reserved; +}; + +struct ieee_types_vendor_header { + u8 element_id; + u8 len; + u8 oui[4]; /* 0~2: oui, 3: oui_type */ + u8 oui_subtype; + u8 version; +} __packed; + +struct ieee_types_wmm_parameter { + /* + * WMM Parameter IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [24] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [1] + * Version [1] + */ + struct ieee_types_vendor_header vend_hdr; + u8 qos_info_bitmap; + u8 reserved; + struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS]; +} __packed; + +struct ieee_types_wmm_info { + + /* + * WMM Info IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [7] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [0] + * Version [1] + */ + struct ieee_types_vendor_header vend_hdr; + + u8 qos_info_bitmap; +} __packed; + +struct host_cmd_ds_wmm_get_status { + u8 queue_status_tlv[sizeof(struct mwifiex_ie_types_wmm_queue_status) * + IEEE80211_NUM_ACS]; + u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2]; +} __packed; + +struct mwifiex_wmm_ac_status { + u8 disabled; + u8 flow_required; + u8 flow_created; +}; + +struct mwifiex_ie_types_htcap { + struct mwifiex_ie_types_header header; + struct ieee80211_ht_cap ht_cap; +} __packed; + +struct mwifiex_ie_types_vhtcap { + struct mwifiex_ie_types_header header; + struct ieee80211_vht_cap vht_cap; +} __packed; + +struct mwifiex_ie_types_aid { + struct mwifiex_ie_types_header header; + __le16 aid; +} __packed; + +struct mwifiex_ie_types_oper_mode_ntf { + struct mwifiex_ie_types_header header; + u8 oper_mode; +} __packed; + +/* VHT Operations IE */ +struct mwifiex_ie_types_vht_oper { + struct mwifiex_ie_types_header header; + u8 chan_width; + u8 chan_center_freq_1; + u8 chan_center_freq_2; + /* Basic MCS set map, each 2 bits stands for a NSS */ + __le16 basic_mcs_map; +} __packed; + +struct mwifiex_ie_types_wmmcap { + struct mwifiex_ie_types_header header; + struct mwifiex_types_wmm_info wmm_info; +} __packed; + +struct mwifiex_ie_types_htinfo { + struct mwifiex_ie_types_header header; + struct ieee80211_ht_operation ht_oper; +} __packed; + +struct mwifiex_ie_types_2040bssco { + struct mwifiex_ie_types_header header; + u8 bss_co_2040; +} __packed; + +struct mwifiex_ie_types_extcap { + struct mwifiex_ie_types_header header; + u8 ext_capab[0]; +} __packed; + +struct host_cmd_ds_mem_access { + __le16 action; + __le16 reserved; + __le32 addr; + __le32 value; +}; + +struct mwifiex_ie_types_qos_info { + struct mwifiex_ie_types_header header; + u8 qos_info; +} __packed; + +struct host_cmd_ds_mac_reg_access { + __le16 action; + __le16 offset; + __le32 value; +} __packed; + +struct host_cmd_ds_bbp_reg_access { + __le16 action; + __le16 offset; + u8 value; + u8 reserved[3]; +} __packed; + +struct host_cmd_ds_rf_reg_access { + __le16 action; + __le16 offset; + u8 value; + u8 reserved[3]; +} __packed; + +struct host_cmd_ds_pmic_reg_access { + __le16 action; + __le16 offset; + u8 value; + u8 reserved[3]; +} __packed; + +struct host_cmd_ds_802_11_eeprom_access { + __le16 action; + + __le16 offset; + __le16 byte_count; + u8 value; +} __packed; + +struct mwifiex_assoc_event { + u8 sta_addr[ETH_ALEN]; + __le16 type; + __le16 len; + __le16 frame_control; + __le16 cap_info; + __le16 listen_interval; + u8 data[0]; +} __packed; + +struct host_cmd_ds_sys_config { + __le16 action; + u8 tlv[0]; +}; + +struct host_cmd_11ac_vht_cfg { + __le16 action; + u8 band_config; + u8 misc_config; + __le32 cap_info; + __le32 mcs_tx_set; + __le32 mcs_rx_set; +} __packed; + +struct host_cmd_tlv_akmp { + struct mwifiex_ie_types_header header; + __le16 key_mgmt; + __le16 key_mgmt_operation; +} __packed; + +struct host_cmd_tlv_pwk_cipher { + struct mwifiex_ie_types_header header; + __le16 proto; + u8 cipher; + u8 reserved; +} __packed; + +struct host_cmd_tlv_gwk_cipher { + struct mwifiex_ie_types_header header; + u8 cipher; + u8 reserved; +} __packed; + +struct host_cmd_tlv_passphrase { + struct mwifiex_ie_types_header header; + u8 passphrase[0]; +} __packed; + +struct host_cmd_tlv_wep_key { + struct mwifiex_ie_types_header header; + u8 key_index; + u8 is_default; + u8 key[1]; +}; + +struct host_cmd_tlv_auth_type { + struct mwifiex_ie_types_header header; + u8 auth_type; +} __packed; + +struct host_cmd_tlv_encrypt_protocol { + struct mwifiex_ie_types_header header; + __le16 proto; +} __packed; + +struct host_cmd_tlv_ssid { + struct mwifiex_ie_types_header header; + u8 ssid[0]; +} __packed; + +struct host_cmd_tlv_rates { + struct mwifiex_ie_types_header header; + u8 rates[0]; +} __packed; + +struct mwifiex_ie_types_bssid_list { + struct mwifiex_ie_types_header header; + u8 bssid[ETH_ALEN]; +} __packed; + +struct host_cmd_tlv_bcast_ssid { + struct mwifiex_ie_types_header header; + u8 bcast_ctl; +} __packed; + +struct host_cmd_tlv_beacon_period { + struct mwifiex_ie_types_header header; + __le16 period; +} __packed; + +struct host_cmd_tlv_dtim_period { + struct mwifiex_ie_types_header header; + u8 period; +} __packed; + +struct host_cmd_tlv_frag_threshold { + struct mwifiex_ie_types_header header; + __le16 frag_thr; +} __packed; + +struct host_cmd_tlv_rts_threshold { + struct mwifiex_ie_types_header header; + __le16 rts_thr; +} __packed; + +struct host_cmd_tlv_retry_limit { + struct mwifiex_ie_types_header header; + u8 limit; +} __packed; + +struct host_cmd_tlv_mac_addr { + struct mwifiex_ie_types_header header; + u8 mac_addr[ETH_ALEN]; +} __packed; + +struct host_cmd_tlv_channel_band { + struct mwifiex_ie_types_header header; + u8 band_config; + u8 channel; +} __packed; + +struct host_cmd_tlv_ageout_timer { + struct mwifiex_ie_types_header header; + __le32 sta_ao_timer; +} __packed; + +struct host_cmd_tlv_power_constraint { + struct mwifiex_ie_types_header header; + u8 constraint; +} __packed; + +struct mwifiex_ie_types_btcoex_scan_time { + struct mwifiex_ie_types_header header; + u8 coex_scan; + u8 reserved; + u16 min_scan_time; + u16 max_scan_time; +} __packed; + +struct mwifiex_ie_types_btcoex_aggr_win_size { + struct mwifiex_ie_types_header header; + u8 coex_win_size; + u8 tx_win_size; + u8 rx_win_size; + u8 reserved; +} __packed; + +struct mwifiex_ie_types_robust_coex { + struct mwifiex_ie_types_header header; + __le32 mode; +} __packed; + +struct host_cmd_ds_version_ext { + u8 version_str_sel; + char version_str[128]; +} __packed; + +struct host_cmd_ds_mgmt_frame_reg { + __le16 action; + __le32 mask; +} __packed; + +struct host_cmd_ds_p2p_mode_cfg { + __le16 action; + __le16 mode; +} __packed; + +struct host_cmd_ds_remain_on_chan { + __le16 action; + u8 status; + u8 reserved; + u8 band_cfg; + u8 channel; + __le32 duration; +} __packed; + +struct host_cmd_ds_802_11_ibss_status { + __le16 action; + __le16 enable; + u8 bssid[ETH_ALEN]; + __le16 beacon_interval; + __le16 atim_window; + __le16 use_g_rate_protect; +} __packed; + +struct mwifiex_fw_mef_entry { + u8 mode; + u8 action; + __le16 exprsize; + u8 expr[0]; +} __packed; + +struct host_cmd_ds_mef_cfg { + __le32 criteria; + __le16 num_entries; + struct mwifiex_fw_mef_entry mef_entry[0]; +} __packed; + +#define CONNECTION_TYPE_INFRA 0 +#define CONNECTION_TYPE_ADHOC 1 +#define CONNECTION_TYPE_AP 2 + +struct host_cmd_ds_set_bss_mode { + u8 con_type; +} __packed; + +struct host_cmd_ds_pcie_details { + /* TX buffer descriptor ring address */ + u32 txbd_addr_lo; + u32 txbd_addr_hi; + /* TX buffer descriptor ring count */ + u32 txbd_count; + + /* RX buffer descriptor ring address */ + u32 rxbd_addr_lo; + u32 rxbd_addr_hi; + /* RX buffer descriptor ring count */ + u32 rxbd_count; + + /* Event buffer descriptor ring address */ + u32 evtbd_addr_lo; + u32 evtbd_addr_hi; + /* Event buffer descriptor ring count */ + u32 evtbd_count; + + /* Sleep cookie buffer physical address */ + u32 sleep_cookie_addr_lo; + u32 sleep_cookie_addr_hi; +} __packed; + +struct mwifiex_ie_types_rssi_threshold { + struct mwifiex_ie_types_header header; + u8 abs_value; + u8 evt_freq; +} __packed; + +#define MWIFIEX_DFS_REC_HDR_LEN 8 +#define MWIFIEX_DFS_REC_HDR_NUM 10 +#define MWIFIEX_BIN_COUNTER_LEN 7 + +struct mwifiex_radar_det_event { + __le32 detect_count; + u8 reg_domain; /*1=fcc, 2=etsi, 3=mic*/ + u8 det_type; /*0=none, 1=pw(chirp), 2=pri(radar)*/ + __le16 pw_chirp_type; + u8 pw_chirp_idx; + u8 pw_value; + u8 pri_radar_type; + u8 pri_bincnt; + u8 bin_counter[MWIFIEX_BIN_COUNTER_LEN]; + u8 num_dfs_records; + u8 dfs_record_hdr[MWIFIEX_DFS_REC_HDR_NUM][MWIFIEX_DFS_REC_HDR_LEN]; + __le32 passed; +} __packed; + +struct mwifiex_ie_types_multi_chan_info { + struct mwifiex_ie_types_header header; + __le16 status; + u8 tlv_buffer[0]; +} __packed; + +struct mwifiex_ie_types_mc_group_info { + struct mwifiex_ie_types_header header; + u8 chan_group_id; + u8 chan_buf_weight; + u8 band_config; + u8 chan_num; + u32 chan_time; + u32 reserved; + union { + u8 sdio_func_num; + u8 usb_ep_num; + } hid_num; + u8 intf_num; + u8 bss_type_numlist[0]; +} __packed; + +struct meas_rpt_map { + u8 rssi:3; + u8 unmeasured:1; + u8 radar:1; + u8 unidentified_sig:1; + u8 ofdm_preamble:1; + u8 bss:1; +} __packed; + +struct mwifiex_ie_types_chan_rpt_data { + struct mwifiex_ie_types_header header; + struct meas_rpt_map map; +} __packed; + +struct host_cmd_ds_802_11_subsc_evt { + __le16 action; + __le16 events; +} __packed; + +struct chan_switch_result { + u8 cur_chan; + u8 status; + u8 reason; +} __packed; + +struct mwifiex_tdls_generic_event { + __le16 type; + u8 peer_mac[ETH_ALEN]; + union { + struct chan_switch_result switch_result; + u8 cs_stop_reason; + __le16 reason_code; + __le16 reserved; + } u; +} __packed; + +struct mwifiex_ie { + __le16 ie_index; + __le16 mgmt_subtype_mask; + __le16 ie_length; + u8 ie_buffer[IEEE_MAX_IE_SIZE]; +} __packed; + +#define MAX_MGMT_IE_INDEX 16 +struct mwifiex_ie_list { + __le16 type; + __le16 len; + struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX]; +} __packed; + +struct coalesce_filt_field_param { + u8 operation; + u8 operand_len; + __le16 offset; + u8 operand_byte_stream[4]; +}; + +struct coalesce_receive_filt_rule { + struct mwifiex_ie_types_header header; + u8 num_of_fields; + u8 pkt_type; + __le16 max_coalescing_delay; + struct coalesce_filt_field_param params[0]; +} __packed; + +struct host_cmd_ds_coalesce_cfg { + __le16 action; + __le16 num_of_rules; + struct coalesce_receive_filt_rule rule[0]; +} __packed; + +struct host_cmd_ds_multi_chan_policy { + __le16 action; + __le16 policy; +} __packed; + +struct host_cmd_ds_robust_coex { + __le16 action; + __le16 reserved; +} __packed; + +struct host_cmd_ds_command { + __le16 command; + __le16 size; + __le16 seq_num; + __le16 result; + union { + struct host_cmd_ds_get_hw_spec hw_spec; + struct host_cmd_ds_mac_control mac_ctrl; + struct host_cmd_ds_802_11_mac_address mac_addr; + struct host_cmd_ds_mac_multicast_adr mc_addr; + struct host_cmd_ds_802_11_get_log get_log; + struct host_cmd_ds_802_11_rssi_info rssi_info; + struct host_cmd_ds_802_11_rssi_info_rsp rssi_info_rsp; + struct host_cmd_ds_802_11_snmp_mib smib; + struct host_cmd_ds_tx_rate_query tx_rate; + struct host_cmd_ds_tx_rate_cfg tx_rate_cfg; + struct host_cmd_ds_txpwr_cfg txp_cfg; + struct host_cmd_ds_rf_tx_pwr txp; + struct host_cmd_ds_rf_ant_mimo ant_mimo; + struct host_cmd_ds_rf_ant_siso ant_siso; + struct host_cmd_ds_802_11_ps_mode_enh psmode_enh; + struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg; + struct host_cmd_ds_802_11_scan scan; + struct host_cmd_ds_802_11_scan_ext ext_scan; + struct host_cmd_ds_802_11_scan_rsp scan_resp; + struct host_cmd_ds_802_11_bg_scan_query bg_scan_query; + struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp; + struct host_cmd_ds_802_11_associate associate; + struct host_cmd_ds_802_11_associate_rsp associate_rsp; + struct host_cmd_ds_802_11_deauthenticate deauth; + struct host_cmd_ds_802_11_ad_hoc_start adhoc_start; + struct host_cmd_ds_802_11_ad_hoc_result adhoc_result; + struct host_cmd_ds_802_11_ad_hoc_join adhoc_join; + struct host_cmd_ds_802_11d_domain_info domain_info; + struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp; + struct host_cmd_ds_11n_addba_req add_ba_req; + struct host_cmd_ds_11n_addba_rsp add_ba_rsp; + struct host_cmd_ds_11n_delba del_ba; + struct host_cmd_ds_txbuf_cfg tx_buf; + struct host_cmd_ds_amsdu_aggr_ctrl amsdu_aggr_ctrl; + struct host_cmd_ds_11n_cfg htcfg; + struct host_cmd_ds_wmm_get_status get_wmm_status; + struct host_cmd_ds_802_11_key_material key_material; + struct host_cmd_ds_802_11_key_material_v2 key_material_v2; + struct host_cmd_ds_version_ext verext; + struct host_cmd_ds_mgmt_frame_reg reg_mask; + struct host_cmd_ds_remain_on_chan roc_cfg; + struct host_cmd_ds_p2p_mode_cfg mode_cfg; + struct host_cmd_ds_802_11_ibss_status ibss_coalescing; + struct host_cmd_ds_mef_cfg mef_cfg; + struct host_cmd_ds_mem_access mem; + struct host_cmd_ds_mac_reg_access mac_reg; + struct host_cmd_ds_bbp_reg_access bbp_reg; + struct host_cmd_ds_rf_reg_access rf_reg; + struct host_cmd_ds_pmic_reg_access pmic_reg; + struct host_cmd_ds_set_bss_mode bss_mode; + struct host_cmd_ds_pcie_details pcie_host_spec; + struct host_cmd_ds_802_11_eeprom_access eeprom; + struct host_cmd_ds_802_11_subsc_evt subsc_evt; + struct host_cmd_ds_sys_config uap_sys_config; + struct host_cmd_ds_sta_deauth sta_deauth; + struct host_cmd_ds_sta_list sta_list; + struct host_cmd_11ac_vht_cfg vht_cfg; + struct host_cmd_ds_coalesce_cfg coalesce_cfg; + struct host_cmd_ds_tdls_config tdls_config; + struct host_cmd_ds_tdls_oper tdls_oper; + struct host_cmd_ds_chan_rpt_req chan_rpt_req; + struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg; + struct host_cmd_ds_multi_chan_policy mc_policy; + struct host_cmd_ds_robust_coex coex; + } params; +} __packed; + +struct mwifiex_opt_sleep_confirm { + __le16 command; + __le16 size; + __le16 seq_num; + __le16 result; + __le16 action; + __le16 resp_ctrl; +} __packed; +#endif /* !_MWIFIEX_FW_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/ie.c b/drivers/net/wireless/marvell/mwifiex/ie.c new file mode 100644 index 000000000000..abf52d25b981 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/ie.c @@ -0,0 +1,488 @@ +/* + * Marvell Wireless LAN device driver: management IE handling- setting and + * deleting IE. + * + * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" + +/* This function checks if current IE index is used by any on other interface. + * Return: -1: yes, current IE index is used by someone else. + * 0: no, current IE index is NOT used by other interface. + */ +static int +mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx) +{ + int i; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie *ie; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i] != priv) { + ie = &adapter->priv[i]->mgmt_ie[idx]; + if (ie->mgmt_subtype_mask && ie->ie_length) + return -1; + } + } + + return 0; +} + +/* Get unused IE index. This index will be used for setting new IE */ +static int +mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask, + struct mwifiex_ie *ie, u16 *index) +{ + u16 mask, len, i; + + for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) { + mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask); + len = le16_to_cpu(ie->ie_length); + + if (mask == MWIFIEX_AUTO_IDX_MASK) + continue; + + if (mask == subtype_mask) { + if (len > IEEE_MAX_IE_SIZE) + continue; + + *index = i; + return 0; + } + + if (!priv->mgmt_ie[i].ie_length) { + if (mwifiex_ie_index_used_by_other_intf(priv, i)) + continue; + + *index = i; + return 0; + } + } + + return -1; +} + +/* This function prepares IE data buffer for command to be sent to FW */ +static int +mwifiex_update_autoindex_ies(struct mwifiex_private *priv, + struct mwifiex_ie_list *ie_list) +{ + u16 travel_len, index, mask; + s16 input_len, tlv_len; + struct mwifiex_ie *ie; + u8 *tmp; + + input_len = le16_to_cpu(ie_list->len); + travel_len = sizeof(struct mwifiex_ie_types_header); + + ie_list->len = 0; + + while (input_len >= sizeof(struct mwifiex_ie_types_header)) { + ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len); + tlv_len = le16_to_cpu(ie->ie_length); + travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE; + + if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE) + return -1; + index = le16_to_cpu(ie->ie_index); + mask = le16_to_cpu(ie->mgmt_subtype_mask); + + if (index == MWIFIEX_AUTO_IDX_MASK) { + /* automatic addition */ + if (mwifiex_ie_get_autoidx(priv, mask, ie, &index)) + return -1; + if (index == MWIFIEX_AUTO_IDX_MASK) + return -1; + + tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer; + memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length)); + priv->mgmt_ie[index].ie_length = ie->ie_length; + priv->mgmt_ie[index].ie_index = cpu_to_le16(index); + priv->mgmt_ie[index].mgmt_subtype_mask = + cpu_to_le16(mask); + + ie->ie_index = cpu_to_le16(index); + } else { + if (mask != MWIFIEX_DELETE_MASK) + return -1; + /* + * Check if this index is being used on any + * other interface. + */ + if (mwifiex_ie_index_used_by_other_intf(priv, index)) + return -1; + + ie->ie_length = 0; + memcpy(&priv->mgmt_ie[index], ie, + sizeof(struct mwifiex_ie)); + } + + le16_add_cpu(&ie_list->len, + le16_to_cpu(priv->mgmt_ie[index].ie_length) + + MWIFIEX_IE_HDR_SIZE); + input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE; + } + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_CUSTOM_IE_I, ie_list, false); + + return 0; +} + +/* Copy individual custom IEs for beacon, probe response and assoc response + * and prepare single structure for IE setting. + * This function also updates allocated IE indices from driver. + */ +static int +mwifiex_update_uap_custom_ie(struct mwifiex_private *priv, + struct mwifiex_ie *beacon_ie, u16 *beacon_idx, + struct mwifiex_ie *pr_ie, u16 *probe_idx, + struct mwifiex_ie *ar_ie, u16 *assoc_idx) +{ + struct mwifiex_ie_list *ap_custom_ie; + u8 *pos; + u16 len; + int ret; + + ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL); + if (!ap_custom_ie) + return -ENOMEM; + + ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); + pos = (u8 *)ap_custom_ie->ie_list; + + if (beacon_ie) { + len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(beacon_ie->ie_length); + memcpy(pos, beacon_ie, len); + pos += len; + le16_add_cpu(&ap_custom_ie->len, len); + } + if (pr_ie) { + len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(pr_ie->ie_length); + memcpy(pos, pr_ie, len); + pos += len; + le16_add_cpu(&ap_custom_ie->len, len); + } + if (ar_ie) { + len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(ar_ie->ie_length); + memcpy(pos, ar_ie, len); + pos += len; + le16_add_cpu(&ap_custom_ie->len, len); + } + + ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie); + + pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index); + if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) { + /* save beacon ie index after auto-indexing */ + *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index); + len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(beacon_ie->ie_length); + pos += len; + } + if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) { + /* save probe resp ie index after auto-indexing */ + *probe_idx = *((u16 *)pos); + len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(pr_ie->ie_length); + pos += len; + } + if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) + /* save assoc resp ie index after auto-indexing */ + *assoc_idx = *((u16 *)pos); + + kfree(ap_custom_ie); + return ret; +} + +/* This function checks if the vendor specified IE is present in passed buffer + * and copies it to mwifiex_ie structure. + * Function takes pointer to struct mwifiex_ie pointer as argument. + * If the vendor specified IE is present then memory is allocated for + * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing + * this memory. + */ +static int mwifiex_update_vs_ie(const u8 *ies, int ies_len, + struct mwifiex_ie **ie_ptr, u16 mask, + unsigned int oui, u8 oui_type) +{ + struct ieee_types_header *vs_ie; + struct mwifiex_ie *ie = *ie_ptr; + const u8 *vendor_ie; + + vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len); + if (vendor_ie) { + if (!*ie_ptr) { + *ie_ptr = kzalloc(sizeof(struct mwifiex_ie), + GFP_KERNEL); + if (!*ie_ptr) + return -ENOMEM; + ie = *ie_ptr; + } + + vs_ie = (struct ieee_types_header *)vendor_ie; + memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length), + vs_ie, vs_ie->len + 2); + le16_add_cpu(&ie->ie_length, vs_ie->len + 2); + ie->mgmt_subtype_mask = cpu_to_le16(mask); + ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK); + } + + *ie_ptr = ie; + return 0; +} + +/* This function parses beacon IEs, probe response IEs, association response IEs + * from cfg80211_ap_settings->beacon and sets these IE to FW. + */ +static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv, + struct cfg80211_beacon_data *data) +{ + struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL; + u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK; + u16 ar_idx = MWIFIEX_AUTO_IDX_MASK; + int ret = 0; + + if (data->beacon_ies && data->beacon_ies_len) { + mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len, + &beacon_ie, MGMT_MASK_BEACON, + WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len, + &beacon_ie, MGMT_MASK_BEACON, + WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); + } + + if (data->proberesp_ies && data->proberesp_ies_len) { + mwifiex_update_vs_ie(data->proberesp_ies, + data->proberesp_ies_len, &pr_ie, + MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + mwifiex_update_vs_ie(data->proberesp_ies, + data->proberesp_ies_len, &pr_ie, + MGMT_MASK_PROBE_RESP, + WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); + } + + if (data->assocresp_ies && data->assocresp_ies_len) { + mwifiex_update_vs_ie(data->assocresp_ies, + data->assocresp_ies_len, &ar_ie, + MGMT_MASK_ASSOC_RESP | + MGMT_MASK_REASSOC_RESP, + WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + mwifiex_update_vs_ie(data->assocresp_ies, + data->assocresp_ies_len, &ar_ie, + MGMT_MASK_ASSOC_RESP | + MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA, + WLAN_OUI_TYPE_WFA_P2P); + } + + if (beacon_ie || pr_ie || ar_ie) { + ret = mwifiex_update_uap_custom_ie(priv, beacon_ie, + &beacon_idx, pr_ie, + &pr_idx, ar_ie, &ar_idx); + if (ret) + goto done; + } + + priv->beacon_idx = beacon_idx; + priv->proberesp_idx = pr_idx; + priv->assocresp_idx = ar_idx; + +done: + kfree(beacon_ie); + kfree(pr_ie); + kfree(ar_ie); + + return ret; +} + +/* This function parses head and tail IEs, from cfg80211_beacon_data and sets + * these IE to FW. + */ +static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv, + struct cfg80211_beacon_data *info) +{ + struct mwifiex_ie *gen_ie; + struct ieee_types_header *hdr; + struct ieee80211_vendor_ie *vendorhdr; + u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0; + int left_len, parsed_len = 0; + + if (!info->tail || !info->tail_len) + return 0; + + gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL); + if (!gen_ie) + return -ENOMEM; + + left_len = info->tail_len; + + /* Many IEs are generated in FW by parsing bss configuration. + * Let's not add them here; else we may end up duplicating these IEs + */ + while (left_len > sizeof(struct ieee_types_header)) { + hdr = (void *)(info->tail + parsed_len); + switch (hdr->element_id) { + case WLAN_EID_SSID: + case WLAN_EID_SUPP_RATES: + case WLAN_EID_COUNTRY: + case WLAN_EID_PWR_CONSTRAINT: + case WLAN_EID_EXT_SUPP_RATES: + case WLAN_EID_HT_CAPABILITY: + case WLAN_EID_HT_OPERATION: + case WLAN_EID_VHT_CAPABILITY: + case WLAN_EID_VHT_OPERATION: + case WLAN_EID_VENDOR_SPECIFIC: + break; + default: + memcpy(gen_ie->ie_buffer + ie_len, hdr, + hdr->len + sizeof(struct ieee_types_header)); + ie_len += hdr->len + sizeof(struct ieee_types_header); + break; + } + left_len -= hdr->len + sizeof(struct ieee_types_header); + parsed_len += hdr->len + sizeof(struct ieee_types_header); + } + + /* parse only WPA vendor IE from tail, WMM IE is configured by + * bss_config command + */ + vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPA, + info->tail, info->tail_len); + if (vendorhdr) { + memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, + vendorhdr->len + sizeof(struct ieee_types_header)); + ie_len += vendorhdr->len + sizeof(struct ieee_types_header); + } + + if (!ie_len) { + kfree(gen_ie); + return 0; + } + + gen_ie->ie_index = cpu_to_le16(gen_idx); + gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON | + MGMT_MASK_PROBE_RESP | + MGMT_MASK_ASSOC_RESP); + gen_ie->ie_length = cpu_to_le16(ie_len); + + if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL, + NULL, NULL)) { + kfree(gen_ie); + return -1; + } + + priv->gen_idx = gen_idx; + kfree(gen_ie); + return 0; +} + +/* This function parses different IEs-head & tail IEs, beacon IEs, + * probe response IEs, association response IEs from cfg80211_ap_settings + * function and sets these IE to FW. + */ +int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, + struct cfg80211_beacon_data *info) +{ + int ret; + + ret = mwifiex_uap_parse_tail_ies(priv, info); + + if (ret) + return ret; + + return mwifiex_set_mgmt_beacon_data_ies(priv, info); +} + +/* This function removes management IE set */ +int mwifiex_del_mgmt_ies(struct mwifiex_private *priv) +{ + struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL; + struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL; + int ret = 0; + + if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) { + gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL); + if (!gen_ie) + return -ENOMEM; + + gen_ie->ie_index = cpu_to_le16(priv->gen_idx); + gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); + gen_ie->ie_length = 0; + if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx, + NULL, &priv->proberesp_idx, + NULL, &priv->assocresp_idx)) { + ret = -1; + goto done; + } + + priv->gen_idx = MWIFIEX_AUTO_IDX_MASK; + } + + if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) { + beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); + if (!beacon_ie) { + ret = -ENOMEM; + goto done; + } + beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx); + beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); + beacon_ie->ie_length = 0; + } + if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) { + pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); + if (!pr_ie) { + ret = -ENOMEM; + goto done; + } + pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx); + pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); + pr_ie->ie_length = 0; + } + if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) { + ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); + if (!ar_ie) { + ret = -ENOMEM; + goto done; + } + ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx); + ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); + ar_ie->ie_length = 0; + } + + if (beacon_ie || pr_ie || ar_ie) + ret = mwifiex_update_uap_custom_ie(priv, + beacon_ie, &priv->beacon_idx, + pr_ie, &priv->proberesp_idx, + ar_ie, &priv->assocresp_idx); + +done: + kfree(gen_ie); + kfree(beacon_ie); + kfree(pr_ie); + kfree(ar_ie); + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c new file mode 100644 index 000000000000..de74a7773fb6 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -0,0 +1,782 @@ +/* + * Marvell Wireless LAN device driver: HW/FW Initialization + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" + +/* + * This function adds a BSS priority table to the table list. + * + * The function allocates a new BSS priority table node and adds it to + * the end of BSS priority table list, kept in driver memory. + */ +static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_bss_prio_node *bss_prio; + struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; + unsigned long flags; + + bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL); + if (!bss_prio) + return -ENOMEM; + + bss_prio->priv = priv; + INIT_LIST_HEAD(&bss_prio->list); + + spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags); + list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head); + spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags); + + return 0; +} + +static void wakeup_timer_fn(unsigned long data) +{ + struct mwifiex_adapter *adapter = (struct mwifiex_adapter *)data; + + mwifiex_dbg(adapter, ERROR, "Firmware wakeup failed\n"); + adapter->hw_status = MWIFIEX_HW_STATUS_RESET; + mwifiex_cancel_all_pending_cmd(adapter); + + if (adapter->if_ops.card_reset) + adapter->if_ops.card_reset(adapter); +} + +/* + * This function initializes the private structure and sets default + * values to the members. + * + * Additionally, it also initializes all the locks and sets up all the + * lists. + */ +int mwifiex_init_priv(struct mwifiex_private *priv) +{ + u32 i; + + priv->media_connected = false; + eth_broadcast_addr(priv->curr_addr); + priv->port_open = false; + priv->usb_port = MWIFIEX_USB_EP_DATA; + priv->pkt_tx_ctrl = 0; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->data_rate = 0; /* Initially indicate the rate as auto */ + priv->is_data_rate_auto = true; + priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; + priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; + + priv->sec_info.wep_enabled = 0; + priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; + priv->sec_info.encryption_mode = 0; + for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++) + memset(&priv->wep_key[i], 0, sizeof(struct mwifiex_wep_key)); + priv->wep_key_curr_index = 0; + priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON | + HostCmd_ACT_MAC_ETHERNETII_ENABLE; + + priv->beacon_period = 100; /* beacon interval */ ; + priv->attempted_bss_desc = NULL; + memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params)); + priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL; + + memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid)); + memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid)); + memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf)); + priv->assoc_rsp_size = 0; + priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + priv->atim_window = 0; + priv->adhoc_state = ADHOC_IDLE; + priv->tx_power_level = 0; + priv->max_tx_power_level = 0; + priv->min_tx_power_level = 0; + priv->tx_rate = 0; + priv->rxpd_htinfo = 0; + priv->rxpd_rate = 0; + priv->rate_bitmap = 0; + priv->data_rssi_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->data_nf_last = 0; + priv->bcn_rssi_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->bcn_nf_last = 0; + memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie)); + memset(&priv->aes_key, 0, sizeof(priv->aes_key)); + priv->wpa_ie_len = 0; + priv->wpa_is_gtk_set = false; + + memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf)); + priv->assoc_tlv_buf_len = 0; + memset(&priv->wps, 0, sizeof(priv->wps)); + memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); + priv->gen_ie_buf_len = 0; + memset(priv->vs_ie, 0, sizeof(priv->vs_ie)); + + priv->wmm_required = true; + priv->wmm_enabled = false; + priv->wmm_qosinfo = 0; + priv->curr_bcn_buf = NULL; + priv->curr_bcn_size = 0; + priv->wps_ie = NULL; + priv->wps_ie_len = 0; + priv->ap_11n_enabled = 0; + memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg)); + + priv->scan_block = false; + + priv->csa_chan = 0; + priv->csa_expire_time = 0; + priv->del_list_idx = 0; + priv->hs2_enabled = false; + priv->check_tdls_tx = false; + memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID); + + mwifiex_init_11h_params(priv); + + return mwifiex_add_bss_prio_tbl(priv); +} + +/* + * This function allocates buffers for members of the adapter + * structure. + * + * The memory allocated includes scan table, command buffers, and + * sleep confirm command buffer. In addition, the queues are + * also initialized. + */ +static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter) +{ + int ret; + + /* Allocate command buffer */ + ret = mwifiex_alloc_cmd_buffer(adapter); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to alloc cmd buffer\n", + __func__); + return -1; + } + + adapter->sleep_cfm = + dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) + + INTF_HEADER_LEN); + + if (!adapter->sleep_cfm) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to alloc sleep cfm\t" + " cmd buffer\n", __func__); + return -1; + } + skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN); + + return 0; +} + +/* + * This function initializes the adapter structure and sets default + * values to the members of adapter. + * + * This also initializes the WMM related parameters in the driver private + * structures. + */ +static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) +{ + struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = NULL; + + skb_put(adapter->sleep_cfm, sizeof(struct mwifiex_opt_sleep_confirm)); + + adapter->cmd_sent = false; + + if (adapter->iface_type == MWIFIEX_SDIO) + adapter->data_sent = true; + else + adapter->data_sent = false; + + adapter->cmd_resp_received = false; + adapter->event_received = false; + adapter->data_received = false; + + adapter->surprise_removed = false; + + adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; + + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; + adapter->ps_state = PS_STATE_AWAKE; + adapter->need_to_wakeup = false; + + adapter->scan_mode = HostCmd_BSS_MODE_ANY; + adapter->specific_scan_time = MWIFIEX_SPECIFIC_SCAN_CHAN_TIME; + adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME; + adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME; + adapter->scan_chan_gap_time = MWIFIEX_DEF_SCAN_CHAN_GAP_TIME; + + adapter->scan_probes = 1; + + adapter->multiple_dtim = 1; + + adapter->local_listen_interval = 0; /* default value in firmware + will be used */ + + adapter->is_deep_sleep = false; + + adapter->delay_null_pkt = false; + adapter->delay_to_ps = 1000; + adapter->enhanced_ps_mode = PS_MODE_AUTO; + + adapter->gen_null_pkt = false; /* Disable NULL Pkg generation by + default */ + adapter->pps_uapsd_mode = false; /* Disable pps/uapsd mode by + default */ + adapter->pm_wakeup_card_req = false; + + adapter->pm_wakeup_fw_try = false; + + adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; + + adapter->is_hs_configured = false; + adapter->hs_cfg.conditions = cpu_to_le32(HS_CFG_COND_DEF); + adapter->hs_cfg.gpio = HS_CFG_GPIO_DEF; + adapter->hs_cfg.gap = HS_CFG_GAP_DEF; + adapter->hs_activated = false; + + memset(adapter->event_body, 0, sizeof(adapter->event_body)); + adapter->hw_dot_11n_dev_cap = 0; + adapter->hw_dev_mcs_support = 0; + adapter->sec_chan_offset = 0; + adapter->adhoc_11n_enabled = false; + + mwifiex_wmm_init(adapter); + + sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *) + adapter->sleep_cfm->data; + memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len); + sleep_cfm_buf->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); + sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len); + sleep_cfm_buf->result = 0; + sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM); + sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED); + + memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params)); + memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period)); + adapter->tx_lock_flag = false; + adapter->null_pkt_interval = 0; + adapter->fw_bands = 0; + adapter->config_bands = 0; + adapter->adhoc_start_band = 0; + adapter->scan_channels = NULL; + adapter->fw_release_number = 0; + adapter->fw_cap_info = 0; + memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf)); + adapter->event_cause = 0; + adapter->region_code = 0; + adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT; + adapter->adhoc_awake_period = 0; + memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter)); + adapter->arp_filter_size = 0; + adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; + adapter->key_api_major_ver = 0; + adapter->key_api_minor_ver = 0; + eth_broadcast_addr(adapter->perm_addr); + adapter->iface_limit.sta_intf = MWIFIEX_MAX_STA_NUM; + adapter->iface_limit.uap_intf = MWIFIEX_MAX_UAP_NUM; + adapter->iface_limit.p2p_intf = MWIFIEX_MAX_P2P_NUM; + adapter->active_scan_triggered = false; + setup_timer(&adapter->wakeup_timer, wakeup_timer_fn, + (unsigned long)adapter); +} + +/* + * This function sets trans_start per tx_queue + */ +void mwifiex_set_trans_start(struct net_device *dev) +{ + int i; + + for (i = 0; i < dev->num_tx_queues; i++) + netdev_get_tx_queue(dev, i)->trans_start = jiffies; + + dev->trans_start = jiffies; +} + +/* + * This function wakes up all queues in net_device + */ +void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, + struct mwifiex_adapter *adapter) +{ + unsigned long dev_queue_flags; + unsigned int i; + + spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); + + for (i = 0; i < netdev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); + + if (netif_tx_queue_stopped(txq)) + netif_tx_wake_queue(txq); + } + + spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); +} + +/* + * This function stops all queues in net_device + */ +void mwifiex_stop_net_dev_queue(struct net_device *netdev, + struct mwifiex_adapter *adapter) +{ + unsigned long dev_queue_flags; + unsigned int i; + + spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); + + for (i = 0; i < netdev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); + + if (!netif_tx_queue_stopped(txq)) + netif_tx_stop_queue(txq); + } + + spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); +} + +/* + * This function releases the lock variables and frees the locks and + * associated locks. + */ +static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + s32 i, j; + + /* Free lists */ + list_del(&adapter->cmd_free_q); + list_del(&adapter->cmd_pending_q); + list_del(&adapter->scan_pending_q); + + for (i = 0; i < adapter->priv_num; i++) + list_del(&adapter->bss_prio_tbl[i].bss_prio_head); + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + priv = adapter->priv[i]; + for (j = 0; j < MAX_NUM_TID; ++j) + list_del(&priv->wmm.tid_tbl_ptr[j].ra_list); + list_del(&priv->tx_ba_stream_tbl_ptr); + list_del(&priv->rx_reorder_tbl_ptr); + list_del(&priv->sta_list); + list_del(&priv->auto_tdls_list); + } + } +} + +/* + * This function performs cleanup for adapter structure. + * + * The cleanup is done recursively, by canceling all pending + * commands, freeing the member buffers previously allocated + * (command buffers, scan table buffer, sleep confirm command + * buffer), stopping the timers and calling the cleanup routines + * for every interface. + */ +static void +mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) +{ + int idx; + + if (!adapter) { + pr_err("%s: adapter is NULL\n", __func__); + return; + } + + del_timer(&adapter->wakeup_timer); + mwifiex_cancel_all_pending_cmd(adapter); + wake_up_interruptible(&adapter->cmd_wait_q.wait); + wake_up_interruptible(&adapter->hs_activate_wait_q); + + /* Free lock variables */ + mwifiex_free_lock_list(adapter); + + /* Free command buffer */ + mwifiex_dbg(adapter, INFO, "info: free cmd buffer\n"); + mwifiex_free_cmd_buffer(adapter); + + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + if (adapter->drv_info_dump) { + vfree(adapter->drv_info_dump); + adapter->drv_info_dump = NULL; + adapter->drv_info_size = 0; + } + + if (adapter->sleep_cfm) + dev_kfree_skb_any(adapter->sleep_cfm); +} + +/* + * This function intializes the lock variables and + * the list heads. + */ +int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + s32 i, j; + + spin_lock_init(&adapter->mwifiex_lock); + spin_lock_init(&adapter->int_lock); + spin_lock_init(&adapter->main_proc_lock); + spin_lock_init(&adapter->mwifiex_cmd_lock); + spin_lock_init(&adapter->queue_lock); + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + priv = adapter->priv[i]; + spin_lock_init(&priv->rx_pkt_lock); + spin_lock_init(&priv->wmm.ra_list_spinlock); + spin_lock_init(&priv->curr_bcn_buf_lock); + spin_lock_init(&priv->sta_list_spinlock); + spin_lock_init(&priv->auto_tdls_lock); + } + } + + /* Initialize cmd_free_q */ + INIT_LIST_HEAD(&adapter->cmd_free_q); + /* Initialize cmd_pending_q */ + INIT_LIST_HEAD(&adapter->cmd_pending_q); + /* Initialize scan_pending_q */ + INIT_LIST_HEAD(&adapter->scan_pending_q); + + spin_lock_init(&adapter->cmd_free_q_lock); + spin_lock_init(&adapter->cmd_pending_q_lock); + spin_lock_init(&adapter->scan_pending_q_lock); + spin_lock_init(&adapter->rx_proc_lock); + + skb_queue_head_init(&adapter->rx_data_q); + skb_queue_head_init(&adapter->tx_data_q); + + for (i = 0; i < adapter->priv_num; ++i) { + INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head); + spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock); + } + + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i]) + continue; + priv = adapter->priv[i]; + for (j = 0; j < MAX_NUM_TID; ++j) + INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list); + INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); + INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); + INIT_LIST_HEAD(&priv->sta_list); + INIT_LIST_HEAD(&priv->auto_tdls_list); + skb_queue_head_init(&priv->tdls_txq); + skb_queue_head_init(&priv->bypass_txq); + + spin_lock_init(&priv->tx_ba_stream_tbl_lock); + spin_lock_init(&priv->rx_reorder_tbl_lock); + + spin_lock_init(&priv->ack_status_lock); + idr_init(&priv->ack_status_frames); + } + + return 0; +} + +/* + * This function initializes the firmware. + * + * The following operations are performed sequentially - + * - Allocate adapter structure + * - Initialize the adapter structure + * - Initialize the private structure + * - Add BSS priority tables to the adapter structure + * - For each interface, send the init commands to firmware + * - Send the first command in command pending queue, if available + */ +int mwifiex_init_fw(struct mwifiex_adapter *adapter) +{ + int ret; + struct mwifiex_private *priv; + u8 i, first_sta = true; + int is_cmd_pend_q_empty; + unsigned long flags; + + adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; + + /* Allocate memory for member of adapter structure */ + ret = mwifiex_allocate_adapter(adapter); + if (ret) + return -1; + + /* Initialize adapter structure */ + mwifiex_init_adapter(adapter); + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + priv = adapter->priv[i]; + + /* Initialize private structure */ + ret = mwifiex_init_priv(priv); + if (ret) + return -1; + } + } + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta, + true); + if (ret == -1) + return -1; + + first_sta = false; + } + } + + spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + if (!is_cmd_pend_q_empty) { + /* Send the first command in queue and return */ + if (mwifiex_main_process(adapter) != -1) + ret = -EINPROGRESS; + } else { + adapter->hw_status = MWIFIEX_HW_STATUS_READY; + } + + return ret; +} + +/* + * This function deletes the BSS priority tables. + * + * The function traverses through all the allocated BSS priority nodes + * in every BSS priority table and frees them. + */ +static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv) +{ + int i; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_bss_prio_node *bssprio_node, *tmp_node; + struct list_head *head; + spinlock_t *lock; /* bss priority lock */ + unsigned long flags; + + for (i = 0; i < adapter->priv_num; ++i) { + head = &adapter->bss_prio_tbl[i].bss_prio_head; + lock = &adapter->bss_prio_tbl[i].bss_prio_lock; + mwifiex_dbg(adapter, INFO, + "info: delete BSS priority table,\t" + "bss_type = %d, bss_num = %d, i = %d,\t" + "head = %p\n", + priv->bss_type, priv->bss_num, i, head); + + { + spin_lock_irqsave(lock, flags); + if (list_empty(head)) { + spin_unlock_irqrestore(lock, flags); + continue; + } + list_for_each_entry_safe(bssprio_node, tmp_node, head, + list) { + if (bssprio_node->priv == priv) { + mwifiex_dbg(adapter, INFO, + "info: Delete\t" + "node %p, next = %p\n", + bssprio_node, tmp_node); + list_del(&bssprio_node->list); + kfree(bssprio_node); + } + } + spin_unlock_irqrestore(lock, flags); + } + } +} + +/* + * This function frees the private structure, including cleans + * up the TX and RX queues and frees the BSS priority tables. + */ +void mwifiex_free_priv(struct mwifiex_private *priv) +{ + mwifiex_clean_txrx(priv); + mwifiex_delete_bss_prio_tbl(priv); + mwifiex_free_curr_bcn(priv); +} + +/* + * This function is used to shutdown the driver. + * + * The following operations are performed sequentially - + * - Check if already shut down + * - Make sure the main process has stopped + * - Clean up the Tx and Rx queues + * - Delete BSS priority tables + * - Free the adapter + * - Notify completion + */ +int +mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) +{ + int ret = -EINPROGRESS; + struct mwifiex_private *priv; + s32 i; + unsigned long flags; + struct sk_buff *skb; + + /* mwifiex already shutdown */ + if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY) + return 0; + + adapter->hw_status = MWIFIEX_HW_STATUS_CLOSING; + /* wait for mwifiex_process to complete */ + if (adapter->mwifiex_processing) { + mwifiex_dbg(adapter, WARN, + "main process is still running\n"); + return ret; + } + + /* cancel current command */ + if (adapter->curr_cmd) { + mwifiex_dbg(adapter, WARN, + "curr_cmd is still in processing\n"); + del_timer_sync(&adapter->cmd_timer); + mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); + adapter->curr_cmd = NULL; + } + + /* shut down mwifiex */ + mwifiex_dbg(adapter, MSG, + "info: shutdown mwifiex...\n"); + + /* Clean up Tx/Rx queues and delete BSS priority table */ + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + priv = adapter->priv[i]; + + mwifiex_clean_auto_tdls(priv); + mwifiex_abort_cac(priv); + mwifiex_clean_txrx(priv); + mwifiex_delete_bss_prio_tbl(priv); + } + } + + atomic_set(&adapter->tx_queued, 0); + while ((skb = skb_dequeue(&adapter->tx_data_q))) + mwifiex_write_data_complete(adapter, skb, 0, 0); + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + + while ((skb = skb_dequeue(&adapter->rx_data_q))) { + struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); + + atomic_dec(&adapter->rx_pending); + priv = adapter->priv[rx_info->bss_num]; + if (priv) + priv->stats.rx_dropped++; + + dev_kfree_skb_any(skb); + } + + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + + spin_lock(&adapter->mwifiex_lock); + + mwifiex_adapter_cleanup(adapter); + + spin_unlock(&adapter->mwifiex_lock); + + /* Notify completion */ + ret = mwifiex_shutdown_fw_complete(adapter); + + return ret; +} + +/* + * This function downloads the firmware to the card. + * + * The actual download is preceded by two sanity checks - + * - Check if firmware is already running + * - Check if the interface is the winner to download the firmware + * + * ...and followed by another - + * - Check if the firmware is downloaded successfully + * + * After download is successfully completed, the host interrupts are enabled. + */ +int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, + struct mwifiex_fw_image *pmfw) +{ + int ret; + u32 poll_num = 1; + + if (adapter->if_ops.check_fw_status) { + adapter->winner = 0; + + /* check if firmware is already running */ + ret = adapter->if_ops.check_fw_status(adapter, poll_num); + if (!ret) { + mwifiex_dbg(adapter, MSG, + "WLAN FW already running! Skip FW dnld\n"); + return 0; + } + + poll_num = MAX_FIRMWARE_POLL_TRIES; + + /* check if we are the winner for downloading FW */ + if (!adapter->winner) { + mwifiex_dbg(adapter, MSG, + "FW already running! Skip FW dnld\n"); + goto poll_fw; + } + } + + if (pmfw) { + /* Download firmware with helper */ + ret = adapter->if_ops.prog_fw(adapter, pmfw); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "prog_fw failed ret=%#x\n", ret); + return ret; + } + } + +poll_fw: + /* Check if the firmware is downloaded successfully or not */ + ret = adapter->if_ops.check_fw_status(adapter, poll_num); + if (ret) + mwifiex_dbg(adapter, ERROR, + "FW failed to be active in time\n"); + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/ioctl.h b/drivers/net/wireless/marvell/mwifiex/ioctl.h new file mode 100644 index 000000000000..4f0174c64946 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/ioctl.h @@ -0,0 +1,470 @@ +/* + * Marvell Wireless LAN device driver: ioctl data structures & APIs + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_IOCTL_H_ +#define _MWIFIEX_IOCTL_H_ + +#include + +enum { + MWIFIEX_SCAN_TYPE_UNCHANGED = 0, + MWIFIEX_SCAN_TYPE_ACTIVE, + MWIFIEX_SCAN_TYPE_PASSIVE +}; + +struct mwifiex_user_scan { + u32 scan_cfg_len; + u8 scan_cfg_buf[1]; +}; + +#define MWIFIEX_PROMISC_MODE 1 +#define MWIFIEX_MULTICAST_MODE 2 +#define MWIFIEX_ALL_MULTI_MODE 4 +#define MWIFIEX_MAX_MULTICAST_LIST_SIZE 32 + +struct mwifiex_multicast_list { + u32 mode; + u32 num_multicast_addr; + u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; +}; + +struct mwifiex_chan_freq { + u32 channel; + u32 freq; +}; + +struct mwifiex_ssid_bssid { + struct cfg80211_ssid ssid; + u8 bssid[ETH_ALEN]; +}; + +enum { + BAND_B = 1, + BAND_G = 2, + BAND_A = 4, + BAND_GN = 8, + BAND_AN = 16, + BAND_AAC = 32, +}; + +#define MWIFIEX_WPA_PASSHPHRASE_LEN 64 +struct wpa_param { + u8 pairwise_cipher_wpa; + u8 pairwise_cipher_wpa2; + u8 group_cipher; + u32 length; + u8 passphrase[MWIFIEX_WPA_PASSHPHRASE_LEN]; +}; + +struct wep_key { + u8 key_index; + u8 is_default; + u16 length; + u8 key[WLAN_KEY_LEN_WEP104]; +}; + +#define KEY_MGMT_ON_HOST 0x03 +#define MWIFIEX_AUTH_MODE_AUTO 0xFF +#define BAND_CONFIG_BG 0x00 +#define BAND_CONFIG_A 0x01 +#define MWIFIEX_SUPPORTED_RATES 14 +#define MWIFIEX_SUPPORTED_RATES_EXT 32 +#define MWIFIEX_TDLS_SUPPORTED_RATES 8 +#define MWIFIEX_TDLS_DEF_QOS_CAPAB 0xf +#define MWIFIEX_PRIO_BK 2 +#define MWIFIEX_PRIO_VI 5 + +struct mwifiex_uap_bss_param { + u8 channel; + u8 band_cfg; + u16 rts_threshold; + u16 frag_threshold; + u8 retry_limit; + struct mwifiex_802_11_ssid ssid; + u8 bcast_ssid_ctl; + u8 radio_ctl; + u8 dtim_period; + u16 beacon_period; + u16 auth_mode; + u16 protocol; + u16 key_mgmt; + u16 key_mgmt_operation; + struct wpa_param wpa_cfg; + struct wep_key wep_cfg[NUM_WEP_KEYS]; + struct ieee80211_ht_cap ht_cap; + struct ieee80211_vht_cap vht_cap; + u8 rates[MWIFIEX_SUPPORTED_RATES]; + u32 sta_ao_timer; + u32 ps_sta_ao_timer; + u8 qos_info; + u8 power_constraint; + struct mwifiex_types_wmm_info wmm_info; +}; + +enum { + ADHOC_IDLE, + ADHOC_STARTED, + ADHOC_JOINED, + ADHOC_COALESCED +}; + +struct mwifiex_ds_get_stats { + u32 mcast_tx_frame; + u32 failed; + u32 retry; + u32 multi_retry; + u32 frame_dup; + u32 rts_success; + u32 rts_failure; + u32 ack_failure; + u32 rx_frag; + u32 mcast_rx_frame; + u32 fcs_error; + u32 tx_frame; + u32 wep_icv_error[4]; + u32 bcn_rcv_cnt; + u32 bcn_miss_cnt; +}; + +#define MWIFIEX_MAX_VER_STR_LEN 128 + +struct mwifiex_ver_ext { + u32 version_str_sel; + char version_str[MWIFIEX_MAX_VER_STR_LEN]; +}; + +struct mwifiex_bss_info { + u32 bss_mode; + struct cfg80211_ssid ssid; + u32 bss_chan; + u8 country_code[3]; + u32 media_connected; + u32 max_power_level; + u32 min_power_level; + u32 adhoc_state; + signed int bcn_nf_last; + u32 wep_status; + u32 is_hs_configured; + u32 is_deep_sleep; + u8 bssid[ETH_ALEN]; +}; + +#define MAX_NUM_TID 8 + +#define MAX_RX_WINSIZE 64 + +struct mwifiex_ds_rx_reorder_tbl { + u16 tid; + u8 ta[ETH_ALEN]; + u32 start_win; + u32 win_size; + u32 buffer[MAX_RX_WINSIZE]; +}; + +struct mwifiex_ds_tx_ba_stream_tbl { + u16 tid; + u8 ra[ETH_ALEN]; + u8 amsdu; +}; + +#define DBG_CMD_NUM 5 + +struct tdls_peer_info { + u8 peer_addr[ETH_ALEN]; +}; + +struct mwifiex_debug_info { + unsigned int debug_mask; + u32 int_counter; + u32 packets_out[MAX_NUM_TID]; + u32 tx_buf_size; + u32 curr_tx_buf_size; + u32 tx_tbl_num; + struct mwifiex_ds_tx_ba_stream_tbl + tx_tbl[MWIFIEX_MAX_TX_BASTREAM_SUPPORTED]; + u32 rx_tbl_num; + struct mwifiex_ds_rx_reorder_tbl rx_tbl + [MWIFIEX_MAX_RX_BASTREAM_SUPPORTED]; + u32 tdls_peer_num; + struct tdls_peer_info tdls_list + [MWIFIEX_MAX_TDLS_PEER_SUPPORTED]; + u16 ps_mode; + u32 ps_state; + u8 is_deep_sleep; + u8 pm_wakeup_card_req; + u32 pm_wakeup_fw_try; + u8 is_hs_configured; + u8 hs_activated; + u32 num_cmd_host_to_card_failure; + u32 num_cmd_sleep_cfm_host_to_card_failure; + u32 num_tx_host_to_card_failure; + u32 num_event_deauth; + u32 num_event_disassoc; + u32 num_event_link_lost; + u32 num_cmd_deauth; + u32 num_cmd_assoc_success; + u32 num_cmd_assoc_failure; + u32 num_tx_timeout; + u8 is_cmd_timedout; + u16 timeout_cmd_id; + u16 timeout_cmd_act; + u16 last_cmd_id[DBG_CMD_NUM]; + u16 last_cmd_act[DBG_CMD_NUM]; + u16 last_cmd_index; + u16 last_cmd_resp_id[DBG_CMD_NUM]; + u16 last_cmd_resp_index; + u16 last_event[DBG_CMD_NUM]; + u16 last_event_index; + u8 data_sent; + u8 cmd_sent; + u8 cmd_resp_received; + u8 event_received; +}; + +#define MWIFIEX_KEY_INDEX_UNICAST 0x40000000 +#define PN_LEN 16 + +struct mwifiex_ds_encrypt_key { + u32 key_disable; + u32 key_index; + u32 key_len; + u8 key_material[WLAN_MAX_KEY_LEN]; + u8 mac_addr[ETH_ALEN]; + u32 is_wapi_key; + u8 pn[PN_LEN]; /* packet number */ + u8 pn_len; + u8 is_igtk_key; + u8 is_current_wep_key; + u8 is_rx_seq_valid; +}; + +struct mwifiex_power_cfg { + u32 is_power_auto; + u32 power_level; +}; + +struct mwifiex_ds_hs_cfg { + u32 is_invoke_hostcmd; + /* Bit0: non-unicast data + * Bit1: unicast data + * Bit2: mac events + * Bit3: magic packet + */ + u32 conditions; + u32 gpio; + u32 gap; +}; + +#define DEEP_SLEEP_ON 1 +#define DEEP_SLEEP_OFF 0 +#define DEEP_SLEEP_IDLE_TIME 100 +#define PS_MODE_AUTO 1 + +struct mwifiex_ds_auto_ds { + u16 auto_ds; + u16 idle_time; +}; + +struct mwifiex_ds_pm_cfg { + union { + u32 ps_mode; + struct mwifiex_ds_hs_cfg hs_cfg; + struct mwifiex_ds_auto_ds auto_deep_sleep; + u32 sleep_period; + } param; +}; + +struct mwifiex_11ac_vht_cfg { + u8 band_config; + u8 misc_config; + u32 cap_info; + u32 mcs_tx_set; + u32 mcs_rx_set; +}; + +struct mwifiex_ds_11n_tx_cfg { + u16 tx_htcap; + u16 tx_htinfo; + u16 misc_config; /* Needed for 802.11AC cards only */ +}; + +struct mwifiex_ds_11n_amsdu_aggr_ctrl { + u16 enable; + u16 curr_buf_size; +}; + +struct mwifiex_ds_ant_cfg { + u32 tx_ant; + u32 rx_ant; +}; + +#define MWIFIEX_NUM_OF_CMD_BUFFER 50 +#define MWIFIEX_SIZE_OF_CMD_BUFFER 2048 + +enum { + MWIFIEX_IE_TYPE_GEN_IE = 0, + MWIFIEX_IE_TYPE_ARP_FILTER, +}; + +enum { + MWIFIEX_REG_MAC = 1, + MWIFIEX_REG_BBP, + MWIFIEX_REG_RF, + MWIFIEX_REG_PMIC, + MWIFIEX_REG_CAU, +}; + +struct mwifiex_ds_reg_rw { + __le32 type; + __le32 offset; + __le32 value; +}; + +#define MAX_EEPROM_DATA 256 + +struct mwifiex_ds_read_eeprom { + __le16 offset; + __le16 byte_count; + u8 value[MAX_EEPROM_DATA]; +}; + +struct mwifiex_ds_mem_rw { + u32 addr; + u32 value; +}; + +#define IEEE_MAX_IE_SIZE 256 + +#define MWIFIEX_IE_HDR_SIZE (sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE) + +struct mwifiex_ds_misc_gen_ie { + u32 type; + u32 len; + u8 ie_data[IEEE_MAX_IE_SIZE]; +}; + +struct mwifiex_ds_misc_cmd { + u32 len; + u8 cmd[MWIFIEX_SIZE_OF_CMD_BUFFER]; +}; + +#define BITMASK_BCN_RSSI_LOW BIT(0) +#define BITMASK_BCN_RSSI_HIGH BIT(4) + +enum subsc_evt_rssi_state { + EVENT_HANDLED, + RSSI_LOW_RECVD, + RSSI_HIGH_RECVD +}; + +struct subsc_evt_cfg { + u8 abs_value; + u8 evt_freq; +}; + +struct mwifiex_ds_misc_subsc_evt { + u16 action; + u16 events; + struct subsc_evt_cfg bcn_l_rssi_cfg; + struct subsc_evt_cfg bcn_h_rssi_cfg; +}; + +#define MWIFIEX_MEF_MAX_BYTESEQ 6 /* non-adjustable */ +#define MWIFIEX_MEF_MAX_FILTERS 10 + +struct mwifiex_mef_filter { + u16 repeat; + u16 offset; + s8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; + u8 filt_type; + u8 filt_action; +}; + +struct mwifiex_mef_entry { + u8 mode; + u8 action; + struct mwifiex_mef_filter filter[MWIFIEX_MEF_MAX_FILTERS]; +}; + +struct mwifiex_ds_mef_cfg { + u32 criteria; + u16 num_entries; + struct mwifiex_mef_entry *mef_entry; +}; + +#define MWIFIEX_MAX_VSIE_LEN (256) +#define MWIFIEX_MAX_VSIE_NUM (8) +#define MWIFIEX_VSIE_MASK_CLEAR 0x00 +#define MWIFIEX_VSIE_MASK_SCAN 0x01 +#define MWIFIEX_VSIE_MASK_ASSOC 0x02 +#define MWIFIEX_VSIE_MASK_ADHOC 0x04 + +enum { + MWIFIEX_FUNC_INIT = 1, + MWIFIEX_FUNC_SHUTDOWN, +}; + +enum COALESCE_OPERATION { + RECV_FILTER_MATCH_TYPE_EQ = 0x80, + RECV_FILTER_MATCH_TYPE_NE, +}; + +enum COALESCE_PACKET_TYPE { + PACKET_TYPE_UNICAST = 1, + PACKET_TYPE_MULTICAST = 2, + PACKET_TYPE_BROADCAST = 3 +}; + +#define MWIFIEX_COALESCE_MAX_RULES 8 +#define MWIFIEX_COALESCE_MAX_BYTESEQ 4 /* non-adjustable */ +#define MWIFIEX_COALESCE_MAX_FILTERS 4 +#define MWIFIEX_MAX_COALESCING_DELAY 100 /* in msecs */ + +struct filt_field_param { + u8 operation; + u8 operand_len; + u16 offset; + u8 operand_byte_stream[MWIFIEX_COALESCE_MAX_BYTESEQ]; +}; + +struct mwifiex_coalesce_rule { + u16 max_coalescing_delay; + u8 num_of_fields; + u8 pkt_type; + struct filt_field_param params[MWIFIEX_COALESCE_MAX_FILTERS]; +}; + +struct mwifiex_ds_coalesce_cfg { + u16 num_of_rules; + struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES]; +}; + +struct mwifiex_ds_tdls_oper { + u16 tdls_action; + u8 peer_mac[ETH_ALEN]; + u16 capability; + u8 qos_info; + u8 *ext_capab; + u8 ext_capab_len; + u8 *supp_rates; + u8 supp_rates_len; + u8 *ht_capab; +}; + +#endif /* !_MWIFIEX_IOCTL_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c new file mode 100644 index 000000000000..3cda1f956f0b --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/join.c @@ -0,0 +1,1525 @@ +/* + * Marvell Wireless LAN device driver: association and ad-hoc start/join + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11ac.h" + +#define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9))) + +/* + * Append a generic IE as a pass through TLV to a TLV buffer. + * + * This function is called from the network join command preparation routine. + * + * If the IE buffer has been setup by the application, this routine appends + * the buffer as a pass through TLV type to the request. + */ +static int +mwifiex_cmd_append_generic_ie(struct mwifiex_private *priv, u8 **buffer) +{ + int ret_len = 0; + struct mwifiex_ie_types_header ie_header; + + /* Null Checks */ + if (!buffer) + return 0; + if (!(*buffer)) + return 0; + + /* + * If there is a generic ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->gen_ie_buf_len) { + mwifiex_dbg(priv->adapter, INFO, + "info: %s: append generic ie len %d to %p\n", + __func__, priv->gen_ie_buf_len, *buffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len = cpu_to_le16(priv->gen_ie_buf_len); + memcpy(*buffer, &ie_header, sizeof(ie_header)); + + /* Increment the return size and the return buffer pointer + param */ + *buffer += sizeof(ie_header); + ret_len += sizeof(ie_header); + + /* Copy the generic IE buffer to the output buffer, advance + pointer */ + memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len); + + /* Increment the return size and the return buffer pointer + param */ + *buffer += priv->gen_ie_buf_len; + ret_len += priv->gen_ie_buf_len; + + /* Reset the generic IE buffer */ + priv->gen_ie_buf_len = 0; + } + + /* return the length appended to the buffer */ + return ret_len; +} + +/* + * Append TSF tracking info from the scan table for the target AP. + * + * This function is called from the network join command preparation routine. + * + * The TSF table TSF sent to the firmware contains two TSF values: + * - The TSF of the target AP from its previous beacon/probe response + * - The TSF timestamp of our local MAC at the time we observed the + * beacon/probe response. + * + * The firmware uses the timestamp values to set an initial TSF value + * in the MAC for the new association after a reassociation attempt. + */ +static int +mwifiex_cmd_append_tsf_tlv(struct mwifiex_private *priv, u8 **buffer, + struct mwifiex_bssdescriptor *bss_desc) +{ + struct mwifiex_ie_types_tsf_timestamp tsf_tlv; + __le64 tsf_val; + + /* Null Checks */ + if (buffer == NULL) + return 0; + if (*buffer == NULL) + return 0; + + memset(&tsf_tlv, 0x00, sizeof(struct mwifiex_ie_types_tsf_timestamp)); + + tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP); + tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val)); + + memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header)); + *buffer += sizeof(tsf_tlv.header); + + /* TSF at the time when beacon/probe_response was received */ + tsf_val = cpu_to_le64(bss_desc->fw_tsf); + memcpy(*buffer, &tsf_val, sizeof(tsf_val)); + *buffer += sizeof(tsf_val); + + tsf_val = cpu_to_le64(bss_desc->timestamp); + + mwifiex_dbg(priv->adapter, INFO, + "info: %s: TSF offset calc: %016llx - %016llx\n", + __func__, bss_desc->timestamp, bss_desc->fw_tsf); + + memcpy(*buffer, &tsf_val, sizeof(tsf_val)); + *buffer += sizeof(tsf_val); + + return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val)); +} + +/* + * This function finds out the common rates between rate1 and rate2. + * + * It will fill common rates in rate1 as output if found. + * + * NOTE: Setting the MSB of the basic rates needs to be taken + * care of, either before or after calling this function. + */ +static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1, + u32 rate1_size, u8 *rate2, u32 rate2_size) +{ + int ret; + u8 *ptr = rate1, *tmp; + u32 i, j; + + tmp = kmemdup(rate1, rate1_size, GFP_KERNEL); + if (!tmp) { + mwifiex_dbg(priv->adapter, ERROR, "failed to alloc tmp buf\n"); + return -ENOMEM; + } + + memset(rate1, 0, rate1_size); + + for (i = 0; i < rate2_size && rate2[i]; i++) { + for (j = 0; j < rate1_size && tmp[j]; j++) { + /* Check common rate, excluding the bit for + basic rate */ + if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) { + *rate1++ = tmp[j]; + break; + } + } + } + + mwifiex_dbg(priv->adapter, INFO, "info: Tx data rate set to %#x\n", + priv->data_rate); + + if (!priv->is_data_rate_auto) { + while (*ptr) { + if ((*ptr & 0x7f) == priv->data_rate) { + ret = 0; + goto done; + } + ptr++; + } + mwifiex_dbg(priv->adapter, ERROR, + "previously set fixed data rate %#x\t" + "is not compatible with the network\n", + priv->data_rate); + + ret = -1; + goto done; + } + + ret = 0; +done: + kfree(tmp); + return ret; +} + +/* + * This function creates the intersection of the rates supported by a + * target BSS and our adapter settings for use in an assoc/join command. + */ +static int +mwifiex_setup_rates_from_bssdesc(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, + u8 *out_rates, u32 *out_rates_size) +{ + u8 card_rates[MWIFIEX_SUPPORTED_RATES]; + u32 card_rates_size; + + /* Copy AP supported rates */ + memcpy(out_rates, bss_desc->supported_rates, MWIFIEX_SUPPORTED_RATES); + /* Get the STA supported rates */ + card_rates_size = mwifiex_get_active_data_rates(priv, card_rates); + /* Get the common rates between AP and STA supported rates */ + if (mwifiex_get_common_rates(priv, out_rates, MWIFIEX_SUPPORTED_RATES, + card_rates, card_rates_size)) { + *out_rates_size = 0; + mwifiex_dbg(priv->adapter, ERROR, + "%s: cannot get common rates\n", + __func__); + return -1; + } + + *out_rates_size = + min_t(size_t, strlen(out_rates), MWIFIEX_SUPPORTED_RATES); + + return 0; +} + +/* + * This function appends a WPS IE. It is called from the network join command + * preparation routine. + * + * If the IE buffer has been setup by the application, this routine appends + * the buffer as a WPS TLV type to the request. + */ +static int +mwifiex_cmd_append_wps_ie(struct mwifiex_private *priv, u8 **buffer) +{ + int retLen = 0; + struct mwifiex_ie_types_header ie_header; + + if (!buffer || !*buffer) + return 0; + + /* + * If there is a wps ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->wps_ie_len) { + mwifiex_dbg(priv->adapter, CMD, + "cmd: append wps ie %d to %p\n", + priv->wps_ie_len, *buffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = cpu_to_le16(TLV_TYPE_MGMT_IE); + ie_header.len = cpu_to_le16(priv->wps_ie_len); + memcpy(*buffer, &ie_header, sizeof(ie_header)); + *buffer += sizeof(ie_header); + retLen += sizeof(ie_header); + + memcpy(*buffer, priv->wps_ie, priv->wps_ie_len); + *buffer += priv->wps_ie_len; + retLen += priv->wps_ie_len; + + } + + kfree(priv->wps_ie); + priv->wps_ie_len = 0; + return retLen; +} + +/* + * This function appends a WAPI IE. + * + * This function is called from the network join command preparation routine. + * + * If the IE buffer has been setup by the application, this routine appends + * the buffer as a WAPI TLV type to the request. + */ +static int +mwifiex_cmd_append_wapi_ie(struct mwifiex_private *priv, u8 **buffer) +{ + int retLen = 0; + struct mwifiex_ie_types_header ie_header; + + /* Null Checks */ + if (buffer == NULL) + return 0; + if (*buffer == NULL) + return 0; + + /* + * If there is a wapi ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->wapi_ie_len) { + mwifiex_dbg(priv->adapter, CMD, + "cmd: append wapi ie %d to %p\n", + priv->wapi_ie_len, *buffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = cpu_to_le16(TLV_TYPE_WAPI_IE); + ie_header.len = cpu_to_le16(priv->wapi_ie_len); + memcpy(*buffer, &ie_header, sizeof(ie_header)); + + /* Increment the return size and the return buffer pointer + param */ + *buffer += sizeof(ie_header); + retLen += sizeof(ie_header); + + /* Copy the wapi IE buffer to the output buffer, advance + pointer */ + memcpy(*buffer, priv->wapi_ie, priv->wapi_ie_len); + + /* Increment the return size and the return buffer pointer + param */ + *buffer += priv->wapi_ie_len; + retLen += priv->wapi_ie_len; + + } + /* return the length appended to the buffer */ + return retLen; +} + +/* + * This function appends rsn ie tlv for wpa/wpa2 security modes. + * It is called from the network join command preparation routine. + */ +static int mwifiex_append_rsn_ie_wpa_wpa2(struct mwifiex_private *priv, + u8 **buffer) +{ + struct mwifiex_ie_types_rsn_param_set *rsn_ie_tlv; + int rsn_ie_len; + + if (!buffer || !(*buffer)) + return 0; + + rsn_ie_tlv = (struct mwifiex_ie_types_rsn_param_set *) (*buffer); + rsn_ie_tlv->header.type = cpu_to_le16((u16) priv->wpa_ie[0]); + rsn_ie_tlv->header.type = cpu_to_le16( + le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF); + rsn_ie_tlv->header.len = cpu_to_le16((u16) priv->wpa_ie[1]); + rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len) + & 0x00FF); + if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2)) + memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2], + le16_to_cpu(rsn_ie_tlv->header.len)); + else + return -1; + + rsn_ie_len = sizeof(rsn_ie_tlv->header) + + le16_to_cpu(rsn_ie_tlv->header.len); + *buffer += rsn_ie_len; + + return rsn_ie_len; +} + +/* + * This function prepares command for association. + * + * This sets the following parameters - + * - Peer MAC address + * - Listen interval + * - Beacon interval + * - Capability information + * + * ...and the following TLVs, as required - + * - SSID TLV + * - PHY TLV + * - SS TLV + * - Rates TLV + * - Authentication TLV + * - Channel TLV + * - WPA/WPA2 IE + * - 11n TLV + * - Vendor specific TLV + * - WMM TLV + * - WAPI IE + * - Generic IE + * - TSF TLV + * + * Preparation also includes - + * - Setting command ID and proper size + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_bssdescriptor *bss_desc) +{ + struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate; + struct mwifiex_ie_types_ssid_param_set *ssid_tlv; + struct mwifiex_ie_types_phy_param_set *phy_tlv; + struct mwifiex_ie_types_ss_param_set *ss_tlv; + struct mwifiex_ie_types_rates_param_set *rates_tlv; + struct mwifiex_ie_types_auth_type *auth_tlv; + struct mwifiex_ie_types_chan_list_param_set *chan_tlv; + u8 rates[MWIFIEX_SUPPORTED_RATES]; + u32 rates_size; + u16 tmp_cap; + u8 *pos; + int rsn_ie_len = 0; + + pos = (u8 *) assoc; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE); + + /* Save so we know which BSS Desc to use in the response handler */ + priv->attempted_bss_desc = bss_desc; + + memcpy(assoc->peer_sta_addr, + bss_desc->mac_address, sizeof(assoc->peer_sta_addr)); + pos += sizeof(assoc->peer_sta_addr); + + /* Set the listen interval */ + assoc->listen_interval = cpu_to_le16(priv->listen_interval); + /* Set the beacon period */ + assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period); + + pos += sizeof(assoc->cap_info_bitmap); + pos += sizeof(assoc->listen_interval); + pos += sizeof(assoc->beacon_period); + pos += sizeof(assoc->dtim_period); + + ssid_tlv = (struct mwifiex_ie_types_ssid_param_set *) pos; + ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID); + ssid_tlv->header.len = cpu_to_le16((u16) bss_desc->ssid.ssid_len); + memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid, + le16_to_cpu(ssid_tlv->header.len)); + pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len); + + phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos; + phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS); + phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set)); + memcpy(&phy_tlv->fh_ds.ds_param_set, + &bss_desc->phy_param_set.ds_param_set.current_chan, + sizeof(phy_tlv->fh_ds.ds_param_set)); + pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len); + + ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos; + ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS); + ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set)); + pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len); + + /* Get the common rates supported between the driver and the BSS Desc */ + if (mwifiex_setup_rates_from_bssdesc + (priv, bss_desc, rates, &rates_size)) + return -1; + + /* Save the data rates into Current BSS state structure */ + priv->curr_bss_params.num_of_rates = rates_size; + memcpy(&priv->curr_bss_params.data_rates, rates, rates_size); + + /* Setup the Rates TLV in the association command */ + rates_tlv = (struct mwifiex_ie_types_rates_param_set *) pos; + rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); + rates_tlv->header.len = cpu_to_le16((u16) rates_size); + memcpy(rates_tlv->rates, rates, rates_size); + pos += sizeof(rates_tlv->header) + rates_size; + mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_CMD: rates size = %d\n", + rates_size); + + /* Add the Authentication type to be used for Auth frames */ + auth_tlv = (struct mwifiex_ie_types_auth_type *) pos; + auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); + auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type)); + if (priv->sec_info.wep_enabled) + auth_tlv->auth_type = cpu_to_le16( + (u16) priv->sec_info.authentication_mode); + else + auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM); + + pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len); + + if (IS_SUPPORT_MULTI_BANDS(priv->adapter) && + !(ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && + (!bss_desc->disable_11n) && + (priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN) && + (bss_desc->bcn_ht_cap) + ) + ) { + /* Append a channel TLV for the channel the attempted AP was + found on */ + chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; + chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + chan_tlv->header.len = + cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); + + memset(chan_tlv->chan_scan_param, 0x00, + sizeof(struct mwifiex_chan_scan_param_set)); + chan_tlv->chan_scan_param[0].chan_number = + (bss_desc->phy_param_set.ds_param_set.current_chan); + mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Chan = %d\n", + chan_tlv->chan_scan_param[0].chan_number); + + chan_tlv->chan_scan_param[0].radio_type = + mwifiex_band_to_radio_type((u8) bss_desc->bss_band); + + mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Band = %d\n", + chan_tlv->chan_scan_param[0].radio_type); + pos += sizeof(chan_tlv->header) + + sizeof(struct mwifiex_chan_scan_param_set); + } + + if (!priv->wps.session_enable) { + if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) + rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); + + if (rsn_ie_len == -1) + return -1; + } + + if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && + (!bss_desc->disable_11n) && + (priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN)) + mwifiex_cmd_append_11n_tlv(priv, bss_desc, &pos); + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && + !bss_desc->disable_11n && !bss_desc->disable_11ac && + priv->adapter->config_bands & BAND_AAC) + mwifiex_cmd_append_11ac_tlv(priv, bss_desc, &pos); + + /* Append vendor specific IE TLV */ + mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_ASSOC, &pos); + + mwifiex_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie, + bss_desc->bcn_ht_cap); + if (priv->sec_info.wapi_enabled && priv->wapi_ie_len) + mwifiex_cmd_append_wapi_ie(priv, &pos); + + if (priv->wps.session_enable && priv->wps_ie_len) + mwifiex_cmd_append_wps_ie(priv, &pos); + + mwifiex_cmd_append_generic_ie(priv, &pos); + + mwifiex_cmd_append_tsf_tlv(priv, &pos, bss_desc); + + mwifiex_11h_process_join(priv, &pos, bss_desc); + + cmd->size = cpu_to_le16((u16) (pos - (u8 *) assoc) + S_DS_GEN); + + /* Set the Capability info at last */ + tmp_cap = bss_desc->cap_info_bitmap; + + if (priv->adapter->config_bands == BAND_B) + tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; + + tmp_cap &= CAPINFO_MASK; + mwifiex_dbg(priv->adapter, INFO, + "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", + tmp_cap, CAPINFO_MASK); + assoc->cap_info_bitmap = cpu_to_le16(tmp_cap); + + return 0; +} + +static const char *assoc_failure_reason_to_str(u16 cap_info) +{ + switch (cap_info) { + case CONNECT_ERR_AUTH_ERR_STA_FAILURE: + return "CONNECT_ERR_AUTH_ERR_STA_FAILURE"; + case CONNECT_ERR_AUTH_MSG_UNHANDLED: + return "CONNECT_ERR_AUTH_MSG_UNHANDLED"; + case CONNECT_ERR_ASSOC_ERR_TIMEOUT: + return "CONNECT_ERR_ASSOC_ERR_TIMEOUT"; + case CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED: + return "CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED"; + case CONNECT_ERR_STA_FAILURE: + return "CONNECT_ERR_STA_FAILURE"; + } + + return "Unknown connect failure"; +} +/* + * Association firmware command response handler + * + * The response buffer for the association command has the following + * memory layout. + * + * For cases where an association response was not received (indicated + * by the CapInfo and AId field): + * + * .------------------------------------------------------------. + * | Header(4 * sizeof(t_u16)): Standard command response hdr | + * .------------------------------------------------------------. + * | cap_info/Error Return(t_u16): | + * | 0xFFFF(-1): Internal error | + * | 0xFFFE(-2): Authentication unhandled message | + * | 0xFFFD(-3): Authentication refused | + * | 0xFFFC(-4): Timeout waiting for AP response | + * .------------------------------------------------------------. + * | status_code(t_u16): | + * | If cap_info is -1: | + * | An internal firmware failure prevented the | + * | command from being processed. The status_code | + * | will be set to 1. | + * | | + * | If cap_info is -2: | + * | An authentication frame was received but was | + * | not handled by the firmware. IEEE Status | + * | code for the failure is returned. | + * | | + * | If cap_info is -3: | + * | An authentication frame was received and the | + * | status_code is the IEEE Status reported in the | + * | response. | + * | | + * | If cap_info is -4: | + * | (1) Association response timeout | + * | (2) Authentication response timeout | + * .------------------------------------------------------------. + * | a_id(t_u16): 0xFFFF | + * .------------------------------------------------------------. + * + * + * For cases where an association response was received, the IEEE + * standard association response frame is returned: + * + * .------------------------------------------------------------. + * | Header(4 * sizeof(t_u16)): Standard command response hdr | + * .------------------------------------------------------------. + * | cap_info(t_u16): IEEE Capability | + * .------------------------------------------------------------. + * | status_code(t_u16): IEEE Status Code | + * .------------------------------------------------------------. + * | a_id(t_u16): IEEE Association ID | + * .------------------------------------------------------------. + * | IEEE IEs(variable): Any received IEs comprising the | + * | remaining portion of a received | + * | association response frame. | + * .------------------------------------------------------------. + * + * For simplistic handling, the status_code field can be used to determine + * an association success (0) or failure (non-zero). + */ +int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret = 0; + struct ieee_types_assoc_rsp *assoc_rsp; + struct mwifiex_bssdescriptor *bss_desc; + bool enable_data = true; + u16 cap_info, status_code, aid; + + assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; + + cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap); + status_code = le16_to_cpu(assoc_rsp->status_code); + aid = le16_to_cpu(assoc_rsp->a_id); + + if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) + dev_err(priv->adapter->dev, + "invalid AID value 0x%x; bits 15:14 not set\n", + aid); + + aid &= ~(BIT(15) | BIT(14)); + + priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN, + sizeof(priv->assoc_rsp_buf)); + + memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size); + + assoc_rsp->a_id = cpu_to_le16(aid); + + if (status_code) { + priv->adapter->dbg.num_cmd_assoc_failure++; + mwifiex_dbg(priv->adapter, ERROR, + "ASSOC_RESP: failed,\t" + "status code=%d err=%#x a_id=%#x\n", + status_code, cap_info, + le16_to_cpu(assoc_rsp->a_id)); + + mwifiex_dbg(priv->adapter, ERROR, "assoc failure: reason %s\n", + assoc_failure_reason_to_str(cap_info)); + if (cap_info == CONNECT_ERR_ASSOC_ERR_TIMEOUT) { + if (status_code == MWIFIEX_ASSOC_CMD_FAILURE_AUTH) { + ret = WLAN_STATUS_AUTH_TIMEOUT; + mwifiex_dbg(priv->adapter, ERROR, + "ASSOC_RESP: AUTH timeout\n"); + } else { + ret = WLAN_STATUS_UNSPECIFIED_FAILURE; + mwifiex_dbg(priv->adapter, ERROR, + "ASSOC_RESP: UNSPECIFIED failure\n"); + } + } else { + ret = status_code; + } + + goto done; + } + + /* Send a Media Connected event, according to the Spec */ + priv->media_connected = true; + + priv->adapter->ps_state = PS_STATE_AWAKE; + priv->adapter->pps_uapsd_mode = false; + priv->adapter->tx_lock_flag = false; + + /* Set the attempted BSSID Index to current */ + bss_desc = priv->attempted_bss_desc; + + mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: %s\n", + bss_desc->ssid.ssid); + + /* Make a copy of current BSSID descriptor */ + memcpy(&priv->curr_bss_params.bss_descriptor, + bss_desc, sizeof(struct mwifiex_bssdescriptor)); + + /* Update curr_bss_params */ + priv->curr_bss_params.bss_descriptor.channel + = bss_desc->phy_param_set.ds_param_set.current_chan; + + priv->curr_bss_params.band = (u8) bss_desc->bss_band; + + if (bss_desc->wmm_ie.vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) + priv->curr_bss_params.wmm_enabled = true; + else + priv->curr_bss_params.wmm_enabled = false; + + if ((priv->wmm_required || bss_desc->bcn_ht_cap) && + priv->curr_bss_params.wmm_enabled) + priv->wmm_enabled = true; + else + priv->wmm_enabled = false; + + priv->curr_bss_params.wmm_uapsd_enabled = false; + + if (priv->wmm_enabled) + priv->curr_bss_params.wmm_uapsd_enabled + = ((bss_desc->wmm_ie.qos_info_bitmap & + IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0); + + mwifiex_dbg(priv->adapter, INFO, + "info: ASSOC_RESP: curr_pkt_filter is %#x\n", + priv->curr_pkt_filter); + if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) + priv->wpa_is_gtk_set = false; + + if (priv->wmm_enabled) { + /* Don't re-enable carrier until we get the WMM_GET_STATUS + event */ + enable_data = false; + } else { + /* Since WMM is not enabled, setup the queues with the + defaults */ + mwifiex_wmm_setup_queue_priorities(priv, NULL); + mwifiex_wmm_setup_ac_downgrade(priv); + } + + if (enable_data) + mwifiex_dbg(priv->adapter, INFO, + "info: post association, re-enabling data flow\n"); + + /* Reset SNR/NF/RSSI values */ + priv->data_rssi_last = 0; + priv->data_nf_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->bcn_rssi_last = 0; + priv->bcn_nf_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->rxpd_rate = 0; + priv->rxpd_htinfo = 0; + + mwifiex_save_curr_bcn(priv); + + priv->adapter->dbg.num_cmd_assoc_success++; + + mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: associated\n"); + + /* Add the ra_list here for infra mode as there will be only 1 ra + always */ + mwifiex_ralist_add(priv, + priv->curr_bss_params.bss_descriptor.mac_address); + + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); + + if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) + priv->scan_block = true; + else + priv->port_open = true; + +done: + /* Need to indicate IOCTL complete */ + if (adapter->curr_cmd->wait_q_enabled) { + if (ret) + adapter->cmd_wait_q.status = -1; + else + adapter->cmd_wait_q.status = 0; + } + + return ret; +} + +/* + * This function prepares command for ad-hoc start. + * + * Driver will fill up SSID, BSS mode, IBSS parameters, physical + * parameters, probe delay, and capability information. Firmware + * will fill up beacon period, basic rates and operational rates. + * + * In addition, the following TLVs are added - + * - Channel TLV + * - Vendor specific IE + * - WPA/WPA2 IE + * - HT Capabilities IE + * - HT Information IE + * + * Preparation also includes - + * - Setting command ID and proper size + * - Ensuring correct endian-ness + */ +int +mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct cfg80211_ssid *req_ssid) +{ + int rsn_ie_len = 0; + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_ad_hoc_start *adhoc_start = + &cmd->params.adhoc_start; + struct mwifiex_bssdescriptor *bss_desc; + u32 cmd_append_size = 0; + u32 i; + u16 tmp_cap; + struct mwifiex_ie_types_chan_list_param_set *chan_tlv; + u8 radio_type; + + struct mwifiex_ie_types_htcap *ht_cap; + struct mwifiex_ie_types_htinfo *ht_info; + u8 *pos = (u8 *) adhoc_start + + sizeof(struct host_cmd_ds_802_11_ad_hoc_start); + + if (!adapter) + return -1; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_START); + + bss_desc = &priv->curr_bss_params.bss_descriptor; + priv->attempted_bss_desc = bss_desc; + + /* + * Fill in the parameters for 2 data structures: + * 1. struct host_cmd_ds_802_11_ad_hoc_start command + * 2. bss_desc + * Driver will fill up SSID, bss_mode,IBSS param, Physical Param, + * probe delay, and Cap info. + * Firmware will fill up beacon period, Basic rates + * and operational rates. + */ + + memset(adhoc_start->ssid, 0, IEEE80211_MAX_SSID_LEN); + + memcpy(adhoc_start->ssid, req_ssid->ssid, req_ssid->ssid_len); + + mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: SSID = %s\n", + adhoc_start->ssid); + + memset(bss_desc->ssid.ssid, 0, IEEE80211_MAX_SSID_LEN); + memcpy(bss_desc->ssid.ssid, req_ssid->ssid, req_ssid->ssid_len); + + bss_desc->ssid.ssid_len = req_ssid->ssid_len; + + /* Set the BSS mode */ + adhoc_start->bss_mode = HostCmd_BSS_MODE_IBSS; + bss_desc->bss_mode = NL80211_IFTYPE_ADHOC; + adhoc_start->beacon_period = cpu_to_le16(priv->beacon_period); + bss_desc->beacon_period = priv->beacon_period; + + /* Set Physical param set */ +/* Parameter IE Id */ +#define DS_PARA_IE_ID 3 +/* Parameter IE length */ +#define DS_PARA_IE_LEN 1 + + adhoc_start->phy_param_set.ds_param_set.element_id = DS_PARA_IE_ID; + adhoc_start->phy_param_set.ds_param_set.len = DS_PARA_IE_LEN; + + if (!mwifiex_get_cfp(priv, adapter->adhoc_start_band, + (u16) priv->adhoc_channel, 0)) { + struct mwifiex_chan_freq_power *cfp; + cfp = mwifiex_get_cfp(priv, adapter->adhoc_start_band, + FIRST_VALID_CHANNEL, 0); + if (cfp) + priv->adhoc_channel = (u8) cfp->channel; + } + + if (!priv->adhoc_channel) { + mwifiex_dbg(adapter, ERROR, + "ADHOC_S_CMD: adhoc_channel cannot be 0\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "info: ADHOC_S_CMD: creating ADHOC on channel %d\n", + priv->adhoc_channel); + + priv->curr_bss_params.bss_descriptor.channel = priv->adhoc_channel; + priv->curr_bss_params.band = adapter->adhoc_start_band; + + bss_desc->channel = priv->adhoc_channel; + adhoc_start->phy_param_set.ds_param_set.current_chan = + priv->adhoc_channel; + + memcpy(&bss_desc->phy_param_set, &adhoc_start->phy_param_set, + sizeof(union ieee_types_phy_param_set)); + + /* Set IBSS param set */ +/* IBSS parameter IE Id */ +#define IBSS_PARA_IE_ID 6 +/* IBSS parameter IE length */ +#define IBSS_PARA_IE_LEN 2 + + adhoc_start->ss_param_set.ibss_param_set.element_id = IBSS_PARA_IE_ID; + adhoc_start->ss_param_set.ibss_param_set.len = IBSS_PARA_IE_LEN; + adhoc_start->ss_param_set.ibss_param_set.atim_window + = cpu_to_le16(priv->atim_window); + memcpy(&bss_desc->ss_param_set, &adhoc_start->ss_param_set, + sizeof(union ieee_types_ss_param_set)); + + /* Set Capability info */ + bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_IBSS; + tmp_cap = WLAN_CAPABILITY_IBSS; + + /* Set up privacy in bss_desc */ + if (priv->sec_info.encryption_mode) { + /* Ad-Hoc capability privacy on */ + mwifiex_dbg(adapter, INFO, + "info: ADHOC_S_CMD: wep_status set privacy to WEP\n"); + bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; + tmp_cap |= WLAN_CAPABILITY_PRIVACY; + } else { + mwifiex_dbg(adapter, INFO, + "info: ADHOC_S_CMD: wep_status NOT set,\t" + "setting privacy to ACCEPT ALL\n"); + bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; + } + + memset(adhoc_start->data_rate, 0, sizeof(adhoc_start->data_rate)); + mwifiex_get_active_data_rates(priv, adhoc_start->data_rate); + if ((adapter->adhoc_start_band & BAND_G) && + (priv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, false)) { + mwifiex_dbg(adapter, ERROR, + "ADHOC_S_CMD: G Protection config failed\n"); + return -1; + } + } + /* Find the last non zero */ + for (i = 0; i < sizeof(adhoc_start->data_rate); i++) + if (!adhoc_start->data_rate[i]) + break; + + priv->curr_bss_params.num_of_rates = i; + + /* Copy the ad-hoc creating rates into Current BSS rate structure */ + memcpy(&priv->curr_bss_params.data_rates, + &adhoc_start->data_rate, priv->curr_bss_params.num_of_rates); + + mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: rates=%4ph\n", + adhoc_start->data_rate); + + mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n"); + + if (IS_SUPPORT_MULTI_BANDS(adapter)) { + /* Append a channel TLV */ + chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; + chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + chan_tlv->header.len = + cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); + + memset(chan_tlv->chan_scan_param, 0x00, + sizeof(struct mwifiex_chan_scan_param_set)); + chan_tlv->chan_scan_param[0].chan_number = + (u8) priv->curr_bss_params.bss_descriptor.channel; + + mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Chan = %d\n", + chan_tlv->chan_scan_param[0].chan_number); + + chan_tlv->chan_scan_param[0].radio_type + = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + if (adapter->adhoc_start_band & BAND_GN || + adapter->adhoc_start_band & BAND_AN) { + if (adapter->sec_chan_offset == + IEEE80211_HT_PARAM_CHA_SEC_ABOVE) + chan_tlv->chan_scan_param[0].radio_type |= + (IEEE80211_HT_PARAM_CHA_SEC_ABOVE << 4); + else if (adapter->sec_chan_offset == + IEEE80211_HT_PARAM_CHA_SEC_BELOW) + chan_tlv->chan_scan_param[0].radio_type |= + (IEEE80211_HT_PARAM_CHA_SEC_BELOW << 4); + } + mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Band = %d\n", + chan_tlv->chan_scan_param[0].radio_type); + pos += sizeof(chan_tlv->header) + + sizeof(struct mwifiex_chan_scan_param_set); + cmd_append_size += + sizeof(chan_tlv->header) + + sizeof(struct mwifiex_chan_scan_param_set); + } + + /* Append vendor specific IE TLV */ + cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, + MWIFIEX_VSIE_MASK_ADHOC, &pos); + + if (priv->sec_info.wpa_enabled) { + rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); + if (rsn_ie_len == -1) + return -1; + cmd_append_size += rsn_ie_len; + } + + if (adapter->adhoc_11n_enabled) { + /* Fill HT CAPABILITY */ + ht_cap = (struct mwifiex_ie_types_htcap *) pos; + memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); + ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_cap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + radio_type = mwifiex_band_to_radio_type( + priv->adapter->config_bands); + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); + + if (adapter->sec_chan_offset == + IEEE80211_HT_PARAM_CHA_SEC_NONE) { + u16 tmp_ht_cap; + + tmp_ht_cap = le16_to_cpu(ht_cap->ht_cap.cap_info); + tmp_ht_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + tmp_ht_cap &= ~IEEE80211_HT_CAP_SGI_40; + ht_cap->ht_cap.cap_info = cpu_to_le16(tmp_ht_cap); + } + + pos += sizeof(struct mwifiex_ie_types_htcap); + cmd_append_size += sizeof(struct mwifiex_ie_types_htcap); + + /* Fill HT INFORMATION */ + ht_info = (struct mwifiex_ie_types_htinfo *) pos; + memset(ht_info, 0, sizeof(struct mwifiex_ie_types_htinfo)); + ht_info->header.type = cpu_to_le16(WLAN_EID_HT_OPERATION); + ht_info->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_operation)); + + ht_info->ht_oper.primary_chan = + (u8) priv->curr_bss_params.bss_descriptor.channel; + if (adapter->sec_chan_offset) { + ht_info->ht_oper.ht_param = adapter->sec_chan_offset; + ht_info->ht_oper.ht_param |= + IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; + } + ht_info->ht_oper.operation_mode = + cpu_to_le16(IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + ht_info->ht_oper.basic_set[0] = 0xff; + pos += sizeof(struct mwifiex_ie_types_htinfo); + cmd_append_size += + sizeof(struct mwifiex_ie_types_htinfo); + } + + cmd->size = + cpu_to_le16((u16)(sizeof(struct host_cmd_ds_802_11_ad_hoc_start) + + S_DS_GEN + cmd_append_size)); + + if (adapter->adhoc_start_band == BAND_B) + tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; + else + tmp_cap |= WLAN_CAPABILITY_SHORT_SLOT_TIME; + + adhoc_start->cap_info_bitmap = cpu_to_le16(tmp_cap); + + return 0; +} + +/* + * This function prepares command for ad-hoc join. + * + * Most of the parameters are set up by copying from the target BSS descriptor + * from the scan response. + * + * In addition, the following TLVs are added - + * - Channel TLV + * - Vendor specific IE + * - WPA/WPA2 IE + * - 11n IE + * + * Preparation also includes - + * - Setting command ID and proper size + * - Ensuring correct endian-ness + */ +int +mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_bssdescriptor *bss_desc) +{ + int rsn_ie_len = 0; + struct host_cmd_ds_802_11_ad_hoc_join *adhoc_join = + &cmd->params.adhoc_join; + struct mwifiex_ie_types_chan_list_param_set *chan_tlv; + u32 cmd_append_size = 0; + u16 tmp_cap; + u32 i, rates_size = 0; + u16 curr_pkt_filter; + u8 *pos = + (u8 *) adhoc_join + + sizeof(struct host_cmd_ds_802_11_ad_hoc_join); + +/* Use G protection */ +#define USE_G_PROTECTION 0x02 + if (bss_desc->erp_flags & USE_G_PROTECTION) { + curr_pkt_filter = + priv-> + curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &curr_pkt_filter, false)) { + mwifiex_dbg(priv->adapter, ERROR, + "ADHOC_J_CMD: G Protection config failed\n"); + return -1; + } + } + + priv->attempted_bss_desc = bss_desc; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_JOIN); + + adhoc_join->bss_descriptor.bss_mode = HostCmd_BSS_MODE_IBSS; + + adhoc_join->bss_descriptor.beacon_period + = cpu_to_le16(bss_desc->beacon_period); + + memcpy(&adhoc_join->bss_descriptor.bssid, + &bss_desc->mac_address, ETH_ALEN); + + memcpy(&adhoc_join->bss_descriptor.ssid, + &bss_desc->ssid.ssid, bss_desc->ssid.ssid_len); + + memcpy(&adhoc_join->bss_descriptor.phy_param_set, + &bss_desc->phy_param_set, + sizeof(union ieee_types_phy_param_set)); + + memcpy(&adhoc_join->bss_descriptor.ss_param_set, + &bss_desc->ss_param_set, sizeof(union ieee_types_ss_param_set)); + + tmp_cap = bss_desc->cap_info_bitmap; + + tmp_cap &= CAPINFO_MASK; + + mwifiex_dbg(priv->adapter, INFO, + "info: ADHOC_J_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", + tmp_cap, CAPINFO_MASK); + + /* Information on BSSID descriptor passed to FW */ + mwifiex_dbg(priv->adapter, INFO, + "info: ADHOC_J_CMD: BSSID=%pM, SSID='%s'\n", + adhoc_join->bss_descriptor.bssid, + adhoc_join->bss_descriptor.ssid); + + for (i = 0; i < MWIFIEX_SUPPORTED_RATES && + bss_desc->supported_rates[i]; i++) + ; + rates_size = i; + + /* Copy Data Rates from the Rates recorded in scan response */ + memset(adhoc_join->bss_descriptor.data_rates, 0, + sizeof(adhoc_join->bss_descriptor.data_rates)); + memcpy(adhoc_join->bss_descriptor.data_rates, + bss_desc->supported_rates, rates_size); + + /* Copy the adhoc join rates into Current BSS state structure */ + priv->curr_bss_params.num_of_rates = rates_size; + memcpy(&priv->curr_bss_params.data_rates, bss_desc->supported_rates, + rates_size); + + /* Copy the channel information */ + priv->curr_bss_params.bss_descriptor.channel = bss_desc->channel; + priv->curr_bss_params.band = (u8) bss_desc->bss_band; + + if (priv->sec_info.wep_enabled || priv->sec_info.wpa_enabled) + tmp_cap |= WLAN_CAPABILITY_PRIVACY; + + if (IS_SUPPORT_MULTI_BANDS(priv->adapter)) { + /* Append a channel TLV */ + chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; + chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + chan_tlv->header.len = + cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); + + memset(chan_tlv->chan_scan_param, 0x00, + sizeof(struct mwifiex_chan_scan_param_set)); + chan_tlv->chan_scan_param[0].chan_number = + (bss_desc->phy_param_set.ds_param_set.current_chan); + mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Chan=%d\n", + chan_tlv->chan_scan_param[0].chan_number); + + chan_tlv->chan_scan_param[0].radio_type = + mwifiex_band_to_radio_type((u8) bss_desc->bss_band); + + mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Band=%d\n", + chan_tlv->chan_scan_param[0].radio_type); + pos += sizeof(chan_tlv->header) + + sizeof(struct mwifiex_chan_scan_param_set); + cmd_append_size += sizeof(chan_tlv->header) + + sizeof(struct mwifiex_chan_scan_param_set); + } + + if (priv->sec_info.wpa_enabled) + rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); + if (rsn_ie_len == -1) + return -1; + cmd_append_size += rsn_ie_len; + + if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) + cmd_append_size += mwifiex_cmd_append_11n_tlv(priv, + bss_desc, &pos); + + /* Append vendor specific IE TLV */ + cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, + MWIFIEX_VSIE_MASK_ADHOC, &pos); + + cmd->size = cpu_to_le16 + ((u16) (sizeof(struct host_cmd_ds_802_11_ad_hoc_join) + + S_DS_GEN + cmd_append_size)); + + adhoc_join->bss_descriptor.cap_info_bitmap = cpu_to_le16(tmp_cap); + + return 0; +} + +/* + * This function handles the command response of ad-hoc start and + * ad-hoc join. + * + * The function generates a device-connected event to notify + * the applications, in case of successful ad-hoc start/join, and + * saves the beacon buffer. + */ +int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_ad_hoc_result *adhoc_result; + struct mwifiex_bssdescriptor *bss_desc; + u16 reason_code; + + adhoc_result = &resp->params.adhoc_result; + + bss_desc = priv->attempted_bss_desc; + + /* Join result code 0 --> SUCCESS */ + reason_code = le16_to_cpu(resp->result); + if (reason_code) { + mwifiex_dbg(priv->adapter, ERROR, "ADHOC_RESP: failed\n"); + if (priv->media_connected) + mwifiex_reset_connect_state(priv, reason_code); + + memset(&priv->curr_bss_params.bss_descriptor, + 0x00, sizeof(struct mwifiex_bssdescriptor)); + + ret = -1; + goto done; + } + + /* Send a Media Connected event, according to the Spec */ + priv->media_connected = true; + + if (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_AD_HOC_START) { + mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_S_RESP %s\n", + bss_desc->ssid.ssid); + + /* Update the created network descriptor with the new BSSID */ + memcpy(bss_desc->mac_address, + adhoc_result->bssid, ETH_ALEN); + + priv->adhoc_state = ADHOC_STARTED; + } else { + /* + * Now the join cmd should be successful. + * If BSSID has changed use SSID to compare instead of BSSID + */ + mwifiex_dbg(priv->adapter, INFO, + "info: ADHOC_J_RESP %s\n", + bss_desc->ssid.ssid); + + /* + * Make a copy of current BSSID descriptor, only needed for + * join since the current descriptor is already being used + * for adhoc start + */ + memcpy(&priv->curr_bss_params.bss_descriptor, + bss_desc, sizeof(struct mwifiex_bssdescriptor)); + + priv->adhoc_state = ADHOC_JOINED; + } + + mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: channel = %d\n", + priv->adhoc_channel); + mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: BSSID = %pM\n", + priv->curr_bss_params.bss_descriptor.mac_address); + + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); + + mwifiex_save_curr_bcn(priv); + +done: + /* Need to indicate IOCTL complete */ + if (adapter->curr_cmd->wait_q_enabled) { + if (ret) + adapter->cmd_wait_q.status = -1; + else + adapter->cmd_wait_q.status = 0; + + } + + return ret; +} + +/* + * This function associates to a specific BSS discovered in a scan. + * + * It clears any past association response stored for application + * retrieval and calls the command preparation routine to send the + * command to firmware. + */ +int mwifiex_associate(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + /* Return error if the adapter is not STA role or table entry + * is not marked as infra. + */ + if ((GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) || + (bss_desc->bss_mode != NL80211_IFTYPE_STATION)) + return -1; + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && + !bss_desc->disable_11n && !bss_desc->disable_11ac && + priv->adapter->config_bands & BAND_AAC) + mwifiex_set_11ac_ba_params(priv); + else + mwifiex_set_ba_params(priv); + + /* Clear any past association response stored for application + retrieval */ + priv->assoc_rsp_size = 0; + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_ASSOCIATE, + HostCmd_ACT_GEN_SET, 0, bss_desc, true); +} + +/* + * This function starts an ad-hoc network. + * + * It calls the command preparation routine to send the command to firmware. + */ +int +mwifiex_adhoc_start(struct mwifiex_private *priv, + struct cfg80211_ssid *adhoc_ssid) +{ + mwifiex_dbg(priv->adapter, INFO, "info: Adhoc Channel = %d\n", + priv->adhoc_channel); + mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.channel = %d\n", + priv->curr_bss_params.bss_descriptor.channel); + mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.band = %d\n", + priv->curr_bss_params.band); + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && + priv->adapter->config_bands & BAND_AAC) + mwifiex_set_11ac_ba_params(priv); + else + mwifiex_set_ba_params(priv); + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_START, + HostCmd_ACT_GEN_SET, 0, adhoc_ssid, true); +} + +/* + * This function joins an ad-hoc network found in a previous scan. + * + * It calls the command preparation routine to send the command to firmware, + * if already not connected to the requested SSID. + */ +int mwifiex_adhoc_join(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + mwifiex_dbg(priv->adapter, INFO, + "info: adhoc join: curr_bss ssid =%s\n", + priv->curr_bss_params.bss_descriptor.ssid.ssid); + mwifiex_dbg(priv->adapter, INFO, + "info: adhoc join: curr_bss ssid_len =%u\n", + priv->curr_bss_params.bss_descriptor.ssid.ssid_len); + mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid =%s\n", + bss_desc->ssid.ssid); + mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid_len =%u\n", + bss_desc->ssid.ssid_len); + + /* Check if the requested SSID is already joined */ + if (priv->curr_bss_params.bss_descriptor.ssid.ssid_len && + !mwifiex_ssid_cmp(&bss_desc->ssid, + &priv->curr_bss_params.bss_descriptor.ssid) && + (priv->curr_bss_params.bss_descriptor.bss_mode == + NL80211_IFTYPE_ADHOC)) { + mwifiex_dbg(priv->adapter, INFO, + "info: ADHOC_J_CMD: new ad-hoc SSID\t" + "is the same as current; not attempting to re-join\n"); + return -1; + } + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && + !bss_desc->disable_11n && !bss_desc->disable_11ac && + priv->adapter->config_bands & BAND_AAC) + mwifiex_set_11ac_ba_params(priv); + else + mwifiex_set_ba_params(priv); + + mwifiex_dbg(priv->adapter, INFO, + "info: curr_bss_params.channel = %d\n", + priv->curr_bss_params.bss_descriptor.channel); + mwifiex_dbg(priv->adapter, INFO, + "info: curr_bss_params.band = %c\n", + priv->curr_bss_params.band); + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_JOIN, + HostCmd_ACT_GEN_SET, 0, bss_desc, true); +} + +/* + * This function deauthenticates/disconnects from infra network by sending + * deauthentication request. + */ +static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac) +{ + u8 mac_address[ETH_ALEN]; + int ret; + + if (!mac || is_zero_ether_addr(mac)) + memcpy(mac_address, + priv->curr_bss_params.bss_descriptor.mac_address, + ETH_ALEN); + else + memcpy(mac_address, mac, ETH_ALEN); + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, + HostCmd_ACT_GEN_SET, 0, mac_address, true); + + return ret; +} + +/* + * This function deauthenticates/disconnects from a BSS. + * + * In case of infra made, it sends deauthentication request, and + * in case of ad-hoc mode, a stop network request is sent to the firmware. + * In AP mode, a command to stop bss is sent to firmware. + */ +int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac) +{ + int ret = 0; + + if (!priv->media_connected) + return 0; + + switch (priv->bss_mode) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + ret = mwifiex_deauthenticate_infra(priv, mac); + if (ret) + cfg80211_disconnected(priv->netdev, 0, NULL, 0, + true, GFP_KERNEL); + break; + case NL80211_IFTYPE_ADHOC: + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true); + case NL80211_IFTYPE_AP: + return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true); + default: + break; + } + + return ret; +} + +/* This function deauthenticates/disconnects from all BSS. */ +void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + mwifiex_deauthenticate(priv, NULL); + } +} +EXPORT_SYMBOL_GPL(mwifiex_deauthenticate_all); + +/* + * This function converts band to radio type used in channel TLV. + */ +u8 +mwifiex_band_to_radio_type(u8 band) +{ + switch (band) { + case BAND_A: + case BAND_AN: + case BAND_A | BAND_AN: + case BAND_A | BAND_AN | BAND_AAC: + return HostCmd_SCAN_RADIO_TYPE_A; + case BAND_B: + case BAND_G: + case BAND_B | BAND_G: + default: + return HostCmd_SCAN_RADIO_TYPE_BG; + } +} diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c new file mode 100644 index 000000000000..969ca1e1f3e9 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -0,0 +1,1552 @@ +/* + * Marvell Wireless LAN device driver: major functions + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" +#include "wmm.h" +#include "cfg80211.h" +#include "11n.h" + +#define VERSION "1.0" + +static unsigned int debug_mask = MWIFIEX_DEFAULT_DEBUG_MASK; +module_param(debug_mask, uint, 0); +MODULE_PARM_DESC(debug_mask, "bitmap for debug flags"); + +const char driver_version[] = "mwifiex " VERSION " (%s) "; +static char *cal_data_cfg; +module_param(cal_data_cfg, charp, 0); + +static unsigned short driver_mode; +module_param(driver_mode, ushort, 0); +MODULE_PARM_DESC(driver_mode, + "station=0x1(default), ap-sta=0x3, station-p2p=0x5, ap-sta-p2p=0x7"); + +/* + * This function registers the device and performs all the necessary + * initializations. + * + * The following initialization operations are performed - + * - Allocate adapter structure + * - Save interface specific operations table in adapter + * - Call interface specific initialization routine + * - Allocate private structures + * - Set default adapter structure parameters + * - Initialize locks + * + * In case of any errors during inittialization, this function also ensures + * proper cleanup before exiting. + */ +static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, + void **padapter) +{ + struct mwifiex_adapter *adapter; + int i; + + adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL); + if (!adapter) + return -ENOMEM; + + *padapter = adapter; + adapter->card = card; + + /* Save interface specific operations in adapter */ + memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops)); + adapter->debug_mask = debug_mask; + + /* card specific initialization has been deferred until now .. */ + if (adapter->if_ops.init_if) + if (adapter->if_ops.init_if(adapter)) + goto error; + + adapter->priv_num = 0; + + for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) { + /* Allocate memory for private structure */ + adapter->priv[i] = + kzalloc(sizeof(struct mwifiex_private), GFP_KERNEL); + if (!adapter->priv[i]) + goto error; + + adapter->priv[i]->adapter = adapter; + adapter->priv_num++; + } + mwifiex_init_lock_list(adapter); + + setup_timer(&adapter->cmd_timer, mwifiex_cmd_timeout_func, + (unsigned long)adapter); + + return 0; + +error: + mwifiex_dbg(adapter, ERROR, + "info: leave mwifiex_register with error\n"); + + for (i = 0; i < adapter->priv_num; i++) + kfree(adapter->priv[i]); + + kfree(adapter); + + return -1; +} + +/* + * This function unregisters the device and performs all the necessary + * cleanups. + * + * The following cleanup operations are performed - + * - Free the timers + * - Free beacon buffers + * - Free private structures + * - Free adapter structure + */ +static int mwifiex_unregister(struct mwifiex_adapter *adapter) +{ + s32 i; + + if (adapter->if_ops.cleanup_if) + adapter->if_ops.cleanup_if(adapter); + + del_timer_sync(&adapter->cmd_timer); + + /* Free private structures */ + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + mwifiex_free_curr_bcn(adapter->priv[i]); + kfree(adapter->priv[i]); + } + } + + vfree(adapter->chan_stats); + kfree(adapter); + return 0; +} + +void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + if (adapter->mwifiex_processing) { + adapter->more_task_flag = true; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + } else { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + queue_work(adapter->workqueue, &adapter->main_work); + } +} +EXPORT_SYMBOL_GPL(mwifiex_queue_main_work); + +static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + if (adapter->rx_processing) { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + } else { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + queue_work(adapter->rx_workqueue, &adapter->rx_work); + } +} + +static int mwifiex_process_rx(struct mwifiex_adapter *adapter) +{ + unsigned long flags; + struct sk_buff *skb; + struct mwifiex_rxinfo *rx_info; + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + if (adapter->rx_processing || adapter->rx_locked) { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + goto exit_rx_proc; + } else { + adapter->rx_processing = true; + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + } + + /* Check for Rx data */ + while ((skb = skb_dequeue(&adapter->rx_data_q))) { + atomic_dec(&adapter->rx_pending); + if ((adapter->delay_main_work || + adapter->iface_type == MWIFIEX_USB) && + (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) { + if (adapter->if_ops.submit_rem_rx_urbs) + adapter->if_ops.submit_rem_rx_urbs(adapter); + adapter->delay_main_work = false; + mwifiex_queue_main_work(adapter); + } + rx_info = MWIFIEX_SKB_RXCB(skb); + if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) { + if (adapter->if_ops.deaggr_pkt) + adapter->if_ops.deaggr_pkt(adapter, skb); + dev_kfree_skb_any(skb); + } else { + mwifiex_handle_rx_packet(adapter, skb); + } + } + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + adapter->rx_processing = false; + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + +exit_rx_proc: + return 0; +} + +/* + * The main process. + * + * This function is the main procedure of the driver and handles various driver + * operations. It runs in a loop and provides the core functionalities. + * + * The main responsibilities of this function are - + * - Ensure concurrency control + * - Handle pending interrupts and call interrupt handlers + * - Wake up the card if required + * - Handle command responses and call response handlers + * - Handle events and call event handlers + * - Execute pending commands + * - Transmit pending data packets + */ +int mwifiex_main_process(struct mwifiex_adapter *adapter) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + + /* Check if already processing */ + if (adapter->mwifiex_processing || adapter->main_locked) { + adapter->more_task_flag = true; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + goto exit_main_proc; + } else { + adapter->mwifiex_processing = true; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + } +process_start: + do { + if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) || + (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)) + break; + + /* For non-USB interfaces, If we process interrupts first, it + * would increase RX pending even further. Avoid this by + * checking if rx_pending has crossed high threshold and + * schedule rx work queue and then process interrupts. + * For USB interface, there are no interrupts. We already have + * HIGH_RX_PENDING check in usb.c + */ + if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING && + adapter->iface_type != MWIFIEX_USB) { + adapter->delay_main_work = true; + mwifiex_queue_rx_work(adapter); + break; + } + + /* Handle pending interrupt if any */ + if (adapter->int_status) { + if (adapter->hs_activated) + mwifiex_process_hs_config(adapter); + if (adapter->if_ops.process_int_status) + adapter->if_ops.process_int_status(adapter); + } + + if (adapter->rx_work_enabled && adapter->data_received) + mwifiex_queue_rx_work(adapter); + + /* Need to wake up the card ? */ + if ((adapter->ps_state == PS_STATE_SLEEP) && + (adapter->pm_wakeup_card_req && + !adapter->pm_wakeup_fw_try) && + (is_command_pending(adapter) || + !skb_queue_empty(&adapter->tx_data_q) || + !mwifiex_bypass_txlist_empty(adapter) || + !mwifiex_wmm_lists_empty(adapter))) { + adapter->pm_wakeup_fw_try = true; + mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3)); + adapter->if_ops.wakeup(adapter); + continue; + } + + if (IS_CARD_RX_RCVD(adapter)) { + adapter->data_received = false; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + if (adapter->ps_state == PS_STATE_SLEEP) + adapter->ps_state = PS_STATE_AWAKE; + } else { + /* We have tried to wakeup the card already */ + if (adapter->pm_wakeup_fw_try) + break; + if (adapter->ps_state != PS_STATE_AWAKE) + break; + if (adapter->tx_lock_flag) { + if (adapter->iface_type == MWIFIEX_USB) { + if (!adapter->usb_mc_setup) + break; + } else + break; + } + + if ((!adapter->scan_chan_gap_enabled && + adapter->scan_processing) || adapter->data_sent || + mwifiex_is_tdls_chan_switching + (mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_STA)) || + (mwifiex_wmm_lists_empty(adapter) && + mwifiex_bypass_txlist_empty(adapter) && + skb_queue_empty(&adapter->tx_data_q))) { + if (adapter->cmd_sent || adapter->curr_cmd || + !mwifiex_is_send_cmd_allowed + (mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_STA)) || + (!is_command_pending(adapter))) + break; + } + } + + /* Check for event */ + if (adapter->event_received) { + adapter->event_received = false; + mwifiex_process_event(adapter); + } + + /* Check for Cmd Resp */ + if (adapter->cmd_resp_received) { + adapter->cmd_resp_received = false; + mwifiex_process_cmdresp(adapter); + + /* call mwifiex back when init_fw is done */ + if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) { + adapter->hw_status = MWIFIEX_HW_STATUS_READY; + mwifiex_init_fw_complete(adapter); + } + } + + /* Check if we need to confirm Sleep Request + received previously */ + if (adapter->ps_state == PS_STATE_PRE_SLEEP) { + if (!adapter->cmd_sent && !adapter->curr_cmd) + mwifiex_check_ps_cond(adapter); + } + + /* * The ps_state may have been changed during processing of + * Sleep Request event. + */ + if ((adapter->ps_state == PS_STATE_SLEEP) || + (adapter->ps_state == PS_STATE_PRE_SLEEP) || + (adapter->ps_state == PS_STATE_SLEEP_CFM)) { + continue; + } + + if (adapter->tx_lock_flag) { + if (adapter->iface_type == MWIFIEX_USB) { + if (!adapter->usb_mc_setup) + continue; + } else + continue; + } + + if (!adapter->cmd_sent && !adapter->curr_cmd && + mwifiex_is_send_cmd_allowed + (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { + if (mwifiex_exec_next_cmd(adapter) == -1) { + ret = -1; + break; + } + } + + /** If USB Multi channel setup ongoing, + * wait for ready to tx data. + */ + if (adapter->iface_type == MWIFIEX_USB && + adapter->usb_mc_setup) + continue; + + if ((adapter->scan_chan_gap_enabled || + !adapter->scan_processing) && + !adapter->data_sent && + !skb_queue_empty(&adapter->tx_data_q)) { + mwifiex_process_tx_queue(adapter); + if (adapter->hs_activated) { + adapter->is_hs_configured = false; + mwifiex_hs_activated_event + (mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), + false); + } + } + + if ((adapter->scan_chan_gap_enabled || + !adapter->scan_processing) && + !adapter->data_sent && + !mwifiex_bypass_txlist_empty(adapter) && + !mwifiex_is_tdls_chan_switching + (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { + mwifiex_process_bypass_tx(adapter); + if (adapter->hs_activated) { + adapter->is_hs_configured = false; + mwifiex_hs_activated_event + (mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), + false); + } + } + + if ((adapter->scan_chan_gap_enabled || + !adapter->scan_processing) && + !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter) && + !mwifiex_is_tdls_chan_switching + (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { + mwifiex_wmm_process_tx(adapter); + if (adapter->hs_activated) { + adapter->is_hs_configured = false; + mwifiex_hs_activated_event + (mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), + false); + } + } + + if (adapter->delay_null_pkt && !adapter->cmd_sent && + !adapter->curr_cmd && !is_command_pending(adapter) && + (mwifiex_wmm_lists_empty(adapter) && + mwifiex_bypass_txlist_empty(adapter) && + skb_queue_empty(&adapter->tx_data_q))) { + if (!mwifiex_send_null_packet + (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), + MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | + MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) { + adapter->delay_null_pkt = false; + adapter->ps_state = PS_STATE_SLEEP; + } + break; + } + } while (true); + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + if (adapter->more_task_flag) { + adapter->more_task_flag = false; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + goto process_start; + } + adapter->mwifiex_processing = false; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + +exit_main_proc: + if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) + mwifiex_shutdown_drv(adapter); + return ret; +} +EXPORT_SYMBOL_GPL(mwifiex_main_process); + +/* + * This function frees the adapter structure. + * + * Additionally, this closes the netlink socket, frees the timers + * and private structures. + */ +static void mwifiex_free_adapter(struct mwifiex_adapter *adapter) +{ + if (!adapter) { + pr_err("%s: adapter is NULL\n", __func__); + return; + } + + mwifiex_unregister(adapter); + pr_debug("info: %s: free adapter\n", __func__); +} + +/* + * This function cancels all works in the queue and destroys + * the main workqueue. + */ +static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) +{ + flush_workqueue(adapter->workqueue); + destroy_workqueue(adapter->workqueue); + adapter->workqueue = NULL; + + if (adapter->rx_workqueue) { + flush_workqueue(adapter->rx_workqueue); + destroy_workqueue(adapter->rx_workqueue); + adapter->rx_workqueue = NULL; + } +} + +/* + * This function gets firmware and initializes it. + * + * The main initialization steps followed are - + * - Download the correct firmware to card + * - Issue the init commands to firmware + */ +static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) +{ + int ret; + char fmt[64]; + struct mwifiex_private *priv; + struct mwifiex_adapter *adapter = context; + struct mwifiex_fw_image fw; + struct semaphore *sem = adapter->card_sem; + bool init_failed = false; + struct wireless_dev *wdev; + + if (!firmware) { + mwifiex_dbg(adapter, ERROR, + "Failed to get firmware %s\n", adapter->fw_name); + goto err_dnld_fw; + } + + memset(&fw, 0, sizeof(struct mwifiex_fw_image)); + adapter->firmware = firmware; + fw.fw_buf = (u8 *) adapter->firmware->data; + fw.fw_len = adapter->firmware->size; + + if (adapter->if_ops.dnld_fw) + ret = adapter->if_ops.dnld_fw(adapter, &fw); + else + ret = mwifiex_dnld_fw(adapter, &fw); + if (ret == -1) + goto err_dnld_fw; + + mwifiex_dbg(adapter, MSG, "WLAN FW is active\n"); + + if (cal_data_cfg) { + if ((request_firmware(&adapter->cal_data, cal_data_cfg, + adapter->dev)) < 0) + mwifiex_dbg(adapter, ERROR, + "Cal data request_firmware() failed\n"); + } + + /* enable host interrupt after fw dnld is successful */ + if (adapter->if_ops.enable_int) { + if (adapter->if_ops.enable_int(adapter)) + goto err_dnld_fw; + } + + adapter->init_wait_q_woken = false; + ret = mwifiex_init_fw(adapter); + if (ret == -1) { + goto err_init_fw; + } else if (!ret) { + adapter->hw_status = MWIFIEX_HW_STATUS_READY; + goto done; + } + /* Wait for mwifiex_init to complete */ + wait_event_interruptible(adapter->init_wait_q, + adapter->init_wait_q_woken); + if (adapter->hw_status != MWIFIEX_HW_STATUS_READY) + goto err_init_fw; + + priv = adapter->priv[MWIFIEX_BSS_ROLE_STA]; + if (mwifiex_register_cfg80211(adapter)) { + mwifiex_dbg(adapter, ERROR, + "cannot register with cfg80211\n"); + goto err_init_fw; + } + + if (mwifiex_init_channel_scan_gap(adapter)) { + mwifiex_dbg(adapter, ERROR, + "could not init channel stats table\n"); + goto err_init_fw; + } + + if (driver_mode) { + driver_mode &= MWIFIEX_DRIVER_MODE_BITMASK; + driver_mode |= MWIFIEX_DRIVER_MODE_STA; + } + + rtnl_lock(); + /* Create station interface by default */ + wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM, + NL80211_IFTYPE_STATION, NULL, NULL); + if (IS_ERR(wdev)) { + mwifiex_dbg(adapter, ERROR, + "cannot create default STA interface\n"); + rtnl_unlock(); + goto err_add_intf; + } + + if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) { + wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM, + NL80211_IFTYPE_AP, NULL, NULL); + if (IS_ERR(wdev)) { + mwifiex_dbg(adapter, ERROR, + "cannot create AP interface\n"); + rtnl_unlock(); + goto err_add_intf; + } + } + + if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) { + wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM, + NL80211_IFTYPE_P2P_CLIENT, NULL, + NULL); + if (IS_ERR(wdev)) { + mwifiex_dbg(adapter, ERROR, + "cannot create p2p client interface\n"); + rtnl_unlock(); + goto err_add_intf; + } + } + rtnl_unlock(); + + mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); + mwifiex_dbg(adapter, MSG, "driver_version = %s\n", fmt); + goto done; + +err_add_intf: + wiphy_unregister(adapter->wiphy); + wiphy_free(adapter->wiphy); +err_init_fw: + if (adapter->if_ops.disable_int) + adapter->if_ops.disable_int(adapter); +err_dnld_fw: + mwifiex_dbg(adapter, ERROR, + "info: %s: unregister device\n", __func__); + if (adapter->if_ops.unregister_dev) + adapter->if_ops.unregister_dev(adapter); + + if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { + pr_debug("info: %s: shutdown mwifiex\n", __func__); + adapter->init_wait_q_woken = false; + + if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) + wait_event_interruptible(adapter->init_wait_q, + adapter->init_wait_q_woken); + } + adapter->surprise_removed = true; + mwifiex_terminate_workqueue(adapter); + init_failed = true; +done: + if (adapter->cal_data) { + release_firmware(adapter->cal_data); + adapter->cal_data = NULL; + } + if (adapter->firmware) { + release_firmware(adapter->firmware); + adapter->firmware = NULL; + } + if (init_failed) + mwifiex_free_adapter(adapter); + up(sem); + return; +} + +/* + * This function initializes the hardware and gets firmware. + */ +static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter) +{ + int ret; + + ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name, + adapter->dev, GFP_KERNEL, adapter, + mwifiex_fw_dpc); + if (ret < 0) + mwifiex_dbg(adapter, ERROR, + "request_firmware_nowait error %d\n", ret); + return ret; +} + +/* + * CFG802.11 network device handler for open. + * + * Starts the data queue. + */ +static int +mwifiex_open(struct net_device *dev) +{ + netif_carrier_off(dev); + + return 0; +} + +/* + * CFG802.11 network device handler for close. + */ +static int +mwifiex_close(struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (priv->scan_request) { + mwifiex_dbg(priv->adapter, INFO, + "aborting scan on ndo_stop\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + priv->scan_aborting = true; + } + + return 0; +} + +static bool +mwifiex_bypass_tx_queue(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; + + if (ntohs(eth_hdr->h_proto) == ETH_P_PAE || + mwifiex_is_skb_mgmt_frame(skb) || + (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + (ntohs(eth_hdr->h_proto) == ETH_P_TDLS))) { + mwifiex_dbg(priv->adapter, DATA, + "bypass txqueue; eth type %#x, mgmt %d\n", + ntohs(eth_hdr->h_proto), + mwifiex_is_skb_mgmt_frame(skb)); + return true; + } + + return false; +} +/* + * Add buffer into wmm tx queue and queue work to transmit it. + */ +int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb) +{ + struct netdev_queue *txq; + int index = mwifiex_1d_to_wmm_queue[skb->priority]; + + if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) { + txq = netdev_get_tx_queue(priv->netdev, index); + if (!netif_tx_queue_stopped(txq)) { + netif_tx_stop_queue(txq); + mwifiex_dbg(priv->adapter, DATA, + "stop queue: %d\n", index); + } + } + + if (mwifiex_bypass_tx_queue(priv, skb)) { + atomic_inc(&priv->adapter->tx_pending); + atomic_inc(&priv->adapter->bypass_tx_pending); + mwifiex_wmm_add_buf_bypass_txqueue(priv, skb); + } else { + atomic_inc(&priv->adapter->tx_pending); + mwifiex_wmm_add_buf_txqueue(priv, skb); + } + + mwifiex_queue_main_work(priv->adapter); + + return 0; +} + +struct sk_buff * +mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, + struct sk_buff *skb, u8 flag, u64 *cookie) +{ + struct sk_buff *orig_skb = skb; + struct mwifiex_txinfo *tx_info, *orig_tx_info; + + skb = skb_clone(skb, GFP_ATOMIC); + if (skb) { + unsigned long flags; + int id; + + spin_lock_irqsave(&priv->ack_status_lock, flags); + id = idr_alloc(&priv->ack_status_frames, orig_skb, + 1, 0xff, GFP_ATOMIC); + spin_unlock_irqrestore(&priv->ack_status_lock, flags); + + if (id >= 0) { + tx_info = MWIFIEX_SKB_TXCB(skb); + tx_info->ack_frame_id = id; + tx_info->flags |= flag; + orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb); + orig_tx_info->ack_frame_id = id; + orig_tx_info->flags |= flag; + + if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie) + orig_tx_info->cookie = *cookie; + + } else if (skb_shared(skb)) { + kfree_skb(orig_skb); + } else { + kfree_skb(skb); + skb = orig_skb; + } + } else { + /* couldn't clone -- lose tx status ... */ + skb = orig_skb; + } + + return skb; +} + +/* + * CFG802.11 network device handler for data transmission. + */ +static int +mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct sk_buff *new_skb; + struct mwifiex_txinfo *tx_info; + bool multicast; + + mwifiex_dbg(priv->adapter, DATA, + "data: %lu BSS(%d-%d): Data <= kernel\n", + jiffies, priv->bss_type, priv->bss_num); + + if (priv->adapter->surprise_removed) { + kfree_skb(skb); + priv->stats.tx_dropped++; + return 0; + } + if (!skb->len || (skb->len > ETH_FRAME_LEN)) { + mwifiex_dbg(priv->adapter, ERROR, + "Tx: bad skb len %d\n", skb->len); + kfree_skb(skb); + priv->stats.tx_dropped++; + return 0; + } + if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { + mwifiex_dbg(priv->adapter, DATA, + "data: Tx: insufficient skb headroom %d\n", + skb_headroom(skb)); + /* Insufficient skb headroom - allocate a new skb */ + new_skb = + skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + if (unlikely(!new_skb)) { + mwifiex_dbg(priv->adapter, ERROR, + "Tx: cannot alloca new_skb\n"); + kfree_skb(skb); + priv->stats.tx_dropped++; + return 0; + } + kfree_skb(skb); + skb = new_skb; + mwifiex_dbg(priv->adapter, INFO, + "info: new skb headroomd %d\n", + skb_headroom(skb)); + } + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = skb->len; + + multicast = is_multicast_ether_addr(skb->data); + + if (unlikely(!multicast && skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS && + priv->adapter->fw_api_ver == MWIFIEX_FW_V15)) + skb = mwifiex_clone_skb_for_tx_status(priv, + skb, + MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL); + + /* Record the current time the packet was queued; used to + * determine the amount of time the packet was queued in + * the driver before it was sent to the firmware. + * The delay is then sent along with the packet to the + * firmware for aggregate delay calculation for stats and + * MSDU lifetime expiry. + */ + __net_timestamp(skb); + + if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->bss_type == MWIFIEX_BSS_TYPE_STA && + !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) { + if (priv->adapter->auto_tdls && priv->check_tdls_tx) + mwifiex_tdls_check_tx(priv, skb); + } + + mwifiex_queue_tx_pkt(priv, skb); + + return 0; +} + +/* + * CFG802.11 network device handler for setting MAC address. + */ +static int +mwifiex_set_mac_address(struct net_device *dev, void *addr) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct sockaddr *hw_addr = addr; + int ret; + + memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN); + + /* Send request to firmware */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS, + HostCmd_ACT_GEN_SET, 0, NULL, true); + + if (!ret) + memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN); + else + mwifiex_dbg(priv->adapter, ERROR, + "set mac address failed: ret=%d\n", ret); + + memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); + + return ret; +} + +/* + * CFG802.11 network device handler for setting multicast list. + */ +static void mwifiex_set_multicast_list(struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_multicast_list mcast_list; + + if (dev->flags & IFF_PROMISC) { + mcast_list.mode = MWIFIEX_PROMISC_MODE; + } else if (dev->flags & IFF_ALLMULTI || + netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) { + mcast_list.mode = MWIFIEX_ALL_MULTI_MODE; + } else { + mcast_list.mode = MWIFIEX_MULTICAST_MODE; + mcast_list.num_multicast_addr = + mwifiex_copy_mcast_addr(&mcast_list, dev); + } + mwifiex_request_set_multicast_list(priv, &mcast_list); +} + +/* + * CFG802.11 network device handler for transmission timeout. + */ +static void +mwifiex_tx_timeout(struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + priv->num_tx_timeout++; + priv->tx_timeout_cnt++; + mwifiex_dbg(priv->adapter, ERROR, + "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n", + jiffies, priv->tx_timeout_cnt, priv->bss_type, + priv->bss_num); + mwifiex_set_trans_start(dev); + + if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD && + priv->adapter->if_ops.card_reset) { + mwifiex_dbg(priv->adapter, ERROR, + "tx_timeout_cnt exceeds threshold.\t" + "Triggering card reset!\n"); + priv->adapter->if_ops.card_reset(priv->adapter); + } +} + +void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + struct mwifiex_private *priv; + u16 tx_buf_size; + int i, ret; + + card->mc_resync_flag = true; + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + if (atomic_read(&card->port[i].tx_data_urb_pending)) { + mwifiex_dbg(adapter, WARN, "pending data urb in sys\n"); + return; + } + } + + card->mc_resync_flag = false; + tx_buf_size = 0xffff; + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, + HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false); + if (ret) + mwifiex_dbg(adapter, ERROR, + "send reconfig tx buf size cmd err\n"); +} +EXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync); + +void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter) +{ + void *p; + char drv_version[64]; + struct usb_card_rec *cardp; + struct sdio_mmc_card *sdio_card; + struct mwifiex_private *priv; + int i, idx; + struct netdev_queue *txq; + struct mwifiex_debug_info *debug_info; + + if (adapter->drv_info_dump) { + vfree(adapter->drv_info_dump); + adapter->drv_info_dump = NULL; + adapter->drv_info_size = 0; + } + + mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump start===\n"); + + adapter->drv_info_dump = vzalloc(MWIFIEX_DRV_INFO_SIZE_MAX); + + if (!adapter->drv_info_dump) + return; + + p = (char *)(adapter->drv_info_dump); + p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); + + mwifiex_drv_get_driver_version(adapter, drv_version, + sizeof(drv_version) - 1); + p += sprintf(p, "driver_version = %s\n", drv_version); + + if (adapter->iface_type == MWIFIEX_USB) { + cardp = (struct usb_card_rec *)adapter->card; + p += sprintf(p, "tx_cmd_urb_pending = %d\n", + atomic_read(&cardp->tx_cmd_urb_pending)); + p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n", + atomic_read(&cardp->port[0].tx_data_urb_pending)); + p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n", + atomic_read(&cardp->port[1].tx_data_urb_pending)); + p += sprintf(p, "rx_cmd_urb_pending = %d\n", + atomic_read(&cardp->rx_cmd_urb_pending)); + p += sprintf(p, "rx_data_urb_pending = %d\n", + atomic_read(&cardp->rx_data_urb_pending)); + } + + p += sprintf(p, "tx_pending = %d\n", + atomic_read(&adapter->tx_pending)); + p += sprintf(p, "rx_pending = %d\n", + atomic_read(&adapter->rx_pending)); + + if (adapter->iface_type == MWIFIEX_SDIO) { + sdio_card = (struct sdio_mmc_card *)adapter->card; + p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port); + p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port); + } + + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i] || !adapter->priv[i]->netdev) + continue; + priv = adapter->priv[i]; + p += sprintf(p, "\n[interface : \"%s\"]\n", + priv->netdev->name); + p += sprintf(p, "wmm_tx_pending[0] = %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + p += sprintf(p, "wmm_tx_pending[1] = %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + p += sprintf(p, "wmm_tx_pending[2] = %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + p += sprintf(p, "wmm_tx_pending[3] = %d\n", + atomic_read(&priv->wmm_tx_pending[3])); + p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ? + "Disconnected" : "Connected"); + p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev) + ? "on" : "off")); + for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) { + txq = netdev_get_tx_queue(priv->netdev, idx); + p += sprintf(p, "tx queue %d:%s ", idx, + netif_tx_queue_stopped(txq) ? + "stopped" : "started"); + } + p += sprintf(p, "\n%s: num_tx_timeout = %d\n", + priv->netdev->name, priv->num_tx_timeout); + } + + if (adapter->iface_type == MWIFIEX_SDIO) { + p += sprintf(p, "\n=== SDIO register dump===\n"); + if (adapter->if_ops.reg_dump) + p += adapter->if_ops.reg_dump(adapter, p); + } + + p += sprintf(p, "\n=== more debug information\n"); + debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL); + if (debug_info) { + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i] || !adapter->priv[i]->netdev) + continue; + priv = adapter->priv[i]; + mwifiex_get_debug_info(priv, debug_info); + p += mwifiex_debug_info_to_buffer(priv, p, debug_info); + break; + } + kfree(debug_info); + } + + adapter->drv_info_size = p - adapter->drv_info_dump; + mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump end===\n"); +} +EXPORT_SYMBOL_GPL(mwifiex_drv_info_dump); + +void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter) +{ + u8 idx, *dump_data, *fw_dump_ptr; + u32 dump_len; + + dump_len = (strlen("========Start dump driverinfo========\n") + + adapter->drv_info_size + + strlen("\n========End dump========\n")); + + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + dump_len += (strlen("========Start dump ") + + strlen(entry->mem_name) + + strlen("========\n") + + (entry->mem_size + 1) + + strlen("\n========End dump========\n")); + } + } + + dump_data = vzalloc(dump_len + 1); + if (!dump_data) + goto done; + + fw_dump_ptr = dump_data; + + /* Dump all the memory data into single file, a userspace script will + * be used to split all the memory data to multiple files + */ + mwifiex_dbg(adapter, MSG, + "== mwifiex dump information to /sys/class/devcoredump start"); + + strcpy(fw_dump_ptr, "========Start dump driverinfo========\n"); + fw_dump_ptr += strlen("========Start dump driverinfo========\n"); + memcpy(fw_dump_ptr, adapter->drv_info_dump, adapter->drv_info_size); + fw_dump_ptr += adapter->drv_info_size; + strcpy(fw_dump_ptr, "\n========End dump========\n"); + fw_dump_ptr += strlen("\n========End dump========\n"); + + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + strcpy(fw_dump_ptr, "========Start dump "); + fw_dump_ptr += strlen("========Start dump "); + + strcpy(fw_dump_ptr, entry->mem_name); + fw_dump_ptr += strlen(entry->mem_name); + + strcpy(fw_dump_ptr, "========\n"); + fw_dump_ptr += strlen("========\n"); + + memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size); + fw_dump_ptr += entry->mem_size; + + strcpy(fw_dump_ptr, "\n========End dump========\n"); + fw_dump_ptr += strlen("\n========End dump========\n"); + } + } + + /* device dump data will be free in device coredump release function + * after 5 min + */ + dev_coredumpv(adapter->dev, dump_data, dump_len, GFP_KERNEL); + mwifiex_dbg(adapter, MSG, + "== mwifiex dump information to /sys/class/devcoredump end"); + +done: + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + if (adapter->drv_info_dump) { + vfree(adapter->drv_info_dump); + adapter->drv_info_dump = NULL; + adapter->drv_info_size = 0; + } +} +EXPORT_SYMBOL_GPL(mwifiex_upload_device_dump); + +/* + * CFG802.11 network device handler for statistics retrieval. + */ +static struct net_device_stats *mwifiex_get_stats(struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + return &priv->stats; +} + +static u16 +mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback) +{ + skb->priority = cfg80211_classify8021d(skb, NULL); + return mwifiex_1d_to_wmm_queue[skb->priority]; +} + +/* Network device handlers */ +static const struct net_device_ops mwifiex_netdev_ops = { + .ndo_open = mwifiex_open, + .ndo_stop = mwifiex_close, + .ndo_start_xmit = mwifiex_hard_start_xmit, + .ndo_set_mac_address = mwifiex_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = mwifiex_tx_timeout, + .ndo_get_stats = mwifiex_get_stats, + .ndo_set_rx_mode = mwifiex_set_multicast_list, + .ndo_select_queue = mwifiex_netdev_select_wmm_queue, +}; + +/* + * This function initializes the private structure parameters. + * + * The following wait queues are initialized - + * - IOCTL wait queue + * - Command wait queue + * - Statistics wait queue + * + * ...and the following default parameters are set - + * - Current key index : Set to 0 + * - Rate index : Set to auto + * - Media connected : Set to disconnected + * - Adhoc link sensed : Set to false + * - Nick name : Set to null + * - Number of Tx timeout : Set to 0 + * - Device address : Set to current address + * - Rx histogram statistc : Set to 0 + * + * In addition, the CFG80211 work queue is also created. + */ +void mwifiex_init_priv_params(struct mwifiex_private *priv, + struct net_device *dev) +{ + dev->netdev_ops = &mwifiex_netdev_ops; + dev->destructor = free_netdev; + /* Initialize private structure */ + priv->current_key_index = 0; + priv->media_connected = false; + memset(priv->mgmt_ie, 0, + sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX); + priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK; + priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK; + priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK; + priv->gen_idx = MWIFIEX_AUTO_IDX_MASK; + priv->num_tx_timeout = 0; + ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr); + memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL); + if (priv->hist_data) + mwifiex_hist_data_reset(priv); + } +} + +/* + * This function check if command is pending. + */ +int is_command_pending(struct mwifiex_adapter *adapter) +{ + unsigned long flags; + int is_cmd_pend_q_empty; + + spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + + return !is_cmd_pend_q_empty; +} + +/* + * This is the RX work queue function. + * + * It handles the RX operations. + */ +static void mwifiex_rx_work_queue(struct work_struct *work) +{ + struct mwifiex_adapter *adapter = + container_of(work, struct mwifiex_adapter, rx_work); + + if (adapter->surprise_removed) + return; + mwifiex_process_rx(adapter); +} + +/* + * This is the main work queue function. + * + * It handles the main process, which in turn handles the complete + * driver operations. + */ +static void mwifiex_main_work_queue(struct work_struct *work) +{ + struct mwifiex_adapter *adapter = + container_of(work, struct mwifiex_adapter, main_work); + + if (adapter->surprise_removed) + return; + mwifiex_main_process(adapter); +} + +/* + * This function adds the card. + * + * This function follows the following major steps to set up the device - + * - Initialize software. This includes probing the card, registering + * the interface operations table, and allocating/initializing the + * adapter structure + * - Set up the netlink socket + * - Create and start the main work queue + * - Register the device + * - Initialize firmware and hardware + * - Add logical interfaces + */ +int +mwifiex_add_card(void *card, struct semaphore *sem, + struct mwifiex_if_ops *if_ops, u8 iface_type) +{ + struct mwifiex_adapter *adapter; + + if (down_interruptible(sem)) + goto exit_sem_err; + + if (mwifiex_register(card, if_ops, (void **)&adapter)) { + pr_err("%s: software init failed\n", __func__); + goto err_init_sw; + } + + adapter->iface_type = iface_type; + adapter->card_sem = sem; + + adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; + adapter->surprise_removed = false; + init_waitqueue_head(&adapter->init_wait_q); + adapter->is_suspended = false; + adapter->hs_activated = false; + init_waitqueue_head(&adapter->hs_activate_wait_q); + init_waitqueue_head(&adapter->cmd_wait_q.wait); + adapter->cmd_wait_q.status = 0; + adapter->scan_wait_q_woken = false; + + if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB) { + adapter->rx_work_enabled = true; + pr_notice("rx work enabled, cpus %d\n", num_possible_cpus()); + } + + adapter->workqueue = + alloc_workqueue("MWIFIEX_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); + if (!adapter->workqueue) + goto err_kmalloc; + + INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); + + if (adapter->rx_work_enabled) { + adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE", + WQ_HIGHPRI | + WQ_MEM_RECLAIM | + WQ_UNBOUND, 1); + if (!adapter->rx_workqueue) + goto err_kmalloc; + + INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue); + } + + /* Register the device. Fill up the private data structure with relevant + information from the card. */ + if (adapter->if_ops.register_dev(adapter)) { + pr_err("%s: failed to register mwifiex device\n", __func__); + goto err_registerdev; + } + + if (mwifiex_init_hw_fw(adapter)) { + pr_err("%s: firmware init failed\n", __func__); + goto err_init_fw; + } + + return 0; + +err_init_fw: + pr_debug("info: %s: unregister device\n", __func__); + if (adapter->if_ops.unregister_dev) + adapter->if_ops.unregister_dev(adapter); + if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { + pr_debug("info: %s: shutdown mwifiex\n", __func__); + adapter->init_wait_q_woken = false; + + if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) + wait_event_interruptible(adapter->init_wait_q, + adapter->init_wait_q_woken); + } +err_registerdev: + adapter->surprise_removed = true; + mwifiex_terminate_workqueue(adapter); +err_kmalloc: + mwifiex_free_adapter(adapter); + +err_init_sw: + up(sem); + +exit_sem_err: + return -1; +} +EXPORT_SYMBOL_GPL(mwifiex_add_card); + +/* + * This function removes the card. + * + * This function follows the following major steps to remove the device - + * - Stop data traffic + * - Shutdown firmware + * - Remove the logical interfaces + * - Terminate the work queue + * - Unregister the device + * - Free the adapter structure + */ +int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) +{ + struct mwifiex_private *priv = NULL; + int i; + + if (down_interruptible(sem)) + goto exit_sem_err; + + if (!adapter) + goto exit_remove; + + /* We can no longer handle interrupts once we start doing the teardown + * below. */ + if (adapter->if_ops.disable_int) + adapter->if_ops.disable_int(adapter); + + adapter->surprise_removed = true; + + mwifiex_terminate_workqueue(adapter); + + /* Stop data */ + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv && priv->netdev) { + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + } + } + + mwifiex_dbg(adapter, CMD, + "cmd: calling mwifiex_shutdown_drv...\n"); + adapter->init_wait_q_woken = false; + + if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) + wait_event_interruptible(adapter->init_wait_q, + adapter->init_wait_q_woken); + mwifiex_dbg(adapter, CMD, + "cmd: mwifiex_shutdown_drv done\n"); + if (atomic_read(&adapter->rx_pending) || + atomic_read(&adapter->tx_pending) || + atomic_read(&adapter->cmd_pending)) { + mwifiex_dbg(adapter, ERROR, + "rx_pending=%d, tx_pending=%d,\t" + "cmd_pending=%d\n", + atomic_read(&adapter->rx_pending), + atomic_read(&adapter->tx_pending), + atomic_read(&adapter->cmd_pending)); + } + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + + if (!priv) + continue; + + rtnl_lock(); + if (priv->netdev && + priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) + mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev); + rtnl_unlock(); + } + + wiphy_unregister(adapter->wiphy); + wiphy_free(adapter->wiphy); + + /* Unregister device */ + mwifiex_dbg(adapter, INFO, + "info: unregister device\n"); + if (adapter->if_ops.unregister_dev) + adapter->if_ops.unregister_dev(adapter); + /* Free adapter structure */ + mwifiex_dbg(adapter, INFO, + "info: free adapter\n"); + mwifiex_free_adapter(adapter); + +exit_remove: + up(sem); +exit_sem_err: + return 0; +} +EXPORT_SYMBOL_GPL(mwifiex_remove_card); + +void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (!adapter->dev || !(adapter->debug_mask & mask)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + dev_info(adapter->dev, "%pV", &vaf); + + va_end(args); +} +EXPORT_SYMBOL_GPL(_mwifiex_dbg); + +/* + * This function initializes the module. + * + * The debug FS is also initialized if configured. + */ +static int +mwifiex_init_module(void) +{ +#ifdef CONFIG_DEBUG_FS + mwifiex_debugfs_init(); +#endif + return 0; +} + +/* + * This function cleans up the module. + * + * The debug FS is removed if available. + */ +static void +mwifiex_cleanup_module(void) +{ +#ifdef CONFIG_DEBUG_FS + mwifiex_debugfs_remove(); +#endif +} + +module_init(mwifiex_init_module); +module_exit(mwifiex_cleanup_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h new file mode 100644 index 000000000000..3959f1c97f4e --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -0,0 +1,1579 @@ +/* + * Marvell Wireless LAN device driver: major data structures and prototypes + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_MAIN_H_ +#define _MWIFIEX_MAIN_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "pcie.h" +#include "usb.h" +#include "sdio.h" + +extern const char driver_version[]; + +struct mwifiex_adapter; +struct mwifiex_private; + +enum { + MWIFIEX_ASYNC_CMD, + MWIFIEX_SYNC_CMD +}; + +#define MWIFIEX_DRIVER_MODE_STA BIT(0) +#define MWIFIEX_DRIVER_MODE_UAP BIT(1) +#define MWIFIEX_DRIVER_MODE_P2P BIT(2) +#define MWIFIEX_DRIVER_MODE_BITMASK (BIT(0) | BIT(1) | BIT(2)) + +#define MWIFIEX_MAX_AP 64 + +#define MWIFIEX_MAX_PKTS_TXQ 16 + +#define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ) + +#define MWIFIEX_TIMER_10S 10000 +#define MWIFIEX_TIMER_1S 1000 + +#define MAX_TX_PENDING 100 +#define LOW_TX_PENDING 80 + +#define HIGH_RX_PENDING 50 +#define LOW_RX_PENDING 20 + +#define MWIFIEX_UPLD_SIZE (2312) + +#define MAX_EVENT_SIZE 2048 + +#define ARP_FILTER_MAX_BUF_SIZE 68 + +#define MWIFIEX_KEY_BUFFER_SIZE 16 +#define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10 +#define MWIFIEX_MAX_REGION_CODE 7 + +#define DEFAULT_BCN_AVG_FACTOR 8 +#define DEFAULT_DATA_AVG_FACTOR 8 + +#define FIRST_VALID_CHANNEL 0xff +#define DEFAULT_AD_HOC_CHANNEL 6 +#define DEFAULT_AD_HOC_CHANNEL_A 36 + +#define DEFAULT_BCN_MISS_TIMEOUT 5 + +#define MAX_SCAN_BEACON_BUFFER 8000 + +#define SCAN_BEACON_ENTRY_PAD 6 + +#define MWIFIEX_PASSIVE_SCAN_CHAN_TIME 110 +#define MWIFIEX_ACTIVE_SCAN_CHAN_TIME 30 +#define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME 30 +#define MWIFIEX_DEF_SCAN_CHAN_GAP_TIME 50 + +#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) + +#define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S) + +#define RSN_GTK_OUI_OFFSET 2 + +#define MWIFIEX_OUI_NOT_PRESENT 0 +#define MWIFIEX_OUI_PRESENT 1 + +#define PKT_TYPE_MGMT 0xE5 + +/* + * Do not check for data_received for USB, as data_received + * is handled in mwifiex_usb_recv for USB + */ +#define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \ + adapter->event_received || \ + adapter->data_received) + +#define MWIFIEX_TYPE_CMD 1 +#define MWIFIEX_TYPE_DATA 0 +#define MWIFIEX_TYPE_AGGR_DATA 10 +#define MWIFIEX_TYPE_EVENT 3 + +#define MAX_BITMAP_RATES_SIZE 18 + +#define MAX_CHANNEL_BAND_BG 14 +#define MAX_CHANNEL_BAND_A 165 + +#define MAX_FREQUENCY_BAND_BG 2484 + +#define MWIFIEX_EVENT_HEADER_LEN 4 +#define MWIFIEX_UAP_EVENT_EXTRA_HEADER 2 + +#define MWIFIEX_TYPE_LEN 4 +#define MWIFIEX_USB_TYPE_CMD 0xF00DFACE +#define MWIFIEX_USB_TYPE_DATA 0xBEADC0DE +#define MWIFIEX_USB_TYPE_EVENT 0xBEEFFACE + +/* Threshold for tx_timeout_cnt before we trigger a card reset */ +#define TX_TIMEOUT_THRESHOLD 6 + +#define MWIFIEX_DRV_INFO_SIZE_MAX 0x40000 + +/* Address alignment */ +#define MWIFIEX_ALIGN_ADDR(p, a) (((long)(p) + (a) - 1) & ~((a) - 1)) + +/** + *enum mwifiex_debug_level - marvell wifi debug level + */ +enum MWIFIEX_DEBUG_LEVEL { + MWIFIEX_DBG_MSG = 0x00000001, + MWIFIEX_DBG_FATAL = 0x00000002, + MWIFIEX_DBG_ERROR = 0x00000004, + MWIFIEX_DBG_DATA = 0x00000008, + MWIFIEX_DBG_CMD = 0x00000010, + MWIFIEX_DBG_EVENT = 0x00000020, + MWIFIEX_DBG_INTR = 0x00000040, + MWIFIEX_DBG_IOCTL = 0x00000080, + + MWIFIEX_DBG_MPA_D = 0x00008000, + MWIFIEX_DBG_DAT_D = 0x00010000, + MWIFIEX_DBG_CMD_D = 0x00020000, + MWIFIEX_DBG_EVT_D = 0x00040000, + MWIFIEX_DBG_FW_D = 0x00080000, + MWIFIEX_DBG_IF_D = 0x00100000, + + MWIFIEX_DBG_ENTRY = 0x10000000, + MWIFIEX_DBG_WARN = 0x20000000, + MWIFIEX_DBG_INFO = 0x40000000, + MWIFIEX_DBG_DUMP = 0x80000000, + + MWIFIEX_DBG_ANY = 0xffffffff +}; + +#define MWIFIEX_DEFAULT_DEBUG_MASK (MWIFIEX_DBG_MSG | \ + MWIFIEX_DBG_FATAL | \ + MWIFIEX_DBG_ERROR) + +__printf(3, 4) +void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, + const char *fmt, ...); +#define mwifiex_dbg(adapter, mask, fmt, ...) \ + _mwifiex_dbg(adapter, MWIFIEX_DBG_##mask, fmt, ##__VA_ARGS__) + +#define DEBUG_DUMP_DATA_MAX_LEN 128 +#define mwifiex_dbg_dump(adapter, dbg_mask, str, buf, len) \ +do { \ + if ((adapter)->debug_mask & MWIFIEX_DBG_##dbg_mask) \ + print_hex_dump(KERN_DEBUG, str, \ + DUMP_PREFIX_OFFSET, 16, 1, \ + buf, len, false); \ +} while (0) + +struct mwifiex_dbg { + u32 num_cmd_host_to_card_failure; + u32 num_cmd_sleep_cfm_host_to_card_failure; + u32 num_tx_host_to_card_failure; + u32 num_event_deauth; + u32 num_event_disassoc; + u32 num_event_link_lost; + u32 num_cmd_deauth; + u32 num_cmd_assoc_success; + u32 num_cmd_assoc_failure; + u32 num_tx_timeout; + u16 timeout_cmd_id; + u16 timeout_cmd_act; + u16 last_cmd_id[DBG_CMD_NUM]; + u16 last_cmd_act[DBG_CMD_NUM]; + u16 last_cmd_index; + u16 last_cmd_resp_id[DBG_CMD_NUM]; + u16 last_cmd_resp_index; + u16 last_event[DBG_CMD_NUM]; + u16 last_event_index; +}; + +enum MWIFIEX_HARDWARE_STATUS { + MWIFIEX_HW_STATUS_READY, + MWIFIEX_HW_STATUS_INITIALIZING, + MWIFIEX_HW_STATUS_INIT_DONE, + MWIFIEX_HW_STATUS_RESET, + MWIFIEX_HW_STATUS_CLOSING, + MWIFIEX_HW_STATUS_NOT_READY +}; + +enum MWIFIEX_802_11_POWER_MODE { + MWIFIEX_802_11_POWER_MODE_CAM, + MWIFIEX_802_11_POWER_MODE_PSP +}; + +struct mwifiex_tx_param { + u32 next_pkt_len; +}; + +enum MWIFIEX_PS_STATE { + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP_CFM, + PS_STATE_SLEEP +}; + +enum mwifiex_iface_type { + MWIFIEX_SDIO, + MWIFIEX_PCIE, + MWIFIEX_USB +}; + +struct mwifiex_add_ba_param { + u32 tx_win_size; + u32 rx_win_size; + u32 timeout; + u8 tx_amsdu; + u8 rx_amsdu; +}; + +struct mwifiex_tx_aggr { + u8 ampdu_user; + u8 ampdu_ap; + u8 amsdu; +}; + +enum mwifiex_ba_status { + BA_SETUP_NONE = 0, + BA_SETUP_INPROGRESS, + BA_SETUP_COMPLETE +}; + +struct mwifiex_ra_list_tbl { + struct list_head list; + struct sk_buff_head skb_head; + u8 ra[ETH_ALEN]; + u32 is_11n_enabled; + u16 max_amsdu; + u16 ba_pkt_count; + u8 ba_packet_thr; + enum mwifiex_ba_status ba_status; + u8 amsdu_in_ampdu; + u16 total_pkt_count; + bool tdls_link; + bool tx_paused; +}; + +struct mwifiex_tid_tbl { + struct list_head ra_list; +}; + +#define WMM_HIGHEST_PRIORITY 7 +#define HIGH_PRIO_TID 7 +#define LOW_PRIO_TID 0 + +struct mwifiex_wmm_desc { + struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID]; + u32 packets_out[MAX_NUM_TID]; + u32 pkts_paused[MAX_NUM_TID]; + /* spin lock to protect ra_list */ + spinlock_t ra_list_spinlock; + struct mwifiex_wmm_ac_status ac_status[IEEE80211_NUM_ACS]; + enum mwifiex_wmm_ac_e ac_down_graded_vals[IEEE80211_NUM_ACS]; + u32 drv_pkt_delay_max; + u8 queue_priority[IEEE80211_NUM_ACS]; + u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */ + /* Number of transmit packets queued */ + atomic_t tx_pkts_queued; + /* Tracks highest priority with a packet queued */ + atomic_t highest_queued_prio; +}; + +struct mwifiex_802_11_security { + u8 wpa_enabled; + u8 wpa2_enabled; + u8 wapi_enabled; + u8 wapi_key_on; + u8 wep_enabled; + u32 authentication_mode; + u8 is_authtype_auto; + u32 encryption_mode; +}; + +struct ieee_types_header { + u8 element_id; + u8 len; +} __packed; + +struct ieee_types_vendor_specific { + struct ieee_types_vendor_header vend_hdr; + u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)]; +} __packed; + +struct ieee_types_generic { + struct ieee_types_header ieee_hdr; + u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)]; +} __packed; + +struct ieee_types_bss_co_2040 { + struct ieee_types_header ieee_hdr; + u8 bss_2040co; +} __packed; + +struct ieee_types_extcap { + struct ieee_types_header ieee_hdr; + u8 ext_capab[8]; +} __packed; + +struct ieee_types_vht_cap { + struct ieee_types_header ieee_hdr; + struct ieee80211_vht_cap vhtcap; +} __packed; + +struct ieee_types_vht_oper { + struct ieee_types_header ieee_hdr; + struct ieee80211_vht_operation vhtoper; +} __packed; + +struct ieee_types_aid { + struct ieee_types_header ieee_hdr; + u16 aid; +} __packed; + +struct mwifiex_bssdescriptor { + u8 mac_address[ETH_ALEN]; + struct cfg80211_ssid ssid; + u32 privacy; + s32 rssi; + u32 channel; + u32 freq; + u16 beacon_period; + u8 erp_flags; + u32 bss_mode; + u8 supported_rates[MWIFIEX_SUPPORTED_RATES]; + u8 data_rates[MWIFIEX_SUPPORTED_RATES]; + /* Network band. + * BAND_B(0x01): 'b' band + * BAND_G(0x02): 'g' band + * BAND_A(0X04): 'a' band + */ + u16 bss_band; + u64 fw_tsf; + u64 timestamp; + union ieee_types_phy_param_set phy_param_set; + union ieee_types_ss_param_set ss_param_set; + u16 cap_info_bitmap; + struct ieee_types_wmm_parameter wmm_ie; + u8 disable_11n; + struct ieee80211_ht_cap *bcn_ht_cap; + u16 ht_cap_offset; + struct ieee80211_ht_operation *bcn_ht_oper; + u16 ht_info_offset; + u8 *bcn_bss_co_2040; + u16 bss_co_2040_offset; + u8 *bcn_ext_cap; + u16 ext_cap_offset; + struct ieee80211_vht_cap *bcn_vht_cap; + u16 vht_cap_offset; + struct ieee80211_vht_operation *bcn_vht_oper; + u16 vht_info_offset; + struct ieee_types_oper_mode_ntf *oper_mode; + u16 oper_mode_offset; + u8 disable_11ac; + struct ieee_types_vendor_specific *bcn_wpa_ie; + u16 wpa_offset; + struct ieee_types_generic *bcn_rsn_ie; + u16 rsn_offset; + struct ieee_types_generic *bcn_wapi_ie; + u16 wapi_offset; + u8 *beacon_buf; + u32 beacon_buf_size; + u8 sensed_11h; + u8 local_constraint; + u8 chan_sw_ie_present; +}; + +struct mwifiex_current_bss_params { + struct mwifiex_bssdescriptor bss_descriptor; + u8 wmm_enabled; + u8 wmm_uapsd_enabled; + u8 band; + u32 num_of_rates; + u8 data_rates[MWIFIEX_SUPPORTED_RATES]; +}; + +struct mwifiex_sleep_params { + u16 sp_error; + u16 sp_offset; + u16 sp_stable_time; + u8 sp_cal_control; + u8 sp_ext_sleep_clk; + u16 sp_reserved; +}; + +struct mwifiex_sleep_period { + u16 period; + u16 reserved; +}; + +struct mwifiex_wep_key { + u32 length; + u32 key_index; + u32 key_length; + u8 key_material[MWIFIEX_KEY_BUFFER_SIZE]; +}; + +#define MAX_REGION_CHANNEL_NUM 2 + +struct mwifiex_chan_freq_power { + u16 channel; + u32 freq; + u16 max_tx_power; + u8 unsupported; +}; + +enum state_11d_t { + DISABLE_11D = 0, + ENABLE_11D = 1, +}; + +#define MWIFIEX_MAX_TRIPLET_802_11D 83 + +struct mwifiex_802_11d_domain_reg { + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + u8 no_of_triplet; + struct ieee80211_country_ie_triplet + triplet[MWIFIEX_MAX_TRIPLET_802_11D]; +}; + +struct mwifiex_vendor_spec_cfg_ie { + u16 mask; + u16 flag; + u8 ie[MWIFIEX_MAX_VSIE_LEN]; +}; + +struct wps { + u8 session_enable; +}; + +struct mwifiex_roc_cfg { + u64 cookie; + struct ieee80211_channel chan; +}; + +#define MWIFIEX_FW_DUMP_IDX 0xff +#define MWIFIEX_DRV_INFO_IDX 20 +#define FW_DUMP_MAX_NAME_LEN 8 +#define FW_DUMP_HOST_READY 0xEE +#define FW_DUMP_DONE 0xFF +#define FW_DUMP_READ_DONE 0xFE + +struct memory_type_mapping { + u8 mem_name[FW_DUMP_MAX_NAME_LEN]; + u8 *mem_ptr; + u32 mem_size; + u8 done_flag; +}; + +enum rdwr_status { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +}; + +enum mwifiex_iface_work_flags { + MWIFIEX_IFACE_WORK_DEVICE_DUMP, + MWIFIEX_IFACE_WORK_CARD_RESET, +}; + +struct mwifiex_private { + struct mwifiex_adapter *adapter; + u8 bss_type; + u8 bss_role; + u8 bss_priority; + u8 bss_num; + u8 bss_started; + u8 frame_type; + u8 curr_addr[ETH_ALEN]; + u8 media_connected; + u8 port_open; + u8 usb_port; + u32 num_tx_timeout; + /* track consecutive timeout */ + u8 tx_timeout_cnt; + struct net_device *netdev; + struct net_device_stats stats; + u16 curr_pkt_filter; + u32 bss_mode; + u32 pkt_tx_ctrl; + u16 tx_power_level; + u8 max_tx_power_level; + u8 min_tx_power_level; + u8 tx_rate; + u8 tx_htinfo; + u8 rxpd_htinfo; + u8 rxpd_rate; + u16 rate_bitmap; + u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + u32 data_rate; + u8 is_data_rate_auto; + u16 bcn_avg_factor; + u16 data_avg_factor; + s16 data_rssi_last; + s16 data_nf_last; + s16 data_rssi_avg; + s16 data_nf_avg; + s16 bcn_rssi_last; + s16 bcn_nf_last; + s16 bcn_rssi_avg; + s16 bcn_nf_avg; + struct mwifiex_bssdescriptor *attempted_bss_desc; + struct cfg80211_ssid prev_ssid; + u8 prev_bssid[ETH_ALEN]; + struct mwifiex_current_bss_params curr_bss_params; + u16 beacon_period; + u8 dtim_period; + u16 listen_interval; + u16 atim_window; + u8 adhoc_channel; + u8 adhoc_is_link_sensed; + u8 adhoc_state; + struct mwifiex_802_11_security sec_info; + struct mwifiex_wep_key wep_key[NUM_WEP_KEYS]; + u16 wep_key_curr_index; + u8 wpa_ie[256]; + u8 wpa_ie_len; + u8 wpa_is_gtk_set; + struct host_cmd_ds_802_11_key_material aes_key; + struct host_cmd_ds_802_11_key_material_v2 aes_key_v2; + u8 wapi_ie[256]; + u8 wapi_ie_len; + u8 *wps_ie; + u8 wps_ie_len; + u8 wmm_required; + u8 wmm_enabled; + u8 wmm_qosinfo; + struct mwifiex_wmm_desc wmm; + atomic_t wmm_tx_pending[IEEE80211_NUM_ACS]; + struct list_head sta_list; + /* spin lock for associated station/TDLS peers list */ + spinlock_t sta_list_spinlock; + struct list_head auto_tdls_list; + /* spin lock for auto TDLS peer list */ + spinlock_t auto_tdls_lock; + struct list_head tx_ba_stream_tbl_ptr; + /* spin lock for tx_ba_stream_tbl_ptr queue */ + spinlock_t tx_ba_stream_tbl_lock; + struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID]; + struct mwifiex_add_ba_param add_ba_param; + u16 rx_seq[MAX_NUM_TID]; + u8 tos_to_tid_inv[MAX_NUM_TID]; + struct list_head rx_reorder_tbl_ptr; + /* spin lock for rx_reorder_tbl_ptr queue */ + spinlock_t rx_reorder_tbl_lock; + /* spin lock for Rx packets */ + spinlock_t rx_pkt_lock; + +#define MWIFIEX_ASSOC_RSP_BUF_SIZE 500 + u8 assoc_rsp_buf[MWIFIEX_ASSOC_RSP_BUF_SIZE]; + u32 assoc_rsp_size; + +#define MWIFIEX_GENIE_BUF_SIZE 256 + u8 gen_ie_buf[MWIFIEX_GENIE_BUF_SIZE]; + u8 gen_ie_buf_len; + + struct mwifiex_vendor_spec_cfg_ie vs_ie[MWIFIEX_MAX_VSIE_NUM]; + +#define MWIFIEX_ASSOC_TLV_BUF_SIZE 256 + u8 assoc_tlv_buf[MWIFIEX_ASSOC_TLV_BUF_SIZE]; + u8 assoc_tlv_buf_len; + + u8 *curr_bcn_buf; + u32 curr_bcn_size; + /* spin lock for beacon buffer */ + spinlock_t curr_bcn_buf_lock; + struct wireless_dev wdev; + struct mwifiex_chan_freq_power cfp; + char version_str[128]; +#ifdef CONFIG_DEBUG_FS + struct dentry *dfs_dev_dir; +#endif + u16 current_key_index; + struct semaphore async_sem; + struct cfg80211_scan_request *scan_request; + u8 cfg_bssid[6]; + struct wps wps; + u8 scan_block; + s32 cqm_rssi_thold; + u32 cqm_rssi_hyst; + u8 subsc_evt_rssi_state; + struct mwifiex_ds_misc_subsc_evt async_subsc_evt_storage; + struct mwifiex_ie mgmt_ie[MAX_MGMT_IE_INDEX]; + u16 beacon_idx; + u16 proberesp_idx; + u16 assocresp_idx; + u16 gen_idx; + u8 ap_11n_enabled; + u8 ap_11ac_enabled; + u32 mgmt_frame_mask; + struct mwifiex_roc_cfg roc_cfg; + bool scan_aborting; + u8 csa_chan; + unsigned long csa_expire_time; + u8 del_list_idx; + bool hs2_enabled; + struct mwifiex_uap_bss_param bss_cfg; + struct cfg80211_chan_def bss_chandef; + struct station_parameters *sta_params; + struct sk_buff_head tdls_txq; + u8 check_tdls_tx; + struct timer_list auto_tdls_timer; + bool auto_tdls_timer_active; + struct idr ack_status_frames; + /* spin lock for ack status */ + spinlock_t ack_status_lock; + /** rx histogram data */ + struct mwifiex_histogram_data *hist_data; + struct cfg80211_chan_def dfs_chandef; + struct workqueue_struct *dfs_cac_workqueue; + struct delayed_work dfs_cac_work; + struct timer_list dfs_chan_switch_timer; + struct workqueue_struct *dfs_chan_sw_workqueue; + struct delayed_work dfs_chan_sw_work; + struct cfg80211_beacon_data beacon_after; + struct mwifiex_11h_intf_state state_11h; + struct mwifiex_ds_mem_rw mem_rw; + struct sk_buff_head bypass_txq; + struct mwifiex_user_scan_chan hidden_chan[MWIFIEX_USER_SCAN_CHAN_MAX]; +}; + + +struct mwifiex_tx_ba_stream_tbl { + struct list_head list; + int tid; + u8 ra[ETH_ALEN]; + enum mwifiex_ba_status ba_status; + u8 amsdu; +}; + +struct mwifiex_rx_reorder_tbl; + +struct reorder_tmr_cnxt { + struct timer_list timer; + struct mwifiex_rx_reorder_tbl *ptr; + struct mwifiex_private *priv; + u8 timer_is_set; +}; + +struct mwifiex_rx_reorder_tbl { + struct list_head list; + int tid; + u8 ta[ETH_ALEN]; + int init_win; + int start_win; + int win_size; + void **rx_reorder_ptr; + struct reorder_tmr_cnxt timer_context; + u8 amsdu; + u8 flags; +}; + +struct mwifiex_bss_prio_node { + struct list_head list; + struct mwifiex_private *priv; +}; + +struct mwifiex_bss_prio_tbl { + struct list_head bss_prio_head; + /* spin lock for bss priority */ + spinlock_t bss_prio_lock; + struct mwifiex_bss_prio_node *bss_prio_cur; +}; + +struct cmd_ctrl_node { + struct list_head list; + struct mwifiex_private *priv; + u32 cmd_oid; + u32 cmd_flag; + struct sk_buff *cmd_skb; + struct sk_buff *resp_skb; + void *data_buf; + u32 wait_q_enabled; + struct sk_buff *skb; + u8 *condition; + u8 cmd_wait_q_woken; +}; + +struct mwifiex_bss_priv { + u8 band; + u64 fw_tsf; +}; + +struct mwifiex_tdls_capab { + __le16 capab; + u8 rates[32]; + u8 rates_len; + u8 qos_info; + u8 coex_2040; + u16 aid; + struct ieee80211_ht_cap ht_capb; + struct ieee80211_ht_operation ht_oper; + struct ieee_types_extcap extcap; + struct ieee_types_generic rsn_ie; + struct ieee80211_vht_cap vhtcap; + struct ieee80211_vht_operation vhtoper; +}; + +struct mwifiex_station_stats { + u64 last_rx; + s8 rssi; + u64 rx_bytes; + u64 tx_bytes; + u32 rx_packets; + u32 tx_packets; + u32 tx_failed; + u8 last_tx_rate; + u8 last_tx_htinfo; +}; + +/* This is AP/TDLS specific structure which stores information + * about associated/peer STA + */ +struct mwifiex_sta_node { + struct list_head list; + u8 mac_addr[ETH_ALEN]; + u8 is_wmm_enabled; + u8 is_11n_enabled; + u8 is_11ac_enabled; + u8 ampdu_sta[MAX_NUM_TID]; + u16 rx_seq[MAX_NUM_TID]; + u16 max_amsdu; + u8 tdls_status; + struct mwifiex_tdls_capab tdls_cap; + struct mwifiex_station_stats stats; + u8 tx_pause; +}; + +struct mwifiex_auto_tdls_peer { + struct list_head list; + u8 mac_addr[ETH_ALEN]; + u8 tdls_status; + int rssi; + long rssi_jiffies; + u8 failure_count; + u8 do_discover; + u8 do_setup; +}; + +struct mwifiex_if_ops { + int (*init_if) (struct mwifiex_adapter *); + void (*cleanup_if) (struct mwifiex_adapter *); + int (*check_fw_status) (struct mwifiex_adapter *, u32); + int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); + int (*register_dev) (struct mwifiex_adapter *); + void (*unregister_dev) (struct mwifiex_adapter *); + int (*enable_int) (struct mwifiex_adapter *); + void (*disable_int) (struct mwifiex_adapter *); + int (*process_int_status) (struct mwifiex_adapter *); + int (*host_to_card) (struct mwifiex_adapter *, u8, struct sk_buff *, + struct mwifiex_tx_param *); + int (*wakeup) (struct mwifiex_adapter *); + int (*wakeup_complete) (struct mwifiex_adapter *); + + /* Interface specific functions */ + void (*update_mp_end_port) (struct mwifiex_adapter *, u16); + void (*cleanup_mpa_buf) (struct mwifiex_adapter *); + int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *); + int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *); + int (*init_fw_port) (struct mwifiex_adapter *); + int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); + void (*card_reset) (struct mwifiex_adapter *); + int (*reg_dump)(struct mwifiex_adapter *, char *); + void (*device_dump)(struct mwifiex_adapter *); + int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); + void (*iface_work)(struct work_struct *work); + void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter); + void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *); + void (*multi_port_resync)(struct mwifiex_adapter *); + bool (*is_port_ready)(struct mwifiex_private *); +}; + +struct mwifiex_adapter { + u8 iface_type; + unsigned int debug_mask; + struct mwifiex_iface_comb iface_limit; + struct mwifiex_iface_comb curr_iface_comb; + struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM]; + u8 priv_num; + const struct firmware *firmware; + char fw_name[32]; + int winner; + struct device *dev; + struct wiphy *wiphy; + u8 perm_addr[ETH_ALEN]; + bool surprise_removed; + u32 fw_release_number; + u16 init_wait_q_woken; + wait_queue_head_t init_wait_q; + void *card; + struct mwifiex_if_ops if_ops; + atomic_t bypass_tx_pending; + atomic_t rx_pending; + atomic_t tx_pending; + atomic_t cmd_pending; + struct workqueue_struct *workqueue; + struct work_struct main_work; + struct workqueue_struct *rx_workqueue; + struct work_struct rx_work; + struct workqueue_struct *dfs_workqueue; + struct work_struct dfs_work; + bool rx_work_enabled; + bool rx_processing; + bool delay_main_work; + bool rx_locked; + bool main_locked; + struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM]; + /* spin lock for init/shutdown */ + spinlock_t mwifiex_lock; + /* spin lock for main process */ + spinlock_t main_proc_lock; + u32 mwifiex_processing; + u8 more_task_flag; + u16 tx_buf_size; + u16 curr_tx_buf_size; + /* sdio single port rx aggregation capability */ + bool host_disable_sdio_rx_aggr; + bool sdio_rx_aggr_enable; + u16 sdio_rx_block_size; + u32 ioport; + enum MWIFIEX_HARDWARE_STATUS hw_status; + u16 number_of_antenna; + u32 fw_cap_info; + /* spin lock for interrupt handling */ + spinlock_t int_lock; + u8 int_status; + u32 event_cause; + struct sk_buff *event_skb; + u8 upld_buf[MWIFIEX_UPLD_SIZE]; + u8 data_sent; + u8 cmd_sent; + u8 cmd_resp_received; + u8 event_received; + u8 data_received; + u16 seq_num; + struct cmd_ctrl_node *cmd_pool; + struct cmd_ctrl_node *curr_cmd; + /* spin lock for command */ + spinlock_t mwifiex_cmd_lock; + u8 is_cmd_timedout; + u16 last_init_cmd; + struct timer_list cmd_timer; + struct list_head cmd_free_q; + /* spin lock for cmd_free_q */ + spinlock_t cmd_free_q_lock; + struct list_head cmd_pending_q; + /* spin lock for cmd_pending_q */ + spinlock_t cmd_pending_q_lock; + struct list_head scan_pending_q; + /* spin lock for scan_pending_q */ + spinlock_t scan_pending_q_lock; + /* spin lock for RX processing routine */ + spinlock_t rx_proc_lock; + struct sk_buff_head tx_data_q; + atomic_t tx_queued; + u32 scan_processing; + u16 region_code; + struct mwifiex_802_11d_domain_reg domain_reg; + u16 scan_probes; + u32 scan_mode; + u16 specific_scan_time; + u16 active_scan_time; + u16 passive_scan_time; + u16 scan_chan_gap_time; + u8 fw_bands; + u8 adhoc_start_band; + u8 config_bands; + struct mwifiex_chan_scan_param_set *scan_channels; + u8 tx_lock_flag; + struct mwifiex_sleep_params sleep_params; + struct mwifiex_sleep_period sleep_period; + u16 ps_mode; + u32 ps_state; + u8 need_to_wakeup; + u16 multiple_dtim; + u16 local_listen_interval; + u16 null_pkt_interval; + struct sk_buff *sleep_cfm; + u16 bcn_miss_time_out; + u16 adhoc_awake_period; + u8 is_deep_sleep; + u8 delay_null_pkt; + u16 delay_to_ps; + u16 enhanced_ps_mode; + u8 pm_wakeup_card_req; + u16 gen_null_pkt; + u16 pps_uapsd_mode; + u32 pm_wakeup_fw_try; + struct timer_list wakeup_timer; + u8 is_hs_configured; + struct mwifiex_hs_config_param hs_cfg; + u8 hs_activated; + u16 hs_activate_wait_q_woken; + wait_queue_head_t hs_activate_wait_q; + bool is_suspended; + bool hs_enabling; + u8 event_body[MAX_EVENT_SIZE]; + u32 hw_dot_11n_dev_cap; + u8 hw_dev_mcs_support; + u8 user_dev_mcs_support; + u8 adhoc_11n_enabled; + u8 sec_chan_offset; + struct mwifiex_dbg dbg; + u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE]; + u32 arp_filter_size; + struct mwifiex_wait_queue cmd_wait_q; + u8 scan_wait_q_woken; + spinlock_t queue_lock; /* lock for tx queues */ + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + u16 max_mgmt_ie_index; + const struct firmware *cal_data; + struct device_node *dt_node; + + /* 11AC */ + u32 is_hw_11ac_capable; + u32 hw_dot_11ac_dev_cap; + u32 hw_dot_11ac_mcs_support; + u32 usr_dot_11ac_dev_cap_bg; + u32 usr_dot_11ac_dev_cap_a; + u32 usr_dot_11ac_mcs_support; + + atomic_t pending_bridged_pkts; + struct semaphore *card_sem; + bool ext_scan; + u8 fw_api_ver; + u8 key_api_major_ver, key_api_minor_ver; + struct memory_type_mapping *mem_type_mapping_tbl; + u8 num_mem_types; + void *drv_info_dump; + u32 drv_info_size; + bool scan_chan_gap_enabled; + struct sk_buff_head rx_data_q; + struct mwifiex_chan_stats *chan_stats; + u32 num_in_chan_stats; + int survey_idx; + bool auto_tdls; + u8 coex_scan; + u8 coex_min_scan_time; + u8 coex_max_scan_time; + u8 coex_win_size; + u8 coex_tx_win_size; + u8 coex_rx_win_size; + bool drcs_enabled; + u8 active_scan_triggered; + bool usb_mc_status; + bool usb_mc_setup; +}; + +void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter); + +int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); + +void mwifiex_set_trans_start(struct net_device *dev); + +void mwifiex_stop_net_dev_queue(struct net_device *netdev, + struct mwifiex_adapter *adapter); + +void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, + struct mwifiex_adapter *adapter); + +int mwifiex_init_priv(struct mwifiex_private *priv); +void mwifiex_free_priv(struct mwifiex_private *priv); + +int mwifiex_init_fw(struct mwifiex_adapter *adapter); + +int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter); + +int mwifiex_shutdown_drv(struct mwifiex_adapter *adapter); + +int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter); + +int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *); + +int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb); + +int mwifiex_process_mgmt_packet(struct mwifiex_private *priv, + struct sk_buff *skb); + +int mwifiex_process_event(struct mwifiex_adapter *adapter); + +int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node); + +int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, + u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync); + +void mwifiex_cmd_timeout_func(unsigned long function_context); + +int mwifiex_get_debug_info(struct mwifiex_private *, + struct mwifiex_debug_info *); + +int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter); +int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter); +void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter); +void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter); + +void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node); +void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node); + +void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node, + u32 addtail); + +int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter); +int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter); +int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, + struct sk_buff *skb); +int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, + struct mwifiex_tx_param *tx_param); +int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags); +int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb, int aggr, int status); +void mwifiex_clean_txrx(struct mwifiex_private *priv); +u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv); +void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter); +void mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *, u8 *, + u32); +int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, uint16_t ps_bitmap, + struct mwifiex_ds_auto_ds *auto_ds); +int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct mwifiex_ds_pm_cfg *pm_cfg); +void mwifiex_process_hs_config(struct mwifiex_adapter *adapter); +void mwifiex_hs_activated_event(struct mwifiex_private *priv, + u8 activated); +int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, + int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg); +int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int mwifiex_process_rx_packet(struct mwifiex_private *priv, + struct sk_buff *skb); +int mwifiex_sta_prepare_cmd(struct mwifiex_private *, uint16_t cmd_no, + u16 cmd_action, u32 cmd_oid, + void *data_buf, void *cmd_buf); +int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, + u16 cmd_action, u32 cmd_oid, + void *data_buf, void *cmd_buf); +int mwifiex_process_sta_cmdresp(struct mwifiex_private *, u16 cmdresp_no, + struct host_cmd_ds_command *resp); +int mwifiex_process_sta_rx_packet(struct mwifiex_private *, + struct sk_buff *skb); +int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, + struct sk_buff *skb); +int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, + struct sk_buff *skb); +int mwifiex_process_sta_event(struct mwifiex_private *); +int mwifiex_process_uap_event(struct mwifiex_private *); +void mwifiex_delete_all_station_list(struct mwifiex_private *priv); +void mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, + const u8 *ra_addr); +void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); +void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb); +int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta, bool init); +int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, + struct mwifiex_scan_cmd_config *scan_cfg); +void mwifiex_queue_scan_cmd(struct mwifiex_private *priv, + struct cmd_ctrl_node *cmd_node); +int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +s32 mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2); +int mwifiex_associate(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +void mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason); +u8 mwifiex_band_to_radio_type(u8 band); +int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac); +void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter); +int mwifiex_adhoc_start(struct mwifiex_private *priv, + struct cfg80211_ssid *adhoc_ssid); +int mwifiex_adhoc_join(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct cfg80211_ssid *req_ssid); +int mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd); +struct mwifiex_chan_freq_power *mwifiex_get_cfp(struct mwifiex_private *priv, + u8 band, u16 channel, u32 freq); +u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, + u8 index, u8 ht_info); +u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, + u8 index, u8 ht_info); +u32 mwifiex_find_freq_from_band_chan(u8, u8); +int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask, + u8 **buffer); +u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, + u8 *rates); +u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates); +u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv, + u8 *rates, u8 radio_type); +u8 mwifiex_is_rate_auto(struct mwifiex_private *priv); +extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE]; +void mwifiex_save_curr_bcn(struct mwifiex_private *priv); +void mwifiex_free_curr_bcn(struct mwifiex_private *priv); +int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd); +int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int is_command_pending(struct mwifiex_adapter *adapter); +void mwifiex_init_priv_params(struct mwifiex_private *priv, + struct net_device *dev); +int mwifiex_set_secure_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_config, + struct cfg80211_ap_settings *params); +void mwifiex_set_ht_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void mwifiex_set_vht_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void mwifiex_set_tpc_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void mwifiex_set_vht_width(struct mwifiex_private *priv, + enum nl80211_chan_width width, + bool ap_11ac_disable); +void +mwifiex_set_wmm_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void mwifiex_set_ba_params(struct mwifiex_private *priv); + +void mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *pmadapter); +void mwifiex_bt_coex_wlan_param_update_event(struct mwifiex_private *priv, + struct sk_buff *event_skb); + +void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv); +int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf); +int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, + void *buf); + +/* + * This function checks if the queuing is RA based or not. + */ +static inline u8 +mwifiex_queuing_ra_based(struct mwifiex_private *priv) +{ + /* + * Currently we assume if we are in Infra, then DA=RA. This might not be + * true in the future + */ + if ((priv->bss_mode == NL80211_IFTYPE_STATION) && + (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)) + return false; + + return true; +} + +/* + * This function copies rates. + */ +static inline u32 +mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len) +{ + int i; + + for (i = 0; i < len && src[i]; i++, pos++) { + if (pos >= MWIFIEX_SUPPORTED_RATES) + break; + dest[pos] = src[i]; + } + + return pos; +} + +/* + * This function returns the correct private structure pointer based + * upon the BSS type and BSS number. + */ +static inline struct mwifiex_private * +mwifiex_get_priv_by_id(struct mwifiex_adapter *adapter, + u8 bss_num, u8 bss_type) +{ + int i; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + if ((adapter->priv[i]->bss_num == bss_num) && + (adapter->priv[i]->bss_type == bss_type)) + break; + } + } + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* + * This function returns the first available private structure pointer + * based upon the BSS role. + */ +static inline struct mwifiex_private * +mwifiex_get_priv(struct mwifiex_adapter *adapter, + enum mwifiex_bss_role bss_role) +{ + int i; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + if (bss_role == MWIFIEX_BSS_ROLE_ANY || + GET_BSS_ROLE(adapter->priv[i]) == bss_role) + break; + } + } + + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* + * This function returns the first available unused private structure pointer. + */ +static inline struct mwifiex_private * +mwifiex_get_unused_priv(struct mwifiex_adapter *adapter) +{ + int i; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + if (adapter->priv[i]->bss_mode == + NL80211_IFTYPE_UNSPECIFIED) + break; + } + } + + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* + * This function returns the driver private structure of a network device. + */ +static inline struct mwifiex_private * +mwifiex_netdev_get_priv(struct net_device *dev) +{ + return (struct mwifiex_private *) (*(unsigned long *) netdev_priv(dev)); +} + +/* + * This function checks if a skb holds a management frame. + */ +static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb) +{ + return (le32_to_cpu(*(__le32 *)skb->data) == PKT_TYPE_MGMT); +} + +/* This function retrieves channel closed for operation by Channel + * Switch Announcement. + */ +static inline u8 +mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv) +{ + if (!priv->csa_chan) + return 0; + + /* Clear csa channel, if DFS channel move time has passed */ + if (time_after(jiffies, priv->csa_expire_time)) { + priv->csa_chan = 0; + priv->csa_expire_time = 0; + } + + return priv->csa_chan; +} + +static inline u8 mwifiex_is_any_intf_active(struct mwifiex_private *priv) +{ + struct mwifiex_private *priv_num; + int i; + + for (i = 0; i < priv->adapter->priv_num; i++) { + priv_num = priv->adapter->priv[i]; + if (priv_num) { + if ((GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_UAP && + priv_num->bss_started) || + (GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_STA && + priv_num->media_connected)) + return 1; + } + } + + return 0; +} + +static inline u8 mwifiex_is_tdls_link_setup(u8 status) +{ + switch (status) { + case TDLS_SETUP_COMPLETE: + case TDLS_CHAN_SWITCHING: + case TDLS_IN_BASE_CHAN: + case TDLS_IN_OFF_CHAN: + return true; + default: + break; + } + + return false; +} + +int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, + u32 func_init_shutdown); +int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8); +int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *); + +void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version, + int maxlen); +int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, + struct mwifiex_multicast_list *mcast_list); +int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, + struct net_device *dev); +int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_queued); +int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, + struct cfg80211_ssid *req_ssid); +int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type); +int mwifiex_enable_hs(struct mwifiex_adapter *adapter); +int mwifiex_disable_auto_ds(struct mwifiex_private *priv); +int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate); +int mwifiex_request_scan(struct mwifiex_private *priv, + struct cfg80211_ssid *req_ssid); +int mwifiex_scan_networks(struct mwifiex_private *priv, + const struct mwifiex_user_scan_cfg *user_scan_in); +int mwifiex_set_radio(struct mwifiex_private *priv, u8 option); + +int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, + const u8 *key, int key_len, u8 key_index, + const u8 *mac_addr, int disable); + +int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len); + +int mwifiex_get_ver_ext(struct mwifiex_private *priv); + +int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, + struct ieee80211_channel *chan, + unsigned int duration); + +int mwifiex_get_stats_info(struct mwifiex_private *priv, + struct mwifiex_ds_get_stats *log); + +int mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, + u32 reg_offset, u32 reg_value); + +int mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, + u32 reg_offset, u32 *value); + +int mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, + u8 *value); + +int mwifiex_set_11n_httx_cfg(struct mwifiex_private *priv, int data); + +int mwifiex_get_11n_httx_cfg(struct mwifiex_private *priv, int *data); + +int mwifiex_set_tx_rate_cfg(struct mwifiex_private *priv, int tx_rate_index); + +int mwifiex_get_tx_rate_cfg(struct mwifiex_private *priv, int *tx_rate_index); + +int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode); + +int mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, + char *version, int max_len); + +int mwifiex_set_tx_power(struct mwifiex_private *priv, + struct mwifiex_power_cfg *power_cfg); + +int mwifiex_main_process(struct mwifiex_adapter *); + +int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb); + +int mwifiex_get_bss_info(struct mwifiex_private *, + struct mwifiex_bss_info *); +int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, + struct cfg80211_bss *bss, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, + struct mwifiex_bssdescriptor *bss_entry); +int mwifiex_check_network_compatibility(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc); + +u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type); +u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset); + +struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params); +int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev); + +void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config); + +int mwifiex_add_wowlan_magic_pkt_filter(struct mwifiex_adapter *adapter); + +int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, + struct cfg80211_beacon_data *data); +int mwifiex_del_mgmt_ies(struct mwifiex_private *priv); +u8 *mwifiex_11d_code_2_region(u8 code); +void mwifiex_uap_set_channel(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_chan_def chandef); +int mwifiex_config_start_uap(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg); +void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, + struct mwifiex_sta_node *node); + +void mwifiex_init_11h_params(struct mwifiex_private *priv); +int mwifiex_is_11h_active(struct mwifiex_private *priv); +int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag); + +void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv); +int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, + struct device_node *node, const char *prefix); +void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv); + +extern const struct ethtool_ops mwifiex_ethtool_ops; + +void mwifiex_del_all_sta_list(struct mwifiex_private *priv); +void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac); +void +mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, + int ies_len, struct mwifiex_sta_node *node); +struct mwifiex_sta_node * +mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac); +struct mwifiex_sta_node * +mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac); +u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv); +u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv); +u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv); +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len); +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len); +void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, + u8 *buf, int len); +int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action); +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac); +int mwifiex_get_tdls_list(struct mwifiex_private *priv, + struct tdls_peer_info *buf); +void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv); +bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv); +u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, + u32 pri_chan, u8 chan_bw); +int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter); + +int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb); +void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv); +void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv, + const u8 *mac, u8 link_status); +void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv, + u8 *mac, s8 snr, s8 nflr); +void mwifiex_check_auto_tdls(unsigned long context); +void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac); +void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv); +void mwifiex_clean_auto_tdls(struct mwifiex_private *priv); +int mwifiex_config_tdls_enable(struct mwifiex_private *priv); +int mwifiex_config_tdls_disable(struct mwifiex_private *priv); +int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv); +int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac); +int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac, + u8 primary_chan, u8 second_chan_offset, u8 band); + +int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf); +int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, + struct sk_buff *skb); + +void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, + void *event_body); + +struct sk_buff * +mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, + struct sk_buff *skb, u8 flag, u64 *cookie); +void mwifiex_dfs_cac_work_queue(struct work_struct *work); +void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work); +void mwifiex_abort_cac(struct mwifiex_private *priv); +int mwifiex_stop_radar_detection(struct mwifiex_private *priv, + struct cfg80211_chan_def *chandef); +int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, + struct sk_buff *skb); + +void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, + s8 nflr); +void mwifiex_hist_data_reset(struct mwifiex_private *priv); +void mwifiex_hist_data_add(struct mwifiex_private *priv, + u8 rx_rate, s8 snr, s8 nflr); +u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, + u8 rx_rate, u8 ht_info); + +void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter); +void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter); +void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags); +void mwifiex_queue_main_work(struct mwifiex_adapter *adapter); +void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter); +void mwifiex_11n_delba(struct mwifiex_private *priv, int tid); +int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy); +void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, + struct sk_buff *event); +void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, + struct sk_buff *event_skb); +void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter); + +#ifdef CONFIG_DEBUG_FS +void mwifiex_debugfs_init(void); +void mwifiex_debugfs_remove(void); + +void mwifiex_dev_debugfs_init(struct mwifiex_private *priv); +void mwifiex_dev_debugfs_remove(struct mwifiex_private *priv); +#endif +#endif /* !_MWIFIEX_MAIN_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c new file mode 100644 index 000000000000..21192b6f9c64 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -0,0 +1,2742 @@ +/* + * Marvell Wireless LAN device driver: PCIE specific handling + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "pcie.h" + +#define PCIE_VERSION "1.0" +#define DRV_NAME "Marvell mwifiex PCIe" + +static u8 user_rmmod; + +static struct mwifiex_if_ops pcie_ops; + +static struct semaphore add_remove_card_sem; + +static struct memory_type_mapping mem_type_mapping_tbl[] = { + {"ITCM", NULL, 0, 0xF0}, + {"DTCM", NULL, 0, 0xF1}, + {"SQRAM", NULL, 0, 0xF2}, + {"IRAM", NULL, 0, 0xF3}, + {"APU", NULL, 0, 0xF4}, + {"CIU", NULL, 0, 0xF5}, + {"ICU", NULL, 0, 0xF6}, + {"MAC", NULL, 0, 0xF7}, +}; + +static int +mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, + size_t size, int flags) +{ + struct pcie_service_card *card = adapter->card; + struct mwifiex_dma_mapping mapping; + + mapping.addr = pci_map_single(card->dev, skb->data, size, flags); + if (pci_dma_mapping_error(card->dev, mapping.addr)) { + mwifiex_dbg(adapter, ERROR, "failed to map pci memory!\n"); + return -1; + } + mapping.len = size; + mwifiex_store_mapping(skb, &mapping); + return 0; +} + +static void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter, + struct sk_buff *skb, int flags) +{ + struct pcie_service_card *card = adapter->card; + struct mwifiex_dma_mapping mapping; + + mwifiex_get_mapping(skb, &mapping); + pci_unmap_single(card->dev, mapping.addr, mapping.len, flags); +} + +/* + * This function reads sleep cookie and checks if FW is ready + */ +static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) +{ + u32 *cookie_addr; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + if (!reg->sleep_cookie) + return true; + + if (card->sleep_cookie_vbase) { + cookie_addr = (u32 *)card->sleep_cookie_vbase; + mwifiex_dbg(adapter, INFO, + "info: ACCESS_HW: sleep cookie=0x%x\n", + *cookie_addr); + if (*cookie_addr == FW_AWAKE_COOKIE) + return true; + } + + return false; +} + +#ifdef CONFIG_PM_SLEEP +/* + * Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not suspended, this function allocates and sends a host + * sleep activate request to the firmware and turns off the traffic. + */ +static int mwifiex_pcie_suspend(struct device *dev) +{ + struct mwifiex_adapter *adapter; + struct pcie_service_card *card; + int hs_actived; + struct pci_dev *pdev = to_pci_dev(dev); + + if (pdev) { + card = pci_get_drvdata(pdev); + if (!card || !card->adapter) { + pr_err("Card or adapter structure is not valid\n"); + return 0; + } + } else { + pr_err("PCIE device is not specified\n"); + return 0; + } + + adapter = card->adapter; + + hs_actived = mwifiex_enable_hs(adapter); + + /* Indicate device suspended */ + adapter->is_suspended = true; + adapter->hs_enabling = false; + + return 0; +} + +/* + * Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not resumed, this function turns on the traffic and + * sends a host sleep cancel request to the firmware. + */ +static int mwifiex_pcie_resume(struct device *dev) +{ + struct mwifiex_adapter *adapter; + struct pcie_service_card *card; + struct pci_dev *pdev = to_pci_dev(dev); + + if (pdev) { + card = pci_get_drvdata(pdev); + if (!card || !card->adapter) { + pr_err("Card or adapter structure is not valid\n"); + return 0; + } + } else { + pr_err("PCIE device is not specified\n"); + return 0; + } + + adapter = card->adapter; + + if (!adapter->is_suspended) { + mwifiex_dbg(adapter, WARN, + "Device already resumed\n"); + return 0; + } + + adapter->is_suspended = false; + + mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), + MWIFIEX_ASYNC_CMD); + + return 0; +} +#endif + +/* + * This function probes an mwifiex device and registers it. It allocates + * the card structure, enables PCIE function number and initiates the + * device registration and initialization procedure by adding a logical + * interface. + */ +static int mwifiex_pcie_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct pcie_service_card *card; + + pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", + pdev->vendor, pdev->device, pdev->revision); + + card = kzalloc(sizeof(struct pcie_service_card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->dev = pdev; + + if (ent->driver_data) { + struct mwifiex_pcie_device *data = (void *)ent->driver_data; + card->pcie.firmware = data->firmware; + card->pcie.reg = data->reg; + card->pcie.blksz_fw_dl = data->blksz_fw_dl; + card->pcie.tx_buf_size = data->tx_buf_size; + card->pcie.can_dump_fw = data->can_dump_fw; + card->pcie.can_ext_scan = data->can_ext_scan; + } + + if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, + MWIFIEX_PCIE)) { + pr_err("%s failed\n", __func__); + kfree(card); + return -1; + } + + return 0; +} + +/* + * This function removes the interface and frees up the card structure. + */ +static void mwifiex_pcie_remove(struct pci_dev *pdev) +{ + struct pcie_service_card *card; + struct mwifiex_adapter *adapter; + struct mwifiex_private *priv; + + card = pci_get_drvdata(pdev); + if (!card) + return; + + adapter = card->adapter; + if (!adapter || !adapter->priv_num) + return; + + if (user_rmmod) { +#ifdef CONFIG_PM_SLEEP + if (adapter->is_suspended) + mwifiex_pcie_resume(&pdev->dev); +#endif + + mwifiex_deauthenticate_all(adapter); + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + mwifiex_disable_auto_ds(priv); + + mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); + } + + mwifiex_remove_card(card->adapter, &add_remove_card_sem); +} + +static void mwifiex_pcie_shutdown(struct pci_dev *pdev) +{ + user_rmmod = 1; + mwifiex_pcie_remove(pdev); + + return; +} + +static const struct pci_device_id mwifiex_ids[] = { + { + PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + .driver_data = (unsigned long)&mwifiex_pcie8766, + }, + { + PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8897, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + .driver_data = (unsigned long)&mwifiex_pcie8897, + }, + { + PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8997, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + .driver_data = (unsigned long)&mwifiex_pcie8997, + }, + {}, +}; + +MODULE_DEVICE_TABLE(pci, mwifiex_ids); + +#ifdef CONFIG_PM_SLEEP +/* Power Management Hooks */ +static SIMPLE_DEV_PM_OPS(mwifiex_pcie_pm_ops, mwifiex_pcie_suspend, + mwifiex_pcie_resume); +#endif + +/* PCI Device Driver */ +static struct pci_driver __refdata mwifiex_pcie = { + .name = "mwifiex_pcie", + .id_table = mwifiex_ids, + .probe = mwifiex_pcie_probe, + .remove = mwifiex_pcie_remove, +#ifdef CONFIG_PM_SLEEP + .driver = { + .pm = &mwifiex_pcie_pm_ops, + }, +#endif + .shutdown = mwifiex_pcie_shutdown, +}; + +/* + * This function writes data into PCIE card register. + */ +static int mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data) +{ + struct pcie_service_card *card = adapter->card; + + iowrite32(data, card->pci_mmap1 + reg); + + return 0; +} + +/* + * This function reads data from PCIE card register. + */ +static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data) +{ + struct pcie_service_card *card = adapter->card; + + *data = ioread32(card->pci_mmap1 + reg); + + return 0; +} + +/* This function reads u8 data from PCIE card register. */ +static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter, + int reg, u8 *data) +{ + struct pcie_service_card *card = adapter->card; + + *data = ioread8(card->pci_mmap1 + reg); + + return 0; +} + +/* + * This function adds delay loop to ensure FW is awake before proceeding. + */ +static void mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter) +{ + int i = 0; + + while (mwifiex_pcie_ok_to_access_hw(adapter)) { + i++; + usleep_range(10, 20); + /* 50ms max wait */ + if (i == 5000) + break; + } + + return; +} + +static void mwifiex_delay_for_sleep_cookie(struct mwifiex_adapter *adapter, + u32 max_delay_loop_cnt) +{ + struct pcie_service_card *card = adapter->card; + u8 *buffer; + u32 sleep_cookie, count; + + for (count = 0; count < max_delay_loop_cnt; count++) { + buffer = card->cmdrsp_buf->data - INTF_HEADER_LEN; + sleep_cookie = *(u32 *)buffer; + + if (sleep_cookie == MWIFIEX_DEF_SLEEP_COOKIE) { + mwifiex_dbg(adapter, INFO, + "sleep cookie found at count %d\n", count); + break; + } + usleep_range(20, 30); + } + + if (count >= max_delay_loop_cnt) + mwifiex_dbg(adapter, INFO, + "max count reached while accessing sleep cookie\n"); +} + +/* This function wakes up the card by reading fw_status register. */ +static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) +{ + u32 fw_status; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + mwifiex_dbg(adapter, EVENT, + "event: Wakeup device...\n"); + + if (reg->sleep_cookie) + mwifiex_pcie_dev_wakeup_delay(adapter); + + /* Reading fw_status register will wakeup device */ + if (mwifiex_read_reg(adapter, reg->fw_status, &fw_status)) { + mwifiex_dbg(adapter, ERROR, + "Reading fw_status register failed\n"); + return -1; + } + + if (reg->sleep_cookie) { + mwifiex_pcie_dev_wakeup_delay(adapter); + mwifiex_dbg(adapter, INFO, + "PCIE wakeup: Setting PS_STATE_AWAKE\n"); + adapter->ps_state = PS_STATE_AWAKE; + } + + return 0; +} + +/* + * This function is called after the card has woken up. + * + * The card configuration register is reset. + */ +static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) +{ + mwifiex_dbg(adapter, CMD, + "cmd: Wakeup device completed\n"); + + return 0; +} + +/* + * This function disables the host interrupt. + * + * The host interrupt mask is read, the disable bit is reset and + * written back to the card host interrupt mask register. + */ +static int mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter) +{ + if (mwifiex_pcie_ok_to_access_hw(adapter)) { + if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, + 0x00000000)) { + mwifiex_dbg(adapter, ERROR, + "Disable host interrupt failed\n"); + return -1; + } + } + + return 0; +} + +/* + * This function enables the host interrupt. + * + * The host interrupt enable mask is written to the card + * host interrupt mask register. + */ +static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter) +{ + if (mwifiex_pcie_ok_to_access_hw(adapter)) { + /* Simply write the mask to the register */ + if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, + HOST_INTR_MASK)) { + mwifiex_dbg(adapter, ERROR, + "Enable host interrupt failed\n"); + return -1; + } + } + + return 0; +} + +/* + * This function initializes TX buffer ring descriptors + */ +static int mwifiex_init_txq_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + int i; + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + card->tx_buf_list[i] = NULL; + if (reg->pfu_enabled) { + card->txbd_ring[i] = (void *)card->txbd_ring_vbase + + (sizeof(*desc2) * i); + desc2 = card->txbd_ring[i]; + memset(desc2, 0, sizeof(*desc2)); + } else { + card->txbd_ring[i] = (void *)card->txbd_ring_vbase + + (sizeof(*desc) * i); + desc = card->txbd_ring[i]; + memset(desc, 0, sizeof(*desc)); + } + } + + return 0; +} + +/* This function initializes RX buffer ring descriptors. Each SKB is allocated + * here and after mapping PCI memory, its physical address is assigned to + * PCIE Rx buffer descriptor's physical address. + */ +static int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + struct sk_buff *skb; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + dma_addr_t buf_pa; + int i; + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + /* Allocate skb here so that firmware can DMA data from it */ + skb = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, + GFP_KERNEL | GFP_DMA); + if (!skb) { + mwifiex_dbg(adapter, ERROR, + "Unable to allocate skb for RX ring.\n"); + kfree(card->rxbd_ring_vbase); + return -ENOMEM; + } + + if (mwifiex_map_pci_memory(adapter, skb, + MWIFIEX_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); + + mwifiex_dbg(adapter, INFO, + "info: RX ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", + skb, skb->len, skb->data, (u32)buf_pa, + (u32)((u64)buf_pa >> 32)); + + card->rx_buf_list[i] = skb; + if (reg->pfu_enabled) { + card->rxbd_ring[i] = (void *)card->rxbd_ring_vbase + + (sizeof(*desc2) * i); + desc2 = card->rxbd_ring[i]; + desc2->paddr = buf_pa; + desc2->len = (u16)skb->len; + desc2->frag_len = (u16)skb->len; + desc2->flags = reg->ring_flag_eop | reg->ring_flag_sop; + desc2->offset = 0; + } else { + card->rxbd_ring[i] = (void *)(card->rxbd_ring_vbase + + (sizeof(*desc) * i)); + desc = card->rxbd_ring[i]; + desc->paddr = buf_pa; + desc->len = (u16)skb->len; + desc->flags = 0; + } + } + + return 0; +} + +/* This function initializes event buffer ring descriptors. Each SKB is + * allocated here and after mapping PCI memory, its physical address is assigned + * to PCIE Rx buffer descriptor's physical address + */ +static int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + struct mwifiex_evt_buf_desc *desc; + struct sk_buff *skb; + dma_addr_t buf_pa; + int i; + + for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { + /* Allocate skb here so that firmware can DMA data from it */ + skb = dev_alloc_skb(MAX_EVENT_SIZE); + if (!skb) { + mwifiex_dbg(adapter, ERROR, + "Unable to allocate skb for EVENT buf.\n"); + kfree(card->evtbd_ring_vbase); + return -ENOMEM; + } + skb_put(skb, MAX_EVENT_SIZE); + + if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); + + mwifiex_dbg(adapter, EVENT, + "info: EVT ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", + skb, skb->len, skb->data, (u32)buf_pa, + (u32)((u64)buf_pa >> 32)); + + card->evt_buf_list[i] = skb; + card->evtbd_ring[i] = (void *)(card->evtbd_ring_vbase + + (sizeof(*desc) * i)); + desc = card->evtbd_ring[i]; + desc->paddr = buf_pa; + desc->len = (u16)skb->len; + desc->flags = 0; + } + + return 0; +} + +/* This function cleans up TX buffer rings. If any of the buffer list has valid + * SKB address, associated SKB is freed. + */ +static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + struct sk_buff *skb; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + int i; + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + if (reg->pfu_enabled) { + desc2 = card->txbd_ring[i]; + if (card->tx_buf_list[i]) { + skb = card->tx_buf_list[i]; + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); + dev_kfree_skb_any(skb); + } + memset(desc2, 0, sizeof(*desc2)); + } else { + desc = card->txbd_ring[i]; + if (card->tx_buf_list[i]) { + skb = card->tx_buf_list[i]; + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); + dev_kfree_skb_any(skb); + } + memset(desc, 0, sizeof(*desc)); + } + card->tx_buf_list[i] = NULL; + } + + return; +} + +/* This function cleans up RX buffer rings. If any of the buffer list has valid + * SKB address, associated SKB is freed. + */ +static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + struct sk_buff *skb; + int i; + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + if (reg->pfu_enabled) { + desc2 = card->rxbd_ring[i]; + if (card->rx_buf_list[i]) { + skb = card->rx_buf_list[i]; + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + } + memset(desc2, 0, sizeof(*desc2)); + } else { + desc = card->rxbd_ring[i]; + if (card->rx_buf_list[i]) { + skb = card->rx_buf_list[i]; + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + } + memset(desc, 0, sizeof(*desc)); + } + card->rx_buf_list[i] = NULL; + } + + return; +} + +/* This function cleans up event buffer rings. If any of the buffer list has + * valid SKB address, associated SKB is freed. + */ +static void mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + struct mwifiex_evt_buf_desc *desc; + struct sk_buff *skb; + int i; + + for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { + desc = card->evtbd_ring[i]; + if (card->evt_buf_list[i]) { + skb = card->evt_buf_list[i]; + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + } + card->evt_buf_list[i] = NULL; + memset(desc, 0, sizeof(*desc)); + } + + return; +} + +/* This function creates buffer descriptor ring for TX + */ +static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + /* + * driver maintaines the write pointer and firmware maintaines the read + * pointer. The write pointer starts at 0 (zero) while the read pointer + * starts at zero with rollover bit set + */ + card->txbd_wrptr = 0; + + if (reg->pfu_enabled) + card->txbd_rdptr = 0; + else + card->txbd_rdptr |= reg->tx_rollover_ind; + + /* allocate shared memory for the BD ring and divide the same in to + several descriptors */ + if (reg->pfu_enabled) + card->txbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) * + MWIFIEX_MAX_TXRX_BD; + else + card->txbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * + MWIFIEX_MAX_TXRX_BD; + + mwifiex_dbg(adapter, INFO, + "info: txbd_ring: Allocating %d bytes\n", + card->txbd_ring_size); + card->txbd_ring_vbase = pci_alloc_consistent(card->dev, + card->txbd_ring_size, + &card->txbd_ring_pbase); + if (!card->txbd_ring_vbase) { + mwifiex_dbg(adapter, ERROR, + "allocate consistent memory (%d bytes) failed!\n", + card->txbd_ring_size); + return -ENOMEM; + } + mwifiex_dbg(adapter, DATA, + "info: txbd_ring - base: %p, pbase: %#x:%x, len: %x\n", + card->txbd_ring_vbase, (unsigned int)card->txbd_ring_pbase, + (u32)((u64)card->txbd_ring_pbase >> 32), + card->txbd_ring_size); + + return mwifiex_init_txq_ring(adapter); +} + +static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + mwifiex_cleanup_txq_ring(adapter); + + if (card->txbd_ring_vbase) + pci_free_consistent(card->dev, card->txbd_ring_size, + card->txbd_ring_vbase, + card->txbd_ring_pbase); + card->txbd_ring_size = 0; + card->txbd_wrptr = 0; + card->txbd_rdptr = 0 | reg->tx_rollover_ind; + card->txbd_ring_vbase = NULL; + card->txbd_ring_pbase = 0; + + return 0; +} + +/* + * This function creates buffer descriptor ring for RX + */ +static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + /* + * driver maintaines the read pointer and firmware maintaines the write + * pointer. The write pointer starts at 0 (zero) while the read pointer + * starts at zero with rollover bit set + */ + card->rxbd_wrptr = 0; + card->rxbd_rdptr = reg->rx_rollover_ind; + + if (reg->pfu_enabled) + card->rxbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) * + MWIFIEX_MAX_TXRX_BD; + else + card->rxbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * + MWIFIEX_MAX_TXRX_BD; + + mwifiex_dbg(adapter, INFO, + "info: rxbd_ring: Allocating %d bytes\n", + card->rxbd_ring_size); + card->rxbd_ring_vbase = pci_alloc_consistent(card->dev, + card->rxbd_ring_size, + &card->rxbd_ring_pbase); + if (!card->rxbd_ring_vbase) { + mwifiex_dbg(adapter, ERROR, + "allocate consistent memory (%d bytes) failed!\n", + card->rxbd_ring_size); + return -ENOMEM; + } + + mwifiex_dbg(adapter, DATA, + "info: rxbd_ring - base: %p, pbase: %#x:%x, len: %#x\n", + card->rxbd_ring_vbase, (u32)card->rxbd_ring_pbase, + (u32)((u64)card->rxbd_ring_pbase >> 32), + card->rxbd_ring_size); + + return mwifiex_init_rxq_ring(adapter); +} + +/* + * This function deletes Buffer descriptor ring for RX + */ +static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + mwifiex_cleanup_rxq_ring(adapter); + + if (card->rxbd_ring_vbase) + pci_free_consistent(card->dev, card->rxbd_ring_size, + card->rxbd_ring_vbase, + card->rxbd_ring_pbase); + card->rxbd_ring_size = 0; + card->rxbd_wrptr = 0; + card->rxbd_rdptr = 0 | reg->rx_rollover_ind; + card->rxbd_ring_vbase = NULL; + card->rxbd_ring_pbase = 0; + + return 0; +} + +/* + * This function creates buffer descriptor ring for Events + */ +static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + /* + * driver maintaines the read pointer and firmware maintaines the write + * pointer. The write pointer starts at 0 (zero) while the read pointer + * starts at zero with rollover bit set + */ + card->evtbd_wrptr = 0; + card->evtbd_rdptr = reg->evt_rollover_ind; + + card->evtbd_ring_size = sizeof(struct mwifiex_evt_buf_desc) * + MWIFIEX_MAX_EVT_BD; + + mwifiex_dbg(adapter, INFO, + "info: evtbd_ring: Allocating %d bytes\n", + card->evtbd_ring_size); + card->evtbd_ring_vbase = pci_alloc_consistent(card->dev, + card->evtbd_ring_size, + &card->evtbd_ring_pbase); + if (!card->evtbd_ring_vbase) { + mwifiex_dbg(adapter, ERROR, + "allocate consistent memory (%d bytes) failed!\n", + card->evtbd_ring_size); + return -ENOMEM; + } + + mwifiex_dbg(adapter, EVENT, + "info: CMDRSP/EVT bd_ring - base: %p pbase: %#x:%x len: %#x\n", + card->evtbd_ring_vbase, (u32)card->evtbd_ring_pbase, + (u32)((u64)card->evtbd_ring_pbase >> 32), + card->evtbd_ring_size); + + return mwifiex_pcie_init_evt_ring(adapter); +} + +/* + * This function deletes Buffer descriptor ring for Events + */ +static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + mwifiex_cleanup_evt_ring(adapter); + + if (card->evtbd_ring_vbase) + pci_free_consistent(card->dev, card->evtbd_ring_size, + card->evtbd_ring_vbase, + card->evtbd_ring_pbase); + card->evtbd_wrptr = 0; + card->evtbd_rdptr = 0 | reg->evt_rollover_ind; + card->evtbd_ring_size = 0; + card->evtbd_ring_vbase = NULL; + card->evtbd_ring_pbase = 0; + + return 0; +} + +/* + * This function allocates a buffer for CMDRSP + */ +static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + struct sk_buff *skb; + + /* Allocate memory for receiving command response data */ + skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); + if (!skb) { + mwifiex_dbg(adapter, ERROR, + "Unable to allocate skb for command response data.\n"); + return -ENOMEM; + } + skb_put(skb, MWIFIEX_UPLD_SIZE); + if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + card->cmdrsp_buf = skb; + + return 0; +} + +/* + * This function deletes a buffer for CMDRSP + */ +static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card; + + if (!adapter) + return 0; + + card = adapter->card; + + if (card && card->cmdrsp_buf) { + mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(card->cmdrsp_buf); + } + + if (card && card->cmd_buf) { + mwifiex_unmap_pci_memory(adapter, card->cmd_buf, + PCI_DMA_TODEVICE); + } + return 0; +} + +/* + * This function allocates a buffer for sleep cookie + */ +static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + + card->sleep_cookie_vbase = pci_alloc_consistent(card->dev, sizeof(u32), + &card->sleep_cookie_pbase); + if (!card->sleep_cookie_vbase) { + mwifiex_dbg(adapter, ERROR, + "pci_alloc_consistent failed!\n"); + return -ENOMEM; + } + /* Init val of Sleep Cookie */ + *(u32 *)card->sleep_cookie_vbase = FW_AWAKE_COOKIE; + + mwifiex_dbg(adapter, INFO, + "alloc_scook: sleep cookie=0x%x\n", + *((u32 *)card->sleep_cookie_vbase)); + + return 0; +} + +/* + * This function deletes buffer for sleep cookie + */ +static int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card; + + if (!adapter) + return 0; + + card = adapter->card; + + if (card && card->sleep_cookie_vbase) { + pci_free_consistent(card->dev, sizeof(u32), + card->sleep_cookie_vbase, + card->sleep_cookie_pbase); + card->sleep_cookie_vbase = NULL; + } + + return 0; +} + +/* This function flushes the TX buffer descriptor ring + * This function defined as handler is also called while cleaning TXRX + * during disconnect/ bss stop. + */ +static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + + if (!mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) { + card->txbd_flush = 1; + /* write pointer already set at last send + * send dnld-rdy intr again, wait for completion. + */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DNLD_RDY)) { + mwifiex_dbg(adapter, ERROR, + "failed to assert dnld-rdy interrupt.\n"); + return -1; + } + } + return 0; +} + +/* + * This function unmaps and frees downloaded data buffer + */ +static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) +{ + struct sk_buff *skb; + u32 wrdoneidx, rdptr, num_tx_buffs, unmap_count = 0; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + + /* Read the TX ring read pointer set by firmware */ + if (mwifiex_read_reg(adapter, reg->tx_rdptr, &rdptr)) { + mwifiex_dbg(adapter, ERROR, + "SEND COMP: failed to read reg->tx_rdptr\n"); + return -1; + } + + mwifiex_dbg(adapter, DATA, + "SEND COMP: rdptr_prev=0x%x, rdptr=0x%x\n", + card->txbd_rdptr, rdptr); + + num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr; + /* free from previous txbd_rdptr to current txbd_rdptr */ + while (((card->txbd_rdptr & reg->tx_mask) != + (rdptr & reg->tx_mask)) || + ((card->txbd_rdptr & reg->tx_rollover_ind) != + (rdptr & reg->tx_rollover_ind))) { + wrdoneidx = (card->txbd_rdptr & reg->tx_mask) >> + reg->tx_start_ptr; + + skb = card->tx_buf_list[wrdoneidx]; + + if (skb) { + mwifiex_dbg(adapter, DATA, + "SEND COMP: Detach skb %p at txbd_rdidx=%d\n", + skb, wrdoneidx); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); + + unmap_count++; + + if (card->txbd_flush) + mwifiex_write_data_complete(adapter, skb, 0, + -1); + else + mwifiex_write_data_complete(adapter, skb, 0, 0); + } + + card->tx_buf_list[wrdoneidx] = NULL; + + if (reg->pfu_enabled) { + desc2 = card->txbd_ring[wrdoneidx]; + memset(desc2, 0, sizeof(*desc2)); + } else { + desc = card->txbd_ring[wrdoneidx]; + memset(desc, 0, sizeof(*desc)); + } + switch (card->dev->device) { + case PCIE_DEVICE_ID_MARVELL_88W8766P: + card->txbd_rdptr++; + break; + case PCIE_DEVICE_ID_MARVELL_88W8897: + case PCIE_DEVICE_ID_MARVELL_88W8997: + card->txbd_rdptr += reg->ring_tx_start_ptr; + break; + } + + + if ((card->txbd_rdptr & reg->tx_mask) == num_tx_buffs) + card->txbd_rdptr = ((card->txbd_rdptr & + reg->tx_rollover_ind) ^ + reg->tx_rollover_ind); + } + + if (unmap_count) + adapter->data_sent = false; + + if (card->txbd_flush) { + if (mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) + card->txbd_flush = 0; + else + mwifiex_clean_pcie_ring_buf(adapter); + } + + return 0; +} + +/* This function sends data buffer to device. First 4 bytes of payload + * are filled with payload length and payload type. Then this payload + * is mapped to PCI device memory. Tx ring pointers are advanced accordingly. + * Download ready interrupt to FW is deffered if Tx ring is not full and + * additional payload can be accomodated. + * Caller must ensure tx_param parameter to this function is not NULL. + */ +static int +mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + u32 wrindx, num_tx_buffs, rx_val; + int ret; + dma_addr_t buf_pa; + struct mwifiex_pcie_buf_desc *desc = NULL; + struct mwifiex_pfu_buf_desc *desc2 = NULL; + __le16 *tmp; + + if (!(skb->data && skb->len)) { + mwifiex_dbg(adapter, ERROR, + "%s(): invalid parameter <%p, %#x>\n", + __func__, skb->data, skb->len); + return -1; + } + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + + num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr; + mwifiex_dbg(adapter, DATA, + "info: SEND DATA: \n", + card->txbd_rdptr, card->txbd_wrptr); + if (mwifiex_pcie_txbd_not_full(card)) { + u8 *payload; + + adapter->data_sent = true; + payload = skb->data; + tmp = (__le16 *)&payload[0]; + *tmp = cpu_to_le16((u16)skb->len); + tmp = (__le16 *)&payload[2]; + *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA); + + if (mwifiex_map_pci_memory(adapter, skb, skb->len, + PCI_DMA_TODEVICE)) + return -1; + + wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr; + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); + card->tx_buf_list[wrindx] = skb; + + if (reg->pfu_enabled) { + desc2 = card->txbd_ring[wrindx]; + desc2->paddr = buf_pa; + desc2->len = (u16)skb->len; + desc2->frag_len = (u16)skb->len; + desc2->offset = 0; + desc2->flags = MWIFIEX_BD_FLAG_FIRST_DESC | + MWIFIEX_BD_FLAG_LAST_DESC; + } else { + desc = card->txbd_ring[wrindx]; + desc->paddr = buf_pa; + desc->len = (u16)skb->len; + desc->flags = MWIFIEX_BD_FLAG_FIRST_DESC | + MWIFIEX_BD_FLAG_LAST_DESC; + } + + switch (card->dev->device) { + case PCIE_DEVICE_ID_MARVELL_88W8766P: + card->txbd_wrptr++; + break; + case PCIE_DEVICE_ID_MARVELL_88W8897: + case PCIE_DEVICE_ID_MARVELL_88W8997: + card->txbd_wrptr += reg->ring_tx_start_ptr; + break; + } + + if ((card->txbd_wrptr & reg->tx_mask) == num_tx_buffs) + card->txbd_wrptr = ((card->txbd_wrptr & + reg->tx_rollover_ind) ^ + reg->tx_rollover_ind); + + rx_val = card->rxbd_rdptr & reg->rx_wrap_mask; + /* Write the TX ring write pointer in to reg->tx_wrptr */ + if (mwifiex_write_reg(adapter, reg->tx_wrptr, + card->txbd_wrptr | rx_val)) { + mwifiex_dbg(adapter, ERROR, + "SEND DATA: failed to write reg->tx_wrptr\n"); + ret = -1; + goto done_unmap; + } + if ((mwifiex_pcie_txbd_not_full(card)) && + tx_param->next_pkt_len) { + /* have more packets and TxBD still can hold more */ + mwifiex_dbg(adapter, DATA, + "SEND DATA: delay dnld-rdy interrupt.\n"); + adapter->data_sent = false; + } else { + /* Send the TX ready interrupt */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DNLD_RDY)) { + mwifiex_dbg(adapter, ERROR, + "SEND DATA: failed to assert dnld-rdy interrupt.\n"); + ret = -1; + goto done_unmap; + } + } + mwifiex_dbg(adapter, DATA, + "info: SEND DATA: Updated and sent packet to firmware successfully\n", + card->txbd_rdptr, card->txbd_wrptr); + } else { + mwifiex_dbg(adapter, DATA, + "info: TX Ring full, can't send packets to fw\n"); + adapter->data_sent = true; + /* Send the TX ready interrupt */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DNLD_RDY)) + mwifiex_dbg(adapter, ERROR, + "SEND DATA: failed to assert door-bell intr\n"); + return -EBUSY; + } + + return -EINPROGRESS; +done_unmap: + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + card->tx_buf_list[wrindx] = NULL; + if (reg->pfu_enabled) + memset(desc2, 0, sizeof(*desc2)); + else + memset(desc, 0, sizeof(*desc)); + + return ret; +} + +/* + * This function handles received buffer ring and + * dispatches packets to upper + */ +static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + u32 wrptr, rd_index, tx_val; + dma_addr_t buf_pa; + int ret = 0; + struct sk_buff *skb_tmp = NULL; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + + /* Read the RX ring Write pointer set by firmware */ + if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) { + mwifiex_dbg(adapter, ERROR, + "RECV DATA: failed to read reg->rx_wrptr\n"); + ret = -1; + goto done; + } + card->rxbd_wrptr = wrptr; + + while (((wrptr & reg->rx_mask) != + (card->rxbd_rdptr & reg->rx_mask)) || + ((wrptr & reg->rx_rollover_ind) == + (card->rxbd_rdptr & reg->rx_rollover_ind))) { + struct sk_buff *skb_data; + u16 rx_len; + __le16 pkt_len; + + rd_index = card->rxbd_rdptr & reg->rx_mask; + skb_data = card->rx_buf_list[rd_index]; + + /* If skb allocation was failed earlier for Rx packet, + * rx_buf_list[rd_index] would have been left with a NULL. + */ + if (!skb_data) + return -ENOMEM; + + mwifiex_unmap_pci_memory(adapter, skb_data, PCI_DMA_FROMDEVICE); + card->rx_buf_list[rd_index] = NULL; + + /* Get data length from interface header - + * first 2 bytes for len, next 2 bytes is for type + */ + pkt_len = *((__le16 *)skb_data->data); + rx_len = le16_to_cpu(pkt_len); + if (WARN_ON(rx_len <= INTF_HEADER_LEN || + rx_len > MWIFIEX_RX_DATA_BUF_SIZE)) { + mwifiex_dbg(adapter, ERROR, + "Invalid RX len %d, Rd=%#x, Wr=%#x\n", + rx_len, card->rxbd_rdptr, wrptr); + dev_kfree_skb_any(skb_data); + } else { + skb_put(skb_data, rx_len); + mwifiex_dbg(adapter, DATA, + "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n", + card->rxbd_rdptr, wrptr, rx_len); + skb_pull(skb_data, INTF_HEADER_LEN); + if (adapter->rx_work_enabled) { + skb_queue_tail(&adapter->rx_data_q, skb_data); + adapter->data_received = true; + atomic_inc(&adapter->rx_pending); + } else { + mwifiex_handle_rx_packet(adapter, skb_data); + } + } + + skb_tmp = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, + GFP_KERNEL | GFP_DMA); + if (!skb_tmp) { + mwifiex_dbg(adapter, ERROR, + "Unable to allocate skb.\n"); + return -ENOMEM; + } + + if (mwifiex_map_pci_memory(adapter, skb_tmp, + MWIFIEX_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb_tmp); + + mwifiex_dbg(adapter, INFO, + "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n", + skb_tmp, rd_index); + card->rx_buf_list[rd_index] = skb_tmp; + + if (reg->pfu_enabled) { + desc2 = card->rxbd_ring[rd_index]; + desc2->paddr = buf_pa; + desc2->len = skb_tmp->len; + desc2->frag_len = skb_tmp->len; + desc2->offset = 0; + desc2->flags = reg->ring_flag_sop | reg->ring_flag_eop; + } else { + desc = card->rxbd_ring[rd_index]; + desc->paddr = buf_pa; + desc->len = skb_tmp->len; + desc->flags = 0; + } + + if ((++card->rxbd_rdptr & reg->rx_mask) == + MWIFIEX_MAX_TXRX_BD) { + card->rxbd_rdptr = ((card->rxbd_rdptr & + reg->rx_rollover_ind) ^ + reg->rx_rollover_ind); + } + mwifiex_dbg(adapter, DATA, + "info: RECV DATA: \n", + card->rxbd_rdptr, wrptr); + + tx_val = card->txbd_wrptr & reg->tx_wrap_mask; + /* Write the RX ring read pointer in to reg->rx_rdptr */ + if (mwifiex_write_reg(adapter, reg->rx_rdptr, + card->rxbd_rdptr | tx_val)) { + mwifiex_dbg(adapter, DATA, + "RECV DATA: failed to write reg->rx_rdptr\n"); + ret = -1; + goto done; + } + + /* Read the RX ring Write pointer set by firmware */ + if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) { + mwifiex_dbg(adapter, ERROR, + "RECV DATA: failed to read reg->rx_wrptr\n"); + ret = -1; + goto done; + } + mwifiex_dbg(adapter, DATA, + "info: RECV DATA: Rcvd packet from fw successfully\n"); + card->rxbd_wrptr = wrptr; + } + +done: + return ret; +} + +/* + * This function downloads the boot command to device + */ +static int +mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) +{ + dma_addr_t buf_pa; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + if (!(skb->data && skb->len)) { + mwifiex_dbg(adapter, ERROR, + "Invalid parameter in %s <%p. len %d>\n", + __func__, skb->data, skb->len); + return -1; + } + + if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE)) + return -1; + + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); + + /* Write the lower 32bits of the physical address to low command + * address scratch register + */ + if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, (u32)buf_pa)) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to write download command to boot code.\n", + __func__); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + return -1; + } + + /* Write the upper 32bits of the physical address to high command + * address scratch register + */ + if (mwifiex_write_reg(adapter, reg->cmd_addr_hi, + (u32)((u64)buf_pa >> 32))) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to write download command to boot code.\n", + __func__); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + return -1; + } + + /* Write the command length to cmd_size scratch register */ + if (mwifiex_write_reg(adapter, reg->cmd_size, skb->len)) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to write command len to cmd_size scratch reg\n", + __func__); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + return -1; + } + + /* Ring the door bell */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DOOR_BELL)) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to assert door-bell intr\n", __func__); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + return -1; + } + + return 0; +} + +/* This function init rx port in firmware which in turn enables to receive data + * from device before transmitting any packet. + */ +static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask; + + /* Write the RX ring read pointer in to reg->rx_rdptr */ + if (mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr | + tx_wrap)) { + mwifiex_dbg(adapter, ERROR, + "RECV DATA: failed to write reg->rx_rdptr\n"); + return -1; + } + return 0; +} + +/* This function downloads commands to the device + */ +static int +mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + int ret = 0; + dma_addr_t cmd_buf_pa, cmdrsp_buf_pa; + u8 *payload = (u8 *)skb->data; + + if (!(skb->data && skb->len)) { + mwifiex_dbg(adapter, ERROR, + "Invalid parameter in %s <%p, %#x>\n", + __func__, skb->data, skb->len); + return -1; + } + + /* Make sure a command response buffer is available */ + if (!card->cmdrsp_buf) { + mwifiex_dbg(adapter, ERROR, + "No response buffer available, send command failed\n"); + return -EBUSY; + } + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + + adapter->cmd_sent = true; + + *(__le16 *)&payload[0] = cpu_to_le16((u16)skb->len); + *(__le16 *)&payload[2] = cpu_to_le16(MWIFIEX_TYPE_CMD); + + if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE)) + return -1; + + card->cmd_buf = skb; + + /* To send a command, the driver will: + 1. Write the 64bit physical address of the data buffer to + cmd response address low + cmd response address high + 2. Ring the door bell (i.e. set the door bell interrupt) + + In response to door bell interrupt, the firmware will perform + the DMA of the command packet (first header to obtain the total + length and then rest of the command). + */ + + if (card->cmdrsp_buf) { + cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf); + /* Write the lower 32bits of the cmdrsp buffer physical + address */ + if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, + (u32)cmdrsp_buf_pa)) { + mwifiex_dbg(adapter, ERROR, + "Failed to write download cmd to boot code.\n"); + ret = -1; + goto done; + } + /* Write the upper 32bits of the cmdrsp buffer physical + address */ + if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, + (u32)((u64)cmdrsp_buf_pa >> 32))) { + mwifiex_dbg(adapter, ERROR, + "Failed to write download cmd to boot code.\n"); + ret = -1; + goto done; + } + } + + cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf); + /* Write the lower 32bits of the physical address to reg->cmd_addr_lo */ + if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, + (u32)cmd_buf_pa)) { + mwifiex_dbg(adapter, ERROR, + "Failed to write download cmd to boot code.\n"); + ret = -1; + goto done; + } + /* Write the upper 32bits of the physical address to reg->cmd_addr_hi */ + if (mwifiex_write_reg(adapter, reg->cmd_addr_hi, + (u32)((u64)cmd_buf_pa >> 32))) { + mwifiex_dbg(adapter, ERROR, + "Failed to write download cmd to boot code.\n"); + ret = -1; + goto done; + } + + /* Write the command length to reg->cmd_size */ + if (mwifiex_write_reg(adapter, reg->cmd_size, + card->cmd_buf->len)) { + mwifiex_dbg(adapter, ERROR, + "Failed to write cmd len to reg->cmd_size\n"); + ret = -1; + goto done; + } + + /* Ring the door bell */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DOOR_BELL)) { + mwifiex_dbg(adapter, ERROR, + "Failed to assert door-bell intr\n"); + ret = -1; + goto done; + } + +done: + if (ret) + adapter->cmd_sent = false; + + return 0; +} + +/* + * This function handles command complete interrupt + */ +static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + struct sk_buff *skb = card->cmdrsp_buf; + int count = 0; + u16 rx_len; + __le16 pkt_len; + + mwifiex_dbg(adapter, CMD, + "info: Rx CMD Response\n"); + + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_FROMDEVICE); + + /* Unmap the command as a response has been received. */ + if (card->cmd_buf) { + mwifiex_unmap_pci_memory(adapter, card->cmd_buf, + PCI_DMA_TODEVICE); + card->cmd_buf = NULL; + } + + pkt_len = *((__le16 *)skb->data); + rx_len = le16_to_cpu(pkt_len); + skb_trim(skb, rx_len); + skb_pull(skb, INTF_HEADER_LEN); + + if (!adapter->curr_cmd) { + if (adapter->ps_state == PS_STATE_SLEEP_CFM) { + mwifiex_process_sleep_confirm_resp(adapter, skb->data, + skb->len); + mwifiex_pcie_enable_host_int(adapter); + if (mwifiex_write_reg(adapter, + PCIE_CPU_INT_EVENT, + CPU_INTR_SLEEP_CFM_DONE)) { + mwifiex_dbg(adapter, ERROR, + "Write register failed\n"); + return -1; + } + mwifiex_delay_for_sleep_cookie(adapter, + MWIFIEX_MAX_DELAY_COUNT); + while (reg->sleep_cookie && (count++ < 10) && + mwifiex_pcie_ok_to_access_hw(adapter)) + usleep_range(50, 60); + } else { + mwifiex_dbg(adapter, ERROR, + "There is no command but got cmdrsp\n"); + } + memcpy(adapter->upld_buf, skb->data, + min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len)); + skb_push(skb, INTF_HEADER_LEN); + if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + } else if (mwifiex_pcie_ok_to_access_hw(adapter)) { + adapter->curr_cmd->resp_skb = skb; + adapter->cmd_resp_received = true; + /* Take the pointer and set it to CMD node and will + return in the response complete callback */ + card->cmdrsp_buf = NULL; + + /* Clear the cmd-rsp buffer address in scratch registers. This + will prevent firmware from writing to the same response + buffer again. */ + if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, 0)) { + mwifiex_dbg(adapter, ERROR, + "cmd_done: failed to clear cmd_rsp_addr_lo\n"); + return -1; + } + /* Write the upper 32bits of the cmdrsp buffer physical + address */ + if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, 0)) { + mwifiex_dbg(adapter, ERROR, + "cmd_done: failed to clear cmd_rsp_addr_hi\n"); + return -1; + } + } + + return 0; +} + +/* + * Command Response processing complete handler + */ +static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + struct pcie_service_card *card = adapter->card; + + if (skb) { + card->cmdrsp_buf = skb; + skb_push(card->cmdrsp_buf, INTF_HEADER_LEN); + if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + } + + return 0; +} + +/* + * This function handles firmware event ready interrupt + */ +static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; + u32 wrptr, event; + struct mwifiex_evt_buf_desc *desc; + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + + if (adapter->event_received) { + mwifiex_dbg(adapter, EVENT, + "info: Event being processed,\t" + "do not process this interrupt just yet\n"); + return 0; + } + + if (rdptr >= MWIFIEX_MAX_EVT_BD) { + mwifiex_dbg(adapter, ERROR, + "info: Invalid read pointer...\n"); + return -1; + } + + /* Read the event ring write pointer set by firmware */ + if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) { + mwifiex_dbg(adapter, ERROR, + "EventReady: failed to read reg->evt_wrptr\n"); + return -1; + } + + mwifiex_dbg(adapter, EVENT, + "info: EventReady: Initial ", + card->evtbd_rdptr, wrptr); + if (((wrptr & MWIFIEX_EVTBD_MASK) != (card->evtbd_rdptr + & MWIFIEX_EVTBD_MASK)) || + ((wrptr & reg->evt_rollover_ind) == + (card->evtbd_rdptr & reg->evt_rollover_ind))) { + struct sk_buff *skb_cmd; + __le16 data_len = 0; + u16 evt_len; + + mwifiex_dbg(adapter, INFO, + "info: Read Index: %d\n", rdptr); + skb_cmd = card->evt_buf_list[rdptr]; + mwifiex_unmap_pci_memory(adapter, skb_cmd, PCI_DMA_FROMDEVICE); + + /* Take the pointer and set it to event pointer in adapter + and will return back after event handling callback */ + card->evt_buf_list[rdptr] = NULL; + desc = card->evtbd_ring[rdptr]; + memset(desc, 0, sizeof(*desc)); + + event = *(u32 *) &skb_cmd->data[INTF_HEADER_LEN]; + adapter->event_cause = event; + /* The first 4bytes will be the event transfer header + len is 2 bytes followed by type which is 2 bytes */ + memcpy(&data_len, skb_cmd->data, sizeof(__le16)); + evt_len = le16_to_cpu(data_len); + skb_trim(skb_cmd, evt_len); + skb_pull(skb_cmd, INTF_HEADER_LEN); + mwifiex_dbg(adapter, EVENT, + "info: Event length: %d\n", evt_len); + + if ((evt_len > 0) && (evt_len < MAX_EVENT_SIZE)) + memcpy(adapter->event_body, skb_cmd->data + + MWIFIEX_EVENT_HEADER_LEN, evt_len - + MWIFIEX_EVENT_HEADER_LEN); + + adapter->event_received = true; + adapter->event_skb = skb_cmd; + + /* Do not update the event read pointer here, wait till the + buffer is released. This is just to make things simpler, + we need to find a better method of managing these buffers. + */ + } else { + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_EVENT_DONE)) { + mwifiex_dbg(adapter, ERROR, + "Write register failed\n"); + return -1; + } + } + + return 0; +} + +/* + * Event processing complete handler + */ +static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + int ret = 0; + u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; + u32 wrptr; + struct mwifiex_evt_buf_desc *desc; + + if (!skb) + return 0; + + if (rdptr >= MWIFIEX_MAX_EVT_BD) { + mwifiex_dbg(adapter, ERROR, + "event_complete: Invalid rdptr 0x%x\n", + rdptr); + return -EINVAL; + } + + /* Read the event ring write pointer set by firmware */ + if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) { + mwifiex_dbg(adapter, ERROR, + "event_complete: failed to read reg->evt_wrptr\n"); + return -1; + } + + if (!card->evt_buf_list[rdptr]) { + skb_push(skb, INTF_HEADER_LEN); + skb_put(skb, MAX_EVENT_SIZE - skb->len); + if (mwifiex_map_pci_memory(adapter, skb, + MAX_EVENT_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + card->evt_buf_list[rdptr] = skb; + desc = card->evtbd_ring[rdptr]; + desc->paddr = MWIFIEX_SKB_DMA_ADDR(skb); + desc->len = (u16)skb->len; + desc->flags = 0; + skb = NULL; + } else { + mwifiex_dbg(adapter, ERROR, + "info: ERROR: buf still valid at index %d, <%p, %p>\n", + rdptr, card->evt_buf_list[rdptr], skb); + } + + if ((++card->evtbd_rdptr & MWIFIEX_EVTBD_MASK) == MWIFIEX_MAX_EVT_BD) { + card->evtbd_rdptr = ((card->evtbd_rdptr & + reg->evt_rollover_ind) ^ + reg->evt_rollover_ind); + } + + mwifiex_dbg(adapter, EVENT, + "info: Updated ", + card->evtbd_rdptr, wrptr); + + /* Write the event ring read pointer in to reg->evt_rdptr */ + if (mwifiex_write_reg(adapter, reg->evt_rdptr, + card->evtbd_rdptr)) { + mwifiex_dbg(adapter, ERROR, + "event_complete: failed to read reg->evt_rdptr\n"); + return -1; + } + + mwifiex_dbg(adapter, EVENT, + "info: Check Events Again\n"); + ret = mwifiex_pcie_process_event_ready(adapter); + + return ret; +} + +/* + * This function downloads the firmware to the card. + * + * Firmware is downloaded to the card in blocks. Every block download + * is tested for CRC errors, and retried a number of times before + * returning failure. + */ +static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, + struct mwifiex_fw_image *fw) +{ + int ret; + u8 *firmware = fw->fw_buf; + u32 firmware_len = fw->fw_len; + u32 offset = 0; + struct sk_buff *skb; + u32 txlen, tx_blocks = 0, tries, len; + u32 block_retry_cnt = 0; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + if (!firmware || !firmware_len) { + mwifiex_dbg(adapter, ERROR, + "No firmware image found! Terminating download\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "info: Downloading FW image (%d bytes)\n", + firmware_len); + + if (mwifiex_pcie_disable_host_int(adapter)) { + mwifiex_dbg(adapter, ERROR, + "%s: Disabling interrupts failed.\n", __func__); + return -1; + } + + skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); + if (!skb) { + ret = -ENOMEM; + goto done; + } + + /* Perform firmware data transfer */ + do { + u32 ireg_intr = 0; + + /* More data? */ + if (offset >= firmware_len) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = mwifiex_read_reg(adapter, reg->cmd_size, + &len); + if (ret) { + mwifiex_dbg(adapter, FATAL, + "Failed reading len from boot code\n"); + goto done; + } + if (len) + break; + usleep_range(10, 20); + } + + if (!len) { + break; + } else if (len > MWIFIEX_UPLD_SIZE) { + mwifiex_dbg(adapter, ERROR, + "FW download failure @ %d, invalid length %d\n", + offset, len); + ret = -1; + goto done; + } + + txlen = len; + + if (len & BIT(0)) { + block_retry_cnt++; + if (block_retry_cnt > MAX_WRITE_IOMEM_RETRY) { + mwifiex_dbg(adapter, ERROR, + "FW download failure @ %d, over max\t" + "retry count\n", offset); + ret = -1; + goto done; + } + mwifiex_dbg(adapter, ERROR, + "FW CRC error indicated by the\t" + "helper: len = 0x%04X, txlen = %d\n", + len, txlen); + len &= ~BIT(0); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + block_retry_cnt = 0; + /* Set blocksize to transfer - checking for + last block */ + if (firmware_len - offset < txlen) + txlen = firmware_len - offset; + + mwifiex_dbg(adapter, INFO, "."); + + tx_blocks = (txlen + card->pcie.blksz_fw_dl - 1) / + card->pcie.blksz_fw_dl; + + /* Copy payload to buffer */ + memmove(skb->data, &firmware[offset], txlen); + } + + skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); + skb_trim(skb, tx_blocks * card->pcie.blksz_fw_dl); + + /* Send the boot command to device */ + if (mwifiex_pcie_send_boot_cmd(adapter, skb)) { + mwifiex_dbg(adapter, ERROR, + "Failed to send firmware download command\n"); + ret = -1; + goto done; + } + + /* Wait for the command done interrupt */ + do { + if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS, + &ireg_intr)) { + mwifiex_dbg(adapter, ERROR, + "%s: Failed to read\t" + "interrupt status during fw dnld.\n", + __func__); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); + ret = -1; + goto done; + } + } while ((ireg_intr & CPU_INTR_DOOR_BELL) == + CPU_INTR_DOOR_BELL); + + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + + offset += txlen; + } while (true); + + mwifiex_dbg(adapter, MSG, + "info: FW download over, size %d bytes\n", offset); + + ret = 0; + +done: + dev_kfree_skb_any(skb); + return ret; +} + +/* + * This function checks the firmware status in card. + * + * The winner interface is also determined by this function. + */ +static int +mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) +{ + int ret = 0; + u32 firmware_stat, winner_status; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + u32 tries; + + /* Mask spurios interrupts */ + if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS_MASK, + HOST_INTR_MASK)) { + mwifiex_dbg(adapter, ERROR, + "Write register failed\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "Setting driver ready signature\n"); + if (mwifiex_write_reg(adapter, reg->drv_rdy, + FIRMWARE_READY_PCIE)) { + mwifiex_dbg(adapter, ERROR, + "Failed to write driver ready signature\n"); + return -1; + } + + /* Wait for firmware initialization event */ + for (tries = 0; tries < poll_num; tries++) { + if (mwifiex_read_reg(adapter, reg->fw_status, + &firmware_stat)) + ret = -1; + else + ret = 0; + if (ret) + continue; + if (firmware_stat == FIRMWARE_READY_PCIE) { + ret = 0; + break; + } else { + msleep(100); + ret = -1; + } + } + + if (ret) { + if (mwifiex_read_reg(adapter, reg->fw_status, + &winner_status)) + ret = -1; + else if (!winner_status) { + mwifiex_dbg(adapter, INFO, + "PCI-E is the winner\n"); + adapter->winner = 1; + } else { + mwifiex_dbg(adapter, ERROR, + "PCI-E is not the winner <%#x,%d>, exit dnld\n", + ret, adapter->winner); + } + } + + return ret; +} + +/* + * This function reads the interrupt status from card. + */ +static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) +{ + u32 pcie_ireg; + unsigned long flags; + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + return; + + if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, &pcie_ireg)) { + mwifiex_dbg(adapter, ERROR, "Read register failed\n"); + return; + } + + if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { + + mwifiex_pcie_disable_host_int(adapter); + + /* Clear the pending interrupts */ + if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS, + ~pcie_ireg)) { + mwifiex_dbg(adapter, ERROR, + "Write register failed\n"); + return; + } + spin_lock_irqsave(&adapter->int_lock, flags); + adapter->int_status |= pcie_ireg; + spin_unlock_irqrestore(&adapter->int_lock, flags); + + if (!adapter->pps_uapsd_mode && + adapter->ps_state == PS_STATE_SLEEP && + mwifiex_pcie_ok_to_access_hw(adapter)) { + /* Potentially for PCIe we could get other + * interrupts like shared. Don't change power + * state until cookie is set */ + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + } + } +} + +/* + * Interrupt handler for PCIe root port + * + * This function reads the interrupt status from firmware and assigns + * the main process in workqueue which will handle the interrupt. + */ +static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) +{ + struct pci_dev *pdev = (struct pci_dev *)context; + struct pcie_service_card *card; + struct mwifiex_adapter *adapter; + + if (!pdev) { + pr_debug("info: %s: pdev is NULL\n", (u8 *)pdev); + goto exit; + } + + card = pci_get_drvdata(pdev); + if (!card || !card->adapter) { + pr_debug("info: %s: card=%p adapter=%p\n", __func__, card, + card ? card->adapter : NULL); + goto exit; + } + adapter = card->adapter; + + if (adapter->surprise_removed) + goto exit; + + mwifiex_interrupt_status(adapter); + mwifiex_queue_main_work(adapter); + +exit: + return IRQ_HANDLED; +} + +/* + * This function checks the current interrupt status. + * + * The following interrupts are checked and handled by this function - + * - Data sent + * - Command sent + * - Command received + * - Packets received + * - Events received + * + * In case of Rx packets received, the packets are uploaded from card to + * host and processed accordingly. + */ +static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) +{ + int ret; + u32 pcie_ireg; + unsigned long flags; + + spin_lock_irqsave(&adapter->int_lock, flags); + /* Clear out unused interrupts */ + pcie_ireg = adapter->int_status; + adapter->int_status = 0; + spin_unlock_irqrestore(&adapter->int_lock, flags); + + while (pcie_ireg & HOST_INTR_MASK) { + if (pcie_ireg & HOST_INTR_DNLD_DONE) { + pcie_ireg &= ~HOST_INTR_DNLD_DONE; + mwifiex_dbg(adapter, INTR, + "info: TX DNLD Done\n"); + ret = mwifiex_pcie_send_data_complete(adapter); + if (ret) + return ret; + } + if (pcie_ireg & HOST_INTR_UPLD_RDY) { + pcie_ireg &= ~HOST_INTR_UPLD_RDY; + mwifiex_dbg(adapter, INTR, + "info: Rx DATA\n"); + ret = mwifiex_pcie_process_recv_data(adapter); + if (ret) + return ret; + } + if (pcie_ireg & HOST_INTR_EVENT_RDY) { + pcie_ireg &= ~HOST_INTR_EVENT_RDY; + mwifiex_dbg(adapter, INTR, + "info: Rx EVENT\n"); + ret = mwifiex_pcie_process_event_ready(adapter); + if (ret) + return ret; + } + + if (pcie_ireg & HOST_INTR_CMD_DONE) { + pcie_ireg &= ~HOST_INTR_CMD_DONE; + if (adapter->cmd_sent) { + mwifiex_dbg(adapter, INTR, + "info: CMD sent Interrupt\n"); + adapter->cmd_sent = false; + } + /* Handle command response */ + ret = mwifiex_pcie_process_cmd_complete(adapter); + if (ret) + return ret; + } + + if (mwifiex_pcie_ok_to_access_hw(adapter)) { + if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, + &pcie_ireg)) { + mwifiex_dbg(adapter, ERROR, + "Read register failed\n"); + return -1; + } + + if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { + if (mwifiex_write_reg(adapter, + PCIE_HOST_INT_STATUS, + ~pcie_ireg)) { + mwifiex_dbg(adapter, ERROR, + "Write register failed\n"); + return -1; + } + } + + } + } + mwifiex_dbg(adapter, INTR, + "info: cmd_sent=%d data_sent=%d\n", + adapter->cmd_sent, adapter->data_sent); + if (adapter->ps_state != PS_STATE_SLEEP) + mwifiex_pcie_enable_host_int(adapter); + + return 0; +} + +/* + * This function downloads data from driver to card. + * + * Both commands and data packets are transferred to the card by this + * function. + * + * This function adds the PCIE specific header to the front of the buffer + * before transferring. The header contains the length of the packet and + * the type. The firmware handles the packets based upon this set type. + */ +static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type, + struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + if (!skb) { + mwifiex_dbg(adapter, ERROR, + "Passed NULL skb to %s\n", __func__); + return -1; + } + + if (type == MWIFIEX_TYPE_DATA) + return mwifiex_pcie_send_data(adapter, skb, tx_param); + else if (type == MWIFIEX_TYPE_CMD) + return mwifiex_pcie_send_cmd(adapter, skb); + + return 0; +} + +/* This function read/write firmware */ +static enum rdwr_status +mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag) +{ + int ret, tries; + u8 ctrl_data; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "PCIE write err\n"); + return RDWR_STATUS_FAILURE; + } + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data); + if (ctrl_data == FW_DUMP_DONE) + return RDWR_STATUS_SUCCESS; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != FW_DUMP_HOST_READY) { + mwifiex_dbg(adapter, WARN, + "The ctrl reg was changed, re-try again!\n"); + ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, + FW_DUMP_HOST_READY); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "PCIE write err\n"); + return RDWR_STATUS_FAILURE; + } + } + usleep_range(100, 200); + } + + mwifiex_dbg(adapter, ERROR, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; +} + +/* This function dump firmware memory to file */ +static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *creg = card->pcie.reg; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; + enum rdwr_status stat; + u32 memory_size; + int ret; + + if (!card->pcie.can_dump_fw) + return; + + for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump start ==\n"); + + /* Read the number of the memories which will dump */ + stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + return; + + reg = creg->fw_dump_start; + mwifiex_read_reg_byte(adapter, reg, &dump_num); + + /* Read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + return; + + memory_size = 0; + reg = creg->fw_dump_start; + for (i = 0; i < 4; i++) { + mwifiex_read_reg_byte(adapter, reg, &read_reg); + memory_size |= (read_reg << (i * 8)); + reg++; + } + + if (memory_size == 0) { + mwifiex_dbg(adapter, MSG, "Firmware dump Finished!\n"); + ret = mwifiex_write_reg(adapter, creg->fw_dump_ctrl, + FW_DUMP_READ_DONE); + if (ret) { + mwifiex_dbg(adapter, ERROR, "PCIE write err\n"); + return; + } + break; + } + + mwifiex_dbg(adapter, DUMP, + "%s_SIZE=0x%x\n", entry->mem_name, memory_size); + entry->mem_ptr = vmalloc(memory_size + 1); + entry->mem_size = memory_size; + if (!entry->mem_ptr) { + mwifiex_dbg(adapter, ERROR, + "Vmalloc %s failed\n", entry->mem_name); + return; + } + dbg_ptr = entry->mem_ptr; + end_ptr = dbg_ptr + memory_size; + + doneflag = entry->done_flag; + mwifiex_dbg(adapter, DUMP, "Start %s output, please wait...\n", + entry->mem_name); + + do { + stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); + if (RDWR_STATUS_FAILURE == stat) + return; + + reg_start = creg->fw_dump_start; + reg_end = creg->fw_dump_end; + for (reg = reg_start; reg <= reg_end; reg++) { + mwifiex_read_reg_byte(adapter, reg, dbg_ptr); + if (dbg_ptr < end_ptr) { + dbg_ptr++; + } else { + mwifiex_dbg(adapter, ERROR, + "Allocated buf not enough\n"); + return; + } + } + + if (stat != RDWR_STATUS_DONE) + continue; + + mwifiex_dbg(adapter, DUMP, + "%s done: size=0x%tx\n", + entry->mem_name, dbg_ptr - entry->mem_ptr); + break; + } while (true); + } + mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump end ==\n"); +} + +static void mwifiex_pcie_device_dump_work(struct mwifiex_adapter *adapter) +{ + mwifiex_drv_info_dump(adapter); + mwifiex_pcie_fw_dump(adapter); + mwifiex_upload_device_dump(adapter); +} + +static unsigned long iface_work_flags; +static struct mwifiex_adapter *save_adapter; +static void mwifiex_pcie_work(struct work_struct *work) +{ + if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, + &iface_work_flags)) + mwifiex_pcie_device_dump_work(save_adapter); +} + +static DECLARE_WORK(pcie_work, mwifiex_pcie_work); +/* This function dumps FW information */ +static void mwifiex_pcie_device_dump(struct mwifiex_adapter *adapter) +{ + save_adapter = adapter; + if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags)) + return; + + set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags); + + schedule_work(&pcie_work); +} + +/* + * This function initializes the PCI-E host memory space, WCB rings, etc. + * + * The following initializations steps are followed - + * - Allocate TXBD ring buffers + * - Allocate RXBD ring buffers + * - Allocate event BD ring buffers + * - Allocate command response ring buffer + * - Allocate sleep cookie buffer + */ +static int mwifiex_pcie_init(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + int ret; + struct pci_dev *pdev = card->dev; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + pci_set_drvdata(pdev, card); + + ret = pci_enable_device(pdev); + if (ret) + goto err_enable_dev; + + pci_set_master(pdev); + + mwifiex_dbg(adapter, INFO, + "try set_consistent_dma_mask(32)\n"); + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "set_dma_mask(32) failed\n"); + goto err_set_dma_mask; + } + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "set_consistent_dma_mask(64) failed\n"); + goto err_set_dma_mask; + } + + ret = pci_request_region(pdev, 0, DRV_NAME); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "req_reg(0) error\n"); + goto err_req_region0; + } + card->pci_mmap = pci_iomap(pdev, 0, 0); + if (!card->pci_mmap) { + mwifiex_dbg(adapter, ERROR, "iomap(0) error\n"); + ret = -EIO; + goto err_iomap0; + } + ret = pci_request_region(pdev, 2, DRV_NAME); + if (ret) { + mwifiex_dbg(adapter, ERROR, "req_reg(2) error\n"); + goto err_req_region2; + } + card->pci_mmap1 = pci_iomap(pdev, 2, 0); + if (!card->pci_mmap1) { + mwifiex_dbg(adapter, ERROR, + "iomap(2) error\n"); + ret = -EIO; + goto err_iomap2; + } + + mwifiex_dbg(adapter, INFO, + "PCI memory map Virt0: %p PCI memory map Virt2: %p\n", + card->pci_mmap, card->pci_mmap1); + + card->cmdrsp_buf = NULL; + ret = mwifiex_pcie_create_txbd_ring(adapter); + if (ret) + goto err_cre_txbd; + ret = mwifiex_pcie_create_rxbd_ring(adapter); + if (ret) + goto err_cre_rxbd; + ret = mwifiex_pcie_create_evtbd_ring(adapter); + if (ret) + goto err_cre_evtbd; + ret = mwifiex_pcie_alloc_cmdrsp_buf(adapter); + if (ret) + goto err_alloc_cmdbuf; + if (reg->sleep_cookie) { + ret = mwifiex_pcie_alloc_sleep_cookie_buf(adapter); + if (ret) + goto err_alloc_cookie; + } else { + card->sleep_cookie_vbase = NULL; + } + return ret; + +err_alloc_cookie: + mwifiex_pcie_delete_cmdrsp_buf(adapter); +err_alloc_cmdbuf: + mwifiex_pcie_delete_evtbd_ring(adapter); +err_cre_evtbd: + mwifiex_pcie_delete_rxbd_ring(adapter); +err_cre_rxbd: + mwifiex_pcie_delete_txbd_ring(adapter); +err_cre_txbd: + pci_iounmap(pdev, card->pci_mmap1); +err_iomap2: + pci_release_region(pdev, 2); +err_req_region2: + pci_iounmap(pdev, card->pci_mmap); +err_iomap0: + pci_release_region(pdev, 0); +err_req_region0: +err_set_dma_mask: + pci_disable_device(pdev); +err_enable_dev: + pci_set_drvdata(pdev, NULL); + return ret; +} + +/* + * This function cleans up the allocated card buffers. + * + * The following are freed by this function - + * - TXBD ring buffers + * - RXBD ring buffers + * - Event BD ring buffers + * - Command response ring buffer + * - Sleep cookie buffer + */ +static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + struct pci_dev *pdev = card->dev; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + if (user_rmmod) { + mwifiex_dbg(adapter, INFO, + "Clearing driver ready signature\n"); + if (mwifiex_write_reg(adapter, reg->drv_rdy, 0x00000000)) + mwifiex_dbg(adapter, ERROR, + "Failed to write driver not-ready signature\n"); + } + + if (pdev) { + pci_iounmap(pdev, card->pci_mmap); + pci_iounmap(pdev, card->pci_mmap1); + pci_disable_device(pdev); + pci_release_region(pdev, 2); + pci_release_region(pdev, 0); + pci_set_drvdata(pdev, NULL); + } + kfree(card); +} + +/* + * This function registers the PCIE device. + * + * PCIE IRQ is claimed, block size is set and driver data is initialized. + */ +static int mwifiex_register_dev(struct mwifiex_adapter *adapter) +{ + int ret; + struct pcie_service_card *card = adapter->card; + struct pci_dev *pdev = card->dev; + + /* save adapter pointer in card */ + card->adapter = adapter; + + ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED, + "MRVL_PCIE", pdev); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "request_irq failed: ret=%d\n", ret); + adapter->card = NULL; + return -1; + } + + adapter->dev = &pdev->dev; + adapter->tx_buf_size = card->pcie.tx_buf_size; + adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; + adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); + strcpy(adapter->fw_name, card->pcie.firmware); + adapter->ext_scan = card->pcie.can_ext_scan; + + return 0; +} + +/* + * This function unregisters the PCIE device. + * + * The PCIE IRQ is released, the function is disabled and driver + * data is set to null. + */ +static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg; + + if (card) { + mwifiex_dbg(adapter, INFO, + "%s(): calling free_irq()\n", __func__); + free_irq(card->dev->irq, card->dev); + + reg = card->pcie.reg; + if (reg->sleep_cookie) + mwifiex_pcie_delete_sleep_cookie_buf(adapter); + + mwifiex_pcie_delete_cmdrsp_buf(adapter); + mwifiex_pcie_delete_evtbd_ring(adapter); + mwifiex_pcie_delete_rxbd_ring(adapter); + mwifiex_pcie_delete_txbd_ring(adapter); + card->cmdrsp_buf = NULL; + } +} + +static struct mwifiex_if_ops pcie_ops = { + .init_if = mwifiex_pcie_init, + .cleanup_if = mwifiex_pcie_cleanup, + .check_fw_status = mwifiex_check_fw_status, + .prog_fw = mwifiex_prog_fw_w_helper, + .register_dev = mwifiex_register_dev, + .unregister_dev = mwifiex_unregister_dev, + .enable_int = mwifiex_pcie_enable_host_int, + .process_int_status = mwifiex_process_int_status, + .host_to_card = mwifiex_pcie_host_to_card, + .wakeup = mwifiex_pm_wakeup_card, + .wakeup_complete = mwifiex_pm_wakeup_card_complete, + + /* PCIE specific */ + .cmdrsp_complete = mwifiex_pcie_cmdrsp_complete, + .event_complete = mwifiex_pcie_event_complete, + .update_mp_end_port = NULL, + .cleanup_mpa_buf = NULL, + .init_fw_port = mwifiex_pcie_init_fw_port, + .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, + .device_dump = mwifiex_pcie_device_dump, +}; + +/* + * This function initializes the PCIE driver module. + * + * This initiates the semaphore and registers the device with + * PCIE bus. + */ +static int mwifiex_pcie_init_module(void) +{ + int ret; + + pr_debug("Marvell PCIe Driver\n"); + + sema_init(&add_remove_card_sem, 1); + + /* Clear the flag in case user removes the card. */ + user_rmmod = 0; + + ret = pci_register_driver(&mwifiex_pcie); + if (ret) + pr_err("Driver register failed!\n"); + else + pr_debug("info: Driver registered successfully!\n"); + + return ret; +} + +/* + * This function cleans up the PCIE driver. + * + * The following major steps are followed for cleanup - + * - Resume the device if its suspended + * - Disconnect the device if connected + * - Shutdown the firmware + * - Unregister the device from PCIE bus. + */ +static void mwifiex_pcie_cleanup_module(void) +{ + if (!down_interruptible(&add_remove_card_sem)) + up(&add_remove_card_sem); + + /* Set the flag as user is removing this module. */ + user_rmmod = 1; + + cancel_work_sync(&pcie_work); + pci_unregister_driver(&mwifiex_pcie); +} + +module_init(mwifiex_pcie_init_module); +module_exit(mwifiex_pcie_cleanup_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell WiFi-Ex PCI-Express Driver version " PCIE_VERSION); +MODULE_VERSION(PCIE_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE(PCIE8766_DEFAULT_FW_NAME); +MODULE_FIRMWARE(PCIE8897_DEFAULT_FW_NAME); +MODULE_FIRMWARE(PCIE8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h new file mode 100644 index 000000000000..48e549c3b285 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/pcie.h @@ -0,0 +1,382 @@ +/* @file mwifiex_pcie.h + * + * @brief This file contains definitions for PCI-E interface. + * driver. + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_PCIE_H +#define _MWIFIEX_PCIE_H + +#include +#include +#include + +#include "main.h" + +#define PCIE8766_DEFAULT_FW_NAME "mrvl/pcie8766_uapsta.bin" +#define PCIE8897_DEFAULT_FW_NAME "mrvl/pcie8897_uapsta.bin" +#define PCIE8997_DEFAULT_FW_NAME "mrvl/pcie8997_uapsta.bin" + +#define PCIE_VENDOR_ID_MARVELL (0x11ab) +#define PCIE_DEVICE_ID_MARVELL_88W8766P (0x2b30) +#define PCIE_DEVICE_ID_MARVELL_88W8897 (0x2b38) +#define PCIE_DEVICE_ID_MARVELL_88W8997 (0x2b42) + +/* Constants for Buffer Descriptor (BD) rings */ +#define MWIFIEX_MAX_TXRX_BD 0x20 +#define MWIFIEX_TXBD_MASK 0x3F +#define MWIFIEX_RXBD_MASK 0x3F + +#define MWIFIEX_MAX_EVT_BD 0x08 +#define MWIFIEX_EVTBD_MASK 0x0f + +/* PCIE INTERNAL REGISTERS */ +#define PCIE_SCRATCH_0_REG 0xC10 +#define PCIE_SCRATCH_1_REG 0xC14 +#define PCIE_CPU_INT_EVENT 0xC18 +#define PCIE_CPU_INT_STATUS 0xC1C +#define PCIE_HOST_INT_STATUS 0xC30 +#define PCIE_HOST_INT_MASK 0xC34 +#define PCIE_HOST_INT_STATUS_MASK 0xC3C +#define PCIE_SCRATCH_2_REG 0xC40 +#define PCIE_SCRATCH_3_REG 0xC44 +#define PCIE_SCRATCH_4_REG 0xCD0 +#define PCIE_SCRATCH_5_REG 0xCD4 +#define PCIE_SCRATCH_6_REG 0xCD8 +#define PCIE_SCRATCH_7_REG 0xCDC +#define PCIE_SCRATCH_8_REG 0xCE0 +#define PCIE_SCRATCH_9_REG 0xCE4 +#define PCIE_SCRATCH_10_REG 0xCE8 +#define PCIE_SCRATCH_11_REG 0xCEC +#define PCIE_SCRATCH_12_REG 0xCF0 +#define PCIE_RD_DATA_PTR_Q0_Q1 0xC08C +#define PCIE_WR_DATA_PTR_Q0_Q1 0xC05C + +#define CPU_INTR_DNLD_RDY BIT(0) +#define CPU_INTR_DOOR_BELL BIT(1) +#define CPU_INTR_SLEEP_CFM_DONE BIT(2) +#define CPU_INTR_RESET BIT(3) +#define CPU_INTR_EVENT_DONE BIT(5) + +#define HOST_INTR_DNLD_DONE BIT(0) +#define HOST_INTR_UPLD_RDY BIT(1) +#define HOST_INTR_CMD_DONE BIT(2) +#define HOST_INTR_EVENT_RDY BIT(3) +#define HOST_INTR_MASK (HOST_INTR_DNLD_DONE | \ + HOST_INTR_UPLD_RDY | \ + HOST_INTR_CMD_DONE | \ + HOST_INTR_EVENT_RDY) + +#define MWIFIEX_BD_FLAG_ROLLOVER_IND BIT(7) +#define MWIFIEX_BD_FLAG_FIRST_DESC BIT(0) +#define MWIFIEX_BD_FLAG_LAST_DESC BIT(1) +#define MWIFIEX_BD_FLAG_SOP BIT(0) +#define MWIFIEX_BD_FLAG_EOP BIT(1) +#define MWIFIEX_BD_FLAG_XS_SOP BIT(2) +#define MWIFIEX_BD_FLAG_XS_EOP BIT(3) +#define MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND BIT(7) +#define MWIFIEX_BD_FLAG_RX_ROLLOVER_IND BIT(10) +#define MWIFIEX_BD_FLAG_TX_START_PTR BIT(16) +#define MWIFIEX_BD_FLAG_TX_ROLLOVER_IND BIT(26) + +/* Max retry number of command write */ +#define MAX_WRITE_IOMEM_RETRY 2 +/* Define PCIE block size for firmware download */ +#define MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD 256 +/* FW awake cookie after FW ready */ +#define FW_AWAKE_COOKIE (0xAA55AA55) +#define MWIFIEX_DEF_SLEEP_COOKIE 0xBEEFBEEF +#define MWIFIEX_MAX_DELAY_COUNT 5 + +struct mwifiex_pcie_card_reg { + u16 cmd_addr_lo; + u16 cmd_addr_hi; + u16 fw_status; + u16 cmd_size; + u16 cmdrsp_addr_lo; + u16 cmdrsp_addr_hi; + u16 tx_rdptr; + u16 tx_wrptr; + u16 rx_rdptr; + u16 rx_wrptr; + u16 evt_rdptr; + u16 evt_wrptr; + u16 drv_rdy; + u16 tx_start_ptr; + u32 tx_mask; + u32 tx_wrap_mask; + u32 rx_mask; + u32 rx_wrap_mask; + u32 tx_rollover_ind; + u32 rx_rollover_ind; + u32 evt_rollover_ind; + u8 ring_flag_sop; + u8 ring_flag_eop; + u8 ring_flag_xs_sop; + u8 ring_flag_xs_eop; + u32 ring_tx_start_ptr; + u8 pfu_enabled; + u8 sleep_cookie; + u16 fw_dump_ctrl; + u16 fw_dump_start; + u16 fw_dump_end; +}; + +static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { + .cmd_addr_lo = PCIE_SCRATCH_0_REG, + .cmd_addr_hi = PCIE_SCRATCH_1_REG, + .cmd_size = PCIE_SCRATCH_2_REG, + .fw_status = PCIE_SCRATCH_3_REG, + .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, + .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, + .tx_rdptr = PCIE_SCRATCH_6_REG, + .tx_wrptr = PCIE_SCRATCH_7_REG, + .rx_rdptr = PCIE_SCRATCH_8_REG, + .rx_wrptr = PCIE_SCRATCH_9_REG, + .evt_rdptr = PCIE_SCRATCH_10_REG, + .evt_wrptr = PCIE_SCRATCH_11_REG, + .drv_rdy = PCIE_SCRATCH_12_REG, + .tx_start_ptr = 0, + .tx_mask = MWIFIEX_TXBD_MASK, + .tx_wrap_mask = 0, + .rx_mask = MWIFIEX_RXBD_MASK, + .rx_wrap_mask = 0, + .tx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, + .rx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, + .evt_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, + .ring_flag_sop = 0, + .ring_flag_eop = 0, + .ring_flag_xs_sop = 0, + .ring_flag_xs_eop = 0, + .ring_tx_start_ptr = 0, + .pfu_enabled = 0, + .sleep_cookie = 1, +}; + +static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = { + .cmd_addr_lo = PCIE_SCRATCH_0_REG, + .cmd_addr_hi = PCIE_SCRATCH_1_REG, + .cmd_size = PCIE_SCRATCH_2_REG, + .fw_status = PCIE_SCRATCH_3_REG, + .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, + .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, + .tx_rdptr = PCIE_RD_DATA_PTR_Q0_Q1, + .tx_wrptr = PCIE_WR_DATA_PTR_Q0_Q1, + .rx_rdptr = PCIE_WR_DATA_PTR_Q0_Q1, + .rx_wrptr = PCIE_RD_DATA_PTR_Q0_Q1, + .evt_rdptr = PCIE_SCRATCH_10_REG, + .evt_wrptr = PCIE_SCRATCH_11_REG, + .drv_rdy = PCIE_SCRATCH_12_REG, + .tx_start_ptr = 16, + .tx_mask = 0x03FF0000, + .tx_wrap_mask = 0x07FF0000, + .rx_mask = 0x000003FF, + .rx_wrap_mask = 0x000007FF, + .tx_rollover_ind = MWIFIEX_BD_FLAG_TX_ROLLOVER_IND, + .rx_rollover_ind = MWIFIEX_BD_FLAG_RX_ROLLOVER_IND, + .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND, + .ring_flag_sop = MWIFIEX_BD_FLAG_SOP, + .ring_flag_eop = MWIFIEX_BD_FLAG_EOP, + .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP, + .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP, + .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, + .pfu_enabled = 1, + .sleep_cookie = 0, + .fw_dump_ctrl = 0xcf4, + .fw_dump_start = 0xcf8, + .fw_dump_end = 0xcff, +}; + +static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = { + .cmd_addr_lo = PCIE_SCRATCH_0_REG, + .cmd_addr_hi = PCIE_SCRATCH_1_REG, + .cmd_size = PCIE_SCRATCH_2_REG, + .fw_status = PCIE_SCRATCH_3_REG, + .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, + .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, + .tx_rdptr = 0xC1A4, + .tx_wrptr = 0xC1A8, + .rx_rdptr = 0xC1A8, + .rx_wrptr = 0xC1A4, + .evt_rdptr = PCIE_SCRATCH_10_REG, + .evt_wrptr = PCIE_SCRATCH_11_REG, + .drv_rdy = PCIE_SCRATCH_12_REG, + .tx_start_ptr = 16, + .tx_mask = 0x0FFF0000, + .tx_wrap_mask = 0x01FF0000, + .rx_mask = 0x00000FFF, + .rx_wrap_mask = 0x000001FF, + .tx_rollover_ind = BIT(28), + .rx_rollover_ind = BIT(12), + .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND, + .ring_flag_sop = MWIFIEX_BD_FLAG_SOP, + .ring_flag_eop = MWIFIEX_BD_FLAG_EOP, + .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP, + .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP, + .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, + .pfu_enabled = 1, + .sleep_cookie = 0, +}; + +struct mwifiex_pcie_device { + const char *firmware; + const struct mwifiex_pcie_card_reg *reg; + u16 blksz_fw_dl; + u16 tx_buf_size; + bool can_dump_fw; + bool can_ext_scan; +}; + +static const struct mwifiex_pcie_device mwifiex_pcie8766 = { + .firmware = PCIE8766_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_8766, + .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .can_dump_fw = false, + .can_ext_scan = true, +}; + +static const struct mwifiex_pcie_device mwifiex_pcie8897 = { + .firmware = PCIE8897_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_8897, + .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, + .can_dump_fw = true, + .can_ext_scan = true, +}; + +static const struct mwifiex_pcie_device mwifiex_pcie8997 = { + .firmware = PCIE8997_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_8997, + .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, + .can_dump_fw = false, + .can_ext_scan = true, +}; + +struct mwifiex_evt_buf_desc { + u64 paddr; + u16 len; + u16 flags; +} __packed; + +struct mwifiex_pcie_buf_desc { + u64 paddr; + u16 len; + u16 flags; +} __packed; + +struct mwifiex_pfu_buf_desc { + u16 flags; + u16 offset; + u16 frag_len; + u16 len; + u64 paddr; + u32 reserved; +} __packed; + +struct pcie_service_card { + struct pci_dev *dev; + struct mwifiex_adapter *adapter; + struct mwifiex_pcie_device pcie; + + u8 txbd_flush; + u32 txbd_wrptr; + u32 txbd_rdptr; + u32 txbd_ring_size; + u8 *txbd_ring_vbase; + dma_addr_t txbd_ring_pbase; + void *txbd_ring[MWIFIEX_MAX_TXRX_BD]; + struct sk_buff *tx_buf_list[MWIFIEX_MAX_TXRX_BD]; + + u32 rxbd_wrptr; + u32 rxbd_rdptr; + u32 rxbd_ring_size; + u8 *rxbd_ring_vbase; + dma_addr_t rxbd_ring_pbase; + void *rxbd_ring[MWIFIEX_MAX_TXRX_BD]; + struct sk_buff *rx_buf_list[MWIFIEX_MAX_TXRX_BD]; + + u32 evtbd_wrptr; + u32 evtbd_rdptr; + u32 evtbd_ring_size; + u8 *evtbd_ring_vbase; + dma_addr_t evtbd_ring_pbase; + void *evtbd_ring[MWIFIEX_MAX_EVT_BD]; + struct sk_buff *evt_buf_list[MWIFIEX_MAX_EVT_BD]; + + struct sk_buff *cmd_buf; + struct sk_buff *cmdrsp_buf; + u8 *sleep_cookie_vbase; + dma_addr_t sleep_cookie_pbase; + void __iomem *pci_mmap; + void __iomem *pci_mmap1; +}; + +static inline int +mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr) +{ + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + switch (card->dev->device) { + case PCIE_DEVICE_ID_MARVELL_88W8766P: + if (((card->txbd_wrptr & reg->tx_mask) == + (rdptr & reg->tx_mask)) && + ((card->txbd_wrptr & reg->tx_rollover_ind) != + (rdptr & reg->tx_rollover_ind))) + return 1; + break; + case PCIE_DEVICE_ID_MARVELL_88W8897: + if (((card->txbd_wrptr & reg->tx_mask) == + (rdptr & reg->tx_mask)) && + ((card->txbd_wrptr & reg->tx_rollover_ind) == + (rdptr & reg->tx_rollover_ind))) + return 1; + break; + } + + return 0; +} + +static inline int +mwifiex_pcie_txbd_not_full(struct pcie_service_card *card) +{ + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + switch (card->dev->device) { + case PCIE_DEVICE_ID_MARVELL_88W8766P: + if (((card->txbd_wrptr & reg->tx_mask) != + (card->txbd_rdptr & reg->tx_mask)) || + ((card->txbd_wrptr & reg->tx_rollover_ind) != + (card->txbd_rdptr & reg->tx_rollover_ind))) + return 1; + break; + case PCIE_DEVICE_ID_MARVELL_88W8897: + case PCIE_DEVICE_ID_MARVELL_88W8997: + if (((card->txbd_wrptr & reg->tx_mask) != + (card->txbd_rdptr & reg->tx_mask)) || + ((card->txbd_wrptr & reg->tx_rollover_ind) == + (card->txbd_rdptr & reg->tx_rollover_ind))) + return 1; + break; + } + + return 0; +} + +#endif /* _MWIFIEX_PCIE_H */ diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c new file mode 100644 index 000000000000..c20017ced566 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -0,0 +1,2639 @@ +/* + * Marvell Wireless LAN device driver: scan ioctl and command handling + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "11n.h" +#include "cfg80211.h" + +/* The maximum number of channels the firmware can scan per command */ +#define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 + +#define MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD 4 + +/* Memory needed to store a max sized Channel List TLV for a firmware scan */ +#define CHAN_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_header) \ + + (MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN \ + *sizeof(struct mwifiex_chan_scan_param_set))) + +/* Memory needed to store supported rate */ +#define RATE_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_rates_param_set) \ + + HOSTCMD_SUPPORTED_RATES) + +/* Memory needed to store a max number/size WildCard SSID TLV for a firmware + scan */ +#define WILDCARD_SSID_TLV_MAX_SIZE \ + (MWIFIEX_MAX_SSID_LIST_LENGTH * \ + (sizeof(struct mwifiex_ie_types_wildcard_ssid_params) \ + + IEEE80211_MAX_SSID_LEN)) + +/* Maximum memory needed for a mwifiex_scan_cmd_config with all TLVs at max */ +#define MAX_SCAN_CFG_ALLOC (sizeof(struct mwifiex_scan_cmd_config) \ + + sizeof(struct mwifiex_ie_types_num_probes) \ + + sizeof(struct mwifiex_ie_types_htcap) \ + + CHAN_TLV_MAX_SIZE \ + + RATE_TLV_MAX_SIZE \ + + WILDCARD_SSID_TLV_MAX_SIZE) + + +union mwifiex_scan_cmd_config_tlv { + /* Scan configuration (variable length) */ + struct mwifiex_scan_cmd_config config; + /* Max allocated block */ + u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC]; +}; + +enum cipher_suite { + CIPHER_SUITE_TKIP, + CIPHER_SUITE_CCMP, + CIPHER_SUITE_MAX +}; +static u8 mwifiex_wpa_oui[CIPHER_SUITE_MAX][4] = { + { 0x00, 0x50, 0xf2, 0x02 }, /* TKIP */ + { 0x00, 0x50, 0xf2, 0x04 }, /* AES */ +}; +static u8 mwifiex_rsn_oui[CIPHER_SUITE_MAX][4] = { + { 0x00, 0x0f, 0xac, 0x02 }, /* TKIP */ + { 0x00, 0x0f, 0xac, 0x04 }, /* AES */ +}; + +/* + * This function parses a given IE for a given OUI. + * + * This is used to parse a WPA/RSN IE to find if it has + * a given oui in PTK. + */ +static u8 +mwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui) +{ + u8 count; + + count = iebody->ptk_cnt[0]; + + /* There could be multiple OUIs for PTK hence + 1) Take the length. + 2) Check all the OUIs for AES. + 3) If one of them is AES then pass success. */ + while (count) { + if (!memcmp(iebody->ptk_body, oui, sizeof(iebody->ptk_body))) + return MWIFIEX_OUI_PRESENT; + + --count; + if (count) + iebody = (struct ie_body *) ((u8 *) iebody + + sizeof(iebody->ptk_body)); + } + + pr_debug("info: %s: OUI is not found in PTK\n", __func__); + return MWIFIEX_OUI_NOT_PRESENT; +} + +/* + * This function checks if a given OUI is present in a RSN IE. + * + * The function first checks if a RSN IE is present or not in the + * BSS descriptor. It tries to locate the OUI only if such an IE is + * present. + */ +static u8 +mwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) +{ + u8 *oui; + struct ie_body *iebody; + u8 ret = MWIFIEX_OUI_NOT_PRESENT; + + if (((bss_desc->bcn_rsn_ie) && ((*(bss_desc->bcn_rsn_ie)). + ieee_hdr.element_id == WLAN_EID_RSN))) { + iebody = (struct ie_body *) + (((u8 *) bss_desc->bcn_rsn_ie->data) + + RSN_GTK_OUI_OFFSET); + oui = &mwifiex_rsn_oui[cipher][0]; + ret = mwifiex_search_oui_in_ie(iebody, oui); + if (ret) + return ret; + } + return ret; +} + +/* + * This function checks if a given OUI is present in a WPA IE. + * + * The function first checks if a WPA IE is present or not in the + * BSS descriptor. It tries to locate the OUI only if such an IE is + * present. + */ +static u8 +mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) +{ + u8 *oui; + struct ie_body *iebody; + u8 ret = MWIFIEX_OUI_NOT_PRESENT; + + if (((bss_desc->bcn_wpa_ie) && + ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id == + WLAN_EID_VENDOR_SPECIFIC))) { + iebody = (struct ie_body *) bss_desc->bcn_wpa_ie->data; + oui = &mwifiex_wpa_oui[cipher][0]; + ret = mwifiex_search_oui_in_ie(iebody, oui); + if (ret) + return ret; + } + return ret; +} + +/* + * This function compares two SSIDs and checks if they match. + */ +s32 +mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2) +{ + if (!ssid1 || !ssid2 || (ssid1->ssid_len != ssid2->ssid_len)) + return -1; + return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len); +} + +/* + * This function checks if wapi is enabled in driver and scanned network is + * compatible with it. + */ +static bool +mwifiex_is_bss_wapi(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (priv->sec_info.wapi_enabled && + (bss_desc->bcn_wapi_ie && + ((*(bss_desc->bcn_wapi_ie)).ieee_hdr.element_id == + WLAN_EID_BSS_AC_ACCESS_DELAY))) { + return true; + } + return false; +} + +/* + * This function checks if driver is configured with no security mode and + * scanned network is compatible with it. + */ +static bool +mwifiex_is_bss_no_sec(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && ((!bss_desc->bcn_wpa_ie) || + ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != + WLAN_EID_VENDOR_SPECIFIC)) && + ((!bss_desc->bcn_rsn_ie) || + ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != + WLAN_EID_RSN)) && + !priv->sec_info.encryption_mode && !bss_desc->privacy) { + return true; + } + return false; +} + +/* + * This function checks if static WEP is enabled in driver and scanned network + * is compatible with it. + */ +static bool +mwifiex_is_bss_static_wep(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && bss_desc->privacy) { + return true; + } + return false; +} + +/* + * This function checks if wpa is enabled in driver and scanned network is + * compatible with it. + */ +static bool +mwifiex_is_bss_wpa(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && ((bss_desc->bcn_wpa_ie) && + ((*(bss_desc->bcn_wpa_ie)). + vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC)) + /* + * Privacy bit may NOT be set in some APs like + * LinkSys WRT54G && bss_desc->privacy + */ + ) { + mwifiex_dbg(priv->adapter, INFO, + "info: %s: WPA:\t" + "wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\t" + "EncMode=%#x privacy=%#x\n", __func__, + (bss_desc->bcn_wpa_ie) ? + (*bss_desc->bcn_wpa_ie). + vend_hdr.element_id : 0, + (bss_desc->bcn_rsn_ie) ? + (*bss_desc->bcn_rsn_ie). + ieee_hdr.element_id : 0, + (priv->sec_info.wep_enabled) ? "e" : "d", + (priv->sec_info.wpa_enabled) ? "e" : "d", + (priv->sec_info.wpa2_enabled) ? "e" : "d", + priv->sec_info.encryption_mode, + bss_desc->privacy); + return true; + } + return false; +} + +/* + * This function checks if wpa2 is enabled in driver and scanned network is + * compatible with it. + */ +static bool +mwifiex_is_bss_wpa2(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && + !priv->sec_info.wpa_enabled && + priv->sec_info.wpa2_enabled && + ((bss_desc->bcn_rsn_ie) && + ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id == WLAN_EID_RSN))) { + /* + * Privacy bit may NOT be set in some APs like + * LinkSys WRT54G && bss_desc->privacy + */ + mwifiex_dbg(priv->adapter, INFO, + "info: %s: WPA2:\t" + "wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\t" + "EncMode=%#x privacy=%#x\n", __func__, + (bss_desc->bcn_wpa_ie) ? + (*bss_desc->bcn_wpa_ie). + vend_hdr.element_id : 0, + (bss_desc->bcn_rsn_ie) ? + (*bss_desc->bcn_rsn_ie). + ieee_hdr.element_id : 0, + (priv->sec_info.wep_enabled) ? "e" : "d", + (priv->sec_info.wpa_enabled) ? "e" : "d", + (priv->sec_info.wpa2_enabled) ? "e" : "d", + priv->sec_info.encryption_mode, + bss_desc->privacy); + return true; + } + return false; +} + +/* + * This function checks if adhoc AES is enabled in driver and scanned network is + * compatible with it. + */ +static bool +mwifiex_is_bss_adhoc_aes(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && + ((!bss_desc->bcn_wpa_ie) || + ((*(bss_desc->bcn_wpa_ie)). + vend_hdr.element_id != WLAN_EID_VENDOR_SPECIFIC)) && + ((!bss_desc->bcn_rsn_ie) || + ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && + !priv->sec_info.encryption_mode && bss_desc->privacy) { + return true; + } + return false; +} + +/* + * This function checks if dynamic WEP is enabled in driver and scanned network + * is compatible with it. + */ +static bool +mwifiex_is_bss_dynamic_wep(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && + ((!bss_desc->bcn_wpa_ie) || + ((*(bss_desc->bcn_wpa_ie)). + vend_hdr.element_id != WLAN_EID_VENDOR_SPECIFIC)) && + ((!bss_desc->bcn_rsn_ie) || + ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && + priv->sec_info.encryption_mode && bss_desc->privacy) { + mwifiex_dbg(priv->adapter, INFO, + "info: %s: dynamic\t" + "WEP: wpa_ie=%#x wpa2_ie=%#x\t" + "EncMode=%#x privacy=%#x\n", + __func__, + (bss_desc->bcn_wpa_ie) ? + (*bss_desc->bcn_wpa_ie). + vend_hdr.element_id : 0, + (bss_desc->bcn_rsn_ie) ? + (*bss_desc->bcn_rsn_ie). + ieee_hdr.element_id : 0, + priv->sec_info.encryption_mode, + bss_desc->privacy); + return true; + } + return false; +} + +/* + * This function checks if a scanned network is compatible with the driver + * settings. + * + * WEP WPA WPA2 ad-hoc encrypt Network + * enabled enabled enabled AES mode Privacy WPA WPA2 Compatible + * 0 0 0 0 NONE 0 0 0 yes No security + * 0 1 0 0 x 1x 1 x yes WPA (disable + * HT if no AES) + * 0 0 1 0 x 1x x 1 yes WPA2 (disable + * HT if no AES) + * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES + * 1 0 0 0 NONE 1 0 0 yes Static WEP + * (disable HT) + * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP + * + * Compatibility is not matched while roaming, except for mode. + */ +static s32 +mwifiex_is_network_compatible(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, u32 mode) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + bss_desc->disable_11n = false; + + /* Don't check for compatibility if roaming */ + if (priv->media_connected && + (priv->bss_mode == NL80211_IFTYPE_STATION) && + (bss_desc->bss_mode == NL80211_IFTYPE_STATION)) + return 0; + + if (priv->wps.session_enable) { + mwifiex_dbg(adapter, IOCTL, + "info: return success directly in WPS period\n"); + return 0; + } + + if (bss_desc->chan_sw_ie_present) { + mwifiex_dbg(adapter, INFO, + "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n"); + return -1; + } + + if (mwifiex_is_bss_wapi(priv, bss_desc)) { + mwifiex_dbg(adapter, INFO, + "info: return success for WAPI AP\n"); + return 0; + } + + if (bss_desc->bss_mode == mode) { + if (mwifiex_is_bss_no_sec(priv, bss_desc)) { + /* No security */ + return 0; + } else if (mwifiex_is_bss_static_wep(priv, bss_desc)) { + /* Static WEP enabled */ + mwifiex_dbg(adapter, INFO, + "info: Disable 11n in WEP mode.\n"); + bss_desc->disable_11n = true; + return 0; + } else if (mwifiex_is_bss_wpa(priv, bss_desc)) { + /* WPA enabled */ + if (((priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN) && + bss_desc->bcn_ht_cap) && + !mwifiex_is_wpa_oui_present(bss_desc, + CIPHER_SUITE_CCMP)) { + + if (mwifiex_is_wpa_oui_present + (bss_desc, CIPHER_SUITE_TKIP)) { + mwifiex_dbg(adapter, INFO, + "info: Disable 11n if AES\t" + "is not supported by AP\n"); + bss_desc->disable_11n = true; + } else { + return -1; + } + } + return 0; + } else if (mwifiex_is_bss_wpa2(priv, bss_desc)) { + /* WPA2 enabled */ + if (((priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN) && + bss_desc->bcn_ht_cap) && + !mwifiex_is_rsn_oui_present(bss_desc, + CIPHER_SUITE_CCMP)) { + + if (mwifiex_is_rsn_oui_present + (bss_desc, CIPHER_SUITE_TKIP)) { + mwifiex_dbg(adapter, INFO, + "info: Disable 11n if AES\t" + "is not supported by AP\n"); + bss_desc->disable_11n = true; + } else { + return -1; + } + } + return 0; + } else if (mwifiex_is_bss_adhoc_aes(priv, bss_desc)) { + /* Ad-hoc AES enabled */ + return 0; + } else if (mwifiex_is_bss_dynamic_wep(priv, bss_desc)) { + /* Dynamic WEP enabled */ + return 0; + } + + /* Security doesn't match */ + mwifiex_dbg(adapter, ERROR, + "info: %s: failed: wpa_ie=%#x wpa2_ie=%#x WEP=%s\t" + "WPA=%s WPA2=%s EncMode=%#x privacy=%#x\n", + __func__, + (bss_desc->bcn_wpa_ie) ? + (*bss_desc->bcn_wpa_ie).vend_hdr.element_id : 0, + (bss_desc->bcn_rsn_ie) ? + (*bss_desc->bcn_rsn_ie).ieee_hdr.element_id : 0, + (priv->sec_info.wep_enabled) ? "e" : "d", + (priv->sec_info.wpa_enabled) ? "e" : "d", + (priv->sec_info.wpa2_enabled) ? "e" : "d", + priv->sec_info.encryption_mode, bss_desc->privacy); + return -1; + } + + /* Mode doesn't match */ + return -1; +} + +/* + * This function creates a channel list for the driver to scan, based + * on region/band information. + * + * This routine is used for any scan that is not provided with a + * specific channel list to scan. + */ +static int +mwifiex_scan_create_channel_list(struct mwifiex_private *priv, + const struct mwifiex_user_scan_cfg + *user_scan_in, + struct mwifiex_chan_scan_param_set + *scan_chan_list, + u8 filtered_scan) +{ + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct mwifiex_adapter *adapter = priv->adapter; + int chan_idx = 0, i; + + for (band = 0; (band < IEEE80211_NUM_BANDS) ; band++) { + + if (!priv->wdev.wiphy->bands[band]) + continue; + + sband = priv->wdev.wiphy->bands[band]; + + for (i = 0; (i < sband->n_channels) ; i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + scan_chan_list[chan_idx].radio_type = band; + + if (user_scan_in && + user_scan_in->chan_list[0].scan_time) + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16((u16) user_scan_in-> + chan_list[0].scan_time); + else if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter->passive_scan_time); + else + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter->active_scan_time); + + if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].chan_scan_mode_bitmap + |= (MWIFIEX_PASSIVE_SCAN | + MWIFIEX_HIDDEN_SSID_REPORT); + else + scan_chan_list[chan_idx].chan_scan_mode_bitmap + &= ~MWIFIEX_PASSIVE_SCAN; + scan_chan_list[chan_idx].chan_number = + (u32) ch->hw_value; + if (filtered_scan) { + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter->specific_scan_time); + scan_chan_list[chan_idx].chan_scan_mode_bitmap + |= MWIFIEX_DISABLE_CHAN_FILT; + } + chan_idx++; + } + + } + return chan_idx; +} + +/* This function appends rate TLV to scan config command. */ +static int +mwifiex_append_rate_tlv(struct mwifiex_private *priv, + struct mwifiex_scan_cmd_config *scan_cfg_out, + u8 radio) +{ + struct mwifiex_ie_types_rates_param_set *rates_tlv; + u8 rates[MWIFIEX_SUPPORTED_RATES], *tlv_pos; + u32 rates_size; + + memset(rates, 0, sizeof(rates)); + + tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len; + + if (priv->scan_request) + rates_size = mwifiex_get_rates_from_cfg80211(priv, rates, + radio); + else + rates_size = mwifiex_get_supported_rates(priv, rates); + + mwifiex_dbg(priv->adapter, CMD, + "info: SCAN_CMD: Rates size = %d\n", + rates_size); + rates_tlv = (struct mwifiex_ie_types_rates_param_set *)tlv_pos; + rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); + rates_tlv->header.len = cpu_to_le16((u16) rates_size); + memcpy(rates_tlv->rates, rates, rates_size); + scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size; + + return rates_size; +} + +/* + * This function constructs and sends multiple scan config commands to + * the firmware. + * + * Previous routines in the code flow have created a scan command configuration + * with any requested TLVs. This function splits the channel TLV into maximum + * channels supported per scan lists and sends the portion of the channel TLV, + * along with the other TLVs, to the firmware. + */ +static int +mwifiex_scan_channel_list(struct mwifiex_private *priv, + u32 max_chan_per_scan, u8 filtered_scan, + struct mwifiex_scan_cmd_config *scan_cfg_out, + struct mwifiex_ie_types_chan_list_param_set + *chan_tlv_out, + struct mwifiex_chan_scan_param_set *scan_chan_list) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret = 0; + struct mwifiex_chan_scan_param_set *tmp_chan_list; + struct mwifiex_chan_scan_param_set *start_chan; + struct cmd_ctrl_node *cmd_node, *tmp_node; + unsigned long flags; + u32 tlv_idx, rates_size, cmd_no; + u32 total_scan_time; + u32 done_early; + u8 radio_type; + + if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) { + mwifiex_dbg(priv->adapter, ERROR, + "info: Scan: Null detect: %p, %p, %p\n", + scan_cfg_out, chan_tlv_out, scan_chan_list); + return -1; + } + + /* Check csa channel expiry before preparing scan list */ + mwifiex_11h_get_csa_closed_channel(priv); + + chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + + /* Set the temp channel struct pointer to the start of the desired + list */ + tmp_chan_list = scan_chan_list; + + /* Loop through the desired channel list, sending a new firmware scan + commands for each max_chan_per_scan channels (or for 1,6,11 + individually if configured accordingly) */ + while (tmp_chan_list->chan_number) { + + tlv_idx = 0; + total_scan_time = 0; + radio_type = 0; + chan_tlv_out->header.len = 0; + start_chan = tmp_chan_list; + done_early = false; + + /* + * Construct the Channel TLV for the scan command. Continue to + * insert channel TLVs until: + * - the tlv_idx hits the maximum configured per scan command + * - the next channel to insert is 0 (end of desired channel + * list) + * - done_early is set (controlling individual scanning of + * 1,6,11) + */ + while (tlv_idx < max_chan_per_scan && + tmp_chan_list->chan_number && !done_early) { + + if (tmp_chan_list->chan_number == priv->csa_chan) { + tmp_chan_list++; + continue; + } + + radio_type = tmp_chan_list->radio_type; + mwifiex_dbg(priv->adapter, INFO, + "info: Scan: Chan(%3d), Radio(%d),\t" + "Mode(%d, %d), Dur(%d)\n", + tmp_chan_list->chan_number, + tmp_chan_list->radio_type, + tmp_chan_list->chan_scan_mode_bitmap + & MWIFIEX_PASSIVE_SCAN, + (tmp_chan_list->chan_scan_mode_bitmap + & MWIFIEX_DISABLE_CHAN_FILT) >> 1, + le16_to_cpu(tmp_chan_list->max_scan_time)); + + /* Copy the current channel TLV to the command being + prepared */ + memcpy(chan_tlv_out->chan_scan_param + tlv_idx, + tmp_chan_list, + sizeof(chan_tlv_out->chan_scan_param)); + + /* Increment the TLV header length by the size + appended */ + le16_add_cpu(&chan_tlv_out->header.len, + sizeof(chan_tlv_out->chan_scan_param)); + + /* + * The tlv buffer length is set to the number of bytes + * of the between the channel tlv pointer and the start + * of the tlv buffer. This compensates for any TLVs + * that were appended before the channel list. + */ + scan_cfg_out->tlv_buf_len = (u32) ((u8 *) chan_tlv_out - + scan_cfg_out->tlv_buf); + + /* Add the size of the channel tlv header and the data + length */ + scan_cfg_out->tlv_buf_len += + (sizeof(chan_tlv_out->header) + + le16_to_cpu(chan_tlv_out->header.len)); + + /* Increment the index to the channel tlv we are + constructing */ + tlv_idx++; + + /* Count the total scan time per command */ + total_scan_time += + le16_to_cpu(tmp_chan_list->max_scan_time); + + done_early = false; + + /* Stop the loop if the *current* channel is in the + 1,6,11 set and we are not filtering on a BSSID + or SSID. */ + if (!filtered_scan && + (tmp_chan_list->chan_number == 1 || + tmp_chan_list->chan_number == 6 || + tmp_chan_list->chan_number == 11)) + done_early = true; + + /* Increment the tmp pointer to the next channel to + be scanned */ + tmp_chan_list++; + + /* Stop the loop if the *next* channel is in the 1,6,11 + set. This will cause it to be the only channel + scanned on the next interation */ + if (!filtered_scan && + (tmp_chan_list->chan_number == 1 || + tmp_chan_list->chan_number == 6 || + tmp_chan_list->chan_number == 11)) + done_early = true; + } + + /* The total scan time should be less than scan command timeout + value */ + if (total_scan_time > MWIFIEX_MAX_TOTAL_SCAN_TIME) { + mwifiex_dbg(priv->adapter, ERROR, + "total scan time %dms\t" + "is over limit (%dms), scan skipped\n", + total_scan_time, + MWIFIEX_MAX_TOTAL_SCAN_TIME); + ret = -1; + break; + } + + rates_size = mwifiex_append_rate_tlv(priv, scan_cfg_out, + radio_type); + + priv->adapter->scan_channels = start_chan; + + /* Send the scan command to the firmware with the specified + cfg */ + if (priv->adapter->ext_scan) + cmd_no = HostCmd_CMD_802_11_SCAN_EXT; + else + cmd_no = HostCmd_CMD_802_11_SCAN; + + ret = mwifiex_send_cmd(priv, cmd_no, HostCmd_ACT_GEN_SET, + 0, scan_cfg_out, false); + + /* rate IE is updated per scan command but same starting + * pointer is used each time so that rate IE from earlier + * scan_cfg_out->buf is overwritten with new one. + */ + scan_cfg_out->tlv_buf_len -= + sizeof(struct mwifiex_ie_types_header) + rates_size; + + if (ret) { + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, + list) { + list_del(&cmd_node->list); + cmd_node->wait_q_enabled = false; + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + break; + } + } + + if (ret) + return -1; + + return 0; +} + +/* + * This function constructs a scan command configuration structure to use + * in scan commands. + * + * Application layer or other functions can invoke network scanning + * with a scan configuration supplied in a user scan configuration structure. + * This structure is used as the basis of one or many scan command configuration + * commands that are sent to the command processing module and eventually to the + * firmware. + * + * This function creates a scan command configuration structure based on the + * following user supplied parameters (if present): + * - SSID filter + * - BSSID filter + * - Number of Probes to be sent + * - Channel list + * + * If the SSID or BSSID filter is not present, the filter is disabled/cleared. + * If the number of probes is not set, adapter default setting is used. + */ +static void +mwifiex_config_scan(struct mwifiex_private *priv, + const struct mwifiex_user_scan_cfg *user_scan_in, + struct mwifiex_scan_cmd_config *scan_cfg_out, + struct mwifiex_ie_types_chan_list_param_set **chan_list_out, + struct mwifiex_chan_scan_param_set *scan_chan_list, + u8 *max_chan_per_scan, u8 *filtered_scan, + u8 *scan_current_only) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_num_probes *num_probes_tlv; + struct mwifiex_ie_types_scan_chan_gap *chan_gap_tlv; + struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; + struct mwifiex_ie_types_bssid_list *bssid_tlv; + u8 *tlv_pos; + u32 num_probes; + u32 ssid_len; + u32 chan_idx; + u32 chan_num; + u32 scan_type; + u16 scan_dur; + u8 channel; + u8 radio_type; + int i; + u8 ssid_filter; + struct mwifiex_ie_types_htcap *ht_cap; + struct mwifiex_ie_types_bss_mode *bss_mode; + + /* The tlv_buf_len is calculated for each scan command. The TLVs added + in this routine will be preserved since the routine that sends the + command will append channelTLVs at *chan_list_out. The difference + between the *chan_list_out and the tlv_buf start will be used to + calculate the size of anything we add in this routine. */ + scan_cfg_out->tlv_buf_len = 0; + + /* Running tlv pointer. Assigned to chan_list_out at end of function + so later routines know where channels can be added to the command + buf */ + tlv_pos = scan_cfg_out->tlv_buf; + + /* Initialize the scan as un-filtered; the flag is later set to TRUE + below if a SSID or BSSID filter is sent in the command */ + *filtered_scan = false; + + /* Initialize the scan as not being only on the current channel. If + the channel list is customized, only contains one channel, and is + the active channel, this is set true and data flow is not halted. */ + *scan_current_only = false; + + if (user_scan_in) { + + /* Default the ssid_filter flag to TRUE, set false under + certain wildcard conditions and qualified by the existence + of an SSID list before marking the scan as filtered */ + ssid_filter = true; + + /* Set the BSS type scan filter, use Adapter setting if + unset */ + scan_cfg_out->bss_mode = + (user_scan_in->bss_mode ? (u8) user_scan_in-> + bss_mode : (u8) adapter->scan_mode); + + /* Set the number of probes to send, use Adapter setting + if unset */ + num_probes = + (user_scan_in->num_probes ? user_scan_in-> + num_probes : adapter->scan_probes); + + /* + * Set the BSSID filter to the incoming configuration, + * if non-zero. If not set, it will remain disabled + * (all zeros). + */ + memcpy(scan_cfg_out->specific_bssid, + user_scan_in->specific_bssid, + sizeof(scan_cfg_out->specific_bssid)); + + if (adapter->ext_scan && + !is_zero_ether_addr(scan_cfg_out->specific_bssid)) { + bssid_tlv = + (struct mwifiex_ie_types_bssid_list *)tlv_pos; + bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID); + bssid_tlv->header.len = cpu_to_le16(ETH_ALEN); + memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid, + ETH_ALEN); + tlv_pos += sizeof(struct mwifiex_ie_types_bssid_list); + } + + for (i = 0; i < user_scan_in->num_ssids; i++) { + ssid_len = user_scan_in->ssid_list[i].ssid_len; + + wildcard_ssid_tlv = + (struct mwifiex_ie_types_wildcard_ssid_params *) + tlv_pos; + wildcard_ssid_tlv->header.type = + cpu_to_le16(TLV_TYPE_WILDCARDSSID); + wildcard_ssid_tlv->header.len = cpu_to_le16( + (u16) (ssid_len + sizeof(wildcard_ssid_tlv-> + max_ssid_length))); + + /* + * max_ssid_length = 0 tells firmware to perform + * specific scan for the SSID filled, whereas + * max_ssid_length = IEEE80211_MAX_SSID_LEN is for + * wildcard scan. + */ + if (ssid_len) + wildcard_ssid_tlv->max_ssid_length = 0; + else + wildcard_ssid_tlv->max_ssid_length = + IEEE80211_MAX_SSID_LEN; + + if (!memcmp(user_scan_in->ssid_list[i].ssid, + "DIRECT-", 7)) + wildcard_ssid_tlv->max_ssid_length = 0xfe; + + memcpy(wildcard_ssid_tlv->ssid, + user_scan_in->ssid_list[i].ssid, ssid_len); + + tlv_pos += (sizeof(wildcard_ssid_tlv->header) + + le16_to_cpu(wildcard_ssid_tlv->header.len)); + + mwifiex_dbg(adapter, INFO, + "info: scan: ssid[%d]: %s, %d\n", + i, wildcard_ssid_tlv->ssid, + wildcard_ssid_tlv->max_ssid_length); + + /* Empty wildcard ssid with a maxlen will match many or + potentially all SSIDs (maxlen == 32), therefore do + not treat the scan as + filtered. */ + if (!ssid_len && wildcard_ssid_tlv->max_ssid_length) + ssid_filter = false; + } + + /* + * The default number of channels sent in the command is low to + * ensure the response buffer from the firmware does not + * truncate scan results. That is not an issue with an SSID + * or BSSID filter applied to the scan results in the firmware. + */ + if ((i && ssid_filter) || + !is_zero_ether_addr(scan_cfg_out->specific_bssid)) + *filtered_scan = true; + + if (user_scan_in->scan_chan_gap) { + mwifiex_dbg(adapter, INFO, + "info: scan: channel gap = %d\n", + user_scan_in->scan_chan_gap); + *max_chan_per_scan = + MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; + + chan_gap_tlv = (void *)tlv_pos; + chan_gap_tlv->header.type = + cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); + chan_gap_tlv->header.len = + cpu_to_le16(sizeof(chan_gap_tlv->chan_gap)); + chan_gap_tlv->chan_gap = + cpu_to_le16((user_scan_in->scan_chan_gap)); + tlv_pos += + sizeof(struct mwifiex_ie_types_scan_chan_gap); + } + } else { + scan_cfg_out->bss_mode = (u8) adapter->scan_mode; + num_probes = adapter->scan_probes; + } + + /* + * If a specific BSSID or SSID is used, the number of channels in the + * scan command will be increased to the absolute maximum. + */ + if (*filtered_scan) + *max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; + else + *max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD; + + if (adapter->ext_scan) { + bss_mode = (struct mwifiex_ie_types_bss_mode *)tlv_pos; + bss_mode->header.type = cpu_to_le16(TLV_TYPE_BSS_MODE); + bss_mode->header.len = cpu_to_le16(sizeof(bss_mode->bss_mode)); + bss_mode->bss_mode = scan_cfg_out->bss_mode; + tlv_pos += sizeof(bss_mode->header) + + le16_to_cpu(bss_mode->header.len); + } + + /* If the input config or adapter has the number of Probes set, + add tlv */ + if (num_probes) { + + mwifiex_dbg(adapter, INFO, + "info: scan: num_probes = %d\n", + num_probes); + + num_probes_tlv = (struct mwifiex_ie_types_num_probes *) tlv_pos; + num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); + num_probes_tlv->header.len = + cpu_to_le16(sizeof(num_probes_tlv->num_probes)); + num_probes_tlv->num_probes = cpu_to_le16((u16) num_probes); + + tlv_pos += sizeof(num_probes_tlv->header) + + le16_to_cpu(num_probes_tlv->header.len); + + } + + if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && + (priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN)) { + ht_cap = (struct mwifiex_ie_types_htcap *) tlv_pos; + memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); + ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_cap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + radio_type = + mwifiex_band_to_radio_type(priv->adapter->config_bands); + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); + tlv_pos += sizeof(struct mwifiex_ie_types_htcap); + } + + /* Append vendor specific IE TLV */ + mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_SCAN, &tlv_pos); + + /* + * Set the output for the channel TLV to the address in the tlv buffer + * past any TLVs that were added in this function (SSID, num_probes). + * Channel TLVs will be added past this for each scan command, + * preserving the TLVs that were previously added. + */ + *chan_list_out = + (struct mwifiex_ie_types_chan_list_param_set *) tlv_pos; + + if (user_scan_in && user_scan_in->chan_list[0].chan_number) { + + mwifiex_dbg(adapter, INFO, + "info: Scan: Using supplied channel list\n"); + + for (chan_idx = 0; + chan_idx < MWIFIEX_USER_SCAN_CHAN_MAX && + user_scan_in->chan_list[chan_idx].chan_number; + chan_idx++) { + + channel = user_scan_in->chan_list[chan_idx].chan_number; + (scan_chan_list + chan_idx)->chan_number = channel; + + radio_type = + user_scan_in->chan_list[chan_idx].radio_type; + (scan_chan_list + chan_idx)->radio_type = radio_type; + + scan_type = user_scan_in->chan_list[chan_idx].scan_type; + + if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) + (scan_chan_list + + chan_idx)->chan_scan_mode_bitmap + |= (MWIFIEX_PASSIVE_SCAN | + MWIFIEX_HIDDEN_SSID_REPORT); + else + (scan_chan_list + + chan_idx)->chan_scan_mode_bitmap + &= ~MWIFIEX_PASSIVE_SCAN; + + if (*filtered_scan) + (scan_chan_list + + chan_idx)->chan_scan_mode_bitmap + |= MWIFIEX_DISABLE_CHAN_FILT; + + if (user_scan_in->chan_list[chan_idx].scan_time) { + scan_dur = (u16) user_scan_in-> + chan_list[chan_idx].scan_time; + } else { + if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) + scan_dur = adapter->passive_scan_time; + else if (*filtered_scan) + scan_dur = adapter->specific_scan_time; + else + scan_dur = adapter->active_scan_time; + } + + (scan_chan_list + chan_idx)->min_scan_time = + cpu_to_le16(scan_dur); + (scan_chan_list + chan_idx)->max_scan_time = + cpu_to_le16(scan_dur); + } + + /* Check if we are only scanning the current channel */ + if ((chan_idx == 1) && + (user_scan_in->chan_list[0].chan_number == + priv->curr_bss_params.bss_descriptor.channel)) { + *scan_current_only = true; + mwifiex_dbg(adapter, INFO, + "info: Scan: Scanning current channel only\n"); + } + chan_num = chan_idx; + } else { + mwifiex_dbg(adapter, INFO, + "info: Scan: Creating full region channel list\n"); + chan_num = mwifiex_scan_create_channel_list(priv, user_scan_in, + scan_chan_list, + *filtered_scan); + } + +} + +/* + * This function inspects the scan response buffer for pointers to + * expected TLVs. + * + * TLVs can be included at the end of the scan response BSS information. + * + * Data in the buffer is parsed pointers to TLVs that can potentially + * be passed back in the response. + */ +static void +mwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter, + struct mwifiex_ie_types_data *tlv, + u32 tlv_buf_size, u32 req_tlv_type, + struct mwifiex_ie_types_data **tlv_data) +{ + struct mwifiex_ie_types_data *current_tlv; + u32 tlv_buf_left; + u32 tlv_type; + u32 tlv_len; + + current_tlv = tlv; + tlv_buf_left = tlv_buf_size; + *tlv_data = NULL; + + mwifiex_dbg(adapter, INFO, + "info: SCAN_RESP: tlv_buf_size = %d\n", + tlv_buf_size); + + while (tlv_buf_left >= sizeof(struct mwifiex_ie_types_header)) { + + tlv_type = le16_to_cpu(current_tlv->header.type); + tlv_len = le16_to_cpu(current_tlv->header.len); + + if (sizeof(tlv->header) + tlv_len > tlv_buf_left) { + mwifiex_dbg(adapter, ERROR, + "SCAN_RESP: TLV buffer corrupt\n"); + break; + } + + if (req_tlv_type == tlv_type) { + switch (tlv_type) { + case TLV_TYPE_TSFTIMESTAMP: + mwifiex_dbg(adapter, INFO, + "info: SCAN_RESP: TSF\t" + "timestamp TLV, len = %d\n", + tlv_len); + *tlv_data = current_tlv; + break; + case TLV_TYPE_CHANNELBANDLIST: + mwifiex_dbg(adapter, INFO, + "info: SCAN_RESP: channel\t" + "band list TLV, len = %d\n", + tlv_len); + *tlv_data = current_tlv; + break; + default: + mwifiex_dbg(adapter, ERROR, + "SCAN_RESP: unhandled TLV = %d\n", + tlv_type); + /* Give up, this seems corrupted */ + return; + } + } + + if (*tlv_data) + break; + + + tlv_buf_left -= (sizeof(tlv->header) + tlv_len); + current_tlv = + (struct mwifiex_ie_types_data *) (current_tlv->data + + tlv_len); + + } /* while */ +} + +/* + * This function parses provided beacon buffer and updates + * respective fields in bss descriptor structure. + */ +int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, + struct mwifiex_bssdescriptor *bss_entry) +{ + int ret = 0; + u8 element_id; + struct ieee_types_fh_param_set *fh_param_set; + struct ieee_types_ds_param_set *ds_param_set; + struct ieee_types_cf_param_set *cf_param_set; + struct ieee_types_ibss_param_set *ibss_param_set; + u8 *current_ptr; + u8 *rate; + u8 element_len; + u16 total_ie_len; + u8 bytes_to_copy; + u8 rate_size; + u8 found_data_rate_ie; + u32 bytes_left; + struct ieee_types_vendor_specific *vendor_ie; + const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; + const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + + found_data_rate_ie = false; + rate_size = 0; + current_ptr = bss_entry->beacon_buf; + bytes_left = bss_entry->beacon_buf_size; + + /* Process variable IE */ + while (bytes_left >= 2) { + element_id = *current_ptr; + element_len = *(current_ptr + 1); + total_ie_len = element_len + sizeof(struct ieee_types_header); + + if (bytes_left < total_ie_len) { + mwifiex_dbg(adapter, ERROR, + "err: InterpretIE: in processing\t" + "IE, bytes left < IE length\n"); + return -1; + } + switch (element_id) { + case WLAN_EID_SSID: + bss_entry->ssid.ssid_len = element_len; + memcpy(bss_entry->ssid.ssid, (current_ptr + 2), + element_len); + mwifiex_dbg(adapter, INFO, + "info: InterpretIE: ssid: %-32s\n", + bss_entry->ssid.ssid); + break; + + case WLAN_EID_SUPP_RATES: + memcpy(bss_entry->data_rates, current_ptr + 2, + element_len); + memcpy(bss_entry->supported_rates, current_ptr + 2, + element_len); + rate_size = element_len; + found_data_rate_ie = true; + break; + + case WLAN_EID_FH_PARAMS: + fh_param_set = + (struct ieee_types_fh_param_set *) current_ptr; + memcpy(&bss_entry->phy_param_set.fh_param_set, + fh_param_set, + sizeof(struct ieee_types_fh_param_set)); + break; + + case WLAN_EID_DS_PARAMS: + ds_param_set = + (struct ieee_types_ds_param_set *) current_ptr; + + bss_entry->channel = ds_param_set->current_chan; + + memcpy(&bss_entry->phy_param_set.ds_param_set, + ds_param_set, + sizeof(struct ieee_types_ds_param_set)); + break; + + case WLAN_EID_CF_PARAMS: + cf_param_set = + (struct ieee_types_cf_param_set *) current_ptr; + memcpy(&bss_entry->ss_param_set.cf_param_set, + cf_param_set, + sizeof(struct ieee_types_cf_param_set)); + break; + + case WLAN_EID_IBSS_PARAMS: + ibss_param_set = + (struct ieee_types_ibss_param_set *) + current_ptr; + memcpy(&bss_entry->ss_param_set.ibss_param_set, + ibss_param_set, + sizeof(struct ieee_types_ibss_param_set)); + break; + + case WLAN_EID_ERP_INFO: + bss_entry->erp_flags = *(current_ptr + 2); + break; + + case WLAN_EID_PWR_CONSTRAINT: + bss_entry->local_constraint = *(current_ptr + 2); + bss_entry->sensed_11h = true; + break; + + case WLAN_EID_CHANNEL_SWITCH: + bss_entry->chan_sw_ie_present = true; + case WLAN_EID_PWR_CAPABILITY: + case WLAN_EID_TPC_REPORT: + case WLAN_EID_QUIET: + bss_entry->sensed_11h = true; + break; + + case WLAN_EID_EXT_SUPP_RATES: + /* + * Only process extended supported rate + * if data rate is already found. + * Data rate IE should come before + * extended supported rate IE + */ + if (found_data_rate_ie) { + if ((element_len + rate_size) > + MWIFIEX_SUPPORTED_RATES) + bytes_to_copy = + (MWIFIEX_SUPPORTED_RATES - + rate_size); + else + bytes_to_copy = element_len; + + rate = (u8 *) bss_entry->data_rates; + rate += rate_size; + memcpy(rate, current_ptr + 2, bytes_to_copy); + + rate = (u8 *) bss_entry->supported_rates; + rate += rate_size; + memcpy(rate, current_ptr + 2, bytes_to_copy); + } + break; + + case WLAN_EID_VENDOR_SPECIFIC: + vendor_ie = (struct ieee_types_vendor_specific *) + current_ptr; + + if (!memcmp + (vendor_ie->vend_hdr.oui, wpa_oui, + sizeof(wpa_oui))) { + bss_entry->bcn_wpa_ie = + (struct ieee_types_vendor_specific *) + current_ptr; + bss_entry->wpa_offset = (u16) + (current_ptr - bss_entry->beacon_buf); + } else if (!memcmp(vendor_ie->vend_hdr.oui, wmm_oui, + sizeof(wmm_oui))) { + if (total_ie_len == + sizeof(struct ieee_types_wmm_parameter) || + total_ie_len == + sizeof(struct ieee_types_wmm_info)) + /* + * Only accept and copy the WMM IE if + * it matches the size expected for the + * WMM Info IE or the WMM Parameter IE. + */ + memcpy((u8 *) &bss_entry->wmm_ie, + current_ptr, total_ie_len); + } + break; + case WLAN_EID_RSN: + bss_entry->bcn_rsn_ie = + (struct ieee_types_generic *) current_ptr; + bss_entry->rsn_offset = (u16) (current_ptr - + bss_entry->beacon_buf); + break; + case WLAN_EID_BSS_AC_ACCESS_DELAY: + bss_entry->bcn_wapi_ie = + (struct ieee_types_generic *) current_ptr; + bss_entry->wapi_offset = (u16) (current_ptr - + bss_entry->beacon_buf); + break; + case WLAN_EID_HT_CAPABILITY: + bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *) + (current_ptr + + sizeof(struct ieee_types_header)); + bss_entry->ht_cap_offset = (u16) (current_ptr + + sizeof(struct ieee_types_header) - + bss_entry->beacon_buf); + break; + case WLAN_EID_HT_OPERATION: + bss_entry->bcn_ht_oper = + (struct ieee80211_ht_operation *)(current_ptr + + sizeof(struct ieee_types_header)); + bss_entry->ht_info_offset = (u16) (current_ptr + + sizeof(struct ieee_types_header) - + bss_entry->beacon_buf); + break; + case WLAN_EID_VHT_CAPABILITY: + bss_entry->disable_11ac = false; + bss_entry->bcn_vht_cap = + (void *)(current_ptr + + sizeof(struct ieee_types_header)); + bss_entry->vht_cap_offset = + (u16)((u8 *)bss_entry->bcn_vht_cap - + bss_entry->beacon_buf); + break; + case WLAN_EID_VHT_OPERATION: + bss_entry->bcn_vht_oper = + (void *)(current_ptr + + sizeof(struct ieee_types_header)); + bss_entry->vht_info_offset = + (u16)((u8 *)bss_entry->bcn_vht_oper - + bss_entry->beacon_buf); + break; + case WLAN_EID_BSS_COEX_2040: + bss_entry->bcn_bss_co_2040 = current_ptr; + bss_entry->bss_co_2040_offset = + (u16) (current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_EXT_CAPABILITY: + bss_entry->bcn_ext_cap = current_ptr; + bss_entry->ext_cap_offset = + (u16) (current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_OPMODE_NOTIF: + bss_entry->oper_mode = (void *)current_ptr; + bss_entry->oper_mode_offset = + (u16)((u8 *)bss_entry->oper_mode - + bss_entry->beacon_buf); + break; + default: + break; + } + + current_ptr += element_len + 2; + + /* Need to account for IE ID and IE Len */ + bytes_left -= (element_len + 2); + + } /* while (bytes_left > 2) */ + return ret; +} + +/* + * This function converts radio type scan parameter to a band configuration + * to be used in join command. + */ +static u8 +mwifiex_radio_type_to_band(u8 radio_type) +{ + switch (radio_type) { + case HostCmd_SCAN_RADIO_TYPE_A: + return BAND_A; + case HostCmd_SCAN_RADIO_TYPE_BG: + default: + return BAND_G; + } +} + +/* + * This is an internal function used to start a scan based on an input + * configuration. + * + * This uses the input user scan configuration information when provided in + * order to send the appropriate scan commands to firmware to populate or + * update the internal driver scan table. + */ +int mwifiex_scan_networks(struct mwifiex_private *priv, + const struct mwifiex_user_scan_cfg *user_scan_in) +{ + int ret; + struct mwifiex_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node; + union mwifiex_scan_cmd_config_tlv *scan_cfg_out; + struct mwifiex_ie_types_chan_list_param_set *chan_list_out; + struct mwifiex_chan_scan_param_set *scan_chan_list; + u8 filtered_scan; + u8 scan_current_chan_only; + u8 max_chan_per_scan; + unsigned long flags; + + if (adapter->scan_processing) { + mwifiex_dbg(adapter, WARN, + "cmd: Scan already in process...\n"); + return -EBUSY; + } + + if (priv->scan_block) { + mwifiex_dbg(adapter, WARN, + "cmd: Scan is blocked during association...\n"); + return -EBUSY; + } + + if (adapter->surprise_removed || adapter->is_cmd_timedout) { + mwifiex_dbg(adapter, ERROR, + "Ignore scan. Card removed or firmware in bad state\n"); + return -EFAULT; + } + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = true; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv), + GFP_KERNEL); + if (!scan_cfg_out) { + ret = -ENOMEM; + goto done; + } + + scan_chan_list = kcalloc(MWIFIEX_USER_SCAN_CHAN_MAX, + sizeof(struct mwifiex_chan_scan_param_set), + GFP_KERNEL); + if (!scan_chan_list) { + kfree(scan_cfg_out); + ret = -ENOMEM; + goto done; + } + + mwifiex_config_scan(priv, user_scan_in, &scan_cfg_out->config, + &chan_list_out, scan_chan_list, &max_chan_per_scan, + &filtered_scan, &scan_current_chan_only); + + ret = mwifiex_scan_channel_list(priv, max_chan_per_scan, filtered_scan, + &scan_cfg_out->config, chan_list_out, + scan_chan_list); + + /* Get scan command from scan_pending_q and put to cmd_pending_q */ + if (!ret) { + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + if (!list_empty(&adapter->scan_pending_q)) { + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, + true); + queue_work(adapter->workqueue, &adapter->main_work); + + /* Perform internal scan synchronously */ + if (!priv->scan_request) { + mwifiex_dbg(adapter, INFO, + "wait internal scan\n"); + mwifiex_wait_queue_complete(adapter, cmd_node); + } + } else { + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + } + } + + kfree(scan_cfg_out); + kfree(scan_chan_list); +done: + if (ret) { + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + } + return ret; +} + +/* + * This function prepares a scan command to be sent to the firmware. + * + * This uses the scan command configuration sent to the command processing + * module in command preparation stage to configure a scan command structure + * to send to firmware. + * + * The fixed fields specifying the BSS type and BSSID filters as well as a + * variable number/length of TLVs are sent in the command to firmware. + * + * Preparation also includes - + * - Setting command ID, and proper size + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, + struct mwifiex_scan_cmd_config *scan_cfg) +{ + struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan; + + /* Set fixed field variables in scan command */ + scan_cmd->bss_mode = scan_cfg->bss_mode; + memcpy(scan_cmd->bssid, scan_cfg->specific_bssid, + sizeof(scan_cmd->bssid)); + memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16((u16) (sizeof(scan_cmd->bss_mode) + + sizeof(scan_cmd->bssid) + + scan_cfg->tlv_buf_len + S_DS_GEN)); + + return 0; +} + +/* + * This function checks compatibility of requested network with current + * driver settings. + */ +int mwifiex_check_network_compatibility(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + int ret = -1; + + if (!bss_desc) + return -1; + + if ((mwifiex_get_cfp(priv, (u8) bss_desc->bss_band, + (u16) bss_desc->channel, 0))) { + switch (priv->bss_mode) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + ret = mwifiex_is_network_compatible(priv, bss_desc, + priv->bss_mode); + if (ret) + mwifiex_dbg(priv->adapter, ERROR, + "Incompatible network settings\n"); + break; + default: + ret = 0; + } + } + + return ret; +} + +/* This function checks if SSID string contains all zeroes or length is zero */ +static bool mwifiex_is_hidden_ssid(struct cfg80211_ssid *ssid) +{ + int idx; + + for (idx = 0; idx < ssid->ssid_len; idx++) { + if (ssid->ssid[idx]) + return false; + } + + return true; +} + +/* This function checks if any hidden SSID found in passive scan channels + * and save those channels for specific SSID active scan + */ +static int mwifiex_save_hidden_ssid_channels(struct mwifiex_private *priv, + struct cfg80211_bss *bss) +{ + struct mwifiex_bssdescriptor *bss_desc; + int ret; + int chid; + + /* Allocate and fill new bss descriptor */ + bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); + if (ret) + goto done; + + if (mwifiex_is_hidden_ssid(&bss_desc->ssid)) { + mwifiex_dbg(priv->adapter, INFO, "found hidden SSID\n"); + for (chid = 0 ; chid < MWIFIEX_USER_SCAN_CHAN_MAX; chid++) { + if (priv->hidden_chan[chid].chan_number == + bss->channel->hw_value) + break; + + if (!priv->hidden_chan[chid].chan_number) { + priv->hidden_chan[chid].chan_number = + bss->channel->hw_value; + priv->hidden_chan[chid].radio_type = + bss->channel->band; + priv->hidden_chan[chid].scan_type = + MWIFIEX_SCAN_TYPE_ACTIVE; + break; + } + } + } + +done: + kfree(bss_desc); + return 0; +} + +static int mwifiex_update_curr_bss_params(struct mwifiex_private *priv, + struct cfg80211_bss *bss) +{ + struct mwifiex_bssdescriptor *bss_desc; + int ret; + unsigned long flags; + + /* Allocate and fill new bss descriptor */ + bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); + if (ret) + goto done; + + ret = mwifiex_check_network_compatibility(priv, bss_desc); + if (ret) + goto done; + + spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags); + /* Make a copy of current BSSID descriptor */ + memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc, + sizeof(priv->curr_bss_params.bss_descriptor)); + + /* The contents of beacon_ie will be copied to its own buffer + * in mwifiex_save_curr_bcn() + */ + mwifiex_save_curr_bcn(priv); + spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags); + +done: + /* beacon_ie buffer was allocated in function + * mwifiex_fill_new_bss_desc(). Free it now. + */ + kfree(bss_desc->beacon_buf); + kfree(bss_desc); + return 0; +} + +static int +mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, + u32 *bytes_left, u64 fw_tsf, u8 *radio_type, + bool ext_scan, s32 rssi_val) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_chan_freq_power *cfp; + struct cfg80211_bss *bss; + u8 bssid[ETH_ALEN]; + s32 rssi; + const u8 *ie_buf; + size_t ie_len; + u16 channel = 0; + u16 beacon_size = 0; + u32 curr_bcn_bytes; + u32 freq; + u16 beacon_period; + u16 cap_info_bitmap; + u8 *current_ptr; + u64 timestamp; + struct mwifiex_fixed_bcn_param *bcn_param; + struct mwifiex_bss_priv *bss_priv; + + if (*bytes_left >= sizeof(beacon_size)) { + /* Extract & convert beacon size from command buffer */ + beacon_size = le16_to_cpu(*(__le16 *)(*bss_info)); + *bytes_left -= sizeof(beacon_size); + *bss_info += sizeof(beacon_size); + } + + if (!beacon_size || beacon_size > *bytes_left) { + *bss_info += *bytes_left; + *bytes_left = 0; + return -EFAULT; + } + + /* Initialize the current working beacon pointer for this BSS + * iteration + */ + current_ptr = *bss_info; + + /* Advance the return beacon pointer past the current beacon */ + *bss_info += beacon_size; + *bytes_left -= beacon_size; + + curr_bcn_bytes = beacon_size; + + /* First 5 fields are bssid, RSSI(for legacy scan only), + * time stamp, beacon interval, and capability information + */ + if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) + + sizeof(struct mwifiex_fixed_bcn_param)) { + mwifiex_dbg(adapter, ERROR, + "InterpretIE: not enough bytes left\n"); + return -EFAULT; + } + + memcpy(bssid, current_ptr, ETH_ALEN); + current_ptr += ETH_ALEN; + curr_bcn_bytes -= ETH_ALEN; + + if (!ext_scan) { + rssi = (s32) *current_ptr; + rssi = (-rssi) * 100; /* Convert dBm to mBm */ + current_ptr += sizeof(u8); + curr_bcn_bytes -= sizeof(u8); + mwifiex_dbg(adapter, INFO, + "info: InterpretIE: RSSI=%d\n", rssi); + } else { + rssi = rssi_val; + } + + bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr; + current_ptr += sizeof(*bcn_param); + curr_bcn_bytes -= sizeof(*bcn_param); + + timestamp = le64_to_cpu(bcn_param->timestamp); + beacon_period = le16_to_cpu(bcn_param->beacon_period); + + cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap); + mwifiex_dbg(adapter, INFO, + "info: InterpretIE: capabilities=0x%X\n", + cap_info_bitmap); + + /* Rest of the current buffer are IE's */ + ie_buf = current_ptr; + ie_len = curr_bcn_bytes; + mwifiex_dbg(adapter, INFO, + "info: InterpretIE: IELength for this AP = %d\n", + curr_bcn_bytes); + + while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) { + u8 element_id, element_len; + + element_id = *current_ptr; + element_len = *(current_ptr + 1); + if (curr_bcn_bytes < element_len + + sizeof(struct ieee_types_header)) { + mwifiex_dbg(adapter, ERROR, + "%s: bytes left < IE length\n", __func__); + return -EFAULT; + } + if (element_id == WLAN_EID_DS_PARAMS) { + channel = *(current_ptr + + sizeof(struct ieee_types_header)); + break; + } + + current_ptr += element_len + sizeof(struct ieee_types_header); + curr_bcn_bytes -= element_len + + sizeof(struct ieee_types_header); + } + + if (channel) { + struct ieee80211_channel *chan; + u8 band; + + /* Skip entry if on csa closed channel */ + if (channel == priv->csa_chan) { + mwifiex_dbg(adapter, WARN, + "Dropping entry on csa closed channel\n"); + return 0; + } + + band = BAND_G; + if (radio_type) + band = mwifiex_radio_type_to_band(*radio_type & + (BIT(0) | BIT(1))); + + cfp = mwifiex_get_cfp(priv, band, channel, 0); + + freq = cfp ? cfp->freq : 0; + + chan = ieee80211_get_channel(priv->wdev.wiphy, freq); + + if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { + bss = cfg80211_inform_bss(priv->wdev.wiphy, + chan, CFG80211_BSS_FTYPE_UNKNOWN, + bssid, timestamp, + cap_info_bitmap, beacon_period, + ie_buf, ie_len, rssi, GFP_KERNEL); + if (bss) { + bss_priv = (struct mwifiex_bss_priv *)bss->priv; + bss_priv->band = band; + bss_priv->fw_tsf = fw_tsf; + if (priv->media_connected && + !memcmp(bssid, priv->curr_bss_params. + bss_descriptor.mac_address, + ETH_ALEN)) + mwifiex_update_curr_bss_params(priv, + bss); + cfg80211_put_bss(priv->wdev.wiphy, bss); + } + + if ((chan->flags & IEEE80211_CHAN_RADAR) || + (chan->flags & IEEE80211_CHAN_NO_IR)) { + mwifiex_dbg(adapter, INFO, + "radar or passive channel %d\n", + channel); + mwifiex_save_hidden_ssid_channels(priv, bss); + } + } + } else { + mwifiex_dbg(adapter, WARN, "missing BSS channel IE\n"); + } + + return 0; +} + +static void mwifiex_complete_scan(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + adapter->survey_idx = 0; + if (adapter->curr_cmd->wait_q_enabled) { + adapter->cmd_wait_q.status = 0; + if (!priv->scan_request) { + mwifiex_dbg(adapter, INFO, + "complete internal scan\n"); + mwifiex_complete_cmd(adapter, adapter->curr_cmd); + } + } +} + +/* This function checks if any hidden SSID found in passive scan channels + * and do specific SSID active scan for those channels + */ +static int +mwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv) +{ + int ret; + struct mwifiex_adapter *adapter = priv->adapter; + u8 id = 0; + struct mwifiex_user_scan_cfg *user_scan_cfg; + + if (adapter->active_scan_triggered || !priv->scan_request) { + adapter->active_scan_triggered = false; + return 0; + } + + if (!priv->hidden_chan[0].chan_number) { + mwifiex_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n"); + return 0; + } + user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); + + if (!user_scan_cfg) + return -ENOMEM; + + memset(user_scan_cfg, 0, sizeof(*user_scan_cfg)); + + for (id = 0; id < MWIFIEX_USER_SCAN_CHAN_MAX; id++) { + if (!priv->hidden_chan[id].chan_number) + break; + memcpy(&user_scan_cfg->chan_list[id], + &priv->hidden_chan[id], + sizeof(struct mwifiex_user_scan_chan)); + } + + adapter->active_scan_triggered = true; + user_scan_cfg->num_ssids = priv->scan_request->n_ssids; + user_scan_cfg->ssid_list = priv->scan_request->ssids; + + ret = mwifiex_scan_networks(priv, user_scan_cfg); + kfree(user_scan_cfg); + + memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan)); + + if (ret) { + dev_err(priv->adapter->dev, "scan failed: %d\n", ret); + return ret; + } + + return 0; +} +static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node, *tmp_node; + unsigned long flags; + + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + if (list_empty(&adapter->scan_pending_q)) { + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + mwifiex_active_scan_req_for_passive_chan(priv); + + if (!adapter->ext_scan) + mwifiex_complete_scan(priv); + + if (priv->scan_request) { + mwifiex_dbg(adapter, INFO, + "info: notifying scan done\n"); + cfg80211_scan_done(priv->scan_request, 0); + priv->scan_request = NULL; + } else { + priv->scan_aborting = false; + mwifiex_dbg(adapter, INFO, + "info: scan already aborted\n"); + } + } else if ((priv->scan_aborting && !priv->scan_request) || + priv->scan_block) { + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, list) { + list_del(&cmd_node->list); + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + if (!adapter->active_scan_triggered) { + if (priv->scan_request) { + mwifiex_dbg(adapter, INFO, + "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } else { + priv->scan_aborting = false; + mwifiex_dbg(adapter, INFO, + "info: scan already aborted\n"); + } + } + } else { + /* Get scan command from scan_pending_q and put to + * cmd_pending_q + */ + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + } + + return; +} + +/* + * This function handles the command response of scan. + * + * The response buffer for the scan command has the following + * memory layout: + * + * .-------------------------------------------------------------. + * | Header (4 * sizeof(t_u16)): Standard command response hdr | + * .-------------------------------------------------------------. + * | BufSize (t_u16) : sizeof the BSS Description data | + * .-------------------------------------------------------------. + * | NumOfSet (t_u8) : Number of BSS Descs returned | + * .-------------------------------------------------------------. + * | BSSDescription data (variable, size given in BufSize) | + * .-------------------------------------------------------------. + * | TLV data (variable, size calculated using Header->Size, | + * | BufSize and sizeof the fixed fields above) | + * .-------------------------------------------------------------. + */ +int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_scan_rsp *scan_rsp; + struct mwifiex_ie_types_data *tlv_data; + struct mwifiex_ie_types_tsf_timestamp *tsf_tlv; + u8 *bss_info; + u32 scan_resp_size; + u32 bytes_left; + u32 idx; + u32 tlv_buf_size; + struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv; + struct chan_band_param_set *chan_band; + u8 is_bgscan_resp; + __le64 fw_tsf = 0; + u8 *radio_type; + + is_bgscan_resp = (le16_to_cpu(resp->command) + == HostCmd_CMD_802_11_BG_SCAN_QUERY); + if (is_bgscan_resp) + scan_rsp = &resp->params.bg_scan_query_resp.scan_resp; + else + scan_rsp = &resp->params.scan_resp; + + + if (scan_rsp->number_of_sets > MWIFIEX_MAX_AP) { + mwifiex_dbg(adapter, ERROR, + "SCAN_RESP: too many AP returned (%d)\n", + scan_rsp->number_of_sets); + ret = -1; + goto check_next_scan; + } + + /* Check csa channel expiry before parsing scan response */ + mwifiex_11h_get_csa_closed_channel(priv); + + bytes_left = le16_to_cpu(scan_rsp->bss_descript_size); + mwifiex_dbg(adapter, INFO, + "info: SCAN_RESP: bss_descript_size %d\n", + bytes_left); + + scan_resp_size = le16_to_cpu(resp->size); + + mwifiex_dbg(adapter, INFO, + "info: SCAN_RESP: returned %d APs before parsing\n", + scan_rsp->number_of_sets); + + bss_info = scan_rsp->bss_desc_and_tlv_buffer; + + /* + * The size of the TLV buffer is equal to the entire command response + * size (scan_resp_size) minus the fixed fields (sizeof()'s), the + * BSS Descriptions (bss_descript_size as bytesLef) and the command + * response header (S_DS_GEN) + */ + tlv_buf_size = scan_resp_size - (bytes_left + + sizeof(scan_rsp->bss_descript_size) + + sizeof(scan_rsp->number_of_sets) + + S_DS_GEN); + + tlv_data = (struct mwifiex_ie_types_data *) (scan_rsp-> + bss_desc_and_tlv_buffer + + bytes_left); + + /* Search the TLV buffer space in the scan response for any valid + TLVs */ + mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, + TLV_TYPE_TSFTIMESTAMP, + (struct mwifiex_ie_types_data **) + &tsf_tlv); + + /* Search the TLV buffer space in the scan response for any valid + TLVs */ + mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, + TLV_TYPE_CHANNELBANDLIST, + (struct mwifiex_ie_types_data **) + &chan_band_tlv); + + for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { + /* + * If the TSF TLV was appended to the scan results, save this + * entry's TSF value in the fw_tsf field. It is the firmware's + * TSF value at the time the beacon or probe response was + * received. + */ + if (tsf_tlv) + memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE], + sizeof(fw_tsf)); + + if (chan_band_tlv) { + chan_band = &chan_band_tlv->chan_band_param[idx]; + radio_type = &chan_band->radio_type; + } else { + radio_type = NULL; + } + + ret = mwifiex_parse_single_response_buf(priv, &bss_info, + &bytes_left, + le64_to_cpu(fw_tsf), + radio_type, false, 0); + if (ret) + goto check_next_scan; + } + +check_next_scan: + mwifiex_check_next_scan_command(priv); + return ret; +} + +/* + * This function prepares an extended scan command to be sent to the firmware + * + * This uses the scan command configuration sent to the command processing + * module in command preparation stage to configure a extended scan command + * structure to send to firmware. + */ +int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan; + struct mwifiex_scan_cmd_config *scan_cfg = data_buf; + + memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved) + + scan_cfg->tlv_buf_len + S_DS_GEN)); + + return 0; +} + +static void +mwifiex_update_chan_statistics(struct mwifiex_private *priv, + struct mwifiex_ietypes_chanstats *tlv_stat) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u8 i, num_chan; + struct mwifiex_fw_chan_stats *fw_chan_stats; + struct mwifiex_chan_stats chan_stats; + + fw_chan_stats = (void *)((u8 *)tlv_stat + + sizeof(struct mwifiex_ie_types_header)); + num_chan = le16_to_cpu(tlv_stat->header.len) / + sizeof(struct mwifiex_chan_stats); + + for (i = 0 ; i < num_chan; i++) { + chan_stats.chan_num = fw_chan_stats->chan_num; + chan_stats.bandcfg = fw_chan_stats->bandcfg; + chan_stats.flags = fw_chan_stats->flags; + chan_stats.noise = fw_chan_stats->noise; + chan_stats.total_bss = le16_to_cpu(fw_chan_stats->total_bss); + chan_stats.cca_scan_dur = + le16_to_cpu(fw_chan_stats->cca_scan_dur); + chan_stats.cca_busy_dur = + le16_to_cpu(fw_chan_stats->cca_busy_dur); + mwifiex_dbg(adapter, INFO, + "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n", + chan_stats.chan_num, + chan_stats.noise, + chan_stats.total_bss, + chan_stats.cca_scan_dur, + chan_stats.cca_busy_dur); + memcpy(&adapter->chan_stats[adapter->survey_idx++], &chan_stats, + sizeof(struct mwifiex_chan_stats)); + fw_chan_stats++; + } +} + +/* This function handles the command response of extended scan */ +int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_scan_ext *ext_scan_resp; + struct mwifiex_ie_types_header *tlv; + struct mwifiex_ietypes_chanstats *tlv_stat; + u16 buf_left, type, len; + + struct host_cmd_ds_command *cmd_ptr; + struct cmd_ctrl_node *cmd_node; + unsigned long cmd_flags, scan_flags; + bool complete_scan = false; + + mwifiex_dbg(adapter, INFO, "info: EXT scan returns successfully\n"); + + ext_scan_resp = &resp->params.ext_scan; + + tlv = (void *)ext_scan_resp->tlv_buffer; + buf_left = le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN + - 1); + + while (buf_left >= sizeof(struct mwifiex_ie_types_header)) { + type = le16_to_cpu(tlv->type); + len = le16_to_cpu(tlv->len); + + if (buf_left < (sizeof(struct mwifiex_ie_types_header) + len)) { + mwifiex_dbg(adapter, ERROR, + "error processing scan response TLVs"); + break; + } + + switch (type) { + case TLV_TYPE_CHANNEL_STATS: + tlv_stat = (void *)tlv; + mwifiex_update_chan_statistics(priv, tlv_stat); + break; + default: + break; + } + + buf_left -= len + sizeof(struct mwifiex_ie_types_header); + tlv = (void *)((u8 *)tlv + len + + sizeof(struct mwifiex_ie_types_header)); + } + + spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_flags); + spin_lock_irqsave(&adapter->scan_pending_q_lock, scan_flags); + if (list_empty(&adapter->scan_pending_q)) { + complete_scan = true; + list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) { + cmd_ptr = (void *)cmd_node->cmd_skb->data; + if (le16_to_cpu(cmd_ptr->command) == + HostCmd_CMD_802_11_SCAN_EXT) { + mwifiex_dbg(adapter, INFO, + "Scan pending in command pending list"); + complete_scan = false; + break; + } + } + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, scan_flags); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, cmd_flags); + + if (complete_scan) + mwifiex_complete_scan(priv); + + return 0; +} + +/* This function This function handles the event extended scan report. It + * parses extended scan results and informs to cfg80211 stack. + */ +int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, + void *buf) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + u8 *bss_info; + u32 bytes_left, bytes_left_for_tlv, idx; + u16 type, len; + struct mwifiex_ie_types_data *tlv; + struct mwifiex_ie_types_bss_scan_rsp *scan_rsp_tlv; + struct mwifiex_ie_types_bss_scan_info *scan_info_tlv; + u8 *radio_type; + u64 fw_tsf = 0; + s32 rssi = 0; + struct mwifiex_event_scan_result *event_scan = buf; + u8 num_of_set = event_scan->num_of_set; + u8 *scan_resp = buf + sizeof(struct mwifiex_event_scan_result); + u16 scan_resp_size = le16_to_cpu(event_scan->buf_size); + + if (num_of_set > MWIFIEX_MAX_AP) { + mwifiex_dbg(adapter, ERROR, + "EXT_SCAN: Invalid number of AP returned (%d)!!\n", + num_of_set); + ret = -1; + goto check_next_scan; + } + + bytes_left = scan_resp_size; + mwifiex_dbg(adapter, INFO, + "EXT_SCAN: size %d, returned %d APs...", + scan_resp_size, num_of_set); + mwifiex_dbg_dump(adapter, CMD_D, "EXT_SCAN buffer:", buf, + scan_resp_size + + sizeof(struct mwifiex_event_scan_result)); + + tlv = (struct mwifiex_ie_types_data *)scan_resp; + + for (idx = 0; idx < num_of_set && bytes_left; idx++) { + type = le16_to_cpu(tlv->header.type); + len = le16_to_cpu(tlv->header.len); + if (bytes_left < sizeof(struct mwifiex_ie_types_header) + len) { + mwifiex_dbg(adapter, ERROR, + "EXT_SCAN: Error bytes left < TLV length\n"); + break; + } + scan_rsp_tlv = NULL; + scan_info_tlv = NULL; + bytes_left_for_tlv = bytes_left; + + /* BSS response TLV with beacon or probe response buffer + * at the initial position of each descriptor + */ + if (type != TLV_TYPE_BSS_SCAN_RSP) + break; + + bss_info = (u8 *)tlv; + scan_rsp_tlv = (struct mwifiex_ie_types_bss_scan_rsp *)tlv; + tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); + bytes_left_for_tlv -= + (len + sizeof(struct mwifiex_ie_types_header)); + + while (bytes_left_for_tlv >= + sizeof(struct mwifiex_ie_types_header) && + le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) { + type = le16_to_cpu(tlv->header.type); + len = le16_to_cpu(tlv->header.len); + if (bytes_left_for_tlv < + sizeof(struct mwifiex_ie_types_header) + len) { + mwifiex_dbg(adapter, ERROR, + "EXT_SCAN: Error in processing TLV,\t" + "bytes left < TLV length\n"); + scan_rsp_tlv = NULL; + bytes_left_for_tlv = 0; + continue; + } + switch (type) { + case TLV_TYPE_BSS_SCAN_INFO: + scan_info_tlv = + (struct mwifiex_ie_types_bss_scan_info *)tlv; + if (len != + sizeof(struct mwifiex_ie_types_bss_scan_info) - + sizeof(struct mwifiex_ie_types_header)) { + bytes_left_for_tlv = 0; + continue; + } + break; + default: + break; + } + tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); + bytes_left -= + (len + sizeof(struct mwifiex_ie_types_header)); + bytes_left_for_tlv -= + (len + sizeof(struct mwifiex_ie_types_header)); + } + + if (!scan_rsp_tlv) + break; + + /* Advance pointer to the beacon buffer length and + * update the bytes count so that the function + * wlan_interpret_bss_desc_with_ie() can handle the + * scan buffer withut any change + */ + bss_info += sizeof(u16); + bytes_left -= sizeof(u16); + + if (scan_info_tlv) { + rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi)); + rssi *= 100; /* Convert dBm to mBm */ + mwifiex_dbg(adapter, INFO, + "info: InterpretIE: RSSI=%d\n", rssi); + fw_tsf = le64_to_cpu(scan_info_tlv->tsf); + radio_type = &scan_info_tlv->radio_type; + } else { + radio_type = NULL; + } + ret = mwifiex_parse_single_response_buf(priv, &bss_info, + &bytes_left, fw_tsf, + radio_type, true, rssi); + if (ret) + goto check_next_scan; + } + +check_next_scan: + if (!event_scan->more_event) + mwifiex_check_next_scan_command(priv); + + return ret; +} + +/* + * This function prepares command for background scan query. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting background scan flush parameter + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd) +{ + struct host_cmd_ds_802_11_bg_scan_query *bg_query = + &cmd->params.bg_scan_query; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query) + + S_DS_GEN); + + bg_query->flush = 1; + + return 0; +} + +/* + * This function inserts scan command node to the scan pending queue. + */ +void +mwifiex_queue_scan_cmd(struct mwifiex_private *priv, + struct cmd_ctrl_node *cmd_node) +{ + struct mwifiex_adapter *adapter = priv->adapter; + unsigned long flags; + + cmd_node->wait_q_enabled = true; + cmd_node->condition = &adapter->scan_wait_q_woken; + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_add_tail(&cmd_node->list, &adapter->scan_pending_q); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); +} + +/* + * This function sends a scan command for all available channels to the + * firmware, filtered on a specific SSID. + */ +static int mwifiex_scan_specific_ssid(struct mwifiex_private *priv, + struct cfg80211_ssid *req_ssid) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + struct mwifiex_user_scan_cfg *scan_cfg; + + if (adapter->scan_processing) { + mwifiex_dbg(adapter, WARN, + "cmd: Scan already in process...\n"); + return -EBUSY; + } + + if (priv->scan_block) { + mwifiex_dbg(adapter, WARN, + "cmd: Scan is blocked during association...\n"); + return -EBUSY; + } + + scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL); + if (!scan_cfg) + return -ENOMEM; + + scan_cfg->ssid_list = req_ssid; + scan_cfg->num_ssids = 1; + + ret = mwifiex_scan_networks(priv, scan_cfg); + + kfree(scan_cfg); + return ret; +} + +/* + * Sends IOCTL request to start a scan. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + * + * Scan command can be issued for both normal scan and specific SSID + * scan, depending upon whether an SSID is provided or not. + */ +int mwifiex_request_scan(struct mwifiex_private *priv, + struct cfg80211_ssid *req_ssid) +{ + int ret; + + if (down_interruptible(&priv->async_sem)) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: acquire semaphore fail\n", + __func__); + return -1; + } + + priv->adapter->scan_wait_q_woken = false; + + if (req_ssid && req_ssid->ssid_len != 0) + /* Specific SSID scan */ + ret = mwifiex_scan_specific_ssid(priv, req_ssid); + else + /* Normal scan */ + ret = mwifiex_scan_networks(priv, NULL); + + up(&priv->async_sem); + + return ret; +} + +/* + * This function appends the vendor specific IE TLV to a buffer. + */ +int +mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, + u16 vsie_mask, u8 **buffer) +{ + int id, ret_len = 0; + struct mwifiex_ie_types_vendor_param_set *vs_param_set; + + if (!buffer) + return 0; + if (!(*buffer)) + return 0; + + /* + * Traverse through the saved vendor specific IE array and append + * the selected(scan/assoc/adhoc) IE as TLV to the command + */ + for (id = 0; id < MWIFIEX_MAX_VSIE_NUM; id++) { + if (priv->vs_ie[id].mask & vsie_mask) { + vs_param_set = + (struct mwifiex_ie_types_vendor_param_set *) + *buffer; + vs_param_set->header.type = + cpu_to_le16(TLV_TYPE_PASSTHROUGH); + vs_param_set->header.len = + cpu_to_le16((((u16) priv->vs_ie[id].ie[1]) + & 0x00FF) + 2); + memcpy(vs_param_set->ie, priv->vs_ie[id].ie, + le16_to_cpu(vs_param_set->header.len)); + *buffer += le16_to_cpu(vs_param_set->header.len) + + sizeof(struct mwifiex_ie_types_header); + ret_len += le16_to_cpu(vs_param_set->header.len) + + sizeof(struct mwifiex_ie_types_header); + } + } + return ret_len; +} + +/* + * This function saves a beacon buffer of the current BSS descriptor. + * + * The current beacon buffer is saved so that it can be restored in the + * following cases that makes the beacon buffer not to contain the current + * ssid's beacon buffer. + * - The current ssid was not found somehow in the last scan. + * - The current ssid was the last entry of the scan table and overloaded. + */ +void +mwifiex_save_curr_bcn(struct mwifiex_private *priv) +{ + struct mwifiex_bssdescriptor *curr_bss = + &priv->curr_bss_params.bss_descriptor; + + if (!curr_bss->beacon_buf_size) + return; + + /* allocate beacon buffer at 1st time; or if it's size has changed */ + if (!priv->curr_bcn_buf || + priv->curr_bcn_size != curr_bss->beacon_buf_size) { + priv->curr_bcn_size = curr_bss->beacon_buf_size; + + kfree(priv->curr_bcn_buf); + priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size, + GFP_ATOMIC); + if (!priv->curr_bcn_buf) + return; + } + + memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf, + curr_bss->beacon_buf_size); + mwifiex_dbg(priv->adapter, INFO, + "info: current beacon saved %d\n", + priv->curr_bcn_size); + + curr_bss->beacon_buf = priv->curr_bcn_buf; + + /* adjust the pointers in the current BSS descriptor */ + if (curr_bss->bcn_wpa_ie) + curr_bss->bcn_wpa_ie = + (struct ieee_types_vendor_specific *) + (curr_bss->beacon_buf + + curr_bss->wpa_offset); + + if (curr_bss->bcn_rsn_ie) + curr_bss->bcn_rsn_ie = (struct ieee_types_generic *) + (curr_bss->beacon_buf + + curr_bss->rsn_offset); + + if (curr_bss->bcn_ht_cap) + curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *) + (curr_bss->beacon_buf + + curr_bss->ht_cap_offset); + + if (curr_bss->bcn_ht_oper) + curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *) + (curr_bss->beacon_buf + + curr_bss->ht_info_offset); + + if (curr_bss->bcn_vht_cap) + curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf + + curr_bss->vht_cap_offset); + + if (curr_bss->bcn_vht_oper) + curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf + + curr_bss->vht_info_offset); + + if (curr_bss->bcn_bss_co_2040) + curr_bss->bcn_bss_co_2040 = + (curr_bss->beacon_buf + curr_bss->bss_co_2040_offset); + + if (curr_bss->bcn_ext_cap) + curr_bss->bcn_ext_cap = curr_bss->beacon_buf + + curr_bss->ext_cap_offset; + + if (curr_bss->oper_mode) + curr_bss->oper_mode = (void *)(curr_bss->beacon_buf + + curr_bss->oper_mode_offset); +} + +/* + * This function frees the current BSS descriptor beacon buffer. + */ +void +mwifiex_free_curr_bcn(struct mwifiex_private *priv) +{ + kfree(priv->curr_bcn_buf); + priv->curr_bcn_buf = NULL; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c new file mode 100644 index 000000000000..78a8474e1a3d --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -0,0 +1,2684 @@ +/* + * Marvell Wireless LAN device driver: SDIO specific handling + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "sdio.h" + + +#define SDIO_VERSION "1.0" + +/* The mwifiex_sdio_remove() callback function is called when + * user removes this module from kernel space or ejects + * the card from the slot. The driver handles these 2 cases + * differently. + * If the user is removing the module, the few commands (FUNC_SHUTDOWN, + * HS_CANCEL etc.) are sent to the firmware. + * If the card is removed, there is no need to send these command. + * + * The variable 'user_rmmod' is used to distinguish these two + * scenarios. This flag is initialized as FALSE in case the card + * is removed, and will be set to TRUE for module removal when + * module_exit function is called. + */ +static u8 user_rmmod; + +static struct mwifiex_if_ops sdio_ops; +static unsigned long iface_work_flags; + +static struct semaphore add_remove_card_sem; + +static struct memory_type_mapping generic_mem_type_map[] = { + {"DUMP", NULL, 0, 0xDD}, +}; + +static struct memory_type_mapping mem_type_mapping_tbl[] = { + {"ITCM", NULL, 0, 0xF0}, + {"DTCM", NULL, 0, 0xF1}, + {"SQRAM", NULL, 0, 0xF2}, + {"APU", NULL, 0, 0xF3}, + {"CIU", NULL, 0, 0xF4}, + {"ICU", NULL, 0, 0xF5}, + {"MAC", NULL, 0, 0xF6}, + {"EXT7", NULL, 0, 0xF7}, + {"EXT8", NULL, 0, 0xF8}, + {"EXT9", NULL, 0, 0xF9}, + {"EXT10", NULL, 0, 0xFA}, + {"EXT11", NULL, 0, 0xFB}, + {"EXT12", NULL, 0, 0xFC}, + {"EXT13", NULL, 0, 0xFD}, + {"EXTLAST", NULL, 0, 0xFE}, +}; + +/* + * SDIO probe. + * + * This function probes an mwifiex device and registers it. It allocates + * the card structure, enables SDIO function number and initiates the + * device registration and initialization procedure by adding a logical + * interface. + */ +static int +mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret; + struct sdio_mmc_card *card = NULL; + + pr_debug("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", + func->vendor, func->device, func->class, func->num); + + card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->func = func; + card->device_id = id; + + func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; + + if (id->driver_data) { + struct mwifiex_sdio_device *data = (void *)id->driver_data; + + card->firmware = data->firmware; + card->reg = data->reg; + card->max_ports = data->max_ports; + card->mp_agg_pkt_limit = data->mp_agg_pkt_limit; + card->supports_sdio_new_mode = data->supports_sdio_new_mode; + card->has_control_mask = data->has_control_mask; + card->tx_buf_size = data->tx_buf_size; + card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size; + card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size; + card->can_dump_fw = data->can_dump_fw; + card->fw_dump_enh = data->fw_dump_enh; + card->can_auto_tdls = data->can_auto_tdls; + card->can_ext_scan = data->can_ext_scan; + } + + sdio_claim_host(func); + ret = sdio_enable_func(func); + sdio_release_host(func); + + if (ret) { + pr_err("%s: failed to enable function\n", __func__); + kfree(card); + return -EIO; + } + + if (mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops, + MWIFIEX_SDIO)) { + pr_err("%s: add card failed\n", __func__); + kfree(card); + sdio_claim_host(func); + ret = sdio_disable_func(func); + sdio_release_host(func); + ret = -1; + } + + return ret; +} + +/* + * SDIO resume. + * + * Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not resumed, this function turns on the traffic and + * sends a host sleep cancel request to the firmware. + */ +static int mwifiex_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sdio_mmc_card *card; + struct mwifiex_adapter *adapter; + mmc_pm_flag_t pm_flag = 0; + + if (func) { + pm_flag = sdio_get_host_pm_caps(func); + card = sdio_get_drvdata(func); + if (!card || !card->adapter) { + pr_err("resume: invalid card or adapter\n"); + return 0; + } + } else { + pr_err("resume: sdio_func is not specified\n"); + return 0; + } + + adapter = card->adapter; + + if (!adapter->is_suspended) { + mwifiex_dbg(adapter, WARN, + "device already resumed\n"); + return 0; + } + + adapter->is_suspended = false; + + /* Disable Host Sleep */ + mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), + MWIFIEX_ASYNC_CMD); + + return 0; +} + +/* + * SDIO remove. + * + * This function removes the interface and frees up the card structure. + */ +static void +mwifiex_sdio_remove(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + struct mwifiex_adapter *adapter; + struct mwifiex_private *priv; + + card = sdio_get_drvdata(func); + if (!card) + return; + + adapter = card->adapter; + if (!adapter || !adapter->priv_num) + return; + + mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num); + + if (user_rmmod) { + if (adapter->is_suspended) + mwifiex_sdio_resume(adapter->dev); + + mwifiex_deauthenticate_all(adapter); + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + mwifiex_disable_auto_ds(priv); + mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); + } + + mwifiex_remove_card(card->adapter, &add_remove_card_sem); +} + +/* + * SDIO suspend. + * + * Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not suspended, this function allocates and sends a host + * sleep activate request to the firmware and turns off the traffic. + */ +static int mwifiex_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sdio_mmc_card *card; + struct mwifiex_adapter *adapter; + mmc_pm_flag_t pm_flag = 0; + int ret = 0; + + if (func) { + pm_flag = sdio_get_host_pm_caps(func); + pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", + sdio_func_id(func), pm_flag); + if (!(pm_flag & MMC_PM_KEEP_POWER)) { + pr_err("%s: cannot remain alive while host is" + " suspended\n", sdio_func_id(func)); + return -ENOSYS; + } + + card = sdio_get_drvdata(func); + if (!card || !card->adapter) { + pr_err("suspend: invalid card or adapter\n"); + return 0; + } + } else { + pr_err("suspend: sdio_func is not specified\n"); + return 0; + } + + adapter = card->adapter; + + /* Enable the Host Sleep */ + if (!mwifiex_enable_hs(adapter)) { + mwifiex_dbg(adapter, ERROR, + "cmd: failed to suspend\n"); + adapter->hs_enabling = false; + return -EFAULT; + } + + mwifiex_dbg(adapter, INFO, + "cmd: suspend with MMC_PM_KEEP_POWER\n"); + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + + /* Indicate device suspended */ + adapter->is_suspended = true; + adapter->hs_enabling = false; + + return ret; +} + +/* Device ID for SD8786 */ +#define SDIO_DEVICE_ID_MARVELL_8786 (0x9116) +/* Device ID for SD8787 */ +#define SDIO_DEVICE_ID_MARVELL_8787 (0x9119) +/* Device ID for SD8797 */ +#define SDIO_DEVICE_ID_MARVELL_8797 (0x9129) +/* Device ID for SD8897 */ +#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d) +/* Device ID for SD8887 */ +#define SDIO_DEVICE_ID_MARVELL_8887 (0x9135) +/* Device ID for SD8801 */ +#define SDIO_DEVICE_ID_MARVELL_8801 (0x9139) +/* Device ID for SD8997 */ +#define SDIO_DEVICE_ID_MARVELL_8997 (0x9141) + + +/* WLAN IDs */ +static const struct sdio_device_id mwifiex_ids[] = { + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786), + .driver_data = (unsigned long) &mwifiex_sdio_sd8786}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787), + .driver_data = (unsigned long) &mwifiex_sdio_sd8787}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797), + .driver_data = (unsigned long) &mwifiex_sdio_sd8797}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897), + .driver_data = (unsigned long) &mwifiex_sdio_sd8897}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887), + .driver_data = (unsigned long)&mwifiex_sdio_sd8887}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801), + .driver_data = (unsigned long)&mwifiex_sdio_sd8801}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997), + .driver_data = (unsigned long)&mwifiex_sdio_sd8997}, + {}, +}; + +MODULE_DEVICE_TABLE(sdio, mwifiex_ids); + +static const struct dev_pm_ops mwifiex_sdio_pm_ops = { + .suspend = mwifiex_sdio_suspend, + .resume = mwifiex_sdio_resume, +}; + +static struct sdio_driver mwifiex_sdio = { + .name = "mwifiex_sdio", + .id_table = mwifiex_ids, + .probe = mwifiex_sdio_probe, + .remove = mwifiex_sdio_remove, + .drv = { + .owner = THIS_MODULE, + .pm = &mwifiex_sdio_pm_ops, + } +}; + +/* Write data into SDIO card register. Caller claims SDIO device. */ +static int +mwifiex_write_reg_locked(struct sdio_func *func, u32 reg, u8 data) +{ + int ret = -1; + sdio_writeb(func, data, reg, &ret); + return ret; +} + +/* + * This function writes data into SDIO card register. + */ +static int +mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + + sdio_claim_host(card->func); + ret = mwifiex_write_reg_locked(card->func, reg, data); + sdio_release_host(card->func); + + return ret; +} + +/* + * This function reads data from SDIO card register. + */ +static int +mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data) +{ + struct sdio_mmc_card *card = adapter->card; + int ret = -1; + u8 val; + + sdio_claim_host(card->func); + val = sdio_readb(card->func, reg, &ret); + sdio_release_host(card->func); + + *data = val; + + return ret; +} + +/* + * This function writes multiple data into SDIO card memory. + * + * This does not work in suspended mode. + */ +static int +mwifiex_write_data_sync(struct mwifiex_adapter *adapter, + u8 *buffer, u32 pkt_len, u32 port) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + u8 blk_mode = + (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; + u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; + u32 blk_cnt = + (blk_mode == + BLOCK_MODE) ? (pkt_len / + MWIFIEX_SDIO_BLOCK_SIZE) : pkt_len; + u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); + + if (adapter->is_suspended) { + mwifiex_dbg(adapter, ERROR, + "%s: not allowed while suspended\n", __func__); + return -1; + } + + sdio_claim_host(card->func); + + ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size); + + sdio_release_host(card->func); + + return ret; +} + +/* + * This function reads multiple data from SDIO card memory. + */ +static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, + u32 len, u32 port, u8 claim) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE + : BLOCK_MODE; + u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; + u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (len / MWIFIEX_SDIO_BLOCK_SIZE) + : len; + u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); + + if (claim) + sdio_claim_host(card->func); + + ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size); + + if (claim) + sdio_release_host(card->func); + + return ret; +} + +/* + * This function wakes up the card. + * + * A host power up command is written to the card configuration + * register to wake up the card. + */ +static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) +{ + mwifiex_dbg(adapter, EVENT, + "event: wakeup device...\n"); + + return mwifiex_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP); +} + +/* + * This function is called after the card has woken up. + * + * The card configuration register is reset. + */ +static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) +{ + mwifiex_dbg(adapter, EVENT, + "cmd: wakeup device completed\n"); + + return mwifiex_write_reg(adapter, CONFIGURATION_REG, 0); +} + +/* + * This function is used to initialize IO ports for the + * chipsets supporting SDIO new mode eg SD8897. + */ +static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter) +{ + u8 reg; + struct sdio_mmc_card *card = adapter->card; + + adapter->ioport = MEM_PORT; + + /* enable sdio new mode */ + if (mwifiex_read_reg(adapter, card->reg->card_cfg_2_1_reg, ®)) + return -1; + if (mwifiex_write_reg(adapter, card->reg->card_cfg_2_1_reg, + reg | CMD53_NEW_MODE)) + return -1; + + /* Configure cmd port and enable reading rx length from the register */ + if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_0, ®)) + return -1; + if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_0, + reg | CMD_PORT_RD_LEN_EN)) + return -1; + + /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is + * completed + */ + if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_1, ®)) + return -1; + if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_1, + reg | CMD_PORT_AUTO_EN)) + return -1; + + return 0; +} + +/* This function initializes the IO ports. + * + * The following operations are performed - + * - Read the IO ports (0, 1 and 2) + * - Set host interrupt Reset-To-Read to clear + * - Set auto re-enable interrupt + */ +static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) +{ + u8 reg; + struct sdio_mmc_card *card = adapter->card; + + adapter->ioport = 0; + + if (card->supports_sdio_new_mode) { + if (mwifiex_init_sdio_new_mode(adapter)) + return -1; + goto cont; + } + + /* Read the IO port */ + if (!mwifiex_read_reg(adapter, card->reg->io_port_0_reg, ®)) + adapter->ioport |= (reg & 0xff); + else + return -1; + + if (!mwifiex_read_reg(adapter, card->reg->io_port_1_reg, ®)) + adapter->ioport |= ((reg & 0xff) << 8); + else + return -1; + + if (!mwifiex_read_reg(adapter, card->reg->io_port_2_reg, ®)) + adapter->ioport |= ((reg & 0xff) << 16); + else + return -1; +cont: + mwifiex_dbg(adapter, INFO, + "info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); + + /* Set Host interrupt reset to read to clear */ + if (!mwifiex_read_reg(adapter, card->reg->host_int_rsr_reg, ®)) + mwifiex_write_reg(adapter, card->reg->host_int_rsr_reg, + reg | card->reg->sdio_int_mask); + else + return -1; + + /* Dnld/Upld ready set to auto reset */ + if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, ®)) + mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg, + reg | AUTO_RE_ENABLE_INT); + else + return -1; + + return 0; +} + +/* + * This function sends data to the card. + */ +static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, + u8 *payload, u32 pkt_len, u32 port) +{ + u32 i = 0; + int ret; + + do { + ret = mwifiex_write_data_sync(adapter, payload, pkt_len, port); + if (ret) { + i++; + mwifiex_dbg(adapter, ERROR, + "host_to_card, write iomem\t" + "(%d) failed: %d\n", i, ret); + if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) + mwifiex_dbg(adapter, ERROR, + "write CFG reg failed\n"); + + ret = -1; + if (i > MAX_WRITE_IOMEM_RETRY) + return ret; + } + } while (ret == -1); + + return ret; +} + +/* + * This function gets the read port. + * + * If control port bit is set in MP read bitmap, the control port + * is returned, otherwise the current read port is returned and + * the value is increased (provided it does not reach the maximum + * limit, in which case it is reset to 1) + */ +static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + u32 rd_bitmap = card->mp_rd_bitmap; + + mwifiex_dbg(adapter, DATA, + "data: mp_rd_bitmap=0x%08x\n", rd_bitmap); + + if (card->supports_sdio_new_mode) { + if (!(rd_bitmap & reg->data_port_mask)) + return -1; + } else { + if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask))) + return -1; + } + + if ((card->has_control_mask) && + (card->mp_rd_bitmap & CTRL_PORT_MASK)) { + card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK); + *port = CTRL_PORT; + mwifiex_dbg(adapter, DATA, + "data: port=%d mp_rd_bitmap=0x%08x\n", + *port, card->mp_rd_bitmap); + return 0; + } + + if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port))) + return -1; + + /* We are now handling the SDIO data ports */ + card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port)); + *port = card->curr_rd_port; + + if (++card->curr_rd_port == card->max_ports) + card->curr_rd_port = reg->start_rd_port; + + mwifiex_dbg(adapter, DATA, + "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", + *port, rd_bitmap, card->mp_rd_bitmap); + + return 0; +} + +/* + * This function gets the write port for data. + * + * The current write port is returned if available and the value is + * increased (provided it does not reach the maximum limit, in which + * case it is reset to 1) + */ +static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + u32 wr_bitmap = card->mp_wr_bitmap; + + mwifiex_dbg(adapter, DATA, + "data: mp_wr_bitmap=0x%08x\n", wr_bitmap); + + if (!(wr_bitmap & card->mp_data_port_mask)) { + adapter->data_sent = true; + return -EBUSY; + } + + if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { + card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port)); + *port = card->curr_wr_port; + if (++card->curr_wr_port == card->mp_end_port) + card->curr_wr_port = reg->start_wr_port; + } else { + adapter->data_sent = true; + return -EBUSY; + } + + if ((card->has_control_mask) && (*port == CTRL_PORT)) { + mwifiex_dbg(adapter, ERROR, + "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", + *port, card->curr_wr_port, wr_bitmap, + card->mp_wr_bitmap); + return -1; + } + + mwifiex_dbg(adapter, DATA, + "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", + *port, wr_bitmap, card->mp_wr_bitmap); + + return 0; +} + +/* + * This function polls the card status. + */ +static int +mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) +{ + struct sdio_mmc_card *card = adapter->card; + u32 tries; + u8 cs; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs)) + break; + else if ((cs & bits) == bits) + return 0; + + usleep_range(10, 20); + } + + mwifiex_dbg(adapter, ERROR, + "poll card status failed, tries = %d\n", tries); + + return -1; +} + +/* + * This function reads the firmware status. + */ +static int +mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + u8 fws0, fws1; + + if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0)) + return -1; + + if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1)) + return -1; + + *dat = (u16) ((fws1 << 8) | fws0); + + return 0; +} + +/* + * This function disables the host interrupt. + * + * The host interrupt mask is read, the disable bit is reset and + * written back to the card host interrupt mask register. + */ +static void mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + struct sdio_func *func = card->func; + + sdio_claim_host(func); + mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, 0); + sdio_release_irq(func); + sdio_release_host(func); +} + +/* + * This function reads the interrupt status from card. + */ +static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + u8 sdio_ireg; + unsigned long flags; + + if (mwifiex_read_data_sync(adapter, card->mp_regs, + card->reg->max_mp_regs, + REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) { + mwifiex_dbg(adapter, ERROR, "read mp_regs failed\n"); + return; + } + + sdio_ireg = card->mp_regs[card->reg->host_int_status_reg]; + if (sdio_ireg) { + /* + * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * For SDIO new mode CMD port interrupts + * DN_LD_CMD_PORT_HOST_INT_STATUS and/or + * UP_LD_CMD_PORT_HOST_INT_STATUS + * Clear the interrupt status register + */ + mwifiex_dbg(adapter, INTR, + "int: sdio_ireg = %#x\n", sdio_ireg); + spin_lock_irqsave(&adapter->int_lock, flags); + adapter->int_status |= sdio_ireg; + spin_unlock_irqrestore(&adapter->int_lock, flags); + } +} + +/* + * SDIO interrupt handler. + * + * This function reads the interrupt status from firmware and handles + * the interrupt in current thread (ksdioirqd) right away. + */ +static void +mwifiex_sdio_interrupt(struct sdio_func *func) +{ + struct mwifiex_adapter *adapter; + struct sdio_mmc_card *card; + + card = sdio_get_drvdata(func); + if (!card || !card->adapter) { + pr_debug("int: func=%p card=%p adapter=%p\n", + func, card, card ? card->adapter : NULL); + return; + } + adapter = card->adapter; + + if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) + adapter->ps_state = PS_STATE_AWAKE; + + mwifiex_interrupt_status(adapter); + mwifiex_main_process(adapter); +} + +/* + * This function enables the host interrupt. + * + * The host interrupt enable mask is written to the card + * host interrupt mask register. + */ +static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + struct sdio_func *func = card->func; + int ret; + + sdio_claim_host(func); + + /* Request the SDIO IRQ */ + ret = sdio_claim_irq(func, mwifiex_sdio_interrupt); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "claim irq failed: ret=%d\n", ret); + goto out; + } + + /* Simply write the mask to the register */ + ret = mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, + card->reg->host_int_enable); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "enable host interrupt failed\n"); + sdio_release_irq(func); + } + +out: + sdio_release_host(func); + return ret; +} + +/* + * This function sends a data buffer to the card. + */ +static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, + u32 *type, u8 *buffer, + u32 npayload, u32 ioport) +{ + int ret; + u32 nb; + + if (!buffer) { + mwifiex_dbg(adapter, ERROR, + "%s: buffer is NULL\n", __func__); + return -1; + } + + ret = mwifiex_read_data_sync(adapter, buffer, npayload, ioport, 1); + + if (ret) { + mwifiex_dbg(adapter, ERROR, + "%s: read iomem failed: %d\n", __func__, + ret); + return -1; + } + + nb = le16_to_cpu(*(__le16 *) (buffer)); + if (nb > npayload) { + mwifiex_dbg(adapter, ERROR, + "%s: invalid packet, nb=%d npayload=%d\n", + __func__, nb, npayload); + return -1; + } + + *type = le16_to_cpu(*(__le16 *) (buffer + 2)); + + return ret; +} + +/* + * This function downloads the firmware to the card. + * + * Firmware is downloaded to the card in blocks. Every block download + * is tested for CRC errors, and retried a number of times before + * returning failure. + */ +static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, + struct mwifiex_fw_image *fw) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + int ret; + u8 *firmware = fw->fw_buf; + u32 firmware_len = fw->fw_len; + u32 offset = 0; + u8 base0, base1; + u8 *fwbuf; + u16 len = 0; + u32 txlen, tx_blocks = 0, tries; + u32 i = 0; + + if (!firmware_len) { + mwifiex_dbg(adapter, ERROR, + "firmware image not found! Terminating download\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "info: downloading FW image (%d bytes)\n", + firmware_len); + + /* Assume that the allocated buffer is 8-byte aligned */ + fwbuf = kzalloc(MWIFIEX_UPLD_SIZE, GFP_KERNEL); + if (!fwbuf) + return -ENOMEM; + + sdio_claim_host(card->func); + + /* Perform firmware data transfer */ + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY + bits */ + ret = mwifiex_sdio_poll_card_status(adapter, CARD_IO_READY | + DN_LD_CARD_RDY); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "FW download with helper:\t" + "poll status timeout @ %d\n", offset); + goto done; + } + + /* More data? */ + if (offset >= firmware_len) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = mwifiex_read_reg(adapter, reg->base_0_reg, + &base0); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "dev BASE0 register read failed:\t" + "base0=%#04X(%d). Terminating dnld\n", + base0, base0); + goto done; + } + ret = mwifiex_read_reg(adapter, reg->base_1_reg, + &base1); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "dev BASE1 register read failed:\t" + "base1=%#04X(%d). Terminating dnld\n", + base1, base1); + goto done; + } + len = (u16) (((base1 & 0xff) << 8) | (base0 & 0xff)); + + if (len) + break; + + usleep_range(10, 20); + } + + if (!len) { + break; + } else if (len > MWIFIEX_UPLD_SIZE) { + mwifiex_dbg(adapter, ERROR, + "FW dnld failed @ %d, invalid length %d\n", + offset, len); + ret = -1; + goto done; + } + + txlen = len; + + if (len & BIT(0)) { + i++; + if (i > MAX_WRITE_IOMEM_RETRY) { + mwifiex_dbg(adapter, ERROR, + "FW dnld failed @ %d, over max retry\n", + offset); + ret = -1; + goto done; + } + mwifiex_dbg(adapter, ERROR, + "CRC indicated by the helper:\t" + "len = 0x%04X, txlen = %d\n", len, txlen); + len &= ~BIT(0); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + i = 0; + + /* Set blocksize to transfer - checking for last + block */ + if (firmware_len - offset < txlen) + txlen = firmware_len - offset; + + tx_blocks = (txlen + MWIFIEX_SDIO_BLOCK_SIZE - 1) + / MWIFIEX_SDIO_BLOCK_SIZE; + + /* Copy payload to buffer */ + memmove(fwbuf, &firmware[offset], txlen); + } + + ret = mwifiex_write_data_sync(adapter, fwbuf, tx_blocks * + MWIFIEX_SDIO_BLOCK_SIZE, + adapter->ioport); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "FW download, write iomem (%d) failed @ %d\n", + i, offset); + if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) + mwifiex_dbg(adapter, ERROR, + "write CFG reg failed\n"); + + ret = -1; + goto done; + } + + offset += txlen; + } while (true); + + sdio_release_host(card->func); + + mwifiex_dbg(adapter, MSG, + "info: FW download over, size %d bytes\n", offset); + + ret = 0; +done: + kfree(fwbuf); + return ret; +} + +/* + * This function checks the firmware status in card. + * + * The winner interface is also determined by this function. + */ +static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, + u32 poll_num) +{ + struct sdio_mmc_card *card = adapter->card; + int ret = 0; + u16 firmware_stat; + u32 tries; + u8 winner_status; + + /* Wait for firmware initialization event */ + for (tries = 0; tries < poll_num; tries++) { + ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); + if (ret) + continue; + if (firmware_stat == FIRMWARE_READY_SDIO) { + ret = 0; + break; + } else { + msleep(100); + ret = -1; + } + } + + if (ret) { + if (mwifiex_read_reg + (adapter, card->reg->status_reg_0, &winner_status)) + winner_status = 0; + + if (winner_status) + adapter->winner = 0; + else + adapter->winner = 1; + } + return ret; +} + +/* + * This function decode sdio aggreation pkt. + * + * Based on the the data block size and pkt_len, + * skb data will be decoded to few packets. + */ +static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + u32 total_pkt_len, pkt_len; + struct sk_buff *skb_deaggr; + u32 pkt_type; + u16 blk_size; + u8 blk_num; + u8 *data; + + data = skb->data; + total_pkt_len = skb->len; + + while (total_pkt_len >= (SDIO_HEADER_OFFSET + INTF_HEADER_LEN)) { + if (total_pkt_len < adapter->sdio_rx_block_size) + break; + blk_num = *(data + BLOCK_NUMBER_OFFSET); + blk_size = adapter->sdio_rx_block_size * blk_num; + if (blk_size > total_pkt_len) { + mwifiex_dbg(adapter, ERROR, + "%s: error in blk_size,\t" + "blk_num=%d, blk_size=%d, total_pkt_len=%d\n", + __func__, blk_num, blk_size, total_pkt_len); + break; + } + pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET)); + pkt_type = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET + + 2)); + if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) { + mwifiex_dbg(adapter, ERROR, + "%s: error in pkt_len,\t" + "pkt_len=%d, blk_size=%d\n", + __func__, pkt_len, blk_size); + break; + } + skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len, + GFP_KERNEL | GFP_DMA); + if (!skb_deaggr) + break; + skb_put(skb_deaggr, pkt_len); + memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len); + skb_pull(skb_deaggr, INTF_HEADER_LEN); + + mwifiex_handle_rx_packet(adapter, skb_deaggr); + data += blk_size; + total_pkt_len -= blk_size; + } +} + +/* + * This function decodes a received packet. + * + * Based on the type, the packet is treated as either a data, or + * a command response, or an event, and the correct handler + * function is invoked. + */ +static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, + struct sk_buff *skb, u32 upld_typ) +{ + u8 *cmd_buf; + __le16 *curr_ptr = (__le16 *)skb->data; + u16 pkt_len = le16_to_cpu(*curr_ptr); + struct mwifiex_rxinfo *rx_info; + + if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) { + skb_trim(skb, pkt_len); + skb_pull(skb, INTF_HEADER_LEN); + } + + switch (upld_typ) { + case MWIFIEX_TYPE_AGGR_DATA: + mwifiex_dbg(adapter, INFO, + "info: --- Rx: Aggr Data packet ---\n"); + rx_info = MWIFIEX_SKB_RXCB(skb); + rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA; + if (adapter->rx_work_enabled) { + skb_queue_tail(&adapter->rx_data_q, skb); + atomic_inc(&adapter->rx_pending); + adapter->data_received = true; + } else { + mwifiex_deaggr_sdio_pkt(adapter, skb); + dev_kfree_skb_any(skb); + } + break; + + case MWIFIEX_TYPE_DATA: + mwifiex_dbg(adapter, DATA, + "info: --- Rx: Data packet ---\n"); + if (adapter->rx_work_enabled) { + skb_queue_tail(&adapter->rx_data_q, skb); + adapter->data_received = true; + atomic_inc(&adapter->rx_pending); + } else { + mwifiex_handle_rx_packet(adapter, skb); + } + break; + + case MWIFIEX_TYPE_CMD: + mwifiex_dbg(adapter, CMD, + "info: --- Rx: Cmd Response ---\n"); + /* take care of curr_cmd = NULL case */ + if (!adapter->curr_cmd) { + cmd_buf = adapter->upld_buf; + + if (adapter->ps_state == PS_STATE_SLEEP_CFM) + mwifiex_process_sleep_confirm_resp(adapter, + skb->data, + skb->len); + + memcpy(cmd_buf, skb->data, + min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, + skb->len)); + + dev_kfree_skb_any(skb); + } else { + adapter->cmd_resp_received = true; + adapter->curr_cmd->resp_skb = skb; + } + break; + + case MWIFIEX_TYPE_EVENT: + mwifiex_dbg(adapter, EVENT, + "info: --- Rx: Event ---\n"); + adapter->event_cause = le32_to_cpu(*(__le32 *) skb->data); + + if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) + memcpy(adapter->event_body, + skb->data + MWIFIEX_EVENT_HEADER_LEN, + skb->len); + + /* event cause has been saved to adapter->event_cause */ + adapter->event_received = true; + adapter->event_skb = skb; + + break; + + default: + mwifiex_dbg(adapter, ERROR, + "unknown upload type %#x\n", upld_typ); + dev_kfree_skb_any(skb); + break; + } + + return 0; +} + +/* + * This function transfers received packets from card to driver, performing + * aggregation if required. + * + * For data received on control port, or if aggregation is disabled, the + * received buffers are uploaded as separate packets. However, if aggregation + * is enabled and required, the buffers are copied onto an aggregation buffer, + * provided there is space left, processed and finally uploaded. + */ +static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, + u16 rx_len, u8 port) +{ + struct sdio_mmc_card *card = adapter->card; + s32 f_do_rx_aggr = 0; + s32 f_do_rx_cur = 0; + s32 f_aggr_cur = 0; + s32 f_post_aggr_cur = 0; + struct sk_buff *skb_deaggr; + struct sk_buff *skb = NULL; + u32 pkt_len, pkt_type, mport, pind; + u8 *curr_ptr; + + if ((card->has_control_mask) && (port == CTRL_PORT)) { + /* Read the command Resp without aggr */ + mwifiex_dbg(adapter, CMD, + "info: %s: no aggregation for cmd\t" + "response\n", __func__); + + f_do_rx_cur = 1; + goto rx_curr_single; + } + + if (!card->mpa_rx.enabled) { + mwifiex_dbg(adapter, WARN, + "info: %s: rx aggregation disabled\n", + __func__); + + f_do_rx_cur = 1; + goto rx_curr_single; + } + + if ((!card->has_control_mask && (card->mp_rd_bitmap & + card->reg->data_port_mask)) || + (card->has_control_mask && (card->mp_rd_bitmap & + (~((u32) CTRL_PORT_MASK))))) { + /* Some more data RX pending */ + mwifiex_dbg(adapter, INFO, + "info: %s: not last packet\n", __func__); + + if (MP_RX_AGGR_IN_PROGRESS(card)) { + if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) { + f_aggr_cur = 1; + } else { + /* No room in Aggr buf, do rx aggr now */ + f_do_rx_aggr = 1; + f_post_aggr_cur = 1; + } + } else { + /* Rx aggr not in progress */ + f_aggr_cur = 1; + } + + } else { + /* No more data RX pending */ + mwifiex_dbg(adapter, INFO, + "info: %s: last packet\n", __func__); + + if (MP_RX_AGGR_IN_PROGRESS(card)) { + f_do_rx_aggr = 1; + if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) + f_aggr_cur = 1; + else + /* No room in Aggr buf, do rx aggr now */ + f_do_rx_cur = 1; + } else { + f_do_rx_cur = 1; + } + } + + if (f_aggr_cur) { + mwifiex_dbg(adapter, INFO, + "info: current packet aggregation\n"); + /* Curr pkt can be aggregated */ + mp_rx_aggr_setup(card, rx_len, port); + + if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || + mp_rx_aggr_port_limit_reached(card)) { + mwifiex_dbg(adapter, INFO, + "info: %s: aggregated packet\t" + "limit reached\n", __func__); + /* No more pkts allowed in Aggr buf, rx it */ + f_do_rx_aggr = 1; + } + } + + if (f_do_rx_aggr) { + /* do aggr RX now */ + mwifiex_dbg(adapter, DATA, + "info: do_rx_aggr: num of packets: %d\n", + card->mpa_rx.pkt_cnt); + + if (card->supports_sdio_new_mode) { + int i; + u32 port_count; + + for (i = 0, port_count = 0; i < card->max_ports; i++) + if (card->mpa_rx.ports & BIT(i)) + port_count++; + + /* Reading data from "start_port + 0" to "start_port + + * port_count -1", so decrease the count by 1 + */ + port_count--; + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_rx.start_port; + } else { + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (card->mpa_rx.ports << 4)) + + card->mpa_rx.start_port; + } + + if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, + card->mpa_rx.buf_len, mport, 1)) + goto error; + + curr_ptr = card->mpa_rx.buf; + + for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { + u32 *len_arr = card->mpa_rx.len_arr; + + /* get curr PKT len & type */ + pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]); + pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]); + + /* copy pkt to deaggr buf */ + skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind], + GFP_KERNEL | + GFP_DMA); + if (!skb_deaggr) { + mwifiex_dbg(adapter, ERROR, "skb allocation failure\t" + "drop pkt len=%d type=%d\n", + pkt_len, pkt_type); + curr_ptr += len_arr[pind]; + continue; + } + + skb_put(skb_deaggr, len_arr[pind]); + + if ((pkt_type == MWIFIEX_TYPE_DATA || + (pkt_type == MWIFIEX_TYPE_AGGR_DATA && + adapter->sdio_rx_aggr_enable)) && + (pkt_len <= len_arr[pind])) { + + memcpy(skb_deaggr->data, curr_ptr, pkt_len); + + skb_trim(skb_deaggr, pkt_len); + + /* Process de-aggr packet */ + mwifiex_decode_rx_packet(adapter, skb_deaggr, + pkt_type); + } else { + mwifiex_dbg(adapter, ERROR, + "drop wrong aggr pkt:\t" + "sdio_single_port_rx_aggr=%d\t" + "type=%d len=%d max_len=%d\n", + adapter->sdio_rx_aggr_enable, + pkt_type, pkt_len, len_arr[pind]); + dev_kfree_skb_any(skb_deaggr); + } + curr_ptr += len_arr[pind]; + } + MP_RX_AGGR_BUF_RESET(card); + } + +rx_curr_single: + if (f_do_rx_cur) { + mwifiex_dbg(adapter, INFO, "info: RX: port: %d, rx_len: %d\n", + port, rx_len); + + skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA); + if (!skb) { + mwifiex_dbg(adapter, ERROR, + "single skb allocated fail,\t" + "drop pkt port=%d len=%d\n", port, rx_len); + if (mwifiex_sdio_card_to_host(adapter, &pkt_type, + card->mpa_rx.buf, rx_len, + adapter->ioport + port)) + goto error; + return 0; + } + + skb_put(skb, rx_len); + + if (mwifiex_sdio_card_to_host(adapter, &pkt_type, + skb->data, skb->len, + adapter->ioport + port)) + goto error; + if (!adapter->sdio_rx_aggr_enable && + pkt_type == MWIFIEX_TYPE_AGGR_DATA) { + mwifiex_dbg(adapter, ERROR, "drop wrong pkt type %d\t" + "current SDIO RX Aggr not enabled\n", + pkt_type); + dev_kfree_skb_any(skb); + return 0; + } + + mwifiex_decode_rx_packet(adapter, skb, pkt_type); + } + if (f_post_aggr_cur) { + mwifiex_dbg(adapter, INFO, + "info: current packet aggregation\n"); + /* Curr pkt can be aggregated */ + mp_rx_aggr_setup(card, rx_len, port); + } + + return 0; +error: + if (MP_RX_AGGR_IN_PROGRESS(card)) + MP_RX_AGGR_BUF_RESET(card); + + if (f_do_rx_cur && skb) + /* Single transfer pending. Free curr buff also */ + dev_kfree_skb_any(skb); + + return -1; +} + +/* + * This function checks the current interrupt status. + * + * The following interrupts are checked and handled by this function - + * - Data sent + * - Command sent + * - Packets received + * + * Since the firmware does not generate download ready interrupt if the + * port updated is command port only, command sent interrupt checking + * should be done manually, and for every SDIO interrupt. + * + * In case of Rx packets received, the packets are uploaded from card to + * host and processed accordingly. + */ +static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + int ret = 0; + u8 sdio_ireg; + struct sk_buff *skb; + u8 port = CTRL_PORT; + u32 len_reg_l, len_reg_u; + u32 rx_blocks; + u16 rx_len; + unsigned long flags; + u32 bitmap; + u8 cr; + + spin_lock_irqsave(&adapter->int_lock, flags); + sdio_ireg = adapter->int_status; + adapter->int_status = 0; + spin_unlock_irqrestore(&adapter->int_lock, flags); + + if (!sdio_ireg) + return ret; + + /* Following interrupt is only for SDIO new mode */ + if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent) + adapter->cmd_sent = false; + + /* Following interrupt is only for SDIO new mode */ + if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { + u32 pkt_type; + + /* read the len of control packet */ + rx_len = card->mp_regs[reg->cmd_rd_len_1] << 8; + rx_len |= (u16)card->mp_regs[reg->cmd_rd_len_0]; + rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE); + if (rx_len <= INTF_HEADER_LEN || + (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > + MWIFIEX_RX_DATA_BUF_SIZE) + return -1; + rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); + mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", rx_len); + + skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA); + if (!skb) + return -1; + + skb_put(skb, rx_len); + + if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data, + skb->len, adapter->ioport | + CMD_PORT_SLCT)) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to card_to_host", __func__); + dev_kfree_skb_any(skb); + goto term_cmd; + } + + if ((pkt_type != MWIFIEX_TYPE_CMD) && + (pkt_type != MWIFIEX_TYPE_EVENT)) + mwifiex_dbg(adapter, ERROR, + "%s:Received wrong packet on cmd port", + __func__); + + mwifiex_decode_rx_packet(adapter, skb, pkt_type); + } + + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { + bitmap = (u32) card->mp_regs[reg->wr_bitmap_l]; + bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8; + if (card->supports_sdio_new_mode) { + bitmap |= + ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16; + bitmap |= + ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24; + } + card->mp_wr_bitmap = bitmap; + + mwifiex_dbg(adapter, INTR, + "int: DNLD: wr_bitmap=0x%x\n", + card->mp_wr_bitmap); + if (adapter->data_sent && + (card->mp_wr_bitmap & card->mp_data_port_mask)) { + mwifiex_dbg(adapter, INTR, + "info: <--- Tx DONE Interrupt --->\n"); + adapter->data_sent = false; + } + } + + /* As firmware will not generate download ready interrupt if the port + updated is command port only, cmd_sent should be done for any SDIO + interrupt. */ + if (card->has_control_mask && adapter->cmd_sent) { + /* Check if firmware has attach buffer at command port and + update just that in wr_bit_map. */ + card->mp_wr_bitmap |= + (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK; + if (card->mp_wr_bitmap & CTRL_PORT_MASK) + adapter->cmd_sent = false; + } + + mwifiex_dbg(adapter, INTR, "info: cmd_sent=%d data_sent=%d\n", + adapter->cmd_sent, adapter->data_sent); + if (sdio_ireg & UP_LD_HOST_INT_STATUS) { + bitmap = (u32) card->mp_regs[reg->rd_bitmap_l]; + bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8; + if (card->supports_sdio_new_mode) { + bitmap |= + ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16; + bitmap |= + ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24; + } + card->mp_rd_bitmap = bitmap; + mwifiex_dbg(adapter, INTR, + "int: UPLD: rd_bitmap=0x%x\n", + card->mp_rd_bitmap); + + while (true) { + ret = mwifiex_get_rd_port(adapter, &port); + if (ret) { + mwifiex_dbg(adapter, INFO, + "info: no more rd_port available\n"); + break; + } + len_reg_l = reg->rd_len_p0_l + (port << 1); + len_reg_u = reg->rd_len_p0_u + (port << 1); + rx_len = ((u16) card->mp_regs[len_reg_u]) << 8; + rx_len |= (u16) card->mp_regs[len_reg_l]; + mwifiex_dbg(adapter, INFO, + "info: RX: port=%d rx_len=%u\n", + port, rx_len); + rx_blocks = + (rx_len + MWIFIEX_SDIO_BLOCK_SIZE - + 1) / MWIFIEX_SDIO_BLOCK_SIZE; + if (rx_len <= INTF_HEADER_LEN || + (card->mpa_rx.enabled && + ((rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > + card->mpa_rx.buf_size))) { + mwifiex_dbg(adapter, ERROR, + "invalid rx_len=%d\n", + rx_len); + return -1; + } + + rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); + mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", + rx_len); + + if (mwifiex_sdio_card_to_host_mp_aggr(adapter, rx_len, + port)) { + mwifiex_dbg(adapter, ERROR, + "card_to_host_mpa failed: int status=%#x\n", + sdio_ireg); + goto term_cmd; + } + } + } + + return 0; + +term_cmd: + /* terminate cmd */ + if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) + mwifiex_dbg(adapter, ERROR, "read CFG reg failed\n"); + else + mwifiex_dbg(adapter, INFO, + "info: CFG reg val = %d\n", cr); + + if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04))) + mwifiex_dbg(adapter, ERROR, + "write CFG reg failed\n"); + else + mwifiex_dbg(adapter, INFO, "info: write success\n"); + + if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) + mwifiex_dbg(adapter, ERROR, + "read CFG reg failed\n"); + else + mwifiex_dbg(adapter, INFO, + "info: CFG reg val =%x\n", cr); + + return -1; +} + +/* + * This function aggregates transmission buffers in driver and downloads + * the aggregated packet to card. + * + * The individual packets are aggregated by copying into an aggregation + * buffer and then downloaded to the card. Previous unsent packets in the + * aggregation buffer are pre-copied first before new packets are added. + * Aggregation is done till there is space left in the aggregation buffer, + * or till new packets are available. + * + * The function will only download the packet to the card when aggregation + * stops, otherwise it will just aggregate the packet in aggregation buffer + * and return. + */ +static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, + u8 *payload, u32 pkt_len, u32 port, + u32 next_pkt_len) +{ + struct sdio_mmc_card *card = adapter->card; + int ret = 0; + s32 f_send_aggr_buf = 0; + s32 f_send_cur_buf = 0; + s32 f_precopy_cur_buf = 0; + s32 f_postcopy_cur_buf = 0; + u32 mport; + + if (!card->mpa_tx.enabled || + (card->has_control_mask && (port == CTRL_PORT)) || + (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) { + mwifiex_dbg(adapter, WARN, + "info: %s: tx aggregation disabled\n", + __func__); + + f_send_cur_buf = 1; + goto tx_curr_single; + } + + if (next_pkt_len) { + /* More pkt in TX queue */ + mwifiex_dbg(adapter, INFO, + "info: %s: more packets in queue.\n", + __func__); + + if (MP_TX_AGGR_IN_PROGRESS(card)) { + if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { + f_precopy_cur_buf = 1; + + if (!(card->mp_wr_bitmap & + (1 << card->curr_wr_port)) || + !MP_TX_AGGR_BUF_HAS_ROOM( + card, pkt_len + next_pkt_len)) + f_send_aggr_buf = 1; + } else { + /* No room in Aggr buf, send it */ + f_send_aggr_buf = 1; + + if (!(card->mp_wr_bitmap & + (1 << card->curr_wr_port))) + f_send_cur_buf = 1; + else + f_postcopy_cur_buf = 1; + } + } else { + if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) && + (card->mp_wr_bitmap & (1 << card->curr_wr_port))) + f_precopy_cur_buf = 1; + else + f_send_cur_buf = 1; + } + } else { + /* Last pkt in TX queue */ + mwifiex_dbg(adapter, INFO, + "info: %s: Last packet in Tx Queue.\n", + __func__); + + if (MP_TX_AGGR_IN_PROGRESS(card)) { + /* some packs in Aggr buf already */ + f_send_aggr_buf = 1; + + if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) + f_precopy_cur_buf = 1; + else + /* No room in Aggr buf, send it */ + f_send_cur_buf = 1; + } else { + f_send_cur_buf = 1; + } + } + + if (f_precopy_cur_buf) { + mwifiex_dbg(adapter, DATA, + "data: %s: precopy current buffer\n", + __func__); + MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); + + if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || + mp_tx_aggr_port_limit_reached(card)) + /* No more pkts allowed in Aggr buf, send it */ + f_send_aggr_buf = 1; + } + + if (f_send_aggr_buf) { + mwifiex_dbg(adapter, DATA, + "data: %s: send aggr buffer: %d %d\n", + __func__, card->mpa_tx.start_port, + card->mpa_tx.ports); + if (card->supports_sdio_new_mode) { + u32 port_count; + int i; + + for (i = 0, port_count = 0; i < card->max_ports; i++) + if (card->mpa_tx.ports & BIT(i)) + port_count++; + + /* Writing data from "start_port + 0" to "start_port + + * port_count -1", so decrease the count by 1 + */ + port_count--; + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_tx.start_port; + } else { + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (card->mpa_tx.ports << 4)) + + card->mpa_tx.start_port; + } + + ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, + card->mpa_tx.buf_len, mport); + + MP_TX_AGGR_BUF_RESET(card); + } + +tx_curr_single: + if (f_send_cur_buf) { + mwifiex_dbg(adapter, DATA, + "data: %s: send current buffer %d\n", + __func__, port); + ret = mwifiex_write_data_to_card(adapter, payload, pkt_len, + adapter->ioport + port); + } + + if (f_postcopy_cur_buf) { + mwifiex_dbg(adapter, DATA, + "data: %s: postcopy current buffer\n", + __func__); + MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); + } + + return ret; +} + +/* + * This function downloads data from driver to card. + * + * Both commands and data packets are transferred to the card by this + * function. + * + * This function adds the SDIO specific header to the front of the buffer + * before transferring. The header contains the length of the packet and + * the type. The firmware handles the packets based upon this set type. + */ +static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, + u8 type, struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + u32 buf_block_len; + u32 blk_size; + u32 port = CTRL_PORT; + u8 *payload = (u8 *)skb->data; + u32 pkt_len = skb->len; + + /* Allocate buffer and copy payload */ + blk_size = MWIFIEX_SDIO_BLOCK_SIZE; + buf_block_len = (pkt_len + blk_size - 1) / blk_size; + *(__le16 *)&payload[0] = cpu_to_le16((u16)pkt_len); + *(__le16 *)&payload[2] = cpu_to_le16(type); + + /* + * This is SDIO specific header + * u16 length, + * u16 type (MWIFIEX_TYPE_DATA = 0, MWIFIEX_TYPE_CMD = 1, + * MWIFIEX_TYPE_EVENT = 3) + */ + if (type == MWIFIEX_TYPE_DATA) { + ret = mwifiex_get_wr_port_data(adapter, &port); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "%s: no wr_port available\n", + __func__); + return ret; + } + } else { + adapter->cmd_sent = true; + /* Type must be MWIFIEX_TYPE_CMD */ + + if (pkt_len <= INTF_HEADER_LEN || + pkt_len > MWIFIEX_UPLD_SIZE) + mwifiex_dbg(adapter, ERROR, + "%s: payload=%p, nb=%d\n", + __func__, payload, pkt_len); + + if (card->supports_sdio_new_mode) + port = CMD_PORT_SLCT; + } + + /* Transfer data to card */ + pkt_len = buf_block_len * blk_size; + + if (tx_param) + ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, + port, tx_param->next_pkt_len + ); + else + ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, + port, 0); + + if (ret) { + if (type == MWIFIEX_TYPE_CMD) + adapter->cmd_sent = false; + if (type == MWIFIEX_TYPE_DATA) { + adapter->data_sent = false; + /* restore curr_wr_port in error cases */ + card->curr_wr_port = port; + card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port); + } + } else { + if (type == MWIFIEX_TYPE_DATA) { + if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) + adapter->data_sent = true; + else + adapter->data_sent = false; + } + } + + return ret; +} + +/* + * This function allocates the MPA Tx and Rx buffers. + */ +static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter, + u32 mpa_tx_buf_size, u32 mpa_rx_buf_size) +{ + struct sdio_mmc_card *card = adapter->card; + u32 rx_buf_size; + int ret = 0; + + card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL); + if (!card->mpa_tx.buf) { + ret = -1; + goto error; + } + + card->mpa_tx.buf_size = mpa_tx_buf_size; + + rx_buf_size = max_t(u32, mpa_rx_buf_size, + (u32)SDIO_MAX_AGGR_BUF_SIZE); + card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL); + if (!card->mpa_rx.buf) { + ret = -1; + goto error; + } + + card->mpa_rx.buf_size = rx_buf_size; + +error: + if (ret) { + kfree(card->mpa_tx.buf); + kfree(card->mpa_rx.buf); + card->mpa_tx.buf_size = 0; + card->mpa_rx.buf_size = 0; + } + + return ret; +} + +/* + * This function unregisters the SDIO device. + * + * The SDIO IRQ is released, the function is disabled and driver + * data is set to null. + */ +static void +mwifiex_unregister_dev(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + if (adapter->card) { + sdio_claim_host(card->func); + sdio_disable_func(card->func); + sdio_release_host(card->func); + } +} + +/* + * This function registers the SDIO device. + * + * SDIO IRQ is claimed, block size is set and driver data is initialized. + */ +static int mwifiex_register_dev(struct mwifiex_adapter *adapter) +{ + int ret; + struct sdio_mmc_card *card = adapter->card; + struct sdio_func *func = card->func; + + /* save adapter pointer in card */ + card->adapter = adapter; + adapter->tx_buf_size = card->tx_buf_size; + + sdio_claim_host(func); + + /* Set block size */ + ret = sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE); + sdio_release_host(func); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "cannot set SDIO block size\n"); + return ret; + } + + + adapter->dev = &func->dev; + + strcpy(adapter->fw_name, card->firmware); + if (card->fw_dump_enh) { + adapter->mem_type_mapping_tbl = generic_mem_type_map; + adapter->num_mem_types = 1; + } else { + adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; + adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); + } + + return 0; +} + +/* + * This function initializes the SDIO driver. + * + * The following initializations steps are followed - + * - Read the Host interrupt status register to acknowledge + * the first interrupt got from bootloader + * - Disable host interrupt mask register + * - Get SDIO port + * - Initialize SDIO variables in card + * - Allocate MP registers + * - Allocate MPA Tx and Rx buffers + */ +static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + int ret; + u8 sdio_ireg; + + sdio_set_drvdata(card->func, card); + + /* + * Read the host_int_status_reg for ACK the first interrupt got + * from the bootloader. If we don't do this we get a interrupt + * as soon as we register the irq. + */ + mwifiex_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg); + + /* Get SDIO ioport */ + mwifiex_init_sdio_ioport(adapter); + + /* Initialize SDIO variables in card */ + card->mp_rd_bitmap = 0; + card->mp_wr_bitmap = 0; + card->curr_rd_port = reg->start_rd_port; + card->curr_wr_port = reg->start_wr_port; + + card->mp_data_port_mask = reg->data_port_mask; + + card->mpa_tx.buf_len = 0; + card->mpa_tx.pkt_cnt = 0; + card->mpa_tx.start_port = 0; + + card->mpa_tx.enabled = 1; + card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit; + + card->mpa_rx.buf_len = 0; + card->mpa_rx.pkt_cnt = 0; + card->mpa_rx.start_port = 0; + + card->mpa_rx.enabled = 1; + card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit; + + /* Allocate buffers for SDIO MP-A */ + card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL); + if (!card->mp_regs) + return -ENOMEM; + + /* Allocate skb pointer buffers */ + card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) * + card->mp_agg_pkt_limit, GFP_KERNEL); + card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) * + card->mp_agg_pkt_limit, GFP_KERNEL); + ret = mwifiex_alloc_sdio_mpa_buffers(adapter, + card->mp_tx_agg_buf_size, + card->mp_rx_agg_buf_size); + + /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */ + if (ret && (card->mp_tx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX || + card->mp_rx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX)) { + /* Disable rx single port aggregation */ + adapter->host_disable_sdio_rx_aggr = true; + + ret = mwifiex_alloc_sdio_mpa_buffers + (adapter, MWIFIEX_MP_AGGR_BUF_SIZE_32K, + MWIFIEX_MP_AGGR_BUF_SIZE_32K); + if (ret) { + /* Disable multi port aggregation */ + card->mpa_tx.enabled = 0; + card->mpa_rx.enabled = 0; + } + } + + adapter->auto_tdls = card->can_auto_tdls; + adapter->ext_scan = card->can_ext_scan; + return 0; +} + +/* + * This function resets the MPA Tx and Rx buffers. + */ +static void mwifiex_cleanup_mpa_buf(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + MP_TX_AGGR_BUF_RESET(card); + MP_RX_AGGR_BUF_RESET(card); +} + +/* + * This function cleans up the allocated card buffers. + * + * The following are freed by this function - + * - MP registers + * - MPA Tx buffer + * - MPA Rx buffer + */ +static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + kfree(card->mp_regs); + kfree(card->mpa_rx.skb_arr); + kfree(card->mpa_rx.len_arr); + kfree(card->mpa_tx.buf); + kfree(card->mpa_rx.buf); + sdio_set_drvdata(card->func, NULL); + kfree(card); +} + +/* + * This function updates the MP end port in card. + */ +static void +mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + int i; + + card->mp_end_port = port; + + card->mp_data_port_mask = reg->data_port_mask; + + if (reg->start_wr_port) { + for (i = 1; i <= card->max_ports - card->mp_end_port; i++) + card->mp_data_port_mask &= + ~(1 << (card->max_ports - i)); + } + + card->curr_wr_port = reg->start_wr_port; + + mwifiex_dbg(adapter, CMD, + "cmd: mp_end_port %d, data port mask 0x%x\n", + port, card->mp_data_port_mask); +} + +static void mwifiex_recreate_adapter(struct sdio_mmc_card *card) +{ + struct sdio_func *func = card->func; + const struct sdio_device_id *device_id = card->device_id; + + /* TODO mmc_hw_reset does not require destroying and re-probing the + * whole adapter. Hence there was no need to for this rube-goldberg + * design to reload the fw from an external workqueue. If we don't + * destroy the adapter we could reload the fw from + * mwifiex_main_work_queue directly. + * The real difficulty with fw reset is to restore all the user + * settings applied through ioctl. By destroying and recreating the + * adapter, we take the easy way out, since we rely on user space to + * restore them. We assume that user space will treat the new + * incarnation of the adapter(interfaces) as if they had been just + * discovered and initializes them from scratch. + */ + + mwifiex_sdio_remove(func); + + /* power cycle the adapter */ + sdio_claim_host(func); + mmc_hw_reset(func->card->host); + sdio_release_host(func); + + mwifiex_sdio_probe(func, device_id); +} + +static struct mwifiex_adapter *save_adapter; +static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + /* TODO card pointer is unprotected. If the adapter is removed + * physically, sdio core might trigger mwifiex_sdio_remove, before this + * workqueue is run, which will destroy the adapter struct. When this + * workqueue eventually exceutes it will dereference an invalid adapter + * pointer + */ + mwifiex_recreate_adapter(card); +} + +/* This function read/write firmware */ +static enum +rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter, + u8 doneflag) +{ + struct sdio_mmc_card *card = adapter->card; + int ret, tries; + u8 ctrl_data = 0; + + sdio_writeb(card->func, card->reg->fw_dump_host_ready, + card->reg->fw_dump_ctrl, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, + &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == FW_DUMP_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != card->reg->fw_dump_host_ready) { + mwifiex_dbg(adapter, WARN, + "The ctrl reg was changed, re-try again\n"); + sdio_writeb(card->func, card->reg->fw_dump_host_ready, + card->reg->fw_dump_ctrl, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); + return RDWR_STATUS_FAILURE; + } + } + usleep_range(100, 200); + } + if (ctrl_data == card->reg->fw_dump_host_ready) { + mwifiex_dbg(adapter, ERROR, + "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/* This function dump firmware memory to file */ +static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + int ret = 0; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; + enum rdwr_status stat; + u32 memory_size; + + if (!card->can_dump_fw) + return; + + for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + mwifiex_pm_wakeup_card(adapter); + sdio_claim_host(card->func); + + mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); + + stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg = card->reg->fw_dump_start; + /* Read the number of the memories which will dump */ + dump_num = sdio_readb(card->func, reg, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO read memory length err\n"); + goto done; + } + + /* Read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + memory_size = 0; + reg = card->reg->fw_dump_start; + for (i = 0; i < 4; i++) { + read_reg = sdio_readb(card->func, reg, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); + goto done; + } + memory_size |= (read_reg << i*8); + reg++; + } + + if (memory_size == 0) { + mwifiex_dbg(adapter, DUMP, "Firmware dump Finished!\n"); + ret = mwifiex_write_reg(adapter, + card->reg->fw_dump_ctrl, + FW_DUMP_READ_DONE); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); + return; + } + break; + } + + mwifiex_dbg(adapter, DUMP, + "%s_SIZE=0x%x\n", entry->mem_name, memory_size); + entry->mem_ptr = vmalloc(memory_size + 1); + entry->mem_size = memory_size; + if (!entry->mem_ptr) { + mwifiex_dbg(adapter, ERROR, "Vmalloc %s failed\n", + entry->mem_name); + goto done; + } + dbg_ptr = entry->mem_ptr; + end_ptr = dbg_ptr + memory_size; + + doneflag = entry->done_flag; + mwifiex_dbg(adapter, DUMP, + "Start %s output, please wait...\n", + entry->mem_name); + + do { + stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg_start = card->reg->fw_dump_start; + reg_end = card->reg->fw_dump_end; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = sdio_readb(card->func, reg, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "SDIO read err\n"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + mwifiex_dbg(adapter, ERROR, + "Allocated buf not enough\n"); + } + + if (stat != RDWR_STATUS_DONE) + continue; + + mwifiex_dbg(adapter, DUMP, "%s done: size=0x%tx\n", + entry->mem_name, dbg_ptr - entry->mem_ptr); + break; + } while (1); + } + mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); + +done: + sdio_release_host(card->func); +} + +static void mwifiex_sdio_generic_fw_dump(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + struct memory_type_mapping *entry = &generic_mem_type_map[0]; + unsigned int reg, reg_start, reg_end; + u8 start_flag = 0, done_flag = 0; + u8 *dbg_ptr, *end_ptr; + enum rdwr_status stat; + int ret = -1, tries; + + if (!card->fw_dump_enh) + return; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + + mwifiex_pm_wakeup_card(adapter); + sdio_claim_host(card->func); + + mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); + + stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg_start = card->reg->fw_dump_start; + reg_end = card->reg->fw_dump_end; + for (reg = reg_start; reg <= reg_end; reg++) { + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + start_flag = sdio_readb(card->func, reg, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "SDIO read err\n"); + goto done; + } + if (start_flag == 0) + break; + if (tries == MAX_POLL_TRIES) { + mwifiex_dbg(adapter, ERROR, + "FW not ready to dump\n"); + ret = -1; + goto done; + } + } + usleep_range(100, 200); + } + + entry->mem_ptr = vmalloc(0xf0000 + 1); + if (!entry->mem_ptr) { + ret = -1; + goto done; + } + dbg_ptr = entry->mem_ptr; + entry->mem_size = 0xf0000; + end_ptr = dbg_ptr + entry->mem_size; + + done_flag = entry->done_flag; + mwifiex_dbg(adapter, DUMP, + "Start %s output, please wait...\n", entry->mem_name); + + while (true) { + stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = sdio_readb(card->func, reg, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "SDIO read err\n"); + goto done; + } + dbg_ptr++; + if (dbg_ptr >= end_ptr) { + u8 *tmp_ptr; + + tmp_ptr = vmalloc(entry->mem_size + 0x4000 + 1); + if (!tmp_ptr) + goto done; + + memcpy(tmp_ptr, entry->mem_ptr, + entry->mem_size); + vfree(entry->mem_ptr); + entry->mem_ptr = tmp_ptr; + tmp_ptr = NULL; + dbg_ptr = entry->mem_ptr + entry->mem_size; + entry->mem_size += 0x4000; + end_ptr = entry->mem_ptr + entry->mem_size; + } + } + if (stat == RDWR_STATUS_DONE) { + entry->mem_size = dbg_ptr - entry->mem_ptr; + mwifiex_dbg(adapter, DUMP, "dump %s done size=0x%x\n", + entry->mem_name, entry->mem_size); + ret = 0; + break; + } + } + mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); + +done: + if (ret) { + mwifiex_dbg(adapter, ERROR, "firmware dump failed\n"); + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + sdio_release_host(card->func); +} + +static void mwifiex_sdio_device_dump_work(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + mwifiex_drv_info_dump(adapter); + if (card->fw_dump_enh) + mwifiex_sdio_generic_fw_dump(adapter); + else + mwifiex_sdio_fw_dump(adapter); + mwifiex_upload_device_dump(adapter); +} + +static void mwifiex_sdio_work(struct work_struct *work) +{ + if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, + &iface_work_flags)) + mwifiex_sdio_device_dump_work(save_adapter); + if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, + &iface_work_flags)) + mwifiex_sdio_card_reset_work(save_adapter); +} + +static DECLARE_WORK(sdio_work, mwifiex_sdio_work); +/* This function resets the card */ +static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter) +{ + save_adapter = adapter; + if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags)) + return; + + set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags); + + schedule_work(&sdio_work); +} + +/* This function dumps FW information */ +static void mwifiex_sdio_device_dump(struct mwifiex_adapter *adapter) +{ + save_adapter = adapter; + if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags)) + return; + + set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags); + schedule_work(&sdio_work); +} + +/* Function to dump SDIO function registers and SDIO scratch registers in case + * of FW crash + */ +static int +mwifiex_sdio_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf) +{ + char *p = drv_buf; + struct sdio_mmc_card *cardp = adapter->card; + int ret = 0; + u8 count, func, data, index = 0, size = 0; + u8 reg, reg_start, reg_end; + char buf[256], *ptr; + + if (!p) + return 0; + + mwifiex_dbg(adapter, MSG, "SDIO register dump start\n"); + + mwifiex_pm_wakeup_card(adapter); + + sdio_claim_host(cardp->func); + + for (count = 0; count < 5; count++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + + switch (count) { + case 0: + /* Read the registers of SDIO function0 */ + func = count; + reg_start = 0; + reg_end = 9; + break; + case 1: + /* Read the registers of SDIO function1 */ + func = count; + reg_start = cardp->reg->func1_dump_reg_start; + reg_end = cardp->reg->func1_dump_reg_end; + break; + case 2: + index = 0; + func = 1; + reg_start = cardp->reg->func1_spec_reg_table[index++]; + size = cardp->reg->func1_spec_reg_num; + reg_end = cardp->reg->func1_spec_reg_table[size-1]; + break; + default: + /* Read the scratch registers of SDIO function1 */ + if (count == 4) + mdelay(100); + func = 1; + reg_start = cardp->reg->func1_scratch_reg; + reg_end = reg_start + MWIFIEX_SDIO_SCRATCH_SIZE; + } + + if (count != 2) + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", + func, reg_start, reg_end); + else + ptr += sprintf(ptr, "SDIO Func%d: ", func); + + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + data = sdio_f0_readb(cardp->func, reg, &ret); + else + data = sdio_readb(cardp->func, reg, &ret); + + if (count == 2) + ptr += sprintf(ptr, "(%#x) ", reg); + if (!ret) { + ptr += sprintf(ptr, "%02x ", data); + } else { + ptr += sprintf(ptr, "ERR"); + break; + } + + if (count == 2 && reg < reg_end) + reg = cardp->reg->func1_spec_reg_table[index++]; + else + reg++; + } + + mwifiex_dbg(adapter, MSG, "%s\n", buf); + p += sprintf(p, "%s\n", buf); + } + + sdio_release_host(cardp->func); + + mwifiex_dbg(adapter, MSG, "SDIO register dump end\n"); + + return p - drv_buf; +} + +static struct mwifiex_if_ops sdio_ops = { + .init_if = mwifiex_init_sdio, + .cleanup_if = mwifiex_cleanup_sdio, + .check_fw_status = mwifiex_check_fw_status, + .prog_fw = mwifiex_prog_fw_w_helper, + .register_dev = mwifiex_register_dev, + .unregister_dev = mwifiex_unregister_dev, + .enable_int = mwifiex_sdio_enable_host_int, + .disable_int = mwifiex_sdio_disable_host_int, + .process_int_status = mwifiex_process_int_status, + .host_to_card = mwifiex_sdio_host_to_card, + .wakeup = mwifiex_pm_wakeup_card, + .wakeup_complete = mwifiex_pm_wakeup_card_complete, + + /* SDIO specific */ + .update_mp_end_port = mwifiex_update_mp_end_port, + .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, + .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, + .event_complete = mwifiex_sdio_event_complete, + .card_reset = mwifiex_sdio_card_reset, + .reg_dump = mwifiex_sdio_reg_dump, + .device_dump = mwifiex_sdio_device_dump, + .deaggr_pkt = mwifiex_deaggr_sdio_pkt, +}; + +/* + * This function initializes the SDIO driver. + * + * This initiates the semaphore and registers the device with + * SDIO bus. + */ +static int +mwifiex_sdio_init_module(void) +{ + sema_init(&add_remove_card_sem, 1); + + /* Clear the flag in case user removes the card. */ + user_rmmod = 0; + + return sdio_register_driver(&mwifiex_sdio); +} + +/* + * This function cleans up the SDIO driver. + * + * The following major steps are followed for cleanup - + * - Resume the device if its suspended + * - Disconnect the device if connected + * - Shutdown the firmware + * - Unregister the device from SDIO bus. + */ +static void +mwifiex_sdio_cleanup_module(void) +{ + if (!down_interruptible(&add_remove_card_sem)) + up(&add_remove_card_sem); + + /* Set the flag as user is removing this module. */ + user_rmmod = 1; + cancel_work_sync(&sdio_work); + + sdio_unregister_driver(&mwifiex_sdio); +} + +module_init(mwifiex_sdio_init_module); +module_exit(mwifiex_sdio_cleanup_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION); +MODULE_VERSION(SDIO_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h new file mode 100644 index 000000000000..b9fbc5cf6262 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sdio.h @@ -0,0 +1,672 @@ +/* + * Marvell Wireless LAN device driver: SDIO specific definitions + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_SDIO_H +#define _MWIFIEX_SDIO_H + + +#include +#include +#include +#include +#include + +#include "main.h" + +#define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin" +#define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin" +#define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin" +#define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin" +#define SD8887_DEFAULT_FW_NAME "mrvl/sd8887_uapsta.bin" +#define SD8801_DEFAULT_FW_NAME "mrvl/sd8801_uapsta.bin" +#define SD8997_DEFAULT_FW_NAME "mrvl/sd8997_uapsta.bin" + +#define BLOCK_MODE 1 +#define BYTE_MODE 0 + +#define REG_PORT 0 + +#define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff + +#define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000 + +#define MWIFIEX_MAX_FUNC2_REG_NUM 13 +#define MWIFIEX_SDIO_SCRATCH_SIZE 10 + +#define SDIO_MPA_ADDR_BASE 0x1000 +#define CTRL_PORT 0 +#define CTRL_PORT_MASK 0x0001 + +#define CMD_PORT_UPLD_INT_MASK (0x1U<<6) +#define CMD_PORT_DNLD_INT_MASK (0x1U<<7) +#define HOST_TERM_CMD53 (0x1U << 2) +#define REG_PORT 0 +#define MEM_PORT 0x10000 + +#define CMD53_NEW_MODE (0x1U << 0) +#define CMD_PORT_RD_LEN_EN (0x1U << 2) +#define CMD_PORT_AUTO_EN (0x1U << 0) +#define CMD_PORT_SLCT 0x8000 +#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U) +#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U) + +#define MWIFIEX_MP_AGGR_BUF_SIZE_16K (16384) +#define MWIFIEX_MP_AGGR_BUF_SIZE_32K (32768) +/* we leave one block of 256 bytes for DMA alignment*/ +#define MWIFIEX_MP_AGGR_BUF_SIZE_MAX (65280) + +/* Misc. Config Register : Auto Re-enable interrupts */ +#define AUTO_RE_ENABLE_INT BIT(4) + +/* Host Control Registers : Configuration */ +#define CONFIGURATION_REG 0x00 +/* Host Control Registers : Host power up */ +#define HOST_POWER_UP (0x1U << 1) + +/* Host Control Registers : Upload host interrupt mask */ +#define UP_LD_HOST_INT_MASK (0x1U) +/* Host Control Registers : Download host interrupt mask */ +#define DN_LD_HOST_INT_MASK (0x2U) + +/* Host Control Registers : Upload host interrupt status */ +#define UP_LD_HOST_INT_STATUS (0x1U) +/* Host Control Registers : Download host interrupt status */ +#define DN_LD_HOST_INT_STATUS (0x2U) + +/* Host Control Registers : Host interrupt status */ +#define CARD_INT_STATUS_REG 0x28 + +/* Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/* Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/* Max retry number of CMD53 write */ +#define MAX_WRITE_IOMEM_RETRY 2 + +/* SDIO Tx aggregation in progress ? */ +#define MP_TX_AGGR_IN_PROGRESS(a) (a->mpa_tx.pkt_cnt > 0) + +/* SDIO Tx aggregation buffer room for next packet ? */ +#define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ((a->mpa_tx.buf_len+len) \ + <= a->mpa_tx.buf_size) + +/* Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */ +#define MP_TX_AGGR_BUF_PUT(a, payload, pkt_len, port) do { \ + memmove(&a->mpa_tx.buf[a->mpa_tx.buf_len], \ + payload, pkt_len); \ + a->mpa_tx.buf_len += pkt_len; \ + if (!a->mpa_tx.pkt_cnt) \ + a->mpa_tx.start_port = port; \ + if (a->mpa_tx.start_port <= port) \ + a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \ + else \ + a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+ \ + (a->max_ports - \ + a->mp_end_port))); \ + a->mpa_tx.pkt_cnt++; \ +} while (0) + +/* SDIO Tx aggregation limit ? */ +#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \ + (a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit) + +/* Reset SDIO Tx aggregation buffer parameters */ +#define MP_TX_AGGR_BUF_RESET(a) do { \ + a->mpa_tx.pkt_cnt = 0; \ + a->mpa_tx.buf_len = 0; \ + a->mpa_tx.ports = 0; \ + a->mpa_tx.start_port = 0; \ +} while (0) + +/* SDIO Rx aggregation limit ? */ +#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \ + (a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit) + +/* SDIO Rx aggregation in progress ? */ +#define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0) + +/* SDIO Rx aggregation buffer room for next packet ? */ +#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \ + ((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size) + +/* Reset SDIO Rx aggregation buffer parameters */ +#define MP_RX_AGGR_BUF_RESET(a) do { \ + a->mpa_rx.pkt_cnt = 0; \ + a->mpa_rx.buf_len = 0; \ + a->mpa_rx.ports = 0; \ + a->mpa_rx.start_port = 0; \ +} while (0) + +/* data structure for SDIO MPA TX */ +struct mwifiex_sdio_mpa_tx { + /* multiport tx aggregation buffer pointer */ + u8 *buf; + u32 buf_len; + u32 pkt_cnt; + u32 ports; + u16 start_port; + u8 enabled; + u32 buf_size; + u32 pkt_aggr_limit; +}; + +struct mwifiex_sdio_mpa_rx { + u8 *buf; + u32 buf_len; + u32 pkt_cnt; + u32 ports; + u16 start_port; + + struct sk_buff **skb_arr; + u32 *len_arr; + + u8 enabled; + u32 buf_size; + u32 pkt_aggr_limit; +}; + +int mwifiex_bus_register(void); +void mwifiex_bus_unregister(void); + +struct mwifiex_sdio_card_reg { + u8 start_rd_port; + u8 start_wr_port; + u8 base_0_reg; + u8 base_1_reg; + u8 poll_reg; + u8 host_int_enable; + u8 host_int_rsr_reg; + u8 host_int_status_reg; + u8 host_int_mask_reg; + u8 status_reg_0; + u8 status_reg_1; + u8 sdio_int_mask; + u32 data_port_mask; + u8 io_port_0_reg; + u8 io_port_1_reg; + u8 io_port_2_reg; + u8 max_mp_regs; + u8 rd_bitmap_l; + u8 rd_bitmap_u; + u8 rd_bitmap_1l; + u8 rd_bitmap_1u; + u8 wr_bitmap_l; + u8 wr_bitmap_u; + u8 wr_bitmap_1l; + u8 wr_bitmap_1u; + u8 rd_len_p0_l; + u8 rd_len_p0_u; + u8 card_misc_cfg_reg; + u8 card_cfg_2_1_reg; + u8 cmd_rd_len_0; + u8 cmd_rd_len_1; + u8 cmd_rd_len_2; + u8 cmd_rd_len_3; + u8 cmd_cfg_0; + u8 cmd_cfg_1; + u8 cmd_cfg_2; + u8 cmd_cfg_3; + u8 fw_dump_host_ready; + u8 fw_dump_ctrl; + u8 fw_dump_start; + u8 fw_dump_end; + u8 func1_dump_reg_start; + u8 func1_dump_reg_end; + u8 func1_scratch_reg; + u8 func1_spec_reg_num; + u8 func1_spec_reg_table[MWIFIEX_MAX_FUNC2_REG_NUM]; +}; + +struct sdio_mmc_card { + struct sdio_func *func; + struct mwifiex_adapter *adapter; + + const char *firmware; + const struct mwifiex_sdio_card_reg *reg; + u8 max_ports; + u8 mp_agg_pkt_limit; + u16 tx_buf_size; + u32 mp_tx_agg_buf_size; + u32 mp_rx_agg_buf_size; + + u32 mp_rd_bitmap; + u32 mp_wr_bitmap; + + u16 mp_end_port; + u32 mp_data_port_mask; + + u8 curr_rd_port; + u8 curr_wr_port; + + u8 *mp_regs; + bool supports_sdio_new_mode; + bool has_control_mask; + bool can_dump_fw; + bool fw_dump_enh; + bool can_auto_tdls; + bool can_ext_scan; + + struct mwifiex_sdio_mpa_tx mpa_tx; + struct mwifiex_sdio_mpa_rx mpa_rx; + + /* needed for card reset */ + const struct sdio_device_id *device_id; +}; + +struct mwifiex_sdio_device { + const char *firmware; + const struct mwifiex_sdio_card_reg *reg; + u8 max_ports; + u8 mp_agg_pkt_limit; + u16 tx_buf_size; + u32 mp_tx_agg_buf_size; + u32 mp_rx_agg_buf_size; + bool supports_sdio_new_mode; + bool has_control_mask; + bool can_dump_fw; + bool fw_dump_enh; + bool can_auto_tdls; + bool can_ext_scan; +}; + +static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { + .start_rd_port = 1, + .start_wr_port = 1, + .base_0_reg = 0x0040, + .base_1_reg = 0x0041, + .poll_reg = 0x30, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK, + .host_int_rsr_reg = 0x1, + .host_int_mask_reg = 0x02, + .host_int_status_reg = 0x03, + .status_reg_0 = 0x60, + .status_reg_1 = 0x61, + .sdio_int_mask = 0x3f, + .data_port_mask = 0x0000fffe, + .io_port_0_reg = 0x78, + .io_port_1_reg = 0x79, + .io_port_2_reg = 0x7A, + .max_mp_regs = 64, + .rd_bitmap_l = 0x04, + .rd_bitmap_u = 0x05, + .wr_bitmap_l = 0x06, + .wr_bitmap_u = 0x07, + .rd_len_p0_l = 0x08, + .rd_len_p0_u = 0x09, + .card_misc_cfg_reg = 0x6c, + .func1_dump_reg_start = 0x0, + .func1_dump_reg_end = 0x9, + .func1_scratch_reg = 0x60, + .func1_spec_reg_num = 5, + .func1_spec_reg_table = {0x28, 0x30, 0x34, 0x38, 0x3c}, +}; + +static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = { + .start_rd_port = 0, + .start_wr_port = 0, + .base_0_reg = 0x60, + .base_1_reg = 0x61, + .poll_reg = 0x50, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | + CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, + .host_int_rsr_reg = 0x1, + .host_int_status_reg = 0x03, + .host_int_mask_reg = 0x02, + .status_reg_0 = 0xc0, + .status_reg_1 = 0xc1, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .io_port_0_reg = 0xD8, + .io_port_1_reg = 0xD9, + .io_port_2_reg = 0xDA, + .max_mp_regs = 184, + .rd_bitmap_l = 0x04, + .rd_bitmap_u = 0x05, + .rd_bitmap_1l = 0x06, + .rd_bitmap_1u = 0x07, + .wr_bitmap_l = 0x08, + .wr_bitmap_u = 0x09, + .wr_bitmap_1l = 0x0a, + .wr_bitmap_1u = 0x0b, + .rd_len_p0_l = 0x0c, + .rd_len_p0_u = 0x0d, + .card_misc_cfg_reg = 0xcc, + .card_cfg_2_1_reg = 0xcd, + .cmd_rd_len_0 = 0xb4, + .cmd_rd_len_1 = 0xb5, + .cmd_rd_len_2 = 0xb6, + .cmd_rd_len_3 = 0xb7, + .cmd_cfg_0 = 0xb8, + .cmd_cfg_1 = 0xb9, + .cmd_cfg_2 = 0xba, + .cmd_cfg_3 = 0xbb, + .fw_dump_host_ready = 0xee, + .fw_dump_ctrl = 0xe2, + .fw_dump_start = 0xe3, + .fw_dump_end = 0xea, + .func1_dump_reg_start = 0x0, + .func1_dump_reg_end = 0xb, + .func1_scratch_reg = 0xc0, + .func1_spec_reg_num = 8, + .func1_spec_reg_table = {0x4C, 0x50, 0x54, 0x55, 0x58, + 0x59, 0x5c, 0x5d}, +}; + +static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8997 = { + .start_rd_port = 0, + .start_wr_port = 0, + .base_0_reg = 0xF8, + .base_1_reg = 0xF9, + .poll_reg = 0x5C, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | + CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, + .host_int_rsr_reg = 0x4, + .host_int_status_reg = 0x0C, + .host_int_mask_reg = 0x08, + .status_reg_0 = 0xE8, + .status_reg_1 = 0xE9, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .io_port_0_reg = 0xE4, + .io_port_1_reg = 0xE5, + .io_port_2_reg = 0xE6, + .max_mp_regs = 196, + .rd_bitmap_l = 0x10, + .rd_bitmap_u = 0x11, + .rd_bitmap_1l = 0x12, + .rd_bitmap_1u = 0x13, + .wr_bitmap_l = 0x14, + .wr_bitmap_u = 0x15, + .wr_bitmap_1l = 0x16, + .wr_bitmap_1u = 0x17, + .rd_len_p0_l = 0x18, + .rd_len_p0_u = 0x19, + .card_misc_cfg_reg = 0xd8, + .card_cfg_2_1_reg = 0xd9, + .cmd_rd_len_0 = 0xc0, + .cmd_rd_len_1 = 0xc1, + .cmd_rd_len_2 = 0xc2, + .cmd_rd_len_3 = 0xc3, + .cmd_cfg_0 = 0xc4, + .cmd_cfg_1 = 0xc5, + .cmd_cfg_2 = 0xc6, + .cmd_cfg_3 = 0xc7, + .fw_dump_host_ready = 0xcc, + .fw_dump_ctrl = 0xf0, + .fw_dump_start = 0xf1, + .fw_dump_end = 0xf8, + .func1_dump_reg_start = 0x10, + .func1_dump_reg_end = 0x17, + .func1_scratch_reg = 0xe8, + .func1_spec_reg_num = 13, + .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, + 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, + 0x6a}, +}; + +static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = { + .start_rd_port = 0, + .start_wr_port = 0, + .base_0_reg = 0x6C, + .base_1_reg = 0x6D, + .poll_reg = 0x5C, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | + CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, + .host_int_rsr_reg = 0x4, + .host_int_status_reg = 0x0C, + .host_int_mask_reg = 0x08, + .status_reg_0 = 0x90, + .status_reg_1 = 0x91, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .io_port_0_reg = 0xE4, + .io_port_1_reg = 0xE5, + .io_port_2_reg = 0xE6, + .max_mp_regs = 196, + .rd_bitmap_l = 0x10, + .rd_bitmap_u = 0x11, + .rd_bitmap_1l = 0x12, + .rd_bitmap_1u = 0x13, + .wr_bitmap_l = 0x14, + .wr_bitmap_u = 0x15, + .wr_bitmap_1l = 0x16, + .wr_bitmap_1u = 0x17, + .rd_len_p0_l = 0x18, + .rd_len_p0_u = 0x19, + .card_misc_cfg_reg = 0xd8, + .card_cfg_2_1_reg = 0xd9, + .cmd_rd_len_0 = 0xc0, + .cmd_rd_len_1 = 0xc1, + .cmd_rd_len_2 = 0xc2, + .cmd_rd_len_3 = 0xc3, + .cmd_cfg_0 = 0xc4, + .cmd_cfg_1 = 0xc5, + .cmd_cfg_2 = 0xc6, + .cmd_cfg_3 = 0xc7, + .func1_dump_reg_start = 0x10, + .func1_dump_reg_end = 0x17, + .func1_scratch_reg = 0x90, + .func1_spec_reg_num = 13, + .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, + 0x61, 0x62, 0x64, 0x65, 0x66, + 0x68, 0x69, 0x6a}, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { + .firmware = SD8786_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .supports_sdio_new_mode = false, + .has_control_mask = true, + .can_dump_fw = false, + .can_auto_tdls = false, + .can_ext_scan = false, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { + .firmware = SD8787_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .supports_sdio_new_mode = false, + .has_control_mask = true, + .can_dump_fw = false, + .can_auto_tdls = false, + .can_ext_scan = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { + .firmware = SD8797_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .supports_sdio_new_mode = false, + .has_control_mask = true, + .can_dump_fw = false, + .can_auto_tdls = false, + .can_ext_scan = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { + .firmware = SD8897_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd8897, + .max_ports = 32, + .mp_agg_pkt_limit = 16, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, + .supports_sdio_new_mode = true, + .has_control_mask = false, + .can_dump_fw = true, + .can_auto_tdls = false, + .can_ext_scan = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = { + .firmware = SD8997_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd8997, + .max_ports = 32, + .mp_agg_pkt_limit = 16, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, + .supports_sdio_new_mode = true, + .has_control_mask = false, + .can_dump_fw = true, + .fw_dump_enh = true, + .can_auto_tdls = false, + .can_ext_scan = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = { + .firmware = SD8887_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd8887, + .max_ports = 32, + .mp_agg_pkt_limit = 16, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, + .supports_sdio_new_mode = true, + .has_control_mask = false, + .can_dump_fw = false, + .can_auto_tdls = true, + .can_ext_scan = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = { + .firmware = SD8801_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .supports_sdio_new_mode = false, + .has_control_mask = true, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .can_dump_fw = false, + .can_auto_tdls = false, + .can_ext_scan = true, +}; + +/* + * .cmdrsp_complete handler + */ +static inline int mwifiex_sdio_cmdrsp_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); + return 0; +} + +/* + * .event_complete handler + */ +static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); + return 0; +} + +static inline bool +mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card) +{ + u8 tmp; + + if (card->curr_rd_port < card->mpa_rx.start_port) { + if (card->supports_sdio_new_mode) + tmp = card->mp_end_port >> 1; + else + tmp = card->mp_agg_pkt_limit; + + if (((card->max_ports - card->mpa_rx.start_port) + + card->curr_rd_port) >= tmp) + return true; + } + + if (!card->supports_sdio_new_mode) + return false; + + if ((card->curr_rd_port - card->mpa_rx.start_port) >= + (card->mp_end_port >> 1)) + return true; + + return false; +} + +static inline bool +mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card) +{ + u16 tmp; + + if (card->curr_wr_port < card->mpa_tx.start_port) { + if (card->supports_sdio_new_mode) + tmp = card->mp_end_port >> 1; + else + tmp = card->mp_agg_pkt_limit; + + if (((card->max_ports - card->mpa_tx.start_port) + + card->curr_wr_port) >= tmp) + return true; + } + + if (!card->supports_sdio_new_mode) + return false; + + if ((card->curr_wr_port - card->mpa_tx.start_port) >= + (card->mp_end_port >> 1)) + return true; + + return false; +} + +/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ +static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card, + u16 rx_len, u8 port) +{ + card->mpa_rx.buf_len += rx_len; + + if (!card->mpa_rx.pkt_cnt) + card->mpa_rx.start_port = port; + + if (card->supports_sdio_new_mode) { + card->mpa_rx.ports |= (1 << port); + } else { + if (card->mpa_rx.start_port <= port) + card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt); + else + card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1); + } + card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = NULL; + card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = rx_len; + card->mpa_rx.pkt_cnt++; +} +#endif /* _MWIFIEX_SDIO_H */ diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c new file mode 100644 index 000000000000..e486867a4c67 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -0,0 +1,2282 @@ +/* + * Marvell Wireless LAN device driver: station command handling + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11ac.h" + +static bool drcs; +module_param(drcs, bool, 0644); +MODULE_PARM_DESC(drcs, "multi-channel operation:1, single-channel operation:0"); + +static bool disable_auto_ds; +module_param(disable_auto_ds, bool, 0); +MODULE_PARM_DESC(disable_auto_ds, + "deepsleep enabled=0(default), deepsleep disabled=1"); +/* + * This function prepares command to set/get RSSI information. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting data/beacon average factors + * - Resetting SNR/NF/RSSI values in private structure + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_rssi_info(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action) +{ + cmd->command = cpu_to_le16(HostCmd_CMD_RSSI_INFO); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rssi_info) + + S_DS_GEN); + cmd->params.rssi_info.action = cpu_to_le16(cmd_action); + cmd->params.rssi_info.ndata = cpu_to_le16(priv->data_avg_factor); + cmd->params.rssi_info.nbcn = cpu_to_le16(priv->bcn_avg_factor); + + /* Reset SNR/NF/RSSI values in private structure */ + priv->data_rssi_last = 0; + priv->data_nf_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->bcn_rssi_last = 0; + priv->bcn_nf_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + + return 0; +} + +/* + * This function prepares command to set MAC control. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_mac_control(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u16 *action) +{ + struct host_cmd_ds_mac_control *mac_ctrl = &cmd->params.mac_ctrl; + + if (cmd_action != HostCmd_ACT_GEN_SET) { + mwifiex_dbg(priv->adapter, ERROR, + "mac_control: only support set cmd\n"); + return -1; + } + + cmd->command = cpu_to_le16(HostCmd_CMD_MAC_CONTROL); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN); + mac_ctrl->action = cpu_to_le16(*action); + + return 0; +} + +/* + * This function prepares command to set/get SNMP MIB. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting SNMP MIB OID number and value + * (as required) + * - Ensuring correct endian-ness + * + * The following SNMP MIB OIDs are supported - + * - FRAG_THRESH_I : Fragmentation threshold + * - RTS_THRESH_I : RTS threshold + * - SHORT_RETRY_LIM_I : Short retry limit + * - DOT11D_I : 11d support + */ +static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + u16 *ul_temp) +{ + struct host_cmd_ds_802_11_snmp_mib *snmp_mib = &cmd->params.smib; + + mwifiex_dbg(priv->adapter, CMD, + "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib) + - 1 + S_DS_GEN); + + snmp_mib->oid = cpu_to_le16((u16)cmd_oid); + if (cmd_action == HostCmd_ACT_GEN_GET) { + snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_GET); + snmp_mib->buf_size = cpu_to_le16(MAX_SNMP_BUF_SIZE); + le16_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE); + } else if (cmd_action == HostCmd_ACT_GEN_SET) { + snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET); + snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); + *((__le16 *) (snmp_mib->value)) = cpu_to_le16(*ul_temp); + le16_add_cpu(&cmd->size, sizeof(u16)); + } + + mwifiex_dbg(priv->adapter, CMD, + "cmd: SNMP_CMD: Action=0x%x, OID=0x%x,\t" + "OIDSize=0x%x, Value=0x%x\n", + cmd_action, cmd_oid, le16_to_cpu(snmp_mib->buf_size), + le16_to_cpu(*(__le16 *)snmp_mib->value)); + return 0; +} + +/* + * This function prepares command to get log. + * + * Preparation includes - + * - Setting command ID and proper size + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_get_log(struct host_cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_GET_LOG); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_get_log) + + S_DS_GEN); + return 0; +} + +/* + * This function prepares command to set/get Tx data rate configuration. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting configuration index, rate scope and rate drop pattern + * parameters (as required) + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u16 *pbitmap_rates) +{ + struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg; + struct mwifiex_rate_scope *rate_scope; + struct mwifiex_rate_drop_pattern *rate_drop; + u32 i; + + cmd->command = cpu_to_le16(HostCmd_CMD_TX_RATE_CFG); + + rate_cfg->action = cpu_to_le16(cmd_action); + rate_cfg->cfg_index = 0; + + rate_scope = (struct mwifiex_rate_scope *) ((u8 *) rate_cfg + + sizeof(struct host_cmd_ds_tx_rate_cfg)); + rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE); + rate_scope->length = cpu_to_le16 + (sizeof(*rate_scope) - sizeof(struct mwifiex_ie_types_header)); + if (pbitmap_rates != NULL) { + rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); + rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); + for (i = 0; + i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); + i++) + rate_scope->ht_mcs_rate_bitmap[i] = + cpu_to_le16(pbitmap_rates[2 + i]); + if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { + for (i = 0; + i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + rate_scope->vht_mcs_rate_bitmap[i] = + cpu_to_le16(pbitmap_rates[10 + i]); + } + } else { + rate_scope->hr_dsss_rate_bitmap = + cpu_to_le16(priv->bitmap_rates[0]); + rate_scope->ofdm_rate_bitmap = + cpu_to_le16(priv->bitmap_rates[1]); + for (i = 0; + i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); + i++) + rate_scope->ht_mcs_rate_bitmap[i] = + cpu_to_le16(priv->bitmap_rates[2 + i]); + if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { + for (i = 0; + i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + rate_scope->vht_mcs_rate_bitmap[i] = + cpu_to_le16(priv->bitmap_rates[10 + i]); + } + } + + rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope + + sizeof(struct mwifiex_rate_scope)); + rate_drop->type = cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL); + rate_drop->length = cpu_to_le16(sizeof(rate_drop->rate_drop_mode)); + rate_drop->rate_drop_mode = 0; + + cmd->size = + cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_tx_rate_cfg) + + sizeof(struct mwifiex_rate_scope) + + sizeof(struct mwifiex_rate_drop_pattern)); + + return 0; +} + +/* + * This function prepares command to set/get Tx power configuration. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting Tx power mode, power group TLV + * (as required) + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd, + u16 cmd_action, + struct host_cmd_ds_txpwr_cfg *txp) +{ + struct mwifiex_types_power_group *pg_tlv; + struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg; + + cmd->command = cpu_to_le16(HostCmd_CMD_TXPWR_CFG); + cmd->size = + cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg)); + switch (cmd_action) { + case HostCmd_ACT_GEN_SET: + if (txp->mode) { + pg_tlv = (struct mwifiex_types_power_group + *) ((unsigned long) txp + + sizeof(struct host_cmd_ds_txpwr_cfg)); + memmove(cmd_txp_cfg, txp, + sizeof(struct host_cmd_ds_txpwr_cfg) + + sizeof(struct mwifiex_types_power_group) + + le16_to_cpu(pg_tlv->length)); + + pg_tlv = (struct mwifiex_types_power_group *) ((u8 *) + cmd_txp_cfg + + sizeof(struct host_cmd_ds_txpwr_cfg)); + cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + + sizeof(struct mwifiex_types_power_group) + + le16_to_cpu(pg_tlv->length)); + } else { + memmove(cmd_txp_cfg, txp, sizeof(*txp)); + } + cmd_txp_cfg->action = cpu_to_le16(cmd_action); + break; + case HostCmd_ACT_GEN_GET: + cmd_txp_cfg->action = cpu_to_le16(cmd_action); + break; + } + + return 0; +} + +/* + * This function prepares command to get RF Tx power. + */ +static int mwifiex_cmd_rf_tx_power(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_ds_rf_tx_pwr *txp = &cmd->params.txp; + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_tx_pwr) + + S_DS_GEN); + cmd->command = cpu_to_le16(HostCmd_CMD_RF_TX_PWR); + txp->action = cpu_to_le16(cmd_action); + + return 0; +} + +/* + * This function prepares command to set rf antenna. + */ +static int mwifiex_cmd_rf_antenna(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, + struct mwifiex_ds_ant_cfg *ant_cfg) +{ + struct host_cmd_ds_rf_ant_mimo *ant_mimo = &cmd->params.ant_mimo; + struct host_cmd_ds_rf_ant_siso *ant_siso = &cmd->params.ant_siso; + + cmd->command = cpu_to_le16(HostCmd_CMD_RF_ANTENNA); + + if (cmd_action != HostCmd_ACT_GEN_SET) + return 0; + + if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) { + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_mimo) + + S_DS_GEN); + ant_mimo->action_tx = cpu_to_le16(HostCmd_ACT_SET_TX); + ant_mimo->tx_ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); + ant_mimo->action_rx = cpu_to_le16(HostCmd_ACT_SET_RX); + ant_mimo->rx_ant_mode = cpu_to_le16((u16)ant_cfg->rx_ant); + } else { + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_siso) + + S_DS_GEN); + ant_siso->action = cpu_to_le16(HostCmd_ACT_SET_BOTH); + ant_siso->ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); + } + + return 0; +} + +/* + * This function prepares command to set Host Sleep configuration. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting Host Sleep action, conditions, ARP filters + * (as required) + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, + struct mwifiex_hs_config_param *hscfg_param) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; + bool hs_activate = false; + + if (!hscfg_param) + /* New Activate command */ + hs_activate = true; + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH); + + if (!hs_activate && + (hscfg_param->conditions != cpu_to_le32(HS_CFG_CANCEL)) && + ((adapter->arp_filter_size > 0) && + (adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) { + mwifiex_dbg(adapter, CMD, + "cmd: Attach %d bytes ArpFilter to HSCfg cmd\n", + adapter->arp_filter_size); + memcpy(((u8 *) hs_cfg) + + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh), + adapter->arp_filter, adapter->arp_filter_size); + cmd->size = cpu_to_le16 + (adapter->arp_filter_size + + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) + + S_DS_GEN); + } else { + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct + host_cmd_ds_802_11_hs_cfg_enh)); + } + if (hs_activate) { + hs_cfg->action = cpu_to_le16(HS_ACTIVATE); + hs_cfg->params.hs_activate.resp_ctrl = cpu_to_le16(RESP_NEEDED); + } else { + hs_cfg->action = cpu_to_le16(HS_CONFIGURE); + hs_cfg->params.hs_config.conditions = hscfg_param->conditions; + hs_cfg->params.hs_config.gpio = hscfg_param->gpio; + hs_cfg->params.hs_config.gap = hscfg_param->gap; + mwifiex_dbg(adapter, CMD, + "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", + hs_cfg->params.hs_config.conditions, + hs_cfg->params.hs_config.gpio, + hs_cfg->params.hs_config.gap); + } + + return 0; +} + +/* + * This function prepares command to set/get MAC address. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting MAC address (for SET only) + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_802_11_mac_address(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action) +{ + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_MAC_ADDRESS); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.mac_addr.action = cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) + memcpy(cmd->params.mac_addr.mac_addr, priv->curr_addr, + ETH_ALEN); + return 0; +} + +/* + * This function prepares command to set MAC multicast address. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting MAC multicast address + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_mac_multicast_adr(struct host_cmd_ds_command *cmd, + u16 cmd_action, + struct mwifiex_multicast_list *mcast_list) +{ + struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr; + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) + + S_DS_GEN); + cmd->command = cpu_to_le16(HostCmd_CMD_MAC_MULTICAST_ADR); + + mcast_addr->action = cpu_to_le16(cmd_action); + mcast_addr->num_of_adrs = + cpu_to_le16((u16) mcast_list->num_multicast_addr); + memcpy(mcast_addr->mac_list, mcast_list->mac_list, + mcast_list->num_multicast_addr * ETH_ALEN); + + return 0; +} + +/* + * This function prepares command to deauthenticate. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting AP MAC address and reason code + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_802_11_deauthenticate(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u8 *mac) +{ + struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_DEAUTHENTICATE); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_deauthenticate) + + S_DS_GEN); + + /* Set AP MAC address */ + memcpy(deauth->mac_addr, mac, ETH_ALEN); + + mwifiex_dbg(priv->adapter, CMD, "cmd: Deauth: %pM\n", deauth->mac_addr); + + deauth->reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); + + return 0; +} + +/* + * This function prepares command to stop Ad-Hoc network. + * + * Preparation includes - + * - Setting command ID and proper size + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_802_11_ad_hoc_stop(struct host_cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_STOP); + cmd->size = cpu_to_le16(S_DS_GEN); + return 0; +} + +/* + * This function sets WEP key(s) to key parameter TLV(s). + * + * Multi-key parameter TLVs are supported, so we can send multiple + * WEP keys in a single buffer. + */ +static int +mwifiex_set_keyparamset_wep(struct mwifiex_private *priv, + struct mwifiex_ie_type_key_param_set *key_param_set, + u16 *key_param_len) +{ + int cur_key_param_len; + u8 i; + + /* Multi-key_param_set TLV is supported */ + for (i = 0; i < NUM_WEP_KEYS; i++) { + if ((priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP40) || + (priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP104)) { + key_param_set->type = + cpu_to_le16(TLV_TYPE_KEY_MATERIAL); +/* Key_param_set WEP fixed length */ +#define KEYPARAMSET_WEP_FIXED_LEN 8 + key_param_set->length = cpu_to_le16((u16) + (priv->wep_key[i]. + key_length + + KEYPARAMSET_WEP_FIXED_LEN)); + key_param_set->key_type_id = + cpu_to_le16(KEY_TYPE_ID_WEP); + key_param_set->key_info = + cpu_to_le16(KEY_ENABLED | KEY_UNICAST | + KEY_MCAST); + key_param_set->key_len = + cpu_to_le16(priv->wep_key[i].key_length); + /* Set WEP key index */ + key_param_set->key[0] = i; + /* Set default Tx key flag */ + if (i == + (priv-> + wep_key_curr_index & HostCmd_WEP_KEY_INDEX_MASK)) + key_param_set->key[1] = 1; + else + key_param_set->key[1] = 0; + memmove(&key_param_set->key[2], + priv->wep_key[i].key_material, + priv->wep_key[i].key_length); + + cur_key_param_len = priv->wep_key[i].key_length + + KEYPARAMSET_WEP_FIXED_LEN + + sizeof(struct mwifiex_ie_types_header); + *key_param_len += (u16) cur_key_param_len; + key_param_set = + (struct mwifiex_ie_type_key_param_set *) + ((u8 *)key_param_set + + cur_key_param_len); + } else if (!priv->wep_key[i].key_length) { + continue; + } else { + mwifiex_dbg(priv->adapter, ERROR, + "key%d Length = %d is incorrect\n", + (i + 1), priv->wep_key[i].key_length); + return -1; + } + } + + return 0; +} + +/* This function populates key material v2 command + * to set network key for AES & CMAC AES. + */ +static int mwifiex_set_aes_key_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_ds_encrypt_key *enc_key, + struct host_cmd_ds_802_11_key_material_v2 *km) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u16 size, len = KEY_PARAMS_FIXED_LEN; + + if (enc_key->is_igtk_key) { + mwifiex_dbg(adapter, INFO, + "%s: Set CMAC AES Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.cmac_aes.ipn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST); + km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK); + km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC; + km->key_param_set.key_params.cmac_aes.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.cmac_aes.key, + enc_key->key_material, enc_key->key_len); + len += sizeof(struct mwifiex_cmac_aes_param); + } else { + mwifiex_dbg(adapter, INFO, + "%s: Set AES Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.aes.pn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_type = KEY_TYPE_ID_AES; + km->key_param_set.key_params.aes.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.aes.key, + enc_key->key_material, enc_key->key_len); + len += sizeof(struct mwifiex_aes_param); + } + + km->key_param_set.len = cpu_to_le16(len); + size = len + sizeof(struct mwifiex_ie_types_header) + + sizeof(km->action) + S_DS_GEN; + cmd->size = cpu_to_le16(size); + + return 0; +} + +/* This function prepares command to set/get/reset network key(s). + * This function prepares key material command for V2 format. + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting WEP keys, WAPI keys or WPA keys along with required + * encryption (TKIP, AES) (as required) + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_key_material_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u8 *mac = enc_key->mac_addr; + u16 key_info, len = KEY_PARAMS_FIXED_LEN; + struct host_cmd_ds_802_11_key_material_v2 *km = + &cmd->params.key_material_v2; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); + km->action = cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_GET) { + mwifiex_dbg(adapter, INFO, "%s: Get key\n", __func__); + km->key_param_set.key_idx = + enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + + if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) + key_info = KEY_UNICAST; + else + key_info = KEY_MCAST; + + if (enc_key->is_igtk_key) + key_info |= KEY_IGTK; + + km->key_param_set.key_info = cpu_to_le16(key_info); + + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(km->action)); + return 0; + } + + memset(&km->key_param_set, 0, + sizeof(struct mwifiex_ie_type_key_param_set_v2)); + + if (enc_key->key_disable) { + mwifiex_dbg(adapter, INFO, "%s: Remove key\n", __func__); + km->action = cpu_to_le16(HostCmd_ACT_GEN_REMOVE); + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); + km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; + key_info = KEY_MCAST | KEY_UNICAST; + km->key_param_set.key_info = cpu_to_le16(key_info); + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(km->action)); + return 0; + } + + km->action = cpu_to_le16(HostCmd_ACT_GEN_SET); + km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + key_info = KEY_ENABLED; + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + + if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) { + mwifiex_dbg(adapter, INFO, "%s: Set WEP Key\n", __func__); + len += sizeof(struct mwifiex_wep_param); + km->key_param_set.len = cpu_to_le16(len); + km->key_param_set.key_type = KEY_TYPE_ID_WEP; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + key_info |= KEY_MCAST | KEY_UNICAST; + } else { + if (enc_key->is_current_wep_key) { + key_info |= KEY_MCAST | KEY_UNICAST; + if (km->key_param_set.key_idx == + (priv->wep_key_curr_index & KEY_INDEX_MASK)) + key_info |= KEY_DEFAULT; + } else { + if (mac) { + if (is_broadcast_ether_addr(mac)) + key_info |= KEY_MCAST; + else + key_info |= KEY_UNICAST | + KEY_DEFAULT; + } else { + key_info |= KEY_MCAST; + } + } + } + km->key_param_set.key_info = cpu_to_le16(key_info); + + km->key_param_set.key_params.wep.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.wep.key, + enc_key->key_material, enc_key->key_len); + + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + return 0; + } + + if (is_broadcast_ether_addr(mac)) + key_info |= KEY_MCAST | KEY_RX_KEY; + else + key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY; + + if (enc_key->is_wapi_key) { + mwifiex_dbg(adapter, INFO, "%s: Set WAPI Key\n", __func__); + km->key_param_set.key_type = KEY_TYPE_ID_WAPI; + memcpy(km->key_param_set.key_params.wapi.pn, enc_key->pn, + PN_LEN); + km->key_param_set.key_params.wapi.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.wapi.key, + enc_key->key_material, enc_key->key_len); + if (is_broadcast_ether_addr(mac)) + priv->sec_info.wapi_key_on = true; + + if (!priv->sec_info.wapi_key_on) + key_info |= KEY_DEFAULT; + km->key_param_set.key_info = cpu_to_le16(key_info); + + len += sizeof(struct mwifiex_wapi_param); + km->key_param_set.len = cpu_to_le16(len); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + return 0; + } + + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { + key_info |= KEY_DEFAULT; + /* Enable unicast bit for WPA-NONE/ADHOC_AES */ + if (!priv->sec_info.wpa2_enabled && + !is_broadcast_ether_addr(mac)) + key_info |= KEY_UNICAST; + } else { + /* Enable default key for WPA/WPA2 */ + if (!priv->wpa_is_gtk_set) + key_info |= KEY_DEFAULT; + } + + km->key_param_set.key_info = cpu_to_le16(key_info); + + if (enc_key->key_len == WLAN_KEY_LEN_CCMP) + return mwifiex_set_aes_key_v2(priv, cmd, enc_key, km); + + if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { + mwifiex_dbg(adapter, INFO, + "%s: Set TKIP Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.tkip.pn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_type = KEY_TYPE_ID_TKIP; + km->key_param_set.key_params.tkip.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.tkip.key, + enc_key->key_material, enc_key->key_len); + + len += sizeof(struct mwifiex_tkip_param); + km->key_param_set.len = cpu_to_le16(len); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + } + + return 0; +} + +/* + * This function prepares command to set/get/reset network key(s). + * This function prepares key material command for V1 format. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting WEP keys, WAPI keys or WPA keys along with required + * encryption (TKIP, AES) (as required) + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_key_material_v1(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) +{ + struct host_cmd_ds_802_11_key_material *key_material = + &cmd->params.key_material; + struct host_cmd_tlv_mac_addr *tlv_mac; + u16 key_param_len = 0, cmd_size; + int ret = 0; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); + key_material->action = cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_GET) { + cmd->size = + cpu_to_le16(sizeof(key_material->action) + S_DS_GEN); + return ret; + } + + if (!enc_key) { + memset(&key_material->key_param_set, 0, + (NUM_WEP_KEYS * + sizeof(struct mwifiex_ie_type_key_param_set))); + ret = mwifiex_set_keyparamset_wep(priv, + &key_material->key_param_set, + &key_param_len); + cmd->size = cpu_to_le16(key_param_len + + sizeof(key_material->action) + S_DS_GEN); + return ret; + } else + memset(&key_material->key_param_set, 0, + sizeof(struct mwifiex_ie_type_key_param_set)); + if (enc_key->is_wapi_key) { + mwifiex_dbg(priv->adapter, INFO, "info: Set WAPI Key\n"); + key_material->key_param_set.key_type_id = + cpu_to_le16(KEY_TYPE_ID_WAPI); + if (cmd_oid == KEY_INFO_ENABLED) + key_material->key_param_set.key_info = + cpu_to_le16(KEY_ENABLED); + else + key_material->key_param_set.key_info = + cpu_to_le16(!KEY_ENABLED); + + key_material->key_param_set.key[0] = enc_key->key_index; + if (!priv->sec_info.wapi_key_on) + key_material->key_param_set.key[1] = 1; + else + /* set 0 when re-key */ + key_material->key_param_set.key[1] = 0; + + if (!is_broadcast_ether_addr(enc_key->mac_addr)) { + /* WAPI pairwise key: unicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_UNICAST); + } else { /* WAPI group key: multicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_MCAST); + priv->sec_info.wapi_key_on = true; + } + + key_material->key_param_set.type = + cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + key_material->key_param_set.key_len = + cpu_to_le16(WAPI_KEY_LEN); + memcpy(&key_material->key_param_set.key[2], + enc_key->key_material, enc_key->key_len); + memcpy(&key_material->key_param_set.key[2 + enc_key->key_len], + enc_key->pn, PN_LEN); + key_material->key_param_set.length = + cpu_to_le16(WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN); + + key_param_len = (WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN) + + sizeof(struct mwifiex_ie_types_header); + cmd->size = cpu_to_le16(sizeof(key_material->action) + + S_DS_GEN + key_param_len); + return ret; + } + if (enc_key->key_len == WLAN_KEY_LEN_CCMP) { + if (enc_key->is_igtk_key) { + mwifiex_dbg(priv->adapter, CMD, "cmd: CMAC_AES\n"); + key_material->key_param_set.key_type_id = + cpu_to_le16(KEY_TYPE_ID_AES_CMAC); + if (cmd_oid == KEY_INFO_ENABLED) + key_material->key_param_set.key_info = + cpu_to_le16(KEY_ENABLED); + else + key_material->key_param_set.key_info = + cpu_to_le16(!KEY_ENABLED); + + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_IGTK); + } else { + mwifiex_dbg(priv->adapter, CMD, "cmd: WPA_AES\n"); + key_material->key_param_set.key_type_id = + cpu_to_le16(KEY_TYPE_ID_AES); + if (cmd_oid == KEY_INFO_ENABLED) + key_material->key_param_set.key_info = + cpu_to_le16(KEY_ENABLED); + else + key_material->key_param_set.key_info = + cpu_to_le16(!KEY_ENABLED); + + if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) + /* AES pairwise key: unicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_UNICAST); + else /* AES group key: multicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_MCAST); + } + } else if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { + mwifiex_dbg(priv->adapter, CMD, "cmd: WPA_TKIP\n"); + key_material->key_param_set.key_type_id = + cpu_to_le16(KEY_TYPE_ID_TKIP); + key_material->key_param_set.key_info = + cpu_to_le16(KEY_ENABLED); + + if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) + /* TKIP pairwise key: unicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_UNICAST); + else /* TKIP group key: multicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_MCAST); + } + + if (key_material->key_param_set.key_type_id) { + key_material->key_param_set.type = + cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + key_material->key_param_set.key_len = + cpu_to_le16((u16) enc_key->key_len); + memcpy(key_material->key_param_set.key, enc_key->key_material, + enc_key->key_len); + key_material->key_param_set.length = + cpu_to_le16((u16) enc_key->key_len + + KEYPARAMSET_FIXED_LEN); + + key_param_len = (u16)(enc_key->key_len + KEYPARAMSET_FIXED_LEN) + + sizeof(struct mwifiex_ie_types_header); + + if (le16_to_cpu(key_material->key_param_set.key_type_id) == + KEY_TYPE_ID_AES_CMAC) { + struct mwifiex_cmac_param *param = + (void *)key_material->key_param_set.key; + + memcpy(param->ipn, enc_key->pn, IGTK_PN_LEN); + memcpy(param->key, enc_key->key_material, + WLAN_KEY_LEN_AES_CMAC); + + key_param_len = sizeof(struct mwifiex_cmac_param); + key_material->key_param_set.key_len = + cpu_to_le16(key_param_len); + key_param_len += KEYPARAMSET_FIXED_LEN; + key_material->key_param_set.length = + cpu_to_le16(key_param_len); + key_param_len += sizeof(struct mwifiex_ie_types_header); + } + + cmd->size = cpu_to_le16(sizeof(key_material->action) + S_DS_GEN + + key_param_len); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + tlv_mac = (void *)((u8 *)&key_material->key_param_set + + key_param_len); + tlv_mac->header.type = + cpu_to_le16(TLV_TYPE_STA_MAC_ADDR); + tlv_mac->header.len = cpu_to_le16(ETH_ALEN); + memcpy(tlv_mac->mac_addr, enc_key->mac_addr, ETH_ALEN); + cmd_size = key_param_len + S_DS_GEN + + sizeof(key_material->action) + + sizeof(struct host_cmd_tlv_mac_addr); + } else { + cmd_size = key_param_len + S_DS_GEN + + sizeof(key_material->action); + } + cmd->size = cpu_to_le16(cmd_size); + } + + return ret; +} + +/* Wrapper function for setting network key depending upon FW KEY API version */ +static int +mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) +{ + if (priv->adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) + return mwifiex_cmd_802_11_key_material_v2(priv, cmd, + cmd_action, cmd_oid, + enc_key); + + else + return mwifiex_cmd_802_11_key_material_v1(priv, cmd, + cmd_action, cmd_oid, + enc_key); +} + +/* + * This function prepares command to set/get 11d domain information. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting domain information fields (for SET only) + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_802_11d_domain_info(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11d_domain_info *domain_info = + &cmd->params.domain_info; + struct mwifiex_ietypes_domain_param_set *domain = + &domain_info->domain; + u8 no_of_triplet = adapter->domain_reg.no_of_triplet; + + mwifiex_dbg(adapter, INFO, + "info: 11D: no_of_triplet=0x%x\n", no_of_triplet); + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO); + domain_info->action = cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_GET) { + cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); + return 0; + } + + /* Set domain info fields */ + domain->header.type = cpu_to_le16(WLAN_EID_COUNTRY); + memcpy(domain->country_code, adapter->domain_reg.country_code, + sizeof(domain->country_code)); + + domain->header.len = + cpu_to_le16((no_of_triplet * + sizeof(struct ieee80211_country_ie_triplet)) + + sizeof(domain->country_code)); + + if (no_of_triplet) { + memcpy(domain->triplet, adapter->domain_reg.triplet, + no_of_triplet * sizeof(struct + ieee80211_country_ie_triplet)); + + cmd->size = cpu_to_le16(sizeof(domain_info->action) + + le16_to_cpu(domain->header.len) + + sizeof(struct mwifiex_ie_types_header) + + S_DS_GEN); + } else { + cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); + } + + return 0; +} + +/* + * This function prepares command to set/get IBSS coalescing status. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting status to enable or disable (for SET only) + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_ibss_coalescing_status(struct host_cmd_ds_command *cmd, + u16 cmd_action, u16 *enable) +{ + struct host_cmd_ds_802_11_ibss_status *ibss_coal = + &(cmd->params.ibss_coalescing); + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_IBSS_COALESCING_STATUS); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_ibss_status) + + S_DS_GEN); + cmd->result = 0; + ibss_coal->action = cpu_to_le16(cmd_action); + + switch (cmd_action) { + case HostCmd_ACT_GEN_SET: + if (enable) + ibss_coal->enable = cpu_to_le16(*enable); + else + ibss_coal->enable = 0; + break; + + /* In other case.. Nothing to do */ + case HostCmd_ACT_GEN_GET: + default: + break; + } + + return 0; +} + +/* This function prepares command buffer to get/set memory location value. + */ +static int +mwifiex_cmd_mem_access(struct host_cmd_ds_command *cmd, u16 cmd_action, + void *pdata_buf) +{ + struct mwifiex_ds_mem_rw *mem_rw = (void *)pdata_buf; + struct host_cmd_ds_mem_access *mem_access = (void *)&cmd->params.mem; + + cmd->command = cpu_to_le16(HostCmd_CMD_MEM_ACCESS); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mem_access) + + S_DS_GEN); + + mem_access->action = cpu_to_le16(cmd_action); + mem_access->addr = cpu_to_le32(mem_rw->addr); + mem_access->value = cpu_to_le32(mem_rw->value); + + return 0; +} + +/* + * This function prepares command to set/get register value. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting register offset (for both GET and SET) and + * register value (for SET only) + * - Ensuring correct endian-ness + * + * The following type of registers can be accessed with this function - + * - MAC register + * - BBP register + * - RF register + * - PMIC register + * - CAU register + * - EEPROM + */ +static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct mwifiex_ds_reg_rw *reg_rw = data_buf; + + switch (le16_to_cpu(cmd->command)) { + case HostCmd_CMD_MAC_REG_ACCESS: + { + struct host_cmd_ds_mac_reg_access *mac_reg; + + cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN); + mac_reg = &cmd->params.mac_reg; + mac_reg->action = cpu_to_le16(cmd_action); + mac_reg->offset = + cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); + mac_reg->value = reg_rw->value; + break; + } + case HostCmd_CMD_BBP_REG_ACCESS: + { + struct host_cmd_ds_bbp_reg_access *bbp_reg; + + cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN); + bbp_reg = &cmd->params.bbp_reg; + bbp_reg->action = cpu_to_le16(cmd_action); + bbp_reg->offset = + cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); + bbp_reg->value = (u8) le32_to_cpu(reg_rw->value); + break; + } + case HostCmd_CMD_RF_REG_ACCESS: + { + struct host_cmd_ds_rf_reg_access *rf_reg; + + cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN); + rf_reg = &cmd->params.rf_reg; + rf_reg->action = cpu_to_le16(cmd_action); + rf_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); + rf_reg->value = (u8) le32_to_cpu(reg_rw->value); + break; + } + case HostCmd_CMD_PMIC_REG_ACCESS: + { + struct host_cmd_ds_pmic_reg_access *pmic_reg; + + cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN); + pmic_reg = &cmd->params.pmic_reg; + pmic_reg->action = cpu_to_le16(cmd_action); + pmic_reg->offset = + cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); + pmic_reg->value = (u8) le32_to_cpu(reg_rw->value); + break; + } + case HostCmd_CMD_CAU_REG_ACCESS: + { + struct host_cmd_ds_rf_reg_access *cau_reg; + + cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN); + cau_reg = &cmd->params.rf_reg; + cau_reg->action = cpu_to_le16(cmd_action); + cau_reg->offset = + cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); + cau_reg->value = (u8) le32_to_cpu(reg_rw->value); + break; + } + case HostCmd_CMD_802_11_EEPROM_ACCESS: + { + struct mwifiex_ds_read_eeprom *rd_eeprom = data_buf; + struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom = + &cmd->params.eeprom; + + cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN); + cmd_eeprom->action = cpu_to_le16(cmd_action); + cmd_eeprom->offset = rd_eeprom->offset; + cmd_eeprom->byte_count = rd_eeprom->byte_count; + cmd_eeprom->value = 0; + break; + } + default: + return -1; + } + + return 0; +} + +/* + * This function prepares command to set PCI-Express + * host buffer configuration + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting host buffer configuration + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 action) +{ + struct host_cmd_ds_pcie_details *host_spec = + &cmd->params.pcie_host_spec; + struct pcie_service_card *card = priv->adapter->card; + + cmd->command = cpu_to_le16(HostCmd_CMD_PCIE_DESC_DETAILS); + cmd->size = cpu_to_le16(sizeof(struct + host_cmd_ds_pcie_details) + S_DS_GEN); + cmd->result = 0; + + memset(host_spec, 0, sizeof(struct host_cmd_ds_pcie_details)); + + if (action != HostCmd_ACT_GEN_SET) + return 0; + + /* Send the ring base addresses and count to firmware */ + host_spec->txbd_addr_lo = (u32)(card->txbd_ring_pbase); + host_spec->txbd_addr_hi = (u32)(((u64)card->txbd_ring_pbase)>>32); + host_spec->txbd_count = MWIFIEX_MAX_TXRX_BD; + host_spec->rxbd_addr_lo = (u32)(card->rxbd_ring_pbase); + host_spec->rxbd_addr_hi = (u32)(((u64)card->rxbd_ring_pbase)>>32); + host_spec->rxbd_count = MWIFIEX_MAX_TXRX_BD; + host_spec->evtbd_addr_lo = (u32)(card->evtbd_ring_pbase); + host_spec->evtbd_addr_hi = (u32)(((u64)card->evtbd_ring_pbase)>>32); + host_spec->evtbd_count = MWIFIEX_MAX_EVT_BD; + if (card->sleep_cookie_vbase) { + host_spec->sleep_cookie_addr_lo = + (u32)(card->sleep_cookie_pbase); + host_spec->sleep_cookie_addr_hi = + (u32)(((u64)(card->sleep_cookie_pbase)) >> 32); + mwifiex_dbg(priv->adapter, INFO, + "sleep_cook_lo phy addr: 0x%x\n", + host_spec->sleep_cookie_addr_lo); + } + + return 0; +} + +/* + * This function prepares command for event subscription, configuration + * and query. Events can be subscribed or unsubscribed. Current subscribed + * events can be queried. Also, current subscribed events are reported in + * every FW response. + */ +static int +mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_ds_misc_subsc_evt *subsc_evt_cfg) +{ + struct host_cmd_ds_802_11_subsc_evt *subsc_evt = &cmd->params.subsc_evt; + struct mwifiex_ie_types_rssi_threshold *rssi_tlv; + u16 event_bitmap; + u8 *pos; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SUBSCRIBE_EVENT); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) + + S_DS_GEN); + + subsc_evt->action = cpu_to_le16(subsc_evt_cfg->action); + mwifiex_dbg(priv->adapter, CMD, + "cmd: action: %d\n", subsc_evt_cfg->action); + + /*For query requests, no configuration TLV structures are to be added.*/ + if (subsc_evt_cfg->action == HostCmd_ACT_GEN_GET) + return 0; + + subsc_evt->events = cpu_to_le16(subsc_evt_cfg->events); + + event_bitmap = subsc_evt_cfg->events; + mwifiex_dbg(priv->adapter, CMD, "cmd: event bitmap : %16x\n", + event_bitmap); + + if (((subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) || + (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_SET)) && + (event_bitmap == 0)) { + mwifiex_dbg(priv->adapter, ERROR, + "Error: No event specified\t" + "for bitwise action type\n"); + return -EINVAL; + } + + /* + * Append TLV structures for each of the specified events for + * subscribing or re-configuring. This is not required for + * bitwise unsubscribing request. + */ + if (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) + return 0; + + pos = ((u8 *)subsc_evt) + + sizeof(struct host_cmd_ds_802_11_subsc_evt); + + if (event_bitmap & BITMASK_BCN_RSSI_LOW) { + rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos; + + rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_tlv->header.len = + cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) - + sizeof(struct mwifiex_ie_types_header)); + rssi_tlv->abs_value = subsc_evt_cfg->bcn_l_rssi_cfg.abs_value; + rssi_tlv->evt_freq = subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq; + + mwifiex_dbg(priv->adapter, EVENT, + "Cfg Beacon Low Rssi event,\t" + "RSSI:-%d dBm, Freq:%d\n", + subsc_evt_cfg->bcn_l_rssi_cfg.abs_value, + subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq); + + pos += sizeof(struct mwifiex_ie_types_rssi_threshold); + le16_add_cpu(&cmd->size, + sizeof(struct mwifiex_ie_types_rssi_threshold)); + } + + if (event_bitmap & BITMASK_BCN_RSSI_HIGH) { + rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos; + + rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); + rssi_tlv->header.len = + cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) - + sizeof(struct mwifiex_ie_types_header)); + rssi_tlv->abs_value = subsc_evt_cfg->bcn_h_rssi_cfg.abs_value; + rssi_tlv->evt_freq = subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq; + + mwifiex_dbg(priv->adapter, EVENT, + "Cfg Beacon High Rssi event,\t" + "RSSI:-%d dBm, Freq:%d\n", + subsc_evt_cfg->bcn_h_rssi_cfg.abs_value, + subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq); + + pos += sizeof(struct mwifiex_ie_types_rssi_threshold); + le16_add_cpu(&cmd->size, + sizeof(struct mwifiex_ie_types_rssi_threshold)); + } + + return 0; +} + +static int +mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv, + struct mwifiex_mef_entry *mef_entry, + u8 **buffer) +{ + struct mwifiex_mef_filter *filter = mef_entry->filter; + int i, byte_len; + u8 *stack_ptr = *buffer; + + for (i = 0; i < MWIFIEX_MEF_MAX_FILTERS; i++) { + filter = &mef_entry->filter[i]; + if (!filter->filt_type) + break; + *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->repeat); + stack_ptr += 4; + *stack_ptr = TYPE_DNUM; + stack_ptr += 1; + + byte_len = filter->byte_seq[MWIFIEX_MEF_MAX_BYTESEQ]; + memcpy(stack_ptr, filter->byte_seq, byte_len); + stack_ptr += byte_len; + *stack_ptr = byte_len; + stack_ptr += 1; + *stack_ptr = TYPE_BYTESEQ; + stack_ptr += 1; + + *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->offset); + stack_ptr += 4; + *stack_ptr = TYPE_DNUM; + stack_ptr += 1; + + *stack_ptr = filter->filt_type; + stack_ptr += 1; + + if (filter->filt_action) { + *stack_ptr = filter->filt_action; + stack_ptr += 1; + } + + if (stack_ptr - *buffer > STACK_NBYTES) + return -1; + } + + *buffer = stack_ptr; + return 0; +} + +static int +mwifiex_cmd_mef_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_ds_mef_cfg *mef) +{ + struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg; + struct mwifiex_fw_mef_entry *mef_entry = NULL; + u8 *pos = (u8 *)mef_cfg; + u16 i; + + cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG); + + mef_cfg->criteria = cpu_to_le32(mef->criteria); + mef_cfg->num_entries = cpu_to_le16(mef->num_entries); + pos += sizeof(*mef_cfg); + + for (i = 0; i < mef->num_entries; i++) { + mef_entry = (struct mwifiex_fw_mef_entry *)pos; + mef_entry->mode = mef->mef_entry[i].mode; + mef_entry->action = mef->mef_entry[i].action; + pos += sizeof(*mef_cfg->mef_entry); + + if (mwifiex_cmd_append_rpn_expression(priv, + &mef->mef_entry[i], &pos)) + return -1; + + mef_entry->exprsize = + cpu_to_le16(pos - mef_entry->expr); + } + cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN); + + return 0; +} + +/* This function parse cal data from ASCII to hex */ +static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst) +{ + u8 *s = src, *d = dst; + + while (s - src < len) { + if (*s && (isspace(*s) || *s == '\t')) { + s++; + continue; + } + if (isxdigit(*s)) { + *d++ = simple_strtol(s, NULL, 16); + s += 2; + } else { + s++; + } + } + + return d - dst; +} + +int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, + struct device_node *node, const char *prefix) +{ +#ifdef CONFIG_OF + struct property *prop; + size_t len = strlen(prefix); + int ret; + + /* look for all matching property names */ + for_each_property_of_node(node, prop) { + if (len > strlen(prop->name) || + strncmp(prop->name, prefix, len)) + continue; + + /* property header is 6 bytes, data must fit in cmd buffer */ + if (prop && prop->value && prop->length > 6 && + prop->length <= MWIFIEX_SIZE_OF_CMD_BUFFER - S_DS_GEN) { + ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, 0, + prop, true); + if (ret) + return ret; + } + } +#endif + return 0; +} + +/* This function prepares command of set_cfg_data. */ +static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, void *data_buf) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct property *prop = data_buf; + u32 len; + u8 *data = (u8 *)cmd + S_DS_GEN; + int ret; + + if (prop) { + len = prop->length; + ret = of_property_read_u8_array(adapter->dt_node, prop->name, + data, len); + if (ret) + return ret; + mwifiex_dbg(adapter, INFO, + "download cfg_data from device tree: %s\n", + prop->name); + } else if (adapter->cal_data->data && adapter->cal_data->size > 0) { + len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data, + adapter->cal_data->size, data); + mwifiex_dbg(adapter, INFO, + "download cfg_data from config file\n"); + } else { + return -1; + } + + cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); + cmd->size = cpu_to_le16(S_DS_GEN + len); + + return 0; +} + +static int +mwifiex_cmd_set_mc_policy(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_ds_multi_chan_policy *mc_pol = &cmd->params.mc_policy; + const u16 *drcs_info = data_buf; + + mc_pol->action = cpu_to_le16(cmd_action); + mc_pol->policy = cpu_to_le16(*drcs_info); + cmd->command = cpu_to_le16(HostCmd_CMD_MC_POLICY); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_multi_chan_policy) + + S_DS_GEN); + return 0; +} + +static int mwifiex_cmd_robust_coex(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, bool *is_timeshare) +{ + struct host_cmd_ds_robust_coex *coex = &cmd->params.coex; + struct mwifiex_ie_types_robust_coex *coex_tlv; + + cmd->command = cpu_to_le16(HostCmd_CMD_ROBUST_COEX); + cmd->size = cpu_to_le16(sizeof(*coex) + sizeof(*coex_tlv) + S_DS_GEN); + + coex->action = cpu_to_le16(cmd_action); + coex_tlv = (struct mwifiex_ie_types_robust_coex *) + ((u8 *)coex + sizeof(*coex)); + coex_tlv->header.type = cpu_to_le16(TLV_TYPE_ROBUST_COEX); + coex_tlv->header.len = cpu_to_le16(sizeof(coex_tlv->mode)); + + if (coex->action == HostCmd_ACT_GEN_GET) + return 0; + + if (*is_timeshare) + coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_TIMESHARE); + else + coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_SPATIAL); + + return 0; +} + +static int +mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_ds_coalesce_cfg *coalesce_cfg = + &cmd->params.coalesce_cfg; + struct mwifiex_ds_coalesce_cfg *cfg = data_buf; + struct coalesce_filt_field_param *param; + u16 cnt, idx, length; + struct coalesce_receive_filt_rule *rule; + + cmd->command = cpu_to_le16(HostCmd_CMD_COALESCE_CFG); + cmd->size = cpu_to_le16(S_DS_GEN); + + coalesce_cfg->action = cpu_to_le16(cmd_action); + coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules); + rule = coalesce_cfg->rule; + + for (cnt = 0; cnt < cfg->num_of_rules; cnt++) { + rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE); + rule->max_coalescing_delay = + cpu_to_le16(cfg->rule[cnt].max_coalescing_delay); + rule->pkt_type = cfg->rule[cnt].pkt_type; + rule->num_of_fields = cfg->rule[cnt].num_of_fields; + + length = 0; + + param = rule->params; + for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) { + param->operation = cfg->rule[cnt].params[idx].operation; + param->operand_len = + cfg->rule[cnt].params[idx].operand_len; + param->offset = + cpu_to_le16(cfg->rule[cnt].params[idx].offset); + memcpy(param->operand_byte_stream, + cfg->rule[cnt].params[idx].operand_byte_stream, + param->operand_len); + + length += sizeof(struct coalesce_filt_field_param); + + param++; + } + + /* Total rule length is sizeof max_coalescing_delay(u16), + * num_of_fields(u8), pkt_type(u8) and total length of the all + * params + */ + rule->header.len = cpu_to_le16(length + sizeof(u16) + + sizeof(u8) + sizeof(u8)); + + /* Add the rule length to the command size*/ + le16_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) + + sizeof(struct mwifiex_ie_types_header)); + + rule = (void *)((u8 *)rule->params + length); + } + + /* Add sizeof action, num_of_rules to total command length */ + le16_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16)); + + return 0; +} + +static int +mwifiex_cmd_tdls_config(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_ds_tdls_config *tdls_config = &cmd->params.tdls_config; + struct mwifiex_tdls_init_cs_params *config; + struct mwifiex_tdls_config *init_config; + u16 len; + + cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_CONFIG); + cmd->size = cpu_to_le16(S_DS_GEN); + tdls_config->tdls_action = cpu_to_le16(cmd_action); + le16_add_cpu(&cmd->size, sizeof(tdls_config->tdls_action)); + + switch (cmd_action) { + case ACT_TDLS_CS_ENABLE_CONFIG: + init_config = data_buf; + len = sizeof(*init_config); + memcpy(tdls_config->tdls_data, init_config, len); + break; + case ACT_TDLS_CS_INIT: + config = data_buf; + len = sizeof(*config); + memcpy(tdls_config->tdls_data, config, len); + break; + case ACT_TDLS_CS_STOP: + len = sizeof(struct mwifiex_tdls_stop_cs_params); + memcpy(tdls_config->tdls_data, data_buf, len); + break; + case ACT_TDLS_CS_PARAMS: + len = sizeof(struct mwifiex_tdls_config_cs_params); + memcpy(tdls_config->tdls_data, data_buf, len); + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "Unknown TDLS configuration\n"); + return -ENOTSUPP; + } + + le16_add_cpu(&cmd->size, len); + return 0; +} + +static int +mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_tdls_oper *tdls_oper = &cmd->params.tdls_oper; + struct mwifiex_ds_tdls_oper *oper = data_buf; + struct mwifiex_sta_node *sta_ptr; + struct host_cmd_tlv_rates *tlv_rates; + struct mwifiex_ie_types_htcap *ht_capab; + struct mwifiex_ie_types_qos_info *wmm_qos_info; + struct mwifiex_ie_types_extcap *extcap; + struct mwifiex_ie_types_vhtcap *vht_capab; + struct mwifiex_ie_types_aid *aid; + struct mwifiex_ie_types_tdls_idle_timeout *timeout; + u8 *pos, qos_info; + u16 config_len = 0; + struct station_parameters *params = priv->sta_params; + + cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_OPER); + cmd->size = cpu_to_le16(S_DS_GEN); + le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper)); + + tdls_oper->reason = 0; + memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN); + sta_ptr = mwifiex_get_sta_entry(priv, oper->peer_mac); + + pos = (u8 *)tdls_oper + sizeof(struct host_cmd_ds_tdls_oper); + + switch (oper->tdls_action) { + case MWIFIEX_TDLS_DISABLE_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_DELETE); + break; + case MWIFIEX_TDLS_CREATE_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CREATE); + break; + case MWIFIEX_TDLS_CONFIG_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CONFIG); + + if (!params) { + mwifiex_dbg(priv->adapter, ERROR, + "TDLS config params not available for %pM\n", + oper->peer_mac); + return -ENODATA; + } + + *(__le16 *)pos = cpu_to_le16(params->capability); + config_len += sizeof(params->capability); + + qos_info = params->uapsd_queues | (params->max_sp << 5); + wmm_qos_info = (struct mwifiex_ie_types_qos_info *)(pos + + config_len); + wmm_qos_info->header.type = cpu_to_le16(WLAN_EID_QOS_CAPA); + wmm_qos_info->header.len = cpu_to_le16(sizeof(qos_info)); + wmm_qos_info->qos_info = qos_info; + config_len += sizeof(struct mwifiex_ie_types_qos_info); + + if (params->ht_capa) { + ht_capab = (struct mwifiex_ie_types_htcap *)(pos + + config_len); + ht_capab->header.type = + cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_capab->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + memcpy(&ht_capab->ht_cap, params->ht_capa, + sizeof(struct ieee80211_ht_cap)); + config_len += sizeof(struct mwifiex_ie_types_htcap); + } + + if (params->supported_rates && params->supported_rates_len) { + tlv_rates = (struct host_cmd_tlv_rates *)(pos + + config_len); + tlv_rates->header.type = + cpu_to_le16(WLAN_EID_SUPP_RATES); + tlv_rates->header.len = + cpu_to_le16(params->supported_rates_len); + memcpy(tlv_rates->rates, params->supported_rates, + params->supported_rates_len); + config_len += sizeof(struct host_cmd_tlv_rates) + + params->supported_rates_len; + } + + if (params->ext_capab && params->ext_capab_len) { + extcap = (struct mwifiex_ie_types_extcap *)(pos + + config_len); + extcap->header.type = + cpu_to_le16(WLAN_EID_EXT_CAPABILITY); + extcap->header.len = cpu_to_le16(params->ext_capab_len); + memcpy(extcap->ext_capab, params->ext_capab, + params->ext_capab_len); + config_len += sizeof(struct mwifiex_ie_types_extcap) + + params->ext_capab_len; + } + if (params->vht_capa) { + vht_capab = (struct mwifiex_ie_types_vhtcap *)(pos + + config_len); + vht_capab->header.type = + cpu_to_le16(WLAN_EID_VHT_CAPABILITY); + vht_capab->header.len = + cpu_to_le16(sizeof(struct ieee80211_vht_cap)); + memcpy(&vht_capab->vht_cap, params->vht_capa, + sizeof(struct ieee80211_vht_cap)); + config_len += sizeof(struct mwifiex_ie_types_vhtcap); + } + if (params->aid) { + aid = (struct mwifiex_ie_types_aid *)(pos + config_len); + aid->header.type = cpu_to_le16(WLAN_EID_AID); + aid->header.len = cpu_to_le16(sizeof(params->aid)); + aid->aid = cpu_to_le16(params->aid); + config_len += sizeof(struct mwifiex_ie_types_aid); + } + + timeout = (void *)(pos + config_len); + timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT); + timeout->header.len = cpu_to_le16(sizeof(timeout->value)); + timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC); + config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout); + + break; + default: + mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS operation\n"); + return -ENOTSUPP; + } + + le16_add_cpu(&cmd->size, config_len); + + return 0; +} + +/* This function prepares command of sdio rx aggr info. */ +static int mwifiex_cmd_sdio_rx_aggr_cfg(struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = + &cmd->params.sdio_rx_aggr_cfg; + + cmd->command = cpu_to_le16(HostCmd_CMD_SDIO_SP_RX_AGGR_CFG); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) + + S_DS_GEN); + cfg->action = cmd_action; + if (cmd_action == HostCmd_ACT_GEN_SET) + cfg->enable = *(u8 *)data_buf; + + return 0; +} + +/* + * This function prepares the commands before sending them to the firmware. + * + * This is a generic function which calls specific command preparation + * routines based upon the command number. + */ +int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, + u16 cmd_action, u32 cmd_oid, + void *data_buf, void *cmd_buf) +{ + struct host_cmd_ds_command *cmd_ptr = cmd_buf; + int ret = 0; + + /* Prepare command */ + switch (cmd_no) { + case HostCmd_CMD_GET_HW_SPEC: + ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr); + break; + case HostCmd_CMD_CFG_DATA: + ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_MAC_CONTROL: + ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_802_11_MAC_ADDRESS: + ret = mwifiex_cmd_802_11_mac_address(priv, cmd_ptr, + cmd_action); + break; + case HostCmd_CMD_MAC_MULTICAST_ADR: + ret = mwifiex_cmd_mac_multicast_adr(cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = mwifiex_cmd_tx_rate_cfg(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_TXPWR_CFG: + ret = mwifiex_cmd_tx_power_cfg(cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_RF_TX_PWR: + ret = mwifiex_cmd_rf_tx_power(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_RF_ANTENNA: + ret = mwifiex_cmd_rf_antenna(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = mwifiex_cmd_enh_power_mode(priv, cmd_ptr, cmd_action, + (uint16_t)cmd_oid, data_buf); + break; + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = mwifiex_cmd_802_11_hs_cfg(priv, cmd_ptr, cmd_action, + (struct mwifiex_hs_config_param *) data_buf); + break; + case HostCmd_CMD_802_11_SCAN: + ret = mwifiex_cmd_802_11_scan(cmd_ptr, data_buf); + break; + case HostCmd_CMD_802_11_BG_SCAN_QUERY: + ret = mwifiex_cmd_802_11_bg_scan_query(cmd_ptr); + break; + case HostCmd_CMD_802_11_ASSOCIATE: + ret = mwifiex_cmd_802_11_associate(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_802_11_DEAUTHENTICATE: + ret = mwifiex_cmd_802_11_deauthenticate(priv, cmd_ptr, + data_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_START: + ret = mwifiex_cmd_802_11_ad_hoc_start(priv, cmd_ptr, + data_buf); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = mwifiex_cmd_802_11_get_log(cmd_ptr); + break; + case HostCmd_CMD_802_11_AD_HOC_JOIN: + ret = mwifiex_cmd_802_11_ad_hoc_join(priv, cmd_ptr, + data_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_STOP: + ret = mwifiex_cmd_802_11_ad_hoc_stop(cmd_ptr); + break; + case HostCmd_CMD_RSSI_INFO: + ret = mwifiex_cmd_802_11_rssi_info(priv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = mwifiex_cmd_802_11_snmp_mib(priv, cmd_ptr, cmd_action, + cmd_oid, data_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + cmd_ptr->command = + cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) + + S_DS_GEN); + priv->tx_rate = 0; + ret = 0; + break; + case HostCmd_CMD_VERSION_EXT: + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->params.verext.version_str_sel = + (u8) (*((u32 *) data_buf)); + memcpy(&cmd_ptr->params, data_buf, + sizeof(struct host_cmd_ds_version_ext)); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_version_ext) + + S_DS_GEN); + ret = 0; + break; + case HostCmd_CMD_MGMT_FRAME_REG: + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->params.reg_mask.action = cpu_to_le16(cmd_action); + cmd_ptr->params.reg_mask.mask = cpu_to_le32(*(u32 *)data_buf); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_mgmt_frame_reg) + + S_DS_GEN); + ret = 0; + break; + case HostCmd_CMD_REMAIN_ON_CHAN: + cmd_ptr->command = cpu_to_le16(cmd_no); + memcpy(&cmd_ptr->params, data_buf, + sizeof(struct host_cmd_ds_remain_on_chan)); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_remain_on_chan) + + S_DS_GEN); + break; + case HostCmd_CMD_11AC_CFG: + ret = mwifiex_cmd_11ac_cfg(priv, cmd_ptr, cmd_action, data_buf); + break; + case HostCmd_CMD_P2P_MODE_CFG: + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->params.mode_cfg.action = cpu_to_le16(cmd_action); + cmd_ptr->params.mode_cfg.mode = cpu_to_le16(*(u16 *)data_buf); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_p2p_mode_cfg) + + S_DS_GEN); + break; + case HostCmd_CMD_FUNC_INIT: + if (priv->adapter->hw_status == MWIFIEX_HW_STATUS_RESET) + priv->adapter->hw_status = MWIFIEX_HW_STATUS_READY; + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->size = cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_FUNC_SHUTDOWN: + priv->adapter->hw_status = MWIFIEX_HW_STATUS_RESET; + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->size = cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_11N_ADDBA_REQ: + ret = mwifiex_cmd_11n_addba_req(cmd_ptr, data_buf); + break; + case HostCmd_CMD_11N_DELBA: + ret = mwifiex_cmd_11n_delba(cmd_ptr, data_buf); + break; + case HostCmd_CMD_11N_ADDBA_RSP: + ret = mwifiex_cmd_11n_addba_rsp_gen(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_802_11_KEY_MATERIAL: + ret = mwifiex_cmd_802_11_key_material(priv, cmd_ptr, + cmd_action, cmd_oid, + data_buf); + break; + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = mwifiex_cmd_802_11d_domain_info(priv, cmd_ptr, + cmd_action); + break; + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + ret = mwifiex_cmd_recfg_tx_buf(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_AMSDU_AGGR_CTRL: + ret = mwifiex_cmd_amsdu_aggr_ctrl(cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_11N_CFG: + ret = mwifiex_cmd_11n_cfg(priv, cmd_ptr, cmd_action, data_buf); + break; + case HostCmd_CMD_WMM_GET_STATUS: + mwifiex_dbg(priv->adapter, CMD, + "cmd: WMM: WMM_GET_STATUS cmd sent\n"); + cmd_ptr->command = cpu_to_le16(HostCmd_CMD_WMM_GET_STATUS); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_wmm_get_status) + + S_DS_GEN); + ret = 0; + break; + case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: + ret = mwifiex_cmd_ibss_coalescing_status(cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = mwifiex_cmd_802_11_scan_ext(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_MEM_ACCESS: + ret = mwifiex_cmd_mem_access(cmd_ptr, cmd_action, data_buf); + break; + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_PMIC_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + ret = mwifiex_cmd_reg_access(cmd_ptr, cmd_action, data_buf); + break; + case HostCmd_CMD_SET_BSS_MODE: + cmd_ptr->command = cpu_to_le16(cmd_no); + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) + cmd_ptr->params.bss_mode.con_type = + CONNECTION_TYPE_ADHOC; + else if (priv->bss_mode == NL80211_IFTYPE_STATION || + priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) + cmd_ptr->params.bss_mode.con_type = + CONNECTION_TYPE_INFRA; + else if (priv->bss_mode == NL80211_IFTYPE_AP || + priv->bss_mode == NL80211_IFTYPE_P2P_GO) + cmd_ptr->params.bss_mode.con_type = CONNECTION_TYPE_AP; + cmd_ptr->size = cpu_to_le16(sizeof(struct + host_cmd_ds_set_bss_mode) + S_DS_GEN); + ret = 0; + break; + case HostCmd_CMD_PCIE_DESC_DETAILS: + ret = mwifiex_cmd_pcie_host_spec(priv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: + ret = mwifiex_cmd_802_11_subsc_evt(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_MEF_CFG: + ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_COALESCE_CFG: + ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_TDLS_OPER: + ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_TDLS_CONFIG: + ret = mwifiex_cmd_tdls_config(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr, + data_buf); + break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = mwifiex_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_MC_POLICY: + ret = mwifiex_cmd_set_mc_policy(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_ROBUST_COEX: + ret = mwifiex_cmd_robust_coex(priv, cmd_ptr, cmd_action, + data_buf); + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "PREP_CMD: unknown cmd- %#x\n", cmd_no); + ret = -1; + break; + } + return ret; +} + +/* + * This function issues commands to initialize firmware. + * + * This is called after firmware download to bring the card to + * working state. + * Function is also called during reinitialization of virtual + * interfaces. + * + * The following commands are issued sequentially - + * - Set PCI-Express host buffer configuration (PCIE only) + * - Function init (for first interface only) + * - Read MAC address (for first interface only) + * - Reconfigure Tx buffer size (for first interface only) + * - Enable auto deep sleep (for first interface only) + * - Get Tx rate + * - Get Tx power + * - Set IBSS coalescing status + * - Set AMSDU aggregation control + * - Set 11d control + * - Set MAC control (this must be the last command to initialize firmware) + */ +int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + u16 enable = true; + struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; + struct mwifiex_ds_auto_ds auto_ds; + enum state_11d_t state_11d; + struct mwifiex_ds_11n_tx_cfg tx_cfg; + u8 sdio_sp_rx_aggr_enable; + + if (first_sta) { + if (priv->adapter->iface_type == MWIFIEX_PCIE) { + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_PCIE_DESC_DETAILS, + HostCmd_ACT_GEN_SET, 0, NULL, + true); + if (ret) + return -1; + } + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_FUNC_INIT, + HostCmd_ACT_GEN_SET, 0, NULL, true); + if (ret) + return -1; + + /* Download calibration data to firmware. + * The cal-data can be read from device tree and/or + * a configuration file and downloaded to firmware. + */ + adapter->dt_node = + of_find_node_by_name(NULL, "marvell_cfgdata"); + if (adapter->dt_node) { + ret = mwifiex_dnld_dt_cfgdata(priv, adapter->dt_node, + "marvell,caldata"); + if (ret) + return -1; + } + + if (adapter->cal_data) { + ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, 0, NULL, + true); + if (ret) + return -1; + } + + /* Read MAC address from HW */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_GET_HW_SPEC, + HostCmd_ACT_GEN_GET, 0, NULL, true); + if (ret) + return -1; + + /** Set SDIO Single Port RX Aggr Info */ + if (priv->adapter->iface_type == MWIFIEX_SDIO && + ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) && + !priv->adapter->host_disable_sdio_rx_aggr) { + sdio_sp_rx_aggr_enable = true; + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_SDIO_SP_RX_AGGR_CFG, + HostCmd_ACT_GEN_SET, 0, + &sdio_sp_rx_aggr_enable, + true); + if (ret) { + mwifiex_dbg(priv->adapter, ERROR, + "error while enabling SP aggregation..disable it"); + adapter->sdio_rx_aggr_enable = false; + } + } + + /* Reconfigure tx buf size */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, + HostCmd_ACT_GEN_SET, 0, + &priv->adapter->tx_buf_size, true); + if (ret) + return -1; + + if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { + /* Enable IEEE PS by default */ + priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_STA_PS, NULL, + true); + if (ret) + return -1; + } + + if (drcs) { + adapter->drcs_enabled = true; + if (ISSUPP_DRCS_ENABLED(adapter->fw_cap_info)) + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_MC_POLICY, + HostCmd_ACT_GEN_SET, 0, + &adapter->drcs_enabled, + true); + if (ret) + return -1; + } + } + + /* get tx rate */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_GET, 0, NULL, true); + if (ret) + return -1; + priv->data_rate = 0; + + /* get tx power */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_RF_TX_PWR, + HostCmd_ACT_GEN_GET, 0, NULL, true); + if (ret) + return -1; + + if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) { + /* set ibss coalescing_status */ + ret = mwifiex_send_cmd( + priv, + HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, + HostCmd_ACT_GEN_SET, 0, &enable, true); + if (ret) + return -1; + } + + memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); + amsdu_aggr_ctrl.enable = true; + /* Send request to firmware */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_AMSDU_AGGR_CTRL, + HostCmd_ACT_GEN_SET, 0, + &amsdu_aggr_ctrl, true); + if (ret) + return -1; + /* MAC Control must be the last command in init_fw */ + /* set MAC Control */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true); + if (ret) + return -1; + + if (!disable_auto_ds && + first_sta && priv->adapter->iface_type != MWIFIEX_USB && + priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { + /* Enable auto deep sleep */ + auto_ds.auto_ds = DEEP_SLEEP_ON; + auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_AUTO_DS, + &auto_ds, true); + if (ret) + return -1; + } + + if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { + /* Send cmd to FW to enable/disable 11D function */ + state_11d = ENABLE_11D; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, DOT11D_I, + &state_11d, true); + if (ret) + mwifiex_dbg(priv->adapter, ERROR, + "11D: failed to enable 11D\n"); + } + + /* Send cmd to FW to configure 11n specific configuration + * (Short GI, Channel BW, Green field support etc.) for transmit + */ + tx_cfg.tx_htcap = MWIFIEX_FW_DEF_HTTXCFG; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_CFG, + HostCmd_ACT_GEN_SET, 0, &tx_cfg, true); + + if (init) { + /* set last_init_cmd before sending the command */ + priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG; + ret = -EINPROGRESS; + } + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c new file mode 100644 index 000000000000..9ac7aa2431b4 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c @@ -0,0 +1,1249 @@ +/* + * Marvell Wireless LAN device driver: station command response handling + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11ac.h" + + +/* + * This function handles the command response error case. + * + * For scan response error, the function cancels all the pending + * scan commands and generates an event to inform the applications + * of the scan completion. + * + * For Power Save command failure, we do not retry enter PS + * command in case of Ad-hoc mode. + * + * For all other response errors, the current command buffer is freed + * and returned to the free command queue. + */ +static void +mwifiex_process_cmdresp_error(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_ps_mode_enh *pm; + unsigned long flags; + + mwifiex_dbg(adapter, ERROR, + "CMD_RESP: cmd %#x error, result=%#x\n", + resp->command, resp->result); + + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + + switch (le16_to_cpu(resp->command)) { + case HostCmd_CMD_802_11_PS_MODE_ENH: + pm = &resp->params.psmode_enh; + mwifiex_dbg(adapter, ERROR, + "PS_MODE_ENH cmd failed: result=0x%x action=0x%X\n", + resp->result, le16_to_cpu(pm->action)); + /* We do not re-try enter-ps command in ad-hoc mode. */ + if (le16_to_cpu(pm->action) == EN_AUTO_PS && + (le16_to_cpu(pm->params.ps_bitmap) & BITMAP_STA_PS) && + priv->bss_mode == NL80211_IFTYPE_ADHOC) + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; + + break; + case HostCmd_CMD_802_11_SCAN: + case HostCmd_CMD_802_11_SCAN_EXT: + /* Cancel all pending scan command */ + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, list) { + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + break; + + case HostCmd_CMD_MAC_CONTROL: + break; + + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + mwifiex_dbg(adapter, MSG, + "SDIO RX single-port aggregation Not support\n"); + break; + + default: + break; + } + /* Handling errors here */ + mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->curr_cmd = NULL; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); +} + +/* + * This function handles the command response of get RSSI info. + * + * Handling includes changing the header fields into CPU format + * and saving the following parameters in driver - + * - Last data and beacon RSSI value + * - Average data and beacon RSSI value + * - Last data and beacon NF value + * - Average data and beacon NF value + * + * The parameters are send to the application as well, along with + * calculated SNR values. + */ +static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp = + &resp->params.rssi_info_rsp; + struct mwifiex_ds_misc_subsc_evt *subsc_evt = + &priv->async_subsc_evt_storage; + + priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last); + priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last); + + priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg); + priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg); + + priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last); + priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last); + + priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg); + priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg); + + if (priv->subsc_evt_rssi_state == EVENT_HANDLED) + return 0; + + memset(subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); + + /* Resubscribe low and high rssi events with new thresholds */ + subsc_evt->events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; + subsc_evt->action = HostCmd_ACT_BITWISE_SET; + if (priv->subsc_evt_rssi_state == RSSI_LOW_RECVD) { + subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg - + priv->cqm_rssi_hyst); + subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); + } else if (priv->subsc_evt_rssi_state == RSSI_HIGH_RECVD) { + subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); + subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg + + priv->cqm_rssi_hyst); + } + subsc_evt->bcn_l_rssi_cfg.evt_freq = 1; + subsc_evt->bcn_h_rssi_cfg.evt_freq = 1; + + priv->subsc_evt_rssi_state = EVENT_HANDLED; + + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, subsc_evt, false); + + return 0; +} + +/* + * This function handles the command response of set/get SNMP + * MIB parameters. + * + * Handling includes changing the header fields into CPU format + * and saving the parameter in driver. + * + * The following parameters are supported - + * - Fragmentation threshold + * - RTS threshold + * - Short retry limit + */ +static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + u32 *data_buf) +{ + struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; + u16 oid = le16_to_cpu(smib->oid); + u16 query_type = le16_to_cpu(smib->query_type); + u32 ul_temp; + + mwifiex_dbg(priv->adapter, INFO, + "info: SNMP_RESP: oid value = %#x,\t" + "query_type = %#x, buf size = %#x\n", + oid, query_type, le16_to_cpu(smib->buf_size)); + if (query_type == HostCmd_ACT_GEN_GET) { + ul_temp = le16_to_cpu(*((__le16 *) (smib->value))); + if (data_buf) + *data_buf = ul_temp; + switch (oid) { + case FRAG_THRESH_I: + mwifiex_dbg(priv->adapter, INFO, + "info: SNMP_RESP: FragThsd =%u\n", + ul_temp); + break; + case RTS_THRESH_I: + mwifiex_dbg(priv->adapter, INFO, + "info: SNMP_RESP: RTSThsd =%u\n", + ul_temp); + break; + case SHORT_RETRY_LIM_I: + mwifiex_dbg(priv->adapter, INFO, + "info: SNMP_RESP: TxRetryCount=%u\n", + ul_temp); + break; + case DTIM_PERIOD_I: + mwifiex_dbg(priv->adapter, INFO, + "info: SNMP_RESP: DTIM period=%u\n", + ul_temp); + default: + break; + } + } + + return 0; +} + +/* + * This function handles the command response of get log request + * + * Handling includes changing the header fields into CPU format + * and sending the received parameters to application. + */ +static int mwifiex_ret_get_log(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct mwifiex_ds_get_stats *stats) +{ + struct host_cmd_ds_802_11_get_log *get_log = + &resp->params.get_log; + + if (stats) { + stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame); + stats->failed = le32_to_cpu(get_log->failed); + stats->retry = le32_to_cpu(get_log->retry); + stats->multi_retry = le32_to_cpu(get_log->multi_retry); + stats->frame_dup = le32_to_cpu(get_log->frame_dup); + stats->rts_success = le32_to_cpu(get_log->rts_success); + stats->rts_failure = le32_to_cpu(get_log->rts_failure); + stats->ack_failure = le32_to_cpu(get_log->ack_failure); + stats->rx_frag = le32_to_cpu(get_log->rx_frag); + stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame); + stats->fcs_error = le32_to_cpu(get_log->fcs_error); + stats->tx_frame = le32_to_cpu(get_log->tx_frame); + stats->wep_icv_error[0] = + le32_to_cpu(get_log->wep_icv_err_cnt[0]); + stats->wep_icv_error[1] = + le32_to_cpu(get_log->wep_icv_err_cnt[1]); + stats->wep_icv_error[2] = + le32_to_cpu(get_log->wep_icv_err_cnt[2]); + stats->wep_icv_error[3] = + le32_to_cpu(get_log->wep_icv_err_cnt[3]); + stats->bcn_rcv_cnt = le32_to_cpu(get_log->bcn_rcv_cnt); + stats->bcn_miss_cnt = le32_to_cpu(get_log->bcn_miss_cnt); + } + + return 0; +} + +/* + * This function handles the command response of set/get Tx rate + * configurations. + * + * Handling includes changing the header fields into CPU format + * and saving the following parameters in driver - + * - DSSS rate bitmap + * - OFDM rate bitmap + * - HT MCS rate bitmaps + * + * Based on the new rate bitmaps, the function re-evaluates if + * auto data rate has been activated. If not, it sends another + * query to the firmware to get the current Tx data rate. + */ +static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg; + struct mwifiex_rate_scope *rate_scope; + struct mwifiex_ie_types_header *head; + u16 tlv, tlv_buf_len, tlv_buf_left; + u8 *tlv_buf; + u32 i; + + tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg); + tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*rate_cfg); + + while (tlv_buf_left >= sizeof(*head)) { + head = (struct mwifiex_ie_types_header *)tlv_buf; + tlv = le16_to_cpu(head->type); + tlv_buf_len = le16_to_cpu(head->len); + + if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) + break; + + switch (tlv) { + case TLV_TYPE_RATE_SCOPE: + rate_scope = (struct mwifiex_rate_scope *) tlv_buf; + priv->bitmap_rates[0] = + le16_to_cpu(rate_scope->hr_dsss_rate_bitmap); + priv->bitmap_rates[1] = + le16_to_cpu(rate_scope->ofdm_rate_bitmap); + for (i = 0; + i < + sizeof(rate_scope->ht_mcs_rate_bitmap) / + sizeof(u16); i++) + priv->bitmap_rates[2 + i] = + le16_to_cpu(rate_scope-> + ht_mcs_rate_bitmap[i]); + + if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { + for (i = 0; i < ARRAY_SIZE(rate_scope-> + vht_mcs_rate_bitmap); + i++) + priv->bitmap_rates[10 + i] = + le16_to_cpu(rate_scope-> + vht_mcs_rate_bitmap[i]); + } + break; + /* Add RATE_DROP tlv here */ + } + + tlv_buf += (sizeof(*head) + tlv_buf_len); + tlv_buf_left -= (sizeof(*head) + tlv_buf_len); + } + + priv->is_data_rate_auto = mwifiex_is_rate_auto(priv); + + if (priv->is_data_rate_auto) + priv->data_rate = 0; + else + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, 0, NULL, false); + + return 0; +} + +/* + * This function handles the command response of get Tx power level. + * + * Handling includes saving the maximum and minimum Tx power levels + * in driver, as well as sending the values to user. + */ +static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf) +{ + int length, max_power = -1, min_power = -1; + struct mwifiex_types_power_group *pg_tlv_hdr; + struct mwifiex_power_group *pg; + + if (!data_buf) + return -1; + + pg_tlv_hdr = (struct mwifiex_types_power_group *)((u8 *)data_buf); + pg = (struct mwifiex_power_group *) + ((u8 *) pg_tlv_hdr + sizeof(struct mwifiex_types_power_group)); + length = le16_to_cpu(pg_tlv_hdr->length); + + /* At least one structure required to update power */ + if (length < sizeof(struct mwifiex_power_group)) + return 0; + + max_power = pg->power_max; + min_power = pg->power_min; + length -= sizeof(struct mwifiex_power_group); + + while (length >= sizeof(struct mwifiex_power_group)) { + pg++; + if (max_power < pg->power_max) + max_power = pg->power_max; + + if (min_power > pg->power_min) + min_power = pg->power_min; + + length -= sizeof(struct mwifiex_power_group); + } + priv->min_tx_power_level = (u8) min_power; + priv->max_tx_power_level = (u8) max_power; + + return 0; +} + +/* + * This function handles the command response of set/get Tx power + * configurations. + * + * Handling includes changing the header fields into CPU format + * and saving the current Tx power level in driver. + */ +static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg; + struct mwifiex_types_power_group *pg_tlv_hdr; + struct mwifiex_power_group *pg; + u16 action = le16_to_cpu(txp_cfg->action); + u16 tlv_buf_left; + + pg_tlv_hdr = (struct mwifiex_types_power_group *) + ((u8 *)txp_cfg + + sizeof(struct host_cmd_ds_txpwr_cfg)); + + pg = (struct mwifiex_power_group *) + ((u8 *)pg_tlv_hdr + + sizeof(struct mwifiex_types_power_group)); + + tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*txp_cfg); + if (tlv_buf_left < + le16_to_cpu(pg_tlv_hdr->length) + sizeof(*pg_tlv_hdr)) + return 0; + + switch (action) { + case HostCmd_ACT_GEN_GET: + if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) + mwifiex_get_power_level(priv, pg_tlv_hdr); + + priv->tx_power_level = (u16) pg->power_min; + break; + + case HostCmd_ACT_GEN_SET: + if (!le32_to_cpu(txp_cfg->mode)) + break; + + if (pg->power_max == pg->power_min) + priv->tx_power_level = (u16) pg->power_min; + break; + default: + mwifiex_dbg(adapter, ERROR, + "CMD_RESP: unknown cmd action %d\n", + action); + return 0; + } + mwifiex_dbg(adapter, INFO, + "info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n", + priv->tx_power_level, priv->max_tx_power_level, + priv->min_tx_power_level); + + return 0; +} + +/* + * This function handles the command response of get RF Tx power. + */ +static int mwifiex_ret_rf_tx_power(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_rf_tx_pwr *txp = &resp->params.txp; + u16 action = le16_to_cpu(txp->action); + + priv->tx_power_level = le16_to_cpu(txp->cur_level); + + if (action == HostCmd_ACT_GEN_GET) { + priv->max_tx_power_level = txp->max_power; + priv->min_tx_power_level = txp->min_power; + } + + mwifiex_dbg(priv->adapter, INFO, + "Current TxPower Level=%d, Max Power=%d, Min Power=%d\n", + priv->tx_power_level, priv->max_tx_power_level, + priv->min_tx_power_level); + + return 0; +} + +/* + * This function handles the command response of set rf antenna + */ +static int mwifiex_ret_rf_antenna(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_rf_ant_mimo *ant_mimo = &resp->params.ant_mimo; + struct host_cmd_ds_rf_ant_siso *ant_siso = &resp->params.ant_siso; + struct mwifiex_adapter *adapter = priv->adapter; + + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) + mwifiex_dbg(adapter, INFO, + "RF_ANT_RESP: Tx action = 0x%x, Tx Mode = 0x%04x\t" + "Rx action = 0x%x, Rx Mode = 0x%04x\n", + le16_to_cpu(ant_mimo->action_tx), + le16_to_cpu(ant_mimo->tx_ant_mode), + le16_to_cpu(ant_mimo->action_rx), + le16_to_cpu(ant_mimo->rx_ant_mode)); + else + mwifiex_dbg(adapter, INFO, + "RF_ANT_RESP: action = 0x%x, Mode = 0x%04x\n", + le16_to_cpu(ant_siso->action), + le16_to_cpu(ant_siso->ant_mode)); + + return 0; +} + +/* + * This function handles the command response of set/get MAC address. + * + * Handling includes saving the MAC address in driver. + */ +static int mwifiex_ret_802_11_mac_address(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_mac_address *cmd_mac_addr = + &resp->params.mac_addr; + + memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN); + + mwifiex_dbg(priv->adapter, INFO, + "info: set mac address: %pM\n", priv->curr_addr); + + return 0; +} + +/* + * This function handles the command response of set/get MAC multicast + * address. + */ +static int mwifiex_ret_mac_multicast_adr(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + return 0; +} + +/* + * This function handles the command response of get Tx rate query. + * + * Handling includes changing the header fields into CPU format + * and saving the Tx rate and HT information parameters in driver. + * + * Both rate configuration and current data rate can be retrieved + * with this request. + */ +static int mwifiex_ret_802_11_tx_rate_query(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + priv->tx_rate = resp->params.tx_rate.tx_rate; + priv->tx_htinfo = resp->params.tx_rate.ht_info; + if (!priv->is_data_rate_auto) + priv->data_rate = + mwifiex_index_to_data_rate(priv, priv->tx_rate, + priv->tx_htinfo); + + return 0; +} + +/* + * This function handles the command response of a deauthenticate + * command. + * + * If the deauthenticated MAC matches the current BSS MAC, the connection + * state is reset. + */ +static int mwifiex_ret_802_11_deauthenticate(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + adapter->dbg.num_cmd_deauth++; + if (!memcmp(resp->params.deauth.mac_addr, + &priv->curr_bss_params.bss_descriptor.mac_address, + sizeof(resp->params.deauth.mac_addr))) + mwifiex_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING); + + return 0; +} + +/* + * This function handles the command response of ad-hoc stop. + * + * The function resets the connection state in driver. + */ +static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + mwifiex_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING); + return 0; +} + +/* + * This function handles the command response of set/get v1 key material. + * + * Handling includes updating the driver parameters to reflect the + * changes. + */ +static int mwifiex_ret_802_11_key_material_v1(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_key_material *key = + &resp->params.key_material; + + if (le16_to_cpu(key->action) == HostCmd_ACT_GEN_SET) { + if ((le16_to_cpu(key->key_param_set.key_info) & KEY_MCAST)) { + mwifiex_dbg(priv->adapter, INFO, + "info: key: GTK is set\n"); + priv->wpa_is_gtk_set = true; + priv->scan_block = false; + priv->port_open = true; + } + } + + memset(priv->aes_key.key_param_set.key, 0, + sizeof(key->key_param_set.key)); + priv->aes_key.key_param_set.key_len = key->key_param_set.key_len; + memcpy(priv->aes_key.key_param_set.key, key->key_param_set.key, + le16_to_cpu(priv->aes_key.key_param_set.key_len)); + + return 0; +} + +/* + * This function handles the command response of set/get v2 key material. + * + * Handling includes updating the driver parameters to reflect the + * changes. + */ +static int mwifiex_ret_802_11_key_material_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_key_material_v2 *key_v2; + __le16 len; + + key_v2 = &resp->params.key_material_v2; + if (le16_to_cpu(key_v2->action) == HostCmd_ACT_GEN_SET) { + if ((le16_to_cpu(key_v2->key_param_set.key_info) & KEY_MCAST)) { + mwifiex_dbg(priv->adapter, INFO, "info: key: GTK is set\n"); + priv->wpa_is_gtk_set = true; + priv->scan_block = false; + priv->port_open = true; + } + } + + if (key_v2->key_param_set.key_type != KEY_TYPE_ID_AES) + return 0; + + memset(priv->aes_key_v2.key_param_set.key_params.aes.key, 0, + WLAN_KEY_LEN_CCMP); + priv->aes_key_v2.key_param_set.key_params.aes.key_len = + key_v2->key_param_set.key_params.aes.key_len; + len = priv->aes_key_v2.key_param_set.key_params.aes.key_len; + memcpy(priv->aes_key_v2.key_param_set.key_params.aes.key, + key_v2->key_param_set.key_params.aes.key, le16_to_cpu(len)); + + return 0; +} + +/* Wrapper function for processing response of key material command */ +static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + if (priv->adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) + return mwifiex_ret_802_11_key_material_v2(priv, resp); + else + return mwifiex_ret_802_11_key_material_v1(priv, resp); +} + +/* + * This function handles the command response of get 11d domain information. + */ +static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11d_domain_info_rsp *domain_info = + &resp->params.domain_info_resp; + struct mwifiex_ietypes_domain_param_set *domain = &domain_info->domain; + u16 action = le16_to_cpu(domain_info->action); + u8 no_of_triplet; + + no_of_triplet = (u8) ((le16_to_cpu(domain->header.len) + - IEEE80211_COUNTRY_STRING_LEN) + / sizeof(struct ieee80211_country_ie_triplet)); + + mwifiex_dbg(priv->adapter, INFO, + "info: 11D Domain Info Resp: no_of_triplet=%d\n", + no_of_triplet); + + if (no_of_triplet > MWIFIEX_MAX_TRIPLET_802_11D) { + mwifiex_dbg(priv->adapter, FATAL, + "11D: invalid number of triplets %d returned\n", + no_of_triplet); + return -1; + } + + switch (action) { + case HostCmd_ACT_GEN_SET: /* Proc Set Action */ + break; + case HostCmd_ACT_GEN_GET: + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "11D: invalid action:%d\n", domain_info->action); + return -1; + } + + return 0; +} + +/* + * This function handles the command response of get extended version. + * + * Handling includes forming the extended version string and sending it + * to application. + */ +static int mwifiex_ret_ver_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct host_cmd_ds_version_ext *version_ext) +{ + struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext; + + if (version_ext) { + version_ext->version_str_sel = ver_ext->version_str_sel; + memcpy(version_ext->version_str, ver_ext->version_str, + sizeof(char) * 128); + memcpy(priv->version_str, ver_ext->version_str, 128); + } + return 0; +} + +/* + * This function handles the command response of remain on channel. + */ +static int +mwifiex_ret_remain_on_chan(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct host_cmd_ds_remain_on_chan *roc_cfg) +{ + struct host_cmd_ds_remain_on_chan *resp_cfg = &resp->params.roc_cfg; + + if (roc_cfg) + memcpy(roc_cfg, resp_cfg, sizeof(*roc_cfg)); + + return 0; +} + +/* + * This function handles the command response of P2P mode cfg. + */ +static int +mwifiex_ret_p2p_mode_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + void *data_buf) +{ + struct host_cmd_ds_p2p_mode_cfg *mode_cfg = &resp->params.mode_cfg; + + if (data_buf) + *((u16 *)data_buf) = le16_to_cpu(mode_cfg->mode); + + return 0; +} + +/* This function handles the command response of mem_access command + */ +static int +mwifiex_ret_mem_access(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, void *pioctl_buf) +{ + struct host_cmd_ds_mem_access *mem = (void *)&resp->params.mem; + + priv->mem_rw.addr = le32_to_cpu(mem->addr); + priv->mem_rw.value = le32_to_cpu(mem->value); + + return 0; +} +/* + * This function handles the command response of register access. + * + * The register value and offset are returned to the user. For EEPROM + * access, the byte count is also returned. + */ +static int mwifiex_ret_reg_access(u16 type, struct host_cmd_ds_command *resp, + void *data_buf) +{ + struct mwifiex_ds_reg_rw *reg_rw; + struct mwifiex_ds_read_eeprom *eeprom; + union reg { + struct host_cmd_ds_mac_reg_access *mac; + struct host_cmd_ds_bbp_reg_access *bbp; + struct host_cmd_ds_rf_reg_access *rf; + struct host_cmd_ds_pmic_reg_access *pmic; + struct host_cmd_ds_802_11_eeprom_access *eeprom; + } r; + + if (!data_buf) + return 0; + + reg_rw = data_buf; + eeprom = data_buf; + switch (type) { + case HostCmd_CMD_MAC_REG_ACCESS: + r.mac = &resp->params.mac_reg; + reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.mac->offset)); + reg_rw->value = r.mac->value; + break; + case HostCmd_CMD_BBP_REG_ACCESS: + r.bbp = &resp->params.bbp_reg; + reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.bbp->offset)); + reg_rw->value = cpu_to_le32((u32) r.bbp->value); + break; + + case HostCmd_CMD_RF_REG_ACCESS: + r.rf = &resp->params.rf_reg; + reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); + reg_rw->value = cpu_to_le32((u32) r.bbp->value); + break; + case HostCmd_CMD_PMIC_REG_ACCESS: + r.pmic = &resp->params.pmic_reg; + reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.pmic->offset)); + reg_rw->value = cpu_to_le32((u32) r.pmic->value); + break; + case HostCmd_CMD_CAU_REG_ACCESS: + r.rf = &resp->params.rf_reg; + reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); + reg_rw->value = cpu_to_le32((u32) r.rf->value); + break; + case HostCmd_CMD_802_11_EEPROM_ACCESS: + r.eeprom = &resp->params.eeprom; + pr_debug("info: EEPROM read len=%x\n", r.eeprom->byte_count); + if (le16_to_cpu(eeprom->byte_count) < + le16_to_cpu(r.eeprom->byte_count)) { + eeprom->byte_count = cpu_to_le16(0); + pr_debug("info: EEPROM read length is too big\n"); + return -1; + } + eeprom->offset = r.eeprom->offset; + eeprom->byte_count = r.eeprom->byte_count; + if (le16_to_cpu(eeprom->byte_count) > 0) + memcpy(&eeprom->value, &r.eeprom->value, + le16_to_cpu(r.eeprom->byte_count)); + + break; + default: + return -1; + } + return 0; +} + +/* + * This function handles the command response of get IBSS coalescing status. + * + * If the received BSSID is different than the current one, the current BSSID, + * beacon interval, ATIM window and ERP information are updated, along with + * changing the ad-hoc state accordingly. + */ +static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_ibss_status *ibss_coal_resp = + &(resp->params.ibss_coalescing); + + if (le16_to_cpu(ibss_coal_resp->action) == HostCmd_ACT_GEN_SET) + return 0; + + mwifiex_dbg(priv->adapter, INFO, + "info: new BSSID %pM\n", ibss_coal_resp->bssid); + + /* If rsp has NULL BSSID, Just return..... No Action */ + if (is_zero_ether_addr(ibss_coal_resp->bssid)) { + mwifiex_dbg(priv->adapter, FATAL, "new BSSID is NULL\n"); + return 0; + } + + /* If BSSID is diff, modify current BSS parameters */ + if (!ether_addr_equal(priv->curr_bss_params.bss_descriptor.mac_address, ibss_coal_resp->bssid)) { + /* BSSID */ + memcpy(priv->curr_bss_params.bss_descriptor.mac_address, + ibss_coal_resp->bssid, ETH_ALEN); + + /* Beacon Interval */ + priv->curr_bss_params.bss_descriptor.beacon_period + = le16_to_cpu(ibss_coal_resp->beacon_interval); + + /* ERP Information */ + priv->curr_bss_params.bss_descriptor.erp_flags = + (u8) le16_to_cpu(ibss_coal_resp->use_g_rate_protect); + + priv->adhoc_state = ADHOC_COALESCED; + } + + return 0; +} +static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_tdls_oper *cmd_tdls_oper = &resp->params.tdls_oper; + u16 reason = le16_to_cpu(cmd_tdls_oper->reason); + u16 action = le16_to_cpu(cmd_tdls_oper->tdls_action); + struct mwifiex_sta_node *node = + mwifiex_get_sta_entry(priv, cmd_tdls_oper->peer_mac); + + switch (action) { + case ACT_TDLS_DELETE: + if (reason) { + if (!node || reason == TDLS_ERR_LINK_NONEXISTENT) + mwifiex_dbg(priv->adapter, MSG, + "TDLS link delete for %pM failed: reason %d\n", + cmd_tdls_oper->peer_mac, reason); + else + mwifiex_dbg(priv->adapter, ERROR, + "TDLS link delete for %pM failed: reason %d\n", + cmd_tdls_oper->peer_mac, reason); + } else { + mwifiex_dbg(priv->adapter, MSG, + "TDLS link delete for %pM successful\n", + cmd_tdls_oper->peer_mac); + } + break; + case ACT_TDLS_CREATE: + if (reason) { + mwifiex_dbg(priv->adapter, ERROR, + "TDLS link creation for %pM failed: reason %d", + cmd_tdls_oper->peer_mac, reason); + if (node && reason != TDLS_ERR_LINK_EXISTS) + node->tdls_status = TDLS_SETUP_FAILURE; + } else { + mwifiex_dbg(priv->adapter, MSG, + "TDLS link creation for %pM successful", + cmd_tdls_oper->peer_mac); + } + break; + case ACT_TDLS_CONFIG: + if (reason) { + mwifiex_dbg(priv->adapter, ERROR, + "TDLS link config for %pM failed, reason %d\n", + cmd_tdls_oper->peer_mac, reason); + if (node) + node->tdls_status = TDLS_SETUP_FAILURE; + } else { + mwifiex_dbg(priv->adapter, MSG, + "TDLS link config for %pM successful\n", + cmd_tdls_oper->peer_mac); + } + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "Unknown TDLS command action response %d", action); + return -1; + } + + return 0; +} +/* + * This function handles the command response for subscribe event command. + */ +static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event = + &resp->params.subsc_evt; + + /* For every subscribe event command (Get/Set/Clear), FW reports the + * current set of subscribed events*/ + mwifiex_dbg(priv->adapter, EVENT, + "Bitmap of currently subscribed events: %16x\n", + le16_to_cpu(cmd_sub_event->events)); + + return 0; +} + +static int mwifiex_ret_uap_sta_list(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_sta_list *sta_list = + &resp->params.sta_list; + struct mwifiex_ie_types_sta_info *sta_info = (void *)&sta_list->tlv; + int i; + struct mwifiex_sta_node *sta_node; + + for (i = 0; i < sta_list->sta_count; i++) { + sta_node = mwifiex_get_sta_entry(priv, sta_info->mac); + if (unlikely(!sta_node)) + continue; + + sta_node->stats.rssi = sta_info->rssi; + sta_info++; + } + + return 0; +} + +/* This function handles the command response of set_cfg_data */ +static int mwifiex_ret_cfg_data(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + if (resp->result != HostCmd_RESULT_OK) { + mwifiex_dbg(priv->adapter, ERROR, "Cal data cmd resp failed\n"); + return -1; + } + + return 0; +} + +/** This Function handles the command response of sdio rx aggr */ +static int mwifiex_ret_sdio_rx_aggr_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = + &resp->params.sdio_rx_aggr_cfg; + + adapter->sdio_rx_aggr_enable = cfg->enable; + adapter->sdio_rx_block_size = le16_to_cpu(cfg->block_size); + + return 0; +} + +static int mwifiex_ret_robust_coex(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + bool *is_timeshare) +{ + struct host_cmd_ds_robust_coex *coex = &resp->params.coex; + struct mwifiex_ie_types_robust_coex *coex_tlv; + u16 action = le16_to_cpu(coex->action); + u32 mode; + + coex_tlv = (struct mwifiex_ie_types_robust_coex + *)((u8 *)coex + sizeof(struct host_cmd_ds_robust_coex)); + if (action == HostCmd_ACT_GEN_GET) { + mode = le32_to_cpu(coex_tlv->mode); + if (mode == MWIFIEX_COEX_MODE_TIMESHARE) + *is_timeshare = true; + else + *is_timeshare = false; + } + + return 0; +} + +/* + * This function handles the command responses. + * + * This is a generic function, which calls command specific + * response handlers based on the command ID. + */ +int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, + struct host_cmd_ds_command *resp) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + void *data_buf = adapter->curr_cmd->data_buf; + + /* If the command is not successful, cleanup and return failure */ + if (resp->result != HostCmd_RESULT_OK) { + mwifiex_process_cmdresp_error(priv, resp); + return -1; + } + /* Command successful, handle response */ + switch (cmdresp_no) { + case HostCmd_CMD_GET_HW_SPEC: + ret = mwifiex_ret_get_hw_spec(priv, resp); + break; + case HostCmd_CMD_CFG_DATA: + ret = mwifiex_ret_cfg_data(priv, resp); + break; + case HostCmd_CMD_MAC_CONTROL: + break; + case HostCmd_CMD_802_11_MAC_ADDRESS: + ret = mwifiex_ret_802_11_mac_address(priv, resp); + break; + case HostCmd_CMD_MAC_MULTICAST_ADR: + ret = mwifiex_ret_mac_multicast_adr(priv, resp); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = mwifiex_ret_tx_rate_cfg(priv, resp); + break; + case HostCmd_CMD_802_11_SCAN: + ret = mwifiex_ret_802_11_scan(priv, resp); + adapter->curr_cmd->wait_q_enabled = false; + break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = mwifiex_ret_802_11_scan_ext(priv, resp); + adapter->curr_cmd->wait_q_enabled = false; + break; + case HostCmd_CMD_802_11_BG_SCAN_QUERY: + ret = mwifiex_ret_802_11_scan(priv, resp); + mwifiex_dbg(adapter, CMD, + "info: CMD_RESP: BG_SCAN result is ready!\n"); + break; + case HostCmd_CMD_TXPWR_CFG: + ret = mwifiex_ret_tx_power_cfg(priv, resp); + break; + case HostCmd_CMD_RF_TX_PWR: + ret = mwifiex_ret_rf_tx_power(priv, resp); + break; + case HostCmd_CMD_RF_ANTENNA: + ret = mwifiex_ret_rf_antenna(priv, resp); + break; + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = mwifiex_ret_enh_power_mode(priv, resp, data_buf); + break; + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = mwifiex_ret_802_11_hs_cfg(priv, resp); + break; + case HostCmd_CMD_802_11_ASSOCIATE: + ret = mwifiex_ret_802_11_associate(priv, resp); + break; + case HostCmd_CMD_802_11_DEAUTHENTICATE: + ret = mwifiex_ret_802_11_deauthenticate(priv, resp); + break; + case HostCmd_CMD_802_11_AD_HOC_START: + case HostCmd_CMD_802_11_AD_HOC_JOIN: + ret = mwifiex_ret_802_11_ad_hoc(priv, resp); + break; + case HostCmd_CMD_802_11_AD_HOC_STOP: + ret = mwifiex_ret_802_11_ad_hoc_stop(priv, resp); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = mwifiex_ret_get_log(priv, resp, data_buf); + break; + case HostCmd_CMD_RSSI_INFO: + ret = mwifiex_ret_802_11_rssi_info(priv, resp); + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = mwifiex_ret_802_11_snmp_mib(priv, resp, data_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + ret = mwifiex_ret_802_11_tx_rate_query(priv, resp); + break; + case HostCmd_CMD_VERSION_EXT: + ret = mwifiex_ret_ver_ext(priv, resp, data_buf); + break; + case HostCmd_CMD_REMAIN_ON_CHAN: + ret = mwifiex_ret_remain_on_chan(priv, resp, data_buf); + break; + case HostCmd_CMD_11AC_CFG: + break; + case HostCmd_CMD_P2P_MODE_CFG: + ret = mwifiex_ret_p2p_mode_cfg(priv, resp, data_buf); + break; + case HostCmd_CMD_MGMT_FRAME_REG: + case HostCmd_CMD_FUNC_INIT: + case HostCmd_CMD_FUNC_SHUTDOWN: + break; + case HostCmd_CMD_802_11_KEY_MATERIAL: + ret = mwifiex_ret_802_11_key_material(priv, resp); + break; + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = mwifiex_ret_802_11d_domain_info(priv, resp); + break; + case HostCmd_CMD_11N_ADDBA_REQ: + ret = mwifiex_ret_11n_addba_req(priv, resp); + break; + case HostCmd_CMD_11N_DELBA: + ret = mwifiex_ret_11n_delba(priv, resp); + break; + case HostCmd_CMD_11N_ADDBA_RSP: + ret = mwifiex_ret_11n_addba_resp(priv, resp); + break; + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + if (0xffff == (u16)le16_to_cpu(resp->params.tx_buf.buff_size)) { + if (adapter->iface_type == MWIFIEX_USB && + adapter->usb_mc_setup) { + if (adapter->if_ops.multi_port_resync) + adapter->if_ops. + multi_port_resync(adapter); + adapter->usb_mc_setup = false; + adapter->tx_lock_flag = false; + } + break; + } + adapter->tx_buf_size = (u16) le16_to_cpu(resp->params. + tx_buf.buff_size); + adapter->tx_buf_size = (adapter->tx_buf_size + / MWIFIEX_SDIO_BLOCK_SIZE) + * MWIFIEX_SDIO_BLOCK_SIZE; + adapter->curr_tx_buf_size = adapter->tx_buf_size; + mwifiex_dbg(adapter, CMD, "cmd: curr_tx_buf_size=%d\n", + adapter->curr_tx_buf_size); + + if (adapter->if_ops.update_mp_end_port) + adapter->if_ops.update_mp_end_port(adapter, + le16_to_cpu(resp->params.tx_buf.mp_end_port)); + break; + case HostCmd_CMD_AMSDU_AGGR_CTRL: + break; + case HostCmd_CMD_WMM_GET_STATUS: + ret = mwifiex_ret_wmm_get_status(priv, resp); + break; + case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: + ret = mwifiex_ret_ibss_coalescing_status(priv, resp); + break; + case HostCmd_CMD_MEM_ACCESS: + ret = mwifiex_ret_mem_access(priv, resp, data_buf); + break; + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_PMIC_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + ret = mwifiex_ret_reg_access(cmdresp_no, resp, data_buf); + break; + case HostCmd_CMD_SET_BSS_MODE: + break; + case HostCmd_CMD_11N_CFG: + break; + case HostCmd_CMD_PCIE_DESC_DETAILS: + break; + case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: + ret = mwifiex_ret_subsc_evt(priv, resp); + break; + case HostCmd_CMD_UAP_SYS_CONFIG: + break; + case HOST_CMD_APCMD_STA_LIST: + ret = mwifiex_ret_uap_sta_list(priv, resp); + break; + case HostCmd_CMD_UAP_BSS_START: + adapter->tx_lock_flag = false; + adapter->pps_uapsd_mode = false; + adapter->delay_null_pkt = false; + priv->bss_started = 1; + break; + case HostCmd_CMD_UAP_BSS_STOP: + priv->bss_started = 0; + break; + case HostCmd_CMD_UAP_STA_DEAUTH: + break; + case HOST_CMD_APCMD_SYS_RESET: + break; + case HostCmd_CMD_MEF_CFG: + break; + case HostCmd_CMD_COALESCE_CFG: + break; + case HostCmd_CMD_TDLS_OPER: + ret = mwifiex_ret_tdls_oper(priv, resp); + case HostCmd_CMD_MC_POLICY: + break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = mwifiex_ret_sdio_rx_aggr_cfg(priv, resp); + break; + case HostCmd_CMD_TDLS_CONFIG: + break; + case HostCmd_CMD_ROBUST_COEX: + ret = mwifiex_ret_robust_coex(priv, resp, data_buf); + break; + default: + mwifiex_dbg(adapter, ERROR, + "CMD_RESP: unknown cmd response %#x\n", + resp->command); + break; + } + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c new file mode 100644 index 000000000000..ff3ee9dfbbd5 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c @@ -0,0 +1,864 @@ +/* + * Marvell Wireless LAN device driver: station event handling + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" + +/* + * This function resets the connection state. + * + * The function is invoked after receiving a disconnect event from firmware, + * and performs the following actions - + * - Set media status to disconnected + * - Clean up Tx and Rx packets + * - Resets SNR/NF/RSSI value in driver + * - Resets security configurations in driver + * - Enables auto data rate + * - Saves the previous SSID and BSSID so that they can + * be used for re-association, if required + * - Erases current SSID and BSSID information + * - Sends a disconnect event to upper layers/applications. + */ +void +mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (!priv->media_connected) + return; + + mwifiex_dbg(adapter, INFO, + "info: handles disconnect event\n"); + + priv->media_connected = false; + + priv->scan_block = false; + priv->port_open = false; + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) { + mwifiex_disable_all_tdls_links(priv); + + if (priv->adapter->auto_tdls) + mwifiex_clean_auto_tdls(priv); + } + + /* Free Tx and Rx packets, report disconnect to upper layer */ + mwifiex_clean_txrx(priv); + + /* Reset SNR/NF/RSSI values */ + priv->data_rssi_last = 0; + priv->data_nf_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->bcn_rssi_last = 0; + priv->bcn_nf_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->rxpd_rate = 0; + priv->rxpd_htinfo = 0; + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + priv->wpa_ie_len = 0; + + priv->sec_info.wapi_enabled = false; + priv->wapi_ie_len = 0; + priv->sec_info.wapi_key_on = false; + + priv->sec_info.encryption_mode = 0; + + /* Enable auto data rate */ + priv->is_data_rate_auto = true; + priv->data_rate = 0; + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && priv->hist_data) + mwifiex_hist_data_reset(priv); + + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { + priv->adhoc_state = ADHOC_IDLE; + priv->adhoc_is_link_sensed = false; + } + + /* + * Memorize the previous SSID and BSSID so + * it could be used for re-assoc + */ + + mwifiex_dbg(adapter, INFO, + "info: previous SSID=%s, SSID len=%u\n", + priv->prev_ssid.ssid, priv->prev_ssid.ssid_len); + + mwifiex_dbg(adapter, INFO, + "info: current SSID=%s, SSID len=%u\n", + priv->curr_bss_params.bss_descriptor.ssid.ssid, + priv->curr_bss_params.bss_descriptor.ssid.ssid_len); + + memcpy(&priv->prev_ssid, + &priv->curr_bss_params.bss_descriptor.ssid, + sizeof(struct cfg80211_ssid)); + + memcpy(priv->prev_bssid, + priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); + + /* Need to erase the current SSID and BSSID info */ + memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params)); + + adapter->tx_lock_flag = false; + adapter->pps_uapsd_mode = false; + + if (adapter->is_cmd_timedout && adapter->curr_cmd) + return; + priv->media_connected = false; + mwifiex_dbg(adapter, MSG, + "info: successfully disconnected from %pM: reason code %d\n", + priv->cfg_bssid, reason_code); + if (priv->bss_mode == NL80211_IFTYPE_STATION || + priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { + cfg80211_disconnected(priv->netdev, reason_code, NULL, 0, + false, GFP_KERNEL); + } + eth_zero_addr(priv->cfg_bssid); + + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); +} + +static int mwifiex_parse_tdls_event(struct mwifiex_private *priv, + struct sk_buff *event_skb) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_tdls_generic_event *tdls_evt = + (void *)event_skb->data + sizeof(adapter->event_cause); + u8 *mac = tdls_evt->peer_mac; + + /* reserved 2 bytes are not mandatory in tdls event */ + if (event_skb->len < (sizeof(struct mwifiex_tdls_generic_event) - + sizeof(u16) - sizeof(adapter->event_cause))) { + mwifiex_dbg(adapter, ERROR, "Invalid event length!\n"); + return -1; + } + + sta_ptr = mwifiex_get_sta_entry(priv, tdls_evt->peer_mac); + if (!sta_ptr) { + mwifiex_dbg(adapter, ERROR, "cannot get sta entry!\n"); + return -1; + } + + switch (le16_to_cpu(tdls_evt->type)) { + case TDLS_EVENT_LINK_TEAR_DOWN: + cfg80211_tdls_oper_request(priv->netdev, + tdls_evt->peer_mac, + NL80211_TDLS_TEARDOWN, + le16_to_cpu(tdls_evt->u.reason_code), + GFP_KERNEL); + break; + case TDLS_EVENT_CHAN_SWITCH_RESULT: + mwifiex_dbg(adapter, EVENT, "tdls channel switch result :\n"); + mwifiex_dbg(adapter, EVENT, + "status=0x%x, reason=0x%x cur_chan=%d\n", + tdls_evt->u.switch_result.status, + tdls_evt->u.switch_result.reason, + tdls_evt->u.switch_result.cur_chan); + + /* tdls channel switch failed */ + if (tdls_evt->u.switch_result.status != 0) { + switch (tdls_evt->u.switch_result.cur_chan) { + case TDLS_BASE_CHANNEL: + sta_ptr->tdls_status = TDLS_IN_BASE_CHAN; + break; + case TDLS_OFF_CHANNEL: + sta_ptr->tdls_status = TDLS_IN_OFF_CHAN; + break; + default: + break; + } + return ret; + } + + /* tdls channel switch success */ + switch (tdls_evt->u.switch_result.cur_chan) { + case TDLS_BASE_CHANNEL: + if (sta_ptr->tdls_status == TDLS_IN_BASE_CHAN) + break; + mwifiex_update_ralist_tx_pause_in_tdls_cs(priv, mac, + false); + sta_ptr->tdls_status = TDLS_IN_BASE_CHAN; + break; + case TDLS_OFF_CHANNEL: + if (sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) + break; + mwifiex_update_ralist_tx_pause_in_tdls_cs(priv, mac, + true); + sta_ptr->tdls_status = TDLS_IN_OFF_CHAN; + break; + default: + break; + } + + break; + case TDLS_EVENT_START_CHAN_SWITCH: + mwifiex_dbg(adapter, EVENT, "tdls start channel switch...\n"); + sta_ptr->tdls_status = TDLS_CHAN_SWITCHING; + break; + case TDLS_EVENT_CHAN_SWITCH_STOPPED: + mwifiex_dbg(adapter, EVENT, + "tdls chan switch stopped, reason=%d\n", + tdls_evt->u.cs_stop_reason); + break; + default: + break; + } + + return ret; +} + +static void mwifiex_process_uap_tx_pause(struct mwifiex_private *priv, + struct mwifiex_ie_types_header *tlv) +{ + struct mwifiex_tx_pause_tlv *tp; + struct mwifiex_sta_node *sta_ptr; + unsigned long flags; + + tp = (void *)tlv; + mwifiex_dbg(priv->adapter, EVENT, + "uap tx_pause: %pM pause=%d, pkts=%d\n", + tp->peermac, tp->tx_pause, + tp->pkt_cnt); + + if (ether_addr_equal(tp->peermac, priv->netdev->dev_addr)) { + if (tp->tx_pause) + priv->port_open = false; + else + priv->port_open = true; + } else if (is_multicast_ether_addr(tp->peermac)) { + mwifiex_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause); + } else { + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { + sta_ptr->tx_pause = tp->tx_pause; + mwifiex_update_ralist_tx_pause(priv, tp->peermac, + tp->tx_pause); + } + } +} + +static void mwifiex_process_sta_tx_pause(struct mwifiex_private *priv, + struct mwifiex_ie_types_header *tlv) +{ + struct mwifiex_tx_pause_tlv *tp; + struct mwifiex_sta_node *sta_ptr; + int status; + unsigned long flags; + + tp = (void *)tlv; + mwifiex_dbg(priv->adapter, EVENT, + "sta tx_pause: %pM pause=%d, pkts=%d\n", + tp->peermac, tp->tx_pause, + tp->pkt_cnt); + + if (ether_addr_equal(tp->peermac, priv->cfg_bssid)) { + if (tp->tx_pause) + priv->port_open = false; + else + priv->port_open = true; + } else { + if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return; + + status = mwifiex_get_tdls_link_status(priv, tp->peermac); + if (mwifiex_is_tdls_link_setup(status)) { + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { + sta_ptr->tx_pause = tp->tx_pause; + mwifiex_update_ralist_tx_pause(priv, + tp->peermac, + tp->tx_pause); + } + } + } +} + +void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, + struct sk_buff *event_skb) +{ + struct mwifiex_ie_types_multi_chan_info *chan_info; + struct mwifiex_ie_types_mc_group_info *grp_info; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_header *tlv; + u16 tlv_buf_left, tlv_type, tlv_len; + int intf_num, bss_type, bss_num, i; + struct mwifiex_private *intf_priv; + + tlv_buf_left = event_skb->len - sizeof(u32); + chan_info = (void *)event_skb->data + sizeof(u32); + + if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO || + tlv_buf_left < sizeof(struct mwifiex_ie_types_multi_chan_info)) { + mwifiex_dbg(adapter, ERROR, + "unknown TLV in chan_info event\n"); + return; + } + + adapter->usb_mc_status = le16_to_cpu(chan_info->status); + mwifiex_dbg(adapter, EVENT, "multi chan operation %s\n", + adapter->usb_mc_status ? "started" : "over"); + + tlv_buf_left -= sizeof(struct mwifiex_ie_types_multi_chan_info); + tlv = (struct mwifiex_ie_types_header *)chan_info->tlv_buffer; + + while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_len = le16_to_cpu(tlv->len); + if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) > + tlv_buf_left) { + mwifiex_dbg(adapter, ERROR, "wrong tlv: tlvLen=%d,\t" + "tlvBufLeft=%d\n", tlv_len, tlv_buf_left); + break; + } + if (tlv_type != TLV_TYPE_MC_GROUP_INFO) { + mwifiex_dbg(adapter, ERROR, "wrong tlv type: 0x%x\n", + tlv_type); + break; + } + + grp_info = (struct mwifiex_ie_types_mc_group_info *)tlv; + intf_num = grp_info->intf_num; + for (i = 0; i < intf_num; i++) { + bss_type = grp_info->bss_type_numlist[i] >> 4; + bss_num = grp_info->bss_type_numlist[i] & BSS_NUM_MASK; + intf_priv = mwifiex_get_priv_by_id(adapter, bss_num, + bss_type); + if (!intf_priv) { + mwifiex_dbg(adapter, ERROR, + "Invalid bss_type bss_num\t" + "in multi channel event\n"); + continue; + } + if (adapter->iface_type == MWIFIEX_USB) { + u8 ep; + + ep = grp_info->hid_num.usb_ep_num; + if (ep == MWIFIEX_USB_EP_DATA || + ep == MWIFIEX_USB_EP_DATA_CH2) + intf_priv->usb_port = ep; + } + } + + tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) + + tlv_len; + tlv = (void *)((u8 *)tlv + tlv_len + + sizeof(struct mwifiex_ie_types_header)); + } + + if (adapter->iface_type == MWIFIEX_USB) { + adapter->tx_lock_flag = true; + adapter->usb_mc_setup = true; + mwifiex_multi_chan_resync(adapter); + } +} + +void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, + struct sk_buff *event_skb) +{ + struct mwifiex_ie_types_header *tlv; + u16 tlv_type, tlv_len; + int tlv_buf_left; + + if (!priv->media_connected) { + mwifiex_dbg(priv->adapter, ERROR, + "tx_pause event while disconnected; bss_role=%d\n", + priv->bss_role); + return; + } + + tlv_buf_left = event_skb->len - sizeof(u32); + tlv = (void *)event_skb->data + sizeof(u32); + + while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_len = le16_to_cpu(tlv->len); + if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) > + tlv_buf_left) { + mwifiex_dbg(priv->adapter, ERROR, + "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_TX_PAUSE) { + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) + mwifiex_process_sta_tx_pause(priv, tlv); + else + mwifiex_process_uap_tx_pause(priv, tlv); + } + + tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) + + tlv_len; + tlv = (void *)((u8 *)tlv + tlv_len + + sizeof(struct mwifiex_ie_types_header)); + } + +} + +/* +* This function handles coex events generated by firmware +*/ +void mwifiex_bt_coex_wlan_param_update_event(struct mwifiex_private *priv, + struct sk_buff *event_skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_header *tlv; + struct mwifiex_ie_types_btcoex_aggr_win_size *winsizetlv; + struct mwifiex_ie_types_btcoex_scan_time *scantlv; + s32 len = event_skb->len - sizeof(u32); + u8 *cur_ptr = event_skb->data + sizeof(u32); + u16 tlv_type, tlv_len; + + while (len >= sizeof(struct mwifiex_ie_types_header)) { + tlv = (struct mwifiex_ie_types_header *)cur_ptr; + tlv_len = le16_to_cpu(tlv->len); + tlv_type = le16_to_cpu(tlv->type); + + if ((tlv_len + sizeof(struct mwifiex_ie_types_header)) > len) + break; + switch (tlv_type) { + case TLV_BTCOEX_WL_AGGR_WINSIZE: + winsizetlv = + (struct mwifiex_ie_types_btcoex_aggr_win_size *)tlv; + adapter->coex_win_size = winsizetlv->coex_win_size; + adapter->coex_tx_win_size = + winsizetlv->tx_win_size; + adapter->coex_rx_win_size = + winsizetlv->rx_win_size; + mwifiex_coex_ampdu_rxwinsize(adapter); + mwifiex_update_ampdu_txwinsize(adapter); + break; + + case TLV_BTCOEX_WL_SCANTIME: + scantlv = + (struct mwifiex_ie_types_btcoex_scan_time *)tlv; + adapter->coex_scan = scantlv->coex_scan; + adapter->coex_min_scan_time = scantlv->min_scan_time; + adapter->coex_max_scan_time = scantlv->max_scan_time; + break; + + default: + break; + } + + len -= tlv_len + sizeof(struct mwifiex_ie_types_header); + cur_ptr += tlv_len + + sizeof(struct mwifiex_ie_types_header); + } + + dev_dbg(adapter->dev, "coex_scan=%d min_scan=%d coex_win=%d, tx_win=%d rx_win=%d\n", + adapter->coex_scan, adapter->coex_min_scan_time, + adapter->coex_win_size, adapter->coex_tx_win_size, + adapter->coex_rx_win_size); +} + +/* + * This function handles events generated by firmware. + * + * This is a generic function and handles all events. + * + * Event specific routines are called by this function based + * upon the generated event cause. + * + * For the following events, the function just forwards them to upper + * layers, optionally recording the change - + * - EVENT_LINK_SENSED + * - EVENT_MIC_ERR_UNICAST + * - EVENT_MIC_ERR_MULTICAST + * - EVENT_PORT_RELEASE + * - EVENT_RSSI_LOW + * - EVENT_SNR_LOW + * - EVENT_MAX_FAIL + * - EVENT_RSSI_HIGH + * - EVENT_SNR_HIGH + * - EVENT_DATA_RSSI_LOW + * - EVENT_DATA_SNR_LOW + * - EVENT_DATA_RSSI_HIGH + * - EVENT_DATA_SNR_HIGH + * - EVENT_LINK_QUALITY + * - EVENT_PRE_BEACON_LOST + * - EVENT_IBSS_COALESCED + * - EVENT_WEP_ICV_ERR + * - EVENT_BW_CHANGE + * - EVENT_HOSTWAKE_STAIE + * + * For the following events, no action is taken - + * - EVENT_MIB_CHANGED + * - EVENT_INIT_DONE + * - EVENT_DUMMY_HOST_WAKEUP_SIGNAL + * + * Rest of the supported events requires driver handling - + * - EVENT_DEAUTHENTICATED + * - EVENT_DISASSOCIATED + * - EVENT_LINK_LOST + * - EVENT_PS_SLEEP + * - EVENT_PS_AWAKE + * - EVENT_DEEP_SLEEP_AWAKE + * - EVENT_HS_ACT_REQ + * - EVENT_ADHOC_BCN_LOST + * - EVENT_BG_SCAN_REPORT + * - EVENT_WMM_STATUS_CHANGE + * - EVENT_ADDBA + * - EVENT_DELBA + * - EVENT_BA_STREAM_TIEMOUT + * - EVENT_AMSDU_AGGR_CTRL + */ +int mwifiex_process_sta_event(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret = 0; + u32 eventcause = adapter->event_cause; + u16 ctrl, reason_code; + + switch (eventcause) { + case EVENT_DUMMY_HOST_WAKEUP_SIGNAL: + mwifiex_dbg(adapter, ERROR, + "invalid EVENT: DUMMY_HOST_WAKEUP_SIGNAL, ignore it\n"); + break; + case EVENT_LINK_SENSED: + mwifiex_dbg(adapter, EVENT, "event: LINK_SENSED\n"); + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); + break; + + case EVENT_DEAUTHENTICATED: + mwifiex_dbg(adapter, EVENT, "event: Deauthenticated\n"); + if (priv->wps.session_enable) { + mwifiex_dbg(adapter, INFO, + "info: receive deauth event in wps session\n"); + break; + } + adapter->dbg.num_event_deauth++; + if (priv->media_connected) { + reason_code = + le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_reset_connect_state(priv, reason_code); + } + break; + + case EVENT_DISASSOCIATED: + mwifiex_dbg(adapter, EVENT, "event: Disassociated\n"); + if (priv->wps.session_enable) { + mwifiex_dbg(adapter, INFO, + "info: receive disassoc event in wps session\n"); + break; + } + adapter->dbg.num_event_disassoc++; + if (priv->media_connected) { + reason_code = + le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_reset_connect_state(priv, reason_code); + } + break; + + case EVENT_LINK_LOST: + mwifiex_dbg(adapter, EVENT, "event: Link lost\n"); + adapter->dbg.num_event_link_lost++; + if (priv->media_connected) { + reason_code = + le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_reset_connect_state(priv, reason_code); + } + break; + + case EVENT_PS_SLEEP: + mwifiex_dbg(adapter, EVENT, "info: EVENT: SLEEP\n"); + + adapter->ps_state = PS_STATE_PRE_SLEEP; + + mwifiex_check_ps_cond(adapter); + break; + + case EVENT_PS_AWAKE: + mwifiex_dbg(adapter, EVENT, "info: EVENT: AWAKE\n"); + if (!adapter->pps_uapsd_mode && priv->port_open && + priv->media_connected && adapter->sleep_period.period) { + adapter->pps_uapsd_mode = true; + mwifiex_dbg(adapter, EVENT, + "event: PPS/UAPSD mode activated\n"); + } + adapter->tx_lock_flag = false; + if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { + if (mwifiex_check_last_packet_indication(priv)) { + if (adapter->data_sent || + (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv))) { + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + break; + } + if (!mwifiex_send_null_packet + (priv, + MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | + MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) + adapter->ps_state = + PS_STATE_SLEEP; + return 0; + } + } + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + + break; + + case EVENT_DEEP_SLEEP_AWAKE: + adapter->if_ops.wakeup_complete(adapter); + mwifiex_dbg(adapter, EVENT, "event: DS_AWAKE\n"); + if (adapter->is_deep_sleep) + adapter->is_deep_sleep = false; + break; + + case EVENT_HS_ACT_REQ: + mwifiex_dbg(adapter, EVENT, "event: HS_ACT_REQ\n"); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_HS_CFG_ENH, + 0, 0, NULL, false); + break; + + case EVENT_MIC_ERR_UNICAST: + mwifiex_dbg(adapter, EVENT, "event: UNICAST MIC ERROR\n"); + cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, + NL80211_KEYTYPE_PAIRWISE, + -1, NULL, GFP_KERNEL); + break; + + case EVENT_MIC_ERR_MULTICAST: + mwifiex_dbg(adapter, EVENT, "event: MULTICAST MIC ERROR\n"); + cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, + NL80211_KEYTYPE_GROUP, + -1, NULL, GFP_KERNEL); + break; + case EVENT_MIB_CHANGED: + case EVENT_INIT_DONE: + break; + + case EVENT_ADHOC_BCN_LOST: + mwifiex_dbg(adapter, EVENT, "event: ADHOC_BCN_LOST\n"); + priv->adhoc_is_link_sensed = false; + mwifiex_clean_txrx(priv); + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + break; + + case EVENT_BG_SCAN_REPORT: + mwifiex_dbg(adapter, EVENT, "event: BGS_REPORT\n"); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_QUERY, + HostCmd_ACT_GEN_GET, 0, NULL, false); + break; + + case EVENT_PORT_RELEASE: + mwifiex_dbg(adapter, EVENT, "event: PORT RELEASE\n"); + priv->port_open = true; + break; + + case EVENT_EXT_SCAN_REPORT: + mwifiex_dbg(adapter, EVENT, "event: EXT_SCAN Report\n"); + if (adapter->ext_scan) + ret = mwifiex_handle_event_ext_scan_report(priv, + adapter->event_skb->data); + + break; + + case EVENT_WMM_STATUS_CHANGE: + mwifiex_dbg(adapter, EVENT, "event: WMM status changed\n"); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_WMM_GET_STATUS, + 0, 0, NULL, false); + break; + + case EVENT_RSSI_LOW: + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + GFP_KERNEL); + mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, 0, NULL, false); + priv->subsc_evt_rssi_state = RSSI_LOW_RECVD; + mwifiex_dbg(adapter, EVENT, "event: Beacon RSSI_LOW\n"); + break; + case EVENT_SNR_LOW: + mwifiex_dbg(adapter, EVENT, "event: Beacon SNR_LOW\n"); + break; + case EVENT_MAX_FAIL: + mwifiex_dbg(adapter, EVENT, "event: MAX_FAIL\n"); + break; + case EVENT_RSSI_HIGH: + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + GFP_KERNEL); + mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, 0, NULL, false); + priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD; + mwifiex_dbg(adapter, EVENT, "event: Beacon RSSI_HIGH\n"); + break; + case EVENT_SNR_HIGH: + mwifiex_dbg(adapter, EVENT, "event: Beacon SNR_HIGH\n"); + break; + case EVENT_DATA_RSSI_LOW: + mwifiex_dbg(adapter, EVENT, "event: Data RSSI_LOW\n"); + break; + case EVENT_DATA_SNR_LOW: + mwifiex_dbg(adapter, EVENT, "event: Data SNR_LOW\n"); + break; + case EVENT_DATA_RSSI_HIGH: + mwifiex_dbg(adapter, EVENT, "event: Data RSSI_HIGH\n"); + break; + case EVENT_DATA_SNR_HIGH: + mwifiex_dbg(adapter, EVENT, "event: Data SNR_HIGH\n"); + break; + case EVENT_LINK_QUALITY: + mwifiex_dbg(adapter, EVENT, "event: Link Quality\n"); + break; + case EVENT_PRE_BEACON_LOST: + mwifiex_dbg(adapter, EVENT, "event: Pre-Beacon Lost\n"); + break; + case EVENT_IBSS_COALESCED: + mwifiex_dbg(adapter, EVENT, "event: IBSS_COALESCED\n"); + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, + HostCmd_ACT_GEN_GET, 0, NULL, false); + break; + case EVENT_ADDBA: + mwifiex_dbg(adapter, EVENT, "event: ADDBA Request\n"); + mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, + adapter->event_body, false); + break; + case EVENT_DELBA: + mwifiex_dbg(adapter, EVENT, "event: DELBA Request\n"); + mwifiex_11n_delete_ba_stream(priv, adapter->event_body); + break; + case EVENT_BA_STREAM_TIEMOUT: + mwifiex_dbg(adapter, EVENT, "event: BA Stream timeout\n"); + mwifiex_11n_ba_stream_timeout(priv, + (struct host_cmd_ds_11n_batimeout + *) + adapter->event_body); + break; + case EVENT_AMSDU_AGGR_CTRL: + ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_dbg(adapter, EVENT, + "event: AMSDU_AGGR_CTRL %d\n", ctrl); + + adapter->tx_buf_size = + min_t(u16, adapter->curr_tx_buf_size, ctrl); + mwifiex_dbg(adapter, EVENT, "event: tx_buf_size %d\n", + adapter->tx_buf_size); + break; + + case EVENT_WEP_ICV_ERR: + mwifiex_dbg(adapter, EVENT, "event: WEP ICV error\n"); + break; + + case EVENT_BW_CHANGE: + mwifiex_dbg(adapter, EVENT, "event: BW Change\n"); + break; + + case EVENT_HOSTWAKE_STAIE: + mwifiex_dbg(adapter, EVENT, + "event: HOSTWAKE_STAIE %d\n", eventcause); + break; + + case EVENT_REMAIN_ON_CHAN_EXPIRED: + mwifiex_dbg(adapter, EVENT, + "event: Remain on channel expired\n"); + cfg80211_remain_on_channel_expired(&priv->wdev, + priv->roc_cfg.cookie, + &priv->roc_cfg.chan, + GFP_ATOMIC); + + memset(&priv->roc_cfg, 0x00, sizeof(struct mwifiex_roc_cfg)); + + break; + + case EVENT_CHANNEL_SWITCH_ANN: + mwifiex_dbg(adapter, EVENT, "event: Channel Switch Announcement\n"); + priv->csa_expire_time = + jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME); + priv->csa_chan = priv->curr_bss_params.bss_descriptor.channel; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, + HostCmd_ACT_GEN_SET, 0, + priv->curr_bss_params.bss_descriptor.mac_address, + false); + break; + + case EVENT_TDLS_GENERIC_EVENT: + ret = mwifiex_parse_tdls_event(priv, adapter->event_skb); + break; + + case EVENT_TX_DATA_PAUSE: + mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n"); + mwifiex_process_tx_pause_event(priv, adapter->event_skb); + break; + + case EVENT_MULTI_CHAN_INFO: + mwifiex_dbg(adapter, EVENT, "event: multi-chan info\n"); + mwifiex_process_multi_chan_event(priv, adapter->event_skb); + break; + + case EVENT_TX_STATUS_REPORT: + mwifiex_dbg(adapter, EVENT, "event: TX_STATUS Report\n"); + mwifiex_parse_tx_status_event(priv, adapter->event_body); + break; + + case EVENT_CHANNEL_REPORT_RDY: + mwifiex_dbg(adapter, EVENT, "event: Channel Report\n"); + ret = mwifiex_11h_handle_chanrpt_ready(priv, + adapter->event_skb); + break; + case EVENT_RADAR_DETECTED: + mwifiex_dbg(adapter, EVENT, "event: Radar detected\n"); + ret = mwifiex_11h_handle_radar_detected(priv, + adapter->event_skb); + break; + case EVENT_BT_COEX_WLAN_PARA_CHANGE: + dev_dbg(adapter->dev, "EVENT: BT coex wlan param update\n"); + mwifiex_bt_coex_wlan_param_update_event(priv, + adapter->event_skb); + break; + default: + mwifiex_dbg(adapter, ERROR, "event: unknown event id: %#x\n", + eventcause); + break; + } + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c new file mode 100644 index 000000000000..a6c8a4f7bfe9 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -0,0 +1,1421 @@ +/* + * Marvell Wireless LAN device driver: functions for station ioctl + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "cfg80211.h" + +static int disconnect_on_suspend; +module_param(disconnect_on_suspend, int, 0644); + +/* + * Copies the multicast address list from device to driver. + * + * This function does not validate the destination memory for + * size, and the calling function must ensure enough memory is + * available. + */ +int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, + struct net_device *dev) +{ + int i = 0; + struct netdev_hw_addr *ha; + + netdev_for_each_mc_addr(ha, dev) + memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN); + + return i; +} + +/* + * Wait queue completion handler. + * + * This function waits on a cmd wait queue. It also cancels the pending + * request after waking up, in case of errors. + */ +int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_queued) +{ + int status; + + /* Wait for completion */ + status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait, + *(cmd_queued->condition), + (12 * HZ)); + if (status <= 0) { + if (status == 0) + status = -ETIMEDOUT; + mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n", + status); + mwifiex_cancel_all_pending_cmd(adapter); + return status; + } + + status = adapter->cmd_wait_q.status; + adapter->cmd_wait_q.status = 0; + + return status; +} + +/* + * This function prepares the correct firmware command and + * issues it to set the multicast list. + * + * This function can be used to enable promiscuous mode, or enable all + * multicast packets, or to enable selective multicast. + */ +int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, + struct mwifiex_multicast_list *mcast_list) +{ + int ret = 0; + u16 old_pkt_filter; + + old_pkt_filter = priv->curr_pkt_filter; + + if (mcast_list->mode == MWIFIEX_PROMISC_MODE) { + mwifiex_dbg(priv->adapter, INFO, + "info: Enable Promiscuous mode\n"); + priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; + priv->curr_pkt_filter &= + ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + /* Multicast */ + priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; + if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) { + mwifiex_dbg(priv->adapter, INFO, + "info: Enabling All Multicast!\n"); + priv->curr_pkt_filter |= + HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + priv->curr_pkt_filter &= + ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + mwifiex_dbg(priv->adapter, INFO, + "info: Set multicast list=%d\n", + mcast_list->num_multicast_addr); + /* Send multicast addresses to firmware */ + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_MAC_MULTICAST_ADR, + HostCmd_ACT_GEN_SET, 0, + mcast_list, false); + } + } + mwifiex_dbg(priv->adapter, INFO, + "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n", + old_pkt_filter, priv->curr_pkt_filter); + if (old_pkt_filter != priv->curr_pkt_filter) { + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, + 0, &priv->curr_pkt_filter, false); + } + + return ret; +} + +/* + * This function fills bss descriptor structure using provided + * information. + * beacon_ie buffer is allocated in this function. It is caller's + * responsibility to free the memory. + */ +int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, + struct cfg80211_bss *bss, + struct mwifiex_bssdescriptor *bss_desc) +{ + u8 *beacon_ie; + size_t beacon_ie_len; + struct mwifiex_bss_priv *bss_priv = (void *)bss->priv; + const struct cfg80211_bss_ies *ies; + + rcu_read_lock(); + ies = rcu_dereference(bss->ies); + beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC); + beacon_ie_len = ies->len; + bss_desc->timestamp = ies->tsf; + rcu_read_unlock(); + + if (!beacon_ie) { + mwifiex_dbg(priv->adapter, ERROR, + " failed to alloc beacon_ie\n"); + return -ENOMEM; + } + + memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN); + bss_desc->rssi = bss->signal; + /* The caller of this function will free beacon_ie */ + bss_desc->beacon_buf = beacon_ie; + bss_desc->beacon_buf_size = beacon_ie_len; + bss_desc->beacon_period = bss->beacon_interval; + bss_desc->cap_info_bitmap = bss->capability; + bss_desc->bss_band = bss_priv->band; + bss_desc->fw_tsf = bss_priv->fw_tsf; + if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) { + mwifiex_dbg(priv->adapter, INFO, + "info: InterpretIE: AP WEP enabled\n"); + bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; + } else { + bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; + } + if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_IBSS) + bss_desc->bss_mode = NL80211_IFTYPE_ADHOC; + else + bss_desc->bss_mode = NL80211_IFTYPE_STATION; + + /* Disable 11ac by default. Enable it only where there + * exist VHT_CAP IE in AP beacon + */ + bss_desc->disable_11ac = true; + + if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT) + bss_desc->sensed_11h = true; + + return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc); +} + +void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv) +{ + if (priv->adapter->dt_node) { + char txpwr[] = {"marvell,00_txpwrlimit"}; + + memcpy(&txpwr[8], priv->adapter->country_code, 2); + mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr); + } +} + +static int mwifiex_process_country_ie(struct mwifiex_private *priv, + struct cfg80211_bss *bss) +{ + const u8 *country_ie; + u8 country_ie_len; + struct mwifiex_802_11d_domain_reg *domain_info = + &priv->adapter->domain_reg; + + rcu_read_lock(); + country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + if (!country_ie) { + rcu_read_unlock(); + return 0; + } + + country_ie_len = country_ie[1]; + if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { + rcu_read_unlock(); + return 0; + } + + if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) { + rcu_read_unlock(); + mwifiex_dbg(priv->adapter, INFO, + "11D: skip setting domain info in FW\n"); + return 0; + } + memcpy(priv->adapter->country_code, &country_ie[2], 2); + + domain_info->country_code[0] = country_ie[2]; + domain_info->country_code[1] = country_ie[3]; + domain_info->country_code[2] = ' '; + + country_ie_len -= IEEE80211_COUNTRY_STRING_LEN; + + domain_info->no_of_triplet = + country_ie_len / sizeof(struct ieee80211_country_ie_triplet); + + memcpy((u8 *)domain_info->triplet, + &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); + + rcu_read_unlock(); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { + mwifiex_dbg(priv->adapter, ERROR, + "11D: setting domain info in FW fail\n"); + return -1; + } + + mwifiex_dnld_txpwr_table(priv); + + return 0; +} + +/* + * In Ad-Hoc mode, the IBSS is created if not found in scan list. + * In both Ad-Hoc and infra mode, an deauthentication is performed + * first. + */ +int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, + struct cfg80211_ssid *req_ssid) +{ + int ret; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_bssdescriptor *bss_desc = NULL; + + priv->scan_block = false; + + if (bss) { + mwifiex_process_country_ie(priv, bss); + + /* Allocate and fill new bss descriptor */ + bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), + GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); + if (ret) + goto done; + } + + if (priv->bss_mode == NL80211_IFTYPE_STATION || + priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { + u8 config_bands; + + if (!bss_desc) + return -1; + + if (mwifiex_band_to_radio_type(bss_desc->bss_band) == + HostCmd_SCAN_RADIO_TYPE_BG) { + config_bands = BAND_B | BAND_G | BAND_GN; + } else { + config_bands = BAND_A | BAND_AN; + if (adapter->fw_bands & BAND_AAC) + config_bands |= BAND_AAC; + } + + if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) + adapter->config_bands = config_bands; + + ret = mwifiex_check_network_compatibility(priv, bss_desc); + if (ret) + goto done; + + if (mwifiex_11h_get_csa_closed_channel(priv) == + (u8)bss_desc->channel) { + mwifiex_dbg(adapter, ERROR, + "Attempt to reconnect on csa closed chan(%d)\n", + bss_desc->channel); + goto done; + } + + mwifiex_dbg(adapter, INFO, + "info: SSID found in scan list ...\t" + "associating...\n"); + + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + + /* Clear any past association response stored for + * application retrieval */ + priv->assoc_rsp_size = 0; + ret = mwifiex_associate(priv, bss_desc); + + /* If auth type is auto and association fails using open mode, + * try to connect using shared mode */ + if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && + priv->sec_info.is_authtype_auto && + priv->sec_info.wep_enabled) { + priv->sec_info.authentication_mode = + NL80211_AUTHTYPE_SHARED_KEY; + ret = mwifiex_associate(priv, bss_desc); + } + + if (bss) + cfg80211_put_bss(priv->adapter->wiphy, bss); + } else { + /* Adhoc mode */ + /* If the requested SSID matches current SSID, return */ + if (bss_desc && bss_desc->ssid.ssid_len && + (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor. + ssid, &bss_desc->ssid))) { + ret = 0; + goto done; + } + + priv->adhoc_is_link_sensed = false; + + ret = mwifiex_check_network_compatibility(priv, bss_desc); + + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + + if (!ret) { + mwifiex_dbg(adapter, INFO, + "info: network found in scan\t" + " list. Joining...\n"); + ret = mwifiex_adhoc_join(priv, bss_desc); + if (bss) + cfg80211_put_bss(priv->adapter->wiphy, bss); + } else { + mwifiex_dbg(adapter, INFO, + "info: Network not found in\t" + "the list, creating adhoc with ssid = %s\n", + req_ssid->ssid); + ret = mwifiex_adhoc_start(priv, req_ssid); + } + } + +done: + /* beacon_ie buffer was allocated in function + * mwifiex_fill_new_bss_desc(). Free it now. + */ + if (bss_desc) + kfree(bss_desc->beacon_buf); + kfree(bss_desc); + return ret; +} + +/* + * IOCTL request handler to set host sleep configuration. + * + * This function prepares the correct firmware command and + * issues it. + */ +int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, + int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg) + +{ + struct mwifiex_adapter *adapter = priv->adapter; + int status = 0; + u32 prev_cond = 0; + + if (!hs_cfg) + return -ENOMEM; + + switch (action) { + case HostCmd_ACT_GEN_SET: + if (adapter->pps_uapsd_mode) { + mwifiex_dbg(adapter, INFO, + "info: Host Sleep IOCTL\t" + "is blocked in UAPSD/PPS mode\n"); + status = -1; + break; + } + if (hs_cfg->is_invoke_hostcmd) { + if (hs_cfg->conditions == HS_CFG_CANCEL) { + if (!adapter->is_hs_configured) + /* Already cancelled */ + break; + /* Save previous condition */ + prev_cond = le32_to_cpu(adapter->hs_cfg + .conditions); + adapter->hs_cfg.conditions = + cpu_to_le32(hs_cfg->conditions); + } else if (hs_cfg->conditions) { + adapter->hs_cfg.conditions = + cpu_to_le32(hs_cfg->conditions); + adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; + if (hs_cfg->gap) + adapter->hs_cfg.gap = (u8)hs_cfg->gap; + } else if (adapter->hs_cfg.conditions == + cpu_to_le32(HS_CFG_CANCEL)) { + /* Return failure if no parameters for HS + enable */ + status = -1; + break; + } + + status = mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_HS_CFG_ENH, + HostCmd_ACT_GEN_SET, 0, + &adapter->hs_cfg, + cmd_type == MWIFIEX_SYNC_CMD); + + if (hs_cfg->conditions == HS_CFG_CANCEL) + /* Restore previous condition */ + adapter->hs_cfg.conditions = + cpu_to_le32(prev_cond); + } else { + adapter->hs_cfg.conditions = + cpu_to_le32(hs_cfg->conditions); + adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; + adapter->hs_cfg.gap = (u8)hs_cfg->gap; + } + break; + case HostCmd_ACT_GEN_GET: + hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions); + hs_cfg->gpio = adapter->hs_cfg.gpio; + hs_cfg->gap = adapter->hs_cfg.gap; + break; + default: + status = -1; + break; + } + + return status; +} + +/* + * Sends IOCTL request to cancel the existing Host Sleep configuration. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type) +{ + struct mwifiex_ds_hs_cfg hscfg; + + hscfg.conditions = HS_CFG_CANCEL; + hscfg.is_invoke_hostcmd = true; + + return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, + cmd_type, &hscfg); +} +EXPORT_SYMBOL_GPL(mwifiex_cancel_hs); + +/* + * Sends IOCTL request to cancel the existing Host Sleep configuration. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int mwifiex_enable_hs(struct mwifiex_adapter *adapter) +{ + struct mwifiex_ds_hs_cfg hscfg; + struct mwifiex_private *priv; + int i; + + if (disconnect_on_suspend) { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + mwifiex_deauthenticate(priv, NULL); + } + } + + if (adapter->hs_activated) { + mwifiex_dbg(adapter, CMD, + "cmd: HS Already activated\n"); + return true; + } + + adapter->hs_activate_wait_q_woken = false; + + memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); + hscfg.is_invoke_hostcmd = true; + + adapter->hs_enabling = true; + mwifiex_cancel_all_pending_cmd(adapter); + + if (mwifiex_set_hs_params(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_STA), + HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD, + &hscfg)) { + mwifiex_dbg(adapter, ERROR, + "IOCTL request HS enable failed\n"); + return false; + } + + if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q, + adapter->hs_activate_wait_q_woken, + (10 * HZ)) <= 0) { + mwifiex_dbg(adapter, ERROR, + "hs_activate_wait_q terminated\n"); + return false; + } + + return true; +} +EXPORT_SYMBOL_GPL(mwifiex_enable_hs); + +/* + * IOCTL request handler to get BSS information. + * + * This function collates the information from different driver structures + * to send to the user. + */ +int mwifiex_get_bss_info(struct mwifiex_private *priv, + struct mwifiex_bss_info *info) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_bssdescriptor *bss_desc; + + if (!info) + return -1; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + + info->bss_mode = priv->bss_mode; + + memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid)); + + memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN); + + info->bss_chan = bss_desc->channel; + + memcpy(info->country_code, adapter->country_code, + IEEE80211_COUNTRY_STRING_LEN); + + info->media_connected = priv->media_connected; + + info->max_power_level = priv->max_tx_power_level; + info->min_power_level = priv->min_tx_power_level; + + info->adhoc_state = priv->adhoc_state; + + info->bcn_nf_last = priv->bcn_nf_last; + + if (priv->sec_info.wep_enabled) + info->wep_status = true; + else + info->wep_status = false; + + info->is_hs_configured = adapter->is_hs_configured; + info->is_deep_sleep = adapter->is_deep_sleep; + + return 0; +} + +/* + * The function disables auto deep sleep mode. + */ +int mwifiex_disable_auto_ds(struct mwifiex_private *priv) +{ + struct mwifiex_ds_auto_ds auto_ds; + + auto_ds.auto_ds = DEEP_SLEEP_OFF; + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true); +} +EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds); + +/* + * Sends IOCTL request to get the data rate. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate) +{ + int ret; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, 0, NULL, true); + + if (!ret) { + if (priv->is_data_rate_auto) + *rate = mwifiex_index_to_data_rate(priv, priv->tx_rate, + priv->tx_htinfo); + else + *rate = priv->data_rate; + } + + return ret; +} + +/* + * IOCTL request handler to set tx power configuration. + * + * This function prepares the correct firmware command and + * issues it. + * + * For non-auto power mode, all the following power groups are set - + * - Modulation class HR/DSSS + * - Modulation class OFDM + * - Modulation class HTBW20 + * - Modulation class HTBW40 + */ +int mwifiex_set_tx_power(struct mwifiex_private *priv, + struct mwifiex_power_cfg *power_cfg) +{ + int ret; + struct host_cmd_ds_txpwr_cfg *txp_cfg; + struct mwifiex_types_power_group *pg_tlv; + struct mwifiex_power_group *pg; + u8 *buf; + u16 dbm = 0; + + if (!power_cfg->is_power_auto) { + dbm = (u16) power_cfg->power_level; + if ((dbm < priv->min_tx_power_level) || + (dbm > priv->max_tx_power_level)) { + mwifiex_dbg(priv->adapter, ERROR, + "txpower value %d dBm\t" + "is out of range (%d dBm-%d dBm)\n", + dbm, priv->min_tx_power_level, + priv->max_tx_power_level); + return -1; + } + } + buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf; + txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET); + if (!power_cfg->is_power_auto) { + txp_cfg->mode = cpu_to_le32(1); + pg_tlv = (struct mwifiex_types_power_group *) + (buf + sizeof(struct host_cmd_ds_txpwr_cfg)); + pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP); + pg_tlv->length = + cpu_to_le16(4 * sizeof(struct mwifiex_power_group)); + pg = (struct mwifiex_power_group *) + (buf + sizeof(struct host_cmd_ds_txpwr_cfg) + + sizeof(struct mwifiex_types_power_group)); + /* Power group for modulation class HR/DSSS */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x03; + pg->modulation_class = MOD_CLASS_HR_DSSS; + pg->power_step = 0; + pg->power_min = (s8) dbm; + pg->power_max = (s8) dbm; + pg++; + /* Power group for modulation class OFDM */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x07; + pg->modulation_class = MOD_CLASS_OFDM; + pg->power_step = 0; + pg->power_min = (s8) dbm; + pg->power_max = (s8) dbm; + pg++; + /* Power group for modulation class HTBW20 */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x20; + pg->modulation_class = MOD_CLASS_HT; + pg->power_step = 0; + pg->power_min = (s8) dbm; + pg->power_max = (s8) dbm; + pg->ht_bandwidth = HT_BW_20; + pg++; + /* Power group for modulation class HTBW40 */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x20; + pg->modulation_class = MOD_CLASS_HT; + pg->power_step = 0; + pg->power_min = (s8) dbm; + pg->power_max = (s8) dbm; + pg->ht_bandwidth = HT_BW_40; + } + ret = mwifiex_send_cmd(priv, HostCmd_CMD_TXPWR_CFG, + HostCmd_ACT_GEN_SET, 0, buf, true); + + kfree(buf); + return ret; +} + +/* + * IOCTL request handler to get power save mode. + * + * This function prepares the correct firmware command and + * issues it. + */ +int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode) +{ + int ret; + struct mwifiex_adapter *adapter = priv->adapter; + u16 sub_cmd; + + if (*ps_mode) + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; + else + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; + sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + sub_cmd, BITMAP_STA_PS, NULL, true); + if ((!ret) && (sub_cmd == DIS_AUTO_PS)) + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + GET_PS, 0, NULL, false); + + return ret; +} + +/* + * IOCTL request handler to set/reset WPA IE. + * + * The supplied WPA IE is treated as a opaque buffer. Only the first field + * is checked to determine WPA version. If buffer length is zero, the existing + * WPA IE is reset. + */ +static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, + u8 *ie_data_ptr, u16 ie_len) +{ + if (ie_len) { + if (ie_len > sizeof(priv->wpa_ie)) { + mwifiex_dbg(priv->adapter, ERROR, + "failed to copy WPA IE, too big\n"); + return -1; + } + memcpy(priv->wpa_ie, ie_data_ptr, ie_len); + priv->wpa_ie_len = (u8) ie_len; + mwifiex_dbg(priv->adapter, CMD, + "cmd: Set Wpa_ie_len=%d IE=%#x\n", + priv->wpa_ie_len, priv->wpa_ie[0]); + + if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) { + priv->sec_info.wpa_enabled = true; + } else if (priv->wpa_ie[0] == WLAN_EID_RSN) { + priv->sec_info.wpa2_enabled = true; + } else { + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + } + } else { + memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie)); + priv->wpa_ie_len = 0; + mwifiex_dbg(priv->adapter, INFO, + "info: reset wpa_ie_len=%d IE=%#x\n", + priv->wpa_ie_len, priv->wpa_ie[0]); + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + } + + return 0; +} + +/* + * IOCTL request handler to set/reset WAPI IE. + * + * The supplied WAPI IE is treated as a opaque buffer. Only the first field + * is checked to internally enable WAPI. If buffer length is zero, the existing + * WAPI IE is reset. + */ +static int mwifiex_set_wapi_ie(struct mwifiex_private *priv, + u8 *ie_data_ptr, u16 ie_len) +{ + if (ie_len) { + if (ie_len > sizeof(priv->wapi_ie)) { + mwifiex_dbg(priv->adapter, ERROR, + "info: failed to copy WAPI IE, too big\n"); + return -1; + } + memcpy(priv->wapi_ie, ie_data_ptr, ie_len); + priv->wapi_ie_len = ie_len; + mwifiex_dbg(priv->adapter, CMD, + "cmd: Set wapi_ie_len=%d IE=%#x\n", + priv->wapi_ie_len, priv->wapi_ie[0]); + + if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY) + priv->sec_info.wapi_enabled = true; + } else { + memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie)); + priv->wapi_ie_len = ie_len; + mwifiex_dbg(priv->adapter, INFO, + "info: Reset wapi_ie_len=%d IE=%#x\n", + priv->wapi_ie_len, priv->wapi_ie[0]); + priv->sec_info.wapi_enabled = false; + } + return 0; +} + +/* + * IOCTL request handler to set/reset WPS IE. + * + * The supplied WPS IE is treated as a opaque buffer. Only the first field + * is checked to internally enable WPS. If buffer length is zero, the existing + * WPS IE is reset. + */ +static int mwifiex_set_wps_ie(struct mwifiex_private *priv, + u8 *ie_data_ptr, u16 ie_len) +{ + if (ie_len) { + if (ie_len > MWIFIEX_MAX_VSIE_LEN) { + mwifiex_dbg(priv->adapter, ERROR, + "info: failed to copy WPS IE, too big\n"); + return -1; + } + + priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL); + if (!priv->wps_ie) + return -ENOMEM; + + memcpy(priv->wps_ie, ie_data_ptr, ie_len); + priv->wps_ie_len = ie_len; + mwifiex_dbg(priv->adapter, CMD, + "cmd: Set wps_ie_len=%d IE=%#x\n", + priv->wps_ie_len, priv->wps_ie[0]); + } else { + kfree(priv->wps_ie); + priv->wps_ie_len = ie_len; + mwifiex_dbg(priv->adapter, INFO, + "info: Reset wps_ie_len=%d\n", priv->wps_ie_len); + } + return 0; +} + +/* + * IOCTL request handler to set WAPI key. + * + * This function prepares the correct firmware command and + * issues it. + */ +static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv, + struct mwifiex_ds_encrypt_key *encrypt_key) +{ + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, + encrypt_key, true); +} + +/* + * IOCTL request handler to set WEP network key. + * + * This function prepares the correct firmware command and + * issues it, after validation checks. + */ +static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, + struct mwifiex_ds_encrypt_key *encrypt_key) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + struct mwifiex_wep_key *wep_key; + int index; + + if (priv->wep_key_curr_index >= NUM_WEP_KEYS) + priv->wep_key_curr_index = 0; + wep_key = &priv->wep_key[priv->wep_key_curr_index]; + index = encrypt_key->key_index; + if (encrypt_key->key_disable) { + priv->sec_info.wep_enabled = 0; + } else if (!encrypt_key->key_len) { + /* Copy the required key as the current key */ + wep_key = &priv->wep_key[index]; + if (!wep_key->key_length) { + mwifiex_dbg(adapter, ERROR, + "key not set, so cannot enable it\n"); + return -1; + } + + if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) { + memcpy(encrypt_key->key_material, + wep_key->key_material, wep_key->key_length); + encrypt_key->key_len = wep_key->key_length; + } + + priv->wep_key_curr_index = (u16) index; + priv->sec_info.wep_enabled = 1; + } else { + wep_key = &priv->wep_key[index]; + memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); + /* Copy the key in the driver */ + memcpy(wep_key->key_material, + encrypt_key->key_material, + encrypt_key->key_len); + wep_key->key_index = index; + wep_key->key_length = encrypt_key->key_len; + priv->sec_info.wep_enabled = 1; + } + if (wep_key->key_length) { + void *enc_key; + + if (encrypt_key->key_disable) { + memset(&priv->wep_key[index], 0, + sizeof(struct mwifiex_wep_key)); + if (wep_key->key_length) + goto done; + } + + if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) + enc_key = encrypt_key; + else + enc_key = NULL; + + /* Send request to firmware */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, 0, enc_key, false); + if (ret) + return ret; + } + +done: + if (priv->sec_info.wep_enabled) + priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; + else + priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true); + + return ret; +} + +/* + * IOCTL request handler to set WPA key. + * + * This function prepares the correct firmware command and + * issues it, after validation checks. + * + * Current driver only supports key length of up to 32 bytes. + * + * This function can also be used to disable a currently set key. + */ +static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv, + struct mwifiex_ds_encrypt_key *encrypt_key) +{ + int ret; + u8 remove_key = false; + struct host_cmd_ds_802_11_key_material *ibss_key; + + /* Current driver only supports key length of up to 32 bytes */ + if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) { + mwifiex_dbg(priv->adapter, ERROR, + "key length too long\n"); + return -1; + } + + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { + /* + * IBSS/WPA-None uses only one key (Group) for both receiving + * and sending unicast and multicast packets. + */ + /* Send the key as PTK to firmware */ + encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, encrypt_key, false); + if (ret) + return ret; + + ibss_key = &priv->aes_key; + memset(ibss_key, 0, + sizeof(struct host_cmd_ds_802_11_key_material)); + /* Copy the key in the driver */ + memcpy(ibss_key->key_param_set.key, encrypt_key->key_material, + encrypt_key->key_len); + memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len, + sizeof(ibss_key->key_param_set.key_len)); + ibss_key->key_param_set.key_type_id + = cpu_to_le16(KEY_TYPE_ID_TKIP); + ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED); + + /* Send the key as GTK to firmware */ + encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST; + } + + if (!encrypt_key->key_index) + encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; + + if (remove_key) + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + !KEY_INFO_ENABLED, encrypt_key, true); + else + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, encrypt_key, true); + + return ret; +} + +/* + * IOCTL request handler to set/get network keys. + * + * This is a generic key handling function which supports WEP, WPA + * and WAPI. + */ +static int +mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv, + struct mwifiex_ds_encrypt_key *encrypt_key) +{ + int status; + + if (encrypt_key->is_wapi_key) + status = mwifiex_sec_ioctl_set_wapi_key(priv, encrypt_key); + else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104) + status = mwifiex_sec_ioctl_set_wpa_key(priv, encrypt_key); + else + status = mwifiex_sec_ioctl_set_wep_key(priv, encrypt_key); + return status; +} + +/* + * This function returns the driver version. + */ +int +mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version, + int max_len) +{ + union { + __le32 l; + u8 c[4]; + } ver; + char fw_ver[32]; + + ver.l = cpu_to_le32(adapter->fw_release_number); + sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]); + + snprintf(version, max_len, driver_version, fw_ver); + + mwifiex_dbg(adapter, MSG, "info: MWIFIEX VERSION: %s\n", version); + + return 0; +} + +/* + * Sends IOCTL request to set encoding parameters. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, + const u8 *key, int key_len, u8 key_index, + const u8 *mac_addr, int disable) +{ + struct mwifiex_ds_encrypt_key encrypt_key; + + memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key)); + encrypt_key.key_len = key_len; + encrypt_key.key_index = key_index; + + if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC) + encrypt_key.is_igtk_key = true; + + if (!disable) { + if (key_len) + memcpy(encrypt_key.key_material, key, key_len); + else + encrypt_key.is_current_wep_key = true; + + if (mac_addr) + memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); + if (kp && kp->seq && kp->seq_len) { + memcpy(encrypt_key.pn, kp->seq, kp->seq_len); + encrypt_key.pn_len = kp->seq_len; + encrypt_key.is_rx_seq_valid = true; + } + } else { + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + return 0; + encrypt_key.key_disable = true; + if (mac_addr) + memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); + } + + return mwifiex_sec_ioctl_encrypt_key(priv, &encrypt_key); +} + +/* + * Sends IOCTL request to get extended version. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_get_ver_ext(struct mwifiex_private *priv) +{ + struct mwifiex_ver_ext ver_ext; + + memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext)); + if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT, + HostCmd_ACT_GEN_GET, 0, &ver_ext, true)) + return -1; + + return 0; +} + +int +mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, + struct ieee80211_channel *chan, + unsigned int duration) +{ + struct host_cmd_ds_remain_on_chan roc_cfg; + u8 sc; + + memset(&roc_cfg, 0, sizeof(roc_cfg)); + roc_cfg.action = cpu_to_le16(action); + if (action == HostCmd_ACT_GEN_SET) { + roc_cfg.band_cfg = chan->band; + sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT); + roc_cfg.band_cfg |= (sc << 2); + + roc_cfg.channel = + ieee80211_frequency_to_channel(chan->center_freq); + roc_cfg.duration = cpu_to_le32(duration); + } + if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN, + action, 0, &roc_cfg, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "failed to remain on channel\n"); + return -1; + } + + return roc_cfg.status; +} + +/* + * Sends IOCTL request to get statistics information. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_get_stats_info(struct mwifiex_private *priv, + struct mwifiex_ds_get_stats *log) +{ + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_GET_LOG, + HostCmd_ACT_GEN_GET, 0, log, true); +} + +/* + * IOCTL request handler to read/write register. + * + * This function prepares the correct firmware command and + * issues it. + * + * Access to the following registers are supported - + * - MAC + * - BBP + * - RF + * - PMIC + * - CAU + */ +static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv, + struct mwifiex_ds_reg_rw *reg_rw, + u16 action) +{ + u16 cmd_no; + + switch (le32_to_cpu(reg_rw->type)) { + case MWIFIEX_REG_MAC: + cmd_no = HostCmd_CMD_MAC_REG_ACCESS; + break; + case MWIFIEX_REG_BBP: + cmd_no = HostCmd_CMD_BBP_REG_ACCESS; + break; + case MWIFIEX_REG_RF: + cmd_no = HostCmd_CMD_RF_REG_ACCESS; + break; + case MWIFIEX_REG_PMIC: + cmd_no = HostCmd_CMD_PMIC_REG_ACCESS; + break; + case MWIFIEX_REG_CAU: + cmd_no = HostCmd_CMD_CAU_REG_ACCESS; + break; + default: + return -1; + } + + return mwifiex_send_cmd(priv, cmd_no, action, 0, reg_rw, true); +} + +/* + * Sends IOCTL request to write to a register. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, + u32 reg_offset, u32 reg_value) +{ + struct mwifiex_ds_reg_rw reg_rw; + + reg_rw.type = cpu_to_le32(reg_type); + reg_rw.offset = cpu_to_le32(reg_offset); + reg_rw.value = cpu_to_le32(reg_value); + + return mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_SET); +} + +/* + * Sends IOCTL request to read from a register. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, + u32 reg_offset, u32 *value) +{ + int ret; + struct mwifiex_ds_reg_rw reg_rw; + + reg_rw.type = cpu_to_le32(reg_type); + reg_rw.offset = cpu_to_le32(reg_offset); + ret = mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_GET); + + if (ret) + goto done; + + *value = le32_to_cpu(reg_rw.value); + +done: + return ret; +} + +/* + * Sends IOCTL request to read from EEPROM. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, + u8 *value) +{ + int ret; + struct mwifiex_ds_read_eeprom rd_eeprom; + + rd_eeprom.offset = cpu_to_le16((u16) offset); + rd_eeprom.byte_count = cpu_to_le16((u16) bytes); + + /* Send request to firmware */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS, + HostCmd_ACT_GEN_GET, 0, &rd_eeprom, true); + + if (!ret) + memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA); + return ret; +} + +/* + * This function sets a generic IE. In addition to generic IE, it can + * also handle WPA, WPA2 and WAPI IEs. + */ +static int +mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, + u16 ie_len) +{ + int ret = 0; + struct ieee_types_vendor_header *pvendor_ie; + const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; + const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; + + /* If the passed length is zero, reset the buffer */ + if (!ie_len) { + priv->gen_ie_buf_len = 0; + priv->wps.session_enable = false; + + return 0; + } else if (!ie_data_ptr) { + return -1; + } + pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; + /* Test to see if it is a WPA IE, if not, then it is a gen IE */ + if (((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && + (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) || + (pvendor_ie->element_id == WLAN_EID_RSN)) { + + /* IE is a WPA/WPA2 IE so call set_wpa function */ + ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len); + priv->wps.session_enable = false; + + return ret; + } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { + /* IE is a WAPI IE so call set_wapi function */ + ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len); + + return ret; + } + /* + * Verify that the passed length is not larger than the + * available space remaining in the buffer + */ + if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) { + + /* Test to see if it is a WPS IE, if so, enable + * wps session flag + */ + pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; + if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && + (!memcmp(pvendor_ie->oui, wps_oui, sizeof(wps_oui)))) { + priv->wps.session_enable = true; + mwifiex_dbg(priv->adapter, INFO, + "info: WPS Session Enabled.\n"); + ret = mwifiex_set_wps_ie(priv, ie_data_ptr, ie_len); + } + + /* Append the passed data to the end of the + genIeBuffer */ + memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr, + ie_len); + /* Increment the stored buffer length by the + size passed */ + priv->gen_ie_buf_len += ie_len; + } else { + /* Passed data does not fit in the remaining + buffer space */ + ret = -1; + } + + /* Return 0, or -1 for error case */ + return ret; +} + +/* + * IOCTL request handler to set/get generic IE. + * + * In addition to various generic IEs, this function can also be + * used to set the ARP filter. + */ +static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, + struct mwifiex_ds_misc_gen_ie *gen_ie, + u16 action) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + switch (gen_ie->type) { + case MWIFIEX_IE_TYPE_GEN_IE: + if (action == HostCmd_ACT_GEN_GET) { + gen_ie->len = priv->wpa_ie_len; + memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len); + } else { + mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data, + (u16) gen_ie->len); + } + break; + case MWIFIEX_IE_TYPE_ARP_FILTER: + memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter)); + if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) { + adapter->arp_filter_size = 0; + mwifiex_dbg(adapter, ERROR, + "invalid ARP filter size\n"); + return -1; + } else { + memcpy(adapter->arp_filter, gen_ie->ie_data, + gen_ie->len); + adapter->arp_filter_size = gen_ie->len; + } + break; + default: + mwifiex_dbg(adapter, ERROR, "invalid IE type\n"); + return -1; + } + return 0; +} + +/* + * Sends IOCTL request to set a generic IE. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len) +{ + struct mwifiex_ds_misc_gen_ie gen_ie; + + if (ie_len > IEEE_MAX_IE_SIZE) + return -EFAULT; + + gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE; + gen_ie.len = ie_len; + memcpy(gen_ie.ie_data, ie, ie_len); + if (mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET)) + return -EFAULT; + + return 0; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sta_rx.c b/drivers/net/wireless/marvell/mwifiex/sta_rx.c new file mode 100644 index 000000000000..d4d4cb1ce95b --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_rx.c @@ -0,0 +1,267 @@ +/* + * Marvell Wireless LAN device driver: station RX data handling + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "11n_aggr.h" +#include "11n_rxreorder.h" + +/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement + * frame. If frame has both source and destination mac address as same, this + * function drops such gratuitous frames. + */ +static bool +mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + const struct mwifiex_arp_eth_header *arp; + struct ethhdr *eth; + struct ipv6hdr *ipv6; + struct icmp6hdr *icmpv6; + + eth = (struct ethhdr *)skb->data; + switch (ntohs(eth->h_proto)) { + case ETH_P_ARP: + arp = (void *)(skb->data + sizeof(struct ethhdr)); + if (arp->hdr.ar_op == htons(ARPOP_REPLY) || + arp->hdr.ar_op == htons(ARPOP_REQUEST)) { + if (!memcmp(arp->ar_sip, arp->ar_tip, 4)) + return true; + } + break; + case ETH_P_IPV6: + ipv6 = (void *)(skb->data + sizeof(struct ethhdr)); + icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) + + sizeof(struct ipv6hdr)); + if (NDISC_NEIGHBOUR_ADVERTISEMENT == icmpv6->icmp6_type) { + if (!memcmp(&ipv6->saddr, &ipv6->daddr, + sizeof(struct in6_addr))) + return true; + } + break; + default: + break; + } + + return false; +} + +/* + * This function processes the received packet and forwards it + * to kernel/upper layer. + * + * This function parses through the received packet and determines + * if it is a debug packet or normal packet. + * + * For non-debug packets, the function chops off unnecessary leading + * header bytes, reconstructs the packet as an ethernet frame or + * 802.2/llc/snap frame as required, and sends it to kernel/upper layer. + * + * The completion callback is called after processing in complete. + */ +int mwifiex_process_rx_packet(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + int ret; + struct rx_packet_hdr *rx_pkt_hdr; + struct rxpd *local_rx_pd; + int hdr_chop; + struct ethhdr *eth; + u16 rx_pkt_off, rx_pkt_len; + u8 *offset; + u8 adj_rx_rate = 0; + + local_rx_pd = (struct rxpd *) (skb->data); + + rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length); + rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off; + + if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, + sizeof(bridge_tunnel_header))) || + (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, + sizeof(rfc1042_header)) && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { + /* + * Replace the 803 header and rfc1042 header (llc/snap) with an + * EthernetII header, keep the src/dst and snap_type + * (ethertype). + * The firmware only passes up SNAP frames converting + * all RX Data from 802.11 to 802.2/LLC/SNAP frames. + * To create the Ethernet II, just move the src, dst address + * right before the snap_type. + */ + eth = (struct ethhdr *) + ((u8 *) &rx_pkt_hdr->eth803_hdr + + sizeof(rx_pkt_hdr->eth803_hdr) + + sizeof(rx_pkt_hdr->rfc1042_hdr) + - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) + - sizeof(rx_pkt_hdr->eth803_hdr.h_source) + - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); + + memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source, + sizeof(eth->h_source)); + memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, + sizeof(eth->h_dest)); + + /* Chop off the rxpd + the excess memory from the 802.2/llc/snap + header that was removed. */ + hdr_chop = (u8 *) eth - (u8 *) local_rx_pd; + } else { + /* Chop off the rxpd */ + hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr - + (u8 *) local_rx_pd; + } + + /* Chop off the leading header bytes so the it points to the start of + either the reconstructed EthII frame or the 802.2/llc/snap frame */ + skb_pull(skb, hdr_chop); + + if (priv->hs2_enabled && + mwifiex_discard_gratuitous_arp(priv, skb)) { + mwifiex_dbg(priv->adapter, INFO, "Bypassed Gratuitous ARP\n"); + dev_kfree_skb_any(skb); + return 0; + } + + if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { + offset = (u8 *)local_rx_pd + rx_pkt_off; + mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len); + } + + priv->rxpd_rate = local_rx_pd->rx_rate; + + priv->rxpd_htinfo = local_rx_pd->ht_info; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + adj_rx_rate = mwifiex_adjust_data_rate(priv, priv->rxpd_rate, + priv->rxpd_htinfo); + mwifiex_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr, + local_rx_pd->nf); + } + + ret = mwifiex_recv_packet(priv, skb); + if (ret == -1) + mwifiex_dbg(priv->adapter, ERROR, + "recv packet failed\n"); + + return ret; +} + +/* + * This function processes the received buffer. + * + * The function looks into the RxPD and performs sanity tests on the + * received buffer to ensure its a valid packet, before processing it + * further. If the packet is determined to be aggregated, it is + * de-aggregated accordingly. Non-unicast packets are sent directly to + * the kernel/upper layers. Unicast packets are handed over to the + * Rx reordering routine if 11n is enabled. + * + * The completion callback is called after processing in complete. + */ +int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret = 0; + struct rxpd *local_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + u8 ta[ETH_ALEN]; + u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num; + struct mwifiex_sta_node *sta_ptr; + + local_rx_pd = (struct rxpd *) (skb->data); + rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type); + rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length); + seq_num = le16_to_cpu(local_rx_pd->seq_num); + + rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset; + + if ((rx_pkt_offset + rx_pkt_length) > (u16) skb->len) { + mwifiex_dbg(adapter, ERROR, + "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n", + skb->len, rx_pkt_offset, rx_pkt_length); + priv->stats.rx_dropped++; + dev_kfree_skb_any(skb); + return ret; + } + + if (rx_pkt_type == PKT_TYPE_MGMT) { + ret = mwifiex_process_mgmt_packet(priv, skb); + if (ret) + mwifiex_dbg(adapter, ERROR, "Rx of mgmt packet failed"); + dev_kfree_skb_any(skb); + return ret; + } + + /* + * If the packet is not an unicast packet then send the packet + * directly to os. Don't pass thru rx reordering + */ + if ((!IS_11N_ENABLED(priv) && + !(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + !(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) || + !ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) { + mwifiex_process_rx_packet(priv, skb); + return ret; + } + + if (mwifiex_queuing_ra_based(priv) || + (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) { + memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); + if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET && + local_rx_pd->priority < MAX_NUM_TID) { + sta_ptr = mwifiex_get_sta_entry(priv, ta); + if (sta_ptr) + sta_ptr->rx_seq[local_rx_pd->priority] = + le16_to_cpu(local_rx_pd->seq_num); + mwifiex_auto_tdls_update_peer_signal(priv, ta, + local_rx_pd->snr, + local_rx_pd->nf); + } + } else { + if (rx_pkt_type != PKT_TYPE_BAR) + priv->rx_seq[local_rx_pd->priority] = seq_num; + memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address, + ETH_ALEN); + } + + /* Reorder and send to OS */ + ret = mwifiex_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority, + ta, (u8) rx_pkt_type, skb); + + if (ret || (rx_pkt_type == PKT_TYPE_BAR)) + dev_kfree_skb_any(skb); + + if (ret) + priv->stats.rx_dropped++; + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sta_tx.c b/drivers/net/wireless/marvell/mwifiex/sta_tx.c new file mode 100644 index 000000000000..f6683ea6bd5d --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_tx.c @@ -0,0 +1,244 @@ +/* + * Marvell Wireless LAN device driver: station TX data handling + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" + +/* + * This function fills the TxPD for tx packets. + * + * The Tx buffer received by this function should already have the + * header space allocated for TxPD. + * + * This function inserts the TxPD in between interface header and actual + * data and adjusts the buffer pointers accordingly. + * + * The following TxPD fields are set by this function, as required - + * - BSS number + * - Tx packet length and offset + * - Priority + * - Packet delay + * - Priority specific Tx control + * - Flags + */ +void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct txpd *local_tx_pd; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + unsigned int pad; + u16 pkt_type, pkt_offset; + int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : + INTF_HEADER_LEN; + + if (!skb->len) { + mwifiex_dbg(adapter, ERROR, + "Tx: bad packet length: %d\n", skb->len); + tx_info->status_code = -1; + return skb->data; + } + + BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); + + pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + + pad = ((void *)skb->data - (sizeof(*local_tx_pd) + hroom)- + NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); + skb_push(skb, sizeof(*local_tx_pd) + pad); + + local_tx_pd = (struct txpd *) skb->data; + memset(local_tx_pd, 0, sizeof(struct txpd)); + local_tx_pd->bss_num = priv->bss_num; + local_tx_pd->bss_type = priv->bss_type; + local_tx_pd->tx_pkt_length = cpu_to_le16((u16)(skb->len - + (sizeof(struct txpd) + + pad))); + + local_tx_pd->priority = (u8) skb->priority; + local_tx_pd->pkt_delay_2ms = + mwifiex_wmm_compute_drv_pkt_delay(priv, skb); + + if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS || + tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) { + local_tx_pd->tx_token_id = tx_info->ack_frame_id; + local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS; + } + + if (local_tx_pd->priority < + ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) + /* + * Set the priority specific tx_control field, setting of 0 will + * cause the default value to be used later in this function + */ + local_tx_pd->tx_control = + cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd-> + priority]); + + if (adapter->pps_uapsd_mode) { + if (mwifiex_check_last_packet_indication(priv)) { + adapter->tx_lock_flag = true; + local_tx_pd->flags = + MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; + } + } + + if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; + + /* Offset of actual data */ + pkt_offset = sizeof(struct txpd) + pad; + if (pkt_type == PKT_TYPE_MGMT) { + /* Set the packet type and add header for management frame */ + local_tx_pd->tx_pkt_type = cpu_to_le16(pkt_type); + pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; + } + + local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset); + + /* make space for INTF_HEADER_LEN */ + skb_push(skb, hroom); + + if (!local_tx_pd->tx_control) + /* TxCtrl set by user or default */ + local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); + + return skb->data; +} + +/* + * This function tells firmware to send a NULL data packet. + * + * The function creates a NULL data packet with TxPD and sends to the + * firmware for transmission, with highest priority setting. + */ +int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct txpd *local_tx_pd; + struct mwifiex_tx_param tx_param; +/* sizeof(struct txpd) + Interface specific header */ +#define NULL_PACKET_HDR 64 + u32 data_len = NULL_PACKET_HDR; + struct sk_buff *skb; + int ret; + struct mwifiex_txinfo *tx_info = NULL; + + if (adapter->surprise_removed) + return -1; + + if (!priv->media_connected) + return -1; + + if (adapter->data_sent) + return -1; + + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + return -1; + + skb = dev_alloc_skb(data_len); + if (!skb) + return -1; + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = data_len - (sizeof(struct txpd) + INTF_HEADER_LEN); + skb_reserve(skb, sizeof(struct txpd) + INTF_HEADER_LEN); + skb_push(skb, sizeof(struct txpd)); + + local_tx_pd = (struct txpd *) skb->data; + local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); + local_tx_pd->flags = flags; + local_tx_pd->priority = WMM_HIGHEST_PRIORITY; + local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); + local_tx_pd->bss_num = priv->bss_num; + local_tx_pd->bss_type = priv->bss_type; + + if (adapter->iface_type == MWIFIEX_USB) { + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, + skb, NULL); + } else { + skb_push(skb, INTF_HEADER_LEN); + tx_param.next_pkt_len = 0; + ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, + skb, &tx_param); + } + switch (ret) { + case -EBUSY: + dev_kfree_skb_any(skb); + mwifiex_dbg(adapter, ERROR, + "%s: host_to_card failed: ret=%d\n", + __func__, ret); + adapter->dbg.num_tx_host_to_card_failure++; + break; + case -1: + dev_kfree_skb_any(skb); + mwifiex_dbg(adapter, ERROR, + "%s: host_to_card failed: ret=%d\n", + __func__, ret); + adapter->dbg.num_tx_host_to_card_failure++; + break; + case 0: + dev_kfree_skb_any(skb); + mwifiex_dbg(adapter, DATA, + "data: %s: host_to_card succeeded\n", + __func__); + adapter->tx_lock_flag = true; + break; + case -EINPROGRESS: + adapter->tx_lock_flag = true; + break; + default: + break; + } + + return ret; +} + +/* + * This function checks if we need to send last packet indication. + */ +u8 +mwifiex_check_last_packet_indication(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u8 ret = false; + + if (!adapter->sleep_period.period) + return ret; + if (mwifiex_wmm_lists_empty(adapter)) + ret = true; + + if (ret && !adapter->cmd_sent && !adapter->curr_cmd && + !is_command_pending(adapter)) { + adapter->delay_null_pkt = false; + ret = true; + } else { + ret = false; + adapter->delay_null_pkt = true; + } + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c new file mode 100644 index 000000000000..9275f9c3f869 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -0,0 +1,1500 @@ +/* Marvell Wireless LAN device driver: TDLS handling + * + * 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 "main.h" +#include "wmm.h" +#include "11n.h" +#include "11n_rxreorder.h" +#include "11ac.h" + +#define TDLS_REQ_FIX_LEN 6 +#define TDLS_RESP_FIX_LEN 8 +#define TDLS_CONFIRM_FIX_LEN 6 +#define MWIFIEX_TDLS_WMM_INFO_SIZE 7 + +static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv, + const u8 *mac, u8 status) +{ + struct mwifiex_ra_list_tbl *ra_list; + struct list_head *tid_list; + struct sk_buff *skb, *tmp; + struct mwifiex_txinfo *tx_info; + unsigned long flags; + u32 tid; + u8 tid_down; + + mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { + if (!ether_addr_equal(mac, skb->data)) + continue; + + __skb_unlink(skb, &priv->tdls_txq); + tx_info = MWIFIEX_SKB_TXCB(skb); + tid = skb->priority; + tid_down = mwifiex_wmm_downgrade_tid(priv, tid); + + if (mwifiex_is_tdls_link_setup(status)) { + ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac); + ra_list->tdls_link = true; + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + } else { + tid_list = &priv->wmm.tid_tbl_ptr[tid_down].ra_list; + if (!list_empty(tid_list)) + ra_list = list_first_entry(tid_list, + struct mwifiex_ra_list_tbl, list); + else + ra_list = NULL; + tx_info->flags &= ~MWIFIEX_BUF_FLAG_TDLS_PKT; + } + + if (!ra_list) { + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + continue; + } + + skb_queue_tail(&ra_list->skb_head, skb); + + ra_list->ba_pkt_count++; + ra_list->total_pkt_count++; + + if (atomic_read(&priv->wmm.highest_queued_prio) < + tos_to_tid_inv[tid_down]) + atomic_set(&priv->wmm.highest_queued_prio, + tos_to_tid_inv[tid_down]); + + atomic_inc(&priv->wmm.tx_pkts_queued); + } + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + return; +} + +static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, + const u8 *mac) +{ + struct mwifiex_ra_list_tbl *ra_list; + struct list_head *ra_list_head; + struct sk_buff *skb, *tmp; + unsigned long flags; + int i; + + mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; i++) { + if (!list_empty(&priv->wmm.tid_tbl_ptr[i].ra_list)) { + ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; + list_for_each_entry(ra_list, ra_list_head, list) { + skb_queue_walk_safe(&ra_list->skb_head, skb, + tmp) { + if (!ether_addr_equal(mac, skb->data)) + continue; + __skb_unlink(skb, &ra_list->skb_head); + atomic_dec(&priv->wmm.tx_pkts_queued); + ra_list->total_pkt_count--; + skb_queue_tail(&priv->tdls_txq, skb); + } + } + } + } + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + return; +} + +/* This function appends rate TLV to scan config command. */ +static int +mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + u8 rates[MWIFIEX_SUPPORTED_RATES], *pos; + u16 rates_size, supp_rates_size, ext_rates_size; + + memset(rates, 0, sizeof(rates)); + rates_size = mwifiex_get_supported_rates(priv, rates); + + supp_rates_size = min_t(u16, rates_size, MWIFIEX_TDLS_SUPPORTED_RATES); + + if (skb_tailroom(skb) < rates_size + 4) { + mwifiex_dbg(priv->adapter, ERROR, + "Insuffient space while adding rates\n"); + return -ENOMEM; + } + + pos = skb_put(skb, supp_rates_size + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = supp_rates_size; + memcpy(pos, rates, supp_rates_size); + + if (rates_size > MWIFIEX_TDLS_SUPPORTED_RATES) { + ext_rates_size = rates_size - MWIFIEX_TDLS_SUPPORTED_RATES; + pos = skb_put(skb, ext_rates_size + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = ext_rates_size; + memcpy(pos, rates + MWIFIEX_TDLS_SUPPORTED_RATES, + ext_rates_size); + } + + return 0; +} + +static void mwifiex_tdls_add_aid(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ieee_types_assoc_rsp *assoc_rsp; + u8 *pos; + + assoc_rsp = (struct ieee_types_assoc_rsp *)&priv->assoc_rsp_buf; + pos = (void *)skb_put(skb, 4); + *pos++ = WLAN_EID_AID; + *pos++ = 2; + memcpy(pos, &assoc_rsp->a_id, sizeof(assoc_rsp->a_id)); + + return; +} + +static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ieee80211_vht_cap vht_cap; + u8 *pos; + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); + *pos++ = WLAN_EID_VHT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_vht_cap); + + memset(&vht_cap, 0, sizeof(struct ieee80211_vht_cap)); + + mwifiex_fill_vht_cap_tlv(priv, &vht_cap, priv->curr_bss_params.band); + memcpy(pos, &vht_cap, sizeof(vht_cap)); + + return 0; +} + +static int +mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, + u8 vht_enabled, struct sk_buff *skb) +{ + struct ieee80211_ht_operation *ht_oper; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_bssdescriptor *bss_desc = + &priv->curr_bss_params.bss_descriptor; + u8 *pos; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (unlikely(!sta_ptr)) { + mwifiex_dbg(priv->adapter, ERROR, + "TDLS peer station not found in list\n"); + return -1; + } + + if (!(le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info))) { + mwifiex_dbg(priv->adapter, WARN, + "TDLS peer doesn't support ht capabilities\n"); + return 0; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2); + *pos++ = WLAN_EID_HT_OPERATION; + *pos++ = sizeof(struct ieee80211_ht_operation); + ht_oper = (void *)pos; + + ht_oper->primary_chan = bss_desc->channel; + + /* follow AP's channel bandwidth */ + if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && + bss_desc->bcn_ht_cap && + ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) + ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param; + + if (vht_enabled) { + ht_oper->ht_param = + mwifiex_get_sec_chan_offset(bss_desc->channel); + ht_oper->ht_param |= BIT(2); + } + + memcpy(&sta_ptr->tdls_cap.ht_oper, ht_oper, + sizeof(struct ieee80211_ht_operation)); + + return 0; +} + +static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, + const u8 *mac, struct sk_buff *skb) +{ + struct mwifiex_bssdescriptor *bss_desc; + struct ieee80211_vht_operation *vht_oper; + struct ieee80211_vht_cap *vht_cap, *ap_vht_cap = NULL; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_adapter *adapter = priv->adapter; + u8 supp_chwd_set, peer_supp_chwd_set; + u8 *pos, ap_supp_chwd_set, chan_bw; + u16 mcs_map_user, mcs_map_resp, mcs_map_result; + u16 mcs_user, mcs_resp, nss; + u32 usr_vht_cap_info; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (unlikely(!sta_ptr)) { + mwifiex_dbg(adapter, ERROR, + "TDLS peer station not found in list\n"); + return -1; + } + + if (!(le32_to_cpu(sta_ptr->tdls_cap.vhtcap.vht_cap_info))) { + mwifiex_dbg(adapter, WARN, + "TDLS peer doesn't support vht capabilities\n"); + return 0; + } + + if (!mwifiex_is_bss_in_11ac_mode(priv)) { + if (sta_ptr->tdls_cap.extcap.ext_capab[7] & + WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { + mwifiex_dbg(adapter, WARN, + "TDLS peer doesn't support wider bandwidth\n"); + return 0; + } + } else { + ap_vht_cap = bss_desc->bcn_vht_cap; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_operation) + 2); + *pos++ = WLAN_EID_VHT_OPERATION; + *pos++ = sizeof(struct ieee80211_vht_operation); + vht_oper = (struct ieee80211_vht_operation *)pos; + + if (bss_desc->bss_band & BAND_A) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; + + /* find the minmum bandwith between AP/TDLS peers */ + vht_cap = &sta_ptr->tdls_cap.vhtcap; + supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); + peer_supp_chwd_set = + GET_VHTCAP_CHWDSET(le32_to_cpu(vht_cap->vht_cap_info)); + supp_chwd_set = min_t(u8, supp_chwd_set, peer_supp_chwd_set); + + /* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */ + + if (ap_vht_cap && sta_ptr->tdls_cap.extcap.ext_capab[7] & + WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { + ap_supp_chwd_set = + GET_VHTCAP_CHWDSET(le32_to_cpu(ap_vht_cap->vht_cap_info)); + supp_chwd_set = min_t(u8, supp_chwd_set, ap_supp_chwd_set); + } + + switch (supp_chwd_set) { + case IEEE80211_VHT_CHANWIDTH_80MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; + break; + default: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + + mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); + mcs_map_result = 0; + + for (nss = 1; nss <= 8; nss++) { + mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); + + if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min_t(u16, mcs_user, mcs_resp)); + } + + vht_oper->basic_mcs_set = cpu_to_le16(mcs_map_result); + + switch (vht_oper->chan_width) { + case IEEE80211_VHT_CHANWIDTH_80MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_160MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + default: + chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + vht_oper->center_freq_seg1_idx = + mwifiex_get_center_freq_index(priv, BAND_AAC, + bss_desc->channel, + chan_bw); + + return 0; +} + +static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ieee_types_extcap *extcap; + + extcap = (void *)skb_put(skb, sizeof(struct ieee_types_extcap)); + extcap->ieee_hdr.element_id = WLAN_EID_EXT_CAPABILITY; + extcap->ieee_hdr.len = 8; + memset(extcap->ext_capab, 0, 8); + extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED; + extcap->ext_capab[3] |= WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH; + + if (priv->adapter->is_hw_11ac_capable) + extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED; +} + +static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb) +{ + u8 *pos = (void *)skb_put(skb, 3); + + *pos++ = WLAN_EID_QOS_CAPA; + *pos++ = 1; + *pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB; +} + +static void +mwifiex_tdls_add_wmm_param_ie(struct mwifiex_private *priv, struct sk_buff *skb) +{ + struct ieee80211_wmm_param_ie *wmm; + u8 ac_vi[] = {0x42, 0x43, 0x5e, 0x00}; + u8 ac_vo[] = {0x62, 0x32, 0x2f, 0x00}; + u8 ac_be[] = {0x03, 0xa4, 0x00, 0x00}; + u8 ac_bk[] = {0x27, 0xa4, 0x00, 0x00}; + + wmm = (void *)skb_put(skb, sizeof(*wmm)); + memset(wmm, 0, sizeof(*wmm)); + + wmm->element_id = WLAN_EID_VENDOR_SPECIFIC; + wmm->len = sizeof(*wmm) - 2; + wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */ + wmm->oui[1] = 0x50; + wmm->oui[2] = 0xf2; + wmm->oui_type = 2; /* WME */ + wmm->oui_subtype = 1; /* WME param */ + wmm->version = 1; /* WME ver */ + wmm->qos_info = 0; /* U-APSD not in use */ + + /* use default WMM AC parameters for TDLS link*/ + memcpy(&wmm->ac[0], ac_be, sizeof(ac_be)); + memcpy(&wmm->ac[1], ac_bk, sizeof(ac_bk)); + memcpy(&wmm->ac[2], ac_vi, sizeof(ac_vi)); + memcpy(&wmm->ac[3], ac_vo, sizeof(ac_vo)); +} + +static void +mwifiex_add_wmm_info_ie(struct mwifiex_private *priv, struct sk_buff *skb, + u8 qosinfo) +{ + u8 *buf; + + buf = (void *)skb_put(skb, MWIFIEX_TDLS_WMM_INFO_SIZE + + sizeof(struct ieee_types_header)); + + *buf++ = WLAN_EID_VENDOR_SPECIFIC; + *buf++ = 7; /* len */ + *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */ + *buf++ = 0x50; + *buf++ = 0xf2; + *buf++ = 2; /* WME */ + *buf++ = 0; /* WME info */ + *buf++ = 1; /* WME ver */ + *buf++ = qosinfo; /* U-APSD no in use */ +} + +static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, + const u8 *peer, u8 action_code, + u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_tdls_data *tf; + int ret; + u16 capab; + struct ieee80211_ht_cap *ht_cap; + u8 radio, *pos; + + capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; + + tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); + memcpy(tf->da, peer, ETH_ALEN); + memcpy(tf->sa, priv->curr_addr, ETH_ALEN); + tf->ether_type = cpu_to_be16(ETH_P_TDLS); + tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_REQUEST; + skb_put(skb, sizeof(tf->u.setup_req)); + tf->u.setup_req.dialog_token = dialog_token; + tf->u.setup_req.capability = cpu_to_le16(capab); + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); + mwifiex_tdls_add_qos_capab(skb); + mwifiex_add_wmm_info_ie(priv, skb, 0); + break; + + case WLAN_TDLS_SETUP_RESPONSE: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_RESPONSE; + skb_put(skb, sizeof(tf->u.setup_resp)); + tf->u.setup_resp.status_code = cpu_to_le16(status_code); + tf->u.setup_resp.dialog_token = dialog_token; + tf->u.setup_resp.capability = cpu_to_le16(capab); + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); + mwifiex_tdls_add_qos_capab(skb); + mwifiex_add_wmm_info_ie(priv, skb, 0); + break; + + case WLAN_TDLS_SETUP_CONFIRM: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_CONFIRM; + skb_put(skb, sizeof(tf->u.setup_cfm)); + tf->u.setup_cfm.status_code = cpu_to_le16(status_code); + tf->u.setup_cfm.dialog_token = dialog_token; + + mwifiex_tdls_add_wmm_param_ie(priv, skb); + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_oper(priv, peer, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + ret = mwifiex_tdls_add_ht_oper(priv, peer, 1, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + } else { + ret = mwifiex_tdls_add_ht_oper(priv, peer, 0, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + } + break; + + case WLAN_TDLS_TEARDOWN: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_TEARDOWN; + skb_put(skb, sizeof(tf->u.teardown)); + tf->u.teardown.reason_code = cpu_to_le16(status_code); + break; + + case WLAN_TDLS_DISCOVERY_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + skb_put(skb, sizeof(tf->u.discover_req)); + tf->u.discover_req.dialog_token = dialog_token; + break; + default: + mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS frame type.\n"); + return -EINVAL; + } + + return 0; +} + +static void +mwifiex_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, + const u8 *peer, const u8 *bssid) +{ + struct ieee80211_tdls_lnkie *lnkid; + + lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); + lnkid->ie_type = WLAN_EID_LINK_ID; + lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - + sizeof(struct ieee_types_header); + + memcpy(lnkid->bssid, bssid, ETH_ALEN); + memcpy(lnkid->init_sta, src_addr, ETH_ALEN); + memcpy(lnkid->resp_sta, peer, ETH_ALEN); +} + +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + int ret; + u16 skb_len; + + skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + MWIFIEX_SUPPORTED_RATES + + 3 + /* Qos Info */ + sizeof(struct ieee_types_extcap) + + sizeof(struct ieee80211_ht_cap) + + sizeof(struct ieee_types_bss_co_2040) + + sizeof(struct ieee80211_ht_operation) + + sizeof(struct ieee80211_tdls_lnkie) + + sizeof(struct ieee80211_wmm_param_ie) + + extra_ies_len; + + if (priv->adapter->is_hw_11ac_capable) + skb_len += sizeof(struct ieee_types_vht_cap) + + sizeof(struct ieee_types_vht_oper) + + sizeof(struct ieee_types_aid); + + skb = dev_alloc_skb(skb_len); + if (!skb) { + mwifiex_dbg(priv->adapter, ERROR, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, + dialog_token, status_code, + skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, + extra_ies_len); + mwifiex_tdls_add_link_ie(skb, priv->curr_addr, peer, + priv->cfg_bssid); + break; + case WLAN_TDLS_SETUP_RESPONSE: + ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, + dialog_token, status_code, + skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, + extra_ies_len); + mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, + priv->cfg_bssid); + break; + } + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + skb->priority = MWIFIEX_PRIO_BK; + break; + default: + skb->priority = MWIFIEX_PRIO_VI; + break; + } + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + + __net_timestamp(skb); + mwifiex_queue_tx_pkt(priv, skb); + + return 0; +} + +static int +mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, + const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt; + u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + int ret; + u16 capab; + struct ieee80211_ht_cap *ht_cap; + u8 radio, *pos; + + capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; + + mgmt = (void *)skb_put(skb, offsetof(struct ieee80211_mgmt, u)); + + memset(mgmt, 0, 24); + memcpy(mgmt->da, peer, ETH_ALEN); + memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN); + memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + /* add address 4 */ + pos = skb_put(skb, ETH_ALEN); + + switch (action_code) { + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + skb_put(skb, sizeof(mgmt->u.action.u.tdls_discover_resp) + 1); + mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; + mgmt->u.action.u.tdls_discover_resp.action_code = + WLAN_PUB_ACTION_TDLS_DISCOVER_RES; + mgmt->u.action.u.tdls_discover_resp.dialog_token = + dialog_token; + mgmt->u.action.u.tdls_discover_resp.capability = + cpu_to_le16(capab); + /* move back for addr4 */ + memmove(pos + ETH_ALEN, &mgmt->u.action.category, + sizeof(mgmt->u.action.u.tdls_discover_resp)); + /* init address 4 */ + memcpy(pos, bc_addr, ETH_ALEN); + + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); + mwifiex_tdls_add_qos_capab(skb); + break; + default: + mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS action frame type\n"); + return -EINVAL; + } + + return 0; +} + +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + u8 *pos; + u32 pkt_type, tx_control; + u16 pkt_len, skb_len; + + skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + MWIFIEX_SUPPORTED_RATES + + sizeof(struct ieee_types_extcap) + + sizeof(struct ieee80211_ht_cap) + + sizeof(struct ieee_types_bss_co_2040) + + sizeof(struct ieee80211_ht_operation) + + sizeof(struct ieee80211_tdls_lnkie) + + extra_ies_len + + 3 + /* Qos Info */ + ETH_ALEN; /* Address4 */ + + if (priv->adapter->is_hw_11ac_capable) + skb_len += sizeof(struct ieee_types_vht_cap) + + sizeof(struct ieee_types_vht_oper) + + sizeof(struct ieee_types_aid); + + skb = dev_alloc_skb(skb_len); + if (!skb) { + mwifiex_dbg(priv->adapter, ERROR, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + + skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + + pkt_type = PKT_TYPE_MGMT; + tx_control = 0; + pos = skb_put(skb, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memset(pos, 0, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memcpy(pos, &pkt_type, sizeof(pkt_type)); + memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control)); + + if (mwifiex_construct_tdls_action_frame(priv, peer, action_code, + dialog_token, status_code, + skb)) { + dev_kfree_skb_any(skb); + return -EINVAL; + } + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + + /* the TDLS link IE is always added last we are the responder */ + + mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, + priv->cfg_bssid); + + skb->priority = MWIFIEX_PRIO_VI; + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + + pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len); + memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len, + sizeof(pkt_len)); + __net_timestamp(skb); + mwifiex_queue_tx_pkt(priv, skb); + + return 0; +} + +/* This function process tdls action frame from peer. + * Peer capabilities are stored into station node structure. + */ +void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, + u8 *buf, int len) +{ + struct mwifiex_sta_node *sta_ptr; + u8 *peer, *pos, *end; + u8 i, action, basic; + __le16 cap = 0; + int ie_len = 0; + + if (len < (sizeof(struct ethhdr) + 3)) + return; + if (*(buf + sizeof(struct ethhdr)) != WLAN_TDLS_SNAP_RFTYPE) + return; + if (*(buf + sizeof(struct ethhdr) + 1) != WLAN_CATEGORY_TDLS) + return; + + peer = buf + ETH_ALEN; + action = *(buf + sizeof(struct ethhdr) + 2); + mwifiex_dbg(priv->adapter, DATA, + "rx:tdls action: peer=%pM, action=%d\n", peer, action); + + switch (action) { + case WLAN_TDLS_SETUP_REQUEST: + if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN)) + return; + + pos = buf + sizeof(struct ethhdr) + 4; + /* payload 1+ category 1 + action 1 + dialog 1 */ + cap = cpu_to_le16(*(u16 *)pos); + ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; + pos += 2; + break; + + case WLAN_TDLS_SETUP_RESPONSE: + if (len < (sizeof(struct ethhdr) + TDLS_RESP_FIX_LEN)) + return; + /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/ + pos = buf + sizeof(struct ethhdr) + 6; + cap = cpu_to_le16(*(u16 *)pos); + ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; + pos += 2; + break; + + case WLAN_TDLS_SETUP_CONFIRM: + if (len < (sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN)) + return; + pos = buf + sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN; + ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN; + break; + default: + mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS frame type.\n"); + return; + } + + sta_ptr = mwifiex_add_sta_entry(priv, peer); + if (!sta_ptr) + return; + + sta_ptr->tdls_cap.capab = cap; + + for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos + 2 + pos[1] > end) + break; + + switch (*pos) { + case WLAN_EID_SUPP_RATES: + sta_ptr->tdls_cap.rates_len = pos[1]; + for (i = 0; i < pos[1]; i++) + sta_ptr->tdls_cap.rates[i] = pos[i + 2]; + break; + + case WLAN_EID_EXT_SUPP_RATES: + basic = sta_ptr->tdls_cap.rates_len; + for (i = 0; i < pos[1]; i++) + sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2]; + sta_ptr->tdls_cap.rates_len += pos[1]; + break; + case WLAN_EID_HT_CAPABILITY: + memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos, + sizeof(struct ieee80211_ht_cap)); + sta_ptr->is_11n_enabled = 1; + break; + case WLAN_EID_HT_OPERATION: + memcpy(&sta_ptr->tdls_cap.ht_oper, pos, + sizeof(struct ieee80211_ht_operation)); + break; + case WLAN_EID_BSS_COEX_2040: + sta_ptr->tdls_cap.coex_2040 = pos[2]; + break; + case WLAN_EID_EXT_CAPABILITY: + memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos, + sizeof(struct ieee_types_header) + + min_t(u8, pos[1], 8)); + break; + case WLAN_EID_RSN: + memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos, + sizeof(struct ieee_types_header) + + min_t(u8, pos[1], IEEE_MAX_IE_SIZE - + sizeof(struct ieee_types_header))); + break; + case WLAN_EID_QOS_CAPA: + sta_ptr->tdls_cap.qos_info = pos[2]; + break; + case WLAN_EID_VHT_OPERATION: + if (priv->adapter->is_hw_11ac_capable) + memcpy(&sta_ptr->tdls_cap.vhtoper, pos, + sizeof(struct ieee80211_vht_operation)); + break; + case WLAN_EID_VHT_CAPABILITY: + if (priv->adapter->is_hw_11ac_capable) { + memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos, + sizeof(struct ieee80211_vht_cap)); + sta_ptr->is_11ac_enabled = 1; + } + break; + case WLAN_EID_AID: + if (priv->adapter->is_hw_11ac_capable) + sta_ptr->tdls_cap.aid = + le16_to_cpu(*(__le16 *)(pos + 2)); + default: + break; + } + } + + return; +} + +static int +mwifiex_tdls_process_config_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (!sta_ptr || sta_ptr->tdls_status == TDLS_SETUP_FAILURE) { + mwifiex_dbg(priv->adapter, ERROR, + "link absent for peer %pM; cannot config\n", peer); + return -EINVAL; + } + + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_CONFIG_LINK; + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); +} + +static int +mwifiex_tdls_process_create_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr && sta_ptr->tdls_status == TDLS_SETUP_INPROGRESS) { + mwifiex_dbg(priv->adapter, WARN, + "Setup already in progress for peer %pM\n", peer); + return 0; + } + + sta_ptr = mwifiex_add_sta_entry(priv, peer); + if (!sta_ptr) + return -ENOMEM; + + sta_ptr->tdls_status = TDLS_SETUP_INPROGRESS; + mwifiex_hold_tdls_packets(priv, peer); + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK; + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); +} + +static int +mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + unsigned long flags; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr) { + if (sta_ptr->is_11n_enabled) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + } + mwifiex_del_sta_entry(priv, peer); + } + + mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); + mwifiex_auto_tdls_update_peer_status(priv, peer, TDLS_NOT_SETUP); + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); +} + +static int +mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct ieee80211_mcs_info mcs; + unsigned long flags; + int i; + + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr && (sta_ptr->tdls_status != TDLS_SETUP_FAILURE)) { + mwifiex_dbg(priv->adapter, MSG, + "tdls: enable link %pM success\n", peer); + + sta_ptr->tdls_status = TDLS_SETUP_COMPLETE; + + mcs = sta_ptr->tdls_cap.ht_capb.mcs; + if (mcs.rx_mask[0] != 0xff) + sta_ptr->is_11n_enabled = true; + if (sta_ptr->is_11n_enabled) { + if (le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info) & + IEEE80211_HT_CAP_MAX_AMSDU) + sta_ptr->max_amsdu = + MWIFIEX_TX_DATA_BUF_SIZE_8K; + else + sta_ptr->max_amsdu = + MWIFIEX_TX_DATA_BUF_SIZE_4K; + + for (i = 0; i < MAX_NUM_TID; i++) + sta_ptr->ampdu_sta[i] = + priv->aggr_prio_tbl[i].ampdu_user; + } else { + for (i = 0; i < MAX_NUM_TID; i++) + sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; + } + if (sta_ptr->tdls_cap.extcap.ext_capab[3] & + WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) { + mwifiex_config_tdls_enable(priv); + mwifiex_config_tdls_cs_params(priv); + } + + memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq)); + mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE); + mwifiex_auto_tdls_update_peer_status(priv, peer, + TDLS_SETUP_COMPLETE); + } else { + mwifiex_dbg(priv->adapter, ERROR, + "tdls: enable link %pM failed\n", peer); + if (sta_ptr) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_del_sta_entry(priv, peer); + } + mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); + mwifiex_auto_tdls_update_peer_status(priv, peer, + TDLS_NOT_SETUP); + + return -1; + } + + return 0; +} + +int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action) +{ + switch (action) { + case MWIFIEX_TDLS_ENABLE_LINK: + return mwifiex_tdls_process_enable_link(priv, peer); + case MWIFIEX_TDLS_DISABLE_LINK: + return mwifiex_tdls_process_disable_link(priv, peer); + case MWIFIEX_TDLS_CREATE_LINK: + return mwifiex_tdls_process_create_link(priv, peer); + case MWIFIEX_TDLS_CONFIG_LINK: + return mwifiex_tdls_process_config_link(priv, peer); + } + return 0; +} + +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *sta_ptr; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (sta_ptr) + return sta_ptr->tdls_status; + + return TDLS_NOT_SETUP; +} + +int mwifiex_get_tdls_list(struct mwifiex_private *priv, + struct tdls_peer_info *buf) +{ + struct mwifiex_sta_node *sta_ptr; + struct tdls_peer_info *peer = buf; + int count = 0; + unsigned long flags; + + if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return 0; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return 0; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + list_for_each_entry(sta_ptr, &priv->sta_list, list) { + if (mwifiex_is_tdls_link_setup(sta_ptr->tdls_status)) { + ether_addr_copy(peer->peer_addr, sta_ptr->mac_addr); + peer++; + count++; + if (count >= MWIFIEX_MAX_TDLS_PEER_SUPPORTED) + break; + } + } + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + return count; +} + +void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + unsigned long flags; + + if (list_empty(&priv->sta_list)) + return; + + list_for_each_entry(sta_ptr, &priv->sta_list, list) { + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + + if (sta_ptr->is_11n_enabled) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + } + + mwifiex_restore_tdls_packets(priv, sta_ptr->mac_addr, + TDLS_LINK_TEARDOWN); + memcpy(&tdls_oper.peer_mac, sta_ptr->mac_addr, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; + if (mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, false)) + mwifiex_dbg(priv->adapter, ERROR, + "Disable link failed for TDLS peer %pM", + sta_ptr->mac_addr); + } + + mwifiex_del_all_sta_list(priv); +} + +int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb) +{ + struct mwifiex_auto_tdls_peer *peer; + unsigned long flags; + u8 mac[ETH_ALEN]; + + ether_addr_copy(mac, skb->data); + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry(peer, &priv->auto_tdls_list, list) { + if (!memcmp(mac, peer->mac_addr, ETH_ALEN)) { + if (peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH && + peer->tdls_status == TDLS_NOT_SETUP && + (peer->failure_count < + MWIFIEX_TDLS_MAX_FAIL_COUNT)) { + peer->tdls_status = TDLS_SETUP_INPROGRESS; + mwifiex_dbg(priv->adapter, INFO, + "setup TDLS link, peer=%pM rssi=%d\n", + peer->mac_addr, peer->rssi); + + cfg80211_tdls_oper_request(priv->netdev, + peer->mac_addr, + NL80211_TDLS_SETUP, + 0, GFP_ATOMIC); + peer->do_setup = false; + priv->check_tdls_tx = false; + } else if (peer->failure_count < + MWIFIEX_TDLS_MAX_FAIL_COUNT && + peer->do_discover) { + mwifiex_send_tdls_data_frame(priv, + peer->mac_addr, + WLAN_TDLS_DISCOVERY_REQUEST, + 1, 0, NULL, 0); + peer->do_discover = false; + } + } + } + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + + return 0; +} + +void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv) +{ + struct mwifiex_auto_tdls_peer *peer, *tmp_node; + unsigned long flags; + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry_safe(peer, tmp_node, &priv->auto_tdls_list, list) { + list_del(&peer->list); + kfree(peer); + } + + INIT_LIST_HEAD(&priv->auto_tdls_list); + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + priv->check_tdls_tx = false; +} + +void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_auto_tdls_peer *tdls_peer; + unsigned long flags; + + if (!priv->adapter->auto_tdls) + return; + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { + if (!memcmp(tdls_peer->mac_addr, mac, ETH_ALEN)) { + tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS; + tdls_peer->rssi_jiffies = jiffies; + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + return; + } + } + + /* create new TDLS peer */ + tdls_peer = kzalloc(sizeof(*tdls_peer), GFP_ATOMIC); + if (tdls_peer) { + ether_addr_copy(tdls_peer->mac_addr, mac); + tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS; + tdls_peer->rssi_jiffies = jiffies; + INIT_LIST_HEAD(&tdls_peer->list); + list_add_tail(&tdls_peer->list, &priv->auto_tdls_list); + mwifiex_dbg(priv->adapter, INFO, + "Add auto TDLS peer= %pM to list\n", mac); + } + + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); +} + +void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv, + const u8 *mac, u8 link_status) +{ + struct mwifiex_auto_tdls_peer *peer; + unsigned long flags; + + if (!priv->adapter->auto_tdls) + return; + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry(peer, &priv->auto_tdls_list, list) { + if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { + if ((link_status == TDLS_NOT_SETUP) && + (peer->tdls_status == TDLS_SETUP_INPROGRESS)) + peer->failure_count++; + else if (mwifiex_is_tdls_link_setup(link_status)) + peer->failure_count = 0; + + peer->tdls_status = link_status; + break; + } + } + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); +} + +void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv, + u8 *mac, s8 snr, s8 nflr) +{ + struct mwifiex_auto_tdls_peer *peer; + unsigned long flags; + + if (!priv->adapter->auto_tdls) + return; + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry(peer, &priv->auto_tdls_list, list) { + if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { + peer->rssi = nflr - snr; + peer->rssi_jiffies = jiffies; + break; + } + } + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); +} + +void mwifiex_check_auto_tdls(unsigned long context) +{ + struct mwifiex_private *priv = (struct mwifiex_private *)context; + struct mwifiex_auto_tdls_peer *tdls_peer; + unsigned long flags; + u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; + + if (WARN_ON_ONCE(!priv || !priv->adapter)) { + pr_err("mwifiex: %s: adapter or private structure is NULL\n", + __func__); + return; + } + + if (unlikely(!priv->adapter->auto_tdls)) + return; + + if (!priv->auto_tdls_timer_active) { + mwifiex_dbg(priv->adapter, INFO, + "auto TDLS timer inactive; return"); + return; + } + + priv->check_tdls_tx = false; + + if (list_empty(&priv->auto_tdls_list)) { + mod_timer(&priv->auto_tdls_timer, + jiffies + + msecs_to_jiffies(MWIFIEX_TIMER_10S)); + return; + } + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { + if ((jiffies - tdls_peer->rssi_jiffies) > + (MWIFIEX_AUTO_TDLS_IDLE_TIME * HZ)) { + tdls_peer->rssi = 0; + tdls_peer->do_discover = true; + priv->check_tdls_tx = true; + } + + if (((tdls_peer->rssi >= MWIFIEX_TDLS_RSSI_LOW) || + !tdls_peer->rssi) && + mwifiex_is_tdls_link_setup(tdls_peer->tdls_status)) { + tdls_peer->tdls_status = TDLS_LINK_TEARDOWN; + mwifiex_dbg(priv->adapter, MSG, + "teardown TDLS link,peer=%pM rssi=%d\n", + tdls_peer->mac_addr, -tdls_peer->rssi); + tdls_peer->do_discover = true; + priv->check_tdls_tx = true; + cfg80211_tdls_oper_request(priv->netdev, + tdls_peer->mac_addr, + NL80211_TDLS_TEARDOWN, + reason, GFP_ATOMIC); + } else if (tdls_peer->rssi && + tdls_peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH && + tdls_peer->tdls_status == TDLS_NOT_SETUP && + tdls_peer->failure_count < + MWIFIEX_TDLS_MAX_FAIL_COUNT) { + priv->check_tdls_tx = true; + tdls_peer->do_setup = true; + mwifiex_dbg(priv->adapter, INFO, + "check TDLS with peer=%pM\t" + "rssi=%d\n", tdls_peer->mac_addr, + tdls_peer->rssi); + } + } + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + + mod_timer(&priv->auto_tdls_timer, + jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); +} + +void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv) +{ + setup_timer(&priv->auto_tdls_timer, mwifiex_check_auto_tdls, + (unsigned long)priv); + priv->auto_tdls_timer_active = true; + mod_timer(&priv->auto_tdls_timer, + jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); +} + +void mwifiex_clean_auto_tdls(struct mwifiex_private *priv) +{ + if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->auto_tdls && + priv->bss_type == MWIFIEX_BSS_TYPE_STA) { + priv->auto_tdls_timer_active = false; + del_timer(&priv->auto_tdls_timer); + mwifiex_flush_auto_tdls_list(priv); + } +} + +static int mwifiex_config_tdls(struct mwifiex_private *priv, u8 enable) +{ + struct mwifiex_tdls_config config; + + config.enable = cpu_to_le16(enable); + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, + ACT_TDLS_CS_ENABLE_CONFIG, 0, &config, true); +} + +int mwifiex_config_tdls_enable(struct mwifiex_private *priv) +{ + return mwifiex_config_tdls(priv, true); +} + +int mwifiex_config_tdls_disable(struct mwifiex_private *priv) +{ + return mwifiex_config_tdls(priv, false); +} + +int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv) +{ + struct mwifiex_tdls_config_cs_params config_tdls_cs_params; + + config_tdls_cs_params.unit_time = MWIFIEX_DEF_CS_UNIT_TIME; + config_tdls_cs_params.thr_otherlink = MWIFIEX_DEF_CS_THR_OTHERLINK; + config_tdls_cs_params.thr_directlink = MWIFIEX_DEF_THR_DIRECTLINK; + + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, + ACT_TDLS_CS_PARAMS, 0, + &config_tdls_cs_params, true); +} + +int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac) +{ + struct mwifiex_tdls_stop_cs_params stop_tdls_cs_params; + + ether_addr_copy(stop_tdls_cs_params.peer_mac, peer_mac); + + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, + ACT_TDLS_CS_STOP, 0, + &stop_tdls_cs_params, true); +} + +int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac, + u8 primary_chan, u8 second_chan_offset, u8 band) +{ + struct mwifiex_tdls_init_cs_params start_tdls_cs_params; + + ether_addr_copy(start_tdls_cs_params.peer_mac, peer_mac); + start_tdls_cs_params.primary_chan = primary_chan; + start_tdls_cs_params.second_chan_offset = second_chan_offset; + start_tdls_cs_params.band = band; + + start_tdls_cs_params.switch_time = cpu_to_le16(MWIFIEX_DEF_CS_TIME); + start_tdls_cs_params.switch_timeout = + cpu_to_le16(MWIFIEX_DEF_CS_TIMEOUT); + start_tdls_cs_params.reg_class = MWIFIEX_DEF_CS_REG_CLASS; + start_tdls_cs_params.periodicity = MWIFIEX_DEF_CS_PERIODICITY; + + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, + ACT_TDLS_CS_INIT, 0, + &start_tdls_cs_params, true); +} diff --git a/drivers/net/wireless/marvell/mwifiex/txrx.c b/drivers/net/wireless/marvell/mwifiex/txrx.c new file mode 100644 index 000000000000..bf6182b646a5 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/txrx.c @@ -0,0 +1,386 @@ +/* + * Marvell Wireless LAN device driver: generic TX/RX data handling + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" + +/* + * This function processes the received buffer. + * + * Main responsibility of this function is to parse the RxPD to + * identify the correct interface this packet is headed for and + * forwarding it to the associated handling function, where the + * packet will be further processed and sent to kernel/upper layer + * if required. + */ +int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + struct rxpd *local_rx_pd; + struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); + int ret; + + local_rx_pd = (struct rxpd *) (skb->data); + /* Get the BSS number from rxpd, get corresponding priv */ + priv = mwifiex_get_priv_by_id(adapter, local_rx_pd->bss_num & + BSS_NUM_MASK, local_rx_pd->bss_type); + if (!priv) + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + if (!priv) { + mwifiex_dbg(adapter, ERROR, + "data: priv not found. Drop RX packet\n"); + dev_kfree_skb_any(skb); + return -1; + } + + mwifiex_dbg_dump(adapter, DAT_D, "rx pkt:", skb->data, + min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN)); + + memset(rx_info, 0, sizeof(*rx_info)); + rx_info->bss_num = priv->bss_num; + rx_info->bss_type = priv->bss_type; + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) + ret = mwifiex_process_uap_rx_packet(priv, skb); + else + ret = mwifiex_process_sta_rx_packet(priv, skb); + + return ret; +} +EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet); + +/* + * This function sends a packet to device. + * + * It processes the packet to add the TxPD, checks condition and + * sends the processed packet to firmware for transmission. + * + * On successful completion, the function calls the completion callback + * and logs the time. + */ +int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + int hroom, ret = -1; + struct mwifiex_adapter *adapter = priv->adapter; + u8 *head_ptr; + struct txpd *local_tx_pd = NULL; + struct mwifiex_sta_node *dest_node; + struct ethhdr *hdr = (void *)skb->data; + + hroom = (adapter->iface_type == MWIFIEX_USB) ? 0 : INTF_HEADER_LEN; + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { + dest_node = mwifiex_get_sta_entry(priv, hdr->h_dest); + if (dest_node) { + dest_node->stats.tx_bytes += skb->len; + dest_node->stats.tx_packets++; + } + + head_ptr = mwifiex_process_uap_txpd(priv, skb); + } else { + head_ptr = mwifiex_process_sta_txpd(priv, skb); + } + + if ((adapter->data_sent || adapter->tx_lock_flag) && head_ptr) { + skb_queue_tail(&adapter->tx_data_q, skb); + atomic_inc(&adapter->tx_queued); + return 0; + } + + if (head_ptr) { + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) + local_tx_pd = (struct txpd *)(head_ptr + hroom); + if (adapter->iface_type == MWIFIEX_USB) { + ret = adapter->if_ops.host_to_card(adapter, + priv->usb_port, + skb, NULL); + } else { + ret = adapter->if_ops.host_to_card(adapter, + MWIFIEX_TYPE_DATA, + skb, tx_param); + } + } + mwifiex_dbg_dump(adapter, DAT_D, "tx pkt:", skb->data, + min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN)); + + switch (ret) { + case -ENOSR: + mwifiex_dbg(adapter, DATA, "data: -ENOSR is returned\n"); + break; + case -EBUSY: + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + (adapter->pps_uapsd_mode) && (adapter->tx_lock_flag)) { + priv->adapter->tx_lock_flag = false; + if (local_tx_pd) + local_tx_pd->flags = 0; + } + mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + break; + case -1: + mwifiex_dbg(adapter, ERROR, + "mwifiex_write_data_async failed: 0x%X\n", + ret); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + case -EINPROGRESS: + break; + case 0: + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + default: + break; + } + + return ret; +} + +static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, + struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + struct txpd *local_tx_pd = NULL; + u8 *head_ptr = skb->data; + int ret = 0; + struct mwifiex_private *priv; + struct mwifiex_txinfo *tx_info; + + tx_info = MWIFIEX_SKB_TXCB(skb); + priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num, + tx_info->bss_type); + if (!priv) { + mwifiex_dbg(adapter, ERROR, + "data: priv not found. Drop TX packet\n"); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb, 0, 0); + return ret; + } + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { + if (adapter->iface_type == MWIFIEX_USB) + local_tx_pd = (struct txpd *)head_ptr; + else + local_tx_pd = (struct txpd *) (head_ptr + + INTF_HEADER_LEN); + } + + if (adapter->iface_type == MWIFIEX_USB) { + ret = adapter->if_ops.host_to_card(adapter, + priv->usb_port, + skb, NULL); + } else { + ret = adapter->if_ops.host_to_card(adapter, + MWIFIEX_TYPE_DATA, + skb, tx_param); + } + switch (ret) { + case -ENOSR: + mwifiex_dbg(adapter, ERROR, "data: -ENOSR is returned\n"); + break; + case -EBUSY: + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + (adapter->pps_uapsd_mode) && + (adapter->tx_lock_flag)) { + priv->adapter->tx_lock_flag = false; + if (local_tx_pd) + local_tx_pd->flags = 0; + } + skb_queue_head(&adapter->tx_data_q, skb); + if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) + atomic_add(tx_info->aggr_num, &adapter->tx_queued); + else + atomic_inc(&adapter->tx_queued); + mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + break; + case -1: + mwifiex_dbg(adapter, ERROR, + "mwifiex_write_data_async failed: 0x%X\n", ret); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + case -EINPROGRESS: + break; + case 0: + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + default: + break; + } + return ret; +} + +static int +mwifiex_dequeue_tx_queue(struct mwifiex_adapter *adapter) +{ + struct sk_buff *skb, *skb_next; + struct mwifiex_txinfo *tx_info; + struct mwifiex_tx_param tx_param; + + skb = skb_dequeue(&adapter->tx_data_q); + if (!skb) + return -1; + + tx_info = MWIFIEX_SKB_TXCB(skb); + if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) + atomic_sub(tx_info->aggr_num, &adapter->tx_queued); + else + atomic_dec(&adapter->tx_queued); + + if (!skb_queue_empty(&adapter->tx_data_q)) + skb_next = skb_peek(&adapter->tx_data_q); + else + skb_next = NULL; + tx_param.next_pkt_len = ((skb_next) ? skb_next->len : 0); + if (!tx_param.next_pkt_len) { + if (!mwifiex_wmm_lists_empty(adapter)) + tx_param.next_pkt_len = 1; + } + return mwifiex_host_to_card(adapter, skb, &tx_param); +} + +void +mwifiex_process_tx_queue(struct mwifiex_adapter *adapter) +{ + do { + if (adapter->data_sent || adapter->tx_lock_flag) + break; + if (mwifiex_dequeue_tx_queue(adapter)) + break; + } while (!skb_queue_empty(&adapter->tx_data_q)); +} + +/* + * Packet send completion callback handler. + * + * It either frees the buffer directly or forwards it to another + * completion callback which checks conditions, updates statistics, + * wakes up stalled traffic queue if required, and then frees the buffer. + */ +int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb, int aggr, int status) +{ + struct mwifiex_private *priv; + struct mwifiex_txinfo *tx_info; + struct netdev_queue *txq; + int index; + + if (!skb) + return 0; + + tx_info = MWIFIEX_SKB_TXCB(skb); + priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num, + tx_info->bss_type); + if (!priv) + goto done; + + mwifiex_set_trans_start(priv->netdev); + if (!status) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += tx_info->pkt_len; + if (priv->tx_timeout_cnt) + priv->tx_timeout_cnt = 0; + } else { + priv->stats.tx_errors++; + } + + if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) + atomic_dec_return(&adapter->pending_bridged_pkts); + + if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) + goto done; + + if (aggr) + /* For skb_aggr, do not wake up tx queue */ + goto done; + + atomic_dec(&adapter->tx_pending); + + index = mwifiex_1d_to_wmm_queue[skb->priority]; + if (atomic_dec_return(&priv->wmm_tx_pending[index]) < LOW_TX_PENDING) { + txq = netdev_get_tx_queue(priv->netdev, index); + if (netif_tx_queue_stopped(txq)) { + netif_tx_wake_queue(txq); + mwifiex_dbg(adapter, DATA, "wake queue: %d\n", index); + } + } +done: + dev_kfree_skb_any(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(mwifiex_write_data_complete); + +void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, + void *event_body) +{ + struct tx_status_event *tx_status = (void *)priv->adapter->event_body; + struct sk_buff *ack_skb; + unsigned long flags; + struct mwifiex_txinfo *tx_info; + + if (!tx_status->tx_token_id) + return; + + spin_lock_irqsave(&priv->ack_status_lock, flags); + ack_skb = idr_find(&priv->ack_status_frames, tx_status->tx_token_id); + if (ack_skb) + idr_remove(&priv->ack_status_frames, tx_status->tx_token_id); + spin_unlock_irqrestore(&priv->ack_status_lock, flags); + + if (ack_skb) { + tx_info = MWIFIEX_SKB_TXCB(ack_skb); + + if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS) { + /* consumes ack_skb */ + skb_complete_wifi_ack(ack_skb, !tx_status->status); + } else { + /* Remove broadcast address which was added by driver */ + memmove(ack_skb->data + + sizeof(struct ieee80211_hdr_3addr) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16), + ack_skb->data + + sizeof(struct ieee80211_hdr_3addr) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) + + ETH_ALEN, ack_skb->len - + (sizeof(struct ieee80211_hdr_3addr) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) + + ETH_ALEN)); + ack_skb->len = ack_skb->len - ETH_ALEN; + /* Remove driver's proprietary header including 2 bytes + * of packet length and pass actual management frame buffer + * to cfg80211. + */ + cfg80211_mgmt_tx_status(&priv->wdev, tx_info->cookie, + ack_skb->data + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + sizeof(u16), ack_skb->len - + (MWIFIEX_MGMT_FRAME_HEADER_SIZE + + sizeof(u16)), + !tx_status->status, GFP_ATOMIC); + dev_kfree_skb_any(ack_skb); + } + } +} diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c new file mode 100644 index 000000000000..759a6ada5b0f --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c @@ -0,0 +1,885 @@ +/* + * Marvell Wireless LAN device driver: AP specific command handling + * + * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" +#include "11ac.h" + +/* This function parses security related parameters from cfg80211_ap_settings + * and sets into FW understandable bss_config structure. + */ +int mwifiex_set_secure_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_config, + struct cfg80211_ap_settings *params) { + int i; + struct mwifiex_wep_key wep_key; + + if (!params->privacy) { + bss_config->protocol = PROTOCOL_NO_SECURITY; + bss_config->key_mgmt = KEY_MGMT_NONE; + bss_config->wpa_cfg.length = 0; + priv->sec_info.wep_enabled = 0; + priv->sec_info.wpa_enabled = 0; + priv->sec_info.wpa2_enabled = 0; + + return 0; + } + + switch (params->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + bss_config->auth_mode = WLAN_AUTH_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + bss_config->auth_mode = WLAN_AUTH_SHARED_KEY; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + bss_config->auth_mode = WLAN_AUTH_LEAP; + break; + default: + bss_config->auth_mode = MWIFIEX_AUTH_MODE_AUTO; + break; + } + + bss_config->key_mgmt_operation |= KEY_MGMT_ON_HOST; + + for (i = 0; i < params->crypto.n_akm_suites; i++) { + switch (params->crypto.akm_suites[i]) { + case WLAN_AKM_SUITE_8021X: + if (params->crypto.wpa_versions & + NL80211_WPA_VERSION_1) { + bss_config->protocol = PROTOCOL_WPA; + bss_config->key_mgmt = KEY_MGMT_EAP; + } + if (params->crypto.wpa_versions & + NL80211_WPA_VERSION_2) { + bss_config->protocol |= PROTOCOL_WPA2; + bss_config->key_mgmt = KEY_MGMT_EAP; + } + break; + case WLAN_AKM_SUITE_PSK: + if (params->crypto.wpa_versions & + NL80211_WPA_VERSION_1) { + bss_config->protocol = PROTOCOL_WPA; + bss_config->key_mgmt = KEY_MGMT_PSK; + } + if (params->crypto.wpa_versions & + NL80211_WPA_VERSION_2) { + bss_config->protocol |= PROTOCOL_WPA2; + bss_config->key_mgmt = KEY_MGMT_PSK; + } + break; + default: + break; + } + } + for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) { + switch (params->crypto.ciphers_pairwise[i]) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + break; + case WLAN_CIPHER_SUITE_TKIP: + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + bss_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_TKIP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + bss_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_AES_CCMP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_AES_CCMP; + default: + break; + } + } + + switch (params->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (priv->sec_info.wep_enabled) { + bss_config->protocol = PROTOCOL_STATIC_WEP; + bss_config->key_mgmt = KEY_MGMT_NONE; + bss_config->wpa_cfg.length = 0; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + wep_key = priv->wep_key[i]; + bss_config->wep_cfg[i].key_index = i; + + if (priv->wep_key_curr_index == i) + bss_config->wep_cfg[i].is_default = 1; + else + bss_config->wep_cfg[i].is_default = 0; + + bss_config->wep_cfg[i].length = + wep_key.key_length; + memcpy(&bss_config->wep_cfg[i].key, + &wep_key.key_material, + wep_key.key_length); + } + } + break; + case WLAN_CIPHER_SUITE_TKIP: + bss_config->wpa_cfg.group_cipher = CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + bss_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + break; + default: + break; + } + + return 0; +} + +/* This function updates 11n related parameters from IE and sets them into + * bss_config structure. + */ +void +mwifiex_set_ht_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *ht_ie; + u16 cap_info; + + if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) + return; + + ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail, + params->beacon.tail_len); + if (ht_ie) { + memcpy(&bss_cfg->ht_cap, ht_ie + 2, + sizeof(struct ieee80211_ht_cap)); + cap_info = le16_to_cpu(bss_cfg->ht_cap.cap_info); + memset(&bss_cfg->ht_cap.mcs, 0, + priv->adapter->number_of_antenna); + switch (GET_RXSTBC(cap_info)) { + case MWIFIEX_RX_STBC1: + /* HT_CAP 1X1 mode */ + bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; + break; + case MWIFIEX_RX_STBC12: /* fall through */ + case MWIFIEX_RX_STBC123: + /* HT_CAP 2X2 mode */ + bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; + bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff; + break; + default: + mwifiex_dbg(priv->adapter, WARN, + "Unsupported RX-STBC, default to 2x2\n"); + bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; + bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff; + break; + } + priv->ap_11n_enabled = 1; + } else { + memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap)); + bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP); + bss_cfg->ht_cap.ampdu_params_info = MWIFIEX_DEF_AMPDU; + } + + return; +} + +/* This function updates 11ac related parameters from IE + * and sets them into bss_config structure. + */ +void mwifiex_set_vht_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *vht_ie; + + vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail, + params->beacon.tail_len); + if (vht_ie) { + memcpy(&bss_cfg->vht_cap, vht_ie + 2, + sizeof(struct ieee80211_vht_cap)); + priv->ap_11ac_enabled = 1; + } else { + priv->ap_11ac_enabled = 0; + } + + return; +} + +/* This function updates 11ac related parameters from IE + * and sets them into bss_config structure. + */ +void mwifiex_set_tpc_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *tpc_ie; + + tpc_ie = cfg80211_find_ie(WLAN_EID_TPC_REQUEST, params->beacon.tail, + params->beacon.tail_len); + if (tpc_ie) + bss_cfg->power_constraint = *(tpc_ie + 2); + else + bss_cfg->power_constraint = 0; +} + +/* Enable VHT only when cfg80211_ap_settings has VHT IE. + * Otherwise disable VHT. + */ +void mwifiex_set_vht_width(struct mwifiex_private *priv, + enum nl80211_chan_width width, + bool ap_11ac_enable) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_11ac_vht_cfg vht_cfg; + + vht_cfg.band_config = VHT_CFG_5GHZ; + vht_cfg.cap_info = adapter->hw_dot_11ac_dev_cap; + + if (!ap_11ac_enable) { + vht_cfg.mcs_tx_set = DISABLE_VHT_MCS_SET; + vht_cfg.mcs_rx_set = DISABLE_VHT_MCS_SET; + } else { + vht_cfg.mcs_tx_set = DEFAULT_VHT_MCS_SET; + vht_cfg.mcs_rx_set = DEFAULT_VHT_MCS_SET; + } + + vht_cfg.misc_config = VHT_CAP_UAP_ONLY; + + if (ap_11ac_enable && width >= NL80211_CHAN_WIDTH_80) + vht_cfg.misc_config |= VHT_BW_80_160_80P80; + + mwifiex_send_cmd(priv, HostCmd_CMD_11AC_CFG, + HostCmd_ACT_GEN_SET, 0, &vht_cfg, true); + + return; +} + +/* This function finds supported rates IE from beacon parameter and sets + * these rates into bss_config structure. + */ +void +mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + struct ieee_types_header *rate_ie; + int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + const u8 *var_pos = params->beacon.head + var_offset; + int len = params->beacon.head_len - var_offset; + u8 rate_len = 0; + + rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len); + if (rate_ie) { + memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len); + rate_len = rate_ie->len; + } + + rate_ie = (void *)cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, + params->beacon.tail, + params->beacon.tail_len); + if (rate_ie) + memcpy(bss_cfg->rates + rate_len, rate_ie + 1, rate_ie->len); + + return; +} + +/* This function initializes some of mwifiex_uap_bss_param variables. + * This helps FW in ignoring invalid values. These values may or may not + * be get updated to valid ones at later stage. + */ +void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config) +{ + config->bcast_ssid_ctl = 0x7F; + config->radio_ctl = 0x7F; + config->dtim_period = 0x7F; + config->beacon_period = 0x7FFF; + config->auth_mode = 0x7F; + config->rts_threshold = 0x7FFF; + config->frag_threshold = 0x7FFF; + config->retry_limit = 0x7F; + config->qos_info = 0xFF; +} + +/* This function parses BSS related parameters from structure + * and prepares TLVs specific to WPA/WPA2 security. + * These TLVs are appended to command buffer. + */ +static void +mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) +{ + struct host_cmd_tlv_pwk_cipher *pwk_cipher; + struct host_cmd_tlv_gwk_cipher *gwk_cipher; + struct host_cmd_tlv_passphrase *passphrase; + struct host_cmd_tlv_akmp *tlv_akmp; + struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; + u16 cmd_size = *param_size; + u8 *tlv = *tlv_buf; + + tlv_akmp = (struct host_cmd_tlv_akmp *)tlv; + tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP); + tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) - + sizeof(struct mwifiex_ie_types_header)); + tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation); + tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt); + cmd_size += sizeof(struct host_cmd_tlv_akmp); + tlv += sizeof(struct host_cmd_tlv_akmp); + + if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) { + pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; + pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - + sizeof(struct mwifiex_ie_types_header)); + pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA); + pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa; + cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); + tlv += sizeof(struct host_cmd_tlv_pwk_cipher); + } + + if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) { + pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; + pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - + sizeof(struct mwifiex_ie_types_header)); + pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2); + pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2; + cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); + tlv += sizeof(struct host_cmd_tlv_pwk_cipher); + } + + if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) { + gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv; + gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER); + gwk_cipher->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) - + sizeof(struct mwifiex_ie_types_header)); + gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher; + cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher); + tlv += sizeof(struct host_cmd_tlv_gwk_cipher); + } + + if (bss_cfg->wpa_cfg.length) { + passphrase = (struct host_cmd_tlv_passphrase *)tlv; + passphrase->header.type = + cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE); + passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length); + memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase, + bss_cfg->wpa_cfg.length); + cmd_size += sizeof(struct mwifiex_ie_types_header) + + bss_cfg->wpa_cfg.length; + tlv += sizeof(struct mwifiex_ie_types_header) + + bss_cfg->wpa_cfg.length; + } + + *param_size = cmd_size; + *tlv_buf = tlv; + + return; +} + +/* This function parses WMM related parameters from cfg80211_ap_settings + * structure and updates bss_config structure. + */ +void +mwifiex_set_wmm_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *vendor_ie; + struct ieee_types_header *wmm_ie; + u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02}; + + vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WMM, + params->beacon.tail, + params->beacon.tail_len); + if (vendor_ie) { + wmm_ie = (struct ieee_types_header *)vendor_ie; + memcpy(&bss_cfg->wmm_info, wmm_ie + 1, + sizeof(bss_cfg->wmm_info)); + priv->wmm_enabled = 1; + } else { + memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info)); + memcpy(&bss_cfg->wmm_info.oui, wmm_oui, sizeof(wmm_oui)); + bss_cfg->wmm_info.subtype = MWIFIEX_WMM_SUBTYPE; + bss_cfg->wmm_info.version = MWIFIEX_WMM_VERSION; + priv->wmm_enabled = 0; + } + + bss_cfg->qos_info = 0x00; + return; +} +/* This function parses BSS related parameters from structure + * and prepares TLVs specific to WEP encryption. + * These TLVs are appended to command buffer. + */ +static void +mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size) +{ + struct host_cmd_tlv_wep_key *wep_key; + u16 cmd_size = *param_size; + int i; + u8 *tlv = *tlv_buf; + struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (bss_cfg->wep_cfg[i].length && + (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 || + bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) { + wep_key = (struct host_cmd_tlv_wep_key *)tlv; + wep_key->header.type = + cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); + wep_key->header.len = + cpu_to_le16(bss_cfg->wep_cfg[i].length + 2); + wep_key->key_index = bss_cfg->wep_cfg[i].key_index; + wep_key->is_default = bss_cfg->wep_cfg[i].is_default; + memcpy(wep_key->key, bss_cfg->wep_cfg[i].key, + bss_cfg->wep_cfg[i].length); + cmd_size += sizeof(struct mwifiex_ie_types_header) + 2 + + bss_cfg->wep_cfg[i].length; + tlv += sizeof(struct mwifiex_ie_types_header) + 2 + + bss_cfg->wep_cfg[i].length; + } + } + + *param_size = cmd_size; + *tlv_buf = tlv; + + return; +} + +/* This function parses BSS related parameters from structure + * and prepares TLVs. These TLVs are appended to command buffer. +*/ +static int +mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) +{ + struct host_cmd_tlv_dtim_period *dtim_period; + struct host_cmd_tlv_beacon_period *beacon_period; + struct host_cmd_tlv_ssid *ssid; + struct host_cmd_tlv_bcast_ssid *bcast_ssid; + struct host_cmd_tlv_channel_band *chan_band; + struct host_cmd_tlv_frag_threshold *frag_threshold; + struct host_cmd_tlv_rts_threshold *rts_threshold; + struct host_cmd_tlv_retry_limit *retry_limit; + struct host_cmd_tlv_encrypt_protocol *encrypt_protocol; + struct host_cmd_tlv_auth_type *auth_type; + struct host_cmd_tlv_rates *tlv_rates; + struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer; + struct host_cmd_tlv_power_constraint *pwr_ct; + struct mwifiex_ie_types_htcap *htcap; + struct mwifiex_ie_types_wmmcap *wmm_cap; + struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; + int i; + u16 cmd_size = *param_size; + + if (bss_cfg->ssid.ssid_len) { + ssid = (struct host_cmd_tlv_ssid *)tlv; + ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID); + ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len); + memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len); + cmd_size += sizeof(struct mwifiex_ie_types_header) + + bss_cfg->ssid.ssid_len; + tlv += sizeof(struct mwifiex_ie_types_header) + + bss_cfg->ssid.ssid_len; + + bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv; + bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID); + bcast_ssid->header.len = + cpu_to_le16(sizeof(bcast_ssid->bcast_ctl)); + bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl; + cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid); + tlv += sizeof(struct host_cmd_tlv_bcast_ssid); + } + if (bss_cfg->rates[0]) { + tlv_rates = (struct host_cmd_tlv_rates *)tlv; + tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES); + + for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i]; + i++) + tlv_rates->rates[i] = bss_cfg->rates[i]; + + tlv_rates->header.len = cpu_to_le16(i); + cmd_size += sizeof(struct host_cmd_tlv_rates) + i; + tlv += sizeof(struct host_cmd_tlv_rates) + i; + } + if (bss_cfg->channel && + ((bss_cfg->band_cfg == BAND_CONFIG_BG && + bss_cfg->channel <= MAX_CHANNEL_BAND_BG) || + (bss_cfg->band_cfg == BAND_CONFIG_A && + bss_cfg->channel <= MAX_CHANNEL_BAND_A))) { + chan_band = (struct host_cmd_tlv_channel_band *)tlv; + chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); + chan_band->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) - + sizeof(struct mwifiex_ie_types_header)); + chan_band->band_config = bss_cfg->band_cfg; + chan_band->channel = bss_cfg->channel; + cmd_size += sizeof(struct host_cmd_tlv_channel_band); + tlv += sizeof(struct host_cmd_tlv_channel_band); + } + if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD && + bss_cfg->beacon_period <= MAX_BEACON_PERIOD) { + beacon_period = (struct host_cmd_tlv_beacon_period *)tlv; + beacon_period->header.type = + cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD); + beacon_period->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) - + sizeof(struct mwifiex_ie_types_header)); + beacon_period->period = cpu_to_le16(bss_cfg->beacon_period); + cmd_size += sizeof(struct host_cmd_tlv_beacon_period); + tlv += sizeof(struct host_cmd_tlv_beacon_period); + } + if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD && + bss_cfg->dtim_period <= MAX_DTIM_PERIOD) { + dtim_period = (struct host_cmd_tlv_dtim_period *)tlv; + dtim_period->header.type = + cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD); + dtim_period->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) - + sizeof(struct mwifiex_ie_types_header)); + dtim_period->period = bss_cfg->dtim_period; + cmd_size += sizeof(struct host_cmd_tlv_dtim_period); + tlv += sizeof(struct host_cmd_tlv_dtim_period); + } + if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) { + rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv; + rts_threshold->header.type = + cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD); + rts_threshold->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) - + sizeof(struct mwifiex_ie_types_header)); + rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold); + cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); + tlv += sizeof(struct host_cmd_tlv_frag_threshold); + } + if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) && + (bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) { + frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv; + frag_threshold->header.type = + cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD); + frag_threshold->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) - + sizeof(struct mwifiex_ie_types_header)); + frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold); + cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); + tlv += sizeof(struct host_cmd_tlv_frag_threshold); + } + if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) { + retry_limit = (struct host_cmd_tlv_retry_limit *)tlv; + retry_limit->header.type = + cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT); + retry_limit->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) - + sizeof(struct mwifiex_ie_types_header)); + retry_limit->limit = (u8)bss_cfg->retry_limit; + cmd_size += sizeof(struct host_cmd_tlv_retry_limit); + tlv += sizeof(struct host_cmd_tlv_retry_limit); + } + if ((bss_cfg->protocol & PROTOCOL_WPA) || + (bss_cfg->protocol & PROTOCOL_WPA2) || + (bss_cfg->protocol & PROTOCOL_EAP)) + mwifiex_uap_bss_wpa(&tlv, cmd_buf, &cmd_size); + else + mwifiex_uap_bss_wep(&tlv, cmd_buf, &cmd_size); + + if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) || + (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) { + auth_type = (struct host_cmd_tlv_auth_type *)tlv; + auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); + auth_type->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) - + sizeof(struct mwifiex_ie_types_header)); + auth_type->auth_type = (u8)bss_cfg->auth_mode; + cmd_size += sizeof(struct host_cmd_tlv_auth_type); + tlv += sizeof(struct host_cmd_tlv_auth_type); + } + if (bss_cfg->protocol) { + encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv; + encrypt_protocol->header.type = + cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL); + encrypt_protocol->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol) + - sizeof(struct mwifiex_ie_types_header)); + encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol); + cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol); + tlv += sizeof(struct host_cmd_tlv_encrypt_protocol); + } + + if (bss_cfg->ht_cap.cap_info) { + htcap = (struct mwifiex_ie_types_htcap *)tlv; + htcap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + htcap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + htcap->ht_cap.cap_info = bss_cfg->ht_cap.cap_info; + htcap->ht_cap.ampdu_params_info = + bss_cfg->ht_cap.ampdu_params_info; + memcpy(&htcap->ht_cap.mcs, &bss_cfg->ht_cap.mcs, + sizeof(struct ieee80211_mcs_info)); + htcap->ht_cap.extended_ht_cap_info = + bss_cfg->ht_cap.extended_ht_cap_info; + htcap->ht_cap.tx_BF_cap_info = bss_cfg->ht_cap.tx_BF_cap_info; + htcap->ht_cap.antenna_selection_info = + bss_cfg->ht_cap.antenna_selection_info; + cmd_size += sizeof(struct mwifiex_ie_types_htcap); + tlv += sizeof(struct mwifiex_ie_types_htcap); + } + + if (bss_cfg->wmm_info.qos_info != 0xFF) { + wmm_cap = (struct mwifiex_ie_types_wmmcap *)tlv; + wmm_cap->header.type = cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC); + wmm_cap->header.len = cpu_to_le16(sizeof(wmm_cap->wmm_info)); + memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info, + sizeof(wmm_cap->wmm_info)); + cmd_size += sizeof(struct mwifiex_ie_types_wmmcap); + tlv += sizeof(struct mwifiex_ie_types_wmmcap); + } + + if (bss_cfg->sta_ao_timer) { + ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; + ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER); + ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) - + sizeof(struct mwifiex_ie_types_header)); + ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer); + cmd_size += sizeof(*ao_timer); + tlv += sizeof(*ao_timer); + } + + if (bss_cfg->power_constraint) { + pwr_ct = (void *)tlv; + pwr_ct->header.type = cpu_to_le16(TLV_TYPE_PWR_CONSTRAINT); + pwr_ct->header.len = cpu_to_le16(sizeof(u8)); + pwr_ct->constraint = bss_cfg->power_constraint; + cmd_size += sizeof(*pwr_ct); + tlv += sizeof(*pwr_ct); + } + + if (bss_cfg->ps_sta_ao_timer) { + ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; + ps_ao_timer->header.type = + cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER); + ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) - + sizeof(struct mwifiex_ie_types_header)); + ps_ao_timer->sta_ao_timer = + cpu_to_le32(bss_cfg->ps_sta_ao_timer); + cmd_size += sizeof(*ps_ao_timer); + tlv += sizeof(*ps_ao_timer); + } + + *param_size = cmd_size; + + return 0; +} + +/* This function parses custom IEs from IE list and prepares command buffer */ +static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size) +{ + struct mwifiex_ie_list *ap_ie = cmd_buf; + struct mwifiex_ie_types_header *tlv_ie = (void *)tlv; + + if (!ap_ie || !ap_ie->len || !ap_ie->ie_list) + return -1; + + *ie_size += le16_to_cpu(ap_ie->len) + + sizeof(struct mwifiex_ie_types_header); + + tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); + tlv_ie->len = ap_ie->len; + tlv += sizeof(struct mwifiex_ie_types_header); + + memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len)); + + return 0; +} + +/* Parse AP config structure and prepare TLV based command structure + * to be sent to FW for uAP configuration + */ +static int +mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action, + u32 type, void *cmd_buf) +{ + u8 *tlv; + u16 cmd_size, param_size, ie_size; + struct host_cmd_ds_sys_config *sys_cfg; + + cmd->command = cpu_to_le16(HostCmd_CMD_UAP_SYS_CONFIG); + cmd_size = (u16)(sizeof(struct host_cmd_ds_sys_config) + S_DS_GEN); + sys_cfg = (struct host_cmd_ds_sys_config *)&cmd->params.uap_sys_config; + sys_cfg->action = cpu_to_le16(cmd_action); + tlv = sys_cfg->tlv; + + switch (type) { + case UAP_BSS_PARAMS_I: + param_size = cmd_size; + if (mwifiex_uap_bss_param_prepare(tlv, cmd_buf, ¶m_size)) + return -1; + cmd->size = cpu_to_le16(param_size); + break; + case UAP_CUSTOM_IE_I: + ie_size = cmd_size; + if (mwifiex_uap_custom_ie_prepare(tlv, cmd_buf, &ie_size)) + return -1; + cmd->size = cpu_to_le16(ie_size); + break; + default: + return -1; + } + + return 0; +} + +/* This function prepares AP specific deauth command with mac supplied in + * function parameter. + */ +static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u8 *mac) +{ + struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth; + + cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH); + memcpy(sta_deauth->mac, mac, ETH_ALEN); + sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) + + S_DS_GEN); + return 0; +} + +/* This function prepares the AP specific commands before sending them + * to the firmware. + * This is a generic function which calls specific command preparation + * routines based upon the command number. + */ +int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, + u16 cmd_action, u32 type, + void *data_buf, void *cmd_buf) +{ + struct host_cmd_ds_command *cmd = cmd_buf; + + switch (cmd_no) { + case HostCmd_CMD_UAP_SYS_CONFIG: + if (mwifiex_cmd_uap_sys_config(cmd, cmd_action, type, data_buf)) + return -1; + break; + case HostCmd_CMD_UAP_BSS_START: + case HostCmd_CMD_UAP_BSS_STOP: + case HOST_CMD_APCMD_SYS_RESET: + case HOST_CMD_APCMD_STA_LIST: + cmd->command = cpu_to_le16(cmd_no); + cmd->size = cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_UAP_STA_DEAUTH: + if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf)) + return -1; + break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + if (mwifiex_cmd_issue_chan_report_request(priv, cmd_buf, + data_buf)) + return -1; + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "PREP_CMD: unknown cmd %#x\n", cmd_no); + return -1; + } + + return 0; +} + +void mwifiex_uap_set_channel(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_chan_def chandef) +{ + u8 config_bands = 0, old_bands = priv->adapter->config_bands; + + priv->bss_chandef = chandef; + + bss_cfg->channel = ieee80211_frequency_to_channel( + chandef.chan->center_freq); + + /* Set appropriate bands */ + if (chandef.chan->band == IEEE80211_BAND_2GHZ) { + bss_cfg->band_cfg = BAND_CONFIG_BG; + config_bands = BAND_B | BAND_G; + + if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |= BAND_GN; + } else { + bss_cfg->band_cfg = BAND_CONFIG_A; + config_bands = BAND_A; + + if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |= BAND_AN; + + if (chandef.width > NL80211_CHAN_WIDTH_40) + config_bands |= BAND_AAC; + } + + priv->adapter->config_bands = config_bands; + + if (old_bands != config_bands) { + mwifiex_send_domain_info_cmd_fw(priv->adapter->wiphy); + mwifiex_dnld_txpwr_table(priv); + } +} + +int mwifiex_config_start_uap(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg) +{ + enum state_11d_t state_11d; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, false)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to set the SSID\n"); + return -1; + } + + /* Send cmd to FW to enable 11D function */ + state_11d = ENABLE_11D; + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, DOT11D_I, + &state_11d, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "11D: failed to enable 11D\n"); + return -1; + } + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to start the BSS\n"); + return -1; + } + + if (priv->sec_info.wep_enabled) + priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; + else + priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true)) + return -1; + + return 0; +} diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c new file mode 100644 index 000000000000..86ff54296f39 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c @@ -0,0 +1,333 @@ +/* + * Marvell Wireless LAN device driver: AP event handling + * + * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "main.h" +#include "11n.h" + +#define MWIFIEX_BSS_START_EVT_FIX_SIZE 12 + +static int mwifiex_check_uap_capabilties(struct mwifiex_private *priv, + struct sk_buff *event) +{ + int evt_len; + u8 *curr; + u16 tlv_len; + struct mwifiex_ie_types_data *tlv_hdr; + struct ieee_types_wmm_parameter *wmm_param_ie = NULL; + int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; + + priv->wmm_enabled = false; + skb_pull(event, MWIFIEX_BSS_START_EVT_FIX_SIZE); + evt_len = event->len; + curr = event->data; + + mwifiex_dbg_dump(priv->adapter, EVT_D, "uap capabilties:", + event->data, event->len); + + skb_push(event, MWIFIEX_BSS_START_EVT_FIX_SIZE); + + while ((evt_len >= sizeof(tlv_hdr->header))) { + tlv_hdr = (struct mwifiex_ie_types_data *)curr; + tlv_len = le16_to_cpu(tlv_hdr->header.len); + + if (evt_len < tlv_len + sizeof(tlv_hdr->header)) + break; + + switch (le16_to_cpu(tlv_hdr->header.type)) { + case WLAN_EID_HT_CAPABILITY: + priv->ap_11n_enabled = true; + break; + + case WLAN_EID_VHT_CAPABILITY: + priv->ap_11ac_enabled = true; + break; + + case WLAN_EID_VENDOR_SPECIFIC: + /* Point the regular IEEE IE 2 bytes into the Marvell IE + * and setup the IEEE IE type and length byte fields + */ + wmm_param_ie = (void *)(curr + 2); + wmm_param_ie->vend_hdr.len = (u8)tlv_len; + wmm_param_ie->vend_hdr.element_id = + WLAN_EID_VENDOR_SPECIFIC; + mwifiex_dbg(priv->adapter, EVENT, + "info: check uap capabilities:\t" + "wmm parameter set count: %d\n", + wmm_param_ie->qos_info_bitmap & mask); + + mwifiex_wmm_setup_ac_downgrade(priv); + priv->wmm_enabled = true; + mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); + break; + + default: + break; + } + + curr += (tlv_len + sizeof(tlv_hdr->header)); + evt_len -= (tlv_len + sizeof(tlv_hdr->header)); + } + + return 0; +} + +/* + * This function handles AP interface specific events generated by firmware. + * + * Event specific routines are called by this function based + * upon the generated event cause. + * + * + * Events supported for AP - + * - EVENT_UAP_STA_ASSOC + * - EVENT_UAP_STA_DEAUTH + * - EVENT_UAP_BSS_ACTIVE + * - EVENT_UAP_BSS_START + * - EVENT_UAP_BSS_IDLE + * - EVENT_UAP_MIC_COUNTERMEASURES: + */ +int mwifiex_process_uap_event(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int len, i; + u32 eventcause = adapter->event_cause; + struct station_info sinfo; + struct mwifiex_assoc_event *event; + struct mwifiex_sta_node *node; + u8 *deauth_mac; + struct host_cmd_ds_11n_batimeout *ba_timeout; + u16 ctrl; + + switch (eventcause) { + case EVENT_UAP_STA_ASSOC: + memset(&sinfo, 0, sizeof(sinfo)); + event = (struct mwifiex_assoc_event *) + (adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER); + if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) { + len = -1; + + if (ieee80211_is_assoc_req(event->frame_control)) + len = 0; + else if (ieee80211_is_reassoc_req(event->frame_control)) + /* There will be ETH_ALEN bytes of + * current_ap_addr before the re-assoc ies. + */ + len = ETH_ALEN; + + if (len != -1) { + sinfo.assoc_req_ies = &event->data[len]; + len = (u8 *)sinfo.assoc_req_ies - + (u8 *)&event->frame_control; + sinfo.assoc_req_ies_len = + le16_to_cpu(event->len) - (u16)len; + } + } + cfg80211_new_sta(priv->netdev, event->sta_addr, &sinfo, + GFP_KERNEL); + + node = mwifiex_add_sta_entry(priv, event->sta_addr); + if (!node) { + mwifiex_dbg(adapter, ERROR, + "could not create station entry!\n"); + return -1; + } + + if (!priv->ap_11n_enabled) + break; + + mwifiex_set_sta_ht_cap(priv, sinfo.assoc_req_ies, + sinfo.assoc_req_ies_len, node); + + for (i = 0; i < MAX_NUM_TID; i++) { + if (node->is_11n_enabled) + node->ampdu_sta[i] = + priv->aggr_prio_tbl[i].ampdu_user; + else + node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; + } + memset(node->rx_seq, 0xff, sizeof(node->rx_seq)); + break; + case EVENT_UAP_STA_DEAUTH: + deauth_mac = adapter->event_body + + MWIFIEX_UAP_EVENT_EXTRA_HEADER; + cfg80211_del_sta(priv->netdev, deauth_mac, GFP_KERNEL); + + if (priv->ap_11n_enabled) { + mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac); + mwifiex_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac); + } + mwifiex_wmm_del_peer_ra_list(priv, deauth_mac); + mwifiex_del_sta_entry(priv, deauth_mac); + break; + case EVENT_UAP_BSS_IDLE: + priv->media_connected = false; + priv->port_open = false; + mwifiex_clean_txrx(priv); + mwifiex_del_all_sta_list(priv); + break; + case EVENT_UAP_BSS_ACTIVE: + priv->media_connected = true; + priv->port_open = true; + break; + case EVENT_UAP_BSS_START: + mwifiex_dbg(adapter, EVENT, + "AP EVENT: event id: %#x\n", eventcause); + priv->port_open = false; + memcpy(priv->netdev->dev_addr, adapter->event_body + 2, + ETH_ALEN); + if (priv->hist_data) + mwifiex_hist_data_reset(priv); + mwifiex_check_uap_capabilties(priv, adapter->event_skb); + break; + case EVENT_UAP_MIC_COUNTERMEASURES: + /* For future development */ + mwifiex_dbg(adapter, EVENT, + "AP EVENT: event id: %#x\n", eventcause); + break; + case EVENT_AMSDU_AGGR_CTRL: + ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_dbg(adapter, EVENT, + "event: AMSDU_AGGR_CTRL %d\n", ctrl); + + if (priv->media_connected) { + adapter->tx_buf_size = + min_t(u16, adapter->curr_tx_buf_size, ctrl); + mwifiex_dbg(adapter, EVENT, + "event: tx_buf_size %d\n", + adapter->tx_buf_size); + } + break; + case EVENT_ADDBA: + mwifiex_dbg(adapter, EVENT, "event: ADDBA Request\n"); + if (priv->media_connected) + mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, + adapter->event_body, false); + break; + case EVENT_DELBA: + mwifiex_dbg(adapter, EVENT, "event: DELBA Request\n"); + if (priv->media_connected) + mwifiex_11n_delete_ba_stream(priv, adapter->event_body); + break; + case EVENT_BA_STREAM_TIEMOUT: + mwifiex_dbg(adapter, EVENT, "event: BA Stream timeout\n"); + if (priv->media_connected) { + ba_timeout = (void *)adapter->event_body; + mwifiex_11n_ba_stream_timeout(priv, ba_timeout); + } + break; + case EVENT_EXT_SCAN_REPORT: + mwifiex_dbg(adapter, EVENT, "event: EXT_SCAN Report\n"); + if (adapter->ext_scan) + return mwifiex_handle_event_ext_scan_report(priv, + adapter->event_skb->data); + break; + case EVENT_TX_STATUS_REPORT: + mwifiex_dbg(adapter, EVENT, "event: TX_STATUS Report\n"); + mwifiex_parse_tx_status_event(priv, adapter->event_body); + break; + case EVENT_PS_SLEEP: + mwifiex_dbg(adapter, EVENT, "info: EVENT: SLEEP\n"); + + adapter->ps_state = PS_STATE_PRE_SLEEP; + + mwifiex_check_ps_cond(adapter); + break; + + case EVENT_PS_AWAKE: + mwifiex_dbg(adapter, EVENT, "info: EVENT: AWAKE\n"); + if (!adapter->pps_uapsd_mode && + priv->media_connected && adapter->sleep_period.period) { + adapter->pps_uapsd_mode = true; + mwifiex_dbg(adapter, EVENT, + "event: PPS/UAPSD mode activated\n"); + } + adapter->tx_lock_flag = false; + if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { + if (mwifiex_check_last_packet_indication(priv)) { + if (adapter->data_sent || + (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv))) { + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + break; + } + if (!mwifiex_send_null_packet + (priv, + MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | + MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) + adapter->ps_state = + PS_STATE_SLEEP; + return 0; + } + } + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + break; + + case EVENT_CHANNEL_REPORT_RDY: + mwifiex_dbg(adapter, EVENT, "event: Channel Report\n"); + mwifiex_11h_handle_chanrpt_ready(priv, adapter->event_skb); + break; + case EVENT_RADAR_DETECTED: + mwifiex_dbg(adapter, EVENT, "event: Radar detected\n"); + mwifiex_11h_handle_radar_detected(priv, adapter->event_skb); + break; + case EVENT_BT_COEX_WLAN_PARA_CHANGE: + dev_err(adapter->dev, "EVENT: BT coex wlan param update\n"); + mwifiex_bt_coex_wlan_param_update_event(priv, + adapter->event_skb); + break; + case EVENT_TX_DATA_PAUSE: + mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n"); + mwifiex_process_tx_pause_event(priv, adapter->event_skb); + break; + + case EVENT_MULTI_CHAN_INFO: + mwifiex_dbg(adapter, EVENT, "event: multi-chan info\n"); + mwifiex_process_multi_chan_event(priv, adapter->event_skb); + break; + + default: + mwifiex_dbg(adapter, EVENT, + "event: unknown event id: %#x\n", eventcause); + break; + } + + return 0; +} + +/* This function deletes station entry from associated station list. + * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream + * tables created for this station are deleted. + */ +void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, + struct mwifiex_sta_node *node) +{ + if (priv->ap_11n_enabled && node->is_11n_enabled) { + mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr); + mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr); + } + mwifiex_del_sta_entry(priv, node->mac_addr); + + return; +} diff --git a/drivers/net/wireless/marvell/mwifiex/uap_txrx.c b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c new file mode 100644 index 000000000000..74d5d7238633 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c @@ -0,0 +1,437 @@ +/* + * Marvell Wireless LAN device driver: AP TX and RX data handling + * + * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "main.h" +#include "wmm.h" +#include "11n_aggr.h" +#include "11n_rxreorder.h" + +/* This function checks if particular RA list has packets more than low bridge + * packet threshold and then deletes packet from this RA list. + * Function deletes packets from such RA list and returns true. If no such list + * is found, false is returned. + */ +static bool +mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv, + struct list_head *ra_list_head, + int tid) +{ + struct mwifiex_ra_list_tbl *ra_list; + struct sk_buff *skb, *tmp; + bool pkt_deleted = false; + struct mwifiex_txinfo *tx_info; + struct mwifiex_adapter *adapter = priv->adapter; + + list_for_each_entry(ra_list, ra_list_head, list) { + if (skb_queue_empty(&ra_list->skb_head)) + continue; + + skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) { + tx_info = MWIFIEX_SKB_TXCB(skb); + if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) { + __skb_unlink(skb, &ra_list->skb_head); + mwifiex_write_data_complete(adapter, skb, 0, + -1); + if (ra_list->tx_paused) + priv->wmm.pkts_paused[tid]--; + else + atomic_dec(&priv->wmm.tx_pkts_queued); + pkt_deleted = true; + } + if ((atomic_read(&adapter->pending_bridged_pkts) <= + MWIFIEX_BRIDGED_PKTS_THR_LOW)) + break; + } + } + + return pkt_deleted; +} + +/* This function deletes packets from particular RA List. RA list index + * from which packets are deleted is preserved so that packets from next RA + * list are deleted upon subsequent call thus maintaining fairness. + */ +static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv) +{ + unsigned long flags; + struct list_head *ra_list; + int i; + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) { + if (priv->del_list_idx == MAX_NUM_TID) + priv->del_list_idx = 0; + ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list; + if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) { + priv->del_list_idx++; + break; + } + } + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + + +static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct uap_rxpd *uap_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + struct sk_buff *new_skb; + struct mwifiex_txinfo *tx_info; + int hdr_chop; + struct ethhdr *p_ethhdr; + struct mwifiex_sta_node *src_node; + + uap_rx_pd = (struct uap_rxpd *)(skb->data); + rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); + + if ((atomic_read(&adapter->pending_bridged_pkts) >= + MWIFIEX_BRIDGED_PKTS_THR_HIGH)) { + mwifiex_dbg(priv->adapter, ERROR, + "Tx: Bridge packet limit reached. Drop packet!\n"); + kfree_skb(skb); + mwifiex_uap_cleanup_tx_queues(priv); + return; + } + + if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, + sizeof(bridge_tunnel_header))) || + (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, + sizeof(rfc1042_header)) && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { + /* Replace the 803 header and rfc1042 header (llc/snap) with + * an Ethernet II header, keep the src/dst and snap_type + * (ethertype). + * + * The firmware only passes up SNAP frames converting all RX + * data from 802.11 to 802.2/LLC/SNAP frames. + * + * To create the Ethernet II, just move the src, dst address + * right before the snap_type. + */ + p_ethhdr = (struct ethhdr *) + ((u8 *)(&rx_pkt_hdr->eth803_hdr) + + sizeof(rx_pkt_hdr->eth803_hdr) + + sizeof(rx_pkt_hdr->rfc1042_hdr) + - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) + - sizeof(rx_pkt_hdr->eth803_hdr.h_source) + - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); + memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source, + sizeof(p_ethhdr->h_source)); + memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, + sizeof(p_ethhdr->h_dest)); + /* Chop off the rxpd + the excess memory from + * 802.2/llc/snap header that was removed. + */ + hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd; + } else { + /* Chop off the rxpd */ + hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd; + } + + /* Chop off the leading header bytes so that it points + * to the start of either the reconstructed EthII frame + * or the 802.2/llc/snap frame. + */ + skb_pull(skb, hdr_chop); + + if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { + mwifiex_dbg(priv->adapter, ERROR, + "data: Tx: insufficient skb headroom %d\n", + skb_headroom(skb)); + /* Insufficient skb headroom - allocate a new skb */ + new_skb = + skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + if (unlikely(!new_skb)) { + mwifiex_dbg(priv->adapter, ERROR, + "Tx: cannot allocate new_skb\n"); + kfree_skb(skb); + priv->stats.tx_dropped++; + return; + } + + kfree_skb(skb); + skb = new_skb; + mwifiex_dbg(priv->adapter, INFO, + "info: new skb headroom %d\n", + skb_headroom(skb)); + } + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT; + + src_node = mwifiex_get_sta_entry(priv, rx_pkt_hdr->eth803_hdr.h_source); + if (src_node) { + src_node->stats.last_rx = jiffies; + src_node->stats.rx_bytes += skb->len; + src_node->stats.rx_packets++; + src_node->stats.last_tx_rate = uap_rx_pd->rx_rate; + src_node->stats.last_tx_htinfo = uap_rx_pd->ht_info; + } + + if (is_unicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest)) { + /* Update bridge packet statistics as the + * packet is not going to kernel/upper layer. + */ + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + /* Sending bridge packet to TX queue, so save the packet + * length in TXCB to update statistics in TX complete. + */ + tx_info->pkt_len = skb->len; + } + + __net_timestamp(skb); + mwifiex_wmm_add_buf_txqueue(priv, skb); + atomic_inc(&adapter->tx_pending); + atomic_inc(&adapter->pending_bridged_pkts); + + return; +} + +/* + * This function contains logic for AP packet forwarding. + * + * If a packet is multicast/broadcast, it is sent to kernel/upper layer + * as well as queued back to AP TX queue so that it can be sent to other + * associated stations. + * If a packet is unicast and RA is present in associated station list, + * it is again requeued into AP TX queue. + * If a packet is unicast and RA is not in associated station list, + * packet is forwarded to kernel to handle routing logic. + */ +int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct uap_rxpd *uap_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + u8 ra[ETH_ALEN]; + struct sk_buff *skb_uap; + + uap_rx_pd = (struct uap_rxpd *)(skb->data); + rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); + + /* don't do packet forwarding in disconnected state */ + if (!priv->media_connected) { + mwifiex_dbg(adapter, ERROR, + "drop packet in disconnected state.\n"); + dev_kfree_skb_any(skb); + return 0; + } + + memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN); + + if (is_multicast_ether_addr(ra)) { + skb_uap = skb_copy(skb, GFP_ATOMIC); + mwifiex_uap_queue_bridged_pkt(priv, skb_uap); + } else { + if (mwifiex_get_sta_entry(priv, ra)) { + /* Requeue Intra-BSS packet */ + mwifiex_uap_queue_bridged_pkt(priv, skb); + return 0; + } + } + + /* Forward unicat/Inter-BSS packets to kernel. */ + return mwifiex_process_rx_packet(priv, skb); +} + +/* + * This function processes the packet received on AP interface. + * + * The function looks into the RxPD and performs sanity tests on the + * received buffer to ensure its a valid packet before processing it + * further. If the packet is determined to be aggregated, it is + * de-aggregated accordingly. Then skb is passed to AP packet forwarding logic. + * + * The completion callback is called after processing is complete. + */ +int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + struct uap_rxpd *uap_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + u16 rx_pkt_type; + u8 ta[ETH_ALEN], pkt_type; + unsigned long flags; + struct mwifiex_sta_node *node; + + uap_rx_pd = (struct uap_rxpd *)(skb->data); + rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type); + rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); + + ether_addr_copy(ta, rx_pkt_hdr->eth803_hdr.h_source); + + if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) + + le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16) skb->len) { + mwifiex_dbg(adapter, ERROR, + "wrong rx packet: len=%d, offset=%d, length=%d\n", + skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset), + le16_to_cpu(uap_rx_pd->rx_pkt_length)); + priv->stats.rx_dropped++; + + node = mwifiex_get_sta_entry(priv, ta); + if (node) + node->stats.tx_failed++; + + dev_kfree_skb_any(skb); + return 0; + } + + if (rx_pkt_type == PKT_TYPE_MGMT) { + ret = mwifiex_process_mgmt_packet(priv, skb); + if (ret) + mwifiex_dbg(adapter, ERROR, + "Rx of mgmt packet failed"); + dev_kfree_skb_any(skb); + return ret; + } + + + if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) { + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + node = mwifiex_get_sta_entry(priv, ta); + if (node) + node->rx_seq[uap_rx_pd->priority] = + le16_to_cpu(uap_rx_pd->seq_num); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + } + + if (!priv->ap_11n_enabled || + (!mwifiex_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) && + (le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) { + ret = mwifiex_handle_uap_rx_forward(priv, skb); + return ret; + } + + /* Reorder and send to kernel */ + pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type); + ret = mwifiex_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num), + uap_rx_pd->priority, ta, pkt_type, + skb); + + if (ret || (rx_pkt_type == PKT_TYPE_BAR)) + dev_kfree_skb_any(skb); + + if (ret) + priv->stats.rx_dropped++; + + return ret; +} + +/* + * This function fills the TxPD for AP tx packets. + * + * The Tx buffer received by this function should already have the + * header space allocated for TxPD. + * + * This function inserts the TxPD in between interface header and actual + * data and adjusts the buffer pointers accordingly. + * + * The following TxPD fields are set by this function, as required - + * - BSS number + * - Tx packet length and offset + * - Priority + * - Packet delay + * - Priority specific Tx control + * - Flags + */ +void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct uap_txpd *txpd; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + int pad; + u16 pkt_type, pkt_offset; + int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : + INTF_HEADER_LEN; + + if (!skb->len) { + mwifiex_dbg(adapter, ERROR, + "Tx: bad packet length: %d\n", skb->len); + tx_info->status_code = -1; + return skb->data; + } + + BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); + + pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + + pad = ((void *)skb->data - (sizeof(*txpd) + hroom) - NULL) & + (MWIFIEX_DMA_ALIGN_SZ - 1); + + skb_push(skb, sizeof(*txpd) + pad); + + txpd = (struct uap_txpd *)skb->data; + memset(txpd, 0, sizeof(*txpd)); + txpd->bss_num = priv->bss_num; + txpd->bss_type = priv->bss_type; + txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) + + pad))); + txpd->priority = (u8)skb->priority; + + txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb); + + if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS || + tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) { + txpd->tx_token_id = tx_info->ack_frame_id; + txpd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS; + } + + if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) + /* + * Set the priority specific tx_control field, setting of 0 will + * cause the default value to be used later in this function. + */ + txpd->tx_control = + cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]); + + /* Offset of actual data */ + pkt_offset = sizeof(*txpd) + pad; + if (pkt_type == PKT_TYPE_MGMT) { + /* Set the packet type and add header for management frame */ + txpd->tx_pkt_type = cpu_to_le16(pkt_type); + pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; + } + + txpd->tx_pkt_offset = cpu_to_le16(pkt_offset); + + /* make space for INTF_HEADER_LEN */ + skb_push(skb, hroom); + + if (!txpd->tx_control) + /* TxCtrl set by user or default */ + txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); + + return skb->data; +} diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c new file mode 100644 index 000000000000..e43aff932360 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -0,0 +1,1267 @@ +/* + * Marvell Wireless LAN device driver: USB specific handling + * + * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" +#include "usb.h" + +#define USB_VERSION "1.0" + +static u8 user_rmmod; +static struct mwifiex_if_ops usb_ops; +static struct semaphore add_remove_card_sem; + +static struct usb_device_id mwifiex_usb_table[] = { + /* 8766 */ + {USB_DEVICE(USB8XXX_VID, USB8766_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8766_PID_2, + USB_CLASS_VENDOR_SPEC, + USB_SUBCLASS_VENDOR_SPEC, 0xff)}, + /* 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)}, + /* 8801 */ + {USB_DEVICE(USB8XXX_VID, USB8801_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8801_PID_2, + USB_CLASS_VENDOR_SPEC, + USB_SUBCLASS_VENDOR_SPEC, 0xff)}, + /* 8997 */ + {USB_DEVICE(USB8XXX_VID, USB8997_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8997_PID_2, + USB_CLASS_VENDOR_SPEC, + USB_SUBCLASS_VENDOR_SPEC, 0xff)}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, mwifiex_usb_table); + +static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size); + +/* This function handles received packet. Necessary action is taken based on + * cmd/event/data. + */ +static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, + struct sk_buff *skb, u8 ep) +{ + u32 recv_type; + __le32 tmp; + int ret; + + if (adapter->hs_activated) + mwifiex_process_hs_config(adapter); + + if (skb->len < INTF_HEADER_LEN) { + mwifiex_dbg(adapter, ERROR, + "%s: invalid skb->len\n", __func__); + return -1; + } + + switch (ep) { + case MWIFIEX_USB_EP_CMD_EVENT: + mwifiex_dbg(adapter, EVENT, + "%s: EP_CMD_EVENT\n", __func__); + skb_copy_from_linear_data(skb, &tmp, INTF_HEADER_LEN); + recv_type = le32_to_cpu(tmp); + skb_pull(skb, INTF_HEADER_LEN); + + switch (recv_type) { + case MWIFIEX_USB_TYPE_CMD: + if (skb->len > MWIFIEX_SIZE_OF_CMD_BUFFER) { + mwifiex_dbg(adapter, ERROR, + "CMD: skb->len too large\n"); + ret = -1; + goto exit_restore_skb; + } else if (!adapter->curr_cmd) { + mwifiex_dbg(adapter, WARN, "CMD: no curr_cmd\n"); + if (adapter->ps_state == PS_STATE_SLEEP_CFM) { + mwifiex_process_sleep_confirm_resp( + adapter, skb->data, + skb->len); + ret = 0; + goto exit_restore_skb; + } + ret = -1; + goto exit_restore_skb; + } + + adapter->curr_cmd->resp_skb = skb; + adapter->cmd_resp_received = true; + break; + case MWIFIEX_USB_TYPE_EVENT: + if (skb->len < sizeof(u32)) { + mwifiex_dbg(adapter, ERROR, + "EVENT: skb->len too small\n"); + ret = -1; + goto exit_restore_skb; + } + skb_copy_from_linear_data(skb, &tmp, sizeof(u32)); + adapter->event_cause = le32_to_cpu(tmp); + mwifiex_dbg(adapter, EVENT, + "event_cause %#x\n", adapter->event_cause); + + if (skb->len > MAX_EVENT_SIZE) { + mwifiex_dbg(adapter, ERROR, + "EVENT: event body too large\n"); + ret = -1; + goto exit_restore_skb; + } + + memcpy(adapter->event_body, skb->data + + MWIFIEX_EVENT_HEADER_LEN, skb->len); + + adapter->event_received = true; + adapter->event_skb = skb; + break; + default: + mwifiex_dbg(adapter, ERROR, + "unknown recv_type %#x\n", recv_type); + return -1; + } + break; + case MWIFIEX_USB_EP_DATA: + mwifiex_dbg(adapter, DATA, "%s: EP_DATA\n", __func__); + if (skb->len > MWIFIEX_RX_DATA_BUF_SIZE) { + mwifiex_dbg(adapter, ERROR, + "DATA: skb->len too large\n"); + return -1; + } + + skb_queue_tail(&adapter->rx_data_q, skb); + adapter->data_received = true; + atomic_inc(&adapter->rx_pending); + break; + default: + mwifiex_dbg(adapter, ERROR, + "%s: unknown endport %#x\n", __func__, ep); + return -1; + } + + return -EINPROGRESS; + +exit_restore_skb: + /* The buffer will be reused for further cmds/events */ + skb_push(skb, INTF_HEADER_LEN); + + return ret; +} + +static void mwifiex_usb_rx_complete(struct urb *urb) +{ + struct urb_context *context = (struct urb_context *)urb->context; + struct mwifiex_adapter *adapter = context->adapter; + struct sk_buff *skb = context->skb; + struct usb_card_rec *card; + int recv_length = urb->actual_length; + int size, status; + + if (!adapter || !adapter->card) { + pr_err("mwifiex adapter or card structure is not valid\n"); + return; + } + + card = (struct usb_card_rec *)adapter->card; + if (card->rx_cmd_ep == context->ep) + atomic_dec(&card->rx_cmd_urb_pending); + else + atomic_dec(&card->rx_data_urb_pending); + + if (recv_length) { + if (urb->status || (adapter->surprise_removed)) { + mwifiex_dbg(adapter, ERROR, + "URB status is failed: %d\n", urb->status); + /* Do not free skb in case of command ep */ + if (card->rx_cmd_ep != context->ep) + dev_kfree_skb_any(skb); + goto setup_for_next; + } + if (skb->len > recv_length) + skb_trim(skb, recv_length); + else + skb_put(skb, recv_length - skb->len); + + status = mwifiex_usb_recv(adapter, skb, context->ep); + + mwifiex_dbg(adapter, INFO, + "info: recv_length=%d, status=%d\n", + recv_length, status); + if (status == -EINPROGRESS) { + mwifiex_queue_main_work(adapter); + + /* urb for data_ep is re-submitted now; + * urb for cmd_ep will be re-submitted in callback + * mwifiex_usb_recv_complete + */ + if (card->rx_cmd_ep == context->ep) + return; + } else { + if (status == -1) + mwifiex_dbg(adapter, ERROR, + "received data processing failed!\n"); + + /* Do not free skb in case of command ep */ + if (card->rx_cmd_ep != context->ep) + dev_kfree_skb_any(skb); + } + } else if (urb->status) { + if (!adapter->is_suspended) { + mwifiex_dbg(adapter, FATAL, + "Card is removed: %d\n", urb->status); + adapter->surprise_removed = true; + } + dev_kfree_skb_any(skb); + return; + } else { + /* Do not free skb in case of command ep */ + if (card->rx_cmd_ep != context->ep) + dev_kfree_skb_any(skb); + + /* fall through setup_for_next */ + } + +setup_for_next: + if (card->rx_cmd_ep == context->ep) + size = MWIFIEX_RX_CMD_BUF_SIZE; + else + size = MWIFIEX_RX_DATA_BUF_SIZE; + + if (card->rx_cmd_ep == context->ep) { + mwifiex_usb_submit_rx_urb(context, size); + } else { + if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING){ + mwifiex_usb_submit_rx_urb(context, size); + }else{ + context->skb = NULL; + } + } + + return; +} + +static void mwifiex_usb_tx_complete(struct urb *urb) +{ + struct urb_context *context = (struct urb_context *)(urb->context); + struct mwifiex_adapter *adapter = context->adapter; + struct usb_card_rec *card = adapter->card; + struct usb_tx_data_port *port; + int i; + + mwifiex_dbg(adapter, INFO, + "%s: status: %d\n", __func__, urb->status); + + if (context->ep == card->tx_cmd_ep) { + mwifiex_dbg(adapter, CMD, + "%s: CMD\n", __func__); + atomic_dec(&card->tx_cmd_urb_pending); + adapter->cmd_sent = false; + } else { + mwifiex_dbg(adapter, DATA, + "%s: DATA\n", __func__); + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + if (context->ep == port->tx_data_ep) { + atomic_dec(&port->tx_data_urb_pending); + port->block_status = false; + break; + } + } + adapter->data_sent = false; + mwifiex_write_data_complete(adapter, context->skb, 0, + urb->status ? -1 : 0); + } + + if (card->mc_resync_flag) + mwifiex_multi_chan_resync(adapter); + + mwifiex_queue_main_work(adapter); + + return; +} + +static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size) +{ + struct mwifiex_adapter *adapter = ctx->adapter; + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + + if (card->rx_cmd_ep != ctx->ep) { + ctx->skb = dev_alloc_skb(size); + if (!ctx->skb) { + mwifiex_dbg(adapter, ERROR, + "%s: dev_alloc_skb failed\n", __func__); + return -ENOMEM; + } + } + + usb_fill_bulk_urb(ctx->urb, card->udev, + usb_rcvbulkpipe(card->udev, ctx->ep), ctx->skb->data, + size, mwifiex_usb_rx_complete, (void *)ctx); + + if (card->rx_cmd_ep == ctx->ep) + atomic_inc(&card->rx_cmd_urb_pending); + else + atomic_inc(&card->rx_data_urb_pending); + + if (usb_submit_urb(ctx->urb, GFP_ATOMIC)) { + mwifiex_dbg(adapter, ERROR, "usb_submit_urb failed\n"); + dev_kfree_skb_any(ctx->skb); + ctx->skb = NULL; + + if (card->rx_cmd_ep == ctx->ep) + atomic_dec(&card->rx_cmd_urb_pending); + else + atomic_dec(&card->rx_data_urb_pending); + + return -1; + } + + return 0; +} + +static void mwifiex_usb_free(struct usb_card_rec *card) +{ + struct usb_tx_data_port *port; + int i, j; + + if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) + usb_kill_urb(card->rx_cmd.urb); + + usb_free_urb(card->rx_cmd.urb); + card->rx_cmd.urb = NULL; + + if (atomic_read(&card->rx_data_urb_pending)) + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) + if (card->rx_data_list[i].urb) + usb_kill_urb(card->rx_data_list[i].urb); + + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { + usb_free_urb(card->rx_data_list[i].urb); + card->rx_data_list[i].urb = NULL; + } + + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + usb_free_urb(port->tx_data_list[j].urb); + port->tx_data_list[j].urb = NULL; + } + } + + usb_free_urb(card->tx_cmd.urb); + card->tx_cmd.urb = NULL; + + return; +} + +/* This function probes an mwifiex device and registers it. It allocates + * the card structure, initiates the device registration and initialization + * procedure by adding a logical interface. + */ +static int mwifiex_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *iface_desc = intf->cur_altsetting; + struct usb_endpoint_descriptor *epd; + int ret, i; + struct usb_card_rec *card; + u16 id_vendor, id_product, bcd_device, bcd_usb; + + card = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); + if (!card) + return -ENOMEM; + + id_vendor = le16_to_cpu(udev->descriptor.idVendor); + id_product = le16_to_cpu(udev->descriptor.idProduct); + bcd_device = le16_to_cpu(udev->descriptor.bcdDevice); + bcd_usb = le16_to_cpu(udev->descriptor.bcdUSB); + pr_debug("info: VID/PID = %X/%X, Boot2 version = %X\n", + id_vendor, id_product, bcd_device); + + /* PID_1 is used for firmware downloading only */ + switch (id_product) { + case USB8766_PID_1: + case USB8797_PID_1: + case USB8801_PID_1: + case USB8997_PID_1: + card->usb_boot_state = USB8XXX_FW_DNLD; + break; + case USB8766_PID_2: + case USB8797_PID_2: + case USB8801_PID_2: + case USB8997_PID_2: + card->usb_boot_state = USB8XXX_FW_READY; + break; + default: + pr_warn("unknown id_product %#x\n", id_product); + card->usb_boot_state = USB8XXX_FW_DNLD; + break; + } + + card->udev = udev; + card->intf = intf; + + pr_debug("info: bcdUSB=%#x Device Class=%#x SubClass=%#x Protocol=%#x\n", + udev->descriptor.bcdUSB, udev->descriptor.bDeviceClass, + udev->descriptor.bDeviceSubClass, + udev->descriptor.bDeviceProtocol); + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + epd = &iface_desc->endpoint[i].desc; + if (usb_endpoint_dir_in(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->rx_cmd_ep = usb_endpoint_num(epd); + atomic_set(&card->rx_cmd_urb_pending, 0); + } + if (usb_endpoint_dir_in(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->rx_data_ep = usb_endpoint_num(epd); + atomic_set(&card->rx_data_urb_pending, 0); + } + if (usb_endpoint_dir_out(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->port[0].tx_data_ep = usb_endpoint_num(epd); + atomic_set(&card->port[0].tx_data_urb_pending, 0); + } + if (usb_endpoint_dir_out(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA_CH2 && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk OUT chan2:\t" + "max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->port[1].tx_data_ep = usb_endpoint_num(epd); + atomic_set(&card->port[1].tx_data_urb_pending, 0); + } + if (usb_endpoint_dir_out(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->tx_cmd_ep = usb_endpoint_num(epd); + atomic_set(&card->tx_cmd_urb_pending, 0); + card->bulk_out_maxpktsize = + le16_to_cpu(epd->wMaxPacketSize); + } + } + + usb_set_intfdata(intf, card); + + ret = mwifiex_add_card(card, &add_remove_card_sem, &usb_ops, + MWIFIEX_USB); + if (ret) { + pr_err("%s: mwifiex_add_card failed: %d\n", __func__, ret); + usb_reset_device(udev); + kfree(card); + return ret; + } + + usb_get_dev(udev); + + return 0; +} + +/* Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not suspended, this function allocates and sends a + * 'host sleep activate' request to the firmware and turns off the traffic. + */ +static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_card_rec *card = usb_get_intfdata(intf); + struct mwifiex_adapter *adapter; + struct usb_tx_data_port *port; + int i, j; + + if (!card || !card->adapter) { + pr_err("%s: card or card->adapter is NULL\n", __func__); + return 0; + } + adapter = card->adapter; + + if (unlikely(adapter->is_suspended)) + mwifiex_dbg(adapter, WARN, + "Device already suspended\n"); + + mwifiex_enable_hs(adapter); + + /* 'is_suspended' flag indicates device is suspended. + * It must be set here before the usb_kill_urb() calls. Reason + * is in the complete handlers, urb->status(= -ENOENT) and + * this flag is used in combination to distinguish between a + * 'suspended' state and a 'disconnect' one. + */ + adapter->is_suspended = true; + adapter->hs_enabling = false; + + if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) + usb_kill_urb(card->rx_cmd.urb); + + if (atomic_read(&card->rx_data_urb_pending)) + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) + if (card->rx_data_list[i].urb) + usb_kill_urb(card->rx_data_list[i].urb); + + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + if (port->tx_data_list[j].urb) + usb_kill_urb(port->tx_data_list[j].urb); + } + } + + if (card->tx_cmd.urb) + usb_kill_urb(card->tx_cmd.urb); + + return 0; +} + +/* Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not resumed, this function turns on the traffic and + * sends a 'host sleep cancel' request to the firmware. + */ +static int mwifiex_usb_resume(struct usb_interface *intf) +{ + struct usb_card_rec *card = usb_get_intfdata(intf); + struct mwifiex_adapter *adapter; + int i; + + if (!card || !card->adapter) { + pr_err("%s: card or card->adapter is NULL\n", __func__); + return 0; + } + adapter = card->adapter; + + if (unlikely(!adapter->is_suspended)) { + mwifiex_dbg(adapter, WARN, + "Device already resumed\n"); + return 0; + } + + /* Indicate device resumed. The netdev queue will be resumed only + * after the urbs have been re-submitted + */ + adapter->is_suspended = false; + + if (!atomic_read(&card->rx_data_urb_pending)) + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) + mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], + MWIFIEX_RX_DATA_BUF_SIZE); + + if (!atomic_read(&card->rx_cmd_urb_pending)) { + card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); + if (card->rx_cmd.skb) + mwifiex_usb_submit_rx_urb(&card->rx_cmd, + MWIFIEX_RX_CMD_BUF_SIZE); + } + + /* Disable Host Sleep */ + if (adapter->hs_activated) + mwifiex_cancel_hs(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY), + MWIFIEX_ASYNC_CMD); + + return 0; +} + +static void mwifiex_usb_disconnect(struct usb_interface *intf) +{ + struct usb_card_rec *card = usb_get_intfdata(intf); + struct mwifiex_adapter *adapter; + + if (!card || !card->adapter) { + pr_err("%s: card or card->adapter is NULL\n", __func__); + return; + } + + adapter = card->adapter; + if (!adapter->priv_num) + return; + + if (user_rmmod) { +#ifdef CONFIG_PM + if (adapter->is_suspended) + mwifiex_usb_resume(intf); +#endif + + mwifiex_deauthenticate_all(adapter); + + mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY), + MWIFIEX_FUNC_SHUTDOWN); + } + + mwifiex_usb_free(card); + + mwifiex_dbg(adapter, FATAL, + "%s: removing card\n", __func__); + mwifiex_remove_card(adapter, &add_remove_card_sem); + + usb_set_intfdata(intf, NULL); + usb_put_dev(interface_to_usbdev(intf)); + kfree(card); + + return; +} + +static struct usb_driver mwifiex_usb_driver = { + .name = "mwifiex_usb", + .probe = mwifiex_usb_probe, + .disconnect = mwifiex_usb_disconnect, + .id_table = mwifiex_usb_table, + .suspend = mwifiex_usb_suspend, + .resume = mwifiex_usb_resume, + .soft_unbind = 1, +}; + +static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + struct usb_tx_data_port *port; + int i, j; + + card->tx_cmd.adapter = adapter; + card->tx_cmd.ep = card->tx_cmd_ep; + + card->tx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); + if (!card->tx_cmd.urb) { + mwifiex_dbg(adapter, ERROR, + "tx_cmd.urb allocation failed\n"); + return -ENOMEM; + } + + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + if (!port->tx_data_ep) + continue; + port->tx_data_ix = 0; + if (port->tx_data_ep == MWIFIEX_USB_EP_DATA) + port->block_status = false; + else + port->block_status = true; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + port->tx_data_list[j].adapter = adapter; + port->tx_data_list[j].ep = port->tx_data_ep; + port->tx_data_list[j].urb = + usb_alloc_urb(0, GFP_KERNEL); + if (!port->tx_data_list[j].urb) { + mwifiex_dbg(adapter, ERROR, + "urb allocation failed\n"); + return -ENOMEM; + } + } + } + + return 0; +} + +static int mwifiex_usb_rx_init(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + int i; + + card->rx_cmd.adapter = adapter; + card->rx_cmd.ep = card->rx_cmd_ep; + + card->rx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); + if (!card->rx_cmd.urb) { + mwifiex_dbg(adapter, ERROR, "rx_cmd.urb allocation failed\n"); + return -ENOMEM; + } + + card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); + if (!card->rx_cmd.skb) + return -ENOMEM; + + if (mwifiex_usb_submit_rx_urb(&card->rx_cmd, MWIFIEX_RX_CMD_BUF_SIZE)) + return -1; + + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { + card->rx_data_list[i].adapter = adapter; + card->rx_data_list[i].ep = card->rx_data_ep; + + card->rx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); + if (!card->rx_data_list[i].urb) { + mwifiex_dbg(adapter, ERROR, + "rx_data_list[] urb allocation failed\n"); + return -1; + } + if (mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], + MWIFIEX_RX_DATA_BUF_SIZE)) + return -1; + } + + return 0; +} + +static int mwifiex_write_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, + u32 *len, u8 ep, u32 timeout) +{ + struct usb_card_rec *card = adapter->card; + int actual_length, ret; + + if (!(*len % card->bulk_out_maxpktsize)) + (*len)++; + + /* Send the data block */ + ret = usb_bulk_msg(card->udev, usb_sndbulkpipe(card->udev, ep), pbuf, + *len, &actual_length, timeout); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "usb_bulk_msg for tx failed: %d\n", ret); + return ret; + } + + *len = actual_length; + + return ret; +} + +static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, + u32 *len, u8 ep, u32 timeout) +{ + struct usb_card_rec *card = adapter->card; + int actual_length, ret; + + /* Receive the data response */ + ret = usb_bulk_msg(card->udev, usb_rcvbulkpipe(card->udev, ep), pbuf, + *len, &actual_length, timeout); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "usb_bulk_msg for rx failed: %d\n", ret); + return ret; + } + + *len = actual_length; + + return ret; +} + +static void mwifiex_usb_port_resync(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + u8 active_port = MWIFIEX_USB_EP_DATA; + struct mwifiex_private *priv = NULL; + int i; + + if (adapter->usb_mc_status) { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && + !priv->bss_started) || + (priv->bss_role == MWIFIEX_BSS_ROLE_STA && + !priv->media_connected)) + priv->usb_port = MWIFIEX_USB_EP_DATA; + } + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) + card->port[i].block_status = false; + } else { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && + priv->bss_started) || + (priv->bss_role == MWIFIEX_BSS_ROLE_STA && + priv->media_connected)) { + active_port = priv->usb_port; + break; + } + } + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + priv->usb_port = active_port; + } + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + if (active_port == card->port[i].tx_data_ep) + card->port[i].block_status = false; + else + card->port[i].block_status = true; + } + } +} + +static bool mwifiex_usb_is_port_ready(struct mwifiex_private *priv) +{ + struct usb_card_rec *card = priv->adapter->card; + int idx; + + for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { + if (priv->usb_port == card->port[idx].tx_data_ep) + return !card->port[idx].block_status; + } + + return false; +} + +static inline u8 mwifiex_usb_data_sent(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + int i; + + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) + if (!card->port[i].block_status) + return false; + + return true; +} + +/* This function write a command/data packet to card. */ +static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, + struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + struct usb_card_rec *card = adapter->card; + struct urb_context *context = NULL; + struct usb_tx_data_port *port = NULL; + u8 *data = (u8 *)skb->data; + struct urb *tx_urb; + int idx, ret; + + if (adapter->is_suspended) { + mwifiex_dbg(adapter, ERROR, + "%s: not allowed while suspended\n", __func__); + return -1; + } + + if (adapter->surprise_removed) { + mwifiex_dbg(adapter, ERROR, "%s: device removed\n", __func__); + return -1; + } + + mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep); + + if (ep == card->tx_cmd_ep) { + context = &card->tx_cmd; + } else { + for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { + if (ep == card->port[idx].tx_data_ep) { + port = &card->port[idx]; + if (atomic_read(&port->tx_data_urb_pending) + >= MWIFIEX_TX_DATA_URB) { + port->block_status = true; + ret = -EBUSY; + goto done; + } + if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) + port->tx_data_ix = 0; + context = + &port->tx_data_list[port->tx_data_ix++]; + break; + } + } + if (!port) { + mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n"); + return -1; + } + } + + context->adapter = adapter; + context->ep = ep; + context->skb = skb; + tx_urb = context->urb; + + usb_fill_bulk_urb(tx_urb, card->udev, usb_sndbulkpipe(card->udev, ep), + data, skb->len, mwifiex_usb_tx_complete, + (void *)context); + + tx_urb->transfer_flags |= URB_ZERO_PACKET; + + if (ep == card->tx_cmd_ep) + atomic_inc(&card->tx_cmd_urb_pending); + else + atomic_inc(&port->tx_data_urb_pending); + + if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { + mwifiex_dbg(adapter, ERROR, + "%s: usb_submit_urb failed\n", __func__); + if (ep == card->tx_cmd_ep) { + atomic_dec(&card->tx_cmd_urb_pending); + } else { + atomic_dec(&port->tx_data_urb_pending); + port->block_status = false; + if (port->tx_data_ix) + port->tx_data_ix--; + else + port->tx_data_ix = MWIFIEX_TX_DATA_URB; + } + + return -1; + } else { + if (ep != card->tx_cmd_ep && + atomic_read(&port->tx_data_urb_pending) == + MWIFIEX_TX_DATA_URB) { + port->block_status = true; + ret = -ENOSR; + goto done; + } + } + + return -EINPROGRESS; + +done: + if (ep != card->tx_cmd_ep) + adapter->data_sent = mwifiex_usb_data_sent(adapter); + + return ret; +} + +/* This function register usb device and initialize parameter. */ +static int mwifiex_register_dev(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + + card->adapter = adapter; + adapter->dev = &card->udev->dev; + + switch (le16_to_cpu(card->udev->descriptor.idProduct)) { + case USB8997_PID_1: + case USB8997_PID_2: + adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K; + strcpy(adapter->fw_name, USB8997_DEFAULT_FW_NAME); + adapter->ext_scan = true; + break; + case USB8766_PID_1: + case USB8766_PID_2: + adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; + strcpy(adapter->fw_name, USB8766_DEFAULT_FW_NAME); + adapter->ext_scan = true; + break; + case USB8801_PID_1: + case USB8801_PID_2: + adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; + strcpy(adapter->fw_name, USB8801_DEFAULT_FW_NAME); + adapter->ext_scan = false; + break; + case USB8797_PID_1: + case USB8797_PID_2: + default: + adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; + strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME); + break; + } + + adapter->usb_mc_status = false; + adapter->usb_mc_setup = false; + + return 0; +} + +static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + + card->adapter = NULL; +} + +static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, + struct mwifiex_fw_image *fw) +{ + int ret = 0; + u8 *firmware = fw->fw_buf, *recv_buff; + 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; + u8 check_winner = 1; + + if (!firmware) { + mwifiex_dbg(adapter, ERROR, + "No firmware image found! Terminating download\n"); + ret = -1; + goto fw_exit; + } + + /* Allocate memory for transmit */ + fwdata = kzalloc(FW_DNLD_TX_BUF_SIZE, GFP_KERNEL); + if (!fwdata) { + ret = -ENOMEM; + goto fw_exit; + } + + /* Allocate memory for receive */ + recv_buff = kzalloc(FW_DNLD_RX_BUF_SIZE, GFP_KERNEL); + if (!recv_buff) + goto cleanup; + + do { + /* Send pseudo data to check winner status first */ + if (check_winner) { + memset(&fwdata->fw_hdr, 0, sizeof(struct fw_header)); + dlen = 0; + } else { + /* copy the header of the fw_data to get the length */ + memcpy(&fwdata->fw_hdr, &firmware[tlen], + sizeof(struct fw_header)); + + dlen = le32_to_cpu(fwdata->fw_hdr.data_len); + dnld_cmd = le32_to_cpu(fwdata->fw_hdr.dnld_cmd); + tlen += sizeof(struct fw_header); + + memcpy(fwdata->data, &firmware[tlen], dlen); + + fwdata->seq_num = cpu_to_le32(fw_seqnum); + tlen += dlen; + } + + /* If the send/receive fails or CRC occurs then retry */ + while (retries--) { + u8 *buf = (u8 *)fwdata; + u32 len = FW_DATA_XMIT_SIZE; + + /* send the firmware block */ + ret = mwifiex_write_data_sync(adapter, buf, &len, + MWIFIEX_USB_EP_CMD_EVENT, + MWIFIEX_USB_TIMEOUT); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "write_data_sync: failed: %d\n", + ret); + continue; + } + + buf = recv_buff; + len = FW_DNLD_RX_BUF_SIZE; + + /* Receive the firmware block response */ + ret = mwifiex_read_data_sync(adapter, buf, &len, + MWIFIEX_USB_EP_CMD_EVENT, + MWIFIEX_USB_TIMEOUT); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "read_data_sync: failed: %d\n", + ret); + continue; + } + + memcpy(&sync_fw, recv_buff, + sizeof(struct fw_sync_header)); + + /* check 1st firmware block resp for highest bit set */ + if (check_winner) { + if (le32_to_cpu(sync_fw.cmd) & 0x80000000) { + mwifiex_dbg(adapter, WARN, + "USB is not the winner %#x\n", + sync_fw.cmd); + + /* returning success */ + ret = 0; + goto cleanup; + } + + mwifiex_dbg(adapter, MSG, + "start to download FW...\n"); + + check_winner = 0; + break; + } + + /* check the firmware block response for CRC errors */ + if (sync_fw.cmd) { + mwifiex_dbg(adapter, ERROR, + "FW received block with CRC %#x\n", + sync_fw.cmd); + ret = -1; + continue; + } + + retries = USB8XXX_FW_MAX_RETRY; + break; + } + fw_seqnum++; + } while ((dnld_cmd != FW_HAS_LAST_BLOCK) && retries); + +cleanup: + mwifiex_dbg(adapter, MSG, + "info: FW download over, size %d bytes\n", tlen); + + kfree(recv_buff); + kfree(fwdata); + + if (retries) + ret = 0; +fw_exit: + return ret; +} + +static int mwifiex_usb_dnld_fw(struct mwifiex_adapter *adapter, + struct mwifiex_fw_image *fw) +{ + int ret; + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + + 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 == USB8XXX_FW_DNLD) + return -1; + } + + ret = mwifiex_usb_rx_init(adapter); + if (!ret) + ret = mwifiex_usb_tx_init(adapter); + + return ret; +} + +static void mwifiex_submit_rx_urb(struct mwifiex_adapter *adapter, u8 ep) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + + skb_push(card->rx_cmd.skb, INTF_HEADER_LEN); + if ((ep == card->rx_cmd_ep) && + (!atomic_read(&card->rx_cmd_urb_pending))) + mwifiex_usb_submit_rx_urb(&card->rx_cmd, + MWIFIEX_RX_CMD_BUF_SIZE); + + return; +} + +static int mwifiex_usb_cmd_event_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + mwifiex_submit_rx_urb(adapter, MWIFIEX_USB_EP_CMD_EVENT); + + return 0; +} + +/* This function wakes up the card. */ +static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) +{ + /* Simulation of HS_AWAKE event */ + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + adapter->pm_wakeup_card_req = false; + adapter->ps_state = PS_STATE_AWAKE; + + return 0; +} + +static void mwifiex_usb_submit_rem_rx_urbs(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + int i; + struct urb_context *ctx; + + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { + if (card->rx_data_list[i].skb) + continue; + ctx = &card->rx_data_list[i]; + mwifiex_usb_submit_rx_urb(ctx, MWIFIEX_RX_DATA_BUF_SIZE); + } +} + +/* This function is called after the card has woken up. */ +static inline int +mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) +{ + return 0; +} + +static struct mwifiex_if_ops usb_ops = { + .register_dev = mwifiex_register_dev, + .unregister_dev = mwifiex_unregister_dev, + .wakeup = mwifiex_pm_wakeup_card, + .wakeup_complete = mwifiex_pm_wakeup_card_complete, + + /* USB specific */ + .dnld_fw = mwifiex_usb_dnld_fw, + .cmdrsp_complete = mwifiex_usb_cmd_event_complete, + .event_complete = mwifiex_usb_cmd_event_complete, + .host_to_card = mwifiex_usb_host_to_card, + .submit_rem_rx_urbs = mwifiex_usb_submit_rem_rx_urbs, + .multi_port_resync = mwifiex_usb_port_resync, + .is_port_ready = mwifiex_usb_is_port_ready, +}; + +/* This function initializes the USB driver module. + * + * This initiates the semaphore and registers the device with + * USB bus. + */ +static int mwifiex_usb_init_module(void) +{ + int ret; + + pr_debug("Marvell USB8797 Driver\n"); + + sema_init(&add_remove_card_sem, 1); + + ret = usb_register(&mwifiex_usb_driver); + if (ret) + pr_err("Driver register failed!\n"); + else + pr_debug("info: Driver registered successfully!\n"); + + return ret; +} + +/* This function cleans up the USB driver. + * + * The following major steps are followed in .disconnect for cleanup: + * - Resume the device if its suspended + * - Disconnect the device if connected + * - Shutdown the firmware + * - Unregister the device from USB bus. + */ +static void mwifiex_usb_cleanup_module(void) +{ + if (!down_interruptible(&add_remove_card_sem)) + up(&add_remove_card_sem); + + /* set the flag as user is removing this module */ + user_rmmod = 1; + + usb_deregister(&mwifiex_usb_driver); +} + +module_init(mwifiex_usb_init_module); +module_exit(mwifiex_usb_cleanup_module); + +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(USB8766_DEFAULT_FW_NAME); +MODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME); +MODULE_FIRMWARE(USB8801_DEFAULT_FW_NAME); +MODULE_FIRMWARE(USB8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/marvell/mwifiex/usb.h b/drivers/net/wireless/marvell/mwifiex/usb.h new file mode 100644 index 000000000000..b4e9246bbcdc --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/usb.h @@ -0,0 +1,110 @@ +/* + * This file contains definitions for mwifiex USB interface driver. + * + * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_USB_H +#define _MWIFIEX_USB_H + +#include + +#define USB8XXX_VID 0x1286 + +#define USB8766_PID_1 0x2041 +#define USB8766_PID_2 0x2042 +#define USB8797_PID_1 0x2043 +#define USB8797_PID_2 0x2044 +#define USB8801_PID_1 0x2049 +#define USB8801_PID_2 0x204a +#define USB8997_PID_1 0x2052 +#define USB8997_PID_2 0x204e + + +#define USB8XXX_FW_DNLD 1 +#define USB8XXX_FW_READY 2 +#define USB8XXX_FW_MAX_RETRY 3 + +#define MWIFIEX_TX_DATA_PORT 2 +#define MWIFIEX_TX_DATA_URB 6 +#define MWIFIEX_RX_DATA_URB 6 +#define MWIFIEX_USB_TIMEOUT 100 + +#define USB8766_DEFAULT_FW_NAME "mrvl/usb8766_uapsta.bin" +#define USB8797_DEFAULT_FW_NAME "mrvl/usb8797_uapsta.bin" +#define USB8801_DEFAULT_FW_NAME "mrvl/usb8801_uapsta.bin" +#define USB8997_DEFAULT_FW_NAME "mrvl/usb8997_uapsta.bin" + +#define FW_DNLD_TX_BUF_SIZE 620 +#define FW_DNLD_RX_BUF_SIZE 2048 +#define FW_HAS_LAST_BLOCK 0x00000004 + +#define FW_DATA_XMIT_SIZE \ + (sizeof(struct fw_header) + dlen + sizeof(u32)) + +struct urb_context { + struct mwifiex_adapter *adapter; + struct sk_buff *skb; + struct urb *urb; + u8 ep; +}; + +struct usb_tx_data_port { + u8 tx_data_ep; + u8 block_status; + atomic_t tx_data_urb_pending; + int tx_data_ix; + struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB]; +}; + +struct usb_card_rec { + struct mwifiex_adapter *adapter; + struct usb_device *udev; + struct usb_interface *intf; + u8 rx_cmd_ep; + struct urb_context rx_cmd; + atomic_t rx_cmd_urb_pending; + struct urb_context rx_data_list[MWIFIEX_RX_DATA_URB]; + u8 usb_boot_state; + u8 rx_data_ep; + atomic_t rx_data_urb_pending; + u8 tx_cmd_ep; + atomic_t tx_cmd_urb_pending; + int bulk_out_maxpktsize; + struct urb_context tx_cmd; + u8 mc_resync_flag; + struct usb_tx_data_port port[MWIFIEX_TX_DATA_PORT]; +}; + +struct fw_header { + __le32 dnld_cmd; + __le32 base_addr; + __le32 data_len; + __le32 crc; +}; + +struct fw_sync_header { + __le32 cmd; + __le32 seq_num; +}; + +struct fw_data { + struct fw_header fw_hdr; + __le32 seq_num; + u8 data[1]; +}; + +#endif /*_MWIFIEX_USB_H */ diff --git a/drivers/net/wireless/marvell/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c new file mode 100644 index 000000000000..0cec8a64473e --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/util.c @@ -0,0 +1,751 @@ +/* + * Marvell Wireless LAN device driver: utility functions + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" + +static struct mwifiex_debug_data items[] = { + {"debug_mask", item_size(debug_mask), + item_addr(debug_mask), 1}, + {"int_counter", item_size(int_counter), + item_addr(int_counter), 1}, + {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]), + item_addr(packets_out[WMM_AC_VO]), 1}, + {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]), + item_addr(packets_out[WMM_AC_VI]), 1}, + {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]), + item_addr(packets_out[WMM_AC_BE]), 1}, + {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]), + item_addr(packets_out[WMM_AC_BK]), 1}, + {"tx_buf_size", item_size(tx_buf_size), + item_addr(tx_buf_size), 1}, + {"curr_tx_buf_size", item_size(curr_tx_buf_size), + item_addr(curr_tx_buf_size), 1}, + {"ps_mode", item_size(ps_mode), + item_addr(ps_mode), 1}, + {"ps_state", item_size(ps_state), + item_addr(ps_state), 1}, + {"is_deep_sleep", item_size(is_deep_sleep), + item_addr(is_deep_sleep), 1}, + {"wakeup_dev_req", item_size(pm_wakeup_card_req), + item_addr(pm_wakeup_card_req), 1}, + {"wakeup_tries", item_size(pm_wakeup_fw_try), + item_addr(pm_wakeup_fw_try), 1}, + {"hs_configured", item_size(is_hs_configured), + item_addr(is_hs_configured), 1}, + {"hs_activated", item_size(hs_activated), + item_addr(hs_activated), 1}, + {"num_tx_timeout", item_size(num_tx_timeout), + item_addr(num_tx_timeout), 1}, + {"is_cmd_timedout", item_size(is_cmd_timedout), + item_addr(is_cmd_timedout), 1}, + {"timeout_cmd_id", item_size(timeout_cmd_id), + item_addr(timeout_cmd_id), 1}, + {"timeout_cmd_act", item_size(timeout_cmd_act), + item_addr(timeout_cmd_act), 1}, + {"last_cmd_id", item_size(last_cmd_id), + item_addr(last_cmd_id), DBG_CMD_NUM}, + {"last_cmd_act", item_size(last_cmd_act), + item_addr(last_cmd_act), DBG_CMD_NUM}, + {"last_cmd_index", item_size(last_cmd_index), + item_addr(last_cmd_index), 1}, + {"last_cmd_resp_id", item_size(last_cmd_resp_id), + item_addr(last_cmd_resp_id), DBG_CMD_NUM}, + {"last_cmd_resp_index", item_size(last_cmd_resp_index), + item_addr(last_cmd_resp_index), 1}, + {"last_event", item_size(last_event), + item_addr(last_event), DBG_CMD_NUM}, + {"last_event_index", item_size(last_event_index), + item_addr(last_event_index), 1}, + {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), + item_addr(num_cmd_host_to_card_failure), 1}, + {"num_cmd_sleep_cfm_fail", + item_size(num_cmd_sleep_cfm_host_to_card_failure), + item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1}, + {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), + item_addr(num_tx_host_to_card_failure), 1}, + {"num_evt_deauth", item_size(num_event_deauth), + item_addr(num_event_deauth), 1}, + {"num_evt_disassoc", item_size(num_event_disassoc), + item_addr(num_event_disassoc), 1}, + {"num_evt_link_lost", item_size(num_event_link_lost), + item_addr(num_event_link_lost), 1}, + {"num_cmd_deauth", item_size(num_cmd_deauth), + item_addr(num_cmd_deauth), 1}, + {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), + item_addr(num_cmd_assoc_success), 1}, + {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), + item_addr(num_cmd_assoc_failure), 1}, + {"cmd_sent", item_size(cmd_sent), + item_addr(cmd_sent), 1}, + {"data_sent", item_size(data_sent), + item_addr(data_sent), 1}, + {"cmd_resp_received", item_size(cmd_resp_received), + item_addr(cmd_resp_received), 1}, + {"event_received", item_size(event_received), + item_addr(event_received), 1}, + + /* variables defined in struct mwifiex_adapter */ + {"cmd_pending", adapter_item_size(cmd_pending), + adapter_item_addr(cmd_pending), 1}, + {"tx_pending", adapter_item_size(tx_pending), + adapter_item_addr(tx_pending), 1}, + {"rx_pending", adapter_item_size(rx_pending), + adapter_item_addr(rx_pending), 1}, +}; + +static int num_of_items = ARRAY_SIZE(items); + +/* + * Firmware initialization complete callback handler. + * + * This function wakes up the function waiting on the init + * wait queue for the firmware initialization to complete. + */ +int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter) +{ + + if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) + if (adapter->if_ops.init_fw_port) + adapter->if_ops.init_fw_port(adapter); + + adapter->init_wait_q_woken = true; + wake_up_interruptible(&adapter->init_wait_q); + return 0; +} + +/* + * Firmware shutdown complete callback handler. + * + * This function sets the hardware status to not ready and wakes up + * the function waiting on the init wait queue for the firmware + * shutdown to complete. + */ +int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter) +{ + adapter->hw_status = MWIFIEX_HW_STATUS_NOT_READY; + adapter->init_wait_q_woken = true; + wake_up_interruptible(&adapter->init_wait_q); + return 0; +} + +/* + * This function sends init/shutdown command + * to firmware. + */ +int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, + u32 func_init_shutdown) +{ + u16 cmd; + + if (func_init_shutdown == MWIFIEX_FUNC_INIT) { + cmd = HostCmd_CMD_FUNC_INIT; + } else if (func_init_shutdown == MWIFIEX_FUNC_SHUTDOWN) { + cmd = HostCmd_CMD_FUNC_SHUTDOWN; + } else { + mwifiex_dbg(priv->adapter, ERROR, + "unsupported parameter\n"); + return -1; + } + + return mwifiex_send_cmd(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL, true); +} +EXPORT_SYMBOL_GPL(mwifiex_init_shutdown_fw); + +/* + * IOCTL request handler to set/get debug information. + * + * This function collates/sets the information from/to different driver + * structures. + */ +int mwifiex_get_debug_info(struct mwifiex_private *priv, + struct mwifiex_debug_info *info) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (info) { + info->debug_mask = adapter->debug_mask; + memcpy(info->packets_out, + priv->wmm.packets_out, + sizeof(priv->wmm.packets_out)); + info->curr_tx_buf_size = (u32) adapter->curr_tx_buf_size; + info->tx_buf_size = (u32) adapter->tx_buf_size; + info->rx_tbl_num = mwifiex_get_rx_reorder_tbl(priv, + info->rx_tbl); + info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl(priv, + info->tx_tbl); + info->tdls_peer_num = mwifiex_get_tdls_list(priv, + info->tdls_list); + info->ps_mode = adapter->ps_mode; + info->ps_state = adapter->ps_state; + info->is_deep_sleep = adapter->is_deep_sleep; + info->pm_wakeup_card_req = adapter->pm_wakeup_card_req; + info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try; + info->is_hs_configured = adapter->is_hs_configured; + info->hs_activated = adapter->hs_activated; + info->is_cmd_timedout = adapter->is_cmd_timedout; + info->num_cmd_host_to_card_failure + = adapter->dbg.num_cmd_host_to_card_failure; + info->num_cmd_sleep_cfm_host_to_card_failure + = adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; + info->num_tx_host_to_card_failure + = adapter->dbg.num_tx_host_to_card_failure; + info->num_event_deauth = adapter->dbg.num_event_deauth; + info->num_event_disassoc = adapter->dbg.num_event_disassoc; + info->num_event_link_lost = adapter->dbg.num_event_link_lost; + info->num_cmd_deauth = adapter->dbg.num_cmd_deauth; + info->num_cmd_assoc_success = + adapter->dbg.num_cmd_assoc_success; + info->num_cmd_assoc_failure = + adapter->dbg.num_cmd_assoc_failure; + info->num_tx_timeout = adapter->dbg.num_tx_timeout; + info->timeout_cmd_id = adapter->dbg.timeout_cmd_id; + info->timeout_cmd_act = adapter->dbg.timeout_cmd_act; + memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id, + sizeof(adapter->dbg.last_cmd_id)); + memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act, + sizeof(adapter->dbg.last_cmd_act)); + info->last_cmd_index = adapter->dbg.last_cmd_index; + memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id, + sizeof(adapter->dbg.last_cmd_resp_id)); + info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index; + memcpy(info->last_event, adapter->dbg.last_event, + sizeof(adapter->dbg.last_event)); + info->last_event_index = adapter->dbg.last_event_index; + info->data_sent = adapter->data_sent; + info->cmd_sent = adapter->cmd_sent; + info->cmd_resp_received = adapter->cmd_resp_received; + } + + return 0; +} + +int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, + struct mwifiex_debug_info *info) +{ + char *p = buf; + struct mwifiex_debug_data *d = &items[0]; + size_t size, addr; + long val; + int i, j; + + if (!info) + return 0; + + for (i = 0; i < num_of_items; i++) { + p += sprintf(p, "%s=", d[i].name); + + size = d[i].size / d[i].num; + + if (i < (num_of_items - 3)) + addr = d[i].addr + (size_t)info; + else /* The last 3 items are struct mwifiex_adapter variables */ + addr = d[i].addr + (size_t)priv->adapter; + + for (j = 0; j < d[i].num; j++) { + switch (size) { + case 1: + val = *((u8 *)addr); + break; + case 2: + val = *((u16 *)addr); + break; + case 4: + val = *((u32 *)addr); + break; + case 8: + val = *((long long *)addr); + break; + default: + val = -1; + break; + } + + p += sprintf(p, "%#lx ", val); + addr += size; + } + + p += sprintf(p, "\n"); + } + + if (info->tx_tbl_num) { + p += sprintf(p, "Tx BA stream table:\n"); + for (i = 0; i < info->tx_tbl_num; i++) + p += sprintf(p, "tid = %d, ra = %pM\n", + info->tx_tbl[i].tid, info->tx_tbl[i].ra); + } + + if (info->rx_tbl_num) { + p += sprintf(p, "Rx reorder table:\n"); + for (i = 0; i < info->rx_tbl_num; i++) { + p += sprintf(p, "tid = %d, ta = %pM, ", + info->rx_tbl[i].tid, + info->rx_tbl[i].ta); + p += sprintf(p, "start_win = %d, ", + info->rx_tbl[i].start_win); + p += sprintf(p, "win_size = %d, buffer: ", + info->rx_tbl[i].win_size); + + for (j = 0; j < info->rx_tbl[i].win_size; j++) + p += sprintf(p, "%c ", + info->rx_tbl[i].buffer[j] ? + '1' : '0'); + + p += sprintf(p, "\n"); + } + } + + if (info->tdls_peer_num) { + p += sprintf(p, "TDLS peer table:\n"); + for (i = 0; i < info->tdls_peer_num; i++) { + p += sprintf(p, "peer = %pM", + info->tdls_list[i].peer_addr); + p += sprintf(p, "\n"); + } + } + + return p - buf; +} + +static int +mwifiex_parse_mgmt_packet(struct mwifiex_private *priv, u8 *payload, u16 len, + struct rxpd *rx_pd) +{ + u16 stype; + u8 category, action_code, *addr2; + struct ieee80211_hdr *ieee_hdr = (void *)payload; + + stype = (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE); + + switch (stype) { + case IEEE80211_STYPE_ACTION: + category = *(payload + sizeof(struct ieee80211_hdr)); + switch (category) { + case WLAN_CATEGORY_PUBLIC: + action_code = *(payload + sizeof(struct ieee80211_hdr) + + 1); + if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) { + addr2 = ieee_hdr->addr2; + mwifiex_dbg(priv->adapter, INFO, + "TDLS discovery response %pM nf=%d, snr=%d\n", + addr2, rx_pd->nf, rx_pd->snr); + mwifiex_auto_tdls_update_peer_signal(priv, + addr2, + rx_pd->snr, + rx_pd->nf); + } + break; + case WLAN_CATEGORY_BACK: + /*we dont indicate BACK action frames to cfg80211*/ + mwifiex_dbg(priv->adapter, INFO, + "drop BACK action frames"); + return -1; + default: + mwifiex_dbg(priv->adapter, INFO, + "unknown public action frame category %d\n", + category); + } + default: + mwifiex_dbg(priv->adapter, INFO, + "unknown mgmt frame subtype %#x\n", stype); + return 0; + } + + return 0; +} +/* + * This function processes the received management packet and send it + * to the kernel. + */ +int +mwifiex_process_mgmt_packet(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct rxpd *rx_pd; + u16 pkt_len; + struct ieee80211_hdr *ieee_hdr; + + if (!skb) + return -1; + + if (!priv->mgmt_frame_mask || + priv->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) { + mwifiex_dbg(priv->adapter, ERROR, + "do not receive mgmt frames on uninitialized intf"); + return -1; + } + + rx_pd = (struct rxpd *)skb->data; + + skb_pull(skb, le16_to_cpu(rx_pd->rx_pkt_offset)); + skb_pull(skb, sizeof(pkt_len)); + + pkt_len = le16_to_cpu(rx_pd->rx_pkt_length); + + ieee_hdr = (void *)skb->data; + if (ieee80211_is_mgmt(ieee_hdr->frame_control)) { + if (mwifiex_parse_mgmt_packet(priv, (u8 *)ieee_hdr, + pkt_len, rx_pd)) + return -1; + } + /* Remove address4 */ + memmove(skb->data + sizeof(struct ieee80211_hdr_3addr), + skb->data + sizeof(struct ieee80211_hdr), + pkt_len - sizeof(struct ieee80211_hdr)); + + pkt_len -= ETH_ALEN + sizeof(pkt_len); + rx_pd->rx_pkt_length = cpu_to_le16(pkt_len); + + cfg80211_rx_mgmt(&priv->wdev, priv->roc_cfg.chan.center_freq, + CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len, + 0); + + return 0; +} + +/* + * This function processes the received packet before sending it to the + * kernel. + * + * It extracts the SKB from the received buffer and sends it to kernel. + * In case the received buffer does not contain the data in SKB format, + * the function creates a blank SKB, fills it with the data from the + * received buffer and then sends this new SKB to the kernel. + */ +int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb) +{ + struct mwifiex_sta_node *src_node; + struct ethhdr *p_ethhdr; + + if (!skb) + return -1; + + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + p_ethhdr = (void *)skb->data; + src_node = mwifiex_get_sta_entry(priv, p_ethhdr->h_source); + if (src_node) { + src_node->stats.last_rx = jiffies; + src_node->stats.rx_bytes += skb->len; + src_node->stats.rx_packets++; + } + } + + skb->dev = priv->netdev; + skb->protocol = eth_type_trans(skb, priv->netdev); + skb->ip_summed = CHECKSUM_NONE; + + /* This is required only in case of 11n and USB/PCIE as we alloc + * a buffer of 4K only if its 11N (to be able to receive 4K + * AMSDU packets). In case of SD we allocate buffers based + * on the size of packet and hence this is not needed. + * + * Modifying the truesize here as our allocation for each + * skb is 4K but we only receive 2K packets and this cause + * the kernel to start dropping packets in case where + * application has allocated buffer based on 2K size i.e. + * if there a 64K packet received (in IP fragments and + * application allocates 64K to receive this packet but + * this packet would almost double up because we allocate + * each 1.5K fragment in 4K and pass it up. As soon as the + * 64K limit hits kernel will start to drop rest of the + * fragments. Currently we fail the Filesndl-ht.scr script + * for UDP, hence this fix + */ + if ((priv->adapter->iface_type == MWIFIEX_USB || + priv->adapter->iface_type == MWIFIEX_PCIE) && + (skb->truesize > MWIFIEX_RX_DATA_BUF_SIZE)) + skb->truesize += (skb->len - MWIFIEX_RX_DATA_BUF_SIZE); + + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + + return 0; +} + +/* + * IOCTL completion callback handler. + * + * This function is called when a pending IOCTL is completed. + * + * If work queue support is enabled, the function wakes up the + * corresponding waiting function. Otherwise, it processes the + * IOCTL response and frees the response buffer. + */ +int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + WARN_ON(!cmd_node->wait_q_enabled); + mwifiex_dbg(adapter, CMD, "cmd completed: status=%d\n", + adapter->cmd_wait_q.status); + + *cmd_node->condition = true; + wake_up_interruptible(&adapter->cmd_wait_q.wait); + + return 0; +} + +/* This function will return the pointer to station entry in station list + * table which matches specified mac address. + * This function should be called after acquiring RA list spinlock. + * NULL is returned if station entry is not found in associated STA list. + */ +struct mwifiex_sta_node * +mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *node; + + if (!mac) + return NULL; + + list_for_each_entry(node, &priv->sta_list, list) { + if (!memcmp(node->mac_addr, mac, ETH_ALEN)) + return node; + } + + return NULL; +} + +static struct mwifiex_sta_node * +mwifiex_get_tdls_sta_entry(struct mwifiex_private *priv, u8 status) +{ + struct mwifiex_sta_node *node; + + list_for_each_entry(node, &priv->sta_list, list) { + if (node->tdls_status == status) + return node; + } + + return NULL; +} + +/* If tdls channel switching is on-going, tx data traffic should be + * blocked until the switching stage completed. + */ +u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *sta_ptr; + + if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return false; + + sta_ptr = mwifiex_get_tdls_sta_entry(priv, TDLS_CHAN_SWITCHING); + if (sta_ptr) + return true; + + return false; +} + +u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *sta_ptr; + + if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return false; + + sta_ptr = mwifiex_get_tdls_sta_entry(priv, TDLS_IN_OFF_CHAN); + if (sta_ptr) + return true; + + return false; +} + +/* If tdls channel switching is on-going or tdls operate on off-channel, + * cmd path should be blocked until tdls switched to base-channel. + */ +u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv) +{ + if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return true; + + if (mwifiex_is_tdls_chan_switching(priv) || + mwifiex_is_tdls_off_chan(priv)) + return false; + + return true; +} + +/* This function will add a sta_node entry to associated station list + * table with the given mac address. + * If entry exist already, existing entry is returned. + * If received mac address is NULL, NULL is returned. + */ +struct mwifiex_sta_node * +mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *node; + unsigned long flags; + + if (!mac) + return NULL; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + node = mwifiex_get_sta_entry(priv, mac); + if (node) + goto done; + + node = kzalloc(sizeof(*node), GFP_ATOMIC); + if (!node) + goto done; + + memcpy(node->mac_addr, mac, ETH_ALEN); + list_add_tail(&node->list, &priv->sta_list); + +done: + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return node; +} + +/* This function will search for HT IE in association request IEs + * and set station HT parameters accordingly. + */ +void +mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, + int ies_len, struct mwifiex_sta_node *node) +{ + struct ieee_types_header *ht_cap_ie; + const struct ieee80211_ht_cap *ht_cap; + + if (!ies) + return; + + ht_cap_ie = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, + ies_len); + if (ht_cap_ie) { + ht_cap = (void *)(ht_cap_ie + 1); + node->is_11n_enabled = 1; + node->max_amsdu = le16_to_cpu(ht_cap->cap_info) & + IEEE80211_HT_CAP_MAX_AMSDU ? + MWIFIEX_TX_DATA_BUF_SIZE_8K : + MWIFIEX_TX_DATA_BUF_SIZE_4K; + } else { + node->is_11n_enabled = 0; + } + + return; +} + +/* This function will delete a station entry from station list */ +void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *node; + unsigned long flags; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + + node = mwifiex_get_sta_entry(priv, mac); + if (node) { + list_del(&node->list); + kfree(node); + } + + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return; +} + +/* This function will delete all stations from associated station list. */ +void mwifiex_del_all_sta_list(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *node, *tmp; + unsigned long flags; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + + list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { + list_del(&node->list); + kfree(node); + } + + INIT_LIST_HEAD(&priv->sta_list); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return; +} + +/* This function adds histogram data to histogram array*/ +void mwifiex_hist_data_add(struct mwifiex_private *priv, + u8 rx_rate, s8 snr, s8 nflr) +{ + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + if (atomic_read(&phist_data->num_samples) > MWIFIEX_HIST_MAX_SAMPLES) + mwifiex_hist_data_reset(priv); + mwifiex_hist_data_set(priv, rx_rate, snr, nflr); +} + +/* function to add histogram record */ +void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, + s8 nflr) +{ + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + atomic_inc(&phist_data->num_samples); + atomic_inc(&phist_data->rx_rate[rx_rate]); + atomic_inc(&phist_data->snr[snr]); + atomic_inc(&phist_data->noise_flr[128 + nflr]); + atomic_inc(&phist_data->sig_str[nflr - snr]); +} + +/* function to reset histogram data during init/reset */ +void mwifiex_hist_data_reset(struct mwifiex_private *priv) +{ + int ix; + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + atomic_set(&phist_data->num_samples, 0); + for (ix = 0; ix < MWIFIEX_MAX_AC_RX_RATES; ix++) + atomic_set(&phist_data->rx_rate[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_SNR; ix++) + atomic_set(&phist_data->snr[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_NOISE_FLR; ix++) + atomic_set(&phist_data->noise_flr[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_SIG_STRENGTH; ix++) + atomic_set(&phist_data->sig_str[ix], 0); +} + +void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags) +{ + struct sk_buff *skb; + int buf_len, pad; + + buf_len = rx_len + MWIFIEX_RX_HEADROOM + MWIFIEX_DMA_ALIGN_SZ; + + skb = __dev_alloc_skb(buf_len, flags); + + if (!skb) + return NULL; + + skb_reserve(skb, MWIFIEX_RX_HEADROOM); + + pad = MWIFIEX_ALIGN_ADDR(skb->data, MWIFIEX_DMA_ALIGN_SZ) - + (long)skb->data; + + skb_reserve(skb, pad); + + return skb; +} +EXPORT_SYMBOL_GPL(mwifiex_alloc_dma_align_buf); diff --git a/drivers/net/wireless/marvell/mwifiex/util.h b/drivers/net/wireless/marvell/mwifiex/util.h new file mode 100644 index 000000000000..b541d66c01eb --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/util.h @@ -0,0 +1,96 @@ +/* + * Marvell Wireless LAN device driver: utility functions + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_UTIL_H_ +#define _MWIFIEX_UTIL_H_ + +struct mwifiex_private; + +struct mwifiex_dma_mapping { + dma_addr_t addr; + size_t len; +}; + +struct mwifiex_cb { + struct mwifiex_dma_mapping dma_mapping; + union { + struct mwifiex_rxinfo rx_info; + struct mwifiex_txinfo tx_info; + }; +}; + +/* size/addr for mwifiex_debug_info */ +#define item_size(n) (FIELD_SIZEOF(struct mwifiex_debug_info, n)) +#define item_addr(n) (offsetof(struct mwifiex_debug_info, n)) + +/* size/addr for struct mwifiex_adapter */ +#define adapter_item_size(n) (FIELD_SIZEOF(struct mwifiex_adapter, n)) +#define adapter_item_addr(n) (offsetof(struct mwifiex_adapter, n)) + +struct mwifiex_debug_data { + char name[32]; /* variable/array name */ + u32 size; /* size of the variable/array */ + size_t addr; /* address of the variable/array */ + int num; /* number of variables in an array */ +}; + +static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + BUILD_BUG_ON(sizeof(struct mwifiex_cb) > sizeof(skb->cb)); + return &cb->rx_info; +} + +static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + return &cb->tx_info; +} + +static inline void mwifiex_store_mapping(struct sk_buff *skb, + struct mwifiex_dma_mapping *mapping) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + memcpy(&cb->dma_mapping, mapping, sizeof(*mapping)); +} + +static inline void mwifiex_get_mapping(struct sk_buff *skb, + struct mwifiex_dma_mapping *mapping) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + memcpy(mapping, &cb->dma_mapping, sizeof(*mapping)); +} + +static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb) +{ + struct mwifiex_dma_mapping mapping; + + mwifiex_get_mapping(skb, &mapping); + + return mapping.addr; +} + +int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, + struct mwifiex_debug_info *info); + +#endif /* !_MWIFIEX_UTIL_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c new file mode 100644 index 000000000000..acccd6734e3b --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/wmm.c @@ -0,0 +1,1531 @@ +/* + * Marvell Wireless LAN device driver: WMM + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" + + +/* Maximum value FW can accept for driver delay in packet transmission */ +#define DRV_PKT_DELAY_TO_FW_MAX 512 + + +#define WMM_QUEUED_PACKET_LOWER_LIMIT 180 + +#define WMM_QUEUED_PACKET_UPPER_LIMIT 200 + +/* Offset for TOS field in the IP header */ +#define IPTOS_OFFSET 5 + +static bool disable_tx_amsdu; +module_param(disable_tx_amsdu, bool, 0644); + +/* WMM information IE */ +static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07, + 0x00, 0x50, 0xf2, 0x02, + 0x00, 0x01, 0x00 +}; + +static const u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE, + WMM_AC_BK, + WMM_AC_VI, + WMM_AC_VO +}; + +static u8 tos_to_tid[] = { + /* TID DSCP_P2 DSCP_P1 DSCP_P0 WMM_AC */ + 0x01, /* 0 1 0 AC_BK */ + 0x02, /* 0 0 0 AC_BK */ + 0x00, /* 0 0 1 AC_BE */ + 0x03, /* 0 1 1 AC_BE */ + 0x04, /* 1 0 0 AC_VI */ + 0x05, /* 1 0 1 AC_VI */ + 0x06, /* 1 1 0 AC_VO */ + 0x07 /* 1 1 1 AC_VO */ +}; + +static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; + +/* + * This function debug prints the priority parameters for a WMM AC. + */ +static void +mwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param) +{ + const char *ac_str[] = { "BK", "BE", "VI", "VO" }; + + pr_debug("info: WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, " + "EcwMin=%d, EcwMax=%d, TxopLimit=%d\n", + ac_str[wmm_aci_to_qidx_map[(ac_param->aci_aifsn_bitmap + & MWIFIEX_ACI) >> 5]], + (ac_param->aci_aifsn_bitmap & MWIFIEX_ACI) >> 5, + (ac_param->aci_aifsn_bitmap & MWIFIEX_ACM) >> 4, + ac_param->aci_aifsn_bitmap & MWIFIEX_AIFSN, + ac_param->ecw_bitmap & MWIFIEX_ECW_MIN, + (ac_param->ecw_bitmap & MWIFIEX_ECW_MAX) >> 4, + le16_to_cpu(ac_param->tx_op_limit)); +} + +/* + * This function allocates a route address list. + * + * The function also initializes the list with the provided RA. + */ +static struct mwifiex_ra_list_tbl * +mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra) +{ + struct mwifiex_ra_list_tbl *ra_list; + + ra_list = kzalloc(sizeof(struct mwifiex_ra_list_tbl), GFP_ATOMIC); + if (!ra_list) + return NULL; + + INIT_LIST_HEAD(&ra_list->list); + skb_queue_head_init(&ra_list->skb_head); + + memcpy(ra_list->ra, ra, ETH_ALEN); + + ra_list->total_pkt_count = 0; + + mwifiex_dbg(adapter, INFO, "info: allocated ra_list %p\n", ra_list); + + return ra_list; +} + +/* This function returns random no between 16 and 32 to be used as threshold + * for no of packets after which BA setup is initiated. + */ +static u8 mwifiex_get_random_ba_threshold(void) +{ + u64 ns; + /* setup ba_packet_threshold here random number between + * [BA_SETUP_PACKET_OFFSET, + * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1] + */ + ns = ktime_get_ns(); + ns += (ns >> 32) + (ns >> 16); + + return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET; +} + +/* + * This function allocates and adds a RA list for all TIDs + * with the given RA. + */ +void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) +{ + int i; + struct mwifiex_ra_list_tbl *ra_list; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_sta_node *node; + unsigned long flags; + + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = mwifiex_wmm_allocate_ralist_node(adapter, ra); + mwifiex_dbg(adapter, INFO, + "info: created ra_list %p\n", ra_list); + + if (!ra_list) + break; + + ra_list->is_11n_enabled = 0; + ra_list->tdls_link = false; + ra_list->ba_status = BA_SETUP_NONE; + ra_list->amsdu_in_ampdu = false; + if (!mwifiex_queuing_ra_based(priv)) { + if (mwifiex_is_tdls_link_setup + (mwifiex_get_tdls_link_status(priv, ra))) { + ra_list->tdls_link = true; + ra_list->is_11n_enabled = + mwifiex_tdls_peer_11n_enabled(priv, ra); + } else { + ra_list->is_11n_enabled = IS_11N_ENABLED(priv); + } + } else { + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + node = mwifiex_get_sta_entry(priv, ra); + if (node) + ra_list->tx_paused = node->tx_pause; + ra_list->is_11n_enabled = + mwifiex_is_sta_11n_enabled(priv, node); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu = node->max_amsdu; + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + } + + mwifiex_dbg(adapter, DATA, "data: ralist %p: is_11n_enabled=%d\n", + ra_list, ra_list->is_11n_enabled); + + if (ra_list->is_11n_enabled) { + ra_list->ba_pkt_count = 0; + ra_list->ba_packet_thr = + mwifiex_get_random_ba_threshold(); + } + list_add_tail(&ra_list->list, + &priv->wmm.tid_tbl_ptr[i].ra_list); + } +} + +/* + * This function sets the WMM queue priorities to their default values. + */ +static void mwifiex_wmm_default_queue_priorities(struct mwifiex_private *priv) +{ + /* Default queue priorities: VO->VI->BE->BK */ + priv->wmm.queue_priority[0] = WMM_AC_VO; + priv->wmm.queue_priority[1] = WMM_AC_VI; + priv->wmm.queue_priority[2] = WMM_AC_BE; + priv->wmm.queue_priority[3] = WMM_AC_BK; +} + +/* + * This function map ACs to TIDs. + */ +static void +mwifiex_wmm_queue_priorities_tid(struct mwifiex_private *priv) +{ + struct mwifiex_wmm_desc *wmm = &priv->wmm; + u8 *queue_priority = wmm->queue_priority; + int i; + + for (i = 0; i < 4; ++i) { + tos_to_tid[7 - (i * 2)] = ac_to_tid[queue_priority[i]][1]; + tos_to_tid[6 - (i * 2)] = ac_to_tid[queue_priority[i]][0]; + } + + for (i = 0; i < MAX_NUM_TID; ++i) + priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i; + + atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID); +} + +/* + * This function initializes WMM priority queues. + */ +void +mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, + struct ieee_types_wmm_parameter *wmm_ie) +{ + u16 cw_min, avg_back_off, tmp[4]; + u32 i, j, num_ac; + u8 ac_idx; + + if (!wmm_ie || !priv->wmm_enabled) { + /* WMM is not enabled, just set the defaults and return */ + mwifiex_wmm_default_queue_priorities(priv); + return; + } + + mwifiex_dbg(priv->adapter, INFO, + "info: WMM Parameter IE: version=%d,\t" + "qos_info Parameter Set Count=%d, Reserved=%#x\n", + wmm_ie->vend_hdr.version, wmm_ie->qos_info_bitmap & + IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK, + wmm_ie->reserved); + + for (num_ac = 0; num_ac < ARRAY_SIZE(wmm_ie->ac_params); num_ac++) { + u8 ecw = wmm_ie->ac_params[num_ac].ecw_bitmap; + u8 aci_aifsn = wmm_ie->ac_params[num_ac].aci_aifsn_bitmap; + cw_min = (1 << (ecw & MWIFIEX_ECW_MIN)) - 1; + avg_back_off = (cw_min >> 1) + (aci_aifsn & MWIFIEX_AIFSN); + + ac_idx = wmm_aci_to_qidx_map[(aci_aifsn & MWIFIEX_ACI) >> 5]; + priv->wmm.queue_priority[ac_idx] = ac_idx; + tmp[ac_idx] = avg_back_off; + + mwifiex_dbg(priv->adapter, INFO, + "info: WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n", + (1 << ((ecw & MWIFIEX_ECW_MAX) >> 4)) - 1, + cw_min, avg_back_off); + mwifiex_wmm_ac_debug_print(&wmm_ie->ac_params[num_ac]); + } + + /* Bubble sort */ + for (i = 0; i < num_ac; i++) { + for (j = 1; j < num_ac - i; j++) { + if (tmp[j - 1] > tmp[j]) { + swap(tmp[j - 1], tmp[j]); + swap(priv->wmm.queue_priority[j - 1], + priv->wmm.queue_priority[j]); + } else if (tmp[j - 1] == tmp[j]) { + if (priv->wmm.queue_priority[j - 1] + < priv->wmm.queue_priority[j]) + swap(priv->wmm.queue_priority[j - 1], + priv->wmm.queue_priority[j]); + } + } + } + + mwifiex_wmm_queue_priorities_tid(priv); +} + +/* + * This function evaluates whether or not an AC is to be downgraded. + * + * In case the AC is not enabled, the highest AC is returned that is + * enabled and does not require admission control. + */ +static enum mwifiex_wmm_ac_e +mwifiex_wmm_eval_downgrade_ac(struct mwifiex_private *priv, + enum mwifiex_wmm_ac_e eval_ac) +{ + int down_ac; + enum mwifiex_wmm_ac_e ret_ac; + struct mwifiex_wmm_ac_status *ac_status; + + ac_status = &priv->wmm.ac_status[eval_ac]; + + if (!ac_status->disabled) + /* Okay to use this AC, its enabled */ + return eval_ac; + + /* Setup a default return value of the lowest priority */ + ret_ac = WMM_AC_BK; + + /* + * Find the highest AC that is enabled and does not require + * admission control. The spec disallows downgrading to an AC, + * which is enabled due to a completed admission control. + * Unadmitted traffic is not to be sent on an AC with admitted + * traffic. + */ + for (down_ac = WMM_AC_BK; down_ac < eval_ac; down_ac++) { + ac_status = &priv->wmm.ac_status[down_ac]; + + if (!ac_status->disabled && !ac_status->flow_required) + /* AC is enabled and does not require admission + control */ + ret_ac = (enum mwifiex_wmm_ac_e) down_ac; + } + + return ret_ac; +} + +/* + * This function downgrades WMM priority queue. + */ +void +mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv) +{ + int ac_val; + + mwifiex_dbg(priv->adapter, INFO, "info: WMM: AC Priorities:\t" + "BK(0), BE(1), VI(2), VO(3)\n"); + + if (!priv->wmm_enabled) { + /* WMM is not enabled, default priorities */ + for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) + priv->wmm.ac_down_graded_vals[ac_val] = + (enum mwifiex_wmm_ac_e) ac_val; + } else { + for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) { + priv->wmm.ac_down_graded_vals[ac_val] + = mwifiex_wmm_eval_downgrade_ac(priv, + (enum mwifiex_wmm_ac_e) ac_val); + mwifiex_dbg(priv->adapter, INFO, + "info: WMM: AC PRIO %d maps to %d\n", + ac_val, + priv->wmm.ac_down_graded_vals[ac_val]); + } + } +} + +/* + * This function converts the IP TOS field to an WMM AC + * Queue assignment. + */ +static enum mwifiex_wmm_ac_e +mwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos) +{ + /* Map of TOS UP values to WMM AC */ + const enum mwifiex_wmm_ac_e tos_to_ac[] = { WMM_AC_BE, + WMM_AC_BK, + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VI, + WMM_AC_VO, + WMM_AC_VO + }; + + if (tos >= ARRAY_SIZE(tos_to_ac)) + return WMM_AC_BE; + + return tos_to_ac[tos]; +} + +/* + * This function evaluates a given TID and downgrades it to a lower + * TID if the WMM Parameter IE received from the AP indicates that the + * AP is disabled (due to call admission control (ACM bit). Mapping + * of TID to AC is taken care of internally. + */ +u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) +{ + enum mwifiex_wmm_ac_e ac, ac_down; + u8 new_tid; + + ac = mwifiex_wmm_convert_tos_to_ac(priv->adapter, tid); + ac_down = priv->wmm.ac_down_graded_vals[ac]; + + /* Send the index to tid array, picking from the array will be + * taken care by dequeuing function + */ + new_tid = ac_to_tid[ac_down][tid % 2]; + + return new_tid; +} + +/* + * This function initializes the WMM state information and the + * WMM data path queues. + */ +void +mwifiex_wmm_init(struct mwifiex_adapter *adapter) +{ + int i, j; + struct mwifiex_private *priv; + + for (j = 0; j < adapter->priv_num; ++j) { + priv = adapter->priv[j]; + if (!priv) + continue; + + for (i = 0; i < MAX_NUM_TID; ++i) { + if (!disable_tx_amsdu && + adapter->tx_buf_size > MWIFIEX_TX_DATA_BUF_SIZE_2K) + priv->aggr_prio_tbl[i].amsdu = + priv->tos_to_tid_inv[i]; + else + priv->aggr_prio_tbl[i].amsdu = + BA_STREAM_NOT_ALLOWED; + priv->aggr_prio_tbl[i].ampdu_ap = + priv->tos_to_tid_inv[i]; + priv->aggr_prio_tbl[i].ampdu_user = + priv->tos_to_tid_inv[i]; + } + + priv->aggr_prio_tbl[6].amsdu + = priv->aggr_prio_tbl[6].ampdu_ap + = priv->aggr_prio_tbl[6].ampdu_user + = BA_STREAM_NOT_ALLOWED; + + priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap + = priv->aggr_prio_tbl[7].ampdu_user + = BA_STREAM_NOT_ALLOWED; + + mwifiex_set_ba_params(priv); + mwifiex_reset_11n_rx_seq_num(priv); + + atomic_set(&priv->wmm.tx_pkts_queued, 0); + atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); + } +} + +int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + continue; + if (!skb_queue_empty(&priv->bypass_txq)) + return false; + } + + return true; +} + +/* + * This function checks if WMM Tx queue is empty. + */ +int +mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter) +{ + int i; + struct mwifiex_private *priv; + + for (i = 0; i < adapter->priv_num; ++i) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (!priv->port_open) + continue; + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + continue; + if (atomic_read(&priv->wmm.tx_pkts_queued)) + return false; + } + + return true; +} + +/* + * This function deletes all packets in an RA list node. + * + * The packet sent completion callback handler are called with + * status failure, after they are dequeued to ensure proper + * cleanup. The RA list node itself is freed at the end. + */ +static void +mwifiex_wmm_del_pkts_in_ralist_node(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ra_list) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct sk_buff *skb, *tmp; + + skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) + mwifiex_write_data_complete(adapter, skb, 0, -1); +} + +/* + * This function deletes all packets in an RA list. + * + * Each nodes in the RA list are freed individually first, and then + * the RA list itself is freed. + */ +static void +mwifiex_wmm_del_pkts_in_ralist(struct mwifiex_private *priv, + struct list_head *ra_list_head) +{ + struct mwifiex_ra_list_tbl *ra_list; + + list_for_each_entry(ra_list, ra_list_head, list) + mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); +} + +/* + * This function deletes all packets in all RA lists. + */ +static void mwifiex_wmm_cleanup_queues(struct mwifiex_private *priv) +{ + int i; + + for (i = 0; i < MAX_NUM_TID; i++) + mwifiex_wmm_del_pkts_in_ralist(priv, &priv->wmm.tid_tbl_ptr[i]. + ra_list); + + atomic_set(&priv->wmm.tx_pkts_queued, 0); + atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); +} + +/* + * This function deletes all route addresses from all RA lists. + */ +static void mwifiex_wmm_delete_all_ralist(struct mwifiex_private *priv) +{ + struct mwifiex_ra_list_tbl *ra_list, *tmp_node; + int i; + + for (i = 0; i < MAX_NUM_TID; ++i) { + mwifiex_dbg(priv->adapter, INFO, + "info: ra_list: freeing buf for tid %d\n", i); + list_for_each_entry_safe(ra_list, tmp_node, + &priv->wmm.tid_tbl_ptr[i].ra_list, + list) { + list_del(&ra_list->list); + kfree(ra_list); + } + + INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[i].ra_list); + } +} + +static int mwifiex_free_ack_frame(int id, void *p, void *data) +{ + pr_warn("Have pending ack frames!\n"); + kfree_skb(p); + return 0; +} + +/* + * This function cleans up the Tx and Rx queues. + * + * Cleanup includes - + * - All packets in RA lists + * - All entries in Rx reorder table + * - All entries in Tx BA stream table + * - MPA buffer (if required) + * - All RA lists + */ +void +mwifiex_clean_txrx(struct mwifiex_private *priv) +{ + unsigned long flags; + struct sk_buff *skb, *tmp; + + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + mwifiex_wmm_cleanup_queues(priv); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + + if (priv->adapter->if_ops.cleanup_mpa_buf) + priv->adapter->if_ops.cleanup_mpa_buf(priv->adapter); + + mwifiex_wmm_delete_all_ralist(priv); + memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); + + if (priv->adapter->if_ops.clean_pcie_ring && + !priv->adapter->surprise_removed) + priv->adapter->if_ops.clean_pcie_ring(priv->adapter); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + + skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + + skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + atomic_set(&priv->adapter->bypass_tx_pending, 0); + + idr_for_each(&priv->ack_status_frames, mwifiex_free_ack_frame, NULL); + idr_destroy(&priv->ack_status_frames); +} + +/* + * This function retrieves a particular RA list node, matching with the + * given TID and RA address. + */ +struct mwifiex_ra_list_tbl * +mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr) +{ + struct mwifiex_ra_list_tbl *ra_list; + + list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[tid].ra_list, + list) { + if (!memcmp(ra_list->ra, ra_addr, ETH_ALEN)) + return ra_list; + } + + return NULL; +} + +void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, + u8 tx_pause) +{ + struct mwifiex_ra_list_tbl *ra_list; + u32 pkt_cnt = 0, tx_pkts_queued; + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = mwifiex_wmm_get_ralist_node(priv, i, mac); + if (ra_list && ra_list->tx_paused != tx_pause) { + pkt_cnt += ra_list->total_pkt_count; + ra_list->tx_paused = tx_pause; + if (tx_pause) + priv->wmm.pkts_paused[i] += + ra_list->total_pkt_count; + else + priv->wmm.pkts_paused[i] -= + ra_list->total_pkt_count; + } + } + + if (pkt_cnt) { + tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued); + if (tx_pause) + tx_pkts_queued -= pkt_cnt; + else + tx_pkts_queued += pkt_cnt; + + atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); + atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); + } + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + +/* This function update non-tdls peer ralist tx_pause while + * tdls channel swithing + */ +void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, + u8 *mac, u8 tx_pause) +{ + struct mwifiex_ra_list_tbl *ra_list; + u32 pkt_cnt = 0, tx_pkts_queued; + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; ++i) { + list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[i].ra_list, + list) { + if (!memcmp(ra_list->ra, mac, ETH_ALEN)) + continue; + + if (ra_list->tx_paused != tx_pause) { + pkt_cnt += ra_list->total_pkt_count; + ra_list->tx_paused = tx_pause; + if (tx_pause) + priv->wmm.pkts_paused[i] += + ra_list->total_pkt_count; + else + priv->wmm.pkts_paused[i] -= + ra_list->total_pkt_count; + } + } + } + + if (pkt_cnt) { + tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued); + if (tx_pause) + tx_pkts_queued -= pkt_cnt; + else + tx_pkts_queued += pkt_cnt; + + atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); + atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); + } + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + +/* + * This function retrieves an RA list node for a given TID and + * RA address pair. + * + * If no such node is found, a new node is added first and then + * retrieved. + */ +struct mwifiex_ra_list_tbl * +mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr) +{ + struct mwifiex_ra_list_tbl *ra_list; + + ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); + if (ra_list) + return ra_list; + mwifiex_ralist_add(priv, ra_addr); + + return mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); +} + +/* + * This function deletes RA list nodes for given mac for all TIDs. + * Function also decrements TX pending count accordingly. + */ +void +mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr) +{ + struct mwifiex_ra_list_tbl *ra_list; + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = mwifiex_wmm_get_ralist_node(priv, i, ra_addr); + + if (!ra_list) + continue; + mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); + if (ra_list->tx_paused) + priv->wmm.pkts_paused[i] -= ra_list->total_pkt_count; + else + atomic_sub(ra_list->total_pkt_count, + &priv->wmm.tx_pkts_queued); + list_del(&ra_list->list); + kfree(ra_list); + } + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + +/* + * This function checks if a particular RA list node exists in a given TID + * table index. + */ +int +mwifiex_is_ralist_valid(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ra_list, int ptr_index) +{ + struct mwifiex_ra_list_tbl *rlist; + + list_for_each_entry(rlist, &priv->wmm.tid_tbl_ptr[ptr_index].ra_list, + list) { + if (rlist == ra_list) + return true; + } + + return false; +} + +/* + * This function adds a packet to bypass TX queue. + * This is special TX queue for packets which can be sent even when port_open + * is false. + */ +void +mwifiex_wmm_add_buf_bypass_txqueue(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + skb_queue_tail(&priv->bypass_txq, skb); +} + +/* + * This function adds a packet to WMM queue. + * + * In disconnected state the packet is immediately dropped and the + * packet send completion callback is called with status failure. + * + * Otherwise, the correct RA list node is located and the packet + * is queued at the list tail. + */ +void +mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u32 tid; + struct mwifiex_ra_list_tbl *ra_list; + u8 ra[ETH_ALEN], tid_down; + unsigned long flags; + struct list_head list_head; + int tdls_status = TDLS_NOT_SETUP; + struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + + memcpy(ra, eth_hdr->h_dest, ETH_ALEN); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && + ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) { + if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS) + mwifiex_dbg(adapter, DATA, + "TDLS setup packet for %pM.\t" + "Don't block\n", ra); + else if (memcmp(priv->cfg_bssid, ra, ETH_ALEN)) + tdls_status = mwifiex_get_tdls_link_status(priv, ra); + } + + if (!priv->media_connected && !mwifiex_is_skb_mgmt_frame(skb)) { + mwifiex_dbg(adapter, DATA, "data: drop packet in disconnect\n"); + mwifiex_write_data_complete(adapter, skb, 0, -1); + return; + } + + tid = skb->priority; + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + tid_down = mwifiex_wmm_downgrade_tid(priv, tid); + + /* In case of infra as we have already created the list during + association we just don't have to call get_queue_raptr, we will + have only 1 raptr for a tid in case of infra */ + if (!mwifiex_queuing_ra_based(priv) && + !mwifiex_is_skb_mgmt_frame(skb)) { + switch (tdls_status) { + case TDLS_SETUP_COMPLETE: + case TDLS_CHAN_SWITCHING: + case TDLS_IN_BASE_CHAN: + case TDLS_IN_OFF_CHAN: + ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, + ra); + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + break; + case TDLS_SETUP_INPROGRESS: + skb_queue_tail(&priv->tdls_txq, skb); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + return; + default: + list_head = priv->wmm.tid_tbl_ptr[tid_down].ra_list; + if (!list_empty(&list_head)) + ra_list = list_first_entry( + &list_head, struct mwifiex_ra_list_tbl, + list); + else + ra_list = NULL; + break; + } + } else { + memcpy(ra, skb->data, ETH_ALEN); + if (ra[0] & 0x01 || mwifiex_is_skb_mgmt_frame(skb)) + eth_broadcast_addr(ra); + ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, ra); + } + + if (!ra_list) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + mwifiex_write_data_complete(adapter, skb, 0, -1); + return; + } + + skb_queue_tail(&ra_list->skb_head, skb); + + ra_list->ba_pkt_count++; + ra_list->total_pkt_count++; + + if (atomic_read(&priv->wmm.highest_queued_prio) < + priv->tos_to_tid_inv[tid_down]) + atomic_set(&priv->wmm.highest_queued_prio, + priv->tos_to_tid_inv[tid_down]); + + if (ra_list->tx_paused) + priv->wmm.pkts_paused[tid_down]++; + else + atomic_inc(&priv->wmm.tx_pkts_queued); + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + +/* + * This function processes the get WMM status command response from firmware. + * + * The response may contain multiple TLVs - + * - AC Queue status TLVs + * - Current WMM Parameter IE TLV + * - Admission Control action frame TLVs + * + * This function parses the TLVs and then calls further specific functions + * to process any changes in the queue prioritize or state. + */ +int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, + const struct host_cmd_ds_command *resp) +{ + u8 *curr = (u8 *) &resp->params.get_wmm_status; + uint16_t resp_len = le16_to_cpu(resp->size), tlv_len; + int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; + bool valid = true; + + struct mwifiex_ie_types_data *tlv_hdr; + struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus; + struct ieee_types_wmm_parameter *wmm_param_ie = NULL; + struct mwifiex_wmm_ac_status *ac_status; + + mwifiex_dbg(priv->adapter, INFO, + "info: WMM: WMM_GET_STATUS cmdresp received: %d\n", + resp_len); + + while ((resp_len >= sizeof(tlv_hdr->header)) && valid) { + tlv_hdr = (struct mwifiex_ie_types_data *) curr; + tlv_len = le16_to_cpu(tlv_hdr->header.len); + + if (resp_len < tlv_len + sizeof(tlv_hdr->header)) + break; + + switch (le16_to_cpu(tlv_hdr->header.type)) { + case TLV_TYPE_WMMQSTATUS: + tlv_wmm_qstatus = + (struct mwifiex_ie_types_wmm_queue_status *) + tlv_hdr; + mwifiex_dbg(priv->adapter, CMD, + "info: CMD_RESP: WMM_GET_STATUS:\t" + "QSTATUS TLV: %d, %d, %d\n", + tlv_wmm_qstatus->queue_index, + tlv_wmm_qstatus->flow_required, + tlv_wmm_qstatus->disabled); + + ac_status = &priv->wmm.ac_status[tlv_wmm_qstatus-> + queue_index]; + ac_status->disabled = tlv_wmm_qstatus->disabled; + ac_status->flow_required = + tlv_wmm_qstatus->flow_required; + ac_status->flow_created = tlv_wmm_qstatus->flow_created; + break; + + case WLAN_EID_VENDOR_SPECIFIC: + /* + * Point the regular IEEE IE 2 bytes into the Marvell IE + * and setup the IEEE IE type and length byte fields + */ + + wmm_param_ie = + (struct ieee_types_wmm_parameter *) (curr + + 2); + wmm_param_ie->vend_hdr.len = (u8) tlv_len; + wmm_param_ie->vend_hdr.element_id = + WLAN_EID_VENDOR_SPECIFIC; + + mwifiex_dbg(priv->adapter, CMD, + "info: CMD_RESP: WMM_GET_STATUS:\t" + "WMM Parameter Set Count: %d\n", + wmm_param_ie->qos_info_bitmap & mask); + + memcpy((u8 *) &priv->curr_bss_params.bss_descriptor. + wmm_ie, wmm_param_ie, + wmm_param_ie->vend_hdr.len + 2); + + break; + + default: + valid = false; + break; + } + + curr += (tlv_len + sizeof(tlv_hdr->header)); + resp_len -= (tlv_len + sizeof(tlv_hdr->header)); + } + + mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); + mwifiex_wmm_setup_ac_downgrade(priv); + + return 0; +} + +/* + * Callback handler from the command module to allow insertion of a WMM TLV. + * + * If the BSS we are associating to supports WMM, this function adds the + * required WMM Information IE to the association request command buffer in + * the form of a Marvell extended IEEE IE. + */ +u32 +mwifiex_wmm_process_association_req(struct mwifiex_private *priv, + u8 **assoc_buf, + struct ieee_types_wmm_parameter *wmm_ie, + struct ieee80211_ht_cap *ht_cap) +{ + struct mwifiex_ie_types_wmm_param_set *wmm_tlv; + u32 ret_len = 0; + + /* Null checks */ + if (!assoc_buf) + return 0; + if (!(*assoc_buf)) + return 0; + + if (!wmm_ie) + return 0; + + mwifiex_dbg(priv->adapter, INFO, + "info: WMM: process assoc req: bss->wmm_ie=%#x\n", + wmm_ie->vend_hdr.element_id); + + if ((priv->wmm_required || + (ht_cap && (priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN))) && + wmm_ie->vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) { + wmm_tlv = (struct mwifiex_ie_types_wmm_param_set *) *assoc_buf; + wmm_tlv->header.type = cpu_to_le16((u16) wmm_info_ie[0]); + wmm_tlv->header.len = cpu_to_le16((u16) wmm_info_ie[1]); + memcpy(wmm_tlv->wmm_ie, &wmm_info_ie[2], + le16_to_cpu(wmm_tlv->header.len)); + if (wmm_ie->qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) + memcpy((u8 *) (wmm_tlv->wmm_ie + + le16_to_cpu(wmm_tlv->header.len) + - sizeof(priv->wmm_qosinfo)), + &priv->wmm_qosinfo, sizeof(priv->wmm_qosinfo)); + + ret_len = sizeof(wmm_tlv->header) + + le16_to_cpu(wmm_tlv->header.len); + + *assoc_buf += ret_len; + } + + return ret_len; +} + +/* + * This function computes the time delay in the driver queues for a + * given packet. + * + * When the packet is received at the OS/Driver interface, the current + * time is set in the packet structure. The difference between the present + * time and that received time is computed in this function and limited + * based on pre-compiled limits in the driver. + */ +u8 +mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, + const struct sk_buff *skb) +{ + u32 queue_delay = ktime_to_ms(net_timedelta(skb->tstamp)); + u8 ret_val; + + /* + * Queue delay is passed as a uint8 in units of 2ms (ms shifted + * by 1). Min value (other than 0) is therefore 2ms, max is 510ms. + * + * Pass max value if queue_delay is beyond the uint8 range + */ + ret_val = (u8) (min(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1); + + mwifiex_dbg(priv->adapter, DATA, "data: WMM: Pkt Delay: %d ms,\t" + "%d ms sent to FW\n", queue_delay, ret_val); + + return ret_val; +} + +/* + * This function retrieves the highest priority RA list table pointer. + */ +static struct mwifiex_ra_list_tbl * +mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, + struct mwifiex_private **priv, int *tid) +{ + struct mwifiex_private *priv_tmp; + struct mwifiex_ra_list_tbl *ptr; + struct mwifiex_tid_tbl *tid_ptr; + atomic_t *hqp; + unsigned long flags_ra; + int i, j; + + /* check the BSS with highest priority first */ + for (j = adapter->priv_num - 1; j >= 0; --j) { + /* iterate over BSS with the equal priority */ + list_for_each_entry(adapter->bss_prio_tbl[j].bss_prio_cur, + &adapter->bss_prio_tbl[j].bss_prio_head, + list) { + + priv_tmp = adapter->bss_prio_tbl[j].bss_prio_cur->priv; + + if (!priv_tmp->port_open || + (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0)) + continue; + + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv_tmp)) + continue; + + /* iterate over the WMM queues of the BSS */ + hqp = &priv_tmp->wmm.highest_queued_prio; + for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) { + + spin_lock_irqsave(&priv_tmp->wmm. + ra_list_spinlock, flags_ra); + + tid_ptr = &(priv_tmp)->wmm. + tid_tbl_ptr[tos_to_tid[i]]; + + /* iterate over receiver addresses */ + list_for_each_entry(ptr, &tid_ptr->ra_list, + list) { + + if (!ptr->tx_paused && + !skb_queue_empty(&ptr->skb_head)) + /* holds both locks */ + goto found; + } + + spin_unlock_irqrestore(&priv_tmp->wmm. + ra_list_spinlock, + flags_ra); + } + } + + } + + return NULL; + +found: + /* holds ra_list_spinlock */ + if (atomic_read(hqp) > i) + atomic_set(hqp, i); + spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags_ra); + + *priv = priv_tmp; + *tid = tos_to_tid[i]; + + return ptr; +} + +/* This functions rotates ra and bss lists so packets are picked round robin. + * + * After a packet is successfully transmitted, rotate the ra list, so the ra + * next to the one transmitted, will come first in the list. This way we pick + * the ra' in a round robin fashion. Same applies to bss nodes of equal + * priority. + * + * Function also increments wmm.packets_out counter. + */ +void mwifiex_rotate_priolists(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ra, + int tid) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; + struct mwifiex_tid_tbl *tid_ptr = &priv->wmm.tid_tbl_ptr[tid]; + unsigned long flags; + + spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags); + /* + * dirty trick: we remove 'head' temporarily and reinsert it after + * curr bss node. imagine list to stay fixed while head is moved + */ + list_move(&tbl[priv->bss_priority].bss_prio_head, + &tbl[priv->bss_priority].bss_prio_cur->list); + spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags); + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + if (mwifiex_is_ralist_valid(priv, ra, tid)) { + priv->wmm.packets_out[tid]++; + /* same as above */ + list_move(&tid_ptr->ra_list, &ra->list); + } + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + +/* + * This function checks if 11n aggregation is possible. + */ +static int +mwifiex_is_11n_aggragation_possible(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, + int max_buf_size) +{ + int count = 0, total_size = 0; + struct sk_buff *skb, *tmp; + int max_amsdu_size; + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP && priv->ap_11n_enabled && + ptr->is_11n_enabled) + max_amsdu_size = min_t(int, ptr->max_amsdu, max_buf_size); + else + max_amsdu_size = max_buf_size; + + skb_queue_walk_safe(&ptr->skb_head, skb, tmp) { + total_size += skb->len; + if (total_size >= max_amsdu_size) + break; + if (++count >= MIN_NUM_AMSDU) + return true; + } + + return false; +} + +/* + * This function sends a single packet to firmware for transmission. + */ +static void +mwifiex_send_single_packet(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int ptr_index, + unsigned long ra_list_flags) + __releases(&priv->wmm.ra_list_spinlock) +{ + struct sk_buff *skb, *skb_next; + struct mwifiex_tx_param tx_param; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_txinfo *tx_info; + + if (skb_queue_empty(&ptr->skb_head)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_dbg(adapter, DATA, "data: nothing to send\n"); + return; + } + + skb = skb_dequeue(&ptr->skb_head); + + tx_info = MWIFIEX_SKB_TXCB(skb); + mwifiex_dbg(adapter, DATA, + "data: dequeuing the packet %p %p\n", ptr, skb); + + ptr->total_pkt_count--; + + if (!skb_queue_empty(&ptr->skb_head)) + skb_next = skb_peek(&ptr->skb_head); + else + skb_next = NULL; + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); + + tx_param.next_pkt_len = ((skb_next) ? skb_next->len + + sizeof(struct txpd) : 0); + + if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { + /* Queue the packet back at the head */ + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + + if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_write_data_complete(adapter, skb, 0, -1); + return; + } + + skb_queue_tail(&ptr->skb_head, skb); + + ptr->total_pkt_count++; + ptr->ba_pkt_count++; + tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + } else { + mwifiex_rotate_priolists(priv, ptr, ptr_index); + atomic_dec(&priv->wmm.tx_pkts_queued); + } +} + +/* + * This function checks if the first packet in the given RA list + * is already processed or not. + */ +static int +mwifiex_is_ptr_processed(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr) +{ + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + + if (skb_queue_empty(&ptr->skb_head)) + return false; + + skb = skb_peek(&ptr->skb_head); + + tx_info = MWIFIEX_SKB_TXCB(skb); + if (tx_info->flags & MWIFIEX_BUF_FLAG_REQUEUED_PKT) + return true; + + return false; +} + +/* + * This function sends a single processed packet to firmware for + * transmission. + */ +static void +mwifiex_send_processed_packet(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int ptr_index, + unsigned long ra_list_flags) + __releases(&priv->wmm.ra_list_spinlock) +{ + struct mwifiex_tx_param tx_param; + struct mwifiex_adapter *adapter = priv->adapter; + int ret = -1; + struct sk_buff *skb, *skb_next; + struct mwifiex_txinfo *tx_info; + + if (skb_queue_empty(&ptr->skb_head)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + return; + } + + skb = skb_dequeue(&ptr->skb_head); + + if (adapter->data_sent || adapter->tx_lock_flag) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + skb_queue_tail(&adapter->tx_data_q, skb); + atomic_inc(&adapter->tx_queued); + return; + } + + if (!skb_queue_empty(&ptr->skb_head)) + skb_next = skb_peek(&ptr->skb_head); + else + skb_next = NULL; + + tx_info = MWIFIEX_SKB_TXCB(skb); + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); + + if (adapter->iface_type == MWIFIEX_USB) { + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, + skb, NULL); + } else { + tx_param.next_pkt_len = + ((skb_next) ? skb_next->len + + sizeof(struct txpd) : 0); + ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, + skb, &tx_param); + } + + switch (ret) { + case -EBUSY: + mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + + if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_write_data_complete(adapter, skb, 0, -1); + return; + } + + skb_queue_tail(&ptr->skb_head, skb); + + tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + break; + case -1: + mwifiex_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + case -EINPROGRESS: + break; + case 0: + mwifiex_write_data_complete(adapter, skb, 0, ret); + default: + break; + } + if (ret != -EBUSY) { + mwifiex_rotate_priolists(priv, ptr, ptr_index); + atomic_dec(&priv->wmm.tx_pkts_queued); + } +} + +/* + * This function dequeues a packet from the highest priority list + * and transmits it. + */ +static int +mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) +{ + struct mwifiex_ra_list_tbl *ptr; + struct mwifiex_private *priv = NULL; + int ptr_index = 0; + u8 ra[ETH_ALEN]; + int tid_del = 0, tid = 0; + unsigned long flags; + + ptr = mwifiex_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index); + if (!ptr) + return -1; + + tid = mwifiex_get_tid(ptr); + + mwifiex_dbg(adapter, DATA, "data: tid=%d\n", tid); + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + return -1; + } + + if (mwifiex_is_ptr_processed(priv, ptr)) { + mwifiex_send_processed_packet(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + mwifiex_send_processed_packet() */ + return 0; + } + + if (!ptr->is_11n_enabled || + ptr->ba_status || + priv->wps.session_enable) { + if (ptr->is_11n_enabled && + ptr->ba_status && + ptr->amsdu_in_ampdu && + mwifiex_is_amsdu_allowed(priv, tid) && + mwifiex_is_11n_aggragation_possible(priv, ptr, + adapter->tx_buf_size)) + mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + * mwifiex_11n_aggregate_pkt() + */ + else + mwifiex_send_single_packet(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + * mwifiex_send_single_packet() + */ + } else { + if (mwifiex_is_ampdu_allowed(priv, ptr, tid) && + ptr->ba_pkt_count > ptr->ba_packet_thr) { + if (mwifiex_space_avail_for_new_ba_stream(adapter)) { + mwifiex_create_ba_tbl(priv, ptr->ra, tid, + BA_SETUP_INPROGRESS); + mwifiex_send_addba(priv, tid, ptr->ra); + } else if (mwifiex_find_stream_to_delete + (priv, tid, &tid_del, ra)) { + mwifiex_create_ba_tbl(priv, ptr->ra, tid, + BA_SETUP_INPROGRESS); + mwifiex_send_delba(priv, tid_del, ra, 1); + } + } + if (mwifiex_is_amsdu_allowed(priv, tid) && + mwifiex_is_11n_aggragation_possible(priv, ptr, + adapter->tx_buf_size)) + mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + mwifiex_11n_aggregate_pkt() */ + else + mwifiex_send_single_packet(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + mwifiex_send_single_packet() */ + } + return 0; +} + +void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter) +{ + struct mwifiex_tx_param tx_param; + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + struct mwifiex_private *priv; + int i; + + if (adapter->data_sent || adapter->tx_lock_flag) + return; + + for (i = 0; i < adapter->priv_num; ++i) { + priv = adapter->priv[i]; + + if (!priv) + continue; + + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + continue; + + if (skb_queue_empty(&priv->bypass_txq)) + continue; + + skb = skb_dequeue(&priv->bypass_txq); + tx_info = MWIFIEX_SKB_TXCB(skb); + + /* no aggregation for bypass packets */ + tx_param.next_pkt_len = 0; + + if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { + skb_queue_head(&priv->bypass_txq, skb); + tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; + } else { + atomic_dec(&adapter->bypass_tx_pending); + } + } +} + +/* + * This function transmits the highest priority packet awaiting in the + * WMM Queues. + */ +void +mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter) +{ + do { + if (mwifiex_dequeue_tx_packet(adapter)) + break; + if (adapter->iface_type != MWIFIEX_SDIO) { + if (adapter->data_sent || + adapter->tx_lock_flag) + break; + } else { + if (atomic_read(&adapter->tx_queued) >= + MWIFIEX_MAX_PKTS_TXQ) + break; + } + } while (!mwifiex_wmm_lists_empty(adapter)); +} diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.h b/drivers/net/wireless/marvell/mwifiex/wmm.h new file mode 100644 index 000000000000..38f09762bd2f --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/wmm.h @@ -0,0 +1,140 @@ +/* + * Marvell Wireless LAN device driver: WMM + * + * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. + */ + +#ifndef _MWIFIEX_WMM_H_ +#define _MWIFIEX_WMM_H_ + +enum ieee_types_wmm_aciaifsn_bitmasks { + MWIFIEX_AIFSN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), + MWIFIEX_ACM = BIT(4), + MWIFIEX_ACI = (BIT(5) | BIT(6)), +}; + +enum ieee_types_wmm_ecw_bitmasks { + MWIFIEX_ECW_MIN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), + MWIFIEX_ECW_MAX = (BIT(4) | BIT(5) | BIT(6) | BIT(7)), +}; + +static const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; + +/* + * This table inverses the tos_to_tid operation to get a priority + * which is in sequential order, and can be compared. + * Use this to compare the priority of two different TIDs. + */ +static const u8 tos_to_tid_inv[] = { + 0x02, /* from tos_to_tid[2] = 0 */ + 0x00, /* from tos_to_tid[0] = 1 */ + 0x01, /* from tos_to_tid[1] = 2 */ + 0x03, + 0x04, + 0x05, + 0x06, + 0x07}; + +/* + * This function retrieves the TID of the given RA list. + */ +static inline int +mwifiex_get_tid(struct mwifiex_ra_list_tbl *ptr) +{ + struct sk_buff *skb; + + if (skb_queue_empty(&ptr->skb_head)) + return 0; + + skb = skb_peek(&ptr->skb_head); + + return skb->priority; +} + +/* + * This function gets the length of a list. + */ +static inline int +mwifiex_wmm_list_len(struct list_head *head) +{ + struct list_head *pos; + int count = 0; + + list_for_each(pos, head) + ++count; + + return count; +} + +/* + * This function checks if a RA list is empty or not. + */ +static inline u8 +mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead) +{ + struct mwifiex_ra_list_tbl *ra_list; + int is_list_empty; + + list_for_each_entry(ra_list, ra_list_hhead, list) { + is_list_empty = skb_queue_empty(&ra_list->skb_head); + if (!is_list_empty) + return false; + } + + return true; +} + +void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, + struct sk_buff *skb); +void mwifiex_wmm_add_buf_bypass_txqueue(struct mwifiex_private *priv, + struct sk_buff *skb); +void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra); +void mwifiex_rotate_priolists(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ra, int tid); + +int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter); +int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter); +void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter); +void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter); +int mwifiex_is_ralist_valid(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ra_list, int tid); + +u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, + const struct sk_buff *skb); +void mwifiex_wmm_init(struct mwifiex_adapter *adapter); + +u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, + u8 **assoc_buf, + struct ieee_types_wmm_parameter *wmmie, + struct ieee80211_ht_cap *htcap); + +void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, + struct ieee_types_wmm_parameter *wmm_ie); +void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); +int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, + const struct host_cmd_ds_command *resp); +struct mwifiex_ra_list_tbl * +mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr); +u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid); +void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, + u8 tx_pause); +void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, + u8 *mac, u8 tx_pause); + +struct mwifiex_ra_list_tbl *mwifiex_wmm_get_ralist_node(struct mwifiex_private + *priv, u8 tid, const u8 *ra_addr); +#endif /* !_MWIFIEX_WMM_H_ */ diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c deleted file mode 100644 index 59d23fb2365f..000000000000 --- a/drivers/net/wireless/mwifiex/11ac.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11ac - * - * Copyright (C) 2013-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "fw.h" -#include "main.h" -#include "11ac.h" - -/* Tables of the MCS map to the highest data rate (in Mbps) supported - * for long GI. - */ -static const u16 max_rate_lgi_80MHZ[8][3] = { - {0x124, 0x15F, 0x186}, /* NSS = 1 */ - {0x249, 0x2BE, 0x30C}, /* NSS = 2 */ - {0x36D, 0x41D, 0x492}, /* NSS = 3 */ - {0x492, 0x57C, 0x618}, /* NSS = 4 */ - {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */ - {0x6DB, 0x83A, 0x0}, /* NSS = 6 */ - {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */ - {0x924, 0xAF8, 0xC30} /* NSS = 8 */ -}; - -static const u16 max_rate_lgi_160MHZ[8][3] = { - {0x249, 0x2BE, 0x30C}, /* NSS = 1 */ - {0x492, 0x57C, 0x618}, /* NSS = 2 */ - {0x6DB, 0x83A, 0x0}, /* NSS = 3 */ - {0x924, 0xAF8, 0xC30}, /* NSS = 4 */ - {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */ - {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */ - {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */ - {0x1248, 0x15F0, 0x1860} /* NSS = 8 */ -}; - -/* This function converts the 2-bit MCS map to the highest long GI - * VHT data rate. - */ -static u16 -mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, - u8 bands, u16 mcs_map) -{ - u8 i, nss, mcs; - u16 max_rate = 0; - u32 usr_vht_cap_info = 0; - struct mwifiex_adapter *adapter = priv->adapter; - - if (bands & BAND_AAC) - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; - else - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; - - /* find the max NSS supported */ - nss = 1; - for (i = 1; i <= 8; i++) { - mcs = GET_VHTNSSMCS(mcs_map, i); - if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED) - nss = i; - } - mcs = GET_VHTNSSMCS(mcs_map, nss); - - /* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */ - if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED) - mcs = IEEE80211_VHT_MCS_SUPPORT_0_9; - - if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) { - /* support 160 MHz */ - max_rate = max_rate_lgi_160MHZ[nss - 1][mcs]; - if (!max_rate) - /* MCS9 is not supported in NSS6 */ - max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1]; - } else { - max_rate = max_rate_lgi_80MHZ[nss - 1][mcs]; - if (!max_rate) - /* MCS9 is not supported in NSS3 */ - max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1]; - } - - return max_rate; -} - -static void -mwifiex_fill_vht_cap_info(struct mwifiex_private *priv, - struct ieee80211_vht_cap *vht_cap, u8 bands) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - if (bands & BAND_A) - vht_cap->vht_cap_info = - cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a); - else - vht_cap->vht_cap_info = - cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg); -} - -void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, - struct ieee80211_vht_cap *vht_cap, u8 bands) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u16 mcs_map_user, mcs_map_resp, mcs_map_result; - u16 mcs_user, mcs_resp, nss, tmp; - - /* Fill VHT cap info */ - mwifiex_fill_vht_cap_info(priv, vht_cap, bands); - - /* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */ - mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); - mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); - mcs_map_result = 0; - - for (nss = 1; nss <= 8; nss++) { - mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); - mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); - - if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || - (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) - SET_VHTNSSMCS(mcs_map_result, nss, - IEEE80211_VHT_MCS_NOT_SUPPORTED); - else - SET_VHTNSSMCS(mcs_map_result, nss, - min(mcs_user, mcs_resp)); - } - - vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result); - - tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); - vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp); - - /* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */ - mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support); - mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); - mcs_map_result = 0; - - for (nss = 1; nss <= 8; nss++) { - mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); - mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); - if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || - (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) - SET_VHTNSSMCS(mcs_map_result, nss, - IEEE80211_VHT_MCS_NOT_SUPPORTED); - else - SET_VHTNSSMCS(mcs_map_result, nss, - min(mcs_user, mcs_resp)); - } - - vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result); - - tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); - vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp); - - return; -} - -int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, - u8 **buffer) -{ - struct mwifiex_ie_types_vhtcap *vht_cap; - struct mwifiex_ie_types_oper_mode_ntf *oper_ntf; - struct ieee_types_oper_mode_ntf *ieee_oper_ntf; - struct mwifiex_ie_types_vht_oper *vht_op; - struct mwifiex_adapter *adapter = priv->adapter; - u8 supp_chwd_set; - u32 usr_vht_cap_info; - int ret_len = 0; - - if (bss_desc->bss_band & BAND_A) - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; - else - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; - - /* VHT Capabilities IE */ - if (bss_desc->bcn_vht_cap) { - vht_cap = (struct mwifiex_ie_types_vhtcap *)*buffer; - memset(vht_cap, 0, sizeof(*vht_cap)); - vht_cap->header.type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY); - vht_cap->header.len = - cpu_to_le16(sizeof(struct ieee80211_vht_cap)); - memcpy((u8 *)vht_cap + sizeof(struct mwifiex_ie_types_header), - (u8 *)bss_desc->bcn_vht_cap, - le16_to_cpu(vht_cap->header.len)); - - mwifiex_fill_vht_cap_tlv(priv, &vht_cap->vht_cap, - bss_desc->bss_band); - *buffer += sizeof(*vht_cap); - ret_len += sizeof(*vht_cap); - } - - /* VHT Operation IE */ - if (bss_desc->bcn_vht_oper) { - if (priv->bss_mode == NL80211_IFTYPE_STATION) { - vht_op = (struct mwifiex_ie_types_vht_oper *)*buffer; - memset(vht_op, 0, sizeof(*vht_op)); - vht_op->header.type = - cpu_to_le16(WLAN_EID_VHT_OPERATION); - vht_op->header.len = cpu_to_le16(sizeof(*vht_op) - - sizeof(struct mwifiex_ie_types_header)); - memcpy((u8 *)vht_op + - sizeof(struct mwifiex_ie_types_header), - (u8 *)bss_desc->bcn_vht_oper, - le16_to_cpu(vht_op->header.len)); - - /* negotiate the channel width and central freq - * and keep the central freq as the peer suggests - */ - supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); - - switch (supp_chwd_set) { - case 0: - vht_op->chan_width = - min_t(u8, IEEE80211_VHT_CHANWIDTH_80MHZ, - bss_desc->bcn_vht_oper->chan_width); - break; - case 1: - vht_op->chan_width = - min_t(u8, IEEE80211_VHT_CHANWIDTH_160MHZ, - bss_desc->bcn_vht_oper->chan_width); - break; - case 2: - vht_op->chan_width = - min_t(u8, IEEE80211_VHT_CHANWIDTH_80P80MHZ, - bss_desc->bcn_vht_oper->chan_width); - break; - default: - vht_op->chan_width = - IEEE80211_VHT_CHANWIDTH_USE_HT; - break; - } - - *buffer += sizeof(*vht_op); - ret_len += sizeof(*vht_op); - } - } - - /* Operating Mode Notification IE */ - if (bss_desc->oper_mode) { - ieee_oper_ntf = bss_desc->oper_mode; - oper_ntf = (void *)*buffer; - memset(oper_ntf, 0, sizeof(*oper_ntf)); - oper_ntf->header.type = cpu_to_le16(WLAN_EID_OPMODE_NOTIF); - oper_ntf->header.len = cpu_to_le16(sizeof(u8)); - oper_ntf->oper_mode = ieee_oper_ntf->oper_mode; - *buffer += sizeof(*oper_ntf); - ret_len += sizeof(*oper_ntf); - } - - return ret_len; -} - -int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 cmd_action, - struct mwifiex_11ac_vht_cfg *cfg) -{ - struct host_cmd_11ac_vht_cfg *vhtcfg = &cmd->params.vht_cfg; - - cmd->command = cpu_to_le16(HostCmd_CMD_11AC_CFG); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_11ac_vht_cfg) + - S_DS_GEN); - vhtcfg->action = cpu_to_le16(cmd_action); - vhtcfg->band_config = cfg->band_config; - vhtcfg->misc_config = cfg->misc_config; - vhtcfg->cap_info = cpu_to_le32(cfg->cap_info); - vhtcfg->mcs_tx_set = cpu_to_le32(cfg->mcs_tx_set); - vhtcfg->mcs_rx_set = cpu_to_le32(cfg->mcs_rx_set); - - return 0; -} - -/* This function initializes the BlockACK setup information for given - * mwifiex_private structure for 11ac enabled networks. - */ -void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv) -{ - priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - priv->add_ba_param.tx_win_size = - MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE; - priv->add_ba_param.rx_win_size = - MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE; - } else { - priv->add_ba_param.tx_win_size = - MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; - priv->add_ba_param.rx_win_size = - MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; - } - - return; -} - -bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv) -{ - struct mwifiex_bssdescriptor *bss_desc; - struct ieee80211_vht_operation *vht_oper; - - bss_desc = &priv->curr_bss_params.bss_descriptor; - vht_oper = bss_desc->bcn_vht_oper; - - if (!bss_desc->bcn_vht_cap || !vht_oper) - return false; - - if (vht_oper->chan_width == IEEE80211_VHT_CHANWIDTH_USE_HT) - return false; - - return true; -} - -u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, - u32 pri_chan, u8 chan_bw) -{ - u8 center_freq_idx = 0; - - if (band & BAND_AAC) { - switch (pri_chan) { - case 36: - case 40: - case 44: - case 48: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 42; - break; - case 52: - case 56: - case 60: - case 64: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 58; - else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) - center_freq_idx = 50; - break; - case 100: - case 104: - case 108: - case 112: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 106; - break; - case 116: - case 120: - case 124: - case 128: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 122; - else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) - center_freq_idx = 114; - break; - case 132: - case 136: - case 140: - case 144: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 138; - break; - case 149: - case 153: - case 157: - case 161: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 155; - break; - default: - center_freq_idx = 42; - } - } - - return center_freq_idx; -} diff --git a/drivers/net/wireless/mwifiex/11ac.h b/drivers/net/wireless/mwifiex/11ac.h deleted file mode 100644 index 1ca92c7a8a4a..000000000000 --- a/drivers/net/wireless/mwifiex/11ac.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11ac - * - * Copyright (C) 2013-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_11AC_H_ -#define _MWIFIEX_11AC_H_ - -#define VHT_CFG_2GHZ BIT(0) -#define VHT_CFG_5GHZ BIT(1) - -enum vht_cfg_misc_config { - VHT_CAP_TX_OPERATION = 1, - VHT_CAP_ASSOCIATION, - VHT_CAP_UAP_ONLY -}; - -#define DEFAULT_VHT_MCS_SET 0xfffa -#define DISABLE_VHT_MCS_SET 0xffff - -#define VHT_BW_80_160_80P80 BIT(2) - -int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, - u8 **buffer); -int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 cmd_action, - struct mwifiex_11ac_vht_cfg *cfg); -void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, - struct ieee80211_vht_cap *vht_cap, u8 bands); -#endif /* _MWIFIEX_11AC_H_ */ diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c deleted file mode 100644 index 71a1b580796f..000000000000 --- a/drivers/net/wireless/mwifiex/11h.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11h - * - * Copyright (C) 2013-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" -#include "fw.h" - - -void mwifiex_init_11h_params(struct mwifiex_private *priv) -{ - priv->state_11h.is_11h_enabled = true; - priv->state_11h.is_11h_active = false; -} - -inline int mwifiex_is_11h_active(struct mwifiex_private *priv) -{ - return priv->state_11h.is_11h_active; -} -/* This function appends 11h info to a buffer while joining an - * infrastructure BSS - */ -static void -mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer, - struct mwifiex_bssdescriptor *bss_desc) -{ - struct mwifiex_ie_types_header *ie_header; - struct mwifiex_ie_types_pwr_capability *cap; - struct mwifiex_ie_types_local_pwr_constraint *constraint; - struct ieee80211_supported_band *sband; - u8 radio_type; - int i; - - if (!buffer || !(*buffer)) - return; - - radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - sband = priv->wdev.wiphy->bands[radio_type]; - - cap = (struct mwifiex_ie_types_pwr_capability *)*buffer; - cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY); - cap->header.len = cpu_to_le16(2); - cap->min_pwr = 0; - cap->max_pwr = 0; - *buffer += sizeof(*cap); - - constraint = (struct mwifiex_ie_types_local_pwr_constraint *)*buffer; - constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT); - constraint->header.len = cpu_to_le16(2); - constraint->chan = bss_desc->channel; - constraint->constraint = bss_desc->local_constraint; - *buffer += sizeof(*constraint); - - ie_header = (struct mwifiex_ie_types_header *)*buffer; - ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); - ie_header->len = cpu_to_le16(2 * sband->n_channels + 2); - *buffer += sizeof(*ie_header); - *(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS; - *(*buffer)++ = 2 * sband->n_channels; - for (i = 0; i < sband->n_channels; i++) { - *(*buffer)++ = ieee80211_frequency_to_channel( - sband->channels[i].center_freq); - *(*buffer)++ = 1; /* one channel in the subband */ - } -} - -/* Enable or disable the 11h extensions in the firmware */ -int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag) -{ - u32 enable = flag; - - /* enable master mode radar detection on AP interface */ - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && enable) - enable |= MWIFIEX_MASTER_RADAR_DET_MASK; - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, DOT11H_I, &enable, true); -} - -/* This functions processes TLV buffer for a pending BSS Join command. - * - * Activate 11h functionality in the firmware if the spectrum management - * capability bit is found in the network we are joining. Also, necessary - * TLVs are set based on requested network's 11h capability. - */ -void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (bss_desc->sensed_11h) { - /* Activate 11h functions in firmware, turns on capability - * bit - */ - mwifiex_11h_activate(priv, true); - priv->state_11h.is_11h_active = true; - bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT; - mwifiex_11h_process_infra_join(priv, buffer, bss_desc); - } else { - /* Deactivate 11h functions in the firmware */ - mwifiex_11h_activate(priv, false); - priv->state_11h.is_11h_active = false; - bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT; - } -} - -/* This is DFS CAC work queue function. - * This delayed work emits CAC finished event for cfg80211 if - * CAC was started earlier. - */ -void mwifiex_dfs_cac_work_queue(struct work_struct *work) -{ - struct cfg80211_chan_def chandef; - struct delayed_work *delayed_work = - container_of(work, struct delayed_work, work); - struct mwifiex_private *priv = - container_of(delayed_work, struct mwifiex_private, - dfs_cac_work); - - if (WARN_ON(!priv)) - return; - - chandef = priv->dfs_chandef; - if (priv->wdev.cac_started) { - mwifiex_dbg(priv->adapter, MSG, - "CAC timer finished; No radar detected\n"); - cfg80211_cac_event(priv->netdev, &chandef, - NL80211_RADAR_CAC_FINISHED, - GFP_KERNEL); - } -} - -/* This function prepares channel report request command to FW for - * starting radar detection. - */ -int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - void *data_buf) -{ - struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req; - struct mwifiex_radar_params *radar_params = (void *)data_buf; - - cmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST); - cmd->size = cpu_to_le16(S_DS_GEN); - le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_chan_rpt_req)); - - cr_req->chan_desc.start_freq = cpu_to_le16(MWIFIEX_A_BAND_START_FREQ); - cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value; - cr_req->chan_desc.chan_width = radar_params->chandef->width; - cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms); - - if (radar_params->cac_time_ms) - mwifiex_dbg(priv->adapter, MSG, - "11h: issuing DFS Radar check for channel=%d\n", - radar_params->chandef->chan->hw_value); - else - mwifiex_dbg(priv->adapter, MSG, "cancelling CAC\n"); - - return 0; -} - -int mwifiex_stop_radar_detection(struct mwifiex_private *priv, - struct cfg80211_chan_def *chandef) -{ - struct mwifiex_radar_params radar_params; - - memset(&radar_params, 0, sizeof(struct mwifiex_radar_params)); - radar_params.chandef = chandef; - radar_params.cac_time_ms = 0; - - return mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, - HostCmd_ACT_GEN_SET, 0, &radar_params, true); -} - -/* This function is to abort ongoing CAC upon stopping AP operations - * or during unload. - */ -void mwifiex_abort_cac(struct mwifiex_private *priv) -{ - if (priv->wdev.cac_started) { - if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) - mwifiex_dbg(priv->adapter, ERROR, - "failed to stop CAC in FW\n"); - mwifiex_dbg(priv->adapter, MSG, - "Aborting delayed work for CAC.\n"); - cancel_delayed_work_sync(&priv->dfs_cac_work); - cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, - NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); - } -} - -/* This function handles channel report event from FW during CAC period. - * If radar is detected during CAC, driver indicates the same to cfg80211 - * and also cancels ongoing delayed work. - */ -int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct host_cmd_ds_chan_rpt_event *rpt_event; - struct mwifiex_ie_types_chan_rpt_data *rpt; - u8 *evt_buf; - u16 event_len, tlv_len; - - rpt_event = (void *)(skb->data + sizeof(u32)); - event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event)+ - sizeof(u32)); - - if (le32_to_cpu(rpt_event->result) != HostCmd_RESULT_OK) { - mwifiex_dbg(priv->adapter, ERROR, - "Error in channel report event\n"); - return -1; - } - - evt_buf = (void *)&rpt_event->tlvbuf; - - while (event_len >= sizeof(struct mwifiex_ie_types_header)) { - rpt = (void *)&rpt_event->tlvbuf; - tlv_len = le16_to_cpu(rpt->header.len); - - switch (le16_to_cpu(rpt->header.type)) { - case TLV_TYPE_CHANRPT_11H_BASIC: - if (rpt->map.radar) { - mwifiex_dbg(priv->adapter, MSG, - "RADAR Detected on channel %d!\n", - priv->dfs_chandef.chan->hw_value); - cancel_delayed_work_sync(&priv->dfs_cac_work); - cfg80211_cac_event(priv->netdev, - &priv->dfs_chandef, - NL80211_RADAR_DETECTED, - GFP_KERNEL); - } - break; - default: - break; - } - - evt_buf += (tlv_len + sizeof(rpt->header)); - event_len -= (tlv_len + sizeof(rpt->header)); - } - - return 0; -} - -/* Handler for radar detected event from FW.*/ -int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_radar_det_event *rdr_event; - - rdr_event = (void *)(skb->data + sizeof(u32)); - - if (le32_to_cpu(rdr_event->passed)) { - mwifiex_dbg(priv->adapter, MSG, - "radar detected; indicating kernel\n"); - if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) - mwifiex_dbg(priv->adapter, ERROR, - "Failed to stop CAC in FW\n"); - cfg80211_radar_event(priv->adapter->wiphy, &priv->dfs_chandef, - GFP_KERNEL); - mwifiex_dbg(priv->adapter, MSG, "regdomain: %d\n", - rdr_event->reg_domain); - mwifiex_dbg(priv->adapter, MSG, "radar detection type: %d\n", - rdr_event->det_type); - } else { - mwifiex_dbg(priv->adapter, MSG, - "false radar detection event!\n"); - } - - return 0; -} - -/* This is work queue function for channel switch handling. - * This function takes care of updating new channel definitin to - * bss config structure, restart AP and indicate channel switch success - * to cfg80211. - */ -void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work) -{ - struct mwifiex_uap_bss_param *bss_cfg; - struct delayed_work *delayed_work = - container_of(work, struct delayed_work, work); - struct mwifiex_private *priv = - container_of(delayed_work, struct mwifiex_private, - dfs_chan_sw_work); - - if (WARN_ON(!priv)) - return; - - bss_cfg = &priv->bss_cfg; - if (!bss_cfg->beacon_period) { - mwifiex_dbg(priv->adapter, ERROR, - "channel switch: AP already stopped\n"); - return; - } - - mwifiex_uap_set_channel(priv, bss_cfg, priv->dfs_chandef); - - if (mwifiex_config_start_uap(priv, bss_cfg)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to start AP after channel switch\n"); - return; - } - - mwifiex_dbg(priv->adapter, MSG, - "indicating channel switch completion to kernel\n"); - cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef); -} diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c deleted file mode 100644 index c174e79e6df2..000000000000 --- a/drivers/net/wireless/mwifiex/11n.c +++ /dev/null @@ -1,914 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" - -/* - * Fills HT capability information field, AMPDU Parameters field, HT extended - * capability field, and supported MCS set fields. - * - * HT capability information field, AMPDU Parameters field, supported MCS set - * fields are retrieved from cfg80211 stack - * - * RD responder bit to set to clear in the extended capability header. - */ -int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, - struct ieee80211_ht_cap *ht_cap) -{ - uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info); - struct ieee80211_supported_band *sband = - priv->wdev.wiphy->bands[radio_type]; - - if (WARN_ON_ONCE(!sband)) { - mwifiex_dbg(priv->adapter, ERROR, "Invalid radio type!\n"); - return -EINVAL; - } - - ht_cap->ampdu_params_info = - (sband->ht_cap.ampdu_factor & - IEEE80211_HT_AMPDU_PARM_FACTOR) | - ((sband->ht_cap.ampdu_density << - IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) & - IEEE80211_HT_AMPDU_PARM_DENSITY); - - memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs, - sizeof(sband->ht_cap.mcs)); - - if (priv->bss_mode == NL80211_IFTYPE_STATION || - (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && - (priv->adapter->sec_chan_offset != - IEEE80211_HT_PARAM_CHA_SEC_NONE))) - /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ - SETHT_MCS32(ht_cap->mcs.rx_mask); - - /* Clear RD responder bit */ - ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER; - - ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap); - ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap); - - if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap)) - ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP); - - return 0; -} - -/* - * This function returns the pointer to an entry in BA Stream - * table which matches the requested BA status. - */ -static struct mwifiex_tx_ba_stream_tbl * -mwifiex_get_ba_status(struct mwifiex_private *priv, - enum mwifiex_ba_status ba_status) -{ - struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; - unsigned long flags; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { - if (tx_ba_tsr_tbl->ba_status == ba_status) { - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, - flags); - return tx_ba_tsr_tbl; - } - } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - return NULL; -} - -/* - * This function handles the command response of delete a block - * ack request. - * - * The function checks the response success status and takes action - * accordingly (send an add BA request in case of success, or recreate - * the deleted stream in case of failure, if the add BA was also - * initiated by us). - */ -int mwifiex_ret_11n_delba(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - int tid; - struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; - struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba; - uint16_t del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set); - - tid = del_ba_param_set >> DELBA_TID_POS; - if (del_ba->del_result == BA_RESULT_SUCCESS) { - mwifiex_del_ba_tbl(priv, tid, del_ba->peer_mac_addr, - TYPE_DELBA_SENT, - INITIATOR_BIT(del_ba_param_set)); - - tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS); - if (tx_ba_tbl) - mwifiex_send_addba(priv, tx_ba_tbl->tid, - tx_ba_tbl->ra); - } else { /* - * In case of failure, recreate the deleted stream in case - * we initiated the ADDBA - */ - if (!INITIATOR_BIT(del_ba_param_set)) - return 0; - - mwifiex_create_ba_tbl(priv, del_ba->peer_mac_addr, tid, - BA_SETUP_INPROGRESS); - - tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS); - - if (tx_ba_tbl) - mwifiex_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra, - TYPE_DELBA_SENT, true); - } - - return 0; -} - -/* - * This function handles the command response of add a block - * ack request. - * - * Handling includes changing the header fields to CPU formats, checking - * the response success status and taking actions accordingly (delete the - * BA stream table in case of failure). - */ -int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - int tid, tid_down; - struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; - struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; - struct mwifiex_ra_list_tbl *ra_list; - u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); - - add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) - & SSN_MASK); - - tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) - >> BLOCKACKPARAM_TID_POS; - - tid_down = mwifiex_wmm_downgrade_tid(priv, tid); - ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, add_ba_rsp-> - peer_mac_addr); - if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { - if (ra_list) { - ra_list->ba_status = BA_SETUP_NONE; - ra_list->amsdu_in_ampdu = false; - } - mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr, - TYPE_DELBA_SENT, true); - if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) - priv->aggr_prio_tbl[tid].ampdu_ap = - BA_STREAM_NOT_ALLOWED; - return 0; - } - - tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr); - if (tx_ba_tbl) { - mwifiex_dbg(priv->adapter, EVENT, "info: BA stream complete\n"); - tx_ba_tbl->ba_status = BA_SETUP_COMPLETE; - if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && - priv->add_ba_param.tx_amsdu && - (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) - tx_ba_tbl->amsdu = true; - else - tx_ba_tbl->amsdu = false; - if (ra_list) { - ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu; - ra_list->ba_status = BA_SETUP_COMPLETE; - } - } else { - mwifiex_dbg(priv->adapter, ERROR, "BA stream not created\n"); - } - - return 0; -} - -/* - * This function prepares command of reconfigure Tx buffer. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting Tx buffer size (for SET only) - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, int cmd_action, - u16 *buf_size) -{ - struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf; - u16 action = (u16) cmd_action; - - cmd->command = cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF); - cmd->size = - cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN); - tx_buf->action = cpu_to_le16(action); - switch (action) { - case HostCmd_ACT_GEN_SET: - mwifiex_dbg(priv->adapter, CMD, - "cmd: set tx_buf=%d\n", *buf_size); - tx_buf->buff_size = cpu_to_le16(*buf_size); - break; - case HostCmd_ACT_GEN_GET: - default: - tx_buf->buff_size = 0; - break; - } - return 0; -} - -/* - * This function prepares command of AMSDU aggregation control. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting AMSDU control parameters (for SET only) - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, - int cmd_action, - struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl) -{ - struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl = - &cmd->params.amsdu_aggr_ctrl; - u16 action = (u16) cmd_action; - - cmd->command = cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl) - + S_DS_GEN); - amsdu_ctrl->action = cpu_to_le16(action); - switch (action) { - case HostCmd_ACT_GEN_SET: - amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable); - amsdu_ctrl->curr_buf_size = 0; - break; - case HostCmd_ACT_GEN_GET: - default: - amsdu_ctrl->curr_buf_size = 0; - break; - } - return 0; -} - -/* - * This function prepares 11n configuration command. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting HT Tx capability and HT Tx information fields - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 cmd_action, - struct mwifiex_ds_11n_tx_cfg *txcfg) -{ - struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg; - - cmd->command = cpu_to_le16(HostCmd_CMD_11N_CFG); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN); - htcfg->action = cpu_to_le16(cmd_action); - htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap); - htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo); - - if (priv->adapter->is_hw_11ac_capable) - htcfg->misc_config = cpu_to_le16(txcfg->misc_config); - - return 0; -} - -/* - * This function appends an 11n TLV to a buffer. - * - * Buffer allocation is responsibility of the calling - * function. No size validation is made here. - * - * The function fills up the following sections, if applicable - - * - HT capability IE - * - HT information IE (with channel list) - * - 20/40 BSS Coexistence IE - * - HT Extended Capabilities IE - */ -int -mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, - u8 **buffer) -{ - struct mwifiex_ie_types_htcap *ht_cap; - struct mwifiex_ie_types_htinfo *ht_info; - struct mwifiex_ie_types_chan_list_param_set *chan_list; - struct mwifiex_ie_types_2040bssco *bss_co_2040; - struct mwifiex_ie_types_extcap *ext_cap; - int ret_len = 0; - struct ieee80211_supported_band *sband; - struct ieee_types_header *hdr; - u8 radio_type; - - if (!buffer || !*buffer) - return ret_len; - - radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - sband = priv->wdev.wiphy->bands[radio_type]; - - if (bss_desc->bcn_ht_cap) { - ht_cap = (struct mwifiex_ie_types_htcap *) *buffer; - memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); - ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); - ht_cap->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_cap)); - memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header), - (u8 *)bss_desc->bcn_ht_cap, - le16_to_cpu(ht_cap->header.len)); - - mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); - - *buffer += sizeof(struct mwifiex_ie_types_htcap); - ret_len += sizeof(struct mwifiex_ie_types_htcap); - } - - if (bss_desc->bcn_ht_oper) { - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { - ht_info = (struct mwifiex_ie_types_htinfo *) *buffer; - memset(ht_info, 0, - sizeof(struct mwifiex_ie_types_htinfo)); - ht_info->header.type = - cpu_to_le16(WLAN_EID_HT_OPERATION); - ht_info->header.len = - cpu_to_le16( - sizeof(struct ieee80211_ht_operation)); - - memcpy((u8 *) ht_info + - sizeof(struct mwifiex_ie_types_header), - (u8 *)bss_desc->bcn_ht_oper, - le16_to_cpu(ht_info->header.len)); - - if (!(sband->ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40)) - ht_info->ht_oper.ht_param &= - ~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY | - IEEE80211_HT_PARAM_CHA_SEC_OFFSET); - - *buffer += sizeof(struct mwifiex_ie_types_htinfo); - ret_len += sizeof(struct mwifiex_ie_types_htinfo); - } - - chan_list = - (struct mwifiex_ie_types_chan_list_param_set *) *buffer; - memset(chan_list, 0, - sizeof(struct mwifiex_ie_types_chan_list_param_set)); - chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - chan_list->header.len = cpu_to_le16( - sizeof(struct mwifiex_ie_types_chan_list_param_set) - - sizeof(struct mwifiex_ie_types_header)); - chan_list->chan_scan_param[0].chan_number = - bss_desc->bcn_ht_oper->primary_chan; - chan_list->chan_scan_param[0].radio_type = - mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - - if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && - bss_desc->bcn_ht_oper->ht_param & - IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) - SET_SECONDARYCHAN(chan_list->chan_scan_param[0]. - radio_type, - (bss_desc->bcn_ht_oper->ht_param & - IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); - - *buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set); - ret_len += sizeof(struct mwifiex_ie_types_chan_list_param_set); - } - - if (bss_desc->bcn_bss_co_2040) { - bss_co_2040 = (struct mwifiex_ie_types_2040bssco *) *buffer; - memset(bss_co_2040, 0, - sizeof(struct mwifiex_ie_types_2040bssco)); - bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040); - bss_co_2040->header.len = - cpu_to_le16(sizeof(bss_co_2040->bss_co_2040)); - - memcpy((u8 *) bss_co_2040 + - sizeof(struct mwifiex_ie_types_header), - bss_desc->bcn_bss_co_2040 + - sizeof(struct ieee_types_header), - le16_to_cpu(bss_co_2040->header.len)); - - *buffer += sizeof(struct mwifiex_ie_types_2040bssco); - ret_len += sizeof(struct mwifiex_ie_types_2040bssco); - } - - if (bss_desc->bcn_ext_cap) { - hdr = (void *)bss_desc->bcn_ext_cap; - ext_cap = (struct mwifiex_ie_types_extcap *) *buffer; - memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap)); - ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY); - ext_cap->header.len = cpu_to_le16(hdr->len); - - memcpy((u8 *)ext_cap->ext_capab, - bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header), - le16_to_cpu(ext_cap->header.len)); - - if (hdr->len > 3 && - ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED) - priv->hs2_enabled = true; - else - priv->hs2_enabled = false; - - *buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len; - ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len; - } - - return ret_len; -} - -/* - * This function checks if the given pointer is valid entry of - * Tx BA Stream table. - */ -static int mwifiex_is_tx_ba_stream_ptr_valid(struct mwifiex_private *priv, - struct mwifiex_tx_ba_stream_tbl *tx_tbl_ptr) -{ - struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; - - list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { - if (tx_ba_tsr_tbl == tx_tbl_ptr) - return true; - } - - return false; -} - -/* - * This function deletes the given entry in Tx BA Stream table. - * - * The function also performs a validity check on the supplied - * pointer before trying to delete. - */ -void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, - struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl) -{ - if (!tx_ba_tsr_tbl && - mwifiex_is_tx_ba_stream_ptr_valid(priv, tx_ba_tsr_tbl)) - return; - - mwifiex_dbg(priv->adapter, INFO, - "info: tx_ba_tsr_tbl %p\n", tx_ba_tsr_tbl); - - list_del(&tx_ba_tsr_tbl->list); - - kfree(tx_ba_tsr_tbl); -} - -/* - * This function deletes all the entries in Tx BA Stream table. - */ -void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv) -{ - int i; - struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node; - unsigned long flags; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry_safe(del_tbl_ptr, tmp_node, - &priv->tx_ba_stream_tbl_ptr, list) - mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr); - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - - INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); - - for (i = 0; i < MAX_NUM_TID; ++i) - priv->aggr_prio_tbl[i].ampdu_ap = - priv->aggr_prio_tbl[i].ampdu_user; -} - -/* - * This function returns the pointer to an entry in BA Stream - * table which matches the given RA/TID pair. - */ -struct mwifiex_tx_ba_stream_tbl * -mwifiex_get_ba_tbl(struct mwifiex_private *priv, int tid, u8 *ra) -{ - struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; - unsigned long flags; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { - if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) && - tx_ba_tsr_tbl->tid == tid) { - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, - flags); - return tx_ba_tsr_tbl; - } - } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - return NULL; -} - -/* - * This function creates an entry in Tx BA stream table for the - * given RA/TID pair. - */ -void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, - enum mwifiex_ba_status ba_status) -{ - struct mwifiex_tx_ba_stream_tbl *new_node; - struct mwifiex_ra_list_tbl *ra_list; - unsigned long flags; - int tid_down; - - if (!mwifiex_get_ba_tbl(priv, tid, ra)) { - new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl), - GFP_ATOMIC); - if (!new_node) - return; - - tid_down = mwifiex_wmm_downgrade_tid(priv, tid); - ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, ra); - if (ra_list) { - ra_list->ba_status = ba_status; - ra_list->amsdu_in_ampdu = false; - } - INIT_LIST_HEAD(&new_node->list); - - new_node->tid = tid; - new_node->ba_status = ba_status; - memcpy(new_node->ra, ra, ETH_ALEN); - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr); - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - } -} - -/* - * This function sends an add BA request to the given TID/RA pair. - */ -int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) -{ - struct host_cmd_ds_11n_addba_req add_ba_req; - u32 tx_win_size = priv->add_ba_param.tx_win_size; - static u8 dialog_tok; - int ret; - unsigned long flags; - u16 block_ack_param_set; - - mwifiex_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid); - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - priv->adapter->is_hw_11ac_capable && - memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) { - struct mwifiex_sta_node *sta_ptr; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, peer_mac); - if (!sta_ptr) { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - mwifiex_dbg(priv->adapter, ERROR, - "BA setup with unknown TDLS peer %pM!\n", - peer_mac); - return -1; - } - if (sta_ptr->is_11ac_enabled) - tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - } - - block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) | - tx_win_size << BLOCKACKPARAM_WINSIZE_POS | - IMMEDIATE_BLOCK_ACK); - - /* enable AMSDU inside AMPDU */ - if (priv->add_ba_param.tx_amsdu && - (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) - block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK; - - add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set); - add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout); - - ++dialog_tok; - - if (dialog_tok == 0) - dialog_tok = 1; - - add_ba_req.dialog_token = dialog_tok; - memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN); - - /* We don't wait for the response of this command */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ, - 0, 0, &add_ba_req, false); - - return ret; -} - -/* - * This function sends a delete BA request to the given TID/RA pair. - */ -int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, - int initiator) -{ - struct host_cmd_ds_11n_delba delba; - int ret; - uint16_t del_ba_param_set; - - memset(&delba, 0, sizeof(delba)); - delba.del_ba_param_set = cpu_to_le16(tid << DELBA_TID_POS); - - del_ba_param_set = le16_to_cpu(delba.del_ba_param_set); - if (initiator) - del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK; - else - del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK; - - memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN); - - /* We don't wait for the response of this command */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, - HostCmd_ACT_GEN_SET, 0, &delba, false); - - return ret; -} - -/* - * This function sends delba to specific tid - */ -void mwifiex_11n_delba(struct mwifiex_private *priv, int tid) -{ - struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; - - if (list_empty(&priv->rx_reorder_tbl_ptr)) { - dev_dbg(priv->adapter->dev, - "mwifiex_11n_delba: rx_reorder_tbl_ptr empty\n"); - return; - } - - list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) { - if (rx_reor_tbl_ptr->tid == tid) { - dev_dbg(priv->adapter->dev, - "Send delba to tid=%d, %pM\n", - tid, rx_reor_tbl_ptr->ta); - mwifiex_send_delba(priv, tid, rx_reor_tbl_ptr->ta, 0); - return; - } - } -} - -/* - * This function handles the command response of a delete BA request. - */ -void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba) -{ - struct host_cmd_ds_11n_delba *cmd_del_ba = - (struct host_cmd_ds_11n_delba *) del_ba; - uint16_t del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set); - int tid; - - tid = del_ba_param_set >> DELBA_TID_POS; - - mwifiex_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr, - TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set)); -} - -/* - * This function retrieves the Rx reordering table. - */ -int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, - struct mwifiex_ds_rx_reorder_tbl *buf) -{ - int i; - struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf; - struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr; - int count = 0; - unsigned long flags; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr, - list) { - rx_reo_tbl->tid = (u16) rx_reorder_tbl_ptr->tid; - memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN); - rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win; - rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size; - for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) { - if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) - rx_reo_tbl->buffer[i] = true; - else - rx_reo_tbl->buffer[i] = false; - } - rx_reo_tbl++; - count++; - - if (count >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED) - break; - } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - return count; -} - -/* - * This function retrieves the Tx BA stream table. - */ -int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, - struct mwifiex_ds_tx_ba_stream_tbl *buf) -{ - struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; - struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf; - int count = 0; - unsigned long flags; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { - rx_reo_tbl->tid = (u16) tx_ba_tsr_tbl->tid; - mwifiex_dbg(priv->adapter, DATA, "data: %s tid=%d\n", - __func__, rx_reo_tbl->tid); - memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN); - rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu; - rx_reo_tbl++; - count++; - if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) - break; - } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - - return count; -} - -/* - * This function retrieves the entry for specific tx BA stream table by RA and - * deletes it. - */ -void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra) -{ - struct mwifiex_tx_ba_stream_tbl *tbl, *tmp; - unsigned long flags; - - if (!ra) - return; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list) { - if (!memcmp(tbl->ra, ra, ETH_ALEN)) { - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, - flags); - mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, tbl); - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - } - } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - - return; -} - -/* This function initializes the BlockACK setup information for given - * mwifiex_private structure. - */ -void mwifiex_set_ba_params(struct mwifiex_private *priv) -{ - priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - priv->add_ba_param.tx_win_size = - MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE; - priv->add_ba_param.rx_win_size = - MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE; - } else { - priv->add_ba_param.tx_win_size = - MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; - priv->add_ba_param.rx_win_size = - MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; - } - - priv->add_ba_param.tx_amsdu = true; - priv->add_ba_param.rx_amsdu = true; - - return; -} - -u8 mwifiex_get_sec_chan_offset(int chan) -{ - u8 sec_offset; - - switch (chan) { - case 36: - case 44: - case 52: - case 60: - case 100: - case 108: - case 116: - case 124: - case 132: - case 140: - case 149: - case 157: - sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - break; - case 40: - case 48: - case 56: - case 64: - case 104: - case 112: - case 120: - case 128: - case 136: - case 144: - case 153: - case 161: - sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; - break; - case 165: - default: - sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; - break; - } - - return sec_offset; -} - -/* This function will send DELBA to entries in the priv's - * Tx BA stream table - */ -static void -mwifiex_send_delba_txbastream_tbl(struct mwifiex_private *priv, u8 tid) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_tx_ba_stream_tbl *tx_ba_stream_tbl_ptr; - - if (list_empty(&priv->tx_ba_stream_tbl_ptr)) - return; - - list_for_each_entry(tx_ba_stream_tbl_ptr, - &priv->tx_ba_stream_tbl_ptr, list) { - if (tx_ba_stream_tbl_ptr->ba_status == BA_SETUP_COMPLETE) { - if (tid == tx_ba_stream_tbl_ptr->tid) { - dev_dbg(adapter->dev, - "Tx:Send delba to tid=%d, %pM\n", tid, - tx_ba_stream_tbl_ptr->ra); - mwifiex_send_delba(priv, - tx_ba_stream_tbl_ptr->tid, - tx_ba_stream_tbl_ptr->ra, 1); - return; - } - } - } -} - -/* This function updates all the tx_win_size - */ -void mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *adapter) -{ - u8 i; - u32 tx_win_size; - struct mwifiex_private *priv; - - for (i = 0; i < adapter->priv_num; i++) { - if (!adapter->priv[i]) - continue; - priv = adapter->priv[i]; - tx_win_size = priv->add_ba_param.tx_win_size; - - if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) - priv->add_ba_param.tx_win_size = - MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; - - if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) - priv->add_ba_param.tx_win_size = - MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; - - if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) - priv->add_ba_param.tx_win_size = - MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE; - - if (adapter->coex_win_size) { - if (adapter->coex_tx_win_size) - priv->add_ba_param.tx_win_size = - adapter->coex_tx_win_size; - } - - if (tx_win_size != priv->add_ba_param.tx_win_size) { - if (!priv->media_connected) - continue; - for (i = 0; i < MAX_NUM_TID; i++) - mwifiex_send_delba_txbastream_tbl(priv, i); - } - } -} diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h deleted file mode 100644 index afdd58aa90de..000000000000 --- a/drivers/net/wireless/mwifiex/11n.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_11N_H_ -#define _MWIFIEX_11N_H_ - -#include "11n_aggr.h" -#include "11n_rxreorder.h" -#include "wmm.h" - -int mwifiex_ret_11n_delba(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 cmd_action, - struct mwifiex_ds_11n_tx_cfg *txcfg); -int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, - u8 **buffer); -int mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type, - struct ieee80211_ht_cap *); -int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv, - u16 action, int *htcap_cfg); -void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, - struct mwifiex_tx_ba_stream_tbl - *tx_tbl); -void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv); -struct mwifiex_tx_ba_stream_tbl *mwifiex_get_ba_tbl(struct - mwifiex_private - *priv, int tid, - u8 *ra); -void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, - enum mwifiex_ba_status ba_status); -int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac); -int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, - int initiator); -void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba); -int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, - struct mwifiex_ds_rx_reorder_tbl *buf); -int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, - struct mwifiex_ds_tx_ba_stream_tbl *buf); -int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - int cmd_action, u16 *buf_size); -int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, - int cmd_action, - struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl); -void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra); -u8 mwifiex_get_sec_chan_offset(int chan); - -static inline u8 -mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int tid) -{ - struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ptr->ra); - - if (unlikely(!node)) - return false; - - return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false; -} - -/* This function checks whether AMPDU is allowed or not for a particular TID. */ -static inline u8 -mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int tid) -{ - if (is_broadcast_ether_addr(ptr->ra)) - return false; - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); - } else { - if (ptr->tdls_link) - return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); - - return (priv->aggr_prio_tbl[tid].ampdu_ap != - BA_STREAM_NOT_ALLOWED) ? true : false; - } -} - -/* - * This function checks whether AMSDU is allowed or not for a particular TID. - */ -static inline u8 -mwifiex_is_amsdu_allowed(struct mwifiex_private *priv, int tid) -{ - return (((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) && - (priv->is_data_rate_auto || !(priv->bitmap_rates[2] & 0x03))) - ? true : false); -} - -/* - * This function checks whether a space is available for new BA stream or not. - */ -static inline u8 mwifiex_space_avail_for_new_ba_stream( - struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - u8 i; - u32 ba_stream_num = 0, ba_stream_max; - - ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv) - ba_stream_num += mwifiex_wmm_list_len( - &priv->tx_ba_stream_tbl_ptr); - } - - if (adapter->fw_api_ver == MWIFIEX_FW_V15) { - ba_stream_max = - GETSUPP_TXBASTREAMS(adapter->hw_dot_11n_dev_cap); - if (!ba_stream_max) - ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; - } - - return ((ba_stream_num < ba_stream_max) ? true : false); -} - -/* - * This function finds the correct Tx BA stream to delete. - * - * Upon successfully locating, both the TID and the RA are returned. - */ -static inline u8 -mwifiex_find_stream_to_delete(struct mwifiex_private *priv, int ptr_tid, - int *ptid, u8 *ra) -{ - int tid; - u8 ret = false; - struct mwifiex_tx_ba_stream_tbl *tx_tbl; - unsigned long flags; - - tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr, list) { - if (tid > priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user) { - tid = priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user; - *ptid = tx_tbl->tid; - memcpy(ra, tx_tbl->ra, ETH_ALEN); - ret = true; - } - } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - - return ret; -} - -/* - * This function checks whether associated station is 11n enabled - */ -static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv, - struct mwifiex_sta_node *node) -{ - - if (!node || (priv->bss_role != MWIFIEX_BSS_ROLE_UAP) || - !priv->ap_11n_enabled) - return 0; - - return node->is_11n_enabled; -} - -static inline u8 -mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, const u8 *ra) -{ - struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra); - if (node) - return node->is_11n_enabled; - - return false; -} -#endif /* !_MWIFIEX_11N_H_ */ diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c deleted file mode 100644 index aa498e0d2204..000000000000 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n Aggregation - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11n_aggr.h" - -/* - * Creates an AMSDU subframe for aggregation into one AMSDU packet. - * - * The resultant AMSDU subframe format is - - * - * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ - * | DA | SA | Length | SNAP header | MSDU | - * | data[0..5] | data[6..11] | | | data[14..] | - * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ - * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes--> - * - * This function also computes the amount of padding required to make the - * buffer length multiple of 4 bytes. - * - * Data => |DA|SA|SNAP-TYPE|........ .| - * MSDU => |DA|SA|Length|SNAP|...... ..| - */ -static int -mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr, - struct sk_buff *skb_src, int *pad) - -{ - int dt_offset; - struct rfc_1042_hdr snap = { - 0xaa, /* LLC DSAP */ - 0xaa, /* LLC SSAP */ - 0x03, /* LLC CTRL */ - {0x00, 0x00, 0x00}, /* SNAP OUI */ - 0x0000 /* SNAP type */ - /* - * This field will be overwritten - * later with ethertype - */ - }; - struct tx_packet_hdr *tx_header; - - tx_header = (void *)skb_put(skb_aggr, sizeof(*tx_header)); - - /* Copy DA and SA */ - dt_offset = 2 * ETH_ALEN; - memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); - - /* Copy SNAP header */ - snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto; - - dt_offset += sizeof(__be16); - - memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr)); - - skb_pull(skb_src, dt_offset); - - /* Update Length field */ - tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN); - - /* Add payload */ - memcpy(skb_put(skb_aggr, skb_src->len), skb_src->data, skb_src->len); - - /* Add padding for new MSDU to start from 4 byte boundary */ - *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4; - - return skb_aggr->len + *pad; -} - -/* - * Adds TxPD to AMSDU header. - * - * Each AMSDU packet will contain one TxPD at the beginning, - * followed by multiple AMSDU subframes. - */ -static void -mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct txpd *local_tx_pd; - struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - unsigned int pad; - int headroom = (priv->adapter->iface_type == - MWIFIEX_USB) ? 0 : INTF_HEADER_LEN; - - pad = ((void *)skb->data - sizeof(*local_tx_pd) - - headroom - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); - skb_push(skb, pad); - - skb_push(skb, sizeof(*local_tx_pd)); - - local_tx_pd = (struct txpd *) skb->data; - memset(local_tx_pd, 0, sizeof(struct txpd)); - - /* Original priority has been overwritten */ - local_tx_pd->priority = (u8) skb->priority; - local_tx_pd->pkt_delay_2ms = - mwifiex_wmm_compute_drv_pkt_delay(priv, skb); - local_tx_pd->bss_num = priv->bss_num; - local_tx_pd->bss_type = priv->bss_type; - /* Always zero as the data is followed by struct txpd */ - local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) + - pad); - local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU); - local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - - sizeof(*local_tx_pd) - - pad); - - if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) - local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; - - if (local_tx_pd->tx_control == 0) - /* TxCtrl set by user or default */ - local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && - priv->adapter->pps_uapsd_mode) { - if (true == mwifiex_check_last_packet_indication(priv)) { - priv->adapter->tx_lock_flag = true; - local_tx_pd->flags = - MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; - } - } -} - -/* - * Create aggregated packet. - * - * This function creates an aggregated MSDU packet, by combining buffers - * from the RA list. Each individual buffer is encapsulated as an AMSDU - * subframe and all such subframes are concatenated together to form the - * AMSDU packet. - * - * A TxPD is also added to the front of the resultant AMSDU packets for - * transmission. The resultant packets format is - - * - * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+ - * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame| - * | | 1 | 2 | .. | n | - * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+ - */ -int -mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *pra_list, - int ptrindex, unsigned long ra_list_flags) - __releases(&priv->wmm.ra_list_spinlock) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct sk_buff *skb_aggr, *skb_src; - struct mwifiex_txinfo *tx_info_aggr, *tx_info_src; - int pad = 0, aggr_num = 0, ret; - struct mwifiex_tx_param tx_param; - struct txpd *ptx_pd = NULL; - int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN; - - skb_src = skb_peek(&pra_list->skb_head); - if (!skb_src) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - return 0; - } - - tx_info_src = MWIFIEX_SKB_TXCB(skb_src); - skb_aggr = mwifiex_alloc_dma_align_buf(adapter->tx_buf_size, - GFP_ATOMIC | GFP_DMA); - if (!skb_aggr) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - return -1; - } - skb_reserve(skb_aggr, MWIFIEX_MIN_DATA_HEADER_LEN); - tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); - - memset(tx_info_aggr, 0, sizeof(*tx_info_aggr)); - tx_info_aggr->bss_type = tx_info_src->bss_type; - tx_info_aggr->bss_num = tx_info_src->bss_num; - - if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) - tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; - tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT; - skb_aggr->priority = skb_src->priority; - skb_aggr->tstamp = skb_src->tstamp; - - skb_aggr->tstamp = ktime_get_real(); - - do { - /* Check if AMSDU can accommodate this MSDU */ - if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN)) - break; - - skb_src = skb_dequeue(&pra_list->skb_head); - pra_list->total_pkt_count--; - atomic_dec(&priv->wmm.tx_pkts_queued); - aggr_num++; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad); - - mwifiex_write_data_complete(adapter, skb_src, 0, 0); - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); - - if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - return -1; - } - - if (skb_tailroom(skb_aggr) < pad) { - pad = 0; - break; - } - skb_put(skb_aggr, pad); - - skb_src = skb_peek(&pra_list->skb_head); - - } while (skb_src); - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); - - /* Last AMSDU packet does not need padding */ - skb_trim(skb_aggr, skb_aggr->len - pad); - - /* Form AMSDU */ - mwifiex_11n_form_amsdu_txpd(priv, skb_aggr); - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) - ptx_pd = (struct txpd *)skb_aggr->data; - - skb_push(skb_aggr, headroom); - tx_info_aggr->aggr_num = aggr_num * 2; - if (adapter->data_sent || adapter->tx_lock_flag) { - atomic_add(aggr_num * 2, &adapter->tx_queued); - skb_queue_tail(&adapter->tx_data_q, skb_aggr); - return 0; - } - - if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, - skb_aggr, NULL); - } else { - if (skb_src) - tx_param.next_pkt_len = - skb_src->len + sizeof(struct txpd); - else - tx_param.next_pkt_len = 0; - - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, - skb_aggr, &tx_param); - } - switch (ret) { - case -EBUSY: - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); - if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_write_data_complete(adapter, skb_aggr, 1, -1); - return -1; - } - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && - adapter->pps_uapsd_mode && adapter->tx_lock_flag) { - priv->adapter->tx_lock_flag = false; - if (ptx_pd) - ptx_pd->flags = 0; - } - - skb_queue_tail(&pra_list->skb_head, skb_aggr); - - pra_list->total_pkt_count++; - - atomic_inc(&priv->wmm.tx_pkts_queued); - - tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); - break; - case -1: - mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n", - __func__, ret); - adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); - return 0; - case -EINPROGRESS: - break; - case 0: - mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); - break; - default: - break; - } - if (ret != -EBUSY) { - mwifiex_rotate_priolists(priv, pra_list, ptrindex); - } - - return 0; -} diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/mwifiex/11n_aggr.h deleted file mode 100644 index 0cd2a3eb6c17..000000000000 --- a/drivers/net/wireless/mwifiex/11n_aggr.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n Aggregation - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_11N_AGGR_H_ -#define _MWIFIEX_11N_AGGR_H_ - -#define PKT_TYPE_AMSDU 0xE6 -#define MIN_NUM_AMSDU 2 - -int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv, - struct sk_buff *skb); -int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, - int ptr_index, unsigned long flags) - __releases(&priv->wmm.ra_list_spinlock); - -#endif /* !_MWIFIEX_11N_AGGR_H_ */ diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c deleted file mode 100644 index b3970a8c9e48..000000000000 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ /dev/null @@ -1,910 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n RX Re-ordering - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11n_rxreorder.h" - -/* This function will dispatch amsdu packet and forward it to kernel/upper - * layer. - */ -static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct rxpd *local_rx_pd = (struct rxpd *)(skb->data); - int ret; - - if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) { - struct sk_buff_head list; - struct sk_buff *rx_skb; - - __skb_queue_head_init(&list); - - skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset)); - skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length)); - - ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, - priv->wdev.iftype, 0, false); - - while (!skb_queue_empty(&list)) { - rx_skb = __skb_dequeue(&list); - ret = mwifiex_recv_packet(priv, rx_skb); - if (ret == -1) - mwifiex_dbg(priv->adapter, ERROR, - "Rx of A-MSDU failed"); - } - return 0; - } - - return -1; -} - -/* This function will process the rx packet and forward it to kernel/upper - * layer. - */ -static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload) -{ - int ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload); - - if (!ret) - return 0; - - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) - return mwifiex_handle_uap_rx_forward(priv, payload); - - return mwifiex_process_rx_packet(priv, payload); -} - -/* - * This function dispatches all packets in the Rx reorder table until the - * start window. - * - * There could be holes in the buffer, which are skipped by the function. - * Since the buffer is linear, the function uses rotation to simulate - * circular buffer. - */ -static void -mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, - struct mwifiex_rx_reorder_tbl *tbl, - int start_win) -{ - int pkt_to_send, i; - void *rx_tmp_ptr; - unsigned long flags; - - pkt_to_send = (start_win > tbl->start_win) ? - min((start_win - tbl->start_win), tbl->win_size) : - tbl->win_size; - - for (i = 0; i < pkt_to_send; ++i) { - spin_lock_irqsave(&priv->rx_pkt_lock, flags); - rx_tmp_ptr = NULL; - if (tbl->rx_reorder_ptr[i]) { - rx_tmp_ptr = tbl->rx_reorder_ptr[i]; - tbl->rx_reorder_ptr[i] = NULL; - } - spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); - if (rx_tmp_ptr) - mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); - } - - spin_lock_irqsave(&priv->rx_pkt_lock, flags); - /* - * We don't have a circular buffer, hence use rotation to simulate - * circular buffer - */ - for (i = 0; i < tbl->win_size - pkt_to_send; ++i) { - tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i]; - tbl->rx_reorder_ptr[pkt_to_send + i] = NULL; - } - - tbl->start_win = start_win; - spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); -} - -/* - * This function dispatches all packets in the Rx reorder table until - * a hole is found. - * - * The start window is adjusted automatically when a hole is located. - * Since the buffer is linear, the function uses rotation to simulate - * circular buffer. - */ -static void -mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, - struct mwifiex_rx_reorder_tbl *tbl) -{ - int i, j, xchg; - void *rx_tmp_ptr; - unsigned long flags; - - for (i = 0; i < tbl->win_size; ++i) { - spin_lock_irqsave(&priv->rx_pkt_lock, flags); - if (!tbl->rx_reorder_ptr[i]) { - spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); - break; - } - rx_tmp_ptr = tbl->rx_reorder_ptr[i]; - tbl->rx_reorder_ptr[i] = NULL; - spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); - mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); - } - - spin_lock_irqsave(&priv->rx_pkt_lock, flags); - /* - * We don't have a circular buffer, hence use rotation to simulate - * circular buffer - */ - if (i > 0) { - xchg = tbl->win_size - i; - for (j = 0; j < xchg; ++j) { - tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j]; - tbl->rx_reorder_ptr[i + j] = NULL; - } - } - tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1); - spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); -} - -/* - * This function deletes the Rx reorder table and frees the memory. - * - * The function stops the associated timer and dispatches all the - * pending packets in the Rx reorder table before deletion. - */ -static void -mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, - struct mwifiex_rx_reorder_tbl *tbl) -{ - unsigned long flags; - int start_win; - - if (!tbl) - return; - - spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); - priv->adapter->rx_locked = true; - if (priv->adapter->rx_processing) { - spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); - flush_workqueue(priv->adapter->rx_workqueue); - } else { - spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); - } - - start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); - mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); - - del_timer_sync(&tbl->timer_context.timer); - tbl->timer_context.timer_is_set = false; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_del(&tbl->list); - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - kfree(tbl->rx_reorder_ptr); - kfree(tbl); - - spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); - priv->adapter->rx_locked = false; - spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); - -} - -/* - * This function returns the pointer to an entry in Rx reordering - * table which matches the given TA/TID pair. - */ -struct mwifiex_rx_reorder_tbl * -mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) -{ - struct mwifiex_rx_reorder_tbl *tbl; - unsigned long flags; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) { - if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - flags); - return tbl; - } - } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - return NULL; -} - -/* This function retrieves the pointer to an entry in Rx reordering - * table which matches the given TA and deletes it. - */ -void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta) -{ - struct mwifiex_rx_reorder_tbl *tbl, *tmp; - unsigned long flags; - - if (!ta) - return; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) { - if (!memcmp(tbl->ta, ta, ETH_ALEN)) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - flags); - mwifiex_del_rx_reorder_entry(priv, tbl); - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - } - } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - return; -} - -/* - * This function finds the last sequence number used in the packets - * buffered in Rx reordering table. - */ -static int -mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx) -{ - struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr; - struct mwifiex_private *priv = ctx->priv; - unsigned long flags; - int i; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) { - if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - flags); - return i; - } - } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - return -1; -} - -/* - * This function flushes all the packets in Rx reordering table. - * - * The function checks if any packets are currently buffered in the - * table or not. In case there are packets available, it dispatches - * them and then dumps the Rx reordering table. - */ -static void -mwifiex_flush_data(unsigned long context) -{ - struct reorder_tmr_cnxt *ctx = - (struct reorder_tmr_cnxt *) context; - int start_win, seq_num; - - ctx->timer_is_set = false; - seq_num = mwifiex_11n_find_last_seq_num(ctx); - - if (seq_num < 0) - return; - - mwifiex_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num); - start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1); - mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr, - start_win); -} - -/* - * This function creates an entry in Rx reordering table for the - * given TA/TID. - * - * The function also initializes the entry with sequence number, window - * size as well as initializes the timer. - * - * If the received TA/TID pair is already present, all the packets are - * dispatched and the window size is moved until the SSN. - */ -static void -mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, - int tid, int win_size, int seq_num) -{ - int i; - struct mwifiex_rx_reorder_tbl *tbl, *new_node; - u16 last_seq = 0; - unsigned long flags; - struct mwifiex_sta_node *node; - - /* - * If we get a TID, ta pair which is already present dispatch all the - * the packets and move the window size until the ssn - */ - tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); - if (tbl) { - mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num); - return; - } - /* if !tbl then create one */ - new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL); - if (!new_node) - return; - - INIT_LIST_HEAD(&new_node->list); - new_node->tid = tid; - memcpy(new_node->ta, ta, ETH_ALEN); - new_node->start_win = seq_num; - new_node->init_win = seq_num; - new_node->flags = 0; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - if (mwifiex_queuing_ra_based(priv)) { - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { - node = mwifiex_get_sta_entry(priv, ta); - if (node) - last_seq = node->rx_seq[tid]; - } - } else { - node = mwifiex_get_sta_entry(priv, ta); - if (node) - last_seq = node->rx_seq[tid]; - else - last_seq = priv->rx_seq[tid]; - } - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - mwifiex_dbg(priv->adapter, INFO, - "info: last_seq=%d start_win=%d\n", - last_seq, new_node->start_win); - - if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && - last_seq >= new_node->start_win) { - new_node->start_win = last_seq + 1; - new_node->flags |= RXREOR_INIT_WINDOW_SHIFT; - } - - new_node->win_size = win_size; - - new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size, - GFP_KERNEL); - if (!new_node->rx_reorder_ptr) { - kfree((u8 *) new_node); - mwifiex_dbg(priv->adapter, ERROR, - "%s: failed to alloc reorder_ptr\n", __func__); - return; - } - - new_node->timer_context.ptr = new_node; - new_node->timer_context.priv = priv; - new_node->timer_context.timer_is_set = false; - - setup_timer(&new_node->timer_context.timer, mwifiex_flush_data, - (unsigned long)&new_node->timer_context); - - for (i = 0; i < win_size; ++i) - new_node->rx_reorder_ptr[i] = NULL; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); -} - -static void -mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl) -{ - u32 min_flush_time; - - if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32) - min_flush_time = MIN_FLUSH_TIMER_15_MS; - else - min_flush_time = MIN_FLUSH_TIMER_MS; - - mod_timer(&tbl->timer_context.timer, - jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size)); - - tbl->timer_context.timer_is_set = true; -} - -/* - * This function prepares command for adding a BA request. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting add BA request buffer - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf) -{ - struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req; - - cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); - cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); - memcpy(add_ba_req, data_buf, sizeof(*add_ba_req)); - - return 0; -} - -/* - * This function prepares command for adding a BA response. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting add BA response buffer - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct host_cmd_ds_11n_addba_req - *cmd_addba_req) -{ - struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; - struct mwifiex_sta_node *sta_ptr; - u32 rx_win_size = priv->add_ba_param.rx_win_size; - u8 tid; - int win_size; - unsigned long flags; - uint16_t block_ack_param_set; - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - priv->adapter->is_hw_11ac_capable && - memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, - cmd_addba_req->peer_mac_addr); - if (!sta_ptr) { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - mwifiex_dbg(priv->adapter, ERROR, - "BA setup with unknown TDLS peer %pM!\n", - cmd_addba_req->peer_mac_addr); - return -1; - } - if (sta_ptr->is_11ac_enabled) - rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - } - - cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); - cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); - - memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr, - ETH_ALEN); - add_ba_rsp->dialog_token = cmd_addba_req->dialog_token; - add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo; - add_ba_rsp->ssn = cmd_addba_req->ssn; - - block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set); - tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) - >> BLOCKACKPARAM_TID_POS; - add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); - block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; - - /* If we don't support AMSDU inside AMPDU, reset the bit */ - if (!priv->add_ba_param.rx_amsdu || - (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)) - block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; - block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS; - add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); - win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) - & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) - >> BLOCKACKPARAM_WINSIZE_POS; - cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set); - - mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr, - tid, win_size, - le16_to_cpu(cmd_addba_req->ssn)); - return 0; -} - -/* - * This function prepares command for deleting a BA request. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting del BA request buffer - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) -{ - struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba; - - cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA); - cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); - memcpy(del_ba, data_buf, sizeof(*del_ba)); - - return 0; -} - -/* - * This function identifies if Rx reordering is needed for a received packet. - * - * In case reordering is required, the function will do the reordering - * before sending it to kernel. - * - * The Rx reorder table is checked first with the received TID/TA pair. If - * not found, the received packet is dispatched immediately. But if found, - * the packet is reordered and all the packets in the updated Rx reordering - * table is dispatched until a hole is found. - * - * For sequence number less than the starting window, the packet is dropped. - */ -int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, - u16 seq_num, u16 tid, - u8 *ta, u8 pkt_type, void *payload) -{ - struct mwifiex_rx_reorder_tbl *tbl; - int prev_start_win, start_win, end_win, win_size; - u16 pkt_index; - bool init_window_shift = false; - int ret = 0; - - tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); - if (!tbl) { - if (pkt_type != PKT_TYPE_BAR) - mwifiex_11n_dispatch_pkt(priv, payload); - return ret; - } - - if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) { - mwifiex_11n_dispatch_pkt(priv, payload); - return ret; - } - - start_win = tbl->start_win; - prev_start_win = start_win; - win_size = tbl->win_size; - end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); - if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) { - init_window_shift = true; - tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT; - } - - if (tbl->flags & RXREOR_FORCE_NO_DROP) { - mwifiex_dbg(priv->adapter, INFO, - "RXREOR_FORCE_NO_DROP when HS is activated\n"); - tbl->flags &= ~RXREOR_FORCE_NO_DROP; - } else if (init_window_shift && seq_num < start_win && - seq_num >= tbl->init_win) { - mwifiex_dbg(priv->adapter, INFO, - "Sender TID sequence number reset %d->%d for SSN %d\n", - start_win, seq_num, tbl->init_win); - tbl->start_win = start_win = seq_num; - end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); - } else { - /* - * If seq_num is less then starting win then ignore and drop - * the packet - */ - if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { - if (seq_num >= ((start_win + TWOPOW11) & - (MAX_TID_VALUE - 1)) && - seq_num < start_win) { - ret = -1; - goto done; - } - } else if ((seq_num < start_win) || - (seq_num >= (start_win + TWOPOW11))) { - ret = -1; - goto done; - } - } - - /* - * If this packet is a BAR we adjust seq_num as - * WinStart = seq_num - */ - if (pkt_type == PKT_TYPE_BAR) - seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1); - - if (((end_win < start_win) && - (seq_num < start_win) && (seq_num > end_win)) || - ((end_win > start_win) && ((seq_num > end_win) || - (seq_num < start_win)))) { - end_win = seq_num; - if (((end_win - win_size) + 1) >= 0) - start_win = (end_win - win_size) + 1; - else - start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1; - mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); - } - - if (pkt_type != PKT_TYPE_BAR) { - if (seq_num >= start_win) - pkt_index = seq_num - start_win; - else - pkt_index = (seq_num+MAX_TID_VALUE) - start_win; - - if (tbl->rx_reorder_ptr[pkt_index]) { - ret = -1; - goto done; - } - - tbl->rx_reorder_ptr[pkt_index] = payload; - } - - /* - * Dispatch all packets sequentially from start_win until a - * hole is found and adjust the start_win appropriately - */ - mwifiex_11n_scan_and_dispatch(priv, tbl); - -done: - if (!tbl->timer_context.timer_is_set || - prev_start_win != tbl->start_win) - mwifiex_11n_rxreorder_timer_restart(tbl); - return ret; -} - -/* - * This function deletes an entry for a given TID/TA pair. - * - * The TID/TA are taken from del BA event body. - */ -void -mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, - u8 type, int initiator) -{ - struct mwifiex_rx_reorder_tbl *tbl; - struct mwifiex_tx_ba_stream_tbl *ptx_tbl; - struct mwifiex_ra_list_tbl *ra_list; - u8 cleanup_rx_reorder_tbl; - unsigned long flags; - int tid_down; - - if (type == TYPE_DELBA_RECEIVE) - cleanup_rx_reorder_tbl = (initiator) ? true : false; - else - cleanup_rx_reorder_tbl = (initiator) ? false : true; - - mwifiex_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n", - peer_mac, tid, initiator); - - if (cleanup_rx_reorder_tbl) { - tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, - peer_mac); - if (!tbl) { - mwifiex_dbg(priv->adapter, EVENT, - "event: TID, TA not found in table\n"); - return; - } - mwifiex_del_rx_reorder_entry(priv, tbl); - } else { - ptx_tbl = mwifiex_get_ba_tbl(priv, tid, peer_mac); - if (!ptx_tbl) { - mwifiex_dbg(priv->adapter, EVENT, - "event: TID, RA not found in table\n"); - return; - } - - tid_down = mwifiex_wmm_downgrade_tid(priv, tid); - ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, peer_mac); - if (ra_list) { - ra_list->amsdu_in_ampdu = false; - ra_list->ba_status = BA_SETUP_NONE; - } - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - } -} - -/* - * This function handles the command response of an add BA response. - * - * Handling includes changing the header fields into CPU format and - * creating the stream, provided the add BA is accepted. - */ -int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; - int tid, win_size; - struct mwifiex_rx_reorder_tbl *tbl; - uint16_t block_ack_param_set; - - block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); - - tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) - >> BLOCKACKPARAM_TID_POS; - /* - * Check if we had rejected the ADDBA, if yes then do not create - * the stream - */ - if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { - mwifiex_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n", - add_ba_rsp->peer_mac_addr, tid); - - tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, - add_ba_rsp->peer_mac_addr); - if (tbl) - mwifiex_del_rx_reorder_entry(priv, tbl); - - return 0; - } - - win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) - >> BLOCKACKPARAM_WINSIZE_POS; - - tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, - add_ba_rsp->peer_mac_addr); - if (tbl) { - if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && - priv->add_ba_param.rx_amsdu && - (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) - tbl->amsdu = true; - else - tbl->amsdu = false; - } - - mwifiex_dbg(priv->adapter, CMD, - "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", - add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size); - - return 0; -} - -/* - * This function handles BA stream timeout event by preparing and sending - * a command to the firmware. - */ -void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, - struct host_cmd_ds_11n_batimeout *event) -{ - struct host_cmd_ds_11n_delba delba; - - memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba)); - memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN); - - delba.del_ba_param_set |= - cpu_to_le16((u16) event->tid << DELBA_TID_POS); - delba.del_ba_param_set |= cpu_to_le16( - (u16) event->origninator << DELBA_INITIATOR_POS); - delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); - mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false); -} - -/* - * This function cleans up the Rx reorder table by deleting all the entries - * and re-initializing. - */ -void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) -{ - struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node; - unsigned long flags; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_for_each_entry_safe(del_tbl_ptr, tmp_node, - &priv->rx_reorder_tbl_ptr, list) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr); - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - } - INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - mwifiex_reset_11n_rx_seq_num(priv); -} - -/* - * This function updates all rx_reorder_tbl's flags. - */ -void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags) -{ - struct mwifiex_private *priv; - struct mwifiex_rx_reorder_tbl *tbl; - unsigned long lock_flags; - int i; - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags); - if (list_empty(&priv->rx_reorder_tbl_ptr)) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - lock_flags); - continue; - } - - list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) - tbl->flags = flags; - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags); - } - - return; -} - -/* This function update all the rx_win_size based on coex flag - */ -static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter, - bool coex_flag) -{ - u8 i; - u32 rx_win_size; - struct mwifiex_private *priv; - - dev_dbg(adapter->dev, "Update rxwinsize %d\n", coex_flag); - - for (i = 0; i < adapter->priv_num; i++) { - if (!adapter->priv[i]) - continue; - priv = adapter->priv[i]; - rx_win_size = priv->add_ba_param.rx_win_size; - if (coex_flag) { - if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) - priv->add_ba_param.rx_win_size = - MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; - if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) - priv->add_ba_param.rx_win_size = - MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; - if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) - priv->add_ba_param.rx_win_size = - MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE; - } else { - if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) - priv->add_ba_param.rx_win_size = - MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; - if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) - priv->add_ba_param.rx_win_size = - MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; - if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) - priv->add_ba_param.rx_win_size = - MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE; - } - - if (adapter->coex_win_size && adapter->coex_rx_win_size) - priv->add_ba_param.rx_win_size = - adapter->coex_rx_win_size; - - if (rx_win_size != priv->add_ba_param.rx_win_size) { - if (!priv->media_connected) - continue; - for (i = 0; i < MAX_NUM_TID; i++) - mwifiex_11n_delba(priv, i); - } - } -} - -/* This function check coex for RX BA - */ -void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter) -{ - u8 i; - struct mwifiex_private *priv; - u8 count = 0; - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - priv = adapter->priv[i]; - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { - if (priv->media_connected) - count++; - } - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - if (priv->bss_started) - count++; - } - } - if (count >= MWIFIEX_BSS_COEX_COUNT) - break; - } - if (count >= MWIFIEX_BSS_COEX_COUNT) - mwifiex_update_ampdu_rxwinsize(adapter, true); - else - mwifiex_update_ampdu_rxwinsize(adapter, false); -} diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h deleted file mode 100644 index 63ecea89b4ab..000000000000 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n RX Re-ordering - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_11N_RXREORDER_H_ -#define _MWIFIEX_11N_RXREORDER_H_ - -#define MIN_FLUSH_TIMER_MS 50 -#define MIN_FLUSH_TIMER_15_MS 15 -#define MWIFIEX_BA_WIN_SIZE_32 32 - -#define PKT_TYPE_BAR 0xE7 -#define MAX_TID_VALUE (2 << 11) -#define TWOPOW11 (2 << 10) - -#define BLOCKACKPARAM_TID_POS 2 -#define BLOCKACKPARAM_AMSDU_SUPP_MASK 0x1 -#define BLOCKACKPARAM_WINSIZE_POS 6 -#define DELBA_TID_POS 12 -#define DELBA_INITIATOR_POS 11 -#define TYPE_DELBA_SENT 1 -#define TYPE_DELBA_RECEIVE 2 -#define IMMEDIATE_BLOCK_ACK 0x2 - -#define ADDBA_RSP_STATUS_ACCEPT 0 - -#define MWIFIEX_DEF_11N_RX_SEQ_NUM 0xffff -#define BA_SETUP_MAX_PACKET_THRESHOLD 16 -#define BA_SETUP_PACKET_OFFSET 16 - -enum mwifiex_rxreor_flags { - RXREOR_FORCE_NO_DROP = 1<<0, - RXREOR_INIT_WINDOW_SHIFT = 1<<1, -}; - -static inline void mwifiex_reset_11n_rx_seq_num(struct mwifiex_private *priv) -{ - memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq)); -} - -int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *, - u16 seqNum, - u16 tid, u8 *ta, - u8 pkttype, void *payload); -void mwifiex_del_ba_tbl(struct mwifiex_private *priv, int Tid, - u8 *PeerMACAddr, u8 type, int initiator); -void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, - struct host_cmd_ds_11n_batimeout *event); -int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, - struct host_cmd_ds_command - *resp); -int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, - void *data_buf); -int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct host_cmd_ds_11n_addba_req - *cmd_addba_req); -int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, - void *data_buf); -void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv); -struct mwifiex_rx_reorder_tbl *mwifiex_11n_get_rxreorder_tbl(struct - mwifiex_private - *priv, int tid, - u8 *ta); -struct mwifiex_rx_reorder_tbl * -mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta); -void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta); -void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags); - -#endif /* _MWIFIEX_11N_RXREORDER_H_ */ diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig deleted file mode 100644 index 279167ddd293..000000000000 --- a/drivers/net/wireless/mwifiex/Kconfig +++ /dev/null @@ -1,44 +0,0 @@ -config MWIFIEX - tristate "Marvell WiFi-Ex Driver" - depends on CFG80211 - ---help--- - This adds support for wireless adapters based on Marvell - 802.11n/ac chipsets. - - If you choose to build it as a module, it will be called - mwifiex. - -config MWIFIEX_SDIO - tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897/SD8997" - depends on MWIFIEX && MMC - select FW_LOADER - select WANT_DEV_COREDUMP - ---help--- - This adds support for wireless adapters based on Marvell - 8786/8787/8797/8887/8897/8997 chipsets with SDIO interface. - - If you choose to build it as a module, it will be called - mwifiex_sdio. - -config MWIFIEX_PCIE - tristate "Marvell WiFi-Ex Driver for PCIE 8766/8897/8997" - depends on MWIFIEX && PCI - select FW_LOADER - select WANT_DEV_COREDUMP - ---help--- - This adds support for wireless adapters based on Marvell - 8766/8897/8997 chipsets with PCIe interface. - - If you choose to build it as a module, it will be called - mwifiex_pcie. - -config MWIFIEX_USB - tristate "Marvell WiFi-Ex Driver for USB8766/8797/8997" - depends on MWIFIEX && USB - select FW_LOADER - ---help--- - This adds support for wireless adapters based on Marvell - 8797/8997 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/Makefile b/drivers/net/wireless/mwifiex/Makefile deleted file mode 100644 index fdfd9bf15ed4..000000000000 --- a/drivers/net/wireless/mwifiex/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - - -mwifiex-y += main.o -mwifiex-y += init.o -mwifiex-y += cfp.o -mwifiex-y += cmdevt.o -mwifiex-y += util.o -mwifiex-y += txrx.o -mwifiex-y += wmm.o -mwifiex-y += 11n.o -mwifiex-y += 11ac.o -mwifiex-y += 11n_aggr.o -mwifiex-y += 11n_rxreorder.o -mwifiex-y += scan.o -mwifiex-y += join.o -mwifiex-y += sta_ioctl.o -mwifiex-y += sta_cmd.o -mwifiex-y += uap_cmd.o -mwifiex-y += ie.o -mwifiex-y += sta_cmdresp.o -mwifiex-y += sta_event.o -mwifiex-y += uap_event.o -mwifiex-y += sta_tx.o -mwifiex-y += sta_rx.o -mwifiex-y += uap_txrx.o -mwifiex-y += cfg80211.o -mwifiex-y += ethtool.o -mwifiex-y += 11h.o -mwifiex-y += tdls.o -mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o -obj-$(CONFIG_MWIFIEX) += mwifiex.o - -mwifiex_sdio-y += sdio.o -obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o - -mwifiex_pcie-y += pcie.o -obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o - -mwifiex_usb-y += usb.o -obj-$(CONFIG_MWIFIEX_USB) += mwifiex_usb.o - -ccflags-y += -D__CHECK_ENDIAN diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/mwifiex/README deleted file mode 100644 index 2f0f9b5609d0..000000000000 --- a/drivers/net/wireless/mwifiex/README +++ /dev/null @@ -1,240 +0,0 @@ -# Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - - -=============================================================================== - U S E R M A N U A L - -1) FOR DRIVER INSTALL - - a) Copy sd8787.bin to /lib/firmware/mrvl/ directory, - create the directory if it doesn't exist. - b) Install WLAN driver, - insmod mwifiex.ko - c) Uninstall WLAN driver, - ifconfig mlanX down - rmmod mwifiex - - -2) FOR DRIVER CONFIGURATION AND INFO - The configurations can be done either using the 'iw' user space - utility or debugfs. - - a) 'iw' utility commands - - Following are some useful iw commands:- - -iw dev mlan0 scan - - This command will trigger a scan. - The command will then display the scan table entries - -iw dev mlan0 connect -w [] [] [key 0:abcde d:1123456789a] - The above command can be used to connect to an AP with a particular SSID. - Ap's operating frequency can be specified or even the bssid. If the AP is using - WEP encryption, wep keys can be specified in the command. - Note: Every time before connecting to an AP scan command (iw dev mlan0 scan) should be used by user. - -iw dev mlan0 disconnect - This command will be used to disconnect from an AP. - - -iw dev mlan0 ibss join [fixed-freq] [fixed-bssid] [key 0:abcde] - The command will be used to join or create an ibss. Optionally, operating frequency, - bssid and the security related parameters can be specified while joining/creating - and ibss. - -iw dev mlan0 ibss leave - The command will be used to leave an ibss network. - -iw dev mlan0 link - The command will be used to get the connection status. The command will return parameters - such as SSID, operating frequency, rx/tx packets, signal strength, tx bitrate. - - Apart from the iw utility all standard configurations using the 'iwconfig' utility are also supported. - - b) Debugfs interface - - The debugfs interface can be used for configurations and for getting - some useful information from the driver. - The section below explains the configurations that can be - done. - - Mount debugfs to /debugfs mount point: - - mkdir /debugfs - mount -t debugfs debugfs /debugfs - - The information is provided in /debugfs/mwifiex/mlanX/: - -iw reg set - The command will be used to change the regulatory domain. - -iw reg get - The command will be used to get current regulatory domain. - -info - This command is used to get driver info. - - Usage: - cat info - - driver_name = "mwifiex" - driver_version = - interface_name = "mlanX" - bss_mode = "Ad-hoc" | "Managed" | "Auto" | "Unknown" - media_state = "Disconnected" | "Connected" - mac_address = <6-byte adapter MAC address> - multicase_count = - essid = - bssid = - channel = - region_code = - multicasr_address[n] = - num_tx_bytes = - num_rx_bytes = - num_tx_pkts = - num_rx_pkts = - num_tx_pkts_dropped = - num_rx_pkts_dropped = - num_tx_pkts_err = - num_rx_pkts_err = - carrier "on" | "off" - tx queue "stopped" | "started" - - The following debug info are provided in /debugfs/mwifiex/mlanX/debug: - - int_counter = - wmm_ac_vo = - wmm_ac_vi = - wmm_ac_be = - wmm_ac_bk = - tx_buf_size = - curr_tx_buf_size = - ps_mode = <0/1, CAM mode/PS mode> - ps_state = <0/1/2/3, full power state/awake state/pre-sleep state/sleep state> - is_deep_sleep = <0/1, not deep sleep state/deep sleep state> - wakeup_dev_req = <0/1, wakeup device not required/required> - wakeup_tries = - hs_configured = <0/1, host sleep not configured/configured> - hs_activated = <0/1, extended host sleep not activated/activated> - num_tx_timeout = - is_cmd_timedout = <0/1 command timeout not occurred/occurred> - timeout_cmd_id = - timeout_cmd_act = - last_cmd_id = - last_cmd_act = - last_cmd_index = <0 based last command index> - last_cmd_resp_id = - last_cmd_resp_index = <0 based last command response index> - last_event = - last_event_index = <0 based last event index> - num_cmd_h2c_fail = - num_cmd_sleep_cfm_fail = - num_tx_h2c_fail = - num_evt_deauth = - num_evt_disassoc = - num_evt_link_lost = - num_cmd_deauth = - num_cmd_assoc_ok = - num_cmd_assoc_fail = - cmd_sent = <0/1, send command resources available/sending command to device> - data_sent = <0/1, send data resources available/sending data to device> - mp_rd_bitmap = - mp_wr_bitmap = - cmd_resp_received = <0/1, no cmd response to process/response received and yet to process> - event_received = <0/1, no event to process/event received and yet to process> - cmd_pending = - tx_pending = - rx_pending = - - -3) FOR DRIVER CONFIGURATION - -regrdwr - This command is used to read/write the adapter register. - - Usage: - echo " [value]" > regrdwr - cat regrdwr - - where the parameters are, - : 1:MAC/SOC, 2:BBP, 3:RF, 4:PMIC, 5:CAU - : offset of register - [value]: value to be written - - Examples: - echo "1 0xa060" > regrdwr : Read the MAC register - echo "1 0xa060 0x12" > regrdwr : Write the MAC register - echo "1 0xa794 0x80000000" > regrdwr - : Write 0x80000000 to MAC register -rdeeprom - This command is used to read the EEPROM contents of the card. - - Usage: - echo " " > rdeeprom - cat rdeeprom - - where the parameters are, - : multiples of 4 - : 4-20, multiples of 4 - - Example: - echo "0 20" > rdeeprom : Read 20 bytes of EEPROM data from offset 0 - -hscfg - This command is used to debug/simulate host sleep feature using - different configuration parameters. - - Usage: - echo " [GPIO# [gap]]]" > hscfg - cat hscfg - - where the parameters are, - : bit 0 = 1 -- broadcast data - bit 1 = 1 -- unicast data - bit 2 = 1 -- mac event - bit 3 = 1 -- multicast data - [GPIO#]: pin number of GPIO used to wakeup the host. - GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO - will be used instead). - [gap]: the gap in milliseconds between wakeup signal and - wakeup event or 0xff for special setting (host - acknowledge required) when GPIO is used to wakeup host. - - Examples: - echo "-1" > hscfg : Cancel host sleep mode - echo "3" > hscfg : Broadcast and unicast data; - Use GPIO and gap set previously - echo "2 3" > hscfg : Unicast data and GPIO 3; - Use gap set previously - echo "2 1 160" > hscfg : Unicast data, GPIO 1 and gap 160 ms - echo "2 1 0xff" > hscfg : Unicast data, GPIO 1; Wait for host - to ack before sending wakeup event - -getlog - This command is used to get the statistics available in the station. - Usage: - - cat getlog - -device_dump - This command is used to dump driver information and firmware memory - segments. - Usage: - - cat fw_dump - -=============================================================================== diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c deleted file mode 100644 index 4073116e6e9f..000000000000 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ /dev/null @@ -1,3887 +0,0 @@ -/* - * Marvell Wireless LAN device driver: CFG80211 - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "cfg80211.h" -#include "main.h" -#include "11n.h" - -static char *reg_alpha2; -module_param(reg_alpha2, charp, 0); - -static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { - { - .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT), - }, - { - .max = 1, .types = BIT(NL80211_IFTYPE_AP), - }, -}; - -static const struct ieee80211_iface_combination -mwifiex_iface_comb_ap_sta = { - .limits = mwifiex_ap_sta_limits, - .num_different_channels = 1, - .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), - .max_interfaces = MWIFIEX_MAX_BSS_NUM, - .beacon_int_infra_match = true, - .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40), -}; - -static const struct ieee80211_iface_combination -mwifiex_iface_comb_ap_sta_vht = { - .limits = mwifiex_ap_sta_limits, - .num_different_channels = 1, - .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), - .max_interfaces = MWIFIEX_MAX_BSS_NUM, - .beacon_int_infra_match = true, - .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40) | - BIT(NL80211_CHAN_WIDTH_80), -}; - -static const struct -ieee80211_iface_combination mwifiex_iface_comb_ap_sta_drcs = { - .limits = mwifiex_ap_sta_limits, - .num_different_channels = 2, - .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), - .max_interfaces = MWIFIEX_MAX_BSS_NUM, - .beacon_int_infra_match = true, -}; - -/* - * This function maps the nl802.11 channel type into driver channel type. - * - * The mapping is as follows - - * NL80211_CHAN_NO_HT -> IEEE80211_HT_PARAM_CHA_SEC_NONE - * NL80211_CHAN_HT20 -> IEEE80211_HT_PARAM_CHA_SEC_NONE - * NL80211_CHAN_HT40PLUS -> IEEE80211_HT_PARAM_CHA_SEC_ABOVE - * NL80211_CHAN_HT40MINUS -> IEEE80211_HT_PARAM_CHA_SEC_BELOW - * Others -> IEEE80211_HT_PARAM_CHA_SEC_NONE - */ -u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type) -{ - switch (chan_type) { - case NL80211_CHAN_NO_HT: - case NL80211_CHAN_HT20: - return IEEE80211_HT_PARAM_CHA_SEC_NONE; - case NL80211_CHAN_HT40PLUS: - return IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - case NL80211_CHAN_HT40MINUS: - return IEEE80211_HT_PARAM_CHA_SEC_BELOW; - default: - return IEEE80211_HT_PARAM_CHA_SEC_NONE; - } -} - -/* This function maps IEEE HT secondary channel type to NL80211 channel type - */ -u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset) -{ - switch (second_chan_offset) { - case IEEE80211_HT_PARAM_CHA_SEC_NONE: - return NL80211_CHAN_HT20; - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - return NL80211_CHAN_HT40PLUS; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - return NL80211_CHAN_HT40MINUS; - default: - return NL80211_CHAN_HT20; - } -} - -/* - * This function checks whether WEP is set. - */ -static int -mwifiex_is_alg_wep(u32 cipher) -{ - switch (cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - return 1; - default: - break; - } - - return 0; -} - -/* - * This function retrieves the private structure from kernel wiphy structure. - */ -static void *mwifiex_cfg80211_get_adapter(struct wiphy *wiphy) -{ - return (void *) (*(unsigned long *) wiphy_priv(wiphy)); -} - -/* - * CFG802.11 operation handler to delete a network key. - */ -static int -mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, - u8 key_index, bool pairwise, const u8 *mac_addr) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); - const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - const u8 *peer_mac = pairwise ? mac_addr : bc_mac; - - if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1)) { - mwifiex_dbg(priv->adapter, ERROR, "deleting the crypto keys\n"); - return -EFAULT; - } - - mwifiex_dbg(priv->adapter, INFO, "info: crypto keys deleted\n"); - return 0; -} - -/* - * This function forms an skb for management frame. - */ -static int -mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) -{ - u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - u16 pkt_len; - u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT; - - pkt_len = len + ETH_ALEN; - - skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); - memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len)); - - memcpy(skb_push(skb, sizeof(tx_control)), - &tx_control, sizeof(tx_control)); - - memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type)); - - /* Add packet data and address4 */ - memcpy(skb_put(skb, sizeof(struct ieee80211_hdr_3addr)), buf, - sizeof(struct ieee80211_hdr_3addr)); - memcpy(skb_put(skb, ETH_ALEN), addr, ETH_ALEN); - memcpy(skb_put(skb, len - sizeof(struct ieee80211_hdr_3addr)), - buf + sizeof(struct ieee80211_hdr_3addr), - len - sizeof(struct ieee80211_hdr_3addr)); - - skb->priority = LOW_PRIO_TID; - __net_timestamp(skb); - - return 0; -} - -/* - * CFG802.11 operation handler to transmit a management frame. - */ -static int -mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) -{ - const u8 *buf = params->buf; - size_t len = params->len; - struct sk_buff *skb; - u16 pkt_len; - const struct ieee80211_mgmt *mgmt; - struct mwifiex_txinfo *tx_info; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - - if (!buf || !len) { - mwifiex_dbg(priv->adapter, ERROR, "invalid buffer and length\n"); - return -EFAULT; - } - - mgmt = (const struct ieee80211_mgmt *)buf; - if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA && - ieee80211_is_probe_resp(mgmt->frame_control)) { - /* Since we support offload probe resp, we need to skip probe - * resp in AP or GO mode */ - mwifiex_dbg(priv->adapter, INFO, - "info: skip to send probe resp in AP or GO mode\n"); - return 0; - } - - pkt_len = len + ETH_ALEN; - skb = dev_alloc_skb(MWIFIEX_MIN_DATA_HEADER_LEN + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + - pkt_len + sizeof(pkt_len)); - - if (!skb) { - mwifiex_dbg(priv->adapter, ERROR, - "allocate skb failed for management frame\n"); - return -ENOMEM; - } - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - tx_info->pkt_len = pkt_len; - - mwifiex_form_mgmt_frame(skb, buf, len); - *cookie = prandom_u32() | 1; - - if (ieee80211_is_action(mgmt->frame_control)) - skb = mwifiex_clone_skb_for_tx_status(priv, - skb, - MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, cookie); - else - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, - GFP_ATOMIC); - - mwifiex_queue_tx_pkt(priv, skb); - - mwifiex_dbg(priv->adapter, INFO, "info: management frame transmitted\n"); - return 0; -} - -/* - * CFG802.11 operation handler to register a mgmt frame. - */ -static void -mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy, - struct wireless_dev *wdev, - u16 frame_type, bool reg) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - u32 mask; - - if (reg) - mask = priv->mgmt_frame_mask | BIT(frame_type >> 4); - else - mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4); - - if (mask != priv->mgmt_frame_mask) { - priv->mgmt_frame_mask = mask; - mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, - HostCmd_ACT_GEN_SET, 0, - &priv->mgmt_frame_mask, false); - mwifiex_dbg(priv->adapter, INFO, "info: mgmt frame registered\n"); - } -} - -/* - * CFG802.11 operation handler to remain on channel. - */ -static int -mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, - struct wireless_dev *wdev, - struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - int ret; - - if (!chan || !cookie) { - mwifiex_dbg(priv->adapter, ERROR, "Invalid parameter for ROC\n"); - return -EINVAL; - } - - if (priv->roc_cfg.cookie) { - mwifiex_dbg(priv->adapter, INFO, - "info: ongoing ROC, cookie = 0x%llx\n", - priv->roc_cfg.cookie); - return -EBUSY; - } - - ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, chan, - duration); - - if (!ret) { - *cookie = prandom_u32() | 1; - priv->roc_cfg.cookie = *cookie; - priv->roc_cfg.chan = *chan; - - cfg80211_ready_on_channel(wdev, *cookie, chan, - duration, GFP_ATOMIC); - - mwifiex_dbg(priv->adapter, INFO, - "info: ROC, cookie = 0x%llx\n", *cookie); - } - - return ret; -} - -/* - * CFG802.11 operation handler to cancel remain on channel. - */ -static int -mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, - struct wireless_dev *wdev, u64 cookie) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - int ret; - - if (cookie != priv->roc_cfg.cookie) - return -ENOENT; - - ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_REMOVE, - &priv->roc_cfg.chan, 0); - - if (!ret) { - cfg80211_remain_on_channel_expired(wdev, cookie, - &priv->roc_cfg.chan, - GFP_ATOMIC); - - memset(&priv->roc_cfg, 0, sizeof(struct mwifiex_roc_cfg)); - - mwifiex_dbg(priv->adapter, INFO, - "info: cancel ROC, cookie = 0x%llx\n", cookie); - } - - return ret; -} - -/* - * CFG802.11 operation handler to set Tx power. - */ -static int -mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, - struct wireless_dev *wdev, - enum nl80211_tx_power_setting type, - int mbm) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv; - struct mwifiex_power_cfg power_cfg; - int dbm = MBM_TO_DBM(mbm); - - if (type == NL80211_TX_POWER_FIXED) { - power_cfg.is_power_auto = 0; - power_cfg.power_level = dbm; - } else { - power_cfg.is_power_auto = 1; - } - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - return mwifiex_set_tx_power(priv, &power_cfg); -} - -/* - * CFG802.11 operation handler to set Power Save option. - * - * The timeout value, if provided, is currently ignored. - */ -static int -mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, - struct net_device *dev, - bool enabled, int timeout) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - u32 ps_mode; - - if (timeout) - mwifiex_dbg(priv->adapter, INFO, - "info: ignore timeout value for IEEE Power Save\n"); - - ps_mode = enabled; - - return mwifiex_drv_set_power(priv, &ps_mode); -} - -/* - * CFG802.11 operation handler to set the default network key. - */ -static int -mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev, - u8 key_index, bool unicast, - bool multicast) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); - - /* Return if WEP key not configured */ - if (!priv->sec_info.wep_enabled) - return 0; - - if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) { - priv->wep_key_curr_index = key_index; - } else if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, - NULL, 0)) { - mwifiex_dbg(priv->adapter, ERROR, "set default Tx key index\n"); - return -EFAULT; - } - - return 0; -} - -/* - * CFG802.11 operation handler to add a network key. - */ -static int -mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, - u8 key_index, bool pairwise, const u8 *mac_addr, - struct key_params *params) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); - struct mwifiex_wep_key *wep_key; - const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - const u8 *peer_mac = pairwise ? mac_addr : bc_mac; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP && - (params->cipher == WLAN_CIPHER_SUITE_WEP40 || - params->cipher == WLAN_CIPHER_SUITE_WEP104)) { - if (params->key && params->key_len) { - wep_key = &priv->wep_key[key_index]; - memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); - memcpy(wep_key->key_material, params->key, - params->key_len); - wep_key->key_index = key_index; - wep_key->key_length = params->key_len; - priv->sec_info.wep_enabled = 1; - } - return 0; - } - - if (mwifiex_set_encode(priv, params, params->key, params->key_len, - key_index, peer_mac, 0)) { - mwifiex_dbg(priv->adapter, ERROR, "crypto keys added\n"); - return -EFAULT; - } - - return 0; -} - -/* - * This function sends domain information to the firmware. - * - * The following information are passed to the firmware - - * - Country codes - * - Sub bands (first channel, number of channels, maximum Tx power) - */ -int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) -{ - u8 no_of_triplet = 0; - struct ieee80211_country_ie_triplet *t; - u8 no_of_parsed_chan = 0; - u8 first_chan = 0, next_chan = 0, max_pwr = 0; - u8 i, flag = 0; - enum ieee80211_band band; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *ch; - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv; - struct mwifiex_802_11d_domain_reg *domain_info = &adapter->domain_reg; - - /* Set country code */ - domain_info->country_code[0] = adapter->country_code[0]; - domain_info->country_code[1] = adapter->country_code[1]; - domain_info->country_code[2] = ' '; - - band = mwifiex_band_to_radio_type(adapter->config_bands); - if (!wiphy->bands[band]) { - mwifiex_dbg(adapter, ERROR, - "11D: setting domain info in FW\n"); - return -1; - } - - sband = wiphy->bands[band]; - - for (i = 0; i < sband->n_channels ; i++) { - ch = &sband->channels[i]; - if (ch->flags & IEEE80211_CHAN_DISABLED) - continue; - - if (!flag) { - flag = 1; - first_chan = (u32) ch->hw_value; - next_chan = first_chan; - max_pwr = ch->max_power; - no_of_parsed_chan = 1; - continue; - } - - if (ch->hw_value == next_chan + 1 && - ch->max_power == max_pwr) { - next_chan++; - no_of_parsed_chan++; - } else { - t = &domain_info->triplet[no_of_triplet]; - t->chans.first_channel = first_chan; - t->chans.num_channels = no_of_parsed_chan; - t->chans.max_power = max_pwr; - no_of_triplet++; - first_chan = (u32) ch->hw_value; - next_chan = first_chan; - max_pwr = ch->max_power; - no_of_parsed_chan = 1; - } - } - - if (flag) { - t = &domain_info->triplet[no_of_triplet]; - t->chans.first_channel = first_chan; - t->chans.num_channels = no_of_parsed_chan; - t->chans.max_power = max_pwr; - no_of_triplet++; - } - - domain_info->no_of_triplet = no_of_triplet; - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, - HostCmd_ACT_GEN_SET, 0, NULL, false)) { - mwifiex_dbg(adapter, INFO, - "11D: setting domain info in FW\n"); - return -1; - } - - return 0; -} - -/* - * CFG802.11 regulatory domain callback function. - * - * This function is called when the regulatory domain is changed due to the - * following reasons - - * - Set by driver - * - Set by system core - * - Set by user - * - Set bt Country IE - */ -static void mwifiex_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv = mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY); - mwifiex_dbg(adapter, INFO, - "info: cfg80211 regulatory domain callback for %c%c\n", - request->alpha2[0], request->alpha2[1]); - - switch (request->initiator) { - case NL80211_REGDOM_SET_BY_DRIVER: - case NL80211_REGDOM_SET_BY_CORE: - case NL80211_REGDOM_SET_BY_USER: - case NL80211_REGDOM_SET_BY_COUNTRY_IE: - break; - default: - mwifiex_dbg(adapter, ERROR, - "unknown regdom initiator: %d\n", - request->initiator); - return; - } - - /* Don't send world or same regdom info to firmware */ - if (strncmp(request->alpha2, "00", 2) && - strncmp(request->alpha2, adapter->country_code, - sizeof(request->alpha2))) { - memcpy(adapter->country_code, request->alpha2, - sizeof(request->alpha2)); - mwifiex_send_domain_info_cmd_fw(wiphy); - mwifiex_dnld_txpwr_table(priv); - } -} - -/* - * This function sets the fragmentation threshold. - * - * The fragmentation threshold value must lie between MWIFIEX_FRAG_MIN_VALUE - * and MWIFIEX_FRAG_MAX_VALUE. - */ -static int -mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr) -{ - if (frag_thr < MWIFIEX_FRAG_MIN_VALUE || - frag_thr > MWIFIEX_FRAG_MAX_VALUE) - frag_thr = MWIFIEX_FRAG_MAX_VALUE; - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, FRAG_THRESH_I, - &frag_thr, true); -} - -/* - * This function sets the RTS threshold. - - * The rts value must lie between MWIFIEX_RTS_MIN_VALUE - * and MWIFIEX_RTS_MAX_VALUE. - */ -static int -mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr) -{ - if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE) - rts_thr = MWIFIEX_RTS_MAX_VALUE; - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, RTS_THRESH_I, - &rts_thr, true); -} - -/* - * CFG802.11 operation handler to set wiphy parameters. - * - * This function can be used to set the RTS threshold and the - * Fragmentation threshold of the driver. - */ -static int -mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv; - struct mwifiex_uap_bss_param *bss_cfg; - int ret; - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - switch (priv->bss_role) { - case MWIFIEX_BSS_ROLE_UAP: - if (priv->bss_started) { - mwifiex_dbg(adapter, ERROR, - "cannot change wiphy params when bss started"); - return -EINVAL; - } - - bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL); - if (!bss_cfg) - return -ENOMEM; - - mwifiex_set_sys_config_invalid_data(bss_cfg); - - if (changed & WIPHY_PARAM_RTS_THRESHOLD) - bss_cfg->rts_threshold = wiphy->rts_threshold; - if (changed & WIPHY_PARAM_FRAG_THRESHOLD) - bss_cfg->frag_threshold = wiphy->frag_threshold; - if (changed & WIPHY_PARAM_RETRY_LONG) - bss_cfg->retry_limit = wiphy->retry_long; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_BSS_PARAMS_I, bss_cfg, - false); - - kfree(bss_cfg); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "Failed to set wiphy phy params\n"); - return ret; - } - break; - - case MWIFIEX_BSS_ROLE_STA: - if (priv->media_connected) { - mwifiex_dbg(adapter, ERROR, - "cannot change wiphy params when connected"); - return -EINVAL; - } - if (changed & WIPHY_PARAM_RTS_THRESHOLD) { - ret = mwifiex_set_rts(priv, - wiphy->rts_threshold); - if (ret) - return ret; - } - if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { - ret = mwifiex_set_frag(priv, - wiphy->frag_threshold); - if (ret) - return ret; - } - break; - } - - return 0; -} - -static int -mwifiex_cfg80211_deinit_p2p(struct mwifiex_private *priv) -{ - u16 mode = P2P_MODE_DISABLE; - - if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode, true)) - return -1; - - return 0; -} - -/* - * This function initializes the functionalities for P2P client. - * The P2P client initialization sequence is: - * disable -> device -> client - */ -static int -mwifiex_cfg80211_init_p2p_client(struct mwifiex_private *priv) -{ - u16 mode; - - if (mwifiex_cfg80211_deinit_p2p(priv)) - return -1; - - mode = P2P_MODE_DEVICE; - if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode, true)) - return -1; - - mode = P2P_MODE_CLIENT; - if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode, true)) - return -1; - - return 0; -} - -/* - * This function initializes the functionalities for P2P GO. - * The P2P GO initialization sequence is: - * disable -> device -> GO - */ -static int -mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv) -{ - u16 mode; - - if (mwifiex_cfg80211_deinit_p2p(priv)) - return -1; - - mode = P2P_MODE_DEVICE; - if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode, true)) - return -1; - - mode = P2P_MODE_GO; - if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode, true)) - return -1; - - return 0; -} - -static int mwifiex_deinit_priv_params(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - unsigned long flags; - - priv->mgmt_frame_mask = 0; - if (mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, - HostCmd_ACT_GEN_SET, 0, - &priv->mgmt_frame_mask, false)) { - mwifiex_dbg(adapter, ERROR, - "could not unregister mgmt frame rx\n"); - return -1; - } - - mwifiex_deauthenticate(priv, NULL); - - spin_lock_irqsave(&adapter->main_proc_lock, flags); - adapter->main_locked = true; - if (adapter->mwifiex_processing) { - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - flush_workqueue(adapter->workqueue); - } else { - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - } - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - adapter->rx_locked = true; - if (adapter->rx_processing) { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - flush_workqueue(adapter->rx_workqueue); - } else { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - } - - mwifiex_free_priv(priv); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; - - return 0; -} - -static int -mwifiex_init_new_priv_params(struct mwifiex_private *priv, - struct net_device *dev, - enum nl80211_iftype type) -{ - struct mwifiex_adapter *adapter = priv->adapter; - unsigned long flags; - - mwifiex_init_priv(priv); - - priv->bss_mode = type; - priv->wdev.iftype = type; - - mwifiex_init_priv_params(priv, priv->netdev); - priv->bss_started = 0; - - switch (type) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - priv->bss_role = MWIFIEX_BSS_ROLE_STA; - priv->bss_type = MWIFIEX_BSS_TYPE_STA; - break; - case NL80211_IFTYPE_P2P_CLIENT: - priv->bss_role = MWIFIEX_BSS_ROLE_STA; - priv->bss_type = MWIFIEX_BSS_TYPE_P2P; - break; - case NL80211_IFTYPE_P2P_GO: - priv->bss_role = MWIFIEX_BSS_ROLE_UAP; - priv->bss_type = MWIFIEX_BSS_TYPE_P2P; - break; - case NL80211_IFTYPE_AP: - priv->bss_type = MWIFIEX_BSS_TYPE_UAP; - priv->bss_role = MWIFIEX_BSS_ROLE_UAP; - break; - default: - mwifiex_dbg(adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - - spin_lock_irqsave(&adapter->main_proc_lock, flags); - adapter->main_locked = false; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - adapter->rx_locked = false; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - - return 0; -} - -static int -mwifiex_change_vif_to_p2p(struct net_device *dev, - enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct mwifiex_private *priv; - struct mwifiex_adapter *adapter; - - priv = mwifiex_netdev_get_priv(dev); - - if (!priv) - return -1; - - adapter = priv->adapter; - - if (adapter->curr_iface_comb.p2p_intf == - adapter->iface_limit.p2p_intf) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple P2P ifaces\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "%s: changing role to p2p\n", dev->name); - - if (mwifiex_deinit_priv_params(priv)) - return -1; - if (mwifiex_init_new_priv_params(priv, dev, type)) - return -1; - - switch (type) { - case NL80211_IFTYPE_P2P_CLIENT: - if (mwifiex_cfg80211_init_p2p_client(priv)) - return -EFAULT; - break; - case NL80211_IFTYPE_P2P_GO: - if (mwifiex_cfg80211_init_p2p_go(priv)) - return -EFAULT; - break; - default: - mwifiex_dbg(adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - - if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, true)) - return -1; - - if (mwifiex_sta_init_cmd(priv, false, false)) - return -1; - - switch (curr_iftype) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - adapter->curr_iface_comb.sta_intf--; - break; - case NL80211_IFTYPE_AP: - adapter->curr_iface_comb.uap_intf--; - break; - default: - break; - } - - adapter->curr_iface_comb.p2p_intf++; - dev->ieee80211_ptr->iftype = type; - - return 0; -} - -static int -mwifiex_change_vif_to_sta_adhoc(struct net_device *dev, - enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct mwifiex_private *priv; - struct mwifiex_adapter *adapter; - - priv = mwifiex_netdev_get_priv(dev); - - if (!priv) - return -1; - - adapter = priv->adapter; - - if ((curr_iftype != NL80211_IFTYPE_P2P_CLIENT && - curr_iftype != NL80211_IFTYPE_P2P_GO) && - (adapter->curr_iface_comb.sta_intf == - adapter->iface_limit.sta_intf)) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple station/adhoc ifaces\n"); - return -1; - } - - if (type == NL80211_IFTYPE_STATION) - mwifiex_dbg(adapter, INFO, - "%s: changing role to station\n", dev->name); - else - mwifiex_dbg(adapter, INFO, - "%s: changing role to adhoc\n", dev->name); - - if (mwifiex_deinit_priv_params(priv)) - return -1; - if (mwifiex_init_new_priv_params(priv, dev, type)) - return -1; - if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, true)) - return -1; - if (mwifiex_sta_init_cmd(priv, false, false)) - return -1; - - switch (curr_iftype) { - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - adapter->curr_iface_comb.p2p_intf--; - break; - case NL80211_IFTYPE_AP: - adapter->curr_iface_comb.uap_intf--; - break; - default: - break; - } - - adapter->curr_iface_comb.sta_intf++; - dev->ieee80211_ptr->iftype = type; - return 0; -} - -static int -mwifiex_change_vif_to_ap(struct net_device *dev, - enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct mwifiex_private *priv; - struct mwifiex_adapter *adapter; - - priv = mwifiex_netdev_get_priv(dev); - - if (!priv) - return -1; - - adapter = priv->adapter; - - if (adapter->curr_iface_comb.uap_intf == - adapter->iface_limit.uap_intf) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple AP ifaces\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "%s: changing role to AP\n", dev->name); - - if (mwifiex_deinit_priv_params(priv)) - return -1; - if (mwifiex_init_new_priv_params(priv, dev, type)) - return -1; - if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, true)) - return -1; - if (mwifiex_sta_init_cmd(priv, false, false)) - return -1; - - switch (curr_iftype) { - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - adapter->curr_iface_comb.p2p_intf--; - break; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - adapter->curr_iface_comb.sta_intf--; - break; - default: - break; - } - - adapter->curr_iface_comb.uap_intf++; - dev->ieee80211_ptr->iftype = type; - return 0; -} -/* - * CFG802.11 operation handler to change interface type. - */ -static int -mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, - struct net_device *dev, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype; - - switch (curr_iftype) { - case NL80211_IFTYPE_ADHOC: - switch (type) { - case NL80211_IFTYPE_STATION: - priv->bss_mode = type; - priv->sec_info.authentication_mode = - NL80211_AUTHTYPE_OPEN_SYSTEM; - dev->ieee80211_ptr->iftype = type; - mwifiex_deauthenticate(priv, NULL); - return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, - true); - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); - case NL80211_IFTYPE_AP: - return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); - case NL80211_IFTYPE_UNSPECIFIED: - mwifiex_dbg(priv->adapter, INFO, - "%s: kept type as IBSS\n", dev->name); - case NL80211_IFTYPE_ADHOC: /* This shouldn't happen */ - return 0; - default: - mwifiex_dbg(priv->adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - break; - case NL80211_IFTYPE_STATION: - switch (type) { - case NL80211_IFTYPE_ADHOC: - priv->bss_mode = type; - priv->sec_info.authentication_mode = - NL80211_AUTHTYPE_OPEN_SYSTEM; - dev->ieee80211_ptr->iftype = type; - mwifiex_deauthenticate(priv, NULL); - return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, - true); - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); - case NL80211_IFTYPE_AP: - return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); - case NL80211_IFTYPE_UNSPECIFIED: - mwifiex_dbg(priv->adapter, INFO, - "%s: kept type as STA\n", dev->name); - case NL80211_IFTYPE_STATION: /* This shouldn't happen */ - return 0; - default: - mwifiex_dbg(priv->adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - break; - case NL80211_IFTYPE_AP: - switch (type) { - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: - return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, - type, flags, - params); - break; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); - case NL80211_IFTYPE_UNSPECIFIED: - mwifiex_dbg(priv->adapter, INFO, - "%s: kept type as AP\n", dev->name); - case NL80211_IFTYPE_AP: /* This shouldn't happen */ - return 0; - default: - mwifiex_dbg(priv->adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - break; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - switch (type) { - case NL80211_IFTYPE_STATION: - if (mwifiex_cfg80211_deinit_p2p(priv)) - return -EFAULT; - priv->adapter->curr_iface_comb.p2p_intf--; - priv->adapter->curr_iface_comb.sta_intf++; - dev->ieee80211_ptr->iftype = type; - break; - case NL80211_IFTYPE_ADHOC: - if (mwifiex_cfg80211_deinit_p2p(priv)) - return -EFAULT; - return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, - type, flags, - params); - break; - case NL80211_IFTYPE_AP: - if (mwifiex_cfg80211_deinit_p2p(priv)) - return -EFAULT; - return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); - case NL80211_IFTYPE_UNSPECIFIED: - mwifiex_dbg(priv->adapter, INFO, - "%s: kept type as P2P\n", dev->name); - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - return 0; - default: - mwifiex_dbg(priv->adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "%s: unknown iftype: %d\n", - dev->name, dev->ieee80211_ptr->iftype); - return -EOPNOTSUPP; - } - - - return 0; -} - -static void -mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo, - struct rate_info *rate) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - if (adapter->is_hw_11ac_capable) { - /* bit[1-0]: 00=LG 01=HT 10=VHT */ - if (tx_htinfo & BIT(0)) { - /* HT */ - rate->mcs = priv->tx_rate; - rate->flags |= RATE_INFO_FLAGS_MCS; - } - if (tx_htinfo & BIT(1)) { - /* VHT */ - rate->mcs = priv->tx_rate & 0x0F; - rate->flags |= RATE_INFO_FLAGS_VHT_MCS; - } - - if (tx_htinfo & (BIT(1) | BIT(0))) { - /* HT or VHT */ - switch (tx_htinfo & (BIT(3) | BIT(2))) { - case 0: - rate->bw = RATE_INFO_BW_20; - break; - case (BIT(2)): - rate->bw = RATE_INFO_BW_40; - break; - case (BIT(3)): - rate->bw = RATE_INFO_BW_80; - break; - case (BIT(3) | BIT(2)): - rate->bw = RATE_INFO_BW_160; - break; - } - - if (tx_htinfo & BIT(4)) - rate->flags |= RATE_INFO_FLAGS_SHORT_GI; - - if ((priv->tx_rate >> 4) == 1) - rate->nss = 2; - else - rate->nss = 1; - } - } else { - /* - * Bit 0 in tx_htinfo indicates that current Tx rate - * is 11n rate. Valid MCS index values for us are 0 to 15. - */ - if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) { - rate->mcs = priv->tx_rate; - rate->flags |= RATE_INFO_FLAGS_MCS; - rate->bw = RATE_INFO_BW_20; - if (tx_htinfo & BIT(1)) - rate->bw = RATE_INFO_BW_40; - if (tx_htinfo & BIT(2)) - rate->flags |= RATE_INFO_FLAGS_SHORT_GI; - } - } -} - -/* - * This function dumps the station information on a buffer. - * - * The following information are shown - - * - Total bytes transmitted - * - Total bytes received - * - Total packets transmitted - * - Total packets received - * - Signal quality level - * - Transmission rate - */ -static int -mwifiex_dump_station_info(struct mwifiex_private *priv, - struct mwifiex_sta_node *node, - struct station_info *sinfo) -{ - u32 rate; - - sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES) | - BIT(NL80211_STA_INFO_RX_PACKETS) | BIT(NL80211_STA_INFO_TX_PACKETS) | - BIT(NL80211_STA_INFO_TX_BITRATE) | - BIT(NL80211_STA_INFO_SIGNAL) | BIT(NL80211_STA_INFO_SIGNAL_AVG); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - if (!node) - return -ENOENT; - - sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME) | - BIT(NL80211_STA_INFO_TX_FAILED); - sinfo->inactive_time = - jiffies_to_msecs(jiffies - node->stats.last_rx); - - sinfo->signal = node->stats.rssi; - sinfo->signal_avg = node->stats.rssi; - sinfo->rx_bytes = node->stats.rx_bytes; - sinfo->tx_bytes = node->stats.tx_bytes; - sinfo->rx_packets = node->stats.rx_packets; - sinfo->tx_packets = node->stats.tx_packets; - sinfo->tx_failed = node->stats.tx_failed; - - mwifiex_parse_htinfo(priv, node->stats.last_tx_htinfo, - &sinfo->txrate); - sinfo->txrate.legacy = node->stats.last_tx_rate * 5; - - return 0; - } - - /* Get signal information from the firmware */ - if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, - HostCmd_ACT_GEN_GET, 0, NULL, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "failed to get signal information\n"); - return -EFAULT; - } - - if (mwifiex_drv_get_data_rate(priv, &rate)) { - mwifiex_dbg(priv->adapter, ERROR, - "getting data rate error\n"); - return -EFAULT; - } - - /* Get DTIM period information from firmware */ - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_GET, DTIM_PERIOD_I, - &priv->dtim_period, true); - - mwifiex_parse_htinfo(priv, priv->tx_htinfo, &sinfo->txrate); - - sinfo->signal_avg = priv->bcn_rssi_avg; - sinfo->rx_bytes = priv->stats.rx_bytes; - sinfo->tx_bytes = priv->stats.tx_bytes; - sinfo->rx_packets = priv->stats.rx_packets; - sinfo->tx_packets = priv->stats.tx_packets; - sinfo->signal = priv->bcn_rssi_avg; - /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */ - sinfo->txrate.legacy = rate * 5; - - if (priv->bss_mode == NL80211_IFTYPE_STATION) { - sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); - sinfo->bss_param.flags = 0; - if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & - WLAN_CAPABILITY_SHORT_PREAMBLE) - sinfo->bss_param.flags |= - BSS_PARAM_FLAGS_SHORT_PREAMBLE; - if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & - WLAN_CAPABILITY_SHORT_SLOT_TIME) - sinfo->bss_param.flags |= - BSS_PARAM_FLAGS_SHORT_SLOT_TIME; - sinfo->bss_param.dtim_period = priv->dtim_period; - sinfo->bss_param.beacon_interval = - priv->curr_bss_params.bss_descriptor.beacon_period; - } - - return 0; -} - -/* - * CFG802.11 operation handler to get station information. - * - * This function only works in connected mode, and dumps the - * requested station information, if available. - */ -static int -mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac, struct station_info *sinfo) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (!priv->media_connected) - return -ENOENT; - if (memcmp(mac, priv->cfg_bssid, ETH_ALEN)) - return -ENOENT; - - return mwifiex_dump_station_info(priv, NULL, sinfo); -} - -/* - * CFG802.11 operation handler to dump station information. - */ -static int -mwifiex_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *mac, struct station_info *sinfo) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - static struct mwifiex_sta_node *node; - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - priv->media_connected && idx == 0) { - ether_addr_copy(mac, priv->cfg_bssid); - return mwifiex_dump_station_info(priv, NULL, sinfo); - } else if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - mwifiex_send_cmd(priv, HOST_CMD_APCMD_STA_LIST, - HostCmd_ACT_GEN_GET, 0, NULL, true); - - if (node && (&node->list == &priv->sta_list)) { - node = NULL; - return -ENOENT; - } - - node = list_prepare_entry(node, &priv->sta_list, list); - list_for_each_entry_continue(node, &priv->sta_list, list) { - ether_addr_copy(mac, node->mac_addr); - return mwifiex_dump_station_info(priv, node, sinfo); - } - } - - return -ENOENT; -} - -static int -mwifiex_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev, - int idx, struct survey_info *survey) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_chan_stats *pchan_stats = priv->adapter->chan_stats; - enum ieee80211_band band; - - mwifiex_dbg(priv->adapter, DUMP, "dump_survey idx=%d\n", idx); - - memset(survey, 0, sizeof(struct survey_info)); - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - priv->media_connected && idx == 0) { - u8 curr_bss_band = priv->curr_bss_params.band; - u32 chan = priv->curr_bss_params.bss_descriptor.channel; - - band = mwifiex_band_to_radio_type(curr_bss_band); - survey->channel = ieee80211_get_channel(wiphy, - ieee80211_channel_to_frequency(chan, band)); - - if (priv->bcn_nf_last) { - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = priv->bcn_nf_last; - } - return 0; - } - - if (idx >= priv->adapter->num_in_chan_stats) - return -ENOENT; - - if (!pchan_stats[idx].cca_scan_dur) - return 0; - - band = pchan_stats[idx].bandcfg; - survey->channel = ieee80211_get_channel(wiphy, - ieee80211_channel_to_frequency(pchan_stats[idx].chan_num, band)); - survey->filled = SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_TIME | - SURVEY_INFO_TIME_BUSY; - survey->noise = pchan_stats[idx].noise; - survey->time = pchan_stats[idx].cca_scan_dur; - survey->time_busy = pchan_stats[idx].cca_busy_dur; - - return 0; -} - -/* Supported rates to be advertised to the cfg80211 */ -static struct ieee80211_rate mwifiex_rates[] = { - {.bitrate = 10, .hw_value = 2, }, - {.bitrate = 20, .hw_value = 4, }, - {.bitrate = 55, .hw_value = 11, }, - {.bitrate = 110, .hw_value = 22, }, - {.bitrate = 60, .hw_value = 12, }, - {.bitrate = 90, .hw_value = 18, }, - {.bitrate = 120, .hw_value = 24, }, - {.bitrate = 180, .hw_value = 36, }, - {.bitrate = 240, .hw_value = 48, }, - {.bitrate = 360, .hw_value = 72, }, - {.bitrate = 480, .hw_value = 96, }, - {.bitrate = 540, .hw_value = 108, }, -}; - -/* Channel definitions to be advertised to cfg80211 */ -static struct ieee80211_channel mwifiex_channels_2ghz[] = { - {.center_freq = 2412, .hw_value = 1, }, - {.center_freq = 2417, .hw_value = 2, }, - {.center_freq = 2422, .hw_value = 3, }, - {.center_freq = 2427, .hw_value = 4, }, - {.center_freq = 2432, .hw_value = 5, }, - {.center_freq = 2437, .hw_value = 6, }, - {.center_freq = 2442, .hw_value = 7, }, - {.center_freq = 2447, .hw_value = 8, }, - {.center_freq = 2452, .hw_value = 9, }, - {.center_freq = 2457, .hw_value = 10, }, - {.center_freq = 2462, .hw_value = 11, }, - {.center_freq = 2467, .hw_value = 12, }, - {.center_freq = 2472, .hw_value = 13, }, - {.center_freq = 2484, .hw_value = 14, }, -}; - -static struct ieee80211_supported_band mwifiex_band_2ghz = { - .channels = mwifiex_channels_2ghz, - .n_channels = ARRAY_SIZE(mwifiex_channels_2ghz), - .bitrates = mwifiex_rates, - .n_bitrates = ARRAY_SIZE(mwifiex_rates), -}; - -static struct ieee80211_channel mwifiex_channels_5ghz[] = { - {.center_freq = 5040, .hw_value = 8, }, - {.center_freq = 5060, .hw_value = 12, }, - {.center_freq = 5080, .hw_value = 16, }, - {.center_freq = 5170, .hw_value = 34, }, - {.center_freq = 5190, .hw_value = 38, }, - {.center_freq = 5210, .hw_value = 42, }, - {.center_freq = 5230, .hw_value = 46, }, - {.center_freq = 5180, .hw_value = 36, }, - {.center_freq = 5200, .hw_value = 40, }, - {.center_freq = 5220, .hw_value = 44, }, - {.center_freq = 5240, .hw_value = 48, }, - {.center_freq = 5260, .hw_value = 52, }, - {.center_freq = 5280, .hw_value = 56, }, - {.center_freq = 5300, .hw_value = 60, }, - {.center_freq = 5320, .hw_value = 64, }, - {.center_freq = 5500, .hw_value = 100, }, - {.center_freq = 5520, .hw_value = 104, }, - {.center_freq = 5540, .hw_value = 108, }, - {.center_freq = 5560, .hw_value = 112, }, - {.center_freq = 5580, .hw_value = 116, }, - {.center_freq = 5600, .hw_value = 120, }, - {.center_freq = 5620, .hw_value = 124, }, - {.center_freq = 5640, .hw_value = 128, }, - {.center_freq = 5660, .hw_value = 132, }, - {.center_freq = 5680, .hw_value = 136, }, - {.center_freq = 5700, .hw_value = 140, }, - {.center_freq = 5745, .hw_value = 149, }, - {.center_freq = 5765, .hw_value = 153, }, - {.center_freq = 5785, .hw_value = 157, }, - {.center_freq = 5805, .hw_value = 161, }, - {.center_freq = 5825, .hw_value = 165, }, -}; - -static struct ieee80211_supported_band mwifiex_band_5ghz = { - .channels = mwifiex_channels_5ghz, - .n_channels = ARRAY_SIZE(mwifiex_channels_5ghz), - .bitrates = mwifiex_rates + 4, - .n_bitrates = ARRAY_SIZE(mwifiex_rates) - 4, -}; - - -/* Supported crypto cipher suits to be advertised to cfg80211 */ -static const u32 mwifiex_cipher_suites[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, - WLAN_CIPHER_SUITE_AES_CMAC, -}; - -/* Supported mgmt frame types to be advertised to cfg80211 */ -static const struct ieee80211_txrx_stypes -mwifiex_mgmt_stypes[NUM_NL80211_IFTYPES] = { - [NL80211_IFTYPE_STATION] = { - .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_RESP >> 4), - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4), - }, - [NL80211_IFTYPE_AP] = { - .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_RESP >> 4), - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4), - }, - [NL80211_IFTYPE_P2P_CLIENT] = { - .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_RESP >> 4), - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4), - }, - [NL80211_IFTYPE_P2P_GO] = { - .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_RESP >> 4), - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4), - }, -}; - -/* - * CFG802.11 operation handler for setting bit rates. - * - * Function configures data rates to firmware using bitrate mask - * provided by cfg80211. - */ -static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, - struct net_device *dev, - const u8 *peer, - const struct cfg80211_bitrate_mask *mask) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; - enum ieee80211_band band; - struct mwifiex_adapter *adapter = priv->adapter; - - if (!priv->media_connected) { - mwifiex_dbg(adapter, ERROR, - "Can not set Tx data rate in disconnected state\n"); - return -EINVAL; - } - - band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - - memset(bitmap_rates, 0, sizeof(bitmap_rates)); - - /* Fill HR/DSSS rates. */ - if (band == IEEE80211_BAND_2GHZ) - bitmap_rates[0] = mask->control[band].legacy & 0x000f; - - /* Fill OFDM rates */ - if (band == IEEE80211_BAND_2GHZ) - bitmap_rates[1] = (mask->control[band].legacy & 0x0ff0) >> 4; - else - bitmap_rates[1] = mask->control[band].legacy; - - /* Fill HT MCS rates */ - bitmap_rates[2] = mask->control[band].ht_mcs[0]; - if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) - bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8; - - /* Fill VHT MCS rates */ - if (adapter->fw_api_ver == MWIFIEX_FW_V15) { - bitmap_rates[10] = mask->control[band].vht_mcs[0]; - if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) - bitmap_rates[11] = mask->control[band].vht_mcs[1]; - } - - return mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, - HostCmd_ACT_GEN_SET, 0, bitmap_rates, true); -} - -/* - * CFG802.11 operation handler for connection quality monitoring. - * - * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI - * events to FW. - */ -static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, - struct net_device *dev, - s32 rssi_thold, u32 rssi_hyst) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_ds_misc_subsc_evt subsc_evt; - - priv->cqm_rssi_thold = rssi_thold; - priv->cqm_rssi_hyst = rssi_hyst; - - memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); - subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; - - /* Subscribe/unsubscribe low and high rssi events */ - if (rssi_thold && rssi_hyst) { - subsc_evt.action = HostCmd_ACT_BITWISE_SET; - subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold); - subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold); - subsc_evt.bcn_l_rssi_cfg.evt_freq = 1; - subsc_evt.bcn_h_rssi_cfg.evt_freq = 1; - return mwifiex_send_cmd(priv, - HostCmd_CMD_802_11_SUBSCRIBE_EVENT, - 0, 0, &subsc_evt, true); - } else { - subsc_evt.action = HostCmd_ACT_BITWISE_CLR; - return mwifiex_send_cmd(priv, - HostCmd_CMD_802_11_SUBSCRIBE_EVENT, - 0, 0, &subsc_evt, true); - } - - return 0; -} - -/* cfg80211 operation handler for change_beacon. - * Function retrieves and sets modified management IEs to FW. - */ -static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, - struct net_device *dev, - struct cfg80211_beacon_data *data) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: bss_type mismatched\n", __func__); - return -EINVAL; - } - - if (!priv->bss_started) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: bss not started\n", __func__); - return -EINVAL; - } - - if (mwifiex_set_mgmt_ies(priv, data)) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: setting mgmt ies failed\n", __func__); - return -EFAULT; - } - - return 0; -} - -/* cfg80211 operation handler for del_station. - * Function deauthenticates station which value is provided in mac parameter. - * If mac is NULL/broadcast, all stations in associated station list are - * deauthenticated. If bss is not started or there are no stations in - * associated stations list, no action is taken. - */ -static int -mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, - struct station_del_parameters *params) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_sta_node *sta_node; - u8 deauth_mac[ETH_ALEN]; - unsigned long flags; - - if (list_empty(&priv->sta_list) || !priv->bss_started) - return 0; - - if (!params->mac || is_broadcast_ether_addr(params->mac)) - return 0; - - mwifiex_dbg(priv->adapter, INFO, "%s: mac address %pM\n", - __func__, params->mac); - - eth_zero_addr(deauth_mac); - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_node = mwifiex_get_sta_entry(priv, params->mac); - if (sta_node) - ether_addr_copy(deauth_mac, params->mac); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - if (is_valid_ether_addr(deauth_mac)) { - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH, - HostCmd_ACT_GEN_SET, 0, - deauth_mac, true)) - return -1; - } - - return 0; -} - -static int -mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv = mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY); - struct mwifiex_ds_ant_cfg ant_cfg; - - if (!tx_ant || !rx_ant) - return -EOPNOTSUPP; - - if (adapter->hw_dev_mcs_support != HT_STREAM_2X2) { - /* Not a MIMO chip. User should provide specific antenna number - * for Tx/Rx path or enable all antennas for diversity - */ - if (tx_ant != rx_ant) - return -EOPNOTSUPP; - - if ((tx_ant & (tx_ant - 1)) && - (tx_ant != BIT(adapter->number_of_antenna) - 1)) - return -EOPNOTSUPP; - - if ((tx_ant == BIT(adapter->number_of_antenna) - 1) && - (priv->adapter->number_of_antenna > 1)) { - tx_ant = RF_ANTENNA_AUTO; - rx_ant = RF_ANTENNA_AUTO; - } - } else { - struct ieee80211_sta_ht_cap *ht_info; - int rx_mcs_supp; - enum ieee80211_band band; - - if ((tx_ant == 0x1 && rx_ant == 0x1)) { - adapter->user_dev_mcs_support = HT_STREAM_1X1; - if (adapter->is_hw_11ac_capable) - adapter->usr_dot_11ac_mcs_support = - MWIFIEX_11AC_MCS_MAP_1X1; - } else { - adapter->user_dev_mcs_support = HT_STREAM_2X2; - if (adapter->is_hw_11ac_capable) - adapter->usr_dot_11ac_mcs_support = - MWIFIEX_11AC_MCS_MAP_2X2; - } - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!adapter->wiphy->bands[band]) - continue; - - ht_info = &adapter->wiphy->bands[band]->ht_cap; - rx_mcs_supp = - GET_RXMCSSUPP(adapter->user_dev_mcs_support); - memset(&ht_info->mcs, 0, adapter->number_of_antenna); - memset(&ht_info->mcs, 0xff, rx_mcs_supp); - } - } - - ant_cfg.tx_ant = tx_ant; - ant_cfg.rx_ant = rx_ant; - - return mwifiex_send_cmd(priv, HostCmd_CMD_RF_ANTENNA, - HostCmd_ACT_GEN_SET, 0, &ant_cfg, true); -} - -/* cfg80211 operation handler for stop ap. - * Function stops BSS running at uAP interface. - */ -static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - mwifiex_abort_cac(priv); - - if (mwifiex_del_mgmt_ies(priv)) - mwifiex_dbg(priv->adapter, ERROR, - "Failed to delete mgmt IEs!\n"); - - priv->ap_11n_enabled = 0; - memset(&priv->bss_cfg, 0, sizeof(priv->bss_cfg)); - - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, - HostCmd_ACT_GEN_SET, 0, NULL, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to stop the BSS\n"); - return -1; - } - - if (mwifiex_send_cmd(priv, HOST_CMD_APCMD_SYS_RESET, - HostCmd_ACT_GEN_SET, 0, NULL, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to reset BSS\n"); - return -1; - } - - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); - - return 0; -} - -/* cfg80211 operation handler for start_ap. - * Function sets beacon period, DTIM period, SSID and security into - * AP config structure. - * AP is configured with these settings and BSS is started. - */ -static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, - struct net_device *dev, - struct cfg80211_ap_settings *params) -{ - struct mwifiex_uap_bss_param *bss_cfg; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) - return -1; - - bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL); - if (!bss_cfg) - return -ENOMEM; - - mwifiex_set_sys_config_invalid_data(bss_cfg); - - if (params->beacon_interval) - bss_cfg->beacon_period = params->beacon_interval; - if (params->dtim_period) - bss_cfg->dtim_period = params->dtim_period; - - if (params->ssid && params->ssid_len) { - memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len); - bss_cfg->ssid.ssid_len = params->ssid_len; - } - if (params->inactivity_timeout > 0) { - /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */ - bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout; - bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout; - } - - switch (params->hidden_ssid) { - case NL80211_HIDDEN_SSID_NOT_IN_USE: - bss_cfg->bcast_ssid_ctl = 1; - break; - case NL80211_HIDDEN_SSID_ZERO_LEN: - bss_cfg->bcast_ssid_ctl = 0; - break; - case NL80211_HIDDEN_SSID_ZERO_CONTENTS: - /* firmware doesn't support this type of hidden SSID */ - default: - kfree(bss_cfg); - return -EINVAL; - } - - mwifiex_uap_set_channel(priv, bss_cfg, params->chandef); - mwifiex_set_uap_rates(bss_cfg, params); - - if (mwifiex_set_secure_params(priv, bss_cfg, params)) { - kfree(bss_cfg); - mwifiex_dbg(priv->adapter, ERROR, - "Failed to parse secuirty parameters!\n"); - return -1; - } - - mwifiex_set_ht_params(priv, bss_cfg, params); - - if (priv->adapter->is_hw_11ac_capable) { - mwifiex_set_vht_params(priv, bss_cfg, params); - mwifiex_set_vht_width(priv, params->chandef.width, - priv->ap_11ac_enabled); - } - - if (priv->ap_11ac_enabled) - mwifiex_set_11ac_ba_params(priv); - else - mwifiex_set_ba_params(priv); - - mwifiex_set_wmm_params(priv, bss_cfg, params); - - if (mwifiex_is_11h_active(priv)) - mwifiex_set_tpc_params(priv, bss_cfg, params); - - if (mwifiex_is_11h_active(priv) && - !cfg80211_chandef_dfs_required(wiphy, ¶ms->chandef, - priv->bss_mode)) { - mwifiex_dbg(priv->adapter, INFO, - "Disable 11h extensions in FW\n"); - if (mwifiex_11h_activate(priv, false)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to disable 11h extensions!!"); - return -1; - } - priv->state_11h.is_11h_active = false; - } - - if (mwifiex_config_start_uap(priv, bss_cfg)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to start AP\n"); - kfree(bss_cfg); - return -1; - } - - if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon)) - return -1; - - if (!netif_carrier_ok(priv->netdev)) - netif_carrier_on(priv->netdev); - mwifiex_wake_up_net_dev_queue(priv->netdev, priv->adapter); - - memcpy(&priv->bss_cfg, bss_cfg, sizeof(priv->bss_cfg)); - kfree(bss_cfg); - return 0; -} - -/* - * CFG802.11 operation handler for disconnection request. - * - * This function does not work when there is already a disconnection - * procedure going on. - */ -static int -mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, - u16 reason_code) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (mwifiex_deauthenticate(priv, NULL)) - return -EFAULT; - - mwifiex_dbg(priv->adapter, MSG, - "info: successfully disconnected from %pM:\t" - "reason code %d\n", priv->cfg_bssid, reason_code); - - eth_zero_addr(priv->cfg_bssid); - priv->hs2_enabled = false; - - return 0; -} - -/* - * This function informs the CFG802.11 subsystem of a new IBSS. - * - * The following information are sent to the CFG802.11 subsystem - * to register the new IBSS. If we do not register the new IBSS, - * a kernel panic will result. - * - SSID - * - SSID length - * - BSSID - * - Channel - */ -static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) -{ - struct ieee80211_channel *chan; - struct mwifiex_bss_info bss_info; - struct cfg80211_bss *bss; - int ie_len; - u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)]; - enum ieee80211_band band; - - if (mwifiex_get_bss_info(priv, &bss_info)) - return -1; - - ie_buf[0] = WLAN_EID_SSID; - ie_buf[1] = bss_info.ssid.ssid_len; - - memcpy(&ie_buf[sizeof(struct ieee_types_header)], - &bss_info.ssid.ssid, bss_info.ssid.ssid_len); - ie_len = ie_buf[1] + sizeof(struct ieee_types_header); - - band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - chan = __ieee80211_get_channel(priv->wdev.wiphy, - ieee80211_channel_to_frequency(bss_info.bss_chan, - band)); - - bss = cfg80211_inform_bss(priv->wdev.wiphy, chan, - CFG80211_BSS_FTYPE_UNKNOWN, - bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, - 0, ie_buf, ie_len, 0, GFP_KERNEL); - if (bss) { - cfg80211_put_bss(priv->wdev.wiphy, bss); - ether_addr_copy(priv->cfg_bssid, bss_info.bssid); - } - - return 0; -} - -/* - * This function connects with a BSS. - * - * This function handles both Infra and Ad-Hoc modes. It also performs - * validity checking on the provided parameters, disconnects from the - * current BSS (if any), sets up the association/scan parameters, - * including security settings, and performs specific SSID scan before - * trying to connect. - * - * For Infra mode, the function returns failure if the specified SSID - * is not found in scan table. However, for Ad-Hoc mode, it can create - * the IBSS if it does not exist. On successful completion in either case, - * the function notifies the CFG802.11 subsystem of the new BSS connection. - */ -static int -mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, - const u8 *ssid, const u8 *bssid, int mode, - struct ieee80211_channel *channel, - struct cfg80211_connect_params *sme, bool privacy) -{ - struct cfg80211_ssid req_ssid; - int ret, auth_type = 0; - struct cfg80211_bss *bss = NULL; - u8 is_scanning_required = 0; - - memset(&req_ssid, 0, sizeof(struct cfg80211_ssid)); - - req_ssid.ssid_len = ssid_len; - if (ssid_len > IEEE80211_MAX_SSID_LEN) { - mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n"); - return -EINVAL; - } - - memcpy(req_ssid.ssid, ssid, ssid_len); - if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { - mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n"); - return -EINVAL; - } - - /* As this is new association, clear locally stored - * keys and security related flags */ - priv->sec_info.wpa_enabled = false; - priv->sec_info.wpa2_enabled = false; - priv->wep_key_curr_index = 0; - priv->sec_info.encryption_mode = 0; - priv->sec_info.is_authtype_auto = 0; - ret = mwifiex_set_encode(priv, NULL, NULL, 0, 0, NULL, 1); - - if (mode == NL80211_IFTYPE_ADHOC) { - /* "privacy" is set only for ad-hoc mode */ - if (privacy) { - /* - * Keep WLAN_CIPHER_SUITE_WEP104 for now so that - * the firmware can find a matching network from the - * scan. The cfg80211 does not give us the encryption - * mode at this stage so just setting it to WEP here. - */ - priv->sec_info.encryption_mode = - WLAN_CIPHER_SUITE_WEP104; - priv->sec_info.authentication_mode = - NL80211_AUTHTYPE_OPEN_SYSTEM; - } - - goto done; - } - - /* Now handle infra mode. "sme" is valid for infra mode only */ - if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { - auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; - priv->sec_info.is_authtype_auto = 1; - } else { - auth_type = sme->auth_type; - } - - if (sme->crypto.n_ciphers_pairwise) { - priv->sec_info.encryption_mode = - sme->crypto.ciphers_pairwise[0]; - priv->sec_info.authentication_mode = auth_type; - } - - if (sme->crypto.cipher_group) { - priv->sec_info.encryption_mode = sme->crypto.cipher_group; - priv->sec_info.authentication_mode = auth_type; - } - if (sme->ie) - ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len); - - if (sme->key) { - if (mwifiex_is_alg_wep(priv->sec_info.encryption_mode)) { - mwifiex_dbg(priv->adapter, INFO, - "info: setting wep encryption\t" - "with key len %d\n", sme->key_len); - priv->wep_key_curr_index = sme->key_idx; - ret = mwifiex_set_encode(priv, NULL, sme->key, - sme->key_len, sme->key_idx, - NULL, 0); - } - } -done: - /* - * Scan entries are valid for some time (15 sec). So we can save one - * active scan time if we just try cfg80211_get_bss first. If it fails - * then request scan and cfg80211_get_bss() again for final output. - */ - while (1) { - if (is_scanning_required) { - /* Do specific SSID scanning */ - if (mwifiex_request_scan(priv, &req_ssid)) { - mwifiex_dbg(priv->adapter, ERROR, "scan error\n"); - return -EFAULT; - } - } - - /* Find the BSS we want using available scan results */ - if (mode == NL80211_IFTYPE_ADHOC) - bss = cfg80211_get_bss(priv->wdev.wiphy, channel, - bssid, ssid, ssid_len, - IEEE80211_BSS_TYPE_IBSS, - IEEE80211_PRIVACY_ANY); - else - bss = cfg80211_get_bss(priv->wdev.wiphy, channel, - bssid, ssid, ssid_len, - IEEE80211_BSS_TYPE_ESS, - IEEE80211_PRIVACY_ANY); - - if (!bss) { - if (is_scanning_required) { - mwifiex_dbg(priv->adapter, WARN, - "assoc: requested bss not found in scan results\n"); - break; - } - is_scanning_required = 1; - } else { - mwifiex_dbg(priv->adapter, MSG, - "info: trying to associate to '%s' bssid %pM\n", - (char *)req_ssid.ssid, bss->bssid); - memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN); - break; - } - } - - ret = mwifiex_bss_start(priv, bss, &req_ssid); - if (ret) - return ret; - - if (mode == NL80211_IFTYPE_ADHOC) { - /* Inform the BSS information to kernel, otherwise - * kernel will give a panic after successful assoc */ - if (mwifiex_cfg80211_inform_ibss_bss(priv)) - return -EFAULT; - } - - return ret; -} - -/* - * CFG802.11 operation handler for association request. - * - * This function does not work when the current mode is set to Ad-Hoc, or - * when there is already an association procedure going on. The given BSS - * information is used to associate. - */ -static int -mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_connect_params *sme) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - - if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) { - mwifiex_dbg(adapter, ERROR, - "%s: reject infra assoc request in non-STA role\n", - dev->name); - return -EINVAL; - } - - if (priv->wdev.current_bss) { - mwifiex_dbg(adapter, ERROR, - "%s: already connected\n", dev->name); - return -EALREADY; - } - - if (adapter->surprise_removed || adapter->is_cmd_timedout) { - mwifiex_dbg(adapter, ERROR, - "%s: Ignore connection.\t" - "Card removed or FW in bad state\n", - dev->name); - return -EFAULT; - } - - mwifiex_dbg(adapter, INFO, - "info: Trying to associate to %s and bssid %pM\n", - (char *)sme->ssid, sme->bssid); - - ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, - priv->bss_mode, sme->channel, sme, 0); - if (!ret) { - cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, - NULL, 0, WLAN_STATUS_SUCCESS, - GFP_KERNEL); - mwifiex_dbg(priv->adapter, MSG, - "info: associated to bssid %pM successfully\n", - priv->cfg_bssid); - if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - priv->adapter->auto_tdls && - priv->bss_type == MWIFIEX_BSS_TYPE_STA) - mwifiex_setup_auto_tdls_timer(priv); - } else { - mwifiex_dbg(priv->adapter, ERROR, - "info: association to bssid %pM failed\n", - priv->cfg_bssid); - eth_zero_addr(priv->cfg_bssid); - - if (ret > 0) - cfg80211_connect_result(priv->netdev, priv->cfg_bssid, - NULL, 0, NULL, 0, ret, - GFP_KERNEL); - else - cfg80211_connect_result(priv->netdev, priv->cfg_bssid, - NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); - } - - return 0; -} - -/* - * This function sets following parameters for ibss network. - * - channel - * - start band - * - 11n flag - * - secondary channel offset - */ -static int mwifiex_set_ibss_params(struct mwifiex_private *priv, - struct cfg80211_ibss_params *params) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int index = 0, i; - u8 config_bands = 0; - - if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) { - if (!params->basic_rates) { - config_bands = BAND_B | BAND_G; - } else { - for (i = 0; i < mwifiex_band_2ghz.n_bitrates; i++) { - /* - * Rates below 6 Mbps in the table are CCK - * rates; 802.11b and from 6 they are OFDM; - * 802.11G - */ - if (mwifiex_rates[i].bitrate == 60) { - index = 1 << i; - break; - } - } - - if (params->basic_rates < index) { - config_bands = BAND_B; - } else { - config_bands = BAND_G; - if (params->basic_rates % index) - config_bands |= BAND_B; - } - } - - if (cfg80211_get_chandef_type(¶ms->chandef) != - NL80211_CHAN_NO_HT) - config_bands |= BAND_G | BAND_GN; - } else { - if (cfg80211_get_chandef_type(¶ms->chandef) == - NL80211_CHAN_NO_HT) - config_bands = BAND_A; - else - config_bands = BAND_AN | BAND_A; - } - - if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) { - adapter->config_bands = config_bands; - adapter->adhoc_start_band = config_bands; - - if ((config_bands & BAND_GN) || (config_bands & BAND_AN)) - adapter->adhoc_11n_enabled = true; - else - adapter->adhoc_11n_enabled = false; - } - - adapter->sec_chan_offset = - mwifiex_chan_type_to_sec_chan_offset( - cfg80211_get_chandef_type(¶ms->chandef)); - priv->adhoc_channel = ieee80211_frequency_to_channel( - params->chandef.chan->center_freq); - - mwifiex_dbg(adapter, INFO, - "info: set ibss band %d, chan %d, chan offset %d\n", - config_bands, priv->adhoc_channel, - adapter->sec_chan_offset); - - return 0; -} - -/* - * CFG802.11 operation handler to join an IBSS. - * - * This function does not work in any mode other than Ad-Hoc, or if - * a join operation is already in progress. - */ -static int -mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_ibss_params *params) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - int ret = 0; - - if (priv->bss_mode != NL80211_IFTYPE_ADHOC) { - mwifiex_dbg(priv->adapter, ERROR, - "request to join ibss received\t" - "when station is not in ibss mode\n"); - goto done; - } - - mwifiex_dbg(priv->adapter, MSG, - "info: trying to join to %s and bssid %pM\n", - (char *)params->ssid, params->bssid); - - mwifiex_set_ibss_params(priv, params); - - ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, - params->bssid, priv->bss_mode, - params->chandef.chan, NULL, - params->privacy); -done: - if (!ret) { - cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, - params->chandef.chan, GFP_KERNEL); - mwifiex_dbg(priv->adapter, MSG, - "info: joined/created adhoc network with bssid\t" - "%pM successfully\n", priv->cfg_bssid); - } else { - mwifiex_dbg(priv->adapter, ERROR, - "info: failed creating/joining adhoc network\n"); - } - - return ret; -} - -/* - * CFG802.11 operation handler to leave an IBSS. - * - * This function does not work if a leave operation is - * already in progress. - */ -static int -mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - mwifiex_dbg(priv->adapter, MSG, "info: disconnecting from essid %pM\n", - priv->cfg_bssid); - if (mwifiex_deauthenticate(priv, NULL)) - return -EFAULT; - - eth_zero_addr(priv->cfg_bssid); - - return 0; -} - -/* - * CFG802.11 operation handler for scan request. - * - * This function issues a scan request to the firmware based upon - * the user specified scan configuration. On successful completion, - * it also informs the results. - */ -static int -mwifiex_cfg80211_scan(struct wiphy *wiphy, - struct cfg80211_scan_request *request) -{ - struct net_device *dev = request->wdev->netdev; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - int i, offset, ret; - struct ieee80211_channel *chan; - struct ieee_types_header *ie; - struct mwifiex_user_scan_cfg *user_scan_cfg; - - mwifiex_dbg(priv->adapter, CMD, - "info: received scan request on %s\n", dev->name); - - /* Block scan request if scan operation or scan cleanup when interface - * is disabled is in process - */ - if (priv->scan_request || priv->scan_aborting) { - mwifiex_dbg(priv->adapter, WARN, - "cmd: Scan already in process..\n"); - return -EBUSY; - } - - user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); - if (!user_scan_cfg) - return -ENOMEM; - - priv->scan_request = request; - - user_scan_cfg->num_ssids = request->n_ssids; - user_scan_cfg->ssid_list = request->ssids; - - if (request->ie && request->ie_len) { - offset = 0; - for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { - if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR) - continue; - priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_SCAN; - ie = (struct ieee_types_header *)(request->ie + offset); - memcpy(&priv->vs_ie[i].ie, ie, sizeof(*ie) + ie->len); - offset += sizeof(*ie) + ie->len; - - if (offset >= request->ie_len) - break; - } - } - - for (i = 0; i < min_t(u32, request->n_channels, - MWIFIEX_USER_SCAN_CHAN_MAX); i++) { - chan = request->channels[i]; - user_scan_cfg->chan_list[i].chan_number = chan->hw_value; - user_scan_cfg->chan_list[i].radio_type = chan->band; - - if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids) - user_scan_cfg->chan_list[i].scan_type = - MWIFIEX_SCAN_TYPE_PASSIVE; - else - user_scan_cfg->chan_list[i].scan_type = - MWIFIEX_SCAN_TYPE_ACTIVE; - - user_scan_cfg->chan_list[i].scan_time = 0; - } - - if (priv->adapter->scan_chan_gap_enabled && - mwifiex_is_any_intf_active(priv)) - user_scan_cfg->scan_chan_gap = - priv->adapter->scan_chan_gap_time; - - ret = mwifiex_scan_networks(priv, user_scan_cfg); - kfree(user_scan_cfg); - if (ret) { - mwifiex_dbg(priv->adapter, ERROR, - "scan failed: %d\n", ret); - priv->scan_aborting = false; - priv->scan_request = NULL; - return ret; - } - - if (request->ie && request->ie_len) { - for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { - if (priv->vs_ie[i].mask == MWIFIEX_VSIE_MASK_SCAN) { - priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_CLEAR; - memset(&priv->vs_ie[i].ie, 0, - MWIFIEX_MAX_VSIE_LEN); - } - } - } - return 0; -} - -static void mwifiex_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info, - struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - vht_info->vht_supported = true; - - vht_info->cap = adapter->hw_dot_11ac_dev_cap; - /* Update MCS support for VHT */ - vht_info->vht_mcs.rx_mcs_map = cpu_to_le16( - adapter->hw_dot_11ac_mcs_support & 0xFFFF); - vht_info->vht_mcs.rx_highest = 0; - vht_info->vht_mcs.tx_mcs_map = cpu_to_le16( - adapter->hw_dot_11ac_mcs_support >> 16); - vht_info->vht_mcs.tx_highest = 0; -} - -/* - * This function sets up the CFG802.11 specific HT capability fields - * with default values. - * - * The following default values are set - - * - HT Supported = True - * - Maximum AMPDU length factor = IEEE80211_HT_MAX_AMPDU_64K - * - Minimum AMPDU spacing = IEEE80211_HT_MPDU_DENSITY_NONE - * - HT Capabilities supported by firmware - * - MCS information, Rx mask = 0xff - * - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01) - */ -static void -mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, - struct mwifiex_private *priv) -{ - int rx_mcs_supp; - struct ieee80211_mcs_info mcs_set; - u8 *mcs = (u8 *)&mcs_set; - struct mwifiex_adapter *adapter = priv->adapter; - - ht_info->ht_supported = true; - ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; - - memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); - - /* Fill HT capability information */ - if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - else - ht_info->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - - if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_SGI_20; - else - ht_info->cap &= ~IEEE80211_HT_CAP_SGI_20; - - if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_SGI_40; - else - ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40; - - if (adapter->user_dev_mcs_support == HT_STREAM_2X2) - ht_info->cap |= 3 << IEEE80211_HT_CAP_RX_STBC_SHIFT; - else - ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; - - if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; - else - ht_info->cap &= ~IEEE80211_HT_CAP_TX_STBC; - - if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; - else - ht_info->cap &= ~IEEE80211_HT_CAP_GRN_FLD; - - if (ISENABLED_40MHZ_INTOLERANT(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT; - else - ht_info->cap &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT; - - if (ISSUPP_RXLDPC(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; - else - ht_info->cap &= ~IEEE80211_HT_CAP_LDPC_CODING; - - ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU; - ht_info->cap |= IEEE80211_HT_CAP_SM_PS; - - rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support); - /* Set MCS for 1x1/2x2 */ - memset(mcs, 0xff, rx_mcs_supp); - /* Clear all the other values */ - memset(&mcs[rx_mcs_supp], 0, - sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); - if (priv->bss_mode == NL80211_IFTYPE_STATION || - ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) - /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ - SETHT_MCS32(mcs_set.rx_mask); - - memcpy((u8 *) &ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info)); - - ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; -} - -/* - * create a new virtual interface with the given name and name assign type - */ -struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, - const char *name, - unsigned char name_assign_type, - enum nl80211_iftype type, - u32 *flags, - struct vif_params *params) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv; - struct net_device *dev; - void *mdev_priv; - - if (!adapter) - return ERR_PTR(-EFAULT); - - switch (type) { - case NL80211_IFTYPE_UNSPECIFIED: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - if (adapter->curr_iface_comb.sta_intf == - adapter->iface_limit.sta_intf) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple sta/adhoc ifaces\n"); - return ERR_PTR(-EINVAL); - } - - priv = mwifiex_get_unused_priv(adapter); - if (!priv) { - mwifiex_dbg(adapter, ERROR, - "could not get free private struct\n"); - return ERR_PTR(-EFAULT); - } - - priv->wdev.wiphy = wiphy; - priv->wdev.iftype = NL80211_IFTYPE_STATION; - - if (type == NL80211_IFTYPE_UNSPECIFIED) - priv->bss_mode = NL80211_IFTYPE_STATION; - else - priv->bss_mode = type; - - priv->bss_type = MWIFIEX_BSS_TYPE_STA; - priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; - priv->bss_priority = 0; - priv->bss_role = MWIFIEX_BSS_ROLE_STA; - priv->bss_num = adapter->curr_iface_comb.sta_intf; - - break; - case NL80211_IFTYPE_AP: - if (adapter->curr_iface_comb.uap_intf == - adapter->iface_limit.uap_intf) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple AP ifaces\n"); - return ERR_PTR(-EINVAL); - } - - priv = mwifiex_get_unused_priv(adapter); - if (!priv) { - mwifiex_dbg(adapter, ERROR, - "could not get free private struct\n"); - return ERR_PTR(-EFAULT); - } - - priv->wdev.wiphy = wiphy; - priv->wdev.iftype = NL80211_IFTYPE_AP; - - priv->bss_type = MWIFIEX_BSS_TYPE_UAP; - priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; - priv->bss_priority = 0; - priv->bss_role = MWIFIEX_BSS_ROLE_UAP; - priv->bss_started = 0; - priv->bss_num = adapter->curr_iface_comb.uap_intf; - priv->bss_mode = type; - - break; - case NL80211_IFTYPE_P2P_CLIENT: - if (adapter->curr_iface_comb.p2p_intf == - adapter->iface_limit.p2p_intf) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple P2P ifaces\n"); - return ERR_PTR(-EINVAL); - } - - priv = mwifiex_get_unused_priv(adapter); - if (!priv) { - mwifiex_dbg(adapter, ERROR, - "could not get free private struct\n"); - return ERR_PTR(-EFAULT); - } - - priv->wdev.wiphy = wiphy; - /* At start-up, wpa_supplicant tries to change the interface - * to NL80211_IFTYPE_STATION if it is not managed mode. - */ - priv->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT; - priv->bss_mode = NL80211_IFTYPE_P2P_CLIENT; - - /* Setting bss_type to P2P tells firmware that this interface - * is receiving P2P peers found during find phase and doing - * action frame handshake. - */ - priv->bss_type = MWIFIEX_BSS_TYPE_P2P; - - priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; - priv->bss_priority = MWIFIEX_BSS_ROLE_STA; - priv->bss_role = MWIFIEX_BSS_ROLE_STA; - priv->bss_started = 0; - priv->bss_num = adapter->curr_iface_comb.p2p_intf; - - if (mwifiex_cfg80211_init_p2p_client(priv)) { - memset(&priv->wdev, 0, sizeof(priv->wdev)); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-EFAULT); - } - - break; - default: - mwifiex_dbg(adapter, ERROR, "type not supported\n"); - return ERR_PTR(-EINVAL); - } - - dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name, - name_assign_type, ether_setup, - IEEE80211_NUM_ACS, 1); - if (!dev) { - mwifiex_dbg(adapter, ERROR, - "no memory available for netdevice\n"); - memset(&priv->wdev, 0, sizeof(priv->wdev)); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-ENOMEM); - } - - mwifiex_init_priv_params(priv, dev); - priv->netdev = dev; - - mwifiex_setup_ht_caps(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv); - if (adapter->is_hw_11ac_capable) - mwifiex_setup_vht_caps( - &wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap, priv); - - if (adapter->config_bands & BAND_A) - mwifiex_setup_ht_caps( - &wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv); - - if ((adapter->config_bands & BAND_A) && adapter->is_hw_11ac_capable) - mwifiex_setup_vht_caps( - &wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap, priv); - - dev_net_set(dev, wiphy_net(wiphy)); - dev->ieee80211_ptr = &priv->wdev; - dev->ieee80211_ptr->iftype = priv->bss_mode; - memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); - SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); - - dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT; - dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN; - dev->ethtool_ops = &mwifiex_ethtool_ops; - - mdev_priv = netdev_priv(dev); - *((unsigned long *) mdev_priv) = (unsigned long) priv; - - SET_NETDEV_DEV(dev, adapter->dev); - - /* Register network device */ - if (register_netdevice(dev)) { - mwifiex_dbg(adapter, ERROR, - "cannot register virtual network device\n"); - free_netdev(dev); - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - priv->netdev = NULL; - memset(&priv->wdev, 0, sizeof(priv->wdev)); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-EFAULT); - } - - priv->dfs_cac_workqueue = alloc_workqueue("MWIFIEX_DFS_CAC%s", - WQ_HIGHPRI | - WQ_MEM_RECLAIM | - WQ_UNBOUND, 1, name); - if (!priv->dfs_cac_workqueue) { - mwifiex_dbg(adapter, ERROR, - "cannot register virtual network device\n"); - free_netdev(dev); - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - priv->netdev = NULL; - memset(&priv->wdev, 0, sizeof(priv->wdev)); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-ENOMEM); - } - - INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue); - - priv->dfs_chan_sw_workqueue = alloc_workqueue("MWIFIEX_DFS_CHSW%s", - WQ_HIGHPRI | WQ_UNBOUND | - WQ_MEM_RECLAIM, 1, name); - if (!priv->dfs_chan_sw_workqueue) { - mwifiex_dbg(adapter, ERROR, - "cannot register virtual network device\n"); - free_netdev(dev); - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - priv->netdev = NULL; - memset(&priv->wdev, 0, sizeof(priv->wdev)); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-ENOMEM); - } - - INIT_DELAYED_WORK(&priv->dfs_chan_sw_work, - mwifiex_dfs_chan_sw_work_queue); - - sema_init(&priv->async_sem, 1); - - mwifiex_dbg(adapter, INFO, - "info: %s: Marvell 802.11 Adapter\n", dev->name); - -#ifdef CONFIG_DEBUG_FS - mwifiex_dev_debugfs_init(priv); -#endif - - switch (type) { - case NL80211_IFTYPE_UNSPECIFIED: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - adapter->curr_iface_comb.sta_intf++; - break; - case NL80211_IFTYPE_AP: - adapter->curr_iface_comb.uap_intf++; - break; - case NL80211_IFTYPE_P2P_CLIENT: - adapter->curr_iface_comb.p2p_intf++; - break; - default: - mwifiex_dbg(adapter, ERROR, "type not supported\n"); - return ERR_PTR(-EINVAL); - } - - return &priv->wdev; -} -EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf); - -/* - * del_virtual_intf: remove the virtual interface determined by dev - */ -int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - struct mwifiex_adapter *adapter = priv->adapter; - struct sk_buff *skb, *tmp; - -#ifdef CONFIG_DEBUG_FS - mwifiex_dev_debugfs_remove(priv); -#endif - - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - - skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) - mwifiex_write_data_complete(priv->adapter, skb, 0, -1); - - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - - if (wdev->netdev->reg_state == NETREG_REGISTERED) - unregister_netdevice(wdev->netdev); - - if (priv->dfs_cac_workqueue) { - flush_workqueue(priv->dfs_cac_workqueue); - destroy_workqueue(priv->dfs_cac_workqueue); - priv->dfs_cac_workqueue = NULL; - } - - if (priv->dfs_chan_sw_workqueue) { - flush_workqueue(priv->dfs_chan_sw_workqueue); - destroy_workqueue(priv->dfs_chan_sw_workqueue); - priv->dfs_chan_sw_workqueue = NULL; - } - /* Clear the priv in adapter */ - priv->netdev->ieee80211_ptr = NULL; - priv->netdev = NULL; - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - - priv->media_connected = false; - - switch (priv->bss_mode) { - case NL80211_IFTYPE_UNSPECIFIED: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - adapter->curr_iface_comb.sta_intf--; - break; - case NL80211_IFTYPE_AP: - adapter->curr_iface_comb.uap_intf--; - break; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - adapter->curr_iface_comb.p2p_intf--; - break; - default: - mwifiex_dbg(adapter, ERROR, - "del_virtual_intf: type not supported\n"); - break; - } - - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || - GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) - kfree(priv->hist_data); - - return 0; -} -EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); - -static bool -mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq, - u8 max_byte_seq) -{ - int j, k, valid_byte_cnt = 0; - bool dont_care_byte = false; - - for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { - for (k = 0; k < 8; k++) { - if (pat->mask[j] & 1 << k) { - memcpy(byte_seq + valid_byte_cnt, - &pat->pattern[j * 8 + k], 1); - valid_byte_cnt++; - if (dont_care_byte) - return false; - } else { - if (valid_byte_cnt) - dont_care_byte = true; - } - - if (valid_byte_cnt > max_byte_seq) - return false; - } - } - - byte_seq[max_byte_seq] = valid_byte_cnt; - - return true; -} - -#ifdef CONFIG_PM -static void mwifiex_set_auto_arp_mef_entry(struct mwifiex_private *priv, - struct mwifiex_mef_entry *mef_entry) -{ - int i, filt_num = 0, num_ipv4 = 0; - struct in_device *in_dev; - struct in_ifaddr *ifa; - __be32 ips[MWIFIEX_MAX_SUPPORTED_IPADDR]; - struct mwifiex_adapter *adapter = priv->adapter; - - mef_entry->mode = MEF_MODE_HOST_SLEEP; - mef_entry->action = MEF_ACTION_AUTO_ARP; - - /* Enable ARP offload feature */ - memset(ips, 0, sizeof(ips)); - for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) { - if (adapter->priv[i]->netdev) { - in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev); - if (!in_dev) - continue; - ifa = in_dev->ifa_list; - if (!ifa || !ifa->ifa_local) - continue; - ips[i] = ifa->ifa_local; - num_ipv4++; - } - } - - for (i = 0; i < num_ipv4; i++) { - if (!ips[i]) - continue; - mef_entry->filter[filt_num].repeat = 1; - memcpy(mef_entry->filter[filt_num].byte_seq, - (u8 *)&ips[i], sizeof(ips[i])); - mef_entry->filter[filt_num]. - byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = - sizeof(ips[i]); - mef_entry->filter[filt_num].offset = 46; - mef_entry->filter[filt_num].filt_type = TYPE_EQ; - if (filt_num) { - mef_entry->filter[filt_num].filt_action = - TYPE_OR; - } - filt_num++; - } - - mef_entry->filter[filt_num].repeat = 1; - mef_entry->filter[filt_num].byte_seq[0] = 0x08; - mef_entry->filter[filt_num].byte_seq[1] = 0x06; - mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = 2; - mef_entry->filter[filt_num].offset = 20; - mef_entry->filter[filt_num].filt_type = TYPE_EQ; - mef_entry->filter[filt_num].filt_action = TYPE_AND; -} - -static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv, - struct mwifiex_ds_mef_cfg *mef_cfg, - struct mwifiex_mef_entry *mef_entry, - struct cfg80211_wowlan *wowlan) -{ - int i, filt_num = 0, ret = 0; - bool first_pat = true; - u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; - const u8 ipv4_mc_mac[] = {0x33, 0x33}; - const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; - - mef_entry->mode = MEF_MODE_HOST_SLEEP; - mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST; - - for (i = 0; i < wowlan->n_patterns; i++) { - memset(byte_seq, 0, sizeof(byte_seq)); - if (!mwifiex_is_pattern_supported(&wowlan->patterns[i], - byte_seq, - MWIFIEX_MEF_MAX_BYTESEQ)) { - mwifiex_dbg(priv->adapter, ERROR, - "Pattern not supported\n"); - return -EOPNOTSUPP; - } - - if (!wowlan->patterns[i].pkt_offset) { - if (!(byte_seq[0] & 0x01) && - (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) { - mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST; - continue; - } else if (is_broadcast_ether_addr(byte_seq)) { - mef_cfg->criteria |= MWIFIEX_CRITERIA_BROADCAST; - continue; - } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && - (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) || - (!memcmp(byte_seq, ipv6_mc_mac, 3) && - (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) { - mef_cfg->criteria |= MWIFIEX_CRITERIA_MULTICAST; - continue; - } - } - mef_entry->filter[filt_num].repeat = 1; - mef_entry->filter[filt_num].offset = - wowlan->patterns[i].pkt_offset; - memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq, - sizeof(byte_seq)); - mef_entry->filter[filt_num].filt_type = TYPE_EQ; - - if (first_pat) - first_pat = false; - else - mef_entry->filter[filt_num].filt_action = TYPE_AND; - - filt_num++; - } - - if (wowlan->magic_pkt) { - mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST; - mef_entry->filter[filt_num].repeat = 16; - memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, - ETH_ALEN); - mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = - ETH_ALEN; - mef_entry->filter[filt_num].offset = 28; - mef_entry->filter[filt_num].filt_type = TYPE_EQ; - if (filt_num) - mef_entry->filter[filt_num].filt_action = TYPE_OR; - - filt_num++; - mef_entry->filter[filt_num].repeat = 16; - memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, - ETH_ALEN); - mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = - ETH_ALEN; - mef_entry->filter[filt_num].offset = 56; - mef_entry->filter[filt_num].filt_type = TYPE_EQ; - mef_entry->filter[filt_num].filt_action = TYPE_OR; - } - return ret; -} - -static int mwifiex_set_mef_filter(struct mwifiex_private *priv, - struct cfg80211_wowlan *wowlan) -{ - int ret = 0, num_entries = 1; - struct mwifiex_ds_mef_cfg mef_cfg; - struct mwifiex_mef_entry *mef_entry; - - if (wowlan->n_patterns || wowlan->magic_pkt) - num_entries++; - - mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL); - if (!mef_entry) - return -ENOMEM; - - memset(&mef_cfg, 0, sizeof(mef_cfg)); - mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST | - MWIFIEX_CRITERIA_UNICAST; - mef_cfg.num_entries = num_entries; - mef_cfg.mef_entry = mef_entry; - - mwifiex_set_auto_arp_mef_entry(priv, &mef_entry[0]); - - if (wowlan->n_patterns || wowlan->magic_pkt) { - ret = mwifiex_set_wowlan_mef_entry(priv, &mef_cfg, - &mef_entry[1], wowlan); - if (ret) - goto err; - } - - if (!mef_cfg.criteria) - mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST | - MWIFIEX_CRITERIA_UNICAST | - MWIFIEX_CRITERIA_MULTICAST; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG, - HostCmd_ACT_GEN_SET, 0, - &mef_cfg, true); - -err: - kfree(mef_entry); - return ret; -} - -static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, - struct cfg80211_wowlan *wowlan) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_ds_hs_cfg hs_cfg; - int i, ret = 0; - struct mwifiex_private *priv; - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - mwifiex_abort_cac(priv); - } - - mwifiex_cancel_all_pending_cmd(adapter); - - if (!wowlan) { - mwifiex_dbg(adapter, ERROR, - "None of the WOWLAN triggers enabled\n"); - return 0; - } - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); - - if (!priv->media_connected) { - mwifiex_dbg(adapter, ERROR, - "Can not configure WOWLAN in disconnected state\n"); - return 0; - } - - ret = mwifiex_set_mef_filter(priv, wowlan); - if (ret) { - mwifiex_dbg(adapter, ERROR, "Failed to set MEF filter\n"); - return ret; - } - - if (wowlan->disconnect) { - memset(&hs_cfg, 0, sizeof(hs_cfg)); - hs_cfg.is_invoke_hostcmd = false; - hs_cfg.conditions = HS_CFG_COND_MAC_EVENT; - hs_cfg.gpio = HS_CFG_GPIO_DEF; - hs_cfg.gap = HS_CFG_GAP_DEF; - ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, - MWIFIEX_SYNC_CMD, &hs_cfg); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "Failed to set HS params\n"); - return ret; - } - } - - return ret; -} - -static int mwifiex_cfg80211_resume(struct wiphy *wiphy) -{ - return 0; -} - -static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy, - bool enabled) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - - device_set_wakeup_enable(adapter->dev, enabled); -} -#endif - -static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq) -{ - const u8 ipv4_mc_mac[] = {0x33, 0x33}; - const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; - const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff}; - - if ((byte_seq[0] & 0x01) && - (byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 1)) - return PACKET_TYPE_UNICAST; - else if (!memcmp(byte_seq, bc_mac, 4)) - return PACKET_TYPE_BROADCAST; - else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && - byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) || - (!memcmp(byte_seq, ipv6_mc_mac, 3) && - byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3)) - return PACKET_TYPE_MULTICAST; - - return 0; -} - -static int -mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv, - struct cfg80211_coalesce_rules *crule, - struct mwifiex_coalesce_rule *mrule) -{ - u8 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ + 1]; - struct filt_field_param *param; - int i; - - mrule->max_coalescing_delay = crule->delay; - - param = mrule->params; - - for (i = 0; i < crule->n_patterns; i++) { - memset(byte_seq, 0, sizeof(byte_seq)); - if (!mwifiex_is_pattern_supported(&crule->patterns[i], - byte_seq, - MWIFIEX_COALESCE_MAX_BYTESEQ)) { - mwifiex_dbg(priv->adapter, ERROR, - "Pattern not supported\n"); - return -EOPNOTSUPP; - } - - if (!crule->patterns[i].pkt_offset) { - u8 pkt_type; - - pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq); - if (pkt_type && mrule->pkt_type) { - mwifiex_dbg(priv->adapter, ERROR, - "Multiple packet types not allowed\n"); - return -EOPNOTSUPP; - } else if (pkt_type) { - mrule->pkt_type = pkt_type; - continue; - } - } - - if (crule->condition == NL80211_COALESCE_CONDITION_MATCH) - param->operation = RECV_FILTER_MATCH_TYPE_EQ; - else - param->operation = RECV_FILTER_MATCH_TYPE_NE; - - param->operand_len = byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ]; - memcpy(param->operand_byte_stream, byte_seq, - param->operand_len); - param->offset = crule->patterns[i].pkt_offset; - param++; - - mrule->num_of_fields++; - } - - if (!mrule->pkt_type) { - mwifiex_dbg(priv->adapter, ERROR, - "Packet type can not be determined\n"); - return -EOPNOTSUPP; - } - - return 0; -} - -static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, - struct cfg80211_coalesce *coalesce) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - int i, ret; - struct mwifiex_ds_coalesce_cfg coalesce_cfg; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); - - memset(&coalesce_cfg, 0, sizeof(coalesce_cfg)); - if (!coalesce) { - mwifiex_dbg(adapter, WARN, - "Disable coalesce and reset all previous rules\n"); - return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, - HostCmd_ACT_GEN_SET, 0, - &coalesce_cfg, true); - } - - coalesce_cfg.num_of_rules = coalesce->n_rules; - for (i = 0; i < coalesce->n_rules; i++) { - ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i], - &coalesce_cfg.rule[i]); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "Recheck the patterns provided for rule %d\n", - i + 1); - return ret; - } - } - - return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, - HostCmd_ACT_GEN_SET, 0, &coalesce_cfg, true); -} - -/* cfg80211 ops handler for tdls_mgmt. - * Function prepares TDLS action frame packets and forwards them to FW - */ -static int -mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, u32 peer_capability, - bool initiator, const u8 *extra_ies, - size_t extra_ies_len) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - int ret; - - if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) - return -ENOTSUPP; - - /* make sure we are in station mode and connected */ - if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) - return -ENOTSUPP; - - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Setup Request to %pM status_code=%d\n", - peer, status_code); - mwifiex_add_auto_tdls_peer(priv, peer); - ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - case WLAN_TDLS_SETUP_RESPONSE: - mwifiex_add_auto_tdls_peer(priv, peer); - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Setup Response to %pM status_code=%d\n", - peer, status_code); - ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - case WLAN_TDLS_SETUP_CONFIRM: - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Confirm to %pM status_code=%d\n", peer, - status_code); - ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - case WLAN_TDLS_TEARDOWN: - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Tear down to %pM\n", peer); - ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - case WLAN_TDLS_DISCOVERY_REQUEST: - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Discovery Request to %pM\n", peer); - ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Discovery Response to %pM\n", peer); - ret = mwifiex_send_tdls_action_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "Unknown TDLS mgmt/action frame %pM\n", peer); - ret = -EINVAL; - break; - } - - return ret; -} - -static int -mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, enum nl80211_tdls_operation action) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) || - !(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) - return -ENOTSUPP; - - /* make sure we are in station mode and connected */ - if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) - return -ENOTSUPP; - - mwifiex_dbg(priv->adapter, MSG, - "TDLS peer=%pM, oper=%d\n", peer, action); - - switch (action) { - case NL80211_TDLS_ENABLE_LINK: - action = MWIFIEX_TDLS_ENABLE_LINK; - break; - case NL80211_TDLS_DISABLE_LINK: - action = MWIFIEX_TDLS_DISABLE_LINK; - break; - case NL80211_TDLS_TEARDOWN: - /* shouldn't happen!*/ - mwifiex_dbg(priv->adapter, ERROR, - "tdls_oper: teardown from driver not supported\n"); - return -EINVAL; - case NL80211_TDLS_SETUP: - /* shouldn't happen!*/ - mwifiex_dbg(priv->adapter, ERROR, - "tdls_oper: setup from driver not supported\n"); - return -EINVAL; - case NL80211_TDLS_DISCOVERY_REQ: - /* shouldn't happen!*/ - mwifiex_dbg(priv->adapter, ERROR, - "tdls_oper: discovery from driver not supported\n"); - return -EINVAL; - default: - mwifiex_dbg(priv->adapter, ERROR, - "tdls_oper: operation not supported\n"); - return -ENOTSUPP; - } - - return mwifiex_tdls_oper(priv, peer, action); -} - -static int -mwifiex_cfg80211_tdls_chan_switch(struct wiphy *wiphy, struct net_device *dev, - const u8 *addr, u8 oper_class, - struct cfg80211_chan_def *chandef) -{ - struct mwifiex_sta_node *sta_ptr; - unsigned long flags; - u16 chan; - u8 second_chan_offset, band; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, addr); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - if (!sta_ptr) { - wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n", - __func__, addr); - return -ENOENT; - } - - if (!(sta_ptr->tdls_cap.extcap.ext_capab[3] & - WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)) { - wiphy_err(wiphy, "%pM do not support tdls cs\n", addr); - return -ENOENT; - } - - if (sta_ptr->tdls_status == TDLS_CHAN_SWITCHING || - sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) { - wiphy_err(wiphy, "channel switch is running, abort request\n"); - return -EALREADY; - } - - chan = chandef->chan->hw_value; - second_chan_offset = mwifiex_get_sec_chan_offset(chan); - band = chandef->chan->band; - mwifiex_start_tdls_cs(priv, addr, chan, second_chan_offset, band); - - return 0; -} - -static void -mwifiex_cfg80211_tdls_cancel_chan_switch(struct wiphy *wiphy, - struct net_device *dev, - const u8 *addr) -{ - struct mwifiex_sta_node *sta_ptr; - unsigned long flags; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, addr); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - if (!sta_ptr) { - wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n", - __func__, addr); - } else if (!(sta_ptr->tdls_status == TDLS_CHAN_SWITCHING || - sta_ptr->tdls_status == TDLS_IN_BASE_CHAN || - sta_ptr->tdls_status == TDLS_IN_OFF_CHAN)) { - wiphy_err(wiphy, "tdls chan switch not initialize by %pM\n", - addr); - } else - mwifiex_stop_tdls_cs(priv, addr); -} - -static int -mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac, struct station_parameters *params) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) - return -ENOTSUPP; - - /* make sure we are in station mode and connected */ - if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) - return -ENOTSUPP; - - return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK); -} - -static int -mwifiex_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_csa_settings *params) -{ - struct ieee_types_header *chsw_ie; - struct ieee80211_channel_sw_ie *channel_sw; - int chsw_msec; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (priv->adapter->scan_processing) { - mwifiex_dbg(priv->adapter, ERROR, - "radar detection: scan in process...\n"); - return -EBUSY; - } - - if (priv->wdev.cac_started) - return -EBUSY; - - if (cfg80211_chandef_identical(¶ms->chandef, - &priv->dfs_chandef)) - return -EINVAL; - - chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, - params->beacon_csa.tail, - params->beacon_csa.tail_len); - if (!chsw_ie) { - mwifiex_dbg(priv->adapter, ERROR, - "Could not parse channel switch announcement IE\n"); - return -EINVAL; - } - - channel_sw = (void *)(chsw_ie + 1); - if (channel_sw->mode) { - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); - } - - if (mwifiex_del_mgmt_ies(priv)) - mwifiex_dbg(priv->adapter, ERROR, - "Failed to delete mgmt IEs!\n"); - - if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon_csa)) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: setting mgmt ies failed\n", __func__); - return -EFAULT; - } - - memcpy(&priv->dfs_chandef, ¶ms->chandef, sizeof(priv->dfs_chandef)); - memcpy(&priv->beacon_after, ¶ms->beacon_after, - sizeof(priv->beacon_after)); - - chsw_msec = max(channel_sw->count * priv->bss_cfg.beacon_period, 100); - queue_delayed_work(priv->dfs_chan_sw_workqueue, &priv->dfs_chan_sw_work, - msecs_to_jiffies(chsw_msec)); - return 0; -} - -static int mwifiex_cfg80211_get_channel(struct wiphy *wiphy, - struct wireless_dev *wdev, - struct cfg80211_chan_def *chandef) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - struct mwifiex_bssdescriptor *curr_bss; - struct ieee80211_channel *chan; - u8 second_chan_offset; - enum nl80211_channel_type chan_type; - enum ieee80211_band band; - int freq; - int ret = -ENODATA; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP && - cfg80211_chandef_valid(&priv->bss_chandef)) { - *chandef = priv->bss_chandef; - ret = 0; - } else if (priv->media_connected) { - curr_bss = &priv->curr_bss_params.bss_descriptor; - band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - freq = ieee80211_channel_to_frequency(curr_bss->channel, band); - chan = ieee80211_get_channel(wiphy, freq); - - if (curr_bss->bcn_ht_oper) { - second_chan_offset = curr_bss->bcn_ht_oper->ht_param & - IEEE80211_HT_PARAM_CHA_SEC_OFFSET; - chan_type = mwifiex_sec_chan_offset_to_chan_type - (second_chan_offset); - cfg80211_chandef_create(chandef, chan, chan_type); - } else { - cfg80211_chandef_create(chandef, chan, - NL80211_CHAN_NO_HT); - } - ret = 0; - } - - return ret; -} - -static int -mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy, - struct net_device *dev, - struct cfg80211_chan_def *chandef, - u32 cac_time_ms) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_radar_params radar_params; - - if (priv->adapter->scan_processing) { - mwifiex_dbg(priv->adapter, ERROR, - "radar detection: scan already in process...\n"); - return -EBUSY; - } - - if (!mwifiex_is_11h_active(priv)) { - mwifiex_dbg(priv->adapter, INFO, - "Enable 11h extensions in FW\n"); - if (mwifiex_11h_activate(priv, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to activate 11h extensions!!"); - return -1; - } - priv->state_11h.is_11h_active = true; - } - - memset(&radar_params, 0, sizeof(struct mwifiex_radar_params)); - radar_params.chandef = chandef; - radar_params.cac_time_ms = cac_time_ms; - - memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef)); - - if (mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, - HostCmd_ACT_GEN_SET, 0, &radar_params, true)) - return -1; - - queue_delayed_work(priv->dfs_cac_workqueue, &priv->dfs_cac_work, - msecs_to_jiffies(cac_time_ms)); - return 0; -} - -static int -mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac, - struct station_parameters *params) -{ - int ret; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - /* we support change_station handler only for TDLS peers*/ - if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) - return -ENOTSUPP; - - /* make sure we are in station mode and connected */ - if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) - return -ENOTSUPP; - - priv->sta_params = params; - - ret = mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CONFIG_LINK); - priv->sta_params = NULL; - - return ret; -} - -/* station cfg80211 operations */ -static struct cfg80211_ops mwifiex_cfg80211_ops = { - .add_virtual_intf = mwifiex_add_virtual_intf, - .del_virtual_intf = mwifiex_del_virtual_intf, - .change_virtual_intf = mwifiex_cfg80211_change_virtual_intf, - .scan = mwifiex_cfg80211_scan, - .connect = mwifiex_cfg80211_connect, - .disconnect = mwifiex_cfg80211_disconnect, - .get_station = mwifiex_cfg80211_get_station, - .dump_station = mwifiex_cfg80211_dump_station, - .dump_survey = mwifiex_cfg80211_dump_survey, - .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params, - .join_ibss = mwifiex_cfg80211_join_ibss, - .leave_ibss = mwifiex_cfg80211_leave_ibss, - .add_key = mwifiex_cfg80211_add_key, - .del_key = mwifiex_cfg80211_del_key, - .mgmt_tx = mwifiex_cfg80211_mgmt_tx, - .mgmt_frame_register = mwifiex_cfg80211_mgmt_frame_register, - .remain_on_channel = mwifiex_cfg80211_remain_on_channel, - .cancel_remain_on_channel = mwifiex_cfg80211_cancel_remain_on_channel, - .set_default_key = mwifiex_cfg80211_set_default_key, - .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt, - .set_tx_power = mwifiex_cfg80211_set_tx_power, - .set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask, - .start_ap = mwifiex_cfg80211_start_ap, - .stop_ap = mwifiex_cfg80211_stop_ap, - .change_beacon = mwifiex_cfg80211_change_beacon, - .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config, - .set_antenna = mwifiex_cfg80211_set_antenna, - .del_station = mwifiex_cfg80211_del_station, -#ifdef CONFIG_PM - .suspend = mwifiex_cfg80211_suspend, - .resume = mwifiex_cfg80211_resume, - .set_wakeup = mwifiex_cfg80211_set_wakeup, -#endif - .set_coalesce = mwifiex_cfg80211_set_coalesce, - .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt, - .tdls_oper = mwifiex_cfg80211_tdls_oper, - .tdls_channel_switch = mwifiex_cfg80211_tdls_chan_switch, - .tdls_cancel_channel_switch = mwifiex_cfg80211_tdls_cancel_chan_switch, - .add_station = mwifiex_cfg80211_add_station, - .change_station = mwifiex_cfg80211_change_station, - .get_channel = mwifiex_cfg80211_get_channel, - .start_radar_detection = mwifiex_cfg80211_start_radar_detection, - .channel_switch = mwifiex_cfg80211_channel_switch, -}; - -#ifdef CONFIG_PM -static const struct wiphy_wowlan_support mwifiex_wowlan_support = { - .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, - .n_patterns = MWIFIEX_MEF_MAX_FILTERS, - .pattern_min_len = 1, - .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, - .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, -}; -#endif - -static bool mwifiex_is_valid_alpha2(const char *alpha2) -{ - if (!alpha2 || strlen(alpha2) != 2) - return false; - - if (isalpha(alpha2[0]) && isalpha(alpha2[1])) - return true; - - return false; -} - -static const struct wiphy_coalesce_support mwifiex_coalesce_support = { - .n_rules = MWIFIEX_COALESCE_MAX_RULES, - .max_delay = MWIFIEX_MAX_COALESCING_DELAY, - .n_patterns = MWIFIEX_COALESCE_MAX_FILTERS, - .pattern_min_len = 1, - .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, - .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, -}; - -int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter) -{ - u32 n_channels_bg, n_channels_a = 0; - - n_channels_bg = mwifiex_band_2ghz.n_channels; - - if (adapter->config_bands & BAND_A) - n_channels_a = mwifiex_band_5ghz.n_channels; - - adapter->num_in_chan_stats = max_t(u32, n_channels_bg, n_channels_a); - adapter->chan_stats = vmalloc(sizeof(*adapter->chan_stats) * - adapter->num_in_chan_stats); - - if (!adapter->chan_stats) - return -ENOMEM; - - return 0; -} - -/* - * This function registers the device with CFG802.11 subsystem. - * - * The function creates the wireless device/wiphy, populates it with - * default parameters and handler function pointers, and finally - * registers the device. - */ - -int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) -{ - int ret; - void *wdev_priv; - 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, - sizeof(struct mwifiex_adapter *)); - if (!wiphy) { - mwifiex_dbg(adapter, ERROR, - "%s: creating new wiphy\n", __func__); - return -ENOMEM; - } - wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; - wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; - wiphy->mgmt_stypes = mwifiex_mgmt_stypes; - wiphy->max_remain_on_channel_duration = 5000; - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_AP); - - wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz; - if (adapter->config_bands & BAND_A) - wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz; - else - wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; - - if (adapter->drcs_enabled && ISSUPP_DRCS_ENABLED(adapter->fw_cap_info)) - wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_drcs; - else if (adapter->is_hw_11ac_capable) - wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_vht; - else - wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta; - wiphy->n_iface_combinations = 1; - - /* Initialize cipher suits */ - wiphy->cipher_suites = mwifiex_cipher_suites; - wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); - - ether_addr_copy(wiphy->perm_addr, adapter->perm_addr); - wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | - WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | - WIPHY_FLAG_AP_UAPSD | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_HAS_CHANNEL_SWITCH | - WIPHY_FLAG_PS_ON_BY_DEFAULT; - - if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) - wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | - WIPHY_FLAG_TDLS_EXTERNAL_SETUP; - -#ifdef CONFIG_PM - wiphy->wowlan = &mwifiex_wowlan_support; -#endif - - wiphy->coalesce = &mwifiex_coalesce_support; - - wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | - NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | - NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; - - wiphy->available_antennas_tx = BIT(adapter->number_of_antenna) - 1; - wiphy->available_antennas_rx = BIT(adapter->number_of_antenna) - 1; - - wiphy->features |= NL80211_FEATURE_HT_IBSS | - NL80211_FEATURE_INACTIVITY_TIMER | - NL80211_FEATURE_NEED_OBSS_SCAN; - - if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) - wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH; - - if (adapter->fw_api_ver == MWIFIEX_FW_V15) - wiphy->features |= NL80211_FEATURE_SK_TX_STATUS; - - /* Reserve space for mwifiex specific private data for BSS */ - wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv); - - wiphy->reg_notifier = mwifiex_reg_notifier; - - /* Set struct mwifiex_adapter pointer in wiphy_priv */ - wdev_priv = wiphy_priv(wiphy); - *(unsigned long *)wdev_priv = (unsigned long)adapter; - - set_wiphy_dev(wiphy, priv->adapter->dev); - - ret = wiphy_register(wiphy); - if (ret < 0) { - mwifiex_dbg(adapter, ERROR, - "%s: wiphy_register failed: %d\n", __func__, ret); - wiphy_free(wiphy); - return ret; - } - - if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) { - mwifiex_dbg(adapter, INFO, - "driver hint alpha2: %2.2s\n", reg_alpha2); - regulatory_hint(wiphy, reg_alpha2); - } else { - country_code = mwifiex_11d_code_2_region(adapter->region_code); - if (country_code) - mwifiex_dbg(adapter, WARN, - "ignoring F/W country code %2.2s\n", - country_code); - } - - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_GET, FRAG_THRESH_I, &thr, true); - wiphy->frag_threshold = thr; - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_GET, RTS_THRESH_I, &thr, true); - wiphy->rts_threshold = thr; - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true); - wiphy->retry_short = (u8) retry; - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry, true); - wiphy->retry_long = (u8) retry; - - adapter->wiphy = wiphy; - return ret; -} diff --git a/drivers/net/wireless/mwifiex/cfg80211.h b/drivers/net/wireless/mwifiex/cfg80211.h deleted file mode 100644 index 908367857d58..000000000000 --- a/drivers/net/wireless/mwifiex/cfg80211.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Marvell Wireless LAN device driver: CFG80211 - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef __MWIFIEX_CFG80211__ -#define __MWIFIEX_CFG80211__ - -#include - -#include "main.h" - -int mwifiex_register_cfg80211(struct mwifiex_adapter *); - -#endif diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c deleted file mode 100644 index 3ddb8ec676ed..000000000000 --- a/drivers/net/wireless/mwifiex/cfp.c +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Marvell Wireless LAN device driver: Channel, Frequence and Power - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "cfg80211.h" - -/* 100mW */ -#define MWIFIEX_TX_PWR_DEFAULT 20 -/* 100mW */ -#define MWIFIEX_TX_PWR_US_DEFAULT 20 -/* 50mW */ -#define MWIFIEX_TX_PWR_JP_DEFAULT 16 -/* 100mW */ -#define MWIFIEX_TX_PWR_FR_100MW 20 -/* 10mW */ -#define MWIFIEX_TX_PWR_FR_10MW 10 -/* 100mW */ -#define MWIFIEX_TX_PWR_EMEA_DEFAULT 20 - -static u8 adhoc_rates_b[B_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, 0 }; - -static u8 adhoc_rates_g[G_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, - 0xb0, 0x48, 0x60, 0x6c, 0 }; - -static u8 adhoc_rates_bg[BG_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, - 0x0c, 0x12, 0x18, 0x24, - 0x30, 0x48, 0x60, 0x6c, 0 }; - -static u8 adhoc_rates_a[A_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, - 0xb0, 0x48, 0x60, 0x6c, 0 }; -static u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, - 0xb0, 0x48, 0x60, 0x6c, 0 }; -static u16 mwifiex_data_rates[MWIFIEX_SUPPORTED_RATES_EXT] = { 0x02, 0x04, - 0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18, - 0x24, 0x30, 0x48, 0x60, 0x6C, 0x90, - 0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68, - 0x75, 0x82, 0x0C, 0x1B, 0x36, 0x51, - 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00 }; - -static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 }; - -static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, - 0x30, 0x48, 0x60, 0x6c, 0 }; - -static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c, - 0x12, 0x16, 0x18, 0x24, 0x30, 0x48, - 0x60, 0x6c, 0 }; - -u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30, - 0x32, 0x40, 0x41, 0xff }; - -static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; - -/* For every mcs_rate line, the first 8 bytes are for stream 1x1, - * and all 16 bytes are for stream 2x2. - */ -static const u16 mcs_rate[4][16] = { - /* LGI 40M */ - { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, - 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, - - /* SGI 40M */ - { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, - 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, - - /* LGI 20M */ - { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, - 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, - - /* SGI 20M */ - { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, - 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } -}; - -/* AC rates */ -static const u16 ac_mcs_rate_nss1[8][10] = { - /* LG 160M */ - { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, - 0x492, 0x57C, 0x618 }, - - /* SG 160M */ - { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, - 0x514, 0x618, 0x6C6 }, - - /* LG 80M */ - { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, - 0x249, 0x2BE, 0x30C }, - - /* SG 80M */ - { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, - 0x28A, 0x30C, 0x363 }, - - /* LG 40M */ - { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, - 0x10E, 0x144, 0x168 }, - - /* SG 40M */ - { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, - 0x12C, 0x168, 0x190 }, - - /* LG 20M */ - { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 }, - - /* SG 20M */ - { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 }, -}; - -/* NSS2 note: the value in the table is 2 multiplier of the actual rate */ -static const u16 ac_mcs_rate_nss2[8][10] = { - /* LG 160M */ - { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, - 0x924, 0xAF8, 0xC30 }, - - /* SG 160M */ - { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, - 0xA28, 0xC30, 0xD8B }, - - /* LG 80M */ - { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, - 0x492, 0x57C, 0x618 }, - - /* SG 80M */ - { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, - 0x514, 0x618, 0x6C6 }, - - /* LG 40M */ - { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, - 0x21C, 0x288, 0x2D0 }, - - /* SG 40M */ - { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, - 0x258, 0x2D0, 0x320 }, - - /* LG 20M */ - { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, - 0x138, 0x00 }, - - /* SG 20M */ - { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, - 0x15B, 0x00 }, -}; - -struct region_code_mapping { - u8 code; - u8 region[IEEE80211_COUNTRY_STRING_LEN]; -}; - -static struct region_code_mapping region_code_mapping_t[] = { - { 0x10, "US " }, /* US FCC */ - { 0x20, "CA " }, /* IC Canada */ - { 0x30, "EU " }, /* ETSI */ - { 0x31, "ES " }, /* Spain */ - { 0x32, "FR " }, /* France */ - { 0x40, "JP " }, /* Japan */ - { 0x41, "JP " }, /* Japan */ - { 0x50, "CN " }, /* China */ -}; - -/* This function converts integer code to region string */ -u8 *mwifiex_11d_code_2_region(u8 code) -{ - u8 i; - u8 size = sizeof(region_code_mapping_t)/ - sizeof(struct region_code_mapping); - - /* Look for code in mapping table */ - for (i = 0; i < size; i++) - if (region_code_mapping_t[i].code == code) - return region_code_mapping_t[i].region; - - return NULL; -} - -/* - * This function maps an index in supported rates table into - * the corresponding data rate. - */ -u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, - u8 index, u8 ht_info) -{ - u32 rate = 0; - u8 mcs_index = 0; - u8 bw = 0; - u8 gi = 0; - - if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_VHT) { - mcs_index = min(index & 0xF, 9); - - /* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */ - bw = (ht_info & 0xC) >> 2; - - /* LGI: gi =0, SGI: gi = 1 */ - gi = (ht_info & 0x10) >> 4; - - if ((index >> 4) == 1) /* NSS = 2 */ - rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index]; - else /* NSS = 1 */ - rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index]; - } else if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_HT) { - /* 20M: bw=0, 40M: bw=1 */ - bw = (ht_info & 0xC) >> 2; - - /* LGI: gi =0, SGI: gi = 1 */ - gi = (ht_info & 0x10) >> 4; - - if (index == MWIFIEX_RATE_BITMAP_MCS0) { - if (gi == 1) - rate = 0x0D; /* MCS 32 SGI rate */ - else - rate = 0x0C; /* MCS 32 LGI rate */ - } else if (index < 16) { - if ((bw == 1) || (bw == 0)) - rate = mcs_rate[2 * (1 - bw) + gi][index]; - else - rate = mwifiex_data_rates[0]; - } else { - rate = mwifiex_data_rates[0]; - } - } else { - /* 11n non-HT rates */ - if (index >= MWIFIEX_SUPPORTED_RATES_EXT) - index = 0; - rate = mwifiex_data_rates[index]; - } - - return rate; -} - -/* This function maps an index in supported rates table into - * the corresponding data rate. - */ -u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, - u8 index, u8 ht_info) -{ - u32 mcs_num_supp = - (priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8; - u32 rate; - - if (priv->adapter->is_hw_11ac_capable) - return mwifiex_index_to_acs_data_rate(priv, index, ht_info); - - if (ht_info & BIT(0)) { - if (index == MWIFIEX_RATE_BITMAP_MCS0) { - if (ht_info & BIT(2)) - rate = 0x0D; /* MCS 32 SGI rate */ - else - rate = 0x0C; /* MCS 32 LGI rate */ - } else if (index < mcs_num_supp) { - if (ht_info & BIT(1)) { - if (ht_info & BIT(2)) - /* SGI, 40M */ - rate = mcs_rate[1][index]; - else - /* LGI, 40M */ - rate = mcs_rate[0][index]; - } else { - if (ht_info & BIT(2)) - /* SGI, 20M */ - rate = mcs_rate[3][index]; - else - /* LGI, 20M */ - rate = mcs_rate[2][index]; - } - } else - rate = mwifiex_data_rates[0]; - } else { - if (index >= MWIFIEX_SUPPORTED_RATES_EXT) - index = 0; - rate = mwifiex_data_rates[index]; - } - return rate; -} - -/* - * This function returns the current active data rates. - * - * The result may vary depending upon connection status. - */ -u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates) -{ - if (!priv->media_connected) - return mwifiex_get_supported_rates(priv, rates); - else - return mwifiex_copy_rates(rates, 0, - priv->curr_bss_params.data_rates, - priv->curr_bss_params.num_of_rates); -} - -/* - * This function locates the Channel-Frequency-Power triplet based upon - * band and channel/frequency parameters. - */ -struct mwifiex_chan_freq_power * -mwifiex_get_cfp(struct mwifiex_private *priv, u8 band, u16 channel, u32 freq) -{ - struct mwifiex_chan_freq_power *cfp = NULL; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *ch = NULL; - int i; - - if (!channel && !freq) - return cfp; - - if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG) - sband = priv->wdev.wiphy->bands[IEEE80211_BAND_2GHZ]; - else - sband = priv->wdev.wiphy->bands[IEEE80211_BAND_5GHZ]; - - if (!sband) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: cannot find cfp by band %d\n", - __func__, band); - return cfp; - } - - for (i = 0; i < sband->n_channels; i++) { - ch = &sband->channels[i]; - - if (ch->flags & IEEE80211_CHAN_DISABLED) - continue; - - if (freq) { - if (ch->center_freq == freq) - break; - } else { - /* find by valid channel*/ - if (ch->hw_value == channel || - channel == FIRST_VALID_CHANNEL) - break; - } - } - if (i == sband->n_channels) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: cannot find cfp by band %d\t" - "& channel=%d freq=%d\n", - __func__, band, channel, freq); - } else { - if (!ch) - return cfp; - - priv->cfp.channel = ch->hw_value; - priv->cfp.freq = ch->center_freq; - priv->cfp.max_tx_power = ch->max_power; - cfp = &priv->cfp; - } - - return cfp; -} - -/* - * This function checks if the data rate is set to auto. - */ -u8 -mwifiex_is_rate_auto(struct mwifiex_private *priv) -{ - u32 i; - int rate_num = 0; - - for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++) - if (priv->bitmap_rates[i]) - rate_num++; - - if (rate_num > 1) - return true; - else - return false; -} - -/* This function gets the supported data rates from bitmask inside - * cfg80211_scan_request. - */ -u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv, - u8 *rates, u8 radio_type) -{ - struct wiphy *wiphy = priv->adapter->wiphy; - struct cfg80211_scan_request *request = priv->scan_request; - u32 num_rates, rate_mask; - struct ieee80211_supported_band *sband; - int i; - - if (radio_type) { - sband = wiphy->bands[IEEE80211_BAND_5GHZ]; - if (WARN_ON_ONCE(!sband)) - return 0; - rate_mask = request->rates[IEEE80211_BAND_5GHZ]; - } else { - sband = wiphy->bands[IEEE80211_BAND_2GHZ]; - if (WARN_ON_ONCE(!sband)) - return 0; - rate_mask = request->rates[IEEE80211_BAND_2GHZ]; - } - - num_rates = 0; - for (i = 0; i < sband->n_bitrates; i++) { - if ((BIT(i) & rate_mask) == 0) - continue; /* skip rate */ - rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5); - } - - return num_rates; -} - -/* This function gets the supported data rates. The function works in - * both Ad-Hoc and infra mode by printing the band and returning the - * data rates. - */ -u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) -{ - u32 k = 0; - struct mwifiex_adapter *adapter = priv->adapter; - - if (priv->bss_mode == NL80211_IFTYPE_STATION || - priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { - switch (adapter->config_bands) { - case BAND_B: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_b\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_b, - sizeof(supported_rates_b)); - break; - case BAND_G: - case BAND_G | BAND_GN: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_g\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_g, - sizeof(supported_rates_g)); - break; - case BAND_B | BAND_G: - case BAND_A | BAND_B | BAND_G: - case BAND_A | BAND_B: - case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN: - case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC: - case BAND_B | BAND_G | BAND_GN: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_bg\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_bg, - sizeof(supported_rates_bg)); - break; - case BAND_A: - case BAND_A | BAND_G: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_a\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_a, - sizeof(supported_rates_a)); - break; - case BAND_AN: - case BAND_A | BAND_AN: - case BAND_A | BAND_AN | BAND_AAC: - case BAND_A | BAND_G | BAND_AN | BAND_GN: - case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_a\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_a, - sizeof(supported_rates_a)); - break; - case BAND_GN: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_n\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_n, - sizeof(supported_rates_n)); - break; - } - } else { - /* Ad-hoc mode */ - switch (adapter->adhoc_start_band) { - case BAND_B: - mwifiex_dbg(adapter, INFO, "info: adhoc B\n"); - k = mwifiex_copy_rates(rates, k, adhoc_rates_b, - sizeof(adhoc_rates_b)); - break; - case BAND_G: - case BAND_G | BAND_GN: - mwifiex_dbg(adapter, INFO, "info: adhoc G only\n"); - k = mwifiex_copy_rates(rates, k, adhoc_rates_g, - sizeof(adhoc_rates_g)); - break; - case BAND_B | BAND_G: - case BAND_B | BAND_G | BAND_GN: - mwifiex_dbg(adapter, INFO, "info: adhoc BG\n"); - k = mwifiex_copy_rates(rates, k, adhoc_rates_bg, - sizeof(adhoc_rates_bg)); - break; - case BAND_A: - case BAND_A | BAND_AN: - mwifiex_dbg(adapter, INFO, "info: adhoc A\n"); - k = mwifiex_copy_rates(rates, k, adhoc_rates_a, - sizeof(adhoc_rates_a)); - break; - } - } - - return k; -} - -u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, - u8 rx_rate, u8 rate_info) -{ - u8 rate_index = 0; - - /* HT40 */ - if ((rate_info & BIT(0)) && (rate_info & BIT(1))) - rate_index = MWIFIEX_RATE_INDEX_MCS0 + - MWIFIEX_BW20_MCS_NUM + rx_rate; - else if (rate_info & BIT(0)) /* HT20 */ - rate_index = MWIFIEX_RATE_INDEX_MCS0 + rx_rate; - else - rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ? - rx_rate - 1 : rx_rate; - - return rate_index; -} diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c deleted file mode 100644 index 45ae38e32621..000000000000 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ /dev/null @@ -1,1659 +0,0 @@ -/* - * Marvell Wireless LAN device driver: commands and events - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11ac.h" - -/* - * This function initializes a command node. - * - * The actual allocation of the node is not done by this function. It only - * initiates a node by filling it with default parameters. Similarly, - * allocation of the different buffers used (IOCTL buffer, data buffer) are - * not done by this function either. - */ -static void -mwifiex_init_cmd_node(struct mwifiex_private *priv, - struct cmd_ctrl_node *cmd_node, - u32 cmd_oid, void *data_buf, bool sync) -{ - cmd_node->priv = priv; - cmd_node->cmd_oid = cmd_oid; - if (sync) { - cmd_node->wait_q_enabled = true; - cmd_node->cmd_wait_q_woken = false; - cmd_node->condition = &cmd_node->cmd_wait_q_woken; - } - cmd_node->data_buf = data_buf; - cmd_node->cmd_skb = cmd_node->skb; -} - -/* - * This function returns a command node from the free queue depending upon - * availability. - */ -static struct cmd_ctrl_node * -mwifiex_get_cmd_node(struct mwifiex_adapter *adapter) -{ - struct cmd_ctrl_node *cmd_node; - unsigned long flags; - - spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); - if (list_empty(&adapter->cmd_free_q)) { - mwifiex_dbg(adapter, ERROR, - "GET_CMD_NODE: cmd node not available\n"); - spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); - return NULL; - } - cmd_node = list_first_entry(&adapter->cmd_free_q, - struct cmd_ctrl_node, list); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); - - return cmd_node; -} - -/* - * This function cleans up a command node. - * - * The function resets the fields including the buffer pointers. - * This function does not try to free the buffers. They must be - * freed before calling this function. - * - * This function will however call the receive completion callback - * in case a response buffer is still available before resetting - * the pointer. - */ -static void -mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node) -{ - cmd_node->cmd_oid = 0; - cmd_node->cmd_flag = 0; - cmd_node->data_buf = NULL; - cmd_node->wait_q_enabled = false; - - if (cmd_node->cmd_skb) - skb_trim(cmd_node->cmd_skb, 0); - - if (cmd_node->resp_skb) { - adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb); - cmd_node->resp_skb = NULL; - } -} - -/* - * This function sends a host command to the firmware. - * - * The function copies the host command into the driver command - * buffer, which will be transferred to the firmware later by the - * main thread. - */ -static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_ds_misc_cmd *pcmd_ptr) -{ - /* Copy the HOST command to command buffer */ - memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len); - mwifiex_dbg(priv->adapter, CMD, - "cmd: host cmd size = %d\n", pcmd_ptr->len); - return 0; -} - -/* - * This function downloads a command to the firmware. - * - * The function performs sanity tests, sets the command sequence - * number and size, converts the header fields to CPU format before - * sending. Afterwards, it logs the command ID and action for debugging - * and sets up the command timeout timer. - */ -static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, - struct cmd_ctrl_node *cmd_node) -{ - - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - struct host_cmd_ds_command *host_cmd; - uint16_t cmd_code; - uint16_t cmd_size; - unsigned long flags; - __le32 tmp; - - if (!adapter || !cmd_node) - return -1; - - host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); - - /* Sanity test */ - if (host_cmd == NULL || host_cmd->size == 0) { - mwifiex_dbg(adapter, ERROR, - "DNLD_CMD: host_cmd is null\t" - "or cmd size is 0, not sending\n"); - if (cmd_node->wait_q_enabled) - adapter->cmd_wait_q.status = -1; - mwifiex_recycle_cmd_node(adapter, cmd_node); - return -1; - } - - cmd_code = le16_to_cpu(host_cmd->command); - cmd_size = le16_to_cpu(host_cmd->size); - - if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET && - cmd_code != HostCmd_CMD_FUNC_SHUTDOWN && - cmd_code != HostCmd_CMD_FUNC_INIT) { - mwifiex_dbg(adapter, ERROR, - "DNLD_CMD: FW in reset state, ignore cmd %#x\n", - cmd_code); - mwifiex_recycle_cmd_node(adapter, cmd_node); - queue_work(adapter->workqueue, &adapter->main_work); - return -1; - } - - /* Set command sequence number */ - adapter->seq_num++; - host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO - (adapter->seq_num, - cmd_node->priv->bss_num, - cmd_node->priv->bss_type)); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->curr_cmd = cmd_node; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - /* Adjust skb length */ - if (cmd_node->cmd_skb->len > cmd_size) - /* - * cmd_size is less than sizeof(struct host_cmd_ds_command). - * Trim off the unused portion. - */ - skb_trim(cmd_node->cmd_skb, cmd_size); - else if (cmd_node->cmd_skb->len < cmd_size) - /* - * cmd_size is larger than sizeof(struct host_cmd_ds_command) - * because we have appended custom IE TLV. Increase skb length - * accordingly. - */ - skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len); - - mwifiex_dbg(adapter, CMD, - "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", - cmd_code, - le16_to_cpu(*(__le16 *)((u8 *)host_cmd + S_DS_GEN)), - cmd_size, le16_to_cpu(host_cmd->seq_num)); - mwifiex_dbg_dump(adapter, CMD_D, "cmd buffer:", host_cmd, cmd_size); - - if (adapter->iface_type == MWIFIEX_USB) { - tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD); - skb_push(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN); - memcpy(cmd_node->cmd_skb->data, &tmp, MWIFIEX_TYPE_LEN); - adapter->cmd_sent = true; - ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_USB_EP_CMD_EVENT, - cmd_node->cmd_skb, NULL); - skb_pull(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN); - if (ret == -EBUSY) - cmd_node->cmd_skb = NULL; - } else { - skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN); - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, - cmd_node->cmd_skb, NULL); - skb_pull(cmd_node->cmd_skb, INTF_HEADER_LEN); - } - - if (ret == -1) { - mwifiex_dbg(adapter, ERROR, - "DNLD_CMD: host to card failed\n"); - if (adapter->iface_type == MWIFIEX_USB) - adapter->cmd_sent = false; - if (cmd_node->wait_q_enabled) - adapter->cmd_wait_q.status = -1; - mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - adapter->dbg.num_cmd_host_to_card_failure++; - return -1; - } - - /* Save the last command id and action to debug log */ - adapter->dbg.last_cmd_index = - (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; - adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code; - adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] = - le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)); - - /* Clear BSS_NO_BITS from HostCmd */ - cmd_code &= HostCmd_CMD_ID_MASK; - - /* Setup the timer after transmit command */ - mod_timer(&adapter->cmd_timer, - jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); - - return 0; -} - -/* - * This function downloads a sleep confirm command to the firmware. - * - * The function performs sanity tests, sets the command sequence - * number and size, converts the header fields to CPU format before - * sending. - * - * No responses are needed for sleep confirm command. - */ -static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) -{ - int ret; - struct mwifiex_private *priv; - struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = - (struct mwifiex_opt_sleep_confirm *) - adapter->sleep_cfm->data; - struct sk_buff *sleep_cfm_tmp; - __le32 tmp; - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - adapter->seq_num++; - sleep_cfm_buf->seq_num = - cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO - (adapter->seq_num, priv->bss_num, - priv->bss_type))); - - mwifiex_dbg(adapter, CMD, - "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", - le16_to_cpu(sleep_cfm_buf->command), - le16_to_cpu(sleep_cfm_buf->action), - le16_to_cpu(sleep_cfm_buf->size), - le16_to_cpu(sleep_cfm_buf->seq_num)); - mwifiex_dbg_dump(adapter, CMD_D, "SLEEP_CFM buffer: ", sleep_cfm_buf, - le16_to_cpu(sleep_cfm_buf->size)); - - if (adapter->iface_type == MWIFIEX_USB) { - sleep_cfm_tmp = - dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) - + MWIFIEX_TYPE_LEN); - skb_put(sleep_cfm_tmp, sizeof(struct mwifiex_opt_sleep_confirm) - + MWIFIEX_TYPE_LEN); - tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD); - memcpy(sleep_cfm_tmp->data, &tmp, MWIFIEX_TYPE_LEN); - memcpy(sleep_cfm_tmp->data + MWIFIEX_TYPE_LEN, - adapter->sleep_cfm->data, - sizeof(struct mwifiex_opt_sleep_confirm)); - ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_USB_EP_CMD_EVENT, - sleep_cfm_tmp, NULL); - if (ret != -EBUSY) - dev_kfree_skb_any(sleep_cfm_tmp); - } else { - skb_push(adapter->sleep_cfm, INTF_HEADER_LEN); - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, - adapter->sleep_cfm, NULL); - skb_pull(adapter->sleep_cfm, INTF_HEADER_LEN); - } - - if (ret == -1) { - mwifiex_dbg(adapter, ERROR, "SLEEP_CFM: failed\n"); - adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; - return -1; - } - - if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl)) - /* Response is not needed for sleep confirm command */ - adapter->ps_state = PS_STATE_SLEEP; - else - adapter->ps_state = PS_STATE_SLEEP_CFM; - - if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) && - (adapter->is_hs_configured && - !adapter->sleep_period.period)) { - adapter->pm_wakeup_card_req = true; - mwifiex_hs_activated_event(mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), true); - } - - return ret; -} - -/* - * This function allocates the command buffers and links them to - * the command free queue. - * - * The driver uses a pre allocated number of command buffers, which - * are created at driver initializations and freed at driver cleanup. - * Every command needs to obtain a command buffer from this pool before - * it can be issued. The command free queue lists the command buffers - * currently free to use, while the command pending queue lists the - * command buffers already in use and awaiting handling. Command buffers - * are returned to the free queue after use. - */ -int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter) -{ - struct cmd_ctrl_node *cmd_array; - u32 i; - - /* Allocate and initialize struct cmd_ctrl_node */ - cmd_array = kcalloc(MWIFIEX_NUM_OF_CMD_BUFFER, - sizeof(struct cmd_ctrl_node), GFP_KERNEL); - if (!cmd_array) - return -ENOMEM; - - adapter->cmd_pool = cmd_array; - - /* Allocate and initialize command buffers */ - for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { - cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER); - if (!cmd_array[i].skb) { - mwifiex_dbg(adapter, ERROR, - "unable to allocate command buffer\n"); - return -ENOMEM; - } - } - - for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) - mwifiex_insert_cmd_to_free_q(adapter, &cmd_array[i]); - - return 0; -} - -/* - * This function frees the command buffers. - * - * The function calls the completion callback for all the command - * buffers that still have response buffers associated with them. - */ -int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) -{ - struct cmd_ctrl_node *cmd_array; - u32 i; - - /* Need to check if cmd pool is allocated or not */ - if (!adapter->cmd_pool) { - mwifiex_dbg(adapter, FATAL, - "info: FREE_CMD_BUF: cmd_pool is null\n"); - return 0; - } - - cmd_array = adapter->cmd_pool; - - /* Release shared memory buffers */ - for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { - if (cmd_array[i].skb) { - mwifiex_dbg(adapter, CMD, - "cmd: free cmd buffer %d\n", i); - dev_kfree_skb_any(cmd_array[i].skb); - } - if (!cmd_array[i].resp_skb) - continue; - - if (adapter->iface_type == MWIFIEX_USB) - adapter->if_ops.cmdrsp_complete(adapter, - cmd_array[i].resp_skb); - else - dev_kfree_skb_any(cmd_array[i].resp_skb); - } - /* Release struct cmd_ctrl_node */ - if (adapter->cmd_pool) { - mwifiex_dbg(adapter, CMD, - "cmd: free cmd pool\n"); - kfree(adapter->cmd_pool); - adapter->cmd_pool = NULL; - } - - return 0; -} - -/* - * This function handles events generated by firmware. - * - * Event body of events received from firmware are not used (though they are - * saved), only the event ID is used. Some events are re-invoked by - * the driver, with a new event body. - * - * After processing, the function calls the completion callback - * for cleanup. - */ -int mwifiex_process_event(struct mwifiex_adapter *adapter) -{ - int ret; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - struct sk_buff *skb = adapter->event_skb; - u32 eventcause = adapter->event_cause; - struct mwifiex_rxinfo *rx_info; - - /* Save the last event to debug log */ - adapter->dbg.last_event_index = - (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM; - adapter->dbg.last_event[adapter->dbg.last_event_index] = - (u16) eventcause; - - /* Get BSS number and corresponding priv */ - priv = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause), - EVENT_GET_BSS_TYPE(eventcause)); - if (!priv) - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - /* Clear BSS_NO_BITS from event */ - eventcause &= EVENT_ID_MASK; - adapter->event_cause = eventcause; - - if (skb) { - rx_info = MWIFIEX_SKB_RXCB(skb); - memset(rx_info, 0, sizeof(*rx_info)); - rx_info->bss_num = priv->bss_num; - rx_info->bss_type = priv->bss_type; - mwifiex_dbg_dump(adapter, EVT_D, "Event Buf:", - skb->data, skb->len); - } - - mwifiex_dbg(adapter, EVENT, "EVENT: cause: %#x\n", eventcause); - - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) - ret = mwifiex_process_uap_event(priv); - else - ret = mwifiex_process_sta_event(priv); - - adapter->event_cause = 0; - adapter->event_skb = NULL; - adapter->if_ops.event_complete(adapter, skb); - - return ret; -} - -/* - * This function prepares a command and send it to the firmware. - * - * Preparation includes - - * - Sanity tests to make sure the card is still present or the FW - * is not reset - * - Getting a new command node from the command free queue - * - Initializing the command node for default parameters - * - Fill up the non-default parameters and buffer pointers - * - Add the command to pending queue - */ -int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, - u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync) -{ - int ret; - struct mwifiex_adapter *adapter = priv->adapter; - struct cmd_ctrl_node *cmd_node; - struct host_cmd_ds_command *cmd_ptr; - - if (!adapter) { - pr_err("PREP_CMD: adapter is NULL\n"); - return -1; - } - - if (adapter->is_suspended) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: device in suspended state\n"); - return -1; - } - - if (adapter->hs_enabling && cmd_no != HostCmd_CMD_802_11_HS_CFG_ENH) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: host entering sleep state\n"); - return -1; - } - - if (adapter->surprise_removed) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: card is removed\n"); - return -1; - } - - if (adapter->is_cmd_timedout) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: FW is in bad state\n"); - return -1; - } - - if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) { - if (cmd_no != HostCmd_CMD_FUNC_INIT) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: FW in reset state\n"); - return -1; - } - } - - /* Get a new command node */ - cmd_node = mwifiex_get_cmd_node(adapter); - - if (!cmd_node) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: no free cmd node\n"); - return -1; - } - - /* Initialize the command node */ - mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf, sync); - - if (!cmd_node->cmd_skb) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: no free cmd buf\n"); - return -1; - } - - memset(skb_put(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command)), - 0, sizeof(struct host_cmd_ds_command)); - - cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->result = 0; - - /* Prepare command */ - if (cmd_no) { - switch (cmd_no) { - case HostCmd_CMD_UAP_SYS_CONFIG: - case HostCmd_CMD_UAP_BSS_START: - case HostCmd_CMD_UAP_BSS_STOP: - case HostCmd_CMD_UAP_STA_DEAUTH: - case HOST_CMD_APCMD_SYS_RESET: - case HOST_CMD_APCMD_STA_LIST: - ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action, - cmd_oid, data_buf, - cmd_ptr); - break; - default: - ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action, - cmd_oid, data_buf, - cmd_ptr); - break; - } - } else { - ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf); - cmd_node->cmd_flag |= CMD_F_HOSTCMD; - } - - /* Return error, since the command preparation failed */ - if (ret) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: cmd %#x preparation failed\n", - cmd_no); - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - return -1; - } - - /* Send command */ - if (cmd_no == HostCmd_CMD_802_11_SCAN || - cmd_no == HostCmd_CMD_802_11_SCAN_EXT) { - mwifiex_queue_scan_cmd(priv, cmd_node); - } else { - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); - queue_work(adapter->workqueue, &adapter->main_work); - if (cmd_node->wait_q_enabled) - ret = mwifiex_wait_queue_complete(adapter, cmd_node); - } - - return ret; -} - -/* - * This function returns a command to the command free queue. - * - * The function also calls the completion callback if required, before - * cleaning the command node and re-inserting it into the free queue. - */ -void -mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node) -{ - unsigned long flags; - - if (!cmd_node) - return; - - if (cmd_node->wait_q_enabled) - mwifiex_complete_cmd(adapter, cmd_node); - /* Clean the node */ - mwifiex_clean_cmd_node(adapter, cmd_node); - - /* Insert node into cmd_free_q */ - spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); - list_add_tail(&cmd_node->list, &adapter->cmd_free_q); - spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); -} - -/* This function reuses a command node. */ -void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node) -{ - struct host_cmd_ds_command *host_cmd = (void *)cmd_node->cmd_skb->data; - - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - - atomic_dec(&adapter->cmd_pending); - mwifiex_dbg(adapter, CMD, - "cmd: FREE_CMD: cmd=%#x, cmd_pending=%d\n", - le16_to_cpu(host_cmd->command), - atomic_read(&adapter->cmd_pending)); -} - -/* - * This function queues a command to the command pending queue. - * - * This in effect adds the command to the command list to be executed. - * Exit PS command is handled specially, by placing it always to the - * front of the command queue. - */ -void -mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node, u32 add_tail) -{ - struct host_cmd_ds_command *host_cmd = NULL; - u16 command; - unsigned long flags; - - host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); - if (!host_cmd) { - mwifiex_dbg(adapter, ERROR, "QUEUE_CMD: host_cmd is NULL\n"); - return; - } - - command = le16_to_cpu(host_cmd->command); - - /* Exit_PS command needs to be queued in the header always. */ - if (command == HostCmd_CMD_802_11_PS_MODE_ENH) { - struct host_cmd_ds_802_11_ps_mode_enh *pm = - &host_cmd->params.psmode_enh; - if ((le16_to_cpu(pm->action) == DIS_PS) || - (le16_to_cpu(pm->action) == DIS_AUTO_PS)) { - if (adapter->ps_state != PS_STATE_AWAKE) - add_tail = false; - } - } - - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); - if (add_tail) - list_add_tail(&cmd_node->list, &adapter->cmd_pending_q); - else - list_add(&cmd_node->list, &adapter->cmd_pending_q); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); - - atomic_inc(&adapter->cmd_pending); - mwifiex_dbg(adapter, CMD, - "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n", - command, atomic_read(&adapter->cmd_pending)); -} - -/* - * This function executes the next command in command pending queue. - * - * This function will fail if a command is already in processing stage, - * otherwise it will dequeue the first command from the command pending - * queue and send to the firmware. - * - * If the device is currently in host sleep mode, any commands, except the - * host sleep configuration command will de-activate the host sleep. For PS - * mode, the function will put the firmware back to sleep if applicable. - */ -int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - struct cmd_ctrl_node *cmd_node; - int ret = 0; - struct host_cmd_ds_command *host_cmd; - unsigned long cmd_flags; - unsigned long cmd_pending_q_flags; - - /* Check if already in processing */ - if (adapter->curr_cmd) { - mwifiex_dbg(adapter, FATAL, - "EXEC_NEXT_CMD: cmd in processing\n"); - return -1; - } - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); - /* Check if any command is pending */ - spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); - if (list_empty(&adapter->cmd_pending_q)) { - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, - cmd_pending_q_flags); - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - return 0; - } - cmd_node = list_first_entry(&adapter->cmd_pending_q, - struct cmd_ctrl_node, list); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, - cmd_pending_q_flags); - - host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); - priv = cmd_node->priv; - - if (adapter->ps_state != PS_STATE_AWAKE) { - mwifiex_dbg(adapter, ERROR, - "%s: cannot send cmd in sleep state,\t" - "this should not happen\n", __func__); - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - return ret; - } - - spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, - cmd_pending_q_flags); - - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node); - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - /* Any command sent to the firmware when host is in sleep - * mode should de-configure host sleep. We should skip the - * host sleep configuration command itself though - */ - if (priv && (host_cmd->command != - cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) { - if (adapter->hs_activated) { - adapter->is_hs_configured = false; - mwifiex_hs_activated_event(priv, false); - } - } - - return ret; -} - -/* - * This function handles the command response. - * - * After processing, the function cleans the command node and puts - * it back to the command free queue. - */ -int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) -{ - struct host_cmd_ds_command *resp; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - int ret = 0; - uint16_t orig_cmdresp_no; - uint16_t cmdresp_no; - uint16_t cmdresp_result; - unsigned long flags; - - /* Now we got response from FW, cancel the command timer */ - del_timer_sync(&adapter->cmd_timer); - - if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) { - resp = (struct host_cmd_ds_command *) adapter->upld_buf; - mwifiex_dbg(adapter, ERROR, - "CMD_RESP: NULL curr_cmd, %#x\n", - le16_to_cpu(resp->command)); - return -1; - } - - adapter->is_cmd_timedout = 0; - - resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data; - if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { - /* Copy original response back to response buffer */ - struct mwifiex_ds_misc_cmd *hostcmd; - uint16_t size = le16_to_cpu(resp->size); - mwifiex_dbg(adapter, INFO, - "info: host cmd resp size = %d\n", size); - size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER); - if (adapter->curr_cmd->data_buf) { - hostcmd = adapter->curr_cmd->data_buf; - hostcmd->len = size; - memcpy(hostcmd->cmd, resp, size); - } - } - orig_cmdresp_no = le16_to_cpu(resp->command); - - /* Get BSS number and corresponding priv */ - priv = mwifiex_get_priv_by_id(adapter, - HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)), - HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num))); - if (!priv) - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - /* Clear RET_BIT from HostCmd */ - resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_CMD_ID_MASK); - - cmdresp_no = le16_to_cpu(resp->command); - cmdresp_result = le16_to_cpu(resp->result); - - /* Save the last command response to debug log */ - adapter->dbg.last_cmd_resp_index = - (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM; - adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] = - orig_cmdresp_no; - - mwifiex_dbg(adapter, CMD, - "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", - orig_cmdresp_no, cmdresp_result, - le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num)); - mwifiex_dbg_dump(adapter, CMD_D, "CMD_RESP buffer:", resp, - le16_to_cpu(resp->size)); - - if (!(orig_cmdresp_no & HostCmd_RET_BIT)) { - mwifiex_dbg(adapter, ERROR, "CMD_RESP: invalid cmd resp\n"); - if (adapter->curr_cmd->wait_q_enabled) - adapter->cmd_wait_q.status = -1; - - mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - return -1; - } - - if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { - adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD; - if ((cmdresp_result == HostCmd_RESULT_OK) && - (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH)) - ret = mwifiex_ret_802_11_hs_cfg(priv, resp); - } else { - /* handle response */ - ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp); - } - - /* Check init command response */ - if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { - if (ret) { - mwifiex_dbg(adapter, ERROR, - "%s: cmd %#x failed during\t" - "initialization\n", __func__, cmdresp_no); - mwifiex_init_fw_complete(adapter); - return -1; - } else if (adapter->last_init_cmd == cmdresp_no) - adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE; - } - - if (adapter->curr_cmd) { - if (adapter->curr_cmd->wait_q_enabled) - adapter->cmd_wait_q.status = ret; - - mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - } - - return ret; -} - -/* - * This function handles the timeout of command sending. - * - * It will re-send the same command again. - */ -void -mwifiex_cmd_timeout_func(unsigned long function_context) -{ - struct mwifiex_adapter *adapter = - (struct mwifiex_adapter *) function_context; - struct cmd_ctrl_node *cmd_node; - - adapter->is_cmd_timedout = 1; - if (!adapter->curr_cmd) { - mwifiex_dbg(adapter, ERROR, - "cmd: empty curr_cmd\n"); - return; - } - cmd_node = adapter->curr_cmd; - if (cmd_node) { - adapter->dbg.timeout_cmd_id = - adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index]; - adapter->dbg.timeout_cmd_act = - adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index]; - mwifiex_dbg(adapter, MSG, - "%s: Timeout cmd id = %#x, act = %#x\n", __func__, - adapter->dbg.timeout_cmd_id, - adapter->dbg.timeout_cmd_act); - - mwifiex_dbg(adapter, MSG, - "num_data_h2c_failure = %d\n", - adapter->dbg.num_tx_host_to_card_failure); - mwifiex_dbg(adapter, MSG, - "num_cmd_h2c_failure = %d\n", - adapter->dbg.num_cmd_host_to_card_failure); - - mwifiex_dbg(adapter, MSG, - "is_cmd_timedout = %d\n", - adapter->is_cmd_timedout); - mwifiex_dbg(adapter, MSG, - "num_tx_timeout = %d\n", - adapter->dbg.num_tx_timeout); - - mwifiex_dbg(adapter, MSG, - "last_cmd_index = %d\n", - adapter->dbg.last_cmd_index); - mwifiex_dbg(adapter, MSG, - "last_cmd_id: %*ph\n", - (int)sizeof(adapter->dbg.last_cmd_id), - adapter->dbg.last_cmd_id); - mwifiex_dbg(adapter, MSG, - "last_cmd_act: %*ph\n", - (int)sizeof(adapter->dbg.last_cmd_act), - adapter->dbg.last_cmd_act); - - mwifiex_dbg(adapter, MSG, - "last_cmd_resp_index = %d\n", - adapter->dbg.last_cmd_resp_index); - mwifiex_dbg(adapter, MSG, - "last_cmd_resp_id: %*ph\n", - (int)sizeof(adapter->dbg.last_cmd_resp_id), - adapter->dbg.last_cmd_resp_id); - - mwifiex_dbg(adapter, MSG, - "last_event_index = %d\n", - adapter->dbg.last_event_index); - mwifiex_dbg(adapter, MSG, - "last_event: %*ph\n", - (int)sizeof(adapter->dbg.last_event), - adapter->dbg.last_event); - - mwifiex_dbg(adapter, MSG, - "data_sent=%d cmd_sent=%d\n", - adapter->data_sent, adapter->cmd_sent); - - mwifiex_dbg(adapter, MSG, - "ps_mode=%d ps_state=%d\n", - adapter->ps_mode, adapter->ps_state); - - if (cmd_node->wait_q_enabled) { - adapter->cmd_wait_q.status = -ETIMEDOUT; - mwifiex_cancel_pending_ioctl(adapter); - } - } - if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { - mwifiex_init_fw_complete(adapter); - return; - } - - if (adapter->if_ops.device_dump) - adapter->if_ops.device_dump(adapter); - - if (adapter->if_ops.card_reset) - adapter->if_ops.card_reset(adapter); -} - -/* - * This function cancels all the pending commands. - * - * The current command, all commands in command pending queue and all scan - * commands in scan pending queue are cancelled. All the completion callbacks - * are called with failure status to ensure cleanup. - */ -void -mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) -{ - struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; - unsigned long flags, cmd_flags; - struct mwifiex_private *priv; - int i; - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); - /* Cancel current cmd */ - if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { - adapter->curr_cmd->wait_q_enabled = false; - adapter->cmd_wait_q.status = -1; - mwifiex_complete_cmd(adapter, adapter->curr_cmd); - /* no recycle probably wait for response */ - } - /* Cancel all pending command */ - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->cmd_pending_q, list) { - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); - - if (cmd_node->wait_q_enabled) - adapter->cmd_wait_q.status = -1; - mwifiex_recycle_cmd_node(adapter, cmd_node); - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); - } - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - - /* Cancel all pending scan command */ - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, list) { - list_del(&cmd_node->list); - - cmd_node->wait_q_enabled = false; - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - - if (adapter->scan_processing) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - if (priv->scan_request) { - mwifiex_dbg(adapter, WARN, "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); - priv->scan_request = NULL; - } - } - } -} - -/* - * This function cancels all pending commands that matches with - * the given IOCTL request. - * - * Both the current command buffer and the pending command queue are - * searched for matching IOCTL request. The completion callback of - * the matched command is called with failure status to ensure cleanup. - * In case of scan commands, all pending commands in scan pending queue - * are cancelled. - */ -void -mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) -{ - struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; - unsigned long cmd_flags; - unsigned long scan_pending_q_flags; - struct mwifiex_private *priv; - int i; - - if ((adapter->curr_cmd) && - (adapter->curr_cmd->wait_q_enabled)) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); - cmd_node = adapter->curr_cmd; - /* setting curr_cmd to NULL is quite dangerous, because - * mwifiex_process_cmdresp checks curr_cmd to be != NULL - * at the beginning then relies on it and dereferences - * it at will - * this probably works since mwifiex_cmd_timeout_func - * is the only caller of this function and responses - * at that point - */ - adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - - mwifiex_recycle_cmd_node(adapter, cmd_node); - } - - /* Cancel all pending scan command */ - spin_lock_irqsave(&adapter->scan_pending_q_lock, - scan_pending_q_flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, list) { - list_del(&cmd_node->list); - cmd_node->wait_q_enabled = false; - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - scan_pending_q_flags); - - if (adapter->scan_processing) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - if (priv->scan_request) { - mwifiex_dbg(adapter, WARN, "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); - priv->scan_request = NULL; - } - } - } -} - -/* - * This function sends the sleep confirm command to firmware, if - * possible. - * - * The sleep confirm command cannot be issued if command response, - * data response or event response is awaiting handling, or if we - * are in the middle of sending a command, or expecting a command - * response. - */ -void -mwifiex_check_ps_cond(struct mwifiex_adapter *adapter) -{ - if (!adapter->cmd_sent && - !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter)) - mwifiex_dnld_sleep_confirm_cmd(adapter); - else - mwifiex_dbg(adapter, CMD, - "cmd: Delay Sleep Confirm (%s%s%s)\n", - (adapter->cmd_sent) ? "D" : "", - (adapter->curr_cmd) ? "C" : "", - (IS_CARD_RX_RCVD(adapter)) ? "R" : ""); -} - -/* - * This function sends a Host Sleep activated event to applications. - * - * This event is generated by the driver, with a blank event body. - */ -void -mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated) -{ - if (activated) { - if (priv->adapter->is_hs_configured) { - priv->adapter->hs_activated = true; - mwifiex_update_rxreor_flags(priv->adapter, - RXREOR_FORCE_NO_DROP); - mwifiex_dbg(priv->adapter, EVENT, - "event: hs_activated\n"); - priv->adapter->hs_activate_wait_q_woken = true; - wake_up_interruptible( - &priv->adapter->hs_activate_wait_q); - } else { - mwifiex_dbg(priv->adapter, EVENT, - "event: HS not configured\n"); - } - } else { - mwifiex_dbg(priv->adapter, EVENT, - "event: hs_deactivated\n"); - priv->adapter->hs_activated = false; - } -} - -/* - * This function handles the command response of a Host Sleep configuration - * command. - * - * Handling includes changing the header fields into CPU format - * and setting the current host sleep activation status in driver. - * - * In case host sleep status change, the function generates an event to - * notify the applications. - */ -int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg = - &resp->params.opt_hs_cfg; - uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions); - - if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) && - adapter->iface_type != MWIFIEX_USB) { - mwifiex_hs_activated_event(priv, true); - return 0; - } else { - mwifiex_dbg(adapter, CMD, - "cmd: CMD_RESP: HS_CFG cmd reply\t" - " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n", - resp->result, conditions, - phs_cfg->params.hs_config.gpio, - phs_cfg->params.hs_config.gap); - } - if (conditions != HS_CFG_CANCEL) { - adapter->is_hs_configured = true; - if (adapter->iface_type == MWIFIEX_USB) - mwifiex_hs_activated_event(priv, true); - } else { - adapter->is_hs_configured = false; - if (adapter->hs_activated) - mwifiex_hs_activated_event(priv, false); - } - - return 0; -} - -/* - * This function wakes up the adapter and generates a Host Sleep - * cancel event on receiving the power up interrupt. - */ -void -mwifiex_process_hs_config(struct mwifiex_adapter *adapter) -{ - mwifiex_dbg(adapter, INFO, - "info: %s: auto cancelling host sleep\t" - "since there is interrupt from the firmware\n", - __func__); - - adapter->if_ops.wakeup(adapter); - adapter->hs_activated = false; - adapter->is_hs_configured = false; - adapter->is_suspended = false; - mwifiex_hs_activated_event(mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY), - false); -} -EXPORT_SYMBOL_GPL(mwifiex_process_hs_config); - -/* - * This function handles the command response of a sleep confirm command. - * - * The function sets the card state to SLEEP if the response indicates success. - */ -void -mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter, - u8 *pbuf, u32 upld_len) -{ - struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - uint16_t result = le16_to_cpu(cmd->result); - uint16_t command = le16_to_cpu(cmd->command); - uint16_t seq_num = le16_to_cpu(cmd->seq_num); - - if (!upld_len) { - mwifiex_dbg(adapter, ERROR, - "%s: cmd size is 0\n", __func__); - return; - } - - mwifiex_dbg(adapter, CMD, - "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", - command, result, le16_to_cpu(cmd->size), seq_num); - - /* Get BSS number and corresponding priv */ - priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num), - HostCmd_GET_BSS_TYPE(seq_num)); - if (!priv) - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - /* Update sequence number */ - seq_num = HostCmd_GET_SEQ_NO(seq_num); - /* Clear RET_BIT from HostCmd */ - command &= HostCmd_CMD_ID_MASK; - - if (command != HostCmd_CMD_802_11_PS_MODE_ENH) { - mwifiex_dbg(adapter, ERROR, - "%s: rcvd unexpected resp for cmd %#x, result = %x\n", - __func__, command, result); - return; - } - - if (result) { - mwifiex_dbg(adapter, ERROR, - "%s: sleep confirm cmd failed\n", - __func__); - adapter->pm_wakeup_card_req = false; - adapter->ps_state = PS_STATE_AWAKE; - return; - } - adapter->pm_wakeup_card_req = true; - if (adapter->is_hs_configured) - mwifiex_hs_activated_event(mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - true); - adapter->ps_state = PS_STATE_SLEEP; - cmd->command = cpu_to_le16(command); - cmd->seq_num = cpu_to_le16(seq_num); -} -EXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp); - -/* - * This function prepares an enhanced power mode command. - * - * This function can be used to disable power save or to configure - * power save with auto PS or STA PS or auto deep sleep. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting Power Save bitmap, PS parameters TLV, PS mode TLV, - * auto deep sleep TLV (as required) - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, uint16_t ps_bitmap, - struct mwifiex_ds_auto_ds *auto_ds) -{ - struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh = - &cmd->params.psmode_enh; - u8 *tlv; - u16 cmd_size = 0; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); - if (cmd_action == DIS_AUTO_PS) { - psmode_enh->action = cpu_to_le16(DIS_AUTO_PS); - psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); - cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + - sizeof(psmode_enh->params.ps_bitmap)); - } else if (cmd_action == GET_PS) { - psmode_enh->action = cpu_to_le16(GET_PS); - psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); - cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + - sizeof(psmode_enh->params.ps_bitmap)); - } else if (cmd_action == EN_AUTO_PS) { - psmode_enh->action = cpu_to_le16(EN_AUTO_PS); - psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); - cmd_size = S_DS_GEN + sizeof(psmode_enh->action) + - sizeof(psmode_enh->params.ps_bitmap); - tlv = (u8 *) cmd + cmd_size; - if (ps_bitmap & BITMAP_STA_PS) { - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie_types_ps_param *ps_tlv = - (struct mwifiex_ie_types_ps_param *) tlv; - struct mwifiex_ps_param *ps_mode = &ps_tlv->param; - ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM); - ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) - - sizeof(struct mwifiex_ie_types_header)); - cmd_size += sizeof(*ps_tlv); - tlv += sizeof(*ps_tlv); - mwifiex_dbg(priv->adapter, CMD, - "cmd: PS Command: Enter PS\n"); - ps_mode->null_pkt_interval = - cpu_to_le16(adapter->null_pkt_interval); - ps_mode->multiple_dtims = - cpu_to_le16(adapter->multiple_dtim); - ps_mode->bcn_miss_timeout = - cpu_to_le16(adapter->bcn_miss_time_out); - ps_mode->local_listen_interval = - cpu_to_le16(adapter->local_listen_interval); - ps_mode->adhoc_wake_period = - cpu_to_le16(adapter->adhoc_awake_period); - ps_mode->delay_to_ps = - cpu_to_le16(adapter->delay_to_ps); - ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode); - - } - if (ps_bitmap & BITMAP_AUTO_DS) { - struct mwifiex_ie_types_auto_ds_param *auto_ds_tlv = - (struct mwifiex_ie_types_auto_ds_param *) tlv; - u16 idletime = 0; - - auto_ds_tlv->header.type = - cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); - auto_ds_tlv->header.len = - cpu_to_le16(sizeof(*auto_ds_tlv) - - sizeof(struct mwifiex_ie_types_header)); - cmd_size += sizeof(*auto_ds_tlv); - tlv += sizeof(*auto_ds_tlv); - if (auto_ds) - idletime = auto_ds->idle_time; - mwifiex_dbg(priv->adapter, CMD, - "cmd: PS Command: Enter Auto Deep Sleep\n"); - auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime); - } - cmd->size = cpu_to_le16(cmd_size); - } - return 0; -} - -/* - * This function handles the command response of an enhanced power mode - * command. - * - * Handling includes changing the header fields into CPU format - * and setting the current enhanced power mode in driver. - */ -int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - struct mwifiex_ds_pm_cfg *pm_cfg) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_ps_mode_enh *ps_mode = - &resp->params.psmode_enh; - uint16_t action = le16_to_cpu(ps_mode->action); - uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap); - uint16_t auto_ps_bitmap = - le16_to_cpu(ps_mode->params.ps_bitmap); - - mwifiex_dbg(adapter, INFO, - "info: %s: PS_MODE cmd reply result=%#x action=%#X\n", - __func__, resp->result, action); - if (action == EN_AUTO_PS) { - if (auto_ps_bitmap & BITMAP_AUTO_DS) { - mwifiex_dbg(adapter, CMD, - "cmd: Enabled auto deep sleep\n"); - priv->adapter->is_deep_sleep = true; - } - if (auto_ps_bitmap & BITMAP_STA_PS) { - mwifiex_dbg(adapter, CMD, - "cmd: Enabled STA power save\n"); - if (adapter->sleep_period.period) - mwifiex_dbg(adapter, CMD, - "cmd: set to uapsd/pps mode\n"); - } - } else if (action == DIS_AUTO_PS) { - if (ps_bitmap & BITMAP_AUTO_DS) { - priv->adapter->is_deep_sleep = false; - mwifiex_dbg(adapter, CMD, - "cmd: Disabled auto deep sleep\n"); - } - if (ps_bitmap & BITMAP_STA_PS) { - mwifiex_dbg(adapter, CMD, - "cmd: Disabled STA power save\n"); - if (adapter->sleep_period.period) { - adapter->delay_null_pkt = false; - adapter->tx_lock_flag = false; - adapter->pps_uapsd_mode = false; - } - } - } else if (action == GET_PS) { - if (ps_bitmap & BITMAP_STA_PS) - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; - else - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; - - mwifiex_dbg(adapter, CMD, - "cmd: ps_bitmap=%#x\n", ps_bitmap); - - if (pm_cfg) { - /* This section is for get power save mode */ - if (ps_bitmap & BITMAP_STA_PS) - pm_cfg->param.ps_mode = 1; - else - pm_cfg->param.ps_mode = 0; - } - } - return 0; -} - -/* - * This function prepares command to get hardware specifications. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting permanent address parameter - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd) -{ - struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec; - - cmd->command = cpu_to_le16(HostCmd_CMD_GET_HW_SPEC); - cmd->size = - cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN); - memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN); - - return 0; -} - -/* - * This function handles the command response of get hardware - * specifications. - * - * Handling includes changing the header fields into CPU format - * and saving/updating the following parameters in driver - - * - Firmware capability information - * - Firmware band settings - * - Ad-hoc start band and channel - * - Ad-hoc 11n activation status - * - Firmware release number - * - Number of antennas - * - Hardware address - * - Hardware interface version - * - Firmware version - * - Region code - * - 11n capabilities - * - MCS support fields - * - MP end port - */ -int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie_types_header *tlv; - struct hw_spec_api_rev *api_rev; - u16 resp_size, api_id; - int i, left_len, parsed_len = 0; - - adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info); - - if (IS_SUPPORT_MULTI_BANDS(adapter)) - adapter->fw_bands = (u8) GET_FW_DEFAULT_BANDS(adapter); - else - adapter->fw_bands = BAND_B; - - adapter->config_bands = adapter->fw_bands; - - if (adapter->fw_bands & BAND_A) { - if (adapter->fw_bands & BAND_GN) { - adapter->config_bands |= BAND_AN; - adapter->fw_bands |= BAND_AN; - } - if (adapter->fw_bands & BAND_AN) { - adapter->adhoc_start_band = BAND_A | BAND_AN; - adapter->adhoc_11n_enabled = true; - } else { - adapter->adhoc_start_band = BAND_A; - } - priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A; - } else if (adapter->fw_bands & BAND_GN) { - adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; - priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; - adapter->adhoc_11n_enabled = true; - } else if (adapter->fw_bands & BAND_G) { - adapter->adhoc_start_band = BAND_G | BAND_B; - priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; - } else if (adapter->fw_bands & BAND_B) { - adapter->adhoc_start_band = BAND_B; - priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; - } - - adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number); - adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff; - adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna); - - if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) { - adapter->is_hw_11ac_capable = true; - - /* Copy 11AC cap */ - adapter->hw_dot_11ac_dev_cap = - le32_to_cpu(hw_spec->dot_11ac_dev_cap); - adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap - & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; - adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap - & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; - - /* Copy 11AC mcs */ - adapter->hw_dot_11ac_mcs_support = - le32_to_cpu(hw_spec->dot_11ac_mcs_support); - adapter->usr_dot_11ac_mcs_support = - adapter->hw_dot_11ac_mcs_support; - } else { - adapter->is_hw_11ac_capable = false; - } - - resp_size = le16_to_cpu(resp->size) - S_DS_GEN; - if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) { - /* we have variable HW SPEC information */ - left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec); - while (left_len > sizeof(struct mwifiex_ie_types_header)) { - tlv = (void *)&hw_spec->tlvs + parsed_len; - switch (le16_to_cpu(tlv->type)) { - case TLV_TYPE_API_REV: - api_rev = (struct hw_spec_api_rev *)tlv; - api_id = le16_to_cpu(api_rev->api_id); - switch (api_id) { - case KEY_API_VER_ID: - adapter->key_api_major_ver = - api_rev->major_ver; - adapter->key_api_minor_ver = - api_rev->minor_ver; - mwifiex_dbg(adapter, INFO, - "key_api v%d.%d\n", - adapter->key_api_major_ver, - adapter->key_api_minor_ver); - break; - case FW_API_VER_ID: - adapter->fw_api_ver = - api_rev->major_ver; - mwifiex_dbg(adapter, INFO, - "Firmware api version %d\n", - adapter->fw_api_ver); - break; - default: - mwifiex_dbg(adapter, FATAL, - "Unknown api_id: %d\n", - api_id); - break; - } - break; - default: - mwifiex_dbg(adapter, FATAL, - "Unknown GET_HW_SPEC TLV type: %#x\n", - le16_to_cpu(tlv->type)); - break; - } - parsed_len += le16_to_cpu(tlv->len) + - sizeof(struct mwifiex_ie_types_header); - left_len -= le16_to_cpu(tlv->len) + - sizeof(struct mwifiex_ie_types_header); - } - } - - mwifiex_dbg(adapter, INFO, - "info: GET_HW_SPEC: fw_release_number- %#x\n", - adapter->fw_release_number); - mwifiex_dbg(adapter, INFO, - "info: GET_HW_SPEC: permanent addr: %pM\n", - hw_spec->permanent_addr); - mwifiex_dbg(adapter, INFO, - "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n", - le16_to_cpu(hw_spec->hw_if_version), - le16_to_cpu(hw_spec->version)); - - ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr); - adapter->region_code = le16_to_cpu(hw_spec->region_code); - - for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++) - /* Use the region code to search for the index */ - if (adapter->region_code == region_code_index[i]) - break; - - /* If it's unidentified region code, use the default (USA) */ - if (i >= MWIFIEX_MAX_REGION_CODE) { - adapter->region_code = 0x10; - mwifiex_dbg(adapter, WARN, - "cmd: unknown region code, use default (USA)\n"); - } - - adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap); - adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; - adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support; - - if (adapter->if_ops.update_mp_end_port) - adapter->if_ops.update_mp_end_port(adapter, - le16_to_cpu(hw_spec->mp_end_port)); - - if (adapter->fw_api_ver == MWIFIEX_FW_V15) - adapter->scan_chan_gap_enabled = true; - - return 0; -} diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c deleted file mode 100644 index 9824d8dd2b44..000000000000 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ /dev/null @@ -1,1005 +0,0 @@ -/* - * Marvell Wireless LAN device driver: debugfs - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" -#include "11n.h" - - -static struct dentry *mwifiex_dfs_dir; - -static char *bss_modes[] = { - "UNSPECIFIED", - "ADHOC", - "STATION", - "AP", - "AP_VLAN", - "WDS", - "MONITOR", - "MESH_POINT", - "P2P_CLIENT", - "P2P_GO", - "P2P_DEVICE", -}; - -/* - * Proc info file read handler. - * - * This function is called when the 'info' file is opened for reading. - * It prints the following driver related information - - * - Driver name - * - Driver version - * - Driver extended version - * - Interface name - * - BSS mode - * - Media state (connected or disconnected) - * - MAC address - * - Total number of Tx bytes - * - Total number of Rx bytes - * - Total number of Tx packets - * - Total number of Rx packets - * - Total number of dropped Tx packets - * - Total number of dropped Rx packets - * - Total number of corrupted Tx packets - * - Total number of corrupted Rx packets - * - Carrier status (on or off) - * - Tx queue status (started or stopped) - * - * For STA mode drivers, it also prints the following extra - - * - ESSID - * - BSSID - * - Channel - * - Region code - * - Multicast count - * - Multicast addresses - */ -static ssize_t -mwifiex_info_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *) file->private_data; - struct net_device *netdev = priv->netdev; - struct netdev_hw_addr *ha; - struct netdev_queue *txq; - unsigned long page = get_zeroed_page(GFP_KERNEL); - char *p = (char *) page, fmt[64]; - struct mwifiex_bss_info info; - ssize_t ret; - int i = 0; - - if (!p) - return -ENOMEM; - - memset(&info, 0, sizeof(info)); - ret = mwifiex_get_bss_info(priv, &info); - if (ret) - goto free_and_exit; - - mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1); - - if (!priv->version_str[0]) - mwifiex_get_ver_ext(priv); - - p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); - p += sprintf(p, "driver_version = %s", fmt); - p += sprintf(p, "\nverext = %s", priv->version_str); - p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name); - - if (info.bss_mode >= ARRAY_SIZE(bss_modes)) - p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode); - else - p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]); - - p += sprintf(p, "media_state=\"%s\"\n", - (!priv->media_connected ? "Disconnected" : "Connected")); - p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { - p += sprintf(p, "multicast_count=\"%d\"\n", - netdev_mc_count(netdev)); - p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid); - p += sprintf(p, "bssid=\"%pM\"\n", info.bssid); - p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan); - p += sprintf(p, "country_code = \"%s\"\n", info.country_code); - - netdev_for_each_mc_addr(ha, netdev) - p += sprintf(p, "multicast_address[%d]=\"%pM\"\n", - i++, ha->addr); - } - - p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); - p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); - p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); - p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); - p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); - p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); - p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); - p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); - p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) - ? "on" : "off")); - p += sprintf(p, "tx queue"); - for (i = 0; i < netdev->num_tx_queues; i++) { - txq = netdev_get_tx_queue(netdev, i); - p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ? - "stopped" : "started"); - } - p += sprintf(p, "\n"); - - ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, - (unsigned long) p - page); - -free_and_exit: - free_page(page); - return ret; -} - -/* - * Proc device dump read handler. - * - * This function is called when the 'device_dump' file is opened for - * reading. - * This function dumps driver information and firmware memory segments - * (ex. DTCM, ITCM, SQRAM etc.) for - * debugging. - */ -static ssize_t -mwifiex_device_dump_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = file->private_data; - - if (!priv->adapter->if_ops.device_dump) - return -EIO; - - priv->adapter->if_ops.device_dump(priv->adapter); - - return 0; -} - -/* - * Proc getlog file read handler. - * - * This function is called when the 'getlog' file is opened for reading - * It prints the following log information - - * - Number of multicast Tx frames - * - Number of failed packets - * - Number of Tx retries - * - Number of multicast Tx retries - * - Number of duplicate frames - * - Number of RTS successes - * - Number of RTS failures - * - Number of ACK failures - * - Number of fragmented Rx frames - * - Number of multicast Rx frames - * - Number of FCS errors - * - Number of Tx frames - * - WEP ICV error counts - * - Number of received beacons - * - Number of missed beacons - */ -static ssize_t -mwifiex_getlog_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *) file->private_data; - unsigned long page = get_zeroed_page(GFP_KERNEL); - char *p = (char *) page; - ssize_t ret; - struct mwifiex_ds_get_stats stats; - - if (!p) - return -ENOMEM; - - memset(&stats, 0, sizeof(stats)); - ret = mwifiex_get_stats_info(priv, &stats); - if (ret) - goto free_and_exit; - - p += sprintf(p, "\n" - "mcasttxframe %u\n" - "failed %u\n" - "retry %u\n" - "multiretry %u\n" - "framedup %u\n" - "rtssuccess %u\n" - "rtsfailure %u\n" - "ackfailure %u\n" - "rxfrag %u\n" - "mcastrxframe %u\n" - "fcserror %u\n" - "txframe %u\n" - "wepicverrcnt-1 %u\n" - "wepicverrcnt-2 %u\n" - "wepicverrcnt-3 %u\n" - "wepicverrcnt-4 %u\n" - "bcn_rcv_cnt %u\n" - "bcn_miss_cnt %u\n", - stats.mcast_tx_frame, - stats.failed, - stats.retry, - stats.multi_retry, - stats.frame_dup, - stats.rts_success, - stats.rts_failure, - stats.ack_failure, - stats.rx_frag, - stats.mcast_rx_frame, - stats.fcs_error, - stats.tx_frame, - stats.wep_icv_error[0], - stats.wep_icv_error[1], - stats.wep_icv_error[2], - stats.wep_icv_error[3], - stats.bcn_rcv_cnt, - stats.bcn_miss_cnt); - - - ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, - (unsigned long) p - page); - -free_and_exit: - free_page(page); - return ret; -} - -/* Sysfs histogram file read handler. - * - * This function is called when the 'histogram' file is opened for reading - * It prints the following histogram information - - * - Number of histogram samples - * - Receive packet number of each rx_rate - * - Receive packet number of each snr - * - Receive packet number of each nosie_flr - * - Receive packet number of each signal streath - */ -static ssize_t -mwifiex_histogram_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *)file->private_data; - ssize_t ret; - struct mwifiex_histogram_data *phist_data; - int i, value; - unsigned long page = get_zeroed_page(GFP_KERNEL); - char *p = (char *)page; - - if (!p) - return -ENOMEM; - - if (!priv || !priv->hist_data) - return -EFAULT; - phist_data = priv->hist_data; - - p += sprintf(p, "\n" - "total samples = %d\n", - atomic_read(&phist_data->num_samples)); - - p += sprintf(p, "rx rates (in Mbps): 0=1M 1=2M"); - p += sprintf(p, "2=5.5M 3=11M 4=6M 5=9M 6=12M\n"); - p += sprintf(p, "7=18M 8=24M 9=36M 10=48M 11=54M"); - p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n"); - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { - p += sprintf(p, "44-53=MCS0-9(VHT:BW20)"); - p += sprintf(p, "54-63=MCS0-9(VHT:BW40)"); - p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n"); - } else { - p += sprintf(p, "\n"); - } - - for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) { - value = atomic_read(&phist_data->rx_rate[i]); - if (value) - p += sprintf(p, "rx_rate[%02d] = %d\n", i, value); - } - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { - for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES; - i++) { - value = atomic_read(&phist_data->rx_rate[i]); - if (value) - p += sprintf(p, "rx_rate[%02d] = %d\n", - i, value); - } - } - - for (i = 0; i < MWIFIEX_MAX_SNR; i++) { - value = atomic_read(&phist_data->snr[i]); - if (value) - p += sprintf(p, "snr[%02ddB] = %d\n", i, value); - } - for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) { - value = atomic_read(&phist_data->noise_flr[i]); - if (value) - p += sprintf(p, "noise_flr[-%02ddBm] = %d\n", - (int)(i-128), value); - } - for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) { - value = atomic_read(&phist_data->sig_str[i]); - if (value) - p += sprintf(p, "sig_strength[-%02ddBm] = %d\n", - i, value); - } - - ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, - (unsigned long)p - page); - - return ret; -} - -static ssize_t -mwifiex_histogram_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = (void *)file->private_data; - - if (priv && priv->hist_data) - mwifiex_hist_data_reset(priv); - return 0; -} - -static struct mwifiex_debug_info info; - -/* - * Proc debug file read handler. - * - * This function is called when the 'debug' file is opened for reading - * It prints the following log information - - * - Interrupt count - * - WMM AC VO packets count - * - WMM AC VI packets count - * - WMM AC BE packets count - * - WMM AC BK packets count - * - Maximum Tx buffer size - * - Tx buffer size - * - Current Tx buffer size - * - Power Save mode - * - Power Save state - * - Deep Sleep status - * - Device wakeup required status - * - Number of wakeup tries - * - Host Sleep configured status - * - Host Sleep activated status - * - Number of Tx timeouts - * - Number of command timeouts - * - Last timed out command ID - * - Last timed out command action - * - Last command ID - * - Last command action - * - Last command index - * - Last command response ID - * - Last command response index - * - Last event - * - Last event index - * - Number of host to card command failures - * - Number of sleep confirm command failures - * - Number of host to card data failure - * - Number of deauthentication events - * - Number of disassociation events - * - Number of link lost events - * - Number of deauthentication commands - * - Number of association success commands - * - Number of association failure commands - * - Number of commands sent - * - Number of data packets sent - * - Number of command responses received - * - Number of events received - * - Tx BA stream table (TID, RA) - * - Rx reorder table (TID, TA, Start window, Window size, Buffer) - */ -static ssize_t -mwifiex_debug_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *) file->private_data; - unsigned long page = get_zeroed_page(GFP_KERNEL); - char *p = (char *) page; - ssize_t ret; - - if (!p) - return -ENOMEM; - - ret = mwifiex_get_debug_info(priv, &info); - if (ret) - goto free_and_exit; - - p += mwifiex_debug_info_to_buffer(priv, p, &info); - - ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, - (unsigned long) p - page); - -free_and_exit: - free_page(page); - return ret; -} - -static u32 saved_reg_type, saved_reg_offset, saved_reg_value; - -/* - * Proc regrdwr file write handler. - * - * This function is called when the 'regrdwr' file is opened for writing - * - * This function can be used to write to a register. - */ -static ssize_t -mwifiex_regrdwr_write(struct file *file, - const char __user *ubuf, size_t count, loff_t *ppos) -{ - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *) addr; - size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); - int ret; - u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; - - if (!buf) - return -ENOMEM; - - - if (copy_from_user(buf, ubuf, buf_size)) { - ret = -EFAULT; - goto done; - } - - sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); - - if (reg_type == 0 || reg_offset == 0) { - ret = -EINVAL; - goto done; - } else { - saved_reg_type = reg_type; - saved_reg_offset = reg_offset; - saved_reg_value = reg_value; - ret = count; - } -done: - free_page(addr); - return ret; -} - -/* - * Proc regrdwr file read handler. - * - * This function is called when the 'regrdwr' file is opened for reading - * - * This function can be used to read from a register. - */ -static ssize_t -mwifiex_regrdwr_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *) file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *) addr; - int pos = 0, ret = 0; - u32 reg_value; - - if (!buf) - return -ENOMEM; - - if (!saved_reg_type) { - /* No command has been given */ - pos += snprintf(buf, PAGE_SIZE, "0"); - goto done; - } - /* Set command has been given */ - if (saved_reg_value != UINT_MAX) { - ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset, - saved_reg_value); - - pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", - saved_reg_type, saved_reg_offset, - saved_reg_value); - - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); - - goto done; - } - /* Get command has been given */ - ret = mwifiex_reg_read(priv, saved_reg_type, - saved_reg_offset, ®_value); - if (ret) { - ret = -EINVAL; - goto done; - } - - pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, - saved_reg_offset, reg_value); - - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); - -done: - free_page(addr); - return ret; -} - -/* Proc debug_mask file read handler. - * This function is called when the 'debug_mask' file is opened for reading - * This function can be used read driver debugging mask value. - */ -static ssize_t -mwifiex_debug_mask_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *)file->private_data; - unsigned long page = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)page; - size_t ret = 0; - int pos = 0; - - if (!buf) - return -ENOMEM; - - pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n", - priv->adapter->debug_mask); - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); - - free_page(page); - return ret; -} - -/* Proc debug_mask file read handler. - * This function is called when the 'debug_mask' file is opened for reading - * This function can be used read driver debugging mask value. - */ -static ssize_t -mwifiex_debug_mask_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - int ret; - unsigned long debug_mask; - struct mwifiex_private *priv = (void *)file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (void *)addr; - size_t buf_size = min(count, (size_t)(PAGE_SIZE - 1)); - - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, ubuf, buf_size)) { - ret = -EFAULT; - goto done; - } - - if (kstrtoul(buf, 0, &debug_mask)) { - ret = -EINVAL; - goto done; - } - - priv->adapter->debug_mask = debug_mask; - ret = count; -done: - free_page(addr); - return ret; -} - -/* Proc memrw file write handler. - * This function is called when the 'memrw' file is opened for writing - * This function can be used to write to a memory location. - */ -static ssize_t -mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count, - loff_t *ppos) -{ - int ret; - char cmd; - struct mwifiex_ds_mem_rw mem_rw; - u16 cmd_action; - struct mwifiex_private *priv = (void *)file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (void *)addr; - size_t buf_size = min(count, (size_t)(PAGE_SIZE - 1)); - - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, ubuf, buf_size)) { - ret = -EFAULT; - goto done; - } - - ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value); - if (ret != 3) { - ret = -EINVAL; - goto done; - } - - if ((cmd == 'r') || (cmd == 'R')) { - cmd_action = HostCmd_ACT_GEN_GET; - mem_rw.value = 0; - } else if ((cmd == 'w') || (cmd == 'W')) { - cmd_action = HostCmd_ACT_GEN_SET; - } else { - ret = -EINVAL; - goto done; - } - - memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw)); - if (mwifiex_send_cmd(priv, HostCmd_CMD_MEM_ACCESS, cmd_action, 0, - &mem_rw, true)) - ret = -1; - else - ret = count; - -done: - free_page(addr); - return ret; -} - -/* Proc memrw file read handler. - * This function is called when the 'memrw' file is opened for reading - * This function can be used to read from a memory location. - */ -static ssize_t -mwifiex_memrw_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = (void *)file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - int ret, pos = 0; - - if (!buf) - return -ENOMEM; - - pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr, - priv->mem_rw.value); - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); - - free_page(addr); - return ret; -} - -static u32 saved_offset = -1, saved_bytes = -1; - -/* - * Proc rdeeprom file write handler. - * - * This function is called when the 'rdeeprom' file is opened for writing - * - * This function can be used to write to a RDEEPROM location. - */ -static ssize_t -mwifiex_rdeeprom_write(struct file *file, - const char __user *ubuf, size_t count, loff_t *ppos) -{ - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *) addr; - size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); - int ret = 0; - int offset = -1, bytes = -1; - - if (!buf) - return -ENOMEM; - - - if (copy_from_user(buf, ubuf, buf_size)) { - ret = -EFAULT; - goto done; - } - - sscanf(buf, "%d %d", &offset, &bytes); - - if (offset == -1 || bytes == -1) { - ret = -EINVAL; - goto done; - } else { - saved_offset = offset; - saved_bytes = bytes; - ret = count; - } -done: - free_page(addr); - return ret; -} - -/* - * Proc rdeeprom read write handler. - * - * This function is called when the 'rdeeprom' file is opened for reading - * - * This function can be used to read from a RDEEPROM location. - */ -static ssize_t -mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *) file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *) addr; - int pos, ret, i; - u8 value[MAX_EEPROM_DATA]; - - if (!buf) - return -ENOMEM; - - if (saved_offset == -1) { - /* No command has been given */ - pos = snprintf(buf, PAGE_SIZE, "0"); - goto done; - } - - /* Get command has been given */ - ret = mwifiex_eeprom_read(priv, (u16) saved_offset, - (u16) saved_bytes, value); - if (ret) { - ret = -EINVAL; - goto out_free; - } - - pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); - - for (i = 0; i < saved_bytes; i++) - pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]); - -done: - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); -out_free: - free_page(addr); - return ret; -} - -/* Proc hscfg file write handler - * This function can be used to configure the host sleep parameters. - */ -static ssize_t -mwifiex_hscfg_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = (void *)file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); - int ret, arg_num; - struct mwifiex_ds_hs_cfg hscfg; - int conditions = HS_CFG_COND_DEF; - u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF; - - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, ubuf, buf_size)) { - ret = -EFAULT; - goto done; - } - - arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap); - - memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); - - if (arg_num > 3) { - mwifiex_dbg(priv->adapter, ERROR, - "Too many arguments\n"); - ret = -EINVAL; - goto done; - } - - if (arg_num >= 1 && arg_num < 3) - mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, - MWIFIEX_SYNC_CMD, &hscfg); - - if (arg_num) { - if (conditions == HS_CFG_CANCEL) { - mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD); - ret = count; - goto done; - } - hscfg.conditions = conditions; - } - if (arg_num >= 2) - hscfg.gpio = gpio; - if (arg_num == 3) - hscfg.gap = gap; - - hscfg.is_invoke_hostcmd = false; - mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, - MWIFIEX_SYNC_CMD, &hscfg); - - mwifiex_enable_hs(priv->adapter); - priv->adapter->hs_enabling = false; - ret = count; -done: - free_page(addr); - return ret; -} - -/* Proc hscfg file read handler - * This function can be used to read host sleep configuration - * parameters from driver. - */ -static ssize_t -mwifiex_hscfg_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = (void *)file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - int pos, ret; - struct mwifiex_ds_hs_cfg hscfg; - - if (!buf) - return -ENOMEM; - - mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, - MWIFIEX_SYNC_CMD, &hscfg); - - pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions, - hscfg.gpio, hscfg.gap); - - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); - - free_page(addr); - return ret; -} - -static ssize_t -mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = file->private_data; - char buf[3]; - bool timeshare_coex; - int ret; - unsigned int len; - - if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) - return -EOPNOTSUPP; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, - HostCmd_ACT_GEN_GET, 0, ×hare_coex, true); - if (ret) - return ret; - - len = sprintf(buf, "%d\n", timeshare_coex); - return simple_read_from_buffer(ubuf, count, ppos, buf, len); -} - -static ssize_t -mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - bool timeshare_coex; - struct mwifiex_private *priv = file->private_data; - char kbuf[16]; - int ret; - - if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) - return -EOPNOTSUPP; - - memset(kbuf, 0, sizeof(kbuf)); - - if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count))) - return -EFAULT; - - if (strtobool(kbuf, ×hare_coex)) - return -EINVAL; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, - HostCmd_ACT_GEN_SET, 0, ×hare_coex, true); - if (ret) - return ret; - else - return count; -} - -#define MWIFIEX_DFS_ADD_FILE(name) do { \ - if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \ - priv, &mwifiex_dfs_##name##_fops)) \ - return; \ -} while (0); - -#define MWIFIEX_DFS_FILE_OPS(name) \ -static const struct file_operations mwifiex_dfs_##name##_fops = { \ - .read = mwifiex_##name##_read, \ - .write = mwifiex_##name##_write, \ - .open = simple_open, \ -}; - -#define MWIFIEX_DFS_FILE_READ_OPS(name) \ -static const struct file_operations mwifiex_dfs_##name##_fops = { \ - .read = mwifiex_##name##_read, \ - .open = simple_open, \ -}; - -#define MWIFIEX_DFS_FILE_WRITE_OPS(name) \ -static const struct file_operations mwifiex_dfs_##name##_fops = { \ - .write = mwifiex_##name##_write, \ - .open = simple_open, \ -}; - - -MWIFIEX_DFS_FILE_READ_OPS(info); -MWIFIEX_DFS_FILE_READ_OPS(debug); -MWIFIEX_DFS_FILE_READ_OPS(getlog); -MWIFIEX_DFS_FILE_READ_OPS(device_dump); -MWIFIEX_DFS_FILE_OPS(regrdwr); -MWIFIEX_DFS_FILE_OPS(rdeeprom); -MWIFIEX_DFS_FILE_OPS(memrw); -MWIFIEX_DFS_FILE_OPS(hscfg); -MWIFIEX_DFS_FILE_OPS(histogram); -MWIFIEX_DFS_FILE_OPS(debug_mask); -MWIFIEX_DFS_FILE_OPS(timeshare_coex); - -/* - * This function creates the debug FS directory structure and the files. - */ -void -mwifiex_dev_debugfs_init(struct mwifiex_private *priv) -{ - if (!mwifiex_dfs_dir || !priv) - return; - - priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name, - mwifiex_dfs_dir); - - if (!priv->dfs_dev_dir) - return; - - MWIFIEX_DFS_ADD_FILE(info); - MWIFIEX_DFS_ADD_FILE(debug); - MWIFIEX_DFS_ADD_FILE(getlog); - MWIFIEX_DFS_ADD_FILE(regrdwr); - MWIFIEX_DFS_ADD_FILE(rdeeprom); - MWIFIEX_DFS_ADD_FILE(device_dump); - MWIFIEX_DFS_ADD_FILE(memrw); - MWIFIEX_DFS_ADD_FILE(hscfg); - MWIFIEX_DFS_ADD_FILE(histogram); - MWIFIEX_DFS_ADD_FILE(debug_mask); - MWIFIEX_DFS_ADD_FILE(timeshare_coex); -} - -/* - * This function removes the debug FS directory structure and the files. - */ -void -mwifiex_dev_debugfs_remove(struct mwifiex_private *priv) -{ - if (!priv) - return; - - debugfs_remove_recursive(priv->dfs_dev_dir); -} - -/* - * This function creates the top level proc directory. - */ -void -mwifiex_debugfs_init(void) -{ - if (!mwifiex_dfs_dir) - mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL); -} - -/* - * This function removes the top level proc directory. - */ -void -mwifiex_debugfs_remove(void) -{ - if (mwifiex_dfs_dir) - debugfs_remove(mwifiex_dfs_dir); -} diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h deleted file mode 100644 index 098e1f14dc9a..000000000000 --- a/drivers/net/wireless/mwifiex/decl.h +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Marvell Wireless LAN device driver: generic data structures and APIs - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_DECL_H_ -#define _MWIFIEX_DECL_H_ - -#undef pr_fmt -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include - -#define MWIFIEX_BSS_COEX_COUNT 2 -#define MWIFIEX_MAX_BSS_NUM (3) - -#define MWIFIEX_DMA_ALIGN_SZ 64 -#define MWIFIEX_RX_HEADROOM 64 -#define MAX_TXPD_SZ 32 -#define INTF_HDR_ALIGN 4 - -#define MWIFIEX_MIN_DATA_HEADER_LEN (MWIFIEX_DMA_ALIGN_SZ + INTF_HDR_ALIGN + \ - MAX_TXPD_SZ) -#define MWIFIEX_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type) - * + sizeof(tx_control) - */ - -#define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2 -#define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16 -#define MWIFIEX_MAX_TDLS_PEER_SUPPORTED 8 - -#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 64 -#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 64 -#define MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE 16 - -#define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE 32 - -#define MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE 16 - -#define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE 16 -#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE 64 -#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE 64 -#define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE 64 -#define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE 64 - -#define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff - -#define MWIFIEX_RATE_BITMAP_MCS0 32 - -#define MWIFIEX_RX_DATA_BUF_SIZE (4 * 1024) -#define MWIFIEX_RX_CMD_BUF_SIZE (2 * 1024) - -#define MAX_BEACON_PERIOD (4000) -#define MIN_BEACON_PERIOD (50) -#define MAX_DTIM_PERIOD (100) -#define MIN_DTIM_PERIOD (1) - -#define MWIFIEX_RTS_MIN_VALUE (0) -#define MWIFIEX_RTS_MAX_VALUE (2347) -#define MWIFIEX_FRAG_MIN_VALUE (256) -#define MWIFIEX_FRAG_MAX_VALUE (2346) -#define MWIFIEX_WMM_VERSION 0x01 -#define MWIFIEX_WMM_SUBTYPE 0x01 - -#define MWIFIEX_RETRY_LIMIT 14 -#define MWIFIEX_SDIO_BLOCK_SIZE 256 - -#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0) -#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1) -#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2) -#define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS BIT(3) -#define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS BIT(4) -#define MWIFIEX_BUF_FLAG_AGGR_PKT BIT(5) - -#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024 -#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128 - -#define MWIFIEX_TDLS_DISABLE_LINK 0x00 -#define MWIFIEX_TDLS_ENABLE_LINK 0x01 -#define MWIFIEX_TDLS_CREATE_LINK 0x02 -#define MWIFIEX_TDLS_CONFIG_LINK 0x03 - -#define MWIFIEX_TDLS_RSSI_HIGH 50 -#define MWIFIEX_TDLS_RSSI_LOW 55 -#define MWIFIEX_TDLS_MAX_FAIL_COUNT 4 -#define MWIFIEX_AUTO_TDLS_IDLE_TIME 10 - -/* 54M rates, index from 0 to 11 */ -#define MWIFIEX_RATE_INDEX_MCS0 12 -/* 12-27=MCS0-15(BW20) */ -#define MWIFIEX_BW20_MCS_NUM 15 - -/* Rate index for OFDM 0 */ -#define MWIFIEX_RATE_INDEX_OFDM0 4 - -#define MWIFIEX_MAX_STA_NUM 1 -#define MWIFIEX_MAX_UAP_NUM 1 -#define MWIFIEX_MAX_P2P_NUM 1 - -#define MWIFIEX_A_BAND_START_FREQ 5000 - -/* SDIO Aggr data packet special info */ -#define SDIO_MAX_AGGR_BUF_SIZE (256 * 255) -#define BLOCK_NUMBER_OFFSET 15 -#define SDIO_HEADER_OFFSET 28 - -enum mwifiex_bss_type { - MWIFIEX_BSS_TYPE_STA = 0, - MWIFIEX_BSS_TYPE_UAP = 1, - MWIFIEX_BSS_TYPE_P2P = 2, - MWIFIEX_BSS_TYPE_ANY = 0xff, -}; - -enum mwifiex_bss_role { - MWIFIEX_BSS_ROLE_STA = 0, - MWIFIEX_BSS_ROLE_UAP = 1, - MWIFIEX_BSS_ROLE_ANY = 0xff, -}; - -enum mwifiex_tdls_status { - TDLS_NOT_SETUP = 0, - TDLS_SETUP_INPROGRESS, - TDLS_SETUP_COMPLETE, - TDLS_SETUP_FAILURE, - TDLS_LINK_TEARDOWN, - TDLS_CHAN_SWITCHING, - TDLS_IN_BASE_CHAN, - TDLS_IN_OFF_CHAN, -}; - -enum mwifiex_tdls_error_code { - TDLS_ERR_NO_ERROR = 0, - TDLS_ERR_INTERNAL_ERROR, - TDLS_ERR_MAX_LINKS_EST, - TDLS_ERR_LINK_EXISTS, - TDLS_ERR_LINK_NONEXISTENT, - TDLS_ERR_PEER_STA_UNREACHABLE = 25, -}; - -#define BSS_ROLE_BIT_MASK BIT(0) - -#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) - -enum mwifiex_data_frame_type { - MWIFIEX_DATA_FRAME_TYPE_ETH_II = 0, - MWIFIEX_DATA_FRAME_TYPE_802_11, -}; - -struct mwifiex_fw_image { - u8 *helper_buf; - u32 helper_len; - u8 *fw_buf; - u32 fw_len; -}; - -struct mwifiex_802_11_ssid { - u32 ssid_len; - u8 ssid[IEEE80211_MAX_SSID_LEN]; -}; - -struct mwifiex_wait_queue { - wait_queue_head_t wait; - int status; -}; - -struct mwifiex_rxinfo { - struct sk_buff *parent; - u8 bss_num; - u8 bss_type; - u8 use_count; - u8 buf_type; -}; - -struct mwifiex_txinfo { - u32 status_code; - u8 flags; - u8 bss_num; - u8 bss_type; - u8 aggr_num; - u32 pkt_len; - u8 ack_frame_id; - u64 cookie; -}; - -enum mwifiex_wmm_ac_e { - WMM_AC_BK, - WMM_AC_BE, - WMM_AC_VI, - WMM_AC_VO -} __packed; - -struct ieee_types_wmm_ac_parameters { - u8 aci_aifsn_bitmap; - u8 ecw_bitmap; - __le16 tx_op_limit; -} __packed; - -struct mwifiex_types_wmm_info { - u8 oui[4]; - u8 subtype; - u8 version; - u8 qos_info; - u8 reserved; - struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS]; -} __packed; - -struct mwifiex_arp_eth_header { - struct arphdr hdr; - u8 ar_sha[ETH_ALEN]; - u8 ar_sip[4]; - u8 ar_tha[ETH_ALEN]; - u8 ar_tip[4]; -} __packed; - -struct mwifiex_chan_stats { - u8 chan_num; - u8 bandcfg; - u8 flags; - s8 noise; - u16 total_bss; - u16 cca_scan_dur; - u16 cca_busy_dur; -} __packed; - -#define MWIFIEX_HIST_MAX_SAMPLES 1048576 -#define MWIFIEX_MAX_RX_RATES 44 -#define MWIFIEX_MAX_AC_RX_RATES 74 -#define MWIFIEX_MAX_SNR 256 -#define MWIFIEX_MAX_NOISE_FLR 256 -#define MWIFIEX_MAX_SIG_STRENGTH 256 - -struct mwifiex_histogram_data { - atomic_t rx_rate[MWIFIEX_MAX_AC_RX_RATES]; - atomic_t snr[MWIFIEX_MAX_SNR]; - atomic_t noise_flr[MWIFIEX_MAX_NOISE_FLR]; - atomic_t sig_str[MWIFIEX_MAX_SIG_STRENGTH]; - atomic_t num_samples; -}; - -struct mwifiex_iface_comb { - u8 sta_intf; - u8 uap_intf; - u8 p2p_intf; -}; - -struct mwifiex_radar_params { - struct cfg80211_chan_def *chandef; - u32 cac_time_ms; -} __packed; - -struct mwifiex_11h_intf_state { - bool is_11h_enabled; - bool is_11h_active; -} __packed; -#endif /* !_MWIFIEX_DECL_H_ */ diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/mwifiex/ethtool.c deleted file mode 100644 index 58400c69ab26..000000000000 --- a/drivers/net/wireless/mwifiex/ethtool.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Marvell Wireless LAN device driver: ethtool - * - * Copyright (C) 2013-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" - -static void mwifiex_ethtool_get_wol(struct net_device *dev, - struct ethtool_wolinfo *wol) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - u32 conditions = le32_to_cpu(priv->adapter->hs_cfg.conditions); - - wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY; - - if (conditions == HS_CFG_COND_DEF) - return; - - if (conditions & HS_CFG_COND_UNICAST_DATA) - wol->wolopts |= WAKE_UCAST; - if (conditions & HS_CFG_COND_MULTICAST_DATA) - wol->wolopts |= WAKE_MCAST; - if (conditions & HS_CFG_COND_BROADCAST_DATA) - wol->wolopts |= WAKE_BCAST; - if (conditions & HS_CFG_COND_MAC_EVENT) - wol->wolopts |= WAKE_PHY; -} - -static int mwifiex_ethtool_set_wol(struct net_device *dev, - struct ethtool_wolinfo *wol) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - u32 conditions = 0; - - if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY)) - return -EOPNOTSUPP; - - if (wol->wolopts & WAKE_UCAST) - conditions |= HS_CFG_COND_UNICAST_DATA; - if (wol->wolopts & WAKE_MCAST) - conditions |= HS_CFG_COND_MULTICAST_DATA; - if (wol->wolopts & WAKE_BCAST) - conditions |= HS_CFG_COND_BROADCAST_DATA; - if (wol->wolopts & WAKE_PHY) - conditions |= HS_CFG_COND_MAC_EVENT; - if (wol->wolopts == 0) - conditions |= HS_CFG_COND_DEF; - priv->adapter->hs_cfg.conditions = cpu_to_le32(conditions); - - return 0; -} - -const struct ethtool_ops mwifiex_ethtool_ops = { - .get_wol = mwifiex_ethtool_get_wol, - .set_wol = mwifiex_ethtool_set_wol, -}; diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h deleted file mode 100644 index 1e1e81a0a8d4..000000000000 --- a/drivers/net/wireless/mwifiex/fw.h +++ /dev/null @@ -1,2177 +0,0 @@ -/* - * Marvell Wireless LAN device driver: Firmware specific macros & structures - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_FW_H_ -#define _MWIFIEX_FW_H_ - -#include - - -#define INTF_HEADER_LEN 4 - -struct rfc_1042_hdr { - u8 llc_dsap; - u8 llc_ssap; - u8 llc_ctrl; - u8 snap_oui[3]; - __be16 snap_type; -}; - -struct rx_packet_hdr { - struct ethhdr eth803_hdr; - struct rfc_1042_hdr rfc1042_hdr; -}; - -struct tx_packet_hdr { - struct ethhdr eth803_hdr; - struct rfc_1042_hdr rfc1042_hdr; -}; - -#define B_SUPPORTED_RATES 5 -#define G_SUPPORTED_RATES 9 -#define BG_SUPPORTED_RATES 13 -#define A_SUPPORTED_RATES 9 -#define HOSTCMD_SUPPORTED_RATES 14 -#define N_SUPPORTED_RATES 3 -#define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN | \ - BAND_AN | BAND_AAC) - -#define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \ - BIT(13)) -#define IS_SUPPORT_MULTI_BANDS(adapter) \ - (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT) - -/* bit 13: 11ac BAND_AAC - * bit 12: reserved for lab testing, will be reused for BAND_AN - * bit 11: 11n BAND_GN - * bit 10: 11a BAND_A - * bit 9: 11g BAND_G - * bit 8: 11b BAND_B - * Map these bits to band capability by right shifting 8 bits. - */ -#define GET_FW_DEFAULT_BANDS(adapter) \ - (((adapter->fw_cap_info & 0x2f00) >> 8) & \ - ALL_802_11_BANDS) - -#define HostCmd_WEP_KEY_INDEX_MASK 0x3fff - -#define KEY_INFO_ENABLED 0x01 -enum KEY_TYPE_ID { - KEY_TYPE_ID_WEP = 0, - KEY_TYPE_ID_TKIP, - KEY_TYPE_ID_AES, - KEY_TYPE_ID_WAPI, - KEY_TYPE_ID_AES_CMAC, -}; - -#define WPA_PN_SIZE 8 -#define KEY_PARAMS_FIXED_LEN 10 -#define KEY_INDEX_MASK 0xf -#define KEY_API_VER_MAJOR_V2 2 - -#define KEY_MCAST BIT(0) -#define KEY_UNICAST BIT(1) -#define KEY_ENABLED BIT(2) -#define KEY_DEFAULT BIT(3) -#define KEY_TX_KEY BIT(4) -#define KEY_RX_KEY BIT(5) -#define KEY_IGTK BIT(10) - -#define WAPI_KEY_LEN (WLAN_KEY_LEN_SMS4 + PN_LEN + 2) - -#define MAX_POLL_TRIES 100 -#define MAX_FIRMWARE_POLL_TRIES 100 - -#define FIRMWARE_READY_SDIO 0xfedc -#define FIRMWARE_READY_PCIE 0xfedcba00 - -#define MWIFIEX_COEX_MODE_TIMESHARE 0x01 -#define MWIFIEX_COEX_MODE_SPATIAL 0x82 - -enum mwifiex_usb_ep { - MWIFIEX_USB_EP_CMD_EVENT = 1, - MWIFIEX_USB_EP_DATA = 2, - MWIFIEX_USB_EP_DATA_CH2 = 3, -}; - -enum MWIFIEX_802_11_PRIVACY_FILTER { - MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL, - MWIFIEX_802_11_PRIV_FILTER_8021X_WEP -}; - -#define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI)-(s16)(NF))) -#define CAL_RSSI(SNR, NF) ((s16)((s16)(SNR)+(s16)(NF))) - -#define UAP_BSS_PARAMS_I 0 -#define UAP_CUSTOM_IE_I 1 -#define MWIFIEX_AUTO_IDX_MASK 0xffff -#define MWIFIEX_DELETE_MASK 0x0000 -#define MGMT_MASK_ASSOC_REQ 0x01 -#define MGMT_MASK_REASSOC_REQ 0x04 -#define MGMT_MASK_ASSOC_RESP 0x02 -#define MGMT_MASK_REASSOC_RESP 0x08 -#define MGMT_MASK_PROBE_REQ 0x10 -#define MGMT_MASK_PROBE_RESP 0x20 -#define MGMT_MASK_BEACON 0x100 - -#define TLV_TYPE_UAP_SSID 0x0000 -#define TLV_TYPE_UAP_RATES 0x0001 -#define TLV_TYPE_PWR_CONSTRAINT 0x0020 - -#define PROPRIETARY_TLV_BASE_ID 0x0100 -#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) -#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) -#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) -#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) -#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) -#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) -#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) -#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) -#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) -#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) -#define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32) -#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35) -#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42) -#define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44) -#define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45) -#define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48) -#define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51) -#define TLV_TYPE_UAP_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 57) -#define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 59) -#define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60) -#define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64) -#define TLV_TYPE_UAP_AKMP (PROPRIETARY_TLV_BASE_ID + 65) -#define TLV_TYPE_UAP_FRAG_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 70) -#define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82) -#define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83) -#define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84) -#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86) -#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87) -#define TLV_TYPE_CHANRPT_11H_BASIC (PROPRIETARY_TLV_BASE_ID + 91) -#define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93) -#define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94) -#define TLV_TYPE_ROBUST_COEX (PROPRIETARY_TLV_BASE_ID + 96) -#define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104) -#define TLV_TYPE_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 105) -#define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113) -#define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114) -#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123) -#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) -#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) -#define TLV_TYPE_TX_PAUSE (PROPRIETARY_TLV_BASE_ID + 148) -#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) -#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) -#define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183) -#define TLV_TYPE_MC_GROUP_INFO (PROPRIETARY_TLV_BASE_ID + 184) -#define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194) -#define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197) -#define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199) -#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 198) -#define TLV_BTCOEX_WL_AGGR_WINSIZE (PROPRIETARY_TLV_BASE_ID + 202) -#define TLV_BTCOEX_WL_SCANTIME (PROPRIETARY_TLV_BASE_ID + 203) -#define TLV_TYPE_BSS_MODE (PROPRIETARY_TLV_BASE_ID + 206) - -#define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 - -#define SSN_MASK 0xfff0 - -#define BA_RESULT_SUCCESS 0x0 -#define BA_RESULT_TIMEOUT 0x2 - -#define IS_BASTREAM_SETUP(ptr) (ptr->ba_status) - -#define BA_STREAM_NOT_ALLOWED 0xff - -#define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN || \ - priv->adapter->config_bands & BAND_AN) && \ - priv->curr_bss_params.bss_descriptor.bcn_ht_cap) -#define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) &\ - BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS) - -#define MWIFIEX_TX_DATA_BUF_SIZE_4K 4096 -#define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192 - -#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) -#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14)) -#define ISSUPP_DRCS_ENABLED(FwCapInfo) (FwCapInfo & BIT(15)) -#define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16)) - -#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ - (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ - IEEE80211_HT_CAP_SM_PS) - -#define MWIFIEX_DEF_11N_TX_BF_CAP 0x09E1E008 - -#define MWIFIEX_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR - -#define GET_RXSTBC(x) (x & IEEE80211_HT_CAP_RX_STBC) -#define MWIFIEX_RX_STBC1 0x0100 -#define MWIFIEX_RX_STBC12 0x0200 -#define MWIFIEX_RX_STBC123 0x0300 - -/* dev_cap bitmap - * BIT - * 0-16 reserved - * 17 IEEE80211_HT_CAP_SUP_WIDTH_20_40 - * 18-22 reserved - * 23 IEEE80211_HT_CAP_SGI_20 - * 24 IEEE80211_HT_CAP_SGI_40 - * 25 IEEE80211_HT_CAP_TX_STBC - * 26 IEEE80211_HT_CAP_RX_STBC - * 27-28 reserved - * 29 IEEE80211_HT_CAP_GRN_FLD - * 30-31 reserved - */ -#define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & BIT(17)) -#define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & BIT(23)) -#define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & BIT(24)) -#define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(25)) -#define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(26)) -#define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29)) -#define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8)) -#define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22)) -#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30)) -#define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2)) -#define GETSUPP_TXBASTREAMS(Dot11nDevCap) ((Dot11nDevCap >> 18) & 0xF) - -/* httxcfg bitmap - * 0 reserved - * 1 20/40 Mhz enable(1)/disable(0) - * 2-3 reserved - * 4 green field enable(1)/disable(0) - * 5 short GI in 20 Mhz enable(1)/disable(0) - * 6 short GI in 40 Mhz enable(1)/disable(0) - * 7-15 reserved - */ -#define MWIFIEX_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6)) - -/* 11AC Tx and Rx MCS map for 1x1 mode: - * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 - * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 7 streams - */ -#define MWIFIEX_11AC_MCS_MAP_1X1 0xfffefffe - -/* 11AC Tx and Rx MCS map for 2x2 mode: - * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 and 2 - * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 6 streams - */ -#define MWIFIEX_11AC_MCS_MAP_2X2 0xfffafffa - -#define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f) -#define SETHT_MCS32(x) (x[4] |= 1) -#define HT_STREAM_1X1 0x11 -#define HT_STREAM_2X2 0x22 - -#define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4)) - -#define LLC_SNAP_LEN 8 - -/* HW_SPEC fw_cap_info */ - -#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & 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) -#define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \ - (2 * (nss - 1))) -#define GET_DEVTXMCSMAP(dev_mcs_map) (dev_mcs_map >> 16) -#define GET_DEVRXMCSMAP(dev_mcs_map) (dev_mcs_map & 0xFFFF) - -/* Clear SU Beanformer, MU beanformer, MU beanformee and - * sounding dimensions bits - */ -#define MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK \ - (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \ - IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \ - IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \ - IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK) - -#define MOD_CLASS_HR_DSSS 0x03 -#define MOD_CLASS_OFDM 0x07 -#define MOD_CLASS_HT 0x08 -#define HT_BW_20 0 -#define HT_BW_40 1 - -#define DFS_CHAN_MOVE_TIME 10000 - -#define HostCmd_CMD_GET_HW_SPEC 0x0003 -#define HostCmd_CMD_802_11_SCAN 0x0006 -#define HostCmd_CMD_802_11_GET_LOG 0x000b -#define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010 -#define HostCmd_CMD_802_11_EEPROM_ACCESS 0x0059 -#define HostCmd_CMD_802_11_ASSOCIATE 0x0012 -#define HostCmd_CMD_802_11_SNMP_MIB 0x0016 -#define HostCmd_CMD_MAC_REG_ACCESS 0x0019 -#define HostCmd_CMD_BBP_REG_ACCESS 0x001a -#define HostCmd_CMD_RF_REG_ACCESS 0x001b -#define HostCmd_CMD_PMIC_REG_ACCESS 0x00ad -#define HostCmd_CMD_RF_TX_PWR 0x001e -#define HostCmd_CMD_RF_ANTENNA 0x0020 -#define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024 -#define HostCmd_CMD_MAC_CONTROL 0x0028 -#define HostCmd_CMD_802_11_AD_HOC_START 0x002b -#define HostCmd_CMD_802_11_AD_HOC_JOIN 0x002c -#define HostCmd_CMD_802_11_AD_HOC_STOP 0x0040 -#define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D -#define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b -#define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e -#define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c -#define HostCmd_CMD_WMM_GET_STATUS 0x0071 -#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 -#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f -#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083 -#define HostCmd_CMD_MEM_ACCESS 0x0086 -#define HostCmd_CMD_CFG_DATA 0x008f -#define HostCmd_CMD_VERSION_EXT 0x0097 -#define HostCmd_CMD_MEF_CFG 0x009a -#define HostCmd_CMD_RSSI_INFO 0x00a4 -#define HostCmd_CMD_FUNC_INIT 0x00a9 -#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa -#define HOST_CMD_APCMD_SYS_RESET 0x00af -#define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0 -#define HostCmd_CMD_UAP_BSS_START 0x00b1 -#define HostCmd_CMD_UAP_BSS_STOP 0x00b2 -#define HOST_CMD_APCMD_STA_LIST 0x00b3 -#define HostCmd_CMD_UAP_STA_DEAUTH 0x00b5 -#define HostCmd_CMD_11N_CFG 0x00cd -#define HostCmd_CMD_11N_ADDBA_REQ 0x00ce -#define HostCmd_CMD_11N_ADDBA_RSP 0x00cf -#define HostCmd_CMD_11N_DELBA 0x00d0 -#define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9 -#define HostCmd_CMD_CHAN_REPORT_REQUEST 0x00dd -#define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df -#define HostCmd_CMD_TXPWR_CFG 0x00d1 -#define HostCmd_CMD_TX_RATE_CFG 0x00d6 -#define HostCmd_CMD_ROBUST_COEX 0x00e0 -#define HostCmd_CMD_802_11_PS_MODE_ENH 0x00e4 -#define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5 -#define HostCmd_CMD_P2P_MODE_CFG 0x00eb -#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed -#define HostCmd_CMD_SET_BSS_MODE 0x00f7 -#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa -#define HostCmd_CMD_802_11_SCAN_EXT 0x0107 -#define HostCmd_CMD_COALESCE_CFG 0x010a -#define HostCmd_CMD_MGMT_FRAME_REG 0x010c -#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d -#define HostCmd_CMD_11AC_CFG 0x0112 -#define HostCmd_CMD_TDLS_CONFIG 0x0100 -#define HostCmd_CMD_MC_POLICY 0x0121 -#define HostCmd_CMD_TDLS_OPER 0x0122 -#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223 - -#define PROTOCOL_NO_SECURITY 0x01 -#define PROTOCOL_STATIC_WEP 0x02 -#define PROTOCOL_WPA 0x08 -#define PROTOCOL_WPA2 0x20 -#define PROTOCOL_WPA2_MIXED 0x28 -#define PROTOCOL_EAP 0x40 -#define KEY_MGMT_NONE 0x04 -#define KEY_MGMT_PSK 0x02 -#define KEY_MGMT_EAP 0x01 -#define CIPHER_TKIP 0x04 -#define CIPHER_AES_CCMP 0x08 -#define VALID_CIPHER_BITMAP 0x0c - -enum ENH_PS_MODES { - EN_PS = 1, - DIS_PS = 2, - EN_AUTO_DS = 3, - DIS_AUTO_DS = 4, - SLEEP_CONFIRM = 5, - GET_PS = 0, - EN_AUTO_PS = 0xff, - DIS_AUTO_PS = 0xfe, -}; - -enum P2P_MODES { - P2P_MODE_DISABLE = 0, - P2P_MODE_DEVICE = 1, - P2P_MODE_GO = 2, - P2P_MODE_CLIENT = 3, -}; - -#define HostCmd_RET_BIT 0x8000 -#define HostCmd_ACT_GEN_GET 0x0000 -#define HostCmd_ACT_GEN_SET 0x0001 -#define HostCmd_ACT_GEN_REMOVE 0x0004 -#define HostCmd_ACT_BITWISE_SET 0x0002 -#define HostCmd_ACT_BITWISE_CLR 0x0003 -#define HostCmd_RESULT_OK 0x0000 - -#define HostCmd_ACT_MAC_RX_ON 0x0001 -#define HostCmd_ACT_MAC_TX_ON 0x0002 -#define HostCmd_ACT_MAC_WEP_ENABLE 0x0008 -#define HostCmd_ACT_MAC_ETHERNETII_ENABLE 0x0010 -#define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 -#define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 -#define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON 0x2000 - -#define HostCmd_BSS_MODE_IBSS 0x0002 -#define HostCmd_BSS_MODE_ANY 0x0003 - -#define HostCmd_SCAN_RADIO_TYPE_BG 0 -#define HostCmd_SCAN_RADIO_TYPE_A 1 - -#define HS_CFG_CANCEL 0xffffffff -#define HS_CFG_COND_DEF 0x00000000 -#define HS_CFG_GPIO_DEF 0xff -#define HS_CFG_GAP_DEF 0xff -#define HS_CFG_COND_BROADCAST_DATA 0x00000001 -#define HS_CFG_COND_UNICAST_DATA 0x00000002 -#define HS_CFG_COND_MAC_EVENT 0x00000004 -#define HS_CFG_COND_MULTICAST_DATA 0x00000008 - -#define CONNECT_ERR_AUTH_ERR_STA_FAILURE 0xFFFB -#define CONNECT_ERR_ASSOC_ERR_TIMEOUT 0xFFFC -#define CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED 0xFFFD -#define CONNECT_ERR_AUTH_MSG_UNHANDLED 0xFFFE -#define CONNECT_ERR_STA_FAILURE 0xFFFF - - -#define CMD_F_HOSTCMD (1 << 0) - -#define HostCmd_CMD_ID_MASK 0x0fff - -#define HostCmd_SEQ_NUM_MASK 0x00ff - -#define HostCmd_BSS_NUM_MASK 0x0f00 - -#define HostCmd_BSS_TYPE_MASK 0xf000 - -#define HostCmd_ACT_SET_RX 0x0001 -#define HostCmd_ACT_SET_TX 0x0002 -#define HostCmd_ACT_SET_BOTH 0x0003 - -#define RF_ANTENNA_AUTO 0xFFFF - -#define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) { \ - (((seq) & 0x00ff) | \ - (((num) & 0x000f) << 8)) | \ - (((type) & 0x000f) << 12); } - -#define HostCmd_GET_SEQ_NO(seq) \ - ((seq) & HostCmd_SEQ_NUM_MASK) - -#define HostCmd_GET_BSS_NO(seq) \ - (((seq) & HostCmd_BSS_NUM_MASK) >> 8) - -#define HostCmd_GET_BSS_TYPE(seq) \ - (((seq) & HostCmd_BSS_TYPE_MASK) >> 12) - -#define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001 -#define EVENT_LINK_LOST 0x00000003 -#define EVENT_LINK_SENSED 0x00000004 -#define EVENT_MIB_CHANGED 0x00000006 -#define EVENT_INIT_DONE 0x00000007 -#define EVENT_DEAUTHENTICATED 0x00000008 -#define EVENT_DISASSOCIATED 0x00000009 -#define EVENT_PS_AWAKE 0x0000000a -#define EVENT_PS_SLEEP 0x0000000b -#define EVENT_MIC_ERR_MULTICAST 0x0000000d -#define EVENT_MIC_ERR_UNICAST 0x0000000e -#define EVENT_DEEP_SLEEP_AWAKE 0x00000010 -#define EVENT_ADHOC_BCN_LOST 0x00000011 - -#define EVENT_WMM_STATUS_CHANGE 0x00000017 -#define EVENT_BG_SCAN_REPORT 0x00000018 -#define EVENT_RSSI_LOW 0x00000019 -#define EVENT_SNR_LOW 0x0000001a -#define EVENT_MAX_FAIL 0x0000001b -#define EVENT_RSSI_HIGH 0x0000001c -#define EVENT_SNR_HIGH 0x0000001d -#define EVENT_IBSS_COALESCED 0x0000001e -#define EVENT_DATA_RSSI_LOW 0x00000024 -#define EVENT_DATA_SNR_LOW 0x00000025 -#define EVENT_DATA_RSSI_HIGH 0x00000026 -#define EVENT_DATA_SNR_HIGH 0x00000027 -#define EVENT_LINK_QUALITY 0x00000028 -#define EVENT_PORT_RELEASE 0x0000002b -#define EVENT_UAP_STA_DEAUTH 0x0000002c -#define EVENT_UAP_STA_ASSOC 0x0000002d -#define EVENT_UAP_BSS_START 0x0000002e -#define EVENT_PRE_BEACON_LOST 0x00000031 -#define EVENT_ADDBA 0x00000033 -#define EVENT_DELBA 0x00000034 -#define EVENT_BA_STREAM_TIEMOUT 0x00000037 -#define EVENT_AMSDU_AGGR_CTRL 0x00000042 -#define EVENT_UAP_BSS_IDLE 0x00000043 -#define EVENT_UAP_BSS_ACTIVE 0x00000044 -#define EVENT_WEP_ICV_ERR 0x00000046 -#define EVENT_HS_ACT_REQ 0x00000047 -#define EVENT_BW_CHANGE 0x00000048 -#define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c -#define EVENT_HOSTWAKE_STAIE 0x0000004d -#define EVENT_CHANNEL_SWITCH_ANN 0x00000050 -#define EVENT_TDLS_GENERIC_EVENT 0x00000052 -#define EVENT_RADAR_DETECTED 0x00000053 -#define EVENT_CHANNEL_REPORT_RDY 0x00000054 -#define EVENT_TX_DATA_PAUSE 0x00000055 -#define EVENT_EXT_SCAN_REPORT 0x00000058 -#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f -#define EVENT_MULTI_CHAN_INFO 0x0000006a -#define EVENT_TX_STATUS_REPORT 0x00000074 -#define EVENT_BT_COEX_WLAN_PARA_CHANGE 0X00000076 - -#define EVENT_ID_MASK 0xffff -#define BSS_NUM_MASK 0xf - -#define EVENT_GET_BSS_NUM(event_cause) \ - (((event_cause) >> 16) & BSS_NUM_MASK) - -#define EVENT_GET_BSS_TYPE(event_cause) \ - (((event_cause) >> 24) & 0x00ff) - -#define MWIFIEX_MAX_PATTERN_LEN 20 -#define MWIFIEX_MAX_OFFSET_LEN 100 -#define STACK_NBYTES 100 -#define TYPE_DNUM 1 -#define TYPE_BYTESEQ 2 -#define MAX_OPERAND 0x40 -#define TYPE_EQ (MAX_OPERAND+1) -#define TYPE_EQ_DNUM (MAX_OPERAND+2) -#define TYPE_EQ_BIT (MAX_OPERAND+3) -#define TYPE_AND (MAX_OPERAND+4) -#define TYPE_OR (MAX_OPERAND+5) -#define MEF_MODE_HOST_SLEEP 1 -#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3 -#define MEF_ACTION_AUTO_ARP 0x10 -#define MWIFIEX_CRITERIA_BROADCAST BIT(0) -#define MWIFIEX_CRITERIA_UNICAST BIT(1) -#define MWIFIEX_CRITERIA_MULTICAST BIT(3) -#define MWIFIEX_MAX_SUPPORTED_IPADDR 4 - -#define ACT_TDLS_DELETE 0x00 -#define ACT_TDLS_CREATE 0x01 -#define ACT_TDLS_CONFIG 0x02 - -#define TDLS_EVENT_LINK_TEAR_DOWN 3 -#define TDLS_EVENT_CHAN_SWITCH_RESULT 7 -#define TDLS_EVENT_START_CHAN_SWITCH 8 -#define TDLS_EVENT_CHAN_SWITCH_STOPPED 9 - -#define TDLS_BASE_CHANNEL 0 -#define TDLS_OFF_CHANNEL 1 - -#define ACT_TDLS_CS_ENABLE_CONFIG 0x00 -#define ACT_TDLS_CS_INIT 0x06 -#define ACT_TDLS_CS_STOP 0x07 -#define ACT_TDLS_CS_PARAMS 0x08 - -#define MWIFIEX_DEF_CS_UNIT_TIME 2 -#define MWIFIEX_DEF_CS_THR_OTHERLINK 10 -#define MWIFIEX_DEF_THR_DIRECTLINK 0 -#define MWIFIEX_DEF_CS_TIME 10 -#define MWIFIEX_DEF_CS_TIMEOUT 16 -#define MWIFIEX_DEF_CS_REG_CLASS 12 -#define MWIFIEX_DEF_CS_PERIODICITY 1 - -#define MWIFIEX_FW_V15 15 - -#define MWIFIEX_MASTER_RADAR_DET_MASK BIT(1) - -struct mwifiex_ie_types_header { - __le16 type; - __le16 len; -} __packed; - -struct mwifiex_ie_types_data { - struct mwifiex_ie_types_header header; - u8 data[1]; -} __packed; - -#define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01 -#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08 -#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10 -#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET 0x01 -#define MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS 0x20 - -struct txpd { - u8 bss_type; - u8 bss_num; - __le16 tx_pkt_length; - __le16 tx_pkt_offset; - __le16 tx_pkt_type; - __le32 tx_control; - u8 priority; - u8 flags; - u8 pkt_delay_2ms; - u8 reserved1[2]; - u8 tx_token_id; - u8 reserved[2]; -} __packed; - -struct rxpd { - u8 bss_type; - u8 bss_num; - __le16 rx_pkt_length; - __le16 rx_pkt_offset; - __le16 rx_pkt_type; - __le16 seq_num; - u8 priority; - u8 rx_rate; - s8 snr; - s8 nf; - - /* For: Non-802.11 AC cards - * - * Ht Info [Bit 0] RxRate format: LG=0, HT=1 - * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 - * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 - * - * For: 802.11 AC cards - * [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10 - * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01 - * BW80 = 10 BW160 = 11 - * [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1 - * [Bit 5] STBC support Enabled = 1 - * [Bit 6] LDPC support Enabled = 1 - * [Bit 7] Reserved - */ - u8 ht_info; - u8 reserved[3]; - u8 flags; -} __packed; - -struct uap_txpd { - u8 bss_type; - u8 bss_num; - __le16 tx_pkt_length; - __le16 tx_pkt_offset; - __le16 tx_pkt_type; - __le32 tx_control; - u8 priority; - u8 flags; - u8 pkt_delay_2ms; - u8 reserved1[2]; - u8 tx_token_id; - u8 reserved[2]; -}; - -struct uap_rxpd { - u8 bss_type; - u8 bss_num; - __le16 rx_pkt_length; - __le16 rx_pkt_offset; - __le16 rx_pkt_type; - __le16 seq_num; - u8 priority; - u8 rx_rate; - s8 snr; - s8 nf; - u8 ht_info; - u8 reserved[3]; - u8 flags; -}; - -struct mwifiex_fw_chan_stats { - u8 chan_num; - u8 bandcfg; - u8 flags; - s8 noise; - __le16 total_bss; - __le16 cca_scan_dur; - __le16 cca_busy_dur; -} __packed; - -enum mwifiex_chan_scan_mode_bitmasks { - MWIFIEX_PASSIVE_SCAN = BIT(0), - MWIFIEX_DISABLE_CHAN_FILT = BIT(1), - MWIFIEX_HIDDEN_SSID_REPORT = BIT(4), -}; - -struct mwifiex_chan_scan_param_set { - u8 radio_type; - u8 chan_number; - u8 chan_scan_mode_bitmap; - __le16 min_scan_time; - __le16 max_scan_time; -} __packed; - -struct mwifiex_ie_types_chan_list_param_set { - struct mwifiex_ie_types_header header; - struct mwifiex_chan_scan_param_set chan_scan_param[1]; -} __packed; - -struct chan_band_param_set { - u8 radio_type; - u8 chan_number; -}; - -struct mwifiex_ie_types_chan_band_list_param_set { - struct mwifiex_ie_types_header header; - struct chan_band_param_set chan_band_param[1]; -} __packed; - -struct mwifiex_ie_types_rates_param_set { - struct mwifiex_ie_types_header header; - u8 rates[1]; -} __packed; - -struct mwifiex_ie_types_ssid_param_set { - struct mwifiex_ie_types_header header; - u8 ssid[1]; -} __packed; - -struct mwifiex_ie_types_num_probes { - struct mwifiex_ie_types_header header; - __le16 num_probes; -} __packed; - -struct mwifiex_ie_types_scan_chan_gap { - struct mwifiex_ie_types_header header; - /* time gap in TUs to be used between two consecutive channels scan */ - __le16 chan_gap; -} __packed; - -struct mwifiex_ietypes_chanstats { - struct mwifiex_ie_types_header header; - struct mwifiex_fw_chan_stats chanstats[0]; -} __packed; - -struct mwifiex_ie_types_wildcard_ssid_params { - struct mwifiex_ie_types_header header; - u8 max_ssid_length; - u8 ssid[1]; -} __packed; - -#define TSF_DATA_SIZE 8 -struct mwifiex_ie_types_tsf_timestamp { - struct mwifiex_ie_types_header header; - u8 tsf_data[1]; -} __packed; - -struct mwifiex_cf_param_set { - u8 cfp_cnt; - u8 cfp_period; - __le16 cfp_max_duration; - __le16 cfp_duration_remaining; -} __packed; - -struct mwifiex_ibss_param_set { - __le16 atim_window; -} __packed; - -struct mwifiex_ie_types_ss_param_set { - struct mwifiex_ie_types_header header; - union { - struct mwifiex_cf_param_set cf_param_set[1]; - struct mwifiex_ibss_param_set ibss_param_set[1]; - } cf_ibss; -} __packed; - -struct mwifiex_fh_param_set { - __le16 dwell_time; - u8 hop_set; - u8 hop_pattern; - u8 hop_index; -} __packed; - -struct mwifiex_ds_param_set { - u8 current_chan; -} __packed; - -struct mwifiex_ie_types_phy_param_set { - struct mwifiex_ie_types_header header; - union { - struct mwifiex_fh_param_set fh_param_set[1]; - struct mwifiex_ds_param_set ds_param_set[1]; - } fh_ds; -} __packed; - -struct mwifiex_ie_types_auth_type { - struct mwifiex_ie_types_header header; - __le16 auth_type; -} __packed; - -struct mwifiex_ie_types_vendor_param_set { - struct mwifiex_ie_types_header header; - u8 ie[MWIFIEX_MAX_VSIE_LEN]; -}; - -#define MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC 60 - -struct mwifiex_ie_types_tdls_idle_timeout { - struct mwifiex_ie_types_header header; - __le16 value; -} __packed; - -struct mwifiex_ie_types_rsn_param_set { - struct mwifiex_ie_types_header header; - u8 rsn_ie[1]; -} __packed; - -#define KEYPARAMSET_FIXED_LEN 6 - -struct mwifiex_ie_type_key_param_set { - __le16 type; - __le16 length; - __le16 key_type_id; - __le16 key_info; - __le16 key_len; - u8 key[50]; -} __packed; - -#define IGTK_PN_LEN 8 - -struct mwifiex_cmac_param { - u8 ipn[IGTK_PN_LEN]; - u8 key[WLAN_KEY_LEN_AES_CMAC]; -} __packed; - -struct mwifiex_wep_param { - __le16 key_len; - u8 key[WLAN_KEY_LEN_WEP104]; -} __packed; - -struct mwifiex_tkip_param { - u8 pn[WPA_PN_SIZE]; - __le16 key_len; - u8 key[WLAN_KEY_LEN_TKIP]; -} __packed; - -struct mwifiex_aes_param { - u8 pn[WPA_PN_SIZE]; - __le16 key_len; - u8 key[WLAN_KEY_LEN_CCMP]; -} __packed; - -struct mwifiex_wapi_param { - u8 pn[PN_LEN]; - __le16 key_len; - u8 key[WLAN_KEY_LEN_SMS4]; -} __packed; - -struct mwifiex_cmac_aes_param { - u8 ipn[IGTK_PN_LEN]; - __le16 key_len; - u8 key[WLAN_KEY_LEN_AES_CMAC]; -} __packed; - -struct mwifiex_ie_type_key_param_set_v2 { - __le16 type; - __le16 len; - u8 mac_addr[ETH_ALEN]; - u8 key_idx; - u8 key_type; - __le16 key_info; - union { - struct mwifiex_wep_param wep; - struct mwifiex_tkip_param tkip; - struct mwifiex_aes_param aes; - struct mwifiex_wapi_param wapi; - struct mwifiex_cmac_aes_param cmac_aes; - } key_params; -} __packed; - -struct host_cmd_ds_802_11_key_material_v2 { - __le16 action; - struct mwifiex_ie_type_key_param_set_v2 key_param_set; -} __packed; - -struct host_cmd_ds_802_11_key_material { - __le16 action; - struct mwifiex_ie_type_key_param_set key_param_set; -} __packed; - -struct host_cmd_ds_gen { - __le16 command; - __le16 size; - __le16 seq_num; - __le16 result; -}; - -#define S_DS_GEN sizeof(struct host_cmd_ds_gen) - -enum sleep_resp_ctrl { - RESP_NOT_NEEDED = 0, - RESP_NEEDED, -}; - -struct mwifiex_ps_param { - __le16 null_pkt_interval; - __le16 multiple_dtims; - __le16 bcn_miss_timeout; - __le16 local_listen_interval; - __le16 adhoc_wake_period; - __le16 mode; - __le16 delay_to_ps; -}; - -#define BITMAP_AUTO_DS 0x01 -#define BITMAP_STA_PS 0x10 - -struct mwifiex_ie_types_auto_ds_param { - struct mwifiex_ie_types_header header; - __le16 deep_sleep_timeout; -} __packed; - -struct mwifiex_ie_types_ps_param { - struct mwifiex_ie_types_header header; - struct mwifiex_ps_param param; -} __packed; - -struct host_cmd_ds_802_11_ps_mode_enh { - __le16 action; - - union { - struct mwifiex_ps_param opt_ps; - __le16 ps_bitmap; - } params; -} __packed; - -enum API_VER_ID { - KEY_API_VER_ID = 1, - FW_API_VER_ID = 2, -}; - -struct hw_spec_api_rev { - struct mwifiex_ie_types_header header; - __le16 api_id; - u8 major_ver; - u8 minor_ver; -} __packed; - -struct host_cmd_ds_get_hw_spec { - __le16 hw_if_version; - __le16 version; - __le16 reserved; - __le16 num_of_mcast_adr; - u8 permanent_addr[ETH_ALEN]; - __le16 region_code; - __le16 number_of_antenna; - __le32 fw_release_number; - __le32 reserved_1; - __le32 reserved_2; - __le32 reserved_3; - __le32 fw_cap_info; - __le32 dot_11n_dev_cap; - u8 dev_mcs_support; - __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ - __le16 mgmt_buf_count; /* mgmt IE buffer count */ - __le32 reserved_5; - __le32 reserved_6; - __le32 dot_11ac_dev_cap; - __le32 dot_11ac_mcs_support; - u8 tlvs[0]; -} __packed; - -struct host_cmd_ds_802_11_rssi_info { - __le16 action; - __le16 ndata; - __le16 nbcn; - __le16 reserved[9]; - long long reserved_1; -}; - -struct host_cmd_ds_802_11_rssi_info_rsp { - __le16 action; - __le16 ndata; - __le16 nbcn; - __le16 data_rssi_last; - __le16 data_nf_last; - __le16 data_rssi_avg; - __le16 data_nf_avg; - __le16 bcn_rssi_last; - __le16 bcn_nf_last; - __le16 bcn_rssi_avg; - __le16 bcn_nf_avg; - long long tsf_bcn; -}; - -struct host_cmd_ds_802_11_mac_address { - __le16 action; - u8 mac_addr[ETH_ALEN]; -}; - -struct host_cmd_ds_mac_control { - __le16 action; - __le16 reserved; -}; - -struct host_cmd_ds_mac_multicast_adr { - __le16 action; - __le16 num_of_adrs; - u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; -} __packed; - -struct host_cmd_ds_802_11_deauthenticate { - u8 mac_addr[ETH_ALEN]; - __le16 reason_code; -} __packed; - -struct host_cmd_ds_802_11_associate { - u8 peer_sta_addr[ETH_ALEN]; - __le16 cap_info_bitmap; - __le16 listen_interval; - __le16 beacon_period; - u8 dtim_period; -} __packed; - -struct ieee_types_assoc_rsp { - __le16 cap_info_bitmap; - __le16 status_code; - __le16 a_id; - u8 ie_buffer[1]; -} __packed; - -struct host_cmd_ds_802_11_associate_rsp { - struct ieee_types_assoc_rsp assoc_rsp; -} __packed; - -struct ieee_types_cf_param_set { - u8 element_id; - u8 len; - u8 cfp_cnt; - u8 cfp_period; - __le16 cfp_max_duration; - __le16 cfp_duration_remaining; -} __packed; - -struct ieee_types_ibss_param_set { - u8 element_id; - u8 len; - __le16 atim_window; -} __packed; - -union ieee_types_ss_param_set { - struct ieee_types_cf_param_set cf_param_set; - struct ieee_types_ibss_param_set ibss_param_set; -} __packed; - -struct ieee_types_fh_param_set { - u8 element_id; - u8 len; - __le16 dwell_time; - u8 hop_set; - u8 hop_pattern; - u8 hop_index; -} __packed; - -struct ieee_types_ds_param_set { - u8 element_id; - u8 len; - u8 current_chan; -} __packed; - -union ieee_types_phy_param_set { - struct ieee_types_fh_param_set fh_param_set; - struct ieee_types_ds_param_set ds_param_set; -} __packed; - -struct ieee_types_oper_mode_ntf { - u8 element_id; - u8 len; - u8 oper_mode; -} __packed; - -struct host_cmd_ds_802_11_ad_hoc_start { - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 bss_mode; - __le16 beacon_period; - u8 dtim_period; - union ieee_types_ss_param_set ss_param_set; - union ieee_types_phy_param_set phy_param_set; - u16 reserved1; - __le16 cap_info_bitmap; - u8 data_rate[HOSTCMD_SUPPORTED_RATES]; -} __packed; - -struct host_cmd_ds_802_11_ad_hoc_result { - u8 pad[3]; - u8 bssid[ETH_ALEN]; -} __packed; - -struct adhoc_bss_desc { - u8 bssid[ETH_ALEN]; - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 bss_mode; - __le16 beacon_period; - u8 dtim_period; - u8 time_stamp[8]; - u8 local_time[8]; - union ieee_types_phy_param_set phy_param_set; - union ieee_types_ss_param_set ss_param_set; - __le16 cap_info_bitmap; - u8 data_rates[HOSTCMD_SUPPORTED_RATES]; - - /* - * DO NOT ADD ANY FIELDS TO THIS STRUCTURE. - * It is used in the Adhoc join command and will cause a - * binary layout mismatch with the firmware - */ -} __packed; - -struct host_cmd_ds_802_11_ad_hoc_join { - struct adhoc_bss_desc bss_descriptor; - u16 reserved1; - u16 reserved2; -} __packed; - -struct host_cmd_ds_802_11_get_log { - __le32 mcast_tx_frame; - __le32 failed; - __le32 retry; - __le32 multi_retry; - __le32 frame_dup; - __le32 rts_success; - __le32 rts_failure; - __le32 ack_failure; - __le32 rx_frag; - __le32 mcast_rx_frame; - __le32 fcs_error; - __le32 tx_frame; - __le32 reserved; - __le32 wep_icv_err_cnt[4]; - __le32 bcn_rcv_cnt; - __le32 bcn_miss_cnt; -}; - -/* Enumeration for rate format */ -enum _mwifiex_rate_format { - MWIFIEX_RATE_FORMAT_LG = 0, - MWIFIEX_RATE_FORMAT_HT, - MWIFIEX_RATE_FORMAT_VHT, - MWIFIEX_RATE_FORMAT_AUTO = 0xFF, -}; - -struct host_cmd_ds_tx_rate_query { - u8 tx_rate; - /* Tx Rate Info: For 802.11 AC cards - * - * [Bit 0-1] tx rate formate: LG = 0, HT = 1, VHT = 2 - * [Bit 2-3] HT/VHT Bandwidth: BW20 = 0, BW40 = 1, BW80 = 2, BW160 = 3 - * [Bit 4] HT/VHT Guard Interval: LGI = 0, SGI = 1 - * - * For non-802.11 AC cards - * Ht Info [Bit 0] RxRate format: LG=0, HT=1 - * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 - * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 - */ - u8 ht_info; -} __packed; - -struct mwifiex_tx_pause_tlv { - struct mwifiex_ie_types_header header; - u8 peermac[ETH_ALEN]; - u8 tx_pause; - u8 pkt_cnt; -} __packed; - -enum Host_Sleep_Action { - HS_CONFIGURE = 0x0001, - HS_ACTIVATE = 0x0002, -}; - -struct mwifiex_hs_config_param { - __le32 conditions; - u8 gpio; - u8 gap; -} __packed; - -struct hs_activate_param { - __le16 resp_ctrl; -} __packed; - -struct host_cmd_ds_802_11_hs_cfg_enh { - __le16 action; - - union { - struct mwifiex_hs_config_param hs_config; - struct hs_activate_param hs_activate; - } params; -} __packed; - -enum SNMP_MIB_INDEX { - OP_RATE_SET_I = 1, - DTIM_PERIOD_I = 3, - RTS_THRESH_I = 5, - SHORT_RETRY_LIM_I = 6, - LONG_RETRY_LIM_I = 7, - FRAG_THRESH_I = 8, - DOT11D_I = 9, - DOT11H_I = 10, -}; - -enum mwifiex_assocmd_failurepoint { - MWIFIEX_ASSOC_CMD_SUCCESS = 0, - MWIFIEX_ASSOC_CMD_FAILURE_ASSOC, - MWIFIEX_ASSOC_CMD_FAILURE_AUTH, - MWIFIEX_ASSOC_CMD_FAILURE_JOIN -}; - -#define MAX_SNMP_BUF_SIZE 128 - -struct host_cmd_ds_802_11_snmp_mib { - __le16 query_type; - __le16 oid; - __le16 buf_size; - u8 value[1]; -} __packed; - -struct mwifiex_rate_scope { - __le16 type; - __le16 length; - __le16 hr_dsss_rate_bitmap; - __le16 ofdm_rate_bitmap; - __le16 ht_mcs_rate_bitmap[8]; - __le16 vht_mcs_rate_bitmap[8]; -} __packed; - -struct mwifiex_rate_drop_pattern { - __le16 type; - __le16 length; - __le32 rate_drop_mode; -} __packed; - -struct host_cmd_ds_tx_rate_cfg { - __le16 action; - __le16 cfg_index; -} __packed; - -struct mwifiex_power_group { - u8 modulation_class; - u8 first_rate_code; - u8 last_rate_code; - s8 power_step; - s8 power_min; - s8 power_max; - u8 ht_bandwidth; - u8 reserved; -} __packed; - -struct mwifiex_types_power_group { - __le16 type; - __le16 length; -} __packed; - -struct host_cmd_ds_txpwr_cfg { - __le16 action; - __le16 cfg_index; - __le32 mode; -} __packed; - -struct host_cmd_ds_rf_tx_pwr { - __le16 action; - __le16 cur_level; - u8 max_power; - u8 min_power; -} __packed; - -struct host_cmd_ds_rf_ant_mimo { - __le16 action_tx; - __le16 tx_ant_mode; - __le16 action_rx; - __le16 rx_ant_mode; -}; - -struct host_cmd_ds_rf_ant_siso { - __le16 action; - __le16 ant_mode; -}; - -struct host_cmd_ds_tdls_oper { - __le16 tdls_action; - __le16 reason; - u8 peer_mac[ETH_ALEN]; -} __packed; - -struct mwifiex_tdls_config { - __le16 enable; -}; - -struct mwifiex_tdls_config_cs_params { - u8 unit_time; - u8 thr_otherlink; - u8 thr_directlink; -}; - -struct mwifiex_tdls_init_cs_params { - u8 peer_mac[ETH_ALEN]; - u8 primary_chan; - u8 second_chan_offset; - u8 band; - __le16 switch_time; - __le16 switch_timeout; - u8 reg_class; - u8 periodicity; -} __packed; - -struct mwifiex_tdls_stop_cs_params { - u8 peer_mac[ETH_ALEN]; -}; - -struct host_cmd_ds_tdls_config { - __le16 tdls_action; - u8 tdls_data[1]; -} __packed; - -struct mwifiex_chan_desc { - __le16 start_freq; - u8 chan_width; - u8 chan_num; -} __packed; - -struct host_cmd_ds_chan_rpt_req { - struct mwifiex_chan_desc chan_desc; - __le32 msec_dwell_time; -} __packed; - -struct host_cmd_ds_chan_rpt_event { - __le32 result; - __le64 start_tsf; - __le32 duration; - u8 tlvbuf[0]; -} __packed; - -struct host_cmd_sdio_sp_rx_aggr_cfg { - u8 action; - u8 enable; - __le16 block_size; -} __packed; - -struct mwifiex_fixed_bcn_param { - __le64 timestamp; - __le16 beacon_period; - __le16 cap_info_bitmap; -} __packed; - -struct mwifiex_event_scan_result { - __le16 event_id; - u8 bss_index; - u8 bss_type; - u8 more_event; - u8 reserved[3]; - __le16 buf_size; - u8 num_of_set; -} __packed; - -struct tx_status_event { - u8 packet_type; - u8 tx_token_id; - u8 status; -} __packed; - -#define MWIFIEX_USER_SCAN_CHAN_MAX 50 - -#define MWIFIEX_MAX_SSID_LIST_LENGTH 10 - -struct mwifiex_scan_cmd_config { - /* - * BSS mode to be sent in the firmware command - */ - u8 bss_mode; - - /* Specific BSSID used to filter scan results in the firmware */ - u8 specific_bssid[ETH_ALEN]; - - /* Length of TLVs sent in command starting at tlvBuffer */ - u32 tlv_buf_len; - - /* - * SSID TLV(s) and ChanList TLVs to be sent in the firmware command - * - * TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set - * WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set - */ - u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored - here */ -} __packed; - -struct mwifiex_user_scan_chan { - u8 chan_number; - u8 radio_type; - u8 scan_type; - u8 reserved; - u32 scan_time; -} __packed; - -struct mwifiex_user_scan_cfg { - /* - * BSS mode to be sent in the firmware command - */ - u8 bss_mode; - /* Configure the number of probe requests for active chan scans */ - u8 num_probes; - u8 reserved; - /* BSSID filter sent in the firmware command to limit the results */ - u8 specific_bssid[ETH_ALEN]; - /* SSID filter list used in the firmware to limit the scan results */ - struct cfg80211_ssid *ssid_list; - u8 num_ssids; - /* Variable number (fixed maximum) of channels to scan up */ - struct mwifiex_user_scan_chan chan_list[MWIFIEX_USER_SCAN_CHAN_MAX]; - u16 scan_chan_gap; -} __packed; - -struct ie_body { - u8 grp_key_oui[4]; - u8 ptk_cnt[2]; - u8 ptk_body[4]; -} __packed; - -struct host_cmd_ds_802_11_scan { - u8 bss_mode; - u8 bssid[ETH_ALEN]; - u8 tlv_buffer[1]; -} __packed; - -struct host_cmd_ds_802_11_scan_rsp { - __le16 bss_descript_size; - u8 number_of_sets; - u8 bss_desc_and_tlv_buffer[1]; -} __packed; - -struct host_cmd_ds_802_11_scan_ext { - u32 reserved; - u8 tlv_buffer[1]; -} __packed; - -struct mwifiex_ie_types_bss_mode { - struct mwifiex_ie_types_header header; - u8 bss_mode; -} __packed; - -struct mwifiex_ie_types_bss_scan_rsp { - struct mwifiex_ie_types_header header; - u8 bssid[ETH_ALEN]; - u8 frame_body[1]; -} __packed; - -struct mwifiex_ie_types_bss_scan_info { - struct mwifiex_ie_types_header header; - __le16 rssi; - __le16 anpi; - u8 cca_busy_fraction; - u8 radio_type; - u8 channel; - u8 reserved; - __le64 tsf; -} __packed; - -struct host_cmd_ds_802_11_bg_scan_query { - u8 flush; -} __packed; - -struct host_cmd_ds_802_11_bg_scan_query_rsp { - __le32 report_condition; - struct host_cmd_ds_802_11_scan_rsp scan_resp; -} __packed; - -struct mwifiex_ietypes_domain_param_set { - struct mwifiex_ie_types_header header; - u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - struct ieee80211_country_ie_triplet triplet[1]; -} __packed; - -struct host_cmd_ds_802_11d_domain_info { - __le16 action; - struct mwifiex_ietypes_domain_param_set domain; -} __packed; - -struct host_cmd_ds_802_11d_domain_info_rsp { - __le16 action; - struct mwifiex_ietypes_domain_param_set domain; -} __packed; - -struct host_cmd_ds_11n_addba_req { - u8 add_req_result; - u8 peer_mac_addr[ETH_ALEN]; - u8 dialog_token; - __le16 block_ack_param_set; - __le16 block_ack_tmo; - __le16 ssn; -} __packed; - -struct host_cmd_ds_11n_addba_rsp { - u8 add_rsp_result; - u8 peer_mac_addr[ETH_ALEN]; - u8 dialog_token; - __le16 status_code; - __le16 block_ack_param_set; - __le16 block_ack_tmo; - __le16 ssn; -} __packed; - -struct host_cmd_ds_11n_delba { - u8 del_result; - u8 peer_mac_addr[ETH_ALEN]; - __le16 del_ba_param_set; - __le16 reason_code; - u8 reserved; -} __packed; - -struct host_cmd_ds_11n_batimeout { - u8 tid; - u8 peer_mac_addr[ETH_ALEN]; - u8 origninator; -} __packed; - -struct host_cmd_ds_11n_cfg { - __le16 action; - __le16 ht_tx_cap; - __le16 ht_tx_info; - __le16 misc_config; /* Needed for 802.11AC cards only */ -} __packed; - -struct host_cmd_ds_txbuf_cfg { - __le16 action; - __le16 buff_size; - __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ - __le16 reserved3; -} __packed; - -struct host_cmd_ds_amsdu_aggr_ctrl { - __le16 action; - __le16 enable; - __le16 curr_buf_size; -} __packed; - -struct host_cmd_ds_sta_deauth { - u8 mac[ETH_ALEN]; - __le16 reason; -} __packed; - -struct mwifiex_ie_types_sta_info { - struct mwifiex_ie_types_header header; - u8 mac[ETH_ALEN]; - u8 power_mfg_status; - s8 rssi; -}; - -struct host_cmd_ds_sta_list { - u16 sta_count; - u8 tlv[0]; -} __packed; - -struct mwifiex_ie_types_pwr_capability { - struct mwifiex_ie_types_header header; - s8 min_pwr; - s8 max_pwr; -}; - -struct mwifiex_ie_types_local_pwr_constraint { - struct mwifiex_ie_types_header header; - u8 chan; - u8 constraint; -}; - -struct mwifiex_ie_types_wmm_param_set { - struct mwifiex_ie_types_header header; - u8 wmm_ie[1]; -}; - -struct mwifiex_ie_types_wmm_queue_status { - struct mwifiex_ie_types_header header; - u8 queue_index; - u8 disabled; - __le16 medium_time; - u8 flow_required; - u8 flow_created; - u32 reserved; -}; - -struct ieee_types_vendor_header { - u8 element_id; - u8 len; - u8 oui[4]; /* 0~2: oui, 3: oui_type */ - u8 oui_subtype; - u8 version; -} __packed; - -struct ieee_types_wmm_parameter { - /* - * WMM Parameter IE - Vendor Specific Header: - * element_id [221/0xdd] - * Len [24] - * Oui [00:50:f2] - * OuiType [2] - * OuiSubType [1] - * Version [1] - */ - struct ieee_types_vendor_header vend_hdr; - u8 qos_info_bitmap; - u8 reserved; - struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS]; -} __packed; - -struct ieee_types_wmm_info { - - /* - * WMM Info IE - Vendor Specific Header: - * element_id [221/0xdd] - * Len [7] - * Oui [00:50:f2] - * OuiType [2] - * OuiSubType [0] - * Version [1] - */ - struct ieee_types_vendor_header vend_hdr; - - u8 qos_info_bitmap; -} __packed; - -struct host_cmd_ds_wmm_get_status { - u8 queue_status_tlv[sizeof(struct mwifiex_ie_types_wmm_queue_status) * - IEEE80211_NUM_ACS]; - u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2]; -} __packed; - -struct mwifiex_wmm_ac_status { - u8 disabled; - u8 flow_required; - u8 flow_created; -}; - -struct mwifiex_ie_types_htcap { - struct mwifiex_ie_types_header header; - struct ieee80211_ht_cap ht_cap; -} __packed; - -struct mwifiex_ie_types_vhtcap { - struct mwifiex_ie_types_header header; - struct ieee80211_vht_cap vht_cap; -} __packed; - -struct mwifiex_ie_types_aid { - struct mwifiex_ie_types_header header; - __le16 aid; -} __packed; - -struct mwifiex_ie_types_oper_mode_ntf { - struct mwifiex_ie_types_header header; - u8 oper_mode; -} __packed; - -/* VHT Operations IE */ -struct mwifiex_ie_types_vht_oper { - struct mwifiex_ie_types_header header; - u8 chan_width; - u8 chan_center_freq_1; - u8 chan_center_freq_2; - /* Basic MCS set map, each 2 bits stands for a NSS */ - __le16 basic_mcs_map; -} __packed; - -struct mwifiex_ie_types_wmmcap { - struct mwifiex_ie_types_header header; - struct mwifiex_types_wmm_info wmm_info; -} __packed; - -struct mwifiex_ie_types_htinfo { - struct mwifiex_ie_types_header header; - struct ieee80211_ht_operation ht_oper; -} __packed; - -struct mwifiex_ie_types_2040bssco { - struct mwifiex_ie_types_header header; - u8 bss_co_2040; -} __packed; - -struct mwifiex_ie_types_extcap { - struct mwifiex_ie_types_header header; - u8 ext_capab[0]; -} __packed; - -struct host_cmd_ds_mem_access { - __le16 action; - __le16 reserved; - __le32 addr; - __le32 value; -}; - -struct mwifiex_ie_types_qos_info { - struct mwifiex_ie_types_header header; - u8 qos_info; -} __packed; - -struct host_cmd_ds_mac_reg_access { - __le16 action; - __le16 offset; - __le32 value; -} __packed; - -struct host_cmd_ds_bbp_reg_access { - __le16 action; - __le16 offset; - u8 value; - u8 reserved[3]; -} __packed; - -struct host_cmd_ds_rf_reg_access { - __le16 action; - __le16 offset; - u8 value; - u8 reserved[3]; -} __packed; - -struct host_cmd_ds_pmic_reg_access { - __le16 action; - __le16 offset; - u8 value; - u8 reserved[3]; -} __packed; - -struct host_cmd_ds_802_11_eeprom_access { - __le16 action; - - __le16 offset; - __le16 byte_count; - u8 value; -} __packed; - -struct mwifiex_assoc_event { - u8 sta_addr[ETH_ALEN]; - __le16 type; - __le16 len; - __le16 frame_control; - __le16 cap_info; - __le16 listen_interval; - u8 data[0]; -} __packed; - -struct host_cmd_ds_sys_config { - __le16 action; - u8 tlv[0]; -}; - -struct host_cmd_11ac_vht_cfg { - __le16 action; - u8 band_config; - u8 misc_config; - __le32 cap_info; - __le32 mcs_tx_set; - __le32 mcs_rx_set; -} __packed; - -struct host_cmd_tlv_akmp { - struct mwifiex_ie_types_header header; - __le16 key_mgmt; - __le16 key_mgmt_operation; -} __packed; - -struct host_cmd_tlv_pwk_cipher { - struct mwifiex_ie_types_header header; - __le16 proto; - u8 cipher; - u8 reserved; -} __packed; - -struct host_cmd_tlv_gwk_cipher { - struct mwifiex_ie_types_header header; - u8 cipher; - u8 reserved; -} __packed; - -struct host_cmd_tlv_passphrase { - struct mwifiex_ie_types_header header; - u8 passphrase[0]; -} __packed; - -struct host_cmd_tlv_wep_key { - struct mwifiex_ie_types_header header; - u8 key_index; - u8 is_default; - u8 key[1]; -}; - -struct host_cmd_tlv_auth_type { - struct mwifiex_ie_types_header header; - u8 auth_type; -} __packed; - -struct host_cmd_tlv_encrypt_protocol { - struct mwifiex_ie_types_header header; - __le16 proto; -} __packed; - -struct host_cmd_tlv_ssid { - struct mwifiex_ie_types_header header; - u8 ssid[0]; -} __packed; - -struct host_cmd_tlv_rates { - struct mwifiex_ie_types_header header; - u8 rates[0]; -} __packed; - -struct mwifiex_ie_types_bssid_list { - struct mwifiex_ie_types_header header; - u8 bssid[ETH_ALEN]; -} __packed; - -struct host_cmd_tlv_bcast_ssid { - struct mwifiex_ie_types_header header; - u8 bcast_ctl; -} __packed; - -struct host_cmd_tlv_beacon_period { - struct mwifiex_ie_types_header header; - __le16 period; -} __packed; - -struct host_cmd_tlv_dtim_period { - struct mwifiex_ie_types_header header; - u8 period; -} __packed; - -struct host_cmd_tlv_frag_threshold { - struct mwifiex_ie_types_header header; - __le16 frag_thr; -} __packed; - -struct host_cmd_tlv_rts_threshold { - struct mwifiex_ie_types_header header; - __le16 rts_thr; -} __packed; - -struct host_cmd_tlv_retry_limit { - struct mwifiex_ie_types_header header; - u8 limit; -} __packed; - -struct host_cmd_tlv_mac_addr { - struct mwifiex_ie_types_header header; - u8 mac_addr[ETH_ALEN]; -} __packed; - -struct host_cmd_tlv_channel_band { - struct mwifiex_ie_types_header header; - u8 band_config; - u8 channel; -} __packed; - -struct host_cmd_tlv_ageout_timer { - struct mwifiex_ie_types_header header; - __le32 sta_ao_timer; -} __packed; - -struct host_cmd_tlv_power_constraint { - struct mwifiex_ie_types_header header; - u8 constraint; -} __packed; - -struct mwifiex_ie_types_btcoex_scan_time { - struct mwifiex_ie_types_header header; - u8 coex_scan; - u8 reserved; - u16 min_scan_time; - u16 max_scan_time; -} __packed; - -struct mwifiex_ie_types_btcoex_aggr_win_size { - struct mwifiex_ie_types_header header; - u8 coex_win_size; - u8 tx_win_size; - u8 rx_win_size; - u8 reserved; -} __packed; - -struct mwifiex_ie_types_robust_coex { - struct mwifiex_ie_types_header header; - __le32 mode; -} __packed; - -struct host_cmd_ds_version_ext { - u8 version_str_sel; - char version_str[128]; -} __packed; - -struct host_cmd_ds_mgmt_frame_reg { - __le16 action; - __le32 mask; -} __packed; - -struct host_cmd_ds_p2p_mode_cfg { - __le16 action; - __le16 mode; -} __packed; - -struct host_cmd_ds_remain_on_chan { - __le16 action; - u8 status; - u8 reserved; - u8 band_cfg; - u8 channel; - __le32 duration; -} __packed; - -struct host_cmd_ds_802_11_ibss_status { - __le16 action; - __le16 enable; - u8 bssid[ETH_ALEN]; - __le16 beacon_interval; - __le16 atim_window; - __le16 use_g_rate_protect; -} __packed; - -struct mwifiex_fw_mef_entry { - u8 mode; - u8 action; - __le16 exprsize; - u8 expr[0]; -} __packed; - -struct host_cmd_ds_mef_cfg { - __le32 criteria; - __le16 num_entries; - struct mwifiex_fw_mef_entry mef_entry[0]; -} __packed; - -#define CONNECTION_TYPE_INFRA 0 -#define CONNECTION_TYPE_ADHOC 1 -#define CONNECTION_TYPE_AP 2 - -struct host_cmd_ds_set_bss_mode { - u8 con_type; -} __packed; - -struct host_cmd_ds_pcie_details { - /* TX buffer descriptor ring address */ - u32 txbd_addr_lo; - u32 txbd_addr_hi; - /* TX buffer descriptor ring count */ - u32 txbd_count; - - /* RX buffer descriptor ring address */ - u32 rxbd_addr_lo; - u32 rxbd_addr_hi; - /* RX buffer descriptor ring count */ - u32 rxbd_count; - - /* Event buffer descriptor ring address */ - u32 evtbd_addr_lo; - u32 evtbd_addr_hi; - /* Event buffer descriptor ring count */ - u32 evtbd_count; - - /* Sleep cookie buffer physical address */ - u32 sleep_cookie_addr_lo; - u32 sleep_cookie_addr_hi; -} __packed; - -struct mwifiex_ie_types_rssi_threshold { - struct mwifiex_ie_types_header header; - u8 abs_value; - u8 evt_freq; -} __packed; - -#define MWIFIEX_DFS_REC_HDR_LEN 8 -#define MWIFIEX_DFS_REC_HDR_NUM 10 -#define MWIFIEX_BIN_COUNTER_LEN 7 - -struct mwifiex_radar_det_event { - __le32 detect_count; - u8 reg_domain; /*1=fcc, 2=etsi, 3=mic*/ - u8 det_type; /*0=none, 1=pw(chirp), 2=pri(radar)*/ - __le16 pw_chirp_type; - u8 pw_chirp_idx; - u8 pw_value; - u8 pri_radar_type; - u8 pri_bincnt; - u8 bin_counter[MWIFIEX_BIN_COUNTER_LEN]; - u8 num_dfs_records; - u8 dfs_record_hdr[MWIFIEX_DFS_REC_HDR_NUM][MWIFIEX_DFS_REC_HDR_LEN]; - __le32 passed; -} __packed; - -struct mwifiex_ie_types_multi_chan_info { - struct mwifiex_ie_types_header header; - __le16 status; - u8 tlv_buffer[0]; -} __packed; - -struct mwifiex_ie_types_mc_group_info { - struct mwifiex_ie_types_header header; - u8 chan_group_id; - u8 chan_buf_weight; - u8 band_config; - u8 chan_num; - u32 chan_time; - u32 reserved; - union { - u8 sdio_func_num; - u8 usb_ep_num; - } hid_num; - u8 intf_num; - u8 bss_type_numlist[0]; -} __packed; - -struct meas_rpt_map { - u8 rssi:3; - u8 unmeasured:1; - u8 radar:1; - u8 unidentified_sig:1; - u8 ofdm_preamble:1; - u8 bss:1; -} __packed; - -struct mwifiex_ie_types_chan_rpt_data { - struct mwifiex_ie_types_header header; - struct meas_rpt_map map; -} __packed; - -struct host_cmd_ds_802_11_subsc_evt { - __le16 action; - __le16 events; -} __packed; - -struct chan_switch_result { - u8 cur_chan; - u8 status; - u8 reason; -} __packed; - -struct mwifiex_tdls_generic_event { - __le16 type; - u8 peer_mac[ETH_ALEN]; - union { - struct chan_switch_result switch_result; - u8 cs_stop_reason; - __le16 reason_code; - __le16 reserved; - } u; -} __packed; - -struct mwifiex_ie { - __le16 ie_index; - __le16 mgmt_subtype_mask; - __le16 ie_length; - u8 ie_buffer[IEEE_MAX_IE_SIZE]; -} __packed; - -#define MAX_MGMT_IE_INDEX 16 -struct mwifiex_ie_list { - __le16 type; - __le16 len; - struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX]; -} __packed; - -struct coalesce_filt_field_param { - u8 operation; - u8 operand_len; - __le16 offset; - u8 operand_byte_stream[4]; -}; - -struct coalesce_receive_filt_rule { - struct mwifiex_ie_types_header header; - u8 num_of_fields; - u8 pkt_type; - __le16 max_coalescing_delay; - struct coalesce_filt_field_param params[0]; -} __packed; - -struct host_cmd_ds_coalesce_cfg { - __le16 action; - __le16 num_of_rules; - struct coalesce_receive_filt_rule rule[0]; -} __packed; - -struct host_cmd_ds_multi_chan_policy { - __le16 action; - __le16 policy; -} __packed; - -struct host_cmd_ds_robust_coex { - __le16 action; - __le16 reserved; -} __packed; - -struct host_cmd_ds_command { - __le16 command; - __le16 size; - __le16 seq_num; - __le16 result; - union { - struct host_cmd_ds_get_hw_spec hw_spec; - struct host_cmd_ds_mac_control mac_ctrl; - struct host_cmd_ds_802_11_mac_address mac_addr; - struct host_cmd_ds_mac_multicast_adr mc_addr; - struct host_cmd_ds_802_11_get_log get_log; - struct host_cmd_ds_802_11_rssi_info rssi_info; - struct host_cmd_ds_802_11_rssi_info_rsp rssi_info_rsp; - struct host_cmd_ds_802_11_snmp_mib smib; - struct host_cmd_ds_tx_rate_query tx_rate; - struct host_cmd_ds_tx_rate_cfg tx_rate_cfg; - struct host_cmd_ds_txpwr_cfg txp_cfg; - struct host_cmd_ds_rf_tx_pwr txp; - struct host_cmd_ds_rf_ant_mimo ant_mimo; - struct host_cmd_ds_rf_ant_siso ant_siso; - struct host_cmd_ds_802_11_ps_mode_enh psmode_enh; - struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg; - struct host_cmd_ds_802_11_scan scan; - struct host_cmd_ds_802_11_scan_ext ext_scan; - struct host_cmd_ds_802_11_scan_rsp scan_resp; - struct host_cmd_ds_802_11_bg_scan_query bg_scan_query; - struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp; - struct host_cmd_ds_802_11_associate associate; - struct host_cmd_ds_802_11_associate_rsp associate_rsp; - struct host_cmd_ds_802_11_deauthenticate deauth; - struct host_cmd_ds_802_11_ad_hoc_start adhoc_start; - struct host_cmd_ds_802_11_ad_hoc_result adhoc_result; - struct host_cmd_ds_802_11_ad_hoc_join adhoc_join; - struct host_cmd_ds_802_11d_domain_info domain_info; - struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp; - struct host_cmd_ds_11n_addba_req add_ba_req; - struct host_cmd_ds_11n_addba_rsp add_ba_rsp; - struct host_cmd_ds_11n_delba del_ba; - struct host_cmd_ds_txbuf_cfg tx_buf; - struct host_cmd_ds_amsdu_aggr_ctrl amsdu_aggr_ctrl; - struct host_cmd_ds_11n_cfg htcfg; - struct host_cmd_ds_wmm_get_status get_wmm_status; - struct host_cmd_ds_802_11_key_material key_material; - struct host_cmd_ds_802_11_key_material_v2 key_material_v2; - struct host_cmd_ds_version_ext verext; - struct host_cmd_ds_mgmt_frame_reg reg_mask; - struct host_cmd_ds_remain_on_chan roc_cfg; - struct host_cmd_ds_p2p_mode_cfg mode_cfg; - struct host_cmd_ds_802_11_ibss_status ibss_coalescing; - struct host_cmd_ds_mef_cfg mef_cfg; - struct host_cmd_ds_mem_access mem; - struct host_cmd_ds_mac_reg_access mac_reg; - struct host_cmd_ds_bbp_reg_access bbp_reg; - struct host_cmd_ds_rf_reg_access rf_reg; - struct host_cmd_ds_pmic_reg_access pmic_reg; - struct host_cmd_ds_set_bss_mode bss_mode; - struct host_cmd_ds_pcie_details pcie_host_spec; - struct host_cmd_ds_802_11_eeprom_access eeprom; - struct host_cmd_ds_802_11_subsc_evt subsc_evt; - struct host_cmd_ds_sys_config uap_sys_config; - struct host_cmd_ds_sta_deauth sta_deauth; - struct host_cmd_ds_sta_list sta_list; - struct host_cmd_11ac_vht_cfg vht_cfg; - struct host_cmd_ds_coalesce_cfg coalesce_cfg; - struct host_cmd_ds_tdls_config tdls_config; - struct host_cmd_ds_tdls_oper tdls_oper; - struct host_cmd_ds_chan_rpt_req chan_rpt_req; - struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg; - struct host_cmd_ds_multi_chan_policy mc_policy; - struct host_cmd_ds_robust_coex coex; - } params; -} __packed; - -struct mwifiex_opt_sleep_confirm { - __le16 command; - __le16 size; - __le16 seq_num; - __le16 result; - __le16 action; - __le16 resp_ctrl; -} __packed; -#endif /* !_MWIFIEX_FW_H_ */ diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c deleted file mode 100644 index abf52d25b981..000000000000 --- a/drivers/net/wireless/mwifiex/ie.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * Marvell Wireless LAN device driver: management IE handling- setting and - * deleting IE. - * - * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" - -/* This function checks if current IE index is used by any on other interface. - * Return: -1: yes, current IE index is used by someone else. - * 0: no, current IE index is NOT used by other interface. - */ -static int -mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx) -{ - int i; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie *ie; - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i] != priv) { - ie = &adapter->priv[i]->mgmt_ie[idx]; - if (ie->mgmt_subtype_mask && ie->ie_length) - return -1; - } - } - - return 0; -} - -/* Get unused IE index. This index will be used for setting new IE */ -static int -mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask, - struct mwifiex_ie *ie, u16 *index) -{ - u16 mask, len, i; - - for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) { - mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask); - len = le16_to_cpu(ie->ie_length); - - if (mask == MWIFIEX_AUTO_IDX_MASK) - continue; - - if (mask == subtype_mask) { - if (len > IEEE_MAX_IE_SIZE) - continue; - - *index = i; - return 0; - } - - if (!priv->mgmt_ie[i].ie_length) { - if (mwifiex_ie_index_used_by_other_intf(priv, i)) - continue; - - *index = i; - return 0; - } - } - - return -1; -} - -/* This function prepares IE data buffer for command to be sent to FW */ -static int -mwifiex_update_autoindex_ies(struct mwifiex_private *priv, - struct mwifiex_ie_list *ie_list) -{ - u16 travel_len, index, mask; - s16 input_len, tlv_len; - struct mwifiex_ie *ie; - u8 *tmp; - - input_len = le16_to_cpu(ie_list->len); - travel_len = sizeof(struct mwifiex_ie_types_header); - - ie_list->len = 0; - - while (input_len >= sizeof(struct mwifiex_ie_types_header)) { - ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len); - tlv_len = le16_to_cpu(ie->ie_length); - travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE; - - if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE) - return -1; - index = le16_to_cpu(ie->ie_index); - mask = le16_to_cpu(ie->mgmt_subtype_mask); - - if (index == MWIFIEX_AUTO_IDX_MASK) { - /* automatic addition */ - if (mwifiex_ie_get_autoidx(priv, mask, ie, &index)) - return -1; - if (index == MWIFIEX_AUTO_IDX_MASK) - return -1; - - tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer; - memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length)); - priv->mgmt_ie[index].ie_length = ie->ie_length; - priv->mgmt_ie[index].ie_index = cpu_to_le16(index); - priv->mgmt_ie[index].mgmt_subtype_mask = - cpu_to_le16(mask); - - ie->ie_index = cpu_to_le16(index); - } else { - if (mask != MWIFIEX_DELETE_MASK) - return -1; - /* - * Check if this index is being used on any - * other interface. - */ - if (mwifiex_ie_index_used_by_other_intf(priv, index)) - return -1; - - ie->ie_length = 0; - memcpy(&priv->mgmt_ie[index], ie, - sizeof(struct mwifiex_ie)); - } - - le16_add_cpu(&ie_list->len, - le16_to_cpu(priv->mgmt_ie[index].ie_length) + - MWIFIEX_IE_HDR_SIZE); - input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE; - } - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) - return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_CUSTOM_IE_I, ie_list, false); - - return 0; -} - -/* Copy individual custom IEs for beacon, probe response and assoc response - * and prepare single structure for IE setting. - * This function also updates allocated IE indices from driver. - */ -static int -mwifiex_update_uap_custom_ie(struct mwifiex_private *priv, - struct mwifiex_ie *beacon_ie, u16 *beacon_idx, - struct mwifiex_ie *pr_ie, u16 *probe_idx, - struct mwifiex_ie *ar_ie, u16 *assoc_idx) -{ - struct mwifiex_ie_list *ap_custom_ie; - u8 *pos; - u16 len; - int ret; - - ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL); - if (!ap_custom_ie) - return -ENOMEM; - - ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); - pos = (u8 *)ap_custom_ie->ie_list; - - if (beacon_ie) { - len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + - le16_to_cpu(beacon_ie->ie_length); - memcpy(pos, beacon_ie, len); - pos += len; - le16_add_cpu(&ap_custom_ie->len, len); - } - if (pr_ie) { - len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + - le16_to_cpu(pr_ie->ie_length); - memcpy(pos, pr_ie, len); - pos += len; - le16_add_cpu(&ap_custom_ie->len, len); - } - if (ar_ie) { - len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + - le16_to_cpu(ar_ie->ie_length); - memcpy(pos, ar_ie, len); - pos += len; - le16_add_cpu(&ap_custom_ie->len, len); - } - - ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie); - - pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index); - if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) { - /* save beacon ie index after auto-indexing */ - *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index); - len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE + - le16_to_cpu(beacon_ie->ie_length); - pos += len; - } - if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) { - /* save probe resp ie index after auto-indexing */ - *probe_idx = *((u16 *)pos); - len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE + - le16_to_cpu(pr_ie->ie_length); - pos += len; - } - if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) - /* save assoc resp ie index after auto-indexing */ - *assoc_idx = *((u16 *)pos); - - kfree(ap_custom_ie); - return ret; -} - -/* This function checks if the vendor specified IE is present in passed buffer - * and copies it to mwifiex_ie structure. - * Function takes pointer to struct mwifiex_ie pointer as argument. - * If the vendor specified IE is present then memory is allocated for - * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing - * this memory. - */ -static int mwifiex_update_vs_ie(const u8 *ies, int ies_len, - struct mwifiex_ie **ie_ptr, u16 mask, - unsigned int oui, u8 oui_type) -{ - struct ieee_types_header *vs_ie; - struct mwifiex_ie *ie = *ie_ptr; - const u8 *vendor_ie; - - vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len); - if (vendor_ie) { - if (!*ie_ptr) { - *ie_ptr = kzalloc(sizeof(struct mwifiex_ie), - GFP_KERNEL); - if (!*ie_ptr) - return -ENOMEM; - ie = *ie_ptr; - } - - vs_ie = (struct ieee_types_header *)vendor_ie; - memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length), - vs_ie, vs_ie->len + 2); - le16_add_cpu(&ie->ie_length, vs_ie->len + 2); - ie->mgmt_subtype_mask = cpu_to_le16(mask); - ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK); - } - - *ie_ptr = ie; - return 0; -} - -/* This function parses beacon IEs, probe response IEs, association response IEs - * from cfg80211_ap_settings->beacon and sets these IE to FW. - */ -static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv, - struct cfg80211_beacon_data *data) -{ - struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL; - u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK; - u16 ar_idx = MWIFIEX_AUTO_IDX_MASK; - int ret = 0; - - if (data->beacon_ies && data->beacon_ies_len) { - mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len, - &beacon_ie, MGMT_MASK_BEACON, - WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WPS); - mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len, - &beacon_ie, MGMT_MASK_BEACON, - WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); - } - - if (data->proberesp_ies && data->proberesp_ies_len) { - mwifiex_update_vs_ie(data->proberesp_ies, - data->proberesp_ies_len, &pr_ie, - MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WPS); - mwifiex_update_vs_ie(data->proberesp_ies, - data->proberesp_ies_len, &pr_ie, - MGMT_MASK_PROBE_RESP, - WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); - } - - if (data->assocresp_ies && data->assocresp_ies_len) { - mwifiex_update_vs_ie(data->assocresp_ies, - data->assocresp_ies_len, &ar_ie, - MGMT_MASK_ASSOC_RESP | - MGMT_MASK_REASSOC_RESP, - WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WPS); - mwifiex_update_vs_ie(data->assocresp_ies, - data->assocresp_ies_len, &ar_ie, - MGMT_MASK_ASSOC_RESP | - MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA, - WLAN_OUI_TYPE_WFA_P2P); - } - - if (beacon_ie || pr_ie || ar_ie) { - ret = mwifiex_update_uap_custom_ie(priv, beacon_ie, - &beacon_idx, pr_ie, - &pr_idx, ar_ie, &ar_idx); - if (ret) - goto done; - } - - priv->beacon_idx = beacon_idx; - priv->proberesp_idx = pr_idx; - priv->assocresp_idx = ar_idx; - -done: - kfree(beacon_ie); - kfree(pr_ie); - kfree(ar_ie); - - return ret; -} - -/* This function parses head and tail IEs, from cfg80211_beacon_data and sets - * these IE to FW. - */ -static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv, - struct cfg80211_beacon_data *info) -{ - struct mwifiex_ie *gen_ie; - struct ieee_types_header *hdr; - struct ieee80211_vendor_ie *vendorhdr; - u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0; - int left_len, parsed_len = 0; - - if (!info->tail || !info->tail_len) - return 0; - - gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL); - if (!gen_ie) - return -ENOMEM; - - left_len = info->tail_len; - - /* Many IEs are generated in FW by parsing bss configuration. - * Let's not add them here; else we may end up duplicating these IEs - */ - while (left_len > sizeof(struct ieee_types_header)) { - hdr = (void *)(info->tail + parsed_len); - switch (hdr->element_id) { - case WLAN_EID_SSID: - case WLAN_EID_SUPP_RATES: - case WLAN_EID_COUNTRY: - case WLAN_EID_PWR_CONSTRAINT: - case WLAN_EID_EXT_SUPP_RATES: - case WLAN_EID_HT_CAPABILITY: - case WLAN_EID_HT_OPERATION: - case WLAN_EID_VHT_CAPABILITY: - case WLAN_EID_VHT_OPERATION: - case WLAN_EID_VENDOR_SPECIFIC: - break; - default: - memcpy(gen_ie->ie_buffer + ie_len, hdr, - hdr->len + sizeof(struct ieee_types_header)); - ie_len += hdr->len + sizeof(struct ieee_types_header); - break; - } - left_len -= hdr->len + sizeof(struct ieee_types_header); - parsed_len += hdr->len + sizeof(struct ieee_types_header); - } - - /* parse only WPA vendor IE from tail, WMM IE is configured by - * bss_config command - */ - vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WPA, - info->tail, info->tail_len); - if (vendorhdr) { - memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, - vendorhdr->len + sizeof(struct ieee_types_header)); - ie_len += vendorhdr->len + sizeof(struct ieee_types_header); - } - - if (!ie_len) { - kfree(gen_ie); - return 0; - } - - gen_ie->ie_index = cpu_to_le16(gen_idx); - gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON | - MGMT_MASK_PROBE_RESP | - MGMT_MASK_ASSOC_RESP); - gen_ie->ie_length = cpu_to_le16(ie_len); - - if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL, - NULL, NULL)) { - kfree(gen_ie); - return -1; - } - - priv->gen_idx = gen_idx; - kfree(gen_ie); - return 0; -} - -/* This function parses different IEs-head & tail IEs, beacon IEs, - * probe response IEs, association response IEs from cfg80211_ap_settings - * function and sets these IE to FW. - */ -int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, - struct cfg80211_beacon_data *info) -{ - int ret; - - ret = mwifiex_uap_parse_tail_ies(priv, info); - - if (ret) - return ret; - - return mwifiex_set_mgmt_beacon_data_ies(priv, info); -} - -/* This function removes management IE set */ -int mwifiex_del_mgmt_ies(struct mwifiex_private *priv) -{ - struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL; - struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL; - int ret = 0; - - if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) { - gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL); - if (!gen_ie) - return -ENOMEM; - - gen_ie->ie_index = cpu_to_le16(priv->gen_idx); - gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); - gen_ie->ie_length = 0; - if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx, - NULL, &priv->proberesp_idx, - NULL, &priv->assocresp_idx)) { - ret = -1; - goto done; - } - - priv->gen_idx = MWIFIEX_AUTO_IDX_MASK; - } - - if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) { - beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); - if (!beacon_ie) { - ret = -ENOMEM; - goto done; - } - beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx); - beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); - beacon_ie->ie_length = 0; - } - if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) { - pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); - if (!pr_ie) { - ret = -ENOMEM; - goto done; - } - pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx); - pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); - pr_ie->ie_length = 0; - } - if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) { - ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); - if (!ar_ie) { - ret = -ENOMEM; - goto done; - } - ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx); - ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); - ar_ie->ie_length = 0; - } - - if (beacon_ie || pr_ie || ar_ie) - ret = mwifiex_update_uap_custom_ie(priv, - beacon_ie, &priv->beacon_idx, - pr_ie, &priv->proberesp_idx, - ar_ie, &priv->assocresp_idx); - -done: - kfree(gen_ie); - kfree(beacon_ie); - kfree(pr_ie); - kfree(ar_ie); - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c deleted file mode 100644 index de74a7773fb6..000000000000 --- a/drivers/net/wireless/mwifiex/init.c +++ /dev/null @@ -1,782 +0,0 @@ -/* - * Marvell Wireless LAN device driver: HW/FW Initialization - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" - -/* - * This function adds a BSS priority table to the table list. - * - * The function allocates a new BSS priority table node and adds it to - * the end of BSS priority table list, kept in driver memory. - */ -static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_bss_prio_node *bss_prio; - struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; - unsigned long flags; - - bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL); - if (!bss_prio) - return -ENOMEM; - - bss_prio->priv = priv; - INIT_LIST_HEAD(&bss_prio->list); - - spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags); - list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head); - spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags); - - return 0; -} - -static void wakeup_timer_fn(unsigned long data) -{ - struct mwifiex_adapter *adapter = (struct mwifiex_adapter *)data; - - mwifiex_dbg(adapter, ERROR, "Firmware wakeup failed\n"); - adapter->hw_status = MWIFIEX_HW_STATUS_RESET; - mwifiex_cancel_all_pending_cmd(adapter); - - if (adapter->if_ops.card_reset) - adapter->if_ops.card_reset(adapter); -} - -/* - * This function initializes the private structure and sets default - * values to the members. - * - * Additionally, it also initializes all the locks and sets up all the - * lists. - */ -int mwifiex_init_priv(struct mwifiex_private *priv) -{ - u32 i; - - priv->media_connected = false; - eth_broadcast_addr(priv->curr_addr); - priv->port_open = false; - priv->usb_port = MWIFIEX_USB_EP_DATA; - priv->pkt_tx_ctrl = 0; - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - priv->data_rate = 0; /* Initially indicate the rate as auto */ - priv->is_data_rate_auto = true; - priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; - priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; - - priv->sec_info.wep_enabled = 0; - priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; - priv->sec_info.encryption_mode = 0; - for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++) - memset(&priv->wep_key[i], 0, sizeof(struct mwifiex_wep_key)); - priv->wep_key_curr_index = 0; - priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON | - HostCmd_ACT_MAC_ETHERNETII_ENABLE; - - priv->beacon_period = 100; /* beacon interval */ ; - priv->attempted_bss_desc = NULL; - memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params)); - priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL; - - memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid)); - memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid)); - memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf)); - priv->assoc_rsp_size = 0; - priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; - priv->atim_window = 0; - priv->adhoc_state = ADHOC_IDLE; - priv->tx_power_level = 0; - priv->max_tx_power_level = 0; - priv->min_tx_power_level = 0; - priv->tx_rate = 0; - priv->rxpd_htinfo = 0; - priv->rxpd_rate = 0; - priv->rate_bitmap = 0; - priv->data_rssi_last = 0; - priv->data_rssi_avg = 0; - priv->data_nf_avg = 0; - priv->data_nf_last = 0; - priv->bcn_rssi_last = 0; - priv->bcn_rssi_avg = 0; - priv->bcn_nf_avg = 0; - priv->bcn_nf_last = 0; - memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie)); - memset(&priv->aes_key, 0, sizeof(priv->aes_key)); - priv->wpa_ie_len = 0; - priv->wpa_is_gtk_set = false; - - memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf)); - priv->assoc_tlv_buf_len = 0; - memset(&priv->wps, 0, sizeof(priv->wps)); - memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); - priv->gen_ie_buf_len = 0; - memset(priv->vs_ie, 0, sizeof(priv->vs_ie)); - - priv->wmm_required = true; - priv->wmm_enabled = false; - priv->wmm_qosinfo = 0; - priv->curr_bcn_buf = NULL; - priv->curr_bcn_size = 0; - priv->wps_ie = NULL; - priv->wps_ie_len = 0; - priv->ap_11n_enabled = 0; - memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg)); - - priv->scan_block = false; - - priv->csa_chan = 0; - priv->csa_expire_time = 0; - priv->del_list_idx = 0; - priv->hs2_enabled = false; - priv->check_tdls_tx = false; - memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID); - - mwifiex_init_11h_params(priv); - - return mwifiex_add_bss_prio_tbl(priv); -} - -/* - * This function allocates buffers for members of the adapter - * structure. - * - * The memory allocated includes scan table, command buffers, and - * sleep confirm command buffer. In addition, the queues are - * also initialized. - */ -static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter) -{ - int ret; - - /* Allocate command buffer */ - ret = mwifiex_alloc_cmd_buffer(adapter); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to alloc cmd buffer\n", - __func__); - return -1; - } - - adapter->sleep_cfm = - dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) - + INTF_HEADER_LEN); - - if (!adapter->sleep_cfm) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to alloc sleep cfm\t" - " cmd buffer\n", __func__); - return -1; - } - skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN); - - return 0; -} - -/* - * This function initializes the adapter structure and sets default - * values to the members of adapter. - * - * This also initializes the WMM related parameters in the driver private - * structures. - */ -static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) -{ - struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = NULL; - - skb_put(adapter->sleep_cfm, sizeof(struct mwifiex_opt_sleep_confirm)); - - adapter->cmd_sent = false; - - if (adapter->iface_type == MWIFIEX_SDIO) - adapter->data_sent = true; - else - adapter->data_sent = false; - - adapter->cmd_resp_received = false; - adapter->event_received = false; - adapter->data_received = false; - - adapter->surprise_removed = false; - - adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; - - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; - adapter->ps_state = PS_STATE_AWAKE; - adapter->need_to_wakeup = false; - - adapter->scan_mode = HostCmd_BSS_MODE_ANY; - adapter->specific_scan_time = MWIFIEX_SPECIFIC_SCAN_CHAN_TIME; - adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME; - adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME; - adapter->scan_chan_gap_time = MWIFIEX_DEF_SCAN_CHAN_GAP_TIME; - - adapter->scan_probes = 1; - - adapter->multiple_dtim = 1; - - adapter->local_listen_interval = 0; /* default value in firmware - will be used */ - - adapter->is_deep_sleep = false; - - adapter->delay_null_pkt = false; - adapter->delay_to_ps = 1000; - adapter->enhanced_ps_mode = PS_MODE_AUTO; - - adapter->gen_null_pkt = false; /* Disable NULL Pkg generation by - default */ - adapter->pps_uapsd_mode = false; /* Disable pps/uapsd mode by - default */ - adapter->pm_wakeup_card_req = false; - - adapter->pm_wakeup_fw_try = false; - - adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; - - adapter->is_hs_configured = false; - adapter->hs_cfg.conditions = cpu_to_le32(HS_CFG_COND_DEF); - adapter->hs_cfg.gpio = HS_CFG_GPIO_DEF; - adapter->hs_cfg.gap = HS_CFG_GAP_DEF; - adapter->hs_activated = false; - - memset(adapter->event_body, 0, sizeof(adapter->event_body)); - adapter->hw_dot_11n_dev_cap = 0; - adapter->hw_dev_mcs_support = 0; - adapter->sec_chan_offset = 0; - adapter->adhoc_11n_enabled = false; - - mwifiex_wmm_init(adapter); - - sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *) - adapter->sleep_cfm->data; - memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len); - sleep_cfm_buf->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); - sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len); - sleep_cfm_buf->result = 0; - sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM); - sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED); - - memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params)); - memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period)); - adapter->tx_lock_flag = false; - adapter->null_pkt_interval = 0; - adapter->fw_bands = 0; - adapter->config_bands = 0; - adapter->adhoc_start_band = 0; - adapter->scan_channels = NULL; - adapter->fw_release_number = 0; - adapter->fw_cap_info = 0; - memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf)); - adapter->event_cause = 0; - adapter->region_code = 0; - adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT; - adapter->adhoc_awake_period = 0; - memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter)); - adapter->arp_filter_size = 0; - adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; - adapter->key_api_major_ver = 0; - adapter->key_api_minor_ver = 0; - eth_broadcast_addr(adapter->perm_addr); - adapter->iface_limit.sta_intf = MWIFIEX_MAX_STA_NUM; - adapter->iface_limit.uap_intf = MWIFIEX_MAX_UAP_NUM; - adapter->iface_limit.p2p_intf = MWIFIEX_MAX_P2P_NUM; - adapter->active_scan_triggered = false; - setup_timer(&adapter->wakeup_timer, wakeup_timer_fn, - (unsigned long)adapter); -} - -/* - * This function sets trans_start per tx_queue - */ -void mwifiex_set_trans_start(struct net_device *dev) -{ - int i; - - for (i = 0; i < dev->num_tx_queues; i++) - netdev_get_tx_queue(dev, i)->trans_start = jiffies; - - dev->trans_start = jiffies; -} - -/* - * This function wakes up all queues in net_device - */ -void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, - struct mwifiex_adapter *adapter) -{ - unsigned long dev_queue_flags; - unsigned int i; - - spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); - - for (i = 0; i < netdev->num_tx_queues; i++) { - struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); - - if (netif_tx_queue_stopped(txq)) - netif_tx_wake_queue(txq); - } - - spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); -} - -/* - * This function stops all queues in net_device - */ -void mwifiex_stop_net_dev_queue(struct net_device *netdev, - struct mwifiex_adapter *adapter) -{ - unsigned long dev_queue_flags; - unsigned int i; - - spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); - - for (i = 0; i < netdev->num_tx_queues; i++) { - struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); - - if (!netif_tx_queue_stopped(txq)) - netif_tx_stop_queue(txq); - } - - spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); -} - -/* - * This function releases the lock variables and frees the locks and - * associated locks. - */ -static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - s32 i, j; - - /* Free lists */ - list_del(&adapter->cmd_free_q); - list_del(&adapter->cmd_pending_q); - list_del(&adapter->scan_pending_q); - - for (i = 0; i < adapter->priv_num; i++) - list_del(&adapter->bss_prio_tbl[i].bss_prio_head); - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - priv = adapter->priv[i]; - for (j = 0; j < MAX_NUM_TID; ++j) - list_del(&priv->wmm.tid_tbl_ptr[j].ra_list); - list_del(&priv->tx_ba_stream_tbl_ptr); - list_del(&priv->rx_reorder_tbl_ptr); - list_del(&priv->sta_list); - list_del(&priv->auto_tdls_list); - } - } -} - -/* - * This function performs cleanup for adapter structure. - * - * The cleanup is done recursively, by canceling all pending - * commands, freeing the member buffers previously allocated - * (command buffers, scan table buffer, sleep confirm command - * buffer), stopping the timers and calling the cleanup routines - * for every interface. - */ -static void -mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) -{ - int idx; - - if (!adapter) { - pr_err("%s: adapter is NULL\n", __func__); - return; - } - - del_timer(&adapter->wakeup_timer); - mwifiex_cancel_all_pending_cmd(adapter); - wake_up_interruptible(&adapter->cmd_wait_q.wait); - wake_up_interruptible(&adapter->hs_activate_wait_q); - - /* Free lock variables */ - mwifiex_free_lock_list(adapter); - - /* Free command buffer */ - mwifiex_dbg(adapter, INFO, "info: free cmd buffer\n"); - mwifiex_free_cmd_buffer(adapter); - - for (idx = 0; idx < adapter->num_mem_types; idx++) { - struct memory_type_mapping *entry = - &adapter->mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - } - - if (adapter->drv_info_dump) { - vfree(adapter->drv_info_dump); - adapter->drv_info_dump = NULL; - adapter->drv_info_size = 0; - } - - if (adapter->sleep_cfm) - dev_kfree_skb_any(adapter->sleep_cfm); -} - -/* - * This function intializes the lock variables and - * the list heads. - */ -int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - s32 i, j; - - spin_lock_init(&adapter->mwifiex_lock); - spin_lock_init(&adapter->int_lock); - spin_lock_init(&adapter->main_proc_lock); - spin_lock_init(&adapter->mwifiex_cmd_lock); - spin_lock_init(&adapter->queue_lock); - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - priv = adapter->priv[i]; - spin_lock_init(&priv->rx_pkt_lock); - spin_lock_init(&priv->wmm.ra_list_spinlock); - spin_lock_init(&priv->curr_bcn_buf_lock); - spin_lock_init(&priv->sta_list_spinlock); - spin_lock_init(&priv->auto_tdls_lock); - } - } - - /* Initialize cmd_free_q */ - INIT_LIST_HEAD(&adapter->cmd_free_q); - /* Initialize cmd_pending_q */ - INIT_LIST_HEAD(&adapter->cmd_pending_q); - /* Initialize scan_pending_q */ - INIT_LIST_HEAD(&adapter->scan_pending_q); - - spin_lock_init(&adapter->cmd_free_q_lock); - spin_lock_init(&adapter->cmd_pending_q_lock); - spin_lock_init(&adapter->scan_pending_q_lock); - spin_lock_init(&adapter->rx_proc_lock); - - skb_queue_head_init(&adapter->rx_data_q); - skb_queue_head_init(&adapter->tx_data_q); - - for (i = 0; i < adapter->priv_num; ++i) { - INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head); - spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock); - } - - for (i = 0; i < adapter->priv_num; i++) { - if (!adapter->priv[i]) - continue; - priv = adapter->priv[i]; - for (j = 0; j < MAX_NUM_TID; ++j) - INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list); - INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); - INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); - INIT_LIST_HEAD(&priv->sta_list); - INIT_LIST_HEAD(&priv->auto_tdls_list); - skb_queue_head_init(&priv->tdls_txq); - skb_queue_head_init(&priv->bypass_txq); - - spin_lock_init(&priv->tx_ba_stream_tbl_lock); - spin_lock_init(&priv->rx_reorder_tbl_lock); - - spin_lock_init(&priv->ack_status_lock); - idr_init(&priv->ack_status_frames); - } - - return 0; -} - -/* - * This function initializes the firmware. - * - * The following operations are performed sequentially - - * - Allocate adapter structure - * - Initialize the adapter structure - * - Initialize the private structure - * - Add BSS priority tables to the adapter structure - * - For each interface, send the init commands to firmware - * - Send the first command in command pending queue, if available - */ -int mwifiex_init_fw(struct mwifiex_adapter *adapter) -{ - int ret; - struct mwifiex_private *priv; - u8 i, first_sta = true; - int is_cmd_pend_q_empty; - unsigned long flags; - - adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; - - /* Allocate memory for member of adapter structure */ - ret = mwifiex_allocate_adapter(adapter); - if (ret) - return -1; - - /* Initialize adapter structure */ - mwifiex_init_adapter(adapter); - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - priv = adapter->priv[i]; - - /* Initialize private structure */ - ret = mwifiex_init_priv(priv); - if (ret) - return -1; - } - } - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta, - true); - if (ret == -1) - return -1; - - first_sta = false; - } - } - - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); - is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); - if (!is_cmd_pend_q_empty) { - /* Send the first command in queue and return */ - if (mwifiex_main_process(adapter) != -1) - ret = -EINPROGRESS; - } else { - adapter->hw_status = MWIFIEX_HW_STATUS_READY; - } - - return ret; -} - -/* - * This function deletes the BSS priority tables. - * - * The function traverses through all the allocated BSS priority nodes - * in every BSS priority table and frees them. - */ -static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv) -{ - int i; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_bss_prio_node *bssprio_node, *tmp_node; - struct list_head *head; - spinlock_t *lock; /* bss priority lock */ - unsigned long flags; - - for (i = 0; i < adapter->priv_num; ++i) { - head = &adapter->bss_prio_tbl[i].bss_prio_head; - lock = &adapter->bss_prio_tbl[i].bss_prio_lock; - mwifiex_dbg(adapter, INFO, - "info: delete BSS priority table,\t" - "bss_type = %d, bss_num = %d, i = %d,\t" - "head = %p\n", - priv->bss_type, priv->bss_num, i, head); - - { - spin_lock_irqsave(lock, flags); - if (list_empty(head)) { - spin_unlock_irqrestore(lock, flags); - continue; - } - list_for_each_entry_safe(bssprio_node, tmp_node, head, - list) { - if (bssprio_node->priv == priv) { - mwifiex_dbg(adapter, INFO, - "info: Delete\t" - "node %p, next = %p\n", - bssprio_node, tmp_node); - list_del(&bssprio_node->list); - kfree(bssprio_node); - } - } - spin_unlock_irqrestore(lock, flags); - } - } -} - -/* - * This function frees the private structure, including cleans - * up the TX and RX queues and frees the BSS priority tables. - */ -void mwifiex_free_priv(struct mwifiex_private *priv) -{ - mwifiex_clean_txrx(priv); - mwifiex_delete_bss_prio_tbl(priv); - mwifiex_free_curr_bcn(priv); -} - -/* - * This function is used to shutdown the driver. - * - * The following operations are performed sequentially - - * - Check if already shut down - * - Make sure the main process has stopped - * - Clean up the Tx and Rx queues - * - Delete BSS priority tables - * - Free the adapter - * - Notify completion - */ -int -mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) -{ - int ret = -EINPROGRESS; - struct mwifiex_private *priv; - s32 i; - unsigned long flags; - struct sk_buff *skb; - - /* mwifiex already shutdown */ - if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY) - return 0; - - adapter->hw_status = MWIFIEX_HW_STATUS_CLOSING; - /* wait for mwifiex_process to complete */ - if (adapter->mwifiex_processing) { - mwifiex_dbg(adapter, WARN, - "main process is still running\n"); - return ret; - } - - /* cancel current command */ - if (adapter->curr_cmd) { - mwifiex_dbg(adapter, WARN, - "curr_cmd is still in processing\n"); - del_timer_sync(&adapter->cmd_timer); - mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - adapter->curr_cmd = NULL; - } - - /* shut down mwifiex */ - mwifiex_dbg(adapter, MSG, - "info: shutdown mwifiex...\n"); - - /* Clean up Tx/Rx queues and delete BSS priority table */ - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - priv = adapter->priv[i]; - - mwifiex_clean_auto_tdls(priv); - mwifiex_abort_cac(priv); - mwifiex_clean_txrx(priv); - mwifiex_delete_bss_prio_tbl(priv); - } - } - - atomic_set(&adapter->tx_queued, 0); - while ((skb = skb_dequeue(&adapter->tx_data_q))) - mwifiex_write_data_complete(adapter, skb, 0, 0); - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - - while ((skb = skb_dequeue(&adapter->rx_data_q))) { - struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); - - atomic_dec(&adapter->rx_pending); - priv = adapter->priv[rx_info->bss_num]; - if (priv) - priv->stats.rx_dropped++; - - dev_kfree_skb_any(skb); - } - - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - - spin_lock(&adapter->mwifiex_lock); - - mwifiex_adapter_cleanup(adapter); - - spin_unlock(&adapter->mwifiex_lock); - - /* Notify completion */ - ret = mwifiex_shutdown_fw_complete(adapter); - - return ret; -} - -/* - * This function downloads the firmware to the card. - * - * The actual download is preceded by two sanity checks - - * - Check if firmware is already running - * - Check if the interface is the winner to download the firmware - * - * ...and followed by another - - * - Check if the firmware is downloaded successfully - * - * After download is successfully completed, the host interrupts are enabled. - */ -int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, - struct mwifiex_fw_image *pmfw) -{ - int ret; - u32 poll_num = 1; - - if (adapter->if_ops.check_fw_status) { - adapter->winner = 0; - - /* check if firmware is already running */ - ret = adapter->if_ops.check_fw_status(adapter, poll_num); - if (!ret) { - mwifiex_dbg(adapter, MSG, - "WLAN FW already running! Skip FW dnld\n"); - return 0; - } - - poll_num = MAX_FIRMWARE_POLL_TRIES; - - /* check if we are the winner for downloading FW */ - if (!adapter->winner) { - mwifiex_dbg(adapter, MSG, - "FW already running! Skip FW dnld\n"); - goto poll_fw; - } - } - - if (pmfw) { - /* Download firmware with helper */ - ret = adapter->if_ops.prog_fw(adapter, pmfw); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "prog_fw failed ret=%#x\n", ret); - return ret; - } - } - -poll_fw: - /* Check if the firmware is downloaded successfully or not */ - ret = adapter->if_ops.check_fw_status(adapter, poll_num); - if (ret) - mwifiex_dbg(adapter, ERROR, - "FW failed to be active in time\n"); - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h deleted file mode 100644 index 4f0174c64946..000000000000 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ /dev/null @@ -1,470 +0,0 @@ -/* - * Marvell Wireless LAN device driver: ioctl data structures & APIs - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_IOCTL_H_ -#define _MWIFIEX_IOCTL_H_ - -#include - -enum { - MWIFIEX_SCAN_TYPE_UNCHANGED = 0, - MWIFIEX_SCAN_TYPE_ACTIVE, - MWIFIEX_SCAN_TYPE_PASSIVE -}; - -struct mwifiex_user_scan { - u32 scan_cfg_len; - u8 scan_cfg_buf[1]; -}; - -#define MWIFIEX_PROMISC_MODE 1 -#define MWIFIEX_MULTICAST_MODE 2 -#define MWIFIEX_ALL_MULTI_MODE 4 -#define MWIFIEX_MAX_MULTICAST_LIST_SIZE 32 - -struct mwifiex_multicast_list { - u32 mode; - u32 num_multicast_addr; - u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; -}; - -struct mwifiex_chan_freq { - u32 channel; - u32 freq; -}; - -struct mwifiex_ssid_bssid { - struct cfg80211_ssid ssid; - u8 bssid[ETH_ALEN]; -}; - -enum { - BAND_B = 1, - BAND_G = 2, - BAND_A = 4, - BAND_GN = 8, - BAND_AN = 16, - BAND_AAC = 32, -}; - -#define MWIFIEX_WPA_PASSHPHRASE_LEN 64 -struct wpa_param { - u8 pairwise_cipher_wpa; - u8 pairwise_cipher_wpa2; - u8 group_cipher; - u32 length; - u8 passphrase[MWIFIEX_WPA_PASSHPHRASE_LEN]; -}; - -struct wep_key { - u8 key_index; - u8 is_default; - u16 length; - u8 key[WLAN_KEY_LEN_WEP104]; -}; - -#define KEY_MGMT_ON_HOST 0x03 -#define MWIFIEX_AUTH_MODE_AUTO 0xFF -#define BAND_CONFIG_BG 0x00 -#define BAND_CONFIG_A 0x01 -#define MWIFIEX_SUPPORTED_RATES 14 -#define MWIFIEX_SUPPORTED_RATES_EXT 32 -#define MWIFIEX_TDLS_SUPPORTED_RATES 8 -#define MWIFIEX_TDLS_DEF_QOS_CAPAB 0xf -#define MWIFIEX_PRIO_BK 2 -#define MWIFIEX_PRIO_VI 5 - -struct mwifiex_uap_bss_param { - u8 channel; - u8 band_cfg; - u16 rts_threshold; - u16 frag_threshold; - u8 retry_limit; - struct mwifiex_802_11_ssid ssid; - u8 bcast_ssid_ctl; - u8 radio_ctl; - u8 dtim_period; - u16 beacon_period; - u16 auth_mode; - u16 protocol; - u16 key_mgmt; - u16 key_mgmt_operation; - struct wpa_param wpa_cfg; - struct wep_key wep_cfg[NUM_WEP_KEYS]; - struct ieee80211_ht_cap ht_cap; - struct ieee80211_vht_cap vht_cap; - u8 rates[MWIFIEX_SUPPORTED_RATES]; - u32 sta_ao_timer; - u32 ps_sta_ao_timer; - u8 qos_info; - u8 power_constraint; - struct mwifiex_types_wmm_info wmm_info; -}; - -enum { - ADHOC_IDLE, - ADHOC_STARTED, - ADHOC_JOINED, - ADHOC_COALESCED -}; - -struct mwifiex_ds_get_stats { - u32 mcast_tx_frame; - u32 failed; - u32 retry; - u32 multi_retry; - u32 frame_dup; - u32 rts_success; - u32 rts_failure; - u32 ack_failure; - u32 rx_frag; - u32 mcast_rx_frame; - u32 fcs_error; - u32 tx_frame; - u32 wep_icv_error[4]; - u32 bcn_rcv_cnt; - u32 bcn_miss_cnt; -}; - -#define MWIFIEX_MAX_VER_STR_LEN 128 - -struct mwifiex_ver_ext { - u32 version_str_sel; - char version_str[MWIFIEX_MAX_VER_STR_LEN]; -}; - -struct mwifiex_bss_info { - u32 bss_mode; - struct cfg80211_ssid ssid; - u32 bss_chan; - u8 country_code[3]; - u32 media_connected; - u32 max_power_level; - u32 min_power_level; - u32 adhoc_state; - signed int bcn_nf_last; - u32 wep_status; - u32 is_hs_configured; - u32 is_deep_sleep; - u8 bssid[ETH_ALEN]; -}; - -#define MAX_NUM_TID 8 - -#define MAX_RX_WINSIZE 64 - -struct mwifiex_ds_rx_reorder_tbl { - u16 tid; - u8 ta[ETH_ALEN]; - u32 start_win; - u32 win_size; - u32 buffer[MAX_RX_WINSIZE]; -}; - -struct mwifiex_ds_tx_ba_stream_tbl { - u16 tid; - u8 ra[ETH_ALEN]; - u8 amsdu; -}; - -#define DBG_CMD_NUM 5 - -struct tdls_peer_info { - u8 peer_addr[ETH_ALEN]; -}; - -struct mwifiex_debug_info { - unsigned int debug_mask; - u32 int_counter; - u32 packets_out[MAX_NUM_TID]; - u32 tx_buf_size; - u32 curr_tx_buf_size; - u32 tx_tbl_num; - struct mwifiex_ds_tx_ba_stream_tbl - tx_tbl[MWIFIEX_MAX_TX_BASTREAM_SUPPORTED]; - u32 rx_tbl_num; - struct mwifiex_ds_rx_reorder_tbl rx_tbl - [MWIFIEX_MAX_RX_BASTREAM_SUPPORTED]; - u32 tdls_peer_num; - struct tdls_peer_info tdls_list - [MWIFIEX_MAX_TDLS_PEER_SUPPORTED]; - u16 ps_mode; - u32 ps_state; - u8 is_deep_sleep; - u8 pm_wakeup_card_req; - u32 pm_wakeup_fw_try; - u8 is_hs_configured; - u8 hs_activated; - u32 num_cmd_host_to_card_failure; - u32 num_cmd_sleep_cfm_host_to_card_failure; - u32 num_tx_host_to_card_failure; - u32 num_event_deauth; - u32 num_event_disassoc; - u32 num_event_link_lost; - u32 num_cmd_deauth; - u32 num_cmd_assoc_success; - u32 num_cmd_assoc_failure; - u32 num_tx_timeout; - u8 is_cmd_timedout; - u16 timeout_cmd_id; - u16 timeout_cmd_act; - u16 last_cmd_id[DBG_CMD_NUM]; - u16 last_cmd_act[DBG_CMD_NUM]; - u16 last_cmd_index; - u16 last_cmd_resp_id[DBG_CMD_NUM]; - u16 last_cmd_resp_index; - u16 last_event[DBG_CMD_NUM]; - u16 last_event_index; - u8 data_sent; - u8 cmd_sent; - u8 cmd_resp_received; - u8 event_received; -}; - -#define MWIFIEX_KEY_INDEX_UNICAST 0x40000000 -#define PN_LEN 16 - -struct mwifiex_ds_encrypt_key { - u32 key_disable; - u32 key_index; - u32 key_len; - u8 key_material[WLAN_MAX_KEY_LEN]; - u8 mac_addr[ETH_ALEN]; - u32 is_wapi_key; - u8 pn[PN_LEN]; /* packet number */ - u8 pn_len; - u8 is_igtk_key; - u8 is_current_wep_key; - u8 is_rx_seq_valid; -}; - -struct mwifiex_power_cfg { - u32 is_power_auto; - u32 power_level; -}; - -struct mwifiex_ds_hs_cfg { - u32 is_invoke_hostcmd; - /* Bit0: non-unicast data - * Bit1: unicast data - * Bit2: mac events - * Bit3: magic packet - */ - u32 conditions; - u32 gpio; - u32 gap; -}; - -#define DEEP_SLEEP_ON 1 -#define DEEP_SLEEP_OFF 0 -#define DEEP_SLEEP_IDLE_TIME 100 -#define PS_MODE_AUTO 1 - -struct mwifiex_ds_auto_ds { - u16 auto_ds; - u16 idle_time; -}; - -struct mwifiex_ds_pm_cfg { - union { - u32 ps_mode; - struct mwifiex_ds_hs_cfg hs_cfg; - struct mwifiex_ds_auto_ds auto_deep_sleep; - u32 sleep_period; - } param; -}; - -struct mwifiex_11ac_vht_cfg { - u8 band_config; - u8 misc_config; - u32 cap_info; - u32 mcs_tx_set; - u32 mcs_rx_set; -}; - -struct mwifiex_ds_11n_tx_cfg { - u16 tx_htcap; - u16 tx_htinfo; - u16 misc_config; /* Needed for 802.11AC cards only */ -}; - -struct mwifiex_ds_11n_amsdu_aggr_ctrl { - u16 enable; - u16 curr_buf_size; -}; - -struct mwifiex_ds_ant_cfg { - u32 tx_ant; - u32 rx_ant; -}; - -#define MWIFIEX_NUM_OF_CMD_BUFFER 50 -#define MWIFIEX_SIZE_OF_CMD_BUFFER 2048 - -enum { - MWIFIEX_IE_TYPE_GEN_IE = 0, - MWIFIEX_IE_TYPE_ARP_FILTER, -}; - -enum { - MWIFIEX_REG_MAC = 1, - MWIFIEX_REG_BBP, - MWIFIEX_REG_RF, - MWIFIEX_REG_PMIC, - MWIFIEX_REG_CAU, -}; - -struct mwifiex_ds_reg_rw { - __le32 type; - __le32 offset; - __le32 value; -}; - -#define MAX_EEPROM_DATA 256 - -struct mwifiex_ds_read_eeprom { - __le16 offset; - __le16 byte_count; - u8 value[MAX_EEPROM_DATA]; -}; - -struct mwifiex_ds_mem_rw { - u32 addr; - u32 value; -}; - -#define IEEE_MAX_IE_SIZE 256 - -#define MWIFIEX_IE_HDR_SIZE (sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE) - -struct mwifiex_ds_misc_gen_ie { - u32 type; - u32 len; - u8 ie_data[IEEE_MAX_IE_SIZE]; -}; - -struct mwifiex_ds_misc_cmd { - u32 len; - u8 cmd[MWIFIEX_SIZE_OF_CMD_BUFFER]; -}; - -#define BITMASK_BCN_RSSI_LOW BIT(0) -#define BITMASK_BCN_RSSI_HIGH BIT(4) - -enum subsc_evt_rssi_state { - EVENT_HANDLED, - RSSI_LOW_RECVD, - RSSI_HIGH_RECVD -}; - -struct subsc_evt_cfg { - u8 abs_value; - u8 evt_freq; -}; - -struct mwifiex_ds_misc_subsc_evt { - u16 action; - u16 events; - struct subsc_evt_cfg bcn_l_rssi_cfg; - struct subsc_evt_cfg bcn_h_rssi_cfg; -}; - -#define MWIFIEX_MEF_MAX_BYTESEQ 6 /* non-adjustable */ -#define MWIFIEX_MEF_MAX_FILTERS 10 - -struct mwifiex_mef_filter { - u16 repeat; - u16 offset; - s8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; - u8 filt_type; - u8 filt_action; -}; - -struct mwifiex_mef_entry { - u8 mode; - u8 action; - struct mwifiex_mef_filter filter[MWIFIEX_MEF_MAX_FILTERS]; -}; - -struct mwifiex_ds_mef_cfg { - u32 criteria; - u16 num_entries; - struct mwifiex_mef_entry *mef_entry; -}; - -#define MWIFIEX_MAX_VSIE_LEN (256) -#define MWIFIEX_MAX_VSIE_NUM (8) -#define MWIFIEX_VSIE_MASK_CLEAR 0x00 -#define MWIFIEX_VSIE_MASK_SCAN 0x01 -#define MWIFIEX_VSIE_MASK_ASSOC 0x02 -#define MWIFIEX_VSIE_MASK_ADHOC 0x04 - -enum { - MWIFIEX_FUNC_INIT = 1, - MWIFIEX_FUNC_SHUTDOWN, -}; - -enum COALESCE_OPERATION { - RECV_FILTER_MATCH_TYPE_EQ = 0x80, - RECV_FILTER_MATCH_TYPE_NE, -}; - -enum COALESCE_PACKET_TYPE { - PACKET_TYPE_UNICAST = 1, - PACKET_TYPE_MULTICAST = 2, - PACKET_TYPE_BROADCAST = 3 -}; - -#define MWIFIEX_COALESCE_MAX_RULES 8 -#define MWIFIEX_COALESCE_MAX_BYTESEQ 4 /* non-adjustable */ -#define MWIFIEX_COALESCE_MAX_FILTERS 4 -#define MWIFIEX_MAX_COALESCING_DELAY 100 /* in msecs */ - -struct filt_field_param { - u8 operation; - u8 operand_len; - u16 offset; - u8 operand_byte_stream[MWIFIEX_COALESCE_MAX_BYTESEQ]; -}; - -struct mwifiex_coalesce_rule { - u16 max_coalescing_delay; - u8 num_of_fields; - u8 pkt_type; - struct filt_field_param params[MWIFIEX_COALESCE_MAX_FILTERS]; -}; - -struct mwifiex_ds_coalesce_cfg { - u16 num_of_rules; - struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES]; -}; - -struct mwifiex_ds_tdls_oper { - u16 tdls_action; - u8 peer_mac[ETH_ALEN]; - u16 capability; - u8 qos_info; - u8 *ext_capab; - u8 ext_capab_len; - u8 *supp_rates; - u8 supp_rates_len; - u8 *ht_capab; -}; - -#endif /* !_MWIFIEX_IOCTL_H_ */ diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c deleted file mode 100644 index 3cda1f956f0b..000000000000 --- a/drivers/net/wireless/mwifiex/join.c +++ /dev/null @@ -1,1525 +0,0 @@ -/* - * Marvell Wireless LAN device driver: association and ad-hoc start/join - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11ac.h" - -#define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9))) - -/* - * Append a generic IE as a pass through TLV to a TLV buffer. - * - * This function is called from the network join command preparation routine. - * - * If the IE buffer has been setup by the application, this routine appends - * the buffer as a pass through TLV type to the request. - */ -static int -mwifiex_cmd_append_generic_ie(struct mwifiex_private *priv, u8 **buffer) -{ - int ret_len = 0; - struct mwifiex_ie_types_header ie_header; - - /* Null Checks */ - if (!buffer) - return 0; - if (!(*buffer)) - return 0; - - /* - * If there is a generic ie buffer setup, append it to the return - * parameter buffer pointer. - */ - if (priv->gen_ie_buf_len) { - mwifiex_dbg(priv->adapter, INFO, - "info: %s: append generic ie len %d to %p\n", - __func__, priv->gen_ie_buf_len, *buffer); - - /* Wrap the generic IE buffer with a pass through TLV type */ - ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); - ie_header.len = cpu_to_le16(priv->gen_ie_buf_len); - memcpy(*buffer, &ie_header, sizeof(ie_header)); - - /* Increment the return size and the return buffer pointer - param */ - *buffer += sizeof(ie_header); - ret_len += sizeof(ie_header); - - /* Copy the generic IE buffer to the output buffer, advance - pointer */ - memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len); - - /* Increment the return size and the return buffer pointer - param */ - *buffer += priv->gen_ie_buf_len; - ret_len += priv->gen_ie_buf_len; - - /* Reset the generic IE buffer */ - priv->gen_ie_buf_len = 0; - } - - /* return the length appended to the buffer */ - return ret_len; -} - -/* - * Append TSF tracking info from the scan table for the target AP. - * - * This function is called from the network join command preparation routine. - * - * The TSF table TSF sent to the firmware contains two TSF values: - * - The TSF of the target AP from its previous beacon/probe response - * - The TSF timestamp of our local MAC at the time we observed the - * beacon/probe response. - * - * The firmware uses the timestamp values to set an initial TSF value - * in the MAC for the new association after a reassociation attempt. - */ -static int -mwifiex_cmd_append_tsf_tlv(struct mwifiex_private *priv, u8 **buffer, - struct mwifiex_bssdescriptor *bss_desc) -{ - struct mwifiex_ie_types_tsf_timestamp tsf_tlv; - __le64 tsf_val; - - /* Null Checks */ - if (buffer == NULL) - return 0; - if (*buffer == NULL) - return 0; - - memset(&tsf_tlv, 0x00, sizeof(struct mwifiex_ie_types_tsf_timestamp)); - - tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP); - tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val)); - - memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header)); - *buffer += sizeof(tsf_tlv.header); - - /* TSF at the time when beacon/probe_response was received */ - tsf_val = cpu_to_le64(bss_desc->fw_tsf); - memcpy(*buffer, &tsf_val, sizeof(tsf_val)); - *buffer += sizeof(tsf_val); - - tsf_val = cpu_to_le64(bss_desc->timestamp); - - mwifiex_dbg(priv->adapter, INFO, - "info: %s: TSF offset calc: %016llx - %016llx\n", - __func__, bss_desc->timestamp, bss_desc->fw_tsf); - - memcpy(*buffer, &tsf_val, sizeof(tsf_val)); - *buffer += sizeof(tsf_val); - - return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val)); -} - -/* - * This function finds out the common rates between rate1 and rate2. - * - * It will fill common rates in rate1 as output if found. - * - * NOTE: Setting the MSB of the basic rates needs to be taken - * care of, either before or after calling this function. - */ -static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1, - u32 rate1_size, u8 *rate2, u32 rate2_size) -{ - int ret; - u8 *ptr = rate1, *tmp; - u32 i, j; - - tmp = kmemdup(rate1, rate1_size, GFP_KERNEL); - if (!tmp) { - mwifiex_dbg(priv->adapter, ERROR, "failed to alloc tmp buf\n"); - return -ENOMEM; - } - - memset(rate1, 0, rate1_size); - - for (i = 0; i < rate2_size && rate2[i]; i++) { - for (j = 0; j < rate1_size && tmp[j]; j++) { - /* Check common rate, excluding the bit for - basic rate */ - if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) { - *rate1++ = tmp[j]; - break; - } - } - } - - mwifiex_dbg(priv->adapter, INFO, "info: Tx data rate set to %#x\n", - priv->data_rate); - - if (!priv->is_data_rate_auto) { - while (*ptr) { - if ((*ptr & 0x7f) == priv->data_rate) { - ret = 0; - goto done; - } - ptr++; - } - mwifiex_dbg(priv->adapter, ERROR, - "previously set fixed data rate %#x\t" - "is not compatible with the network\n", - priv->data_rate); - - ret = -1; - goto done; - } - - ret = 0; -done: - kfree(tmp); - return ret; -} - -/* - * This function creates the intersection of the rates supported by a - * target BSS and our adapter settings for use in an assoc/join command. - */ -static int -mwifiex_setup_rates_from_bssdesc(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, - u8 *out_rates, u32 *out_rates_size) -{ - u8 card_rates[MWIFIEX_SUPPORTED_RATES]; - u32 card_rates_size; - - /* Copy AP supported rates */ - memcpy(out_rates, bss_desc->supported_rates, MWIFIEX_SUPPORTED_RATES); - /* Get the STA supported rates */ - card_rates_size = mwifiex_get_active_data_rates(priv, card_rates); - /* Get the common rates between AP and STA supported rates */ - if (mwifiex_get_common_rates(priv, out_rates, MWIFIEX_SUPPORTED_RATES, - card_rates, card_rates_size)) { - *out_rates_size = 0; - mwifiex_dbg(priv->adapter, ERROR, - "%s: cannot get common rates\n", - __func__); - return -1; - } - - *out_rates_size = - min_t(size_t, strlen(out_rates), MWIFIEX_SUPPORTED_RATES); - - return 0; -} - -/* - * This function appends a WPS IE. It is called from the network join command - * preparation routine. - * - * If the IE buffer has been setup by the application, this routine appends - * the buffer as a WPS TLV type to the request. - */ -static int -mwifiex_cmd_append_wps_ie(struct mwifiex_private *priv, u8 **buffer) -{ - int retLen = 0; - struct mwifiex_ie_types_header ie_header; - - if (!buffer || !*buffer) - return 0; - - /* - * If there is a wps ie buffer setup, append it to the return - * parameter buffer pointer. - */ - if (priv->wps_ie_len) { - mwifiex_dbg(priv->adapter, CMD, - "cmd: append wps ie %d to %p\n", - priv->wps_ie_len, *buffer); - - /* Wrap the generic IE buffer with a pass through TLV type */ - ie_header.type = cpu_to_le16(TLV_TYPE_MGMT_IE); - ie_header.len = cpu_to_le16(priv->wps_ie_len); - memcpy(*buffer, &ie_header, sizeof(ie_header)); - *buffer += sizeof(ie_header); - retLen += sizeof(ie_header); - - memcpy(*buffer, priv->wps_ie, priv->wps_ie_len); - *buffer += priv->wps_ie_len; - retLen += priv->wps_ie_len; - - } - - kfree(priv->wps_ie); - priv->wps_ie_len = 0; - return retLen; -} - -/* - * This function appends a WAPI IE. - * - * This function is called from the network join command preparation routine. - * - * If the IE buffer has been setup by the application, this routine appends - * the buffer as a WAPI TLV type to the request. - */ -static int -mwifiex_cmd_append_wapi_ie(struct mwifiex_private *priv, u8 **buffer) -{ - int retLen = 0; - struct mwifiex_ie_types_header ie_header; - - /* Null Checks */ - if (buffer == NULL) - return 0; - if (*buffer == NULL) - return 0; - - /* - * If there is a wapi ie buffer setup, append it to the return - * parameter buffer pointer. - */ - if (priv->wapi_ie_len) { - mwifiex_dbg(priv->adapter, CMD, - "cmd: append wapi ie %d to %p\n", - priv->wapi_ie_len, *buffer); - - /* Wrap the generic IE buffer with a pass through TLV type */ - ie_header.type = cpu_to_le16(TLV_TYPE_WAPI_IE); - ie_header.len = cpu_to_le16(priv->wapi_ie_len); - memcpy(*buffer, &ie_header, sizeof(ie_header)); - - /* Increment the return size and the return buffer pointer - param */ - *buffer += sizeof(ie_header); - retLen += sizeof(ie_header); - - /* Copy the wapi IE buffer to the output buffer, advance - pointer */ - memcpy(*buffer, priv->wapi_ie, priv->wapi_ie_len); - - /* Increment the return size and the return buffer pointer - param */ - *buffer += priv->wapi_ie_len; - retLen += priv->wapi_ie_len; - - } - /* return the length appended to the buffer */ - return retLen; -} - -/* - * This function appends rsn ie tlv for wpa/wpa2 security modes. - * It is called from the network join command preparation routine. - */ -static int mwifiex_append_rsn_ie_wpa_wpa2(struct mwifiex_private *priv, - u8 **buffer) -{ - struct mwifiex_ie_types_rsn_param_set *rsn_ie_tlv; - int rsn_ie_len; - - if (!buffer || !(*buffer)) - return 0; - - rsn_ie_tlv = (struct mwifiex_ie_types_rsn_param_set *) (*buffer); - rsn_ie_tlv->header.type = cpu_to_le16((u16) priv->wpa_ie[0]); - rsn_ie_tlv->header.type = cpu_to_le16( - le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF); - rsn_ie_tlv->header.len = cpu_to_le16((u16) priv->wpa_ie[1]); - rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len) - & 0x00FF); - if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2)) - memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2], - le16_to_cpu(rsn_ie_tlv->header.len)); - else - return -1; - - rsn_ie_len = sizeof(rsn_ie_tlv->header) + - le16_to_cpu(rsn_ie_tlv->header.len); - *buffer += rsn_ie_len; - - return rsn_ie_len; -} - -/* - * This function prepares command for association. - * - * This sets the following parameters - - * - Peer MAC address - * - Listen interval - * - Beacon interval - * - Capability information - * - * ...and the following TLVs, as required - - * - SSID TLV - * - PHY TLV - * - SS TLV - * - Rates TLV - * - Authentication TLV - * - Channel TLV - * - WPA/WPA2 IE - * - 11n TLV - * - Vendor specific TLV - * - WMM TLV - * - WAPI IE - * - Generic IE - * - TSF TLV - * - * Preparation also includes - - * - Setting command ID and proper size - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_bssdescriptor *bss_desc) -{ - struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate; - struct mwifiex_ie_types_ssid_param_set *ssid_tlv; - struct mwifiex_ie_types_phy_param_set *phy_tlv; - struct mwifiex_ie_types_ss_param_set *ss_tlv; - struct mwifiex_ie_types_rates_param_set *rates_tlv; - struct mwifiex_ie_types_auth_type *auth_tlv; - struct mwifiex_ie_types_chan_list_param_set *chan_tlv; - u8 rates[MWIFIEX_SUPPORTED_RATES]; - u32 rates_size; - u16 tmp_cap; - u8 *pos; - int rsn_ie_len = 0; - - pos = (u8 *) assoc; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE); - - /* Save so we know which BSS Desc to use in the response handler */ - priv->attempted_bss_desc = bss_desc; - - memcpy(assoc->peer_sta_addr, - bss_desc->mac_address, sizeof(assoc->peer_sta_addr)); - pos += sizeof(assoc->peer_sta_addr); - - /* Set the listen interval */ - assoc->listen_interval = cpu_to_le16(priv->listen_interval); - /* Set the beacon period */ - assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period); - - pos += sizeof(assoc->cap_info_bitmap); - pos += sizeof(assoc->listen_interval); - pos += sizeof(assoc->beacon_period); - pos += sizeof(assoc->dtim_period); - - ssid_tlv = (struct mwifiex_ie_types_ssid_param_set *) pos; - ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID); - ssid_tlv->header.len = cpu_to_le16((u16) bss_desc->ssid.ssid_len); - memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid, - le16_to_cpu(ssid_tlv->header.len)); - pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len); - - phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos; - phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS); - phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set)); - memcpy(&phy_tlv->fh_ds.ds_param_set, - &bss_desc->phy_param_set.ds_param_set.current_chan, - sizeof(phy_tlv->fh_ds.ds_param_set)); - pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len); - - ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos; - ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS); - ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set)); - pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len); - - /* Get the common rates supported between the driver and the BSS Desc */ - if (mwifiex_setup_rates_from_bssdesc - (priv, bss_desc, rates, &rates_size)) - return -1; - - /* Save the data rates into Current BSS state structure */ - priv->curr_bss_params.num_of_rates = rates_size; - memcpy(&priv->curr_bss_params.data_rates, rates, rates_size); - - /* Setup the Rates TLV in the association command */ - rates_tlv = (struct mwifiex_ie_types_rates_param_set *) pos; - rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); - rates_tlv->header.len = cpu_to_le16((u16) rates_size); - memcpy(rates_tlv->rates, rates, rates_size); - pos += sizeof(rates_tlv->header) + rates_size; - mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_CMD: rates size = %d\n", - rates_size); - - /* Add the Authentication type to be used for Auth frames */ - auth_tlv = (struct mwifiex_ie_types_auth_type *) pos; - auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); - auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type)); - if (priv->sec_info.wep_enabled) - auth_tlv->auth_type = cpu_to_le16( - (u16) priv->sec_info.authentication_mode); - else - auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM); - - pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len); - - if (IS_SUPPORT_MULTI_BANDS(priv->adapter) && - !(ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && - (!bss_desc->disable_11n) && - (priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN) && - (bss_desc->bcn_ht_cap) - ) - ) { - /* Append a channel TLV for the channel the attempted AP was - found on */ - chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; - chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - chan_tlv->header.len = - cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); - - memset(chan_tlv->chan_scan_param, 0x00, - sizeof(struct mwifiex_chan_scan_param_set)); - chan_tlv->chan_scan_param[0].chan_number = - (bss_desc->phy_param_set.ds_param_set.current_chan); - mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Chan = %d\n", - chan_tlv->chan_scan_param[0].chan_number); - - chan_tlv->chan_scan_param[0].radio_type = - mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - - mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Band = %d\n", - chan_tlv->chan_scan_param[0].radio_type); - pos += sizeof(chan_tlv->header) + - sizeof(struct mwifiex_chan_scan_param_set); - } - - if (!priv->wps.session_enable) { - if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) - rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); - - if (rsn_ie_len == -1) - return -1; - } - - if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && - (!bss_desc->disable_11n) && - (priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN)) - mwifiex_cmd_append_11n_tlv(priv, bss_desc, &pos); - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && - !bss_desc->disable_11n && !bss_desc->disable_11ac && - priv->adapter->config_bands & BAND_AAC) - mwifiex_cmd_append_11ac_tlv(priv, bss_desc, &pos); - - /* Append vendor specific IE TLV */ - mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_ASSOC, &pos); - - mwifiex_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie, - bss_desc->bcn_ht_cap); - if (priv->sec_info.wapi_enabled && priv->wapi_ie_len) - mwifiex_cmd_append_wapi_ie(priv, &pos); - - if (priv->wps.session_enable && priv->wps_ie_len) - mwifiex_cmd_append_wps_ie(priv, &pos); - - mwifiex_cmd_append_generic_ie(priv, &pos); - - mwifiex_cmd_append_tsf_tlv(priv, &pos, bss_desc); - - mwifiex_11h_process_join(priv, &pos, bss_desc); - - cmd->size = cpu_to_le16((u16) (pos - (u8 *) assoc) + S_DS_GEN); - - /* Set the Capability info at last */ - tmp_cap = bss_desc->cap_info_bitmap; - - if (priv->adapter->config_bands == BAND_B) - tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; - - tmp_cap &= CAPINFO_MASK; - mwifiex_dbg(priv->adapter, INFO, - "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", - tmp_cap, CAPINFO_MASK); - assoc->cap_info_bitmap = cpu_to_le16(tmp_cap); - - return 0; -} - -static const char *assoc_failure_reason_to_str(u16 cap_info) -{ - switch (cap_info) { - case CONNECT_ERR_AUTH_ERR_STA_FAILURE: - return "CONNECT_ERR_AUTH_ERR_STA_FAILURE"; - case CONNECT_ERR_AUTH_MSG_UNHANDLED: - return "CONNECT_ERR_AUTH_MSG_UNHANDLED"; - case CONNECT_ERR_ASSOC_ERR_TIMEOUT: - return "CONNECT_ERR_ASSOC_ERR_TIMEOUT"; - case CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED: - return "CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED"; - case CONNECT_ERR_STA_FAILURE: - return "CONNECT_ERR_STA_FAILURE"; - } - - return "Unknown connect failure"; -} -/* - * Association firmware command response handler - * - * The response buffer for the association command has the following - * memory layout. - * - * For cases where an association response was not received (indicated - * by the CapInfo and AId field): - * - * .------------------------------------------------------------. - * | Header(4 * sizeof(t_u16)): Standard command response hdr | - * .------------------------------------------------------------. - * | cap_info/Error Return(t_u16): | - * | 0xFFFF(-1): Internal error | - * | 0xFFFE(-2): Authentication unhandled message | - * | 0xFFFD(-3): Authentication refused | - * | 0xFFFC(-4): Timeout waiting for AP response | - * .------------------------------------------------------------. - * | status_code(t_u16): | - * | If cap_info is -1: | - * | An internal firmware failure prevented the | - * | command from being processed. The status_code | - * | will be set to 1. | - * | | - * | If cap_info is -2: | - * | An authentication frame was received but was | - * | not handled by the firmware. IEEE Status | - * | code for the failure is returned. | - * | | - * | If cap_info is -3: | - * | An authentication frame was received and the | - * | status_code is the IEEE Status reported in the | - * | response. | - * | | - * | If cap_info is -4: | - * | (1) Association response timeout | - * | (2) Authentication response timeout | - * .------------------------------------------------------------. - * | a_id(t_u16): 0xFFFF | - * .------------------------------------------------------------. - * - * - * For cases where an association response was received, the IEEE - * standard association response frame is returned: - * - * .------------------------------------------------------------. - * | Header(4 * sizeof(t_u16)): Standard command response hdr | - * .------------------------------------------------------------. - * | cap_info(t_u16): IEEE Capability | - * .------------------------------------------------------------. - * | status_code(t_u16): IEEE Status Code | - * .------------------------------------------------------------. - * | a_id(t_u16): IEEE Association ID | - * .------------------------------------------------------------. - * | IEEE IEs(variable): Any received IEs comprising the | - * | remaining portion of a received | - * | association response frame. | - * .------------------------------------------------------------. - * - * For simplistic handling, the status_code field can be used to determine - * an association success (0) or failure (non-zero). - */ -int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret = 0; - struct ieee_types_assoc_rsp *assoc_rsp; - struct mwifiex_bssdescriptor *bss_desc; - bool enable_data = true; - u16 cap_info, status_code, aid; - - assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; - - cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap); - status_code = le16_to_cpu(assoc_rsp->status_code); - aid = le16_to_cpu(assoc_rsp->a_id); - - if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) - dev_err(priv->adapter->dev, - "invalid AID value 0x%x; bits 15:14 not set\n", - aid); - - aid &= ~(BIT(15) | BIT(14)); - - priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN, - sizeof(priv->assoc_rsp_buf)); - - memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size); - - assoc_rsp->a_id = cpu_to_le16(aid); - - if (status_code) { - priv->adapter->dbg.num_cmd_assoc_failure++; - mwifiex_dbg(priv->adapter, ERROR, - "ASSOC_RESP: failed,\t" - "status code=%d err=%#x a_id=%#x\n", - status_code, cap_info, - le16_to_cpu(assoc_rsp->a_id)); - - mwifiex_dbg(priv->adapter, ERROR, "assoc failure: reason %s\n", - assoc_failure_reason_to_str(cap_info)); - if (cap_info == CONNECT_ERR_ASSOC_ERR_TIMEOUT) { - if (status_code == MWIFIEX_ASSOC_CMD_FAILURE_AUTH) { - ret = WLAN_STATUS_AUTH_TIMEOUT; - mwifiex_dbg(priv->adapter, ERROR, - "ASSOC_RESP: AUTH timeout\n"); - } else { - ret = WLAN_STATUS_UNSPECIFIED_FAILURE; - mwifiex_dbg(priv->adapter, ERROR, - "ASSOC_RESP: UNSPECIFIED failure\n"); - } - } else { - ret = status_code; - } - - goto done; - } - - /* Send a Media Connected event, according to the Spec */ - priv->media_connected = true; - - priv->adapter->ps_state = PS_STATE_AWAKE; - priv->adapter->pps_uapsd_mode = false; - priv->adapter->tx_lock_flag = false; - - /* Set the attempted BSSID Index to current */ - bss_desc = priv->attempted_bss_desc; - - mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: %s\n", - bss_desc->ssid.ssid); - - /* Make a copy of current BSSID descriptor */ - memcpy(&priv->curr_bss_params.bss_descriptor, - bss_desc, sizeof(struct mwifiex_bssdescriptor)); - - /* Update curr_bss_params */ - priv->curr_bss_params.bss_descriptor.channel - = bss_desc->phy_param_set.ds_param_set.current_chan; - - priv->curr_bss_params.band = (u8) bss_desc->bss_band; - - if (bss_desc->wmm_ie.vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) - priv->curr_bss_params.wmm_enabled = true; - else - priv->curr_bss_params.wmm_enabled = false; - - if ((priv->wmm_required || bss_desc->bcn_ht_cap) && - priv->curr_bss_params.wmm_enabled) - priv->wmm_enabled = true; - else - priv->wmm_enabled = false; - - priv->curr_bss_params.wmm_uapsd_enabled = false; - - if (priv->wmm_enabled) - priv->curr_bss_params.wmm_uapsd_enabled - = ((bss_desc->wmm_ie.qos_info_bitmap & - IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0); - - mwifiex_dbg(priv->adapter, INFO, - "info: ASSOC_RESP: curr_pkt_filter is %#x\n", - priv->curr_pkt_filter); - if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) - priv->wpa_is_gtk_set = false; - - if (priv->wmm_enabled) { - /* Don't re-enable carrier until we get the WMM_GET_STATUS - event */ - enable_data = false; - } else { - /* Since WMM is not enabled, setup the queues with the - defaults */ - mwifiex_wmm_setup_queue_priorities(priv, NULL); - mwifiex_wmm_setup_ac_downgrade(priv); - } - - if (enable_data) - mwifiex_dbg(priv->adapter, INFO, - "info: post association, re-enabling data flow\n"); - - /* Reset SNR/NF/RSSI values */ - priv->data_rssi_last = 0; - priv->data_nf_last = 0; - priv->data_rssi_avg = 0; - priv->data_nf_avg = 0; - priv->bcn_rssi_last = 0; - priv->bcn_nf_last = 0; - priv->bcn_rssi_avg = 0; - priv->bcn_nf_avg = 0; - priv->rxpd_rate = 0; - priv->rxpd_htinfo = 0; - - mwifiex_save_curr_bcn(priv); - - priv->adapter->dbg.num_cmd_assoc_success++; - - mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: associated\n"); - - /* Add the ra_list here for infra mode as there will be only 1 ra - always */ - mwifiex_ralist_add(priv, - priv->curr_bss_params.bss_descriptor.mac_address); - - if (!netif_carrier_ok(priv->netdev)) - netif_carrier_on(priv->netdev); - mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); - - if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) - priv->scan_block = true; - else - priv->port_open = true; - -done: - /* Need to indicate IOCTL complete */ - if (adapter->curr_cmd->wait_q_enabled) { - if (ret) - adapter->cmd_wait_q.status = -1; - else - adapter->cmd_wait_q.status = 0; - } - - return ret; -} - -/* - * This function prepares command for ad-hoc start. - * - * Driver will fill up SSID, BSS mode, IBSS parameters, physical - * parameters, probe delay, and capability information. Firmware - * will fill up beacon period, basic rates and operational rates. - * - * In addition, the following TLVs are added - - * - Channel TLV - * - Vendor specific IE - * - WPA/WPA2 IE - * - HT Capabilities IE - * - HT Information IE - * - * Preparation also includes - - * - Setting command ID and proper size - * - Ensuring correct endian-ness - */ -int -mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct cfg80211_ssid *req_ssid) -{ - int rsn_ie_len = 0; - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_ad_hoc_start *adhoc_start = - &cmd->params.adhoc_start; - struct mwifiex_bssdescriptor *bss_desc; - u32 cmd_append_size = 0; - u32 i; - u16 tmp_cap; - struct mwifiex_ie_types_chan_list_param_set *chan_tlv; - u8 radio_type; - - struct mwifiex_ie_types_htcap *ht_cap; - struct mwifiex_ie_types_htinfo *ht_info; - u8 *pos = (u8 *) adhoc_start + - sizeof(struct host_cmd_ds_802_11_ad_hoc_start); - - if (!adapter) - return -1; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_START); - - bss_desc = &priv->curr_bss_params.bss_descriptor; - priv->attempted_bss_desc = bss_desc; - - /* - * Fill in the parameters for 2 data structures: - * 1. struct host_cmd_ds_802_11_ad_hoc_start command - * 2. bss_desc - * Driver will fill up SSID, bss_mode,IBSS param, Physical Param, - * probe delay, and Cap info. - * Firmware will fill up beacon period, Basic rates - * and operational rates. - */ - - memset(adhoc_start->ssid, 0, IEEE80211_MAX_SSID_LEN); - - memcpy(adhoc_start->ssid, req_ssid->ssid, req_ssid->ssid_len); - - mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: SSID = %s\n", - adhoc_start->ssid); - - memset(bss_desc->ssid.ssid, 0, IEEE80211_MAX_SSID_LEN); - memcpy(bss_desc->ssid.ssid, req_ssid->ssid, req_ssid->ssid_len); - - bss_desc->ssid.ssid_len = req_ssid->ssid_len; - - /* Set the BSS mode */ - adhoc_start->bss_mode = HostCmd_BSS_MODE_IBSS; - bss_desc->bss_mode = NL80211_IFTYPE_ADHOC; - adhoc_start->beacon_period = cpu_to_le16(priv->beacon_period); - bss_desc->beacon_period = priv->beacon_period; - - /* Set Physical param set */ -/* Parameter IE Id */ -#define DS_PARA_IE_ID 3 -/* Parameter IE length */ -#define DS_PARA_IE_LEN 1 - - adhoc_start->phy_param_set.ds_param_set.element_id = DS_PARA_IE_ID; - adhoc_start->phy_param_set.ds_param_set.len = DS_PARA_IE_LEN; - - if (!mwifiex_get_cfp(priv, adapter->adhoc_start_band, - (u16) priv->adhoc_channel, 0)) { - struct mwifiex_chan_freq_power *cfp; - cfp = mwifiex_get_cfp(priv, adapter->adhoc_start_band, - FIRST_VALID_CHANNEL, 0); - if (cfp) - priv->adhoc_channel = (u8) cfp->channel; - } - - if (!priv->adhoc_channel) { - mwifiex_dbg(adapter, ERROR, - "ADHOC_S_CMD: adhoc_channel cannot be 0\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "info: ADHOC_S_CMD: creating ADHOC on channel %d\n", - priv->adhoc_channel); - - priv->curr_bss_params.bss_descriptor.channel = priv->adhoc_channel; - priv->curr_bss_params.band = adapter->adhoc_start_band; - - bss_desc->channel = priv->adhoc_channel; - adhoc_start->phy_param_set.ds_param_set.current_chan = - priv->adhoc_channel; - - memcpy(&bss_desc->phy_param_set, &adhoc_start->phy_param_set, - sizeof(union ieee_types_phy_param_set)); - - /* Set IBSS param set */ -/* IBSS parameter IE Id */ -#define IBSS_PARA_IE_ID 6 -/* IBSS parameter IE length */ -#define IBSS_PARA_IE_LEN 2 - - adhoc_start->ss_param_set.ibss_param_set.element_id = IBSS_PARA_IE_ID; - adhoc_start->ss_param_set.ibss_param_set.len = IBSS_PARA_IE_LEN; - adhoc_start->ss_param_set.ibss_param_set.atim_window - = cpu_to_le16(priv->atim_window); - memcpy(&bss_desc->ss_param_set, &adhoc_start->ss_param_set, - sizeof(union ieee_types_ss_param_set)); - - /* Set Capability info */ - bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_IBSS; - tmp_cap = WLAN_CAPABILITY_IBSS; - - /* Set up privacy in bss_desc */ - if (priv->sec_info.encryption_mode) { - /* Ad-Hoc capability privacy on */ - mwifiex_dbg(adapter, INFO, - "info: ADHOC_S_CMD: wep_status set privacy to WEP\n"); - bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; - tmp_cap |= WLAN_CAPABILITY_PRIVACY; - } else { - mwifiex_dbg(adapter, INFO, - "info: ADHOC_S_CMD: wep_status NOT set,\t" - "setting privacy to ACCEPT ALL\n"); - bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; - } - - memset(adhoc_start->data_rate, 0, sizeof(adhoc_start->data_rate)); - mwifiex_get_active_data_rates(priv, adhoc_start->data_rate); - if ((adapter->adhoc_start_band & BAND_G) && - (priv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) { - if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter, false)) { - mwifiex_dbg(adapter, ERROR, - "ADHOC_S_CMD: G Protection config failed\n"); - return -1; - } - } - /* Find the last non zero */ - for (i = 0; i < sizeof(adhoc_start->data_rate); i++) - if (!adhoc_start->data_rate[i]) - break; - - priv->curr_bss_params.num_of_rates = i; - - /* Copy the ad-hoc creating rates into Current BSS rate structure */ - memcpy(&priv->curr_bss_params.data_rates, - &adhoc_start->data_rate, priv->curr_bss_params.num_of_rates); - - mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: rates=%4ph\n", - adhoc_start->data_rate); - - mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n"); - - if (IS_SUPPORT_MULTI_BANDS(adapter)) { - /* Append a channel TLV */ - chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; - chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - chan_tlv->header.len = - cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); - - memset(chan_tlv->chan_scan_param, 0x00, - sizeof(struct mwifiex_chan_scan_param_set)); - chan_tlv->chan_scan_param[0].chan_number = - (u8) priv->curr_bss_params.bss_descriptor.channel; - - mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Chan = %d\n", - chan_tlv->chan_scan_param[0].chan_number); - - chan_tlv->chan_scan_param[0].radio_type - = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - if (adapter->adhoc_start_band & BAND_GN || - adapter->adhoc_start_band & BAND_AN) { - if (adapter->sec_chan_offset == - IEEE80211_HT_PARAM_CHA_SEC_ABOVE) - chan_tlv->chan_scan_param[0].radio_type |= - (IEEE80211_HT_PARAM_CHA_SEC_ABOVE << 4); - else if (adapter->sec_chan_offset == - IEEE80211_HT_PARAM_CHA_SEC_BELOW) - chan_tlv->chan_scan_param[0].radio_type |= - (IEEE80211_HT_PARAM_CHA_SEC_BELOW << 4); - } - mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Band = %d\n", - chan_tlv->chan_scan_param[0].radio_type); - pos += sizeof(chan_tlv->header) + - sizeof(struct mwifiex_chan_scan_param_set); - cmd_append_size += - sizeof(chan_tlv->header) + - sizeof(struct mwifiex_chan_scan_param_set); - } - - /* Append vendor specific IE TLV */ - cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, - MWIFIEX_VSIE_MASK_ADHOC, &pos); - - if (priv->sec_info.wpa_enabled) { - rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); - if (rsn_ie_len == -1) - return -1; - cmd_append_size += rsn_ie_len; - } - - if (adapter->adhoc_11n_enabled) { - /* Fill HT CAPABILITY */ - ht_cap = (struct mwifiex_ie_types_htcap *) pos; - memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); - ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); - ht_cap->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_cap)); - radio_type = mwifiex_band_to_radio_type( - priv->adapter->config_bands); - mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); - - if (adapter->sec_chan_offset == - IEEE80211_HT_PARAM_CHA_SEC_NONE) { - u16 tmp_ht_cap; - - tmp_ht_cap = le16_to_cpu(ht_cap->ht_cap.cap_info); - tmp_ht_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - tmp_ht_cap &= ~IEEE80211_HT_CAP_SGI_40; - ht_cap->ht_cap.cap_info = cpu_to_le16(tmp_ht_cap); - } - - pos += sizeof(struct mwifiex_ie_types_htcap); - cmd_append_size += sizeof(struct mwifiex_ie_types_htcap); - - /* Fill HT INFORMATION */ - ht_info = (struct mwifiex_ie_types_htinfo *) pos; - memset(ht_info, 0, sizeof(struct mwifiex_ie_types_htinfo)); - ht_info->header.type = cpu_to_le16(WLAN_EID_HT_OPERATION); - ht_info->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_operation)); - - ht_info->ht_oper.primary_chan = - (u8) priv->curr_bss_params.bss_descriptor.channel; - if (adapter->sec_chan_offset) { - ht_info->ht_oper.ht_param = adapter->sec_chan_offset; - ht_info->ht_oper.ht_param |= - IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; - } - ht_info->ht_oper.operation_mode = - cpu_to_le16(IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - ht_info->ht_oper.basic_set[0] = 0xff; - pos += sizeof(struct mwifiex_ie_types_htinfo); - cmd_append_size += - sizeof(struct mwifiex_ie_types_htinfo); - } - - cmd->size = - cpu_to_le16((u16)(sizeof(struct host_cmd_ds_802_11_ad_hoc_start) - + S_DS_GEN + cmd_append_size)); - - if (adapter->adhoc_start_band == BAND_B) - tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; - else - tmp_cap |= WLAN_CAPABILITY_SHORT_SLOT_TIME; - - adhoc_start->cap_info_bitmap = cpu_to_le16(tmp_cap); - - return 0; -} - -/* - * This function prepares command for ad-hoc join. - * - * Most of the parameters are set up by copying from the target BSS descriptor - * from the scan response. - * - * In addition, the following TLVs are added - - * - Channel TLV - * - Vendor specific IE - * - WPA/WPA2 IE - * - 11n IE - * - * Preparation also includes - - * - Setting command ID and proper size - * - Ensuring correct endian-ness - */ -int -mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_bssdescriptor *bss_desc) -{ - int rsn_ie_len = 0; - struct host_cmd_ds_802_11_ad_hoc_join *adhoc_join = - &cmd->params.adhoc_join; - struct mwifiex_ie_types_chan_list_param_set *chan_tlv; - u32 cmd_append_size = 0; - u16 tmp_cap; - u32 i, rates_size = 0; - u16 curr_pkt_filter; - u8 *pos = - (u8 *) adhoc_join + - sizeof(struct host_cmd_ds_802_11_ad_hoc_join); - -/* Use G protection */ -#define USE_G_PROTECTION 0x02 - if (bss_desc->erp_flags & USE_G_PROTECTION) { - curr_pkt_filter = - priv-> - curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON; - - if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &curr_pkt_filter, false)) { - mwifiex_dbg(priv->adapter, ERROR, - "ADHOC_J_CMD: G Protection config failed\n"); - return -1; - } - } - - priv->attempted_bss_desc = bss_desc; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_JOIN); - - adhoc_join->bss_descriptor.bss_mode = HostCmd_BSS_MODE_IBSS; - - adhoc_join->bss_descriptor.beacon_period - = cpu_to_le16(bss_desc->beacon_period); - - memcpy(&adhoc_join->bss_descriptor.bssid, - &bss_desc->mac_address, ETH_ALEN); - - memcpy(&adhoc_join->bss_descriptor.ssid, - &bss_desc->ssid.ssid, bss_desc->ssid.ssid_len); - - memcpy(&adhoc_join->bss_descriptor.phy_param_set, - &bss_desc->phy_param_set, - sizeof(union ieee_types_phy_param_set)); - - memcpy(&adhoc_join->bss_descriptor.ss_param_set, - &bss_desc->ss_param_set, sizeof(union ieee_types_ss_param_set)); - - tmp_cap = bss_desc->cap_info_bitmap; - - tmp_cap &= CAPINFO_MASK; - - mwifiex_dbg(priv->adapter, INFO, - "info: ADHOC_J_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", - tmp_cap, CAPINFO_MASK); - - /* Information on BSSID descriptor passed to FW */ - mwifiex_dbg(priv->adapter, INFO, - "info: ADHOC_J_CMD: BSSID=%pM, SSID='%s'\n", - adhoc_join->bss_descriptor.bssid, - adhoc_join->bss_descriptor.ssid); - - for (i = 0; i < MWIFIEX_SUPPORTED_RATES && - bss_desc->supported_rates[i]; i++) - ; - rates_size = i; - - /* Copy Data Rates from the Rates recorded in scan response */ - memset(adhoc_join->bss_descriptor.data_rates, 0, - sizeof(adhoc_join->bss_descriptor.data_rates)); - memcpy(adhoc_join->bss_descriptor.data_rates, - bss_desc->supported_rates, rates_size); - - /* Copy the adhoc join rates into Current BSS state structure */ - priv->curr_bss_params.num_of_rates = rates_size; - memcpy(&priv->curr_bss_params.data_rates, bss_desc->supported_rates, - rates_size); - - /* Copy the channel information */ - priv->curr_bss_params.bss_descriptor.channel = bss_desc->channel; - priv->curr_bss_params.band = (u8) bss_desc->bss_band; - - if (priv->sec_info.wep_enabled || priv->sec_info.wpa_enabled) - tmp_cap |= WLAN_CAPABILITY_PRIVACY; - - if (IS_SUPPORT_MULTI_BANDS(priv->adapter)) { - /* Append a channel TLV */ - chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; - chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - chan_tlv->header.len = - cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); - - memset(chan_tlv->chan_scan_param, 0x00, - sizeof(struct mwifiex_chan_scan_param_set)); - chan_tlv->chan_scan_param[0].chan_number = - (bss_desc->phy_param_set.ds_param_set.current_chan); - mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Chan=%d\n", - chan_tlv->chan_scan_param[0].chan_number); - - chan_tlv->chan_scan_param[0].radio_type = - mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - - mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Band=%d\n", - chan_tlv->chan_scan_param[0].radio_type); - pos += sizeof(chan_tlv->header) + - sizeof(struct mwifiex_chan_scan_param_set); - cmd_append_size += sizeof(chan_tlv->header) + - sizeof(struct mwifiex_chan_scan_param_set); - } - - if (priv->sec_info.wpa_enabled) - rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); - if (rsn_ie_len == -1) - return -1; - cmd_append_size += rsn_ie_len; - - if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) - cmd_append_size += mwifiex_cmd_append_11n_tlv(priv, - bss_desc, &pos); - - /* Append vendor specific IE TLV */ - cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, - MWIFIEX_VSIE_MASK_ADHOC, &pos); - - cmd->size = cpu_to_le16 - ((u16) (sizeof(struct host_cmd_ds_802_11_ad_hoc_join) - + S_DS_GEN + cmd_append_size)); - - adhoc_join->bss_descriptor.cap_info_bitmap = cpu_to_le16(tmp_cap); - - return 0; -} - -/* - * This function handles the command response of ad-hoc start and - * ad-hoc join. - * - * The function generates a device-connected event to notify - * the applications, in case of successful ad-hoc start/join, and - * saves the beacon buffer. - */ -int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - int ret = 0; - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_ad_hoc_result *adhoc_result; - struct mwifiex_bssdescriptor *bss_desc; - u16 reason_code; - - adhoc_result = &resp->params.adhoc_result; - - bss_desc = priv->attempted_bss_desc; - - /* Join result code 0 --> SUCCESS */ - reason_code = le16_to_cpu(resp->result); - if (reason_code) { - mwifiex_dbg(priv->adapter, ERROR, "ADHOC_RESP: failed\n"); - if (priv->media_connected) - mwifiex_reset_connect_state(priv, reason_code); - - memset(&priv->curr_bss_params.bss_descriptor, - 0x00, sizeof(struct mwifiex_bssdescriptor)); - - ret = -1; - goto done; - } - - /* Send a Media Connected event, according to the Spec */ - priv->media_connected = true; - - if (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_AD_HOC_START) { - mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_S_RESP %s\n", - bss_desc->ssid.ssid); - - /* Update the created network descriptor with the new BSSID */ - memcpy(bss_desc->mac_address, - adhoc_result->bssid, ETH_ALEN); - - priv->adhoc_state = ADHOC_STARTED; - } else { - /* - * Now the join cmd should be successful. - * If BSSID has changed use SSID to compare instead of BSSID - */ - mwifiex_dbg(priv->adapter, INFO, - "info: ADHOC_J_RESP %s\n", - bss_desc->ssid.ssid); - - /* - * Make a copy of current BSSID descriptor, only needed for - * join since the current descriptor is already being used - * for adhoc start - */ - memcpy(&priv->curr_bss_params.bss_descriptor, - bss_desc, sizeof(struct mwifiex_bssdescriptor)); - - priv->adhoc_state = ADHOC_JOINED; - } - - mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: channel = %d\n", - priv->adhoc_channel); - mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: BSSID = %pM\n", - priv->curr_bss_params.bss_descriptor.mac_address); - - if (!netif_carrier_ok(priv->netdev)) - netif_carrier_on(priv->netdev); - mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); - - mwifiex_save_curr_bcn(priv); - -done: - /* Need to indicate IOCTL complete */ - if (adapter->curr_cmd->wait_q_enabled) { - if (ret) - adapter->cmd_wait_q.status = -1; - else - adapter->cmd_wait_q.status = 0; - - } - - return ret; -} - -/* - * This function associates to a specific BSS discovered in a scan. - * - * It clears any past association response stored for application - * retrieval and calls the command preparation routine to send the - * command to firmware. - */ -int mwifiex_associate(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - /* Return error if the adapter is not STA role or table entry - * is not marked as infra. - */ - if ((GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) || - (bss_desc->bss_mode != NL80211_IFTYPE_STATION)) - return -1; - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && - !bss_desc->disable_11n && !bss_desc->disable_11ac && - priv->adapter->config_bands & BAND_AAC) - mwifiex_set_11ac_ba_params(priv); - else - mwifiex_set_ba_params(priv); - - /* Clear any past association response stored for application - retrieval */ - priv->assoc_rsp_size = 0; - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_ASSOCIATE, - HostCmd_ACT_GEN_SET, 0, bss_desc, true); -} - -/* - * This function starts an ad-hoc network. - * - * It calls the command preparation routine to send the command to firmware. - */ -int -mwifiex_adhoc_start(struct mwifiex_private *priv, - struct cfg80211_ssid *adhoc_ssid) -{ - mwifiex_dbg(priv->adapter, INFO, "info: Adhoc Channel = %d\n", - priv->adhoc_channel); - mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.channel = %d\n", - priv->curr_bss_params.bss_descriptor.channel); - mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.band = %d\n", - priv->curr_bss_params.band); - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && - priv->adapter->config_bands & BAND_AAC) - mwifiex_set_11ac_ba_params(priv); - else - mwifiex_set_ba_params(priv); - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_START, - HostCmd_ACT_GEN_SET, 0, adhoc_ssid, true); -} - -/* - * This function joins an ad-hoc network found in a previous scan. - * - * It calls the command preparation routine to send the command to firmware, - * if already not connected to the requested SSID. - */ -int mwifiex_adhoc_join(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - mwifiex_dbg(priv->adapter, INFO, - "info: adhoc join: curr_bss ssid =%s\n", - priv->curr_bss_params.bss_descriptor.ssid.ssid); - mwifiex_dbg(priv->adapter, INFO, - "info: adhoc join: curr_bss ssid_len =%u\n", - priv->curr_bss_params.bss_descriptor.ssid.ssid_len); - mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid =%s\n", - bss_desc->ssid.ssid); - mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid_len =%u\n", - bss_desc->ssid.ssid_len); - - /* Check if the requested SSID is already joined */ - if (priv->curr_bss_params.bss_descriptor.ssid.ssid_len && - !mwifiex_ssid_cmp(&bss_desc->ssid, - &priv->curr_bss_params.bss_descriptor.ssid) && - (priv->curr_bss_params.bss_descriptor.bss_mode == - NL80211_IFTYPE_ADHOC)) { - mwifiex_dbg(priv->adapter, INFO, - "info: ADHOC_J_CMD: new ad-hoc SSID\t" - "is the same as current; not attempting to re-join\n"); - return -1; - } - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && - !bss_desc->disable_11n && !bss_desc->disable_11ac && - priv->adapter->config_bands & BAND_AAC) - mwifiex_set_11ac_ba_params(priv); - else - mwifiex_set_ba_params(priv); - - mwifiex_dbg(priv->adapter, INFO, - "info: curr_bss_params.channel = %d\n", - priv->curr_bss_params.bss_descriptor.channel); - mwifiex_dbg(priv->adapter, INFO, - "info: curr_bss_params.band = %c\n", - priv->curr_bss_params.band); - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_JOIN, - HostCmd_ACT_GEN_SET, 0, bss_desc, true); -} - -/* - * This function deauthenticates/disconnects from infra network by sending - * deauthentication request. - */ -static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac) -{ - u8 mac_address[ETH_ALEN]; - int ret; - - if (!mac || is_zero_ether_addr(mac)) - memcpy(mac_address, - priv->curr_bss_params.bss_descriptor.mac_address, - ETH_ALEN); - else - memcpy(mac_address, mac, ETH_ALEN); - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, - HostCmd_ACT_GEN_SET, 0, mac_address, true); - - return ret; -} - -/* - * This function deauthenticates/disconnects from a BSS. - * - * In case of infra made, it sends deauthentication request, and - * in case of ad-hoc mode, a stop network request is sent to the firmware. - * In AP mode, a command to stop bss is sent to firmware. - */ -int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac) -{ - int ret = 0; - - if (!priv->media_connected) - return 0; - - switch (priv->bss_mode) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - ret = mwifiex_deauthenticate_infra(priv, mac); - if (ret) - cfg80211_disconnected(priv->netdev, 0, NULL, 0, - true, GFP_KERNEL); - break; - case NL80211_IFTYPE_ADHOC: - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_STOP, - HostCmd_ACT_GEN_SET, 0, NULL, true); - case NL80211_IFTYPE_AP: - return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, - HostCmd_ACT_GEN_SET, 0, NULL, true); - default: - break; - } - - return ret; -} - -/* This function deauthenticates/disconnects from all BSS. */ -void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - int i; - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv) - mwifiex_deauthenticate(priv, NULL); - } -} -EXPORT_SYMBOL_GPL(mwifiex_deauthenticate_all); - -/* - * This function converts band to radio type used in channel TLV. - */ -u8 -mwifiex_band_to_radio_type(u8 band) -{ - switch (band) { - case BAND_A: - case BAND_AN: - case BAND_A | BAND_AN: - case BAND_A | BAND_AN | BAND_AAC: - return HostCmd_SCAN_RADIO_TYPE_A; - case BAND_B: - case BAND_G: - case BAND_B | BAND_G: - default: - return HostCmd_SCAN_RADIO_TYPE_BG; - } -} diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c deleted file mode 100644 index 969ca1e1f3e9..000000000000 --- a/drivers/net/wireless/mwifiex/main.c +++ /dev/null @@ -1,1552 +0,0 @@ -/* - * Marvell Wireless LAN device driver: major functions - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" -#include "wmm.h" -#include "cfg80211.h" -#include "11n.h" - -#define VERSION "1.0" - -static unsigned int debug_mask = MWIFIEX_DEFAULT_DEBUG_MASK; -module_param(debug_mask, uint, 0); -MODULE_PARM_DESC(debug_mask, "bitmap for debug flags"); - -const char driver_version[] = "mwifiex " VERSION " (%s) "; -static char *cal_data_cfg; -module_param(cal_data_cfg, charp, 0); - -static unsigned short driver_mode; -module_param(driver_mode, ushort, 0); -MODULE_PARM_DESC(driver_mode, - "station=0x1(default), ap-sta=0x3, station-p2p=0x5, ap-sta-p2p=0x7"); - -/* - * This function registers the device and performs all the necessary - * initializations. - * - * The following initialization operations are performed - - * - Allocate adapter structure - * - Save interface specific operations table in adapter - * - Call interface specific initialization routine - * - Allocate private structures - * - Set default adapter structure parameters - * - Initialize locks - * - * In case of any errors during inittialization, this function also ensures - * proper cleanup before exiting. - */ -static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, - void **padapter) -{ - struct mwifiex_adapter *adapter; - int i; - - adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL); - if (!adapter) - return -ENOMEM; - - *padapter = adapter; - adapter->card = card; - - /* Save interface specific operations in adapter */ - memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops)); - adapter->debug_mask = debug_mask; - - /* card specific initialization has been deferred until now .. */ - if (adapter->if_ops.init_if) - if (adapter->if_ops.init_if(adapter)) - goto error; - - adapter->priv_num = 0; - - for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) { - /* Allocate memory for private structure */ - adapter->priv[i] = - kzalloc(sizeof(struct mwifiex_private), GFP_KERNEL); - if (!adapter->priv[i]) - goto error; - - adapter->priv[i]->adapter = adapter; - adapter->priv_num++; - } - mwifiex_init_lock_list(adapter); - - setup_timer(&adapter->cmd_timer, mwifiex_cmd_timeout_func, - (unsigned long)adapter); - - return 0; - -error: - mwifiex_dbg(adapter, ERROR, - "info: leave mwifiex_register with error\n"); - - for (i = 0; i < adapter->priv_num; i++) - kfree(adapter->priv[i]); - - kfree(adapter); - - return -1; -} - -/* - * This function unregisters the device and performs all the necessary - * cleanups. - * - * The following cleanup operations are performed - - * - Free the timers - * - Free beacon buffers - * - Free private structures - * - Free adapter structure - */ -static int mwifiex_unregister(struct mwifiex_adapter *adapter) -{ - s32 i; - - if (adapter->if_ops.cleanup_if) - adapter->if_ops.cleanup_if(adapter); - - del_timer_sync(&adapter->cmd_timer); - - /* Free private structures */ - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - mwifiex_free_curr_bcn(adapter->priv[i]); - kfree(adapter->priv[i]); - } - } - - vfree(adapter->chan_stats); - kfree(adapter); - return 0; -} - -void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) -{ - unsigned long flags; - - spin_lock_irqsave(&adapter->main_proc_lock, flags); - if (adapter->mwifiex_processing) { - adapter->more_task_flag = true; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - } else { - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - queue_work(adapter->workqueue, &adapter->main_work); - } -} -EXPORT_SYMBOL_GPL(mwifiex_queue_main_work); - -static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) -{ - unsigned long flags; - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - if (adapter->rx_processing) { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - } else { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - queue_work(adapter->rx_workqueue, &adapter->rx_work); - } -} - -static int mwifiex_process_rx(struct mwifiex_adapter *adapter) -{ - unsigned long flags; - struct sk_buff *skb; - struct mwifiex_rxinfo *rx_info; - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - if (adapter->rx_processing || adapter->rx_locked) { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - goto exit_rx_proc; - } else { - adapter->rx_processing = true; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - } - - /* Check for Rx data */ - while ((skb = skb_dequeue(&adapter->rx_data_q))) { - atomic_dec(&adapter->rx_pending); - if ((adapter->delay_main_work || - adapter->iface_type == MWIFIEX_USB) && - (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) { - if (adapter->if_ops.submit_rem_rx_urbs) - adapter->if_ops.submit_rem_rx_urbs(adapter); - adapter->delay_main_work = false; - mwifiex_queue_main_work(adapter); - } - rx_info = MWIFIEX_SKB_RXCB(skb); - if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) { - if (adapter->if_ops.deaggr_pkt) - adapter->if_ops.deaggr_pkt(adapter, skb); - dev_kfree_skb_any(skb); - } else { - mwifiex_handle_rx_packet(adapter, skb); - } - } - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - adapter->rx_processing = false; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - -exit_rx_proc: - return 0; -} - -/* - * The main process. - * - * This function is the main procedure of the driver and handles various driver - * operations. It runs in a loop and provides the core functionalities. - * - * The main responsibilities of this function are - - * - Ensure concurrency control - * - Handle pending interrupts and call interrupt handlers - * - Wake up the card if required - * - Handle command responses and call response handlers - * - Handle events and call event handlers - * - Execute pending commands - * - Transmit pending data packets - */ -int mwifiex_main_process(struct mwifiex_adapter *adapter) -{ - int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&adapter->main_proc_lock, flags); - - /* Check if already processing */ - if (adapter->mwifiex_processing || adapter->main_locked) { - adapter->more_task_flag = true; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - goto exit_main_proc; - } else { - adapter->mwifiex_processing = true; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - } -process_start: - do { - if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) || - (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)) - break; - - /* For non-USB interfaces, If we process interrupts first, it - * would increase RX pending even further. Avoid this by - * checking if rx_pending has crossed high threshold and - * schedule rx work queue and then process interrupts. - * For USB interface, there are no interrupts. We already have - * HIGH_RX_PENDING check in usb.c - */ - if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING && - adapter->iface_type != MWIFIEX_USB) { - adapter->delay_main_work = true; - mwifiex_queue_rx_work(adapter); - break; - } - - /* Handle pending interrupt if any */ - if (adapter->int_status) { - if (adapter->hs_activated) - mwifiex_process_hs_config(adapter); - if (adapter->if_ops.process_int_status) - adapter->if_ops.process_int_status(adapter); - } - - if (adapter->rx_work_enabled && adapter->data_received) - mwifiex_queue_rx_work(adapter); - - /* Need to wake up the card ? */ - if ((adapter->ps_state == PS_STATE_SLEEP) && - (adapter->pm_wakeup_card_req && - !adapter->pm_wakeup_fw_try) && - (is_command_pending(adapter) || - !skb_queue_empty(&adapter->tx_data_q) || - !mwifiex_bypass_txlist_empty(adapter) || - !mwifiex_wmm_lists_empty(adapter))) { - adapter->pm_wakeup_fw_try = true; - mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3)); - adapter->if_ops.wakeup(adapter); - continue; - } - - if (IS_CARD_RX_RCVD(adapter)) { - adapter->data_received = false; - adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); - if (adapter->ps_state == PS_STATE_SLEEP) - adapter->ps_state = PS_STATE_AWAKE; - } else { - /* We have tried to wakeup the card already */ - if (adapter->pm_wakeup_fw_try) - break; - if (adapter->ps_state != PS_STATE_AWAKE) - break; - if (adapter->tx_lock_flag) { - if (adapter->iface_type == MWIFIEX_USB) { - if (!adapter->usb_mc_setup) - break; - } else - break; - } - - if ((!adapter->scan_chan_gap_enabled && - adapter->scan_processing) || adapter->data_sent || - mwifiex_is_tdls_chan_switching - (mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_STA)) || - (mwifiex_wmm_lists_empty(adapter) && - mwifiex_bypass_txlist_empty(adapter) && - skb_queue_empty(&adapter->tx_data_q))) { - if (adapter->cmd_sent || adapter->curr_cmd || - !mwifiex_is_send_cmd_allowed - (mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_STA)) || - (!is_command_pending(adapter))) - break; - } - } - - /* Check for event */ - if (adapter->event_received) { - adapter->event_received = false; - mwifiex_process_event(adapter); - } - - /* Check for Cmd Resp */ - if (adapter->cmd_resp_received) { - adapter->cmd_resp_received = false; - mwifiex_process_cmdresp(adapter); - - /* call mwifiex back when init_fw is done */ - if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) { - adapter->hw_status = MWIFIEX_HW_STATUS_READY; - mwifiex_init_fw_complete(adapter); - } - } - - /* Check if we need to confirm Sleep Request - received previously */ - if (adapter->ps_state == PS_STATE_PRE_SLEEP) { - if (!adapter->cmd_sent && !adapter->curr_cmd) - mwifiex_check_ps_cond(adapter); - } - - /* * The ps_state may have been changed during processing of - * Sleep Request event. - */ - if ((adapter->ps_state == PS_STATE_SLEEP) || - (adapter->ps_state == PS_STATE_PRE_SLEEP) || - (adapter->ps_state == PS_STATE_SLEEP_CFM)) { - continue; - } - - if (adapter->tx_lock_flag) { - if (adapter->iface_type == MWIFIEX_USB) { - if (!adapter->usb_mc_setup) - continue; - } else - continue; - } - - if (!adapter->cmd_sent && !adapter->curr_cmd && - mwifiex_is_send_cmd_allowed - (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { - if (mwifiex_exec_next_cmd(adapter) == -1) { - ret = -1; - break; - } - } - - /** If USB Multi channel setup ongoing, - * wait for ready to tx data. - */ - if (adapter->iface_type == MWIFIEX_USB && - adapter->usb_mc_setup) - continue; - - if ((adapter->scan_chan_gap_enabled || - !adapter->scan_processing) && - !adapter->data_sent && - !skb_queue_empty(&adapter->tx_data_q)) { - mwifiex_process_tx_queue(adapter); - if (adapter->hs_activated) { - adapter->is_hs_configured = false; - mwifiex_hs_activated_event - (mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - false); - } - } - - if ((adapter->scan_chan_gap_enabled || - !adapter->scan_processing) && - !adapter->data_sent && - !mwifiex_bypass_txlist_empty(adapter) && - !mwifiex_is_tdls_chan_switching - (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { - mwifiex_process_bypass_tx(adapter); - if (adapter->hs_activated) { - adapter->is_hs_configured = false; - mwifiex_hs_activated_event - (mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - false); - } - } - - if ((adapter->scan_chan_gap_enabled || - !adapter->scan_processing) && - !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter) && - !mwifiex_is_tdls_chan_switching - (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { - mwifiex_wmm_process_tx(adapter); - if (adapter->hs_activated) { - adapter->is_hs_configured = false; - mwifiex_hs_activated_event - (mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - false); - } - } - - if (adapter->delay_null_pkt && !adapter->cmd_sent && - !adapter->curr_cmd && !is_command_pending(adapter) && - (mwifiex_wmm_lists_empty(adapter) && - mwifiex_bypass_txlist_empty(adapter) && - skb_queue_empty(&adapter->tx_data_q))) { - if (!mwifiex_send_null_packet - (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), - MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | - MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) { - adapter->delay_null_pkt = false; - adapter->ps_state = PS_STATE_SLEEP; - } - break; - } - } while (true); - - spin_lock_irqsave(&adapter->main_proc_lock, flags); - if (adapter->more_task_flag) { - adapter->more_task_flag = false; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - goto process_start; - } - adapter->mwifiex_processing = false; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - -exit_main_proc: - if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) - mwifiex_shutdown_drv(adapter); - return ret; -} -EXPORT_SYMBOL_GPL(mwifiex_main_process); - -/* - * This function frees the adapter structure. - * - * Additionally, this closes the netlink socket, frees the timers - * and private structures. - */ -static void mwifiex_free_adapter(struct mwifiex_adapter *adapter) -{ - if (!adapter) { - pr_err("%s: adapter is NULL\n", __func__); - return; - } - - mwifiex_unregister(adapter); - pr_debug("info: %s: free adapter\n", __func__); -} - -/* - * This function cancels all works in the queue and destroys - * the main workqueue. - */ -static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) -{ - flush_workqueue(adapter->workqueue); - destroy_workqueue(adapter->workqueue); - adapter->workqueue = NULL; - - if (adapter->rx_workqueue) { - flush_workqueue(adapter->rx_workqueue); - destroy_workqueue(adapter->rx_workqueue); - adapter->rx_workqueue = NULL; - } -} - -/* - * This function gets firmware and initializes it. - * - * The main initialization steps followed are - - * - Download the correct firmware to card - * - Issue the init commands to firmware - */ -static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) -{ - int ret; - char fmt[64]; - struct mwifiex_private *priv; - struct mwifiex_adapter *adapter = context; - struct mwifiex_fw_image fw; - struct semaphore *sem = adapter->card_sem; - bool init_failed = false; - struct wireless_dev *wdev; - - if (!firmware) { - mwifiex_dbg(adapter, ERROR, - "Failed to get firmware %s\n", adapter->fw_name); - goto err_dnld_fw; - } - - memset(&fw, 0, sizeof(struct mwifiex_fw_image)); - adapter->firmware = firmware; - fw.fw_buf = (u8 *) adapter->firmware->data; - fw.fw_len = adapter->firmware->size; - - if (adapter->if_ops.dnld_fw) - ret = adapter->if_ops.dnld_fw(adapter, &fw); - else - ret = mwifiex_dnld_fw(adapter, &fw); - if (ret == -1) - goto err_dnld_fw; - - mwifiex_dbg(adapter, MSG, "WLAN FW is active\n"); - - if (cal_data_cfg) { - if ((request_firmware(&adapter->cal_data, cal_data_cfg, - adapter->dev)) < 0) - mwifiex_dbg(adapter, ERROR, - "Cal data request_firmware() failed\n"); - } - - /* enable host interrupt after fw dnld is successful */ - if (adapter->if_ops.enable_int) { - if (adapter->if_ops.enable_int(adapter)) - goto err_dnld_fw; - } - - adapter->init_wait_q_woken = false; - ret = mwifiex_init_fw(adapter); - if (ret == -1) { - goto err_init_fw; - } else if (!ret) { - adapter->hw_status = MWIFIEX_HW_STATUS_READY; - goto done; - } - /* Wait for mwifiex_init to complete */ - wait_event_interruptible(adapter->init_wait_q, - adapter->init_wait_q_woken); - if (adapter->hw_status != MWIFIEX_HW_STATUS_READY) - goto err_init_fw; - - priv = adapter->priv[MWIFIEX_BSS_ROLE_STA]; - if (mwifiex_register_cfg80211(adapter)) { - mwifiex_dbg(adapter, ERROR, - "cannot register with cfg80211\n"); - goto err_init_fw; - } - - if (mwifiex_init_channel_scan_gap(adapter)) { - mwifiex_dbg(adapter, ERROR, - "could not init channel stats table\n"); - goto err_init_fw; - } - - if (driver_mode) { - driver_mode &= MWIFIEX_DRIVER_MODE_BITMASK; - driver_mode |= MWIFIEX_DRIVER_MODE_STA; - } - - rtnl_lock(); - /* Create station interface by default */ - wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM, - NL80211_IFTYPE_STATION, NULL, NULL); - if (IS_ERR(wdev)) { - mwifiex_dbg(adapter, ERROR, - "cannot create default STA interface\n"); - rtnl_unlock(); - goto err_add_intf; - } - - if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) { - wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM, - NL80211_IFTYPE_AP, NULL, NULL); - if (IS_ERR(wdev)) { - mwifiex_dbg(adapter, ERROR, - "cannot create AP interface\n"); - rtnl_unlock(); - goto err_add_intf; - } - } - - if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) { - wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM, - NL80211_IFTYPE_P2P_CLIENT, NULL, - NULL); - if (IS_ERR(wdev)) { - mwifiex_dbg(adapter, ERROR, - "cannot create p2p client interface\n"); - rtnl_unlock(); - goto err_add_intf; - } - } - rtnl_unlock(); - - mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); - mwifiex_dbg(adapter, MSG, "driver_version = %s\n", fmt); - goto done; - -err_add_intf: - wiphy_unregister(adapter->wiphy); - wiphy_free(adapter->wiphy); -err_init_fw: - if (adapter->if_ops.disable_int) - adapter->if_ops.disable_int(adapter); -err_dnld_fw: - mwifiex_dbg(adapter, ERROR, - "info: %s: unregister device\n", __func__); - if (adapter->if_ops.unregister_dev) - adapter->if_ops.unregister_dev(adapter); - - if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { - pr_debug("info: %s: shutdown mwifiex\n", __func__); - adapter->init_wait_q_woken = false; - - if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) - wait_event_interruptible(adapter->init_wait_q, - adapter->init_wait_q_woken); - } - adapter->surprise_removed = true; - mwifiex_terminate_workqueue(adapter); - init_failed = true; -done: - if (adapter->cal_data) { - release_firmware(adapter->cal_data); - adapter->cal_data = NULL; - } - if (adapter->firmware) { - release_firmware(adapter->firmware); - adapter->firmware = NULL; - } - if (init_failed) - mwifiex_free_adapter(adapter); - up(sem); - return; -} - -/* - * This function initializes the hardware and gets firmware. - */ -static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter) -{ - int ret; - - ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name, - adapter->dev, GFP_KERNEL, adapter, - mwifiex_fw_dpc); - if (ret < 0) - mwifiex_dbg(adapter, ERROR, - "request_firmware_nowait error %d\n", ret); - return ret; -} - -/* - * CFG802.11 network device handler for open. - * - * Starts the data queue. - */ -static int -mwifiex_open(struct net_device *dev) -{ - netif_carrier_off(dev); - - return 0; -} - -/* - * CFG802.11 network device handler for close. - */ -static int -mwifiex_close(struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (priv->scan_request) { - mwifiex_dbg(priv->adapter, INFO, - "aborting scan on ndo_stop\n"); - cfg80211_scan_done(priv->scan_request, 1); - priv->scan_request = NULL; - priv->scan_aborting = true; - } - - return 0; -} - -static bool -mwifiex_bypass_tx_queue(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; - - if (ntohs(eth_hdr->h_proto) == ETH_P_PAE || - mwifiex_is_skb_mgmt_frame(skb) || - (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && - ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - (ntohs(eth_hdr->h_proto) == ETH_P_TDLS))) { - mwifiex_dbg(priv->adapter, DATA, - "bypass txqueue; eth type %#x, mgmt %d\n", - ntohs(eth_hdr->h_proto), - mwifiex_is_skb_mgmt_frame(skb)); - return true; - } - - return false; -} -/* - * Add buffer into wmm tx queue and queue work to transmit it. - */ -int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb) -{ - struct netdev_queue *txq; - int index = mwifiex_1d_to_wmm_queue[skb->priority]; - - if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) { - txq = netdev_get_tx_queue(priv->netdev, index); - if (!netif_tx_queue_stopped(txq)) { - netif_tx_stop_queue(txq); - mwifiex_dbg(priv->adapter, DATA, - "stop queue: %d\n", index); - } - } - - if (mwifiex_bypass_tx_queue(priv, skb)) { - atomic_inc(&priv->adapter->tx_pending); - atomic_inc(&priv->adapter->bypass_tx_pending); - mwifiex_wmm_add_buf_bypass_txqueue(priv, skb); - } else { - atomic_inc(&priv->adapter->tx_pending); - mwifiex_wmm_add_buf_txqueue(priv, skb); - } - - mwifiex_queue_main_work(priv->adapter); - - return 0; -} - -struct sk_buff * -mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, - struct sk_buff *skb, u8 flag, u64 *cookie) -{ - struct sk_buff *orig_skb = skb; - struct mwifiex_txinfo *tx_info, *orig_tx_info; - - skb = skb_clone(skb, GFP_ATOMIC); - if (skb) { - unsigned long flags; - int id; - - spin_lock_irqsave(&priv->ack_status_lock, flags); - id = idr_alloc(&priv->ack_status_frames, orig_skb, - 1, 0xff, GFP_ATOMIC); - spin_unlock_irqrestore(&priv->ack_status_lock, flags); - - if (id >= 0) { - tx_info = MWIFIEX_SKB_TXCB(skb); - tx_info->ack_frame_id = id; - tx_info->flags |= flag; - orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb); - orig_tx_info->ack_frame_id = id; - orig_tx_info->flags |= flag; - - if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie) - orig_tx_info->cookie = *cookie; - - } else if (skb_shared(skb)) { - kfree_skb(orig_skb); - } else { - kfree_skb(skb); - skb = orig_skb; - } - } else { - /* couldn't clone -- lose tx status ... */ - skb = orig_skb; - } - - return skb; -} - -/* - * CFG802.11 network device handler for data transmission. - */ -static int -mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct sk_buff *new_skb; - struct mwifiex_txinfo *tx_info; - bool multicast; - - mwifiex_dbg(priv->adapter, DATA, - "data: %lu BSS(%d-%d): Data <= kernel\n", - jiffies, priv->bss_type, priv->bss_num); - - if (priv->adapter->surprise_removed) { - kfree_skb(skb); - priv->stats.tx_dropped++; - return 0; - } - if (!skb->len || (skb->len > ETH_FRAME_LEN)) { - mwifiex_dbg(priv->adapter, ERROR, - "Tx: bad skb len %d\n", skb->len); - kfree_skb(skb); - priv->stats.tx_dropped++; - return 0; - } - if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { - mwifiex_dbg(priv->adapter, DATA, - "data: Tx: insufficient skb headroom %d\n", - skb_headroom(skb)); - /* Insufficient skb headroom - allocate a new skb */ - new_skb = - skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); - if (unlikely(!new_skb)) { - mwifiex_dbg(priv->adapter, ERROR, - "Tx: cannot alloca new_skb\n"); - kfree_skb(skb); - priv->stats.tx_dropped++; - return 0; - } - kfree_skb(skb); - skb = new_skb; - mwifiex_dbg(priv->adapter, INFO, - "info: new skb headroomd %d\n", - skb_headroom(skb)); - } - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - tx_info->pkt_len = skb->len; - - multicast = is_multicast_ether_addr(skb->data); - - if (unlikely(!multicast && skb->sk && - skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS && - priv->adapter->fw_api_ver == MWIFIEX_FW_V15)) - skb = mwifiex_clone_skb_for_tx_status(priv, - skb, - MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL); - - /* Record the current time the packet was queued; used to - * determine the amount of time the packet was queued in - * the driver before it was sent to the firmware. - * The delay is then sent along with the packet to the - * firmware for aggregate delay calculation for stats and - * MSDU lifetime expiry. - */ - __net_timestamp(skb); - - if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - priv->bss_type == MWIFIEX_BSS_TYPE_STA && - !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) { - if (priv->adapter->auto_tdls && priv->check_tdls_tx) - mwifiex_tdls_check_tx(priv, skb); - } - - mwifiex_queue_tx_pkt(priv, skb); - - return 0; -} - -/* - * CFG802.11 network device handler for setting MAC address. - */ -static int -mwifiex_set_mac_address(struct net_device *dev, void *addr) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct sockaddr *hw_addr = addr; - int ret; - - memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN); - - /* Send request to firmware */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS, - HostCmd_ACT_GEN_SET, 0, NULL, true); - - if (!ret) - memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN); - else - mwifiex_dbg(priv->adapter, ERROR, - "set mac address failed: ret=%d\n", ret); - - memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); - - return ret; -} - -/* - * CFG802.11 network device handler for setting multicast list. - */ -static void mwifiex_set_multicast_list(struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_multicast_list mcast_list; - - if (dev->flags & IFF_PROMISC) { - mcast_list.mode = MWIFIEX_PROMISC_MODE; - } else if (dev->flags & IFF_ALLMULTI || - netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) { - mcast_list.mode = MWIFIEX_ALL_MULTI_MODE; - } else { - mcast_list.mode = MWIFIEX_MULTICAST_MODE; - mcast_list.num_multicast_addr = - mwifiex_copy_mcast_addr(&mcast_list, dev); - } - mwifiex_request_set_multicast_list(priv, &mcast_list); -} - -/* - * CFG802.11 network device handler for transmission timeout. - */ -static void -mwifiex_tx_timeout(struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - priv->num_tx_timeout++; - priv->tx_timeout_cnt++; - mwifiex_dbg(priv->adapter, ERROR, - "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n", - jiffies, priv->tx_timeout_cnt, priv->bss_type, - priv->bss_num); - mwifiex_set_trans_start(dev); - - if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD && - priv->adapter->if_ops.card_reset) { - mwifiex_dbg(priv->adapter, ERROR, - "tx_timeout_cnt exceeds threshold.\t" - "Triggering card reset!\n"); - priv->adapter->if_ops.card_reset(priv->adapter); - } -} - -void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = adapter->card; - struct mwifiex_private *priv; - u16 tx_buf_size; - int i, ret; - - card->mc_resync_flag = true; - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - if (atomic_read(&card->port[i].tx_data_urb_pending)) { - mwifiex_dbg(adapter, WARN, "pending data urb in sys\n"); - return; - } - } - - card->mc_resync_flag = false; - tx_buf_size = 0xffff; - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, - HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false); - if (ret) - mwifiex_dbg(adapter, ERROR, - "send reconfig tx buf size cmd err\n"); -} -EXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync); - -void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter) -{ - void *p; - char drv_version[64]; - struct usb_card_rec *cardp; - struct sdio_mmc_card *sdio_card; - struct mwifiex_private *priv; - int i, idx; - struct netdev_queue *txq; - struct mwifiex_debug_info *debug_info; - - if (adapter->drv_info_dump) { - vfree(adapter->drv_info_dump); - adapter->drv_info_dump = NULL; - adapter->drv_info_size = 0; - } - - mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump start===\n"); - - adapter->drv_info_dump = vzalloc(MWIFIEX_DRV_INFO_SIZE_MAX); - - if (!adapter->drv_info_dump) - return; - - p = (char *)(adapter->drv_info_dump); - p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); - - mwifiex_drv_get_driver_version(adapter, drv_version, - sizeof(drv_version) - 1); - p += sprintf(p, "driver_version = %s\n", drv_version); - - if (adapter->iface_type == MWIFIEX_USB) { - cardp = (struct usb_card_rec *)adapter->card; - p += sprintf(p, "tx_cmd_urb_pending = %d\n", - atomic_read(&cardp->tx_cmd_urb_pending)); - p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n", - atomic_read(&cardp->port[0].tx_data_urb_pending)); - p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n", - atomic_read(&cardp->port[1].tx_data_urb_pending)); - p += sprintf(p, "rx_cmd_urb_pending = %d\n", - atomic_read(&cardp->rx_cmd_urb_pending)); - p += sprintf(p, "rx_data_urb_pending = %d\n", - atomic_read(&cardp->rx_data_urb_pending)); - } - - p += sprintf(p, "tx_pending = %d\n", - atomic_read(&adapter->tx_pending)); - p += sprintf(p, "rx_pending = %d\n", - atomic_read(&adapter->rx_pending)); - - if (adapter->iface_type == MWIFIEX_SDIO) { - sdio_card = (struct sdio_mmc_card *)adapter->card; - p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n", - sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port); - p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", - sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port); - } - - for (i = 0; i < adapter->priv_num; i++) { - if (!adapter->priv[i] || !adapter->priv[i]->netdev) - continue; - priv = adapter->priv[i]; - p += sprintf(p, "\n[interface : \"%s\"]\n", - priv->netdev->name); - p += sprintf(p, "wmm_tx_pending[0] = %d\n", - atomic_read(&priv->wmm_tx_pending[0])); - p += sprintf(p, "wmm_tx_pending[1] = %d\n", - atomic_read(&priv->wmm_tx_pending[1])); - p += sprintf(p, "wmm_tx_pending[2] = %d\n", - atomic_read(&priv->wmm_tx_pending[2])); - p += sprintf(p, "wmm_tx_pending[3] = %d\n", - atomic_read(&priv->wmm_tx_pending[3])); - p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ? - "Disconnected" : "Connected"); - p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev) - ? "on" : "off")); - for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) { - txq = netdev_get_tx_queue(priv->netdev, idx); - p += sprintf(p, "tx queue %d:%s ", idx, - netif_tx_queue_stopped(txq) ? - "stopped" : "started"); - } - p += sprintf(p, "\n%s: num_tx_timeout = %d\n", - priv->netdev->name, priv->num_tx_timeout); - } - - if (adapter->iface_type == MWIFIEX_SDIO) { - p += sprintf(p, "\n=== SDIO register dump===\n"); - if (adapter->if_ops.reg_dump) - p += adapter->if_ops.reg_dump(adapter, p); - } - - p += sprintf(p, "\n=== more debug information\n"); - debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL); - if (debug_info) { - for (i = 0; i < adapter->priv_num; i++) { - if (!adapter->priv[i] || !adapter->priv[i]->netdev) - continue; - priv = adapter->priv[i]; - mwifiex_get_debug_info(priv, debug_info); - p += mwifiex_debug_info_to_buffer(priv, p, debug_info); - break; - } - kfree(debug_info); - } - - adapter->drv_info_size = p - adapter->drv_info_dump; - mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump end===\n"); -} -EXPORT_SYMBOL_GPL(mwifiex_drv_info_dump); - -void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter) -{ - u8 idx, *dump_data, *fw_dump_ptr; - u32 dump_len; - - dump_len = (strlen("========Start dump driverinfo========\n") + - adapter->drv_info_size + - strlen("\n========End dump========\n")); - - for (idx = 0; idx < adapter->num_mem_types; idx++) { - struct memory_type_mapping *entry = - &adapter->mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - dump_len += (strlen("========Start dump ") + - strlen(entry->mem_name) + - strlen("========\n") + - (entry->mem_size + 1) + - strlen("\n========End dump========\n")); - } - } - - dump_data = vzalloc(dump_len + 1); - if (!dump_data) - goto done; - - fw_dump_ptr = dump_data; - - /* Dump all the memory data into single file, a userspace script will - * be used to split all the memory data to multiple files - */ - mwifiex_dbg(adapter, MSG, - "== mwifiex dump information to /sys/class/devcoredump start"); - - strcpy(fw_dump_ptr, "========Start dump driverinfo========\n"); - fw_dump_ptr += strlen("========Start dump driverinfo========\n"); - memcpy(fw_dump_ptr, adapter->drv_info_dump, adapter->drv_info_size); - fw_dump_ptr += adapter->drv_info_size; - strcpy(fw_dump_ptr, "\n========End dump========\n"); - fw_dump_ptr += strlen("\n========End dump========\n"); - - for (idx = 0; idx < adapter->num_mem_types; idx++) { - struct memory_type_mapping *entry = - &adapter->mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - strcpy(fw_dump_ptr, "========Start dump "); - fw_dump_ptr += strlen("========Start dump "); - - strcpy(fw_dump_ptr, entry->mem_name); - fw_dump_ptr += strlen(entry->mem_name); - - strcpy(fw_dump_ptr, "========\n"); - fw_dump_ptr += strlen("========\n"); - - memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size); - fw_dump_ptr += entry->mem_size; - - strcpy(fw_dump_ptr, "\n========End dump========\n"); - fw_dump_ptr += strlen("\n========End dump========\n"); - } - } - - /* device dump data will be free in device coredump release function - * after 5 min - */ - dev_coredumpv(adapter->dev, dump_data, dump_len, GFP_KERNEL); - mwifiex_dbg(adapter, MSG, - "== mwifiex dump information to /sys/class/devcoredump end"); - -done: - for (idx = 0; idx < adapter->num_mem_types; idx++) { - struct memory_type_mapping *entry = - &adapter->mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - } - - if (adapter->drv_info_dump) { - vfree(adapter->drv_info_dump); - adapter->drv_info_dump = NULL; - adapter->drv_info_size = 0; - } -} -EXPORT_SYMBOL_GPL(mwifiex_upload_device_dump); - -/* - * CFG802.11 network device handler for statistics retrieval. - */ -static struct net_device_stats *mwifiex_get_stats(struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - return &priv->stats; -} - -static u16 -mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb, - void *accel_priv, select_queue_fallback_t fallback) -{ - skb->priority = cfg80211_classify8021d(skb, NULL); - return mwifiex_1d_to_wmm_queue[skb->priority]; -} - -/* Network device handlers */ -static const struct net_device_ops mwifiex_netdev_ops = { - .ndo_open = mwifiex_open, - .ndo_stop = mwifiex_close, - .ndo_start_xmit = mwifiex_hard_start_xmit, - .ndo_set_mac_address = mwifiex_set_mac_address, - .ndo_validate_addr = eth_validate_addr, - .ndo_tx_timeout = mwifiex_tx_timeout, - .ndo_get_stats = mwifiex_get_stats, - .ndo_set_rx_mode = mwifiex_set_multicast_list, - .ndo_select_queue = mwifiex_netdev_select_wmm_queue, -}; - -/* - * This function initializes the private structure parameters. - * - * The following wait queues are initialized - - * - IOCTL wait queue - * - Command wait queue - * - Statistics wait queue - * - * ...and the following default parameters are set - - * - Current key index : Set to 0 - * - Rate index : Set to auto - * - Media connected : Set to disconnected - * - Adhoc link sensed : Set to false - * - Nick name : Set to null - * - Number of Tx timeout : Set to 0 - * - Device address : Set to current address - * - Rx histogram statistc : Set to 0 - * - * In addition, the CFG80211 work queue is also created. - */ -void mwifiex_init_priv_params(struct mwifiex_private *priv, - struct net_device *dev) -{ - dev->netdev_ops = &mwifiex_netdev_ops; - dev->destructor = free_netdev; - /* Initialize private structure */ - priv->current_key_index = 0; - priv->media_connected = false; - memset(priv->mgmt_ie, 0, - sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX); - priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK; - priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK; - priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK; - priv->gen_idx = MWIFIEX_AUTO_IDX_MASK; - priv->num_tx_timeout = 0; - ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr); - memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || - GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL); - if (priv->hist_data) - mwifiex_hist_data_reset(priv); - } -} - -/* - * This function check if command is pending. - */ -int is_command_pending(struct mwifiex_adapter *adapter) -{ - unsigned long flags; - int is_cmd_pend_q_empty; - - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); - is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); - - return !is_cmd_pend_q_empty; -} - -/* - * This is the RX work queue function. - * - * It handles the RX operations. - */ -static void mwifiex_rx_work_queue(struct work_struct *work) -{ - struct mwifiex_adapter *adapter = - container_of(work, struct mwifiex_adapter, rx_work); - - if (adapter->surprise_removed) - return; - mwifiex_process_rx(adapter); -} - -/* - * This is the main work queue function. - * - * It handles the main process, which in turn handles the complete - * driver operations. - */ -static void mwifiex_main_work_queue(struct work_struct *work) -{ - struct mwifiex_adapter *adapter = - container_of(work, struct mwifiex_adapter, main_work); - - if (adapter->surprise_removed) - return; - mwifiex_main_process(adapter); -} - -/* - * This function adds the card. - * - * This function follows the following major steps to set up the device - - * - Initialize software. This includes probing the card, registering - * the interface operations table, and allocating/initializing the - * adapter structure - * - Set up the netlink socket - * - Create and start the main work queue - * - Register the device - * - Initialize firmware and hardware - * - Add logical interfaces - */ -int -mwifiex_add_card(void *card, struct semaphore *sem, - struct mwifiex_if_ops *if_ops, u8 iface_type) -{ - struct mwifiex_adapter *adapter; - - if (down_interruptible(sem)) - goto exit_sem_err; - - if (mwifiex_register(card, if_ops, (void **)&adapter)) { - pr_err("%s: software init failed\n", __func__); - goto err_init_sw; - } - - adapter->iface_type = iface_type; - adapter->card_sem = sem; - - adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; - adapter->surprise_removed = false; - init_waitqueue_head(&adapter->init_wait_q); - adapter->is_suspended = false; - adapter->hs_activated = false; - init_waitqueue_head(&adapter->hs_activate_wait_q); - init_waitqueue_head(&adapter->cmd_wait_q.wait); - adapter->cmd_wait_q.status = 0; - adapter->scan_wait_q_woken = false; - - if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB) { - adapter->rx_work_enabled = true; - pr_notice("rx work enabled, cpus %d\n", num_possible_cpus()); - } - - adapter->workqueue = - alloc_workqueue("MWIFIEX_WORK_QUEUE", - WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); - if (!adapter->workqueue) - goto err_kmalloc; - - INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); - - if (adapter->rx_work_enabled) { - adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE", - WQ_HIGHPRI | - WQ_MEM_RECLAIM | - WQ_UNBOUND, 1); - if (!adapter->rx_workqueue) - goto err_kmalloc; - - INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue); - } - - /* Register the device. Fill up the private data structure with relevant - information from the card. */ - if (adapter->if_ops.register_dev(adapter)) { - pr_err("%s: failed to register mwifiex device\n", __func__); - goto err_registerdev; - } - - if (mwifiex_init_hw_fw(adapter)) { - pr_err("%s: firmware init failed\n", __func__); - goto err_init_fw; - } - - return 0; - -err_init_fw: - pr_debug("info: %s: unregister device\n", __func__); - if (adapter->if_ops.unregister_dev) - adapter->if_ops.unregister_dev(adapter); - if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { - pr_debug("info: %s: shutdown mwifiex\n", __func__); - adapter->init_wait_q_woken = false; - - if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) - wait_event_interruptible(adapter->init_wait_q, - adapter->init_wait_q_woken); - } -err_registerdev: - adapter->surprise_removed = true; - mwifiex_terminate_workqueue(adapter); -err_kmalloc: - mwifiex_free_adapter(adapter); - -err_init_sw: - up(sem); - -exit_sem_err: - return -1; -} -EXPORT_SYMBOL_GPL(mwifiex_add_card); - -/* - * This function removes the card. - * - * This function follows the following major steps to remove the device - - * - Stop data traffic - * - Shutdown firmware - * - Remove the logical interfaces - * - Terminate the work queue - * - Unregister the device - * - Free the adapter structure - */ -int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) -{ - struct mwifiex_private *priv = NULL; - int i; - - if (down_interruptible(sem)) - goto exit_sem_err; - - if (!adapter) - goto exit_remove; - - /* We can no longer handle interrupts once we start doing the teardown - * below. */ - if (adapter->if_ops.disable_int) - adapter->if_ops.disable_int(adapter); - - adapter->surprise_removed = true; - - mwifiex_terminate_workqueue(adapter); - - /* Stop data */ - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv && priv->netdev) { - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - } - } - - mwifiex_dbg(adapter, CMD, - "cmd: calling mwifiex_shutdown_drv...\n"); - adapter->init_wait_q_woken = false; - - if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) - wait_event_interruptible(adapter->init_wait_q, - adapter->init_wait_q_woken); - mwifiex_dbg(adapter, CMD, - "cmd: mwifiex_shutdown_drv done\n"); - if (atomic_read(&adapter->rx_pending) || - atomic_read(&adapter->tx_pending) || - atomic_read(&adapter->cmd_pending)) { - mwifiex_dbg(adapter, ERROR, - "rx_pending=%d, tx_pending=%d,\t" - "cmd_pending=%d\n", - atomic_read(&adapter->rx_pending), - atomic_read(&adapter->tx_pending), - atomic_read(&adapter->cmd_pending)); - } - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - - if (!priv) - continue; - - rtnl_lock(); - if (priv->netdev && - priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) - mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev); - rtnl_unlock(); - } - - wiphy_unregister(adapter->wiphy); - wiphy_free(adapter->wiphy); - - /* Unregister device */ - mwifiex_dbg(adapter, INFO, - "info: unregister device\n"); - if (adapter->if_ops.unregister_dev) - adapter->if_ops.unregister_dev(adapter); - /* Free adapter structure */ - mwifiex_dbg(adapter, INFO, - "info: free adapter\n"); - mwifiex_free_adapter(adapter); - -exit_remove: - up(sem); -exit_sem_err: - return 0; -} -EXPORT_SYMBOL_GPL(mwifiex_remove_card); - -void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, - const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - if (!adapter->dev || !(adapter->debug_mask & mask)) - return; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - dev_info(adapter->dev, "%pV", &vaf); - - va_end(args); -} -EXPORT_SYMBOL_GPL(_mwifiex_dbg); - -/* - * This function initializes the module. - * - * The debug FS is also initialized if configured. - */ -static int -mwifiex_init_module(void) -{ -#ifdef CONFIG_DEBUG_FS - mwifiex_debugfs_init(); -#endif - return 0; -} - -/* - * This function cleans up the module. - * - * The debug FS is removed if available. - */ -static void -mwifiex_cleanup_module(void) -{ -#ifdef CONFIG_DEBUG_FS - mwifiex_debugfs_remove(); -#endif -} - -module_init(mwifiex_init_module); -module_exit(mwifiex_cleanup_module); - -MODULE_AUTHOR("Marvell International Ltd."); -MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h deleted file mode 100644 index 3959f1c97f4e..000000000000 --- a/drivers/net/wireless/mwifiex/main.h +++ /dev/null @@ -1,1579 +0,0 @@ -/* - * Marvell Wireless LAN device driver: major data structures and prototypes - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_MAIN_H_ -#define _MWIFIEX_MAIN_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "pcie.h" -#include "usb.h" -#include "sdio.h" - -extern const char driver_version[]; - -struct mwifiex_adapter; -struct mwifiex_private; - -enum { - MWIFIEX_ASYNC_CMD, - MWIFIEX_SYNC_CMD -}; - -#define MWIFIEX_DRIVER_MODE_STA BIT(0) -#define MWIFIEX_DRIVER_MODE_UAP BIT(1) -#define MWIFIEX_DRIVER_MODE_P2P BIT(2) -#define MWIFIEX_DRIVER_MODE_BITMASK (BIT(0) | BIT(1) | BIT(2)) - -#define MWIFIEX_MAX_AP 64 - -#define MWIFIEX_MAX_PKTS_TXQ 16 - -#define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ) - -#define MWIFIEX_TIMER_10S 10000 -#define MWIFIEX_TIMER_1S 1000 - -#define MAX_TX_PENDING 100 -#define LOW_TX_PENDING 80 - -#define HIGH_RX_PENDING 50 -#define LOW_RX_PENDING 20 - -#define MWIFIEX_UPLD_SIZE (2312) - -#define MAX_EVENT_SIZE 2048 - -#define ARP_FILTER_MAX_BUF_SIZE 68 - -#define MWIFIEX_KEY_BUFFER_SIZE 16 -#define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10 -#define MWIFIEX_MAX_REGION_CODE 7 - -#define DEFAULT_BCN_AVG_FACTOR 8 -#define DEFAULT_DATA_AVG_FACTOR 8 - -#define FIRST_VALID_CHANNEL 0xff -#define DEFAULT_AD_HOC_CHANNEL 6 -#define DEFAULT_AD_HOC_CHANNEL_A 36 - -#define DEFAULT_BCN_MISS_TIMEOUT 5 - -#define MAX_SCAN_BEACON_BUFFER 8000 - -#define SCAN_BEACON_ENTRY_PAD 6 - -#define MWIFIEX_PASSIVE_SCAN_CHAN_TIME 110 -#define MWIFIEX_ACTIVE_SCAN_CHAN_TIME 30 -#define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME 30 -#define MWIFIEX_DEF_SCAN_CHAN_GAP_TIME 50 - -#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) - -#define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S) - -#define RSN_GTK_OUI_OFFSET 2 - -#define MWIFIEX_OUI_NOT_PRESENT 0 -#define MWIFIEX_OUI_PRESENT 1 - -#define PKT_TYPE_MGMT 0xE5 - -/* - * Do not check for data_received for USB, as data_received - * is handled in mwifiex_usb_recv for USB - */ -#define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \ - adapter->event_received || \ - adapter->data_received) - -#define MWIFIEX_TYPE_CMD 1 -#define MWIFIEX_TYPE_DATA 0 -#define MWIFIEX_TYPE_AGGR_DATA 10 -#define MWIFIEX_TYPE_EVENT 3 - -#define MAX_BITMAP_RATES_SIZE 18 - -#define MAX_CHANNEL_BAND_BG 14 -#define MAX_CHANNEL_BAND_A 165 - -#define MAX_FREQUENCY_BAND_BG 2484 - -#define MWIFIEX_EVENT_HEADER_LEN 4 -#define MWIFIEX_UAP_EVENT_EXTRA_HEADER 2 - -#define MWIFIEX_TYPE_LEN 4 -#define MWIFIEX_USB_TYPE_CMD 0xF00DFACE -#define MWIFIEX_USB_TYPE_DATA 0xBEADC0DE -#define MWIFIEX_USB_TYPE_EVENT 0xBEEFFACE - -/* Threshold for tx_timeout_cnt before we trigger a card reset */ -#define TX_TIMEOUT_THRESHOLD 6 - -#define MWIFIEX_DRV_INFO_SIZE_MAX 0x40000 - -/* Address alignment */ -#define MWIFIEX_ALIGN_ADDR(p, a) (((long)(p) + (a) - 1) & ~((a) - 1)) - -/** - *enum mwifiex_debug_level - marvell wifi debug level - */ -enum MWIFIEX_DEBUG_LEVEL { - MWIFIEX_DBG_MSG = 0x00000001, - MWIFIEX_DBG_FATAL = 0x00000002, - MWIFIEX_DBG_ERROR = 0x00000004, - MWIFIEX_DBG_DATA = 0x00000008, - MWIFIEX_DBG_CMD = 0x00000010, - MWIFIEX_DBG_EVENT = 0x00000020, - MWIFIEX_DBG_INTR = 0x00000040, - MWIFIEX_DBG_IOCTL = 0x00000080, - - MWIFIEX_DBG_MPA_D = 0x00008000, - MWIFIEX_DBG_DAT_D = 0x00010000, - MWIFIEX_DBG_CMD_D = 0x00020000, - MWIFIEX_DBG_EVT_D = 0x00040000, - MWIFIEX_DBG_FW_D = 0x00080000, - MWIFIEX_DBG_IF_D = 0x00100000, - - MWIFIEX_DBG_ENTRY = 0x10000000, - MWIFIEX_DBG_WARN = 0x20000000, - MWIFIEX_DBG_INFO = 0x40000000, - MWIFIEX_DBG_DUMP = 0x80000000, - - MWIFIEX_DBG_ANY = 0xffffffff -}; - -#define MWIFIEX_DEFAULT_DEBUG_MASK (MWIFIEX_DBG_MSG | \ - MWIFIEX_DBG_FATAL | \ - MWIFIEX_DBG_ERROR) - -__printf(3, 4) -void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, - const char *fmt, ...); -#define mwifiex_dbg(adapter, mask, fmt, ...) \ - _mwifiex_dbg(adapter, MWIFIEX_DBG_##mask, fmt, ##__VA_ARGS__) - -#define DEBUG_DUMP_DATA_MAX_LEN 128 -#define mwifiex_dbg_dump(adapter, dbg_mask, str, buf, len) \ -do { \ - if ((adapter)->debug_mask & MWIFIEX_DBG_##dbg_mask) \ - print_hex_dump(KERN_DEBUG, str, \ - DUMP_PREFIX_OFFSET, 16, 1, \ - buf, len, false); \ -} while (0) - -struct mwifiex_dbg { - u32 num_cmd_host_to_card_failure; - u32 num_cmd_sleep_cfm_host_to_card_failure; - u32 num_tx_host_to_card_failure; - u32 num_event_deauth; - u32 num_event_disassoc; - u32 num_event_link_lost; - u32 num_cmd_deauth; - u32 num_cmd_assoc_success; - u32 num_cmd_assoc_failure; - u32 num_tx_timeout; - u16 timeout_cmd_id; - u16 timeout_cmd_act; - u16 last_cmd_id[DBG_CMD_NUM]; - u16 last_cmd_act[DBG_CMD_NUM]; - u16 last_cmd_index; - u16 last_cmd_resp_id[DBG_CMD_NUM]; - u16 last_cmd_resp_index; - u16 last_event[DBG_CMD_NUM]; - u16 last_event_index; -}; - -enum MWIFIEX_HARDWARE_STATUS { - MWIFIEX_HW_STATUS_READY, - MWIFIEX_HW_STATUS_INITIALIZING, - MWIFIEX_HW_STATUS_INIT_DONE, - MWIFIEX_HW_STATUS_RESET, - MWIFIEX_HW_STATUS_CLOSING, - MWIFIEX_HW_STATUS_NOT_READY -}; - -enum MWIFIEX_802_11_POWER_MODE { - MWIFIEX_802_11_POWER_MODE_CAM, - MWIFIEX_802_11_POWER_MODE_PSP -}; - -struct mwifiex_tx_param { - u32 next_pkt_len; -}; - -enum MWIFIEX_PS_STATE { - PS_STATE_AWAKE, - PS_STATE_PRE_SLEEP, - PS_STATE_SLEEP_CFM, - PS_STATE_SLEEP -}; - -enum mwifiex_iface_type { - MWIFIEX_SDIO, - MWIFIEX_PCIE, - MWIFIEX_USB -}; - -struct mwifiex_add_ba_param { - u32 tx_win_size; - u32 rx_win_size; - u32 timeout; - u8 tx_amsdu; - u8 rx_amsdu; -}; - -struct mwifiex_tx_aggr { - u8 ampdu_user; - u8 ampdu_ap; - u8 amsdu; -}; - -enum mwifiex_ba_status { - BA_SETUP_NONE = 0, - BA_SETUP_INPROGRESS, - BA_SETUP_COMPLETE -}; - -struct mwifiex_ra_list_tbl { - struct list_head list; - struct sk_buff_head skb_head; - u8 ra[ETH_ALEN]; - u32 is_11n_enabled; - u16 max_amsdu; - u16 ba_pkt_count; - u8 ba_packet_thr; - enum mwifiex_ba_status ba_status; - u8 amsdu_in_ampdu; - u16 total_pkt_count; - bool tdls_link; - bool tx_paused; -}; - -struct mwifiex_tid_tbl { - struct list_head ra_list; -}; - -#define WMM_HIGHEST_PRIORITY 7 -#define HIGH_PRIO_TID 7 -#define LOW_PRIO_TID 0 - -struct mwifiex_wmm_desc { - struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID]; - u32 packets_out[MAX_NUM_TID]; - u32 pkts_paused[MAX_NUM_TID]; - /* spin lock to protect ra_list */ - spinlock_t ra_list_spinlock; - struct mwifiex_wmm_ac_status ac_status[IEEE80211_NUM_ACS]; - enum mwifiex_wmm_ac_e ac_down_graded_vals[IEEE80211_NUM_ACS]; - u32 drv_pkt_delay_max; - u8 queue_priority[IEEE80211_NUM_ACS]; - u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */ - /* Number of transmit packets queued */ - atomic_t tx_pkts_queued; - /* Tracks highest priority with a packet queued */ - atomic_t highest_queued_prio; -}; - -struct mwifiex_802_11_security { - u8 wpa_enabled; - u8 wpa2_enabled; - u8 wapi_enabled; - u8 wapi_key_on; - u8 wep_enabled; - u32 authentication_mode; - u8 is_authtype_auto; - u32 encryption_mode; -}; - -struct ieee_types_header { - u8 element_id; - u8 len; -} __packed; - -struct ieee_types_vendor_specific { - struct ieee_types_vendor_header vend_hdr; - u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)]; -} __packed; - -struct ieee_types_generic { - struct ieee_types_header ieee_hdr; - u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)]; -} __packed; - -struct ieee_types_bss_co_2040 { - struct ieee_types_header ieee_hdr; - u8 bss_2040co; -} __packed; - -struct ieee_types_extcap { - struct ieee_types_header ieee_hdr; - u8 ext_capab[8]; -} __packed; - -struct ieee_types_vht_cap { - struct ieee_types_header ieee_hdr; - struct ieee80211_vht_cap vhtcap; -} __packed; - -struct ieee_types_vht_oper { - struct ieee_types_header ieee_hdr; - struct ieee80211_vht_operation vhtoper; -} __packed; - -struct ieee_types_aid { - struct ieee_types_header ieee_hdr; - u16 aid; -} __packed; - -struct mwifiex_bssdescriptor { - u8 mac_address[ETH_ALEN]; - struct cfg80211_ssid ssid; - u32 privacy; - s32 rssi; - u32 channel; - u32 freq; - u16 beacon_period; - u8 erp_flags; - u32 bss_mode; - u8 supported_rates[MWIFIEX_SUPPORTED_RATES]; - u8 data_rates[MWIFIEX_SUPPORTED_RATES]; - /* Network band. - * BAND_B(0x01): 'b' band - * BAND_G(0x02): 'g' band - * BAND_A(0X04): 'a' band - */ - u16 bss_band; - u64 fw_tsf; - u64 timestamp; - union ieee_types_phy_param_set phy_param_set; - union ieee_types_ss_param_set ss_param_set; - u16 cap_info_bitmap; - struct ieee_types_wmm_parameter wmm_ie; - u8 disable_11n; - struct ieee80211_ht_cap *bcn_ht_cap; - u16 ht_cap_offset; - struct ieee80211_ht_operation *bcn_ht_oper; - u16 ht_info_offset; - u8 *bcn_bss_co_2040; - u16 bss_co_2040_offset; - u8 *bcn_ext_cap; - u16 ext_cap_offset; - struct ieee80211_vht_cap *bcn_vht_cap; - u16 vht_cap_offset; - struct ieee80211_vht_operation *bcn_vht_oper; - u16 vht_info_offset; - struct ieee_types_oper_mode_ntf *oper_mode; - u16 oper_mode_offset; - u8 disable_11ac; - struct ieee_types_vendor_specific *bcn_wpa_ie; - u16 wpa_offset; - struct ieee_types_generic *bcn_rsn_ie; - u16 rsn_offset; - struct ieee_types_generic *bcn_wapi_ie; - u16 wapi_offset; - u8 *beacon_buf; - u32 beacon_buf_size; - u8 sensed_11h; - u8 local_constraint; - u8 chan_sw_ie_present; -}; - -struct mwifiex_current_bss_params { - struct mwifiex_bssdescriptor bss_descriptor; - u8 wmm_enabled; - u8 wmm_uapsd_enabled; - u8 band; - u32 num_of_rates; - u8 data_rates[MWIFIEX_SUPPORTED_RATES]; -}; - -struct mwifiex_sleep_params { - u16 sp_error; - u16 sp_offset; - u16 sp_stable_time; - u8 sp_cal_control; - u8 sp_ext_sleep_clk; - u16 sp_reserved; -}; - -struct mwifiex_sleep_period { - u16 period; - u16 reserved; -}; - -struct mwifiex_wep_key { - u32 length; - u32 key_index; - u32 key_length; - u8 key_material[MWIFIEX_KEY_BUFFER_SIZE]; -}; - -#define MAX_REGION_CHANNEL_NUM 2 - -struct mwifiex_chan_freq_power { - u16 channel; - u32 freq; - u16 max_tx_power; - u8 unsupported; -}; - -enum state_11d_t { - DISABLE_11D = 0, - ENABLE_11D = 1, -}; - -#define MWIFIEX_MAX_TRIPLET_802_11D 83 - -struct mwifiex_802_11d_domain_reg { - u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - u8 no_of_triplet; - struct ieee80211_country_ie_triplet - triplet[MWIFIEX_MAX_TRIPLET_802_11D]; -}; - -struct mwifiex_vendor_spec_cfg_ie { - u16 mask; - u16 flag; - u8 ie[MWIFIEX_MAX_VSIE_LEN]; -}; - -struct wps { - u8 session_enable; -}; - -struct mwifiex_roc_cfg { - u64 cookie; - struct ieee80211_channel chan; -}; - -#define MWIFIEX_FW_DUMP_IDX 0xff -#define MWIFIEX_DRV_INFO_IDX 20 -#define FW_DUMP_MAX_NAME_LEN 8 -#define FW_DUMP_HOST_READY 0xEE -#define FW_DUMP_DONE 0xFF -#define FW_DUMP_READ_DONE 0xFE - -struct memory_type_mapping { - u8 mem_name[FW_DUMP_MAX_NAME_LEN]; - u8 *mem_ptr; - u32 mem_size; - u8 done_flag; -}; - -enum rdwr_status { - RDWR_STATUS_SUCCESS = 0, - RDWR_STATUS_FAILURE = 1, - RDWR_STATUS_DONE = 2 -}; - -enum mwifiex_iface_work_flags { - MWIFIEX_IFACE_WORK_DEVICE_DUMP, - MWIFIEX_IFACE_WORK_CARD_RESET, -}; - -struct mwifiex_private { - struct mwifiex_adapter *adapter; - u8 bss_type; - u8 bss_role; - u8 bss_priority; - u8 bss_num; - u8 bss_started; - u8 frame_type; - u8 curr_addr[ETH_ALEN]; - u8 media_connected; - u8 port_open; - u8 usb_port; - u32 num_tx_timeout; - /* track consecutive timeout */ - u8 tx_timeout_cnt; - struct net_device *netdev; - struct net_device_stats stats; - u16 curr_pkt_filter; - u32 bss_mode; - u32 pkt_tx_ctrl; - u16 tx_power_level; - u8 max_tx_power_level; - u8 min_tx_power_level; - u8 tx_rate; - u8 tx_htinfo; - u8 rxpd_htinfo; - u8 rxpd_rate; - u16 rate_bitmap; - u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; - u32 data_rate; - u8 is_data_rate_auto; - u16 bcn_avg_factor; - u16 data_avg_factor; - s16 data_rssi_last; - s16 data_nf_last; - s16 data_rssi_avg; - s16 data_nf_avg; - s16 bcn_rssi_last; - s16 bcn_nf_last; - s16 bcn_rssi_avg; - s16 bcn_nf_avg; - struct mwifiex_bssdescriptor *attempted_bss_desc; - struct cfg80211_ssid prev_ssid; - u8 prev_bssid[ETH_ALEN]; - struct mwifiex_current_bss_params curr_bss_params; - u16 beacon_period; - u8 dtim_period; - u16 listen_interval; - u16 atim_window; - u8 adhoc_channel; - u8 adhoc_is_link_sensed; - u8 adhoc_state; - struct mwifiex_802_11_security sec_info; - struct mwifiex_wep_key wep_key[NUM_WEP_KEYS]; - u16 wep_key_curr_index; - u8 wpa_ie[256]; - u8 wpa_ie_len; - u8 wpa_is_gtk_set; - struct host_cmd_ds_802_11_key_material aes_key; - struct host_cmd_ds_802_11_key_material_v2 aes_key_v2; - u8 wapi_ie[256]; - u8 wapi_ie_len; - u8 *wps_ie; - u8 wps_ie_len; - u8 wmm_required; - u8 wmm_enabled; - u8 wmm_qosinfo; - struct mwifiex_wmm_desc wmm; - atomic_t wmm_tx_pending[IEEE80211_NUM_ACS]; - struct list_head sta_list; - /* spin lock for associated station/TDLS peers list */ - spinlock_t sta_list_spinlock; - struct list_head auto_tdls_list; - /* spin lock for auto TDLS peer list */ - spinlock_t auto_tdls_lock; - struct list_head tx_ba_stream_tbl_ptr; - /* spin lock for tx_ba_stream_tbl_ptr queue */ - spinlock_t tx_ba_stream_tbl_lock; - struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID]; - struct mwifiex_add_ba_param add_ba_param; - u16 rx_seq[MAX_NUM_TID]; - u8 tos_to_tid_inv[MAX_NUM_TID]; - struct list_head rx_reorder_tbl_ptr; - /* spin lock for rx_reorder_tbl_ptr queue */ - spinlock_t rx_reorder_tbl_lock; - /* spin lock for Rx packets */ - spinlock_t rx_pkt_lock; - -#define MWIFIEX_ASSOC_RSP_BUF_SIZE 500 - u8 assoc_rsp_buf[MWIFIEX_ASSOC_RSP_BUF_SIZE]; - u32 assoc_rsp_size; - -#define MWIFIEX_GENIE_BUF_SIZE 256 - u8 gen_ie_buf[MWIFIEX_GENIE_BUF_SIZE]; - u8 gen_ie_buf_len; - - struct mwifiex_vendor_spec_cfg_ie vs_ie[MWIFIEX_MAX_VSIE_NUM]; - -#define MWIFIEX_ASSOC_TLV_BUF_SIZE 256 - u8 assoc_tlv_buf[MWIFIEX_ASSOC_TLV_BUF_SIZE]; - u8 assoc_tlv_buf_len; - - u8 *curr_bcn_buf; - u32 curr_bcn_size; - /* spin lock for beacon buffer */ - spinlock_t curr_bcn_buf_lock; - struct wireless_dev wdev; - struct mwifiex_chan_freq_power cfp; - char version_str[128]; -#ifdef CONFIG_DEBUG_FS - struct dentry *dfs_dev_dir; -#endif - u16 current_key_index; - struct semaphore async_sem; - struct cfg80211_scan_request *scan_request; - u8 cfg_bssid[6]; - struct wps wps; - u8 scan_block; - s32 cqm_rssi_thold; - u32 cqm_rssi_hyst; - u8 subsc_evt_rssi_state; - struct mwifiex_ds_misc_subsc_evt async_subsc_evt_storage; - struct mwifiex_ie mgmt_ie[MAX_MGMT_IE_INDEX]; - u16 beacon_idx; - u16 proberesp_idx; - u16 assocresp_idx; - u16 gen_idx; - u8 ap_11n_enabled; - u8 ap_11ac_enabled; - u32 mgmt_frame_mask; - struct mwifiex_roc_cfg roc_cfg; - bool scan_aborting; - u8 csa_chan; - unsigned long csa_expire_time; - u8 del_list_idx; - bool hs2_enabled; - struct mwifiex_uap_bss_param bss_cfg; - struct cfg80211_chan_def bss_chandef; - struct station_parameters *sta_params; - struct sk_buff_head tdls_txq; - u8 check_tdls_tx; - struct timer_list auto_tdls_timer; - bool auto_tdls_timer_active; - struct idr ack_status_frames; - /* spin lock for ack status */ - spinlock_t ack_status_lock; - /** rx histogram data */ - struct mwifiex_histogram_data *hist_data; - struct cfg80211_chan_def dfs_chandef; - struct workqueue_struct *dfs_cac_workqueue; - struct delayed_work dfs_cac_work; - struct timer_list dfs_chan_switch_timer; - struct workqueue_struct *dfs_chan_sw_workqueue; - struct delayed_work dfs_chan_sw_work; - struct cfg80211_beacon_data beacon_after; - struct mwifiex_11h_intf_state state_11h; - struct mwifiex_ds_mem_rw mem_rw; - struct sk_buff_head bypass_txq; - struct mwifiex_user_scan_chan hidden_chan[MWIFIEX_USER_SCAN_CHAN_MAX]; -}; - - -struct mwifiex_tx_ba_stream_tbl { - struct list_head list; - int tid; - u8 ra[ETH_ALEN]; - enum mwifiex_ba_status ba_status; - u8 amsdu; -}; - -struct mwifiex_rx_reorder_tbl; - -struct reorder_tmr_cnxt { - struct timer_list timer; - struct mwifiex_rx_reorder_tbl *ptr; - struct mwifiex_private *priv; - u8 timer_is_set; -}; - -struct mwifiex_rx_reorder_tbl { - struct list_head list; - int tid; - u8 ta[ETH_ALEN]; - int init_win; - int start_win; - int win_size; - void **rx_reorder_ptr; - struct reorder_tmr_cnxt timer_context; - u8 amsdu; - u8 flags; -}; - -struct mwifiex_bss_prio_node { - struct list_head list; - struct mwifiex_private *priv; -}; - -struct mwifiex_bss_prio_tbl { - struct list_head bss_prio_head; - /* spin lock for bss priority */ - spinlock_t bss_prio_lock; - struct mwifiex_bss_prio_node *bss_prio_cur; -}; - -struct cmd_ctrl_node { - struct list_head list; - struct mwifiex_private *priv; - u32 cmd_oid; - u32 cmd_flag; - struct sk_buff *cmd_skb; - struct sk_buff *resp_skb; - void *data_buf; - u32 wait_q_enabled; - struct sk_buff *skb; - u8 *condition; - u8 cmd_wait_q_woken; -}; - -struct mwifiex_bss_priv { - u8 band; - u64 fw_tsf; -}; - -struct mwifiex_tdls_capab { - __le16 capab; - u8 rates[32]; - u8 rates_len; - u8 qos_info; - u8 coex_2040; - u16 aid; - struct ieee80211_ht_cap ht_capb; - struct ieee80211_ht_operation ht_oper; - struct ieee_types_extcap extcap; - struct ieee_types_generic rsn_ie; - struct ieee80211_vht_cap vhtcap; - struct ieee80211_vht_operation vhtoper; -}; - -struct mwifiex_station_stats { - u64 last_rx; - s8 rssi; - u64 rx_bytes; - u64 tx_bytes; - u32 rx_packets; - u32 tx_packets; - u32 tx_failed; - u8 last_tx_rate; - u8 last_tx_htinfo; -}; - -/* This is AP/TDLS specific structure which stores information - * about associated/peer STA - */ -struct mwifiex_sta_node { - struct list_head list; - u8 mac_addr[ETH_ALEN]; - u8 is_wmm_enabled; - u8 is_11n_enabled; - u8 is_11ac_enabled; - u8 ampdu_sta[MAX_NUM_TID]; - u16 rx_seq[MAX_NUM_TID]; - u16 max_amsdu; - u8 tdls_status; - struct mwifiex_tdls_capab tdls_cap; - struct mwifiex_station_stats stats; - u8 tx_pause; -}; - -struct mwifiex_auto_tdls_peer { - struct list_head list; - u8 mac_addr[ETH_ALEN]; - u8 tdls_status; - int rssi; - long rssi_jiffies; - u8 failure_count; - u8 do_discover; - u8 do_setup; -}; - -struct mwifiex_if_ops { - int (*init_if) (struct mwifiex_adapter *); - void (*cleanup_if) (struct mwifiex_adapter *); - int (*check_fw_status) (struct mwifiex_adapter *, u32); - int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); - int (*register_dev) (struct mwifiex_adapter *); - void (*unregister_dev) (struct mwifiex_adapter *); - int (*enable_int) (struct mwifiex_adapter *); - void (*disable_int) (struct mwifiex_adapter *); - int (*process_int_status) (struct mwifiex_adapter *); - int (*host_to_card) (struct mwifiex_adapter *, u8, struct sk_buff *, - struct mwifiex_tx_param *); - int (*wakeup) (struct mwifiex_adapter *); - int (*wakeup_complete) (struct mwifiex_adapter *); - - /* Interface specific functions */ - void (*update_mp_end_port) (struct mwifiex_adapter *, u16); - void (*cleanup_mpa_buf) (struct mwifiex_adapter *); - int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *); - int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *); - int (*init_fw_port) (struct mwifiex_adapter *); - int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); - void (*card_reset) (struct mwifiex_adapter *); - int (*reg_dump)(struct mwifiex_adapter *, char *); - void (*device_dump)(struct mwifiex_adapter *); - int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); - void (*iface_work)(struct work_struct *work); - void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter); - void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *); - void (*multi_port_resync)(struct mwifiex_adapter *); - bool (*is_port_ready)(struct mwifiex_private *); -}; - -struct mwifiex_adapter { - u8 iface_type; - unsigned int debug_mask; - struct mwifiex_iface_comb iface_limit; - struct mwifiex_iface_comb curr_iface_comb; - struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM]; - u8 priv_num; - const struct firmware *firmware; - char fw_name[32]; - int winner; - struct device *dev; - struct wiphy *wiphy; - u8 perm_addr[ETH_ALEN]; - bool surprise_removed; - u32 fw_release_number; - u16 init_wait_q_woken; - wait_queue_head_t init_wait_q; - void *card; - struct mwifiex_if_ops if_ops; - atomic_t bypass_tx_pending; - atomic_t rx_pending; - atomic_t tx_pending; - atomic_t cmd_pending; - struct workqueue_struct *workqueue; - struct work_struct main_work; - struct workqueue_struct *rx_workqueue; - struct work_struct rx_work; - struct workqueue_struct *dfs_workqueue; - struct work_struct dfs_work; - bool rx_work_enabled; - bool rx_processing; - bool delay_main_work; - bool rx_locked; - bool main_locked; - struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM]; - /* spin lock for init/shutdown */ - spinlock_t mwifiex_lock; - /* spin lock for main process */ - spinlock_t main_proc_lock; - u32 mwifiex_processing; - u8 more_task_flag; - u16 tx_buf_size; - u16 curr_tx_buf_size; - /* sdio single port rx aggregation capability */ - bool host_disable_sdio_rx_aggr; - bool sdio_rx_aggr_enable; - u16 sdio_rx_block_size; - u32 ioport; - enum MWIFIEX_HARDWARE_STATUS hw_status; - u16 number_of_antenna; - u32 fw_cap_info; - /* spin lock for interrupt handling */ - spinlock_t int_lock; - u8 int_status; - u32 event_cause; - struct sk_buff *event_skb; - u8 upld_buf[MWIFIEX_UPLD_SIZE]; - u8 data_sent; - u8 cmd_sent; - u8 cmd_resp_received; - u8 event_received; - u8 data_received; - u16 seq_num; - struct cmd_ctrl_node *cmd_pool; - struct cmd_ctrl_node *curr_cmd; - /* spin lock for command */ - spinlock_t mwifiex_cmd_lock; - u8 is_cmd_timedout; - u16 last_init_cmd; - struct timer_list cmd_timer; - struct list_head cmd_free_q; - /* spin lock for cmd_free_q */ - spinlock_t cmd_free_q_lock; - struct list_head cmd_pending_q; - /* spin lock for cmd_pending_q */ - spinlock_t cmd_pending_q_lock; - struct list_head scan_pending_q; - /* spin lock for scan_pending_q */ - spinlock_t scan_pending_q_lock; - /* spin lock for RX processing routine */ - spinlock_t rx_proc_lock; - struct sk_buff_head tx_data_q; - atomic_t tx_queued; - u32 scan_processing; - u16 region_code; - struct mwifiex_802_11d_domain_reg domain_reg; - u16 scan_probes; - u32 scan_mode; - u16 specific_scan_time; - u16 active_scan_time; - u16 passive_scan_time; - u16 scan_chan_gap_time; - u8 fw_bands; - u8 adhoc_start_band; - u8 config_bands; - struct mwifiex_chan_scan_param_set *scan_channels; - u8 tx_lock_flag; - struct mwifiex_sleep_params sleep_params; - struct mwifiex_sleep_period sleep_period; - u16 ps_mode; - u32 ps_state; - u8 need_to_wakeup; - u16 multiple_dtim; - u16 local_listen_interval; - u16 null_pkt_interval; - struct sk_buff *sleep_cfm; - u16 bcn_miss_time_out; - u16 adhoc_awake_period; - u8 is_deep_sleep; - u8 delay_null_pkt; - u16 delay_to_ps; - u16 enhanced_ps_mode; - u8 pm_wakeup_card_req; - u16 gen_null_pkt; - u16 pps_uapsd_mode; - u32 pm_wakeup_fw_try; - struct timer_list wakeup_timer; - u8 is_hs_configured; - struct mwifiex_hs_config_param hs_cfg; - u8 hs_activated; - u16 hs_activate_wait_q_woken; - wait_queue_head_t hs_activate_wait_q; - bool is_suspended; - bool hs_enabling; - u8 event_body[MAX_EVENT_SIZE]; - u32 hw_dot_11n_dev_cap; - u8 hw_dev_mcs_support; - u8 user_dev_mcs_support; - u8 adhoc_11n_enabled; - u8 sec_chan_offset; - struct mwifiex_dbg dbg; - u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE]; - u32 arp_filter_size; - struct mwifiex_wait_queue cmd_wait_q; - u8 scan_wait_q_woken; - spinlock_t queue_lock; /* lock for tx queues */ - u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - u16 max_mgmt_ie_index; - const struct firmware *cal_data; - struct device_node *dt_node; - - /* 11AC */ - u32 is_hw_11ac_capable; - u32 hw_dot_11ac_dev_cap; - u32 hw_dot_11ac_mcs_support; - u32 usr_dot_11ac_dev_cap_bg; - u32 usr_dot_11ac_dev_cap_a; - u32 usr_dot_11ac_mcs_support; - - atomic_t pending_bridged_pkts; - struct semaphore *card_sem; - bool ext_scan; - u8 fw_api_ver; - u8 key_api_major_ver, key_api_minor_ver; - struct memory_type_mapping *mem_type_mapping_tbl; - u8 num_mem_types; - void *drv_info_dump; - u32 drv_info_size; - bool scan_chan_gap_enabled; - struct sk_buff_head rx_data_q; - struct mwifiex_chan_stats *chan_stats; - u32 num_in_chan_stats; - int survey_idx; - bool auto_tdls; - u8 coex_scan; - u8 coex_min_scan_time; - u8 coex_max_scan_time; - u8 coex_win_size; - u8 coex_tx_win_size; - u8 coex_rx_win_size; - bool drcs_enabled; - u8 active_scan_triggered; - bool usb_mc_status; - bool usb_mc_setup; -}; - -void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter); - -int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); - -void mwifiex_set_trans_start(struct net_device *dev); - -void mwifiex_stop_net_dev_queue(struct net_device *netdev, - struct mwifiex_adapter *adapter); - -void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, - struct mwifiex_adapter *adapter); - -int mwifiex_init_priv(struct mwifiex_private *priv); -void mwifiex_free_priv(struct mwifiex_private *priv); - -int mwifiex_init_fw(struct mwifiex_adapter *adapter); - -int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter); - -int mwifiex_shutdown_drv(struct mwifiex_adapter *adapter); - -int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter); - -int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *); - -int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb); - -int mwifiex_process_mgmt_packet(struct mwifiex_private *priv, - struct sk_buff *skb); - -int mwifiex_process_event(struct mwifiex_adapter *adapter); - -int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node); - -int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, - u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync); - -void mwifiex_cmd_timeout_func(unsigned long function_context); - -int mwifiex_get_debug_info(struct mwifiex_private *, - struct mwifiex_debug_info *); - -int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter); -int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter); -void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter); -void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter); - -void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node); -void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node); - -void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node, - u32 addtail); - -int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter); -int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter); -int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, - struct sk_buff *skb); -int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, - struct mwifiex_tx_param *tx_param); -int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags); -int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb, int aggr, int status); -void mwifiex_clean_txrx(struct mwifiex_private *priv); -u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv); -void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter); -void mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *, u8 *, - u32); -int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, uint16_t ps_bitmap, - struct mwifiex_ds_auto_ds *auto_ds); -int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - struct mwifiex_ds_pm_cfg *pm_cfg); -void mwifiex_process_hs_config(struct mwifiex_adapter *adapter); -void mwifiex_hs_activated_event(struct mwifiex_private *priv, - u8 activated); -int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, - int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg); -int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int mwifiex_process_rx_packet(struct mwifiex_private *priv, - struct sk_buff *skb); -int mwifiex_sta_prepare_cmd(struct mwifiex_private *, uint16_t cmd_no, - u16 cmd_action, u32 cmd_oid, - void *data_buf, void *cmd_buf); -int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, - u16 cmd_action, u32 cmd_oid, - void *data_buf, void *cmd_buf); -int mwifiex_process_sta_cmdresp(struct mwifiex_private *, u16 cmdresp_no, - struct host_cmd_ds_command *resp); -int mwifiex_process_sta_rx_packet(struct mwifiex_private *, - struct sk_buff *skb); -int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, - struct sk_buff *skb); -int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, - struct sk_buff *skb); -int mwifiex_process_sta_event(struct mwifiex_private *); -int mwifiex_process_uap_event(struct mwifiex_private *); -void mwifiex_delete_all_station_list(struct mwifiex_private *priv); -void mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, - const u8 *ra_addr); -void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); -void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb); -int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta, bool init); -int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, - struct mwifiex_scan_cmd_config *scan_cfg); -void mwifiex_queue_scan_cmd(struct mwifiex_private *priv, - struct cmd_ctrl_node *cmd_node); -int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -s32 mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2); -int mwifiex_associate(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -void mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason); -u8 mwifiex_band_to_radio_type(u8 band); -int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac); -void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter); -int mwifiex_adhoc_start(struct mwifiex_private *priv, - struct cfg80211_ssid *adhoc_ssid); -int mwifiex_adhoc_join(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct cfg80211_ssid *req_ssid); -int mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd); -struct mwifiex_chan_freq_power *mwifiex_get_cfp(struct mwifiex_private *priv, - u8 band, u16 channel, u32 freq); -u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, - u8 index, u8 ht_info); -u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, - u8 index, u8 ht_info); -u32 mwifiex_find_freq_from_band_chan(u8, u8); -int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask, - u8 **buffer); -u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, - u8 *rates); -u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates); -u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv, - u8 *rates, u8 radio_type); -u8 mwifiex_is_rate_auto(struct mwifiex_private *priv); -extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE]; -void mwifiex_save_curr_bcn(struct mwifiex_private *priv); -void mwifiex_free_curr_bcn(struct mwifiex_private *priv); -int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd); -int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int is_command_pending(struct mwifiex_adapter *adapter); -void mwifiex_init_priv_params(struct mwifiex_private *priv, - struct net_device *dev); -int mwifiex_set_secure_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_config, - struct cfg80211_ap_settings *params); -void mwifiex_set_ht_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params); -void mwifiex_set_vht_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params); -void mwifiex_set_tpc_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params); -void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params); -void mwifiex_set_vht_width(struct mwifiex_private *priv, - enum nl80211_chan_width width, - bool ap_11ac_disable); -void -mwifiex_set_wmm_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params); -void mwifiex_set_ba_params(struct mwifiex_private *priv); - -void mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *pmadapter); -void mwifiex_bt_coex_wlan_param_update_event(struct mwifiex_private *priv, - struct sk_buff *event_skb); - -void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv); -int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - void *data_buf); -int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, - void *buf); - -/* - * This function checks if the queuing is RA based or not. - */ -static inline u8 -mwifiex_queuing_ra_based(struct mwifiex_private *priv) -{ - /* - * Currently we assume if we are in Infra, then DA=RA. This might not be - * true in the future - */ - if ((priv->bss_mode == NL80211_IFTYPE_STATION) && - (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)) - return false; - - return true; -} - -/* - * This function copies rates. - */ -static inline u32 -mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len) -{ - int i; - - for (i = 0; i < len && src[i]; i++, pos++) { - if (pos >= MWIFIEX_SUPPORTED_RATES) - break; - dest[pos] = src[i]; - } - - return pos; -} - -/* - * This function returns the correct private structure pointer based - * upon the BSS type and BSS number. - */ -static inline struct mwifiex_private * -mwifiex_get_priv_by_id(struct mwifiex_adapter *adapter, - u8 bss_num, u8 bss_type) -{ - int i; - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - if ((adapter->priv[i]->bss_num == bss_num) && - (adapter->priv[i]->bss_type == bss_type)) - break; - } - } - return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); -} - -/* - * This function returns the first available private structure pointer - * based upon the BSS role. - */ -static inline struct mwifiex_private * -mwifiex_get_priv(struct mwifiex_adapter *adapter, - enum mwifiex_bss_role bss_role) -{ - int i; - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - if (bss_role == MWIFIEX_BSS_ROLE_ANY || - GET_BSS_ROLE(adapter->priv[i]) == bss_role) - break; - } - } - - return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); -} - -/* - * This function returns the first available unused private structure pointer. - */ -static inline struct mwifiex_private * -mwifiex_get_unused_priv(struct mwifiex_adapter *adapter) -{ - int i; - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - if (adapter->priv[i]->bss_mode == - NL80211_IFTYPE_UNSPECIFIED) - break; - } - } - - return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); -} - -/* - * This function returns the driver private structure of a network device. - */ -static inline struct mwifiex_private * -mwifiex_netdev_get_priv(struct net_device *dev) -{ - return (struct mwifiex_private *) (*(unsigned long *) netdev_priv(dev)); -} - -/* - * This function checks if a skb holds a management frame. - */ -static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb) -{ - return (le32_to_cpu(*(__le32 *)skb->data) == PKT_TYPE_MGMT); -} - -/* This function retrieves channel closed for operation by Channel - * Switch Announcement. - */ -static inline u8 -mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv) -{ - if (!priv->csa_chan) - return 0; - - /* Clear csa channel, if DFS channel move time has passed */ - if (time_after(jiffies, priv->csa_expire_time)) { - priv->csa_chan = 0; - priv->csa_expire_time = 0; - } - - return priv->csa_chan; -} - -static inline u8 mwifiex_is_any_intf_active(struct mwifiex_private *priv) -{ - struct mwifiex_private *priv_num; - int i; - - for (i = 0; i < priv->adapter->priv_num; i++) { - priv_num = priv->adapter->priv[i]; - if (priv_num) { - if ((GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_UAP && - priv_num->bss_started) || - (GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_STA && - priv_num->media_connected)) - return 1; - } - } - - return 0; -} - -static inline u8 mwifiex_is_tdls_link_setup(u8 status) -{ - switch (status) { - case TDLS_SETUP_COMPLETE: - case TDLS_CHAN_SWITCHING: - case TDLS_IN_BASE_CHAN: - case TDLS_IN_OFF_CHAN: - return true; - default: - break; - } - - return false; -} - -int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, - u32 func_init_shutdown); -int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8); -int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *); - -void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version, - int maxlen); -int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, - struct mwifiex_multicast_list *mcast_list); -int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, - struct net_device *dev); -int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_queued); -int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, - struct cfg80211_ssid *req_ssid); -int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type); -int mwifiex_enable_hs(struct mwifiex_adapter *adapter); -int mwifiex_disable_auto_ds(struct mwifiex_private *priv); -int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate); -int mwifiex_request_scan(struct mwifiex_private *priv, - struct cfg80211_ssid *req_ssid); -int mwifiex_scan_networks(struct mwifiex_private *priv, - const struct mwifiex_user_scan_cfg *user_scan_in); -int mwifiex_set_radio(struct mwifiex_private *priv, u8 option); - -int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, - const u8 *key, int key_len, u8 key_index, - const u8 *mac_addr, int disable); - -int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len); - -int mwifiex_get_ver_ext(struct mwifiex_private *priv); - -int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, - struct ieee80211_channel *chan, - unsigned int duration); - -int mwifiex_get_stats_info(struct mwifiex_private *priv, - struct mwifiex_ds_get_stats *log); - -int mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, - u32 reg_offset, u32 reg_value); - -int mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, - u32 reg_offset, u32 *value); - -int mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, - u8 *value); - -int mwifiex_set_11n_httx_cfg(struct mwifiex_private *priv, int data); - -int mwifiex_get_11n_httx_cfg(struct mwifiex_private *priv, int *data); - -int mwifiex_set_tx_rate_cfg(struct mwifiex_private *priv, int tx_rate_index); - -int mwifiex_get_tx_rate_cfg(struct mwifiex_private *priv, int *tx_rate_index); - -int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode); - -int mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, - char *version, int max_len); - -int mwifiex_set_tx_power(struct mwifiex_private *priv, - struct mwifiex_power_cfg *power_cfg); - -int mwifiex_main_process(struct mwifiex_adapter *); - -int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb); - -int mwifiex_get_bss_info(struct mwifiex_private *, - struct mwifiex_bss_info *); -int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, - struct cfg80211_bss *bss, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, - struct mwifiex_bssdescriptor *bss_entry); -int mwifiex_check_network_compatibility(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc); - -u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type); -u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset); - -struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, - const char *name, - unsigned char name_assign_type, - enum nl80211_iftype type, - u32 *flags, - struct vif_params *params); -int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev); - -void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config); - -int mwifiex_add_wowlan_magic_pkt_filter(struct mwifiex_adapter *adapter); - -int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, - struct cfg80211_beacon_data *data); -int mwifiex_del_mgmt_ies(struct mwifiex_private *priv); -u8 *mwifiex_11d_code_2_region(u8 code); -void mwifiex_uap_set_channel(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_chan_def chandef); -int mwifiex_config_start_uap(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg); -void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, - struct mwifiex_sta_node *node); - -void mwifiex_init_11h_params(struct mwifiex_private *priv); -int mwifiex_is_11h_active(struct mwifiex_private *priv); -int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag); - -void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv); -int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, - struct device_node *node, const char *prefix); -void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv); - -extern const struct ethtool_ops mwifiex_ethtool_ops; - -void mwifiex_del_all_sta_list(struct mwifiex_private *priv); -void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac); -void -mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, - int ies_len, struct mwifiex_sta_node *node); -struct mwifiex_sta_node * -mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac); -struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac); -u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv); -u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv); -u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv); -int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, - u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len); -int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, - u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len); -void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, - u8 *buf, int len); -int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action); -int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac); -int mwifiex_get_tdls_list(struct mwifiex_private *priv, - struct tdls_peer_info *buf); -void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv); -bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv); -u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, - u32 pri_chan, u8 chan_bw); -int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter); - -int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb); -void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv); -void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv, - const u8 *mac, u8 link_status); -void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv, - u8 *mac, s8 snr, s8 nflr); -void mwifiex_check_auto_tdls(unsigned long context); -void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac); -void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv); -void mwifiex_clean_auto_tdls(struct mwifiex_private *priv); -int mwifiex_config_tdls_enable(struct mwifiex_private *priv); -int mwifiex_config_tdls_disable(struct mwifiex_private *priv); -int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv); -int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac); -int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac, - u8 primary_chan, u8 second_chan_offset, u8 band); - -int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - void *data_buf); -int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, - struct sk_buff *skb); - -void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, - void *event_body); - -struct sk_buff * -mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, - struct sk_buff *skb, u8 flag, u64 *cookie); -void mwifiex_dfs_cac_work_queue(struct work_struct *work); -void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work); -void mwifiex_abort_cac(struct mwifiex_private *priv); -int mwifiex_stop_radar_detection(struct mwifiex_private *priv, - struct cfg80211_chan_def *chandef); -int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, - struct sk_buff *skb); - -void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, - s8 nflr); -void mwifiex_hist_data_reset(struct mwifiex_private *priv); -void mwifiex_hist_data_add(struct mwifiex_private *priv, - u8 rx_rate, s8 snr, s8 nflr); -u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, - u8 rx_rate, u8 ht_info); - -void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter); -void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter); -void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags); -void mwifiex_queue_main_work(struct mwifiex_adapter *adapter); -void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter); -void mwifiex_11n_delba(struct mwifiex_private *priv, int tid); -int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy); -void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, - struct sk_buff *event); -void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, - struct sk_buff *event_skb); -void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter); - -#ifdef CONFIG_DEBUG_FS -void mwifiex_debugfs_init(void); -void mwifiex_debugfs_remove(void); - -void mwifiex_dev_debugfs_init(struct mwifiex_private *priv); -void mwifiex_dev_debugfs_remove(struct mwifiex_private *priv); -#endif -#endif /* !_MWIFIEX_MAIN_H_ */ diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c deleted file mode 100644 index 21192b6f9c64..000000000000 --- a/drivers/net/wireless/mwifiex/pcie.c +++ /dev/null @@ -1,2742 +0,0 @@ -/* - * Marvell Wireless LAN device driver: PCIE specific handling - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "pcie.h" - -#define PCIE_VERSION "1.0" -#define DRV_NAME "Marvell mwifiex PCIe" - -static u8 user_rmmod; - -static struct mwifiex_if_ops pcie_ops; - -static struct semaphore add_remove_card_sem; - -static struct memory_type_mapping mem_type_mapping_tbl[] = { - {"ITCM", NULL, 0, 0xF0}, - {"DTCM", NULL, 0, 0xF1}, - {"SQRAM", NULL, 0, 0xF2}, - {"IRAM", NULL, 0, 0xF3}, - {"APU", NULL, 0, 0xF4}, - {"CIU", NULL, 0, 0xF5}, - {"ICU", NULL, 0, 0xF6}, - {"MAC", NULL, 0, 0xF7}, -}; - -static int -mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, - size_t size, int flags) -{ - struct pcie_service_card *card = adapter->card; - struct mwifiex_dma_mapping mapping; - - mapping.addr = pci_map_single(card->dev, skb->data, size, flags); - if (pci_dma_mapping_error(card->dev, mapping.addr)) { - mwifiex_dbg(adapter, ERROR, "failed to map pci memory!\n"); - return -1; - } - mapping.len = size; - mwifiex_store_mapping(skb, &mapping); - return 0; -} - -static void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter, - struct sk_buff *skb, int flags) -{ - struct pcie_service_card *card = adapter->card; - struct mwifiex_dma_mapping mapping; - - mwifiex_get_mapping(skb, &mapping); - pci_unmap_single(card->dev, mapping.addr, mapping.len, flags); -} - -/* - * This function reads sleep cookie and checks if FW is ready - */ -static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) -{ - u32 *cookie_addr; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - if (!reg->sleep_cookie) - return true; - - if (card->sleep_cookie_vbase) { - cookie_addr = (u32 *)card->sleep_cookie_vbase; - mwifiex_dbg(adapter, INFO, - "info: ACCESS_HW: sleep cookie=0x%x\n", - *cookie_addr); - if (*cookie_addr == FW_AWAKE_COOKIE) - return true; - } - - return false; -} - -#ifdef CONFIG_PM_SLEEP -/* - * Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not suspended, this function allocates and sends a host - * sleep activate request to the firmware and turns off the traffic. - */ -static int mwifiex_pcie_suspend(struct device *dev) -{ - struct mwifiex_adapter *adapter; - struct pcie_service_card *card; - int hs_actived; - struct pci_dev *pdev = to_pci_dev(dev); - - if (pdev) { - card = pci_get_drvdata(pdev); - if (!card || !card->adapter) { - pr_err("Card or adapter structure is not valid\n"); - return 0; - } - } else { - pr_err("PCIE device is not specified\n"); - return 0; - } - - adapter = card->adapter; - - hs_actived = mwifiex_enable_hs(adapter); - - /* Indicate device suspended */ - adapter->is_suspended = true; - adapter->hs_enabling = false; - - return 0; -} - -/* - * Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not resumed, this function turns on the traffic and - * sends a host sleep cancel request to the firmware. - */ -static int mwifiex_pcie_resume(struct device *dev) -{ - struct mwifiex_adapter *adapter; - struct pcie_service_card *card; - struct pci_dev *pdev = to_pci_dev(dev); - - if (pdev) { - card = pci_get_drvdata(pdev); - if (!card || !card->adapter) { - pr_err("Card or adapter structure is not valid\n"); - return 0; - } - } else { - pr_err("PCIE device is not specified\n"); - return 0; - } - - adapter = card->adapter; - - if (!adapter->is_suspended) { - mwifiex_dbg(adapter, WARN, - "Device already resumed\n"); - return 0; - } - - adapter->is_suspended = false; - - mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), - MWIFIEX_ASYNC_CMD); - - return 0; -} -#endif - -/* - * This function probes an mwifiex device and registers it. It allocates - * the card structure, enables PCIE function number and initiates the - * device registration and initialization procedure by adding a logical - * interface. - */ -static int mwifiex_pcie_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct pcie_service_card *card; - - pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", - pdev->vendor, pdev->device, pdev->revision); - - card = kzalloc(sizeof(struct pcie_service_card), GFP_KERNEL); - if (!card) - return -ENOMEM; - - card->dev = pdev; - - if (ent->driver_data) { - struct mwifiex_pcie_device *data = (void *)ent->driver_data; - card->pcie.firmware = data->firmware; - card->pcie.reg = data->reg; - card->pcie.blksz_fw_dl = data->blksz_fw_dl; - card->pcie.tx_buf_size = data->tx_buf_size; - card->pcie.can_dump_fw = data->can_dump_fw; - card->pcie.can_ext_scan = data->can_ext_scan; - } - - if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, - MWIFIEX_PCIE)) { - pr_err("%s failed\n", __func__); - kfree(card); - return -1; - } - - return 0; -} - -/* - * This function removes the interface and frees up the card structure. - */ -static void mwifiex_pcie_remove(struct pci_dev *pdev) -{ - struct pcie_service_card *card; - struct mwifiex_adapter *adapter; - struct mwifiex_private *priv; - - card = pci_get_drvdata(pdev); - if (!card) - return; - - adapter = card->adapter; - if (!adapter || !adapter->priv_num) - return; - - if (user_rmmod) { -#ifdef CONFIG_PM_SLEEP - if (adapter->is_suspended) - mwifiex_pcie_resume(&pdev->dev); -#endif - - mwifiex_deauthenticate_all(adapter); - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - mwifiex_disable_auto_ds(priv); - - mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); - } - - mwifiex_remove_card(card->adapter, &add_remove_card_sem); -} - -static void mwifiex_pcie_shutdown(struct pci_dev *pdev) -{ - user_rmmod = 1; - mwifiex_pcie_remove(pdev); - - return; -} - -static const struct pci_device_id mwifiex_ids[] = { - { - PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - .driver_data = (unsigned long)&mwifiex_pcie8766, - }, - { - PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8897, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - .driver_data = (unsigned long)&mwifiex_pcie8897, - }, - { - PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8997, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - .driver_data = (unsigned long)&mwifiex_pcie8997, - }, - {}, -}; - -MODULE_DEVICE_TABLE(pci, mwifiex_ids); - -#ifdef CONFIG_PM_SLEEP -/* Power Management Hooks */ -static SIMPLE_DEV_PM_OPS(mwifiex_pcie_pm_ops, mwifiex_pcie_suspend, - mwifiex_pcie_resume); -#endif - -/* PCI Device Driver */ -static struct pci_driver __refdata mwifiex_pcie = { - .name = "mwifiex_pcie", - .id_table = mwifiex_ids, - .probe = mwifiex_pcie_probe, - .remove = mwifiex_pcie_remove, -#ifdef CONFIG_PM_SLEEP - .driver = { - .pm = &mwifiex_pcie_pm_ops, - }, -#endif - .shutdown = mwifiex_pcie_shutdown, -}; - -/* - * This function writes data into PCIE card register. - */ -static int mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data) -{ - struct pcie_service_card *card = adapter->card; - - iowrite32(data, card->pci_mmap1 + reg); - - return 0; -} - -/* - * This function reads data from PCIE card register. - */ -static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data) -{ - struct pcie_service_card *card = adapter->card; - - *data = ioread32(card->pci_mmap1 + reg); - - return 0; -} - -/* This function reads u8 data from PCIE card register. */ -static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter, - int reg, u8 *data) -{ - struct pcie_service_card *card = adapter->card; - - *data = ioread8(card->pci_mmap1 + reg); - - return 0; -} - -/* - * This function adds delay loop to ensure FW is awake before proceeding. - */ -static void mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter) -{ - int i = 0; - - while (mwifiex_pcie_ok_to_access_hw(adapter)) { - i++; - usleep_range(10, 20); - /* 50ms max wait */ - if (i == 5000) - break; - } - - return; -} - -static void mwifiex_delay_for_sleep_cookie(struct mwifiex_adapter *adapter, - u32 max_delay_loop_cnt) -{ - struct pcie_service_card *card = adapter->card; - u8 *buffer; - u32 sleep_cookie, count; - - for (count = 0; count < max_delay_loop_cnt; count++) { - buffer = card->cmdrsp_buf->data - INTF_HEADER_LEN; - sleep_cookie = *(u32 *)buffer; - - if (sleep_cookie == MWIFIEX_DEF_SLEEP_COOKIE) { - mwifiex_dbg(adapter, INFO, - "sleep cookie found at count %d\n", count); - break; - } - usleep_range(20, 30); - } - - if (count >= max_delay_loop_cnt) - mwifiex_dbg(adapter, INFO, - "max count reached while accessing sleep cookie\n"); -} - -/* This function wakes up the card by reading fw_status register. */ -static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) -{ - u32 fw_status; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - mwifiex_dbg(adapter, EVENT, - "event: Wakeup device...\n"); - - if (reg->sleep_cookie) - mwifiex_pcie_dev_wakeup_delay(adapter); - - /* Reading fw_status register will wakeup device */ - if (mwifiex_read_reg(adapter, reg->fw_status, &fw_status)) { - mwifiex_dbg(adapter, ERROR, - "Reading fw_status register failed\n"); - return -1; - } - - if (reg->sleep_cookie) { - mwifiex_pcie_dev_wakeup_delay(adapter); - mwifiex_dbg(adapter, INFO, - "PCIE wakeup: Setting PS_STATE_AWAKE\n"); - adapter->ps_state = PS_STATE_AWAKE; - } - - return 0; -} - -/* - * This function is called after the card has woken up. - * - * The card configuration register is reset. - */ -static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) -{ - mwifiex_dbg(adapter, CMD, - "cmd: Wakeup device completed\n"); - - return 0; -} - -/* - * This function disables the host interrupt. - * - * The host interrupt mask is read, the disable bit is reset and - * written back to the card host interrupt mask register. - */ -static int mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter) -{ - if (mwifiex_pcie_ok_to_access_hw(adapter)) { - if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, - 0x00000000)) { - mwifiex_dbg(adapter, ERROR, - "Disable host interrupt failed\n"); - return -1; - } - } - - return 0; -} - -/* - * This function enables the host interrupt. - * - * The host interrupt enable mask is written to the card - * host interrupt mask register. - */ -static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter) -{ - if (mwifiex_pcie_ok_to_access_hw(adapter)) { - /* Simply write the mask to the register */ - if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, - HOST_INTR_MASK)) { - mwifiex_dbg(adapter, ERROR, - "Enable host interrupt failed\n"); - return -1; - } - } - - return 0; -} - -/* - * This function initializes TX buffer ring descriptors - */ -static int mwifiex_init_txq_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - int i; - - for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { - card->tx_buf_list[i] = NULL; - if (reg->pfu_enabled) { - card->txbd_ring[i] = (void *)card->txbd_ring_vbase + - (sizeof(*desc2) * i); - desc2 = card->txbd_ring[i]; - memset(desc2, 0, sizeof(*desc2)); - } else { - card->txbd_ring[i] = (void *)card->txbd_ring_vbase + - (sizeof(*desc) * i); - desc = card->txbd_ring[i]; - memset(desc, 0, sizeof(*desc)); - } - } - - return 0; -} - -/* This function initializes RX buffer ring descriptors. Each SKB is allocated - * here and after mapping PCI memory, its physical address is assigned to - * PCIE Rx buffer descriptor's physical address. - */ -static int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - struct sk_buff *skb; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - dma_addr_t buf_pa; - int i; - - for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { - /* Allocate skb here so that firmware can DMA data from it */ - skb = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, - GFP_KERNEL | GFP_DMA); - if (!skb) { - mwifiex_dbg(adapter, ERROR, - "Unable to allocate skb for RX ring.\n"); - kfree(card->rxbd_ring_vbase); - return -ENOMEM; - } - - if (mwifiex_map_pci_memory(adapter, skb, - MWIFIEX_RX_DATA_BUF_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - - buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); - - mwifiex_dbg(adapter, INFO, - "info: RX ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", - skb, skb->len, skb->data, (u32)buf_pa, - (u32)((u64)buf_pa >> 32)); - - card->rx_buf_list[i] = skb; - if (reg->pfu_enabled) { - card->rxbd_ring[i] = (void *)card->rxbd_ring_vbase + - (sizeof(*desc2) * i); - desc2 = card->rxbd_ring[i]; - desc2->paddr = buf_pa; - desc2->len = (u16)skb->len; - desc2->frag_len = (u16)skb->len; - desc2->flags = reg->ring_flag_eop | reg->ring_flag_sop; - desc2->offset = 0; - } else { - card->rxbd_ring[i] = (void *)(card->rxbd_ring_vbase + - (sizeof(*desc) * i)); - desc = card->rxbd_ring[i]; - desc->paddr = buf_pa; - desc->len = (u16)skb->len; - desc->flags = 0; - } - } - - return 0; -} - -/* This function initializes event buffer ring descriptors. Each SKB is - * allocated here and after mapping PCI memory, its physical address is assigned - * to PCIE Rx buffer descriptor's physical address - */ -static int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - struct mwifiex_evt_buf_desc *desc; - struct sk_buff *skb; - dma_addr_t buf_pa; - int i; - - for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { - /* Allocate skb here so that firmware can DMA data from it */ - skb = dev_alloc_skb(MAX_EVENT_SIZE); - if (!skb) { - mwifiex_dbg(adapter, ERROR, - "Unable to allocate skb for EVENT buf.\n"); - kfree(card->evtbd_ring_vbase); - return -ENOMEM; - } - skb_put(skb, MAX_EVENT_SIZE); - - if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - - buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); - - mwifiex_dbg(adapter, EVENT, - "info: EVT ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", - skb, skb->len, skb->data, (u32)buf_pa, - (u32)((u64)buf_pa >> 32)); - - card->evt_buf_list[i] = skb; - card->evtbd_ring[i] = (void *)(card->evtbd_ring_vbase + - (sizeof(*desc) * i)); - desc = card->evtbd_ring[i]; - desc->paddr = buf_pa; - desc->len = (u16)skb->len; - desc->flags = 0; - } - - return 0; -} - -/* This function cleans up TX buffer rings. If any of the buffer list has valid - * SKB address, associated SKB is freed. - */ -static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - struct sk_buff *skb; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - int i; - - for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { - if (reg->pfu_enabled) { - desc2 = card->txbd_ring[i]; - if (card->tx_buf_list[i]) { - skb = card->tx_buf_list[i]; - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_TODEVICE); - dev_kfree_skb_any(skb); - } - memset(desc2, 0, sizeof(*desc2)); - } else { - desc = card->txbd_ring[i]; - if (card->tx_buf_list[i]) { - skb = card->tx_buf_list[i]; - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_TODEVICE); - dev_kfree_skb_any(skb); - } - memset(desc, 0, sizeof(*desc)); - } - card->tx_buf_list[i] = NULL; - } - - return; -} - -/* This function cleans up RX buffer rings. If any of the buffer list has valid - * SKB address, associated SKB is freed. - */ -static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - struct sk_buff *skb; - int i; - - for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { - if (reg->pfu_enabled) { - desc2 = card->rxbd_ring[i]; - if (card->rx_buf_list[i]) { - skb = card->rx_buf_list[i]; - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(skb); - } - memset(desc2, 0, sizeof(*desc2)); - } else { - desc = card->rxbd_ring[i]; - if (card->rx_buf_list[i]) { - skb = card->rx_buf_list[i]; - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(skb); - } - memset(desc, 0, sizeof(*desc)); - } - card->rx_buf_list[i] = NULL; - } - - return; -} - -/* This function cleans up event buffer rings. If any of the buffer list has - * valid SKB address, associated SKB is freed. - */ -static void mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - struct mwifiex_evt_buf_desc *desc; - struct sk_buff *skb; - int i; - - for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { - desc = card->evtbd_ring[i]; - if (card->evt_buf_list[i]) { - skb = card->evt_buf_list[i]; - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(skb); - } - card->evt_buf_list[i] = NULL; - memset(desc, 0, sizeof(*desc)); - } - - return; -} - -/* This function creates buffer descriptor ring for TX - */ -static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - /* - * driver maintaines the write pointer and firmware maintaines the read - * pointer. The write pointer starts at 0 (zero) while the read pointer - * starts at zero with rollover bit set - */ - card->txbd_wrptr = 0; - - if (reg->pfu_enabled) - card->txbd_rdptr = 0; - else - card->txbd_rdptr |= reg->tx_rollover_ind; - - /* allocate shared memory for the BD ring and divide the same in to - several descriptors */ - if (reg->pfu_enabled) - card->txbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) * - MWIFIEX_MAX_TXRX_BD; - else - card->txbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * - MWIFIEX_MAX_TXRX_BD; - - mwifiex_dbg(adapter, INFO, - "info: txbd_ring: Allocating %d bytes\n", - card->txbd_ring_size); - card->txbd_ring_vbase = pci_alloc_consistent(card->dev, - card->txbd_ring_size, - &card->txbd_ring_pbase); - if (!card->txbd_ring_vbase) { - mwifiex_dbg(adapter, ERROR, - "allocate consistent memory (%d bytes) failed!\n", - card->txbd_ring_size); - return -ENOMEM; - } - mwifiex_dbg(adapter, DATA, - "info: txbd_ring - base: %p, pbase: %#x:%x, len: %x\n", - card->txbd_ring_vbase, (unsigned int)card->txbd_ring_pbase, - (u32)((u64)card->txbd_ring_pbase >> 32), - card->txbd_ring_size); - - return mwifiex_init_txq_ring(adapter); -} - -static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - mwifiex_cleanup_txq_ring(adapter); - - if (card->txbd_ring_vbase) - pci_free_consistent(card->dev, card->txbd_ring_size, - card->txbd_ring_vbase, - card->txbd_ring_pbase); - card->txbd_ring_size = 0; - card->txbd_wrptr = 0; - card->txbd_rdptr = 0 | reg->tx_rollover_ind; - card->txbd_ring_vbase = NULL; - card->txbd_ring_pbase = 0; - - return 0; -} - -/* - * This function creates buffer descriptor ring for RX - */ -static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - /* - * driver maintaines the read pointer and firmware maintaines the write - * pointer. The write pointer starts at 0 (zero) while the read pointer - * starts at zero with rollover bit set - */ - card->rxbd_wrptr = 0; - card->rxbd_rdptr = reg->rx_rollover_ind; - - if (reg->pfu_enabled) - card->rxbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) * - MWIFIEX_MAX_TXRX_BD; - else - card->rxbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * - MWIFIEX_MAX_TXRX_BD; - - mwifiex_dbg(adapter, INFO, - "info: rxbd_ring: Allocating %d bytes\n", - card->rxbd_ring_size); - card->rxbd_ring_vbase = pci_alloc_consistent(card->dev, - card->rxbd_ring_size, - &card->rxbd_ring_pbase); - if (!card->rxbd_ring_vbase) { - mwifiex_dbg(adapter, ERROR, - "allocate consistent memory (%d bytes) failed!\n", - card->rxbd_ring_size); - return -ENOMEM; - } - - mwifiex_dbg(adapter, DATA, - "info: rxbd_ring - base: %p, pbase: %#x:%x, len: %#x\n", - card->rxbd_ring_vbase, (u32)card->rxbd_ring_pbase, - (u32)((u64)card->rxbd_ring_pbase >> 32), - card->rxbd_ring_size); - - return mwifiex_init_rxq_ring(adapter); -} - -/* - * This function deletes Buffer descriptor ring for RX - */ -static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - mwifiex_cleanup_rxq_ring(adapter); - - if (card->rxbd_ring_vbase) - pci_free_consistent(card->dev, card->rxbd_ring_size, - card->rxbd_ring_vbase, - card->rxbd_ring_pbase); - card->rxbd_ring_size = 0; - card->rxbd_wrptr = 0; - card->rxbd_rdptr = 0 | reg->rx_rollover_ind; - card->rxbd_ring_vbase = NULL; - card->rxbd_ring_pbase = 0; - - return 0; -} - -/* - * This function creates buffer descriptor ring for Events - */ -static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - /* - * driver maintaines the read pointer and firmware maintaines the write - * pointer. The write pointer starts at 0 (zero) while the read pointer - * starts at zero with rollover bit set - */ - card->evtbd_wrptr = 0; - card->evtbd_rdptr = reg->evt_rollover_ind; - - card->evtbd_ring_size = sizeof(struct mwifiex_evt_buf_desc) * - MWIFIEX_MAX_EVT_BD; - - mwifiex_dbg(adapter, INFO, - "info: evtbd_ring: Allocating %d bytes\n", - card->evtbd_ring_size); - card->evtbd_ring_vbase = pci_alloc_consistent(card->dev, - card->evtbd_ring_size, - &card->evtbd_ring_pbase); - if (!card->evtbd_ring_vbase) { - mwifiex_dbg(adapter, ERROR, - "allocate consistent memory (%d bytes) failed!\n", - card->evtbd_ring_size); - return -ENOMEM; - } - - mwifiex_dbg(adapter, EVENT, - "info: CMDRSP/EVT bd_ring - base: %p pbase: %#x:%x len: %#x\n", - card->evtbd_ring_vbase, (u32)card->evtbd_ring_pbase, - (u32)((u64)card->evtbd_ring_pbase >> 32), - card->evtbd_ring_size); - - return mwifiex_pcie_init_evt_ring(adapter); -} - -/* - * This function deletes Buffer descriptor ring for Events - */ -static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - mwifiex_cleanup_evt_ring(adapter); - - if (card->evtbd_ring_vbase) - pci_free_consistent(card->dev, card->evtbd_ring_size, - card->evtbd_ring_vbase, - card->evtbd_ring_pbase); - card->evtbd_wrptr = 0; - card->evtbd_rdptr = 0 | reg->evt_rollover_ind; - card->evtbd_ring_size = 0; - card->evtbd_ring_vbase = NULL; - card->evtbd_ring_pbase = 0; - - return 0; -} - -/* - * This function allocates a buffer for CMDRSP - */ -static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - struct sk_buff *skb; - - /* Allocate memory for receiving command response data */ - skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); - if (!skb) { - mwifiex_dbg(adapter, ERROR, - "Unable to allocate skb for command response data.\n"); - return -ENOMEM; - } - skb_put(skb, MWIFIEX_UPLD_SIZE); - if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - - card->cmdrsp_buf = skb; - - return 0; -} - -/* - * This function deletes a buffer for CMDRSP - */ -static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card; - - if (!adapter) - return 0; - - card = adapter->card; - - if (card && card->cmdrsp_buf) { - mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(card->cmdrsp_buf); - } - - if (card && card->cmd_buf) { - mwifiex_unmap_pci_memory(adapter, card->cmd_buf, - PCI_DMA_TODEVICE); - } - return 0; -} - -/* - * This function allocates a buffer for sleep cookie - */ -static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - - card->sleep_cookie_vbase = pci_alloc_consistent(card->dev, sizeof(u32), - &card->sleep_cookie_pbase); - if (!card->sleep_cookie_vbase) { - mwifiex_dbg(adapter, ERROR, - "pci_alloc_consistent failed!\n"); - return -ENOMEM; - } - /* Init val of Sleep Cookie */ - *(u32 *)card->sleep_cookie_vbase = FW_AWAKE_COOKIE; - - mwifiex_dbg(adapter, INFO, - "alloc_scook: sleep cookie=0x%x\n", - *((u32 *)card->sleep_cookie_vbase)); - - return 0; -} - -/* - * This function deletes buffer for sleep cookie - */ -static int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card; - - if (!adapter) - return 0; - - card = adapter->card; - - if (card && card->sleep_cookie_vbase) { - pci_free_consistent(card->dev, sizeof(u32), - card->sleep_cookie_vbase, - card->sleep_cookie_pbase); - card->sleep_cookie_vbase = NULL; - } - - return 0; -} - -/* This function flushes the TX buffer descriptor ring - * This function defined as handler is also called while cleaning TXRX - * during disconnect/ bss stop. - */ -static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - - if (!mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) { - card->txbd_flush = 1; - /* write pointer already set at last send - * send dnld-rdy intr again, wait for completion. - */ - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_DNLD_RDY)) { - mwifiex_dbg(adapter, ERROR, - "failed to assert dnld-rdy interrupt.\n"); - return -1; - } - } - return 0; -} - -/* - * This function unmaps and frees downloaded data buffer - */ -static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) -{ - struct sk_buff *skb; - u32 wrdoneidx, rdptr, num_tx_buffs, unmap_count = 0; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - mwifiex_pm_wakeup_card(adapter); - - /* Read the TX ring read pointer set by firmware */ - if (mwifiex_read_reg(adapter, reg->tx_rdptr, &rdptr)) { - mwifiex_dbg(adapter, ERROR, - "SEND COMP: failed to read reg->tx_rdptr\n"); - return -1; - } - - mwifiex_dbg(adapter, DATA, - "SEND COMP: rdptr_prev=0x%x, rdptr=0x%x\n", - card->txbd_rdptr, rdptr); - - num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr; - /* free from previous txbd_rdptr to current txbd_rdptr */ - while (((card->txbd_rdptr & reg->tx_mask) != - (rdptr & reg->tx_mask)) || - ((card->txbd_rdptr & reg->tx_rollover_ind) != - (rdptr & reg->tx_rollover_ind))) { - wrdoneidx = (card->txbd_rdptr & reg->tx_mask) >> - reg->tx_start_ptr; - - skb = card->tx_buf_list[wrdoneidx]; - - if (skb) { - mwifiex_dbg(adapter, DATA, - "SEND COMP: Detach skb %p at txbd_rdidx=%d\n", - skb, wrdoneidx); - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_TODEVICE); - - unmap_count++; - - if (card->txbd_flush) - mwifiex_write_data_complete(adapter, skb, 0, - -1); - else - mwifiex_write_data_complete(adapter, skb, 0, 0); - } - - card->tx_buf_list[wrdoneidx] = NULL; - - if (reg->pfu_enabled) { - desc2 = card->txbd_ring[wrdoneidx]; - memset(desc2, 0, sizeof(*desc2)); - } else { - desc = card->txbd_ring[wrdoneidx]; - memset(desc, 0, sizeof(*desc)); - } - switch (card->dev->device) { - case PCIE_DEVICE_ID_MARVELL_88W8766P: - card->txbd_rdptr++; - break; - case PCIE_DEVICE_ID_MARVELL_88W8897: - case PCIE_DEVICE_ID_MARVELL_88W8997: - card->txbd_rdptr += reg->ring_tx_start_ptr; - break; - } - - - if ((card->txbd_rdptr & reg->tx_mask) == num_tx_buffs) - card->txbd_rdptr = ((card->txbd_rdptr & - reg->tx_rollover_ind) ^ - reg->tx_rollover_ind); - } - - if (unmap_count) - adapter->data_sent = false; - - if (card->txbd_flush) { - if (mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) - card->txbd_flush = 0; - else - mwifiex_clean_pcie_ring_buf(adapter); - } - - return 0; -} - -/* This function sends data buffer to device. First 4 bytes of payload - * are filled with payload length and payload type. Then this payload - * is mapped to PCI device memory. Tx ring pointers are advanced accordingly. - * Download ready interrupt to FW is deffered if Tx ring is not full and - * additional payload can be accomodated. - * Caller must ensure tx_param parameter to this function is not NULL. - */ -static int -mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - u32 wrindx, num_tx_buffs, rx_val; - int ret; - dma_addr_t buf_pa; - struct mwifiex_pcie_buf_desc *desc = NULL; - struct mwifiex_pfu_buf_desc *desc2 = NULL; - __le16 *tmp; - - if (!(skb->data && skb->len)) { - mwifiex_dbg(adapter, ERROR, - "%s(): invalid parameter <%p, %#x>\n", - __func__, skb->data, skb->len); - return -1; - } - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - mwifiex_pm_wakeup_card(adapter); - - num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr; - mwifiex_dbg(adapter, DATA, - "info: SEND DATA: \n", - card->txbd_rdptr, card->txbd_wrptr); - if (mwifiex_pcie_txbd_not_full(card)) { - u8 *payload; - - adapter->data_sent = true; - payload = skb->data; - tmp = (__le16 *)&payload[0]; - *tmp = cpu_to_le16((u16)skb->len); - tmp = (__le16 *)&payload[2]; - *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA); - - if (mwifiex_map_pci_memory(adapter, skb, skb->len, - PCI_DMA_TODEVICE)) - return -1; - - wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr; - buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); - card->tx_buf_list[wrindx] = skb; - - if (reg->pfu_enabled) { - desc2 = card->txbd_ring[wrindx]; - desc2->paddr = buf_pa; - desc2->len = (u16)skb->len; - desc2->frag_len = (u16)skb->len; - desc2->offset = 0; - desc2->flags = MWIFIEX_BD_FLAG_FIRST_DESC | - MWIFIEX_BD_FLAG_LAST_DESC; - } else { - desc = card->txbd_ring[wrindx]; - desc->paddr = buf_pa; - desc->len = (u16)skb->len; - desc->flags = MWIFIEX_BD_FLAG_FIRST_DESC | - MWIFIEX_BD_FLAG_LAST_DESC; - } - - switch (card->dev->device) { - case PCIE_DEVICE_ID_MARVELL_88W8766P: - card->txbd_wrptr++; - break; - case PCIE_DEVICE_ID_MARVELL_88W8897: - case PCIE_DEVICE_ID_MARVELL_88W8997: - card->txbd_wrptr += reg->ring_tx_start_ptr; - break; - } - - if ((card->txbd_wrptr & reg->tx_mask) == num_tx_buffs) - card->txbd_wrptr = ((card->txbd_wrptr & - reg->tx_rollover_ind) ^ - reg->tx_rollover_ind); - - rx_val = card->rxbd_rdptr & reg->rx_wrap_mask; - /* Write the TX ring write pointer in to reg->tx_wrptr */ - if (mwifiex_write_reg(adapter, reg->tx_wrptr, - card->txbd_wrptr | rx_val)) { - mwifiex_dbg(adapter, ERROR, - "SEND DATA: failed to write reg->tx_wrptr\n"); - ret = -1; - goto done_unmap; - } - if ((mwifiex_pcie_txbd_not_full(card)) && - tx_param->next_pkt_len) { - /* have more packets and TxBD still can hold more */ - mwifiex_dbg(adapter, DATA, - "SEND DATA: delay dnld-rdy interrupt.\n"); - adapter->data_sent = false; - } else { - /* Send the TX ready interrupt */ - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_DNLD_RDY)) { - mwifiex_dbg(adapter, ERROR, - "SEND DATA: failed to assert dnld-rdy interrupt.\n"); - ret = -1; - goto done_unmap; - } - } - mwifiex_dbg(adapter, DATA, - "info: SEND DATA: Updated and sent packet to firmware successfully\n", - card->txbd_rdptr, card->txbd_wrptr); - } else { - mwifiex_dbg(adapter, DATA, - "info: TX Ring full, can't send packets to fw\n"); - adapter->data_sent = true; - /* Send the TX ready interrupt */ - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_DNLD_RDY)) - mwifiex_dbg(adapter, ERROR, - "SEND DATA: failed to assert door-bell intr\n"); - return -EBUSY; - } - - return -EINPROGRESS; -done_unmap: - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - card->tx_buf_list[wrindx] = NULL; - if (reg->pfu_enabled) - memset(desc2, 0, sizeof(*desc2)); - else - memset(desc, 0, sizeof(*desc)); - - return ret; -} - -/* - * This function handles received buffer ring and - * dispatches packets to upper - */ -static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - u32 wrptr, rd_index, tx_val; - dma_addr_t buf_pa; - int ret = 0; - struct sk_buff *skb_tmp = NULL; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - mwifiex_pm_wakeup_card(adapter); - - /* Read the RX ring Write pointer set by firmware */ - if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) { - mwifiex_dbg(adapter, ERROR, - "RECV DATA: failed to read reg->rx_wrptr\n"); - ret = -1; - goto done; - } - card->rxbd_wrptr = wrptr; - - while (((wrptr & reg->rx_mask) != - (card->rxbd_rdptr & reg->rx_mask)) || - ((wrptr & reg->rx_rollover_ind) == - (card->rxbd_rdptr & reg->rx_rollover_ind))) { - struct sk_buff *skb_data; - u16 rx_len; - __le16 pkt_len; - - rd_index = card->rxbd_rdptr & reg->rx_mask; - skb_data = card->rx_buf_list[rd_index]; - - /* If skb allocation was failed earlier for Rx packet, - * rx_buf_list[rd_index] would have been left with a NULL. - */ - if (!skb_data) - return -ENOMEM; - - mwifiex_unmap_pci_memory(adapter, skb_data, PCI_DMA_FROMDEVICE); - card->rx_buf_list[rd_index] = NULL; - - /* Get data length from interface header - - * first 2 bytes for len, next 2 bytes is for type - */ - pkt_len = *((__le16 *)skb_data->data); - rx_len = le16_to_cpu(pkt_len); - if (WARN_ON(rx_len <= INTF_HEADER_LEN || - rx_len > MWIFIEX_RX_DATA_BUF_SIZE)) { - mwifiex_dbg(adapter, ERROR, - "Invalid RX len %d, Rd=%#x, Wr=%#x\n", - rx_len, card->rxbd_rdptr, wrptr); - dev_kfree_skb_any(skb_data); - } else { - skb_put(skb_data, rx_len); - mwifiex_dbg(adapter, DATA, - "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n", - card->rxbd_rdptr, wrptr, rx_len); - skb_pull(skb_data, INTF_HEADER_LEN); - if (adapter->rx_work_enabled) { - skb_queue_tail(&adapter->rx_data_q, skb_data); - adapter->data_received = true; - atomic_inc(&adapter->rx_pending); - } else { - mwifiex_handle_rx_packet(adapter, skb_data); - } - } - - skb_tmp = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, - GFP_KERNEL | GFP_DMA); - if (!skb_tmp) { - mwifiex_dbg(adapter, ERROR, - "Unable to allocate skb.\n"); - return -ENOMEM; - } - - if (mwifiex_map_pci_memory(adapter, skb_tmp, - MWIFIEX_RX_DATA_BUF_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - - buf_pa = MWIFIEX_SKB_DMA_ADDR(skb_tmp); - - mwifiex_dbg(adapter, INFO, - "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n", - skb_tmp, rd_index); - card->rx_buf_list[rd_index] = skb_tmp; - - if (reg->pfu_enabled) { - desc2 = card->rxbd_ring[rd_index]; - desc2->paddr = buf_pa; - desc2->len = skb_tmp->len; - desc2->frag_len = skb_tmp->len; - desc2->offset = 0; - desc2->flags = reg->ring_flag_sop | reg->ring_flag_eop; - } else { - desc = card->rxbd_ring[rd_index]; - desc->paddr = buf_pa; - desc->len = skb_tmp->len; - desc->flags = 0; - } - - if ((++card->rxbd_rdptr & reg->rx_mask) == - MWIFIEX_MAX_TXRX_BD) { - card->rxbd_rdptr = ((card->rxbd_rdptr & - reg->rx_rollover_ind) ^ - reg->rx_rollover_ind); - } - mwifiex_dbg(adapter, DATA, - "info: RECV DATA: \n", - card->rxbd_rdptr, wrptr); - - tx_val = card->txbd_wrptr & reg->tx_wrap_mask; - /* Write the RX ring read pointer in to reg->rx_rdptr */ - if (mwifiex_write_reg(adapter, reg->rx_rdptr, - card->rxbd_rdptr | tx_val)) { - mwifiex_dbg(adapter, DATA, - "RECV DATA: failed to write reg->rx_rdptr\n"); - ret = -1; - goto done; - } - - /* Read the RX ring Write pointer set by firmware */ - if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) { - mwifiex_dbg(adapter, ERROR, - "RECV DATA: failed to read reg->rx_wrptr\n"); - ret = -1; - goto done; - } - mwifiex_dbg(adapter, DATA, - "info: RECV DATA: Rcvd packet from fw successfully\n"); - card->rxbd_wrptr = wrptr; - } - -done: - return ret; -} - -/* - * This function downloads the boot command to device - */ -static int -mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) -{ - dma_addr_t buf_pa; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - if (!(skb->data && skb->len)) { - mwifiex_dbg(adapter, ERROR, - "Invalid parameter in %s <%p. len %d>\n", - __func__, skb->data, skb->len); - return -1; - } - - if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE)) - return -1; - - buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); - - /* Write the lower 32bits of the physical address to low command - * address scratch register - */ - if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, (u32)buf_pa)) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to write download command to boot code.\n", - __func__); - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - return -1; - } - - /* Write the upper 32bits of the physical address to high command - * address scratch register - */ - if (mwifiex_write_reg(adapter, reg->cmd_addr_hi, - (u32)((u64)buf_pa >> 32))) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to write download command to boot code.\n", - __func__); - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - return -1; - } - - /* Write the command length to cmd_size scratch register */ - if (mwifiex_write_reg(adapter, reg->cmd_size, skb->len)) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to write command len to cmd_size scratch reg\n", - __func__); - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - return -1; - } - - /* Ring the door bell */ - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_DOOR_BELL)) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to assert door-bell intr\n", __func__); - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - return -1; - } - - return 0; -} - -/* This function init rx port in firmware which in turn enables to receive data - * from device before transmitting any packet. - */ -static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask; - - /* Write the RX ring read pointer in to reg->rx_rdptr */ - if (mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr | - tx_wrap)) { - mwifiex_dbg(adapter, ERROR, - "RECV DATA: failed to write reg->rx_rdptr\n"); - return -1; - } - return 0; -} - -/* This function downloads commands to the device - */ -static int -mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - int ret = 0; - dma_addr_t cmd_buf_pa, cmdrsp_buf_pa; - u8 *payload = (u8 *)skb->data; - - if (!(skb->data && skb->len)) { - mwifiex_dbg(adapter, ERROR, - "Invalid parameter in %s <%p, %#x>\n", - __func__, skb->data, skb->len); - return -1; - } - - /* Make sure a command response buffer is available */ - if (!card->cmdrsp_buf) { - mwifiex_dbg(adapter, ERROR, - "No response buffer available, send command failed\n"); - return -EBUSY; - } - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - mwifiex_pm_wakeup_card(adapter); - - adapter->cmd_sent = true; - - *(__le16 *)&payload[0] = cpu_to_le16((u16)skb->len); - *(__le16 *)&payload[2] = cpu_to_le16(MWIFIEX_TYPE_CMD); - - if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE)) - return -1; - - card->cmd_buf = skb; - - /* To send a command, the driver will: - 1. Write the 64bit physical address of the data buffer to - cmd response address low + cmd response address high - 2. Ring the door bell (i.e. set the door bell interrupt) - - In response to door bell interrupt, the firmware will perform - the DMA of the command packet (first header to obtain the total - length and then rest of the command). - */ - - if (card->cmdrsp_buf) { - cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf); - /* Write the lower 32bits of the cmdrsp buffer physical - address */ - if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, - (u32)cmdrsp_buf_pa)) { - mwifiex_dbg(adapter, ERROR, - "Failed to write download cmd to boot code.\n"); - ret = -1; - goto done; - } - /* Write the upper 32bits of the cmdrsp buffer physical - address */ - if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, - (u32)((u64)cmdrsp_buf_pa >> 32))) { - mwifiex_dbg(adapter, ERROR, - "Failed to write download cmd to boot code.\n"); - ret = -1; - goto done; - } - } - - cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf); - /* Write the lower 32bits of the physical address to reg->cmd_addr_lo */ - if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, - (u32)cmd_buf_pa)) { - mwifiex_dbg(adapter, ERROR, - "Failed to write download cmd to boot code.\n"); - ret = -1; - goto done; - } - /* Write the upper 32bits of the physical address to reg->cmd_addr_hi */ - if (mwifiex_write_reg(adapter, reg->cmd_addr_hi, - (u32)((u64)cmd_buf_pa >> 32))) { - mwifiex_dbg(adapter, ERROR, - "Failed to write download cmd to boot code.\n"); - ret = -1; - goto done; - } - - /* Write the command length to reg->cmd_size */ - if (mwifiex_write_reg(adapter, reg->cmd_size, - card->cmd_buf->len)) { - mwifiex_dbg(adapter, ERROR, - "Failed to write cmd len to reg->cmd_size\n"); - ret = -1; - goto done; - } - - /* Ring the door bell */ - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_DOOR_BELL)) { - mwifiex_dbg(adapter, ERROR, - "Failed to assert door-bell intr\n"); - ret = -1; - goto done; - } - -done: - if (ret) - adapter->cmd_sent = false; - - return 0; -} - -/* - * This function handles command complete interrupt - */ -static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - struct sk_buff *skb = card->cmdrsp_buf; - int count = 0; - u16 rx_len; - __le16 pkt_len; - - mwifiex_dbg(adapter, CMD, - "info: Rx CMD Response\n"); - - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_FROMDEVICE); - - /* Unmap the command as a response has been received. */ - if (card->cmd_buf) { - mwifiex_unmap_pci_memory(adapter, card->cmd_buf, - PCI_DMA_TODEVICE); - card->cmd_buf = NULL; - } - - pkt_len = *((__le16 *)skb->data); - rx_len = le16_to_cpu(pkt_len); - skb_trim(skb, rx_len); - skb_pull(skb, INTF_HEADER_LEN); - - if (!adapter->curr_cmd) { - if (adapter->ps_state == PS_STATE_SLEEP_CFM) { - mwifiex_process_sleep_confirm_resp(adapter, skb->data, - skb->len); - mwifiex_pcie_enable_host_int(adapter); - if (mwifiex_write_reg(adapter, - PCIE_CPU_INT_EVENT, - CPU_INTR_SLEEP_CFM_DONE)) { - mwifiex_dbg(adapter, ERROR, - "Write register failed\n"); - return -1; - } - mwifiex_delay_for_sleep_cookie(adapter, - MWIFIEX_MAX_DELAY_COUNT); - while (reg->sleep_cookie && (count++ < 10) && - mwifiex_pcie_ok_to_access_hw(adapter)) - usleep_range(50, 60); - } else { - mwifiex_dbg(adapter, ERROR, - "There is no command but got cmdrsp\n"); - } - memcpy(adapter->upld_buf, skb->data, - min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len)); - skb_push(skb, INTF_HEADER_LEN); - if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - } else if (mwifiex_pcie_ok_to_access_hw(adapter)) { - adapter->curr_cmd->resp_skb = skb; - adapter->cmd_resp_received = true; - /* Take the pointer and set it to CMD node and will - return in the response complete callback */ - card->cmdrsp_buf = NULL; - - /* Clear the cmd-rsp buffer address in scratch registers. This - will prevent firmware from writing to the same response - buffer again. */ - if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, 0)) { - mwifiex_dbg(adapter, ERROR, - "cmd_done: failed to clear cmd_rsp_addr_lo\n"); - return -1; - } - /* Write the upper 32bits of the cmdrsp buffer physical - address */ - if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, 0)) { - mwifiex_dbg(adapter, ERROR, - "cmd_done: failed to clear cmd_rsp_addr_hi\n"); - return -1; - } - } - - return 0; -} - -/* - * Command Response processing complete handler - */ -static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - struct pcie_service_card *card = adapter->card; - - if (skb) { - card->cmdrsp_buf = skb; - skb_push(card->cmdrsp_buf, INTF_HEADER_LEN); - if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - } - - return 0; -} - -/* - * This function handles firmware event ready interrupt - */ -static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; - u32 wrptr, event; - struct mwifiex_evt_buf_desc *desc; - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - mwifiex_pm_wakeup_card(adapter); - - if (adapter->event_received) { - mwifiex_dbg(adapter, EVENT, - "info: Event being processed,\t" - "do not process this interrupt just yet\n"); - return 0; - } - - if (rdptr >= MWIFIEX_MAX_EVT_BD) { - mwifiex_dbg(adapter, ERROR, - "info: Invalid read pointer...\n"); - return -1; - } - - /* Read the event ring write pointer set by firmware */ - if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) { - mwifiex_dbg(adapter, ERROR, - "EventReady: failed to read reg->evt_wrptr\n"); - return -1; - } - - mwifiex_dbg(adapter, EVENT, - "info: EventReady: Initial ", - card->evtbd_rdptr, wrptr); - if (((wrptr & MWIFIEX_EVTBD_MASK) != (card->evtbd_rdptr - & MWIFIEX_EVTBD_MASK)) || - ((wrptr & reg->evt_rollover_ind) == - (card->evtbd_rdptr & reg->evt_rollover_ind))) { - struct sk_buff *skb_cmd; - __le16 data_len = 0; - u16 evt_len; - - mwifiex_dbg(adapter, INFO, - "info: Read Index: %d\n", rdptr); - skb_cmd = card->evt_buf_list[rdptr]; - mwifiex_unmap_pci_memory(adapter, skb_cmd, PCI_DMA_FROMDEVICE); - - /* Take the pointer and set it to event pointer in adapter - and will return back after event handling callback */ - card->evt_buf_list[rdptr] = NULL; - desc = card->evtbd_ring[rdptr]; - memset(desc, 0, sizeof(*desc)); - - event = *(u32 *) &skb_cmd->data[INTF_HEADER_LEN]; - adapter->event_cause = event; - /* The first 4bytes will be the event transfer header - len is 2 bytes followed by type which is 2 bytes */ - memcpy(&data_len, skb_cmd->data, sizeof(__le16)); - evt_len = le16_to_cpu(data_len); - skb_trim(skb_cmd, evt_len); - skb_pull(skb_cmd, INTF_HEADER_LEN); - mwifiex_dbg(adapter, EVENT, - "info: Event length: %d\n", evt_len); - - if ((evt_len > 0) && (evt_len < MAX_EVENT_SIZE)) - memcpy(adapter->event_body, skb_cmd->data + - MWIFIEX_EVENT_HEADER_LEN, evt_len - - MWIFIEX_EVENT_HEADER_LEN); - - adapter->event_received = true; - adapter->event_skb = skb_cmd; - - /* Do not update the event read pointer here, wait till the - buffer is released. This is just to make things simpler, - we need to find a better method of managing these buffers. - */ - } else { - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_EVENT_DONE)) { - mwifiex_dbg(adapter, ERROR, - "Write register failed\n"); - return -1; - } - } - - return 0; -} - -/* - * Event processing complete handler - */ -static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - int ret = 0; - u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; - u32 wrptr; - struct mwifiex_evt_buf_desc *desc; - - if (!skb) - return 0; - - if (rdptr >= MWIFIEX_MAX_EVT_BD) { - mwifiex_dbg(adapter, ERROR, - "event_complete: Invalid rdptr 0x%x\n", - rdptr); - return -EINVAL; - } - - /* Read the event ring write pointer set by firmware */ - if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) { - mwifiex_dbg(adapter, ERROR, - "event_complete: failed to read reg->evt_wrptr\n"); - return -1; - } - - if (!card->evt_buf_list[rdptr]) { - skb_push(skb, INTF_HEADER_LEN); - skb_put(skb, MAX_EVENT_SIZE - skb->len); - if (mwifiex_map_pci_memory(adapter, skb, - MAX_EVENT_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - card->evt_buf_list[rdptr] = skb; - desc = card->evtbd_ring[rdptr]; - desc->paddr = MWIFIEX_SKB_DMA_ADDR(skb); - desc->len = (u16)skb->len; - desc->flags = 0; - skb = NULL; - } else { - mwifiex_dbg(adapter, ERROR, - "info: ERROR: buf still valid at index %d, <%p, %p>\n", - rdptr, card->evt_buf_list[rdptr], skb); - } - - if ((++card->evtbd_rdptr & MWIFIEX_EVTBD_MASK) == MWIFIEX_MAX_EVT_BD) { - card->evtbd_rdptr = ((card->evtbd_rdptr & - reg->evt_rollover_ind) ^ - reg->evt_rollover_ind); - } - - mwifiex_dbg(adapter, EVENT, - "info: Updated ", - card->evtbd_rdptr, wrptr); - - /* Write the event ring read pointer in to reg->evt_rdptr */ - if (mwifiex_write_reg(adapter, reg->evt_rdptr, - card->evtbd_rdptr)) { - mwifiex_dbg(adapter, ERROR, - "event_complete: failed to read reg->evt_rdptr\n"); - return -1; - } - - mwifiex_dbg(adapter, EVENT, - "info: Check Events Again\n"); - ret = mwifiex_pcie_process_event_ready(adapter); - - return ret; -} - -/* - * This function downloads the firmware to the card. - * - * Firmware is downloaded to the card in blocks. Every block download - * is tested for CRC errors, and retried a number of times before - * returning failure. - */ -static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, - struct mwifiex_fw_image *fw) -{ - int ret; - u8 *firmware = fw->fw_buf; - u32 firmware_len = fw->fw_len; - u32 offset = 0; - struct sk_buff *skb; - u32 txlen, tx_blocks = 0, tries, len; - u32 block_retry_cnt = 0; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - if (!firmware || !firmware_len) { - mwifiex_dbg(adapter, ERROR, - "No firmware image found! Terminating download\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "info: Downloading FW image (%d bytes)\n", - firmware_len); - - if (mwifiex_pcie_disable_host_int(adapter)) { - mwifiex_dbg(adapter, ERROR, - "%s: Disabling interrupts failed.\n", __func__); - return -1; - } - - skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); - if (!skb) { - ret = -ENOMEM; - goto done; - } - - /* Perform firmware data transfer */ - do { - u32 ireg_intr = 0; - - /* More data? */ - if (offset >= firmware_len) - break; - - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - ret = mwifiex_read_reg(adapter, reg->cmd_size, - &len); - if (ret) { - mwifiex_dbg(adapter, FATAL, - "Failed reading len from boot code\n"); - goto done; - } - if (len) - break; - usleep_range(10, 20); - } - - if (!len) { - break; - } else if (len > MWIFIEX_UPLD_SIZE) { - mwifiex_dbg(adapter, ERROR, - "FW download failure @ %d, invalid length %d\n", - offset, len); - ret = -1; - goto done; - } - - txlen = len; - - if (len & BIT(0)) { - block_retry_cnt++; - if (block_retry_cnt > MAX_WRITE_IOMEM_RETRY) { - mwifiex_dbg(adapter, ERROR, - "FW download failure @ %d, over max\t" - "retry count\n", offset); - ret = -1; - goto done; - } - mwifiex_dbg(adapter, ERROR, - "FW CRC error indicated by the\t" - "helper: len = 0x%04X, txlen = %d\n", - len, txlen); - len &= ~BIT(0); - /* Setting this to 0 to resend from same offset */ - txlen = 0; - } else { - block_retry_cnt = 0; - /* Set blocksize to transfer - checking for - last block */ - if (firmware_len - offset < txlen) - txlen = firmware_len - offset; - - mwifiex_dbg(adapter, INFO, "."); - - tx_blocks = (txlen + card->pcie.blksz_fw_dl - 1) / - card->pcie.blksz_fw_dl; - - /* Copy payload to buffer */ - memmove(skb->data, &firmware[offset], txlen); - } - - skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); - skb_trim(skb, tx_blocks * card->pcie.blksz_fw_dl); - - /* Send the boot command to device */ - if (mwifiex_pcie_send_boot_cmd(adapter, skb)) { - mwifiex_dbg(adapter, ERROR, - "Failed to send firmware download command\n"); - ret = -1; - goto done; - } - - /* Wait for the command done interrupt */ - do { - if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS, - &ireg_intr)) { - mwifiex_dbg(adapter, ERROR, - "%s: Failed to read\t" - "interrupt status during fw dnld.\n", - __func__); - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_TODEVICE); - ret = -1; - goto done; - } - } while ((ireg_intr & CPU_INTR_DOOR_BELL) == - CPU_INTR_DOOR_BELL); - - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - - offset += txlen; - } while (true); - - mwifiex_dbg(adapter, MSG, - "info: FW download over, size %d bytes\n", offset); - - ret = 0; - -done: - dev_kfree_skb_any(skb); - return ret; -} - -/* - * This function checks the firmware status in card. - * - * The winner interface is also determined by this function. - */ -static int -mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) -{ - int ret = 0; - u32 firmware_stat, winner_status; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - u32 tries; - - /* Mask spurios interrupts */ - if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS_MASK, - HOST_INTR_MASK)) { - mwifiex_dbg(adapter, ERROR, - "Write register failed\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "Setting driver ready signature\n"); - if (mwifiex_write_reg(adapter, reg->drv_rdy, - FIRMWARE_READY_PCIE)) { - mwifiex_dbg(adapter, ERROR, - "Failed to write driver ready signature\n"); - return -1; - } - - /* Wait for firmware initialization event */ - for (tries = 0; tries < poll_num; tries++) { - if (mwifiex_read_reg(adapter, reg->fw_status, - &firmware_stat)) - ret = -1; - else - ret = 0; - if (ret) - continue; - if (firmware_stat == FIRMWARE_READY_PCIE) { - ret = 0; - break; - } else { - msleep(100); - ret = -1; - } - } - - if (ret) { - if (mwifiex_read_reg(adapter, reg->fw_status, - &winner_status)) - ret = -1; - else if (!winner_status) { - mwifiex_dbg(adapter, INFO, - "PCI-E is the winner\n"); - adapter->winner = 1; - } else { - mwifiex_dbg(adapter, ERROR, - "PCI-E is not the winner <%#x,%d>, exit dnld\n", - ret, adapter->winner); - } - } - - return ret; -} - -/* - * This function reads the interrupt status from card. - */ -static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) -{ - u32 pcie_ireg; - unsigned long flags; - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - return; - - if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, &pcie_ireg)) { - mwifiex_dbg(adapter, ERROR, "Read register failed\n"); - return; - } - - if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { - - mwifiex_pcie_disable_host_int(adapter); - - /* Clear the pending interrupts */ - if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS, - ~pcie_ireg)) { - mwifiex_dbg(adapter, ERROR, - "Write register failed\n"); - return; - } - spin_lock_irqsave(&adapter->int_lock, flags); - adapter->int_status |= pcie_ireg; - spin_unlock_irqrestore(&adapter->int_lock, flags); - - if (!adapter->pps_uapsd_mode && - adapter->ps_state == PS_STATE_SLEEP && - mwifiex_pcie_ok_to_access_hw(adapter)) { - /* Potentially for PCIe we could get other - * interrupts like shared. Don't change power - * state until cookie is set */ - adapter->ps_state = PS_STATE_AWAKE; - adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); - } - } -} - -/* - * Interrupt handler for PCIe root port - * - * This function reads the interrupt status from firmware and assigns - * the main process in workqueue which will handle the interrupt. - */ -static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) -{ - struct pci_dev *pdev = (struct pci_dev *)context; - struct pcie_service_card *card; - struct mwifiex_adapter *adapter; - - if (!pdev) { - pr_debug("info: %s: pdev is NULL\n", (u8 *)pdev); - goto exit; - } - - card = pci_get_drvdata(pdev); - if (!card || !card->adapter) { - pr_debug("info: %s: card=%p adapter=%p\n", __func__, card, - card ? card->adapter : NULL); - goto exit; - } - adapter = card->adapter; - - if (adapter->surprise_removed) - goto exit; - - mwifiex_interrupt_status(adapter); - mwifiex_queue_main_work(adapter); - -exit: - return IRQ_HANDLED; -} - -/* - * This function checks the current interrupt status. - * - * The following interrupts are checked and handled by this function - - * - Data sent - * - Command sent - * - Command received - * - Packets received - * - Events received - * - * In case of Rx packets received, the packets are uploaded from card to - * host and processed accordingly. - */ -static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) -{ - int ret; - u32 pcie_ireg; - unsigned long flags; - - spin_lock_irqsave(&adapter->int_lock, flags); - /* Clear out unused interrupts */ - pcie_ireg = adapter->int_status; - adapter->int_status = 0; - spin_unlock_irqrestore(&adapter->int_lock, flags); - - while (pcie_ireg & HOST_INTR_MASK) { - if (pcie_ireg & HOST_INTR_DNLD_DONE) { - pcie_ireg &= ~HOST_INTR_DNLD_DONE; - mwifiex_dbg(adapter, INTR, - "info: TX DNLD Done\n"); - ret = mwifiex_pcie_send_data_complete(adapter); - if (ret) - return ret; - } - if (pcie_ireg & HOST_INTR_UPLD_RDY) { - pcie_ireg &= ~HOST_INTR_UPLD_RDY; - mwifiex_dbg(adapter, INTR, - "info: Rx DATA\n"); - ret = mwifiex_pcie_process_recv_data(adapter); - if (ret) - return ret; - } - if (pcie_ireg & HOST_INTR_EVENT_RDY) { - pcie_ireg &= ~HOST_INTR_EVENT_RDY; - mwifiex_dbg(adapter, INTR, - "info: Rx EVENT\n"); - ret = mwifiex_pcie_process_event_ready(adapter); - if (ret) - return ret; - } - - if (pcie_ireg & HOST_INTR_CMD_DONE) { - pcie_ireg &= ~HOST_INTR_CMD_DONE; - if (adapter->cmd_sent) { - mwifiex_dbg(adapter, INTR, - "info: CMD sent Interrupt\n"); - adapter->cmd_sent = false; - } - /* Handle command response */ - ret = mwifiex_pcie_process_cmd_complete(adapter); - if (ret) - return ret; - } - - if (mwifiex_pcie_ok_to_access_hw(adapter)) { - if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, - &pcie_ireg)) { - mwifiex_dbg(adapter, ERROR, - "Read register failed\n"); - return -1; - } - - if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { - if (mwifiex_write_reg(adapter, - PCIE_HOST_INT_STATUS, - ~pcie_ireg)) { - mwifiex_dbg(adapter, ERROR, - "Write register failed\n"); - return -1; - } - } - - } - } - mwifiex_dbg(adapter, INTR, - "info: cmd_sent=%d data_sent=%d\n", - adapter->cmd_sent, adapter->data_sent); - if (adapter->ps_state != PS_STATE_SLEEP) - mwifiex_pcie_enable_host_int(adapter); - - return 0; -} - -/* - * This function downloads data from driver to card. - * - * Both commands and data packets are transferred to the card by this - * function. - * - * This function adds the PCIE specific header to the front of the buffer - * before transferring. The header contains the length of the packet and - * the type. The firmware handles the packets based upon this set type. - */ -static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type, - struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - if (!skb) { - mwifiex_dbg(adapter, ERROR, - "Passed NULL skb to %s\n", __func__); - return -1; - } - - if (type == MWIFIEX_TYPE_DATA) - return mwifiex_pcie_send_data(adapter, skb, tx_param); - else if (type == MWIFIEX_TYPE_CMD) - return mwifiex_pcie_send_cmd(adapter, skb); - - return 0; -} - -/* This function read/write firmware */ -static enum rdwr_status -mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag) -{ - int ret, tries; - u8 ctrl_data; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "PCIE write err\n"); - return RDWR_STATUS_FAILURE; - } - - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data); - if (ctrl_data == FW_DUMP_DONE) - return RDWR_STATUS_SUCCESS; - if (doneflag && ctrl_data == doneflag) - return RDWR_STATUS_DONE; - if (ctrl_data != FW_DUMP_HOST_READY) { - mwifiex_dbg(adapter, WARN, - "The ctrl reg was changed, re-try again!\n"); - ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, - FW_DUMP_HOST_READY); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "PCIE write err\n"); - return RDWR_STATUS_FAILURE; - } - } - usleep_range(100, 200); - } - - mwifiex_dbg(adapter, ERROR, "Fail to pull ctrl_data\n"); - return RDWR_STATUS_FAILURE; -} - -/* This function dump firmware memory to file */ -static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *creg = card->pcie.reg; - unsigned int reg, reg_start, reg_end; - u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; - enum rdwr_status stat; - u32 memory_size; - int ret; - - if (!card->pcie.can_dump_fw) - return; - - for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { - struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - } - - mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump start ==\n"); - - /* Read the number of the memories which will dump */ - stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); - if (stat == RDWR_STATUS_FAILURE) - return; - - reg = creg->fw_dump_start; - mwifiex_read_reg_byte(adapter, reg, &dump_num); - - /* Read the length of every memory which will dump */ - for (idx = 0; idx < dump_num; idx++) { - struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; - - stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); - if (stat == RDWR_STATUS_FAILURE) - return; - - memory_size = 0; - reg = creg->fw_dump_start; - for (i = 0; i < 4; i++) { - mwifiex_read_reg_byte(adapter, reg, &read_reg); - memory_size |= (read_reg << (i * 8)); - reg++; - } - - if (memory_size == 0) { - mwifiex_dbg(adapter, MSG, "Firmware dump Finished!\n"); - ret = mwifiex_write_reg(adapter, creg->fw_dump_ctrl, - FW_DUMP_READ_DONE); - if (ret) { - mwifiex_dbg(adapter, ERROR, "PCIE write err\n"); - return; - } - break; - } - - mwifiex_dbg(adapter, DUMP, - "%s_SIZE=0x%x\n", entry->mem_name, memory_size); - entry->mem_ptr = vmalloc(memory_size + 1); - entry->mem_size = memory_size; - if (!entry->mem_ptr) { - mwifiex_dbg(adapter, ERROR, - "Vmalloc %s failed\n", entry->mem_name); - return; - } - dbg_ptr = entry->mem_ptr; - end_ptr = dbg_ptr + memory_size; - - doneflag = entry->done_flag; - mwifiex_dbg(adapter, DUMP, "Start %s output, please wait...\n", - entry->mem_name); - - do { - stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); - if (RDWR_STATUS_FAILURE == stat) - return; - - reg_start = creg->fw_dump_start; - reg_end = creg->fw_dump_end; - for (reg = reg_start; reg <= reg_end; reg++) { - mwifiex_read_reg_byte(adapter, reg, dbg_ptr); - if (dbg_ptr < end_ptr) { - dbg_ptr++; - } else { - mwifiex_dbg(adapter, ERROR, - "Allocated buf not enough\n"); - return; - } - } - - if (stat != RDWR_STATUS_DONE) - continue; - - mwifiex_dbg(adapter, DUMP, - "%s done: size=0x%tx\n", - entry->mem_name, dbg_ptr - entry->mem_ptr); - break; - } while (true); - } - mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump end ==\n"); -} - -static void mwifiex_pcie_device_dump_work(struct mwifiex_adapter *adapter) -{ - mwifiex_drv_info_dump(adapter); - mwifiex_pcie_fw_dump(adapter); - mwifiex_upload_device_dump(adapter); -} - -static unsigned long iface_work_flags; -static struct mwifiex_adapter *save_adapter; -static void mwifiex_pcie_work(struct work_struct *work) -{ - if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, - &iface_work_flags)) - mwifiex_pcie_device_dump_work(save_adapter); -} - -static DECLARE_WORK(pcie_work, mwifiex_pcie_work); -/* This function dumps FW information */ -static void mwifiex_pcie_device_dump(struct mwifiex_adapter *adapter) -{ - save_adapter = adapter; - if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags)) - return; - - set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags); - - schedule_work(&pcie_work); -} - -/* - * This function initializes the PCI-E host memory space, WCB rings, etc. - * - * The following initializations steps are followed - - * - Allocate TXBD ring buffers - * - Allocate RXBD ring buffers - * - Allocate event BD ring buffers - * - Allocate command response ring buffer - * - Allocate sleep cookie buffer - */ -static int mwifiex_pcie_init(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - int ret; - struct pci_dev *pdev = card->dev; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - pci_set_drvdata(pdev, card); - - ret = pci_enable_device(pdev); - if (ret) - goto err_enable_dev; - - pci_set_master(pdev); - - mwifiex_dbg(adapter, INFO, - "try set_consistent_dma_mask(32)\n"); - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "set_dma_mask(32) failed\n"); - goto err_set_dma_mask; - } - - ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "set_consistent_dma_mask(64) failed\n"); - goto err_set_dma_mask; - } - - ret = pci_request_region(pdev, 0, DRV_NAME); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "req_reg(0) error\n"); - goto err_req_region0; - } - card->pci_mmap = pci_iomap(pdev, 0, 0); - if (!card->pci_mmap) { - mwifiex_dbg(adapter, ERROR, "iomap(0) error\n"); - ret = -EIO; - goto err_iomap0; - } - ret = pci_request_region(pdev, 2, DRV_NAME); - if (ret) { - mwifiex_dbg(adapter, ERROR, "req_reg(2) error\n"); - goto err_req_region2; - } - card->pci_mmap1 = pci_iomap(pdev, 2, 0); - if (!card->pci_mmap1) { - mwifiex_dbg(adapter, ERROR, - "iomap(2) error\n"); - ret = -EIO; - goto err_iomap2; - } - - mwifiex_dbg(adapter, INFO, - "PCI memory map Virt0: %p PCI memory map Virt2: %p\n", - card->pci_mmap, card->pci_mmap1); - - card->cmdrsp_buf = NULL; - ret = mwifiex_pcie_create_txbd_ring(adapter); - if (ret) - goto err_cre_txbd; - ret = mwifiex_pcie_create_rxbd_ring(adapter); - if (ret) - goto err_cre_rxbd; - ret = mwifiex_pcie_create_evtbd_ring(adapter); - if (ret) - goto err_cre_evtbd; - ret = mwifiex_pcie_alloc_cmdrsp_buf(adapter); - if (ret) - goto err_alloc_cmdbuf; - if (reg->sleep_cookie) { - ret = mwifiex_pcie_alloc_sleep_cookie_buf(adapter); - if (ret) - goto err_alloc_cookie; - } else { - card->sleep_cookie_vbase = NULL; - } - return ret; - -err_alloc_cookie: - mwifiex_pcie_delete_cmdrsp_buf(adapter); -err_alloc_cmdbuf: - mwifiex_pcie_delete_evtbd_ring(adapter); -err_cre_evtbd: - mwifiex_pcie_delete_rxbd_ring(adapter); -err_cre_rxbd: - mwifiex_pcie_delete_txbd_ring(adapter); -err_cre_txbd: - pci_iounmap(pdev, card->pci_mmap1); -err_iomap2: - pci_release_region(pdev, 2); -err_req_region2: - pci_iounmap(pdev, card->pci_mmap); -err_iomap0: - pci_release_region(pdev, 0); -err_req_region0: -err_set_dma_mask: - pci_disable_device(pdev); -err_enable_dev: - pci_set_drvdata(pdev, NULL); - return ret; -} - -/* - * This function cleans up the allocated card buffers. - * - * The following are freed by this function - - * - TXBD ring buffers - * - RXBD ring buffers - * - Event BD ring buffers - * - Command response ring buffer - * - Sleep cookie buffer - */ -static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - struct pci_dev *pdev = card->dev; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - if (user_rmmod) { - mwifiex_dbg(adapter, INFO, - "Clearing driver ready signature\n"); - if (mwifiex_write_reg(adapter, reg->drv_rdy, 0x00000000)) - mwifiex_dbg(adapter, ERROR, - "Failed to write driver not-ready signature\n"); - } - - if (pdev) { - pci_iounmap(pdev, card->pci_mmap); - pci_iounmap(pdev, card->pci_mmap1); - pci_disable_device(pdev); - pci_release_region(pdev, 2); - pci_release_region(pdev, 0); - pci_set_drvdata(pdev, NULL); - } - kfree(card); -} - -/* - * This function registers the PCIE device. - * - * PCIE IRQ is claimed, block size is set and driver data is initialized. - */ -static int mwifiex_register_dev(struct mwifiex_adapter *adapter) -{ - int ret; - struct pcie_service_card *card = adapter->card; - struct pci_dev *pdev = card->dev; - - /* save adapter pointer in card */ - card->adapter = adapter; - - ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED, - "MRVL_PCIE", pdev); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "request_irq failed: ret=%d\n", ret); - adapter->card = NULL; - return -1; - } - - adapter->dev = &pdev->dev; - adapter->tx_buf_size = card->pcie.tx_buf_size; - adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; - adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); - strcpy(adapter->fw_name, card->pcie.firmware); - adapter->ext_scan = card->pcie.can_ext_scan; - - return 0; -} - -/* - * This function unregisters the PCIE device. - * - * The PCIE IRQ is released, the function is disabled and driver - * data is set to null. - */ -static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg; - - if (card) { - mwifiex_dbg(adapter, INFO, - "%s(): calling free_irq()\n", __func__); - free_irq(card->dev->irq, card->dev); - - reg = card->pcie.reg; - if (reg->sleep_cookie) - mwifiex_pcie_delete_sleep_cookie_buf(adapter); - - mwifiex_pcie_delete_cmdrsp_buf(adapter); - mwifiex_pcie_delete_evtbd_ring(adapter); - mwifiex_pcie_delete_rxbd_ring(adapter); - mwifiex_pcie_delete_txbd_ring(adapter); - card->cmdrsp_buf = NULL; - } -} - -static struct mwifiex_if_ops pcie_ops = { - .init_if = mwifiex_pcie_init, - .cleanup_if = mwifiex_pcie_cleanup, - .check_fw_status = mwifiex_check_fw_status, - .prog_fw = mwifiex_prog_fw_w_helper, - .register_dev = mwifiex_register_dev, - .unregister_dev = mwifiex_unregister_dev, - .enable_int = mwifiex_pcie_enable_host_int, - .process_int_status = mwifiex_process_int_status, - .host_to_card = mwifiex_pcie_host_to_card, - .wakeup = mwifiex_pm_wakeup_card, - .wakeup_complete = mwifiex_pm_wakeup_card_complete, - - /* PCIE specific */ - .cmdrsp_complete = mwifiex_pcie_cmdrsp_complete, - .event_complete = mwifiex_pcie_event_complete, - .update_mp_end_port = NULL, - .cleanup_mpa_buf = NULL, - .init_fw_port = mwifiex_pcie_init_fw_port, - .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, - .device_dump = mwifiex_pcie_device_dump, -}; - -/* - * This function initializes the PCIE driver module. - * - * This initiates the semaphore and registers the device with - * PCIE bus. - */ -static int mwifiex_pcie_init_module(void) -{ - int ret; - - pr_debug("Marvell PCIe Driver\n"); - - sema_init(&add_remove_card_sem, 1); - - /* Clear the flag in case user removes the card. */ - user_rmmod = 0; - - ret = pci_register_driver(&mwifiex_pcie); - if (ret) - pr_err("Driver register failed!\n"); - else - pr_debug("info: Driver registered successfully!\n"); - - return ret; -} - -/* - * This function cleans up the PCIE driver. - * - * The following major steps are followed for cleanup - - * - Resume the device if its suspended - * - Disconnect the device if connected - * - Shutdown the firmware - * - Unregister the device from PCIE bus. - */ -static void mwifiex_pcie_cleanup_module(void) -{ - if (!down_interruptible(&add_remove_card_sem)) - up(&add_remove_card_sem); - - /* Set the flag as user is removing this module. */ - user_rmmod = 1; - - cancel_work_sync(&pcie_work); - pci_unregister_driver(&mwifiex_pcie); -} - -module_init(mwifiex_pcie_init_module); -module_exit(mwifiex_pcie_cleanup_module); - -MODULE_AUTHOR("Marvell International Ltd."); -MODULE_DESCRIPTION("Marvell WiFi-Ex PCI-Express Driver version " PCIE_VERSION); -MODULE_VERSION(PCIE_VERSION); -MODULE_LICENSE("GPL v2"); -MODULE_FIRMWARE(PCIE8766_DEFAULT_FW_NAME); -MODULE_FIRMWARE(PCIE8897_DEFAULT_FW_NAME); -MODULE_FIRMWARE(PCIE8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h deleted file mode 100644 index 48e549c3b285..000000000000 --- a/drivers/net/wireless/mwifiex/pcie.h +++ /dev/null @@ -1,382 +0,0 @@ -/* @file mwifiex_pcie.h - * - * @brief This file contains definitions for PCI-E interface. - * driver. - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_PCIE_H -#define _MWIFIEX_PCIE_H - -#include -#include -#include - -#include "main.h" - -#define PCIE8766_DEFAULT_FW_NAME "mrvl/pcie8766_uapsta.bin" -#define PCIE8897_DEFAULT_FW_NAME "mrvl/pcie8897_uapsta.bin" -#define PCIE8997_DEFAULT_FW_NAME "mrvl/pcie8997_uapsta.bin" - -#define PCIE_VENDOR_ID_MARVELL (0x11ab) -#define PCIE_DEVICE_ID_MARVELL_88W8766P (0x2b30) -#define PCIE_DEVICE_ID_MARVELL_88W8897 (0x2b38) -#define PCIE_DEVICE_ID_MARVELL_88W8997 (0x2b42) - -/* Constants for Buffer Descriptor (BD) rings */ -#define MWIFIEX_MAX_TXRX_BD 0x20 -#define MWIFIEX_TXBD_MASK 0x3F -#define MWIFIEX_RXBD_MASK 0x3F - -#define MWIFIEX_MAX_EVT_BD 0x08 -#define MWIFIEX_EVTBD_MASK 0x0f - -/* PCIE INTERNAL REGISTERS */ -#define PCIE_SCRATCH_0_REG 0xC10 -#define PCIE_SCRATCH_1_REG 0xC14 -#define PCIE_CPU_INT_EVENT 0xC18 -#define PCIE_CPU_INT_STATUS 0xC1C -#define PCIE_HOST_INT_STATUS 0xC30 -#define PCIE_HOST_INT_MASK 0xC34 -#define PCIE_HOST_INT_STATUS_MASK 0xC3C -#define PCIE_SCRATCH_2_REG 0xC40 -#define PCIE_SCRATCH_3_REG 0xC44 -#define PCIE_SCRATCH_4_REG 0xCD0 -#define PCIE_SCRATCH_5_REG 0xCD4 -#define PCIE_SCRATCH_6_REG 0xCD8 -#define PCIE_SCRATCH_7_REG 0xCDC -#define PCIE_SCRATCH_8_REG 0xCE0 -#define PCIE_SCRATCH_9_REG 0xCE4 -#define PCIE_SCRATCH_10_REG 0xCE8 -#define PCIE_SCRATCH_11_REG 0xCEC -#define PCIE_SCRATCH_12_REG 0xCF0 -#define PCIE_RD_DATA_PTR_Q0_Q1 0xC08C -#define PCIE_WR_DATA_PTR_Q0_Q1 0xC05C - -#define CPU_INTR_DNLD_RDY BIT(0) -#define CPU_INTR_DOOR_BELL BIT(1) -#define CPU_INTR_SLEEP_CFM_DONE BIT(2) -#define CPU_INTR_RESET BIT(3) -#define CPU_INTR_EVENT_DONE BIT(5) - -#define HOST_INTR_DNLD_DONE BIT(0) -#define HOST_INTR_UPLD_RDY BIT(1) -#define HOST_INTR_CMD_DONE BIT(2) -#define HOST_INTR_EVENT_RDY BIT(3) -#define HOST_INTR_MASK (HOST_INTR_DNLD_DONE | \ - HOST_INTR_UPLD_RDY | \ - HOST_INTR_CMD_DONE | \ - HOST_INTR_EVENT_RDY) - -#define MWIFIEX_BD_FLAG_ROLLOVER_IND BIT(7) -#define MWIFIEX_BD_FLAG_FIRST_DESC BIT(0) -#define MWIFIEX_BD_FLAG_LAST_DESC BIT(1) -#define MWIFIEX_BD_FLAG_SOP BIT(0) -#define MWIFIEX_BD_FLAG_EOP BIT(1) -#define MWIFIEX_BD_FLAG_XS_SOP BIT(2) -#define MWIFIEX_BD_FLAG_XS_EOP BIT(3) -#define MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND BIT(7) -#define MWIFIEX_BD_FLAG_RX_ROLLOVER_IND BIT(10) -#define MWIFIEX_BD_FLAG_TX_START_PTR BIT(16) -#define MWIFIEX_BD_FLAG_TX_ROLLOVER_IND BIT(26) - -/* Max retry number of command write */ -#define MAX_WRITE_IOMEM_RETRY 2 -/* Define PCIE block size for firmware download */ -#define MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD 256 -/* FW awake cookie after FW ready */ -#define FW_AWAKE_COOKIE (0xAA55AA55) -#define MWIFIEX_DEF_SLEEP_COOKIE 0xBEEFBEEF -#define MWIFIEX_MAX_DELAY_COUNT 5 - -struct mwifiex_pcie_card_reg { - u16 cmd_addr_lo; - u16 cmd_addr_hi; - u16 fw_status; - u16 cmd_size; - u16 cmdrsp_addr_lo; - u16 cmdrsp_addr_hi; - u16 tx_rdptr; - u16 tx_wrptr; - u16 rx_rdptr; - u16 rx_wrptr; - u16 evt_rdptr; - u16 evt_wrptr; - u16 drv_rdy; - u16 tx_start_ptr; - u32 tx_mask; - u32 tx_wrap_mask; - u32 rx_mask; - u32 rx_wrap_mask; - u32 tx_rollover_ind; - u32 rx_rollover_ind; - u32 evt_rollover_ind; - u8 ring_flag_sop; - u8 ring_flag_eop; - u8 ring_flag_xs_sop; - u8 ring_flag_xs_eop; - u32 ring_tx_start_ptr; - u8 pfu_enabled; - u8 sleep_cookie; - u16 fw_dump_ctrl; - u16 fw_dump_start; - u16 fw_dump_end; -}; - -static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { - .cmd_addr_lo = PCIE_SCRATCH_0_REG, - .cmd_addr_hi = PCIE_SCRATCH_1_REG, - .cmd_size = PCIE_SCRATCH_2_REG, - .fw_status = PCIE_SCRATCH_3_REG, - .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, - .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, - .tx_rdptr = PCIE_SCRATCH_6_REG, - .tx_wrptr = PCIE_SCRATCH_7_REG, - .rx_rdptr = PCIE_SCRATCH_8_REG, - .rx_wrptr = PCIE_SCRATCH_9_REG, - .evt_rdptr = PCIE_SCRATCH_10_REG, - .evt_wrptr = PCIE_SCRATCH_11_REG, - .drv_rdy = PCIE_SCRATCH_12_REG, - .tx_start_ptr = 0, - .tx_mask = MWIFIEX_TXBD_MASK, - .tx_wrap_mask = 0, - .rx_mask = MWIFIEX_RXBD_MASK, - .rx_wrap_mask = 0, - .tx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, - .rx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, - .evt_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, - .ring_flag_sop = 0, - .ring_flag_eop = 0, - .ring_flag_xs_sop = 0, - .ring_flag_xs_eop = 0, - .ring_tx_start_ptr = 0, - .pfu_enabled = 0, - .sleep_cookie = 1, -}; - -static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = { - .cmd_addr_lo = PCIE_SCRATCH_0_REG, - .cmd_addr_hi = PCIE_SCRATCH_1_REG, - .cmd_size = PCIE_SCRATCH_2_REG, - .fw_status = PCIE_SCRATCH_3_REG, - .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, - .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, - .tx_rdptr = PCIE_RD_DATA_PTR_Q0_Q1, - .tx_wrptr = PCIE_WR_DATA_PTR_Q0_Q1, - .rx_rdptr = PCIE_WR_DATA_PTR_Q0_Q1, - .rx_wrptr = PCIE_RD_DATA_PTR_Q0_Q1, - .evt_rdptr = PCIE_SCRATCH_10_REG, - .evt_wrptr = PCIE_SCRATCH_11_REG, - .drv_rdy = PCIE_SCRATCH_12_REG, - .tx_start_ptr = 16, - .tx_mask = 0x03FF0000, - .tx_wrap_mask = 0x07FF0000, - .rx_mask = 0x000003FF, - .rx_wrap_mask = 0x000007FF, - .tx_rollover_ind = MWIFIEX_BD_FLAG_TX_ROLLOVER_IND, - .rx_rollover_ind = MWIFIEX_BD_FLAG_RX_ROLLOVER_IND, - .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND, - .ring_flag_sop = MWIFIEX_BD_FLAG_SOP, - .ring_flag_eop = MWIFIEX_BD_FLAG_EOP, - .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP, - .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP, - .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, - .pfu_enabled = 1, - .sleep_cookie = 0, - .fw_dump_ctrl = 0xcf4, - .fw_dump_start = 0xcf8, - .fw_dump_end = 0xcff, -}; - -static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = { - .cmd_addr_lo = PCIE_SCRATCH_0_REG, - .cmd_addr_hi = PCIE_SCRATCH_1_REG, - .cmd_size = PCIE_SCRATCH_2_REG, - .fw_status = PCIE_SCRATCH_3_REG, - .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, - .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, - .tx_rdptr = 0xC1A4, - .tx_wrptr = 0xC1A8, - .rx_rdptr = 0xC1A8, - .rx_wrptr = 0xC1A4, - .evt_rdptr = PCIE_SCRATCH_10_REG, - .evt_wrptr = PCIE_SCRATCH_11_REG, - .drv_rdy = PCIE_SCRATCH_12_REG, - .tx_start_ptr = 16, - .tx_mask = 0x0FFF0000, - .tx_wrap_mask = 0x01FF0000, - .rx_mask = 0x00000FFF, - .rx_wrap_mask = 0x000001FF, - .tx_rollover_ind = BIT(28), - .rx_rollover_ind = BIT(12), - .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND, - .ring_flag_sop = MWIFIEX_BD_FLAG_SOP, - .ring_flag_eop = MWIFIEX_BD_FLAG_EOP, - .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP, - .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP, - .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, - .pfu_enabled = 1, - .sleep_cookie = 0, -}; - -struct mwifiex_pcie_device { - const char *firmware; - const struct mwifiex_pcie_card_reg *reg; - u16 blksz_fw_dl; - u16 tx_buf_size; - bool can_dump_fw; - bool can_ext_scan; -}; - -static const struct mwifiex_pcie_device mwifiex_pcie8766 = { - .firmware = PCIE8766_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_8766, - .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .can_dump_fw = false, - .can_ext_scan = true, -}; - -static const struct mwifiex_pcie_device mwifiex_pcie8897 = { - .firmware = PCIE8897_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_8897, - .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, - .can_dump_fw = true, - .can_ext_scan = true, -}; - -static const struct mwifiex_pcie_device mwifiex_pcie8997 = { - .firmware = PCIE8997_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_8997, - .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, - .can_dump_fw = false, - .can_ext_scan = true, -}; - -struct mwifiex_evt_buf_desc { - u64 paddr; - u16 len; - u16 flags; -} __packed; - -struct mwifiex_pcie_buf_desc { - u64 paddr; - u16 len; - u16 flags; -} __packed; - -struct mwifiex_pfu_buf_desc { - u16 flags; - u16 offset; - u16 frag_len; - u16 len; - u64 paddr; - u32 reserved; -} __packed; - -struct pcie_service_card { - struct pci_dev *dev; - struct mwifiex_adapter *adapter; - struct mwifiex_pcie_device pcie; - - u8 txbd_flush; - u32 txbd_wrptr; - u32 txbd_rdptr; - u32 txbd_ring_size; - u8 *txbd_ring_vbase; - dma_addr_t txbd_ring_pbase; - void *txbd_ring[MWIFIEX_MAX_TXRX_BD]; - struct sk_buff *tx_buf_list[MWIFIEX_MAX_TXRX_BD]; - - u32 rxbd_wrptr; - u32 rxbd_rdptr; - u32 rxbd_ring_size; - u8 *rxbd_ring_vbase; - dma_addr_t rxbd_ring_pbase; - void *rxbd_ring[MWIFIEX_MAX_TXRX_BD]; - struct sk_buff *rx_buf_list[MWIFIEX_MAX_TXRX_BD]; - - u32 evtbd_wrptr; - u32 evtbd_rdptr; - u32 evtbd_ring_size; - u8 *evtbd_ring_vbase; - dma_addr_t evtbd_ring_pbase; - void *evtbd_ring[MWIFIEX_MAX_EVT_BD]; - struct sk_buff *evt_buf_list[MWIFIEX_MAX_EVT_BD]; - - struct sk_buff *cmd_buf; - struct sk_buff *cmdrsp_buf; - u8 *sleep_cookie_vbase; - dma_addr_t sleep_cookie_pbase; - void __iomem *pci_mmap; - void __iomem *pci_mmap1; -}; - -static inline int -mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr) -{ - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - switch (card->dev->device) { - case PCIE_DEVICE_ID_MARVELL_88W8766P: - if (((card->txbd_wrptr & reg->tx_mask) == - (rdptr & reg->tx_mask)) && - ((card->txbd_wrptr & reg->tx_rollover_ind) != - (rdptr & reg->tx_rollover_ind))) - return 1; - break; - case PCIE_DEVICE_ID_MARVELL_88W8897: - if (((card->txbd_wrptr & reg->tx_mask) == - (rdptr & reg->tx_mask)) && - ((card->txbd_wrptr & reg->tx_rollover_ind) == - (rdptr & reg->tx_rollover_ind))) - return 1; - break; - } - - return 0; -} - -static inline int -mwifiex_pcie_txbd_not_full(struct pcie_service_card *card) -{ - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - switch (card->dev->device) { - case PCIE_DEVICE_ID_MARVELL_88W8766P: - if (((card->txbd_wrptr & reg->tx_mask) != - (card->txbd_rdptr & reg->tx_mask)) || - ((card->txbd_wrptr & reg->tx_rollover_ind) != - (card->txbd_rdptr & reg->tx_rollover_ind))) - return 1; - break; - case PCIE_DEVICE_ID_MARVELL_88W8897: - case PCIE_DEVICE_ID_MARVELL_88W8997: - if (((card->txbd_wrptr & reg->tx_mask) != - (card->txbd_rdptr & reg->tx_mask)) || - ((card->txbd_wrptr & reg->tx_rollover_ind) == - (card->txbd_rdptr & reg->tx_rollover_ind))) - return 1; - break; - } - - return 0; -} - -#endif /* _MWIFIEX_PCIE_H */ diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c deleted file mode 100644 index c20017ced566..000000000000 --- a/drivers/net/wireless/mwifiex/scan.c +++ /dev/null @@ -1,2639 +0,0 @@ -/* - * Marvell Wireless LAN device driver: scan ioctl and command handling - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "11n.h" -#include "cfg80211.h" - -/* The maximum number of channels the firmware can scan per command */ -#define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 - -#define MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD 4 - -/* Memory needed to store a max sized Channel List TLV for a firmware scan */ -#define CHAN_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_header) \ - + (MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN \ - *sizeof(struct mwifiex_chan_scan_param_set))) - -/* Memory needed to store supported rate */ -#define RATE_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_rates_param_set) \ - + HOSTCMD_SUPPORTED_RATES) - -/* Memory needed to store a max number/size WildCard SSID TLV for a firmware - scan */ -#define WILDCARD_SSID_TLV_MAX_SIZE \ - (MWIFIEX_MAX_SSID_LIST_LENGTH * \ - (sizeof(struct mwifiex_ie_types_wildcard_ssid_params) \ - + IEEE80211_MAX_SSID_LEN)) - -/* Maximum memory needed for a mwifiex_scan_cmd_config with all TLVs at max */ -#define MAX_SCAN_CFG_ALLOC (sizeof(struct mwifiex_scan_cmd_config) \ - + sizeof(struct mwifiex_ie_types_num_probes) \ - + sizeof(struct mwifiex_ie_types_htcap) \ - + CHAN_TLV_MAX_SIZE \ - + RATE_TLV_MAX_SIZE \ - + WILDCARD_SSID_TLV_MAX_SIZE) - - -union mwifiex_scan_cmd_config_tlv { - /* Scan configuration (variable length) */ - struct mwifiex_scan_cmd_config config; - /* Max allocated block */ - u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC]; -}; - -enum cipher_suite { - CIPHER_SUITE_TKIP, - CIPHER_SUITE_CCMP, - CIPHER_SUITE_MAX -}; -static u8 mwifiex_wpa_oui[CIPHER_SUITE_MAX][4] = { - { 0x00, 0x50, 0xf2, 0x02 }, /* TKIP */ - { 0x00, 0x50, 0xf2, 0x04 }, /* AES */ -}; -static u8 mwifiex_rsn_oui[CIPHER_SUITE_MAX][4] = { - { 0x00, 0x0f, 0xac, 0x02 }, /* TKIP */ - { 0x00, 0x0f, 0xac, 0x04 }, /* AES */ -}; - -/* - * This function parses a given IE for a given OUI. - * - * This is used to parse a WPA/RSN IE to find if it has - * a given oui in PTK. - */ -static u8 -mwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui) -{ - u8 count; - - count = iebody->ptk_cnt[0]; - - /* There could be multiple OUIs for PTK hence - 1) Take the length. - 2) Check all the OUIs for AES. - 3) If one of them is AES then pass success. */ - while (count) { - if (!memcmp(iebody->ptk_body, oui, sizeof(iebody->ptk_body))) - return MWIFIEX_OUI_PRESENT; - - --count; - if (count) - iebody = (struct ie_body *) ((u8 *) iebody + - sizeof(iebody->ptk_body)); - } - - pr_debug("info: %s: OUI is not found in PTK\n", __func__); - return MWIFIEX_OUI_NOT_PRESENT; -} - -/* - * This function checks if a given OUI is present in a RSN IE. - * - * The function first checks if a RSN IE is present or not in the - * BSS descriptor. It tries to locate the OUI only if such an IE is - * present. - */ -static u8 -mwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) -{ - u8 *oui; - struct ie_body *iebody; - u8 ret = MWIFIEX_OUI_NOT_PRESENT; - - if (((bss_desc->bcn_rsn_ie) && ((*(bss_desc->bcn_rsn_ie)). - ieee_hdr.element_id == WLAN_EID_RSN))) { - iebody = (struct ie_body *) - (((u8 *) bss_desc->bcn_rsn_ie->data) + - RSN_GTK_OUI_OFFSET); - oui = &mwifiex_rsn_oui[cipher][0]; - ret = mwifiex_search_oui_in_ie(iebody, oui); - if (ret) - return ret; - } - return ret; -} - -/* - * This function checks if a given OUI is present in a WPA IE. - * - * The function first checks if a WPA IE is present or not in the - * BSS descriptor. It tries to locate the OUI only if such an IE is - * present. - */ -static u8 -mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) -{ - u8 *oui; - struct ie_body *iebody; - u8 ret = MWIFIEX_OUI_NOT_PRESENT; - - if (((bss_desc->bcn_wpa_ie) && - ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id == - WLAN_EID_VENDOR_SPECIFIC))) { - iebody = (struct ie_body *) bss_desc->bcn_wpa_ie->data; - oui = &mwifiex_wpa_oui[cipher][0]; - ret = mwifiex_search_oui_in_ie(iebody, oui); - if (ret) - return ret; - } - return ret; -} - -/* - * This function compares two SSIDs and checks if they match. - */ -s32 -mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2) -{ - if (!ssid1 || !ssid2 || (ssid1->ssid_len != ssid2->ssid_len)) - return -1; - return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len); -} - -/* - * This function checks if wapi is enabled in driver and scanned network is - * compatible with it. - */ -static bool -mwifiex_is_bss_wapi(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (priv->sec_info.wapi_enabled && - (bss_desc->bcn_wapi_ie && - ((*(bss_desc->bcn_wapi_ie)).ieee_hdr.element_id == - WLAN_EID_BSS_AC_ACCESS_DELAY))) { - return true; - } - return false; -} - -/* - * This function checks if driver is configured with no security mode and - * scanned network is compatible with it. - */ -static bool -mwifiex_is_bss_no_sec(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && - !priv->sec_info.wpa2_enabled && ((!bss_desc->bcn_wpa_ie) || - ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != - WLAN_EID_VENDOR_SPECIFIC)) && - ((!bss_desc->bcn_rsn_ie) || - ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != - WLAN_EID_RSN)) && - !priv->sec_info.encryption_mode && !bss_desc->privacy) { - return true; - } - return false; -} - -/* - * This function checks if static WEP is enabled in driver and scanned network - * is compatible with it. - */ -static bool -mwifiex_is_bss_static_wep(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && - !priv->sec_info.wpa2_enabled && bss_desc->privacy) { - return true; - } - return false; -} - -/* - * This function checks if wpa is enabled in driver and scanned network is - * compatible with it. - */ -static bool -mwifiex_is_bss_wpa(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled && - !priv->sec_info.wpa2_enabled && ((bss_desc->bcn_wpa_ie) && - ((*(bss_desc->bcn_wpa_ie)). - vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC)) - /* - * Privacy bit may NOT be set in some APs like - * LinkSys WRT54G && bss_desc->privacy - */ - ) { - mwifiex_dbg(priv->adapter, INFO, - "info: %s: WPA:\t" - "wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\t" - "EncMode=%#x privacy=%#x\n", __func__, - (bss_desc->bcn_wpa_ie) ? - (*bss_desc->bcn_wpa_ie). - vend_hdr.element_id : 0, - (bss_desc->bcn_rsn_ie) ? - (*bss_desc->bcn_rsn_ie). - ieee_hdr.element_id : 0, - (priv->sec_info.wep_enabled) ? "e" : "d", - (priv->sec_info.wpa_enabled) ? "e" : "d", - (priv->sec_info.wpa2_enabled) ? "e" : "d", - priv->sec_info.encryption_mode, - bss_desc->privacy); - return true; - } - return false; -} - -/* - * This function checks if wpa2 is enabled in driver and scanned network is - * compatible with it. - */ -static bool -mwifiex_is_bss_wpa2(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (!priv->sec_info.wep_enabled && - !priv->sec_info.wpa_enabled && - priv->sec_info.wpa2_enabled && - ((bss_desc->bcn_rsn_ie) && - ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id == WLAN_EID_RSN))) { - /* - * Privacy bit may NOT be set in some APs like - * LinkSys WRT54G && bss_desc->privacy - */ - mwifiex_dbg(priv->adapter, INFO, - "info: %s: WPA2:\t" - "wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\t" - "EncMode=%#x privacy=%#x\n", __func__, - (bss_desc->bcn_wpa_ie) ? - (*bss_desc->bcn_wpa_ie). - vend_hdr.element_id : 0, - (bss_desc->bcn_rsn_ie) ? - (*bss_desc->bcn_rsn_ie). - ieee_hdr.element_id : 0, - (priv->sec_info.wep_enabled) ? "e" : "d", - (priv->sec_info.wpa_enabled) ? "e" : "d", - (priv->sec_info.wpa2_enabled) ? "e" : "d", - priv->sec_info.encryption_mode, - bss_desc->privacy); - return true; - } - return false; -} - -/* - * This function checks if adhoc AES is enabled in driver and scanned network is - * compatible with it. - */ -static bool -mwifiex_is_bss_adhoc_aes(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && - !priv->sec_info.wpa2_enabled && - ((!bss_desc->bcn_wpa_ie) || - ((*(bss_desc->bcn_wpa_ie)). - vend_hdr.element_id != WLAN_EID_VENDOR_SPECIFIC)) && - ((!bss_desc->bcn_rsn_ie) || - ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && - !priv->sec_info.encryption_mode && bss_desc->privacy) { - return true; - } - return false; -} - -/* - * This function checks if dynamic WEP is enabled in driver and scanned network - * is compatible with it. - */ -static bool -mwifiex_is_bss_dynamic_wep(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && - !priv->sec_info.wpa2_enabled && - ((!bss_desc->bcn_wpa_ie) || - ((*(bss_desc->bcn_wpa_ie)). - vend_hdr.element_id != WLAN_EID_VENDOR_SPECIFIC)) && - ((!bss_desc->bcn_rsn_ie) || - ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && - priv->sec_info.encryption_mode && bss_desc->privacy) { - mwifiex_dbg(priv->adapter, INFO, - "info: %s: dynamic\t" - "WEP: wpa_ie=%#x wpa2_ie=%#x\t" - "EncMode=%#x privacy=%#x\n", - __func__, - (bss_desc->bcn_wpa_ie) ? - (*bss_desc->bcn_wpa_ie). - vend_hdr.element_id : 0, - (bss_desc->bcn_rsn_ie) ? - (*bss_desc->bcn_rsn_ie). - ieee_hdr.element_id : 0, - priv->sec_info.encryption_mode, - bss_desc->privacy); - return true; - } - return false; -} - -/* - * This function checks if a scanned network is compatible with the driver - * settings. - * - * WEP WPA WPA2 ad-hoc encrypt Network - * enabled enabled enabled AES mode Privacy WPA WPA2 Compatible - * 0 0 0 0 NONE 0 0 0 yes No security - * 0 1 0 0 x 1x 1 x yes WPA (disable - * HT if no AES) - * 0 0 1 0 x 1x x 1 yes WPA2 (disable - * HT if no AES) - * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES - * 1 0 0 0 NONE 1 0 0 yes Static WEP - * (disable HT) - * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP - * - * Compatibility is not matched while roaming, except for mode. - */ -static s32 -mwifiex_is_network_compatible(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, u32 mode) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - bss_desc->disable_11n = false; - - /* Don't check for compatibility if roaming */ - if (priv->media_connected && - (priv->bss_mode == NL80211_IFTYPE_STATION) && - (bss_desc->bss_mode == NL80211_IFTYPE_STATION)) - return 0; - - if (priv->wps.session_enable) { - mwifiex_dbg(adapter, IOCTL, - "info: return success directly in WPS period\n"); - return 0; - } - - if (bss_desc->chan_sw_ie_present) { - mwifiex_dbg(adapter, INFO, - "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n"); - return -1; - } - - if (mwifiex_is_bss_wapi(priv, bss_desc)) { - mwifiex_dbg(adapter, INFO, - "info: return success for WAPI AP\n"); - return 0; - } - - if (bss_desc->bss_mode == mode) { - if (mwifiex_is_bss_no_sec(priv, bss_desc)) { - /* No security */ - return 0; - } else if (mwifiex_is_bss_static_wep(priv, bss_desc)) { - /* Static WEP enabled */ - mwifiex_dbg(adapter, INFO, - "info: Disable 11n in WEP mode.\n"); - bss_desc->disable_11n = true; - return 0; - } else if (mwifiex_is_bss_wpa(priv, bss_desc)) { - /* WPA enabled */ - if (((priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN) && - bss_desc->bcn_ht_cap) && - !mwifiex_is_wpa_oui_present(bss_desc, - CIPHER_SUITE_CCMP)) { - - if (mwifiex_is_wpa_oui_present - (bss_desc, CIPHER_SUITE_TKIP)) { - mwifiex_dbg(adapter, INFO, - "info: Disable 11n if AES\t" - "is not supported by AP\n"); - bss_desc->disable_11n = true; - } else { - return -1; - } - } - return 0; - } else if (mwifiex_is_bss_wpa2(priv, bss_desc)) { - /* WPA2 enabled */ - if (((priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN) && - bss_desc->bcn_ht_cap) && - !mwifiex_is_rsn_oui_present(bss_desc, - CIPHER_SUITE_CCMP)) { - - if (mwifiex_is_rsn_oui_present - (bss_desc, CIPHER_SUITE_TKIP)) { - mwifiex_dbg(adapter, INFO, - "info: Disable 11n if AES\t" - "is not supported by AP\n"); - bss_desc->disable_11n = true; - } else { - return -1; - } - } - return 0; - } else if (mwifiex_is_bss_adhoc_aes(priv, bss_desc)) { - /* Ad-hoc AES enabled */ - return 0; - } else if (mwifiex_is_bss_dynamic_wep(priv, bss_desc)) { - /* Dynamic WEP enabled */ - return 0; - } - - /* Security doesn't match */ - mwifiex_dbg(adapter, ERROR, - "info: %s: failed: wpa_ie=%#x wpa2_ie=%#x WEP=%s\t" - "WPA=%s WPA2=%s EncMode=%#x privacy=%#x\n", - __func__, - (bss_desc->bcn_wpa_ie) ? - (*bss_desc->bcn_wpa_ie).vend_hdr.element_id : 0, - (bss_desc->bcn_rsn_ie) ? - (*bss_desc->bcn_rsn_ie).ieee_hdr.element_id : 0, - (priv->sec_info.wep_enabled) ? "e" : "d", - (priv->sec_info.wpa_enabled) ? "e" : "d", - (priv->sec_info.wpa2_enabled) ? "e" : "d", - priv->sec_info.encryption_mode, bss_desc->privacy); - return -1; - } - - /* Mode doesn't match */ - return -1; -} - -/* - * This function creates a channel list for the driver to scan, based - * on region/band information. - * - * This routine is used for any scan that is not provided with a - * specific channel list to scan. - */ -static int -mwifiex_scan_create_channel_list(struct mwifiex_private *priv, - const struct mwifiex_user_scan_cfg - *user_scan_in, - struct mwifiex_chan_scan_param_set - *scan_chan_list, - u8 filtered_scan) -{ - enum ieee80211_band band; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *ch; - struct mwifiex_adapter *adapter = priv->adapter; - int chan_idx = 0, i; - - for (band = 0; (band < IEEE80211_NUM_BANDS) ; band++) { - - if (!priv->wdev.wiphy->bands[band]) - continue; - - sband = priv->wdev.wiphy->bands[band]; - - for (i = 0; (i < sband->n_channels) ; i++) { - ch = &sband->channels[i]; - if (ch->flags & IEEE80211_CHAN_DISABLED) - continue; - scan_chan_list[chan_idx].radio_type = band; - - if (user_scan_in && - user_scan_in->chan_list[0].scan_time) - scan_chan_list[chan_idx].max_scan_time = - cpu_to_le16((u16) user_scan_in-> - chan_list[0].scan_time); - else if (ch->flags & IEEE80211_CHAN_NO_IR) - scan_chan_list[chan_idx].max_scan_time = - cpu_to_le16(adapter->passive_scan_time); - else - scan_chan_list[chan_idx].max_scan_time = - cpu_to_le16(adapter->active_scan_time); - - if (ch->flags & IEEE80211_CHAN_NO_IR) - scan_chan_list[chan_idx].chan_scan_mode_bitmap - |= (MWIFIEX_PASSIVE_SCAN | - MWIFIEX_HIDDEN_SSID_REPORT); - else - scan_chan_list[chan_idx].chan_scan_mode_bitmap - &= ~MWIFIEX_PASSIVE_SCAN; - scan_chan_list[chan_idx].chan_number = - (u32) ch->hw_value; - if (filtered_scan) { - scan_chan_list[chan_idx].max_scan_time = - cpu_to_le16(adapter->specific_scan_time); - scan_chan_list[chan_idx].chan_scan_mode_bitmap - |= MWIFIEX_DISABLE_CHAN_FILT; - } - chan_idx++; - } - - } - return chan_idx; -} - -/* This function appends rate TLV to scan config command. */ -static int -mwifiex_append_rate_tlv(struct mwifiex_private *priv, - struct mwifiex_scan_cmd_config *scan_cfg_out, - u8 radio) -{ - struct mwifiex_ie_types_rates_param_set *rates_tlv; - u8 rates[MWIFIEX_SUPPORTED_RATES], *tlv_pos; - u32 rates_size; - - memset(rates, 0, sizeof(rates)); - - tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len; - - if (priv->scan_request) - rates_size = mwifiex_get_rates_from_cfg80211(priv, rates, - radio); - else - rates_size = mwifiex_get_supported_rates(priv, rates); - - mwifiex_dbg(priv->adapter, CMD, - "info: SCAN_CMD: Rates size = %d\n", - rates_size); - rates_tlv = (struct mwifiex_ie_types_rates_param_set *)tlv_pos; - rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); - rates_tlv->header.len = cpu_to_le16((u16) rates_size); - memcpy(rates_tlv->rates, rates, rates_size); - scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size; - - return rates_size; -} - -/* - * This function constructs and sends multiple scan config commands to - * the firmware. - * - * Previous routines in the code flow have created a scan command configuration - * with any requested TLVs. This function splits the channel TLV into maximum - * channels supported per scan lists and sends the portion of the channel TLV, - * along with the other TLVs, to the firmware. - */ -static int -mwifiex_scan_channel_list(struct mwifiex_private *priv, - u32 max_chan_per_scan, u8 filtered_scan, - struct mwifiex_scan_cmd_config *scan_cfg_out, - struct mwifiex_ie_types_chan_list_param_set - *chan_tlv_out, - struct mwifiex_chan_scan_param_set *scan_chan_list) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret = 0; - struct mwifiex_chan_scan_param_set *tmp_chan_list; - struct mwifiex_chan_scan_param_set *start_chan; - struct cmd_ctrl_node *cmd_node, *tmp_node; - unsigned long flags; - u32 tlv_idx, rates_size, cmd_no; - u32 total_scan_time; - u32 done_early; - u8 radio_type; - - if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) { - mwifiex_dbg(priv->adapter, ERROR, - "info: Scan: Null detect: %p, %p, %p\n", - scan_cfg_out, chan_tlv_out, scan_chan_list); - return -1; - } - - /* Check csa channel expiry before preparing scan list */ - mwifiex_11h_get_csa_closed_channel(priv); - - chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - - /* Set the temp channel struct pointer to the start of the desired - list */ - tmp_chan_list = scan_chan_list; - - /* Loop through the desired channel list, sending a new firmware scan - commands for each max_chan_per_scan channels (or for 1,6,11 - individually if configured accordingly) */ - while (tmp_chan_list->chan_number) { - - tlv_idx = 0; - total_scan_time = 0; - radio_type = 0; - chan_tlv_out->header.len = 0; - start_chan = tmp_chan_list; - done_early = false; - - /* - * Construct the Channel TLV for the scan command. Continue to - * insert channel TLVs until: - * - the tlv_idx hits the maximum configured per scan command - * - the next channel to insert is 0 (end of desired channel - * list) - * - done_early is set (controlling individual scanning of - * 1,6,11) - */ - while (tlv_idx < max_chan_per_scan && - tmp_chan_list->chan_number && !done_early) { - - if (tmp_chan_list->chan_number == priv->csa_chan) { - tmp_chan_list++; - continue; - } - - radio_type = tmp_chan_list->radio_type; - mwifiex_dbg(priv->adapter, INFO, - "info: Scan: Chan(%3d), Radio(%d),\t" - "Mode(%d, %d), Dur(%d)\n", - tmp_chan_list->chan_number, - tmp_chan_list->radio_type, - tmp_chan_list->chan_scan_mode_bitmap - & MWIFIEX_PASSIVE_SCAN, - (tmp_chan_list->chan_scan_mode_bitmap - & MWIFIEX_DISABLE_CHAN_FILT) >> 1, - le16_to_cpu(tmp_chan_list->max_scan_time)); - - /* Copy the current channel TLV to the command being - prepared */ - memcpy(chan_tlv_out->chan_scan_param + tlv_idx, - tmp_chan_list, - sizeof(chan_tlv_out->chan_scan_param)); - - /* Increment the TLV header length by the size - appended */ - le16_add_cpu(&chan_tlv_out->header.len, - sizeof(chan_tlv_out->chan_scan_param)); - - /* - * The tlv buffer length is set to the number of bytes - * of the between the channel tlv pointer and the start - * of the tlv buffer. This compensates for any TLVs - * that were appended before the channel list. - */ - scan_cfg_out->tlv_buf_len = (u32) ((u8 *) chan_tlv_out - - scan_cfg_out->tlv_buf); - - /* Add the size of the channel tlv header and the data - length */ - scan_cfg_out->tlv_buf_len += - (sizeof(chan_tlv_out->header) - + le16_to_cpu(chan_tlv_out->header.len)); - - /* Increment the index to the channel tlv we are - constructing */ - tlv_idx++; - - /* Count the total scan time per command */ - total_scan_time += - le16_to_cpu(tmp_chan_list->max_scan_time); - - done_early = false; - - /* Stop the loop if the *current* channel is in the - 1,6,11 set and we are not filtering on a BSSID - or SSID. */ - if (!filtered_scan && - (tmp_chan_list->chan_number == 1 || - tmp_chan_list->chan_number == 6 || - tmp_chan_list->chan_number == 11)) - done_early = true; - - /* Increment the tmp pointer to the next channel to - be scanned */ - tmp_chan_list++; - - /* Stop the loop if the *next* channel is in the 1,6,11 - set. This will cause it to be the only channel - scanned on the next interation */ - if (!filtered_scan && - (tmp_chan_list->chan_number == 1 || - tmp_chan_list->chan_number == 6 || - tmp_chan_list->chan_number == 11)) - done_early = true; - } - - /* The total scan time should be less than scan command timeout - value */ - if (total_scan_time > MWIFIEX_MAX_TOTAL_SCAN_TIME) { - mwifiex_dbg(priv->adapter, ERROR, - "total scan time %dms\t" - "is over limit (%dms), scan skipped\n", - total_scan_time, - MWIFIEX_MAX_TOTAL_SCAN_TIME); - ret = -1; - break; - } - - rates_size = mwifiex_append_rate_tlv(priv, scan_cfg_out, - radio_type); - - priv->adapter->scan_channels = start_chan; - - /* Send the scan command to the firmware with the specified - cfg */ - if (priv->adapter->ext_scan) - cmd_no = HostCmd_CMD_802_11_SCAN_EXT; - else - cmd_no = HostCmd_CMD_802_11_SCAN; - - ret = mwifiex_send_cmd(priv, cmd_no, HostCmd_ACT_GEN_SET, - 0, scan_cfg_out, false); - - /* rate IE is updated per scan command but same starting - * pointer is used each time so that rate IE from earlier - * scan_cfg_out->buf is overwritten with new one. - */ - scan_cfg_out->tlv_buf_len -= - sizeof(struct mwifiex_ie_types_header) + rates_size; - - if (ret) { - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, - list) { - list_del(&cmd_node->list); - cmd_node->wait_q_enabled = false; - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - break; - } - } - - if (ret) - return -1; - - return 0; -} - -/* - * This function constructs a scan command configuration structure to use - * in scan commands. - * - * Application layer or other functions can invoke network scanning - * with a scan configuration supplied in a user scan configuration structure. - * This structure is used as the basis of one or many scan command configuration - * commands that are sent to the command processing module and eventually to the - * firmware. - * - * This function creates a scan command configuration structure based on the - * following user supplied parameters (if present): - * - SSID filter - * - BSSID filter - * - Number of Probes to be sent - * - Channel list - * - * If the SSID or BSSID filter is not present, the filter is disabled/cleared. - * If the number of probes is not set, adapter default setting is used. - */ -static void -mwifiex_config_scan(struct mwifiex_private *priv, - const struct mwifiex_user_scan_cfg *user_scan_in, - struct mwifiex_scan_cmd_config *scan_cfg_out, - struct mwifiex_ie_types_chan_list_param_set **chan_list_out, - struct mwifiex_chan_scan_param_set *scan_chan_list, - u8 *max_chan_per_scan, u8 *filtered_scan, - u8 *scan_current_only) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie_types_num_probes *num_probes_tlv; - struct mwifiex_ie_types_scan_chan_gap *chan_gap_tlv; - struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; - struct mwifiex_ie_types_bssid_list *bssid_tlv; - u8 *tlv_pos; - u32 num_probes; - u32 ssid_len; - u32 chan_idx; - u32 chan_num; - u32 scan_type; - u16 scan_dur; - u8 channel; - u8 radio_type; - int i; - u8 ssid_filter; - struct mwifiex_ie_types_htcap *ht_cap; - struct mwifiex_ie_types_bss_mode *bss_mode; - - /* The tlv_buf_len is calculated for each scan command. The TLVs added - in this routine will be preserved since the routine that sends the - command will append channelTLVs at *chan_list_out. The difference - between the *chan_list_out and the tlv_buf start will be used to - calculate the size of anything we add in this routine. */ - scan_cfg_out->tlv_buf_len = 0; - - /* Running tlv pointer. Assigned to chan_list_out at end of function - so later routines know where channels can be added to the command - buf */ - tlv_pos = scan_cfg_out->tlv_buf; - - /* Initialize the scan as un-filtered; the flag is later set to TRUE - below if a SSID or BSSID filter is sent in the command */ - *filtered_scan = false; - - /* Initialize the scan as not being only on the current channel. If - the channel list is customized, only contains one channel, and is - the active channel, this is set true and data flow is not halted. */ - *scan_current_only = false; - - if (user_scan_in) { - - /* Default the ssid_filter flag to TRUE, set false under - certain wildcard conditions and qualified by the existence - of an SSID list before marking the scan as filtered */ - ssid_filter = true; - - /* Set the BSS type scan filter, use Adapter setting if - unset */ - scan_cfg_out->bss_mode = - (user_scan_in->bss_mode ? (u8) user_scan_in-> - bss_mode : (u8) adapter->scan_mode); - - /* Set the number of probes to send, use Adapter setting - if unset */ - num_probes = - (user_scan_in->num_probes ? user_scan_in-> - num_probes : adapter->scan_probes); - - /* - * Set the BSSID filter to the incoming configuration, - * if non-zero. If not set, it will remain disabled - * (all zeros). - */ - memcpy(scan_cfg_out->specific_bssid, - user_scan_in->specific_bssid, - sizeof(scan_cfg_out->specific_bssid)); - - if (adapter->ext_scan && - !is_zero_ether_addr(scan_cfg_out->specific_bssid)) { - bssid_tlv = - (struct mwifiex_ie_types_bssid_list *)tlv_pos; - bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID); - bssid_tlv->header.len = cpu_to_le16(ETH_ALEN); - memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid, - ETH_ALEN); - tlv_pos += sizeof(struct mwifiex_ie_types_bssid_list); - } - - for (i = 0; i < user_scan_in->num_ssids; i++) { - ssid_len = user_scan_in->ssid_list[i].ssid_len; - - wildcard_ssid_tlv = - (struct mwifiex_ie_types_wildcard_ssid_params *) - tlv_pos; - wildcard_ssid_tlv->header.type = - cpu_to_le16(TLV_TYPE_WILDCARDSSID); - wildcard_ssid_tlv->header.len = cpu_to_le16( - (u16) (ssid_len + sizeof(wildcard_ssid_tlv-> - max_ssid_length))); - - /* - * max_ssid_length = 0 tells firmware to perform - * specific scan for the SSID filled, whereas - * max_ssid_length = IEEE80211_MAX_SSID_LEN is for - * wildcard scan. - */ - if (ssid_len) - wildcard_ssid_tlv->max_ssid_length = 0; - else - wildcard_ssid_tlv->max_ssid_length = - IEEE80211_MAX_SSID_LEN; - - if (!memcmp(user_scan_in->ssid_list[i].ssid, - "DIRECT-", 7)) - wildcard_ssid_tlv->max_ssid_length = 0xfe; - - memcpy(wildcard_ssid_tlv->ssid, - user_scan_in->ssid_list[i].ssid, ssid_len); - - tlv_pos += (sizeof(wildcard_ssid_tlv->header) - + le16_to_cpu(wildcard_ssid_tlv->header.len)); - - mwifiex_dbg(adapter, INFO, - "info: scan: ssid[%d]: %s, %d\n", - i, wildcard_ssid_tlv->ssid, - wildcard_ssid_tlv->max_ssid_length); - - /* Empty wildcard ssid with a maxlen will match many or - potentially all SSIDs (maxlen == 32), therefore do - not treat the scan as - filtered. */ - if (!ssid_len && wildcard_ssid_tlv->max_ssid_length) - ssid_filter = false; - } - - /* - * The default number of channels sent in the command is low to - * ensure the response buffer from the firmware does not - * truncate scan results. That is not an issue with an SSID - * or BSSID filter applied to the scan results in the firmware. - */ - if ((i && ssid_filter) || - !is_zero_ether_addr(scan_cfg_out->specific_bssid)) - *filtered_scan = true; - - if (user_scan_in->scan_chan_gap) { - mwifiex_dbg(adapter, INFO, - "info: scan: channel gap = %d\n", - user_scan_in->scan_chan_gap); - *max_chan_per_scan = - MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; - - chan_gap_tlv = (void *)tlv_pos; - chan_gap_tlv->header.type = - cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); - chan_gap_tlv->header.len = - cpu_to_le16(sizeof(chan_gap_tlv->chan_gap)); - chan_gap_tlv->chan_gap = - cpu_to_le16((user_scan_in->scan_chan_gap)); - tlv_pos += - sizeof(struct mwifiex_ie_types_scan_chan_gap); - } - } else { - scan_cfg_out->bss_mode = (u8) adapter->scan_mode; - num_probes = adapter->scan_probes; - } - - /* - * If a specific BSSID or SSID is used, the number of channels in the - * scan command will be increased to the absolute maximum. - */ - if (*filtered_scan) - *max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; - else - *max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD; - - if (adapter->ext_scan) { - bss_mode = (struct mwifiex_ie_types_bss_mode *)tlv_pos; - bss_mode->header.type = cpu_to_le16(TLV_TYPE_BSS_MODE); - bss_mode->header.len = cpu_to_le16(sizeof(bss_mode->bss_mode)); - bss_mode->bss_mode = scan_cfg_out->bss_mode; - tlv_pos += sizeof(bss_mode->header) + - le16_to_cpu(bss_mode->header.len); - } - - /* If the input config or adapter has the number of Probes set, - add tlv */ - if (num_probes) { - - mwifiex_dbg(adapter, INFO, - "info: scan: num_probes = %d\n", - num_probes); - - num_probes_tlv = (struct mwifiex_ie_types_num_probes *) tlv_pos; - num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); - num_probes_tlv->header.len = - cpu_to_le16(sizeof(num_probes_tlv->num_probes)); - num_probes_tlv->num_probes = cpu_to_le16((u16) num_probes); - - tlv_pos += sizeof(num_probes_tlv->header) + - le16_to_cpu(num_probes_tlv->header.len); - - } - - if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && - (priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN)) { - ht_cap = (struct mwifiex_ie_types_htcap *) tlv_pos; - memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); - ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); - ht_cap->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_cap)); - radio_type = - mwifiex_band_to_radio_type(priv->adapter->config_bands); - mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); - tlv_pos += sizeof(struct mwifiex_ie_types_htcap); - } - - /* Append vendor specific IE TLV */ - mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_SCAN, &tlv_pos); - - /* - * Set the output for the channel TLV to the address in the tlv buffer - * past any TLVs that were added in this function (SSID, num_probes). - * Channel TLVs will be added past this for each scan command, - * preserving the TLVs that were previously added. - */ - *chan_list_out = - (struct mwifiex_ie_types_chan_list_param_set *) tlv_pos; - - if (user_scan_in && user_scan_in->chan_list[0].chan_number) { - - mwifiex_dbg(adapter, INFO, - "info: Scan: Using supplied channel list\n"); - - for (chan_idx = 0; - chan_idx < MWIFIEX_USER_SCAN_CHAN_MAX && - user_scan_in->chan_list[chan_idx].chan_number; - chan_idx++) { - - channel = user_scan_in->chan_list[chan_idx].chan_number; - (scan_chan_list + chan_idx)->chan_number = channel; - - radio_type = - user_scan_in->chan_list[chan_idx].radio_type; - (scan_chan_list + chan_idx)->radio_type = radio_type; - - scan_type = user_scan_in->chan_list[chan_idx].scan_type; - - if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) - (scan_chan_list + - chan_idx)->chan_scan_mode_bitmap - |= (MWIFIEX_PASSIVE_SCAN | - MWIFIEX_HIDDEN_SSID_REPORT); - else - (scan_chan_list + - chan_idx)->chan_scan_mode_bitmap - &= ~MWIFIEX_PASSIVE_SCAN; - - if (*filtered_scan) - (scan_chan_list + - chan_idx)->chan_scan_mode_bitmap - |= MWIFIEX_DISABLE_CHAN_FILT; - - if (user_scan_in->chan_list[chan_idx].scan_time) { - scan_dur = (u16) user_scan_in-> - chan_list[chan_idx].scan_time; - } else { - if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) - scan_dur = adapter->passive_scan_time; - else if (*filtered_scan) - scan_dur = adapter->specific_scan_time; - else - scan_dur = adapter->active_scan_time; - } - - (scan_chan_list + chan_idx)->min_scan_time = - cpu_to_le16(scan_dur); - (scan_chan_list + chan_idx)->max_scan_time = - cpu_to_le16(scan_dur); - } - - /* Check if we are only scanning the current channel */ - if ((chan_idx == 1) && - (user_scan_in->chan_list[0].chan_number == - priv->curr_bss_params.bss_descriptor.channel)) { - *scan_current_only = true; - mwifiex_dbg(adapter, INFO, - "info: Scan: Scanning current channel only\n"); - } - chan_num = chan_idx; - } else { - mwifiex_dbg(adapter, INFO, - "info: Scan: Creating full region channel list\n"); - chan_num = mwifiex_scan_create_channel_list(priv, user_scan_in, - scan_chan_list, - *filtered_scan); - } - -} - -/* - * This function inspects the scan response buffer for pointers to - * expected TLVs. - * - * TLVs can be included at the end of the scan response BSS information. - * - * Data in the buffer is parsed pointers to TLVs that can potentially - * be passed back in the response. - */ -static void -mwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter, - struct mwifiex_ie_types_data *tlv, - u32 tlv_buf_size, u32 req_tlv_type, - struct mwifiex_ie_types_data **tlv_data) -{ - struct mwifiex_ie_types_data *current_tlv; - u32 tlv_buf_left; - u32 tlv_type; - u32 tlv_len; - - current_tlv = tlv; - tlv_buf_left = tlv_buf_size; - *tlv_data = NULL; - - mwifiex_dbg(adapter, INFO, - "info: SCAN_RESP: tlv_buf_size = %d\n", - tlv_buf_size); - - while (tlv_buf_left >= sizeof(struct mwifiex_ie_types_header)) { - - tlv_type = le16_to_cpu(current_tlv->header.type); - tlv_len = le16_to_cpu(current_tlv->header.len); - - if (sizeof(tlv->header) + tlv_len > tlv_buf_left) { - mwifiex_dbg(adapter, ERROR, - "SCAN_RESP: TLV buffer corrupt\n"); - break; - } - - if (req_tlv_type == tlv_type) { - switch (tlv_type) { - case TLV_TYPE_TSFTIMESTAMP: - mwifiex_dbg(adapter, INFO, - "info: SCAN_RESP: TSF\t" - "timestamp TLV, len = %d\n", - tlv_len); - *tlv_data = current_tlv; - break; - case TLV_TYPE_CHANNELBANDLIST: - mwifiex_dbg(adapter, INFO, - "info: SCAN_RESP: channel\t" - "band list TLV, len = %d\n", - tlv_len); - *tlv_data = current_tlv; - break; - default: - mwifiex_dbg(adapter, ERROR, - "SCAN_RESP: unhandled TLV = %d\n", - tlv_type); - /* Give up, this seems corrupted */ - return; - } - } - - if (*tlv_data) - break; - - - tlv_buf_left -= (sizeof(tlv->header) + tlv_len); - current_tlv = - (struct mwifiex_ie_types_data *) (current_tlv->data + - tlv_len); - - } /* while */ -} - -/* - * This function parses provided beacon buffer and updates - * respective fields in bss descriptor structure. - */ -int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, - struct mwifiex_bssdescriptor *bss_entry) -{ - int ret = 0; - u8 element_id; - struct ieee_types_fh_param_set *fh_param_set; - struct ieee_types_ds_param_set *ds_param_set; - struct ieee_types_cf_param_set *cf_param_set; - struct ieee_types_ibss_param_set *ibss_param_set; - u8 *current_ptr; - u8 *rate; - u8 element_len; - u16 total_ie_len; - u8 bytes_to_copy; - u8 rate_size; - u8 found_data_rate_ie; - u32 bytes_left; - struct ieee_types_vendor_specific *vendor_ie; - const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; - const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; - - found_data_rate_ie = false; - rate_size = 0; - current_ptr = bss_entry->beacon_buf; - bytes_left = bss_entry->beacon_buf_size; - - /* Process variable IE */ - while (bytes_left >= 2) { - element_id = *current_ptr; - element_len = *(current_ptr + 1); - total_ie_len = element_len + sizeof(struct ieee_types_header); - - if (bytes_left < total_ie_len) { - mwifiex_dbg(adapter, ERROR, - "err: InterpretIE: in processing\t" - "IE, bytes left < IE length\n"); - return -1; - } - switch (element_id) { - case WLAN_EID_SSID: - bss_entry->ssid.ssid_len = element_len; - memcpy(bss_entry->ssid.ssid, (current_ptr + 2), - element_len); - mwifiex_dbg(adapter, INFO, - "info: InterpretIE: ssid: %-32s\n", - bss_entry->ssid.ssid); - break; - - case WLAN_EID_SUPP_RATES: - memcpy(bss_entry->data_rates, current_ptr + 2, - element_len); - memcpy(bss_entry->supported_rates, current_ptr + 2, - element_len); - rate_size = element_len; - found_data_rate_ie = true; - break; - - case WLAN_EID_FH_PARAMS: - fh_param_set = - (struct ieee_types_fh_param_set *) current_ptr; - memcpy(&bss_entry->phy_param_set.fh_param_set, - fh_param_set, - sizeof(struct ieee_types_fh_param_set)); - break; - - case WLAN_EID_DS_PARAMS: - ds_param_set = - (struct ieee_types_ds_param_set *) current_ptr; - - bss_entry->channel = ds_param_set->current_chan; - - memcpy(&bss_entry->phy_param_set.ds_param_set, - ds_param_set, - sizeof(struct ieee_types_ds_param_set)); - break; - - case WLAN_EID_CF_PARAMS: - cf_param_set = - (struct ieee_types_cf_param_set *) current_ptr; - memcpy(&bss_entry->ss_param_set.cf_param_set, - cf_param_set, - sizeof(struct ieee_types_cf_param_set)); - break; - - case WLAN_EID_IBSS_PARAMS: - ibss_param_set = - (struct ieee_types_ibss_param_set *) - current_ptr; - memcpy(&bss_entry->ss_param_set.ibss_param_set, - ibss_param_set, - sizeof(struct ieee_types_ibss_param_set)); - break; - - case WLAN_EID_ERP_INFO: - bss_entry->erp_flags = *(current_ptr + 2); - break; - - case WLAN_EID_PWR_CONSTRAINT: - bss_entry->local_constraint = *(current_ptr + 2); - bss_entry->sensed_11h = true; - break; - - case WLAN_EID_CHANNEL_SWITCH: - bss_entry->chan_sw_ie_present = true; - case WLAN_EID_PWR_CAPABILITY: - case WLAN_EID_TPC_REPORT: - case WLAN_EID_QUIET: - bss_entry->sensed_11h = true; - break; - - case WLAN_EID_EXT_SUPP_RATES: - /* - * Only process extended supported rate - * if data rate is already found. - * Data rate IE should come before - * extended supported rate IE - */ - if (found_data_rate_ie) { - if ((element_len + rate_size) > - MWIFIEX_SUPPORTED_RATES) - bytes_to_copy = - (MWIFIEX_SUPPORTED_RATES - - rate_size); - else - bytes_to_copy = element_len; - - rate = (u8 *) bss_entry->data_rates; - rate += rate_size; - memcpy(rate, current_ptr + 2, bytes_to_copy); - - rate = (u8 *) bss_entry->supported_rates; - rate += rate_size; - memcpy(rate, current_ptr + 2, bytes_to_copy); - } - break; - - case WLAN_EID_VENDOR_SPECIFIC: - vendor_ie = (struct ieee_types_vendor_specific *) - current_ptr; - - if (!memcmp - (vendor_ie->vend_hdr.oui, wpa_oui, - sizeof(wpa_oui))) { - bss_entry->bcn_wpa_ie = - (struct ieee_types_vendor_specific *) - current_ptr; - bss_entry->wpa_offset = (u16) - (current_ptr - bss_entry->beacon_buf); - } else if (!memcmp(vendor_ie->vend_hdr.oui, wmm_oui, - sizeof(wmm_oui))) { - if (total_ie_len == - sizeof(struct ieee_types_wmm_parameter) || - total_ie_len == - sizeof(struct ieee_types_wmm_info)) - /* - * Only accept and copy the WMM IE if - * it matches the size expected for the - * WMM Info IE or the WMM Parameter IE. - */ - memcpy((u8 *) &bss_entry->wmm_ie, - current_ptr, total_ie_len); - } - break; - case WLAN_EID_RSN: - bss_entry->bcn_rsn_ie = - (struct ieee_types_generic *) current_ptr; - bss_entry->rsn_offset = (u16) (current_ptr - - bss_entry->beacon_buf); - break; - case WLAN_EID_BSS_AC_ACCESS_DELAY: - bss_entry->bcn_wapi_ie = - (struct ieee_types_generic *) current_ptr; - bss_entry->wapi_offset = (u16) (current_ptr - - bss_entry->beacon_buf); - break; - case WLAN_EID_HT_CAPABILITY: - bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *) - (current_ptr + - sizeof(struct ieee_types_header)); - bss_entry->ht_cap_offset = (u16) (current_ptr + - sizeof(struct ieee_types_header) - - bss_entry->beacon_buf); - break; - case WLAN_EID_HT_OPERATION: - bss_entry->bcn_ht_oper = - (struct ieee80211_ht_operation *)(current_ptr + - sizeof(struct ieee_types_header)); - bss_entry->ht_info_offset = (u16) (current_ptr + - sizeof(struct ieee_types_header) - - bss_entry->beacon_buf); - break; - case WLAN_EID_VHT_CAPABILITY: - bss_entry->disable_11ac = false; - bss_entry->bcn_vht_cap = - (void *)(current_ptr + - sizeof(struct ieee_types_header)); - bss_entry->vht_cap_offset = - (u16)((u8 *)bss_entry->bcn_vht_cap - - bss_entry->beacon_buf); - break; - case WLAN_EID_VHT_OPERATION: - bss_entry->bcn_vht_oper = - (void *)(current_ptr + - sizeof(struct ieee_types_header)); - bss_entry->vht_info_offset = - (u16)((u8 *)bss_entry->bcn_vht_oper - - bss_entry->beacon_buf); - break; - case WLAN_EID_BSS_COEX_2040: - bss_entry->bcn_bss_co_2040 = current_ptr; - bss_entry->bss_co_2040_offset = - (u16) (current_ptr - bss_entry->beacon_buf); - break; - case WLAN_EID_EXT_CAPABILITY: - bss_entry->bcn_ext_cap = current_ptr; - bss_entry->ext_cap_offset = - (u16) (current_ptr - bss_entry->beacon_buf); - break; - case WLAN_EID_OPMODE_NOTIF: - bss_entry->oper_mode = (void *)current_ptr; - bss_entry->oper_mode_offset = - (u16)((u8 *)bss_entry->oper_mode - - bss_entry->beacon_buf); - break; - default: - break; - } - - current_ptr += element_len + 2; - - /* Need to account for IE ID and IE Len */ - bytes_left -= (element_len + 2); - - } /* while (bytes_left > 2) */ - return ret; -} - -/* - * This function converts radio type scan parameter to a band configuration - * to be used in join command. - */ -static u8 -mwifiex_radio_type_to_band(u8 radio_type) -{ - switch (radio_type) { - case HostCmd_SCAN_RADIO_TYPE_A: - return BAND_A; - case HostCmd_SCAN_RADIO_TYPE_BG: - default: - return BAND_G; - } -} - -/* - * This is an internal function used to start a scan based on an input - * configuration. - * - * This uses the input user scan configuration information when provided in - * order to send the appropriate scan commands to firmware to populate or - * update the internal driver scan table. - */ -int mwifiex_scan_networks(struct mwifiex_private *priv, - const struct mwifiex_user_scan_cfg *user_scan_in) -{ - int ret; - struct mwifiex_adapter *adapter = priv->adapter; - struct cmd_ctrl_node *cmd_node; - union mwifiex_scan_cmd_config_tlv *scan_cfg_out; - struct mwifiex_ie_types_chan_list_param_set *chan_list_out; - struct mwifiex_chan_scan_param_set *scan_chan_list; - u8 filtered_scan; - u8 scan_current_chan_only; - u8 max_chan_per_scan; - unsigned long flags; - - if (adapter->scan_processing) { - mwifiex_dbg(adapter, WARN, - "cmd: Scan already in process...\n"); - return -EBUSY; - } - - if (priv->scan_block) { - mwifiex_dbg(adapter, WARN, - "cmd: Scan is blocked during association...\n"); - return -EBUSY; - } - - if (adapter->surprise_removed || adapter->is_cmd_timedout) { - mwifiex_dbg(adapter, ERROR, - "Ignore scan. Card removed or firmware in bad state\n"); - return -EFAULT; - } - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = true; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv), - GFP_KERNEL); - if (!scan_cfg_out) { - ret = -ENOMEM; - goto done; - } - - scan_chan_list = kcalloc(MWIFIEX_USER_SCAN_CHAN_MAX, - sizeof(struct mwifiex_chan_scan_param_set), - GFP_KERNEL); - if (!scan_chan_list) { - kfree(scan_cfg_out); - ret = -ENOMEM; - goto done; - } - - mwifiex_config_scan(priv, user_scan_in, &scan_cfg_out->config, - &chan_list_out, scan_chan_list, &max_chan_per_scan, - &filtered_scan, &scan_current_chan_only); - - ret = mwifiex_scan_channel_list(priv, max_chan_per_scan, filtered_scan, - &scan_cfg_out->config, chan_list_out, - scan_chan_list); - - /* Get scan command from scan_pending_q and put to cmd_pending_q */ - if (!ret) { - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - if (!list_empty(&adapter->scan_pending_q)) { - cmd_node = list_first_entry(&adapter->scan_pending_q, - struct cmd_ctrl_node, list); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, - true); - queue_work(adapter->workqueue, &adapter->main_work); - - /* Perform internal scan synchronously */ - if (!priv->scan_request) { - mwifiex_dbg(adapter, INFO, - "wait internal scan\n"); - mwifiex_wait_queue_complete(adapter, cmd_node); - } - } else { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - } - } - - kfree(scan_cfg_out); - kfree(scan_chan_list); -done: - if (ret) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - } - return ret; -} - -/* - * This function prepares a scan command to be sent to the firmware. - * - * This uses the scan command configuration sent to the command processing - * module in command preparation stage to configure a scan command structure - * to send to firmware. - * - * The fixed fields specifying the BSS type and BSSID filters as well as a - * variable number/length of TLVs are sent in the command to firmware. - * - * Preparation also includes - - * - Setting command ID, and proper size - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, - struct mwifiex_scan_cmd_config *scan_cfg) -{ - struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan; - - /* Set fixed field variables in scan command */ - scan_cmd->bss_mode = scan_cfg->bss_mode; - memcpy(scan_cmd->bssid, scan_cfg->specific_bssid, - sizeof(scan_cmd->bssid)); - memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN); - - /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ - cmd->size = cpu_to_le16((u16) (sizeof(scan_cmd->bss_mode) - + sizeof(scan_cmd->bssid) - + scan_cfg->tlv_buf_len + S_DS_GEN)); - - return 0; -} - -/* - * This function checks compatibility of requested network with current - * driver settings. - */ -int mwifiex_check_network_compatibility(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - int ret = -1; - - if (!bss_desc) - return -1; - - if ((mwifiex_get_cfp(priv, (u8) bss_desc->bss_band, - (u16) bss_desc->channel, 0))) { - switch (priv->bss_mode) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - ret = mwifiex_is_network_compatible(priv, bss_desc, - priv->bss_mode); - if (ret) - mwifiex_dbg(priv->adapter, ERROR, - "Incompatible network settings\n"); - break; - default: - ret = 0; - } - } - - return ret; -} - -/* This function checks if SSID string contains all zeroes or length is zero */ -static bool mwifiex_is_hidden_ssid(struct cfg80211_ssid *ssid) -{ - int idx; - - for (idx = 0; idx < ssid->ssid_len; idx++) { - if (ssid->ssid[idx]) - return false; - } - - return true; -} - -/* This function checks if any hidden SSID found in passive scan channels - * and save those channels for specific SSID active scan - */ -static int mwifiex_save_hidden_ssid_channels(struct mwifiex_private *priv, - struct cfg80211_bss *bss) -{ - struct mwifiex_bssdescriptor *bss_desc; - int ret; - int chid; - - /* Allocate and fill new bss descriptor */ - bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL); - if (!bss_desc) - return -ENOMEM; - - ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); - if (ret) - goto done; - - if (mwifiex_is_hidden_ssid(&bss_desc->ssid)) { - mwifiex_dbg(priv->adapter, INFO, "found hidden SSID\n"); - for (chid = 0 ; chid < MWIFIEX_USER_SCAN_CHAN_MAX; chid++) { - if (priv->hidden_chan[chid].chan_number == - bss->channel->hw_value) - break; - - if (!priv->hidden_chan[chid].chan_number) { - priv->hidden_chan[chid].chan_number = - bss->channel->hw_value; - priv->hidden_chan[chid].radio_type = - bss->channel->band; - priv->hidden_chan[chid].scan_type = - MWIFIEX_SCAN_TYPE_ACTIVE; - break; - } - } - } - -done: - kfree(bss_desc); - return 0; -} - -static int mwifiex_update_curr_bss_params(struct mwifiex_private *priv, - struct cfg80211_bss *bss) -{ - struct mwifiex_bssdescriptor *bss_desc; - int ret; - unsigned long flags; - - /* Allocate and fill new bss descriptor */ - bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); - if (!bss_desc) - return -ENOMEM; - - ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); - if (ret) - goto done; - - ret = mwifiex_check_network_compatibility(priv, bss_desc); - if (ret) - goto done; - - spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags); - /* Make a copy of current BSSID descriptor */ - memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc, - sizeof(priv->curr_bss_params.bss_descriptor)); - - /* The contents of beacon_ie will be copied to its own buffer - * in mwifiex_save_curr_bcn() - */ - mwifiex_save_curr_bcn(priv); - spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags); - -done: - /* beacon_ie buffer was allocated in function - * mwifiex_fill_new_bss_desc(). Free it now. - */ - kfree(bss_desc->beacon_buf); - kfree(bss_desc); - return 0; -} - -static int -mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, - u32 *bytes_left, u64 fw_tsf, u8 *radio_type, - bool ext_scan, s32 rssi_val) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_chan_freq_power *cfp; - struct cfg80211_bss *bss; - u8 bssid[ETH_ALEN]; - s32 rssi; - const u8 *ie_buf; - size_t ie_len; - u16 channel = 0; - u16 beacon_size = 0; - u32 curr_bcn_bytes; - u32 freq; - u16 beacon_period; - u16 cap_info_bitmap; - u8 *current_ptr; - u64 timestamp; - struct mwifiex_fixed_bcn_param *bcn_param; - struct mwifiex_bss_priv *bss_priv; - - if (*bytes_left >= sizeof(beacon_size)) { - /* Extract & convert beacon size from command buffer */ - beacon_size = le16_to_cpu(*(__le16 *)(*bss_info)); - *bytes_left -= sizeof(beacon_size); - *bss_info += sizeof(beacon_size); - } - - if (!beacon_size || beacon_size > *bytes_left) { - *bss_info += *bytes_left; - *bytes_left = 0; - return -EFAULT; - } - - /* Initialize the current working beacon pointer for this BSS - * iteration - */ - current_ptr = *bss_info; - - /* Advance the return beacon pointer past the current beacon */ - *bss_info += beacon_size; - *bytes_left -= beacon_size; - - curr_bcn_bytes = beacon_size; - - /* First 5 fields are bssid, RSSI(for legacy scan only), - * time stamp, beacon interval, and capability information - */ - if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) + - sizeof(struct mwifiex_fixed_bcn_param)) { - mwifiex_dbg(adapter, ERROR, - "InterpretIE: not enough bytes left\n"); - return -EFAULT; - } - - memcpy(bssid, current_ptr, ETH_ALEN); - current_ptr += ETH_ALEN; - curr_bcn_bytes -= ETH_ALEN; - - if (!ext_scan) { - rssi = (s32) *current_ptr; - rssi = (-rssi) * 100; /* Convert dBm to mBm */ - current_ptr += sizeof(u8); - curr_bcn_bytes -= sizeof(u8); - mwifiex_dbg(adapter, INFO, - "info: InterpretIE: RSSI=%d\n", rssi); - } else { - rssi = rssi_val; - } - - bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr; - current_ptr += sizeof(*bcn_param); - curr_bcn_bytes -= sizeof(*bcn_param); - - timestamp = le64_to_cpu(bcn_param->timestamp); - beacon_period = le16_to_cpu(bcn_param->beacon_period); - - cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap); - mwifiex_dbg(adapter, INFO, - "info: InterpretIE: capabilities=0x%X\n", - cap_info_bitmap); - - /* Rest of the current buffer are IE's */ - ie_buf = current_ptr; - ie_len = curr_bcn_bytes; - mwifiex_dbg(adapter, INFO, - "info: InterpretIE: IELength for this AP = %d\n", - curr_bcn_bytes); - - while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) { - u8 element_id, element_len; - - element_id = *current_ptr; - element_len = *(current_ptr + 1); - if (curr_bcn_bytes < element_len + - sizeof(struct ieee_types_header)) { - mwifiex_dbg(adapter, ERROR, - "%s: bytes left < IE length\n", __func__); - return -EFAULT; - } - if (element_id == WLAN_EID_DS_PARAMS) { - channel = *(current_ptr + - sizeof(struct ieee_types_header)); - break; - } - - current_ptr += element_len + sizeof(struct ieee_types_header); - curr_bcn_bytes -= element_len + - sizeof(struct ieee_types_header); - } - - if (channel) { - struct ieee80211_channel *chan; - u8 band; - - /* Skip entry if on csa closed channel */ - if (channel == priv->csa_chan) { - mwifiex_dbg(adapter, WARN, - "Dropping entry on csa closed channel\n"); - return 0; - } - - band = BAND_G; - if (radio_type) - band = mwifiex_radio_type_to_band(*radio_type & - (BIT(0) | BIT(1))); - - cfp = mwifiex_get_cfp(priv, band, channel, 0); - - freq = cfp ? cfp->freq : 0; - - chan = ieee80211_get_channel(priv->wdev.wiphy, freq); - - if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { - bss = cfg80211_inform_bss(priv->wdev.wiphy, - chan, CFG80211_BSS_FTYPE_UNKNOWN, - bssid, timestamp, - cap_info_bitmap, beacon_period, - ie_buf, ie_len, rssi, GFP_KERNEL); - if (bss) { - bss_priv = (struct mwifiex_bss_priv *)bss->priv; - bss_priv->band = band; - bss_priv->fw_tsf = fw_tsf; - if (priv->media_connected && - !memcmp(bssid, priv->curr_bss_params. - bss_descriptor.mac_address, - ETH_ALEN)) - mwifiex_update_curr_bss_params(priv, - bss); - cfg80211_put_bss(priv->wdev.wiphy, bss); - } - - if ((chan->flags & IEEE80211_CHAN_RADAR) || - (chan->flags & IEEE80211_CHAN_NO_IR)) { - mwifiex_dbg(adapter, INFO, - "radar or passive channel %d\n", - channel); - mwifiex_save_hidden_ssid_channels(priv, bss); - } - } - } else { - mwifiex_dbg(adapter, WARN, "missing BSS channel IE\n"); - } - - return 0; -} - -static void mwifiex_complete_scan(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - adapter->survey_idx = 0; - if (adapter->curr_cmd->wait_q_enabled) { - adapter->cmd_wait_q.status = 0; - if (!priv->scan_request) { - mwifiex_dbg(adapter, INFO, - "complete internal scan\n"); - mwifiex_complete_cmd(adapter, adapter->curr_cmd); - } - } -} - -/* This function checks if any hidden SSID found in passive scan channels - * and do specific SSID active scan for those channels - */ -static int -mwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv) -{ - int ret; - struct mwifiex_adapter *adapter = priv->adapter; - u8 id = 0; - struct mwifiex_user_scan_cfg *user_scan_cfg; - - if (adapter->active_scan_triggered || !priv->scan_request) { - adapter->active_scan_triggered = false; - return 0; - } - - if (!priv->hidden_chan[0].chan_number) { - mwifiex_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n"); - return 0; - } - user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); - - if (!user_scan_cfg) - return -ENOMEM; - - memset(user_scan_cfg, 0, sizeof(*user_scan_cfg)); - - for (id = 0; id < MWIFIEX_USER_SCAN_CHAN_MAX; id++) { - if (!priv->hidden_chan[id].chan_number) - break; - memcpy(&user_scan_cfg->chan_list[id], - &priv->hidden_chan[id], - sizeof(struct mwifiex_user_scan_chan)); - } - - adapter->active_scan_triggered = true; - user_scan_cfg->num_ssids = priv->scan_request->n_ssids; - user_scan_cfg->ssid_list = priv->scan_request->ssids; - - ret = mwifiex_scan_networks(priv, user_scan_cfg); - kfree(user_scan_cfg); - - memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan)); - - if (ret) { - dev_err(priv->adapter->dev, "scan failed: %d\n", ret); - return ret; - } - - return 0; -} -static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct cmd_ctrl_node *cmd_node, *tmp_node; - unsigned long flags; - - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - if (list_empty(&adapter->scan_pending_q)) { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - mwifiex_active_scan_req_for_passive_chan(priv); - - if (!adapter->ext_scan) - mwifiex_complete_scan(priv); - - if (priv->scan_request) { - mwifiex_dbg(adapter, INFO, - "info: notifying scan done\n"); - cfg80211_scan_done(priv->scan_request, 0); - priv->scan_request = NULL; - } else { - priv->scan_aborting = false; - mwifiex_dbg(adapter, INFO, - "info: scan already aborted\n"); - } - } else if ((priv->scan_aborting && !priv->scan_request) || - priv->scan_block) { - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, list) { - list_del(&cmd_node->list); - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - if (!adapter->active_scan_triggered) { - if (priv->scan_request) { - mwifiex_dbg(adapter, INFO, - "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); - priv->scan_request = NULL; - } else { - priv->scan_aborting = false; - mwifiex_dbg(adapter, INFO, - "info: scan already aborted\n"); - } - } - } else { - /* Get scan command from scan_pending_q and put to - * cmd_pending_q - */ - cmd_node = list_first_entry(&adapter->scan_pending_q, - struct cmd_ctrl_node, list); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); - } - - return; -} - -/* - * This function handles the command response of scan. - * - * The response buffer for the scan command has the following - * memory layout: - * - * .-------------------------------------------------------------. - * | Header (4 * sizeof(t_u16)): Standard command response hdr | - * .-------------------------------------------------------------. - * | BufSize (t_u16) : sizeof the BSS Description data | - * .-------------------------------------------------------------. - * | NumOfSet (t_u8) : Number of BSS Descs returned | - * .-------------------------------------------------------------. - * | BSSDescription data (variable, size given in BufSize) | - * .-------------------------------------------------------------. - * | TLV data (variable, size calculated using Header->Size, | - * | BufSize and sizeof the fixed fields above) | - * .-------------------------------------------------------------. - */ -int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - int ret = 0; - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_scan_rsp *scan_rsp; - struct mwifiex_ie_types_data *tlv_data; - struct mwifiex_ie_types_tsf_timestamp *tsf_tlv; - u8 *bss_info; - u32 scan_resp_size; - u32 bytes_left; - u32 idx; - u32 tlv_buf_size; - struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv; - struct chan_band_param_set *chan_band; - u8 is_bgscan_resp; - __le64 fw_tsf = 0; - u8 *radio_type; - - is_bgscan_resp = (le16_to_cpu(resp->command) - == HostCmd_CMD_802_11_BG_SCAN_QUERY); - if (is_bgscan_resp) - scan_rsp = &resp->params.bg_scan_query_resp.scan_resp; - else - scan_rsp = &resp->params.scan_resp; - - - if (scan_rsp->number_of_sets > MWIFIEX_MAX_AP) { - mwifiex_dbg(adapter, ERROR, - "SCAN_RESP: too many AP returned (%d)\n", - scan_rsp->number_of_sets); - ret = -1; - goto check_next_scan; - } - - /* Check csa channel expiry before parsing scan response */ - mwifiex_11h_get_csa_closed_channel(priv); - - bytes_left = le16_to_cpu(scan_rsp->bss_descript_size); - mwifiex_dbg(adapter, INFO, - "info: SCAN_RESP: bss_descript_size %d\n", - bytes_left); - - scan_resp_size = le16_to_cpu(resp->size); - - mwifiex_dbg(adapter, INFO, - "info: SCAN_RESP: returned %d APs before parsing\n", - scan_rsp->number_of_sets); - - bss_info = scan_rsp->bss_desc_and_tlv_buffer; - - /* - * The size of the TLV buffer is equal to the entire command response - * size (scan_resp_size) minus the fixed fields (sizeof()'s), the - * BSS Descriptions (bss_descript_size as bytesLef) and the command - * response header (S_DS_GEN) - */ - tlv_buf_size = scan_resp_size - (bytes_left - + sizeof(scan_rsp->bss_descript_size) - + sizeof(scan_rsp->number_of_sets) - + S_DS_GEN); - - tlv_data = (struct mwifiex_ie_types_data *) (scan_rsp-> - bss_desc_and_tlv_buffer + - bytes_left); - - /* Search the TLV buffer space in the scan response for any valid - TLVs */ - mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, - TLV_TYPE_TSFTIMESTAMP, - (struct mwifiex_ie_types_data **) - &tsf_tlv); - - /* Search the TLV buffer space in the scan response for any valid - TLVs */ - mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, - TLV_TYPE_CHANNELBANDLIST, - (struct mwifiex_ie_types_data **) - &chan_band_tlv); - - for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { - /* - * If the TSF TLV was appended to the scan results, save this - * entry's TSF value in the fw_tsf field. It is the firmware's - * TSF value at the time the beacon or probe response was - * received. - */ - if (tsf_tlv) - memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE], - sizeof(fw_tsf)); - - if (chan_band_tlv) { - chan_band = &chan_band_tlv->chan_band_param[idx]; - radio_type = &chan_band->radio_type; - } else { - radio_type = NULL; - } - - ret = mwifiex_parse_single_response_buf(priv, &bss_info, - &bytes_left, - le64_to_cpu(fw_tsf), - radio_type, false, 0); - if (ret) - goto check_next_scan; - } - -check_next_scan: - mwifiex_check_next_scan_command(priv); - return ret; -} - -/* - * This function prepares an extended scan command to be sent to the firmware - * - * This uses the scan command configuration sent to the command processing - * module in command preparation stage to configure a extended scan command - * structure to send to firmware. - */ -int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - void *data_buf) -{ - struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan; - struct mwifiex_scan_cmd_config *scan_cfg = data_buf; - - memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); - - /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ - cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved) - + scan_cfg->tlv_buf_len + S_DS_GEN)); - - return 0; -} - -static void -mwifiex_update_chan_statistics(struct mwifiex_private *priv, - struct mwifiex_ietypes_chanstats *tlv_stat) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u8 i, num_chan; - struct mwifiex_fw_chan_stats *fw_chan_stats; - struct mwifiex_chan_stats chan_stats; - - fw_chan_stats = (void *)((u8 *)tlv_stat + - sizeof(struct mwifiex_ie_types_header)); - num_chan = le16_to_cpu(tlv_stat->header.len) / - sizeof(struct mwifiex_chan_stats); - - for (i = 0 ; i < num_chan; i++) { - chan_stats.chan_num = fw_chan_stats->chan_num; - chan_stats.bandcfg = fw_chan_stats->bandcfg; - chan_stats.flags = fw_chan_stats->flags; - chan_stats.noise = fw_chan_stats->noise; - chan_stats.total_bss = le16_to_cpu(fw_chan_stats->total_bss); - chan_stats.cca_scan_dur = - le16_to_cpu(fw_chan_stats->cca_scan_dur); - chan_stats.cca_busy_dur = - le16_to_cpu(fw_chan_stats->cca_busy_dur); - mwifiex_dbg(adapter, INFO, - "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n", - chan_stats.chan_num, - chan_stats.noise, - chan_stats.total_bss, - chan_stats.cca_scan_dur, - chan_stats.cca_busy_dur); - memcpy(&adapter->chan_stats[adapter->survey_idx++], &chan_stats, - sizeof(struct mwifiex_chan_stats)); - fw_chan_stats++; - } -} - -/* This function handles the command response of extended scan */ -int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_scan_ext *ext_scan_resp; - struct mwifiex_ie_types_header *tlv; - struct mwifiex_ietypes_chanstats *tlv_stat; - u16 buf_left, type, len; - - struct host_cmd_ds_command *cmd_ptr; - struct cmd_ctrl_node *cmd_node; - unsigned long cmd_flags, scan_flags; - bool complete_scan = false; - - mwifiex_dbg(adapter, INFO, "info: EXT scan returns successfully\n"); - - ext_scan_resp = &resp->params.ext_scan; - - tlv = (void *)ext_scan_resp->tlv_buffer; - buf_left = le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN - - 1); - - while (buf_left >= sizeof(struct mwifiex_ie_types_header)) { - type = le16_to_cpu(tlv->type); - len = le16_to_cpu(tlv->len); - - if (buf_left < (sizeof(struct mwifiex_ie_types_header) + len)) { - mwifiex_dbg(adapter, ERROR, - "error processing scan response TLVs"); - break; - } - - switch (type) { - case TLV_TYPE_CHANNEL_STATS: - tlv_stat = (void *)tlv; - mwifiex_update_chan_statistics(priv, tlv_stat); - break; - default: - break; - } - - buf_left -= len + sizeof(struct mwifiex_ie_types_header); - tlv = (void *)((u8 *)tlv + len + - sizeof(struct mwifiex_ie_types_header)); - } - - spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_flags); - spin_lock_irqsave(&adapter->scan_pending_q_lock, scan_flags); - if (list_empty(&adapter->scan_pending_q)) { - complete_scan = true; - list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) { - cmd_ptr = (void *)cmd_node->cmd_skb->data; - if (le16_to_cpu(cmd_ptr->command) == - HostCmd_CMD_802_11_SCAN_EXT) { - mwifiex_dbg(adapter, INFO, - "Scan pending in command pending list"); - complete_scan = false; - break; - } - } - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, scan_flags); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, cmd_flags); - - if (complete_scan) - mwifiex_complete_scan(priv); - - return 0; -} - -/* This function This function handles the event extended scan report. It - * parses extended scan results and informs to cfg80211 stack. - */ -int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, - void *buf) -{ - int ret = 0; - struct mwifiex_adapter *adapter = priv->adapter; - u8 *bss_info; - u32 bytes_left, bytes_left_for_tlv, idx; - u16 type, len; - struct mwifiex_ie_types_data *tlv; - struct mwifiex_ie_types_bss_scan_rsp *scan_rsp_tlv; - struct mwifiex_ie_types_bss_scan_info *scan_info_tlv; - u8 *radio_type; - u64 fw_tsf = 0; - s32 rssi = 0; - struct mwifiex_event_scan_result *event_scan = buf; - u8 num_of_set = event_scan->num_of_set; - u8 *scan_resp = buf + sizeof(struct mwifiex_event_scan_result); - u16 scan_resp_size = le16_to_cpu(event_scan->buf_size); - - if (num_of_set > MWIFIEX_MAX_AP) { - mwifiex_dbg(adapter, ERROR, - "EXT_SCAN: Invalid number of AP returned (%d)!!\n", - num_of_set); - ret = -1; - goto check_next_scan; - } - - bytes_left = scan_resp_size; - mwifiex_dbg(adapter, INFO, - "EXT_SCAN: size %d, returned %d APs...", - scan_resp_size, num_of_set); - mwifiex_dbg_dump(adapter, CMD_D, "EXT_SCAN buffer:", buf, - scan_resp_size + - sizeof(struct mwifiex_event_scan_result)); - - tlv = (struct mwifiex_ie_types_data *)scan_resp; - - for (idx = 0; idx < num_of_set && bytes_left; idx++) { - type = le16_to_cpu(tlv->header.type); - len = le16_to_cpu(tlv->header.len); - if (bytes_left < sizeof(struct mwifiex_ie_types_header) + len) { - mwifiex_dbg(adapter, ERROR, - "EXT_SCAN: Error bytes left < TLV length\n"); - break; - } - scan_rsp_tlv = NULL; - scan_info_tlv = NULL; - bytes_left_for_tlv = bytes_left; - - /* BSS response TLV with beacon or probe response buffer - * at the initial position of each descriptor - */ - if (type != TLV_TYPE_BSS_SCAN_RSP) - break; - - bss_info = (u8 *)tlv; - scan_rsp_tlv = (struct mwifiex_ie_types_bss_scan_rsp *)tlv; - tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); - bytes_left_for_tlv -= - (len + sizeof(struct mwifiex_ie_types_header)); - - while (bytes_left_for_tlv >= - sizeof(struct mwifiex_ie_types_header) && - le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) { - type = le16_to_cpu(tlv->header.type); - len = le16_to_cpu(tlv->header.len); - if (bytes_left_for_tlv < - sizeof(struct mwifiex_ie_types_header) + len) { - mwifiex_dbg(adapter, ERROR, - "EXT_SCAN: Error in processing TLV,\t" - "bytes left < TLV length\n"); - scan_rsp_tlv = NULL; - bytes_left_for_tlv = 0; - continue; - } - switch (type) { - case TLV_TYPE_BSS_SCAN_INFO: - scan_info_tlv = - (struct mwifiex_ie_types_bss_scan_info *)tlv; - if (len != - sizeof(struct mwifiex_ie_types_bss_scan_info) - - sizeof(struct mwifiex_ie_types_header)) { - bytes_left_for_tlv = 0; - continue; - } - break; - default: - break; - } - tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); - bytes_left -= - (len + sizeof(struct mwifiex_ie_types_header)); - bytes_left_for_tlv -= - (len + sizeof(struct mwifiex_ie_types_header)); - } - - if (!scan_rsp_tlv) - break; - - /* Advance pointer to the beacon buffer length and - * update the bytes count so that the function - * wlan_interpret_bss_desc_with_ie() can handle the - * scan buffer withut any change - */ - bss_info += sizeof(u16); - bytes_left -= sizeof(u16); - - if (scan_info_tlv) { - rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi)); - rssi *= 100; /* Convert dBm to mBm */ - mwifiex_dbg(adapter, INFO, - "info: InterpretIE: RSSI=%d\n", rssi); - fw_tsf = le64_to_cpu(scan_info_tlv->tsf); - radio_type = &scan_info_tlv->radio_type; - } else { - radio_type = NULL; - } - ret = mwifiex_parse_single_response_buf(priv, &bss_info, - &bytes_left, fw_tsf, - radio_type, true, rssi); - if (ret) - goto check_next_scan; - } - -check_next_scan: - if (!event_scan->more_event) - mwifiex_check_next_scan_command(priv); - - return ret; -} - -/* - * This function prepares command for background scan query. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting background scan flush parameter - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd) -{ - struct host_cmd_ds_802_11_bg_scan_query *bg_query = - &cmd->params.bg_scan_query; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query) - + S_DS_GEN); - - bg_query->flush = 1; - - return 0; -} - -/* - * This function inserts scan command node to the scan pending queue. - */ -void -mwifiex_queue_scan_cmd(struct mwifiex_private *priv, - struct cmd_ctrl_node *cmd_node) -{ - struct mwifiex_adapter *adapter = priv->adapter; - unsigned long flags; - - cmd_node->wait_q_enabled = true; - cmd_node->condition = &adapter->scan_wait_q_woken; - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - list_add_tail(&cmd_node->list, &adapter->scan_pending_q); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); -} - -/* - * This function sends a scan command for all available channels to the - * firmware, filtered on a specific SSID. - */ -static int mwifiex_scan_specific_ssid(struct mwifiex_private *priv, - struct cfg80211_ssid *req_ssid) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - struct mwifiex_user_scan_cfg *scan_cfg; - - if (adapter->scan_processing) { - mwifiex_dbg(adapter, WARN, - "cmd: Scan already in process...\n"); - return -EBUSY; - } - - if (priv->scan_block) { - mwifiex_dbg(adapter, WARN, - "cmd: Scan is blocked during association...\n"); - return -EBUSY; - } - - scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL); - if (!scan_cfg) - return -ENOMEM; - - scan_cfg->ssid_list = req_ssid; - scan_cfg->num_ssids = 1; - - ret = mwifiex_scan_networks(priv, scan_cfg); - - kfree(scan_cfg); - return ret; -} - -/* - * Sends IOCTL request to start a scan. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - * - * Scan command can be issued for both normal scan and specific SSID - * scan, depending upon whether an SSID is provided or not. - */ -int mwifiex_request_scan(struct mwifiex_private *priv, - struct cfg80211_ssid *req_ssid) -{ - int ret; - - if (down_interruptible(&priv->async_sem)) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: acquire semaphore fail\n", - __func__); - return -1; - } - - priv->adapter->scan_wait_q_woken = false; - - if (req_ssid && req_ssid->ssid_len != 0) - /* Specific SSID scan */ - ret = mwifiex_scan_specific_ssid(priv, req_ssid); - else - /* Normal scan */ - ret = mwifiex_scan_networks(priv, NULL); - - up(&priv->async_sem); - - return ret; -} - -/* - * This function appends the vendor specific IE TLV to a buffer. - */ -int -mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, - u16 vsie_mask, u8 **buffer) -{ - int id, ret_len = 0; - struct mwifiex_ie_types_vendor_param_set *vs_param_set; - - if (!buffer) - return 0; - if (!(*buffer)) - return 0; - - /* - * Traverse through the saved vendor specific IE array and append - * the selected(scan/assoc/adhoc) IE as TLV to the command - */ - for (id = 0; id < MWIFIEX_MAX_VSIE_NUM; id++) { - if (priv->vs_ie[id].mask & vsie_mask) { - vs_param_set = - (struct mwifiex_ie_types_vendor_param_set *) - *buffer; - vs_param_set->header.type = - cpu_to_le16(TLV_TYPE_PASSTHROUGH); - vs_param_set->header.len = - cpu_to_le16((((u16) priv->vs_ie[id].ie[1]) - & 0x00FF) + 2); - memcpy(vs_param_set->ie, priv->vs_ie[id].ie, - le16_to_cpu(vs_param_set->header.len)); - *buffer += le16_to_cpu(vs_param_set->header.len) + - sizeof(struct mwifiex_ie_types_header); - ret_len += le16_to_cpu(vs_param_set->header.len) + - sizeof(struct mwifiex_ie_types_header); - } - } - return ret_len; -} - -/* - * This function saves a beacon buffer of the current BSS descriptor. - * - * The current beacon buffer is saved so that it can be restored in the - * following cases that makes the beacon buffer not to contain the current - * ssid's beacon buffer. - * - The current ssid was not found somehow in the last scan. - * - The current ssid was the last entry of the scan table and overloaded. - */ -void -mwifiex_save_curr_bcn(struct mwifiex_private *priv) -{ - struct mwifiex_bssdescriptor *curr_bss = - &priv->curr_bss_params.bss_descriptor; - - if (!curr_bss->beacon_buf_size) - return; - - /* allocate beacon buffer at 1st time; or if it's size has changed */ - if (!priv->curr_bcn_buf || - priv->curr_bcn_size != curr_bss->beacon_buf_size) { - priv->curr_bcn_size = curr_bss->beacon_buf_size; - - kfree(priv->curr_bcn_buf); - priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size, - GFP_ATOMIC); - if (!priv->curr_bcn_buf) - return; - } - - memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf, - curr_bss->beacon_buf_size); - mwifiex_dbg(priv->adapter, INFO, - "info: current beacon saved %d\n", - priv->curr_bcn_size); - - curr_bss->beacon_buf = priv->curr_bcn_buf; - - /* adjust the pointers in the current BSS descriptor */ - if (curr_bss->bcn_wpa_ie) - curr_bss->bcn_wpa_ie = - (struct ieee_types_vendor_specific *) - (curr_bss->beacon_buf + - curr_bss->wpa_offset); - - if (curr_bss->bcn_rsn_ie) - curr_bss->bcn_rsn_ie = (struct ieee_types_generic *) - (curr_bss->beacon_buf + - curr_bss->rsn_offset); - - if (curr_bss->bcn_ht_cap) - curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *) - (curr_bss->beacon_buf + - curr_bss->ht_cap_offset); - - if (curr_bss->bcn_ht_oper) - curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *) - (curr_bss->beacon_buf + - curr_bss->ht_info_offset); - - if (curr_bss->bcn_vht_cap) - curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf + - curr_bss->vht_cap_offset); - - if (curr_bss->bcn_vht_oper) - curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf + - curr_bss->vht_info_offset); - - if (curr_bss->bcn_bss_co_2040) - curr_bss->bcn_bss_co_2040 = - (curr_bss->beacon_buf + curr_bss->bss_co_2040_offset); - - if (curr_bss->bcn_ext_cap) - curr_bss->bcn_ext_cap = curr_bss->beacon_buf + - curr_bss->ext_cap_offset; - - if (curr_bss->oper_mode) - curr_bss->oper_mode = (void *)(curr_bss->beacon_buf + - curr_bss->oper_mode_offset); -} - -/* - * This function frees the current BSS descriptor beacon buffer. - */ -void -mwifiex_free_curr_bcn(struct mwifiex_private *priv) -{ - kfree(priv->curr_bcn_buf); - priv->curr_bcn_buf = NULL; -} diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c deleted file mode 100644 index 78a8474e1a3d..000000000000 --- a/drivers/net/wireless/mwifiex/sdio.c +++ /dev/null @@ -1,2684 +0,0 @@ -/* - * Marvell Wireless LAN device driver: SDIO specific handling - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "sdio.h" - - -#define SDIO_VERSION "1.0" - -/* The mwifiex_sdio_remove() callback function is called when - * user removes this module from kernel space or ejects - * the card from the slot. The driver handles these 2 cases - * differently. - * If the user is removing the module, the few commands (FUNC_SHUTDOWN, - * HS_CANCEL etc.) are sent to the firmware. - * If the card is removed, there is no need to send these command. - * - * The variable 'user_rmmod' is used to distinguish these two - * scenarios. This flag is initialized as FALSE in case the card - * is removed, and will be set to TRUE for module removal when - * module_exit function is called. - */ -static u8 user_rmmod; - -static struct mwifiex_if_ops sdio_ops; -static unsigned long iface_work_flags; - -static struct semaphore add_remove_card_sem; - -static struct memory_type_mapping generic_mem_type_map[] = { - {"DUMP", NULL, 0, 0xDD}, -}; - -static struct memory_type_mapping mem_type_mapping_tbl[] = { - {"ITCM", NULL, 0, 0xF0}, - {"DTCM", NULL, 0, 0xF1}, - {"SQRAM", NULL, 0, 0xF2}, - {"APU", NULL, 0, 0xF3}, - {"CIU", NULL, 0, 0xF4}, - {"ICU", NULL, 0, 0xF5}, - {"MAC", NULL, 0, 0xF6}, - {"EXT7", NULL, 0, 0xF7}, - {"EXT8", NULL, 0, 0xF8}, - {"EXT9", NULL, 0, 0xF9}, - {"EXT10", NULL, 0, 0xFA}, - {"EXT11", NULL, 0, 0xFB}, - {"EXT12", NULL, 0, 0xFC}, - {"EXT13", NULL, 0, 0xFD}, - {"EXTLAST", NULL, 0, 0xFE}, -}; - -/* - * SDIO probe. - * - * This function probes an mwifiex device and registers it. It allocates - * the card structure, enables SDIO function number and initiates the - * device registration and initialization procedure by adding a logical - * interface. - */ -static int -mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) -{ - int ret; - struct sdio_mmc_card *card = NULL; - - pr_debug("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", - func->vendor, func->device, func->class, func->num); - - card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); - if (!card) - return -ENOMEM; - - card->func = func; - card->device_id = id; - - func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; - - if (id->driver_data) { - struct mwifiex_sdio_device *data = (void *)id->driver_data; - - card->firmware = data->firmware; - card->reg = data->reg; - card->max_ports = data->max_ports; - card->mp_agg_pkt_limit = data->mp_agg_pkt_limit; - card->supports_sdio_new_mode = data->supports_sdio_new_mode; - card->has_control_mask = data->has_control_mask; - card->tx_buf_size = data->tx_buf_size; - card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size; - card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size; - card->can_dump_fw = data->can_dump_fw; - card->fw_dump_enh = data->fw_dump_enh; - card->can_auto_tdls = data->can_auto_tdls; - card->can_ext_scan = data->can_ext_scan; - } - - sdio_claim_host(func); - ret = sdio_enable_func(func); - sdio_release_host(func); - - if (ret) { - pr_err("%s: failed to enable function\n", __func__); - kfree(card); - return -EIO; - } - - if (mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops, - MWIFIEX_SDIO)) { - pr_err("%s: add card failed\n", __func__); - kfree(card); - sdio_claim_host(func); - ret = sdio_disable_func(func); - sdio_release_host(func); - ret = -1; - } - - return ret; -} - -/* - * SDIO resume. - * - * Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not resumed, this function turns on the traffic and - * sends a host sleep cancel request to the firmware. - */ -static int mwifiex_sdio_resume(struct device *dev) -{ - struct sdio_func *func = dev_to_sdio_func(dev); - struct sdio_mmc_card *card; - struct mwifiex_adapter *adapter; - mmc_pm_flag_t pm_flag = 0; - - if (func) { - pm_flag = sdio_get_host_pm_caps(func); - card = sdio_get_drvdata(func); - if (!card || !card->adapter) { - pr_err("resume: invalid card or adapter\n"); - return 0; - } - } else { - pr_err("resume: sdio_func is not specified\n"); - return 0; - } - - adapter = card->adapter; - - if (!adapter->is_suspended) { - mwifiex_dbg(adapter, WARN, - "device already resumed\n"); - return 0; - } - - adapter->is_suspended = false; - - /* Disable Host Sleep */ - mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), - MWIFIEX_ASYNC_CMD); - - return 0; -} - -/* - * SDIO remove. - * - * This function removes the interface and frees up the card structure. - */ -static void -mwifiex_sdio_remove(struct sdio_func *func) -{ - struct sdio_mmc_card *card; - struct mwifiex_adapter *adapter; - struct mwifiex_private *priv; - - card = sdio_get_drvdata(func); - if (!card) - return; - - adapter = card->adapter; - if (!adapter || !adapter->priv_num) - return; - - mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num); - - if (user_rmmod) { - if (adapter->is_suspended) - mwifiex_sdio_resume(adapter->dev); - - mwifiex_deauthenticate_all(adapter); - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - mwifiex_disable_auto_ds(priv); - mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); - } - - mwifiex_remove_card(card->adapter, &add_remove_card_sem); -} - -/* - * SDIO suspend. - * - * Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not suspended, this function allocates and sends a host - * sleep activate request to the firmware and turns off the traffic. - */ -static int mwifiex_sdio_suspend(struct device *dev) -{ - struct sdio_func *func = dev_to_sdio_func(dev); - struct sdio_mmc_card *card; - struct mwifiex_adapter *adapter; - mmc_pm_flag_t pm_flag = 0; - int ret = 0; - - if (func) { - pm_flag = sdio_get_host_pm_caps(func); - pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", - sdio_func_id(func), pm_flag); - if (!(pm_flag & MMC_PM_KEEP_POWER)) { - pr_err("%s: cannot remain alive while host is" - " suspended\n", sdio_func_id(func)); - return -ENOSYS; - } - - card = sdio_get_drvdata(func); - if (!card || !card->adapter) { - pr_err("suspend: invalid card or adapter\n"); - return 0; - } - } else { - pr_err("suspend: sdio_func is not specified\n"); - return 0; - } - - adapter = card->adapter; - - /* Enable the Host Sleep */ - if (!mwifiex_enable_hs(adapter)) { - mwifiex_dbg(adapter, ERROR, - "cmd: failed to suspend\n"); - adapter->hs_enabling = false; - return -EFAULT; - } - - mwifiex_dbg(adapter, INFO, - "cmd: suspend with MMC_PM_KEEP_POWER\n"); - ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); - - /* Indicate device suspended */ - adapter->is_suspended = true; - adapter->hs_enabling = false; - - return ret; -} - -/* Device ID for SD8786 */ -#define SDIO_DEVICE_ID_MARVELL_8786 (0x9116) -/* Device ID for SD8787 */ -#define SDIO_DEVICE_ID_MARVELL_8787 (0x9119) -/* Device ID for SD8797 */ -#define SDIO_DEVICE_ID_MARVELL_8797 (0x9129) -/* Device ID for SD8897 */ -#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d) -/* Device ID for SD8887 */ -#define SDIO_DEVICE_ID_MARVELL_8887 (0x9135) -/* Device ID for SD8801 */ -#define SDIO_DEVICE_ID_MARVELL_8801 (0x9139) -/* Device ID for SD8997 */ -#define SDIO_DEVICE_ID_MARVELL_8997 (0x9141) - - -/* WLAN IDs */ -static const struct sdio_device_id mwifiex_ids[] = { - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786), - .driver_data = (unsigned long) &mwifiex_sdio_sd8786}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787), - .driver_data = (unsigned long) &mwifiex_sdio_sd8787}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797), - .driver_data = (unsigned long) &mwifiex_sdio_sd8797}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897), - .driver_data = (unsigned long) &mwifiex_sdio_sd8897}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887), - .driver_data = (unsigned long)&mwifiex_sdio_sd8887}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801), - .driver_data = (unsigned long)&mwifiex_sdio_sd8801}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997), - .driver_data = (unsigned long)&mwifiex_sdio_sd8997}, - {}, -}; - -MODULE_DEVICE_TABLE(sdio, mwifiex_ids); - -static const struct dev_pm_ops mwifiex_sdio_pm_ops = { - .suspend = mwifiex_sdio_suspend, - .resume = mwifiex_sdio_resume, -}; - -static struct sdio_driver mwifiex_sdio = { - .name = "mwifiex_sdio", - .id_table = mwifiex_ids, - .probe = mwifiex_sdio_probe, - .remove = mwifiex_sdio_remove, - .drv = { - .owner = THIS_MODULE, - .pm = &mwifiex_sdio_pm_ops, - } -}; - -/* Write data into SDIO card register. Caller claims SDIO device. */ -static int -mwifiex_write_reg_locked(struct sdio_func *func, u32 reg, u8 data) -{ - int ret = -1; - sdio_writeb(func, data, reg, &ret); - return ret; -} - -/* - * This function writes data into SDIO card register. - */ -static int -mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data) -{ - struct sdio_mmc_card *card = adapter->card; - int ret; - - sdio_claim_host(card->func); - ret = mwifiex_write_reg_locked(card->func, reg, data); - sdio_release_host(card->func); - - return ret; -} - -/* - * This function reads data from SDIO card register. - */ -static int -mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data) -{ - struct sdio_mmc_card *card = adapter->card; - int ret = -1; - u8 val; - - sdio_claim_host(card->func); - val = sdio_readb(card->func, reg, &ret); - sdio_release_host(card->func); - - *data = val; - - return ret; -} - -/* - * This function writes multiple data into SDIO card memory. - * - * This does not work in suspended mode. - */ -static int -mwifiex_write_data_sync(struct mwifiex_adapter *adapter, - u8 *buffer, u32 pkt_len, u32 port) -{ - struct sdio_mmc_card *card = adapter->card; - int ret; - u8 blk_mode = - (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; - u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; - u32 blk_cnt = - (blk_mode == - BLOCK_MODE) ? (pkt_len / - MWIFIEX_SDIO_BLOCK_SIZE) : pkt_len; - u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); - - if (adapter->is_suspended) { - mwifiex_dbg(adapter, ERROR, - "%s: not allowed while suspended\n", __func__); - return -1; - } - - sdio_claim_host(card->func); - - ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size); - - sdio_release_host(card->func); - - return ret; -} - -/* - * This function reads multiple data from SDIO card memory. - */ -static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, - u32 len, u32 port, u8 claim) -{ - struct sdio_mmc_card *card = adapter->card; - int ret; - u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE - : BLOCK_MODE; - u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; - u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (len / MWIFIEX_SDIO_BLOCK_SIZE) - : len; - u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); - - if (claim) - sdio_claim_host(card->func); - - ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size); - - if (claim) - sdio_release_host(card->func); - - return ret; -} - -/* - * This function wakes up the card. - * - * A host power up command is written to the card configuration - * register to wake up the card. - */ -static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) -{ - mwifiex_dbg(adapter, EVENT, - "event: wakeup device...\n"); - - return mwifiex_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP); -} - -/* - * This function is called after the card has woken up. - * - * The card configuration register is reset. - */ -static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) -{ - mwifiex_dbg(adapter, EVENT, - "cmd: wakeup device completed\n"); - - return mwifiex_write_reg(adapter, CONFIGURATION_REG, 0); -} - -/* - * This function is used to initialize IO ports for the - * chipsets supporting SDIO new mode eg SD8897. - */ -static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter) -{ - u8 reg; - struct sdio_mmc_card *card = adapter->card; - - adapter->ioport = MEM_PORT; - - /* enable sdio new mode */ - if (mwifiex_read_reg(adapter, card->reg->card_cfg_2_1_reg, ®)) - return -1; - if (mwifiex_write_reg(adapter, card->reg->card_cfg_2_1_reg, - reg | CMD53_NEW_MODE)) - return -1; - - /* Configure cmd port and enable reading rx length from the register */ - if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_0, ®)) - return -1; - if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_0, - reg | CMD_PORT_RD_LEN_EN)) - return -1; - - /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is - * completed - */ - if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_1, ®)) - return -1; - if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_1, - reg | CMD_PORT_AUTO_EN)) - return -1; - - return 0; -} - -/* This function initializes the IO ports. - * - * The following operations are performed - - * - Read the IO ports (0, 1 and 2) - * - Set host interrupt Reset-To-Read to clear - * - Set auto re-enable interrupt - */ -static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) -{ - u8 reg; - struct sdio_mmc_card *card = adapter->card; - - adapter->ioport = 0; - - if (card->supports_sdio_new_mode) { - if (mwifiex_init_sdio_new_mode(adapter)) - return -1; - goto cont; - } - - /* Read the IO port */ - if (!mwifiex_read_reg(adapter, card->reg->io_port_0_reg, ®)) - adapter->ioport |= (reg & 0xff); - else - return -1; - - if (!mwifiex_read_reg(adapter, card->reg->io_port_1_reg, ®)) - adapter->ioport |= ((reg & 0xff) << 8); - else - return -1; - - if (!mwifiex_read_reg(adapter, card->reg->io_port_2_reg, ®)) - adapter->ioport |= ((reg & 0xff) << 16); - else - return -1; -cont: - mwifiex_dbg(adapter, INFO, - "info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); - - /* Set Host interrupt reset to read to clear */ - if (!mwifiex_read_reg(adapter, card->reg->host_int_rsr_reg, ®)) - mwifiex_write_reg(adapter, card->reg->host_int_rsr_reg, - reg | card->reg->sdio_int_mask); - else - return -1; - - /* Dnld/Upld ready set to auto reset */ - if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, ®)) - mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg, - reg | AUTO_RE_ENABLE_INT); - else - return -1; - - return 0; -} - -/* - * This function sends data to the card. - */ -static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, - u8 *payload, u32 pkt_len, u32 port) -{ - u32 i = 0; - int ret; - - do { - ret = mwifiex_write_data_sync(adapter, payload, pkt_len, port); - if (ret) { - i++; - mwifiex_dbg(adapter, ERROR, - "host_to_card, write iomem\t" - "(%d) failed: %d\n", i, ret); - if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) - mwifiex_dbg(adapter, ERROR, - "write CFG reg failed\n"); - - ret = -1; - if (i > MAX_WRITE_IOMEM_RETRY) - return ret; - } - } while (ret == -1); - - return ret; -} - -/* - * This function gets the read port. - * - * If control port bit is set in MP read bitmap, the control port - * is returned, otherwise the current read port is returned and - * the value is increased (provided it does not reach the maximum - * limit, in which case it is reset to 1) - */ -static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - u32 rd_bitmap = card->mp_rd_bitmap; - - mwifiex_dbg(adapter, DATA, - "data: mp_rd_bitmap=0x%08x\n", rd_bitmap); - - if (card->supports_sdio_new_mode) { - if (!(rd_bitmap & reg->data_port_mask)) - return -1; - } else { - if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask))) - return -1; - } - - if ((card->has_control_mask) && - (card->mp_rd_bitmap & CTRL_PORT_MASK)) { - card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK); - *port = CTRL_PORT; - mwifiex_dbg(adapter, DATA, - "data: port=%d mp_rd_bitmap=0x%08x\n", - *port, card->mp_rd_bitmap); - return 0; - } - - if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port))) - return -1; - - /* We are now handling the SDIO data ports */ - card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port)); - *port = card->curr_rd_port; - - if (++card->curr_rd_port == card->max_ports) - card->curr_rd_port = reg->start_rd_port; - - mwifiex_dbg(adapter, DATA, - "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", - *port, rd_bitmap, card->mp_rd_bitmap); - - return 0; -} - -/* - * This function gets the write port for data. - * - * The current write port is returned if available and the value is - * increased (provided it does not reach the maximum limit, in which - * case it is reset to 1) - */ -static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - u32 wr_bitmap = card->mp_wr_bitmap; - - mwifiex_dbg(adapter, DATA, - "data: mp_wr_bitmap=0x%08x\n", wr_bitmap); - - if (!(wr_bitmap & card->mp_data_port_mask)) { - adapter->data_sent = true; - return -EBUSY; - } - - if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { - card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port)); - *port = card->curr_wr_port; - if (++card->curr_wr_port == card->mp_end_port) - card->curr_wr_port = reg->start_wr_port; - } else { - adapter->data_sent = true; - return -EBUSY; - } - - if ((card->has_control_mask) && (*port == CTRL_PORT)) { - mwifiex_dbg(adapter, ERROR, - "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", - *port, card->curr_wr_port, wr_bitmap, - card->mp_wr_bitmap); - return -1; - } - - mwifiex_dbg(adapter, DATA, - "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", - *port, wr_bitmap, card->mp_wr_bitmap); - - return 0; -} - -/* - * This function polls the card status. - */ -static int -mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) -{ - struct sdio_mmc_card *card = adapter->card; - u32 tries; - u8 cs; - - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs)) - break; - else if ((cs & bits) == bits) - return 0; - - usleep_range(10, 20); - } - - mwifiex_dbg(adapter, ERROR, - "poll card status failed, tries = %d\n", tries); - - return -1; -} - -/* - * This function reads the firmware status. - */ -static int -mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - u8 fws0, fws1; - - if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0)) - return -1; - - if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1)) - return -1; - - *dat = (u16) ((fws1 << 8) | fws0); - - return 0; -} - -/* - * This function disables the host interrupt. - * - * The host interrupt mask is read, the disable bit is reset and - * written back to the card host interrupt mask register. - */ -static void mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - struct sdio_func *func = card->func; - - sdio_claim_host(func); - mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, 0); - sdio_release_irq(func); - sdio_release_host(func); -} - -/* - * This function reads the interrupt status from card. - */ -static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - u8 sdio_ireg; - unsigned long flags; - - if (mwifiex_read_data_sync(adapter, card->mp_regs, - card->reg->max_mp_regs, - REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) { - mwifiex_dbg(adapter, ERROR, "read mp_regs failed\n"); - return; - } - - sdio_ireg = card->mp_regs[card->reg->host_int_status_reg]; - if (sdio_ireg) { - /* - * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS - * For SDIO new mode CMD port interrupts - * DN_LD_CMD_PORT_HOST_INT_STATUS and/or - * UP_LD_CMD_PORT_HOST_INT_STATUS - * Clear the interrupt status register - */ - mwifiex_dbg(adapter, INTR, - "int: sdio_ireg = %#x\n", sdio_ireg); - spin_lock_irqsave(&adapter->int_lock, flags); - adapter->int_status |= sdio_ireg; - spin_unlock_irqrestore(&adapter->int_lock, flags); - } -} - -/* - * SDIO interrupt handler. - * - * This function reads the interrupt status from firmware and handles - * the interrupt in current thread (ksdioirqd) right away. - */ -static void -mwifiex_sdio_interrupt(struct sdio_func *func) -{ - struct mwifiex_adapter *adapter; - struct sdio_mmc_card *card; - - card = sdio_get_drvdata(func); - if (!card || !card->adapter) { - pr_debug("int: func=%p card=%p adapter=%p\n", - func, card, card ? card->adapter : NULL); - return; - } - adapter = card->adapter; - - if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) - adapter->ps_state = PS_STATE_AWAKE; - - mwifiex_interrupt_status(adapter); - mwifiex_main_process(adapter); -} - -/* - * This function enables the host interrupt. - * - * The host interrupt enable mask is written to the card - * host interrupt mask register. - */ -static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - struct sdio_func *func = card->func; - int ret; - - sdio_claim_host(func); - - /* Request the SDIO IRQ */ - ret = sdio_claim_irq(func, mwifiex_sdio_interrupt); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "claim irq failed: ret=%d\n", ret); - goto out; - } - - /* Simply write the mask to the register */ - ret = mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, - card->reg->host_int_enable); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "enable host interrupt failed\n"); - sdio_release_irq(func); - } - -out: - sdio_release_host(func); - return ret; -} - -/* - * This function sends a data buffer to the card. - */ -static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, - u32 *type, u8 *buffer, - u32 npayload, u32 ioport) -{ - int ret; - u32 nb; - - if (!buffer) { - mwifiex_dbg(adapter, ERROR, - "%s: buffer is NULL\n", __func__); - return -1; - } - - ret = mwifiex_read_data_sync(adapter, buffer, npayload, ioport, 1); - - if (ret) { - mwifiex_dbg(adapter, ERROR, - "%s: read iomem failed: %d\n", __func__, - ret); - return -1; - } - - nb = le16_to_cpu(*(__le16 *) (buffer)); - if (nb > npayload) { - mwifiex_dbg(adapter, ERROR, - "%s: invalid packet, nb=%d npayload=%d\n", - __func__, nb, npayload); - return -1; - } - - *type = le16_to_cpu(*(__le16 *) (buffer + 2)); - - return ret; -} - -/* - * This function downloads the firmware to the card. - * - * Firmware is downloaded to the card in blocks. Every block download - * is tested for CRC errors, and retried a number of times before - * returning failure. - */ -static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, - struct mwifiex_fw_image *fw) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - int ret; - u8 *firmware = fw->fw_buf; - u32 firmware_len = fw->fw_len; - u32 offset = 0; - u8 base0, base1; - u8 *fwbuf; - u16 len = 0; - u32 txlen, tx_blocks = 0, tries; - u32 i = 0; - - if (!firmware_len) { - mwifiex_dbg(adapter, ERROR, - "firmware image not found! Terminating download\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "info: downloading FW image (%d bytes)\n", - firmware_len); - - /* Assume that the allocated buffer is 8-byte aligned */ - fwbuf = kzalloc(MWIFIEX_UPLD_SIZE, GFP_KERNEL); - if (!fwbuf) - return -ENOMEM; - - sdio_claim_host(card->func); - - /* Perform firmware data transfer */ - do { - /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY - bits */ - ret = mwifiex_sdio_poll_card_status(adapter, CARD_IO_READY | - DN_LD_CARD_RDY); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "FW download with helper:\t" - "poll status timeout @ %d\n", offset); - goto done; - } - - /* More data? */ - if (offset >= firmware_len) - break; - - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - ret = mwifiex_read_reg(adapter, reg->base_0_reg, - &base0); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "dev BASE0 register read failed:\t" - "base0=%#04X(%d). Terminating dnld\n", - base0, base0); - goto done; - } - ret = mwifiex_read_reg(adapter, reg->base_1_reg, - &base1); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "dev BASE1 register read failed:\t" - "base1=%#04X(%d). Terminating dnld\n", - base1, base1); - goto done; - } - len = (u16) (((base1 & 0xff) << 8) | (base0 & 0xff)); - - if (len) - break; - - usleep_range(10, 20); - } - - if (!len) { - break; - } else if (len > MWIFIEX_UPLD_SIZE) { - mwifiex_dbg(adapter, ERROR, - "FW dnld failed @ %d, invalid length %d\n", - offset, len); - ret = -1; - goto done; - } - - txlen = len; - - if (len & BIT(0)) { - i++; - if (i > MAX_WRITE_IOMEM_RETRY) { - mwifiex_dbg(adapter, ERROR, - "FW dnld failed @ %d, over max retry\n", - offset); - ret = -1; - goto done; - } - mwifiex_dbg(adapter, ERROR, - "CRC indicated by the helper:\t" - "len = 0x%04X, txlen = %d\n", len, txlen); - len &= ~BIT(0); - /* Setting this to 0 to resend from same offset */ - txlen = 0; - } else { - i = 0; - - /* Set blocksize to transfer - checking for last - block */ - if (firmware_len - offset < txlen) - txlen = firmware_len - offset; - - tx_blocks = (txlen + MWIFIEX_SDIO_BLOCK_SIZE - 1) - / MWIFIEX_SDIO_BLOCK_SIZE; - - /* Copy payload to buffer */ - memmove(fwbuf, &firmware[offset], txlen); - } - - ret = mwifiex_write_data_sync(adapter, fwbuf, tx_blocks * - MWIFIEX_SDIO_BLOCK_SIZE, - adapter->ioport); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "FW download, write iomem (%d) failed @ %d\n", - i, offset); - if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) - mwifiex_dbg(adapter, ERROR, - "write CFG reg failed\n"); - - ret = -1; - goto done; - } - - offset += txlen; - } while (true); - - sdio_release_host(card->func); - - mwifiex_dbg(adapter, MSG, - "info: FW download over, size %d bytes\n", offset); - - ret = 0; -done: - kfree(fwbuf); - return ret; -} - -/* - * This function checks the firmware status in card. - * - * The winner interface is also determined by this function. - */ -static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, - u32 poll_num) -{ - struct sdio_mmc_card *card = adapter->card; - int ret = 0; - u16 firmware_stat; - u32 tries; - u8 winner_status; - - /* Wait for firmware initialization event */ - for (tries = 0; tries < poll_num; tries++) { - ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); - if (ret) - continue; - if (firmware_stat == FIRMWARE_READY_SDIO) { - ret = 0; - break; - } else { - msleep(100); - ret = -1; - } - } - - if (ret) { - if (mwifiex_read_reg - (adapter, card->reg->status_reg_0, &winner_status)) - winner_status = 0; - - if (winner_status) - adapter->winner = 0; - else - adapter->winner = 1; - } - return ret; -} - -/* - * This function decode sdio aggreation pkt. - * - * Based on the the data block size and pkt_len, - * skb data will be decoded to few packets. - */ -static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - u32 total_pkt_len, pkt_len; - struct sk_buff *skb_deaggr; - u32 pkt_type; - u16 blk_size; - u8 blk_num; - u8 *data; - - data = skb->data; - total_pkt_len = skb->len; - - while (total_pkt_len >= (SDIO_HEADER_OFFSET + INTF_HEADER_LEN)) { - if (total_pkt_len < adapter->sdio_rx_block_size) - break; - blk_num = *(data + BLOCK_NUMBER_OFFSET); - blk_size = adapter->sdio_rx_block_size * blk_num; - if (blk_size > total_pkt_len) { - mwifiex_dbg(adapter, ERROR, - "%s: error in blk_size,\t" - "blk_num=%d, blk_size=%d, total_pkt_len=%d\n", - __func__, blk_num, blk_size, total_pkt_len); - break; - } - pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET)); - pkt_type = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET + - 2)); - if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) { - mwifiex_dbg(adapter, ERROR, - "%s: error in pkt_len,\t" - "pkt_len=%d, blk_size=%d\n", - __func__, pkt_len, blk_size); - break; - } - skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len, - GFP_KERNEL | GFP_DMA); - if (!skb_deaggr) - break; - skb_put(skb_deaggr, pkt_len); - memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len); - skb_pull(skb_deaggr, INTF_HEADER_LEN); - - mwifiex_handle_rx_packet(adapter, skb_deaggr); - data += blk_size; - total_pkt_len -= blk_size; - } -} - -/* - * This function decodes a received packet. - * - * Based on the type, the packet is treated as either a data, or - * a command response, or an event, and the correct handler - * function is invoked. - */ -static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, - struct sk_buff *skb, u32 upld_typ) -{ - u8 *cmd_buf; - __le16 *curr_ptr = (__le16 *)skb->data; - u16 pkt_len = le16_to_cpu(*curr_ptr); - struct mwifiex_rxinfo *rx_info; - - if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) { - skb_trim(skb, pkt_len); - skb_pull(skb, INTF_HEADER_LEN); - } - - switch (upld_typ) { - case MWIFIEX_TYPE_AGGR_DATA: - mwifiex_dbg(adapter, INFO, - "info: --- Rx: Aggr Data packet ---\n"); - rx_info = MWIFIEX_SKB_RXCB(skb); - rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA; - if (adapter->rx_work_enabled) { - skb_queue_tail(&adapter->rx_data_q, skb); - atomic_inc(&adapter->rx_pending); - adapter->data_received = true; - } else { - mwifiex_deaggr_sdio_pkt(adapter, skb); - dev_kfree_skb_any(skb); - } - break; - - case MWIFIEX_TYPE_DATA: - mwifiex_dbg(adapter, DATA, - "info: --- Rx: Data packet ---\n"); - if (adapter->rx_work_enabled) { - skb_queue_tail(&adapter->rx_data_q, skb); - adapter->data_received = true; - atomic_inc(&adapter->rx_pending); - } else { - mwifiex_handle_rx_packet(adapter, skb); - } - break; - - case MWIFIEX_TYPE_CMD: - mwifiex_dbg(adapter, CMD, - "info: --- Rx: Cmd Response ---\n"); - /* take care of curr_cmd = NULL case */ - if (!adapter->curr_cmd) { - cmd_buf = adapter->upld_buf; - - if (adapter->ps_state == PS_STATE_SLEEP_CFM) - mwifiex_process_sleep_confirm_resp(adapter, - skb->data, - skb->len); - - memcpy(cmd_buf, skb->data, - min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, - skb->len)); - - dev_kfree_skb_any(skb); - } else { - adapter->cmd_resp_received = true; - adapter->curr_cmd->resp_skb = skb; - } - break; - - case MWIFIEX_TYPE_EVENT: - mwifiex_dbg(adapter, EVENT, - "info: --- Rx: Event ---\n"); - adapter->event_cause = le32_to_cpu(*(__le32 *) skb->data); - - if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) - memcpy(adapter->event_body, - skb->data + MWIFIEX_EVENT_HEADER_LEN, - skb->len); - - /* event cause has been saved to adapter->event_cause */ - adapter->event_received = true; - adapter->event_skb = skb; - - break; - - default: - mwifiex_dbg(adapter, ERROR, - "unknown upload type %#x\n", upld_typ); - dev_kfree_skb_any(skb); - break; - } - - return 0; -} - -/* - * This function transfers received packets from card to driver, performing - * aggregation if required. - * - * For data received on control port, or if aggregation is disabled, the - * received buffers are uploaded as separate packets. However, if aggregation - * is enabled and required, the buffers are copied onto an aggregation buffer, - * provided there is space left, processed and finally uploaded. - */ -static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, - u16 rx_len, u8 port) -{ - struct sdio_mmc_card *card = adapter->card; - s32 f_do_rx_aggr = 0; - s32 f_do_rx_cur = 0; - s32 f_aggr_cur = 0; - s32 f_post_aggr_cur = 0; - struct sk_buff *skb_deaggr; - struct sk_buff *skb = NULL; - u32 pkt_len, pkt_type, mport, pind; - u8 *curr_ptr; - - if ((card->has_control_mask) && (port == CTRL_PORT)) { - /* Read the command Resp without aggr */ - mwifiex_dbg(adapter, CMD, - "info: %s: no aggregation for cmd\t" - "response\n", __func__); - - f_do_rx_cur = 1; - goto rx_curr_single; - } - - if (!card->mpa_rx.enabled) { - mwifiex_dbg(adapter, WARN, - "info: %s: rx aggregation disabled\n", - __func__); - - f_do_rx_cur = 1; - goto rx_curr_single; - } - - if ((!card->has_control_mask && (card->mp_rd_bitmap & - card->reg->data_port_mask)) || - (card->has_control_mask && (card->mp_rd_bitmap & - (~((u32) CTRL_PORT_MASK))))) { - /* Some more data RX pending */ - mwifiex_dbg(adapter, INFO, - "info: %s: not last packet\n", __func__); - - if (MP_RX_AGGR_IN_PROGRESS(card)) { - if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) { - f_aggr_cur = 1; - } else { - /* No room in Aggr buf, do rx aggr now */ - f_do_rx_aggr = 1; - f_post_aggr_cur = 1; - } - } else { - /* Rx aggr not in progress */ - f_aggr_cur = 1; - } - - } else { - /* No more data RX pending */ - mwifiex_dbg(adapter, INFO, - "info: %s: last packet\n", __func__); - - if (MP_RX_AGGR_IN_PROGRESS(card)) { - f_do_rx_aggr = 1; - if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) - f_aggr_cur = 1; - else - /* No room in Aggr buf, do rx aggr now */ - f_do_rx_cur = 1; - } else { - f_do_rx_cur = 1; - } - } - - if (f_aggr_cur) { - mwifiex_dbg(adapter, INFO, - "info: current packet aggregation\n"); - /* Curr pkt can be aggregated */ - mp_rx_aggr_setup(card, rx_len, port); - - if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || - mp_rx_aggr_port_limit_reached(card)) { - mwifiex_dbg(adapter, INFO, - "info: %s: aggregated packet\t" - "limit reached\n", __func__); - /* No more pkts allowed in Aggr buf, rx it */ - f_do_rx_aggr = 1; - } - } - - if (f_do_rx_aggr) { - /* do aggr RX now */ - mwifiex_dbg(adapter, DATA, - "info: do_rx_aggr: num of packets: %d\n", - card->mpa_rx.pkt_cnt); - - if (card->supports_sdio_new_mode) { - int i; - u32 port_count; - - for (i = 0, port_count = 0; i < card->max_ports; i++) - if (card->mpa_rx.ports & BIT(i)) - port_count++; - - /* Reading data from "start_port + 0" to "start_port + - * port_count -1", so decrease the count by 1 - */ - port_count--; - mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | - (port_count << 8)) + card->mpa_rx.start_port; - } else { - mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | - (card->mpa_rx.ports << 4)) + - card->mpa_rx.start_port; - } - - if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, - card->mpa_rx.buf_len, mport, 1)) - goto error; - - curr_ptr = card->mpa_rx.buf; - - for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { - u32 *len_arr = card->mpa_rx.len_arr; - - /* get curr PKT len & type */ - pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]); - pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]); - - /* copy pkt to deaggr buf */ - skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind], - GFP_KERNEL | - GFP_DMA); - if (!skb_deaggr) { - mwifiex_dbg(adapter, ERROR, "skb allocation failure\t" - "drop pkt len=%d type=%d\n", - pkt_len, pkt_type); - curr_ptr += len_arr[pind]; - continue; - } - - skb_put(skb_deaggr, len_arr[pind]); - - if ((pkt_type == MWIFIEX_TYPE_DATA || - (pkt_type == MWIFIEX_TYPE_AGGR_DATA && - adapter->sdio_rx_aggr_enable)) && - (pkt_len <= len_arr[pind])) { - - memcpy(skb_deaggr->data, curr_ptr, pkt_len); - - skb_trim(skb_deaggr, pkt_len); - - /* Process de-aggr packet */ - mwifiex_decode_rx_packet(adapter, skb_deaggr, - pkt_type); - } else { - mwifiex_dbg(adapter, ERROR, - "drop wrong aggr pkt:\t" - "sdio_single_port_rx_aggr=%d\t" - "type=%d len=%d max_len=%d\n", - adapter->sdio_rx_aggr_enable, - pkt_type, pkt_len, len_arr[pind]); - dev_kfree_skb_any(skb_deaggr); - } - curr_ptr += len_arr[pind]; - } - MP_RX_AGGR_BUF_RESET(card); - } - -rx_curr_single: - if (f_do_rx_cur) { - mwifiex_dbg(adapter, INFO, "info: RX: port: %d, rx_len: %d\n", - port, rx_len); - - skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA); - if (!skb) { - mwifiex_dbg(adapter, ERROR, - "single skb allocated fail,\t" - "drop pkt port=%d len=%d\n", port, rx_len); - if (mwifiex_sdio_card_to_host(adapter, &pkt_type, - card->mpa_rx.buf, rx_len, - adapter->ioport + port)) - goto error; - return 0; - } - - skb_put(skb, rx_len); - - if (mwifiex_sdio_card_to_host(adapter, &pkt_type, - skb->data, skb->len, - adapter->ioport + port)) - goto error; - if (!adapter->sdio_rx_aggr_enable && - pkt_type == MWIFIEX_TYPE_AGGR_DATA) { - mwifiex_dbg(adapter, ERROR, "drop wrong pkt type %d\t" - "current SDIO RX Aggr not enabled\n", - pkt_type); - dev_kfree_skb_any(skb); - return 0; - } - - mwifiex_decode_rx_packet(adapter, skb, pkt_type); - } - if (f_post_aggr_cur) { - mwifiex_dbg(adapter, INFO, - "info: current packet aggregation\n"); - /* Curr pkt can be aggregated */ - mp_rx_aggr_setup(card, rx_len, port); - } - - return 0; -error: - if (MP_RX_AGGR_IN_PROGRESS(card)) - MP_RX_AGGR_BUF_RESET(card); - - if (f_do_rx_cur && skb) - /* Single transfer pending. Free curr buff also */ - dev_kfree_skb_any(skb); - - return -1; -} - -/* - * This function checks the current interrupt status. - * - * The following interrupts are checked and handled by this function - - * - Data sent - * - Command sent - * - Packets received - * - * Since the firmware does not generate download ready interrupt if the - * port updated is command port only, command sent interrupt checking - * should be done manually, and for every SDIO interrupt. - * - * In case of Rx packets received, the packets are uploaded from card to - * host and processed accordingly. - */ -static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - int ret = 0; - u8 sdio_ireg; - struct sk_buff *skb; - u8 port = CTRL_PORT; - u32 len_reg_l, len_reg_u; - u32 rx_blocks; - u16 rx_len; - unsigned long flags; - u32 bitmap; - u8 cr; - - spin_lock_irqsave(&adapter->int_lock, flags); - sdio_ireg = adapter->int_status; - adapter->int_status = 0; - spin_unlock_irqrestore(&adapter->int_lock, flags); - - if (!sdio_ireg) - return ret; - - /* Following interrupt is only for SDIO new mode */ - if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent) - adapter->cmd_sent = false; - - /* Following interrupt is only for SDIO new mode */ - if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { - u32 pkt_type; - - /* read the len of control packet */ - rx_len = card->mp_regs[reg->cmd_rd_len_1] << 8; - rx_len |= (u16)card->mp_regs[reg->cmd_rd_len_0]; - rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE); - if (rx_len <= INTF_HEADER_LEN || - (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > - MWIFIEX_RX_DATA_BUF_SIZE) - return -1; - rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); - mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", rx_len); - - skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA); - if (!skb) - return -1; - - skb_put(skb, rx_len); - - if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data, - skb->len, adapter->ioport | - CMD_PORT_SLCT)) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to card_to_host", __func__); - dev_kfree_skb_any(skb); - goto term_cmd; - } - - if ((pkt_type != MWIFIEX_TYPE_CMD) && - (pkt_type != MWIFIEX_TYPE_EVENT)) - mwifiex_dbg(adapter, ERROR, - "%s:Received wrong packet on cmd port", - __func__); - - mwifiex_decode_rx_packet(adapter, skb, pkt_type); - } - - if (sdio_ireg & DN_LD_HOST_INT_STATUS) { - bitmap = (u32) card->mp_regs[reg->wr_bitmap_l]; - bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8; - if (card->supports_sdio_new_mode) { - bitmap |= - ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16; - bitmap |= - ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24; - } - card->mp_wr_bitmap = bitmap; - - mwifiex_dbg(adapter, INTR, - "int: DNLD: wr_bitmap=0x%x\n", - card->mp_wr_bitmap); - if (adapter->data_sent && - (card->mp_wr_bitmap & card->mp_data_port_mask)) { - mwifiex_dbg(adapter, INTR, - "info: <--- Tx DONE Interrupt --->\n"); - adapter->data_sent = false; - } - } - - /* As firmware will not generate download ready interrupt if the port - updated is command port only, cmd_sent should be done for any SDIO - interrupt. */ - if (card->has_control_mask && adapter->cmd_sent) { - /* Check if firmware has attach buffer at command port and - update just that in wr_bit_map. */ - card->mp_wr_bitmap |= - (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK; - if (card->mp_wr_bitmap & CTRL_PORT_MASK) - adapter->cmd_sent = false; - } - - mwifiex_dbg(adapter, INTR, "info: cmd_sent=%d data_sent=%d\n", - adapter->cmd_sent, adapter->data_sent); - if (sdio_ireg & UP_LD_HOST_INT_STATUS) { - bitmap = (u32) card->mp_regs[reg->rd_bitmap_l]; - bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8; - if (card->supports_sdio_new_mode) { - bitmap |= - ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16; - bitmap |= - ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24; - } - card->mp_rd_bitmap = bitmap; - mwifiex_dbg(adapter, INTR, - "int: UPLD: rd_bitmap=0x%x\n", - card->mp_rd_bitmap); - - while (true) { - ret = mwifiex_get_rd_port(adapter, &port); - if (ret) { - mwifiex_dbg(adapter, INFO, - "info: no more rd_port available\n"); - break; - } - len_reg_l = reg->rd_len_p0_l + (port << 1); - len_reg_u = reg->rd_len_p0_u + (port << 1); - rx_len = ((u16) card->mp_regs[len_reg_u]) << 8; - rx_len |= (u16) card->mp_regs[len_reg_l]; - mwifiex_dbg(adapter, INFO, - "info: RX: port=%d rx_len=%u\n", - port, rx_len); - rx_blocks = - (rx_len + MWIFIEX_SDIO_BLOCK_SIZE - - 1) / MWIFIEX_SDIO_BLOCK_SIZE; - if (rx_len <= INTF_HEADER_LEN || - (card->mpa_rx.enabled && - ((rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > - card->mpa_rx.buf_size))) { - mwifiex_dbg(adapter, ERROR, - "invalid rx_len=%d\n", - rx_len); - return -1; - } - - rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); - mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", - rx_len); - - if (mwifiex_sdio_card_to_host_mp_aggr(adapter, rx_len, - port)) { - mwifiex_dbg(adapter, ERROR, - "card_to_host_mpa failed: int status=%#x\n", - sdio_ireg); - goto term_cmd; - } - } - } - - return 0; - -term_cmd: - /* terminate cmd */ - if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) - mwifiex_dbg(adapter, ERROR, "read CFG reg failed\n"); - else - mwifiex_dbg(adapter, INFO, - "info: CFG reg val = %d\n", cr); - - if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04))) - mwifiex_dbg(adapter, ERROR, - "write CFG reg failed\n"); - else - mwifiex_dbg(adapter, INFO, "info: write success\n"); - - if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) - mwifiex_dbg(adapter, ERROR, - "read CFG reg failed\n"); - else - mwifiex_dbg(adapter, INFO, - "info: CFG reg val =%x\n", cr); - - return -1; -} - -/* - * This function aggregates transmission buffers in driver and downloads - * the aggregated packet to card. - * - * The individual packets are aggregated by copying into an aggregation - * buffer and then downloaded to the card. Previous unsent packets in the - * aggregation buffer are pre-copied first before new packets are added. - * Aggregation is done till there is space left in the aggregation buffer, - * or till new packets are available. - * - * The function will only download the packet to the card when aggregation - * stops, otherwise it will just aggregate the packet in aggregation buffer - * and return. - */ -static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, - u8 *payload, u32 pkt_len, u32 port, - u32 next_pkt_len) -{ - struct sdio_mmc_card *card = adapter->card; - int ret = 0; - s32 f_send_aggr_buf = 0; - s32 f_send_cur_buf = 0; - s32 f_precopy_cur_buf = 0; - s32 f_postcopy_cur_buf = 0; - u32 mport; - - if (!card->mpa_tx.enabled || - (card->has_control_mask && (port == CTRL_PORT)) || - (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) { - mwifiex_dbg(adapter, WARN, - "info: %s: tx aggregation disabled\n", - __func__); - - f_send_cur_buf = 1; - goto tx_curr_single; - } - - if (next_pkt_len) { - /* More pkt in TX queue */ - mwifiex_dbg(adapter, INFO, - "info: %s: more packets in queue.\n", - __func__); - - if (MP_TX_AGGR_IN_PROGRESS(card)) { - if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { - f_precopy_cur_buf = 1; - - if (!(card->mp_wr_bitmap & - (1 << card->curr_wr_port)) || - !MP_TX_AGGR_BUF_HAS_ROOM( - card, pkt_len + next_pkt_len)) - f_send_aggr_buf = 1; - } else { - /* No room in Aggr buf, send it */ - f_send_aggr_buf = 1; - - if (!(card->mp_wr_bitmap & - (1 << card->curr_wr_port))) - f_send_cur_buf = 1; - else - f_postcopy_cur_buf = 1; - } - } else { - if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) && - (card->mp_wr_bitmap & (1 << card->curr_wr_port))) - f_precopy_cur_buf = 1; - else - f_send_cur_buf = 1; - } - } else { - /* Last pkt in TX queue */ - mwifiex_dbg(adapter, INFO, - "info: %s: Last packet in Tx Queue.\n", - __func__); - - if (MP_TX_AGGR_IN_PROGRESS(card)) { - /* some packs in Aggr buf already */ - f_send_aggr_buf = 1; - - if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) - f_precopy_cur_buf = 1; - else - /* No room in Aggr buf, send it */ - f_send_cur_buf = 1; - } else { - f_send_cur_buf = 1; - } - } - - if (f_precopy_cur_buf) { - mwifiex_dbg(adapter, DATA, - "data: %s: precopy current buffer\n", - __func__); - MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); - - if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || - mp_tx_aggr_port_limit_reached(card)) - /* No more pkts allowed in Aggr buf, send it */ - f_send_aggr_buf = 1; - } - - if (f_send_aggr_buf) { - mwifiex_dbg(adapter, DATA, - "data: %s: send aggr buffer: %d %d\n", - __func__, card->mpa_tx.start_port, - card->mpa_tx.ports); - if (card->supports_sdio_new_mode) { - u32 port_count; - int i; - - for (i = 0, port_count = 0; i < card->max_ports; i++) - if (card->mpa_tx.ports & BIT(i)) - port_count++; - - /* Writing data from "start_port + 0" to "start_port + - * port_count -1", so decrease the count by 1 - */ - port_count--; - mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | - (port_count << 8)) + card->mpa_tx.start_port; - } else { - mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | - (card->mpa_tx.ports << 4)) + - card->mpa_tx.start_port; - } - - ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, - card->mpa_tx.buf_len, mport); - - MP_TX_AGGR_BUF_RESET(card); - } - -tx_curr_single: - if (f_send_cur_buf) { - mwifiex_dbg(adapter, DATA, - "data: %s: send current buffer %d\n", - __func__, port); - ret = mwifiex_write_data_to_card(adapter, payload, pkt_len, - adapter->ioport + port); - } - - if (f_postcopy_cur_buf) { - mwifiex_dbg(adapter, DATA, - "data: %s: postcopy current buffer\n", - __func__); - MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); - } - - return ret; -} - -/* - * This function downloads data from driver to card. - * - * Both commands and data packets are transferred to the card by this - * function. - * - * This function adds the SDIO specific header to the front of the buffer - * before transferring. The header contains the length of the packet and - * the type. The firmware handles the packets based upon this set type. - */ -static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, - u8 type, struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - struct sdio_mmc_card *card = adapter->card; - int ret; - u32 buf_block_len; - u32 blk_size; - u32 port = CTRL_PORT; - u8 *payload = (u8 *)skb->data; - u32 pkt_len = skb->len; - - /* Allocate buffer and copy payload */ - blk_size = MWIFIEX_SDIO_BLOCK_SIZE; - buf_block_len = (pkt_len + blk_size - 1) / blk_size; - *(__le16 *)&payload[0] = cpu_to_le16((u16)pkt_len); - *(__le16 *)&payload[2] = cpu_to_le16(type); - - /* - * This is SDIO specific header - * u16 length, - * u16 type (MWIFIEX_TYPE_DATA = 0, MWIFIEX_TYPE_CMD = 1, - * MWIFIEX_TYPE_EVENT = 3) - */ - if (type == MWIFIEX_TYPE_DATA) { - ret = mwifiex_get_wr_port_data(adapter, &port); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "%s: no wr_port available\n", - __func__); - return ret; - } - } else { - adapter->cmd_sent = true; - /* Type must be MWIFIEX_TYPE_CMD */ - - if (pkt_len <= INTF_HEADER_LEN || - pkt_len > MWIFIEX_UPLD_SIZE) - mwifiex_dbg(adapter, ERROR, - "%s: payload=%p, nb=%d\n", - __func__, payload, pkt_len); - - if (card->supports_sdio_new_mode) - port = CMD_PORT_SLCT; - } - - /* Transfer data to card */ - pkt_len = buf_block_len * blk_size; - - if (tx_param) - ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, - port, tx_param->next_pkt_len - ); - else - ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, - port, 0); - - if (ret) { - if (type == MWIFIEX_TYPE_CMD) - adapter->cmd_sent = false; - if (type == MWIFIEX_TYPE_DATA) { - adapter->data_sent = false; - /* restore curr_wr_port in error cases */ - card->curr_wr_port = port; - card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port); - } - } else { - if (type == MWIFIEX_TYPE_DATA) { - if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) - adapter->data_sent = true; - else - adapter->data_sent = false; - } - } - - return ret; -} - -/* - * This function allocates the MPA Tx and Rx buffers. - */ -static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter, - u32 mpa_tx_buf_size, u32 mpa_rx_buf_size) -{ - struct sdio_mmc_card *card = adapter->card; - u32 rx_buf_size; - int ret = 0; - - card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL); - if (!card->mpa_tx.buf) { - ret = -1; - goto error; - } - - card->mpa_tx.buf_size = mpa_tx_buf_size; - - rx_buf_size = max_t(u32, mpa_rx_buf_size, - (u32)SDIO_MAX_AGGR_BUF_SIZE); - card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL); - if (!card->mpa_rx.buf) { - ret = -1; - goto error; - } - - card->mpa_rx.buf_size = rx_buf_size; - -error: - if (ret) { - kfree(card->mpa_tx.buf); - kfree(card->mpa_rx.buf); - card->mpa_tx.buf_size = 0; - card->mpa_rx.buf_size = 0; - } - - return ret; -} - -/* - * This function unregisters the SDIO device. - * - * The SDIO IRQ is released, the function is disabled and driver - * data is set to null. - */ -static void -mwifiex_unregister_dev(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - - if (adapter->card) { - sdio_claim_host(card->func); - sdio_disable_func(card->func); - sdio_release_host(card->func); - } -} - -/* - * This function registers the SDIO device. - * - * SDIO IRQ is claimed, block size is set and driver data is initialized. - */ -static int mwifiex_register_dev(struct mwifiex_adapter *adapter) -{ - int ret; - struct sdio_mmc_card *card = adapter->card; - struct sdio_func *func = card->func; - - /* save adapter pointer in card */ - card->adapter = adapter; - adapter->tx_buf_size = card->tx_buf_size; - - sdio_claim_host(func); - - /* Set block size */ - ret = sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE); - sdio_release_host(func); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "cannot set SDIO block size\n"); - return ret; - } - - - adapter->dev = &func->dev; - - strcpy(adapter->fw_name, card->firmware); - if (card->fw_dump_enh) { - adapter->mem_type_mapping_tbl = generic_mem_type_map; - adapter->num_mem_types = 1; - } else { - adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; - adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); - } - - return 0; -} - -/* - * This function initializes the SDIO driver. - * - * The following initializations steps are followed - - * - Read the Host interrupt status register to acknowledge - * the first interrupt got from bootloader - * - Disable host interrupt mask register - * - Get SDIO port - * - Initialize SDIO variables in card - * - Allocate MP registers - * - Allocate MPA Tx and Rx buffers - */ -static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - int ret; - u8 sdio_ireg; - - sdio_set_drvdata(card->func, card); - - /* - * Read the host_int_status_reg for ACK the first interrupt got - * from the bootloader. If we don't do this we get a interrupt - * as soon as we register the irq. - */ - mwifiex_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg); - - /* Get SDIO ioport */ - mwifiex_init_sdio_ioport(adapter); - - /* Initialize SDIO variables in card */ - card->mp_rd_bitmap = 0; - card->mp_wr_bitmap = 0; - card->curr_rd_port = reg->start_rd_port; - card->curr_wr_port = reg->start_wr_port; - - card->mp_data_port_mask = reg->data_port_mask; - - card->mpa_tx.buf_len = 0; - card->mpa_tx.pkt_cnt = 0; - card->mpa_tx.start_port = 0; - - card->mpa_tx.enabled = 1; - card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit; - - card->mpa_rx.buf_len = 0; - card->mpa_rx.pkt_cnt = 0; - card->mpa_rx.start_port = 0; - - card->mpa_rx.enabled = 1; - card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit; - - /* Allocate buffers for SDIO MP-A */ - card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL); - if (!card->mp_regs) - return -ENOMEM; - - /* Allocate skb pointer buffers */ - card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) * - card->mp_agg_pkt_limit, GFP_KERNEL); - card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) * - card->mp_agg_pkt_limit, GFP_KERNEL); - ret = mwifiex_alloc_sdio_mpa_buffers(adapter, - card->mp_tx_agg_buf_size, - card->mp_rx_agg_buf_size); - - /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */ - if (ret && (card->mp_tx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX || - card->mp_rx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX)) { - /* Disable rx single port aggregation */ - adapter->host_disable_sdio_rx_aggr = true; - - ret = mwifiex_alloc_sdio_mpa_buffers - (adapter, MWIFIEX_MP_AGGR_BUF_SIZE_32K, - MWIFIEX_MP_AGGR_BUF_SIZE_32K); - if (ret) { - /* Disable multi port aggregation */ - card->mpa_tx.enabled = 0; - card->mpa_rx.enabled = 0; - } - } - - adapter->auto_tdls = card->can_auto_tdls; - adapter->ext_scan = card->can_ext_scan; - return 0; -} - -/* - * This function resets the MPA Tx and Rx buffers. - */ -static void mwifiex_cleanup_mpa_buf(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - - MP_TX_AGGR_BUF_RESET(card); - MP_RX_AGGR_BUF_RESET(card); -} - -/* - * This function cleans up the allocated card buffers. - * - * The following are freed by this function - - * - MP registers - * - MPA Tx buffer - * - MPA Rx buffer - */ -static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - - kfree(card->mp_regs); - kfree(card->mpa_rx.skb_arr); - kfree(card->mpa_rx.len_arr); - kfree(card->mpa_tx.buf); - kfree(card->mpa_rx.buf); - sdio_set_drvdata(card->func, NULL); - kfree(card); -} - -/* - * This function updates the MP end port in card. - */ -static void -mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - int i; - - card->mp_end_port = port; - - card->mp_data_port_mask = reg->data_port_mask; - - if (reg->start_wr_port) { - for (i = 1; i <= card->max_ports - card->mp_end_port; i++) - card->mp_data_port_mask &= - ~(1 << (card->max_ports - i)); - } - - card->curr_wr_port = reg->start_wr_port; - - mwifiex_dbg(adapter, CMD, - "cmd: mp_end_port %d, data port mask 0x%x\n", - port, card->mp_data_port_mask); -} - -static void mwifiex_recreate_adapter(struct sdio_mmc_card *card) -{ - struct sdio_func *func = card->func; - const struct sdio_device_id *device_id = card->device_id; - - /* TODO mmc_hw_reset does not require destroying and re-probing the - * whole adapter. Hence there was no need to for this rube-goldberg - * design to reload the fw from an external workqueue. If we don't - * destroy the adapter we could reload the fw from - * mwifiex_main_work_queue directly. - * The real difficulty with fw reset is to restore all the user - * settings applied through ioctl. By destroying and recreating the - * adapter, we take the easy way out, since we rely on user space to - * restore them. We assume that user space will treat the new - * incarnation of the adapter(interfaces) as if they had been just - * discovered and initializes them from scratch. - */ - - mwifiex_sdio_remove(func); - - /* power cycle the adapter */ - sdio_claim_host(func); - mmc_hw_reset(func->card->host); - sdio_release_host(func); - - mwifiex_sdio_probe(func, device_id); -} - -static struct mwifiex_adapter *save_adapter; -static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - - /* TODO card pointer is unprotected. If the adapter is removed - * physically, sdio core might trigger mwifiex_sdio_remove, before this - * workqueue is run, which will destroy the adapter struct. When this - * workqueue eventually exceutes it will dereference an invalid adapter - * pointer - */ - mwifiex_recreate_adapter(card); -} - -/* This function read/write firmware */ -static enum -rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter, - u8 doneflag) -{ - struct sdio_mmc_card *card = adapter->card; - int ret, tries; - u8 ctrl_data = 0; - - sdio_writeb(card->func, card->reg->fw_dump_host_ready, - card->reg->fw_dump_ctrl, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO Write ERR\n"); - return RDWR_STATUS_FAILURE; - } - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, - &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); - return RDWR_STATUS_FAILURE; - } - if (ctrl_data == FW_DUMP_DONE) - break; - if (doneflag && ctrl_data == doneflag) - return RDWR_STATUS_DONE; - if (ctrl_data != card->reg->fw_dump_host_ready) { - mwifiex_dbg(adapter, WARN, - "The ctrl reg was changed, re-try again\n"); - sdio_writeb(card->func, card->reg->fw_dump_host_ready, - card->reg->fw_dump_ctrl, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); - return RDWR_STATUS_FAILURE; - } - } - usleep_range(100, 200); - } - if (ctrl_data == card->reg->fw_dump_host_ready) { - mwifiex_dbg(adapter, ERROR, - "Fail to pull ctrl_data\n"); - return RDWR_STATUS_FAILURE; - } - - return RDWR_STATUS_SUCCESS; -} - -/* This function dump firmware memory to file */ -static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - int ret = 0; - unsigned int reg, reg_start, reg_end; - u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; - enum rdwr_status stat; - u32 memory_size; - - if (!card->can_dump_fw) - return; - - for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { - struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - } - - mwifiex_pm_wakeup_card(adapter); - sdio_claim_host(card->func); - - mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); - - stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); - if (stat == RDWR_STATUS_FAILURE) - goto done; - - reg = card->reg->fw_dump_start; - /* Read the number of the memories which will dump */ - dump_num = sdio_readb(card->func, reg, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO read memory length err\n"); - goto done; - } - - /* Read the length of every memory which will dump */ - for (idx = 0; idx < dump_num; idx++) { - struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; - - stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); - if (stat == RDWR_STATUS_FAILURE) - goto done; - - memory_size = 0; - reg = card->reg->fw_dump_start; - for (i = 0; i < 4; i++) { - read_reg = sdio_readb(card->func, reg, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); - goto done; - } - memory_size |= (read_reg << i*8); - reg++; - } - - if (memory_size == 0) { - mwifiex_dbg(adapter, DUMP, "Firmware dump Finished!\n"); - ret = mwifiex_write_reg(adapter, - card->reg->fw_dump_ctrl, - FW_DUMP_READ_DONE); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); - return; - } - break; - } - - mwifiex_dbg(adapter, DUMP, - "%s_SIZE=0x%x\n", entry->mem_name, memory_size); - entry->mem_ptr = vmalloc(memory_size + 1); - entry->mem_size = memory_size; - if (!entry->mem_ptr) { - mwifiex_dbg(adapter, ERROR, "Vmalloc %s failed\n", - entry->mem_name); - goto done; - } - dbg_ptr = entry->mem_ptr; - end_ptr = dbg_ptr + memory_size; - - doneflag = entry->done_flag; - mwifiex_dbg(adapter, DUMP, - "Start %s output, please wait...\n", - entry->mem_name); - - do { - stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); - if (stat == RDWR_STATUS_FAILURE) - goto done; - - reg_start = card->reg->fw_dump_start; - reg_end = card->reg->fw_dump_end; - for (reg = reg_start; reg <= reg_end; reg++) { - *dbg_ptr = sdio_readb(card->func, reg, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "SDIO read err\n"); - goto done; - } - if (dbg_ptr < end_ptr) - dbg_ptr++; - else - mwifiex_dbg(adapter, ERROR, - "Allocated buf not enough\n"); - } - - if (stat != RDWR_STATUS_DONE) - continue; - - mwifiex_dbg(adapter, DUMP, "%s done: size=0x%tx\n", - entry->mem_name, dbg_ptr - entry->mem_ptr); - break; - } while (1); - } - mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); - -done: - sdio_release_host(card->func); -} - -static void mwifiex_sdio_generic_fw_dump(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - struct memory_type_mapping *entry = &generic_mem_type_map[0]; - unsigned int reg, reg_start, reg_end; - u8 start_flag = 0, done_flag = 0; - u8 *dbg_ptr, *end_ptr; - enum rdwr_status stat; - int ret = -1, tries; - - if (!card->fw_dump_enh) - return; - - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - - mwifiex_pm_wakeup_card(adapter); - sdio_claim_host(card->func); - - mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); - - stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); - if (stat == RDWR_STATUS_FAILURE) - goto done; - - reg_start = card->reg->fw_dump_start; - reg_end = card->reg->fw_dump_end; - for (reg = reg_start; reg <= reg_end; reg++) { - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - start_flag = sdio_readb(card->func, reg, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "SDIO read err\n"); - goto done; - } - if (start_flag == 0) - break; - if (tries == MAX_POLL_TRIES) { - mwifiex_dbg(adapter, ERROR, - "FW not ready to dump\n"); - ret = -1; - goto done; - } - } - usleep_range(100, 200); - } - - entry->mem_ptr = vmalloc(0xf0000 + 1); - if (!entry->mem_ptr) { - ret = -1; - goto done; - } - dbg_ptr = entry->mem_ptr; - entry->mem_size = 0xf0000; - end_ptr = dbg_ptr + entry->mem_size; - - done_flag = entry->done_flag; - mwifiex_dbg(adapter, DUMP, - "Start %s output, please wait...\n", entry->mem_name); - - while (true) { - stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); - if (stat == RDWR_STATUS_FAILURE) - goto done; - for (reg = reg_start; reg <= reg_end; reg++) { - *dbg_ptr = sdio_readb(card->func, reg, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "SDIO read err\n"); - goto done; - } - dbg_ptr++; - if (dbg_ptr >= end_ptr) { - u8 *tmp_ptr; - - tmp_ptr = vmalloc(entry->mem_size + 0x4000 + 1); - if (!tmp_ptr) - goto done; - - memcpy(tmp_ptr, entry->mem_ptr, - entry->mem_size); - vfree(entry->mem_ptr); - entry->mem_ptr = tmp_ptr; - tmp_ptr = NULL; - dbg_ptr = entry->mem_ptr + entry->mem_size; - entry->mem_size += 0x4000; - end_ptr = entry->mem_ptr + entry->mem_size; - } - } - if (stat == RDWR_STATUS_DONE) { - entry->mem_size = dbg_ptr - entry->mem_ptr; - mwifiex_dbg(adapter, DUMP, "dump %s done size=0x%x\n", - entry->mem_name, entry->mem_size); - ret = 0; - break; - } - } - mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); - -done: - if (ret) { - mwifiex_dbg(adapter, ERROR, "firmware dump failed\n"); - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - } - sdio_release_host(card->func); -} - -static void mwifiex_sdio_device_dump_work(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - - mwifiex_drv_info_dump(adapter); - if (card->fw_dump_enh) - mwifiex_sdio_generic_fw_dump(adapter); - else - mwifiex_sdio_fw_dump(adapter); - mwifiex_upload_device_dump(adapter); -} - -static void mwifiex_sdio_work(struct work_struct *work) -{ - if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, - &iface_work_flags)) - mwifiex_sdio_device_dump_work(save_adapter); - if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, - &iface_work_flags)) - mwifiex_sdio_card_reset_work(save_adapter); -} - -static DECLARE_WORK(sdio_work, mwifiex_sdio_work); -/* This function resets the card */ -static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter) -{ - save_adapter = adapter; - if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags)) - return; - - set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags); - - schedule_work(&sdio_work); -} - -/* This function dumps FW information */ -static void mwifiex_sdio_device_dump(struct mwifiex_adapter *adapter) -{ - save_adapter = adapter; - if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags)) - return; - - set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags); - schedule_work(&sdio_work); -} - -/* Function to dump SDIO function registers and SDIO scratch registers in case - * of FW crash - */ -static int -mwifiex_sdio_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf) -{ - char *p = drv_buf; - struct sdio_mmc_card *cardp = adapter->card; - int ret = 0; - u8 count, func, data, index = 0, size = 0; - u8 reg, reg_start, reg_end; - char buf[256], *ptr; - - if (!p) - return 0; - - mwifiex_dbg(adapter, MSG, "SDIO register dump start\n"); - - mwifiex_pm_wakeup_card(adapter); - - sdio_claim_host(cardp->func); - - for (count = 0; count < 5; count++) { - memset(buf, 0, sizeof(buf)); - ptr = buf; - - switch (count) { - case 0: - /* Read the registers of SDIO function0 */ - func = count; - reg_start = 0; - reg_end = 9; - break; - case 1: - /* Read the registers of SDIO function1 */ - func = count; - reg_start = cardp->reg->func1_dump_reg_start; - reg_end = cardp->reg->func1_dump_reg_end; - break; - case 2: - index = 0; - func = 1; - reg_start = cardp->reg->func1_spec_reg_table[index++]; - size = cardp->reg->func1_spec_reg_num; - reg_end = cardp->reg->func1_spec_reg_table[size-1]; - break; - default: - /* Read the scratch registers of SDIO function1 */ - if (count == 4) - mdelay(100); - func = 1; - reg_start = cardp->reg->func1_scratch_reg; - reg_end = reg_start + MWIFIEX_SDIO_SCRATCH_SIZE; - } - - if (count != 2) - ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", - func, reg_start, reg_end); - else - ptr += sprintf(ptr, "SDIO Func%d: ", func); - - for (reg = reg_start; reg <= reg_end;) { - if (func == 0) - data = sdio_f0_readb(cardp->func, reg, &ret); - else - data = sdio_readb(cardp->func, reg, &ret); - - if (count == 2) - ptr += sprintf(ptr, "(%#x) ", reg); - if (!ret) { - ptr += sprintf(ptr, "%02x ", data); - } else { - ptr += sprintf(ptr, "ERR"); - break; - } - - if (count == 2 && reg < reg_end) - reg = cardp->reg->func1_spec_reg_table[index++]; - else - reg++; - } - - mwifiex_dbg(adapter, MSG, "%s\n", buf); - p += sprintf(p, "%s\n", buf); - } - - sdio_release_host(cardp->func); - - mwifiex_dbg(adapter, MSG, "SDIO register dump end\n"); - - return p - drv_buf; -} - -static struct mwifiex_if_ops sdio_ops = { - .init_if = mwifiex_init_sdio, - .cleanup_if = mwifiex_cleanup_sdio, - .check_fw_status = mwifiex_check_fw_status, - .prog_fw = mwifiex_prog_fw_w_helper, - .register_dev = mwifiex_register_dev, - .unregister_dev = mwifiex_unregister_dev, - .enable_int = mwifiex_sdio_enable_host_int, - .disable_int = mwifiex_sdio_disable_host_int, - .process_int_status = mwifiex_process_int_status, - .host_to_card = mwifiex_sdio_host_to_card, - .wakeup = mwifiex_pm_wakeup_card, - .wakeup_complete = mwifiex_pm_wakeup_card_complete, - - /* SDIO specific */ - .update_mp_end_port = mwifiex_update_mp_end_port, - .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, - .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, - .event_complete = mwifiex_sdio_event_complete, - .card_reset = mwifiex_sdio_card_reset, - .reg_dump = mwifiex_sdio_reg_dump, - .device_dump = mwifiex_sdio_device_dump, - .deaggr_pkt = mwifiex_deaggr_sdio_pkt, -}; - -/* - * This function initializes the SDIO driver. - * - * This initiates the semaphore and registers the device with - * SDIO bus. - */ -static int -mwifiex_sdio_init_module(void) -{ - sema_init(&add_remove_card_sem, 1); - - /* Clear the flag in case user removes the card. */ - user_rmmod = 0; - - return sdio_register_driver(&mwifiex_sdio); -} - -/* - * This function cleans up the SDIO driver. - * - * The following major steps are followed for cleanup - - * - Resume the device if its suspended - * - Disconnect the device if connected - * - Shutdown the firmware - * - Unregister the device from SDIO bus. - */ -static void -mwifiex_sdio_cleanup_module(void) -{ - if (!down_interruptible(&add_remove_card_sem)) - up(&add_remove_card_sem); - - /* Set the flag as user is removing this module. */ - user_rmmod = 1; - cancel_work_sync(&sdio_work); - - sdio_unregister_driver(&mwifiex_sdio); -} - -module_init(mwifiex_sdio_init_module); -module_exit(mwifiex_sdio_cleanup_module); - -MODULE_AUTHOR("Marvell International Ltd."); -MODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION); -MODULE_VERSION(SDIO_VERSION); -MODULE_LICENSE("GPL v2"); -MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME); -MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME); -MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME); -MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME); -MODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME); -MODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h deleted file mode 100644 index b9fbc5cf6262..000000000000 --- a/drivers/net/wireless/mwifiex/sdio.h +++ /dev/null @@ -1,672 +0,0 @@ -/* - * Marvell Wireless LAN device driver: SDIO specific definitions - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_SDIO_H -#define _MWIFIEX_SDIO_H - - -#include -#include -#include -#include -#include - -#include "main.h" - -#define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin" -#define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin" -#define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin" -#define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin" -#define SD8887_DEFAULT_FW_NAME "mrvl/sd8887_uapsta.bin" -#define SD8801_DEFAULT_FW_NAME "mrvl/sd8801_uapsta.bin" -#define SD8997_DEFAULT_FW_NAME "mrvl/sd8997_uapsta.bin" - -#define BLOCK_MODE 1 -#define BYTE_MODE 0 - -#define REG_PORT 0 - -#define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff - -#define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000 - -#define MWIFIEX_MAX_FUNC2_REG_NUM 13 -#define MWIFIEX_SDIO_SCRATCH_SIZE 10 - -#define SDIO_MPA_ADDR_BASE 0x1000 -#define CTRL_PORT 0 -#define CTRL_PORT_MASK 0x0001 - -#define CMD_PORT_UPLD_INT_MASK (0x1U<<6) -#define CMD_PORT_DNLD_INT_MASK (0x1U<<7) -#define HOST_TERM_CMD53 (0x1U << 2) -#define REG_PORT 0 -#define MEM_PORT 0x10000 - -#define CMD53_NEW_MODE (0x1U << 0) -#define CMD_PORT_RD_LEN_EN (0x1U << 2) -#define CMD_PORT_AUTO_EN (0x1U << 0) -#define CMD_PORT_SLCT 0x8000 -#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U) -#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U) - -#define MWIFIEX_MP_AGGR_BUF_SIZE_16K (16384) -#define MWIFIEX_MP_AGGR_BUF_SIZE_32K (32768) -/* we leave one block of 256 bytes for DMA alignment*/ -#define MWIFIEX_MP_AGGR_BUF_SIZE_MAX (65280) - -/* Misc. Config Register : Auto Re-enable interrupts */ -#define AUTO_RE_ENABLE_INT BIT(4) - -/* Host Control Registers : Configuration */ -#define CONFIGURATION_REG 0x00 -/* Host Control Registers : Host power up */ -#define HOST_POWER_UP (0x1U << 1) - -/* Host Control Registers : Upload host interrupt mask */ -#define UP_LD_HOST_INT_MASK (0x1U) -/* Host Control Registers : Download host interrupt mask */ -#define DN_LD_HOST_INT_MASK (0x2U) - -/* Host Control Registers : Upload host interrupt status */ -#define UP_LD_HOST_INT_STATUS (0x1U) -/* Host Control Registers : Download host interrupt status */ -#define DN_LD_HOST_INT_STATUS (0x2U) - -/* Host Control Registers : Host interrupt status */ -#define CARD_INT_STATUS_REG 0x28 - -/* Card Control Registers : Card I/O ready */ -#define CARD_IO_READY (0x1U << 3) -/* Card Control Registers : Download card ready */ -#define DN_LD_CARD_RDY (0x1U << 0) - -/* Max retry number of CMD53 write */ -#define MAX_WRITE_IOMEM_RETRY 2 - -/* SDIO Tx aggregation in progress ? */ -#define MP_TX_AGGR_IN_PROGRESS(a) (a->mpa_tx.pkt_cnt > 0) - -/* SDIO Tx aggregation buffer room for next packet ? */ -#define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ((a->mpa_tx.buf_len+len) \ - <= a->mpa_tx.buf_size) - -/* Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */ -#define MP_TX_AGGR_BUF_PUT(a, payload, pkt_len, port) do { \ - memmove(&a->mpa_tx.buf[a->mpa_tx.buf_len], \ - payload, pkt_len); \ - a->mpa_tx.buf_len += pkt_len; \ - if (!a->mpa_tx.pkt_cnt) \ - a->mpa_tx.start_port = port; \ - if (a->mpa_tx.start_port <= port) \ - a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \ - else \ - a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+ \ - (a->max_ports - \ - a->mp_end_port))); \ - a->mpa_tx.pkt_cnt++; \ -} while (0) - -/* SDIO Tx aggregation limit ? */ -#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \ - (a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit) - -/* Reset SDIO Tx aggregation buffer parameters */ -#define MP_TX_AGGR_BUF_RESET(a) do { \ - a->mpa_tx.pkt_cnt = 0; \ - a->mpa_tx.buf_len = 0; \ - a->mpa_tx.ports = 0; \ - a->mpa_tx.start_port = 0; \ -} while (0) - -/* SDIO Rx aggregation limit ? */ -#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \ - (a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit) - -/* SDIO Rx aggregation in progress ? */ -#define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0) - -/* SDIO Rx aggregation buffer room for next packet ? */ -#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \ - ((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size) - -/* Reset SDIO Rx aggregation buffer parameters */ -#define MP_RX_AGGR_BUF_RESET(a) do { \ - a->mpa_rx.pkt_cnt = 0; \ - a->mpa_rx.buf_len = 0; \ - a->mpa_rx.ports = 0; \ - a->mpa_rx.start_port = 0; \ -} while (0) - -/* data structure for SDIO MPA TX */ -struct mwifiex_sdio_mpa_tx { - /* multiport tx aggregation buffer pointer */ - u8 *buf; - u32 buf_len; - u32 pkt_cnt; - u32 ports; - u16 start_port; - u8 enabled; - u32 buf_size; - u32 pkt_aggr_limit; -}; - -struct mwifiex_sdio_mpa_rx { - u8 *buf; - u32 buf_len; - u32 pkt_cnt; - u32 ports; - u16 start_port; - - struct sk_buff **skb_arr; - u32 *len_arr; - - u8 enabled; - u32 buf_size; - u32 pkt_aggr_limit; -}; - -int mwifiex_bus_register(void); -void mwifiex_bus_unregister(void); - -struct mwifiex_sdio_card_reg { - u8 start_rd_port; - u8 start_wr_port; - u8 base_0_reg; - u8 base_1_reg; - u8 poll_reg; - u8 host_int_enable; - u8 host_int_rsr_reg; - u8 host_int_status_reg; - u8 host_int_mask_reg; - u8 status_reg_0; - u8 status_reg_1; - u8 sdio_int_mask; - u32 data_port_mask; - u8 io_port_0_reg; - u8 io_port_1_reg; - u8 io_port_2_reg; - u8 max_mp_regs; - u8 rd_bitmap_l; - u8 rd_bitmap_u; - u8 rd_bitmap_1l; - u8 rd_bitmap_1u; - u8 wr_bitmap_l; - u8 wr_bitmap_u; - u8 wr_bitmap_1l; - u8 wr_bitmap_1u; - u8 rd_len_p0_l; - u8 rd_len_p0_u; - u8 card_misc_cfg_reg; - u8 card_cfg_2_1_reg; - u8 cmd_rd_len_0; - u8 cmd_rd_len_1; - u8 cmd_rd_len_2; - u8 cmd_rd_len_3; - u8 cmd_cfg_0; - u8 cmd_cfg_1; - u8 cmd_cfg_2; - u8 cmd_cfg_3; - u8 fw_dump_host_ready; - u8 fw_dump_ctrl; - u8 fw_dump_start; - u8 fw_dump_end; - u8 func1_dump_reg_start; - u8 func1_dump_reg_end; - u8 func1_scratch_reg; - u8 func1_spec_reg_num; - u8 func1_spec_reg_table[MWIFIEX_MAX_FUNC2_REG_NUM]; -}; - -struct sdio_mmc_card { - struct sdio_func *func; - struct mwifiex_adapter *adapter; - - const char *firmware; - const struct mwifiex_sdio_card_reg *reg; - u8 max_ports; - u8 mp_agg_pkt_limit; - u16 tx_buf_size; - u32 mp_tx_agg_buf_size; - u32 mp_rx_agg_buf_size; - - u32 mp_rd_bitmap; - u32 mp_wr_bitmap; - - u16 mp_end_port; - u32 mp_data_port_mask; - - u8 curr_rd_port; - u8 curr_wr_port; - - u8 *mp_regs; - bool supports_sdio_new_mode; - bool has_control_mask; - bool can_dump_fw; - bool fw_dump_enh; - bool can_auto_tdls; - bool can_ext_scan; - - struct mwifiex_sdio_mpa_tx mpa_tx; - struct mwifiex_sdio_mpa_rx mpa_rx; - - /* needed for card reset */ - const struct sdio_device_id *device_id; -}; - -struct mwifiex_sdio_device { - const char *firmware; - const struct mwifiex_sdio_card_reg *reg; - u8 max_ports; - u8 mp_agg_pkt_limit; - u16 tx_buf_size; - u32 mp_tx_agg_buf_size; - u32 mp_rx_agg_buf_size; - bool supports_sdio_new_mode; - bool has_control_mask; - bool can_dump_fw; - bool fw_dump_enh; - bool can_auto_tdls; - bool can_ext_scan; -}; - -static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { - .start_rd_port = 1, - .start_wr_port = 1, - .base_0_reg = 0x0040, - .base_1_reg = 0x0041, - .poll_reg = 0x30, - .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK, - .host_int_rsr_reg = 0x1, - .host_int_mask_reg = 0x02, - .host_int_status_reg = 0x03, - .status_reg_0 = 0x60, - .status_reg_1 = 0x61, - .sdio_int_mask = 0x3f, - .data_port_mask = 0x0000fffe, - .io_port_0_reg = 0x78, - .io_port_1_reg = 0x79, - .io_port_2_reg = 0x7A, - .max_mp_regs = 64, - .rd_bitmap_l = 0x04, - .rd_bitmap_u = 0x05, - .wr_bitmap_l = 0x06, - .wr_bitmap_u = 0x07, - .rd_len_p0_l = 0x08, - .rd_len_p0_u = 0x09, - .card_misc_cfg_reg = 0x6c, - .func1_dump_reg_start = 0x0, - .func1_dump_reg_end = 0x9, - .func1_scratch_reg = 0x60, - .func1_spec_reg_num = 5, - .func1_spec_reg_table = {0x28, 0x30, 0x34, 0x38, 0x3c}, -}; - -static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = { - .start_rd_port = 0, - .start_wr_port = 0, - .base_0_reg = 0x60, - .base_1_reg = 0x61, - .poll_reg = 0x50, - .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | - CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, - .host_int_rsr_reg = 0x1, - .host_int_status_reg = 0x03, - .host_int_mask_reg = 0x02, - .status_reg_0 = 0xc0, - .status_reg_1 = 0xc1, - .sdio_int_mask = 0xff, - .data_port_mask = 0xffffffff, - .io_port_0_reg = 0xD8, - .io_port_1_reg = 0xD9, - .io_port_2_reg = 0xDA, - .max_mp_regs = 184, - .rd_bitmap_l = 0x04, - .rd_bitmap_u = 0x05, - .rd_bitmap_1l = 0x06, - .rd_bitmap_1u = 0x07, - .wr_bitmap_l = 0x08, - .wr_bitmap_u = 0x09, - .wr_bitmap_1l = 0x0a, - .wr_bitmap_1u = 0x0b, - .rd_len_p0_l = 0x0c, - .rd_len_p0_u = 0x0d, - .card_misc_cfg_reg = 0xcc, - .card_cfg_2_1_reg = 0xcd, - .cmd_rd_len_0 = 0xb4, - .cmd_rd_len_1 = 0xb5, - .cmd_rd_len_2 = 0xb6, - .cmd_rd_len_3 = 0xb7, - .cmd_cfg_0 = 0xb8, - .cmd_cfg_1 = 0xb9, - .cmd_cfg_2 = 0xba, - .cmd_cfg_3 = 0xbb, - .fw_dump_host_ready = 0xee, - .fw_dump_ctrl = 0xe2, - .fw_dump_start = 0xe3, - .fw_dump_end = 0xea, - .func1_dump_reg_start = 0x0, - .func1_dump_reg_end = 0xb, - .func1_scratch_reg = 0xc0, - .func1_spec_reg_num = 8, - .func1_spec_reg_table = {0x4C, 0x50, 0x54, 0x55, 0x58, - 0x59, 0x5c, 0x5d}, -}; - -static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8997 = { - .start_rd_port = 0, - .start_wr_port = 0, - .base_0_reg = 0xF8, - .base_1_reg = 0xF9, - .poll_reg = 0x5C, - .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | - CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, - .host_int_rsr_reg = 0x4, - .host_int_status_reg = 0x0C, - .host_int_mask_reg = 0x08, - .status_reg_0 = 0xE8, - .status_reg_1 = 0xE9, - .sdio_int_mask = 0xff, - .data_port_mask = 0xffffffff, - .io_port_0_reg = 0xE4, - .io_port_1_reg = 0xE5, - .io_port_2_reg = 0xE6, - .max_mp_regs = 196, - .rd_bitmap_l = 0x10, - .rd_bitmap_u = 0x11, - .rd_bitmap_1l = 0x12, - .rd_bitmap_1u = 0x13, - .wr_bitmap_l = 0x14, - .wr_bitmap_u = 0x15, - .wr_bitmap_1l = 0x16, - .wr_bitmap_1u = 0x17, - .rd_len_p0_l = 0x18, - .rd_len_p0_u = 0x19, - .card_misc_cfg_reg = 0xd8, - .card_cfg_2_1_reg = 0xd9, - .cmd_rd_len_0 = 0xc0, - .cmd_rd_len_1 = 0xc1, - .cmd_rd_len_2 = 0xc2, - .cmd_rd_len_3 = 0xc3, - .cmd_cfg_0 = 0xc4, - .cmd_cfg_1 = 0xc5, - .cmd_cfg_2 = 0xc6, - .cmd_cfg_3 = 0xc7, - .fw_dump_host_ready = 0xcc, - .fw_dump_ctrl = 0xf0, - .fw_dump_start = 0xf1, - .fw_dump_end = 0xf8, - .func1_dump_reg_start = 0x10, - .func1_dump_reg_end = 0x17, - .func1_scratch_reg = 0xe8, - .func1_spec_reg_num = 13, - .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, - 0x60, 0x61, 0x62, 0x64, - 0x65, 0x66, 0x68, 0x69, - 0x6a}, -}; - -static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = { - .start_rd_port = 0, - .start_wr_port = 0, - .base_0_reg = 0x6C, - .base_1_reg = 0x6D, - .poll_reg = 0x5C, - .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | - CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, - .host_int_rsr_reg = 0x4, - .host_int_status_reg = 0x0C, - .host_int_mask_reg = 0x08, - .status_reg_0 = 0x90, - .status_reg_1 = 0x91, - .sdio_int_mask = 0xff, - .data_port_mask = 0xffffffff, - .io_port_0_reg = 0xE4, - .io_port_1_reg = 0xE5, - .io_port_2_reg = 0xE6, - .max_mp_regs = 196, - .rd_bitmap_l = 0x10, - .rd_bitmap_u = 0x11, - .rd_bitmap_1l = 0x12, - .rd_bitmap_1u = 0x13, - .wr_bitmap_l = 0x14, - .wr_bitmap_u = 0x15, - .wr_bitmap_1l = 0x16, - .wr_bitmap_1u = 0x17, - .rd_len_p0_l = 0x18, - .rd_len_p0_u = 0x19, - .card_misc_cfg_reg = 0xd8, - .card_cfg_2_1_reg = 0xd9, - .cmd_rd_len_0 = 0xc0, - .cmd_rd_len_1 = 0xc1, - .cmd_rd_len_2 = 0xc2, - .cmd_rd_len_3 = 0xc3, - .cmd_cfg_0 = 0xc4, - .cmd_cfg_1 = 0xc5, - .cmd_cfg_2 = 0xc6, - .cmd_cfg_3 = 0xc7, - .func1_dump_reg_start = 0x10, - .func1_dump_reg_end = 0x17, - .func1_scratch_reg = 0x90, - .func1_spec_reg_num = 13, - .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, - 0x61, 0x62, 0x64, 0x65, 0x66, - 0x68, 0x69, 0x6a}, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { - .firmware = SD8786_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd87xx, - .max_ports = 16, - .mp_agg_pkt_limit = 8, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .supports_sdio_new_mode = false, - .has_control_mask = true, - .can_dump_fw = false, - .can_auto_tdls = false, - .can_ext_scan = false, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { - .firmware = SD8787_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd87xx, - .max_ports = 16, - .mp_agg_pkt_limit = 8, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .supports_sdio_new_mode = false, - .has_control_mask = true, - .can_dump_fw = false, - .can_auto_tdls = false, - .can_ext_scan = true, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { - .firmware = SD8797_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd87xx, - .max_ports = 16, - .mp_agg_pkt_limit = 8, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .supports_sdio_new_mode = false, - .has_control_mask = true, - .can_dump_fw = false, - .can_auto_tdls = false, - .can_ext_scan = true, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { - .firmware = SD8897_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd8897, - .max_ports = 32, - .mp_agg_pkt_limit = 16, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, - .supports_sdio_new_mode = true, - .has_control_mask = false, - .can_dump_fw = true, - .can_auto_tdls = false, - .can_ext_scan = true, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = { - .firmware = SD8997_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd8997, - .max_ports = 32, - .mp_agg_pkt_limit = 16, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, - .supports_sdio_new_mode = true, - .has_control_mask = false, - .can_dump_fw = true, - .fw_dump_enh = true, - .can_auto_tdls = false, - .can_ext_scan = true, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = { - .firmware = SD8887_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd8887, - .max_ports = 32, - .mp_agg_pkt_limit = 16, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, - .supports_sdio_new_mode = true, - .has_control_mask = false, - .can_dump_fw = false, - .can_auto_tdls = true, - .can_ext_scan = true, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = { - .firmware = SD8801_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd87xx, - .max_ports = 16, - .mp_agg_pkt_limit = 8, - .supports_sdio_new_mode = false, - .has_control_mask = true, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .can_dump_fw = false, - .can_auto_tdls = false, - .can_ext_scan = true, -}; - -/* - * .cmdrsp_complete handler - */ -static inline int mwifiex_sdio_cmdrsp_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - dev_kfree_skb_any(skb); - return 0; -} - -/* - * .event_complete handler - */ -static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - dev_kfree_skb_any(skb); - return 0; -} - -static inline bool -mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card) -{ - u8 tmp; - - if (card->curr_rd_port < card->mpa_rx.start_port) { - if (card->supports_sdio_new_mode) - tmp = card->mp_end_port >> 1; - else - tmp = card->mp_agg_pkt_limit; - - if (((card->max_ports - card->mpa_rx.start_port) + - card->curr_rd_port) >= tmp) - return true; - } - - if (!card->supports_sdio_new_mode) - return false; - - if ((card->curr_rd_port - card->mpa_rx.start_port) >= - (card->mp_end_port >> 1)) - return true; - - return false; -} - -static inline bool -mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card) -{ - u16 tmp; - - if (card->curr_wr_port < card->mpa_tx.start_port) { - if (card->supports_sdio_new_mode) - tmp = card->mp_end_port >> 1; - else - tmp = card->mp_agg_pkt_limit; - - if (((card->max_ports - card->mpa_tx.start_port) + - card->curr_wr_port) >= tmp) - return true; - } - - if (!card->supports_sdio_new_mode) - return false; - - if ((card->curr_wr_port - card->mpa_tx.start_port) >= - (card->mp_end_port >> 1)) - return true; - - return false; -} - -/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ -static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card, - u16 rx_len, u8 port) -{ - card->mpa_rx.buf_len += rx_len; - - if (!card->mpa_rx.pkt_cnt) - card->mpa_rx.start_port = port; - - if (card->supports_sdio_new_mode) { - card->mpa_rx.ports |= (1 << port); - } else { - if (card->mpa_rx.start_port <= port) - card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt); - else - card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1); - } - card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = NULL; - card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = rx_len; - card->mpa_rx.pkt_cnt++; -} -#endif /* _MWIFIEX_SDIO_H */ diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c deleted file mode 100644 index e486867a4c67..000000000000 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ /dev/null @@ -1,2282 +0,0 @@ -/* - * Marvell Wireless LAN device driver: station command handling - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11ac.h" - -static bool drcs; -module_param(drcs, bool, 0644); -MODULE_PARM_DESC(drcs, "multi-channel operation:1, single-channel operation:0"); - -static bool disable_auto_ds; -module_param(disable_auto_ds, bool, 0); -MODULE_PARM_DESC(disable_auto_ds, - "deepsleep enabled=0(default), deepsleep disabled=1"); -/* - * This function prepares command to set/get RSSI information. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting data/beacon average factors - * - Resetting SNR/NF/RSSI values in private structure - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_802_11_rssi_info(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 cmd_action) -{ - cmd->command = cpu_to_le16(HostCmd_CMD_RSSI_INFO); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rssi_info) + - S_DS_GEN); - cmd->params.rssi_info.action = cpu_to_le16(cmd_action); - cmd->params.rssi_info.ndata = cpu_to_le16(priv->data_avg_factor); - cmd->params.rssi_info.nbcn = cpu_to_le16(priv->bcn_avg_factor); - - /* Reset SNR/NF/RSSI values in private structure */ - priv->data_rssi_last = 0; - priv->data_nf_last = 0; - priv->data_rssi_avg = 0; - priv->data_nf_avg = 0; - priv->bcn_rssi_last = 0; - priv->bcn_nf_last = 0; - priv->bcn_rssi_avg = 0; - priv->bcn_nf_avg = 0; - - return 0; -} - -/* - * This function prepares command to set MAC control. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_mac_control(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u16 *action) -{ - struct host_cmd_ds_mac_control *mac_ctrl = &cmd->params.mac_ctrl; - - if (cmd_action != HostCmd_ACT_GEN_SET) { - mwifiex_dbg(priv->adapter, ERROR, - "mac_control: only support set cmd\n"); - return -1; - } - - cmd->command = cpu_to_le16(HostCmd_CMD_MAC_CONTROL); - cmd->size = - cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN); - mac_ctrl->action = cpu_to_le16(*action); - - return 0; -} - -/* - * This function prepares command to set/get SNMP MIB. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting SNMP MIB OID number and value - * (as required) - * - Ensuring correct endian-ness - * - * The following SNMP MIB OIDs are supported - - * - FRAG_THRESH_I : Fragmentation threshold - * - RTS_THRESH_I : RTS threshold - * - SHORT_RETRY_LIM_I : Short retry limit - * - DOT11D_I : 11d support - */ -static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u32 cmd_oid, - u16 *ul_temp) -{ - struct host_cmd_ds_802_11_snmp_mib *snmp_mib = &cmd->params.smib; - - mwifiex_dbg(priv->adapter, CMD, - "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib) - - 1 + S_DS_GEN); - - snmp_mib->oid = cpu_to_le16((u16)cmd_oid); - if (cmd_action == HostCmd_ACT_GEN_GET) { - snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_GET); - snmp_mib->buf_size = cpu_to_le16(MAX_SNMP_BUF_SIZE); - le16_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE); - } else if (cmd_action == HostCmd_ACT_GEN_SET) { - snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET); - snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); - *((__le16 *) (snmp_mib->value)) = cpu_to_le16(*ul_temp); - le16_add_cpu(&cmd->size, sizeof(u16)); - } - - mwifiex_dbg(priv->adapter, CMD, - "cmd: SNMP_CMD: Action=0x%x, OID=0x%x,\t" - "OIDSize=0x%x, Value=0x%x\n", - cmd_action, cmd_oid, le16_to_cpu(snmp_mib->buf_size), - le16_to_cpu(*(__le16 *)snmp_mib->value)); - return 0; -} - -/* - * This function prepares command to get log. - * - * Preparation includes - - * - Setting command ID and proper size - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_802_11_get_log(struct host_cmd_ds_command *cmd) -{ - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_GET_LOG); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_get_log) + - S_DS_GEN); - return 0; -} - -/* - * This function prepares command to set/get Tx data rate configuration. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting configuration index, rate scope and rate drop pattern - * parameters (as required) - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u16 *pbitmap_rates) -{ - struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg; - struct mwifiex_rate_scope *rate_scope; - struct mwifiex_rate_drop_pattern *rate_drop; - u32 i; - - cmd->command = cpu_to_le16(HostCmd_CMD_TX_RATE_CFG); - - rate_cfg->action = cpu_to_le16(cmd_action); - rate_cfg->cfg_index = 0; - - rate_scope = (struct mwifiex_rate_scope *) ((u8 *) rate_cfg + - sizeof(struct host_cmd_ds_tx_rate_cfg)); - rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE); - rate_scope->length = cpu_to_le16 - (sizeof(*rate_scope) - sizeof(struct mwifiex_ie_types_header)); - if (pbitmap_rates != NULL) { - rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); - rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); - for (i = 0; - i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); - i++) - rate_scope->ht_mcs_rate_bitmap[i] = - cpu_to_le16(pbitmap_rates[2 + i]); - if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { - for (i = 0; - i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); - i++) - rate_scope->vht_mcs_rate_bitmap[i] = - cpu_to_le16(pbitmap_rates[10 + i]); - } - } else { - rate_scope->hr_dsss_rate_bitmap = - cpu_to_le16(priv->bitmap_rates[0]); - rate_scope->ofdm_rate_bitmap = - cpu_to_le16(priv->bitmap_rates[1]); - for (i = 0; - i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); - i++) - rate_scope->ht_mcs_rate_bitmap[i] = - cpu_to_le16(priv->bitmap_rates[2 + i]); - if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { - for (i = 0; - i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); - i++) - rate_scope->vht_mcs_rate_bitmap[i] = - cpu_to_le16(priv->bitmap_rates[10 + i]); - } - } - - rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope + - sizeof(struct mwifiex_rate_scope)); - rate_drop->type = cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL); - rate_drop->length = cpu_to_le16(sizeof(rate_drop->rate_drop_mode)); - rate_drop->rate_drop_mode = 0; - - cmd->size = - cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_tx_rate_cfg) + - sizeof(struct mwifiex_rate_scope) + - sizeof(struct mwifiex_rate_drop_pattern)); - - return 0; -} - -/* - * This function prepares command to set/get Tx power configuration. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting Tx power mode, power group TLV - * (as required) - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd, - u16 cmd_action, - struct host_cmd_ds_txpwr_cfg *txp) -{ - struct mwifiex_types_power_group *pg_tlv; - struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg; - - cmd->command = cpu_to_le16(HostCmd_CMD_TXPWR_CFG); - cmd->size = - cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg)); - switch (cmd_action) { - case HostCmd_ACT_GEN_SET: - if (txp->mode) { - pg_tlv = (struct mwifiex_types_power_group - *) ((unsigned long) txp + - sizeof(struct host_cmd_ds_txpwr_cfg)); - memmove(cmd_txp_cfg, txp, - sizeof(struct host_cmd_ds_txpwr_cfg) + - sizeof(struct mwifiex_types_power_group) + - le16_to_cpu(pg_tlv->length)); - - pg_tlv = (struct mwifiex_types_power_group *) ((u8 *) - cmd_txp_cfg + - sizeof(struct host_cmd_ds_txpwr_cfg)); - cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + - sizeof(struct mwifiex_types_power_group) + - le16_to_cpu(pg_tlv->length)); - } else { - memmove(cmd_txp_cfg, txp, sizeof(*txp)); - } - cmd_txp_cfg->action = cpu_to_le16(cmd_action); - break; - case HostCmd_ACT_GEN_GET: - cmd_txp_cfg->action = cpu_to_le16(cmd_action); - break; - } - - return 0; -} - -/* - * This function prepares command to get RF Tx power. - */ -static int mwifiex_cmd_rf_tx_power(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct host_cmd_ds_rf_tx_pwr *txp = &cmd->params.txp; - - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_tx_pwr) - + S_DS_GEN); - cmd->command = cpu_to_le16(HostCmd_CMD_RF_TX_PWR); - txp->action = cpu_to_le16(cmd_action); - - return 0; -} - -/* - * This function prepares command to set rf antenna. - */ -static int mwifiex_cmd_rf_antenna(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, - struct mwifiex_ds_ant_cfg *ant_cfg) -{ - struct host_cmd_ds_rf_ant_mimo *ant_mimo = &cmd->params.ant_mimo; - struct host_cmd_ds_rf_ant_siso *ant_siso = &cmd->params.ant_siso; - - cmd->command = cpu_to_le16(HostCmd_CMD_RF_ANTENNA); - - if (cmd_action != HostCmd_ACT_GEN_SET) - return 0; - - if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) { - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_mimo) + - S_DS_GEN); - ant_mimo->action_tx = cpu_to_le16(HostCmd_ACT_SET_TX); - ant_mimo->tx_ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); - ant_mimo->action_rx = cpu_to_le16(HostCmd_ACT_SET_RX); - ant_mimo->rx_ant_mode = cpu_to_le16((u16)ant_cfg->rx_ant); - } else { - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_siso) + - S_DS_GEN); - ant_siso->action = cpu_to_le16(HostCmd_ACT_SET_BOTH); - ant_siso->ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); - } - - return 0; -} - -/* - * This function prepares command to set Host Sleep configuration. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting Host Sleep action, conditions, ARP filters - * (as required) - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, - struct mwifiex_hs_config_param *hscfg_param) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; - bool hs_activate = false; - - if (!hscfg_param) - /* New Activate command */ - hs_activate = true; - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH); - - if (!hs_activate && - (hscfg_param->conditions != cpu_to_le32(HS_CFG_CANCEL)) && - ((adapter->arp_filter_size > 0) && - (adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) { - mwifiex_dbg(adapter, CMD, - "cmd: Attach %d bytes ArpFilter to HSCfg cmd\n", - adapter->arp_filter_size); - memcpy(((u8 *) hs_cfg) + - sizeof(struct host_cmd_ds_802_11_hs_cfg_enh), - adapter->arp_filter, adapter->arp_filter_size); - cmd->size = cpu_to_le16 - (adapter->arp_filter_size + - sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) - + S_DS_GEN); - } else { - cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct - host_cmd_ds_802_11_hs_cfg_enh)); - } - if (hs_activate) { - hs_cfg->action = cpu_to_le16(HS_ACTIVATE); - hs_cfg->params.hs_activate.resp_ctrl = cpu_to_le16(RESP_NEEDED); - } else { - hs_cfg->action = cpu_to_le16(HS_CONFIGURE); - hs_cfg->params.hs_config.conditions = hscfg_param->conditions; - hs_cfg->params.hs_config.gpio = hscfg_param->gpio; - hs_cfg->params.hs_config.gap = hscfg_param->gap; - mwifiex_dbg(adapter, CMD, - "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", - hs_cfg->params.hs_config.conditions, - hs_cfg->params.hs_config.gpio, - hs_cfg->params.hs_config.gap); - } - - return 0; -} - -/* - * This function prepares command to set/get MAC address. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting MAC address (for SET only) - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_802_11_mac_address(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action) -{ - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_MAC_ADDRESS); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) + - S_DS_GEN); - cmd->result = 0; - - cmd->params.mac_addr.action = cpu_to_le16(cmd_action); - - if (cmd_action == HostCmd_ACT_GEN_SET) - memcpy(cmd->params.mac_addr.mac_addr, priv->curr_addr, - ETH_ALEN); - return 0; -} - -/* - * This function prepares command to set MAC multicast address. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting MAC multicast address - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_mac_multicast_adr(struct host_cmd_ds_command *cmd, - u16 cmd_action, - struct mwifiex_multicast_list *mcast_list) -{ - struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr; - - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) + - S_DS_GEN); - cmd->command = cpu_to_le16(HostCmd_CMD_MAC_MULTICAST_ADR); - - mcast_addr->action = cpu_to_le16(cmd_action); - mcast_addr->num_of_adrs = - cpu_to_le16((u16) mcast_list->num_multicast_addr); - memcpy(mcast_addr->mac_list, mcast_list->mac_list, - mcast_list->num_multicast_addr * ETH_ALEN); - - return 0; -} - -/* - * This function prepares command to deauthenticate. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting AP MAC address and reason code - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_802_11_deauthenticate(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u8 *mac) -{ - struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_DEAUTHENTICATE); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_deauthenticate) - + S_DS_GEN); - - /* Set AP MAC address */ - memcpy(deauth->mac_addr, mac, ETH_ALEN); - - mwifiex_dbg(priv->adapter, CMD, "cmd: Deauth: %pM\n", deauth->mac_addr); - - deauth->reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); - - return 0; -} - -/* - * This function prepares command to stop Ad-Hoc network. - * - * Preparation includes - - * - Setting command ID and proper size - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_802_11_ad_hoc_stop(struct host_cmd_ds_command *cmd) -{ - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_STOP); - cmd->size = cpu_to_le16(S_DS_GEN); - return 0; -} - -/* - * This function sets WEP key(s) to key parameter TLV(s). - * - * Multi-key parameter TLVs are supported, so we can send multiple - * WEP keys in a single buffer. - */ -static int -mwifiex_set_keyparamset_wep(struct mwifiex_private *priv, - struct mwifiex_ie_type_key_param_set *key_param_set, - u16 *key_param_len) -{ - int cur_key_param_len; - u8 i; - - /* Multi-key_param_set TLV is supported */ - for (i = 0; i < NUM_WEP_KEYS; i++) { - if ((priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP40) || - (priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP104)) { - key_param_set->type = - cpu_to_le16(TLV_TYPE_KEY_MATERIAL); -/* Key_param_set WEP fixed length */ -#define KEYPARAMSET_WEP_FIXED_LEN 8 - key_param_set->length = cpu_to_le16((u16) - (priv->wep_key[i]. - key_length + - KEYPARAMSET_WEP_FIXED_LEN)); - key_param_set->key_type_id = - cpu_to_le16(KEY_TYPE_ID_WEP); - key_param_set->key_info = - cpu_to_le16(KEY_ENABLED | KEY_UNICAST | - KEY_MCAST); - key_param_set->key_len = - cpu_to_le16(priv->wep_key[i].key_length); - /* Set WEP key index */ - key_param_set->key[0] = i; - /* Set default Tx key flag */ - if (i == - (priv-> - wep_key_curr_index & HostCmd_WEP_KEY_INDEX_MASK)) - key_param_set->key[1] = 1; - else - key_param_set->key[1] = 0; - memmove(&key_param_set->key[2], - priv->wep_key[i].key_material, - priv->wep_key[i].key_length); - - cur_key_param_len = priv->wep_key[i].key_length + - KEYPARAMSET_WEP_FIXED_LEN + - sizeof(struct mwifiex_ie_types_header); - *key_param_len += (u16) cur_key_param_len; - key_param_set = - (struct mwifiex_ie_type_key_param_set *) - ((u8 *)key_param_set + - cur_key_param_len); - } else if (!priv->wep_key[i].key_length) { - continue; - } else { - mwifiex_dbg(priv->adapter, ERROR, - "key%d Length = %d is incorrect\n", - (i + 1), priv->wep_key[i].key_length); - return -1; - } - } - - return 0; -} - -/* This function populates key material v2 command - * to set network key for AES & CMAC AES. - */ -static int mwifiex_set_aes_key_v2(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_ds_encrypt_key *enc_key, - struct host_cmd_ds_802_11_key_material_v2 *km) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u16 size, len = KEY_PARAMS_FIXED_LEN; - - if (enc_key->is_igtk_key) { - mwifiex_dbg(adapter, INFO, - "%s: Set CMAC AES Key\n", __func__); - if (enc_key->is_rx_seq_valid) - memcpy(km->key_param_set.key_params.cmac_aes.ipn, - enc_key->pn, enc_key->pn_len); - km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST); - km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK); - km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC; - km->key_param_set.key_params.cmac_aes.key_len = - cpu_to_le16(enc_key->key_len); - memcpy(km->key_param_set.key_params.cmac_aes.key, - enc_key->key_material, enc_key->key_len); - len += sizeof(struct mwifiex_cmac_aes_param); - } else { - mwifiex_dbg(adapter, INFO, - "%s: Set AES Key\n", __func__); - if (enc_key->is_rx_seq_valid) - memcpy(km->key_param_set.key_params.aes.pn, - enc_key->pn, enc_key->pn_len); - km->key_param_set.key_type = KEY_TYPE_ID_AES; - km->key_param_set.key_params.aes.key_len = - cpu_to_le16(enc_key->key_len); - memcpy(km->key_param_set.key_params.aes.key, - enc_key->key_material, enc_key->key_len); - len += sizeof(struct mwifiex_aes_param); - } - - km->key_param_set.len = cpu_to_le16(len); - size = len + sizeof(struct mwifiex_ie_types_header) + - sizeof(km->action) + S_DS_GEN; - cmd->size = cpu_to_le16(size); - - return 0; -} - -/* This function prepares command to set/get/reset network key(s). - * This function prepares key material command for V2 format. - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting WEP keys, WAPI keys or WPA keys along with required - * encryption (TKIP, AES) (as required) - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_802_11_key_material_v2(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u32 cmd_oid, - struct mwifiex_ds_encrypt_key *enc_key) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u8 *mac = enc_key->mac_addr; - u16 key_info, len = KEY_PARAMS_FIXED_LEN; - struct host_cmd_ds_802_11_key_material_v2 *km = - &cmd->params.key_material_v2; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); - km->action = cpu_to_le16(cmd_action); - - if (cmd_action == HostCmd_ACT_GEN_GET) { - mwifiex_dbg(adapter, INFO, "%s: Get key\n", __func__); - km->key_param_set.key_idx = - enc_key->key_index & KEY_INDEX_MASK; - km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); - km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); - memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); - - if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) - key_info = KEY_UNICAST; - else - key_info = KEY_MCAST; - - if (enc_key->is_igtk_key) - key_info |= KEY_IGTK; - - km->key_param_set.key_info = cpu_to_le16(key_info); - - cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + - S_DS_GEN + KEY_PARAMS_FIXED_LEN + - sizeof(km->action)); - return 0; - } - - memset(&km->key_param_set, 0, - sizeof(struct mwifiex_ie_type_key_param_set_v2)); - - if (enc_key->key_disable) { - mwifiex_dbg(adapter, INFO, "%s: Remove key\n", __func__); - km->action = cpu_to_le16(HostCmd_ACT_GEN_REMOVE); - km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); - km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); - km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; - key_info = KEY_MCAST | KEY_UNICAST; - km->key_param_set.key_info = cpu_to_le16(key_info); - memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); - cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + - S_DS_GEN + KEY_PARAMS_FIXED_LEN + - sizeof(km->action)); - return 0; - } - - km->action = cpu_to_le16(HostCmd_ACT_GEN_SET); - km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; - km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); - key_info = KEY_ENABLED; - memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); - - if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) { - mwifiex_dbg(adapter, INFO, "%s: Set WEP Key\n", __func__); - len += sizeof(struct mwifiex_wep_param); - km->key_param_set.len = cpu_to_le16(len); - km->key_param_set.key_type = KEY_TYPE_ID_WEP; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - key_info |= KEY_MCAST | KEY_UNICAST; - } else { - if (enc_key->is_current_wep_key) { - key_info |= KEY_MCAST | KEY_UNICAST; - if (km->key_param_set.key_idx == - (priv->wep_key_curr_index & KEY_INDEX_MASK)) - key_info |= KEY_DEFAULT; - } else { - if (mac) { - if (is_broadcast_ether_addr(mac)) - key_info |= KEY_MCAST; - else - key_info |= KEY_UNICAST | - KEY_DEFAULT; - } else { - key_info |= KEY_MCAST; - } - } - } - km->key_param_set.key_info = cpu_to_le16(key_info); - - km->key_param_set.key_params.wep.key_len = - cpu_to_le16(enc_key->key_len); - memcpy(km->key_param_set.key_params.wep.key, - enc_key->key_material, enc_key->key_len); - - cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + - len + sizeof(km->action) + S_DS_GEN); - return 0; - } - - if (is_broadcast_ether_addr(mac)) - key_info |= KEY_MCAST | KEY_RX_KEY; - else - key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY; - - if (enc_key->is_wapi_key) { - mwifiex_dbg(adapter, INFO, "%s: Set WAPI Key\n", __func__); - km->key_param_set.key_type = KEY_TYPE_ID_WAPI; - memcpy(km->key_param_set.key_params.wapi.pn, enc_key->pn, - PN_LEN); - km->key_param_set.key_params.wapi.key_len = - cpu_to_le16(enc_key->key_len); - memcpy(km->key_param_set.key_params.wapi.key, - enc_key->key_material, enc_key->key_len); - if (is_broadcast_ether_addr(mac)) - priv->sec_info.wapi_key_on = true; - - if (!priv->sec_info.wapi_key_on) - key_info |= KEY_DEFAULT; - km->key_param_set.key_info = cpu_to_le16(key_info); - - len += sizeof(struct mwifiex_wapi_param); - km->key_param_set.len = cpu_to_le16(len); - cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + - len + sizeof(km->action) + S_DS_GEN); - return 0; - } - - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { - key_info |= KEY_DEFAULT; - /* Enable unicast bit for WPA-NONE/ADHOC_AES */ - if (!priv->sec_info.wpa2_enabled && - !is_broadcast_ether_addr(mac)) - key_info |= KEY_UNICAST; - } else { - /* Enable default key for WPA/WPA2 */ - if (!priv->wpa_is_gtk_set) - key_info |= KEY_DEFAULT; - } - - km->key_param_set.key_info = cpu_to_le16(key_info); - - if (enc_key->key_len == WLAN_KEY_LEN_CCMP) - return mwifiex_set_aes_key_v2(priv, cmd, enc_key, km); - - if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { - mwifiex_dbg(adapter, INFO, - "%s: Set TKIP Key\n", __func__); - if (enc_key->is_rx_seq_valid) - memcpy(km->key_param_set.key_params.tkip.pn, - enc_key->pn, enc_key->pn_len); - km->key_param_set.key_type = KEY_TYPE_ID_TKIP; - km->key_param_set.key_params.tkip.key_len = - cpu_to_le16(enc_key->key_len); - memcpy(km->key_param_set.key_params.tkip.key, - enc_key->key_material, enc_key->key_len); - - len += sizeof(struct mwifiex_tkip_param); - km->key_param_set.len = cpu_to_le16(len); - cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + - len + sizeof(km->action) + S_DS_GEN); - } - - return 0; -} - -/* - * This function prepares command to set/get/reset network key(s). - * This function prepares key material command for V1 format. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting WEP keys, WAPI keys or WPA keys along with required - * encryption (TKIP, AES) (as required) - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_802_11_key_material_v1(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u32 cmd_oid, - struct mwifiex_ds_encrypt_key *enc_key) -{ - struct host_cmd_ds_802_11_key_material *key_material = - &cmd->params.key_material; - struct host_cmd_tlv_mac_addr *tlv_mac; - u16 key_param_len = 0, cmd_size; - int ret = 0; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); - key_material->action = cpu_to_le16(cmd_action); - - if (cmd_action == HostCmd_ACT_GEN_GET) { - cmd->size = - cpu_to_le16(sizeof(key_material->action) + S_DS_GEN); - return ret; - } - - if (!enc_key) { - memset(&key_material->key_param_set, 0, - (NUM_WEP_KEYS * - sizeof(struct mwifiex_ie_type_key_param_set))); - ret = mwifiex_set_keyparamset_wep(priv, - &key_material->key_param_set, - &key_param_len); - cmd->size = cpu_to_le16(key_param_len + - sizeof(key_material->action) + S_DS_GEN); - return ret; - } else - memset(&key_material->key_param_set, 0, - sizeof(struct mwifiex_ie_type_key_param_set)); - if (enc_key->is_wapi_key) { - mwifiex_dbg(priv->adapter, INFO, "info: Set WAPI Key\n"); - key_material->key_param_set.key_type_id = - cpu_to_le16(KEY_TYPE_ID_WAPI); - if (cmd_oid == KEY_INFO_ENABLED) - key_material->key_param_set.key_info = - cpu_to_le16(KEY_ENABLED); - else - key_material->key_param_set.key_info = - cpu_to_le16(!KEY_ENABLED); - - key_material->key_param_set.key[0] = enc_key->key_index; - if (!priv->sec_info.wapi_key_on) - key_material->key_param_set.key[1] = 1; - else - /* set 0 when re-key */ - key_material->key_param_set.key[1] = 0; - - if (!is_broadcast_ether_addr(enc_key->mac_addr)) { - /* WAPI pairwise key: unicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_UNICAST); - } else { /* WAPI group key: multicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_MCAST); - priv->sec_info.wapi_key_on = true; - } - - key_material->key_param_set.type = - cpu_to_le16(TLV_TYPE_KEY_MATERIAL); - key_material->key_param_set.key_len = - cpu_to_le16(WAPI_KEY_LEN); - memcpy(&key_material->key_param_set.key[2], - enc_key->key_material, enc_key->key_len); - memcpy(&key_material->key_param_set.key[2 + enc_key->key_len], - enc_key->pn, PN_LEN); - key_material->key_param_set.length = - cpu_to_le16(WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN); - - key_param_len = (WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN) + - sizeof(struct mwifiex_ie_types_header); - cmd->size = cpu_to_le16(sizeof(key_material->action) - + S_DS_GEN + key_param_len); - return ret; - } - if (enc_key->key_len == WLAN_KEY_LEN_CCMP) { - if (enc_key->is_igtk_key) { - mwifiex_dbg(priv->adapter, CMD, "cmd: CMAC_AES\n"); - key_material->key_param_set.key_type_id = - cpu_to_le16(KEY_TYPE_ID_AES_CMAC); - if (cmd_oid == KEY_INFO_ENABLED) - key_material->key_param_set.key_info = - cpu_to_le16(KEY_ENABLED); - else - key_material->key_param_set.key_info = - cpu_to_le16(!KEY_ENABLED); - - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_IGTK); - } else { - mwifiex_dbg(priv->adapter, CMD, "cmd: WPA_AES\n"); - key_material->key_param_set.key_type_id = - cpu_to_le16(KEY_TYPE_ID_AES); - if (cmd_oid == KEY_INFO_ENABLED) - key_material->key_param_set.key_info = - cpu_to_le16(KEY_ENABLED); - else - key_material->key_param_set.key_info = - cpu_to_le16(!KEY_ENABLED); - - if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) - /* AES pairwise key: unicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_UNICAST); - else /* AES group key: multicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_MCAST); - } - } else if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { - mwifiex_dbg(priv->adapter, CMD, "cmd: WPA_TKIP\n"); - key_material->key_param_set.key_type_id = - cpu_to_le16(KEY_TYPE_ID_TKIP); - key_material->key_param_set.key_info = - cpu_to_le16(KEY_ENABLED); - - if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) - /* TKIP pairwise key: unicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_UNICAST); - else /* TKIP group key: multicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_MCAST); - } - - if (key_material->key_param_set.key_type_id) { - key_material->key_param_set.type = - cpu_to_le16(TLV_TYPE_KEY_MATERIAL); - key_material->key_param_set.key_len = - cpu_to_le16((u16) enc_key->key_len); - memcpy(key_material->key_param_set.key, enc_key->key_material, - enc_key->key_len); - key_material->key_param_set.length = - cpu_to_le16((u16) enc_key->key_len + - KEYPARAMSET_FIXED_LEN); - - key_param_len = (u16)(enc_key->key_len + KEYPARAMSET_FIXED_LEN) - + sizeof(struct mwifiex_ie_types_header); - - if (le16_to_cpu(key_material->key_param_set.key_type_id) == - KEY_TYPE_ID_AES_CMAC) { - struct mwifiex_cmac_param *param = - (void *)key_material->key_param_set.key; - - memcpy(param->ipn, enc_key->pn, IGTK_PN_LEN); - memcpy(param->key, enc_key->key_material, - WLAN_KEY_LEN_AES_CMAC); - - key_param_len = sizeof(struct mwifiex_cmac_param); - key_material->key_param_set.key_len = - cpu_to_le16(key_param_len); - key_param_len += KEYPARAMSET_FIXED_LEN; - key_material->key_param_set.length = - cpu_to_le16(key_param_len); - key_param_len += sizeof(struct mwifiex_ie_types_header); - } - - cmd->size = cpu_to_le16(sizeof(key_material->action) + S_DS_GEN - + key_param_len); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - tlv_mac = (void *)((u8 *)&key_material->key_param_set + - key_param_len); - tlv_mac->header.type = - cpu_to_le16(TLV_TYPE_STA_MAC_ADDR); - tlv_mac->header.len = cpu_to_le16(ETH_ALEN); - memcpy(tlv_mac->mac_addr, enc_key->mac_addr, ETH_ALEN); - cmd_size = key_param_len + S_DS_GEN + - sizeof(key_material->action) + - sizeof(struct host_cmd_tlv_mac_addr); - } else { - cmd_size = key_param_len + S_DS_GEN + - sizeof(key_material->action); - } - cmd->size = cpu_to_le16(cmd_size); - } - - return ret; -} - -/* Wrapper function for setting network key depending upon FW KEY API version */ -static int -mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u32 cmd_oid, - struct mwifiex_ds_encrypt_key *enc_key) -{ - if (priv->adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) - return mwifiex_cmd_802_11_key_material_v2(priv, cmd, - cmd_action, cmd_oid, - enc_key); - - else - return mwifiex_cmd_802_11_key_material_v1(priv, cmd, - cmd_action, cmd_oid, - enc_key); -} - -/* - * This function prepares command to set/get 11d domain information. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting domain information fields (for SET only) - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_802_11d_domain_info(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11d_domain_info *domain_info = - &cmd->params.domain_info; - struct mwifiex_ietypes_domain_param_set *domain = - &domain_info->domain; - u8 no_of_triplet = adapter->domain_reg.no_of_triplet; - - mwifiex_dbg(adapter, INFO, - "info: 11D: no_of_triplet=0x%x\n", no_of_triplet); - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO); - domain_info->action = cpu_to_le16(cmd_action); - if (cmd_action == HostCmd_ACT_GEN_GET) { - cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); - return 0; - } - - /* Set domain info fields */ - domain->header.type = cpu_to_le16(WLAN_EID_COUNTRY); - memcpy(domain->country_code, adapter->domain_reg.country_code, - sizeof(domain->country_code)); - - domain->header.len = - cpu_to_le16((no_of_triplet * - sizeof(struct ieee80211_country_ie_triplet)) - + sizeof(domain->country_code)); - - if (no_of_triplet) { - memcpy(domain->triplet, adapter->domain_reg.triplet, - no_of_triplet * sizeof(struct - ieee80211_country_ie_triplet)); - - cmd->size = cpu_to_le16(sizeof(domain_info->action) + - le16_to_cpu(domain->header.len) + - sizeof(struct mwifiex_ie_types_header) - + S_DS_GEN); - } else { - cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); - } - - return 0; -} - -/* - * This function prepares command to set/get IBSS coalescing status. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting status to enable or disable (for SET only) - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_ibss_coalescing_status(struct host_cmd_ds_command *cmd, - u16 cmd_action, u16 *enable) -{ - struct host_cmd_ds_802_11_ibss_status *ibss_coal = - &(cmd->params.ibss_coalescing); - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_IBSS_COALESCING_STATUS); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_ibss_status) + - S_DS_GEN); - cmd->result = 0; - ibss_coal->action = cpu_to_le16(cmd_action); - - switch (cmd_action) { - case HostCmd_ACT_GEN_SET: - if (enable) - ibss_coal->enable = cpu_to_le16(*enable); - else - ibss_coal->enable = 0; - break; - - /* In other case.. Nothing to do */ - case HostCmd_ACT_GEN_GET: - default: - break; - } - - return 0; -} - -/* This function prepares command buffer to get/set memory location value. - */ -static int -mwifiex_cmd_mem_access(struct host_cmd_ds_command *cmd, u16 cmd_action, - void *pdata_buf) -{ - struct mwifiex_ds_mem_rw *mem_rw = (void *)pdata_buf; - struct host_cmd_ds_mem_access *mem_access = (void *)&cmd->params.mem; - - cmd->command = cpu_to_le16(HostCmd_CMD_MEM_ACCESS); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mem_access) + - S_DS_GEN); - - mem_access->action = cpu_to_le16(cmd_action); - mem_access->addr = cpu_to_le32(mem_rw->addr); - mem_access->value = cpu_to_le32(mem_rw->value); - - return 0; -} - -/* - * This function prepares command to set/get register value. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting register offset (for both GET and SET) and - * register value (for SET only) - * - Ensuring correct endian-ness - * - * The following type of registers can be accessed with this function - - * - MAC register - * - BBP register - * - RF register - * - PMIC register - * - CAU register - * - EEPROM - */ -static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct mwifiex_ds_reg_rw *reg_rw = data_buf; - - switch (le16_to_cpu(cmd->command)) { - case HostCmd_CMD_MAC_REG_ACCESS: - { - struct host_cmd_ds_mac_reg_access *mac_reg; - - cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN); - mac_reg = &cmd->params.mac_reg; - mac_reg->action = cpu_to_le16(cmd_action); - mac_reg->offset = - cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); - mac_reg->value = reg_rw->value; - break; - } - case HostCmd_CMD_BBP_REG_ACCESS: - { - struct host_cmd_ds_bbp_reg_access *bbp_reg; - - cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN); - bbp_reg = &cmd->params.bbp_reg; - bbp_reg->action = cpu_to_le16(cmd_action); - bbp_reg->offset = - cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); - bbp_reg->value = (u8) le32_to_cpu(reg_rw->value); - break; - } - case HostCmd_CMD_RF_REG_ACCESS: - { - struct host_cmd_ds_rf_reg_access *rf_reg; - - cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN); - rf_reg = &cmd->params.rf_reg; - rf_reg->action = cpu_to_le16(cmd_action); - rf_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); - rf_reg->value = (u8) le32_to_cpu(reg_rw->value); - break; - } - case HostCmd_CMD_PMIC_REG_ACCESS: - { - struct host_cmd_ds_pmic_reg_access *pmic_reg; - - cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN); - pmic_reg = &cmd->params.pmic_reg; - pmic_reg->action = cpu_to_le16(cmd_action); - pmic_reg->offset = - cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); - pmic_reg->value = (u8) le32_to_cpu(reg_rw->value); - break; - } - case HostCmd_CMD_CAU_REG_ACCESS: - { - struct host_cmd_ds_rf_reg_access *cau_reg; - - cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN); - cau_reg = &cmd->params.rf_reg; - cau_reg->action = cpu_to_le16(cmd_action); - cau_reg->offset = - cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); - cau_reg->value = (u8) le32_to_cpu(reg_rw->value); - break; - } - case HostCmd_CMD_802_11_EEPROM_ACCESS: - { - struct mwifiex_ds_read_eeprom *rd_eeprom = data_buf; - struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom = - &cmd->params.eeprom; - - cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN); - cmd_eeprom->action = cpu_to_le16(cmd_action); - cmd_eeprom->offset = rd_eeprom->offset; - cmd_eeprom->byte_count = rd_eeprom->byte_count; - cmd_eeprom->value = 0; - break; - } - default: - return -1; - } - - return 0; -} - -/* - * This function prepares command to set PCI-Express - * host buffer configuration - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting host buffer configuration - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 action) -{ - struct host_cmd_ds_pcie_details *host_spec = - &cmd->params.pcie_host_spec; - struct pcie_service_card *card = priv->adapter->card; - - cmd->command = cpu_to_le16(HostCmd_CMD_PCIE_DESC_DETAILS); - cmd->size = cpu_to_le16(sizeof(struct - host_cmd_ds_pcie_details) + S_DS_GEN); - cmd->result = 0; - - memset(host_spec, 0, sizeof(struct host_cmd_ds_pcie_details)); - - if (action != HostCmd_ACT_GEN_SET) - return 0; - - /* Send the ring base addresses and count to firmware */ - host_spec->txbd_addr_lo = (u32)(card->txbd_ring_pbase); - host_spec->txbd_addr_hi = (u32)(((u64)card->txbd_ring_pbase)>>32); - host_spec->txbd_count = MWIFIEX_MAX_TXRX_BD; - host_spec->rxbd_addr_lo = (u32)(card->rxbd_ring_pbase); - host_spec->rxbd_addr_hi = (u32)(((u64)card->rxbd_ring_pbase)>>32); - host_spec->rxbd_count = MWIFIEX_MAX_TXRX_BD; - host_spec->evtbd_addr_lo = (u32)(card->evtbd_ring_pbase); - host_spec->evtbd_addr_hi = (u32)(((u64)card->evtbd_ring_pbase)>>32); - host_spec->evtbd_count = MWIFIEX_MAX_EVT_BD; - if (card->sleep_cookie_vbase) { - host_spec->sleep_cookie_addr_lo = - (u32)(card->sleep_cookie_pbase); - host_spec->sleep_cookie_addr_hi = - (u32)(((u64)(card->sleep_cookie_pbase)) >> 32); - mwifiex_dbg(priv->adapter, INFO, - "sleep_cook_lo phy addr: 0x%x\n", - host_spec->sleep_cookie_addr_lo); - } - - return 0; -} - -/* - * This function prepares command for event subscription, configuration - * and query. Events can be subscribed or unsubscribed. Current subscribed - * events can be queried. Also, current subscribed events are reported in - * every FW response. - */ -static int -mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_ds_misc_subsc_evt *subsc_evt_cfg) -{ - struct host_cmd_ds_802_11_subsc_evt *subsc_evt = &cmd->params.subsc_evt; - struct mwifiex_ie_types_rssi_threshold *rssi_tlv; - u16 event_bitmap; - u8 *pos; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SUBSCRIBE_EVENT); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) + - S_DS_GEN); - - subsc_evt->action = cpu_to_le16(subsc_evt_cfg->action); - mwifiex_dbg(priv->adapter, CMD, - "cmd: action: %d\n", subsc_evt_cfg->action); - - /*For query requests, no configuration TLV structures are to be added.*/ - if (subsc_evt_cfg->action == HostCmd_ACT_GEN_GET) - return 0; - - subsc_evt->events = cpu_to_le16(subsc_evt_cfg->events); - - event_bitmap = subsc_evt_cfg->events; - mwifiex_dbg(priv->adapter, CMD, "cmd: event bitmap : %16x\n", - event_bitmap); - - if (((subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) || - (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_SET)) && - (event_bitmap == 0)) { - mwifiex_dbg(priv->adapter, ERROR, - "Error: No event specified\t" - "for bitwise action type\n"); - return -EINVAL; - } - - /* - * Append TLV structures for each of the specified events for - * subscribing or re-configuring. This is not required for - * bitwise unsubscribing request. - */ - if (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) - return 0; - - pos = ((u8 *)subsc_evt) + - sizeof(struct host_cmd_ds_802_11_subsc_evt); - - if (event_bitmap & BITMASK_BCN_RSSI_LOW) { - rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos; - - rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_LOW); - rssi_tlv->header.len = - cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) - - sizeof(struct mwifiex_ie_types_header)); - rssi_tlv->abs_value = subsc_evt_cfg->bcn_l_rssi_cfg.abs_value; - rssi_tlv->evt_freq = subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq; - - mwifiex_dbg(priv->adapter, EVENT, - "Cfg Beacon Low Rssi event,\t" - "RSSI:-%d dBm, Freq:%d\n", - subsc_evt_cfg->bcn_l_rssi_cfg.abs_value, - subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq); - - pos += sizeof(struct mwifiex_ie_types_rssi_threshold); - le16_add_cpu(&cmd->size, - sizeof(struct mwifiex_ie_types_rssi_threshold)); - } - - if (event_bitmap & BITMASK_BCN_RSSI_HIGH) { - rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos; - - rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); - rssi_tlv->header.len = - cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) - - sizeof(struct mwifiex_ie_types_header)); - rssi_tlv->abs_value = subsc_evt_cfg->bcn_h_rssi_cfg.abs_value; - rssi_tlv->evt_freq = subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq; - - mwifiex_dbg(priv->adapter, EVENT, - "Cfg Beacon High Rssi event,\t" - "RSSI:-%d dBm, Freq:%d\n", - subsc_evt_cfg->bcn_h_rssi_cfg.abs_value, - subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq); - - pos += sizeof(struct mwifiex_ie_types_rssi_threshold); - le16_add_cpu(&cmd->size, - sizeof(struct mwifiex_ie_types_rssi_threshold)); - } - - return 0; -} - -static int -mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv, - struct mwifiex_mef_entry *mef_entry, - u8 **buffer) -{ - struct mwifiex_mef_filter *filter = mef_entry->filter; - int i, byte_len; - u8 *stack_ptr = *buffer; - - for (i = 0; i < MWIFIEX_MEF_MAX_FILTERS; i++) { - filter = &mef_entry->filter[i]; - if (!filter->filt_type) - break; - *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->repeat); - stack_ptr += 4; - *stack_ptr = TYPE_DNUM; - stack_ptr += 1; - - byte_len = filter->byte_seq[MWIFIEX_MEF_MAX_BYTESEQ]; - memcpy(stack_ptr, filter->byte_seq, byte_len); - stack_ptr += byte_len; - *stack_ptr = byte_len; - stack_ptr += 1; - *stack_ptr = TYPE_BYTESEQ; - stack_ptr += 1; - - *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->offset); - stack_ptr += 4; - *stack_ptr = TYPE_DNUM; - stack_ptr += 1; - - *stack_ptr = filter->filt_type; - stack_ptr += 1; - - if (filter->filt_action) { - *stack_ptr = filter->filt_action; - stack_ptr += 1; - } - - if (stack_ptr - *buffer > STACK_NBYTES) - return -1; - } - - *buffer = stack_ptr; - return 0; -} - -static int -mwifiex_cmd_mef_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_ds_mef_cfg *mef) -{ - struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg; - struct mwifiex_fw_mef_entry *mef_entry = NULL; - u8 *pos = (u8 *)mef_cfg; - u16 i; - - cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG); - - mef_cfg->criteria = cpu_to_le32(mef->criteria); - mef_cfg->num_entries = cpu_to_le16(mef->num_entries); - pos += sizeof(*mef_cfg); - - for (i = 0; i < mef->num_entries; i++) { - mef_entry = (struct mwifiex_fw_mef_entry *)pos; - mef_entry->mode = mef->mef_entry[i].mode; - mef_entry->action = mef->mef_entry[i].action; - pos += sizeof(*mef_cfg->mef_entry); - - if (mwifiex_cmd_append_rpn_expression(priv, - &mef->mef_entry[i], &pos)) - return -1; - - mef_entry->exprsize = - cpu_to_le16(pos - mef_entry->expr); - } - cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN); - - return 0; -} - -/* This function parse cal data from ASCII to hex */ -static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst) -{ - u8 *s = src, *d = dst; - - while (s - src < len) { - if (*s && (isspace(*s) || *s == '\t')) { - s++; - continue; - } - if (isxdigit(*s)) { - *d++ = simple_strtol(s, NULL, 16); - s += 2; - } else { - s++; - } - } - - return d - dst; -} - -int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, - struct device_node *node, const char *prefix) -{ -#ifdef CONFIG_OF - struct property *prop; - size_t len = strlen(prefix); - int ret; - - /* look for all matching property names */ - for_each_property_of_node(node, prop) { - if (len > strlen(prop->name) || - strncmp(prop->name, prefix, len)) - continue; - - /* property header is 6 bytes, data must fit in cmd buffer */ - if (prop && prop->value && prop->length > 6 && - prop->length <= MWIFIEX_SIZE_OF_CMD_BUFFER - S_DS_GEN) { - ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, - HostCmd_ACT_GEN_SET, 0, - prop, true); - if (ret) - return ret; - } - } -#endif - return 0; -} - -/* This function prepares command of set_cfg_data. */ -static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, void *data_buf) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct property *prop = data_buf; - u32 len; - u8 *data = (u8 *)cmd + S_DS_GEN; - int ret; - - if (prop) { - len = prop->length; - ret = of_property_read_u8_array(adapter->dt_node, prop->name, - data, len); - if (ret) - return ret; - mwifiex_dbg(adapter, INFO, - "download cfg_data from device tree: %s\n", - prop->name); - } else if (adapter->cal_data->data && adapter->cal_data->size > 0) { - len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data, - adapter->cal_data->size, data); - mwifiex_dbg(adapter, INFO, - "download cfg_data from config file\n"); - } else { - return -1; - } - - cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); - cmd->size = cpu_to_le16(S_DS_GEN + len); - - return 0; -} - -static int -mwifiex_cmd_set_mc_policy(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct host_cmd_ds_multi_chan_policy *mc_pol = &cmd->params.mc_policy; - const u16 *drcs_info = data_buf; - - mc_pol->action = cpu_to_le16(cmd_action); - mc_pol->policy = cpu_to_le16(*drcs_info); - cmd->command = cpu_to_le16(HostCmd_CMD_MC_POLICY); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_multi_chan_policy) + - S_DS_GEN); - return 0; -} - -static int mwifiex_cmd_robust_coex(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, bool *is_timeshare) -{ - struct host_cmd_ds_robust_coex *coex = &cmd->params.coex; - struct mwifiex_ie_types_robust_coex *coex_tlv; - - cmd->command = cpu_to_le16(HostCmd_CMD_ROBUST_COEX); - cmd->size = cpu_to_le16(sizeof(*coex) + sizeof(*coex_tlv) + S_DS_GEN); - - coex->action = cpu_to_le16(cmd_action); - coex_tlv = (struct mwifiex_ie_types_robust_coex *) - ((u8 *)coex + sizeof(*coex)); - coex_tlv->header.type = cpu_to_le16(TLV_TYPE_ROBUST_COEX); - coex_tlv->header.len = cpu_to_le16(sizeof(coex_tlv->mode)); - - if (coex->action == HostCmd_ACT_GEN_GET) - return 0; - - if (*is_timeshare) - coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_TIMESHARE); - else - coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_SPATIAL); - - return 0; -} - -static int -mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct host_cmd_ds_coalesce_cfg *coalesce_cfg = - &cmd->params.coalesce_cfg; - struct mwifiex_ds_coalesce_cfg *cfg = data_buf; - struct coalesce_filt_field_param *param; - u16 cnt, idx, length; - struct coalesce_receive_filt_rule *rule; - - cmd->command = cpu_to_le16(HostCmd_CMD_COALESCE_CFG); - cmd->size = cpu_to_le16(S_DS_GEN); - - coalesce_cfg->action = cpu_to_le16(cmd_action); - coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules); - rule = coalesce_cfg->rule; - - for (cnt = 0; cnt < cfg->num_of_rules; cnt++) { - rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE); - rule->max_coalescing_delay = - cpu_to_le16(cfg->rule[cnt].max_coalescing_delay); - rule->pkt_type = cfg->rule[cnt].pkt_type; - rule->num_of_fields = cfg->rule[cnt].num_of_fields; - - length = 0; - - param = rule->params; - for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) { - param->operation = cfg->rule[cnt].params[idx].operation; - param->operand_len = - cfg->rule[cnt].params[idx].operand_len; - param->offset = - cpu_to_le16(cfg->rule[cnt].params[idx].offset); - memcpy(param->operand_byte_stream, - cfg->rule[cnt].params[idx].operand_byte_stream, - param->operand_len); - - length += sizeof(struct coalesce_filt_field_param); - - param++; - } - - /* Total rule length is sizeof max_coalescing_delay(u16), - * num_of_fields(u8), pkt_type(u8) and total length of the all - * params - */ - rule->header.len = cpu_to_le16(length + sizeof(u16) + - sizeof(u8) + sizeof(u8)); - - /* Add the rule length to the command size*/ - le16_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) + - sizeof(struct mwifiex_ie_types_header)); - - rule = (void *)((u8 *)rule->params + length); - } - - /* Add sizeof action, num_of_rules to total command length */ - le16_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16)); - - return 0; -} - -static int -mwifiex_cmd_tdls_config(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct host_cmd_ds_tdls_config *tdls_config = &cmd->params.tdls_config; - struct mwifiex_tdls_init_cs_params *config; - struct mwifiex_tdls_config *init_config; - u16 len; - - cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_CONFIG); - cmd->size = cpu_to_le16(S_DS_GEN); - tdls_config->tdls_action = cpu_to_le16(cmd_action); - le16_add_cpu(&cmd->size, sizeof(tdls_config->tdls_action)); - - switch (cmd_action) { - case ACT_TDLS_CS_ENABLE_CONFIG: - init_config = data_buf; - len = sizeof(*init_config); - memcpy(tdls_config->tdls_data, init_config, len); - break; - case ACT_TDLS_CS_INIT: - config = data_buf; - len = sizeof(*config); - memcpy(tdls_config->tdls_data, config, len); - break; - case ACT_TDLS_CS_STOP: - len = sizeof(struct mwifiex_tdls_stop_cs_params); - memcpy(tdls_config->tdls_data, data_buf, len); - break; - case ACT_TDLS_CS_PARAMS: - len = sizeof(struct mwifiex_tdls_config_cs_params); - memcpy(tdls_config->tdls_data, data_buf, len); - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "Unknown TDLS configuration\n"); - return -ENOTSUPP; - } - - le16_add_cpu(&cmd->size, len); - return 0; -} - -static int -mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - void *data_buf) -{ - struct host_cmd_ds_tdls_oper *tdls_oper = &cmd->params.tdls_oper; - struct mwifiex_ds_tdls_oper *oper = data_buf; - struct mwifiex_sta_node *sta_ptr; - struct host_cmd_tlv_rates *tlv_rates; - struct mwifiex_ie_types_htcap *ht_capab; - struct mwifiex_ie_types_qos_info *wmm_qos_info; - struct mwifiex_ie_types_extcap *extcap; - struct mwifiex_ie_types_vhtcap *vht_capab; - struct mwifiex_ie_types_aid *aid; - struct mwifiex_ie_types_tdls_idle_timeout *timeout; - u8 *pos, qos_info; - u16 config_len = 0; - struct station_parameters *params = priv->sta_params; - - cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_OPER); - cmd->size = cpu_to_le16(S_DS_GEN); - le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper)); - - tdls_oper->reason = 0; - memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN); - sta_ptr = mwifiex_get_sta_entry(priv, oper->peer_mac); - - pos = (u8 *)tdls_oper + sizeof(struct host_cmd_ds_tdls_oper); - - switch (oper->tdls_action) { - case MWIFIEX_TDLS_DISABLE_LINK: - tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_DELETE); - break; - case MWIFIEX_TDLS_CREATE_LINK: - tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CREATE); - break; - case MWIFIEX_TDLS_CONFIG_LINK: - tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CONFIG); - - if (!params) { - mwifiex_dbg(priv->adapter, ERROR, - "TDLS config params not available for %pM\n", - oper->peer_mac); - return -ENODATA; - } - - *(__le16 *)pos = cpu_to_le16(params->capability); - config_len += sizeof(params->capability); - - qos_info = params->uapsd_queues | (params->max_sp << 5); - wmm_qos_info = (struct mwifiex_ie_types_qos_info *)(pos + - config_len); - wmm_qos_info->header.type = cpu_to_le16(WLAN_EID_QOS_CAPA); - wmm_qos_info->header.len = cpu_to_le16(sizeof(qos_info)); - wmm_qos_info->qos_info = qos_info; - config_len += sizeof(struct mwifiex_ie_types_qos_info); - - if (params->ht_capa) { - ht_capab = (struct mwifiex_ie_types_htcap *)(pos + - config_len); - ht_capab->header.type = - cpu_to_le16(WLAN_EID_HT_CAPABILITY); - ht_capab->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_cap)); - memcpy(&ht_capab->ht_cap, params->ht_capa, - sizeof(struct ieee80211_ht_cap)); - config_len += sizeof(struct mwifiex_ie_types_htcap); - } - - if (params->supported_rates && params->supported_rates_len) { - tlv_rates = (struct host_cmd_tlv_rates *)(pos + - config_len); - tlv_rates->header.type = - cpu_to_le16(WLAN_EID_SUPP_RATES); - tlv_rates->header.len = - cpu_to_le16(params->supported_rates_len); - memcpy(tlv_rates->rates, params->supported_rates, - params->supported_rates_len); - config_len += sizeof(struct host_cmd_tlv_rates) + - params->supported_rates_len; - } - - if (params->ext_capab && params->ext_capab_len) { - extcap = (struct mwifiex_ie_types_extcap *)(pos + - config_len); - extcap->header.type = - cpu_to_le16(WLAN_EID_EXT_CAPABILITY); - extcap->header.len = cpu_to_le16(params->ext_capab_len); - memcpy(extcap->ext_capab, params->ext_capab, - params->ext_capab_len); - config_len += sizeof(struct mwifiex_ie_types_extcap) + - params->ext_capab_len; - } - if (params->vht_capa) { - vht_capab = (struct mwifiex_ie_types_vhtcap *)(pos + - config_len); - vht_capab->header.type = - cpu_to_le16(WLAN_EID_VHT_CAPABILITY); - vht_capab->header.len = - cpu_to_le16(sizeof(struct ieee80211_vht_cap)); - memcpy(&vht_capab->vht_cap, params->vht_capa, - sizeof(struct ieee80211_vht_cap)); - config_len += sizeof(struct mwifiex_ie_types_vhtcap); - } - if (params->aid) { - aid = (struct mwifiex_ie_types_aid *)(pos + config_len); - aid->header.type = cpu_to_le16(WLAN_EID_AID); - aid->header.len = cpu_to_le16(sizeof(params->aid)); - aid->aid = cpu_to_le16(params->aid); - config_len += sizeof(struct mwifiex_ie_types_aid); - } - - timeout = (void *)(pos + config_len); - timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT); - timeout->header.len = cpu_to_le16(sizeof(timeout->value)); - timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC); - config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout); - - break; - default: - mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS operation\n"); - return -ENOTSUPP; - } - - le16_add_cpu(&cmd->size, config_len); - - return 0; -} - -/* This function prepares command of sdio rx aggr info. */ -static int mwifiex_cmd_sdio_rx_aggr_cfg(struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = - &cmd->params.sdio_rx_aggr_cfg; - - cmd->command = cpu_to_le16(HostCmd_CMD_SDIO_SP_RX_AGGR_CFG); - cmd->size = - cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) + - S_DS_GEN); - cfg->action = cmd_action; - if (cmd_action == HostCmd_ACT_GEN_SET) - cfg->enable = *(u8 *)data_buf; - - return 0; -} - -/* - * This function prepares the commands before sending them to the firmware. - * - * This is a generic function which calls specific command preparation - * routines based upon the command number. - */ -int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, - u16 cmd_action, u32 cmd_oid, - void *data_buf, void *cmd_buf) -{ - struct host_cmd_ds_command *cmd_ptr = cmd_buf; - int ret = 0; - - /* Prepare command */ - switch (cmd_no) { - case HostCmd_CMD_GET_HW_SPEC: - ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr); - break; - case HostCmd_CMD_CFG_DATA: - ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_MAC_CONTROL: - ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_802_11_MAC_ADDRESS: - ret = mwifiex_cmd_802_11_mac_address(priv, cmd_ptr, - cmd_action); - break; - case HostCmd_CMD_MAC_MULTICAST_ADR: - ret = mwifiex_cmd_mac_multicast_adr(cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_TX_RATE_CFG: - ret = mwifiex_cmd_tx_rate_cfg(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_TXPWR_CFG: - ret = mwifiex_cmd_tx_power_cfg(cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_RF_TX_PWR: - ret = mwifiex_cmd_rf_tx_power(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_RF_ANTENNA: - ret = mwifiex_cmd_rf_antenna(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_802_11_PS_MODE_ENH: - ret = mwifiex_cmd_enh_power_mode(priv, cmd_ptr, cmd_action, - (uint16_t)cmd_oid, data_buf); - break; - case HostCmd_CMD_802_11_HS_CFG_ENH: - ret = mwifiex_cmd_802_11_hs_cfg(priv, cmd_ptr, cmd_action, - (struct mwifiex_hs_config_param *) data_buf); - break; - case HostCmd_CMD_802_11_SCAN: - ret = mwifiex_cmd_802_11_scan(cmd_ptr, data_buf); - break; - case HostCmd_CMD_802_11_BG_SCAN_QUERY: - ret = mwifiex_cmd_802_11_bg_scan_query(cmd_ptr); - break; - case HostCmd_CMD_802_11_ASSOCIATE: - ret = mwifiex_cmd_802_11_associate(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_802_11_DEAUTHENTICATE: - ret = mwifiex_cmd_802_11_deauthenticate(priv, cmd_ptr, - data_buf); - break; - case HostCmd_CMD_802_11_AD_HOC_START: - ret = mwifiex_cmd_802_11_ad_hoc_start(priv, cmd_ptr, - data_buf); - break; - case HostCmd_CMD_802_11_GET_LOG: - ret = mwifiex_cmd_802_11_get_log(cmd_ptr); - break; - case HostCmd_CMD_802_11_AD_HOC_JOIN: - ret = mwifiex_cmd_802_11_ad_hoc_join(priv, cmd_ptr, - data_buf); - break; - case HostCmd_CMD_802_11_AD_HOC_STOP: - ret = mwifiex_cmd_802_11_ad_hoc_stop(cmd_ptr); - break; - case HostCmd_CMD_RSSI_INFO: - ret = mwifiex_cmd_802_11_rssi_info(priv, cmd_ptr, cmd_action); - break; - case HostCmd_CMD_802_11_SNMP_MIB: - ret = mwifiex_cmd_802_11_snmp_mib(priv, cmd_ptr, cmd_action, - cmd_oid, data_buf); - break; - case HostCmd_CMD_802_11_TX_RATE_QUERY: - cmd_ptr->command = - cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) + - S_DS_GEN); - priv->tx_rate = 0; - ret = 0; - break; - case HostCmd_CMD_VERSION_EXT: - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->params.verext.version_str_sel = - (u8) (*((u32 *) data_buf)); - memcpy(&cmd_ptr->params, data_buf, - sizeof(struct host_cmd_ds_version_ext)); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_version_ext) + - S_DS_GEN); - ret = 0; - break; - case HostCmd_CMD_MGMT_FRAME_REG: - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->params.reg_mask.action = cpu_to_le16(cmd_action); - cmd_ptr->params.reg_mask.mask = cpu_to_le32(*(u32 *)data_buf); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_mgmt_frame_reg) + - S_DS_GEN); - ret = 0; - break; - case HostCmd_CMD_REMAIN_ON_CHAN: - cmd_ptr->command = cpu_to_le16(cmd_no); - memcpy(&cmd_ptr->params, data_buf, - sizeof(struct host_cmd_ds_remain_on_chan)); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_remain_on_chan) + - S_DS_GEN); - break; - case HostCmd_CMD_11AC_CFG: - ret = mwifiex_cmd_11ac_cfg(priv, cmd_ptr, cmd_action, data_buf); - break; - case HostCmd_CMD_P2P_MODE_CFG: - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->params.mode_cfg.action = cpu_to_le16(cmd_action); - cmd_ptr->params.mode_cfg.mode = cpu_to_le16(*(u16 *)data_buf); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_p2p_mode_cfg) + - S_DS_GEN); - break; - case HostCmd_CMD_FUNC_INIT: - if (priv->adapter->hw_status == MWIFIEX_HW_STATUS_RESET) - priv->adapter->hw_status = MWIFIEX_HW_STATUS_READY; - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->size = cpu_to_le16(S_DS_GEN); - break; - case HostCmd_CMD_FUNC_SHUTDOWN: - priv->adapter->hw_status = MWIFIEX_HW_STATUS_RESET; - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->size = cpu_to_le16(S_DS_GEN); - break; - case HostCmd_CMD_11N_ADDBA_REQ: - ret = mwifiex_cmd_11n_addba_req(cmd_ptr, data_buf); - break; - case HostCmd_CMD_11N_DELBA: - ret = mwifiex_cmd_11n_delba(cmd_ptr, data_buf); - break; - case HostCmd_CMD_11N_ADDBA_RSP: - ret = mwifiex_cmd_11n_addba_rsp_gen(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_802_11_KEY_MATERIAL: - ret = mwifiex_cmd_802_11_key_material(priv, cmd_ptr, - cmd_action, cmd_oid, - data_buf); - break; - case HostCmd_CMD_802_11D_DOMAIN_INFO: - ret = mwifiex_cmd_802_11d_domain_info(priv, cmd_ptr, - cmd_action); - break; - case HostCmd_CMD_RECONFIGURE_TX_BUFF: - ret = mwifiex_cmd_recfg_tx_buf(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_AMSDU_AGGR_CTRL: - ret = mwifiex_cmd_amsdu_aggr_ctrl(cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_11N_CFG: - ret = mwifiex_cmd_11n_cfg(priv, cmd_ptr, cmd_action, data_buf); - break; - case HostCmd_CMD_WMM_GET_STATUS: - mwifiex_dbg(priv->adapter, CMD, - "cmd: WMM: WMM_GET_STATUS cmd sent\n"); - cmd_ptr->command = cpu_to_le16(HostCmd_CMD_WMM_GET_STATUS); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_wmm_get_status) + - S_DS_GEN); - ret = 0; - break; - case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: - ret = mwifiex_cmd_ibss_coalescing_status(cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_802_11_SCAN_EXT: - ret = mwifiex_cmd_802_11_scan_ext(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_MEM_ACCESS: - ret = mwifiex_cmd_mem_access(cmd_ptr, cmd_action, data_buf); - break; - case HostCmd_CMD_MAC_REG_ACCESS: - case HostCmd_CMD_BBP_REG_ACCESS: - case HostCmd_CMD_RF_REG_ACCESS: - case HostCmd_CMD_PMIC_REG_ACCESS: - case HostCmd_CMD_CAU_REG_ACCESS: - case HostCmd_CMD_802_11_EEPROM_ACCESS: - ret = mwifiex_cmd_reg_access(cmd_ptr, cmd_action, data_buf); - break; - case HostCmd_CMD_SET_BSS_MODE: - cmd_ptr->command = cpu_to_le16(cmd_no); - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) - cmd_ptr->params.bss_mode.con_type = - CONNECTION_TYPE_ADHOC; - else if (priv->bss_mode == NL80211_IFTYPE_STATION || - priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) - cmd_ptr->params.bss_mode.con_type = - CONNECTION_TYPE_INFRA; - else if (priv->bss_mode == NL80211_IFTYPE_AP || - priv->bss_mode == NL80211_IFTYPE_P2P_GO) - cmd_ptr->params.bss_mode.con_type = CONNECTION_TYPE_AP; - cmd_ptr->size = cpu_to_le16(sizeof(struct - host_cmd_ds_set_bss_mode) + S_DS_GEN); - ret = 0; - break; - case HostCmd_CMD_PCIE_DESC_DETAILS: - ret = mwifiex_cmd_pcie_host_spec(priv, cmd_ptr, cmd_action); - break; - case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: - ret = mwifiex_cmd_802_11_subsc_evt(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_MEF_CFG: - ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_COALESCE_CFG: - ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_TDLS_OPER: - ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_TDLS_CONFIG: - ret = mwifiex_cmd_tdls_config(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_CHAN_REPORT_REQUEST: - ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr, - data_buf); - break; - case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: - ret = mwifiex_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_MC_POLICY: - ret = mwifiex_cmd_set_mc_policy(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_ROBUST_COEX: - ret = mwifiex_cmd_robust_coex(priv, cmd_ptr, cmd_action, - data_buf); - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "PREP_CMD: unknown cmd- %#x\n", cmd_no); - ret = -1; - break; - } - return ret; -} - -/* - * This function issues commands to initialize firmware. - * - * This is called after firmware download to bring the card to - * working state. - * Function is also called during reinitialization of virtual - * interfaces. - * - * The following commands are issued sequentially - - * - Set PCI-Express host buffer configuration (PCIE only) - * - Function init (for first interface only) - * - Read MAC address (for first interface only) - * - Reconfigure Tx buffer size (for first interface only) - * - Enable auto deep sleep (for first interface only) - * - Get Tx rate - * - Get Tx power - * - Set IBSS coalescing status - * - Set AMSDU aggregation control - * - Set 11d control - * - Set MAC control (this must be the last command to initialize firmware) - */ -int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - u16 enable = true; - struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; - struct mwifiex_ds_auto_ds auto_ds; - enum state_11d_t state_11d; - struct mwifiex_ds_11n_tx_cfg tx_cfg; - u8 sdio_sp_rx_aggr_enable; - - if (first_sta) { - if (priv->adapter->iface_type == MWIFIEX_PCIE) { - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_PCIE_DESC_DETAILS, - HostCmd_ACT_GEN_SET, 0, NULL, - true); - if (ret) - return -1; - } - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_FUNC_INIT, - HostCmd_ACT_GEN_SET, 0, NULL, true); - if (ret) - return -1; - - /* Download calibration data to firmware. - * The cal-data can be read from device tree and/or - * a configuration file and downloaded to firmware. - */ - adapter->dt_node = - of_find_node_by_name(NULL, "marvell_cfgdata"); - if (adapter->dt_node) { - ret = mwifiex_dnld_dt_cfgdata(priv, adapter->dt_node, - "marvell,caldata"); - if (ret) - return -1; - } - - if (adapter->cal_data) { - ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, - HostCmd_ACT_GEN_SET, 0, NULL, - true); - if (ret) - return -1; - } - - /* Read MAC address from HW */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_GET_HW_SPEC, - HostCmd_ACT_GEN_GET, 0, NULL, true); - if (ret) - return -1; - - /** Set SDIO Single Port RX Aggr Info */ - if (priv->adapter->iface_type == MWIFIEX_SDIO && - ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) && - !priv->adapter->host_disable_sdio_rx_aggr) { - sdio_sp_rx_aggr_enable = true; - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_SDIO_SP_RX_AGGR_CFG, - HostCmd_ACT_GEN_SET, 0, - &sdio_sp_rx_aggr_enable, - true); - if (ret) { - mwifiex_dbg(priv->adapter, ERROR, - "error while enabling SP aggregation..disable it"); - adapter->sdio_rx_aggr_enable = false; - } - } - - /* Reconfigure tx buf size */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, - HostCmd_ACT_GEN_SET, 0, - &priv->adapter->tx_buf_size, true); - if (ret) - return -1; - - if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { - /* Enable IEEE PS by default */ - priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_802_11_PS_MODE_ENH, - EN_AUTO_PS, BITMAP_STA_PS, NULL, - true); - if (ret) - return -1; - } - - if (drcs) { - adapter->drcs_enabled = true; - if (ISSUPP_DRCS_ENABLED(adapter->fw_cap_info)) - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_MC_POLICY, - HostCmd_ACT_GEN_SET, 0, - &adapter->drcs_enabled, - true); - if (ret) - return -1; - } - } - - /* get tx rate */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, - HostCmd_ACT_GEN_GET, 0, NULL, true); - if (ret) - return -1; - priv->data_rate = 0; - - /* get tx power */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_RF_TX_PWR, - HostCmd_ACT_GEN_GET, 0, NULL, true); - if (ret) - return -1; - - if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) { - /* set ibss coalescing_status */ - ret = mwifiex_send_cmd( - priv, - HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, - HostCmd_ACT_GEN_SET, 0, &enable, true); - if (ret) - return -1; - } - - memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); - amsdu_aggr_ctrl.enable = true; - /* Send request to firmware */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_AMSDU_AGGR_CTRL, - HostCmd_ACT_GEN_SET, 0, - &amsdu_aggr_ctrl, true); - if (ret) - return -1; - /* MAC Control must be the last command in init_fw */ - /* set MAC Control */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter, true); - if (ret) - return -1; - - if (!disable_auto_ds && - first_sta && priv->adapter->iface_type != MWIFIEX_USB && - priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { - /* Enable auto deep sleep */ - auto_ds.auto_ds = DEEP_SLEEP_ON; - auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, - EN_AUTO_PS, BITMAP_AUTO_DS, - &auto_ds, true); - if (ret) - return -1; - } - - if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { - /* Send cmd to FW to enable/disable 11D function */ - state_11d = ENABLE_11D; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, DOT11D_I, - &state_11d, true); - if (ret) - mwifiex_dbg(priv->adapter, ERROR, - "11D: failed to enable 11D\n"); - } - - /* Send cmd to FW to configure 11n specific configuration - * (Short GI, Channel BW, Green field support etc.) for transmit - */ - tx_cfg.tx_htcap = MWIFIEX_FW_DEF_HTTXCFG; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_CFG, - HostCmd_ACT_GEN_SET, 0, &tx_cfg, true); - - if (init) { - /* set last_init_cmd before sending the command */ - priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG; - ret = -EINPROGRESS; - } - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c deleted file mode 100644 index 9ac7aa2431b4..000000000000 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ /dev/null @@ -1,1249 +0,0 @@ -/* - * Marvell Wireless LAN device driver: station command response handling - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11ac.h" - - -/* - * This function handles the command response error case. - * - * For scan response error, the function cancels all the pending - * scan commands and generates an event to inform the applications - * of the scan completion. - * - * For Power Save command failure, we do not retry enter PS - * command in case of Ad-hoc mode. - * - * For all other response errors, the current command buffer is freed - * and returned to the free command queue. - */ -static void -mwifiex_process_cmdresp_error(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_ps_mode_enh *pm; - unsigned long flags; - - mwifiex_dbg(adapter, ERROR, - "CMD_RESP: cmd %#x error, result=%#x\n", - resp->command, resp->result); - - if (adapter->curr_cmd->wait_q_enabled) - adapter->cmd_wait_q.status = -1; - - switch (le16_to_cpu(resp->command)) { - case HostCmd_CMD_802_11_PS_MODE_ENH: - pm = &resp->params.psmode_enh; - mwifiex_dbg(adapter, ERROR, - "PS_MODE_ENH cmd failed: result=0x%x action=0x%X\n", - resp->result, le16_to_cpu(pm->action)); - /* We do not re-try enter-ps command in ad-hoc mode. */ - if (le16_to_cpu(pm->action) == EN_AUTO_PS && - (le16_to_cpu(pm->params.ps_bitmap) & BITMAP_STA_PS) && - priv->bss_mode == NL80211_IFTYPE_ADHOC) - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; - - break; - case HostCmd_CMD_802_11_SCAN: - case HostCmd_CMD_802_11_SCAN_EXT: - /* Cancel all pending scan command */ - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, list) { - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - break; - - case HostCmd_CMD_MAC_CONTROL: - break; - - case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: - mwifiex_dbg(adapter, MSG, - "SDIO RX single-port aggregation Not support\n"); - break; - - default: - break; - } - /* Handling errors here */ - mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); -} - -/* - * This function handles the command response of get RSSI info. - * - * Handling includes changing the header fields into CPU format - * and saving the following parameters in driver - - * - Last data and beacon RSSI value - * - Average data and beacon RSSI value - * - Last data and beacon NF value - * - Average data and beacon NF value - * - * The parameters are send to the application as well, along with - * calculated SNR values. - */ -static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp = - &resp->params.rssi_info_rsp; - struct mwifiex_ds_misc_subsc_evt *subsc_evt = - &priv->async_subsc_evt_storage; - - priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last); - priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last); - - priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg); - priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg); - - priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last); - priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last); - - priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg); - priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg); - - if (priv->subsc_evt_rssi_state == EVENT_HANDLED) - return 0; - - memset(subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); - - /* Resubscribe low and high rssi events with new thresholds */ - subsc_evt->events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; - subsc_evt->action = HostCmd_ACT_BITWISE_SET; - if (priv->subsc_evt_rssi_state == RSSI_LOW_RECVD) { - subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg - - priv->cqm_rssi_hyst); - subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); - } else if (priv->subsc_evt_rssi_state == RSSI_HIGH_RECVD) { - subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); - subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg + - priv->cqm_rssi_hyst); - } - subsc_evt->bcn_l_rssi_cfg.evt_freq = 1; - subsc_evt->bcn_h_rssi_cfg.evt_freq = 1; - - priv->subsc_evt_rssi_state = EVENT_HANDLED; - - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT, - 0, 0, subsc_evt, false); - - return 0; -} - -/* - * This function handles the command response of set/get SNMP - * MIB parameters. - * - * Handling includes changing the header fields into CPU format - * and saving the parameter in driver. - * - * The following parameters are supported - - * - Fragmentation threshold - * - RTS threshold - * - Short retry limit - */ -static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - u32 *data_buf) -{ - struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; - u16 oid = le16_to_cpu(smib->oid); - u16 query_type = le16_to_cpu(smib->query_type); - u32 ul_temp; - - mwifiex_dbg(priv->adapter, INFO, - "info: SNMP_RESP: oid value = %#x,\t" - "query_type = %#x, buf size = %#x\n", - oid, query_type, le16_to_cpu(smib->buf_size)); - if (query_type == HostCmd_ACT_GEN_GET) { - ul_temp = le16_to_cpu(*((__le16 *) (smib->value))); - if (data_buf) - *data_buf = ul_temp; - switch (oid) { - case FRAG_THRESH_I: - mwifiex_dbg(priv->adapter, INFO, - "info: SNMP_RESP: FragThsd =%u\n", - ul_temp); - break; - case RTS_THRESH_I: - mwifiex_dbg(priv->adapter, INFO, - "info: SNMP_RESP: RTSThsd =%u\n", - ul_temp); - break; - case SHORT_RETRY_LIM_I: - mwifiex_dbg(priv->adapter, INFO, - "info: SNMP_RESP: TxRetryCount=%u\n", - ul_temp); - break; - case DTIM_PERIOD_I: - mwifiex_dbg(priv->adapter, INFO, - "info: SNMP_RESP: DTIM period=%u\n", - ul_temp); - default: - break; - } - } - - return 0; -} - -/* - * This function handles the command response of get log request - * - * Handling includes changing the header fields into CPU format - * and sending the received parameters to application. - */ -static int mwifiex_ret_get_log(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - struct mwifiex_ds_get_stats *stats) -{ - struct host_cmd_ds_802_11_get_log *get_log = - &resp->params.get_log; - - if (stats) { - stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame); - stats->failed = le32_to_cpu(get_log->failed); - stats->retry = le32_to_cpu(get_log->retry); - stats->multi_retry = le32_to_cpu(get_log->multi_retry); - stats->frame_dup = le32_to_cpu(get_log->frame_dup); - stats->rts_success = le32_to_cpu(get_log->rts_success); - stats->rts_failure = le32_to_cpu(get_log->rts_failure); - stats->ack_failure = le32_to_cpu(get_log->ack_failure); - stats->rx_frag = le32_to_cpu(get_log->rx_frag); - stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame); - stats->fcs_error = le32_to_cpu(get_log->fcs_error); - stats->tx_frame = le32_to_cpu(get_log->tx_frame); - stats->wep_icv_error[0] = - le32_to_cpu(get_log->wep_icv_err_cnt[0]); - stats->wep_icv_error[1] = - le32_to_cpu(get_log->wep_icv_err_cnt[1]); - stats->wep_icv_error[2] = - le32_to_cpu(get_log->wep_icv_err_cnt[2]); - stats->wep_icv_error[3] = - le32_to_cpu(get_log->wep_icv_err_cnt[3]); - stats->bcn_rcv_cnt = le32_to_cpu(get_log->bcn_rcv_cnt); - stats->bcn_miss_cnt = le32_to_cpu(get_log->bcn_miss_cnt); - } - - return 0; -} - -/* - * This function handles the command response of set/get Tx rate - * configurations. - * - * Handling includes changing the header fields into CPU format - * and saving the following parameters in driver - - * - DSSS rate bitmap - * - OFDM rate bitmap - * - HT MCS rate bitmaps - * - * Based on the new rate bitmaps, the function re-evaluates if - * auto data rate has been activated. If not, it sends another - * query to the firmware to get the current Tx data rate. - */ -static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg; - struct mwifiex_rate_scope *rate_scope; - struct mwifiex_ie_types_header *head; - u16 tlv, tlv_buf_len, tlv_buf_left; - u8 *tlv_buf; - u32 i; - - tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg); - tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*rate_cfg); - - while (tlv_buf_left >= sizeof(*head)) { - head = (struct mwifiex_ie_types_header *)tlv_buf; - tlv = le16_to_cpu(head->type); - tlv_buf_len = le16_to_cpu(head->len); - - if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) - break; - - switch (tlv) { - case TLV_TYPE_RATE_SCOPE: - rate_scope = (struct mwifiex_rate_scope *) tlv_buf; - priv->bitmap_rates[0] = - le16_to_cpu(rate_scope->hr_dsss_rate_bitmap); - priv->bitmap_rates[1] = - le16_to_cpu(rate_scope->ofdm_rate_bitmap); - for (i = 0; - i < - sizeof(rate_scope->ht_mcs_rate_bitmap) / - sizeof(u16); i++) - priv->bitmap_rates[2 + i] = - le16_to_cpu(rate_scope-> - ht_mcs_rate_bitmap[i]); - - if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { - for (i = 0; i < ARRAY_SIZE(rate_scope-> - vht_mcs_rate_bitmap); - i++) - priv->bitmap_rates[10 + i] = - le16_to_cpu(rate_scope-> - vht_mcs_rate_bitmap[i]); - } - break; - /* Add RATE_DROP tlv here */ - } - - tlv_buf += (sizeof(*head) + tlv_buf_len); - tlv_buf_left -= (sizeof(*head) + tlv_buf_len); - } - - priv->is_data_rate_auto = mwifiex_is_rate_auto(priv); - - if (priv->is_data_rate_auto) - priv->data_rate = 0; - else - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, - HostCmd_ACT_GEN_GET, 0, NULL, false); - - return 0; -} - -/* - * This function handles the command response of get Tx power level. - * - * Handling includes saving the maximum and minimum Tx power levels - * in driver, as well as sending the values to user. - */ -static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf) -{ - int length, max_power = -1, min_power = -1; - struct mwifiex_types_power_group *pg_tlv_hdr; - struct mwifiex_power_group *pg; - - if (!data_buf) - return -1; - - pg_tlv_hdr = (struct mwifiex_types_power_group *)((u8 *)data_buf); - pg = (struct mwifiex_power_group *) - ((u8 *) pg_tlv_hdr + sizeof(struct mwifiex_types_power_group)); - length = le16_to_cpu(pg_tlv_hdr->length); - - /* At least one structure required to update power */ - if (length < sizeof(struct mwifiex_power_group)) - return 0; - - max_power = pg->power_max; - min_power = pg->power_min; - length -= sizeof(struct mwifiex_power_group); - - while (length >= sizeof(struct mwifiex_power_group)) { - pg++; - if (max_power < pg->power_max) - max_power = pg->power_max; - - if (min_power > pg->power_min) - min_power = pg->power_min; - - length -= sizeof(struct mwifiex_power_group); - } - priv->min_tx_power_level = (u8) min_power; - priv->max_tx_power_level = (u8) max_power; - - return 0; -} - -/* - * This function handles the command response of set/get Tx power - * configurations. - * - * Handling includes changing the header fields into CPU format - * and saving the current Tx power level in driver. - */ -static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg; - struct mwifiex_types_power_group *pg_tlv_hdr; - struct mwifiex_power_group *pg; - u16 action = le16_to_cpu(txp_cfg->action); - u16 tlv_buf_left; - - pg_tlv_hdr = (struct mwifiex_types_power_group *) - ((u8 *)txp_cfg + - sizeof(struct host_cmd_ds_txpwr_cfg)); - - pg = (struct mwifiex_power_group *) - ((u8 *)pg_tlv_hdr + - sizeof(struct mwifiex_types_power_group)); - - tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*txp_cfg); - if (tlv_buf_left < - le16_to_cpu(pg_tlv_hdr->length) + sizeof(*pg_tlv_hdr)) - return 0; - - switch (action) { - case HostCmd_ACT_GEN_GET: - if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) - mwifiex_get_power_level(priv, pg_tlv_hdr); - - priv->tx_power_level = (u16) pg->power_min; - break; - - case HostCmd_ACT_GEN_SET: - if (!le32_to_cpu(txp_cfg->mode)) - break; - - if (pg->power_max == pg->power_min) - priv->tx_power_level = (u16) pg->power_min; - break; - default: - mwifiex_dbg(adapter, ERROR, - "CMD_RESP: unknown cmd action %d\n", - action); - return 0; - } - mwifiex_dbg(adapter, INFO, - "info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n", - priv->tx_power_level, priv->max_tx_power_level, - priv->min_tx_power_level); - - return 0; -} - -/* - * This function handles the command response of get RF Tx power. - */ -static int mwifiex_ret_rf_tx_power(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_rf_tx_pwr *txp = &resp->params.txp; - u16 action = le16_to_cpu(txp->action); - - priv->tx_power_level = le16_to_cpu(txp->cur_level); - - if (action == HostCmd_ACT_GEN_GET) { - priv->max_tx_power_level = txp->max_power; - priv->min_tx_power_level = txp->min_power; - } - - mwifiex_dbg(priv->adapter, INFO, - "Current TxPower Level=%d, Max Power=%d, Min Power=%d\n", - priv->tx_power_level, priv->max_tx_power_level, - priv->min_tx_power_level); - - return 0; -} - -/* - * This function handles the command response of set rf antenna - */ -static int mwifiex_ret_rf_antenna(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_rf_ant_mimo *ant_mimo = &resp->params.ant_mimo; - struct host_cmd_ds_rf_ant_siso *ant_siso = &resp->params.ant_siso; - struct mwifiex_adapter *adapter = priv->adapter; - - if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) - mwifiex_dbg(adapter, INFO, - "RF_ANT_RESP: Tx action = 0x%x, Tx Mode = 0x%04x\t" - "Rx action = 0x%x, Rx Mode = 0x%04x\n", - le16_to_cpu(ant_mimo->action_tx), - le16_to_cpu(ant_mimo->tx_ant_mode), - le16_to_cpu(ant_mimo->action_rx), - le16_to_cpu(ant_mimo->rx_ant_mode)); - else - mwifiex_dbg(adapter, INFO, - "RF_ANT_RESP: action = 0x%x, Mode = 0x%04x\n", - le16_to_cpu(ant_siso->action), - le16_to_cpu(ant_siso->ant_mode)); - - return 0; -} - -/* - * This function handles the command response of set/get MAC address. - * - * Handling includes saving the MAC address in driver. - */ -static int mwifiex_ret_802_11_mac_address(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_mac_address *cmd_mac_addr = - &resp->params.mac_addr; - - memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN); - - mwifiex_dbg(priv->adapter, INFO, - "info: set mac address: %pM\n", priv->curr_addr); - - return 0; -} - -/* - * This function handles the command response of set/get MAC multicast - * address. - */ -static int mwifiex_ret_mac_multicast_adr(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - return 0; -} - -/* - * This function handles the command response of get Tx rate query. - * - * Handling includes changing the header fields into CPU format - * and saving the Tx rate and HT information parameters in driver. - * - * Both rate configuration and current data rate can be retrieved - * with this request. - */ -static int mwifiex_ret_802_11_tx_rate_query(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - priv->tx_rate = resp->params.tx_rate.tx_rate; - priv->tx_htinfo = resp->params.tx_rate.ht_info; - if (!priv->is_data_rate_auto) - priv->data_rate = - mwifiex_index_to_data_rate(priv, priv->tx_rate, - priv->tx_htinfo); - - return 0; -} - -/* - * This function handles the command response of a deauthenticate - * command. - * - * If the deauthenticated MAC matches the current BSS MAC, the connection - * state is reset. - */ -static int mwifiex_ret_802_11_deauthenticate(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - adapter->dbg.num_cmd_deauth++; - if (!memcmp(resp->params.deauth.mac_addr, - &priv->curr_bss_params.bss_descriptor.mac_address, - sizeof(resp->params.deauth.mac_addr))) - mwifiex_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING); - - return 0; -} - -/* - * This function handles the command response of ad-hoc stop. - * - * The function resets the connection state in driver. - */ -static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - mwifiex_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING); - return 0; -} - -/* - * This function handles the command response of set/get v1 key material. - * - * Handling includes updating the driver parameters to reflect the - * changes. - */ -static int mwifiex_ret_802_11_key_material_v1(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_key_material *key = - &resp->params.key_material; - - if (le16_to_cpu(key->action) == HostCmd_ACT_GEN_SET) { - if ((le16_to_cpu(key->key_param_set.key_info) & KEY_MCAST)) { - mwifiex_dbg(priv->adapter, INFO, - "info: key: GTK is set\n"); - priv->wpa_is_gtk_set = true; - priv->scan_block = false; - priv->port_open = true; - } - } - - memset(priv->aes_key.key_param_set.key, 0, - sizeof(key->key_param_set.key)); - priv->aes_key.key_param_set.key_len = key->key_param_set.key_len; - memcpy(priv->aes_key.key_param_set.key, key->key_param_set.key, - le16_to_cpu(priv->aes_key.key_param_set.key_len)); - - return 0; -} - -/* - * This function handles the command response of set/get v2 key material. - * - * Handling includes updating the driver parameters to reflect the - * changes. - */ -static int mwifiex_ret_802_11_key_material_v2(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_key_material_v2 *key_v2; - __le16 len; - - key_v2 = &resp->params.key_material_v2; - if (le16_to_cpu(key_v2->action) == HostCmd_ACT_GEN_SET) { - if ((le16_to_cpu(key_v2->key_param_set.key_info) & KEY_MCAST)) { - mwifiex_dbg(priv->adapter, INFO, "info: key: GTK is set\n"); - priv->wpa_is_gtk_set = true; - priv->scan_block = false; - priv->port_open = true; - } - } - - if (key_v2->key_param_set.key_type != KEY_TYPE_ID_AES) - return 0; - - memset(priv->aes_key_v2.key_param_set.key_params.aes.key, 0, - WLAN_KEY_LEN_CCMP); - priv->aes_key_v2.key_param_set.key_params.aes.key_len = - key_v2->key_param_set.key_params.aes.key_len; - len = priv->aes_key_v2.key_param_set.key_params.aes.key_len; - memcpy(priv->aes_key_v2.key_param_set.key_params.aes.key, - key_v2->key_param_set.key_params.aes.key, le16_to_cpu(len)); - - return 0; -} - -/* Wrapper function for processing response of key material command */ -static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - if (priv->adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) - return mwifiex_ret_802_11_key_material_v2(priv, resp); - else - return mwifiex_ret_802_11_key_material_v1(priv, resp); -} - -/* - * This function handles the command response of get 11d domain information. - */ -static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11d_domain_info_rsp *domain_info = - &resp->params.domain_info_resp; - struct mwifiex_ietypes_domain_param_set *domain = &domain_info->domain; - u16 action = le16_to_cpu(domain_info->action); - u8 no_of_triplet; - - no_of_triplet = (u8) ((le16_to_cpu(domain->header.len) - - IEEE80211_COUNTRY_STRING_LEN) - / sizeof(struct ieee80211_country_ie_triplet)); - - mwifiex_dbg(priv->adapter, INFO, - "info: 11D Domain Info Resp: no_of_triplet=%d\n", - no_of_triplet); - - if (no_of_triplet > MWIFIEX_MAX_TRIPLET_802_11D) { - mwifiex_dbg(priv->adapter, FATAL, - "11D: invalid number of triplets %d returned\n", - no_of_triplet); - return -1; - } - - switch (action) { - case HostCmd_ACT_GEN_SET: /* Proc Set Action */ - break; - case HostCmd_ACT_GEN_GET: - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "11D: invalid action:%d\n", domain_info->action); - return -1; - } - - return 0; -} - -/* - * This function handles the command response of get extended version. - * - * Handling includes forming the extended version string and sending it - * to application. - */ -static int mwifiex_ret_ver_ext(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - struct host_cmd_ds_version_ext *version_ext) -{ - struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext; - - if (version_ext) { - version_ext->version_str_sel = ver_ext->version_str_sel; - memcpy(version_ext->version_str, ver_ext->version_str, - sizeof(char) * 128); - memcpy(priv->version_str, ver_ext->version_str, 128); - } - return 0; -} - -/* - * This function handles the command response of remain on channel. - */ -static int -mwifiex_ret_remain_on_chan(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - struct host_cmd_ds_remain_on_chan *roc_cfg) -{ - struct host_cmd_ds_remain_on_chan *resp_cfg = &resp->params.roc_cfg; - - if (roc_cfg) - memcpy(roc_cfg, resp_cfg, sizeof(*roc_cfg)); - - return 0; -} - -/* - * This function handles the command response of P2P mode cfg. - */ -static int -mwifiex_ret_p2p_mode_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - void *data_buf) -{ - struct host_cmd_ds_p2p_mode_cfg *mode_cfg = &resp->params.mode_cfg; - - if (data_buf) - *((u16 *)data_buf) = le16_to_cpu(mode_cfg->mode); - - return 0; -} - -/* This function handles the command response of mem_access command - */ -static int -mwifiex_ret_mem_access(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, void *pioctl_buf) -{ - struct host_cmd_ds_mem_access *mem = (void *)&resp->params.mem; - - priv->mem_rw.addr = le32_to_cpu(mem->addr); - priv->mem_rw.value = le32_to_cpu(mem->value); - - return 0; -} -/* - * This function handles the command response of register access. - * - * The register value and offset are returned to the user. For EEPROM - * access, the byte count is also returned. - */ -static int mwifiex_ret_reg_access(u16 type, struct host_cmd_ds_command *resp, - void *data_buf) -{ - struct mwifiex_ds_reg_rw *reg_rw; - struct mwifiex_ds_read_eeprom *eeprom; - union reg { - struct host_cmd_ds_mac_reg_access *mac; - struct host_cmd_ds_bbp_reg_access *bbp; - struct host_cmd_ds_rf_reg_access *rf; - struct host_cmd_ds_pmic_reg_access *pmic; - struct host_cmd_ds_802_11_eeprom_access *eeprom; - } r; - - if (!data_buf) - return 0; - - reg_rw = data_buf; - eeprom = data_buf; - switch (type) { - case HostCmd_CMD_MAC_REG_ACCESS: - r.mac = &resp->params.mac_reg; - reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.mac->offset)); - reg_rw->value = r.mac->value; - break; - case HostCmd_CMD_BBP_REG_ACCESS: - r.bbp = &resp->params.bbp_reg; - reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.bbp->offset)); - reg_rw->value = cpu_to_le32((u32) r.bbp->value); - break; - - case HostCmd_CMD_RF_REG_ACCESS: - r.rf = &resp->params.rf_reg; - reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); - reg_rw->value = cpu_to_le32((u32) r.bbp->value); - break; - case HostCmd_CMD_PMIC_REG_ACCESS: - r.pmic = &resp->params.pmic_reg; - reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.pmic->offset)); - reg_rw->value = cpu_to_le32((u32) r.pmic->value); - break; - case HostCmd_CMD_CAU_REG_ACCESS: - r.rf = &resp->params.rf_reg; - reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); - reg_rw->value = cpu_to_le32((u32) r.rf->value); - break; - case HostCmd_CMD_802_11_EEPROM_ACCESS: - r.eeprom = &resp->params.eeprom; - pr_debug("info: EEPROM read len=%x\n", r.eeprom->byte_count); - if (le16_to_cpu(eeprom->byte_count) < - le16_to_cpu(r.eeprom->byte_count)) { - eeprom->byte_count = cpu_to_le16(0); - pr_debug("info: EEPROM read length is too big\n"); - return -1; - } - eeprom->offset = r.eeprom->offset; - eeprom->byte_count = r.eeprom->byte_count; - if (le16_to_cpu(eeprom->byte_count) > 0) - memcpy(&eeprom->value, &r.eeprom->value, - le16_to_cpu(r.eeprom->byte_count)); - - break; - default: - return -1; - } - return 0; -} - -/* - * This function handles the command response of get IBSS coalescing status. - * - * If the received BSSID is different than the current one, the current BSSID, - * beacon interval, ATIM window and ERP information are updated, along with - * changing the ad-hoc state accordingly. - */ -static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_ibss_status *ibss_coal_resp = - &(resp->params.ibss_coalescing); - - if (le16_to_cpu(ibss_coal_resp->action) == HostCmd_ACT_GEN_SET) - return 0; - - mwifiex_dbg(priv->adapter, INFO, - "info: new BSSID %pM\n", ibss_coal_resp->bssid); - - /* If rsp has NULL BSSID, Just return..... No Action */ - if (is_zero_ether_addr(ibss_coal_resp->bssid)) { - mwifiex_dbg(priv->adapter, FATAL, "new BSSID is NULL\n"); - return 0; - } - - /* If BSSID is diff, modify current BSS parameters */ - if (!ether_addr_equal(priv->curr_bss_params.bss_descriptor.mac_address, ibss_coal_resp->bssid)) { - /* BSSID */ - memcpy(priv->curr_bss_params.bss_descriptor.mac_address, - ibss_coal_resp->bssid, ETH_ALEN); - - /* Beacon Interval */ - priv->curr_bss_params.bss_descriptor.beacon_period - = le16_to_cpu(ibss_coal_resp->beacon_interval); - - /* ERP Information */ - priv->curr_bss_params.bss_descriptor.erp_flags = - (u8) le16_to_cpu(ibss_coal_resp->use_g_rate_protect); - - priv->adhoc_state = ADHOC_COALESCED; - } - - return 0; -} -static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_tdls_oper *cmd_tdls_oper = &resp->params.tdls_oper; - u16 reason = le16_to_cpu(cmd_tdls_oper->reason); - u16 action = le16_to_cpu(cmd_tdls_oper->tdls_action); - struct mwifiex_sta_node *node = - mwifiex_get_sta_entry(priv, cmd_tdls_oper->peer_mac); - - switch (action) { - case ACT_TDLS_DELETE: - if (reason) { - if (!node || reason == TDLS_ERR_LINK_NONEXISTENT) - mwifiex_dbg(priv->adapter, MSG, - "TDLS link delete for %pM failed: reason %d\n", - cmd_tdls_oper->peer_mac, reason); - else - mwifiex_dbg(priv->adapter, ERROR, - "TDLS link delete for %pM failed: reason %d\n", - cmd_tdls_oper->peer_mac, reason); - } else { - mwifiex_dbg(priv->adapter, MSG, - "TDLS link delete for %pM successful\n", - cmd_tdls_oper->peer_mac); - } - break; - case ACT_TDLS_CREATE: - if (reason) { - mwifiex_dbg(priv->adapter, ERROR, - "TDLS link creation for %pM failed: reason %d", - cmd_tdls_oper->peer_mac, reason); - if (node && reason != TDLS_ERR_LINK_EXISTS) - node->tdls_status = TDLS_SETUP_FAILURE; - } else { - mwifiex_dbg(priv->adapter, MSG, - "TDLS link creation for %pM successful", - cmd_tdls_oper->peer_mac); - } - break; - case ACT_TDLS_CONFIG: - if (reason) { - mwifiex_dbg(priv->adapter, ERROR, - "TDLS link config for %pM failed, reason %d\n", - cmd_tdls_oper->peer_mac, reason); - if (node) - node->tdls_status = TDLS_SETUP_FAILURE; - } else { - mwifiex_dbg(priv->adapter, MSG, - "TDLS link config for %pM successful\n", - cmd_tdls_oper->peer_mac); - } - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "Unknown TDLS command action response %d", action); - return -1; - } - - return 0; -} -/* - * This function handles the command response for subscribe event command. - */ -static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event = - &resp->params.subsc_evt; - - /* For every subscribe event command (Get/Set/Clear), FW reports the - * current set of subscribed events*/ - mwifiex_dbg(priv->adapter, EVENT, - "Bitmap of currently subscribed events: %16x\n", - le16_to_cpu(cmd_sub_event->events)); - - return 0; -} - -static int mwifiex_ret_uap_sta_list(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_sta_list *sta_list = - &resp->params.sta_list; - struct mwifiex_ie_types_sta_info *sta_info = (void *)&sta_list->tlv; - int i; - struct mwifiex_sta_node *sta_node; - - for (i = 0; i < sta_list->sta_count; i++) { - sta_node = mwifiex_get_sta_entry(priv, sta_info->mac); - if (unlikely(!sta_node)) - continue; - - sta_node->stats.rssi = sta_info->rssi; - sta_info++; - } - - return 0; -} - -/* This function handles the command response of set_cfg_data */ -static int mwifiex_ret_cfg_data(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - if (resp->result != HostCmd_RESULT_OK) { - mwifiex_dbg(priv->adapter, ERROR, "Cal data cmd resp failed\n"); - return -1; - } - - return 0; -} - -/** This Function handles the command response of sdio rx aggr */ -static int mwifiex_ret_sdio_rx_aggr_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = - &resp->params.sdio_rx_aggr_cfg; - - adapter->sdio_rx_aggr_enable = cfg->enable; - adapter->sdio_rx_block_size = le16_to_cpu(cfg->block_size); - - return 0; -} - -static int mwifiex_ret_robust_coex(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - bool *is_timeshare) -{ - struct host_cmd_ds_robust_coex *coex = &resp->params.coex; - struct mwifiex_ie_types_robust_coex *coex_tlv; - u16 action = le16_to_cpu(coex->action); - u32 mode; - - coex_tlv = (struct mwifiex_ie_types_robust_coex - *)((u8 *)coex + sizeof(struct host_cmd_ds_robust_coex)); - if (action == HostCmd_ACT_GEN_GET) { - mode = le32_to_cpu(coex_tlv->mode); - if (mode == MWIFIEX_COEX_MODE_TIMESHARE) - *is_timeshare = true; - else - *is_timeshare = false; - } - - return 0; -} - -/* - * This function handles the command responses. - * - * This is a generic function, which calls command specific - * response handlers based on the command ID. - */ -int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, - struct host_cmd_ds_command *resp) -{ - int ret = 0; - struct mwifiex_adapter *adapter = priv->adapter; - void *data_buf = adapter->curr_cmd->data_buf; - - /* If the command is not successful, cleanup and return failure */ - if (resp->result != HostCmd_RESULT_OK) { - mwifiex_process_cmdresp_error(priv, resp); - return -1; - } - /* Command successful, handle response */ - switch (cmdresp_no) { - case HostCmd_CMD_GET_HW_SPEC: - ret = mwifiex_ret_get_hw_spec(priv, resp); - break; - case HostCmd_CMD_CFG_DATA: - ret = mwifiex_ret_cfg_data(priv, resp); - break; - case HostCmd_CMD_MAC_CONTROL: - break; - case HostCmd_CMD_802_11_MAC_ADDRESS: - ret = mwifiex_ret_802_11_mac_address(priv, resp); - break; - case HostCmd_CMD_MAC_MULTICAST_ADR: - ret = mwifiex_ret_mac_multicast_adr(priv, resp); - break; - case HostCmd_CMD_TX_RATE_CFG: - ret = mwifiex_ret_tx_rate_cfg(priv, resp); - break; - case HostCmd_CMD_802_11_SCAN: - ret = mwifiex_ret_802_11_scan(priv, resp); - adapter->curr_cmd->wait_q_enabled = false; - break; - case HostCmd_CMD_802_11_SCAN_EXT: - ret = mwifiex_ret_802_11_scan_ext(priv, resp); - adapter->curr_cmd->wait_q_enabled = false; - break; - case HostCmd_CMD_802_11_BG_SCAN_QUERY: - ret = mwifiex_ret_802_11_scan(priv, resp); - mwifiex_dbg(adapter, CMD, - "info: CMD_RESP: BG_SCAN result is ready!\n"); - break; - case HostCmd_CMD_TXPWR_CFG: - ret = mwifiex_ret_tx_power_cfg(priv, resp); - break; - case HostCmd_CMD_RF_TX_PWR: - ret = mwifiex_ret_rf_tx_power(priv, resp); - break; - case HostCmd_CMD_RF_ANTENNA: - ret = mwifiex_ret_rf_antenna(priv, resp); - break; - case HostCmd_CMD_802_11_PS_MODE_ENH: - ret = mwifiex_ret_enh_power_mode(priv, resp, data_buf); - break; - case HostCmd_CMD_802_11_HS_CFG_ENH: - ret = mwifiex_ret_802_11_hs_cfg(priv, resp); - break; - case HostCmd_CMD_802_11_ASSOCIATE: - ret = mwifiex_ret_802_11_associate(priv, resp); - break; - case HostCmd_CMD_802_11_DEAUTHENTICATE: - ret = mwifiex_ret_802_11_deauthenticate(priv, resp); - break; - case HostCmd_CMD_802_11_AD_HOC_START: - case HostCmd_CMD_802_11_AD_HOC_JOIN: - ret = mwifiex_ret_802_11_ad_hoc(priv, resp); - break; - case HostCmd_CMD_802_11_AD_HOC_STOP: - ret = mwifiex_ret_802_11_ad_hoc_stop(priv, resp); - break; - case HostCmd_CMD_802_11_GET_LOG: - ret = mwifiex_ret_get_log(priv, resp, data_buf); - break; - case HostCmd_CMD_RSSI_INFO: - ret = mwifiex_ret_802_11_rssi_info(priv, resp); - break; - case HostCmd_CMD_802_11_SNMP_MIB: - ret = mwifiex_ret_802_11_snmp_mib(priv, resp, data_buf); - break; - case HostCmd_CMD_802_11_TX_RATE_QUERY: - ret = mwifiex_ret_802_11_tx_rate_query(priv, resp); - break; - case HostCmd_CMD_VERSION_EXT: - ret = mwifiex_ret_ver_ext(priv, resp, data_buf); - break; - case HostCmd_CMD_REMAIN_ON_CHAN: - ret = mwifiex_ret_remain_on_chan(priv, resp, data_buf); - break; - case HostCmd_CMD_11AC_CFG: - break; - case HostCmd_CMD_P2P_MODE_CFG: - ret = mwifiex_ret_p2p_mode_cfg(priv, resp, data_buf); - break; - case HostCmd_CMD_MGMT_FRAME_REG: - case HostCmd_CMD_FUNC_INIT: - case HostCmd_CMD_FUNC_SHUTDOWN: - break; - case HostCmd_CMD_802_11_KEY_MATERIAL: - ret = mwifiex_ret_802_11_key_material(priv, resp); - break; - case HostCmd_CMD_802_11D_DOMAIN_INFO: - ret = mwifiex_ret_802_11d_domain_info(priv, resp); - break; - case HostCmd_CMD_11N_ADDBA_REQ: - ret = mwifiex_ret_11n_addba_req(priv, resp); - break; - case HostCmd_CMD_11N_DELBA: - ret = mwifiex_ret_11n_delba(priv, resp); - break; - case HostCmd_CMD_11N_ADDBA_RSP: - ret = mwifiex_ret_11n_addba_resp(priv, resp); - break; - case HostCmd_CMD_RECONFIGURE_TX_BUFF: - if (0xffff == (u16)le16_to_cpu(resp->params.tx_buf.buff_size)) { - if (adapter->iface_type == MWIFIEX_USB && - adapter->usb_mc_setup) { - if (adapter->if_ops.multi_port_resync) - adapter->if_ops. - multi_port_resync(adapter); - adapter->usb_mc_setup = false; - adapter->tx_lock_flag = false; - } - break; - } - adapter->tx_buf_size = (u16) le16_to_cpu(resp->params. - tx_buf.buff_size); - adapter->tx_buf_size = (adapter->tx_buf_size - / MWIFIEX_SDIO_BLOCK_SIZE) - * MWIFIEX_SDIO_BLOCK_SIZE; - adapter->curr_tx_buf_size = adapter->tx_buf_size; - mwifiex_dbg(adapter, CMD, "cmd: curr_tx_buf_size=%d\n", - adapter->curr_tx_buf_size); - - if (adapter->if_ops.update_mp_end_port) - adapter->if_ops.update_mp_end_port(adapter, - le16_to_cpu(resp->params.tx_buf.mp_end_port)); - break; - case HostCmd_CMD_AMSDU_AGGR_CTRL: - break; - case HostCmd_CMD_WMM_GET_STATUS: - ret = mwifiex_ret_wmm_get_status(priv, resp); - break; - case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: - ret = mwifiex_ret_ibss_coalescing_status(priv, resp); - break; - case HostCmd_CMD_MEM_ACCESS: - ret = mwifiex_ret_mem_access(priv, resp, data_buf); - break; - case HostCmd_CMD_MAC_REG_ACCESS: - case HostCmd_CMD_BBP_REG_ACCESS: - case HostCmd_CMD_RF_REG_ACCESS: - case HostCmd_CMD_PMIC_REG_ACCESS: - case HostCmd_CMD_CAU_REG_ACCESS: - case HostCmd_CMD_802_11_EEPROM_ACCESS: - ret = mwifiex_ret_reg_access(cmdresp_no, resp, data_buf); - break; - case HostCmd_CMD_SET_BSS_MODE: - break; - case HostCmd_CMD_11N_CFG: - break; - case HostCmd_CMD_PCIE_DESC_DETAILS: - break; - case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: - ret = mwifiex_ret_subsc_evt(priv, resp); - break; - case HostCmd_CMD_UAP_SYS_CONFIG: - break; - case HOST_CMD_APCMD_STA_LIST: - ret = mwifiex_ret_uap_sta_list(priv, resp); - break; - case HostCmd_CMD_UAP_BSS_START: - adapter->tx_lock_flag = false; - adapter->pps_uapsd_mode = false; - adapter->delay_null_pkt = false; - priv->bss_started = 1; - break; - case HostCmd_CMD_UAP_BSS_STOP: - priv->bss_started = 0; - break; - case HostCmd_CMD_UAP_STA_DEAUTH: - break; - case HOST_CMD_APCMD_SYS_RESET: - break; - case HostCmd_CMD_MEF_CFG: - break; - case HostCmd_CMD_COALESCE_CFG: - break; - case HostCmd_CMD_TDLS_OPER: - ret = mwifiex_ret_tdls_oper(priv, resp); - case HostCmd_CMD_MC_POLICY: - break; - case HostCmd_CMD_CHAN_REPORT_REQUEST: - break; - case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: - ret = mwifiex_ret_sdio_rx_aggr_cfg(priv, resp); - break; - case HostCmd_CMD_TDLS_CONFIG: - break; - case HostCmd_CMD_ROBUST_COEX: - ret = mwifiex_ret_robust_coex(priv, resp, data_buf); - break; - default: - mwifiex_dbg(adapter, ERROR, - "CMD_RESP: unknown cmd response %#x\n", - resp->command); - break; - } - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c deleted file mode 100644 index ff3ee9dfbbd5..000000000000 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ /dev/null @@ -1,864 +0,0 @@ -/* - * Marvell Wireless LAN device driver: station event handling - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" - -/* - * This function resets the connection state. - * - * The function is invoked after receiving a disconnect event from firmware, - * and performs the following actions - - * - Set media status to disconnected - * - Clean up Tx and Rx packets - * - Resets SNR/NF/RSSI value in driver - * - Resets security configurations in driver - * - Enables auto data rate - * - Saves the previous SSID and BSSID so that they can - * be used for re-association, if required - * - Erases current SSID and BSSID information - * - Sends a disconnect event to upper layers/applications. - */ -void -mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - if (!priv->media_connected) - return; - - mwifiex_dbg(adapter, INFO, - "info: handles disconnect event\n"); - - priv->media_connected = false; - - priv->scan_block = false; - priv->port_open = false; - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) { - mwifiex_disable_all_tdls_links(priv); - - if (priv->adapter->auto_tdls) - mwifiex_clean_auto_tdls(priv); - } - - /* Free Tx and Rx packets, report disconnect to upper layer */ - mwifiex_clean_txrx(priv); - - /* Reset SNR/NF/RSSI values */ - priv->data_rssi_last = 0; - priv->data_nf_last = 0; - priv->data_rssi_avg = 0; - priv->data_nf_avg = 0; - priv->bcn_rssi_last = 0; - priv->bcn_nf_last = 0; - priv->bcn_rssi_avg = 0; - priv->bcn_nf_avg = 0; - priv->rxpd_rate = 0; - priv->rxpd_htinfo = 0; - priv->sec_info.wpa_enabled = false; - priv->sec_info.wpa2_enabled = false; - priv->wpa_ie_len = 0; - - priv->sec_info.wapi_enabled = false; - priv->wapi_ie_len = 0; - priv->sec_info.wapi_key_on = false; - - priv->sec_info.encryption_mode = 0; - - /* Enable auto data rate */ - priv->is_data_rate_auto = true; - priv->data_rate = 0; - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || - GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && priv->hist_data) - mwifiex_hist_data_reset(priv); - - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { - priv->adhoc_state = ADHOC_IDLE; - priv->adhoc_is_link_sensed = false; - } - - /* - * Memorize the previous SSID and BSSID so - * it could be used for re-assoc - */ - - mwifiex_dbg(adapter, INFO, - "info: previous SSID=%s, SSID len=%u\n", - priv->prev_ssid.ssid, priv->prev_ssid.ssid_len); - - mwifiex_dbg(adapter, INFO, - "info: current SSID=%s, SSID len=%u\n", - priv->curr_bss_params.bss_descriptor.ssid.ssid, - priv->curr_bss_params.bss_descriptor.ssid.ssid_len); - - memcpy(&priv->prev_ssid, - &priv->curr_bss_params.bss_descriptor.ssid, - sizeof(struct cfg80211_ssid)); - - memcpy(priv->prev_bssid, - priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); - - /* Need to erase the current SSID and BSSID info */ - memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params)); - - adapter->tx_lock_flag = false; - adapter->pps_uapsd_mode = false; - - if (adapter->is_cmd_timedout && adapter->curr_cmd) - return; - priv->media_connected = false; - mwifiex_dbg(adapter, MSG, - "info: successfully disconnected from %pM: reason code %d\n", - priv->cfg_bssid, reason_code); - if (priv->bss_mode == NL80211_IFTYPE_STATION || - priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { - cfg80211_disconnected(priv->netdev, reason_code, NULL, 0, - false, GFP_KERNEL); - } - eth_zero_addr(priv->cfg_bssid); - - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); -} - -static int mwifiex_parse_tdls_event(struct mwifiex_private *priv, - struct sk_buff *event_skb) -{ - int ret = 0; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_tdls_generic_event *tdls_evt = - (void *)event_skb->data + sizeof(adapter->event_cause); - u8 *mac = tdls_evt->peer_mac; - - /* reserved 2 bytes are not mandatory in tdls event */ - if (event_skb->len < (sizeof(struct mwifiex_tdls_generic_event) - - sizeof(u16) - sizeof(adapter->event_cause))) { - mwifiex_dbg(adapter, ERROR, "Invalid event length!\n"); - return -1; - } - - sta_ptr = mwifiex_get_sta_entry(priv, tdls_evt->peer_mac); - if (!sta_ptr) { - mwifiex_dbg(adapter, ERROR, "cannot get sta entry!\n"); - return -1; - } - - switch (le16_to_cpu(tdls_evt->type)) { - case TDLS_EVENT_LINK_TEAR_DOWN: - cfg80211_tdls_oper_request(priv->netdev, - tdls_evt->peer_mac, - NL80211_TDLS_TEARDOWN, - le16_to_cpu(tdls_evt->u.reason_code), - GFP_KERNEL); - break; - case TDLS_EVENT_CHAN_SWITCH_RESULT: - mwifiex_dbg(adapter, EVENT, "tdls channel switch result :\n"); - mwifiex_dbg(adapter, EVENT, - "status=0x%x, reason=0x%x cur_chan=%d\n", - tdls_evt->u.switch_result.status, - tdls_evt->u.switch_result.reason, - tdls_evt->u.switch_result.cur_chan); - - /* tdls channel switch failed */ - if (tdls_evt->u.switch_result.status != 0) { - switch (tdls_evt->u.switch_result.cur_chan) { - case TDLS_BASE_CHANNEL: - sta_ptr->tdls_status = TDLS_IN_BASE_CHAN; - break; - case TDLS_OFF_CHANNEL: - sta_ptr->tdls_status = TDLS_IN_OFF_CHAN; - break; - default: - break; - } - return ret; - } - - /* tdls channel switch success */ - switch (tdls_evt->u.switch_result.cur_chan) { - case TDLS_BASE_CHANNEL: - if (sta_ptr->tdls_status == TDLS_IN_BASE_CHAN) - break; - mwifiex_update_ralist_tx_pause_in_tdls_cs(priv, mac, - false); - sta_ptr->tdls_status = TDLS_IN_BASE_CHAN; - break; - case TDLS_OFF_CHANNEL: - if (sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) - break; - mwifiex_update_ralist_tx_pause_in_tdls_cs(priv, mac, - true); - sta_ptr->tdls_status = TDLS_IN_OFF_CHAN; - break; - default: - break; - } - - break; - case TDLS_EVENT_START_CHAN_SWITCH: - mwifiex_dbg(adapter, EVENT, "tdls start channel switch...\n"); - sta_ptr->tdls_status = TDLS_CHAN_SWITCHING; - break; - case TDLS_EVENT_CHAN_SWITCH_STOPPED: - mwifiex_dbg(adapter, EVENT, - "tdls chan switch stopped, reason=%d\n", - tdls_evt->u.cs_stop_reason); - break; - default: - break; - } - - return ret; -} - -static void mwifiex_process_uap_tx_pause(struct mwifiex_private *priv, - struct mwifiex_ie_types_header *tlv) -{ - struct mwifiex_tx_pause_tlv *tp; - struct mwifiex_sta_node *sta_ptr; - unsigned long flags; - - tp = (void *)tlv; - mwifiex_dbg(priv->adapter, EVENT, - "uap tx_pause: %pM pause=%d, pkts=%d\n", - tp->peermac, tp->tx_pause, - tp->pkt_cnt); - - if (ether_addr_equal(tp->peermac, priv->netdev->dev_addr)) { - if (tp->tx_pause) - priv->port_open = false; - else - priv->port_open = true; - } else if (is_multicast_ether_addr(tp->peermac)) { - mwifiex_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause); - } else { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { - sta_ptr->tx_pause = tp->tx_pause; - mwifiex_update_ralist_tx_pause(priv, tp->peermac, - tp->tx_pause); - } - } -} - -static void mwifiex_process_sta_tx_pause(struct mwifiex_private *priv, - struct mwifiex_ie_types_header *tlv) -{ - struct mwifiex_tx_pause_tlv *tp; - struct mwifiex_sta_node *sta_ptr; - int status; - unsigned long flags; - - tp = (void *)tlv; - mwifiex_dbg(priv->adapter, EVENT, - "sta tx_pause: %pM pause=%d, pkts=%d\n", - tp->peermac, tp->tx_pause, - tp->pkt_cnt); - - if (ether_addr_equal(tp->peermac, priv->cfg_bssid)) { - if (tp->tx_pause) - priv->port_open = false; - else - priv->port_open = true; - } else { - if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) - return; - - status = mwifiex_get_tdls_link_status(priv, tp->peermac); - if (mwifiex_is_tdls_link_setup(status)) { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { - sta_ptr->tx_pause = tp->tx_pause; - mwifiex_update_ralist_tx_pause(priv, - tp->peermac, - tp->tx_pause); - } - } - } -} - -void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, - struct sk_buff *event_skb) -{ - struct mwifiex_ie_types_multi_chan_info *chan_info; - struct mwifiex_ie_types_mc_group_info *grp_info; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie_types_header *tlv; - u16 tlv_buf_left, tlv_type, tlv_len; - int intf_num, bss_type, bss_num, i; - struct mwifiex_private *intf_priv; - - tlv_buf_left = event_skb->len - sizeof(u32); - chan_info = (void *)event_skb->data + sizeof(u32); - - if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO || - tlv_buf_left < sizeof(struct mwifiex_ie_types_multi_chan_info)) { - mwifiex_dbg(adapter, ERROR, - "unknown TLV in chan_info event\n"); - return; - } - - adapter->usb_mc_status = le16_to_cpu(chan_info->status); - mwifiex_dbg(adapter, EVENT, "multi chan operation %s\n", - adapter->usb_mc_status ? "started" : "over"); - - tlv_buf_left -= sizeof(struct mwifiex_ie_types_multi_chan_info); - tlv = (struct mwifiex_ie_types_header *)chan_info->tlv_buffer; - - while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) { - tlv_type = le16_to_cpu(tlv->type); - tlv_len = le16_to_cpu(tlv->len); - if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) > - tlv_buf_left) { - mwifiex_dbg(adapter, ERROR, "wrong tlv: tlvLen=%d,\t" - "tlvBufLeft=%d\n", tlv_len, tlv_buf_left); - break; - } - if (tlv_type != TLV_TYPE_MC_GROUP_INFO) { - mwifiex_dbg(adapter, ERROR, "wrong tlv type: 0x%x\n", - tlv_type); - break; - } - - grp_info = (struct mwifiex_ie_types_mc_group_info *)tlv; - intf_num = grp_info->intf_num; - for (i = 0; i < intf_num; i++) { - bss_type = grp_info->bss_type_numlist[i] >> 4; - bss_num = grp_info->bss_type_numlist[i] & BSS_NUM_MASK; - intf_priv = mwifiex_get_priv_by_id(adapter, bss_num, - bss_type); - if (!intf_priv) { - mwifiex_dbg(adapter, ERROR, - "Invalid bss_type bss_num\t" - "in multi channel event\n"); - continue; - } - if (adapter->iface_type == MWIFIEX_USB) { - u8 ep; - - ep = grp_info->hid_num.usb_ep_num; - if (ep == MWIFIEX_USB_EP_DATA || - ep == MWIFIEX_USB_EP_DATA_CH2) - intf_priv->usb_port = ep; - } - } - - tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) + - tlv_len; - tlv = (void *)((u8 *)tlv + tlv_len + - sizeof(struct mwifiex_ie_types_header)); - } - - if (adapter->iface_type == MWIFIEX_USB) { - adapter->tx_lock_flag = true; - adapter->usb_mc_setup = true; - mwifiex_multi_chan_resync(adapter); - } -} - -void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, - struct sk_buff *event_skb) -{ - struct mwifiex_ie_types_header *tlv; - u16 tlv_type, tlv_len; - int tlv_buf_left; - - if (!priv->media_connected) { - mwifiex_dbg(priv->adapter, ERROR, - "tx_pause event while disconnected; bss_role=%d\n", - priv->bss_role); - return; - } - - tlv_buf_left = event_skb->len - sizeof(u32); - tlv = (void *)event_skb->data + sizeof(u32); - - while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) { - tlv_type = le16_to_cpu(tlv->type); - tlv_len = le16_to_cpu(tlv->len); - if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) > - tlv_buf_left) { - mwifiex_dbg(priv->adapter, ERROR, - "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", - tlv_len, tlv_buf_left); - break; - } - if (tlv_type == TLV_TYPE_TX_PAUSE) { - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) - mwifiex_process_sta_tx_pause(priv, tlv); - else - mwifiex_process_uap_tx_pause(priv, tlv); - } - - tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) + - tlv_len; - tlv = (void *)((u8 *)tlv + tlv_len + - sizeof(struct mwifiex_ie_types_header)); - } - -} - -/* -* This function handles coex events generated by firmware -*/ -void mwifiex_bt_coex_wlan_param_update_event(struct mwifiex_private *priv, - struct sk_buff *event_skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie_types_header *tlv; - struct mwifiex_ie_types_btcoex_aggr_win_size *winsizetlv; - struct mwifiex_ie_types_btcoex_scan_time *scantlv; - s32 len = event_skb->len - sizeof(u32); - u8 *cur_ptr = event_skb->data + sizeof(u32); - u16 tlv_type, tlv_len; - - while (len >= sizeof(struct mwifiex_ie_types_header)) { - tlv = (struct mwifiex_ie_types_header *)cur_ptr; - tlv_len = le16_to_cpu(tlv->len); - tlv_type = le16_to_cpu(tlv->type); - - if ((tlv_len + sizeof(struct mwifiex_ie_types_header)) > len) - break; - switch (tlv_type) { - case TLV_BTCOEX_WL_AGGR_WINSIZE: - winsizetlv = - (struct mwifiex_ie_types_btcoex_aggr_win_size *)tlv; - adapter->coex_win_size = winsizetlv->coex_win_size; - adapter->coex_tx_win_size = - winsizetlv->tx_win_size; - adapter->coex_rx_win_size = - winsizetlv->rx_win_size; - mwifiex_coex_ampdu_rxwinsize(adapter); - mwifiex_update_ampdu_txwinsize(adapter); - break; - - case TLV_BTCOEX_WL_SCANTIME: - scantlv = - (struct mwifiex_ie_types_btcoex_scan_time *)tlv; - adapter->coex_scan = scantlv->coex_scan; - adapter->coex_min_scan_time = scantlv->min_scan_time; - adapter->coex_max_scan_time = scantlv->max_scan_time; - break; - - default: - break; - } - - len -= tlv_len + sizeof(struct mwifiex_ie_types_header); - cur_ptr += tlv_len + - sizeof(struct mwifiex_ie_types_header); - } - - dev_dbg(adapter->dev, "coex_scan=%d min_scan=%d coex_win=%d, tx_win=%d rx_win=%d\n", - adapter->coex_scan, adapter->coex_min_scan_time, - adapter->coex_win_size, adapter->coex_tx_win_size, - adapter->coex_rx_win_size); -} - -/* - * This function handles events generated by firmware. - * - * This is a generic function and handles all events. - * - * Event specific routines are called by this function based - * upon the generated event cause. - * - * For the following events, the function just forwards them to upper - * layers, optionally recording the change - - * - EVENT_LINK_SENSED - * - EVENT_MIC_ERR_UNICAST - * - EVENT_MIC_ERR_MULTICAST - * - EVENT_PORT_RELEASE - * - EVENT_RSSI_LOW - * - EVENT_SNR_LOW - * - EVENT_MAX_FAIL - * - EVENT_RSSI_HIGH - * - EVENT_SNR_HIGH - * - EVENT_DATA_RSSI_LOW - * - EVENT_DATA_SNR_LOW - * - EVENT_DATA_RSSI_HIGH - * - EVENT_DATA_SNR_HIGH - * - EVENT_LINK_QUALITY - * - EVENT_PRE_BEACON_LOST - * - EVENT_IBSS_COALESCED - * - EVENT_WEP_ICV_ERR - * - EVENT_BW_CHANGE - * - EVENT_HOSTWAKE_STAIE - * - * For the following events, no action is taken - - * - EVENT_MIB_CHANGED - * - EVENT_INIT_DONE - * - EVENT_DUMMY_HOST_WAKEUP_SIGNAL - * - * Rest of the supported events requires driver handling - - * - EVENT_DEAUTHENTICATED - * - EVENT_DISASSOCIATED - * - EVENT_LINK_LOST - * - EVENT_PS_SLEEP - * - EVENT_PS_AWAKE - * - EVENT_DEEP_SLEEP_AWAKE - * - EVENT_HS_ACT_REQ - * - EVENT_ADHOC_BCN_LOST - * - EVENT_BG_SCAN_REPORT - * - EVENT_WMM_STATUS_CHANGE - * - EVENT_ADDBA - * - EVENT_DELBA - * - EVENT_BA_STREAM_TIEMOUT - * - EVENT_AMSDU_AGGR_CTRL - */ -int mwifiex_process_sta_event(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret = 0; - u32 eventcause = adapter->event_cause; - u16 ctrl, reason_code; - - switch (eventcause) { - case EVENT_DUMMY_HOST_WAKEUP_SIGNAL: - mwifiex_dbg(adapter, ERROR, - "invalid EVENT: DUMMY_HOST_WAKEUP_SIGNAL, ignore it\n"); - break; - case EVENT_LINK_SENSED: - mwifiex_dbg(adapter, EVENT, "event: LINK_SENSED\n"); - if (!netif_carrier_ok(priv->netdev)) - netif_carrier_on(priv->netdev); - mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); - break; - - case EVENT_DEAUTHENTICATED: - mwifiex_dbg(adapter, EVENT, "event: Deauthenticated\n"); - if (priv->wps.session_enable) { - mwifiex_dbg(adapter, INFO, - "info: receive deauth event in wps session\n"); - break; - } - adapter->dbg.num_event_deauth++; - if (priv->media_connected) { - reason_code = - le16_to_cpu(*(__le16 *)adapter->event_body); - mwifiex_reset_connect_state(priv, reason_code); - } - break; - - case EVENT_DISASSOCIATED: - mwifiex_dbg(adapter, EVENT, "event: Disassociated\n"); - if (priv->wps.session_enable) { - mwifiex_dbg(adapter, INFO, - "info: receive disassoc event in wps session\n"); - break; - } - adapter->dbg.num_event_disassoc++; - if (priv->media_connected) { - reason_code = - le16_to_cpu(*(__le16 *)adapter->event_body); - mwifiex_reset_connect_state(priv, reason_code); - } - break; - - case EVENT_LINK_LOST: - mwifiex_dbg(adapter, EVENT, "event: Link lost\n"); - adapter->dbg.num_event_link_lost++; - if (priv->media_connected) { - reason_code = - le16_to_cpu(*(__le16 *)adapter->event_body); - mwifiex_reset_connect_state(priv, reason_code); - } - break; - - case EVENT_PS_SLEEP: - mwifiex_dbg(adapter, EVENT, "info: EVENT: SLEEP\n"); - - adapter->ps_state = PS_STATE_PRE_SLEEP; - - mwifiex_check_ps_cond(adapter); - break; - - case EVENT_PS_AWAKE: - mwifiex_dbg(adapter, EVENT, "info: EVENT: AWAKE\n"); - if (!adapter->pps_uapsd_mode && priv->port_open && - priv->media_connected && adapter->sleep_period.period) { - adapter->pps_uapsd_mode = true; - mwifiex_dbg(adapter, EVENT, - "event: PPS/UAPSD mode activated\n"); - } - adapter->tx_lock_flag = false; - if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { - if (mwifiex_check_last_packet_indication(priv)) { - if (adapter->data_sent || - (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv))) { - adapter->ps_state = PS_STATE_AWAKE; - adapter->pm_wakeup_card_req = false; - adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); - break; - } - if (!mwifiex_send_null_packet - (priv, - MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | - MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) - adapter->ps_state = - PS_STATE_SLEEP; - return 0; - } - } - adapter->ps_state = PS_STATE_AWAKE; - adapter->pm_wakeup_card_req = false; - adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); - - break; - - case EVENT_DEEP_SLEEP_AWAKE: - adapter->if_ops.wakeup_complete(adapter); - mwifiex_dbg(adapter, EVENT, "event: DS_AWAKE\n"); - if (adapter->is_deep_sleep) - adapter->is_deep_sleep = false; - break; - - case EVENT_HS_ACT_REQ: - mwifiex_dbg(adapter, EVENT, "event: HS_ACT_REQ\n"); - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_HS_CFG_ENH, - 0, 0, NULL, false); - break; - - case EVENT_MIC_ERR_UNICAST: - mwifiex_dbg(adapter, EVENT, "event: UNICAST MIC ERROR\n"); - cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, - NL80211_KEYTYPE_PAIRWISE, - -1, NULL, GFP_KERNEL); - break; - - case EVENT_MIC_ERR_MULTICAST: - mwifiex_dbg(adapter, EVENT, "event: MULTICAST MIC ERROR\n"); - cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, - NL80211_KEYTYPE_GROUP, - -1, NULL, GFP_KERNEL); - break; - case EVENT_MIB_CHANGED: - case EVENT_INIT_DONE: - break; - - case EVENT_ADHOC_BCN_LOST: - mwifiex_dbg(adapter, EVENT, "event: ADHOC_BCN_LOST\n"); - priv->adhoc_is_link_sensed = false; - mwifiex_clean_txrx(priv); - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - break; - - case EVENT_BG_SCAN_REPORT: - mwifiex_dbg(adapter, EVENT, "event: BGS_REPORT\n"); - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_QUERY, - HostCmd_ACT_GEN_GET, 0, NULL, false); - break; - - case EVENT_PORT_RELEASE: - mwifiex_dbg(adapter, EVENT, "event: PORT RELEASE\n"); - priv->port_open = true; - break; - - case EVENT_EXT_SCAN_REPORT: - mwifiex_dbg(adapter, EVENT, "event: EXT_SCAN Report\n"); - if (adapter->ext_scan) - ret = mwifiex_handle_event_ext_scan_report(priv, - adapter->event_skb->data); - - break; - - case EVENT_WMM_STATUS_CHANGE: - mwifiex_dbg(adapter, EVENT, "event: WMM status changed\n"); - ret = mwifiex_send_cmd(priv, HostCmd_CMD_WMM_GET_STATUS, - 0, 0, NULL, false); - break; - - case EVENT_RSSI_LOW: - cfg80211_cqm_rssi_notify(priv->netdev, - NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, - GFP_KERNEL); - mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, - HostCmd_ACT_GEN_GET, 0, NULL, false); - priv->subsc_evt_rssi_state = RSSI_LOW_RECVD; - mwifiex_dbg(adapter, EVENT, "event: Beacon RSSI_LOW\n"); - break; - case EVENT_SNR_LOW: - mwifiex_dbg(adapter, EVENT, "event: Beacon SNR_LOW\n"); - break; - case EVENT_MAX_FAIL: - mwifiex_dbg(adapter, EVENT, "event: MAX_FAIL\n"); - break; - case EVENT_RSSI_HIGH: - cfg80211_cqm_rssi_notify(priv->netdev, - NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, - GFP_KERNEL); - mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, - HostCmd_ACT_GEN_GET, 0, NULL, false); - priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD; - mwifiex_dbg(adapter, EVENT, "event: Beacon RSSI_HIGH\n"); - break; - case EVENT_SNR_HIGH: - mwifiex_dbg(adapter, EVENT, "event: Beacon SNR_HIGH\n"); - break; - case EVENT_DATA_RSSI_LOW: - mwifiex_dbg(adapter, EVENT, "event: Data RSSI_LOW\n"); - break; - case EVENT_DATA_SNR_LOW: - mwifiex_dbg(adapter, EVENT, "event: Data SNR_LOW\n"); - break; - case EVENT_DATA_RSSI_HIGH: - mwifiex_dbg(adapter, EVENT, "event: Data RSSI_HIGH\n"); - break; - case EVENT_DATA_SNR_HIGH: - mwifiex_dbg(adapter, EVENT, "event: Data SNR_HIGH\n"); - break; - case EVENT_LINK_QUALITY: - mwifiex_dbg(adapter, EVENT, "event: Link Quality\n"); - break; - case EVENT_PRE_BEACON_LOST: - mwifiex_dbg(adapter, EVENT, "event: Pre-Beacon Lost\n"); - break; - case EVENT_IBSS_COALESCED: - mwifiex_dbg(adapter, EVENT, "event: IBSS_COALESCED\n"); - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, - HostCmd_ACT_GEN_GET, 0, NULL, false); - break; - case EVENT_ADDBA: - mwifiex_dbg(adapter, EVENT, "event: ADDBA Request\n"); - mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP, - HostCmd_ACT_GEN_SET, 0, - adapter->event_body, false); - break; - case EVENT_DELBA: - mwifiex_dbg(adapter, EVENT, "event: DELBA Request\n"); - mwifiex_11n_delete_ba_stream(priv, adapter->event_body); - break; - case EVENT_BA_STREAM_TIEMOUT: - mwifiex_dbg(adapter, EVENT, "event: BA Stream timeout\n"); - mwifiex_11n_ba_stream_timeout(priv, - (struct host_cmd_ds_11n_batimeout - *) - adapter->event_body); - break; - case EVENT_AMSDU_AGGR_CTRL: - ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); - mwifiex_dbg(adapter, EVENT, - "event: AMSDU_AGGR_CTRL %d\n", ctrl); - - adapter->tx_buf_size = - min_t(u16, adapter->curr_tx_buf_size, ctrl); - mwifiex_dbg(adapter, EVENT, "event: tx_buf_size %d\n", - adapter->tx_buf_size); - break; - - case EVENT_WEP_ICV_ERR: - mwifiex_dbg(adapter, EVENT, "event: WEP ICV error\n"); - break; - - case EVENT_BW_CHANGE: - mwifiex_dbg(adapter, EVENT, "event: BW Change\n"); - break; - - case EVENT_HOSTWAKE_STAIE: - mwifiex_dbg(adapter, EVENT, - "event: HOSTWAKE_STAIE %d\n", eventcause); - break; - - case EVENT_REMAIN_ON_CHAN_EXPIRED: - mwifiex_dbg(adapter, EVENT, - "event: Remain on channel expired\n"); - cfg80211_remain_on_channel_expired(&priv->wdev, - priv->roc_cfg.cookie, - &priv->roc_cfg.chan, - GFP_ATOMIC); - - memset(&priv->roc_cfg, 0x00, sizeof(struct mwifiex_roc_cfg)); - - break; - - case EVENT_CHANNEL_SWITCH_ANN: - mwifiex_dbg(adapter, EVENT, "event: Channel Switch Announcement\n"); - priv->csa_expire_time = - jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME); - priv->csa_chan = priv->curr_bss_params.bss_descriptor.channel; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, - HostCmd_ACT_GEN_SET, 0, - priv->curr_bss_params.bss_descriptor.mac_address, - false); - break; - - case EVENT_TDLS_GENERIC_EVENT: - ret = mwifiex_parse_tdls_event(priv, adapter->event_skb); - break; - - case EVENT_TX_DATA_PAUSE: - mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n"); - mwifiex_process_tx_pause_event(priv, adapter->event_skb); - break; - - case EVENT_MULTI_CHAN_INFO: - mwifiex_dbg(adapter, EVENT, "event: multi-chan info\n"); - mwifiex_process_multi_chan_event(priv, adapter->event_skb); - break; - - case EVENT_TX_STATUS_REPORT: - mwifiex_dbg(adapter, EVENT, "event: TX_STATUS Report\n"); - mwifiex_parse_tx_status_event(priv, adapter->event_body); - break; - - case EVENT_CHANNEL_REPORT_RDY: - mwifiex_dbg(adapter, EVENT, "event: Channel Report\n"); - ret = mwifiex_11h_handle_chanrpt_ready(priv, - adapter->event_skb); - break; - case EVENT_RADAR_DETECTED: - mwifiex_dbg(adapter, EVENT, "event: Radar detected\n"); - ret = mwifiex_11h_handle_radar_detected(priv, - adapter->event_skb); - break; - case EVENT_BT_COEX_WLAN_PARA_CHANGE: - dev_dbg(adapter->dev, "EVENT: BT coex wlan param update\n"); - mwifiex_bt_coex_wlan_param_update_event(priv, - adapter->event_skb); - break; - default: - mwifiex_dbg(adapter, ERROR, "event: unknown event id: %#x\n", - eventcause); - break; - } - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c deleted file mode 100644 index a6c8a4f7bfe9..000000000000 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ /dev/null @@ -1,1421 +0,0 @@ -/* - * Marvell Wireless LAN device driver: functions for station ioctl - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "cfg80211.h" - -static int disconnect_on_suspend; -module_param(disconnect_on_suspend, int, 0644); - -/* - * Copies the multicast address list from device to driver. - * - * This function does not validate the destination memory for - * size, and the calling function must ensure enough memory is - * available. - */ -int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, - struct net_device *dev) -{ - int i = 0; - struct netdev_hw_addr *ha; - - netdev_for_each_mc_addr(ha, dev) - memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN); - - return i; -} - -/* - * Wait queue completion handler. - * - * This function waits on a cmd wait queue. It also cancels the pending - * request after waking up, in case of errors. - */ -int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_queued) -{ - int status; - - /* Wait for completion */ - status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait, - *(cmd_queued->condition), - (12 * HZ)); - if (status <= 0) { - if (status == 0) - status = -ETIMEDOUT; - mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n", - status); - mwifiex_cancel_all_pending_cmd(adapter); - return status; - } - - status = adapter->cmd_wait_q.status; - adapter->cmd_wait_q.status = 0; - - return status; -} - -/* - * This function prepares the correct firmware command and - * issues it to set the multicast list. - * - * This function can be used to enable promiscuous mode, or enable all - * multicast packets, or to enable selective multicast. - */ -int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, - struct mwifiex_multicast_list *mcast_list) -{ - int ret = 0; - u16 old_pkt_filter; - - old_pkt_filter = priv->curr_pkt_filter; - - if (mcast_list->mode == MWIFIEX_PROMISC_MODE) { - mwifiex_dbg(priv->adapter, INFO, - "info: Enable Promiscuous mode\n"); - priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; - priv->curr_pkt_filter &= - ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; - } else { - /* Multicast */ - priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; - if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) { - mwifiex_dbg(priv->adapter, INFO, - "info: Enabling All Multicast!\n"); - priv->curr_pkt_filter |= - HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; - } else { - priv->curr_pkt_filter &= - ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; - mwifiex_dbg(priv->adapter, INFO, - "info: Set multicast list=%d\n", - mcast_list->num_multicast_addr); - /* Send multicast addresses to firmware */ - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_MAC_MULTICAST_ADR, - HostCmd_ACT_GEN_SET, 0, - mcast_list, false); - } - } - mwifiex_dbg(priv->adapter, INFO, - "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n", - old_pkt_filter, priv->curr_pkt_filter); - if (old_pkt_filter != priv->curr_pkt_filter) { - ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, - 0, &priv->curr_pkt_filter, false); - } - - return ret; -} - -/* - * This function fills bss descriptor structure using provided - * information. - * beacon_ie buffer is allocated in this function. It is caller's - * responsibility to free the memory. - */ -int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, - struct cfg80211_bss *bss, - struct mwifiex_bssdescriptor *bss_desc) -{ - u8 *beacon_ie; - size_t beacon_ie_len; - struct mwifiex_bss_priv *bss_priv = (void *)bss->priv; - const struct cfg80211_bss_ies *ies; - - rcu_read_lock(); - ies = rcu_dereference(bss->ies); - beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC); - beacon_ie_len = ies->len; - bss_desc->timestamp = ies->tsf; - rcu_read_unlock(); - - if (!beacon_ie) { - mwifiex_dbg(priv->adapter, ERROR, - " failed to alloc beacon_ie\n"); - return -ENOMEM; - } - - memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN); - bss_desc->rssi = bss->signal; - /* The caller of this function will free beacon_ie */ - bss_desc->beacon_buf = beacon_ie; - bss_desc->beacon_buf_size = beacon_ie_len; - bss_desc->beacon_period = bss->beacon_interval; - bss_desc->cap_info_bitmap = bss->capability; - bss_desc->bss_band = bss_priv->band; - bss_desc->fw_tsf = bss_priv->fw_tsf; - if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) { - mwifiex_dbg(priv->adapter, INFO, - "info: InterpretIE: AP WEP enabled\n"); - bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; - } else { - bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; - } - if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_IBSS) - bss_desc->bss_mode = NL80211_IFTYPE_ADHOC; - else - bss_desc->bss_mode = NL80211_IFTYPE_STATION; - - /* Disable 11ac by default. Enable it only where there - * exist VHT_CAP IE in AP beacon - */ - bss_desc->disable_11ac = true; - - if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT) - bss_desc->sensed_11h = true; - - return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc); -} - -void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv) -{ - if (priv->adapter->dt_node) { - char txpwr[] = {"marvell,00_txpwrlimit"}; - - memcpy(&txpwr[8], priv->adapter->country_code, 2); - mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr); - } -} - -static int mwifiex_process_country_ie(struct mwifiex_private *priv, - struct cfg80211_bss *bss) -{ - const u8 *country_ie; - u8 country_ie_len; - struct mwifiex_802_11d_domain_reg *domain_info = - &priv->adapter->domain_reg; - - rcu_read_lock(); - country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); - if (!country_ie) { - rcu_read_unlock(); - return 0; - } - - country_ie_len = country_ie[1]; - if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { - rcu_read_unlock(); - return 0; - } - - if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) { - rcu_read_unlock(); - mwifiex_dbg(priv->adapter, INFO, - "11D: skip setting domain info in FW\n"); - return 0; - } - memcpy(priv->adapter->country_code, &country_ie[2], 2); - - domain_info->country_code[0] = country_ie[2]; - domain_info->country_code[1] = country_ie[3]; - domain_info->country_code[2] = ' '; - - country_ie_len -= IEEE80211_COUNTRY_STRING_LEN; - - domain_info->no_of_triplet = - country_ie_len / sizeof(struct ieee80211_country_ie_triplet); - - memcpy((u8 *)domain_info->triplet, - &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); - - rcu_read_unlock(); - - if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, - HostCmd_ACT_GEN_SET, 0, NULL, false)) { - mwifiex_dbg(priv->adapter, ERROR, - "11D: setting domain info in FW fail\n"); - return -1; - } - - mwifiex_dnld_txpwr_table(priv); - - return 0; -} - -/* - * In Ad-Hoc mode, the IBSS is created if not found in scan list. - * In both Ad-Hoc and infra mode, an deauthentication is performed - * first. - */ -int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, - struct cfg80211_ssid *req_ssid) -{ - int ret; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_bssdescriptor *bss_desc = NULL; - - priv->scan_block = false; - - if (bss) { - mwifiex_process_country_ie(priv, bss); - - /* Allocate and fill new bss descriptor */ - bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), - GFP_KERNEL); - if (!bss_desc) - return -ENOMEM; - - ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); - if (ret) - goto done; - } - - if (priv->bss_mode == NL80211_IFTYPE_STATION || - priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { - u8 config_bands; - - if (!bss_desc) - return -1; - - if (mwifiex_band_to_radio_type(bss_desc->bss_band) == - HostCmd_SCAN_RADIO_TYPE_BG) { - config_bands = BAND_B | BAND_G | BAND_GN; - } else { - config_bands = BAND_A | BAND_AN; - if (adapter->fw_bands & BAND_AAC) - config_bands |= BAND_AAC; - } - - if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) - adapter->config_bands = config_bands; - - ret = mwifiex_check_network_compatibility(priv, bss_desc); - if (ret) - goto done; - - if (mwifiex_11h_get_csa_closed_channel(priv) == - (u8)bss_desc->channel) { - mwifiex_dbg(adapter, ERROR, - "Attempt to reconnect on csa closed chan(%d)\n", - bss_desc->channel); - goto done; - } - - mwifiex_dbg(adapter, INFO, - "info: SSID found in scan list ...\t" - "associating...\n"); - - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - - /* Clear any past association response stored for - * application retrieval */ - priv->assoc_rsp_size = 0; - ret = mwifiex_associate(priv, bss_desc); - - /* If auth type is auto and association fails using open mode, - * try to connect using shared mode */ - if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && - priv->sec_info.is_authtype_auto && - priv->sec_info.wep_enabled) { - priv->sec_info.authentication_mode = - NL80211_AUTHTYPE_SHARED_KEY; - ret = mwifiex_associate(priv, bss_desc); - } - - if (bss) - cfg80211_put_bss(priv->adapter->wiphy, bss); - } else { - /* Adhoc mode */ - /* If the requested SSID matches current SSID, return */ - if (bss_desc && bss_desc->ssid.ssid_len && - (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor. - ssid, &bss_desc->ssid))) { - ret = 0; - goto done; - } - - priv->adhoc_is_link_sensed = false; - - ret = mwifiex_check_network_compatibility(priv, bss_desc); - - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - - if (!ret) { - mwifiex_dbg(adapter, INFO, - "info: network found in scan\t" - " list. Joining...\n"); - ret = mwifiex_adhoc_join(priv, bss_desc); - if (bss) - cfg80211_put_bss(priv->adapter->wiphy, bss); - } else { - mwifiex_dbg(adapter, INFO, - "info: Network not found in\t" - "the list, creating adhoc with ssid = %s\n", - req_ssid->ssid); - ret = mwifiex_adhoc_start(priv, req_ssid); - } - } - -done: - /* beacon_ie buffer was allocated in function - * mwifiex_fill_new_bss_desc(). Free it now. - */ - if (bss_desc) - kfree(bss_desc->beacon_buf); - kfree(bss_desc); - return ret; -} - -/* - * IOCTL request handler to set host sleep configuration. - * - * This function prepares the correct firmware command and - * issues it. - */ -int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, - int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg) - -{ - struct mwifiex_adapter *adapter = priv->adapter; - int status = 0; - u32 prev_cond = 0; - - if (!hs_cfg) - return -ENOMEM; - - switch (action) { - case HostCmd_ACT_GEN_SET: - if (adapter->pps_uapsd_mode) { - mwifiex_dbg(adapter, INFO, - "info: Host Sleep IOCTL\t" - "is blocked in UAPSD/PPS mode\n"); - status = -1; - break; - } - if (hs_cfg->is_invoke_hostcmd) { - if (hs_cfg->conditions == HS_CFG_CANCEL) { - if (!adapter->is_hs_configured) - /* Already cancelled */ - break; - /* Save previous condition */ - prev_cond = le32_to_cpu(adapter->hs_cfg - .conditions); - adapter->hs_cfg.conditions = - cpu_to_le32(hs_cfg->conditions); - } else if (hs_cfg->conditions) { - adapter->hs_cfg.conditions = - cpu_to_le32(hs_cfg->conditions); - adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; - if (hs_cfg->gap) - adapter->hs_cfg.gap = (u8)hs_cfg->gap; - } else if (adapter->hs_cfg.conditions == - cpu_to_le32(HS_CFG_CANCEL)) { - /* Return failure if no parameters for HS - enable */ - status = -1; - break; - } - - status = mwifiex_send_cmd(priv, - HostCmd_CMD_802_11_HS_CFG_ENH, - HostCmd_ACT_GEN_SET, 0, - &adapter->hs_cfg, - cmd_type == MWIFIEX_SYNC_CMD); - - if (hs_cfg->conditions == HS_CFG_CANCEL) - /* Restore previous condition */ - adapter->hs_cfg.conditions = - cpu_to_le32(prev_cond); - } else { - adapter->hs_cfg.conditions = - cpu_to_le32(hs_cfg->conditions); - adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; - adapter->hs_cfg.gap = (u8)hs_cfg->gap; - } - break; - case HostCmd_ACT_GEN_GET: - hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions); - hs_cfg->gpio = adapter->hs_cfg.gpio; - hs_cfg->gap = adapter->hs_cfg.gap; - break; - default: - status = -1; - break; - } - - return status; -} - -/* - * Sends IOCTL request to cancel the existing Host Sleep configuration. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type) -{ - struct mwifiex_ds_hs_cfg hscfg; - - hscfg.conditions = HS_CFG_CANCEL; - hscfg.is_invoke_hostcmd = true; - - return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, - cmd_type, &hscfg); -} -EXPORT_SYMBOL_GPL(mwifiex_cancel_hs); - -/* - * Sends IOCTL request to cancel the existing Host Sleep configuration. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int mwifiex_enable_hs(struct mwifiex_adapter *adapter) -{ - struct mwifiex_ds_hs_cfg hscfg; - struct mwifiex_private *priv; - int i; - - if (disconnect_on_suspend) { - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv) - mwifiex_deauthenticate(priv, NULL); - } - } - - if (adapter->hs_activated) { - mwifiex_dbg(adapter, CMD, - "cmd: HS Already activated\n"); - return true; - } - - adapter->hs_activate_wait_q_woken = false; - - memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); - hscfg.is_invoke_hostcmd = true; - - adapter->hs_enabling = true; - mwifiex_cancel_all_pending_cmd(adapter); - - if (mwifiex_set_hs_params(mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_STA), - HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD, - &hscfg)) { - mwifiex_dbg(adapter, ERROR, - "IOCTL request HS enable failed\n"); - return false; - } - - if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q, - adapter->hs_activate_wait_q_woken, - (10 * HZ)) <= 0) { - mwifiex_dbg(adapter, ERROR, - "hs_activate_wait_q terminated\n"); - return false; - } - - return true; -} -EXPORT_SYMBOL_GPL(mwifiex_enable_hs); - -/* - * IOCTL request handler to get BSS information. - * - * This function collates the information from different driver structures - * to send to the user. - */ -int mwifiex_get_bss_info(struct mwifiex_private *priv, - struct mwifiex_bss_info *info) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_bssdescriptor *bss_desc; - - if (!info) - return -1; - - bss_desc = &priv->curr_bss_params.bss_descriptor; - - info->bss_mode = priv->bss_mode; - - memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid)); - - memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN); - - info->bss_chan = bss_desc->channel; - - memcpy(info->country_code, adapter->country_code, - IEEE80211_COUNTRY_STRING_LEN); - - info->media_connected = priv->media_connected; - - info->max_power_level = priv->max_tx_power_level; - info->min_power_level = priv->min_tx_power_level; - - info->adhoc_state = priv->adhoc_state; - - info->bcn_nf_last = priv->bcn_nf_last; - - if (priv->sec_info.wep_enabled) - info->wep_status = true; - else - info->wep_status = false; - - info->is_hs_configured = adapter->is_hs_configured; - info->is_deep_sleep = adapter->is_deep_sleep; - - return 0; -} - -/* - * The function disables auto deep sleep mode. - */ -int mwifiex_disable_auto_ds(struct mwifiex_private *priv) -{ - struct mwifiex_ds_auto_ds auto_ds; - - auto_ds.auto_ds = DEEP_SLEEP_OFF; - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, - DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true); -} -EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds); - -/* - * Sends IOCTL request to get the data rate. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate) -{ - int ret; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, - HostCmd_ACT_GEN_GET, 0, NULL, true); - - if (!ret) { - if (priv->is_data_rate_auto) - *rate = mwifiex_index_to_data_rate(priv, priv->tx_rate, - priv->tx_htinfo); - else - *rate = priv->data_rate; - } - - return ret; -} - -/* - * IOCTL request handler to set tx power configuration. - * - * This function prepares the correct firmware command and - * issues it. - * - * For non-auto power mode, all the following power groups are set - - * - Modulation class HR/DSSS - * - Modulation class OFDM - * - Modulation class HTBW20 - * - Modulation class HTBW40 - */ -int mwifiex_set_tx_power(struct mwifiex_private *priv, - struct mwifiex_power_cfg *power_cfg) -{ - int ret; - struct host_cmd_ds_txpwr_cfg *txp_cfg; - struct mwifiex_types_power_group *pg_tlv; - struct mwifiex_power_group *pg; - u8 *buf; - u16 dbm = 0; - - if (!power_cfg->is_power_auto) { - dbm = (u16) power_cfg->power_level; - if ((dbm < priv->min_tx_power_level) || - (dbm > priv->max_tx_power_level)) { - mwifiex_dbg(priv->adapter, ERROR, - "txpower value %d dBm\t" - "is out of range (%d dBm-%d dBm)\n", - dbm, priv->min_tx_power_level, - priv->max_tx_power_level); - return -1; - } - } - buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf; - txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET); - if (!power_cfg->is_power_auto) { - txp_cfg->mode = cpu_to_le32(1); - pg_tlv = (struct mwifiex_types_power_group *) - (buf + sizeof(struct host_cmd_ds_txpwr_cfg)); - pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP); - pg_tlv->length = - cpu_to_le16(4 * sizeof(struct mwifiex_power_group)); - pg = (struct mwifiex_power_group *) - (buf + sizeof(struct host_cmd_ds_txpwr_cfg) - + sizeof(struct mwifiex_types_power_group)); - /* Power group for modulation class HR/DSSS */ - pg->first_rate_code = 0x00; - pg->last_rate_code = 0x03; - pg->modulation_class = MOD_CLASS_HR_DSSS; - pg->power_step = 0; - pg->power_min = (s8) dbm; - pg->power_max = (s8) dbm; - pg++; - /* Power group for modulation class OFDM */ - pg->first_rate_code = 0x00; - pg->last_rate_code = 0x07; - pg->modulation_class = MOD_CLASS_OFDM; - pg->power_step = 0; - pg->power_min = (s8) dbm; - pg->power_max = (s8) dbm; - pg++; - /* Power group for modulation class HTBW20 */ - pg->first_rate_code = 0x00; - pg->last_rate_code = 0x20; - pg->modulation_class = MOD_CLASS_HT; - pg->power_step = 0; - pg->power_min = (s8) dbm; - pg->power_max = (s8) dbm; - pg->ht_bandwidth = HT_BW_20; - pg++; - /* Power group for modulation class HTBW40 */ - pg->first_rate_code = 0x00; - pg->last_rate_code = 0x20; - pg->modulation_class = MOD_CLASS_HT; - pg->power_step = 0; - pg->power_min = (s8) dbm; - pg->power_max = (s8) dbm; - pg->ht_bandwidth = HT_BW_40; - } - ret = mwifiex_send_cmd(priv, HostCmd_CMD_TXPWR_CFG, - HostCmd_ACT_GEN_SET, 0, buf, true); - - kfree(buf); - return ret; -} - -/* - * IOCTL request handler to get power save mode. - * - * This function prepares the correct firmware command and - * issues it. - */ -int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode) -{ - int ret; - struct mwifiex_adapter *adapter = priv->adapter; - u16 sub_cmd; - - if (*ps_mode) - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; - else - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; - sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, - sub_cmd, BITMAP_STA_PS, NULL, true); - if ((!ret) && (sub_cmd == DIS_AUTO_PS)) - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, - GET_PS, 0, NULL, false); - - return ret; -} - -/* - * IOCTL request handler to set/reset WPA IE. - * - * The supplied WPA IE is treated as a opaque buffer. Only the first field - * is checked to determine WPA version. If buffer length is zero, the existing - * WPA IE is reset. - */ -static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, - u8 *ie_data_ptr, u16 ie_len) -{ - if (ie_len) { - if (ie_len > sizeof(priv->wpa_ie)) { - mwifiex_dbg(priv->adapter, ERROR, - "failed to copy WPA IE, too big\n"); - return -1; - } - memcpy(priv->wpa_ie, ie_data_ptr, ie_len); - priv->wpa_ie_len = (u8) ie_len; - mwifiex_dbg(priv->adapter, CMD, - "cmd: Set Wpa_ie_len=%d IE=%#x\n", - priv->wpa_ie_len, priv->wpa_ie[0]); - - if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) { - priv->sec_info.wpa_enabled = true; - } else if (priv->wpa_ie[0] == WLAN_EID_RSN) { - priv->sec_info.wpa2_enabled = true; - } else { - priv->sec_info.wpa_enabled = false; - priv->sec_info.wpa2_enabled = false; - } - } else { - memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie)); - priv->wpa_ie_len = 0; - mwifiex_dbg(priv->adapter, INFO, - "info: reset wpa_ie_len=%d IE=%#x\n", - priv->wpa_ie_len, priv->wpa_ie[0]); - priv->sec_info.wpa_enabled = false; - priv->sec_info.wpa2_enabled = false; - } - - return 0; -} - -/* - * IOCTL request handler to set/reset WAPI IE. - * - * The supplied WAPI IE is treated as a opaque buffer. Only the first field - * is checked to internally enable WAPI. If buffer length is zero, the existing - * WAPI IE is reset. - */ -static int mwifiex_set_wapi_ie(struct mwifiex_private *priv, - u8 *ie_data_ptr, u16 ie_len) -{ - if (ie_len) { - if (ie_len > sizeof(priv->wapi_ie)) { - mwifiex_dbg(priv->adapter, ERROR, - "info: failed to copy WAPI IE, too big\n"); - return -1; - } - memcpy(priv->wapi_ie, ie_data_ptr, ie_len); - priv->wapi_ie_len = ie_len; - mwifiex_dbg(priv->adapter, CMD, - "cmd: Set wapi_ie_len=%d IE=%#x\n", - priv->wapi_ie_len, priv->wapi_ie[0]); - - if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY) - priv->sec_info.wapi_enabled = true; - } else { - memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie)); - priv->wapi_ie_len = ie_len; - mwifiex_dbg(priv->adapter, INFO, - "info: Reset wapi_ie_len=%d IE=%#x\n", - priv->wapi_ie_len, priv->wapi_ie[0]); - priv->sec_info.wapi_enabled = false; - } - return 0; -} - -/* - * IOCTL request handler to set/reset WPS IE. - * - * The supplied WPS IE is treated as a opaque buffer. Only the first field - * is checked to internally enable WPS. If buffer length is zero, the existing - * WPS IE is reset. - */ -static int mwifiex_set_wps_ie(struct mwifiex_private *priv, - u8 *ie_data_ptr, u16 ie_len) -{ - if (ie_len) { - if (ie_len > MWIFIEX_MAX_VSIE_LEN) { - mwifiex_dbg(priv->adapter, ERROR, - "info: failed to copy WPS IE, too big\n"); - return -1; - } - - priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL); - if (!priv->wps_ie) - return -ENOMEM; - - memcpy(priv->wps_ie, ie_data_ptr, ie_len); - priv->wps_ie_len = ie_len; - mwifiex_dbg(priv->adapter, CMD, - "cmd: Set wps_ie_len=%d IE=%#x\n", - priv->wps_ie_len, priv->wps_ie[0]); - } else { - kfree(priv->wps_ie); - priv->wps_ie_len = ie_len; - mwifiex_dbg(priv->adapter, INFO, - "info: Reset wps_ie_len=%d\n", priv->wps_ie_len); - } - return 0; -} - -/* - * IOCTL request handler to set WAPI key. - * - * This function prepares the correct firmware command and - * issues it. - */ -static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv, - struct mwifiex_ds_encrypt_key *encrypt_key) -{ - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, - encrypt_key, true); -} - -/* - * IOCTL request handler to set WEP network key. - * - * This function prepares the correct firmware command and - * issues it, after validation checks. - */ -static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, - struct mwifiex_ds_encrypt_key *encrypt_key) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - struct mwifiex_wep_key *wep_key; - int index; - - if (priv->wep_key_curr_index >= NUM_WEP_KEYS) - priv->wep_key_curr_index = 0; - wep_key = &priv->wep_key[priv->wep_key_curr_index]; - index = encrypt_key->key_index; - if (encrypt_key->key_disable) { - priv->sec_info.wep_enabled = 0; - } else if (!encrypt_key->key_len) { - /* Copy the required key as the current key */ - wep_key = &priv->wep_key[index]; - if (!wep_key->key_length) { - mwifiex_dbg(adapter, ERROR, - "key not set, so cannot enable it\n"); - return -1; - } - - if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) { - memcpy(encrypt_key->key_material, - wep_key->key_material, wep_key->key_length); - encrypt_key->key_len = wep_key->key_length; - } - - priv->wep_key_curr_index = (u16) index; - priv->sec_info.wep_enabled = 1; - } else { - wep_key = &priv->wep_key[index]; - memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); - /* Copy the key in the driver */ - memcpy(wep_key->key_material, - encrypt_key->key_material, - encrypt_key->key_len); - wep_key->key_index = index; - wep_key->key_length = encrypt_key->key_len; - priv->sec_info.wep_enabled = 1; - } - if (wep_key->key_length) { - void *enc_key; - - if (encrypt_key->key_disable) { - memset(&priv->wep_key[index], 0, - sizeof(struct mwifiex_wep_key)); - if (wep_key->key_length) - goto done; - } - - if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) - enc_key = encrypt_key; - else - enc_key = NULL; - - /* Send request to firmware */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, 0, enc_key, false); - if (ret) - return ret; - } - -done: - if (priv->sec_info.wep_enabled) - priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; - else - priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter, true); - - return ret; -} - -/* - * IOCTL request handler to set WPA key. - * - * This function prepares the correct firmware command and - * issues it, after validation checks. - * - * Current driver only supports key length of up to 32 bytes. - * - * This function can also be used to disable a currently set key. - */ -static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv, - struct mwifiex_ds_encrypt_key *encrypt_key) -{ - int ret; - u8 remove_key = false; - struct host_cmd_ds_802_11_key_material *ibss_key; - - /* Current driver only supports key length of up to 32 bytes */ - if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) { - mwifiex_dbg(priv->adapter, ERROR, - "key length too long\n"); - return -1; - } - - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { - /* - * IBSS/WPA-None uses only one key (Group) for both receiving - * and sending unicast and multicast packets. - */ - /* Send the key as PTK to firmware */ - encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, - KEY_INFO_ENABLED, encrypt_key, false); - if (ret) - return ret; - - ibss_key = &priv->aes_key; - memset(ibss_key, 0, - sizeof(struct host_cmd_ds_802_11_key_material)); - /* Copy the key in the driver */ - memcpy(ibss_key->key_param_set.key, encrypt_key->key_material, - encrypt_key->key_len); - memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len, - sizeof(ibss_key->key_param_set.key_len)); - ibss_key->key_param_set.key_type_id - = cpu_to_le16(KEY_TYPE_ID_TKIP); - ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED); - - /* Send the key as GTK to firmware */ - encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST; - } - - if (!encrypt_key->key_index) - encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; - - if (remove_key) - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, - !KEY_INFO_ENABLED, encrypt_key, true); - else - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, - KEY_INFO_ENABLED, encrypt_key, true); - - return ret; -} - -/* - * IOCTL request handler to set/get network keys. - * - * This is a generic key handling function which supports WEP, WPA - * and WAPI. - */ -static int -mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv, - struct mwifiex_ds_encrypt_key *encrypt_key) -{ - int status; - - if (encrypt_key->is_wapi_key) - status = mwifiex_sec_ioctl_set_wapi_key(priv, encrypt_key); - else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104) - status = mwifiex_sec_ioctl_set_wpa_key(priv, encrypt_key); - else - status = mwifiex_sec_ioctl_set_wep_key(priv, encrypt_key); - return status; -} - -/* - * This function returns the driver version. - */ -int -mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version, - int max_len) -{ - union { - __le32 l; - u8 c[4]; - } ver; - char fw_ver[32]; - - ver.l = cpu_to_le32(adapter->fw_release_number); - sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]); - - snprintf(version, max_len, driver_version, fw_ver); - - mwifiex_dbg(adapter, MSG, "info: MWIFIEX VERSION: %s\n", version); - - return 0; -} - -/* - * Sends IOCTL request to set encoding parameters. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, - const u8 *key, int key_len, u8 key_index, - const u8 *mac_addr, int disable) -{ - struct mwifiex_ds_encrypt_key encrypt_key; - - memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key)); - encrypt_key.key_len = key_len; - encrypt_key.key_index = key_index; - - if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC) - encrypt_key.is_igtk_key = true; - - if (!disable) { - if (key_len) - memcpy(encrypt_key.key_material, key, key_len); - else - encrypt_key.is_current_wep_key = true; - - if (mac_addr) - memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); - if (kp && kp->seq && kp->seq_len) { - memcpy(encrypt_key.pn, kp->seq, kp->seq_len); - encrypt_key.pn_len = kp->seq_len; - encrypt_key.is_rx_seq_valid = true; - } - } else { - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) - return 0; - encrypt_key.key_disable = true; - if (mac_addr) - memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); - } - - return mwifiex_sec_ioctl_encrypt_key(priv, &encrypt_key); -} - -/* - * Sends IOCTL request to get extended version. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_get_ver_ext(struct mwifiex_private *priv) -{ - struct mwifiex_ver_ext ver_ext; - - memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext)); - if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT, - HostCmd_ACT_GEN_GET, 0, &ver_ext, true)) - return -1; - - return 0; -} - -int -mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, - struct ieee80211_channel *chan, - unsigned int duration) -{ - struct host_cmd_ds_remain_on_chan roc_cfg; - u8 sc; - - memset(&roc_cfg, 0, sizeof(roc_cfg)); - roc_cfg.action = cpu_to_le16(action); - if (action == HostCmd_ACT_GEN_SET) { - roc_cfg.band_cfg = chan->band; - sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT); - roc_cfg.band_cfg |= (sc << 2); - - roc_cfg.channel = - ieee80211_frequency_to_channel(chan->center_freq); - roc_cfg.duration = cpu_to_le32(duration); - } - if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN, - action, 0, &roc_cfg, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "failed to remain on channel\n"); - return -1; - } - - return roc_cfg.status; -} - -/* - * Sends IOCTL request to get statistics information. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_get_stats_info(struct mwifiex_private *priv, - struct mwifiex_ds_get_stats *log) -{ - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_GET_LOG, - HostCmd_ACT_GEN_GET, 0, log, true); -} - -/* - * IOCTL request handler to read/write register. - * - * This function prepares the correct firmware command and - * issues it. - * - * Access to the following registers are supported - - * - MAC - * - BBP - * - RF - * - PMIC - * - CAU - */ -static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv, - struct mwifiex_ds_reg_rw *reg_rw, - u16 action) -{ - u16 cmd_no; - - switch (le32_to_cpu(reg_rw->type)) { - case MWIFIEX_REG_MAC: - cmd_no = HostCmd_CMD_MAC_REG_ACCESS; - break; - case MWIFIEX_REG_BBP: - cmd_no = HostCmd_CMD_BBP_REG_ACCESS; - break; - case MWIFIEX_REG_RF: - cmd_no = HostCmd_CMD_RF_REG_ACCESS; - break; - case MWIFIEX_REG_PMIC: - cmd_no = HostCmd_CMD_PMIC_REG_ACCESS; - break; - case MWIFIEX_REG_CAU: - cmd_no = HostCmd_CMD_CAU_REG_ACCESS; - break; - default: - return -1; - } - - return mwifiex_send_cmd(priv, cmd_no, action, 0, reg_rw, true); -} - -/* - * Sends IOCTL request to write to a register. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, - u32 reg_offset, u32 reg_value) -{ - struct mwifiex_ds_reg_rw reg_rw; - - reg_rw.type = cpu_to_le32(reg_type); - reg_rw.offset = cpu_to_le32(reg_offset); - reg_rw.value = cpu_to_le32(reg_value); - - return mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_SET); -} - -/* - * Sends IOCTL request to read from a register. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, - u32 reg_offset, u32 *value) -{ - int ret; - struct mwifiex_ds_reg_rw reg_rw; - - reg_rw.type = cpu_to_le32(reg_type); - reg_rw.offset = cpu_to_le32(reg_offset); - ret = mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_GET); - - if (ret) - goto done; - - *value = le32_to_cpu(reg_rw.value); - -done: - return ret; -} - -/* - * Sends IOCTL request to read from EEPROM. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, - u8 *value) -{ - int ret; - struct mwifiex_ds_read_eeprom rd_eeprom; - - rd_eeprom.offset = cpu_to_le16((u16) offset); - rd_eeprom.byte_count = cpu_to_le16((u16) bytes); - - /* Send request to firmware */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS, - HostCmd_ACT_GEN_GET, 0, &rd_eeprom, true); - - if (!ret) - memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA); - return ret; -} - -/* - * This function sets a generic IE. In addition to generic IE, it can - * also handle WPA, WPA2 and WAPI IEs. - */ -static int -mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, - u16 ie_len) -{ - int ret = 0; - struct ieee_types_vendor_header *pvendor_ie; - const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; - const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; - - /* If the passed length is zero, reset the buffer */ - if (!ie_len) { - priv->gen_ie_buf_len = 0; - priv->wps.session_enable = false; - - return 0; - } else if (!ie_data_ptr) { - return -1; - } - pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; - /* Test to see if it is a WPA IE, if not, then it is a gen IE */ - if (((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && - (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) || - (pvendor_ie->element_id == WLAN_EID_RSN)) { - - /* IE is a WPA/WPA2 IE so call set_wpa function */ - ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len); - priv->wps.session_enable = false; - - return ret; - } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { - /* IE is a WAPI IE so call set_wapi function */ - ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len); - - return ret; - } - /* - * Verify that the passed length is not larger than the - * available space remaining in the buffer - */ - if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) { - - /* Test to see if it is a WPS IE, if so, enable - * wps session flag - */ - pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; - if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && - (!memcmp(pvendor_ie->oui, wps_oui, sizeof(wps_oui)))) { - priv->wps.session_enable = true; - mwifiex_dbg(priv->adapter, INFO, - "info: WPS Session Enabled.\n"); - ret = mwifiex_set_wps_ie(priv, ie_data_ptr, ie_len); - } - - /* Append the passed data to the end of the - genIeBuffer */ - memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr, - ie_len); - /* Increment the stored buffer length by the - size passed */ - priv->gen_ie_buf_len += ie_len; - } else { - /* Passed data does not fit in the remaining - buffer space */ - ret = -1; - } - - /* Return 0, or -1 for error case */ - return ret; -} - -/* - * IOCTL request handler to set/get generic IE. - * - * In addition to various generic IEs, this function can also be - * used to set the ARP filter. - */ -static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, - struct mwifiex_ds_misc_gen_ie *gen_ie, - u16 action) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - switch (gen_ie->type) { - case MWIFIEX_IE_TYPE_GEN_IE: - if (action == HostCmd_ACT_GEN_GET) { - gen_ie->len = priv->wpa_ie_len; - memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len); - } else { - mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data, - (u16) gen_ie->len); - } - break; - case MWIFIEX_IE_TYPE_ARP_FILTER: - memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter)); - if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) { - adapter->arp_filter_size = 0; - mwifiex_dbg(adapter, ERROR, - "invalid ARP filter size\n"); - return -1; - } else { - memcpy(adapter->arp_filter, gen_ie->ie_data, - gen_ie->len); - adapter->arp_filter_size = gen_ie->len; - } - break; - default: - mwifiex_dbg(adapter, ERROR, "invalid IE type\n"); - return -1; - } - return 0; -} - -/* - * Sends IOCTL request to set a generic IE. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len) -{ - struct mwifiex_ds_misc_gen_ie gen_ie; - - if (ie_len > IEEE_MAX_IE_SIZE) - return -EFAULT; - - gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE; - gen_ie.len = ie_len; - memcpy(gen_ie.ie_data, ie, ie_len); - if (mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET)) - return -EFAULT; - - return 0; -} diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c deleted file mode 100644 index d4d4cb1ce95b..000000000000 --- a/drivers/net/wireless/mwifiex/sta_rx.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Marvell Wireless LAN device driver: station RX data handling - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "11n_aggr.h" -#include "11n_rxreorder.h" - -/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement - * frame. If frame has both source and destination mac address as same, this - * function drops such gratuitous frames. - */ -static bool -mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - const struct mwifiex_arp_eth_header *arp; - struct ethhdr *eth; - struct ipv6hdr *ipv6; - struct icmp6hdr *icmpv6; - - eth = (struct ethhdr *)skb->data; - switch (ntohs(eth->h_proto)) { - case ETH_P_ARP: - arp = (void *)(skb->data + sizeof(struct ethhdr)); - if (arp->hdr.ar_op == htons(ARPOP_REPLY) || - arp->hdr.ar_op == htons(ARPOP_REQUEST)) { - if (!memcmp(arp->ar_sip, arp->ar_tip, 4)) - return true; - } - break; - case ETH_P_IPV6: - ipv6 = (void *)(skb->data + sizeof(struct ethhdr)); - icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) + - sizeof(struct ipv6hdr)); - if (NDISC_NEIGHBOUR_ADVERTISEMENT == icmpv6->icmp6_type) { - if (!memcmp(&ipv6->saddr, &ipv6->daddr, - sizeof(struct in6_addr))) - return true; - } - break; - default: - break; - } - - return false; -} - -/* - * This function processes the received packet and forwards it - * to kernel/upper layer. - * - * This function parses through the received packet and determines - * if it is a debug packet or normal packet. - * - * For non-debug packets, the function chops off unnecessary leading - * header bytes, reconstructs the packet as an ethernet frame or - * 802.2/llc/snap frame as required, and sends it to kernel/upper layer. - * - * The completion callback is called after processing in complete. - */ -int mwifiex_process_rx_packet(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - int ret; - struct rx_packet_hdr *rx_pkt_hdr; - struct rxpd *local_rx_pd; - int hdr_chop; - struct ethhdr *eth; - u16 rx_pkt_off, rx_pkt_len; - u8 *offset; - u8 adj_rx_rate = 0; - - local_rx_pd = (struct rxpd *) (skb->data); - - rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset); - rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length); - rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off; - - if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, - sizeof(bridge_tunnel_header))) || - (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, - sizeof(rfc1042_header)) && - ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && - ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { - /* - * Replace the 803 header and rfc1042 header (llc/snap) with an - * EthernetII header, keep the src/dst and snap_type - * (ethertype). - * The firmware only passes up SNAP frames converting - * all RX Data from 802.11 to 802.2/LLC/SNAP frames. - * To create the Ethernet II, just move the src, dst address - * right before the snap_type. - */ - eth = (struct ethhdr *) - ((u8 *) &rx_pkt_hdr->eth803_hdr - + sizeof(rx_pkt_hdr->eth803_hdr) + - sizeof(rx_pkt_hdr->rfc1042_hdr) - - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) - - sizeof(rx_pkt_hdr->eth803_hdr.h_source) - - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); - - memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source, - sizeof(eth->h_source)); - memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, - sizeof(eth->h_dest)); - - /* Chop off the rxpd + the excess memory from the 802.2/llc/snap - header that was removed. */ - hdr_chop = (u8 *) eth - (u8 *) local_rx_pd; - } else { - /* Chop off the rxpd */ - hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr - - (u8 *) local_rx_pd; - } - - /* Chop off the leading header bytes so the it points to the start of - either the reconstructed EthII frame or the 802.2/llc/snap frame */ - skb_pull(skb, hdr_chop); - - if (priv->hs2_enabled && - mwifiex_discard_gratuitous_arp(priv, skb)) { - mwifiex_dbg(priv->adapter, INFO, "Bypassed Gratuitous ARP\n"); - dev_kfree_skb_any(skb); - return 0; - } - - if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { - offset = (u8 *)local_rx_pd + rx_pkt_off; - mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len); - } - - priv->rxpd_rate = local_rx_pd->rx_rate; - - priv->rxpd_htinfo = local_rx_pd->ht_info; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || - GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - adj_rx_rate = mwifiex_adjust_data_rate(priv, priv->rxpd_rate, - priv->rxpd_htinfo); - mwifiex_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr, - local_rx_pd->nf); - } - - ret = mwifiex_recv_packet(priv, skb); - if (ret == -1) - mwifiex_dbg(priv->adapter, ERROR, - "recv packet failed\n"); - - return ret; -} - -/* - * This function processes the received buffer. - * - * The function looks into the RxPD and performs sanity tests on the - * received buffer to ensure its a valid packet, before processing it - * further. If the packet is determined to be aggregated, it is - * de-aggregated accordingly. Non-unicast packets are sent directly to - * the kernel/upper layers. Unicast packets are handed over to the - * Rx reordering routine if 11n is enabled. - * - * The completion callback is called after processing in complete. - */ -int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret = 0; - struct rxpd *local_rx_pd; - struct rx_packet_hdr *rx_pkt_hdr; - u8 ta[ETH_ALEN]; - u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num; - struct mwifiex_sta_node *sta_ptr; - - local_rx_pd = (struct rxpd *) (skb->data); - rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type); - rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset); - rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length); - seq_num = le16_to_cpu(local_rx_pd->seq_num); - - rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset; - - if ((rx_pkt_offset + rx_pkt_length) > (u16) skb->len) { - mwifiex_dbg(adapter, ERROR, - "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n", - skb->len, rx_pkt_offset, rx_pkt_length); - priv->stats.rx_dropped++; - dev_kfree_skb_any(skb); - return ret; - } - - if (rx_pkt_type == PKT_TYPE_MGMT) { - ret = mwifiex_process_mgmt_packet(priv, skb); - if (ret) - mwifiex_dbg(adapter, ERROR, "Rx of mgmt packet failed"); - dev_kfree_skb_any(skb); - return ret; - } - - /* - * If the packet is not an unicast packet then send the packet - * directly to os. Don't pass thru rx reordering - */ - if ((!IS_11N_ENABLED(priv) && - !(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - !(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) || - !ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) { - mwifiex_process_rx_packet(priv, skb); - return ret; - } - - if (mwifiex_queuing_ra_based(priv) || - (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) { - memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); - if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET && - local_rx_pd->priority < MAX_NUM_TID) { - sta_ptr = mwifiex_get_sta_entry(priv, ta); - if (sta_ptr) - sta_ptr->rx_seq[local_rx_pd->priority] = - le16_to_cpu(local_rx_pd->seq_num); - mwifiex_auto_tdls_update_peer_signal(priv, ta, - local_rx_pd->snr, - local_rx_pd->nf); - } - } else { - if (rx_pkt_type != PKT_TYPE_BAR) - priv->rx_seq[local_rx_pd->priority] = seq_num; - memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address, - ETH_ALEN); - } - - /* Reorder and send to OS */ - ret = mwifiex_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority, - ta, (u8) rx_pkt_type, skb); - - if (ret || (rx_pkt_type == PKT_TYPE_BAR)) - dev_kfree_skb_any(skb); - - if (ret) - priv->stats.rx_dropped++; - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c deleted file mode 100644 index f6683ea6bd5d..000000000000 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Marvell Wireless LAN device driver: station TX data handling - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" - -/* - * This function fills the TxPD for tx packets. - * - * The Tx buffer received by this function should already have the - * header space allocated for TxPD. - * - * This function inserts the TxPD in between interface header and actual - * data and adjusts the buffer pointers accordingly. - * - * The following TxPD fields are set by this function, as required - - * - BSS number - * - Tx packet length and offset - * - Priority - * - Packet delay - * - Priority specific Tx control - * - Flags - */ -void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct txpd *local_tx_pd; - struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - unsigned int pad; - u16 pkt_type, pkt_offset; - int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : - INTF_HEADER_LEN; - - if (!skb->len) { - mwifiex_dbg(adapter, ERROR, - "Tx: bad packet length: %d\n", skb->len); - tx_info->status_code = -1; - return skb->data; - } - - BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); - - pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; - - pad = ((void *)skb->data - (sizeof(*local_tx_pd) + hroom)- - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); - skb_push(skb, sizeof(*local_tx_pd) + pad); - - local_tx_pd = (struct txpd *) skb->data; - memset(local_tx_pd, 0, sizeof(struct txpd)); - local_tx_pd->bss_num = priv->bss_num; - local_tx_pd->bss_type = priv->bss_type; - local_tx_pd->tx_pkt_length = cpu_to_le16((u16)(skb->len - - (sizeof(struct txpd) + - pad))); - - local_tx_pd->priority = (u8) skb->priority; - local_tx_pd->pkt_delay_2ms = - mwifiex_wmm_compute_drv_pkt_delay(priv, skb); - - if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS || - tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) { - local_tx_pd->tx_token_id = tx_info->ack_frame_id; - local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS; - } - - if (local_tx_pd->priority < - ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) - /* - * Set the priority specific tx_control field, setting of 0 will - * cause the default value to be used later in this function - */ - local_tx_pd->tx_control = - cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd-> - priority]); - - if (adapter->pps_uapsd_mode) { - if (mwifiex_check_last_packet_indication(priv)) { - adapter->tx_lock_flag = true; - local_tx_pd->flags = - MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; - } - } - - if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) - local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; - - /* Offset of actual data */ - pkt_offset = sizeof(struct txpd) + pad; - if (pkt_type == PKT_TYPE_MGMT) { - /* Set the packet type and add header for management frame */ - local_tx_pd->tx_pkt_type = cpu_to_le16(pkt_type); - pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; - } - - local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset); - - /* make space for INTF_HEADER_LEN */ - skb_push(skb, hroom); - - if (!local_tx_pd->tx_control) - /* TxCtrl set by user or default */ - local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); - - return skb->data; -} - -/* - * This function tells firmware to send a NULL data packet. - * - * The function creates a NULL data packet with TxPD and sends to the - * firmware for transmission, with highest priority setting. - */ -int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct txpd *local_tx_pd; - struct mwifiex_tx_param tx_param; -/* sizeof(struct txpd) + Interface specific header */ -#define NULL_PACKET_HDR 64 - u32 data_len = NULL_PACKET_HDR; - struct sk_buff *skb; - int ret; - struct mwifiex_txinfo *tx_info = NULL; - - if (adapter->surprise_removed) - return -1; - - if (!priv->media_connected) - return -1; - - if (adapter->data_sent) - return -1; - - if (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv)) - return -1; - - skb = dev_alloc_skb(data_len); - if (!skb) - return -1; - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - tx_info->pkt_len = data_len - (sizeof(struct txpd) + INTF_HEADER_LEN); - skb_reserve(skb, sizeof(struct txpd) + INTF_HEADER_LEN); - skb_push(skb, sizeof(struct txpd)); - - local_tx_pd = (struct txpd *) skb->data; - local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); - local_tx_pd->flags = flags; - local_tx_pd->priority = WMM_HIGHEST_PRIORITY; - local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); - local_tx_pd->bss_num = priv->bss_num; - local_tx_pd->bss_type = priv->bss_type; - - if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, - skb, NULL); - } else { - skb_push(skb, INTF_HEADER_LEN); - tx_param.next_pkt_len = 0; - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, - skb, &tx_param); - } - switch (ret) { - case -EBUSY: - dev_kfree_skb_any(skb); - mwifiex_dbg(adapter, ERROR, - "%s: host_to_card failed: ret=%d\n", - __func__, ret); - adapter->dbg.num_tx_host_to_card_failure++; - break; - case -1: - dev_kfree_skb_any(skb); - mwifiex_dbg(adapter, ERROR, - "%s: host_to_card failed: ret=%d\n", - __func__, ret); - adapter->dbg.num_tx_host_to_card_failure++; - break; - case 0: - dev_kfree_skb_any(skb); - mwifiex_dbg(adapter, DATA, - "data: %s: host_to_card succeeded\n", - __func__); - adapter->tx_lock_flag = true; - break; - case -EINPROGRESS: - adapter->tx_lock_flag = true; - break; - default: - break; - } - - return ret; -} - -/* - * This function checks if we need to send last packet indication. - */ -u8 -mwifiex_check_last_packet_indication(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u8 ret = false; - - if (!adapter->sleep_period.period) - return ret; - if (mwifiex_wmm_lists_empty(adapter)) - ret = true; - - if (ret && !adapter->cmd_sent && !adapter->curr_cmd && - !is_command_pending(adapter)) { - adapter->delay_null_pkt = false; - ret = true; - } else { - ret = false; - adapter->delay_null_pkt = true; - } - return ret; -} diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c deleted file mode 100644 index 9275f9c3f869..000000000000 --- a/drivers/net/wireless/mwifiex/tdls.c +++ /dev/null @@ -1,1500 +0,0 @@ -/* Marvell Wireless LAN device driver: TDLS handling - * - * 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 "main.h" -#include "wmm.h" -#include "11n.h" -#include "11n_rxreorder.h" -#include "11ac.h" - -#define TDLS_REQ_FIX_LEN 6 -#define TDLS_RESP_FIX_LEN 8 -#define TDLS_CONFIRM_FIX_LEN 6 -#define MWIFIEX_TDLS_WMM_INFO_SIZE 7 - -static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv, - const u8 *mac, u8 status) -{ - struct mwifiex_ra_list_tbl *ra_list; - struct list_head *tid_list; - struct sk_buff *skb, *tmp; - struct mwifiex_txinfo *tx_info; - unsigned long flags; - u32 tid; - u8 tid_down; - - mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { - if (!ether_addr_equal(mac, skb->data)) - continue; - - __skb_unlink(skb, &priv->tdls_txq); - tx_info = MWIFIEX_SKB_TXCB(skb); - tid = skb->priority; - tid_down = mwifiex_wmm_downgrade_tid(priv, tid); - - if (mwifiex_is_tdls_link_setup(status)) { - ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac); - ra_list->tdls_link = true; - tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; - } else { - tid_list = &priv->wmm.tid_tbl_ptr[tid_down].ra_list; - if (!list_empty(tid_list)) - ra_list = list_first_entry(tid_list, - struct mwifiex_ra_list_tbl, list); - else - ra_list = NULL; - tx_info->flags &= ~MWIFIEX_BUF_FLAG_TDLS_PKT; - } - - if (!ra_list) { - mwifiex_write_data_complete(priv->adapter, skb, 0, -1); - continue; - } - - skb_queue_tail(&ra_list->skb_head, skb); - - ra_list->ba_pkt_count++; - ra_list->total_pkt_count++; - - if (atomic_read(&priv->wmm.highest_queued_prio) < - tos_to_tid_inv[tid_down]) - atomic_set(&priv->wmm.highest_queued_prio, - tos_to_tid_inv[tid_down]); - - atomic_inc(&priv->wmm.tx_pkts_queued); - } - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - return; -} - -static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, - const u8 *mac) -{ - struct mwifiex_ra_list_tbl *ra_list; - struct list_head *ra_list_head; - struct sk_buff *skb, *tmp; - unsigned long flags; - int i; - - mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - for (i = 0; i < MAX_NUM_TID; i++) { - if (!list_empty(&priv->wmm.tid_tbl_ptr[i].ra_list)) { - ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; - list_for_each_entry(ra_list, ra_list_head, list) { - skb_queue_walk_safe(&ra_list->skb_head, skb, - tmp) { - if (!ether_addr_equal(mac, skb->data)) - continue; - __skb_unlink(skb, &ra_list->skb_head); - atomic_dec(&priv->wmm.tx_pkts_queued); - ra_list->total_pkt_count--; - skb_queue_tail(&priv->tdls_txq, skb); - } - } - } - } - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - return; -} - -/* This function appends rate TLV to scan config command. */ -static int -mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - u8 rates[MWIFIEX_SUPPORTED_RATES], *pos; - u16 rates_size, supp_rates_size, ext_rates_size; - - memset(rates, 0, sizeof(rates)); - rates_size = mwifiex_get_supported_rates(priv, rates); - - supp_rates_size = min_t(u16, rates_size, MWIFIEX_TDLS_SUPPORTED_RATES); - - if (skb_tailroom(skb) < rates_size + 4) { - mwifiex_dbg(priv->adapter, ERROR, - "Insuffient space while adding rates\n"); - return -ENOMEM; - } - - pos = skb_put(skb, supp_rates_size + 2); - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = supp_rates_size; - memcpy(pos, rates, supp_rates_size); - - if (rates_size > MWIFIEX_TDLS_SUPPORTED_RATES) { - ext_rates_size = rates_size - MWIFIEX_TDLS_SUPPORTED_RATES; - pos = skb_put(skb, ext_rates_size + 2); - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = ext_rates_size; - memcpy(pos, rates + MWIFIEX_TDLS_SUPPORTED_RATES, - ext_rates_size); - } - - return 0; -} - -static void mwifiex_tdls_add_aid(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct ieee_types_assoc_rsp *assoc_rsp; - u8 *pos; - - assoc_rsp = (struct ieee_types_assoc_rsp *)&priv->assoc_rsp_buf; - pos = (void *)skb_put(skb, 4); - *pos++ = WLAN_EID_AID; - *pos++ = 2; - memcpy(pos, &assoc_rsp->a_id, sizeof(assoc_rsp->a_id)); - - return; -} - -static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct ieee80211_vht_cap vht_cap; - u8 *pos; - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); - *pos++ = WLAN_EID_VHT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_vht_cap); - - memset(&vht_cap, 0, sizeof(struct ieee80211_vht_cap)); - - mwifiex_fill_vht_cap_tlv(priv, &vht_cap, priv->curr_bss_params.band); - memcpy(pos, &vht_cap, sizeof(vht_cap)); - - return 0; -} - -static int -mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, - u8 vht_enabled, struct sk_buff *skb) -{ - struct ieee80211_ht_operation *ht_oper; - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_bssdescriptor *bss_desc = - &priv->curr_bss_params.bss_descriptor; - u8 *pos; - - sta_ptr = mwifiex_get_sta_entry(priv, mac); - if (unlikely(!sta_ptr)) { - mwifiex_dbg(priv->adapter, ERROR, - "TDLS peer station not found in list\n"); - return -1; - } - - if (!(le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info))) { - mwifiex_dbg(priv->adapter, WARN, - "TDLS peer doesn't support ht capabilities\n"); - return 0; - } - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2); - *pos++ = WLAN_EID_HT_OPERATION; - *pos++ = sizeof(struct ieee80211_ht_operation); - ht_oper = (void *)pos; - - ht_oper->primary_chan = bss_desc->channel; - - /* follow AP's channel bandwidth */ - if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && - bss_desc->bcn_ht_cap && - ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) - ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param; - - if (vht_enabled) { - ht_oper->ht_param = - mwifiex_get_sec_chan_offset(bss_desc->channel); - ht_oper->ht_param |= BIT(2); - } - - memcpy(&sta_ptr->tdls_cap.ht_oper, ht_oper, - sizeof(struct ieee80211_ht_operation)); - - return 0; -} - -static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, - const u8 *mac, struct sk_buff *skb) -{ - struct mwifiex_bssdescriptor *bss_desc; - struct ieee80211_vht_operation *vht_oper; - struct ieee80211_vht_cap *vht_cap, *ap_vht_cap = NULL; - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_adapter *adapter = priv->adapter; - u8 supp_chwd_set, peer_supp_chwd_set; - u8 *pos, ap_supp_chwd_set, chan_bw; - u16 mcs_map_user, mcs_map_resp, mcs_map_result; - u16 mcs_user, mcs_resp, nss; - u32 usr_vht_cap_info; - - bss_desc = &priv->curr_bss_params.bss_descriptor; - - sta_ptr = mwifiex_get_sta_entry(priv, mac); - if (unlikely(!sta_ptr)) { - mwifiex_dbg(adapter, ERROR, - "TDLS peer station not found in list\n"); - return -1; - } - - if (!(le32_to_cpu(sta_ptr->tdls_cap.vhtcap.vht_cap_info))) { - mwifiex_dbg(adapter, WARN, - "TDLS peer doesn't support vht capabilities\n"); - return 0; - } - - if (!mwifiex_is_bss_in_11ac_mode(priv)) { - if (sta_ptr->tdls_cap.extcap.ext_capab[7] & - WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { - mwifiex_dbg(adapter, WARN, - "TDLS peer doesn't support wider bandwidth\n"); - return 0; - } - } else { - ap_vht_cap = bss_desc->bcn_vht_cap; - } - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_operation) + 2); - *pos++ = WLAN_EID_VHT_OPERATION; - *pos++ = sizeof(struct ieee80211_vht_operation); - vht_oper = (struct ieee80211_vht_operation *)pos; - - if (bss_desc->bss_band & BAND_A) - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; - else - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; - - /* find the minmum bandwith between AP/TDLS peers */ - vht_cap = &sta_ptr->tdls_cap.vhtcap; - supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); - peer_supp_chwd_set = - GET_VHTCAP_CHWDSET(le32_to_cpu(vht_cap->vht_cap_info)); - supp_chwd_set = min_t(u8, supp_chwd_set, peer_supp_chwd_set); - - /* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */ - - if (ap_vht_cap && sta_ptr->tdls_cap.extcap.ext_capab[7] & - WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { - ap_supp_chwd_set = - GET_VHTCAP_CHWDSET(le32_to_cpu(ap_vht_cap->vht_cap_info)); - supp_chwd_set = min_t(u8, supp_chwd_set, ap_supp_chwd_set); - } - - switch (supp_chwd_set) { - case IEEE80211_VHT_CHANWIDTH_80MHZ: - vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; - break; - case IEEE80211_VHT_CHANWIDTH_160MHZ: - vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; - break; - case IEEE80211_VHT_CHANWIDTH_80P80MHZ: - vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; - break; - default: - vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; - break; - } - - mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); - mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); - mcs_map_result = 0; - - for (nss = 1; nss <= 8; nss++) { - mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); - mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); - - if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || - (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) - SET_VHTNSSMCS(mcs_map_result, nss, - IEEE80211_VHT_MCS_NOT_SUPPORTED); - else - SET_VHTNSSMCS(mcs_map_result, nss, - min_t(u16, mcs_user, mcs_resp)); - } - - vht_oper->basic_mcs_set = cpu_to_le16(mcs_map_result); - - switch (vht_oper->chan_width) { - case IEEE80211_VHT_CHANWIDTH_80MHZ: - chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; - break; - case IEEE80211_VHT_CHANWIDTH_160MHZ: - chan_bw = IEEE80211_VHT_CHANWIDTH_160MHZ; - break; - case IEEE80211_VHT_CHANWIDTH_80P80MHZ: - chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; - break; - default: - chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT; - break; - } - vht_oper->center_freq_seg1_idx = - mwifiex_get_center_freq_index(priv, BAND_AAC, - bss_desc->channel, - chan_bw); - - return 0; -} - -static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct ieee_types_extcap *extcap; - - extcap = (void *)skb_put(skb, sizeof(struct ieee_types_extcap)); - extcap->ieee_hdr.element_id = WLAN_EID_EXT_CAPABILITY; - extcap->ieee_hdr.len = 8; - memset(extcap->ext_capab, 0, 8); - extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED; - extcap->ext_capab[3] |= WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH; - - if (priv->adapter->is_hw_11ac_capable) - extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED; -} - -static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb) -{ - u8 *pos = (void *)skb_put(skb, 3); - - *pos++ = WLAN_EID_QOS_CAPA; - *pos++ = 1; - *pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB; -} - -static void -mwifiex_tdls_add_wmm_param_ie(struct mwifiex_private *priv, struct sk_buff *skb) -{ - struct ieee80211_wmm_param_ie *wmm; - u8 ac_vi[] = {0x42, 0x43, 0x5e, 0x00}; - u8 ac_vo[] = {0x62, 0x32, 0x2f, 0x00}; - u8 ac_be[] = {0x03, 0xa4, 0x00, 0x00}; - u8 ac_bk[] = {0x27, 0xa4, 0x00, 0x00}; - - wmm = (void *)skb_put(skb, sizeof(*wmm)); - memset(wmm, 0, sizeof(*wmm)); - - wmm->element_id = WLAN_EID_VENDOR_SPECIFIC; - wmm->len = sizeof(*wmm) - 2; - wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */ - wmm->oui[1] = 0x50; - wmm->oui[2] = 0xf2; - wmm->oui_type = 2; /* WME */ - wmm->oui_subtype = 1; /* WME param */ - wmm->version = 1; /* WME ver */ - wmm->qos_info = 0; /* U-APSD not in use */ - - /* use default WMM AC parameters for TDLS link*/ - memcpy(&wmm->ac[0], ac_be, sizeof(ac_be)); - memcpy(&wmm->ac[1], ac_bk, sizeof(ac_bk)); - memcpy(&wmm->ac[2], ac_vi, sizeof(ac_vi)); - memcpy(&wmm->ac[3], ac_vo, sizeof(ac_vo)); -} - -static void -mwifiex_add_wmm_info_ie(struct mwifiex_private *priv, struct sk_buff *skb, - u8 qosinfo) -{ - u8 *buf; - - buf = (void *)skb_put(skb, MWIFIEX_TDLS_WMM_INFO_SIZE + - sizeof(struct ieee_types_header)); - - *buf++ = WLAN_EID_VENDOR_SPECIFIC; - *buf++ = 7; /* len */ - *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */ - *buf++ = 0x50; - *buf++ = 0xf2; - *buf++ = 2; /* WME */ - *buf++ = 0; /* WME info */ - *buf++ = 1; /* WME ver */ - *buf++ = qosinfo; /* U-APSD no in use */ -} - -static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, - const u8 *peer, u8 action_code, - u8 dialog_token, - u16 status_code, struct sk_buff *skb) -{ - struct ieee80211_tdls_data *tf; - int ret; - u16 capab; - struct ieee80211_ht_cap *ht_cap; - u8 radio, *pos; - - capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; - - tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); - memcpy(tf->da, peer, ETH_ALEN); - memcpy(tf->sa, priv->curr_addr, ETH_ALEN); - tf->ether_type = cpu_to_be16(ETH_P_TDLS); - tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; - - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_SETUP_REQUEST; - skb_put(skb, sizeof(tf->u.setup_req)); - tf->u.setup_req.dialog_token = dialog_token; - tf->u.setup_req.capability = cpu_to_le16(capab); - ret = mwifiex_tdls_append_rates_ie(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - ht_cap = (void *)pos; - radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - ret = mwifiex_fill_cap_info(priv, radio, ht_cap); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - if (priv->adapter->is_hw_11ac_capable) { - ret = mwifiex_tdls_add_vht_capab(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - mwifiex_tdls_add_aid(priv, skb); - } - - mwifiex_tdls_add_ext_capab(priv, skb); - mwifiex_tdls_add_qos_capab(skb); - mwifiex_add_wmm_info_ie(priv, skb, 0); - break; - - case WLAN_TDLS_SETUP_RESPONSE: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_SETUP_RESPONSE; - skb_put(skb, sizeof(tf->u.setup_resp)); - tf->u.setup_resp.status_code = cpu_to_le16(status_code); - tf->u.setup_resp.dialog_token = dialog_token; - tf->u.setup_resp.capability = cpu_to_le16(capab); - ret = mwifiex_tdls_append_rates_ie(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - ht_cap = (void *)pos; - radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - ret = mwifiex_fill_cap_info(priv, radio, ht_cap); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - if (priv->adapter->is_hw_11ac_capable) { - ret = mwifiex_tdls_add_vht_capab(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - mwifiex_tdls_add_aid(priv, skb); - } - - mwifiex_tdls_add_ext_capab(priv, skb); - mwifiex_tdls_add_qos_capab(skb); - mwifiex_add_wmm_info_ie(priv, skb, 0); - break; - - case WLAN_TDLS_SETUP_CONFIRM: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_SETUP_CONFIRM; - skb_put(skb, sizeof(tf->u.setup_cfm)); - tf->u.setup_cfm.status_code = cpu_to_le16(status_code); - tf->u.setup_cfm.dialog_token = dialog_token; - - mwifiex_tdls_add_wmm_param_ie(priv, skb); - if (priv->adapter->is_hw_11ac_capable) { - ret = mwifiex_tdls_add_vht_oper(priv, peer, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - ret = mwifiex_tdls_add_ht_oper(priv, peer, 1, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - } else { - ret = mwifiex_tdls_add_ht_oper(priv, peer, 0, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - } - break; - - case WLAN_TDLS_TEARDOWN: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_TEARDOWN; - skb_put(skb, sizeof(tf->u.teardown)); - tf->u.teardown.reason_code = cpu_to_le16(status_code); - break; - - case WLAN_TDLS_DISCOVERY_REQUEST: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; - skb_put(skb, sizeof(tf->u.discover_req)); - tf->u.discover_req.dialog_token = dialog_token; - break; - default: - mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS frame type.\n"); - return -EINVAL; - } - - return 0; -} - -static void -mwifiex_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, - const u8 *peer, const u8 *bssid) -{ - struct ieee80211_tdls_lnkie *lnkid; - - lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); - lnkid->ie_type = WLAN_EID_LINK_ID; - lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - - sizeof(struct ieee_types_header); - - memcpy(lnkid->bssid, bssid, ETH_ALEN); - memcpy(lnkid->init_sta, src_addr, ETH_ALEN); - memcpy(lnkid->resp_sta, peer, ETH_ALEN); -} - -int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, - u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len) -{ - struct sk_buff *skb; - struct mwifiex_txinfo *tx_info; - int ret; - u16 skb_len; - - skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + - max(sizeof(struct ieee80211_mgmt), - sizeof(struct ieee80211_tdls_data)) + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + - MWIFIEX_SUPPORTED_RATES + - 3 + /* Qos Info */ - sizeof(struct ieee_types_extcap) + - sizeof(struct ieee80211_ht_cap) + - sizeof(struct ieee_types_bss_co_2040) + - sizeof(struct ieee80211_ht_operation) + - sizeof(struct ieee80211_tdls_lnkie) + - sizeof(struct ieee80211_wmm_param_ie) + - extra_ies_len; - - if (priv->adapter->is_hw_11ac_capable) - skb_len += sizeof(struct ieee_types_vht_cap) + - sizeof(struct ieee_types_vht_oper) + - sizeof(struct ieee_types_aid); - - skb = dev_alloc_skb(skb_len); - if (!skb) { - mwifiex_dbg(priv->adapter, ERROR, - "allocate skb failed for management frame\n"); - return -ENOMEM; - } - skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); - - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - case WLAN_TDLS_SETUP_CONFIRM: - case WLAN_TDLS_TEARDOWN: - case WLAN_TDLS_DISCOVERY_REQUEST: - ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, - dialog_token, status_code, - skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - if (extra_ies_len) - memcpy(skb_put(skb, extra_ies_len), extra_ies, - extra_ies_len); - mwifiex_tdls_add_link_ie(skb, priv->curr_addr, peer, - priv->cfg_bssid); - break; - case WLAN_TDLS_SETUP_RESPONSE: - ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, - dialog_token, status_code, - skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - if (extra_ies_len) - memcpy(skb_put(skb, extra_ies_len), extra_ies, - extra_ies_len); - mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, - priv->cfg_bssid); - break; - } - - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - case WLAN_TDLS_SETUP_RESPONSE: - skb->priority = MWIFIEX_PRIO_BK; - break; - default: - skb->priority = MWIFIEX_PRIO_VI; - break; - } - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - - __net_timestamp(skb); - mwifiex_queue_tx_pkt(priv, skb); - - return 0; -} - -static int -mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, - const u8 *peer, - u8 action_code, u8 dialog_token, - u16 status_code, struct sk_buff *skb) -{ - struct ieee80211_mgmt *mgmt; - u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - int ret; - u16 capab; - struct ieee80211_ht_cap *ht_cap; - u8 radio, *pos; - - capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; - - mgmt = (void *)skb_put(skb, offsetof(struct ieee80211_mgmt, u)); - - memset(mgmt, 0, 24); - memcpy(mgmt->da, peer, ETH_ALEN); - memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN); - memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN); - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_ACTION); - - /* add address 4 */ - pos = skb_put(skb, ETH_ALEN); - - switch (action_code) { - case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - skb_put(skb, sizeof(mgmt->u.action.u.tdls_discover_resp) + 1); - mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; - mgmt->u.action.u.tdls_discover_resp.action_code = - WLAN_PUB_ACTION_TDLS_DISCOVER_RES; - mgmt->u.action.u.tdls_discover_resp.dialog_token = - dialog_token; - mgmt->u.action.u.tdls_discover_resp.capability = - cpu_to_le16(capab); - /* move back for addr4 */ - memmove(pos + ETH_ALEN, &mgmt->u.action.category, - sizeof(mgmt->u.action.u.tdls_discover_resp)); - /* init address 4 */ - memcpy(pos, bc_addr, ETH_ALEN); - - ret = mwifiex_tdls_append_rates_ie(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - ht_cap = (void *)pos; - radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - ret = mwifiex_fill_cap_info(priv, radio, ht_cap); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - if (priv->adapter->is_hw_11ac_capable) { - ret = mwifiex_tdls_add_vht_capab(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - mwifiex_tdls_add_aid(priv, skb); - } - - mwifiex_tdls_add_ext_capab(priv, skb); - mwifiex_tdls_add_qos_capab(skb); - break; - default: - mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS action frame type\n"); - return -EINVAL; - } - - return 0; -} - -int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, - u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len) -{ - struct sk_buff *skb; - struct mwifiex_txinfo *tx_info; - u8 *pos; - u32 pkt_type, tx_control; - u16 pkt_len, skb_len; - - skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + - max(sizeof(struct ieee80211_mgmt), - sizeof(struct ieee80211_tdls_data)) + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + - MWIFIEX_SUPPORTED_RATES + - sizeof(struct ieee_types_extcap) + - sizeof(struct ieee80211_ht_cap) + - sizeof(struct ieee_types_bss_co_2040) + - sizeof(struct ieee80211_ht_operation) + - sizeof(struct ieee80211_tdls_lnkie) + - extra_ies_len + - 3 + /* Qos Info */ - ETH_ALEN; /* Address4 */ - - if (priv->adapter->is_hw_11ac_capable) - skb_len += sizeof(struct ieee_types_vht_cap) + - sizeof(struct ieee_types_vht_oper) + - sizeof(struct ieee_types_aid); - - skb = dev_alloc_skb(skb_len); - if (!skb) { - mwifiex_dbg(priv->adapter, ERROR, - "allocate skb failed for management frame\n"); - return -ENOMEM; - } - - skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); - - pkt_type = PKT_TYPE_MGMT; - tx_control = 0; - pos = skb_put(skb, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); - memset(pos, 0, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); - memcpy(pos, &pkt_type, sizeof(pkt_type)); - memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control)); - - if (mwifiex_construct_tdls_action_frame(priv, peer, action_code, - dialog_token, status_code, - skb)) { - dev_kfree_skb_any(skb); - return -EINVAL; - } - - if (extra_ies_len) - memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); - - /* the TDLS link IE is always added last we are the responder */ - - mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, - priv->cfg_bssid); - - skb->priority = MWIFIEX_PRIO_VI; - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; - - pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len); - memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len, - sizeof(pkt_len)); - __net_timestamp(skb); - mwifiex_queue_tx_pkt(priv, skb); - - return 0; -} - -/* This function process tdls action frame from peer. - * Peer capabilities are stored into station node structure. - */ -void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, - u8 *buf, int len) -{ - struct mwifiex_sta_node *sta_ptr; - u8 *peer, *pos, *end; - u8 i, action, basic; - __le16 cap = 0; - int ie_len = 0; - - if (len < (sizeof(struct ethhdr) + 3)) - return; - if (*(buf + sizeof(struct ethhdr)) != WLAN_TDLS_SNAP_RFTYPE) - return; - if (*(buf + sizeof(struct ethhdr) + 1) != WLAN_CATEGORY_TDLS) - return; - - peer = buf + ETH_ALEN; - action = *(buf + sizeof(struct ethhdr) + 2); - mwifiex_dbg(priv->adapter, DATA, - "rx:tdls action: peer=%pM, action=%d\n", peer, action); - - switch (action) { - case WLAN_TDLS_SETUP_REQUEST: - if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN)) - return; - - pos = buf + sizeof(struct ethhdr) + 4; - /* payload 1+ category 1 + action 1 + dialog 1 */ - cap = cpu_to_le16(*(u16 *)pos); - ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; - pos += 2; - break; - - case WLAN_TDLS_SETUP_RESPONSE: - if (len < (sizeof(struct ethhdr) + TDLS_RESP_FIX_LEN)) - return; - /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/ - pos = buf + sizeof(struct ethhdr) + 6; - cap = cpu_to_le16(*(u16 *)pos); - ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; - pos += 2; - break; - - case WLAN_TDLS_SETUP_CONFIRM: - if (len < (sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN)) - return; - pos = buf + sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN; - ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN; - break; - default: - mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS frame type.\n"); - return; - } - - sta_ptr = mwifiex_add_sta_entry(priv, peer); - if (!sta_ptr) - return; - - sta_ptr->tdls_cap.capab = cap; - - for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { - if (pos + 2 + pos[1] > end) - break; - - switch (*pos) { - case WLAN_EID_SUPP_RATES: - sta_ptr->tdls_cap.rates_len = pos[1]; - for (i = 0; i < pos[1]; i++) - sta_ptr->tdls_cap.rates[i] = pos[i + 2]; - break; - - case WLAN_EID_EXT_SUPP_RATES: - basic = sta_ptr->tdls_cap.rates_len; - for (i = 0; i < pos[1]; i++) - sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2]; - sta_ptr->tdls_cap.rates_len += pos[1]; - break; - case WLAN_EID_HT_CAPABILITY: - memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos, - sizeof(struct ieee80211_ht_cap)); - sta_ptr->is_11n_enabled = 1; - break; - case WLAN_EID_HT_OPERATION: - memcpy(&sta_ptr->tdls_cap.ht_oper, pos, - sizeof(struct ieee80211_ht_operation)); - break; - case WLAN_EID_BSS_COEX_2040: - sta_ptr->tdls_cap.coex_2040 = pos[2]; - break; - case WLAN_EID_EXT_CAPABILITY: - memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos, - sizeof(struct ieee_types_header) + - min_t(u8, pos[1], 8)); - break; - case WLAN_EID_RSN: - memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos, - sizeof(struct ieee_types_header) + - min_t(u8, pos[1], IEEE_MAX_IE_SIZE - - sizeof(struct ieee_types_header))); - break; - case WLAN_EID_QOS_CAPA: - sta_ptr->tdls_cap.qos_info = pos[2]; - break; - case WLAN_EID_VHT_OPERATION: - if (priv->adapter->is_hw_11ac_capable) - memcpy(&sta_ptr->tdls_cap.vhtoper, pos, - sizeof(struct ieee80211_vht_operation)); - break; - case WLAN_EID_VHT_CAPABILITY: - if (priv->adapter->is_hw_11ac_capable) { - memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos, - sizeof(struct ieee80211_vht_cap)); - sta_ptr->is_11ac_enabled = 1; - } - break; - case WLAN_EID_AID: - if (priv->adapter->is_hw_11ac_capable) - sta_ptr->tdls_cap.aid = - le16_to_cpu(*(__le16 *)(pos + 2)); - default: - break; - } - } - - return; -} - -static int -mwifiex_tdls_process_config_link(struct mwifiex_private *priv, const u8 *peer) -{ - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_ds_tdls_oper tdls_oper; - - memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); - sta_ptr = mwifiex_get_sta_entry(priv, peer); - - if (!sta_ptr || sta_ptr->tdls_status == TDLS_SETUP_FAILURE) { - mwifiex_dbg(priv->adapter, ERROR, - "link absent for peer %pM; cannot config\n", peer); - return -EINVAL; - } - - memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); - tdls_oper.tdls_action = MWIFIEX_TDLS_CONFIG_LINK; - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, - HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); -} - -static int -mwifiex_tdls_process_create_link(struct mwifiex_private *priv, const u8 *peer) -{ - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_ds_tdls_oper tdls_oper; - - memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); - sta_ptr = mwifiex_get_sta_entry(priv, peer); - - if (sta_ptr && sta_ptr->tdls_status == TDLS_SETUP_INPROGRESS) { - mwifiex_dbg(priv->adapter, WARN, - "Setup already in progress for peer %pM\n", peer); - return 0; - } - - sta_ptr = mwifiex_add_sta_entry(priv, peer); - if (!sta_ptr) - return -ENOMEM; - - sta_ptr->tdls_status = TDLS_SETUP_INPROGRESS; - mwifiex_hold_tdls_packets(priv, peer); - memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); - tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK; - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, - HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); -} - -static int -mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer) -{ - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_ds_tdls_oper tdls_oper; - unsigned long flags; - - memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); - sta_ptr = mwifiex_get_sta_entry(priv, peer); - - if (sta_ptr) { - if (sta_ptr->is_11n_enabled) { - mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, - flags); - mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); - } - mwifiex_del_sta_entry(priv, peer); - } - - mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); - mwifiex_auto_tdls_update_peer_status(priv, peer, TDLS_NOT_SETUP); - memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); - tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, - HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); -} - -static int -mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer) -{ - struct mwifiex_sta_node *sta_ptr; - struct ieee80211_mcs_info mcs; - unsigned long flags; - int i; - - sta_ptr = mwifiex_get_sta_entry(priv, peer); - - if (sta_ptr && (sta_ptr->tdls_status != TDLS_SETUP_FAILURE)) { - mwifiex_dbg(priv->adapter, MSG, - "tdls: enable link %pM success\n", peer); - - sta_ptr->tdls_status = TDLS_SETUP_COMPLETE; - - mcs = sta_ptr->tdls_cap.ht_capb.mcs; - if (mcs.rx_mask[0] != 0xff) - sta_ptr->is_11n_enabled = true; - if (sta_ptr->is_11n_enabled) { - if (le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info) & - IEEE80211_HT_CAP_MAX_AMSDU) - sta_ptr->max_amsdu = - MWIFIEX_TX_DATA_BUF_SIZE_8K; - else - sta_ptr->max_amsdu = - MWIFIEX_TX_DATA_BUF_SIZE_4K; - - for (i = 0; i < MAX_NUM_TID; i++) - sta_ptr->ampdu_sta[i] = - priv->aggr_prio_tbl[i].ampdu_user; - } else { - for (i = 0; i < MAX_NUM_TID; i++) - sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; - } - if (sta_ptr->tdls_cap.extcap.ext_capab[3] & - WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) { - mwifiex_config_tdls_enable(priv); - mwifiex_config_tdls_cs_params(priv); - } - - memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq)); - mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE); - mwifiex_auto_tdls_update_peer_status(priv, peer, - TDLS_SETUP_COMPLETE); - } else { - mwifiex_dbg(priv->adapter, ERROR, - "tdls: enable link %pM failed\n", peer); - if (sta_ptr) { - mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, - flags); - mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); - mwifiex_del_sta_entry(priv, peer); - } - mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); - mwifiex_auto_tdls_update_peer_status(priv, peer, - TDLS_NOT_SETUP); - - return -1; - } - - return 0; -} - -int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action) -{ - switch (action) { - case MWIFIEX_TDLS_ENABLE_LINK: - return mwifiex_tdls_process_enable_link(priv, peer); - case MWIFIEX_TDLS_DISABLE_LINK: - return mwifiex_tdls_process_disable_link(priv, peer); - case MWIFIEX_TDLS_CREATE_LINK: - return mwifiex_tdls_process_create_link(priv, peer); - case MWIFIEX_TDLS_CONFIG_LINK: - return mwifiex_tdls_process_config_link(priv, peer); - } - return 0; -} - -int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac) -{ - struct mwifiex_sta_node *sta_ptr; - - sta_ptr = mwifiex_get_sta_entry(priv, mac); - if (sta_ptr) - return sta_ptr->tdls_status; - - return TDLS_NOT_SETUP; -} - -int mwifiex_get_tdls_list(struct mwifiex_private *priv, - struct tdls_peer_info *buf) -{ - struct mwifiex_sta_node *sta_ptr; - struct tdls_peer_info *peer = buf; - int count = 0; - unsigned long flags; - - if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) - return 0; - - /* make sure we are in station mode and connected */ - if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) - return 0; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - list_for_each_entry(sta_ptr, &priv->sta_list, list) { - if (mwifiex_is_tdls_link_setup(sta_ptr->tdls_status)) { - ether_addr_copy(peer->peer_addr, sta_ptr->mac_addr); - peer++; - count++; - if (count >= MWIFIEX_MAX_TDLS_PEER_SUPPORTED) - break; - } - } - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - return count; -} - -void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) -{ - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_ds_tdls_oper tdls_oper; - unsigned long flags; - - if (list_empty(&priv->sta_list)) - return; - - list_for_each_entry(sta_ptr, &priv->sta_list, list) { - memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); - - if (sta_ptr->is_11n_enabled) { - mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, - flags); - mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); - } - - mwifiex_restore_tdls_packets(priv, sta_ptr->mac_addr, - TDLS_LINK_TEARDOWN); - memcpy(&tdls_oper.peer_mac, sta_ptr->mac_addr, ETH_ALEN); - tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; - if (mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, - HostCmd_ACT_GEN_SET, 0, &tdls_oper, false)) - mwifiex_dbg(priv->adapter, ERROR, - "Disable link failed for TDLS peer %pM", - sta_ptr->mac_addr); - } - - mwifiex_del_all_sta_list(priv); -} - -int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb) -{ - struct mwifiex_auto_tdls_peer *peer; - unsigned long flags; - u8 mac[ETH_ALEN]; - - ether_addr_copy(mac, skb->data); - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry(peer, &priv->auto_tdls_list, list) { - if (!memcmp(mac, peer->mac_addr, ETH_ALEN)) { - if (peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH && - peer->tdls_status == TDLS_NOT_SETUP && - (peer->failure_count < - MWIFIEX_TDLS_MAX_FAIL_COUNT)) { - peer->tdls_status = TDLS_SETUP_INPROGRESS; - mwifiex_dbg(priv->adapter, INFO, - "setup TDLS link, peer=%pM rssi=%d\n", - peer->mac_addr, peer->rssi); - - cfg80211_tdls_oper_request(priv->netdev, - peer->mac_addr, - NL80211_TDLS_SETUP, - 0, GFP_ATOMIC); - peer->do_setup = false; - priv->check_tdls_tx = false; - } else if (peer->failure_count < - MWIFIEX_TDLS_MAX_FAIL_COUNT && - peer->do_discover) { - mwifiex_send_tdls_data_frame(priv, - peer->mac_addr, - WLAN_TDLS_DISCOVERY_REQUEST, - 1, 0, NULL, 0); - peer->do_discover = false; - } - } - } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); - - return 0; -} - -void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv) -{ - struct mwifiex_auto_tdls_peer *peer, *tmp_node; - unsigned long flags; - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry_safe(peer, tmp_node, &priv->auto_tdls_list, list) { - list_del(&peer->list); - kfree(peer); - } - - INIT_LIST_HEAD(&priv->auto_tdls_list); - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); - priv->check_tdls_tx = false; -} - -void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac) -{ - struct mwifiex_auto_tdls_peer *tdls_peer; - unsigned long flags; - - if (!priv->adapter->auto_tdls) - return; - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { - if (!memcmp(tdls_peer->mac_addr, mac, ETH_ALEN)) { - tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS; - tdls_peer->rssi_jiffies = jiffies; - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); - return; - } - } - - /* create new TDLS peer */ - tdls_peer = kzalloc(sizeof(*tdls_peer), GFP_ATOMIC); - if (tdls_peer) { - ether_addr_copy(tdls_peer->mac_addr, mac); - tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS; - tdls_peer->rssi_jiffies = jiffies; - INIT_LIST_HEAD(&tdls_peer->list); - list_add_tail(&tdls_peer->list, &priv->auto_tdls_list); - mwifiex_dbg(priv->adapter, INFO, - "Add auto TDLS peer= %pM to list\n", mac); - } - - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); -} - -void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv, - const u8 *mac, u8 link_status) -{ - struct mwifiex_auto_tdls_peer *peer; - unsigned long flags; - - if (!priv->adapter->auto_tdls) - return; - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry(peer, &priv->auto_tdls_list, list) { - if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { - if ((link_status == TDLS_NOT_SETUP) && - (peer->tdls_status == TDLS_SETUP_INPROGRESS)) - peer->failure_count++; - else if (mwifiex_is_tdls_link_setup(link_status)) - peer->failure_count = 0; - - peer->tdls_status = link_status; - break; - } - } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); -} - -void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv, - u8 *mac, s8 snr, s8 nflr) -{ - struct mwifiex_auto_tdls_peer *peer; - unsigned long flags; - - if (!priv->adapter->auto_tdls) - return; - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry(peer, &priv->auto_tdls_list, list) { - if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { - peer->rssi = nflr - snr; - peer->rssi_jiffies = jiffies; - break; - } - } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); -} - -void mwifiex_check_auto_tdls(unsigned long context) -{ - struct mwifiex_private *priv = (struct mwifiex_private *)context; - struct mwifiex_auto_tdls_peer *tdls_peer; - unsigned long flags; - u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; - - if (WARN_ON_ONCE(!priv || !priv->adapter)) { - pr_err("mwifiex: %s: adapter or private structure is NULL\n", - __func__); - return; - } - - if (unlikely(!priv->adapter->auto_tdls)) - return; - - if (!priv->auto_tdls_timer_active) { - mwifiex_dbg(priv->adapter, INFO, - "auto TDLS timer inactive; return"); - return; - } - - priv->check_tdls_tx = false; - - if (list_empty(&priv->auto_tdls_list)) { - mod_timer(&priv->auto_tdls_timer, - jiffies + - msecs_to_jiffies(MWIFIEX_TIMER_10S)); - return; - } - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { - if ((jiffies - tdls_peer->rssi_jiffies) > - (MWIFIEX_AUTO_TDLS_IDLE_TIME * HZ)) { - tdls_peer->rssi = 0; - tdls_peer->do_discover = true; - priv->check_tdls_tx = true; - } - - if (((tdls_peer->rssi >= MWIFIEX_TDLS_RSSI_LOW) || - !tdls_peer->rssi) && - mwifiex_is_tdls_link_setup(tdls_peer->tdls_status)) { - tdls_peer->tdls_status = TDLS_LINK_TEARDOWN; - mwifiex_dbg(priv->adapter, MSG, - "teardown TDLS link,peer=%pM rssi=%d\n", - tdls_peer->mac_addr, -tdls_peer->rssi); - tdls_peer->do_discover = true; - priv->check_tdls_tx = true; - cfg80211_tdls_oper_request(priv->netdev, - tdls_peer->mac_addr, - NL80211_TDLS_TEARDOWN, - reason, GFP_ATOMIC); - } else if (tdls_peer->rssi && - tdls_peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH && - tdls_peer->tdls_status == TDLS_NOT_SETUP && - tdls_peer->failure_count < - MWIFIEX_TDLS_MAX_FAIL_COUNT) { - priv->check_tdls_tx = true; - tdls_peer->do_setup = true; - mwifiex_dbg(priv->adapter, INFO, - "check TDLS with peer=%pM\t" - "rssi=%d\n", tdls_peer->mac_addr, - tdls_peer->rssi); - } - } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); - - mod_timer(&priv->auto_tdls_timer, - jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); -} - -void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv) -{ - setup_timer(&priv->auto_tdls_timer, mwifiex_check_auto_tdls, - (unsigned long)priv); - priv->auto_tdls_timer_active = true; - mod_timer(&priv->auto_tdls_timer, - jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); -} - -void mwifiex_clean_auto_tdls(struct mwifiex_private *priv) -{ - if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - priv->adapter->auto_tdls && - priv->bss_type == MWIFIEX_BSS_TYPE_STA) { - priv->auto_tdls_timer_active = false; - del_timer(&priv->auto_tdls_timer); - mwifiex_flush_auto_tdls_list(priv); - } -} - -static int mwifiex_config_tdls(struct mwifiex_private *priv, u8 enable) -{ - struct mwifiex_tdls_config config; - - config.enable = cpu_to_le16(enable); - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, - ACT_TDLS_CS_ENABLE_CONFIG, 0, &config, true); -} - -int mwifiex_config_tdls_enable(struct mwifiex_private *priv) -{ - return mwifiex_config_tdls(priv, true); -} - -int mwifiex_config_tdls_disable(struct mwifiex_private *priv) -{ - return mwifiex_config_tdls(priv, false); -} - -int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv) -{ - struct mwifiex_tdls_config_cs_params config_tdls_cs_params; - - config_tdls_cs_params.unit_time = MWIFIEX_DEF_CS_UNIT_TIME; - config_tdls_cs_params.thr_otherlink = MWIFIEX_DEF_CS_THR_OTHERLINK; - config_tdls_cs_params.thr_directlink = MWIFIEX_DEF_THR_DIRECTLINK; - - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, - ACT_TDLS_CS_PARAMS, 0, - &config_tdls_cs_params, true); -} - -int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac) -{ - struct mwifiex_tdls_stop_cs_params stop_tdls_cs_params; - - ether_addr_copy(stop_tdls_cs_params.peer_mac, peer_mac); - - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, - ACT_TDLS_CS_STOP, 0, - &stop_tdls_cs_params, true); -} - -int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac, - u8 primary_chan, u8 second_chan_offset, u8 band) -{ - struct mwifiex_tdls_init_cs_params start_tdls_cs_params; - - ether_addr_copy(start_tdls_cs_params.peer_mac, peer_mac); - start_tdls_cs_params.primary_chan = primary_chan; - start_tdls_cs_params.second_chan_offset = second_chan_offset; - start_tdls_cs_params.band = band; - - start_tdls_cs_params.switch_time = cpu_to_le16(MWIFIEX_DEF_CS_TIME); - start_tdls_cs_params.switch_timeout = - cpu_to_le16(MWIFIEX_DEF_CS_TIMEOUT); - start_tdls_cs_params.reg_class = MWIFIEX_DEF_CS_REG_CLASS; - start_tdls_cs_params.periodicity = MWIFIEX_DEF_CS_PERIODICITY; - - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, - ACT_TDLS_CS_INIT, 0, - &start_tdls_cs_params, true); -} diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c deleted file mode 100644 index bf6182b646a5..000000000000 --- a/drivers/net/wireless/mwifiex/txrx.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Marvell Wireless LAN device driver: generic TX/RX data handling - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" - -/* - * This function processes the received buffer. - * - * Main responsibility of this function is to parse the RxPD to - * identify the correct interface this packet is headed for and - * forwarding it to the associated handling function, where the - * packet will be further processed and sent to kernel/upper layer - * if required. - */ -int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - struct rxpd *local_rx_pd; - struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); - int ret; - - local_rx_pd = (struct rxpd *) (skb->data); - /* Get the BSS number from rxpd, get corresponding priv */ - priv = mwifiex_get_priv_by_id(adapter, local_rx_pd->bss_num & - BSS_NUM_MASK, local_rx_pd->bss_type); - if (!priv) - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - if (!priv) { - mwifiex_dbg(adapter, ERROR, - "data: priv not found. Drop RX packet\n"); - dev_kfree_skb_any(skb); - return -1; - } - - mwifiex_dbg_dump(adapter, DAT_D, "rx pkt:", skb->data, - min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN)); - - memset(rx_info, 0, sizeof(*rx_info)); - rx_info->bss_num = priv->bss_num; - rx_info->bss_type = priv->bss_type; - - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) - ret = mwifiex_process_uap_rx_packet(priv, skb); - else - ret = mwifiex_process_sta_rx_packet(priv, skb); - - return ret; -} -EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet); - -/* - * This function sends a packet to device. - * - * It processes the packet to add the TxPD, checks condition and - * sends the processed packet to firmware for transmission. - * - * On successful completion, the function calls the completion callback - * and logs the time. - */ -int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - int hroom, ret = -1; - struct mwifiex_adapter *adapter = priv->adapter; - u8 *head_ptr; - struct txpd *local_tx_pd = NULL; - struct mwifiex_sta_node *dest_node; - struct ethhdr *hdr = (void *)skb->data; - - hroom = (adapter->iface_type == MWIFIEX_USB) ? 0 : INTF_HEADER_LEN; - - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { - dest_node = mwifiex_get_sta_entry(priv, hdr->h_dest); - if (dest_node) { - dest_node->stats.tx_bytes += skb->len; - dest_node->stats.tx_packets++; - } - - head_ptr = mwifiex_process_uap_txpd(priv, skb); - } else { - head_ptr = mwifiex_process_sta_txpd(priv, skb); - } - - if ((adapter->data_sent || adapter->tx_lock_flag) && head_ptr) { - skb_queue_tail(&adapter->tx_data_q, skb); - atomic_inc(&adapter->tx_queued); - return 0; - } - - if (head_ptr) { - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) - local_tx_pd = (struct txpd *)(head_ptr + hroom); - if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, - priv->usb_port, - skb, NULL); - } else { - ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_TYPE_DATA, - skb, tx_param); - } - } - mwifiex_dbg_dump(adapter, DAT_D, "tx pkt:", skb->data, - min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN)); - - switch (ret) { - case -ENOSR: - mwifiex_dbg(adapter, DATA, "data: -ENOSR is returned\n"); - break; - case -EBUSY: - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - (adapter->pps_uapsd_mode) && (adapter->tx_lock_flag)) { - priv->adapter->tx_lock_flag = false; - if (local_tx_pd) - local_tx_pd->flags = 0; - } - mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); - break; - case -1: - mwifiex_dbg(adapter, ERROR, - "mwifiex_write_data_async failed: 0x%X\n", - ret); - adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb, 0, ret); - break; - case -EINPROGRESS: - break; - case 0: - mwifiex_write_data_complete(adapter, skb, 0, ret); - break; - default: - break; - } - - return ret; -} - -static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, - struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - struct txpd *local_tx_pd = NULL; - u8 *head_ptr = skb->data; - int ret = 0; - struct mwifiex_private *priv; - struct mwifiex_txinfo *tx_info; - - tx_info = MWIFIEX_SKB_TXCB(skb); - priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num, - tx_info->bss_type); - if (!priv) { - mwifiex_dbg(adapter, ERROR, - "data: priv not found. Drop TX packet\n"); - adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb, 0, 0); - return ret; - } - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { - if (adapter->iface_type == MWIFIEX_USB) - local_tx_pd = (struct txpd *)head_ptr; - else - local_tx_pd = (struct txpd *) (head_ptr + - INTF_HEADER_LEN); - } - - if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, - priv->usb_port, - skb, NULL); - } else { - ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_TYPE_DATA, - skb, tx_param); - } - switch (ret) { - case -ENOSR: - mwifiex_dbg(adapter, ERROR, "data: -ENOSR is returned\n"); - break; - case -EBUSY: - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - (adapter->pps_uapsd_mode) && - (adapter->tx_lock_flag)) { - priv->adapter->tx_lock_flag = false; - if (local_tx_pd) - local_tx_pd->flags = 0; - } - skb_queue_head(&adapter->tx_data_q, skb); - if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) - atomic_add(tx_info->aggr_num, &adapter->tx_queued); - else - atomic_inc(&adapter->tx_queued); - mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); - break; - case -1: - mwifiex_dbg(adapter, ERROR, - "mwifiex_write_data_async failed: 0x%X\n", ret); - adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb, 0, ret); - break; - case -EINPROGRESS: - break; - case 0: - mwifiex_write_data_complete(adapter, skb, 0, ret); - break; - default: - break; - } - return ret; -} - -static int -mwifiex_dequeue_tx_queue(struct mwifiex_adapter *adapter) -{ - struct sk_buff *skb, *skb_next; - struct mwifiex_txinfo *tx_info; - struct mwifiex_tx_param tx_param; - - skb = skb_dequeue(&adapter->tx_data_q); - if (!skb) - return -1; - - tx_info = MWIFIEX_SKB_TXCB(skb); - if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) - atomic_sub(tx_info->aggr_num, &adapter->tx_queued); - else - atomic_dec(&adapter->tx_queued); - - if (!skb_queue_empty(&adapter->tx_data_q)) - skb_next = skb_peek(&adapter->tx_data_q); - else - skb_next = NULL; - tx_param.next_pkt_len = ((skb_next) ? skb_next->len : 0); - if (!tx_param.next_pkt_len) { - if (!mwifiex_wmm_lists_empty(adapter)) - tx_param.next_pkt_len = 1; - } - return mwifiex_host_to_card(adapter, skb, &tx_param); -} - -void -mwifiex_process_tx_queue(struct mwifiex_adapter *adapter) -{ - do { - if (adapter->data_sent || adapter->tx_lock_flag) - break; - if (mwifiex_dequeue_tx_queue(adapter)) - break; - } while (!skb_queue_empty(&adapter->tx_data_q)); -} - -/* - * Packet send completion callback handler. - * - * It either frees the buffer directly or forwards it to another - * completion callback which checks conditions, updates statistics, - * wakes up stalled traffic queue if required, and then frees the buffer. - */ -int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb, int aggr, int status) -{ - struct mwifiex_private *priv; - struct mwifiex_txinfo *tx_info; - struct netdev_queue *txq; - int index; - - if (!skb) - return 0; - - tx_info = MWIFIEX_SKB_TXCB(skb); - priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num, - tx_info->bss_type); - if (!priv) - goto done; - - mwifiex_set_trans_start(priv->netdev); - if (!status) { - priv->stats.tx_packets++; - priv->stats.tx_bytes += tx_info->pkt_len; - if (priv->tx_timeout_cnt) - priv->tx_timeout_cnt = 0; - } else { - priv->stats.tx_errors++; - } - - if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) - atomic_dec_return(&adapter->pending_bridged_pkts); - - if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) - goto done; - - if (aggr) - /* For skb_aggr, do not wake up tx queue */ - goto done; - - atomic_dec(&adapter->tx_pending); - - index = mwifiex_1d_to_wmm_queue[skb->priority]; - if (atomic_dec_return(&priv->wmm_tx_pending[index]) < LOW_TX_PENDING) { - txq = netdev_get_tx_queue(priv->netdev, index); - if (netif_tx_queue_stopped(txq)) { - netif_tx_wake_queue(txq); - mwifiex_dbg(adapter, DATA, "wake queue: %d\n", index); - } - } -done: - dev_kfree_skb_any(skb); - - return 0; -} -EXPORT_SYMBOL_GPL(mwifiex_write_data_complete); - -void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, - void *event_body) -{ - struct tx_status_event *tx_status = (void *)priv->adapter->event_body; - struct sk_buff *ack_skb; - unsigned long flags; - struct mwifiex_txinfo *tx_info; - - if (!tx_status->tx_token_id) - return; - - spin_lock_irqsave(&priv->ack_status_lock, flags); - ack_skb = idr_find(&priv->ack_status_frames, tx_status->tx_token_id); - if (ack_skb) - idr_remove(&priv->ack_status_frames, tx_status->tx_token_id); - spin_unlock_irqrestore(&priv->ack_status_lock, flags); - - if (ack_skb) { - tx_info = MWIFIEX_SKB_TXCB(ack_skb); - - if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS) { - /* consumes ack_skb */ - skb_complete_wifi_ack(ack_skb, !tx_status->status); - } else { - /* Remove broadcast address which was added by driver */ - memmove(ack_skb->data + - sizeof(struct ieee80211_hdr_3addr) + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16), - ack_skb->data + - sizeof(struct ieee80211_hdr_3addr) + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) + - ETH_ALEN, ack_skb->len - - (sizeof(struct ieee80211_hdr_3addr) + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) + - ETH_ALEN)); - ack_skb->len = ack_skb->len - ETH_ALEN; - /* Remove driver's proprietary header including 2 bytes - * of packet length and pass actual management frame buffer - * to cfg80211. - */ - cfg80211_mgmt_tx_status(&priv->wdev, tx_info->cookie, - ack_skb->data + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + - sizeof(u16), ack_skb->len - - (MWIFIEX_MGMT_FRAME_HEADER_SIZE - + sizeof(u16)), - !tx_status->status, GFP_ATOMIC); - dev_kfree_skb_any(ack_skb); - } - } -} diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c deleted file mode 100644 index 759a6ada5b0f..000000000000 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ /dev/null @@ -1,885 +0,0 @@ -/* - * Marvell Wireless LAN device driver: AP specific command handling - * - * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" -#include "11ac.h" - -/* This function parses security related parameters from cfg80211_ap_settings - * and sets into FW understandable bss_config structure. - */ -int mwifiex_set_secure_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_config, - struct cfg80211_ap_settings *params) { - int i; - struct mwifiex_wep_key wep_key; - - if (!params->privacy) { - bss_config->protocol = PROTOCOL_NO_SECURITY; - bss_config->key_mgmt = KEY_MGMT_NONE; - bss_config->wpa_cfg.length = 0; - priv->sec_info.wep_enabled = 0; - priv->sec_info.wpa_enabled = 0; - priv->sec_info.wpa2_enabled = 0; - - return 0; - } - - switch (params->auth_type) { - case NL80211_AUTHTYPE_OPEN_SYSTEM: - bss_config->auth_mode = WLAN_AUTH_OPEN; - break; - case NL80211_AUTHTYPE_SHARED_KEY: - bss_config->auth_mode = WLAN_AUTH_SHARED_KEY; - break; - case NL80211_AUTHTYPE_NETWORK_EAP: - bss_config->auth_mode = WLAN_AUTH_LEAP; - break; - default: - bss_config->auth_mode = MWIFIEX_AUTH_MODE_AUTO; - break; - } - - bss_config->key_mgmt_operation |= KEY_MGMT_ON_HOST; - - for (i = 0; i < params->crypto.n_akm_suites; i++) { - switch (params->crypto.akm_suites[i]) { - case WLAN_AKM_SUITE_8021X: - if (params->crypto.wpa_versions & - NL80211_WPA_VERSION_1) { - bss_config->protocol = PROTOCOL_WPA; - bss_config->key_mgmt = KEY_MGMT_EAP; - } - if (params->crypto.wpa_versions & - NL80211_WPA_VERSION_2) { - bss_config->protocol |= PROTOCOL_WPA2; - bss_config->key_mgmt = KEY_MGMT_EAP; - } - break; - case WLAN_AKM_SUITE_PSK: - if (params->crypto.wpa_versions & - NL80211_WPA_VERSION_1) { - bss_config->protocol = PROTOCOL_WPA; - bss_config->key_mgmt = KEY_MGMT_PSK; - } - if (params->crypto.wpa_versions & - NL80211_WPA_VERSION_2) { - bss_config->protocol |= PROTOCOL_WPA2; - bss_config->key_mgmt = KEY_MGMT_PSK; - } - break; - default: - break; - } - } - for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) { - switch (params->crypto.ciphers_pairwise[i]) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - break; - case WLAN_CIPHER_SUITE_TKIP: - if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) - bss_config->wpa_cfg.pairwise_cipher_wpa |= - CIPHER_TKIP; - if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) - bss_config->wpa_cfg.pairwise_cipher_wpa2 |= - CIPHER_TKIP; - break; - case WLAN_CIPHER_SUITE_CCMP: - if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) - bss_config->wpa_cfg.pairwise_cipher_wpa |= - CIPHER_AES_CCMP; - if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) - bss_config->wpa_cfg.pairwise_cipher_wpa2 |= - CIPHER_AES_CCMP; - default: - break; - } - } - - switch (params->crypto.cipher_group) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - if (priv->sec_info.wep_enabled) { - bss_config->protocol = PROTOCOL_STATIC_WEP; - bss_config->key_mgmt = KEY_MGMT_NONE; - bss_config->wpa_cfg.length = 0; - - for (i = 0; i < NUM_WEP_KEYS; i++) { - wep_key = priv->wep_key[i]; - bss_config->wep_cfg[i].key_index = i; - - if (priv->wep_key_curr_index == i) - bss_config->wep_cfg[i].is_default = 1; - else - bss_config->wep_cfg[i].is_default = 0; - - bss_config->wep_cfg[i].length = - wep_key.key_length; - memcpy(&bss_config->wep_cfg[i].key, - &wep_key.key_material, - wep_key.key_length); - } - } - break; - case WLAN_CIPHER_SUITE_TKIP: - bss_config->wpa_cfg.group_cipher = CIPHER_TKIP; - break; - case WLAN_CIPHER_SUITE_CCMP: - bss_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; - break; - default: - break; - } - - return 0; -} - -/* This function updates 11n related parameters from IE and sets them into - * bss_config structure. - */ -void -mwifiex_set_ht_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params) -{ - const u8 *ht_ie; - u16 cap_info; - - if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) - return; - - ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail, - params->beacon.tail_len); - if (ht_ie) { - memcpy(&bss_cfg->ht_cap, ht_ie + 2, - sizeof(struct ieee80211_ht_cap)); - cap_info = le16_to_cpu(bss_cfg->ht_cap.cap_info); - memset(&bss_cfg->ht_cap.mcs, 0, - priv->adapter->number_of_antenna); - switch (GET_RXSTBC(cap_info)) { - case MWIFIEX_RX_STBC1: - /* HT_CAP 1X1 mode */ - bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; - break; - case MWIFIEX_RX_STBC12: /* fall through */ - case MWIFIEX_RX_STBC123: - /* HT_CAP 2X2 mode */ - bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; - bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff; - break; - default: - mwifiex_dbg(priv->adapter, WARN, - "Unsupported RX-STBC, default to 2x2\n"); - bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; - bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff; - break; - } - priv->ap_11n_enabled = 1; - } else { - memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap)); - bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP); - bss_cfg->ht_cap.ampdu_params_info = MWIFIEX_DEF_AMPDU; - } - - return; -} - -/* This function updates 11ac related parameters from IE - * and sets them into bss_config structure. - */ -void mwifiex_set_vht_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params) -{ - const u8 *vht_ie; - - vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail, - params->beacon.tail_len); - if (vht_ie) { - memcpy(&bss_cfg->vht_cap, vht_ie + 2, - sizeof(struct ieee80211_vht_cap)); - priv->ap_11ac_enabled = 1; - } else { - priv->ap_11ac_enabled = 0; - } - - return; -} - -/* This function updates 11ac related parameters from IE - * and sets them into bss_config structure. - */ -void mwifiex_set_tpc_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params) -{ - const u8 *tpc_ie; - - tpc_ie = cfg80211_find_ie(WLAN_EID_TPC_REQUEST, params->beacon.tail, - params->beacon.tail_len); - if (tpc_ie) - bss_cfg->power_constraint = *(tpc_ie + 2); - else - bss_cfg->power_constraint = 0; -} - -/* Enable VHT only when cfg80211_ap_settings has VHT IE. - * Otherwise disable VHT. - */ -void mwifiex_set_vht_width(struct mwifiex_private *priv, - enum nl80211_chan_width width, - bool ap_11ac_enable) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_11ac_vht_cfg vht_cfg; - - vht_cfg.band_config = VHT_CFG_5GHZ; - vht_cfg.cap_info = adapter->hw_dot_11ac_dev_cap; - - if (!ap_11ac_enable) { - vht_cfg.mcs_tx_set = DISABLE_VHT_MCS_SET; - vht_cfg.mcs_rx_set = DISABLE_VHT_MCS_SET; - } else { - vht_cfg.mcs_tx_set = DEFAULT_VHT_MCS_SET; - vht_cfg.mcs_rx_set = DEFAULT_VHT_MCS_SET; - } - - vht_cfg.misc_config = VHT_CAP_UAP_ONLY; - - if (ap_11ac_enable && width >= NL80211_CHAN_WIDTH_80) - vht_cfg.misc_config |= VHT_BW_80_160_80P80; - - mwifiex_send_cmd(priv, HostCmd_CMD_11AC_CFG, - HostCmd_ACT_GEN_SET, 0, &vht_cfg, true); - - return; -} - -/* This function finds supported rates IE from beacon parameter and sets - * these rates into bss_config structure. - */ -void -mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params) -{ - struct ieee_types_header *rate_ie; - int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); - const u8 *var_pos = params->beacon.head + var_offset; - int len = params->beacon.head_len - var_offset; - u8 rate_len = 0; - - rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len); - if (rate_ie) { - memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len); - rate_len = rate_ie->len; - } - - rate_ie = (void *)cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, - params->beacon.tail, - params->beacon.tail_len); - if (rate_ie) - memcpy(bss_cfg->rates + rate_len, rate_ie + 1, rate_ie->len); - - return; -} - -/* This function initializes some of mwifiex_uap_bss_param variables. - * This helps FW in ignoring invalid values. These values may or may not - * be get updated to valid ones at later stage. - */ -void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config) -{ - config->bcast_ssid_ctl = 0x7F; - config->radio_ctl = 0x7F; - config->dtim_period = 0x7F; - config->beacon_period = 0x7FFF; - config->auth_mode = 0x7F; - config->rts_threshold = 0x7FFF; - config->frag_threshold = 0x7FFF; - config->retry_limit = 0x7F; - config->qos_info = 0xFF; -} - -/* This function parses BSS related parameters from structure - * and prepares TLVs specific to WPA/WPA2 security. - * These TLVs are appended to command buffer. - */ -static void -mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) -{ - struct host_cmd_tlv_pwk_cipher *pwk_cipher; - struct host_cmd_tlv_gwk_cipher *gwk_cipher; - struct host_cmd_tlv_passphrase *passphrase; - struct host_cmd_tlv_akmp *tlv_akmp; - struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; - u16 cmd_size = *param_size; - u8 *tlv = *tlv_buf; - - tlv_akmp = (struct host_cmd_tlv_akmp *)tlv; - tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP); - tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) - - sizeof(struct mwifiex_ie_types_header)); - tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation); - tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt); - cmd_size += sizeof(struct host_cmd_tlv_akmp); - tlv += sizeof(struct host_cmd_tlv_akmp); - - if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) { - pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; - pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); - pwk_cipher->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - - sizeof(struct mwifiex_ie_types_header)); - pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA); - pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa; - cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); - tlv += sizeof(struct host_cmd_tlv_pwk_cipher); - } - - if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) { - pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; - pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); - pwk_cipher->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - - sizeof(struct mwifiex_ie_types_header)); - pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2); - pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2; - cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); - tlv += sizeof(struct host_cmd_tlv_pwk_cipher); - } - - if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) { - gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv; - gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER); - gwk_cipher->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) - - sizeof(struct mwifiex_ie_types_header)); - gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher; - cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher); - tlv += sizeof(struct host_cmd_tlv_gwk_cipher); - } - - if (bss_cfg->wpa_cfg.length) { - passphrase = (struct host_cmd_tlv_passphrase *)tlv; - passphrase->header.type = - cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE); - passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length); - memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase, - bss_cfg->wpa_cfg.length); - cmd_size += sizeof(struct mwifiex_ie_types_header) + - bss_cfg->wpa_cfg.length; - tlv += sizeof(struct mwifiex_ie_types_header) + - bss_cfg->wpa_cfg.length; - } - - *param_size = cmd_size; - *tlv_buf = tlv; - - return; -} - -/* This function parses WMM related parameters from cfg80211_ap_settings - * structure and updates bss_config structure. - */ -void -mwifiex_set_wmm_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params) -{ - const u8 *vendor_ie; - struct ieee_types_header *wmm_ie; - u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02}; - - vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WMM, - params->beacon.tail, - params->beacon.tail_len); - if (vendor_ie) { - wmm_ie = (struct ieee_types_header *)vendor_ie; - memcpy(&bss_cfg->wmm_info, wmm_ie + 1, - sizeof(bss_cfg->wmm_info)); - priv->wmm_enabled = 1; - } else { - memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info)); - memcpy(&bss_cfg->wmm_info.oui, wmm_oui, sizeof(wmm_oui)); - bss_cfg->wmm_info.subtype = MWIFIEX_WMM_SUBTYPE; - bss_cfg->wmm_info.version = MWIFIEX_WMM_VERSION; - priv->wmm_enabled = 0; - } - - bss_cfg->qos_info = 0x00; - return; -} -/* This function parses BSS related parameters from structure - * and prepares TLVs specific to WEP encryption. - * These TLVs are appended to command buffer. - */ -static void -mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size) -{ - struct host_cmd_tlv_wep_key *wep_key; - u16 cmd_size = *param_size; - int i; - u8 *tlv = *tlv_buf; - struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; - - for (i = 0; i < NUM_WEP_KEYS; i++) { - if (bss_cfg->wep_cfg[i].length && - (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 || - bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) { - wep_key = (struct host_cmd_tlv_wep_key *)tlv; - wep_key->header.type = - cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); - wep_key->header.len = - cpu_to_le16(bss_cfg->wep_cfg[i].length + 2); - wep_key->key_index = bss_cfg->wep_cfg[i].key_index; - wep_key->is_default = bss_cfg->wep_cfg[i].is_default; - memcpy(wep_key->key, bss_cfg->wep_cfg[i].key, - bss_cfg->wep_cfg[i].length); - cmd_size += sizeof(struct mwifiex_ie_types_header) + 2 + - bss_cfg->wep_cfg[i].length; - tlv += sizeof(struct mwifiex_ie_types_header) + 2 + - bss_cfg->wep_cfg[i].length; - } - } - - *param_size = cmd_size; - *tlv_buf = tlv; - - return; -} - -/* This function parses BSS related parameters from structure - * and prepares TLVs. These TLVs are appended to command buffer. -*/ -static int -mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) -{ - struct host_cmd_tlv_dtim_period *dtim_period; - struct host_cmd_tlv_beacon_period *beacon_period; - struct host_cmd_tlv_ssid *ssid; - struct host_cmd_tlv_bcast_ssid *bcast_ssid; - struct host_cmd_tlv_channel_band *chan_band; - struct host_cmd_tlv_frag_threshold *frag_threshold; - struct host_cmd_tlv_rts_threshold *rts_threshold; - struct host_cmd_tlv_retry_limit *retry_limit; - struct host_cmd_tlv_encrypt_protocol *encrypt_protocol; - struct host_cmd_tlv_auth_type *auth_type; - struct host_cmd_tlv_rates *tlv_rates; - struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer; - struct host_cmd_tlv_power_constraint *pwr_ct; - struct mwifiex_ie_types_htcap *htcap; - struct mwifiex_ie_types_wmmcap *wmm_cap; - struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; - int i; - u16 cmd_size = *param_size; - - if (bss_cfg->ssid.ssid_len) { - ssid = (struct host_cmd_tlv_ssid *)tlv; - ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID); - ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len); - memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len); - cmd_size += sizeof(struct mwifiex_ie_types_header) + - bss_cfg->ssid.ssid_len; - tlv += sizeof(struct mwifiex_ie_types_header) + - bss_cfg->ssid.ssid_len; - - bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv; - bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID); - bcast_ssid->header.len = - cpu_to_le16(sizeof(bcast_ssid->bcast_ctl)); - bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl; - cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid); - tlv += sizeof(struct host_cmd_tlv_bcast_ssid); - } - if (bss_cfg->rates[0]) { - tlv_rates = (struct host_cmd_tlv_rates *)tlv; - tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES); - - for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i]; - i++) - tlv_rates->rates[i] = bss_cfg->rates[i]; - - tlv_rates->header.len = cpu_to_le16(i); - cmd_size += sizeof(struct host_cmd_tlv_rates) + i; - tlv += sizeof(struct host_cmd_tlv_rates) + i; - } - if (bss_cfg->channel && - ((bss_cfg->band_cfg == BAND_CONFIG_BG && - bss_cfg->channel <= MAX_CHANNEL_BAND_BG) || - (bss_cfg->band_cfg == BAND_CONFIG_A && - bss_cfg->channel <= MAX_CHANNEL_BAND_A))) { - chan_band = (struct host_cmd_tlv_channel_band *)tlv; - chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); - chan_band->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) - - sizeof(struct mwifiex_ie_types_header)); - chan_band->band_config = bss_cfg->band_cfg; - chan_band->channel = bss_cfg->channel; - cmd_size += sizeof(struct host_cmd_tlv_channel_band); - tlv += sizeof(struct host_cmd_tlv_channel_band); - } - if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD && - bss_cfg->beacon_period <= MAX_BEACON_PERIOD) { - beacon_period = (struct host_cmd_tlv_beacon_period *)tlv; - beacon_period->header.type = - cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD); - beacon_period->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) - - sizeof(struct mwifiex_ie_types_header)); - beacon_period->period = cpu_to_le16(bss_cfg->beacon_period); - cmd_size += sizeof(struct host_cmd_tlv_beacon_period); - tlv += sizeof(struct host_cmd_tlv_beacon_period); - } - if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD && - bss_cfg->dtim_period <= MAX_DTIM_PERIOD) { - dtim_period = (struct host_cmd_tlv_dtim_period *)tlv; - dtim_period->header.type = - cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD); - dtim_period->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) - - sizeof(struct mwifiex_ie_types_header)); - dtim_period->period = bss_cfg->dtim_period; - cmd_size += sizeof(struct host_cmd_tlv_dtim_period); - tlv += sizeof(struct host_cmd_tlv_dtim_period); - } - if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) { - rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv; - rts_threshold->header.type = - cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD); - rts_threshold->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) - - sizeof(struct mwifiex_ie_types_header)); - rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold); - cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); - tlv += sizeof(struct host_cmd_tlv_frag_threshold); - } - if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) && - (bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) { - frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv; - frag_threshold->header.type = - cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD); - frag_threshold->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) - - sizeof(struct mwifiex_ie_types_header)); - frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold); - cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); - tlv += sizeof(struct host_cmd_tlv_frag_threshold); - } - if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) { - retry_limit = (struct host_cmd_tlv_retry_limit *)tlv; - retry_limit->header.type = - cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT); - retry_limit->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) - - sizeof(struct mwifiex_ie_types_header)); - retry_limit->limit = (u8)bss_cfg->retry_limit; - cmd_size += sizeof(struct host_cmd_tlv_retry_limit); - tlv += sizeof(struct host_cmd_tlv_retry_limit); - } - if ((bss_cfg->protocol & PROTOCOL_WPA) || - (bss_cfg->protocol & PROTOCOL_WPA2) || - (bss_cfg->protocol & PROTOCOL_EAP)) - mwifiex_uap_bss_wpa(&tlv, cmd_buf, &cmd_size); - else - mwifiex_uap_bss_wep(&tlv, cmd_buf, &cmd_size); - - if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) || - (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) { - auth_type = (struct host_cmd_tlv_auth_type *)tlv; - auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); - auth_type->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) - - sizeof(struct mwifiex_ie_types_header)); - auth_type->auth_type = (u8)bss_cfg->auth_mode; - cmd_size += sizeof(struct host_cmd_tlv_auth_type); - tlv += sizeof(struct host_cmd_tlv_auth_type); - } - if (bss_cfg->protocol) { - encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv; - encrypt_protocol->header.type = - cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL); - encrypt_protocol->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol) - - sizeof(struct mwifiex_ie_types_header)); - encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol); - cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol); - tlv += sizeof(struct host_cmd_tlv_encrypt_protocol); - } - - if (bss_cfg->ht_cap.cap_info) { - htcap = (struct mwifiex_ie_types_htcap *)tlv; - htcap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); - htcap->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_cap)); - htcap->ht_cap.cap_info = bss_cfg->ht_cap.cap_info; - htcap->ht_cap.ampdu_params_info = - bss_cfg->ht_cap.ampdu_params_info; - memcpy(&htcap->ht_cap.mcs, &bss_cfg->ht_cap.mcs, - sizeof(struct ieee80211_mcs_info)); - htcap->ht_cap.extended_ht_cap_info = - bss_cfg->ht_cap.extended_ht_cap_info; - htcap->ht_cap.tx_BF_cap_info = bss_cfg->ht_cap.tx_BF_cap_info; - htcap->ht_cap.antenna_selection_info = - bss_cfg->ht_cap.antenna_selection_info; - cmd_size += sizeof(struct mwifiex_ie_types_htcap); - tlv += sizeof(struct mwifiex_ie_types_htcap); - } - - if (bss_cfg->wmm_info.qos_info != 0xFF) { - wmm_cap = (struct mwifiex_ie_types_wmmcap *)tlv; - wmm_cap->header.type = cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC); - wmm_cap->header.len = cpu_to_le16(sizeof(wmm_cap->wmm_info)); - memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info, - sizeof(wmm_cap->wmm_info)); - cmd_size += sizeof(struct mwifiex_ie_types_wmmcap); - tlv += sizeof(struct mwifiex_ie_types_wmmcap); - } - - if (bss_cfg->sta_ao_timer) { - ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; - ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER); - ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) - - sizeof(struct mwifiex_ie_types_header)); - ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer); - cmd_size += sizeof(*ao_timer); - tlv += sizeof(*ao_timer); - } - - if (bss_cfg->power_constraint) { - pwr_ct = (void *)tlv; - pwr_ct->header.type = cpu_to_le16(TLV_TYPE_PWR_CONSTRAINT); - pwr_ct->header.len = cpu_to_le16(sizeof(u8)); - pwr_ct->constraint = bss_cfg->power_constraint; - cmd_size += sizeof(*pwr_ct); - tlv += sizeof(*pwr_ct); - } - - if (bss_cfg->ps_sta_ao_timer) { - ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; - ps_ao_timer->header.type = - cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER); - ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) - - sizeof(struct mwifiex_ie_types_header)); - ps_ao_timer->sta_ao_timer = - cpu_to_le32(bss_cfg->ps_sta_ao_timer); - cmd_size += sizeof(*ps_ao_timer); - tlv += sizeof(*ps_ao_timer); - } - - *param_size = cmd_size; - - return 0; -} - -/* This function parses custom IEs from IE list and prepares command buffer */ -static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size) -{ - struct mwifiex_ie_list *ap_ie = cmd_buf; - struct mwifiex_ie_types_header *tlv_ie = (void *)tlv; - - if (!ap_ie || !ap_ie->len || !ap_ie->ie_list) - return -1; - - *ie_size += le16_to_cpu(ap_ie->len) + - sizeof(struct mwifiex_ie_types_header); - - tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); - tlv_ie->len = ap_ie->len; - tlv += sizeof(struct mwifiex_ie_types_header); - - memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len)); - - return 0; -} - -/* Parse AP config structure and prepare TLV based command structure - * to be sent to FW for uAP configuration - */ -static int -mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action, - u32 type, void *cmd_buf) -{ - u8 *tlv; - u16 cmd_size, param_size, ie_size; - struct host_cmd_ds_sys_config *sys_cfg; - - cmd->command = cpu_to_le16(HostCmd_CMD_UAP_SYS_CONFIG); - cmd_size = (u16)(sizeof(struct host_cmd_ds_sys_config) + S_DS_GEN); - sys_cfg = (struct host_cmd_ds_sys_config *)&cmd->params.uap_sys_config; - sys_cfg->action = cpu_to_le16(cmd_action); - tlv = sys_cfg->tlv; - - switch (type) { - case UAP_BSS_PARAMS_I: - param_size = cmd_size; - if (mwifiex_uap_bss_param_prepare(tlv, cmd_buf, ¶m_size)) - return -1; - cmd->size = cpu_to_le16(param_size); - break; - case UAP_CUSTOM_IE_I: - ie_size = cmd_size; - if (mwifiex_uap_custom_ie_prepare(tlv, cmd_buf, &ie_size)) - return -1; - cmd->size = cpu_to_le16(ie_size); - break; - default: - return -1; - } - - return 0; -} - -/* This function prepares AP specific deauth command with mac supplied in - * function parameter. - */ -static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u8 *mac) -{ - struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth; - - cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH); - memcpy(sta_deauth->mac, mac, ETH_ALEN); - sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); - - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) + - S_DS_GEN); - return 0; -} - -/* This function prepares the AP specific commands before sending them - * to the firmware. - * This is a generic function which calls specific command preparation - * routines based upon the command number. - */ -int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, - u16 cmd_action, u32 type, - void *data_buf, void *cmd_buf) -{ - struct host_cmd_ds_command *cmd = cmd_buf; - - switch (cmd_no) { - case HostCmd_CMD_UAP_SYS_CONFIG: - if (mwifiex_cmd_uap_sys_config(cmd, cmd_action, type, data_buf)) - return -1; - break; - case HostCmd_CMD_UAP_BSS_START: - case HostCmd_CMD_UAP_BSS_STOP: - case HOST_CMD_APCMD_SYS_RESET: - case HOST_CMD_APCMD_STA_LIST: - cmd->command = cpu_to_le16(cmd_no); - cmd->size = cpu_to_le16(S_DS_GEN); - break; - case HostCmd_CMD_UAP_STA_DEAUTH: - if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf)) - return -1; - break; - case HostCmd_CMD_CHAN_REPORT_REQUEST: - if (mwifiex_cmd_issue_chan_report_request(priv, cmd_buf, - data_buf)) - return -1; - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "PREP_CMD: unknown cmd %#x\n", cmd_no); - return -1; - } - - return 0; -} - -void mwifiex_uap_set_channel(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_chan_def chandef) -{ - u8 config_bands = 0, old_bands = priv->adapter->config_bands; - - priv->bss_chandef = chandef; - - bss_cfg->channel = ieee80211_frequency_to_channel( - chandef.chan->center_freq); - - /* Set appropriate bands */ - if (chandef.chan->band == IEEE80211_BAND_2GHZ) { - bss_cfg->band_cfg = BAND_CONFIG_BG; - config_bands = BAND_B | BAND_G; - - if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) - config_bands |= BAND_GN; - } else { - bss_cfg->band_cfg = BAND_CONFIG_A; - config_bands = BAND_A; - - if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) - config_bands |= BAND_AN; - - if (chandef.width > NL80211_CHAN_WIDTH_40) - config_bands |= BAND_AAC; - } - - priv->adapter->config_bands = config_bands; - - if (old_bands != config_bands) { - mwifiex_send_domain_info_cmd_fw(priv->adapter->wiphy); - mwifiex_dnld_txpwr_table(priv); - } -} - -int mwifiex_config_start_uap(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg) -{ - enum state_11d_t state_11d; - - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_BSS_PARAMS_I, bss_cfg, false)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to set the SSID\n"); - return -1; - } - - /* Send cmd to FW to enable 11D function */ - state_11d = ENABLE_11D; - if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, DOT11D_I, - &state_11d, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "11D: failed to enable 11D\n"); - return -1; - } - - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, - HostCmd_ACT_GEN_SET, 0, NULL, false)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to start the BSS\n"); - return -1; - } - - if (priv->sec_info.wep_enabled) - priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; - else - priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; - - if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter, true)) - return -1; - - return 0; -} diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c deleted file mode 100644 index 86ff54296f39..000000000000 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Marvell Wireless LAN device driver: AP event handling - * - * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "main.h" -#include "11n.h" - -#define MWIFIEX_BSS_START_EVT_FIX_SIZE 12 - -static int mwifiex_check_uap_capabilties(struct mwifiex_private *priv, - struct sk_buff *event) -{ - int evt_len; - u8 *curr; - u16 tlv_len; - struct mwifiex_ie_types_data *tlv_hdr; - struct ieee_types_wmm_parameter *wmm_param_ie = NULL; - int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; - - priv->wmm_enabled = false; - skb_pull(event, MWIFIEX_BSS_START_EVT_FIX_SIZE); - evt_len = event->len; - curr = event->data; - - mwifiex_dbg_dump(priv->adapter, EVT_D, "uap capabilties:", - event->data, event->len); - - skb_push(event, MWIFIEX_BSS_START_EVT_FIX_SIZE); - - while ((evt_len >= sizeof(tlv_hdr->header))) { - tlv_hdr = (struct mwifiex_ie_types_data *)curr; - tlv_len = le16_to_cpu(tlv_hdr->header.len); - - if (evt_len < tlv_len + sizeof(tlv_hdr->header)) - break; - - switch (le16_to_cpu(tlv_hdr->header.type)) { - case WLAN_EID_HT_CAPABILITY: - priv->ap_11n_enabled = true; - break; - - case WLAN_EID_VHT_CAPABILITY: - priv->ap_11ac_enabled = true; - break; - - case WLAN_EID_VENDOR_SPECIFIC: - /* Point the regular IEEE IE 2 bytes into the Marvell IE - * and setup the IEEE IE type and length byte fields - */ - wmm_param_ie = (void *)(curr + 2); - wmm_param_ie->vend_hdr.len = (u8)tlv_len; - wmm_param_ie->vend_hdr.element_id = - WLAN_EID_VENDOR_SPECIFIC; - mwifiex_dbg(priv->adapter, EVENT, - "info: check uap capabilities:\t" - "wmm parameter set count: %d\n", - wmm_param_ie->qos_info_bitmap & mask); - - mwifiex_wmm_setup_ac_downgrade(priv); - priv->wmm_enabled = true; - mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); - break; - - default: - break; - } - - curr += (tlv_len + sizeof(tlv_hdr->header)); - evt_len -= (tlv_len + sizeof(tlv_hdr->header)); - } - - return 0; -} - -/* - * This function handles AP interface specific events generated by firmware. - * - * Event specific routines are called by this function based - * upon the generated event cause. - * - * - * Events supported for AP - - * - EVENT_UAP_STA_ASSOC - * - EVENT_UAP_STA_DEAUTH - * - EVENT_UAP_BSS_ACTIVE - * - EVENT_UAP_BSS_START - * - EVENT_UAP_BSS_IDLE - * - EVENT_UAP_MIC_COUNTERMEASURES: - */ -int mwifiex_process_uap_event(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int len, i; - u32 eventcause = adapter->event_cause; - struct station_info sinfo; - struct mwifiex_assoc_event *event; - struct mwifiex_sta_node *node; - u8 *deauth_mac; - struct host_cmd_ds_11n_batimeout *ba_timeout; - u16 ctrl; - - switch (eventcause) { - case EVENT_UAP_STA_ASSOC: - memset(&sinfo, 0, sizeof(sinfo)); - event = (struct mwifiex_assoc_event *) - (adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER); - if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) { - len = -1; - - if (ieee80211_is_assoc_req(event->frame_control)) - len = 0; - else if (ieee80211_is_reassoc_req(event->frame_control)) - /* There will be ETH_ALEN bytes of - * current_ap_addr before the re-assoc ies. - */ - len = ETH_ALEN; - - if (len != -1) { - sinfo.assoc_req_ies = &event->data[len]; - len = (u8 *)sinfo.assoc_req_ies - - (u8 *)&event->frame_control; - sinfo.assoc_req_ies_len = - le16_to_cpu(event->len) - (u16)len; - } - } - cfg80211_new_sta(priv->netdev, event->sta_addr, &sinfo, - GFP_KERNEL); - - node = mwifiex_add_sta_entry(priv, event->sta_addr); - if (!node) { - mwifiex_dbg(adapter, ERROR, - "could not create station entry!\n"); - return -1; - } - - if (!priv->ap_11n_enabled) - break; - - mwifiex_set_sta_ht_cap(priv, sinfo.assoc_req_ies, - sinfo.assoc_req_ies_len, node); - - for (i = 0; i < MAX_NUM_TID; i++) { - if (node->is_11n_enabled) - node->ampdu_sta[i] = - priv->aggr_prio_tbl[i].ampdu_user; - else - node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; - } - memset(node->rx_seq, 0xff, sizeof(node->rx_seq)); - break; - case EVENT_UAP_STA_DEAUTH: - deauth_mac = adapter->event_body + - MWIFIEX_UAP_EVENT_EXTRA_HEADER; - cfg80211_del_sta(priv->netdev, deauth_mac, GFP_KERNEL); - - if (priv->ap_11n_enabled) { - mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac); - mwifiex_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac); - } - mwifiex_wmm_del_peer_ra_list(priv, deauth_mac); - mwifiex_del_sta_entry(priv, deauth_mac); - break; - case EVENT_UAP_BSS_IDLE: - priv->media_connected = false; - priv->port_open = false; - mwifiex_clean_txrx(priv); - mwifiex_del_all_sta_list(priv); - break; - case EVENT_UAP_BSS_ACTIVE: - priv->media_connected = true; - priv->port_open = true; - break; - case EVENT_UAP_BSS_START: - mwifiex_dbg(adapter, EVENT, - "AP EVENT: event id: %#x\n", eventcause); - priv->port_open = false; - memcpy(priv->netdev->dev_addr, adapter->event_body + 2, - ETH_ALEN); - if (priv->hist_data) - mwifiex_hist_data_reset(priv); - mwifiex_check_uap_capabilties(priv, adapter->event_skb); - break; - case EVENT_UAP_MIC_COUNTERMEASURES: - /* For future development */ - mwifiex_dbg(adapter, EVENT, - "AP EVENT: event id: %#x\n", eventcause); - break; - case EVENT_AMSDU_AGGR_CTRL: - ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); - mwifiex_dbg(adapter, EVENT, - "event: AMSDU_AGGR_CTRL %d\n", ctrl); - - if (priv->media_connected) { - adapter->tx_buf_size = - min_t(u16, adapter->curr_tx_buf_size, ctrl); - mwifiex_dbg(adapter, EVENT, - "event: tx_buf_size %d\n", - adapter->tx_buf_size); - } - break; - case EVENT_ADDBA: - mwifiex_dbg(adapter, EVENT, "event: ADDBA Request\n"); - if (priv->media_connected) - mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP, - HostCmd_ACT_GEN_SET, 0, - adapter->event_body, false); - break; - case EVENT_DELBA: - mwifiex_dbg(adapter, EVENT, "event: DELBA Request\n"); - if (priv->media_connected) - mwifiex_11n_delete_ba_stream(priv, adapter->event_body); - break; - case EVENT_BA_STREAM_TIEMOUT: - mwifiex_dbg(adapter, EVENT, "event: BA Stream timeout\n"); - if (priv->media_connected) { - ba_timeout = (void *)adapter->event_body; - mwifiex_11n_ba_stream_timeout(priv, ba_timeout); - } - break; - case EVENT_EXT_SCAN_REPORT: - mwifiex_dbg(adapter, EVENT, "event: EXT_SCAN Report\n"); - if (adapter->ext_scan) - return mwifiex_handle_event_ext_scan_report(priv, - adapter->event_skb->data); - break; - case EVENT_TX_STATUS_REPORT: - mwifiex_dbg(adapter, EVENT, "event: TX_STATUS Report\n"); - mwifiex_parse_tx_status_event(priv, adapter->event_body); - break; - case EVENT_PS_SLEEP: - mwifiex_dbg(adapter, EVENT, "info: EVENT: SLEEP\n"); - - adapter->ps_state = PS_STATE_PRE_SLEEP; - - mwifiex_check_ps_cond(adapter); - break; - - case EVENT_PS_AWAKE: - mwifiex_dbg(adapter, EVENT, "info: EVENT: AWAKE\n"); - if (!adapter->pps_uapsd_mode && - priv->media_connected && adapter->sleep_period.period) { - adapter->pps_uapsd_mode = true; - mwifiex_dbg(adapter, EVENT, - "event: PPS/UAPSD mode activated\n"); - } - adapter->tx_lock_flag = false; - if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { - if (mwifiex_check_last_packet_indication(priv)) { - if (adapter->data_sent || - (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv))) { - adapter->ps_state = PS_STATE_AWAKE; - adapter->pm_wakeup_card_req = false; - adapter->pm_wakeup_fw_try = false; - break; - } - if (!mwifiex_send_null_packet - (priv, - MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | - MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) - adapter->ps_state = - PS_STATE_SLEEP; - return 0; - } - } - adapter->ps_state = PS_STATE_AWAKE; - adapter->pm_wakeup_card_req = false; - adapter->pm_wakeup_fw_try = false; - break; - - case EVENT_CHANNEL_REPORT_RDY: - mwifiex_dbg(adapter, EVENT, "event: Channel Report\n"); - mwifiex_11h_handle_chanrpt_ready(priv, adapter->event_skb); - break; - case EVENT_RADAR_DETECTED: - mwifiex_dbg(adapter, EVENT, "event: Radar detected\n"); - mwifiex_11h_handle_radar_detected(priv, adapter->event_skb); - break; - case EVENT_BT_COEX_WLAN_PARA_CHANGE: - dev_err(adapter->dev, "EVENT: BT coex wlan param update\n"); - mwifiex_bt_coex_wlan_param_update_event(priv, - adapter->event_skb); - break; - case EVENT_TX_DATA_PAUSE: - mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n"); - mwifiex_process_tx_pause_event(priv, adapter->event_skb); - break; - - case EVENT_MULTI_CHAN_INFO: - mwifiex_dbg(adapter, EVENT, "event: multi-chan info\n"); - mwifiex_process_multi_chan_event(priv, adapter->event_skb); - break; - - default: - mwifiex_dbg(adapter, EVENT, - "event: unknown event id: %#x\n", eventcause); - break; - } - - return 0; -} - -/* This function deletes station entry from associated station list. - * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream - * tables created for this station are deleted. - */ -void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, - struct mwifiex_sta_node *node) -{ - if (priv->ap_11n_enabled && node->is_11n_enabled) { - mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr); - mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr); - } - mwifiex_del_sta_entry(priv, node->mac_addr); - - return; -} diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c deleted file mode 100644 index 74d5d7238633..000000000000 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Marvell Wireless LAN device driver: AP TX and RX data handling - * - * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "main.h" -#include "wmm.h" -#include "11n_aggr.h" -#include "11n_rxreorder.h" - -/* This function checks if particular RA list has packets more than low bridge - * packet threshold and then deletes packet from this RA list. - * Function deletes packets from such RA list and returns true. If no such list - * is found, false is returned. - */ -static bool -mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv, - struct list_head *ra_list_head, - int tid) -{ - struct mwifiex_ra_list_tbl *ra_list; - struct sk_buff *skb, *tmp; - bool pkt_deleted = false; - struct mwifiex_txinfo *tx_info; - struct mwifiex_adapter *adapter = priv->adapter; - - list_for_each_entry(ra_list, ra_list_head, list) { - if (skb_queue_empty(&ra_list->skb_head)) - continue; - - skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) { - tx_info = MWIFIEX_SKB_TXCB(skb); - if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) { - __skb_unlink(skb, &ra_list->skb_head); - mwifiex_write_data_complete(adapter, skb, 0, - -1); - if (ra_list->tx_paused) - priv->wmm.pkts_paused[tid]--; - else - atomic_dec(&priv->wmm.tx_pkts_queued); - pkt_deleted = true; - } - if ((atomic_read(&adapter->pending_bridged_pkts) <= - MWIFIEX_BRIDGED_PKTS_THR_LOW)) - break; - } - } - - return pkt_deleted; -} - -/* This function deletes packets from particular RA List. RA list index - * from which packets are deleted is preserved so that packets from next RA - * list are deleted upon subsequent call thus maintaining fairness. - */ -static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv) -{ - unsigned long flags; - struct list_head *ra_list; - int i; - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) { - if (priv->del_list_idx == MAX_NUM_TID) - priv->del_list_idx = 0; - ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list; - if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) { - priv->del_list_idx++; - break; - } - } - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - - -static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct uap_rxpd *uap_rx_pd; - struct rx_packet_hdr *rx_pkt_hdr; - struct sk_buff *new_skb; - struct mwifiex_txinfo *tx_info; - int hdr_chop; - struct ethhdr *p_ethhdr; - struct mwifiex_sta_node *src_node; - - uap_rx_pd = (struct uap_rxpd *)(skb->data); - rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); - - if ((atomic_read(&adapter->pending_bridged_pkts) >= - MWIFIEX_BRIDGED_PKTS_THR_HIGH)) { - mwifiex_dbg(priv->adapter, ERROR, - "Tx: Bridge packet limit reached. Drop packet!\n"); - kfree_skb(skb); - mwifiex_uap_cleanup_tx_queues(priv); - return; - } - - if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, - sizeof(bridge_tunnel_header))) || - (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, - sizeof(rfc1042_header)) && - ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && - ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { - /* Replace the 803 header and rfc1042 header (llc/snap) with - * an Ethernet II header, keep the src/dst and snap_type - * (ethertype). - * - * The firmware only passes up SNAP frames converting all RX - * data from 802.11 to 802.2/LLC/SNAP frames. - * - * To create the Ethernet II, just move the src, dst address - * right before the snap_type. - */ - p_ethhdr = (struct ethhdr *) - ((u8 *)(&rx_pkt_hdr->eth803_hdr) - + sizeof(rx_pkt_hdr->eth803_hdr) - + sizeof(rx_pkt_hdr->rfc1042_hdr) - - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) - - sizeof(rx_pkt_hdr->eth803_hdr.h_source) - - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); - memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source, - sizeof(p_ethhdr->h_source)); - memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, - sizeof(p_ethhdr->h_dest)); - /* Chop off the rxpd + the excess memory from - * 802.2/llc/snap header that was removed. - */ - hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd; - } else { - /* Chop off the rxpd */ - hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd; - } - - /* Chop off the leading header bytes so that it points - * to the start of either the reconstructed EthII frame - * or the 802.2/llc/snap frame. - */ - skb_pull(skb, hdr_chop); - - if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { - mwifiex_dbg(priv->adapter, ERROR, - "data: Tx: insufficient skb headroom %d\n", - skb_headroom(skb)); - /* Insufficient skb headroom - allocate a new skb */ - new_skb = - skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); - if (unlikely(!new_skb)) { - mwifiex_dbg(priv->adapter, ERROR, - "Tx: cannot allocate new_skb\n"); - kfree_skb(skb); - priv->stats.tx_dropped++; - return; - } - - kfree_skb(skb); - skb = new_skb; - mwifiex_dbg(priv->adapter, INFO, - "info: new skb headroom %d\n", - skb_headroom(skb)); - } - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT; - - src_node = mwifiex_get_sta_entry(priv, rx_pkt_hdr->eth803_hdr.h_source); - if (src_node) { - src_node->stats.last_rx = jiffies; - src_node->stats.rx_bytes += skb->len; - src_node->stats.rx_packets++; - src_node->stats.last_tx_rate = uap_rx_pd->rx_rate; - src_node->stats.last_tx_htinfo = uap_rx_pd->ht_info; - } - - if (is_unicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest)) { - /* Update bridge packet statistics as the - * packet is not going to kernel/upper layer. - */ - priv->stats.rx_bytes += skb->len; - priv->stats.rx_packets++; - - /* Sending bridge packet to TX queue, so save the packet - * length in TXCB to update statistics in TX complete. - */ - tx_info->pkt_len = skb->len; - } - - __net_timestamp(skb); - mwifiex_wmm_add_buf_txqueue(priv, skb); - atomic_inc(&adapter->tx_pending); - atomic_inc(&adapter->pending_bridged_pkts); - - return; -} - -/* - * This function contains logic for AP packet forwarding. - * - * If a packet is multicast/broadcast, it is sent to kernel/upper layer - * as well as queued back to AP TX queue so that it can be sent to other - * associated stations. - * If a packet is unicast and RA is present in associated station list, - * it is again requeued into AP TX queue. - * If a packet is unicast and RA is not in associated station list, - * packet is forwarded to kernel to handle routing logic. - */ -int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct uap_rxpd *uap_rx_pd; - struct rx_packet_hdr *rx_pkt_hdr; - u8 ra[ETH_ALEN]; - struct sk_buff *skb_uap; - - uap_rx_pd = (struct uap_rxpd *)(skb->data); - rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); - - /* don't do packet forwarding in disconnected state */ - if (!priv->media_connected) { - mwifiex_dbg(adapter, ERROR, - "drop packet in disconnected state.\n"); - dev_kfree_skb_any(skb); - return 0; - } - - memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN); - - if (is_multicast_ether_addr(ra)) { - skb_uap = skb_copy(skb, GFP_ATOMIC); - mwifiex_uap_queue_bridged_pkt(priv, skb_uap); - } else { - if (mwifiex_get_sta_entry(priv, ra)) { - /* Requeue Intra-BSS packet */ - mwifiex_uap_queue_bridged_pkt(priv, skb); - return 0; - } - } - - /* Forward unicat/Inter-BSS packets to kernel. */ - return mwifiex_process_rx_packet(priv, skb); -} - -/* - * This function processes the packet received on AP interface. - * - * The function looks into the RxPD and performs sanity tests on the - * received buffer to ensure its a valid packet before processing it - * further. If the packet is determined to be aggregated, it is - * de-aggregated accordingly. Then skb is passed to AP packet forwarding logic. - * - * The completion callback is called after processing is complete. - */ -int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - struct uap_rxpd *uap_rx_pd; - struct rx_packet_hdr *rx_pkt_hdr; - u16 rx_pkt_type; - u8 ta[ETH_ALEN], pkt_type; - unsigned long flags; - struct mwifiex_sta_node *node; - - uap_rx_pd = (struct uap_rxpd *)(skb->data); - rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type); - rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); - - ether_addr_copy(ta, rx_pkt_hdr->eth803_hdr.h_source); - - if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) + - le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16) skb->len) { - mwifiex_dbg(adapter, ERROR, - "wrong rx packet: len=%d, offset=%d, length=%d\n", - skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset), - le16_to_cpu(uap_rx_pd->rx_pkt_length)); - priv->stats.rx_dropped++; - - node = mwifiex_get_sta_entry(priv, ta); - if (node) - node->stats.tx_failed++; - - dev_kfree_skb_any(skb); - return 0; - } - - if (rx_pkt_type == PKT_TYPE_MGMT) { - ret = mwifiex_process_mgmt_packet(priv, skb); - if (ret) - mwifiex_dbg(adapter, ERROR, - "Rx of mgmt packet failed"); - dev_kfree_skb_any(skb); - return ret; - } - - - if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - node = mwifiex_get_sta_entry(priv, ta); - if (node) - node->rx_seq[uap_rx_pd->priority] = - le16_to_cpu(uap_rx_pd->seq_num); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - } - - if (!priv->ap_11n_enabled || - (!mwifiex_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) && - (le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) { - ret = mwifiex_handle_uap_rx_forward(priv, skb); - return ret; - } - - /* Reorder and send to kernel */ - pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type); - ret = mwifiex_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num), - uap_rx_pd->priority, ta, pkt_type, - skb); - - if (ret || (rx_pkt_type == PKT_TYPE_BAR)) - dev_kfree_skb_any(skb); - - if (ret) - priv->stats.rx_dropped++; - - return ret; -} - -/* - * This function fills the TxPD for AP tx packets. - * - * The Tx buffer received by this function should already have the - * header space allocated for TxPD. - * - * This function inserts the TxPD in between interface header and actual - * data and adjusts the buffer pointers accordingly. - * - * The following TxPD fields are set by this function, as required - - * - BSS number - * - Tx packet length and offset - * - Priority - * - Packet delay - * - Priority specific Tx control - * - Flags - */ -void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct uap_txpd *txpd; - struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - int pad; - u16 pkt_type, pkt_offset; - int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : - INTF_HEADER_LEN; - - if (!skb->len) { - mwifiex_dbg(adapter, ERROR, - "Tx: bad packet length: %d\n", skb->len); - tx_info->status_code = -1; - return skb->data; - } - - BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); - - pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; - - pad = ((void *)skb->data - (sizeof(*txpd) + hroom) - NULL) & - (MWIFIEX_DMA_ALIGN_SZ - 1); - - skb_push(skb, sizeof(*txpd) + pad); - - txpd = (struct uap_txpd *)skb->data; - memset(txpd, 0, sizeof(*txpd)); - txpd->bss_num = priv->bss_num; - txpd->bss_type = priv->bss_type; - txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) + - pad))); - txpd->priority = (u8)skb->priority; - - txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb); - - if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS || - tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) { - txpd->tx_token_id = tx_info->ack_frame_id; - txpd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS; - } - - if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) - /* - * Set the priority specific tx_control field, setting of 0 will - * cause the default value to be used later in this function. - */ - txpd->tx_control = - cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]); - - /* Offset of actual data */ - pkt_offset = sizeof(*txpd) + pad; - if (pkt_type == PKT_TYPE_MGMT) { - /* Set the packet type and add header for management frame */ - txpd->tx_pkt_type = cpu_to_le16(pkt_type); - pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; - } - - txpd->tx_pkt_offset = cpu_to_le16(pkt_offset); - - /* make space for INTF_HEADER_LEN */ - skb_push(skb, hroom); - - if (!txpd->tx_control) - /* TxCtrl set by user or default */ - txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); - - return skb->data; -} diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c deleted file mode 100644 index e43aff932360..000000000000 --- a/drivers/net/wireless/mwifiex/usb.c +++ /dev/null @@ -1,1267 +0,0 @@ -/* - * Marvell Wireless LAN device driver: USB specific handling - * - * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "main.h" -#include "usb.h" - -#define USB_VERSION "1.0" - -static u8 user_rmmod; -static struct mwifiex_if_ops usb_ops; -static struct semaphore add_remove_card_sem; - -static struct usb_device_id mwifiex_usb_table[] = { - /* 8766 */ - {USB_DEVICE(USB8XXX_VID, USB8766_PID_1)}, - {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8766_PID_2, - USB_CLASS_VENDOR_SPEC, - USB_SUBCLASS_VENDOR_SPEC, 0xff)}, - /* 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)}, - /* 8801 */ - {USB_DEVICE(USB8XXX_VID, USB8801_PID_1)}, - {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8801_PID_2, - USB_CLASS_VENDOR_SPEC, - USB_SUBCLASS_VENDOR_SPEC, 0xff)}, - /* 8997 */ - {USB_DEVICE(USB8XXX_VID, USB8997_PID_1)}, - {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8997_PID_2, - USB_CLASS_VENDOR_SPEC, - USB_SUBCLASS_VENDOR_SPEC, 0xff)}, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, mwifiex_usb_table); - -static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size); - -/* This function handles received packet. Necessary action is taken based on - * cmd/event/data. - */ -static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, - struct sk_buff *skb, u8 ep) -{ - u32 recv_type; - __le32 tmp; - int ret; - - if (adapter->hs_activated) - mwifiex_process_hs_config(adapter); - - if (skb->len < INTF_HEADER_LEN) { - mwifiex_dbg(adapter, ERROR, - "%s: invalid skb->len\n", __func__); - return -1; - } - - switch (ep) { - case MWIFIEX_USB_EP_CMD_EVENT: - mwifiex_dbg(adapter, EVENT, - "%s: EP_CMD_EVENT\n", __func__); - skb_copy_from_linear_data(skb, &tmp, INTF_HEADER_LEN); - recv_type = le32_to_cpu(tmp); - skb_pull(skb, INTF_HEADER_LEN); - - switch (recv_type) { - case MWIFIEX_USB_TYPE_CMD: - if (skb->len > MWIFIEX_SIZE_OF_CMD_BUFFER) { - mwifiex_dbg(adapter, ERROR, - "CMD: skb->len too large\n"); - ret = -1; - goto exit_restore_skb; - } else if (!adapter->curr_cmd) { - mwifiex_dbg(adapter, WARN, "CMD: no curr_cmd\n"); - if (adapter->ps_state == PS_STATE_SLEEP_CFM) { - mwifiex_process_sleep_confirm_resp( - adapter, skb->data, - skb->len); - ret = 0; - goto exit_restore_skb; - } - ret = -1; - goto exit_restore_skb; - } - - adapter->curr_cmd->resp_skb = skb; - adapter->cmd_resp_received = true; - break; - case MWIFIEX_USB_TYPE_EVENT: - if (skb->len < sizeof(u32)) { - mwifiex_dbg(adapter, ERROR, - "EVENT: skb->len too small\n"); - ret = -1; - goto exit_restore_skb; - } - skb_copy_from_linear_data(skb, &tmp, sizeof(u32)); - adapter->event_cause = le32_to_cpu(tmp); - mwifiex_dbg(adapter, EVENT, - "event_cause %#x\n", adapter->event_cause); - - if (skb->len > MAX_EVENT_SIZE) { - mwifiex_dbg(adapter, ERROR, - "EVENT: event body too large\n"); - ret = -1; - goto exit_restore_skb; - } - - memcpy(adapter->event_body, skb->data + - MWIFIEX_EVENT_HEADER_LEN, skb->len); - - adapter->event_received = true; - adapter->event_skb = skb; - break; - default: - mwifiex_dbg(adapter, ERROR, - "unknown recv_type %#x\n", recv_type); - return -1; - } - break; - case MWIFIEX_USB_EP_DATA: - mwifiex_dbg(adapter, DATA, "%s: EP_DATA\n", __func__); - if (skb->len > MWIFIEX_RX_DATA_BUF_SIZE) { - mwifiex_dbg(adapter, ERROR, - "DATA: skb->len too large\n"); - return -1; - } - - skb_queue_tail(&adapter->rx_data_q, skb); - adapter->data_received = true; - atomic_inc(&adapter->rx_pending); - break; - default: - mwifiex_dbg(adapter, ERROR, - "%s: unknown endport %#x\n", __func__, ep); - return -1; - } - - return -EINPROGRESS; - -exit_restore_skb: - /* The buffer will be reused for further cmds/events */ - skb_push(skb, INTF_HEADER_LEN); - - return ret; -} - -static void mwifiex_usb_rx_complete(struct urb *urb) -{ - struct urb_context *context = (struct urb_context *)urb->context; - struct mwifiex_adapter *adapter = context->adapter; - struct sk_buff *skb = context->skb; - struct usb_card_rec *card; - int recv_length = urb->actual_length; - int size, status; - - if (!adapter || !adapter->card) { - pr_err("mwifiex adapter or card structure is not valid\n"); - return; - } - - card = (struct usb_card_rec *)adapter->card; - if (card->rx_cmd_ep == context->ep) - atomic_dec(&card->rx_cmd_urb_pending); - else - atomic_dec(&card->rx_data_urb_pending); - - if (recv_length) { - if (urb->status || (adapter->surprise_removed)) { - mwifiex_dbg(adapter, ERROR, - "URB status is failed: %d\n", urb->status); - /* Do not free skb in case of command ep */ - if (card->rx_cmd_ep != context->ep) - dev_kfree_skb_any(skb); - goto setup_for_next; - } - if (skb->len > recv_length) - skb_trim(skb, recv_length); - else - skb_put(skb, recv_length - skb->len); - - status = mwifiex_usb_recv(adapter, skb, context->ep); - - mwifiex_dbg(adapter, INFO, - "info: recv_length=%d, status=%d\n", - recv_length, status); - if (status == -EINPROGRESS) { - mwifiex_queue_main_work(adapter); - - /* urb for data_ep is re-submitted now; - * urb for cmd_ep will be re-submitted in callback - * mwifiex_usb_recv_complete - */ - if (card->rx_cmd_ep == context->ep) - return; - } else { - if (status == -1) - mwifiex_dbg(adapter, ERROR, - "received data processing failed!\n"); - - /* Do not free skb in case of command ep */ - if (card->rx_cmd_ep != context->ep) - dev_kfree_skb_any(skb); - } - } else if (urb->status) { - if (!adapter->is_suspended) { - mwifiex_dbg(adapter, FATAL, - "Card is removed: %d\n", urb->status); - adapter->surprise_removed = true; - } - dev_kfree_skb_any(skb); - return; - } else { - /* Do not free skb in case of command ep */ - if (card->rx_cmd_ep != context->ep) - dev_kfree_skb_any(skb); - - /* fall through setup_for_next */ - } - -setup_for_next: - if (card->rx_cmd_ep == context->ep) - size = MWIFIEX_RX_CMD_BUF_SIZE; - else - size = MWIFIEX_RX_DATA_BUF_SIZE; - - if (card->rx_cmd_ep == context->ep) { - mwifiex_usb_submit_rx_urb(context, size); - } else { - if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING){ - mwifiex_usb_submit_rx_urb(context, size); - }else{ - context->skb = NULL; - } - } - - return; -} - -static void mwifiex_usb_tx_complete(struct urb *urb) -{ - struct urb_context *context = (struct urb_context *)(urb->context); - struct mwifiex_adapter *adapter = context->adapter; - struct usb_card_rec *card = adapter->card; - struct usb_tx_data_port *port; - int i; - - mwifiex_dbg(adapter, INFO, - "%s: status: %d\n", __func__, urb->status); - - if (context->ep == card->tx_cmd_ep) { - mwifiex_dbg(adapter, CMD, - "%s: CMD\n", __func__); - atomic_dec(&card->tx_cmd_urb_pending); - adapter->cmd_sent = false; - } else { - mwifiex_dbg(adapter, DATA, - "%s: DATA\n", __func__); - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - port = &card->port[i]; - if (context->ep == port->tx_data_ep) { - atomic_dec(&port->tx_data_urb_pending); - port->block_status = false; - break; - } - } - adapter->data_sent = false; - mwifiex_write_data_complete(adapter, context->skb, 0, - urb->status ? -1 : 0); - } - - if (card->mc_resync_flag) - mwifiex_multi_chan_resync(adapter); - - mwifiex_queue_main_work(adapter); - - return; -} - -static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size) -{ - struct mwifiex_adapter *adapter = ctx->adapter; - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - - if (card->rx_cmd_ep != ctx->ep) { - ctx->skb = dev_alloc_skb(size); - if (!ctx->skb) { - mwifiex_dbg(adapter, ERROR, - "%s: dev_alloc_skb failed\n", __func__); - return -ENOMEM; - } - } - - usb_fill_bulk_urb(ctx->urb, card->udev, - usb_rcvbulkpipe(card->udev, ctx->ep), ctx->skb->data, - size, mwifiex_usb_rx_complete, (void *)ctx); - - if (card->rx_cmd_ep == ctx->ep) - atomic_inc(&card->rx_cmd_urb_pending); - else - atomic_inc(&card->rx_data_urb_pending); - - if (usb_submit_urb(ctx->urb, GFP_ATOMIC)) { - mwifiex_dbg(adapter, ERROR, "usb_submit_urb failed\n"); - dev_kfree_skb_any(ctx->skb); - ctx->skb = NULL; - - if (card->rx_cmd_ep == ctx->ep) - atomic_dec(&card->rx_cmd_urb_pending); - else - atomic_dec(&card->rx_data_urb_pending); - - return -1; - } - - return 0; -} - -static void mwifiex_usb_free(struct usb_card_rec *card) -{ - struct usb_tx_data_port *port; - int i, j; - - if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) - usb_kill_urb(card->rx_cmd.urb); - - usb_free_urb(card->rx_cmd.urb); - card->rx_cmd.urb = NULL; - - if (atomic_read(&card->rx_data_urb_pending)) - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) - if (card->rx_data_list[i].urb) - usb_kill_urb(card->rx_data_list[i].urb); - - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { - usb_free_urb(card->rx_data_list[i].urb); - card->rx_data_list[i].urb = NULL; - } - - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - port = &card->port[i]; - for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { - usb_free_urb(port->tx_data_list[j].urb); - port->tx_data_list[j].urb = NULL; - } - } - - usb_free_urb(card->tx_cmd.urb); - card->tx_cmd.urb = NULL; - - return; -} - -/* This function probes an mwifiex device and registers it. It allocates - * the card structure, initiates the device registration and initialization - * procedure by adding a logical interface. - */ -static int mwifiex_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_host_interface *iface_desc = intf->cur_altsetting; - struct usb_endpoint_descriptor *epd; - int ret, i; - struct usb_card_rec *card; - u16 id_vendor, id_product, bcd_device, bcd_usb; - - card = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); - if (!card) - return -ENOMEM; - - id_vendor = le16_to_cpu(udev->descriptor.idVendor); - id_product = le16_to_cpu(udev->descriptor.idProduct); - bcd_device = le16_to_cpu(udev->descriptor.bcdDevice); - bcd_usb = le16_to_cpu(udev->descriptor.bcdUSB); - pr_debug("info: VID/PID = %X/%X, Boot2 version = %X\n", - id_vendor, id_product, bcd_device); - - /* PID_1 is used for firmware downloading only */ - switch (id_product) { - case USB8766_PID_1: - case USB8797_PID_1: - case USB8801_PID_1: - case USB8997_PID_1: - card->usb_boot_state = USB8XXX_FW_DNLD; - break; - case USB8766_PID_2: - case USB8797_PID_2: - case USB8801_PID_2: - case USB8997_PID_2: - card->usb_boot_state = USB8XXX_FW_READY; - break; - default: - pr_warn("unknown id_product %#x\n", id_product); - card->usb_boot_state = USB8XXX_FW_DNLD; - break; - } - - card->udev = udev; - card->intf = intf; - - pr_debug("info: bcdUSB=%#x Device Class=%#x SubClass=%#x Protocol=%#x\n", - udev->descriptor.bcdUSB, udev->descriptor.bDeviceClass, - udev->descriptor.bDeviceSubClass, - udev->descriptor.bDeviceProtocol); - - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - epd = &iface_desc->endpoint[i].desc; - if (usb_endpoint_dir_in(epd) && - usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && - usb_endpoint_xfer_bulk(epd)) { - pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", - le16_to_cpu(epd->wMaxPacketSize), - epd->bEndpointAddress); - card->rx_cmd_ep = usb_endpoint_num(epd); - atomic_set(&card->rx_cmd_urb_pending, 0); - } - if (usb_endpoint_dir_in(epd) && - usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && - usb_endpoint_xfer_bulk(epd)) { - pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", - le16_to_cpu(epd->wMaxPacketSize), - epd->bEndpointAddress); - card->rx_data_ep = usb_endpoint_num(epd); - atomic_set(&card->rx_data_urb_pending, 0); - } - if (usb_endpoint_dir_out(epd) && - usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && - usb_endpoint_xfer_bulk(epd)) { - pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", - le16_to_cpu(epd->wMaxPacketSize), - epd->bEndpointAddress); - card->port[0].tx_data_ep = usb_endpoint_num(epd); - atomic_set(&card->port[0].tx_data_urb_pending, 0); - } - if (usb_endpoint_dir_out(epd) && - usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA_CH2 && - usb_endpoint_xfer_bulk(epd)) { - pr_debug("info: bulk OUT chan2:\t" - "max pkt size: %d, addr: %d\n", - le16_to_cpu(epd->wMaxPacketSize), - epd->bEndpointAddress); - card->port[1].tx_data_ep = usb_endpoint_num(epd); - atomic_set(&card->port[1].tx_data_urb_pending, 0); - } - if (usb_endpoint_dir_out(epd) && - usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && - usb_endpoint_xfer_bulk(epd)) { - pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", - le16_to_cpu(epd->wMaxPacketSize), - epd->bEndpointAddress); - card->tx_cmd_ep = usb_endpoint_num(epd); - atomic_set(&card->tx_cmd_urb_pending, 0); - card->bulk_out_maxpktsize = - le16_to_cpu(epd->wMaxPacketSize); - } - } - - usb_set_intfdata(intf, card); - - ret = mwifiex_add_card(card, &add_remove_card_sem, &usb_ops, - MWIFIEX_USB); - if (ret) { - pr_err("%s: mwifiex_add_card failed: %d\n", __func__, ret); - usb_reset_device(udev); - kfree(card); - return ret; - } - - usb_get_dev(udev); - - return 0; -} - -/* Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not suspended, this function allocates and sends a - * 'host sleep activate' request to the firmware and turns off the traffic. - */ -static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct usb_card_rec *card = usb_get_intfdata(intf); - struct mwifiex_adapter *adapter; - struct usb_tx_data_port *port; - int i, j; - - if (!card || !card->adapter) { - pr_err("%s: card or card->adapter is NULL\n", __func__); - return 0; - } - adapter = card->adapter; - - if (unlikely(adapter->is_suspended)) - mwifiex_dbg(adapter, WARN, - "Device already suspended\n"); - - mwifiex_enable_hs(adapter); - - /* 'is_suspended' flag indicates device is suspended. - * It must be set here before the usb_kill_urb() calls. Reason - * is in the complete handlers, urb->status(= -ENOENT) and - * this flag is used in combination to distinguish between a - * 'suspended' state and a 'disconnect' one. - */ - adapter->is_suspended = true; - adapter->hs_enabling = false; - - if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) - usb_kill_urb(card->rx_cmd.urb); - - if (atomic_read(&card->rx_data_urb_pending)) - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) - if (card->rx_data_list[i].urb) - usb_kill_urb(card->rx_data_list[i].urb); - - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - port = &card->port[i]; - for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { - if (port->tx_data_list[j].urb) - usb_kill_urb(port->tx_data_list[j].urb); - } - } - - if (card->tx_cmd.urb) - usb_kill_urb(card->tx_cmd.urb); - - return 0; -} - -/* Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not resumed, this function turns on the traffic and - * sends a 'host sleep cancel' request to the firmware. - */ -static int mwifiex_usb_resume(struct usb_interface *intf) -{ - struct usb_card_rec *card = usb_get_intfdata(intf); - struct mwifiex_adapter *adapter; - int i; - - if (!card || !card->adapter) { - pr_err("%s: card or card->adapter is NULL\n", __func__); - return 0; - } - adapter = card->adapter; - - if (unlikely(!adapter->is_suspended)) { - mwifiex_dbg(adapter, WARN, - "Device already resumed\n"); - return 0; - } - - /* Indicate device resumed. The netdev queue will be resumed only - * after the urbs have been re-submitted - */ - adapter->is_suspended = false; - - if (!atomic_read(&card->rx_data_urb_pending)) - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) - mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], - MWIFIEX_RX_DATA_BUF_SIZE); - - if (!atomic_read(&card->rx_cmd_urb_pending)) { - card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); - if (card->rx_cmd.skb) - mwifiex_usb_submit_rx_urb(&card->rx_cmd, - MWIFIEX_RX_CMD_BUF_SIZE); - } - - /* Disable Host Sleep */ - if (adapter->hs_activated) - mwifiex_cancel_hs(mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY), - MWIFIEX_ASYNC_CMD); - - return 0; -} - -static void mwifiex_usb_disconnect(struct usb_interface *intf) -{ - struct usb_card_rec *card = usb_get_intfdata(intf); - struct mwifiex_adapter *adapter; - - if (!card || !card->adapter) { - pr_err("%s: card or card->adapter is NULL\n", __func__); - return; - } - - adapter = card->adapter; - if (!adapter->priv_num) - return; - - if (user_rmmod) { -#ifdef CONFIG_PM - if (adapter->is_suspended) - mwifiex_usb_resume(intf); -#endif - - mwifiex_deauthenticate_all(adapter); - - mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY), - MWIFIEX_FUNC_SHUTDOWN); - } - - mwifiex_usb_free(card); - - mwifiex_dbg(adapter, FATAL, - "%s: removing card\n", __func__); - mwifiex_remove_card(adapter, &add_remove_card_sem); - - usb_set_intfdata(intf, NULL); - usb_put_dev(interface_to_usbdev(intf)); - kfree(card); - - return; -} - -static struct usb_driver mwifiex_usb_driver = { - .name = "mwifiex_usb", - .probe = mwifiex_usb_probe, - .disconnect = mwifiex_usb_disconnect, - .id_table = mwifiex_usb_table, - .suspend = mwifiex_usb_suspend, - .resume = mwifiex_usb_resume, - .soft_unbind = 1, -}; - -static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - struct usb_tx_data_port *port; - int i, j; - - card->tx_cmd.adapter = adapter; - card->tx_cmd.ep = card->tx_cmd_ep; - - card->tx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); - if (!card->tx_cmd.urb) { - mwifiex_dbg(adapter, ERROR, - "tx_cmd.urb allocation failed\n"); - return -ENOMEM; - } - - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - port = &card->port[i]; - if (!port->tx_data_ep) - continue; - port->tx_data_ix = 0; - if (port->tx_data_ep == MWIFIEX_USB_EP_DATA) - port->block_status = false; - else - port->block_status = true; - for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { - port->tx_data_list[j].adapter = adapter; - port->tx_data_list[j].ep = port->tx_data_ep; - port->tx_data_list[j].urb = - usb_alloc_urb(0, GFP_KERNEL); - if (!port->tx_data_list[j].urb) { - mwifiex_dbg(adapter, ERROR, - "urb allocation failed\n"); - return -ENOMEM; - } - } - } - - return 0; -} - -static int mwifiex_usb_rx_init(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - int i; - - card->rx_cmd.adapter = adapter; - card->rx_cmd.ep = card->rx_cmd_ep; - - card->rx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); - if (!card->rx_cmd.urb) { - mwifiex_dbg(adapter, ERROR, "rx_cmd.urb allocation failed\n"); - return -ENOMEM; - } - - card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); - if (!card->rx_cmd.skb) - return -ENOMEM; - - if (mwifiex_usb_submit_rx_urb(&card->rx_cmd, MWIFIEX_RX_CMD_BUF_SIZE)) - return -1; - - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { - card->rx_data_list[i].adapter = adapter; - card->rx_data_list[i].ep = card->rx_data_ep; - - card->rx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); - if (!card->rx_data_list[i].urb) { - mwifiex_dbg(adapter, ERROR, - "rx_data_list[] urb allocation failed\n"); - return -1; - } - if (mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], - MWIFIEX_RX_DATA_BUF_SIZE)) - return -1; - } - - return 0; -} - -static int mwifiex_write_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, - u32 *len, u8 ep, u32 timeout) -{ - struct usb_card_rec *card = adapter->card; - int actual_length, ret; - - if (!(*len % card->bulk_out_maxpktsize)) - (*len)++; - - /* Send the data block */ - ret = usb_bulk_msg(card->udev, usb_sndbulkpipe(card->udev, ep), pbuf, - *len, &actual_length, timeout); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "usb_bulk_msg for tx failed: %d\n", ret); - return ret; - } - - *len = actual_length; - - return ret; -} - -static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, - u32 *len, u8 ep, u32 timeout) -{ - struct usb_card_rec *card = adapter->card; - int actual_length, ret; - - /* Receive the data response */ - ret = usb_bulk_msg(card->udev, usb_rcvbulkpipe(card->udev, ep), pbuf, - *len, &actual_length, timeout); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "usb_bulk_msg for rx failed: %d\n", ret); - return ret; - } - - *len = actual_length; - - return ret; -} - -static void mwifiex_usb_port_resync(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = adapter->card; - u8 active_port = MWIFIEX_USB_EP_DATA; - struct mwifiex_private *priv = NULL; - int i; - - if (adapter->usb_mc_status) { - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && - !priv->bss_started) || - (priv->bss_role == MWIFIEX_BSS_ROLE_STA && - !priv->media_connected)) - priv->usb_port = MWIFIEX_USB_EP_DATA; - } - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) - card->port[i].block_status = false; - } else { - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && - priv->bss_started) || - (priv->bss_role == MWIFIEX_BSS_ROLE_STA && - priv->media_connected)) { - active_port = priv->usb_port; - break; - } - } - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv) - priv->usb_port = active_port; - } - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - if (active_port == card->port[i].tx_data_ep) - card->port[i].block_status = false; - else - card->port[i].block_status = true; - } - } -} - -static bool mwifiex_usb_is_port_ready(struct mwifiex_private *priv) -{ - struct usb_card_rec *card = priv->adapter->card; - int idx; - - for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { - if (priv->usb_port == card->port[idx].tx_data_ep) - return !card->port[idx].block_status; - } - - return false; -} - -static inline u8 mwifiex_usb_data_sent(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = adapter->card; - int i; - - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) - if (!card->port[i].block_status) - return false; - - return true; -} - -/* This function write a command/data packet to card. */ -static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, - struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - struct usb_card_rec *card = adapter->card; - struct urb_context *context = NULL; - struct usb_tx_data_port *port = NULL; - u8 *data = (u8 *)skb->data; - struct urb *tx_urb; - int idx, ret; - - if (adapter->is_suspended) { - mwifiex_dbg(adapter, ERROR, - "%s: not allowed while suspended\n", __func__); - return -1; - } - - if (adapter->surprise_removed) { - mwifiex_dbg(adapter, ERROR, "%s: device removed\n", __func__); - return -1; - } - - mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep); - - if (ep == card->tx_cmd_ep) { - context = &card->tx_cmd; - } else { - for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { - if (ep == card->port[idx].tx_data_ep) { - port = &card->port[idx]; - if (atomic_read(&port->tx_data_urb_pending) - >= MWIFIEX_TX_DATA_URB) { - port->block_status = true; - ret = -EBUSY; - goto done; - } - if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) - port->tx_data_ix = 0; - context = - &port->tx_data_list[port->tx_data_ix++]; - break; - } - } - if (!port) { - mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n"); - return -1; - } - } - - context->adapter = adapter; - context->ep = ep; - context->skb = skb; - tx_urb = context->urb; - - usb_fill_bulk_urb(tx_urb, card->udev, usb_sndbulkpipe(card->udev, ep), - data, skb->len, mwifiex_usb_tx_complete, - (void *)context); - - tx_urb->transfer_flags |= URB_ZERO_PACKET; - - if (ep == card->tx_cmd_ep) - atomic_inc(&card->tx_cmd_urb_pending); - else - atomic_inc(&port->tx_data_urb_pending); - - if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { - mwifiex_dbg(adapter, ERROR, - "%s: usb_submit_urb failed\n", __func__); - if (ep == card->tx_cmd_ep) { - atomic_dec(&card->tx_cmd_urb_pending); - } else { - atomic_dec(&port->tx_data_urb_pending); - port->block_status = false; - if (port->tx_data_ix) - port->tx_data_ix--; - else - port->tx_data_ix = MWIFIEX_TX_DATA_URB; - } - - return -1; - } else { - if (ep != card->tx_cmd_ep && - atomic_read(&port->tx_data_urb_pending) == - MWIFIEX_TX_DATA_URB) { - port->block_status = true; - ret = -ENOSR; - goto done; - } - } - - return -EINPROGRESS; - -done: - if (ep != card->tx_cmd_ep) - adapter->data_sent = mwifiex_usb_data_sent(adapter); - - return ret; -} - -/* This function register usb device and initialize parameter. */ -static int mwifiex_register_dev(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - - card->adapter = adapter; - adapter->dev = &card->udev->dev; - - switch (le16_to_cpu(card->udev->descriptor.idProduct)) { - case USB8997_PID_1: - case USB8997_PID_2: - adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K; - strcpy(adapter->fw_name, USB8997_DEFAULT_FW_NAME); - adapter->ext_scan = true; - break; - case USB8766_PID_1: - case USB8766_PID_2: - adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; - strcpy(adapter->fw_name, USB8766_DEFAULT_FW_NAME); - adapter->ext_scan = true; - break; - case USB8801_PID_1: - case USB8801_PID_2: - adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; - strcpy(adapter->fw_name, USB8801_DEFAULT_FW_NAME); - adapter->ext_scan = false; - break; - case USB8797_PID_1: - case USB8797_PID_2: - default: - adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; - strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME); - break; - } - - adapter->usb_mc_status = false; - adapter->usb_mc_setup = false; - - return 0; -} - -static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - - card->adapter = NULL; -} - -static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, - struct mwifiex_fw_image *fw) -{ - int ret = 0; - u8 *firmware = fw->fw_buf, *recv_buff; - 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; - u8 check_winner = 1; - - if (!firmware) { - mwifiex_dbg(adapter, ERROR, - "No firmware image found! Terminating download\n"); - ret = -1; - goto fw_exit; - } - - /* Allocate memory for transmit */ - fwdata = kzalloc(FW_DNLD_TX_BUF_SIZE, GFP_KERNEL); - if (!fwdata) { - ret = -ENOMEM; - goto fw_exit; - } - - /* Allocate memory for receive */ - recv_buff = kzalloc(FW_DNLD_RX_BUF_SIZE, GFP_KERNEL); - if (!recv_buff) - goto cleanup; - - do { - /* Send pseudo data to check winner status first */ - if (check_winner) { - memset(&fwdata->fw_hdr, 0, sizeof(struct fw_header)); - dlen = 0; - } else { - /* copy the header of the fw_data to get the length */ - memcpy(&fwdata->fw_hdr, &firmware[tlen], - sizeof(struct fw_header)); - - dlen = le32_to_cpu(fwdata->fw_hdr.data_len); - dnld_cmd = le32_to_cpu(fwdata->fw_hdr.dnld_cmd); - tlen += sizeof(struct fw_header); - - memcpy(fwdata->data, &firmware[tlen], dlen); - - fwdata->seq_num = cpu_to_le32(fw_seqnum); - tlen += dlen; - } - - /* If the send/receive fails or CRC occurs then retry */ - while (retries--) { - u8 *buf = (u8 *)fwdata; - u32 len = FW_DATA_XMIT_SIZE; - - /* send the firmware block */ - ret = mwifiex_write_data_sync(adapter, buf, &len, - MWIFIEX_USB_EP_CMD_EVENT, - MWIFIEX_USB_TIMEOUT); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "write_data_sync: failed: %d\n", - ret); - continue; - } - - buf = recv_buff; - len = FW_DNLD_RX_BUF_SIZE; - - /* Receive the firmware block response */ - ret = mwifiex_read_data_sync(adapter, buf, &len, - MWIFIEX_USB_EP_CMD_EVENT, - MWIFIEX_USB_TIMEOUT); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "read_data_sync: failed: %d\n", - ret); - continue; - } - - memcpy(&sync_fw, recv_buff, - sizeof(struct fw_sync_header)); - - /* check 1st firmware block resp for highest bit set */ - if (check_winner) { - if (le32_to_cpu(sync_fw.cmd) & 0x80000000) { - mwifiex_dbg(adapter, WARN, - "USB is not the winner %#x\n", - sync_fw.cmd); - - /* returning success */ - ret = 0; - goto cleanup; - } - - mwifiex_dbg(adapter, MSG, - "start to download FW...\n"); - - check_winner = 0; - break; - } - - /* check the firmware block response for CRC errors */ - if (sync_fw.cmd) { - mwifiex_dbg(adapter, ERROR, - "FW received block with CRC %#x\n", - sync_fw.cmd); - ret = -1; - continue; - } - - retries = USB8XXX_FW_MAX_RETRY; - break; - } - fw_seqnum++; - } while ((dnld_cmd != FW_HAS_LAST_BLOCK) && retries); - -cleanup: - mwifiex_dbg(adapter, MSG, - "info: FW download over, size %d bytes\n", tlen); - - kfree(recv_buff); - kfree(fwdata); - - if (retries) - ret = 0; -fw_exit: - return ret; -} - -static int mwifiex_usb_dnld_fw(struct mwifiex_adapter *adapter, - struct mwifiex_fw_image *fw) -{ - int ret; - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - - 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 == USB8XXX_FW_DNLD) - return -1; - } - - ret = mwifiex_usb_rx_init(adapter); - if (!ret) - ret = mwifiex_usb_tx_init(adapter); - - return ret; -} - -static void mwifiex_submit_rx_urb(struct mwifiex_adapter *adapter, u8 ep) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - - skb_push(card->rx_cmd.skb, INTF_HEADER_LEN); - if ((ep == card->rx_cmd_ep) && - (!atomic_read(&card->rx_cmd_urb_pending))) - mwifiex_usb_submit_rx_urb(&card->rx_cmd, - MWIFIEX_RX_CMD_BUF_SIZE); - - return; -} - -static int mwifiex_usb_cmd_event_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - mwifiex_submit_rx_urb(adapter, MWIFIEX_USB_EP_CMD_EVENT); - - return 0; -} - -/* This function wakes up the card. */ -static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) -{ - /* Simulation of HS_AWAKE event */ - adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); - adapter->pm_wakeup_card_req = false; - adapter->ps_state = PS_STATE_AWAKE; - - return 0; -} - -static void mwifiex_usb_submit_rem_rx_urbs(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - int i; - struct urb_context *ctx; - - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { - if (card->rx_data_list[i].skb) - continue; - ctx = &card->rx_data_list[i]; - mwifiex_usb_submit_rx_urb(ctx, MWIFIEX_RX_DATA_BUF_SIZE); - } -} - -/* This function is called after the card has woken up. */ -static inline int -mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) -{ - return 0; -} - -static struct mwifiex_if_ops usb_ops = { - .register_dev = mwifiex_register_dev, - .unregister_dev = mwifiex_unregister_dev, - .wakeup = mwifiex_pm_wakeup_card, - .wakeup_complete = mwifiex_pm_wakeup_card_complete, - - /* USB specific */ - .dnld_fw = mwifiex_usb_dnld_fw, - .cmdrsp_complete = mwifiex_usb_cmd_event_complete, - .event_complete = mwifiex_usb_cmd_event_complete, - .host_to_card = mwifiex_usb_host_to_card, - .submit_rem_rx_urbs = mwifiex_usb_submit_rem_rx_urbs, - .multi_port_resync = mwifiex_usb_port_resync, - .is_port_ready = mwifiex_usb_is_port_ready, -}; - -/* This function initializes the USB driver module. - * - * This initiates the semaphore and registers the device with - * USB bus. - */ -static int mwifiex_usb_init_module(void) -{ - int ret; - - pr_debug("Marvell USB8797 Driver\n"); - - sema_init(&add_remove_card_sem, 1); - - ret = usb_register(&mwifiex_usb_driver); - if (ret) - pr_err("Driver register failed!\n"); - else - pr_debug("info: Driver registered successfully!\n"); - - return ret; -} - -/* This function cleans up the USB driver. - * - * The following major steps are followed in .disconnect for cleanup: - * - Resume the device if its suspended - * - Disconnect the device if connected - * - Shutdown the firmware - * - Unregister the device from USB bus. - */ -static void mwifiex_usb_cleanup_module(void) -{ - if (!down_interruptible(&add_remove_card_sem)) - up(&add_remove_card_sem); - - /* set the flag as user is removing this module */ - user_rmmod = 1; - - usb_deregister(&mwifiex_usb_driver); -} - -module_init(mwifiex_usb_init_module); -module_exit(mwifiex_usb_cleanup_module); - -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(USB8766_DEFAULT_FW_NAME); -MODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME); -MODULE_FIRMWARE(USB8801_DEFAULT_FW_NAME); -MODULE_FIRMWARE(USB8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h deleted file mode 100644 index b4e9246bbcdc..000000000000 --- a/drivers/net/wireless/mwifiex/usb.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This file contains definitions for mwifiex USB interface driver. - * - * Copyright (C) 2012-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_USB_H -#define _MWIFIEX_USB_H - -#include - -#define USB8XXX_VID 0x1286 - -#define USB8766_PID_1 0x2041 -#define USB8766_PID_2 0x2042 -#define USB8797_PID_1 0x2043 -#define USB8797_PID_2 0x2044 -#define USB8801_PID_1 0x2049 -#define USB8801_PID_2 0x204a -#define USB8997_PID_1 0x2052 -#define USB8997_PID_2 0x204e - - -#define USB8XXX_FW_DNLD 1 -#define USB8XXX_FW_READY 2 -#define USB8XXX_FW_MAX_RETRY 3 - -#define MWIFIEX_TX_DATA_PORT 2 -#define MWIFIEX_TX_DATA_URB 6 -#define MWIFIEX_RX_DATA_URB 6 -#define MWIFIEX_USB_TIMEOUT 100 - -#define USB8766_DEFAULT_FW_NAME "mrvl/usb8766_uapsta.bin" -#define USB8797_DEFAULT_FW_NAME "mrvl/usb8797_uapsta.bin" -#define USB8801_DEFAULT_FW_NAME "mrvl/usb8801_uapsta.bin" -#define USB8997_DEFAULT_FW_NAME "mrvl/usb8997_uapsta.bin" - -#define FW_DNLD_TX_BUF_SIZE 620 -#define FW_DNLD_RX_BUF_SIZE 2048 -#define FW_HAS_LAST_BLOCK 0x00000004 - -#define FW_DATA_XMIT_SIZE \ - (sizeof(struct fw_header) + dlen + sizeof(u32)) - -struct urb_context { - struct mwifiex_adapter *adapter; - struct sk_buff *skb; - struct urb *urb; - u8 ep; -}; - -struct usb_tx_data_port { - u8 tx_data_ep; - u8 block_status; - atomic_t tx_data_urb_pending; - int tx_data_ix; - struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB]; -}; - -struct usb_card_rec { - struct mwifiex_adapter *adapter; - struct usb_device *udev; - struct usb_interface *intf; - u8 rx_cmd_ep; - struct urb_context rx_cmd; - atomic_t rx_cmd_urb_pending; - struct urb_context rx_data_list[MWIFIEX_RX_DATA_URB]; - u8 usb_boot_state; - u8 rx_data_ep; - atomic_t rx_data_urb_pending; - u8 tx_cmd_ep; - atomic_t tx_cmd_urb_pending; - int bulk_out_maxpktsize; - struct urb_context tx_cmd; - u8 mc_resync_flag; - struct usb_tx_data_port port[MWIFIEX_TX_DATA_PORT]; -}; - -struct fw_header { - __le32 dnld_cmd; - __le32 base_addr; - __le32 data_len; - __le32 crc; -}; - -struct fw_sync_header { - __le32 cmd; - __le32 seq_num; -}; - -struct fw_data { - struct fw_header fw_hdr; - __le32 seq_num; - u8 data[1]; -}; - -#endif /*_MWIFIEX_USB_H */ diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c deleted file mode 100644 index 0cec8a64473e..000000000000 --- a/drivers/net/wireless/mwifiex/util.c +++ /dev/null @@ -1,751 +0,0 @@ -/* - * Marvell Wireless LAN device driver: utility functions - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" - -static struct mwifiex_debug_data items[] = { - {"debug_mask", item_size(debug_mask), - item_addr(debug_mask), 1}, - {"int_counter", item_size(int_counter), - item_addr(int_counter), 1}, - {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]), - item_addr(packets_out[WMM_AC_VO]), 1}, - {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]), - item_addr(packets_out[WMM_AC_VI]), 1}, - {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]), - item_addr(packets_out[WMM_AC_BE]), 1}, - {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]), - item_addr(packets_out[WMM_AC_BK]), 1}, - {"tx_buf_size", item_size(tx_buf_size), - item_addr(tx_buf_size), 1}, - {"curr_tx_buf_size", item_size(curr_tx_buf_size), - item_addr(curr_tx_buf_size), 1}, - {"ps_mode", item_size(ps_mode), - item_addr(ps_mode), 1}, - {"ps_state", item_size(ps_state), - item_addr(ps_state), 1}, - {"is_deep_sleep", item_size(is_deep_sleep), - item_addr(is_deep_sleep), 1}, - {"wakeup_dev_req", item_size(pm_wakeup_card_req), - item_addr(pm_wakeup_card_req), 1}, - {"wakeup_tries", item_size(pm_wakeup_fw_try), - item_addr(pm_wakeup_fw_try), 1}, - {"hs_configured", item_size(is_hs_configured), - item_addr(is_hs_configured), 1}, - {"hs_activated", item_size(hs_activated), - item_addr(hs_activated), 1}, - {"num_tx_timeout", item_size(num_tx_timeout), - item_addr(num_tx_timeout), 1}, - {"is_cmd_timedout", item_size(is_cmd_timedout), - item_addr(is_cmd_timedout), 1}, - {"timeout_cmd_id", item_size(timeout_cmd_id), - item_addr(timeout_cmd_id), 1}, - {"timeout_cmd_act", item_size(timeout_cmd_act), - item_addr(timeout_cmd_act), 1}, - {"last_cmd_id", item_size(last_cmd_id), - item_addr(last_cmd_id), DBG_CMD_NUM}, - {"last_cmd_act", item_size(last_cmd_act), - item_addr(last_cmd_act), DBG_CMD_NUM}, - {"last_cmd_index", item_size(last_cmd_index), - item_addr(last_cmd_index), 1}, - {"last_cmd_resp_id", item_size(last_cmd_resp_id), - item_addr(last_cmd_resp_id), DBG_CMD_NUM}, - {"last_cmd_resp_index", item_size(last_cmd_resp_index), - item_addr(last_cmd_resp_index), 1}, - {"last_event", item_size(last_event), - item_addr(last_event), DBG_CMD_NUM}, - {"last_event_index", item_size(last_event_index), - item_addr(last_event_index), 1}, - {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), - item_addr(num_cmd_host_to_card_failure), 1}, - {"num_cmd_sleep_cfm_fail", - item_size(num_cmd_sleep_cfm_host_to_card_failure), - item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1}, - {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), - item_addr(num_tx_host_to_card_failure), 1}, - {"num_evt_deauth", item_size(num_event_deauth), - item_addr(num_event_deauth), 1}, - {"num_evt_disassoc", item_size(num_event_disassoc), - item_addr(num_event_disassoc), 1}, - {"num_evt_link_lost", item_size(num_event_link_lost), - item_addr(num_event_link_lost), 1}, - {"num_cmd_deauth", item_size(num_cmd_deauth), - item_addr(num_cmd_deauth), 1}, - {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), - item_addr(num_cmd_assoc_success), 1}, - {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), - item_addr(num_cmd_assoc_failure), 1}, - {"cmd_sent", item_size(cmd_sent), - item_addr(cmd_sent), 1}, - {"data_sent", item_size(data_sent), - item_addr(data_sent), 1}, - {"cmd_resp_received", item_size(cmd_resp_received), - item_addr(cmd_resp_received), 1}, - {"event_received", item_size(event_received), - item_addr(event_received), 1}, - - /* variables defined in struct mwifiex_adapter */ - {"cmd_pending", adapter_item_size(cmd_pending), - adapter_item_addr(cmd_pending), 1}, - {"tx_pending", adapter_item_size(tx_pending), - adapter_item_addr(tx_pending), 1}, - {"rx_pending", adapter_item_size(rx_pending), - adapter_item_addr(rx_pending), 1}, -}; - -static int num_of_items = ARRAY_SIZE(items); - -/* - * Firmware initialization complete callback handler. - * - * This function wakes up the function waiting on the init - * wait queue for the firmware initialization to complete. - */ -int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter) -{ - - if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) - if (adapter->if_ops.init_fw_port) - adapter->if_ops.init_fw_port(adapter); - - adapter->init_wait_q_woken = true; - wake_up_interruptible(&adapter->init_wait_q); - return 0; -} - -/* - * Firmware shutdown complete callback handler. - * - * This function sets the hardware status to not ready and wakes up - * the function waiting on the init wait queue for the firmware - * shutdown to complete. - */ -int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter) -{ - adapter->hw_status = MWIFIEX_HW_STATUS_NOT_READY; - adapter->init_wait_q_woken = true; - wake_up_interruptible(&adapter->init_wait_q); - return 0; -} - -/* - * This function sends init/shutdown command - * to firmware. - */ -int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, - u32 func_init_shutdown) -{ - u16 cmd; - - if (func_init_shutdown == MWIFIEX_FUNC_INIT) { - cmd = HostCmd_CMD_FUNC_INIT; - } else if (func_init_shutdown == MWIFIEX_FUNC_SHUTDOWN) { - cmd = HostCmd_CMD_FUNC_SHUTDOWN; - } else { - mwifiex_dbg(priv->adapter, ERROR, - "unsupported parameter\n"); - return -1; - } - - return mwifiex_send_cmd(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL, true); -} -EXPORT_SYMBOL_GPL(mwifiex_init_shutdown_fw); - -/* - * IOCTL request handler to set/get debug information. - * - * This function collates/sets the information from/to different driver - * structures. - */ -int mwifiex_get_debug_info(struct mwifiex_private *priv, - struct mwifiex_debug_info *info) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - if (info) { - info->debug_mask = adapter->debug_mask; - memcpy(info->packets_out, - priv->wmm.packets_out, - sizeof(priv->wmm.packets_out)); - info->curr_tx_buf_size = (u32) adapter->curr_tx_buf_size; - info->tx_buf_size = (u32) adapter->tx_buf_size; - info->rx_tbl_num = mwifiex_get_rx_reorder_tbl(priv, - info->rx_tbl); - info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl(priv, - info->tx_tbl); - info->tdls_peer_num = mwifiex_get_tdls_list(priv, - info->tdls_list); - info->ps_mode = adapter->ps_mode; - info->ps_state = adapter->ps_state; - info->is_deep_sleep = adapter->is_deep_sleep; - info->pm_wakeup_card_req = adapter->pm_wakeup_card_req; - info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try; - info->is_hs_configured = adapter->is_hs_configured; - info->hs_activated = adapter->hs_activated; - info->is_cmd_timedout = adapter->is_cmd_timedout; - info->num_cmd_host_to_card_failure - = adapter->dbg.num_cmd_host_to_card_failure; - info->num_cmd_sleep_cfm_host_to_card_failure - = adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; - info->num_tx_host_to_card_failure - = adapter->dbg.num_tx_host_to_card_failure; - info->num_event_deauth = adapter->dbg.num_event_deauth; - info->num_event_disassoc = adapter->dbg.num_event_disassoc; - info->num_event_link_lost = adapter->dbg.num_event_link_lost; - info->num_cmd_deauth = adapter->dbg.num_cmd_deauth; - info->num_cmd_assoc_success = - adapter->dbg.num_cmd_assoc_success; - info->num_cmd_assoc_failure = - adapter->dbg.num_cmd_assoc_failure; - info->num_tx_timeout = adapter->dbg.num_tx_timeout; - info->timeout_cmd_id = adapter->dbg.timeout_cmd_id; - info->timeout_cmd_act = adapter->dbg.timeout_cmd_act; - memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id, - sizeof(adapter->dbg.last_cmd_id)); - memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act, - sizeof(adapter->dbg.last_cmd_act)); - info->last_cmd_index = adapter->dbg.last_cmd_index; - memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id, - sizeof(adapter->dbg.last_cmd_resp_id)); - info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index; - memcpy(info->last_event, adapter->dbg.last_event, - sizeof(adapter->dbg.last_event)); - info->last_event_index = adapter->dbg.last_event_index; - info->data_sent = adapter->data_sent; - info->cmd_sent = adapter->cmd_sent; - info->cmd_resp_received = adapter->cmd_resp_received; - } - - return 0; -} - -int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, - struct mwifiex_debug_info *info) -{ - char *p = buf; - struct mwifiex_debug_data *d = &items[0]; - size_t size, addr; - long val; - int i, j; - - if (!info) - return 0; - - for (i = 0; i < num_of_items; i++) { - p += sprintf(p, "%s=", d[i].name); - - size = d[i].size / d[i].num; - - if (i < (num_of_items - 3)) - addr = d[i].addr + (size_t)info; - else /* The last 3 items are struct mwifiex_adapter variables */ - addr = d[i].addr + (size_t)priv->adapter; - - for (j = 0; j < d[i].num; j++) { - switch (size) { - case 1: - val = *((u8 *)addr); - break; - case 2: - val = *((u16 *)addr); - break; - case 4: - val = *((u32 *)addr); - break; - case 8: - val = *((long long *)addr); - break; - default: - val = -1; - break; - } - - p += sprintf(p, "%#lx ", val); - addr += size; - } - - p += sprintf(p, "\n"); - } - - if (info->tx_tbl_num) { - p += sprintf(p, "Tx BA stream table:\n"); - for (i = 0; i < info->tx_tbl_num; i++) - p += sprintf(p, "tid = %d, ra = %pM\n", - info->tx_tbl[i].tid, info->tx_tbl[i].ra); - } - - if (info->rx_tbl_num) { - p += sprintf(p, "Rx reorder table:\n"); - for (i = 0; i < info->rx_tbl_num; i++) { - p += sprintf(p, "tid = %d, ta = %pM, ", - info->rx_tbl[i].tid, - info->rx_tbl[i].ta); - p += sprintf(p, "start_win = %d, ", - info->rx_tbl[i].start_win); - p += sprintf(p, "win_size = %d, buffer: ", - info->rx_tbl[i].win_size); - - for (j = 0; j < info->rx_tbl[i].win_size; j++) - p += sprintf(p, "%c ", - info->rx_tbl[i].buffer[j] ? - '1' : '0'); - - p += sprintf(p, "\n"); - } - } - - if (info->tdls_peer_num) { - p += sprintf(p, "TDLS peer table:\n"); - for (i = 0; i < info->tdls_peer_num; i++) { - p += sprintf(p, "peer = %pM", - info->tdls_list[i].peer_addr); - p += sprintf(p, "\n"); - } - } - - return p - buf; -} - -static int -mwifiex_parse_mgmt_packet(struct mwifiex_private *priv, u8 *payload, u16 len, - struct rxpd *rx_pd) -{ - u16 stype; - u8 category, action_code, *addr2; - struct ieee80211_hdr *ieee_hdr = (void *)payload; - - stype = (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE); - - switch (stype) { - case IEEE80211_STYPE_ACTION: - category = *(payload + sizeof(struct ieee80211_hdr)); - switch (category) { - case WLAN_CATEGORY_PUBLIC: - action_code = *(payload + sizeof(struct ieee80211_hdr) - + 1); - if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) { - addr2 = ieee_hdr->addr2; - mwifiex_dbg(priv->adapter, INFO, - "TDLS discovery response %pM nf=%d, snr=%d\n", - addr2, rx_pd->nf, rx_pd->snr); - mwifiex_auto_tdls_update_peer_signal(priv, - addr2, - rx_pd->snr, - rx_pd->nf); - } - break; - case WLAN_CATEGORY_BACK: - /*we dont indicate BACK action frames to cfg80211*/ - mwifiex_dbg(priv->adapter, INFO, - "drop BACK action frames"); - return -1; - default: - mwifiex_dbg(priv->adapter, INFO, - "unknown public action frame category %d\n", - category); - } - default: - mwifiex_dbg(priv->adapter, INFO, - "unknown mgmt frame subtype %#x\n", stype); - return 0; - } - - return 0; -} -/* - * This function processes the received management packet and send it - * to the kernel. - */ -int -mwifiex_process_mgmt_packet(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct rxpd *rx_pd; - u16 pkt_len; - struct ieee80211_hdr *ieee_hdr; - - if (!skb) - return -1; - - if (!priv->mgmt_frame_mask || - priv->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) { - mwifiex_dbg(priv->adapter, ERROR, - "do not receive mgmt frames on uninitialized intf"); - return -1; - } - - rx_pd = (struct rxpd *)skb->data; - - skb_pull(skb, le16_to_cpu(rx_pd->rx_pkt_offset)); - skb_pull(skb, sizeof(pkt_len)); - - pkt_len = le16_to_cpu(rx_pd->rx_pkt_length); - - ieee_hdr = (void *)skb->data; - if (ieee80211_is_mgmt(ieee_hdr->frame_control)) { - if (mwifiex_parse_mgmt_packet(priv, (u8 *)ieee_hdr, - pkt_len, rx_pd)) - return -1; - } - /* Remove address4 */ - memmove(skb->data + sizeof(struct ieee80211_hdr_3addr), - skb->data + sizeof(struct ieee80211_hdr), - pkt_len - sizeof(struct ieee80211_hdr)); - - pkt_len -= ETH_ALEN + sizeof(pkt_len); - rx_pd->rx_pkt_length = cpu_to_le16(pkt_len); - - cfg80211_rx_mgmt(&priv->wdev, priv->roc_cfg.chan.center_freq, - CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len, - 0); - - return 0; -} - -/* - * This function processes the received packet before sending it to the - * kernel. - * - * It extracts the SKB from the received buffer and sends it to kernel. - * In case the received buffer does not contain the data in SKB format, - * the function creates a blank SKB, fills it with the data from the - * received buffer and then sends this new SKB to the kernel. - */ -int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb) -{ - struct mwifiex_sta_node *src_node; - struct ethhdr *p_ethhdr; - - if (!skb) - return -1; - - priv->stats.rx_bytes += skb->len; - priv->stats.rx_packets++; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - p_ethhdr = (void *)skb->data; - src_node = mwifiex_get_sta_entry(priv, p_ethhdr->h_source); - if (src_node) { - src_node->stats.last_rx = jiffies; - src_node->stats.rx_bytes += skb->len; - src_node->stats.rx_packets++; - } - } - - skb->dev = priv->netdev; - skb->protocol = eth_type_trans(skb, priv->netdev); - skb->ip_summed = CHECKSUM_NONE; - - /* This is required only in case of 11n and USB/PCIE as we alloc - * a buffer of 4K only if its 11N (to be able to receive 4K - * AMSDU packets). In case of SD we allocate buffers based - * on the size of packet and hence this is not needed. - * - * Modifying the truesize here as our allocation for each - * skb is 4K but we only receive 2K packets and this cause - * the kernel to start dropping packets in case where - * application has allocated buffer based on 2K size i.e. - * if there a 64K packet received (in IP fragments and - * application allocates 64K to receive this packet but - * this packet would almost double up because we allocate - * each 1.5K fragment in 4K and pass it up. As soon as the - * 64K limit hits kernel will start to drop rest of the - * fragments. Currently we fail the Filesndl-ht.scr script - * for UDP, hence this fix - */ - if ((priv->adapter->iface_type == MWIFIEX_USB || - priv->adapter->iface_type == MWIFIEX_PCIE) && - (skb->truesize > MWIFIEX_RX_DATA_BUF_SIZE)) - skb->truesize += (skb->len - MWIFIEX_RX_DATA_BUF_SIZE); - - if (in_interrupt()) - netif_rx(skb); - else - netif_rx_ni(skb); - - return 0; -} - -/* - * IOCTL completion callback handler. - * - * This function is called when a pending IOCTL is completed. - * - * If work queue support is enabled, the function wakes up the - * corresponding waiting function. Otherwise, it processes the - * IOCTL response and frees the response buffer. - */ -int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node) -{ - WARN_ON(!cmd_node->wait_q_enabled); - mwifiex_dbg(adapter, CMD, "cmd completed: status=%d\n", - adapter->cmd_wait_q.status); - - *cmd_node->condition = true; - wake_up_interruptible(&adapter->cmd_wait_q.wait); - - return 0; -} - -/* This function will return the pointer to station entry in station list - * table which matches specified mac address. - * This function should be called after acquiring RA list spinlock. - * NULL is returned if station entry is not found in associated STA list. - */ -struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac) -{ - struct mwifiex_sta_node *node; - - if (!mac) - return NULL; - - list_for_each_entry(node, &priv->sta_list, list) { - if (!memcmp(node->mac_addr, mac, ETH_ALEN)) - return node; - } - - return NULL; -} - -static struct mwifiex_sta_node * -mwifiex_get_tdls_sta_entry(struct mwifiex_private *priv, u8 status) -{ - struct mwifiex_sta_node *node; - - list_for_each_entry(node, &priv->sta_list, list) { - if (node->tdls_status == status) - return node; - } - - return NULL; -} - -/* If tdls channel switching is on-going, tx data traffic should be - * blocked until the switching stage completed. - */ -u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv) -{ - struct mwifiex_sta_node *sta_ptr; - - if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) - return false; - - sta_ptr = mwifiex_get_tdls_sta_entry(priv, TDLS_CHAN_SWITCHING); - if (sta_ptr) - return true; - - return false; -} - -u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv) -{ - struct mwifiex_sta_node *sta_ptr; - - if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) - return false; - - sta_ptr = mwifiex_get_tdls_sta_entry(priv, TDLS_IN_OFF_CHAN); - if (sta_ptr) - return true; - - return false; -} - -/* If tdls channel switching is on-going or tdls operate on off-channel, - * cmd path should be blocked until tdls switched to base-channel. - */ -u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv) -{ - if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) - return true; - - if (mwifiex_is_tdls_chan_switching(priv) || - mwifiex_is_tdls_off_chan(priv)) - return false; - - return true; -} - -/* This function will add a sta_node entry to associated station list - * table with the given mac address. - * If entry exist already, existing entry is returned. - * If received mac address is NULL, NULL is returned. - */ -struct mwifiex_sta_node * -mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac) -{ - struct mwifiex_sta_node *node; - unsigned long flags; - - if (!mac) - return NULL; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - node = mwifiex_get_sta_entry(priv, mac); - if (node) - goto done; - - node = kzalloc(sizeof(*node), GFP_ATOMIC); - if (!node) - goto done; - - memcpy(node->mac_addr, mac, ETH_ALEN); - list_add_tail(&node->list, &priv->sta_list); - -done: - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return node; -} - -/* This function will search for HT IE in association request IEs - * and set station HT parameters accordingly. - */ -void -mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, - int ies_len, struct mwifiex_sta_node *node) -{ - struct ieee_types_header *ht_cap_ie; - const struct ieee80211_ht_cap *ht_cap; - - if (!ies) - return; - - ht_cap_ie = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, - ies_len); - if (ht_cap_ie) { - ht_cap = (void *)(ht_cap_ie + 1); - node->is_11n_enabled = 1; - node->max_amsdu = le16_to_cpu(ht_cap->cap_info) & - IEEE80211_HT_CAP_MAX_AMSDU ? - MWIFIEX_TX_DATA_BUF_SIZE_8K : - MWIFIEX_TX_DATA_BUF_SIZE_4K; - } else { - node->is_11n_enabled = 0; - } - - return; -} - -/* This function will delete a station entry from station list */ -void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac) -{ - struct mwifiex_sta_node *node; - unsigned long flags; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - - node = mwifiex_get_sta_entry(priv, mac); - if (node) { - list_del(&node->list); - kfree(node); - } - - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return; -} - -/* This function will delete all stations from associated station list. */ -void mwifiex_del_all_sta_list(struct mwifiex_private *priv) -{ - struct mwifiex_sta_node *node, *tmp; - unsigned long flags; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - - list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { - list_del(&node->list); - kfree(node); - } - - INIT_LIST_HEAD(&priv->sta_list); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return; -} - -/* This function adds histogram data to histogram array*/ -void mwifiex_hist_data_add(struct mwifiex_private *priv, - u8 rx_rate, s8 snr, s8 nflr) -{ - struct mwifiex_histogram_data *phist_data = priv->hist_data; - - if (atomic_read(&phist_data->num_samples) > MWIFIEX_HIST_MAX_SAMPLES) - mwifiex_hist_data_reset(priv); - mwifiex_hist_data_set(priv, rx_rate, snr, nflr); -} - -/* function to add histogram record */ -void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, - s8 nflr) -{ - struct mwifiex_histogram_data *phist_data = priv->hist_data; - - atomic_inc(&phist_data->num_samples); - atomic_inc(&phist_data->rx_rate[rx_rate]); - atomic_inc(&phist_data->snr[snr]); - atomic_inc(&phist_data->noise_flr[128 + nflr]); - atomic_inc(&phist_data->sig_str[nflr - snr]); -} - -/* function to reset histogram data during init/reset */ -void mwifiex_hist_data_reset(struct mwifiex_private *priv) -{ - int ix; - struct mwifiex_histogram_data *phist_data = priv->hist_data; - - atomic_set(&phist_data->num_samples, 0); - for (ix = 0; ix < MWIFIEX_MAX_AC_RX_RATES; ix++) - atomic_set(&phist_data->rx_rate[ix], 0); - for (ix = 0; ix < MWIFIEX_MAX_SNR; ix++) - atomic_set(&phist_data->snr[ix], 0); - for (ix = 0; ix < MWIFIEX_MAX_NOISE_FLR; ix++) - atomic_set(&phist_data->noise_flr[ix], 0); - for (ix = 0; ix < MWIFIEX_MAX_SIG_STRENGTH; ix++) - atomic_set(&phist_data->sig_str[ix], 0); -} - -void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags) -{ - struct sk_buff *skb; - int buf_len, pad; - - buf_len = rx_len + MWIFIEX_RX_HEADROOM + MWIFIEX_DMA_ALIGN_SZ; - - skb = __dev_alloc_skb(buf_len, flags); - - if (!skb) - return NULL; - - skb_reserve(skb, MWIFIEX_RX_HEADROOM); - - pad = MWIFIEX_ALIGN_ADDR(skb->data, MWIFIEX_DMA_ALIGN_SZ) - - (long)skb->data; - - skb_reserve(skb, pad); - - return skb; -} -EXPORT_SYMBOL_GPL(mwifiex_alloc_dma_align_buf); diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h deleted file mode 100644 index b541d66c01eb..000000000000 --- a/drivers/net/wireless/mwifiex/util.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Marvell Wireless LAN device driver: utility functions - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_UTIL_H_ -#define _MWIFIEX_UTIL_H_ - -struct mwifiex_private; - -struct mwifiex_dma_mapping { - dma_addr_t addr; - size_t len; -}; - -struct mwifiex_cb { - struct mwifiex_dma_mapping dma_mapping; - union { - struct mwifiex_rxinfo rx_info; - struct mwifiex_txinfo tx_info; - }; -}; - -/* size/addr for mwifiex_debug_info */ -#define item_size(n) (FIELD_SIZEOF(struct mwifiex_debug_info, n)) -#define item_addr(n) (offsetof(struct mwifiex_debug_info, n)) - -/* size/addr for struct mwifiex_adapter */ -#define adapter_item_size(n) (FIELD_SIZEOF(struct mwifiex_adapter, n)) -#define adapter_item_addr(n) (offsetof(struct mwifiex_adapter, n)) - -struct mwifiex_debug_data { - char name[32]; /* variable/array name */ - u32 size; /* size of the variable/array */ - size_t addr; /* address of the variable/array */ - int num; /* number of variables in an array */ -}; - -static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) -{ - struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; - - BUILD_BUG_ON(sizeof(struct mwifiex_cb) > sizeof(skb->cb)); - return &cb->rx_info; -} - -static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb) -{ - struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; - - return &cb->tx_info; -} - -static inline void mwifiex_store_mapping(struct sk_buff *skb, - struct mwifiex_dma_mapping *mapping) -{ - struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; - - memcpy(&cb->dma_mapping, mapping, sizeof(*mapping)); -} - -static inline void mwifiex_get_mapping(struct sk_buff *skb, - struct mwifiex_dma_mapping *mapping) -{ - struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; - - memcpy(mapping, &cb->dma_mapping, sizeof(*mapping)); -} - -static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb) -{ - struct mwifiex_dma_mapping mapping; - - mwifiex_get_mapping(skb, &mapping); - - return mapping.addr; -} - -int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, - struct mwifiex_debug_info *info); - -#endif /* !_MWIFIEX_UTIL_H_ */ diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c deleted file mode 100644 index acccd6734e3b..000000000000 --- a/drivers/net/wireless/mwifiex/wmm.c +++ /dev/null @@ -1,1531 +0,0 @@ -/* - * Marvell Wireless LAN device driver: WMM - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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 "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" - - -/* Maximum value FW can accept for driver delay in packet transmission */ -#define DRV_PKT_DELAY_TO_FW_MAX 512 - - -#define WMM_QUEUED_PACKET_LOWER_LIMIT 180 - -#define WMM_QUEUED_PACKET_UPPER_LIMIT 200 - -/* Offset for TOS field in the IP header */ -#define IPTOS_OFFSET 5 - -static bool disable_tx_amsdu; -module_param(disable_tx_amsdu, bool, 0644); - -/* WMM information IE */ -static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07, - 0x00, 0x50, 0xf2, 0x02, - 0x00, 0x01, 0x00 -}; - -static const u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE, - WMM_AC_BK, - WMM_AC_VI, - WMM_AC_VO -}; - -static u8 tos_to_tid[] = { - /* TID DSCP_P2 DSCP_P1 DSCP_P0 WMM_AC */ - 0x01, /* 0 1 0 AC_BK */ - 0x02, /* 0 0 0 AC_BK */ - 0x00, /* 0 0 1 AC_BE */ - 0x03, /* 0 1 1 AC_BE */ - 0x04, /* 1 0 0 AC_VI */ - 0x05, /* 1 0 1 AC_VI */ - 0x06, /* 1 1 0 AC_VO */ - 0x07 /* 1 1 1 AC_VO */ -}; - -static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; - -/* - * This function debug prints the priority parameters for a WMM AC. - */ -static void -mwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param) -{ - const char *ac_str[] = { "BK", "BE", "VI", "VO" }; - - pr_debug("info: WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, " - "EcwMin=%d, EcwMax=%d, TxopLimit=%d\n", - ac_str[wmm_aci_to_qidx_map[(ac_param->aci_aifsn_bitmap - & MWIFIEX_ACI) >> 5]], - (ac_param->aci_aifsn_bitmap & MWIFIEX_ACI) >> 5, - (ac_param->aci_aifsn_bitmap & MWIFIEX_ACM) >> 4, - ac_param->aci_aifsn_bitmap & MWIFIEX_AIFSN, - ac_param->ecw_bitmap & MWIFIEX_ECW_MIN, - (ac_param->ecw_bitmap & MWIFIEX_ECW_MAX) >> 4, - le16_to_cpu(ac_param->tx_op_limit)); -} - -/* - * This function allocates a route address list. - * - * The function also initializes the list with the provided RA. - */ -static struct mwifiex_ra_list_tbl * -mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra) -{ - struct mwifiex_ra_list_tbl *ra_list; - - ra_list = kzalloc(sizeof(struct mwifiex_ra_list_tbl), GFP_ATOMIC); - if (!ra_list) - return NULL; - - INIT_LIST_HEAD(&ra_list->list); - skb_queue_head_init(&ra_list->skb_head); - - memcpy(ra_list->ra, ra, ETH_ALEN); - - ra_list->total_pkt_count = 0; - - mwifiex_dbg(adapter, INFO, "info: allocated ra_list %p\n", ra_list); - - return ra_list; -} - -/* This function returns random no between 16 and 32 to be used as threshold - * for no of packets after which BA setup is initiated. - */ -static u8 mwifiex_get_random_ba_threshold(void) -{ - u64 ns; - /* setup ba_packet_threshold here random number between - * [BA_SETUP_PACKET_OFFSET, - * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1] - */ - ns = ktime_get_ns(); - ns += (ns >> 32) + (ns >> 16); - - return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET; -} - -/* - * This function allocates and adds a RA list for all TIDs - * with the given RA. - */ -void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) -{ - int i; - struct mwifiex_ra_list_tbl *ra_list; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_sta_node *node; - unsigned long flags; - - - for (i = 0; i < MAX_NUM_TID; ++i) { - ra_list = mwifiex_wmm_allocate_ralist_node(adapter, ra); - mwifiex_dbg(adapter, INFO, - "info: created ra_list %p\n", ra_list); - - if (!ra_list) - break; - - ra_list->is_11n_enabled = 0; - ra_list->tdls_link = false; - ra_list->ba_status = BA_SETUP_NONE; - ra_list->amsdu_in_ampdu = false; - if (!mwifiex_queuing_ra_based(priv)) { - if (mwifiex_is_tdls_link_setup - (mwifiex_get_tdls_link_status(priv, ra))) { - ra_list->tdls_link = true; - ra_list->is_11n_enabled = - mwifiex_tdls_peer_11n_enabled(priv, ra); - } else { - ra_list->is_11n_enabled = IS_11N_ENABLED(priv); - } - } else { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - node = mwifiex_get_sta_entry(priv, ra); - if (node) - ra_list->tx_paused = node->tx_pause; - ra_list->is_11n_enabled = - mwifiex_is_sta_11n_enabled(priv, node); - if (ra_list->is_11n_enabled) - ra_list->max_amsdu = node->max_amsdu; - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - } - - mwifiex_dbg(adapter, DATA, "data: ralist %p: is_11n_enabled=%d\n", - ra_list, ra_list->is_11n_enabled); - - if (ra_list->is_11n_enabled) { - ra_list->ba_pkt_count = 0; - ra_list->ba_packet_thr = - mwifiex_get_random_ba_threshold(); - } - list_add_tail(&ra_list->list, - &priv->wmm.tid_tbl_ptr[i].ra_list); - } -} - -/* - * This function sets the WMM queue priorities to their default values. - */ -static void mwifiex_wmm_default_queue_priorities(struct mwifiex_private *priv) -{ - /* Default queue priorities: VO->VI->BE->BK */ - priv->wmm.queue_priority[0] = WMM_AC_VO; - priv->wmm.queue_priority[1] = WMM_AC_VI; - priv->wmm.queue_priority[2] = WMM_AC_BE; - priv->wmm.queue_priority[3] = WMM_AC_BK; -} - -/* - * This function map ACs to TIDs. - */ -static void -mwifiex_wmm_queue_priorities_tid(struct mwifiex_private *priv) -{ - struct mwifiex_wmm_desc *wmm = &priv->wmm; - u8 *queue_priority = wmm->queue_priority; - int i; - - for (i = 0; i < 4; ++i) { - tos_to_tid[7 - (i * 2)] = ac_to_tid[queue_priority[i]][1]; - tos_to_tid[6 - (i * 2)] = ac_to_tid[queue_priority[i]][0]; - } - - for (i = 0; i < MAX_NUM_TID; ++i) - priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i; - - atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID); -} - -/* - * This function initializes WMM priority queues. - */ -void -mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, - struct ieee_types_wmm_parameter *wmm_ie) -{ - u16 cw_min, avg_back_off, tmp[4]; - u32 i, j, num_ac; - u8 ac_idx; - - if (!wmm_ie || !priv->wmm_enabled) { - /* WMM is not enabled, just set the defaults and return */ - mwifiex_wmm_default_queue_priorities(priv); - return; - } - - mwifiex_dbg(priv->adapter, INFO, - "info: WMM Parameter IE: version=%d,\t" - "qos_info Parameter Set Count=%d, Reserved=%#x\n", - wmm_ie->vend_hdr.version, wmm_ie->qos_info_bitmap & - IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK, - wmm_ie->reserved); - - for (num_ac = 0; num_ac < ARRAY_SIZE(wmm_ie->ac_params); num_ac++) { - u8 ecw = wmm_ie->ac_params[num_ac].ecw_bitmap; - u8 aci_aifsn = wmm_ie->ac_params[num_ac].aci_aifsn_bitmap; - cw_min = (1 << (ecw & MWIFIEX_ECW_MIN)) - 1; - avg_back_off = (cw_min >> 1) + (aci_aifsn & MWIFIEX_AIFSN); - - ac_idx = wmm_aci_to_qidx_map[(aci_aifsn & MWIFIEX_ACI) >> 5]; - priv->wmm.queue_priority[ac_idx] = ac_idx; - tmp[ac_idx] = avg_back_off; - - mwifiex_dbg(priv->adapter, INFO, - "info: WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n", - (1 << ((ecw & MWIFIEX_ECW_MAX) >> 4)) - 1, - cw_min, avg_back_off); - mwifiex_wmm_ac_debug_print(&wmm_ie->ac_params[num_ac]); - } - - /* Bubble sort */ - for (i = 0; i < num_ac; i++) { - for (j = 1; j < num_ac - i; j++) { - if (tmp[j - 1] > tmp[j]) { - swap(tmp[j - 1], tmp[j]); - swap(priv->wmm.queue_priority[j - 1], - priv->wmm.queue_priority[j]); - } else if (tmp[j - 1] == tmp[j]) { - if (priv->wmm.queue_priority[j - 1] - < priv->wmm.queue_priority[j]) - swap(priv->wmm.queue_priority[j - 1], - priv->wmm.queue_priority[j]); - } - } - } - - mwifiex_wmm_queue_priorities_tid(priv); -} - -/* - * This function evaluates whether or not an AC is to be downgraded. - * - * In case the AC is not enabled, the highest AC is returned that is - * enabled and does not require admission control. - */ -static enum mwifiex_wmm_ac_e -mwifiex_wmm_eval_downgrade_ac(struct mwifiex_private *priv, - enum mwifiex_wmm_ac_e eval_ac) -{ - int down_ac; - enum mwifiex_wmm_ac_e ret_ac; - struct mwifiex_wmm_ac_status *ac_status; - - ac_status = &priv->wmm.ac_status[eval_ac]; - - if (!ac_status->disabled) - /* Okay to use this AC, its enabled */ - return eval_ac; - - /* Setup a default return value of the lowest priority */ - ret_ac = WMM_AC_BK; - - /* - * Find the highest AC that is enabled and does not require - * admission control. The spec disallows downgrading to an AC, - * which is enabled due to a completed admission control. - * Unadmitted traffic is not to be sent on an AC with admitted - * traffic. - */ - for (down_ac = WMM_AC_BK; down_ac < eval_ac; down_ac++) { - ac_status = &priv->wmm.ac_status[down_ac]; - - if (!ac_status->disabled && !ac_status->flow_required) - /* AC is enabled and does not require admission - control */ - ret_ac = (enum mwifiex_wmm_ac_e) down_ac; - } - - return ret_ac; -} - -/* - * This function downgrades WMM priority queue. - */ -void -mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv) -{ - int ac_val; - - mwifiex_dbg(priv->adapter, INFO, "info: WMM: AC Priorities:\t" - "BK(0), BE(1), VI(2), VO(3)\n"); - - if (!priv->wmm_enabled) { - /* WMM is not enabled, default priorities */ - for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) - priv->wmm.ac_down_graded_vals[ac_val] = - (enum mwifiex_wmm_ac_e) ac_val; - } else { - for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) { - priv->wmm.ac_down_graded_vals[ac_val] - = mwifiex_wmm_eval_downgrade_ac(priv, - (enum mwifiex_wmm_ac_e) ac_val); - mwifiex_dbg(priv->adapter, INFO, - "info: WMM: AC PRIO %d maps to %d\n", - ac_val, - priv->wmm.ac_down_graded_vals[ac_val]); - } - } -} - -/* - * This function converts the IP TOS field to an WMM AC - * Queue assignment. - */ -static enum mwifiex_wmm_ac_e -mwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos) -{ - /* Map of TOS UP values to WMM AC */ - const enum mwifiex_wmm_ac_e tos_to_ac[] = { WMM_AC_BE, - WMM_AC_BK, - WMM_AC_BK, - WMM_AC_BE, - WMM_AC_VI, - WMM_AC_VI, - WMM_AC_VO, - WMM_AC_VO - }; - - if (tos >= ARRAY_SIZE(tos_to_ac)) - return WMM_AC_BE; - - return tos_to_ac[tos]; -} - -/* - * This function evaluates a given TID and downgrades it to a lower - * TID if the WMM Parameter IE received from the AP indicates that the - * AP is disabled (due to call admission control (ACM bit). Mapping - * of TID to AC is taken care of internally. - */ -u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) -{ - enum mwifiex_wmm_ac_e ac, ac_down; - u8 new_tid; - - ac = mwifiex_wmm_convert_tos_to_ac(priv->adapter, tid); - ac_down = priv->wmm.ac_down_graded_vals[ac]; - - /* Send the index to tid array, picking from the array will be - * taken care by dequeuing function - */ - new_tid = ac_to_tid[ac_down][tid % 2]; - - return new_tid; -} - -/* - * This function initializes the WMM state information and the - * WMM data path queues. - */ -void -mwifiex_wmm_init(struct mwifiex_adapter *adapter) -{ - int i, j; - struct mwifiex_private *priv; - - for (j = 0; j < adapter->priv_num; ++j) { - priv = adapter->priv[j]; - if (!priv) - continue; - - for (i = 0; i < MAX_NUM_TID; ++i) { - if (!disable_tx_amsdu && - adapter->tx_buf_size > MWIFIEX_TX_DATA_BUF_SIZE_2K) - priv->aggr_prio_tbl[i].amsdu = - priv->tos_to_tid_inv[i]; - else - priv->aggr_prio_tbl[i].amsdu = - BA_STREAM_NOT_ALLOWED; - priv->aggr_prio_tbl[i].ampdu_ap = - priv->tos_to_tid_inv[i]; - priv->aggr_prio_tbl[i].ampdu_user = - priv->tos_to_tid_inv[i]; - } - - priv->aggr_prio_tbl[6].amsdu - = priv->aggr_prio_tbl[6].ampdu_ap - = priv->aggr_prio_tbl[6].ampdu_user - = BA_STREAM_NOT_ALLOWED; - - priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap - = priv->aggr_prio_tbl[7].ampdu_user - = BA_STREAM_NOT_ALLOWED; - - mwifiex_set_ba_params(priv); - mwifiex_reset_11n_rx_seq_num(priv); - - atomic_set(&priv->wmm.tx_pkts_queued, 0); - atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); - } -} - -int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - int i; - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - if (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv)) - continue; - if (!skb_queue_empty(&priv->bypass_txq)) - return false; - } - - return true; -} - -/* - * This function checks if WMM Tx queue is empty. - */ -int -mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter) -{ - int i; - struct mwifiex_private *priv; - - for (i = 0; i < adapter->priv_num; ++i) { - priv = adapter->priv[i]; - if (!priv) - continue; - if (!priv->port_open) - continue; - if (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv)) - continue; - if (atomic_read(&priv->wmm.tx_pkts_queued)) - return false; - } - - return true; -} - -/* - * This function deletes all packets in an RA list node. - * - * The packet sent completion callback handler are called with - * status failure, after they are dequeued to ensure proper - * cleanup. The RA list node itself is freed at the end. - */ -static void -mwifiex_wmm_del_pkts_in_ralist_node(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra_list) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct sk_buff *skb, *tmp; - - skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) - mwifiex_write_data_complete(adapter, skb, 0, -1); -} - -/* - * This function deletes all packets in an RA list. - * - * Each nodes in the RA list are freed individually first, and then - * the RA list itself is freed. - */ -static void -mwifiex_wmm_del_pkts_in_ralist(struct mwifiex_private *priv, - struct list_head *ra_list_head) -{ - struct mwifiex_ra_list_tbl *ra_list; - - list_for_each_entry(ra_list, ra_list_head, list) - mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); -} - -/* - * This function deletes all packets in all RA lists. - */ -static void mwifiex_wmm_cleanup_queues(struct mwifiex_private *priv) -{ - int i; - - for (i = 0; i < MAX_NUM_TID; i++) - mwifiex_wmm_del_pkts_in_ralist(priv, &priv->wmm.tid_tbl_ptr[i]. - ra_list); - - atomic_set(&priv->wmm.tx_pkts_queued, 0); - atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); -} - -/* - * This function deletes all route addresses from all RA lists. - */ -static void mwifiex_wmm_delete_all_ralist(struct mwifiex_private *priv) -{ - struct mwifiex_ra_list_tbl *ra_list, *tmp_node; - int i; - - for (i = 0; i < MAX_NUM_TID; ++i) { - mwifiex_dbg(priv->adapter, INFO, - "info: ra_list: freeing buf for tid %d\n", i); - list_for_each_entry_safe(ra_list, tmp_node, - &priv->wmm.tid_tbl_ptr[i].ra_list, - list) { - list_del(&ra_list->list); - kfree(ra_list); - } - - INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[i].ra_list); - } -} - -static int mwifiex_free_ack_frame(int id, void *p, void *data) -{ - pr_warn("Have pending ack frames!\n"); - kfree_skb(p); - return 0; -} - -/* - * This function cleans up the Tx and Rx queues. - * - * Cleanup includes - - * - All packets in RA lists - * - All entries in Rx reorder table - * - All entries in Tx BA stream table - * - MPA buffer (if required) - * - All RA lists - */ -void -mwifiex_clean_txrx(struct mwifiex_private *priv) -{ - unsigned long flags; - struct sk_buff *skb, *tmp; - - mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - mwifiex_wmm_cleanup_queues(priv); - mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); - - if (priv->adapter->if_ops.cleanup_mpa_buf) - priv->adapter->if_ops.cleanup_mpa_buf(priv->adapter); - - mwifiex_wmm_delete_all_ralist(priv); - memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); - - if (priv->adapter->if_ops.clean_pcie_ring && - !priv->adapter->surprise_removed) - priv->adapter->if_ops.clean_pcie_ring(priv->adapter); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - - skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) - mwifiex_write_data_complete(priv->adapter, skb, 0, -1); - - skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) - mwifiex_write_data_complete(priv->adapter, skb, 0, -1); - atomic_set(&priv->adapter->bypass_tx_pending, 0); - - idr_for_each(&priv->ack_status_frames, mwifiex_free_ack_frame, NULL); - idr_destroy(&priv->ack_status_frames); -} - -/* - * This function retrieves a particular RA list node, matching with the - * given TID and RA address. - */ -struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, - const u8 *ra_addr) -{ - struct mwifiex_ra_list_tbl *ra_list; - - list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[tid].ra_list, - list) { - if (!memcmp(ra_list->ra, ra_addr, ETH_ALEN)) - return ra_list; - } - - return NULL; -} - -void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, - u8 tx_pause) -{ - struct mwifiex_ra_list_tbl *ra_list; - u32 pkt_cnt = 0, tx_pkts_queued; - unsigned long flags; - int i; - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - for (i = 0; i < MAX_NUM_TID; ++i) { - ra_list = mwifiex_wmm_get_ralist_node(priv, i, mac); - if (ra_list && ra_list->tx_paused != tx_pause) { - pkt_cnt += ra_list->total_pkt_count; - ra_list->tx_paused = tx_pause; - if (tx_pause) - priv->wmm.pkts_paused[i] += - ra_list->total_pkt_count; - else - priv->wmm.pkts_paused[i] -= - ra_list->total_pkt_count; - } - } - - if (pkt_cnt) { - tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued); - if (tx_pause) - tx_pkts_queued -= pkt_cnt; - else - tx_pkts_queued += pkt_cnt; - - atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); - atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); - } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - -/* This function update non-tdls peer ralist tx_pause while - * tdls channel swithing - */ -void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, - u8 *mac, u8 tx_pause) -{ - struct mwifiex_ra_list_tbl *ra_list; - u32 pkt_cnt = 0, tx_pkts_queued; - unsigned long flags; - int i; - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - for (i = 0; i < MAX_NUM_TID; ++i) { - list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[i].ra_list, - list) { - if (!memcmp(ra_list->ra, mac, ETH_ALEN)) - continue; - - if (ra_list->tx_paused != tx_pause) { - pkt_cnt += ra_list->total_pkt_count; - ra_list->tx_paused = tx_pause; - if (tx_pause) - priv->wmm.pkts_paused[i] += - ra_list->total_pkt_count; - else - priv->wmm.pkts_paused[i] -= - ra_list->total_pkt_count; - } - } - } - - if (pkt_cnt) { - tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued); - if (tx_pause) - tx_pkts_queued -= pkt_cnt; - else - tx_pkts_queued += pkt_cnt; - - atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); - atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); - } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - -/* - * This function retrieves an RA list node for a given TID and - * RA address pair. - * - * If no such node is found, a new node is added first and then - * retrieved. - */ -struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, - const u8 *ra_addr) -{ - struct mwifiex_ra_list_tbl *ra_list; - - ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); - if (ra_list) - return ra_list; - mwifiex_ralist_add(priv, ra_addr); - - return mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); -} - -/* - * This function deletes RA list nodes for given mac for all TIDs. - * Function also decrements TX pending count accordingly. - */ -void -mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr) -{ - struct mwifiex_ra_list_tbl *ra_list; - unsigned long flags; - int i; - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - for (i = 0; i < MAX_NUM_TID; ++i) { - ra_list = mwifiex_wmm_get_ralist_node(priv, i, ra_addr); - - if (!ra_list) - continue; - mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); - if (ra_list->tx_paused) - priv->wmm.pkts_paused[i] -= ra_list->total_pkt_count; - else - atomic_sub(ra_list->total_pkt_count, - &priv->wmm.tx_pkts_queued); - list_del(&ra_list->list); - kfree(ra_list); - } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - -/* - * This function checks if a particular RA list node exists in a given TID - * table index. - */ -int -mwifiex_is_ralist_valid(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra_list, int ptr_index) -{ - struct mwifiex_ra_list_tbl *rlist; - - list_for_each_entry(rlist, &priv->wmm.tid_tbl_ptr[ptr_index].ra_list, - list) { - if (rlist == ra_list) - return true; - } - - return false; -} - -/* - * This function adds a packet to bypass TX queue. - * This is special TX queue for packets which can be sent even when port_open - * is false. - */ -void -mwifiex_wmm_add_buf_bypass_txqueue(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - skb_queue_tail(&priv->bypass_txq, skb); -} - -/* - * This function adds a packet to WMM queue. - * - * In disconnected state the packet is immediately dropped and the - * packet send completion callback is called with status failure. - * - * Otherwise, the correct RA list node is located and the packet - * is queued at the list tail. - */ -void -mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u32 tid; - struct mwifiex_ra_list_tbl *ra_list; - u8 ra[ETH_ALEN], tid_down; - unsigned long flags; - struct list_head list_head; - int tdls_status = TDLS_NOT_SETUP; - struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; - struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - - memcpy(ra, eth_hdr->h_dest, ETH_ALEN); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && - ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) { - if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS) - mwifiex_dbg(adapter, DATA, - "TDLS setup packet for %pM.\t" - "Don't block\n", ra); - else if (memcmp(priv->cfg_bssid, ra, ETH_ALEN)) - tdls_status = mwifiex_get_tdls_link_status(priv, ra); - } - - if (!priv->media_connected && !mwifiex_is_skb_mgmt_frame(skb)) { - mwifiex_dbg(adapter, DATA, "data: drop packet in disconnect\n"); - mwifiex_write_data_complete(adapter, skb, 0, -1); - return; - } - - tid = skb->priority; - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - tid_down = mwifiex_wmm_downgrade_tid(priv, tid); - - /* In case of infra as we have already created the list during - association we just don't have to call get_queue_raptr, we will - have only 1 raptr for a tid in case of infra */ - if (!mwifiex_queuing_ra_based(priv) && - !mwifiex_is_skb_mgmt_frame(skb)) { - switch (tdls_status) { - case TDLS_SETUP_COMPLETE: - case TDLS_CHAN_SWITCHING: - case TDLS_IN_BASE_CHAN: - case TDLS_IN_OFF_CHAN: - ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, - ra); - tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; - break; - case TDLS_SETUP_INPROGRESS: - skb_queue_tail(&priv->tdls_txq, skb); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); - return; - default: - list_head = priv->wmm.tid_tbl_ptr[tid_down].ra_list; - if (!list_empty(&list_head)) - ra_list = list_first_entry( - &list_head, struct mwifiex_ra_list_tbl, - list); - else - ra_list = NULL; - break; - } - } else { - memcpy(ra, skb->data, ETH_ALEN); - if (ra[0] & 0x01 || mwifiex_is_skb_mgmt_frame(skb)) - eth_broadcast_addr(ra); - ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, ra); - } - - if (!ra_list) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - mwifiex_write_data_complete(adapter, skb, 0, -1); - return; - } - - skb_queue_tail(&ra_list->skb_head, skb); - - ra_list->ba_pkt_count++; - ra_list->total_pkt_count++; - - if (atomic_read(&priv->wmm.highest_queued_prio) < - priv->tos_to_tid_inv[tid_down]) - atomic_set(&priv->wmm.highest_queued_prio, - priv->tos_to_tid_inv[tid_down]); - - if (ra_list->tx_paused) - priv->wmm.pkts_paused[tid_down]++; - else - atomic_inc(&priv->wmm.tx_pkts_queued); - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - -/* - * This function processes the get WMM status command response from firmware. - * - * The response may contain multiple TLVs - - * - AC Queue status TLVs - * - Current WMM Parameter IE TLV - * - Admission Control action frame TLVs - * - * This function parses the TLVs and then calls further specific functions - * to process any changes in the queue prioritize or state. - */ -int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, - const struct host_cmd_ds_command *resp) -{ - u8 *curr = (u8 *) &resp->params.get_wmm_status; - uint16_t resp_len = le16_to_cpu(resp->size), tlv_len; - int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; - bool valid = true; - - struct mwifiex_ie_types_data *tlv_hdr; - struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus; - struct ieee_types_wmm_parameter *wmm_param_ie = NULL; - struct mwifiex_wmm_ac_status *ac_status; - - mwifiex_dbg(priv->adapter, INFO, - "info: WMM: WMM_GET_STATUS cmdresp received: %d\n", - resp_len); - - while ((resp_len >= sizeof(tlv_hdr->header)) && valid) { - tlv_hdr = (struct mwifiex_ie_types_data *) curr; - tlv_len = le16_to_cpu(tlv_hdr->header.len); - - if (resp_len < tlv_len + sizeof(tlv_hdr->header)) - break; - - switch (le16_to_cpu(tlv_hdr->header.type)) { - case TLV_TYPE_WMMQSTATUS: - tlv_wmm_qstatus = - (struct mwifiex_ie_types_wmm_queue_status *) - tlv_hdr; - mwifiex_dbg(priv->adapter, CMD, - "info: CMD_RESP: WMM_GET_STATUS:\t" - "QSTATUS TLV: %d, %d, %d\n", - tlv_wmm_qstatus->queue_index, - tlv_wmm_qstatus->flow_required, - tlv_wmm_qstatus->disabled); - - ac_status = &priv->wmm.ac_status[tlv_wmm_qstatus-> - queue_index]; - ac_status->disabled = tlv_wmm_qstatus->disabled; - ac_status->flow_required = - tlv_wmm_qstatus->flow_required; - ac_status->flow_created = tlv_wmm_qstatus->flow_created; - break; - - case WLAN_EID_VENDOR_SPECIFIC: - /* - * Point the regular IEEE IE 2 bytes into the Marvell IE - * and setup the IEEE IE type and length byte fields - */ - - wmm_param_ie = - (struct ieee_types_wmm_parameter *) (curr + - 2); - wmm_param_ie->vend_hdr.len = (u8) tlv_len; - wmm_param_ie->vend_hdr.element_id = - WLAN_EID_VENDOR_SPECIFIC; - - mwifiex_dbg(priv->adapter, CMD, - "info: CMD_RESP: WMM_GET_STATUS:\t" - "WMM Parameter Set Count: %d\n", - wmm_param_ie->qos_info_bitmap & mask); - - memcpy((u8 *) &priv->curr_bss_params.bss_descriptor. - wmm_ie, wmm_param_ie, - wmm_param_ie->vend_hdr.len + 2); - - break; - - default: - valid = false; - break; - } - - curr += (tlv_len + sizeof(tlv_hdr->header)); - resp_len -= (tlv_len + sizeof(tlv_hdr->header)); - } - - mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); - mwifiex_wmm_setup_ac_downgrade(priv); - - return 0; -} - -/* - * Callback handler from the command module to allow insertion of a WMM TLV. - * - * If the BSS we are associating to supports WMM, this function adds the - * required WMM Information IE to the association request command buffer in - * the form of a Marvell extended IEEE IE. - */ -u32 -mwifiex_wmm_process_association_req(struct mwifiex_private *priv, - u8 **assoc_buf, - struct ieee_types_wmm_parameter *wmm_ie, - struct ieee80211_ht_cap *ht_cap) -{ - struct mwifiex_ie_types_wmm_param_set *wmm_tlv; - u32 ret_len = 0; - - /* Null checks */ - if (!assoc_buf) - return 0; - if (!(*assoc_buf)) - return 0; - - if (!wmm_ie) - return 0; - - mwifiex_dbg(priv->adapter, INFO, - "info: WMM: process assoc req: bss->wmm_ie=%#x\n", - wmm_ie->vend_hdr.element_id); - - if ((priv->wmm_required || - (ht_cap && (priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN))) && - wmm_ie->vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) { - wmm_tlv = (struct mwifiex_ie_types_wmm_param_set *) *assoc_buf; - wmm_tlv->header.type = cpu_to_le16((u16) wmm_info_ie[0]); - wmm_tlv->header.len = cpu_to_le16((u16) wmm_info_ie[1]); - memcpy(wmm_tlv->wmm_ie, &wmm_info_ie[2], - le16_to_cpu(wmm_tlv->header.len)); - if (wmm_ie->qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) - memcpy((u8 *) (wmm_tlv->wmm_ie - + le16_to_cpu(wmm_tlv->header.len) - - sizeof(priv->wmm_qosinfo)), - &priv->wmm_qosinfo, sizeof(priv->wmm_qosinfo)); - - ret_len = sizeof(wmm_tlv->header) - + le16_to_cpu(wmm_tlv->header.len); - - *assoc_buf += ret_len; - } - - return ret_len; -} - -/* - * This function computes the time delay in the driver queues for a - * given packet. - * - * When the packet is received at the OS/Driver interface, the current - * time is set in the packet structure. The difference between the present - * time and that received time is computed in this function and limited - * based on pre-compiled limits in the driver. - */ -u8 -mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, - const struct sk_buff *skb) -{ - u32 queue_delay = ktime_to_ms(net_timedelta(skb->tstamp)); - u8 ret_val; - - /* - * Queue delay is passed as a uint8 in units of 2ms (ms shifted - * by 1). Min value (other than 0) is therefore 2ms, max is 510ms. - * - * Pass max value if queue_delay is beyond the uint8 range - */ - ret_val = (u8) (min(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1); - - mwifiex_dbg(priv->adapter, DATA, "data: WMM: Pkt Delay: %d ms,\t" - "%d ms sent to FW\n", queue_delay, ret_val); - - return ret_val; -} - -/* - * This function retrieves the highest priority RA list table pointer. - */ -static struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, - struct mwifiex_private **priv, int *tid) -{ - struct mwifiex_private *priv_tmp; - struct mwifiex_ra_list_tbl *ptr; - struct mwifiex_tid_tbl *tid_ptr; - atomic_t *hqp; - unsigned long flags_ra; - int i, j; - - /* check the BSS with highest priority first */ - for (j = adapter->priv_num - 1; j >= 0; --j) { - /* iterate over BSS with the equal priority */ - list_for_each_entry(adapter->bss_prio_tbl[j].bss_prio_cur, - &adapter->bss_prio_tbl[j].bss_prio_head, - list) { - - priv_tmp = adapter->bss_prio_tbl[j].bss_prio_cur->priv; - - if (!priv_tmp->port_open || - (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0)) - continue; - - if (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv_tmp)) - continue; - - /* iterate over the WMM queues of the BSS */ - hqp = &priv_tmp->wmm.highest_queued_prio; - for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) { - - spin_lock_irqsave(&priv_tmp->wmm. - ra_list_spinlock, flags_ra); - - tid_ptr = &(priv_tmp)->wmm. - tid_tbl_ptr[tos_to_tid[i]]; - - /* iterate over receiver addresses */ - list_for_each_entry(ptr, &tid_ptr->ra_list, - list) { - - if (!ptr->tx_paused && - !skb_queue_empty(&ptr->skb_head)) - /* holds both locks */ - goto found; - } - - spin_unlock_irqrestore(&priv_tmp->wmm. - ra_list_spinlock, - flags_ra); - } - } - - } - - return NULL; - -found: - /* holds ra_list_spinlock */ - if (atomic_read(hqp) > i) - atomic_set(hqp, i); - spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags_ra); - - *priv = priv_tmp; - *tid = tos_to_tid[i]; - - return ptr; -} - -/* This functions rotates ra and bss lists so packets are picked round robin. - * - * After a packet is successfully transmitted, rotate the ra list, so the ra - * next to the one transmitted, will come first in the list. This way we pick - * the ra' in a round robin fashion. Same applies to bss nodes of equal - * priority. - * - * Function also increments wmm.packets_out counter. - */ -void mwifiex_rotate_priolists(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra, - int tid) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; - struct mwifiex_tid_tbl *tid_ptr = &priv->wmm.tid_tbl_ptr[tid]; - unsigned long flags; - - spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags); - /* - * dirty trick: we remove 'head' temporarily and reinsert it after - * curr bss node. imagine list to stay fixed while head is moved - */ - list_move(&tbl[priv->bss_priority].bss_prio_head, - &tbl[priv->bss_priority].bss_prio_cur->list); - spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags); - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - if (mwifiex_is_ralist_valid(priv, ra, tid)) { - priv->wmm.packets_out[tid]++; - /* same as above */ - list_move(&tid_ptr->ra_list, &ra->list); - } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - -/* - * This function checks if 11n aggregation is possible. - */ -static int -mwifiex_is_11n_aggragation_possible(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, - int max_buf_size) -{ - int count = 0, total_size = 0; - struct sk_buff *skb, *tmp; - int max_amsdu_size; - - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP && priv->ap_11n_enabled && - ptr->is_11n_enabled) - max_amsdu_size = min_t(int, ptr->max_amsdu, max_buf_size); - else - max_amsdu_size = max_buf_size; - - skb_queue_walk_safe(&ptr->skb_head, skb, tmp) { - total_size += skb->len; - if (total_size >= max_amsdu_size) - break; - if (++count >= MIN_NUM_AMSDU) - return true; - } - - return false; -} - -/* - * This function sends a single packet to firmware for transmission. - */ -static void -mwifiex_send_single_packet(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int ptr_index, - unsigned long ra_list_flags) - __releases(&priv->wmm.ra_list_spinlock) -{ - struct sk_buff *skb, *skb_next; - struct mwifiex_tx_param tx_param; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_txinfo *tx_info; - - if (skb_queue_empty(&ptr->skb_head)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_dbg(adapter, DATA, "data: nothing to send\n"); - return; - } - - skb = skb_dequeue(&ptr->skb_head); - - tx_info = MWIFIEX_SKB_TXCB(skb); - mwifiex_dbg(adapter, DATA, - "data: dequeuing the packet %p %p\n", ptr, skb); - - ptr->total_pkt_count--; - - if (!skb_queue_empty(&ptr->skb_head)) - skb_next = skb_peek(&ptr->skb_head); - else - skb_next = NULL; - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); - - tx_param.next_pkt_len = ((skb_next) ? skb_next->len + - sizeof(struct txpd) : 0); - - if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { - /* Queue the packet back at the head */ - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); - - if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_write_data_complete(adapter, skb, 0, -1); - return; - } - - skb_queue_tail(&ptr->skb_head, skb); - - ptr->total_pkt_count++; - ptr->ba_pkt_count++; - tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - } else { - mwifiex_rotate_priolists(priv, ptr, ptr_index); - atomic_dec(&priv->wmm.tx_pkts_queued); - } -} - -/* - * This function checks if the first packet in the given RA list - * is already processed or not. - */ -static int -mwifiex_is_ptr_processed(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr) -{ - struct sk_buff *skb; - struct mwifiex_txinfo *tx_info; - - if (skb_queue_empty(&ptr->skb_head)) - return false; - - skb = skb_peek(&ptr->skb_head); - - tx_info = MWIFIEX_SKB_TXCB(skb); - if (tx_info->flags & MWIFIEX_BUF_FLAG_REQUEUED_PKT) - return true; - - return false; -} - -/* - * This function sends a single processed packet to firmware for - * transmission. - */ -static void -mwifiex_send_processed_packet(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int ptr_index, - unsigned long ra_list_flags) - __releases(&priv->wmm.ra_list_spinlock) -{ - struct mwifiex_tx_param tx_param; - struct mwifiex_adapter *adapter = priv->adapter; - int ret = -1; - struct sk_buff *skb, *skb_next; - struct mwifiex_txinfo *tx_info; - - if (skb_queue_empty(&ptr->skb_head)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - return; - } - - skb = skb_dequeue(&ptr->skb_head); - - if (adapter->data_sent || adapter->tx_lock_flag) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - skb_queue_tail(&adapter->tx_data_q, skb); - atomic_inc(&adapter->tx_queued); - return; - } - - if (!skb_queue_empty(&ptr->skb_head)) - skb_next = skb_peek(&ptr->skb_head); - else - skb_next = NULL; - - tx_info = MWIFIEX_SKB_TXCB(skb); - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); - - if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, - skb, NULL); - } else { - tx_param.next_pkt_len = - ((skb_next) ? skb_next->len + - sizeof(struct txpd) : 0); - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, - skb, &tx_param); - } - - switch (ret) { - case -EBUSY: - mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); - - if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_write_data_complete(adapter, skb, 0, -1); - return; - } - - skb_queue_tail(&ptr->skb_head, skb); - - tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - break; - case -1: - mwifiex_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret); - adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb, 0, ret); - break; - case -EINPROGRESS: - break; - case 0: - mwifiex_write_data_complete(adapter, skb, 0, ret); - default: - break; - } - if (ret != -EBUSY) { - mwifiex_rotate_priolists(priv, ptr, ptr_index); - atomic_dec(&priv->wmm.tx_pkts_queued); - } -} - -/* - * This function dequeues a packet from the highest priority list - * and transmits it. - */ -static int -mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) -{ - struct mwifiex_ra_list_tbl *ptr; - struct mwifiex_private *priv = NULL; - int ptr_index = 0; - u8 ra[ETH_ALEN]; - int tid_del = 0, tid = 0; - unsigned long flags; - - ptr = mwifiex_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index); - if (!ptr) - return -1; - - tid = mwifiex_get_tid(ptr); - - mwifiex_dbg(adapter, DATA, "data: tid=%d\n", tid); - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - return -1; - } - - if (mwifiex_is_ptr_processed(priv, ptr)) { - mwifiex_send_processed_packet(priv, ptr, ptr_index, flags); - /* ra_list_spinlock has been freed in - mwifiex_send_processed_packet() */ - return 0; - } - - if (!ptr->is_11n_enabled || - ptr->ba_status || - priv->wps.session_enable) { - if (ptr->is_11n_enabled && - ptr->ba_status && - ptr->amsdu_in_ampdu && - mwifiex_is_amsdu_allowed(priv, tid) && - mwifiex_is_11n_aggragation_possible(priv, ptr, - adapter->tx_buf_size)) - mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); - /* ra_list_spinlock has been freed in - * mwifiex_11n_aggregate_pkt() - */ - else - mwifiex_send_single_packet(priv, ptr, ptr_index, flags); - /* ra_list_spinlock has been freed in - * mwifiex_send_single_packet() - */ - } else { - if (mwifiex_is_ampdu_allowed(priv, ptr, tid) && - ptr->ba_pkt_count > ptr->ba_packet_thr) { - if (mwifiex_space_avail_for_new_ba_stream(adapter)) { - mwifiex_create_ba_tbl(priv, ptr->ra, tid, - BA_SETUP_INPROGRESS); - mwifiex_send_addba(priv, tid, ptr->ra); - } else if (mwifiex_find_stream_to_delete - (priv, tid, &tid_del, ra)) { - mwifiex_create_ba_tbl(priv, ptr->ra, tid, - BA_SETUP_INPROGRESS); - mwifiex_send_delba(priv, tid_del, ra, 1); - } - } - if (mwifiex_is_amsdu_allowed(priv, tid) && - mwifiex_is_11n_aggragation_possible(priv, ptr, - adapter->tx_buf_size)) - mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); - /* ra_list_spinlock has been freed in - mwifiex_11n_aggregate_pkt() */ - else - mwifiex_send_single_packet(priv, ptr, ptr_index, flags); - /* ra_list_spinlock has been freed in - mwifiex_send_single_packet() */ - } - return 0; -} - -void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter) -{ - struct mwifiex_tx_param tx_param; - struct sk_buff *skb; - struct mwifiex_txinfo *tx_info; - struct mwifiex_private *priv; - int i; - - if (adapter->data_sent || adapter->tx_lock_flag) - return; - - for (i = 0; i < adapter->priv_num; ++i) { - priv = adapter->priv[i]; - - if (!priv) - continue; - - if (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv)) - continue; - - if (skb_queue_empty(&priv->bypass_txq)) - continue; - - skb = skb_dequeue(&priv->bypass_txq); - tx_info = MWIFIEX_SKB_TXCB(skb); - - /* no aggregation for bypass packets */ - tx_param.next_pkt_len = 0; - - if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { - skb_queue_head(&priv->bypass_txq, skb); - tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; - } else { - atomic_dec(&adapter->bypass_tx_pending); - } - } -} - -/* - * This function transmits the highest priority packet awaiting in the - * WMM Queues. - */ -void -mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter) -{ - do { - if (mwifiex_dequeue_tx_packet(adapter)) - break; - if (adapter->iface_type != MWIFIEX_SDIO) { - if (adapter->data_sent || - adapter->tx_lock_flag) - break; - } else { - if (atomic_read(&adapter->tx_queued) >= - MWIFIEX_MAX_PKTS_TXQ) - break; - } - } while (!mwifiex_wmm_lists_empty(adapter)); -} diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h deleted file mode 100644 index 38f09762bd2f..000000000000 --- a/drivers/net/wireless/mwifiex/wmm.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Marvell Wireless LAN device driver: WMM - * - * Copyright (C) 2011-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 by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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. - */ - -#ifndef _MWIFIEX_WMM_H_ -#define _MWIFIEX_WMM_H_ - -enum ieee_types_wmm_aciaifsn_bitmasks { - MWIFIEX_AIFSN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), - MWIFIEX_ACM = BIT(4), - MWIFIEX_ACI = (BIT(5) | BIT(6)), -}; - -enum ieee_types_wmm_ecw_bitmasks { - MWIFIEX_ECW_MIN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), - MWIFIEX_ECW_MAX = (BIT(4) | BIT(5) | BIT(6) | BIT(7)), -}; - -static const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; - -/* - * This table inverses the tos_to_tid operation to get a priority - * which is in sequential order, and can be compared. - * Use this to compare the priority of two different TIDs. - */ -static const u8 tos_to_tid_inv[] = { - 0x02, /* from tos_to_tid[2] = 0 */ - 0x00, /* from tos_to_tid[0] = 1 */ - 0x01, /* from tos_to_tid[1] = 2 */ - 0x03, - 0x04, - 0x05, - 0x06, - 0x07}; - -/* - * This function retrieves the TID of the given RA list. - */ -static inline int -mwifiex_get_tid(struct mwifiex_ra_list_tbl *ptr) -{ - struct sk_buff *skb; - - if (skb_queue_empty(&ptr->skb_head)) - return 0; - - skb = skb_peek(&ptr->skb_head); - - return skb->priority; -} - -/* - * This function gets the length of a list. - */ -static inline int -mwifiex_wmm_list_len(struct list_head *head) -{ - struct list_head *pos; - int count = 0; - - list_for_each(pos, head) - ++count; - - return count; -} - -/* - * This function checks if a RA list is empty or not. - */ -static inline u8 -mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead) -{ - struct mwifiex_ra_list_tbl *ra_list; - int is_list_empty; - - list_for_each_entry(ra_list, ra_list_hhead, list) { - is_list_empty = skb_queue_empty(&ra_list->skb_head); - if (!is_list_empty) - return false; - } - - return true; -} - -void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, - struct sk_buff *skb); -void mwifiex_wmm_add_buf_bypass_txqueue(struct mwifiex_private *priv, - struct sk_buff *skb); -void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra); -void mwifiex_rotate_priolists(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra, int tid); - -int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter); -int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter); -void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter); -void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter); -int mwifiex_is_ralist_valid(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra_list, int tid); - -u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, - const struct sk_buff *skb); -void mwifiex_wmm_init(struct mwifiex_adapter *adapter); - -u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, - u8 **assoc_buf, - struct ieee_types_wmm_parameter *wmmie, - struct ieee80211_ht_cap *htcap); - -void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, - struct ieee_types_wmm_parameter *wmm_ie); -void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); -int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, - const struct host_cmd_ds_command *resp); -struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, - const u8 *ra_addr); -u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid); -void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, - u8 tx_pause); -void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, - u8 *mac, u8 tx_pause); - -struct mwifiex_ra_list_tbl *mwifiex_wmm_get_ralist_node(struct mwifiex_private - *priv, u8 tid, const u8 *ra_addr); -#endif /* !_MWIFIEX_WMM_H_ */ -- cgit v1.2.3-59-g8ed1b From de60f1dc23f65c308636036cbb14e0d3ada36f27 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Nov 2015 21:18:12 +0200 Subject: mwl8k: move under marvell vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 9 - drivers/net/wireless/Makefile | 2 - drivers/net/wireless/marvell/Kconfig | 9 + drivers/net/wireless/marvell/Makefile | 2 + drivers/net/wireless/marvell/mwl8k.c | 6346 +++++++++++++++++++++++++++++++++ drivers/net/wireless/mwl8k.c | 6346 --------------------------------- 7 files changed, 6358 insertions(+), 6358 deletions(-) create mode 100644 drivers/net/wireless/marvell/mwl8k.c delete mode 100644 drivers/net/wireless/mwl8k.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 5b889f6972c0..e622877aaab4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6734,7 +6734,7 @@ MARVELL MWL8K WIRELESS DRIVER M: Lennert Buytenhek L: linux-wireless@vger.kernel.org S: Odd Fixes -F: drivers/net/wireless/mwl8k.c +F: drivers/net/wireless/marvell/mwl8k.c MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER M: Nicolas Pitre diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 465665237308..315982a5ee73 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -133,15 +133,6 @@ config MAC80211_HWSIM To compile this driver as a module, choose M here: the module will be called mac80211_hwsim. If unsure, say N. -config MWL8K - tristate "Marvell 88W8xxx PCI/PCIe Wireless support" - depends on MAC80211 && PCI - ---help--- - This driver supports Marvell TOPDOG 802.11 wireless cards. - - To compile this driver as a module, choose M here: the module - will be called mwl8k. If unsure, say N. - source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/orinoco/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 7a95b58a5c76..9498753f596b 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -26,8 +26,6 @@ obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o obj-$(CONFIG_USB_ZD1201) += zd1201.o -obj-$(CONFIG_MWL8K) += mwl8k.o - obj-$(CONFIG_RT2X00) += rt2x00/ obj-$(CONFIG_WL_MEDIATEK) += mediatek/ diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig index bbdbac9a2a45..4938c7ec0009 100644 --- a/drivers/net/wireless/marvell/Kconfig +++ b/drivers/net/wireless/marvell/Kconfig @@ -15,4 +15,13 @@ source "drivers/net/wireless/marvell/libertas/Kconfig" source "drivers/net/wireless/marvell/libertas_tf/Kconfig" source "drivers/net/wireless/marvell/mwifiex/Kconfig" +config MWL8K + tristate "Marvell 88W8xxx PCI/PCIe Wireless support" + depends on MAC80211 && PCI + ---help--- + This driver supports Marvell TOPDOG 802.11 wireless cards. + + To compile this driver as a module, choose M here: the module + will be called mwl8k. If unsure, say N. + endif # WLAN_VENDOR_MARVELL diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile index f4ab48aaff3c..1b0a7d2bc8e6 100644 --- a/drivers/net/wireless/marvell/Makefile +++ b/drivers/net/wireless/marvell/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_LIBERTAS) += libertas/ obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ obj-$(CONFIG_MWIFIEX) += mwifiex/ + +obj-$(CONFIG_MWL8K) += mwl8k.o diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c new file mode 100644 index 000000000000..30e3aaae32e2 --- /dev/null +++ b/drivers/net/wireless/marvell/mwl8k.c @@ -0,0 +1,6346 @@ +/* + * drivers/net/wireless/mwl8k.c + * Driver for Marvell TOPDOG 802.11 Wireless cards + * + * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver" +#define MWL8K_NAME KBUILD_MODNAME +#define MWL8K_VERSION "0.13" + +/* Module parameters */ +static bool ap_mode_default; +module_param(ap_mode_default, bool, 0); +MODULE_PARM_DESC(ap_mode_default, + "Set to 1 to make ap mode the default instead of sta mode"); + +/* Register definitions */ +#define MWL8K_HIU_GEN_PTR 0x00000c10 +#define MWL8K_MODE_STA 0x0000005a +#define MWL8K_MODE_AP 0x000000a5 +#define MWL8K_HIU_INT_CODE 0x00000c14 +#define MWL8K_FWSTA_READY 0xf0f1f2f4 +#define MWL8K_FWAP_READY 0xf1f2f4a5 +#define MWL8K_INT_CODE_CMD_FINISHED 0x00000005 +#define MWL8K_HIU_SCRATCH 0x00000c40 + +/* Host->device communications */ +#define MWL8K_HIU_H2A_INTERRUPT_EVENTS 0x00000c18 +#define MWL8K_HIU_H2A_INTERRUPT_STATUS 0x00000c1c +#define MWL8K_HIU_H2A_INTERRUPT_MASK 0x00000c20 +#define MWL8K_HIU_H2A_INTERRUPT_CLEAR_SEL 0x00000c24 +#define MWL8K_HIU_H2A_INTERRUPT_STATUS_MASK 0x00000c28 +#define MWL8K_H2A_INT_DUMMY (1 << 20) +#define MWL8K_H2A_INT_RESET (1 << 15) +#define MWL8K_H2A_INT_DOORBELL (1 << 1) +#define MWL8K_H2A_INT_PPA_READY (1 << 0) + +/* Device->host communications */ +#define MWL8K_HIU_A2H_INTERRUPT_EVENTS 0x00000c2c +#define MWL8K_HIU_A2H_INTERRUPT_STATUS 0x00000c30 +#define MWL8K_HIU_A2H_INTERRUPT_MASK 0x00000c34 +#define MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL 0x00000c38 +#define MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK 0x00000c3c +#define MWL8K_A2H_INT_DUMMY (1 << 20) +#define MWL8K_A2H_INT_BA_WATCHDOG (1 << 14) +#define MWL8K_A2H_INT_CHNL_SWITCHED (1 << 11) +#define MWL8K_A2H_INT_QUEUE_EMPTY (1 << 10) +#define MWL8K_A2H_INT_RADAR_DETECT (1 << 7) +#define MWL8K_A2H_INT_RADIO_ON (1 << 6) +#define MWL8K_A2H_INT_RADIO_OFF (1 << 5) +#define MWL8K_A2H_INT_MAC_EVENT (1 << 3) +#define MWL8K_A2H_INT_OPC_DONE (1 << 2) +#define MWL8K_A2H_INT_RX_READY (1 << 1) +#define MWL8K_A2H_INT_TX_DONE (1 << 0) + +/* HW micro second timer register + * located at offset 0xA600. This + * will be used to timestamp tx + * packets. + */ + +#define MWL8K_HW_TIMER_REGISTER 0x0000a600 +#define BBU_RXRDY_CNT_REG 0x0000a860 +#define NOK_CCA_CNT_REG 0x0000a6a0 +#define BBU_AVG_NOISE_VAL 0x67 + +#define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \ + MWL8K_A2H_INT_CHNL_SWITCHED | \ + MWL8K_A2H_INT_QUEUE_EMPTY | \ + MWL8K_A2H_INT_RADAR_DETECT | \ + MWL8K_A2H_INT_RADIO_ON | \ + MWL8K_A2H_INT_RADIO_OFF | \ + MWL8K_A2H_INT_MAC_EVENT | \ + MWL8K_A2H_INT_OPC_DONE | \ + MWL8K_A2H_INT_RX_READY | \ + MWL8K_A2H_INT_TX_DONE | \ + MWL8K_A2H_INT_BA_WATCHDOG) + +#define MWL8K_RX_QUEUES 1 +#define MWL8K_TX_WMM_QUEUES 4 +#define MWL8K_MAX_AMPDU_QUEUES 8 +#define MWL8K_MAX_TX_QUEUES (MWL8K_TX_WMM_QUEUES + MWL8K_MAX_AMPDU_QUEUES) +#define mwl8k_tx_queues(priv) (MWL8K_TX_WMM_QUEUES + (priv)->num_ampdu_queues) + +/* txpriorities are mapped with hw queues. + * Each hw queue has a txpriority. + */ +#define TOTAL_HW_TX_QUEUES 8 + +/* Each HW queue can have one AMPDU stream. + * But, because one of the hw queue is reserved, + * maximum AMPDU queues that can be created are + * one short of total tx queues. + */ +#define MWL8K_NUM_AMPDU_STREAMS (TOTAL_HW_TX_QUEUES - 1) + +#define MWL8K_NUM_CHANS 18 + +struct rxd_ops { + int rxd_size; + void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); + void (*rxd_refill)(void *rxd, dma_addr_t addr, int len); + int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status, + __le16 *qos, s8 *noise); +}; + +struct mwl8k_device_info { + char *part_name; + char *helper_image; + char *fw_image_sta; + char *fw_image_ap; + struct rxd_ops *ap_rxd_ops; + u32 fw_api_ap; +}; + +struct mwl8k_rx_queue { + int rxd_count; + + /* hw receives here */ + int head; + + /* refill descs here */ + int tail; + + void *rxd; + dma_addr_t rxd_dma; + struct { + struct sk_buff *skb; + DEFINE_DMA_UNMAP_ADDR(dma); + } *buf; +}; + +struct mwl8k_tx_queue { + /* hw transmits here */ + int head; + + /* sw appends here */ + int tail; + + unsigned int len; + struct mwl8k_tx_desc *txd; + dma_addr_t txd_dma; + struct sk_buff **skb; +}; + +enum { + AMPDU_NO_STREAM, + AMPDU_STREAM_NEW, + AMPDU_STREAM_IN_PROGRESS, + AMPDU_STREAM_ACTIVE, +}; + +struct mwl8k_ampdu_stream { + struct ieee80211_sta *sta; + u8 tid; + u8 state; + u8 idx; +}; + +struct mwl8k_priv { + struct ieee80211_hw *hw; + struct pci_dev *pdev; + int irq; + + struct mwl8k_device_info *device_info; + + void __iomem *sram; + void __iomem *regs; + + /* firmware */ + const struct firmware *fw_helper; + const struct firmware *fw_ucode; + + /* hardware/firmware parameters */ + bool ap_fw; + struct rxd_ops *rxd_ops; + struct ieee80211_supported_band band_24; + struct ieee80211_channel channels_24[14]; + struct ieee80211_rate rates_24[13]; + struct ieee80211_supported_band band_50; + struct ieee80211_channel channels_50[4]; + struct ieee80211_rate rates_50[8]; + u32 ap_macids_supported; + u32 sta_macids_supported; + + /* Ampdu stream information */ + u8 num_ampdu_queues; + spinlock_t stream_lock; + struct mwl8k_ampdu_stream ampdu[MWL8K_MAX_AMPDU_QUEUES]; + struct work_struct watchdog_ba_handle; + + /* firmware access */ + struct mutex fw_mutex; + struct task_struct *fw_mutex_owner; + struct task_struct *hw_restart_owner; + int fw_mutex_depth; + struct completion *hostcmd_wait; + + atomic_t watchdog_event_pending; + + /* lock held over TX and TX reap */ + spinlock_t tx_lock; + + /* TX quiesce completion, protected by fw_mutex and tx_lock */ + struct completion *tx_wait; + + /* List of interfaces. */ + u32 macids_used; + struct list_head vif_list; + + /* power management status cookie from firmware */ + u32 *cookie; + dma_addr_t cookie_dma; + + u16 num_mcaddrs; + u8 hw_rev; + u32 fw_rev; + u32 caps; + + /* + * Running count of TX packets in flight, to avoid + * iterating over the transmit rings each time. + */ + int pending_tx_pkts; + + struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES]; + struct mwl8k_tx_queue txq[MWL8K_MAX_TX_QUEUES]; + u32 txq_offset[MWL8K_MAX_TX_QUEUES]; + + bool radio_on; + bool radio_short_preamble; + bool sniffer_enabled; + bool wmm_enabled; + + /* XXX need to convert this to handle multiple interfaces */ + bool capture_beacon; + u8 capture_bssid[ETH_ALEN]; + struct sk_buff *beacon_skb; + + /* + * This FJ worker has to be global as it is scheduled from the + * RX handler. At this point we don't know which interface it + * belongs to until the list of bssids waiting to complete join + * is checked. + */ + struct work_struct finalize_join_worker; + + /* Tasklet to perform TX reclaim. */ + struct tasklet_struct poll_tx_task; + + /* Tasklet to perform RX. */ + struct tasklet_struct poll_rx_task; + + /* Most recently reported noise in dBm */ + s8 noise; + + /* + * preserve the queue configurations so they can be restored if/when + * the firmware image is swapped. + */ + struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES]; + + /* To perform the task of reloading the firmware */ + struct work_struct fw_reload; + bool hw_restart_in_progress; + + /* async firmware loading state */ + unsigned fw_state; + char *fw_pref; + char *fw_alt; + bool is_8764; + struct completion firmware_loading_complete; + + /* bitmap of running BSSes */ + u32 running_bsses; + + /* ACS related */ + bool sw_scan_start; + struct ieee80211_channel *acs_chan; + unsigned long channel_time; + struct survey_info survey[MWL8K_NUM_CHANS]; +}; + +#define MAX_WEP_KEY_LEN 13 +#define NUM_WEP_KEYS 4 + +/* Per interface specific private data */ +struct mwl8k_vif { + struct list_head list; + struct ieee80211_vif *vif; + + /* Firmware macid for this vif. */ + int macid; + + /* Non AMPDU sequence number assigned by driver. */ + u16 seqno; + + /* Saved WEP keys */ + struct { + u8 enabled; + u8 key[sizeof(struct ieee80211_key_conf) + MAX_WEP_KEY_LEN]; + } wep_key_conf[NUM_WEP_KEYS]; + + /* BSSID */ + u8 bssid[ETH_ALEN]; + + /* A flag to indicate is HW crypto is enabled for this bssid */ + bool is_hw_crypto_enabled; +}; +#define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) +#define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8)) + +struct tx_traffic_info { + u32 start_time; + u32 pkts; +}; + +#define MWL8K_MAX_TID 8 +struct mwl8k_sta { + /* Index into station database. Returned by UPDATE_STADB. */ + u8 peer_id; + u8 is_ampdu_allowed; + struct tx_traffic_info tx_stats[MWL8K_MAX_TID]; +}; +#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv)) + +static const struct ieee80211_channel mwl8k_channels_24[] = { + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2412, .hw_value = 1, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2417, .hw_value = 2, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2422, .hw_value = 3, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2427, .hw_value = 4, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2432, .hw_value = 5, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2437, .hw_value = 6, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2442, .hw_value = 7, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2447, .hw_value = 8, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2452, .hw_value = 9, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2457, .hw_value = 10, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2462, .hw_value = 11, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2467, .hw_value = 12, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2472, .hw_value = 13, }, + { .band = IEEE80211_BAND_2GHZ, .center_freq = 2484, .hw_value = 14, }, +}; + +static const struct ieee80211_rate mwl8k_rates_24[] = { + { .bitrate = 10, .hw_value = 2, }, + { .bitrate = 20, .hw_value = 4, }, + { .bitrate = 55, .hw_value = 11, }, + { .bitrate = 110, .hw_value = 22, }, + { .bitrate = 220, .hw_value = 44, }, + { .bitrate = 60, .hw_value = 12, }, + { .bitrate = 90, .hw_value = 18, }, + { .bitrate = 120, .hw_value = 24, }, + { .bitrate = 180, .hw_value = 36, }, + { .bitrate = 240, .hw_value = 48, }, + { .bitrate = 360, .hw_value = 72, }, + { .bitrate = 480, .hw_value = 96, }, + { .bitrate = 540, .hw_value = 108, }, +}; + +static const struct ieee80211_channel mwl8k_channels_50[] = { + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5180, .hw_value = 36, }, + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, }, + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, }, + { .band = IEEE80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, }, +}; + +static const struct ieee80211_rate mwl8k_rates_50[] = { + { .bitrate = 60, .hw_value = 12, }, + { .bitrate = 90, .hw_value = 18, }, + { .bitrate = 120, .hw_value = 24, }, + { .bitrate = 180, .hw_value = 36, }, + { .bitrate = 240, .hw_value = 48, }, + { .bitrate = 360, .hw_value = 72, }, + { .bitrate = 480, .hw_value = 96, }, + { .bitrate = 540, .hw_value = 108, }, +}; + +/* Set or get info from Firmware */ +#define MWL8K_CMD_GET 0x0000 +#define MWL8K_CMD_SET 0x0001 +#define MWL8K_CMD_SET_LIST 0x0002 + +/* Firmware command codes */ +#define MWL8K_CMD_CODE_DNLD 0x0001 +#define MWL8K_CMD_GET_HW_SPEC 0x0003 +#define MWL8K_CMD_SET_HW_SPEC 0x0004 +#define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010 +#define MWL8K_CMD_GET_STAT 0x0014 +#define MWL8K_CMD_BBP_REG_ACCESS 0x001a +#define MWL8K_CMD_RADIO_CONTROL 0x001c +#define MWL8K_CMD_RF_TX_POWER 0x001e +#define MWL8K_CMD_TX_POWER 0x001f +#define MWL8K_CMD_RF_ANTENNA 0x0020 +#define MWL8K_CMD_SET_BEACON 0x0100 /* per-vif */ +#define MWL8K_CMD_SET_PRE_SCAN 0x0107 +#define MWL8K_CMD_SET_POST_SCAN 0x0108 +#define MWL8K_CMD_SET_RF_CHANNEL 0x010a +#define MWL8K_CMD_SET_AID 0x010d +#define MWL8K_CMD_SET_RATE 0x0110 +#define MWL8K_CMD_SET_FINALIZE_JOIN 0x0111 +#define MWL8K_CMD_RTS_THRESHOLD 0x0113 +#define MWL8K_CMD_SET_SLOT 0x0114 +#define MWL8K_CMD_SET_EDCA_PARAMS 0x0115 +#define MWL8K_CMD_SET_WMM_MODE 0x0123 +#define MWL8K_CMD_MIMO_CONFIG 0x0125 +#define MWL8K_CMD_USE_FIXED_RATE 0x0126 +#define MWL8K_CMD_ENABLE_SNIFFER 0x0150 +#define MWL8K_CMD_SET_MAC_ADDR 0x0202 /* per-vif */ +#define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 +#define MWL8K_CMD_GET_WATCHDOG_BITMAP 0x0205 +#define MWL8K_CMD_DEL_MAC_ADDR 0x0206 /* per-vif */ +#define MWL8K_CMD_BSS_START 0x1100 /* per-vif */ +#define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */ +#define MWL8K_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */ +#define MWL8K_CMD_UPDATE_STADB 0x1123 +#define MWL8K_CMD_BASTREAM 0x1125 + +static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize) +{ + u16 command = le16_to_cpu(cmd); + +#define MWL8K_CMDNAME(x) case MWL8K_CMD_##x: do {\ + snprintf(buf, bufsize, "%s", #x);\ + return buf;\ + } while (0) + switch (command & ~0x8000) { + MWL8K_CMDNAME(CODE_DNLD); + MWL8K_CMDNAME(GET_HW_SPEC); + MWL8K_CMDNAME(SET_HW_SPEC); + MWL8K_CMDNAME(MAC_MULTICAST_ADR); + MWL8K_CMDNAME(GET_STAT); + MWL8K_CMDNAME(RADIO_CONTROL); + MWL8K_CMDNAME(RF_TX_POWER); + MWL8K_CMDNAME(TX_POWER); + MWL8K_CMDNAME(RF_ANTENNA); + MWL8K_CMDNAME(SET_BEACON); + MWL8K_CMDNAME(SET_PRE_SCAN); + MWL8K_CMDNAME(SET_POST_SCAN); + MWL8K_CMDNAME(SET_RF_CHANNEL); + MWL8K_CMDNAME(SET_AID); + MWL8K_CMDNAME(SET_RATE); + MWL8K_CMDNAME(SET_FINALIZE_JOIN); + MWL8K_CMDNAME(RTS_THRESHOLD); + MWL8K_CMDNAME(SET_SLOT); + MWL8K_CMDNAME(SET_EDCA_PARAMS); + MWL8K_CMDNAME(SET_WMM_MODE); + MWL8K_CMDNAME(MIMO_CONFIG); + MWL8K_CMDNAME(USE_FIXED_RATE); + MWL8K_CMDNAME(ENABLE_SNIFFER); + MWL8K_CMDNAME(SET_MAC_ADDR); + MWL8K_CMDNAME(SET_RATEADAPT_MODE); + MWL8K_CMDNAME(BSS_START); + MWL8K_CMDNAME(SET_NEW_STN); + MWL8K_CMDNAME(UPDATE_ENCRYPTION); + MWL8K_CMDNAME(UPDATE_STADB); + MWL8K_CMDNAME(BASTREAM); + MWL8K_CMDNAME(GET_WATCHDOG_BITMAP); + default: + snprintf(buf, bufsize, "0x%x", cmd); + } +#undef MWL8K_CMDNAME + + return buf; +} + +/* Hardware and firmware reset */ +static void mwl8k_hw_reset(struct mwl8k_priv *priv) +{ + iowrite32(MWL8K_H2A_INT_RESET, + priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); + iowrite32(MWL8K_H2A_INT_RESET, + priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); + msleep(20); +} + +/* Release fw image */ +static void mwl8k_release_fw(const struct firmware **fw) +{ + if (*fw == NULL) + return; + release_firmware(*fw); + *fw = NULL; +} + +static void mwl8k_release_firmware(struct mwl8k_priv *priv) +{ + mwl8k_release_fw(&priv->fw_ucode); + mwl8k_release_fw(&priv->fw_helper); +} + +/* states for asynchronous f/w loading */ +static void mwl8k_fw_state_machine(const struct firmware *fw, void *context); +enum { + FW_STATE_INIT = 0, + FW_STATE_LOADING_PREF, + FW_STATE_LOADING_ALT, + FW_STATE_ERROR, +}; + +/* Request fw image */ +static int mwl8k_request_fw(struct mwl8k_priv *priv, + const char *fname, const struct firmware **fw, + bool nowait) +{ + /* release current image */ + if (*fw != NULL) + mwl8k_release_fw(fw); + + if (nowait) + return request_firmware_nowait(THIS_MODULE, 1, fname, + &priv->pdev->dev, GFP_KERNEL, + priv, mwl8k_fw_state_machine); + else + return request_firmware(fw, fname, &priv->pdev->dev); +} + +static int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image, + bool nowait) +{ + struct mwl8k_device_info *di = priv->device_info; + int rc; + + if (di->helper_image != NULL) { + if (nowait) + rc = mwl8k_request_fw(priv, di->helper_image, + &priv->fw_helper, true); + else + rc = mwl8k_request_fw(priv, di->helper_image, + &priv->fw_helper, false); + if (rc) + printk(KERN_ERR "%s: Error requesting helper fw %s\n", + pci_name(priv->pdev), di->helper_image); + + if (rc || nowait) + return rc; + } + + if (nowait) { + /* + * if we get here, no helper image is needed. Skip the + * FW_STATE_INIT state. + */ + priv->fw_state = FW_STATE_LOADING_PREF; + rc = mwl8k_request_fw(priv, fw_image, + &priv->fw_ucode, + true); + } else + rc = mwl8k_request_fw(priv, fw_image, + &priv->fw_ucode, false); + if (rc) { + printk(KERN_ERR "%s: Error requesting firmware file %s\n", + pci_name(priv->pdev), fw_image); + mwl8k_release_fw(&priv->fw_helper); + return rc; + } + + return 0; +} + +struct mwl8k_cmd_pkt { + __le16 code; + __le16 length; + __u8 seq_num; + __u8 macid; + __le16 result; + char payload[0]; +} __packed; + +/* + * Firmware loading. + */ +static int +mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length) +{ + void __iomem *regs = priv->regs; + dma_addr_t dma_addr; + int loops; + + dma_addr = pci_map_single(priv->pdev, data, length, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(priv->pdev, dma_addr)) + return -ENOMEM; + + iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); + iowrite32(0, regs + MWL8K_HIU_INT_CODE); + iowrite32(MWL8K_H2A_INT_DOORBELL, + regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); + iowrite32(MWL8K_H2A_INT_DUMMY, + regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); + + loops = 1000; + do { + u32 int_code; + if (priv->is_8764) { + int_code = ioread32(regs + + MWL8K_HIU_H2A_INTERRUPT_STATUS); + if (int_code == 0) + break; + } else { + int_code = ioread32(regs + MWL8K_HIU_INT_CODE); + if (int_code == MWL8K_INT_CODE_CMD_FINISHED) { + iowrite32(0, regs + MWL8K_HIU_INT_CODE); + break; + } + } + cond_resched(); + udelay(1); + } while (--loops); + + pci_unmap_single(priv->pdev, dma_addr, length, PCI_DMA_TODEVICE); + + return loops ? 0 : -ETIMEDOUT; +} + +static int mwl8k_load_fw_image(struct mwl8k_priv *priv, + const u8 *data, size_t length) +{ + struct mwl8k_cmd_pkt *cmd; + int done; + int rc = 0; + + cmd = kmalloc(sizeof(*cmd) + 256, GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD); + cmd->seq_num = 0; + cmd->macid = 0; + cmd->result = 0; + + done = 0; + while (length) { + int block_size = length > 256 ? 256 : length; + + memcpy(cmd->payload, data + done, block_size); + cmd->length = cpu_to_le16(block_size); + + rc = mwl8k_send_fw_load_cmd(priv, cmd, + sizeof(*cmd) + block_size); + if (rc) + break; + + done += block_size; + length -= block_size; + } + + if (!rc) { + cmd->length = 0; + rc = mwl8k_send_fw_load_cmd(priv, cmd, sizeof(*cmd)); + } + + kfree(cmd); + + return rc; +} + +static int mwl8k_feed_fw_image(struct mwl8k_priv *priv, + const u8 *data, size_t length) +{ + unsigned char *buffer; + int may_continue, rc = 0; + u32 done, prev_block_size; + + buffer = kmalloc(1024, GFP_KERNEL); + if (buffer == NULL) + return -ENOMEM; + + done = 0; + prev_block_size = 0; + may_continue = 1000; + while (may_continue > 0) { + u32 block_size; + + block_size = ioread32(priv->regs + MWL8K_HIU_SCRATCH); + if (block_size & 1) { + block_size &= ~1; + may_continue--; + } else { + done += prev_block_size; + length -= prev_block_size; + } + + if (block_size > 1024 || block_size > length) { + rc = -EOVERFLOW; + break; + } + + if (length == 0) { + rc = 0; + break; + } + + if (block_size == 0) { + rc = -EPROTO; + may_continue--; + udelay(1); + continue; + } + + prev_block_size = block_size; + memcpy(buffer, data + done, block_size); + + rc = mwl8k_send_fw_load_cmd(priv, buffer, block_size); + if (rc) + break; + } + + if (!rc && length != 0) + rc = -EREMOTEIO; + + kfree(buffer); + + return rc; +} + +static int mwl8k_load_firmware(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + const struct firmware *fw = priv->fw_ucode; + int rc; + int loops; + + if (!memcmp(fw->data, "\x01\x00\x00\x00", 4) && !priv->is_8764) { + const struct firmware *helper = priv->fw_helper; + + if (helper == NULL) { + printk(KERN_ERR "%s: helper image needed but none " + "given\n", pci_name(priv->pdev)); + return -EINVAL; + } + + rc = mwl8k_load_fw_image(priv, helper->data, helper->size); + if (rc) { + printk(KERN_ERR "%s: unable to load firmware " + "helper image\n", pci_name(priv->pdev)); + return rc; + } + msleep(20); + + rc = mwl8k_feed_fw_image(priv, fw->data, fw->size); + } else { + if (priv->is_8764) + rc = mwl8k_feed_fw_image(priv, fw->data, fw->size); + else + rc = mwl8k_load_fw_image(priv, fw->data, fw->size); + } + + if (rc) { + printk(KERN_ERR "%s: unable to load firmware image\n", + pci_name(priv->pdev)); + return rc; + } + + iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR); + + loops = 500000; + do { + u32 ready_code; + + ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE); + if (ready_code == MWL8K_FWAP_READY) { + priv->ap_fw = true; + break; + } else if (ready_code == MWL8K_FWSTA_READY) { + priv->ap_fw = false; + break; + } + + cond_resched(); + udelay(1); + } while (--loops); + + return loops ? 0 : -ETIMEDOUT; +} + + +/* DMA header used by firmware and hardware. */ +struct mwl8k_dma_data { + __le16 fwlen; + struct ieee80211_hdr wh; + char data[0]; +} __packed; + +/* Routines to add/remove DMA header from skb. */ +static inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos) +{ + struct mwl8k_dma_data *tr; + int hdrlen; + + tr = (struct mwl8k_dma_data *)skb->data; + hdrlen = ieee80211_hdrlen(tr->wh.frame_control); + + if (hdrlen != sizeof(tr->wh)) { + if (ieee80211_is_data_qos(tr->wh.frame_control)) { + memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2); + *((__le16 *)(tr->data - 2)) = qos; + } else { + memmove(tr->data - hdrlen, &tr->wh, hdrlen); + } + } + + if (hdrlen != sizeof(*tr)) + skb_pull(skb, sizeof(*tr) - hdrlen); +} + +#define REDUCED_TX_HEADROOM 8 + +static void +mwl8k_add_dma_header(struct mwl8k_priv *priv, struct sk_buff *skb, + int head_pad, int tail_pad) +{ + struct ieee80211_hdr *wh; + int hdrlen; + int reqd_hdrlen; + struct mwl8k_dma_data *tr; + + /* + * Add a firmware DMA header; the firmware requires that we + * present a 2-byte payload length followed by a 4-address + * header (without QoS field), followed (optionally) by any + * WEP/ExtIV header (but only filled in for CCMP). + */ + wh = (struct ieee80211_hdr *)skb->data; + + hdrlen = ieee80211_hdrlen(wh->frame_control); + + /* + * Check if skb_resize is required because of + * tx_headroom adjustment. + */ + if (priv->ap_fw && (hdrlen < (sizeof(struct ieee80211_cts) + + REDUCED_TX_HEADROOM))) { + if (pskb_expand_head(skb, REDUCED_TX_HEADROOM, 0, GFP_ATOMIC)) { + + wiphy_err(priv->hw->wiphy, + "Failed to reallocate TX buffer\n"); + return; + } + skb->truesize += REDUCED_TX_HEADROOM; + } + + reqd_hdrlen = sizeof(*tr) + head_pad; + + if (hdrlen != reqd_hdrlen) + skb_push(skb, reqd_hdrlen - hdrlen); + + if (ieee80211_is_data_qos(wh->frame_control)) + hdrlen -= IEEE80211_QOS_CTL_LEN; + + tr = (struct mwl8k_dma_data *)skb->data; + if (wh != &tr->wh) + memmove(&tr->wh, wh, hdrlen); + if (hdrlen != sizeof(tr->wh)) + memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen); + + /* + * Firmware length is the length of the fully formed "802.11 + * payload". That is, everything except for the 802.11 header. + * This includes all crypto material including the MIC. + */ + tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr) + tail_pad); +} + +static void mwl8k_encapsulate_tx_frame(struct mwl8k_priv *priv, + struct sk_buff *skb) +{ + struct ieee80211_hdr *wh; + struct ieee80211_tx_info *tx_info; + struct ieee80211_key_conf *key_conf; + int data_pad; + int head_pad = 0; + + wh = (struct ieee80211_hdr *)skb->data; + + tx_info = IEEE80211_SKB_CB(skb); + + key_conf = NULL; + if (ieee80211_is_data(wh->frame_control)) + key_conf = tx_info->control.hw_key; + + /* + * Make sure the packet header is in the DMA header format (4-address + * without QoS), and add head & tail padding when HW crypto is enabled. + * + * We have the following trailer padding requirements: + * - WEP: 4 trailer bytes (ICV) + * - TKIP: 12 trailer bytes (8 MIC + 4 ICV) + * - CCMP: 8 trailer bytes (MIC) + */ + data_pad = 0; + if (key_conf != NULL) { + head_pad = key_conf->iv_len; + switch (key_conf->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + data_pad = 4; + break; + case WLAN_CIPHER_SUITE_TKIP: + data_pad = 12; + break; + case WLAN_CIPHER_SUITE_CCMP: + data_pad = 8; + break; + } + } + mwl8k_add_dma_header(priv, skb, head_pad, data_pad); +} + +/* + * Packet reception for 88w8366/88w8764 AP firmware. + */ +struct mwl8k_rxd_ap { + __le16 pkt_len; + __u8 sq2; + __u8 rate; + __le32 pkt_phys_addr; + __le32 next_rxd_phys_addr; + __le16 qos_control; + __le16 htsig2; + __le32 hw_rssi_info; + __le32 hw_noise_floor_info; + __u8 noise_floor; + __u8 pad0[3]; + __u8 rssi; + __u8 rx_status; + __u8 channel; + __u8 rx_ctrl; +} __packed; + +#define MWL8K_AP_RATE_INFO_MCS_FORMAT 0x80 +#define MWL8K_AP_RATE_INFO_40MHZ 0x40 +#define MWL8K_AP_RATE_INFO_RATEID(x) ((x) & 0x3f) + +#define MWL8K_AP_RX_CTRL_OWNED_BY_HOST 0x80 + +/* 8366/8764 AP rx_status bits */ +#define MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK 0x80 +#define MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR 0xFF +#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR 0x02 +#define MWL8K_AP_RXSTAT_WEP_DECRYPT_ICV_ERR 0x04 +#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR 0x08 + +static void mwl8k_rxd_ap_init(void *_rxd, dma_addr_t next_dma_addr) +{ + struct mwl8k_rxd_ap *rxd = _rxd; + + rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); + rxd->rx_ctrl = MWL8K_AP_RX_CTRL_OWNED_BY_HOST; +} + +static void mwl8k_rxd_ap_refill(void *_rxd, dma_addr_t addr, int len) +{ + struct mwl8k_rxd_ap *rxd = _rxd; + + rxd->pkt_len = cpu_to_le16(len); + rxd->pkt_phys_addr = cpu_to_le32(addr); + wmb(); + rxd->rx_ctrl = 0; +} + +static int +mwl8k_rxd_ap_process(void *_rxd, struct ieee80211_rx_status *status, + __le16 *qos, s8 *noise) +{ + struct mwl8k_rxd_ap *rxd = _rxd; + + if (!(rxd->rx_ctrl & MWL8K_AP_RX_CTRL_OWNED_BY_HOST)) + return -1; + rmb(); + + memset(status, 0, sizeof(*status)); + + status->signal = -rxd->rssi; + *noise = -rxd->noise_floor; + + if (rxd->rate & MWL8K_AP_RATE_INFO_MCS_FORMAT) { + status->flag |= RX_FLAG_HT; + if (rxd->rate & MWL8K_AP_RATE_INFO_40MHZ) + status->flag |= RX_FLAG_40MHZ; + status->rate_idx = MWL8K_AP_RATE_INFO_RATEID(rxd->rate); + } else { + int i; + + for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) { + if (mwl8k_rates_24[i].hw_value == rxd->rate) { + status->rate_idx = i; + break; + } + } + } + + if (rxd->channel > 14) { + status->band = IEEE80211_BAND_5GHZ; + if (!(status->flag & RX_FLAG_HT)) + status->rate_idx -= 5; + } else { + status->band = IEEE80211_BAND_2GHZ; + } + status->freq = ieee80211_channel_to_frequency(rxd->channel, + status->band); + + *qos = rxd->qos_control; + + if ((rxd->rx_status != MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR) && + (rxd->rx_status & MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK) && + (rxd->rx_status & MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR)) + status->flag |= RX_FLAG_MMIC_ERROR; + + return le16_to_cpu(rxd->pkt_len); +} + +static struct rxd_ops rxd_ap_ops = { + .rxd_size = sizeof(struct mwl8k_rxd_ap), + .rxd_init = mwl8k_rxd_ap_init, + .rxd_refill = mwl8k_rxd_ap_refill, + .rxd_process = mwl8k_rxd_ap_process, +}; + +/* + * Packet reception for STA firmware. + */ +struct mwl8k_rxd_sta { + __le16 pkt_len; + __u8 link_quality; + __u8 noise_level; + __le32 pkt_phys_addr; + __le32 next_rxd_phys_addr; + __le16 qos_control; + __le16 rate_info; + __le32 pad0[4]; + __u8 rssi; + __u8 channel; + __le16 pad1; + __u8 rx_ctrl; + __u8 rx_status; + __u8 pad2[2]; +} __packed; + +#define MWL8K_STA_RATE_INFO_SHORTPRE 0x8000 +#define MWL8K_STA_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3) +#define MWL8K_STA_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f) +#define MWL8K_STA_RATE_INFO_40MHZ 0x0004 +#define MWL8K_STA_RATE_INFO_SHORTGI 0x0002 +#define MWL8K_STA_RATE_INFO_MCS_FORMAT 0x0001 + +#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST 0x02 +#define MWL8K_STA_RX_CTRL_DECRYPT_ERROR 0x04 +/* ICV=0 or MIC=1 */ +#define MWL8K_STA_RX_CTRL_DEC_ERR_TYPE 0x08 +/* Key is uploaded only in failure case */ +#define MWL8K_STA_RX_CTRL_KEY_INDEX 0x30 + +static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr) +{ + struct mwl8k_rxd_sta *rxd = _rxd; + + rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); + rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST; +} + +static void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len) +{ + struct mwl8k_rxd_sta *rxd = _rxd; + + rxd->pkt_len = cpu_to_le16(len); + rxd->pkt_phys_addr = cpu_to_le32(addr); + wmb(); + rxd->rx_ctrl = 0; +} + +static int +mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status, + __le16 *qos, s8 *noise) +{ + struct mwl8k_rxd_sta *rxd = _rxd; + u16 rate_info; + + if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST)) + return -1; + rmb(); + + rate_info = le16_to_cpu(rxd->rate_info); + + memset(status, 0, sizeof(*status)); + + status->signal = -rxd->rssi; + *noise = -rxd->noise_level; + status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info); + status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info); + + if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE) + status->flag |= RX_FLAG_SHORTPRE; + if (rate_info & MWL8K_STA_RATE_INFO_40MHZ) + status->flag |= RX_FLAG_40MHZ; + if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI) + status->flag |= RX_FLAG_SHORT_GI; + if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT) + status->flag |= RX_FLAG_HT; + + if (rxd->channel > 14) { + status->band = IEEE80211_BAND_5GHZ; + if (!(status->flag & RX_FLAG_HT)) + status->rate_idx -= 5; + } else { + status->band = IEEE80211_BAND_2GHZ; + } + status->freq = ieee80211_channel_to_frequency(rxd->channel, + status->band); + + *qos = rxd->qos_control; + if ((rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DECRYPT_ERROR) && + (rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DEC_ERR_TYPE)) + status->flag |= RX_FLAG_MMIC_ERROR; + + return le16_to_cpu(rxd->pkt_len); +} + +static struct rxd_ops rxd_sta_ops = { + .rxd_size = sizeof(struct mwl8k_rxd_sta), + .rxd_init = mwl8k_rxd_sta_init, + .rxd_refill = mwl8k_rxd_sta_refill, + .rxd_process = mwl8k_rxd_sta_process, +}; + + +#define MWL8K_RX_DESCS 256 +#define MWL8K_RX_MAXSZ 3800 + +static int mwl8k_rxq_init(struct ieee80211_hw *hw, int index) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_rx_queue *rxq = priv->rxq + index; + int size; + int i; + + rxq->rxd_count = 0; + rxq->head = 0; + rxq->tail = 0; + + size = MWL8K_RX_DESCS * priv->rxd_ops->rxd_size; + + rxq->rxd = pci_zalloc_consistent(priv->pdev, size, &rxq->rxd_dma); + if (rxq->rxd == NULL) { + wiphy_err(hw->wiphy, "failed to alloc RX descriptors\n"); + return -ENOMEM; + } + + rxq->buf = kcalloc(MWL8K_RX_DESCS, sizeof(*rxq->buf), GFP_KERNEL); + if (rxq->buf == NULL) { + pci_free_consistent(priv->pdev, size, rxq->rxd, rxq->rxd_dma); + return -ENOMEM; + } + + for (i = 0; i < MWL8K_RX_DESCS; i++) { + int desc_size; + void *rxd; + int nexti; + dma_addr_t next_dma_addr; + + desc_size = priv->rxd_ops->rxd_size; + rxd = rxq->rxd + (i * priv->rxd_ops->rxd_size); + + nexti = i + 1; + if (nexti == MWL8K_RX_DESCS) + nexti = 0; + next_dma_addr = rxq->rxd_dma + (nexti * desc_size); + + priv->rxd_ops->rxd_init(rxd, next_dma_addr); + } + + return 0; +} + +static int rxq_refill(struct ieee80211_hw *hw, int index, int limit) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_rx_queue *rxq = priv->rxq + index; + int refilled; + + refilled = 0; + while (rxq->rxd_count < MWL8K_RX_DESCS && limit--) { + struct sk_buff *skb; + dma_addr_t addr; + int rx; + void *rxd; + + skb = dev_alloc_skb(MWL8K_RX_MAXSZ); + if (skb == NULL) + break; + + addr = pci_map_single(priv->pdev, skb->data, + MWL8K_RX_MAXSZ, DMA_FROM_DEVICE); + + rxq->rxd_count++; + rx = rxq->tail++; + if (rxq->tail == MWL8K_RX_DESCS) + rxq->tail = 0; + rxq->buf[rx].skb = skb; + dma_unmap_addr_set(&rxq->buf[rx], dma, addr); + + rxd = rxq->rxd + (rx * priv->rxd_ops->rxd_size); + priv->rxd_ops->rxd_refill(rxd, addr, MWL8K_RX_MAXSZ); + + refilled++; + } + + return refilled; +} + +/* Must be called only when the card's reception is completely halted */ +static void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_rx_queue *rxq = priv->rxq + index; + int i; + + if (rxq->rxd == NULL) + return; + + for (i = 0; i < MWL8K_RX_DESCS; i++) { + if (rxq->buf[i].skb != NULL) { + pci_unmap_single(priv->pdev, + dma_unmap_addr(&rxq->buf[i], dma), + MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE); + dma_unmap_addr_set(&rxq->buf[i], dma, 0); + + kfree_skb(rxq->buf[i].skb); + rxq->buf[i].skb = NULL; + } + } + + kfree(rxq->buf); + rxq->buf = NULL; + + pci_free_consistent(priv->pdev, + MWL8K_RX_DESCS * priv->rxd_ops->rxd_size, + rxq->rxd, rxq->rxd_dma); + rxq->rxd = NULL; +} + + +/* + * Scan a list of BSSIDs to process for finalize join. + * Allows for extension to process multiple BSSIDs. + */ +static inline int +mwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh) +{ + return priv->capture_beacon && + ieee80211_is_beacon(wh->frame_control) && + ether_addr_equal_64bits(wh->addr3, priv->capture_bssid); +} + +static inline void mwl8k_save_beacon(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + struct mwl8k_priv *priv = hw->priv; + + priv->capture_beacon = false; + eth_zero_addr(priv->capture_bssid); + + /* + * Use GFP_ATOMIC as rxq_process is called from + * the primary interrupt handler, memory allocation call + * must not sleep. + */ + priv->beacon_skb = skb_copy(skb, GFP_ATOMIC); + if (priv->beacon_skb != NULL) + ieee80211_queue_work(hw, &priv->finalize_join_worker); +} + +static inline struct mwl8k_vif *mwl8k_find_vif_bss(struct list_head *vif_list, + u8 *bssid) +{ + struct mwl8k_vif *mwl8k_vif; + + list_for_each_entry(mwl8k_vif, + vif_list, list) { + if (memcmp(bssid, mwl8k_vif->bssid, + ETH_ALEN) == 0) + return mwl8k_vif; + } + + return NULL; +} + +static int rxq_process(struct ieee80211_hw *hw, int index, int limit) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif = NULL; + struct mwl8k_rx_queue *rxq = priv->rxq + index; + int processed; + + processed = 0; + while (rxq->rxd_count && limit--) { + struct sk_buff *skb; + void *rxd; + int pkt_len; + struct ieee80211_rx_status status; + struct ieee80211_hdr *wh; + __le16 qos; + + skb = rxq->buf[rxq->head].skb; + if (skb == NULL) + break; + + rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size); + + pkt_len = priv->rxd_ops->rxd_process(rxd, &status, &qos, + &priv->noise); + if (pkt_len < 0) + break; + + rxq->buf[rxq->head].skb = NULL; + + pci_unmap_single(priv->pdev, + dma_unmap_addr(&rxq->buf[rxq->head], dma), + MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE); + dma_unmap_addr_set(&rxq->buf[rxq->head], dma, 0); + + rxq->head++; + if (rxq->head == MWL8K_RX_DESCS) + rxq->head = 0; + + rxq->rxd_count--; + + wh = &((struct mwl8k_dma_data *)skb->data)->wh; + + /* + * Check for a pending join operation. Save a + * copy of the beacon and schedule a tasklet to + * send a FINALIZE_JOIN command to the firmware. + */ + if (mwl8k_capture_bssid(priv, (void *)skb->data)) + mwl8k_save_beacon(hw, skb); + + if (ieee80211_has_protected(wh->frame_control)) { + + /* Check if hw crypto has been enabled for + * this bss. If yes, set the status flags + * accordingly + */ + mwl8k_vif = mwl8k_find_vif_bss(&priv->vif_list, + wh->addr1); + + if (mwl8k_vif != NULL && + mwl8k_vif->is_hw_crypto_enabled) { + /* + * When MMIC ERROR is encountered + * by the firmware, payload is + * dropped and only 32 bytes of + * mwl8k Firmware header is sent + * to the host. + * + * We need to add four bytes of + * key information. In it + * MAC80211 expects keyidx set to + * 0 for triggering Counter + * Measure of MMIC failure. + */ + if (status.flag & RX_FLAG_MMIC_ERROR) { + struct mwl8k_dma_data *tr; + tr = (struct mwl8k_dma_data *)skb->data; + memset((void *)&(tr->data), 0, 4); + pkt_len += 4; + } + + if (!ieee80211_is_auth(wh->frame_control)) + status.flag |= RX_FLAG_IV_STRIPPED | + RX_FLAG_DECRYPTED | + RX_FLAG_MMIC_STRIPPED; + } + } + + skb_put(skb, pkt_len); + mwl8k_remove_dma_header(skb, qos); + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + ieee80211_rx_irqsafe(hw, skb); + + processed++; + } + + return processed; +} + + +/* + * Packet transmission. + */ + +#define MWL8K_TXD_STATUS_OK 0x00000001 +#define MWL8K_TXD_STATUS_OK_RETRY 0x00000002 +#define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004 +#define MWL8K_TXD_STATUS_MULTICAST_TX 0x00000008 +#define MWL8K_TXD_STATUS_FW_OWNED 0x80000000 + +#define MWL8K_QOS_QLEN_UNSPEC 0xff00 +#define MWL8K_QOS_ACK_POLICY_MASK 0x0060 +#define MWL8K_QOS_ACK_POLICY_NORMAL 0x0000 +#define MWL8K_QOS_ACK_POLICY_BLOCKACK 0x0060 +#define MWL8K_QOS_EOSP 0x0010 + +struct mwl8k_tx_desc { + __le32 status; + __u8 data_rate; + __u8 tx_priority; + __le16 qos_control; + __le32 pkt_phys_addr; + __le16 pkt_len; + __u8 dest_MAC_addr[ETH_ALEN]; + __le32 next_txd_phys_addr; + __le32 timestamp; + __le16 rate_info; + __u8 peer_id; + __u8 tx_frag_cnt; +} __packed; + +#define MWL8K_TX_DESCS 128 + +static int mwl8k_txq_init(struct ieee80211_hw *hw, int index) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_tx_queue *txq = priv->txq + index; + int size; + int i; + + txq->len = 0; + txq->head = 0; + txq->tail = 0; + + size = MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc); + + txq->txd = pci_zalloc_consistent(priv->pdev, size, &txq->txd_dma); + if (txq->txd == NULL) { + wiphy_err(hw->wiphy, "failed to alloc TX descriptors\n"); + return -ENOMEM; + } + + txq->skb = kcalloc(MWL8K_TX_DESCS, sizeof(*txq->skb), GFP_KERNEL); + if (txq->skb == NULL) { + pci_free_consistent(priv->pdev, size, txq->txd, txq->txd_dma); + return -ENOMEM; + } + + for (i = 0; i < MWL8K_TX_DESCS; i++) { + struct mwl8k_tx_desc *tx_desc; + int nexti; + + tx_desc = txq->txd + i; + nexti = (i + 1) % MWL8K_TX_DESCS; + + tx_desc->status = 0; + tx_desc->next_txd_phys_addr = + cpu_to_le32(txq->txd_dma + nexti * sizeof(*tx_desc)); + } + + return 0; +} + +static inline void mwl8k_tx_start(struct mwl8k_priv *priv) +{ + iowrite32(MWL8K_H2A_INT_PPA_READY, + priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); + iowrite32(MWL8K_H2A_INT_DUMMY, + priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); + ioread32(priv->regs + MWL8K_HIU_INT_CODE); +} + +static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + int i; + + for (i = 0; i < mwl8k_tx_queues(priv); i++) { + struct mwl8k_tx_queue *txq = priv->txq + i; + int fw_owned = 0; + int drv_owned = 0; + int unused = 0; + int desc; + + for (desc = 0; desc < MWL8K_TX_DESCS; desc++) { + struct mwl8k_tx_desc *tx_desc = txq->txd + desc; + u32 status; + + status = le32_to_cpu(tx_desc->status); + if (status & MWL8K_TXD_STATUS_FW_OWNED) + fw_owned++; + else + drv_owned++; + + if (tx_desc->pkt_len == 0) + unused++; + } + + wiphy_err(hw->wiphy, + "txq[%d] len=%d head=%d tail=%d " + "fw_owned=%d drv_owned=%d unused=%d\n", + i, + txq->len, txq->head, txq->tail, + fw_owned, drv_owned, unused); + } +} + +/* + * Must be called with priv->fw_mutex held and tx queues stopped. + */ +#define MWL8K_TX_WAIT_TIMEOUT_MS 5000 + +static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + DECLARE_COMPLETION_ONSTACK(tx_wait); + int retry; + int rc; + + might_sleep(); + + /* Since fw restart is in progress, allow only the firmware + * commands from the restart code and block the other + * commands since they are going to fail in any case since + * the firmware has crashed + */ + if (priv->hw_restart_in_progress) { + if (priv->hw_restart_owner == current) + return 0; + else + return -EBUSY; + } + + if (atomic_read(&priv->watchdog_event_pending)) + return 0; + + /* + * The TX queues are stopped at this point, so this test + * doesn't need to take ->tx_lock. + */ + if (!priv->pending_tx_pkts) + return 0; + + retry = 1; + rc = 0; + + spin_lock_bh(&priv->tx_lock); + priv->tx_wait = &tx_wait; + while (!rc) { + int oldcount; + unsigned long timeout; + + oldcount = priv->pending_tx_pkts; + + spin_unlock_bh(&priv->tx_lock); + timeout = wait_for_completion_timeout(&tx_wait, + msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS)); + + if (atomic_read(&priv->watchdog_event_pending)) { + spin_lock_bh(&priv->tx_lock); + priv->tx_wait = NULL; + spin_unlock_bh(&priv->tx_lock); + return 0; + } + + spin_lock_bh(&priv->tx_lock); + + if (timeout || !priv->pending_tx_pkts) { + WARN_ON(priv->pending_tx_pkts); + if (retry) + wiphy_notice(hw->wiphy, "tx rings drained\n"); + break; + } + + if (retry) { + mwl8k_tx_start(priv); + retry = 0; + continue; + } + + if (priv->pending_tx_pkts < oldcount) { + wiphy_notice(hw->wiphy, + "waiting for tx rings to drain (%d -> %d pkts)\n", + oldcount, priv->pending_tx_pkts); + retry = 1; + continue; + } + + priv->tx_wait = NULL; + + wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n", + MWL8K_TX_WAIT_TIMEOUT_MS); + mwl8k_dump_tx_rings(hw); + priv->hw_restart_in_progress = true; + ieee80211_queue_work(hw, &priv->fw_reload); + + rc = -ETIMEDOUT; + } + priv->tx_wait = NULL; + spin_unlock_bh(&priv->tx_lock); + + return rc; +} + +#define MWL8K_TXD_SUCCESS(status) \ + ((status) & (MWL8K_TXD_STATUS_OK | \ + MWL8K_TXD_STATUS_OK_RETRY | \ + MWL8K_TXD_STATUS_OK_MORE_RETRY)) + +static int mwl8k_tid_queue_mapping(u8 tid) +{ + BUG_ON(tid > 7); + + switch (tid) { + case 0: + case 3: + return IEEE80211_AC_BE; + case 1: + case 2: + return IEEE80211_AC_BK; + case 4: + case 5: + return IEEE80211_AC_VI; + case 6: + case 7: + return IEEE80211_AC_VO; + default: + return -1; + } +} + +/* The firmware will fill in the rate information + * for each packet that gets queued in the hardware + * and these macros will interpret that info. + */ + +#define RI_FORMAT(a) (a & 0x0001) +#define RI_RATE_ID_MCS(a) ((a & 0x01f8) >> 3) + +static int +mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_tx_queue *txq = priv->txq + index; + int processed; + + processed = 0; + while (txq->len > 0 && limit--) { + int tx; + struct mwl8k_tx_desc *tx_desc; + unsigned long addr; + int size; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + u32 status; + struct ieee80211_sta *sta; + struct mwl8k_sta *sta_info = NULL; + u16 rate_info; + struct ieee80211_hdr *wh; + + tx = txq->head; + tx_desc = txq->txd + tx; + + status = le32_to_cpu(tx_desc->status); + + if (status & MWL8K_TXD_STATUS_FW_OWNED) { + if (!force) + break; + tx_desc->status &= + ~cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED); + } + + txq->head = (tx + 1) % MWL8K_TX_DESCS; + BUG_ON(txq->len == 0); + txq->len--; + priv->pending_tx_pkts--; + + addr = le32_to_cpu(tx_desc->pkt_phys_addr); + size = le16_to_cpu(tx_desc->pkt_len); + skb = txq->skb[tx]; + txq->skb[tx] = NULL; + + BUG_ON(skb == NULL); + pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE); + + mwl8k_remove_dma_header(skb, tx_desc->qos_control); + + wh = (struct ieee80211_hdr *) skb->data; + + /* Mark descriptor as unused */ + tx_desc->pkt_phys_addr = 0; + tx_desc->pkt_len = 0; + + info = IEEE80211_SKB_CB(skb); + if (ieee80211_is_data(wh->frame_control)) { + rcu_read_lock(); + sta = ieee80211_find_sta_by_ifaddr(hw, wh->addr1, + wh->addr2); + if (sta) { + sta_info = MWL8K_STA(sta); + BUG_ON(sta_info == NULL); + rate_info = le16_to_cpu(tx_desc->rate_info); + /* If rate is < 6.5 Mpbs for an ht station + * do not form an ampdu. If the station is a + * legacy station (format = 0), do not form an + * ampdu + */ + if (RI_RATE_ID_MCS(rate_info) < 1 || + RI_FORMAT(rate_info) == 0) { + sta_info->is_ampdu_allowed = false; + } else { + sta_info->is_ampdu_allowed = true; + } + } + rcu_read_unlock(); + } + + ieee80211_tx_info_clear_status(info); + + /* Rate control is happening in the firmware. + * Ensure no tx rate is being reported. + */ + info->status.rates[0].idx = -1; + info->status.rates[0].count = 1; + + if (MWL8K_TXD_SUCCESS(status)) + info->flags |= IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_irqsafe(hw, skb); + + processed++; + } + + return processed; +} + +/* must be called only when the card's transmit is completely halted */ +static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_tx_queue *txq = priv->txq + index; + + if (txq->txd == NULL) + return; + + mwl8k_txq_reclaim(hw, index, INT_MAX, 1); + + kfree(txq->skb); + txq->skb = NULL; + + pci_free_consistent(priv->pdev, + MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc), + txq->txd, txq->txd_dma); + txq->txd = NULL; +} + +/* caller must hold priv->stream_lock when calling the stream functions */ +static struct mwl8k_ampdu_stream * +mwl8k_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid) +{ + struct mwl8k_ampdu_stream *stream; + struct mwl8k_priv *priv = hw->priv; + int i; + + for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { + stream = &priv->ampdu[i]; + if (stream->state == AMPDU_NO_STREAM) { + stream->sta = sta; + stream->state = AMPDU_STREAM_NEW; + stream->tid = tid; + stream->idx = i; + wiphy_debug(hw->wiphy, "Added a new stream for %pM %d", + sta->addr, tid); + return stream; + } + } + return NULL; +} + +static int +mwl8k_start_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) +{ + int ret; + + /* if the stream has already been started, don't start it again */ + if (stream->state != AMPDU_STREAM_NEW) + return 0; + ret = ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0); + if (ret) + wiphy_debug(hw->wiphy, "Failed to start stream for %pM %d: " + "%d\n", stream->sta->addr, stream->tid, ret); + else + wiphy_debug(hw->wiphy, "Started stream for %pM %d\n", + stream->sta->addr, stream->tid); + return ret; +} + +static void +mwl8k_remove_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) +{ + wiphy_debug(hw->wiphy, "Remove stream for %pM %d\n", stream->sta->addr, + stream->tid); + memset(stream, 0, sizeof(*stream)); +} + +static struct mwl8k_ampdu_stream * +mwl8k_lookup_stream(struct ieee80211_hw *hw, u8 *addr, u8 tid) +{ + struct mwl8k_priv *priv = hw->priv; + int i; + + for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { + struct mwl8k_ampdu_stream *stream; + stream = &priv->ampdu[i]; + if (stream->state == AMPDU_NO_STREAM) + continue; + if (!memcmp(stream->sta->addr, addr, ETH_ALEN) && + stream->tid == tid) + return stream; + } + return NULL; +} + +#define MWL8K_AMPDU_PACKET_THRESHOLD 64 +static inline bool mwl8k_ampdu_allowed(struct ieee80211_sta *sta, u8 tid) +{ + struct mwl8k_sta *sta_info = MWL8K_STA(sta); + struct tx_traffic_info *tx_stats; + + BUG_ON(tid >= MWL8K_MAX_TID); + tx_stats = &sta_info->tx_stats[tid]; + + return sta_info->is_ampdu_allowed && + tx_stats->pkts > MWL8K_AMPDU_PACKET_THRESHOLD; +} + +static inline void mwl8k_tx_count_packet(struct ieee80211_sta *sta, u8 tid) +{ + struct mwl8k_sta *sta_info = MWL8K_STA(sta); + struct tx_traffic_info *tx_stats; + + BUG_ON(tid >= MWL8K_MAX_TID); + tx_stats = &sta_info->tx_stats[tid]; + + if (tx_stats->start_time == 0) + tx_stats->start_time = jiffies; + + /* reset the packet count after each second elapses. If the number of + * packets ever exceeds the ampdu_min_traffic threshold, we will allow + * an ampdu stream to be started. + */ + if (jiffies - tx_stats->start_time > HZ) { + tx_stats->pkts = 0; + tx_stats->start_time = 0; + } else + tx_stats->pkts++; +} + +/* The hardware ampdu queues start from 5. + * txpriorities for ampdu queues are + * 5 6 7 0 1 2 3 4 ie., queue 5 is highest + * and queue 3 is lowest (queue 4 is reserved) + */ +#define BA_QUEUE 5 + +static void +mwl8k_txq_xmit(struct ieee80211_hw *hw, + int index, + struct ieee80211_sta *sta, + struct sk_buff *skb) +{ + struct mwl8k_priv *priv = hw->priv; + struct ieee80211_tx_info *tx_info; + struct mwl8k_vif *mwl8k_vif; + struct ieee80211_hdr *wh; + struct mwl8k_tx_queue *txq; + struct mwl8k_tx_desc *tx; + dma_addr_t dma; + u32 txstatus; + u8 txdatarate; + u16 qos; + int txpriority; + u8 tid = 0; + struct mwl8k_ampdu_stream *stream = NULL; + bool start_ba_session = false; + bool mgmtframe = false; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + bool eapol_frame = false; + + wh = (struct ieee80211_hdr *)skb->data; + if (ieee80211_is_data_qos(wh->frame_control)) + qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); + else + qos = 0; + + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) + eapol_frame = true; + + if (ieee80211_is_mgmt(wh->frame_control)) + mgmtframe = true; + + if (priv->ap_fw) + mwl8k_encapsulate_tx_frame(priv, skb); + else + mwl8k_add_dma_header(priv, skb, 0, 0); + + wh = &((struct mwl8k_dma_data *)skb->data)->wh; + + tx_info = IEEE80211_SKB_CB(skb); + mwl8k_vif = MWL8K_VIF(tx_info->control.vif); + + if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno); + mwl8k_vif->seqno += 0x10; + } + + /* Setup firmware control bit fields for each frame type. */ + txstatus = 0; + txdatarate = 0; + if (ieee80211_is_mgmt(wh->frame_control) || + ieee80211_is_ctl(wh->frame_control)) { + txdatarate = 0; + qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP; + } else if (ieee80211_is_data(wh->frame_control)) { + txdatarate = 1; + if (is_multicast_ether_addr(wh->addr1)) + txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX; + + qos &= ~MWL8K_QOS_ACK_POLICY_MASK; + if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) + qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK; + else + qos |= MWL8K_QOS_ACK_POLICY_NORMAL; + } + + /* Queue ADDBA request in the respective data queue. While setting up + * the ampdu stream, mac80211 queues further packets for that + * particular ra/tid pair. However, packets piled up in the hardware + * for that ra/tid pair will still go out. ADDBA request and the + * related data packets going out from different queues asynchronously + * will cause a shift in the receiver window which might result in + * ampdu packets getting dropped at the receiver after the stream has + * been setup. + */ + if (unlikely(ieee80211_is_action(wh->frame_control) && + mgmt->u.action.category == WLAN_CATEGORY_BACK && + mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ && + priv->ap_fw)) { + u16 capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); + tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; + index = mwl8k_tid_queue_mapping(tid); + } + + txpriority = index; + + if (priv->ap_fw && sta && sta->ht_cap.ht_supported && !eapol_frame && + ieee80211_is_data_qos(wh->frame_control)) { + tid = qos & 0xf; + mwl8k_tx_count_packet(sta, tid); + spin_lock(&priv->stream_lock); + stream = mwl8k_lookup_stream(hw, sta->addr, tid); + if (stream != NULL) { + if (stream->state == AMPDU_STREAM_ACTIVE) { + WARN_ON(!(qos & MWL8K_QOS_ACK_POLICY_BLOCKACK)); + txpriority = (BA_QUEUE + stream->idx) % + TOTAL_HW_TX_QUEUES; + if (stream->idx <= 1) + index = stream->idx + + MWL8K_TX_WMM_QUEUES; + + } else if (stream->state == AMPDU_STREAM_NEW) { + /* We get here if the driver sends us packets + * after we've initiated a stream, but before + * our ampdu_action routine has been called + * with IEEE80211_AMPDU_TX_START to get the SSN + * for the ADDBA request. So this packet can + * go out with no risk of sequence number + * mismatch. No special handling is required. + */ + } else { + /* Drop packets that would go out after the + * ADDBA request was sent but before the ADDBA + * response is received. If we don't do this, + * the recipient would probably receive it + * after the ADDBA request with SSN 0. This + * will cause the recipient's BA receive window + * to shift, which would cause the subsequent + * packets in the BA stream to be discarded. + * mac80211 queues our packets for us in this + * case, so this is really just a safety check. + */ + wiphy_warn(hw->wiphy, + "Cannot send packet while ADDBA " + "dialog is underway.\n"); + spin_unlock(&priv->stream_lock); + dev_kfree_skb(skb); + return; + } + } else { + /* Defer calling mwl8k_start_stream so that the current + * skb can go out before the ADDBA request. This + * prevents sequence number mismatch at the recepient + * as described above. + */ + if (mwl8k_ampdu_allowed(sta, tid)) { + stream = mwl8k_add_stream(hw, sta, tid); + if (stream != NULL) + start_ba_session = true; + } + } + spin_unlock(&priv->stream_lock); + } else { + qos &= ~MWL8K_QOS_ACK_POLICY_MASK; + qos |= MWL8K_QOS_ACK_POLICY_NORMAL; + } + + dma = pci_map_single(priv->pdev, skb->data, + skb->len, PCI_DMA_TODEVICE); + + if (pci_dma_mapping_error(priv->pdev, dma)) { + wiphy_debug(hw->wiphy, + "failed to dma map skb, dropping TX frame.\n"); + if (start_ba_session) { + spin_lock(&priv->stream_lock); + mwl8k_remove_stream(hw, stream); + spin_unlock(&priv->stream_lock); + } + dev_kfree_skb(skb); + return; + } + + spin_lock_bh(&priv->tx_lock); + + txq = priv->txq + index; + + /* Mgmt frames that go out frequently are probe + * responses. Other mgmt frames got out relatively + * infrequently. Hence reserve 2 buffers so that + * other mgmt frames do not get dropped due to an + * already queued probe response in one of the + * reserved buffers. + */ + + if (txq->len >= MWL8K_TX_DESCS - 2) { + if (!mgmtframe || txq->len == MWL8K_TX_DESCS) { + if (start_ba_session) { + spin_lock(&priv->stream_lock); + mwl8k_remove_stream(hw, stream); + spin_unlock(&priv->stream_lock); + } + mwl8k_tx_start(priv); + spin_unlock_bh(&priv->tx_lock); + pci_unmap_single(priv->pdev, dma, skb->len, + PCI_DMA_TODEVICE); + dev_kfree_skb(skb); + return; + } + } + + BUG_ON(txq->skb[txq->tail] != NULL); + txq->skb[txq->tail] = skb; + + tx = txq->txd + txq->tail; + tx->data_rate = txdatarate; + tx->tx_priority = txpriority; + tx->qos_control = cpu_to_le16(qos); + tx->pkt_phys_addr = cpu_to_le32(dma); + tx->pkt_len = cpu_to_le16(skb->len); + tx->rate_info = 0; + if (!priv->ap_fw && sta != NULL) + tx->peer_id = MWL8K_STA(sta)->peer_id; + else + tx->peer_id = 0; + + if (priv->ap_fw && ieee80211_is_data(wh->frame_control) && !eapol_frame) + tx->timestamp = cpu_to_le32(ioread32(priv->regs + + MWL8K_HW_TIMER_REGISTER)); + else + tx->timestamp = 0; + + wmb(); + tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); + + txq->len++; + priv->pending_tx_pkts++; + + txq->tail++; + if (txq->tail == MWL8K_TX_DESCS) + txq->tail = 0; + + mwl8k_tx_start(priv); + + spin_unlock_bh(&priv->tx_lock); + + /* Initiate the ampdu session here */ + if (start_ba_session) { + spin_lock(&priv->stream_lock); + if (mwl8k_start_stream(hw, stream)) + mwl8k_remove_stream(hw, stream); + spin_unlock(&priv->stream_lock); + } +} + + +/* + * Firmware access. + * + * We have the following requirements for issuing firmware commands: + * - Some commands require that the packet transmit path is idle when + * the command is issued. (For simplicity, we'll just quiesce the + * transmit path for every command.) + * - There are certain sequences of commands that need to be issued to + * the hardware sequentially, with no other intervening commands. + * + * This leads to an implementation of a "firmware lock" as a mutex that + * can be taken recursively, and which is taken by both the low-level + * command submission function (mwl8k_post_cmd) as well as any users of + * that function that require issuing of an atomic sequence of commands, + * and quiesces the transmit path whenever it's taken. + */ +static int mwl8k_fw_lock(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + if (priv->fw_mutex_owner != current) { + int rc; + + mutex_lock(&priv->fw_mutex); + ieee80211_stop_queues(hw); + + rc = mwl8k_tx_wait_empty(hw); + if (rc) { + if (!priv->hw_restart_in_progress) + ieee80211_wake_queues(hw); + + mutex_unlock(&priv->fw_mutex); + + return rc; + } + + priv->fw_mutex_owner = current; + } + + priv->fw_mutex_depth++; + + return 0; +} + +static void mwl8k_fw_unlock(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + if (!--priv->fw_mutex_depth) { + if (!priv->hw_restart_in_progress) + ieee80211_wake_queues(hw); + + priv->fw_mutex_owner = NULL; + mutex_unlock(&priv->fw_mutex); + } +} + +static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, + u32 bitmap); + +/* + * Command processing. + */ + +/* Timeout firmware commands after 10s */ +#define MWL8K_CMD_TIMEOUT_MS 10000 + +static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) +{ + DECLARE_COMPLETION_ONSTACK(cmd_wait); + struct mwl8k_priv *priv = hw->priv; + void __iomem *regs = priv->regs; + dma_addr_t dma_addr; + unsigned int dma_size; + int rc; + unsigned long timeout = 0; + u8 buf[32]; + u32 bitmap = 0; + + wiphy_dbg(hw->wiphy, "Posting %s [%d]\n", + mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), cmd->macid); + + /* Before posting firmware commands that could change the hardware + * characteristics, make sure that all BSSes are stopped temporary. + * Enable these stopped BSSes after completion of the commands + */ + + rc = mwl8k_fw_lock(hw); + if (rc) + return rc; + + if (priv->ap_fw && priv->running_bsses) { + switch (le16_to_cpu(cmd->code)) { + case MWL8K_CMD_SET_RF_CHANNEL: + case MWL8K_CMD_RADIO_CONTROL: + case MWL8K_CMD_RF_TX_POWER: + case MWL8K_CMD_TX_POWER: + case MWL8K_CMD_RF_ANTENNA: + case MWL8K_CMD_RTS_THRESHOLD: + case MWL8K_CMD_MIMO_CONFIG: + bitmap = priv->running_bsses; + mwl8k_enable_bsses(hw, false, bitmap); + break; + } + } + + cmd->result = (__force __le16) 0xffff; + dma_size = le16_to_cpu(cmd->length); + dma_addr = pci_map_single(priv->pdev, cmd, dma_size, + PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(priv->pdev, dma_addr)) + return -ENOMEM; + + priv->hostcmd_wait = &cmd_wait; + iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); + iowrite32(MWL8K_H2A_INT_DOORBELL, + regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); + iowrite32(MWL8K_H2A_INT_DUMMY, + regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); + + timeout = wait_for_completion_timeout(&cmd_wait, + msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS)); + + priv->hostcmd_wait = NULL; + + + pci_unmap_single(priv->pdev, dma_addr, dma_size, + PCI_DMA_BIDIRECTIONAL); + + if (!timeout) { + wiphy_err(hw->wiphy, "Command %s timeout after %u ms\n", + mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), + MWL8K_CMD_TIMEOUT_MS); + rc = -ETIMEDOUT; + } else { + int ms; + + ms = MWL8K_CMD_TIMEOUT_MS - jiffies_to_msecs(timeout); + + rc = cmd->result ? -EINVAL : 0; + if (rc) + wiphy_err(hw->wiphy, "Command %s error 0x%x\n", + mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), + le16_to_cpu(cmd->result)); + else if (ms > 2000) + wiphy_notice(hw->wiphy, "Command %s took %d ms\n", + mwl8k_cmd_name(cmd->code, + buf, sizeof(buf)), + ms); + } + + if (bitmap) + mwl8k_enable_bsses(hw, true, bitmap); + + mwl8k_fw_unlock(hw); + + return rc; +} + +static int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct mwl8k_cmd_pkt *cmd) +{ + if (vif != NULL) + cmd->macid = MWL8K_VIF(vif)->macid; + return mwl8k_post_cmd(hw, cmd); +} + +/* + * Setup code shared between STA and AP firmware images. + */ +static void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24)); + memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24)); + + BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24)); + memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24)); + + priv->band_24.band = IEEE80211_BAND_2GHZ; + priv->band_24.channels = priv->channels_24; + priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24); + priv->band_24.bitrates = priv->rates_24; + priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24); + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24; +} + +static void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50)); + memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50)); + + BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50)); + memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50)); + + priv->band_50.band = IEEE80211_BAND_5GHZ; + priv->band_50.channels = priv->channels_50; + priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50); + priv->band_50.bitrates = priv->rates_50; + priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50); + + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->band_50; +} + +/* + * CMD_GET_HW_SPEC (STA version). + */ +struct mwl8k_cmd_get_hw_spec_sta { + struct mwl8k_cmd_pkt header; + __u8 hw_rev; + __u8 host_interface; + __le16 num_mcaddrs; + __u8 perm_addr[ETH_ALEN]; + __le16 region_code; + __le32 fw_rev; + __le32 ps_cookie; + __le32 caps; + __u8 mcs_bitmap[16]; + __le32 rx_queue_ptr; + __le32 num_tx_queues; + __le32 tx_queue_ptrs[MWL8K_TX_WMM_QUEUES]; + __le32 caps2; + __le32 num_tx_desc_per_queue; + __le32 total_rxd; +} __packed; + +#define MWL8K_CAP_MAX_AMSDU 0x20000000 +#define MWL8K_CAP_GREENFIELD 0x08000000 +#define MWL8K_CAP_AMPDU 0x04000000 +#define MWL8K_CAP_RX_STBC 0x01000000 +#define MWL8K_CAP_TX_STBC 0x00800000 +#define MWL8K_CAP_SHORTGI_40MHZ 0x00400000 +#define MWL8K_CAP_SHORTGI_20MHZ 0x00200000 +#define MWL8K_CAP_RX_ANTENNA_MASK 0x000e0000 +#define MWL8K_CAP_TX_ANTENNA_MASK 0x0001c000 +#define MWL8K_CAP_DELAY_BA 0x00003000 +#define MWL8K_CAP_MIMO 0x00000200 +#define MWL8K_CAP_40MHZ 0x00000100 +#define MWL8K_CAP_BAND_MASK 0x00000007 +#define MWL8K_CAP_5GHZ 0x00000004 +#define MWL8K_CAP_2GHZ4 0x00000001 + +static void +mwl8k_set_ht_caps(struct ieee80211_hw *hw, + struct ieee80211_supported_band *band, u32 cap) +{ + int rx_streams; + int tx_streams; + + band->ht_cap.ht_supported = 1; + + if (cap & MWL8K_CAP_MAX_AMSDU) + band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; + if (cap & MWL8K_CAP_GREENFIELD) + band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD; + if (cap & MWL8K_CAP_AMPDU) { + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; + } + if (cap & MWL8K_CAP_RX_STBC) + band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC; + if (cap & MWL8K_CAP_TX_STBC) + band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + if (cap & MWL8K_CAP_SHORTGI_40MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + if (cap & MWL8K_CAP_SHORTGI_20MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + if (cap & MWL8K_CAP_DELAY_BA) + band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA; + if (cap & MWL8K_CAP_40MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK); + tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK); + + band->ht_cap.mcs.rx_mask[0] = 0xff; + if (rx_streams >= 2) + band->ht_cap.mcs.rx_mask[1] = 0xff; + if (rx_streams >= 3) + band->ht_cap.mcs.rx_mask[2] = 0xff; + band->ht_cap.mcs.rx_mask[4] = 0x01; + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + if (rx_streams != tx_streams) { + band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + band->ht_cap.mcs.tx_params |= (tx_streams - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + } +} + +static void +mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps) +{ + struct mwl8k_priv *priv = hw->priv; + + if (priv->caps) + return; + + if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) { + mwl8k_setup_2ghz_band(hw); + if (caps & MWL8K_CAP_MIMO) + mwl8k_set_ht_caps(hw, &priv->band_24, caps); + } + + if (caps & MWL8K_CAP_5GHZ) { + mwl8k_setup_5ghz_band(hw); + if (caps & MWL8K_CAP_MIMO) + mwl8k_set_ht_caps(hw, &priv->band_50, caps); + } + + priv->caps = caps; +} + +static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_get_hw_spec_sta *cmd; + int rc; + int i; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + + memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr)); + cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); + cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); + cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv)); + for (i = 0; i < mwl8k_tx_queues(priv); i++) + cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); + cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); + cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); + + rc = mwl8k_post_cmd(hw, &cmd->header); + + if (!rc) { + SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr); + priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); + priv->fw_rev = le32_to_cpu(cmd->fw_rev); + priv->hw_rev = cmd->hw_rev; + mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); + priv->ap_macids_supported = 0x00000000; + priv->sta_macids_supported = 0x00000001; + } + + kfree(cmd); + return rc; +} + +/* + * CMD_GET_HW_SPEC (AP version). + */ +struct mwl8k_cmd_get_hw_spec_ap { + struct mwl8k_cmd_pkt header; + __u8 hw_rev; + __u8 host_interface; + __le16 num_wcb; + __le16 num_mcaddrs; + __u8 perm_addr[ETH_ALEN]; + __le16 region_code; + __le16 num_antenna; + __le32 fw_rev; + __le32 wcbbase0; + __le32 rxwrptr; + __le32 rxrdptr; + __le32 ps_cookie; + __le32 wcbbase1; + __le32 wcbbase2; + __le32 wcbbase3; + __le32 fw_api_version; + __le32 caps; + __le32 num_of_ampdu_queues; + __le32 wcbbase_ampdu[MWL8K_MAX_AMPDU_QUEUES]; +} __packed; + +static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_get_hw_spec_ap *cmd; + int rc, i; + u32 api_version; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + + memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr)); + cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); + + rc = mwl8k_post_cmd(hw, &cmd->header); + + if (!rc) { + int off; + + api_version = le32_to_cpu(cmd->fw_api_version); + if (priv->device_info->fw_api_ap != api_version) { + printk(KERN_ERR "%s: Unsupported fw API version for %s." + " Expected %d got %d.\n", MWL8K_NAME, + priv->device_info->part_name, + priv->device_info->fw_api_ap, + api_version); + rc = -EINVAL; + goto done; + } + SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr); + priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); + priv->fw_rev = le32_to_cpu(cmd->fw_rev); + priv->hw_rev = cmd->hw_rev; + mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); + priv->ap_macids_supported = 0x000000ff; + priv->sta_macids_supported = 0x00000100; + priv->num_ampdu_queues = le32_to_cpu(cmd->num_of_ampdu_queues); + if (priv->num_ampdu_queues > MWL8K_MAX_AMPDU_QUEUES) { + wiphy_warn(hw->wiphy, "fw reported %d ampdu queues" + " but we only support %d.\n", + priv->num_ampdu_queues, + MWL8K_MAX_AMPDU_QUEUES); + priv->num_ampdu_queues = MWL8K_MAX_AMPDU_QUEUES; + } + off = le32_to_cpu(cmd->rxwrptr) & 0xffff; + iowrite32(priv->rxq[0].rxd_dma, priv->sram + off); + + off = le32_to_cpu(cmd->rxrdptr) & 0xffff; + iowrite32(priv->rxq[0].rxd_dma, priv->sram + off); + + priv->txq_offset[0] = le32_to_cpu(cmd->wcbbase0) & 0xffff; + priv->txq_offset[1] = le32_to_cpu(cmd->wcbbase1) & 0xffff; + priv->txq_offset[2] = le32_to_cpu(cmd->wcbbase2) & 0xffff; + priv->txq_offset[3] = le32_to_cpu(cmd->wcbbase3) & 0xffff; + + for (i = 0; i < priv->num_ampdu_queues; i++) + priv->txq_offset[i + MWL8K_TX_WMM_QUEUES] = + le32_to_cpu(cmd->wcbbase_ampdu[i]) & 0xffff; + } + +done: + kfree(cmd); + return rc; +} + +/* + * CMD_SET_HW_SPEC. + */ +struct mwl8k_cmd_set_hw_spec { + struct mwl8k_cmd_pkt header; + __u8 hw_rev; + __u8 host_interface; + __le16 num_mcaddrs; + __u8 perm_addr[ETH_ALEN]; + __le16 region_code; + __le32 fw_rev; + __le32 ps_cookie; + __le32 caps; + __le32 rx_queue_ptr; + __le32 num_tx_queues; + __le32 tx_queue_ptrs[MWL8K_MAX_TX_QUEUES]; + __le32 flags; + __le32 num_tx_desc_per_queue; + __le32 total_rxd; +} __packed; + +/* If enabled, MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY will cause + * packets to expire 500 ms after the timestamp in the tx descriptor. That is, + * the packets that are queued for more than 500ms, will be dropped in the + * hardware. This helps minimizing the issues caused due to head-of-line + * blocking where a slow client can hog the bandwidth and affect traffic to a + * faster client. + */ +#define MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY 0x00000400 +#define MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR 0x00000200 +#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 +#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP 0x00000020 +#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON 0x00000010 + +static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_set_hw_spec *cmd; + int rc; + int i; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_HW_SPEC); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + + cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); + cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); + cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv)); + + /* + * Mac80211 stack has Q0 as highest priority and Q3 as lowest in + * that order. Firmware has Q3 as highest priority and Q0 as lowest + * in that order. Map Q3 of mac80211 to Q0 of firmware so that the + * priority is interpreted the right way in firmware. + */ + for (i = 0; i < mwl8k_tx_queues(priv); i++) { + int j = mwl8k_tx_queues(priv) - 1 - i; + cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[j].txd_dma); + } + + cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT | + MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP | + MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON | + MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY | + MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR); + cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); + cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_MAC_MULTICAST_ADR. + */ +struct mwl8k_cmd_mac_multicast_adr { + struct mwl8k_cmd_pkt header; + __le16 action; + __le16 numaddr; + __u8 addr[0][ETH_ALEN]; +}; + +#define MWL8K_ENABLE_RX_DIRECTED 0x0001 +#define MWL8K_ENABLE_RX_MULTICAST 0x0002 +#define MWL8K_ENABLE_RX_ALL_MULTICAST 0x0004 +#define MWL8K_ENABLE_RX_BROADCAST 0x0008 + +static struct mwl8k_cmd_pkt * +__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti, + struct netdev_hw_addr_list *mc_list) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_mac_multicast_adr *cmd; + int size; + int mc_count = 0; + + if (mc_list) + mc_count = netdev_hw_addr_list_count(mc_list); + + if (allmulti || mc_count > priv->num_mcaddrs) { + allmulti = 1; + mc_count = 0; + } + + size = sizeof(*cmd) + mc_count * ETH_ALEN; + + cmd = kzalloc(size, GFP_ATOMIC); + if (cmd == NULL) + return NULL; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR); + cmd->header.length = cpu_to_le16(size); + cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_DIRECTED | + MWL8K_ENABLE_RX_BROADCAST); + + if (allmulti) { + cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST); + } else if (mc_count) { + struct netdev_hw_addr *ha; + int i = 0; + + cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST); + cmd->numaddr = cpu_to_le16(mc_count); + netdev_hw_addr_list_for_each(ha, mc_list) { + memcpy(cmd->addr[i], ha->addr, ETH_ALEN); + } + } + + return &cmd->header; +} + +/* + * CMD_GET_STAT. + */ +struct mwl8k_cmd_get_stat { + struct mwl8k_cmd_pkt header; + __le32 stats[64]; +} __packed; + +#define MWL8K_STAT_ACK_FAILURE 9 +#define MWL8K_STAT_RTS_FAILURE 12 +#define MWL8K_STAT_FCS_ERROR 24 +#define MWL8K_STAT_RTS_SUCCESS 11 + +static int mwl8k_cmd_get_stat(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct mwl8k_cmd_get_stat *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_STAT); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + + rc = mwl8k_post_cmd(hw, &cmd->header); + if (!rc) { + stats->dot11ACKFailureCount = + le32_to_cpu(cmd->stats[MWL8K_STAT_ACK_FAILURE]); + stats->dot11RTSFailureCount = + le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_FAILURE]); + stats->dot11FCSErrorCount = + le32_to_cpu(cmd->stats[MWL8K_STAT_FCS_ERROR]); + stats->dot11RTSSuccessCount = + le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_SUCCESS]); + } + kfree(cmd); + + return rc; +} + +/* + * CMD_RADIO_CONTROL. + */ +struct mwl8k_cmd_radio_control { + struct mwl8k_cmd_pkt header; + __le16 action; + __le16 control; + __le16 radio_on; +} __packed; + +static int +mwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_radio_control *cmd; + int rc; + + if (enable == priv->radio_on && !force) + return 0; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_RADIO_CONTROL); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->control = cpu_to_le16(priv->radio_short_preamble ? 3 : 1); + cmd->radio_on = cpu_to_le16(enable ? 0x0001 : 0x0000); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + if (!rc) + priv->radio_on = enable; + + return rc; +} + +static int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw) +{ + return mwl8k_cmd_radio_control(hw, 0, 0); +} + +static int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw) +{ + return mwl8k_cmd_radio_control(hw, 1, 0); +} + +static int +mwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble) +{ + struct mwl8k_priv *priv = hw->priv; + + priv->radio_short_preamble = short_preamble; + + return mwl8k_cmd_radio_control(hw, 1, 1); +} + +/* + * CMD_RF_TX_POWER. + */ +#define MWL8K_RF_TX_POWER_LEVEL_TOTAL 8 + +struct mwl8k_cmd_rf_tx_power { + struct mwl8k_cmd_pkt header; + __le16 action; + __le16 support_level; + __le16 current_level; + __le16 reserved; + __le16 power_level_list[MWL8K_RF_TX_POWER_LEVEL_TOTAL]; +} __packed; + +static int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm) +{ + struct mwl8k_cmd_rf_tx_power *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_TX_POWER); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->support_level = cpu_to_le16(dBm); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_TX_POWER. + */ +#define MWL8K_TX_POWER_LEVEL_TOTAL 12 + +struct mwl8k_cmd_tx_power { + struct mwl8k_cmd_pkt header; + __le16 action; + __le16 band; + __le16 channel; + __le16 bw; + __le16 sub_ch; + __le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL]; +} __packed; + +static int mwl8k_cmd_tx_power(struct ieee80211_hw *hw, + struct ieee80211_conf *conf, + unsigned short pwr) +{ + struct ieee80211_channel *channel = conf->chandef.chan; + enum nl80211_channel_type channel_type = + cfg80211_get_chandef_type(&conf->chandef); + struct mwl8k_cmd_tx_power *cmd; + int rc; + int i; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_TX_POWER); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(MWL8K_CMD_SET_LIST); + + if (channel->band == IEEE80211_BAND_2GHZ) + cmd->band = cpu_to_le16(0x1); + else if (channel->band == IEEE80211_BAND_5GHZ) + cmd->band = cpu_to_le16(0x4); + + cmd->channel = cpu_to_le16(channel->hw_value); + + if (channel_type == NL80211_CHAN_NO_HT || + channel_type == NL80211_CHAN_HT20) { + cmd->bw = cpu_to_le16(0x2); + } else { + cmd->bw = cpu_to_le16(0x4); + if (channel_type == NL80211_CHAN_HT40MINUS) + cmd->sub_ch = cpu_to_le16(0x3); + else if (channel_type == NL80211_CHAN_HT40PLUS) + cmd->sub_ch = cpu_to_le16(0x1); + } + + for (i = 0; i < MWL8K_TX_POWER_LEVEL_TOTAL; i++) + cmd->power_level_list[i] = cpu_to_le16(pwr); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_RF_ANTENNA. + */ +struct mwl8k_cmd_rf_antenna { + struct mwl8k_cmd_pkt header; + __le16 antenna; + __le16 mode; +} __packed; + +#define MWL8K_RF_ANTENNA_RX 1 +#define MWL8K_RF_ANTENNA_TX 2 + +static int +mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask) +{ + struct mwl8k_cmd_rf_antenna *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->antenna = cpu_to_le16(antenna); + cmd->mode = cpu_to_le16(mask); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_BEACON. + */ +struct mwl8k_cmd_set_beacon { + struct mwl8k_cmd_pkt header; + __le16 beacon_len; + __u8 beacon[0]; +}; + +static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *beacon, int len) +{ + struct mwl8k_cmd_set_beacon *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON); + cmd->header.length = cpu_to_le16(sizeof(*cmd) + len); + cmd->beacon_len = cpu_to_le16(len); + memcpy(cmd->beacon, beacon, len); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_PRE_SCAN. + */ +struct mwl8k_cmd_set_pre_scan { + struct mwl8k_cmd_pkt header; +} __packed; + +static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw) +{ + struct mwl8k_cmd_set_pre_scan *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_PRE_SCAN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_BBP_REG_ACCESS. + */ +struct mwl8k_cmd_bbp_reg_access { + struct mwl8k_cmd_pkt header; + __le16 action; + __le16 offset; + u8 value; + u8 rsrv[3]; +} __packed; + +static int +mwl8k_cmd_bbp_reg_access(struct ieee80211_hw *hw, + u16 action, + u16 offset, + u8 *value) +{ + struct mwl8k_cmd_bbp_reg_access *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_BBP_REG_ACCESS); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(action); + cmd->offset = cpu_to_le16(offset); + + rc = mwl8k_post_cmd(hw, &cmd->header); + + if (!rc) + *value = cmd->value; + else + *value = 0; + + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_POST_SCAN. + */ +struct mwl8k_cmd_set_post_scan { + struct mwl8k_cmd_pkt header; + __le32 isibss; + __u8 bssid[ETH_ALEN]; +} __packed; + +static int +mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac) +{ + struct mwl8k_cmd_set_post_scan *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_POST_SCAN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->isibss = 0; + memcpy(cmd->bssid, mac, ETH_ALEN); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +static int freq_to_idx(struct mwl8k_priv *priv, int freq) +{ + struct ieee80211_supported_band *sband; + int band, ch, idx = 0; + + for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { + sband = priv->hw->wiphy->bands[band]; + if (!sband) + continue; + + for (ch = 0; ch < sband->n_channels; ch++, idx++) + if (sband->channels[ch].center_freq == freq) + goto exit; + } + +exit: + return idx; +} + +static void mwl8k_update_survey(struct mwl8k_priv *priv, + struct ieee80211_channel *channel) +{ + u32 cca_cnt, rx_rdy; + s8 nf = 0, idx; + struct survey_info *survey; + + idx = freq_to_idx(priv, priv->acs_chan->center_freq); + if (idx >= MWL8K_NUM_CHANS) { + wiphy_err(priv->hw->wiphy, "Failed to update survey\n"); + return; + } + + survey = &priv->survey[idx]; + + cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG); + cca_cnt /= 1000; /* uSecs to mSecs */ + survey->time_busy = (u64) cca_cnt; + + rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG); + rx_rdy /= 1000; /* uSecs to mSecs */ + survey->time_rx = (u64) rx_rdy; + + priv->channel_time = jiffies - priv->channel_time; + survey->time = jiffies_to_msecs(priv->channel_time); + + survey->channel = channel; + + mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &nf); + + /* Make sure sign is negative else ACS at hostapd fails */ + survey->noise = nf * -1; + + survey->filled = SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX; +} + +/* + * CMD_SET_RF_CHANNEL. + */ +struct mwl8k_cmd_set_rf_channel { + struct mwl8k_cmd_pkt header; + __le16 action; + __u8 current_channel; + __le32 channel_flags; +} __packed; + +static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, + struct ieee80211_conf *conf) +{ + struct ieee80211_channel *channel = conf->chandef.chan; + enum nl80211_channel_type channel_type = + cfg80211_get_chandef_type(&conf->chandef); + struct mwl8k_cmd_set_rf_channel *cmd; + struct mwl8k_priv *priv = hw->priv; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RF_CHANNEL); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->current_channel = channel->hw_value; + + if (channel->band == IEEE80211_BAND_2GHZ) + cmd->channel_flags |= cpu_to_le32(0x00000001); + else if (channel->band == IEEE80211_BAND_5GHZ) + cmd->channel_flags |= cpu_to_le32(0x00000004); + + if (!priv->sw_scan_start) { + if (channel_type == NL80211_CHAN_NO_HT || + channel_type == NL80211_CHAN_HT20) + cmd->channel_flags |= cpu_to_le32(0x00000080); + else if (channel_type == NL80211_CHAN_HT40MINUS) + cmd->channel_flags |= cpu_to_le32(0x000001900); + else if (channel_type == NL80211_CHAN_HT40PLUS) + cmd->channel_flags |= cpu_to_le32(0x000000900); + } else { + cmd->channel_flags |= cpu_to_le32(0x00000080); + } + + if (priv->sw_scan_start) { + /* Store current channel stats + * before switching to newer one. + * This will be processed only for AP fw. + */ + if (priv->channel_time != 0) + mwl8k_update_survey(priv, priv->acs_chan); + + priv->channel_time = jiffies; + priv->acs_chan = channel; + } + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_AID. + */ +#define MWL8K_FRAME_PROT_DISABLED 0x00 +#define MWL8K_FRAME_PROT_11G 0x07 +#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02 +#define MWL8K_FRAME_PROT_11N_HT_ALL 0x06 + +struct mwl8k_cmd_update_set_aid { + struct mwl8k_cmd_pkt header; + __le16 aid; + + /* AP's MAC address (BSSID) */ + __u8 bssid[ETH_ALEN]; + __le16 protection_mode; + __u8 supp_rates[14]; +} __packed; + +static void legacy_rate_mask_to_array(u8 *rates, u32 mask) +{ + int i; + int j; + + /* + * Clear nonstandard rate 4. + */ + mask &= 0x1fef; + + for (i = 0, j = 0; i < 13; i++) { + if (mask & (1 << i)) + rates[j++] = mwl8k_rates_24[i].hw_value; + } +} + +static int +mwl8k_cmd_set_aid(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u32 legacy_rate_mask) +{ + struct mwl8k_cmd_update_set_aid *cmd; + u16 prot_mode; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->aid = cpu_to_le16(vif->bss_conf.aid); + memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); + + if (vif->bss_conf.use_cts_prot) { + prot_mode = MWL8K_FRAME_PROT_11G; + } else { + switch (vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION) { + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY; + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL; + break; + default: + prot_mode = MWL8K_FRAME_PROT_DISABLED; + break; + } + } + cmd->protection_mode = cpu_to_le16(prot_mode); + + legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_RATE. + */ +struct mwl8k_cmd_set_rate { + struct mwl8k_cmd_pkt header; + __u8 legacy_rates[14]; + + /* Bitmap for supported MCS codes. */ + __u8 mcs_set[16]; + __u8 reserved[16]; +} __packed; + +static int +mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 legacy_rate_mask, u8 *mcs_rates) +{ + struct mwl8k_cmd_set_rate *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask); + memcpy(cmd->mcs_set, mcs_rates, 16); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_FINALIZE_JOIN. + */ +#define MWL8K_FJ_BEACON_MAXLEN 128 + +struct mwl8k_cmd_finalize_join { + struct mwl8k_cmd_pkt header; + __le32 sleep_interval; /* Number of beacon periods to sleep */ + __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN]; +} __packed; + +static int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame, + int framelen, int dtim) +{ + struct mwl8k_cmd_finalize_join *cmd; + struct ieee80211_mgmt *payload = frame; + int payload_len; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1); + + payload_len = framelen - ieee80211_hdrlen(payload->frame_control); + if (payload_len < 0) + payload_len = 0; + else if (payload_len > MWL8K_FJ_BEACON_MAXLEN) + payload_len = MWL8K_FJ_BEACON_MAXLEN; + + memcpy(cmd->beacon_data, &payload->u.beacon, payload_len); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_RTS_THRESHOLD. + */ +struct mwl8k_cmd_set_rts_threshold { + struct mwl8k_cmd_pkt header; + __le16 action; + __le16 threshold; +} __packed; + +static int +mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh) +{ + struct mwl8k_cmd_set_rts_threshold *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->threshold = cpu_to_le16(rts_thresh); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_SLOT. + */ +struct mwl8k_cmd_set_slot { + struct mwl8k_cmd_pkt header; + __le16 action; + __u8 short_slot; +} __packed; + +static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time) +{ + struct mwl8k_cmd_set_slot *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->short_slot = short_slot_time; + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_EDCA_PARAMS. + */ +struct mwl8k_cmd_set_edca_params { + struct mwl8k_cmd_pkt header; + + /* See MWL8K_SET_EDCA_XXX below */ + __le16 action; + + /* TX opportunity in units of 32 us */ + __le16 txop; + + union { + struct { + /* Log exponent of max contention period: 0...15 */ + __le32 log_cw_max; + + /* Log exponent of min contention period: 0...15 */ + __le32 log_cw_min; + + /* Adaptive interframe spacing in units of 32us */ + __u8 aifs; + + /* TX queue to configure */ + __u8 txq; + } ap; + struct { + /* Log exponent of max contention period: 0...15 */ + __u8 log_cw_max; + + /* Log exponent of min contention period: 0...15 */ + __u8 log_cw_min; + + /* Adaptive interframe spacing in units of 32us */ + __u8 aifs; + + /* TX queue to configure */ + __u8 txq; + } sta; + }; +} __packed; + +#define MWL8K_SET_EDCA_CW 0x01 +#define MWL8K_SET_EDCA_TXOP 0x02 +#define MWL8K_SET_EDCA_AIFS 0x04 + +#define MWL8K_SET_EDCA_ALL (MWL8K_SET_EDCA_CW | \ + MWL8K_SET_EDCA_TXOP | \ + MWL8K_SET_EDCA_AIFS) + +static int +mwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, + __u16 cw_min, __u16 cw_max, + __u8 aifs, __u16 txop) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_set_edca_params *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL); + cmd->txop = cpu_to_le16(txop); + if (priv->ap_fw) { + cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1)); + cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1)); + cmd->ap.aifs = aifs; + cmd->ap.txq = qnum; + } else { + cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1); + cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1); + cmd->sta.aifs = aifs; + cmd->sta.txq = qnum; + } + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_WMM_MODE. + */ +struct mwl8k_cmd_set_wmm_mode { + struct mwl8k_cmd_pkt header; + __le16 action; +} __packed; + +static int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_set_wmm_mode *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(!!enable); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + if (!rc) + priv->wmm_enabled = enable; + + return rc; +} + +/* + * CMD_MIMO_CONFIG. + */ +struct mwl8k_cmd_mimo_config { + struct mwl8k_cmd_pkt header; + __le32 action; + __u8 rx_antenna_map; + __u8 tx_antenna_map; +} __packed; + +static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx) +{ + struct mwl8k_cmd_mimo_config *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET); + cmd->rx_antenna_map = rx; + cmd->tx_antenna_map = tx; + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_USE_FIXED_RATE (STA version). + */ +struct mwl8k_cmd_use_fixed_rate_sta { + struct mwl8k_cmd_pkt header; + __le32 action; + __le32 allow_rate_drop; + __le32 num_rates; + struct { + __le32 is_ht_rate; + __le32 enable_retry; + __le32 rate; + __le32 retry_count; + } rate_entry[8]; + __le32 rate_type; + __le32 reserved1; + __le32 reserved2; +} __packed; + +#define MWL8K_USE_AUTO_RATE 0x0002 +#define MWL8K_UCAST_RATE 0 + +static int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw) +{ + struct mwl8k_cmd_use_fixed_rate_sta *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); + cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_USE_FIXED_RATE (AP version). + */ +struct mwl8k_cmd_use_fixed_rate_ap { + struct mwl8k_cmd_pkt header; + __le32 action; + __le32 allow_rate_drop; + __le32 num_rates; + struct mwl8k_rate_entry_ap { + __le32 is_ht_rate; + __le32 enable_retry; + __le32 rate; + __le32 retry_count; + } rate_entry[4]; + u8 multicast_rate; + u8 multicast_rate_type; + u8 management_rate; +} __packed; + +static int +mwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt) +{ + struct mwl8k_cmd_use_fixed_rate_ap *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); + cmd->multicast_rate = mcast; + cmd->management_rate = mgmt; + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_ENABLE_SNIFFER. + */ +struct mwl8k_cmd_enable_sniffer { + struct mwl8k_cmd_pkt header; + __le32 action; +} __packed; + +static int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable) +{ + struct mwl8k_cmd_enable_sniffer *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(!!enable); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +struct mwl8k_cmd_update_mac_addr { + struct mwl8k_cmd_pkt header; + union { + struct { + __le16 mac_type; + __u8 mac_addr[ETH_ALEN]; + } mbss; + __u8 mac_addr[ETH_ALEN]; + }; +} __packed; + +#define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0 +#define MWL8K_MAC_TYPE_SECONDARY_CLIENT 1 +#define MWL8K_MAC_TYPE_PRIMARY_AP 2 +#define MWL8K_MAC_TYPE_SECONDARY_AP 3 + +static int mwl8k_cmd_update_mac_addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *mac, bool set) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + struct mwl8k_cmd_update_mac_addr *cmd; + int mac_type; + int rc; + + mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; + if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) { + if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported)) + if (priv->ap_fw) + mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; + else + mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT; + else + mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; + } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) { + if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported)) + mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; + else + mac_type = MWL8K_MAC_TYPE_SECONDARY_AP; + } + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + if (set) + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); + else + cmd->header.code = cpu_to_le16(MWL8K_CMD_DEL_MAC_ADDR); + + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + if (priv->ap_fw) { + cmd->mbss.mac_type = cpu_to_le16(mac_type); + memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN); + } else { + memcpy(cmd->mac_addr, mac, ETH_ALEN); + } + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * MWL8K_CMD_SET_MAC_ADDR. + */ +static inline int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *mac) +{ + return mwl8k_cmd_update_mac_addr(hw, vif, mac, true); +} + +/* + * MWL8K_CMD_DEL_MAC_ADDR. + */ +static inline int mwl8k_cmd_del_mac_addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *mac) +{ + return mwl8k_cmd_update_mac_addr(hw, vif, mac, false); +} + +/* + * CMD_SET_RATEADAPT_MODE. + */ +struct mwl8k_cmd_set_rate_adapt_mode { + struct mwl8k_cmd_pkt header; + __le16 action; + __le16 mode; +} __packed; + +static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) +{ + struct mwl8k_cmd_set_rate_adapt_mode *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->mode = cpu_to_le16(mode); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_GET_WATCHDOG_BITMAP. + */ +struct mwl8k_cmd_get_watchdog_bitmap { + struct mwl8k_cmd_pkt header; + u8 bitmap; +} __packed; + +static int mwl8k_cmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap) +{ + struct mwl8k_cmd_get_watchdog_bitmap *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_WATCHDOG_BITMAP); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + + rc = mwl8k_post_cmd(hw, &cmd->header); + if (!rc) + *bitmap = cmd->bitmap; + + kfree(cmd); + + return rc; +} + +#define MWL8K_WMM_QUEUE_NUMBER 3 + +static void mwl8k_destroy_ba(struct ieee80211_hw *hw, + u8 idx); + +static void mwl8k_watchdog_ba_events(struct work_struct *work) +{ + int rc; + u8 bitmap = 0, stream_index; + struct mwl8k_ampdu_stream *streams; + struct mwl8k_priv *priv = + container_of(work, struct mwl8k_priv, watchdog_ba_handle); + struct ieee80211_hw *hw = priv->hw; + int i; + u32 status = 0; + + mwl8k_fw_lock(hw); + + rc = mwl8k_cmd_get_watchdog_bitmap(priv->hw, &bitmap); + if (rc) + goto done; + + spin_lock(&priv->stream_lock); + + /* the bitmap is the hw queue number. Map it to the ampdu queue. */ + for (i = 0; i < TOTAL_HW_TX_QUEUES; i++) { + if (bitmap & (1 << i)) { + stream_index = (i + MWL8K_WMM_QUEUE_NUMBER) % + TOTAL_HW_TX_QUEUES; + streams = &priv->ampdu[stream_index]; + if (streams->state == AMPDU_STREAM_ACTIVE) { + ieee80211_stop_tx_ba_session(streams->sta, + streams->tid); + spin_unlock(&priv->stream_lock); + mwl8k_destroy_ba(hw, stream_index); + spin_lock(&priv->stream_lock); + } + } + } + + spin_unlock(&priv->stream_lock); +done: + atomic_dec(&priv->watchdog_event_pending); + status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); + iowrite32((status | MWL8K_A2H_INT_BA_WATCHDOG), + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); + mwl8k_fw_unlock(hw); + return; +} + + +/* + * CMD_BSS_START. + */ +struct mwl8k_cmd_bss_start { + struct mwl8k_cmd_pkt header; + __le32 enable; +} __packed; + +static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int enable) +{ + struct mwl8k_cmd_bss_start *cmd; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + struct mwl8k_priv *priv = hw->priv; + int rc; + + if (enable && (priv->running_bsses & (1 << mwl8k_vif->macid))) + return 0; + + if (!enable && !(priv->running_bsses & (1 << mwl8k_vif->macid))) + return 0; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->enable = cpu_to_le32(enable); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + if (!rc) { + if (enable) + priv->running_bsses |= (1 << mwl8k_vif->macid); + else + priv->running_bsses &= ~(1 << mwl8k_vif->macid); + } + return rc; +} + +static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, u32 bitmap) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif, *tmp_vif; + struct ieee80211_vif *vif; + + list_for_each_entry_safe(mwl8k_vif, tmp_vif, &priv->vif_list, list) { + vif = mwl8k_vif->vif; + + if (!(bitmap & (1 << mwl8k_vif->macid))) + continue; + + if (vif->type == NL80211_IFTYPE_AP) + mwl8k_cmd_bss_start(hw, vif, enable); + } +} +/* + * CMD_BASTREAM. + */ + +/* + * UPSTREAM is tx direction + */ +#define BASTREAM_FLAG_DIRECTION_UPSTREAM 0x00 +#define BASTREAM_FLAG_IMMEDIATE_TYPE 0x01 + +enum ba_stream_action_type { + MWL8K_BA_CREATE, + MWL8K_BA_UPDATE, + MWL8K_BA_DESTROY, + MWL8K_BA_FLUSH, + MWL8K_BA_CHECK, +}; + + +struct mwl8k_create_ba_stream { + __le32 flags; + __le32 idle_thrs; + __le32 bar_thrs; + __le32 window_size; + u8 peer_mac_addr[6]; + u8 dialog_token; + u8 tid; + u8 queue_id; + u8 param_info; + __le32 ba_context; + u8 reset_seq_no_flag; + __le16 curr_seq_no; + u8 sta_src_mac_addr[6]; +} __packed; + +struct mwl8k_destroy_ba_stream { + __le32 flags; + __le32 ba_context; +} __packed; + +struct mwl8k_cmd_bastream { + struct mwl8k_cmd_pkt header; + __le32 action; + union { + struct mwl8k_create_ba_stream create_params; + struct mwl8k_destroy_ba_stream destroy_params; + }; +} __packed; + +static int +mwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, + struct ieee80211_vif *vif) +{ + struct mwl8k_cmd_bastream *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + + cmd->action = cpu_to_le32(MWL8K_BA_CHECK); + + cmd->create_params.queue_id = stream->idx; + memcpy(&cmd->create_params.peer_mac_addr[0], stream->sta->addr, + ETH_ALEN); + cmd->create_params.tid = stream->tid; + + cmd->create_params.flags = + cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE) | + cpu_to_le32(BASTREAM_FLAG_DIRECTION_UPSTREAM); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + + kfree(cmd); + + return rc; +} + +static int +mwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, + u8 buf_size, struct ieee80211_vif *vif) +{ + struct mwl8k_cmd_bastream *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + + cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + + cmd->action = cpu_to_le32(MWL8K_BA_CREATE); + + cmd->create_params.bar_thrs = cpu_to_le32((u32)buf_size); + cmd->create_params.window_size = cpu_to_le32((u32)buf_size); + cmd->create_params.queue_id = stream->idx; + + memcpy(cmd->create_params.peer_mac_addr, stream->sta->addr, ETH_ALEN); + cmd->create_params.tid = stream->tid; + cmd->create_params.curr_seq_no = cpu_to_le16(0); + cmd->create_params.reset_seq_no_flag = 1; + + cmd->create_params.param_info = + (stream->sta->ht_cap.ampdu_factor & + IEEE80211_HT_AMPDU_PARM_FACTOR) | + ((stream->sta->ht_cap.ampdu_density << 2) & + IEEE80211_HT_AMPDU_PARM_DENSITY); + + cmd->create_params.flags = + cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE | + BASTREAM_FLAG_DIRECTION_UPSTREAM); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + + wiphy_debug(hw->wiphy, "Created a BA stream for %pM : tid %d\n", + stream->sta->addr, stream->tid); + kfree(cmd); + + return rc; +} + +static void mwl8k_destroy_ba(struct ieee80211_hw *hw, + u8 idx) +{ + struct mwl8k_cmd_bastream *cmd; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_BA_DESTROY); + + cmd->destroy_params.ba_context = cpu_to_le32(idx); + mwl8k_post_cmd(hw, &cmd->header); + + wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", idx); + + kfree(cmd); +} + +/* + * CMD_SET_NEW_STN. + */ +struct mwl8k_cmd_set_new_stn { + struct mwl8k_cmd_pkt header; + __le16 aid; + __u8 mac_addr[6]; + __le16 stn_id; + __le16 action; + __le16 rsvd; + __le32 legacy_rates; + __u8 ht_rates[4]; + __le16 cap_info; + __le16 ht_capabilities_info; + __u8 mac_ht_param_info; + __u8 rev; + __u8 control_channel; + __u8 add_channel; + __le16 op_mode; + __le16 stbc; + __u8 add_qos_info; + __u8 is_qos_sta; + __le32 fw_sta_ptr; +} __packed; + +#define MWL8K_STA_ACTION_ADD 0 +#define MWL8K_STA_ACTION_REMOVE 2 + +static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mwl8k_cmd_set_new_stn *cmd; + u32 rates; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->aid = cpu_to_le16(sta->aid); + memcpy(cmd->mac_addr, sta->addr, ETH_ALEN); + cmd->stn_id = cpu_to_le16(sta->aid); + cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD); + if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) + rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; + else + rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; + cmd->legacy_rates = cpu_to_le32(rates); + if (sta->ht_cap.ht_supported) { + cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0]; + cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1]; + cmd->ht_rates[2] = sta->ht_cap.mcs.rx_mask[2]; + cmd->ht_rates[3] = sta->ht_cap.mcs.rx_mask[3]; + cmd->ht_capabilities_info = cpu_to_le16(sta->ht_cap.cap); + cmd->mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) | + ((sta->ht_cap.ampdu_density & 7) << 2); + cmd->is_qos_sta = 1; + } + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mwl8k_cmd_set_new_stn *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + memcpy(cmd->mac_addr, vif->addr, ETH_ALEN); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *addr) +{ + struct mwl8k_cmd_set_new_stn *cmd; + struct mwl8k_priv *priv = hw->priv; + int rc, i; + u8 idx; + + spin_lock(&priv->stream_lock); + /* Destroy any active ampdu streams for this sta */ + for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { + struct mwl8k_ampdu_stream *s; + s = &priv->ampdu[i]; + if (s->state != AMPDU_NO_STREAM) { + if (memcmp(s->sta->addr, addr, ETH_ALEN) == 0) { + if (s->state == AMPDU_STREAM_ACTIVE) { + idx = s->idx; + spin_unlock(&priv->stream_lock); + mwl8k_destroy_ba(hw, idx); + spin_lock(&priv->stream_lock); + } else if (s->state == AMPDU_STREAM_NEW) { + mwl8k_remove_stream(hw, s); + } + } + } + } + + spin_unlock(&priv->stream_lock); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + memcpy(cmd->mac_addr, addr, ETH_ALEN); + cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_UPDATE_ENCRYPTION. + */ + +#define MAX_ENCR_KEY_LENGTH 16 +#define MIC_KEY_LENGTH 8 + +struct mwl8k_cmd_update_encryption { + struct mwl8k_cmd_pkt header; + + __le32 action; + __le32 reserved; + __u8 mac_addr[6]; + __u8 encr_type; + +} __packed; + +struct mwl8k_cmd_set_key { + struct mwl8k_cmd_pkt header; + + __le32 action; + __le32 reserved; + __le16 length; + __le16 key_type_id; + __le32 key_info; + __le32 key_id; + __le16 key_len; + __u8 key_material[MAX_ENCR_KEY_LENGTH]; + __u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; + __u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; + __le16 tkip_rsc_low; + __le32 tkip_rsc_high; + __le16 tkip_tsc_low; + __le32 tkip_tsc_high; + __u8 mac_addr[6]; +} __packed; + +enum { + MWL8K_ENCR_ENABLE, + MWL8K_ENCR_SET_KEY, + MWL8K_ENCR_REMOVE_KEY, + MWL8K_ENCR_SET_GROUP_KEY, +}; + +#define MWL8K_UPDATE_ENCRYPTION_TYPE_WEP 0 +#define MWL8K_UPDATE_ENCRYPTION_TYPE_DISABLE 1 +#define MWL8K_UPDATE_ENCRYPTION_TYPE_TKIP 4 +#define MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED 7 +#define MWL8K_UPDATE_ENCRYPTION_TYPE_AES 8 + +enum { + MWL8K_ALG_WEP, + MWL8K_ALG_TKIP, + MWL8K_ALG_CCMP, +}; + +#define MWL8K_KEY_FLAG_TXGROUPKEY 0x00000004 +#define MWL8K_KEY_FLAG_PAIRWISE 0x00000008 +#define MWL8K_KEY_FLAG_TSC_VALID 0x00000040 +#define MWL8K_KEY_FLAG_WEP_TXKEY 0x01000000 +#define MWL8K_KEY_FLAG_MICKEY_VALID 0x02000000 + +static int mwl8k_cmd_update_encryption_enable(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u8 *addr, + u8 encr_type) +{ + struct mwl8k_cmd_update_encryption *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_ENCR_ENABLE); + memcpy(cmd->mac_addr, addr, ETH_ALEN); + cmd->encr_type = encr_type; + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +static int mwl8k_encryption_set_cmd_info(struct mwl8k_cmd_set_key *cmd, + u8 *addr, + struct ieee80211_key_conf *key) +{ + cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->length = cpu_to_le16(sizeof(*cmd) - + offsetof(struct mwl8k_cmd_set_key, length)); + cmd->key_id = cpu_to_le32(key->keyidx); + cmd->key_len = cpu_to_le16(key->keylen); + memcpy(cmd->mac_addr, addr, ETH_ALEN); + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + cmd->key_type_id = cpu_to_le16(MWL8K_ALG_WEP); + if (key->keyidx == 0) + cmd->key_info = cpu_to_le32(MWL8K_KEY_FLAG_WEP_TXKEY); + + break; + case WLAN_CIPHER_SUITE_TKIP: + cmd->key_type_id = cpu_to_le16(MWL8K_ALG_TKIP); + cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) + : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); + cmd->key_info |= cpu_to_le32(MWL8K_KEY_FLAG_MICKEY_VALID + | MWL8K_KEY_FLAG_TSC_VALID); + break; + case WLAN_CIPHER_SUITE_CCMP: + cmd->key_type_id = cpu_to_le16(MWL8K_ALG_CCMP); + cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) + : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); + break; + default: + return -ENOTSUPP; + } + + return 0; +} + +static int mwl8k_cmd_encryption_set_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u8 *addr, + struct ieee80211_key_conf *key) +{ + struct mwl8k_cmd_set_key *cmd; + int rc; + int keymlen; + u32 action; + u8 idx; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); + if (rc < 0) + goto done; + + idx = key->keyidx; + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + action = MWL8K_ENCR_SET_KEY; + else + action = MWL8K_ENCR_SET_GROUP_KEY; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (!mwl8k_vif->wep_key_conf[idx].enabled) { + memcpy(mwl8k_vif->wep_key_conf[idx].key, key, + sizeof(*key) + key->keylen); + mwl8k_vif->wep_key_conf[idx].enabled = 1; + } + + keymlen = key->keylen; + action = MWL8K_ENCR_SET_KEY; + break; + case WLAN_CIPHER_SUITE_TKIP: + keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH; + break; + case WLAN_CIPHER_SUITE_CCMP: + keymlen = key->keylen; + break; + default: + rc = -ENOTSUPP; + goto done; + } + + memcpy(cmd->key_material, key->key, keymlen); + cmd->action = cpu_to_le32(action); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); +done: + kfree(cmd); + + return rc; +} + +static int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u8 *addr, + struct ieee80211_key_conf *key) +{ + struct mwl8k_cmd_set_key *cmd; + int rc; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); + if (rc < 0) + goto done; + + if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) + mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0; + + cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); +done: + kfree(cmd); + + return rc; +} + +static int mwl8k_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd_param, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int rc = 0; + u8 encr_type; + u8 *addr; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + struct mwl8k_priv *priv = hw->priv; + + if (vif->type == NL80211_IFTYPE_STATION && !priv->ap_fw) + return -EOPNOTSUPP; + + if (sta == NULL) + addr = vif->addr; + else + addr = sta->addr; + + if (cmd_param == SET_KEY) { + rc = mwl8k_cmd_encryption_set_key(hw, vif, addr, key); + if (rc) + goto out; + + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40) + || (key->cipher == WLAN_CIPHER_SUITE_WEP104)) + encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_WEP; + else + encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED; + + rc = mwl8k_cmd_update_encryption_enable(hw, vif, addr, + encr_type); + if (rc) + goto out; + + mwl8k_vif->is_hw_crypto_enabled = true; + + } else { + rc = mwl8k_cmd_encryption_remove_key(hw, vif, addr, key); + + if (rc) + goto out; + } +out: + return rc; +} + +/* + * CMD_UPDATE_STADB. + */ +struct ewc_ht_info { + __le16 control1; + __le16 control2; + __le16 control3; +} __packed; + +struct peer_capability_info { + /* Peer type - AP vs. STA. */ + __u8 peer_type; + + /* Basic 802.11 capabilities from assoc resp. */ + __le16 basic_caps; + + /* Set if peer supports 802.11n high throughput (HT). */ + __u8 ht_support; + + /* Valid if HT is supported. */ + __le16 ht_caps; + __u8 extended_ht_caps; + struct ewc_ht_info ewc_info; + + /* Legacy rate table. Intersection of our rates and peer rates. */ + __u8 legacy_rates[12]; + + /* HT rate table. Intersection of our rates and peer rates. */ + __u8 ht_rates[16]; + __u8 pad[16]; + + /* If set, interoperability mode, no proprietary extensions. */ + __u8 interop; + __u8 pad2; + __u8 station_id; + __le16 amsdu_enabled; +} __packed; + +struct mwl8k_cmd_update_stadb { + struct mwl8k_cmd_pkt header; + + /* See STADB_ACTION_TYPE */ + __le32 action; + + /* Peer MAC address */ + __u8 peer_addr[ETH_ALEN]; + + __le32 reserved; + + /* Peer info - valid during add/update. */ + struct peer_capability_info peer_info; +} __packed; + +#define MWL8K_STA_DB_MODIFY_ENTRY 1 +#define MWL8K_STA_DB_DEL_ENTRY 2 + +/* Peer Entry flags - used to define the type of the peer node */ +#define MWL8K_PEER_TYPE_ACCESSPOINT 2 + +static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mwl8k_cmd_update_stadb *cmd; + struct peer_capability_info *p; + u32 rates; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY); + memcpy(cmd->peer_addr, sta->addr, ETH_ALEN); + + p = &cmd->peer_info; + p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; + p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability); + p->ht_support = sta->ht_cap.ht_supported; + p->ht_caps = cpu_to_le16(sta->ht_cap.cap); + p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) | + ((sta->ht_cap.ampdu_density & 7) << 2); + if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) + rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; + else + rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; + legacy_rate_mask_to_array(p->legacy_rates, rates); + memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16); + p->interop = 1; + p->amsdu_enabled = 0; + + rc = mwl8k_post_cmd(hw, &cmd->header); + if (!rc) + rc = p->station_id; + kfree(cmd); + + return rc; +} + +static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *addr) +{ + struct mwl8k_cmd_update_stadb *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY); + memcpy(cmd->peer_addr, addr, ETH_ALEN); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + + +/* + * Interrupt handling. + */ +static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) +{ + struct ieee80211_hw *hw = dev_id; + struct mwl8k_priv *priv = hw->priv; + u32 status; + + status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + if (!status) + return IRQ_NONE; + + if (status & MWL8K_A2H_INT_TX_DONE) { + status &= ~MWL8K_A2H_INT_TX_DONE; + tasklet_schedule(&priv->poll_tx_task); + } + + if (status & MWL8K_A2H_INT_RX_READY) { + status &= ~MWL8K_A2H_INT_RX_READY; + tasklet_schedule(&priv->poll_rx_task); + } + + if (status & MWL8K_A2H_INT_BA_WATCHDOG) { + iowrite32(~MWL8K_A2H_INT_BA_WATCHDOG, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); + + atomic_inc(&priv->watchdog_event_pending); + status &= ~MWL8K_A2H_INT_BA_WATCHDOG; + ieee80211_queue_work(hw, &priv->watchdog_ba_handle); + } + + if (status) + iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + + if (status & MWL8K_A2H_INT_OPC_DONE) { + if (priv->hostcmd_wait != NULL) + complete(priv->hostcmd_wait); + } + + if (status & MWL8K_A2H_INT_QUEUE_EMPTY) { + if (!mutex_is_locked(&priv->fw_mutex) && + priv->radio_on && priv->pending_tx_pkts) + mwl8k_tx_start(priv); + } + + return IRQ_HANDLED; +} + +static void mwl8k_tx_poll(unsigned long data) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; + struct mwl8k_priv *priv = hw->priv; + int limit; + int i; + + limit = 32; + + spin_lock_bh(&priv->tx_lock); + + for (i = 0; i < mwl8k_tx_queues(priv); i++) + limit -= mwl8k_txq_reclaim(hw, i, limit, 0); + + if (!priv->pending_tx_pkts && priv->tx_wait != NULL) { + complete(priv->tx_wait); + priv->tx_wait = NULL; + } + + spin_unlock_bh(&priv->tx_lock); + + if (limit) { + writel(~MWL8K_A2H_INT_TX_DONE, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + } else { + tasklet_schedule(&priv->poll_tx_task); + } +} + +static void mwl8k_rx_poll(unsigned long data) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; + struct mwl8k_priv *priv = hw->priv; + int limit; + + limit = 32; + limit -= rxq_process(hw, 0, limit); + limit -= rxq_refill(hw, 0, limit); + + if (limit) { + writel(~MWL8K_A2H_INT_RX_READY, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + } else { + tasklet_schedule(&priv->poll_rx_task); + } +} + + +/* + * Core driver operations. + */ +static void mwl8k_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct mwl8k_priv *priv = hw->priv; + int index = skb_get_queue_mapping(skb); + + if (!priv->radio_on) { + wiphy_debug(hw->wiphy, + "dropped TX frame since radio disabled\n"); + dev_kfree_skb(skb); + return; + } + + mwl8k_txq_xmit(hw, index, control->sta, skb); +} + +static int mwl8k_start(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + int rc; + + rc = request_irq(priv->pdev->irq, mwl8k_interrupt, + IRQF_SHARED, MWL8K_NAME, hw); + if (rc) { + priv->irq = -1; + wiphy_err(hw->wiphy, "failed to register IRQ handler\n"); + return -EIO; + } + priv->irq = priv->pdev->irq; + + /* Enable TX reclaim and RX tasklets. */ + tasklet_enable(&priv->poll_tx_task); + tasklet_enable(&priv->poll_rx_task); + + /* Enable interrupts */ + iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); + iowrite32(MWL8K_A2H_EVENTS, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); + + rc = mwl8k_fw_lock(hw); + if (!rc) { + rc = mwl8k_cmd_radio_enable(hw); + + if (!priv->ap_fw) { + if (!rc) + rc = mwl8k_cmd_enable_sniffer(hw, 0); + + if (!rc) + rc = mwl8k_cmd_set_pre_scan(hw); + + if (!rc) + rc = mwl8k_cmd_set_post_scan(hw, + "\x00\x00\x00\x00\x00\x00"); + } + + if (!rc) + rc = mwl8k_cmd_set_rateadapt_mode(hw, 0); + + if (!rc) + rc = mwl8k_cmd_set_wmm_mode(hw, 0); + + mwl8k_fw_unlock(hw); + } + + if (rc) { + iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); + free_irq(priv->pdev->irq, hw); + priv->irq = -1; + tasklet_disable(&priv->poll_tx_task); + tasklet_disable(&priv->poll_rx_task); + } else { + ieee80211_wake_queues(hw); + } + + return rc; +} + +static void mwl8k_stop(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + int i; + + if (!priv->hw_restart_in_progress) + mwl8k_cmd_radio_disable(hw); + + ieee80211_stop_queues(hw); + + /* Disable interrupts */ + iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); + if (priv->irq != -1) { + free_irq(priv->pdev->irq, hw); + priv->irq = -1; + } + + /* Stop finalize join worker */ + cancel_work_sync(&priv->finalize_join_worker); + cancel_work_sync(&priv->watchdog_ba_handle); + if (priv->beacon_skb != NULL) + dev_kfree_skb(priv->beacon_skb); + + /* Stop TX reclaim and RX tasklets. */ + tasklet_disable(&priv->poll_tx_task); + tasklet_disable(&priv->poll_rx_task); + + /* Return all skbs to mac80211 */ + for (i = 0; i < mwl8k_tx_queues(priv); i++) + mwl8k_txq_reclaim(hw, i, INT_MAX, 1); +} + +static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image); + +static int mwl8k_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif; + u32 macids_supported; + int macid, rc; + struct mwl8k_device_info *di; + + /* + * Reject interface creation if sniffer mode is active, as + * STA operation is mutually exclusive with hardware sniffer + * mode. (Sniffer mode is only used on STA firmware.) + */ + if (priv->sniffer_enabled) { + wiphy_info(hw->wiphy, + "unable to create STA interface because sniffer mode is enabled\n"); + return -EINVAL; + } + + di = priv->device_info; + switch (vif->type) { + case NL80211_IFTYPE_AP: + if (!priv->ap_fw && di->fw_image_ap) { + /* we must load the ap fw to meet this request */ + if (!list_empty(&priv->vif_list)) + return -EBUSY; + rc = mwl8k_reload_firmware(hw, di->fw_image_ap); + if (rc) + return rc; + } + macids_supported = priv->ap_macids_supported; + break; + case NL80211_IFTYPE_STATION: + if (priv->ap_fw && di->fw_image_sta) { + if (!list_empty(&priv->vif_list)) { + wiphy_warn(hw->wiphy, "AP interface is running.\n" + "Adding STA interface for WDS"); + } else { + /* we must load the sta fw to + * meet this request. + */ + rc = mwl8k_reload_firmware(hw, + di->fw_image_sta); + if (rc) + return rc; + } + } + macids_supported = priv->sta_macids_supported; + break; + default: + return -EINVAL; + } + + macid = ffs(macids_supported & ~priv->macids_used); + if (!macid--) + return -EBUSY; + + /* Setup driver private area. */ + mwl8k_vif = MWL8K_VIF(vif); + memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); + mwl8k_vif->vif = vif; + mwl8k_vif->macid = macid; + mwl8k_vif->seqno = 0; + memcpy(mwl8k_vif->bssid, vif->addr, ETH_ALEN); + mwl8k_vif->is_hw_crypto_enabled = false; + + /* Set the mac address. */ + mwl8k_cmd_set_mac_addr(hw, vif, vif->addr); + + if (vif->type == NL80211_IFTYPE_AP) + mwl8k_cmd_set_new_stn_add_self(hw, vif); + + priv->macids_used |= 1 << mwl8k_vif->macid; + list_add_tail(&mwl8k_vif->list, &priv->vif_list); + + return 0; +} + +static void mwl8k_remove_vif(struct mwl8k_priv *priv, struct mwl8k_vif *vif) +{ + /* Has ieee80211_restart_hw re-added the removed interfaces? */ + if (!priv->macids_used) + return; + + priv->macids_used &= ~(1 << vif->macid); + list_del(&vif->list); +} + +static void mwl8k_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + + if (vif->type == NL80211_IFTYPE_AP) + mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); + + mwl8k_cmd_del_mac_addr(hw, vif, vif->addr); + + mwl8k_remove_vif(priv, mwl8k_vif); +} + +static void mwl8k_hw_restart_work(struct work_struct *work) +{ + struct mwl8k_priv *priv = + container_of(work, struct mwl8k_priv, fw_reload); + struct ieee80211_hw *hw = priv->hw; + struct mwl8k_device_info *di; + int rc; + + /* If some command is waiting for a response, clear it */ + if (priv->hostcmd_wait != NULL) { + complete(priv->hostcmd_wait); + priv->hostcmd_wait = NULL; + } + + priv->hw_restart_owner = current; + di = priv->device_info; + mwl8k_fw_lock(hw); + + if (priv->ap_fw) + rc = mwl8k_reload_firmware(hw, di->fw_image_ap); + else + rc = mwl8k_reload_firmware(hw, di->fw_image_sta); + + if (rc) + goto fail; + + priv->hw_restart_owner = NULL; + priv->hw_restart_in_progress = false; + + /* + * This unlock will wake up the queues and + * also opens the command path for other + * commands + */ + mwl8k_fw_unlock(hw); + + ieee80211_restart_hw(hw); + + wiphy_err(hw->wiphy, "Firmware restarted successfully\n"); + + return; +fail: + mwl8k_fw_unlock(hw); + + wiphy_err(hw->wiphy, "Firmware restart failed\n"); +} + +static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ieee80211_conf *conf = &hw->conf; + struct mwl8k_priv *priv = hw->priv; + int rc; + + rc = mwl8k_fw_lock(hw); + if (rc) + return rc; + + if (conf->flags & IEEE80211_CONF_IDLE) + rc = mwl8k_cmd_radio_disable(hw); + else + rc = mwl8k_cmd_radio_enable(hw); + if (rc) + goto out; + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + rc = mwl8k_cmd_set_rf_channel(hw, conf); + if (rc) + goto out; + } + + if (conf->power_level > 18) + conf->power_level = 18; + + if (priv->ap_fw) { + + if (conf->flags & IEEE80211_CONF_CHANGE_POWER) { + rc = mwl8k_cmd_tx_power(hw, conf, conf->power_level); + if (rc) + goto out; + } + + + } else { + rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level); + if (rc) + goto out; + rc = mwl8k_cmd_mimo_config(hw, 0x7, 0x7); + } + +out: + mwl8k_fw_unlock(hw); + + return rc; +} + +static void +mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + struct mwl8k_priv *priv = hw->priv; + u32 ap_legacy_rates = 0; + u8 ap_mcs_rates[16]; + int rc; + + if (mwl8k_fw_lock(hw)) + return; + + /* + * No need to capture a beacon if we're no longer associated. + */ + if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc) + priv->capture_beacon = false; + + /* + * Get the AP's legacy and MCS rates. + */ + if (vif->bss_conf.assoc) { + struct ieee80211_sta *ap; + + rcu_read_lock(); + + ap = ieee80211_find_sta(vif, vif->bss_conf.bssid); + if (ap == NULL) { + rcu_read_unlock(); + goto out; + } + + if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) { + ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ]; + } else { + ap_legacy_rates = + ap->supp_rates[IEEE80211_BAND_5GHZ] << 5; + } + memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16); + + rcu_read_unlock(); + + if (changed & BSS_CHANGED_ASSOC) { + if (!priv->ap_fw) { + rc = mwl8k_cmd_set_rate(hw, vif, + ap_legacy_rates, + ap_mcs_rates); + if (rc) + goto out; + + rc = mwl8k_cmd_use_fixed_rate_sta(hw); + if (rc) + goto out; + } else { + int idx; + int rate; + + /* Use AP firmware specific rate command. + */ + idx = ffs(vif->bss_conf.basic_rates); + if (idx) + idx--; + + if (hw->conf.chandef.chan->band == + IEEE80211_BAND_2GHZ) + rate = mwl8k_rates_24[idx].hw_value; + else + rate = mwl8k_rates_50[idx].hw_value; + + mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); + } + } + } + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rc = mwl8k_set_radio_preamble(hw, + vif->bss_conf.use_short_preamble); + if (rc) + goto out; + } + + if ((changed & BSS_CHANGED_ERP_SLOT) && !priv->ap_fw) { + rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot); + if (rc) + goto out; + } + + if (vif->bss_conf.assoc && !priv->ap_fw && + (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_HT))) { + rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates); + if (rc) + goto out; + } + + if (vif->bss_conf.assoc && + (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) { + /* + * Finalize the join. Tell rx handler to process + * next beacon from our BSSID. + */ + memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN); + priv->capture_beacon = true; + } + +out: + mwl8k_fw_unlock(hw); +} + +static void +mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + int rc; + + if (mwl8k_fw_lock(hw)) + return; + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rc = mwl8k_set_radio_preamble(hw, + vif->bss_conf.use_short_preamble); + if (rc) + goto out; + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + int idx; + int rate; + + /* + * Use lowest supported basic rate for multicasts + * and management frames (such as probe responses -- + * beacons will always go out at 1 Mb/s). + */ + idx = ffs(vif->bss_conf.basic_rates); + if (idx) + idx--; + + if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) + rate = mwl8k_rates_24[idx].hw_value; + else + rate = mwl8k_rates_50[idx].hw_value; + + mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); + } + + if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) { + struct sk_buff *skb; + + skb = ieee80211_beacon_get(hw, vif); + if (skb != NULL) { + mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len); + kfree_skb(skb); + } + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) + mwl8k_cmd_bss_start(hw, vif, info->enable_beacon); + +out: + mwl8k_fw_unlock(hw); +} + +static void +mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + if (vif->type == NL80211_IFTYPE_STATION) + mwl8k_bss_info_changed_sta(hw, vif, info, changed); + if (vif->type == NL80211_IFTYPE_AP) + mwl8k_bss_info_changed_ap(hw, vif, info, changed); +} + +static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct mwl8k_cmd_pkt *cmd; + + /* + * Synthesize and return a command packet that programs the + * hardware multicast address filter. At this point we don't + * know whether FIF_ALLMULTI is being requested, but if it is, + * we'll end up throwing this packet away and creating a new + * one in mwl8k_configure_filter(). + */ + cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_list); + + return (unsigned long)cmd; +} + +static int +mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags) +{ + struct mwl8k_priv *priv = hw->priv; + + /* + * Hardware sniffer mode is mutually exclusive with STA + * operation, so refuse to enable sniffer mode if a STA + * interface is active. + */ + if (!list_empty(&priv->vif_list)) { + if (net_ratelimit()) + wiphy_info(hw->wiphy, + "not enabling sniffer mode because STA interface is active\n"); + return 0; + } + + if (!priv->sniffer_enabled) { + if (mwl8k_cmd_enable_sniffer(hw, 1)) + return 0; + priv->sniffer_enabled = true; + } + + *total_flags &= FIF_ALLMULTI | + FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL | + FIF_OTHER_BSS; + + return 1; +} + +static struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv) +{ + if (!list_empty(&priv->vif_list)) + return list_entry(priv->vif_list.next, struct mwl8k_vif, list); + + return NULL; +} + +static void mwl8k_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_pkt *cmd = (void *)(unsigned long)multicast; + + /* + * AP firmware doesn't allow fine-grained control over + * the receive filter. + */ + if (priv->ap_fw) { + *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; + kfree(cmd); + return; + } + + /* + * Enable hardware sniffer mode if FIF_CONTROL or + * FIF_OTHER_BSS is requested. + */ + if (*total_flags & (FIF_CONTROL | FIF_OTHER_BSS) && + mwl8k_configure_filter_sniffer(hw, changed_flags, total_flags)) { + kfree(cmd); + return; + } + + /* Clear unsupported feature flags */ + *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; + + if (mwl8k_fw_lock(hw)) { + kfree(cmd); + return; + } + + if (priv->sniffer_enabled) { + mwl8k_cmd_enable_sniffer(hw, 0); + priv->sniffer_enabled = false; + } + + if (changed_flags & FIF_BCN_PRBRESP_PROMISC) { + if (*total_flags & FIF_BCN_PRBRESP_PROMISC) { + /* + * Disable the BSS filter. + */ + mwl8k_cmd_set_pre_scan(hw); + } else { + struct mwl8k_vif *mwl8k_vif; + const u8 *bssid; + + /* + * Enable the BSS filter. + * + * If there is an active STA interface, use that + * interface's BSSID, otherwise use a dummy one + * (where the OUI part needs to be nonzero for + * the BSSID to be accepted by POST_SCAN). + */ + mwl8k_vif = mwl8k_first_vif(priv); + if (mwl8k_vif != NULL) + bssid = mwl8k_vif->vif->bss_conf.bssid; + else + bssid = "\x01\x00\x00\x00\x00\x00"; + + mwl8k_cmd_set_post_scan(hw, bssid); + } + } + + /* + * If FIF_ALLMULTI is being requested, throw away the command + * packet that ->prepare_multicast() built and replace it with + * a command packet that enables reception of all multicast + * packets. + */ + if (*total_flags & FIF_ALLMULTI) { + kfree(cmd); + cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, NULL); + } + + if (cmd != NULL) { + mwl8k_post_cmd(hw, cmd); + kfree(cmd); + } + + mwl8k_fw_unlock(hw); +} + +static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + return mwl8k_cmd_set_rts_threshold(hw, value); +} + +static int mwl8k_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mwl8k_priv *priv = hw->priv; + + if (priv->ap_fw) + return mwl8k_cmd_set_new_stn_del(hw, vif, sta->addr); + else + return mwl8k_cmd_update_stadb_del(hw, vif, sta->addr); +} + +static int mwl8k_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mwl8k_priv *priv = hw->priv; + int ret; + int i; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + struct ieee80211_key_conf *key; + + if (!priv->ap_fw) { + ret = mwl8k_cmd_update_stadb_add(hw, vif, sta); + if (ret >= 0) { + MWL8K_STA(sta)->peer_id = ret; + if (sta->ht_cap.ht_supported) + MWL8K_STA(sta)->is_ampdu_allowed = true; + ret = 0; + } + + } else { + ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta); + } + + for (i = 0; i < NUM_WEP_KEYS; i++) { + key = IEEE80211_KEY_CONF(mwl8k_vif->wep_key_conf[i].key); + if (mwl8k_vif->wep_key_conf[i].enabled) + mwl8k_set_key(hw, SET_KEY, vif, sta, key); + } + return ret; +} + +static int mwl8k_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mwl8k_priv *priv = hw->priv; + int rc; + + rc = mwl8k_fw_lock(hw); + if (!rc) { + BUG_ON(queue > MWL8K_TX_WMM_QUEUES - 1); + memcpy(&priv->wmm_params[queue], params, sizeof(*params)); + + if (!priv->wmm_enabled) + rc = mwl8k_cmd_set_wmm_mode(hw, 1); + + if (!rc) { + int q = MWL8K_TX_WMM_QUEUES - 1 - queue; + rc = mwl8k_cmd_set_edca_params(hw, q, + params->cw_min, + params->cw_max, + params->aifs, + params->txop); + } + + mwl8k_fw_unlock(hw); + } + + return rc; +} + +static int mwl8k_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + return mwl8k_cmd_get_stat(hw, stats); +} + +static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct mwl8k_priv *priv = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_supported_band *sband; + + if (priv->ap_fw) { + sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + + if (sband && idx >= sband->n_channels) { + idx -= sband->n_channels; + sband = NULL; + } + + if (!sband) + sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ]; + + if (!sband || idx >= sband->n_channels) + return -ENOENT; + + memcpy(survey, &priv->survey[idx], sizeof(*survey)); + survey->channel = &sband->channels[idx]; + + return 0; + } + + if (idx != 0) + return -ENOENT; + + survey->channel = conf->chandef.chan; + survey->filled = SURVEY_INFO_NOISE_DBM; + survey->noise = priv->noise; + + return 0; +} + +#define MAX_AMPDU_ATTEMPTS 5 + +static int +mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size, bool amsdu) +{ + + int i, rc = 0; + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_ampdu_stream *stream; + u8 *addr = sta->addr, idx; + struct mwl8k_sta *sta_info = MWL8K_STA(sta); + + if (!ieee80211_hw_check(hw, AMPDU_AGGREGATION)) + return -ENOTSUPP; + + spin_lock(&priv->stream_lock); + stream = mwl8k_lookup_stream(hw, addr, tid); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + break; + case IEEE80211_AMPDU_TX_START: + /* By the time we get here the hw queues may contain outgoing + * packets for this RA/TID that are not part of this BA + * session. The hw will assign sequence numbers to these + * packets as they go out. So if we query the hw for its next + * sequence number and use that for the SSN here, it may end up + * being wrong, which will lead to sequence number mismatch at + * the recipient. To avoid this, we reset the sequence number + * to O for the first MPDU in this BA stream. + */ + *ssn = 0; + if (stream == NULL) { + /* This means that somebody outside this driver called + * ieee80211_start_tx_ba_session. This is unexpected + * because we do our own rate control. Just warn and + * move on. + */ + wiphy_warn(hw->wiphy, "Unexpected call to %s. " + "Proceeding anyway.\n", __func__); + stream = mwl8k_add_stream(hw, sta, tid); + } + if (stream == NULL) { + wiphy_debug(hw->wiphy, "no free AMPDU streams\n"); + rc = -EBUSY; + break; + } + stream->state = AMPDU_STREAM_IN_PROGRESS; + + /* Release the lock before we do the time consuming stuff */ + spin_unlock(&priv->stream_lock); + for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) { + + /* Check if link is still valid */ + if (!sta_info->is_ampdu_allowed) { + spin_lock(&priv->stream_lock); + mwl8k_remove_stream(hw, stream); + spin_unlock(&priv->stream_lock); + return -EBUSY; + } + + rc = mwl8k_check_ba(hw, stream, vif); + + /* If HW restart is in progress mwl8k_post_cmd will + * return -EBUSY. Avoid retrying mwl8k_check_ba in + * such cases + */ + if (!rc || rc == -EBUSY) + break; + /* + * HW queues take time to be flushed, give them + * sufficient time + */ + + msleep(1000); + } + spin_lock(&priv->stream_lock); + if (rc) { + wiphy_err(hw->wiphy, "Stream for tid %d busy after %d" + " attempts\n", tid, MAX_AMPDU_ATTEMPTS); + mwl8k_remove_stream(hw, stream); + rc = -EBUSY; + break; + } + ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + if (stream) { + if (stream->state == AMPDU_STREAM_ACTIVE) { + idx = stream->idx; + spin_unlock(&priv->stream_lock); + mwl8k_destroy_ba(hw, idx); + spin_lock(&priv->stream_lock); + } + mwl8k_remove_stream(hw, stream); + } + ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + BUG_ON(stream == NULL); + BUG_ON(stream->state != AMPDU_STREAM_IN_PROGRESS); + spin_unlock(&priv->stream_lock); + rc = mwl8k_create_ba(hw, stream, buf_size, vif); + spin_lock(&priv->stream_lock); + if (!rc) + stream->state = AMPDU_STREAM_ACTIVE; + else { + idx = stream->idx; + spin_unlock(&priv->stream_lock); + mwl8k_destroy_ba(hw, idx); + spin_lock(&priv->stream_lock); + wiphy_debug(hw->wiphy, + "Failed adding stream for sta %pM tid %d\n", + addr, tid); + mwl8k_remove_stream(hw, stream); + } + break; + + default: + rc = -ENOTSUPP; + } + + spin_unlock(&priv->stream_lock); + return rc; +} + +static void mwl8k_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) +{ + struct mwl8k_priv *priv = hw->priv; + u8 tmp; + + if (!priv->ap_fw) + return; + + /* clear all stats */ + priv->channel_time = 0; + ioread32(priv->regs + BBU_RXRDY_CNT_REG); + ioread32(priv->regs + NOK_CCA_CNT_REG); + mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp); + + priv->sw_scan_start = true; +} + +static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mwl8k_priv *priv = hw->priv; + u8 tmp; + + if (!priv->ap_fw) + return; + + priv->sw_scan_start = false; + + /* clear all stats */ + priv->channel_time = 0; + ioread32(priv->regs + BBU_RXRDY_CNT_REG); + ioread32(priv->regs + NOK_CCA_CNT_REG); + mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp); +} + +static const struct ieee80211_ops mwl8k_ops = { + .tx = mwl8k_tx, + .start = mwl8k_start, + .stop = mwl8k_stop, + .add_interface = mwl8k_add_interface, + .remove_interface = mwl8k_remove_interface, + .config = mwl8k_config, + .bss_info_changed = mwl8k_bss_info_changed, + .prepare_multicast = mwl8k_prepare_multicast, + .configure_filter = mwl8k_configure_filter, + .set_key = mwl8k_set_key, + .set_rts_threshold = mwl8k_set_rts_threshold, + .sta_add = mwl8k_sta_add, + .sta_remove = mwl8k_sta_remove, + .conf_tx = mwl8k_conf_tx, + .get_stats = mwl8k_get_stats, + .get_survey = mwl8k_get_survey, + .ampdu_action = mwl8k_ampdu_action, + .sw_scan_start = mwl8k_sw_scan_start, + .sw_scan_complete = mwl8k_sw_scan_complete, +}; + +static void mwl8k_finalize_join_worker(struct work_struct *work) +{ + struct mwl8k_priv *priv = + container_of(work, struct mwl8k_priv, finalize_join_worker); + struct sk_buff *skb = priv->beacon_skb; + struct ieee80211_mgmt *mgmt = (void *)skb->data; + int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable); + const u8 *tim = cfg80211_find_ie(WLAN_EID_TIM, + mgmt->u.beacon.variable, len); + int dtim_period = 1; + + if (tim && tim[1] >= 2) + dtim_period = tim[3]; + + mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, dtim_period); + + dev_kfree_skb(skb); + priv->beacon_skb = NULL; +} + +enum { + MWL8363 = 0, + MWL8687, + MWL8366, + MWL8764, +}; + +#define MWL8K_8366_AP_FW_API 3 +#define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw" +#define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api) + +#define MWL8K_8764_AP_FW_API 1 +#define _MWL8K_8764_AP_FW(api) "mwl8k/fmimage_8764_ap-" #api ".fw" +#define MWL8K_8764_AP_FW(api) _MWL8K_8764_AP_FW(api) + +static struct mwl8k_device_info mwl8k_info_tbl[] = { + [MWL8363] = { + .part_name = "88w8363", + .helper_image = "mwl8k/helper_8363.fw", + .fw_image_sta = "mwl8k/fmimage_8363.fw", + }, + [MWL8687] = { + .part_name = "88w8687", + .helper_image = "mwl8k/helper_8687.fw", + .fw_image_sta = "mwl8k/fmimage_8687.fw", + }, + [MWL8366] = { + .part_name = "88w8366", + .helper_image = "mwl8k/helper_8366.fw", + .fw_image_sta = "mwl8k/fmimage_8366.fw", + .fw_image_ap = MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API), + .fw_api_ap = MWL8K_8366_AP_FW_API, + .ap_rxd_ops = &rxd_ap_ops, + }, + [MWL8764] = { + .part_name = "88w8764", + .fw_image_ap = MWL8K_8764_AP_FW(MWL8K_8764_AP_FW_API), + .fw_api_ap = MWL8K_8764_AP_FW_API, + .ap_rxd_ops = &rxd_ap_ops, + }, +}; + +MODULE_FIRMWARE("mwl8k/helper_8363.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8363.fw"); +MODULE_FIRMWARE("mwl8k/helper_8687.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8687.fw"); +MODULE_FIRMWARE("mwl8k/helper_8366.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8366.fw"); +MODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API)); + +static const struct pci_device_id mwl8k_pci_id_table[] = { + { PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, }, + { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, }, + { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, }, + { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, }, + { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, }, + { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, }, + { PCI_VDEVICE(MARVELL, 0x2a41), .driver_data = MWL8366, }, + { PCI_VDEVICE(MARVELL, 0x2a42), .driver_data = MWL8366, }, + { PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, }, + { PCI_VDEVICE(MARVELL, 0x2b36), .driver_data = MWL8764, }, + { }, +}; +MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); + +static int mwl8k_request_alt_fw(struct mwl8k_priv *priv) +{ + int rc; + printk(KERN_ERR "%s: Error requesting preferred fw %s.\n" + "Trying alternative firmware %s\n", pci_name(priv->pdev), + priv->fw_pref, priv->fw_alt); + rc = mwl8k_request_fw(priv, priv->fw_alt, &priv->fw_ucode, true); + if (rc) { + printk(KERN_ERR "%s: Error requesting alt fw %s\n", + pci_name(priv->pdev), priv->fw_alt); + return rc; + } + return 0; +} + +static int mwl8k_firmware_load_success(struct mwl8k_priv *priv); +static void mwl8k_fw_state_machine(const struct firmware *fw, void *context) +{ + struct mwl8k_priv *priv = context; + struct mwl8k_device_info *di = priv->device_info; + int rc; + + switch (priv->fw_state) { + case FW_STATE_INIT: + if (!fw) { + printk(KERN_ERR "%s: Error requesting helper fw %s\n", + pci_name(priv->pdev), di->helper_image); + goto fail; + } + priv->fw_helper = fw; + rc = mwl8k_request_fw(priv, priv->fw_pref, &priv->fw_ucode, + true); + if (rc && priv->fw_alt) { + rc = mwl8k_request_alt_fw(priv); + if (rc) + goto fail; + priv->fw_state = FW_STATE_LOADING_ALT; + } else if (rc) + goto fail; + else + priv->fw_state = FW_STATE_LOADING_PREF; + break; + + case FW_STATE_LOADING_PREF: + if (!fw) { + if (priv->fw_alt) { + rc = mwl8k_request_alt_fw(priv); + if (rc) + goto fail; + priv->fw_state = FW_STATE_LOADING_ALT; + } else + goto fail; + } else { + priv->fw_ucode = fw; + rc = mwl8k_firmware_load_success(priv); + if (rc) + goto fail; + else + complete(&priv->firmware_loading_complete); + } + break; + + case FW_STATE_LOADING_ALT: + if (!fw) { + printk(KERN_ERR "%s: Error requesting alt fw %s\n", + pci_name(priv->pdev), di->helper_image); + goto fail; + } + priv->fw_ucode = fw; + rc = mwl8k_firmware_load_success(priv); + if (rc) + goto fail; + else + complete(&priv->firmware_loading_complete); + break; + + default: + printk(KERN_ERR "%s: Unexpected firmware loading state: %d\n", + MWL8K_NAME, priv->fw_state); + BUG_ON(1); + } + + return; + +fail: + priv->fw_state = FW_STATE_ERROR; + complete(&priv->firmware_loading_complete); + device_release_driver(&priv->pdev->dev); + mwl8k_release_firmware(priv); +} + +#define MAX_RESTART_ATTEMPTS 1 +static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image, + bool nowait) +{ + struct mwl8k_priv *priv = hw->priv; + int rc; + int count = MAX_RESTART_ATTEMPTS; + +retry: + /* Reset firmware and hardware */ + mwl8k_hw_reset(priv); + + /* Ask userland hotplug daemon for the device firmware */ + rc = mwl8k_request_firmware(priv, fw_image, nowait); + if (rc) { + wiphy_err(hw->wiphy, "Firmware files not found\n"); + return rc; + } + + if (nowait) + return rc; + + /* Load firmware into hardware */ + rc = mwl8k_load_firmware(hw); + if (rc) + wiphy_err(hw->wiphy, "Cannot start firmware\n"); + + /* Reclaim memory once firmware is successfully loaded */ + mwl8k_release_firmware(priv); + + if (rc && count) { + /* FW did not start successfully; + * lets try one more time + */ + count--; + wiphy_err(hw->wiphy, "Trying to reload the firmware again\n"); + msleep(20); + goto retry; + } + + return rc; +} + +static int mwl8k_init_txqs(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + int rc = 0; + int i; + + for (i = 0; i < mwl8k_tx_queues(priv); i++) { + rc = mwl8k_txq_init(hw, i); + if (rc) + break; + if (priv->ap_fw) + iowrite32(priv->txq[i].txd_dma, + priv->sram + priv->txq_offset[i]); + } + return rc; +} + +/* initialize hw after successfully loading a firmware image */ +static int mwl8k_probe_hw(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + int rc = 0; + int i; + + if (priv->ap_fw) { + priv->rxd_ops = priv->device_info->ap_rxd_ops; + if (priv->rxd_ops == NULL) { + wiphy_err(hw->wiphy, + "Driver does not have AP firmware image support for this hardware\n"); + rc = -ENOENT; + goto err_stop_firmware; + } + } else { + priv->rxd_ops = &rxd_sta_ops; + } + + priv->sniffer_enabled = false; + priv->wmm_enabled = false; + priv->pending_tx_pkts = 0; + atomic_set(&priv->watchdog_event_pending, 0); + + rc = mwl8k_rxq_init(hw, 0); + if (rc) + goto err_stop_firmware; + rxq_refill(hw, 0, INT_MAX); + + /* For the sta firmware, we need to know the dma addresses of tx queues + * before sending MWL8K_CMD_GET_HW_SPEC. So we must initialize them + * prior to issuing this command. But for the AP case, we learn the + * total number of queues from the result CMD_GET_HW_SPEC, so for this + * case we must initialize the tx queues after. + */ + priv->num_ampdu_queues = 0; + if (!priv->ap_fw) { + rc = mwl8k_init_txqs(hw); + if (rc) + goto err_free_queues; + } + + iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); + iowrite32(MWL8K_A2H_INT_TX_DONE|MWL8K_A2H_INT_RX_READY| + MWL8K_A2H_INT_BA_WATCHDOG, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); + iowrite32(MWL8K_A2H_INT_OPC_DONE, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); + + rc = request_irq(priv->pdev->irq, mwl8k_interrupt, + IRQF_SHARED, MWL8K_NAME, hw); + if (rc) { + wiphy_err(hw->wiphy, "failed to register IRQ handler\n"); + goto err_free_queues; + } + + /* + * When hw restart is requested, + * mac80211 will take care of clearing + * the ampdu streams, so do not clear + * the ampdu state here + */ + if (!priv->hw_restart_in_progress) + memset(priv->ampdu, 0, sizeof(priv->ampdu)); + + /* + * Temporarily enable interrupts. Initial firmware host + * commands use interrupts and avoid polling. Disable + * interrupts when done. + */ + iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); + + /* Get config data, mac addrs etc */ + if (priv->ap_fw) { + rc = mwl8k_cmd_get_hw_spec_ap(hw); + if (!rc) + rc = mwl8k_init_txqs(hw); + if (!rc) + rc = mwl8k_cmd_set_hw_spec(hw); + } else { + rc = mwl8k_cmd_get_hw_spec_sta(hw); + } + if (rc) { + wiphy_err(hw->wiphy, "Cannot initialise firmware\n"); + goto err_free_irq; + } + + /* Turn radio off */ + rc = mwl8k_cmd_radio_disable(hw); + if (rc) { + wiphy_err(hw->wiphy, "Cannot disable\n"); + goto err_free_irq; + } + + /* Clear MAC address */ + rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00"); + if (rc) { + wiphy_err(hw->wiphy, "Cannot clear MAC address\n"); + goto err_free_irq; + } + + /* Configure Antennas */ + rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3); + if (rc) + wiphy_warn(hw->wiphy, "failed to set # of RX antennas"); + rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7); + if (rc) + wiphy_warn(hw->wiphy, "failed to set # of TX antennas"); + + + /* Disable interrupts */ + iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); + free_irq(priv->pdev->irq, hw); + + wiphy_info(hw->wiphy, "%s v%d, %pm, %s firmware %u.%u.%u.%u\n", + priv->device_info->part_name, + priv->hw_rev, hw->wiphy->perm_addr, + priv->ap_fw ? "AP" : "STA", + (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff, + (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff); + + return 0; + +err_free_irq: + iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); + free_irq(priv->pdev->irq, hw); + +err_free_queues: + for (i = 0; i < mwl8k_tx_queues(priv); i++) + mwl8k_txq_deinit(hw, i); + mwl8k_rxq_deinit(hw, 0); + +err_stop_firmware: + mwl8k_hw_reset(priv); + + return rc; +} + +/* + * invoke mwl8k_reload_firmware to change the firmware image after the device + * has already been registered + */ +static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image) +{ + int i, rc = 0; + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *vif, *tmp_vif; + + mwl8k_stop(hw); + mwl8k_rxq_deinit(hw, 0); + + /* + * All the existing interfaces are re-added by the ieee80211_reconfig; + * which means driver should remove existing interfaces before calling + * ieee80211_restart_hw + */ + if (priv->hw_restart_in_progress) + list_for_each_entry_safe(vif, tmp_vif, &priv->vif_list, list) + mwl8k_remove_vif(priv, vif); + + for (i = 0; i < mwl8k_tx_queues(priv); i++) + mwl8k_txq_deinit(hw, i); + + rc = mwl8k_init_firmware(hw, fw_image, false); + if (rc) + goto fail; + + rc = mwl8k_probe_hw(hw); + if (rc) + goto fail; + + if (priv->hw_restart_in_progress) + return rc; + + rc = mwl8k_start(hw); + if (rc) + goto fail; + + rc = mwl8k_config(hw, ~0); + if (rc) + goto fail; + + for (i = 0; i < MWL8K_TX_WMM_QUEUES; i++) { + rc = mwl8k_conf_tx(hw, NULL, i, &priv->wmm_params[i]); + if (rc) + goto fail; + } + + return rc; + +fail: + printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n"); + return rc; +} + +static const struct ieee80211_iface_limit ap_if_limits[] = { + { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) }, +}; + +static const struct ieee80211_iface_combination ap_if_comb = { + .limits = ap_if_limits, + .n_limits = ARRAY_SIZE(ap_if_limits), + .max_interfaces = 8, + .num_different_channels = 1, +}; + + +static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) +{ + struct ieee80211_hw *hw = priv->hw; + int i, rc; + + rc = mwl8k_load_firmware(hw); + mwl8k_release_firmware(priv); + if (rc) { + wiphy_err(hw->wiphy, "Cannot start firmware\n"); + return rc; + } + + /* + * Extra headroom is the size of the required DMA header + * minus the size of the smallest 802.11 frame (CTS frame). + */ + hw->extra_tx_headroom = + sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts); + + hw->extra_tx_headroom -= priv->ap_fw ? REDUCED_TX_HEADROOM : 0; + + hw->queues = MWL8K_TX_WMM_QUEUES; + + /* Set rssi values to dBm */ + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, HAS_RATE_CONTROL); + + /* + * Ask mac80211 to not to trigger PS mode + * based on PM bit of incoming frames. + */ + if (priv->ap_fw) + ieee80211_hw_set(hw, AP_LINK_PS); + + hw->vif_data_size = sizeof(struct mwl8k_vif); + hw->sta_data_size = sizeof(struct mwl8k_sta); + + priv->macids_used = 0; + INIT_LIST_HEAD(&priv->vif_list); + + /* Set default radio state and preamble */ + priv->radio_on = false; + priv->radio_short_preamble = false; + + /* Finalize join worker */ + INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); + /* Handle watchdog ba events */ + INIT_WORK(&priv->watchdog_ba_handle, mwl8k_watchdog_ba_events); + /* To reload the firmware if it crashes */ + INIT_WORK(&priv->fw_reload, mwl8k_hw_restart_work); + + /* TX reclaim and RX tasklets. */ + tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw); + tasklet_disable(&priv->poll_tx_task); + tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw); + tasklet_disable(&priv->poll_rx_task); + + /* Power management cookie */ + priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma); + if (priv->cookie == NULL) + return -ENOMEM; + + mutex_init(&priv->fw_mutex); + priv->fw_mutex_owner = NULL; + priv->fw_mutex_depth = 0; + priv->hostcmd_wait = NULL; + + spin_lock_init(&priv->tx_lock); + + spin_lock_init(&priv->stream_lock); + + priv->tx_wait = NULL; + + rc = mwl8k_probe_hw(hw); + if (rc) + goto err_free_cookie; + + hw->wiphy->interface_modes = 0; + + if (priv->ap_macids_supported || priv->device_info->fw_image_ap) { + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); + hw->wiphy->iface_combinations = &ap_if_comb; + hw->wiphy->n_iface_combinations = 1; + } + + if (priv->sta_macids_supported || priv->device_info->fw_image_sta) + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); + + rc = ieee80211_register_hw(hw); + if (rc) { + wiphy_err(hw->wiphy, "Cannot register device\n"); + goto err_unprobe_hw; + } + + return 0; + +err_unprobe_hw: + for (i = 0; i < mwl8k_tx_queues(priv); i++) + mwl8k_txq_deinit(hw, i); + mwl8k_rxq_deinit(hw, 0); + +err_free_cookie: + if (priv->cookie != NULL) + pci_free_consistent(priv->pdev, 4, + priv->cookie, priv->cookie_dma); + + return rc; +} +static int mwl8k_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + static int printed_version; + struct ieee80211_hw *hw; + struct mwl8k_priv *priv; + struct mwl8k_device_info *di; + int rc; + + if (!printed_version) { + printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION); + printed_version = 1; + } + + + rc = pci_enable_device(pdev); + if (rc) { + printk(KERN_ERR "%s: Cannot enable new PCI device\n", + MWL8K_NAME); + return rc; + } + + rc = pci_request_regions(pdev, MWL8K_NAME); + if (rc) { + printk(KERN_ERR "%s: Cannot obtain PCI resources\n", + MWL8K_NAME); + goto err_disable_device; + } + + pci_set_master(pdev); + + + hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops); + if (hw == NULL) { + printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME); + rc = -ENOMEM; + goto err_free_reg; + } + + SET_IEEE80211_DEV(hw, &pdev->dev); + pci_set_drvdata(pdev, hw); + + priv = hw->priv; + priv->hw = hw; + priv->pdev = pdev; + priv->device_info = &mwl8k_info_tbl[id->driver_data]; + + if (id->driver_data == MWL8764) + priv->is_8764 = true; + + priv->sram = pci_iomap(pdev, 0, 0x10000); + if (priv->sram == NULL) { + wiphy_err(hw->wiphy, "Cannot map device SRAM\n"); + rc = -EIO; + goto err_iounmap; + } + + /* + * If BAR0 is a 32 bit BAR, the register BAR will be BAR1. + * If BAR0 is a 64 bit BAR, the register BAR will be BAR2. + */ + priv->regs = pci_iomap(pdev, 1, 0x10000); + if (priv->regs == NULL) { + priv->regs = pci_iomap(pdev, 2, 0x10000); + if (priv->regs == NULL) { + wiphy_err(hw->wiphy, "Cannot map device registers\n"); + rc = -EIO; + goto err_iounmap; + } + } + + /* + * Choose the initial fw image depending on user input. If a second + * image is available, make it the alternative image that will be + * loaded if the first one fails. + */ + init_completion(&priv->firmware_loading_complete); + di = priv->device_info; + if (ap_mode_default && di->fw_image_ap) { + priv->fw_pref = di->fw_image_ap; + priv->fw_alt = di->fw_image_sta; + } else if (!ap_mode_default && di->fw_image_sta) { + priv->fw_pref = di->fw_image_sta; + priv->fw_alt = di->fw_image_ap; + } else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) { + printk(KERN_WARNING "AP fw is unavailable. Using STA fw."); + priv->fw_pref = di->fw_image_sta; + } else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) { + printk(KERN_WARNING "STA fw is unavailable. Using AP fw."); + priv->fw_pref = di->fw_image_ap; + } + rc = mwl8k_init_firmware(hw, priv->fw_pref, true); + if (rc) + goto err_stop_firmware; + + priv->hw_restart_in_progress = false; + + priv->running_bsses = 0; + + return rc; + +err_stop_firmware: + mwl8k_hw_reset(priv); + +err_iounmap: + if (priv->regs != NULL) + pci_iounmap(pdev, priv->regs); + + if (priv->sram != NULL) + pci_iounmap(pdev, priv->sram); + + ieee80211_free_hw(hw); + +err_free_reg: + pci_release_regions(pdev); + +err_disable_device: + pci_disable_device(pdev); + + return rc; +} + +static void mwl8k_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct mwl8k_priv *priv; + int i; + + if (hw == NULL) + return; + priv = hw->priv; + + wait_for_completion(&priv->firmware_loading_complete); + + if (priv->fw_state == FW_STATE_ERROR) { + mwl8k_hw_reset(priv); + goto unmap; + } + + ieee80211_stop_queues(hw); + + ieee80211_unregister_hw(hw); + + /* Remove TX reclaim and RX tasklets. */ + tasklet_kill(&priv->poll_tx_task); + tasklet_kill(&priv->poll_rx_task); + + /* Stop hardware */ + mwl8k_hw_reset(priv); + + /* Return all skbs to mac80211 */ + for (i = 0; i < mwl8k_tx_queues(priv); i++) + mwl8k_txq_reclaim(hw, i, INT_MAX, 1); + + for (i = 0; i < mwl8k_tx_queues(priv); i++) + mwl8k_txq_deinit(hw, i); + + mwl8k_rxq_deinit(hw, 0); + + pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma); + +unmap: + pci_iounmap(pdev, priv->regs); + pci_iounmap(pdev, priv->sram); + ieee80211_free_hw(hw); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static struct pci_driver mwl8k_driver = { + .name = MWL8K_NAME, + .id_table = mwl8k_pci_id_table, + .probe = mwl8k_probe, + .remove = mwl8k_remove, +}; + +module_pci_driver(mwl8k_driver); + +MODULE_DESCRIPTION(MWL8K_DESC); +MODULE_VERSION(MWL8K_VERSION); +MODULE_AUTHOR("Lennert Buytenhek "); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c deleted file mode 100644 index 30e3aaae32e2..000000000000 --- a/drivers/net/wireless/mwl8k.c +++ /dev/null @@ -1,6346 +0,0 @@ -/* - * drivers/net/wireless/mwl8k.c - * Driver for Marvell TOPDOG 802.11 Wireless cards - * - * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver" -#define MWL8K_NAME KBUILD_MODNAME -#define MWL8K_VERSION "0.13" - -/* Module parameters */ -static bool ap_mode_default; -module_param(ap_mode_default, bool, 0); -MODULE_PARM_DESC(ap_mode_default, - "Set to 1 to make ap mode the default instead of sta mode"); - -/* Register definitions */ -#define MWL8K_HIU_GEN_PTR 0x00000c10 -#define MWL8K_MODE_STA 0x0000005a -#define MWL8K_MODE_AP 0x000000a5 -#define MWL8K_HIU_INT_CODE 0x00000c14 -#define MWL8K_FWSTA_READY 0xf0f1f2f4 -#define MWL8K_FWAP_READY 0xf1f2f4a5 -#define MWL8K_INT_CODE_CMD_FINISHED 0x00000005 -#define MWL8K_HIU_SCRATCH 0x00000c40 - -/* Host->device communications */ -#define MWL8K_HIU_H2A_INTERRUPT_EVENTS 0x00000c18 -#define MWL8K_HIU_H2A_INTERRUPT_STATUS 0x00000c1c -#define MWL8K_HIU_H2A_INTERRUPT_MASK 0x00000c20 -#define MWL8K_HIU_H2A_INTERRUPT_CLEAR_SEL 0x00000c24 -#define MWL8K_HIU_H2A_INTERRUPT_STATUS_MASK 0x00000c28 -#define MWL8K_H2A_INT_DUMMY (1 << 20) -#define MWL8K_H2A_INT_RESET (1 << 15) -#define MWL8K_H2A_INT_DOORBELL (1 << 1) -#define MWL8K_H2A_INT_PPA_READY (1 << 0) - -/* Device->host communications */ -#define MWL8K_HIU_A2H_INTERRUPT_EVENTS 0x00000c2c -#define MWL8K_HIU_A2H_INTERRUPT_STATUS 0x00000c30 -#define MWL8K_HIU_A2H_INTERRUPT_MASK 0x00000c34 -#define MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL 0x00000c38 -#define MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK 0x00000c3c -#define MWL8K_A2H_INT_DUMMY (1 << 20) -#define MWL8K_A2H_INT_BA_WATCHDOG (1 << 14) -#define MWL8K_A2H_INT_CHNL_SWITCHED (1 << 11) -#define MWL8K_A2H_INT_QUEUE_EMPTY (1 << 10) -#define MWL8K_A2H_INT_RADAR_DETECT (1 << 7) -#define MWL8K_A2H_INT_RADIO_ON (1 << 6) -#define MWL8K_A2H_INT_RADIO_OFF (1 << 5) -#define MWL8K_A2H_INT_MAC_EVENT (1 << 3) -#define MWL8K_A2H_INT_OPC_DONE (1 << 2) -#define MWL8K_A2H_INT_RX_READY (1 << 1) -#define MWL8K_A2H_INT_TX_DONE (1 << 0) - -/* HW micro second timer register - * located at offset 0xA600. This - * will be used to timestamp tx - * packets. - */ - -#define MWL8K_HW_TIMER_REGISTER 0x0000a600 -#define BBU_RXRDY_CNT_REG 0x0000a860 -#define NOK_CCA_CNT_REG 0x0000a6a0 -#define BBU_AVG_NOISE_VAL 0x67 - -#define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \ - MWL8K_A2H_INT_CHNL_SWITCHED | \ - MWL8K_A2H_INT_QUEUE_EMPTY | \ - MWL8K_A2H_INT_RADAR_DETECT | \ - MWL8K_A2H_INT_RADIO_ON | \ - MWL8K_A2H_INT_RADIO_OFF | \ - MWL8K_A2H_INT_MAC_EVENT | \ - MWL8K_A2H_INT_OPC_DONE | \ - MWL8K_A2H_INT_RX_READY | \ - MWL8K_A2H_INT_TX_DONE | \ - MWL8K_A2H_INT_BA_WATCHDOG) - -#define MWL8K_RX_QUEUES 1 -#define MWL8K_TX_WMM_QUEUES 4 -#define MWL8K_MAX_AMPDU_QUEUES 8 -#define MWL8K_MAX_TX_QUEUES (MWL8K_TX_WMM_QUEUES + MWL8K_MAX_AMPDU_QUEUES) -#define mwl8k_tx_queues(priv) (MWL8K_TX_WMM_QUEUES + (priv)->num_ampdu_queues) - -/* txpriorities are mapped with hw queues. - * Each hw queue has a txpriority. - */ -#define TOTAL_HW_TX_QUEUES 8 - -/* Each HW queue can have one AMPDU stream. - * But, because one of the hw queue is reserved, - * maximum AMPDU queues that can be created are - * one short of total tx queues. - */ -#define MWL8K_NUM_AMPDU_STREAMS (TOTAL_HW_TX_QUEUES - 1) - -#define MWL8K_NUM_CHANS 18 - -struct rxd_ops { - int rxd_size; - void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); - void (*rxd_refill)(void *rxd, dma_addr_t addr, int len); - int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status, - __le16 *qos, s8 *noise); -}; - -struct mwl8k_device_info { - char *part_name; - char *helper_image; - char *fw_image_sta; - char *fw_image_ap; - struct rxd_ops *ap_rxd_ops; - u32 fw_api_ap; -}; - -struct mwl8k_rx_queue { - int rxd_count; - - /* hw receives here */ - int head; - - /* refill descs here */ - int tail; - - void *rxd; - dma_addr_t rxd_dma; - struct { - struct sk_buff *skb; - DEFINE_DMA_UNMAP_ADDR(dma); - } *buf; -}; - -struct mwl8k_tx_queue { - /* hw transmits here */ - int head; - - /* sw appends here */ - int tail; - - unsigned int len; - struct mwl8k_tx_desc *txd; - dma_addr_t txd_dma; - struct sk_buff **skb; -}; - -enum { - AMPDU_NO_STREAM, - AMPDU_STREAM_NEW, - AMPDU_STREAM_IN_PROGRESS, - AMPDU_STREAM_ACTIVE, -}; - -struct mwl8k_ampdu_stream { - struct ieee80211_sta *sta; - u8 tid; - u8 state; - u8 idx; -}; - -struct mwl8k_priv { - struct ieee80211_hw *hw; - struct pci_dev *pdev; - int irq; - - struct mwl8k_device_info *device_info; - - void __iomem *sram; - void __iomem *regs; - - /* firmware */ - const struct firmware *fw_helper; - const struct firmware *fw_ucode; - - /* hardware/firmware parameters */ - bool ap_fw; - struct rxd_ops *rxd_ops; - struct ieee80211_supported_band band_24; - struct ieee80211_channel channels_24[14]; - struct ieee80211_rate rates_24[13]; - struct ieee80211_supported_band band_50; - struct ieee80211_channel channels_50[4]; - struct ieee80211_rate rates_50[8]; - u32 ap_macids_supported; - u32 sta_macids_supported; - - /* Ampdu stream information */ - u8 num_ampdu_queues; - spinlock_t stream_lock; - struct mwl8k_ampdu_stream ampdu[MWL8K_MAX_AMPDU_QUEUES]; - struct work_struct watchdog_ba_handle; - - /* firmware access */ - struct mutex fw_mutex; - struct task_struct *fw_mutex_owner; - struct task_struct *hw_restart_owner; - int fw_mutex_depth; - struct completion *hostcmd_wait; - - atomic_t watchdog_event_pending; - - /* lock held over TX and TX reap */ - spinlock_t tx_lock; - - /* TX quiesce completion, protected by fw_mutex and tx_lock */ - struct completion *tx_wait; - - /* List of interfaces. */ - u32 macids_used; - struct list_head vif_list; - - /* power management status cookie from firmware */ - u32 *cookie; - dma_addr_t cookie_dma; - - u16 num_mcaddrs; - u8 hw_rev; - u32 fw_rev; - u32 caps; - - /* - * Running count of TX packets in flight, to avoid - * iterating over the transmit rings each time. - */ - int pending_tx_pkts; - - struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES]; - struct mwl8k_tx_queue txq[MWL8K_MAX_TX_QUEUES]; - u32 txq_offset[MWL8K_MAX_TX_QUEUES]; - - bool radio_on; - bool radio_short_preamble; - bool sniffer_enabled; - bool wmm_enabled; - - /* XXX need to convert this to handle multiple interfaces */ - bool capture_beacon; - u8 capture_bssid[ETH_ALEN]; - struct sk_buff *beacon_skb; - - /* - * This FJ worker has to be global as it is scheduled from the - * RX handler. At this point we don't know which interface it - * belongs to until the list of bssids waiting to complete join - * is checked. - */ - struct work_struct finalize_join_worker; - - /* Tasklet to perform TX reclaim. */ - struct tasklet_struct poll_tx_task; - - /* Tasklet to perform RX. */ - struct tasklet_struct poll_rx_task; - - /* Most recently reported noise in dBm */ - s8 noise; - - /* - * preserve the queue configurations so they can be restored if/when - * the firmware image is swapped. - */ - struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES]; - - /* To perform the task of reloading the firmware */ - struct work_struct fw_reload; - bool hw_restart_in_progress; - - /* async firmware loading state */ - unsigned fw_state; - char *fw_pref; - char *fw_alt; - bool is_8764; - struct completion firmware_loading_complete; - - /* bitmap of running BSSes */ - u32 running_bsses; - - /* ACS related */ - bool sw_scan_start; - struct ieee80211_channel *acs_chan; - unsigned long channel_time; - struct survey_info survey[MWL8K_NUM_CHANS]; -}; - -#define MAX_WEP_KEY_LEN 13 -#define NUM_WEP_KEYS 4 - -/* Per interface specific private data */ -struct mwl8k_vif { - struct list_head list; - struct ieee80211_vif *vif; - - /* Firmware macid for this vif. */ - int macid; - - /* Non AMPDU sequence number assigned by driver. */ - u16 seqno; - - /* Saved WEP keys */ - struct { - u8 enabled; - u8 key[sizeof(struct ieee80211_key_conf) + MAX_WEP_KEY_LEN]; - } wep_key_conf[NUM_WEP_KEYS]; - - /* BSSID */ - u8 bssid[ETH_ALEN]; - - /* A flag to indicate is HW crypto is enabled for this bssid */ - bool is_hw_crypto_enabled; -}; -#define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) -#define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8)) - -struct tx_traffic_info { - u32 start_time; - u32 pkts; -}; - -#define MWL8K_MAX_TID 8 -struct mwl8k_sta { - /* Index into station database. Returned by UPDATE_STADB. */ - u8 peer_id; - u8 is_ampdu_allowed; - struct tx_traffic_info tx_stats[MWL8K_MAX_TID]; -}; -#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv)) - -static const struct ieee80211_channel mwl8k_channels_24[] = { - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2412, .hw_value = 1, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2417, .hw_value = 2, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2422, .hw_value = 3, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2427, .hw_value = 4, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2432, .hw_value = 5, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2437, .hw_value = 6, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2442, .hw_value = 7, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2447, .hw_value = 8, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2452, .hw_value = 9, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2457, .hw_value = 10, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2462, .hw_value = 11, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2467, .hw_value = 12, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2472, .hw_value = 13, }, - { .band = IEEE80211_BAND_2GHZ, .center_freq = 2484, .hw_value = 14, }, -}; - -static const struct ieee80211_rate mwl8k_rates_24[] = { - { .bitrate = 10, .hw_value = 2, }, - { .bitrate = 20, .hw_value = 4, }, - { .bitrate = 55, .hw_value = 11, }, - { .bitrate = 110, .hw_value = 22, }, - { .bitrate = 220, .hw_value = 44, }, - { .bitrate = 60, .hw_value = 12, }, - { .bitrate = 90, .hw_value = 18, }, - { .bitrate = 120, .hw_value = 24, }, - { .bitrate = 180, .hw_value = 36, }, - { .bitrate = 240, .hw_value = 48, }, - { .bitrate = 360, .hw_value = 72, }, - { .bitrate = 480, .hw_value = 96, }, - { .bitrate = 540, .hw_value = 108, }, -}; - -static const struct ieee80211_channel mwl8k_channels_50[] = { - { .band = IEEE80211_BAND_5GHZ, .center_freq = 5180, .hw_value = 36, }, - { .band = IEEE80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, }, - { .band = IEEE80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, }, - { .band = IEEE80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, }, -}; - -static const struct ieee80211_rate mwl8k_rates_50[] = { - { .bitrate = 60, .hw_value = 12, }, - { .bitrate = 90, .hw_value = 18, }, - { .bitrate = 120, .hw_value = 24, }, - { .bitrate = 180, .hw_value = 36, }, - { .bitrate = 240, .hw_value = 48, }, - { .bitrate = 360, .hw_value = 72, }, - { .bitrate = 480, .hw_value = 96, }, - { .bitrate = 540, .hw_value = 108, }, -}; - -/* Set or get info from Firmware */ -#define MWL8K_CMD_GET 0x0000 -#define MWL8K_CMD_SET 0x0001 -#define MWL8K_CMD_SET_LIST 0x0002 - -/* Firmware command codes */ -#define MWL8K_CMD_CODE_DNLD 0x0001 -#define MWL8K_CMD_GET_HW_SPEC 0x0003 -#define MWL8K_CMD_SET_HW_SPEC 0x0004 -#define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010 -#define MWL8K_CMD_GET_STAT 0x0014 -#define MWL8K_CMD_BBP_REG_ACCESS 0x001a -#define MWL8K_CMD_RADIO_CONTROL 0x001c -#define MWL8K_CMD_RF_TX_POWER 0x001e -#define MWL8K_CMD_TX_POWER 0x001f -#define MWL8K_CMD_RF_ANTENNA 0x0020 -#define MWL8K_CMD_SET_BEACON 0x0100 /* per-vif */ -#define MWL8K_CMD_SET_PRE_SCAN 0x0107 -#define MWL8K_CMD_SET_POST_SCAN 0x0108 -#define MWL8K_CMD_SET_RF_CHANNEL 0x010a -#define MWL8K_CMD_SET_AID 0x010d -#define MWL8K_CMD_SET_RATE 0x0110 -#define MWL8K_CMD_SET_FINALIZE_JOIN 0x0111 -#define MWL8K_CMD_RTS_THRESHOLD 0x0113 -#define MWL8K_CMD_SET_SLOT 0x0114 -#define MWL8K_CMD_SET_EDCA_PARAMS 0x0115 -#define MWL8K_CMD_SET_WMM_MODE 0x0123 -#define MWL8K_CMD_MIMO_CONFIG 0x0125 -#define MWL8K_CMD_USE_FIXED_RATE 0x0126 -#define MWL8K_CMD_ENABLE_SNIFFER 0x0150 -#define MWL8K_CMD_SET_MAC_ADDR 0x0202 /* per-vif */ -#define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 -#define MWL8K_CMD_GET_WATCHDOG_BITMAP 0x0205 -#define MWL8K_CMD_DEL_MAC_ADDR 0x0206 /* per-vif */ -#define MWL8K_CMD_BSS_START 0x1100 /* per-vif */ -#define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */ -#define MWL8K_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */ -#define MWL8K_CMD_UPDATE_STADB 0x1123 -#define MWL8K_CMD_BASTREAM 0x1125 - -static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize) -{ - u16 command = le16_to_cpu(cmd); - -#define MWL8K_CMDNAME(x) case MWL8K_CMD_##x: do {\ - snprintf(buf, bufsize, "%s", #x);\ - return buf;\ - } while (0) - switch (command & ~0x8000) { - MWL8K_CMDNAME(CODE_DNLD); - MWL8K_CMDNAME(GET_HW_SPEC); - MWL8K_CMDNAME(SET_HW_SPEC); - MWL8K_CMDNAME(MAC_MULTICAST_ADR); - MWL8K_CMDNAME(GET_STAT); - MWL8K_CMDNAME(RADIO_CONTROL); - MWL8K_CMDNAME(RF_TX_POWER); - MWL8K_CMDNAME(TX_POWER); - MWL8K_CMDNAME(RF_ANTENNA); - MWL8K_CMDNAME(SET_BEACON); - MWL8K_CMDNAME(SET_PRE_SCAN); - MWL8K_CMDNAME(SET_POST_SCAN); - MWL8K_CMDNAME(SET_RF_CHANNEL); - MWL8K_CMDNAME(SET_AID); - MWL8K_CMDNAME(SET_RATE); - MWL8K_CMDNAME(SET_FINALIZE_JOIN); - MWL8K_CMDNAME(RTS_THRESHOLD); - MWL8K_CMDNAME(SET_SLOT); - MWL8K_CMDNAME(SET_EDCA_PARAMS); - MWL8K_CMDNAME(SET_WMM_MODE); - MWL8K_CMDNAME(MIMO_CONFIG); - MWL8K_CMDNAME(USE_FIXED_RATE); - MWL8K_CMDNAME(ENABLE_SNIFFER); - MWL8K_CMDNAME(SET_MAC_ADDR); - MWL8K_CMDNAME(SET_RATEADAPT_MODE); - MWL8K_CMDNAME(BSS_START); - MWL8K_CMDNAME(SET_NEW_STN); - MWL8K_CMDNAME(UPDATE_ENCRYPTION); - MWL8K_CMDNAME(UPDATE_STADB); - MWL8K_CMDNAME(BASTREAM); - MWL8K_CMDNAME(GET_WATCHDOG_BITMAP); - default: - snprintf(buf, bufsize, "0x%x", cmd); - } -#undef MWL8K_CMDNAME - - return buf; -} - -/* Hardware and firmware reset */ -static void mwl8k_hw_reset(struct mwl8k_priv *priv) -{ - iowrite32(MWL8K_H2A_INT_RESET, - priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); - iowrite32(MWL8K_H2A_INT_RESET, - priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); - msleep(20); -} - -/* Release fw image */ -static void mwl8k_release_fw(const struct firmware **fw) -{ - if (*fw == NULL) - return; - release_firmware(*fw); - *fw = NULL; -} - -static void mwl8k_release_firmware(struct mwl8k_priv *priv) -{ - mwl8k_release_fw(&priv->fw_ucode); - mwl8k_release_fw(&priv->fw_helper); -} - -/* states for asynchronous f/w loading */ -static void mwl8k_fw_state_machine(const struct firmware *fw, void *context); -enum { - FW_STATE_INIT = 0, - FW_STATE_LOADING_PREF, - FW_STATE_LOADING_ALT, - FW_STATE_ERROR, -}; - -/* Request fw image */ -static int mwl8k_request_fw(struct mwl8k_priv *priv, - const char *fname, const struct firmware **fw, - bool nowait) -{ - /* release current image */ - if (*fw != NULL) - mwl8k_release_fw(fw); - - if (nowait) - return request_firmware_nowait(THIS_MODULE, 1, fname, - &priv->pdev->dev, GFP_KERNEL, - priv, mwl8k_fw_state_machine); - else - return request_firmware(fw, fname, &priv->pdev->dev); -} - -static int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image, - bool nowait) -{ - struct mwl8k_device_info *di = priv->device_info; - int rc; - - if (di->helper_image != NULL) { - if (nowait) - rc = mwl8k_request_fw(priv, di->helper_image, - &priv->fw_helper, true); - else - rc = mwl8k_request_fw(priv, di->helper_image, - &priv->fw_helper, false); - if (rc) - printk(KERN_ERR "%s: Error requesting helper fw %s\n", - pci_name(priv->pdev), di->helper_image); - - if (rc || nowait) - return rc; - } - - if (nowait) { - /* - * if we get here, no helper image is needed. Skip the - * FW_STATE_INIT state. - */ - priv->fw_state = FW_STATE_LOADING_PREF; - rc = mwl8k_request_fw(priv, fw_image, - &priv->fw_ucode, - true); - } else - rc = mwl8k_request_fw(priv, fw_image, - &priv->fw_ucode, false); - if (rc) { - printk(KERN_ERR "%s: Error requesting firmware file %s\n", - pci_name(priv->pdev), fw_image); - mwl8k_release_fw(&priv->fw_helper); - return rc; - } - - return 0; -} - -struct mwl8k_cmd_pkt { - __le16 code; - __le16 length; - __u8 seq_num; - __u8 macid; - __le16 result; - char payload[0]; -} __packed; - -/* - * Firmware loading. - */ -static int -mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length) -{ - void __iomem *regs = priv->regs; - dma_addr_t dma_addr; - int loops; - - dma_addr = pci_map_single(priv->pdev, data, length, PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(priv->pdev, dma_addr)) - return -ENOMEM; - - iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); - iowrite32(0, regs + MWL8K_HIU_INT_CODE); - iowrite32(MWL8K_H2A_INT_DOORBELL, - regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); - iowrite32(MWL8K_H2A_INT_DUMMY, - regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); - - loops = 1000; - do { - u32 int_code; - if (priv->is_8764) { - int_code = ioread32(regs + - MWL8K_HIU_H2A_INTERRUPT_STATUS); - if (int_code == 0) - break; - } else { - int_code = ioread32(regs + MWL8K_HIU_INT_CODE); - if (int_code == MWL8K_INT_CODE_CMD_FINISHED) { - iowrite32(0, regs + MWL8K_HIU_INT_CODE); - break; - } - } - cond_resched(); - udelay(1); - } while (--loops); - - pci_unmap_single(priv->pdev, dma_addr, length, PCI_DMA_TODEVICE); - - return loops ? 0 : -ETIMEDOUT; -} - -static int mwl8k_load_fw_image(struct mwl8k_priv *priv, - const u8 *data, size_t length) -{ - struct mwl8k_cmd_pkt *cmd; - int done; - int rc = 0; - - cmd = kmalloc(sizeof(*cmd) + 256, GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD); - cmd->seq_num = 0; - cmd->macid = 0; - cmd->result = 0; - - done = 0; - while (length) { - int block_size = length > 256 ? 256 : length; - - memcpy(cmd->payload, data + done, block_size); - cmd->length = cpu_to_le16(block_size); - - rc = mwl8k_send_fw_load_cmd(priv, cmd, - sizeof(*cmd) + block_size); - if (rc) - break; - - done += block_size; - length -= block_size; - } - - if (!rc) { - cmd->length = 0; - rc = mwl8k_send_fw_load_cmd(priv, cmd, sizeof(*cmd)); - } - - kfree(cmd); - - return rc; -} - -static int mwl8k_feed_fw_image(struct mwl8k_priv *priv, - const u8 *data, size_t length) -{ - unsigned char *buffer; - int may_continue, rc = 0; - u32 done, prev_block_size; - - buffer = kmalloc(1024, GFP_KERNEL); - if (buffer == NULL) - return -ENOMEM; - - done = 0; - prev_block_size = 0; - may_continue = 1000; - while (may_continue > 0) { - u32 block_size; - - block_size = ioread32(priv->regs + MWL8K_HIU_SCRATCH); - if (block_size & 1) { - block_size &= ~1; - may_continue--; - } else { - done += prev_block_size; - length -= prev_block_size; - } - - if (block_size > 1024 || block_size > length) { - rc = -EOVERFLOW; - break; - } - - if (length == 0) { - rc = 0; - break; - } - - if (block_size == 0) { - rc = -EPROTO; - may_continue--; - udelay(1); - continue; - } - - prev_block_size = block_size; - memcpy(buffer, data + done, block_size); - - rc = mwl8k_send_fw_load_cmd(priv, buffer, block_size); - if (rc) - break; - } - - if (!rc && length != 0) - rc = -EREMOTEIO; - - kfree(buffer); - - return rc; -} - -static int mwl8k_load_firmware(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - const struct firmware *fw = priv->fw_ucode; - int rc; - int loops; - - if (!memcmp(fw->data, "\x01\x00\x00\x00", 4) && !priv->is_8764) { - const struct firmware *helper = priv->fw_helper; - - if (helper == NULL) { - printk(KERN_ERR "%s: helper image needed but none " - "given\n", pci_name(priv->pdev)); - return -EINVAL; - } - - rc = mwl8k_load_fw_image(priv, helper->data, helper->size); - if (rc) { - printk(KERN_ERR "%s: unable to load firmware " - "helper image\n", pci_name(priv->pdev)); - return rc; - } - msleep(20); - - rc = mwl8k_feed_fw_image(priv, fw->data, fw->size); - } else { - if (priv->is_8764) - rc = mwl8k_feed_fw_image(priv, fw->data, fw->size); - else - rc = mwl8k_load_fw_image(priv, fw->data, fw->size); - } - - if (rc) { - printk(KERN_ERR "%s: unable to load firmware image\n", - pci_name(priv->pdev)); - return rc; - } - - iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR); - - loops = 500000; - do { - u32 ready_code; - - ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE); - if (ready_code == MWL8K_FWAP_READY) { - priv->ap_fw = true; - break; - } else if (ready_code == MWL8K_FWSTA_READY) { - priv->ap_fw = false; - break; - } - - cond_resched(); - udelay(1); - } while (--loops); - - return loops ? 0 : -ETIMEDOUT; -} - - -/* DMA header used by firmware and hardware. */ -struct mwl8k_dma_data { - __le16 fwlen; - struct ieee80211_hdr wh; - char data[0]; -} __packed; - -/* Routines to add/remove DMA header from skb. */ -static inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos) -{ - struct mwl8k_dma_data *tr; - int hdrlen; - - tr = (struct mwl8k_dma_data *)skb->data; - hdrlen = ieee80211_hdrlen(tr->wh.frame_control); - - if (hdrlen != sizeof(tr->wh)) { - if (ieee80211_is_data_qos(tr->wh.frame_control)) { - memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2); - *((__le16 *)(tr->data - 2)) = qos; - } else { - memmove(tr->data - hdrlen, &tr->wh, hdrlen); - } - } - - if (hdrlen != sizeof(*tr)) - skb_pull(skb, sizeof(*tr) - hdrlen); -} - -#define REDUCED_TX_HEADROOM 8 - -static void -mwl8k_add_dma_header(struct mwl8k_priv *priv, struct sk_buff *skb, - int head_pad, int tail_pad) -{ - struct ieee80211_hdr *wh; - int hdrlen; - int reqd_hdrlen; - struct mwl8k_dma_data *tr; - - /* - * Add a firmware DMA header; the firmware requires that we - * present a 2-byte payload length followed by a 4-address - * header (without QoS field), followed (optionally) by any - * WEP/ExtIV header (but only filled in for CCMP). - */ - wh = (struct ieee80211_hdr *)skb->data; - - hdrlen = ieee80211_hdrlen(wh->frame_control); - - /* - * Check if skb_resize is required because of - * tx_headroom adjustment. - */ - if (priv->ap_fw && (hdrlen < (sizeof(struct ieee80211_cts) - + REDUCED_TX_HEADROOM))) { - if (pskb_expand_head(skb, REDUCED_TX_HEADROOM, 0, GFP_ATOMIC)) { - - wiphy_err(priv->hw->wiphy, - "Failed to reallocate TX buffer\n"); - return; - } - skb->truesize += REDUCED_TX_HEADROOM; - } - - reqd_hdrlen = sizeof(*tr) + head_pad; - - if (hdrlen != reqd_hdrlen) - skb_push(skb, reqd_hdrlen - hdrlen); - - if (ieee80211_is_data_qos(wh->frame_control)) - hdrlen -= IEEE80211_QOS_CTL_LEN; - - tr = (struct mwl8k_dma_data *)skb->data; - if (wh != &tr->wh) - memmove(&tr->wh, wh, hdrlen); - if (hdrlen != sizeof(tr->wh)) - memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen); - - /* - * Firmware length is the length of the fully formed "802.11 - * payload". That is, everything except for the 802.11 header. - * This includes all crypto material including the MIC. - */ - tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr) + tail_pad); -} - -static void mwl8k_encapsulate_tx_frame(struct mwl8k_priv *priv, - struct sk_buff *skb) -{ - struct ieee80211_hdr *wh; - struct ieee80211_tx_info *tx_info; - struct ieee80211_key_conf *key_conf; - int data_pad; - int head_pad = 0; - - wh = (struct ieee80211_hdr *)skb->data; - - tx_info = IEEE80211_SKB_CB(skb); - - key_conf = NULL; - if (ieee80211_is_data(wh->frame_control)) - key_conf = tx_info->control.hw_key; - - /* - * Make sure the packet header is in the DMA header format (4-address - * without QoS), and add head & tail padding when HW crypto is enabled. - * - * We have the following trailer padding requirements: - * - WEP: 4 trailer bytes (ICV) - * - TKIP: 12 trailer bytes (8 MIC + 4 ICV) - * - CCMP: 8 trailer bytes (MIC) - */ - data_pad = 0; - if (key_conf != NULL) { - head_pad = key_conf->iv_len; - switch (key_conf->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - data_pad = 4; - break; - case WLAN_CIPHER_SUITE_TKIP: - data_pad = 12; - break; - case WLAN_CIPHER_SUITE_CCMP: - data_pad = 8; - break; - } - } - mwl8k_add_dma_header(priv, skb, head_pad, data_pad); -} - -/* - * Packet reception for 88w8366/88w8764 AP firmware. - */ -struct mwl8k_rxd_ap { - __le16 pkt_len; - __u8 sq2; - __u8 rate; - __le32 pkt_phys_addr; - __le32 next_rxd_phys_addr; - __le16 qos_control; - __le16 htsig2; - __le32 hw_rssi_info; - __le32 hw_noise_floor_info; - __u8 noise_floor; - __u8 pad0[3]; - __u8 rssi; - __u8 rx_status; - __u8 channel; - __u8 rx_ctrl; -} __packed; - -#define MWL8K_AP_RATE_INFO_MCS_FORMAT 0x80 -#define MWL8K_AP_RATE_INFO_40MHZ 0x40 -#define MWL8K_AP_RATE_INFO_RATEID(x) ((x) & 0x3f) - -#define MWL8K_AP_RX_CTRL_OWNED_BY_HOST 0x80 - -/* 8366/8764 AP rx_status bits */ -#define MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK 0x80 -#define MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR 0xFF -#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR 0x02 -#define MWL8K_AP_RXSTAT_WEP_DECRYPT_ICV_ERR 0x04 -#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR 0x08 - -static void mwl8k_rxd_ap_init(void *_rxd, dma_addr_t next_dma_addr) -{ - struct mwl8k_rxd_ap *rxd = _rxd; - - rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); - rxd->rx_ctrl = MWL8K_AP_RX_CTRL_OWNED_BY_HOST; -} - -static void mwl8k_rxd_ap_refill(void *_rxd, dma_addr_t addr, int len) -{ - struct mwl8k_rxd_ap *rxd = _rxd; - - rxd->pkt_len = cpu_to_le16(len); - rxd->pkt_phys_addr = cpu_to_le32(addr); - wmb(); - rxd->rx_ctrl = 0; -} - -static int -mwl8k_rxd_ap_process(void *_rxd, struct ieee80211_rx_status *status, - __le16 *qos, s8 *noise) -{ - struct mwl8k_rxd_ap *rxd = _rxd; - - if (!(rxd->rx_ctrl & MWL8K_AP_RX_CTRL_OWNED_BY_HOST)) - return -1; - rmb(); - - memset(status, 0, sizeof(*status)); - - status->signal = -rxd->rssi; - *noise = -rxd->noise_floor; - - if (rxd->rate & MWL8K_AP_RATE_INFO_MCS_FORMAT) { - status->flag |= RX_FLAG_HT; - if (rxd->rate & MWL8K_AP_RATE_INFO_40MHZ) - status->flag |= RX_FLAG_40MHZ; - status->rate_idx = MWL8K_AP_RATE_INFO_RATEID(rxd->rate); - } else { - int i; - - for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) { - if (mwl8k_rates_24[i].hw_value == rxd->rate) { - status->rate_idx = i; - break; - } - } - } - - if (rxd->channel > 14) { - status->band = IEEE80211_BAND_5GHZ; - if (!(status->flag & RX_FLAG_HT)) - status->rate_idx -= 5; - } else { - status->band = IEEE80211_BAND_2GHZ; - } - status->freq = ieee80211_channel_to_frequency(rxd->channel, - status->band); - - *qos = rxd->qos_control; - - if ((rxd->rx_status != MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR) && - (rxd->rx_status & MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK) && - (rxd->rx_status & MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR)) - status->flag |= RX_FLAG_MMIC_ERROR; - - return le16_to_cpu(rxd->pkt_len); -} - -static struct rxd_ops rxd_ap_ops = { - .rxd_size = sizeof(struct mwl8k_rxd_ap), - .rxd_init = mwl8k_rxd_ap_init, - .rxd_refill = mwl8k_rxd_ap_refill, - .rxd_process = mwl8k_rxd_ap_process, -}; - -/* - * Packet reception for STA firmware. - */ -struct mwl8k_rxd_sta { - __le16 pkt_len; - __u8 link_quality; - __u8 noise_level; - __le32 pkt_phys_addr; - __le32 next_rxd_phys_addr; - __le16 qos_control; - __le16 rate_info; - __le32 pad0[4]; - __u8 rssi; - __u8 channel; - __le16 pad1; - __u8 rx_ctrl; - __u8 rx_status; - __u8 pad2[2]; -} __packed; - -#define MWL8K_STA_RATE_INFO_SHORTPRE 0x8000 -#define MWL8K_STA_RATE_INFO_ANTSELECT(x) (((x) >> 11) & 0x3) -#define MWL8K_STA_RATE_INFO_RATEID(x) (((x) >> 3) & 0x3f) -#define MWL8K_STA_RATE_INFO_40MHZ 0x0004 -#define MWL8K_STA_RATE_INFO_SHORTGI 0x0002 -#define MWL8K_STA_RATE_INFO_MCS_FORMAT 0x0001 - -#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST 0x02 -#define MWL8K_STA_RX_CTRL_DECRYPT_ERROR 0x04 -/* ICV=0 or MIC=1 */ -#define MWL8K_STA_RX_CTRL_DEC_ERR_TYPE 0x08 -/* Key is uploaded only in failure case */ -#define MWL8K_STA_RX_CTRL_KEY_INDEX 0x30 - -static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr) -{ - struct mwl8k_rxd_sta *rxd = _rxd; - - rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); - rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST; -} - -static void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len) -{ - struct mwl8k_rxd_sta *rxd = _rxd; - - rxd->pkt_len = cpu_to_le16(len); - rxd->pkt_phys_addr = cpu_to_le32(addr); - wmb(); - rxd->rx_ctrl = 0; -} - -static int -mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status, - __le16 *qos, s8 *noise) -{ - struct mwl8k_rxd_sta *rxd = _rxd; - u16 rate_info; - - if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST)) - return -1; - rmb(); - - rate_info = le16_to_cpu(rxd->rate_info); - - memset(status, 0, sizeof(*status)); - - status->signal = -rxd->rssi; - *noise = -rxd->noise_level; - status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info); - status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info); - - if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE) - status->flag |= RX_FLAG_SHORTPRE; - if (rate_info & MWL8K_STA_RATE_INFO_40MHZ) - status->flag |= RX_FLAG_40MHZ; - if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI) - status->flag |= RX_FLAG_SHORT_GI; - if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT) - status->flag |= RX_FLAG_HT; - - if (rxd->channel > 14) { - status->band = IEEE80211_BAND_5GHZ; - if (!(status->flag & RX_FLAG_HT)) - status->rate_idx -= 5; - } else { - status->band = IEEE80211_BAND_2GHZ; - } - status->freq = ieee80211_channel_to_frequency(rxd->channel, - status->band); - - *qos = rxd->qos_control; - if ((rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DECRYPT_ERROR) && - (rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DEC_ERR_TYPE)) - status->flag |= RX_FLAG_MMIC_ERROR; - - return le16_to_cpu(rxd->pkt_len); -} - -static struct rxd_ops rxd_sta_ops = { - .rxd_size = sizeof(struct mwl8k_rxd_sta), - .rxd_init = mwl8k_rxd_sta_init, - .rxd_refill = mwl8k_rxd_sta_refill, - .rxd_process = mwl8k_rxd_sta_process, -}; - - -#define MWL8K_RX_DESCS 256 -#define MWL8K_RX_MAXSZ 3800 - -static int mwl8k_rxq_init(struct ieee80211_hw *hw, int index) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_rx_queue *rxq = priv->rxq + index; - int size; - int i; - - rxq->rxd_count = 0; - rxq->head = 0; - rxq->tail = 0; - - size = MWL8K_RX_DESCS * priv->rxd_ops->rxd_size; - - rxq->rxd = pci_zalloc_consistent(priv->pdev, size, &rxq->rxd_dma); - if (rxq->rxd == NULL) { - wiphy_err(hw->wiphy, "failed to alloc RX descriptors\n"); - return -ENOMEM; - } - - rxq->buf = kcalloc(MWL8K_RX_DESCS, sizeof(*rxq->buf), GFP_KERNEL); - if (rxq->buf == NULL) { - pci_free_consistent(priv->pdev, size, rxq->rxd, rxq->rxd_dma); - return -ENOMEM; - } - - for (i = 0; i < MWL8K_RX_DESCS; i++) { - int desc_size; - void *rxd; - int nexti; - dma_addr_t next_dma_addr; - - desc_size = priv->rxd_ops->rxd_size; - rxd = rxq->rxd + (i * priv->rxd_ops->rxd_size); - - nexti = i + 1; - if (nexti == MWL8K_RX_DESCS) - nexti = 0; - next_dma_addr = rxq->rxd_dma + (nexti * desc_size); - - priv->rxd_ops->rxd_init(rxd, next_dma_addr); - } - - return 0; -} - -static int rxq_refill(struct ieee80211_hw *hw, int index, int limit) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_rx_queue *rxq = priv->rxq + index; - int refilled; - - refilled = 0; - while (rxq->rxd_count < MWL8K_RX_DESCS && limit--) { - struct sk_buff *skb; - dma_addr_t addr; - int rx; - void *rxd; - - skb = dev_alloc_skb(MWL8K_RX_MAXSZ); - if (skb == NULL) - break; - - addr = pci_map_single(priv->pdev, skb->data, - MWL8K_RX_MAXSZ, DMA_FROM_DEVICE); - - rxq->rxd_count++; - rx = rxq->tail++; - if (rxq->tail == MWL8K_RX_DESCS) - rxq->tail = 0; - rxq->buf[rx].skb = skb; - dma_unmap_addr_set(&rxq->buf[rx], dma, addr); - - rxd = rxq->rxd + (rx * priv->rxd_ops->rxd_size); - priv->rxd_ops->rxd_refill(rxd, addr, MWL8K_RX_MAXSZ); - - refilled++; - } - - return refilled; -} - -/* Must be called only when the card's reception is completely halted */ -static void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_rx_queue *rxq = priv->rxq + index; - int i; - - if (rxq->rxd == NULL) - return; - - for (i = 0; i < MWL8K_RX_DESCS; i++) { - if (rxq->buf[i].skb != NULL) { - pci_unmap_single(priv->pdev, - dma_unmap_addr(&rxq->buf[i], dma), - MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE); - dma_unmap_addr_set(&rxq->buf[i], dma, 0); - - kfree_skb(rxq->buf[i].skb); - rxq->buf[i].skb = NULL; - } - } - - kfree(rxq->buf); - rxq->buf = NULL; - - pci_free_consistent(priv->pdev, - MWL8K_RX_DESCS * priv->rxd_ops->rxd_size, - rxq->rxd, rxq->rxd_dma); - rxq->rxd = NULL; -} - - -/* - * Scan a list of BSSIDs to process for finalize join. - * Allows for extension to process multiple BSSIDs. - */ -static inline int -mwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh) -{ - return priv->capture_beacon && - ieee80211_is_beacon(wh->frame_control) && - ether_addr_equal_64bits(wh->addr3, priv->capture_bssid); -} - -static inline void mwl8k_save_beacon(struct ieee80211_hw *hw, - struct sk_buff *skb) -{ - struct mwl8k_priv *priv = hw->priv; - - priv->capture_beacon = false; - eth_zero_addr(priv->capture_bssid); - - /* - * Use GFP_ATOMIC as rxq_process is called from - * the primary interrupt handler, memory allocation call - * must not sleep. - */ - priv->beacon_skb = skb_copy(skb, GFP_ATOMIC); - if (priv->beacon_skb != NULL) - ieee80211_queue_work(hw, &priv->finalize_join_worker); -} - -static inline struct mwl8k_vif *mwl8k_find_vif_bss(struct list_head *vif_list, - u8 *bssid) -{ - struct mwl8k_vif *mwl8k_vif; - - list_for_each_entry(mwl8k_vif, - vif_list, list) { - if (memcmp(bssid, mwl8k_vif->bssid, - ETH_ALEN) == 0) - return mwl8k_vif; - } - - return NULL; -} - -static int rxq_process(struct ieee80211_hw *hw, int index, int limit) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *mwl8k_vif = NULL; - struct mwl8k_rx_queue *rxq = priv->rxq + index; - int processed; - - processed = 0; - while (rxq->rxd_count && limit--) { - struct sk_buff *skb; - void *rxd; - int pkt_len; - struct ieee80211_rx_status status; - struct ieee80211_hdr *wh; - __le16 qos; - - skb = rxq->buf[rxq->head].skb; - if (skb == NULL) - break; - - rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size); - - pkt_len = priv->rxd_ops->rxd_process(rxd, &status, &qos, - &priv->noise); - if (pkt_len < 0) - break; - - rxq->buf[rxq->head].skb = NULL; - - pci_unmap_single(priv->pdev, - dma_unmap_addr(&rxq->buf[rxq->head], dma), - MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE); - dma_unmap_addr_set(&rxq->buf[rxq->head], dma, 0); - - rxq->head++; - if (rxq->head == MWL8K_RX_DESCS) - rxq->head = 0; - - rxq->rxd_count--; - - wh = &((struct mwl8k_dma_data *)skb->data)->wh; - - /* - * Check for a pending join operation. Save a - * copy of the beacon and schedule a tasklet to - * send a FINALIZE_JOIN command to the firmware. - */ - if (mwl8k_capture_bssid(priv, (void *)skb->data)) - mwl8k_save_beacon(hw, skb); - - if (ieee80211_has_protected(wh->frame_control)) { - - /* Check if hw crypto has been enabled for - * this bss. If yes, set the status flags - * accordingly - */ - mwl8k_vif = mwl8k_find_vif_bss(&priv->vif_list, - wh->addr1); - - if (mwl8k_vif != NULL && - mwl8k_vif->is_hw_crypto_enabled) { - /* - * When MMIC ERROR is encountered - * by the firmware, payload is - * dropped and only 32 bytes of - * mwl8k Firmware header is sent - * to the host. - * - * We need to add four bytes of - * key information. In it - * MAC80211 expects keyidx set to - * 0 for triggering Counter - * Measure of MMIC failure. - */ - if (status.flag & RX_FLAG_MMIC_ERROR) { - struct mwl8k_dma_data *tr; - tr = (struct mwl8k_dma_data *)skb->data; - memset((void *)&(tr->data), 0, 4); - pkt_len += 4; - } - - if (!ieee80211_is_auth(wh->frame_control)) - status.flag |= RX_FLAG_IV_STRIPPED | - RX_FLAG_DECRYPTED | - RX_FLAG_MMIC_STRIPPED; - } - } - - skb_put(skb, pkt_len); - mwl8k_remove_dma_header(skb, qos); - memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx_irqsafe(hw, skb); - - processed++; - } - - return processed; -} - - -/* - * Packet transmission. - */ - -#define MWL8K_TXD_STATUS_OK 0x00000001 -#define MWL8K_TXD_STATUS_OK_RETRY 0x00000002 -#define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004 -#define MWL8K_TXD_STATUS_MULTICAST_TX 0x00000008 -#define MWL8K_TXD_STATUS_FW_OWNED 0x80000000 - -#define MWL8K_QOS_QLEN_UNSPEC 0xff00 -#define MWL8K_QOS_ACK_POLICY_MASK 0x0060 -#define MWL8K_QOS_ACK_POLICY_NORMAL 0x0000 -#define MWL8K_QOS_ACK_POLICY_BLOCKACK 0x0060 -#define MWL8K_QOS_EOSP 0x0010 - -struct mwl8k_tx_desc { - __le32 status; - __u8 data_rate; - __u8 tx_priority; - __le16 qos_control; - __le32 pkt_phys_addr; - __le16 pkt_len; - __u8 dest_MAC_addr[ETH_ALEN]; - __le32 next_txd_phys_addr; - __le32 timestamp; - __le16 rate_info; - __u8 peer_id; - __u8 tx_frag_cnt; -} __packed; - -#define MWL8K_TX_DESCS 128 - -static int mwl8k_txq_init(struct ieee80211_hw *hw, int index) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_tx_queue *txq = priv->txq + index; - int size; - int i; - - txq->len = 0; - txq->head = 0; - txq->tail = 0; - - size = MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc); - - txq->txd = pci_zalloc_consistent(priv->pdev, size, &txq->txd_dma); - if (txq->txd == NULL) { - wiphy_err(hw->wiphy, "failed to alloc TX descriptors\n"); - return -ENOMEM; - } - - txq->skb = kcalloc(MWL8K_TX_DESCS, sizeof(*txq->skb), GFP_KERNEL); - if (txq->skb == NULL) { - pci_free_consistent(priv->pdev, size, txq->txd, txq->txd_dma); - return -ENOMEM; - } - - for (i = 0; i < MWL8K_TX_DESCS; i++) { - struct mwl8k_tx_desc *tx_desc; - int nexti; - - tx_desc = txq->txd + i; - nexti = (i + 1) % MWL8K_TX_DESCS; - - tx_desc->status = 0; - tx_desc->next_txd_phys_addr = - cpu_to_le32(txq->txd_dma + nexti * sizeof(*tx_desc)); - } - - return 0; -} - -static inline void mwl8k_tx_start(struct mwl8k_priv *priv) -{ - iowrite32(MWL8K_H2A_INT_PPA_READY, - priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); - iowrite32(MWL8K_H2A_INT_DUMMY, - priv->regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); - ioread32(priv->regs + MWL8K_HIU_INT_CODE); -} - -static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - int i; - - for (i = 0; i < mwl8k_tx_queues(priv); i++) { - struct mwl8k_tx_queue *txq = priv->txq + i; - int fw_owned = 0; - int drv_owned = 0; - int unused = 0; - int desc; - - for (desc = 0; desc < MWL8K_TX_DESCS; desc++) { - struct mwl8k_tx_desc *tx_desc = txq->txd + desc; - u32 status; - - status = le32_to_cpu(tx_desc->status); - if (status & MWL8K_TXD_STATUS_FW_OWNED) - fw_owned++; - else - drv_owned++; - - if (tx_desc->pkt_len == 0) - unused++; - } - - wiphy_err(hw->wiphy, - "txq[%d] len=%d head=%d tail=%d " - "fw_owned=%d drv_owned=%d unused=%d\n", - i, - txq->len, txq->head, txq->tail, - fw_owned, drv_owned, unused); - } -} - -/* - * Must be called with priv->fw_mutex held and tx queues stopped. - */ -#define MWL8K_TX_WAIT_TIMEOUT_MS 5000 - -static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - DECLARE_COMPLETION_ONSTACK(tx_wait); - int retry; - int rc; - - might_sleep(); - - /* Since fw restart is in progress, allow only the firmware - * commands from the restart code and block the other - * commands since they are going to fail in any case since - * the firmware has crashed - */ - if (priv->hw_restart_in_progress) { - if (priv->hw_restart_owner == current) - return 0; - else - return -EBUSY; - } - - if (atomic_read(&priv->watchdog_event_pending)) - return 0; - - /* - * The TX queues are stopped at this point, so this test - * doesn't need to take ->tx_lock. - */ - if (!priv->pending_tx_pkts) - return 0; - - retry = 1; - rc = 0; - - spin_lock_bh(&priv->tx_lock); - priv->tx_wait = &tx_wait; - while (!rc) { - int oldcount; - unsigned long timeout; - - oldcount = priv->pending_tx_pkts; - - spin_unlock_bh(&priv->tx_lock); - timeout = wait_for_completion_timeout(&tx_wait, - msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS)); - - if (atomic_read(&priv->watchdog_event_pending)) { - spin_lock_bh(&priv->tx_lock); - priv->tx_wait = NULL; - spin_unlock_bh(&priv->tx_lock); - return 0; - } - - spin_lock_bh(&priv->tx_lock); - - if (timeout || !priv->pending_tx_pkts) { - WARN_ON(priv->pending_tx_pkts); - if (retry) - wiphy_notice(hw->wiphy, "tx rings drained\n"); - break; - } - - if (retry) { - mwl8k_tx_start(priv); - retry = 0; - continue; - } - - if (priv->pending_tx_pkts < oldcount) { - wiphy_notice(hw->wiphy, - "waiting for tx rings to drain (%d -> %d pkts)\n", - oldcount, priv->pending_tx_pkts); - retry = 1; - continue; - } - - priv->tx_wait = NULL; - - wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n", - MWL8K_TX_WAIT_TIMEOUT_MS); - mwl8k_dump_tx_rings(hw); - priv->hw_restart_in_progress = true; - ieee80211_queue_work(hw, &priv->fw_reload); - - rc = -ETIMEDOUT; - } - priv->tx_wait = NULL; - spin_unlock_bh(&priv->tx_lock); - - return rc; -} - -#define MWL8K_TXD_SUCCESS(status) \ - ((status) & (MWL8K_TXD_STATUS_OK | \ - MWL8K_TXD_STATUS_OK_RETRY | \ - MWL8K_TXD_STATUS_OK_MORE_RETRY)) - -static int mwl8k_tid_queue_mapping(u8 tid) -{ - BUG_ON(tid > 7); - - switch (tid) { - case 0: - case 3: - return IEEE80211_AC_BE; - case 1: - case 2: - return IEEE80211_AC_BK; - case 4: - case 5: - return IEEE80211_AC_VI; - case 6: - case 7: - return IEEE80211_AC_VO; - default: - return -1; - } -} - -/* The firmware will fill in the rate information - * for each packet that gets queued in the hardware - * and these macros will interpret that info. - */ - -#define RI_FORMAT(a) (a & 0x0001) -#define RI_RATE_ID_MCS(a) ((a & 0x01f8) >> 3) - -static int -mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_tx_queue *txq = priv->txq + index; - int processed; - - processed = 0; - while (txq->len > 0 && limit--) { - int tx; - struct mwl8k_tx_desc *tx_desc; - unsigned long addr; - int size; - struct sk_buff *skb; - struct ieee80211_tx_info *info; - u32 status; - struct ieee80211_sta *sta; - struct mwl8k_sta *sta_info = NULL; - u16 rate_info; - struct ieee80211_hdr *wh; - - tx = txq->head; - tx_desc = txq->txd + tx; - - status = le32_to_cpu(tx_desc->status); - - if (status & MWL8K_TXD_STATUS_FW_OWNED) { - if (!force) - break; - tx_desc->status &= - ~cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED); - } - - txq->head = (tx + 1) % MWL8K_TX_DESCS; - BUG_ON(txq->len == 0); - txq->len--; - priv->pending_tx_pkts--; - - addr = le32_to_cpu(tx_desc->pkt_phys_addr); - size = le16_to_cpu(tx_desc->pkt_len); - skb = txq->skb[tx]; - txq->skb[tx] = NULL; - - BUG_ON(skb == NULL); - pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE); - - mwl8k_remove_dma_header(skb, tx_desc->qos_control); - - wh = (struct ieee80211_hdr *) skb->data; - - /* Mark descriptor as unused */ - tx_desc->pkt_phys_addr = 0; - tx_desc->pkt_len = 0; - - info = IEEE80211_SKB_CB(skb); - if (ieee80211_is_data(wh->frame_control)) { - rcu_read_lock(); - sta = ieee80211_find_sta_by_ifaddr(hw, wh->addr1, - wh->addr2); - if (sta) { - sta_info = MWL8K_STA(sta); - BUG_ON(sta_info == NULL); - rate_info = le16_to_cpu(tx_desc->rate_info); - /* If rate is < 6.5 Mpbs for an ht station - * do not form an ampdu. If the station is a - * legacy station (format = 0), do not form an - * ampdu - */ - if (RI_RATE_ID_MCS(rate_info) < 1 || - RI_FORMAT(rate_info) == 0) { - sta_info->is_ampdu_allowed = false; - } else { - sta_info->is_ampdu_allowed = true; - } - } - rcu_read_unlock(); - } - - ieee80211_tx_info_clear_status(info); - - /* Rate control is happening in the firmware. - * Ensure no tx rate is being reported. - */ - info->status.rates[0].idx = -1; - info->status.rates[0].count = 1; - - if (MWL8K_TXD_SUCCESS(status)) - info->flags |= IEEE80211_TX_STAT_ACK; - - ieee80211_tx_status_irqsafe(hw, skb); - - processed++; - } - - return processed; -} - -/* must be called only when the card's transmit is completely halted */ -static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_tx_queue *txq = priv->txq + index; - - if (txq->txd == NULL) - return; - - mwl8k_txq_reclaim(hw, index, INT_MAX, 1); - - kfree(txq->skb); - txq->skb = NULL; - - pci_free_consistent(priv->pdev, - MWL8K_TX_DESCS * sizeof(struct mwl8k_tx_desc), - txq->txd, txq->txd_dma); - txq->txd = NULL; -} - -/* caller must hold priv->stream_lock when calling the stream functions */ -static struct mwl8k_ampdu_stream * -mwl8k_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid) -{ - struct mwl8k_ampdu_stream *stream; - struct mwl8k_priv *priv = hw->priv; - int i; - - for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { - stream = &priv->ampdu[i]; - if (stream->state == AMPDU_NO_STREAM) { - stream->sta = sta; - stream->state = AMPDU_STREAM_NEW; - stream->tid = tid; - stream->idx = i; - wiphy_debug(hw->wiphy, "Added a new stream for %pM %d", - sta->addr, tid); - return stream; - } - } - return NULL; -} - -static int -mwl8k_start_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) -{ - int ret; - - /* if the stream has already been started, don't start it again */ - if (stream->state != AMPDU_STREAM_NEW) - return 0; - ret = ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0); - if (ret) - wiphy_debug(hw->wiphy, "Failed to start stream for %pM %d: " - "%d\n", stream->sta->addr, stream->tid, ret); - else - wiphy_debug(hw->wiphy, "Started stream for %pM %d\n", - stream->sta->addr, stream->tid); - return ret; -} - -static void -mwl8k_remove_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) -{ - wiphy_debug(hw->wiphy, "Remove stream for %pM %d\n", stream->sta->addr, - stream->tid); - memset(stream, 0, sizeof(*stream)); -} - -static struct mwl8k_ampdu_stream * -mwl8k_lookup_stream(struct ieee80211_hw *hw, u8 *addr, u8 tid) -{ - struct mwl8k_priv *priv = hw->priv; - int i; - - for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { - struct mwl8k_ampdu_stream *stream; - stream = &priv->ampdu[i]; - if (stream->state == AMPDU_NO_STREAM) - continue; - if (!memcmp(stream->sta->addr, addr, ETH_ALEN) && - stream->tid == tid) - return stream; - } - return NULL; -} - -#define MWL8K_AMPDU_PACKET_THRESHOLD 64 -static inline bool mwl8k_ampdu_allowed(struct ieee80211_sta *sta, u8 tid) -{ - struct mwl8k_sta *sta_info = MWL8K_STA(sta); - struct tx_traffic_info *tx_stats; - - BUG_ON(tid >= MWL8K_MAX_TID); - tx_stats = &sta_info->tx_stats[tid]; - - return sta_info->is_ampdu_allowed && - tx_stats->pkts > MWL8K_AMPDU_PACKET_THRESHOLD; -} - -static inline void mwl8k_tx_count_packet(struct ieee80211_sta *sta, u8 tid) -{ - struct mwl8k_sta *sta_info = MWL8K_STA(sta); - struct tx_traffic_info *tx_stats; - - BUG_ON(tid >= MWL8K_MAX_TID); - tx_stats = &sta_info->tx_stats[tid]; - - if (tx_stats->start_time == 0) - tx_stats->start_time = jiffies; - - /* reset the packet count after each second elapses. If the number of - * packets ever exceeds the ampdu_min_traffic threshold, we will allow - * an ampdu stream to be started. - */ - if (jiffies - tx_stats->start_time > HZ) { - tx_stats->pkts = 0; - tx_stats->start_time = 0; - } else - tx_stats->pkts++; -} - -/* The hardware ampdu queues start from 5. - * txpriorities for ampdu queues are - * 5 6 7 0 1 2 3 4 ie., queue 5 is highest - * and queue 3 is lowest (queue 4 is reserved) - */ -#define BA_QUEUE 5 - -static void -mwl8k_txq_xmit(struct ieee80211_hw *hw, - int index, - struct ieee80211_sta *sta, - struct sk_buff *skb) -{ - struct mwl8k_priv *priv = hw->priv; - struct ieee80211_tx_info *tx_info; - struct mwl8k_vif *mwl8k_vif; - struct ieee80211_hdr *wh; - struct mwl8k_tx_queue *txq; - struct mwl8k_tx_desc *tx; - dma_addr_t dma; - u32 txstatus; - u8 txdatarate; - u16 qos; - int txpriority; - u8 tid = 0; - struct mwl8k_ampdu_stream *stream = NULL; - bool start_ba_session = false; - bool mgmtframe = false; - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; - bool eapol_frame = false; - - wh = (struct ieee80211_hdr *)skb->data; - if (ieee80211_is_data_qos(wh->frame_control)) - qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); - else - qos = 0; - - if (skb->protocol == cpu_to_be16(ETH_P_PAE)) - eapol_frame = true; - - if (ieee80211_is_mgmt(wh->frame_control)) - mgmtframe = true; - - if (priv->ap_fw) - mwl8k_encapsulate_tx_frame(priv, skb); - else - mwl8k_add_dma_header(priv, skb, 0, 0); - - wh = &((struct mwl8k_dma_data *)skb->data)->wh; - - tx_info = IEEE80211_SKB_CB(skb); - mwl8k_vif = MWL8K_VIF(tx_info->control.vif); - - if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno); - mwl8k_vif->seqno += 0x10; - } - - /* Setup firmware control bit fields for each frame type. */ - txstatus = 0; - txdatarate = 0; - if (ieee80211_is_mgmt(wh->frame_control) || - ieee80211_is_ctl(wh->frame_control)) { - txdatarate = 0; - qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP; - } else if (ieee80211_is_data(wh->frame_control)) { - txdatarate = 1; - if (is_multicast_ether_addr(wh->addr1)) - txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX; - - qos &= ~MWL8K_QOS_ACK_POLICY_MASK; - if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) - qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK; - else - qos |= MWL8K_QOS_ACK_POLICY_NORMAL; - } - - /* Queue ADDBA request in the respective data queue. While setting up - * the ampdu stream, mac80211 queues further packets for that - * particular ra/tid pair. However, packets piled up in the hardware - * for that ra/tid pair will still go out. ADDBA request and the - * related data packets going out from different queues asynchronously - * will cause a shift in the receiver window which might result in - * ampdu packets getting dropped at the receiver after the stream has - * been setup. - */ - if (unlikely(ieee80211_is_action(wh->frame_control) && - mgmt->u.action.category == WLAN_CATEGORY_BACK && - mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ && - priv->ap_fw)) { - u16 capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); - tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; - index = mwl8k_tid_queue_mapping(tid); - } - - txpriority = index; - - if (priv->ap_fw && sta && sta->ht_cap.ht_supported && !eapol_frame && - ieee80211_is_data_qos(wh->frame_control)) { - tid = qos & 0xf; - mwl8k_tx_count_packet(sta, tid); - spin_lock(&priv->stream_lock); - stream = mwl8k_lookup_stream(hw, sta->addr, tid); - if (stream != NULL) { - if (stream->state == AMPDU_STREAM_ACTIVE) { - WARN_ON(!(qos & MWL8K_QOS_ACK_POLICY_BLOCKACK)); - txpriority = (BA_QUEUE + stream->idx) % - TOTAL_HW_TX_QUEUES; - if (stream->idx <= 1) - index = stream->idx + - MWL8K_TX_WMM_QUEUES; - - } else if (stream->state == AMPDU_STREAM_NEW) { - /* We get here if the driver sends us packets - * after we've initiated a stream, but before - * our ampdu_action routine has been called - * with IEEE80211_AMPDU_TX_START to get the SSN - * for the ADDBA request. So this packet can - * go out with no risk of sequence number - * mismatch. No special handling is required. - */ - } else { - /* Drop packets that would go out after the - * ADDBA request was sent but before the ADDBA - * response is received. If we don't do this, - * the recipient would probably receive it - * after the ADDBA request with SSN 0. This - * will cause the recipient's BA receive window - * to shift, which would cause the subsequent - * packets in the BA stream to be discarded. - * mac80211 queues our packets for us in this - * case, so this is really just a safety check. - */ - wiphy_warn(hw->wiphy, - "Cannot send packet while ADDBA " - "dialog is underway.\n"); - spin_unlock(&priv->stream_lock); - dev_kfree_skb(skb); - return; - } - } else { - /* Defer calling mwl8k_start_stream so that the current - * skb can go out before the ADDBA request. This - * prevents sequence number mismatch at the recepient - * as described above. - */ - if (mwl8k_ampdu_allowed(sta, tid)) { - stream = mwl8k_add_stream(hw, sta, tid); - if (stream != NULL) - start_ba_session = true; - } - } - spin_unlock(&priv->stream_lock); - } else { - qos &= ~MWL8K_QOS_ACK_POLICY_MASK; - qos |= MWL8K_QOS_ACK_POLICY_NORMAL; - } - - dma = pci_map_single(priv->pdev, skb->data, - skb->len, PCI_DMA_TODEVICE); - - if (pci_dma_mapping_error(priv->pdev, dma)) { - wiphy_debug(hw->wiphy, - "failed to dma map skb, dropping TX frame.\n"); - if (start_ba_session) { - spin_lock(&priv->stream_lock); - mwl8k_remove_stream(hw, stream); - spin_unlock(&priv->stream_lock); - } - dev_kfree_skb(skb); - return; - } - - spin_lock_bh(&priv->tx_lock); - - txq = priv->txq + index; - - /* Mgmt frames that go out frequently are probe - * responses. Other mgmt frames got out relatively - * infrequently. Hence reserve 2 buffers so that - * other mgmt frames do not get dropped due to an - * already queued probe response in one of the - * reserved buffers. - */ - - if (txq->len >= MWL8K_TX_DESCS - 2) { - if (!mgmtframe || txq->len == MWL8K_TX_DESCS) { - if (start_ba_session) { - spin_lock(&priv->stream_lock); - mwl8k_remove_stream(hw, stream); - spin_unlock(&priv->stream_lock); - } - mwl8k_tx_start(priv); - spin_unlock_bh(&priv->tx_lock); - pci_unmap_single(priv->pdev, dma, skb->len, - PCI_DMA_TODEVICE); - dev_kfree_skb(skb); - return; - } - } - - BUG_ON(txq->skb[txq->tail] != NULL); - txq->skb[txq->tail] = skb; - - tx = txq->txd + txq->tail; - tx->data_rate = txdatarate; - tx->tx_priority = txpriority; - tx->qos_control = cpu_to_le16(qos); - tx->pkt_phys_addr = cpu_to_le32(dma); - tx->pkt_len = cpu_to_le16(skb->len); - tx->rate_info = 0; - if (!priv->ap_fw && sta != NULL) - tx->peer_id = MWL8K_STA(sta)->peer_id; - else - tx->peer_id = 0; - - if (priv->ap_fw && ieee80211_is_data(wh->frame_control) && !eapol_frame) - tx->timestamp = cpu_to_le32(ioread32(priv->regs + - MWL8K_HW_TIMER_REGISTER)); - else - tx->timestamp = 0; - - wmb(); - tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); - - txq->len++; - priv->pending_tx_pkts++; - - txq->tail++; - if (txq->tail == MWL8K_TX_DESCS) - txq->tail = 0; - - mwl8k_tx_start(priv); - - spin_unlock_bh(&priv->tx_lock); - - /* Initiate the ampdu session here */ - if (start_ba_session) { - spin_lock(&priv->stream_lock); - if (mwl8k_start_stream(hw, stream)) - mwl8k_remove_stream(hw, stream); - spin_unlock(&priv->stream_lock); - } -} - - -/* - * Firmware access. - * - * We have the following requirements for issuing firmware commands: - * - Some commands require that the packet transmit path is idle when - * the command is issued. (For simplicity, we'll just quiesce the - * transmit path for every command.) - * - There are certain sequences of commands that need to be issued to - * the hardware sequentially, with no other intervening commands. - * - * This leads to an implementation of a "firmware lock" as a mutex that - * can be taken recursively, and which is taken by both the low-level - * command submission function (mwl8k_post_cmd) as well as any users of - * that function that require issuing of an atomic sequence of commands, - * and quiesces the transmit path whenever it's taken. - */ -static int mwl8k_fw_lock(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - - if (priv->fw_mutex_owner != current) { - int rc; - - mutex_lock(&priv->fw_mutex); - ieee80211_stop_queues(hw); - - rc = mwl8k_tx_wait_empty(hw); - if (rc) { - if (!priv->hw_restart_in_progress) - ieee80211_wake_queues(hw); - - mutex_unlock(&priv->fw_mutex); - - return rc; - } - - priv->fw_mutex_owner = current; - } - - priv->fw_mutex_depth++; - - return 0; -} - -static void mwl8k_fw_unlock(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - - if (!--priv->fw_mutex_depth) { - if (!priv->hw_restart_in_progress) - ieee80211_wake_queues(hw); - - priv->fw_mutex_owner = NULL; - mutex_unlock(&priv->fw_mutex); - } -} - -static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, - u32 bitmap); - -/* - * Command processing. - */ - -/* Timeout firmware commands after 10s */ -#define MWL8K_CMD_TIMEOUT_MS 10000 - -static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) -{ - DECLARE_COMPLETION_ONSTACK(cmd_wait); - struct mwl8k_priv *priv = hw->priv; - void __iomem *regs = priv->regs; - dma_addr_t dma_addr; - unsigned int dma_size; - int rc; - unsigned long timeout = 0; - u8 buf[32]; - u32 bitmap = 0; - - wiphy_dbg(hw->wiphy, "Posting %s [%d]\n", - mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), cmd->macid); - - /* Before posting firmware commands that could change the hardware - * characteristics, make sure that all BSSes are stopped temporary. - * Enable these stopped BSSes after completion of the commands - */ - - rc = mwl8k_fw_lock(hw); - if (rc) - return rc; - - if (priv->ap_fw && priv->running_bsses) { - switch (le16_to_cpu(cmd->code)) { - case MWL8K_CMD_SET_RF_CHANNEL: - case MWL8K_CMD_RADIO_CONTROL: - case MWL8K_CMD_RF_TX_POWER: - case MWL8K_CMD_TX_POWER: - case MWL8K_CMD_RF_ANTENNA: - case MWL8K_CMD_RTS_THRESHOLD: - case MWL8K_CMD_MIMO_CONFIG: - bitmap = priv->running_bsses; - mwl8k_enable_bsses(hw, false, bitmap); - break; - } - } - - cmd->result = (__force __le16) 0xffff; - dma_size = le16_to_cpu(cmd->length); - dma_addr = pci_map_single(priv->pdev, cmd, dma_size, - PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(priv->pdev, dma_addr)) - return -ENOMEM; - - priv->hostcmd_wait = &cmd_wait; - iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); - iowrite32(MWL8K_H2A_INT_DOORBELL, - regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); - iowrite32(MWL8K_H2A_INT_DUMMY, - regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); - - timeout = wait_for_completion_timeout(&cmd_wait, - msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS)); - - priv->hostcmd_wait = NULL; - - - pci_unmap_single(priv->pdev, dma_addr, dma_size, - PCI_DMA_BIDIRECTIONAL); - - if (!timeout) { - wiphy_err(hw->wiphy, "Command %s timeout after %u ms\n", - mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), - MWL8K_CMD_TIMEOUT_MS); - rc = -ETIMEDOUT; - } else { - int ms; - - ms = MWL8K_CMD_TIMEOUT_MS - jiffies_to_msecs(timeout); - - rc = cmd->result ? -EINVAL : 0; - if (rc) - wiphy_err(hw->wiphy, "Command %s error 0x%x\n", - mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), - le16_to_cpu(cmd->result)); - else if (ms > 2000) - wiphy_notice(hw->wiphy, "Command %s took %d ms\n", - mwl8k_cmd_name(cmd->code, - buf, sizeof(buf)), - ms); - } - - if (bitmap) - mwl8k_enable_bsses(hw, true, bitmap); - - mwl8k_fw_unlock(hw); - - return rc; -} - -static int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct mwl8k_cmd_pkt *cmd) -{ - if (vif != NULL) - cmd->macid = MWL8K_VIF(vif)->macid; - return mwl8k_post_cmd(hw, cmd); -} - -/* - * Setup code shared between STA and AP firmware images. - */ -static void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - - BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24)); - memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24)); - - BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24)); - memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24)); - - priv->band_24.band = IEEE80211_BAND_2GHZ; - priv->band_24.channels = priv->channels_24; - priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24); - priv->band_24.bitrates = priv->rates_24; - priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24); - - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24; -} - -static void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - - BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50)); - memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50)); - - BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50)); - memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50)); - - priv->band_50.band = IEEE80211_BAND_5GHZ; - priv->band_50.channels = priv->channels_50; - priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50); - priv->band_50.bitrates = priv->rates_50; - priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50); - - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->band_50; -} - -/* - * CMD_GET_HW_SPEC (STA version). - */ -struct mwl8k_cmd_get_hw_spec_sta { - struct mwl8k_cmd_pkt header; - __u8 hw_rev; - __u8 host_interface; - __le16 num_mcaddrs; - __u8 perm_addr[ETH_ALEN]; - __le16 region_code; - __le32 fw_rev; - __le32 ps_cookie; - __le32 caps; - __u8 mcs_bitmap[16]; - __le32 rx_queue_ptr; - __le32 num_tx_queues; - __le32 tx_queue_ptrs[MWL8K_TX_WMM_QUEUES]; - __le32 caps2; - __le32 num_tx_desc_per_queue; - __le32 total_rxd; -} __packed; - -#define MWL8K_CAP_MAX_AMSDU 0x20000000 -#define MWL8K_CAP_GREENFIELD 0x08000000 -#define MWL8K_CAP_AMPDU 0x04000000 -#define MWL8K_CAP_RX_STBC 0x01000000 -#define MWL8K_CAP_TX_STBC 0x00800000 -#define MWL8K_CAP_SHORTGI_40MHZ 0x00400000 -#define MWL8K_CAP_SHORTGI_20MHZ 0x00200000 -#define MWL8K_CAP_RX_ANTENNA_MASK 0x000e0000 -#define MWL8K_CAP_TX_ANTENNA_MASK 0x0001c000 -#define MWL8K_CAP_DELAY_BA 0x00003000 -#define MWL8K_CAP_MIMO 0x00000200 -#define MWL8K_CAP_40MHZ 0x00000100 -#define MWL8K_CAP_BAND_MASK 0x00000007 -#define MWL8K_CAP_5GHZ 0x00000004 -#define MWL8K_CAP_2GHZ4 0x00000001 - -static void -mwl8k_set_ht_caps(struct ieee80211_hw *hw, - struct ieee80211_supported_band *band, u32 cap) -{ - int rx_streams; - int tx_streams; - - band->ht_cap.ht_supported = 1; - - if (cap & MWL8K_CAP_MAX_AMSDU) - band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; - if (cap & MWL8K_CAP_GREENFIELD) - band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD; - if (cap & MWL8K_CAP_AMPDU) { - ieee80211_hw_set(hw, AMPDU_AGGREGATION); - band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; - } - if (cap & MWL8K_CAP_RX_STBC) - band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC; - if (cap & MWL8K_CAP_TX_STBC) - band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; - if (cap & MWL8K_CAP_SHORTGI_40MHZ) - band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - if (cap & MWL8K_CAP_SHORTGI_20MHZ) - band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; - if (cap & MWL8K_CAP_DELAY_BA) - band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA; - if (cap & MWL8K_CAP_40MHZ) - band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - - rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK); - tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK); - - band->ht_cap.mcs.rx_mask[0] = 0xff; - if (rx_streams >= 2) - band->ht_cap.mcs.rx_mask[1] = 0xff; - if (rx_streams >= 3) - band->ht_cap.mcs.rx_mask[2] = 0xff; - band->ht_cap.mcs.rx_mask[4] = 0x01; - band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - - if (rx_streams != tx_streams) { - band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; - band->ht_cap.mcs.tx_params |= (tx_streams - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; - } -} - -static void -mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps) -{ - struct mwl8k_priv *priv = hw->priv; - - if (priv->caps) - return; - - if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) { - mwl8k_setup_2ghz_band(hw); - if (caps & MWL8K_CAP_MIMO) - mwl8k_set_ht_caps(hw, &priv->band_24, caps); - } - - if (caps & MWL8K_CAP_5GHZ) { - mwl8k_setup_5ghz_band(hw); - if (caps & MWL8K_CAP_MIMO) - mwl8k_set_ht_caps(hw, &priv->band_50, caps); - } - - priv->caps = caps; -} - -static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_get_hw_spec_sta *cmd; - int rc; - int i; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr)); - cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); - cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); - cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv)); - for (i = 0; i < mwl8k_tx_queues(priv); i++) - cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); - cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); - cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); - - rc = mwl8k_post_cmd(hw, &cmd->header); - - if (!rc) { - SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr); - priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); - priv->fw_rev = le32_to_cpu(cmd->fw_rev); - priv->hw_rev = cmd->hw_rev; - mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); - priv->ap_macids_supported = 0x00000000; - priv->sta_macids_supported = 0x00000001; - } - - kfree(cmd); - return rc; -} - -/* - * CMD_GET_HW_SPEC (AP version). - */ -struct mwl8k_cmd_get_hw_spec_ap { - struct mwl8k_cmd_pkt header; - __u8 hw_rev; - __u8 host_interface; - __le16 num_wcb; - __le16 num_mcaddrs; - __u8 perm_addr[ETH_ALEN]; - __le16 region_code; - __le16 num_antenna; - __le32 fw_rev; - __le32 wcbbase0; - __le32 rxwrptr; - __le32 rxrdptr; - __le32 ps_cookie; - __le32 wcbbase1; - __le32 wcbbase2; - __le32 wcbbase3; - __le32 fw_api_version; - __le32 caps; - __le32 num_of_ampdu_queues; - __le32 wcbbase_ampdu[MWL8K_MAX_AMPDU_QUEUES]; -} __packed; - -static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_get_hw_spec_ap *cmd; - int rc, i; - u32 api_version; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_HW_SPEC); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr)); - cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); - - rc = mwl8k_post_cmd(hw, &cmd->header); - - if (!rc) { - int off; - - api_version = le32_to_cpu(cmd->fw_api_version); - if (priv->device_info->fw_api_ap != api_version) { - printk(KERN_ERR "%s: Unsupported fw API version for %s." - " Expected %d got %d.\n", MWL8K_NAME, - priv->device_info->part_name, - priv->device_info->fw_api_ap, - api_version); - rc = -EINVAL; - goto done; - } - SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr); - priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); - priv->fw_rev = le32_to_cpu(cmd->fw_rev); - priv->hw_rev = cmd->hw_rev; - mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); - priv->ap_macids_supported = 0x000000ff; - priv->sta_macids_supported = 0x00000100; - priv->num_ampdu_queues = le32_to_cpu(cmd->num_of_ampdu_queues); - if (priv->num_ampdu_queues > MWL8K_MAX_AMPDU_QUEUES) { - wiphy_warn(hw->wiphy, "fw reported %d ampdu queues" - " but we only support %d.\n", - priv->num_ampdu_queues, - MWL8K_MAX_AMPDU_QUEUES); - priv->num_ampdu_queues = MWL8K_MAX_AMPDU_QUEUES; - } - off = le32_to_cpu(cmd->rxwrptr) & 0xffff; - iowrite32(priv->rxq[0].rxd_dma, priv->sram + off); - - off = le32_to_cpu(cmd->rxrdptr) & 0xffff; - iowrite32(priv->rxq[0].rxd_dma, priv->sram + off); - - priv->txq_offset[0] = le32_to_cpu(cmd->wcbbase0) & 0xffff; - priv->txq_offset[1] = le32_to_cpu(cmd->wcbbase1) & 0xffff; - priv->txq_offset[2] = le32_to_cpu(cmd->wcbbase2) & 0xffff; - priv->txq_offset[3] = le32_to_cpu(cmd->wcbbase3) & 0xffff; - - for (i = 0; i < priv->num_ampdu_queues; i++) - priv->txq_offset[i + MWL8K_TX_WMM_QUEUES] = - le32_to_cpu(cmd->wcbbase_ampdu[i]) & 0xffff; - } - -done: - kfree(cmd); - return rc; -} - -/* - * CMD_SET_HW_SPEC. - */ -struct mwl8k_cmd_set_hw_spec { - struct mwl8k_cmd_pkt header; - __u8 hw_rev; - __u8 host_interface; - __le16 num_mcaddrs; - __u8 perm_addr[ETH_ALEN]; - __le16 region_code; - __le32 fw_rev; - __le32 ps_cookie; - __le32 caps; - __le32 rx_queue_ptr; - __le32 num_tx_queues; - __le32 tx_queue_ptrs[MWL8K_MAX_TX_QUEUES]; - __le32 flags; - __le32 num_tx_desc_per_queue; - __le32 total_rxd; -} __packed; - -/* If enabled, MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY will cause - * packets to expire 500 ms after the timestamp in the tx descriptor. That is, - * the packets that are queued for more than 500ms, will be dropped in the - * hardware. This helps minimizing the issues caused due to head-of-line - * blocking where a slow client can hog the bandwidth and affect traffic to a - * faster client. - */ -#define MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY 0x00000400 -#define MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR 0x00000200 -#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 -#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP 0x00000020 -#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON 0x00000010 - -static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_set_hw_spec *cmd; - int rc; - int i; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_HW_SPEC); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); - cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); - cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv)); - - /* - * Mac80211 stack has Q0 as highest priority and Q3 as lowest in - * that order. Firmware has Q3 as highest priority and Q0 as lowest - * in that order. Map Q3 of mac80211 to Q0 of firmware so that the - * priority is interpreted the right way in firmware. - */ - for (i = 0; i < mwl8k_tx_queues(priv); i++) { - int j = mwl8k_tx_queues(priv) - 1 - i; - cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[j].txd_dma); - } - - cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT | - MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP | - MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON | - MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY | - MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR); - cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); - cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_MAC_MULTICAST_ADR. - */ -struct mwl8k_cmd_mac_multicast_adr { - struct mwl8k_cmd_pkt header; - __le16 action; - __le16 numaddr; - __u8 addr[0][ETH_ALEN]; -}; - -#define MWL8K_ENABLE_RX_DIRECTED 0x0001 -#define MWL8K_ENABLE_RX_MULTICAST 0x0002 -#define MWL8K_ENABLE_RX_ALL_MULTICAST 0x0004 -#define MWL8K_ENABLE_RX_BROADCAST 0x0008 - -static struct mwl8k_cmd_pkt * -__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti, - struct netdev_hw_addr_list *mc_list) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_mac_multicast_adr *cmd; - int size; - int mc_count = 0; - - if (mc_list) - mc_count = netdev_hw_addr_list_count(mc_list); - - if (allmulti || mc_count > priv->num_mcaddrs) { - allmulti = 1; - mc_count = 0; - } - - size = sizeof(*cmd) + mc_count * ETH_ALEN; - - cmd = kzalloc(size, GFP_ATOMIC); - if (cmd == NULL) - return NULL; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR); - cmd->header.length = cpu_to_le16(size); - cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_DIRECTED | - MWL8K_ENABLE_RX_BROADCAST); - - if (allmulti) { - cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_ALL_MULTICAST); - } else if (mc_count) { - struct netdev_hw_addr *ha; - int i = 0; - - cmd->action |= cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST); - cmd->numaddr = cpu_to_le16(mc_count); - netdev_hw_addr_list_for_each(ha, mc_list) { - memcpy(cmd->addr[i], ha->addr, ETH_ALEN); - } - } - - return &cmd->header; -} - -/* - * CMD_GET_STAT. - */ -struct mwl8k_cmd_get_stat { - struct mwl8k_cmd_pkt header; - __le32 stats[64]; -} __packed; - -#define MWL8K_STAT_ACK_FAILURE 9 -#define MWL8K_STAT_RTS_FAILURE 12 -#define MWL8K_STAT_FCS_ERROR 24 -#define MWL8K_STAT_RTS_SUCCESS 11 - -static int mwl8k_cmd_get_stat(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) -{ - struct mwl8k_cmd_get_stat *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_STAT); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - rc = mwl8k_post_cmd(hw, &cmd->header); - if (!rc) { - stats->dot11ACKFailureCount = - le32_to_cpu(cmd->stats[MWL8K_STAT_ACK_FAILURE]); - stats->dot11RTSFailureCount = - le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_FAILURE]); - stats->dot11FCSErrorCount = - le32_to_cpu(cmd->stats[MWL8K_STAT_FCS_ERROR]); - stats->dot11RTSSuccessCount = - le32_to_cpu(cmd->stats[MWL8K_STAT_RTS_SUCCESS]); - } - kfree(cmd); - - return rc; -} - -/* - * CMD_RADIO_CONTROL. - */ -struct mwl8k_cmd_radio_control { - struct mwl8k_cmd_pkt header; - __le16 action; - __le16 control; - __le16 radio_on; -} __packed; - -static int -mwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_radio_control *cmd; - int rc; - - if (enable == priv->radio_on && !force) - return 0; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_RADIO_CONTROL); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->control = cpu_to_le16(priv->radio_short_preamble ? 3 : 1); - cmd->radio_on = cpu_to_le16(enable ? 0x0001 : 0x0000); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - if (!rc) - priv->radio_on = enable; - - return rc; -} - -static int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw) -{ - return mwl8k_cmd_radio_control(hw, 0, 0); -} - -static int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw) -{ - return mwl8k_cmd_radio_control(hw, 1, 0); -} - -static int -mwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble) -{ - struct mwl8k_priv *priv = hw->priv; - - priv->radio_short_preamble = short_preamble; - - return mwl8k_cmd_radio_control(hw, 1, 1); -} - -/* - * CMD_RF_TX_POWER. - */ -#define MWL8K_RF_TX_POWER_LEVEL_TOTAL 8 - -struct mwl8k_cmd_rf_tx_power { - struct mwl8k_cmd_pkt header; - __le16 action; - __le16 support_level; - __le16 current_level; - __le16 reserved; - __le16 power_level_list[MWL8K_RF_TX_POWER_LEVEL_TOTAL]; -} __packed; - -static int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm) -{ - struct mwl8k_cmd_rf_tx_power *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_TX_POWER); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->support_level = cpu_to_le16(dBm); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_TX_POWER. - */ -#define MWL8K_TX_POWER_LEVEL_TOTAL 12 - -struct mwl8k_cmd_tx_power { - struct mwl8k_cmd_pkt header; - __le16 action; - __le16 band; - __le16 channel; - __le16 bw; - __le16 sub_ch; - __le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL]; -} __packed; - -static int mwl8k_cmd_tx_power(struct ieee80211_hw *hw, - struct ieee80211_conf *conf, - unsigned short pwr) -{ - struct ieee80211_channel *channel = conf->chandef.chan; - enum nl80211_channel_type channel_type = - cfg80211_get_chandef_type(&conf->chandef); - struct mwl8k_cmd_tx_power *cmd; - int rc; - int i; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_TX_POWER); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_CMD_SET_LIST); - - if (channel->band == IEEE80211_BAND_2GHZ) - cmd->band = cpu_to_le16(0x1); - else if (channel->band == IEEE80211_BAND_5GHZ) - cmd->band = cpu_to_le16(0x4); - - cmd->channel = cpu_to_le16(channel->hw_value); - - if (channel_type == NL80211_CHAN_NO_HT || - channel_type == NL80211_CHAN_HT20) { - cmd->bw = cpu_to_le16(0x2); - } else { - cmd->bw = cpu_to_le16(0x4); - if (channel_type == NL80211_CHAN_HT40MINUS) - cmd->sub_ch = cpu_to_le16(0x3); - else if (channel_type == NL80211_CHAN_HT40PLUS) - cmd->sub_ch = cpu_to_le16(0x1); - } - - for (i = 0; i < MWL8K_TX_POWER_LEVEL_TOTAL; i++) - cmd->power_level_list[i] = cpu_to_le16(pwr); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_RF_ANTENNA. - */ -struct mwl8k_cmd_rf_antenna { - struct mwl8k_cmd_pkt header; - __le16 antenna; - __le16 mode; -} __packed; - -#define MWL8K_RF_ANTENNA_RX 1 -#define MWL8K_RF_ANTENNA_TX 2 - -static int -mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask) -{ - struct mwl8k_cmd_rf_antenna *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_RF_ANTENNA); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->antenna = cpu_to_le16(antenna); - cmd->mode = cpu_to_le16(mask); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_SET_BEACON. - */ -struct mwl8k_cmd_set_beacon { - struct mwl8k_cmd_pkt header; - __le16 beacon_len; - __u8 beacon[0]; -}; - -static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u8 *beacon, int len) -{ - struct mwl8k_cmd_set_beacon *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON); - cmd->header.length = cpu_to_le16(sizeof(*cmd) + len); - cmd->beacon_len = cpu_to_le16(len); - memcpy(cmd->beacon, beacon, len); - - rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_SET_PRE_SCAN. - */ -struct mwl8k_cmd_set_pre_scan { - struct mwl8k_cmd_pkt header; -} __packed; - -static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw) -{ - struct mwl8k_cmd_set_pre_scan *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_PRE_SCAN); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_BBP_REG_ACCESS. - */ -struct mwl8k_cmd_bbp_reg_access { - struct mwl8k_cmd_pkt header; - __le16 action; - __le16 offset; - u8 value; - u8 rsrv[3]; -} __packed; - -static int -mwl8k_cmd_bbp_reg_access(struct ieee80211_hw *hw, - u16 action, - u16 offset, - u8 *value) -{ - struct mwl8k_cmd_bbp_reg_access *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_BBP_REG_ACCESS); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(action); - cmd->offset = cpu_to_le16(offset); - - rc = mwl8k_post_cmd(hw, &cmd->header); - - if (!rc) - *value = cmd->value; - else - *value = 0; - - kfree(cmd); - - return rc; -} - -/* - * CMD_SET_POST_SCAN. - */ -struct mwl8k_cmd_set_post_scan { - struct mwl8k_cmd_pkt header; - __le32 isibss; - __u8 bssid[ETH_ALEN]; -} __packed; - -static int -mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac) -{ - struct mwl8k_cmd_set_post_scan *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_POST_SCAN); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->isibss = 0; - memcpy(cmd->bssid, mac, ETH_ALEN); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -static int freq_to_idx(struct mwl8k_priv *priv, int freq) -{ - struct ieee80211_supported_band *sband; - int band, ch, idx = 0; - - for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { - sband = priv->hw->wiphy->bands[band]; - if (!sband) - continue; - - for (ch = 0; ch < sband->n_channels; ch++, idx++) - if (sband->channels[ch].center_freq == freq) - goto exit; - } - -exit: - return idx; -} - -static void mwl8k_update_survey(struct mwl8k_priv *priv, - struct ieee80211_channel *channel) -{ - u32 cca_cnt, rx_rdy; - s8 nf = 0, idx; - struct survey_info *survey; - - idx = freq_to_idx(priv, priv->acs_chan->center_freq); - if (idx >= MWL8K_NUM_CHANS) { - wiphy_err(priv->hw->wiphy, "Failed to update survey\n"); - return; - } - - survey = &priv->survey[idx]; - - cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG); - cca_cnt /= 1000; /* uSecs to mSecs */ - survey->time_busy = (u64) cca_cnt; - - rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG); - rx_rdy /= 1000; /* uSecs to mSecs */ - survey->time_rx = (u64) rx_rdy; - - priv->channel_time = jiffies - priv->channel_time; - survey->time = jiffies_to_msecs(priv->channel_time); - - survey->channel = channel; - - mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &nf); - - /* Make sure sign is negative else ACS at hostapd fails */ - survey->noise = nf * -1; - - survey->filled = SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_TIME | - SURVEY_INFO_TIME_BUSY | - SURVEY_INFO_TIME_RX; -} - -/* - * CMD_SET_RF_CHANNEL. - */ -struct mwl8k_cmd_set_rf_channel { - struct mwl8k_cmd_pkt header; - __le16 action; - __u8 current_channel; - __le32 channel_flags; -} __packed; - -static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, - struct ieee80211_conf *conf) -{ - struct ieee80211_channel *channel = conf->chandef.chan; - enum nl80211_channel_type channel_type = - cfg80211_get_chandef_type(&conf->chandef); - struct mwl8k_cmd_set_rf_channel *cmd; - struct mwl8k_priv *priv = hw->priv; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RF_CHANNEL); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->current_channel = channel->hw_value; - - if (channel->band == IEEE80211_BAND_2GHZ) - cmd->channel_flags |= cpu_to_le32(0x00000001); - else if (channel->band == IEEE80211_BAND_5GHZ) - cmd->channel_flags |= cpu_to_le32(0x00000004); - - if (!priv->sw_scan_start) { - if (channel_type == NL80211_CHAN_NO_HT || - channel_type == NL80211_CHAN_HT20) - cmd->channel_flags |= cpu_to_le32(0x00000080); - else if (channel_type == NL80211_CHAN_HT40MINUS) - cmd->channel_flags |= cpu_to_le32(0x000001900); - else if (channel_type == NL80211_CHAN_HT40PLUS) - cmd->channel_flags |= cpu_to_le32(0x000000900); - } else { - cmd->channel_flags |= cpu_to_le32(0x00000080); - } - - if (priv->sw_scan_start) { - /* Store current channel stats - * before switching to newer one. - * This will be processed only for AP fw. - */ - if (priv->channel_time != 0) - mwl8k_update_survey(priv, priv->acs_chan); - - priv->channel_time = jiffies; - priv->acs_chan = channel; - } - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_SET_AID. - */ -#define MWL8K_FRAME_PROT_DISABLED 0x00 -#define MWL8K_FRAME_PROT_11G 0x07 -#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02 -#define MWL8K_FRAME_PROT_11N_HT_ALL 0x06 - -struct mwl8k_cmd_update_set_aid { - struct mwl8k_cmd_pkt header; - __le16 aid; - - /* AP's MAC address (BSSID) */ - __u8 bssid[ETH_ALEN]; - __le16 protection_mode; - __u8 supp_rates[14]; -} __packed; - -static void legacy_rate_mask_to_array(u8 *rates, u32 mask) -{ - int i; - int j; - - /* - * Clear nonstandard rate 4. - */ - mask &= 0x1fef; - - for (i = 0, j = 0; i < 13; i++) { - if (mask & (1 << i)) - rates[j++] = mwl8k_rates_24[i].hw_value; - } -} - -static int -mwl8k_cmd_set_aid(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u32 legacy_rate_mask) -{ - struct mwl8k_cmd_update_set_aid *cmd; - u16 prot_mode; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->aid = cpu_to_le16(vif->bss_conf.aid); - memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); - - if (vif->bss_conf.use_cts_prot) { - prot_mode = MWL8K_FRAME_PROT_11G; - } else { - switch (vif->bss_conf.ht_operation_mode & - IEEE80211_HT_OP_MODE_PROTECTION) { - case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: - prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY; - break; - case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: - prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL; - break; - default: - prot_mode = MWL8K_FRAME_PROT_DISABLED; - break; - } - } - cmd->protection_mode = cpu_to_le16(prot_mode); - - legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_SET_RATE. - */ -struct mwl8k_cmd_set_rate { - struct mwl8k_cmd_pkt header; - __u8 legacy_rates[14]; - - /* Bitmap for supported MCS codes. */ - __u8 mcs_set[16]; - __u8 reserved[16]; -} __packed; - -static int -mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 legacy_rate_mask, u8 *mcs_rates) -{ - struct mwl8k_cmd_set_rate *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask); - memcpy(cmd->mcs_set, mcs_rates, 16); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_FINALIZE_JOIN. - */ -#define MWL8K_FJ_BEACON_MAXLEN 128 - -struct mwl8k_cmd_finalize_join { - struct mwl8k_cmd_pkt header; - __le32 sleep_interval; /* Number of beacon periods to sleep */ - __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN]; -} __packed; - -static int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame, - int framelen, int dtim) -{ - struct mwl8k_cmd_finalize_join *cmd; - struct ieee80211_mgmt *payload = frame; - int payload_len; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1); - - payload_len = framelen - ieee80211_hdrlen(payload->frame_control); - if (payload_len < 0) - payload_len = 0; - else if (payload_len > MWL8K_FJ_BEACON_MAXLEN) - payload_len = MWL8K_FJ_BEACON_MAXLEN; - - memcpy(cmd->beacon_data, &payload->u.beacon, payload_len); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_SET_RTS_THRESHOLD. - */ -struct mwl8k_cmd_set_rts_threshold { - struct mwl8k_cmd_pkt header; - __le16 action; - __le16 threshold; -} __packed; - -static int -mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh) -{ - struct mwl8k_cmd_set_rts_threshold *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->threshold = cpu_to_le16(rts_thresh); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_SET_SLOT. - */ -struct mwl8k_cmd_set_slot { - struct mwl8k_cmd_pkt header; - __le16 action; - __u8 short_slot; -} __packed; - -static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time) -{ - struct mwl8k_cmd_set_slot *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->short_slot = short_slot_time; - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_SET_EDCA_PARAMS. - */ -struct mwl8k_cmd_set_edca_params { - struct mwl8k_cmd_pkt header; - - /* See MWL8K_SET_EDCA_XXX below */ - __le16 action; - - /* TX opportunity in units of 32 us */ - __le16 txop; - - union { - struct { - /* Log exponent of max contention period: 0...15 */ - __le32 log_cw_max; - - /* Log exponent of min contention period: 0...15 */ - __le32 log_cw_min; - - /* Adaptive interframe spacing in units of 32us */ - __u8 aifs; - - /* TX queue to configure */ - __u8 txq; - } ap; - struct { - /* Log exponent of max contention period: 0...15 */ - __u8 log_cw_max; - - /* Log exponent of min contention period: 0...15 */ - __u8 log_cw_min; - - /* Adaptive interframe spacing in units of 32us */ - __u8 aifs; - - /* TX queue to configure */ - __u8 txq; - } sta; - }; -} __packed; - -#define MWL8K_SET_EDCA_CW 0x01 -#define MWL8K_SET_EDCA_TXOP 0x02 -#define MWL8K_SET_EDCA_AIFS 0x04 - -#define MWL8K_SET_EDCA_ALL (MWL8K_SET_EDCA_CW | \ - MWL8K_SET_EDCA_TXOP | \ - MWL8K_SET_EDCA_AIFS) - -static int -mwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, - __u16 cw_min, __u16 cw_max, - __u8 aifs, __u16 txop) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_set_edca_params *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL); - cmd->txop = cpu_to_le16(txop); - if (priv->ap_fw) { - cmd->ap.log_cw_max = cpu_to_le32(ilog2(cw_max + 1)); - cmd->ap.log_cw_min = cpu_to_le32(ilog2(cw_min + 1)); - cmd->ap.aifs = aifs; - cmd->ap.txq = qnum; - } else { - cmd->sta.log_cw_max = (u8)ilog2(cw_max + 1); - cmd->sta.log_cw_min = (u8)ilog2(cw_min + 1); - cmd->sta.aifs = aifs; - cmd->sta.txq = qnum; - } - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_SET_WMM_MODE. - */ -struct mwl8k_cmd_set_wmm_mode { - struct mwl8k_cmd_pkt header; - __le16 action; -} __packed; - -static int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_set_wmm_mode *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(!!enable); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - if (!rc) - priv->wmm_enabled = enable; - - return rc; -} - -/* - * CMD_MIMO_CONFIG. - */ -struct mwl8k_cmd_mimo_config { - struct mwl8k_cmd_pkt header; - __le32 action; - __u8 rx_antenna_map; - __u8 tx_antenna_map; -} __packed; - -static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx) -{ - struct mwl8k_cmd_mimo_config *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET); - cmd->rx_antenna_map = rx; - cmd->tx_antenna_map = tx; - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_USE_FIXED_RATE (STA version). - */ -struct mwl8k_cmd_use_fixed_rate_sta { - struct mwl8k_cmd_pkt header; - __le32 action; - __le32 allow_rate_drop; - __le32 num_rates; - struct { - __le32 is_ht_rate; - __le32 enable_retry; - __le32 rate; - __le32 retry_count; - } rate_entry[8]; - __le32 rate_type; - __le32 reserved1; - __le32 reserved2; -} __packed; - -#define MWL8K_USE_AUTO_RATE 0x0002 -#define MWL8K_UCAST_RATE 0 - -static int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw) -{ - struct mwl8k_cmd_use_fixed_rate_sta *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); - cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_USE_FIXED_RATE (AP version). - */ -struct mwl8k_cmd_use_fixed_rate_ap { - struct mwl8k_cmd_pkt header; - __le32 action; - __le32 allow_rate_drop; - __le32 num_rates; - struct mwl8k_rate_entry_ap { - __le32 is_ht_rate; - __le32 enable_retry; - __le32 rate; - __le32 retry_count; - } rate_entry[4]; - u8 multicast_rate; - u8 multicast_rate_type; - u8 management_rate; -} __packed; - -static int -mwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt) -{ - struct mwl8k_cmd_use_fixed_rate_ap *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); - cmd->multicast_rate = mcast; - cmd->management_rate = mgmt; - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_ENABLE_SNIFFER. - */ -struct mwl8k_cmd_enable_sniffer { - struct mwl8k_cmd_pkt header; - __le32 action; -} __packed; - -static int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable) -{ - struct mwl8k_cmd_enable_sniffer *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32(!!enable); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -struct mwl8k_cmd_update_mac_addr { - struct mwl8k_cmd_pkt header; - union { - struct { - __le16 mac_type; - __u8 mac_addr[ETH_ALEN]; - } mbss; - __u8 mac_addr[ETH_ALEN]; - }; -} __packed; - -#define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0 -#define MWL8K_MAC_TYPE_SECONDARY_CLIENT 1 -#define MWL8K_MAC_TYPE_PRIMARY_AP 2 -#define MWL8K_MAC_TYPE_SECONDARY_AP 3 - -static int mwl8k_cmd_update_mac_addr(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u8 *mac, bool set) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); - struct mwl8k_cmd_update_mac_addr *cmd; - int mac_type; - int rc; - - mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; - if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) { - if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported)) - if (priv->ap_fw) - mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; - else - mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT; - else - mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; - } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) { - if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported)) - mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; - else - mac_type = MWL8K_MAC_TYPE_SECONDARY_AP; - } - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - if (set) - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); - else - cmd->header.code = cpu_to_le16(MWL8K_CMD_DEL_MAC_ADDR); - - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - if (priv->ap_fw) { - cmd->mbss.mac_type = cpu_to_le16(mac_type); - memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN); - } else { - memcpy(cmd->mac_addr, mac, ETH_ALEN); - } - - rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * MWL8K_CMD_SET_MAC_ADDR. - */ -static inline int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u8 *mac) -{ - return mwl8k_cmd_update_mac_addr(hw, vif, mac, true); -} - -/* - * MWL8K_CMD_DEL_MAC_ADDR. - */ -static inline int mwl8k_cmd_del_mac_addr(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u8 *mac) -{ - return mwl8k_cmd_update_mac_addr(hw, vif, mac, false); -} - -/* - * CMD_SET_RATEADAPT_MODE. - */ -struct mwl8k_cmd_set_rate_adapt_mode { - struct mwl8k_cmd_pkt header; - __le16 action; - __le16 mode; -} __packed; - -static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) -{ - struct mwl8k_cmd_set_rate_adapt_mode *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->mode = cpu_to_le16(mode); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_GET_WATCHDOG_BITMAP. - */ -struct mwl8k_cmd_get_watchdog_bitmap { - struct mwl8k_cmd_pkt header; - u8 bitmap; -} __packed; - -static int mwl8k_cmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap) -{ - struct mwl8k_cmd_get_watchdog_bitmap *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_WATCHDOG_BITMAP); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - rc = mwl8k_post_cmd(hw, &cmd->header); - if (!rc) - *bitmap = cmd->bitmap; - - kfree(cmd); - - return rc; -} - -#define MWL8K_WMM_QUEUE_NUMBER 3 - -static void mwl8k_destroy_ba(struct ieee80211_hw *hw, - u8 idx); - -static void mwl8k_watchdog_ba_events(struct work_struct *work) -{ - int rc; - u8 bitmap = 0, stream_index; - struct mwl8k_ampdu_stream *streams; - struct mwl8k_priv *priv = - container_of(work, struct mwl8k_priv, watchdog_ba_handle); - struct ieee80211_hw *hw = priv->hw; - int i; - u32 status = 0; - - mwl8k_fw_lock(hw); - - rc = mwl8k_cmd_get_watchdog_bitmap(priv->hw, &bitmap); - if (rc) - goto done; - - spin_lock(&priv->stream_lock); - - /* the bitmap is the hw queue number. Map it to the ampdu queue. */ - for (i = 0; i < TOTAL_HW_TX_QUEUES; i++) { - if (bitmap & (1 << i)) { - stream_index = (i + MWL8K_WMM_QUEUE_NUMBER) % - TOTAL_HW_TX_QUEUES; - streams = &priv->ampdu[stream_index]; - if (streams->state == AMPDU_STREAM_ACTIVE) { - ieee80211_stop_tx_ba_session(streams->sta, - streams->tid); - spin_unlock(&priv->stream_lock); - mwl8k_destroy_ba(hw, stream_index); - spin_lock(&priv->stream_lock); - } - } - } - - spin_unlock(&priv->stream_lock); -done: - atomic_dec(&priv->watchdog_event_pending); - status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); - iowrite32((status | MWL8K_A2H_INT_BA_WATCHDOG), - priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); - mwl8k_fw_unlock(hw); - return; -} - - -/* - * CMD_BSS_START. - */ -struct mwl8k_cmd_bss_start { - struct mwl8k_cmd_pkt header; - __le32 enable; -} __packed; - -static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, int enable) -{ - struct mwl8k_cmd_bss_start *cmd; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); - struct mwl8k_priv *priv = hw->priv; - int rc; - - if (enable && (priv->running_bsses & (1 << mwl8k_vif->macid))) - return 0; - - if (!enable && !(priv->running_bsses & (1 << mwl8k_vif->macid))) - return 0; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->enable = cpu_to_le32(enable); - - rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); - kfree(cmd); - - if (!rc) { - if (enable) - priv->running_bsses |= (1 << mwl8k_vif->macid); - else - priv->running_bsses &= ~(1 << mwl8k_vif->macid); - } - return rc; -} - -static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, u32 bitmap) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *mwl8k_vif, *tmp_vif; - struct ieee80211_vif *vif; - - list_for_each_entry_safe(mwl8k_vif, tmp_vif, &priv->vif_list, list) { - vif = mwl8k_vif->vif; - - if (!(bitmap & (1 << mwl8k_vif->macid))) - continue; - - if (vif->type == NL80211_IFTYPE_AP) - mwl8k_cmd_bss_start(hw, vif, enable); - } -} -/* - * CMD_BASTREAM. - */ - -/* - * UPSTREAM is tx direction - */ -#define BASTREAM_FLAG_DIRECTION_UPSTREAM 0x00 -#define BASTREAM_FLAG_IMMEDIATE_TYPE 0x01 - -enum ba_stream_action_type { - MWL8K_BA_CREATE, - MWL8K_BA_UPDATE, - MWL8K_BA_DESTROY, - MWL8K_BA_FLUSH, - MWL8K_BA_CHECK, -}; - - -struct mwl8k_create_ba_stream { - __le32 flags; - __le32 idle_thrs; - __le32 bar_thrs; - __le32 window_size; - u8 peer_mac_addr[6]; - u8 dialog_token; - u8 tid; - u8 queue_id; - u8 param_info; - __le32 ba_context; - u8 reset_seq_no_flag; - __le16 curr_seq_no; - u8 sta_src_mac_addr[6]; -} __packed; - -struct mwl8k_destroy_ba_stream { - __le32 flags; - __le32 ba_context; -} __packed; - -struct mwl8k_cmd_bastream { - struct mwl8k_cmd_pkt header; - __le32 action; - union { - struct mwl8k_create_ba_stream create_params; - struct mwl8k_destroy_ba_stream destroy_params; - }; -} __packed; - -static int -mwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, - struct ieee80211_vif *vif) -{ - struct mwl8k_cmd_bastream *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - cmd->action = cpu_to_le32(MWL8K_BA_CHECK); - - cmd->create_params.queue_id = stream->idx; - memcpy(&cmd->create_params.peer_mac_addr[0], stream->sta->addr, - ETH_ALEN); - cmd->create_params.tid = stream->tid; - - cmd->create_params.flags = - cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE) | - cpu_to_le32(BASTREAM_FLAG_DIRECTION_UPSTREAM); - - rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); - - kfree(cmd); - - return rc; -} - -static int -mwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, - u8 buf_size, struct ieee80211_vif *vif) -{ - struct mwl8k_cmd_bastream *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - - cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - cmd->action = cpu_to_le32(MWL8K_BA_CREATE); - - cmd->create_params.bar_thrs = cpu_to_le32((u32)buf_size); - cmd->create_params.window_size = cpu_to_le32((u32)buf_size); - cmd->create_params.queue_id = stream->idx; - - memcpy(cmd->create_params.peer_mac_addr, stream->sta->addr, ETH_ALEN); - cmd->create_params.tid = stream->tid; - cmd->create_params.curr_seq_no = cpu_to_le16(0); - cmd->create_params.reset_seq_no_flag = 1; - - cmd->create_params.param_info = - (stream->sta->ht_cap.ampdu_factor & - IEEE80211_HT_AMPDU_PARM_FACTOR) | - ((stream->sta->ht_cap.ampdu_density << 2) & - IEEE80211_HT_AMPDU_PARM_DENSITY); - - cmd->create_params.flags = - cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE | - BASTREAM_FLAG_DIRECTION_UPSTREAM); - - rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); - - wiphy_debug(hw->wiphy, "Created a BA stream for %pM : tid %d\n", - stream->sta->addr, stream->tid); - kfree(cmd); - - return rc; -} - -static void mwl8k_destroy_ba(struct ieee80211_hw *hw, - u8 idx) -{ - struct mwl8k_cmd_bastream *cmd; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32(MWL8K_BA_DESTROY); - - cmd->destroy_params.ba_context = cpu_to_le32(idx); - mwl8k_post_cmd(hw, &cmd->header); - - wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", idx); - - kfree(cmd); -} - -/* - * CMD_SET_NEW_STN. - */ -struct mwl8k_cmd_set_new_stn { - struct mwl8k_cmd_pkt header; - __le16 aid; - __u8 mac_addr[6]; - __le16 stn_id; - __le16 action; - __le16 rsvd; - __le32 legacy_rates; - __u8 ht_rates[4]; - __le16 cap_info; - __le16 ht_capabilities_info; - __u8 mac_ht_param_info; - __u8 rev; - __u8 control_channel; - __u8 add_channel; - __le16 op_mode; - __le16 stbc; - __u8 add_qos_info; - __u8 is_qos_sta; - __le32 fw_sta_ptr; -} __packed; - -#define MWL8K_STA_ACTION_ADD 0 -#define MWL8K_STA_ACTION_REMOVE 2 - -static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct mwl8k_cmd_set_new_stn *cmd; - u32 rates; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->aid = cpu_to_le16(sta->aid); - memcpy(cmd->mac_addr, sta->addr, ETH_ALEN); - cmd->stn_id = cpu_to_le16(sta->aid); - cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD); - if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) - rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; - else - rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; - cmd->legacy_rates = cpu_to_le32(rates); - if (sta->ht_cap.ht_supported) { - cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0]; - cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1]; - cmd->ht_rates[2] = sta->ht_cap.mcs.rx_mask[2]; - cmd->ht_rates[3] = sta->ht_cap.mcs.rx_mask[3]; - cmd->ht_capabilities_info = cpu_to_le16(sta->ht_cap.cap); - cmd->mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) | - ((sta->ht_cap.ampdu_density & 7) << 2); - cmd->is_qos_sta = 1; - } - - rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); - kfree(cmd); - - return rc; -} - -static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mwl8k_cmd_set_new_stn *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - memcpy(cmd->mac_addr, vif->addr, ETH_ALEN); - - rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); - kfree(cmd); - - return rc; -} - -static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u8 *addr) -{ - struct mwl8k_cmd_set_new_stn *cmd; - struct mwl8k_priv *priv = hw->priv; - int rc, i; - u8 idx; - - spin_lock(&priv->stream_lock); - /* Destroy any active ampdu streams for this sta */ - for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { - struct mwl8k_ampdu_stream *s; - s = &priv->ampdu[i]; - if (s->state != AMPDU_NO_STREAM) { - if (memcmp(s->sta->addr, addr, ETH_ALEN) == 0) { - if (s->state == AMPDU_STREAM_ACTIVE) { - idx = s->idx; - spin_unlock(&priv->stream_lock); - mwl8k_destroy_ba(hw, idx); - spin_lock(&priv->stream_lock); - } else if (s->state == AMPDU_STREAM_NEW) { - mwl8k_remove_stream(hw, s); - } - } - } - } - - spin_unlock(&priv->stream_lock); - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - memcpy(cmd->mac_addr, addr, ETH_ALEN); - cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE); - - rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); - kfree(cmd); - - return rc; -} - -/* - * CMD_UPDATE_ENCRYPTION. - */ - -#define MAX_ENCR_KEY_LENGTH 16 -#define MIC_KEY_LENGTH 8 - -struct mwl8k_cmd_update_encryption { - struct mwl8k_cmd_pkt header; - - __le32 action; - __le32 reserved; - __u8 mac_addr[6]; - __u8 encr_type; - -} __packed; - -struct mwl8k_cmd_set_key { - struct mwl8k_cmd_pkt header; - - __le32 action; - __le32 reserved; - __le16 length; - __le16 key_type_id; - __le32 key_info; - __le32 key_id; - __le16 key_len; - __u8 key_material[MAX_ENCR_KEY_LENGTH]; - __u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; - __u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; - __le16 tkip_rsc_low; - __le32 tkip_rsc_high; - __le16 tkip_tsc_low; - __le32 tkip_tsc_high; - __u8 mac_addr[6]; -} __packed; - -enum { - MWL8K_ENCR_ENABLE, - MWL8K_ENCR_SET_KEY, - MWL8K_ENCR_REMOVE_KEY, - MWL8K_ENCR_SET_GROUP_KEY, -}; - -#define MWL8K_UPDATE_ENCRYPTION_TYPE_WEP 0 -#define MWL8K_UPDATE_ENCRYPTION_TYPE_DISABLE 1 -#define MWL8K_UPDATE_ENCRYPTION_TYPE_TKIP 4 -#define MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED 7 -#define MWL8K_UPDATE_ENCRYPTION_TYPE_AES 8 - -enum { - MWL8K_ALG_WEP, - MWL8K_ALG_TKIP, - MWL8K_ALG_CCMP, -}; - -#define MWL8K_KEY_FLAG_TXGROUPKEY 0x00000004 -#define MWL8K_KEY_FLAG_PAIRWISE 0x00000008 -#define MWL8K_KEY_FLAG_TSC_VALID 0x00000040 -#define MWL8K_KEY_FLAG_WEP_TXKEY 0x01000000 -#define MWL8K_KEY_FLAG_MICKEY_VALID 0x02000000 - -static int mwl8k_cmd_update_encryption_enable(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u8 *addr, - u8 encr_type) -{ - struct mwl8k_cmd_update_encryption *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32(MWL8K_ENCR_ENABLE); - memcpy(cmd->mac_addr, addr, ETH_ALEN); - cmd->encr_type = encr_type; - - rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); - kfree(cmd); - - return rc; -} - -static int mwl8k_encryption_set_cmd_info(struct mwl8k_cmd_set_key *cmd, - u8 *addr, - struct ieee80211_key_conf *key) -{ - cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->length = cpu_to_le16(sizeof(*cmd) - - offsetof(struct mwl8k_cmd_set_key, length)); - cmd->key_id = cpu_to_le32(key->keyidx); - cmd->key_len = cpu_to_le16(key->keylen); - memcpy(cmd->mac_addr, addr, ETH_ALEN); - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - cmd->key_type_id = cpu_to_le16(MWL8K_ALG_WEP); - if (key->keyidx == 0) - cmd->key_info = cpu_to_le32(MWL8K_KEY_FLAG_WEP_TXKEY); - - break; - case WLAN_CIPHER_SUITE_TKIP: - cmd->key_type_id = cpu_to_le16(MWL8K_ALG_TKIP); - cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) - ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) - : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); - cmd->key_info |= cpu_to_le32(MWL8K_KEY_FLAG_MICKEY_VALID - | MWL8K_KEY_FLAG_TSC_VALID); - break; - case WLAN_CIPHER_SUITE_CCMP: - cmd->key_type_id = cpu_to_le16(MWL8K_ALG_CCMP); - cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) - ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) - : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); - break; - default: - return -ENOTSUPP; - } - - return 0; -} - -static int mwl8k_cmd_encryption_set_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u8 *addr, - struct ieee80211_key_conf *key) -{ - struct mwl8k_cmd_set_key *cmd; - int rc; - int keymlen; - u32 action; - u8 idx; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); - if (rc < 0) - goto done; - - idx = key->keyidx; - - if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) - action = MWL8K_ENCR_SET_KEY; - else - action = MWL8K_ENCR_SET_GROUP_KEY; - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - if (!mwl8k_vif->wep_key_conf[idx].enabled) { - memcpy(mwl8k_vif->wep_key_conf[idx].key, key, - sizeof(*key) + key->keylen); - mwl8k_vif->wep_key_conf[idx].enabled = 1; - } - - keymlen = key->keylen; - action = MWL8K_ENCR_SET_KEY; - break; - case WLAN_CIPHER_SUITE_TKIP: - keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH; - break; - case WLAN_CIPHER_SUITE_CCMP: - keymlen = key->keylen; - break; - default: - rc = -ENOTSUPP; - goto done; - } - - memcpy(cmd->key_material, key->key, keymlen); - cmd->action = cpu_to_le32(action); - - rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); -done: - kfree(cmd); - - return rc; -} - -static int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u8 *addr, - struct ieee80211_key_conf *key) -{ - struct mwl8k_cmd_set_key *cmd; - int rc; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); - if (rc < 0) - goto done; - - if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || - key->cipher == WLAN_CIPHER_SUITE_WEP104) - mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0; - - cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY); - - rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); -done: - kfree(cmd); - - return rc; -} - -static int mwl8k_set_key(struct ieee80211_hw *hw, - enum set_key_cmd cmd_param, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - int rc = 0; - u8 encr_type; - u8 *addr; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); - struct mwl8k_priv *priv = hw->priv; - - if (vif->type == NL80211_IFTYPE_STATION && !priv->ap_fw) - return -EOPNOTSUPP; - - if (sta == NULL) - addr = vif->addr; - else - addr = sta->addr; - - if (cmd_param == SET_KEY) { - rc = mwl8k_cmd_encryption_set_key(hw, vif, addr, key); - if (rc) - goto out; - - if ((key->cipher == WLAN_CIPHER_SUITE_WEP40) - || (key->cipher == WLAN_CIPHER_SUITE_WEP104)) - encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_WEP; - else - encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED; - - rc = mwl8k_cmd_update_encryption_enable(hw, vif, addr, - encr_type); - if (rc) - goto out; - - mwl8k_vif->is_hw_crypto_enabled = true; - - } else { - rc = mwl8k_cmd_encryption_remove_key(hw, vif, addr, key); - - if (rc) - goto out; - } -out: - return rc; -} - -/* - * CMD_UPDATE_STADB. - */ -struct ewc_ht_info { - __le16 control1; - __le16 control2; - __le16 control3; -} __packed; - -struct peer_capability_info { - /* Peer type - AP vs. STA. */ - __u8 peer_type; - - /* Basic 802.11 capabilities from assoc resp. */ - __le16 basic_caps; - - /* Set if peer supports 802.11n high throughput (HT). */ - __u8 ht_support; - - /* Valid if HT is supported. */ - __le16 ht_caps; - __u8 extended_ht_caps; - struct ewc_ht_info ewc_info; - - /* Legacy rate table. Intersection of our rates and peer rates. */ - __u8 legacy_rates[12]; - - /* HT rate table. Intersection of our rates and peer rates. */ - __u8 ht_rates[16]; - __u8 pad[16]; - - /* If set, interoperability mode, no proprietary extensions. */ - __u8 interop; - __u8 pad2; - __u8 station_id; - __le16 amsdu_enabled; -} __packed; - -struct mwl8k_cmd_update_stadb { - struct mwl8k_cmd_pkt header; - - /* See STADB_ACTION_TYPE */ - __le32 action; - - /* Peer MAC address */ - __u8 peer_addr[ETH_ALEN]; - - __le32 reserved; - - /* Peer info - valid during add/update. */ - struct peer_capability_info peer_info; -} __packed; - -#define MWL8K_STA_DB_MODIFY_ENTRY 1 -#define MWL8K_STA_DB_DEL_ENTRY 2 - -/* Peer Entry flags - used to define the type of the peer node */ -#define MWL8K_PEER_TYPE_ACCESSPOINT 2 - -static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct mwl8k_cmd_update_stadb *cmd; - struct peer_capability_info *p; - u32 rates; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY); - memcpy(cmd->peer_addr, sta->addr, ETH_ALEN); - - p = &cmd->peer_info; - p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; - p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability); - p->ht_support = sta->ht_cap.ht_supported; - p->ht_caps = cpu_to_le16(sta->ht_cap.cap); - p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) | - ((sta->ht_cap.ampdu_density & 7) << 2); - if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) - rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; - else - rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; - legacy_rate_mask_to_array(p->legacy_rates, rates); - memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16); - p->interop = 1; - p->amsdu_enabled = 0; - - rc = mwl8k_post_cmd(hw, &cmd->header); - if (!rc) - rc = p->station_id; - kfree(cmd); - - return rc; -} - -static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u8 *addr) -{ - struct mwl8k_cmd_update_stadb *cmd; - int rc; - - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (cmd == NULL) - return -ENOMEM; - - cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); - cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY); - memcpy(cmd->peer_addr, addr, ETH_ALEN); - - rc = mwl8k_post_cmd(hw, &cmd->header); - kfree(cmd); - - return rc; -} - - -/* - * Interrupt handling. - */ -static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) -{ - struct ieee80211_hw *hw = dev_id; - struct mwl8k_priv *priv = hw->priv; - u32 status; - - status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - if (!status) - return IRQ_NONE; - - if (status & MWL8K_A2H_INT_TX_DONE) { - status &= ~MWL8K_A2H_INT_TX_DONE; - tasklet_schedule(&priv->poll_tx_task); - } - - if (status & MWL8K_A2H_INT_RX_READY) { - status &= ~MWL8K_A2H_INT_RX_READY; - tasklet_schedule(&priv->poll_rx_task); - } - - if (status & MWL8K_A2H_INT_BA_WATCHDOG) { - iowrite32(~MWL8K_A2H_INT_BA_WATCHDOG, - priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); - - atomic_inc(&priv->watchdog_event_pending); - status &= ~MWL8K_A2H_INT_BA_WATCHDOG; - ieee80211_queue_work(hw, &priv->watchdog_ba_handle); - } - - if (status) - iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - - if (status & MWL8K_A2H_INT_OPC_DONE) { - if (priv->hostcmd_wait != NULL) - complete(priv->hostcmd_wait); - } - - if (status & MWL8K_A2H_INT_QUEUE_EMPTY) { - if (!mutex_is_locked(&priv->fw_mutex) && - priv->radio_on && priv->pending_tx_pkts) - mwl8k_tx_start(priv); - } - - return IRQ_HANDLED; -} - -static void mwl8k_tx_poll(unsigned long data) -{ - struct ieee80211_hw *hw = (struct ieee80211_hw *)data; - struct mwl8k_priv *priv = hw->priv; - int limit; - int i; - - limit = 32; - - spin_lock_bh(&priv->tx_lock); - - for (i = 0; i < mwl8k_tx_queues(priv); i++) - limit -= mwl8k_txq_reclaim(hw, i, limit, 0); - - if (!priv->pending_tx_pkts && priv->tx_wait != NULL) { - complete(priv->tx_wait); - priv->tx_wait = NULL; - } - - spin_unlock_bh(&priv->tx_lock); - - if (limit) { - writel(~MWL8K_A2H_INT_TX_DONE, - priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - } else { - tasklet_schedule(&priv->poll_tx_task); - } -} - -static void mwl8k_rx_poll(unsigned long data) -{ - struct ieee80211_hw *hw = (struct ieee80211_hw *)data; - struct mwl8k_priv *priv = hw->priv; - int limit; - - limit = 32; - limit -= rxq_process(hw, 0, limit); - limit -= rxq_refill(hw, 0, limit); - - if (limit) { - writel(~MWL8K_A2H_INT_RX_READY, - priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - } else { - tasklet_schedule(&priv->poll_rx_task); - } -} - - -/* - * Core driver operations. - */ -static void mwl8k_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct mwl8k_priv *priv = hw->priv; - int index = skb_get_queue_mapping(skb); - - if (!priv->radio_on) { - wiphy_debug(hw->wiphy, - "dropped TX frame since radio disabled\n"); - dev_kfree_skb(skb); - return; - } - - mwl8k_txq_xmit(hw, index, control->sta, skb); -} - -static int mwl8k_start(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - int rc; - - rc = request_irq(priv->pdev->irq, mwl8k_interrupt, - IRQF_SHARED, MWL8K_NAME, hw); - if (rc) { - priv->irq = -1; - wiphy_err(hw->wiphy, "failed to register IRQ handler\n"); - return -EIO; - } - priv->irq = priv->pdev->irq; - - /* Enable TX reclaim and RX tasklets. */ - tasklet_enable(&priv->poll_tx_task); - tasklet_enable(&priv->poll_rx_task); - - /* Enable interrupts */ - iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - iowrite32(MWL8K_A2H_EVENTS, - priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); - - rc = mwl8k_fw_lock(hw); - if (!rc) { - rc = mwl8k_cmd_radio_enable(hw); - - if (!priv->ap_fw) { - if (!rc) - rc = mwl8k_cmd_enable_sniffer(hw, 0); - - if (!rc) - rc = mwl8k_cmd_set_pre_scan(hw); - - if (!rc) - rc = mwl8k_cmd_set_post_scan(hw, - "\x00\x00\x00\x00\x00\x00"); - } - - if (!rc) - rc = mwl8k_cmd_set_rateadapt_mode(hw, 0); - - if (!rc) - rc = mwl8k_cmd_set_wmm_mode(hw, 0); - - mwl8k_fw_unlock(hw); - } - - if (rc) { - iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - free_irq(priv->pdev->irq, hw); - priv->irq = -1; - tasklet_disable(&priv->poll_tx_task); - tasklet_disable(&priv->poll_rx_task); - } else { - ieee80211_wake_queues(hw); - } - - return rc; -} - -static void mwl8k_stop(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - int i; - - if (!priv->hw_restart_in_progress) - mwl8k_cmd_radio_disable(hw); - - ieee80211_stop_queues(hw); - - /* Disable interrupts */ - iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - if (priv->irq != -1) { - free_irq(priv->pdev->irq, hw); - priv->irq = -1; - } - - /* Stop finalize join worker */ - cancel_work_sync(&priv->finalize_join_worker); - cancel_work_sync(&priv->watchdog_ba_handle); - if (priv->beacon_skb != NULL) - dev_kfree_skb(priv->beacon_skb); - - /* Stop TX reclaim and RX tasklets. */ - tasklet_disable(&priv->poll_tx_task); - tasklet_disable(&priv->poll_rx_task); - - /* Return all skbs to mac80211 */ - for (i = 0; i < mwl8k_tx_queues(priv); i++) - mwl8k_txq_reclaim(hw, i, INT_MAX, 1); -} - -static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image); - -static int mwl8k_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *mwl8k_vif; - u32 macids_supported; - int macid, rc; - struct mwl8k_device_info *di; - - /* - * Reject interface creation if sniffer mode is active, as - * STA operation is mutually exclusive with hardware sniffer - * mode. (Sniffer mode is only used on STA firmware.) - */ - if (priv->sniffer_enabled) { - wiphy_info(hw->wiphy, - "unable to create STA interface because sniffer mode is enabled\n"); - return -EINVAL; - } - - di = priv->device_info; - switch (vif->type) { - case NL80211_IFTYPE_AP: - if (!priv->ap_fw && di->fw_image_ap) { - /* we must load the ap fw to meet this request */ - if (!list_empty(&priv->vif_list)) - return -EBUSY; - rc = mwl8k_reload_firmware(hw, di->fw_image_ap); - if (rc) - return rc; - } - macids_supported = priv->ap_macids_supported; - break; - case NL80211_IFTYPE_STATION: - if (priv->ap_fw && di->fw_image_sta) { - if (!list_empty(&priv->vif_list)) { - wiphy_warn(hw->wiphy, "AP interface is running.\n" - "Adding STA interface for WDS"); - } else { - /* we must load the sta fw to - * meet this request. - */ - rc = mwl8k_reload_firmware(hw, - di->fw_image_sta); - if (rc) - return rc; - } - } - macids_supported = priv->sta_macids_supported; - break; - default: - return -EINVAL; - } - - macid = ffs(macids_supported & ~priv->macids_used); - if (!macid--) - return -EBUSY; - - /* Setup driver private area. */ - mwl8k_vif = MWL8K_VIF(vif); - memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); - mwl8k_vif->vif = vif; - mwl8k_vif->macid = macid; - mwl8k_vif->seqno = 0; - memcpy(mwl8k_vif->bssid, vif->addr, ETH_ALEN); - mwl8k_vif->is_hw_crypto_enabled = false; - - /* Set the mac address. */ - mwl8k_cmd_set_mac_addr(hw, vif, vif->addr); - - if (vif->type == NL80211_IFTYPE_AP) - mwl8k_cmd_set_new_stn_add_self(hw, vif); - - priv->macids_used |= 1 << mwl8k_vif->macid; - list_add_tail(&mwl8k_vif->list, &priv->vif_list); - - return 0; -} - -static void mwl8k_remove_vif(struct mwl8k_priv *priv, struct mwl8k_vif *vif) -{ - /* Has ieee80211_restart_hw re-added the removed interfaces? */ - if (!priv->macids_used) - return; - - priv->macids_used &= ~(1 << vif->macid); - list_del(&vif->list); -} - -static void mwl8k_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); - - if (vif->type == NL80211_IFTYPE_AP) - mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); - - mwl8k_cmd_del_mac_addr(hw, vif, vif->addr); - - mwl8k_remove_vif(priv, mwl8k_vif); -} - -static void mwl8k_hw_restart_work(struct work_struct *work) -{ - struct mwl8k_priv *priv = - container_of(work, struct mwl8k_priv, fw_reload); - struct ieee80211_hw *hw = priv->hw; - struct mwl8k_device_info *di; - int rc; - - /* If some command is waiting for a response, clear it */ - if (priv->hostcmd_wait != NULL) { - complete(priv->hostcmd_wait); - priv->hostcmd_wait = NULL; - } - - priv->hw_restart_owner = current; - di = priv->device_info; - mwl8k_fw_lock(hw); - - if (priv->ap_fw) - rc = mwl8k_reload_firmware(hw, di->fw_image_ap); - else - rc = mwl8k_reload_firmware(hw, di->fw_image_sta); - - if (rc) - goto fail; - - priv->hw_restart_owner = NULL; - priv->hw_restart_in_progress = false; - - /* - * This unlock will wake up the queues and - * also opens the command path for other - * commands - */ - mwl8k_fw_unlock(hw); - - ieee80211_restart_hw(hw); - - wiphy_err(hw->wiphy, "Firmware restarted successfully\n"); - - return; -fail: - mwl8k_fw_unlock(hw); - - wiphy_err(hw->wiphy, "Firmware restart failed\n"); -} - -static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) -{ - struct ieee80211_conf *conf = &hw->conf; - struct mwl8k_priv *priv = hw->priv; - int rc; - - rc = mwl8k_fw_lock(hw); - if (rc) - return rc; - - if (conf->flags & IEEE80211_CONF_IDLE) - rc = mwl8k_cmd_radio_disable(hw); - else - rc = mwl8k_cmd_radio_enable(hw); - if (rc) - goto out; - - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - rc = mwl8k_cmd_set_rf_channel(hw, conf); - if (rc) - goto out; - } - - if (conf->power_level > 18) - conf->power_level = 18; - - if (priv->ap_fw) { - - if (conf->flags & IEEE80211_CONF_CHANGE_POWER) { - rc = mwl8k_cmd_tx_power(hw, conf, conf->power_level); - if (rc) - goto out; - } - - - } else { - rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level); - if (rc) - goto out; - rc = mwl8k_cmd_mimo_config(hw, 0x7, 0x7); - } - -out: - mwl8k_fw_unlock(hw); - - return rc; -} - -static void -mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, u32 changed) -{ - struct mwl8k_priv *priv = hw->priv; - u32 ap_legacy_rates = 0; - u8 ap_mcs_rates[16]; - int rc; - - if (mwl8k_fw_lock(hw)) - return; - - /* - * No need to capture a beacon if we're no longer associated. - */ - if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc) - priv->capture_beacon = false; - - /* - * Get the AP's legacy and MCS rates. - */ - if (vif->bss_conf.assoc) { - struct ieee80211_sta *ap; - - rcu_read_lock(); - - ap = ieee80211_find_sta(vif, vif->bss_conf.bssid); - if (ap == NULL) { - rcu_read_unlock(); - goto out; - } - - if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) { - ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ]; - } else { - ap_legacy_rates = - ap->supp_rates[IEEE80211_BAND_5GHZ] << 5; - } - memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16); - - rcu_read_unlock(); - - if (changed & BSS_CHANGED_ASSOC) { - if (!priv->ap_fw) { - rc = mwl8k_cmd_set_rate(hw, vif, - ap_legacy_rates, - ap_mcs_rates); - if (rc) - goto out; - - rc = mwl8k_cmd_use_fixed_rate_sta(hw); - if (rc) - goto out; - } else { - int idx; - int rate; - - /* Use AP firmware specific rate command. - */ - idx = ffs(vif->bss_conf.basic_rates); - if (idx) - idx--; - - if (hw->conf.chandef.chan->band == - IEEE80211_BAND_2GHZ) - rate = mwl8k_rates_24[idx].hw_value; - else - rate = mwl8k_rates_50[idx].hw_value; - - mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); - } - } - } - - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - rc = mwl8k_set_radio_preamble(hw, - vif->bss_conf.use_short_preamble); - if (rc) - goto out; - } - - if ((changed & BSS_CHANGED_ERP_SLOT) && !priv->ap_fw) { - rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot); - if (rc) - goto out; - } - - if (vif->bss_conf.assoc && !priv->ap_fw && - (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT | - BSS_CHANGED_HT))) { - rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates); - if (rc) - goto out; - } - - if (vif->bss_conf.assoc && - (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) { - /* - * Finalize the join. Tell rx handler to process - * next beacon from our BSSID. - */ - memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN); - priv->capture_beacon = true; - } - -out: - mwl8k_fw_unlock(hw); -} - -static void -mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, u32 changed) -{ - int rc; - - if (mwl8k_fw_lock(hw)) - return; - - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - rc = mwl8k_set_radio_preamble(hw, - vif->bss_conf.use_short_preamble); - if (rc) - goto out; - } - - if (changed & BSS_CHANGED_BASIC_RATES) { - int idx; - int rate; - - /* - * Use lowest supported basic rate for multicasts - * and management frames (such as probe responses -- - * beacons will always go out at 1 Mb/s). - */ - idx = ffs(vif->bss_conf.basic_rates); - if (idx) - idx--; - - if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) - rate = mwl8k_rates_24[idx].hw_value; - else - rate = mwl8k_rates_50[idx].hw_value; - - mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); - } - - if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) { - struct sk_buff *skb; - - skb = ieee80211_beacon_get(hw, vif); - if (skb != NULL) { - mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len); - kfree_skb(skb); - } - } - - if (changed & BSS_CHANGED_BEACON_ENABLED) - mwl8k_cmd_bss_start(hw, vif, info->enable_beacon); - -out: - mwl8k_fw_unlock(hw); -} - -static void -mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, u32 changed) -{ - if (vif->type == NL80211_IFTYPE_STATION) - mwl8k_bss_info_changed_sta(hw, vif, info, changed); - if (vif->type == NL80211_IFTYPE_AP) - mwl8k_bss_info_changed_ap(hw, vif, info, changed); -} - -static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, - struct netdev_hw_addr_list *mc_list) -{ - struct mwl8k_cmd_pkt *cmd; - - /* - * Synthesize and return a command packet that programs the - * hardware multicast address filter. At this point we don't - * know whether FIF_ALLMULTI is being requested, but if it is, - * we'll end up throwing this packet away and creating a new - * one in mwl8k_configure_filter(). - */ - cmd = __mwl8k_cmd_mac_multicast_adr(hw, 0, mc_list); - - return (unsigned long)cmd; -} - -static int -mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags) -{ - struct mwl8k_priv *priv = hw->priv; - - /* - * Hardware sniffer mode is mutually exclusive with STA - * operation, so refuse to enable sniffer mode if a STA - * interface is active. - */ - if (!list_empty(&priv->vif_list)) { - if (net_ratelimit()) - wiphy_info(hw->wiphy, - "not enabling sniffer mode because STA interface is active\n"); - return 0; - } - - if (!priv->sniffer_enabled) { - if (mwl8k_cmd_enable_sniffer(hw, 1)) - return 0; - priv->sniffer_enabled = true; - } - - *total_flags &= FIF_ALLMULTI | - FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL | - FIF_OTHER_BSS; - - return 1; -} - -static struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv) -{ - if (!list_empty(&priv->vif_list)) - return list_entry(priv->vif_list.next, struct mwl8k_vif, list); - - return NULL; -} - -static void mwl8k_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_cmd_pkt *cmd = (void *)(unsigned long)multicast; - - /* - * AP firmware doesn't allow fine-grained control over - * the receive filter. - */ - if (priv->ap_fw) { - *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; - kfree(cmd); - return; - } - - /* - * Enable hardware sniffer mode if FIF_CONTROL or - * FIF_OTHER_BSS is requested. - */ - if (*total_flags & (FIF_CONTROL | FIF_OTHER_BSS) && - mwl8k_configure_filter_sniffer(hw, changed_flags, total_flags)) { - kfree(cmd); - return; - } - - /* Clear unsupported feature flags */ - *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; - - if (mwl8k_fw_lock(hw)) { - kfree(cmd); - return; - } - - if (priv->sniffer_enabled) { - mwl8k_cmd_enable_sniffer(hw, 0); - priv->sniffer_enabled = false; - } - - if (changed_flags & FIF_BCN_PRBRESP_PROMISC) { - if (*total_flags & FIF_BCN_PRBRESP_PROMISC) { - /* - * Disable the BSS filter. - */ - mwl8k_cmd_set_pre_scan(hw); - } else { - struct mwl8k_vif *mwl8k_vif; - const u8 *bssid; - - /* - * Enable the BSS filter. - * - * If there is an active STA interface, use that - * interface's BSSID, otherwise use a dummy one - * (where the OUI part needs to be nonzero for - * the BSSID to be accepted by POST_SCAN). - */ - mwl8k_vif = mwl8k_first_vif(priv); - if (mwl8k_vif != NULL) - bssid = mwl8k_vif->vif->bss_conf.bssid; - else - bssid = "\x01\x00\x00\x00\x00\x00"; - - mwl8k_cmd_set_post_scan(hw, bssid); - } - } - - /* - * If FIF_ALLMULTI is being requested, throw away the command - * packet that ->prepare_multicast() built and replace it with - * a command packet that enables reception of all multicast - * packets. - */ - if (*total_flags & FIF_ALLMULTI) { - kfree(cmd); - cmd = __mwl8k_cmd_mac_multicast_adr(hw, 1, NULL); - } - - if (cmd != NULL) { - mwl8k_post_cmd(hw, cmd); - kfree(cmd); - } - - mwl8k_fw_unlock(hw); -} - -static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) -{ - return mwl8k_cmd_set_rts_threshold(hw, value); -} - -static int mwl8k_sta_remove(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct mwl8k_priv *priv = hw->priv; - - if (priv->ap_fw) - return mwl8k_cmd_set_new_stn_del(hw, vif, sta->addr); - else - return mwl8k_cmd_update_stadb_del(hw, vif, sta->addr); -} - -static int mwl8k_sta_add(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct mwl8k_priv *priv = hw->priv; - int ret; - int i; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); - struct ieee80211_key_conf *key; - - if (!priv->ap_fw) { - ret = mwl8k_cmd_update_stadb_add(hw, vif, sta); - if (ret >= 0) { - MWL8K_STA(sta)->peer_id = ret; - if (sta->ht_cap.ht_supported) - MWL8K_STA(sta)->is_ampdu_allowed = true; - ret = 0; - } - - } else { - ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta); - } - - for (i = 0; i < NUM_WEP_KEYS; i++) { - key = IEEE80211_KEY_CONF(mwl8k_vif->wep_key_conf[i].key); - if (mwl8k_vif->wep_key_conf[i].enabled) - mwl8k_set_key(hw, SET_KEY, vif, sta, key); - } - return ret; -} - -static int mwl8k_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct mwl8k_priv *priv = hw->priv; - int rc; - - rc = mwl8k_fw_lock(hw); - if (!rc) { - BUG_ON(queue > MWL8K_TX_WMM_QUEUES - 1); - memcpy(&priv->wmm_params[queue], params, sizeof(*params)); - - if (!priv->wmm_enabled) - rc = mwl8k_cmd_set_wmm_mode(hw, 1); - - if (!rc) { - int q = MWL8K_TX_WMM_QUEUES - 1 - queue; - rc = mwl8k_cmd_set_edca_params(hw, q, - params->cw_min, - params->cw_max, - params->aifs, - params->txop); - } - - mwl8k_fw_unlock(hw); - } - - return rc; -} - -static int mwl8k_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) -{ - return mwl8k_cmd_get_stat(hw, stats); -} - -static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx, - struct survey_info *survey) -{ - struct mwl8k_priv *priv = hw->priv; - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_supported_band *sband; - - if (priv->ap_fw) { - sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ]; - - if (sband && idx >= sband->n_channels) { - idx -= sband->n_channels; - sband = NULL; - } - - if (!sband) - sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ]; - - if (!sband || idx >= sband->n_channels) - return -ENOENT; - - memcpy(survey, &priv->survey[idx], sizeof(*survey)); - survey->channel = &sband->channels[idx]; - - return 0; - } - - if (idx != 0) - return -ENOENT; - - survey->channel = conf->chandef.chan; - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = priv->noise; - - return 0; -} - -#define MAX_AMPDU_ATTEMPTS 5 - -static int -mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) -{ - - int i, rc = 0; - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_ampdu_stream *stream; - u8 *addr = sta->addr, idx; - struct mwl8k_sta *sta_info = MWL8K_STA(sta); - - if (!ieee80211_hw_check(hw, AMPDU_AGGREGATION)) - return -ENOTSUPP; - - spin_lock(&priv->stream_lock); - stream = mwl8k_lookup_stream(hw, addr, tid); - - switch (action) { - case IEEE80211_AMPDU_RX_START: - case IEEE80211_AMPDU_RX_STOP: - break; - case IEEE80211_AMPDU_TX_START: - /* By the time we get here the hw queues may contain outgoing - * packets for this RA/TID that are not part of this BA - * session. The hw will assign sequence numbers to these - * packets as they go out. So if we query the hw for its next - * sequence number and use that for the SSN here, it may end up - * being wrong, which will lead to sequence number mismatch at - * the recipient. To avoid this, we reset the sequence number - * to O for the first MPDU in this BA stream. - */ - *ssn = 0; - if (stream == NULL) { - /* This means that somebody outside this driver called - * ieee80211_start_tx_ba_session. This is unexpected - * because we do our own rate control. Just warn and - * move on. - */ - wiphy_warn(hw->wiphy, "Unexpected call to %s. " - "Proceeding anyway.\n", __func__); - stream = mwl8k_add_stream(hw, sta, tid); - } - if (stream == NULL) { - wiphy_debug(hw->wiphy, "no free AMPDU streams\n"); - rc = -EBUSY; - break; - } - stream->state = AMPDU_STREAM_IN_PROGRESS; - - /* Release the lock before we do the time consuming stuff */ - spin_unlock(&priv->stream_lock); - for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) { - - /* Check if link is still valid */ - if (!sta_info->is_ampdu_allowed) { - spin_lock(&priv->stream_lock); - mwl8k_remove_stream(hw, stream); - spin_unlock(&priv->stream_lock); - return -EBUSY; - } - - rc = mwl8k_check_ba(hw, stream, vif); - - /* If HW restart is in progress mwl8k_post_cmd will - * return -EBUSY. Avoid retrying mwl8k_check_ba in - * such cases - */ - if (!rc || rc == -EBUSY) - break; - /* - * HW queues take time to be flushed, give them - * sufficient time - */ - - msleep(1000); - } - spin_lock(&priv->stream_lock); - if (rc) { - wiphy_err(hw->wiphy, "Stream for tid %d busy after %d" - " attempts\n", tid, MAX_AMPDU_ATTEMPTS); - mwl8k_remove_stream(hw, stream); - rc = -EBUSY; - break; - } - ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); - break; - case IEEE80211_AMPDU_TX_STOP_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - if (stream) { - if (stream->state == AMPDU_STREAM_ACTIVE) { - idx = stream->idx; - spin_unlock(&priv->stream_lock); - mwl8k_destroy_ba(hw, idx); - spin_lock(&priv->stream_lock); - } - mwl8k_remove_stream(hw, stream); - } - ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - BUG_ON(stream == NULL); - BUG_ON(stream->state != AMPDU_STREAM_IN_PROGRESS); - spin_unlock(&priv->stream_lock); - rc = mwl8k_create_ba(hw, stream, buf_size, vif); - spin_lock(&priv->stream_lock); - if (!rc) - stream->state = AMPDU_STREAM_ACTIVE; - else { - idx = stream->idx; - spin_unlock(&priv->stream_lock); - mwl8k_destroy_ba(hw, idx); - spin_lock(&priv->stream_lock); - wiphy_debug(hw->wiphy, - "Failed adding stream for sta %pM tid %d\n", - addr, tid); - mwl8k_remove_stream(hw, stream); - } - break; - - default: - rc = -ENOTSUPP; - } - - spin_unlock(&priv->stream_lock); - return rc; -} - -static void mwl8k_sw_scan_start(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const u8 *mac_addr) -{ - struct mwl8k_priv *priv = hw->priv; - u8 tmp; - - if (!priv->ap_fw) - return; - - /* clear all stats */ - priv->channel_time = 0; - ioread32(priv->regs + BBU_RXRDY_CNT_REG); - ioread32(priv->regs + NOK_CCA_CNT_REG); - mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp); - - priv->sw_scan_start = true; -} - -static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct mwl8k_priv *priv = hw->priv; - u8 tmp; - - if (!priv->ap_fw) - return; - - priv->sw_scan_start = false; - - /* clear all stats */ - priv->channel_time = 0; - ioread32(priv->regs + BBU_RXRDY_CNT_REG); - ioread32(priv->regs + NOK_CCA_CNT_REG); - mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp); -} - -static const struct ieee80211_ops mwl8k_ops = { - .tx = mwl8k_tx, - .start = mwl8k_start, - .stop = mwl8k_stop, - .add_interface = mwl8k_add_interface, - .remove_interface = mwl8k_remove_interface, - .config = mwl8k_config, - .bss_info_changed = mwl8k_bss_info_changed, - .prepare_multicast = mwl8k_prepare_multicast, - .configure_filter = mwl8k_configure_filter, - .set_key = mwl8k_set_key, - .set_rts_threshold = mwl8k_set_rts_threshold, - .sta_add = mwl8k_sta_add, - .sta_remove = mwl8k_sta_remove, - .conf_tx = mwl8k_conf_tx, - .get_stats = mwl8k_get_stats, - .get_survey = mwl8k_get_survey, - .ampdu_action = mwl8k_ampdu_action, - .sw_scan_start = mwl8k_sw_scan_start, - .sw_scan_complete = mwl8k_sw_scan_complete, -}; - -static void mwl8k_finalize_join_worker(struct work_struct *work) -{ - struct mwl8k_priv *priv = - container_of(work, struct mwl8k_priv, finalize_join_worker); - struct sk_buff *skb = priv->beacon_skb; - struct ieee80211_mgmt *mgmt = (void *)skb->data; - int len = skb->len - offsetof(struct ieee80211_mgmt, u.beacon.variable); - const u8 *tim = cfg80211_find_ie(WLAN_EID_TIM, - mgmt->u.beacon.variable, len); - int dtim_period = 1; - - if (tim && tim[1] >= 2) - dtim_period = tim[3]; - - mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, dtim_period); - - dev_kfree_skb(skb); - priv->beacon_skb = NULL; -} - -enum { - MWL8363 = 0, - MWL8687, - MWL8366, - MWL8764, -}; - -#define MWL8K_8366_AP_FW_API 3 -#define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw" -#define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api) - -#define MWL8K_8764_AP_FW_API 1 -#define _MWL8K_8764_AP_FW(api) "mwl8k/fmimage_8764_ap-" #api ".fw" -#define MWL8K_8764_AP_FW(api) _MWL8K_8764_AP_FW(api) - -static struct mwl8k_device_info mwl8k_info_tbl[] = { - [MWL8363] = { - .part_name = "88w8363", - .helper_image = "mwl8k/helper_8363.fw", - .fw_image_sta = "mwl8k/fmimage_8363.fw", - }, - [MWL8687] = { - .part_name = "88w8687", - .helper_image = "mwl8k/helper_8687.fw", - .fw_image_sta = "mwl8k/fmimage_8687.fw", - }, - [MWL8366] = { - .part_name = "88w8366", - .helper_image = "mwl8k/helper_8366.fw", - .fw_image_sta = "mwl8k/fmimage_8366.fw", - .fw_image_ap = MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API), - .fw_api_ap = MWL8K_8366_AP_FW_API, - .ap_rxd_ops = &rxd_ap_ops, - }, - [MWL8764] = { - .part_name = "88w8764", - .fw_image_ap = MWL8K_8764_AP_FW(MWL8K_8764_AP_FW_API), - .fw_api_ap = MWL8K_8764_AP_FW_API, - .ap_rxd_ops = &rxd_ap_ops, - }, -}; - -MODULE_FIRMWARE("mwl8k/helper_8363.fw"); -MODULE_FIRMWARE("mwl8k/fmimage_8363.fw"); -MODULE_FIRMWARE("mwl8k/helper_8687.fw"); -MODULE_FIRMWARE("mwl8k/fmimage_8687.fw"); -MODULE_FIRMWARE("mwl8k/helper_8366.fw"); -MODULE_FIRMWARE("mwl8k/fmimage_8366.fw"); -MODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API)); - -static const struct pci_device_id mwl8k_pci_id_table[] = { - { PCI_VDEVICE(MARVELL, 0x2a0a), .driver_data = MWL8363, }, - { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, }, - { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, }, - { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, }, - { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, }, - { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, }, - { PCI_VDEVICE(MARVELL, 0x2a41), .driver_data = MWL8366, }, - { PCI_VDEVICE(MARVELL, 0x2a42), .driver_data = MWL8366, }, - { PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, }, - { PCI_VDEVICE(MARVELL, 0x2b36), .driver_data = MWL8764, }, - { }, -}; -MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); - -static int mwl8k_request_alt_fw(struct mwl8k_priv *priv) -{ - int rc; - printk(KERN_ERR "%s: Error requesting preferred fw %s.\n" - "Trying alternative firmware %s\n", pci_name(priv->pdev), - priv->fw_pref, priv->fw_alt); - rc = mwl8k_request_fw(priv, priv->fw_alt, &priv->fw_ucode, true); - if (rc) { - printk(KERN_ERR "%s: Error requesting alt fw %s\n", - pci_name(priv->pdev), priv->fw_alt); - return rc; - } - return 0; -} - -static int mwl8k_firmware_load_success(struct mwl8k_priv *priv); -static void mwl8k_fw_state_machine(const struct firmware *fw, void *context) -{ - struct mwl8k_priv *priv = context; - struct mwl8k_device_info *di = priv->device_info; - int rc; - - switch (priv->fw_state) { - case FW_STATE_INIT: - if (!fw) { - printk(KERN_ERR "%s: Error requesting helper fw %s\n", - pci_name(priv->pdev), di->helper_image); - goto fail; - } - priv->fw_helper = fw; - rc = mwl8k_request_fw(priv, priv->fw_pref, &priv->fw_ucode, - true); - if (rc && priv->fw_alt) { - rc = mwl8k_request_alt_fw(priv); - if (rc) - goto fail; - priv->fw_state = FW_STATE_LOADING_ALT; - } else if (rc) - goto fail; - else - priv->fw_state = FW_STATE_LOADING_PREF; - break; - - case FW_STATE_LOADING_PREF: - if (!fw) { - if (priv->fw_alt) { - rc = mwl8k_request_alt_fw(priv); - if (rc) - goto fail; - priv->fw_state = FW_STATE_LOADING_ALT; - } else - goto fail; - } else { - priv->fw_ucode = fw; - rc = mwl8k_firmware_load_success(priv); - if (rc) - goto fail; - else - complete(&priv->firmware_loading_complete); - } - break; - - case FW_STATE_LOADING_ALT: - if (!fw) { - printk(KERN_ERR "%s: Error requesting alt fw %s\n", - pci_name(priv->pdev), di->helper_image); - goto fail; - } - priv->fw_ucode = fw; - rc = mwl8k_firmware_load_success(priv); - if (rc) - goto fail; - else - complete(&priv->firmware_loading_complete); - break; - - default: - printk(KERN_ERR "%s: Unexpected firmware loading state: %d\n", - MWL8K_NAME, priv->fw_state); - BUG_ON(1); - } - - return; - -fail: - priv->fw_state = FW_STATE_ERROR; - complete(&priv->firmware_loading_complete); - device_release_driver(&priv->pdev->dev); - mwl8k_release_firmware(priv); -} - -#define MAX_RESTART_ATTEMPTS 1 -static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image, - bool nowait) -{ - struct mwl8k_priv *priv = hw->priv; - int rc; - int count = MAX_RESTART_ATTEMPTS; - -retry: - /* Reset firmware and hardware */ - mwl8k_hw_reset(priv); - - /* Ask userland hotplug daemon for the device firmware */ - rc = mwl8k_request_firmware(priv, fw_image, nowait); - if (rc) { - wiphy_err(hw->wiphy, "Firmware files not found\n"); - return rc; - } - - if (nowait) - return rc; - - /* Load firmware into hardware */ - rc = mwl8k_load_firmware(hw); - if (rc) - wiphy_err(hw->wiphy, "Cannot start firmware\n"); - - /* Reclaim memory once firmware is successfully loaded */ - mwl8k_release_firmware(priv); - - if (rc && count) { - /* FW did not start successfully; - * lets try one more time - */ - count--; - wiphy_err(hw->wiphy, "Trying to reload the firmware again\n"); - msleep(20); - goto retry; - } - - return rc; -} - -static int mwl8k_init_txqs(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - int rc = 0; - int i; - - for (i = 0; i < mwl8k_tx_queues(priv); i++) { - rc = mwl8k_txq_init(hw, i); - if (rc) - break; - if (priv->ap_fw) - iowrite32(priv->txq[i].txd_dma, - priv->sram + priv->txq_offset[i]); - } - return rc; -} - -/* initialize hw after successfully loading a firmware image */ -static int mwl8k_probe_hw(struct ieee80211_hw *hw) -{ - struct mwl8k_priv *priv = hw->priv; - int rc = 0; - int i; - - if (priv->ap_fw) { - priv->rxd_ops = priv->device_info->ap_rxd_ops; - if (priv->rxd_ops == NULL) { - wiphy_err(hw->wiphy, - "Driver does not have AP firmware image support for this hardware\n"); - rc = -ENOENT; - goto err_stop_firmware; - } - } else { - priv->rxd_ops = &rxd_sta_ops; - } - - priv->sniffer_enabled = false; - priv->wmm_enabled = false; - priv->pending_tx_pkts = 0; - atomic_set(&priv->watchdog_event_pending, 0); - - rc = mwl8k_rxq_init(hw, 0); - if (rc) - goto err_stop_firmware; - rxq_refill(hw, 0, INT_MAX); - - /* For the sta firmware, we need to know the dma addresses of tx queues - * before sending MWL8K_CMD_GET_HW_SPEC. So we must initialize them - * prior to issuing this command. But for the AP case, we learn the - * total number of queues from the result CMD_GET_HW_SPEC, so for this - * case we must initialize the tx queues after. - */ - priv->num_ampdu_queues = 0; - if (!priv->ap_fw) { - rc = mwl8k_init_txqs(hw); - if (rc) - goto err_free_queues; - } - - iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - iowrite32(MWL8K_A2H_INT_TX_DONE|MWL8K_A2H_INT_RX_READY| - MWL8K_A2H_INT_BA_WATCHDOG, - priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); - iowrite32(MWL8K_A2H_INT_OPC_DONE, - priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); - - rc = request_irq(priv->pdev->irq, mwl8k_interrupt, - IRQF_SHARED, MWL8K_NAME, hw); - if (rc) { - wiphy_err(hw->wiphy, "failed to register IRQ handler\n"); - goto err_free_queues; - } - - /* - * When hw restart is requested, - * mac80211 will take care of clearing - * the ampdu streams, so do not clear - * the ampdu state here - */ - if (!priv->hw_restart_in_progress) - memset(priv->ampdu, 0, sizeof(priv->ampdu)); - - /* - * Temporarily enable interrupts. Initial firmware host - * commands use interrupts and avoid polling. Disable - * interrupts when done. - */ - iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - - /* Get config data, mac addrs etc */ - if (priv->ap_fw) { - rc = mwl8k_cmd_get_hw_spec_ap(hw); - if (!rc) - rc = mwl8k_init_txqs(hw); - if (!rc) - rc = mwl8k_cmd_set_hw_spec(hw); - } else { - rc = mwl8k_cmd_get_hw_spec_sta(hw); - } - if (rc) { - wiphy_err(hw->wiphy, "Cannot initialise firmware\n"); - goto err_free_irq; - } - - /* Turn radio off */ - rc = mwl8k_cmd_radio_disable(hw); - if (rc) { - wiphy_err(hw->wiphy, "Cannot disable\n"); - goto err_free_irq; - } - - /* Clear MAC address */ - rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00"); - if (rc) { - wiphy_err(hw->wiphy, "Cannot clear MAC address\n"); - goto err_free_irq; - } - - /* Configure Antennas */ - rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3); - if (rc) - wiphy_warn(hw->wiphy, "failed to set # of RX antennas"); - rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7); - if (rc) - wiphy_warn(hw->wiphy, "failed to set # of TX antennas"); - - - /* Disable interrupts */ - iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - free_irq(priv->pdev->irq, hw); - - wiphy_info(hw->wiphy, "%s v%d, %pm, %s firmware %u.%u.%u.%u\n", - priv->device_info->part_name, - priv->hw_rev, hw->wiphy->perm_addr, - priv->ap_fw ? "AP" : "STA", - (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff, - (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff); - - return 0; - -err_free_irq: - iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - free_irq(priv->pdev->irq, hw); - -err_free_queues: - for (i = 0; i < mwl8k_tx_queues(priv); i++) - mwl8k_txq_deinit(hw, i); - mwl8k_rxq_deinit(hw, 0); - -err_stop_firmware: - mwl8k_hw_reset(priv); - - return rc; -} - -/* - * invoke mwl8k_reload_firmware to change the firmware image after the device - * has already been registered - */ -static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image) -{ - int i, rc = 0; - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *vif, *tmp_vif; - - mwl8k_stop(hw); - mwl8k_rxq_deinit(hw, 0); - - /* - * All the existing interfaces are re-added by the ieee80211_reconfig; - * which means driver should remove existing interfaces before calling - * ieee80211_restart_hw - */ - if (priv->hw_restart_in_progress) - list_for_each_entry_safe(vif, tmp_vif, &priv->vif_list, list) - mwl8k_remove_vif(priv, vif); - - for (i = 0; i < mwl8k_tx_queues(priv); i++) - mwl8k_txq_deinit(hw, i); - - rc = mwl8k_init_firmware(hw, fw_image, false); - if (rc) - goto fail; - - rc = mwl8k_probe_hw(hw); - if (rc) - goto fail; - - if (priv->hw_restart_in_progress) - return rc; - - rc = mwl8k_start(hw); - if (rc) - goto fail; - - rc = mwl8k_config(hw, ~0); - if (rc) - goto fail; - - for (i = 0; i < MWL8K_TX_WMM_QUEUES; i++) { - rc = mwl8k_conf_tx(hw, NULL, i, &priv->wmm_params[i]); - if (rc) - goto fail; - } - - return rc; - -fail: - printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n"); - return rc; -} - -static const struct ieee80211_iface_limit ap_if_limits[] = { - { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, - { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) }, -}; - -static const struct ieee80211_iface_combination ap_if_comb = { - .limits = ap_if_limits, - .n_limits = ARRAY_SIZE(ap_if_limits), - .max_interfaces = 8, - .num_different_channels = 1, -}; - - -static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) -{ - struct ieee80211_hw *hw = priv->hw; - int i, rc; - - rc = mwl8k_load_firmware(hw); - mwl8k_release_firmware(priv); - if (rc) { - wiphy_err(hw->wiphy, "Cannot start firmware\n"); - return rc; - } - - /* - * Extra headroom is the size of the required DMA header - * minus the size of the smallest 802.11 frame (CTS frame). - */ - hw->extra_tx_headroom = - sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts); - - hw->extra_tx_headroom -= priv->ap_fw ? REDUCED_TX_HEADROOM : 0; - - hw->queues = MWL8K_TX_WMM_QUEUES; - - /* Set rssi values to dBm */ - ieee80211_hw_set(hw, SIGNAL_DBM); - ieee80211_hw_set(hw, HAS_RATE_CONTROL); - - /* - * Ask mac80211 to not to trigger PS mode - * based on PM bit of incoming frames. - */ - if (priv->ap_fw) - ieee80211_hw_set(hw, AP_LINK_PS); - - hw->vif_data_size = sizeof(struct mwl8k_vif); - hw->sta_data_size = sizeof(struct mwl8k_sta); - - priv->macids_used = 0; - INIT_LIST_HEAD(&priv->vif_list); - - /* Set default radio state and preamble */ - priv->radio_on = false; - priv->radio_short_preamble = false; - - /* Finalize join worker */ - INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); - /* Handle watchdog ba events */ - INIT_WORK(&priv->watchdog_ba_handle, mwl8k_watchdog_ba_events); - /* To reload the firmware if it crashes */ - INIT_WORK(&priv->fw_reload, mwl8k_hw_restart_work); - - /* TX reclaim and RX tasklets. */ - tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw); - tasklet_disable(&priv->poll_tx_task); - tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw); - tasklet_disable(&priv->poll_rx_task); - - /* Power management cookie */ - priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma); - if (priv->cookie == NULL) - return -ENOMEM; - - mutex_init(&priv->fw_mutex); - priv->fw_mutex_owner = NULL; - priv->fw_mutex_depth = 0; - priv->hostcmd_wait = NULL; - - spin_lock_init(&priv->tx_lock); - - spin_lock_init(&priv->stream_lock); - - priv->tx_wait = NULL; - - rc = mwl8k_probe_hw(hw); - if (rc) - goto err_free_cookie; - - hw->wiphy->interface_modes = 0; - - if (priv->ap_macids_supported || priv->device_info->fw_image_ap) { - hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); - hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); - hw->wiphy->iface_combinations = &ap_if_comb; - hw->wiphy->n_iface_combinations = 1; - } - - if (priv->sta_macids_supported || priv->device_info->fw_image_sta) - hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); - - rc = ieee80211_register_hw(hw); - if (rc) { - wiphy_err(hw->wiphy, "Cannot register device\n"); - goto err_unprobe_hw; - } - - return 0; - -err_unprobe_hw: - for (i = 0; i < mwl8k_tx_queues(priv); i++) - mwl8k_txq_deinit(hw, i); - mwl8k_rxq_deinit(hw, 0); - -err_free_cookie: - if (priv->cookie != NULL) - pci_free_consistent(priv->pdev, 4, - priv->cookie, priv->cookie_dma); - - return rc; -} -static int mwl8k_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - static int printed_version; - struct ieee80211_hw *hw; - struct mwl8k_priv *priv; - struct mwl8k_device_info *di; - int rc; - - if (!printed_version) { - printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION); - printed_version = 1; - } - - - rc = pci_enable_device(pdev); - if (rc) { - printk(KERN_ERR "%s: Cannot enable new PCI device\n", - MWL8K_NAME); - return rc; - } - - rc = pci_request_regions(pdev, MWL8K_NAME); - if (rc) { - printk(KERN_ERR "%s: Cannot obtain PCI resources\n", - MWL8K_NAME); - goto err_disable_device; - } - - pci_set_master(pdev); - - - hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops); - if (hw == NULL) { - printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME); - rc = -ENOMEM; - goto err_free_reg; - } - - SET_IEEE80211_DEV(hw, &pdev->dev); - pci_set_drvdata(pdev, hw); - - priv = hw->priv; - priv->hw = hw; - priv->pdev = pdev; - priv->device_info = &mwl8k_info_tbl[id->driver_data]; - - if (id->driver_data == MWL8764) - priv->is_8764 = true; - - priv->sram = pci_iomap(pdev, 0, 0x10000); - if (priv->sram == NULL) { - wiphy_err(hw->wiphy, "Cannot map device SRAM\n"); - rc = -EIO; - goto err_iounmap; - } - - /* - * If BAR0 is a 32 bit BAR, the register BAR will be BAR1. - * If BAR0 is a 64 bit BAR, the register BAR will be BAR2. - */ - priv->regs = pci_iomap(pdev, 1, 0x10000); - if (priv->regs == NULL) { - priv->regs = pci_iomap(pdev, 2, 0x10000); - if (priv->regs == NULL) { - wiphy_err(hw->wiphy, "Cannot map device registers\n"); - rc = -EIO; - goto err_iounmap; - } - } - - /* - * Choose the initial fw image depending on user input. If a second - * image is available, make it the alternative image that will be - * loaded if the first one fails. - */ - init_completion(&priv->firmware_loading_complete); - di = priv->device_info; - if (ap_mode_default && di->fw_image_ap) { - priv->fw_pref = di->fw_image_ap; - priv->fw_alt = di->fw_image_sta; - } else if (!ap_mode_default && di->fw_image_sta) { - priv->fw_pref = di->fw_image_sta; - priv->fw_alt = di->fw_image_ap; - } else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) { - printk(KERN_WARNING "AP fw is unavailable. Using STA fw."); - priv->fw_pref = di->fw_image_sta; - } else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) { - printk(KERN_WARNING "STA fw is unavailable. Using AP fw."); - priv->fw_pref = di->fw_image_ap; - } - rc = mwl8k_init_firmware(hw, priv->fw_pref, true); - if (rc) - goto err_stop_firmware; - - priv->hw_restart_in_progress = false; - - priv->running_bsses = 0; - - return rc; - -err_stop_firmware: - mwl8k_hw_reset(priv); - -err_iounmap: - if (priv->regs != NULL) - pci_iounmap(pdev, priv->regs); - - if (priv->sram != NULL) - pci_iounmap(pdev, priv->sram); - - ieee80211_free_hw(hw); - -err_free_reg: - pci_release_regions(pdev); - -err_disable_device: - pci_disable_device(pdev); - - return rc; -} - -static void mwl8k_remove(struct pci_dev *pdev) -{ - struct ieee80211_hw *hw = pci_get_drvdata(pdev); - struct mwl8k_priv *priv; - int i; - - if (hw == NULL) - return; - priv = hw->priv; - - wait_for_completion(&priv->firmware_loading_complete); - - if (priv->fw_state == FW_STATE_ERROR) { - mwl8k_hw_reset(priv); - goto unmap; - } - - ieee80211_stop_queues(hw); - - ieee80211_unregister_hw(hw); - - /* Remove TX reclaim and RX tasklets. */ - tasklet_kill(&priv->poll_tx_task); - tasklet_kill(&priv->poll_rx_task); - - /* Stop hardware */ - mwl8k_hw_reset(priv); - - /* Return all skbs to mac80211 */ - for (i = 0; i < mwl8k_tx_queues(priv); i++) - mwl8k_txq_reclaim(hw, i, INT_MAX, 1); - - for (i = 0; i < mwl8k_tx_queues(priv); i++) - mwl8k_txq_deinit(hw, i); - - mwl8k_rxq_deinit(hw, 0); - - pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma); - -unmap: - pci_iounmap(pdev, priv->regs); - pci_iounmap(pdev, priv->sram); - ieee80211_free_hw(hw); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -static struct pci_driver mwl8k_driver = { - .name = MWL8K_NAME, - .id_table = mwl8k_pci_id_table, - .probe = mwl8k_probe, - .remove = mwl8k_remove, -}; - -module_pci_driver(mwl8k_driver); - -MODULE_DESCRIPTION(MWL8K_DESC); -MODULE_VERSION(MWL8K_VERSION); -MODULE_AUTHOR("Lennert Buytenhek "); -MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From ed0ad06f5c8e5b8fc3c6d7c0e3d2dc546638c18c Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 07:39:37 +0200 Subject: zd1201: move under zydas vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 20 +- drivers/net/wireless/Makefile | 3 +- drivers/net/wireless/zd1201.c | 1913 ----------------------------------- drivers/net/wireless/zd1201.h | 147 --- drivers/net/wireless/zydas/Kconfig | 33 + drivers/net/wireless/zydas/Makefile | 1 + drivers/net/wireless/zydas/zd1201.c | 1913 +++++++++++++++++++++++++++++++++++ drivers/net/wireless/zydas/zd1201.h | 147 +++ 9 files changed, 2097 insertions(+), 2082 deletions(-) delete mode 100644 drivers/net/wireless/zd1201.c delete mode 100644 drivers/net/wireless/zd1201.h create mode 100644 drivers/net/wireless/zydas/Kconfig create mode 100644 drivers/net/wireless/zydas/Makefile create mode 100644 drivers/net/wireless/zydas/zd1201.c create mode 100644 drivers/net/wireless/zydas/zd1201.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index e622877aaab4..ec098370ef3d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11252,7 +11252,7 @@ USB ZD1201 DRIVER L: linux-wireless@vger.kernel.org W: http://linux-lc100020.sourceforge.net S: Orphan -F: drivers/net/wireless/zd1201.* +F: drivers/net/wireless/zydas/zd1201.* USB ZR364XX DRIVER M: Antoine Jacquet diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 315982a5ee73..35155e2dc8bf 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -24,6 +24,7 @@ source "drivers/net/wireless/cisco/Kconfig" source "drivers/net/wireless/intel/Kconfig" source "drivers/net/wireless/marvell/Kconfig" source "drivers/net/wireless/st/Kconfig" +source "drivers/net/wireless/zydas/Kconfig" config PCMCIA_RAYCS tristate "Aviator/Raytheon 2.4GHz wireless support" @@ -70,25 +71,6 @@ config PRISM54 When built as module you get the module prism54 -config USB_ZD1201 - tristate "USB ZD1201 based Wireless device support" - depends on CFG80211 && USB - select WIRELESS_EXT - select WEXT_PRIV - select FW_LOADER - ---help--- - Say Y if you want to use wireless LAN adapters based on the ZyDAS - ZD1201 chip. - - This driver makes the adapter appear as a normal Ethernet interface, - typically on wlan0. - - The zd1201 device requires external firmware to be loaded. - This can be found at http://linux-lc100020.sourceforge.net/ - - To compile this driver as a module, choose M here: the - module will be called zd1201. - config USB_NET_RNDIS_WLAN tristate "Wireless RNDIS USB support" depends on USB diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 9498753f596b..4ee6d6d08743 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/ obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ +obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ obj-$(CONFIG_HERMES) += orinoco/ @@ -24,8 +25,6 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o -obj-$(CONFIG_USB_ZD1201) += zd1201.o - obj-$(CONFIG_RT2X00) += rt2x00/ obj-$(CONFIG_WL_MEDIATEK) += mediatek/ diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c deleted file mode 100644 index 6f5c793a7855..000000000000 --- a/drivers/net/wireless/zd1201.c +++ /dev/null @@ -1,1913 +0,0 @@ -/* - * Driver for ZyDAS zd1201 based wireless USB devices. - * - * Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * Parts of this driver have been derived from a wlan-ng version - * modified by ZyDAS. They also made documentation available, thanks! - * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "zd1201.h" - -static struct usb_device_id zd1201_table[] = { - {USB_DEVICE(0x0586, 0x3400)}, /* Peabird Wireless USB Adapter */ - {USB_DEVICE(0x0ace, 0x1201)}, /* ZyDAS ZD1201 Wireless USB Adapter */ - {USB_DEVICE(0x050d, 0x6051)}, /* Belkin F5D6051 usb adapter */ - {USB_DEVICE(0x0db0, 0x6823)}, /* MSI UB11B usb adapter */ - {USB_DEVICE(0x1044, 0x8004)}, /* Gigabyte GN-WLBZ101 */ - {USB_DEVICE(0x1044, 0x8005)}, /* GIGABYTE GN-WLBZ201 usb adapter */ - {} -}; - -static int ap; /* Are we an AP or a normal station? */ - -#define ZD1201_VERSION "0.15" - -MODULE_AUTHOR("Jeroen Vreeken "); -MODULE_DESCRIPTION("Driver for ZyDAS ZD1201 based USB Wireless adapters"); -MODULE_VERSION(ZD1201_VERSION); -MODULE_LICENSE("GPL"); -module_param(ap, int, 0); -MODULE_PARM_DESC(ap, "If non-zero Access Point firmware will be loaded"); -MODULE_DEVICE_TABLE(usb, zd1201_table); - - -static int zd1201_fw_upload(struct usb_device *dev, int apfw) -{ - const struct firmware *fw_entry; - const char *data; - unsigned long len; - int err; - unsigned char ret; - char *buf; - char *fwfile; - - if (apfw) - fwfile = "zd1201-ap.fw"; - else - fwfile = "zd1201.fw"; - - err = request_firmware(&fw_entry, fwfile, &dev->dev); - if (err) { - dev_err(&dev->dev, "Failed to load %s firmware file!\n", fwfile); - dev_err(&dev->dev, "Make sure the hotplug firmware loader is installed.\n"); - dev_err(&dev->dev, "Goto http://linux-lc100020.sourceforge.net for more info.\n"); - return err; - } - - data = fw_entry->data; - len = fw_entry->size; - - buf = kmalloc(1024, GFP_ATOMIC); - if (!buf) { - err = -ENOMEM; - goto exit; - } - - while (len > 0) { - int translen = (len > 1024) ? 1024 : len; - memcpy(buf, data, translen); - - err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0, - USB_DIR_OUT | 0x40, 0, 0, buf, translen, - ZD1201_FW_TIMEOUT); - if (err < 0) - goto exit; - - len -= translen; - data += translen; - } - - err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x2, - USB_DIR_OUT | 0x40, 0, 0, NULL, 0, ZD1201_FW_TIMEOUT); - if (err < 0) - goto exit; - - err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4, - USB_DIR_IN | 0x40, 0, 0, buf, sizeof(ret), ZD1201_FW_TIMEOUT); - if (err < 0) - goto exit; - - memcpy(&ret, buf, sizeof(ret)); - - if (ret & 0x80) { - err = -EIO; - goto exit; - } - - err = 0; -exit: - kfree(buf); - release_firmware(fw_entry); - return err; -} - -MODULE_FIRMWARE("zd1201-ap.fw"); -MODULE_FIRMWARE("zd1201.fw"); - -static void zd1201_usbfree(struct urb *urb) -{ - struct zd1201 *zd = urb->context; - - switch(urb->status) { - case -EILSEQ: - case -ENODEV: - case -ETIME: - case -ENOENT: - case -EPIPE: - case -EOVERFLOW: - case -ESHUTDOWN: - dev_warn(&zd->usb->dev, "%s: urb failed: %d\n", - zd->dev->name, urb->status); - } - - kfree(urb->transfer_buffer); - usb_free_urb(urb); -} - -/* cmdreq message: - u32 type - u16 cmd - u16 parm0 - u16 parm1 - u16 parm2 - u8 pad[4] - - total: 4 + 2 + 2 + 2 + 2 + 4 = 16 -*/ -static int zd1201_docmd(struct zd1201 *zd, int cmd, int parm0, - int parm1, int parm2) -{ - unsigned char *command; - int ret; - struct urb *urb; - - command = kmalloc(16, GFP_ATOMIC); - if (!command) - return -ENOMEM; - - *((__le32*)command) = cpu_to_le32(ZD1201_USB_CMDREQ); - *((__le16*)&command[4]) = cpu_to_le16(cmd); - *((__le16*)&command[6]) = cpu_to_le16(parm0); - *((__le16*)&command[8]) = cpu_to_le16(parm1); - *((__le16*)&command[10])= cpu_to_le16(parm2); - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - kfree(command); - return -ENOMEM; - } - usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2), - command, 16, zd1201_usbfree, zd); - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) { - kfree(command); - usb_free_urb(urb); - } - - return ret; -} - -/* Callback after sending out a packet */ -static void zd1201_usbtx(struct urb *urb) -{ - struct zd1201 *zd = urb->context; - netif_wake_queue(zd->dev); -} - -/* Incoming data */ -static void zd1201_usbrx(struct urb *urb) -{ - struct zd1201 *zd = urb->context; - int free = 0; - unsigned char *data = urb->transfer_buffer; - struct sk_buff *skb; - unsigned char type; - - if (!zd) - return; - - switch(urb->status) { - case -EILSEQ: - case -ENODEV: - case -ETIME: - case -ENOENT: - case -EPIPE: - case -EOVERFLOW: - case -ESHUTDOWN: - dev_warn(&zd->usb->dev, "%s: rx urb failed: %d\n", - zd->dev->name, urb->status); - free = 1; - goto exit; - } - - if (urb->status != 0 || urb->actual_length == 0) - goto resubmit; - - type = data[0]; - if (type == ZD1201_PACKET_EVENTSTAT || type == ZD1201_PACKET_RESOURCE) { - memcpy(zd->rxdata, data, urb->actual_length); - zd->rxlen = urb->actual_length; - zd->rxdatas = 1; - wake_up(&zd->rxdataq); - } - /* Info frame */ - if (type == ZD1201_PACKET_INQUIRE) { - int i = 0; - unsigned short infotype, framelen, copylen; - framelen = le16_to_cpu(*(__le16*)&data[4]); - infotype = le16_to_cpu(*(__le16*)&data[6]); - - if (infotype == ZD1201_INF_LINKSTATUS) { - short linkstatus; - - linkstatus = le16_to_cpu(*(__le16*)&data[8]); - switch(linkstatus) { - case 1: - netif_carrier_on(zd->dev); - break; - case 2: - netif_carrier_off(zd->dev); - break; - case 3: - netif_carrier_off(zd->dev); - break; - case 4: - netif_carrier_on(zd->dev); - break; - default: - netif_carrier_off(zd->dev); - } - goto resubmit; - } - if (infotype == ZD1201_INF_ASSOCSTATUS) { - short status = le16_to_cpu(*(__le16*)(data+8)); - int event; - union iwreq_data wrqu; - - switch (status) { - case ZD1201_ASSOCSTATUS_STAASSOC: - case ZD1201_ASSOCSTATUS_REASSOC: - event = IWEVREGISTERED; - break; - case ZD1201_ASSOCSTATUS_DISASSOC: - case ZD1201_ASSOCSTATUS_ASSOCFAIL: - case ZD1201_ASSOCSTATUS_AUTHFAIL: - default: - event = IWEVEXPIRED; - } - memcpy(wrqu.addr.sa_data, data+10, ETH_ALEN); - wrqu.addr.sa_family = ARPHRD_ETHER; - - /* Send event to user space */ - wireless_send_event(zd->dev, event, &wrqu, NULL); - - goto resubmit; - } - if (infotype == ZD1201_INF_AUTHREQ) { - union iwreq_data wrqu; - - memcpy(wrqu.addr.sa_data, data+8, ETH_ALEN); - wrqu.addr.sa_family = ARPHRD_ETHER; - /* There isn't a event that trully fits this request. - We assume that userspace will be smart enough to - see a new station being expired and sends back a - authstation ioctl to authorize it. */ - wireless_send_event(zd->dev, IWEVEXPIRED, &wrqu, NULL); - goto resubmit; - } - /* Other infotypes are handled outside this handler */ - zd->rxlen = 0; - while (i < urb->actual_length) { - copylen = le16_to_cpu(*(__le16*)&data[i+2]); - /* Sanity check, sometimes we get junk */ - if (copylen+zd->rxlen > sizeof(zd->rxdata)) - break; - memcpy(zd->rxdata+zd->rxlen, data+i+4, copylen); - zd->rxlen += copylen; - i += 64; - } - if (i >= urb->actual_length) { - zd->rxdatas = 1; - wake_up(&zd->rxdataq); - } - goto resubmit; - } - /* Actual data */ - if (data[urb->actual_length-1] == ZD1201_PACKET_RXDATA) { - int datalen = urb->actual_length-1; - unsigned short len, fc, seq; - - len = ntohs(*(__be16 *)&data[datalen-2]); - if (len>datalen) - len=datalen; - fc = le16_to_cpu(*(__le16 *)&data[datalen-16]); - seq = le16_to_cpu(*(__le16 *)&data[datalen-24]); - - if (zd->monitor) { - if (datalen < 24) - goto resubmit; - if (!(skb = dev_alloc_skb(datalen+24))) - goto resubmit; - - memcpy(skb_put(skb, 2), &data[datalen-16], 2); - memcpy(skb_put(skb, 2), &data[datalen-2], 2); - memcpy(skb_put(skb, 6), &data[datalen-14], 6); - memcpy(skb_put(skb, 6), &data[datalen-22], 6); - memcpy(skb_put(skb, 6), &data[datalen-8], 6); - memcpy(skb_put(skb, 2), &data[datalen-24], 2); - memcpy(skb_put(skb, len), data, len); - skb->protocol = eth_type_trans(skb, zd->dev); - zd->dev->stats.rx_packets++; - zd->dev->stats.rx_bytes += skb->len; - netif_rx(skb); - goto resubmit; - } - - if ((seq & IEEE80211_SCTL_FRAG) || - (fc & IEEE80211_FCTL_MOREFRAGS)) { - struct zd1201_frag *frag = NULL; - char *ptr; - - if (datalen<14) - goto resubmit; - if ((seq & IEEE80211_SCTL_FRAG) == 0) { - frag = kmalloc(sizeof(*frag), GFP_ATOMIC); - if (!frag) - goto resubmit; - skb = dev_alloc_skb(IEEE80211_MAX_DATA_LEN +14+2); - if (!skb) { - kfree(frag); - goto resubmit; - } - frag->skb = skb; - frag->seq = seq & IEEE80211_SCTL_SEQ; - skb_reserve(skb, 2); - memcpy(skb_put(skb, 12), &data[datalen-14], 12); - memcpy(skb_put(skb, 2), &data[6], 2); - memcpy(skb_put(skb, len), data+8, len); - hlist_add_head(&frag->fnode, &zd->fraglist); - goto resubmit; - } - hlist_for_each_entry(frag, &zd->fraglist, fnode) - if (frag->seq == (seq&IEEE80211_SCTL_SEQ)) - break; - if (!frag) - goto resubmit; - skb = frag->skb; - ptr = skb_put(skb, len); - if (ptr) - memcpy(ptr, data+8, len); - if (fc & IEEE80211_FCTL_MOREFRAGS) - goto resubmit; - hlist_del_init(&frag->fnode); - kfree(frag); - } else { - if (datalen<14) - goto resubmit; - skb = dev_alloc_skb(len + 14 + 2); - if (!skb) - goto resubmit; - skb_reserve(skb, 2); - memcpy(skb_put(skb, 12), &data[datalen-14], 12); - memcpy(skb_put(skb, 2), &data[6], 2); - memcpy(skb_put(skb, len), data+8, len); - } - skb->protocol = eth_type_trans(skb, zd->dev); - zd->dev->stats.rx_packets++; - zd->dev->stats.rx_bytes += skb->len; - netif_rx(skb); - } -resubmit: - memset(data, 0, ZD1201_RXSIZE); - - urb->status = 0; - urb->dev = zd->usb; - if(usb_submit_urb(urb, GFP_ATOMIC)) - free = 1; - -exit: - if (free) { - zd->rxlen = 0; - zd->rxdatas = 1; - wake_up(&zd->rxdataq); - kfree(urb->transfer_buffer); - } -} - -static int zd1201_getconfig(struct zd1201 *zd, int rid, void *riddata, - unsigned int riddatalen) -{ - int err; - int i = 0; - int code; - int rid_fid; - int length; - unsigned char *pdata; - - zd->rxdatas = 0; - err = zd1201_docmd(zd, ZD1201_CMDCODE_ACCESS, rid, 0, 0); - if (err) - return err; - - wait_event_interruptible(zd->rxdataq, zd->rxdatas); - if (!zd->rxlen) - return -EIO; - - code = le16_to_cpu(*(__le16*)(&zd->rxdata[4])); - rid_fid = le16_to_cpu(*(__le16*)(&zd->rxdata[6])); - length = le16_to_cpu(*(__le16*)(&zd->rxdata[8])); - if (length > zd->rxlen) - length = zd->rxlen-6; - - /* If access bit is not on, then error */ - if ((code & ZD1201_ACCESSBIT) != ZD1201_ACCESSBIT || rid_fid != rid ) - return -EINVAL; - - /* Not enough buffer for allocating data */ - if (riddatalen != (length - 4)) { - dev_dbg(&zd->usb->dev, "riddatalen mismatches, expected=%u, (packet=%u) length=%u, rid=0x%04X, rid_fid=0x%04X\n", - riddatalen, zd->rxlen, length, rid, rid_fid); - return -ENODATA; - } - - zd->rxdatas = 0; - /* Issue SetRxRid commnd */ - err = zd1201_docmd(zd, ZD1201_CMDCODE_SETRXRID, rid, 0, length); - if (err) - return err; - - /* Receive RID record from resource packets */ - wait_event_interruptible(zd->rxdataq, zd->rxdatas); - if (!zd->rxlen) - return -EIO; - - if (zd->rxdata[zd->rxlen - 1] != ZD1201_PACKET_RESOURCE) { - dev_dbg(&zd->usb->dev, "Packet type mismatch: 0x%x not 0x3\n", - zd->rxdata[zd->rxlen-1]); - return -EINVAL; - } - - /* Set the data pointer and received data length */ - pdata = zd->rxdata; - length = zd->rxlen; - - do { - int actual_length; - - actual_length = (length > 64) ? 64 : length; - - if (pdata[0] != 0x3) { - dev_dbg(&zd->usb->dev, "Rx Resource packet type error: %02X\n", - pdata[0]); - return -EINVAL; - } - - if (actual_length != 64) { - /* Trim the last packet type byte */ - actual_length--; - } - - /* Skip the 4 bytes header (RID length and RID) */ - if (i == 0) { - pdata += 8; - actual_length -= 8; - } else { - pdata += 4; - actual_length -= 4; - } - - memcpy(riddata, pdata, actual_length); - riddata += actual_length; - pdata += actual_length; - length -= 64; - i++; - } while (length > 0); - - return 0; -} - -/* - * resreq: - * byte type - * byte sequence - * u16 reserved - * byte data[12] - * total: 16 - */ -static int zd1201_setconfig(struct zd1201 *zd, int rid, void *buf, int len, int wait) -{ - int err; - unsigned char *request; - int reqlen; - char seq=0; - struct urb *urb; - gfp_t gfp_mask = wait ? GFP_NOIO : GFP_ATOMIC; - - len += 4; /* first 4 are for header */ - - zd->rxdatas = 0; - zd->rxlen = 0; - for (seq=0; len > 0; seq++) { - request = kmalloc(16, gfp_mask); - if (!request) - return -ENOMEM; - urb = usb_alloc_urb(0, gfp_mask); - if (!urb) { - kfree(request); - return -ENOMEM; - } - memset(request, 0, 16); - reqlen = len>12 ? 12 : len; - request[0] = ZD1201_USB_RESREQ; - request[1] = seq; - request[2] = 0; - request[3] = 0; - if (request[1] == 0) { - /* add header */ - *(__le16*)&request[4] = cpu_to_le16((len-2+1)/2); - *(__le16*)&request[6] = cpu_to_le16(rid); - memcpy(request+8, buf, reqlen-4); - buf += reqlen-4; - } else { - memcpy(request+4, buf, reqlen); - buf += reqlen; - } - - len -= reqlen; - - usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, - zd->endp_out2), request, 16, zd1201_usbfree, zd); - err = usb_submit_urb(urb, gfp_mask); - if (err) - goto err; - } - - request = kmalloc(16, gfp_mask); - if (!request) - return -ENOMEM; - urb = usb_alloc_urb(0, gfp_mask); - if (!urb) { - kfree(request); - return -ENOMEM; - } - *((__le32*)request) = cpu_to_le32(ZD1201_USB_CMDREQ); - *((__le16*)&request[4]) = - cpu_to_le16(ZD1201_CMDCODE_ACCESS|ZD1201_ACCESSBIT); - *((__le16*)&request[6]) = cpu_to_le16(rid); - *((__le16*)&request[8]) = cpu_to_le16(0); - *((__le16*)&request[10]) = cpu_to_le16(0); - usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2), - request, 16, zd1201_usbfree, zd); - err = usb_submit_urb(urb, gfp_mask); - if (err) - goto err; - - if (wait) { - wait_event_interruptible(zd->rxdataq, zd->rxdatas); - if (!zd->rxlen || le16_to_cpu(*(__le16*)&zd->rxdata[6]) != rid) { - dev_dbg(&zd->usb->dev, "wrong or no RID received\n"); - } - } - - return 0; -err: - kfree(request); - usb_free_urb(urb); - return err; -} - -static inline int zd1201_getconfig16(struct zd1201 *zd, int rid, short *val) -{ - int err; - __le16 zdval; - - err = zd1201_getconfig(zd, rid, &zdval, sizeof(__le16)); - if (err) - return err; - *val = le16_to_cpu(zdval); - return 0; -} - -static inline int zd1201_setconfig16(struct zd1201 *zd, int rid, short val) -{ - __le16 zdval = cpu_to_le16(val); - return (zd1201_setconfig(zd, rid, &zdval, sizeof(__le16), 1)); -} - -static int zd1201_drvr_start(struct zd1201 *zd) -{ - int err, i; - short max; - __le16 zdmax; - unsigned char *buffer; - - buffer = kzalloc(ZD1201_RXSIZE, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - usb_fill_bulk_urb(zd->rx_urb, zd->usb, - usb_rcvbulkpipe(zd->usb, zd->endp_in), buffer, ZD1201_RXSIZE, - zd1201_usbrx, zd); - - err = usb_submit_urb(zd->rx_urb, GFP_KERNEL); - if (err) - goto err_buffer; - - err = zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0); - if (err) - goto err_urb; - - err = zd1201_getconfig(zd, ZD1201_RID_CNFMAXTXBUFFERNUMBER, &zdmax, - sizeof(__le16)); - if (err) - goto err_urb; - - max = le16_to_cpu(zdmax); - for (i=0; irx_urb); - return err; -err_buffer: - kfree(buffer); - return err; -} - -/* Magic alert: The firmware doesn't seem to like the MAC state being - * toggled in promisc (aka monitor) mode. - * (It works a number of times, but will halt eventually) - * So we turn it of before disabling and on after enabling if needed. - */ -static int zd1201_enable(struct zd1201 *zd) -{ - int err; - - if (zd->mac_enabled) - return 0; - - err = zd1201_docmd(zd, ZD1201_CMDCODE_ENABLE, 0, 0, 0); - if (!err) - zd->mac_enabled = 1; - - if (zd->monitor) - err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 1); - - return err; -} - -static int zd1201_disable(struct zd1201 *zd) -{ - int err; - - if (!zd->mac_enabled) - return 0; - if (zd->monitor) { - err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0); - if (err) - return err; - } - - err = zd1201_docmd(zd, ZD1201_CMDCODE_DISABLE, 0, 0, 0); - if (!err) - zd->mac_enabled = 0; - return err; -} - -static int zd1201_mac_reset(struct zd1201 *zd) -{ - if (!zd->mac_enabled) - return 0; - zd1201_disable(zd); - return zd1201_enable(zd); -} - -static int zd1201_join(struct zd1201 *zd, char *essid, int essidlen) -{ - int err, val; - char buf[IW_ESSID_MAX_SIZE+2]; - - err = zd1201_disable(zd); - if (err) - return err; - - val = ZD1201_CNFAUTHENTICATION_OPENSYSTEM; - val |= ZD1201_CNFAUTHENTICATION_SHAREDKEY; - err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, val); - if (err) - return err; - - *(__le16 *)buf = cpu_to_le16(essidlen); - memcpy(buf+2, essid, essidlen); - if (!zd->ap) { /* Normal station */ - err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf, - IW_ESSID_MAX_SIZE+2, 1); - if (err) - return err; - } else { /* AP */ - err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNSSID, buf, - IW_ESSID_MAX_SIZE+2, 1); - if (err) - return err; - } - - err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR, - zd->dev->dev_addr, zd->dev->addr_len, 1); - if (err) - return err; - - err = zd1201_enable(zd); - if (err) - return err; - - msleep(100); - return 0; -} - -static int zd1201_net_open(struct net_device *dev) -{ - struct zd1201 *zd = netdev_priv(dev); - - /* Start MAC with wildcard if no essid set */ - if (!zd->mac_enabled) - zd1201_join(zd, zd->essid, zd->essidlen); - netif_start_queue(dev); - - return 0; -} - -static int zd1201_net_stop(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -/* - RFC 1042 encapsulates Ethernet frames in 802.11 frames - by prefixing them with 0xaa, 0xaa, 0x03) followed by a SNAP OID of 0 - (0x00, 0x00, 0x00). Zd requires an additional padding, copy - of ethernet addresses, length of the standard RFC 1042 packet - and a command byte (which is nul for tx). - - tx frame (from Wlan NG): - RFC 1042: - llc 0xAA 0xAA 0x03 (802.2 LLC) - snap 0x00 0x00 0x00 (Ethernet encapsulated) - type 2 bytes, Ethernet type field - payload (minus eth header) - Zydas specific: - padding 1B if (skb->len+8+1)%64==0 - Eth MAC addr 12 bytes, Ethernet MAC addresses - length 2 bytes, RFC 1042 packet length - (llc+snap+type+payload) - zd 1 null byte, zd1201 packet type - */ -static netdev_tx_t zd1201_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct zd1201 *zd = netdev_priv(dev); - unsigned char *txbuf = zd->txdata; - int txbuflen, pad = 0, err; - struct urb *urb = zd->tx_urb; - - if (!zd->mac_enabled || zd->monitor) { - dev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; - } - netif_stop_queue(dev); - - txbuflen = skb->len + 8 + 1; - if (txbuflen%64 == 0) { - pad = 1; - txbuflen++; - } - txbuf[0] = 0xAA; - txbuf[1] = 0xAA; - txbuf[2] = 0x03; - txbuf[3] = 0x00; /* rfc1042 */ - txbuf[4] = 0x00; - txbuf[5] = 0x00; - - skb_copy_from_linear_data_offset(skb, 12, txbuf + 6, skb->len - 12); - if (pad) - txbuf[skb->len-12+6]=0; - skb_copy_from_linear_data(skb, txbuf + skb->len - 12 + 6 + pad, 12); - *(__be16*)&txbuf[skb->len+6+pad] = htons(skb->len-12+6); - txbuf[txbuflen-1] = 0; - - usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out), - txbuf, txbuflen, zd1201_usbtx, zd); - - err = usb_submit_urb(zd->tx_urb, GFP_ATOMIC); - if (err) { - dev->stats.tx_errors++; - netif_start_queue(dev); - } else { - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - } - kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static void zd1201_tx_timeout(struct net_device *dev) -{ - struct zd1201 *zd = netdev_priv(dev); - - if (!zd) - return; - dev_warn(&zd->usb->dev, "%s: TX timeout, shooting down urb\n", - dev->name); - usb_unlink_urb(zd->tx_urb); - dev->stats.tx_errors++; - /* Restart the timeout to quiet the watchdog: */ - dev->trans_start = jiffies; /* prevent tx timeout */ -} - -static int zd1201_set_mac_address(struct net_device *dev, void *p) -{ - struct sockaddr *addr = p; - struct zd1201 *zd = netdev_priv(dev); - int err; - - if (!zd) - return -ENODEV; - - err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR, - addr->sa_data, dev->addr_len, 1); - if (err) - return err; - memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - - return zd1201_mac_reset(zd); -} - -static struct iw_statistics *zd1201_get_wireless_stats(struct net_device *dev) -{ - struct zd1201 *zd = netdev_priv(dev); - - return &zd->iwstats; -} - -static void zd1201_set_multicast(struct net_device *dev) -{ - struct zd1201 *zd = netdev_priv(dev); - struct netdev_hw_addr *ha; - unsigned char reqbuf[ETH_ALEN*ZD1201_MAXMULTI]; - int i; - - if (netdev_mc_count(dev) > ZD1201_MAXMULTI) - return; - - i = 0; - netdev_for_each_mc_addr(ha, dev) - memcpy(reqbuf + i++ * ETH_ALEN, ha->addr, ETH_ALEN); - zd1201_setconfig(zd, ZD1201_RID_CNFGROUPADDRESS, reqbuf, - netdev_mc_count(dev) * ETH_ALEN, 0); -} - -static int zd1201_config_commit(struct net_device *dev, - struct iw_request_info *info, struct iw_point *data, char *essid) -{ - struct zd1201 *zd = netdev_priv(dev); - - return zd1201_mac_reset(zd); -} - -static int zd1201_get_name(struct net_device *dev, - struct iw_request_info *info, char *name, char *extra) -{ - strcpy(name, "IEEE 802.11b"); - return 0; -} - -static int zd1201_set_freq(struct net_device *dev, - struct iw_request_info *info, struct iw_freq *freq, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short channel = 0; - int err; - - if (freq->e == 0) - channel = freq->m; - else - channel = ieee80211_frequency_to_channel(freq->m); - - err = zd1201_setconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, channel); - if (err) - return err; - - zd1201_mac_reset(zd); - - return 0; -} - -static int zd1201_get_freq(struct net_device *dev, - struct iw_request_info *info, struct iw_freq *freq, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short channel; - int err; - - err = zd1201_getconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, &channel); - if (err) - return err; - freq->e = 0; - freq->m = channel; - - return 0; -} - -static int zd1201_set_mode(struct net_device *dev, - struct iw_request_info *info, __u32 *mode, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short porttype, monitor = 0; - unsigned char buffer[IW_ESSID_MAX_SIZE+2]; - int err; - - if (zd->ap) { - if (*mode != IW_MODE_MASTER) - return -EINVAL; - return 0; - } - - err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0); - if (err) - return err; - zd->dev->type = ARPHRD_ETHER; - switch(*mode) { - case IW_MODE_MONITOR: - monitor = 1; - zd->dev->type = ARPHRD_IEEE80211; - /* Make sure we are no longer associated with by - setting an 'impossible' essid. - (otherwise we mess up firmware) - */ - zd1201_join(zd, "\0-*#\0", 5); - /* Put port in pIBSS */ - case 8: /* No pseudo-IBSS in wireless extensions (yet) */ - porttype = ZD1201_PORTTYPE_PSEUDOIBSS; - break; - case IW_MODE_ADHOC: - porttype = ZD1201_PORTTYPE_IBSS; - break; - case IW_MODE_INFRA: - porttype = ZD1201_PORTTYPE_BSS; - break; - default: - return -EINVAL; - } - - err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype); - if (err) - return err; - if (zd->monitor && !monitor) { - zd1201_disable(zd); - *(__le16 *)buffer = cpu_to_le16(zd->essidlen); - memcpy(buffer+2, zd->essid, zd->essidlen); - err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, - buffer, IW_ESSID_MAX_SIZE+2, 1); - if (err) - return err; - } - zd->monitor = monitor; - /* If monitor mode is set we don't actually turn it on here since it - * is done during mac reset anyway (see zd1201_mac_enable). - */ - zd1201_mac_reset(zd); - - return 0; -} - -static int zd1201_get_mode(struct net_device *dev, - struct iw_request_info *info, __u32 *mode, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short porttype; - int err; - - err = zd1201_getconfig16(zd, ZD1201_RID_CNFPORTTYPE, &porttype); - if (err) - return err; - switch(porttype) { - case ZD1201_PORTTYPE_IBSS: - *mode = IW_MODE_ADHOC; - break; - case ZD1201_PORTTYPE_BSS: - *mode = IW_MODE_INFRA; - break; - case ZD1201_PORTTYPE_WDS: - *mode = IW_MODE_REPEAT; - break; - case ZD1201_PORTTYPE_PSEUDOIBSS: - *mode = 8;/* No Pseudo-IBSS... */ - break; - case ZD1201_PORTTYPE_AP: - *mode = IW_MODE_MASTER; - break; - default: - dev_dbg(&zd->usb->dev, "Unknown porttype: %d\n", - porttype); - *mode = IW_MODE_AUTO; - } - if (zd->monitor) - *mode = IW_MODE_MONITOR; - - return 0; -} - -static int zd1201_get_range(struct net_device *dev, - struct iw_request_info *info, struct iw_point *wrq, char *extra) -{ - struct iw_range *range = (struct iw_range *)extra; - - wrq->length = sizeof(struct iw_range); - memset(range, 0, sizeof(struct iw_range)); - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = WIRELESS_EXT; - - range->max_qual.qual = 128; - range->max_qual.level = 128; - range->max_qual.noise = 128; - range->max_qual.updated = 7; - - range->encoding_size[0] = 5; - range->encoding_size[1] = 13; - range->num_encoding_sizes = 2; - range->max_encoding_tokens = ZD1201_NUMKEYS; - - range->num_bitrates = 4; - range->bitrate[0] = 1000000; - range->bitrate[1] = 2000000; - range->bitrate[2] = 5500000; - range->bitrate[3] = 11000000; - - range->min_rts = 0; - range->min_frag = ZD1201_FRAGMIN; - range->max_rts = ZD1201_RTSMAX; - range->min_frag = ZD1201_FRAGMAX; - - return 0; -} - -/* Little bit of magic here: we only get the quality if we poll - * for it, and we never get an actual request to trigger such - * a poll. Therefore we 'assume' that the user will soon ask for - * the stats after asking the bssid. - */ -static int zd1201_get_wap(struct net_device *dev, - struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - unsigned char buffer[6]; - - if (!zd1201_getconfig(zd, ZD1201_RID_COMMSQUALITY, buffer, 6)) { - /* Unfortunately the quality and noise reported is useless. - they seem to be accumulators that increase until you - read them, unless we poll on a fixed interval we can't - use them - */ - /*zd->iwstats.qual.qual = le16_to_cpu(((__le16 *)buffer)[0]);*/ - zd->iwstats.qual.level = le16_to_cpu(((__le16 *)buffer)[1]); - /*zd->iwstats.qual.noise = le16_to_cpu(((__le16 *)buffer)[2]);*/ - zd->iwstats.qual.updated = 2; - } - - return zd1201_getconfig(zd, ZD1201_RID_CURRENTBSSID, ap_addr->sa_data, 6); -} - -static int zd1201_set_scan(struct net_device *dev, - struct iw_request_info *info, struct iw_point *srq, char *extra) -{ - /* We do everything in get_scan */ - return 0; -} - -static int zd1201_get_scan(struct net_device *dev, - struct iw_request_info *info, struct iw_point *srq, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - int err, i, j, enabled_save; - struct iw_event iwe; - char *cev = extra; - char *end_buf = extra + IW_SCAN_MAX_DATA; - - /* No scanning in AP mode */ - if (zd->ap) - return -EOPNOTSUPP; - - /* Scan doesn't seem to work if disabled */ - enabled_save = zd->mac_enabled; - zd1201_enable(zd); - - zd->rxdatas = 0; - err = zd1201_docmd(zd, ZD1201_CMDCODE_INQUIRE, - ZD1201_INQ_SCANRESULTS, 0, 0); - if (err) - return err; - - wait_event_interruptible(zd->rxdataq, zd->rxdatas); - if (!zd->rxlen) - return -EIO; - - if (le16_to_cpu(*(__le16*)&zd->rxdata[2]) != ZD1201_INQ_SCANRESULTS) - return -EIO; - - for(i=8; irxlen; i+=62) { - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, zd->rxdata+i+6, 6); - cev = iwe_stream_add_event(info, cev, end_buf, - &iwe, IW_EV_ADDR_LEN); - - iwe.cmd = SIOCGIWESSID; - iwe.u.data.length = zd->rxdata[i+16]; - iwe.u.data.flags = 1; - cev = iwe_stream_add_point(info, cev, end_buf, - &iwe, zd->rxdata+i+18); - - iwe.cmd = SIOCGIWMODE; - if (zd->rxdata[i+14]&0x01) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_ADHOC; - cev = iwe_stream_add_event(info, cev, end_buf, - &iwe, IW_EV_UINT_LEN); - - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = zd->rxdata[i+0]; - iwe.u.freq.e = 0; - cev = iwe_stream_add_event(info, cev, end_buf, - &iwe, IW_EV_FREQ_LEN); - - iwe.cmd = SIOCGIWRATE; - iwe.u.bitrate.fixed = 0; - iwe.u.bitrate.disabled = 0; - for (j=0; j<10; j++) if (zd->rxdata[i+50+j]) { - iwe.u.bitrate.value = (zd->rxdata[i+50+j]&0x7f)*500000; - cev = iwe_stream_add_event(info, cev, end_buf, - &iwe, IW_EV_PARAM_LEN); - } - - iwe.cmd = SIOCGIWENCODE; - iwe.u.data.length = 0; - if (zd->rxdata[i+14]&0x10) - iwe.u.data.flags = IW_ENCODE_ENABLED; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL); - - iwe.cmd = IWEVQUAL; - iwe.u.qual.qual = zd->rxdata[i+4]; - iwe.u.qual.noise= zd->rxdata[i+2]/10-100; - iwe.u.qual.level = (256+zd->rxdata[i+4]*100)/255-100; - iwe.u.qual.updated = 7; - cev = iwe_stream_add_event(info, cev, end_buf, - &iwe, IW_EV_QUAL_LEN); - } - - if (!enabled_save) - zd1201_disable(zd); - - srq->length = cev - extra; - srq->flags = 0; - - return 0; -} - -static int zd1201_set_essid(struct net_device *dev, - struct iw_request_info *info, struct iw_point *data, char *essid) -{ - struct zd1201 *zd = netdev_priv(dev); - - if (data->length > IW_ESSID_MAX_SIZE) - return -EINVAL; - if (data->length < 1) - data->length = 1; - zd->essidlen = data->length; - memset(zd->essid, 0, IW_ESSID_MAX_SIZE+1); - memcpy(zd->essid, essid, data->length); - return zd1201_join(zd, zd->essid, zd->essidlen); -} - -static int zd1201_get_essid(struct net_device *dev, - struct iw_request_info *info, struct iw_point *data, char *essid) -{ - struct zd1201 *zd = netdev_priv(dev); - - memcpy(essid, zd->essid, zd->essidlen); - data->flags = 1; - data->length = zd->essidlen; - - return 0; -} - -static int zd1201_get_nick(struct net_device *dev, struct iw_request_info *info, - struct iw_point *data, char *nick) -{ - strcpy(nick, "zd1201"); - data->flags = 1; - data->length = strlen(nick); - return 0; -} - -static int zd1201_set_rate(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short rate; - int err; - - switch (rrq->value) { - case 1000000: - rate = ZD1201_RATEB1; - break; - case 2000000: - rate = ZD1201_RATEB2; - break; - case 5500000: - rate = ZD1201_RATEB5; - break; - case 11000000: - default: - rate = ZD1201_RATEB11; - break; - } - if (!rrq->fixed) { /* Also enable all lower bitrates */ - rate |= rate-1; - } - - err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL, rate); - if (err) - return err; - - return zd1201_mac_reset(zd); -} - -static int zd1201_get_rate(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short rate; - int err; - - err = zd1201_getconfig16(zd, ZD1201_RID_CURRENTTXRATE, &rate); - if (err) - return err; - - switch(rate) { - case 1: - rrq->value = 1000000; - break; - case 2: - rrq->value = 2000000; - break; - case 5: - rrq->value = 5500000; - break; - case 11: - rrq->value = 11000000; - break; - default: - rrq->value = 0; - } - rrq->fixed = 0; - rrq->disabled = 0; - - return 0; -} - -static int zd1201_set_rts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - int err; - short val = rts->value; - - if (rts->disabled || !rts->fixed) - val = ZD1201_RTSMAX; - if (val > ZD1201_RTSMAX) - return -EINVAL; - if (val < 0) - return -EINVAL; - - err = zd1201_setconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, val); - if (err) - return err; - return zd1201_mac_reset(zd); -} - -static int zd1201_get_rts(struct net_device *dev, struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short rtst; - int err; - - err = zd1201_getconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, &rtst); - if (err) - return err; - rts->value = rtst; - rts->disabled = (rts->value == ZD1201_RTSMAX); - rts->fixed = 1; - - return 0; -} - -static int zd1201_set_frag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *frag, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - int err; - short val = frag->value; - - if (frag->disabled || !frag->fixed) - val = ZD1201_FRAGMAX; - if (val > ZD1201_FRAGMAX) - return -EINVAL; - if (val < ZD1201_FRAGMIN) - return -EINVAL; - if (val & 1) - return -EINVAL; - err = zd1201_setconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, val); - if (err) - return err; - return zd1201_mac_reset(zd); -} - -static int zd1201_get_frag(struct net_device *dev, struct iw_request_info *info, - struct iw_param *frag, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short fragt; - int err; - - err = zd1201_getconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, &fragt); - if (err) - return err; - frag->value = fragt; - frag->disabled = (frag->value == ZD1201_FRAGMAX); - frag->fixed = 1; - - return 0; -} - -static int zd1201_set_retry(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) -{ - return 0; -} - -static int zd1201_get_retry(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) -{ - return 0; -} - -static int zd1201_set_encode(struct net_device *dev, - struct iw_request_info *info, struct iw_point *erq, char *key) -{ - struct zd1201 *zd = netdev_priv(dev); - short i; - int err, rid; - - if (erq->length > ZD1201_MAXKEYLEN) - return -EINVAL; - - i = (erq->flags & IW_ENCODE_INDEX)-1; - if (i == -1) { - err = zd1201_getconfig16(zd,ZD1201_RID_CNFDEFAULTKEYID,&i); - if (err) - return err; - } else { - err = zd1201_setconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, i); - if (err) - return err; - } - - if (i < 0 || i >= ZD1201_NUMKEYS) - return -EINVAL; - - rid = ZD1201_RID_CNFDEFAULTKEY0 + i; - err = zd1201_setconfig(zd, rid, key, erq->length, 1); - if (err) - return err; - zd->encode_keylen[i] = erq->length; - memcpy(zd->encode_keys[i], key, erq->length); - - i=0; - if (!(erq->flags & IW_ENCODE_DISABLED & IW_ENCODE_MODE)) { - i |= 0x01; - zd->encode_enabled = 1; - } else - zd->encode_enabled = 0; - if (erq->flags & IW_ENCODE_RESTRICTED & IW_ENCODE_MODE) { - i |= 0x02; - zd->encode_restricted = 1; - } else - zd->encode_restricted = 0; - err = zd1201_setconfig16(zd, ZD1201_RID_CNFWEBFLAGS, i); - if (err) - return err; - - if (zd->encode_enabled) - i = ZD1201_CNFAUTHENTICATION_SHAREDKEY; - else - i = ZD1201_CNFAUTHENTICATION_OPENSYSTEM; - err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, i); - if (err) - return err; - - return zd1201_mac_reset(zd); -} - -static int zd1201_get_encode(struct net_device *dev, - struct iw_request_info *info, struct iw_point *erq, char *key) -{ - struct zd1201 *zd = netdev_priv(dev); - short i; - int err; - - if (zd->encode_enabled) - erq->flags = IW_ENCODE_ENABLED; - else - erq->flags = IW_ENCODE_DISABLED; - if (zd->encode_restricted) - erq->flags |= IW_ENCODE_RESTRICTED; - else - erq->flags |= IW_ENCODE_OPEN; - - i = (erq->flags & IW_ENCODE_INDEX) -1; - if (i == -1) { - err = zd1201_getconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, &i); - if (err) - return err; - } - if (i<0 || i>= ZD1201_NUMKEYS) - return -EINVAL; - - erq->flags |= i+1; - - erq->length = zd->encode_keylen[i]; - memcpy(key, zd->encode_keys[i], erq->length); - - return 0; -} - -static int zd1201_set_power(struct net_device *dev, - struct iw_request_info *info, struct iw_param *vwrq, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short enabled, duration, level; - int err; - - enabled = vwrq->disabled ? 0 : 1; - if (enabled) { - if (vwrq->flags & IW_POWER_PERIOD) { - duration = vwrq->value; - err = zd1201_setconfig16(zd, - ZD1201_RID_CNFMAXSLEEPDURATION, duration); - if (err) - return err; - goto out; - } - if (vwrq->flags & IW_POWER_TIMEOUT) { - err = zd1201_getconfig16(zd, - ZD1201_RID_CNFMAXSLEEPDURATION, &duration); - if (err) - return err; - level = vwrq->value * 4 / duration; - if (level > 4) - level = 4; - if (level < 0) - level = 0; - err = zd1201_setconfig16(zd, ZD1201_RID_CNFPMEPS, - level); - if (err) - return err; - goto out; - } - return -EINVAL; - } -out: - return zd1201_setconfig16(zd, ZD1201_RID_CNFPMENABLED, enabled); -} - -static int zd1201_get_power(struct net_device *dev, - struct iw_request_info *info, struct iw_param *vwrq, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short enabled, level, duration; - int err; - - err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMENABLED, &enabled); - if (err) - return err; - err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMEPS, &level); - if (err) - return err; - err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXSLEEPDURATION, &duration); - if (err) - return err; - vwrq->disabled = enabled ? 0 : 1; - if (vwrq->flags & IW_POWER_TYPE) { - if (vwrq->flags & IW_POWER_PERIOD) { - vwrq->value = duration; - vwrq->flags = IW_POWER_PERIOD; - } else { - vwrq->value = duration * level / 4; - vwrq->flags = IW_POWER_TIMEOUT; - } - } - if (vwrq->flags & IW_POWER_MODE) { - if (enabled && level) - vwrq->flags = IW_POWER_UNICAST_R; - else - vwrq->flags = IW_POWER_ALL_R; - } - - return 0; -} - - -static const iw_handler zd1201_iw_handler[] = -{ - (iw_handler) zd1201_config_commit, /* SIOCSIWCOMMIT */ - (iw_handler) zd1201_get_name, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) zd1201_set_freq, /* SIOCSIWFREQ */ - (iw_handler) zd1201_get_freq, /* SIOCGIWFREQ */ - (iw_handler) zd1201_set_mode, /* SIOCSIWMODE */ - (iw_handler) zd1201_get_mode, /* SIOCGIWMODE */ - (iw_handler) NULL, /* SIOCSIWSENS */ - (iw_handler) NULL, /* SIOCGIWSENS */ - (iw_handler) NULL, /* SIOCSIWRANGE */ - (iw_handler) zd1201_get_range, /* SIOCGIWRANGE */ - (iw_handler) NULL, /* SIOCSIWPRIV */ - (iw_handler) NULL, /* SIOCGIWPRIV */ - (iw_handler) NULL, /* SIOCSIWSTATS */ - (iw_handler) NULL, /* SIOCGIWSTATS */ - (iw_handler) NULL, /* SIOCSIWSPY */ - (iw_handler) NULL, /* SIOCGIWSPY */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL/*zd1201_set_wap*/, /* SIOCSIWAP */ - (iw_handler) zd1201_get_wap, /* SIOCGIWAP */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCGIWAPLIST */ - (iw_handler) zd1201_set_scan, /* SIOCSIWSCAN */ - (iw_handler) zd1201_get_scan, /* SIOCGIWSCAN */ - (iw_handler) zd1201_set_essid, /* SIOCSIWESSID */ - (iw_handler) zd1201_get_essid, /* SIOCGIWESSID */ - (iw_handler) NULL, /* SIOCSIWNICKN */ - (iw_handler) zd1201_get_nick, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) zd1201_set_rate, /* SIOCSIWRATE */ - (iw_handler) zd1201_get_rate, /* SIOCGIWRATE */ - (iw_handler) zd1201_set_rts, /* SIOCSIWRTS */ - (iw_handler) zd1201_get_rts, /* SIOCGIWRTS */ - (iw_handler) zd1201_set_frag, /* SIOCSIWFRAG */ - (iw_handler) zd1201_get_frag, /* SIOCGIWFRAG */ - (iw_handler) NULL, /* SIOCSIWTXPOW */ - (iw_handler) NULL, /* SIOCGIWTXPOW */ - (iw_handler) zd1201_set_retry, /* SIOCSIWRETRY */ - (iw_handler) zd1201_get_retry, /* SIOCGIWRETRY */ - (iw_handler) zd1201_set_encode, /* SIOCSIWENCODE */ - (iw_handler) zd1201_get_encode, /* SIOCGIWENCODE */ - (iw_handler) zd1201_set_power, /* SIOCSIWPOWER */ - (iw_handler) zd1201_get_power, /* SIOCGIWPOWER */ -}; - -static int zd1201_set_hostauth(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - - if (!zd->ap) - return -EOPNOTSUPP; - - return zd1201_setconfig16(zd, ZD1201_RID_CNFHOSTAUTH, rrq->value); -} - -static int zd1201_get_hostauth(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short hostauth; - int err; - - if (!zd->ap) - return -EOPNOTSUPP; - - err = zd1201_getconfig16(zd, ZD1201_RID_CNFHOSTAUTH, &hostauth); - if (err) - return err; - rrq->value = hostauth; - rrq->fixed = 1; - - return 0; -} - -static int zd1201_auth_sta(struct net_device *dev, - struct iw_request_info *info, struct sockaddr *sta, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - unsigned char buffer[10]; - - if (!zd->ap) - return -EOPNOTSUPP; - - memcpy(buffer, sta->sa_data, ETH_ALEN); - *(short*)(buffer+6) = 0; /* 0==success, 1==failure */ - *(short*)(buffer+8) = 0; - - return zd1201_setconfig(zd, ZD1201_RID_AUTHENTICATESTA, buffer, 10, 1); -} - -static int zd1201_set_maxassoc(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - int err; - - if (!zd->ap) - return -EOPNOTSUPP; - - err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, rrq->value); - if (err) - return err; - return 0; -} - -static int zd1201_get_maxassoc(struct net_device *dev, - struct iw_request_info *info, struct iw_param *rrq, char *extra) -{ - struct zd1201 *zd = netdev_priv(dev); - short maxassoc; - int err; - - if (!zd->ap) - return -EOPNOTSUPP; - - err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, &maxassoc); - if (err) - return err; - rrq->value = maxassoc; - rrq->fixed = 1; - - return 0; -} - -static const iw_handler zd1201_private_handler[] = { - (iw_handler) zd1201_set_hostauth, /* ZD1201SIWHOSTAUTH */ - (iw_handler) zd1201_get_hostauth, /* ZD1201GIWHOSTAUTH */ - (iw_handler) zd1201_auth_sta, /* ZD1201SIWAUTHSTA */ - (iw_handler) NULL, /* nothing to get */ - (iw_handler) zd1201_set_maxassoc, /* ZD1201SIMAXASSOC */ - (iw_handler) zd1201_get_maxassoc, /* ZD1201GIMAXASSOC */ -}; - -static const struct iw_priv_args zd1201_private_args[] = { - { ZD1201SIWHOSTAUTH, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - IW_PRIV_TYPE_NONE, "sethostauth" }, - { ZD1201GIWHOSTAUTH, IW_PRIV_TYPE_NONE, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostauth" }, - { ZD1201SIWAUTHSTA, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, - IW_PRIV_TYPE_NONE, "authstation" }, - { ZD1201SIWMAXASSOC, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - IW_PRIV_TYPE_NONE, "setmaxassoc" }, - { ZD1201GIWMAXASSOC, IW_PRIV_TYPE_NONE, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmaxassoc" }, -}; - -static const struct iw_handler_def zd1201_iw_handlers = { - .num_standard = ARRAY_SIZE(zd1201_iw_handler), - .num_private = ARRAY_SIZE(zd1201_private_handler), - .num_private_args = ARRAY_SIZE(zd1201_private_args), - .standard = (iw_handler *)zd1201_iw_handler, - .private = (iw_handler *)zd1201_private_handler, - .private_args = (struct iw_priv_args *) zd1201_private_args, - .get_wireless_stats = zd1201_get_wireless_stats, -}; - -static const struct net_device_ops zd1201_netdev_ops = { - .ndo_open = zd1201_net_open, - .ndo_stop = zd1201_net_stop, - .ndo_start_xmit = zd1201_hard_start_xmit, - .ndo_tx_timeout = zd1201_tx_timeout, - .ndo_set_rx_mode = zd1201_set_multicast, - .ndo_set_mac_address = zd1201_set_mac_address, - .ndo_change_mtu = eth_change_mtu, - .ndo_validate_addr = eth_validate_addr, -}; - -static int zd1201_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct zd1201 *zd; - struct net_device *dev; - struct usb_device *usb; - int err; - short porttype; - char buf[IW_ESSID_MAX_SIZE+2]; - - usb = interface_to_usbdev(interface); - - dev = alloc_etherdev(sizeof(*zd)); - if (!dev) - return -ENOMEM; - zd = netdev_priv(dev); - zd->dev = dev; - - zd->ap = ap; - zd->usb = usb; - zd->removed = 0; - init_waitqueue_head(&zd->rxdataq); - INIT_HLIST_HEAD(&zd->fraglist); - - err = zd1201_fw_upload(usb, zd->ap); - if (err) { - dev_err(&usb->dev, "zd1201 firmware upload failed: %d\n", err); - goto err_zd; - } - - zd->endp_in = 1; - zd->endp_out = 1; - zd->endp_out2 = 2; - zd->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - zd->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!zd->rx_urb || !zd->tx_urb) { - err = -ENOMEM; - goto err_zd; - } - - mdelay(100); - err = zd1201_drvr_start(zd); - if (err) - goto err_zd; - - err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXDATALEN, 2312); - if (err) - goto err_start; - - err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL, - ZD1201_RATEB1 | ZD1201_RATEB2 | ZD1201_RATEB5 | ZD1201_RATEB11); - if (err) - goto err_start; - - dev->netdev_ops = &zd1201_netdev_ops; - dev->wireless_handlers = &zd1201_iw_handlers; - dev->watchdog_timeo = ZD1201_TX_TIMEOUT; - strcpy(dev->name, "wlan%d"); - - err = zd1201_getconfig(zd, ZD1201_RID_CNFOWNMACADDR, - dev->dev_addr, dev->addr_len); - if (err) - goto err_start; - - /* Set wildcard essid to match zd->essid */ - *(__le16 *)buf = cpu_to_le16(0); - err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf, - IW_ESSID_MAX_SIZE+2, 1); - if (err) - goto err_start; - - if (zd->ap) - porttype = ZD1201_PORTTYPE_AP; - else - porttype = ZD1201_PORTTYPE_BSS; - err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype); - if (err) - goto err_start; - - SET_NETDEV_DEV(dev, &usb->dev); - - err = register_netdev(dev); - if (err) - goto err_start; - dev_info(&usb->dev, "%s: ZD1201 USB Wireless interface\n", - dev->name); - - usb_set_intfdata(interface, zd); - zd1201_enable(zd); /* zd1201 likes to startup enabled, */ - zd1201_disable(zd); /* interfering with all the wifis in range */ - return 0; - -err_start: - /* Leave the device in reset state */ - zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0); -err_zd: - usb_free_urb(zd->tx_urb); - usb_free_urb(zd->rx_urb); - free_netdev(dev); - return err; -} - -static void zd1201_disconnect(struct usb_interface *interface) -{ - struct zd1201 *zd = usb_get_intfdata(interface); - struct hlist_node *node2; - struct zd1201_frag *frag; - - if (!zd) - return; - usb_set_intfdata(interface, NULL); - - hlist_for_each_entry_safe(frag, node2, &zd->fraglist, fnode) { - hlist_del_init(&frag->fnode); - kfree_skb(frag->skb); - kfree(frag); - } - - if (zd->tx_urb) { - usb_kill_urb(zd->tx_urb); - usb_free_urb(zd->tx_urb); - } - if (zd->rx_urb) { - usb_kill_urb(zd->rx_urb); - usb_free_urb(zd->rx_urb); - } - - if (zd->dev) { - unregister_netdev(zd->dev); - free_netdev(zd->dev); - } -} - -#ifdef CONFIG_PM - -static int zd1201_suspend(struct usb_interface *interface, - pm_message_t message) -{ - struct zd1201 *zd = usb_get_intfdata(interface); - - netif_device_detach(zd->dev); - - zd->was_enabled = zd->mac_enabled; - - if (zd->was_enabled) - return zd1201_disable(zd); - else - return 0; -} - -static int zd1201_resume(struct usb_interface *interface) -{ - struct zd1201 *zd = usb_get_intfdata(interface); - - if (!zd || !zd->dev) - return -ENODEV; - - netif_device_attach(zd->dev); - - if (zd->was_enabled) - return zd1201_enable(zd); - else - return 0; -} - -#else - -#define zd1201_suspend NULL -#define zd1201_resume NULL - -#endif - -static struct usb_driver zd1201_usb = { - .name = "zd1201", - .probe = zd1201_probe, - .disconnect = zd1201_disconnect, - .id_table = zd1201_table, - .suspend = zd1201_suspend, - .resume = zd1201_resume, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(zd1201_usb); diff --git a/drivers/net/wireless/zd1201.h b/drivers/net/wireless/zd1201.h deleted file mode 100644 index dd7ea1f35bef..000000000000 --- a/drivers/net/wireless/zd1201.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * Parts of this driver have been derived from a wlan-ng version - * modified by ZyDAS. - * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. - */ - -#ifndef _INCLUDE_ZD1201_H_ -#define _INCLUDE_ZD1201_H_ - -#define ZD1201_NUMKEYS 4 -#define ZD1201_MAXKEYLEN 13 -#define ZD1201_MAXMULTI 16 -#define ZD1201_FRAGMAX 2500 -#define ZD1201_FRAGMIN 256 -#define ZD1201_RTSMAX 2500 - -#define ZD1201_RXSIZE 3000 - -struct zd1201 { - struct usb_device *usb; - int removed; - struct net_device *dev; - struct iw_statistics iwstats; - - int endp_in; - int endp_out; - int endp_out2; - struct urb *rx_urb; - struct urb *tx_urb; - - unsigned char rxdata[ZD1201_RXSIZE]; - int rxlen; - wait_queue_head_t rxdataq; - int rxdatas; - struct hlist_head fraglist; - unsigned char txdata[ZD1201_RXSIZE]; - - int ap; - char essid[IW_ESSID_MAX_SIZE+1]; - int essidlen; - int mac_enabled; - int was_enabled; - int monitor; - int encode_enabled; - int encode_restricted; - unsigned char encode_keys[ZD1201_NUMKEYS][ZD1201_MAXKEYLEN]; - int encode_keylen[ZD1201_NUMKEYS]; -}; - -struct zd1201_frag { - struct hlist_node fnode; - int seq; - struct sk_buff *skb; -}; - -#define ZD1201SIWHOSTAUTH SIOCIWFIRSTPRIV -#define ZD1201GIWHOSTAUTH ZD1201SIWHOSTAUTH+1 -#define ZD1201SIWAUTHSTA SIOCIWFIRSTPRIV+2 -#define ZD1201SIWMAXASSOC SIOCIWFIRSTPRIV+4 -#define ZD1201GIWMAXASSOC ZD1201SIWMAXASSOC+1 - -#define ZD1201_FW_TIMEOUT (1000) - -#define ZD1201_TX_TIMEOUT (2000) - -#define ZD1201_USB_CMDREQ 0 -#define ZD1201_USB_RESREQ 1 - -#define ZD1201_CMDCODE_INIT 0x00 -#define ZD1201_CMDCODE_ENABLE 0x01 -#define ZD1201_CMDCODE_DISABLE 0x02 -#define ZD1201_CMDCODE_ALLOC 0x0a -#define ZD1201_CMDCODE_INQUIRE 0x11 -#define ZD1201_CMDCODE_SETRXRID 0x17 -#define ZD1201_CMDCODE_ACCESS 0x21 - -#define ZD1201_PACKET_EVENTSTAT 0x0 -#define ZD1201_PACKET_RXDATA 0x1 -#define ZD1201_PACKET_INQUIRE 0x2 -#define ZD1201_PACKET_RESOURCE 0x3 - -#define ZD1201_ACCESSBIT 0x0100 - -#define ZD1201_RID_CNFPORTTYPE 0xfc00 -#define ZD1201_RID_CNFOWNMACADDR 0xfc01 -#define ZD1201_RID_CNFDESIREDSSID 0xfc02 -#define ZD1201_RID_CNFOWNCHANNEL 0xfc03 -#define ZD1201_RID_CNFOWNSSID 0xfc04 -#define ZD1201_RID_CNFMAXDATALEN 0xfc07 -#define ZD1201_RID_CNFPMENABLED 0xfc09 -#define ZD1201_RID_CNFPMEPS 0xfc0a -#define ZD1201_RID_CNFMAXSLEEPDURATION 0xfc0c -#define ZD1201_RID_CNFDEFAULTKEYID 0xfc23 -#define ZD1201_RID_CNFDEFAULTKEY0 0xfc24 -#define ZD1201_RID_CNFDEFAULTKEY1 0xfc25 -#define ZD1201_RID_CNFDEFAULTKEY2 0xfc26 -#define ZD1201_RID_CNFDEFAULTKEY3 0xfc27 -#define ZD1201_RID_CNFWEBFLAGS 0xfc28 -#define ZD1201_RID_CNFAUTHENTICATION 0xfc2a -#define ZD1201_RID_CNFMAXASSOCSTATIONS 0xfc2b -#define ZD1201_RID_CNFHOSTAUTH 0xfc2e -#define ZD1201_RID_CNFGROUPADDRESS 0xfc80 -#define ZD1201_RID_CNFFRAGTHRESHOLD 0xfc82 -#define ZD1201_RID_CNFRTSTHRESHOLD 0xfc83 -#define ZD1201_RID_TXRATECNTL 0xfc84 -#define ZD1201_RID_PROMISCUOUSMODE 0xfc85 -#define ZD1201_RID_CNFBASICRATES 0xfcb3 -#define ZD1201_RID_AUTHENTICATESTA 0xfce3 -#define ZD1201_RID_CURRENTBSSID 0xfd42 -#define ZD1201_RID_COMMSQUALITY 0xfd43 -#define ZD1201_RID_CURRENTTXRATE 0xfd44 -#define ZD1201_RID_CNFMAXTXBUFFERNUMBER 0xfda0 -#define ZD1201_RID_CURRENTCHANNEL 0xfdc1 - -#define ZD1201_INQ_SCANRESULTS 0xf101 - -#define ZD1201_INF_LINKSTATUS 0xf200 -#define ZD1201_INF_ASSOCSTATUS 0xf201 -#define ZD1201_INF_AUTHREQ 0xf202 - -#define ZD1201_ASSOCSTATUS_STAASSOC 0x1 -#define ZD1201_ASSOCSTATUS_REASSOC 0x2 -#define ZD1201_ASSOCSTATUS_DISASSOC 0x3 -#define ZD1201_ASSOCSTATUS_ASSOCFAIL 0x4 -#define ZD1201_ASSOCSTATUS_AUTHFAIL 0x5 - -#define ZD1201_PORTTYPE_IBSS 0 -#define ZD1201_PORTTYPE_BSS 1 -#define ZD1201_PORTTYPE_WDS 2 -#define ZD1201_PORTTYPE_PSEUDOIBSS 3 -#define ZD1201_PORTTYPE_AP 6 - -#define ZD1201_RATEB1 1 -#define ZD1201_RATEB2 2 -#define ZD1201_RATEB5 4 /* 5.5 really, but 5 is shorter :) */ -#define ZD1201_RATEB11 8 - -#define ZD1201_CNFAUTHENTICATION_OPENSYSTEM 0x0001 -#define ZD1201_CNFAUTHENTICATION_SHAREDKEY 0x0002 - -#endif /* _INCLUDE_ZD1201_H_ */ diff --git a/drivers/net/wireless/zydas/Kconfig b/drivers/net/wireless/zydas/Kconfig new file mode 100644 index 000000000000..6140dfa04fed --- /dev/null +++ b/drivers/net/wireless/zydas/Kconfig @@ -0,0 +1,33 @@ +config WLAN_VENDOR_ZYDAS + bool "ZyDAS devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_ZYDAS + +config USB_ZD1201 + tristate "USB ZD1201 based Wireless device support" + depends on CFG80211 && USB + select WIRELESS_EXT + select WEXT_PRIV + select FW_LOADER + ---help--- + Say Y if you want to use wireless LAN adapters based on the ZyDAS + ZD1201 chip. + + This driver makes the adapter appear as a normal Ethernet interface, + typically on wlan0. + + The zd1201 device requires external firmware to be loaded. + This can be found at http://linux-lc100020.sourceforge.net/ + + To compile this driver as a module, choose M here: the + module will be called zd1201. + +endif # WLAN_VENDOR_ZYDAS diff --git a/drivers/net/wireless/zydas/Makefile b/drivers/net/wireless/zydas/Makefile new file mode 100644 index 000000000000..37179ef890ea --- /dev/null +++ b/drivers/net/wireless/zydas/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_ZD1201) += zd1201.o diff --git a/drivers/net/wireless/zydas/zd1201.c b/drivers/net/wireless/zydas/zd1201.c new file mode 100644 index 000000000000..6f5c793a7855 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1201.c @@ -0,0 +1,1913 @@ +/* + * Driver for ZyDAS zd1201 based wireless USB devices. + * + * Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Parts of this driver have been derived from a wlan-ng version + * modified by ZyDAS. They also made documentation available, thanks! + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "zd1201.h" + +static struct usb_device_id zd1201_table[] = { + {USB_DEVICE(0x0586, 0x3400)}, /* Peabird Wireless USB Adapter */ + {USB_DEVICE(0x0ace, 0x1201)}, /* ZyDAS ZD1201 Wireless USB Adapter */ + {USB_DEVICE(0x050d, 0x6051)}, /* Belkin F5D6051 usb adapter */ + {USB_DEVICE(0x0db0, 0x6823)}, /* MSI UB11B usb adapter */ + {USB_DEVICE(0x1044, 0x8004)}, /* Gigabyte GN-WLBZ101 */ + {USB_DEVICE(0x1044, 0x8005)}, /* GIGABYTE GN-WLBZ201 usb adapter */ + {} +}; + +static int ap; /* Are we an AP or a normal station? */ + +#define ZD1201_VERSION "0.15" + +MODULE_AUTHOR("Jeroen Vreeken "); +MODULE_DESCRIPTION("Driver for ZyDAS ZD1201 based USB Wireless adapters"); +MODULE_VERSION(ZD1201_VERSION); +MODULE_LICENSE("GPL"); +module_param(ap, int, 0); +MODULE_PARM_DESC(ap, "If non-zero Access Point firmware will be loaded"); +MODULE_DEVICE_TABLE(usb, zd1201_table); + + +static int zd1201_fw_upload(struct usb_device *dev, int apfw) +{ + const struct firmware *fw_entry; + const char *data; + unsigned long len; + int err; + unsigned char ret; + char *buf; + char *fwfile; + + if (apfw) + fwfile = "zd1201-ap.fw"; + else + fwfile = "zd1201.fw"; + + err = request_firmware(&fw_entry, fwfile, &dev->dev); + if (err) { + dev_err(&dev->dev, "Failed to load %s firmware file!\n", fwfile); + dev_err(&dev->dev, "Make sure the hotplug firmware loader is installed.\n"); + dev_err(&dev->dev, "Goto http://linux-lc100020.sourceforge.net for more info.\n"); + return err; + } + + data = fw_entry->data; + len = fw_entry->size; + + buf = kmalloc(1024, GFP_ATOMIC); + if (!buf) { + err = -ENOMEM; + goto exit; + } + + while (len > 0) { + int translen = (len > 1024) ? 1024 : len; + memcpy(buf, data, translen); + + err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0, + USB_DIR_OUT | 0x40, 0, 0, buf, translen, + ZD1201_FW_TIMEOUT); + if (err < 0) + goto exit; + + len -= translen; + data += translen; + } + + err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x2, + USB_DIR_OUT | 0x40, 0, 0, NULL, 0, ZD1201_FW_TIMEOUT); + if (err < 0) + goto exit; + + err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4, + USB_DIR_IN | 0x40, 0, 0, buf, sizeof(ret), ZD1201_FW_TIMEOUT); + if (err < 0) + goto exit; + + memcpy(&ret, buf, sizeof(ret)); + + if (ret & 0x80) { + err = -EIO; + goto exit; + } + + err = 0; +exit: + kfree(buf); + release_firmware(fw_entry); + return err; +} + +MODULE_FIRMWARE("zd1201-ap.fw"); +MODULE_FIRMWARE("zd1201.fw"); + +static void zd1201_usbfree(struct urb *urb) +{ + struct zd1201 *zd = urb->context; + + switch(urb->status) { + case -EILSEQ: + case -ENODEV: + case -ETIME: + case -ENOENT: + case -EPIPE: + case -EOVERFLOW: + case -ESHUTDOWN: + dev_warn(&zd->usb->dev, "%s: urb failed: %d\n", + zd->dev->name, urb->status); + } + + kfree(urb->transfer_buffer); + usb_free_urb(urb); +} + +/* cmdreq message: + u32 type + u16 cmd + u16 parm0 + u16 parm1 + u16 parm2 + u8 pad[4] + + total: 4 + 2 + 2 + 2 + 2 + 4 = 16 +*/ +static int zd1201_docmd(struct zd1201 *zd, int cmd, int parm0, + int parm1, int parm2) +{ + unsigned char *command; + int ret; + struct urb *urb; + + command = kmalloc(16, GFP_ATOMIC); + if (!command) + return -ENOMEM; + + *((__le32*)command) = cpu_to_le32(ZD1201_USB_CMDREQ); + *((__le16*)&command[4]) = cpu_to_le16(cmd); + *((__le16*)&command[6]) = cpu_to_le16(parm0); + *((__le16*)&command[8]) = cpu_to_le16(parm1); + *((__le16*)&command[10])= cpu_to_le16(parm2); + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + kfree(command); + return -ENOMEM; + } + usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2), + command, 16, zd1201_usbfree, zd); + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + kfree(command); + usb_free_urb(urb); + } + + return ret; +} + +/* Callback after sending out a packet */ +static void zd1201_usbtx(struct urb *urb) +{ + struct zd1201 *zd = urb->context; + netif_wake_queue(zd->dev); +} + +/* Incoming data */ +static void zd1201_usbrx(struct urb *urb) +{ + struct zd1201 *zd = urb->context; + int free = 0; + unsigned char *data = urb->transfer_buffer; + struct sk_buff *skb; + unsigned char type; + + if (!zd) + return; + + switch(urb->status) { + case -EILSEQ: + case -ENODEV: + case -ETIME: + case -ENOENT: + case -EPIPE: + case -EOVERFLOW: + case -ESHUTDOWN: + dev_warn(&zd->usb->dev, "%s: rx urb failed: %d\n", + zd->dev->name, urb->status); + free = 1; + goto exit; + } + + if (urb->status != 0 || urb->actual_length == 0) + goto resubmit; + + type = data[0]; + if (type == ZD1201_PACKET_EVENTSTAT || type == ZD1201_PACKET_RESOURCE) { + memcpy(zd->rxdata, data, urb->actual_length); + zd->rxlen = urb->actual_length; + zd->rxdatas = 1; + wake_up(&zd->rxdataq); + } + /* Info frame */ + if (type == ZD1201_PACKET_INQUIRE) { + int i = 0; + unsigned short infotype, framelen, copylen; + framelen = le16_to_cpu(*(__le16*)&data[4]); + infotype = le16_to_cpu(*(__le16*)&data[6]); + + if (infotype == ZD1201_INF_LINKSTATUS) { + short linkstatus; + + linkstatus = le16_to_cpu(*(__le16*)&data[8]); + switch(linkstatus) { + case 1: + netif_carrier_on(zd->dev); + break; + case 2: + netif_carrier_off(zd->dev); + break; + case 3: + netif_carrier_off(zd->dev); + break; + case 4: + netif_carrier_on(zd->dev); + break; + default: + netif_carrier_off(zd->dev); + } + goto resubmit; + } + if (infotype == ZD1201_INF_ASSOCSTATUS) { + short status = le16_to_cpu(*(__le16*)(data+8)); + int event; + union iwreq_data wrqu; + + switch (status) { + case ZD1201_ASSOCSTATUS_STAASSOC: + case ZD1201_ASSOCSTATUS_REASSOC: + event = IWEVREGISTERED; + break; + case ZD1201_ASSOCSTATUS_DISASSOC: + case ZD1201_ASSOCSTATUS_ASSOCFAIL: + case ZD1201_ASSOCSTATUS_AUTHFAIL: + default: + event = IWEVEXPIRED; + } + memcpy(wrqu.addr.sa_data, data+10, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(zd->dev, event, &wrqu, NULL); + + goto resubmit; + } + if (infotype == ZD1201_INF_AUTHREQ) { + union iwreq_data wrqu; + + memcpy(wrqu.addr.sa_data, data+8, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + /* There isn't a event that trully fits this request. + We assume that userspace will be smart enough to + see a new station being expired and sends back a + authstation ioctl to authorize it. */ + wireless_send_event(zd->dev, IWEVEXPIRED, &wrqu, NULL); + goto resubmit; + } + /* Other infotypes are handled outside this handler */ + zd->rxlen = 0; + while (i < urb->actual_length) { + copylen = le16_to_cpu(*(__le16*)&data[i+2]); + /* Sanity check, sometimes we get junk */ + if (copylen+zd->rxlen > sizeof(zd->rxdata)) + break; + memcpy(zd->rxdata+zd->rxlen, data+i+4, copylen); + zd->rxlen += copylen; + i += 64; + } + if (i >= urb->actual_length) { + zd->rxdatas = 1; + wake_up(&zd->rxdataq); + } + goto resubmit; + } + /* Actual data */ + if (data[urb->actual_length-1] == ZD1201_PACKET_RXDATA) { + int datalen = urb->actual_length-1; + unsigned short len, fc, seq; + + len = ntohs(*(__be16 *)&data[datalen-2]); + if (len>datalen) + len=datalen; + fc = le16_to_cpu(*(__le16 *)&data[datalen-16]); + seq = le16_to_cpu(*(__le16 *)&data[datalen-24]); + + if (zd->monitor) { + if (datalen < 24) + goto resubmit; + if (!(skb = dev_alloc_skb(datalen+24))) + goto resubmit; + + memcpy(skb_put(skb, 2), &data[datalen-16], 2); + memcpy(skb_put(skb, 2), &data[datalen-2], 2); + memcpy(skb_put(skb, 6), &data[datalen-14], 6); + memcpy(skb_put(skb, 6), &data[datalen-22], 6); + memcpy(skb_put(skb, 6), &data[datalen-8], 6); + memcpy(skb_put(skb, 2), &data[datalen-24], 2); + memcpy(skb_put(skb, len), data, len); + skb->protocol = eth_type_trans(skb, zd->dev); + zd->dev->stats.rx_packets++; + zd->dev->stats.rx_bytes += skb->len; + netif_rx(skb); + goto resubmit; + } + + if ((seq & IEEE80211_SCTL_FRAG) || + (fc & IEEE80211_FCTL_MOREFRAGS)) { + struct zd1201_frag *frag = NULL; + char *ptr; + + if (datalen<14) + goto resubmit; + if ((seq & IEEE80211_SCTL_FRAG) == 0) { + frag = kmalloc(sizeof(*frag), GFP_ATOMIC); + if (!frag) + goto resubmit; + skb = dev_alloc_skb(IEEE80211_MAX_DATA_LEN +14+2); + if (!skb) { + kfree(frag); + goto resubmit; + } + frag->skb = skb; + frag->seq = seq & IEEE80211_SCTL_SEQ; + skb_reserve(skb, 2); + memcpy(skb_put(skb, 12), &data[datalen-14], 12); + memcpy(skb_put(skb, 2), &data[6], 2); + memcpy(skb_put(skb, len), data+8, len); + hlist_add_head(&frag->fnode, &zd->fraglist); + goto resubmit; + } + hlist_for_each_entry(frag, &zd->fraglist, fnode) + if (frag->seq == (seq&IEEE80211_SCTL_SEQ)) + break; + if (!frag) + goto resubmit; + skb = frag->skb; + ptr = skb_put(skb, len); + if (ptr) + memcpy(ptr, data+8, len); + if (fc & IEEE80211_FCTL_MOREFRAGS) + goto resubmit; + hlist_del_init(&frag->fnode); + kfree(frag); + } else { + if (datalen<14) + goto resubmit; + skb = dev_alloc_skb(len + 14 + 2); + if (!skb) + goto resubmit; + skb_reserve(skb, 2); + memcpy(skb_put(skb, 12), &data[datalen-14], 12); + memcpy(skb_put(skb, 2), &data[6], 2); + memcpy(skb_put(skb, len), data+8, len); + } + skb->protocol = eth_type_trans(skb, zd->dev); + zd->dev->stats.rx_packets++; + zd->dev->stats.rx_bytes += skb->len; + netif_rx(skb); + } +resubmit: + memset(data, 0, ZD1201_RXSIZE); + + urb->status = 0; + urb->dev = zd->usb; + if(usb_submit_urb(urb, GFP_ATOMIC)) + free = 1; + +exit: + if (free) { + zd->rxlen = 0; + zd->rxdatas = 1; + wake_up(&zd->rxdataq); + kfree(urb->transfer_buffer); + } +} + +static int zd1201_getconfig(struct zd1201 *zd, int rid, void *riddata, + unsigned int riddatalen) +{ + int err; + int i = 0; + int code; + int rid_fid; + int length; + unsigned char *pdata; + + zd->rxdatas = 0; + err = zd1201_docmd(zd, ZD1201_CMDCODE_ACCESS, rid, 0, 0); + if (err) + return err; + + wait_event_interruptible(zd->rxdataq, zd->rxdatas); + if (!zd->rxlen) + return -EIO; + + code = le16_to_cpu(*(__le16*)(&zd->rxdata[4])); + rid_fid = le16_to_cpu(*(__le16*)(&zd->rxdata[6])); + length = le16_to_cpu(*(__le16*)(&zd->rxdata[8])); + if (length > zd->rxlen) + length = zd->rxlen-6; + + /* If access bit is not on, then error */ + if ((code & ZD1201_ACCESSBIT) != ZD1201_ACCESSBIT || rid_fid != rid ) + return -EINVAL; + + /* Not enough buffer for allocating data */ + if (riddatalen != (length - 4)) { + dev_dbg(&zd->usb->dev, "riddatalen mismatches, expected=%u, (packet=%u) length=%u, rid=0x%04X, rid_fid=0x%04X\n", + riddatalen, zd->rxlen, length, rid, rid_fid); + return -ENODATA; + } + + zd->rxdatas = 0; + /* Issue SetRxRid commnd */ + err = zd1201_docmd(zd, ZD1201_CMDCODE_SETRXRID, rid, 0, length); + if (err) + return err; + + /* Receive RID record from resource packets */ + wait_event_interruptible(zd->rxdataq, zd->rxdatas); + if (!zd->rxlen) + return -EIO; + + if (zd->rxdata[zd->rxlen - 1] != ZD1201_PACKET_RESOURCE) { + dev_dbg(&zd->usb->dev, "Packet type mismatch: 0x%x not 0x3\n", + zd->rxdata[zd->rxlen-1]); + return -EINVAL; + } + + /* Set the data pointer and received data length */ + pdata = zd->rxdata; + length = zd->rxlen; + + do { + int actual_length; + + actual_length = (length > 64) ? 64 : length; + + if (pdata[0] != 0x3) { + dev_dbg(&zd->usb->dev, "Rx Resource packet type error: %02X\n", + pdata[0]); + return -EINVAL; + } + + if (actual_length != 64) { + /* Trim the last packet type byte */ + actual_length--; + } + + /* Skip the 4 bytes header (RID length and RID) */ + if (i == 0) { + pdata += 8; + actual_length -= 8; + } else { + pdata += 4; + actual_length -= 4; + } + + memcpy(riddata, pdata, actual_length); + riddata += actual_length; + pdata += actual_length; + length -= 64; + i++; + } while (length > 0); + + return 0; +} + +/* + * resreq: + * byte type + * byte sequence + * u16 reserved + * byte data[12] + * total: 16 + */ +static int zd1201_setconfig(struct zd1201 *zd, int rid, void *buf, int len, int wait) +{ + int err; + unsigned char *request; + int reqlen; + char seq=0; + struct urb *urb; + gfp_t gfp_mask = wait ? GFP_NOIO : GFP_ATOMIC; + + len += 4; /* first 4 are for header */ + + zd->rxdatas = 0; + zd->rxlen = 0; + for (seq=0; len > 0; seq++) { + request = kmalloc(16, gfp_mask); + if (!request) + return -ENOMEM; + urb = usb_alloc_urb(0, gfp_mask); + if (!urb) { + kfree(request); + return -ENOMEM; + } + memset(request, 0, 16); + reqlen = len>12 ? 12 : len; + request[0] = ZD1201_USB_RESREQ; + request[1] = seq; + request[2] = 0; + request[3] = 0; + if (request[1] == 0) { + /* add header */ + *(__le16*)&request[4] = cpu_to_le16((len-2+1)/2); + *(__le16*)&request[6] = cpu_to_le16(rid); + memcpy(request+8, buf, reqlen-4); + buf += reqlen-4; + } else { + memcpy(request+4, buf, reqlen); + buf += reqlen; + } + + len -= reqlen; + + usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, + zd->endp_out2), request, 16, zd1201_usbfree, zd); + err = usb_submit_urb(urb, gfp_mask); + if (err) + goto err; + } + + request = kmalloc(16, gfp_mask); + if (!request) + return -ENOMEM; + urb = usb_alloc_urb(0, gfp_mask); + if (!urb) { + kfree(request); + return -ENOMEM; + } + *((__le32*)request) = cpu_to_le32(ZD1201_USB_CMDREQ); + *((__le16*)&request[4]) = + cpu_to_le16(ZD1201_CMDCODE_ACCESS|ZD1201_ACCESSBIT); + *((__le16*)&request[6]) = cpu_to_le16(rid); + *((__le16*)&request[8]) = cpu_to_le16(0); + *((__le16*)&request[10]) = cpu_to_le16(0); + usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2), + request, 16, zd1201_usbfree, zd); + err = usb_submit_urb(urb, gfp_mask); + if (err) + goto err; + + if (wait) { + wait_event_interruptible(zd->rxdataq, zd->rxdatas); + if (!zd->rxlen || le16_to_cpu(*(__le16*)&zd->rxdata[6]) != rid) { + dev_dbg(&zd->usb->dev, "wrong or no RID received\n"); + } + } + + return 0; +err: + kfree(request); + usb_free_urb(urb); + return err; +} + +static inline int zd1201_getconfig16(struct zd1201 *zd, int rid, short *val) +{ + int err; + __le16 zdval; + + err = zd1201_getconfig(zd, rid, &zdval, sizeof(__le16)); + if (err) + return err; + *val = le16_to_cpu(zdval); + return 0; +} + +static inline int zd1201_setconfig16(struct zd1201 *zd, int rid, short val) +{ + __le16 zdval = cpu_to_le16(val); + return (zd1201_setconfig(zd, rid, &zdval, sizeof(__le16), 1)); +} + +static int zd1201_drvr_start(struct zd1201 *zd) +{ + int err, i; + short max; + __le16 zdmax; + unsigned char *buffer; + + buffer = kzalloc(ZD1201_RXSIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + usb_fill_bulk_urb(zd->rx_urb, zd->usb, + usb_rcvbulkpipe(zd->usb, zd->endp_in), buffer, ZD1201_RXSIZE, + zd1201_usbrx, zd); + + err = usb_submit_urb(zd->rx_urb, GFP_KERNEL); + if (err) + goto err_buffer; + + err = zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0); + if (err) + goto err_urb; + + err = zd1201_getconfig(zd, ZD1201_RID_CNFMAXTXBUFFERNUMBER, &zdmax, + sizeof(__le16)); + if (err) + goto err_urb; + + max = le16_to_cpu(zdmax); + for (i=0; irx_urb); + return err; +err_buffer: + kfree(buffer); + return err; +} + +/* Magic alert: The firmware doesn't seem to like the MAC state being + * toggled in promisc (aka monitor) mode. + * (It works a number of times, but will halt eventually) + * So we turn it of before disabling and on after enabling if needed. + */ +static int zd1201_enable(struct zd1201 *zd) +{ + int err; + + if (zd->mac_enabled) + return 0; + + err = zd1201_docmd(zd, ZD1201_CMDCODE_ENABLE, 0, 0, 0); + if (!err) + zd->mac_enabled = 1; + + if (zd->monitor) + err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 1); + + return err; +} + +static int zd1201_disable(struct zd1201 *zd) +{ + int err; + + if (!zd->mac_enabled) + return 0; + if (zd->monitor) { + err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0); + if (err) + return err; + } + + err = zd1201_docmd(zd, ZD1201_CMDCODE_DISABLE, 0, 0, 0); + if (!err) + zd->mac_enabled = 0; + return err; +} + +static int zd1201_mac_reset(struct zd1201 *zd) +{ + if (!zd->mac_enabled) + return 0; + zd1201_disable(zd); + return zd1201_enable(zd); +} + +static int zd1201_join(struct zd1201 *zd, char *essid, int essidlen) +{ + int err, val; + char buf[IW_ESSID_MAX_SIZE+2]; + + err = zd1201_disable(zd); + if (err) + return err; + + val = ZD1201_CNFAUTHENTICATION_OPENSYSTEM; + val |= ZD1201_CNFAUTHENTICATION_SHAREDKEY; + err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, val); + if (err) + return err; + + *(__le16 *)buf = cpu_to_le16(essidlen); + memcpy(buf+2, essid, essidlen); + if (!zd->ap) { /* Normal station */ + err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf, + IW_ESSID_MAX_SIZE+2, 1); + if (err) + return err; + } else { /* AP */ + err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNSSID, buf, + IW_ESSID_MAX_SIZE+2, 1); + if (err) + return err; + } + + err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR, + zd->dev->dev_addr, zd->dev->addr_len, 1); + if (err) + return err; + + err = zd1201_enable(zd); + if (err) + return err; + + msleep(100); + return 0; +} + +static int zd1201_net_open(struct net_device *dev) +{ + struct zd1201 *zd = netdev_priv(dev); + + /* Start MAC with wildcard if no essid set */ + if (!zd->mac_enabled) + zd1201_join(zd, zd->essid, zd->essidlen); + netif_start_queue(dev); + + return 0; +} + +static int zd1201_net_stop(struct net_device *dev) +{ + netif_stop_queue(dev); + return 0; +} + +/* + RFC 1042 encapsulates Ethernet frames in 802.11 frames + by prefixing them with 0xaa, 0xaa, 0x03) followed by a SNAP OID of 0 + (0x00, 0x00, 0x00). Zd requires an additional padding, copy + of ethernet addresses, length of the standard RFC 1042 packet + and a command byte (which is nul for tx). + + tx frame (from Wlan NG): + RFC 1042: + llc 0xAA 0xAA 0x03 (802.2 LLC) + snap 0x00 0x00 0x00 (Ethernet encapsulated) + type 2 bytes, Ethernet type field + payload (minus eth header) + Zydas specific: + padding 1B if (skb->len+8+1)%64==0 + Eth MAC addr 12 bytes, Ethernet MAC addresses + length 2 bytes, RFC 1042 packet length + (llc+snap+type+payload) + zd 1 null byte, zd1201 packet type + */ +static netdev_tx_t zd1201_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct zd1201 *zd = netdev_priv(dev); + unsigned char *txbuf = zd->txdata; + int txbuflen, pad = 0, err; + struct urb *urb = zd->tx_urb; + + if (!zd->mac_enabled || zd->monitor) { + dev->stats.tx_dropped++; + kfree_skb(skb); + return NETDEV_TX_OK; + } + netif_stop_queue(dev); + + txbuflen = skb->len + 8 + 1; + if (txbuflen%64 == 0) { + pad = 1; + txbuflen++; + } + txbuf[0] = 0xAA; + txbuf[1] = 0xAA; + txbuf[2] = 0x03; + txbuf[3] = 0x00; /* rfc1042 */ + txbuf[4] = 0x00; + txbuf[5] = 0x00; + + skb_copy_from_linear_data_offset(skb, 12, txbuf + 6, skb->len - 12); + if (pad) + txbuf[skb->len-12+6]=0; + skb_copy_from_linear_data(skb, txbuf + skb->len - 12 + 6 + pad, 12); + *(__be16*)&txbuf[skb->len+6+pad] = htons(skb->len-12+6); + txbuf[txbuflen-1] = 0; + + usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out), + txbuf, txbuflen, zd1201_usbtx, zd); + + err = usb_submit_urb(zd->tx_urb, GFP_ATOMIC); + if (err) { + dev->stats.tx_errors++; + netif_start_queue(dev); + } else { + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + } + kfree_skb(skb); + + return NETDEV_TX_OK; +} + +static void zd1201_tx_timeout(struct net_device *dev) +{ + struct zd1201 *zd = netdev_priv(dev); + + if (!zd) + return; + dev_warn(&zd->usb->dev, "%s: TX timeout, shooting down urb\n", + dev->name); + usb_unlink_urb(zd->tx_urb); + dev->stats.tx_errors++; + /* Restart the timeout to quiet the watchdog: */ + dev->trans_start = jiffies; /* prevent tx timeout */ +} + +static int zd1201_set_mac_address(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + struct zd1201 *zd = netdev_priv(dev); + int err; + + if (!zd) + return -ENODEV; + + err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR, + addr->sa_data, dev->addr_len, 1); + if (err) + return err; + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + return zd1201_mac_reset(zd); +} + +static struct iw_statistics *zd1201_get_wireless_stats(struct net_device *dev) +{ + struct zd1201 *zd = netdev_priv(dev); + + return &zd->iwstats; +} + +static void zd1201_set_multicast(struct net_device *dev) +{ + struct zd1201 *zd = netdev_priv(dev); + struct netdev_hw_addr *ha; + unsigned char reqbuf[ETH_ALEN*ZD1201_MAXMULTI]; + int i; + + if (netdev_mc_count(dev) > ZD1201_MAXMULTI) + return; + + i = 0; + netdev_for_each_mc_addr(ha, dev) + memcpy(reqbuf + i++ * ETH_ALEN, ha->addr, ETH_ALEN); + zd1201_setconfig(zd, ZD1201_RID_CNFGROUPADDRESS, reqbuf, + netdev_mc_count(dev) * ETH_ALEN, 0); +} + +static int zd1201_config_commit(struct net_device *dev, + struct iw_request_info *info, struct iw_point *data, char *essid) +{ + struct zd1201 *zd = netdev_priv(dev); + + return zd1201_mac_reset(zd); +} + +static int zd1201_get_name(struct net_device *dev, + struct iw_request_info *info, char *name, char *extra) +{ + strcpy(name, "IEEE 802.11b"); + return 0; +} + +static int zd1201_set_freq(struct net_device *dev, + struct iw_request_info *info, struct iw_freq *freq, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short channel = 0; + int err; + + if (freq->e == 0) + channel = freq->m; + else + channel = ieee80211_frequency_to_channel(freq->m); + + err = zd1201_setconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, channel); + if (err) + return err; + + zd1201_mac_reset(zd); + + return 0; +} + +static int zd1201_get_freq(struct net_device *dev, + struct iw_request_info *info, struct iw_freq *freq, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short channel; + int err; + + err = zd1201_getconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, &channel); + if (err) + return err; + freq->e = 0; + freq->m = channel; + + return 0; +} + +static int zd1201_set_mode(struct net_device *dev, + struct iw_request_info *info, __u32 *mode, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short porttype, monitor = 0; + unsigned char buffer[IW_ESSID_MAX_SIZE+2]; + int err; + + if (zd->ap) { + if (*mode != IW_MODE_MASTER) + return -EINVAL; + return 0; + } + + err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0); + if (err) + return err; + zd->dev->type = ARPHRD_ETHER; + switch(*mode) { + case IW_MODE_MONITOR: + monitor = 1; + zd->dev->type = ARPHRD_IEEE80211; + /* Make sure we are no longer associated with by + setting an 'impossible' essid. + (otherwise we mess up firmware) + */ + zd1201_join(zd, "\0-*#\0", 5); + /* Put port in pIBSS */ + case 8: /* No pseudo-IBSS in wireless extensions (yet) */ + porttype = ZD1201_PORTTYPE_PSEUDOIBSS; + break; + case IW_MODE_ADHOC: + porttype = ZD1201_PORTTYPE_IBSS; + break; + case IW_MODE_INFRA: + porttype = ZD1201_PORTTYPE_BSS; + break; + default: + return -EINVAL; + } + + err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype); + if (err) + return err; + if (zd->monitor && !monitor) { + zd1201_disable(zd); + *(__le16 *)buffer = cpu_to_le16(zd->essidlen); + memcpy(buffer+2, zd->essid, zd->essidlen); + err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, + buffer, IW_ESSID_MAX_SIZE+2, 1); + if (err) + return err; + } + zd->monitor = monitor; + /* If monitor mode is set we don't actually turn it on here since it + * is done during mac reset anyway (see zd1201_mac_enable). + */ + zd1201_mac_reset(zd); + + return 0; +} + +static int zd1201_get_mode(struct net_device *dev, + struct iw_request_info *info, __u32 *mode, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short porttype; + int err; + + err = zd1201_getconfig16(zd, ZD1201_RID_CNFPORTTYPE, &porttype); + if (err) + return err; + switch(porttype) { + case ZD1201_PORTTYPE_IBSS: + *mode = IW_MODE_ADHOC; + break; + case ZD1201_PORTTYPE_BSS: + *mode = IW_MODE_INFRA; + break; + case ZD1201_PORTTYPE_WDS: + *mode = IW_MODE_REPEAT; + break; + case ZD1201_PORTTYPE_PSEUDOIBSS: + *mode = 8;/* No Pseudo-IBSS... */ + break; + case ZD1201_PORTTYPE_AP: + *mode = IW_MODE_MASTER; + break; + default: + dev_dbg(&zd->usb->dev, "Unknown porttype: %d\n", + porttype); + *mode = IW_MODE_AUTO; + } + if (zd->monitor) + *mode = IW_MODE_MONITOR; + + return 0; +} + +static int zd1201_get_range(struct net_device *dev, + struct iw_request_info *info, struct iw_point *wrq, char *extra) +{ + struct iw_range *range = (struct iw_range *)extra; + + wrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = WIRELESS_EXT; + + range->max_qual.qual = 128; + range->max_qual.level = 128; + range->max_qual.noise = 128; + range->max_qual.updated = 7; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = ZD1201_NUMKEYS; + + range->num_bitrates = 4; + range->bitrate[0] = 1000000; + range->bitrate[1] = 2000000; + range->bitrate[2] = 5500000; + range->bitrate[3] = 11000000; + + range->min_rts = 0; + range->min_frag = ZD1201_FRAGMIN; + range->max_rts = ZD1201_RTSMAX; + range->min_frag = ZD1201_FRAGMAX; + + return 0; +} + +/* Little bit of magic here: we only get the quality if we poll + * for it, and we never get an actual request to trigger such + * a poll. Therefore we 'assume' that the user will soon ask for + * the stats after asking the bssid. + */ +static int zd1201_get_wap(struct net_device *dev, + struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + unsigned char buffer[6]; + + if (!zd1201_getconfig(zd, ZD1201_RID_COMMSQUALITY, buffer, 6)) { + /* Unfortunately the quality and noise reported is useless. + they seem to be accumulators that increase until you + read them, unless we poll on a fixed interval we can't + use them + */ + /*zd->iwstats.qual.qual = le16_to_cpu(((__le16 *)buffer)[0]);*/ + zd->iwstats.qual.level = le16_to_cpu(((__le16 *)buffer)[1]); + /*zd->iwstats.qual.noise = le16_to_cpu(((__le16 *)buffer)[2]);*/ + zd->iwstats.qual.updated = 2; + } + + return zd1201_getconfig(zd, ZD1201_RID_CURRENTBSSID, ap_addr->sa_data, 6); +} + +static int zd1201_set_scan(struct net_device *dev, + struct iw_request_info *info, struct iw_point *srq, char *extra) +{ + /* We do everything in get_scan */ + return 0; +} + +static int zd1201_get_scan(struct net_device *dev, + struct iw_request_info *info, struct iw_point *srq, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + int err, i, j, enabled_save; + struct iw_event iwe; + char *cev = extra; + char *end_buf = extra + IW_SCAN_MAX_DATA; + + /* No scanning in AP mode */ + if (zd->ap) + return -EOPNOTSUPP; + + /* Scan doesn't seem to work if disabled */ + enabled_save = zd->mac_enabled; + zd1201_enable(zd); + + zd->rxdatas = 0; + err = zd1201_docmd(zd, ZD1201_CMDCODE_INQUIRE, + ZD1201_INQ_SCANRESULTS, 0, 0); + if (err) + return err; + + wait_event_interruptible(zd->rxdataq, zd->rxdatas); + if (!zd->rxlen) + return -EIO; + + if (le16_to_cpu(*(__le16*)&zd->rxdata[2]) != ZD1201_INQ_SCANRESULTS) + return -EIO; + + for(i=8; irxlen; i+=62) { + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, zd->rxdata+i+6, 6); + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_ADDR_LEN); + + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = zd->rxdata[i+16]; + iwe.u.data.flags = 1; + cev = iwe_stream_add_point(info, cev, end_buf, + &iwe, zd->rxdata+i+18); + + iwe.cmd = SIOCGIWMODE; + if (zd->rxdata[i+14]&0x01) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_UINT_LEN); + + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = zd->rxdata[i+0]; + iwe.u.freq.e = 0; + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_FREQ_LEN); + + iwe.cmd = SIOCGIWRATE; + iwe.u.bitrate.fixed = 0; + iwe.u.bitrate.disabled = 0; + for (j=0; j<10; j++) if (zd->rxdata[i+50+j]) { + iwe.u.bitrate.value = (zd->rxdata[i+50+j]&0x7f)*500000; + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_PARAM_LEN); + } + + iwe.cmd = SIOCGIWENCODE; + iwe.u.data.length = 0; + if (zd->rxdata[i+14]&0x10) + iwe.u.data.flags = IW_ENCODE_ENABLED; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL); + + iwe.cmd = IWEVQUAL; + iwe.u.qual.qual = zd->rxdata[i+4]; + iwe.u.qual.noise= zd->rxdata[i+2]/10-100; + iwe.u.qual.level = (256+zd->rxdata[i+4]*100)/255-100; + iwe.u.qual.updated = 7; + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_QUAL_LEN); + } + + if (!enabled_save) + zd1201_disable(zd); + + srq->length = cev - extra; + srq->flags = 0; + + return 0; +} + +static int zd1201_set_essid(struct net_device *dev, + struct iw_request_info *info, struct iw_point *data, char *essid) +{ + struct zd1201 *zd = netdev_priv(dev); + + if (data->length > IW_ESSID_MAX_SIZE) + return -EINVAL; + if (data->length < 1) + data->length = 1; + zd->essidlen = data->length; + memset(zd->essid, 0, IW_ESSID_MAX_SIZE+1); + memcpy(zd->essid, essid, data->length); + return zd1201_join(zd, zd->essid, zd->essidlen); +} + +static int zd1201_get_essid(struct net_device *dev, + struct iw_request_info *info, struct iw_point *data, char *essid) +{ + struct zd1201 *zd = netdev_priv(dev); + + memcpy(essid, zd->essid, zd->essidlen); + data->flags = 1; + data->length = zd->essidlen; + + return 0; +} + +static int zd1201_get_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *data, char *nick) +{ + strcpy(nick, "zd1201"); + data->flags = 1; + data->length = strlen(nick); + return 0; +} + +static int zd1201_set_rate(struct net_device *dev, + struct iw_request_info *info, struct iw_param *rrq, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short rate; + int err; + + switch (rrq->value) { + case 1000000: + rate = ZD1201_RATEB1; + break; + case 2000000: + rate = ZD1201_RATEB2; + break; + case 5500000: + rate = ZD1201_RATEB5; + break; + case 11000000: + default: + rate = ZD1201_RATEB11; + break; + } + if (!rrq->fixed) { /* Also enable all lower bitrates */ + rate |= rate-1; + } + + err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL, rate); + if (err) + return err; + + return zd1201_mac_reset(zd); +} + +static int zd1201_get_rate(struct net_device *dev, + struct iw_request_info *info, struct iw_param *rrq, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short rate; + int err; + + err = zd1201_getconfig16(zd, ZD1201_RID_CURRENTTXRATE, &rate); + if (err) + return err; + + switch(rate) { + case 1: + rrq->value = 1000000; + break; + case 2: + rrq->value = 2000000; + break; + case 5: + rrq->value = 5500000; + break; + case 11: + rrq->value = 11000000; + break; + default: + rrq->value = 0; + } + rrq->fixed = 0; + rrq->disabled = 0; + + return 0; +} + +static int zd1201_set_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + int err; + short val = rts->value; + + if (rts->disabled || !rts->fixed) + val = ZD1201_RTSMAX; + if (val > ZD1201_RTSMAX) + return -EINVAL; + if (val < 0) + return -EINVAL; + + err = zd1201_setconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, val); + if (err) + return err; + return zd1201_mac_reset(zd); +} + +static int zd1201_get_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short rtst; + int err; + + err = zd1201_getconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, &rtst); + if (err) + return err; + rts->value = rtst; + rts->disabled = (rts->value == ZD1201_RTSMAX); + rts->fixed = 1; + + return 0; +} + +static int zd1201_set_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *frag, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + int err; + short val = frag->value; + + if (frag->disabled || !frag->fixed) + val = ZD1201_FRAGMAX; + if (val > ZD1201_FRAGMAX) + return -EINVAL; + if (val < ZD1201_FRAGMIN) + return -EINVAL; + if (val & 1) + return -EINVAL; + err = zd1201_setconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, val); + if (err) + return err; + return zd1201_mac_reset(zd); +} + +static int zd1201_get_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *frag, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short fragt; + int err; + + err = zd1201_getconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, &fragt); + if (err) + return err; + frag->value = fragt; + frag->disabled = (frag->value == ZD1201_FRAGMAX); + frag->fixed = 1; + + return 0; +} + +static int zd1201_set_retry(struct net_device *dev, + struct iw_request_info *info, struct iw_param *rrq, char *extra) +{ + return 0; +} + +static int zd1201_get_retry(struct net_device *dev, + struct iw_request_info *info, struct iw_param *rrq, char *extra) +{ + return 0; +} + +static int zd1201_set_encode(struct net_device *dev, + struct iw_request_info *info, struct iw_point *erq, char *key) +{ + struct zd1201 *zd = netdev_priv(dev); + short i; + int err, rid; + + if (erq->length > ZD1201_MAXKEYLEN) + return -EINVAL; + + i = (erq->flags & IW_ENCODE_INDEX)-1; + if (i == -1) { + err = zd1201_getconfig16(zd,ZD1201_RID_CNFDEFAULTKEYID,&i); + if (err) + return err; + } else { + err = zd1201_setconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, i); + if (err) + return err; + } + + if (i < 0 || i >= ZD1201_NUMKEYS) + return -EINVAL; + + rid = ZD1201_RID_CNFDEFAULTKEY0 + i; + err = zd1201_setconfig(zd, rid, key, erq->length, 1); + if (err) + return err; + zd->encode_keylen[i] = erq->length; + memcpy(zd->encode_keys[i], key, erq->length); + + i=0; + if (!(erq->flags & IW_ENCODE_DISABLED & IW_ENCODE_MODE)) { + i |= 0x01; + zd->encode_enabled = 1; + } else + zd->encode_enabled = 0; + if (erq->flags & IW_ENCODE_RESTRICTED & IW_ENCODE_MODE) { + i |= 0x02; + zd->encode_restricted = 1; + } else + zd->encode_restricted = 0; + err = zd1201_setconfig16(zd, ZD1201_RID_CNFWEBFLAGS, i); + if (err) + return err; + + if (zd->encode_enabled) + i = ZD1201_CNFAUTHENTICATION_SHAREDKEY; + else + i = ZD1201_CNFAUTHENTICATION_OPENSYSTEM; + err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, i); + if (err) + return err; + + return zd1201_mac_reset(zd); +} + +static int zd1201_get_encode(struct net_device *dev, + struct iw_request_info *info, struct iw_point *erq, char *key) +{ + struct zd1201 *zd = netdev_priv(dev); + short i; + int err; + + if (zd->encode_enabled) + erq->flags = IW_ENCODE_ENABLED; + else + erq->flags = IW_ENCODE_DISABLED; + if (zd->encode_restricted) + erq->flags |= IW_ENCODE_RESTRICTED; + else + erq->flags |= IW_ENCODE_OPEN; + + i = (erq->flags & IW_ENCODE_INDEX) -1; + if (i == -1) { + err = zd1201_getconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, &i); + if (err) + return err; + } + if (i<0 || i>= ZD1201_NUMKEYS) + return -EINVAL; + + erq->flags |= i+1; + + erq->length = zd->encode_keylen[i]; + memcpy(key, zd->encode_keys[i], erq->length); + + return 0; +} + +static int zd1201_set_power(struct net_device *dev, + struct iw_request_info *info, struct iw_param *vwrq, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short enabled, duration, level; + int err; + + enabled = vwrq->disabled ? 0 : 1; + if (enabled) { + if (vwrq->flags & IW_POWER_PERIOD) { + duration = vwrq->value; + err = zd1201_setconfig16(zd, + ZD1201_RID_CNFMAXSLEEPDURATION, duration); + if (err) + return err; + goto out; + } + if (vwrq->flags & IW_POWER_TIMEOUT) { + err = zd1201_getconfig16(zd, + ZD1201_RID_CNFMAXSLEEPDURATION, &duration); + if (err) + return err; + level = vwrq->value * 4 / duration; + if (level > 4) + level = 4; + if (level < 0) + level = 0; + err = zd1201_setconfig16(zd, ZD1201_RID_CNFPMEPS, + level); + if (err) + return err; + goto out; + } + return -EINVAL; + } +out: + return zd1201_setconfig16(zd, ZD1201_RID_CNFPMENABLED, enabled); +} + +static int zd1201_get_power(struct net_device *dev, + struct iw_request_info *info, struct iw_param *vwrq, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short enabled, level, duration; + int err; + + err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMENABLED, &enabled); + if (err) + return err; + err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMEPS, &level); + if (err) + return err; + err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXSLEEPDURATION, &duration); + if (err) + return err; + vwrq->disabled = enabled ? 0 : 1; + if (vwrq->flags & IW_POWER_TYPE) { + if (vwrq->flags & IW_POWER_PERIOD) { + vwrq->value = duration; + vwrq->flags = IW_POWER_PERIOD; + } else { + vwrq->value = duration * level / 4; + vwrq->flags = IW_POWER_TIMEOUT; + } + } + if (vwrq->flags & IW_POWER_MODE) { + if (enabled && level) + vwrq->flags = IW_POWER_UNICAST_R; + else + vwrq->flags = IW_POWER_ALL_R; + } + + return 0; +} + + +static const iw_handler zd1201_iw_handler[] = +{ + (iw_handler) zd1201_config_commit, /* SIOCSIWCOMMIT */ + (iw_handler) zd1201_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) zd1201_set_freq, /* SIOCSIWFREQ */ + (iw_handler) zd1201_get_freq, /* SIOCGIWFREQ */ + (iw_handler) zd1201_set_mode, /* SIOCSIWMODE */ + (iw_handler) zd1201_get_mode, /* SIOCGIWMODE */ + (iw_handler) NULL, /* SIOCSIWSENS */ + (iw_handler) NULL, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) zd1201_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL/*zd1201_set_wap*/, /* SIOCSIWAP */ + (iw_handler) zd1201_get_wap, /* SIOCGIWAP */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* SIOCGIWAPLIST */ + (iw_handler) zd1201_set_scan, /* SIOCSIWSCAN */ + (iw_handler) zd1201_get_scan, /* SIOCGIWSCAN */ + (iw_handler) zd1201_set_essid, /* SIOCSIWESSID */ + (iw_handler) zd1201_get_essid, /* SIOCGIWESSID */ + (iw_handler) NULL, /* SIOCSIWNICKN */ + (iw_handler) zd1201_get_nick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) zd1201_set_rate, /* SIOCSIWRATE */ + (iw_handler) zd1201_get_rate, /* SIOCGIWRATE */ + (iw_handler) zd1201_set_rts, /* SIOCSIWRTS */ + (iw_handler) zd1201_get_rts, /* SIOCGIWRTS */ + (iw_handler) zd1201_set_frag, /* SIOCSIWFRAG */ + (iw_handler) zd1201_get_frag, /* SIOCGIWFRAG */ + (iw_handler) NULL, /* SIOCSIWTXPOW */ + (iw_handler) NULL, /* SIOCGIWTXPOW */ + (iw_handler) zd1201_set_retry, /* SIOCSIWRETRY */ + (iw_handler) zd1201_get_retry, /* SIOCGIWRETRY */ + (iw_handler) zd1201_set_encode, /* SIOCSIWENCODE */ + (iw_handler) zd1201_get_encode, /* SIOCGIWENCODE */ + (iw_handler) zd1201_set_power, /* SIOCSIWPOWER */ + (iw_handler) zd1201_get_power, /* SIOCGIWPOWER */ +}; + +static int zd1201_set_hostauth(struct net_device *dev, + struct iw_request_info *info, struct iw_param *rrq, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + + if (!zd->ap) + return -EOPNOTSUPP; + + return zd1201_setconfig16(zd, ZD1201_RID_CNFHOSTAUTH, rrq->value); +} + +static int zd1201_get_hostauth(struct net_device *dev, + struct iw_request_info *info, struct iw_param *rrq, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short hostauth; + int err; + + if (!zd->ap) + return -EOPNOTSUPP; + + err = zd1201_getconfig16(zd, ZD1201_RID_CNFHOSTAUTH, &hostauth); + if (err) + return err; + rrq->value = hostauth; + rrq->fixed = 1; + + return 0; +} + +static int zd1201_auth_sta(struct net_device *dev, + struct iw_request_info *info, struct sockaddr *sta, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + unsigned char buffer[10]; + + if (!zd->ap) + return -EOPNOTSUPP; + + memcpy(buffer, sta->sa_data, ETH_ALEN); + *(short*)(buffer+6) = 0; /* 0==success, 1==failure */ + *(short*)(buffer+8) = 0; + + return zd1201_setconfig(zd, ZD1201_RID_AUTHENTICATESTA, buffer, 10, 1); +} + +static int zd1201_set_maxassoc(struct net_device *dev, + struct iw_request_info *info, struct iw_param *rrq, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + int err; + + if (!zd->ap) + return -EOPNOTSUPP; + + err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, rrq->value); + if (err) + return err; + return 0; +} + +static int zd1201_get_maxassoc(struct net_device *dev, + struct iw_request_info *info, struct iw_param *rrq, char *extra) +{ + struct zd1201 *zd = netdev_priv(dev); + short maxassoc; + int err; + + if (!zd->ap) + return -EOPNOTSUPP; + + err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, &maxassoc); + if (err) + return err; + rrq->value = maxassoc; + rrq->fixed = 1; + + return 0; +} + +static const iw_handler zd1201_private_handler[] = { + (iw_handler) zd1201_set_hostauth, /* ZD1201SIWHOSTAUTH */ + (iw_handler) zd1201_get_hostauth, /* ZD1201GIWHOSTAUTH */ + (iw_handler) zd1201_auth_sta, /* ZD1201SIWAUTHSTA */ + (iw_handler) NULL, /* nothing to get */ + (iw_handler) zd1201_set_maxassoc, /* ZD1201SIMAXASSOC */ + (iw_handler) zd1201_get_maxassoc, /* ZD1201GIMAXASSOC */ +}; + +static const struct iw_priv_args zd1201_private_args[] = { + { ZD1201SIWHOSTAUTH, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, "sethostauth" }, + { ZD1201GIWHOSTAUTH, IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostauth" }, + { ZD1201SIWAUTHSTA, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, "authstation" }, + { ZD1201SIWMAXASSOC, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, "setmaxassoc" }, + { ZD1201GIWMAXASSOC, IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmaxassoc" }, +}; + +static const struct iw_handler_def zd1201_iw_handlers = { + .num_standard = ARRAY_SIZE(zd1201_iw_handler), + .num_private = ARRAY_SIZE(zd1201_private_handler), + .num_private_args = ARRAY_SIZE(zd1201_private_args), + .standard = (iw_handler *)zd1201_iw_handler, + .private = (iw_handler *)zd1201_private_handler, + .private_args = (struct iw_priv_args *) zd1201_private_args, + .get_wireless_stats = zd1201_get_wireless_stats, +}; + +static const struct net_device_ops zd1201_netdev_ops = { + .ndo_open = zd1201_net_open, + .ndo_stop = zd1201_net_stop, + .ndo_start_xmit = zd1201_hard_start_xmit, + .ndo_tx_timeout = zd1201_tx_timeout, + .ndo_set_rx_mode = zd1201_set_multicast, + .ndo_set_mac_address = zd1201_set_mac_address, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +static int zd1201_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct zd1201 *zd; + struct net_device *dev; + struct usb_device *usb; + int err; + short porttype; + char buf[IW_ESSID_MAX_SIZE+2]; + + usb = interface_to_usbdev(interface); + + dev = alloc_etherdev(sizeof(*zd)); + if (!dev) + return -ENOMEM; + zd = netdev_priv(dev); + zd->dev = dev; + + zd->ap = ap; + zd->usb = usb; + zd->removed = 0; + init_waitqueue_head(&zd->rxdataq); + INIT_HLIST_HEAD(&zd->fraglist); + + err = zd1201_fw_upload(usb, zd->ap); + if (err) { + dev_err(&usb->dev, "zd1201 firmware upload failed: %d\n", err); + goto err_zd; + } + + zd->endp_in = 1; + zd->endp_out = 1; + zd->endp_out2 = 2; + zd->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + zd->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!zd->rx_urb || !zd->tx_urb) { + err = -ENOMEM; + goto err_zd; + } + + mdelay(100); + err = zd1201_drvr_start(zd); + if (err) + goto err_zd; + + err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXDATALEN, 2312); + if (err) + goto err_start; + + err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL, + ZD1201_RATEB1 | ZD1201_RATEB2 | ZD1201_RATEB5 | ZD1201_RATEB11); + if (err) + goto err_start; + + dev->netdev_ops = &zd1201_netdev_ops; + dev->wireless_handlers = &zd1201_iw_handlers; + dev->watchdog_timeo = ZD1201_TX_TIMEOUT; + strcpy(dev->name, "wlan%d"); + + err = zd1201_getconfig(zd, ZD1201_RID_CNFOWNMACADDR, + dev->dev_addr, dev->addr_len); + if (err) + goto err_start; + + /* Set wildcard essid to match zd->essid */ + *(__le16 *)buf = cpu_to_le16(0); + err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf, + IW_ESSID_MAX_SIZE+2, 1); + if (err) + goto err_start; + + if (zd->ap) + porttype = ZD1201_PORTTYPE_AP; + else + porttype = ZD1201_PORTTYPE_BSS; + err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype); + if (err) + goto err_start; + + SET_NETDEV_DEV(dev, &usb->dev); + + err = register_netdev(dev); + if (err) + goto err_start; + dev_info(&usb->dev, "%s: ZD1201 USB Wireless interface\n", + dev->name); + + usb_set_intfdata(interface, zd); + zd1201_enable(zd); /* zd1201 likes to startup enabled, */ + zd1201_disable(zd); /* interfering with all the wifis in range */ + return 0; + +err_start: + /* Leave the device in reset state */ + zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0); +err_zd: + usb_free_urb(zd->tx_urb); + usb_free_urb(zd->rx_urb); + free_netdev(dev); + return err; +} + +static void zd1201_disconnect(struct usb_interface *interface) +{ + struct zd1201 *zd = usb_get_intfdata(interface); + struct hlist_node *node2; + struct zd1201_frag *frag; + + if (!zd) + return; + usb_set_intfdata(interface, NULL); + + hlist_for_each_entry_safe(frag, node2, &zd->fraglist, fnode) { + hlist_del_init(&frag->fnode); + kfree_skb(frag->skb); + kfree(frag); + } + + if (zd->tx_urb) { + usb_kill_urb(zd->tx_urb); + usb_free_urb(zd->tx_urb); + } + if (zd->rx_urb) { + usb_kill_urb(zd->rx_urb); + usb_free_urb(zd->rx_urb); + } + + if (zd->dev) { + unregister_netdev(zd->dev); + free_netdev(zd->dev); + } +} + +#ifdef CONFIG_PM + +static int zd1201_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct zd1201 *zd = usb_get_intfdata(interface); + + netif_device_detach(zd->dev); + + zd->was_enabled = zd->mac_enabled; + + if (zd->was_enabled) + return zd1201_disable(zd); + else + return 0; +} + +static int zd1201_resume(struct usb_interface *interface) +{ + struct zd1201 *zd = usb_get_intfdata(interface); + + if (!zd || !zd->dev) + return -ENODEV; + + netif_device_attach(zd->dev); + + if (zd->was_enabled) + return zd1201_enable(zd); + else + return 0; +} + +#else + +#define zd1201_suspend NULL +#define zd1201_resume NULL + +#endif + +static struct usb_driver zd1201_usb = { + .name = "zd1201", + .probe = zd1201_probe, + .disconnect = zd1201_disconnect, + .id_table = zd1201_table, + .suspend = zd1201_suspend, + .resume = zd1201_resume, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(zd1201_usb); diff --git a/drivers/net/wireless/zydas/zd1201.h b/drivers/net/wireless/zydas/zd1201.h new file mode 100644 index 000000000000..dd7ea1f35bef --- /dev/null +++ b/drivers/net/wireless/zydas/zd1201.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Parts of this driver have been derived from a wlan-ng version + * modified by ZyDAS. + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + */ + +#ifndef _INCLUDE_ZD1201_H_ +#define _INCLUDE_ZD1201_H_ + +#define ZD1201_NUMKEYS 4 +#define ZD1201_MAXKEYLEN 13 +#define ZD1201_MAXMULTI 16 +#define ZD1201_FRAGMAX 2500 +#define ZD1201_FRAGMIN 256 +#define ZD1201_RTSMAX 2500 + +#define ZD1201_RXSIZE 3000 + +struct zd1201 { + struct usb_device *usb; + int removed; + struct net_device *dev; + struct iw_statistics iwstats; + + int endp_in; + int endp_out; + int endp_out2; + struct urb *rx_urb; + struct urb *tx_urb; + + unsigned char rxdata[ZD1201_RXSIZE]; + int rxlen; + wait_queue_head_t rxdataq; + int rxdatas; + struct hlist_head fraglist; + unsigned char txdata[ZD1201_RXSIZE]; + + int ap; + char essid[IW_ESSID_MAX_SIZE+1]; + int essidlen; + int mac_enabled; + int was_enabled; + int monitor; + int encode_enabled; + int encode_restricted; + unsigned char encode_keys[ZD1201_NUMKEYS][ZD1201_MAXKEYLEN]; + int encode_keylen[ZD1201_NUMKEYS]; +}; + +struct zd1201_frag { + struct hlist_node fnode; + int seq; + struct sk_buff *skb; +}; + +#define ZD1201SIWHOSTAUTH SIOCIWFIRSTPRIV +#define ZD1201GIWHOSTAUTH ZD1201SIWHOSTAUTH+1 +#define ZD1201SIWAUTHSTA SIOCIWFIRSTPRIV+2 +#define ZD1201SIWMAXASSOC SIOCIWFIRSTPRIV+4 +#define ZD1201GIWMAXASSOC ZD1201SIWMAXASSOC+1 + +#define ZD1201_FW_TIMEOUT (1000) + +#define ZD1201_TX_TIMEOUT (2000) + +#define ZD1201_USB_CMDREQ 0 +#define ZD1201_USB_RESREQ 1 + +#define ZD1201_CMDCODE_INIT 0x00 +#define ZD1201_CMDCODE_ENABLE 0x01 +#define ZD1201_CMDCODE_DISABLE 0x02 +#define ZD1201_CMDCODE_ALLOC 0x0a +#define ZD1201_CMDCODE_INQUIRE 0x11 +#define ZD1201_CMDCODE_SETRXRID 0x17 +#define ZD1201_CMDCODE_ACCESS 0x21 + +#define ZD1201_PACKET_EVENTSTAT 0x0 +#define ZD1201_PACKET_RXDATA 0x1 +#define ZD1201_PACKET_INQUIRE 0x2 +#define ZD1201_PACKET_RESOURCE 0x3 + +#define ZD1201_ACCESSBIT 0x0100 + +#define ZD1201_RID_CNFPORTTYPE 0xfc00 +#define ZD1201_RID_CNFOWNMACADDR 0xfc01 +#define ZD1201_RID_CNFDESIREDSSID 0xfc02 +#define ZD1201_RID_CNFOWNCHANNEL 0xfc03 +#define ZD1201_RID_CNFOWNSSID 0xfc04 +#define ZD1201_RID_CNFMAXDATALEN 0xfc07 +#define ZD1201_RID_CNFPMENABLED 0xfc09 +#define ZD1201_RID_CNFPMEPS 0xfc0a +#define ZD1201_RID_CNFMAXSLEEPDURATION 0xfc0c +#define ZD1201_RID_CNFDEFAULTKEYID 0xfc23 +#define ZD1201_RID_CNFDEFAULTKEY0 0xfc24 +#define ZD1201_RID_CNFDEFAULTKEY1 0xfc25 +#define ZD1201_RID_CNFDEFAULTKEY2 0xfc26 +#define ZD1201_RID_CNFDEFAULTKEY3 0xfc27 +#define ZD1201_RID_CNFWEBFLAGS 0xfc28 +#define ZD1201_RID_CNFAUTHENTICATION 0xfc2a +#define ZD1201_RID_CNFMAXASSOCSTATIONS 0xfc2b +#define ZD1201_RID_CNFHOSTAUTH 0xfc2e +#define ZD1201_RID_CNFGROUPADDRESS 0xfc80 +#define ZD1201_RID_CNFFRAGTHRESHOLD 0xfc82 +#define ZD1201_RID_CNFRTSTHRESHOLD 0xfc83 +#define ZD1201_RID_TXRATECNTL 0xfc84 +#define ZD1201_RID_PROMISCUOUSMODE 0xfc85 +#define ZD1201_RID_CNFBASICRATES 0xfcb3 +#define ZD1201_RID_AUTHENTICATESTA 0xfce3 +#define ZD1201_RID_CURRENTBSSID 0xfd42 +#define ZD1201_RID_COMMSQUALITY 0xfd43 +#define ZD1201_RID_CURRENTTXRATE 0xfd44 +#define ZD1201_RID_CNFMAXTXBUFFERNUMBER 0xfda0 +#define ZD1201_RID_CURRENTCHANNEL 0xfdc1 + +#define ZD1201_INQ_SCANRESULTS 0xf101 + +#define ZD1201_INF_LINKSTATUS 0xf200 +#define ZD1201_INF_ASSOCSTATUS 0xf201 +#define ZD1201_INF_AUTHREQ 0xf202 + +#define ZD1201_ASSOCSTATUS_STAASSOC 0x1 +#define ZD1201_ASSOCSTATUS_REASSOC 0x2 +#define ZD1201_ASSOCSTATUS_DISASSOC 0x3 +#define ZD1201_ASSOCSTATUS_ASSOCFAIL 0x4 +#define ZD1201_ASSOCSTATUS_AUTHFAIL 0x5 + +#define ZD1201_PORTTYPE_IBSS 0 +#define ZD1201_PORTTYPE_BSS 1 +#define ZD1201_PORTTYPE_WDS 2 +#define ZD1201_PORTTYPE_PSEUDOIBSS 3 +#define ZD1201_PORTTYPE_AP 6 + +#define ZD1201_RATEB1 1 +#define ZD1201_RATEB2 2 +#define ZD1201_RATEB5 4 /* 5.5 really, but 5 is shorter :) */ +#define ZD1201_RATEB11 8 + +#define ZD1201_CNFAUTHENTICATION_OPENSYSTEM 0x0001 +#define ZD1201_CNFAUTHENTICATION_SHAREDKEY 0x0002 + +#endif /* _INCLUDE_ZD1201_H_ */ -- cgit v1.2.3-59-g8ed1b From 6948300c79dba2b6c7b54af43d1924f51e47e017 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 09:27:32 +0200 Subject: zd1211rw: move under zydas vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 1 - drivers/net/wireless/Makefile | 1 - drivers/net/wireless/zd1211rw/Kconfig | 19 - drivers/net/wireless/zd1211rw/Makefile | 9 - drivers/net/wireless/zd1211rw/zd_chip.c | 1560 --------------- drivers/net/wireless/zd1211rw/zd_chip.h | 983 ---------- drivers/net/wireless/zd1211rw/zd_def.h | 69 - drivers/net/wireless/zd1211rw/zd_mac.c | 1550 --------------- drivers/net/wireless/zd1211rw/zd_mac.h | 327 ---- drivers/net/wireless/zd1211rw/zd_rf.c | 181 -- drivers/net/wireless/zd1211rw/zd_rf.h | 110 -- drivers/net/wireless/zd1211rw/zd_rf_al2230.c | 443 ----- drivers/net/wireless/zd1211rw/zd_rf_al7230b.c | 494 ----- drivers/net/wireless/zd1211rw/zd_rf_rf2959.c | 281 --- drivers/net/wireless/zd1211rw/zd_rf_uw2453.c | 539 ----- drivers/net/wireless/zd1211rw/zd_usb.c | 2060 -------------------- drivers/net/wireless/zd1211rw/zd_usb.h | 292 --- drivers/net/wireless/zydas/Kconfig | 2 + drivers/net/wireless/zydas/Makefile | 2 + drivers/net/wireless/zydas/zd1211rw/Kconfig | 19 + drivers/net/wireless/zydas/zd1211rw/Makefile | 9 + drivers/net/wireless/zydas/zd1211rw/zd_chip.c | 1560 +++++++++++++++ drivers/net/wireless/zydas/zd1211rw/zd_chip.h | 983 ++++++++++ drivers/net/wireless/zydas/zd1211rw/zd_def.h | 69 + drivers/net/wireless/zydas/zd1211rw/zd_mac.c | 1550 +++++++++++++++ drivers/net/wireless/zydas/zd1211rw/zd_mac.h | 327 ++++ drivers/net/wireless/zydas/zd1211rw/zd_rf.c | 181 ++ drivers/net/wireless/zydas/zd1211rw/zd_rf.h | 110 ++ drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c | 443 +++++ .../net/wireless/zydas/zd1211rw/zd_rf_al7230b.c | 494 +++++ drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c | 281 +++ drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c | 539 +++++ drivers/net/wireless/zydas/zd1211rw/zd_usb.c | 2060 ++++++++++++++++++++ drivers/net/wireless/zydas/zd1211rw/zd_usb.h | 292 +++ 35 files changed, 8922 insertions(+), 8920 deletions(-) delete mode 100644 drivers/net/wireless/zd1211rw/Kconfig delete mode 100644 drivers/net/wireless/zd1211rw/Makefile delete mode 100644 drivers/net/wireless/zd1211rw/zd_chip.c delete mode 100644 drivers/net/wireless/zd1211rw/zd_chip.h delete mode 100644 drivers/net/wireless/zd1211rw/zd_def.h delete mode 100644 drivers/net/wireless/zd1211rw/zd_mac.c delete mode 100644 drivers/net/wireless/zd1211rw/zd_mac.h delete mode 100644 drivers/net/wireless/zd1211rw/zd_rf.c delete mode 100644 drivers/net/wireless/zd1211rw/zd_rf.h delete mode 100644 drivers/net/wireless/zd1211rw/zd_rf_al2230.c delete mode 100644 drivers/net/wireless/zd1211rw/zd_rf_al7230b.c delete mode 100644 drivers/net/wireless/zd1211rw/zd_rf_rf2959.c delete mode 100644 drivers/net/wireless/zd1211rw/zd_rf_uw2453.c delete mode 100644 drivers/net/wireless/zd1211rw/zd_usb.c delete mode 100644 drivers/net/wireless/zd1211rw/zd_usb.h create mode 100644 drivers/net/wireless/zydas/zd1211rw/Kconfig create mode 100644 drivers/net/wireless/zydas/zd1211rw/Makefile create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_chip.c create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_chip.h create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_def.h create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_mac.c create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_mac.h create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_rf.c create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_rf.h create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_usb.c create mode 100644 drivers/net/wireless/zydas/zd1211rw/zd_usb.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index ec098370ef3d..f2e78420488c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11883,7 +11883,7 @@ W: http://zd1211.ath.cx/wiki/DriverRewrite L: linux-wireless@vger.kernel.org L: zd1211-devs@lists.sourceforge.net (subscribers-only) S: Maintained -F: drivers/net/wireless/zd1211rw/ +F: drivers/net/wireless/zydas/zd1211rw/ ZPOOL COMPRESSED PAGE STORAGE API M: Dan Streetman diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 35155e2dc8bf..e86dcdd5cfcb 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -124,7 +124,6 @@ source "drivers/net/wireless/mediatek/Kconfig" source "drivers/net/wireless/realtek/rtlwifi/Kconfig" source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig" source "drivers/net/wireless/ti/Kconfig" -source "drivers/net/wireless/zd1211rw/Kconfig" source "drivers/net/wireless/rsi/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 4ee6d6d08743..8f54adde831e 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_HERMES) += orinoco/ obj-$(CONFIG_PRISM54) += prism54/ obj-$(CONFIG_HOSTAP) += hostap/ -obj-$(CONFIG_ZD1211RW) += zd1211rw/ obj-$(CONFIG_WLAN) += realtek/ # 16-bit wireless PCMCIA client drivers diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig deleted file mode 100644 index 95920581860a..000000000000 --- a/drivers/net/wireless/zd1211rw/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -config ZD1211RW - tristate "ZyDAS ZD1211/ZD1211B USB-wireless support" - depends on USB && MAC80211 - select FW_LOADER - ---help--- - This is a driver for the ZyDAS ZD1211/ZD1211B wireless - chip, present in many USB-wireless adapters. - - Device firmware is required alongside this driver. You can download - the firmware distribution from http://sf.net/projects/zd1211/files/ - -config ZD1211RW_DEBUG - bool "ZyDAS ZD1211 debugging" - depends on ZD1211RW - ---help--- - ZD1211 debugging messages. Choosing Y will result in additional debug - messages being saved to your kernel logs, which may help debug any - problems. - diff --git a/drivers/net/wireless/zd1211rw/Makefile b/drivers/net/wireless/zd1211rw/Makefile deleted file mode 100644 index 5728a918e508..000000000000 --- a/drivers/net/wireless/zd1211rw/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -obj-$(CONFIG_ZD1211RW) += zd1211rw.o - -zd1211rw-objs := zd_chip.o zd_mac.o \ - zd_rf_al2230.o zd_rf_rf2959.o \ - zd_rf_al7230b.o zd_rf_uw2453.o \ - zd_rf.o zd_usb.o - -ccflags-$(CONFIG_ZD1211RW_DEBUG) := -DDEBUG - diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c deleted file mode 100644 index 07b94eda9604..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_chip.c +++ /dev/null @@ -1,1560 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -/* This file implements all the hardware specific functions for the ZD1211 - * and ZD1211B chips. Support for the ZD1211B was possible after Timothy - * Legge sent me a ZD1211B device. Thank you Tim. -- Uli - */ - -#include -#include -#include - -#include "zd_def.h" -#include "zd_chip.h" -#include "zd_mac.h" -#include "zd_rf.h" - -void zd_chip_init(struct zd_chip *chip, - struct ieee80211_hw *hw, - struct usb_interface *intf) -{ - memset(chip, 0, sizeof(*chip)); - mutex_init(&chip->mutex); - zd_usb_init(&chip->usb, hw, intf); - zd_rf_init(&chip->rf); -} - -void zd_chip_clear(struct zd_chip *chip) -{ - ZD_ASSERT(!mutex_is_locked(&chip->mutex)); - zd_usb_clear(&chip->usb); - zd_rf_clear(&chip->rf); - mutex_destroy(&chip->mutex); - ZD_MEMCLEAR(chip, sizeof(*chip)); -} - -static int scnprint_mac_oui(struct zd_chip *chip, char *buffer, size_t size) -{ - u8 *addr = zd_mac_get_perm_addr(zd_chip_to_mac(chip)); - return scnprintf(buffer, size, "%02x-%02x-%02x", - addr[0], addr[1], addr[2]); -} - -/* Prints an identifier line, which will support debugging. */ -static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size) -{ - int i = 0; - - i = scnprintf(buffer, size, "zd1211%s chip ", - zd_chip_is_zd1211b(chip) ? "b" : ""); - i += zd_usb_scnprint_id(&chip->usb, buffer+i, size-i); - i += scnprintf(buffer+i, size-i, " "); - i += scnprint_mac_oui(chip, buffer+i, size-i); - i += scnprintf(buffer+i, size-i, " "); - i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i); - i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c%c", chip->pa_type, - chip->patch_cck_gain ? 'g' : '-', - chip->patch_cr157 ? '7' : '-', - chip->patch_6m_band_edge ? '6' : '-', - chip->new_phy_layout ? 'N' : '-', - chip->al2230s_bit ? 'S' : '-'); - return i; -} - -static void print_id(struct zd_chip *chip) -{ - char buffer[80]; - - scnprint_id(chip, buffer, sizeof(buffer)); - buffer[sizeof(buffer)-1] = 0; - dev_info(zd_chip_dev(chip), "%s\n", buffer); -} - -static zd_addr_t inc_addr(zd_addr_t addr) -{ - u16 a = (u16)addr; - /* Control registers use byte addressing, but everything else uses word - * addressing. */ - if ((a & 0xf000) == CR_START) - a += 2; - else - a += 1; - return (zd_addr_t)a; -} - -/* Read a variable number of 32-bit values. Parameter count is not allowed to - * exceed USB_MAX_IOREAD32_COUNT. - */ -int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr, - unsigned int count) -{ - int r; - int i; - zd_addr_t a16[USB_MAX_IOREAD32_COUNT * 2]; - u16 v16[USB_MAX_IOREAD32_COUNT * 2]; - unsigned int count16; - - if (count > USB_MAX_IOREAD32_COUNT) - return -EINVAL; - - /* Use stack for values and addresses. */ - count16 = 2 * count; - BUG_ON(count16 * sizeof(zd_addr_t) > sizeof(a16)); - BUG_ON(count16 * sizeof(u16) > sizeof(v16)); - - for (i = 0; i < count; i++) { - int j = 2*i; - /* We read the high word always first. */ - a16[j] = inc_addr(addr[i]); - a16[j+1] = addr[i]; - } - - r = zd_ioread16v_locked(chip, v16, a16, count16); - if (r) { - dev_dbg_f(zd_chip_dev(chip), - "error: %s. Error number %d\n", __func__, r); - return r; - } - - for (i = 0; i < count; i++) { - int j = 2*i; - values[i] = (v16[j] << 16) | v16[j+1]; - } - - return 0; -} - -static int _zd_iowrite32v_async_locked(struct zd_chip *chip, - const struct zd_ioreq32 *ioreqs, - unsigned int count) -{ - int i, j, r; - struct zd_ioreq16 ioreqs16[USB_MAX_IOWRITE32_COUNT * 2]; - unsigned int count16; - - /* Use stack for values and addresses. */ - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - - if (count == 0) - return 0; - if (count > USB_MAX_IOWRITE32_COUNT) - return -EINVAL; - - count16 = 2 * count; - BUG_ON(count16 * sizeof(struct zd_ioreq16) > sizeof(ioreqs16)); - - for (i = 0; i < count; i++) { - j = 2*i; - /* We write the high word always first. */ - ioreqs16[j].value = ioreqs[i].value >> 16; - ioreqs16[j].addr = inc_addr(ioreqs[i].addr); - ioreqs16[j+1].value = ioreqs[i].value; - ioreqs16[j+1].addr = ioreqs[i].addr; - } - - r = zd_usb_iowrite16v_async(&chip->usb, ioreqs16, count16); -#ifdef DEBUG - if (r) { - dev_dbg_f(zd_chip_dev(chip), - "error %d in zd_usb_write16v\n", r); - } -#endif /* DEBUG */ - return r; -} - -int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, - unsigned int count) -{ - int r; - - zd_usb_iowrite16v_async_start(&chip->usb); - r = _zd_iowrite32v_async_locked(chip, ioreqs, count); - if (r) { - zd_usb_iowrite16v_async_end(&chip->usb, 0); - return r; - } - return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */); -} - -int zd_iowrite16a_locked(struct zd_chip *chip, - const struct zd_ioreq16 *ioreqs, unsigned int count) -{ - int r; - unsigned int i, j, t, max; - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - zd_usb_iowrite16v_async_start(&chip->usb); - - for (i = 0; i < count; i += j + t) { - t = 0; - max = count-i; - if (max > USB_MAX_IOWRITE16_COUNT) - max = USB_MAX_IOWRITE16_COUNT; - for (j = 0; j < max; j++) { - if (!ioreqs[i+j].addr) { - t = 1; - break; - } - } - - r = zd_usb_iowrite16v_async(&chip->usb, &ioreqs[i], j); - if (r) { - zd_usb_iowrite16v_async_end(&chip->usb, 0); - dev_dbg_f(zd_chip_dev(chip), - "error zd_usb_iowrite16v. Error number %d\n", - r); - return r; - } - } - - return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */); -} - -/* Writes a variable number of 32 bit registers. The functions will split - * that in several USB requests. A split can be forced by inserting an IO - * request with an zero address field. - */ -int zd_iowrite32a_locked(struct zd_chip *chip, - const struct zd_ioreq32 *ioreqs, unsigned int count) -{ - int r; - unsigned int i, j, t, max; - - zd_usb_iowrite16v_async_start(&chip->usb); - - for (i = 0; i < count; i += j + t) { - t = 0; - max = count-i; - if (max > USB_MAX_IOWRITE32_COUNT) - max = USB_MAX_IOWRITE32_COUNT; - for (j = 0; j < max; j++) { - if (!ioreqs[i+j].addr) { - t = 1; - break; - } - } - - r = _zd_iowrite32v_async_locked(chip, &ioreqs[i], j); - if (r) { - zd_usb_iowrite16v_async_end(&chip->usb, 0); - dev_dbg_f(zd_chip_dev(chip), - "error _%s. Error number %d\n", __func__, - r); - return r; - } - } - - return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */); -} - -int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value) -{ - int r; - - mutex_lock(&chip->mutex); - r = zd_ioread16_locked(chip, value, addr); - mutex_unlock(&chip->mutex); - return r; -} - -int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value) -{ - int r; - - mutex_lock(&chip->mutex); - r = zd_ioread32_locked(chip, value, addr); - mutex_unlock(&chip->mutex); - return r; -} - -int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value) -{ - int r; - - mutex_lock(&chip->mutex); - r = zd_iowrite16_locked(chip, value, addr); - mutex_unlock(&chip->mutex); - return r; -} - -int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value) -{ - int r; - - mutex_lock(&chip->mutex); - r = zd_iowrite32_locked(chip, value, addr); - mutex_unlock(&chip->mutex); - return r; -} - -int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, - u32 *values, unsigned int count) -{ - int r; - - mutex_lock(&chip->mutex); - r = zd_ioread32v_locked(chip, values, addresses, count); - mutex_unlock(&chip->mutex); - return r; -} - -int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, - unsigned int count) -{ - int r; - - mutex_lock(&chip->mutex); - r = zd_iowrite32a_locked(chip, ioreqs, count); - mutex_unlock(&chip->mutex); - return r; -} - -static int read_pod(struct zd_chip *chip, u8 *rf_type) -{ - int r; - u32 value; - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - r = zd_ioread32_locked(chip, &value, E2P_POD); - if (r) - goto error; - dev_dbg_f(zd_chip_dev(chip), "E2P_POD %#010x\n", value); - - /* FIXME: AL2230 handling (Bit 7 in POD) */ - *rf_type = value & 0x0f; - chip->pa_type = (value >> 16) & 0x0f; - chip->patch_cck_gain = (value >> 8) & 0x1; - chip->patch_cr157 = (value >> 13) & 0x1; - chip->patch_6m_band_edge = (value >> 21) & 0x1; - chip->new_phy_layout = (value >> 31) & 0x1; - chip->al2230s_bit = (value >> 7) & 0x1; - chip->link_led = ((value >> 4) & 1) ? LED1 : LED2; - chip->supports_tx_led = 1; - if (value & (1 << 24)) { /* LED scenario */ - if (value & (1 << 29)) - chip->supports_tx_led = 0; - } - - dev_dbg_f(zd_chip_dev(chip), - "RF %s %#01x PA type %#01x patch CCK %d patch CR157 %d " - "patch 6M %d new PHY %d link LED%d tx led %d\n", - zd_rf_name(*rf_type), *rf_type, - chip->pa_type, chip->patch_cck_gain, - chip->patch_cr157, chip->patch_6m_band_edge, - chip->new_phy_layout, - chip->link_led == LED1 ? 1 : 2, - chip->supports_tx_led); - return 0; -error: - *rf_type = 0; - chip->pa_type = 0; - chip->patch_cck_gain = 0; - chip->patch_cr157 = 0; - chip->patch_6m_band_edge = 0; - chip->new_phy_layout = 0; - return r; -} - -static int zd_write_mac_addr_common(struct zd_chip *chip, const u8 *mac_addr, - const struct zd_ioreq32 *in_reqs, - const char *type) -{ - int r; - struct zd_ioreq32 reqs[2] = {in_reqs[0], in_reqs[1]}; - - if (mac_addr) { - reqs[0].value = (mac_addr[3] << 24) - | (mac_addr[2] << 16) - | (mac_addr[1] << 8) - | mac_addr[0]; - reqs[1].value = (mac_addr[5] << 8) - | mac_addr[4]; - dev_dbg_f(zd_chip_dev(chip), "%s addr %pM\n", type, mac_addr); - } else { - dev_dbg_f(zd_chip_dev(chip), "set NULL %s\n", type); - } - - mutex_lock(&chip->mutex); - r = zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); - mutex_unlock(&chip->mutex); - return r; -} - -/* MAC address: if custom mac addresses are to be used CR_MAC_ADDR_P1 and - * CR_MAC_ADDR_P2 must be overwritten - */ -int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr) -{ - static const struct zd_ioreq32 reqs[2] = { - [0] = { .addr = CR_MAC_ADDR_P1 }, - [1] = { .addr = CR_MAC_ADDR_P2 }, - }; - - return zd_write_mac_addr_common(chip, mac_addr, reqs, "mac"); -} - -int zd_write_bssid(struct zd_chip *chip, const u8 *bssid) -{ - static const struct zd_ioreq32 reqs[2] = { - [0] = { .addr = CR_BSSID_P1 }, - [1] = { .addr = CR_BSSID_P2 }, - }; - - return zd_write_mac_addr_common(chip, bssid, reqs, "bssid"); -} - -int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain) -{ - int r; - u32 value; - - mutex_lock(&chip->mutex); - r = zd_ioread32_locked(chip, &value, E2P_SUBID); - mutex_unlock(&chip->mutex); - if (r) - return r; - - *regdomain = value >> 16; - dev_dbg_f(zd_chip_dev(chip), "regdomain: %#04x\n", *regdomain); - - return 0; -} - -static int read_values(struct zd_chip *chip, u8 *values, size_t count, - zd_addr_t e2p_addr, u32 guard) -{ - int r; - int i; - u32 v; - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - for (i = 0;;) { - r = zd_ioread32_locked(chip, &v, - (zd_addr_t)((u16)e2p_addr+i/2)); - if (r) - return r; - v -= guard; - if (i+4 < count) { - values[i++] = v; - values[i++] = v >> 8; - values[i++] = v >> 16; - values[i++] = v >> 24; - continue; - } - for (;i < count; i++) - values[i] = v >> (8*(i%3)); - return 0; - } -} - -static int read_pwr_cal_values(struct zd_chip *chip) -{ - return read_values(chip, chip->pwr_cal_values, - E2P_CHANNEL_COUNT, E2P_PWR_CAL_VALUE1, - 0); -} - -static int read_pwr_int_values(struct zd_chip *chip) -{ - return read_values(chip, chip->pwr_int_values, - E2P_CHANNEL_COUNT, E2P_PWR_INT_VALUE1, - E2P_PWR_INT_GUARD); -} - -static int read_ofdm_cal_values(struct zd_chip *chip) -{ - int r; - int i; - static const zd_addr_t addresses[] = { - E2P_36M_CAL_VALUE1, - E2P_48M_CAL_VALUE1, - E2P_54M_CAL_VALUE1, - }; - - for (i = 0; i < 3; i++) { - r = read_values(chip, chip->ofdm_cal_values[i], - E2P_CHANNEL_COUNT, addresses[i], 0); - if (r) - return r; - } - return 0; -} - -static int read_cal_int_tables(struct zd_chip *chip) -{ - int r; - - r = read_pwr_cal_values(chip); - if (r) - return r; - r = read_pwr_int_values(chip); - if (r) - return r; - r = read_ofdm_cal_values(chip); - if (r) - return r; - return 0; -} - -/* phy means physical registers */ -int zd_chip_lock_phy_regs(struct zd_chip *chip) -{ - int r; - u32 tmp; - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - r = zd_ioread32_locked(chip, &tmp, CR_REG1); - if (r) { - dev_err(zd_chip_dev(chip), "error ioread32(CR_REG1): %d\n", r); - return r; - } - - tmp &= ~UNLOCK_PHY_REGS; - - r = zd_iowrite32_locked(chip, tmp, CR_REG1); - if (r) - dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); - return r; -} - -int zd_chip_unlock_phy_regs(struct zd_chip *chip) -{ - int r; - u32 tmp; - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - r = zd_ioread32_locked(chip, &tmp, CR_REG1); - if (r) { - dev_err(zd_chip_dev(chip), - "error ioread32(CR_REG1): %d\n", r); - return r; - } - - tmp |= UNLOCK_PHY_REGS; - - r = zd_iowrite32_locked(chip, tmp, CR_REG1); - if (r) - dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); - return r; -} - -/* ZD_CR157 can be optionally patched by the EEPROM for original ZD1211 */ -static int patch_cr157(struct zd_chip *chip) -{ - int r; - u16 value; - - if (!chip->patch_cr157) - return 0; - - r = zd_ioread16_locked(chip, &value, E2P_PHY_REG); - if (r) - return r; - - dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value >> 8); - return zd_iowrite32_locked(chip, value >> 8, ZD_CR157); -} - -/* - * 6M band edge can be optionally overwritten for certain RF's - * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge - * bit (for AL2230, AL2230S) - */ -static int patch_6m_band_edge(struct zd_chip *chip, u8 channel) -{ - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - if (!chip->patch_6m_band_edge) - return 0; - - return zd_rf_patch_6m_band_edge(&chip->rf, channel); -} - -/* Generic implementation of 6M band edge patching, used by most RFs via - * zd_rf_generic_patch_6m() */ -int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel) -{ - struct zd_ioreq16 ioreqs[] = { - { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, - { ZD_CR47, 0x1e }, - }; - - /* FIXME: Channel 11 is not the edge for all regulatory domains. */ - if (channel == 1 || channel == 11) - ioreqs[0].value = 0x12; - - dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int zd1211_hw_reset_phy(struct zd_chip *chip) -{ - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR0, 0x0a }, { ZD_CR1, 0x06 }, { ZD_CR2, 0x26 }, - { ZD_CR3, 0x38 }, { ZD_CR4, 0x80 }, { ZD_CR9, 0xa0 }, - { ZD_CR10, 0x81 }, { ZD_CR11, 0x00 }, { ZD_CR12, 0x7f }, - { ZD_CR13, 0x8c }, { ZD_CR14, 0x80 }, { ZD_CR15, 0x3d }, - { ZD_CR16, 0x20 }, { ZD_CR17, 0x1e }, { ZD_CR18, 0x0a }, - { ZD_CR19, 0x48 }, { ZD_CR20, 0x0c }, { ZD_CR21, 0x0c }, - { ZD_CR22, 0x23 }, { ZD_CR23, 0x90 }, { ZD_CR24, 0x14 }, - { ZD_CR25, 0x40 }, { ZD_CR26, 0x10 }, { ZD_CR27, 0x19 }, - { ZD_CR28, 0x7f }, { ZD_CR29, 0x80 }, { ZD_CR30, 0x4b }, - { ZD_CR31, 0x60 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x08 }, - { ZD_CR34, 0x06 }, { ZD_CR35, 0x0a }, { ZD_CR36, 0x00 }, - { ZD_CR37, 0x00 }, { ZD_CR38, 0x38 }, { ZD_CR39, 0x0c }, - { ZD_CR40, 0x84 }, { ZD_CR41, 0x2a }, { ZD_CR42, 0x80 }, - { ZD_CR43, 0x10 }, { ZD_CR44, 0x12 }, { ZD_CR46, 0xff }, - { ZD_CR47, 0x1E }, { ZD_CR48, 0x26 }, { ZD_CR49, 0x5b }, - { ZD_CR64, 0xd0 }, { ZD_CR65, 0x04 }, { ZD_CR66, 0x58 }, - { ZD_CR67, 0xc9 }, { ZD_CR68, 0x88 }, { ZD_CR69, 0x41 }, - { ZD_CR70, 0x23 }, { ZD_CR71, 0x10 }, { ZD_CR72, 0xff }, - { ZD_CR73, 0x32 }, { ZD_CR74, 0x30 }, { ZD_CR75, 0x65 }, - { ZD_CR76, 0x41 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x30 }, - { ZD_CR79, 0x68 }, { ZD_CR80, 0x64 }, { ZD_CR81, 0x64 }, - { ZD_CR82, 0x00 }, { ZD_CR83, 0x00 }, { ZD_CR84, 0x00 }, - { ZD_CR85, 0x02 }, { ZD_CR86, 0x00 }, { ZD_CR87, 0x00 }, - { ZD_CR88, 0xff }, { ZD_CR89, 0xfc }, { ZD_CR90, 0x00 }, - { ZD_CR91, 0x00 }, { ZD_CR92, 0x00 }, { ZD_CR93, 0x08 }, - { ZD_CR94, 0x00 }, { ZD_CR95, 0x00 }, { ZD_CR96, 0xff }, - { ZD_CR97, 0xe7 }, { ZD_CR98, 0x00 }, { ZD_CR99, 0x00 }, - { ZD_CR100, 0x00 }, { ZD_CR101, 0xae }, { ZD_CR102, 0x02 }, - { ZD_CR103, 0x00 }, { ZD_CR104, 0x03 }, { ZD_CR105, 0x65 }, - { ZD_CR106, 0x04 }, { ZD_CR107, 0x00 }, { ZD_CR108, 0x0a }, - { ZD_CR109, 0xaa }, { ZD_CR110, 0xaa }, { ZD_CR111, 0x25 }, - { ZD_CR112, 0x25 }, { ZD_CR113, 0x00 }, { ZD_CR119, 0x1e }, - { ZD_CR125, 0x90 }, { ZD_CR126, 0x00 }, { ZD_CR127, 0x00 }, - { }, - { ZD_CR5, 0x00 }, { ZD_CR6, 0x00 }, { ZD_CR7, 0x00 }, - { ZD_CR8, 0x00 }, { ZD_CR9, 0x20 }, { ZD_CR12, 0xf0 }, - { ZD_CR20, 0x0e }, { ZD_CR21, 0x0e }, { ZD_CR27, 0x10 }, - { ZD_CR44, 0x33 }, { ZD_CR47, 0x1E }, { ZD_CR83, 0x24 }, - { ZD_CR84, 0x04 }, { ZD_CR85, 0x00 }, { ZD_CR86, 0x0C }, - { ZD_CR87, 0x12 }, { ZD_CR88, 0x0C }, { ZD_CR89, 0x00 }, - { ZD_CR90, 0x10 }, { ZD_CR91, 0x08 }, { ZD_CR93, 0x00 }, - { ZD_CR94, 0x01 }, { ZD_CR95, 0x00 }, { ZD_CR96, 0x50 }, - { ZD_CR97, 0x37 }, { ZD_CR98, 0x35 }, { ZD_CR101, 0x13 }, - { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 }, { ZD_CR104, 0x18 }, - { ZD_CR105, 0x12 }, { ZD_CR109, 0x27 }, { ZD_CR110, 0x27 }, - { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 }, { ZD_CR113, 0x27 }, - { ZD_CR114, 0x27 }, { ZD_CR115, 0x26 }, { ZD_CR116, 0x24 }, - { ZD_CR117, 0xfc }, { ZD_CR118, 0xfa }, { ZD_CR120, 0x4f }, - { ZD_CR125, 0xaa }, { ZD_CR127, 0x03 }, { ZD_CR128, 0x14 }, - { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, { ZD_CR131, 0x0C }, - { ZD_CR136, 0xdf }, { ZD_CR137, 0x40 }, { ZD_CR138, 0xa0 }, - { ZD_CR139, 0xb0 }, { ZD_CR140, 0x99 }, { ZD_CR141, 0x82 }, - { ZD_CR142, 0x54 }, { ZD_CR143, 0x1c }, { ZD_CR144, 0x6c }, - { ZD_CR147, 0x07 }, { ZD_CR148, 0x4c }, { ZD_CR149, 0x50 }, - { ZD_CR150, 0x0e }, { ZD_CR151, 0x18 }, { ZD_CR160, 0xfe }, - { ZD_CR161, 0xee }, { ZD_CR162, 0xaa }, { ZD_CR163, 0xfa }, - { ZD_CR164, 0xfa }, { ZD_CR165, 0xea }, { ZD_CR166, 0xbe }, - { ZD_CR167, 0xbe }, { ZD_CR168, 0x6a }, { ZD_CR169, 0xba }, - { ZD_CR170, 0xba }, { ZD_CR171, 0xba }, - /* Note: ZD_CR204 must lead the ZD_CR203 */ - { ZD_CR204, 0x7d }, - { }, - { ZD_CR203, 0x30 }, - }; - - int r, t; - - dev_dbg_f(zd_chip_dev(chip), "\n"); - - r = zd_chip_lock_phy_regs(chip); - if (r) - goto out; - - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - if (r) - goto unlock; - - r = patch_cr157(chip); -unlock: - t = zd_chip_unlock_phy_regs(chip); - if (t && !r) - r = t; -out: - return r; -} - -static int zd1211b_hw_reset_phy(struct zd_chip *chip) -{ - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR0, 0x14 }, { ZD_CR1, 0x06 }, { ZD_CR2, 0x26 }, - { ZD_CR3, 0x38 }, { ZD_CR4, 0x80 }, { ZD_CR9, 0xe0 }, - { ZD_CR10, 0x81 }, - /* power control { { ZD_CR11, 1 << 6 }, */ - { ZD_CR11, 0x00 }, - { ZD_CR12, 0xf0 }, { ZD_CR13, 0x8c }, { ZD_CR14, 0x80 }, - { ZD_CR15, 0x3d }, { ZD_CR16, 0x20 }, { ZD_CR17, 0x1e }, - { ZD_CR18, 0x0a }, { ZD_CR19, 0x48 }, - { ZD_CR20, 0x10 }, /* Org:0x0E, ComTrend:RalLink AP */ - { ZD_CR21, 0x0e }, { ZD_CR22, 0x23 }, { ZD_CR23, 0x90 }, - { ZD_CR24, 0x14 }, { ZD_CR25, 0x40 }, { ZD_CR26, 0x10 }, - { ZD_CR27, 0x10 }, { ZD_CR28, 0x7f }, { ZD_CR29, 0x80 }, - { ZD_CR30, 0x4b }, /* ASIC/FWT, no jointly decoder */ - { ZD_CR31, 0x60 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x08 }, - { ZD_CR34, 0x06 }, { ZD_CR35, 0x0a }, { ZD_CR36, 0x00 }, - { ZD_CR37, 0x00 }, { ZD_CR38, 0x38 }, { ZD_CR39, 0x0c }, - { ZD_CR40, 0x84 }, { ZD_CR41, 0x2a }, { ZD_CR42, 0x80 }, - { ZD_CR43, 0x10 }, { ZD_CR44, 0x33 }, { ZD_CR46, 0xff }, - { ZD_CR47, 0x1E }, { ZD_CR48, 0x26 }, { ZD_CR49, 0x5b }, - { ZD_CR64, 0xd0 }, { ZD_CR65, 0x04 }, { ZD_CR66, 0x58 }, - { ZD_CR67, 0xc9 }, { ZD_CR68, 0x88 }, { ZD_CR69, 0x41 }, - { ZD_CR70, 0x23 }, { ZD_CR71, 0x10 }, { ZD_CR72, 0xff }, - { ZD_CR73, 0x32 }, { ZD_CR74, 0x30 }, { ZD_CR75, 0x65 }, - { ZD_CR76, 0x41 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x30 }, - { ZD_CR79, 0xf0 }, { ZD_CR80, 0x64 }, { ZD_CR81, 0x64 }, - { ZD_CR82, 0x00 }, { ZD_CR83, 0x24 }, { ZD_CR84, 0x04 }, - { ZD_CR85, 0x00 }, { ZD_CR86, 0x0c }, { ZD_CR87, 0x12 }, - { ZD_CR88, 0x0c }, { ZD_CR89, 0x00 }, { ZD_CR90, 0x58 }, - { ZD_CR91, 0x04 }, { ZD_CR92, 0x00 }, { ZD_CR93, 0x00 }, - { ZD_CR94, 0x01 }, - { ZD_CR95, 0x20 }, /* ZD1211B */ - { ZD_CR96, 0x50 }, { ZD_CR97, 0x37 }, { ZD_CR98, 0x35 }, - { ZD_CR99, 0x00 }, { ZD_CR100, 0x01 }, { ZD_CR101, 0x13 }, - { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 }, { ZD_CR104, 0x18 }, - { ZD_CR105, 0x12 }, { ZD_CR106, 0x04 }, { ZD_CR107, 0x00 }, - { ZD_CR108, 0x0a }, { ZD_CR109, 0x27 }, { ZD_CR110, 0x27 }, - { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 }, { ZD_CR113, 0x27 }, - { ZD_CR114, 0x27 }, { ZD_CR115, 0x26 }, { ZD_CR116, 0x24 }, - { ZD_CR117, 0xfc }, { ZD_CR118, 0xfa }, { ZD_CR119, 0x1e }, - { ZD_CR125, 0x90 }, { ZD_CR126, 0x00 }, { ZD_CR127, 0x00 }, - { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, - { ZD_CR131, 0x0c }, { ZD_CR136, 0xdf }, { ZD_CR137, 0xa0 }, - { ZD_CR138, 0xa8 }, { ZD_CR139, 0xb4 }, { ZD_CR140, 0x98 }, - { ZD_CR141, 0x82 }, { ZD_CR142, 0x53 }, { ZD_CR143, 0x1c }, - { ZD_CR144, 0x6c }, { ZD_CR147, 0x07 }, { ZD_CR148, 0x40 }, - { ZD_CR149, 0x40 }, /* Org:0x50 ComTrend:RalLink AP */ - { ZD_CR150, 0x14 }, /* Org:0x0E ComTrend:RalLink AP */ - { ZD_CR151, 0x18 }, { ZD_CR159, 0x70 }, { ZD_CR160, 0xfe }, - { ZD_CR161, 0xee }, { ZD_CR162, 0xaa }, { ZD_CR163, 0xfa }, - { ZD_CR164, 0xfa }, { ZD_CR165, 0xea }, { ZD_CR166, 0xbe }, - { ZD_CR167, 0xbe }, { ZD_CR168, 0x6a }, { ZD_CR169, 0xba }, - { ZD_CR170, 0xba }, { ZD_CR171, 0xba }, - /* Note: ZD_CR204 must lead the ZD_CR203 */ - { ZD_CR204, 0x7d }, - {}, - { ZD_CR203, 0x30 }, - }; - - int r, t; - - dev_dbg_f(zd_chip_dev(chip), "\n"); - - r = zd_chip_lock_phy_regs(chip); - if (r) - goto out; - - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - t = zd_chip_unlock_phy_regs(chip); - if (t && !r) - r = t; -out: - return r; -} - -static int hw_reset_phy(struct zd_chip *chip) -{ - return zd_chip_is_zd1211b(chip) ? zd1211b_hw_reset_phy(chip) : - zd1211_hw_reset_phy(chip); -} - -static int zd1211_hw_init_hmac(struct zd_chip *chip) -{ - static const struct zd_ioreq32 ioreqs[] = { - { CR_ZD1211_RETRY_MAX, ZD1211_RETRY_COUNT }, - { CR_RX_THRESHOLD, 0x000c0640 }, - }; - - dev_dbg_f(zd_chip_dev(chip), "\n"); - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int zd1211b_hw_init_hmac(struct zd_chip *chip) -{ - static const struct zd_ioreq32 ioreqs[] = { - { CR_ZD1211B_RETRY_MAX, ZD1211B_RETRY_COUNT }, - { CR_ZD1211B_CWIN_MAX_MIN_AC0, 0x007f003f }, - { CR_ZD1211B_CWIN_MAX_MIN_AC1, 0x007f003f }, - { CR_ZD1211B_CWIN_MAX_MIN_AC2, 0x003f001f }, - { CR_ZD1211B_CWIN_MAX_MIN_AC3, 0x001f000f }, - { CR_ZD1211B_AIFS_CTL1, 0x00280028 }, - { CR_ZD1211B_AIFS_CTL2, 0x008C003C }, - { CR_ZD1211B_TXOP, 0x01800824 }, - { CR_RX_THRESHOLD, 0x000c0eff, }, - }; - - dev_dbg_f(zd_chip_dev(chip), "\n"); - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int hw_init_hmac(struct zd_chip *chip) -{ - int r; - static const struct zd_ioreq32 ioreqs[] = { - { CR_ACK_TIMEOUT_EXT, 0x20 }, - { CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, - { CR_SNIFFER_ON, 0 }, - { CR_RX_FILTER, STA_RX_FILTER }, - { CR_GROUP_HASH_P1, 0x00 }, - { CR_GROUP_HASH_P2, 0x80000000 }, - { CR_REG1, 0xa4 }, - { CR_ADDA_PWR_DWN, 0x7f }, - { CR_BCN_PLCP_CFG, 0x00f00401 }, - { CR_PHY_DELAY, 0x00 }, - { CR_ACK_TIMEOUT_EXT, 0x80 }, - { CR_ADDA_PWR_DWN, 0x00 }, - { CR_ACK_TIME_80211, 0x100 }, - { CR_RX_PE_DELAY, 0x70 }, - { CR_PS_CTRL, 0x10000000 }, - { CR_RTS_CTS_RATE, 0x02030203 }, - { CR_AFTER_PNP, 0x1 }, - { CR_WEP_PROTECT, 0x114 }, - { CR_IFS_VALUE, IFS_VALUE_DEFAULT }, - { CR_CAM_MODE, MODE_AP_WDS}, - }; - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - if (r) - return r; - - return zd_chip_is_zd1211b(chip) ? - zd1211b_hw_init_hmac(chip) : zd1211_hw_init_hmac(chip); -} - -struct aw_pt_bi { - u32 atim_wnd_period; - u32 pre_tbtt; - u32 beacon_interval; -}; - -static int get_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) -{ - int r; - static const zd_addr_t aw_pt_bi_addr[] = - { CR_ATIM_WND_PERIOD, CR_PRE_TBTT, CR_BCN_INTERVAL }; - u32 values[3]; - - r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr, - ARRAY_SIZE(aw_pt_bi_addr)); - if (r) { - memset(s, 0, sizeof(*s)); - return r; - } - - s->atim_wnd_period = values[0]; - s->pre_tbtt = values[1]; - s->beacon_interval = values[2]; - return 0; -} - -static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) -{ - struct zd_ioreq32 reqs[3]; - u16 b_interval = s->beacon_interval & 0xffff; - - if (b_interval <= 5) - b_interval = 5; - if (s->pre_tbtt < 4 || s->pre_tbtt >= b_interval) - s->pre_tbtt = b_interval - 1; - if (s->atim_wnd_period >= s->pre_tbtt) - s->atim_wnd_period = s->pre_tbtt - 1; - - reqs[0].addr = CR_ATIM_WND_PERIOD; - reqs[0].value = s->atim_wnd_period; - reqs[1].addr = CR_PRE_TBTT; - reqs[1].value = s->pre_tbtt; - reqs[2].addr = CR_BCN_INTERVAL; - reqs[2].value = (s->beacon_interval & ~0xffff) | b_interval; - - return zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); -} - - -static int set_beacon_interval(struct zd_chip *chip, u16 interval, - u8 dtim_period, int type) -{ - int r; - struct aw_pt_bi s; - u32 b_interval, mode_flag; - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - - if (interval > 0) { - switch (type) { - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - mode_flag = BCN_MODE_IBSS; - break; - case NL80211_IFTYPE_AP: - mode_flag = BCN_MODE_AP; - break; - default: - mode_flag = 0; - break; - } - } else { - dtim_period = 0; - mode_flag = 0; - } - - b_interval = mode_flag | (dtim_period << 16) | interval; - - r = zd_iowrite32_locked(chip, b_interval, CR_BCN_INTERVAL); - if (r) - return r; - r = get_aw_pt_bi(chip, &s); - if (r) - return r; - return set_aw_pt_bi(chip, &s); -} - -int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period, - int type) -{ - int r; - - mutex_lock(&chip->mutex); - r = set_beacon_interval(chip, interval, dtim_period, type); - mutex_unlock(&chip->mutex); - return r; -} - -static int hw_init(struct zd_chip *chip) -{ - int r; - - dev_dbg_f(zd_chip_dev(chip), "\n"); - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - r = hw_reset_phy(chip); - if (r) - return r; - - r = hw_init_hmac(chip); - if (r) - return r; - - return set_beacon_interval(chip, 100, 0, NL80211_IFTYPE_UNSPECIFIED); -} - -static zd_addr_t fw_reg_addr(struct zd_chip *chip, u16 offset) -{ - return (zd_addr_t)((u16)chip->fw_regs_base + offset); -} - -#ifdef DEBUG -static int dump_cr(struct zd_chip *chip, const zd_addr_t addr, - const char *addr_string) -{ - int r; - u32 value; - - r = zd_ioread32_locked(chip, &value, addr); - if (r) { - dev_dbg_f(zd_chip_dev(chip), - "error reading %s. Error number %d\n", addr_string, r); - return r; - } - - dev_dbg_f(zd_chip_dev(chip), "%s %#010x\n", - addr_string, (unsigned int)value); - return 0; -} - -static int test_init(struct zd_chip *chip) -{ - int r; - - r = dump_cr(chip, CR_AFTER_PNP, "CR_AFTER_PNP"); - if (r) - return r; - r = dump_cr(chip, CR_GPI_EN, "CR_GPI_EN"); - if (r) - return r; - return dump_cr(chip, CR_INTERRUPT, "CR_INTERRUPT"); -} - -static void dump_fw_registers(struct zd_chip *chip) -{ - const zd_addr_t addr[4] = { - fw_reg_addr(chip, FW_REG_FIRMWARE_VER), - fw_reg_addr(chip, FW_REG_USB_SPEED), - fw_reg_addr(chip, FW_REG_FIX_TX_RATE), - fw_reg_addr(chip, FW_REG_LED_LINK_STATUS), - }; - - int r; - u16 values[4]; - - r = zd_ioread16v_locked(chip, values, (const zd_addr_t*)addr, - ARRAY_SIZE(addr)); - if (r) { - dev_dbg_f(zd_chip_dev(chip), "error %d zd_ioread16v_locked\n", - r); - return; - } - - dev_dbg_f(zd_chip_dev(chip), "FW_FIRMWARE_VER %#06hx\n", values[0]); - dev_dbg_f(zd_chip_dev(chip), "FW_USB_SPEED %#06hx\n", values[1]); - dev_dbg_f(zd_chip_dev(chip), "FW_FIX_TX_RATE %#06hx\n", values[2]); - dev_dbg_f(zd_chip_dev(chip), "FW_LINK_STATUS %#06hx\n", values[3]); -} -#endif /* DEBUG */ - -static int print_fw_version(struct zd_chip *chip) -{ - struct wiphy *wiphy = zd_chip_to_mac(chip)->hw->wiphy; - int r; - u16 version; - - r = zd_ioread16_locked(chip, &version, - fw_reg_addr(chip, FW_REG_FIRMWARE_VER)); - if (r) - return r; - - dev_info(zd_chip_dev(chip),"firmware version %04hx\n", version); - - snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), - "%04hx", version); - - return 0; -} - -static int set_mandatory_rates(struct zd_chip *chip, int gmode) -{ - u32 rates; - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - /* This sets the mandatory rates, which only depend from the standard - * that the device is supporting. Until further notice we should try - * to support 802.11g also for full speed USB. - */ - if (!gmode) - rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M; - else - rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M| - CR_RATE_6M|CR_RATE_12M|CR_RATE_24M; - - return zd_iowrite32_locked(chip, rates, CR_MANDATORY_RATE_TBL); -} - -int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, - int preamble) -{ - u32 value = 0; - - dev_dbg_f(zd_chip_dev(chip), "preamble=%x\n", preamble); - value |= preamble << RTSCTS_SH_RTS_PMB_TYPE; - value |= preamble << RTSCTS_SH_CTS_PMB_TYPE; - - /* We always send 11M RTS/self-CTS messages, like the vendor driver. */ - value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_RTS_RATE; - value |= ZD_RX_CCK << RTSCTS_SH_RTS_MOD_TYPE; - value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_CTS_RATE; - value |= ZD_RX_CCK << RTSCTS_SH_CTS_MOD_TYPE; - - return zd_iowrite32_locked(chip, value, CR_RTS_CTS_RATE); -} - -int zd_chip_enable_hwint(struct zd_chip *chip) -{ - int r; - - mutex_lock(&chip->mutex); - r = zd_iowrite32_locked(chip, HWINT_ENABLED, CR_INTERRUPT); - mutex_unlock(&chip->mutex); - return r; -} - -static int disable_hwint(struct zd_chip *chip) -{ - return zd_iowrite32_locked(chip, HWINT_DISABLED, CR_INTERRUPT); -} - -int zd_chip_disable_hwint(struct zd_chip *chip) -{ - int r; - - mutex_lock(&chip->mutex); - r = disable_hwint(chip); - mutex_unlock(&chip->mutex); - return r; -} - -static int read_fw_regs_offset(struct zd_chip *chip) -{ - int r; - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - r = zd_ioread16_locked(chip, (u16*)&chip->fw_regs_base, - FWRAW_REGS_ADDR); - if (r) - return r; - dev_dbg_f(zd_chip_dev(chip), "fw_regs_base: %#06hx\n", - (u16)chip->fw_regs_base); - - return 0; -} - -/* Read mac address using pre-firmware interface */ -int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr) -{ - dev_dbg_f(zd_chip_dev(chip), "\n"); - return zd_usb_read_fw(&chip->usb, E2P_MAC_ADDR_P1, addr, - ETH_ALEN); -} - -int zd_chip_init_hw(struct zd_chip *chip) -{ - int r; - u8 rf_type; - - dev_dbg_f(zd_chip_dev(chip), "\n"); - - mutex_lock(&chip->mutex); - -#ifdef DEBUG - r = test_init(chip); - if (r) - goto out; -#endif - r = zd_iowrite32_locked(chip, 1, CR_AFTER_PNP); - if (r) - goto out; - - r = read_fw_regs_offset(chip); - if (r) - goto out; - - /* GPI is always disabled, also in the other driver. - */ - r = zd_iowrite32_locked(chip, 0, CR_GPI_EN); - if (r) - goto out; - r = zd_iowrite32_locked(chip, CWIN_SIZE, CR_CWMIN_CWMAX); - if (r) - goto out; - /* Currently we support IEEE 802.11g for full and high speed USB. - * It might be discussed, whether we should support pure b mode for - * full speed USB. - */ - r = set_mandatory_rates(chip, 1); - if (r) - goto out; - /* Disabling interrupts is certainly a smart thing here. - */ - r = disable_hwint(chip); - if (r) - goto out; - r = read_pod(chip, &rf_type); - if (r) - goto out; - r = hw_init(chip); - if (r) - goto out; - r = zd_rf_init_hw(&chip->rf, rf_type); - if (r) - goto out; - - r = print_fw_version(chip); - if (r) - goto out; - -#ifdef DEBUG - dump_fw_registers(chip); - r = test_init(chip); - if (r) - goto out; -#endif /* DEBUG */ - - r = read_cal_int_tables(chip); - if (r) - goto out; - - print_id(chip); -out: - mutex_unlock(&chip->mutex); - return r; -} - -static int update_pwr_int(struct zd_chip *chip, u8 channel) -{ - u8 value = chip->pwr_int_values[channel - 1]; - return zd_iowrite16_locked(chip, value, ZD_CR31); -} - -static int update_pwr_cal(struct zd_chip *chip, u8 channel) -{ - u8 value = chip->pwr_cal_values[channel-1]; - return zd_iowrite16_locked(chip, value, ZD_CR68); -} - -static int update_ofdm_cal(struct zd_chip *chip, u8 channel) -{ - struct zd_ioreq16 ioreqs[3]; - - ioreqs[0].addr = ZD_CR67; - ioreqs[0].value = chip->ofdm_cal_values[OFDM_36M_INDEX][channel-1]; - ioreqs[1].addr = ZD_CR66; - ioreqs[1].value = chip->ofdm_cal_values[OFDM_48M_INDEX][channel-1]; - ioreqs[2].addr = ZD_CR65; - ioreqs[2].value = chip->ofdm_cal_values[OFDM_54M_INDEX][channel-1]; - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int update_channel_integration_and_calibration(struct zd_chip *chip, - u8 channel) -{ - int r; - - if (!zd_rf_should_update_pwr_int(&chip->rf)) - return 0; - - r = update_pwr_int(chip, channel); - if (r) - return r; - if (zd_chip_is_zd1211b(chip)) { - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR69, 0x28 }, - {}, - { ZD_CR69, 0x2a }, - }; - - r = update_ofdm_cal(chip, channel); - if (r) - return r; - r = update_pwr_cal(chip, channel); - if (r) - return r; - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - if (r) - return r; - } - - return 0; -} - -/* The CCK baseband gain can be optionally patched by the EEPROM */ -static int patch_cck_gain(struct zd_chip *chip) -{ - int r; - u32 value; - - if (!chip->patch_cck_gain || !zd_rf_should_patch_cck_gain(&chip->rf)) - return 0; - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - r = zd_ioread32_locked(chip, &value, E2P_PHY_REG); - if (r) - return r; - dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value & 0xff); - return zd_iowrite16_locked(chip, value & 0xff, ZD_CR47); -} - -int zd_chip_set_channel(struct zd_chip *chip, u8 channel) -{ - int r, t; - - mutex_lock(&chip->mutex); - r = zd_chip_lock_phy_regs(chip); - if (r) - goto out; - r = zd_rf_set_channel(&chip->rf, channel); - if (r) - goto unlock; - r = update_channel_integration_and_calibration(chip, channel); - if (r) - goto unlock; - r = patch_cck_gain(chip); - if (r) - goto unlock; - r = patch_6m_band_edge(chip, channel); - if (r) - goto unlock; - r = zd_iowrite32_locked(chip, 0, CR_CONFIG_PHILIPS); -unlock: - t = zd_chip_unlock_phy_regs(chip); - if (t && !r) - r = t; -out: - mutex_unlock(&chip->mutex); - return r; -} - -u8 zd_chip_get_channel(struct zd_chip *chip) -{ - u8 channel; - - mutex_lock(&chip->mutex); - channel = chip->rf.channel; - mutex_unlock(&chip->mutex); - return channel; -} - -int zd_chip_control_leds(struct zd_chip *chip, enum led_status status) -{ - const zd_addr_t a[] = { - fw_reg_addr(chip, FW_REG_LED_LINK_STATUS), - CR_LED, - }; - - int r; - u16 v[ARRAY_SIZE(a)]; - struct zd_ioreq16 ioreqs[ARRAY_SIZE(a)] = { - [0] = { fw_reg_addr(chip, FW_REG_LED_LINK_STATUS) }, - [1] = { CR_LED }, - }; - u16 other_led; - - mutex_lock(&chip->mutex); - r = zd_ioread16v_locked(chip, v, (const zd_addr_t *)a, ARRAY_SIZE(a)); - if (r) - goto out; - - other_led = chip->link_led == LED1 ? LED2 : LED1; - - switch (status) { - case ZD_LED_OFF: - ioreqs[0].value = FW_LINK_OFF; - ioreqs[1].value = v[1] & ~(LED1|LED2); - break; - case ZD_LED_SCANNING: - ioreqs[0].value = FW_LINK_OFF; - ioreqs[1].value = v[1] & ~other_led; - if (get_seconds() % 3 == 0) { - ioreqs[1].value &= ~chip->link_led; - } else { - ioreqs[1].value |= chip->link_led; - } - break; - case ZD_LED_ASSOCIATED: - ioreqs[0].value = FW_LINK_TX; - ioreqs[1].value = v[1] & ~other_led; - ioreqs[1].value |= chip->link_led; - break; - default: - r = -EINVAL; - goto out; - } - - if (v[0] != ioreqs[0].value || v[1] != ioreqs[1].value) { - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - if (r) - goto out; - } - r = 0; -out: - mutex_unlock(&chip->mutex); - return r; -} - -int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates) -{ - int r; - - if (cr_rates & ~(CR_RATES_80211B|CR_RATES_80211G)) - return -EINVAL; - - mutex_lock(&chip->mutex); - r = zd_iowrite32_locked(chip, cr_rates, CR_BASIC_RATE_TBL); - mutex_unlock(&chip->mutex); - return r; -} - -static inline u8 zd_rate_from_ofdm_plcp_header(const void *rx_frame) -{ - return ZD_OFDM | zd_ofdm_plcp_header_rate(rx_frame); -} - -/** - * zd_rx_rate - report zd-rate - * @rx_frame - received frame - * @rx_status - rx_status as given by the device - * - * This function converts the rate as encoded in the received packet to the - * zd-rate, we are using on other places in the driver. - */ -u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status) -{ - u8 zd_rate; - if (status->frame_status & ZD_RX_OFDM) { - zd_rate = zd_rate_from_ofdm_plcp_header(rx_frame); - } else { - switch (zd_cck_plcp_header_signal(rx_frame)) { - case ZD_CCK_PLCP_SIGNAL_1M: - zd_rate = ZD_CCK_RATE_1M; - break; - case ZD_CCK_PLCP_SIGNAL_2M: - zd_rate = ZD_CCK_RATE_2M; - break; - case ZD_CCK_PLCP_SIGNAL_5M5: - zd_rate = ZD_CCK_RATE_5_5M; - break; - case ZD_CCK_PLCP_SIGNAL_11M: - zd_rate = ZD_CCK_RATE_11M; - break; - default: - zd_rate = 0; - } - } - - return zd_rate; -} - -int zd_chip_switch_radio_on(struct zd_chip *chip) -{ - int r; - - mutex_lock(&chip->mutex); - r = zd_switch_radio_on(&chip->rf); - mutex_unlock(&chip->mutex); - return r; -} - -int zd_chip_switch_radio_off(struct zd_chip *chip) -{ - int r; - - mutex_lock(&chip->mutex); - r = zd_switch_radio_off(&chip->rf); - mutex_unlock(&chip->mutex); - return r; -} - -int zd_chip_enable_int(struct zd_chip *chip) -{ - int r; - - mutex_lock(&chip->mutex); - r = zd_usb_enable_int(&chip->usb); - mutex_unlock(&chip->mutex); - return r; -} - -void zd_chip_disable_int(struct zd_chip *chip) -{ - mutex_lock(&chip->mutex); - zd_usb_disable_int(&chip->usb); - mutex_unlock(&chip->mutex); - - /* cancel pending interrupt work */ - cancel_work_sync(&zd_chip_to_mac(chip)->process_intr); -} - -int zd_chip_enable_rxtx(struct zd_chip *chip) -{ - int r; - - mutex_lock(&chip->mutex); - zd_usb_enable_tx(&chip->usb); - r = zd_usb_enable_rx(&chip->usb); - zd_tx_watchdog_enable(&chip->usb); - mutex_unlock(&chip->mutex); - return r; -} - -void zd_chip_disable_rxtx(struct zd_chip *chip) -{ - mutex_lock(&chip->mutex); - zd_tx_watchdog_disable(&chip->usb); - zd_usb_disable_rx(&chip->usb); - zd_usb_disable_tx(&chip->usb); - mutex_unlock(&chip->mutex); -} - -int zd_rfwritev_locked(struct zd_chip *chip, - const u32* values, unsigned int count, u8 bits) -{ - int r; - unsigned int i; - - for (i = 0; i < count; i++) { - r = zd_rfwrite_locked(chip, values[i], bits); - if (r) - return r; - } - - return 0; -} - -/* - * We can optionally program the RF directly through CR regs, if supported by - * the hardware. This is much faster than the older method. - */ -int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value) -{ - const struct zd_ioreq16 ioreqs[] = { - { ZD_CR244, (value >> 16) & 0xff }, - { ZD_CR243, (value >> 8) & 0xff }, - { ZD_CR242, value & 0xff }, - }; - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -int zd_rfwritev_cr_locked(struct zd_chip *chip, - const u32 *values, unsigned int count) -{ - int r; - unsigned int i; - - for (i = 0; i < count; i++) { - r = zd_rfwrite_cr_locked(chip, values[i]); - if (r) - return r; - } - - return 0; -} - -int zd_chip_set_multicast_hash(struct zd_chip *chip, - struct zd_mc_hash *hash) -{ - const struct zd_ioreq32 ioreqs[] = { - { CR_GROUP_HASH_P1, hash->low }, - { CR_GROUP_HASH_P2, hash->high }, - }; - - return zd_iowrite32a(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -u64 zd_chip_get_tsf(struct zd_chip *chip) -{ - int r; - static const zd_addr_t aw_pt_bi_addr[] = - { CR_TSF_LOW_PART, CR_TSF_HIGH_PART }; - u32 values[2]; - u64 tsf; - - mutex_lock(&chip->mutex); - r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr, - ARRAY_SIZE(aw_pt_bi_addr)); - mutex_unlock(&chip->mutex); - if (r) - return 0; - - tsf = values[1]; - tsf = (tsf << 32) | values[0]; - - return tsf; -} diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h deleted file mode 100644 index b03786c9f3aa..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_chip.h +++ /dev/null @@ -1,983 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#ifndef _ZD_CHIP_H -#define _ZD_CHIP_H - -#include - -#include "zd_rf.h" -#include "zd_usb.h" - -/* Header for the Media Access Controller (MAC) and the Baseband Processor - * (BBP). It appears that the ZD1211 wraps the old ZD1205 with USB glue and - * adds a processor for handling the USB protocol. - */ - -/* Address space */ -enum { - /* CONTROL REGISTERS */ - CR_START = 0x9000, - - - /* FIRMWARE */ - FW_START = 0xee00, - - - /* EEPROM */ - E2P_START = 0xf800, - E2P_LEN = 0x800, - - /* EEPROM layout */ - E2P_LOAD_CODE_LEN = 0xe, /* base 0xf800 */ - E2P_LOAD_VECT_LEN = 0x9, /* base 0xf80e */ - /* E2P_DATA indexes into this */ - E2P_DATA_LEN = 0x7e, /* base 0xf817 */ - E2P_BOOT_CODE_LEN = 0x760, /* base 0xf895 */ - E2P_INTR_VECT_LEN = 0xb, /* base 0xfff5 */ - - /* Some precomputed offsets into the EEPROM */ - E2P_DATA_OFFSET = E2P_LOAD_CODE_LEN + E2P_LOAD_VECT_LEN, - E2P_BOOT_CODE_OFFSET = E2P_DATA_OFFSET + E2P_DATA_LEN, -}; - -#define CTL_REG(offset) ((zd_addr_t)(CR_START + (offset))) -#define E2P_DATA(offset) ((zd_addr_t)(E2P_START + E2P_DATA_OFFSET + (offset))) -#define FWRAW_DATA(offset) ((zd_addr_t)(FW_START + (offset))) - -/* 8-bit hardware registers */ -#define ZD_CR0 CTL_REG(0x0000) -#define ZD_CR1 CTL_REG(0x0004) -#define ZD_CR2 CTL_REG(0x0008) -#define ZD_CR3 CTL_REG(0x000C) - -#define ZD_CR5 CTL_REG(0x0010) -/* bit 5: if set short preamble used - * bit 6: filter band - Japan channel 14 on, else off - */ -#define ZD_CR6 CTL_REG(0x0014) -#define ZD_CR7 CTL_REG(0x0018) -#define ZD_CR8 CTL_REG(0x001C) - -#define ZD_CR4 CTL_REG(0x0020) - -#define ZD_CR9 CTL_REG(0x0024) -/* bit 2: antenna switch (together with ZD_CR10) */ -#define ZD_CR10 CTL_REG(0x0028) -/* bit 1: antenna switch (together with ZD_CR9) - * RF2959 controls with ZD_CR11 radion on and off - */ -#define ZD_CR11 CTL_REG(0x002C) -/* bit 6: TX power control for OFDM - * RF2959 controls with ZD_CR10 radio on and off - */ -#define ZD_CR12 CTL_REG(0x0030) -#define ZD_CR13 CTL_REG(0x0034) -#define ZD_CR14 CTL_REG(0x0038) -#define ZD_CR15 CTL_REG(0x003C) -#define ZD_CR16 CTL_REG(0x0040) -#define ZD_CR17 CTL_REG(0x0044) -#define ZD_CR18 CTL_REG(0x0048) -#define ZD_CR19 CTL_REG(0x004C) -#define ZD_CR20 CTL_REG(0x0050) -#define ZD_CR21 CTL_REG(0x0054) -#define ZD_CR22 CTL_REG(0x0058) -#define ZD_CR23 CTL_REG(0x005C) -#define ZD_CR24 CTL_REG(0x0060) /* CCA threshold */ -#define ZD_CR25 CTL_REG(0x0064) -#define ZD_CR26 CTL_REG(0x0068) -#define ZD_CR27 CTL_REG(0x006C) -#define ZD_CR28 CTL_REG(0x0070) -#define ZD_CR29 CTL_REG(0x0074) -#define ZD_CR30 CTL_REG(0x0078) -#define ZD_CR31 CTL_REG(0x007C) /* TX power control for RF in - * CCK mode - */ -#define ZD_CR32 CTL_REG(0x0080) -#define ZD_CR33 CTL_REG(0x0084) -#define ZD_CR34 CTL_REG(0x0088) -#define ZD_CR35 CTL_REG(0x008C) -#define ZD_CR36 CTL_REG(0x0090) -#define ZD_CR37 CTL_REG(0x0094) -#define ZD_CR38 CTL_REG(0x0098) -#define ZD_CR39 CTL_REG(0x009C) -#define ZD_CR40 CTL_REG(0x00A0) -#define ZD_CR41 CTL_REG(0x00A4) -#define ZD_CR42 CTL_REG(0x00A8) -#define ZD_CR43 CTL_REG(0x00AC) -#define ZD_CR44 CTL_REG(0x00B0) -#define ZD_CR45 CTL_REG(0x00B4) -#define ZD_CR46 CTL_REG(0x00B8) -#define ZD_CR47 CTL_REG(0x00BC) /* CCK baseband gain - * (patch value might be in EEPROM) - */ -#define ZD_CR48 CTL_REG(0x00C0) -#define ZD_CR49 CTL_REG(0x00C4) -#define ZD_CR50 CTL_REG(0x00C8) -#define ZD_CR51 CTL_REG(0x00CC) /* TX power control for RF in - * 6-36M modes - */ -#define ZD_CR52 CTL_REG(0x00D0) /* TX power control for RF in - * 48M mode - */ -#define ZD_CR53 CTL_REG(0x00D4) /* TX power control for RF in - * 54M mode - */ -#define ZD_CR54 CTL_REG(0x00D8) -#define ZD_CR55 CTL_REG(0x00DC) -#define ZD_CR56 CTL_REG(0x00E0) -#define ZD_CR57 CTL_REG(0x00E4) -#define ZD_CR58 CTL_REG(0x00E8) -#define ZD_CR59 CTL_REG(0x00EC) -#define ZD_CR60 CTL_REG(0x00F0) -#define ZD_CR61 CTL_REG(0x00F4) -#define ZD_CR62 CTL_REG(0x00F8) -#define ZD_CR63 CTL_REG(0x00FC) -#define ZD_CR64 CTL_REG(0x0100) -#define ZD_CR65 CTL_REG(0x0104) /* OFDM 54M calibration */ -#define ZD_CR66 CTL_REG(0x0108) /* OFDM 48M calibration */ -#define ZD_CR67 CTL_REG(0x010C) /* OFDM 36M calibration */ -#define ZD_CR68 CTL_REG(0x0110) /* CCK calibration */ -#define ZD_CR69 CTL_REG(0x0114) -#define ZD_CR70 CTL_REG(0x0118) -#define ZD_CR71 CTL_REG(0x011C) -#define ZD_CR72 CTL_REG(0x0120) -#define ZD_CR73 CTL_REG(0x0124) -#define ZD_CR74 CTL_REG(0x0128) -#define ZD_CR75 CTL_REG(0x012C) -#define ZD_CR76 CTL_REG(0x0130) -#define ZD_CR77 CTL_REG(0x0134) -#define ZD_CR78 CTL_REG(0x0138) -#define ZD_CR79 CTL_REG(0x013C) -#define ZD_CR80 CTL_REG(0x0140) -#define ZD_CR81 CTL_REG(0x0144) -#define ZD_CR82 CTL_REG(0x0148) -#define ZD_CR83 CTL_REG(0x014C) -#define ZD_CR84 CTL_REG(0x0150) -#define ZD_CR85 CTL_REG(0x0154) -#define ZD_CR86 CTL_REG(0x0158) -#define ZD_CR87 CTL_REG(0x015C) -#define ZD_CR88 CTL_REG(0x0160) -#define ZD_CR89 CTL_REG(0x0164) -#define ZD_CR90 CTL_REG(0x0168) -#define ZD_CR91 CTL_REG(0x016C) -#define ZD_CR92 CTL_REG(0x0170) -#define ZD_CR93 CTL_REG(0x0174) -#define ZD_CR94 CTL_REG(0x0178) -#define ZD_CR95 CTL_REG(0x017C) -#define ZD_CR96 CTL_REG(0x0180) -#define ZD_CR97 CTL_REG(0x0184) -#define ZD_CR98 CTL_REG(0x0188) -#define ZD_CR99 CTL_REG(0x018C) -#define ZD_CR100 CTL_REG(0x0190) -#define ZD_CR101 CTL_REG(0x0194) -#define ZD_CR102 CTL_REG(0x0198) -#define ZD_CR103 CTL_REG(0x019C) -#define ZD_CR104 CTL_REG(0x01A0) -#define ZD_CR105 CTL_REG(0x01A4) -#define ZD_CR106 CTL_REG(0x01A8) -#define ZD_CR107 CTL_REG(0x01AC) -#define ZD_CR108 CTL_REG(0x01B0) -#define ZD_CR109 CTL_REG(0x01B4) -#define ZD_CR110 CTL_REG(0x01B8) -#define ZD_CR111 CTL_REG(0x01BC) -#define ZD_CR112 CTL_REG(0x01C0) -#define ZD_CR113 CTL_REG(0x01C4) -#define ZD_CR114 CTL_REG(0x01C8) -#define ZD_CR115 CTL_REG(0x01CC) -#define ZD_CR116 CTL_REG(0x01D0) -#define ZD_CR117 CTL_REG(0x01D4) -#define ZD_CR118 CTL_REG(0x01D8) -#define ZD_CR119 CTL_REG(0x01DC) -#define ZD_CR120 CTL_REG(0x01E0) -#define ZD_CR121 CTL_REG(0x01E4) -#define ZD_CR122 CTL_REG(0x01E8) -#define ZD_CR123 CTL_REG(0x01EC) -#define ZD_CR124 CTL_REG(0x01F0) -#define ZD_CR125 CTL_REG(0x01F4) -#define ZD_CR126 CTL_REG(0x01F8) -#define ZD_CR127 CTL_REG(0x01FC) -#define ZD_CR128 CTL_REG(0x0200) -#define ZD_CR129 CTL_REG(0x0204) -#define ZD_CR130 CTL_REG(0x0208) -#define ZD_CR131 CTL_REG(0x020C) -#define ZD_CR132 CTL_REG(0x0210) -#define ZD_CR133 CTL_REG(0x0214) -#define ZD_CR134 CTL_REG(0x0218) -#define ZD_CR135 CTL_REG(0x021C) -#define ZD_CR136 CTL_REG(0x0220) -#define ZD_CR137 CTL_REG(0x0224) -#define ZD_CR138 CTL_REG(0x0228) -#define ZD_CR139 CTL_REG(0x022C) -#define ZD_CR140 CTL_REG(0x0230) -#define ZD_CR141 CTL_REG(0x0234) -#define ZD_CR142 CTL_REG(0x0238) -#define ZD_CR143 CTL_REG(0x023C) -#define ZD_CR144 CTL_REG(0x0240) -#define ZD_CR145 CTL_REG(0x0244) -#define ZD_CR146 CTL_REG(0x0248) -#define ZD_CR147 CTL_REG(0x024C) -#define ZD_CR148 CTL_REG(0x0250) -#define ZD_CR149 CTL_REG(0x0254) -#define ZD_CR150 CTL_REG(0x0258) -#define ZD_CR151 CTL_REG(0x025C) -#define ZD_CR152 CTL_REG(0x0260) -#define ZD_CR153 CTL_REG(0x0264) -#define ZD_CR154 CTL_REG(0x0268) -#define ZD_CR155 CTL_REG(0x026C) -#define ZD_CR156 CTL_REG(0x0270) -#define ZD_CR157 CTL_REG(0x0274) -#define ZD_CR158 CTL_REG(0x0278) -#define ZD_CR159 CTL_REG(0x027C) -#define ZD_CR160 CTL_REG(0x0280) -#define ZD_CR161 CTL_REG(0x0284) -#define ZD_CR162 CTL_REG(0x0288) -#define ZD_CR163 CTL_REG(0x028C) -#define ZD_CR164 CTL_REG(0x0290) -#define ZD_CR165 CTL_REG(0x0294) -#define ZD_CR166 CTL_REG(0x0298) -#define ZD_CR167 CTL_REG(0x029C) -#define ZD_CR168 CTL_REG(0x02A0) -#define ZD_CR169 CTL_REG(0x02A4) -#define ZD_CR170 CTL_REG(0x02A8) -#define ZD_CR171 CTL_REG(0x02AC) -#define ZD_CR172 CTL_REG(0x02B0) -#define ZD_CR173 CTL_REG(0x02B4) -#define ZD_CR174 CTL_REG(0x02B8) -#define ZD_CR175 CTL_REG(0x02BC) -#define ZD_CR176 CTL_REG(0x02C0) -#define ZD_CR177 CTL_REG(0x02C4) -#define ZD_CR178 CTL_REG(0x02C8) -#define ZD_CR179 CTL_REG(0x02CC) -#define ZD_CR180 CTL_REG(0x02D0) -#define ZD_CR181 CTL_REG(0x02D4) -#define ZD_CR182 CTL_REG(0x02D8) -#define ZD_CR183 CTL_REG(0x02DC) -#define ZD_CR184 CTL_REG(0x02E0) -#define ZD_CR185 CTL_REG(0x02E4) -#define ZD_CR186 CTL_REG(0x02E8) -#define ZD_CR187 CTL_REG(0x02EC) -#define ZD_CR188 CTL_REG(0x02F0) -#define ZD_CR189 CTL_REG(0x02F4) -#define ZD_CR190 CTL_REG(0x02F8) -#define ZD_CR191 CTL_REG(0x02FC) -#define ZD_CR192 CTL_REG(0x0300) -#define ZD_CR193 CTL_REG(0x0304) -#define ZD_CR194 CTL_REG(0x0308) -#define ZD_CR195 CTL_REG(0x030C) -#define ZD_CR196 CTL_REG(0x0310) -#define ZD_CR197 CTL_REG(0x0314) -#define ZD_CR198 CTL_REG(0x0318) -#define ZD_CR199 CTL_REG(0x031C) -#define ZD_CR200 CTL_REG(0x0320) -#define ZD_CR201 CTL_REG(0x0324) -#define ZD_CR202 CTL_REG(0x0328) -#define ZD_CR203 CTL_REG(0x032C) /* I2C bus template value & flash - * control - */ -#define ZD_CR204 CTL_REG(0x0330) -#define ZD_CR205 CTL_REG(0x0334) -#define ZD_CR206 CTL_REG(0x0338) -#define ZD_CR207 CTL_REG(0x033C) -#define ZD_CR208 CTL_REG(0x0340) -#define ZD_CR209 CTL_REG(0x0344) -#define ZD_CR210 CTL_REG(0x0348) -#define ZD_CR211 CTL_REG(0x034C) -#define ZD_CR212 CTL_REG(0x0350) -#define ZD_CR213 CTL_REG(0x0354) -#define ZD_CR214 CTL_REG(0x0358) -#define ZD_CR215 CTL_REG(0x035C) -#define ZD_CR216 CTL_REG(0x0360) -#define ZD_CR217 CTL_REG(0x0364) -#define ZD_CR218 CTL_REG(0x0368) -#define ZD_CR219 CTL_REG(0x036C) -#define ZD_CR220 CTL_REG(0x0370) -#define ZD_CR221 CTL_REG(0x0374) -#define ZD_CR222 CTL_REG(0x0378) -#define ZD_CR223 CTL_REG(0x037C) -#define ZD_CR224 CTL_REG(0x0380) -#define ZD_CR225 CTL_REG(0x0384) -#define ZD_CR226 CTL_REG(0x0388) -#define ZD_CR227 CTL_REG(0x038C) -#define ZD_CR228 CTL_REG(0x0390) -#define ZD_CR229 CTL_REG(0x0394) -#define ZD_CR230 CTL_REG(0x0398) -#define ZD_CR231 CTL_REG(0x039C) -#define ZD_CR232 CTL_REG(0x03A0) -#define ZD_CR233 CTL_REG(0x03A4) -#define ZD_CR234 CTL_REG(0x03A8) -#define ZD_CR235 CTL_REG(0x03AC) -#define ZD_CR236 CTL_REG(0x03B0) - -#define ZD_CR240 CTL_REG(0x03C0) -/* bit 7: host-controlled RF register writes - * ZD_CR241-ZD_CR245: for hardware controlled writing of RF bits, not needed for - * USB - */ -#define ZD_CR241 CTL_REG(0x03C4) -#define ZD_CR242 CTL_REG(0x03C8) -#define ZD_CR243 CTL_REG(0x03CC) -#define ZD_CR244 CTL_REG(0x03D0) -#define ZD_CR245 CTL_REG(0x03D4) - -#define ZD_CR251 CTL_REG(0x03EC) /* only used for activation and - * deactivation of Airoha RFs AL2230 - * and AL7230B - */ -#define ZD_CR252 CTL_REG(0x03F0) -#define ZD_CR253 CTL_REG(0x03F4) -#define ZD_CR254 CTL_REG(0x03F8) -#define ZD_CR255 CTL_REG(0x03FC) - -#define CR_MAX_PHY_REG 255 - -/* Taken from the ZYDAS driver, not all of them are relevant for the ZD1211 - * driver. - */ - -#define CR_RF_IF_CLK CTL_REG(0x0400) -#define CR_RF_IF_DATA CTL_REG(0x0404) -#define CR_PE1_PE2 CTL_REG(0x0408) -#define CR_PE2_DLY CTL_REG(0x040C) -#define CR_LE1 CTL_REG(0x0410) -#define CR_LE2 CTL_REG(0x0414) -/* Seems to enable/disable GPI (General Purpose IO?) */ -#define CR_GPI_EN CTL_REG(0x0418) -#define CR_RADIO_PD CTL_REG(0x042C) -#define CR_RF2948_PD CTL_REG(0x042C) -#define CR_ENABLE_PS_MANUAL_AGC CTL_REG(0x043C) -#define CR_CONFIG_PHILIPS CTL_REG(0x0440) -#define CR_SA2400_SER_AP CTL_REG(0x0444) -#define CR_I2C_WRITE CTL_REG(0x0444) -#define CR_SA2400_SER_RP CTL_REG(0x0448) -#define CR_RADIO_PE CTL_REG(0x0458) -#define CR_RST_BUS_MASTER CTL_REG(0x045C) -#define CR_RFCFG CTL_REG(0x0464) -#define CR_HSTSCHG CTL_REG(0x046C) -#define CR_PHY_ON CTL_REG(0x0474) -#define CR_RX_DELAY CTL_REG(0x0478) -#define CR_RX_PE_DELAY CTL_REG(0x047C) -#define CR_GPIO_1 CTL_REG(0x0490) -#define CR_GPIO_2 CTL_REG(0x0494) -#define CR_EncryBufMux CTL_REG(0x04A8) -#define CR_PS_CTRL CTL_REG(0x0500) -#define CR_ADDA_PWR_DWN CTL_REG(0x0504) -#define CR_ADDA_MBIAS_WARMTIME CTL_REG(0x0508) -#define CR_MAC_PS_STATE CTL_REG(0x050C) - -#define CR_INTERRUPT CTL_REG(0x0510) -#define INT_TX_COMPLETE (1 << 0) -#define INT_RX_COMPLETE (1 << 1) -#define INT_RETRY_FAIL (1 << 2) -#define INT_WAKEUP (1 << 3) -#define INT_DTIM_NOTIFY (1 << 5) -#define INT_CFG_NEXT_BCN (1 << 6) -#define INT_BUS_ABORT (1 << 7) -#define INT_TX_FIFO_READY (1 << 8) -#define INT_UART (1 << 9) -#define INT_TX_COMPLETE_EN (1 << 16) -#define INT_RX_COMPLETE_EN (1 << 17) -#define INT_RETRY_FAIL_EN (1 << 18) -#define INT_WAKEUP_EN (1 << 19) -#define INT_DTIM_NOTIFY_EN (1 << 21) -#define INT_CFG_NEXT_BCN_EN (1 << 22) -#define INT_BUS_ABORT_EN (1 << 23) -#define INT_TX_FIFO_READY_EN (1 << 24) -#define INT_UART_EN (1 << 25) - -#define CR_TSF_LOW_PART CTL_REG(0x0514) -#define CR_TSF_HIGH_PART CTL_REG(0x0518) - -/* Following three values are in time units (1024us) - * Following condition must be met: - * atim < tbtt < bcn - */ -#define CR_ATIM_WND_PERIOD CTL_REG(0x051C) -#define CR_BCN_INTERVAL CTL_REG(0x0520) -#define CR_PRE_TBTT CTL_REG(0x0524) -/* in units of TU(1024us) */ - -/* for UART support */ -#define CR_UART_RBR_THR_DLL CTL_REG(0x0540) -#define CR_UART_DLM_IER CTL_REG(0x0544) -#define CR_UART_IIR_FCR CTL_REG(0x0548) -#define CR_UART_LCR CTL_REG(0x054c) -#define CR_UART_MCR CTL_REG(0x0550) -#define CR_UART_LSR CTL_REG(0x0554) -#define CR_UART_MSR CTL_REG(0x0558) -#define CR_UART_ECR CTL_REG(0x055c) -#define CR_UART_STATUS CTL_REG(0x0560) - -#define CR_PCI_TX_ADDR_P1 CTL_REG(0x0600) -#define CR_PCI_TX_AddR_P2 CTL_REG(0x0604) -#define CR_PCI_RX_AddR_P1 CTL_REG(0x0608) -#define CR_PCI_RX_AddR_P2 CTL_REG(0x060C) - -/* must be overwritten if custom MAC address will be used */ -#define CR_MAC_ADDR_P1 CTL_REG(0x0610) -#define CR_MAC_ADDR_P2 CTL_REG(0x0614) -#define CR_BSSID_P1 CTL_REG(0x0618) -#define CR_BSSID_P2 CTL_REG(0x061C) -#define CR_BCN_PLCP_CFG CTL_REG(0x0620) - -/* Group hash table for filtering incoming packets. - * - * The group hash table is 64 bit large and split over two parts. The first - * part is the lower part. The upper 6 bits of the last byte of the target - * address are used as index. Packets are received if the hash table bit is - * set. This is used for multicast handling, but for broadcasts (address - * ff:ff:ff:ff:ff:ff) the highest bit in the second table must also be set. - */ -#define CR_GROUP_HASH_P1 CTL_REG(0x0624) -#define CR_GROUP_HASH_P2 CTL_REG(0x0628) - -#define CR_RX_TIMEOUT CTL_REG(0x062C) - -/* Basic rates supported by the BSS. When producing ACK or CTS messages, the - * device will use a rate in this table that is less than or equal to the rate - * of the incoming frame which prompted the response. */ -#define CR_BASIC_RATE_TBL CTL_REG(0x0630) -#define CR_RATE_1M (1 << 0) /* 802.11b */ -#define CR_RATE_2M (1 << 1) /* 802.11b */ -#define CR_RATE_5_5M (1 << 2) /* 802.11b */ -#define CR_RATE_11M (1 << 3) /* 802.11b */ -#define CR_RATE_6M (1 << 8) /* 802.11g */ -#define CR_RATE_9M (1 << 9) /* 802.11g */ -#define CR_RATE_12M (1 << 10) /* 802.11g */ -#define CR_RATE_18M (1 << 11) /* 802.11g */ -#define CR_RATE_24M (1 << 12) /* 802.11g */ -#define CR_RATE_36M (1 << 13) /* 802.11g */ -#define CR_RATE_48M (1 << 14) /* 802.11g */ -#define CR_RATE_54M (1 << 15) /* 802.11g */ -#define CR_RATES_80211G 0xff00 -#define CR_RATES_80211B 0x000f - -/* Mandatory rates required in the BSS. When producing ACK or CTS messages, if - * the device could not find an appropriate rate in CR_BASIC_RATE_TBL, it will - * look for a rate in this table that is less than or equal to the rate of - * the incoming frame. */ -#define CR_MANDATORY_RATE_TBL CTL_REG(0x0634) -#define CR_RTS_CTS_RATE CTL_REG(0x0638) - -/* These are all bit indexes in CR_RTS_CTS_RATE, so remember to shift. */ -#define RTSCTS_SH_RTS_RATE 0 -#define RTSCTS_SH_EXP_CTS_RATE 4 -#define RTSCTS_SH_RTS_MOD_TYPE 8 -#define RTSCTS_SH_RTS_PMB_TYPE 9 -#define RTSCTS_SH_CTS_RATE 16 -#define RTSCTS_SH_CTS_MOD_TYPE 24 -#define RTSCTS_SH_CTS_PMB_TYPE 25 - -#define CR_WEP_PROTECT CTL_REG(0x063C) -#define CR_RX_THRESHOLD CTL_REG(0x0640) - -/* register for controlling the LEDS */ -#define CR_LED CTL_REG(0x0644) -/* masks for controlling LEDs */ -#define LED1 (1 << 8) -#define LED2 (1 << 9) -#define LED_SW (1 << 10) - -/* Seems to indicate that the configuration is over. - */ -#define CR_AFTER_PNP CTL_REG(0x0648) -#define CR_ACK_TIME_80211 CTL_REG(0x0658) - -#define CR_RX_OFFSET CTL_REG(0x065c) - -#define CR_BCN_LENGTH CTL_REG(0x0664) -#define CR_PHY_DELAY CTL_REG(0x066C) -#define CR_BCN_FIFO CTL_REG(0x0670) -#define CR_SNIFFER_ON CTL_REG(0x0674) - -#define CR_ENCRYPTION_TYPE CTL_REG(0x0678) -#define NO_WEP 0 -#define WEP64 1 -#define WEP128 5 -#define WEP256 6 -#define ENC_SNIFFER 8 - -#define CR_ZD1211_RETRY_MAX CTL_REG(0x067C) - -#define CR_REG1 CTL_REG(0x0680) -/* Setting the bit UNLOCK_PHY_REGS disallows the write access to physical - * registers, so one could argue it is a LOCK bit. But calling it - * LOCK_PHY_REGS makes it confusing. - */ -#define UNLOCK_PHY_REGS (1 << 7) - -#define CR_DEVICE_STATE CTL_REG(0x0684) -#define CR_UNDERRUN_CNT CTL_REG(0x0688) - -#define CR_RX_FILTER CTL_REG(0x068c) -#define RX_FILTER_ASSOC_REQUEST (1 << 0) -#define RX_FILTER_ASSOC_RESPONSE (1 << 1) -#define RX_FILTER_REASSOC_REQUEST (1 << 2) -#define RX_FILTER_REASSOC_RESPONSE (1 << 3) -#define RX_FILTER_PROBE_REQUEST (1 << 4) -#define RX_FILTER_PROBE_RESPONSE (1 << 5) -/* bits 6 and 7 reserved */ -#define RX_FILTER_BEACON (1 << 8) -#define RX_FILTER_ATIM (1 << 9) -#define RX_FILTER_DISASSOC (1 << 10) -#define RX_FILTER_AUTH (1 << 11) -#define RX_FILTER_DEAUTH (1 << 12) -#define RX_FILTER_PSPOLL (1 << 26) -#define RX_FILTER_RTS (1 << 27) -#define RX_FILTER_CTS (1 << 28) -#define RX_FILTER_ACK (1 << 29) -#define RX_FILTER_CFEND (1 << 30) -#define RX_FILTER_CFACK (1 << 31) - -/* Enable bits for all frames you are interested in. */ -#define STA_RX_FILTER (RX_FILTER_ASSOC_REQUEST | RX_FILTER_ASSOC_RESPONSE | \ - RX_FILTER_REASSOC_REQUEST | RX_FILTER_REASSOC_RESPONSE | \ - RX_FILTER_PROBE_REQUEST | RX_FILTER_PROBE_RESPONSE | \ - (0x3 << 6) /* vendor driver sets these reserved bits */ | \ - RX_FILTER_BEACON | RX_FILTER_ATIM | RX_FILTER_DISASSOC | \ - RX_FILTER_AUTH | RX_FILTER_DEAUTH | \ - (0x7 << 13) /* vendor driver sets these reserved bits */ | \ - RX_FILTER_PSPOLL | RX_FILTER_ACK) /* 0x2400ffff */ - -#define RX_FILTER_CTRL (RX_FILTER_RTS | RX_FILTER_CTS | \ - RX_FILTER_CFEND | RX_FILTER_CFACK) - -#define BCN_MODE_AP 0x1000000 -#define BCN_MODE_IBSS 0x2000000 - -/* Monitor mode sets filter to 0xfffff */ - -#define CR_ACK_TIMEOUT_EXT CTL_REG(0x0690) -#define CR_BCN_FIFO_SEMAPHORE CTL_REG(0x0694) - -#define CR_IFS_VALUE CTL_REG(0x0698) -#define IFS_VALUE_DIFS_SH 0 -#define IFS_VALUE_EIFS_SH 12 -#define IFS_VALUE_SIFS_SH 24 -#define IFS_VALUE_DEFAULT (( 50 << IFS_VALUE_DIFS_SH) | \ - (1148 << IFS_VALUE_EIFS_SH) | \ - ( 10 << IFS_VALUE_SIFS_SH)) - -#define CR_RX_TIME_OUT CTL_REG(0x069C) -#define CR_TOTAL_RX_FRM CTL_REG(0x06A0) -#define CR_CRC32_CNT CTL_REG(0x06A4) -#define CR_CRC16_CNT CTL_REG(0x06A8) -#define CR_DECRYPTION_ERR_UNI CTL_REG(0x06AC) -#define CR_RX_FIFO_OVERRUN CTL_REG(0x06B0) - -#define CR_DECRYPTION_ERR_MUL CTL_REG(0x06BC) - -#define CR_NAV_CNT CTL_REG(0x06C4) -#define CR_NAV_CCA CTL_REG(0x06C8) -#define CR_RETRY_CNT CTL_REG(0x06CC) - -#define CR_READ_TCB_ADDR CTL_REG(0x06E8) -#define CR_READ_RFD_ADDR CTL_REG(0x06EC) -#define CR_CWMIN_CWMAX CTL_REG(0x06F0) -#define CR_TOTAL_TX_FRM CTL_REG(0x06F4) - -/* CAM: Continuous Access Mode (power management) */ -#define CR_CAM_MODE CTL_REG(0x0700) -#define MODE_IBSS 0x0 -#define MODE_AP 0x1 -#define MODE_STA 0x2 -#define MODE_AP_WDS 0x3 - -#define CR_CAM_ROLL_TB_LOW CTL_REG(0x0704) -#define CR_CAM_ROLL_TB_HIGH CTL_REG(0x0708) -#define CR_CAM_ADDRESS CTL_REG(0x070C) -#define CR_CAM_DATA CTL_REG(0x0710) - -#define CR_ROMDIR CTL_REG(0x0714) - -#define CR_DECRY_ERR_FLG_LOW CTL_REG(0x0714) -#define CR_DECRY_ERR_FLG_HIGH CTL_REG(0x0718) - -#define CR_WEPKEY0 CTL_REG(0x0720) -#define CR_WEPKEY1 CTL_REG(0x0724) -#define CR_WEPKEY2 CTL_REG(0x0728) -#define CR_WEPKEY3 CTL_REG(0x072C) -#define CR_WEPKEY4 CTL_REG(0x0730) -#define CR_WEPKEY5 CTL_REG(0x0734) -#define CR_WEPKEY6 CTL_REG(0x0738) -#define CR_WEPKEY7 CTL_REG(0x073C) -#define CR_WEPKEY8 CTL_REG(0x0740) -#define CR_WEPKEY9 CTL_REG(0x0744) -#define CR_WEPKEY10 CTL_REG(0x0748) -#define CR_WEPKEY11 CTL_REG(0x074C) -#define CR_WEPKEY12 CTL_REG(0x0750) -#define CR_WEPKEY13 CTL_REG(0x0754) -#define CR_WEPKEY14 CTL_REG(0x0758) -#define CR_WEPKEY15 CTL_REG(0x075c) -#define CR_TKIP_MODE CTL_REG(0x0760) - -#define CR_EEPROM_PROTECT0 CTL_REG(0x0758) -#define CR_EEPROM_PROTECT1 CTL_REG(0x075C) - -#define CR_DBG_FIFO_RD CTL_REG(0x0800) -#define CR_DBG_SELECT CTL_REG(0x0804) -#define CR_FIFO_Length CTL_REG(0x0808) - - -#define CR_RSSI_MGC CTL_REG(0x0810) - -#define CR_PON CTL_REG(0x0818) -#define CR_RX_ON CTL_REG(0x081C) -#define CR_TX_ON CTL_REG(0x0820) -#define CR_CHIP_EN CTL_REG(0x0824) -#define CR_LO_SW CTL_REG(0x0828) -#define CR_TXRX_SW CTL_REG(0x082C) -#define CR_S_MD CTL_REG(0x0830) - -#define CR_USB_DEBUG_PORT CTL_REG(0x0888) -#define CR_ZD1211B_CWIN_MAX_MIN_AC0 CTL_REG(0x0b00) -#define CR_ZD1211B_CWIN_MAX_MIN_AC1 CTL_REG(0x0b04) -#define CR_ZD1211B_CWIN_MAX_MIN_AC2 CTL_REG(0x0b08) -#define CR_ZD1211B_CWIN_MAX_MIN_AC3 CTL_REG(0x0b0c) -#define CR_ZD1211B_AIFS_CTL1 CTL_REG(0x0b10) -#define CR_ZD1211B_AIFS_CTL2 CTL_REG(0x0b14) -#define CR_ZD1211B_TXOP CTL_REG(0x0b20) -#define CR_ZD1211B_RETRY_MAX CTL_REG(0x0b28) - -/* Value for CR_ZD1211_RETRY_MAX & CR_ZD1211B_RETRY_MAX. Vendor driver uses 2, - * we use 0. The first rate is tried (count+2), then all next rates are tried - * twice, until 1 Mbits is tried. */ -#define ZD1211_RETRY_COUNT 0 -#define ZD1211B_RETRY_COUNT \ - (ZD1211_RETRY_COUNT << 0)| \ - (ZD1211_RETRY_COUNT << 8)| \ - (ZD1211_RETRY_COUNT << 16)| \ - (ZD1211_RETRY_COUNT << 24) - -/* Used to detect PLL lock */ -#define UW2453_INTR_REG ((zd_addr_t)0x85c1) - -#define CWIN_SIZE 0x007f043f - - -#define HWINT_ENABLED \ - (INT_TX_COMPLETE_EN| \ - INT_RX_COMPLETE_EN| \ - INT_RETRY_FAIL_EN| \ - INT_WAKEUP_EN| \ - INT_CFG_NEXT_BCN_EN) - -#define HWINT_DISABLED 0 - -#define E2P_PWR_INT_GUARD 8 -#define E2P_CHANNEL_COUNT 14 - -/* If you compare this addresses with the ZYDAS orignal driver, please notify - * that we use word mapping for the EEPROM. - */ - -/* - * Upper 16 bit contains the regulatory domain. - */ -#define E2P_SUBID E2P_DATA(0x00) -#define E2P_POD E2P_DATA(0x02) -#define E2P_MAC_ADDR_P1 E2P_DATA(0x04) -#define E2P_MAC_ADDR_P2 E2P_DATA(0x06) -#define E2P_PWR_CAL_VALUE1 E2P_DATA(0x08) -#define E2P_PWR_CAL_VALUE2 E2P_DATA(0x0a) -#define E2P_PWR_CAL_VALUE3 E2P_DATA(0x0c) -#define E2P_PWR_CAL_VALUE4 E2P_DATA(0x0e) -#define E2P_PWR_INT_VALUE1 E2P_DATA(0x10) -#define E2P_PWR_INT_VALUE2 E2P_DATA(0x12) -#define E2P_PWR_INT_VALUE3 E2P_DATA(0x14) -#define E2P_PWR_INT_VALUE4 E2P_DATA(0x16) - -/* Contains a bit for each allowed channel. It gives for Europe (ETSI 0x30) - * also only 11 channels. */ -#define E2P_ALLOWED_CHANNEL E2P_DATA(0x18) - -#define E2P_DEVICE_VER E2P_DATA(0x20) -#define E2P_PHY_REG E2P_DATA(0x25) -#define E2P_36M_CAL_VALUE1 E2P_DATA(0x28) -#define E2P_36M_CAL_VALUE2 E2P_DATA(0x2a) -#define E2P_36M_CAL_VALUE3 E2P_DATA(0x2c) -#define E2P_36M_CAL_VALUE4 E2P_DATA(0x2e) -#define E2P_11A_INT_VALUE1 E2P_DATA(0x30) -#define E2P_11A_INT_VALUE2 E2P_DATA(0x32) -#define E2P_11A_INT_VALUE3 E2P_DATA(0x34) -#define E2P_11A_INT_VALUE4 E2P_DATA(0x36) -#define E2P_48M_CAL_VALUE1 E2P_DATA(0x38) -#define E2P_48M_CAL_VALUE2 E2P_DATA(0x3a) -#define E2P_48M_CAL_VALUE3 E2P_DATA(0x3c) -#define E2P_48M_CAL_VALUE4 E2P_DATA(0x3e) -#define E2P_48M_INT_VALUE1 E2P_DATA(0x40) -#define E2P_48M_INT_VALUE2 E2P_DATA(0x42) -#define E2P_48M_INT_VALUE3 E2P_DATA(0x44) -#define E2P_48M_INT_VALUE4 E2P_DATA(0x46) -#define E2P_54M_CAL_VALUE1 E2P_DATA(0x48) /* ??? */ -#define E2P_54M_CAL_VALUE2 E2P_DATA(0x4a) -#define E2P_54M_CAL_VALUE3 E2P_DATA(0x4c) -#define E2P_54M_CAL_VALUE4 E2P_DATA(0x4e) -#define E2P_54M_INT_VALUE1 E2P_DATA(0x50) -#define E2P_54M_INT_VALUE2 E2P_DATA(0x52) -#define E2P_54M_INT_VALUE3 E2P_DATA(0x54) -#define E2P_54M_INT_VALUE4 E2P_DATA(0x56) - -/* This word contains the base address of the FW_REG_ registers below */ -#define FWRAW_REGS_ADDR FWRAW_DATA(0x1d) - -/* All 16 bit values, offset from the address in FWRAW_REGS_ADDR */ -enum { - FW_REG_FIRMWARE_VER = 0, - /* non-zero if USB high speed connection */ - FW_REG_USB_SPEED = 1, - FW_REG_FIX_TX_RATE = 2, - /* Seems to be able to control LEDs over the firmware */ - FW_REG_LED_LINK_STATUS = 3, - FW_REG_SOFT_RESET = 4, - FW_REG_FLASH_CHK = 5, -}; - -/* Values for FW_LINK_STATUS */ -#define FW_LINK_OFF 0x0 -#define FW_LINK_TX 0x1 -/* 0x2 - link led on? */ - -enum { - /* indices for ofdm_cal_values */ - OFDM_36M_INDEX = 0, - OFDM_48M_INDEX = 1, - OFDM_54M_INDEX = 2, -}; - -struct zd_chip { - struct zd_usb usb; - struct zd_rf rf; - struct mutex mutex; - /* Base address of FW_REG_ registers */ - zd_addr_t fw_regs_base; - /* EepSetPoint in the vendor driver */ - u8 pwr_cal_values[E2P_CHANNEL_COUNT]; - /* integration values in the vendor driver */ - u8 pwr_int_values[E2P_CHANNEL_COUNT]; - /* SetPointOFDM in the vendor driver */ - u8 ofdm_cal_values[3][E2P_CHANNEL_COUNT]; - u16 link_led; - unsigned int pa_type:4, - patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1, - new_phy_layout:1, al2230s_bit:1, - supports_tx_led:1; -}; - -static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb) -{ - return container_of(usb, struct zd_chip, usb); -} - -static inline struct zd_chip *zd_rf_to_chip(struct zd_rf *rf) -{ - return container_of(rf, struct zd_chip, rf); -} - -#define zd_chip_dev(chip) (&(chip)->usb.intf->dev) - -void zd_chip_init(struct zd_chip *chip, - struct ieee80211_hw *hw, - struct usb_interface *intf); -void zd_chip_clear(struct zd_chip *chip); -int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr); -int zd_chip_init_hw(struct zd_chip *chip); -int zd_chip_reset(struct zd_chip *chip); - -static inline int zd_chip_is_zd1211b(struct zd_chip *chip) -{ - return chip->usb.is_zd1211b; -} - -static inline int zd_ioread16v_locked(struct zd_chip *chip, u16 *values, - const zd_addr_t *addresses, - unsigned int count) -{ - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - return zd_usb_ioread16v(&chip->usb, values, addresses, count); -} - -static inline int zd_ioread16_locked(struct zd_chip *chip, u16 *value, - const zd_addr_t addr) -{ - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - return zd_usb_ioread16(&chip->usb, value, addr); -} - -int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, - const zd_addr_t *addresses, unsigned int count); - -static inline int zd_ioread32_locked(struct zd_chip *chip, u32 *value, - const zd_addr_t addr) -{ - return zd_ioread32v_locked(chip, value, &addr, 1); -} - -static inline int zd_iowrite16_locked(struct zd_chip *chip, u16 value, - zd_addr_t addr) -{ - struct zd_ioreq16 ioreq; - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - ioreq.addr = addr; - ioreq.value = value; - - return zd_usb_iowrite16v(&chip->usb, &ioreq, 1); -} - -int zd_iowrite16a_locked(struct zd_chip *chip, - const struct zd_ioreq16 *ioreqs, unsigned int count); - -int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, - unsigned int count); - -static inline int zd_iowrite32_locked(struct zd_chip *chip, u32 value, - zd_addr_t addr) -{ - struct zd_ioreq32 ioreq; - - ioreq.addr = addr; - ioreq.value = value; - - return _zd_iowrite32v_locked(chip, &ioreq, 1); -} - -int zd_iowrite32a_locked(struct zd_chip *chip, - const struct zd_ioreq32 *ioreqs, unsigned int count); - -static inline int zd_rfwrite_locked(struct zd_chip *chip, u32 value, u8 bits) -{ - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - return zd_usb_rfwrite(&chip->usb, value, bits); -} - -int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value); - -int zd_rfwritev_locked(struct zd_chip *chip, - const u32* values, unsigned int count, u8 bits); -int zd_rfwritev_cr_locked(struct zd_chip *chip, - const u32* values, unsigned int count); - -/* Locking functions for reading and writing registers. - * The different parameters are intentional. - */ -int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value); -int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value); -int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value); -int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value); -int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, - u32 *values, unsigned int count); -int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, - unsigned int count); - -int zd_chip_set_channel(struct zd_chip *chip, u8 channel); -static inline u8 _zd_chip_get_channel(struct zd_chip *chip) -{ - return chip->rf.channel; -} -u8 zd_chip_get_channel(struct zd_chip *chip); -int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain); -int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr); -int zd_write_bssid(struct zd_chip *chip, const u8 *bssid); -int zd_chip_switch_radio_on(struct zd_chip *chip); -int zd_chip_switch_radio_off(struct zd_chip *chip); -int zd_chip_enable_int(struct zd_chip *chip); -void zd_chip_disable_int(struct zd_chip *chip); -int zd_chip_enable_rxtx(struct zd_chip *chip); -void zd_chip_disable_rxtx(struct zd_chip *chip); -int zd_chip_enable_hwint(struct zd_chip *chip); -int zd_chip_disable_hwint(struct zd_chip *chip); -int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel); -int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, int preamble); - -static inline int zd_get_encryption_type(struct zd_chip *chip, u32 *type) -{ - return zd_ioread32(chip, CR_ENCRYPTION_TYPE, type); -} - -static inline int zd_set_encryption_type(struct zd_chip *chip, u32 type) -{ - return zd_iowrite32(chip, CR_ENCRYPTION_TYPE, type); -} - -static inline int zd_chip_get_basic_rates(struct zd_chip *chip, u16 *cr_rates) -{ - return zd_ioread16(chip, CR_BASIC_RATE_TBL, cr_rates); -} - -int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates); - -int zd_chip_lock_phy_regs(struct zd_chip *chip); -int zd_chip_unlock_phy_regs(struct zd_chip *chip); - -enum led_status { - ZD_LED_OFF = 0, - ZD_LED_SCANNING = 1, - ZD_LED_ASSOCIATED = 2, -}; - -int zd_chip_control_leds(struct zd_chip *chip, enum led_status status); - -int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period, - int type); - -static inline int zd_get_beacon_interval(struct zd_chip *chip, u32 *interval) -{ - return zd_ioread32(chip, CR_BCN_INTERVAL, interval); -} - -struct rx_status; - -u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status); - -struct zd_mc_hash { - u32 low; - u32 high; -}; - -static inline void zd_mc_clear(struct zd_mc_hash *hash) -{ - hash->low = 0; - /* The interfaces must always received broadcasts. - * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63. - */ - hash->high = 0x80000000; -} - -static inline void zd_mc_add_all(struct zd_mc_hash *hash) -{ - hash->low = hash->high = 0xffffffff; -} - -static inline void zd_mc_add_addr(struct zd_mc_hash *hash, u8 *addr) -{ - unsigned int i = addr[5] >> 2; - if (i < 32) { - hash->low |= 1 << i; - } else { - hash->high |= 1 << (i-32); - } -} - -int zd_chip_set_multicast_hash(struct zd_chip *chip, - struct zd_mc_hash *hash); - -u64 zd_chip_get_tsf(struct zd_chip *chip); - -#endif /* _ZD_CHIP_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_def.h b/drivers/net/wireless/zd1211rw/zd_def.h deleted file mode 100644 index 41bd755bc135..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_def.h +++ /dev/null @@ -1,69 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#ifndef _ZD_DEF_H -#define _ZD_DEF_H - -#include -#include -#include - -typedef u16 __nocast zd_addr_t; - -#define dev_printk_f(level, dev, fmt, args...) \ - dev_printk(level, dev, "%s() " fmt, __func__, ##args) - -#ifdef DEBUG -# define dev_dbg_f(dev, fmt, args...) \ - dev_printk_f(KERN_DEBUG, dev, fmt, ## args) -# define dev_dbg_f_limit(dev, fmt, args...) do { \ - if (net_ratelimit()) \ - dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \ -} while (0) -# define dev_dbg_f_cond(dev, cond, fmt, args...) ({ \ - bool __cond = !!(cond); \ - if (unlikely(__cond)) \ - dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \ -}) -#else -# define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0) -# define dev_dbg_f_limit(dev, fmt, args...) do { (void)(dev); } while (0) -# define dev_dbg_f_cond(dev, cond, fmt, args...) do { (void)(dev); } while (0) -#endif /* DEBUG */ - -#ifdef DEBUG -# define ZD_ASSERT(x) \ -do { \ - if (unlikely(!(x))) { \ - pr_debug("%s:%d ASSERT %s VIOLATED!\n", \ - __FILE__, __LINE__, __stringify(x)); \ - dump_stack(); \ - } \ -} while (0) -#else -# define ZD_ASSERT(x) do { } while (0) -#endif - -#ifdef DEBUG -# define ZD_MEMCLEAR(pointer, size) memset((pointer), 0xff, (size)) -#else -# define ZD_MEMCLEAR(pointer, size) do { } while (0) -#endif - -#endif /* _ZD_DEF_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c deleted file mode 100644 index e539d9b1b562..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ /dev/null @@ -1,1550 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * Copyright (C) 2006-2007 Michael Wu - * Copyright (C) 2007-2008 Luis R. Rodriguez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include "zd_def.h" -#include "zd_chip.h" -#include "zd_mac.h" -#include "zd_rf.h" - -struct zd_reg_alpha2_map { - u32 reg; - char alpha2[2]; -}; - -static struct zd_reg_alpha2_map reg_alpha2_map[] = { - { ZD_REGDOMAIN_FCC, "US" }, - { ZD_REGDOMAIN_IC, "CA" }, - { ZD_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */ - { ZD_REGDOMAIN_JAPAN, "JP" }, - { ZD_REGDOMAIN_JAPAN_2, "JP" }, - { ZD_REGDOMAIN_JAPAN_3, "JP" }, - { ZD_REGDOMAIN_SPAIN, "ES" }, - { ZD_REGDOMAIN_FRANCE, "FR" }, -}; - -/* This table contains the hardware specific values for the modulation rates. */ -static const struct ieee80211_rate zd_rates[] = { - { .bitrate = 10, - .hw_value = ZD_CCK_RATE_1M, }, - { .bitrate = 20, - .hw_value = ZD_CCK_RATE_2M, - .hw_value_short = ZD_CCK_RATE_2M | ZD_CCK_PREA_SHORT, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, - .hw_value = ZD_CCK_RATE_5_5M, - .hw_value_short = ZD_CCK_RATE_5_5M | ZD_CCK_PREA_SHORT, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, - .hw_value = ZD_CCK_RATE_11M, - .hw_value_short = ZD_CCK_RATE_11M | ZD_CCK_PREA_SHORT, - .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 60, - .hw_value = ZD_OFDM_RATE_6M, - .flags = 0 }, - { .bitrate = 90, - .hw_value = ZD_OFDM_RATE_9M, - .flags = 0 }, - { .bitrate = 120, - .hw_value = ZD_OFDM_RATE_12M, - .flags = 0 }, - { .bitrate = 180, - .hw_value = ZD_OFDM_RATE_18M, - .flags = 0 }, - { .bitrate = 240, - .hw_value = ZD_OFDM_RATE_24M, - .flags = 0 }, - { .bitrate = 360, - .hw_value = ZD_OFDM_RATE_36M, - .flags = 0 }, - { .bitrate = 480, - .hw_value = ZD_OFDM_RATE_48M, - .flags = 0 }, - { .bitrate = 540, - .hw_value = ZD_OFDM_RATE_54M, - .flags = 0 }, -}; - -/* - * Zydas retry rates table. Each line is listed in the same order as - * in zd_rates[] and contains all the rate used when a packet is sent - * starting with a given rates. Let's consider an example : - * - * "11 Mbits : 4, 3, 2, 1, 0" means : - * - packet is sent using 4 different rates - * - 1st rate is index 3 (ie 11 Mbits) - * - 2nd rate is index 2 (ie 5.5 Mbits) - * - 3rd rate is index 1 (ie 2 Mbits) - * - 4th rate is index 0 (ie 1 Mbits) - */ - -static const struct tx_retry_rate zd_retry_rates[] = { - { /* 1 Mbits */ 1, { 0 }}, - { /* 2 Mbits */ 2, { 1, 0 }}, - { /* 5.5 Mbits */ 3, { 2, 1, 0 }}, - { /* 11 Mbits */ 4, { 3, 2, 1, 0 }}, - { /* 6 Mbits */ 5, { 4, 3, 2, 1, 0 }}, - { /* 9 Mbits */ 6, { 5, 4, 3, 2, 1, 0}}, - { /* 12 Mbits */ 5, { 6, 3, 2, 1, 0 }}, - { /* 18 Mbits */ 6, { 7, 6, 3, 2, 1, 0 }}, - { /* 24 Mbits */ 6, { 8, 6, 3, 2, 1, 0 }}, - { /* 36 Mbits */ 7, { 9, 8, 6, 3, 2, 1, 0 }}, - { /* 48 Mbits */ 8, {10, 9, 8, 6, 3, 2, 1, 0 }}, - { /* 54 Mbits */ 9, {11, 10, 9, 8, 6, 3, 2, 1, 0 }} -}; - -static const struct ieee80211_channel zd_channels[] = { - { .center_freq = 2412, .hw_value = 1 }, - { .center_freq = 2417, .hw_value = 2 }, - { .center_freq = 2422, .hw_value = 3 }, - { .center_freq = 2427, .hw_value = 4 }, - { .center_freq = 2432, .hw_value = 5 }, - { .center_freq = 2437, .hw_value = 6 }, - { .center_freq = 2442, .hw_value = 7 }, - { .center_freq = 2447, .hw_value = 8 }, - { .center_freq = 2452, .hw_value = 9 }, - { .center_freq = 2457, .hw_value = 10 }, - { .center_freq = 2462, .hw_value = 11 }, - { .center_freq = 2467, .hw_value = 12 }, - { .center_freq = 2472, .hw_value = 13 }, - { .center_freq = 2484, .hw_value = 14 }, -}; - -static void housekeeping_init(struct zd_mac *mac); -static void housekeeping_enable(struct zd_mac *mac); -static void housekeeping_disable(struct zd_mac *mac); -static void beacon_init(struct zd_mac *mac); -static void beacon_enable(struct zd_mac *mac); -static void beacon_disable(struct zd_mac *mac); -static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble); -static int zd_mac_config_beacon(struct ieee80211_hw *hw, - struct sk_buff *beacon, bool in_intr); - -static int zd_reg2alpha2(u8 regdomain, char *alpha2) -{ - unsigned int i; - struct zd_reg_alpha2_map *reg_map; - for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) { - reg_map = ®_alpha2_map[i]; - if (regdomain == reg_map->reg) { - alpha2[0] = reg_map->alpha2[0]; - alpha2[1] = reg_map->alpha2[1]; - return 0; - } - } - return 1; -} - -static int zd_check_signal(struct ieee80211_hw *hw, int signal) -{ - struct zd_mac *mac = zd_hw_mac(hw); - - dev_dbg_f_cond(zd_mac_dev(mac), signal < 0 || signal > 100, - "%s: signal value from device not in range 0..100, " - "but %d.\n", __func__, signal); - - if (signal < 0) - signal = 0; - else if (signal > 100) - signal = 100; - - return signal; -} - -int zd_mac_preinit_hw(struct ieee80211_hw *hw) -{ - int r; - u8 addr[ETH_ALEN]; - struct zd_mac *mac = zd_hw_mac(hw); - - r = zd_chip_read_mac_addr_fw(&mac->chip, addr); - if (r) - return r; - - SET_IEEE80211_PERM_ADDR(hw, addr); - - return 0; -} - -int zd_mac_init_hw(struct ieee80211_hw *hw) -{ - int r; - struct zd_mac *mac = zd_hw_mac(hw); - struct zd_chip *chip = &mac->chip; - char alpha2[2]; - u8 default_regdomain; - - r = zd_chip_enable_int(chip); - if (r) - goto out; - r = zd_chip_init_hw(chip); - if (r) - goto disable_int; - - ZD_ASSERT(!irqs_disabled()); - - r = zd_read_regdomain(chip, &default_regdomain); - if (r) - goto disable_int; - spin_lock_irq(&mac->lock); - mac->regdomain = mac->default_regdomain = default_regdomain; - spin_unlock_irq(&mac->lock); - - /* We must inform the device that we are doing encryption/decryption in - * software at the moment. */ - r = zd_set_encryption_type(chip, ENC_SNIFFER); - if (r) - goto disable_int; - - r = zd_reg2alpha2(mac->regdomain, alpha2); - if (r) - goto disable_int; - - r = regulatory_hint(hw->wiphy, alpha2); -disable_int: - zd_chip_disable_int(chip); -out: - return r; -} - -void zd_mac_clear(struct zd_mac *mac) -{ - flush_workqueue(zd_workqueue); - zd_chip_clear(&mac->chip); - ZD_ASSERT(!spin_is_locked(&mac->lock)); - ZD_MEMCLEAR(mac, sizeof(struct zd_mac)); -} - -static int set_rx_filter(struct zd_mac *mac) -{ - unsigned long flags; - u32 filter = STA_RX_FILTER; - - spin_lock_irqsave(&mac->lock, flags); - if (mac->pass_ctrl) - filter |= RX_FILTER_CTRL; - spin_unlock_irqrestore(&mac->lock, flags); - - return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter); -} - -static int set_mac_and_bssid(struct zd_mac *mac) -{ - int r; - - if (!mac->vif) - return -1; - - r = zd_write_mac_addr(&mac->chip, mac->vif->addr); - if (r) - return r; - - /* Vendor driver after setting MAC either sets BSSID for AP or - * filter for other modes. - */ - if (mac->type != NL80211_IFTYPE_AP) - return set_rx_filter(mac); - else - return zd_write_bssid(&mac->chip, mac->vif->addr); -} - -static int set_mc_hash(struct zd_mac *mac) -{ - struct zd_mc_hash hash; - zd_mc_clear(&hash); - return zd_chip_set_multicast_hash(&mac->chip, &hash); -} - -int zd_op_start(struct ieee80211_hw *hw) -{ - struct zd_mac *mac = zd_hw_mac(hw); - struct zd_chip *chip = &mac->chip; - struct zd_usb *usb = &chip->usb; - int r; - - if (!usb->initialized) { - r = zd_usb_init_hw(usb); - if (r) - goto out; - } - - r = zd_chip_enable_int(chip); - if (r < 0) - goto out; - - r = zd_chip_set_basic_rates(chip, CR_RATES_80211B | CR_RATES_80211G); - if (r < 0) - goto disable_int; - r = set_rx_filter(mac); - if (r) - goto disable_int; - r = set_mc_hash(mac); - if (r) - goto disable_int; - - /* Wait after setting the multicast hash table and powering on - * the radio otherwise interface bring up will fail. This matches - * what the vendor driver did. - */ - msleep(10); - - r = zd_chip_switch_radio_on(chip); - if (r < 0) { - dev_err(zd_chip_dev(chip), - "%s: failed to set radio on\n", __func__); - goto disable_int; - } - r = zd_chip_enable_rxtx(chip); - if (r < 0) - goto disable_radio; - r = zd_chip_enable_hwint(chip); - if (r < 0) - goto disable_rxtx; - - housekeeping_enable(mac); - beacon_enable(mac); - set_bit(ZD_DEVICE_RUNNING, &mac->flags); - return 0; -disable_rxtx: - zd_chip_disable_rxtx(chip); -disable_radio: - zd_chip_switch_radio_off(chip); -disable_int: - zd_chip_disable_int(chip); -out: - return r; -} - -void zd_op_stop(struct ieee80211_hw *hw) -{ - struct zd_mac *mac = zd_hw_mac(hw); - struct zd_chip *chip = &mac->chip; - struct sk_buff *skb; - struct sk_buff_head *ack_wait_queue = &mac->ack_wait_queue; - - clear_bit(ZD_DEVICE_RUNNING, &mac->flags); - - /* The order here deliberately is a little different from the open() - * method, since we need to make sure there is no opportunity for RX - * frames to be processed by mac80211 after we have stopped it. - */ - - zd_chip_disable_rxtx(chip); - beacon_disable(mac); - housekeeping_disable(mac); - flush_workqueue(zd_workqueue); - - zd_chip_disable_hwint(chip); - zd_chip_switch_radio_off(chip); - zd_chip_disable_int(chip); - - - while ((skb = skb_dequeue(ack_wait_queue))) - dev_kfree_skb_any(skb); -} - -int zd_restore_settings(struct zd_mac *mac) -{ - struct sk_buff *beacon; - struct zd_mc_hash multicast_hash; - unsigned int short_preamble; - int r, beacon_interval, beacon_period; - u8 channel; - - dev_dbg_f(zd_mac_dev(mac), "\n"); - - spin_lock_irq(&mac->lock); - multicast_hash = mac->multicast_hash; - short_preamble = mac->short_preamble; - beacon_interval = mac->beacon.interval; - beacon_period = mac->beacon.period; - channel = mac->channel; - spin_unlock_irq(&mac->lock); - - r = set_mac_and_bssid(mac); - if (r < 0) { - dev_dbg_f(zd_mac_dev(mac), "set_mac_and_bssid failed, %d\n", r); - return r; - } - - r = zd_chip_set_channel(&mac->chip, channel); - if (r < 0) { - dev_dbg_f(zd_mac_dev(mac), "zd_chip_set_channel failed, %d\n", - r); - return r; - } - - set_rts_cts(mac, short_preamble); - - r = zd_chip_set_multicast_hash(&mac->chip, &multicast_hash); - if (r < 0) { - dev_dbg_f(zd_mac_dev(mac), - "zd_chip_set_multicast_hash failed, %d\n", r); - return r; - } - - if (mac->type == NL80211_IFTYPE_MESH_POINT || - mac->type == NL80211_IFTYPE_ADHOC || - mac->type == NL80211_IFTYPE_AP) { - if (mac->vif != NULL) { - beacon = ieee80211_beacon_get(mac->hw, mac->vif); - if (beacon) - zd_mac_config_beacon(mac->hw, beacon, false); - } - - zd_set_beacon_interval(&mac->chip, beacon_interval, - beacon_period, mac->type); - - spin_lock_irq(&mac->lock); - mac->beacon.last_update = jiffies; - spin_unlock_irq(&mac->lock); - } - - return 0; -} - -/** - * zd_mac_tx_status - reports tx status of a packet if required - * @hw - a &struct ieee80211_hw pointer - * @skb - a sk-buffer - * @flags: extra flags to set in the TX status info - * @ackssi: ACK signal strength - * @success - True for successful transmission of the frame - * - * This information calls ieee80211_tx_status_irqsafe() if required by the - * control information. It copies the control information into the status - * information. - * - * If no status information has been requested, the skb is freed. - */ -static void zd_mac_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, - int ackssi, struct tx_status *tx_status) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int i; - int success = 1, retry = 1; - int first_idx; - const struct tx_retry_rate *retries; - - ieee80211_tx_info_clear_status(info); - - if (tx_status) { - success = !tx_status->failure; - retry = tx_status->retry + success; - } - - if (success) { - /* success */ - info->flags |= IEEE80211_TX_STAT_ACK; - } else { - /* failure */ - info->flags &= ~IEEE80211_TX_STAT_ACK; - } - - first_idx = info->status.rates[0].idx; - ZD_ASSERT(0<=first_idx && first_idxcount); - - info->status.rates[0].idx = retries->rate[0]; - info->status.rates[0].count = 1; // (retry > 1 ? 2 : 1); - - for (i=1; istatus.rates[i].idx = retries->rate[i]; - info->status.rates[i].count = 1; // ((i==retry-1) && success ? 1:2); - } - for (; istatus.rates[i].idx = retries->rate[retry - 1]; - info->status.rates[i].count = 1; // (success ? 1:2); - } - if (istatus.rates[i].idx = -1; /* terminate */ - - info->status.ack_signal = zd_check_signal(hw, ackssi); - ieee80211_tx_status_irqsafe(hw, skb); -} - -/** - * zd_mac_tx_failed - callback for failed frames - * @dev: the mac80211 wireless device - * - * This function is called if a frame couldn't be successfully - * transferred. The first frame from the tx queue, will be selected and - * reported as error to the upper layers. - */ -void zd_mac_tx_failed(struct urb *urb) -{ - struct ieee80211_hw * hw = zd_usb_to_hw(urb->context); - struct zd_mac *mac = zd_hw_mac(hw); - struct sk_buff_head *q = &mac->ack_wait_queue; - struct sk_buff *skb; - struct tx_status *tx_status = (struct tx_status *)urb->transfer_buffer; - unsigned long flags; - int success = !tx_status->failure; - int retry = tx_status->retry + success; - int found = 0; - int i, position = 0; - - q = &mac->ack_wait_queue; - spin_lock_irqsave(&q->lock, flags); - - skb_queue_walk(q, skb) { - struct ieee80211_hdr *tx_hdr; - struct ieee80211_tx_info *info; - int first_idx, final_idx; - const struct tx_retry_rate *retries; - u8 final_rate; - - position ++; - - /* if the hardware reports a failure and we had a 802.11 ACK - * pending, then we skip the first skb when searching for a - * matching frame */ - if (tx_status->failure && mac->ack_pending && - skb_queue_is_first(q, skb)) { - continue; - } - - tx_hdr = (struct ieee80211_hdr *)skb->data; - - /* we skip all frames not matching the reported destination */ - if (unlikely(!ether_addr_equal(tx_hdr->addr1, tx_status->mac))) - continue; - - /* we skip all frames not matching the reported final rate */ - - info = IEEE80211_SKB_CB(skb); - first_idx = info->status.rates[0].idx; - ZD_ASSERT(0<=first_idx && first_idx retries->count) - continue; - - final_idx = retries->rate[retry - 1]; - final_rate = zd_rates[final_idx].hw_value; - - if (final_rate != tx_status->rate) { - continue; - } - - found = 1; - break; - } - - if (found) { - for (i=1; i<=position; i++) { - skb = __skb_dequeue(q); - zd_mac_tx_status(hw, skb, - mac->ack_pending ? mac->ack_signal : 0, - i == position ? tx_status : NULL); - mac->ack_pending = 0; - } - } - - spin_unlock_irqrestore(&q->lock, flags); -} - -/** - * zd_mac_tx_to_dev - callback for USB layer - * @skb: a &sk_buff pointer - * @error: error value, 0 if transmission successful - * - * Informs the MAC layer that the frame has successfully transferred to the - * device. If an ACK is required and the transfer to the device has been - * successful, the packets are put on the @ack_wait_queue with - * the control set removed. - */ -void zd_mac_tx_to_dev(struct sk_buff *skb, int error) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hw *hw = info->rate_driver_data[0]; - struct zd_mac *mac = zd_hw_mac(hw); - - ieee80211_tx_info_clear_status(info); - - skb_pull(skb, sizeof(struct zd_ctrlset)); - if (unlikely(error || - (info->flags & IEEE80211_TX_CTL_NO_ACK))) { - /* - * FIXME : do we need to fill in anything ? - */ - ieee80211_tx_status_irqsafe(hw, skb); - } else { - struct sk_buff_head *q = &mac->ack_wait_queue; - - skb_queue_tail(q, skb); - while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS) { - zd_mac_tx_status(hw, skb_dequeue(q), - mac->ack_pending ? mac->ack_signal : 0, - NULL); - mac->ack_pending = 0; - } - } -} - -static int zd_calc_tx_length_us(u8 *service, u8 zd_rate, u16 tx_length) -{ - /* ZD_PURE_RATE() must be used to remove the modulation type flag of - * the zd-rate values. - */ - static const u8 rate_divisor[] = { - [ZD_PURE_RATE(ZD_CCK_RATE_1M)] = 1, - [ZD_PURE_RATE(ZD_CCK_RATE_2M)] = 2, - /* Bits must be doubled. */ - [ZD_PURE_RATE(ZD_CCK_RATE_5_5M)] = 11, - [ZD_PURE_RATE(ZD_CCK_RATE_11M)] = 11, - [ZD_PURE_RATE(ZD_OFDM_RATE_6M)] = 6, - [ZD_PURE_RATE(ZD_OFDM_RATE_9M)] = 9, - [ZD_PURE_RATE(ZD_OFDM_RATE_12M)] = 12, - [ZD_PURE_RATE(ZD_OFDM_RATE_18M)] = 18, - [ZD_PURE_RATE(ZD_OFDM_RATE_24M)] = 24, - [ZD_PURE_RATE(ZD_OFDM_RATE_36M)] = 36, - [ZD_PURE_RATE(ZD_OFDM_RATE_48M)] = 48, - [ZD_PURE_RATE(ZD_OFDM_RATE_54M)] = 54, - }; - - u32 bits = (u32)tx_length * 8; - u32 divisor; - - divisor = rate_divisor[ZD_PURE_RATE(zd_rate)]; - if (divisor == 0) - return -EINVAL; - - switch (zd_rate) { - case ZD_CCK_RATE_5_5M: - bits = (2*bits) + 10; /* round up to the next integer */ - break; - case ZD_CCK_RATE_11M: - if (service) { - u32 t = bits % 11; - *service &= ~ZD_PLCP_SERVICE_LENGTH_EXTENSION; - if (0 < t && t <= 3) { - *service |= ZD_PLCP_SERVICE_LENGTH_EXTENSION; - } - } - bits += 10; /* round up to the next integer */ - break; - } - - return bits/divisor; -} - -static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs, - struct ieee80211_hdr *header, - struct ieee80211_tx_info *info) -{ - /* - * CONTROL TODO: - * - if backoff needed, enable bit 0 - * - if burst (backoff not needed) disable bit 0 - */ - - cs->control = 0; - - /* First fragment */ - if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) - cs->control |= ZD_CS_NEED_RANDOM_BACKOFF; - - /* No ACK expected (multicast, etc.) */ - if (info->flags & IEEE80211_TX_CTL_NO_ACK) - cs->control |= ZD_CS_NO_ACK; - - /* PS-POLL */ - if (ieee80211_is_pspoll(header->frame_control)) - cs->control |= ZD_CS_PS_POLL_FRAME; - - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) - cs->control |= ZD_CS_RTS; - - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) - cs->control |= ZD_CS_SELF_CTS; - - /* FIXME: Management frame? */ -} - -static bool zd_mac_match_cur_beacon(struct zd_mac *mac, struct sk_buff *beacon) -{ - if (!mac->beacon.cur_beacon) - return false; - - if (mac->beacon.cur_beacon->len != beacon->len) - return false; - - return !memcmp(beacon->data, mac->beacon.cur_beacon->data, beacon->len); -} - -static void zd_mac_free_cur_beacon_locked(struct zd_mac *mac) -{ - ZD_ASSERT(mutex_is_locked(&mac->chip.mutex)); - - kfree_skb(mac->beacon.cur_beacon); - mac->beacon.cur_beacon = NULL; -} - -static void zd_mac_free_cur_beacon(struct zd_mac *mac) -{ - mutex_lock(&mac->chip.mutex); - zd_mac_free_cur_beacon_locked(mac); - mutex_unlock(&mac->chip.mutex); -} - -static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon, - bool in_intr) -{ - struct zd_mac *mac = zd_hw_mac(hw); - int r, ret, num_cmds, req_pos = 0; - u32 tmp, j = 0; - /* 4 more bytes for tail CRC */ - u32 full_len = beacon->len + 4; - unsigned long end_jiffies, message_jiffies; - struct zd_ioreq32 *ioreqs; - - mutex_lock(&mac->chip.mutex); - - /* Check if hw already has this beacon. */ - if (zd_mac_match_cur_beacon(mac, beacon)) { - r = 0; - goto out_nofree; - } - - /* Alloc memory for full beacon write at once. */ - num_cmds = 1 + zd_chip_is_zd1211b(&mac->chip) + full_len; - ioreqs = kmalloc(num_cmds * sizeof(struct zd_ioreq32), GFP_KERNEL); - if (!ioreqs) { - r = -ENOMEM; - goto out_nofree; - } - - r = zd_iowrite32_locked(&mac->chip, 0, CR_BCN_FIFO_SEMAPHORE); - if (r < 0) - goto out; - r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE); - if (r < 0) - goto release_sema; - if (in_intr && tmp & 0x2) { - r = -EBUSY; - goto release_sema; - } - - end_jiffies = jiffies + HZ / 2; /*~500ms*/ - message_jiffies = jiffies + HZ / 10; /*~100ms*/ - while (tmp & 0x2) { - r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE); - if (r < 0) - goto release_sema; - if (time_is_before_eq_jiffies(message_jiffies)) { - message_jiffies = jiffies + HZ / 10; - dev_err(zd_mac_dev(mac), - "CR_BCN_FIFO_SEMAPHORE not ready\n"); - if (time_is_before_eq_jiffies(end_jiffies)) { - dev_err(zd_mac_dev(mac), - "Giving up beacon config.\n"); - r = -ETIMEDOUT; - goto reset_device; - } - } - msleep(20); - } - - ioreqs[req_pos].addr = CR_BCN_FIFO; - ioreqs[req_pos].value = full_len - 1; - req_pos++; - if (zd_chip_is_zd1211b(&mac->chip)) { - ioreqs[req_pos].addr = CR_BCN_LENGTH; - ioreqs[req_pos].value = full_len - 1; - req_pos++; - } - - for (j = 0 ; j < beacon->len; j++) { - ioreqs[req_pos].addr = CR_BCN_FIFO; - ioreqs[req_pos].value = *((u8 *)(beacon->data + j)); - req_pos++; - } - - for (j = 0; j < 4; j++) { - ioreqs[req_pos].addr = CR_BCN_FIFO; - ioreqs[req_pos].value = 0x0; - req_pos++; - } - - BUG_ON(req_pos != num_cmds); - - r = zd_iowrite32a_locked(&mac->chip, ioreqs, num_cmds); - -release_sema: - /* - * Try very hard to release device beacon semaphore, as otherwise - * device/driver can be left in unusable state. - */ - end_jiffies = jiffies + HZ / 2; /*~500ms*/ - ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE); - while (ret < 0) { - if (in_intr || time_is_before_eq_jiffies(end_jiffies)) { - ret = -ETIMEDOUT; - break; - } - - msleep(20); - ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE); - } - - if (ret < 0) - dev_err(zd_mac_dev(mac), "Could not release " - "CR_BCN_FIFO_SEMAPHORE!\n"); - if (r < 0 || ret < 0) { - if (r >= 0) - r = ret; - - /* We don't know if beacon was written successfully or not, - * so clear current. */ - zd_mac_free_cur_beacon_locked(mac); - - goto out; - } - - /* Beacon has now been written successfully, update current. */ - zd_mac_free_cur_beacon_locked(mac); - mac->beacon.cur_beacon = beacon; - beacon = NULL; - - /* 802.11b/g 2.4G CCK 1Mb - * 802.11a, not yet implemented, uses different values (see GPL vendor - * driver) - */ - r = zd_iowrite32_locked(&mac->chip, 0x00000400 | (full_len << 19), - CR_BCN_PLCP_CFG); -out: - kfree(ioreqs); -out_nofree: - kfree_skb(beacon); - mutex_unlock(&mac->chip.mutex); - - return r; - -reset_device: - zd_mac_free_cur_beacon_locked(mac); - kfree_skb(beacon); - - mutex_unlock(&mac->chip.mutex); - kfree(ioreqs); - - /* semaphore stuck, reset device to avoid fw freeze later */ - dev_warn(zd_mac_dev(mac), "CR_BCN_FIFO_SEMAPHORE stuck, " - "resetting device..."); - usb_queue_reset_device(mac->chip.usb.intf); - - return r; -} - -static int fill_ctrlset(struct zd_mac *mac, - struct sk_buff *skb) -{ - int r; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - unsigned int frag_len = skb->len + FCS_LEN; - unsigned int packet_length; - struct ieee80211_rate *txrate; - struct zd_ctrlset *cs = (struct zd_ctrlset *) - skb_push(skb, sizeof(struct zd_ctrlset)); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - ZD_ASSERT(frag_len <= 0xffff); - - /* - * Firmware computes the duration itself (for all frames except PSPoll) - * and needs the field set to 0 at input, otherwise firmware messes up - * duration_id and sets bits 14 and 15 on. - */ - if (!ieee80211_is_pspoll(hdr->frame_control)) - hdr->duration_id = 0; - - txrate = ieee80211_get_tx_rate(mac->hw, info); - - cs->modulation = txrate->hw_value; - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) - cs->modulation = txrate->hw_value_short; - - cs->tx_length = cpu_to_le16(frag_len); - - cs_set_control(mac, cs, hdr, info); - - packet_length = frag_len + sizeof(struct zd_ctrlset) + 10; - ZD_ASSERT(packet_length <= 0xffff); - /* ZD1211B: Computing the length difference this way, gives us - * flexibility to compute the packet length. - */ - cs->packet_length = cpu_to_le16(zd_chip_is_zd1211b(&mac->chip) ? - packet_length - frag_len : packet_length); - - /* - * CURRENT LENGTH: - * - transmit frame length in microseconds - * - seems to be derived from frame length - * - see Cal_Us_Service() in zdinlinef.h - * - if macp->bTxBurstEnable is enabled, then multiply by 4 - * - bTxBurstEnable is never set in the vendor driver - * - * SERVICE: - * - "for PLCP configuration" - * - always 0 except in some situations at 802.11b 11M - * - see line 53 of zdinlinef.h - */ - cs->service = 0; - r = zd_calc_tx_length_us(&cs->service, ZD_RATE(cs->modulation), - le16_to_cpu(cs->tx_length)); - if (r < 0) - return r; - cs->current_length = cpu_to_le16(r); - cs->next_frame_length = 0; - - return 0; -} - -/** - * zd_op_tx - transmits a network frame to the device - * - * @dev: mac80211 hardware device - * @skb: socket buffer - * @control: the control structure - * - * This function transmit an IEEE 802.11 network frame to the device. The - * control block of the skbuff will be initialized. If necessary the incoming - * mac80211 queues will be stopped. - */ -static void zd_op_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct zd_mac *mac = zd_hw_mac(hw); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int r; - - r = fill_ctrlset(mac, skb); - if (r) - goto fail; - - info->rate_driver_data[0] = hw; - - r = zd_usb_tx(&mac->chip.usb, skb); - if (r) - goto fail; - return; - -fail: - dev_kfree_skb(skb); -} - -/** - * filter_ack - filters incoming packets for acknowledgements - * @dev: the mac80211 device - * @rx_hdr: received header - * @stats: the status for the received packet - * - * This functions looks for ACK packets and tries to match them with the - * frames in the tx queue. If a match is found the frame will be dequeued and - * the upper layers is informed about the successful transmission. If - * mac80211 queues have been stopped and the number of frames still to be - * transmitted is low the queues will be opened again. - * - * Returns 1 if the frame was an ACK, 0 if it was ignored. - */ -static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr, - struct ieee80211_rx_status *stats) -{ - struct zd_mac *mac = zd_hw_mac(hw); - struct sk_buff *skb; - struct sk_buff_head *q; - unsigned long flags; - int found = 0; - int i, position = 0; - - if (!ieee80211_is_ack(rx_hdr->frame_control)) - return 0; - - q = &mac->ack_wait_queue; - spin_lock_irqsave(&q->lock, flags); - skb_queue_walk(q, skb) { - struct ieee80211_hdr *tx_hdr; - - position ++; - - if (mac->ack_pending && skb_queue_is_first(q, skb)) - continue; - - tx_hdr = (struct ieee80211_hdr *)skb->data; - if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) - { - found = 1; - break; - } - } - - if (found) { - for (i=1; iack_pending ? mac->ack_signal : 0, - NULL); - mac->ack_pending = 0; - } - - mac->ack_pending = 1; - mac->ack_signal = stats->signal; - - /* Prevent pending tx-packet on AP-mode */ - if (mac->type == NL80211_IFTYPE_AP) { - skb = __skb_dequeue(q); - zd_mac_tx_status(hw, skb, mac->ack_signal, NULL); - mac->ack_pending = 0; - } - } - - spin_unlock_irqrestore(&q->lock, flags); - return 1; -} - -int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length) -{ - struct zd_mac *mac = zd_hw_mac(hw); - struct ieee80211_rx_status stats; - const struct rx_status *status; - struct sk_buff *skb; - int bad_frame = 0; - __le16 fc; - int need_padding; - int i; - u8 rate; - - if (length < ZD_PLCP_HEADER_SIZE + 10 /* IEEE80211_1ADDR_LEN */ + - FCS_LEN + sizeof(struct rx_status)) - return -EINVAL; - - memset(&stats, 0, sizeof(stats)); - - /* Note about pass_failed_fcs and pass_ctrl access below: - * mac locking intentionally omitted here, as this is the only unlocked - * reader and the only writer is configure_filter. Plus, if there were - * any races accessing these variables, it wouldn't really matter. - * If mac80211 ever provides a way for us to access filter flags - * from outside configure_filter, we could improve on this. Also, this - * situation may change once we implement some kind of DMA-into-skb - * RX path. */ - - /* Caller has to ensure that length >= sizeof(struct rx_status). */ - status = (struct rx_status *) - (buffer + (length - sizeof(struct rx_status))); - if (status->frame_status & ZD_RX_ERROR) { - if (mac->pass_failed_fcs && - (status->frame_status & ZD_RX_CRC32_ERROR)) { - stats.flag |= RX_FLAG_FAILED_FCS_CRC; - bad_frame = 1; - } else { - return -EINVAL; - } - } - - stats.freq = zd_channels[_zd_chip_get_channel(&mac->chip) - 1].center_freq; - stats.band = IEEE80211_BAND_2GHZ; - stats.signal = zd_check_signal(hw, status->signal_strength); - - rate = zd_rx_rate(buffer, status); - - /* todo: return index in the big switches in zd_rx_rate instead */ - for (i = 0; i < mac->band.n_bitrates; i++) - if (rate == mac->band.bitrates[i].hw_value) - stats.rate_idx = i; - - length -= ZD_PLCP_HEADER_SIZE + sizeof(struct rx_status); - buffer += ZD_PLCP_HEADER_SIZE; - - /* Except for bad frames, filter each frame to see if it is an ACK, in - * which case our internal TX tracking is updated. Normally we then - * bail here as there's no need to pass ACKs on up to the stack, but - * there is also the case where the stack has requested us to pass - * control frames on up (pass_ctrl) which we must consider. */ - if (!bad_frame && - filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) - && !mac->pass_ctrl) - return 0; - - fc = get_unaligned((__le16*)buffer); - need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc); - - skb = dev_alloc_skb(length + (need_padding ? 2 : 0)); - if (skb == NULL) - return -ENOMEM; - if (need_padding) { - /* Make sure the payload data is 4 byte aligned. */ - skb_reserve(skb, 2); - } - - /* FIXME : could we avoid this big memcpy ? */ - memcpy(skb_put(skb, length), buffer, length); - - memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats)); - ieee80211_rx_irqsafe(hw, skb); - return 0; -} - -static int zd_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct zd_mac *mac = zd_hw_mac(hw); - - /* using NL80211_IFTYPE_UNSPECIFIED to indicate no mode selected */ - if (mac->type != NL80211_IFTYPE_UNSPECIFIED) - return -EOPNOTSUPP; - - switch (vif->type) { - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_AP: - mac->type = vif->type; - break; - default: - return -EOPNOTSUPP; - } - - mac->vif = vif; - - return set_mac_and_bssid(mac); -} - -static void zd_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct zd_mac *mac = zd_hw_mac(hw); - mac->type = NL80211_IFTYPE_UNSPECIFIED; - mac->vif = NULL; - zd_set_beacon_interval(&mac->chip, 0, 0, NL80211_IFTYPE_UNSPECIFIED); - zd_write_mac_addr(&mac->chip, NULL); - - zd_mac_free_cur_beacon(mac); -} - -static int zd_op_config(struct ieee80211_hw *hw, u32 changed) -{ - struct zd_mac *mac = zd_hw_mac(hw); - struct ieee80211_conf *conf = &hw->conf; - - spin_lock_irq(&mac->lock); - mac->channel = conf->chandef.chan->hw_value; - spin_unlock_irq(&mac->lock); - - return zd_chip_set_channel(&mac->chip, conf->chandef.chan->hw_value); -} - -static void zd_beacon_done(struct zd_mac *mac) -{ - struct sk_buff *skb, *beacon; - - if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) - return; - if (!mac->vif || mac->vif->type != NL80211_IFTYPE_AP) - return; - - /* - * Send out buffered broad- and multicast frames. - */ - while (!ieee80211_queue_stopped(mac->hw, 0)) { - skb = ieee80211_get_buffered_bc(mac->hw, mac->vif); - if (!skb) - break; - zd_op_tx(mac->hw, NULL, skb); - } - - /* - * Fetch next beacon so that tim_count is updated. - */ - beacon = ieee80211_beacon_get(mac->hw, mac->vif); - if (beacon) - zd_mac_config_beacon(mac->hw, beacon, true); - - spin_lock_irq(&mac->lock); - mac->beacon.last_update = jiffies; - spin_unlock_irq(&mac->lock); -} - -static void zd_process_intr(struct work_struct *work) -{ - u16 int_status; - unsigned long flags; - struct zd_mac *mac = container_of(work, struct zd_mac, process_intr); - - spin_lock_irqsave(&mac->lock, flags); - int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer + 4)); - spin_unlock_irqrestore(&mac->lock, flags); - - if (int_status & INT_CFG_NEXT_BCN) { - /*dev_dbg_f_limit(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n");*/ - zd_beacon_done(mac); - } else { - dev_dbg_f(zd_mac_dev(mac), "Unsupported interrupt\n"); - } - - zd_chip_enable_hwint(&mac->chip); -} - - -static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw, - struct netdev_hw_addr_list *mc_list) -{ - struct zd_mac *mac = zd_hw_mac(hw); - struct zd_mc_hash hash; - struct netdev_hw_addr *ha; - - zd_mc_clear(&hash); - - netdev_hw_addr_list_for_each(ha, mc_list) { - dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", ha->addr); - zd_mc_add_addr(&hash, ha->addr); - } - - return hash.low | ((u64)hash.high << 32); -} - -#define SUPPORTED_FIF_FLAGS \ - (FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \ - FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC) -static void zd_op_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *new_flags, - u64 multicast) -{ - struct zd_mc_hash hash = { - .low = multicast, - .high = multicast >> 32, - }; - struct zd_mac *mac = zd_hw_mac(hw); - unsigned long flags; - int r; - - /* Only deal with supported flags */ - changed_flags &= SUPPORTED_FIF_FLAGS; - *new_flags &= SUPPORTED_FIF_FLAGS; - - /* - * If multicast parameter (as returned by zd_op_prepare_multicast) - * has changed, no bit in changed_flags is set. To handle this - * situation, we do not return if changed_flags is 0. If we do so, - * we will have some issue with IPv6 which uses multicast for link - * layer address resolution. - */ - if (*new_flags & FIF_ALLMULTI) - zd_mc_add_all(&hash); - - spin_lock_irqsave(&mac->lock, flags); - mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL); - mac->pass_ctrl = !!(*new_flags & FIF_CONTROL); - mac->multicast_hash = hash; - spin_unlock_irqrestore(&mac->lock, flags); - - zd_chip_set_multicast_hash(&mac->chip, &hash); - - if (changed_flags & FIF_CONTROL) { - r = set_rx_filter(mac); - if (r) - dev_err(zd_mac_dev(mac), "set_rx_filter error %d\n", r); - } - - /* no handling required for FIF_OTHER_BSS as we don't currently - * do BSSID filtering */ - /* FIXME: in future it would be nice to enable the probe response - * filter (so that the driver doesn't see them) until - * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd - * have to schedule work to enable prbresp reception, which might - * happen too late. For now we'll just listen and forward them all the - * time. */ -} - -static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble) -{ - mutex_lock(&mac->chip.mutex); - zd_chip_set_rts_cts_rate_locked(&mac->chip, short_preamble); - mutex_unlock(&mac->chip.mutex); -} - -static void zd_op_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct zd_mac *mac = zd_hw_mac(hw); - int associated; - - dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes); - - if (mac->type == NL80211_IFTYPE_MESH_POINT || - mac->type == NL80211_IFTYPE_ADHOC || - mac->type == NL80211_IFTYPE_AP) { - associated = true; - if (changes & BSS_CHANGED_BEACON) { - struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); - - if (beacon) { - zd_chip_disable_hwint(&mac->chip); - zd_mac_config_beacon(hw, beacon, false); - zd_chip_enable_hwint(&mac->chip); - } - } - - if (changes & BSS_CHANGED_BEACON_ENABLED) { - u16 interval = 0; - u8 period = 0; - - if (bss_conf->enable_beacon) { - period = bss_conf->dtim_period; - interval = bss_conf->beacon_int; - } - - spin_lock_irq(&mac->lock); - mac->beacon.period = period; - mac->beacon.interval = interval; - mac->beacon.last_update = jiffies; - spin_unlock_irq(&mac->lock); - - zd_set_beacon_interval(&mac->chip, interval, period, - mac->type); - } - } else - associated = is_valid_ether_addr(bss_conf->bssid); - - spin_lock_irq(&mac->lock); - mac->associated = associated; - spin_unlock_irq(&mac->lock); - - /* TODO: do hardware bssid filtering */ - - if (changes & BSS_CHANGED_ERP_PREAMBLE) { - spin_lock_irq(&mac->lock); - mac->short_preamble = bss_conf->use_short_preamble; - spin_unlock_irq(&mac->lock); - - set_rts_cts(mac, bss_conf->use_short_preamble); - } -} - -static u64 zd_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct zd_mac *mac = zd_hw_mac(hw); - return zd_chip_get_tsf(&mac->chip); -} - -static const struct ieee80211_ops zd_ops = { - .tx = zd_op_tx, - .start = zd_op_start, - .stop = zd_op_stop, - .add_interface = zd_op_add_interface, - .remove_interface = zd_op_remove_interface, - .config = zd_op_config, - .prepare_multicast = zd_op_prepare_multicast, - .configure_filter = zd_op_configure_filter, - .bss_info_changed = zd_op_bss_info_changed, - .get_tsf = zd_op_get_tsf, -}; - -struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf) -{ - struct zd_mac *mac; - struct ieee80211_hw *hw; - - hw = ieee80211_alloc_hw(sizeof(struct zd_mac), &zd_ops); - if (!hw) { - dev_dbg_f(&intf->dev, "out of memory\n"); - return NULL; - } - - mac = zd_hw_mac(hw); - - memset(mac, 0, sizeof(*mac)); - spin_lock_init(&mac->lock); - mac->hw = hw; - - mac->type = NL80211_IFTYPE_UNSPECIFIED; - - memcpy(mac->channels, zd_channels, sizeof(zd_channels)); - memcpy(mac->rates, zd_rates, sizeof(zd_rates)); - mac->band.n_bitrates = ARRAY_SIZE(zd_rates); - mac->band.bitrates = mac->rates; - mac->band.n_channels = ARRAY_SIZE(zd_channels); - mac->band.channels = mac->channels; - - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band; - - ieee80211_hw_set(hw, MFP_CAPABLE); - ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); - ieee80211_hw_set(hw, RX_INCLUDES_FCS); - ieee80211_hw_set(hw, SIGNAL_UNSPEC); - - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_MESH_POINT) | - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP); - - hw->max_signal = 100; - hw->queues = 1; - hw->extra_tx_headroom = sizeof(struct zd_ctrlset); - - /* - * Tell mac80211 that we support multi rate retries - */ - hw->max_rates = IEEE80211_TX_MAX_RATES; - hw->max_rate_tries = 18; /* 9 rates * 2 retries/rate */ - - skb_queue_head_init(&mac->ack_wait_queue); - mac->ack_pending = 0; - - zd_chip_init(&mac->chip, hw, intf); - housekeeping_init(mac); - beacon_init(mac); - INIT_WORK(&mac->process_intr, zd_process_intr); - - SET_IEEE80211_DEV(hw, &intf->dev); - return hw; -} - -#define BEACON_WATCHDOG_DELAY round_jiffies_relative(HZ) - -static void beacon_watchdog_handler(struct work_struct *work) -{ - struct zd_mac *mac = - container_of(work, struct zd_mac, beacon.watchdog_work.work); - struct sk_buff *beacon; - unsigned long timeout; - int interval, period; - - if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) - goto rearm; - if (mac->type != NL80211_IFTYPE_AP || !mac->vif) - goto rearm; - - spin_lock_irq(&mac->lock); - interval = mac->beacon.interval; - period = mac->beacon.period; - timeout = mac->beacon.last_update + - msecs_to_jiffies(interval * 1024 / 1000) * 3; - spin_unlock_irq(&mac->lock); - - if (interval > 0 && time_is_before_jiffies(timeout)) { - dev_dbg_f(zd_mac_dev(mac), "beacon interrupt stalled, " - "restarting. " - "(interval: %d, dtim: %d)\n", - interval, period); - - zd_chip_disable_hwint(&mac->chip); - - beacon = ieee80211_beacon_get(mac->hw, mac->vif); - if (beacon) { - zd_mac_free_cur_beacon(mac); - - zd_mac_config_beacon(mac->hw, beacon, false); - } - - zd_set_beacon_interval(&mac->chip, interval, period, mac->type); - - zd_chip_enable_hwint(&mac->chip); - - spin_lock_irq(&mac->lock); - mac->beacon.last_update = jiffies; - spin_unlock_irq(&mac->lock); - } - -rearm: - queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work, - BEACON_WATCHDOG_DELAY); -} - -static void beacon_init(struct zd_mac *mac) -{ - INIT_DELAYED_WORK(&mac->beacon.watchdog_work, beacon_watchdog_handler); -} - -static void beacon_enable(struct zd_mac *mac) -{ - dev_dbg_f(zd_mac_dev(mac), "\n"); - - mac->beacon.last_update = jiffies; - queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work, - BEACON_WATCHDOG_DELAY); -} - -static void beacon_disable(struct zd_mac *mac) -{ - dev_dbg_f(zd_mac_dev(mac), "\n"); - cancel_delayed_work_sync(&mac->beacon.watchdog_work); - - zd_mac_free_cur_beacon(mac); -} - -#define LINK_LED_WORK_DELAY HZ - -static void link_led_handler(struct work_struct *work) -{ - struct zd_mac *mac = - container_of(work, struct zd_mac, housekeeping.link_led_work.work); - struct zd_chip *chip = &mac->chip; - int is_associated; - int r; - - if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) - goto requeue; - - spin_lock_irq(&mac->lock); - is_associated = mac->associated; - spin_unlock_irq(&mac->lock); - - r = zd_chip_control_leds(chip, - is_associated ? ZD_LED_ASSOCIATED : ZD_LED_SCANNING); - if (r) - dev_dbg_f(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r); - -requeue: - queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work, - LINK_LED_WORK_DELAY); -} - -static void housekeeping_init(struct zd_mac *mac) -{ - INIT_DELAYED_WORK(&mac->housekeeping.link_led_work, link_led_handler); -} - -static void housekeeping_enable(struct zd_mac *mac) -{ - dev_dbg_f(zd_mac_dev(mac), "\n"); - queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work, - 0); -} - -static void housekeeping_disable(struct zd_mac *mac) -{ - dev_dbg_f(zd_mac_dev(mac), "\n"); - cancel_delayed_work_sync(&mac->housekeeping.link_led_work); - zd_chip_control_leds(&mac->chip, ZD_LED_OFF); -} diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h deleted file mode 100644 index 5a484235308f..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_mac.h +++ /dev/null @@ -1,327 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#ifndef _ZD_MAC_H -#define _ZD_MAC_H - -#include -#include - -#include "zd_chip.h" - -struct zd_ctrlset { - u8 modulation; - __le16 tx_length; - u8 control; - /* stores only the difference to tx_length on ZD1211B */ - __le16 packet_length; - __le16 current_length; - u8 service; - __le16 next_frame_length; -} __packed; - -#define ZD_CS_RESERVED_SIZE 25 - -/* The field modulation of struct zd_ctrlset controls the bit rate, the use - * of short or long preambles in 802.11b (CCK mode) or the use of 802.11a or - * 802.11g in OFDM mode. - * - * The term zd-rate is used for the combination of the modulation type flag - * and the "pure" rate value. - */ -#define ZD_PURE_RATE_MASK 0x0f -#define ZD_MODULATION_TYPE_MASK 0x10 -#define ZD_RATE_MASK (ZD_PURE_RATE_MASK|ZD_MODULATION_TYPE_MASK) -#define ZD_PURE_RATE(modulation) ((modulation) & ZD_PURE_RATE_MASK) -#define ZD_MODULATION_TYPE(modulation) ((modulation) & ZD_MODULATION_TYPE_MASK) -#define ZD_RATE(modulation) ((modulation) & ZD_RATE_MASK) - -/* The two possible modulation types. Notify that 802.11b doesn't use the CCK - * codeing for the 1 and 2 MBit/s rate. We stay with the term here to remain - * consistent with uses the term at other places. - */ -#define ZD_CCK 0x00 -#define ZD_OFDM 0x10 - -/* The ZD1211 firmware uses proprietary encodings of the 802.11b (CCK) rates. - * For OFDM the PLCP rate encodings are used. We combine these "pure" rates - * with the modulation type flag and call the resulting values zd-rates. - */ -#define ZD_CCK_RATE_1M (ZD_CCK|0x00) -#define ZD_CCK_RATE_2M (ZD_CCK|0x01) -#define ZD_CCK_RATE_5_5M (ZD_CCK|0x02) -#define ZD_CCK_RATE_11M (ZD_CCK|0x03) -#define ZD_OFDM_RATE_6M (ZD_OFDM|ZD_OFDM_PLCP_RATE_6M) -#define ZD_OFDM_RATE_9M (ZD_OFDM|ZD_OFDM_PLCP_RATE_9M) -#define ZD_OFDM_RATE_12M (ZD_OFDM|ZD_OFDM_PLCP_RATE_12M) -#define ZD_OFDM_RATE_18M (ZD_OFDM|ZD_OFDM_PLCP_RATE_18M) -#define ZD_OFDM_RATE_24M (ZD_OFDM|ZD_OFDM_PLCP_RATE_24M) -#define ZD_OFDM_RATE_36M (ZD_OFDM|ZD_OFDM_PLCP_RATE_36M) -#define ZD_OFDM_RATE_48M (ZD_OFDM|ZD_OFDM_PLCP_RATE_48M) -#define ZD_OFDM_RATE_54M (ZD_OFDM|ZD_OFDM_PLCP_RATE_54M) - -/* The bit 5 of the zd_ctrlset modulation field controls the preamble in CCK - * mode or the 802.11a/802.11g selection in OFDM mode. - */ -#define ZD_CCK_PREA_LONG 0x00 -#define ZD_CCK_PREA_SHORT 0x20 -#define ZD_OFDM_MODE_11G 0x00 -#define ZD_OFDM_MODE_11A 0x20 - -/* zd_ctrlset control field */ -#define ZD_CS_NEED_RANDOM_BACKOFF 0x01 -#define ZD_CS_NO_ACK 0x02 - -#define ZD_CS_FRAME_TYPE_MASK 0x0c -#define ZD_CS_DATA_FRAME 0x00 -#define ZD_CS_PS_POLL_FRAME 0x04 -#define ZD_CS_MANAGEMENT_FRAME 0x08 -#define ZD_CS_NO_SEQUENCE_CTL_FRAME 0x0c - -#define ZD_CS_WAKE_DESTINATION 0x10 -#define ZD_CS_RTS 0x20 -#define ZD_CS_ENCRYPT 0x40 -#define ZD_CS_SELF_CTS 0x80 - -/* Incoming frames are prepended by a PLCP header */ -#define ZD_PLCP_HEADER_SIZE 5 - -struct rx_length_info { - __le16 length[3]; - __le16 tag; -} __packed; - -#define RX_LENGTH_INFO_TAG 0x697e - -struct rx_status { - u8 signal_quality_cck; - /* rssi */ - u8 signal_strength; - u8 signal_quality_ofdm; - u8 decryption_type; - u8 frame_status; -} __packed; - -/* rx_status field decryption_type */ -#define ZD_RX_NO_WEP 0 -#define ZD_RX_WEP64 1 -#define ZD_RX_TKIP 2 -#define ZD_RX_AES 4 -#define ZD_RX_WEP128 5 -#define ZD_RX_WEP256 6 - -/* rx_status field frame_status */ -#define ZD_RX_FRAME_MODULATION_MASK 0x01 -#define ZD_RX_CCK 0x00 -#define ZD_RX_OFDM 0x01 - -#define ZD_RX_TIMEOUT_ERROR 0x02 -#define ZD_RX_FIFO_OVERRUN_ERROR 0x04 -#define ZD_RX_DECRYPTION_ERROR 0x08 -#define ZD_RX_CRC32_ERROR 0x10 -#define ZD_RX_NO_ADDR1_MATCH_ERROR 0x20 -#define ZD_RX_CRC16_ERROR 0x40 -#define ZD_RX_ERROR 0x80 - -struct tx_retry_rate { - int count; /* number of valid element in rate[] array */ - int rate[10]; /* retry rates, described by an index in zd_rates[] */ -}; - -struct tx_status { - u8 type; /* must always be 0x01 : USB_INT_TYPE */ - u8 id; /* must always be 0xa0 : USB_INT_ID_RETRY_FAILED */ - u8 rate; - u8 pad; - u8 mac[ETH_ALEN]; - u8 retry; - u8 failure; -} __packed; - -enum mac_flags { - MAC_FIXED_CHANNEL = 0x01, -}; - -struct housekeeping { - struct delayed_work link_led_work; -}; - -struct beacon { - struct delayed_work watchdog_work; - struct sk_buff *cur_beacon; - unsigned long last_update; - u16 interval; - u8 period; -}; - -enum zd_device_flags { - ZD_DEVICE_RUNNING, -}; - -#define ZD_MAC_STATS_BUFFER_SIZE 16 - -#define ZD_MAC_MAX_ACK_WAITERS 50 - -struct zd_mac { - struct zd_chip chip; - spinlock_t lock; - spinlock_t intr_lock; - struct ieee80211_hw *hw; - struct ieee80211_vif *vif; - struct housekeeping housekeeping; - struct beacon beacon; - struct work_struct set_rts_cts_work; - struct work_struct process_intr; - struct zd_mc_hash multicast_hash; - u8 intr_buffer[USB_MAX_EP_INT_BUFFER]; - u8 regdomain; - u8 default_regdomain; - u8 channel; - int type; - int associated; - unsigned long flags; - struct sk_buff_head ack_wait_queue; - struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[12]; - struct ieee80211_supported_band band; - - /* Short preamble (used for RTS/CTS) */ - unsigned int short_preamble:1; - - /* whether to pass frames with CRC errors to stack */ - unsigned int pass_failed_fcs:1; - - /* whether to pass control frames to stack */ - unsigned int pass_ctrl:1; - - /* whether we have received a 802.11 ACK that is pending */ - unsigned int ack_pending:1; - - /* signal strength of the last 802.11 ACK received */ - int ack_signal; -}; - -#define ZD_REGDOMAIN_FCC 0x10 -#define ZD_REGDOMAIN_IC 0x20 -#define ZD_REGDOMAIN_ETSI 0x30 -#define ZD_REGDOMAIN_SPAIN 0x31 -#define ZD_REGDOMAIN_FRANCE 0x32 -#define ZD_REGDOMAIN_JAPAN_2 0x40 -#define ZD_REGDOMAIN_JAPAN 0x41 -#define ZD_REGDOMAIN_JAPAN_3 0x49 - -enum { - MIN_CHANNEL24 = 1, - MAX_CHANNEL24 = 14, -}; - -#define ZD_PLCP_SERVICE_LENGTH_EXTENSION 0x80 - -struct ofdm_plcp_header { - u8 prefix[3]; - __le16 service; -} __packed; - -static inline u8 zd_ofdm_plcp_header_rate(const struct ofdm_plcp_header *header) -{ - return header->prefix[0] & 0xf; -} - -/* The following defines give the encoding of the 4-bit rate field in the - * OFDM (802.11a/802.11g) PLCP header. Notify that these values are used to - * define the zd-rate values for OFDM. - * - * See the struct zd_ctrlset definition in zd_mac.h. - */ -#define ZD_OFDM_PLCP_RATE_6M 0xb -#define ZD_OFDM_PLCP_RATE_9M 0xf -#define ZD_OFDM_PLCP_RATE_12M 0xa -#define ZD_OFDM_PLCP_RATE_18M 0xe -#define ZD_OFDM_PLCP_RATE_24M 0x9 -#define ZD_OFDM_PLCP_RATE_36M 0xd -#define ZD_OFDM_PLCP_RATE_48M 0x8 -#define ZD_OFDM_PLCP_RATE_54M 0xc - -struct cck_plcp_header { - u8 signal; - u8 service; - __le16 length; - __le16 crc16; -} __packed; - -static inline u8 zd_cck_plcp_header_signal(const struct cck_plcp_header *header) -{ - return header->signal; -} - -/* These defines give the encodings of the signal field in the 802.11b PLCP - * header. The signal field gives the bit rate of the following packet. Even - * if technically wrong we use CCK here also for the 1 MBit/s and 2 MBit/s - * rate to stay consistent with Zydas and our use of the term. - * - * Notify that these values are *not* used in the zd-rates. - */ -#define ZD_CCK_PLCP_SIGNAL_1M 0x0a -#define ZD_CCK_PLCP_SIGNAL_2M 0x14 -#define ZD_CCK_PLCP_SIGNAL_5M5 0x37 -#define ZD_CCK_PLCP_SIGNAL_11M 0x6e - -static inline struct zd_mac *zd_hw_mac(struct ieee80211_hw *hw) -{ - return hw->priv; -} - -static inline struct zd_mac *zd_chip_to_mac(struct zd_chip *chip) -{ - return container_of(chip, struct zd_mac, chip); -} - -static inline struct zd_mac *zd_usb_to_mac(struct zd_usb *usb) -{ - return zd_chip_to_mac(zd_usb_to_chip(usb)); -} - -static inline u8 *zd_mac_get_perm_addr(struct zd_mac *mac) -{ - return mac->hw->wiphy->perm_addr; -} - -#define zd_mac_dev(mac) (zd_chip_dev(&(mac)->chip)) - -struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf); -void zd_mac_clear(struct zd_mac *mac); - -int zd_mac_preinit_hw(struct ieee80211_hw *hw); -int zd_mac_init_hw(struct ieee80211_hw *hw); - -int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length); -void zd_mac_tx_failed(struct urb *urb); -void zd_mac_tx_to_dev(struct sk_buff *skb, int error); - -int zd_op_start(struct ieee80211_hw *hw); -void zd_op_stop(struct ieee80211_hw *hw); -int zd_restore_settings(struct zd_mac *mac); - -#ifdef DEBUG -void zd_dump_rx_status(const struct rx_status *status); -#else -#define zd_dump_rx_status(status) -#endif /* DEBUG */ - -#endif /* _ZD_MAC_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_rf.c b/drivers/net/wireless/zd1211rw/zd_rf.c deleted file mode 100644 index dc179c414518..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_rf.c +++ /dev/null @@ -1,181 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include - -#include "zd_def.h" -#include "zd_rf.h" -#include "zd_mac.h" -#include "zd_chip.h" - -static const char * const rfs[] = { - [0] = "unknown RF0", - [1] = "unknown RF1", - [UW2451_RF] = "UW2451_RF", - [UCHIP_RF] = "UCHIP_RF", - [AL2230_RF] = "AL2230_RF", - [AL7230B_RF] = "AL7230B_RF", - [THETA_RF] = "THETA_RF", - [AL2210_RF] = "AL2210_RF", - [MAXIM_NEW_RF] = "MAXIM_NEW_RF", - [UW2453_RF] = "UW2453_RF", - [AL2230S_RF] = "AL2230S_RF", - [RALINK_RF] = "RALINK_RF", - [INTERSIL_RF] = "INTERSIL_RF", - [RF2959_RF] = "RF2959_RF", - [MAXIM_NEW2_RF] = "MAXIM_NEW2_RF", - [PHILIPS_RF] = "PHILIPS_RF", -}; - -const char *zd_rf_name(u8 type) -{ - if (type & 0xf0) - type = 0; - return rfs[type]; -} - -void zd_rf_init(struct zd_rf *rf) -{ - memset(rf, 0, sizeof(*rf)); - - /* default to update channel integration, as almost all RF's do want - * this */ - rf->update_channel_int = 1; -} - -void zd_rf_clear(struct zd_rf *rf) -{ - if (rf->clear) - rf->clear(rf); - ZD_MEMCLEAR(rf, sizeof(*rf)); -} - -int zd_rf_init_hw(struct zd_rf *rf, u8 type) -{ - int r = 0; - int t; - struct zd_chip *chip = zd_rf_to_chip(rf); - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - switch (type) { - case RF2959_RF: - r = zd_rf_init_rf2959(rf); - break; - case AL2230_RF: - case AL2230S_RF: - r = zd_rf_init_al2230(rf); - break; - case AL7230B_RF: - r = zd_rf_init_al7230b(rf); - break; - case MAXIM_NEW_RF: - case UW2453_RF: - r = zd_rf_init_uw2453(rf); - break; - default: - dev_err(zd_chip_dev(chip), - "RF %s %#x is not supported\n", zd_rf_name(type), type); - rf->type = 0; - return -ENODEV; - } - - if (r) - return r; - - rf->type = type; - - r = zd_chip_lock_phy_regs(chip); - if (r) - return r; - t = rf->init_hw(rf); - r = zd_chip_unlock_phy_regs(chip); - if (t) - r = t; - return r; -} - -int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size) -{ - return scnprintf(buffer, size, "%s", zd_rf_name(rf->type)); -} - -int zd_rf_set_channel(struct zd_rf *rf, u8 channel) -{ - int r; - - ZD_ASSERT(mutex_is_locked(&zd_rf_to_chip(rf)->mutex)); - if (channel < MIN_CHANNEL24) - return -EINVAL; - if (channel > MAX_CHANNEL24) - return -EINVAL; - dev_dbg_f(zd_chip_dev(zd_rf_to_chip(rf)), "channel: %d\n", channel); - - r = rf->set_channel(rf, channel); - if (r >= 0) - rf->channel = channel; - return r; -} - -int zd_switch_radio_on(struct zd_rf *rf) -{ - int r, t; - struct zd_chip *chip = zd_rf_to_chip(rf); - - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - r = zd_chip_lock_phy_regs(chip); - if (r) - return r; - t = rf->switch_radio_on(rf); - r = zd_chip_unlock_phy_regs(chip); - if (t) - r = t; - return r; -} - -int zd_switch_radio_off(struct zd_rf *rf) -{ - int r, t; - struct zd_chip *chip = zd_rf_to_chip(rf); - - /* TODO: move phy regs handling to zd_chip */ - ZD_ASSERT(mutex_is_locked(&chip->mutex)); - r = zd_chip_lock_phy_regs(chip); - if (r) - return r; - t = rf->switch_radio_off(rf); - r = zd_chip_unlock_phy_regs(chip); - if (t) - r = t; - return r; -} - -int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel) -{ - if (!rf->patch_6m_band_edge) - return 0; - - return rf->patch_6m_band_edge(rf, channel); -} - -int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel) -{ - return zd_chip_generic_patch_6m_band(zd_rf_to_chip(rf), channel); -} - diff --git a/drivers/net/wireless/zd1211rw/zd_rf.h b/drivers/net/wireless/zd1211rw/zd_rf.h deleted file mode 100644 index 8f14e25e1041..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_rf.h +++ /dev/null @@ -1,110 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#ifndef _ZD_RF_H -#define _ZD_RF_H - -#define UW2451_RF 0x2 -#define UCHIP_RF 0x3 -#define AL2230_RF 0x4 -#define AL7230B_RF 0x5 /* a,b,g */ -#define THETA_RF 0x6 -#define AL2210_RF 0x7 -#define MAXIM_NEW_RF 0x8 -#define UW2453_RF 0x9 -#define AL2230S_RF 0xa -#define RALINK_RF 0xb -#define INTERSIL_RF 0xc -#define RF2959_RF 0xd -#define MAXIM_NEW2_RF 0xe -#define PHILIPS_RF 0xf - -#define RF_CHANNEL(ch) [(ch)-1] - -/* Provides functions of the RF transceiver. */ - -enum { - RF_REG_BITS = 6, - RF_VALUE_BITS = 18, - RF_RV_BITS = RF_REG_BITS + RF_VALUE_BITS, -}; - -struct zd_rf { - u8 type; - - u8 channel; - - /* whether channel integration and calibration should be updated - * defaults to 1 (yes) */ - u8 update_channel_int:1; - - /* whether ZD_CR47 should be patched from the EEPROM, if the appropriate - * flag is set in the POD. The vendor driver suggests that this should - * be done for all RF's, but a bug in their code prevents but their - * HW_OverWritePhyRegFromE2P() routine from ever taking effect. */ - u8 patch_cck_gain:1; - - /* private RF driver data */ - void *priv; - - /* RF-specific functions */ - int (*init_hw)(struct zd_rf *rf); - int (*set_channel)(struct zd_rf *rf, u8 channel); - int (*switch_radio_on)(struct zd_rf *rf); - int (*switch_radio_off)(struct zd_rf *rf); - int (*patch_6m_band_edge)(struct zd_rf *rf, u8 channel); - void (*clear)(struct zd_rf *rf); -}; - -const char *zd_rf_name(u8 type); -void zd_rf_init(struct zd_rf *rf); -void zd_rf_clear(struct zd_rf *rf); -int zd_rf_init_hw(struct zd_rf *rf, u8 type); - -int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size); - -int zd_rf_set_channel(struct zd_rf *rf, u8 channel); - -int zd_switch_radio_on(struct zd_rf *rf); -int zd_switch_radio_off(struct zd_rf *rf); - -int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel); -int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel); - -static inline int zd_rf_should_update_pwr_int(struct zd_rf *rf) -{ - return rf->update_channel_int; -} - -static inline int zd_rf_should_patch_cck_gain(struct zd_rf *rf) -{ - return rf->patch_cck_gain; -} - -int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel); -int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel); - -/* Functions for individual RF chips */ - -int zd_rf_init_rf2959(struct zd_rf *rf); -int zd_rf_init_al2230(struct zd_rf *rf); -int zd_rf_init_al7230b(struct zd_rf *rf); -int zd_rf_init_uw2453(struct zd_rf *rf); - -#endif /* _ZD_RF_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c deleted file mode 100644 index 99aed7d78952..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c +++ /dev/null @@ -1,443 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include - -#include "zd_rf.h" -#include "zd_usb.h" -#include "zd_chip.h" - -#define IS_AL2230S(chip) ((chip)->al2230s_bit || (chip)->rf.type == AL2230S_RF) - -static const u32 zd1211_al2230_table[][3] = { - RF_CHANNEL( 1) = { 0x03f790, 0x033331, 0x00000d, }, - RF_CHANNEL( 2) = { 0x03f790, 0x0b3331, 0x00000d, }, - RF_CHANNEL( 3) = { 0x03e790, 0x033331, 0x00000d, }, - RF_CHANNEL( 4) = { 0x03e790, 0x0b3331, 0x00000d, }, - RF_CHANNEL( 5) = { 0x03f7a0, 0x033331, 0x00000d, }, - RF_CHANNEL( 6) = { 0x03f7a0, 0x0b3331, 0x00000d, }, - RF_CHANNEL( 7) = { 0x03e7a0, 0x033331, 0x00000d, }, - RF_CHANNEL( 8) = { 0x03e7a0, 0x0b3331, 0x00000d, }, - RF_CHANNEL( 9) = { 0x03f7b0, 0x033331, 0x00000d, }, - RF_CHANNEL(10) = { 0x03f7b0, 0x0b3331, 0x00000d, }, - RF_CHANNEL(11) = { 0x03e7b0, 0x033331, 0x00000d, }, - RF_CHANNEL(12) = { 0x03e7b0, 0x0b3331, 0x00000d, }, - RF_CHANNEL(13) = { 0x03f7c0, 0x033331, 0x00000d, }, - RF_CHANNEL(14) = { 0x03e7c0, 0x066661, 0x00000d, }, -}; - -static const u32 zd1211b_al2230_table[][3] = { - RF_CHANNEL( 1) = { 0x09efc0, 0x8cccc0, 0xb00000, }, - RF_CHANNEL( 2) = { 0x09efc0, 0x8cccd0, 0xb00000, }, - RF_CHANNEL( 3) = { 0x09e7c0, 0x8cccc0, 0xb00000, }, - RF_CHANNEL( 4) = { 0x09e7c0, 0x8cccd0, 0xb00000, }, - RF_CHANNEL( 5) = { 0x05efc0, 0x8cccc0, 0xb00000, }, - RF_CHANNEL( 6) = { 0x05efc0, 0x8cccd0, 0xb00000, }, - RF_CHANNEL( 7) = { 0x05e7c0, 0x8cccc0, 0xb00000, }, - RF_CHANNEL( 8) = { 0x05e7c0, 0x8cccd0, 0xb00000, }, - RF_CHANNEL( 9) = { 0x0defc0, 0x8cccc0, 0xb00000, }, - RF_CHANNEL(10) = { 0x0defc0, 0x8cccd0, 0xb00000, }, - RF_CHANNEL(11) = { 0x0de7c0, 0x8cccc0, 0xb00000, }, - RF_CHANNEL(12) = { 0x0de7c0, 0x8cccd0, 0xb00000, }, - RF_CHANNEL(13) = { 0x03efc0, 0x8cccc0, 0xb00000, }, - RF_CHANNEL(14) = { 0x03e7c0, 0x866660, 0xb00000, }, -}; - -static const struct zd_ioreq16 zd1211b_ioreqs_shared_1[] = { - { ZD_CR240, 0x57 }, { ZD_CR9, 0xe0 }, -}; - -static const struct zd_ioreq16 ioreqs_init_al2230s[] = { - { ZD_CR47, 0x1e }, /* MARK_002 */ - { ZD_CR106, 0x22 }, - { ZD_CR107, 0x2a }, /* MARK_002 */ - { ZD_CR109, 0x13 }, /* MARK_002 */ - { ZD_CR118, 0xf8 }, /* MARK_002 */ - { ZD_CR119, 0x12 }, { ZD_CR122, 0xe0 }, - { ZD_CR128, 0x10 }, /* MARK_001 from 0xe->0x10 */ - { ZD_CR129, 0x0e }, /* MARK_001 from 0xd->0x0e */ - { ZD_CR130, 0x10 }, /* MARK_001 from 0xb->0x0d */ -}; - -static int zd1211b_al2230_finalize_rf(struct zd_chip *chip) -{ - int r; - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 }, - { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 }, - { ZD_CR203, 0x06 }, - { }, - - { ZD_CR240, 0x80 }, - }; - - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - if (r) - return r; - - /* related to antenna selection? */ - if (chip->new_phy_layout) { - r = zd_iowrite16_locked(chip, 0xe1, ZD_CR9); - if (r) - return r; - } - - return zd_iowrite16_locked(chip, 0x06, ZD_CR203); -} - -static int zd1211_al2230_init_hw(struct zd_rf *rf) -{ - int r; - struct zd_chip *chip = zd_rf_to_chip(rf); - - static const struct zd_ioreq16 ioreqs_init[] = { - { ZD_CR15, 0x20 }, { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, - { ZD_CR26, 0x11 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, - { ZD_CR44, 0x33 }, { ZD_CR106, 0x2a }, { ZD_CR107, 0x1a }, - { ZD_CR109, 0x09 }, { ZD_CR110, 0x27 }, { ZD_CR111, 0x2b }, - { ZD_CR112, 0x2b }, { ZD_CR119, 0x0a }, { ZD_CR10, 0x89 }, - /* for newest (3rd cut) AL2300 */ - { ZD_CR17, 0x28 }, - { ZD_CR26, 0x93 }, { ZD_CR34, 0x30 }, - /* for newest (3rd cut) AL2300 */ - { ZD_CR35, 0x3e }, - { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, - /* for newest (3rd cut) AL2300 */ - { ZD_CR46, 0x96 }, - { ZD_CR47, 0x1e }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, - { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, - { ZD_CR92, 0x0a }, { ZD_CR99, 0x28 }, { ZD_CR100, 0x00 }, - { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR106, 0x24 }, - { ZD_CR107, 0x2a }, { ZD_CR109, 0x09 }, { ZD_CR110, 0x13 }, - { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, - { ZD_CR114, 0x27 }, - /* for newest (3rd cut) AL2300 */ - { ZD_CR115, 0x24 }, - { ZD_CR116, 0x24 }, { ZD_CR117, 0xf4 }, { ZD_CR118, 0xfc }, - { ZD_CR119, 0x10 }, { ZD_CR120, 0x4f }, { ZD_CR121, 0x77 }, - { ZD_CR122, 0xe0 }, { ZD_CR137, 0x88 }, { ZD_CR252, 0xff }, - { ZD_CR253, 0xff }, - }; - - static const struct zd_ioreq16 ioreqs_pll[] = { - /* shdnb(PLL_ON)=0 */ - { ZD_CR251, 0x2f }, - /* shdnb(PLL_ON)=1 */ - { ZD_CR251, 0x3f }, - { ZD_CR138, 0x28 }, { ZD_CR203, 0x06 }, - }; - - static const u32 rv1[] = { - /* Channel 1 */ - 0x03f790, - 0x033331, - 0x00000d, - - 0x0b3331, - 0x03b812, - 0x00fff3, - }; - - static const u32 rv2[] = { - 0x000da4, - 0x0f4dc5, /* fix freq shift, 0x04edc5 */ - 0x0805b6, - 0x011687, - 0x000688, - 0x0403b9, /* external control TX power (ZD_CR31) */ - 0x00dbba, - 0x00099b, - 0x0bdffc, - 0x00000d, - 0x00500f, - }; - - static const u32 rv3[] = { - 0x00d00f, - 0x004c0f, - 0x00540f, - 0x00700f, - 0x00500f, - }; - - r = zd_iowrite16a_locked(chip, ioreqs_init, ARRAY_SIZE(ioreqs_init)); - if (r) - return r; - - if (IS_AL2230S(chip)) { - r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, - ARRAY_SIZE(ioreqs_init_al2230s)); - if (r) - return r; - } - - r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS); - if (r) - return r; - - /* improve band edge for AL2230S */ - if (IS_AL2230S(chip)) - r = zd_rfwrite_locked(chip, 0x000824, RF_RV_BITS); - else - r = zd_rfwrite_locked(chip, 0x0005a4, RF_RV_BITS); - if (r) - return r; - - r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS); - if (r) - return r; - - r = zd_iowrite16a_locked(chip, ioreqs_pll, ARRAY_SIZE(ioreqs_pll)); - if (r) - return r; - - r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS); - if (r) - return r; - - return 0; -} - -static int zd1211b_al2230_init_hw(struct zd_rf *rf) -{ - int r; - struct zd_chip *chip = zd_rf_to_chip(rf); - - static const struct zd_ioreq16 ioreqs1[] = { - { ZD_CR10, 0x89 }, { ZD_CR15, 0x20 }, - { ZD_CR17, 0x2B }, /* for newest(3rd cut) AL2230 */ - { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 }, - { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, - { ZD_CR33, 0x28 }, /* 5621 */ - { ZD_CR34, 0x30 }, - { ZD_CR35, 0x3e }, /* for newest(3rd cut) AL2230 */ - { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, - { ZD_CR46, 0x99 }, /* for newest(3rd cut) AL2230 */ - { ZD_CR47, 0x1e }, - - /* ZD1211B 05.06.10 */ - { ZD_CR48, 0x06 }, { ZD_CR49, 0xf9 }, { ZD_CR51, 0x01 }, - { ZD_CR52, 0x80 }, { ZD_CR53, 0x7e }, { ZD_CR65, 0x00 }, - { ZD_CR66, 0x00 }, { ZD_CR67, 0x00 }, { ZD_CR68, 0x00 }, - { ZD_CR69, 0x28 }, - - { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, - { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, - { ZD_CR91, 0x00 }, /* 5621 */ - { ZD_CR92, 0x0a }, - { ZD_CR98, 0x8d }, /* 4804, for 1212 new algorithm */ - { ZD_CR99, 0x00 }, /* 5621 */ - { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, - { ZD_CR106, 0x24 }, /* for newest(3rd cut) AL2230 */ - { ZD_CR107, 0x2a }, - { ZD_CR109, 0x13 }, /* 4804, for 1212 new algorithm */ - { ZD_CR110, 0x1f }, /* 4804, for 1212 new algorithm */ - { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, - { ZD_CR114, 0x27 }, - { ZD_CR115, 0x26 }, /* 24->26 at 4902 for newest(3rd cut) - * AL2230 - */ - { ZD_CR116, 0x24 }, - { ZD_CR117, 0xfa }, /* for 1211b */ - { ZD_CR118, 0xfa }, /* for 1211b */ - { ZD_CR119, 0x10 }, - { ZD_CR120, 0x4f }, - { ZD_CR121, 0x6c }, /* for 1211b */ - { ZD_CR122, 0xfc }, /* E0->FC at 4902 */ - { ZD_CR123, 0x57 }, /* 5623 */ - { ZD_CR125, 0xad }, /* 4804, for 1212 new algorithm */ - { ZD_CR126, 0x6c }, /* 5614 */ - { ZD_CR127, 0x03 }, /* 4804, for 1212 new algorithm */ - { ZD_CR137, 0x50 }, /* 5614 */ - { ZD_CR138, 0xa8 }, - { ZD_CR144, 0xac }, /* 5621 */ - { ZD_CR150, 0x0d }, { ZD_CR252, 0x34 }, { ZD_CR253, 0x34 }, - }; - - static const u32 rv1[] = { - 0x8cccd0, - 0x481dc0, - 0xcfff00, - 0x25a000, - }; - - static const u32 rv2[] = { - /* To improve AL2230 yield, improve phase noise, 4713 */ - 0x25a000, - 0xa3b2f0, - - 0x6da010, /* Reg6 update for MP versio */ - 0xe36280, /* Modified by jxiao for Bor-Chin on 2004/08/02 */ - 0x116000, - 0x9dc020, /* External control TX power (ZD_CR31) */ - 0x5ddb00, /* RegA update for MP version */ - 0xd99000, /* RegB update for MP version */ - 0x3ffbd0, /* RegC update for MP version */ - 0xb00000, /* RegD update for MP version */ - - /* improve phase noise and remove phase calibration,4713 */ - 0xf01a00, - }; - - static const struct zd_ioreq16 ioreqs2[] = { - { ZD_CR251, 0x2f }, /* shdnb(PLL_ON)=0 */ - { ZD_CR251, 0x7f }, /* shdnb(PLL_ON)=1 */ - }; - - static const u32 rv3[] = { - /* To improve AL2230 yield, 4713 */ - 0xf01b00, - 0xf01e00, - 0xf01a00, - }; - - static const struct zd_ioreq16 ioreqs3[] = { - /* related to 6M band edge patching, happens unconditionally */ - { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, - }; - - r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1, - ARRAY_SIZE(zd1211b_ioreqs_shared_1)); - if (r) - return r; - r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1)); - if (r) - return r; - - if (IS_AL2230S(chip)) { - r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, - ARRAY_SIZE(ioreqs_init_al2230s)); - if (r) - return r; - } - - r = zd_rfwritev_cr_locked(chip, zd1211b_al2230_table[0], 3); - if (r) - return r; - r = zd_rfwritev_cr_locked(chip, rv1, ARRAY_SIZE(rv1)); - if (r) - return r; - - if (IS_AL2230S(chip)) - r = zd_rfwrite_locked(chip, 0x241000, RF_RV_BITS); - else - r = zd_rfwrite_locked(chip, 0x25a000, RF_RV_BITS); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, rv2, ARRAY_SIZE(rv2)); - if (r) - return r; - r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); - if (r) - return r; - r = zd_rfwritev_cr_locked(chip, rv3, ARRAY_SIZE(rv3)); - if (r) - return r; - r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3)); - if (r) - return r; - return zd1211b_al2230_finalize_rf(chip); -} - -static int zd1211_al2230_set_channel(struct zd_rf *rf, u8 channel) -{ - int r; - const u32 *rv = zd1211_al2230_table[channel-1]; - struct zd_chip *chip = zd_rf_to_chip(rf); - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR138, 0x28 }, - { ZD_CR203, 0x06 }, - }; - - r = zd_rfwritev_locked(chip, rv, 3, RF_RV_BITS); - if (r) - return r; - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int zd1211b_al2230_set_channel(struct zd_rf *rf, u8 channel) -{ - int r; - const u32 *rv = zd1211b_al2230_table[channel-1]; - struct zd_chip *chip = zd_rf_to_chip(rf); - - r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1, - ARRAY_SIZE(zd1211b_ioreqs_shared_1)); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, rv, 3); - if (r) - return r; - - return zd1211b_al2230_finalize_rf(chip); -} - -static int zd1211_al2230_switch_radio_on(struct zd_rf *rf) -{ - struct zd_chip *chip = zd_rf_to_chip(rf); - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR11, 0x00 }, - { ZD_CR251, 0x3f }, - }; - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int zd1211b_al2230_switch_radio_on(struct zd_rf *rf) -{ - struct zd_chip *chip = zd_rf_to_chip(rf); - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR11, 0x00 }, - { ZD_CR251, 0x7f }, - }; - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int al2230_switch_radio_off(struct zd_rf *rf) -{ - struct zd_chip *chip = zd_rf_to_chip(rf); - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR11, 0x04 }, - { ZD_CR251, 0x2f }, - }; - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -int zd_rf_init_al2230(struct zd_rf *rf) -{ - struct zd_chip *chip = zd_rf_to_chip(rf); - - rf->switch_radio_off = al2230_switch_radio_off; - if (zd_chip_is_zd1211b(chip)) { - rf->init_hw = zd1211b_al2230_init_hw; - rf->set_channel = zd1211b_al2230_set_channel; - rf->switch_radio_on = zd1211b_al2230_switch_radio_on; - } else { - rf->init_hw = zd1211_al2230_init_hw; - rf->set_channel = zd1211_al2230_set_channel; - rf->switch_radio_on = zd1211_al2230_switch_radio_on; - } - rf->patch_6m_band_edge = zd_rf_generic_patch_6m; - rf->patch_cck_gain = 1; - return 0; -} diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c b/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c deleted file mode 100644 index 5fea485be574..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c +++ /dev/null @@ -1,494 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include - -#include "zd_rf.h" -#include "zd_usb.h" -#include "zd_chip.h" - -static const u32 chan_rv[][2] = { - RF_CHANNEL( 1) = { 0x09ec00, 0x8cccc8 }, - RF_CHANNEL( 2) = { 0x09ec00, 0x8cccd8 }, - RF_CHANNEL( 3) = { 0x09ec00, 0x8cccc0 }, - RF_CHANNEL( 4) = { 0x09ec00, 0x8cccd0 }, - RF_CHANNEL( 5) = { 0x05ec00, 0x8cccc8 }, - RF_CHANNEL( 6) = { 0x05ec00, 0x8cccd8 }, - RF_CHANNEL( 7) = { 0x05ec00, 0x8cccc0 }, - RF_CHANNEL( 8) = { 0x05ec00, 0x8cccd0 }, - RF_CHANNEL( 9) = { 0x0dec00, 0x8cccc8 }, - RF_CHANNEL(10) = { 0x0dec00, 0x8cccd8 }, - RF_CHANNEL(11) = { 0x0dec00, 0x8cccc0 }, - RF_CHANNEL(12) = { 0x0dec00, 0x8cccd0 }, - RF_CHANNEL(13) = { 0x03ec00, 0x8cccc8 }, - RF_CHANNEL(14) = { 0x03ec00, 0x866660 }, -}; - -static const u32 std_rv[] = { - 0x4ff821, - 0xc5fbfc, - 0x21ebfe, - 0xafd401, /* freq shift 0xaad401 */ - 0x6cf56a, - 0xe04073, - 0x193d76, - 0x9dd844, - 0x500007, - 0xd8c010, -}; - -static const u32 rv_init1[] = { - 0x3c9000, - 0xbfffff, - 0x700000, - 0xf15d58, -}; - -static const u32 rv_init2[] = { - 0xf15d59, - 0xf15d5c, - 0xf15d58, -}; - -static const struct zd_ioreq16 ioreqs_sw[] = { - { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, - { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf }, -}; - -static int zd1211b_al7230b_finalize(struct zd_chip *chip) -{ - int r; - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 }, - { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 }, - { ZD_CR203, 0x04 }, - { }, - { ZD_CR240, 0x80 }, - }; - - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - if (r) - return r; - - if (chip->new_phy_layout) { - /* antenna selection? */ - r = zd_iowrite16_locked(chip, 0xe5, ZD_CR9); - if (r) - return r; - } - - return zd_iowrite16_locked(chip, 0x04, ZD_CR203); -} - -static int zd1211_al7230b_init_hw(struct zd_rf *rf) -{ - int r; - struct zd_chip *chip = zd_rf_to_chip(rf); - - /* All of these writes are identical to AL2230 unless otherwise - * specified */ - static const struct zd_ioreq16 ioreqs_1[] = { - /* This one is 7230-specific, and happens before the rest */ - { ZD_CR240, 0x57 }, - { }, - - { ZD_CR15, 0x20 }, { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, - { ZD_CR26, 0x11 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, - { ZD_CR44, 0x33 }, - /* This value is different for 7230 (was: 0x2a) */ - { ZD_CR106, 0x22 }, - { ZD_CR107, 0x1a }, { ZD_CR109, 0x09 }, { ZD_CR110, 0x27 }, - { ZD_CR111, 0x2b }, { ZD_CR112, 0x2b }, { ZD_CR119, 0x0a }, - /* This happened further down in AL2230, - * and the value changed (was: 0xe0) */ - { ZD_CR122, 0xfc }, - { ZD_CR10, 0x89 }, - /* for newest (3rd cut) AL2300 */ - { ZD_CR17, 0x28 }, - { ZD_CR26, 0x93 }, { ZD_CR34, 0x30 }, - /* for newest (3rd cut) AL2300 */ - { ZD_CR35, 0x3e }, - { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, - /* for newest (3rd cut) AL2300 */ - { ZD_CR46, 0x96 }, - { ZD_CR47, 0x1e }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, - { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, - { ZD_CR92, 0x0a }, { ZD_CR99, 0x28 }, - /* This value is different for 7230 (was: 0x00) */ - { ZD_CR100, 0x02 }, - { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, - /* This value is different for 7230 (was: 0x24) */ - { ZD_CR106, 0x22 }, - /* This value is different for 7230 (was: 0x2a) */ - { ZD_CR107, 0x3f }, - { ZD_CR109, 0x09 }, - /* This value is different for 7230 (was: 0x13) */ - { ZD_CR110, 0x1f }, - { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, - { ZD_CR114, 0x27 }, - /* for newest (3rd cut) AL2300 */ - { ZD_CR115, 0x24 }, - /* This value is different for 7230 (was: 0x24) */ - { ZD_CR116, 0x3f }, - /* This value is different for 7230 (was: 0xf4) */ - { ZD_CR117, 0xfa }, - { ZD_CR118, 0xfc }, { ZD_CR119, 0x10 }, { ZD_CR120, 0x4f }, - { ZD_CR121, 0x77 }, { ZD_CR137, 0x88 }, - /* This one is 7230-specific */ - { ZD_CR138, 0xa8 }, - /* This value is different for 7230 (was: 0xff) */ - { ZD_CR252, 0x34 }, - /* This value is different for 7230 (was: 0xff) */ - { ZD_CR253, 0x34 }, - - /* PLL_OFF */ - { ZD_CR251, 0x2f }, - }; - - static const struct zd_ioreq16 ioreqs_2[] = { - { ZD_CR251, 0x3f }, /* PLL_ON */ - { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, - { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf }, - }; - - r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); - if (r) - return r; - - r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); - if (r) - return r; - - r = zd_iowrite16_locked(chip, 0x06, ZD_CR203); - if (r) - return r; - r = zd_iowrite16_locked(chip, 0x80, ZD_CR240); - if (r) - return r; - - return 0; -} - -static int zd1211b_al7230b_init_hw(struct zd_rf *rf) -{ - int r; - struct zd_chip *chip = zd_rf_to_chip(rf); - - static const struct zd_ioreq16 ioreqs_1[] = { - { ZD_CR240, 0x57 }, { ZD_CR9, 0x9 }, - { }, - { ZD_CR10, 0x8b }, { ZD_CR15, 0x20 }, - { ZD_CR17, 0x2B }, /* for newest (3rd cut) AL2230 */ - { ZD_CR20, 0x10 }, /* 4N25->Stone Request */ - { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 }, - { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, - { ZD_CR33, 0x28 }, /* 5613 */ - { ZD_CR34, 0x30 }, - { ZD_CR35, 0x3e }, /* for newest (3rd cut) AL2230 */ - { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, - { ZD_CR46, 0x99 }, /* for newest (3rd cut) AL2230 */ - { ZD_CR47, 0x1e }, - - /* ZD1215 5610 */ - { ZD_CR48, 0x00 }, { ZD_CR49, 0x00 }, { ZD_CR51, 0x01 }, - { ZD_CR52, 0x80 }, { ZD_CR53, 0x7e }, { ZD_CR65, 0x00 }, - { ZD_CR66, 0x00 }, { ZD_CR67, 0x00 }, { ZD_CR68, 0x00 }, - { ZD_CR69, 0x28 }, - - { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, - { ZD_CR87, 0x0A }, { ZD_CR89, 0x04 }, - { ZD_CR90, 0x58 }, /* 5112 */ - { ZD_CR91, 0x00 }, /* 5613 */ - { ZD_CR92, 0x0a }, - { ZD_CR98, 0x8d }, /* 4804, for 1212 new algorithm */ - { ZD_CR99, 0x00 }, { ZD_CR100, 0x02 }, { ZD_CR101, 0x13 }, - { ZD_CR102, 0x27 }, - { ZD_CR106, 0x20 }, /* change to 0x24 for AL7230B */ - { ZD_CR109, 0x13 }, /* 4804, for 1212 new algorithm */ - { ZD_CR112, 0x1f }, - }; - - static const struct zd_ioreq16 ioreqs_new_phy[] = { - { ZD_CR107, 0x28 }, - { ZD_CR110, 0x1f }, /* 5127, 0x13->0x1f */ - { ZD_CR111, 0x1f }, /* 0x13 to 0x1f for AL7230B */ - { ZD_CR116, 0x2a }, { ZD_CR118, 0xfa }, { ZD_CR119, 0x12 }, - { ZD_CR121, 0x6c }, /* 5613 */ - }; - - static const struct zd_ioreq16 ioreqs_old_phy[] = { - { ZD_CR107, 0x24 }, - { ZD_CR110, 0x13 }, /* 5127, 0x13->0x1f */ - { ZD_CR111, 0x13 }, /* 0x13 to 0x1f for AL7230B */ - { ZD_CR116, 0x24 }, { ZD_CR118, 0xfc }, { ZD_CR119, 0x11 }, - { ZD_CR121, 0x6a }, /* 5613 */ - }; - - static const struct zd_ioreq16 ioreqs_2[] = { - { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, { ZD_CR115, 0x24 }, - { ZD_CR117, 0xfa }, { ZD_CR120, 0x4f }, - { ZD_CR122, 0xfc }, /* E0->FCh at 4901 */ - { ZD_CR123, 0x57 }, /* 5613 */ - { ZD_CR125, 0xad }, /* 4804, for 1212 new algorithm */ - { ZD_CR126, 0x6c }, /* 5613 */ - { ZD_CR127, 0x03 }, /* 4804, for 1212 new algorithm */ - { ZD_CR130, 0x10 }, - { ZD_CR131, 0x00 }, /* 5112 */ - { ZD_CR137, 0x50 }, /* 5613 */ - { ZD_CR138, 0xa8 }, /* 5112 */ - { ZD_CR144, 0xac }, /* 5613 */ - { ZD_CR148, 0x40 }, /* 5112 */ - { ZD_CR149, 0x40 }, /* 4O07, 50->40 */ - { ZD_CR150, 0x1a }, /* 5112, 0C->1A */ - { ZD_CR252, 0x34 }, { ZD_CR253, 0x34 }, - { ZD_CR251, 0x2f }, /* PLL_OFF */ - }; - - static const struct zd_ioreq16 ioreqs_3[] = { - { ZD_CR251, 0x7f }, /* PLL_ON */ - { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, - { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf }, - }; - - r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); - if (r) - return r; - - if (chip->new_phy_layout) - r = zd_iowrite16a_locked(chip, ioreqs_new_phy, - ARRAY_SIZE(ioreqs_new_phy)); - else - r = zd_iowrite16a_locked(chip, ioreqs_old_phy, - ARRAY_SIZE(ioreqs_old_phy)); - if (r) - return r; - - r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); - if (r) - return r; - - r = zd_iowrite16a_locked(chip, ioreqs_3, ARRAY_SIZE(ioreqs_3)); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); - if (r) - return r; - - return zd1211b_al7230b_finalize(chip); -} - -static int zd1211_al7230b_set_channel(struct zd_rf *rf, u8 channel) -{ - int r; - const u32 *rv = chan_rv[channel-1]; - struct zd_chip *chip = zd_rf_to_chip(rf); - - static const struct zd_ioreq16 ioreqs[] = { - /* PLL_ON */ - { ZD_CR251, 0x3f }, - { ZD_CR203, 0x06 }, { ZD_CR240, 0x08 }, - }; - - r = zd_iowrite16_locked(chip, 0x57, ZD_CR240); - if (r) - return r; - - /* PLL_OFF */ - r = zd_iowrite16_locked(chip, 0x2f, ZD_CR251); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); - if (r) - return r; - - r = zd_rfwrite_cr_locked(chip, 0x3c9000); - if (r) - return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d58); - if (r) - return r; - - r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, rv, 2); - if (r) - return r; - - r = zd_rfwrite_cr_locked(chip, 0x3c9000); - if (r) - return r; - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int zd1211b_al7230b_set_channel(struct zd_rf *rf, u8 channel) -{ - int r; - const u32 *rv = chan_rv[channel-1]; - struct zd_chip *chip = zd_rf_to_chip(rf); - - r = zd_iowrite16_locked(chip, 0x57, ZD_CR240); - if (r) - return r; - r = zd_iowrite16_locked(chip, 0xe4, ZD_CR9); - if (r) - return r; - - /* PLL_OFF */ - r = zd_iowrite16_locked(chip, 0x2f, ZD_CR251); - if (r) - return r; - r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); - if (r) - return r; - - r = zd_rfwrite_cr_locked(chip, 0x3c9000); - if (r) - return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d58); - if (r) - return r; - - r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); - if (r) - return r; - - r = zd_rfwritev_cr_locked(chip, rv, 2); - if (r) - return r; - - r = zd_rfwrite_cr_locked(chip, 0x3c9000); - if (r) - return r; - - r = zd_iowrite16_locked(chip, 0x7f, ZD_CR251); - if (r) - return r; - - return zd1211b_al7230b_finalize(chip); -} - -static int zd1211_al7230b_switch_radio_on(struct zd_rf *rf) -{ - struct zd_chip *chip = zd_rf_to_chip(rf); - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR11, 0x00 }, - { ZD_CR251, 0x3f }, - }; - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int zd1211b_al7230b_switch_radio_on(struct zd_rf *rf) -{ - struct zd_chip *chip = zd_rf_to_chip(rf); - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR11, 0x00 }, - { ZD_CR251, 0x7f }, - }; - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int al7230b_switch_radio_off(struct zd_rf *rf) -{ - struct zd_chip *chip = zd_rf_to_chip(rf); - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR11, 0x04 }, - { ZD_CR251, 0x2f }, - }; - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -/* ZD1211B+AL7230B 6m band edge patching differs slightly from other - * configurations */ -static int zd1211b_al7230b_patch_6m(struct zd_rf *rf, u8 channel) -{ - struct zd_chip *chip = zd_rf_to_chip(rf); - struct zd_ioreq16 ioreqs[] = { - { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, - }; - - /* FIXME: Channel 11 is not the edge for all regulatory domains. */ - if (channel == 1) { - ioreqs[0].value = 0x0e; - ioreqs[1].value = 0x10; - } else if (channel == 11) { - ioreqs[0].value = 0x10; - ioreqs[1].value = 0x10; - } - - dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -int zd_rf_init_al7230b(struct zd_rf *rf) -{ - struct zd_chip *chip = zd_rf_to_chip(rf); - - if (zd_chip_is_zd1211b(chip)) { - rf->init_hw = zd1211b_al7230b_init_hw; - rf->switch_radio_on = zd1211b_al7230b_switch_radio_on; - rf->set_channel = zd1211b_al7230b_set_channel; - rf->patch_6m_band_edge = zd1211b_al7230b_patch_6m; - } else { - rf->init_hw = zd1211_al7230b_init_hw; - rf->switch_radio_on = zd1211_al7230b_switch_radio_on; - rf->set_channel = zd1211_al7230b_set_channel; - rf->patch_6m_band_edge = zd_rf_generic_patch_6m; - rf->patch_cck_gain = 1; - } - - rf->switch_radio_off = al7230b_switch_radio_off; - - return 0; -} diff --git a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c deleted file mode 100644 index a93f657a41c7..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c +++ /dev/null @@ -1,281 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include - -#include "zd_rf.h" -#include "zd_usb.h" -#include "zd_chip.h" - -static const u32 rf2959_table[][2] = { - RF_CHANNEL( 1) = { 0x181979, 0x1e6666 }, - RF_CHANNEL( 2) = { 0x181989, 0x1e6666 }, - RF_CHANNEL( 3) = { 0x181999, 0x1e6666 }, - RF_CHANNEL( 4) = { 0x1819a9, 0x1e6666 }, - RF_CHANNEL( 5) = { 0x1819b9, 0x1e6666 }, - RF_CHANNEL( 6) = { 0x1819c9, 0x1e6666 }, - RF_CHANNEL( 7) = { 0x1819d9, 0x1e6666 }, - RF_CHANNEL( 8) = { 0x1819e9, 0x1e6666 }, - RF_CHANNEL( 9) = { 0x1819f9, 0x1e6666 }, - RF_CHANNEL(10) = { 0x181a09, 0x1e6666 }, - RF_CHANNEL(11) = { 0x181a19, 0x1e6666 }, - RF_CHANNEL(12) = { 0x181a29, 0x1e6666 }, - RF_CHANNEL(13) = { 0x181a39, 0x1e6666 }, - RF_CHANNEL(14) = { 0x181a60, 0x1c0000 }, -}; - -#if 0 -static int bits(u32 rw, int from, int to) -{ - rw &= ~(0xffffffffU << (to+1)); - rw >>= from; - return rw; -} - -static int bit(u32 rw, int bit) -{ - return bits(rw, bit, bit); -} - -static void dump_regwrite(u32 rw) -{ - int reg = bits(rw, 18, 22); - int rw_flag = bits(rw, 23, 23); - PDEBUG("rf2959 %#010x reg %d rw %d", rw, reg, rw_flag); - - switch (reg) { - case 0: - PDEBUG("reg0 CFG1 ref_sel %d hybernate %d rf_vco_reg_en %d" - " if_vco_reg_en %d if_vga_en %d", - bits(rw, 14, 15), bit(rw, 3), bit(rw, 2), bit(rw, 1), - bit(rw, 0)); - break; - case 1: - PDEBUG("reg1 IFPLL1 pll_en1 %d kv_en1 %d vtc_en1 %d lpf1 %d" - " cpl1 %d pdp1 %d autocal_en1 %d ld_en1 %d ifloopr %d" - " ifloopc %d dac1 %d", - bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), - bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), - bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0, 3)); - break; - case 2: - PDEBUG("reg2 IFPLL2 n1 %d num1 %d", - bits(rw, 6, 17), bits(rw, 0, 5)); - break; - case 3: - PDEBUG("reg3 IFPLL3 num %d", bits(rw, 0, 17)); - break; - case 4: - PDEBUG("reg4 IFPLL4 dn1 %#04x ct_def1 %d kv_def1 %d", - bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); - break; - case 5: - PDEBUG("reg5 RFPLL1 pll_en %d kv_en %d vtc_en %d lpf %d cpl %d" - " pdp %d autocal_en %d ld_en %d rfloopr %d rfloopc %d" - " dac %d", - bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), - bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), - bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0,3)); - break; - case 6: - PDEBUG("reg6 RFPLL2 n %d num %d", - bits(rw, 6, 17), bits(rw, 0, 5)); - break; - case 7: - PDEBUG("reg7 RFPLL3 num2 %d", bits(rw, 0, 17)); - break; - case 8: - PDEBUG("reg8 RFPLL4 dn %#06x ct_def %d kv_def %d", - bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); - break; - case 9: - PDEBUG("reg9 CAL1 tvco %d tlock %d m_ct_value %d ld_window %d", - bits(rw, 13, 17), bits(rw, 8, 12), bits(rw, 3, 7), - bits(rw, 0, 2)); - break; - case 10: - PDEBUG("reg10 TXRX1 rxdcfbbyps %d pcontrol %d txvgc %d" - " rxlpfbw %d txlpfbw %d txdiffmode %d txenmode %d" - " intbiasen %d tybypass %d", - bit(rw, 17), bits(rw, 15, 16), bits(rw, 10, 14), - bits(rw, 7, 9), bits(rw, 4, 6), bit(rw, 3), bit(rw, 2), - bit(rw, 1), bit(rw, 0)); - break; - case 11: - PDEBUG("reg11 PCNT1 mid_bias %d p_desired %d pc_offset %d" - " tx_delay %d", - bits(rw, 15, 17), bits(rw, 9, 14), bits(rw, 3, 8), - bits(rw, 0, 2)); - break; - case 12: - PDEBUG("reg12 PCNT2 max_power %d mid_power %d min_power %d", - bits(rw, 12, 17), bits(rw, 6, 11), bits(rw, 0, 5)); - break; - case 13: - PDEBUG("reg13 VCOT1 rfpll vco comp %d ifpll vco comp %d" - " lobias %d if_biasbuf %d if_biasvco %d rf_biasbuf %d" - " rf_biasvco %d", - bit(rw, 17), bit(rw, 16), bit(rw, 15), - bits(rw, 8, 9), bits(rw, 5, 7), bits(rw, 3, 4), - bits(rw, 0, 2)); - break; - case 14: - PDEBUG("reg14 IQCAL rx_acal %d rx_pcal %d" - " tx_acal %d tx_pcal %d", - bits(rw, 13, 17), bits(rw, 9, 12), bits(rw, 4, 8), - bits(rw, 0, 3)); - break; - } -} -#endif /* 0 */ - -static int rf2959_init_hw(struct zd_rf *rf) -{ - int r; - struct zd_chip *chip = zd_rf_to_chip(rf); - - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR2, 0x1E }, { ZD_CR9, 0x20 }, { ZD_CR10, 0x89 }, - { ZD_CR11, 0x00 }, { ZD_CR15, 0xD0 }, { ZD_CR17, 0x68 }, - { ZD_CR19, 0x4a }, { ZD_CR20, 0x0c }, { ZD_CR21, 0x0E }, - { ZD_CR23, 0x48 }, - /* normal size for cca threshold */ - { ZD_CR24, 0x14 }, - /* { ZD_CR24, 0x20 }, */ - { ZD_CR26, 0x90 }, { ZD_CR27, 0x30 }, { ZD_CR29, 0x20 }, - { ZD_CR31, 0xb2 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x28 }, - { ZD_CR38, 0x30 }, { ZD_CR34, 0x0f }, { ZD_CR35, 0xF0 }, - { ZD_CR41, 0x2a }, { ZD_CR46, 0x7F }, { ZD_CR47, 0x1E }, - { ZD_CR51, 0xc5 }, { ZD_CR52, 0xc5 }, { ZD_CR53, 0xc5 }, - { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, - { ZD_CR82, 0x00 }, { ZD_CR83, 0x24 }, { ZD_CR84, 0x04 }, - { ZD_CR85, 0x00 }, { ZD_CR86, 0x10 }, { ZD_CR87, 0x2A }, - { ZD_CR88, 0x10 }, { ZD_CR89, 0x24 }, { ZD_CR90, 0x18 }, - /* { ZD_CR91, 0x18 }, */ - /* should solve continuous CTS frame problems */ - { ZD_CR91, 0x00 }, - { ZD_CR92, 0x0a }, { ZD_CR93, 0x00 }, { ZD_CR94, 0x01 }, - { ZD_CR95, 0x00 }, { ZD_CR96, 0x40 }, { ZD_CR97, 0x37 }, - { ZD_CR98, 0x05 }, { ZD_CR99, 0x28 }, { ZD_CR100, 0x00 }, - { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 }, - { ZD_CR104, 0x18 }, { ZD_CR105, 0x12 }, - /* normal size */ - { ZD_CR106, 0x1a }, - /* { ZD_CR106, 0x22 }, */ - { ZD_CR107, 0x24 }, { ZD_CR108, 0x0a }, { ZD_CR109, 0x13 }, - { ZD_CR110, 0x2F }, { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 }, - { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, { ZD_CR115, 0x40 }, - { ZD_CR116, 0x40 }, { ZD_CR117, 0xF0 }, { ZD_CR118, 0xF0 }, - { ZD_CR119, 0x16 }, - /* no TX continuation */ - { ZD_CR122, 0x00 }, - /* { ZD_CR122, 0xff }, */ - { ZD_CR127, 0x03 }, { ZD_CR131, 0x08 }, { ZD_CR138, 0x28 }, - { ZD_CR148, 0x44 }, { ZD_CR150, 0x10 }, { ZD_CR169, 0xBB }, - { ZD_CR170, 0xBB }, - }; - - static const u32 rv[] = { - 0x000007, /* REG0(CFG1) */ - 0x07dd43, /* REG1(IFPLL1) */ - 0x080959, /* REG2(IFPLL2) */ - 0x0e6666, - 0x116a57, /* REG4 */ - 0x17dd43, /* REG5 */ - 0x1819f9, /* REG6 */ - 0x1e6666, - 0x214554, - 0x25e7fa, - 0x27fffa, - /* The Zydas driver somehow forgets to set this value. It's - * only set for Japan. We are using internal power control - * for now. - */ - 0x294128, /* internal power */ - /* 0x28252c, */ /* External control TX power */ - /* ZD_CR31_CCK, ZD_CR51_6-36M, ZD_CR52_48M, ZD_CR53_54M */ - 0x2c0000, - 0x300000, - 0x340000, /* REG13(0xD) */ - 0x381e0f, /* REG14(0xE) */ - /* Bogus, RF2959's data sheet doesn't know register 27, which is - * actually referenced here. The commented 0x11 is 17. - */ - 0x6c180f, /* REG27(0x11) */ - }; - - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - if (r) - return r; - - return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); -} - -static int rf2959_set_channel(struct zd_rf *rf, u8 channel) -{ - int i, r; - const u32 *rv = rf2959_table[channel-1]; - struct zd_chip *chip = zd_rf_to_chip(rf); - - for (i = 0; i < 2; i++) { - r = zd_rfwrite_locked(chip, rv[i], RF_RV_BITS); - if (r) - return r; - } - return 0; -} - -static int rf2959_switch_radio_on(struct zd_rf *rf) -{ - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR10, 0x89 }, - { ZD_CR11, 0x00 }, - }; - struct zd_chip *chip = zd_rf_to_chip(rf); - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int rf2959_switch_radio_off(struct zd_rf *rf) -{ - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR10, 0x15 }, - { ZD_CR11, 0x81 }, - }; - struct zd_chip *chip = zd_rf_to_chip(rf); - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -int zd_rf_init_rf2959(struct zd_rf *rf) -{ - struct zd_chip *chip = zd_rf_to_chip(rf); - - if (zd_chip_is_zd1211b(chip)) { - dev_err(zd_chip_dev(chip), - "RF2959 is currently not supported for ZD1211B" - " devices\n"); - return -ENODEV; - } - rf->init_hw = rf2959_init_hw; - rf->set_channel = rf2959_set_channel; - rf->switch_radio_on = rf2959_switch_radio_on; - rf->switch_radio_off = rf2959_switch_radio_off; - return 0; -} diff --git a/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c b/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c deleted file mode 100644 index 61b924027356..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c +++ /dev/null @@ -1,539 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include - -#include "zd_rf.h" -#include "zd_usb.h" -#include "zd_chip.h" - -/* This RF programming code is based upon the code found in v2.16.0.0 of the - * ZyDAS vendor driver. Unlike other RF's, Ubec publish full technical specs - * for this RF on their website, so we're able to understand more than - * usual as to what is going on. Thumbs up for Ubec for doing that. */ - -/* The 3-wire serial interface provides access to 8 write-only registers. - * The data format is a 4 bit register address followed by a 20 bit value. */ -#define UW2453_REGWRITE(reg, val) ((((reg) & 0xf) << 20) | ((val) & 0xfffff)) - -/* For channel tuning, we have to configure registers 1 (synthesizer), 2 (synth - * fractional divide ratio) and 3 (VCO config). - * - * We configure the RF to produce an interrupt when the PLL is locked onto - * the configured frequency. During initialization, we run through a variety - * of different VCO configurations on channel 1 until we detect a PLL lock. - * When this happens, we remember which VCO configuration produced the lock - * and use it later. Actually, we use the configuration *after* the one that - * produced the lock, which seems odd, but it works. - * - * If we do not see a PLL lock on any standard VCO config, we fall back on an - * autocal configuration, which has a fixed (as opposed to per-channel) VCO - * config and different synth values from the standard set (divide ratio - * is still shared with the standard set). */ - -/* The per-channel synth values for all standard VCO configurations. These get - * written to register 1. */ -static const u8 uw2453_std_synth[] = { - RF_CHANNEL( 1) = 0x47, - RF_CHANNEL( 2) = 0x47, - RF_CHANNEL( 3) = 0x67, - RF_CHANNEL( 4) = 0x67, - RF_CHANNEL( 5) = 0x67, - RF_CHANNEL( 6) = 0x67, - RF_CHANNEL( 7) = 0x57, - RF_CHANNEL( 8) = 0x57, - RF_CHANNEL( 9) = 0x57, - RF_CHANNEL(10) = 0x57, - RF_CHANNEL(11) = 0x77, - RF_CHANNEL(12) = 0x77, - RF_CHANNEL(13) = 0x77, - RF_CHANNEL(14) = 0x4f, -}; - -/* This table stores the synthesizer fractional divide ratio for *all* VCO - * configurations (both standard and autocal). These get written to register 2. - */ -static const u16 uw2453_synth_divide[] = { - RF_CHANNEL( 1) = 0x999, - RF_CHANNEL( 2) = 0x99b, - RF_CHANNEL( 3) = 0x998, - RF_CHANNEL( 4) = 0x99a, - RF_CHANNEL( 5) = 0x999, - RF_CHANNEL( 6) = 0x99b, - RF_CHANNEL( 7) = 0x998, - RF_CHANNEL( 8) = 0x99a, - RF_CHANNEL( 9) = 0x999, - RF_CHANNEL(10) = 0x99b, - RF_CHANNEL(11) = 0x998, - RF_CHANNEL(12) = 0x99a, - RF_CHANNEL(13) = 0x999, - RF_CHANNEL(14) = 0xccc, -}; - -/* Here is the data for all the standard VCO configurations. We shrink our - * table a little by observing that both channels in a consecutive pair share - * the same value. We also observe that the high 4 bits ([0:3] in the specs) - * are all 'Reserved' and are always set to 0x4 - we chop them off in the data - * below. */ -#define CHAN_TO_PAIRIDX(a) ((a - 1) / 2) -#define RF_CHANPAIR(a,b) [CHAN_TO_PAIRIDX(a)] -static const u16 uw2453_std_vco_cfg[][7] = { - { /* table 1 */ - RF_CHANPAIR( 1, 2) = 0x664d, - RF_CHANPAIR( 3, 4) = 0x604d, - RF_CHANPAIR( 5, 6) = 0x6675, - RF_CHANPAIR( 7, 8) = 0x6475, - RF_CHANPAIR( 9, 10) = 0x6655, - RF_CHANPAIR(11, 12) = 0x6455, - RF_CHANPAIR(13, 14) = 0x6665, - }, - { /* table 2 */ - RF_CHANPAIR( 1, 2) = 0x666d, - RF_CHANPAIR( 3, 4) = 0x606d, - RF_CHANPAIR( 5, 6) = 0x664d, - RF_CHANPAIR( 7, 8) = 0x644d, - RF_CHANPAIR( 9, 10) = 0x6675, - RF_CHANPAIR(11, 12) = 0x6475, - RF_CHANPAIR(13, 14) = 0x6655, - }, - { /* table 3 */ - RF_CHANPAIR( 1, 2) = 0x665d, - RF_CHANPAIR( 3, 4) = 0x605d, - RF_CHANPAIR( 5, 6) = 0x666d, - RF_CHANPAIR( 7, 8) = 0x646d, - RF_CHANPAIR( 9, 10) = 0x664d, - RF_CHANPAIR(11, 12) = 0x644d, - RF_CHANPAIR(13, 14) = 0x6675, - }, - { /* table 4 */ - RF_CHANPAIR( 1, 2) = 0x667d, - RF_CHANPAIR( 3, 4) = 0x607d, - RF_CHANPAIR( 5, 6) = 0x665d, - RF_CHANPAIR( 7, 8) = 0x645d, - RF_CHANPAIR( 9, 10) = 0x666d, - RF_CHANPAIR(11, 12) = 0x646d, - RF_CHANPAIR(13, 14) = 0x664d, - }, - { /* table 5 */ - RF_CHANPAIR( 1, 2) = 0x6643, - RF_CHANPAIR( 3, 4) = 0x6043, - RF_CHANPAIR( 5, 6) = 0x667d, - RF_CHANPAIR( 7, 8) = 0x647d, - RF_CHANPAIR( 9, 10) = 0x665d, - RF_CHANPAIR(11, 12) = 0x645d, - RF_CHANPAIR(13, 14) = 0x666d, - }, - { /* table 6 */ - RF_CHANPAIR( 1, 2) = 0x6663, - RF_CHANPAIR( 3, 4) = 0x6063, - RF_CHANPAIR( 5, 6) = 0x6643, - RF_CHANPAIR( 7, 8) = 0x6443, - RF_CHANPAIR( 9, 10) = 0x667d, - RF_CHANPAIR(11, 12) = 0x647d, - RF_CHANPAIR(13, 14) = 0x665d, - }, - { /* table 7 */ - RF_CHANPAIR( 1, 2) = 0x6653, - RF_CHANPAIR( 3, 4) = 0x6053, - RF_CHANPAIR( 5, 6) = 0x6663, - RF_CHANPAIR( 7, 8) = 0x6463, - RF_CHANPAIR( 9, 10) = 0x6643, - RF_CHANPAIR(11, 12) = 0x6443, - RF_CHANPAIR(13, 14) = 0x667d, - }, - { /* table 8 */ - RF_CHANPAIR( 1, 2) = 0x6673, - RF_CHANPAIR( 3, 4) = 0x6073, - RF_CHANPAIR( 5, 6) = 0x6653, - RF_CHANPAIR( 7, 8) = 0x6453, - RF_CHANPAIR( 9, 10) = 0x6663, - RF_CHANPAIR(11, 12) = 0x6463, - RF_CHANPAIR(13, 14) = 0x6643, - }, - { /* table 9 */ - RF_CHANPAIR( 1, 2) = 0x664b, - RF_CHANPAIR( 3, 4) = 0x604b, - RF_CHANPAIR( 5, 6) = 0x6673, - RF_CHANPAIR( 7, 8) = 0x6473, - RF_CHANPAIR( 9, 10) = 0x6653, - RF_CHANPAIR(11, 12) = 0x6453, - RF_CHANPAIR(13, 14) = 0x6663, - }, - { /* table 10 */ - RF_CHANPAIR( 1, 2) = 0x666b, - RF_CHANPAIR( 3, 4) = 0x606b, - RF_CHANPAIR( 5, 6) = 0x664b, - RF_CHANPAIR( 7, 8) = 0x644b, - RF_CHANPAIR( 9, 10) = 0x6673, - RF_CHANPAIR(11, 12) = 0x6473, - RF_CHANPAIR(13, 14) = 0x6653, - }, - { /* table 11 */ - RF_CHANPAIR( 1, 2) = 0x665b, - RF_CHANPAIR( 3, 4) = 0x605b, - RF_CHANPAIR( 5, 6) = 0x666b, - RF_CHANPAIR( 7, 8) = 0x646b, - RF_CHANPAIR( 9, 10) = 0x664b, - RF_CHANPAIR(11, 12) = 0x644b, - RF_CHANPAIR(13, 14) = 0x6673, - }, - -}; - -/* The per-channel synth values for autocal. These get written to register 1. */ -static const u16 uw2453_autocal_synth[] = { - RF_CHANNEL( 1) = 0x6847, - RF_CHANNEL( 2) = 0x6847, - RF_CHANNEL( 3) = 0x6867, - RF_CHANNEL( 4) = 0x6867, - RF_CHANNEL( 5) = 0x6867, - RF_CHANNEL( 6) = 0x6867, - RF_CHANNEL( 7) = 0x6857, - RF_CHANNEL( 8) = 0x6857, - RF_CHANNEL( 9) = 0x6857, - RF_CHANNEL(10) = 0x6857, - RF_CHANNEL(11) = 0x6877, - RF_CHANNEL(12) = 0x6877, - RF_CHANNEL(13) = 0x6877, - RF_CHANNEL(14) = 0x684f, -}; - -/* The VCO configuration for autocal (all channels) */ -static const u16 UW2453_AUTOCAL_VCO_CFG = 0x6662; - -/* TX gain settings. The array index corresponds to the TX power integration - * values found in the EEPROM. The values get written to register 7. */ -static u32 uw2453_txgain[] = { - [0x00] = 0x0e313, - [0x01] = 0x0fb13, - [0x02] = 0x0e093, - [0x03] = 0x0f893, - [0x04] = 0x0ea93, - [0x05] = 0x1f093, - [0x06] = 0x1f493, - [0x07] = 0x1f693, - [0x08] = 0x1f393, - [0x09] = 0x1f35b, - [0x0a] = 0x1e6db, - [0x0b] = 0x1ff3f, - [0x0c] = 0x1ffff, - [0x0d] = 0x361d7, - [0x0e] = 0x37fbf, - [0x0f] = 0x3ff8b, - [0x10] = 0x3ff33, - [0x11] = 0x3fb3f, - [0x12] = 0x3ffff, -}; - -/* RF-specific structure */ -struct uw2453_priv { - /* index into synth/VCO config tables where PLL lock was found - * -1 means autocal */ - int config; -}; - -#define UW2453_PRIV(rf) ((struct uw2453_priv *) (rf)->priv) - -static int uw2453_synth_set_channel(struct zd_chip *chip, int channel, - bool autocal) -{ - int r; - int idx = channel - 1; - u32 val; - - if (autocal) - val = UW2453_REGWRITE(1, uw2453_autocal_synth[idx]); - else - val = UW2453_REGWRITE(1, uw2453_std_synth[idx]); - - r = zd_rfwrite_locked(chip, val, RF_RV_BITS); - if (r) - return r; - - return zd_rfwrite_locked(chip, - UW2453_REGWRITE(2, uw2453_synth_divide[idx]), RF_RV_BITS); -} - -static int uw2453_write_vco_cfg(struct zd_chip *chip, u16 value) -{ - /* vendor driver always sets these upper bits even though the specs say - * they are reserved */ - u32 val = 0x40000 | value; - return zd_rfwrite_locked(chip, UW2453_REGWRITE(3, val), RF_RV_BITS); -} - -static int uw2453_init_mode(struct zd_chip *chip) -{ - static const u32 rv[] = { - UW2453_REGWRITE(0, 0x25f98), /* enter IDLE mode */ - UW2453_REGWRITE(0, 0x25f9a), /* enter CAL_VCO mode */ - UW2453_REGWRITE(0, 0x25f94), /* enter RX/TX mode */ - UW2453_REGWRITE(0, 0x27fd4), /* power down RSSI circuit */ - }; - - return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); -} - -static int uw2453_set_tx_gain_level(struct zd_chip *chip, int channel) -{ - u8 int_value = chip->pwr_int_values[channel - 1]; - - if (int_value >= ARRAY_SIZE(uw2453_txgain)) { - dev_dbg_f(zd_chip_dev(chip), "can't configure TX gain for " - "int value %x on channel %d\n", int_value, channel); - return 0; - } - - return zd_rfwrite_locked(chip, - UW2453_REGWRITE(7, uw2453_txgain[int_value]), RF_RV_BITS); -} - -static int uw2453_init_hw(struct zd_rf *rf) -{ - int i, r; - int found_config = -1; - u16 intr_status; - struct zd_chip *chip = zd_rf_to_chip(rf); - - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR10, 0x89 }, { ZD_CR15, 0x20 }, - { ZD_CR17, 0x28 }, /* 6112 no change */ - { ZD_CR23, 0x38 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 }, - { ZD_CR27, 0x15 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, - { ZD_CR33, 0x28 }, { ZD_CR34, 0x30 }, - { ZD_CR35, 0x43 }, /* 6112 3e->43 */ - { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, - { ZD_CR46, 0x92 }, /* 6112 96->92 */ - { ZD_CR47, 0x1e }, - { ZD_CR48, 0x04 }, /* 5602 Roger */ - { ZD_CR49, 0xfa }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, - { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, - { ZD_CR91, 0x00 }, { ZD_CR92, 0x0a }, { ZD_CR98, 0x8d }, - { ZD_CR99, 0x28 }, { ZD_CR100, 0x02 }, - { ZD_CR101, 0x09 }, /* 6112 13->1f 6220 1f->13 6407 13->9 */ - { ZD_CR102, 0x27 }, - { ZD_CR106, 0x1c }, /* 5d07 5112 1f->1c 6220 1c->1f - * 6221 1f->1c - */ - { ZD_CR107, 0x1c }, /* 6220 1c->1a 5221 1a->1c */ - { ZD_CR109, 0x13 }, - { ZD_CR110, 0x1f }, /* 6112 13->1f 6221 1f->13 6407 13->0x09 */ - { ZD_CR111, 0x13 }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, - { ZD_CR114, 0x23 }, /* 6221 27->23 */ - { ZD_CR115, 0x24 }, /* 6112 24->1c 6220 1c->24 */ - { ZD_CR116, 0x24 }, /* 6220 1c->24 */ - { ZD_CR117, 0xfa }, /* 6112 fa->f8 6220 f8->f4 6220 f4->fa */ - { ZD_CR118, 0xf0 }, /* 5d07 6112 f0->f2 6220 f2->f0 */ - { ZD_CR119, 0x1a }, /* 6112 1a->10 6220 10->14 6220 14->1a */ - { ZD_CR120, 0x4f }, - { ZD_CR121, 0x1f }, /* 6220 4f->1f */ - { ZD_CR122, 0xf0 }, { ZD_CR123, 0x57 }, { ZD_CR125, 0xad }, - { ZD_CR126, 0x6c }, { ZD_CR127, 0x03 }, - { ZD_CR128, 0x14 }, /* 6302 12->11 */ - { ZD_CR129, 0x12 }, /* 6301 10->0f */ - { ZD_CR130, 0x10 }, { ZD_CR137, 0x50 }, { ZD_CR138, 0xa8 }, - { ZD_CR144, 0xac }, { ZD_CR146, 0x20 }, { ZD_CR252, 0xff }, - { ZD_CR253, 0xff }, - }; - - static const u32 rv[] = { - UW2453_REGWRITE(4, 0x2b), /* configure receiver gain */ - UW2453_REGWRITE(5, 0x19e4f), /* configure transmitter gain */ - UW2453_REGWRITE(6, 0xf81ad), /* enable RX/TX filter tuning */ - UW2453_REGWRITE(7, 0x3fffe), /* disable TX gain in test mode */ - - /* enter CAL_FIL mode, TX gain set by registers, RX gain set by pins, - * RSSI circuit powered down, reduced RSSI range */ - UW2453_REGWRITE(0, 0x25f9c), /* 5d01 cal_fil */ - - /* synthesizer configuration for channel 1 */ - UW2453_REGWRITE(1, 0x47), - UW2453_REGWRITE(2, 0x999), - - /* disable manual VCO band selection */ - UW2453_REGWRITE(3, 0x7602), - - /* enable manual VCO band selection, configure current level */ - UW2453_REGWRITE(3, 0x46063), - }; - - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - if (r) - return r; - - r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); - if (r) - return r; - - r = uw2453_init_mode(chip); - if (r) - return r; - - /* Try all standard VCO configuration settings on channel 1 */ - for (i = 0; i < ARRAY_SIZE(uw2453_std_vco_cfg) - 1; i++) { - /* Configure synthesizer for channel 1 */ - r = uw2453_synth_set_channel(chip, 1, false); - if (r) - return r; - - /* Write VCO config */ - r = uw2453_write_vco_cfg(chip, uw2453_std_vco_cfg[i][0]); - if (r) - return r; - - /* ack interrupt event */ - r = zd_iowrite16_locked(chip, 0x0f, UW2453_INTR_REG); - if (r) - return r; - - /* check interrupt status */ - r = zd_ioread16_locked(chip, &intr_status, UW2453_INTR_REG); - if (r) - return r; - - if (!(intr_status & 0xf)) { - dev_dbg_f(zd_chip_dev(chip), - "PLL locked on configuration %d\n", i); - found_config = i; - break; - } - } - - if (found_config == -1) { - /* autocal */ - dev_dbg_f(zd_chip_dev(chip), - "PLL did not lock, using autocal\n"); - - r = uw2453_synth_set_channel(chip, 1, true); - if (r) - return r; - - r = uw2453_write_vco_cfg(chip, UW2453_AUTOCAL_VCO_CFG); - if (r) - return r; - } - - /* To match the vendor driver behaviour, we use the configuration after - * the one that produced a lock. */ - UW2453_PRIV(rf)->config = found_config + 1; - - return zd_iowrite16_locked(chip, 0x06, ZD_CR203); -} - -static int uw2453_set_channel(struct zd_rf *rf, u8 channel) -{ - int r; - u16 vco_cfg; - int config = UW2453_PRIV(rf)->config; - bool autocal = (config == -1); - struct zd_chip *chip = zd_rf_to_chip(rf); - - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 }, - { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 }, - }; - - r = uw2453_synth_set_channel(chip, channel, autocal); - if (r) - return r; - - if (autocal) - vco_cfg = UW2453_AUTOCAL_VCO_CFG; - else - vco_cfg = uw2453_std_vco_cfg[config][CHAN_TO_PAIRIDX(channel)]; - - r = uw2453_write_vco_cfg(chip, vco_cfg); - if (r) - return r; - - r = uw2453_init_mode(chip); - if (r) - return r; - - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - if (r) - return r; - - r = uw2453_set_tx_gain_level(chip, channel); - if (r) - return r; - - return zd_iowrite16_locked(chip, 0x06, ZD_CR203); -} - -static int uw2453_switch_radio_on(struct zd_rf *rf) -{ - int r; - struct zd_chip *chip = zd_rf_to_chip(rf); - struct zd_ioreq16 ioreqs[] = { - { ZD_CR11, 0x00 }, { ZD_CR251, 0x3f }, - }; - - /* enter RXTX mode */ - r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f94), RF_RV_BITS); - if (r) - return r; - - if (zd_chip_is_zd1211b(chip)) - ioreqs[1].value = 0x7f; - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static int uw2453_switch_radio_off(struct zd_rf *rf) -{ - int r; - struct zd_chip *chip = zd_rf_to_chip(rf); - static const struct zd_ioreq16 ioreqs[] = { - { ZD_CR11, 0x04 }, { ZD_CR251, 0x2f }, - }; - - /* enter IDLE mode */ - /* FIXME: shouldn't we go to SLEEP? sent email to zydas */ - r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f90), RF_RV_BITS); - if (r) - return r; - - return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); -} - -static void uw2453_clear(struct zd_rf *rf) -{ - kfree(rf->priv); -} - -int zd_rf_init_uw2453(struct zd_rf *rf) -{ - rf->init_hw = uw2453_init_hw; - rf->set_channel = uw2453_set_channel; - rf->switch_radio_on = uw2453_switch_radio_on; - rf->switch_radio_off = uw2453_switch_radio_off; - rf->patch_6m_band_edge = zd_rf_generic_patch_6m; - rf->clear = uw2453_clear; - /* we have our own TX integration code */ - rf->update_channel_int = 0; - - rf->priv = kmalloc(sizeof(struct uw2453_priv), GFP_KERNEL); - if (rf->priv == NULL) - return -ENOMEM; - - return 0; -} - diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c deleted file mode 100644 index a912dc051111..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ /dev/null @@ -1,2060 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * Copyright (C) 2006-2007 Michael Wu - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "zd_def.h" -#include "zd_mac.h" -#include "zd_usb.h" - -static struct usb_device_id usb_ids[] = { - /* ZD1211 */ - { USB_DEVICE(0x0105, 0x145f), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0586, 0x3402), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0586, 0x3407), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0586, 0x3409), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x079b, 0x004a), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0ace, 0xa211), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0b05, 0x170c), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0b3b, 0x5630), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0df6, 0x9075), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x129b, 0x1666), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x1435, 0x0711), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x14ea, 0xab10), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x157e, 0x300a), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x157e, 0x3204), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x157e, 0x3207), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, - /* ZD1211B */ - { USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0409, 0x0248), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0411, 0x00da), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x054c, 0x0257), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0586, 0x340a), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0586, 0x3413), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x07fa, 0x1196), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x083a, 0xe501), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x083a, 0xe503), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x083a, 0xe506), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0ace, 0xb215), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0cde, 0x001a), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0df6, 0x0036), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x129b, 0x1667), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x2019, 0x5303), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x2019, 0xed01), .driver_info = DEVICE_ZD1211B }, - /* "Driverless" devices that need ejecting */ - { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, - { USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER }, - {} -}; - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("USB driver for devices with the ZD1211 chip."); -MODULE_AUTHOR("Ulrich Kunitz"); -MODULE_AUTHOR("Daniel Drake"); -MODULE_VERSION("1.0"); -MODULE_DEVICE_TABLE(usb, usb_ids); - -#define FW_ZD1211_PREFIX "zd1211/zd1211_" -#define FW_ZD1211B_PREFIX "zd1211/zd1211b_" - -static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req, - unsigned int count); - -/* USB device initialization */ -static void int_urb_complete(struct urb *urb); - -static int request_fw_file( - const struct firmware **fw, const char *name, struct device *device) -{ - int r; - - dev_dbg_f(device, "fw name %s\n", name); - - r = request_firmware(fw, name, device); - if (r) - dev_err(device, - "Could not load firmware file %s. Error number %d\n", - name, r); - return r; -} - -static inline u16 get_bcdDevice(const struct usb_device *udev) -{ - return le16_to_cpu(udev->descriptor.bcdDevice); -} - -enum upload_code_flags { - REBOOT = 1, -}; - -/* Ensures that MAX_TRANSFER_SIZE is even. */ -#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1) - -static int upload_code(struct usb_device *udev, - const u8 *data, size_t size, u16 code_offset, int flags) -{ - u8 *p; - int r; - - /* USB request blocks need "kmalloced" buffers. - */ - p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL); - if (!p) { - r = -ENOMEM; - goto error; - } - - size &= ~1; - while (size > 0) { - size_t transfer_size = size <= MAX_TRANSFER_SIZE ? - size : MAX_TRANSFER_SIZE; - - dev_dbg_f(&udev->dev, "transfer size %zu\n", transfer_size); - - memcpy(p, data, transfer_size); - r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_FIRMWARE_DOWNLOAD, - USB_DIR_OUT | USB_TYPE_VENDOR, - code_offset, 0, p, transfer_size, 1000 /* ms */); - if (r < 0) { - dev_err(&udev->dev, - "USB control request for firmware upload" - " failed. Error number %d\n", r); - goto error; - } - transfer_size = r & ~1; - - size -= transfer_size; - data += transfer_size; - code_offset += transfer_size/sizeof(u16); - } - - if (flags & REBOOT) { - u8 ret; - - /* Use "DMA-aware" buffer. */ - r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - USB_REQ_FIRMWARE_CONFIRM, - USB_DIR_IN | USB_TYPE_VENDOR, - 0, 0, p, sizeof(ret), 5000 /* ms */); - if (r != sizeof(ret)) { - dev_err(&udev->dev, - "control request firmeware confirmation failed." - " Return value %d\n", r); - if (r >= 0) - r = -ENODEV; - goto error; - } - ret = p[0]; - if (ret & 0x80) { - dev_err(&udev->dev, - "Internal error while downloading." - " Firmware confirm return value %#04x\n", - (unsigned int)ret); - r = -ENODEV; - goto error; - } - dev_dbg_f(&udev->dev, "firmware confirm return value %#04x\n", - (unsigned int)ret); - } - - r = 0; -error: - kfree(p); - return r; -} - -static u16 get_word(const void *data, u16 offset) -{ - const __le16 *p = data; - return le16_to_cpu(p[offset]); -} - -static char *get_fw_name(struct zd_usb *usb, char *buffer, size_t size, - const char* postfix) -{ - scnprintf(buffer, size, "%s%s", - usb->is_zd1211b ? - FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX, - postfix); - return buffer; -} - -static int handle_version_mismatch(struct zd_usb *usb, - const struct firmware *ub_fw) -{ - struct usb_device *udev = zd_usb_to_usbdev(usb); - const struct firmware *ur_fw = NULL; - int offset; - int r = 0; - char fw_name[128]; - - r = request_fw_file(&ur_fw, - get_fw_name(usb, fw_name, sizeof(fw_name), "ur"), - &udev->dev); - if (r) - goto error; - - r = upload_code(udev, ur_fw->data, ur_fw->size, FW_START, REBOOT); - if (r) - goto error; - - offset = (E2P_BOOT_CODE_OFFSET * sizeof(u16)); - r = upload_code(udev, ub_fw->data + offset, ub_fw->size - offset, - E2P_START + E2P_BOOT_CODE_OFFSET, REBOOT); - - /* At this point, the vendor driver downloads the whole firmware - * image, hacks around with version IDs, and uploads it again, - * completely overwriting the boot code. We do not do this here as - * it is not required on any tested devices, and it is suspected to - * cause problems. */ -error: - release_firmware(ur_fw); - return r; -} - -static int upload_firmware(struct zd_usb *usb) -{ - int r; - u16 fw_bcdDevice; - u16 bcdDevice; - struct usb_device *udev = zd_usb_to_usbdev(usb); - const struct firmware *ub_fw = NULL; - const struct firmware *uph_fw = NULL; - char fw_name[128]; - - bcdDevice = get_bcdDevice(udev); - - r = request_fw_file(&ub_fw, - get_fw_name(usb, fw_name, sizeof(fw_name), "ub"), - &udev->dev); - if (r) - goto error; - - fw_bcdDevice = get_word(ub_fw->data, E2P_DATA_OFFSET); - - if (fw_bcdDevice != bcdDevice) { - dev_info(&udev->dev, - "firmware version %#06x and device bootcode version " - "%#06x differ\n", fw_bcdDevice, bcdDevice); - if (bcdDevice <= 0x4313) - dev_warn(&udev->dev, "device has old bootcode, please " - "report success or failure\n"); - - r = handle_version_mismatch(usb, ub_fw); - if (r) - goto error; - } else { - dev_dbg_f(&udev->dev, - "firmware device id %#06x is equal to the " - "actual device id\n", fw_bcdDevice); - } - - - r = request_fw_file(&uph_fw, - get_fw_name(usb, fw_name, sizeof(fw_name), "uphr"), - &udev->dev); - if (r) - goto error; - - r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START, REBOOT); - if (r) { - dev_err(&udev->dev, - "Could not upload firmware code uph. Error number %d\n", - r); - } - - /* FALL-THROUGH */ -error: - release_firmware(ub_fw); - release_firmware(uph_fw); - return r; -} - -MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ur"); -MODULE_FIRMWARE(FW_ZD1211_PREFIX "ur"); -MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ub"); -MODULE_FIRMWARE(FW_ZD1211_PREFIX "ub"); -MODULE_FIRMWARE(FW_ZD1211B_PREFIX "uphr"); -MODULE_FIRMWARE(FW_ZD1211_PREFIX "uphr"); - -/* Read data from device address space using "firmware interface" which does - * not require firmware to be loaded. */ -int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len) -{ - int r; - struct usb_device *udev = zd_usb_to_usbdev(usb); - u8 *buf; - - /* Use "DMA-aware" buffer. */ - buf = kmalloc(len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - USB_REQ_FIRMWARE_READ_DATA, USB_DIR_IN | 0x40, addr, 0, - buf, len, 5000); - if (r < 0) { - dev_err(&udev->dev, - "read over firmware interface failed: %d\n", r); - goto exit; - } else if (r != len) { - dev_err(&udev->dev, - "incomplete read over firmware interface: %d/%d\n", - r, len); - r = -EIO; - goto exit; - } - r = 0; - memcpy(data, buf, len); -exit: - kfree(buf); - return r; -} - -#define urb_dev(urb) (&(urb)->dev->dev) - -static inline void handle_regs_int_override(struct urb *urb) -{ - struct zd_usb *usb = urb->context; - struct zd_usb_interrupt *intr = &usb->intr; - - spin_lock(&intr->lock); - if (atomic_read(&intr->read_regs_enabled)) { - atomic_set(&intr->read_regs_enabled, 0); - intr->read_regs_int_overridden = 1; - complete(&intr->read_regs.completion); - } - spin_unlock(&intr->lock); -} - -static inline void handle_regs_int(struct urb *urb) -{ - struct zd_usb *usb = urb->context; - struct zd_usb_interrupt *intr = &usb->intr; - int len; - u16 int_num; - - ZD_ASSERT(in_interrupt()); - spin_lock(&intr->lock); - - int_num = le16_to_cpu(*(__le16 *)(urb->transfer_buffer+2)); - if (int_num == CR_INTERRUPT) { - struct zd_mac *mac = zd_hw_mac(zd_usb_to_hw(urb->context)); - spin_lock(&mac->lock); - memcpy(&mac->intr_buffer, urb->transfer_buffer, - USB_MAX_EP_INT_BUFFER); - spin_unlock(&mac->lock); - schedule_work(&mac->process_intr); - } else if (atomic_read(&intr->read_regs_enabled)) { - len = urb->actual_length; - intr->read_regs.length = urb->actual_length; - if (len > sizeof(intr->read_regs.buffer)) - len = sizeof(intr->read_regs.buffer); - - memcpy(intr->read_regs.buffer, urb->transfer_buffer, len); - - /* Sometimes USB_INT_ID_REGS is not overridden, but comes after - * USB_INT_ID_RETRY_FAILED. Read-reg retry then gets this - * delayed USB_INT_ID_REGS, but leaves USB_INT_ID_REGS of - * retry unhandled. Next read-reg command then might catch - * this wrong USB_INT_ID_REGS. Fix by ignoring wrong reads. - */ - if (!check_read_regs(usb, intr->read_regs.req, - intr->read_regs.req_count)) - goto out; - - atomic_set(&intr->read_regs_enabled, 0); - intr->read_regs_int_overridden = 0; - complete(&intr->read_regs.completion); - - goto out; - } - -out: - spin_unlock(&intr->lock); - - /* CR_INTERRUPT might override read_reg too. */ - if (int_num == CR_INTERRUPT && atomic_read(&intr->read_regs_enabled)) - handle_regs_int_override(urb); -} - -static void int_urb_complete(struct urb *urb) -{ - int r; - struct usb_int_header *hdr; - struct zd_usb *usb; - struct zd_usb_interrupt *intr; - - switch (urb->status) { - case 0: - break; - case -ESHUTDOWN: - case -EINVAL: - case -ENODEV: - case -ENOENT: - case -ECONNRESET: - case -EPIPE: - dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); - return; - default: - dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); - goto resubmit; - } - - if (urb->actual_length < sizeof(hdr)) { - dev_dbg_f(urb_dev(urb), "error: urb %p to small\n", urb); - goto resubmit; - } - - hdr = urb->transfer_buffer; - if (hdr->type != USB_INT_TYPE) { - dev_dbg_f(urb_dev(urb), "error: urb %p wrong type\n", urb); - goto resubmit; - } - - /* USB_INT_ID_RETRY_FAILED triggered by tx-urb submit can override - * pending USB_INT_ID_REGS causing read command timeout. - */ - usb = urb->context; - intr = &usb->intr; - if (hdr->id != USB_INT_ID_REGS && atomic_read(&intr->read_regs_enabled)) - handle_regs_int_override(urb); - - switch (hdr->id) { - case USB_INT_ID_REGS: - handle_regs_int(urb); - break; - case USB_INT_ID_RETRY_FAILED: - zd_mac_tx_failed(urb); - break; - default: - dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb, - (unsigned int)hdr->id); - goto resubmit; - } - -resubmit: - r = usb_submit_urb(urb, GFP_ATOMIC); - if (r) { - dev_dbg_f(urb_dev(urb), "error: resubmit urb %p err code %d\n", - urb, r); - /* TODO: add worker to reset intr->urb */ - } - return; -} - -static inline int int_urb_interval(struct usb_device *udev) -{ - switch (udev->speed) { - case USB_SPEED_HIGH: - return 4; - case USB_SPEED_LOW: - return 10; - case USB_SPEED_FULL: - default: - return 1; - } -} - -static inline int usb_int_enabled(struct zd_usb *usb) -{ - unsigned long flags; - struct zd_usb_interrupt *intr = &usb->intr; - struct urb *urb; - - spin_lock_irqsave(&intr->lock, flags); - urb = intr->urb; - spin_unlock_irqrestore(&intr->lock, flags); - return urb != NULL; -} - -int zd_usb_enable_int(struct zd_usb *usb) -{ - int r; - struct usb_device *udev = zd_usb_to_usbdev(usb); - struct zd_usb_interrupt *intr = &usb->intr; - struct urb *urb; - - dev_dbg_f(zd_usb_dev(usb), "\n"); - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - r = -ENOMEM; - goto out; - } - - ZD_ASSERT(!irqs_disabled()); - spin_lock_irq(&intr->lock); - if (intr->urb) { - spin_unlock_irq(&intr->lock); - r = 0; - goto error_free_urb; - } - intr->urb = urb; - spin_unlock_irq(&intr->lock); - - r = -ENOMEM; - intr->buffer = usb_alloc_coherent(udev, USB_MAX_EP_INT_BUFFER, - GFP_KERNEL, &intr->buffer_dma); - if (!intr->buffer) { - dev_dbg_f(zd_usb_dev(usb), - "couldn't allocate transfer_buffer\n"); - goto error_set_urb_null; - } - - usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN), - intr->buffer, USB_MAX_EP_INT_BUFFER, - int_urb_complete, usb, - intr->interval); - urb->transfer_dma = intr->buffer_dma; - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb); - r = usb_submit_urb(urb, GFP_KERNEL); - if (r) { - dev_dbg_f(zd_usb_dev(usb), - "Couldn't submit urb. Error number %d\n", r); - goto error; - } - - return 0; -error: - usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER, - intr->buffer, intr->buffer_dma); -error_set_urb_null: - spin_lock_irq(&intr->lock); - intr->urb = NULL; - spin_unlock_irq(&intr->lock); -error_free_urb: - usb_free_urb(urb); -out: - return r; -} - -void zd_usb_disable_int(struct zd_usb *usb) -{ - unsigned long flags; - struct usb_device *udev = zd_usb_to_usbdev(usb); - struct zd_usb_interrupt *intr = &usb->intr; - struct urb *urb; - void *buffer; - dma_addr_t buffer_dma; - - spin_lock_irqsave(&intr->lock, flags); - urb = intr->urb; - if (!urb) { - spin_unlock_irqrestore(&intr->lock, flags); - return; - } - intr->urb = NULL; - buffer = intr->buffer; - buffer_dma = intr->buffer_dma; - intr->buffer = NULL; - spin_unlock_irqrestore(&intr->lock, flags); - - usb_kill_urb(urb); - dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb); - usb_free_urb(urb); - - if (buffer) - usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER, - buffer, buffer_dma); -} - -static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer, - unsigned int length) -{ - int i; - const struct rx_length_info *length_info; - - if (length < sizeof(struct rx_length_info)) { - /* It's not a complete packet anyhow. */ - dev_dbg_f(zd_usb_dev(usb), "invalid, small RX packet : %d\n", - length); - return; - } - length_info = (struct rx_length_info *) - (buffer + length - sizeof(struct rx_length_info)); - - /* It might be that three frames are merged into a single URB - * transaction. We have to check for the length info tag. - * - * While testing we discovered that length_info might be unaligned, - * because if USB transactions are merged, the last packet will not - * be padded. Unaligned access might also happen if the length_info - * structure is not present. - */ - if (get_unaligned_le16(&length_info->tag) == RX_LENGTH_INFO_TAG) - { - unsigned int l, k, n; - for (i = 0, l = 0;; i++) { - k = get_unaligned_le16(&length_info->length[i]); - if (k == 0) - return; - n = l+k; - if (n > length) - return; - zd_mac_rx(zd_usb_to_hw(usb), buffer+l, k); - if (i >= 2) - return; - l = (n+3) & ~3; - } - } else { - zd_mac_rx(zd_usb_to_hw(usb), buffer, length); - } -} - -static void rx_urb_complete(struct urb *urb) -{ - int r; - struct zd_usb *usb; - struct zd_usb_rx *rx; - const u8 *buffer; - unsigned int length; - - switch (urb->status) { - case 0: - break; - case -ESHUTDOWN: - case -EINVAL: - case -ENODEV: - case -ENOENT: - case -ECONNRESET: - case -EPIPE: - dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); - return; - default: - dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); - goto resubmit; - } - - buffer = urb->transfer_buffer; - length = urb->actual_length; - usb = urb->context; - rx = &usb->rx; - - tasklet_schedule(&rx->reset_timer_tasklet); - - if (length%rx->usb_packet_size > rx->usb_packet_size-4) { - /* If there is an old first fragment, we don't care. */ - dev_dbg_f(urb_dev(urb), "*** first fragment ***\n"); - ZD_ASSERT(length <= ARRAY_SIZE(rx->fragment)); - spin_lock(&rx->lock); - memcpy(rx->fragment, buffer, length); - rx->fragment_length = length; - spin_unlock(&rx->lock); - goto resubmit; - } - - spin_lock(&rx->lock); - if (rx->fragment_length > 0) { - /* We are on a second fragment, we believe */ - ZD_ASSERT(length + rx->fragment_length <= - ARRAY_SIZE(rx->fragment)); - dev_dbg_f(urb_dev(urb), "*** second fragment ***\n"); - memcpy(rx->fragment+rx->fragment_length, buffer, length); - handle_rx_packet(usb, rx->fragment, - rx->fragment_length + length); - rx->fragment_length = 0; - spin_unlock(&rx->lock); - } else { - spin_unlock(&rx->lock); - handle_rx_packet(usb, buffer, length); - } - -resubmit: - r = usb_submit_urb(urb, GFP_ATOMIC); - if (r) - dev_dbg_f(urb_dev(urb), "urb %p resubmit error %d\n", urb, r); -} - -static struct urb *alloc_rx_urb(struct zd_usb *usb) -{ - struct usb_device *udev = zd_usb_to_usbdev(usb); - struct urb *urb; - void *buffer; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - return NULL; - buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL, - &urb->transfer_dma); - if (!buffer) { - usb_free_urb(urb); - return NULL; - } - - usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN), - buffer, USB_MAX_RX_SIZE, - rx_urb_complete, usb); - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - return urb; -} - -static void free_rx_urb(struct urb *urb) -{ - if (!urb) - return; - usb_free_coherent(urb->dev, urb->transfer_buffer_length, - urb->transfer_buffer, urb->transfer_dma); - usb_free_urb(urb); -} - -static int __zd_usb_enable_rx(struct zd_usb *usb) -{ - int i, r; - struct zd_usb_rx *rx = &usb->rx; - struct urb **urbs; - - dev_dbg_f(zd_usb_dev(usb), "\n"); - - r = -ENOMEM; - urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL); - if (!urbs) - goto error; - for (i = 0; i < RX_URBS_COUNT; i++) { - urbs[i] = alloc_rx_urb(usb); - if (!urbs[i]) - goto error; - } - - ZD_ASSERT(!irqs_disabled()); - spin_lock_irq(&rx->lock); - if (rx->urbs) { - spin_unlock_irq(&rx->lock); - r = 0; - goto error; - } - rx->urbs = urbs; - rx->urbs_count = RX_URBS_COUNT; - spin_unlock_irq(&rx->lock); - - for (i = 0; i < RX_URBS_COUNT; i++) { - r = usb_submit_urb(urbs[i], GFP_KERNEL); - if (r) - goto error_submit; - } - - return 0; -error_submit: - for (i = 0; i < RX_URBS_COUNT; i++) { - usb_kill_urb(urbs[i]); - } - spin_lock_irq(&rx->lock); - rx->urbs = NULL; - rx->urbs_count = 0; - spin_unlock_irq(&rx->lock); -error: - if (urbs) { - for (i = 0; i < RX_URBS_COUNT; i++) - free_rx_urb(urbs[i]); - } - return r; -} - -int zd_usb_enable_rx(struct zd_usb *usb) -{ - int r; - struct zd_usb_rx *rx = &usb->rx; - - mutex_lock(&rx->setup_mutex); - r = __zd_usb_enable_rx(usb); - mutex_unlock(&rx->setup_mutex); - - zd_usb_reset_rx_idle_timer(usb); - - return r; -} - -static void __zd_usb_disable_rx(struct zd_usb *usb) -{ - int i; - unsigned long flags; - struct urb **urbs; - unsigned int count; - struct zd_usb_rx *rx = &usb->rx; - - spin_lock_irqsave(&rx->lock, flags); - urbs = rx->urbs; - count = rx->urbs_count; - spin_unlock_irqrestore(&rx->lock, flags); - if (!urbs) - return; - - for (i = 0; i < count; i++) { - usb_kill_urb(urbs[i]); - free_rx_urb(urbs[i]); - } - kfree(urbs); - - spin_lock_irqsave(&rx->lock, flags); - rx->urbs = NULL; - rx->urbs_count = 0; - spin_unlock_irqrestore(&rx->lock, flags); -} - -void zd_usb_disable_rx(struct zd_usb *usb) -{ - struct zd_usb_rx *rx = &usb->rx; - - mutex_lock(&rx->setup_mutex); - __zd_usb_disable_rx(usb); - mutex_unlock(&rx->setup_mutex); - - tasklet_kill(&rx->reset_timer_tasklet); - cancel_delayed_work_sync(&rx->idle_work); -} - -static void zd_usb_reset_rx(struct zd_usb *usb) -{ - bool do_reset; - struct zd_usb_rx *rx = &usb->rx; - unsigned long flags; - - mutex_lock(&rx->setup_mutex); - - spin_lock_irqsave(&rx->lock, flags); - do_reset = rx->urbs != NULL; - spin_unlock_irqrestore(&rx->lock, flags); - - if (do_reset) { - __zd_usb_disable_rx(usb); - __zd_usb_enable_rx(usb); - } - - mutex_unlock(&rx->setup_mutex); - - if (do_reset) - zd_usb_reset_rx_idle_timer(usb); -} - -/** - * zd_usb_disable_tx - disable transmission - * @usb: the zd1211rw-private USB structure - * - * Frees all URBs in the free list and marks the transmission as disabled. - */ -void zd_usb_disable_tx(struct zd_usb *usb) -{ - struct zd_usb_tx *tx = &usb->tx; - unsigned long flags; - - atomic_set(&tx->enabled, 0); - - /* kill all submitted tx-urbs */ - usb_kill_anchored_urbs(&tx->submitted); - - spin_lock_irqsave(&tx->lock, flags); - WARN_ON(!skb_queue_empty(&tx->submitted_skbs)); - WARN_ON(tx->submitted_urbs != 0); - tx->submitted_urbs = 0; - spin_unlock_irqrestore(&tx->lock, flags); - - /* The stopped state is ignored, relying on ieee80211_wake_queues() - * in a potentionally following zd_usb_enable_tx(). - */ -} - -/** - * zd_usb_enable_tx - enables transmission - * @usb: a &struct zd_usb pointer - * - * This function enables transmission and prepares the &zd_usb_tx data - * structure. - */ -void zd_usb_enable_tx(struct zd_usb *usb) -{ - unsigned long flags; - struct zd_usb_tx *tx = &usb->tx; - - spin_lock_irqsave(&tx->lock, flags); - atomic_set(&tx->enabled, 1); - tx->submitted_urbs = 0; - ieee80211_wake_queues(zd_usb_to_hw(usb)); - tx->stopped = 0; - spin_unlock_irqrestore(&tx->lock, flags); -} - -static void tx_dec_submitted_urbs(struct zd_usb *usb) -{ - struct zd_usb_tx *tx = &usb->tx; - unsigned long flags; - - spin_lock_irqsave(&tx->lock, flags); - --tx->submitted_urbs; - if (tx->stopped && tx->submitted_urbs <= ZD_USB_TX_LOW) { - ieee80211_wake_queues(zd_usb_to_hw(usb)); - tx->stopped = 0; - } - spin_unlock_irqrestore(&tx->lock, flags); -} - -static void tx_inc_submitted_urbs(struct zd_usb *usb) -{ - struct zd_usb_tx *tx = &usb->tx; - unsigned long flags; - - spin_lock_irqsave(&tx->lock, flags); - ++tx->submitted_urbs; - if (!tx->stopped && tx->submitted_urbs > ZD_USB_TX_HIGH) { - ieee80211_stop_queues(zd_usb_to_hw(usb)); - tx->stopped = 1; - } - spin_unlock_irqrestore(&tx->lock, flags); -} - -/** - * tx_urb_complete - completes the execution of an URB - * @urb: a URB - * - * This function is called if the URB has been transferred to a device or an - * error has happened. - */ -static void tx_urb_complete(struct urb *urb) -{ - int r; - struct sk_buff *skb; - struct ieee80211_tx_info *info; - struct zd_usb *usb; - struct zd_usb_tx *tx; - - skb = (struct sk_buff *)urb->context; - info = IEEE80211_SKB_CB(skb); - /* - * grab 'usb' pointer before handing off the skb (since - * it might be freed by zd_mac_tx_to_dev or mac80211) - */ - usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb; - tx = &usb->tx; - - switch (urb->status) { - case 0: - break; - case -ESHUTDOWN: - case -EINVAL: - case -ENODEV: - case -ENOENT: - case -ECONNRESET: - case -EPIPE: - dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); - break; - default: - dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); - goto resubmit; - } -free_urb: - skb_unlink(skb, &usb->tx.submitted_skbs); - zd_mac_tx_to_dev(skb, urb->status); - usb_free_urb(urb); - tx_dec_submitted_urbs(usb); - return; -resubmit: - usb_anchor_urb(urb, &tx->submitted); - r = usb_submit_urb(urb, GFP_ATOMIC); - if (r) { - usb_unanchor_urb(urb); - dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r); - goto free_urb; - } -} - -/** - * zd_usb_tx: initiates transfer of a frame of the device - * - * @usb: the zd1211rw-private USB structure - * @skb: a &struct sk_buff pointer - * - * This function tranmits a frame to the device. It doesn't wait for - * completion. The frame must contain the control set and have all the - * control set information available. - * - * The function returns 0 if the transfer has been successfully initiated. - */ -int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb) -{ - int r; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct usb_device *udev = zd_usb_to_usbdev(usb); - struct urb *urb; - struct zd_usb_tx *tx = &usb->tx; - - if (!atomic_read(&tx->enabled)) { - r = -ENOENT; - goto out; - } - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - r = -ENOMEM; - goto out; - } - - usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT), - skb->data, skb->len, tx_urb_complete, skb); - - info->rate_driver_data[1] = (void *)jiffies; - skb_queue_tail(&tx->submitted_skbs, skb); - usb_anchor_urb(urb, &tx->submitted); - - r = usb_submit_urb(urb, GFP_ATOMIC); - if (r) { - dev_dbg_f(zd_usb_dev(usb), "error submit urb %p %d\n", urb, r); - usb_unanchor_urb(urb); - skb_unlink(skb, &tx->submitted_skbs); - goto error; - } - tx_inc_submitted_urbs(usb); - return 0; -error: - usb_free_urb(urb); -out: - return r; -} - -static bool zd_tx_timeout(struct zd_usb *usb) -{ - struct zd_usb_tx *tx = &usb->tx; - struct sk_buff_head *q = &tx->submitted_skbs; - struct sk_buff *skb, *skbnext; - struct ieee80211_tx_info *info; - unsigned long flags, trans_start; - bool have_timedout = false; - - spin_lock_irqsave(&q->lock, flags); - skb_queue_walk_safe(q, skb, skbnext) { - info = IEEE80211_SKB_CB(skb); - trans_start = (unsigned long)info->rate_driver_data[1]; - - if (time_is_before_jiffies(trans_start + ZD_TX_TIMEOUT)) { - have_timedout = true; - break; - } - } - spin_unlock_irqrestore(&q->lock, flags); - - return have_timedout; -} - -static void zd_tx_watchdog_handler(struct work_struct *work) -{ - struct zd_usb *usb = - container_of(work, struct zd_usb, tx.watchdog_work.work); - struct zd_usb_tx *tx = &usb->tx; - - if (!atomic_read(&tx->enabled) || !tx->watchdog_enabled) - goto out; - if (!zd_tx_timeout(usb)) - goto out; - - /* TX halted, try reset */ - dev_warn(zd_usb_dev(usb), "TX-stall detected, resetting device..."); - - usb_queue_reset_device(usb->intf); - - /* reset will stop this worker, don't rearm */ - return; -out: - queue_delayed_work(zd_workqueue, &tx->watchdog_work, - ZD_TX_WATCHDOG_INTERVAL); -} - -void zd_tx_watchdog_enable(struct zd_usb *usb) -{ - struct zd_usb_tx *tx = &usb->tx; - - if (!tx->watchdog_enabled) { - dev_dbg_f(zd_usb_dev(usb), "\n"); - queue_delayed_work(zd_workqueue, &tx->watchdog_work, - ZD_TX_WATCHDOG_INTERVAL); - tx->watchdog_enabled = 1; - } -} - -void zd_tx_watchdog_disable(struct zd_usb *usb) -{ - struct zd_usb_tx *tx = &usb->tx; - - if (tx->watchdog_enabled) { - dev_dbg_f(zd_usb_dev(usb), "\n"); - tx->watchdog_enabled = 0; - cancel_delayed_work_sync(&tx->watchdog_work); - } -} - -static void zd_rx_idle_timer_handler(struct work_struct *work) -{ - struct zd_usb *usb = - container_of(work, struct zd_usb, rx.idle_work.work); - struct zd_mac *mac = zd_usb_to_mac(usb); - - if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) - return; - - dev_dbg_f(zd_usb_dev(usb), "\n"); - - /* 30 seconds since last rx, reset rx */ - zd_usb_reset_rx(usb); -} - -static void zd_usb_reset_rx_idle_timer_tasklet(unsigned long param) -{ - struct zd_usb *usb = (struct zd_usb *)param; - - zd_usb_reset_rx_idle_timer(usb); -} - -void zd_usb_reset_rx_idle_timer(struct zd_usb *usb) -{ - struct zd_usb_rx *rx = &usb->rx; - - mod_delayed_work(zd_workqueue, &rx->idle_work, ZD_RX_IDLE_INTERVAL); -} - -static inline void init_usb_interrupt(struct zd_usb *usb) -{ - struct zd_usb_interrupt *intr = &usb->intr; - - spin_lock_init(&intr->lock); - intr->interval = int_urb_interval(zd_usb_to_usbdev(usb)); - init_completion(&intr->read_regs.completion); - atomic_set(&intr->read_regs_enabled, 0); - intr->read_regs.cr_int_addr = cpu_to_le16((u16)CR_INTERRUPT); -} - -static inline void init_usb_rx(struct zd_usb *usb) -{ - struct zd_usb_rx *rx = &usb->rx; - - spin_lock_init(&rx->lock); - mutex_init(&rx->setup_mutex); - if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) { - rx->usb_packet_size = 512; - } else { - rx->usb_packet_size = 64; - } - ZD_ASSERT(rx->fragment_length == 0); - INIT_DELAYED_WORK(&rx->idle_work, zd_rx_idle_timer_handler); - rx->reset_timer_tasklet.func = zd_usb_reset_rx_idle_timer_tasklet; - rx->reset_timer_tasklet.data = (unsigned long)usb; -} - -static inline void init_usb_tx(struct zd_usb *usb) -{ - struct zd_usb_tx *tx = &usb->tx; - - spin_lock_init(&tx->lock); - atomic_set(&tx->enabled, 0); - tx->stopped = 0; - skb_queue_head_init(&tx->submitted_skbs); - init_usb_anchor(&tx->submitted); - tx->submitted_urbs = 0; - tx->watchdog_enabled = 0; - INIT_DELAYED_WORK(&tx->watchdog_work, zd_tx_watchdog_handler); -} - -void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw, - struct usb_interface *intf) -{ - memset(usb, 0, sizeof(*usb)); - usb->intf = usb_get_intf(intf); - usb_set_intfdata(usb->intf, hw); - init_usb_anchor(&usb->submitted_cmds); - init_usb_interrupt(usb); - init_usb_tx(usb); - init_usb_rx(usb); -} - -void zd_usb_clear(struct zd_usb *usb) -{ - usb_set_intfdata(usb->intf, NULL); - usb_put_intf(usb->intf); - ZD_MEMCLEAR(usb, sizeof(*usb)); - /* FIXME: usb_interrupt, usb_tx, usb_rx? */ -} - -static const char *speed(enum usb_device_speed speed) -{ - switch (speed) { - case USB_SPEED_LOW: - return "low"; - case USB_SPEED_FULL: - return "full"; - case USB_SPEED_HIGH: - return "high"; - default: - return "unknown speed"; - } -} - -static int scnprint_id(struct usb_device *udev, char *buffer, size_t size) -{ - return scnprintf(buffer, size, "%04hx:%04hx v%04hx %s", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), - get_bcdDevice(udev), - speed(udev->speed)); -} - -int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size) -{ - struct usb_device *udev = interface_to_usbdev(usb->intf); - return scnprint_id(udev, buffer, size); -} - -#ifdef DEBUG -static void print_id(struct usb_device *udev) -{ - char buffer[40]; - - scnprint_id(udev, buffer, sizeof(buffer)); - buffer[sizeof(buffer)-1] = 0; - dev_dbg_f(&udev->dev, "%s\n", buffer); -} -#else -#define print_id(udev) do { } while (0) -#endif - -static int eject_installer(struct usb_interface *intf) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_host_interface *iface_desc = &intf->altsetting[0]; - struct usb_endpoint_descriptor *endpoint; - unsigned char *cmd; - u8 bulk_out_ep; - int r; - - /* Find bulk out endpoint */ - for (r = 1; r >= 0; r--) { - endpoint = &iface_desc->endpoint[r].desc; - if (usb_endpoint_dir_out(endpoint) && - usb_endpoint_xfer_bulk(endpoint)) { - bulk_out_ep = endpoint->bEndpointAddress; - break; - } - } - if (r == -1) { - dev_err(&udev->dev, - "zd1211rw: Could not find bulk out endpoint\n"); - return -ENODEV; - } - - cmd = kzalloc(31, GFP_KERNEL); - if (cmd == NULL) - return -ENODEV; - - /* USB bulk command block */ - cmd[0] = 0x55; /* bulk command signature */ - cmd[1] = 0x53; /* bulk command signature */ - cmd[2] = 0x42; /* bulk command signature */ - cmd[3] = 0x43; /* bulk command signature */ - cmd[14] = 6; /* command length */ - - cmd[15] = 0x1b; /* SCSI command: START STOP UNIT */ - cmd[19] = 0x2; /* eject disc */ - - dev_info(&udev->dev, "Ejecting virtual installer media...\n"); - r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_ep), - cmd, 31, NULL, 2000); - kfree(cmd); - if (r) - return r; - - /* At this point, the device disconnects and reconnects with the real - * ID numbers. */ - - usb_set_intfdata(intf, NULL); - return 0; -} - -int zd_usb_init_hw(struct zd_usb *usb) -{ - int r; - struct zd_mac *mac = zd_usb_to_mac(usb); - - dev_dbg_f(zd_usb_dev(usb), "\n"); - - r = upload_firmware(usb); - if (r) { - dev_err(zd_usb_dev(usb), - "couldn't load firmware. Error number %d\n", r); - return r; - } - - r = usb_reset_configuration(zd_usb_to_usbdev(usb)); - if (r) { - dev_dbg_f(zd_usb_dev(usb), - "couldn't reset configuration. Error number %d\n", r); - return r; - } - - r = zd_mac_init_hw(mac->hw); - if (r) { - dev_dbg_f(zd_usb_dev(usb), - "couldn't initialize mac. Error number %d\n", r); - return r; - } - - usb->initialized = 1; - return 0; -} - -static int probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - int r; - struct usb_device *udev = interface_to_usbdev(intf); - struct zd_usb *usb; - struct ieee80211_hw *hw = NULL; - - print_id(udev); - - if (id->driver_info & DEVICE_INSTALLER) - return eject_installer(intf); - - switch (udev->speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - case USB_SPEED_HIGH: - break; - default: - dev_dbg_f(&intf->dev, "Unknown USB speed\n"); - r = -ENODEV; - goto error; - } - - r = usb_reset_device(udev); - if (r) { - dev_err(&intf->dev, - "couldn't reset usb device. Error number %d\n", r); - goto error; - } - - hw = zd_mac_alloc_hw(intf); - if (hw == NULL) { - r = -ENOMEM; - goto error; - } - - usb = &zd_hw_mac(hw)->chip.usb; - usb->is_zd1211b = (id->driver_info == DEVICE_ZD1211B) != 0; - - r = zd_mac_preinit_hw(hw); - if (r) { - dev_dbg_f(&intf->dev, - "couldn't initialize mac. Error number %d\n", r); - goto error; - } - - r = ieee80211_register_hw(hw); - if (r) { - dev_dbg_f(&intf->dev, - "couldn't register device. Error number %d\n", r); - goto error; - } - - dev_dbg_f(&intf->dev, "successful\n"); - dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy)); - return 0; -error: - usb_reset_device(interface_to_usbdev(intf)); - if (hw) { - zd_mac_clear(zd_hw_mac(hw)); - ieee80211_free_hw(hw); - } - return r; -} - -static void disconnect(struct usb_interface *intf) -{ - struct ieee80211_hw *hw = zd_intf_to_hw(intf); - struct zd_mac *mac; - struct zd_usb *usb; - - /* Either something really bad happened, or we're just dealing with - * a DEVICE_INSTALLER. */ - if (hw == NULL) - return; - - mac = zd_hw_mac(hw); - usb = &mac->chip.usb; - - dev_dbg_f(zd_usb_dev(usb), "\n"); - - ieee80211_unregister_hw(hw); - - /* Just in case something has gone wrong! */ - zd_usb_disable_tx(usb); - zd_usb_disable_rx(usb); - zd_usb_disable_int(usb); - - /* If the disconnect has been caused by a removal of the - * driver module, the reset allows reloading of the driver. If the - * reset will not be executed here, the upload of the firmware in the - * probe function caused by the reloading of the driver will fail. - */ - usb_reset_device(interface_to_usbdev(intf)); - - zd_mac_clear(mac); - ieee80211_free_hw(hw); - dev_dbg(&intf->dev, "disconnected\n"); -} - -static void zd_usb_resume(struct zd_usb *usb) -{ - struct zd_mac *mac = zd_usb_to_mac(usb); - int r; - - dev_dbg_f(zd_usb_dev(usb), "\n"); - - r = zd_op_start(zd_usb_to_hw(usb)); - if (r < 0) { - dev_warn(zd_usb_dev(usb), "Device resume failed " - "with error code %d. Retrying...\n", r); - if (usb->was_running) - set_bit(ZD_DEVICE_RUNNING, &mac->flags); - usb_queue_reset_device(usb->intf); - return; - } - - if (mac->type != NL80211_IFTYPE_UNSPECIFIED) { - r = zd_restore_settings(mac); - if (r < 0) { - dev_dbg(zd_usb_dev(usb), - "failed to restore settings, %d\n", r); - return; - } - } -} - -static void zd_usb_stop(struct zd_usb *usb) -{ - dev_dbg_f(zd_usb_dev(usb), "\n"); - - zd_op_stop(zd_usb_to_hw(usb)); - - zd_usb_disable_tx(usb); - zd_usb_disable_rx(usb); - zd_usb_disable_int(usb); - - usb->initialized = 0; -} - -static int pre_reset(struct usb_interface *intf) -{ - struct ieee80211_hw *hw = usb_get_intfdata(intf); - struct zd_mac *mac; - struct zd_usb *usb; - - if (!hw || intf->condition != USB_INTERFACE_BOUND) - return 0; - - mac = zd_hw_mac(hw); - usb = &mac->chip.usb; - - usb->was_running = test_bit(ZD_DEVICE_RUNNING, &mac->flags); - - zd_usb_stop(usb); - - mutex_lock(&mac->chip.mutex); - return 0; -} - -static int post_reset(struct usb_interface *intf) -{ - struct ieee80211_hw *hw = usb_get_intfdata(intf); - struct zd_mac *mac; - struct zd_usb *usb; - - if (!hw || intf->condition != USB_INTERFACE_BOUND) - return 0; - - mac = zd_hw_mac(hw); - usb = &mac->chip.usb; - - mutex_unlock(&mac->chip.mutex); - - if (usb->was_running) - zd_usb_resume(usb); - return 0; -} - -static struct usb_driver driver = { - .name = KBUILD_MODNAME, - .id_table = usb_ids, - .probe = probe, - .disconnect = disconnect, - .pre_reset = pre_reset, - .post_reset = post_reset, - .disable_hub_initiated_lpm = 1, -}; - -struct workqueue_struct *zd_workqueue; - -static int __init usb_init(void) -{ - int r; - - pr_debug("%s usb_init()\n", driver.name); - - zd_workqueue = create_singlethread_workqueue(driver.name); - if (zd_workqueue == NULL) { - printk(KERN_ERR "%s couldn't create workqueue\n", driver.name); - return -ENOMEM; - } - - r = usb_register(&driver); - if (r) { - destroy_workqueue(zd_workqueue); - printk(KERN_ERR "%s usb_register() failed. Error number %d\n", - driver.name, r); - return r; - } - - pr_debug("%s initialized\n", driver.name); - return 0; -} - -static void __exit usb_exit(void) -{ - pr_debug("%s usb_exit()\n", driver.name); - usb_deregister(&driver); - destroy_workqueue(zd_workqueue); -} - -module_init(usb_init); -module_exit(usb_exit); - -static int zd_ep_regs_out_msg(struct usb_device *udev, void *data, int len, - int *actual_length, int timeout) -{ - /* In USB 2.0 mode EP_REGS_OUT endpoint is interrupt type. However in - * USB 1.1 mode endpoint is bulk. Select correct type URB by endpoint - * descriptor. - */ - struct usb_host_endpoint *ep; - unsigned int pipe; - - pipe = usb_sndintpipe(udev, EP_REGS_OUT); - ep = usb_pipe_endpoint(udev, pipe); - if (!ep) - return -EINVAL; - - if (usb_endpoint_xfer_int(&ep->desc)) { - return usb_interrupt_msg(udev, pipe, data, len, - actual_length, timeout); - } else { - pipe = usb_sndbulkpipe(udev, EP_REGS_OUT); - return usb_bulk_msg(udev, pipe, data, len, actual_length, - timeout); - } -} - -static int usb_int_regs_length(unsigned int count) -{ - return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data); -} - -static void prepare_read_regs_int(struct zd_usb *usb, - struct usb_req_read_regs *req, - unsigned int count) -{ - struct zd_usb_interrupt *intr = &usb->intr; - - spin_lock_irq(&intr->lock); - atomic_set(&intr->read_regs_enabled, 1); - intr->read_regs.req = req; - intr->read_regs.req_count = count; - reinit_completion(&intr->read_regs.completion); - spin_unlock_irq(&intr->lock); -} - -static void disable_read_regs_int(struct zd_usb *usb) -{ - struct zd_usb_interrupt *intr = &usb->intr; - - spin_lock_irq(&intr->lock); - atomic_set(&intr->read_regs_enabled, 0); - spin_unlock_irq(&intr->lock); -} - -static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req, - unsigned int count) -{ - int i; - struct zd_usb_interrupt *intr = &usb->intr; - struct read_regs_int *rr = &intr->read_regs; - struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; - - /* The created block size seems to be larger than expected. - * However results appear to be correct. - */ - if (rr->length < usb_int_regs_length(count)) { - dev_dbg_f(zd_usb_dev(usb), - "error: actual length %d less than expected %d\n", - rr->length, usb_int_regs_length(count)); - return false; - } - - if (rr->length > sizeof(rr->buffer)) { - dev_dbg_f(zd_usb_dev(usb), - "error: actual length %d exceeds buffer size %zu\n", - rr->length, sizeof(rr->buffer)); - return false; - } - - for (i = 0; i < count; i++) { - struct reg_data *rd = ®s->regs[i]; - if (rd->addr != req->addr[i]) { - dev_dbg_f(zd_usb_dev(usb), - "rd[%d] addr %#06hx expected %#06hx\n", i, - le16_to_cpu(rd->addr), - le16_to_cpu(req->addr[i])); - return false; - } - } - - return true; -} - -static int get_results(struct zd_usb *usb, u16 *values, - struct usb_req_read_regs *req, unsigned int count, - bool *retry) -{ - int r; - int i; - struct zd_usb_interrupt *intr = &usb->intr; - struct read_regs_int *rr = &intr->read_regs; - struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; - - spin_lock_irq(&intr->lock); - - r = -EIO; - - /* Read failed because firmware bug? */ - *retry = !!intr->read_regs_int_overridden; - if (*retry) - goto error_unlock; - - if (!check_read_regs(usb, req, count)) { - dev_dbg_f(zd_usb_dev(usb), "error: invalid read regs\n"); - goto error_unlock; - } - - for (i = 0; i < count; i++) { - struct reg_data *rd = ®s->regs[i]; - values[i] = le16_to_cpu(rd->value); - } - - r = 0; -error_unlock: - spin_unlock_irq(&intr->lock); - return r; -} - -int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, - const zd_addr_t *addresses, unsigned int count) -{ - int r, i, req_len, actual_req_len, try_count = 0; - struct usb_device *udev; - struct usb_req_read_regs *req = NULL; - unsigned long timeout; - bool retry = false; - - if (count < 1) { - dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n"); - return -EINVAL; - } - if (count > USB_MAX_IOREAD16_COUNT) { - dev_dbg_f(zd_usb_dev(usb), - "error: count %u exceeds possible max %u\n", - count, USB_MAX_IOREAD16_COUNT); - return -EINVAL; - } - if (in_atomic()) { - dev_dbg_f(zd_usb_dev(usb), - "error: io in atomic context not supported\n"); - return -EWOULDBLOCK; - } - if (!usb_int_enabled(usb)) { - dev_dbg_f(zd_usb_dev(usb), - "error: usb interrupt not enabled\n"); - return -EWOULDBLOCK; - } - - ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); - BUILD_BUG_ON(sizeof(struct usb_req_read_regs) + USB_MAX_IOREAD16_COUNT * - sizeof(__le16) > sizeof(usb->req_buf)); - BUG_ON(sizeof(struct usb_req_read_regs) + count * sizeof(__le16) > - sizeof(usb->req_buf)); - - req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16); - req = (void *)usb->req_buf; - - req->id = cpu_to_le16(USB_REQ_READ_REGS); - for (i = 0; i < count; i++) - req->addr[i] = cpu_to_le16((u16)addresses[i]); - -retry_read: - try_count++; - udev = zd_usb_to_usbdev(usb); - prepare_read_regs_int(usb, req, count); - r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/); - if (r) { - dev_dbg_f(zd_usb_dev(usb), - "error in zd_ep_regs_out_msg(). Error number %d\n", r); - goto error; - } - if (req_len != actual_req_len) { - dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()\n" - " req_len %d != actual_req_len %d\n", - req_len, actual_req_len); - r = -EIO; - goto error; - } - - timeout = wait_for_completion_timeout(&usb->intr.read_regs.completion, - msecs_to_jiffies(50)); - if (!timeout) { - disable_read_regs_int(usb); - dev_dbg_f(zd_usb_dev(usb), "read timed out\n"); - r = -ETIMEDOUT; - goto error; - } - - r = get_results(usb, values, req, count, &retry); - if (retry && try_count < 20) { - dev_dbg_f(zd_usb_dev(usb), "read retry, tries so far: %d\n", - try_count); - goto retry_read; - } -error: - return r; -} - -static void iowrite16v_urb_complete(struct urb *urb) -{ - struct zd_usb *usb = urb->context; - - if (urb->status && !usb->cmd_error) - usb->cmd_error = urb->status; - - if (!usb->cmd_error && - urb->actual_length != urb->transfer_buffer_length) - usb->cmd_error = -EIO; -} - -static int zd_submit_waiting_urb(struct zd_usb *usb, bool last) -{ - int r = 0; - struct urb *urb = usb->urb_async_waiting; - - if (!urb) - return 0; - - usb->urb_async_waiting = NULL; - - if (!last) - urb->transfer_flags |= URB_NO_INTERRUPT; - - usb_anchor_urb(urb, &usb->submitted_cmds); - r = usb_submit_urb(urb, GFP_KERNEL); - if (r) { - usb_unanchor_urb(urb); - dev_dbg_f(zd_usb_dev(usb), - "error in usb_submit_urb(). Error number %d\n", r); - goto error; - } - - /* fall-through with r == 0 */ -error: - usb_free_urb(urb); - return r; -} - -void zd_usb_iowrite16v_async_start(struct zd_usb *usb) -{ - ZD_ASSERT(usb_anchor_empty(&usb->submitted_cmds)); - ZD_ASSERT(usb->urb_async_waiting == NULL); - ZD_ASSERT(!usb->in_async); - - ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); - - usb->in_async = 1; - usb->cmd_error = 0; - usb->urb_async_waiting = NULL; -} - -int zd_usb_iowrite16v_async_end(struct zd_usb *usb, unsigned int timeout) -{ - int r; - - ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); - ZD_ASSERT(usb->in_async); - - /* Submit last iowrite16v URB */ - r = zd_submit_waiting_urb(usb, true); - if (r) { - dev_dbg_f(zd_usb_dev(usb), - "error in zd_submit_waiting_usb(). " - "Error number %d\n", r); - - usb_kill_anchored_urbs(&usb->submitted_cmds); - goto error; - } - - if (timeout) - timeout = usb_wait_anchor_empty_timeout(&usb->submitted_cmds, - timeout); - if (!timeout) { - usb_kill_anchored_urbs(&usb->submitted_cmds); - if (usb->cmd_error == -ENOENT) { - dev_dbg_f(zd_usb_dev(usb), "timed out"); - r = -ETIMEDOUT; - goto error; - } - } - - r = usb->cmd_error; -error: - usb->in_async = 0; - return r; -} - -int zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, - unsigned int count) -{ - int r; - struct usb_device *udev; - struct usb_req_write_regs *req = NULL; - int i, req_len; - struct urb *urb; - struct usb_host_endpoint *ep; - - ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); - ZD_ASSERT(usb->in_async); - - if (count == 0) - return 0; - if (count > USB_MAX_IOWRITE16_COUNT) { - dev_dbg_f(zd_usb_dev(usb), - "error: count %u exceeds possible max %u\n", - count, USB_MAX_IOWRITE16_COUNT); - return -EINVAL; - } - if (in_atomic()) { - dev_dbg_f(zd_usb_dev(usb), - "error: io in atomic context not supported\n"); - return -EWOULDBLOCK; - } - - udev = zd_usb_to_usbdev(usb); - - ep = usb_pipe_endpoint(udev, usb_sndintpipe(udev, EP_REGS_OUT)); - if (!ep) - return -ENOENT; - - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) - return -ENOMEM; - - req_len = sizeof(struct usb_req_write_regs) + - count * sizeof(struct reg_data); - req = kmalloc(req_len, GFP_KERNEL); - if (!req) { - r = -ENOMEM; - goto error; - } - - req->id = cpu_to_le16(USB_REQ_WRITE_REGS); - for (i = 0; i < count; i++) { - struct reg_data *rw = &req->reg_writes[i]; - rw->addr = cpu_to_le16((u16)ioreqs[i].addr); - rw->value = cpu_to_le16(ioreqs[i].value); - } - - /* In USB 2.0 mode endpoint is interrupt type. However in USB 1.1 mode - * endpoint is bulk. Select correct type URB by endpoint descriptor. - */ - if (usb_endpoint_xfer_int(&ep->desc)) - usb_fill_int_urb(urb, udev, usb_sndintpipe(udev, EP_REGS_OUT), - req, req_len, iowrite16v_urb_complete, usb, - ep->desc.bInterval); - else - usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_REGS_OUT), - req, req_len, iowrite16v_urb_complete, usb); - - urb->transfer_flags |= URB_FREE_BUFFER; - - /* Submit previous URB */ - r = zd_submit_waiting_urb(usb, false); - if (r) { - dev_dbg_f(zd_usb_dev(usb), - "error in zd_submit_waiting_usb(). " - "Error number %d\n", r); - goto error; - } - - /* Delay submit so that URB_NO_INTERRUPT flag can be set for all URBs - * of currect batch except for very last. - */ - usb->urb_async_waiting = urb; - return 0; -error: - usb_free_urb(urb); - return r; -} - -int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, - unsigned int count) -{ - int r; - - zd_usb_iowrite16v_async_start(usb); - r = zd_usb_iowrite16v_async(usb, ioreqs, count); - if (r) { - zd_usb_iowrite16v_async_end(usb, 0); - return r; - } - return zd_usb_iowrite16v_async_end(usb, 50 /* ms */); -} - -int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits) -{ - int r; - struct usb_device *udev; - struct usb_req_rfwrite *req = NULL; - int i, req_len, actual_req_len; - u16 bit_value_template; - - if (in_atomic()) { - dev_dbg_f(zd_usb_dev(usb), - "error: io in atomic context not supported\n"); - return -EWOULDBLOCK; - } - if (bits < USB_MIN_RFWRITE_BIT_COUNT) { - dev_dbg_f(zd_usb_dev(usb), - "error: bits %d are smaller than" - " USB_MIN_RFWRITE_BIT_COUNT %d\n", - bits, USB_MIN_RFWRITE_BIT_COUNT); - return -EINVAL; - } - if (bits > USB_MAX_RFWRITE_BIT_COUNT) { - dev_dbg_f(zd_usb_dev(usb), - "error: bits %d exceed USB_MAX_RFWRITE_BIT_COUNT %d\n", - bits, USB_MAX_RFWRITE_BIT_COUNT); - return -EINVAL; - } -#ifdef DEBUG - if (value & (~0UL << bits)) { - dev_dbg_f(zd_usb_dev(usb), - "error: value %#09x has bits >= %d set\n", - value, bits); - return -EINVAL; - } -#endif /* DEBUG */ - - dev_dbg_f(zd_usb_dev(usb), "value %#09x bits %d\n", value, bits); - - r = zd_usb_ioread16(usb, &bit_value_template, ZD_CR203); - if (r) { - dev_dbg_f(zd_usb_dev(usb), - "error %d: Couldn't read ZD_CR203\n", r); - return r; - } - bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA); - - ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); - BUILD_BUG_ON(sizeof(struct usb_req_rfwrite) + - USB_MAX_RFWRITE_BIT_COUNT * sizeof(__le16) > - sizeof(usb->req_buf)); - BUG_ON(sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16) > - sizeof(usb->req_buf)); - - req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16); - req = (void *)usb->req_buf; - - req->id = cpu_to_le16(USB_REQ_WRITE_RF); - /* 1: 3683a, but not used in ZYDAS driver */ - req->value = cpu_to_le16(2); - req->bits = cpu_to_le16(bits); - - for (i = 0; i < bits; i++) { - u16 bv = bit_value_template; - if (value & (1 << (bits-1-i))) - bv |= RF_DATA; - req->bit_values[i] = cpu_to_le16(bv); - } - - udev = zd_usb_to_usbdev(usb); - r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/); - if (r) { - dev_dbg_f(zd_usb_dev(usb), - "error in zd_ep_regs_out_msg(). Error number %d\n", r); - goto out; - } - if (req_len != actual_req_len) { - dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()" - " req_len %d != actual_req_len %d\n", - req_len, actual_req_len); - r = -EIO; - goto out; - } - - /* FALL-THROUGH with r == 0 */ -out: - return r; -} diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h deleted file mode 100644 index a9075f225178..000000000000 --- a/drivers/net/wireless/zd1211rw/zd_usb.h +++ /dev/null @@ -1,292 +0,0 @@ -/* ZD1211 USB-WLAN driver for Linux - * - * Copyright (C) 2005-2007 Ulrich Kunitz - * Copyright (C) 2006-2007 Daniel Drake - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#ifndef _ZD_USB_H -#define _ZD_USB_H - -#include -#include -#include -#include -#include - -#include "zd_def.h" - -#define ZD_USB_TX_HIGH 5 -#define ZD_USB_TX_LOW 2 - -#define ZD_TX_TIMEOUT (HZ * 5) -#define ZD_TX_WATCHDOG_INTERVAL round_jiffies_relative(HZ) -#define ZD_RX_IDLE_INTERVAL round_jiffies_relative(30 * HZ) - -enum devicetype { - DEVICE_ZD1211 = 0, - DEVICE_ZD1211B = 1, - DEVICE_INSTALLER = 2, -}; - -enum endpoints { - EP_CTRL = 0, - EP_DATA_OUT = 1, - EP_DATA_IN = 2, - EP_INT_IN = 3, - EP_REGS_OUT = 4, -}; - -enum { - USB_MAX_TRANSFER_SIZE = 4096, /* bytes */ - /* FIXME: The original driver uses this value. We have to check, - * whether the MAX_TRANSFER_SIZE is sufficient and this needs only be - * used if one combined frame is split over two USB transactions. - */ - USB_MAX_RX_SIZE = 4800, /* bytes */ - USB_MAX_IOWRITE16_COUNT = 15, - USB_MAX_IOWRITE32_COUNT = USB_MAX_IOWRITE16_COUNT/2, - USB_MAX_IOREAD16_COUNT = 15, - USB_MAX_IOREAD32_COUNT = USB_MAX_IOREAD16_COUNT/2, - USB_MIN_RFWRITE_BIT_COUNT = 16, - USB_MAX_RFWRITE_BIT_COUNT = 28, - USB_MAX_EP_INT_BUFFER = 64, - USB_ZD1211B_BCD_DEVICE = 0x4810, -}; - -enum control_requests { - USB_REQ_WRITE_REGS = 0x21, - USB_REQ_READ_REGS = 0x22, - USB_REQ_WRITE_RF = 0x23, - USB_REQ_PROG_FLASH = 0x24, - USB_REQ_EEPROM_START = 0x0128, /* ? request is a byte */ - USB_REQ_EEPROM_MID = 0x28, - USB_REQ_EEPROM_END = 0x0228, /* ? request is a byte */ - USB_REQ_FIRMWARE_DOWNLOAD = 0x30, - USB_REQ_FIRMWARE_CONFIRM = 0x31, - USB_REQ_FIRMWARE_READ_DATA = 0x32, -}; - -struct usb_req_read_regs { - __le16 id; - __le16 addr[0]; -} __packed; - -struct reg_data { - __le16 addr; - __le16 value; -} __packed; - -struct usb_req_write_regs { - __le16 id; - struct reg_data reg_writes[0]; -} __packed; - -enum { - RF_IF_LE = 0x02, - RF_CLK = 0x04, - RF_DATA = 0x08, -}; - -struct usb_req_rfwrite { - __le16 id; - __le16 value; - /* 1: 3683a */ - /* 2: other (default) */ - __le16 bits; - /* RF2595: 24 */ - __le16 bit_values[0]; - /* (ZD_CR203 & ~(RF_IF_LE | RF_CLK | RF_DATA)) | (bit ? RF_DATA : 0) */ -} __packed; - -/* USB interrupt */ - -enum usb_int_id { - USB_INT_TYPE = 0x01, - USB_INT_ID_REGS = 0x90, - USB_INT_ID_RETRY_FAILED = 0xa0, -}; - -enum usb_int_flags { - USB_INT_READ_REGS_EN = 0x01, -}; - -struct usb_int_header { - u8 type; /* must always be 1 */ - u8 id; -} __packed; - -struct usb_int_regs { - struct usb_int_header hdr; - struct reg_data regs[0]; -} __packed; - -struct usb_int_retry_fail { - struct usb_int_header hdr; - u8 new_rate; - u8 _dummy; - u8 addr[ETH_ALEN]; - u8 ibss_wakeup_dest; -} __packed; - -struct read_regs_int { - struct completion completion; - struct usb_req_read_regs *req; - unsigned int req_count; - /* Stores the USB int structure and contains the USB address of the - * first requested register before request. - */ - u8 buffer[USB_MAX_EP_INT_BUFFER]; - int length; - __le16 cr_int_addr; -}; - -struct zd_ioreq16 { - zd_addr_t addr; - u16 value; -}; - -struct zd_ioreq32 { - zd_addr_t addr; - u32 value; -}; - -struct zd_usb_interrupt { - struct read_regs_int read_regs; - spinlock_t lock; - struct urb *urb; - void *buffer; - dma_addr_t buffer_dma; - int interval; - atomic_t read_regs_enabled; - u8 read_regs_int_overridden:1; -}; - -static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr) -{ - return (struct usb_int_regs *)intr->read_regs.buffer; -} - -#define RX_URBS_COUNT 5 - -struct zd_usb_rx { - spinlock_t lock; - struct mutex setup_mutex; - struct delayed_work idle_work; - struct tasklet_struct reset_timer_tasklet; - u8 fragment[2 * USB_MAX_RX_SIZE]; - unsigned int fragment_length; - unsigned int usb_packet_size; - struct urb **urbs; - int urbs_count; -}; - -/** - * struct zd_usb_tx - structure used for transmitting frames - * @enabled: atomic enabled flag, indicates whether tx is enabled - * @lock: lock for transmission - * @submitted: anchor for URBs sent to device - * @submitted_urbs: atomic integer that counts the URBs having sent to the - * device, which haven't been completed - * @stopped: indicates whether higher level tx queues are stopped - */ -struct zd_usb_tx { - atomic_t enabled; - spinlock_t lock; - struct delayed_work watchdog_work; - struct sk_buff_head submitted_skbs; - struct usb_anchor submitted; - int submitted_urbs; - u8 stopped:1, watchdog_enabled:1; -}; - -/* Contains the usb parts. The structure doesn't require a lock because intf - * will not be changed after initialization. - */ -struct zd_usb { - struct zd_usb_interrupt intr; - struct zd_usb_rx rx; - struct zd_usb_tx tx; - struct usb_interface *intf; - struct usb_anchor submitted_cmds; - struct urb *urb_async_waiting; - int cmd_error; - u8 req_buf[64]; /* zd_usb_iowrite16v needs 62 bytes */ - u8 is_zd1211b:1, initialized:1, was_running:1, in_async:1; -}; - -#define zd_usb_dev(usb) (&usb->intf->dev) - -static inline struct usb_device *zd_usb_to_usbdev(struct zd_usb *usb) -{ - return interface_to_usbdev(usb->intf); -} - -static inline struct ieee80211_hw *zd_intf_to_hw(struct usb_interface *intf) -{ - return usb_get_intfdata(intf); -} - -static inline struct ieee80211_hw *zd_usb_to_hw(struct zd_usb *usb) -{ - return zd_intf_to_hw(usb->intf); -} - -void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw, - struct usb_interface *intf); -int zd_usb_init_hw(struct zd_usb *usb); -void zd_usb_clear(struct zd_usb *usb); - -int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size); - -void zd_tx_watchdog_enable(struct zd_usb *usb); -void zd_tx_watchdog_disable(struct zd_usb *usb); - -int zd_usb_enable_int(struct zd_usb *usb); -void zd_usb_disable_int(struct zd_usb *usb); - -int zd_usb_enable_rx(struct zd_usb *usb); -void zd_usb_disable_rx(struct zd_usb *usb); - -void zd_usb_reset_rx_idle_timer(struct zd_usb *usb); - -void zd_usb_enable_tx(struct zd_usb *usb); -void zd_usb_disable_tx(struct zd_usb *usb); - -int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb); - -int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, - const zd_addr_t *addresses, unsigned int count); - -static inline int zd_usb_ioread16(struct zd_usb *usb, u16 *value, - const zd_addr_t addr) -{ - return zd_usb_ioread16v(usb, value, &addr, 1); -} - -void zd_usb_iowrite16v_async_start(struct zd_usb *usb); -int zd_usb_iowrite16v_async_end(struct zd_usb *usb, unsigned int timeout); -int zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, - unsigned int count); -int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, - unsigned int count); - -int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits); - -int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len); - -extern struct workqueue_struct *zd_workqueue; - -#endif /* _ZD_USB_H */ diff --git a/drivers/net/wireless/zydas/Kconfig b/drivers/net/wireless/zydas/Kconfig index 6140dfa04fed..a58c0f65e376 100644 --- a/drivers/net/wireless/zydas/Kconfig +++ b/drivers/net/wireless/zydas/Kconfig @@ -30,4 +30,6 @@ config USB_ZD1201 To compile this driver as a module, choose M here: the module will be called zd1201. +source "drivers/net/wireless/zydas/zd1211rw/Kconfig" + endif # WLAN_VENDOR_ZYDAS diff --git a/drivers/net/wireless/zydas/Makefile b/drivers/net/wireless/zydas/Makefile index 37179ef890ea..679fbbf3a6cd 100644 --- a/drivers/net/wireless/zydas/Makefile +++ b/drivers/net/wireless/zydas/Makefile @@ -1 +1,3 @@ +obj-$(CONFIG_ZD1211RW) += zd1211rw/ + obj-$(CONFIG_USB_ZD1201) += zd1201.o diff --git a/drivers/net/wireless/zydas/zd1211rw/Kconfig b/drivers/net/wireless/zydas/zd1211rw/Kconfig new file mode 100644 index 000000000000..95920581860a --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/Kconfig @@ -0,0 +1,19 @@ +config ZD1211RW + tristate "ZyDAS ZD1211/ZD1211B USB-wireless support" + depends on USB && MAC80211 + select FW_LOADER + ---help--- + This is a driver for the ZyDAS ZD1211/ZD1211B wireless + chip, present in many USB-wireless adapters. + + Device firmware is required alongside this driver. You can download + the firmware distribution from http://sf.net/projects/zd1211/files/ + +config ZD1211RW_DEBUG + bool "ZyDAS ZD1211 debugging" + depends on ZD1211RW + ---help--- + ZD1211 debugging messages. Choosing Y will result in additional debug + messages being saved to your kernel logs, which may help debug any + problems. + diff --git a/drivers/net/wireless/zydas/zd1211rw/Makefile b/drivers/net/wireless/zydas/zd1211rw/Makefile new file mode 100644 index 000000000000..5728a918e508 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/Makefile @@ -0,0 +1,9 @@ +obj-$(CONFIG_ZD1211RW) += zd1211rw.o + +zd1211rw-objs := zd_chip.o zd_mac.o \ + zd_rf_al2230.o zd_rf_rf2959.o \ + zd_rf_al7230b.o zd_rf_uw2453.o \ + zd_rf.o zd_usb.o + +ccflags-$(CONFIG_ZD1211RW_DEBUG) := -DDEBUG + diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_chip.c b/drivers/net/wireless/zydas/zd1211rw/zd_chip.c new file mode 100644 index 000000000000..07b94eda9604 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_chip.c @@ -0,0 +1,1560 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* This file implements all the hardware specific functions for the ZD1211 + * and ZD1211B chips. Support for the ZD1211B was possible after Timothy + * Legge sent me a ZD1211B device. Thank you Tim. -- Uli + */ + +#include +#include +#include + +#include "zd_def.h" +#include "zd_chip.h" +#include "zd_mac.h" +#include "zd_rf.h" + +void zd_chip_init(struct zd_chip *chip, + struct ieee80211_hw *hw, + struct usb_interface *intf) +{ + memset(chip, 0, sizeof(*chip)); + mutex_init(&chip->mutex); + zd_usb_init(&chip->usb, hw, intf); + zd_rf_init(&chip->rf); +} + +void zd_chip_clear(struct zd_chip *chip) +{ + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + zd_usb_clear(&chip->usb); + zd_rf_clear(&chip->rf); + mutex_destroy(&chip->mutex); + ZD_MEMCLEAR(chip, sizeof(*chip)); +} + +static int scnprint_mac_oui(struct zd_chip *chip, char *buffer, size_t size) +{ + u8 *addr = zd_mac_get_perm_addr(zd_chip_to_mac(chip)); + return scnprintf(buffer, size, "%02x-%02x-%02x", + addr[0], addr[1], addr[2]); +} + +/* Prints an identifier line, which will support debugging. */ +static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size) +{ + int i = 0; + + i = scnprintf(buffer, size, "zd1211%s chip ", + zd_chip_is_zd1211b(chip) ? "b" : ""); + i += zd_usb_scnprint_id(&chip->usb, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " "); + i += scnprint_mac_oui(chip, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " "); + i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c%c", chip->pa_type, + chip->patch_cck_gain ? 'g' : '-', + chip->patch_cr157 ? '7' : '-', + chip->patch_6m_band_edge ? '6' : '-', + chip->new_phy_layout ? 'N' : '-', + chip->al2230s_bit ? 'S' : '-'); + return i; +} + +static void print_id(struct zd_chip *chip) +{ + char buffer[80]; + + scnprint_id(chip, buffer, sizeof(buffer)); + buffer[sizeof(buffer)-1] = 0; + dev_info(zd_chip_dev(chip), "%s\n", buffer); +} + +static zd_addr_t inc_addr(zd_addr_t addr) +{ + u16 a = (u16)addr; + /* Control registers use byte addressing, but everything else uses word + * addressing. */ + if ((a & 0xf000) == CR_START) + a += 2; + else + a += 1; + return (zd_addr_t)a; +} + +/* Read a variable number of 32-bit values. Parameter count is not allowed to + * exceed USB_MAX_IOREAD32_COUNT. + */ +int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr, + unsigned int count) +{ + int r; + int i; + zd_addr_t a16[USB_MAX_IOREAD32_COUNT * 2]; + u16 v16[USB_MAX_IOREAD32_COUNT * 2]; + unsigned int count16; + + if (count > USB_MAX_IOREAD32_COUNT) + return -EINVAL; + + /* Use stack for values and addresses. */ + count16 = 2 * count; + BUG_ON(count16 * sizeof(zd_addr_t) > sizeof(a16)); + BUG_ON(count16 * sizeof(u16) > sizeof(v16)); + + for (i = 0; i < count; i++) { + int j = 2*i; + /* We read the high word always first. */ + a16[j] = inc_addr(addr[i]); + a16[j+1] = addr[i]; + } + + r = zd_ioread16v_locked(chip, v16, a16, count16); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error: %s. Error number %d\n", __func__, r); + return r; + } + + for (i = 0; i < count; i++) { + int j = 2*i; + values[i] = (v16[j] << 16) | v16[j+1]; + } + + return 0; +} + +static int _zd_iowrite32v_async_locked(struct zd_chip *chip, + const struct zd_ioreq32 *ioreqs, + unsigned int count) +{ + int i, j, r; + struct zd_ioreq16 ioreqs16[USB_MAX_IOWRITE32_COUNT * 2]; + unsigned int count16; + + /* Use stack for values and addresses. */ + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + + if (count == 0) + return 0; + if (count > USB_MAX_IOWRITE32_COUNT) + return -EINVAL; + + count16 = 2 * count; + BUG_ON(count16 * sizeof(struct zd_ioreq16) > sizeof(ioreqs16)); + + for (i = 0; i < count; i++) { + j = 2*i; + /* We write the high word always first. */ + ioreqs16[j].value = ioreqs[i].value >> 16; + ioreqs16[j].addr = inc_addr(ioreqs[i].addr); + ioreqs16[j+1].value = ioreqs[i].value; + ioreqs16[j+1].addr = ioreqs[i].addr; + } + + r = zd_usb_iowrite16v_async(&chip->usb, ioreqs16, count16); +#ifdef DEBUG + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error %d in zd_usb_write16v\n", r); + } +#endif /* DEBUG */ + return r; +} + +int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count) +{ + int r; + + zd_usb_iowrite16v_async_start(&chip->usb); + r = _zd_iowrite32v_async_locked(chip, ioreqs, count); + if (r) { + zd_usb_iowrite16v_async_end(&chip->usb, 0); + return r; + } + return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */); +} + +int zd_iowrite16a_locked(struct zd_chip *chip, + const struct zd_ioreq16 *ioreqs, unsigned int count) +{ + int r; + unsigned int i, j, t, max; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + zd_usb_iowrite16v_async_start(&chip->usb); + + for (i = 0; i < count; i += j + t) { + t = 0; + max = count-i; + if (max > USB_MAX_IOWRITE16_COUNT) + max = USB_MAX_IOWRITE16_COUNT; + for (j = 0; j < max; j++) { + if (!ioreqs[i+j].addr) { + t = 1; + break; + } + } + + r = zd_usb_iowrite16v_async(&chip->usb, &ioreqs[i], j); + if (r) { + zd_usb_iowrite16v_async_end(&chip->usb, 0); + dev_dbg_f(zd_chip_dev(chip), + "error zd_usb_iowrite16v. Error number %d\n", + r); + return r; + } + } + + return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */); +} + +/* Writes a variable number of 32 bit registers. The functions will split + * that in several USB requests. A split can be forced by inserting an IO + * request with an zero address field. + */ +int zd_iowrite32a_locked(struct zd_chip *chip, + const struct zd_ioreq32 *ioreqs, unsigned int count) +{ + int r; + unsigned int i, j, t, max; + + zd_usb_iowrite16v_async_start(&chip->usb); + + for (i = 0; i < count; i += j + t) { + t = 0; + max = count-i; + if (max > USB_MAX_IOWRITE32_COUNT) + max = USB_MAX_IOWRITE32_COUNT; + for (j = 0; j < max; j++) { + if (!ioreqs[i+j].addr) { + t = 1; + break; + } + } + + r = _zd_iowrite32v_async_locked(chip, &ioreqs[i], j); + if (r) { + zd_usb_iowrite16v_async_end(&chip->usb, 0); + dev_dbg_f(zd_chip_dev(chip), + "error _%s. Error number %d\n", __func__, + r); + return r; + } + } + + return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */); +} + +int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_ioread16_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_ioread32_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_iowrite16_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, + u32 *values, unsigned int count) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_ioread32v_locked(chip, values, addresses, count); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_iowrite32a_locked(chip, ioreqs, count); + mutex_unlock(&chip->mutex); + return r; +} + +static int read_pod(struct zd_chip *chip, u8 *rf_type) +{ + int r; + u32 value; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &value, E2P_POD); + if (r) + goto error; + dev_dbg_f(zd_chip_dev(chip), "E2P_POD %#010x\n", value); + + /* FIXME: AL2230 handling (Bit 7 in POD) */ + *rf_type = value & 0x0f; + chip->pa_type = (value >> 16) & 0x0f; + chip->patch_cck_gain = (value >> 8) & 0x1; + chip->patch_cr157 = (value >> 13) & 0x1; + chip->patch_6m_band_edge = (value >> 21) & 0x1; + chip->new_phy_layout = (value >> 31) & 0x1; + chip->al2230s_bit = (value >> 7) & 0x1; + chip->link_led = ((value >> 4) & 1) ? LED1 : LED2; + chip->supports_tx_led = 1; + if (value & (1 << 24)) { /* LED scenario */ + if (value & (1 << 29)) + chip->supports_tx_led = 0; + } + + dev_dbg_f(zd_chip_dev(chip), + "RF %s %#01x PA type %#01x patch CCK %d patch CR157 %d " + "patch 6M %d new PHY %d link LED%d tx led %d\n", + zd_rf_name(*rf_type), *rf_type, + chip->pa_type, chip->patch_cck_gain, + chip->patch_cr157, chip->patch_6m_band_edge, + chip->new_phy_layout, + chip->link_led == LED1 ? 1 : 2, + chip->supports_tx_led); + return 0; +error: + *rf_type = 0; + chip->pa_type = 0; + chip->patch_cck_gain = 0; + chip->patch_cr157 = 0; + chip->patch_6m_band_edge = 0; + chip->new_phy_layout = 0; + return r; +} + +static int zd_write_mac_addr_common(struct zd_chip *chip, const u8 *mac_addr, + const struct zd_ioreq32 *in_reqs, + const char *type) +{ + int r; + struct zd_ioreq32 reqs[2] = {in_reqs[0], in_reqs[1]}; + + if (mac_addr) { + reqs[0].value = (mac_addr[3] << 24) + | (mac_addr[2] << 16) + | (mac_addr[1] << 8) + | mac_addr[0]; + reqs[1].value = (mac_addr[5] << 8) + | mac_addr[4]; + dev_dbg_f(zd_chip_dev(chip), "%s addr %pM\n", type, mac_addr); + } else { + dev_dbg_f(zd_chip_dev(chip), "set NULL %s\n", type); + } + + mutex_lock(&chip->mutex); + r = zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); + mutex_unlock(&chip->mutex); + return r; +} + +/* MAC address: if custom mac addresses are to be used CR_MAC_ADDR_P1 and + * CR_MAC_ADDR_P2 must be overwritten + */ +int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr) +{ + static const struct zd_ioreq32 reqs[2] = { + [0] = { .addr = CR_MAC_ADDR_P1 }, + [1] = { .addr = CR_MAC_ADDR_P2 }, + }; + + return zd_write_mac_addr_common(chip, mac_addr, reqs, "mac"); +} + +int zd_write_bssid(struct zd_chip *chip, const u8 *bssid) +{ + static const struct zd_ioreq32 reqs[2] = { + [0] = { .addr = CR_BSSID_P1 }, + [1] = { .addr = CR_BSSID_P2 }, + }; + + return zd_write_mac_addr_common(chip, bssid, reqs, "bssid"); +} + +int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain) +{ + int r; + u32 value; + + mutex_lock(&chip->mutex); + r = zd_ioread32_locked(chip, &value, E2P_SUBID); + mutex_unlock(&chip->mutex); + if (r) + return r; + + *regdomain = value >> 16; + dev_dbg_f(zd_chip_dev(chip), "regdomain: %#04x\n", *regdomain); + + return 0; +} + +static int read_values(struct zd_chip *chip, u8 *values, size_t count, + zd_addr_t e2p_addr, u32 guard) +{ + int r; + int i; + u32 v; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + for (i = 0;;) { + r = zd_ioread32_locked(chip, &v, + (zd_addr_t)((u16)e2p_addr+i/2)); + if (r) + return r; + v -= guard; + if (i+4 < count) { + values[i++] = v; + values[i++] = v >> 8; + values[i++] = v >> 16; + values[i++] = v >> 24; + continue; + } + for (;i < count; i++) + values[i] = v >> (8*(i%3)); + return 0; + } +} + +static int read_pwr_cal_values(struct zd_chip *chip) +{ + return read_values(chip, chip->pwr_cal_values, + E2P_CHANNEL_COUNT, E2P_PWR_CAL_VALUE1, + 0); +} + +static int read_pwr_int_values(struct zd_chip *chip) +{ + return read_values(chip, chip->pwr_int_values, + E2P_CHANNEL_COUNT, E2P_PWR_INT_VALUE1, + E2P_PWR_INT_GUARD); +} + +static int read_ofdm_cal_values(struct zd_chip *chip) +{ + int r; + int i; + static const zd_addr_t addresses[] = { + E2P_36M_CAL_VALUE1, + E2P_48M_CAL_VALUE1, + E2P_54M_CAL_VALUE1, + }; + + for (i = 0; i < 3; i++) { + r = read_values(chip, chip->ofdm_cal_values[i], + E2P_CHANNEL_COUNT, addresses[i], 0); + if (r) + return r; + } + return 0; +} + +static int read_cal_int_tables(struct zd_chip *chip) +{ + int r; + + r = read_pwr_cal_values(chip); + if (r) + return r; + r = read_pwr_int_values(chip); + if (r) + return r; + r = read_ofdm_cal_values(chip); + if (r) + return r; + return 0; +} + +/* phy means physical registers */ +int zd_chip_lock_phy_regs(struct zd_chip *chip) +{ + int r; + u32 tmp; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &tmp, CR_REG1); + if (r) { + dev_err(zd_chip_dev(chip), "error ioread32(CR_REG1): %d\n", r); + return r; + } + + tmp &= ~UNLOCK_PHY_REGS; + + r = zd_iowrite32_locked(chip, tmp, CR_REG1); + if (r) + dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); + return r; +} + +int zd_chip_unlock_phy_regs(struct zd_chip *chip) +{ + int r; + u32 tmp; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &tmp, CR_REG1); + if (r) { + dev_err(zd_chip_dev(chip), + "error ioread32(CR_REG1): %d\n", r); + return r; + } + + tmp |= UNLOCK_PHY_REGS; + + r = zd_iowrite32_locked(chip, tmp, CR_REG1); + if (r) + dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); + return r; +} + +/* ZD_CR157 can be optionally patched by the EEPROM for original ZD1211 */ +static int patch_cr157(struct zd_chip *chip) +{ + int r; + u16 value; + + if (!chip->patch_cr157) + return 0; + + r = zd_ioread16_locked(chip, &value, E2P_PHY_REG); + if (r) + return r; + + dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value >> 8); + return zd_iowrite32_locked(chip, value >> 8, ZD_CR157); +} + +/* + * 6M band edge can be optionally overwritten for certain RF's + * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge + * bit (for AL2230, AL2230S) + */ +static int patch_6m_band_edge(struct zd_chip *chip, u8 channel) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + if (!chip->patch_6m_band_edge) + return 0; + + return zd_rf_patch_6m_band_edge(&chip->rf, channel); +} + +/* Generic implementation of 6M band edge patching, used by most RFs via + * zd_rf_generic_patch_6m() */ +int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel) +{ + struct zd_ioreq16 ioreqs[] = { + { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, + { ZD_CR47, 0x1e }, + }; + + /* FIXME: Channel 11 is not the edge for all regulatory domains. */ + if (channel == 1 || channel == 11) + ioreqs[0].value = 0x12; + + dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211_hw_reset_phy(struct zd_chip *chip) +{ + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR0, 0x0a }, { ZD_CR1, 0x06 }, { ZD_CR2, 0x26 }, + { ZD_CR3, 0x38 }, { ZD_CR4, 0x80 }, { ZD_CR9, 0xa0 }, + { ZD_CR10, 0x81 }, { ZD_CR11, 0x00 }, { ZD_CR12, 0x7f }, + { ZD_CR13, 0x8c }, { ZD_CR14, 0x80 }, { ZD_CR15, 0x3d }, + { ZD_CR16, 0x20 }, { ZD_CR17, 0x1e }, { ZD_CR18, 0x0a }, + { ZD_CR19, 0x48 }, { ZD_CR20, 0x0c }, { ZD_CR21, 0x0c }, + { ZD_CR22, 0x23 }, { ZD_CR23, 0x90 }, { ZD_CR24, 0x14 }, + { ZD_CR25, 0x40 }, { ZD_CR26, 0x10 }, { ZD_CR27, 0x19 }, + { ZD_CR28, 0x7f }, { ZD_CR29, 0x80 }, { ZD_CR30, 0x4b }, + { ZD_CR31, 0x60 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x08 }, + { ZD_CR34, 0x06 }, { ZD_CR35, 0x0a }, { ZD_CR36, 0x00 }, + { ZD_CR37, 0x00 }, { ZD_CR38, 0x38 }, { ZD_CR39, 0x0c }, + { ZD_CR40, 0x84 }, { ZD_CR41, 0x2a }, { ZD_CR42, 0x80 }, + { ZD_CR43, 0x10 }, { ZD_CR44, 0x12 }, { ZD_CR46, 0xff }, + { ZD_CR47, 0x1E }, { ZD_CR48, 0x26 }, { ZD_CR49, 0x5b }, + { ZD_CR64, 0xd0 }, { ZD_CR65, 0x04 }, { ZD_CR66, 0x58 }, + { ZD_CR67, 0xc9 }, { ZD_CR68, 0x88 }, { ZD_CR69, 0x41 }, + { ZD_CR70, 0x23 }, { ZD_CR71, 0x10 }, { ZD_CR72, 0xff }, + { ZD_CR73, 0x32 }, { ZD_CR74, 0x30 }, { ZD_CR75, 0x65 }, + { ZD_CR76, 0x41 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x30 }, + { ZD_CR79, 0x68 }, { ZD_CR80, 0x64 }, { ZD_CR81, 0x64 }, + { ZD_CR82, 0x00 }, { ZD_CR83, 0x00 }, { ZD_CR84, 0x00 }, + { ZD_CR85, 0x02 }, { ZD_CR86, 0x00 }, { ZD_CR87, 0x00 }, + { ZD_CR88, 0xff }, { ZD_CR89, 0xfc }, { ZD_CR90, 0x00 }, + { ZD_CR91, 0x00 }, { ZD_CR92, 0x00 }, { ZD_CR93, 0x08 }, + { ZD_CR94, 0x00 }, { ZD_CR95, 0x00 }, { ZD_CR96, 0xff }, + { ZD_CR97, 0xe7 }, { ZD_CR98, 0x00 }, { ZD_CR99, 0x00 }, + { ZD_CR100, 0x00 }, { ZD_CR101, 0xae }, { ZD_CR102, 0x02 }, + { ZD_CR103, 0x00 }, { ZD_CR104, 0x03 }, { ZD_CR105, 0x65 }, + { ZD_CR106, 0x04 }, { ZD_CR107, 0x00 }, { ZD_CR108, 0x0a }, + { ZD_CR109, 0xaa }, { ZD_CR110, 0xaa }, { ZD_CR111, 0x25 }, + { ZD_CR112, 0x25 }, { ZD_CR113, 0x00 }, { ZD_CR119, 0x1e }, + { ZD_CR125, 0x90 }, { ZD_CR126, 0x00 }, { ZD_CR127, 0x00 }, + { }, + { ZD_CR5, 0x00 }, { ZD_CR6, 0x00 }, { ZD_CR7, 0x00 }, + { ZD_CR8, 0x00 }, { ZD_CR9, 0x20 }, { ZD_CR12, 0xf0 }, + { ZD_CR20, 0x0e }, { ZD_CR21, 0x0e }, { ZD_CR27, 0x10 }, + { ZD_CR44, 0x33 }, { ZD_CR47, 0x1E }, { ZD_CR83, 0x24 }, + { ZD_CR84, 0x04 }, { ZD_CR85, 0x00 }, { ZD_CR86, 0x0C }, + { ZD_CR87, 0x12 }, { ZD_CR88, 0x0C }, { ZD_CR89, 0x00 }, + { ZD_CR90, 0x10 }, { ZD_CR91, 0x08 }, { ZD_CR93, 0x00 }, + { ZD_CR94, 0x01 }, { ZD_CR95, 0x00 }, { ZD_CR96, 0x50 }, + { ZD_CR97, 0x37 }, { ZD_CR98, 0x35 }, { ZD_CR101, 0x13 }, + { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 }, { ZD_CR104, 0x18 }, + { ZD_CR105, 0x12 }, { ZD_CR109, 0x27 }, { ZD_CR110, 0x27 }, + { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 }, { ZD_CR113, 0x27 }, + { ZD_CR114, 0x27 }, { ZD_CR115, 0x26 }, { ZD_CR116, 0x24 }, + { ZD_CR117, 0xfc }, { ZD_CR118, 0xfa }, { ZD_CR120, 0x4f }, + { ZD_CR125, 0xaa }, { ZD_CR127, 0x03 }, { ZD_CR128, 0x14 }, + { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, { ZD_CR131, 0x0C }, + { ZD_CR136, 0xdf }, { ZD_CR137, 0x40 }, { ZD_CR138, 0xa0 }, + { ZD_CR139, 0xb0 }, { ZD_CR140, 0x99 }, { ZD_CR141, 0x82 }, + { ZD_CR142, 0x54 }, { ZD_CR143, 0x1c }, { ZD_CR144, 0x6c }, + { ZD_CR147, 0x07 }, { ZD_CR148, 0x4c }, { ZD_CR149, 0x50 }, + { ZD_CR150, 0x0e }, { ZD_CR151, 0x18 }, { ZD_CR160, 0xfe }, + { ZD_CR161, 0xee }, { ZD_CR162, 0xaa }, { ZD_CR163, 0xfa }, + { ZD_CR164, 0xfa }, { ZD_CR165, 0xea }, { ZD_CR166, 0xbe }, + { ZD_CR167, 0xbe }, { ZD_CR168, 0x6a }, { ZD_CR169, 0xba }, + { ZD_CR170, 0xba }, { ZD_CR171, 0xba }, + /* Note: ZD_CR204 must lead the ZD_CR203 */ + { ZD_CR204, 0x7d }, + { }, + { ZD_CR203, 0x30 }, + }; + + int r, t; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + goto unlock; + + r = patch_cr157(chip); +unlock: + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + return r; +} + +static int zd1211b_hw_reset_phy(struct zd_chip *chip) +{ + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR0, 0x14 }, { ZD_CR1, 0x06 }, { ZD_CR2, 0x26 }, + { ZD_CR3, 0x38 }, { ZD_CR4, 0x80 }, { ZD_CR9, 0xe0 }, + { ZD_CR10, 0x81 }, + /* power control { { ZD_CR11, 1 << 6 }, */ + { ZD_CR11, 0x00 }, + { ZD_CR12, 0xf0 }, { ZD_CR13, 0x8c }, { ZD_CR14, 0x80 }, + { ZD_CR15, 0x3d }, { ZD_CR16, 0x20 }, { ZD_CR17, 0x1e }, + { ZD_CR18, 0x0a }, { ZD_CR19, 0x48 }, + { ZD_CR20, 0x10 }, /* Org:0x0E, ComTrend:RalLink AP */ + { ZD_CR21, 0x0e }, { ZD_CR22, 0x23 }, { ZD_CR23, 0x90 }, + { ZD_CR24, 0x14 }, { ZD_CR25, 0x40 }, { ZD_CR26, 0x10 }, + { ZD_CR27, 0x10 }, { ZD_CR28, 0x7f }, { ZD_CR29, 0x80 }, + { ZD_CR30, 0x4b }, /* ASIC/FWT, no jointly decoder */ + { ZD_CR31, 0x60 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x08 }, + { ZD_CR34, 0x06 }, { ZD_CR35, 0x0a }, { ZD_CR36, 0x00 }, + { ZD_CR37, 0x00 }, { ZD_CR38, 0x38 }, { ZD_CR39, 0x0c }, + { ZD_CR40, 0x84 }, { ZD_CR41, 0x2a }, { ZD_CR42, 0x80 }, + { ZD_CR43, 0x10 }, { ZD_CR44, 0x33 }, { ZD_CR46, 0xff }, + { ZD_CR47, 0x1E }, { ZD_CR48, 0x26 }, { ZD_CR49, 0x5b }, + { ZD_CR64, 0xd0 }, { ZD_CR65, 0x04 }, { ZD_CR66, 0x58 }, + { ZD_CR67, 0xc9 }, { ZD_CR68, 0x88 }, { ZD_CR69, 0x41 }, + { ZD_CR70, 0x23 }, { ZD_CR71, 0x10 }, { ZD_CR72, 0xff }, + { ZD_CR73, 0x32 }, { ZD_CR74, 0x30 }, { ZD_CR75, 0x65 }, + { ZD_CR76, 0x41 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x30 }, + { ZD_CR79, 0xf0 }, { ZD_CR80, 0x64 }, { ZD_CR81, 0x64 }, + { ZD_CR82, 0x00 }, { ZD_CR83, 0x24 }, { ZD_CR84, 0x04 }, + { ZD_CR85, 0x00 }, { ZD_CR86, 0x0c }, { ZD_CR87, 0x12 }, + { ZD_CR88, 0x0c }, { ZD_CR89, 0x00 }, { ZD_CR90, 0x58 }, + { ZD_CR91, 0x04 }, { ZD_CR92, 0x00 }, { ZD_CR93, 0x00 }, + { ZD_CR94, 0x01 }, + { ZD_CR95, 0x20 }, /* ZD1211B */ + { ZD_CR96, 0x50 }, { ZD_CR97, 0x37 }, { ZD_CR98, 0x35 }, + { ZD_CR99, 0x00 }, { ZD_CR100, 0x01 }, { ZD_CR101, 0x13 }, + { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 }, { ZD_CR104, 0x18 }, + { ZD_CR105, 0x12 }, { ZD_CR106, 0x04 }, { ZD_CR107, 0x00 }, + { ZD_CR108, 0x0a }, { ZD_CR109, 0x27 }, { ZD_CR110, 0x27 }, + { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 }, { ZD_CR113, 0x27 }, + { ZD_CR114, 0x27 }, { ZD_CR115, 0x26 }, { ZD_CR116, 0x24 }, + { ZD_CR117, 0xfc }, { ZD_CR118, 0xfa }, { ZD_CR119, 0x1e }, + { ZD_CR125, 0x90 }, { ZD_CR126, 0x00 }, { ZD_CR127, 0x00 }, + { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, + { ZD_CR131, 0x0c }, { ZD_CR136, 0xdf }, { ZD_CR137, 0xa0 }, + { ZD_CR138, 0xa8 }, { ZD_CR139, 0xb4 }, { ZD_CR140, 0x98 }, + { ZD_CR141, 0x82 }, { ZD_CR142, 0x53 }, { ZD_CR143, 0x1c }, + { ZD_CR144, 0x6c }, { ZD_CR147, 0x07 }, { ZD_CR148, 0x40 }, + { ZD_CR149, 0x40 }, /* Org:0x50 ComTrend:RalLink AP */ + { ZD_CR150, 0x14 }, /* Org:0x0E ComTrend:RalLink AP */ + { ZD_CR151, 0x18 }, { ZD_CR159, 0x70 }, { ZD_CR160, 0xfe }, + { ZD_CR161, 0xee }, { ZD_CR162, 0xaa }, { ZD_CR163, 0xfa }, + { ZD_CR164, 0xfa }, { ZD_CR165, 0xea }, { ZD_CR166, 0xbe }, + { ZD_CR167, 0xbe }, { ZD_CR168, 0x6a }, { ZD_CR169, 0xba }, + { ZD_CR170, 0xba }, { ZD_CR171, 0xba }, + /* Note: ZD_CR204 must lead the ZD_CR203 */ + { ZD_CR204, 0x7d }, + {}, + { ZD_CR203, 0x30 }, + }; + + int r, t; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + return r; +} + +static int hw_reset_phy(struct zd_chip *chip) +{ + return zd_chip_is_zd1211b(chip) ? zd1211b_hw_reset_phy(chip) : + zd1211_hw_reset_phy(chip); +} + +static int zd1211_hw_init_hmac(struct zd_chip *chip) +{ + static const struct zd_ioreq32 ioreqs[] = { + { CR_ZD1211_RETRY_MAX, ZD1211_RETRY_COUNT }, + { CR_RX_THRESHOLD, 0x000c0640 }, + }; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_hw_init_hmac(struct zd_chip *chip) +{ + static const struct zd_ioreq32 ioreqs[] = { + { CR_ZD1211B_RETRY_MAX, ZD1211B_RETRY_COUNT }, + { CR_ZD1211B_CWIN_MAX_MIN_AC0, 0x007f003f }, + { CR_ZD1211B_CWIN_MAX_MIN_AC1, 0x007f003f }, + { CR_ZD1211B_CWIN_MAX_MIN_AC2, 0x003f001f }, + { CR_ZD1211B_CWIN_MAX_MIN_AC3, 0x001f000f }, + { CR_ZD1211B_AIFS_CTL1, 0x00280028 }, + { CR_ZD1211B_AIFS_CTL2, 0x008C003C }, + { CR_ZD1211B_TXOP, 0x01800824 }, + { CR_RX_THRESHOLD, 0x000c0eff, }, + }; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int hw_init_hmac(struct zd_chip *chip) +{ + int r; + static const struct zd_ioreq32 ioreqs[] = { + { CR_ACK_TIMEOUT_EXT, 0x20 }, + { CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, + { CR_SNIFFER_ON, 0 }, + { CR_RX_FILTER, STA_RX_FILTER }, + { CR_GROUP_HASH_P1, 0x00 }, + { CR_GROUP_HASH_P2, 0x80000000 }, + { CR_REG1, 0xa4 }, + { CR_ADDA_PWR_DWN, 0x7f }, + { CR_BCN_PLCP_CFG, 0x00f00401 }, + { CR_PHY_DELAY, 0x00 }, + { CR_ACK_TIMEOUT_EXT, 0x80 }, + { CR_ADDA_PWR_DWN, 0x00 }, + { CR_ACK_TIME_80211, 0x100 }, + { CR_RX_PE_DELAY, 0x70 }, + { CR_PS_CTRL, 0x10000000 }, + { CR_RTS_CTS_RATE, 0x02030203 }, + { CR_AFTER_PNP, 0x1 }, + { CR_WEP_PROTECT, 0x114 }, + { CR_IFS_VALUE, IFS_VALUE_DEFAULT }, + { CR_CAM_MODE, MODE_AP_WDS}, + }; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + return zd_chip_is_zd1211b(chip) ? + zd1211b_hw_init_hmac(chip) : zd1211_hw_init_hmac(chip); +} + +struct aw_pt_bi { + u32 atim_wnd_period; + u32 pre_tbtt; + u32 beacon_interval; +}; + +static int get_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) +{ + int r; + static const zd_addr_t aw_pt_bi_addr[] = + { CR_ATIM_WND_PERIOD, CR_PRE_TBTT, CR_BCN_INTERVAL }; + u32 values[3]; + + r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr, + ARRAY_SIZE(aw_pt_bi_addr)); + if (r) { + memset(s, 0, sizeof(*s)); + return r; + } + + s->atim_wnd_period = values[0]; + s->pre_tbtt = values[1]; + s->beacon_interval = values[2]; + return 0; +} + +static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) +{ + struct zd_ioreq32 reqs[3]; + u16 b_interval = s->beacon_interval & 0xffff; + + if (b_interval <= 5) + b_interval = 5; + if (s->pre_tbtt < 4 || s->pre_tbtt >= b_interval) + s->pre_tbtt = b_interval - 1; + if (s->atim_wnd_period >= s->pre_tbtt) + s->atim_wnd_period = s->pre_tbtt - 1; + + reqs[0].addr = CR_ATIM_WND_PERIOD; + reqs[0].value = s->atim_wnd_period; + reqs[1].addr = CR_PRE_TBTT; + reqs[1].value = s->pre_tbtt; + reqs[2].addr = CR_BCN_INTERVAL; + reqs[2].value = (s->beacon_interval & ~0xffff) | b_interval; + + return zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); +} + + +static int set_beacon_interval(struct zd_chip *chip, u16 interval, + u8 dtim_period, int type) +{ + int r; + struct aw_pt_bi s; + u32 b_interval, mode_flag; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + + if (interval > 0) { + switch (type) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + mode_flag = BCN_MODE_IBSS; + break; + case NL80211_IFTYPE_AP: + mode_flag = BCN_MODE_AP; + break; + default: + mode_flag = 0; + break; + } + } else { + dtim_period = 0; + mode_flag = 0; + } + + b_interval = mode_flag | (dtim_period << 16) | interval; + + r = zd_iowrite32_locked(chip, b_interval, CR_BCN_INTERVAL); + if (r) + return r; + r = get_aw_pt_bi(chip, &s); + if (r) + return r; + return set_aw_pt_bi(chip, &s); +} + +int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period, + int type) +{ + int r; + + mutex_lock(&chip->mutex); + r = set_beacon_interval(chip, interval, dtim_period, type); + mutex_unlock(&chip->mutex); + return r; +} + +static int hw_init(struct zd_chip *chip) +{ + int r; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = hw_reset_phy(chip); + if (r) + return r; + + r = hw_init_hmac(chip); + if (r) + return r; + + return set_beacon_interval(chip, 100, 0, NL80211_IFTYPE_UNSPECIFIED); +} + +static zd_addr_t fw_reg_addr(struct zd_chip *chip, u16 offset) +{ + return (zd_addr_t)((u16)chip->fw_regs_base + offset); +} + +#ifdef DEBUG +static int dump_cr(struct zd_chip *chip, const zd_addr_t addr, + const char *addr_string) +{ + int r; + u32 value; + + r = zd_ioread32_locked(chip, &value, addr); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error reading %s. Error number %d\n", addr_string, r); + return r; + } + + dev_dbg_f(zd_chip_dev(chip), "%s %#010x\n", + addr_string, (unsigned int)value); + return 0; +} + +static int test_init(struct zd_chip *chip) +{ + int r; + + r = dump_cr(chip, CR_AFTER_PNP, "CR_AFTER_PNP"); + if (r) + return r; + r = dump_cr(chip, CR_GPI_EN, "CR_GPI_EN"); + if (r) + return r; + return dump_cr(chip, CR_INTERRUPT, "CR_INTERRUPT"); +} + +static void dump_fw_registers(struct zd_chip *chip) +{ + const zd_addr_t addr[4] = { + fw_reg_addr(chip, FW_REG_FIRMWARE_VER), + fw_reg_addr(chip, FW_REG_USB_SPEED), + fw_reg_addr(chip, FW_REG_FIX_TX_RATE), + fw_reg_addr(chip, FW_REG_LED_LINK_STATUS), + }; + + int r; + u16 values[4]; + + r = zd_ioread16v_locked(chip, values, (const zd_addr_t*)addr, + ARRAY_SIZE(addr)); + if (r) { + dev_dbg_f(zd_chip_dev(chip), "error %d zd_ioread16v_locked\n", + r); + return; + } + + dev_dbg_f(zd_chip_dev(chip), "FW_FIRMWARE_VER %#06hx\n", values[0]); + dev_dbg_f(zd_chip_dev(chip), "FW_USB_SPEED %#06hx\n", values[1]); + dev_dbg_f(zd_chip_dev(chip), "FW_FIX_TX_RATE %#06hx\n", values[2]); + dev_dbg_f(zd_chip_dev(chip), "FW_LINK_STATUS %#06hx\n", values[3]); +} +#endif /* DEBUG */ + +static int print_fw_version(struct zd_chip *chip) +{ + struct wiphy *wiphy = zd_chip_to_mac(chip)->hw->wiphy; + int r; + u16 version; + + r = zd_ioread16_locked(chip, &version, + fw_reg_addr(chip, FW_REG_FIRMWARE_VER)); + if (r) + return r; + + dev_info(zd_chip_dev(chip),"firmware version %04hx\n", version); + + snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), + "%04hx", version); + + return 0; +} + +static int set_mandatory_rates(struct zd_chip *chip, int gmode) +{ + u32 rates; + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + /* This sets the mandatory rates, which only depend from the standard + * that the device is supporting. Until further notice we should try + * to support 802.11g also for full speed USB. + */ + if (!gmode) + rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M; + else + rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M| + CR_RATE_6M|CR_RATE_12M|CR_RATE_24M; + + return zd_iowrite32_locked(chip, rates, CR_MANDATORY_RATE_TBL); +} + +int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, + int preamble) +{ + u32 value = 0; + + dev_dbg_f(zd_chip_dev(chip), "preamble=%x\n", preamble); + value |= preamble << RTSCTS_SH_RTS_PMB_TYPE; + value |= preamble << RTSCTS_SH_CTS_PMB_TYPE; + + /* We always send 11M RTS/self-CTS messages, like the vendor driver. */ + value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_RTS_RATE; + value |= ZD_RX_CCK << RTSCTS_SH_RTS_MOD_TYPE; + value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_CTS_RATE; + value |= ZD_RX_CCK << RTSCTS_SH_CTS_MOD_TYPE; + + return zd_iowrite32_locked(chip, value, CR_RTS_CTS_RATE); +} + +int zd_chip_enable_hwint(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, HWINT_ENABLED, CR_INTERRUPT); + mutex_unlock(&chip->mutex); + return r; +} + +static int disable_hwint(struct zd_chip *chip) +{ + return zd_iowrite32_locked(chip, HWINT_DISABLED, CR_INTERRUPT); +} + +int zd_chip_disable_hwint(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = disable_hwint(chip); + mutex_unlock(&chip->mutex); + return r; +} + +static int read_fw_regs_offset(struct zd_chip *chip) +{ + int r; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread16_locked(chip, (u16*)&chip->fw_regs_base, + FWRAW_REGS_ADDR); + if (r) + return r; + dev_dbg_f(zd_chip_dev(chip), "fw_regs_base: %#06hx\n", + (u16)chip->fw_regs_base); + + return 0; +} + +/* Read mac address using pre-firmware interface */ +int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr) +{ + dev_dbg_f(zd_chip_dev(chip), "\n"); + return zd_usb_read_fw(&chip->usb, E2P_MAC_ADDR_P1, addr, + ETH_ALEN); +} + +int zd_chip_init_hw(struct zd_chip *chip) +{ + int r; + u8 rf_type; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + mutex_lock(&chip->mutex); + +#ifdef DEBUG + r = test_init(chip); + if (r) + goto out; +#endif + r = zd_iowrite32_locked(chip, 1, CR_AFTER_PNP); + if (r) + goto out; + + r = read_fw_regs_offset(chip); + if (r) + goto out; + + /* GPI is always disabled, also in the other driver. + */ + r = zd_iowrite32_locked(chip, 0, CR_GPI_EN); + if (r) + goto out; + r = zd_iowrite32_locked(chip, CWIN_SIZE, CR_CWMIN_CWMAX); + if (r) + goto out; + /* Currently we support IEEE 802.11g for full and high speed USB. + * It might be discussed, whether we should support pure b mode for + * full speed USB. + */ + r = set_mandatory_rates(chip, 1); + if (r) + goto out; + /* Disabling interrupts is certainly a smart thing here. + */ + r = disable_hwint(chip); + if (r) + goto out; + r = read_pod(chip, &rf_type); + if (r) + goto out; + r = hw_init(chip); + if (r) + goto out; + r = zd_rf_init_hw(&chip->rf, rf_type); + if (r) + goto out; + + r = print_fw_version(chip); + if (r) + goto out; + +#ifdef DEBUG + dump_fw_registers(chip); + r = test_init(chip); + if (r) + goto out; +#endif /* DEBUG */ + + r = read_cal_int_tables(chip); + if (r) + goto out; + + print_id(chip); +out: + mutex_unlock(&chip->mutex); + return r; +} + +static int update_pwr_int(struct zd_chip *chip, u8 channel) +{ + u8 value = chip->pwr_int_values[channel - 1]; + return zd_iowrite16_locked(chip, value, ZD_CR31); +} + +static int update_pwr_cal(struct zd_chip *chip, u8 channel) +{ + u8 value = chip->pwr_cal_values[channel-1]; + return zd_iowrite16_locked(chip, value, ZD_CR68); +} + +static int update_ofdm_cal(struct zd_chip *chip, u8 channel) +{ + struct zd_ioreq16 ioreqs[3]; + + ioreqs[0].addr = ZD_CR67; + ioreqs[0].value = chip->ofdm_cal_values[OFDM_36M_INDEX][channel-1]; + ioreqs[1].addr = ZD_CR66; + ioreqs[1].value = chip->ofdm_cal_values[OFDM_48M_INDEX][channel-1]; + ioreqs[2].addr = ZD_CR65; + ioreqs[2].value = chip->ofdm_cal_values[OFDM_54M_INDEX][channel-1]; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int update_channel_integration_and_calibration(struct zd_chip *chip, + u8 channel) +{ + int r; + + if (!zd_rf_should_update_pwr_int(&chip->rf)) + return 0; + + r = update_pwr_int(chip, channel); + if (r) + return r; + if (zd_chip_is_zd1211b(chip)) { + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR69, 0x28 }, + {}, + { ZD_CR69, 0x2a }, + }; + + r = update_ofdm_cal(chip, channel); + if (r) + return r; + r = update_pwr_cal(chip, channel); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + } + + return 0; +} + +/* The CCK baseband gain can be optionally patched by the EEPROM */ +static int patch_cck_gain(struct zd_chip *chip) +{ + int r; + u32 value; + + if (!chip->patch_cck_gain || !zd_rf_should_patch_cck_gain(&chip->rf)) + return 0; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &value, E2P_PHY_REG); + if (r) + return r; + dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value & 0xff); + return zd_iowrite16_locked(chip, value & 0xff, ZD_CR47); +} + +int zd_chip_set_channel(struct zd_chip *chip, u8 channel) +{ + int r, t; + + mutex_lock(&chip->mutex); + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + r = zd_rf_set_channel(&chip->rf, channel); + if (r) + goto unlock; + r = update_channel_integration_and_calibration(chip, channel); + if (r) + goto unlock; + r = patch_cck_gain(chip); + if (r) + goto unlock; + r = patch_6m_band_edge(chip, channel); + if (r) + goto unlock; + r = zd_iowrite32_locked(chip, 0, CR_CONFIG_PHILIPS); +unlock: + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + mutex_unlock(&chip->mutex); + return r; +} + +u8 zd_chip_get_channel(struct zd_chip *chip) +{ + u8 channel; + + mutex_lock(&chip->mutex); + channel = chip->rf.channel; + mutex_unlock(&chip->mutex); + return channel; +} + +int zd_chip_control_leds(struct zd_chip *chip, enum led_status status) +{ + const zd_addr_t a[] = { + fw_reg_addr(chip, FW_REG_LED_LINK_STATUS), + CR_LED, + }; + + int r; + u16 v[ARRAY_SIZE(a)]; + struct zd_ioreq16 ioreqs[ARRAY_SIZE(a)] = { + [0] = { fw_reg_addr(chip, FW_REG_LED_LINK_STATUS) }, + [1] = { CR_LED }, + }; + u16 other_led; + + mutex_lock(&chip->mutex); + r = zd_ioread16v_locked(chip, v, (const zd_addr_t *)a, ARRAY_SIZE(a)); + if (r) + goto out; + + other_led = chip->link_led == LED1 ? LED2 : LED1; + + switch (status) { + case ZD_LED_OFF: + ioreqs[0].value = FW_LINK_OFF; + ioreqs[1].value = v[1] & ~(LED1|LED2); + break; + case ZD_LED_SCANNING: + ioreqs[0].value = FW_LINK_OFF; + ioreqs[1].value = v[1] & ~other_led; + if (get_seconds() % 3 == 0) { + ioreqs[1].value &= ~chip->link_led; + } else { + ioreqs[1].value |= chip->link_led; + } + break; + case ZD_LED_ASSOCIATED: + ioreqs[0].value = FW_LINK_TX; + ioreqs[1].value = v[1] & ~other_led; + ioreqs[1].value |= chip->link_led; + break; + default: + r = -EINVAL; + goto out; + } + + if (v[0] != ioreqs[0].value || v[1] != ioreqs[1].value) { + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + goto out; + } + r = 0; +out: + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates) +{ + int r; + + if (cr_rates & ~(CR_RATES_80211B|CR_RATES_80211G)) + return -EINVAL; + + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, cr_rates, CR_BASIC_RATE_TBL); + mutex_unlock(&chip->mutex); + return r; +} + +static inline u8 zd_rate_from_ofdm_plcp_header(const void *rx_frame) +{ + return ZD_OFDM | zd_ofdm_plcp_header_rate(rx_frame); +} + +/** + * zd_rx_rate - report zd-rate + * @rx_frame - received frame + * @rx_status - rx_status as given by the device + * + * This function converts the rate as encoded in the received packet to the + * zd-rate, we are using on other places in the driver. + */ +u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status) +{ + u8 zd_rate; + if (status->frame_status & ZD_RX_OFDM) { + zd_rate = zd_rate_from_ofdm_plcp_header(rx_frame); + } else { + switch (zd_cck_plcp_header_signal(rx_frame)) { + case ZD_CCK_PLCP_SIGNAL_1M: + zd_rate = ZD_CCK_RATE_1M; + break; + case ZD_CCK_PLCP_SIGNAL_2M: + zd_rate = ZD_CCK_RATE_2M; + break; + case ZD_CCK_PLCP_SIGNAL_5M5: + zd_rate = ZD_CCK_RATE_5_5M; + break; + case ZD_CCK_PLCP_SIGNAL_11M: + zd_rate = ZD_CCK_RATE_11M; + break; + default: + zd_rate = 0; + } + } + + return zd_rate; +} + +int zd_chip_switch_radio_on(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_switch_radio_on(&chip->rf); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_switch_radio_off(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_switch_radio_off(&chip->rf); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_enable_int(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_usb_enable_int(&chip->usb); + mutex_unlock(&chip->mutex); + return r; +} + +void zd_chip_disable_int(struct zd_chip *chip) +{ + mutex_lock(&chip->mutex); + zd_usb_disable_int(&chip->usb); + mutex_unlock(&chip->mutex); + + /* cancel pending interrupt work */ + cancel_work_sync(&zd_chip_to_mac(chip)->process_intr); +} + +int zd_chip_enable_rxtx(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + zd_usb_enable_tx(&chip->usb); + r = zd_usb_enable_rx(&chip->usb); + zd_tx_watchdog_enable(&chip->usb); + mutex_unlock(&chip->mutex); + return r; +} + +void zd_chip_disable_rxtx(struct zd_chip *chip) +{ + mutex_lock(&chip->mutex); + zd_tx_watchdog_disable(&chip->usb); + zd_usb_disable_rx(&chip->usb); + zd_usb_disable_tx(&chip->usb); + mutex_unlock(&chip->mutex); +} + +int zd_rfwritev_locked(struct zd_chip *chip, + const u32* values, unsigned int count, u8 bits) +{ + int r; + unsigned int i; + + for (i = 0; i < count; i++) { + r = zd_rfwrite_locked(chip, values[i], bits); + if (r) + return r; + } + + return 0; +} + +/* + * We can optionally program the RF directly through CR regs, if supported by + * the hardware. This is much faster than the older method. + */ +int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value) +{ + const struct zd_ioreq16 ioreqs[] = { + { ZD_CR244, (value >> 16) & 0xff }, + { ZD_CR243, (value >> 8) & 0xff }, + { ZD_CR242, value & 0xff }, + }; + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rfwritev_cr_locked(struct zd_chip *chip, + const u32 *values, unsigned int count) +{ + int r; + unsigned int i; + + for (i = 0; i < count; i++) { + r = zd_rfwrite_cr_locked(chip, values[i]); + if (r) + return r; + } + + return 0; +} + +int zd_chip_set_multicast_hash(struct zd_chip *chip, + struct zd_mc_hash *hash) +{ + const struct zd_ioreq32 ioreqs[] = { + { CR_GROUP_HASH_P1, hash->low }, + { CR_GROUP_HASH_P2, hash->high }, + }; + + return zd_iowrite32a(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +u64 zd_chip_get_tsf(struct zd_chip *chip) +{ + int r; + static const zd_addr_t aw_pt_bi_addr[] = + { CR_TSF_LOW_PART, CR_TSF_HIGH_PART }; + u32 values[2]; + u64 tsf; + + mutex_lock(&chip->mutex); + r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr, + ARRAY_SIZE(aw_pt_bi_addr)); + mutex_unlock(&chip->mutex); + if (r) + return 0; + + tsf = values[1]; + tsf = (tsf << 32) | values[0]; + + return tsf; +} diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_chip.h b/drivers/net/wireless/zydas/zd1211rw/zd_chip.h new file mode 100644 index 000000000000..b03786c9f3aa --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_chip.h @@ -0,0 +1,983 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _ZD_CHIP_H +#define _ZD_CHIP_H + +#include + +#include "zd_rf.h" +#include "zd_usb.h" + +/* Header for the Media Access Controller (MAC) and the Baseband Processor + * (BBP). It appears that the ZD1211 wraps the old ZD1205 with USB glue and + * adds a processor for handling the USB protocol. + */ + +/* Address space */ +enum { + /* CONTROL REGISTERS */ + CR_START = 0x9000, + + + /* FIRMWARE */ + FW_START = 0xee00, + + + /* EEPROM */ + E2P_START = 0xf800, + E2P_LEN = 0x800, + + /* EEPROM layout */ + E2P_LOAD_CODE_LEN = 0xe, /* base 0xf800 */ + E2P_LOAD_VECT_LEN = 0x9, /* base 0xf80e */ + /* E2P_DATA indexes into this */ + E2P_DATA_LEN = 0x7e, /* base 0xf817 */ + E2P_BOOT_CODE_LEN = 0x760, /* base 0xf895 */ + E2P_INTR_VECT_LEN = 0xb, /* base 0xfff5 */ + + /* Some precomputed offsets into the EEPROM */ + E2P_DATA_OFFSET = E2P_LOAD_CODE_LEN + E2P_LOAD_VECT_LEN, + E2P_BOOT_CODE_OFFSET = E2P_DATA_OFFSET + E2P_DATA_LEN, +}; + +#define CTL_REG(offset) ((zd_addr_t)(CR_START + (offset))) +#define E2P_DATA(offset) ((zd_addr_t)(E2P_START + E2P_DATA_OFFSET + (offset))) +#define FWRAW_DATA(offset) ((zd_addr_t)(FW_START + (offset))) + +/* 8-bit hardware registers */ +#define ZD_CR0 CTL_REG(0x0000) +#define ZD_CR1 CTL_REG(0x0004) +#define ZD_CR2 CTL_REG(0x0008) +#define ZD_CR3 CTL_REG(0x000C) + +#define ZD_CR5 CTL_REG(0x0010) +/* bit 5: if set short preamble used + * bit 6: filter band - Japan channel 14 on, else off + */ +#define ZD_CR6 CTL_REG(0x0014) +#define ZD_CR7 CTL_REG(0x0018) +#define ZD_CR8 CTL_REG(0x001C) + +#define ZD_CR4 CTL_REG(0x0020) + +#define ZD_CR9 CTL_REG(0x0024) +/* bit 2: antenna switch (together with ZD_CR10) */ +#define ZD_CR10 CTL_REG(0x0028) +/* bit 1: antenna switch (together with ZD_CR9) + * RF2959 controls with ZD_CR11 radion on and off + */ +#define ZD_CR11 CTL_REG(0x002C) +/* bit 6: TX power control for OFDM + * RF2959 controls with ZD_CR10 radio on and off + */ +#define ZD_CR12 CTL_REG(0x0030) +#define ZD_CR13 CTL_REG(0x0034) +#define ZD_CR14 CTL_REG(0x0038) +#define ZD_CR15 CTL_REG(0x003C) +#define ZD_CR16 CTL_REG(0x0040) +#define ZD_CR17 CTL_REG(0x0044) +#define ZD_CR18 CTL_REG(0x0048) +#define ZD_CR19 CTL_REG(0x004C) +#define ZD_CR20 CTL_REG(0x0050) +#define ZD_CR21 CTL_REG(0x0054) +#define ZD_CR22 CTL_REG(0x0058) +#define ZD_CR23 CTL_REG(0x005C) +#define ZD_CR24 CTL_REG(0x0060) /* CCA threshold */ +#define ZD_CR25 CTL_REG(0x0064) +#define ZD_CR26 CTL_REG(0x0068) +#define ZD_CR27 CTL_REG(0x006C) +#define ZD_CR28 CTL_REG(0x0070) +#define ZD_CR29 CTL_REG(0x0074) +#define ZD_CR30 CTL_REG(0x0078) +#define ZD_CR31 CTL_REG(0x007C) /* TX power control for RF in + * CCK mode + */ +#define ZD_CR32 CTL_REG(0x0080) +#define ZD_CR33 CTL_REG(0x0084) +#define ZD_CR34 CTL_REG(0x0088) +#define ZD_CR35 CTL_REG(0x008C) +#define ZD_CR36 CTL_REG(0x0090) +#define ZD_CR37 CTL_REG(0x0094) +#define ZD_CR38 CTL_REG(0x0098) +#define ZD_CR39 CTL_REG(0x009C) +#define ZD_CR40 CTL_REG(0x00A0) +#define ZD_CR41 CTL_REG(0x00A4) +#define ZD_CR42 CTL_REG(0x00A8) +#define ZD_CR43 CTL_REG(0x00AC) +#define ZD_CR44 CTL_REG(0x00B0) +#define ZD_CR45 CTL_REG(0x00B4) +#define ZD_CR46 CTL_REG(0x00B8) +#define ZD_CR47 CTL_REG(0x00BC) /* CCK baseband gain + * (patch value might be in EEPROM) + */ +#define ZD_CR48 CTL_REG(0x00C0) +#define ZD_CR49 CTL_REG(0x00C4) +#define ZD_CR50 CTL_REG(0x00C8) +#define ZD_CR51 CTL_REG(0x00CC) /* TX power control for RF in + * 6-36M modes + */ +#define ZD_CR52 CTL_REG(0x00D0) /* TX power control for RF in + * 48M mode + */ +#define ZD_CR53 CTL_REG(0x00D4) /* TX power control for RF in + * 54M mode + */ +#define ZD_CR54 CTL_REG(0x00D8) +#define ZD_CR55 CTL_REG(0x00DC) +#define ZD_CR56 CTL_REG(0x00E0) +#define ZD_CR57 CTL_REG(0x00E4) +#define ZD_CR58 CTL_REG(0x00E8) +#define ZD_CR59 CTL_REG(0x00EC) +#define ZD_CR60 CTL_REG(0x00F0) +#define ZD_CR61 CTL_REG(0x00F4) +#define ZD_CR62 CTL_REG(0x00F8) +#define ZD_CR63 CTL_REG(0x00FC) +#define ZD_CR64 CTL_REG(0x0100) +#define ZD_CR65 CTL_REG(0x0104) /* OFDM 54M calibration */ +#define ZD_CR66 CTL_REG(0x0108) /* OFDM 48M calibration */ +#define ZD_CR67 CTL_REG(0x010C) /* OFDM 36M calibration */ +#define ZD_CR68 CTL_REG(0x0110) /* CCK calibration */ +#define ZD_CR69 CTL_REG(0x0114) +#define ZD_CR70 CTL_REG(0x0118) +#define ZD_CR71 CTL_REG(0x011C) +#define ZD_CR72 CTL_REG(0x0120) +#define ZD_CR73 CTL_REG(0x0124) +#define ZD_CR74 CTL_REG(0x0128) +#define ZD_CR75 CTL_REG(0x012C) +#define ZD_CR76 CTL_REG(0x0130) +#define ZD_CR77 CTL_REG(0x0134) +#define ZD_CR78 CTL_REG(0x0138) +#define ZD_CR79 CTL_REG(0x013C) +#define ZD_CR80 CTL_REG(0x0140) +#define ZD_CR81 CTL_REG(0x0144) +#define ZD_CR82 CTL_REG(0x0148) +#define ZD_CR83 CTL_REG(0x014C) +#define ZD_CR84 CTL_REG(0x0150) +#define ZD_CR85 CTL_REG(0x0154) +#define ZD_CR86 CTL_REG(0x0158) +#define ZD_CR87 CTL_REG(0x015C) +#define ZD_CR88 CTL_REG(0x0160) +#define ZD_CR89 CTL_REG(0x0164) +#define ZD_CR90 CTL_REG(0x0168) +#define ZD_CR91 CTL_REG(0x016C) +#define ZD_CR92 CTL_REG(0x0170) +#define ZD_CR93 CTL_REG(0x0174) +#define ZD_CR94 CTL_REG(0x0178) +#define ZD_CR95 CTL_REG(0x017C) +#define ZD_CR96 CTL_REG(0x0180) +#define ZD_CR97 CTL_REG(0x0184) +#define ZD_CR98 CTL_REG(0x0188) +#define ZD_CR99 CTL_REG(0x018C) +#define ZD_CR100 CTL_REG(0x0190) +#define ZD_CR101 CTL_REG(0x0194) +#define ZD_CR102 CTL_REG(0x0198) +#define ZD_CR103 CTL_REG(0x019C) +#define ZD_CR104 CTL_REG(0x01A0) +#define ZD_CR105 CTL_REG(0x01A4) +#define ZD_CR106 CTL_REG(0x01A8) +#define ZD_CR107 CTL_REG(0x01AC) +#define ZD_CR108 CTL_REG(0x01B0) +#define ZD_CR109 CTL_REG(0x01B4) +#define ZD_CR110 CTL_REG(0x01B8) +#define ZD_CR111 CTL_REG(0x01BC) +#define ZD_CR112 CTL_REG(0x01C0) +#define ZD_CR113 CTL_REG(0x01C4) +#define ZD_CR114 CTL_REG(0x01C8) +#define ZD_CR115 CTL_REG(0x01CC) +#define ZD_CR116 CTL_REG(0x01D0) +#define ZD_CR117 CTL_REG(0x01D4) +#define ZD_CR118 CTL_REG(0x01D8) +#define ZD_CR119 CTL_REG(0x01DC) +#define ZD_CR120 CTL_REG(0x01E0) +#define ZD_CR121 CTL_REG(0x01E4) +#define ZD_CR122 CTL_REG(0x01E8) +#define ZD_CR123 CTL_REG(0x01EC) +#define ZD_CR124 CTL_REG(0x01F0) +#define ZD_CR125 CTL_REG(0x01F4) +#define ZD_CR126 CTL_REG(0x01F8) +#define ZD_CR127 CTL_REG(0x01FC) +#define ZD_CR128 CTL_REG(0x0200) +#define ZD_CR129 CTL_REG(0x0204) +#define ZD_CR130 CTL_REG(0x0208) +#define ZD_CR131 CTL_REG(0x020C) +#define ZD_CR132 CTL_REG(0x0210) +#define ZD_CR133 CTL_REG(0x0214) +#define ZD_CR134 CTL_REG(0x0218) +#define ZD_CR135 CTL_REG(0x021C) +#define ZD_CR136 CTL_REG(0x0220) +#define ZD_CR137 CTL_REG(0x0224) +#define ZD_CR138 CTL_REG(0x0228) +#define ZD_CR139 CTL_REG(0x022C) +#define ZD_CR140 CTL_REG(0x0230) +#define ZD_CR141 CTL_REG(0x0234) +#define ZD_CR142 CTL_REG(0x0238) +#define ZD_CR143 CTL_REG(0x023C) +#define ZD_CR144 CTL_REG(0x0240) +#define ZD_CR145 CTL_REG(0x0244) +#define ZD_CR146 CTL_REG(0x0248) +#define ZD_CR147 CTL_REG(0x024C) +#define ZD_CR148 CTL_REG(0x0250) +#define ZD_CR149 CTL_REG(0x0254) +#define ZD_CR150 CTL_REG(0x0258) +#define ZD_CR151 CTL_REG(0x025C) +#define ZD_CR152 CTL_REG(0x0260) +#define ZD_CR153 CTL_REG(0x0264) +#define ZD_CR154 CTL_REG(0x0268) +#define ZD_CR155 CTL_REG(0x026C) +#define ZD_CR156 CTL_REG(0x0270) +#define ZD_CR157 CTL_REG(0x0274) +#define ZD_CR158 CTL_REG(0x0278) +#define ZD_CR159 CTL_REG(0x027C) +#define ZD_CR160 CTL_REG(0x0280) +#define ZD_CR161 CTL_REG(0x0284) +#define ZD_CR162 CTL_REG(0x0288) +#define ZD_CR163 CTL_REG(0x028C) +#define ZD_CR164 CTL_REG(0x0290) +#define ZD_CR165 CTL_REG(0x0294) +#define ZD_CR166 CTL_REG(0x0298) +#define ZD_CR167 CTL_REG(0x029C) +#define ZD_CR168 CTL_REG(0x02A0) +#define ZD_CR169 CTL_REG(0x02A4) +#define ZD_CR170 CTL_REG(0x02A8) +#define ZD_CR171 CTL_REG(0x02AC) +#define ZD_CR172 CTL_REG(0x02B0) +#define ZD_CR173 CTL_REG(0x02B4) +#define ZD_CR174 CTL_REG(0x02B8) +#define ZD_CR175 CTL_REG(0x02BC) +#define ZD_CR176 CTL_REG(0x02C0) +#define ZD_CR177 CTL_REG(0x02C4) +#define ZD_CR178 CTL_REG(0x02C8) +#define ZD_CR179 CTL_REG(0x02CC) +#define ZD_CR180 CTL_REG(0x02D0) +#define ZD_CR181 CTL_REG(0x02D4) +#define ZD_CR182 CTL_REG(0x02D8) +#define ZD_CR183 CTL_REG(0x02DC) +#define ZD_CR184 CTL_REG(0x02E0) +#define ZD_CR185 CTL_REG(0x02E4) +#define ZD_CR186 CTL_REG(0x02E8) +#define ZD_CR187 CTL_REG(0x02EC) +#define ZD_CR188 CTL_REG(0x02F0) +#define ZD_CR189 CTL_REG(0x02F4) +#define ZD_CR190 CTL_REG(0x02F8) +#define ZD_CR191 CTL_REG(0x02FC) +#define ZD_CR192 CTL_REG(0x0300) +#define ZD_CR193 CTL_REG(0x0304) +#define ZD_CR194 CTL_REG(0x0308) +#define ZD_CR195 CTL_REG(0x030C) +#define ZD_CR196 CTL_REG(0x0310) +#define ZD_CR197 CTL_REG(0x0314) +#define ZD_CR198 CTL_REG(0x0318) +#define ZD_CR199 CTL_REG(0x031C) +#define ZD_CR200 CTL_REG(0x0320) +#define ZD_CR201 CTL_REG(0x0324) +#define ZD_CR202 CTL_REG(0x0328) +#define ZD_CR203 CTL_REG(0x032C) /* I2C bus template value & flash + * control + */ +#define ZD_CR204 CTL_REG(0x0330) +#define ZD_CR205 CTL_REG(0x0334) +#define ZD_CR206 CTL_REG(0x0338) +#define ZD_CR207 CTL_REG(0x033C) +#define ZD_CR208 CTL_REG(0x0340) +#define ZD_CR209 CTL_REG(0x0344) +#define ZD_CR210 CTL_REG(0x0348) +#define ZD_CR211 CTL_REG(0x034C) +#define ZD_CR212 CTL_REG(0x0350) +#define ZD_CR213 CTL_REG(0x0354) +#define ZD_CR214 CTL_REG(0x0358) +#define ZD_CR215 CTL_REG(0x035C) +#define ZD_CR216 CTL_REG(0x0360) +#define ZD_CR217 CTL_REG(0x0364) +#define ZD_CR218 CTL_REG(0x0368) +#define ZD_CR219 CTL_REG(0x036C) +#define ZD_CR220 CTL_REG(0x0370) +#define ZD_CR221 CTL_REG(0x0374) +#define ZD_CR222 CTL_REG(0x0378) +#define ZD_CR223 CTL_REG(0x037C) +#define ZD_CR224 CTL_REG(0x0380) +#define ZD_CR225 CTL_REG(0x0384) +#define ZD_CR226 CTL_REG(0x0388) +#define ZD_CR227 CTL_REG(0x038C) +#define ZD_CR228 CTL_REG(0x0390) +#define ZD_CR229 CTL_REG(0x0394) +#define ZD_CR230 CTL_REG(0x0398) +#define ZD_CR231 CTL_REG(0x039C) +#define ZD_CR232 CTL_REG(0x03A0) +#define ZD_CR233 CTL_REG(0x03A4) +#define ZD_CR234 CTL_REG(0x03A8) +#define ZD_CR235 CTL_REG(0x03AC) +#define ZD_CR236 CTL_REG(0x03B0) + +#define ZD_CR240 CTL_REG(0x03C0) +/* bit 7: host-controlled RF register writes + * ZD_CR241-ZD_CR245: for hardware controlled writing of RF bits, not needed for + * USB + */ +#define ZD_CR241 CTL_REG(0x03C4) +#define ZD_CR242 CTL_REG(0x03C8) +#define ZD_CR243 CTL_REG(0x03CC) +#define ZD_CR244 CTL_REG(0x03D0) +#define ZD_CR245 CTL_REG(0x03D4) + +#define ZD_CR251 CTL_REG(0x03EC) /* only used for activation and + * deactivation of Airoha RFs AL2230 + * and AL7230B + */ +#define ZD_CR252 CTL_REG(0x03F0) +#define ZD_CR253 CTL_REG(0x03F4) +#define ZD_CR254 CTL_REG(0x03F8) +#define ZD_CR255 CTL_REG(0x03FC) + +#define CR_MAX_PHY_REG 255 + +/* Taken from the ZYDAS driver, not all of them are relevant for the ZD1211 + * driver. + */ + +#define CR_RF_IF_CLK CTL_REG(0x0400) +#define CR_RF_IF_DATA CTL_REG(0x0404) +#define CR_PE1_PE2 CTL_REG(0x0408) +#define CR_PE2_DLY CTL_REG(0x040C) +#define CR_LE1 CTL_REG(0x0410) +#define CR_LE2 CTL_REG(0x0414) +/* Seems to enable/disable GPI (General Purpose IO?) */ +#define CR_GPI_EN CTL_REG(0x0418) +#define CR_RADIO_PD CTL_REG(0x042C) +#define CR_RF2948_PD CTL_REG(0x042C) +#define CR_ENABLE_PS_MANUAL_AGC CTL_REG(0x043C) +#define CR_CONFIG_PHILIPS CTL_REG(0x0440) +#define CR_SA2400_SER_AP CTL_REG(0x0444) +#define CR_I2C_WRITE CTL_REG(0x0444) +#define CR_SA2400_SER_RP CTL_REG(0x0448) +#define CR_RADIO_PE CTL_REG(0x0458) +#define CR_RST_BUS_MASTER CTL_REG(0x045C) +#define CR_RFCFG CTL_REG(0x0464) +#define CR_HSTSCHG CTL_REG(0x046C) +#define CR_PHY_ON CTL_REG(0x0474) +#define CR_RX_DELAY CTL_REG(0x0478) +#define CR_RX_PE_DELAY CTL_REG(0x047C) +#define CR_GPIO_1 CTL_REG(0x0490) +#define CR_GPIO_2 CTL_REG(0x0494) +#define CR_EncryBufMux CTL_REG(0x04A8) +#define CR_PS_CTRL CTL_REG(0x0500) +#define CR_ADDA_PWR_DWN CTL_REG(0x0504) +#define CR_ADDA_MBIAS_WARMTIME CTL_REG(0x0508) +#define CR_MAC_PS_STATE CTL_REG(0x050C) + +#define CR_INTERRUPT CTL_REG(0x0510) +#define INT_TX_COMPLETE (1 << 0) +#define INT_RX_COMPLETE (1 << 1) +#define INT_RETRY_FAIL (1 << 2) +#define INT_WAKEUP (1 << 3) +#define INT_DTIM_NOTIFY (1 << 5) +#define INT_CFG_NEXT_BCN (1 << 6) +#define INT_BUS_ABORT (1 << 7) +#define INT_TX_FIFO_READY (1 << 8) +#define INT_UART (1 << 9) +#define INT_TX_COMPLETE_EN (1 << 16) +#define INT_RX_COMPLETE_EN (1 << 17) +#define INT_RETRY_FAIL_EN (1 << 18) +#define INT_WAKEUP_EN (1 << 19) +#define INT_DTIM_NOTIFY_EN (1 << 21) +#define INT_CFG_NEXT_BCN_EN (1 << 22) +#define INT_BUS_ABORT_EN (1 << 23) +#define INT_TX_FIFO_READY_EN (1 << 24) +#define INT_UART_EN (1 << 25) + +#define CR_TSF_LOW_PART CTL_REG(0x0514) +#define CR_TSF_HIGH_PART CTL_REG(0x0518) + +/* Following three values are in time units (1024us) + * Following condition must be met: + * atim < tbtt < bcn + */ +#define CR_ATIM_WND_PERIOD CTL_REG(0x051C) +#define CR_BCN_INTERVAL CTL_REG(0x0520) +#define CR_PRE_TBTT CTL_REG(0x0524) +/* in units of TU(1024us) */ + +/* for UART support */ +#define CR_UART_RBR_THR_DLL CTL_REG(0x0540) +#define CR_UART_DLM_IER CTL_REG(0x0544) +#define CR_UART_IIR_FCR CTL_REG(0x0548) +#define CR_UART_LCR CTL_REG(0x054c) +#define CR_UART_MCR CTL_REG(0x0550) +#define CR_UART_LSR CTL_REG(0x0554) +#define CR_UART_MSR CTL_REG(0x0558) +#define CR_UART_ECR CTL_REG(0x055c) +#define CR_UART_STATUS CTL_REG(0x0560) + +#define CR_PCI_TX_ADDR_P1 CTL_REG(0x0600) +#define CR_PCI_TX_AddR_P2 CTL_REG(0x0604) +#define CR_PCI_RX_AddR_P1 CTL_REG(0x0608) +#define CR_PCI_RX_AddR_P2 CTL_REG(0x060C) + +/* must be overwritten if custom MAC address will be used */ +#define CR_MAC_ADDR_P1 CTL_REG(0x0610) +#define CR_MAC_ADDR_P2 CTL_REG(0x0614) +#define CR_BSSID_P1 CTL_REG(0x0618) +#define CR_BSSID_P2 CTL_REG(0x061C) +#define CR_BCN_PLCP_CFG CTL_REG(0x0620) + +/* Group hash table for filtering incoming packets. + * + * The group hash table is 64 bit large and split over two parts. The first + * part is the lower part. The upper 6 bits of the last byte of the target + * address are used as index. Packets are received if the hash table bit is + * set. This is used for multicast handling, but for broadcasts (address + * ff:ff:ff:ff:ff:ff) the highest bit in the second table must also be set. + */ +#define CR_GROUP_HASH_P1 CTL_REG(0x0624) +#define CR_GROUP_HASH_P2 CTL_REG(0x0628) + +#define CR_RX_TIMEOUT CTL_REG(0x062C) + +/* Basic rates supported by the BSS. When producing ACK or CTS messages, the + * device will use a rate in this table that is less than or equal to the rate + * of the incoming frame which prompted the response. */ +#define CR_BASIC_RATE_TBL CTL_REG(0x0630) +#define CR_RATE_1M (1 << 0) /* 802.11b */ +#define CR_RATE_2M (1 << 1) /* 802.11b */ +#define CR_RATE_5_5M (1 << 2) /* 802.11b */ +#define CR_RATE_11M (1 << 3) /* 802.11b */ +#define CR_RATE_6M (1 << 8) /* 802.11g */ +#define CR_RATE_9M (1 << 9) /* 802.11g */ +#define CR_RATE_12M (1 << 10) /* 802.11g */ +#define CR_RATE_18M (1 << 11) /* 802.11g */ +#define CR_RATE_24M (1 << 12) /* 802.11g */ +#define CR_RATE_36M (1 << 13) /* 802.11g */ +#define CR_RATE_48M (1 << 14) /* 802.11g */ +#define CR_RATE_54M (1 << 15) /* 802.11g */ +#define CR_RATES_80211G 0xff00 +#define CR_RATES_80211B 0x000f + +/* Mandatory rates required in the BSS. When producing ACK or CTS messages, if + * the device could not find an appropriate rate in CR_BASIC_RATE_TBL, it will + * look for a rate in this table that is less than or equal to the rate of + * the incoming frame. */ +#define CR_MANDATORY_RATE_TBL CTL_REG(0x0634) +#define CR_RTS_CTS_RATE CTL_REG(0x0638) + +/* These are all bit indexes in CR_RTS_CTS_RATE, so remember to shift. */ +#define RTSCTS_SH_RTS_RATE 0 +#define RTSCTS_SH_EXP_CTS_RATE 4 +#define RTSCTS_SH_RTS_MOD_TYPE 8 +#define RTSCTS_SH_RTS_PMB_TYPE 9 +#define RTSCTS_SH_CTS_RATE 16 +#define RTSCTS_SH_CTS_MOD_TYPE 24 +#define RTSCTS_SH_CTS_PMB_TYPE 25 + +#define CR_WEP_PROTECT CTL_REG(0x063C) +#define CR_RX_THRESHOLD CTL_REG(0x0640) + +/* register for controlling the LEDS */ +#define CR_LED CTL_REG(0x0644) +/* masks for controlling LEDs */ +#define LED1 (1 << 8) +#define LED2 (1 << 9) +#define LED_SW (1 << 10) + +/* Seems to indicate that the configuration is over. + */ +#define CR_AFTER_PNP CTL_REG(0x0648) +#define CR_ACK_TIME_80211 CTL_REG(0x0658) + +#define CR_RX_OFFSET CTL_REG(0x065c) + +#define CR_BCN_LENGTH CTL_REG(0x0664) +#define CR_PHY_DELAY CTL_REG(0x066C) +#define CR_BCN_FIFO CTL_REG(0x0670) +#define CR_SNIFFER_ON CTL_REG(0x0674) + +#define CR_ENCRYPTION_TYPE CTL_REG(0x0678) +#define NO_WEP 0 +#define WEP64 1 +#define WEP128 5 +#define WEP256 6 +#define ENC_SNIFFER 8 + +#define CR_ZD1211_RETRY_MAX CTL_REG(0x067C) + +#define CR_REG1 CTL_REG(0x0680) +/* Setting the bit UNLOCK_PHY_REGS disallows the write access to physical + * registers, so one could argue it is a LOCK bit. But calling it + * LOCK_PHY_REGS makes it confusing. + */ +#define UNLOCK_PHY_REGS (1 << 7) + +#define CR_DEVICE_STATE CTL_REG(0x0684) +#define CR_UNDERRUN_CNT CTL_REG(0x0688) + +#define CR_RX_FILTER CTL_REG(0x068c) +#define RX_FILTER_ASSOC_REQUEST (1 << 0) +#define RX_FILTER_ASSOC_RESPONSE (1 << 1) +#define RX_FILTER_REASSOC_REQUEST (1 << 2) +#define RX_FILTER_REASSOC_RESPONSE (1 << 3) +#define RX_FILTER_PROBE_REQUEST (1 << 4) +#define RX_FILTER_PROBE_RESPONSE (1 << 5) +/* bits 6 and 7 reserved */ +#define RX_FILTER_BEACON (1 << 8) +#define RX_FILTER_ATIM (1 << 9) +#define RX_FILTER_DISASSOC (1 << 10) +#define RX_FILTER_AUTH (1 << 11) +#define RX_FILTER_DEAUTH (1 << 12) +#define RX_FILTER_PSPOLL (1 << 26) +#define RX_FILTER_RTS (1 << 27) +#define RX_FILTER_CTS (1 << 28) +#define RX_FILTER_ACK (1 << 29) +#define RX_FILTER_CFEND (1 << 30) +#define RX_FILTER_CFACK (1 << 31) + +/* Enable bits for all frames you are interested in. */ +#define STA_RX_FILTER (RX_FILTER_ASSOC_REQUEST | RX_FILTER_ASSOC_RESPONSE | \ + RX_FILTER_REASSOC_REQUEST | RX_FILTER_REASSOC_RESPONSE | \ + RX_FILTER_PROBE_REQUEST | RX_FILTER_PROBE_RESPONSE | \ + (0x3 << 6) /* vendor driver sets these reserved bits */ | \ + RX_FILTER_BEACON | RX_FILTER_ATIM | RX_FILTER_DISASSOC | \ + RX_FILTER_AUTH | RX_FILTER_DEAUTH | \ + (0x7 << 13) /* vendor driver sets these reserved bits */ | \ + RX_FILTER_PSPOLL | RX_FILTER_ACK) /* 0x2400ffff */ + +#define RX_FILTER_CTRL (RX_FILTER_RTS | RX_FILTER_CTS | \ + RX_FILTER_CFEND | RX_FILTER_CFACK) + +#define BCN_MODE_AP 0x1000000 +#define BCN_MODE_IBSS 0x2000000 + +/* Monitor mode sets filter to 0xfffff */ + +#define CR_ACK_TIMEOUT_EXT CTL_REG(0x0690) +#define CR_BCN_FIFO_SEMAPHORE CTL_REG(0x0694) + +#define CR_IFS_VALUE CTL_REG(0x0698) +#define IFS_VALUE_DIFS_SH 0 +#define IFS_VALUE_EIFS_SH 12 +#define IFS_VALUE_SIFS_SH 24 +#define IFS_VALUE_DEFAULT (( 50 << IFS_VALUE_DIFS_SH) | \ + (1148 << IFS_VALUE_EIFS_SH) | \ + ( 10 << IFS_VALUE_SIFS_SH)) + +#define CR_RX_TIME_OUT CTL_REG(0x069C) +#define CR_TOTAL_RX_FRM CTL_REG(0x06A0) +#define CR_CRC32_CNT CTL_REG(0x06A4) +#define CR_CRC16_CNT CTL_REG(0x06A8) +#define CR_DECRYPTION_ERR_UNI CTL_REG(0x06AC) +#define CR_RX_FIFO_OVERRUN CTL_REG(0x06B0) + +#define CR_DECRYPTION_ERR_MUL CTL_REG(0x06BC) + +#define CR_NAV_CNT CTL_REG(0x06C4) +#define CR_NAV_CCA CTL_REG(0x06C8) +#define CR_RETRY_CNT CTL_REG(0x06CC) + +#define CR_READ_TCB_ADDR CTL_REG(0x06E8) +#define CR_READ_RFD_ADDR CTL_REG(0x06EC) +#define CR_CWMIN_CWMAX CTL_REG(0x06F0) +#define CR_TOTAL_TX_FRM CTL_REG(0x06F4) + +/* CAM: Continuous Access Mode (power management) */ +#define CR_CAM_MODE CTL_REG(0x0700) +#define MODE_IBSS 0x0 +#define MODE_AP 0x1 +#define MODE_STA 0x2 +#define MODE_AP_WDS 0x3 + +#define CR_CAM_ROLL_TB_LOW CTL_REG(0x0704) +#define CR_CAM_ROLL_TB_HIGH CTL_REG(0x0708) +#define CR_CAM_ADDRESS CTL_REG(0x070C) +#define CR_CAM_DATA CTL_REG(0x0710) + +#define CR_ROMDIR CTL_REG(0x0714) + +#define CR_DECRY_ERR_FLG_LOW CTL_REG(0x0714) +#define CR_DECRY_ERR_FLG_HIGH CTL_REG(0x0718) + +#define CR_WEPKEY0 CTL_REG(0x0720) +#define CR_WEPKEY1 CTL_REG(0x0724) +#define CR_WEPKEY2 CTL_REG(0x0728) +#define CR_WEPKEY3 CTL_REG(0x072C) +#define CR_WEPKEY4 CTL_REG(0x0730) +#define CR_WEPKEY5 CTL_REG(0x0734) +#define CR_WEPKEY6 CTL_REG(0x0738) +#define CR_WEPKEY7 CTL_REG(0x073C) +#define CR_WEPKEY8 CTL_REG(0x0740) +#define CR_WEPKEY9 CTL_REG(0x0744) +#define CR_WEPKEY10 CTL_REG(0x0748) +#define CR_WEPKEY11 CTL_REG(0x074C) +#define CR_WEPKEY12 CTL_REG(0x0750) +#define CR_WEPKEY13 CTL_REG(0x0754) +#define CR_WEPKEY14 CTL_REG(0x0758) +#define CR_WEPKEY15 CTL_REG(0x075c) +#define CR_TKIP_MODE CTL_REG(0x0760) + +#define CR_EEPROM_PROTECT0 CTL_REG(0x0758) +#define CR_EEPROM_PROTECT1 CTL_REG(0x075C) + +#define CR_DBG_FIFO_RD CTL_REG(0x0800) +#define CR_DBG_SELECT CTL_REG(0x0804) +#define CR_FIFO_Length CTL_REG(0x0808) + + +#define CR_RSSI_MGC CTL_REG(0x0810) + +#define CR_PON CTL_REG(0x0818) +#define CR_RX_ON CTL_REG(0x081C) +#define CR_TX_ON CTL_REG(0x0820) +#define CR_CHIP_EN CTL_REG(0x0824) +#define CR_LO_SW CTL_REG(0x0828) +#define CR_TXRX_SW CTL_REG(0x082C) +#define CR_S_MD CTL_REG(0x0830) + +#define CR_USB_DEBUG_PORT CTL_REG(0x0888) +#define CR_ZD1211B_CWIN_MAX_MIN_AC0 CTL_REG(0x0b00) +#define CR_ZD1211B_CWIN_MAX_MIN_AC1 CTL_REG(0x0b04) +#define CR_ZD1211B_CWIN_MAX_MIN_AC2 CTL_REG(0x0b08) +#define CR_ZD1211B_CWIN_MAX_MIN_AC3 CTL_REG(0x0b0c) +#define CR_ZD1211B_AIFS_CTL1 CTL_REG(0x0b10) +#define CR_ZD1211B_AIFS_CTL2 CTL_REG(0x0b14) +#define CR_ZD1211B_TXOP CTL_REG(0x0b20) +#define CR_ZD1211B_RETRY_MAX CTL_REG(0x0b28) + +/* Value for CR_ZD1211_RETRY_MAX & CR_ZD1211B_RETRY_MAX. Vendor driver uses 2, + * we use 0. The first rate is tried (count+2), then all next rates are tried + * twice, until 1 Mbits is tried. */ +#define ZD1211_RETRY_COUNT 0 +#define ZD1211B_RETRY_COUNT \ + (ZD1211_RETRY_COUNT << 0)| \ + (ZD1211_RETRY_COUNT << 8)| \ + (ZD1211_RETRY_COUNT << 16)| \ + (ZD1211_RETRY_COUNT << 24) + +/* Used to detect PLL lock */ +#define UW2453_INTR_REG ((zd_addr_t)0x85c1) + +#define CWIN_SIZE 0x007f043f + + +#define HWINT_ENABLED \ + (INT_TX_COMPLETE_EN| \ + INT_RX_COMPLETE_EN| \ + INT_RETRY_FAIL_EN| \ + INT_WAKEUP_EN| \ + INT_CFG_NEXT_BCN_EN) + +#define HWINT_DISABLED 0 + +#define E2P_PWR_INT_GUARD 8 +#define E2P_CHANNEL_COUNT 14 + +/* If you compare this addresses with the ZYDAS orignal driver, please notify + * that we use word mapping for the EEPROM. + */ + +/* + * Upper 16 bit contains the regulatory domain. + */ +#define E2P_SUBID E2P_DATA(0x00) +#define E2P_POD E2P_DATA(0x02) +#define E2P_MAC_ADDR_P1 E2P_DATA(0x04) +#define E2P_MAC_ADDR_P2 E2P_DATA(0x06) +#define E2P_PWR_CAL_VALUE1 E2P_DATA(0x08) +#define E2P_PWR_CAL_VALUE2 E2P_DATA(0x0a) +#define E2P_PWR_CAL_VALUE3 E2P_DATA(0x0c) +#define E2P_PWR_CAL_VALUE4 E2P_DATA(0x0e) +#define E2P_PWR_INT_VALUE1 E2P_DATA(0x10) +#define E2P_PWR_INT_VALUE2 E2P_DATA(0x12) +#define E2P_PWR_INT_VALUE3 E2P_DATA(0x14) +#define E2P_PWR_INT_VALUE4 E2P_DATA(0x16) + +/* Contains a bit for each allowed channel. It gives for Europe (ETSI 0x30) + * also only 11 channels. */ +#define E2P_ALLOWED_CHANNEL E2P_DATA(0x18) + +#define E2P_DEVICE_VER E2P_DATA(0x20) +#define E2P_PHY_REG E2P_DATA(0x25) +#define E2P_36M_CAL_VALUE1 E2P_DATA(0x28) +#define E2P_36M_CAL_VALUE2 E2P_DATA(0x2a) +#define E2P_36M_CAL_VALUE3 E2P_DATA(0x2c) +#define E2P_36M_CAL_VALUE4 E2P_DATA(0x2e) +#define E2P_11A_INT_VALUE1 E2P_DATA(0x30) +#define E2P_11A_INT_VALUE2 E2P_DATA(0x32) +#define E2P_11A_INT_VALUE3 E2P_DATA(0x34) +#define E2P_11A_INT_VALUE4 E2P_DATA(0x36) +#define E2P_48M_CAL_VALUE1 E2P_DATA(0x38) +#define E2P_48M_CAL_VALUE2 E2P_DATA(0x3a) +#define E2P_48M_CAL_VALUE3 E2P_DATA(0x3c) +#define E2P_48M_CAL_VALUE4 E2P_DATA(0x3e) +#define E2P_48M_INT_VALUE1 E2P_DATA(0x40) +#define E2P_48M_INT_VALUE2 E2P_DATA(0x42) +#define E2P_48M_INT_VALUE3 E2P_DATA(0x44) +#define E2P_48M_INT_VALUE4 E2P_DATA(0x46) +#define E2P_54M_CAL_VALUE1 E2P_DATA(0x48) /* ??? */ +#define E2P_54M_CAL_VALUE2 E2P_DATA(0x4a) +#define E2P_54M_CAL_VALUE3 E2P_DATA(0x4c) +#define E2P_54M_CAL_VALUE4 E2P_DATA(0x4e) +#define E2P_54M_INT_VALUE1 E2P_DATA(0x50) +#define E2P_54M_INT_VALUE2 E2P_DATA(0x52) +#define E2P_54M_INT_VALUE3 E2P_DATA(0x54) +#define E2P_54M_INT_VALUE4 E2P_DATA(0x56) + +/* This word contains the base address of the FW_REG_ registers below */ +#define FWRAW_REGS_ADDR FWRAW_DATA(0x1d) + +/* All 16 bit values, offset from the address in FWRAW_REGS_ADDR */ +enum { + FW_REG_FIRMWARE_VER = 0, + /* non-zero if USB high speed connection */ + FW_REG_USB_SPEED = 1, + FW_REG_FIX_TX_RATE = 2, + /* Seems to be able to control LEDs over the firmware */ + FW_REG_LED_LINK_STATUS = 3, + FW_REG_SOFT_RESET = 4, + FW_REG_FLASH_CHK = 5, +}; + +/* Values for FW_LINK_STATUS */ +#define FW_LINK_OFF 0x0 +#define FW_LINK_TX 0x1 +/* 0x2 - link led on? */ + +enum { + /* indices for ofdm_cal_values */ + OFDM_36M_INDEX = 0, + OFDM_48M_INDEX = 1, + OFDM_54M_INDEX = 2, +}; + +struct zd_chip { + struct zd_usb usb; + struct zd_rf rf; + struct mutex mutex; + /* Base address of FW_REG_ registers */ + zd_addr_t fw_regs_base; + /* EepSetPoint in the vendor driver */ + u8 pwr_cal_values[E2P_CHANNEL_COUNT]; + /* integration values in the vendor driver */ + u8 pwr_int_values[E2P_CHANNEL_COUNT]; + /* SetPointOFDM in the vendor driver */ + u8 ofdm_cal_values[3][E2P_CHANNEL_COUNT]; + u16 link_led; + unsigned int pa_type:4, + patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1, + new_phy_layout:1, al2230s_bit:1, + supports_tx_led:1; +}; + +static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb) +{ + return container_of(usb, struct zd_chip, usb); +} + +static inline struct zd_chip *zd_rf_to_chip(struct zd_rf *rf) +{ + return container_of(rf, struct zd_chip, rf); +} + +#define zd_chip_dev(chip) (&(chip)->usb.intf->dev) + +void zd_chip_init(struct zd_chip *chip, + struct ieee80211_hw *hw, + struct usb_interface *intf); +void zd_chip_clear(struct zd_chip *chip); +int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr); +int zd_chip_init_hw(struct zd_chip *chip); +int zd_chip_reset(struct zd_chip *chip); + +static inline int zd_chip_is_zd1211b(struct zd_chip *chip) +{ + return chip->usb.is_zd1211b; +} + +static inline int zd_ioread16v_locked(struct zd_chip *chip, u16 *values, + const zd_addr_t *addresses, + unsigned int count) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_ioread16v(&chip->usb, values, addresses, count); +} + +static inline int zd_ioread16_locked(struct zd_chip *chip, u16 *value, + const zd_addr_t addr) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_ioread16(&chip->usb, value, addr); +} + +int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, + const zd_addr_t *addresses, unsigned int count); + +static inline int zd_ioread32_locked(struct zd_chip *chip, u32 *value, + const zd_addr_t addr) +{ + return zd_ioread32v_locked(chip, value, &addr, 1); +} + +static inline int zd_iowrite16_locked(struct zd_chip *chip, u16 value, + zd_addr_t addr) +{ + struct zd_ioreq16 ioreq; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + ioreq.addr = addr; + ioreq.value = value; + + return zd_usb_iowrite16v(&chip->usb, &ioreq, 1); +} + +int zd_iowrite16a_locked(struct zd_chip *chip, + const struct zd_ioreq16 *ioreqs, unsigned int count); + +int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count); + +static inline int zd_iowrite32_locked(struct zd_chip *chip, u32 value, + zd_addr_t addr) +{ + struct zd_ioreq32 ioreq; + + ioreq.addr = addr; + ioreq.value = value; + + return _zd_iowrite32v_locked(chip, &ioreq, 1); +} + +int zd_iowrite32a_locked(struct zd_chip *chip, + const struct zd_ioreq32 *ioreqs, unsigned int count); + +static inline int zd_rfwrite_locked(struct zd_chip *chip, u32 value, u8 bits) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_rfwrite(&chip->usb, value, bits); +} + +int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value); + +int zd_rfwritev_locked(struct zd_chip *chip, + const u32* values, unsigned int count, u8 bits); +int zd_rfwritev_cr_locked(struct zd_chip *chip, + const u32* values, unsigned int count); + +/* Locking functions for reading and writing registers. + * The different parameters are intentional. + */ +int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value); +int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value); +int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value); +int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value); +int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, + u32 *values, unsigned int count); +int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count); + +int zd_chip_set_channel(struct zd_chip *chip, u8 channel); +static inline u8 _zd_chip_get_channel(struct zd_chip *chip) +{ + return chip->rf.channel; +} +u8 zd_chip_get_channel(struct zd_chip *chip); +int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain); +int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr); +int zd_write_bssid(struct zd_chip *chip, const u8 *bssid); +int zd_chip_switch_radio_on(struct zd_chip *chip); +int zd_chip_switch_radio_off(struct zd_chip *chip); +int zd_chip_enable_int(struct zd_chip *chip); +void zd_chip_disable_int(struct zd_chip *chip); +int zd_chip_enable_rxtx(struct zd_chip *chip); +void zd_chip_disable_rxtx(struct zd_chip *chip); +int zd_chip_enable_hwint(struct zd_chip *chip); +int zd_chip_disable_hwint(struct zd_chip *chip); +int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel); +int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, int preamble); + +static inline int zd_get_encryption_type(struct zd_chip *chip, u32 *type) +{ + return zd_ioread32(chip, CR_ENCRYPTION_TYPE, type); +} + +static inline int zd_set_encryption_type(struct zd_chip *chip, u32 type) +{ + return zd_iowrite32(chip, CR_ENCRYPTION_TYPE, type); +} + +static inline int zd_chip_get_basic_rates(struct zd_chip *chip, u16 *cr_rates) +{ + return zd_ioread16(chip, CR_BASIC_RATE_TBL, cr_rates); +} + +int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates); + +int zd_chip_lock_phy_regs(struct zd_chip *chip); +int zd_chip_unlock_phy_regs(struct zd_chip *chip); + +enum led_status { + ZD_LED_OFF = 0, + ZD_LED_SCANNING = 1, + ZD_LED_ASSOCIATED = 2, +}; + +int zd_chip_control_leds(struct zd_chip *chip, enum led_status status); + +int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period, + int type); + +static inline int zd_get_beacon_interval(struct zd_chip *chip, u32 *interval) +{ + return zd_ioread32(chip, CR_BCN_INTERVAL, interval); +} + +struct rx_status; + +u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status); + +struct zd_mc_hash { + u32 low; + u32 high; +}; + +static inline void zd_mc_clear(struct zd_mc_hash *hash) +{ + hash->low = 0; + /* The interfaces must always received broadcasts. + * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63. + */ + hash->high = 0x80000000; +} + +static inline void zd_mc_add_all(struct zd_mc_hash *hash) +{ + hash->low = hash->high = 0xffffffff; +} + +static inline void zd_mc_add_addr(struct zd_mc_hash *hash, u8 *addr) +{ + unsigned int i = addr[5] >> 2; + if (i < 32) { + hash->low |= 1 << i; + } else { + hash->high |= 1 << (i-32); + } +} + +int zd_chip_set_multicast_hash(struct zd_chip *chip, + struct zd_mc_hash *hash); + +u64 zd_chip_get_tsf(struct zd_chip *chip); + +#endif /* _ZD_CHIP_H */ diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_def.h b/drivers/net/wireless/zydas/zd1211rw/zd_def.h new file mode 100644 index 000000000000..41bd755bc135 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_def.h @@ -0,0 +1,69 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _ZD_DEF_H +#define _ZD_DEF_H + +#include +#include +#include + +typedef u16 __nocast zd_addr_t; + +#define dev_printk_f(level, dev, fmt, args...) \ + dev_printk(level, dev, "%s() " fmt, __func__, ##args) + +#ifdef DEBUG +# define dev_dbg_f(dev, fmt, args...) \ + dev_printk_f(KERN_DEBUG, dev, fmt, ## args) +# define dev_dbg_f_limit(dev, fmt, args...) do { \ + if (net_ratelimit()) \ + dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \ +} while (0) +# define dev_dbg_f_cond(dev, cond, fmt, args...) ({ \ + bool __cond = !!(cond); \ + if (unlikely(__cond)) \ + dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \ +}) +#else +# define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0) +# define dev_dbg_f_limit(dev, fmt, args...) do { (void)(dev); } while (0) +# define dev_dbg_f_cond(dev, cond, fmt, args...) do { (void)(dev); } while (0) +#endif /* DEBUG */ + +#ifdef DEBUG +# define ZD_ASSERT(x) \ +do { \ + if (unlikely(!(x))) { \ + pr_debug("%s:%d ASSERT %s VIOLATED!\n", \ + __FILE__, __LINE__, __stringify(x)); \ + dump_stack(); \ + } \ +} while (0) +#else +# define ZD_ASSERT(x) do { } while (0) +#endif + +#ifdef DEBUG +# define ZD_MEMCLEAR(pointer, size) memset((pointer), 0xff, (size)) +#else +# define ZD_MEMCLEAR(pointer, size) do { } while (0) +#endif + +#endif /* _ZD_DEF_H */ diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c new file mode 100644 index 000000000000..e539d9b1b562 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c @@ -0,0 +1,1550 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * Copyright (C) 2006-2007 Michael Wu + * Copyright (C) 2007-2008 Luis R. Rodriguez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "zd_def.h" +#include "zd_chip.h" +#include "zd_mac.h" +#include "zd_rf.h" + +struct zd_reg_alpha2_map { + u32 reg; + char alpha2[2]; +}; + +static struct zd_reg_alpha2_map reg_alpha2_map[] = { + { ZD_REGDOMAIN_FCC, "US" }, + { ZD_REGDOMAIN_IC, "CA" }, + { ZD_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */ + { ZD_REGDOMAIN_JAPAN, "JP" }, + { ZD_REGDOMAIN_JAPAN_2, "JP" }, + { ZD_REGDOMAIN_JAPAN_3, "JP" }, + { ZD_REGDOMAIN_SPAIN, "ES" }, + { ZD_REGDOMAIN_FRANCE, "FR" }, +}; + +/* This table contains the hardware specific values for the modulation rates. */ +static const struct ieee80211_rate zd_rates[] = { + { .bitrate = 10, + .hw_value = ZD_CCK_RATE_1M, }, + { .bitrate = 20, + .hw_value = ZD_CCK_RATE_2M, + .hw_value_short = ZD_CCK_RATE_2M | ZD_CCK_PREA_SHORT, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, + .hw_value = ZD_CCK_RATE_5_5M, + .hw_value_short = ZD_CCK_RATE_5_5M | ZD_CCK_PREA_SHORT, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, + .hw_value = ZD_CCK_RATE_11M, + .hw_value_short = ZD_CCK_RATE_11M | ZD_CCK_PREA_SHORT, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60, + .hw_value = ZD_OFDM_RATE_6M, + .flags = 0 }, + { .bitrate = 90, + .hw_value = ZD_OFDM_RATE_9M, + .flags = 0 }, + { .bitrate = 120, + .hw_value = ZD_OFDM_RATE_12M, + .flags = 0 }, + { .bitrate = 180, + .hw_value = ZD_OFDM_RATE_18M, + .flags = 0 }, + { .bitrate = 240, + .hw_value = ZD_OFDM_RATE_24M, + .flags = 0 }, + { .bitrate = 360, + .hw_value = ZD_OFDM_RATE_36M, + .flags = 0 }, + { .bitrate = 480, + .hw_value = ZD_OFDM_RATE_48M, + .flags = 0 }, + { .bitrate = 540, + .hw_value = ZD_OFDM_RATE_54M, + .flags = 0 }, +}; + +/* + * Zydas retry rates table. Each line is listed in the same order as + * in zd_rates[] and contains all the rate used when a packet is sent + * starting with a given rates. Let's consider an example : + * + * "11 Mbits : 4, 3, 2, 1, 0" means : + * - packet is sent using 4 different rates + * - 1st rate is index 3 (ie 11 Mbits) + * - 2nd rate is index 2 (ie 5.5 Mbits) + * - 3rd rate is index 1 (ie 2 Mbits) + * - 4th rate is index 0 (ie 1 Mbits) + */ + +static const struct tx_retry_rate zd_retry_rates[] = { + { /* 1 Mbits */ 1, { 0 }}, + { /* 2 Mbits */ 2, { 1, 0 }}, + { /* 5.5 Mbits */ 3, { 2, 1, 0 }}, + { /* 11 Mbits */ 4, { 3, 2, 1, 0 }}, + { /* 6 Mbits */ 5, { 4, 3, 2, 1, 0 }}, + { /* 9 Mbits */ 6, { 5, 4, 3, 2, 1, 0}}, + { /* 12 Mbits */ 5, { 6, 3, 2, 1, 0 }}, + { /* 18 Mbits */ 6, { 7, 6, 3, 2, 1, 0 }}, + { /* 24 Mbits */ 6, { 8, 6, 3, 2, 1, 0 }}, + { /* 36 Mbits */ 7, { 9, 8, 6, 3, 2, 1, 0 }}, + { /* 48 Mbits */ 8, {10, 9, 8, 6, 3, 2, 1, 0 }}, + { /* 54 Mbits */ 9, {11, 10, 9, 8, 6, 3, 2, 1, 0 }} +}; + +static const struct ieee80211_channel zd_channels[] = { + { .center_freq = 2412, .hw_value = 1 }, + { .center_freq = 2417, .hw_value = 2 }, + { .center_freq = 2422, .hw_value = 3 }, + { .center_freq = 2427, .hw_value = 4 }, + { .center_freq = 2432, .hw_value = 5 }, + { .center_freq = 2437, .hw_value = 6 }, + { .center_freq = 2442, .hw_value = 7 }, + { .center_freq = 2447, .hw_value = 8 }, + { .center_freq = 2452, .hw_value = 9 }, + { .center_freq = 2457, .hw_value = 10 }, + { .center_freq = 2462, .hw_value = 11 }, + { .center_freq = 2467, .hw_value = 12 }, + { .center_freq = 2472, .hw_value = 13 }, + { .center_freq = 2484, .hw_value = 14 }, +}; + +static void housekeeping_init(struct zd_mac *mac); +static void housekeeping_enable(struct zd_mac *mac); +static void housekeeping_disable(struct zd_mac *mac); +static void beacon_init(struct zd_mac *mac); +static void beacon_enable(struct zd_mac *mac); +static void beacon_disable(struct zd_mac *mac); +static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble); +static int zd_mac_config_beacon(struct ieee80211_hw *hw, + struct sk_buff *beacon, bool in_intr); + +static int zd_reg2alpha2(u8 regdomain, char *alpha2) +{ + unsigned int i; + struct zd_reg_alpha2_map *reg_map; + for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) { + reg_map = ®_alpha2_map[i]; + if (regdomain == reg_map->reg) { + alpha2[0] = reg_map->alpha2[0]; + alpha2[1] = reg_map->alpha2[1]; + return 0; + } + } + return 1; +} + +static int zd_check_signal(struct ieee80211_hw *hw, int signal) +{ + struct zd_mac *mac = zd_hw_mac(hw); + + dev_dbg_f_cond(zd_mac_dev(mac), signal < 0 || signal > 100, + "%s: signal value from device not in range 0..100, " + "but %d.\n", __func__, signal); + + if (signal < 0) + signal = 0; + else if (signal > 100) + signal = 100; + + return signal; +} + +int zd_mac_preinit_hw(struct ieee80211_hw *hw) +{ + int r; + u8 addr[ETH_ALEN]; + struct zd_mac *mac = zd_hw_mac(hw); + + r = zd_chip_read_mac_addr_fw(&mac->chip, addr); + if (r) + return r; + + SET_IEEE80211_PERM_ADDR(hw, addr); + + return 0; +} + +int zd_mac_init_hw(struct ieee80211_hw *hw) +{ + int r; + struct zd_mac *mac = zd_hw_mac(hw); + struct zd_chip *chip = &mac->chip; + char alpha2[2]; + u8 default_regdomain; + + r = zd_chip_enable_int(chip); + if (r) + goto out; + r = zd_chip_init_hw(chip); + if (r) + goto disable_int; + + ZD_ASSERT(!irqs_disabled()); + + r = zd_read_regdomain(chip, &default_regdomain); + if (r) + goto disable_int; + spin_lock_irq(&mac->lock); + mac->regdomain = mac->default_regdomain = default_regdomain; + spin_unlock_irq(&mac->lock); + + /* We must inform the device that we are doing encryption/decryption in + * software at the moment. */ + r = zd_set_encryption_type(chip, ENC_SNIFFER); + if (r) + goto disable_int; + + r = zd_reg2alpha2(mac->regdomain, alpha2); + if (r) + goto disable_int; + + r = regulatory_hint(hw->wiphy, alpha2); +disable_int: + zd_chip_disable_int(chip); +out: + return r; +} + +void zd_mac_clear(struct zd_mac *mac) +{ + flush_workqueue(zd_workqueue); + zd_chip_clear(&mac->chip); + ZD_ASSERT(!spin_is_locked(&mac->lock)); + ZD_MEMCLEAR(mac, sizeof(struct zd_mac)); +} + +static int set_rx_filter(struct zd_mac *mac) +{ + unsigned long flags; + u32 filter = STA_RX_FILTER; + + spin_lock_irqsave(&mac->lock, flags); + if (mac->pass_ctrl) + filter |= RX_FILTER_CTRL; + spin_unlock_irqrestore(&mac->lock, flags); + + return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter); +} + +static int set_mac_and_bssid(struct zd_mac *mac) +{ + int r; + + if (!mac->vif) + return -1; + + r = zd_write_mac_addr(&mac->chip, mac->vif->addr); + if (r) + return r; + + /* Vendor driver after setting MAC either sets BSSID for AP or + * filter for other modes. + */ + if (mac->type != NL80211_IFTYPE_AP) + return set_rx_filter(mac); + else + return zd_write_bssid(&mac->chip, mac->vif->addr); +} + +static int set_mc_hash(struct zd_mac *mac) +{ + struct zd_mc_hash hash; + zd_mc_clear(&hash); + return zd_chip_set_multicast_hash(&mac->chip, &hash); +} + +int zd_op_start(struct ieee80211_hw *hw) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct zd_chip *chip = &mac->chip; + struct zd_usb *usb = &chip->usb; + int r; + + if (!usb->initialized) { + r = zd_usb_init_hw(usb); + if (r) + goto out; + } + + r = zd_chip_enable_int(chip); + if (r < 0) + goto out; + + r = zd_chip_set_basic_rates(chip, CR_RATES_80211B | CR_RATES_80211G); + if (r < 0) + goto disable_int; + r = set_rx_filter(mac); + if (r) + goto disable_int; + r = set_mc_hash(mac); + if (r) + goto disable_int; + + /* Wait after setting the multicast hash table and powering on + * the radio otherwise interface bring up will fail. This matches + * what the vendor driver did. + */ + msleep(10); + + r = zd_chip_switch_radio_on(chip); + if (r < 0) { + dev_err(zd_chip_dev(chip), + "%s: failed to set radio on\n", __func__); + goto disable_int; + } + r = zd_chip_enable_rxtx(chip); + if (r < 0) + goto disable_radio; + r = zd_chip_enable_hwint(chip); + if (r < 0) + goto disable_rxtx; + + housekeeping_enable(mac); + beacon_enable(mac); + set_bit(ZD_DEVICE_RUNNING, &mac->flags); + return 0; +disable_rxtx: + zd_chip_disable_rxtx(chip); +disable_radio: + zd_chip_switch_radio_off(chip); +disable_int: + zd_chip_disable_int(chip); +out: + return r; +} + +void zd_op_stop(struct ieee80211_hw *hw) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct zd_chip *chip = &mac->chip; + struct sk_buff *skb; + struct sk_buff_head *ack_wait_queue = &mac->ack_wait_queue; + + clear_bit(ZD_DEVICE_RUNNING, &mac->flags); + + /* The order here deliberately is a little different from the open() + * method, since we need to make sure there is no opportunity for RX + * frames to be processed by mac80211 after we have stopped it. + */ + + zd_chip_disable_rxtx(chip); + beacon_disable(mac); + housekeeping_disable(mac); + flush_workqueue(zd_workqueue); + + zd_chip_disable_hwint(chip); + zd_chip_switch_radio_off(chip); + zd_chip_disable_int(chip); + + + while ((skb = skb_dequeue(ack_wait_queue))) + dev_kfree_skb_any(skb); +} + +int zd_restore_settings(struct zd_mac *mac) +{ + struct sk_buff *beacon; + struct zd_mc_hash multicast_hash; + unsigned int short_preamble; + int r, beacon_interval, beacon_period; + u8 channel; + + dev_dbg_f(zd_mac_dev(mac), "\n"); + + spin_lock_irq(&mac->lock); + multicast_hash = mac->multicast_hash; + short_preamble = mac->short_preamble; + beacon_interval = mac->beacon.interval; + beacon_period = mac->beacon.period; + channel = mac->channel; + spin_unlock_irq(&mac->lock); + + r = set_mac_and_bssid(mac); + if (r < 0) { + dev_dbg_f(zd_mac_dev(mac), "set_mac_and_bssid failed, %d\n", r); + return r; + } + + r = zd_chip_set_channel(&mac->chip, channel); + if (r < 0) { + dev_dbg_f(zd_mac_dev(mac), "zd_chip_set_channel failed, %d\n", + r); + return r; + } + + set_rts_cts(mac, short_preamble); + + r = zd_chip_set_multicast_hash(&mac->chip, &multicast_hash); + if (r < 0) { + dev_dbg_f(zd_mac_dev(mac), + "zd_chip_set_multicast_hash failed, %d\n", r); + return r; + } + + if (mac->type == NL80211_IFTYPE_MESH_POINT || + mac->type == NL80211_IFTYPE_ADHOC || + mac->type == NL80211_IFTYPE_AP) { + if (mac->vif != NULL) { + beacon = ieee80211_beacon_get(mac->hw, mac->vif); + if (beacon) + zd_mac_config_beacon(mac->hw, beacon, false); + } + + zd_set_beacon_interval(&mac->chip, beacon_interval, + beacon_period, mac->type); + + spin_lock_irq(&mac->lock); + mac->beacon.last_update = jiffies; + spin_unlock_irq(&mac->lock); + } + + return 0; +} + +/** + * zd_mac_tx_status - reports tx status of a packet if required + * @hw - a &struct ieee80211_hw pointer + * @skb - a sk-buffer + * @flags: extra flags to set in the TX status info + * @ackssi: ACK signal strength + * @success - True for successful transmission of the frame + * + * This information calls ieee80211_tx_status_irqsafe() if required by the + * control information. It copies the control information into the status + * information. + * + * If no status information has been requested, the skb is freed. + */ +static void zd_mac_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, + int ackssi, struct tx_status *tx_status) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + int i; + int success = 1, retry = 1; + int first_idx; + const struct tx_retry_rate *retries; + + ieee80211_tx_info_clear_status(info); + + if (tx_status) { + success = !tx_status->failure; + retry = tx_status->retry + success; + } + + if (success) { + /* success */ + info->flags |= IEEE80211_TX_STAT_ACK; + } else { + /* failure */ + info->flags &= ~IEEE80211_TX_STAT_ACK; + } + + first_idx = info->status.rates[0].idx; + ZD_ASSERT(0<=first_idx && first_idxcount); + + info->status.rates[0].idx = retries->rate[0]; + info->status.rates[0].count = 1; // (retry > 1 ? 2 : 1); + + for (i=1; istatus.rates[i].idx = retries->rate[i]; + info->status.rates[i].count = 1; // ((i==retry-1) && success ? 1:2); + } + for (; istatus.rates[i].idx = retries->rate[retry - 1]; + info->status.rates[i].count = 1; // (success ? 1:2); + } + if (istatus.rates[i].idx = -1; /* terminate */ + + info->status.ack_signal = zd_check_signal(hw, ackssi); + ieee80211_tx_status_irqsafe(hw, skb); +} + +/** + * zd_mac_tx_failed - callback for failed frames + * @dev: the mac80211 wireless device + * + * This function is called if a frame couldn't be successfully + * transferred. The first frame from the tx queue, will be selected and + * reported as error to the upper layers. + */ +void zd_mac_tx_failed(struct urb *urb) +{ + struct ieee80211_hw * hw = zd_usb_to_hw(urb->context); + struct zd_mac *mac = zd_hw_mac(hw); + struct sk_buff_head *q = &mac->ack_wait_queue; + struct sk_buff *skb; + struct tx_status *tx_status = (struct tx_status *)urb->transfer_buffer; + unsigned long flags; + int success = !tx_status->failure; + int retry = tx_status->retry + success; + int found = 0; + int i, position = 0; + + q = &mac->ack_wait_queue; + spin_lock_irqsave(&q->lock, flags); + + skb_queue_walk(q, skb) { + struct ieee80211_hdr *tx_hdr; + struct ieee80211_tx_info *info; + int first_idx, final_idx; + const struct tx_retry_rate *retries; + u8 final_rate; + + position ++; + + /* if the hardware reports a failure and we had a 802.11 ACK + * pending, then we skip the first skb when searching for a + * matching frame */ + if (tx_status->failure && mac->ack_pending && + skb_queue_is_first(q, skb)) { + continue; + } + + tx_hdr = (struct ieee80211_hdr *)skb->data; + + /* we skip all frames not matching the reported destination */ + if (unlikely(!ether_addr_equal(tx_hdr->addr1, tx_status->mac))) + continue; + + /* we skip all frames not matching the reported final rate */ + + info = IEEE80211_SKB_CB(skb); + first_idx = info->status.rates[0].idx; + ZD_ASSERT(0<=first_idx && first_idx retries->count) + continue; + + final_idx = retries->rate[retry - 1]; + final_rate = zd_rates[final_idx].hw_value; + + if (final_rate != tx_status->rate) { + continue; + } + + found = 1; + break; + } + + if (found) { + for (i=1; i<=position; i++) { + skb = __skb_dequeue(q); + zd_mac_tx_status(hw, skb, + mac->ack_pending ? mac->ack_signal : 0, + i == position ? tx_status : NULL); + mac->ack_pending = 0; + } + } + + spin_unlock_irqrestore(&q->lock, flags); +} + +/** + * zd_mac_tx_to_dev - callback for USB layer + * @skb: a &sk_buff pointer + * @error: error value, 0 if transmission successful + * + * Informs the MAC layer that the frame has successfully transferred to the + * device. If an ACK is required and the transfer to the device has been + * successful, the packets are put on the @ack_wait_queue with + * the control set removed. + */ +void zd_mac_tx_to_dev(struct sk_buff *skb, int error) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hw *hw = info->rate_driver_data[0]; + struct zd_mac *mac = zd_hw_mac(hw); + + ieee80211_tx_info_clear_status(info); + + skb_pull(skb, sizeof(struct zd_ctrlset)); + if (unlikely(error || + (info->flags & IEEE80211_TX_CTL_NO_ACK))) { + /* + * FIXME : do we need to fill in anything ? + */ + ieee80211_tx_status_irqsafe(hw, skb); + } else { + struct sk_buff_head *q = &mac->ack_wait_queue; + + skb_queue_tail(q, skb); + while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS) { + zd_mac_tx_status(hw, skb_dequeue(q), + mac->ack_pending ? mac->ack_signal : 0, + NULL); + mac->ack_pending = 0; + } + } +} + +static int zd_calc_tx_length_us(u8 *service, u8 zd_rate, u16 tx_length) +{ + /* ZD_PURE_RATE() must be used to remove the modulation type flag of + * the zd-rate values. + */ + static const u8 rate_divisor[] = { + [ZD_PURE_RATE(ZD_CCK_RATE_1M)] = 1, + [ZD_PURE_RATE(ZD_CCK_RATE_2M)] = 2, + /* Bits must be doubled. */ + [ZD_PURE_RATE(ZD_CCK_RATE_5_5M)] = 11, + [ZD_PURE_RATE(ZD_CCK_RATE_11M)] = 11, + [ZD_PURE_RATE(ZD_OFDM_RATE_6M)] = 6, + [ZD_PURE_RATE(ZD_OFDM_RATE_9M)] = 9, + [ZD_PURE_RATE(ZD_OFDM_RATE_12M)] = 12, + [ZD_PURE_RATE(ZD_OFDM_RATE_18M)] = 18, + [ZD_PURE_RATE(ZD_OFDM_RATE_24M)] = 24, + [ZD_PURE_RATE(ZD_OFDM_RATE_36M)] = 36, + [ZD_PURE_RATE(ZD_OFDM_RATE_48M)] = 48, + [ZD_PURE_RATE(ZD_OFDM_RATE_54M)] = 54, + }; + + u32 bits = (u32)tx_length * 8; + u32 divisor; + + divisor = rate_divisor[ZD_PURE_RATE(zd_rate)]; + if (divisor == 0) + return -EINVAL; + + switch (zd_rate) { + case ZD_CCK_RATE_5_5M: + bits = (2*bits) + 10; /* round up to the next integer */ + break; + case ZD_CCK_RATE_11M: + if (service) { + u32 t = bits % 11; + *service &= ~ZD_PLCP_SERVICE_LENGTH_EXTENSION; + if (0 < t && t <= 3) { + *service |= ZD_PLCP_SERVICE_LENGTH_EXTENSION; + } + } + bits += 10; /* round up to the next integer */ + break; + } + + return bits/divisor; +} + +static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs, + struct ieee80211_hdr *header, + struct ieee80211_tx_info *info) +{ + /* + * CONTROL TODO: + * - if backoff needed, enable bit 0 + * - if burst (backoff not needed) disable bit 0 + */ + + cs->control = 0; + + /* First fragment */ + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + cs->control |= ZD_CS_NEED_RANDOM_BACKOFF; + + /* No ACK expected (multicast, etc.) */ + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + cs->control |= ZD_CS_NO_ACK; + + /* PS-POLL */ + if (ieee80211_is_pspoll(header->frame_control)) + cs->control |= ZD_CS_PS_POLL_FRAME; + + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) + cs->control |= ZD_CS_RTS; + + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) + cs->control |= ZD_CS_SELF_CTS; + + /* FIXME: Management frame? */ +} + +static bool zd_mac_match_cur_beacon(struct zd_mac *mac, struct sk_buff *beacon) +{ + if (!mac->beacon.cur_beacon) + return false; + + if (mac->beacon.cur_beacon->len != beacon->len) + return false; + + return !memcmp(beacon->data, mac->beacon.cur_beacon->data, beacon->len); +} + +static void zd_mac_free_cur_beacon_locked(struct zd_mac *mac) +{ + ZD_ASSERT(mutex_is_locked(&mac->chip.mutex)); + + kfree_skb(mac->beacon.cur_beacon); + mac->beacon.cur_beacon = NULL; +} + +static void zd_mac_free_cur_beacon(struct zd_mac *mac) +{ + mutex_lock(&mac->chip.mutex); + zd_mac_free_cur_beacon_locked(mac); + mutex_unlock(&mac->chip.mutex); +} + +static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon, + bool in_intr) +{ + struct zd_mac *mac = zd_hw_mac(hw); + int r, ret, num_cmds, req_pos = 0; + u32 tmp, j = 0; + /* 4 more bytes for tail CRC */ + u32 full_len = beacon->len + 4; + unsigned long end_jiffies, message_jiffies; + struct zd_ioreq32 *ioreqs; + + mutex_lock(&mac->chip.mutex); + + /* Check if hw already has this beacon. */ + if (zd_mac_match_cur_beacon(mac, beacon)) { + r = 0; + goto out_nofree; + } + + /* Alloc memory for full beacon write at once. */ + num_cmds = 1 + zd_chip_is_zd1211b(&mac->chip) + full_len; + ioreqs = kmalloc(num_cmds * sizeof(struct zd_ioreq32), GFP_KERNEL); + if (!ioreqs) { + r = -ENOMEM; + goto out_nofree; + } + + r = zd_iowrite32_locked(&mac->chip, 0, CR_BCN_FIFO_SEMAPHORE); + if (r < 0) + goto out; + r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE); + if (r < 0) + goto release_sema; + if (in_intr && tmp & 0x2) { + r = -EBUSY; + goto release_sema; + } + + end_jiffies = jiffies + HZ / 2; /*~500ms*/ + message_jiffies = jiffies + HZ / 10; /*~100ms*/ + while (tmp & 0x2) { + r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE); + if (r < 0) + goto release_sema; + if (time_is_before_eq_jiffies(message_jiffies)) { + message_jiffies = jiffies + HZ / 10; + dev_err(zd_mac_dev(mac), + "CR_BCN_FIFO_SEMAPHORE not ready\n"); + if (time_is_before_eq_jiffies(end_jiffies)) { + dev_err(zd_mac_dev(mac), + "Giving up beacon config.\n"); + r = -ETIMEDOUT; + goto reset_device; + } + } + msleep(20); + } + + ioreqs[req_pos].addr = CR_BCN_FIFO; + ioreqs[req_pos].value = full_len - 1; + req_pos++; + if (zd_chip_is_zd1211b(&mac->chip)) { + ioreqs[req_pos].addr = CR_BCN_LENGTH; + ioreqs[req_pos].value = full_len - 1; + req_pos++; + } + + for (j = 0 ; j < beacon->len; j++) { + ioreqs[req_pos].addr = CR_BCN_FIFO; + ioreqs[req_pos].value = *((u8 *)(beacon->data + j)); + req_pos++; + } + + for (j = 0; j < 4; j++) { + ioreqs[req_pos].addr = CR_BCN_FIFO; + ioreqs[req_pos].value = 0x0; + req_pos++; + } + + BUG_ON(req_pos != num_cmds); + + r = zd_iowrite32a_locked(&mac->chip, ioreqs, num_cmds); + +release_sema: + /* + * Try very hard to release device beacon semaphore, as otherwise + * device/driver can be left in unusable state. + */ + end_jiffies = jiffies + HZ / 2; /*~500ms*/ + ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE); + while (ret < 0) { + if (in_intr || time_is_before_eq_jiffies(end_jiffies)) { + ret = -ETIMEDOUT; + break; + } + + msleep(20); + ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE); + } + + if (ret < 0) + dev_err(zd_mac_dev(mac), "Could not release " + "CR_BCN_FIFO_SEMAPHORE!\n"); + if (r < 0 || ret < 0) { + if (r >= 0) + r = ret; + + /* We don't know if beacon was written successfully or not, + * so clear current. */ + zd_mac_free_cur_beacon_locked(mac); + + goto out; + } + + /* Beacon has now been written successfully, update current. */ + zd_mac_free_cur_beacon_locked(mac); + mac->beacon.cur_beacon = beacon; + beacon = NULL; + + /* 802.11b/g 2.4G CCK 1Mb + * 802.11a, not yet implemented, uses different values (see GPL vendor + * driver) + */ + r = zd_iowrite32_locked(&mac->chip, 0x00000400 | (full_len << 19), + CR_BCN_PLCP_CFG); +out: + kfree(ioreqs); +out_nofree: + kfree_skb(beacon); + mutex_unlock(&mac->chip.mutex); + + return r; + +reset_device: + zd_mac_free_cur_beacon_locked(mac); + kfree_skb(beacon); + + mutex_unlock(&mac->chip.mutex); + kfree(ioreqs); + + /* semaphore stuck, reset device to avoid fw freeze later */ + dev_warn(zd_mac_dev(mac), "CR_BCN_FIFO_SEMAPHORE stuck, " + "resetting device..."); + usb_queue_reset_device(mac->chip.usb.intf); + + return r; +} + +static int fill_ctrlset(struct zd_mac *mac, + struct sk_buff *skb) +{ + int r; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + unsigned int frag_len = skb->len + FCS_LEN; + unsigned int packet_length; + struct ieee80211_rate *txrate; + struct zd_ctrlset *cs = (struct zd_ctrlset *) + skb_push(skb, sizeof(struct zd_ctrlset)); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + ZD_ASSERT(frag_len <= 0xffff); + + /* + * Firmware computes the duration itself (for all frames except PSPoll) + * and needs the field set to 0 at input, otherwise firmware messes up + * duration_id and sets bits 14 and 15 on. + */ + if (!ieee80211_is_pspoll(hdr->frame_control)) + hdr->duration_id = 0; + + txrate = ieee80211_get_tx_rate(mac->hw, info); + + cs->modulation = txrate->hw_value; + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + cs->modulation = txrate->hw_value_short; + + cs->tx_length = cpu_to_le16(frag_len); + + cs_set_control(mac, cs, hdr, info); + + packet_length = frag_len + sizeof(struct zd_ctrlset) + 10; + ZD_ASSERT(packet_length <= 0xffff); + /* ZD1211B: Computing the length difference this way, gives us + * flexibility to compute the packet length. + */ + cs->packet_length = cpu_to_le16(zd_chip_is_zd1211b(&mac->chip) ? + packet_length - frag_len : packet_length); + + /* + * CURRENT LENGTH: + * - transmit frame length in microseconds + * - seems to be derived from frame length + * - see Cal_Us_Service() in zdinlinef.h + * - if macp->bTxBurstEnable is enabled, then multiply by 4 + * - bTxBurstEnable is never set in the vendor driver + * + * SERVICE: + * - "for PLCP configuration" + * - always 0 except in some situations at 802.11b 11M + * - see line 53 of zdinlinef.h + */ + cs->service = 0; + r = zd_calc_tx_length_us(&cs->service, ZD_RATE(cs->modulation), + le16_to_cpu(cs->tx_length)); + if (r < 0) + return r; + cs->current_length = cpu_to_le16(r); + cs->next_frame_length = 0; + + return 0; +} + +/** + * zd_op_tx - transmits a network frame to the device + * + * @dev: mac80211 hardware device + * @skb: socket buffer + * @control: the control structure + * + * This function transmit an IEEE 802.11 network frame to the device. The + * control block of the skbuff will be initialized. If necessary the incoming + * mac80211 queues will be stopped. + */ +static void zd_op_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + int r; + + r = fill_ctrlset(mac, skb); + if (r) + goto fail; + + info->rate_driver_data[0] = hw; + + r = zd_usb_tx(&mac->chip.usb, skb); + if (r) + goto fail; + return; + +fail: + dev_kfree_skb(skb); +} + +/** + * filter_ack - filters incoming packets for acknowledgements + * @dev: the mac80211 device + * @rx_hdr: received header + * @stats: the status for the received packet + * + * This functions looks for ACK packets and tries to match them with the + * frames in the tx queue. If a match is found the frame will be dequeued and + * the upper layers is informed about the successful transmission. If + * mac80211 queues have been stopped and the number of frames still to be + * transmitted is low the queues will be opened again. + * + * Returns 1 if the frame was an ACK, 0 if it was ignored. + */ +static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr, + struct ieee80211_rx_status *stats) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct sk_buff *skb; + struct sk_buff_head *q; + unsigned long flags; + int found = 0; + int i, position = 0; + + if (!ieee80211_is_ack(rx_hdr->frame_control)) + return 0; + + q = &mac->ack_wait_queue; + spin_lock_irqsave(&q->lock, flags); + skb_queue_walk(q, skb) { + struct ieee80211_hdr *tx_hdr; + + position ++; + + if (mac->ack_pending && skb_queue_is_first(q, skb)) + continue; + + tx_hdr = (struct ieee80211_hdr *)skb->data; + if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) + { + found = 1; + break; + } + } + + if (found) { + for (i=1; iack_pending ? mac->ack_signal : 0, + NULL); + mac->ack_pending = 0; + } + + mac->ack_pending = 1; + mac->ack_signal = stats->signal; + + /* Prevent pending tx-packet on AP-mode */ + if (mac->type == NL80211_IFTYPE_AP) { + skb = __skb_dequeue(q); + zd_mac_tx_status(hw, skb, mac->ack_signal, NULL); + mac->ack_pending = 0; + } + } + + spin_unlock_irqrestore(&q->lock, flags); + return 1; +} + +int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct ieee80211_rx_status stats; + const struct rx_status *status; + struct sk_buff *skb; + int bad_frame = 0; + __le16 fc; + int need_padding; + int i; + u8 rate; + + if (length < ZD_PLCP_HEADER_SIZE + 10 /* IEEE80211_1ADDR_LEN */ + + FCS_LEN + sizeof(struct rx_status)) + return -EINVAL; + + memset(&stats, 0, sizeof(stats)); + + /* Note about pass_failed_fcs and pass_ctrl access below: + * mac locking intentionally omitted here, as this is the only unlocked + * reader and the only writer is configure_filter. Plus, if there were + * any races accessing these variables, it wouldn't really matter. + * If mac80211 ever provides a way for us to access filter flags + * from outside configure_filter, we could improve on this. Also, this + * situation may change once we implement some kind of DMA-into-skb + * RX path. */ + + /* Caller has to ensure that length >= sizeof(struct rx_status). */ + status = (struct rx_status *) + (buffer + (length - sizeof(struct rx_status))); + if (status->frame_status & ZD_RX_ERROR) { + if (mac->pass_failed_fcs && + (status->frame_status & ZD_RX_CRC32_ERROR)) { + stats.flag |= RX_FLAG_FAILED_FCS_CRC; + bad_frame = 1; + } else { + return -EINVAL; + } + } + + stats.freq = zd_channels[_zd_chip_get_channel(&mac->chip) - 1].center_freq; + stats.band = IEEE80211_BAND_2GHZ; + stats.signal = zd_check_signal(hw, status->signal_strength); + + rate = zd_rx_rate(buffer, status); + + /* todo: return index in the big switches in zd_rx_rate instead */ + for (i = 0; i < mac->band.n_bitrates; i++) + if (rate == mac->band.bitrates[i].hw_value) + stats.rate_idx = i; + + length -= ZD_PLCP_HEADER_SIZE + sizeof(struct rx_status); + buffer += ZD_PLCP_HEADER_SIZE; + + /* Except for bad frames, filter each frame to see if it is an ACK, in + * which case our internal TX tracking is updated. Normally we then + * bail here as there's no need to pass ACKs on up to the stack, but + * there is also the case where the stack has requested us to pass + * control frames on up (pass_ctrl) which we must consider. */ + if (!bad_frame && + filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) + && !mac->pass_ctrl) + return 0; + + fc = get_unaligned((__le16*)buffer); + need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc); + + skb = dev_alloc_skb(length + (need_padding ? 2 : 0)); + if (skb == NULL) + return -ENOMEM; + if (need_padding) { + /* Make sure the payload data is 4 byte aligned. */ + skb_reserve(skb, 2); + } + + /* FIXME : could we avoid this big memcpy ? */ + memcpy(skb_put(skb, length), buffer, length); + + memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats)); + ieee80211_rx_irqsafe(hw, skb); + return 0; +} + +static int zd_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct zd_mac *mac = zd_hw_mac(hw); + + /* using NL80211_IFTYPE_UNSPECIFIED to indicate no mode selected */ + if (mac->type != NL80211_IFTYPE_UNSPECIFIED) + return -EOPNOTSUPP; + + switch (vif->type) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + mac->type = vif->type; + break; + default: + return -EOPNOTSUPP; + } + + mac->vif = vif; + + return set_mac_and_bssid(mac); +} + +static void zd_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct zd_mac *mac = zd_hw_mac(hw); + mac->type = NL80211_IFTYPE_UNSPECIFIED; + mac->vif = NULL; + zd_set_beacon_interval(&mac->chip, 0, 0, NL80211_IFTYPE_UNSPECIFIED); + zd_write_mac_addr(&mac->chip, NULL); + + zd_mac_free_cur_beacon(mac); +} + +static int zd_op_config(struct ieee80211_hw *hw, u32 changed) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct ieee80211_conf *conf = &hw->conf; + + spin_lock_irq(&mac->lock); + mac->channel = conf->chandef.chan->hw_value; + spin_unlock_irq(&mac->lock); + + return zd_chip_set_channel(&mac->chip, conf->chandef.chan->hw_value); +} + +static void zd_beacon_done(struct zd_mac *mac) +{ + struct sk_buff *skb, *beacon; + + if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) + return; + if (!mac->vif || mac->vif->type != NL80211_IFTYPE_AP) + return; + + /* + * Send out buffered broad- and multicast frames. + */ + while (!ieee80211_queue_stopped(mac->hw, 0)) { + skb = ieee80211_get_buffered_bc(mac->hw, mac->vif); + if (!skb) + break; + zd_op_tx(mac->hw, NULL, skb); + } + + /* + * Fetch next beacon so that tim_count is updated. + */ + beacon = ieee80211_beacon_get(mac->hw, mac->vif); + if (beacon) + zd_mac_config_beacon(mac->hw, beacon, true); + + spin_lock_irq(&mac->lock); + mac->beacon.last_update = jiffies; + spin_unlock_irq(&mac->lock); +} + +static void zd_process_intr(struct work_struct *work) +{ + u16 int_status; + unsigned long flags; + struct zd_mac *mac = container_of(work, struct zd_mac, process_intr); + + spin_lock_irqsave(&mac->lock, flags); + int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer + 4)); + spin_unlock_irqrestore(&mac->lock, flags); + + if (int_status & INT_CFG_NEXT_BCN) { + /*dev_dbg_f_limit(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n");*/ + zd_beacon_done(mac); + } else { + dev_dbg_f(zd_mac_dev(mac), "Unsupported interrupt\n"); + } + + zd_chip_enable_hwint(&mac->chip); +} + + +static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct zd_mc_hash hash; + struct netdev_hw_addr *ha; + + zd_mc_clear(&hash); + + netdev_hw_addr_list_for_each(ha, mc_list) { + dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", ha->addr); + zd_mc_add_addr(&hash, ha->addr); + } + + return hash.low | ((u64)hash.high << 32); +} + +#define SUPPORTED_FIF_FLAGS \ + (FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \ + FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC) +static void zd_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *new_flags, + u64 multicast) +{ + struct zd_mc_hash hash = { + .low = multicast, + .high = multicast >> 32, + }; + struct zd_mac *mac = zd_hw_mac(hw); + unsigned long flags; + int r; + + /* Only deal with supported flags */ + changed_flags &= SUPPORTED_FIF_FLAGS; + *new_flags &= SUPPORTED_FIF_FLAGS; + + /* + * If multicast parameter (as returned by zd_op_prepare_multicast) + * has changed, no bit in changed_flags is set. To handle this + * situation, we do not return if changed_flags is 0. If we do so, + * we will have some issue with IPv6 which uses multicast for link + * layer address resolution. + */ + if (*new_flags & FIF_ALLMULTI) + zd_mc_add_all(&hash); + + spin_lock_irqsave(&mac->lock, flags); + mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL); + mac->pass_ctrl = !!(*new_flags & FIF_CONTROL); + mac->multicast_hash = hash; + spin_unlock_irqrestore(&mac->lock, flags); + + zd_chip_set_multicast_hash(&mac->chip, &hash); + + if (changed_flags & FIF_CONTROL) { + r = set_rx_filter(mac); + if (r) + dev_err(zd_mac_dev(mac), "set_rx_filter error %d\n", r); + } + + /* no handling required for FIF_OTHER_BSS as we don't currently + * do BSSID filtering */ + /* FIXME: in future it would be nice to enable the probe response + * filter (so that the driver doesn't see them) until + * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd + * have to schedule work to enable prbresp reception, which might + * happen too late. For now we'll just listen and forward them all the + * time. */ +} + +static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble) +{ + mutex_lock(&mac->chip.mutex); + zd_chip_set_rts_cts_rate_locked(&mac->chip, short_preamble); + mutex_unlock(&mac->chip.mutex); +} + +static void zd_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct zd_mac *mac = zd_hw_mac(hw); + int associated; + + dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes); + + if (mac->type == NL80211_IFTYPE_MESH_POINT || + mac->type == NL80211_IFTYPE_ADHOC || + mac->type == NL80211_IFTYPE_AP) { + associated = true; + if (changes & BSS_CHANGED_BEACON) { + struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); + + if (beacon) { + zd_chip_disable_hwint(&mac->chip); + zd_mac_config_beacon(hw, beacon, false); + zd_chip_enable_hwint(&mac->chip); + } + } + + if (changes & BSS_CHANGED_BEACON_ENABLED) { + u16 interval = 0; + u8 period = 0; + + if (bss_conf->enable_beacon) { + period = bss_conf->dtim_period; + interval = bss_conf->beacon_int; + } + + spin_lock_irq(&mac->lock); + mac->beacon.period = period; + mac->beacon.interval = interval; + mac->beacon.last_update = jiffies; + spin_unlock_irq(&mac->lock); + + zd_set_beacon_interval(&mac->chip, interval, period, + mac->type); + } + } else + associated = is_valid_ether_addr(bss_conf->bssid); + + spin_lock_irq(&mac->lock); + mac->associated = associated; + spin_unlock_irq(&mac->lock); + + /* TODO: do hardware bssid filtering */ + + if (changes & BSS_CHANGED_ERP_PREAMBLE) { + spin_lock_irq(&mac->lock); + mac->short_preamble = bss_conf->use_short_preamble; + spin_unlock_irq(&mac->lock); + + set_rts_cts(mac, bss_conf->use_short_preamble); + } +} + +static u64 zd_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct zd_mac *mac = zd_hw_mac(hw); + return zd_chip_get_tsf(&mac->chip); +} + +static const struct ieee80211_ops zd_ops = { + .tx = zd_op_tx, + .start = zd_op_start, + .stop = zd_op_stop, + .add_interface = zd_op_add_interface, + .remove_interface = zd_op_remove_interface, + .config = zd_op_config, + .prepare_multicast = zd_op_prepare_multicast, + .configure_filter = zd_op_configure_filter, + .bss_info_changed = zd_op_bss_info_changed, + .get_tsf = zd_op_get_tsf, +}; + +struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf) +{ + struct zd_mac *mac; + struct ieee80211_hw *hw; + + hw = ieee80211_alloc_hw(sizeof(struct zd_mac), &zd_ops); + if (!hw) { + dev_dbg_f(&intf->dev, "out of memory\n"); + return NULL; + } + + mac = zd_hw_mac(hw); + + memset(mac, 0, sizeof(*mac)); + spin_lock_init(&mac->lock); + mac->hw = hw; + + mac->type = NL80211_IFTYPE_UNSPECIFIED; + + memcpy(mac->channels, zd_channels, sizeof(zd_channels)); + memcpy(mac->rates, zd_rates, sizeof(zd_rates)); + mac->band.n_bitrates = ARRAY_SIZE(zd_rates); + mac->band.bitrates = mac->rates; + mac->band.n_channels = ARRAY_SIZE(zd_channels); + mac->band.channels = mac->channels; + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band; + + ieee80211_hw_set(hw, MFP_CAPABLE); + ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); + ieee80211_hw_set(hw, RX_INCLUDES_FCS); + ieee80211_hw_set(hw, SIGNAL_UNSPEC); + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP); + + hw->max_signal = 100; + hw->queues = 1; + hw->extra_tx_headroom = sizeof(struct zd_ctrlset); + + /* + * Tell mac80211 that we support multi rate retries + */ + hw->max_rates = IEEE80211_TX_MAX_RATES; + hw->max_rate_tries = 18; /* 9 rates * 2 retries/rate */ + + skb_queue_head_init(&mac->ack_wait_queue); + mac->ack_pending = 0; + + zd_chip_init(&mac->chip, hw, intf); + housekeeping_init(mac); + beacon_init(mac); + INIT_WORK(&mac->process_intr, zd_process_intr); + + SET_IEEE80211_DEV(hw, &intf->dev); + return hw; +} + +#define BEACON_WATCHDOG_DELAY round_jiffies_relative(HZ) + +static void beacon_watchdog_handler(struct work_struct *work) +{ + struct zd_mac *mac = + container_of(work, struct zd_mac, beacon.watchdog_work.work); + struct sk_buff *beacon; + unsigned long timeout; + int interval, period; + + if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) + goto rearm; + if (mac->type != NL80211_IFTYPE_AP || !mac->vif) + goto rearm; + + spin_lock_irq(&mac->lock); + interval = mac->beacon.interval; + period = mac->beacon.period; + timeout = mac->beacon.last_update + + msecs_to_jiffies(interval * 1024 / 1000) * 3; + spin_unlock_irq(&mac->lock); + + if (interval > 0 && time_is_before_jiffies(timeout)) { + dev_dbg_f(zd_mac_dev(mac), "beacon interrupt stalled, " + "restarting. " + "(interval: %d, dtim: %d)\n", + interval, period); + + zd_chip_disable_hwint(&mac->chip); + + beacon = ieee80211_beacon_get(mac->hw, mac->vif); + if (beacon) { + zd_mac_free_cur_beacon(mac); + + zd_mac_config_beacon(mac->hw, beacon, false); + } + + zd_set_beacon_interval(&mac->chip, interval, period, mac->type); + + zd_chip_enable_hwint(&mac->chip); + + spin_lock_irq(&mac->lock); + mac->beacon.last_update = jiffies; + spin_unlock_irq(&mac->lock); + } + +rearm: + queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work, + BEACON_WATCHDOG_DELAY); +} + +static void beacon_init(struct zd_mac *mac) +{ + INIT_DELAYED_WORK(&mac->beacon.watchdog_work, beacon_watchdog_handler); +} + +static void beacon_enable(struct zd_mac *mac) +{ + dev_dbg_f(zd_mac_dev(mac), "\n"); + + mac->beacon.last_update = jiffies; + queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work, + BEACON_WATCHDOG_DELAY); +} + +static void beacon_disable(struct zd_mac *mac) +{ + dev_dbg_f(zd_mac_dev(mac), "\n"); + cancel_delayed_work_sync(&mac->beacon.watchdog_work); + + zd_mac_free_cur_beacon(mac); +} + +#define LINK_LED_WORK_DELAY HZ + +static void link_led_handler(struct work_struct *work) +{ + struct zd_mac *mac = + container_of(work, struct zd_mac, housekeeping.link_led_work.work); + struct zd_chip *chip = &mac->chip; + int is_associated; + int r; + + if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) + goto requeue; + + spin_lock_irq(&mac->lock); + is_associated = mac->associated; + spin_unlock_irq(&mac->lock); + + r = zd_chip_control_leds(chip, + is_associated ? ZD_LED_ASSOCIATED : ZD_LED_SCANNING); + if (r) + dev_dbg_f(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r); + +requeue: + queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work, + LINK_LED_WORK_DELAY); +} + +static void housekeeping_init(struct zd_mac *mac) +{ + INIT_DELAYED_WORK(&mac->housekeeping.link_led_work, link_led_handler); +} + +static void housekeeping_enable(struct zd_mac *mac) +{ + dev_dbg_f(zd_mac_dev(mac), "\n"); + queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work, + 0); +} + +static void housekeeping_disable(struct zd_mac *mac) +{ + dev_dbg_f(zd_mac_dev(mac), "\n"); + cancel_delayed_work_sync(&mac->housekeeping.link_led_work); + zd_chip_control_leds(&mac->chip, ZD_LED_OFF); +} diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.h b/drivers/net/wireless/zydas/zd1211rw/zd_mac.h new file mode 100644 index 000000000000..5a484235308f --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.h @@ -0,0 +1,327 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _ZD_MAC_H +#define _ZD_MAC_H + +#include +#include + +#include "zd_chip.h" + +struct zd_ctrlset { + u8 modulation; + __le16 tx_length; + u8 control; + /* stores only the difference to tx_length on ZD1211B */ + __le16 packet_length; + __le16 current_length; + u8 service; + __le16 next_frame_length; +} __packed; + +#define ZD_CS_RESERVED_SIZE 25 + +/* The field modulation of struct zd_ctrlset controls the bit rate, the use + * of short or long preambles in 802.11b (CCK mode) or the use of 802.11a or + * 802.11g in OFDM mode. + * + * The term zd-rate is used for the combination of the modulation type flag + * and the "pure" rate value. + */ +#define ZD_PURE_RATE_MASK 0x0f +#define ZD_MODULATION_TYPE_MASK 0x10 +#define ZD_RATE_MASK (ZD_PURE_RATE_MASK|ZD_MODULATION_TYPE_MASK) +#define ZD_PURE_RATE(modulation) ((modulation) & ZD_PURE_RATE_MASK) +#define ZD_MODULATION_TYPE(modulation) ((modulation) & ZD_MODULATION_TYPE_MASK) +#define ZD_RATE(modulation) ((modulation) & ZD_RATE_MASK) + +/* The two possible modulation types. Notify that 802.11b doesn't use the CCK + * codeing for the 1 and 2 MBit/s rate. We stay with the term here to remain + * consistent with uses the term at other places. + */ +#define ZD_CCK 0x00 +#define ZD_OFDM 0x10 + +/* The ZD1211 firmware uses proprietary encodings of the 802.11b (CCK) rates. + * For OFDM the PLCP rate encodings are used. We combine these "pure" rates + * with the modulation type flag and call the resulting values zd-rates. + */ +#define ZD_CCK_RATE_1M (ZD_CCK|0x00) +#define ZD_CCK_RATE_2M (ZD_CCK|0x01) +#define ZD_CCK_RATE_5_5M (ZD_CCK|0x02) +#define ZD_CCK_RATE_11M (ZD_CCK|0x03) +#define ZD_OFDM_RATE_6M (ZD_OFDM|ZD_OFDM_PLCP_RATE_6M) +#define ZD_OFDM_RATE_9M (ZD_OFDM|ZD_OFDM_PLCP_RATE_9M) +#define ZD_OFDM_RATE_12M (ZD_OFDM|ZD_OFDM_PLCP_RATE_12M) +#define ZD_OFDM_RATE_18M (ZD_OFDM|ZD_OFDM_PLCP_RATE_18M) +#define ZD_OFDM_RATE_24M (ZD_OFDM|ZD_OFDM_PLCP_RATE_24M) +#define ZD_OFDM_RATE_36M (ZD_OFDM|ZD_OFDM_PLCP_RATE_36M) +#define ZD_OFDM_RATE_48M (ZD_OFDM|ZD_OFDM_PLCP_RATE_48M) +#define ZD_OFDM_RATE_54M (ZD_OFDM|ZD_OFDM_PLCP_RATE_54M) + +/* The bit 5 of the zd_ctrlset modulation field controls the preamble in CCK + * mode or the 802.11a/802.11g selection in OFDM mode. + */ +#define ZD_CCK_PREA_LONG 0x00 +#define ZD_CCK_PREA_SHORT 0x20 +#define ZD_OFDM_MODE_11G 0x00 +#define ZD_OFDM_MODE_11A 0x20 + +/* zd_ctrlset control field */ +#define ZD_CS_NEED_RANDOM_BACKOFF 0x01 +#define ZD_CS_NO_ACK 0x02 + +#define ZD_CS_FRAME_TYPE_MASK 0x0c +#define ZD_CS_DATA_FRAME 0x00 +#define ZD_CS_PS_POLL_FRAME 0x04 +#define ZD_CS_MANAGEMENT_FRAME 0x08 +#define ZD_CS_NO_SEQUENCE_CTL_FRAME 0x0c + +#define ZD_CS_WAKE_DESTINATION 0x10 +#define ZD_CS_RTS 0x20 +#define ZD_CS_ENCRYPT 0x40 +#define ZD_CS_SELF_CTS 0x80 + +/* Incoming frames are prepended by a PLCP header */ +#define ZD_PLCP_HEADER_SIZE 5 + +struct rx_length_info { + __le16 length[3]; + __le16 tag; +} __packed; + +#define RX_LENGTH_INFO_TAG 0x697e + +struct rx_status { + u8 signal_quality_cck; + /* rssi */ + u8 signal_strength; + u8 signal_quality_ofdm; + u8 decryption_type; + u8 frame_status; +} __packed; + +/* rx_status field decryption_type */ +#define ZD_RX_NO_WEP 0 +#define ZD_RX_WEP64 1 +#define ZD_RX_TKIP 2 +#define ZD_RX_AES 4 +#define ZD_RX_WEP128 5 +#define ZD_RX_WEP256 6 + +/* rx_status field frame_status */ +#define ZD_RX_FRAME_MODULATION_MASK 0x01 +#define ZD_RX_CCK 0x00 +#define ZD_RX_OFDM 0x01 + +#define ZD_RX_TIMEOUT_ERROR 0x02 +#define ZD_RX_FIFO_OVERRUN_ERROR 0x04 +#define ZD_RX_DECRYPTION_ERROR 0x08 +#define ZD_RX_CRC32_ERROR 0x10 +#define ZD_RX_NO_ADDR1_MATCH_ERROR 0x20 +#define ZD_RX_CRC16_ERROR 0x40 +#define ZD_RX_ERROR 0x80 + +struct tx_retry_rate { + int count; /* number of valid element in rate[] array */ + int rate[10]; /* retry rates, described by an index in zd_rates[] */ +}; + +struct tx_status { + u8 type; /* must always be 0x01 : USB_INT_TYPE */ + u8 id; /* must always be 0xa0 : USB_INT_ID_RETRY_FAILED */ + u8 rate; + u8 pad; + u8 mac[ETH_ALEN]; + u8 retry; + u8 failure; +} __packed; + +enum mac_flags { + MAC_FIXED_CHANNEL = 0x01, +}; + +struct housekeeping { + struct delayed_work link_led_work; +}; + +struct beacon { + struct delayed_work watchdog_work; + struct sk_buff *cur_beacon; + unsigned long last_update; + u16 interval; + u8 period; +}; + +enum zd_device_flags { + ZD_DEVICE_RUNNING, +}; + +#define ZD_MAC_STATS_BUFFER_SIZE 16 + +#define ZD_MAC_MAX_ACK_WAITERS 50 + +struct zd_mac { + struct zd_chip chip; + spinlock_t lock; + spinlock_t intr_lock; + struct ieee80211_hw *hw; + struct ieee80211_vif *vif; + struct housekeeping housekeeping; + struct beacon beacon; + struct work_struct set_rts_cts_work; + struct work_struct process_intr; + struct zd_mc_hash multicast_hash; + u8 intr_buffer[USB_MAX_EP_INT_BUFFER]; + u8 regdomain; + u8 default_regdomain; + u8 channel; + int type; + int associated; + unsigned long flags; + struct sk_buff_head ack_wait_queue; + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_supported_band band; + + /* Short preamble (used for RTS/CTS) */ + unsigned int short_preamble:1; + + /* whether to pass frames with CRC errors to stack */ + unsigned int pass_failed_fcs:1; + + /* whether to pass control frames to stack */ + unsigned int pass_ctrl:1; + + /* whether we have received a 802.11 ACK that is pending */ + unsigned int ack_pending:1; + + /* signal strength of the last 802.11 ACK received */ + int ack_signal; +}; + +#define ZD_REGDOMAIN_FCC 0x10 +#define ZD_REGDOMAIN_IC 0x20 +#define ZD_REGDOMAIN_ETSI 0x30 +#define ZD_REGDOMAIN_SPAIN 0x31 +#define ZD_REGDOMAIN_FRANCE 0x32 +#define ZD_REGDOMAIN_JAPAN_2 0x40 +#define ZD_REGDOMAIN_JAPAN 0x41 +#define ZD_REGDOMAIN_JAPAN_3 0x49 + +enum { + MIN_CHANNEL24 = 1, + MAX_CHANNEL24 = 14, +}; + +#define ZD_PLCP_SERVICE_LENGTH_EXTENSION 0x80 + +struct ofdm_plcp_header { + u8 prefix[3]; + __le16 service; +} __packed; + +static inline u8 zd_ofdm_plcp_header_rate(const struct ofdm_plcp_header *header) +{ + return header->prefix[0] & 0xf; +} + +/* The following defines give the encoding of the 4-bit rate field in the + * OFDM (802.11a/802.11g) PLCP header. Notify that these values are used to + * define the zd-rate values for OFDM. + * + * See the struct zd_ctrlset definition in zd_mac.h. + */ +#define ZD_OFDM_PLCP_RATE_6M 0xb +#define ZD_OFDM_PLCP_RATE_9M 0xf +#define ZD_OFDM_PLCP_RATE_12M 0xa +#define ZD_OFDM_PLCP_RATE_18M 0xe +#define ZD_OFDM_PLCP_RATE_24M 0x9 +#define ZD_OFDM_PLCP_RATE_36M 0xd +#define ZD_OFDM_PLCP_RATE_48M 0x8 +#define ZD_OFDM_PLCP_RATE_54M 0xc + +struct cck_plcp_header { + u8 signal; + u8 service; + __le16 length; + __le16 crc16; +} __packed; + +static inline u8 zd_cck_plcp_header_signal(const struct cck_plcp_header *header) +{ + return header->signal; +} + +/* These defines give the encodings of the signal field in the 802.11b PLCP + * header. The signal field gives the bit rate of the following packet. Even + * if technically wrong we use CCK here also for the 1 MBit/s and 2 MBit/s + * rate to stay consistent with Zydas and our use of the term. + * + * Notify that these values are *not* used in the zd-rates. + */ +#define ZD_CCK_PLCP_SIGNAL_1M 0x0a +#define ZD_CCK_PLCP_SIGNAL_2M 0x14 +#define ZD_CCK_PLCP_SIGNAL_5M5 0x37 +#define ZD_CCK_PLCP_SIGNAL_11M 0x6e + +static inline struct zd_mac *zd_hw_mac(struct ieee80211_hw *hw) +{ + return hw->priv; +} + +static inline struct zd_mac *zd_chip_to_mac(struct zd_chip *chip) +{ + return container_of(chip, struct zd_mac, chip); +} + +static inline struct zd_mac *zd_usb_to_mac(struct zd_usb *usb) +{ + return zd_chip_to_mac(zd_usb_to_chip(usb)); +} + +static inline u8 *zd_mac_get_perm_addr(struct zd_mac *mac) +{ + return mac->hw->wiphy->perm_addr; +} + +#define zd_mac_dev(mac) (zd_chip_dev(&(mac)->chip)) + +struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf); +void zd_mac_clear(struct zd_mac *mac); + +int zd_mac_preinit_hw(struct ieee80211_hw *hw); +int zd_mac_init_hw(struct ieee80211_hw *hw); + +int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length); +void zd_mac_tx_failed(struct urb *urb); +void zd_mac_tx_to_dev(struct sk_buff *skb, int error); + +int zd_op_start(struct ieee80211_hw *hw); +void zd_op_stop(struct ieee80211_hw *hw); +int zd_restore_settings(struct zd_mac *mac); + +#ifdef DEBUG +void zd_dump_rx_status(const struct rx_status *status); +#else +#define zd_dump_rx_status(status) +#endif /* DEBUG */ + +#endif /* _ZD_MAC_H */ diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf.c new file mode 100644 index 000000000000..dc179c414518 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf.c @@ -0,0 +1,181 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include + +#include "zd_def.h" +#include "zd_rf.h" +#include "zd_mac.h" +#include "zd_chip.h" + +static const char * const rfs[] = { + [0] = "unknown RF0", + [1] = "unknown RF1", + [UW2451_RF] = "UW2451_RF", + [UCHIP_RF] = "UCHIP_RF", + [AL2230_RF] = "AL2230_RF", + [AL7230B_RF] = "AL7230B_RF", + [THETA_RF] = "THETA_RF", + [AL2210_RF] = "AL2210_RF", + [MAXIM_NEW_RF] = "MAXIM_NEW_RF", + [UW2453_RF] = "UW2453_RF", + [AL2230S_RF] = "AL2230S_RF", + [RALINK_RF] = "RALINK_RF", + [INTERSIL_RF] = "INTERSIL_RF", + [RF2959_RF] = "RF2959_RF", + [MAXIM_NEW2_RF] = "MAXIM_NEW2_RF", + [PHILIPS_RF] = "PHILIPS_RF", +}; + +const char *zd_rf_name(u8 type) +{ + if (type & 0xf0) + type = 0; + return rfs[type]; +} + +void zd_rf_init(struct zd_rf *rf) +{ + memset(rf, 0, sizeof(*rf)); + + /* default to update channel integration, as almost all RF's do want + * this */ + rf->update_channel_int = 1; +} + +void zd_rf_clear(struct zd_rf *rf) +{ + if (rf->clear) + rf->clear(rf); + ZD_MEMCLEAR(rf, sizeof(*rf)); +} + +int zd_rf_init_hw(struct zd_rf *rf, u8 type) +{ + int r = 0; + int t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + switch (type) { + case RF2959_RF: + r = zd_rf_init_rf2959(rf); + break; + case AL2230_RF: + case AL2230S_RF: + r = zd_rf_init_al2230(rf); + break; + case AL7230B_RF: + r = zd_rf_init_al7230b(rf); + break; + case MAXIM_NEW_RF: + case UW2453_RF: + r = zd_rf_init_uw2453(rf); + break; + default: + dev_err(zd_chip_dev(chip), + "RF %s %#x is not supported\n", zd_rf_name(type), type); + rf->type = 0; + return -ENODEV; + } + + if (r) + return r; + + rf->type = type; + + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->init_hw(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} + +int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size) +{ + return scnprintf(buffer, size, "%s", zd_rf_name(rf->type)); +} + +int zd_rf_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + + ZD_ASSERT(mutex_is_locked(&zd_rf_to_chip(rf)->mutex)); + if (channel < MIN_CHANNEL24) + return -EINVAL; + if (channel > MAX_CHANNEL24) + return -EINVAL; + dev_dbg_f(zd_chip_dev(zd_rf_to_chip(rf)), "channel: %d\n", channel); + + r = rf->set_channel(rf, channel); + if (r >= 0) + rf->channel = channel; + return r; +} + +int zd_switch_radio_on(struct zd_rf *rf) +{ + int r, t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->switch_radio_on(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} + +int zd_switch_radio_off(struct zd_rf *rf) +{ + int r, t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + /* TODO: move phy regs handling to zd_chip */ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->switch_radio_off(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} + +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel) +{ + if (!rf->patch_6m_band_edge) + return 0; + + return rf->patch_6m_band_edge(rf, channel); +} + +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel) +{ + return zd_chip_generic_patch_6m_band(zd_rf_to_chip(rf), channel); +} + diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf.h b/drivers/net/wireless/zydas/zd1211rw/zd_rf.h new file mode 100644 index 000000000000..8f14e25e1041 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf.h @@ -0,0 +1,110 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _ZD_RF_H +#define _ZD_RF_H + +#define UW2451_RF 0x2 +#define UCHIP_RF 0x3 +#define AL2230_RF 0x4 +#define AL7230B_RF 0x5 /* a,b,g */ +#define THETA_RF 0x6 +#define AL2210_RF 0x7 +#define MAXIM_NEW_RF 0x8 +#define UW2453_RF 0x9 +#define AL2230S_RF 0xa +#define RALINK_RF 0xb +#define INTERSIL_RF 0xc +#define RF2959_RF 0xd +#define MAXIM_NEW2_RF 0xe +#define PHILIPS_RF 0xf + +#define RF_CHANNEL(ch) [(ch)-1] + +/* Provides functions of the RF transceiver. */ + +enum { + RF_REG_BITS = 6, + RF_VALUE_BITS = 18, + RF_RV_BITS = RF_REG_BITS + RF_VALUE_BITS, +}; + +struct zd_rf { + u8 type; + + u8 channel; + + /* whether channel integration and calibration should be updated + * defaults to 1 (yes) */ + u8 update_channel_int:1; + + /* whether ZD_CR47 should be patched from the EEPROM, if the appropriate + * flag is set in the POD. The vendor driver suggests that this should + * be done for all RF's, but a bug in their code prevents but their + * HW_OverWritePhyRegFromE2P() routine from ever taking effect. */ + u8 patch_cck_gain:1; + + /* private RF driver data */ + void *priv; + + /* RF-specific functions */ + int (*init_hw)(struct zd_rf *rf); + int (*set_channel)(struct zd_rf *rf, u8 channel); + int (*switch_radio_on)(struct zd_rf *rf); + int (*switch_radio_off)(struct zd_rf *rf); + int (*patch_6m_band_edge)(struct zd_rf *rf, u8 channel); + void (*clear)(struct zd_rf *rf); +}; + +const char *zd_rf_name(u8 type); +void zd_rf_init(struct zd_rf *rf); +void zd_rf_clear(struct zd_rf *rf); +int zd_rf_init_hw(struct zd_rf *rf, u8 type); + +int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size); + +int zd_rf_set_channel(struct zd_rf *rf, u8 channel); + +int zd_switch_radio_on(struct zd_rf *rf); +int zd_switch_radio_off(struct zd_rf *rf); + +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel); +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel); + +static inline int zd_rf_should_update_pwr_int(struct zd_rf *rf) +{ + return rf->update_channel_int; +} + +static inline int zd_rf_should_patch_cck_gain(struct zd_rf *rf) +{ + return rf->patch_cck_gain; +} + +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel); +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel); + +/* Functions for individual RF chips */ + +int zd_rf_init_rf2959(struct zd_rf *rf); +int zd_rf_init_al2230(struct zd_rf *rf); +int zd_rf_init_al7230b(struct zd_rf *rf); +int zd_rf_init_uw2453(struct zd_rf *rf); + +#endif /* _ZD_RF_H */ diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c new file mode 100644 index 000000000000..99aed7d78952 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c @@ -0,0 +1,443 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +#define IS_AL2230S(chip) ((chip)->al2230s_bit || (chip)->rf.type == AL2230S_RF) + +static const u32 zd1211_al2230_table[][3] = { + RF_CHANNEL( 1) = { 0x03f790, 0x033331, 0x00000d, }, + RF_CHANNEL( 2) = { 0x03f790, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 3) = { 0x03e790, 0x033331, 0x00000d, }, + RF_CHANNEL( 4) = { 0x03e790, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 5) = { 0x03f7a0, 0x033331, 0x00000d, }, + RF_CHANNEL( 6) = { 0x03f7a0, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 7) = { 0x03e7a0, 0x033331, 0x00000d, }, + RF_CHANNEL( 8) = { 0x03e7a0, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 9) = { 0x03f7b0, 0x033331, 0x00000d, }, + RF_CHANNEL(10) = { 0x03f7b0, 0x0b3331, 0x00000d, }, + RF_CHANNEL(11) = { 0x03e7b0, 0x033331, 0x00000d, }, + RF_CHANNEL(12) = { 0x03e7b0, 0x0b3331, 0x00000d, }, + RF_CHANNEL(13) = { 0x03f7c0, 0x033331, 0x00000d, }, + RF_CHANNEL(14) = { 0x03e7c0, 0x066661, 0x00000d, }, +}; + +static const u32 zd1211b_al2230_table[][3] = { + RF_CHANNEL( 1) = { 0x09efc0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL( 2) = { 0x09efc0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL( 3) = { 0x09e7c0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL( 4) = { 0x09e7c0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL( 5) = { 0x05efc0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL( 6) = { 0x05efc0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL( 7) = { 0x05e7c0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL( 8) = { 0x05e7c0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL( 9) = { 0x0defc0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL(10) = { 0x0defc0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL(11) = { 0x0de7c0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL(12) = { 0x0de7c0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL(13) = { 0x03efc0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL(14) = { 0x03e7c0, 0x866660, 0xb00000, }, +}; + +static const struct zd_ioreq16 zd1211b_ioreqs_shared_1[] = { + { ZD_CR240, 0x57 }, { ZD_CR9, 0xe0 }, +}; + +static const struct zd_ioreq16 ioreqs_init_al2230s[] = { + { ZD_CR47, 0x1e }, /* MARK_002 */ + { ZD_CR106, 0x22 }, + { ZD_CR107, 0x2a }, /* MARK_002 */ + { ZD_CR109, 0x13 }, /* MARK_002 */ + { ZD_CR118, 0xf8 }, /* MARK_002 */ + { ZD_CR119, 0x12 }, { ZD_CR122, 0xe0 }, + { ZD_CR128, 0x10 }, /* MARK_001 from 0xe->0x10 */ + { ZD_CR129, 0x0e }, /* MARK_001 from 0xd->0x0e */ + { ZD_CR130, 0x10 }, /* MARK_001 from 0xb->0x0d */ +}; + +static int zd1211b_al2230_finalize_rf(struct zd_chip *chip) +{ + int r; + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 }, + { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 }, + { ZD_CR203, 0x06 }, + { }, + + { ZD_CR240, 0x80 }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + /* related to antenna selection? */ + if (chip->new_phy_layout) { + r = zd_iowrite16_locked(chip, 0xe1, ZD_CR9); + if (r) + return r; + } + + return zd_iowrite16_locked(chip, 0x06, ZD_CR203); +} + +static int zd1211_al2230_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs_init[] = { + { ZD_CR15, 0x20 }, { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, + { ZD_CR26, 0x11 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, + { ZD_CR44, 0x33 }, { ZD_CR106, 0x2a }, { ZD_CR107, 0x1a }, + { ZD_CR109, 0x09 }, { ZD_CR110, 0x27 }, { ZD_CR111, 0x2b }, + { ZD_CR112, 0x2b }, { ZD_CR119, 0x0a }, { ZD_CR10, 0x89 }, + /* for newest (3rd cut) AL2300 */ + { ZD_CR17, 0x28 }, + { ZD_CR26, 0x93 }, { ZD_CR34, 0x30 }, + /* for newest (3rd cut) AL2300 */ + { ZD_CR35, 0x3e }, + { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, + /* for newest (3rd cut) AL2300 */ + { ZD_CR46, 0x96 }, + { ZD_CR47, 0x1e }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, + { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, + { ZD_CR92, 0x0a }, { ZD_CR99, 0x28 }, { ZD_CR100, 0x00 }, + { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR106, 0x24 }, + { ZD_CR107, 0x2a }, { ZD_CR109, 0x09 }, { ZD_CR110, 0x13 }, + { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, + { ZD_CR114, 0x27 }, + /* for newest (3rd cut) AL2300 */ + { ZD_CR115, 0x24 }, + { ZD_CR116, 0x24 }, { ZD_CR117, 0xf4 }, { ZD_CR118, 0xfc }, + { ZD_CR119, 0x10 }, { ZD_CR120, 0x4f }, { ZD_CR121, 0x77 }, + { ZD_CR122, 0xe0 }, { ZD_CR137, 0x88 }, { ZD_CR252, 0xff }, + { ZD_CR253, 0xff }, + }; + + static const struct zd_ioreq16 ioreqs_pll[] = { + /* shdnb(PLL_ON)=0 */ + { ZD_CR251, 0x2f }, + /* shdnb(PLL_ON)=1 */ + { ZD_CR251, 0x3f }, + { ZD_CR138, 0x28 }, { ZD_CR203, 0x06 }, + }; + + static const u32 rv1[] = { + /* Channel 1 */ + 0x03f790, + 0x033331, + 0x00000d, + + 0x0b3331, + 0x03b812, + 0x00fff3, + }; + + static const u32 rv2[] = { + 0x000da4, + 0x0f4dc5, /* fix freq shift, 0x04edc5 */ + 0x0805b6, + 0x011687, + 0x000688, + 0x0403b9, /* external control TX power (ZD_CR31) */ + 0x00dbba, + 0x00099b, + 0x0bdffc, + 0x00000d, + 0x00500f, + }; + + static const u32 rv3[] = { + 0x00d00f, + 0x004c0f, + 0x00540f, + 0x00700f, + 0x00500f, + }; + + r = zd_iowrite16a_locked(chip, ioreqs_init, ARRAY_SIZE(ioreqs_init)); + if (r) + return r; + + if (IS_AL2230S(chip)) { + r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, + ARRAY_SIZE(ioreqs_init_al2230s)); + if (r) + return r; + } + + r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS); + if (r) + return r; + + /* improve band edge for AL2230S */ + if (IS_AL2230S(chip)) + r = zd_rfwrite_locked(chip, 0x000824, RF_RV_BITS); + else + r = zd_rfwrite_locked(chip, 0x0005a4, RF_RV_BITS); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_pll, ARRAY_SIZE(ioreqs_pll)); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS); + if (r) + return r; + + return 0; +} + +static int zd1211b_al2230_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs1[] = { + { ZD_CR10, 0x89 }, { ZD_CR15, 0x20 }, + { ZD_CR17, 0x2B }, /* for newest(3rd cut) AL2230 */ + { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 }, + { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, + { ZD_CR33, 0x28 }, /* 5621 */ + { ZD_CR34, 0x30 }, + { ZD_CR35, 0x3e }, /* for newest(3rd cut) AL2230 */ + { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, + { ZD_CR46, 0x99 }, /* for newest(3rd cut) AL2230 */ + { ZD_CR47, 0x1e }, + + /* ZD1211B 05.06.10 */ + { ZD_CR48, 0x06 }, { ZD_CR49, 0xf9 }, { ZD_CR51, 0x01 }, + { ZD_CR52, 0x80 }, { ZD_CR53, 0x7e }, { ZD_CR65, 0x00 }, + { ZD_CR66, 0x00 }, { ZD_CR67, 0x00 }, { ZD_CR68, 0x00 }, + { ZD_CR69, 0x28 }, + + { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, + { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, + { ZD_CR91, 0x00 }, /* 5621 */ + { ZD_CR92, 0x0a }, + { ZD_CR98, 0x8d }, /* 4804, for 1212 new algorithm */ + { ZD_CR99, 0x00 }, /* 5621 */ + { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, + { ZD_CR106, 0x24 }, /* for newest(3rd cut) AL2230 */ + { ZD_CR107, 0x2a }, + { ZD_CR109, 0x13 }, /* 4804, for 1212 new algorithm */ + { ZD_CR110, 0x1f }, /* 4804, for 1212 new algorithm */ + { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, + { ZD_CR114, 0x27 }, + { ZD_CR115, 0x26 }, /* 24->26 at 4902 for newest(3rd cut) + * AL2230 + */ + { ZD_CR116, 0x24 }, + { ZD_CR117, 0xfa }, /* for 1211b */ + { ZD_CR118, 0xfa }, /* for 1211b */ + { ZD_CR119, 0x10 }, + { ZD_CR120, 0x4f }, + { ZD_CR121, 0x6c }, /* for 1211b */ + { ZD_CR122, 0xfc }, /* E0->FC at 4902 */ + { ZD_CR123, 0x57 }, /* 5623 */ + { ZD_CR125, 0xad }, /* 4804, for 1212 new algorithm */ + { ZD_CR126, 0x6c }, /* 5614 */ + { ZD_CR127, 0x03 }, /* 4804, for 1212 new algorithm */ + { ZD_CR137, 0x50 }, /* 5614 */ + { ZD_CR138, 0xa8 }, + { ZD_CR144, 0xac }, /* 5621 */ + { ZD_CR150, 0x0d }, { ZD_CR252, 0x34 }, { ZD_CR253, 0x34 }, + }; + + static const u32 rv1[] = { + 0x8cccd0, + 0x481dc0, + 0xcfff00, + 0x25a000, + }; + + static const u32 rv2[] = { + /* To improve AL2230 yield, improve phase noise, 4713 */ + 0x25a000, + 0xa3b2f0, + + 0x6da010, /* Reg6 update for MP versio */ + 0xe36280, /* Modified by jxiao for Bor-Chin on 2004/08/02 */ + 0x116000, + 0x9dc020, /* External control TX power (ZD_CR31) */ + 0x5ddb00, /* RegA update for MP version */ + 0xd99000, /* RegB update for MP version */ + 0x3ffbd0, /* RegC update for MP version */ + 0xb00000, /* RegD update for MP version */ + + /* improve phase noise and remove phase calibration,4713 */ + 0xf01a00, + }; + + static const struct zd_ioreq16 ioreqs2[] = { + { ZD_CR251, 0x2f }, /* shdnb(PLL_ON)=0 */ + { ZD_CR251, 0x7f }, /* shdnb(PLL_ON)=1 */ + }; + + static const u32 rv3[] = { + /* To improve AL2230 yield, 4713 */ + 0xf01b00, + 0xf01e00, + 0xf01a00, + }; + + static const struct zd_ioreq16 ioreqs3[] = { + /* related to 6M band edge patching, happens unconditionally */ + { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, + }; + + r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1, + ARRAY_SIZE(zd1211b_ioreqs_shared_1)); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1)); + if (r) + return r; + + if (IS_AL2230S(chip)) { + r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, + ARRAY_SIZE(ioreqs_init_al2230s)); + if (r) + return r; + } + + r = zd_rfwritev_cr_locked(chip, zd1211b_al2230_table[0], 3); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, rv1, ARRAY_SIZE(rv1)); + if (r) + return r; + + if (IS_AL2230S(chip)) + r = zd_rfwrite_locked(chip, 0x241000, RF_RV_BITS); + else + r = zd_rfwrite_locked(chip, 0x25a000, RF_RV_BITS); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv2, ARRAY_SIZE(rv2)); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, rv3, ARRAY_SIZE(rv3)); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3)); + if (r) + return r; + return zd1211b_al2230_finalize_rf(chip); +} + +static int zd1211_al2230_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = zd1211_al2230_table[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR138, 0x28 }, + { ZD_CR203, 0x06 }, + }; + + r = zd_rfwritev_locked(chip, rv, 3, RF_RV_BITS); + if (r) + return r; + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_al2230_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = zd1211b_al2230_table[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1, + ARRAY_SIZE(zd1211b_ioreqs_shared_1)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv, 3); + if (r) + return r; + + return zd1211b_al2230_finalize_rf(chip); +} + +static int zd1211_al2230_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR11, 0x00 }, + { ZD_CR251, 0x3f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_al2230_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR11, 0x00 }, + { ZD_CR251, 0x7f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int al2230_switch_radio_off(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR11, 0x04 }, + { ZD_CR251, 0x2f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rf_init_al2230(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + + rf->switch_radio_off = al2230_switch_radio_off; + if (zd_chip_is_zd1211b(chip)) { + rf->init_hw = zd1211b_al2230_init_hw; + rf->set_channel = zd1211b_al2230_set_channel; + rf->switch_radio_on = zd1211b_al2230_switch_radio_on; + } else { + rf->init_hw = zd1211_al2230_init_hw; + rf->set_channel = zd1211_al2230_set_channel; + rf->switch_radio_on = zd1211_al2230_switch_radio_on; + } + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; + rf->patch_cck_gain = 1; + return 0; +} diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c new file mode 100644 index 000000000000..5fea485be574 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c @@ -0,0 +1,494 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +static const u32 chan_rv[][2] = { + RF_CHANNEL( 1) = { 0x09ec00, 0x8cccc8 }, + RF_CHANNEL( 2) = { 0x09ec00, 0x8cccd8 }, + RF_CHANNEL( 3) = { 0x09ec00, 0x8cccc0 }, + RF_CHANNEL( 4) = { 0x09ec00, 0x8cccd0 }, + RF_CHANNEL( 5) = { 0x05ec00, 0x8cccc8 }, + RF_CHANNEL( 6) = { 0x05ec00, 0x8cccd8 }, + RF_CHANNEL( 7) = { 0x05ec00, 0x8cccc0 }, + RF_CHANNEL( 8) = { 0x05ec00, 0x8cccd0 }, + RF_CHANNEL( 9) = { 0x0dec00, 0x8cccc8 }, + RF_CHANNEL(10) = { 0x0dec00, 0x8cccd8 }, + RF_CHANNEL(11) = { 0x0dec00, 0x8cccc0 }, + RF_CHANNEL(12) = { 0x0dec00, 0x8cccd0 }, + RF_CHANNEL(13) = { 0x03ec00, 0x8cccc8 }, + RF_CHANNEL(14) = { 0x03ec00, 0x866660 }, +}; + +static const u32 std_rv[] = { + 0x4ff821, + 0xc5fbfc, + 0x21ebfe, + 0xafd401, /* freq shift 0xaad401 */ + 0x6cf56a, + 0xe04073, + 0x193d76, + 0x9dd844, + 0x500007, + 0xd8c010, +}; + +static const u32 rv_init1[] = { + 0x3c9000, + 0xbfffff, + 0x700000, + 0xf15d58, +}; + +static const u32 rv_init2[] = { + 0xf15d59, + 0xf15d5c, + 0xf15d58, +}; + +static const struct zd_ioreq16 ioreqs_sw[] = { + { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, + { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf }, +}; + +static int zd1211b_al7230b_finalize(struct zd_chip *chip) +{ + int r; + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 }, + { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 }, + { ZD_CR203, 0x04 }, + { }, + { ZD_CR240, 0x80 }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + if (chip->new_phy_layout) { + /* antenna selection? */ + r = zd_iowrite16_locked(chip, 0xe5, ZD_CR9); + if (r) + return r; + } + + return zd_iowrite16_locked(chip, 0x04, ZD_CR203); +} + +static int zd1211_al7230b_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + /* All of these writes are identical to AL2230 unless otherwise + * specified */ + static const struct zd_ioreq16 ioreqs_1[] = { + /* This one is 7230-specific, and happens before the rest */ + { ZD_CR240, 0x57 }, + { }, + + { ZD_CR15, 0x20 }, { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, + { ZD_CR26, 0x11 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, + { ZD_CR44, 0x33 }, + /* This value is different for 7230 (was: 0x2a) */ + { ZD_CR106, 0x22 }, + { ZD_CR107, 0x1a }, { ZD_CR109, 0x09 }, { ZD_CR110, 0x27 }, + { ZD_CR111, 0x2b }, { ZD_CR112, 0x2b }, { ZD_CR119, 0x0a }, + /* This happened further down in AL2230, + * and the value changed (was: 0xe0) */ + { ZD_CR122, 0xfc }, + { ZD_CR10, 0x89 }, + /* for newest (3rd cut) AL2300 */ + { ZD_CR17, 0x28 }, + { ZD_CR26, 0x93 }, { ZD_CR34, 0x30 }, + /* for newest (3rd cut) AL2300 */ + { ZD_CR35, 0x3e }, + { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, + /* for newest (3rd cut) AL2300 */ + { ZD_CR46, 0x96 }, + { ZD_CR47, 0x1e }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, + { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, + { ZD_CR92, 0x0a }, { ZD_CR99, 0x28 }, + /* This value is different for 7230 (was: 0x00) */ + { ZD_CR100, 0x02 }, + { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, + /* This value is different for 7230 (was: 0x24) */ + { ZD_CR106, 0x22 }, + /* This value is different for 7230 (was: 0x2a) */ + { ZD_CR107, 0x3f }, + { ZD_CR109, 0x09 }, + /* This value is different for 7230 (was: 0x13) */ + { ZD_CR110, 0x1f }, + { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, + { ZD_CR114, 0x27 }, + /* for newest (3rd cut) AL2300 */ + { ZD_CR115, 0x24 }, + /* This value is different for 7230 (was: 0x24) */ + { ZD_CR116, 0x3f }, + /* This value is different for 7230 (was: 0xf4) */ + { ZD_CR117, 0xfa }, + { ZD_CR118, 0xfc }, { ZD_CR119, 0x10 }, { ZD_CR120, 0x4f }, + { ZD_CR121, 0x77 }, { ZD_CR137, 0x88 }, + /* This one is 7230-specific */ + { ZD_CR138, 0xa8 }, + /* This value is different for 7230 (was: 0xff) */ + { ZD_CR252, 0x34 }, + /* This value is different for 7230 (was: 0xff) */ + { ZD_CR253, 0x34 }, + + /* PLL_OFF */ + { ZD_CR251, 0x2f }, + }; + + static const struct zd_ioreq16 ioreqs_2[] = { + { ZD_CR251, 0x3f }, /* PLL_ON */ + { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, + { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); + if (r) + return r; + + r = zd_iowrite16_locked(chip, 0x06, ZD_CR203); + if (r) + return r; + r = zd_iowrite16_locked(chip, 0x80, ZD_CR240); + if (r) + return r; + + return 0; +} + +static int zd1211b_al7230b_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs_1[] = { + { ZD_CR240, 0x57 }, { ZD_CR9, 0x9 }, + { }, + { ZD_CR10, 0x8b }, { ZD_CR15, 0x20 }, + { ZD_CR17, 0x2B }, /* for newest (3rd cut) AL2230 */ + { ZD_CR20, 0x10 }, /* 4N25->Stone Request */ + { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 }, + { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, + { ZD_CR33, 0x28 }, /* 5613 */ + { ZD_CR34, 0x30 }, + { ZD_CR35, 0x3e }, /* for newest (3rd cut) AL2230 */ + { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, + { ZD_CR46, 0x99 }, /* for newest (3rd cut) AL2230 */ + { ZD_CR47, 0x1e }, + + /* ZD1215 5610 */ + { ZD_CR48, 0x00 }, { ZD_CR49, 0x00 }, { ZD_CR51, 0x01 }, + { ZD_CR52, 0x80 }, { ZD_CR53, 0x7e }, { ZD_CR65, 0x00 }, + { ZD_CR66, 0x00 }, { ZD_CR67, 0x00 }, { ZD_CR68, 0x00 }, + { ZD_CR69, 0x28 }, + + { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, + { ZD_CR87, 0x0A }, { ZD_CR89, 0x04 }, + { ZD_CR90, 0x58 }, /* 5112 */ + { ZD_CR91, 0x00 }, /* 5613 */ + { ZD_CR92, 0x0a }, + { ZD_CR98, 0x8d }, /* 4804, for 1212 new algorithm */ + { ZD_CR99, 0x00 }, { ZD_CR100, 0x02 }, { ZD_CR101, 0x13 }, + { ZD_CR102, 0x27 }, + { ZD_CR106, 0x20 }, /* change to 0x24 for AL7230B */ + { ZD_CR109, 0x13 }, /* 4804, for 1212 new algorithm */ + { ZD_CR112, 0x1f }, + }; + + static const struct zd_ioreq16 ioreqs_new_phy[] = { + { ZD_CR107, 0x28 }, + { ZD_CR110, 0x1f }, /* 5127, 0x13->0x1f */ + { ZD_CR111, 0x1f }, /* 0x13 to 0x1f for AL7230B */ + { ZD_CR116, 0x2a }, { ZD_CR118, 0xfa }, { ZD_CR119, 0x12 }, + { ZD_CR121, 0x6c }, /* 5613 */ + }; + + static const struct zd_ioreq16 ioreqs_old_phy[] = { + { ZD_CR107, 0x24 }, + { ZD_CR110, 0x13 }, /* 5127, 0x13->0x1f */ + { ZD_CR111, 0x13 }, /* 0x13 to 0x1f for AL7230B */ + { ZD_CR116, 0x24 }, { ZD_CR118, 0xfc }, { ZD_CR119, 0x11 }, + { ZD_CR121, 0x6a }, /* 5613 */ + }; + + static const struct zd_ioreq16 ioreqs_2[] = { + { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, { ZD_CR115, 0x24 }, + { ZD_CR117, 0xfa }, { ZD_CR120, 0x4f }, + { ZD_CR122, 0xfc }, /* E0->FCh at 4901 */ + { ZD_CR123, 0x57 }, /* 5613 */ + { ZD_CR125, 0xad }, /* 4804, for 1212 new algorithm */ + { ZD_CR126, 0x6c }, /* 5613 */ + { ZD_CR127, 0x03 }, /* 4804, for 1212 new algorithm */ + { ZD_CR130, 0x10 }, + { ZD_CR131, 0x00 }, /* 5112 */ + { ZD_CR137, 0x50 }, /* 5613 */ + { ZD_CR138, 0xa8 }, /* 5112 */ + { ZD_CR144, 0xac }, /* 5613 */ + { ZD_CR148, 0x40 }, /* 5112 */ + { ZD_CR149, 0x40 }, /* 4O07, 50->40 */ + { ZD_CR150, 0x1a }, /* 5112, 0C->1A */ + { ZD_CR252, 0x34 }, { ZD_CR253, 0x34 }, + { ZD_CR251, 0x2f }, /* PLL_OFF */ + }; + + static const struct zd_ioreq16 ioreqs_3[] = { + { ZD_CR251, 0x7f }, /* PLL_ON */ + { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, + { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); + if (r) + return r; + + if (chip->new_phy_layout) + r = zd_iowrite16a_locked(chip, ioreqs_new_phy, + ARRAY_SIZE(ioreqs_new_phy)); + else + r = zd_iowrite16a_locked(chip, ioreqs_old_phy, + ARRAY_SIZE(ioreqs_old_phy)); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_3, ARRAY_SIZE(ioreqs_3)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); + if (r) + return r; + + return zd1211b_al7230b_finalize(chip); +} + +static int zd1211_al7230b_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = chan_rv[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + /* PLL_ON */ + { ZD_CR251, 0x3f }, + { ZD_CR203, 0x06 }, { ZD_CR240, 0x08 }, + }; + + r = zd_iowrite16_locked(chip, 0x57, ZD_CR240); + if (r) + return r; + + /* PLL_OFF */ + r = zd_iowrite16_locked(chip, 0x2f, ZD_CR251); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0xf15d58); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv, 2); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_al7230b_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = chan_rv[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + r = zd_iowrite16_locked(chip, 0x57, ZD_CR240); + if (r) + return r; + r = zd_iowrite16_locked(chip, 0xe4, ZD_CR9); + if (r) + return r; + + /* PLL_OFF */ + r = zd_iowrite16_locked(chip, 0x2f, ZD_CR251); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0xf15d58); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv, 2); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + + r = zd_iowrite16_locked(chip, 0x7f, ZD_CR251); + if (r) + return r; + + return zd1211b_al7230b_finalize(chip); +} + +static int zd1211_al7230b_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR11, 0x00 }, + { ZD_CR251, 0x3f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_al7230b_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR11, 0x00 }, + { ZD_CR251, 0x7f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int al7230b_switch_radio_off(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR11, 0x04 }, + { ZD_CR251, 0x2f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +/* ZD1211B+AL7230B 6m band edge patching differs slightly from other + * configurations */ +static int zd1211b_al7230b_patch_6m(struct zd_rf *rf, u8 channel) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + struct zd_ioreq16 ioreqs[] = { + { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, + }; + + /* FIXME: Channel 11 is not the edge for all regulatory domains. */ + if (channel == 1) { + ioreqs[0].value = 0x0e; + ioreqs[1].value = 0x10; + } else if (channel == 11) { + ioreqs[0].value = 0x10; + ioreqs[1].value = 0x10; + } + + dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rf_init_al7230b(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + + if (zd_chip_is_zd1211b(chip)) { + rf->init_hw = zd1211b_al7230b_init_hw; + rf->switch_radio_on = zd1211b_al7230b_switch_radio_on; + rf->set_channel = zd1211b_al7230b_set_channel; + rf->patch_6m_band_edge = zd1211b_al7230b_patch_6m; + } else { + rf->init_hw = zd1211_al7230b_init_hw; + rf->switch_radio_on = zd1211_al7230b_switch_radio_on; + rf->set_channel = zd1211_al7230b_set_channel; + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; + rf->patch_cck_gain = 1; + } + + rf->switch_radio_off = al7230b_switch_radio_off; + + return 0; +} diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c new file mode 100644 index 000000000000..a93f657a41c7 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c @@ -0,0 +1,281 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +static const u32 rf2959_table[][2] = { + RF_CHANNEL( 1) = { 0x181979, 0x1e6666 }, + RF_CHANNEL( 2) = { 0x181989, 0x1e6666 }, + RF_CHANNEL( 3) = { 0x181999, 0x1e6666 }, + RF_CHANNEL( 4) = { 0x1819a9, 0x1e6666 }, + RF_CHANNEL( 5) = { 0x1819b9, 0x1e6666 }, + RF_CHANNEL( 6) = { 0x1819c9, 0x1e6666 }, + RF_CHANNEL( 7) = { 0x1819d9, 0x1e6666 }, + RF_CHANNEL( 8) = { 0x1819e9, 0x1e6666 }, + RF_CHANNEL( 9) = { 0x1819f9, 0x1e6666 }, + RF_CHANNEL(10) = { 0x181a09, 0x1e6666 }, + RF_CHANNEL(11) = { 0x181a19, 0x1e6666 }, + RF_CHANNEL(12) = { 0x181a29, 0x1e6666 }, + RF_CHANNEL(13) = { 0x181a39, 0x1e6666 }, + RF_CHANNEL(14) = { 0x181a60, 0x1c0000 }, +}; + +#if 0 +static int bits(u32 rw, int from, int to) +{ + rw &= ~(0xffffffffU << (to+1)); + rw >>= from; + return rw; +} + +static int bit(u32 rw, int bit) +{ + return bits(rw, bit, bit); +} + +static void dump_regwrite(u32 rw) +{ + int reg = bits(rw, 18, 22); + int rw_flag = bits(rw, 23, 23); + PDEBUG("rf2959 %#010x reg %d rw %d", rw, reg, rw_flag); + + switch (reg) { + case 0: + PDEBUG("reg0 CFG1 ref_sel %d hybernate %d rf_vco_reg_en %d" + " if_vco_reg_en %d if_vga_en %d", + bits(rw, 14, 15), bit(rw, 3), bit(rw, 2), bit(rw, 1), + bit(rw, 0)); + break; + case 1: + PDEBUG("reg1 IFPLL1 pll_en1 %d kv_en1 %d vtc_en1 %d lpf1 %d" + " cpl1 %d pdp1 %d autocal_en1 %d ld_en1 %d ifloopr %d" + " ifloopc %d dac1 %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), + bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), + bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0, 3)); + break; + case 2: + PDEBUG("reg2 IFPLL2 n1 %d num1 %d", + bits(rw, 6, 17), bits(rw, 0, 5)); + break; + case 3: + PDEBUG("reg3 IFPLL3 num %d", bits(rw, 0, 17)); + break; + case 4: + PDEBUG("reg4 IFPLL4 dn1 %#04x ct_def1 %d kv_def1 %d", + bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); + break; + case 5: + PDEBUG("reg5 RFPLL1 pll_en %d kv_en %d vtc_en %d lpf %d cpl %d" + " pdp %d autocal_en %d ld_en %d rfloopr %d rfloopc %d" + " dac %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), + bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), + bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0,3)); + break; + case 6: + PDEBUG("reg6 RFPLL2 n %d num %d", + bits(rw, 6, 17), bits(rw, 0, 5)); + break; + case 7: + PDEBUG("reg7 RFPLL3 num2 %d", bits(rw, 0, 17)); + break; + case 8: + PDEBUG("reg8 RFPLL4 dn %#06x ct_def %d kv_def %d", + bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); + break; + case 9: + PDEBUG("reg9 CAL1 tvco %d tlock %d m_ct_value %d ld_window %d", + bits(rw, 13, 17), bits(rw, 8, 12), bits(rw, 3, 7), + bits(rw, 0, 2)); + break; + case 10: + PDEBUG("reg10 TXRX1 rxdcfbbyps %d pcontrol %d txvgc %d" + " rxlpfbw %d txlpfbw %d txdiffmode %d txenmode %d" + " intbiasen %d tybypass %d", + bit(rw, 17), bits(rw, 15, 16), bits(rw, 10, 14), + bits(rw, 7, 9), bits(rw, 4, 6), bit(rw, 3), bit(rw, 2), + bit(rw, 1), bit(rw, 0)); + break; + case 11: + PDEBUG("reg11 PCNT1 mid_bias %d p_desired %d pc_offset %d" + " tx_delay %d", + bits(rw, 15, 17), bits(rw, 9, 14), bits(rw, 3, 8), + bits(rw, 0, 2)); + break; + case 12: + PDEBUG("reg12 PCNT2 max_power %d mid_power %d min_power %d", + bits(rw, 12, 17), bits(rw, 6, 11), bits(rw, 0, 5)); + break; + case 13: + PDEBUG("reg13 VCOT1 rfpll vco comp %d ifpll vco comp %d" + " lobias %d if_biasbuf %d if_biasvco %d rf_biasbuf %d" + " rf_biasvco %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), + bits(rw, 8, 9), bits(rw, 5, 7), bits(rw, 3, 4), + bits(rw, 0, 2)); + break; + case 14: + PDEBUG("reg14 IQCAL rx_acal %d rx_pcal %d" + " tx_acal %d tx_pcal %d", + bits(rw, 13, 17), bits(rw, 9, 12), bits(rw, 4, 8), + bits(rw, 0, 3)); + break; + } +} +#endif /* 0 */ + +static int rf2959_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR2, 0x1E }, { ZD_CR9, 0x20 }, { ZD_CR10, 0x89 }, + { ZD_CR11, 0x00 }, { ZD_CR15, 0xD0 }, { ZD_CR17, 0x68 }, + { ZD_CR19, 0x4a }, { ZD_CR20, 0x0c }, { ZD_CR21, 0x0E }, + { ZD_CR23, 0x48 }, + /* normal size for cca threshold */ + { ZD_CR24, 0x14 }, + /* { ZD_CR24, 0x20 }, */ + { ZD_CR26, 0x90 }, { ZD_CR27, 0x30 }, { ZD_CR29, 0x20 }, + { ZD_CR31, 0xb2 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x28 }, + { ZD_CR38, 0x30 }, { ZD_CR34, 0x0f }, { ZD_CR35, 0xF0 }, + { ZD_CR41, 0x2a }, { ZD_CR46, 0x7F }, { ZD_CR47, 0x1E }, + { ZD_CR51, 0xc5 }, { ZD_CR52, 0xc5 }, { ZD_CR53, 0xc5 }, + { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, + { ZD_CR82, 0x00 }, { ZD_CR83, 0x24 }, { ZD_CR84, 0x04 }, + { ZD_CR85, 0x00 }, { ZD_CR86, 0x10 }, { ZD_CR87, 0x2A }, + { ZD_CR88, 0x10 }, { ZD_CR89, 0x24 }, { ZD_CR90, 0x18 }, + /* { ZD_CR91, 0x18 }, */ + /* should solve continuous CTS frame problems */ + { ZD_CR91, 0x00 }, + { ZD_CR92, 0x0a }, { ZD_CR93, 0x00 }, { ZD_CR94, 0x01 }, + { ZD_CR95, 0x00 }, { ZD_CR96, 0x40 }, { ZD_CR97, 0x37 }, + { ZD_CR98, 0x05 }, { ZD_CR99, 0x28 }, { ZD_CR100, 0x00 }, + { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 }, + { ZD_CR104, 0x18 }, { ZD_CR105, 0x12 }, + /* normal size */ + { ZD_CR106, 0x1a }, + /* { ZD_CR106, 0x22 }, */ + { ZD_CR107, 0x24 }, { ZD_CR108, 0x0a }, { ZD_CR109, 0x13 }, + { ZD_CR110, 0x2F }, { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 }, + { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, { ZD_CR115, 0x40 }, + { ZD_CR116, 0x40 }, { ZD_CR117, 0xF0 }, { ZD_CR118, 0xF0 }, + { ZD_CR119, 0x16 }, + /* no TX continuation */ + { ZD_CR122, 0x00 }, + /* { ZD_CR122, 0xff }, */ + { ZD_CR127, 0x03 }, { ZD_CR131, 0x08 }, { ZD_CR138, 0x28 }, + { ZD_CR148, 0x44 }, { ZD_CR150, 0x10 }, { ZD_CR169, 0xBB }, + { ZD_CR170, 0xBB }, + }; + + static const u32 rv[] = { + 0x000007, /* REG0(CFG1) */ + 0x07dd43, /* REG1(IFPLL1) */ + 0x080959, /* REG2(IFPLL2) */ + 0x0e6666, + 0x116a57, /* REG4 */ + 0x17dd43, /* REG5 */ + 0x1819f9, /* REG6 */ + 0x1e6666, + 0x214554, + 0x25e7fa, + 0x27fffa, + /* The Zydas driver somehow forgets to set this value. It's + * only set for Japan. We are using internal power control + * for now. + */ + 0x294128, /* internal power */ + /* 0x28252c, */ /* External control TX power */ + /* ZD_CR31_CCK, ZD_CR51_6-36M, ZD_CR52_48M, ZD_CR53_54M */ + 0x2c0000, + 0x300000, + 0x340000, /* REG13(0xD) */ + 0x381e0f, /* REG14(0xE) */ + /* Bogus, RF2959's data sheet doesn't know register 27, which is + * actually referenced here. The commented 0x11 is 17. + */ + 0x6c180f, /* REG27(0x11) */ + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); +} + +static int rf2959_set_channel(struct zd_rf *rf, u8 channel) +{ + int i, r; + const u32 *rv = rf2959_table[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + for (i = 0; i < 2; i++) { + r = zd_rfwrite_locked(chip, rv[i], RF_RV_BITS); + if (r) + return r; + } + return 0; +} + +static int rf2959_switch_radio_on(struct zd_rf *rf) +{ + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR10, 0x89 }, + { ZD_CR11, 0x00 }, + }; + struct zd_chip *chip = zd_rf_to_chip(rf); + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int rf2959_switch_radio_off(struct zd_rf *rf) +{ + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR10, 0x15 }, + { ZD_CR11, 0x81 }, + }; + struct zd_chip *chip = zd_rf_to_chip(rf); + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rf_init_rf2959(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + + if (zd_chip_is_zd1211b(chip)) { + dev_err(zd_chip_dev(chip), + "RF2959 is currently not supported for ZD1211B" + " devices\n"); + return -ENODEV; + } + rf->init_hw = rf2959_init_hw; + rf->set_channel = rf2959_set_channel; + rf->switch_radio_on = rf2959_switch_radio_on; + rf->switch_radio_off = rf2959_switch_radio_off; + return 0; +} diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c new file mode 100644 index 000000000000..61b924027356 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c @@ -0,0 +1,539 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +/* This RF programming code is based upon the code found in v2.16.0.0 of the + * ZyDAS vendor driver. Unlike other RF's, Ubec publish full technical specs + * for this RF on their website, so we're able to understand more than + * usual as to what is going on. Thumbs up for Ubec for doing that. */ + +/* The 3-wire serial interface provides access to 8 write-only registers. + * The data format is a 4 bit register address followed by a 20 bit value. */ +#define UW2453_REGWRITE(reg, val) ((((reg) & 0xf) << 20) | ((val) & 0xfffff)) + +/* For channel tuning, we have to configure registers 1 (synthesizer), 2 (synth + * fractional divide ratio) and 3 (VCO config). + * + * We configure the RF to produce an interrupt when the PLL is locked onto + * the configured frequency. During initialization, we run through a variety + * of different VCO configurations on channel 1 until we detect a PLL lock. + * When this happens, we remember which VCO configuration produced the lock + * and use it later. Actually, we use the configuration *after* the one that + * produced the lock, which seems odd, but it works. + * + * If we do not see a PLL lock on any standard VCO config, we fall back on an + * autocal configuration, which has a fixed (as opposed to per-channel) VCO + * config and different synth values from the standard set (divide ratio + * is still shared with the standard set). */ + +/* The per-channel synth values for all standard VCO configurations. These get + * written to register 1. */ +static const u8 uw2453_std_synth[] = { + RF_CHANNEL( 1) = 0x47, + RF_CHANNEL( 2) = 0x47, + RF_CHANNEL( 3) = 0x67, + RF_CHANNEL( 4) = 0x67, + RF_CHANNEL( 5) = 0x67, + RF_CHANNEL( 6) = 0x67, + RF_CHANNEL( 7) = 0x57, + RF_CHANNEL( 8) = 0x57, + RF_CHANNEL( 9) = 0x57, + RF_CHANNEL(10) = 0x57, + RF_CHANNEL(11) = 0x77, + RF_CHANNEL(12) = 0x77, + RF_CHANNEL(13) = 0x77, + RF_CHANNEL(14) = 0x4f, +}; + +/* This table stores the synthesizer fractional divide ratio for *all* VCO + * configurations (both standard and autocal). These get written to register 2. + */ +static const u16 uw2453_synth_divide[] = { + RF_CHANNEL( 1) = 0x999, + RF_CHANNEL( 2) = 0x99b, + RF_CHANNEL( 3) = 0x998, + RF_CHANNEL( 4) = 0x99a, + RF_CHANNEL( 5) = 0x999, + RF_CHANNEL( 6) = 0x99b, + RF_CHANNEL( 7) = 0x998, + RF_CHANNEL( 8) = 0x99a, + RF_CHANNEL( 9) = 0x999, + RF_CHANNEL(10) = 0x99b, + RF_CHANNEL(11) = 0x998, + RF_CHANNEL(12) = 0x99a, + RF_CHANNEL(13) = 0x999, + RF_CHANNEL(14) = 0xccc, +}; + +/* Here is the data for all the standard VCO configurations. We shrink our + * table a little by observing that both channels in a consecutive pair share + * the same value. We also observe that the high 4 bits ([0:3] in the specs) + * are all 'Reserved' and are always set to 0x4 - we chop them off in the data + * below. */ +#define CHAN_TO_PAIRIDX(a) ((a - 1) / 2) +#define RF_CHANPAIR(a,b) [CHAN_TO_PAIRIDX(a)] +static const u16 uw2453_std_vco_cfg[][7] = { + { /* table 1 */ + RF_CHANPAIR( 1, 2) = 0x664d, + RF_CHANPAIR( 3, 4) = 0x604d, + RF_CHANPAIR( 5, 6) = 0x6675, + RF_CHANPAIR( 7, 8) = 0x6475, + RF_CHANPAIR( 9, 10) = 0x6655, + RF_CHANPAIR(11, 12) = 0x6455, + RF_CHANPAIR(13, 14) = 0x6665, + }, + { /* table 2 */ + RF_CHANPAIR( 1, 2) = 0x666d, + RF_CHANPAIR( 3, 4) = 0x606d, + RF_CHANPAIR( 5, 6) = 0x664d, + RF_CHANPAIR( 7, 8) = 0x644d, + RF_CHANPAIR( 9, 10) = 0x6675, + RF_CHANPAIR(11, 12) = 0x6475, + RF_CHANPAIR(13, 14) = 0x6655, + }, + { /* table 3 */ + RF_CHANPAIR( 1, 2) = 0x665d, + RF_CHANPAIR( 3, 4) = 0x605d, + RF_CHANPAIR( 5, 6) = 0x666d, + RF_CHANPAIR( 7, 8) = 0x646d, + RF_CHANPAIR( 9, 10) = 0x664d, + RF_CHANPAIR(11, 12) = 0x644d, + RF_CHANPAIR(13, 14) = 0x6675, + }, + { /* table 4 */ + RF_CHANPAIR( 1, 2) = 0x667d, + RF_CHANPAIR( 3, 4) = 0x607d, + RF_CHANPAIR( 5, 6) = 0x665d, + RF_CHANPAIR( 7, 8) = 0x645d, + RF_CHANPAIR( 9, 10) = 0x666d, + RF_CHANPAIR(11, 12) = 0x646d, + RF_CHANPAIR(13, 14) = 0x664d, + }, + { /* table 5 */ + RF_CHANPAIR( 1, 2) = 0x6643, + RF_CHANPAIR( 3, 4) = 0x6043, + RF_CHANPAIR( 5, 6) = 0x667d, + RF_CHANPAIR( 7, 8) = 0x647d, + RF_CHANPAIR( 9, 10) = 0x665d, + RF_CHANPAIR(11, 12) = 0x645d, + RF_CHANPAIR(13, 14) = 0x666d, + }, + { /* table 6 */ + RF_CHANPAIR( 1, 2) = 0x6663, + RF_CHANPAIR( 3, 4) = 0x6063, + RF_CHANPAIR( 5, 6) = 0x6643, + RF_CHANPAIR( 7, 8) = 0x6443, + RF_CHANPAIR( 9, 10) = 0x667d, + RF_CHANPAIR(11, 12) = 0x647d, + RF_CHANPAIR(13, 14) = 0x665d, + }, + { /* table 7 */ + RF_CHANPAIR( 1, 2) = 0x6653, + RF_CHANPAIR( 3, 4) = 0x6053, + RF_CHANPAIR( 5, 6) = 0x6663, + RF_CHANPAIR( 7, 8) = 0x6463, + RF_CHANPAIR( 9, 10) = 0x6643, + RF_CHANPAIR(11, 12) = 0x6443, + RF_CHANPAIR(13, 14) = 0x667d, + }, + { /* table 8 */ + RF_CHANPAIR( 1, 2) = 0x6673, + RF_CHANPAIR( 3, 4) = 0x6073, + RF_CHANPAIR( 5, 6) = 0x6653, + RF_CHANPAIR( 7, 8) = 0x6453, + RF_CHANPAIR( 9, 10) = 0x6663, + RF_CHANPAIR(11, 12) = 0x6463, + RF_CHANPAIR(13, 14) = 0x6643, + }, + { /* table 9 */ + RF_CHANPAIR( 1, 2) = 0x664b, + RF_CHANPAIR( 3, 4) = 0x604b, + RF_CHANPAIR( 5, 6) = 0x6673, + RF_CHANPAIR( 7, 8) = 0x6473, + RF_CHANPAIR( 9, 10) = 0x6653, + RF_CHANPAIR(11, 12) = 0x6453, + RF_CHANPAIR(13, 14) = 0x6663, + }, + { /* table 10 */ + RF_CHANPAIR( 1, 2) = 0x666b, + RF_CHANPAIR( 3, 4) = 0x606b, + RF_CHANPAIR( 5, 6) = 0x664b, + RF_CHANPAIR( 7, 8) = 0x644b, + RF_CHANPAIR( 9, 10) = 0x6673, + RF_CHANPAIR(11, 12) = 0x6473, + RF_CHANPAIR(13, 14) = 0x6653, + }, + { /* table 11 */ + RF_CHANPAIR( 1, 2) = 0x665b, + RF_CHANPAIR( 3, 4) = 0x605b, + RF_CHANPAIR( 5, 6) = 0x666b, + RF_CHANPAIR( 7, 8) = 0x646b, + RF_CHANPAIR( 9, 10) = 0x664b, + RF_CHANPAIR(11, 12) = 0x644b, + RF_CHANPAIR(13, 14) = 0x6673, + }, + +}; + +/* The per-channel synth values for autocal. These get written to register 1. */ +static const u16 uw2453_autocal_synth[] = { + RF_CHANNEL( 1) = 0x6847, + RF_CHANNEL( 2) = 0x6847, + RF_CHANNEL( 3) = 0x6867, + RF_CHANNEL( 4) = 0x6867, + RF_CHANNEL( 5) = 0x6867, + RF_CHANNEL( 6) = 0x6867, + RF_CHANNEL( 7) = 0x6857, + RF_CHANNEL( 8) = 0x6857, + RF_CHANNEL( 9) = 0x6857, + RF_CHANNEL(10) = 0x6857, + RF_CHANNEL(11) = 0x6877, + RF_CHANNEL(12) = 0x6877, + RF_CHANNEL(13) = 0x6877, + RF_CHANNEL(14) = 0x684f, +}; + +/* The VCO configuration for autocal (all channels) */ +static const u16 UW2453_AUTOCAL_VCO_CFG = 0x6662; + +/* TX gain settings. The array index corresponds to the TX power integration + * values found in the EEPROM. The values get written to register 7. */ +static u32 uw2453_txgain[] = { + [0x00] = 0x0e313, + [0x01] = 0x0fb13, + [0x02] = 0x0e093, + [0x03] = 0x0f893, + [0x04] = 0x0ea93, + [0x05] = 0x1f093, + [0x06] = 0x1f493, + [0x07] = 0x1f693, + [0x08] = 0x1f393, + [0x09] = 0x1f35b, + [0x0a] = 0x1e6db, + [0x0b] = 0x1ff3f, + [0x0c] = 0x1ffff, + [0x0d] = 0x361d7, + [0x0e] = 0x37fbf, + [0x0f] = 0x3ff8b, + [0x10] = 0x3ff33, + [0x11] = 0x3fb3f, + [0x12] = 0x3ffff, +}; + +/* RF-specific structure */ +struct uw2453_priv { + /* index into synth/VCO config tables where PLL lock was found + * -1 means autocal */ + int config; +}; + +#define UW2453_PRIV(rf) ((struct uw2453_priv *) (rf)->priv) + +static int uw2453_synth_set_channel(struct zd_chip *chip, int channel, + bool autocal) +{ + int r; + int idx = channel - 1; + u32 val; + + if (autocal) + val = UW2453_REGWRITE(1, uw2453_autocal_synth[idx]); + else + val = UW2453_REGWRITE(1, uw2453_std_synth[idx]); + + r = zd_rfwrite_locked(chip, val, RF_RV_BITS); + if (r) + return r; + + return zd_rfwrite_locked(chip, + UW2453_REGWRITE(2, uw2453_synth_divide[idx]), RF_RV_BITS); +} + +static int uw2453_write_vco_cfg(struct zd_chip *chip, u16 value) +{ + /* vendor driver always sets these upper bits even though the specs say + * they are reserved */ + u32 val = 0x40000 | value; + return zd_rfwrite_locked(chip, UW2453_REGWRITE(3, val), RF_RV_BITS); +} + +static int uw2453_init_mode(struct zd_chip *chip) +{ + static const u32 rv[] = { + UW2453_REGWRITE(0, 0x25f98), /* enter IDLE mode */ + UW2453_REGWRITE(0, 0x25f9a), /* enter CAL_VCO mode */ + UW2453_REGWRITE(0, 0x25f94), /* enter RX/TX mode */ + UW2453_REGWRITE(0, 0x27fd4), /* power down RSSI circuit */ + }; + + return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); +} + +static int uw2453_set_tx_gain_level(struct zd_chip *chip, int channel) +{ + u8 int_value = chip->pwr_int_values[channel - 1]; + + if (int_value >= ARRAY_SIZE(uw2453_txgain)) { + dev_dbg_f(zd_chip_dev(chip), "can't configure TX gain for " + "int value %x on channel %d\n", int_value, channel); + return 0; + } + + return zd_rfwrite_locked(chip, + UW2453_REGWRITE(7, uw2453_txgain[int_value]), RF_RV_BITS); +} + +static int uw2453_init_hw(struct zd_rf *rf) +{ + int i, r; + int found_config = -1; + u16 intr_status; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR10, 0x89 }, { ZD_CR15, 0x20 }, + { ZD_CR17, 0x28 }, /* 6112 no change */ + { ZD_CR23, 0x38 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 }, + { ZD_CR27, 0x15 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, + { ZD_CR33, 0x28 }, { ZD_CR34, 0x30 }, + { ZD_CR35, 0x43 }, /* 6112 3e->43 */ + { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, + { ZD_CR46, 0x92 }, /* 6112 96->92 */ + { ZD_CR47, 0x1e }, + { ZD_CR48, 0x04 }, /* 5602 Roger */ + { ZD_CR49, 0xfa }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, + { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, + { ZD_CR91, 0x00 }, { ZD_CR92, 0x0a }, { ZD_CR98, 0x8d }, + { ZD_CR99, 0x28 }, { ZD_CR100, 0x02 }, + { ZD_CR101, 0x09 }, /* 6112 13->1f 6220 1f->13 6407 13->9 */ + { ZD_CR102, 0x27 }, + { ZD_CR106, 0x1c }, /* 5d07 5112 1f->1c 6220 1c->1f + * 6221 1f->1c + */ + { ZD_CR107, 0x1c }, /* 6220 1c->1a 5221 1a->1c */ + { ZD_CR109, 0x13 }, + { ZD_CR110, 0x1f }, /* 6112 13->1f 6221 1f->13 6407 13->0x09 */ + { ZD_CR111, 0x13 }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, + { ZD_CR114, 0x23 }, /* 6221 27->23 */ + { ZD_CR115, 0x24 }, /* 6112 24->1c 6220 1c->24 */ + { ZD_CR116, 0x24 }, /* 6220 1c->24 */ + { ZD_CR117, 0xfa }, /* 6112 fa->f8 6220 f8->f4 6220 f4->fa */ + { ZD_CR118, 0xf0 }, /* 5d07 6112 f0->f2 6220 f2->f0 */ + { ZD_CR119, 0x1a }, /* 6112 1a->10 6220 10->14 6220 14->1a */ + { ZD_CR120, 0x4f }, + { ZD_CR121, 0x1f }, /* 6220 4f->1f */ + { ZD_CR122, 0xf0 }, { ZD_CR123, 0x57 }, { ZD_CR125, 0xad }, + { ZD_CR126, 0x6c }, { ZD_CR127, 0x03 }, + { ZD_CR128, 0x14 }, /* 6302 12->11 */ + { ZD_CR129, 0x12 }, /* 6301 10->0f */ + { ZD_CR130, 0x10 }, { ZD_CR137, 0x50 }, { ZD_CR138, 0xa8 }, + { ZD_CR144, 0xac }, { ZD_CR146, 0x20 }, { ZD_CR252, 0xff }, + { ZD_CR253, 0xff }, + }; + + static const u32 rv[] = { + UW2453_REGWRITE(4, 0x2b), /* configure receiver gain */ + UW2453_REGWRITE(5, 0x19e4f), /* configure transmitter gain */ + UW2453_REGWRITE(6, 0xf81ad), /* enable RX/TX filter tuning */ + UW2453_REGWRITE(7, 0x3fffe), /* disable TX gain in test mode */ + + /* enter CAL_FIL mode, TX gain set by registers, RX gain set by pins, + * RSSI circuit powered down, reduced RSSI range */ + UW2453_REGWRITE(0, 0x25f9c), /* 5d01 cal_fil */ + + /* synthesizer configuration for channel 1 */ + UW2453_REGWRITE(1, 0x47), + UW2453_REGWRITE(2, 0x999), + + /* disable manual VCO band selection */ + UW2453_REGWRITE(3, 0x7602), + + /* enable manual VCO band selection, configure current level */ + UW2453_REGWRITE(3, 0x46063), + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); + if (r) + return r; + + r = uw2453_init_mode(chip); + if (r) + return r; + + /* Try all standard VCO configuration settings on channel 1 */ + for (i = 0; i < ARRAY_SIZE(uw2453_std_vco_cfg) - 1; i++) { + /* Configure synthesizer for channel 1 */ + r = uw2453_synth_set_channel(chip, 1, false); + if (r) + return r; + + /* Write VCO config */ + r = uw2453_write_vco_cfg(chip, uw2453_std_vco_cfg[i][0]); + if (r) + return r; + + /* ack interrupt event */ + r = zd_iowrite16_locked(chip, 0x0f, UW2453_INTR_REG); + if (r) + return r; + + /* check interrupt status */ + r = zd_ioread16_locked(chip, &intr_status, UW2453_INTR_REG); + if (r) + return r; + + if (!(intr_status & 0xf)) { + dev_dbg_f(zd_chip_dev(chip), + "PLL locked on configuration %d\n", i); + found_config = i; + break; + } + } + + if (found_config == -1) { + /* autocal */ + dev_dbg_f(zd_chip_dev(chip), + "PLL did not lock, using autocal\n"); + + r = uw2453_synth_set_channel(chip, 1, true); + if (r) + return r; + + r = uw2453_write_vco_cfg(chip, UW2453_AUTOCAL_VCO_CFG); + if (r) + return r; + } + + /* To match the vendor driver behaviour, we use the configuration after + * the one that produced a lock. */ + UW2453_PRIV(rf)->config = found_config + 1; + + return zd_iowrite16_locked(chip, 0x06, ZD_CR203); +} + +static int uw2453_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + u16 vco_cfg; + int config = UW2453_PRIV(rf)->config; + bool autocal = (config == -1); + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 }, + { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 }, + }; + + r = uw2453_synth_set_channel(chip, channel, autocal); + if (r) + return r; + + if (autocal) + vco_cfg = UW2453_AUTOCAL_VCO_CFG; + else + vco_cfg = uw2453_std_vco_cfg[config][CHAN_TO_PAIRIDX(channel)]; + + r = uw2453_write_vco_cfg(chip, vco_cfg); + if (r) + return r; + + r = uw2453_init_mode(chip); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + r = uw2453_set_tx_gain_level(chip, channel); + if (r) + return r; + + return zd_iowrite16_locked(chip, 0x06, ZD_CR203); +} + +static int uw2453_switch_radio_on(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + struct zd_ioreq16 ioreqs[] = { + { ZD_CR11, 0x00 }, { ZD_CR251, 0x3f }, + }; + + /* enter RXTX mode */ + r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f94), RF_RV_BITS); + if (r) + return r; + + if (zd_chip_is_zd1211b(chip)) + ioreqs[1].value = 0x7f; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int uw2453_switch_radio_off(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { ZD_CR11, 0x04 }, { ZD_CR251, 0x2f }, + }; + + /* enter IDLE mode */ + /* FIXME: shouldn't we go to SLEEP? sent email to zydas */ + r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f90), RF_RV_BITS); + if (r) + return r; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static void uw2453_clear(struct zd_rf *rf) +{ + kfree(rf->priv); +} + +int zd_rf_init_uw2453(struct zd_rf *rf) +{ + rf->init_hw = uw2453_init_hw; + rf->set_channel = uw2453_set_channel; + rf->switch_radio_on = uw2453_switch_radio_on; + rf->switch_radio_off = uw2453_switch_radio_off; + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; + rf->clear = uw2453_clear; + /* we have our own TX integration code */ + rf->update_channel_int = 0; + + rf->priv = kmalloc(sizeof(struct uw2453_priv), GFP_KERNEL); + if (rf->priv == NULL) + return -ENOMEM; + + return 0; +} + diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c new file mode 100644 index 000000000000..a912dc051111 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c @@ -0,0 +1,2060 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * Copyright (C) 2006-2007 Michael Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zd_def.h" +#include "zd_mac.h" +#include "zd_usb.h" + +static struct usb_device_id usb_ids[] = { + /* ZD1211 */ + { USB_DEVICE(0x0105, 0x145f), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3402), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3407), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3409), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x079b, 0x004a), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0ace, 0xa211), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0b05, 0x170c), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0b3b, 0x5630), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0df6, 0x9075), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x129b, 0x1666), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x1435, 0x0711), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x14ea, 0xab10), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x300a), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x3204), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x3207), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, + /* ZD1211B */ + { USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0409, 0x0248), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0411, 0x00da), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x054c, 0x0257), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x340a), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3413), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x07fa, 0x1196), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x083a, 0xe501), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x083a, 0xe503), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x083a, 0xe506), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0ace, 0xb215), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0cde, 0x001a), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0df6, 0x0036), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x129b, 0x1667), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x2019, 0x5303), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x2019, 0xed01), .driver_info = DEVICE_ZD1211B }, + /* "Driverless" devices that need ejecting */ + { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, + { USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER }, + {} +}; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("USB driver for devices with the ZD1211 chip."); +MODULE_AUTHOR("Ulrich Kunitz"); +MODULE_AUTHOR("Daniel Drake"); +MODULE_VERSION("1.0"); +MODULE_DEVICE_TABLE(usb, usb_ids); + +#define FW_ZD1211_PREFIX "zd1211/zd1211_" +#define FW_ZD1211B_PREFIX "zd1211/zd1211b_" + +static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req, + unsigned int count); + +/* USB device initialization */ +static void int_urb_complete(struct urb *urb); + +static int request_fw_file( + const struct firmware **fw, const char *name, struct device *device) +{ + int r; + + dev_dbg_f(device, "fw name %s\n", name); + + r = request_firmware(fw, name, device); + if (r) + dev_err(device, + "Could not load firmware file %s. Error number %d\n", + name, r); + return r; +} + +static inline u16 get_bcdDevice(const struct usb_device *udev) +{ + return le16_to_cpu(udev->descriptor.bcdDevice); +} + +enum upload_code_flags { + REBOOT = 1, +}; + +/* Ensures that MAX_TRANSFER_SIZE is even. */ +#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1) + +static int upload_code(struct usb_device *udev, + const u8 *data, size_t size, u16 code_offset, int flags) +{ + u8 *p; + int r; + + /* USB request blocks need "kmalloced" buffers. + */ + p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL); + if (!p) { + r = -ENOMEM; + goto error; + } + + size &= ~1; + while (size > 0) { + size_t transfer_size = size <= MAX_TRANSFER_SIZE ? + size : MAX_TRANSFER_SIZE; + + dev_dbg_f(&udev->dev, "transfer size %zu\n", transfer_size); + + memcpy(p, data, transfer_size); + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_FIRMWARE_DOWNLOAD, + USB_DIR_OUT | USB_TYPE_VENDOR, + code_offset, 0, p, transfer_size, 1000 /* ms */); + if (r < 0) { + dev_err(&udev->dev, + "USB control request for firmware upload" + " failed. Error number %d\n", r); + goto error; + } + transfer_size = r & ~1; + + size -= transfer_size; + data += transfer_size; + code_offset += transfer_size/sizeof(u16); + } + + if (flags & REBOOT) { + u8 ret; + + /* Use "DMA-aware" buffer. */ + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_REQ_FIRMWARE_CONFIRM, + USB_DIR_IN | USB_TYPE_VENDOR, + 0, 0, p, sizeof(ret), 5000 /* ms */); + if (r != sizeof(ret)) { + dev_err(&udev->dev, + "control request firmeware confirmation failed." + " Return value %d\n", r); + if (r >= 0) + r = -ENODEV; + goto error; + } + ret = p[0]; + if (ret & 0x80) { + dev_err(&udev->dev, + "Internal error while downloading." + " Firmware confirm return value %#04x\n", + (unsigned int)ret); + r = -ENODEV; + goto error; + } + dev_dbg_f(&udev->dev, "firmware confirm return value %#04x\n", + (unsigned int)ret); + } + + r = 0; +error: + kfree(p); + return r; +} + +static u16 get_word(const void *data, u16 offset) +{ + const __le16 *p = data; + return le16_to_cpu(p[offset]); +} + +static char *get_fw_name(struct zd_usb *usb, char *buffer, size_t size, + const char* postfix) +{ + scnprintf(buffer, size, "%s%s", + usb->is_zd1211b ? + FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX, + postfix); + return buffer; +} + +static int handle_version_mismatch(struct zd_usb *usb, + const struct firmware *ub_fw) +{ + struct usb_device *udev = zd_usb_to_usbdev(usb); + const struct firmware *ur_fw = NULL; + int offset; + int r = 0; + char fw_name[128]; + + r = request_fw_file(&ur_fw, + get_fw_name(usb, fw_name, sizeof(fw_name), "ur"), + &udev->dev); + if (r) + goto error; + + r = upload_code(udev, ur_fw->data, ur_fw->size, FW_START, REBOOT); + if (r) + goto error; + + offset = (E2P_BOOT_CODE_OFFSET * sizeof(u16)); + r = upload_code(udev, ub_fw->data + offset, ub_fw->size - offset, + E2P_START + E2P_BOOT_CODE_OFFSET, REBOOT); + + /* At this point, the vendor driver downloads the whole firmware + * image, hacks around with version IDs, and uploads it again, + * completely overwriting the boot code. We do not do this here as + * it is not required on any tested devices, and it is suspected to + * cause problems. */ +error: + release_firmware(ur_fw); + return r; +} + +static int upload_firmware(struct zd_usb *usb) +{ + int r; + u16 fw_bcdDevice; + u16 bcdDevice; + struct usb_device *udev = zd_usb_to_usbdev(usb); + const struct firmware *ub_fw = NULL; + const struct firmware *uph_fw = NULL; + char fw_name[128]; + + bcdDevice = get_bcdDevice(udev); + + r = request_fw_file(&ub_fw, + get_fw_name(usb, fw_name, sizeof(fw_name), "ub"), + &udev->dev); + if (r) + goto error; + + fw_bcdDevice = get_word(ub_fw->data, E2P_DATA_OFFSET); + + if (fw_bcdDevice != bcdDevice) { + dev_info(&udev->dev, + "firmware version %#06x and device bootcode version " + "%#06x differ\n", fw_bcdDevice, bcdDevice); + if (bcdDevice <= 0x4313) + dev_warn(&udev->dev, "device has old bootcode, please " + "report success or failure\n"); + + r = handle_version_mismatch(usb, ub_fw); + if (r) + goto error; + } else { + dev_dbg_f(&udev->dev, + "firmware device id %#06x is equal to the " + "actual device id\n", fw_bcdDevice); + } + + + r = request_fw_file(&uph_fw, + get_fw_name(usb, fw_name, sizeof(fw_name), "uphr"), + &udev->dev); + if (r) + goto error; + + r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START, REBOOT); + if (r) { + dev_err(&udev->dev, + "Could not upload firmware code uph. Error number %d\n", + r); + } + + /* FALL-THROUGH */ +error: + release_firmware(ub_fw); + release_firmware(uph_fw); + return r; +} + +MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ur"); +MODULE_FIRMWARE(FW_ZD1211_PREFIX "ur"); +MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ub"); +MODULE_FIRMWARE(FW_ZD1211_PREFIX "ub"); +MODULE_FIRMWARE(FW_ZD1211B_PREFIX "uphr"); +MODULE_FIRMWARE(FW_ZD1211_PREFIX "uphr"); + +/* Read data from device address space using "firmware interface" which does + * not require firmware to be loaded. */ +int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len) +{ + int r; + struct usb_device *udev = zd_usb_to_usbdev(usb); + u8 *buf; + + /* Use "DMA-aware" buffer. */ + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_REQ_FIRMWARE_READ_DATA, USB_DIR_IN | 0x40, addr, 0, + buf, len, 5000); + if (r < 0) { + dev_err(&udev->dev, + "read over firmware interface failed: %d\n", r); + goto exit; + } else if (r != len) { + dev_err(&udev->dev, + "incomplete read over firmware interface: %d/%d\n", + r, len); + r = -EIO; + goto exit; + } + r = 0; + memcpy(data, buf, len); +exit: + kfree(buf); + return r; +} + +#define urb_dev(urb) (&(urb)->dev->dev) + +static inline void handle_regs_int_override(struct urb *urb) +{ + struct zd_usb *usb = urb->context; + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock(&intr->lock); + if (atomic_read(&intr->read_regs_enabled)) { + atomic_set(&intr->read_regs_enabled, 0); + intr->read_regs_int_overridden = 1; + complete(&intr->read_regs.completion); + } + spin_unlock(&intr->lock); +} + +static inline void handle_regs_int(struct urb *urb) +{ + struct zd_usb *usb = urb->context; + struct zd_usb_interrupt *intr = &usb->intr; + int len; + u16 int_num; + + ZD_ASSERT(in_interrupt()); + spin_lock(&intr->lock); + + int_num = le16_to_cpu(*(__le16 *)(urb->transfer_buffer+2)); + if (int_num == CR_INTERRUPT) { + struct zd_mac *mac = zd_hw_mac(zd_usb_to_hw(urb->context)); + spin_lock(&mac->lock); + memcpy(&mac->intr_buffer, urb->transfer_buffer, + USB_MAX_EP_INT_BUFFER); + spin_unlock(&mac->lock); + schedule_work(&mac->process_intr); + } else if (atomic_read(&intr->read_regs_enabled)) { + len = urb->actual_length; + intr->read_regs.length = urb->actual_length; + if (len > sizeof(intr->read_regs.buffer)) + len = sizeof(intr->read_regs.buffer); + + memcpy(intr->read_regs.buffer, urb->transfer_buffer, len); + + /* Sometimes USB_INT_ID_REGS is not overridden, but comes after + * USB_INT_ID_RETRY_FAILED. Read-reg retry then gets this + * delayed USB_INT_ID_REGS, but leaves USB_INT_ID_REGS of + * retry unhandled. Next read-reg command then might catch + * this wrong USB_INT_ID_REGS. Fix by ignoring wrong reads. + */ + if (!check_read_regs(usb, intr->read_regs.req, + intr->read_regs.req_count)) + goto out; + + atomic_set(&intr->read_regs_enabled, 0); + intr->read_regs_int_overridden = 0; + complete(&intr->read_regs.completion); + + goto out; + } + +out: + spin_unlock(&intr->lock); + + /* CR_INTERRUPT might override read_reg too. */ + if (int_num == CR_INTERRUPT && atomic_read(&intr->read_regs_enabled)) + handle_regs_int_override(urb); +} + +static void int_urb_complete(struct urb *urb) +{ + int r; + struct usb_int_header *hdr; + struct zd_usb *usb; + struct zd_usb_interrupt *intr; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + case -EPIPE: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + return; + default: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + goto resubmit; + } + + if (urb->actual_length < sizeof(hdr)) { + dev_dbg_f(urb_dev(urb), "error: urb %p to small\n", urb); + goto resubmit; + } + + hdr = urb->transfer_buffer; + if (hdr->type != USB_INT_TYPE) { + dev_dbg_f(urb_dev(urb), "error: urb %p wrong type\n", urb); + goto resubmit; + } + + /* USB_INT_ID_RETRY_FAILED triggered by tx-urb submit can override + * pending USB_INT_ID_REGS causing read command timeout. + */ + usb = urb->context; + intr = &usb->intr; + if (hdr->id != USB_INT_ID_REGS && atomic_read(&intr->read_regs_enabled)) + handle_regs_int_override(urb); + + switch (hdr->id) { + case USB_INT_ID_REGS: + handle_regs_int(urb); + break; + case USB_INT_ID_RETRY_FAILED: + zd_mac_tx_failed(urb); + break; + default: + dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb, + (unsigned int)hdr->id); + goto resubmit; + } + +resubmit: + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) { + dev_dbg_f(urb_dev(urb), "error: resubmit urb %p err code %d\n", + urb, r); + /* TODO: add worker to reset intr->urb */ + } + return; +} + +static inline int int_urb_interval(struct usb_device *udev) +{ + switch (udev->speed) { + case USB_SPEED_HIGH: + return 4; + case USB_SPEED_LOW: + return 10; + case USB_SPEED_FULL: + default: + return 1; + } +} + +static inline int usb_int_enabled(struct zd_usb *usb) +{ + unsigned long flags; + struct zd_usb_interrupt *intr = &usb->intr; + struct urb *urb; + + spin_lock_irqsave(&intr->lock, flags); + urb = intr->urb; + spin_unlock_irqrestore(&intr->lock, flags); + return urb != NULL; +} + +int zd_usb_enable_int(struct zd_usb *usb) +{ + int r; + struct usb_device *udev = zd_usb_to_usbdev(usb); + struct zd_usb_interrupt *intr = &usb->intr; + struct urb *urb; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + r = -ENOMEM; + goto out; + } + + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&intr->lock); + if (intr->urb) { + spin_unlock_irq(&intr->lock); + r = 0; + goto error_free_urb; + } + intr->urb = urb; + spin_unlock_irq(&intr->lock); + + r = -ENOMEM; + intr->buffer = usb_alloc_coherent(udev, USB_MAX_EP_INT_BUFFER, + GFP_KERNEL, &intr->buffer_dma); + if (!intr->buffer) { + dev_dbg_f(zd_usb_dev(usb), + "couldn't allocate transfer_buffer\n"); + goto error_set_urb_null; + } + + usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN), + intr->buffer, USB_MAX_EP_INT_BUFFER, + int_urb_complete, usb, + intr->interval); + urb->transfer_dma = intr->buffer_dma; + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb); + r = usb_submit_urb(urb, GFP_KERNEL); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "Couldn't submit urb. Error number %d\n", r); + goto error; + } + + return 0; +error: + usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER, + intr->buffer, intr->buffer_dma); +error_set_urb_null: + spin_lock_irq(&intr->lock); + intr->urb = NULL; + spin_unlock_irq(&intr->lock); +error_free_urb: + usb_free_urb(urb); +out: + return r; +} + +void zd_usb_disable_int(struct zd_usb *usb) +{ + unsigned long flags; + struct usb_device *udev = zd_usb_to_usbdev(usb); + struct zd_usb_interrupt *intr = &usb->intr; + struct urb *urb; + void *buffer; + dma_addr_t buffer_dma; + + spin_lock_irqsave(&intr->lock, flags); + urb = intr->urb; + if (!urb) { + spin_unlock_irqrestore(&intr->lock, flags); + return; + } + intr->urb = NULL; + buffer = intr->buffer; + buffer_dma = intr->buffer_dma; + intr->buffer = NULL; + spin_unlock_irqrestore(&intr->lock, flags); + + usb_kill_urb(urb); + dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb); + usb_free_urb(urb); + + if (buffer) + usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER, + buffer, buffer_dma); +} + +static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer, + unsigned int length) +{ + int i; + const struct rx_length_info *length_info; + + if (length < sizeof(struct rx_length_info)) { + /* It's not a complete packet anyhow. */ + dev_dbg_f(zd_usb_dev(usb), "invalid, small RX packet : %d\n", + length); + return; + } + length_info = (struct rx_length_info *) + (buffer + length - sizeof(struct rx_length_info)); + + /* It might be that three frames are merged into a single URB + * transaction. We have to check for the length info tag. + * + * While testing we discovered that length_info might be unaligned, + * because if USB transactions are merged, the last packet will not + * be padded. Unaligned access might also happen if the length_info + * structure is not present. + */ + if (get_unaligned_le16(&length_info->tag) == RX_LENGTH_INFO_TAG) + { + unsigned int l, k, n; + for (i = 0, l = 0;; i++) { + k = get_unaligned_le16(&length_info->length[i]); + if (k == 0) + return; + n = l+k; + if (n > length) + return; + zd_mac_rx(zd_usb_to_hw(usb), buffer+l, k); + if (i >= 2) + return; + l = (n+3) & ~3; + } + } else { + zd_mac_rx(zd_usb_to_hw(usb), buffer, length); + } +} + +static void rx_urb_complete(struct urb *urb) +{ + int r; + struct zd_usb *usb; + struct zd_usb_rx *rx; + const u8 *buffer; + unsigned int length; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + case -EPIPE: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + return; + default: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + goto resubmit; + } + + buffer = urb->transfer_buffer; + length = urb->actual_length; + usb = urb->context; + rx = &usb->rx; + + tasklet_schedule(&rx->reset_timer_tasklet); + + if (length%rx->usb_packet_size > rx->usb_packet_size-4) { + /* If there is an old first fragment, we don't care. */ + dev_dbg_f(urb_dev(urb), "*** first fragment ***\n"); + ZD_ASSERT(length <= ARRAY_SIZE(rx->fragment)); + spin_lock(&rx->lock); + memcpy(rx->fragment, buffer, length); + rx->fragment_length = length; + spin_unlock(&rx->lock); + goto resubmit; + } + + spin_lock(&rx->lock); + if (rx->fragment_length > 0) { + /* We are on a second fragment, we believe */ + ZD_ASSERT(length + rx->fragment_length <= + ARRAY_SIZE(rx->fragment)); + dev_dbg_f(urb_dev(urb), "*** second fragment ***\n"); + memcpy(rx->fragment+rx->fragment_length, buffer, length); + handle_rx_packet(usb, rx->fragment, + rx->fragment_length + length); + rx->fragment_length = 0; + spin_unlock(&rx->lock); + } else { + spin_unlock(&rx->lock); + handle_rx_packet(usb, buffer, length); + } + +resubmit: + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) + dev_dbg_f(urb_dev(urb), "urb %p resubmit error %d\n", urb, r); +} + +static struct urb *alloc_rx_urb(struct zd_usb *usb) +{ + struct usb_device *udev = zd_usb_to_usbdev(usb); + struct urb *urb; + void *buffer; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return NULL; + buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL, + &urb->transfer_dma); + if (!buffer) { + usb_free_urb(urb); + return NULL; + } + + usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN), + buffer, USB_MAX_RX_SIZE, + rx_urb_complete, usb); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + return urb; +} + +static void free_rx_urb(struct urb *urb) +{ + if (!urb) + return; + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); +} + +static int __zd_usb_enable_rx(struct zd_usb *usb) +{ + int i, r; + struct zd_usb_rx *rx = &usb->rx; + struct urb **urbs; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + r = -ENOMEM; + urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL); + if (!urbs) + goto error; + for (i = 0; i < RX_URBS_COUNT; i++) { + urbs[i] = alloc_rx_urb(usb); + if (!urbs[i]) + goto error; + } + + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&rx->lock); + if (rx->urbs) { + spin_unlock_irq(&rx->lock); + r = 0; + goto error; + } + rx->urbs = urbs; + rx->urbs_count = RX_URBS_COUNT; + spin_unlock_irq(&rx->lock); + + for (i = 0; i < RX_URBS_COUNT; i++) { + r = usb_submit_urb(urbs[i], GFP_KERNEL); + if (r) + goto error_submit; + } + + return 0; +error_submit: + for (i = 0; i < RX_URBS_COUNT; i++) { + usb_kill_urb(urbs[i]); + } + spin_lock_irq(&rx->lock); + rx->urbs = NULL; + rx->urbs_count = 0; + spin_unlock_irq(&rx->lock); +error: + if (urbs) { + for (i = 0; i < RX_URBS_COUNT; i++) + free_rx_urb(urbs[i]); + } + return r; +} + +int zd_usb_enable_rx(struct zd_usb *usb) +{ + int r; + struct zd_usb_rx *rx = &usb->rx; + + mutex_lock(&rx->setup_mutex); + r = __zd_usb_enable_rx(usb); + mutex_unlock(&rx->setup_mutex); + + zd_usb_reset_rx_idle_timer(usb); + + return r; +} + +static void __zd_usb_disable_rx(struct zd_usb *usb) +{ + int i; + unsigned long flags; + struct urb **urbs; + unsigned int count; + struct zd_usb_rx *rx = &usb->rx; + + spin_lock_irqsave(&rx->lock, flags); + urbs = rx->urbs; + count = rx->urbs_count; + spin_unlock_irqrestore(&rx->lock, flags); + if (!urbs) + return; + + for (i = 0; i < count; i++) { + usb_kill_urb(urbs[i]); + free_rx_urb(urbs[i]); + } + kfree(urbs); + + spin_lock_irqsave(&rx->lock, flags); + rx->urbs = NULL; + rx->urbs_count = 0; + spin_unlock_irqrestore(&rx->lock, flags); +} + +void zd_usb_disable_rx(struct zd_usb *usb) +{ + struct zd_usb_rx *rx = &usb->rx; + + mutex_lock(&rx->setup_mutex); + __zd_usb_disable_rx(usb); + mutex_unlock(&rx->setup_mutex); + + tasklet_kill(&rx->reset_timer_tasklet); + cancel_delayed_work_sync(&rx->idle_work); +} + +static void zd_usb_reset_rx(struct zd_usb *usb) +{ + bool do_reset; + struct zd_usb_rx *rx = &usb->rx; + unsigned long flags; + + mutex_lock(&rx->setup_mutex); + + spin_lock_irqsave(&rx->lock, flags); + do_reset = rx->urbs != NULL; + spin_unlock_irqrestore(&rx->lock, flags); + + if (do_reset) { + __zd_usb_disable_rx(usb); + __zd_usb_enable_rx(usb); + } + + mutex_unlock(&rx->setup_mutex); + + if (do_reset) + zd_usb_reset_rx_idle_timer(usb); +} + +/** + * zd_usb_disable_tx - disable transmission + * @usb: the zd1211rw-private USB structure + * + * Frees all URBs in the free list and marks the transmission as disabled. + */ +void zd_usb_disable_tx(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + unsigned long flags; + + atomic_set(&tx->enabled, 0); + + /* kill all submitted tx-urbs */ + usb_kill_anchored_urbs(&tx->submitted); + + spin_lock_irqsave(&tx->lock, flags); + WARN_ON(!skb_queue_empty(&tx->submitted_skbs)); + WARN_ON(tx->submitted_urbs != 0); + tx->submitted_urbs = 0; + spin_unlock_irqrestore(&tx->lock, flags); + + /* The stopped state is ignored, relying on ieee80211_wake_queues() + * in a potentionally following zd_usb_enable_tx(). + */ +} + +/** + * zd_usb_enable_tx - enables transmission + * @usb: a &struct zd_usb pointer + * + * This function enables transmission and prepares the &zd_usb_tx data + * structure. + */ +void zd_usb_enable_tx(struct zd_usb *usb) +{ + unsigned long flags; + struct zd_usb_tx *tx = &usb->tx; + + spin_lock_irqsave(&tx->lock, flags); + atomic_set(&tx->enabled, 1); + tx->submitted_urbs = 0; + ieee80211_wake_queues(zd_usb_to_hw(usb)); + tx->stopped = 0; + spin_unlock_irqrestore(&tx->lock, flags); +} + +static void tx_dec_submitted_urbs(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + unsigned long flags; + + spin_lock_irqsave(&tx->lock, flags); + --tx->submitted_urbs; + if (tx->stopped && tx->submitted_urbs <= ZD_USB_TX_LOW) { + ieee80211_wake_queues(zd_usb_to_hw(usb)); + tx->stopped = 0; + } + spin_unlock_irqrestore(&tx->lock, flags); +} + +static void tx_inc_submitted_urbs(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + unsigned long flags; + + spin_lock_irqsave(&tx->lock, flags); + ++tx->submitted_urbs; + if (!tx->stopped && tx->submitted_urbs > ZD_USB_TX_HIGH) { + ieee80211_stop_queues(zd_usb_to_hw(usb)); + tx->stopped = 1; + } + spin_unlock_irqrestore(&tx->lock, flags); +} + +/** + * tx_urb_complete - completes the execution of an URB + * @urb: a URB + * + * This function is called if the URB has been transferred to a device or an + * error has happened. + */ +static void tx_urb_complete(struct urb *urb) +{ + int r; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + struct zd_usb *usb; + struct zd_usb_tx *tx; + + skb = (struct sk_buff *)urb->context; + info = IEEE80211_SKB_CB(skb); + /* + * grab 'usb' pointer before handing off the skb (since + * it might be freed by zd_mac_tx_to_dev or mac80211) + */ + usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb; + tx = &usb->tx; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + case -EPIPE: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + break; + default: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + goto resubmit; + } +free_urb: + skb_unlink(skb, &usb->tx.submitted_skbs); + zd_mac_tx_to_dev(skb, urb->status); + usb_free_urb(urb); + tx_dec_submitted_urbs(usb); + return; +resubmit: + usb_anchor_urb(urb, &tx->submitted); + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) { + usb_unanchor_urb(urb); + dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r); + goto free_urb; + } +} + +/** + * zd_usb_tx: initiates transfer of a frame of the device + * + * @usb: the zd1211rw-private USB structure + * @skb: a &struct sk_buff pointer + * + * This function tranmits a frame to the device. It doesn't wait for + * completion. The frame must contain the control set and have all the + * control set information available. + * + * The function returns 0 if the transfer has been successfully initiated. + */ +int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb) +{ + int r; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct usb_device *udev = zd_usb_to_usbdev(usb); + struct urb *urb; + struct zd_usb_tx *tx = &usb->tx; + + if (!atomic_read(&tx->enabled)) { + r = -ENOENT; + goto out; + } + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + r = -ENOMEM; + goto out; + } + + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT), + skb->data, skb->len, tx_urb_complete, skb); + + info->rate_driver_data[1] = (void *)jiffies; + skb_queue_tail(&tx->submitted_skbs, skb); + usb_anchor_urb(urb, &tx->submitted); + + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) { + dev_dbg_f(zd_usb_dev(usb), "error submit urb %p %d\n", urb, r); + usb_unanchor_urb(urb); + skb_unlink(skb, &tx->submitted_skbs); + goto error; + } + tx_inc_submitted_urbs(usb); + return 0; +error: + usb_free_urb(urb); +out: + return r; +} + +static bool zd_tx_timeout(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + struct sk_buff_head *q = &tx->submitted_skbs; + struct sk_buff *skb, *skbnext; + struct ieee80211_tx_info *info; + unsigned long flags, trans_start; + bool have_timedout = false; + + spin_lock_irqsave(&q->lock, flags); + skb_queue_walk_safe(q, skb, skbnext) { + info = IEEE80211_SKB_CB(skb); + trans_start = (unsigned long)info->rate_driver_data[1]; + + if (time_is_before_jiffies(trans_start + ZD_TX_TIMEOUT)) { + have_timedout = true; + break; + } + } + spin_unlock_irqrestore(&q->lock, flags); + + return have_timedout; +} + +static void zd_tx_watchdog_handler(struct work_struct *work) +{ + struct zd_usb *usb = + container_of(work, struct zd_usb, tx.watchdog_work.work); + struct zd_usb_tx *tx = &usb->tx; + + if (!atomic_read(&tx->enabled) || !tx->watchdog_enabled) + goto out; + if (!zd_tx_timeout(usb)) + goto out; + + /* TX halted, try reset */ + dev_warn(zd_usb_dev(usb), "TX-stall detected, resetting device..."); + + usb_queue_reset_device(usb->intf); + + /* reset will stop this worker, don't rearm */ + return; +out: + queue_delayed_work(zd_workqueue, &tx->watchdog_work, + ZD_TX_WATCHDOG_INTERVAL); +} + +void zd_tx_watchdog_enable(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + + if (!tx->watchdog_enabled) { + dev_dbg_f(zd_usb_dev(usb), "\n"); + queue_delayed_work(zd_workqueue, &tx->watchdog_work, + ZD_TX_WATCHDOG_INTERVAL); + tx->watchdog_enabled = 1; + } +} + +void zd_tx_watchdog_disable(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + + if (tx->watchdog_enabled) { + dev_dbg_f(zd_usb_dev(usb), "\n"); + tx->watchdog_enabled = 0; + cancel_delayed_work_sync(&tx->watchdog_work); + } +} + +static void zd_rx_idle_timer_handler(struct work_struct *work) +{ + struct zd_usb *usb = + container_of(work, struct zd_usb, rx.idle_work.work); + struct zd_mac *mac = zd_usb_to_mac(usb); + + if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags)) + return; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + /* 30 seconds since last rx, reset rx */ + zd_usb_reset_rx(usb); +} + +static void zd_usb_reset_rx_idle_timer_tasklet(unsigned long param) +{ + struct zd_usb *usb = (struct zd_usb *)param; + + zd_usb_reset_rx_idle_timer(usb); +} + +void zd_usb_reset_rx_idle_timer(struct zd_usb *usb) +{ + struct zd_usb_rx *rx = &usb->rx; + + mod_delayed_work(zd_workqueue, &rx->idle_work, ZD_RX_IDLE_INTERVAL); +} + +static inline void init_usb_interrupt(struct zd_usb *usb) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock_init(&intr->lock); + intr->interval = int_urb_interval(zd_usb_to_usbdev(usb)); + init_completion(&intr->read_regs.completion); + atomic_set(&intr->read_regs_enabled, 0); + intr->read_regs.cr_int_addr = cpu_to_le16((u16)CR_INTERRUPT); +} + +static inline void init_usb_rx(struct zd_usb *usb) +{ + struct zd_usb_rx *rx = &usb->rx; + + spin_lock_init(&rx->lock); + mutex_init(&rx->setup_mutex); + if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) { + rx->usb_packet_size = 512; + } else { + rx->usb_packet_size = 64; + } + ZD_ASSERT(rx->fragment_length == 0); + INIT_DELAYED_WORK(&rx->idle_work, zd_rx_idle_timer_handler); + rx->reset_timer_tasklet.func = zd_usb_reset_rx_idle_timer_tasklet; + rx->reset_timer_tasklet.data = (unsigned long)usb; +} + +static inline void init_usb_tx(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + + spin_lock_init(&tx->lock); + atomic_set(&tx->enabled, 0); + tx->stopped = 0; + skb_queue_head_init(&tx->submitted_skbs); + init_usb_anchor(&tx->submitted); + tx->submitted_urbs = 0; + tx->watchdog_enabled = 0; + INIT_DELAYED_WORK(&tx->watchdog_work, zd_tx_watchdog_handler); +} + +void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw, + struct usb_interface *intf) +{ + memset(usb, 0, sizeof(*usb)); + usb->intf = usb_get_intf(intf); + usb_set_intfdata(usb->intf, hw); + init_usb_anchor(&usb->submitted_cmds); + init_usb_interrupt(usb); + init_usb_tx(usb); + init_usb_rx(usb); +} + +void zd_usb_clear(struct zd_usb *usb) +{ + usb_set_intfdata(usb->intf, NULL); + usb_put_intf(usb->intf); + ZD_MEMCLEAR(usb, sizeof(*usb)); + /* FIXME: usb_interrupt, usb_tx, usb_rx? */ +} + +static const char *speed(enum usb_device_speed speed) +{ + switch (speed) { + case USB_SPEED_LOW: + return "low"; + case USB_SPEED_FULL: + return "full"; + case USB_SPEED_HIGH: + return "high"; + default: + return "unknown speed"; + } +} + +static int scnprint_id(struct usb_device *udev, char *buffer, size_t size) +{ + return scnprintf(buffer, size, "%04hx:%04hx v%04hx %s", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + get_bcdDevice(udev), + speed(udev->speed)); +} + +int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size) +{ + struct usb_device *udev = interface_to_usbdev(usb->intf); + return scnprint_id(udev, buffer, size); +} + +#ifdef DEBUG +static void print_id(struct usb_device *udev) +{ + char buffer[40]; + + scnprint_id(udev, buffer, sizeof(buffer)); + buffer[sizeof(buffer)-1] = 0; + dev_dbg_f(&udev->dev, "%s\n", buffer); +} +#else +#define print_id(udev) do { } while (0) +#endif + +static int eject_installer(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *iface_desc = &intf->altsetting[0]; + struct usb_endpoint_descriptor *endpoint; + unsigned char *cmd; + u8 bulk_out_ep; + int r; + + /* Find bulk out endpoint */ + for (r = 1; r >= 0; r--) { + endpoint = &iface_desc->endpoint[r].desc; + if (usb_endpoint_dir_out(endpoint) && + usb_endpoint_xfer_bulk(endpoint)) { + bulk_out_ep = endpoint->bEndpointAddress; + break; + } + } + if (r == -1) { + dev_err(&udev->dev, + "zd1211rw: Could not find bulk out endpoint\n"); + return -ENODEV; + } + + cmd = kzalloc(31, GFP_KERNEL); + if (cmd == NULL) + return -ENODEV; + + /* USB bulk command block */ + cmd[0] = 0x55; /* bulk command signature */ + cmd[1] = 0x53; /* bulk command signature */ + cmd[2] = 0x42; /* bulk command signature */ + cmd[3] = 0x43; /* bulk command signature */ + cmd[14] = 6; /* command length */ + + cmd[15] = 0x1b; /* SCSI command: START STOP UNIT */ + cmd[19] = 0x2; /* eject disc */ + + dev_info(&udev->dev, "Ejecting virtual installer media...\n"); + r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_ep), + cmd, 31, NULL, 2000); + kfree(cmd); + if (r) + return r; + + /* At this point, the device disconnects and reconnects with the real + * ID numbers. */ + + usb_set_intfdata(intf, NULL); + return 0; +} + +int zd_usb_init_hw(struct zd_usb *usb) +{ + int r; + struct zd_mac *mac = zd_usb_to_mac(usb); + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + r = upload_firmware(usb); + if (r) { + dev_err(zd_usb_dev(usb), + "couldn't load firmware. Error number %d\n", r); + return r; + } + + r = usb_reset_configuration(zd_usb_to_usbdev(usb)); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "couldn't reset configuration. Error number %d\n", r); + return r; + } + + r = zd_mac_init_hw(mac->hw); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "couldn't initialize mac. Error number %d\n", r); + return r; + } + + usb->initialized = 1; + return 0; +} + +static int probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + int r; + struct usb_device *udev = interface_to_usbdev(intf); + struct zd_usb *usb; + struct ieee80211_hw *hw = NULL; + + print_id(udev); + + if (id->driver_info & DEVICE_INSTALLER) + return eject_installer(intf); + + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + break; + default: + dev_dbg_f(&intf->dev, "Unknown USB speed\n"); + r = -ENODEV; + goto error; + } + + r = usb_reset_device(udev); + if (r) { + dev_err(&intf->dev, + "couldn't reset usb device. Error number %d\n", r); + goto error; + } + + hw = zd_mac_alloc_hw(intf); + if (hw == NULL) { + r = -ENOMEM; + goto error; + } + + usb = &zd_hw_mac(hw)->chip.usb; + usb->is_zd1211b = (id->driver_info == DEVICE_ZD1211B) != 0; + + r = zd_mac_preinit_hw(hw); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't initialize mac. Error number %d\n", r); + goto error; + } + + r = ieee80211_register_hw(hw); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't register device. Error number %d\n", r); + goto error; + } + + dev_dbg_f(&intf->dev, "successful\n"); + dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy)); + return 0; +error: + usb_reset_device(interface_to_usbdev(intf)); + if (hw) { + zd_mac_clear(zd_hw_mac(hw)); + ieee80211_free_hw(hw); + } + return r; +} + +static void disconnect(struct usb_interface *intf) +{ + struct ieee80211_hw *hw = zd_intf_to_hw(intf); + struct zd_mac *mac; + struct zd_usb *usb; + + /* Either something really bad happened, or we're just dealing with + * a DEVICE_INSTALLER. */ + if (hw == NULL) + return; + + mac = zd_hw_mac(hw); + usb = &mac->chip.usb; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + ieee80211_unregister_hw(hw); + + /* Just in case something has gone wrong! */ + zd_usb_disable_tx(usb); + zd_usb_disable_rx(usb); + zd_usb_disable_int(usb); + + /* If the disconnect has been caused by a removal of the + * driver module, the reset allows reloading of the driver. If the + * reset will not be executed here, the upload of the firmware in the + * probe function caused by the reloading of the driver will fail. + */ + usb_reset_device(interface_to_usbdev(intf)); + + zd_mac_clear(mac); + ieee80211_free_hw(hw); + dev_dbg(&intf->dev, "disconnected\n"); +} + +static void zd_usb_resume(struct zd_usb *usb) +{ + struct zd_mac *mac = zd_usb_to_mac(usb); + int r; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + r = zd_op_start(zd_usb_to_hw(usb)); + if (r < 0) { + dev_warn(zd_usb_dev(usb), "Device resume failed " + "with error code %d. Retrying...\n", r); + if (usb->was_running) + set_bit(ZD_DEVICE_RUNNING, &mac->flags); + usb_queue_reset_device(usb->intf); + return; + } + + if (mac->type != NL80211_IFTYPE_UNSPECIFIED) { + r = zd_restore_settings(mac); + if (r < 0) { + dev_dbg(zd_usb_dev(usb), + "failed to restore settings, %d\n", r); + return; + } + } +} + +static void zd_usb_stop(struct zd_usb *usb) +{ + dev_dbg_f(zd_usb_dev(usb), "\n"); + + zd_op_stop(zd_usb_to_hw(usb)); + + zd_usb_disable_tx(usb); + zd_usb_disable_rx(usb); + zd_usb_disable_int(usb); + + usb->initialized = 0; +} + +static int pre_reset(struct usb_interface *intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(intf); + struct zd_mac *mac; + struct zd_usb *usb; + + if (!hw || intf->condition != USB_INTERFACE_BOUND) + return 0; + + mac = zd_hw_mac(hw); + usb = &mac->chip.usb; + + usb->was_running = test_bit(ZD_DEVICE_RUNNING, &mac->flags); + + zd_usb_stop(usb); + + mutex_lock(&mac->chip.mutex); + return 0; +} + +static int post_reset(struct usb_interface *intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(intf); + struct zd_mac *mac; + struct zd_usb *usb; + + if (!hw || intf->condition != USB_INTERFACE_BOUND) + return 0; + + mac = zd_hw_mac(hw); + usb = &mac->chip.usb; + + mutex_unlock(&mac->chip.mutex); + + if (usb->was_running) + zd_usb_resume(usb); + return 0; +} + +static struct usb_driver driver = { + .name = KBUILD_MODNAME, + .id_table = usb_ids, + .probe = probe, + .disconnect = disconnect, + .pre_reset = pre_reset, + .post_reset = post_reset, + .disable_hub_initiated_lpm = 1, +}; + +struct workqueue_struct *zd_workqueue; + +static int __init usb_init(void) +{ + int r; + + pr_debug("%s usb_init()\n", driver.name); + + zd_workqueue = create_singlethread_workqueue(driver.name); + if (zd_workqueue == NULL) { + printk(KERN_ERR "%s couldn't create workqueue\n", driver.name); + return -ENOMEM; + } + + r = usb_register(&driver); + if (r) { + destroy_workqueue(zd_workqueue); + printk(KERN_ERR "%s usb_register() failed. Error number %d\n", + driver.name, r); + return r; + } + + pr_debug("%s initialized\n", driver.name); + return 0; +} + +static void __exit usb_exit(void) +{ + pr_debug("%s usb_exit()\n", driver.name); + usb_deregister(&driver); + destroy_workqueue(zd_workqueue); +} + +module_init(usb_init); +module_exit(usb_exit); + +static int zd_ep_regs_out_msg(struct usb_device *udev, void *data, int len, + int *actual_length, int timeout) +{ + /* In USB 2.0 mode EP_REGS_OUT endpoint is interrupt type. However in + * USB 1.1 mode endpoint is bulk. Select correct type URB by endpoint + * descriptor. + */ + struct usb_host_endpoint *ep; + unsigned int pipe; + + pipe = usb_sndintpipe(udev, EP_REGS_OUT); + ep = usb_pipe_endpoint(udev, pipe); + if (!ep) + return -EINVAL; + + if (usb_endpoint_xfer_int(&ep->desc)) { + return usb_interrupt_msg(udev, pipe, data, len, + actual_length, timeout); + } else { + pipe = usb_sndbulkpipe(udev, EP_REGS_OUT); + return usb_bulk_msg(udev, pipe, data, len, actual_length, + timeout); + } +} + +static int usb_int_regs_length(unsigned int count) +{ + return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data); +} + +static void prepare_read_regs_int(struct zd_usb *usb, + struct usb_req_read_regs *req, + unsigned int count) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock_irq(&intr->lock); + atomic_set(&intr->read_regs_enabled, 1); + intr->read_regs.req = req; + intr->read_regs.req_count = count; + reinit_completion(&intr->read_regs.completion); + spin_unlock_irq(&intr->lock); +} + +static void disable_read_regs_int(struct zd_usb *usb) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock_irq(&intr->lock); + atomic_set(&intr->read_regs_enabled, 0); + spin_unlock_irq(&intr->lock); +} + +static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req, + unsigned int count) +{ + int i; + struct zd_usb_interrupt *intr = &usb->intr; + struct read_regs_int *rr = &intr->read_regs; + struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; + + /* The created block size seems to be larger than expected. + * However results appear to be correct. + */ + if (rr->length < usb_int_regs_length(count)) { + dev_dbg_f(zd_usb_dev(usb), + "error: actual length %d less than expected %d\n", + rr->length, usb_int_regs_length(count)); + return false; + } + + if (rr->length > sizeof(rr->buffer)) { + dev_dbg_f(zd_usb_dev(usb), + "error: actual length %d exceeds buffer size %zu\n", + rr->length, sizeof(rr->buffer)); + return false; + } + + for (i = 0; i < count; i++) { + struct reg_data *rd = ®s->regs[i]; + if (rd->addr != req->addr[i]) { + dev_dbg_f(zd_usb_dev(usb), + "rd[%d] addr %#06hx expected %#06hx\n", i, + le16_to_cpu(rd->addr), + le16_to_cpu(req->addr[i])); + return false; + } + } + + return true; +} + +static int get_results(struct zd_usb *usb, u16 *values, + struct usb_req_read_regs *req, unsigned int count, + bool *retry) +{ + int r; + int i; + struct zd_usb_interrupt *intr = &usb->intr; + struct read_regs_int *rr = &intr->read_regs; + struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; + + spin_lock_irq(&intr->lock); + + r = -EIO; + + /* Read failed because firmware bug? */ + *retry = !!intr->read_regs_int_overridden; + if (*retry) + goto error_unlock; + + if (!check_read_regs(usb, req, count)) { + dev_dbg_f(zd_usb_dev(usb), "error: invalid read regs\n"); + goto error_unlock; + } + + for (i = 0; i < count; i++) { + struct reg_data *rd = ®s->regs[i]; + values[i] = le16_to_cpu(rd->value); + } + + r = 0; +error_unlock: + spin_unlock_irq(&intr->lock); + return r; +} + +int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, + const zd_addr_t *addresses, unsigned int count) +{ + int r, i, req_len, actual_req_len, try_count = 0; + struct usb_device *udev; + struct usb_req_read_regs *req = NULL; + unsigned long timeout; + bool retry = false; + + if (count < 1) { + dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n"); + return -EINVAL; + } + if (count > USB_MAX_IOREAD16_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: count %u exceeds possible max %u\n", + count, USB_MAX_IOREAD16_COUNT); + return -EINVAL; + } + if (in_atomic()) { + dev_dbg_f(zd_usb_dev(usb), + "error: io in atomic context not supported\n"); + return -EWOULDBLOCK; + } + if (!usb_int_enabled(usb)) { + dev_dbg_f(zd_usb_dev(usb), + "error: usb interrupt not enabled\n"); + return -EWOULDBLOCK; + } + + ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); + BUILD_BUG_ON(sizeof(struct usb_req_read_regs) + USB_MAX_IOREAD16_COUNT * + sizeof(__le16) > sizeof(usb->req_buf)); + BUG_ON(sizeof(struct usb_req_read_regs) + count * sizeof(__le16) > + sizeof(usb->req_buf)); + + req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16); + req = (void *)usb->req_buf; + + req->id = cpu_to_le16(USB_REQ_READ_REGS); + for (i = 0; i < count; i++) + req->addr[i] = cpu_to_le16((u16)addresses[i]); + +retry_read: + try_count++; + udev = zd_usb_to_usbdev(usb); + prepare_read_regs_int(usb, req, count); + r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error in zd_ep_regs_out_msg(). Error number %d\n", r); + goto error; + } + if (req_len != actual_req_len) { + dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()\n" + " req_len %d != actual_req_len %d\n", + req_len, actual_req_len); + r = -EIO; + goto error; + } + + timeout = wait_for_completion_timeout(&usb->intr.read_regs.completion, + msecs_to_jiffies(50)); + if (!timeout) { + disable_read_regs_int(usb); + dev_dbg_f(zd_usb_dev(usb), "read timed out\n"); + r = -ETIMEDOUT; + goto error; + } + + r = get_results(usb, values, req, count, &retry); + if (retry && try_count < 20) { + dev_dbg_f(zd_usb_dev(usb), "read retry, tries so far: %d\n", + try_count); + goto retry_read; + } +error: + return r; +} + +static void iowrite16v_urb_complete(struct urb *urb) +{ + struct zd_usb *usb = urb->context; + + if (urb->status && !usb->cmd_error) + usb->cmd_error = urb->status; + + if (!usb->cmd_error && + urb->actual_length != urb->transfer_buffer_length) + usb->cmd_error = -EIO; +} + +static int zd_submit_waiting_urb(struct zd_usb *usb, bool last) +{ + int r = 0; + struct urb *urb = usb->urb_async_waiting; + + if (!urb) + return 0; + + usb->urb_async_waiting = NULL; + + if (!last) + urb->transfer_flags |= URB_NO_INTERRUPT; + + usb_anchor_urb(urb, &usb->submitted_cmds); + r = usb_submit_urb(urb, GFP_KERNEL); + if (r) { + usb_unanchor_urb(urb); + dev_dbg_f(zd_usb_dev(usb), + "error in usb_submit_urb(). Error number %d\n", r); + goto error; + } + + /* fall-through with r == 0 */ +error: + usb_free_urb(urb); + return r; +} + +void zd_usb_iowrite16v_async_start(struct zd_usb *usb) +{ + ZD_ASSERT(usb_anchor_empty(&usb->submitted_cmds)); + ZD_ASSERT(usb->urb_async_waiting == NULL); + ZD_ASSERT(!usb->in_async); + + ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); + + usb->in_async = 1; + usb->cmd_error = 0; + usb->urb_async_waiting = NULL; +} + +int zd_usb_iowrite16v_async_end(struct zd_usb *usb, unsigned int timeout) +{ + int r; + + ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); + ZD_ASSERT(usb->in_async); + + /* Submit last iowrite16v URB */ + r = zd_submit_waiting_urb(usb, true); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error in zd_submit_waiting_usb(). " + "Error number %d\n", r); + + usb_kill_anchored_urbs(&usb->submitted_cmds); + goto error; + } + + if (timeout) + timeout = usb_wait_anchor_empty_timeout(&usb->submitted_cmds, + timeout); + if (!timeout) { + usb_kill_anchored_urbs(&usb->submitted_cmds); + if (usb->cmd_error == -ENOENT) { + dev_dbg_f(zd_usb_dev(usb), "timed out"); + r = -ETIMEDOUT; + goto error; + } + } + + r = usb->cmd_error; +error: + usb->in_async = 0; + return r; +} + +int zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, + unsigned int count) +{ + int r; + struct usb_device *udev; + struct usb_req_write_regs *req = NULL; + int i, req_len; + struct urb *urb; + struct usb_host_endpoint *ep; + + ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); + ZD_ASSERT(usb->in_async); + + if (count == 0) + return 0; + if (count > USB_MAX_IOWRITE16_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: count %u exceeds possible max %u\n", + count, USB_MAX_IOWRITE16_COUNT); + return -EINVAL; + } + if (in_atomic()) { + dev_dbg_f(zd_usb_dev(usb), + "error: io in atomic context not supported\n"); + return -EWOULDBLOCK; + } + + udev = zd_usb_to_usbdev(usb); + + ep = usb_pipe_endpoint(udev, usb_sndintpipe(udev, EP_REGS_OUT)); + if (!ep) + return -ENOENT; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + req_len = sizeof(struct usb_req_write_regs) + + count * sizeof(struct reg_data); + req = kmalloc(req_len, GFP_KERNEL); + if (!req) { + r = -ENOMEM; + goto error; + } + + req->id = cpu_to_le16(USB_REQ_WRITE_REGS); + for (i = 0; i < count; i++) { + struct reg_data *rw = &req->reg_writes[i]; + rw->addr = cpu_to_le16((u16)ioreqs[i].addr); + rw->value = cpu_to_le16(ioreqs[i].value); + } + + /* In USB 2.0 mode endpoint is interrupt type. However in USB 1.1 mode + * endpoint is bulk. Select correct type URB by endpoint descriptor. + */ + if (usb_endpoint_xfer_int(&ep->desc)) + usb_fill_int_urb(urb, udev, usb_sndintpipe(udev, EP_REGS_OUT), + req, req_len, iowrite16v_urb_complete, usb, + ep->desc.bInterval); + else + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_REGS_OUT), + req, req_len, iowrite16v_urb_complete, usb); + + urb->transfer_flags |= URB_FREE_BUFFER; + + /* Submit previous URB */ + r = zd_submit_waiting_urb(usb, false); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error in zd_submit_waiting_usb(). " + "Error number %d\n", r); + goto error; + } + + /* Delay submit so that URB_NO_INTERRUPT flag can be set for all URBs + * of currect batch except for very last. + */ + usb->urb_async_waiting = urb; + return 0; +error: + usb_free_urb(urb); + return r; +} + +int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, + unsigned int count) +{ + int r; + + zd_usb_iowrite16v_async_start(usb); + r = zd_usb_iowrite16v_async(usb, ioreqs, count); + if (r) { + zd_usb_iowrite16v_async_end(usb, 0); + return r; + } + return zd_usb_iowrite16v_async_end(usb, 50 /* ms */); +} + +int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits) +{ + int r; + struct usb_device *udev; + struct usb_req_rfwrite *req = NULL; + int i, req_len, actual_req_len; + u16 bit_value_template; + + if (in_atomic()) { + dev_dbg_f(zd_usb_dev(usb), + "error: io in atomic context not supported\n"); + return -EWOULDBLOCK; + } + if (bits < USB_MIN_RFWRITE_BIT_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: bits %d are smaller than" + " USB_MIN_RFWRITE_BIT_COUNT %d\n", + bits, USB_MIN_RFWRITE_BIT_COUNT); + return -EINVAL; + } + if (bits > USB_MAX_RFWRITE_BIT_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: bits %d exceed USB_MAX_RFWRITE_BIT_COUNT %d\n", + bits, USB_MAX_RFWRITE_BIT_COUNT); + return -EINVAL; + } +#ifdef DEBUG + if (value & (~0UL << bits)) { + dev_dbg_f(zd_usb_dev(usb), + "error: value %#09x has bits >= %d set\n", + value, bits); + return -EINVAL; + } +#endif /* DEBUG */ + + dev_dbg_f(zd_usb_dev(usb), "value %#09x bits %d\n", value, bits); + + r = zd_usb_ioread16(usb, &bit_value_template, ZD_CR203); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error %d: Couldn't read ZD_CR203\n", r); + return r; + } + bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA); + + ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex)); + BUILD_BUG_ON(sizeof(struct usb_req_rfwrite) + + USB_MAX_RFWRITE_BIT_COUNT * sizeof(__le16) > + sizeof(usb->req_buf)); + BUG_ON(sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16) > + sizeof(usb->req_buf)); + + req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16); + req = (void *)usb->req_buf; + + req->id = cpu_to_le16(USB_REQ_WRITE_RF); + /* 1: 3683a, but not used in ZYDAS driver */ + req->value = cpu_to_le16(2); + req->bits = cpu_to_le16(bits); + + for (i = 0; i < bits; i++) { + u16 bv = bit_value_template; + if (value & (1 << (bits-1-i))) + bv |= RF_DATA; + req->bit_values[i] = cpu_to_le16(bv); + } + + udev = zd_usb_to_usbdev(usb); + r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error in zd_ep_regs_out_msg(). Error number %d\n", r); + goto out; + } + if (req_len != actual_req_len) { + dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()" + " req_len %d != actual_req_len %d\n", + req_len, actual_req_len); + r = -EIO; + goto out; + } + + /* FALL-THROUGH with r == 0 */ +out: + return r; +} diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.h b/drivers/net/wireless/zydas/zd1211rw/zd_usb.h new file mode 100644 index 000000000000..a9075f225178 --- /dev/null +++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.h @@ -0,0 +1,292 @@ +/* ZD1211 USB-WLAN driver for Linux + * + * Copyright (C) 2005-2007 Ulrich Kunitz + * Copyright (C) 2006-2007 Daniel Drake + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef _ZD_USB_H +#define _ZD_USB_H + +#include +#include +#include +#include +#include + +#include "zd_def.h" + +#define ZD_USB_TX_HIGH 5 +#define ZD_USB_TX_LOW 2 + +#define ZD_TX_TIMEOUT (HZ * 5) +#define ZD_TX_WATCHDOG_INTERVAL round_jiffies_relative(HZ) +#define ZD_RX_IDLE_INTERVAL round_jiffies_relative(30 * HZ) + +enum devicetype { + DEVICE_ZD1211 = 0, + DEVICE_ZD1211B = 1, + DEVICE_INSTALLER = 2, +}; + +enum endpoints { + EP_CTRL = 0, + EP_DATA_OUT = 1, + EP_DATA_IN = 2, + EP_INT_IN = 3, + EP_REGS_OUT = 4, +}; + +enum { + USB_MAX_TRANSFER_SIZE = 4096, /* bytes */ + /* FIXME: The original driver uses this value. We have to check, + * whether the MAX_TRANSFER_SIZE is sufficient and this needs only be + * used if one combined frame is split over two USB transactions. + */ + USB_MAX_RX_SIZE = 4800, /* bytes */ + USB_MAX_IOWRITE16_COUNT = 15, + USB_MAX_IOWRITE32_COUNT = USB_MAX_IOWRITE16_COUNT/2, + USB_MAX_IOREAD16_COUNT = 15, + USB_MAX_IOREAD32_COUNT = USB_MAX_IOREAD16_COUNT/2, + USB_MIN_RFWRITE_BIT_COUNT = 16, + USB_MAX_RFWRITE_BIT_COUNT = 28, + USB_MAX_EP_INT_BUFFER = 64, + USB_ZD1211B_BCD_DEVICE = 0x4810, +}; + +enum control_requests { + USB_REQ_WRITE_REGS = 0x21, + USB_REQ_READ_REGS = 0x22, + USB_REQ_WRITE_RF = 0x23, + USB_REQ_PROG_FLASH = 0x24, + USB_REQ_EEPROM_START = 0x0128, /* ? request is a byte */ + USB_REQ_EEPROM_MID = 0x28, + USB_REQ_EEPROM_END = 0x0228, /* ? request is a byte */ + USB_REQ_FIRMWARE_DOWNLOAD = 0x30, + USB_REQ_FIRMWARE_CONFIRM = 0x31, + USB_REQ_FIRMWARE_READ_DATA = 0x32, +}; + +struct usb_req_read_regs { + __le16 id; + __le16 addr[0]; +} __packed; + +struct reg_data { + __le16 addr; + __le16 value; +} __packed; + +struct usb_req_write_regs { + __le16 id; + struct reg_data reg_writes[0]; +} __packed; + +enum { + RF_IF_LE = 0x02, + RF_CLK = 0x04, + RF_DATA = 0x08, +}; + +struct usb_req_rfwrite { + __le16 id; + __le16 value; + /* 1: 3683a */ + /* 2: other (default) */ + __le16 bits; + /* RF2595: 24 */ + __le16 bit_values[0]; + /* (ZD_CR203 & ~(RF_IF_LE | RF_CLK | RF_DATA)) | (bit ? RF_DATA : 0) */ +} __packed; + +/* USB interrupt */ + +enum usb_int_id { + USB_INT_TYPE = 0x01, + USB_INT_ID_REGS = 0x90, + USB_INT_ID_RETRY_FAILED = 0xa0, +}; + +enum usb_int_flags { + USB_INT_READ_REGS_EN = 0x01, +}; + +struct usb_int_header { + u8 type; /* must always be 1 */ + u8 id; +} __packed; + +struct usb_int_regs { + struct usb_int_header hdr; + struct reg_data regs[0]; +} __packed; + +struct usb_int_retry_fail { + struct usb_int_header hdr; + u8 new_rate; + u8 _dummy; + u8 addr[ETH_ALEN]; + u8 ibss_wakeup_dest; +} __packed; + +struct read_regs_int { + struct completion completion; + struct usb_req_read_regs *req; + unsigned int req_count; + /* Stores the USB int structure and contains the USB address of the + * first requested register before request. + */ + u8 buffer[USB_MAX_EP_INT_BUFFER]; + int length; + __le16 cr_int_addr; +}; + +struct zd_ioreq16 { + zd_addr_t addr; + u16 value; +}; + +struct zd_ioreq32 { + zd_addr_t addr; + u32 value; +}; + +struct zd_usb_interrupt { + struct read_regs_int read_regs; + spinlock_t lock; + struct urb *urb; + void *buffer; + dma_addr_t buffer_dma; + int interval; + atomic_t read_regs_enabled; + u8 read_regs_int_overridden:1; +}; + +static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr) +{ + return (struct usb_int_regs *)intr->read_regs.buffer; +} + +#define RX_URBS_COUNT 5 + +struct zd_usb_rx { + spinlock_t lock; + struct mutex setup_mutex; + struct delayed_work idle_work; + struct tasklet_struct reset_timer_tasklet; + u8 fragment[2 * USB_MAX_RX_SIZE]; + unsigned int fragment_length; + unsigned int usb_packet_size; + struct urb **urbs; + int urbs_count; +}; + +/** + * struct zd_usb_tx - structure used for transmitting frames + * @enabled: atomic enabled flag, indicates whether tx is enabled + * @lock: lock for transmission + * @submitted: anchor for URBs sent to device + * @submitted_urbs: atomic integer that counts the URBs having sent to the + * device, which haven't been completed + * @stopped: indicates whether higher level tx queues are stopped + */ +struct zd_usb_tx { + atomic_t enabled; + spinlock_t lock; + struct delayed_work watchdog_work; + struct sk_buff_head submitted_skbs; + struct usb_anchor submitted; + int submitted_urbs; + u8 stopped:1, watchdog_enabled:1; +}; + +/* Contains the usb parts. The structure doesn't require a lock because intf + * will not be changed after initialization. + */ +struct zd_usb { + struct zd_usb_interrupt intr; + struct zd_usb_rx rx; + struct zd_usb_tx tx; + struct usb_interface *intf; + struct usb_anchor submitted_cmds; + struct urb *urb_async_waiting; + int cmd_error; + u8 req_buf[64]; /* zd_usb_iowrite16v needs 62 bytes */ + u8 is_zd1211b:1, initialized:1, was_running:1, in_async:1; +}; + +#define zd_usb_dev(usb) (&usb->intf->dev) + +static inline struct usb_device *zd_usb_to_usbdev(struct zd_usb *usb) +{ + return interface_to_usbdev(usb->intf); +} + +static inline struct ieee80211_hw *zd_intf_to_hw(struct usb_interface *intf) +{ + return usb_get_intfdata(intf); +} + +static inline struct ieee80211_hw *zd_usb_to_hw(struct zd_usb *usb) +{ + return zd_intf_to_hw(usb->intf); +} + +void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw, + struct usb_interface *intf); +int zd_usb_init_hw(struct zd_usb *usb); +void zd_usb_clear(struct zd_usb *usb); + +int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size); + +void zd_tx_watchdog_enable(struct zd_usb *usb); +void zd_tx_watchdog_disable(struct zd_usb *usb); + +int zd_usb_enable_int(struct zd_usb *usb); +void zd_usb_disable_int(struct zd_usb *usb); + +int zd_usb_enable_rx(struct zd_usb *usb); +void zd_usb_disable_rx(struct zd_usb *usb); + +void zd_usb_reset_rx_idle_timer(struct zd_usb *usb); + +void zd_usb_enable_tx(struct zd_usb *usb); +void zd_usb_disable_tx(struct zd_usb *usb); + +int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb); + +int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, + const zd_addr_t *addresses, unsigned int count); + +static inline int zd_usb_ioread16(struct zd_usb *usb, u16 *value, + const zd_addr_t addr) +{ + return zd_usb_ioread16v(usb, value, &addr, 1); +} + +void zd_usb_iowrite16v_async_start(struct zd_usb *usb); +int zd_usb_iowrite16v_async_end(struct zd_usb *usb, unsigned int timeout); +int zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, + unsigned int count); +int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, + unsigned int count); + +int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits); + +int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len); + +extern struct workqueue_struct *zd_workqueue; + +#endif /* _ZD_USB_H */ -- cgit v1.2.3-59-g8ed1b From eb4f98d5deb9b3a36e8b3ab7a9dec1b5ebc76e75 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 09:42:58 +0200 Subject: hostap: move under intersil vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 2 +- drivers/net/wireless/Makefile | 2 +- drivers/net/wireless/hostap/Kconfig | 98 - drivers/net/wireless/hostap/Makefile | 7 - drivers/net/wireless/hostap/hostap.h | 95 - drivers/net/wireless/hostap/hostap_80211.h | 96 - drivers/net/wireless/hostap/hostap_80211_rx.c | 1117 ------ drivers/net/wireless/hostap/hostap_80211_tx.c | 553 --- drivers/net/wireless/hostap/hostap_ap.c | 3339 ---------------- drivers/net/wireless/hostap/hostap_ap.h | 263 -- drivers/net/wireless/hostap/hostap_common.h | 419 -- drivers/net/wireless/hostap/hostap_config.h | 48 - drivers/net/wireless/hostap/hostap_cs.c | 708 ---- drivers/net/wireless/hostap/hostap_download.c | 812 ---- drivers/net/wireless/hostap/hostap_hw.c | 3424 ----------------- drivers/net/wireless/hostap/hostap_info.c | 507 --- drivers/net/wireless/hostap/hostap_ioctl.c | 4054 -------------------- drivers/net/wireless/hostap/hostap_main.c | 1140 ------ drivers/net/wireless/hostap/hostap_pci.c | 459 --- drivers/net/wireless/hostap/hostap_plx.c | 618 --- drivers/net/wireless/hostap/hostap_proc.c | 499 --- drivers/net/wireless/hostap/hostap_wlan.h | 1047 ----- drivers/net/wireless/intersil/Kconfig | 16 + drivers/net/wireless/intersil/Makefile | 1 + drivers/net/wireless/intersil/hostap/Kconfig | 98 + drivers/net/wireless/intersil/hostap/Makefile | 7 + drivers/net/wireless/intersil/hostap/hostap.h | 95 + .../net/wireless/intersil/hostap/hostap_80211.h | 96 + .../net/wireless/intersil/hostap/hostap_80211_rx.c | 1117 ++++++ .../net/wireless/intersil/hostap/hostap_80211_tx.c | 553 +++ drivers/net/wireless/intersil/hostap/hostap_ap.c | 3339 ++++++++++++++++ drivers/net/wireless/intersil/hostap/hostap_ap.h | 263 ++ .../net/wireless/intersil/hostap/hostap_common.h | 419 ++ .../net/wireless/intersil/hostap/hostap_config.h | 48 + drivers/net/wireless/intersil/hostap/hostap_cs.c | 708 ++++ .../net/wireless/intersil/hostap/hostap_download.c | 812 ++++ drivers/net/wireless/intersil/hostap/hostap_hw.c | 3424 +++++++++++++++++ drivers/net/wireless/intersil/hostap/hostap_info.c | 507 +++ .../net/wireless/intersil/hostap/hostap_ioctl.c | 4054 ++++++++++++++++++++ drivers/net/wireless/intersil/hostap/hostap_main.c | 1140 ++++++ drivers/net/wireless/intersil/hostap/hostap_pci.c | 459 +++ drivers/net/wireless/intersil/hostap/hostap_plx.c | 618 +++ drivers/net/wireless/intersil/hostap/hostap_proc.c | 499 +++ drivers/net/wireless/intersil/hostap/hostap_wlan.h | 1047 +++++ 45 files changed, 19323 insertions(+), 19306 deletions(-) delete mode 100644 drivers/net/wireless/hostap/Kconfig delete mode 100644 drivers/net/wireless/hostap/Makefile delete mode 100644 drivers/net/wireless/hostap/hostap.h delete mode 100644 drivers/net/wireless/hostap/hostap_80211.h delete mode 100644 drivers/net/wireless/hostap/hostap_80211_rx.c delete mode 100644 drivers/net/wireless/hostap/hostap_80211_tx.c delete mode 100644 drivers/net/wireless/hostap/hostap_ap.c delete mode 100644 drivers/net/wireless/hostap/hostap_ap.h delete mode 100644 drivers/net/wireless/hostap/hostap_common.h delete mode 100644 drivers/net/wireless/hostap/hostap_config.h delete mode 100644 drivers/net/wireless/hostap/hostap_cs.c delete mode 100644 drivers/net/wireless/hostap/hostap_download.c delete mode 100644 drivers/net/wireless/hostap/hostap_hw.c delete mode 100644 drivers/net/wireless/hostap/hostap_info.c delete mode 100644 drivers/net/wireless/hostap/hostap_ioctl.c delete mode 100644 drivers/net/wireless/hostap/hostap_main.c delete mode 100644 drivers/net/wireless/hostap/hostap_pci.c delete mode 100644 drivers/net/wireless/hostap/hostap_plx.c delete mode 100644 drivers/net/wireless/hostap/hostap_proc.c delete mode 100644 drivers/net/wireless/hostap/hostap_wlan.h create mode 100644 drivers/net/wireless/intersil/Kconfig create mode 100644 drivers/net/wireless/intersil/Makefile create mode 100644 drivers/net/wireless/intersil/hostap/Kconfig create mode 100644 drivers/net/wireless/intersil/hostap/Makefile create mode 100644 drivers/net/wireless/intersil/hostap/hostap.h create mode 100644 drivers/net/wireless/intersil/hostap/hostap_80211.h create mode 100644 drivers/net/wireless/intersil/hostap/hostap_80211_rx.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_80211_tx.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_ap.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_ap.h create mode 100644 drivers/net/wireless/intersil/hostap/hostap_common.h create mode 100644 drivers/net/wireless/intersil/hostap/hostap_config.h create mode 100644 drivers/net/wireless/intersil/hostap/hostap_cs.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_download.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_hw.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_info.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_ioctl.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_main.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_pci.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_plx.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_proc.c create mode 100644 drivers/net/wireless/intersil/hostap/hostap_wlan.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index f2e78420488c..63c601b04e68 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5040,7 +5040,7 @@ L: hostap@shmoo.com (subscribers-only) L: linux-wireless@vger.kernel.org W: http://hostap.epitest.fi/ S: Maintained -F: drivers/net/wireless/hostap/ +F: drivers/net/wireless/intersil/hostap/ HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER L: platform-driver-x86@vger.kernel.org diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index e86dcdd5cfcb..da10b00d8af1 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -22,6 +22,7 @@ source "drivers/net/wireless/atmel/Kconfig" source "drivers/net/wireless/broadcom/Kconfig" source "drivers/net/wireless/cisco/Kconfig" source "drivers/net/wireless/intel/Kconfig" +source "drivers/net/wireless/intersil/Kconfig" source "drivers/net/wireless/marvell/Kconfig" source "drivers/net/wireless/st/Kconfig" source "drivers/net/wireless/zydas/Kconfig" @@ -116,7 +117,6 @@ config MAC80211_HWSIM called mac80211_hwsim. If unsure, say N. source "drivers/net/wireless/ath/Kconfig" -source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/orinoco/Kconfig" source "drivers/net/wireless/p54/Kconfig" source "drivers/net/wireless/rt2x00/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 8f54adde831e..a60c5e55b55e 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/ obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/ +obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/ obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ @@ -15,7 +16,6 @@ obj-$(CONFIG_HERMES) += orinoco/ obj-$(CONFIG_PRISM54) += prism54/ -obj-$(CONFIG_HOSTAP) += hostap/ obj-$(CONFIG_WLAN) += realtek/ # 16-bit wireless PCMCIA client drivers diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig deleted file mode 100644 index 287d82728bc3..000000000000 --- a/drivers/net/wireless/hostap/Kconfig +++ /dev/null @@ -1,98 +0,0 @@ -config HOSTAP - tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)" - select WIRELESS_EXT - select WEXT_SPY - select WEXT_PRIV - select CRYPTO - select CRYPTO_ARC4 - select CRYPTO_ECB - select CRYPTO_AES - select CRYPTO_MICHAEL_MIC - select CRYPTO_ECB - select CRC32 - select LIB80211 - select LIB80211_CRYPT_WEP - select LIB80211_CRYPT_TKIP - select LIB80211_CRYPT_CCMP - ---help--- - Shared driver code for IEEE 802.11b wireless cards based on - Intersil Prism2/2.5/3 chipset. This driver supports so called - Host AP mode that allows the card to act as an IEEE 802.11 - access point. - - See for more information about the - Host AP driver configuration and tools. This site includes - information and tools (hostapd and wpa_supplicant) for WPA/WPA2 - support. - - This option includes the base Host AP driver code that is shared by - different hardware models. You will also need to enable support for - PLX/PCI/CS version of the driver to actually use the driver. - - The driver can be compiled as a module and it will be called - hostap. - -config HOSTAP_FIRMWARE - bool "Support downloading firmware images with Host AP driver" - depends on HOSTAP - ---help--- - Configure Host AP driver to include support for firmware image - download. This option by itself only enables downloading to the - volatile memory, i.e. the card RAM. This option is required to - support cards that don't have firmware in flash, such as D-Link - DWL-520 rev E and D-Link DWL-650 rev P. - - Firmware image downloading needs a user space tool, prism2_srec. - It is available from http://hostap.epitest.fi/. - -config HOSTAP_FIRMWARE_NVRAM - bool "Support for non-volatile firmware download" - depends on HOSTAP_FIRMWARE - ---help--- - Allow Host AP driver to write firmware images to the non-volatile - card memory, i.e. flash memory that survives power cycling. - Enable this option if you want to be able to change card firmware - permanently. - - Firmware image downloading needs a user space tool, prism2_srec. - It is available from http://hostap.epitest.fi/. - -config HOSTAP_PLX - tristate "Host AP driver for Prism2/2.5/3 in PLX9052 PCI adaptors" - depends on PCI && HOSTAP - ---help--- - Host AP driver's version for Prism2/2.5/3 PC Cards in PLX9052 based - PCI adaptors. - - "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this - driver and its help text includes more information about the Host AP - driver. - - The driver can be compiled as a module and will be named - hostap_plx. - -config HOSTAP_PCI - tristate "Host AP driver for Prism2.5 PCI adaptors" - depends on PCI && HOSTAP - ---help--- - Host AP driver's version for Prism2.5 PCI adaptors. - - "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this - driver and its help text includes more information about the Host AP - driver. - - The driver can be compiled as a module and will be named - hostap_pci. - -config HOSTAP_CS - tristate "Host AP driver for Prism2/2.5/3 PC Cards" - depends on PCMCIA && HOSTAP - ---help--- - Host AP driver's version for Prism2/2.5/3 PC Cards. - - "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this - driver and its help text includes more information about the Host AP - driver. - - The driver can be compiled as a module and will be named - hostap_cs. diff --git a/drivers/net/wireless/hostap/Makefile b/drivers/net/wireless/hostap/Makefile deleted file mode 100644 index b8e41a702c00..000000000000 --- a/drivers/net/wireless/hostap/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -hostap-y := hostap_80211_rx.o hostap_80211_tx.o hostap_ap.o hostap_info.o \ - hostap_ioctl.o hostap_main.o hostap_proc.o -obj-$(CONFIG_HOSTAP) += hostap.o - -obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o -obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o -obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o diff --git a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/hostap/hostap.h deleted file mode 100644 index ce8721fbc10e..000000000000 --- a/drivers/net/wireless/hostap/hostap.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef HOSTAP_H -#define HOSTAP_H - -#include -#include - -#include "hostap_wlan.h" -#include "hostap_ap.h" - -static const long freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, - 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; -#define FREQ_COUNT ARRAY_SIZE(freq_list) - -/* hostap.c */ - -extern struct proc_dir_entry *hostap_proc; - -u16 hostap_tx_callback_register(local_info_t *local, - void (*func)(struct sk_buff *, int ok, void *), - void *data); -int hostap_tx_callback_unregister(local_info_t *local, u16 idx); -int hostap_set_word(struct net_device *dev, int rid, u16 val); -int hostap_set_string(struct net_device *dev, int rid, const char *val); -u16 hostap_get_porttype(local_info_t *local); -int hostap_set_encryption(local_info_t *local); -int hostap_set_antsel(local_info_t *local); -int hostap_set_roaming(local_info_t *local); -int hostap_set_auth_algs(local_info_t *local); -void hostap_dump_rx_header(const char *name, - const struct hfa384x_rx_frame *rx); -void hostap_dump_tx_header(const char *name, - const struct hfa384x_tx_frame *tx); -extern const struct header_ops hostap_80211_ops; -int hostap_80211_get_hdrlen(__le16 fc); -struct net_device_stats *hostap_get_stats(struct net_device *dev); -void hostap_setup_dev(struct net_device *dev, local_info_t *local, - int type); -void hostap_set_multicast_list_queue(struct work_struct *work); -int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked); -int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked); -void hostap_cleanup(local_info_t *local); -void hostap_cleanup_handler(void *data); -struct net_device * hostap_add_interface(struct local_info *local, - int type, int rtnl_locked, - const char *prefix, const char *name); -void hostap_remove_interface(struct net_device *dev, int rtnl_locked, - int remove_from_list); -int prism2_update_comms_qual(struct net_device *dev); -int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype, - u8 *body, size_t bodylen); -int prism2_sta_deauth(local_info_t *local, u16 reason); -int prism2_wds_add(local_info_t *local, u8 *remote_addr, - int rtnl_locked); -int prism2_wds_del(local_info_t *local, u8 *remote_addr, - int rtnl_locked, int do_not_remove); - - -/* hostap_ap.c */ - -int ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac); -int ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac); -void ap_control_flush_macs(struct mac_restrictions *mac_restrictions); -int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac); -void ap_control_kickall(struct ap_data *ap); -void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, - struct lib80211_crypt_data ***crypt); -int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], - struct iw_quality qual[], int buf_size, - int aplist); -int prism2_ap_translate_scan(struct net_device *dev, - struct iw_request_info *info, char *buffer); -int prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param); - - -/* hostap_proc.c */ - -void hostap_init_proc(local_info_t *local); -void hostap_remove_proc(local_info_t *local); - - -/* hostap_info.c */ - -void hostap_info_init(local_info_t *local); -void hostap_info_process(local_info_t *local, struct sk_buff *skb); - - -/* hostap_ioctl.c */ - -extern const struct iw_handler_def hostap_iw_handler_def; -extern const struct ethtool_ops prism2_ethtool_ops; - -int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); - - -#endif /* HOSTAP_H */ diff --git a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/hostap/hostap_80211.h deleted file mode 100644 index ed98ce7c8f65..000000000000 --- a/drivers/net/wireless/hostap/hostap_80211.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef HOSTAP_80211_H -#define HOSTAP_80211_H - -#include -#include -#include - -struct hostap_ieee80211_mgmt { - __le16 frame_control; - __le16 duration; - u8 da[6]; - u8 sa[6]; - u8 bssid[6]; - __le16 seq_ctrl; - union { - struct { - __le16 auth_alg; - __le16 auth_transaction; - __le16 status_code; - /* possibly followed by Challenge text */ - u8 variable[0]; - } __packed auth; - struct { - __le16 reason_code; - } __packed deauth; - struct { - __le16 capab_info; - __le16 listen_interval; - /* followed by SSID and Supported rates */ - u8 variable[0]; - } __packed assoc_req; - struct { - __le16 capab_info; - __le16 status_code; - __le16 aid; - /* followed by Supported rates */ - u8 variable[0]; - } __packed assoc_resp, reassoc_resp; - struct { - __le16 capab_info; - __le16 listen_interval; - u8 current_ap[6]; - /* followed by SSID and Supported rates */ - u8 variable[0]; - } __packed reassoc_req; - struct { - __le16 reason_code; - } __packed disassoc; - struct { - } __packed probe_req; - struct { - u8 timestamp[8]; - __le16 beacon_int; - __le16 capab_info; - /* followed by some of SSID, Supported rates, - * FH Params, DS Params, CF Params, IBSS Params, TIM */ - u8 variable[0]; - } __packed beacon, probe_resp; - } u; -} __packed; - - -#define IEEE80211_MGMT_HDR_LEN 24 -#define IEEE80211_DATA_HDR3_LEN 24 -#define IEEE80211_DATA_HDR4_LEN 30 - - -struct hostap_80211_rx_status { - u32 mac_time; - u8 signal; - u8 noise; - u16 rate; /* in 100 kbps */ -}; - -/* prism2_rx_80211 'type' argument */ -enum { - PRISM2_RX_MONITOR, PRISM2_RX_MGMT, PRISM2_RX_NON_ASSOC, - PRISM2_RX_NULLFUNC_ACK -}; - -int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats, int type); -void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats); -void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats); - -void hostap_dump_tx_80211(const char *name, struct sk_buff *skb); -netdev_tx_t hostap_data_start_xmit(struct sk_buff *skb, - struct net_device *dev); -netdev_tx_t hostap_mgmt_start_xmit(struct sk_buff *skb, - struct net_device *dev); -netdev_tx_t hostap_master_start_xmit(struct sk_buff *skb, - struct net_device *dev); - -#endif /* HOSTAP_80211_H */ diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c deleted file mode 100644 index 599f30f22841..000000000000 --- a/drivers/net/wireless/hostap/hostap_80211_rx.c +++ /dev/null @@ -1,1117 +0,0 @@ -#include -#include -#include -#include -#include - -#include "hostap_80211.h" -#include "hostap.h" -#include "hostap_ap.h" - -/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ -/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ -static unsigned char rfc1042_header[] = -{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; -/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ -static unsigned char bridge_tunnel_header[] = -{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; -/* No encapsulation header if EtherType < 0x600 (=length) */ - -void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats) -{ - struct ieee80211_hdr *hdr; - u16 fc; - - hdr = (struct ieee80211_hdr *) skb->data; - - printk(KERN_DEBUG "%s: RX signal=%d noise=%d rate=%d len=%d " - "jiffies=%ld\n", - name, rx_stats->signal, rx_stats->noise, rx_stats->rate, - skb->len, jiffies); - - if (skb->len < 2) - return; - - fc = le16_to_cpu(hdr->frame_control); - printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s", - fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, - (fc & IEEE80211_FCTL_STYPE) >> 4, - fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", - fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); - - if (skb->len < IEEE80211_DATA_HDR3_LEN) { - printk("\n"); - return; - } - - printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id), - le16_to_cpu(hdr->seq_ctrl)); - - printk(KERN_DEBUG " A1=%pM", hdr->addr1); - printk(" A2=%pM", hdr->addr2); - printk(" A3=%pM", hdr->addr3); - if (skb->len >= 30) - printk(" A4=%pM", hdr->addr4); - printk("\n"); -} - - -/* Send RX frame to netif with 802.11 (and possible prism) header. - * Called from hardware or software IRQ context. */ -int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats, int type) -{ - struct hostap_interface *iface; - local_info_t *local; - int hdrlen, phdrlen, head_need, tail_need; - u16 fc; - int prism_header, ret; - struct ieee80211_hdr *fhdr; - - iface = netdev_priv(dev); - local = iface->local; - - if (dev->type == ARPHRD_IEEE80211_PRISM) { - if (local->monitor_type == PRISM2_MONITOR_PRISM) { - prism_header = 1; - phdrlen = sizeof(struct linux_wlan_ng_prism_hdr); - } else { /* local->monitor_type == PRISM2_MONITOR_CAPHDR */ - prism_header = 2; - phdrlen = sizeof(struct linux_wlan_ng_cap_hdr); - } - } else if (dev->type == ARPHRD_IEEE80211_RADIOTAP) { - prism_header = 3; - phdrlen = sizeof(struct hostap_radiotap_rx); - } else { - prism_header = 0; - phdrlen = 0; - } - - fhdr = (struct ieee80211_hdr *) skb->data; - fc = le16_to_cpu(fhdr->frame_control); - - if (type == PRISM2_RX_MGMT && (fc & IEEE80211_FCTL_VERS)) { - printk(KERN_DEBUG "%s: dropped management frame with header " - "version %d\n", dev->name, fc & IEEE80211_FCTL_VERS); - dev_kfree_skb_any(skb); - return 0; - } - - hdrlen = hostap_80211_get_hdrlen(fhdr->frame_control); - - /* check if there is enough room for extra data; if not, expand skb - * buffer to be large enough for the changes */ - head_need = phdrlen; - tail_need = 0; -#ifdef PRISM2_ADD_BOGUS_CRC - tail_need += 4; -#endif /* PRISM2_ADD_BOGUS_CRC */ - - head_need -= skb_headroom(skb); - tail_need -= skb_tailroom(skb); - - if (head_need > 0 || tail_need > 0) { - if (pskb_expand_head(skb, head_need > 0 ? head_need : 0, - tail_need > 0 ? tail_need : 0, - GFP_ATOMIC)) { - printk(KERN_DEBUG "%s: prism2_rx_80211 failed to " - "reallocate skb buffer\n", dev->name); - dev_kfree_skb_any(skb); - return 0; - } - } - - /* We now have an skb with enough head and tail room, so just insert - * the extra data */ - -#ifdef PRISM2_ADD_BOGUS_CRC - memset(skb_put(skb, 4), 0xff, 4); /* Prism2 strips CRC */ -#endif /* PRISM2_ADD_BOGUS_CRC */ - - if (prism_header == 1) { - struct linux_wlan_ng_prism_hdr *hdr; - hdr = (struct linux_wlan_ng_prism_hdr *) - skb_push(skb, phdrlen); - memset(hdr, 0, phdrlen); - hdr->msgcode = LWNG_CAP_DID_BASE; - hdr->msglen = sizeof(*hdr); - memcpy(hdr->devname, dev->name, sizeof(hdr->devname)); -#define LWNG_SETVAL(f,i,s,l,d) \ -hdr->f.did = LWNG_CAP_DID_BASE | (i << 12); \ -hdr->f.status = s; hdr->f.len = l; hdr->f.data = d - LWNG_SETVAL(hosttime, 1, 0, 4, jiffies); - LWNG_SETVAL(mactime, 2, 0, 4, rx_stats->mac_time); - LWNG_SETVAL(channel, 3, 1 /* no value */, 4, 0); - LWNG_SETVAL(rssi, 4, 1 /* no value */, 4, 0); - LWNG_SETVAL(sq, 5, 1 /* no value */, 4, 0); - LWNG_SETVAL(signal, 6, 0, 4, rx_stats->signal); - LWNG_SETVAL(noise, 7, 0, 4, rx_stats->noise); - LWNG_SETVAL(rate, 8, 0, 4, rx_stats->rate / 5); - LWNG_SETVAL(istx, 9, 0, 4, 0); - LWNG_SETVAL(frmlen, 10, 0, 4, skb->len - phdrlen); -#undef LWNG_SETVAL - } else if (prism_header == 2) { - struct linux_wlan_ng_cap_hdr *hdr; - hdr = (struct linux_wlan_ng_cap_hdr *) - skb_push(skb, phdrlen); - memset(hdr, 0, phdrlen); - hdr->version = htonl(LWNG_CAPHDR_VERSION); - hdr->length = htonl(phdrlen); - hdr->mactime = __cpu_to_be64(rx_stats->mac_time); - hdr->hosttime = __cpu_to_be64(jiffies); - hdr->phytype = htonl(4); /* dss_dot11_b */ - hdr->channel = htonl(local->channel); - hdr->datarate = htonl(rx_stats->rate); - hdr->antenna = htonl(0); /* unknown */ - hdr->priority = htonl(0); /* unknown */ - hdr->ssi_type = htonl(3); /* raw */ - hdr->ssi_signal = htonl(rx_stats->signal); - hdr->ssi_noise = htonl(rx_stats->noise); - hdr->preamble = htonl(0); /* unknown */ - hdr->encoding = htonl(1); /* cck */ - } else if (prism_header == 3) { - struct hostap_radiotap_rx *hdr; - hdr = (struct hostap_radiotap_rx *)skb_push(skb, phdrlen); - memset(hdr, 0, phdrlen); - hdr->hdr.it_len = cpu_to_le16(phdrlen); - hdr->hdr.it_present = - cpu_to_le32((1 << IEEE80211_RADIOTAP_TSFT) | - (1 << IEEE80211_RADIOTAP_CHANNEL) | - (1 << IEEE80211_RADIOTAP_RATE) | - (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | - (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)); - hdr->tsft = cpu_to_le64(rx_stats->mac_time); - hdr->chan_freq = cpu_to_le16(freq_list[local->channel - 1]); - hdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_CCK | - IEEE80211_CHAN_2GHZ); - hdr->rate = rx_stats->rate / 5; - hdr->dbm_antsignal = rx_stats->signal; - hdr->dbm_antnoise = rx_stats->noise; - } - - ret = skb->len - phdrlen; - skb->dev = dev; - skb_reset_mac_header(skb); - skb_pull(skb, hdrlen); - if (prism_header) - skb_pull(skb, phdrlen); - skb->pkt_type = PACKET_OTHERHOST; - skb->protocol = cpu_to_be16(ETH_P_802_2); - memset(skb->cb, 0, sizeof(skb->cb)); - netif_rx(skb); - - return ret; -} - - -/* Called only as a tasklet (software IRQ) */ -static void monitor_rx(struct net_device *dev, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats) -{ - int len; - - len = prism2_rx_80211(dev, skb, rx_stats, PRISM2_RX_MONITOR); - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; -} - - -/* Called only as a tasklet (software IRQ) */ -static struct prism2_frag_entry * -prism2_frag_cache_find(local_info_t *local, unsigned int seq, - unsigned int frag, u8 *src, u8 *dst) -{ - struct prism2_frag_entry *entry; - int i; - - for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) { - entry = &local->frag_cache[i]; - if (entry->skb != NULL && - time_after(jiffies, entry->first_frag_time + 2 * HZ)) { - printk(KERN_DEBUG "%s: expiring fragment cache entry " - "seq=%u last_frag=%u\n", - local->dev->name, entry->seq, entry->last_frag); - dev_kfree_skb(entry->skb); - entry->skb = NULL; - } - - if (entry->skb != NULL && entry->seq == seq && - (entry->last_frag + 1 == frag || frag == -1) && - memcmp(entry->src_addr, src, ETH_ALEN) == 0 && - memcmp(entry->dst_addr, dst, ETH_ALEN) == 0) - return entry; - } - - return NULL; -} - - -/* Called only as a tasklet (software IRQ) */ -static struct sk_buff * -prism2_frag_cache_get(local_info_t *local, struct ieee80211_hdr *hdr) -{ - struct sk_buff *skb = NULL; - u16 sc; - unsigned int frag, seq; - struct prism2_frag_entry *entry; - - sc = le16_to_cpu(hdr->seq_ctrl); - frag = sc & IEEE80211_SCTL_FRAG; - seq = (sc & IEEE80211_SCTL_SEQ) >> 4; - - if (frag == 0) { - /* Reserve enough space to fit maximum frame length */ - skb = dev_alloc_skb(local->dev->mtu + - sizeof(struct ieee80211_hdr) + - 8 /* LLC */ + - 2 /* alignment */ + - 8 /* WEP */ + ETH_ALEN /* WDS */); - if (skb == NULL) - return NULL; - - entry = &local->frag_cache[local->frag_next_idx]; - local->frag_next_idx++; - if (local->frag_next_idx >= PRISM2_FRAG_CACHE_LEN) - local->frag_next_idx = 0; - - if (entry->skb != NULL) - dev_kfree_skb(entry->skb); - - entry->first_frag_time = jiffies; - entry->seq = seq; - entry->last_frag = frag; - entry->skb = skb; - memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); - memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); - } else { - /* received a fragment of a frame for which the head fragment - * should have already been received */ - entry = prism2_frag_cache_find(local, seq, frag, hdr->addr2, - hdr->addr1); - if (entry != NULL) { - entry->last_frag = frag; - skb = entry->skb; - } - } - - return skb; -} - - -/* Called only as a tasklet (software IRQ) */ -static int prism2_frag_cache_invalidate(local_info_t *local, - struct ieee80211_hdr *hdr) -{ - u16 sc; - unsigned int seq; - struct prism2_frag_entry *entry; - - sc = le16_to_cpu(hdr->seq_ctrl); - seq = (sc & IEEE80211_SCTL_SEQ) >> 4; - - entry = prism2_frag_cache_find(local, seq, -1, hdr->addr2, hdr->addr1); - - if (entry == NULL) { - printk(KERN_DEBUG "%s: could not invalidate fragment cache " - "entry (seq=%u)\n", - local->dev->name, seq); - return -1; - } - - entry->skb = NULL; - return 0; -} - - -static struct hostap_bss_info *__hostap_get_bss(local_info_t *local, u8 *bssid, - u8 *ssid, size_t ssid_len) -{ - struct list_head *ptr; - struct hostap_bss_info *bss; - - list_for_each(ptr, &local->bss_list) { - bss = list_entry(ptr, struct hostap_bss_info, list); - if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && - (ssid == NULL || - (ssid_len == bss->ssid_len && - memcmp(ssid, bss->ssid, ssid_len) == 0))) { - list_move(&bss->list, &local->bss_list); - return bss; - } - } - - return NULL; -} - - -static struct hostap_bss_info *__hostap_add_bss(local_info_t *local, u8 *bssid, - u8 *ssid, size_t ssid_len) -{ - struct hostap_bss_info *bss; - - if (local->num_bss_info >= HOSTAP_MAX_BSS_COUNT) { - bss = list_entry(local->bss_list.prev, - struct hostap_bss_info, list); - list_del(&bss->list); - local->num_bss_info--; - } else { - bss = kmalloc(sizeof(*bss), GFP_ATOMIC); - if (bss == NULL) - return NULL; - } - - memset(bss, 0, sizeof(*bss)); - memcpy(bss->bssid, bssid, ETH_ALEN); - memcpy(bss->ssid, ssid, ssid_len); - bss->ssid_len = ssid_len; - local->num_bss_info++; - list_add(&bss->list, &local->bss_list); - return bss; -} - - -static void __hostap_expire_bss(local_info_t *local) -{ - struct hostap_bss_info *bss; - - while (local->num_bss_info > 0) { - bss = list_entry(local->bss_list.prev, - struct hostap_bss_info, list); - if (!time_after(jiffies, bss->last_update + 60 * HZ)) - break; - - list_del(&bss->list); - local->num_bss_info--; - kfree(bss); - } -} - - -/* Both IEEE 802.11 Beacon and Probe Response frames have similar structure, so - * the same routine can be used to parse both of them. */ -static void hostap_rx_sta_beacon(local_info_t *local, struct sk_buff *skb, - int stype) -{ - struct hostap_ieee80211_mgmt *mgmt; - int left, chan = 0; - u8 *pos; - u8 *ssid = NULL, *wpa = NULL, *rsn = NULL; - size_t ssid_len = 0, wpa_len = 0, rsn_len = 0; - struct hostap_bss_info *bss; - - if (skb->len < IEEE80211_MGMT_HDR_LEN + sizeof(mgmt->u.beacon)) - return; - - mgmt = (struct hostap_ieee80211_mgmt *) skb->data; - pos = mgmt->u.beacon.variable; - left = skb->len - (pos - skb->data); - - while (left >= 2) { - if (2 + pos[1] > left) - return; /* parse failed */ - switch (*pos) { - case WLAN_EID_SSID: - ssid = pos + 2; - ssid_len = pos[1]; - break; - case WLAN_EID_VENDOR_SPECIFIC: - if (pos[1] >= 4 && - pos[2] == 0x00 && pos[3] == 0x50 && - pos[4] == 0xf2 && pos[5] == 1) { - wpa = pos; - wpa_len = pos[1] + 2; - } - break; - case WLAN_EID_RSN: - rsn = pos; - rsn_len = pos[1] + 2; - break; - case WLAN_EID_DS_PARAMS: - if (pos[1] >= 1) - chan = pos[2]; - break; - } - left -= 2 + pos[1]; - pos += 2 + pos[1]; - } - - if (wpa_len > MAX_WPA_IE_LEN) - wpa_len = MAX_WPA_IE_LEN; - if (rsn_len > MAX_WPA_IE_LEN) - rsn_len = MAX_WPA_IE_LEN; - if (ssid_len > sizeof(bss->ssid)) - ssid_len = sizeof(bss->ssid); - - spin_lock(&local->lock); - bss = __hostap_get_bss(local, mgmt->bssid, ssid, ssid_len); - if (bss == NULL) - bss = __hostap_add_bss(local, mgmt->bssid, ssid, ssid_len); - if (bss) { - bss->last_update = jiffies; - bss->count++; - bss->capab_info = le16_to_cpu(mgmt->u.beacon.capab_info); - if (wpa) { - memcpy(bss->wpa_ie, wpa, wpa_len); - bss->wpa_ie_len = wpa_len; - } else - bss->wpa_ie_len = 0; - if (rsn) { - memcpy(bss->rsn_ie, rsn, rsn_len); - bss->rsn_ie_len = rsn_len; - } else - bss->rsn_ie_len = 0; - bss->chan = chan; - } - __hostap_expire_bss(local); - spin_unlock(&local->lock); -} - - -static int -hostap_rx_frame_mgmt(local_info_t *local, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats, u16 type, - u16 stype) -{ - if (local->iw_mode == IW_MODE_MASTER) - hostap_update_sta_ps(local, (struct ieee80211_hdr *) skb->data); - - if (local->hostapd && type == IEEE80211_FTYPE_MGMT) { - if (stype == IEEE80211_STYPE_BEACON && - local->iw_mode == IW_MODE_MASTER) { - struct sk_buff *skb2; - /* Process beacon frames also in kernel driver to - * update STA(AP) table statistics */ - skb2 = skb_clone(skb, GFP_ATOMIC); - if (skb2) - hostap_rx(skb2->dev, skb2, rx_stats); - } - - /* send management frames to the user space daemon for - * processing */ - local->apdevstats.rx_packets++; - local->apdevstats.rx_bytes += skb->len; - if (local->apdev == NULL) - return -1; - prism2_rx_80211(local->apdev, skb, rx_stats, PRISM2_RX_MGMT); - return 0; - } - - if (local->iw_mode == IW_MODE_MASTER) { - if (type != IEEE80211_FTYPE_MGMT && - type != IEEE80211_FTYPE_CTL) { - printk(KERN_DEBUG "%s: unknown management frame " - "(type=0x%02x, stype=0x%02x) dropped\n", - skb->dev->name, type >> 2, stype >> 4); - return -1; - } - - hostap_rx(skb->dev, skb, rx_stats); - return 0; - } else if (type == IEEE80211_FTYPE_MGMT && - (stype == IEEE80211_STYPE_BEACON || - stype == IEEE80211_STYPE_PROBE_RESP)) { - hostap_rx_sta_beacon(local, skb, stype); - return -1; - } else if (type == IEEE80211_FTYPE_MGMT && - (stype == IEEE80211_STYPE_ASSOC_RESP || - stype == IEEE80211_STYPE_REASSOC_RESP)) { - /* Ignore (Re)AssocResp silently since these are not currently - * needed but are still received when WPA/RSN mode is enabled. - */ - return -1; - } else { - printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: dropped unhandled" - " management frame in non-Host AP mode (type=%d:%d)\n", - skb->dev->name, type >> 2, stype >> 4); - return -1; - } -} - - -/* Called only as a tasklet (software IRQ) */ -static struct net_device *prism2_rx_get_wds(local_info_t *local, - u8 *addr) -{ - struct hostap_interface *iface = NULL; - struct list_head *ptr; - - read_lock_bh(&local->iface_lock); - list_for_each(ptr, &local->hostap_interfaces) { - iface = list_entry(ptr, struct hostap_interface, list); - if (iface->type == HOSTAP_INTERFACE_WDS && - memcmp(iface->u.wds.remote_addr, addr, ETH_ALEN) == 0) - break; - iface = NULL; - } - read_unlock_bh(&local->iface_lock); - - return iface ? iface->dev : NULL; -} - - -static int -hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr *hdr, u16 fc, - struct net_device **wds) -{ - /* FIX: is this really supposed to accept WDS frames only in Master - * mode? What about Repeater or Managed with WDS frames? */ - if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) != - (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS) && - (local->iw_mode != IW_MODE_MASTER || !(fc & IEEE80211_FCTL_TODS))) - return 0; /* not a WDS frame */ - - /* Possible WDS frame: either IEEE 802.11 compliant (if FromDS) - * or own non-standard frame with 4th address after payload */ - if (!ether_addr_equal(hdr->addr1, local->dev->dev_addr) && - (hdr->addr1[0] != 0xff || hdr->addr1[1] != 0xff || - hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff || - hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) { - /* RA (or BSSID) is not ours - drop */ - PDEBUG(DEBUG_EXTRA2, "%s: received WDS frame with " - "not own or broadcast %s=%pM\n", - local->dev->name, - fc & IEEE80211_FCTL_FROMDS ? "RA" : "BSSID", - hdr->addr1); - return -1; - } - - /* check if the frame came from a registered WDS connection */ - *wds = prism2_rx_get_wds(local, hdr->addr2); - if (*wds == NULL && fc & IEEE80211_FCTL_FROMDS && - (local->iw_mode != IW_MODE_INFRA || - !(local->wds_type & HOSTAP_WDS_AP_CLIENT) || - memcmp(hdr->addr2, local->bssid, ETH_ALEN) != 0)) { - /* require that WDS link has been registered with TA or the - * frame is from current AP when using 'AP client mode' */ - PDEBUG(DEBUG_EXTRA, "%s: received WDS[4 addr] frame " - "from unknown TA=%pM\n", - local->dev->name, hdr->addr2); - if (local->ap && local->ap->autom_ap_wds) - hostap_wds_link_oper(local, hdr->addr2, WDS_ADD); - return -1; - } - - if (*wds && !(fc & IEEE80211_FCTL_FROMDS) && local->ap && - hostap_is_sta_assoc(local->ap, hdr->addr2)) { - /* STA is actually associated with us even though it has a - * registered WDS link. Assume it is in 'AP client' mode. - * Since this is a 3-addr frame, assume it is not (bogus) WDS - * frame and process it like any normal ToDS frame from - * associated STA. */ - *wds = NULL; - } - - return 0; -} - - -static int hostap_is_eapol_frame(local_info_t *local, struct sk_buff *skb) -{ - struct net_device *dev = local->dev; - u16 fc, ethertype; - struct ieee80211_hdr *hdr; - u8 *pos; - - if (skb->len < 24) - return 0; - - hdr = (struct ieee80211_hdr *) skb->data; - fc = le16_to_cpu(hdr->frame_control); - - /* check that the frame is unicast frame to us */ - if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == - IEEE80211_FCTL_TODS && - ether_addr_equal(hdr->addr1, dev->dev_addr) && - ether_addr_equal(hdr->addr3, dev->dev_addr)) { - /* ToDS frame with own addr BSSID and DA */ - } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == - IEEE80211_FCTL_FROMDS && - ether_addr_equal(hdr->addr1, dev->dev_addr)) { - /* FromDS frame with own addr as DA */ - } else - return 0; - - if (skb->len < 24 + 8) - return 0; - - /* check for port access entity Ethernet type */ - pos = skb->data + 24; - ethertype = (pos[6] << 8) | pos[7]; - if (ethertype == ETH_P_PAE) - return 1; - - return 0; -} - - -/* Called only as a tasklet (software IRQ) */ -static int -hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb, - struct lib80211_crypt_data *crypt) -{ - struct ieee80211_hdr *hdr; - int res, hdrlen; - - if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) - return 0; - - hdr = (struct ieee80211_hdr *) skb->data; - hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); - - if (local->tkip_countermeasures && - strcmp(crypt->ops->name, "TKIP") == 0) { - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " - "received packet from %pM\n", - local->dev->name, hdr->addr2); - } - return -1; - } - - atomic_inc(&crypt->refcnt); - res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); - atomic_dec(&crypt->refcnt); - if (res < 0) { - printk(KERN_DEBUG "%s: decryption failed (SA=%pM) res=%d\n", - local->dev->name, hdr->addr2, res); - local->comm_tallies.rx_discards_wep_undecryptable++; - return -1; - } - - return res; -} - - -/* Called only as a tasklet (software IRQ) */ -static int -hostap_rx_frame_decrypt_msdu(local_info_t *local, struct sk_buff *skb, - int keyidx, struct lib80211_crypt_data *crypt) -{ - struct ieee80211_hdr *hdr; - int res, hdrlen; - - if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) - return 0; - - hdr = (struct ieee80211_hdr *) skb->data; - hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); - - atomic_inc(&crypt->refcnt); - res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); - atomic_dec(&crypt->refcnt); - if (res < 0) { - printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" - " (SA=%pM keyidx=%d)\n", - local->dev->name, hdr->addr2, keyidx); - return -1; - } - - return 0; -} - - -/* All received frames are sent to this function. @skb contains the frame in - * IEEE 802.11 format, i.e., in the format it was sent over air. - * This function is called only as a tasklet (software IRQ). */ -void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats) -{ - struct hostap_interface *iface; - local_info_t *local; - struct ieee80211_hdr *hdr; - size_t hdrlen; - u16 fc, type, stype, sc; - struct net_device *wds = NULL; - unsigned int frag; - u8 *payload; - struct sk_buff *skb2 = NULL; - u16 ethertype; - int frame_authorized = 0; - int from_assoc_ap = 0; - u8 dst[ETH_ALEN]; - u8 src[ETH_ALEN]; - struct lib80211_crypt_data *crypt = NULL; - void *sta = NULL; - int keyidx = 0; - - iface = netdev_priv(dev); - local = iface->local; - iface->stats.rx_packets++; - iface->stats.rx_bytes += skb->len; - - /* dev is the master radio device; change this to be the default - * virtual interface (this may be changed to WDS device below) */ - dev = local->ddev; - iface = netdev_priv(dev); - - hdr = (struct ieee80211_hdr *) skb->data; - - if (skb->len < 10) - goto rx_dropped; - - fc = le16_to_cpu(hdr->frame_control); - type = fc & IEEE80211_FCTL_FTYPE; - stype = fc & IEEE80211_FCTL_STYPE; - sc = le16_to_cpu(hdr->seq_ctrl); - frag = sc & IEEE80211_SCTL_FRAG; - hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); - - /* Put this code here so that we avoid duplicating it in all - * Rx paths. - Jean II */ -#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ - /* If spy monitoring on */ - if (iface->spy_data.spy_number > 0) { - struct iw_quality wstats; - wstats.level = rx_stats->signal; - wstats.noise = rx_stats->noise; - wstats.updated = IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_UPDATED - | IW_QUAL_QUAL_INVALID | IW_QUAL_DBM; - /* Update spy records */ - wireless_spy_update(dev, hdr->addr2, &wstats); - } -#endif /* IW_WIRELESS_SPY */ - hostap_update_rx_stats(local->ap, hdr, rx_stats); - - if (local->iw_mode == IW_MODE_MONITOR) { - monitor_rx(dev, skb, rx_stats); - return; - } - - if (local->host_decrypt) { - int idx = 0; - if (skb->len >= hdrlen + 3) - idx = skb->data[hdrlen + 3] >> 6; - crypt = local->crypt_info.crypt[idx]; - sta = NULL; - - /* Use station specific key to override default keys if the - * receiver address is a unicast address ("individual RA"). If - * bcrx_sta_key parameter is set, station specific key is used - * even with broad/multicast targets (this is against IEEE - * 802.11, but makes it easier to use different keys with - * stations that do not support WEP key mapping). */ - - if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key) - (void) hostap_handle_sta_crypto(local, hdr, &crypt, - &sta); - - /* allow NULL decrypt to indicate an station specific override - * for default encryption */ - if (crypt && (crypt->ops == NULL || - crypt->ops->decrypt_mpdu == NULL)) - crypt = NULL; - - if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) { -#if 0 - /* This seems to be triggered by some (multicast?) - * frames from other than current BSS, so just drop the - * frames silently instead of filling system log with - * these reports. */ - printk(KERN_DEBUG "%s: WEP decryption failed (not set)" - " (SA=%pM)\n", - local->dev->name, hdr->addr2); -#endif - local->comm_tallies.rx_discards_wep_undecryptable++; - goto rx_dropped; - } - } - - if (type != IEEE80211_FTYPE_DATA) { - if (type == IEEE80211_FTYPE_MGMT && - stype == IEEE80211_STYPE_AUTH && - fc & IEEE80211_FCTL_PROTECTED && local->host_decrypt && - (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0) - { - printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " - "from %pM\n", dev->name, hdr->addr2); - /* TODO: could inform hostapd about this so that it - * could send auth failure report */ - goto rx_dropped; - } - - if (hostap_rx_frame_mgmt(local, skb, rx_stats, type, stype)) - goto rx_dropped; - else - goto rx_exit; - } - - /* Data frame - extract src/dst addresses */ - if (skb->len < IEEE80211_DATA_HDR3_LEN) - goto rx_dropped; - - switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { - case IEEE80211_FCTL_FROMDS: - memcpy(dst, hdr->addr1, ETH_ALEN); - memcpy(src, hdr->addr3, ETH_ALEN); - break; - case IEEE80211_FCTL_TODS: - memcpy(dst, hdr->addr3, ETH_ALEN); - memcpy(src, hdr->addr2, ETH_ALEN); - break; - case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: - if (skb->len < IEEE80211_DATA_HDR4_LEN) - goto rx_dropped; - memcpy(dst, hdr->addr3, ETH_ALEN); - memcpy(src, hdr->addr4, ETH_ALEN); - break; - case 0: - memcpy(dst, hdr->addr1, ETH_ALEN); - memcpy(src, hdr->addr2, ETH_ALEN); - break; - } - - if (hostap_rx_frame_wds(local, hdr, fc, &wds)) - goto rx_dropped; - if (wds) - skb->dev = dev = wds; - - if (local->iw_mode == IW_MODE_MASTER && !wds && - (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == - IEEE80211_FCTL_FROMDS && - local->stadev && - memcmp(hdr->addr2, local->assoc_ap_addr, ETH_ALEN) == 0) { - /* Frame from BSSID of the AP for which we are a client */ - skb->dev = dev = local->stadev; - from_assoc_ap = 1; - } - - if ((local->iw_mode == IW_MODE_MASTER || - local->iw_mode == IW_MODE_REPEAT) && - !from_assoc_ap) { - switch (hostap_handle_sta_rx(local, dev, skb, rx_stats, - wds != NULL)) { - case AP_RX_CONTINUE_NOT_AUTHORIZED: - frame_authorized = 0; - break; - case AP_RX_CONTINUE: - frame_authorized = 1; - break; - case AP_RX_DROP: - goto rx_dropped; - case AP_RX_EXIT: - goto rx_exit; - } - } - - /* Nullfunc frames may have PS-bit set, so they must be passed to - * hostap_handle_sta_rx() before being dropped here. */ - if (stype != IEEE80211_STYPE_DATA && - stype != IEEE80211_STYPE_DATA_CFACK && - stype != IEEE80211_STYPE_DATA_CFPOLL && - stype != IEEE80211_STYPE_DATA_CFACKPOLL) { - if (stype != IEEE80211_STYPE_NULLFUNC) - printk(KERN_DEBUG "%s: RX: dropped data frame " - "with no data (type=0x%02x, subtype=0x%02x)\n", - dev->name, type >> 2, stype >> 4); - goto rx_dropped; - } - - /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ - - if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && - (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0) - goto rx_dropped; - hdr = (struct ieee80211_hdr *) skb->data; - - /* skb: hdr + (possibly fragmented) plaintext payload */ - - if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && - (frag != 0 || (fc & IEEE80211_FCTL_MOREFRAGS))) { - int flen; - struct sk_buff *frag_skb = - prism2_frag_cache_get(local, hdr); - if (!frag_skb) { - printk(KERN_DEBUG "%s: Rx cannot get skb from " - "fragment cache (morefrag=%d seq=%u frag=%u)\n", - dev->name, (fc & IEEE80211_FCTL_MOREFRAGS) != 0, - (sc & IEEE80211_SCTL_SEQ) >> 4, frag); - goto rx_dropped; - } - - flen = skb->len; - if (frag != 0) - flen -= hdrlen; - - if (frag_skb->tail + flen > frag_skb->end) { - printk(KERN_WARNING "%s: host decrypted and " - "reassembled frame did not fit skb\n", - dev->name); - prism2_frag_cache_invalidate(local, hdr); - goto rx_dropped; - } - - if (frag == 0) { - /* copy first fragment (including full headers) into - * beginning of the fragment cache skb */ - skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), - flen); - } else { - /* append frame payload to the end of the fragment - * cache skb */ - skb_copy_from_linear_data_offset(skb, hdrlen, - skb_put(frag_skb, - flen), flen); - } - dev_kfree_skb(skb); - skb = NULL; - - if (fc & IEEE80211_FCTL_MOREFRAGS) { - /* more fragments expected - leave the skb in fragment - * cache for now; it will be delivered to upper layers - * after all fragments have been received */ - goto rx_exit; - } - - /* this was the last fragment and the frame will be - * delivered, so remove skb from fragment cache */ - skb = frag_skb; - hdr = (struct ieee80211_hdr *) skb->data; - prism2_frag_cache_invalidate(local, hdr); - } - - /* skb: hdr + (possible reassembled) full MSDU payload; possibly still - * encrypted/authenticated */ - - if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && - hostap_rx_frame_decrypt_msdu(local, skb, keyidx, crypt)) - goto rx_dropped; - - hdr = (struct ieee80211_hdr *) skb->data; - if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !local->open_wep) { - if (local->ieee_802_1x && - hostap_is_eapol_frame(local, skb)) { - /* pass unencrypted EAPOL frames even if encryption is - * configured */ - PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X - passing " - "unencrypted EAPOL frame\n", local->dev->name); - } else { - printk(KERN_DEBUG "%s: encryption configured, but RX " - "frame not encrypted (SA=%pM)\n", - local->dev->name, hdr->addr2); - goto rx_dropped; - } - } - - if (local->drop_unencrypted && !(fc & IEEE80211_FCTL_PROTECTED) && - !hostap_is_eapol_frame(local, skb)) { - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: dropped unencrypted RX data " - "frame from %pM (drop_unencrypted=1)\n", - dev->name, hdr->addr2); - } - goto rx_dropped; - } - - /* skb: hdr + (possible reassembled) full plaintext payload */ - - payload = skb->data + hdrlen; - ethertype = (payload[6] << 8) | payload[7]; - - /* If IEEE 802.1X is used, check whether the port is authorized to send - * the received frame. */ - if (local->ieee_802_1x && local->iw_mode == IW_MODE_MASTER) { - if (ethertype == ETH_P_PAE) { - PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X frame\n", - dev->name); - if (local->hostapd && local->apdev) { - /* Send IEEE 802.1X frames to the user - * space daemon for processing */ - prism2_rx_80211(local->apdev, skb, rx_stats, - PRISM2_RX_MGMT); - local->apdevstats.rx_packets++; - local->apdevstats.rx_bytes += skb->len; - goto rx_exit; - } - } else if (!frame_authorized) { - printk(KERN_DEBUG "%s: dropped frame from " - "unauthorized port (IEEE 802.1X): " - "ethertype=0x%04x\n", - dev->name, ethertype); - goto rx_dropped; - } - } - - /* convert hdr + possible LLC headers into Ethernet header */ - if (skb->len - hdrlen >= 8 && - ((memcmp(payload, rfc1042_header, 6) == 0 && - ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || - memcmp(payload, bridge_tunnel_header, 6) == 0)) { - /* remove RFC1042 or Bridge-Tunnel encapsulation and - * replace EtherType */ - skb_pull(skb, hdrlen + 6); - memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); - } else { - __be16 len; - /* Leave Ethernet header part of hdr and full payload */ - skb_pull(skb, hdrlen); - len = htons(skb->len); - memcpy(skb_push(skb, 2), &len, 2); - memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); - } - - if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == - IEEE80211_FCTL_TODS) && - skb->len >= ETH_HLEN + ETH_ALEN) { - /* Non-standard frame: get addr4 from its bogus location after - * the payload */ - skb_copy_from_linear_data_offset(skb, skb->len - ETH_ALEN, - skb->data + ETH_ALEN, - ETH_ALEN); - skb_trim(skb, skb->len - ETH_ALEN); - } - - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - - if (local->iw_mode == IW_MODE_MASTER && !wds && - local->ap->bridge_packets) { - if (dst[0] & 0x01) { - /* copy multicast frame both to the higher layers and - * to the wireless media */ - local->ap->bridged_multicast++; - skb2 = skb_clone(skb, GFP_ATOMIC); - if (skb2 == NULL) - printk(KERN_DEBUG "%s: skb_clone failed for " - "multicast frame\n", dev->name); - } else if (hostap_is_sta_authorized(local->ap, dst)) { - /* send frame directly to the associated STA using - * wireless media and not passing to higher layers */ - local->ap->bridged_unicast++; - skb2 = skb; - skb = NULL; - } - } - - if (skb2 != NULL) { - /* send to wireless media */ - skb2->dev = dev; - skb2->protocol = cpu_to_be16(ETH_P_802_3); - skb_reset_mac_header(skb2); - skb_reset_network_header(skb2); - /* skb2->network_header += ETH_HLEN; */ - dev_queue_xmit(skb2); - } - - if (skb) { - skb->protocol = eth_type_trans(skb, dev); - memset(skb->cb, 0, sizeof(skb->cb)); - netif_rx(skb); - } - - rx_exit: - if (sta) - hostap_handle_sta_release(sta); - return; - - rx_dropped: - dev_kfree_skb(skb); - - dev->stats.rx_dropped++; - goto rx_exit; -} - - -EXPORT_SYMBOL(hostap_80211_rx); diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c deleted file mode 100644 index 055e11d353ca..000000000000 --- a/drivers/net/wireless/hostap/hostap_80211_tx.c +++ /dev/null @@ -1,553 +0,0 @@ -#include -#include -#include - -#include "hostap_80211.h" -#include "hostap_common.h" -#include "hostap_wlan.h" -#include "hostap.h" -#include "hostap_ap.h" - -/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ -/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ -static unsigned char rfc1042_header[] = -{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; -/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ -static unsigned char bridge_tunnel_header[] = -{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; -/* No encapsulation header if EtherType < 0x600 (=length) */ - -void hostap_dump_tx_80211(const char *name, struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr; - u16 fc; - - hdr = (struct ieee80211_hdr *) skb->data; - - printk(KERN_DEBUG "%s: TX len=%d jiffies=%ld\n", - name, skb->len, jiffies); - - if (skb->len < 2) - return; - - fc = le16_to_cpu(hdr->frame_control); - printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s", - fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, - (fc & IEEE80211_FCTL_STYPE) >> 4, - fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", - fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); - - if (skb->len < IEEE80211_DATA_HDR3_LEN) { - printk("\n"); - return; - } - - printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id), - le16_to_cpu(hdr->seq_ctrl)); - - printk(KERN_DEBUG " A1=%pM", hdr->addr1); - printk(" A2=%pM", hdr->addr2); - printk(" A3=%pM", hdr->addr3); - if (skb->len >= 30) - printk(" A4=%pM", hdr->addr4); - printk("\n"); -} - - -/* hard_start_xmit function for data interfaces (wlan#, wlan#wds#, wlan#sta) - * Convert Ethernet header into a suitable IEEE 802.11 header depending on - * device configuration. */ -netdev_tx_t hostap_data_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - int need_headroom, need_tailroom = 0; - struct ieee80211_hdr hdr; - u16 fc, ethertype = 0; - enum { - WDS_NO = 0, WDS_OWN_FRAME, WDS_COMPLIANT_FRAME - } use_wds = WDS_NO; - u8 *encaps_data; - int hdr_len, encaps_len, skip_header_bytes; - int to_assoc_ap = 0; - struct hostap_skb_tx_data *meta; - - iface = netdev_priv(dev); - local = iface->local; - - if (skb->len < ETH_HLEN) { - printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb " - "(len=%d)\n", dev->name, skb->len); - kfree_skb(skb); - return NETDEV_TX_OK; - } - - if (local->ddev != dev) { - use_wds = (local->iw_mode == IW_MODE_MASTER && - !(local->wds_type & HOSTAP_WDS_STANDARD_FRAME)) ? - WDS_OWN_FRAME : WDS_COMPLIANT_FRAME; - if (dev == local->stadev) { - to_assoc_ap = 1; - use_wds = WDS_NO; - } else if (dev == local->apdev) { - printk(KERN_DEBUG "%s: prism2_tx: trying to use " - "AP device with Ethernet net dev\n", dev->name); - kfree_skb(skb); - return NETDEV_TX_OK; - } - } else { - if (local->iw_mode == IW_MODE_REPEAT) { - printk(KERN_DEBUG "%s: prism2_tx: trying to use " - "non-WDS link in Repeater mode\n", dev->name); - kfree_skb(skb); - return NETDEV_TX_OK; - } else if (local->iw_mode == IW_MODE_INFRA && - (local->wds_type & HOSTAP_WDS_AP_CLIENT) && - !ether_addr_equal(skb->data + ETH_ALEN, dev->dev_addr)) { - /* AP client mode: send frames with foreign src addr - * using 4-addr WDS frames */ - use_wds = WDS_COMPLIANT_FRAME; - } - } - - /* Incoming skb->data: dst_addr[6], src_addr[6], proto[2], payload - * ==> - * Prism2 TX frame with 802.11 header: - * txdesc (address order depending on used mode; includes dst_addr and - * src_addr), possible encapsulation (RFC1042/Bridge-Tunnel; - * proto[2], payload {, possible addr4[6]} */ - - ethertype = (skb->data[12] << 8) | skb->data[13]; - - memset(&hdr, 0, sizeof(hdr)); - - /* Length of data after IEEE 802.11 header */ - encaps_data = NULL; - encaps_len = 0; - skip_header_bytes = ETH_HLEN; - if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { - encaps_data = bridge_tunnel_header; - encaps_len = sizeof(bridge_tunnel_header); - skip_header_bytes -= 2; - } else if (ethertype >= 0x600) { - encaps_data = rfc1042_header; - encaps_len = sizeof(rfc1042_header); - skip_header_bytes -= 2; - } - - fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; - hdr_len = IEEE80211_DATA_HDR3_LEN; - - if (use_wds != WDS_NO) { - /* Note! Prism2 station firmware has problems with sending real - * 802.11 frames with four addresses; until these problems can - * be fixed or worked around, 4-addr frames needed for WDS are - * using incompatible format: FromDS flag is not set and the - * fourth address is added after the frame payload; it is - * assumed, that the receiving station knows how to handle this - * frame format */ - - if (use_wds == WDS_COMPLIANT_FRAME) { - fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS; - /* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA, - * Addr4 = SA */ - skb_copy_from_linear_data_offset(skb, ETH_ALEN, - &hdr.addr4, ETH_ALEN); - hdr_len += ETH_ALEN; - } else { - /* bogus 4-addr format to workaround Prism2 station - * f/w bug */ - fc |= IEEE80211_FCTL_TODS; - /* From DS: Addr1 = DA (used as RA), - * Addr2 = BSSID (used as TA), Addr3 = SA (used as DA), - */ - - /* SA from skb->data + ETH_ALEN will be added after - * frame payload; use hdr.addr4 as a temporary buffer - */ - skb_copy_from_linear_data_offset(skb, ETH_ALEN, - &hdr.addr4, ETH_ALEN); - need_tailroom += ETH_ALEN; - } - - /* send broadcast and multicast frames to broadcast RA, if - * configured; otherwise, use unicast RA of the WDS link */ - if ((local->wds_type & HOSTAP_WDS_BROADCAST_RA) && - is_multicast_ether_addr(skb->data)) - eth_broadcast_addr(hdr.addr1); - else if (iface->type == HOSTAP_INTERFACE_WDS) - memcpy(&hdr.addr1, iface->u.wds.remote_addr, - ETH_ALEN); - else - memcpy(&hdr.addr1, local->bssid, ETH_ALEN); - memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); - skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN); - } else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) { - fc |= IEEE80211_FCTL_FROMDS; - /* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */ - skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN); - memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); - skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr3, - ETH_ALEN); - } else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) { - fc |= IEEE80211_FCTL_TODS; - /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ - memcpy(&hdr.addr1, to_assoc_ap ? - local->assoc_ap_addr : local->bssid, ETH_ALEN); - skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2, - ETH_ALEN); - skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN); - } else if (local->iw_mode == IW_MODE_ADHOC) { - /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ - skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN); - skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2, - ETH_ALEN); - memcpy(&hdr.addr3, local->bssid, ETH_ALEN); - } - - hdr.frame_control = cpu_to_le16(fc); - - skb_pull(skb, skip_header_bytes); - need_headroom = local->func->need_tx_headroom + hdr_len + encaps_len; - if (skb_tailroom(skb) < need_tailroom) { - skb = skb_unshare(skb, GFP_ATOMIC); - if (skb == NULL) { - iface->stats.tx_dropped++; - return NETDEV_TX_OK; - } - if (pskb_expand_head(skb, need_headroom, need_tailroom, - GFP_ATOMIC)) { - kfree_skb(skb); - iface->stats.tx_dropped++; - return NETDEV_TX_OK; - } - } else if (skb_headroom(skb) < need_headroom) { - struct sk_buff *tmp = skb; - skb = skb_realloc_headroom(skb, need_headroom); - kfree_skb(tmp); - if (skb == NULL) { - iface->stats.tx_dropped++; - return NETDEV_TX_OK; - } - } else { - skb = skb_unshare(skb, GFP_ATOMIC); - if (skb == NULL) { - iface->stats.tx_dropped++; - return NETDEV_TX_OK; - } - } - - if (encaps_data) - memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); - memcpy(skb_push(skb, hdr_len), &hdr, hdr_len); - if (use_wds == WDS_OWN_FRAME) { - memcpy(skb_put(skb, ETH_ALEN), &hdr.addr4, ETH_ALEN); - } - - iface->stats.tx_packets++; - iface->stats.tx_bytes += skb->len; - - skb_reset_mac_header(skb); - meta = (struct hostap_skb_tx_data *) skb->cb; - memset(meta, 0, sizeof(*meta)); - meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; - if (use_wds) - meta->flags |= HOSTAP_TX_FLAGS_WDS; - meta->ethertype = ethertype; - meta->iface = iface; - - /* Send IEEE 802.11 encapsulated frame using the master radio device */ - skb->dev = local->dev; - dev_queue_xmit(skb); - return NETDEV_TX_OK; -} - - -/* hard_start_xmit function for hostapd wlan#ap interfaces */ -netdev_tx_t hostap_mgmt_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - struct hostap_skb_tx_data *meta; - struct ieee80211_hdr *hdr; - u16 fc; - - iface = netdev_priv(dev); - local = iface->local; - - if (skb->len < 10) { - printk(KERN_DEBUG "%s: hostap_mgmt_start_xmit: short skb " - "(len=%d)\n", dev->name, skb->len); - kfree_skb(skb); - return NETDEV_TX_OK; - } - - iface->stats.tx_packets++; - iface->stats.tx_bytes += skb->len; - - meta = (struct hostap_skb_tx_data *) skb->cb; - memset(meta, 0, sizeof(*meta)); - meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; - meta->iface = iface; - - if (skb->len >= IEEE80211_DATA_HDR3_LEN + sizeof(rfc1042_header) + 2) { - hdr = (struct ieee80211_hdr *) skb->data; - fc = le16_to_cpu(hdr->frame_control); - if (ieee80211_is_data(hdr->frame_control) && - (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DATA) { - u8 *pos = &skb->data[IEEE80211_DATA_HDR3_LEN + - sizeof(rfc1042_header)]; - meta->ethertype = (pos[0] << 8) | pos[1]; - } - } - - /* Send IEEE 802.11 encapsulated frame using the master radio device */ - skb->dev = local->dev; - dev_queue_xmit(skb); - return NETDEV_TX_OK; -} - - -/* Called only from software IRQ */ -static struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, - struct lib80211_crypt_data *crypt) -{ - struct hostap_interface *iface; - local_info_t *local; - struct ieee80211_hdr *hdr; - int prefix_len, postfix_len, hdr_len, res; - - iface = netdev_priv(skb->dev); - local = iface->local; - - if (skb->len < IEEE80211_DATA_HDR3_LEN) { - kfree_skb(skb); - return NULL; - } - - if (local->tkip_countermeasures && - strcmp(crypt->ops->name, "TKIP") == 0) { - hdr = (struct ieee80211_hdr *) skb->data; - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " - "TX packet to %pM\n", - local->dev->name, hdr->addr1); - } - kfree_skb(skb); - return NULL; - } - - skb = skb_unshare(skb, GFP_ATOMIC); - if (skb == NULL) - return NULL; - - prefix_len = crypt->ops->extra_mpdu_prefix_len + - crypt->ops->extra_msdu_prefix_len; - postfix_len = crypt->ops->extra_mpdu_postfix_len + - crypt->ops->extra_msdu_postfix_len; - if ((skb_headroom(skb) < prefix_len || - skb_tailroom(skb) < postfix_len) && - pskb_expand_head(skb, prefix_len, postfix_len, GFP_ATOMIC)) { - kfree_skb(skb); - return NULL; - } - - hdr = (struct ieee80211_hdr *) skb->data; - hdr_len = hostap_80211_get_hdrlen(hdr->frame_control); - - /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so - * call both MSDU and MPDU encryption functions from here. */ - atomic_inc(&crypt->refcnt); - res = 0; - if (crypt->ops->encrypt_msdu) - res = crypt->ops->encrypt_msdu(skb, hdr_len, crypt->priv); - if (res == 0 && crypt->ops->encrypt_mpdu) - res = crypt->ops->encrypt_mpdu(skb, hdr_len, crypt->priv); - atomic_dec(&crypt->refcnt); - if (res < 0) { - kfree_skb(skb); - return NULL; - } - - return skb; -} - - -/* hard_start_xmit function for master radio interface wifi#. - * AP processing (TX rate control, power save buffering, etc.). - * Use hardware TX function to send the frame. */ -netdev_tx_t hostap_master_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - netdev_tx_t ret = NETDEV_TX_BUSY; - u16 fc; - struct hostap_tx_data tx; - ap_tx_ret tx_ret; - struct hostap_skb_tx_data *meta; - int no_encrypt = 0; - struct ieee80211_hdr *hdr; - - iface = netdev_priv(dev); - local = iface->local; - - tx.skb = skb; - tx.sta_ptr = NULL; - - meta = (struct hostap_skb_tx_data *) skb->cb; - if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) { - printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, " - "expected 0x%08x)\n", - dev->name, meta->magic, HOSTAP_SKB_TX_DATA_MAGIC); - ret = NETDEV_TX_OK; - iface->stats.tx_dropped++; - goto fail; - } - - if (local->host_encrypt) { - /* Set crypt to default algorithm and key; will be replaced in - * AP code if STA has own alg/key */ - tx.crypt = local->crypt_info.crypt[local->crypt_info.tx_keyidx]; - tx.host_encrypt = 1; - } else { - tx.crypt = NULL; - tx.host_encrypt = 0; - } - - if (skb->len < 24) { - printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb " - "(len=%d)\n", dev->name, skb->len); - ret = NETDEV_TX_OK; - iface->stats.tx_dropped++; - goto fail; - } - - /* FIX (?): - * Wi-Fi 802.11b test plan suggests that AP should ignore power save - * bit in authentication and (re)association frames and assume tha - * STA remains awake for the response. */ - tx_ret = hostap_handle_sta_tx(local, &tx); - skb = tx.skb; - meta = (struct hostap_skb_tx_data *) skb->cb; - hdr = (struct ieee80211_hdr *) skb->data; - fc = le16_to_cpu(hdr->frame_control); - switch (tx_ret) { - case AP_TX_CONTINUE: - break; - case AP_TX_CONTINUE_NOT_AUTHORIZED: - if (local->ieee_802_1x && - ieee80211_is_data(hdr->frame_control) && - meta->ethertype != ETH_P_PAE && - !(meta->flags & HOSTAP_TX_FLAGS_WDS)) { - printk(KERN_DEBUG "%s: dropped frame to unauthorized " - "port (IEEE 802.1X): ethertype=0x%04x\n", - dev->name, meta->ethertype); - hostap_dump_tx_80211(dev->name, skb); - - ret = NETDEV_TX_OK; /* drop packet */ - iface->stats.tx_dropped++; - goto fail; - } - break; - case AP_TX_DROP: - ret = NETDEV_TX_OK; /* drop packet */ - iface->stats.tx_dropped++; - goto fail; - case AP_TX_RETRY: - goto fail; - case AP_TX_BUFFERED: - /* do not free skb here, it will be freed when the - * buffered frame is sent/timed out */ - ret = NETDEV_TX_OK; - goto tx_exit; - } - - /* Request TX callback if protocol version is 2 in 802.11 header; - * this version 2 is a special case used between hostapd and kernel - * driver */ - if (((fc & IEEE80211_FCTL_VERS) == BIT(1)) && - local->ap && local->ap->tx_callback_idx && meta->tx_cb_idx == 0) { - meta->tx_cb_idx = local->ap->tx_callback_idx; - - /* remove special version from the frame header */ - fc &= ~IEEE80211_FCTL_VERS; - hdr->frame_control = cpu_to_le16(fc); - } - - if (!ieee80211_is_data(hdr->frame_control)) { - no_encrypt = 1; - tx.crypt = NULL; - } - - if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt && - !(fc & IEEE80211_FCTL_PROTECTED)) { - no_encrypt = 1; - PDEBUG(DEBUG_EXTRA2, "%s: TX: IEEE 802.1X - passing " - "unencrypted EAPOL frame\n", dev->name); - tx.crypt = NULL; /* no encryption for IEEE 802.1X frames */ - } - - if (tx.crypt && (!tx.crypt->ops || !tx.crypt->ops->encrypt_mpdu)) - tx.crypt = NULL; - else if ((tx.crypt || - local->crypt_info.crypt[local->crypt_info.tx_keyidx]) && - !no_encrypt) { - /* Add ISWEP flag both for firmware and host based encryption - */ - fc |= IEEE80211_FCTL_PROTECTED; - hdr->frame_control = cpu_to_le16(fc); - } else if (local->drop_unencrypted && - ieee80211_is_data(hdr->frame_control) && - meta->ethertype != ETH_P_PAE) { - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: dropped unencrypted TX data " - "frame (drop_unencrypted=1)\n", dev->name); - } - iface->stats.tx_dropped++; - ret = NETDEV_TX_OK; - goto fail; - } - - if (tx.crypt) { - skb = hostap_tx_encrypt(skb, tx.crypt); - if (skb == NULL) { - printk(KERN_DEBUG "%s: TX - encryption failed\n", - dev->name); - ret = NETDEV_TX_OK; - goto fail; - } - meta = (struct hostap_skb_tx_data *) skb->cb; - if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) { - printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, " - "expected 0x%08x) after hostap_tx_encrypt\n", - dev->name, meta->magic, - HOSTAP_SKB_TX_DATA_MAGIC); - ret = NETDEV_TX_OK; - iface->stats.tx_dropped++; - goto fail; - } - } - - if (local->func->tx == NULL || local->func->tx(skb, dev)) { - ret = NETDEV_TX_OK; - iface->stats.tx_dropped++; - } else { - ret = NETDEV_TX_OK; - iface->stats.tx_packets++; - iface->stats.tx_bytes += skb->len; - } - - fail: - if (ret == NETDEV_TX_OK && skb) - dev_kfree_skb(skb); - tx_exit: - if (tx.sta_ptr) - hostap_handle_sta_release(tx.sta_ptr); - return ret; -} - - -EXPORT_SYMBOL(hostap_master_start_xmit); diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c deleted file mode 100644 index c995ace153ee..000000000000 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ /dev/null @@ -1,3339 +0,0 @@ -/* - * Intersil Prism2 driver with Host AP (software access point) support - * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2005, Jouni Malinen - * - * This file is to be included into hostap.c when S/W AP functionality is - * compiled. - * - * AP: FIX: - * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from - * unauthenticated STA, send deauth. frame (8802.11: 5.5) - * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received - * from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5) - * - if unicast Class 3 received from unauthenticated STA, send deauth. frame - * (8802.11: 5.5) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hostap_wlan.h" -#include "hostap.h" -#include "hostap_ap.h" - -static int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL, - DEF_INTS }; -module_param_array(other_ap_policy, int, NULL, 0444); -MODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)"); - -static int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC, - DEF_INTS }; -module_param_array(ap_max_inactivity, int, NULL, 0444); -MODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station " - "inactivity"); - -static int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS }; -module_param_array(ap_bridge_packets, int, NULL, 0444); -MODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between " - "stations"); - -static int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS }; -module_param_array(autom_ap_wds, int, NULL, 0444); -MODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs " - "automatically"); - - -static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta); -static void hostap_event_expired_sta(struct net_device *dev, - struct sta_info *sta); -static void handle_add_proc_queue(struct work_struct *work); - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT -static void handle_wds_oper_queue(struct work_struct *work); -static void prism2_send_mgmt(struct net_device *dev, - u16 type_subtype, char *body, - int body_len, u8 *addr, u16 tx_cb_idx); -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - -#ifndef PRISM2_NO_PROCFS_DEBUG -static int ap_debug_proc_show(struct seq_file *m, void *v) -{ - struct ap_data *ap = m->private; - - seq_printf(m, "BridgedUnicastFrames=%u\n", ap->bridged_unicast); - seq_printf(m, "BridgedMulticastFrames=%u\n", ap->bridged_multicast); - seq_printf(m, "max_inactivity=%u\n", ap->max_inactivity / HZ); - seq_printf(m, "bridge_packets=%u\n", ap->bridge_packets); - seq_printf(m, "nullfunc_ack=%u\n", ap->nullfunc_ack); - seq_printf(m, "autom_ap_wds=%u\n", ap->autom_ap_wds); - seq_printf(m, "auth_algs=%u\n", ap->local->auth_algs); - seq_printf(m, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc); - return 0; -} - -static int ap_debug_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, ap_debug_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations ap_debug_proc_fops = { - .open = ap_debug_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif /* PRISM2_NO_PROCFS_DEBUG */ - - -static void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta) -{ - sta->hnext = ap->sta_hash[STA_HASH(sta->addr)]; - ap->sta_hash[STA_HASH(sta->addr)] = sta; -} - -static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta) -{ - struct sta_info *s; - - s = ap->sta_hash[STA_HASH(sta->addr)]; - if (s == NULL) return; - if (ether_addr_equal(s->addr, sta->addr)) { - ap->sta_hash[STA_HASH(sta->addr)] = s->hnext; - return; - } - - while (s->hnext != NULL && !ether_addr_equal(s->hnext->addr, sta->addr)) - s = s->hnext; - if (s->hnext != NULL) - s->hnext = s->hnext->hnext; - else - printk("AP: could not remove STA %pM from hash table\n", - sta->addr); -} - -static void ap_free_sta(struct ap_data *ap, struct sta_info *sta) -{ - if (sta->ap && sta->local) - hostap_event_expired_sta(sta->local->dev, sta); - - if (ap->proc != NULL) { - char name[20]; - sprintf(name, "%pM", sta->addr); - remove_proc_entry(name, ap->proc); - } - - if (sta->crypt) { - sta->crypt->ops->deinit(sta->crypt->priv); - kfree(sta->crypt); - sta->crypt = NULL; - } - - skb_queue_purge(&sta->tx_buf); - - ap->num_sta--; -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - if (sta->aid > 0) - ap->sta_aid[sta->aid - 1] = NULL; - - if (!sta->ap) - kfree(sta->u.sta.challenge); - del_timer_sync(&sta->timer); -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - kfree(sta); -} - - -static void hostap_set_tim(local_info_t *local, int aid, int set) -{ - if (local->func->set_tim) - local->func->set_tim(local->dev, aid, set); -} - - -static void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta) -{ - union iwreq_data wrqu; - memset(&wrqu, 0, sizeof(wrqu)); - memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); - wrqu.addr.sa_family = ARPHRD_ETHER; - wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL); -} - - -static void hostap_event_expired_sta(struct net_device *dev, - struct sta_info *sta) -{ - union iwreq_data wrqu; - memset(&wrqu, 0, sizeof(wrqu)); - memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); - wrqu.addr.sa_family = ARPHRD_ETHER; - wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL); -} - - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - -static void ap_handle_timer(unsigned long data) -{ - struct sta_info *sta = (struct sta_info *) data; - local_info_t *local; - struct ap_data *ap; - unsigned long next_time = 0; - int was_assoc; - - if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) { - PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n"); - return; - } - - local = sta->local; - ap = local->ap; - was_assoc = sta->flags & WLAN_STA_ASSOC; - - if (atomic_read(&sta->users) != 0) - next_time = jiffies + HZ; - else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH)) - next_time = jiffies + ap->max_inactivity; - - if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) { - /* station activity detected; reset timeout state */ - sta->timeout_next = STA_NULLFUNC; - next_time = sta->last_rx + ap->max_inactivity; - } else if (sta->timeout_next == STA_DISASSOC && - !(sta->flags & WLAN_STA_PENDING_POLL)) { - /* STA ACKed data nullfunc frame poll */ - sta->timeout_next = STA_NULLFUNC; - next_time = jiffies + ap->max_inactivity; - } - - if (next_time) { - sta->timer.expires = next_time; - add_timer(&sta->timer); - return; - } - - if (sta->ap) - sta->timeout_next = STA_DEAUTH; - - if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) { - spin_lock(&ap->sta_table_lock); - ap_sta_hash_del(ap, sta); - list_del(&sta->list); - spin_unlock(&ap->sta_table_lock); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - } else if (sta->timeout_next == STA_DISASSOC) - sta->flags &= ~WLAN_STA_ASSOC; - - if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap) - hostap_event_expired_sta(local->dev, sta); - - if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 && - !skb_queue_empty(&sta->tx_buf)) { - hostap_set_tim(local, sta->aid, 0); - sta->flags &= ~WLAN_STA_TIM; - } - - if (sta->ap) { - if (ap->autom_ap_wds) { - PDEBUG(DEBUG_AP, "%s: removing automatic WDS " - "connection to AP %pM\n", - local->dev->name, sta->addr); - hostap_wds_link_oper(local, sta->addr, WDS_DEL); - } - } else if (sta->timeout_next == STA_NULLFUNC) { - /* send data frame to poll STA and check whether this frame - * is ACKed */ - /* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but - * it is apparently not retried so TX Exc events are not - * received for it */ - sta->flags |= WLAN_STA_PENDING_POLL; - prism2_send_mgmt(local->dev, IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_DATA, NULL, 0, - sta->addr, ap->tx_callback_poll); - } else { - int deauth = sta->timeout_next == STA_DEAUTH; - __le16 resp; - PDEBUG(DEBUG_AP, "%s: sending %s info to STA %pM" - "(last=%lu, jiffies=%lu)\n", - local->dev->name, - deauth ? "deauthentication" : "disassociation", - sta->addr, sta->last_rx, jiffies); - - resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID : - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); - prism2_send_mgmt(local->dev, IEEE80211_FTYPE_MGMT | - (deauth ? IEEE80211_STYPE_DEAUTH : - IEEE80211_STYPE_DISASSOC), - (char *) &resp, 2, sta->addr, 0); - } - - if (sta->timeout_next == STA_DEAUTH) { - if (sta->flags & WLAN_STA_PERM) { - PDEBUG(DEBUG_AP, "%s: STA %pM" - " would have been removed, " - "but it has 'perm' flag\n", - local->dev->name, sta->addr); - } else - ap_free_sta(ap, sta); - return; - } - - if (sta->timeout_next == STA_NULLFUNC) { - sta->timeout_next = STA_DISASSOC; - sta->timer.expires = jiffies + AP_DISASSOC_DELAY; - } else { - sta->timeout_next = STA_DEAUTH; - sta->timer.expires = jiffies + AP_DEAUTH_DELAY; - } - - add_timer(&sta->timer); -} - - -void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap, - int resend) -{ - u8 addr[ETH_ALEN]; - __le16 resp; - int i; - - PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name); - eth_broadcast_addr(addr); - - resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); - - /* deauth message sent; try to resend it few times; the message is - * broadcast, so it may be delayed until next DTIM; there is not much - * else we can do at this point since the driver is going to be shut - * down */ - for (i = 0; i < 5; i++) { - prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_DEAUTH, - (char *) &resp, 2, addr, 0); - - if (!resend || ap->num_sta <= 0) - return; - - mdelay(50); - } -} - - -static int ap_control_proc_show(struct seq_file *m, void *v) -{ - struct ap_data *ap = m->private; - char *policy_txt; - struct mac_entry *entry; - - if (v == SEQ_START_TOKEN) { - switch (ap->mac_restrictions.policy) { - case MAC_POLICY_OPEN: - policy_txt = "open"; - break; - case MAC_POLICY_ALLOW: - policy_txt = "allow"; - break; - case MAC_POLICY_DENY: - policy_txt = "deny"; - break; - default: - policy_txt = "unknown"; - break; - } - seq_printf(m, "MAC policy: %s\n", policy_txt); - seq_printf(m, "MAC entries: %u\n", ap->mac_restrictions.entries); - seq_puts(m, "MAC list:\n"); - return 0; - } - - entry = v; - seq_printf(m, "%pM\n", entry->addr); - return 0; -} - -static void *ap_control_proc_start(struct seq_file *m, loff_t *_pos) -{ - struct ap_data *ap = m->private; - spin_lock_bh(&ap->mac_restrictions.lock); - return seq_list_start_head(&ap->mac_restrictions.mac_list, *_pos); -} - -static void *ap_control_proc_next(struct seq_file *m, void *v, loff_t *_pos) -{ - struct ap_data *ap = m->private; - return seq_list_next(v, &ap->mac_restrictions.mac_list, _pos); -} - -static void ap_control_proc_stop(struct seq_file *m, void *v) -{ - struct ap_data *ap = m->private; - spin_unlock_bh(&ap->mac_restrictions.lock); -} - -static const struct seq_operations ap_control_proc_seqops = { - .start = ap_control_proc_start, - .next = ap_control_proc_next, - .stop = ap_control_proc_stop, - .show = ap_control_proc_show, -}; - -static int ap_control_proc_open(struct inode *inode, struct file *file) -{ - int ret = seq_open(file, &ap_control_proc_seqops); - if (ret == 0) { - struct seq_file *m = file->private_data; - m->private = PDE_DATA(inode); - } - return ret; -} - -static const struct file_operations ap_control_proc_fops = { - .open = ap_control_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - - -int ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac) -{ - struct mac_entry *entry; - - entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL); - if (entry == NULL) - return -ENOMEM; - - memcpy(entry->addr, mac, ETH_ALEN); - - spin_lock_bh(&mac_restrictions->lock); - list_add_tail(&entry->list, &mac_restrictions->mac_list); - mac_restrictions->entries++; - spin_unlock_bh(&mac_restrictions->lock); - - return 0; -} - - -int ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac) -{ - struct list_head *ptr; - struct mac_entry *entry; - - spin_lock_bh(&mac_restrictions->lock); - for (ptr = mac_restrictions->mac_list.next; - ptr != &mac_restrictions->mac_list; ptr = ptr->next) { - entry = list_entry(ptr, struct mac_entry, list); - - if (ether_addr_equal(entry->addr, mac)) { - list_del(ptr); - kfree(entry); - mac_restrictions->entries--; - spin_unlock_bh(&mac_restrictions->lock); - return 0; - } - } - spin_unlock_bh(&mac_restrictions->lock); - return -1; -} - - -static int ap_control_mac_deny(struct mac_restrictions *mac_restrictions, - u8 *mac) -{ - struct mac_entry *entry; - int found = 0; - - if (mac_restrictions->policy == MAC_POLICY_OPEN) - return 0; - - spin_lock_bh(&mac_restrictions->lock); - list_for_each_entry(entry, &mac_restrictions->mac_list, list) { - if (ether_addr_equal(entry->addr, mac)) { - found = 1; - break; - } - } - spin_unlock_bh(&mac_restrictions->lock); - - if (mac_restrictions->policy == MAC_POLICY_ALLOW) - return !found; - else - return found; -} - - -void ap_control_flush_macs(struct mac_restrictions *mac_restrictions) -{ - struct list_head *ptr, *n; - struct mac_entry *entry; - - if (mac_restrictions->entries == 0) - return; - - spin_lock_bh(&mac_restrictions->lock); - for (ptr = mac_restrictions->mac_list.next, n = ptr->next; - ptr != &mac_restrictions->mac_list; - ptr = n, n = ptr->next) { - entry = list_entry(ptr, struct mac_entry, list); - list_del(ptr); - kfree(entry); - } - mac_restrictions->entries = 0; - spin_unlock_bh(&mac_restrictions->lock); -} - - -int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac) -{ - struct sta_info *sta; - __le16 resp; - - spin_lock_bh(&ap->sta_table_lock); - sta = ap_get_sta(ap, mac); - if (sta) { - ap_sta_hash_del(ap, sta); - list_del(&sta->list); - } - spin_unlock_bh(&ap->sta_table_lock); - - if (!sta) - return -EINVAL; - - resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); - prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH, - (char *) &resp, 2, sta->addr, 0); - - if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) - hostap_event_expired_sta(dev, sta); - - ap_free_sta(ap, sta); - - return 0; -} - -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - -void ap_control_kickall(struct ap_data *ap) -{ - struct list_head *ptr, *n; - struct sta_info *sta; - - spin_lock_bh(&ap->sta_table_lock); - for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list; - ptr = n, n = ptr->next) { - sta = list_entry(ptr, struct sta_info, list); - ap_sta_hash_del(ap, sta); - list_del(&sta->list); - if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) - hostap_event_expired_sta(sta->local->dev, sta); - ap_free_sta(ap, sta); - } - spin_unlock_bh(&ap->sta_table_lock); -} - - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - -static int prism2_ap_proc_show(struct seq_file *m, void *v) -{ - struct sta_info *sta = v; - int i; - - if (v == SEQ_START_TOKEN) { - seq_printf(m, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n"); - return 0; - } - - if (!sta->ap) - return 0; - - seq_printf(m, "%pM %d %d %d %d '", - sta->addr, - sta->u.ap.channel, sta->last_rx_signal, - sta->last_rx_silence, sta->last_rx_rate); - - for (i = 0; i < sta->u.ap.ssid_len; i++) { - if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127) - seq_putc(m, sta->u.ap.ssid[i]); - else - seq_printf(m, "<%02x>", sta->u.ap.ssid[i]); - } - - seq_putc(m, '\''); - if (sta->capability & WLAN_CAPABILITY_ESS) - seq_puts(m, " [ESS]"); - if (sta->capability & WLAN_CAPABILITY_IBSS) - seq_puts(m, " [IBSS]"); - if (sta->capability & WLAN_CAPABILITY_PRIVACY) - seq_puts(m, " [WEP]"); - seq_putc(m, '\n'); - return 0; -} - -static void *prism2_ap_proc_start(struct seq_file *m, loff_t *_pos) -{ - struct ap_data *ap = m->private; - spin_lock_bh(&ap->sta_table_lock); - return seq_list_start_head(&ap->sta_list, *_pos); -} - -static void *prism2_ap_proc_next(struct seq_file *m, void *v, loff_t *_pos) -{ - struct ap_data *ap = m->private; - return seq_list_next(v, &ap->sta_list, _pos); -} - -static void prism2_ap_proc_stop(struct seq_file *m, void *v) -{ - struct ap_data *ap = m->private; - spin_unlock_bh(&ap->sta_table_lock); -} - -static const struct seq_operations prism2_ap_proc_seqops = { - .start = prism2_ap_proc_start, - .next = prism2_ap_proc_next, - .stop = prism2_ap_proc_stop, - .show = prism2_ap_proc_show, -}; - -static int prism2_ap_proc_open(struct inode *inode, struct file *file) -{ - int ret = seq_open(file, &prism2_ap_proc_seqops); - if (ret == 0) { - struct seq_file *m = file->private_data; - m->private = PDE_DATA(inode); - } - return ret; -} - -static const struct file_operations prism2_ap_proc_fops = { - .open = prism2_ap_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - -void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver) -{ - if (!ap) - return; - - if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) { - PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - " - "firmware upgrade recommended\n"); - ap->nullfunc_ack = 1; - } else - ap->nullfunc_ack = 0; - - if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) { - printk(KERN_WARNING "%s: Warning: secondary station firmware " - "version 1.4.2 does not seem to work in Host AP mode\n", - ap->local->dev->name); - } -} - - -/* Called only as a tasklet (software IRQ) */ -static void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data) -{ - struct ap_data *ap = data; - struct ieee80211_hdr *hdr; - - if (!ap->local->hostapd || !ap->local->apdev) { - dev_kfree_skb(skb); - return; - } - - /* Pass the TX callback frame to the hostapd; use 802.11 header version - * 1 to indicate failure (no ACK) and 2 success (frame ACKed) */ - - hdr = (struct ieee80211_hdr *) skb->data; - hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_VERS); - hdr->frame_control |= cpu_to_le16(ok ? BIT(1) : BIT(0)); - - skb->dev = ap->local->apdev; - skb_pull(skb, hostap_80211_get_hdrlen(hdr->frame_control)); - skb->pkt_type = PACKET_OTHERHOST; - skb->protocol = cpu_to_be16(ETH_P_802_2); - memset(skb->cb, 0, sizeof(skb->cb)); - netif_rx(skb); -} - - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT -/* Called only as a tasklet (software IRQ) */ -static void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data) -{ - struct ap_data *ap = data; - struct net_device *dev = ap->local->dev; - struct ieee80211_hdr *hdr; - u16 auth_alg, auth_transaction, status; - __le16 *pos; - struct sta_info *sta = NULL; - char *txt = NULL; - - if (ap->local->hostapd) { - dev_kfree_skb(skb); - return; - } - - hdr = (struct ieee80211_hdr *) skb->data; - if (!ieee80211_is_auth(hdr->frame_control) || - skb->len < IEEE80211_MGMT_HDR_LEN + 6) { - printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid " - "frame\n", dev->name); - dev_kfree_skb(skb); - return; - } - - pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); - auth_alg = le16_to_cpu(*pos++); - auth_transaction = le16_to_cpu(*pos++); - status = le16_to_cpu(*pos++); - - if (!ok) { - txt = "frame was not ACKed"; - goto done; - } - - spin_lock(&ap->sta_table_lock); - sta = ap_get_sta(ap, hdr->addr1); - if (sta) - atomic_inc(&sta->users); - spin_unlock(&ap->sta_table_lock); - - if (!sta) { - txt = "STA not found"; - goto done; - } - - if (status == WLAN_STATUS_SUCCESS && - ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || - (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { - txt = "STA authenticated"; - sta->flags |= WLAN_STA_AUTH; - sta->last_auth = jiffies; - } else if (status != WLAN_STATUS_SUCCESS) - txt = "authentication failed"; - - done: - if (sta) - atomic_dec(&sta->users); - if (txt) { - PDEBUG(DEBUG_AP, "%s: %pM auth_cb - alg=%d " - "trans#=%d status=%d - %s\n", - dev->name, hdr->addr1, - auth_alg, auth_transaction, status, txt); - } - dev_kfree_skb(skb); -} - - -/* Called only as a tasklet (software IRQ) */ -static void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data) -{ - struct ap_data *ap = data; - struct net_device *dev = ap->local->dev; - struct ieee80211_hdr *hdr; - u16 status; - __le16 *pos; - struct sta_info *sta = NULL; - char *txt = NULL; - - if (ap->local->hostapd) { - dev_kfree_skb(skb); - return; - } - - hdr = (struct ieee80211_hdr *) skb->data; - if ((!ieee80211_is_assoc_resp(hdr->frame_control) && - !ieee80211_is_reassoc_resp(hdr->frame_control)) || - skb->len < IEEE80211_MGMT_HDR_LEN + 4) { - printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid " - "frame\n", dev->name); - dev_kfree_skb(skb); - return; - } - - if (!ok) { - txt = "frame was not ACKed"; - goto done; - } - - spin_lock(&ap->sta_table_lock); - sta = ap_get_sta(ap, hdr->addr1); - if (sta) - atomic_inc(&sta->users); - spin_unlock(&ap->sta_table_lock); - - if (!sta) { - txt = "STA not found"; - goto done; - } - - pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); - pos++; - status = le16_to_cpu(*pos++); - if (status == WLAN_STATUS_SUCCESS) { - if (!(sta->flags & WLAN_STA_ASSOC)) - hostap_event_new_sta(dev, sta); - txt = "STA associated"; - sta->flags |= WLAN_STA_ASSOC; - sta->last_assoc = jiffies; - } else - txt = "association failed"; - - done: - if (sta) - atomic_dec(&sta->users); - if (txt) { - PDEBUG(DEBUG_AP, "%s: %pM assoc_cb - %s\n", - dev->name, hdr->addr1, txt); - } - dev_kfree_skb(skb); -} - -/* Called only as a tasklet (software IRQ); TX callback for poll frames used - * in verifying whether the STA is still present. */ -static void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data) -{ - struct ap_data *ap = data; - struct ieee80211_hdr *hdr; - struct sta_info *sta; - - if (skb->len < 24) - goto fail; - hdr = (struct ieee80211_hdr *) skb->data; - if (ok) { - spin_lock(&ap->sta_table_lock); - sta = ap_get_sta(ap, hdr->addr1); - if (sta) - sta->flags &= ~WLAN_STA_PENDING_POLL; - spin_unlock(&ap->sta_table_lock); - } else { - PDEBUG(DEBUG_AP, - "%s: STA %pM did not ACK activity poll frame\n", - ap->local->dev->name, hdr->addr1); - } - - fail: - dev_kfree_skb(skb); -} -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - -void hostap_init_data(local_info_t *local) -{ - struct ap_data *ap = local->ap; - - if (ap == NULL) { - printk(KERN_WARNING "hostap_init_data: ap == NULL\n"); - return; - } - memset(ap, 0, sizeof(struct ap_data)); - ap->local = local; - - ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx); - ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx); - ap->max_inactivity = - GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ; - ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx); - - spin_lock_init(&ap->sta_table_lock); - INIT_LIST_HEAD(&ap->sta_list); - - /* Initialize task queue structure for AP management */ - INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue); - - ap->tx_callback_idx = - hostap_tx_callback_register(local, hostap_ap_tx_cb, ap); - if (ap->tx_callback_idx == 0) - printk(KERN_WARNING "%s: failed to register TX callback for " - "AP\n", local->dev->name); -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue); - - ap->tx_callback_auth = - hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap); - ap->tx_callback_assoc = - hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap); - ap->tx_callback_poll = - hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap); - if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 || - ap->tx_callback_poll == 0) - printk(KERN_WARNING "%s: failed to register TX callback for " - "AP\n", local->dev->name); - - spin_lock_init(&ap->mac_restrictions.lock); - INIT_LIST_HEAD(&ap->mac_restrictions.mac_list); -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - ap->initialized = 1; -} - - -void hostap_init_ap_proc(local_info_t *local) -{ - struct ap_data *ap = local->ap; - - ap->proc = local->proc; - if (ap->proc == NULL) - return; - -#ifndef PRISM2_NO_PROCFS_DEBUG - proc_create_data("ap_debug", 0, ap->proc, &ap_debug_proc_fops, ap); -#endif /* PRISM2_NO_PROCFS_DEBUG */ - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - proc_create_data("ap_control", 0, ap->proc, &ap_control_proc_fops, ap); - proc_create_data("ap", 0, ap->proc, &prism2_ap_proc_fops, ap); -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - -} - - -void hostap_free_data(struct ap_data *ap) -{ - struct sta_info *n, *sta; - - if (ap == NULL || !ap->initialized) { - printk(KERN_DEBUG "hostap_free_data: ap has not yet been " - "initialized - skip resource freeing\n"); - return; - } - - flush_work(&ap->add_sta_proc_queue); - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - flush_work(&ap->wds_oper_queue); - if (ap->crypt) - ap->crypt->deinit(ap->crypt_priv); - ap->crypt = ap->crypt_priv = NULL; -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - list_for_each_entry_safe(sta, n, &ap->sta_list, list) { - ap_sta_hash_del(ap, sta); - list_del(&sta->list); - if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) - hostap_event_expired_sta(sta->local->dev, sta); - ap_free_sta(ap, sta); - } - -#ifndef PRISM2_NO_PROCFS_DEBUG - if (ap->proc != NULL) { - remove_proc_entry("ap_debug", ap->proc); - } -#endif /* PRISM2_NO_PROCFS_DEBUG */ - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - if (ap->proc != NULL) { - remove_proc_entry("ap", ap->proc); - remove_proc_entry("ap_control", ap->proc); - } - ap_control_flush_macs(&ap->mac_restrictions); -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - ap->initialized = 0; -} - - -/* caller should have mutex for AP STA list handling */ -static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta) -{ - struct sta_info *s; - - s = ap->sta_hash[STA_HASH(sta)]; - while (s != NULL && !ether_addr_equal(s->addr, sta)) - s = s->hnext; - return s; -} - - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - -/* Called from timer handler and from scheduled AP queue handlers */ -static void prism2_send_mgmt(struct net_device *dev, - u16 type_subtype, char *body, - int body_len, u8 *addr, u16 tx_cb_idx) -{ - struct hostap_interface *iface; - local_info_t *local; - struct ieee80211_hdr *hdr; - u16 fc; - struct sk_buff *skb; - struct hostap_skb_tx_data *meta; - int hdrlen; - - iface = netdev_priv(dev); - local = iface->local; - dev = local->dev; /* always use master radio device */ - iface = netdev_priv(dev); - - if (!(dev->flags & IFF_UP)) { - PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - " - "cannot send frame\n", dev->name); - return; - } - - skb = dev_alloc_skb(sizeof(*hdr) + body_len); - if (skb == NULL) { - PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate " - "skb\n", dev->name); - return; - } - - fc = type_subtype; - hdrlen = hostap_80211_get_hdrlen(cpu_to_le16(type_subtype)); - hdr = (struct ieee80211_hdr *) skb_put(skb, hdrlen); - if (body) - memcpy(skb_put(skb, body_len), body, body_len); - - memset(hdr, 0, hdrlen); - - /* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11 - * tx_control instead of using local->tx_control */ - - - memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */ - if (ieee80211_is_data(hdr->frame_control)) { - fc |= IEEE80211_FCTL_FROMDS; - memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */ - memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */ - } else if (ieee80211_is_ctl(hdr->frame_control)) { - /* control:ACK does not have addr2 or addr3 */ - eth_zero_addr(hdr->addr2); - eth_zero_addr(hdr->addr3); - } else { - memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */ - memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */ - } - - hdr->frame_control = cpu_to_le16(fc); - - meta = (struct hostap_skb_tx_data *) skb->cb; - memset(meta, 0, sizeof(*meta)); - meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; - meta->iface = iface; - meta->tx_cb_idx = tx_cb_idx; - - skb->dev = dev; - skb_reset_mac_header(skb); - skb_reset_network_header(skb); - dev_queue_xmit(skb); -} -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - -static int prism2_sta_proc_show(struct seq_file *m, void *v) -{ - struct sta_info *sta = m->private; - int i; - - /* FIX: possible race condition.. the STA data could have just expired, - * but proc entry was still here so that the read could have started; - * some locking should be done here.. */ - - seq_printf(m, - "%s=%pM\nusers=%d\naid=%d\n" - "flags=0x%04x%s%s%s%s%s%s%s\n" - "capability=0x%02x\nlisten_interval=%d\nsupported_rates=", - sta->ap ? "AP" : "STA", - sta->addr, atomic_read(&sta->users), sta->aid, - sta->flags, - sta->flags & WLAN_STA_AUTH ? " AUTH" : "", - sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "", - sta->flags & WLAN_STA_PS ? " PS" : "", - sta->flags & WLAN_STA_TIM ? " TIM" : "", - sta->flags & WLAN_STA_PERM ? " PERM" : "", - sta->flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED" : "", - sta->flags & WLAN_STA_PENDING_POLL ? " POLL" : "", - sta->capability, sta->listen_interval); - /* supported_rates: 500 kbit/s units with msb ignored */ - for (i = 0; i < sizeof(sta->supported_rates); i++) - if (sta->supported_rates[i] != 0) - seq_printf(m, "%d%sMbps ", - (sta->supported_rates[i] & 0x7f) / 2, - sta->supported_rates[i] & 1 ? ".5" : ""); - seq_printf(m, - "\njiffies=%lu\nlast_auth=%lu\nlast_assoc=%lu\n" - "last_rx=%lu\nlast_tx=%lu\nrx_packets=%lu\n" - "tx_packets=%lu\n" - "rx_bytes=%lu\ntx_bytes=%lu\nbuffer_count=%d\n" - "last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps\n" - "tx_rate=%d\ntx[1M]=%d\ntx[2M]=%d\ntx[5.5M]=%d\n" - "tx[11M]=%d\n" - "rx[1M]=%d\nrx[2M]=%d\nrx[5.5M]=%d\nrx[11M]=%d\n", - jiffies, sta->last_auth, sta->last_assoc, sta->last_rx, - sta->last_tx, - sta->rx_packets, sta->tx_packets, sta->rx_bytes, - sta->tx_bytes, skb_queue_len(&sta->tx_buf), - sta->last_rx_silence, - sta->last_rx_signal, sta->last_rx_rate / 10, - sta->last_rx_rate % 10 ? ".5" : "", - sta->tx_rate, sta->tx_count[0], sta->tx_count[1], - sta->tx_count[2], sta->tx_count[3], sta->rx_count[0], - sta->rx_count[1], sta->rx_count[2], sta->rx_count[3]); - if (sta->crypt && sta->crypt->ops && sta->crypt->ops->print_stats) - sta->crypt->ops->print_stats(m, sta->crypt->priv); -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - if (sta->ap) { - if (sta->u.ap.channel >= 0) - seq_printf(m, "channel=%d\n", sta->u.ap.channel); - seq_puts(m, "ssid="); - for (i = 0; i < sta->u.ap.ssid_len; i++) { - if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127) - seq_putc(m, sta->u.ap.ssid[i]); - else - seq_printf(m, "<%02x>", sta->u.ap.ssid[i]); - } - seq_putc(m, '\n'); - } -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - return 0; -} - -static int prism2_sta_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, prism2_sta_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations prism2_sta_proc_fops = { - .open = prism2_sta_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void handle_add_proc_queue(struct work_struct *work) -{ - struct ap_data *ap = container_of(work, struct ap_data, - add_sta_proc_queue); - struct sta_info *sta; - char name[20]; - struct add_sta_proc_data *entry, *prev; - - entry = ap->add_sta_proc_entries; - ap->add_sta_proc_entries = NULL; - - while (entry) { - spin_lock_bh(&ap->sta_table_lock); - sta = ap_get_sta(ap, entry->addr); - if (sta) - atomic_inc(&sta->users); - spin_unlock_bh(&ap->sta_table_lock); - - if (sta) { - sprintf(name, "%pM", sta->addr); - sta->proc = proc_create_data( - name, 0, ap->proc, - &prism2_sta_proc_fops, sta); - - atomic_dec(&sta->users); - } - - prev = entry; - entry = entry->next; - kfree(prev); - } -} - - -static struct sta_info * ap_add_sta(struct ap_data *ap, u8 *addr) -{ - struct sta_info *sta; - - sta = kzalloc(sizeof(struct sta_info), GFP_ATOMIC); - if (sta == NULL) { - PDEBUG(DEBUG_AP, "AP: kmalloc failed\n"); - return NULL; - } - - /* initialize STA info data */ - sta->local = ap->local; - skb_queue_head_init(&sta->tx_buf); - memcpy(sta->addr, addr, ETH_ALEN); - - atomic_inc(&sta->users); - spin_lock_bh(&ap->sta_table_lock); - list_add(&sta->list, &ap->sta_list); - ap->num_sta++; - ap_sta_hash_add(ap, sta); - spin_unlock_bh(&ap->sta_table_lock); - - if (ap->proc) { - struct add_sta_proc_data *entry; - /* schedule a non-interrupt context process to add a procfs - * entry for the STA since procfs code use GFP_KERNEL */ - entry = kmalloc(sizeof(*entry), GFP_ATOMIC); - if (entry) { - memcpy(entry->addr, sta->addr, ETH_ALEN); - entry->next = ap->add_sta_proc_entries; - ap->add_sta_proc_entries = entry; - schedule_work(&ap->add_sta_proc_queue); - } else - printk(KERN_DEBUG "Failed to add STA proc data\n"); - } - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - init_timer(&sta->timer); - sta->timer.expires = jiffies + ap->max_inactivity; - sta->timer.data = (unsigned long) sta; - sta->timer.function = ap_handle_timer; - if (!ap->local->hostapd) - add_timer(&sta->timer); -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - return sta; -} - - -static int ap_tx_rate_ok(int rateidx, struct sta_info *sta, - local_info_t *local) -{ - if (rateidx > sta->tx_max_rate || - !(sta->tx_supp_rates & (1 << rateidx))) - return 0; - - if (local->tx_rate_control != 0 && - !(local->tx_rate_control & (1 << rateidx))) - return 0; - - return 1; -} - - -static void prism2_check_tx_rates(struct sta_info *sta) -{ - int i; - - sta->tx_supp_rates = 0; - for (i = 0; i < sizeof(sta->supported_rates); i++) { - if ((sta->supported_rates[i] & 0x7f) == 2) - sta->tx_supp_rates |= WLAN_RATE_1M; - if ((sta->supported_rates[i] & 0x7f) == 4) - sta->tx_supp_rates |= WLAN_RATE_2M; - if ((sta->supported_rates[i] & 0x7f) == 11) - sta->tx_supp_rates |= WLAN_RATE_5M5; - if ((sta->supported_rates[i] & 0x7f) == 22) - sta->tx_supp_rates |= WLAN_RATE_11M; - } - sta->tx_max_rate = sta->tx_rate = sta->tx_rate_idx = 0; - if (sta->tx_supp_rates & WLAN_RATE_1M) { - sta->tx_max_rate = 0; - if (ap_tx_rate_ok(0, sta, sta->local)) { - sta->tx_rate = 10; - sta->tx_rate_idx = 0; - } - } - if (sta->tx_supp_rates & WLAN_RATE_2M) { - sta->tx_max_rate = 1; - if (ap_tx_rate_ok(1, sta, sta->local)) { - sta->tx_rate = 20; - sta->tx_rate_idx = 1; - } - } - if (sta->tx_supp_rates & WLAN_RATE_5M5) { - sta->tx_max_rate = 2; - if (ap_tx_rate_ok(2, sta, sta->local)) { - sta->tx_rate = 55; - sta->tx_rate_idx = 2; - } - } - if (sta->tx_supp_rates & WLAN_RATE_11M) { - sta->tx_max_rate = 3; - if (ap_tx_rate_ok(3, sta, sta->local)) { - sta->tx_rate = 110; - sta->tx_rate_idx = 3; - } - } -} - - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - -static void ap_crypt_init(struct ap_data *ap) -{ - ap->crypt = lib80211_get_crypto_ops("WEP"); - - if (ap->crypt) { - if (ap->crypt->init) { - ap->crypt_priv = ap->crypt->init(0); - if (ap->crypt_priv == NULL) - ap->crypt = NULL; - else { - u8 key[WEP_KEY_LEN]; - get_random_bytes(key, WEP_KEY_LEN); - ap->crypt->set_key(key, WEP_KEY_LEN, NULL, - ap->crypt_priv); - } - } - } - - if (ap->crypt == NULL) { - printk(KERN_WARNING "AP could not initialize WEP: load module " - "lib80211_crypt_wep.ko\n"); - } -} - - -/* Generate challenge data for shared key authentication. IEEE 802.11 specifies - * that WEP algorithm is used for generating challenge. This should be unique, - * but otherwise there is not really need for randomness etc. Initialize WEP - * with pseudo random key and then use increasing IV to get unique challenge - * streams. - * - * Called only as a scheduled task for pending AP frames. - */ -static char * ap_auth_make_challenge(struct ap_data *ap) -{ - char *tmpbuf; - struct sk_buff *skb; - - if (ap->crypt == NULL) { - ap_crypt_init(ap); - if (ap->crypt == NULL) - return NULL; - } - - tmpbuf = kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC); - if (tmpbuf == NULL) { - PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n"); - return NULL; - } - - skb = dev_alloc_skb(WLAN_AUTH_CHALLENGE_LEN + - ap->crypt->extra_mpdu_prefix_len + - ap->crypt->extra_mpdu_postfix_len); - if (skb == NULL) { - kfree(tmpbuf); - return NULL; - } - - skb_reserve(skb, ap->crypt->extra_mpdu_prefix_len); - memset(skb_put(skb, WLAN_AUTH_CHALLENGE_LEN), 0, - WLAN_AUTH_CHALLENGE_LEN); - if (ap->crypt->encrypt_mpdu(skb, 0, ap->crypt_priv)) { - dev_kfree_skb(skb); - kfree(tmpbuf); - return NULL; - } - - skb_copy_from_linear_data_offset(skb, ap->crypt->extra_mpdu_prefix_len, - tmpbuf, WLAN_AUTH_CHALLENGE_LEN); - dev_kfree_skb(skb); - - return tmpbuf; -} - - -/* Called only as a scheduled task for pending AP frames. */ -static void handle_authen(local_info_t *local, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats) -{ - struct net_device *dev = local->dev; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - size_t hdrlen; - struct ap_data *ap = local->ap; - char body[8 + WLAN_AUTH_CHALLENGE_LEN], *challenge = NULL; - int len, olen; - u16 auth_alg, auth_transaction, status_code; - __le16 *pos; - u16 resp = WLAN_STATUS_SUCCESS; - struct sta_info *sta = NULL; - struct lib80211_crypt_data *crypt; - char *txt = ""; - - len = skb->len - IEEE80211_MGMT_HDR_LEN; - - hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); - - if (len < 6) { - PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload " - "(len=%d) from %pM\n", dev->name, len, hdr->addr2); - return; - } - - spin_lock_bh(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr2); - if (sta) - atomic_inc(&sta->users); - spin_unlock_bh(&local->ap->sta_table_lock); - - if (sta && sta->crypt) - crypt = sta->crypt; - else { - int idx = 0; - if (skb->len >= hdrlen + 3) - idx = skb->data[hdrlen + 3] >> 6; - crypt = local->crypt_info.crypt[idx]; - } - - pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); - auth_alg = __le16_to_cpu(*pos); - pos++; - auth_transaction = __le16_to_cpu(*pos); - pos++; - status_code = __le16_to_cpu(*pos); - pos++; - - if (ether_addr_equal(dev->dev_addr, hdr->addr2) || - ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) { - txt = "authentication denied"; - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - if (((local->auth_algs & PRISM2_AUTH_OPEN) && - auth_alg == WLAN_AUTH_OPEN) || - ((local->auth_algs & PRISM2_AUTH_SHARED_KEY) && - crypt && auth_alg == WLAN_AUTH_SHARED_KEY)) { - } else { - txt = "unsupported algorithm"; - resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; - goto fail; - } - - if (len >= 8) { - u8 *u = (u8 *) pos; - if (*u == WLAN_EID_CHALLENGE) { - if (*(u + 1) != WLAN_AUTH_CHALLENGE_LEN) { - txt = "invalid challenge len"; - resp = WLAN_STATUS_CHALLENGE_FAIL; - goto fail; - } - if (len - 8 < WLAN_AUTH_CHALLENGE_LEN) { - txt = "challenge underflow"; - resp = WLAN_STATUS_CHALLENGE_FAIL; - goto fail; - } - challenge = (char *) (u + 2); - } - } - - if (sta && sta->ap) { - if (time_after(jiffies, sta->u.ap.last_beacon + - (10 * sta->listen_interval * HZ) / 1024)) { - PDEBUG(DEBUG_AP, "%s: no beacons received for a while," - " assuming AP %pM is now STA\n", - dev->name, sta->addr); - sta->ap = 0; - sta->flags = 0; - sta->u.sta.challenge = NULL; - } else { - txt = "AP trying to authenticate?"; - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - } - - if ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) || - (auth_alg == WLAN_AUTH_SHARED_KEY && - (auth_transaction == 1 || - (auth_transaction == 3 && sta != NULL && - sta->u.sta.challenge != NULL)))) { - } else { - txt = "unknown authentication transaction number"; - resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; - goto fail; - } - - if (sta == NULL) { - txt = "new STA"; - - if (local->ap->num_sta >= MAX_STA_COUNT) { - /* FIX: might try to remove some old STAs first? */ - txt = "no more room for new STAs"; - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - sta = ap_add_sta(local->ap, hdr->addr2); - if (sta == NULL) { - txt = "ap_add_sta failed"; - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - } - - switch (auth_alg) { - case WLAN_AUTH_OPEN: - txt = "authOK"; - /* IEEE 802.11 standard is not completely clear about - * whether STA is considered authenticated after - * authentication OK frame has been send or after it - * has been ACKed. In order to reduce interoperability - * issues, mark the STA authenticated before ACK. */ - sta->flags |= WLAN_STA_AUTH; - break; - - case WLAN_AUTH_SHARED_KEY: - if (auth_transaction == 1) { - if (sta->u.sta.challenge == NULL) { - sta->u.sta.challenge = - ap_auth_make_challenge(local->ap); - if (sta->u.sta.challenge == NULL) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - } - } else { - if (sta->u.sta.challenge == NULL || - challenge == NULL || - memcmp(sta->u.sta.challenge, challenge, - WLAN_AUTH_CHALLENGE_LEN) != 0 || - !ieee80211_has_protected(hdr->frame_control)) { - txt = "challenge response incorrect"; - resp = WLAN_STATUS_CHALLENGE_FAIL; - goto fail; - } - - txt = "challenge OK - authOK"; - /* IEEE 802.11 standard is not completely clear about - * whether STA is considered authenticated after - * authentication OK frame has been send or after it - * has been ACKed. In order to reduce interoperability - * issues, mark the STA authenticated before ACK. */ - sta->flags |= WLAN_STA_AUTH; - kfree(sta->u.sta.challenge); - sta->u.sta.challenge = NULL; - } - break; - } - - fail: - pos = (__le16 *) body; - *pos = cpu_to_le16(auth_alg); - pos++; - *pos = cpu_to_le16(auth_transaction + 1); - pos++; - *pos = cpu_to_le16(resp); /* status_code */ - pos++; - olen = 6; - - if (resp == WLAN_STATUS_SUCCESS && sta != NULL && - sta->u.sta.challenge != NULL && - auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 1) { - u8 *tmp = (u8 *) pos; - *tmp++ = WLAN_EID_CHALLENGE; - *tmp++ = WLAN_AUTH_CHALLENGE_LEN; - pos++; - memcpy(pos, sta->u.sta.challenge, WLAN_AUTH_CHALLENGE_LEN); - olen += 2 + WLAN_AUTH_CHALLENGE_LEN; - } - - prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH, - body, olen, hdr->addr2, ap->tx_callback_auth); - - if (sta) { - sta->last_rx = jiffies; - atomic_dec(&sta->users); - } - - if (resp) { - PDEBUG(DEBUG_AP, "%s: %pM auth (alg=%d " - "trans#=%d stat=%d len=%d fc=%04x) ==> %d (%s)\n", - dev->name, hdr->addr2, - auth_alg, auth_transaction, status_code, len, - le16_to_cpu(hdr->frame_control), resp, txt); - } -} - - -/* Called only as a scheduled task for pending AP frames. */ -static void handle_assoc(local_info_t *local, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats, int reassoc) -{ - struct net_device *dev = local->dev; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - char body[12], *p, *lpos; - int len, left; - __le16 *pos; - u16 resp = WLAN_STATUS_SUCCESS; - struct sta_info *sta = NULL; - int send_deauth = 0; - char *txt = ""; - u8 prev_ap[ETH_ALEN]; - - left = len = skb->len - IEEE80211_MGMT_HDR_LEN; - - if (len < (reassoc ? 10 : 4)) { - PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload " - "(len=%d, reassoc=%d) from %pM\n", - dev->name, len, reassoc, hdr->addr2); - return; - } - - spin_lock_bh(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr2); - if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { - spin_unlock_bh(&local->ap->sta_table_lock); - txt = "trying to associate before authentication"; - send_deauth = 1; - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - sta = NULL; /* do not decrement sta->users */ - goto fail; - } - atomic_inc(&sta->users); - spin_unlock_bh(&local->ap->sta_table_lock); - - pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); - sta->capability = __le16_to_cpu(*pos); - pos++; left -= 2; - sta->listen_interval = __le16_to_cpu(*pos); - pos++; left -= 2; - - if (reassoc) { - memcpy(prev_ap, pos, ETH_ALEN); - pos++; pos++; pos++; left -= 6; - } else - eth_zero_addr(prev_ap); - - if (left >= 2) { - unsigned int ileft; - unsigned char *u = (unsigned char *) pos; - - if (*u == WLAN_EID_SSID) { - u++; left--; - ileft = *u; - u++; left--; - - if (ileft > left || ileft > MAX_SSID_LEN) { - txt = "SSID overflow"; - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - if (ileft != strlen(local->essid) || - memcmp(local->essid, u, ileft) != 0) { - txt = "not our SSID"; - resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC; - goto fail; - } - - u += ileft; - left -= ileft; - } - - if (left >= 2 && *u == WLAN_EID_SUPP_RATES) { - u++; left--; - ileft = *u; - u++; left--; - - if (ileft > left || ileft == 0 || - ileft > WLAN_SUPP_RATES_MAX) { - txt = "SUPP_RATES len error"; - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - memset(sta->supported_rates, 0, - sizeof(sta->supported_rates)); - memcpy(sta->supported_rates, u, ileft); - prism2_check_tx_rates(sta); - - u += ileft; - left -= ileft; - } - - if (left > 0) { - PDEBUG(DEBUG_AP, "%s: assoc from %pM" - " with extra data (%d bytes) [", - dev->name, hdr->addr2, left); - while (left > 0) { - PDEBUG2(DEBUG_AP, "<%02x>", *u); - u++; left--; - } - PDEBUG2(DEBUG_AP, "]\n"); - } - } else { - txt = "frame underflow"; - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - /* get a unique AID */ - if (sta->aid > 0) - txt = "OK, old AID"; - else { - spin_lock_bh(&local->ap->sta_table_lock); - for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++) - if (local->ap->sta_aid[sta->aid - 1] == NULL) - break; - if (sta->aid > MAX_AID_TABLE_SIZE) { - sta->aid = 0; - spin_unlock_bh(&local->ap->sta_table_lock); - resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; - txt = "no room for more AIDs"; - } else { - local->ap->sta_aid[sta->aid - 1] = sta; - spin_unlock_bh(&local->ap->sta_table_lock); - txt = "OK, new AID"; - } - } - - fail: - pos = (__le16 *) body; - - if (send_deauth) { - *pos = cpu_to_le16(WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH); - pos++; - } else { - /* FIX: CF-Pollable and CF-PollReq should be set to match the - * values in beacons/probe responses */ - /* FIX: how about privacy and WEP? */ - /* capability */ - *pos = cpu_to_le16(WLAN_CAPABILITY_ESS); - pos++; - - /* status_code */ - *pos = cpu_to_le16(resp); - pos++; - - *pos = cpu_to_le16((sta && sta->aid > 0 ? sta->aid : 0) | - BIT(14) | BIT(15)); /* AID */ - pos++; - - /* Supported rates (Information element) */ - p = (char *) pos; - *p++ = WLAN_EID_SUPP_RATES; - lpos = p; - *p++ = 0; /* len */ - if (local->tx_rate_control & WLAN_RATE_1M) { - *p++ = local->basic_rates & WLAN_RATE_1M ? 0x82 : 0x02; - (*lpos)++; - } - if (local->tx_rate_control & WLAN_RATE_2M) { - *p++ = local->basic_rates & WLAN_RATE_2M ? 0x84 : 0x04; - (*lpos)++; - } - if (local->tx_rate_control & WLAN_RATE_5M5) { - *p++ = local->basic_rates & WLAN_RATE_5M5 ? - 0x8b : 0x0b; - (*lpos)++; - } - if (local->tx_rate_control & WLAN_RATE_11M) { - *p++ = local->basic_rates & WLAN_RATE_11M ? - 0x96 : 0x16; - (*lpos)++; - } - pos = (__le16 *) p; - } - - prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | - (send_deauth ? IEEE80211_STYPE_DEAUTH : - (reassoc ? IEEE80211_STYPE_REASSOC_RESP : - IEEE80211_STYPE_ASSOC_RESP)), - body, (u8 *) pos - (u8 *) body, - hdr->addr2, - send_deauth ? 0 : local->ap->tx_callback_assoc); - - if (sta) { - if (resp == WLAN_STATUS_SUCCESS) { - sta->last_rx = jiffies; - /* STA will be marked associated from TX callback, if - * AssocResp is ACKed */ - } - atomic_dec(&sta->users); - } - -#if 0 - PDEBUG(DEBUG_AP, "%s: %pM %sassoc (len=%d " - "prev_ap=%pM) => %d(%d) (%s)\n", - dev->name, - hdr->addr2, - reassoc ? "re" : "", len, - prev_ap, - resp, send_deauth, txt); -#endif -} - - -/* Called only as a scheduled task for pending AP frames. */ -static void handle_deauth(local_info_t *local, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats) -{ - struct net_device *dev = local->dev; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - char *body = (char *) (skb->data + IEEE80211_MGMT_HDR_LEN); - int len; - u16 reason_code; - __le16 *pos; - struct sta_info *sta = NULL; - - len = skb->len - IEEE80211_MGMT_HDR_LEN; - - if (len < 2) { - printk("handle_deauth - too short payload (len=%d)\n", len); - return; - } - - pos = (__le16 *) body; - reason_code = le16_to_cpu(*pos); - - PDEBUG(DEBUG_AP, "%s: deauthentication: %pM len=%d, " - "reason_code=%d\n", dev->name, hdr->addr2, - len, reason_code); - - spin_lock_bh(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr2); - if (sta != NULL) { - if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) - hostap_event_expired_sta(local->dev, sta); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - } - spin_unlock_bh(&local->ap->sta_table_lock); - if (sta == NULL) { - printk("%s: deauthentication from %pM, " - "reason_code=%d, but STA not authenticated\n", dev->name, - hdr->addr2, reason_code); - } -} - - -/* Called only as a scheduled task for pending AP frames. */ -static void handle_disassoc(local_info_t *local, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats) -{ - struct net_device *dev = local->dev; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - char *body = skb->data + IEEE80211_MGMT_HDR_LEN; - int len; - u16 reason_code; - __le16 *pos; - struct sta_info *sta = NULL; - - len = skb->len - IEEE80211_MGMT_HDR_LEN; - - if (len < 2) { - printk("handle_disassoc - too short payload (len=%d)\n", len); - return; - } - - pos = (__le16 *) body; - reason_code = le16_to_cpu(*pos); - - PDEBUG(DEBUG_AP, "%s: disassociation: %pM len=%d, " - "reason_code=%d\n", dev->name, hdr->addr2, - len, reason_code); - - spin_lock_bh(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr2); - if (sta != NULL) { - if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) - hostap_event_expired_sta(local->dev, sta); - sta->flags &= ~WLAN_STA_ASSOC; - } - spin_unlock_bh(&local->ap->sta_table_lock); - if (sta == NULL) { - printk("%s: disassociation from %pM, " - "reason_code=%d, but STA not authenticated\n", - dev->name, hdr->addr2, reason_code); - } -} - - -/* Called only as a scheduled task for pending AP frames. */ -static void ap_handle_data_nullfunc(local_info_t *local, - struct ieee80211_hdr *hdr) -{ - struct net_device *dev = local->dev; - - /* some STA f/w's seem to require control::ACK frame for - * data::nullfunc, but at least Prism2 station f/w version 0.8.0 does - * not send this.. - * send control::ACK for the data::nullfunc */ - - printk(KERN_DEBUG "Sending control::ACK for data::nullfunc\n"); - prism2_send_mgmt(dev, IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK, - NULL, 0, hdr->addr2, 0); -} - - -/* Called only as a scheduled task for pending AP frames. */ -static void ap_handle_dropped_data(local_info_t *local, - struct ieee80211_hdr *hdr) -{ - struct net_device *dev = local->dev; - struct sta_info *sta; - __le16 reason; - - spin_lock_bh(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr2); - if (sta) - atomic_inc(&sta->users); - spin_unlock_bh(&local->ap->sta_table_lock); - - if (sta != NULL && (sta->flags & WLAN_STA_ASSOC)) { - PDEBUG(DEBUG_AP, "ap_handle_dropped_data: STA is now okay?\n"); - atomic_dec(&sta->users); - return; - } - - reason = cpu_to_le16(WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); - prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | - ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ? - IEEE80211_STYPE_DEAUTH : IEEE80211_STYPE_DISASSOC), - (char *) &reason, sizeof(reason), hdr->addr2, 0); - - if (sta) - atomic_dec(&sta->users); -} - -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - -/* Called only as a scheduled task for pending AP frames. */ -static void pspoll_send_buffered(local_info_t *local, struct sta_info *sta, - struct sk_buff *skb) -{ - struct hostap_skb_tx_data *meta; - - if (!(sta->flags & WLAN_STA_PS)) { - /* Station has moved to non-PS mode, so send all buffered - * frames using normal device queue. */ - dev_queue_xmit(skb); - return; - } - - /* add a flag for hostap_handle_sta_tx() to know that this skb should - * be passed through even though STA is using PS */ - meta = (struct hostap_skb_tx_data *) skb->cb; - meta->flags |= HOSTAP_TX_FLAGS_BUFFERED_FRAME; - if (!skb_queue_empty(&sta->tx_buf)) { - /* indicate to STA that more frames follow */ - meta->flags |= HOSTAP_TX_FLAGS_ADD_MOREDATA; - } - dev_queue_xmit(skb); -} - - -/* Called only as a scheduled task for pending AP frames. */ -static void handle_pspoll(local_info_t *local, - struct ieee80211_hdr *hdr, - struct hostap_80211_rx_status *rx_stats) -{ - struct net_device *dev = local->dev; - struct sta_info *sta; - u16 aid; - struct sk_buff *skb; - - PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=%pM, TA=%pM PWRMGT=%d\n", - hdr->addr1, hdr->addr2, !!ieee80211_has_pm(hdr->frame_control)); - - if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { - PDEBUG(DEBUG_AP, - "handle_pspoll - addr1(BSSID)=%pM not own MAC\n", - hdr->addr1); - return; - } - - aid = le16_to_cpu(hdr->duration_id); - if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) { - PDEBUG(DEBUG_PS, " PSPOLL and AID[15:14] not set\n"); - return; - } - aid &= ~(BIT(15) | BIT(14)); - if (aid == 0 || aid > MAX_AID_TABLE_SIZE) { - PDEBUG(DEBUG_PS, " invalid aid=%d\n", aid); - return; - } - PDEBUG(DEBUG_PS2, " aid=%d\n", aid); - - spin_lock_bh(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr2); - if (sta) - atomic_inc(&sta->users); - spin_unlock_bh(&local->ap->sta_table_lock); - - if (sta == NULL) { - PDEBUG(DEBUG_PS, " STA not found\n"); - return; - } - if (sta->aid != aid) { - PDEBUG(DEBUG_PS, " received aid=%i does not match with " - "assoc.aid=%d\n", aid, sta->aid); - return; - } - - /* FIX: todo: - * - add timeout for buffering (clear aid in TIM vector if buffer timed - * out (expiry time must be longer than ListenInterval for - * the corresponding STA; "8802-11: 11.2.1.9 AP aging function" - * - what to do, if buffered, pspolled, and sent frame is not ACKed by - * sta; store buffer for later use and leave TIM aid bit set? use - * TX event to check whether frame was ACKed? - */ - - while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) { - /* send buffered frame .. */ - PDEBUG(DEBUG_PS2, "Sending buffered frame to STA after PS POLL" - " (buffer_count=%d)\n", skb_queue_len(&sta->tx_buf)); - - pspoll_send_buffered(local, sta, skb); - - if (sta->flags & WLAN_STA_PS) { - /* send only one buffered packet per PS Poll */ - /* FIX: should ignore further PS Polls until the - * buffered packet that was just sent is acknowledged - * (Tx or TxExc event) */ - break; - } - } - - if (skb_queue_empty(&sta->tx_buf)) { - /* try to clear aid from TIM */ - if (!(sta->flags & WLAN_STA_TIM)) - PDEBUG(DEBUG_PS2, "Re-unsetting TIM for aid %d\n", - aid); - hostap_set_tim(local, aid, 0); - sta->flags &= ~WLAN_STA_TIM; - } - - atomic_dec(&sta->users); -} - - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - -static void handle_wds_oper_queue(struct work_struct *work) -{ - struct ap_data *ap = container_of(work, struct ap_data, - wds_oper_queue); - local_info_t *local = ap->local; - struct wds_oper_data *entry, *prev; - - spin_lock_bh(&local->lock); - entry = local->ap->wds_oper_entries; - local->ap->wds_oper_entries = NULL; - spin_unlock_bh(&local->lock); - - while (entry) { - PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection " - "to AP %pM\n", - local->dev->name, - entry->type == WDS_ADD ? "adding" : "removing", - entry->addr); - if (entry->type == WDS_ADD) - prism2_wds_add(local, entry->addr, 0); - else if (entry->type == WDS_DEL) - prism2_wds_del(local, entry->addr, 0, 1); - - prev = entry; - entry = entry->next; - kfree(prev); - } -} - - -/* Called only as a scheduled task for pending AP frames. */ -static void handle_beacon(local_info_t *local, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - char *body = skb->data + IEEE80211_MGMT_HDR_LEN; - int len, left; - u16 beacon_int, capability; - __le16 *pos; - char *ssid = NULL; - unsigned char *supp_rates = NULL; - int ssid_len = 0, supp_rates_len = 0; - struct sta_info *sta = NULL; - int new_sta = 0, channel = -1; - - len = skb->len - IEEE80211_MGMT_HDR_LEN; - - if (len < 8 + 2 + 2) { - printk(KERN_DEBUG "handle_beacon - too short payload " - "(len=%d)\n", len); - return; - } - - pos = (__le16 *) body; - left = len; - - /* Timestamp (8 octets) */ - pos += 4; left -= 8; - /* Beacon interval (2 octets) */ - beacon_int = le16_to_cpu(*pos); - pos++; left -= 2; - /* Capability information (2 octets) */ - capability = le16_to_cpu(*pos); - pos++; left -= 2; - - if (local->ap->ap_policy != AP_OTHER_AP_EVEN_IBSS && - capability & WLAN_CAPABILITY_IBSS) - return; - - if (left >= 2) { - unsigned int ileft; - unsigned char *u = (unsigned char *) pos; - - if (*u == WLAN_EID_SSID) { - u++; left--; - ileft = *u; - u++; left--; - - if (ileft > left || ileft > MAX_SSID_LEN) { - PDEBUG(DEBUG_AP, "SSID: overflow\n"); - return; - } - - if (local->ap->ap_policy == AP_OTHER_AP_SAME_SSID && - (ileft != strlen(local->essid) || - memcmp(local->essid, u, ileft) != 0)) { - /* not our SSID */ - return; - } - - ssid = u; - ssid_len = ileft; - - u += ileft; - left -= ileft; - } - - if (*u == WLAN_EID_SUPP_RATES) { - u++; left--; - ileft = *u; - u++; left--; - - if (ileft > left || ileft == 0 || ileft > 8) { - PDEBUG(DEBUG_AP, " - SUPP_RATES len error\n"); - return; - } - - supp_rates = u; - supp_rates_len = ileft; - - u += ileft; - left -= ileft; - } - - if (*u == WLAN_EID_DS_PARAMS) { - u++; left--; - ileft = *u; - u++; left--; - - if (ileft > left || ileft != 1) { - PDEBUG(DEBUG_AP, " - DS_PARAMS len error\n"); - return; - } - - channel = *u; - - u += ileft; - left -= ileft; - } - } - - spin_lock_bh(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr2); - if (sta != NULL) - atomic_inc(&sta->users); - spin_unlock_bh(&local->ap->sta_table_lock); - - if (sta == NULL) { - /* add new AP */ - new_sta = 1; - sta = ap_add_sta(local->ap, hdr->addr2); - if (sta == NULL) { - printk(KERN_INFO "prism2: kmalloc failed for AP " - "data structure\n"); - return; - } - hostap_event_new_sta(local->dev, sta); - - /* mark APs authentication and associated for pseudo ad-hoc - * style communication */ - sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; - - if (local->ap->autom_ap_wds) { - hostap_wds_link_oper(local, sta->addr, WDS_ADD); - } - } - - sta->ap = 1; - if (ssid) { - sta->u.ap.ssid_len = ssid_len; - memcpy(sta->u.ap.ssid, ssid, ssid_len); - sta->u.ap.ssid[ssid_len] = '\0'; - } else { - sta->u.ap.ssid_len = 0; - sta->u.ap.ssid[0] = '\0'; - } - sta->u.ap.channel = channel; - sta->rx_packets++; - sta->rx_bytes += len; - sta->u.ap.last_beacon = sta->last_rx = jiffies; - sta->capability = capability; - sta->listen_interval = beacon_int; - - atomic_dec(&sta->users); - - if (new_sta) { - memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); - memcpy(sta->supported_rates, supp_rates, supp_rates_len); - prism2_check_tx_rates(sta); - } -} - -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - -/* Called only as a tasklet. */ -static void handle_ap_item(local_info_t *local, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats) -{ -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - struct net_device *dev = local->dev; -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - u16 fc, type, stype; - struct ieee80211_hdr *hdr; - - /* FIX: should give skb->len to handler functions and check that the - * buffer is long enough */ - hdr = (struct ieee80211_hdr *) skb->data; - fc = le16_to_cpu(hdr->frame_control); - type = fc & IEEE80211_FCTL_FTYPE; - stype = fc & IEEE80211_FCTL_STYPE; - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - if (!local->hostapd && type == IEEE80211_FTYPE_DATA) { - PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n"); - - if (!(fc & IEEE80211_FCTL_TODS) || - (fc & IEEE80211_FCTL_FROMDS)) { - if (stype == IEEE80211_STYPE_NULLFUNC) { - /* no ToDS nullfunc seems to be used to check - * AP association; so send reject message to - * speed up re-association */ - ap_handle_dropped_data(local, hdr); - goto done; - } - PDEBUG(DEBUG_AP, " not ToDS frame (fc=0x%04x)\n", - fc); - goto done; - } - - if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { - PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)=%pM" - " not own MAC\n", hdr->addr1); - goto done; - } - - if (local->ap->nullfunc_ack && - stype == IEEE80211_STYPE_NULLFUNC) - ap_handle_data_nullfunc(local, hdr); - else - ap_handle_dropped_data(local, hdr); - goto done; - } - - if (type == IEEE80211_FTYPE_MGMT && stype == IEEE80211_STYPE_BEACON) { - handle_beacon(local, skb, rx_stats); - goto done; - } -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - if (type == IEEE80211_FTYPE_CTL && stype == IEEE80211_STYPE_PSPOLL) { - handle_pspoll(local, hdr, rx_stats); - goto done; - } - - if (local->hostapd) { - PDEBUG(DEBUG_AP, "Unknown frame in AP queue: type=0x%02x " - "subtype=0x%02x\n", type, stype); - goto done; - } - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - if (type != IEEE80211_FTYPE_MGMT) { - PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n"); - goto done; - } - - if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { - PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=%pM" - " not own MAC\n", hdr->addr1); - goto done; - } - - if (!ether_addr_equal(hdr->addr3, dev->dev_addr)) { - PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=%pM" - " not own MAC\n", hdr->addr3); - goto done; - } - - switch (stype) { - case IEEE80211_STYPE_ASSOC_REQ: - handle_assoc(local, skb, rx_stats, 0); - break; - case IEEE80211_STYPE_ASSOC_RESP: - PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n"); - break; - case IEEE80211_STYPE_REASSOC_REQ: - handle_assoc(local, skb, rx_stats, 1); - break; - case IEEE80211_STYPE_REASSOC_RESP: - PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n"); - break; - case IEEE80211_STYPE_ATIM: - PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n"); - break; - case IEEE80211_STYPE_DISASSOC: - handle_disassoc(local, skb, rx_stats); - break; - case IEEE80211_STYPE_AUTH: - handle_authen(local, skb, rx_stats); - break; - case IEEE80211_STYPE_DEAUTH: - handle_deauth(local, skb, rx_stats); - break; - default: - PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n", - stype >> 4); - break; - } -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - done: - dev_kfree_skb(skb); -} - - -/* Called only as a tasklet (software IRQ) */ -void hostap_rx(struct net_device *dev, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats) -{ - struct hostap_interface *iface; - local_info_t *local; - struct ieee80211_hdr *hdr; - - iface = netdev_priv(dev); - local = iface->local; - - if (skb->len < 16) - goto drop; - - dev->stats.rx_packets++; - - hdr = (struct ieee80211_hdr *) skb->data; - - if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL && - ieee80211_is_beacon(hdr->frame_control)) - goto drop; - - skb->protocol = cpu_to_be16(ETH_P_HOSTAP); - handle_ap_item(local, skb, rx_stats); - return; - - drop: - dev_kfree_skb(skb); -} - - -/* Called only as a tasklet (software IRQ) */ -static void schedule_packet_send(local_info_t *local, struct sta_info *sta) -{ - struct sk_buff *skb; - struct ieee80211_hdr *hdr; - struct hostap_80211_rx_status rx_stats; - - if (skb_queue_empty(&sta->tx_buf)) - return; - - skb = dev_alloc_skb(16); - if (skb == NULL) { - printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc " - "failed\n", local->dev->name); - return; - } - - hdr = (struct ieee80211_hdr *) skb_put(skb, 16); - - /* Generate a fake pspoll frame to start packet delivery */ - hdr->frame_control = cpu_to_le16( - IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); - memcpy(hdr->addr1, local->dev->dev_addr, ETH_ALEN); - memcpy(hdr->addr2, sta->addr, ETH_ALEN); - hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14)); - - PDEBUG(DEBUG_PS2, - "%s: Scheduling buffered packet delivery for STA %pM\n", - local->dev->name, sta->addr); - - skb->dev = local->dev; - - memset(&rx_stats, 0, sizeof(rx_stats)); - hostap_rx(local->dev, skb, &rx_stats); -} - - -int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], - struct iw_quality qual[], int buf_size, - int aplist) -{ - struct ap_data *ap = local->ap; - struct list_head *ptr; - int count = 0; - - spin_lock_bh(&ap->sta_table_lock); - - for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list; - ptr = ptr->next) { - struct sta_info *sta = (struct sta_info *) ptr; - - if (aplist && !sta->ap) - continue; - addr[count].sa_family = ARPHRD_ETHER; - memcpy(addr[count].sa_data, sta->addr, ETH_ALEN); - if (sta->last_rx_silence == 0) - qual[count].qual = sta->last_rx_signal < 27 ? - 0 : (sta->last_rx_signal - 27) * 92 / 127; - else - qual[count].qual = sta->last_rx_signal - - sta->last_rx_silence - 35; - qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); - qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); - qual[count].updated = sta->last_rx_updated; - - sta->last_rx_updated = IW_QUAL_DBM; - - count++; - if (count >= buf_size) - break; - } - spin_unlock_bh(&ap->sta_table_lock); - - return count; -} - - -/* Translate our list of Access Points & Stations to a card independent - * format that the Wireless Tools will understand - Jean II */ -int prism2_ap_translate_scan(struct net_device *dev, - struct iw_request_info *info, char *buffer) -{ - struct hostap_interface *iface; - local_info_t *local; - struct ap_data *ap; - struct list_head *ptr; - struct iw_event iwe; - char *current_ev = buffer; - char *end_buf = buffer + IW_SCAN_MAX_DATA; -#if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT) - char buf[64]; -#endif - - iface = netdev_priv(dev); - local = iface->local; - ap = local->ap; - - spin_lock_bh(&ap->sta_table_lock); - - for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list; - ptr = ptr->next) { - struct sta_info *sta = (struct sta_info *) ptr; - - /* First entry *MUST* be the AP MAC address */ - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN); - iwe.len = IW_EV_ADDR_LEN; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_ADDR_LEN); - - /* Use the mode to indicate if it's a station or - * an Access Point */ - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWMODE; - if (sta->ap) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_INFRA; - iwe.len = IW_EV_UINT_LEN; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_UINT_LEN); - - /* Some quality */ - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVQUAL; - if (sta->last_rx_silence == 0) - iwe.u.qual.qual = sta->last_rx_signal < 27 ? - 0 : (sta->last_rx_signal - 27) * 92 / 127; - else - iwe.u.qual.qual = sta->last_rx_signal - - sta->last_rx_silence - 35; - iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); - iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); - iwe.u.qual.updated = sta->last_rx_updated; - iwe.len = IW_EV_QUAL_LEN; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_QUAL_LEN); - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - if (sta->ap) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWESSID; - iwe.u.data.length = sta->u.ap.ssid_len; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, &iwe, - sta->u.ap.ssid); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWENCODE; - if (sta->capability & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = - IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, &iwe, - sta->u.ap.ssid); - - if (sta->u.ap.channel > 0 && - sta->u.ap.channel <= FREQ_COUNT) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = freq_list[sta->u.ap.channel - 1] - * 100000; - iwe.u.freq.e = 1; - current_ev = iwe_stream_add_event( - info, current_ev, end_buf, &iwe, - IW_EV_FREQ_LEN); - } - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "beacon_interval=%d", - sta->listen_interval); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, &iwe, buf); - } -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - sta->last_rx_updated = IW_QUAL_DBM; - - /* To be continued, we should make good use of IWEVCUSTOM */ - } - - spin_unlock_bh(&ap->sta_table_lock); - - return current_ev - buffer; -} - - -static int prism2_hostapd_add_sta(struct ap_data *ap, - struct prism2_hostapd_param *param) -{ - struct sta_info *sta; - - spin_lock_bh(&ap->sta_table_lock); - sta = ap_get_sta(ap, param->sta_addr); - if (sta) - atomic_inc(&sta->users); - spin_unlock_bh(&ap->sta_table_lock); - - if (sta == NULL) { - sta = ap_add_sta(ap, param->sta_addr); - if (sta == NULL) - return -1; - } - - if (!(sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) - hostap_event_new_sta(sta->local->dev, sta); - - sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; - sta->last_rx = jiffies; - sta->aid = param->u.add_sta.aid; - sta->capability = param->u.add_sta.capability; - sta->tx_supp_rates = param->u.add_sta.tx_supp_rates; - if (sta->tx_supp_rates & WLAN_RATE_1M) - sta->supported_rates[0] = 2; - if (sta->tx_supp_rates & WLAN_RATE_2M) - sta->supported_rates[1] = 4; - if (sta->tx_supp_rates & WLAN_RATE_5M5) - sta->supported_rates[2] = 11; - if (sta->tx_supp_rates & WLAN_RATE_11M) - sta->supported_rates[3] = 22; - prism2_check_tx_rates(sta); - atomic_dec(&sta->users); - return 0; -} - - -static int prism2_hostapd_remove_sta(struct ap_data *ap, - struct prism2_hostapd_param *param) -{ - struct sta_info *sta; - - spin_lock_bh(&ap->sta_table_lock); - sta = ap_get_sta(ap, param->sta_addr); - if (sta) { - ap_sta_hash_del(ap, sta); - list_del(&sta->list); - } - spin_unlock_bh(&ap->sta_table_lock); - - if (!sta) - return -ENOENT; - - if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) - hostap_event_expired_sta(sta->local->dev, sta); - ap_free_sta(ap, sta); - - return 0; -} - - -static int prism2_hostapd_get_info_sta(struct ap_data *ap, - struct prism2_hostapd_param *param) -{ - struct sta_info *sta; - - spin_lock_bh(&ap->sta_table_lock); - sta = ap_get_sta(ap, param->sta_addr); - if (sta) - atomic_inc(&sta->users); - spin_unlock_bh(&ap->sta_table_lock); - - if (!sta) - return -ENOENT; - - param->u.get_info_sta.inactive_sec = (jiffies - sta->last_rx) / HZ; - - atomic_dec(&sta->users); - - return 1; -} - - -static int prism2_hostapd_set_flags_sta(struct ap_data *ap, - struct prism2_hostapd_param *param) -{ - struct sta_info *sta; - - spin_lock_bh(&ap->sta_table_lock); - sta = ap_get_sta(ap, param->sta_addr); - if (sta) { - sta->flags |= param->u.set_flags_sta.flags_or; - sta->flags &= param->u.set_flags_sta.flags_and; - } - spin_unlock_bh(&ap->sta_table_lock); - - if (!sta) - return -ENOENT; - - return 0; -} - - -static int prism2_hostapd_sta_clear_stats(struct ap_data *ap, - struct prism2_hostapd_param *param) -{ - struct sta_info *sta; - int rate; - - spin_lock_bh(&ap->sta_table_lock); - sta = ap_get_sta(ap, param->sta_addr); - if (sta) { - sta->rx_packets = sta->tx_packets = 0; - sta->rx_bytes = sta->tx_bytes = 0; - for (rate = 0; rate < WLAN_RATE_COUNT; rate++) { - sta->tx_count[rate] = 0; - sta->rx_count[rate] = 0; - } - } - spin_unlock_bh(&ap->sta_table_lock); - - if (!sta) - return -ENOENT; - - return 0; -} - - -int prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param) -{ - switch (param->cmd) { - case PRISM2_HOSTAPD_FLUSH: - ap_control_kickall(ap); - return 0; - case PRISM2_HOSTAPD_ADD_STA: - return prism2_hostapd_add_sta(ap, param); - case PRISM2_HOSTAPD_REMOVE_STA: - return prism2_hostapd_remove_sta(ap, param); - case PRISM2_HOSTAPD_GET_INFO_STA: - return prism2_hostapd_get_info_sta(ap, param); - case PRISM2_HOSTAPD_SET_FLAGS_STA: - return prism2_hostapd_set_flags_sta(ap, param); - case PRISM2_HOSTAPD_STA_CLEAR_STATS: - return prism2_hostapd_sta_clear_stats(ap, param); - default: - printk(KERN_WARNING "prism2_hostapd: unknown cmd=%d\n", - param->cmd); - return -EOPNOTSUPP; - } -} - - -/* Update station info for host-based TX rate control and return current - * TX rate */ -static int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev) -{ - int ret = sta->tx_rate; - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - sta->tx_count[sta->tx_rate_idx]++; - sta->tx_since_last_failure++; - sta->tx_consecutive_exc = 0; - if (sta->tx_since_last_failure >= WLAN_RATE_UPDATE_COUNT && - sta->tx_rate_idx < sta->tx_max_rate) { - /* use next higher rate */ - int old_rate, new_rate; - old_rate = new_rate = sta->tx_rate_idx; - while (new_rate < sta->tx_max_rate) { - new_rate++; - if (ap_tx_rate_ok(new_rate, sta, local)) { - sta->tx_rate_idx = new_rate; - break; - } - } - if (old_rate != sta->tx_rate_idx) { - switch (sta->tx_rate_idx) { - case 0: sta->tx_rate = 10; break; - case 1: sta->tx_rate = 20; break; - case 2: sta->tx_rate = 55; break; - case 3: sta->tx_rate = 110; break; - default: sta->tx_rate = 0; break; - } - PDEBUG(DEBUG_AP, "%s: STA %pM TX rate raised to %d\n", - dev->name, sta->addr, sta->tx_rate); - } - sta->tx_since_last_failure = 0; - } - - return ret; -} - - -/* Called only from software IRQ. Called for each TX frame prior possible - * encryption and transmit. */ -ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx) -{ - struct sta_info *sta = NULL; - struct sk_buff *skb = tx->skb; - int set_tim, ret; - struct ieee80211_hdr *hdr; - struct hostap_skb_tx_data *meta; - - meta = (struct hostap_skb_tx_data *) skb->cb; - ret = AP_TX_CONTINUE; - if (local->ap == NULL || skb->len < 10 || - meta->iface->type == HOSTAP_INTERFACE_STA) - goto out; - - hdr = (struct ieee80211_hdr *) skb->data; - - if (hdr->addr1[0] & 0x01) { - /* broadcast/multicast frame - no AP related processing */ - if (local->ap->num_sta <= 0) - ret = AP_TX_DROP; - goto out; - } - - /* unicast packet - check whether destination STA is associated */ - spin_lock(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr1); - if (sta) - atomic_inc(&sta->users); - spin_unlock(&local->ap->sta_table_lock); - - if (local->iw_mode == IW_MODE_MASTER && sta == NULL && - !(meta->flags & HOSTAP_TX_FLAGS_WDS) && - meta->iface->type != HOSTAP_INTERFACE_MASTER && - meta->iface->type != HOSTAP_INTERFACE_AP) { -#if 0 - /* This can happen, e.g., when wlan0 is added to a bridge and - * bridging code does not know which port is the correct target - * for a unicast frame. In this case, the packet is send to all - * ports of the bridge. Since this is a valid scenario, do not - * print out any errors here. */ - if (net_ratelimit()) { - printk(KERN_DEBUG "AP: drop packet to non-associated " - "STA %pM\n", hdr->addr1); - } -#endif - local->ap->tx_drop_nonassoc++; - ret = AP_TX_DROP; - goto out; - } - - if (sta == NULL) - goto out; - - if (!(sta->flags & WLAN_STA_AUTHORIZED)) - ret = AP_TX_CONTINUE_NOT_AUTHORIZED; - - /* Set tx_rate if using host-based TX rate control */ - if (!local->fw_tx_rate_control) - local->ap->last_tx_rate = meta->rate = - ap_update_sta_tx_rate(sta, local->dev); - - if (local->iw_mode != IW_MODE_MASTER) - goto out; - - if (!(sta->flags & WLAN_STA_PS)) - goto out; - - if (meta->flags & HOSTAP_TX_FLAGS_ADD_MOREDATA) { - /* indicate to STA that more frames follow */ - hdr->frame_control |= - cpu_to_le16(IEEE80211_FCTL_MOREDATA); - } - - if (meta->flags & HOSTAP_TX_FLAGS_BUFFERED_FRAME) { - /* packet was already buffered and now send due to - * PS poll, so do not rebuffer it */ - goto out; - } - - if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) { - PDEBUG(DEBUG_PS, "%s: No more space in STA (%pM)'s" - "PS mode buffer\n", - local->dev->name, sta->addr); - /* Make sure that TIM is set for the station (it might not be - * after AP wlan hw reset). */ - /* FIX: should fix hw reset to restore bits based on STA - * buffer state.. */ - hostap_set_tim(local, sta->aid, 1); - sta->flags |= WLAN_STA_TIM; - ret = AP_TX_DROP; - goto out; - } - - /* STA in PS mode, buffer frame for later delivery */ - set_tim = skb_queue_empty(&sta->tx_buf); - skb_queue_tail(&sta->tx_buf, skb); - /* FIX: could save RX time to skb and expire buffered frames after - * some time if STA does not poll for them */ - - if (set_tim) { - if (sta->flags & WLAN_STA_TIM) - PDEBUG(DEBUG_PS2, "Re-setting TIM for aid %d\n", - sta->aid); - hostap_set_tim(local, sta->aid, 1); - sta->flags |= WLAN_STA_TIM; - } - - ret = AP_TX_BUFFERED; - - out: - if (sta != NULL) { - if (ret == AP_TX_CONTINUE || - ret == AP_TX_CONTINUE_NOT_AUTHORIZED) { - sta->tx_packets++; - sta->tx_bytes += skb->len; - sta->last_tx = jiffies; - } - - if ((ret == AP_TX_CONTINUE || - ret == AP_TX_CONTINUE_NOT_AUTHORIZED) && - sta->crypt && tx->host_encrypt) { - tx->crypt = sta->crypt; - tx->sta_ptr = sta; /* hostap_handle_sta_release() will - * be called to release sta info - * later */ - } else - atomic_dec(&sta->users); - } - - return ret; -} - - -void hostap_handle_sta_release(void *ptr) -{ - struct sta_info *sta = ptr; - atomic_dec(&sta->users); -} - - -/* Called only as a tasklet (software IRQ) */ -void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb) -{ - struct sta_info *sta; - struct ieee80211_hdr *hdr; - struct hostap_skb_tx_data *meta; - - hdr = (struct ieee80211_hdr *) skb->data; - meta = (struct hostap_skb_tx_data *) skb->cb; - - spin_lock(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr1); - if (!sta) { - spin_unlock(&local->ap->sta_table_lock); - PDEBUG(DEBUG_AP, "%s: Could not find STA %pM" - " for this TX error (@%lu)\n", - local->dev->name, hdr->addr1, jiffies); - return; - } - - sta->tx_since_last_failure = 0; - sta->tx_consecutive_exc++; - - if (sta->tx_consecutive_exc >= WLAN_RATE_DECREASE_THRESHOLD && - sta->tx_rate_idx > 0 && meta->rate <= sta->tx_rate) { - /* use next lower rate */ - int old, rate; - old = rate = sta->tx_rate_idx; - while (rate > 0) { - rate--; - if (ap_tx_rate_ok(rate, sta, local)) { - sta->tx_rate_idx = rate; - break; - } - } - if (old != sta->tx_rate_idx) { - switch (sta->tx_rate_idx) { - case 0: sta->tx_rate = 10; break; - case 1: sta->tx_rate = 20; break; - case 2: sta->tx_rate = 55; break; - case 3: sta->tx_rate = 110; break; - default: sta->tx_rate = 0; break; - } - PDEBUG(DEBUG_AP, - "%s: STA %pM TX rate lowered to %d\n", - local->dev->name, sta->addr, sta->tx_rate); - } - sta->tx_consecutive_exc = 0; - } - spin_unlock(&local->ap->sta_table_lock); -} - - -static void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta, - int pwrmgt, int type, int stype) -{ - if (pwrmgt && !(sta->flags & WLAN_STA_PS)) { - sta->flags |= WLAN_STA_PS; - PDEBUG(DEBUG_PS2, "STA %pM changed to use PS " - "mode (type=0x%02X, stype=0x%02X)\n", - sta->addr, type >> 2, stype >> 4); - } else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) { - sta->flags &= ~WLAN_STA_PS; - PDEBUG(DEBUG_PS2, "STA %pM changed to not use " - "PS mode (type=0x%02X, stype=0x%02X)\n", - sta->addr, type >> 2, stype >> 4); - if (type != IEEE80211_FTYPE_CTL || - stype != IEEE80211_STYPE_PSPOLL) - schedule_packet_send(local, sta); - } -} - - -/* Called only as a tasklet (software IRQ). Called for each RX frame to update - * STA power saving state. pwrmgt is a flag from 802.11 frame_control field. */ -int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr) -{ - struct sta_info *sta; - u16 fc; - - spin_lock(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr2); - if (sta) - atomic_inc(&sta->users); - spin_unlock(&local->ap->sta_table_lock); - - if (!sta) - return -1; - - fc = le16_to_cpu(hdr->frame_control); - hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM, - fc & IEEE80211_FCTL_FTYPE, - fc & IEEE80211_FCTL_STYPE); - - atomic_dec(&sta->users); - return 0; -} - - -/* Called only as a tasklet (software IRQ). Called for each RX frame after - * getting RX header and payload from hardware. */ -ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, - struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats, - int wds) -{ - int ret; - struct sta_info *sta; - u16 fc, type, stype; - struct ieee80211_hdr *hdr; - - if (local->ap == NULL) - return AP_RX_CONTINUE; - - hdr = (struct ieee80211_hdr *) skb->data; - - fc = le16_to_cpu(hdr->frame_control); - type = fc & IEEE80211_FCTL_FTYPE; - stype = fc & IEEE80211_FCTL_STYPE; - - spin_lock(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr2); - if (sta) - atomic_inc(&sta->users); - spin_unlock(&local->ap->sta_table_lock); - - if (sta && !(sta->flags & WLAN_STA_AUTHORIZED)) - ret = AP_RX_CONTINUE_NOT_AUTHORIZED; - else - ret = AP_RX_CONTINUE; - - - if (fc & IEEE80211_FCTL_TODS) { - if (!wds && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { - if (local->hostapd) { - prism2_rx_80211(local->apdev, skb, rx_stats, - PRISM2_RX_NON_ASSOC); -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - } else { - printk(KERN_DEBUG "%s: dropped received packet" - " from non-associated STA %pM" - " (type=0x%02x, subtype=0x%02x)\n", - dev->name, hdr->addr2, - type >> 2, stype >> 4); - hostap_rx(dev, skb, rx_stats); -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - } - ret = AP_RX_EXIT; - goto out; - } - } else if (fc & IEEE80211_FCTL_FROMDS) { - if (!wds) { - /* FromDS frame - not for us; probably - * broadcast/multicast in another BSS - drop */ - if (ether_addr_equal(hdr->addr1, dev->dev_addr)) { - printk(KERN_DEBUG "Odd.. FromDS packet " - "received with own BSSID\n"); - hostap_dump_rx_80211(dev->name, skb, rx_stats); - } - ret = AP_RX_DROP; - goto out; - } - } else if (stype == IEEE80211_STYPE_NULLFUNC && sta == NULL && - ether_addr_equal(hdr->addr1, dev->dev_addr)) { - - if (local->hostapd) { - prism2_rx_80211(local->apdev, skb, rx_stats, - PRISM2_RX_NON_ASSOC); -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - } else { - /* At least Lucent f/w seems to send data::nullfunc - * frames with no ToDS flag when the current AP returns - * after being unavailable for some time. Speed up - * re-association by informing the station about it not - * being associated. */ - printk(KERN_DEBUG "%s: rejected received nullfunc frame" - " without ToDS from not associated STA %pM\n", - dev->name, hdr->addr2); - hostap_rx(dev, skb, rx_stats); -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - } - ret = AP_RX_EXIT; - goto out; - } else if (stype == IEEE80211_STYPE_NULLFUNC) { - /* At least Lucent cards seem to send periodic nullfunc - * frames with ToDS. Let these through to update SQ - * stats and PS state. Nullfunc frames do not contain - * any data and they will be dropped below. */ - } else { - /* If BSSID (Addr3) is foreign, this frame is a normal - * broadcast frame from an IBSS network. Drop it silently. - * If BSSID is own, report the dropping of this frame. */ - if (ether_addr_equal(hdr->addr3, dev->dev_addr)) { - printk(KERN_DEBUG "%s: dropped received packet from %pM" - " with no ToDS flag " - "(type=0x%02x, subtype=0x%02x)\n", dev->name, - hdr->addr2, type >> 2, stype >> 4); - hostap_dump_rx_80211(dev->name, skb, rx_stats); - } - ret = AP_RX_DROP; - goto out; - } - - if (sta) { - hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM, - type, stype); - - sta->rx_packets++; - sta->rx_bytes += skb->len; - sta->last_rx = jiffies; - } - - if (local->ap->nullfunc_ack && stype == IEEE80211_STYPE_NULLFUNC && - fc & IEEE80211_FCTL_TODS) { - if (local->hostapd) { - prism2_rx_80211(local->apdev, skb, rx_stats, - PRISM2_RX_NULLFUNC_ACK); -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - } else { - /* some STA f/w's seem to require control::ACK frame - * for data::nullfunc, but Prism2 f/w 0.8.0 (at least - * from Compaq) does not send this.. Try to generate - * ACK for these frames from the host driver to make - * power saving work with, e.g., Lucent WaveLAN f/w */ - hostap_rx(dev, skb, rx_stats); -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - } - ret = AP_RX_EXIT; - goto out; - } - - out: - if (sta) - atomic_dec(&sta->users); - - return ret; -} - - -/* Called only as a tasklet (software IRQ) */ -int hostap_handle_sta_crypto(local_info_t *local, - struct ieee80211_hdr *hdr, - struct lib80211_crypt_data **crypt, - void **sta_ptr) -{ - struct sta_info *sta; - - spin_lock(&local->ap->sta_table_lock); - sta = ap_get_sta(local->ap, hdr->addr2); - if (sta) - atomic_inc(&sta->users); - spin_unlock(&local->ap->sta_table_lock); - - if (!sta) - return -1; - - if (sta->crypt) { - *crypt = sta->crypt; - *sta_ptr = sta; - /* hostap_handle_sta_release() will be called to release STA - * info */ - } else - atomic_dec(&sta->users); - - return 0; -} - - -/* Called only as a tasklet (software IRQ) */ -int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr) -{ - struct sta_info *sta; - int ret = 0; - - spin_lock(&ap->sta_table_lock); - sta = ap_get_sta(ap, sta_addr); - if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap) - ret = 1; - spin_unlock(&ap->sta_table_lock); - - return ret; -} - - -/* Called only as a tasklet (software IRQ) */ -int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr) -{ - struct sta_info *sta; - int ret = 0; - - spin_lock(&ap->sta_table_lock); - sta = ap_get_sta(ap, sta_addr); - if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap && - ((sta->flags & WLAN_STA_AUTHORIZED) || - ap->local->ieee_802_1x == 0)) - ret = 1; - spin_unlock(&ap->sta_table_lock); - - return ret; -} - - -/* Called only as a tasklet (software IRQ) */ -int hostap_add_sta(struct ap_data *ap, u8 *sta_addr) -{ - struct sta_info *sta; - int ret = 1; - - if (!ap) - return -1; - - spin_lock(&ap->sta_table_lock); - sta = ap_get_sta(ap, sta_addr); - if (sta) - ret = 0; - spin_unlock(&ap->sta_table_lock); - - if (ret == 1) { - sta = ap_add_sta(ap, sta_addr); - if (!sta) - return -1; - sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; - sta->ap = 1; - memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); - /* No way of knowing which rates are supported since we did not - * get supported rates element from beacon/assoc req. Assume - * that remote end supports all 802.11b rates. */ - sta->supported_rates[0] = 0x82; - sta->supported_rates[1] = 0x84; - sta->supported_rates[2] = 0x0b; - sta->supported_rates[3] = 0x16; - sta->tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M | - WLAN_RATE_5M5 | WLAN_RATE_11M; - sta->tx_rate = 110; - sta->tx_max_rate = sta->tx_rate_idx = 3; - } - - return ret; -} - - -/* Called only as a tasklet (software IRQ) */ -int hostap_update_rx_stats(struct ap_data *ap, - struct ieee80211_hdr *hdr, - struct hostap_80211_rx_status *rx_stats) -{ - struct sta_info *sta; - - if (!ap) - return -1; - - spin_lock(&ap->sta_table_lock); - sta = ap_get_sta(ap, hdr->addr2); - if (sta) { - sta->last_rx_silence = rx_stats->noise; - sta->last_rx_signal = rx_stats->signal; - sta->last_rx_rate = rx_stats->rate; - sta->last_rx_updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; - if (rx_stats->rate == 10) - sta->rx_count[0]++; - else if (rx_stats->rate == 20) - sta->rx_count[1]++; - else if (rx_stats->rate == 55) - sta->rx_count[2]++; - else if (rx_stats->rate == 110) - sta->rx_count[3]++; - } - spin_unlock(&ap->sta_table_lock); - - return sta ? 0 : -1; -} - - -void hostap_update_rates(local_info_t *local) -{ - struct sta_info *sta; - struct ap_data *ap = local->ap; - - if (!ap) - return; - - spin_lock_bh(&ap->sta_table_lock); - list_for_each_entry(sta, &ap->sta_list, list) { - prism2_check_tx_rates(sta); - } - spin_unlock_bh(&ap->sta_table_lock); -} - - -void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, - struct lib80211_crypt_data ***crypt) -{ - struct sta_info *sta; - - spin_lock_bh(&ap->sta_table_lock); - sta = ap_get_sta(ap, addr); - if (sta) - atomic_inc(&sta->users); - spin_unlock_bh(&ap->sta_table_lock); - - if (!sta && permanent) - sta = ap_add_sta(ap, addr); - - if (!sta) - return NULL; - - if (permanent) - sta->flags |= WLAN_STA_PERM; - - *crypt = &sta->crypt; - - return sta; -} - - -void hostap_add_wds_links(local_info_t *local) -{ - struct ap_data *ap = local->ap; - struct sta_info *sta; - - spin_lock_bh(&ap->sta_table_lock); - list_for_each_entry(sta, &ap->sta_list, list) { - if (sta->ap) - hostap_wds_link_oper(local, sta->addr, WDS_ADD); - } - spin_unlock_bh(&ap->sta_table_lock); - - schedule_work(&local->ap->wds_oper_queue); -} - - -void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type) -{ - struct wds_oper_data *entry; - - entry = kmalloc(sizeof(*entry), GFP_ATOMIC); - if (!entry) - return; - memcpy(entry->addr, addr, ETH_ALEN); - entry->type = type; - spin_lock_bh(&local->lock); - entry->next = local->ap->wds_oper_entries; - local->ap->wds_oper_entries = entry; - spin_unlock_bh(&local->lock); - - schedule_work(&local->ap->wds_oper_queue); -} - - -EXPORT_SYMBOL(hostap_init_data); -EXPORT_SYMBOL(hostap_init_ap_proc); -EXPORT_SYMBOL(hostap_free_data); -EXPORT_SYMBOL(hostap_check_sta_fw_version); -EXPORT_SYMBOL(hostap_handle_sta_tx_exc); -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ diff --git a/drivers/net/wireless/hostap/hostap_ap.h b/drivers/net/wireless/hostap/hostap_ap.h deleted file mode 100644 index 334e2d0b8e11..000000000000 --- a/drivers/net/wireless/hostap/hostap_ap.h +++ /dev/null @@ -1,263 +0,0 @@ -#ifndef HOSTAP_AP_H -#define HOSTAP_AP_H - -#include "hostap_80211.h" - -/* AP data structures for STAs */ - -/* maximum number of frames to buffer per STA */ -#define STA_MAX_TX_BUFFER 32 - -/* STA flags */ -#define WLAN_STA_AUTH BIT(0) -#define WLAN_STA_ASSOC BIT(1) -#define WLAN_STA_PS BIT(2) -#define WLAN_STA_TIM BIT(3) /* TIM bit is on for PS stations */ -#define WLAN_STA_PERM BIT(4) /* permanent; do not remove entry on expiration */ -#define WLAN_STA_AUTHORIZED BIT(5) /* If 802.1X is used, this flag is - * controlling whether STA is authorized to - * send and receive non-IEEE 802.1X frames - */ -#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ - -#define WLAN_RATE_1M BIT(0) -#define WLAN_RATE_2M BIT(1) -#define WLAN_RATE_5M5 BIT(2) -#define WLAN_RATE_11M BIT(3) -#define WLAN_RATE_COUNT 4 - -/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8, - * but some pre-standard IEEE 802.11g products use longer elements. */ -#define WLAN_SUPP_RATES_MAX 32 - -/* Try to increase TX rate after # successfully sent consecutive packets */ -#define WLAN_RATE_UPDATE_COUNT 50 - -/* Decrease TX rate after # consecutive dropped packets */ -#define WLAN_RATE_DECREASE_THRESHOLD 2 - -struct sta_info { - struct list_head list; - struct sta_info *hnext; /* next entry in hash table list */ - atomic_t users; /* number of users (do not remove if > 0) */ - struct proc_dir_entry *proc; - - u8 addr[6]; - u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ - u32 flags; - u16 capability; - u16 listen_interval; /* or beacon_int for APs */ - u8 supported_rates[WLAN_SUPP_RATES_MAX]; - - unsigned long last_auth; - unsigned long last_assoc; - unsigned long last_rx; - unsigned long last_tx; - unsigned long rx_packets, tx_packets; - unsigned long rx_bytes, tx_bytes; - struct sk_buff_head tx_buf; - /* FIX: timeout buffers with an expiry time somehow derived from - * listen_interval */ - - s8 last_rx_silence; /* Noise in dBm */ - s8 last_rx_signal; /* Signal strength in dBm */ - u8 last_rx_rate; /* TX rate in 0.1 Mbps */ - u8 last_rx_updated; /* IWSPY's struct iw_quality::updated */ - - u8 tx_supp_rates; /* bit field of supported TX rates */ - u8 tx_rate; /* current TX rate (in 0.1 Mbps) */ - u8 tx_rate_idx; /* current TX rate (WLAN_RATE_*) */ - u8 tx_max_rate; /* max TX rate (WLAN_RATE_*) */ - u32 tx_count[WLAN_RATE_COUNT]; /* number of frames sent (per rate) */ - u32 rx_count[WLAN_RATE_COUNT]; /* number of frames received (per rate) - */ - u32 tx_since_last_failure; - u32 tx_consecutive_exc; - - struct lib80211_crypt_data *crypt; - - int ap; /* whether this station is an AP */ - - local_info_t *local; - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - union { - struct { - char *challenge; /* shared key authentication - * challenge */ - } sta; - struct { - int ssid_len; - unsigned char ssid[MAX_SSID_LEN + 1]; /* AP's ssid */ - int channel; - unsigned long last_beacon; /* last RX beacon time */ - } ap; - } u; - - struct timer_list timer; - enum { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH } timeout_next; -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ -}; - - -#define MAX_STA_COUNT 1024 - -/* Maximum number of AIDs to use for STAs; must be 2007 or lower - * (8802.11 limitation) */ -#define MAX_AID_TABLE_SIZE 128 - -#define STA_HASH_SIZE 256 -#define STA_HASH(sta) (sta[5]) - - -/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY_SEC - * has passed since last received frame from the station, a nullfunc data - * frame is sent to the station. If this frame is not acknowledged and no other - * frames have been received, the station will be disassociated after - * AP_DISASSOC_DELAY. Similarly, a the station will be deauthenticated after - * AP_DEAUTH_DELAY. AP_TIMEOUT_RESOLUTION is the resolution that is used with - * max inactivity timer. */ -#define AP_MAX_INACTIVITY_SEC (5 * 60) -#define AP_DISASSOC_DELAY (HZ) -#define AP_DEAUTH_DELAY (HZ) - -/* ap_policy: whether to accept frames to/from other APs/IBSS */ -typedef enum { - AP_OTHER_AP_SKIP_ALL = 0, - AP_OTHER_AP_SAME_SSID = 1, - AP_OTHER_AP_ALL = 2, - AP_OTHER_AP_EVEN_IBSS = 3 -} ap_policy_enum; - -#define PRISM2_AUTH_OPEN BIT(0) -#define PRISM2_AUTH_SHARED_KEY BIT(1) - - -/* MAC address-based restrictions */ -struct mac_entry { - struct list_head list; - u8 addr[6]; -}; - -struct mac_restrictions { - enum { MAC_POLICY_OPEN = 0, MAC_POLICY_ALLOW, MAC_POLICY_DENY } policy; - unsigned int entries; - struct list_head mac_list; - spinlock_t lock; -}; - - -struct add_sta_proc_data { - u8 addr[ETH_ALEN]; - struct add_sta_proc_data *next; -}; - - -typedef enum { WDS_ADD, WDS_DEL } wds_oper_type; -struct wds_oper_data { - wds_oper_type type; - u8 addr[ETH_ALEN]; - struct wds_oper_data *next; -}; - - -struct ap_data { - int initialized; /* whether ap_data has been initialized */ - local_info_t *local; - int bridge_packets; /* send packet to associated STAs directly to the - * wireless media instead of higher layers in the - * kernel */ - unsigned int bridged_unicast; /* number of unicast frames bridged on - * wireless media */ - unsigned int bridged_multicast; /* number of non-unicast frames - * bridged on wireless media */ - unsigned int tx_drop_nonassoc; /* number of unicast TX packets dropped - * because they were to an address that - * was not associated */ - int nullfunc_ack; /* use workaround for nullfunc frame ACKs */ - - spinlock_t sta_table_lock; - int num_sta; /* number of entries in sta_list */ - struct list_head sta_list; /* STA info list head */ - struct sta_info *sta_hash[STA_HASH_SIZE]; - - struct proc_dir_entry *proc; - - ap_policy_enum ap_policy; - unsigned int max_inactivity; - int autom_ap_wds; - - struct mac_restrictions mac_restrictions; /* MAC-based auth */ - int last_tx_rate; - - struct work_struct add_sta_proc_queue; - struct add_sta_proc_data *add_sta_proc_entries; - - struct work_struct wds_oper_queue; - struct wds_oper_data *wds_oper_entries; - - u16 tx_callback_idx; - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - /* pointers to STA info; based on allocated AID or NULL if AID free - * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1 - * and so on - */ - struct sta_info *sta_aid[MAX_AID_TABLE_SIZE]; - - u16 tx_callback_auth, tx_callback_assoc, tx_callback_poll; - - /* WEP operations for generating challenges to be used with shared key - * authentication */ - struct lib80211_crypto_ops *crypt; - void *crypt_priv; -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ -}; - - -void hostap_rx(struct net_device *dev, struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats); -void hostap_init_data(local_info_t *local); -void hostap_init_ap_proc(local_info_t *local); -void hostap_free_data(struct ap_data *ap); -void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver); - -typedef enum { - AP_TX_CONTINUE, AP_TX_DROP, AP_TX_RETRY, AP_TX_BUFFERED, - AP_TX_CONTINUE_NOT_AUTHORIZED -} ap_tx_ret; -struct hostap_tx_data { - struct sk_buff *skb; - int host_encrypt; - struct lib80211_crypt_data *crypt; - void *sta_ptr; -}; -ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx); -void hostap_handle_sta_release(void *ptr); -void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb); -int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr); -typedef enum { - AP_RX_CONTINUE, AP_RX_DROP, AP_RX_EXIT, AP_RX_CONTINUE_NOT_AUTHORIZED -} ap_rx_ret; -ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, - struct sk_buff *skb, - struct hostap_80211_rx_status *rx_stats, - int wds); -int hostap_handle_sta_crypto(local_info_t *local, struct ieee80211_hdr *hdr, - struct lib80211_crypt_data **crypt, - void **sta_ptr); -int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr); -int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr); -int hostap_add_sta(struct ap_data *ap, u8 *sta_addr); -int hostap_update_rx_stats(struct ap_data *ap, struct ieee80211_hdr *hdr, - struct hostap_80211_rx_status *rx_stats); -void hostap_update_rates(local_info_t *local); -void hostap_add_wds_links(local_info_t *local); -void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type); - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT -void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap, - int resend); -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - -#endif /* HOSTAP_AP_H */ diff --git a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/hostap/hostap_common.h deleted file mode 100644 index 4230102ac9e4..000000000000 --- a/drivers/net/wireless/hostap/hostap_common.h +++ /dev/null @@ -1,419 +0,0 @@ -#ifndef HOSTAP_COMMON_H -#define HOSTAP_COMMON_H - -#include -#include - -/* IEEE 802.11 defines */ - -/* HFA384X Configuration RIDs */ -#define HFA384X_RID_CNFPORTTYPE 0xFC00 -#define HFA384X_RID_CNFOWNMACADDR 0xFC01 -#define HFA384X_RID_CNFDESIREDSSID 0xFC02 -#define HFA384X_RID_CNFOWNCHANNEL 0xFC03 -#define HFA384X_RID_CNFOWNSSID 0xFC04 -#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05 -#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06 -#define HFA384X_RID_CNFMAXDATALEN 0xFC07 -#define HFA384X_RID_CNFWDSADDRESS 0xFC08 -#define HFA384X_RID_CNFPMENABLED 0xFC09 -#define HFA384X_RID_CNFPMEPS 0xFC0A -#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B -#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C -#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D -#define HFA384X_RID_CNFOWNNAME 0xFC0E -#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10 -#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */ -#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */ -#define HFA384X_RID_UNKNOWN1 0xFC20 -#define HFA384X_RID_UNKNOWN2 0xFC21 -#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23 -#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24 -#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25 -#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26 -#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27 -#define HFA384X_RID_CNFWEPFLAGS 0xFC28 -#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 -#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A -#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */ -#define HFA384X_RID_CNFTXCONTROL 0xFC2C -#define HFA384X_RID_CNFROAMINGMODE 0xFC2D -#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */ -#define HFA384X_RID_CNFRCVCRCERROR 0xFC30 -#define HFA384X_RID_CNFMMLIFE 0xFC31 -#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32 -#define HFA384X_RID_CNFBEACONINT 0xFC33 -#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */ -#define HFA384X_RID_CNFSTAPCFINFO 0xFC35 -#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37 -#define HFA384X_RID_CNFTIMCTRL 0xFC40 -#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */ -#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */ -#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */ -#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0; - * write only */ -#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */ -#define HFA384X_RID_GROUPADDRESSES 0xFC80 -#define HFA384X_RID_CREATEIBSS 0xFC81 -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82 -#define HFA384X_RID_RTSTHRESHOLD 0xFC83 -#define HFA384X_RID_TXRATECONTROL 0xFC84 -#define HFA384X_RID_PROMISCUOUSMODE 0xFC85 -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */ -#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0 -#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 -#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 -#define HFA384X_RID_CNFBASICRATES 0xFCB3 -#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4 -#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */ -#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */ -#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */ -#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */ -#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */ -#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */ -#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */ -#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */ -#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */ -#define HFA384X_RID_TICKTIME 0xFCE0 -#define HFA384X_RID_SCANREQUEST 0xFCE1 -#define HFA384X_RID_JOINREQUEST 0xFCE2 -#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */ -#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */ -#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */ - -/* HFA384X Information RIDs */ -#define HFA384X_RID_MAXLOADTIME 0xFD00 -#define HFA384X_RID_DOWNLOADBUFFER 0xFD01 -#define HFA384X_RID_PRIID 0xFD02 -#define HFA384X_RID_PRISUPRANGE 0xFD03 -#define HFA384X_RID_CFIACTRANGES 0xFD04 -#define HFA384X_RID_NICSERNUM 0xFD0A -#define HFA384X_RID_NICID 0xFD0B -#define HFA384X_RID_MFISUPRANGE 0xFD0C -#define HFA384X_RID_CFISUPRANGE 0xFD0D -#define HFA384X_RID_CHANNELLIST 0xFD10 -#define HFA384X_RID_REGULATORYDOMAINS 0xFD11 -#define HFA384X_RID_TEMPTYPE 0xFD12 -#define HFA384X_RID_CIS 0xFD13 -#define HFA384X_RID_STAID 0xFD20 -#define HFA384X_RID_STASUPRANGE 0xFD21 -#define HFA384X_RID_MFIACTRANGES 0xFD22 -#define HFA384X_RID_CFIACTRANGES2 0xFD23 -#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1; - * only Prism2.5(?) */ -#define HFA384X_RID_PORTSTATUS 0xFD40 -#define HFA384X_RID_CURRENTSSID 0xFD41 -#define HFA384X_RID_CURRENTBSSID 0xFD42 -#define HFA384X_RID_COMMSQUALITY 0xFD43 -#define HFA384X_RID_CURRENTTXRATE 0xFD44 -#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45 -#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46 -#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47 -#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48 -#define HFA384X_RID_LONGRETRYLIMIT 0xFD49 -#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A -#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B -#define HFA384X_RID_CFPOLLABLE 0xFD4C -#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D -#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F -#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */ -#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */ -#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */ -#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */ -#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */ -#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */ -#define HFA384X_RID_PHYTYPE 0xFDC0 -#define HFA384X_RID_CURRENTCHANNEL 0xFDC1 -#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2 -#define HFA384X_RID_CCAMODE 0xFDC3 -#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6 -#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */ -#define HFA384X_RID_BUILDSEQ 0xFFFE -#define HFA384X_RID_FWID 0xFFFF - - -struct hfa384x_comp_ident -{ - __le16 id; - __le16 variant; - __le16 major; - __le16 minor; -} __packed; - -#define HFA384X_COMP_ID_PRI 0x15 -#define HFA384X_COMP_ID_STA 0x1f -#define HFA384X_COMP_ID_FW_AP 0x14b - -struct hfa384x_sup_range -{ - __le16 role; - __le16 id; - __le16 variant; - __le16 bottom; - __le16 top; -} __packed; - - -struct hfa384x_build_id -{ - __le16 pri_seq; - __le16 sec_seq; -} __packed; - -/* FD01 - Download Buffer */ -struct hfa384x_rid_download_buffer -{ - __le16 page; - __le16 offset; - __le16 length; -} __packed; - -/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */ -struct hfa384x_comms_quality { - __le16 comm_qual; /* 0 .. 92 */ - __le16 signal_level; /* 27 .. 154 */ - __le16 noise_level; /* 27 .. 154 */ -} __packed; - - -/* netdevice private ioctls (used, e.g., with iwpriv from user space) */ - -/* New wireless extensions API - SET/GET convention (even ioctl numbers are - * root only) - */ -#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) -#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) -#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) -#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) -#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) -#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) -#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) -#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) -#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) -#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) -#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) -#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) -#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) -#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) - -/* following are not in SIOCGIWPRIV list; check permission in the driver code - */ -#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) -#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) - - -/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ -enum { - /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ - PRISM2_PARAM_TXRATECTRL = 2, - PRISM2_PARAM_BEACON_INT = 3, - PRISM2_PARAM_PSEUDO_IBSS = 4, - PRISM2_PARAM_ALC = 5, - /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ - PRISM2_PARAM_DUMP = 7, - PRISM2_PARAM_OTHER_AP_POLICY = 8, - PRISM2_PARAM_AP_MAX_INACTIVITY = 9, - PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, - PRISM2_PARAM_DTIM_PERIOD = 11, - PRISM2_PARAM_AP_NULLFUNC_ACK = 12, - PRISM2_PARAM_MAX_WDS = 13, - PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, - PRISM2_PARAM_AP_AUTH_ALGS = 15, - PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, - PRISM2_PARAM_HOST_ENCRYPT = 17, - PRISM2_PARAM_HOST_DECRYPT = 18, - /* PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, REMOVED 2005-08-14 */ - /* PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, REMOVED 2005-08-14 */ - PRISM2_PARAM_HOST_ROAMING = 21, - PRISM2_PARAM_BCRX_STA_KEY = 22, - PRISM2_PARAM_IEEE_802_1X = 23, - PRISM2_PARAM_ANTSEL_TX = 24, - PRISM2_PARAM_ANTSEL_RX = 25, - PRISM2_PARAM_MONITOR_TYPE = 26, - PRISM2_PARAM_WDS_TYPE = 27, - PRISM2_PARAM_HOSTSCAN = 28, - PRISM2_PARAM_AP_SCAN = 29, - PRISM2_PARAM_ENH_SEC = 30, - PRISM2_PARAM_IO_DEBUG = 31, - PRISM2_PARAM_BASIC_RATES = 32, - PRISM2_PARAM_OPER_RATES = 33, - PRISM2_PARAM_HOSTAPD = 34, - PRISM2_PARAM_HOSTAPD_STA = 35, - PRISM2_PARAM_WPA = 36, - PRISM2_PARAM_PRIVACY_INVOKED = 37, - PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, - PRISM2_PARAM_DROP_UNENCRYPTED = 39, - PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, -}; - -enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, - HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; - - -/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ -enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, - AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, - AP_MAC_CMD_KICKALL = 4 }; - - -/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ -enum { - PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, - /* Note! Old versions of prism2_srec have a fatal error in CRC-16 - * calculation, which will corrupt all non-volatile downloads. - * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to - * prevent use of old versions of prism2_srec for non-volatile - * download. */ - PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, - PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, - /* Persistent versions of volatile download commands (keep firmware - * data in memory and automatically re-download after hw_reset */ - PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, - PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, -}; - -struct prism2_download_param { - u32 dl_cmd; - u32 start_addr; - u32 num_areas; - struct prism2_download_area { - u32 addr; /* wlan card address */ - u32 len; - void __user *ptr; /* pointer to data in user space */ - } data[0]; -}; - -#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 -#define PRISM2_MAX_DOWNLOAD_LEN 262144 - - -/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ -enum { - PRISM2_HOSTAPD_FLUSH = 1, - PRISM2_HOSTAPD_ADD_STA = 2, - PRISM2_HOSTAPD_REMOVE_STA = 3, - PRISM2_HOSTAPD_GET_INFO_STA = 4, - /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ - PRISM2_SET_ENCRYPTION = 6, - PRISM2_GET_ENCRYPTION = 7, - PRISM2_HOSTAPD_SET_FLAGS_STA = 8, - PRISM2_HOSTAPD_GET_RID = 9, - PRISM2_HOSTAPD_SET_RID = 10, - PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, - PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, - PRISM2_HOSTAPD_MLME = 13, - PRISM2_HOSTAPD_SCAN_REQ = 14, - PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, -}; - -#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 -#define PRISM2_HOSTAPD_RID_HDR_LEN \ -offsetof(struct prism2_hostapd_param, u.rid.data) -#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ -offsetof(struct prism2_hostapd_param, u.generic_elem.data) - -/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() - */ -#define HOSTAP_CRYPT_ALG_NAME_LEN 16 - - -struct prism2_hostapd_param { - u32 cmd; - u8 sta_addr[ETH_ALEN]; - union { - struct { - u16 aid; - u16 capability; - u8 tx_supp_rates; - } add_sta; - struct { - u32 inactive_sec; - } get_info_sta; - struct { - u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; - u32 flags; - u32 err; - u8 idx; - u8 seq[8]; /* sequence counter (set: RX, get: TX) */ - u16 key_len; - u8 key[0]; - } crypt; - struct { - u32 flags_and; - u32 flags_or; - } set_flags_sta; - struct { - u16 rid; - u16 len; - u8 data[0]; - } rid; - struct { - u8 len; - u8 data[0]; - } generic_elem; - struct { -#define MLME_STA_DEAUTH 0 -#define MLME_STA_DISASSOC 1 - u16 cmd; - u16 reason_code; - } mlme; - struct { - u8 ssid_len; - u8 ssid[32]; - } scan_req; - } u; -}; - -#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) -#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) - -#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 -#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 -#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 -#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 -#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 -#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 - - -#endif /* HOSTAP_COMMON_H */ diff --git a/drivers/net/wireless/hostap/hostap_config.h b/drivers/net/wireless/hostap/hostap_config.h deleted file mode 100644 index 2c8f71f0ed45..000000000000 --- a/drivers/net/wireless/hostap/hostap_config.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef HOSTAP_CONFIG_H -#define HOSTAP_CONFIG_H - -/* In the previous versions of Host AP driver, support for user space version - * of IEEE 802.11 management (hostapd) used to be disabled in the default - * configuration. From now on, support for hostapd is always included and it is - * possible to disable kernel driver version of IEEE 802.11 management with a - * separate define, PRISM2_NO_KERNEL_IEEE80211_MGMT. */ -/* #define PRISM2_NO_KERNEL_IEEE80211_MGMT */ - -/* Maximum number of events handler per one interrupt */ -#define PRISM2_MAX_INTERRUPT_EVENTS 20 - -/* Include code for downloading firmware images into volatile RAM. */ -#define PRISM2_DOWNLOAD_SUPPORT - -/* Allow kernel configuration to enable download support. */ -#if !defined(PRISM2_DOWNLOAD_SUPPORT) && defined(CONFIG_HOSTAP_FIRMWARE) -#define PRISM2_DOWNLOAD_SUPPORT -#endif - -/* Allow kernel configuration to enable non-volatile download support. */ -#ifdef CONFIG_HOSTAP_FIRMWARE_NVRAM -#define PRISM2_NON_VOLATILE_DOWNLOAD -#endif - -/* Save low-level I/O for debugging. This should not be enabled in normal use. - */ -/* #define PRISM2_IO_DEBUG */ - -/* Following defines can be used to remove unneeded parts of the driver, e.g., - * to limit the size of the kernel module. Definitions can be added here in - * hostap_config.h or they can be added to make command with ccflags-y, - * e.g., - * 'make pccard ccflags-y="-DPRISM2_NO_DEBUG -DPRISM2_NO_PROCFS_DEBUG"' - */ - -/* Do not include debug messages into the driver */ -/* #define PRISM2_NO_DEBUG */ - -/* Do not include /proc/net/prism2/wlan#/{registers,debug} */ -/* #define PRISM2_NO_PROCFS_DEBUG */ - -/* Do not include station functionality (i.e., allow only Master (Host AP) mode - */ -/* #define PRISM2_NO_STATION_MODES */ - -#endif /* HOSTAP_CONFIG_H */ diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c deleted file mode 100644 index 50033aa7c7d5..000000000000 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ /dev/null @@ -1,708 +0,0 @@ -#define PRISM2_PCCARD - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "hostap_wlan.h" - - -static char *dev_info = "hostap_cs"; - -MODULE_AUTHOR("Jouni Malinen"); -MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN " - "cards (PC Card)."); -MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PC Card)"); -MODULE_LICENSE("GPL"); - - -static int ignore_cis_vcc; -module_param(ignore_cis_vcc, int, 0444); -MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); - - -/* struct local_info::hw_priv */ -struct hostap_cs_priv { - struct pcmcia_device *link; - int sandisk_connectplus; -}; - - -#ifdef PRISM2_IO_DEBUG - -static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - - iface = netdev_priv(dev); - local = iface->local; - spin_lock_irqsave(&local->lock, flags); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); - outb(v, dev->base_addr + a); - spin_unlock_irqrestore(&local->lock, flags); -} - -static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - u8 v; - - iface = netdev_priv(dev); - local = iface->local; - spin_lock_irqsave(&local->lock, flags); - v = inb(dev->base_addr + a); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); - spin_unlock_irqrestore(&local->lock, flags); - return v; -} - -static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - - iface = netdev_priv(dev); - local = iface->local; - spin_lock_irqsave(&local->lock, flags); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); - outw(v, dev->base_addr + a); - spin_unlock_irqrestore(&local->lock, flags); -} - -static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - u16 v; - - iface = netdev_priv(dev); - local = iface->local; - spin_lock_irqsave(&local->lock, flags); - v = inw(dev->base_addr + a); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); - spin_unlock_irqrestore(&local->lock, flags); - return v; -} - -static inline void hfa384x_outsw_debug(struct net_device *dev, int a, - u8 *buf, int wc) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - - iface = netdev_priv(dev); - local = iface->local; - spin_lock_irqsave(&local->lock, flags); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc); - outsw(dev->base_addr + a, buf, wc); - spin_unlock_irqrestore(&local->lock, flags); -} - -static inline void hfa384x_insw_debug(struct net_device *dev, int a, - u8 *buf, int wc) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - - iface = netdev_priv(dev); - local = iface->local; - spin_lock_irqsave(&local->lock, flags); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc); - insw(dev->base_addr + a, buf, wc); - spin_unlock_irqrestore(&local->lock, flags); -} - -#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) -#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) -#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) -#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) -#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc)) -#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc)) - -#else /* PRISM2_IO_DEBUG */ - -#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a)) -#define HFA384X_INB(a) inb(dev->base_addr + (a)) -#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a)) -#define HFA384X_INW(a) inw(dev->base_addr + (a)) -#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc) -#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc) - -#endif /* PRISM2_IO_DEBUG */ - - -static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, - int len) -{ - u16 d_off; - u16 *pos; - - d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; - pos = (u16 *) buf; - - if (len / 2) - HFA384X_INSW(d_off, buf, len / 2); - pos += len / 2; - - if (len & 1) - *((char *) pos) = HFA384X_INB(d_off); - - return 0; -} - - -static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) -{ - u16 d_off; - u16 *pos; - - d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; - pos = (u16 *) buf; - - if (len / 2) - HFA384X_OUTSW(d_off, buf, len / 2); - pos += len / 2; - - if (len & 1) - HFA384X_OUTB(*((char *) pos), d_off); - - return 0; -} - - -/* FIX: This might change at some point.. */ -#include "hostap_hw.c" - - - -static void prism2_detach(struct pcmcia_device *p_dev); -static void prism2_release(u_long arg); -static int prism2_config(struct pcmcia_device *link); - - -static int prism2_pccard_card_present(local_info_t *local) -{ - struct hostap_cs_priv *hw_priv = local->hw_priv; - if (hw_priv != NULL && hw_priv->link != NULL && pcmcia_dev_present(hw_priv->link)) - return 1; - return 0; -} - - -/* - * SanDisk CompactFlash WLAN Flashcard - Product Manual v1.0 - * Document No. 20-10-00058, January 2004 - * http://www.sandisk.com/pdf/industrial/ProdManualCFWLANv1.0.pdf - */ -#define SANDISK_WLAN_ACTIVATION_OFF 0x40 -#define SANDISK_HCR_OFF 0x42 - - -static void sandisk_set_iobase(local_info_t *local) -{ - int res; - struct hostap_cs_priv *hw_priv = local->hw_priv; - - res = pcmcia_write_config_byte(hw_priv->link, 0x10, - hw_priv->link->resource[0]->start & 0x00ff); - if (res != 0) { - printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -" - " res=%d\n", res); - } - udelay(10); - - res = pcmcia_write_config_byte(hw_priv->link, 0x12, - (hw_priv->link->resource[0]->start >> 8) & 0x00ff); - if (res != 0) { - printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -" - " res=%d\n", res); - } -} - - -static void sandisk_write_hcr(local_info_t *local, int hcr) -{ - struct net_device *dev = local->dev; - int i; - - HFA384X_OUTB(0x80, SANDISK_WLAN_ACTIVATION_OFF); - udelay(50); - for (i = 0; i < 10; i++) { - HFA384X_OUTB(hcr, SANDISK_HCR_OFF); - } - udelay(55); - HFA384X_OUTB(0x45, SANDISK_WLAN_ACTIVATION_OFF); -} - - -static int sandisk_enable_wireless(struct net_device *dev) -{ - int res, ret = 0; - struct hostap_interface *iface = netdev_priv(dev); - local_info_t *local = iface->local; - struct hostap_cs_priv *hw_priv = local->hw_priv; - - if (resource_size(hw_priv->link->resource[0]) < 0x42) { - /* Not enough ports to be SanDisk multi-function card */ - ret = -ENODEV; - goto done; - } - - if (hw_priv->link->manf_id != 0xd601 || hw_priv->link->card_id != 0x0101) { - /* No SanDisk manfid found */ - ret = -ENODEV; - goto done; - } - - if (hw_priv->link->socket->functions < 2) { - /* No multi-function links found */ - ret = -ENODEV; - goto done; - } - - printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected" - " - using vendor-specific initialization\n", dev->name); - hw_priv->sandisk_connectplus = 1; - - res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, - COR_SOFT_RESET); - if (res != 0) { - printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", - dev->name, res); - goto done; - } - mdelay(5); - - /* - * Do not enable interrupts here to avoid some bogus events. Interrupts - * will be enabled during the first cor_sreset call. - */ - res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, - (COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | - COR_FUNC_ENA)); - if (res != 0) { - printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", - dev->name, res); - goto done; - } - mdelay(5); - - sandisk_set_iobase(local); - - HFA384X_OUTB(0xc5, SANDISK_WLAN_ACTIVATION_OFF); - udelay(10); - HFA384X_OUTB(0x4b, SANDISK_WLAN_ACTIVATION_OFF); - udelay(10); - -done: - return ret; -} - - -static void prism2_pccard_cor_sreset(local_info_t *local) -{ - int res; - u8 val; - struct hostap_cs_priv *hw_priv = local->hw_priv; - - if (!prism2_pccard_card_present(local)) - return; - - res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &val); - if (res != 0) { - printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n", - res); - return; - } - printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n", - val); - - val |= COR_SOFT_RESET; - res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val); - if (res != 0) { - printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n", - res); - return; - } - - mdelay(hw_priv->sandisk_connectplus ? 5 : 2); - - val &= ~COR_SOFT_RESET; - if (hw_priv->sandisk_connectplus) - val |= COR_IREQ_ENA; - res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val); - if (res != 0) { - printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n", - res); - return; - } - - mdelay(hw_priv->sandisk_connectplus ? 5 : 2); - - if (hw_priv->sandisk_connectplus) - sandisk_set_iobase(local); -} - - -static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) -{ - int res; - u8 old_cor; - struct hostap_cs_priv *hw_priv = local->hw_priv; - - if (!prism2_pccard_card_present(local)) - return; - - if (hw_priv->sandisk_connectplus) { - sandisk_write_hcr(local, hcr); - return; - } - - res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &old_cor); - if (res != 0) { - printk(KERN_DEBUG "%s failed 1 (%d)\n", __func__, res); - return; - } - printk(KERN_DEBUG "%s: original COR %02x\n", __func__, old_cor); - - res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, - old_cor | COR_SOFT_RESET); - if (res != 0) { - printk(KERN_DEBUG "%s failed 2 (%d)\n", __func__, res); - return; - } - - mdelay(10); - - /* Setup Genesis mode */ - res = pcmcia_write_config_byte(hw_priv->link, CISREG_CCSR, hcr); - if (res != 0) { - printk(KERN_DEBUG "%s failed 3 (%d)\n", __func__, res); - return; - } - mdelay(10); - - res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, - old_cor & ~COR_SOFT_RESET); - if (res != 0) { - printk(KERN_DEBUG "%s failed 4 (%d)\n", __func__, res); - return; - } - - mdelay(10); -} - - -static struct prism2_helper_functions prism2_pccard_funcs = -{ - .card_present = prism2_pccard_card_present, - .cor_sreset = prism2_pccard_cor_sreset, - .genesis_reset = prism2_pccard_genesis_reset, - .hw_type = HOSTAP_HW_PCCARD, -}; - - -/* allocate local data and register with CardServices - * initialize dev_link structure, but do not configure the card yet */ -static int hostap_cs_probe(struct pcmcia_device *p_dev) -{ - int ret; - - PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info); - - ret = prism2_config(p_dev); - if (ret) { - PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n"); - } - - return ret; -} - - -static void prism2_detach(struct pcmcia_device *link) -{ - PDEBUG(DEBUG_FLOW, "prism2_detach\n"); - - prism2_release((u_long)link); - - /* release net devices */ - if (link->priv) { - struct hostap_cs_priv *hw_priv; - struct net_device *dev; - struct hostap_interface *iface; - dev = link->priv; - iface = netdev_priv(dev); - hw_priv = iface->local->hw_priv; - prism2_free_local_data(dev); - kfree(hw_priv); - } -} - - -static int prism2_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - if (p_dev->config_index == 0) - return -EINVAL; - - return pcmcia_request_io(p_dev); -} - -static int prism2_config(struct pcmcia_device *link) -{ - struct net_device *dev; - struct hostap_interface *iface; - local_info_t *local; - int ret = 1; - struct hostap_cs_priv *hw_priv; - unsigned long flags; - - PDEBUG(DEBUG_FLOW, "prism2_config()\n"); - - hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL); - if (hw_priv == NULL) { - ret = -ENOMEM; - goto failed; - } - - /* Look for an appropriate configuration table entry in the CIS */ - link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO | - CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; - if (ignore_cis_vcc) - link->config_flags &= ~CONF_AUTO_CHECK_VCC; - ret = pcmcia_loop_config(link, prism2_config_check, NULL); - if (ret) { - if (!ignore_cis_vcc) - printk(KERN_ERR "GetNextTuple(): No matching " - "CIS configuration. Maybe you need the " - "ignore_cis_vcc=1 parameter.\n"); - goto failed; - } - - /* Need to allocate net_device before requesting IRQ handler */ - dev = prism2_init_local_data(&prism2_pccard_funcs, 0, - &link->dev); - if (dev == NULL) - goto failed; - link->priv = dev; - - iface = netdev_priv(dev); - local = iface->local; - local->hw_priv = hw_priv; - hw_priv->link = link; - - /* - * We enable IRQ here, but IRQ handler will not proceed - * until dev->base_addr is set below. This protect us from - * receive interrupts when driver is not initialized. - */ - ret = pcmcia_request_irq(link, prism2_interrupt); - if (ret) - goto failed; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - spin_lock_irqsave(&local->irq_init_lock, flags); - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - spin_unlock_irqrestore(&local->irq_init_lock, flags); - - local->shutdown = 0; - - sandisk_enable_wireless(dev); - - ret = prism2_hw_config(dev, 1); - if (!ret) - ret = hostap_hw_ready(dev); - - return ret; - - failed: - kfree(hw_priv); - prism2_release((u_long)link); - return ret; -} - - -static void prism2_release(u_long arg) -{ - struct pcmcia_device *link = (struct pcmcia_device *)arg; - - PDEBUG(DEBUG_FLOW, "prism2_release\n"); - - if (link->priv) { - struct net_device *dev = link->priv; - struct hostap_interface *iface; - - iface = netdev_priv(dev); - prism2_hw_shutdown(dev, 0); - iface->local->shutdown = 1; - } - - pcmcia_disable_device(link); - PDEBUG(DEBUG_FLOW, "release - done\n"); -} - -static int hostap_cs_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = (struct net_device *) link->priv; - int dev_open = 0; - struct hostap_interface *iface = NULL; - - if (!dev) - return -ENODEV; - - iface = netdev_priv(dev); - - PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info); - if (iface && iface->local) - dev_open = iface->local->num_dev_open > 0; - if (dev_open) { - netif_stop_queue(dev); - netif_device_detach(dev); - } - prism2_suspend(dev); - - return 0; -} - -static int hostap_cs_resume(struct pcmcia_device *link) -{ - struct net_device *dev = (struct net_device *) link->priv; - int dev_open = 0; - struct hostap_interface *iface = NULL; - - if (!dev) - return -ENODEV; - - iface = netdev_priv(dev); - - PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info); - - if (iface && iface->local) - dev_open = iface->local->num_dev_open > 0; - - prism2_hw_shutdown(dev, 1); - prism2_hw_config(dev, dev_open ? 0 : 1); - if (dev_open) { - netif_device_attach(dev); - netif_start_queue(dev); - } - - return 0; -} - -static const struct pcmcia_device_id hostap_cs_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), - PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), - PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), - PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x3301), - PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), - PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030b), - PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), - PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), - PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), - PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002), - PCMCIA_DEVICE_MANF_CARD(0x02d2, 0x0001), - PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x0001), - PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), -/* PCMCIA_DEVICE_MANF_CARD(0xc00f, 0x0000), conflict with pcnet_cs */ - PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), - PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), - PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), - PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010), - PCMCIA_DEVICE_MANF_CARD(0x0126, 0x0002), - PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0xd601, 0x0005, "ADLINK 345 CF", - 0x2d858104), - PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "INTERSIL", - 0x74c5e40d), - PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "Intersil", - 0x4b801a17), - PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.02", - 0x4b74baa0), - PCMCIA_MFC_DEVICE_PROD_ID12(0, "SanDisk", "ConnectPlus", - 0x7a954bd9, 0x74be00c6), - PCMCIA_DEVICE_PROD_ID123( - "Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02", - 0xe6ec52ce, 0x08649af2, 0x4b74baa0), - PCMCIA_DEVICE_PROD_ID123( - "Canon", "Wireless LAN CF Card K30225", "Version 01.00", - 0x96ef6fe2, 0x263fcbab, 0xa57adb8c), - PCMCIA_DEVICE_PROD_ID123( - "D", "Link DWL-650 11Mbps WLAN Card", "Version 01.02", - 0x71b18589, 0xb6f1b0ab, 0x4b74baa0), - PCMCIA_DEVICE_PROD_ID123( - "Instant Wireless ", " Network PC CARD", "Version 01.02", - 0x11d901af, 0x6e9bd926, 0x4b74baa0), - PCMCIA_DEVICE_PROD_ID123( - "SMC", "SMC2632W", "Version 01.02", - 0xc4f8b18b, 0x474a1f2a, 0x4b74baa0), - PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", - 0x2decece3, 0x82067c18), - PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card", - 0x54f7c49c, 0x15a75e5b), - PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", - 0x74c5e40d, 0xdb472a18), - PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card", - 0x0733cc81, 0x0c52f395), - PCMCIA_DEVICE_PROD_ID12( - "ZoomAir 11Mbps High", "Rate wireless Networking", - 0x273fe3db, 0x32a1eaee), - PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", - 0xa37434e9, 0x9762e8f1), - PCMCIA_DEVICE_PROD_ID123( - "Pretec", "CompactWLAN Card 802.11b", "2.5", - 0x1cadd3e5, 0xe697636c, 0x7a5bfcf1), - PCMCIA_DEVICE_PROD_ID123( - "U.S. Robotics", "IEEE 802.11b PC-CARD", "Version 01.02", - 0xc7b8df9d, 0x1700d087, 0x4b74baa0), - PCMCIA_DEVICE_PROD_ID123( - "Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", - "Ver. 1.00", - 0x5cd01705, 0x4271660f, 0x9d08ee12), - PCMCIA_DEVICE_PROD_ID123( - "Wireless LAN" , "11Mbps PC Card", "Version 01.02", - 0x4b8870ff, 0x70e946d1, 0x4b74baa0), - PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092), - PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2), - PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b), - PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39), - PCMCIA_DEVICE_NULL -}; -MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids); - - -static struct pcmcia_driver hostap_driver = { - .name = "hostap_cs", - .probe = hostap_cs_probe, - .remove = prism2_detach, - .owner = THIS_MODULE, - .id_table = hostap_cs_ids, - .suspend = hostap_cs_suspend, - .resume = hostap_cs_resume, -}; -module_pcmcia_driver(hostap_driver); diff --git a/drivers/net/wireless/hostap/hostap_download.c b/drivers/net/wireless/hostap/hostap_download.c deleted file mode 100644 index 705fe668b969..000000000000 --- a/drivers/net/wireless/hostap/hostap_download.c +++ /dev/null @@ -1,812 +0,0 @@ -static int prism2_enable_aux_port(struct net_device *dev, int enable) -{ - u16 val, reg; - int i, tries; - unsigned long flags; - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->no_pri) { - if (enable) { - PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux " - "port is already enabled\n", dev->name); - } - return 0; - } - - spin_lock_irqsave(&local->cmdlock, flags); - - /* wait until busy bit is clear */ - tries = HFA384X_CMD_BUSY_TIMEOUT; - while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { - tries--; - udelay(1); - } - if (tries == 0) { - reg = HFA384X_INW(HFA384X_CMD_OFF); - spin_unlock_irqrestore(&local->cmdlock, flags); - printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n", - dev->name, reg); - return -ETIMEDOUT; - } - - val = HFA384X_INW(HFA384X_CONTROL_OFF); - - if (enable) { - HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF); - HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF); - HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF); - - if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED) - printk("prism2_enable_aux_port: was not disabled!?\n"); - val &= ~HFA384X_AUX_PORT_MASK; - val |= HFA384X_AUX_PORT_ENABLE; - } else { - HFA384X_OUTW(0, HFA384X_PARAM0_OFF); - HFA384X_OUTW(0, HFA384X_PARAM1_OFF); - HFA384X_OUTW(0, HFA384X_PARAM2_OFF); - - if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED) - printk("prism2_enable_aux_port: was not enabled!?\n"); - val &= ~HFA384X_AUX_PORT_MASK; - val |= HFA384X_AUX_PORT_DISABLE; - } - HFA384X_OUTW(val, HFA384X_CONTROL_OFF); - - udelay(5); - - i = 10000; - while (i > 0) { - val = HFA384X_INW(HFA384X_CONTROL_OFF); - val &= HFA384X_AUX_PORT_MASK; - - if ((enable && val == HFA384X_AUX_PORT_ENABLED) || - (!enable && val == HFA384X_AUX_PORT_DISABLED)) - break; - - udelay(10); - i--; - } - - spin_unlock_irqrestore(&local->cmdlock, flags); - - if (i == 0) { - printk("prism2_enable_aux_port(%d) timed out\n", - enable); - return -ETIMEDOUT; - } - - return 0; -} - - -static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len, - void *buf) -{ - u16 page, offset; - if (addr & 1 || len & 1) - return -1; - - page = addr >> 7; - offset = addr & 0x7f; - - HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); - HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); - - udelay(5); - -#ifdef PRISM2_PCI - { - __le16 *pos = (__le16 *) buf; - while (len > 0) { - *pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF); - len -= 2; - } - } -#else /* PRISM2_PCI */ - HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2); -#endif /* PRISM2_PCI */ - - return 0; -} - - -static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len, - void *buf) -{ - u16 page, offset; - if (addr & 1 || len & 1) - return -1; - - page = addr >> 7; - offset = addr & 0x7f; - - HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); - HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); - - udelay(5); - -#ifdef PRISM2_PCI - { - __le16 *pos = (__le16 *) buf; - while (len > 0) { - HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF); - len -= 2; - } - } -#else /* PRISM2_PCI */ - HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2); -#endif /* PRISM2_PCI */ - - return 0; -} - - -static int prism2_pda_ok(u8 *buf) -{ - __le16 *pda = (__le16 *) buf; - int pos; - u16 len, pdr; - - if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff && - buf[3] == 0x00) - return 0; - - pos = 0; - while (pos + 1 < PRISM2_PDA_SIZE / 2) { - len = le16_to_cpu(pda[pos]); - pdr = le16_to_cpu(pda[pos + 1]); - if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2) - return 0; - - if (pdr == 0x0000 && len == 2) { - /* PDA end found */ - return 1; - } - - pos += len + 1; - } - - return 0; -} - - -#define prism2_download_aux_dump_npages 65536 - -struct prism2_download_aux_dump { - local_info_t *local; - u16 page[0x80]; -}; - -static int prism2_download_aux_dump_proc_show(struct seq_file *m, void *v) -{ - struct prism2_download_aux_dump *ctx = m->private; - - hfa384x_from_aux(ctx->local->dev, (unsigned long)v - 1, 0x80, ctx->page); - seq_write(m, ctx->page, 0x80); - return 0; -} - -static void *prism2_download_aux_dump_proc_start(struct seq_file *m, loff_t *_pos) -{ - struct prism2_download_aux_dump *ctx = m->private; - prism2_enable_aux_port(ctx->local->dev, 1); - if (*_pos >= prism2_download_aux_dump_npages) - return NULL; - return (void *)((unsigned long)*_pos + 1); -} - -static void *prism2_download_aux_dump_proc_next(struct seq_file *m, void *v, loff_t *_pos) -{ - ++*_pos; - if (*_pos >= prism2_download_aux_dump_npages) - return NULL; - return (void *)((unsigned long)*_pos + 1); -} - -static void prism2_download_aux_dump_proc_stop(struct seq_file *m, void *v) -{ - struct prism2_download_aux_dump *ctx = m->private; - prism2_enable_aux_port(ctx->local->dev, 0); -} - -static const struct seq_operations prism2_download_aux_dump_proc_seqops = { - .start = prism2_download_aux_dump_proc_start, - .next = prism2_download_aux_dump_proc_next, - .stop = prism2_download_aux_dump_proc_stop, - .show = prism2_download_aux_dump_proc_show, -}; - -static int prism2_download_aux_dump_proc_open(struct inode *inode, struct file *file) -{ - int ret = seq_open_private(file, &prism2_download_aux_dump_proc_seqops, - sizeof(struct prism2_download_aux_dump)); - if (ret == 0) { - struct seq_file *m = file->private_data; - m->private = PDE_DATA(inode); - } - return ret; -} - -static const struct file_operations prism2_download_aux_dump_proc_fops = { - .open = prism2_download_aux_dump_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; - - -static u8 * prism2_read_pda(struct net_device *dev) -{ - u8 *buf; - int res, i, found = 0; -#define NUM_PDA_ADDRS 4 - unsigned int pda_addr[NUM_PDA_ADDRS] = { - 0x7f0000 /* others than HFA3841 */, - 0x3f0000 /* HFA3841 */, - 0x390000 /* apparently used in older cards */, - 0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */, - }; - - buf = kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL); - if (buf == NULL) - return NULL; - - /* Note: wlan card should be in initial state (just after init cmd) - * and no other operations should be performed concurrently. */ - - prism2_enable_aux_port(dev, 1); - - for (i = 0; i < NUM_PDA_ADDRS; i++) { - PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x", - dev->name, pda_addr[i]); - res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf); - if (res) - continue; - if (res == 0 && prism2_pda_ok(buf)) { - PDEBUG2(DEBUG_EXTRA2, ": OK\n"); - found = 1; - break; - } else { - PDEBUG2(DEBUG_EXTRA2, ": failed\n"); - } - } - - prism2_enable_aux_port(dev, 0); - - if (!found) { - printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name); - kfree(buf); - buf = NULL; - } - - return buf; -} - - -static int prism2_download_volatile(local_info_t *local, - struct prism2_download_data *param) -{ - struct net_device *dev = local->dev; - int ret = 0, i; - u16 param0, param1; - - if (local->hw_downloading) { - printk(KERN_WARNING "%s: Already downloading - aborting new " - "request\n", dev->name); - return -1; - } - - local->hw_downloading = 1; - if (local->pri_only) { - hfa384x_disable_interrupts(dev); - } else { - prism2_hw_shutdown(dev, 0); - - if (prism2_hw_init(dev, 0)) { - printk(KERN_WARNING "%s: Could not initialize card for" - " download\n", dev->name); - ret = -1; - goto out; - } - } - - if (prism2_enable_aux_port(dev, 1)) { - printk(KERN_WARNING "%s: Could not enable AUX port\n", - dev->name); - ret = -1; - goto out; - } - - param0 = param->start_addr & 0xffff; - param1 = param->start_addr >> 16; - - HFA384X_OUTW(0, HFA384X_PARAM2_OFF); - HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); - if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | - (HFA384X_PROGMODE_ENABLE_VOLATILE << 8), - param0)) { - printk(KERN_WARNING "%s: Download command execution failed\n", - dev->name); - ret = -1; - goto out; - } - - for (i = 0; i < param->num_areas; i++) { - PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", - dev->name, param->data[i].len, param->data[i].addr); - if (hfa384x_to_aux(dev, param->data[i].addr, - param->data[i].len, param->data[i].data)) { - printk(KERN_WARNING "%s: RAM download at 0x%08x " - "(len=%d) failed\n", dev->name, - param->data[i].addr, param->data[i].len); - ret = -1; - goto out; - } - } - - HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); - HFA384X_OUTW(0, HFA384X_PARAM2_OFF); - if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD | - (HFA384X_PROGMODE_DISABLE << 8), param0)) { - printk(KERN_WARNING "%s: Download command execution failed\n", - dev->name); - ret = -1; - goto out; - } - /* ProgMode disable causes the hardware to restart itself from the - * given starting address. Give hw some time and ACK command just in - * case restart did not happen. */ - mdelay(5); - HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); - - if (prism2_enable_aux_port(dev, 0)) { - printk(KERN_DEBUG "%s: Disabling AUX port failed\n", - dev->name); - /* continue anyway.. restart should have taken care of this */ - } - - mdelay(5); - local->hw_downloading = 0; - if (prism2_hw_config(dev, 2)) { - printk(KERN_WARNING "%s: Card configuration after RAM " - "download failed\n", dev->name); - ret = -1; - goto out; - } - - out: - local->hw_downloading = 0; - return ret; -} - - -static int prism2_enable_genesis(local_info_t *local, int hcr) -{ - struct net_device *dev = local->dev; - u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff }; - u8 readbuf[4]; - - printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n", - dev->name, hcr); - local->func->cor_sreset(local); - hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); - local->func->genesis_reset(local, hcr); - - /* Readback test */ - hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); - hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); - hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); - - if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) { - printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n", - hcr); - return 0; - } else { - printk(KERN_DEBUG "Readback test failed, HCR 0x%02x " - "write %02x %02x %02x %02x read %02x %02x %02x %02x\n", - hcr, initseq[0], initseq[1], initseq[2], initseq[3], - readbuf[0], readbuf[1], readbuf[2], readbuf[3]); - return 1; - } -} - - -static int prism2_get_ram_size(local_info_t *local) -{ - int ret; - - /* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */ - if (prism2_enable_genesis(local, 0x1f) == 0) - ret = 8; - else if (prism2_enable_genesis(local, 0x0f) == 0) - ret = 16; - else - ret = -1; - - /* Disable genesis mode */ - local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17); - - return ret; -} - - -static int prism2_download_genesis(local_info_t *local, - struct prism2_download_data *param) -{ - struct net_device *dev = local->dev; - int ram16 = 0, i; - int ret = 0; - - if (local->hw_downloading) { - printk(KERN_WARNING "%s: Already downloading - aborting new " - "request\n", dev->name); - return -EBUSY; - } - - if (!local->func->genesis_reset || !local->func->cor_sreset) { - printk(KERN_INFO "%s: Genesis mode downloading not supported " - "with this hwmodel\n", dev->name); - return -EOPNOTSUPP; - } - - local->hw_downloading = 1; - - if (prism2_enable_aux_port(dev, 1)) { - printk(KERN_DEBUG "%s: failed to enable AUX port\n", - dev->name); - ret = -EIO; - goto out; - } - - if (local->sram_type == -1) { - /* 0x1F for x8 SRAM or 0x0F for x16 SRAM */ - if (prism2_enable_genesis(local, 0x1f) == 0) { - ram16 = 0; - PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 " - "SRAM\n", dev->name); - } else if (prism2_enable_genesis(local, 0x0f) == 0) { - ram16 = 1; - PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 " - "SRAM\n", dev->name); - } else { - printk(KERN_DEBUG "%s: Could not initiate genesis " - "mode\n", dev->name); - ret = -EIO; - goto out; - } - } else { - if (prism2_enable_genesis(local, local->sram_type == 8 ? - 0x1f : 0x0f)) { - printk(KERN_DEBUG "%s: Failed to set Genesis " - "mode (sram_type=%d)\n", dev->name, - local->sram_type); - ret = -EIO; - goto out; - } - ram16 = local->sram_type != 8; - } - - for (i = 0; i < param->num_areas; i++) { - PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", - dev->name, param->data[i].len, param->data[i].addr); - if (hfa384x_to_aux(dev, param->data[i].addr, - param->data[i].len, param->data[i].data)) { - printk(KERN_WARNING "%s: RAM download at 0x%08x " - "(len=%d) failed\n", dev->name, - param->data[i].addr, param->data[i].len); - ret = -EIO; - goto out; - } - } - - PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n"); - local->func->genesis_reset(local, ram16 ? 0x07 : 0x17); - if (prism2_enable_aux_port(dev, 0)) { - printk(KERN_DEBUG "%s: Failed to disable AUX port\n", - dev->name); - } - - mdelay(5); - local->hw_downloading = 0; - - PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n"); - /* - * Make sure the INIT command does not generate a command completion - * event by disabling interrupts. - */ - hfa384x_disable_interrupts(dev); - if (prism2_hw_init(dev, 1)) { - printk(KERN_DEBUG "%s: Initialization after genesis mode " - "download failed\n", dev->name); - ret = -EIO; - goto out; - } - - PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n"); - if (prism2_hw_init2(dev, 1)) { - printk(KERN_DEBUG "%s: Initialization(2) after genesis mode " - "download failed\n", dev->name); - ret = -EIO; - goto out; - } - - out: - local->hw_downloading = 0; - return ret; -} - - -#ifdef PRISM2_NON_VOLATILE_DOWNLOAD -/* Note! Non-volatile downloading functionality has not yet been tested - * thoroughly and it may corrupt flash image and effectively kill the card that - * is being updated. You have been warned. */ - -static inline int prism2_download_block(struct net_device *dev, - u32 addr, u8 *data, - u32 bufaddr, int rest_len) -{ - u16 param0, param1; - int block_len; - - block_len = rest_len < 4096 ? rest_len : 4096; - - param0 = addr & 0xffff; - param1 = addr >> 16; - - HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF); - HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); - - if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | - (HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8), - param0)) { - printk(KERN_WARNING "%s: Flash download command execution " - "failed\n", dev->name); - return -1; - } - - if (hfa384x_to_aux(dev, bufaddr, block_len, data)) { - printk(KERN_WARNING "%s: flash download at 0x%08x " - "(len=%d) failed\n", dev->name, addr, block_len); - return -1; - } - - HFA384X_OUTW(0, HFA384X_PARAM2_OFF); - HFA384X_OUTW(0, HFA384X_PARAM1_OFF); - if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | - (HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8), - 0)) { - printk(KERN_WARNING "%s: Flash write command execution " - "failed\n", dev->name); - return -1; - } - - return block_len; -} - - -static int prism2_download_nonvolatile(local_info_t *local, - struct prism2_download_data *dl) -{ - struct net_device *dev = local->dev; - int ret = 0, i; - struct { - __le16 page; - __le16 offset; - __le16 len; - } dlbuffer; - u32 bufaddr; - - if (local->hw_downloading) { - printk(KERN_WARNING "%s: Already downloading - aborting new " - "request\n", dev->name); - return -1; - } - - ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER, - &dlbuffer, 6, 0); - - if (ret < 0) { - printk(KERN_WARNING "%s: Could not read download buffer " - "parameters\n", dev->name); - goto out; - } - - printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n", - le16_to_cpu(dlbuffer.len), - le16_to_cpu(dlbuffer.page), - le16_to_cpu(dlbuffer.offset)); - - bufaddr = (le16_to_cpu(dlbuffer.page) << 7) + le16_to_cpu(dlbuffer.offset); - - local->hw_downloading = 1; - - if (!local->pri_only) { - prism2_hw_shutdown(dev, 0); - - if (prism2_hw_init(dev, 0)) { - printk(KERN_WARNING "%s: Could not initialize card for" - " download\n", dev->name); - ret = -1; - goto out; - } - } - - hfa384x_disable_interrupts(dev); - - if (prism2_enable_aux_port(dev, 1)) { - printk(KERN_WARNING "%s: Could not enable AUX port\n", - dev->name); - ret = -1; - goto out; - } - - printk(KERN_DEBUG "%s: starting flash download\n", dev->name); - for (i = 0; i < dl->num_areas; i++) { - int rest_len = dl->data[i].len; - int data_off = 0; - - while (rest_len > 0) { - int block_len; - - block_len = prism2_download_block( - dev, dl->data[i].addr + data_off, - dl->data[i].data + data_off, bufaddr, - rest_len); - - if (block_len < 0) { - ret = -1; - goto out; - } - - rest_len -= block_len; - data_off += block_len; - } - } - - HFA384X_OUTW(0, HFA384X_PARAM1_OFF); - HFA384X_OUTW(0, HFA384X_PARAM2_OFF); - if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | - (HFA384X_PROGMODE_DISABLE << 8), 0)) { - printk(KERN_WARNING "%s: Download command execution failed\n", - dev->name); - ret = -1; - goto out; - } - - if (prism2_enable_aux_port(dev, 0)) { - printk(KERN_DEBUG "%s: Disabling AUX port failed\n", - dev->name); - /* continue anyway.. restart should have taken care of this */ - } - - mdelay(5); - - local->func->hw_reset(dev); - local->hw_downloading = 0; - if (prism2_hw_config(dev, 2)) { - printk(KERN_WARNING "%s: Card configuration after flash " - "download failed\n", dev->name); - ret = -1; - } else { - printk(KERN_INFO "%s: Card initialized successfully after " - "flash download\n", dev->name); - } - - out: - local->hw_downloading = 0; - return ret; -} -#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ - - -static void prism2_download_free_data(struct prism2_download_data *dl) -{ - int i; - - if (dl == NULL) - return; - - for (i = 0; i < dl->num_areas; i++) - kfree(dl->data[i].data); - kfree(dl); -} - - -static int prism2_download(local_info_t *local, - struct prism2_download_param *param) -{ - int ret = 0; - int i; - u32 total_len = 0; - struct prism2_download_data *dl = NULL; - - printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x " - "num_areas=%d\n", - param->dl_cmd, param->start_addr, param->num_areas); - - if (param->num_areas > 100) { - ret = -EINVAL; - goto out; - } - - dl = kzalloc(sizeof(*dl) + param->num_areas * - sizeof(struct prism2_download_data_area), GFP_KERNEL); - if (dl == NULL) { - ret = -ENOMEM; - goto out; - } - dl->dl_cmd = param->dl_cmd; - dl->start_addr = param->start_addr; - dl->num_areas = param->num_areas; - for (i = 0; i < param->num_areas; i++) { - PDEBUG(DEBUG_EXTRA2, - " area %d: addr=0x%08x len=%d ptr=0x%p\n", - i, param->data[i].addr, param->data[i].len, - param->data[i].ptr); - - dl->data[i].addr = param->data[i].addr; - dl->data[i].len = param->data[i].len; - - total_len += param->data[i].len; - if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN || - total_len > PRISM2_MAX_DOWNLOAD_LEN) { - ret = -E2BIG; - goto out; - } - - dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL); - if (dl->data[i].data == NULL) { - ret = -ENOMEM; - goto out; - } - - if (copy_from_user(dl->data[i].data, param->data[i].ptr, - param->data[i].len)) { - ret = -EFAULT; - goto out; - } - } - - switch (param->dl_cmd) { - case PRISM2_DOWNLOAD_VOLATILE: - case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT: - ret = prism2_download_volatile(local, dl); - break; - case PRISM2_DOWNLOAD_VOLATILE_GENESIS: - case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT: - ret = prism2_download_genesis(local, dl); - break; - case PRISM2_DOWNLOAD_NON_VOLATILE: -#ifdef PRISM2_NON_VOLATILE_DOWNLOAD - ret = prism2_download_nonvolatile(local, dl); -#else /* PRISM2_NON_VOLATILE_DOWNLOAD */ - printk(KERN_INFO "%s: non-volatile downloading not enabled\n", - local->dev->name); - ret = -EOPNOTSUPP; -#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ - break; - default: - printk(KERN_DEBUG "%s: unsupported download command %d\n", - local->dev->name, param->dl_cmd); - ret = -EINVAL; - break; - } - - out: - if (ret == 0 && dl && - param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) { - prism2_download_free_data(local->dl_pri); - local->dl_pri = dl; - } else if (ret == 0 && dl && - param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) { - prism2_download_free_data(local->dl_sec); - local->dl_sec = dl; - } else - prism2_download_free_data(dl); - - return ret; -} diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c deleted file mode 100644 index 6df3ee561d52..000000000000 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ /dev/null @@ -1,3424 +0,0 @@ -/* - * Host AP (software wireless LAN access point) driver for - * Intersil Prism2/2.5/3. - * - * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. See README and COPYING for - * more details. - * - * FIX: - * - there is currently no way of associating TX packets to correct wds device - * when TX Exc/OK event occurs, so all tx_packets and some - * tx_errors/tx_dropped are added to the main netdevice; using sw_support - * field in txdesc might be used to fix this (using Alloc event to increment - * tx_packets would need some further info in txfid table) - * - * Buffer Access Path (BAP) usage: - * Prism2 cards have two separate BAPs for accessing the card memory. These - * should allow concurrent access to two different frames and the driver - * previously used BAP0 for sending data and BAP1 for receiving data. - * However, there seems to be number of issues with concurrent access and at - * least one know hardware bug in using BAP0 and BAP1 concurrently with PCI - * Prism2.5. Therefore, the driver now only uses BAP0 for moving data between - * host and card memories. BAP0 accesses are protected with local->baplock - * (spin_lock_bh) to prevent concurrent use. - */ - - - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hostap_80211.h" -#include "hostap.h" -#include "hostap_ap.h" - - -/* #define final_version */ - -static int mtu = 1500; -module_param(mtu, int, 0444); -MODULE_PARM_DESC(mtu, "Maximum transfer unit"); - -static int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS }; -module_param_array(channel, int, NULL, 0444); -MODULE_PARM_DESC(channel, "Initial channel"); - -static char essid[33] = "test"; -module_param_string(essid, essid, sizeof(essid), 0444); -MODULE_PARM_DESC(essid, "Host AP's ESSID"); - -static int iw_mode[MAX_PARM_DEVICES] = { IW_MODE_MASTER, DEF_INTS }; -module_param_array(iw_mode, int, NULL, 0444); -MODULE_PARM_DESC(iw_mode, "Initial operation mode"); - -static int beacon_int[MAX_PARM_DEVICES] = { 100, DEF_INTS }; -module_param_array(beacon_int, int, NULL, 0444); -MODULE_PARM_DESC(beacon_int, "Beacon interval (1 = 1024 usec)"); - -static int dtim_period[MAX_PARM_DEVICES] = { 1, DEF_INTS }; -module_param_array(dtim_period, int, NULL, 0444); -MODULE_PARM_DESC(dtim_period, "DTIM period"); - -static char dev_template[16] = "wlan%d"; -module_param_string(dev_template, dev_template, sizeof(dev_template), 0444); -MODULE_PARM_DESC(dev_template, "Prefix for network device name (default: " - "wlan%d)"); - -#ifdef final_version -#define EXTRA_EVENTS_WTERR 0 -#else -/* check WTERR events (Wait Time-out) in development versions */ -#define EXTRA_EVENTS_WTERR HFA384X_EV_WTERR -#endif - -/* Events that will be using BAP0 */ -#define HFA384X_BAP0_EVENTS \ - (HFA384X_EV_TXEXC | HFA384X_EV_RX | HFA384X_EV_INFO | HFA384X_EV_TX) - -/* event mask, i.e., events that will result in an interrupt */ -#define HFA384X_EVENT_MASK \ - (HFA384X_BAP0_EVENTS | HFA384X_EV_ALLOC | HFA384X_EV_INFDROP | \ - HFA384X_EV_CMD | HFA384X_EV_TICK | \ - EXTRA_EVENTS_WTERR) - -/* Default TX control flags: use 802.11 headers and request interrupt for - * failed transmits. Frames that request ACK callback, will add - * _TX_OK flag and _ALT_RTRY flag may be used to select different retry policy. - */ -#define HFA384X_TX_CTRL_FLAGS \ - (HFA384X_TX_CTRL_802_11 | HFA384X_TX_CTRL_TX_EX) - - -/* ca. 1 usec */ -#define HFA384X_CMD_BUSY_TIMEOUT 5000 -#define HFA384X_BAP_BUSY_TIMEOUT 50000 - -/* ca. 10 usec */ -#define HFA384X_CMD_COMPL_TIMEOUT 20000 -#define HFA384X_DL_COMPL_TIMEOUT 1000000 - -/* Wait times for initialization; yield to other processes to avoid busy - * waiting for long time. */ -#define HFA384X_INIT_TIMEOUT (HZ / 2) /* 500 ms */ -#define HFA384X_ALLOC_COMPL_TIMEOUT (HZ / 20) /* 50 ms */ - - -static void prism2_hw_reset(struct net_device *dev); -static void prism2_check_sta_fw_version(local_info_t *local); - -#ifdef PRISM2_DOWNLOAD_SUPPORT -/* hostap_download.c */ -static const struct file_operations prism2_download_aux_dump_proc_fops; -static u8 * prism2_read_pda(struct net_device *dev); -static int prism2_download(local_info_t *local, - struct prism2_download_param *param); -static void prism2_download_free_data(struct prism2_download_data *dl); -static int prism2_download_volatile(local_info_t *local, - struct prism2_download_data *param); -static int prism2_download_genesis(local_info_t *local, - struct prism2_download_data *param); -static int prism2_get_ram_size(local_info_t *local); -#endif /* PRISM2_DOWNLOAD_SUPPORT */ - - - - -#ifndef final_version -/* magic value written to SWSUPPORT0 reg. for detecting whether card is still - * present */ -#define HFA384X_MAGIC 0x8A32 -#endif - - -static u16 hfa384x_read_reg(struct net_device *dev, u16 reg) -{ - return HFA384X_INW(reg); -} - - -static void hfa384x_read_regs(struct net_device *dev, - struct hfa384x_regs *regs) -{ - regs->cmd = HFA384X_INW(HFA384X_CMD_OFF); - regs->evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); - regs->offset0 = HFA384X_INW(HFA384X_OFFSET0_OFF); - regs->offset1 = HFA384X_INW(HFA384X_OFFSET1_OFF); - regs->swsupport0 = HFA384X_INW(HFA384X_SWSUPPORT0_OFF); -} - - -/** - * __hostap_cmd_queue_free - Free Prism2 command queue entry (private) - * @local: pointer to private Host AP driver data - * @entry: Prism2 command queue entry to be freed - * @del_req: request the entry to be removed - * - * Internal helper function for freeing Prism2 command queue entries. - * Caller must have acquired local->cmdlock before calling this function. - */ -static inline void __hostap_cmd_queue_free(local_info_t *local, - struct hostap_cmd_queue *entry, - int del_req) -{ - if (del_req) { - entry->del_req = 1; - if (!list_empty(&entry->list)) { - list_del_init(&entry->list); - local->cmd_queue_len--; - } - } - - if (atomic_dec_and_test(&entry->usecnt) && entry->del_req) - kfree(entry); -} - - -/** - * hostap_cmd_queue_free - Free Prism2 command queue entry - * @local: pointer to private Host AP driver data - * @entry: Prism2 command queue entry to be freed - * @del_req: request the entry to be removed - * - * Free a Prism2 command queue entry. - */ -static inline void hostap_cmd_queue_free(local_info_t *local, - struct hostap_cmd_queue *entry, - int del_req) -{ - unsigned long flags; - - spin_lock_irqsave(&local->cmdlock, flags); - __hostap_cmd_queue_free(local, entry, del_req); - spin_unlock_irqrestore(&local->cmdlock, flags); -} - - -/** - * prism2_clear_cmd_queue - Free all pending Prism2 command queue entries - * @local: pointer to private Host AP driver data - */ -static void prism2_clear_cmd_queue(local_info_t *local) -{ - struct list_head *ptr, *n; - unsigned long flags; - struct hostap_cmd_queue *entry; - - spin_lock_irqsave(&local->cmdlock, flags); - list_for_each_safe(ptr, n, &local->cmd_queue) { - entry = list_entry(ptr, struct hostap_cmd_queue, list); - atomic_inc(&entry->usecnt); - printk(KERN_DEBUG "%s: removed pending cmd_queue entry " - "(type=%d, cmd=0x%04x, param0=0x%04x)\n", - local->dev->name, entry->type, entry->cmd, - entry->param0); - __hostap_cmd_queue_free(local, entry, 1); - } - if (local->cmd_queue_len) { - /* This should not happen; print debug message and clear - * queue length. */ - printk(KERN_DEBUG "%s: cmd_queue_len (%d) not zero after " - "flush\n", local->dev->name, local->cmd_queue_len); - local->cmd_queue_len = 0; - } - spin_unlock_irqrestore(&local->cmdlock, flags); -} - - -/** - * hfa384x_cmd_issue - Issue a Prism2 command to the hardware - * @dev: pointer to net_device - * @entry: Prism2 command queue entry to be issued - */ -static int hfa384x_cmd_issue(struct net_device *dev, - struct hostap_cmd_queue *entry) -{ - struct hostap_interface *iface; - local_info_t *local; - int tries; - u16 reg; - unsigned long flags; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->func->card_present && !local->func->card_present(local)) - return -ENODEV; - - if (entry->issued) { - printk(KERN_DEBUG "%s: driver bug - re-issuing command @%p\n", - dev->name, entry); - } - - /* wait until busy bit is clear; this should always be clear since the - * commands are serialized */ - tries = HFA384X_CMD_BUSY_TIMEOUT; - while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { - tries--; - udelay(1); - } -#ifndef final_version - if (tries != HFA384X_CMD_BUSY_TIMEOUT) { - prism2_io_debug_error(dev, 1); - printk(KERN_DEBUG "%s: hfa384x_cmd_issue: cmd reg was busy " - "for %d usec\n", dev->name, - HFA384X_CMD_BUSY_TIMEOUT - tries); - } -#endif - if (tries == 0) { - reg = HFA384X_INW(HFA384X_CMD_OFF); - prism2_io_debug_error(dev, 2); - printk(KERN_DEBUG "%s: hfa384x_cmd_issue - timeout - " - "reg=0x%04x\n", dev->name, reg); - return -ETIMEDOUT; - } - - /* write command */ - spin_lock_irqsave(&local->cmdlock, flags); - HFA384X_OUTW(entry->param0, HFA384X_PARAM0_OFF); - HFA384X_OUTW(entry->param1, HFA384X_PARAM1_OFF); - HFA384X_OUTW(entry->cmd, HFA384X_CMD_OFF); - entry->issued = 1; - spin_unlock_irqrestore(&local->cmdlock, flags); - - return 0; -} - - -/** - * hfa384x_cmd - Issue a Prism2 command and wait (sleep) for completion - * @dev: pointer to net_device - * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) - * @param0: value for Param0 register - * @param1: value for Param1 register (pointer; %NULL if not used) - * @resp0: pointer for Resp0 data or %NULL if Resp0 is not needed - * - * Issue given command (possibly after waiting in command queue) and sleep - * until the command is completed (or timed out or interrupted). This can be - * called only from user process context. - */ -static int hfa384x_cmd(struct net_device *dev, u16 cmd, u16 param0, - u16 *param1, u16 *resp0) -{ - struct hostap_interface *iface; - local_info_t *local; - int err, res, issue, issued = 0; - unsigned long flags; - struct hostap_cmd_queue *entry; - DECLARE_WAITQUEUE(wait, current); - - iface = netdev_priv(dev); - local = iface->local; - - if (in_interrupt()) { - printk(KERN_DEBUG "%s: hfa384x_cmd called from interrupt " - "context\n", dev->name); - return -1; - } - - if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN) { - printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", - dev->name); - return -1; - } - - if (signal_pending(current)) - return -EINTR; - - entry = kzalloc(sizeof(*entry), GFP_ATOMIC); - if (entry == NULL) - return -ENOMEM; - - atomic_set(&entry->usecnt, 1); - entry->type = CMD_SLEEP; - entry->cmd = cmd; - entry->param0 = param0; - if (param1) - entry->param1 = *param1; - init_waitqueue_head(&entry->compl); - - /* prepare to wait for command completion event, but do not sleep yet - */ - add_wait_queue(&entry->compl, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irqsave(&local->cmdlock, flags); - issue = list_empty(&local->cmd_queue); - if (issue) - entry->issuing = 1; - list_add_tail(&entry->list, &local->cmd_queue); - local->cmd_queue_len++; - spin_unlock_irqrestore(&local->cmdlock, flags); - - err = 0; - if (!issue) - goto wait_completion; - - if (signal_pending(current)) - err = -EINTR; - - if (!err) { - if (hfa384x_cmd_issue(dev, entry)) - err = -ETIMEDOUT; - else - issued = 1; - } - - wait_completion: - if (!err && entry->type != CMD_COMPLETED) { - /* sleep until command is completed or timed out */ - res = schedule_timeout(2 * HZ); - } else - res = -1; - - if (!err && signal_pending(current)) - err = -EINTR; - - if (err && issued) { - /* the command was issued, so a CmdCompl event should occur - * soon; however, there's a pending signal and - * schedule_timeout() would be interrupted; wait a short period - * of time to avoid removing entry from the list before - * CmdCompl event */ - udelay(300); - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&entry->compl, &wait); - - /* If entry->list is still in the list, it must be removed - * first and in this case prism2_cmd_ev() does not yet have - * local reference to it, and the data can be kfree()'d - * here. If the command completion event is still generated, - * it will be assigned to next (possibly) pending command, but - * the driver will reset the card anyway due to timeout - * - * If the entry is not in the list prism2_cmd_ev() has a local - * reference to it, but keeps cmdlock as long as the data is - * needed, so the data can be kfree()'d here. */ - - /* FIX: if the entry->list is in the list, it has not been completed - * yet, so removing it here is somewhat wrong.. this could cause - * references to freed memory and next list_del() causing NULL pointer - * dereference.. it would probably be better to leave the entry in the - * list and the list should be emptied during hw reset */ - - spin_lock_irqsave(&local->cmdlock, flags); - if (!list_empty(&entry->list)) { - printk(KERN_DEBUG "%s: hfa384x_cmd: entry still in list? " - "(entry=%p, type=%d, res=%d)\n", dev->name, entry, - entry->type, res); - list_del_init(&entry->list); - local->cmd_queue_len--; - } - spin_unlock_irqrestore(&local->cmdlock, flags); - - if (err) { - printk(KERN_DEBUG "%s: hfa384x_cmd: interrupted; err=%d\n", - dev->name, err); - res = err; - goto done; - } - - if (entry->type != CMD_COMPLETED) { - u16 reg = HFA384X_INW(HFA384X_EVSTAT_OFF); - printk(KERN_DEBUG "%s: hfa384x_cmd: command was not " - "completed (res=%d, entry=%p, type=%d, cmd=0x%04x, " - "param0=0x%04x, EVSTAT=%04x INTEN=%04x)\n", dev->name, - res, entry, entry->type, entry->cmd, entry->param0, reg, - HFA384X_INW(HFA384X_INTEN_OFF)); - if (reg & HFA384X_EV_CMD) { - /* Command completion event is pending, but the - * interrupt was not delivered - probably an issue - * with pcmcia-cs configuration. */ - printk(KERN_WARNING "%s: interrupt delivery does not " - "seem to work\n", dev->name); - } - prism2_io_debug_error(dev, 3); - res = -ETIMEDOUT; - goto done; - } - - if (resp0 != NULL) - *resp0 = entry->resp0; -#ifndef final_version - if (entry->res) { - printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x, " - "resp0=0x%04x\n", - dev->name, cmd, entry->res, entry->resp0); - } -#endif /* final_version */ - - res = entry->res; - done: - hostap_cmd_queue_free(local, entry, 1); - return res; -} - - -/** - * hfa384x_cmd_callback - Issue a Prism2 command; callback when completed - * @dev: pointer to net_device - * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) - * @param0: value for Param0 register - * @callback: command completion callback function (%NULL = no callback) - * @context: context data to be given to the callback function - * - * Issue given command (possibly after waiting in command queue) and use - * callback function to indicate command completion. This can be called both - * from user and interrupt context. The callback function will be called in - * hardware IRQ context. It can be %NULL, when no function is called when - * command is completed. - */ -static int hfa384x_cmd_callback(struct net_device *dev, u16 cmd, u16 param0, - void (*callback)(struct net_device *dev, - long context, u16 resp0, - u16 status), - long context) -{ - struct hostap_interface *iface; - local_info_t *local; - int issue, ret; - unsigned long flags; - struct hostap_cmd_queue *entry; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN + 2) { - printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", - dev->name); - return -1; - } - - entry = kzalloc(sizeof(*entry), GFP_ATOMIC); - if (entry == NULL) - return -ENOMEM; - - atomic_set(&entry->usecnt, 1); - entry->type = CMD_CALLBACK; - entry->cmd = cmd; - entry->param0 = param0; - entry->callback = callback; - entry->context = context; - - spin_lock_irqsave(&local->cmdlock, flags); - issue = list_empty(&local->cmd_queue); - if (issue) - entry->issuing = 1; - list_add_tail(&entry->list, &local->cmd_queue); - local->cmd_queue_len++; - spin_unlock_irqrestore(&local->cmdlock, flags); - - if (issue && hfa384x_cmd_issue(dev, entry)) - ret = -ETIMEDOUT; - else - ret = 0; - - hostap_cmd_queue_free(local, entry, ret); - - return ret; -} - - -/** - * __hfa384x_cmd_no_wait - Issue a Prism2 command (private) - * @dev: pointer to net_device - * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) - * @param0: value for Param0 register - * @io_debug_num: I/O debug error number - * - * Shared helper function for hfa384x_cmd_wait() and hfa384x_cmd_no_wait(). - */ -static int __hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, u16 param0, - int io_debug_num) -{ - int tries; - u16 reg; - - /* wait until busy bit is clear; this should always be clear since the - * commands are serialized */ - tries = HFA384X_CMD_BUSY_TIMEOUT; - while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { - tries--; - udelay(1); - } - if (tries == 0) { - reg = HFA384X_INW(HFA384X_CMD_OFF); - prism2_io_debug_error(dev, io_debug_num); - printk(KERN_DEBUG "%s: __hfa384x_cmd_no_wait(%d) - timeout - " - "reg=0x%04x\n", dev->name, io_debug_num, reg); - return -ETIMEDOUT; - } - - /* write command */ - HFA384X_OUTW(param0, HFA384X_PARAM0_OFF); - HFA384X_OUTW(cmd, HFA384X_CMD_OFF); - - return 0; -} - - -/** - * hfa384x_cmd_wait - Issue a Prism2 command and busy wait for completion - * @dev: pointer to net_device - * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) - * @param0: value for Param0 register - */ -static int hfa384x_cmd_wait(struct net_device *dev, u16 cmd, u16 param0) -{ - int res, tries; - u16 reg; - - res = __hfa384x_cmd_no_wait(dev, cmd, param0, 4); - if (res) - return res; - - /* wait for command completion */ - if ((cmd & HFA384X_CMDCODE_MASK) == HFA384X_CMDCODE_DOWNLOAD) - tries = HFA384X_DL_COMPL_TIMEOUT; - else - tries = HFA384X_CMD_COMPL_TIMEOUT; - - while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && - tries > 0) { - tries--; - udelay(10); - } - if (tries == 0) { - reg = HFA384X_INW(HFA384X_EVSTAT_OFF); - prism2_io_debug_error(dev, 5); - printk(KERN_DEBUG "%s: hfa384x_cmd_wait - timeout2 - " - "reg=0x%04x\n", dev->name, reg); - return -ETIMEDOUT; - } - - res = (HFA384X_INW(HFA384X_STATUS_OFF) & - (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | BIT(9) | - BIT(8))) >> 8; -#ifndef final_version - if (res) { - printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x\n", - dev->name, cmd, res); - } -#endif - - HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); - - return res; -} - - -/** - * hfa384x_cmd_no_wait - Issue a Prism2 command; do not wait for completion - * @dev: pointer to net_device - * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) - * @param0: value for Param0 register - */ -static inline int hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, - u16 param0) -{ - return __hfa384x_cmd_no_wait(dev, cmd, param0, 6); -} - - -/** - * prism2_cmd_ev - Prism2 command completion event handler - * @dev: pointer to net_device - * - * Interrupt handler for command completion events. Called by the main - * interrupt handler in hardware IRQ context. Read Resp0 and status registers - * from the hardware and ACK the event. Depending on the issued command type - * either wake up the sleeping process that is waiting for command completion - * or call the callback function. Issue the next command, if one is pending. - */ -static void prism2_cmd_ev(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - struct hostap_cmd_queue *entry = NULL; - - iface = netdev_priv(dev); - local = iface->local; - - spin_lock(&local->cmdlock); - if (!list_empty(&local->cmd_queue)) { - entry = list_entry(local->cmd_queue.next, - struct hostap_cmd_queue, list); - atomic_inc(&entry->usecnt); - list_del_init(&entry->list); - local->cmd_queue_len--; - - if (!entry->issued) { - printk(KERN_DEBUG "%s: Command completion event, but " - "cmd not issued\n", dev->name); - __hostap_cmd_queue_free(local, entry, 1); - entry = NULL; - } - } - spin_unlock(&local->cmdlock); - - if (!entry) { - HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); - printk(KERN_DEBUG "%s: Command completion event, but no " - "pending commands\n", dev->name); - return; - } - - entry->resp0 = HFA384X_INW(HFA384X_RESP0_OFF); - entry->res = (HFA384X_INW(HFA384X_STATUS_OFF) & - (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | - BIT(9) | BIT(8))) >> 8; - HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); - - /* TODO: rest of the CmdEv handling could be moved to tasklet */ - if (entry->type == CMD_SLEEP) { - entry->type = CMD_COMPLETED; - wake_up_interruptible(&entry->compl); - } else if (entry->type == CMD_CALLBACK) { - if (entry->callback) - entry->callback(dev, entry->context, entry->resp0, - entry->res); - } else { - printk(KERN_DEBUG "%s: Invalid command completion type %d\n", - dev->name, entry->type); - } - hostap_cmd_queue_free(local, entry, 1); - - /* issue next command, if pending */ - entry = NULL; - spin_lock(&local->cmdlock); - if (!list_empty(&local->cmd_queue)) { - entry = list_entry(local->cmd_queue.next, - struct hostap_cmd_queue, list); - if (entry->issuing) { - /* hfa384x_cmd() has already started issuing this - * command, so do not start here */ - entry = NULL; - } - if (entry) - atomic_inc(&entry->usecnt); - } - spin_unlock(&local->cmdlock); - - if (entry) { - /* issue next command; if command issuing fails, remove the - * entry from cmd_queue */ - int res = hfa384x_cmd_issue(dev, entry); - spin_lock(&local->cmdlock); - __hostap_cmd_queue_free(local, entry, res); - spin_unlock(&local->cmdlock); - } -} - - -static int hfa384x_wait_offset(struct net_device *dev, u16 o_off) -{ - int tries = HFA384X_BAP_BUSY_TIMEOUT; - int res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY; - - while (res && tries > 0) { - tries--; - udelay(1); - res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY; - } - return res; -} - - -/* Offset must be even */ -static int hfa384x_setup_bap(struct net_device *dev, u16 bap, u16 id, - int offset) -{ - u16 o_off, s_off; - int ret = 0; - - if (offset % 2 || bap > 1) - return -EINVAL; - - if (bap == BAP1) { - o_off = HFA384X_OFFSET1_OFF; - s_off = HFA384X_SELECT1_OFF; - } else { - o_off = HFA384X_OFFSET0_OFF; - s_off = HFA384X_SELECT0_OFF; - } - - if (hfa384x_wait_offset(dev, o_off)) { - prism2_io_debug_error(dev, 7); - printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout before\n", - dev->name); - ret = -ETIMEDOUT; - goto out; - } - - HFA384X_OUTW(id, s_off); - HFA384X_OUTW(offset, o_off); - - if (hfa384x_wait_offset(dev, o_off)) { - prism2_io_debug_error(dev, 8); - printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout after\n", - dev->name); - ret = -ETIMEDOUT; - goto out; - } -#ifndef final_version - if (HFA384X_INW(o_off) & HFA384X_OFFSET_ERR) { - prism2_io_debug_error(dev, 9); - printk(KERN_DEBUG "%s: hfa384x_setup_bap - offset error " - "(%d,0x04%x,%d); reg=0x%04x\n", - dev->name, bap, id, offset, HFA384X_INW(o_off)); - ret = -EINVAL; - } -#endif - - out: - return ret; -} - - -static int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len, - int exact_len) -{ - struct hostap_interface *iface; - local_info_t *local; - int res, rlen = 0; - struct hfa384x_rid_hdr rec; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->no_pri) { - printk(KERN_DEBUG "%s: cannot get RID %04x (len=%d) - no PRI " - "f/w\n", dev->name, rid, len); - return -ENOTTY; /* Well.. not really correct, but return - * something unique enough.. */ - } - - if ((local->func->card_present && !local->func->card_present(local)) || - local->hw_downloading) - return -ENODEV; - - res = mutex_lock_interruptible(&local->rid_bap_mtx); - if (res) - return res; - - res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS, rid, NULL, NULL); - if (res) { - printk(KERN_DEBUG "%s: hfa384x_get_rid: CMDCODE_ACCESS failed " - "(res=%d, rid=%04x, len=%d)\n", - dev->name, res, rid, len); - mutex_unlock(&local->rid_bap_mtx); - return res; - } - - spin_lock_bh(&local->baplock); - - res = hfa384x_setup_bap(dev, BAP0, rid, 0); - if (!res) - res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); - - if (le16_to_cpu(rec.len) == 0) { - /* RID not available */ - res = -ENODATA; - } - - rlen = (le16_to_cpu(rec.len) - 1) * 2; - if (!res && exact_len && rlen != len) { - printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: " - "rid=0x%04x, len=%d (expected %d)\n", - dev->name, rid, rlen, len); - res = -ENODATA; - } - - if (!res) - res = hfa384x_from_bap(dev, BAP0, buf, len); - - spin_unlock_bh(&local->baplock); - mutex_unlock(&local->rid_bap_mtx); - - if (res) { - if (res != -ENODATA) - printk(KERN_DEBUG "%s: hfa384x_get_rid (rid=%04x, " - "len=%d) - failed - res=%d\n", dev->name, rid, - len, res); - if (res == -ETIMEDOUT) - prism2_hw_reset(dev); - return res; - } - - return rlen; -} - - -static int hfa384x_set_rid(struct net_device *dev, u16 rid, void *buf, int len) -{ - struct hostap_interface *iface; - local_info_t *local; - struct hfa384x_rid_hdr rec; - int res; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->no_pri) { - printk(KERN_DEBUG "%s: cannot set RID %04x (len=%d) - no PRI " - "f/w\n", dev->name, rid, len); - return -ENOTTY; /* Well.. not really correct, but return - * something unique enough.. */ - } - - if ((local->func->card_present && !local->func->card_present(local)) || - local->hw_downloading) - return -ENODEV; - - rec.rid = cpu_to_le16(rid); - /* RID len in words and +1 for rec.rid */ - rec.len = cpu_to_le16(len / 2 + len % 2 + 1); - - res = mutex_lock_interruptible(&local->rid_bap_mtx); - if (res) - return res; - - spin_lock_bh(&local->baplock); - res = hfa384x_setup_bap(dev, BAP0, rid, 0); - if (!res) - res = hfa384x_to_bap(dev, BAP0, &rec, sizeof(rec)); - if (!res) - res = hfa384x_to_bap(dev, BAP0, buf, len); - spin_unlock_bh(&local->baplock); - - if (res) { - printk(KERN_DEBUG "%s: hfa384x_set_rid (rid=%04x, len=%d) - " - "failed - res=%d\n", dev->name, rid, len, res); - mutex_unlock(&local->rid_bap_mtx); - return res; - } - - res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS_WRITE, rid, NULL, NULL); - mutex_unlock(&local->rid_bap_mtx); - - if (res) { - printk(KERN_DEBUG "%s: hfa384x_set_rid: CMDCODE_ACCESS_WRITE " - "failed (res=%d, rid=%04x, len=%d)\n", - dev->name, res, rid, len); - - if (res == -ETIMEDOUT) - prism2_hw_reset(dev); - } - - return res; -} - - -static void hfa384x_disable_interrupts(struct net_device *dev) -{ - /* disable interrupts and clear event status */ - HFA384X_OUTW(0, HFA384X_INTEN_OFF); - HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); -} - - -static void hfa384x_enable_interrupts(struct net_device *dev) -{ - /* ack pending events and enable interrupts from selected events */ - HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); - HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); -} - - -static void hfa384x_events_no_bap0(struct net_device *dev) -{ - HFA384X_OUTW(HFA384X_EVENT_MASK & ~HFA384X_BAP0_EVENTS, - HFA384X_INTEN_OFF); -} - - -static void hfa384x_events_all(struct net_device *dev) -{ - HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); -} - - -static void hfa384x_events_only_cmd(struct net_device *dev) -{ - HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_INTEN_OFF); -} - - -static u16 hfa384x_allocate_fid(struct net_device *dev, int len) -{ - u16 fid; - unsigned long delay; - - /* FIX: this could be replace with hfa384x_cmd() if the Alloc event - * below would be handled like CmdCompl event (sleep here, wake up from - * interrupt handler */ - if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_ALLOC, len)) { - printk(KERN_DEBUG "%s: cannot allocate fid, len=%d\n", - dev->name, len); - return 0xffff; - } - - delay = jiffies + HFA384X_ALLOC_COMPL_TIMEOUT; - while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC) && - time_before(jiffies, delay)) - yield(); - if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC)) { - printk("%s: fid allocate, len=%d - timeout\n", dev->name, len); - return 0xffff; - } - - fid = HFA384X_INW(HFA384X_ALLOCFID_OFF); - HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF); - - return fid; -} - - -static int prism2_reset_port(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - int res; - - iface = netdev_priv(dev); - local = iface->local; - - if (!local->dev_enabled) - return 0; - - res = hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, - NULL, NULL); - if (res) - printk(KERN_DEBUG "%s: reset port failed to disable port\n", - dev->name); - else { - res = hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, - NULL, NULL); - if (res) - printk(KERN_DEBUG "%s: reset port failed to enable " - "port\n", dev->name); - } - - /* It looks like at least some STA firmware versions reset - * fragmentation threshold back to 2346 after enable command. Restore - * the configured value, if it differs from this default. */ - if (local->fragm_threshold != 2346 && - hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, - local->fragm_threshold)) { - printk(KERN_DEBUG "%s: failed to restore fragmentation " - "threshold (%d) after Port0 enable\n", - dev->name, local->fragm_threshold); - } - - /* Some firmwares lose antenna selection settings on reset */ - (void) hostap_set_antsel(local); - - return res; -} - - -static int prism2_get_version_info(struct net_device *dev, u16 rid, - const char *txt) -{ - struct hfa384x_comp_ident comp; - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->no_pri) { - /* PRI f/w not yet available - cannot read RIDs */ - return -1; - } - if (hfa384x_get_rid(dev, rid, &comp, sizeof(comp), 1) < 0) { - printk(KERN_DEBUG "Could not get RID for component %s\n", txt); - return -1; - } - - printk(KERN_INFO "%s: %s: id=0x%02x v%d.%d.%d\n", dev->name, txt, - __le16_to_cpu(comp.id), __le16_to_cpu(comp.major), - __le16_to_cpu(comp.minor), __le16_to_cpu(comp.variant)); - return 0; -} - - -static int prism2_setup_rids(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - __le16 tmp; - int ret = 0; - - iface = netdev_priv(dev); - local = iface->local; - - hostap_set_word(dev, HFA384X_RID_TICKTIME, 2000); - - if (!local->fw_ap) { - u16 tmp1 = hostap_get_porttype(local); - ret = hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, tmp1); - if (ret) { - printk("%s: Port type setting to %d failed\n", - dev->name, tmp1); - goto fail; - } - } - - /* Setting SSID to empty string seems to kill the card in Host AP mode - */ - if (local->iw_mode != IW_MODE_MASTER || local->essid[0] != '\0') { - ret = hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, - local->essid); - if (ret) { - printk("%s: AP own SSID setting failed\n", dev->name); - goto fail; - } - } - - ret = hostap_set_word(dev, HFA384X_RID_CNFMAXDATALEN, - PRISM2_DATA_MAXLEN); - if (ret) { - printk("%s: MAC data length setting to %d failed\n", - dev->name, PRISM2_DATA_MAXLEN); - goto fail; - } - - if (hfa384x_get_rid(dev, HFA384X_RID_CHANNELLIST, &tmp, 2, 1) < 0) { - printk("%s: Channel list read failed\n", dev->name); - ret = -EINVAL; - goto fail; - } - local->channel_mask = le16_to_cpu(tmp); - - if (local->channel < 1 || local->channel > 14 || - !(local->channel_mask & (1 << (local->channel - 1)))) { - printk(KERN_WARNING "%s: Channel setting out of range " - "(%d)!\n", dev->name, local->channel); - ret = -EBUSY; - goto fail; - } - - ret = hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel); - if (ret) { - printk("%s: Channel setting to %d failed\n", - dev->name, local->channel); - goto fail; - } - - ret = hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, - local->beacon_int); - if (ret) { - printk("%s: Beacon interval setting to %d failed\n", - dev->name, local->beacon_int); - /* this may fail with Symbol/Lucent firmware */ - if (ret == -ETIMEDOUT) - goto fail; - } - - ret = hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, - local->dtim_period); - if (ret) { - printk("%s: DTIM period setting to %d failed\n", - dev->name, local->dtim_period); - /* this may fail with Symbol/Lucent firmware */ - if (ret == -ETIMEDOUT) - goto fail; - } - - ret = hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, - local->is_promisc); - if (ret) - printk(KERN_INFO "%s: Setting promiscuous mode (%d) failed\n", - dev->name, local->is_promisc); - - if (!local->fw_ap) { - ret = hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, - local->essid); - if (ret) { - printk("%s: Desired SSID setting failed\n", dev->name); - goto fail; - } - } - - /* Setup TXRateControl, defaults to allow use of 1, 2, 5.5, and - * 11 Mbps in automatic TX rate fallback and 1 and 2 Mbps as basic - * rates */ - if (local->tx_rate_control == 0) { - local->tx_rate_control = - HFA384X_RATES_1MBPS | - HFA384X_RATES_2MBPS | - HFA384X_RATES_5MBPS | - HFA384X_RATES_11MBPS; - } - if (local->basic_rates == 0) - local->basic_rates = HFA384X_RATES_1MBPS | HFA384X_RATES_2MBPS; - - if (!local->fw_ap) { - ret = hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, - local->tx_rate_control); - if (ret) { - printk("%s: TXRateControl setting to %d failed\n", - dev->name, local->tx_rate_control); - goto fail; - } - - ret = hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, - local->tx_rate_control); - if (ret) { - printk("%s: cnfSupportedRates setting to %d failed\n", - dev->name, local->tx_rate_control); - } - - ret = hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, - local->basic_rates); - if (ret) { - printk("%s: cnfBasicRates setting to %d failed\n", - dev->name, local->basic_rates); - } - - ret = hostap_set_word(dev, HFA384X_RID_CREATEIBSS, 1); - if (ret) { - printk("%s: Create IBSS setting to 1 failed\n", - dev->name); - } - } - - if (local->name_set) - (void) hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, - local->name); - - if (hostap_set_encryption(local)) { - printk(KERN_INFO "%s: could not configure encryption\n", - dev->name); - } - - (void) hostap_set_antsel(local); - - if (hostap_set_roaming(local)) { - printk(KERN_INFO "%s: could not set host roaming\n", - dev->name); - } - - if (local->sta_fw_ver >= PRISM2_FW_VER(1,6,3) && - hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, local->enh_sec)) - printk(KERN_INFO "%s: cnfEnhSecurity setting to 0x%x failed\n", - dev->name, local->enh_sec); - - /* 32-bit tallies were added in STA f/w 0.8.0, but they were apparently - * not working correctly (last seven counters report bogus values). - * This has been fixed in 0.8.2, so enable 32-bit tallies only - * beginning with that firmware version. Another bug fix for 32-bit - * tallies in 1.4.0; should 16-bit tallies be used for some other - * versions, too? */ - if (local->sta_fw_ver >= PRISM2_FW_VER(0,8,2)) { - if (hostap_set_word(dev, HFA384X_RID_CNFTHIRTY2TALLY, 1)) { - printk(KERN_INFO "%s: cnfThirty2Tally setting " - "failed\n", dev->name); - local->tallies32 = 0; - } else - local->tallies32 = 1; - } else - local->tallies32 = 0; - - hostap_set_auth_algs(local); - - if (hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, - local->fragm_threshold)) { - printk(KERN_INFO "%s: setting FragmentationThreshold to %d " - "failed\n", dev->name, local->fragm_threshold); - } - - if (hostap_set_word(dev, HFA384X_RID_RTSTHRESHOLD, - local->rts_threshold)) { - printk(KERN_INFO "%s: setting RTSThreshold to %d failed\n", - dev->name, local->rts_threshold); - } - - if (local->manual_retry_count >= 0 && - hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT, - local->manual_retry_count)) { - printk(KERN_INFO "%s: setting cnfAltRetryCount to %d failed\n", - dev->name, local->manual_retry_count); - } - - if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1) && - hfa384x_get_rid(dev, HFA384X_RID_CNFDBMADJUST, &tmp, 2, 1) == 2) { - local->rssi_to_dBm = le16_to_cpu(tmp); - } - - if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->wpa && - hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1)) { - printk(KERN_INFO "%s: setting ssnHandlingMode to 1 failed\n", - dev->name); - } - - if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->generic_elem && - hfa384x_set_rid(dev, HFA384X_RID_GENERICELEMENT, - local->generic_elem, local->generic_elem_len)) { - printk(KERN_INFO "%s: setting genericElement failed\n", - dev->name); - } - - fail: - return ret; -} - - -static int prism2_hw_init(struct net_device *dev, int initial) -{ - struct hostap_interface *iface; - local_info_t *local; - int ret, first = 1; - unsigned long start, delay; - - PDEBUG(DEBUG_FLOW, "prism2_hw_init()\n"); - - iface = netdev_priv(dev); - local = iface->local; - - clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits); - - init: - /* initialize HFA 384x */ - ret = hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_INIT, 0); - if (ret) { - printk(KERN_INFO "%s: first command failed - assuming card " - "does not have primary firmware\n", dev_info); - } - - if (first && (HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) { - /* EvStat has Cmd bit set in some cases, so retry once if no - * wait was needed */ - HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); - printk(KERN_DEBUG "%s: init command completed too quickly - " - "retrying\n", dev->name); - first = 0; - goto init; - } - - start = jiffies; - delay = jiffies + HFA384X_INIT_TIMEOUT; - while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && - time_before(jiffies, delay)) - yield(); - if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) { - printk(KERN_DEBUG "%s: assuming no Primary image in " - "flash - card initialization not completed\n", - dev_info); - local->no_pri = 1; -#ifdef PRISM2_DOWNLOAD_SUPPORT - if (local->sram_type == -1) - local->sram_type = prism2_get_ram_size(local); -#endif /* PRISM2_DOWNLOAD_SUPPORT */ - return 1; - } - local->no_pri = 0; - printk(KERN_DEBUG "prism2_hw_init: initialized in %lu ms\n", - (jiffies - start) * 1000 / HZ); - HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); - return 0; -} - - -static int prism2_hw_init2(struct net_device *dev, int initial) -{ - struct hostap_interface *iface; - local_info_t *local; - int i; - - iface = netdev_priv(dev); - local = iface->local; - -#ifdef PRISM2_DOWNLOAD_SUPPORT - kfree(local->pda); - if (local->no_pri) - local->pda = NULL; - else - local->pda = prism2_read_pda(dev); -#endif /* PRISM2_DOWNLOAD_SUPPORT */ - - hfa384x_disable_interrupts(dev); - -#ifndef final_version - HFA384X_OUTW(HFA384X_MAGIC, HFA384X_SWSUPPORT0_OFF); - if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) { - printk("SWSUPPORT0 write/read failed: %04X != %04X\n", - HFA384X_INW(HFA384X_SWSUPPORT0_OFF), HFA384X_MAGIC); - goto failed; - } -#endif - - if (initial || local->pri_only) { - hfa384x_events_only_cmd(dev); - /* get card version information */ - if (prism2_get_version_info(dev, HFA384X_RID_NICID, "NIC") || - prism2_get_version_info(dev, HFA384X_RID_PRIID, "PRI")) { - hfa384x_disable_interrupts(dev); - goto failed; - } - - if (prism2_get_version_info(dev, HFA384X_RID_STAID, "STA")) { - printk(KERN_DEBUG "%s: Failed to read STA f/w version " - "- only Primary f/w present\n", dev->name); - local->pri_only = 1; - return 0; - } - local->pri_only = 0; - hfa384x_disable_interrupts(dev); - } - - /* FIX: could convert allocate_fid to use sleeping CmdCompl wait and - * enable interrupts before this. This would also require some sort of - * sleeping AllocEv waiting */ - - /* allocate TX FIDs */ - local->txfid_len = PRISM2_TXFID_LEN; - for (i = 0; i < PRISM2_TXFID_COUNT; i++) { - local->txfid[i] = hfa384x_allocate_fid(dev, local->txfid_len); - if (local->txfid[i] == 0xffff && local->txfid_len > 1600) { - local->txfid[i] = hfa384x_allocate_fid(dev, 1600); - if (local->txfid[i] != 0xffff) { - printk(KERN_DEBUG "%s: Using shorter TX FID " - "(1600 bytes)\n", dev->name); - local->txfid_len = 1600; - } - } - if (local->txfid[i] == 0xffff) - goto failed; - local->intransmitfid[i] = PRISM2_TXFID_EMPTY; - } - - hfa384x_events_only_cmd(dev); - - if (initial) { - struct list_head *ptr; - prism2_check_sta_fw_version(local); - - if (hfa384x_get_rid(dev, HFA384X_RID_CNFOWNMACADDR, - dev->dev_addr, 6, 1) < 0) { - printk("%s: could not get own MAC address\n", - dev->name); - } - list_for_each(ptr, &local->hostap_interfaces) { - iface = list_entry(ptr, struct hostap_interface, list); - eth_hw_addr_inherit(iface->dev, dev); - } - } else if (local->fw_ap) - prism2_check_sta_fw_version(local); - - prism2_setup_rids(dev); - - /* MAC is now configured, but port 0 is not yet enabled */ - return 0; - - failed: - if (!local->no_pri) - printk(KERN_WARNING "%s: Initialization failed\n", dev_info); - return 1; -} - - -static int prism2_hw_enable(struct net_device *dev, int initial) -{ - struct hostap_interface *iface; - local_info_t *local; - int was_resetting; - - iface = netdev_priv(dev); - local = iface->local; - was_resetting = local->hw_resetting; - - if (hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, NULL)) { - printk("%s: MAC port 0 enabling failed\n", dev->name); - return 1; - } - - local->hw_ready = 1; - local->hw_reset_tries = 0; - local->hw_resetting = 0; - hfa384x_enable_interrupts(dev); - - /* at least D-Link DWL-650 seems to require additional port reset - * before it starts acting as an AP, so reset port automatically - * here just in case */ - if (initial && prism2_reset_port(dev)) { - printk("%s: MAC port 0 resetting failed\n", dev->name); - return 1; - } - - if (was_resetting && netif_queue_stopped(dev)) { - /* If hw_reset() was called during pending transmit, netif - * queue was stopped. Wake it up now since the wlan card has - * been resetted. */ - netif_wake_queue(dev); - } - - return 0; -} - - -static int prism2_hw_config(struct net_device *dev, int initial) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->hw_downloading) - return 1; - - if (prism2_hw_init(dev, initial)) { - return local->no_pri ? 0 : 1; - } - - if (prism2_hw_init2(dev, initial)) - return 1; - - /* Enable firmware if secondary image is loaded and at least one of the - * netdevices is up. */ - if (!local->pri_only && - (initial == 0 || (initial == 2 && local->num_dev_open > 0))) { - if (!local->dev_enabled) - prism2_callback(local, PRISM2_CALLBACK_ENABLE); - local->dev_enabled = 1; - return prism2_hw_enable(dev, initial); - } - - return 0; -} - - -static void prism2_hw_shutdown(struct net_device *dev, int no_disable) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - /* Allow only command completion events during disable */ - hfa384x_events_only_cmd(dev); - - local->hw_ready = 0; - if (local->dev_enabled) - prism2_callback(local, PRISM2_CALLBACK_DISABLE); - local->dev_enabled = 0; - - if (local->func->card_present && !local->func->card_present(local)) { - printk(KERN_DEBUG "%s: card already removed or not configured " - "during shutdown\n", dev->name); - return; - } - - if ((no_disable & HOSTAP_HW_NO_DISABLE) == 0 && - hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, NULL)) - printk(KERN_WARNING "%s: Shutdown failed\n", dev_info); - - hfa384x_disable_interrupts(dev); - - if (no_disable & HOSTAP_HW_ENABLE_CMDCOMPL) - hfa384x_events_only_cmd(dev); - else - prism2_clear_cmd_queue(local); -} - - -static void prism2_hw_reset(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - -#if 0 - static long last_reset = 0; - - /* do not reset card more than once per second to avoid ending up in a - * busy loop resetting the card */ - if (time_before_eq(jiffies, last_reset + HZ)) - return; - last_reset = jiffies; -#endif - - iface = netdev_priv(dev); - local = iface->local; - - if (in_interrupt()) { - printk(KERN_DEBUG "%s: driver bug - prism2_hw_reset() called " - "in interrupt context\n", dev->name); - return; - } - - if (local->hw_downloading) - return; - - if (local->hw_resetting) { - printk(KERN_WARNING "%s: %s: already resetting card - " - "ignoring reset request\n", dev_info, dev->name); - return; - } - - local->hw_reset_tries++; - if (local->hw_reset_tries > 10) { - printk(KERN_WARNING "%s: too many reset tries, skipping\n", - dev->name); - return; - } - - printk(KERN_WARNING "%s: %s: resetting card\n", dev_info, dev->name); - hfa384x_disable_interrupts(dev); - local->hw_resetting = 1; - if (local->func->cor_sreset) { - /* Host system seems to hang in some cases with high traffic - * load or shared interrupts during COR sreset. Disable shared - * interrupts during reset to avoid these crashes. COS sreset - * takes quite a long time, so it is unfortunate that this - * seems to be needed. Anyway, I do not know of any better way - * of avoiding the crash. */ - disable_irq(dev->irq); - local->func->cor_sreset(local); - enable_irq(dev->irq); - } - prism2_hw_shutdown(dev, 1); - prism2_hw_config(dev, 0); - local->hw_resetting = 0; - -#ifdef PRISM2_DOWNLOAD_SUPPORT - if (local->dl_pri) { - printk(KERN_DEBUG "%s: persistent download of primary " - "firmware\n", dev->name); - if (prism2_download_genesis(local, local->dl_pri) < 0) - printk(KERN_WARNING "%s: download (PRI) failed\n", - dev->name); - } - - if (local->dl_sec) { - printk(KERN_DEBUG "%s: persistent download of secondary " - "firmware\n", dev->name); - if (prism2_download_volatile(local, local->dl_sec) < 0) - printk(KERN_WARNING "%s: download (SEC) failed\n", - dev->name); - } -#endif /* PRISM2_DOWNLOAD_SUPPORT */ - - /* TODO: restore beacon TIM bits for STAs that have buffered frames */ -} - - -static void prism2_schedule_reset(local_info_t *local) -{ - schedule_work(&local->reset_queue); -} - - -/* Called only as scheduled task after noticing card timeout in interrupt - * context */ -static void handle_reset_queue(struct work_struct *work) -{ - local_info_t *local = container_of(work, local_info_t, reset_queue); - - printk(KERN_DEBUG "%s: scheduled card reset\n", local->dev->name); - prism2_hw_reset(local->dev); - - if (netif_queue_stopped(local->dev)) { - int i; - - for (i = 0; i < PRISM2_TXFID_COUNT; i++) - if (local->intransmitfid[i] == PRISM2_TXFID_EMPTY) { - PDEBUG(DEBUG_EXTRA, "prism2_tx_timeout: " - "wake up queue\n"); - netif_wake_queue(local->dev); - break; - } - } -} - - -static int prism2_get_txfid_idx(local_info_t *local) -{ - int idx, end; - unsigned long flags; - - spin_lock_irqsave(&local->txfidlock, flags); - end = idx = local->next_txfid; - do { - if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { - local->intransmitfid[idx] = PRISM2_TXFID_RESERVED; - spin_unlock_irqrestore(&local->txfidlock, flags); - return idx; - } - idx++; - if (idx >= PRISM2_TXFID_COUNT) - idx = 0; - } while (idx != end); - spin_unlock_irqrestore(&local->txfidlock, flags); - - PDEBUG(DEBUG_EXTRA2, "prism2_get_txfid_idx: no room in txfid buf: " - "packet dropped\n"); - local->dev->stats.tx_dropped++; - - return -1; -} - - -/* Called only from hardware IRQ */ -static void prism2_transmit_cb(struct net_device *dev, long context, - u16 resp0, u16 res) -{ - struct hostap_interface *iface; - local_info_t *local; - int idx = (int) context; - - iface = netdev_priv(dev); - local = iface->local; - - if (res) { - printk(KERN_DEBUG "%s: prism2_transmit_cb - res=0x%02x\n", - dev->name, res); - return; - } - - if (idx < 0 || idx >= PRISM2_TXFID_COUNT) { - printk(KERN_DEBUG "%s: prism2_transmit_cb called with invalid " - "idx=%d\n", dev->name, idx); - return; - } - - if (!test_and_clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { - printk(KERN_DEBUG "%s: driver bug: prism2_transmit_cb called " - "with no pending transmit\n", dev->name); - } - - if (netif_queue_stopped(dev)) { - /* ready for next TX, so wake up queue that was stopped in - * prism2_transmit() */ - netif_wake_queue(dev); - } - - spin_lock(&local->txfidlock); - - /* With reclaim, Resp0 contains new txfid for transmit; the old txfid - * will be automatically allocated for the next TX frame */ - local->intransmitfid[idx] = resp0; - - PDEBUG(DEBUG_FID, "%s: prism2_transmit_cb: txfid[%d]=0x%04x, " - "resp0=0x%04x, transmit_txfid=0x%04x\n", - dev->name, idx, local->txfid[idx], - resp0, local->intransmitfid[local->next_txfid]); - - idx++; - if (idx >= PRISM2_TXFID_COUNT) - idx = 0; - local->next_txfid = idx; - - /* check if all TX buffers are occupied */ - do { - if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { - spin_unlock(&local->txfidlock); - return; - } - idx++; - if (idx >= PRISM2_TXFID_COUNT) - idx = 0; - } while (idx != local->next_txfid); - spin_unlock(&local->txfidlock); - - /* no empty TX buffers, stop queue */ - netif_stop_queue(dev); -} - - -/* Called only from software IRQ if PCI bus master is not used (with bus master - * this can be called both from software and hardware IRQ) */ -static int prism2_transmit(struct net_device *dev, int idx) -{ - struct hostap_interface *iface; - local_info_t *local; - int res; - - iface = netdev_priv(dev); - local = iface->local; - - /* The driver tries to stop netif queue so that there would not be - * more than one attempt to transmit frames going on; check that this - * is really the case */ - - if (test_and_set_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { - printk(KERN_DEBUG "%s: driver bug - prism2_transmit() called " - "when previous TX was pending\n", dev->name); - return -1; - } - - /* stop the queue for the time that transmit is pending */ - netif_stop_queue(dev); - - /* transmit packet */ - res = hfa384x_cmd_callback( - dev, - HFA384X_CMDCODE_TRANSMIT | HFA384X_CMD_TX_RECLAIM, - local->txfid[idx], - prism2_transmit_cb, (long) idx); - - if (res) { - printk(KERN_DEBUG "%s: prism2_transmit: CMDCODE_TRANSMIT " - "failed (res=%d)\n", dev->name, res); - dev->stats.tx_dropped++; - netif_wake_queue(dev); - return -1; - } - dev->trans_start = jiffies; - - /* Since we did not wait for command completion, the card continues - * to process on the background and we will finish handling when - * command completion event is handled (prism2_cmd_ev() function) */ - - return 0; -} - - -/* Send IEEE 802.11 frame (convert the header into Prism2 TX descriptor and - * send the payload with this descriptor) */ -/* Called only from software IRQ */ -static int prism2_tx_80211(struct sk_buff *skb, struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - struct hfa384x_tx_frame txdesc; - struct hostap_skb_tx_data *meta; - int hdr_len, data_len, idx, res, ret = -1; - u16 tx_control, fc; - - iface = netdev_priv(dev); - local = iface->local; - - meta = (struct hostap_skb_tx_data *) skb->cb; - - prism2_callback(local, PRISM2_CALLBACK_TX_START); - - if ((local->func->card_present && !local->func->card_present(local)) || - !local->hw_ready || local->hw_downloading || local->pri_only) { - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: prism2_tx_80211: hw not ready -" - " skipping\n", dev->name); - } - goto fail; - } - - memset(&txdesc, 0, sizeof(txdesc)); - - /* skb->data starts with txdesc->frame_control */ - hdr_len = 24; - skb_copy_from_linear_data(skb, &txdesc.frame_control, hdr_len); - fc = le16_to_cpu(txdesc.frame_control); - if (ieee80211_is_data(txdesc.frame_control) && - ieee80211_has_a4(txdesc.frame_control) && - skb->len >= 30) { - /* Addr4 */ - skb_copy_from_linear_data_offset(skb, hdr_len, txdesc.addr4, - ETH_ALEN); - hdr_len += ETH_ALEN; - } - - tx_control = local->tx_control; - if (meta->tx_cb_idx) { - tx_control |= HFA384X_TX_CTRL_TX_OK; - txdesc.sw_support = cpu_to_le32(meta->tx_cb_idx); - } - txdesc.tx_control = cpu_to_le16(tx_control); - txdesc.tx_rate = meta->rate; - - data_len = skb->len - hdr_len; - txdesc.data_len = cpu_to_le16(data_len); - txdesc.len = cpu_to_be16(data_len); - - idx = prism2_get_txfid_idx(local); - if (idx < 0) - goto fail; - - if (local->frame_dump & PRISM2_DUMP_TX_HDR) - hostap_dump_tx_header(dev->name, &txdesc); - - spin_lock(&local->baplock); - res = hfa384x_setup_bap(dev, BAP0, local->txfid[idx], 0); - - if (!res) - res = hfa384x_to_bap(dev, BAP0, &txdesc, sizeof(txdesc)); - if (!res) - res = hfa384x_to_bap(dev, BAP0, skb->data + hdr_len, - skb->len - hdr_len); - spin_unlock(&local->baplock); - - if (!res) - res = prism2_transmit(dev, idx); - if (res) { - printk(KERN_DEBUG "%s: prism2_tx_80211 - to BAP0 failed\n", - dev->name); - local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; - schedule_work(&local->reset_queue); - goto fail; - } - - ret = 0; - -fail: - prism2_callback(local, PRISM2_CALLBACK_TX_END); - return ret; -} - - -/* Some SMP systems have reported number of odd errors with hostap_pci. fid - * register has changed values between consecutive reads for an unknown reason. - * This should really not happen, so more debugging is needed. This test - * version is a bit slower, but it will detect most of such register changes - * and will try to get the correct fid eventually. */ -#define EXTRA_FID_READ_TESTS - -static u16 prism2_read_fid_reg(struct net_device *dev, u16 reg) -{ -#ifdef EXTRA_FID_READ_TESTS - u16 val, val2, val3; - int i; - - for (i = 0; i < 10; i++) { - val = HFA384X_INW(reg); - val2 = HFA384X_INW(reg); - val3 = HFA384X_INW(reg); - - if (val == val2 && val == val3) - return val; - - printk(KERN_DEBUG "%s: detected fid change (try=%d, reg=%04x):" - " %04x %04x %04x\n", - dev->name, i, reg, val, val2, val3); - if ((val == val2 || val == val3) && val != 0) - return val; - if (val2 == val3 && val2 != 0) - return val2; - } - printk(KERN_WARNING "%s: Uhhuh.. could not read good fid from reg " - "%04x (%04x %04x %04x)\n", dev->name, reg, val, val2, val3); - return val; -#else /* EXTRA_FID_READ_TESTS */ - return HFA384X_INW(reg); -#endif /* EXTRA_FID_READ_TESTS */ -} - - -/* Called only as a tasklet (software IRQ) */ -static void prism2_rx(local_info_t *local) -{ - struct net_device *dev = local->dev; - int res, rx_pending = 0; - u16 len, hdr_len, rxfid, status, macport; - struct hfa384x_rx_frame rxdesc; - struct sk_buff *skb = NULL; - - prism2_callback(local, PRISM2_CALLBACK_RX_START); - - rxfid = prism2_read_fid_reg(dev, HFA384X_RXFID_OFF); -#ifndef final_version - if (rxfid == 0) { - rxfid = HFA384X_INW(HFA384X_RXFID_OFF); - printk(KERN_DEBUG "prism2_rx: rxfid=0 (next 0x%04x)\n", - rxfid); - if (rxfid == 0) { - schedule_work(&local->reset_queue); - goto rx_dropped; - } - /* try to continue with the new rxfid value */ - } -#endif - - spin_lock(&local->baplock); - res = hfa384x_setup_bap(dev, BAP0, rxfid, 0); - if (!res) - res = hfa384x_from_bap(dev, BAP0, &rxdesc, sizeof(rxdesc)); - - if (res) { - spin_unlock(&local->baplock); - printk(KERN_DEBUG "%s: copy from BAP0 failed %d\n", dev->name, - res); - if (res == -ETIMEDOUT) { - schedule_work(&local->reset_queue); - } - goto rx_dropped; - } - - len = le16_to_cpu(rxdesc.data_len); - hdr_len = sizeof(rxdesc); - status = le16_to_cpu(rxdesc.status); - macport = (status >> 8) & 0x07; - - /* Drop frames with too large reported payload length. Monitor mode - * seems to sometimes pass frames (e.g., ctrl::ack) with signed and - * negative value, so allow also values 65522 .. 65534 (-14 .. -2) for - * macport 7 */ - if (len > PRISM2_DATA_MAXLEN + 8 /* WEP */) { - if (macport == 7 && local->iw_mode == IW_MODE_MONITOR) { - if (len >= (u16) -14) { - hdr_len -= 65535 - len; - hdr_len--; - } - len = 0; - } else { - spin_unlock(&local->baplock); - printk(KERN_DEBUG "%s: Received frame with invalid " - "length 0x%04x\n", dev->name, len); - hostap_dump_rx_header(dev->name, &rxdesc); - goto rx_dropped; - } - } - - skb = dev_alloc_skb(len + hdr_len); - if (!skb) { - spin_unlock(&local->baplock); - printk(KERN_DEBUG "%s: RX failed to allocate skb\n", - dev->name); - goto rx_dropped; - } - skb->dev = dev; - memcpy(skb_put(skb, hdr_len), &rxdesc, hdr_len); - - if (len > 0) - res = hfa384x_from_bap(dev, BAP0, skb_put(skb, len), len); - spin_unlock(&local->baplock); - if (res) { - printk(KERN_DEBUG "%s: RX failed to read " - "frame data\n", dev->name); - goto rx_dropped; - } - - skb_queue_tail(&local->rx_list, skb); - tasklet_schedule(&local->rx_tasklet); - - rx_exit: - prism2_callback(local, PRISM2_CALLBACK_RX_END); - if (!rx_pending) { - HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); - } - - return; - - rx_dropped: - dev->stats.rx_dropped++; - if (skb) - dev_kfree_skb(skb); - goto rx_exit; -} - - -/* Called only as a tasklet (software IRQ) */ -static void hostap_rx_skb(local_info_t *local, struct sk_buff *skb) -{ - struct hfa384x_rx_frame *rxdesc; - struct net_device *dev = skb->dev; - struct hostap_80211_rx_status stats; - int hdrlen, rx_hdrlen; - - rx_hdrlen = sizeof(*rxdesc); - if (skb->len < sizeof(*rxdesc)) { - /* Allow monitor mode to receive shorter frames */ - if (local->iw_mode == IW_MODE_MONITOR && - skb->len >= sizeof(*rxdesc) - 30) { - rx_hdrlen = skb->len; - } else { - dev_kfree_skb(skb); - return; - } - } - - rxdesc = (struct hfa384x_rx_frame *) skb->data; - - if (local->frame_dump & PRISM2_DUMP_RX_HDR && - skb->len >= sizeof(*rxdesc)) - hostap_dump_rx_header(dev->name, rxdesc); - - if (le16_to_cpu(rxdesc->status) & HFA384X_RX_STATUS_FCSERR && - (!local->monitor_allow_fcserr || - local->iw_mode != IW_MODE_MONITOR)) - goto drop; - - if (skb->len > PRISM2_DATA_MAXLEN) { - printk(KERN_DEBUG "%s: RX: len(%d) > MAX(%d)\n", - dev->name, skb->len, PRISM2_DATA_MAXLEN); - goto drop; - } - - stats.mac_time = le32_to_cpu(rxdesc->time); - stats.signal = rxdesc->signal - local->rssi_to_dBm; - stats.noise = rxdesc->silence - local->rssi_to_dBm; - stats.rate = rxdesc->rate; - - /* Convert Prism2 RX structure into IEEE 802.11 header */ - hdrlen = hostap_80211_get_hdrlen(rxdesc->frame_control); - if (hdrlen > rx_hdrlen) - hdrlen = rx_hdrlen; - - memmove(skb_pull(skb, rx_hdrlen - hdrlen), - &rxdesc->frame_control, hdrlen); - - hostap_80211_rx(dev, skb, &stats); - return; - - drop: - dev_kfree_skb(skb); -} - - -/* Called only as a tasklet (software IRQ) */ -static void hostap_rx_tasklet(unsigned long data) -{ - local_info_t *local = (local_info_t *) data; - struct sk_buff *skb; - - while ((skb = skb_dequeue(&local->rx_list)) != NULL) - hostap_rx_skb(local, skb); -} - - -/* Called only from hardware IRQ */ -static void prism2_alloc_ev(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - int idx; - u16 fid; - - iface = netdev_priv(dev); - local = iface->local; - - fid = prism2_read_fid_reg(dev, HFA384X_ALLOCFID_OFF); - - PDEBUG(DEBUG_FID, "FID: interrupt: ALLOC - fid=0x%04x\n", fid); - - spin_lock(&local->txfidlock); - idx = local->next_alloc; - - do { - if (local->txfid[idx] == fid) { - PDEBUG(DEBUG_FID, "FID: found matching txfid[%d]\n", - idx); - -#ifndef final_version - if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) - printk("Already released txfid found at idx " - "%d\n", idx); - if (local->intransmitfid[idx] == PRISM2_TXFID_RESERVED) - printk("Already reserved txfid found at idx " - "%d\n", idx); -#endif - local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; - idx++; - local->next_alloc = idx >= PRISM2_TXFID_COUNT ? 0 : - idx; - - if (!test_bit(HOSTAP_BITS_TRANSMIT, &local->bits) && - netif_queue_stopped(dev)) - netif_wake_queue(dev); - - spin_unlock(&local->txfidlock); - return; - } - - idx++; - if (idx >= PRISM2_TXFID_COUNT) - idx = 0; - } while (idx != local->next_alloc); - - printk(KERN_WARNING "%s: could not find matching txfid (0x%04x, new " - "read 0x%04x) for alloc event\n", dev->name, fid, - HFA384X_INW(HFA384X_ALLOCFID_OFF)); - printk(KERN_DEBUG "TXFIDs:"); - for (idx = 0; idx < PRISM2_TXFID_COUNT; idx++) - printk(" %04x[%04x]", local->txfid[idx], - local->intransmitfid[idx]); - printk("\n"); - spin_unlock(&local->txfidlock); - - /* FIX: should probably schedule reset; reference to one txfid was lost - * completely.. Bad things will happen if we run out of txfids - * Actually, this will cause netdev watchdog to notice TX timeout and - * then card reset after all txfids have been leaked. */ -} - - -/* Called only as a tasklet (software IRQ) */ -static void hostap_tx_callback(local_info_t *local, - struct hfa384x_tx_frame *txdesc, int ok, - char *payload) -{ - u16 sw_support, hdrlen, len; - struct sk_buff *skb; - struct hostap_tx_callback_info *cb; - - /* Make sure that frame was from us. */ - if (!ether_addr_equal(txdesc->addr2, local->dev->dev_addr)) { - printk(KERN_DEBUG "%s: TX callback - foreign frame\n", - local->dev->name); - return; - } - - sw_support = le32_to_cpu(txdesc->sw_support); - - spin_lock(&local->lock); - cb = local->tx_callback; - while (cb != NULL && cb->idx != sw_support) - cb = cb->next; - spin_unlock(&local->lock); - - if (cb == NULL) { - printk(KERN_DEBUG "%s: could not find TX callback (idx %d)\n", - local->dev->name, sw_support); - return; - } - - hdrlen = hostap_80211_get_hdrlen(txdesc->frame_control); - len = le16_to_cpu(txdesc->data_len); - skb = dev_alloc_skb(hdrlen + len); - if (skb == NULL) { - printk(KERN_DEBUG "%s: hostap_tx_callback failed to allocate " - "skb\n", local->dev->name); - return; - } - - memcpy(skb_put(skb, hdrlen), (void *) &txdesc->frame_control, hdrlen); - if (payload) - memcpy(skb_put(skb, len), payload, len); - - skb->dev = local->dev; - skb_reset_mac_header(skb); - - cb->func(skb, ok, cb->data); -} - - -/* Called only as a tasklet (software IRQ) */ -static int hostap_tx_compl_read(local_info_t *local, int error, - struct hfa384x_tx_frame *txdesc, - char **payload) -{ - u16 fid, len; - int res, ret = 0; - struct net_device *dev = local->dev; - - fid = prism2_read_fid_reg(dev, HFA384X_TXCOMPLFID_OFF); - - PDEBUG(DEBUG_FID, "interrupt: TX (err=%d) - fid=0x%04x\n", fid, error); - - spin_lock(&local->baplock); - res = hfa384x_setup_bap(dev, BAP0, fid, 0); - if (!res) - res = hfa384x_from_bap(dev, BAP0, txdesc, sizeof(*txdesc)); - if (res) { - PDEBUG(DEBUG_EXTRA, "%s: TX (err=%d) - fid=0x%04x - could not " - "read txdesc\n", dev->name, error, fid); - if (res == -ETIMEDOUT) { - schedule_work(&local->reset_queue); - } - ret = -1; - goto fail; - } - if (txdesc->sw_support) { - len = le16_to_cpu(txdesc->data_len); - if (len < PRISM2_DATA_MAXLEN) { - *payload = kmalloc(len, GFP_ATOMIC); - if (*payload == NULL || - hfa384x_from_bap(dev, BAP0, *payload, len)) { - PDEBUG(DEBUG_EXTRA, "%s: could not read TX " - "frame payload\n", dev->name); - kfree(*payload); - *payload = NULL; - ret = -1; - goto fail; - } - } - } - - fail: - spin_unlock(&local->baplock); - - return ret; -} - - -/* Called only as a tasklet (software IRQ) */ -static void prism2_tx_ev(local_info_t *local) -{ - struct net_device *dev = local->dev; - char *payload = NULL; - struct hfa384x_tx_frame txdesc; - - if (hostap_tx_compl_read(local, 0, &txdesc, &payload)) - goto fail; - - if (local->frame_dump & PRISM2_DUMP_TX_HDR) { - PDEBUG(DEBUG_EXTRA, "%s: TX - status=0x%04x " - "retry_count=%d tx_rate=%d seq_ctrl=%d " - "duration_id=%d\n", - dev->name, le16_to_cpu(txdesc.status), - txdesc.retry_count, txdesc.tx_rate, - le16_to_cpu(txdesc.seq_ctrl), - le16_to_cpu(txdesc.duration_id)); - } - - if (txdesc.sw_support) - hostap_tx_callback(local, &txdesc, 1, payload); - kfree(payload); - - fail: - HFA384X_OUTW(HFA384X_EV_TX, HFA384X_EVACK_OFF); -} - - -/* Called only as a tasklet (software IRQ) */ -static void hostap_sta_tx_exc_tasklet(unsigned long data) -{ - local_info_t *local = (local_info_t *) data; - struct sk_buff *skb; - - while ((skb = skb_dequeue(&local->sta_tx_exc_list)) != NULL) { - struct hfa384x_tx_frame *txdesc = - (struct hfa384x_tx_frame *) skb->data; - - if (skb->len >= sizeof(*txdesc)) { - /* Convert Prism2 RX structure into IEEE 802.11 header - */ - int hdrlen = hostap_80211_get_hdrlen(txdesc->frame_control); - memmove(skb_pull(skb, sizeof(*txdesc) - hdrlen), - &txdesc->frame_control, hdrlen); - - hostap_handle_sta_tx_exc(local, skb); - } - dev_kfree_skb(skb); - } -} - - -/* Called only as a tasklet (software IRQ) */ -static void prism2_txexc(local_info_t *local) -{ - struct net_device *dev = local->dev; - u16 status, fc; - int show_dump, res; - char *payload = NULL; - struct hfa384x_tx_frame txdesc; - - show_dump = local->frame_dump & PRISM2_DUMP_TXEXC_HDR; - dev->stats.tx_errors++; - - res = hostap_tx_compl_read(local, 1, &txdesc, &payload); - HFA384X_OUTW(HFA384X_EV_TXEXC, HFA384X_EVACK_OFF); - if (res) - return; - - status = le16_to_cpu(txdesc.status); - - /* We produce a TXDROP event only for retry or lifetime - * exceeded, because that's the only status that really mean - * that this particular node went away. - * Other errors means that *we* screwed up. - Jean II */ - if (status & (HFA384X_TX_STATUS_RETRYERR | HFA384X_TX_STATUS_AGEDERR)) - { - union iwreq_data wrqu; - - /* Copy 802.11 dest address. */ - memcpy(wrqu.addr.sa_data, txdesc.addr1, ETH_ALEN); - wrqu.addr.sa_family = ARPHRD_ETHER; - wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); - } else - show_dump = 1; - - if (local->iw_mode == IW_MODE_MASTER || - local->iw_mode == IW_MODE_REPEAT || - local->wds_type & HOSTAP_WDS_AP_CLIENT) { - struct sk_buff *skb; - skb = dev_alloc_skb(sizeof(txdesc)); - if (skb) { - memcpy(skb_put(skb, sizeof(txdesc)), &txdesc, - sizeof(txdesc)); - skb_queue_tail(&local->sta_tx_exc_list, skb); - tasklet_schedule(&local->sta_tx_exc_tasklet); - } - } - - if (txdesc.sw_support) - hostap_tx_callback(local, &txdesc, 0, payload); - kfree(payload); - - if (!show_dump) - return; - - PDEBUG(DEBUG_EXTRA, "%s: TXEXC - status=0x%04x (%s%s%s%s)" - " tx_control=%04x\n", - dev->name, status, - status & HFA384X_TX_STATUS_RETRYERR ? "[RetryErr]" : "", - status & HFA384X_TX_STATUS_AGEDERR ? "[AgedErr]" : "", - status & HFA384X_TX_STATUS_DISCON ? "[Discon]" : "", - status & HFA384X_TX_STATUS_FORMERR ? "[FormErr]" : "", - le16_to_cpu(txdesc.tx_control)); - - fc = le16_to_cpu(txdesc.frame_control); - PDEBUG(DEBUG_EXTRA, " retry_count=%d tx_rate=%d fc=0x%04x " - "(%s%s%s::%d%s%s)\n", - txdesc.retry_count, txdesc.tx_rate, fc, - ieee80211_is_mgmt(txdesc.frame_control) ? "Mgmt" : "", - ieee80211_is_ctl(txdesc.frame_control) ? "Ctrl" : "", - ieee80211_is_data(txdesc.frame_control) ? "Data" : "", - (fc & IEEE80211_FCTL_STYPE) >> 4, - ieee80211_has_tods(txdesc.frame_control) ? " ToDS" : "", - ieee80211_has_fromds(txdesc.frame_control) ? " FromDS" : ""); - PDEBUG(DEBUG_EXTRA, " A1=%pM A2=%pM A3=%pM A4=%pM\n", - txdesc.addr1, txdesc.addr2, - txdesc.addr3, txdesc.addr4); -} - - -/* Called only as a tasklet (software IRQ) */ -static void hostap_info_tasklet(unsigned long data) -{ - local_info_t *local = (local_info_t *) data; - struct sk_buff *skb; - - while ((skb = skb_dequeue(&local->info_list)) != NULL) { - hostap_info_process(local, skb); - dev_kfree_skb(skb); - } -} - - -/* Called only as a tasklet (software IRQ) */ -static void prism2_info(local_info_t *local) -{ - struct net_device *dev = local->dev; - u16 fid; - int res, left; - struct hfa384x_info_frame info; - struct sk_buff *skb; - - fid = HFA384X_INW(HFA384X_INFOFID_OFF); - - spin_lock(&local->baplock); - res = hfa384x_setup_bap(dev, BAP0, fid, 0); - if (!res) - res = hfa384x_from_bap(dev, BAP0, &info, sizeof(info)); - if (res) { - spin_unlock(&local->baplock); - printk(KERN_DEBUG "Could not get info frame (fid=0x%04x)\n", - fid); - if (res == -ETIMEDOUT) { - schedule_work(&local->reset_queue); - } - goto out; - } - - left = (le16_to_cpu(info.len) - 1) * 2; - - if (info.len & cpu_to_le16(0x8000) || info.len == 0 || left > 2060) { - /* data register seems to give 0x8000 in some error cases even - * though busy bit is not set in offset register; - * in addition, length must be at least 1 due to type field */ - spin_unlock(&local->baplock); - printk(KERN_DEBUG "%s: Received info frame with invalid " - "length 0x%04x (type 0x%04x)\n", dev->name, - le16_to_cpu(info.len), le16_to_cpu(info.type)); - goto out; - } - - skb = dev_alloc_skb(sizeof(info) + left); - if (skb == NULL) { - spin_unlock(&local->baplock); - printk(KERN_DEBUG "%s: Could not allocate skb for info " - "frame\n", dev->name); - goto out; - } - - memcpy(skb_put(skb, sizeof(info)), &info, sizeof(info)); - if (left > 0 && hfa384x_from_bap(dev, BAP0, skb_put(skb, left), left)) - { - spin_unlock(&local->baplock); - printk(KERN_WARNING "%s: Info frame read failed (fid=0x%04x, " - "len=0x%04x, type=0x%04x\n", dev->name, fid, - le16_to_cpu(info.len), le16_to_cpu(info.type)); - dev_kfree_skb(skb); - goto out; - } - spin_unlock(&local->baplock); - - skb_queue_tail(&local->info_list, skb); - tasklet_schedule(&local->info_tasklet); - - out: - HFA384X_OUTW(HFA384X_EV_INFO, HFA384X_EVACK_OFF); -} - - -/* Called only as a tasklet (software IRQ) */ -static void hostap_bap_tasklet(unsigned long data) -{ - local_info_t *local = (local_info_t *) data; - struct net_device *dev = local->dev; - u16 ev; - int frames = 30; - - if (local->func->card_present && !local->func->card_present(local)) - return; - - set_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits); - - /* Process all pending BAP events without generating new interrupts - * for them */ - while (frames-- > 0) { - ev = HFA384X_INW(HFA384X_EVSTAT_OFF); - if (ev == 0xffff || !(ev & HFA384X_BAP0_EVENTS)) - break; - if (ev & HFA384X_EV_RX) - prism2_rx(local); - if (ev & HFA384X_EV_INFO) - prism2_info(local); - if (ev & HFA384X_EV_TX) - prism2_tx_ev(local); - if (ev & HFA384X_EV_TXEXC) - prism2_txexc(local); - } - - set_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits); - clear_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits); - - /* Enable interrupts for new BAP events */ - hfa384x_events_all(dev); - clear_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits); -} - - -/* Called only from hardware IRQ */ -static void prism2_infdrop(struct net_device *dev) -{ - static unsigned long last_inquire = 0; - - PDEBUG(DEBUG_EXTRA, "%s: INFDROP event\n", dev->name); - - /* some firmware versions seem to get stuck with - * full CommTallies in high traffic load cases; every - * packet will then cause INFDROP event and CommTallies - * info frame will not be sent automatically. Try to - * get out of this state by inquiring CommTallies. */ - if (!last_inquire || time_after(jiffies, last_inquire + HZ)) { - hfa384x_cmd_callback(dev, HFA384X_CMDCODE_INQUIRE, - HFA384X_INFO_COMMTALLIES, NULL, 0); - last_inquire = jiffies; - } -} - - -/* Called only from hardware IRQ */ -static void prism2_ev_tick(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - u16 evstat, inten; - static int prev_stuck = 0; - - iface = netdev_priv(dev); - local = iface->local; - - if (time_after(jiffies, local->last_tick_timer + 5 * HZ) && - local->last_tick_timer) { - evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); - inten = HFA384X_INW(HFA384X_INTEN_OFF); - if (!prev_stuck) { - printk(KERN_INFO "%s: SW TICK stuck? " - "bits=0x%lx EvStat=%04x IntEn=%04x\n", - dev->name, local->bits, evstat, inten); - } - local->sw_tick_stuck++; - if ((evstat & HFA384X_BAP0_EVENTS) && - (inten & HFA384X_BAP0_EVENTS)) { - printk(KERN_INFO "%s: trying to recover from IRQ " - "hang\n", dev->name); - hfa384x_events_no_bap0(dev); - } - prev_stuck = 1; - } else - prev_stuck = 0; -} - - -/* Called only from hardware IRQ */ -static void prism2_check_magic(local_info_t *local) -{ - /* at least PCI Prism2.5 with bus mastering seems to sometimes - * return 0x0000 in SWSUPPORT0 for unknown reason, but re-reading the - * register once or twice seems to get the correct value.. PCI cards - * cannot anyway be removed during normal operation, so there is not - * really any need for this verification with them. */ - -#ifndef PRISM2_PCI -#ifndef final_version - static unsigned long last_magic_err = 0; - struct net_device *dev = local->dev; - - if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) { - if (!local->hw_ready) - return; - HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); - if (time_after(jiffies, last_magic_err + 10 * HZ)) { - printk("%s: Interrupt, but SWSUPPORT0 does not match: " - "%04X != %04X - card removed?\n", dev->name, - HFA384X_INW(HFA384X_SWSUPPORT0_OFF), - HFA384X_MAGIC); - last_magic_err = jiffies; - } else if (net_ratelimit()) { - printk(KERN_DEBUG "%s: interrupt - SWSUPPORT0=%04x " - "MAGIC=%04x\n", dev->name, - HFA384X_INW(HFA384X_SWSUPPORT0_OFF), - HFA384X_MAGIC); - } - if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != 0xffff) - schedule_work(&local->reset_queue); - return; - } -#endif /* final_version */ -#endif /* !PRISM2_PCI */ -} - - -/* Called only from hardware IRQ */ -static irqreturn_t prism2_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct hostap_interface *iface; - local_info_t *local; - int events = 0; - u16 ev; - - iface = netdev_priv(dev); - local = iface->local; - - /* Detect early interrupt before driver is fully configured */ - spin_lock(&local->irq_init_lock); - if (!dev->base_addr) { - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: Interrupt, but dev not configured\n", - dev->name); - } - spin_unlock(&local->irq_init_lock); - return IRQ_HANDLED; - } - spin_unlock(&local->irq_init_lock); - - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0); - - if (local->func->card_present && !local->func->card_present(local)) { - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: Interrupt, but dev not OK\n", - dev->name); - } - return IRQ_HANDLED; - } - - prism2_check_magic(local); - - for (;;) { - ev = HFA384X_INW(HFA384X_EVSTAT_OFF); - if (ev == 0xffff) { - if (local->shutdown) - return IRQ_HANDLED; - HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); - printk(KERN_DEBUG "%s: prism2_interrupt: ev=0xffff\n", - dev->name); - return IRQ_HANDLED; - } - - ev &= HFA384X_INW(HFA384X_INTEN_OFF); - if (ev == 0) - break; - - if (ev & HFA384X_EV_CMD) { - prism2_cmd_ev(dev); - } - - /* Above events are needed even before hw is ready, but other - * events should be skipped during initialization. This may - * change for AllocEv if allocate_fid is implemented without - * busy waiting. */ - if (!local->hw_ready || local->hw_resetting || - !local->dev_enabled) { - ev = HFA384X_INW(HFA384X_EVSTAT_OFF); - if (ev & HFA384X_EV_CMD) - goto next_event; - if ((ev & HFA384X_EVENT_MASK) == 0) - return IRQ_HANDLED; - if (local->dev_enabled && (ev & ~HFA384X_EV_TICK) && - net_ratelimit()) { - printk(KERN_DEBUG "%s: prism2_interrupt: hw " - "not ready; skipping events 0x%04x " - "(IntEn=0x%04x)%s%s%s\n", - dev->name, ev, - HFA384X_INW(HFA384X_INTEN_OFF), - !local->hw_ready ? " (!hw_ready)" : "", - local->hw_resetting ? - " (hw_resetting)" : "", - !local->dev_enabled ? - " (!dev_enabled)" : ""); - } - HFA384X_OUTW(ev, HFA384X_EVACK_OFF); - return IRQ_HANDLED; - } - - if (ev & HFA384X_EV_TICK) { - prism2_ev_tick(dev); - HFA384X_OUTW(HFA384X_EV_TICK, HFA384X_EVACK_OFF); - } - - if (ev & HFA384X_EV_ALLOC) { - prism2_alloc_ev(dev); - HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF); - } - - /* Reading data from the card is quite time consuming, so do it - * in tasklets. TX, TXEXC, RX, and INFO events will be ACKed - * and unmasked after needed data has been read completely. */ - if (ev & HFA384X_BAP0_EVENTS) { - hfa384x_events_no_bap0(dev); - tasklet_schedule(&local->bap_tasklet); - } - -#ifndef final_version - if (ev & HFA384X_EV_WTERR) { - PDEBUG(DEBUG_EXTRA, "%s: WTERR event\n", dev->name); - HFA384X_OUTW(HFA384X_EV_WTERR, HFA384X_EVACK_OFF); - } -#endif /* final_version */ - - if (ev & HFA384X_EV_INFDROP) { - prism2_infdrop(dev); - HFA384X_OUTW(HFA384X_EV_INFDROP, HFA384X_EVACK_OFF); - } - - next_event: - events++; - if (events >= PRISM2_MAX_INTERRUPT_EVENTS) { - PDEBUG(DEBUG_EXTRA, "prism2_interrupt: >%d events " - "(EvStat=0x%04x)\n", - PRISM2_MAX_INTERRUPT_EVENTS, - HFA384X_INW(HFA384X_EVSTAT_OFF)); - break; - } - } - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 1); - return IRQ_RETVAL(events); -} - - -static void prism2_check_sta_fw_version(local_info_t *local) -{ - struct hfa384x_comp_ident comp; - int id, variant, major, minor; - - if (hfa384x_get_rid(local->dev, HFA384X_RID_STAID, - &comp, sizeof(comp), 1) < 0) - return; - - local->fw_ap = 0; - id = le16_to_cpu(comp.id); - if (id != HFA384X_COMP_ID_STA) { - if (id == HFA384X_COMP_ID_FW_AP) - local->fw_ap = 1; - return; - } - - major = __le16_to_cpu(comp.major); - minor = __le16_to_cpu(comp.minor); - variant = __le16_to_cpu(comp.variant); - local->sta_fw_ver = PRISM2_FW_VER(major, minor, variant); - - /* Station firmware versions before 1.4.x seem to have a bug in - * firmware-based WEP encryption when using Host AP mode, so use - * host_encrypt as a default for them. Firmware version 1.4.9 is the - * first one that has been seen to produce correct encryption, but the - * bug might be fixed before that (although, at least 1.4.2 is broken). - */ - local->fw_encrypt_ok = local->sta_fw_ver >= PRISM2_FW_VER(1,4,9); - - if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt && - !local->fw_encrypt_ok) { - printk(KERN_DEBUG "%s: defaulting to host-based encryption as " - "a workaround for firmware bug in Host AP mode WEP\n", - local->dev->name); - local->host_encrypt = 1; - } - - /* IEEE 802.11 standard compliant WDS frames (4 addresses) were broken - * in station firmware versions before 1.5.x. With these versions, the - * driver uses a workaround with bogus frame format (4th address after - * the payload). This is not compatible with other AP devices. Since - * the firmware bug is fixed in the latest station firmware versions, - * automatically enable standard compliant mode for cards using station - * firmware version 1.5.0 or newer. */ - if (local->sta_fw_ver >= PRISM2_FW_VER(1,5,0)) - local->wds_type |= HOSTAP_WDS_STANDARD_FRAME; - else { - printk(KERN_DEBUG "%s: defaulting to bogus WDS frame as a " - "workaround for firmware bug in Host AP mode WDS\n", - local->dev->name); - } - - hostap_check_sta_fw_version(local->ap, local->sta_fw_ver); -} - - -static void hostap_passive_scan(unsigned long data) -{ - local_info_t *local = (local_info_t *) data; - struct net_device *dev = local->dev; - u16 chan; - - if (local->passive_scan_interval <= 0) - return; - - if (local->passive_scan_state == PASSIVE_SCAN_LISTEN) { - int max_tries = 16; - - /* Even though host system does not really know when the WLAN - * MAC is sending frames, try to avoid changing channels for - * passive scanning when a host-generated frame is being - * transmitted */ - if (test_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { - printk(KERN_DEBUG "%s: passive scan detected pending " - "TX - delaying\n", dev->name); - local->passive_scan_timer.expires = jiffies + HZ / 10; - add_timer(&local->passive_scan_timer); - return; - } - - do { - local->passive_scan_channel++; - if (local->passive_scan_channel > 14) - local->passive_scan_channel = 1; - max_tries--; - } while (!(local->channel_mask & - (1 << (local->passive_scan_channel - 1))) && - max_tries > 0); - - if (max_tries == 0) { - printk(KERN_INFO "%s: no allowed passive scan channels" - " found\n", dev->name); - return; - } - - printk(KERN_DEBUG "%s: passive scan channel %d\n", - dev->name, local->passive_scan_channel); - chan = local->passive_scan_channel; - local->passive_scan_state = PASSIVE_SCAN_WAIT; - local->passive_scan_timer.expires = jiffies + HZ / 10; - } else { - chan = local->channel; - local->passive_scan_state = PASSIVE_SCAN_LISTEN; - local->passive_scan_timer.expires = jiffies + - local->passive_scan_interval * HZ; - } - - if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_TEST | - (HFA384X_TEST_CHANGE_CHANNEL << 8), - chan, NULL, 0)) - printk(KERN_ERR "%s: passive scan channel set %d " - "failed\n", dev->name, chan); - - add_timer(&local->passive_scan_timer); -} - - -/* Called only as a scheduled task when communications quality values should - * be updated. */ -static void handle_comms_qual_update(struct work_struct *work) -{ - local_info_t *local = - container_of(work, local_info_t, comms_qual_update); - prism2_update_comms_qual(local->dev); -} - - -/* Software watchdog - called as a timer. Hardware interrupt (Tick event) is - * used to monitor that local->last_tick_timer is being updated. If not, - * interrupt busy-loop is assumed and driver tries to recover by masking out - * some events. */ -static void hostap_tick_timer(unsigned long data) -{ - static unsigned long last_inquire = 0; - local_info_t *local = (local_info_t *) data; - local->last_tick_timer = jiffies; - - /* Inquire CommTallies every 10 seconds to keep the statistics updated - * more often during low load and when using 32-bit tallies. */ - if ((!last_inquire || time_after(jiffies, last_inquire + 10 * HZ)) && - !local->hw_downloading && local->hw_ready && - !local->hw_resetting && local->dev_enabled) { - hfa384x_cmd_callback(local->dev, HFA384X_CMDCODE_INQUIRE, - HFA384X_INFO_COMMTALLIES, NULL, 0); - last_inquire = jiffies; - } - - if ((local->last_comms_qual_update == 0 || - time_after(jiffies, local->last_comms_qual_update + 10 * HZ)) && - (local->iw_mode == IW_MODE_INFRA || - local->iw_mode == IW_MODE_ADHOC)) { - schedule_work(&local->comms_qual_update); - } - - local->tick_timer.expires = jiffies + 2 * HZ; - add_timer(&local->tick_timer); -} - - -#ifndef PRISM2_NO_PROCFS_DEBUG -static int prism2_registers_proc_show(struct seq_file *m, void *v) -{ - local_info_t *local = m->private; - -#define SHOW_REG(n) \ - seq_printf(m, #n "=%04x\n", hfa384x_read_reg(local->dev, HFA384X_##n##_OFF)) - - SHOW_REG(CMD); - SHOW_REG(PARAM0); - SHOW_REG(PARAM1); - SHOW_REG(PARAM2); - SHOW_REG(STATUS); - SHOW_REG(RESP0); - SHOW_REG(RESP1); - SHOW_REG(RESP2); - SHOW_REG(INFOFID); - SHOW_REG(CONTROL); - SHOW_REG(SELECT0); - SHOW_REG(SELECT1); - SHOW_REG(OFFSET0); - SHOW_REG(OFFSET1); - SHOW_REG(RXFID); - SHOW_REG(ALLOCFID); - SHOW_REG(TXCOMPLFID); - SHOW_REG(SWSUPPORT0); - SHOW_REG(SWSUPPORT1); - SHOW_REG(SWSUPPORT2); - SHOW_REG(EVSTAT); - SHOW_REG(INTEN); - SHOW_REG(EVACK); - /* Do not read data registers, because they change the state of the - * MAC (offset += 2) */ - /* SHOW_REG(DATA0); */ - /* SHOW_REG(DATA1); */ - SHOW_REG(AUXPAGE); - SHOW_REG(AUXOFFSET); - /* SHOW_REG(AUXDATA); */ -#ifdef PRISM2_PCI - SHOW_REG(PCICOR); - SHOW_REG(PCIHCR); - SHOW_REG(PCI_M0_ADDRH); - SHOW_REG(PCI_M0_ADDRL); - SHOW_REG(PCI_M0_LEN); - SHOW_REG(PCI_M0_CTL); - SHOW_REG(PCI_STATUS); - SHOW_REG(PCI_M1_ADDRH); - SHOW_REG(PCI_M1_ADDRL); - SHOW_REG(PCI_M1_LEN); - SHOW_REG(PCI_M1_CTL); -#endif /* PRISM2_PCI */ - - return 0; -} - -static int prism2_registers_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, prism2_registers_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations prism2_registers_proc_fops = { - .open = prism2_registers_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -#endif /* PRISM2_NO_PROCFS_DEBUG */ - - -struct set_tim_data { - struct list_head list; - int aid; - int set; -}; - -static int prism2_set_tim(struct net_device *dev, int aid, int set) -{ - struct list_head *ptr; - struct set_tim_data *new_entry; - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - new_entry = kzalloc(sizeof(*new_entry), GFP_ATOMIC); - if (new_entry == NULL) - return -ENOMEM; - - new_entry->aid = aid; - new_entry->set = set; - - spin_lock_bh(&local->set_tim_lock); - list_for_each(ptr, &local->set_tim_list) { - struct set_tim_data *entry = - list_entry(ptr, struct set_tim_data, list); - if (entry->aid == aid) { - PDEBUG(DEBUG_PS2, "%s: prism2_set_tim: aid=%d " - "set=%d ==> %d\n", - local->dev->name, aid, entry->set, set); - entry->set = set; - kfree(new_entry); - new_entry = NULL; - break; - } - } - if (new_entry) - list_add_tail(&new_entry->list, &local->set_tim_list); - spin_unlock_bh(&local->set_tim_lock); - - schedule_work(&local->set_tim_queue); - - return 0; -} - - -static void handle_set_tim_queue(struct work_struct *work) -{ - local_info_t *local = container_of(work, local_info_t, set_tim_queue); - struct set_tim_data *entry; - u16 val; - - for (;;) { - entry = NULL; - spin_lock_bh(&local->set_tim_lock); - if (!list_empty(&local->set_tim_list)) { - entry = list_entry(local->set_tim_list.next, - struct set_tim_data, list); - list_del(&entry->list); - } - spin_unlock_bh(&local->set_tim_lock); - if (!entry) - break; - - PDEBUG(DEBUG_PS2, "%s: handle_set_tim_queue: aid=%d set=%d\n", - local->dev->name, entry->aid, entry->set); - - val = entry->aid; - if (entry->set) - val |= 0x8000; - if (hostap_set_word(local->dev, HFA384X_RID_CNFTIMCTRL, val)) { - printk(KERN_DEBUG "%s: set_tim failed (aid=%d " - "set=%d)\n", - local->dev->name, entry->aid, entry->set); - } - - kfree(entry); - } -} - - -static void prism2_clear_set_tim_queue(local_info_t *local) -{ - struct list_head *ptr, *n; - - list_for_each_safe(ptr, n, &local->set_tim_list) { - struct set_tim_data *entry; - entry = list_entry(ptr, struct set_tim_data, list); - list_del(&entry->list); - kfree(entry); - } -} - - -/* - * HostAP uses two layers of net devices, where the inner - * layer gets called all the time from the outer layer. - * This is a natural nesting, which needs a split lock type. - */ -static struct lock_class_key hostap_netdev_xmit_lock_key; -static struct lock_class_key hostap_netdev_addr_lock_key; - -static void prism2_set_lockdep_class_one(struct net_device *dev, - struct netdev_queue *txq, - void *_unused) -{ - lockdep_set_class(&txq->_xmit_lock, - &hostap_netdev_xmit_lock_key); -} - -static void prism2_set_lockdep_class(struct net_device *dev) -{ - lockdep_set_class(&dev->addr_list_lock, - &hostap_netdev_addr_lock_key); - netdev_for_each_tx_queue(dev, prism2_set_lockdep_class_one, NULL); -} - -static struct net_device * -prism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx, - struct device *sdev) -{ - struct net_device *dev; - struct hostap_interface *iface; - struct local_info *local; - int len, i, ret; - - if (funcs == NULL) - return NULL; - - len = strlen(dev_template); - if (len >= IFNAMSIZ || strstr(dev_template, "%d") == NULL) { - printk(KERN_WARNING "hostap: Invalid dev_template='%s'\n", - dev_template); - return NULL; - } - - len = sizeof(struct hostap_interface) + - 3 + sizeof(struct local_info) + - 3 + sizeof(struct ap_data); - - dev = alloc_etherdev(len); - if (dev == NULL) - return NULL; - - iface = netdev_priv(dev); - local = (struct local_info *) ((((long) (iface + 1)) + 3) & ~3); - local->ap = (struct ap_data *) ((((long) (local + 1)) + 3) & ~3); - local->dev = iface->dev = dev; - iface->local = local; - iface->type = HOSTAP_INTERFACE_MASTER; - INIT_LIST_HEAD(&local->hostap_interfaces); - - local->hw_module = THIS_MODULE; - -#ifdef PRISM2_IO_DEBUG - local->io_debug_enabled = 1; -#endif /* PRISM2_IO_DEBUG */ - - local->func = funcs; - local->func->cmd = hfa384x_cmd; - local->func->read_regs = hfa384x_read_regs; - local->func->get_rid = hfa384x_get_rid; - local->func->set_rid = hfa384x_set_rid; - local->func->hw_enable = prism2_hw_enable; - local->func->hw_config = prism2_hw_config; - local->func->hw_reset = prism2_hw_reset; - local->func->hw_shutdown = prism2_hw_shutdown; - local->func->reset_port = prism2_reset_port; - local->func->schedule_reset = prism2_schedule_reset; -#ifdef PRISM2_DOWNLOAD_SUPPORT - local->func->read_aux_fops = &prism2_download_aux_dump_proc_fops; - local->func->download = prism2_download; -#endif /* PRISM2_DOWNLOAD_SUPPORT */ - local->func->tx = prism2_tx_80211; - local->func->set_tim = prism2_set_tim; - local->func->need_tx_headroom = 0; /* no need to add txdesc in - * skb->data (FIX: maybe for DMA bus - * mastering? */ - - local->mtu = mtu; - - rwlock_init(&local->iface_lock); - spin_lock_init(&local->txfidlock); - spin_lock_init(&local->cmdlock); - spin_lock_init(&local->baplock); - spin_lock_init(&local->lock); - spin_lock_init(&local->irq_init_lock); - mutex_init(&local->rid_bap_mtx); - - if (card_idx < 0 || card_idx >= MAX_PARM_DEVICES) - card_idx = 0; - local->card_idx = card_idx; - - len = strlen(essid); - memcpy(local->essid, essid, - len > MAX_SSID_LEN ? MAX_SSID_LEN : len); - local->essid[MAX_SSID_LEN] = '\0'; - i = GET_INT_PARM(iw_mode, card_idx); - if ((i >= IW_MODE_ADHOC && i <= IW_MODE_REPEAT) || - i == IW_MODE_MONITOR) { - local->iw_mode = i; - } else { - printk(KERN_WARNING "prism2: Unknown iw_mode %d; using " - "IW_MODE_MASTER\n", i); - local->iw_mode = IW_MODE_MASTER; - } - local->channel = GET_INT_PARM(channel, card_idx); - local->beacon_int = GET_INT_PARM(beacon_int, card_idx); - local->dtim_period = GET_INT_PARM(dtim_period, card_idx); - local->wds_max_connections = 16; - local->tx_control = HFA384X_TX_CTRL_FLAGS; - local->manual_retry_count = -1; - local->rts_threshold = 2347; - local->fragm_threshold = 2346; - local->rssi_to_dBm = 100; /* default; to be overriden by - * cnfDbmAdjust, if available */ - local->auth_algs = PRISM2_AUTH_OPEN | PRISM2_AUTH_SHARED_KEY; - local->sram_type = -1; - local->scan_channel_mask = 0xffff; - local->monitor_type = PRISM2_MONITOR_RADIOTAP; - - /* Initialize task queue structures */ - INIT_WORK(&local->reset_queue, handle_reset_queue); - INIT_WORK(&local->set_multicast_list_queue, - hostap_set_multicast_list_queue); - - INIT_WORK(&local->set_tim_queue, handle_set_tim_queue); - INIT_LIST_HEAD(&local->set_tim_list); - spin_lock_init(&local->set_tim_lock); - - INIT_WORK(&local->comms_qual_update, handle_comms_qual_update); - - /* Initialize tasklets for handling hardware IRQ related operations - * outside hw IRQ handler */ -#define HOSTAP_TASKLET_INIT(q, f, d) \ -do { memset((q), 0, sizeof(*(q))); (q)->func = (f); (q)->data = (d); } \ -while (0) - HOSTAP_TASKLET_INIT(&local->bap_tasklet, hostap_bap_tasklet, - (unsigned long) local); - - HOSTAP_TASKLET_INIT(&local->info_tasklet, hostap_info_tasklet, - (unsigned long) local); - hostap_info_init(local); - - HOSTAP_TASKLET_INIT(&local->rx_tasklet, - hostap_rx_tasklet, (unsigned long) local); - skb_queue_head_init(&local->rx_list); - - HOSTAP_TASKLET_INIT(&local->sta_tx_exc_tasklet, - hostap_sta_tx_exc_tasklet, (unsigned long) local); - skb_queue_head_init(&local->sta_tx_exc_list); - - INIT_LIST_HEAD(&local->cmd_queue); - init_waitqueue_head(&local->hostscan_wq); - - lib80211_crypt_info_init(&local->crypt_info, dev->name, &local->lock); - - init_timer(&local->passive_scan_timer); - local->passive_scan_timer.data = (unsigned long) local; - local->passive_scan_timer.function = hostap_passive_scan; - - init_timer(&local->tick_timer); - local->tick_timer.data = (unsigned long) local; - local->tick_timer.function = hostap_tick_timer; - local->tick_timer.expires = jiffies + 2 * HZ; - add_timer(&local->tick_timer); - - INIT_LIST_HEAD(&local->bss_list); - - hostap_setup_dev(dev, local, HOSTAP_INTERFACE_MASTER); - - dev->type = ARPHRD_IEEE80211; - dev->header_ops = &hostap_80211_ops; - - rtnl_lock(); - ret = dev_alloc_name(dev, "wifi%d"); - SET_NETDEV_DEV(dev, sdev); - if (ret >= 0) - ret = register_netdevice(dev); - - prism2_set_lockdep_class(dev); - rtnl_unlock(); - if (ret < 0) { - printk(KERN_WARNING "%s: register netdevice failed!\n", - dev_info); - goto fail; - } - printk(KERN_INFO "%s: Registered netdevice %s\n", dev_info, dev->name); - - hostap_init_data(local); - return dev; - - fail: - free_netdev(dev); - return NULL; -} - - -static int hostap_hw_ready(struct net_device *dev) -{ - struct hostap_interface *iface; - struct local_info *local; - - iface = netdev_priv(dev); - local = iface->local; - local->ddev = hostap_add_interface(local, HOSTAP_INTERFACE_MAIN, 0, - "", dev_template); - - if (local->ddev) { - if (local->iw_mode == IW_MODE_INFRA || - local->iw_mode == IW_MODE_ADHOC) { - netif_carrier_off(local->dev); - netif_carrier_off(local->ddev); - } - hostap_init_proc(local); -#ifndef PRISM2_NO_PROCFS_DEBUG - proc_create_data("registers", 0, local->proc, - &prism2_registers_proc_fops, local); -#endif /* PRISM2_NO_PROCFS_DEBUG */ - hostap_init_ap_proc(local); - return 0; - } - - return -1; -} - - -static void prism2_free_local_data(struct net_device *dev) -{ - struct hostap_tx_callback_info *tx_cb, *tx_cb_prev; - int i; - struct hostap_interface *iface; - struct local_info *local; - struct list_head *ptr, *n; - - if (dev == NULL) - return; - - iface = netdev_priv(dev); - local = iface->local; - - /* Unregister all netdevs before freeing local data. */ - list_for_each_safe(ptr, n, &local->hostap_interfaces) { - iface = list_entry(ptr, struct hostap_interface, list); - if (iface->type == HOSTAP_INTERFACE_MASTER) { - /* special handling for this interface below */ - continue; - } - hostap_remove_interface(iface->dev, 0, 1); - } - - unregister_netdev(local->dev); - - flush_work(&local->reset_queue); - flush_work(&local->set_multicast_list_queue); - flush_work(&local->set_tim_queue); -#ifndef PRISM2_NO_STATION_MODES - flush_work(&local->info_queue); -#endif - flush_work(&local->comms_qual_update); - - lib80211_crypt_info_free(&local->crypt_info); - - if (timer_pending(&local->passive_scan_timer)) - del_timer(&local->passive_scan_timer); - - if (timer_pending(&local->tick_timer)) - del_timer(&local->tick_timer); - - prism2_clear_cmd_queue(local); - - skb_queue_purge(&local->info_list); - skb_queue_purge(&local->rx_list); - skb_queue_purge(&local->sta_tx_exc_list); - - if (local->dev_enabled) - prism2_callback(local, PRISM2_CALLBACK_DISABLE); - - if (local->ap != NULL) - hostap_free_data(local->ap); - -#ifndef PRISM2_NO_PROCFS_DEBUG - if (local->proc != NULL) - remove_proc_entry("registers", local->proc); -#endif /* PRISM2_NO_PROCFS_DEBUG */ - hostap_remove_proc(local); - - tx_cb = local->tx_callback; - while (tx_cb != NULL) { - tx_cb_prev = tx_cb; - tx_cb = tx_cb->next; - kfree(tx_cb_prev); - } - - hostap_set_hostapd(local, 0, 0); - hostap_set_hostapd_sta(local, 0, 0); - - for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) { - if (local->frag_cache[i].skb != NULL) - dev_kfree_skb(local->frag_cache[i].skb); - } - -#ifdef PRISM2_DOWNLOAD_SUPPORT - prism2_download_free_data(local->dl_pri); - prism2_download_free_data(local->dl_sec); -#endif /* PRISM2_DOWNLOAD_SUPPORT */ - - prism2_clear_set_tim_queue(local); - - list_for_each_safe(ptr, n, &local->bss_list) { - struct hostap_bss_info *bss = - list_entry(ptr, struct hostap_bss_info, list); - kfree(bss); - } - - kfree(local->pda); - kfree(local->last_scan_results); - kfree(local->generic_elem); - - free_netdev(local->dev); -} - - -#if (defined(PRISM2_PCI) && defined(CONFIG_PM)) || defined(PRISM2_PCCARD) -static void prism2_suspend(struct net_device *dev) -{ - struct hostap_interface *iface; - struct local_info *local; - union iwreq_data wrqu; - - iface = netdev_priv(dev); - local = iface->local; - - /* Send disconnect event, e.g., to trigger reassociation after resume - * if wpa_supplicant is used. */ - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); - - /* Disable hardware and firmware */ - prism2_hw_shutdown(dev, 0); -} -#endif /* (PRISM2_PCI && CONFIG_PM) || PRISM2_PCCARD */ - - -/* These might at some point be compiled separately and used as separate - * kernel modules or linked into one */ -#ifdef PRISM2_DOWNLOAD_SUPPORT -#include "hostap_download.c" -#endif /* PRISM2_DOWNLOAD_SUPPORT */ - -#ifdef PRISM2_CALLBACK -/* External hostap_callback.c file can be used to, e.g., blink activity led. - * This can use platform specific code and must define prism2_callback() - * function (if PRISM2_CALLBACK is not defined, these function calls are not - * used. */ -#include "hostap_callback.c" -#endif /* PRISM2_CALLBACK */ diff --git a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/hostap/hostap_info.c deleted file mode 100644 index 7635ac4f6679..000000000000 --- a/drivers/net/wireless/hostap/hostap_info.c +++ /dev/null @@ -1,507 +0,0 @@ -/* Host AP driver Info Frame processing (part of hostap.o module) */ - -#include -#include -#include -#include -#include -#include "hostap_wlan.h" -#include "hostap.h" -#include "hostap_ap.h" - -/* Called only as a tasklet (software IRQ) */ -static void prism2_info_commtallies16(local_info_t *local, unsigned char *buf, - int left) -{ - struct hfa384x_comm_tallies *tallies; - - if (left < sizeof(struct hfa384x_comm_tallies)) { - printk(KERN_DEBUG "%s: too short (len=%d) commtallies " - "info frame\n", local->dev->name, left); - return; - } - - tallies = (struct hfa384x_comm_tallies *) buf; -#define ADD_COMM_TALLIES(name) \ -local->comm_tallies.name += le16_to_cpu(tallies->name) - ADD_COMM_TALLIES(tx_unicast_frames); - ADD_COMM_TALLIES(tx_multicast_frames); - ADD_COMM_TALLIES(tx_fragments); - ADD_COMM_TALLIES(tx_unicast_octets); - ADD_COMM_TALLIES(tx_multicast_octets); - ADD_COMM_TALLIES(tx_deferred_transmissions); - ADD_COMM_TALLIES(tx_single_retry_frames); - ADD_COMM_TALLIES(tx_multiple_retry_frames); - ADD_COMM_TALLIES(tx_retry_limit_exceeded); - ADD_COMM_TALLIES(tx_discards); - ADD_COMM_TALLIES(rx_unicast_frames); - ADD_COMM_TALLIES(rx_multicast_frames); - ADD_COMM_TALLIES(rx_fragments); - ADD_COMM_TALLIES(rx_unicast_octets); - ADD_COMM_TALLIES(rx_multicast_octets); - ADD_COMM_TALLIES(rx_fcs_errors); - ADD_COMM_TALLIES(rx_discards_no_buffer); - ADD_COMM_TALLIES(tx_discards_wrong_sa); - ADD_COMM_TALLIES(rx_discards_wep_undecryptable); - ADD_COMM_TALLIES(rx_message_in_msg_fragments); - ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments); -#undef ADD_COMM_TALLIES -} - - -/* Called only as a tasklet (software IRQ) */ -static void prism2_info_commtallies32(local_info_t *local, unsigned char *buf, - int left) -{ - struct hfa384x_comm_tallies32 *tallies; - - if (left < sizeof(struct hfa384x_comm_tallies32)) { - printk(KERN_DEBUG "%s: too short (len=%d) commtallies32 " - "info frame\n", local->dev->name, left); - return; - } - - tallies = (struct hfa384x_comm_tallies32 *) buf; -#define ADD_COMM_TALLIES(name) \ -local->comm_tallies.name += le32_to_cpu(tallies->name) - ADD_COMM_TALLIES(tx_unicast_frames); - ADD_COMM_TALLIES(tx_multicast_frames); - ADD_COMM_TALLIES(tx_fragments); - ADD_COMM_TALLIES(tx_unicast_octets); - ADD_COMM_TALLIES(tx_multicast_octets); - ADD_COMM_TALLIES(tx_deferred_transmissions); - ADD_COMM_TALLIES(tx_single_retry_frames); - ADD_COMM_TALLIES(tx_multiple_retry_frames); - ADD_COMM_TALLIES(tx_retry_limit_exceeded); - ADD_COMM_TALLIES(tx_discards); - ADD_COMM_TALLIES(rx_unicast_frames); - ADD_COMM_TALLIES(rx_multicast_frames); - ADD_COMM_TALLIES(rx_fragments); - ADD_COMM_TALLIES(rx_unicast_octets); - ADD_COMM_TALLIES(rx_multicast_octets); - ADD_COMM_TALLIES(rx_fcs_errors); - ADD_COMM_TALLIES(rx_discards_no_buffer); - ADD_COMM_TALLIES(tx_discards_wrong_sa); - ADD_COMM_TALLIES(rx_discards_wep_undecryptable); - ADD_COMM_TALLIES(rx_message_in_msg_fragments); - ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments); -#undef ADD_COMM_TALLIES -} - - -/* Called only as a tasklet (software IRQ) */ -static void prism2_info_commtallies(local_info_t *local, unsigned char *buf, - int left) -{ - if (local->tallies32) - prism2_info_commtallies32(local, buf, left); - else - prism2_info_commtallies16(local, buf, left); -} - - -#ifndef PRISM2_NO_STATION_MODES -#ifndef PRISM2_NO_DEBUG -static const char* hfa384x_linkstatus_str(u16 linkstatus) -{ - switch (linkstatus) { - case HFA384X_LINKSTATUS_CONNECTED: - return "Connected"; - case HFA384X_LINKSTATUS_DISCONNECTED: - return "Disconnected"; - case HFA384X_LINKSTATUS_AP_CHANGE: - return "Access point change"; - case HFA384X_LINKSTATUS_AP_OUT_OF_RANGE: - return "Access point out of range"; - case HFA384X_LINKSTATUS_AP_IN_RANGE: - return "Access point in range"; - case HFA384X_LINKSTATUS_ASSOC_FAILED: - return "Association failed"; - default: - return "Unknown"; - } -} -#endif /* PRISM2_NO_DEBUG */ - - -/* Called only as a tasklet (software IRQ) */ -static void prism2_info_linkstatus(local_info_t *local, unsigned char *buf, - int left) -{ - u16 val; - int non_sta_mode; - - /* Alloc new JoinRequests to occur since LinkStatus for the previous - * has been received */ - local->last_join_time = 0; - - if (left != 2) { - printk(KERN_DEBUG "%s: invalid linkstatus info frame " - "length %d\n", local->dev->name, left); - return; - } - - non_sta_mode = local->iw_mode == IW_MODE_MASTER || - local->iw_mode == IW_MODE_REPEAT || - local->iw_mode == IW_MODE_MONITOR; - - val = buf[0] | (buf[1] << 8); - if (!non_sta_mode || val != HFA384X_LINKSTATUS_DISCONNECTED) { - PDEBUG(DEBUG_EXTRA, "%s: LinkStatus=%d (%s)\n", - local->dev->name, val, hfa384x_linkstatus_str(val)); - } - - if (non_sta_mode) { - netif_carrier_on(local->dev); - netif_carrier_on(local->ddev); - return; - } - - /* Get current BSSID later in scheduled task */ - set_bit(PRISM2_INFO_PENDING_LINKSTATUS, &local->pending_info); - local->prev_link_status = val; - schedule_work(&local->info_queue); -} - - -static void prism2_host_roaming(local_info_t *local) -{ - struct hfa384x_join_request req; - struct net_device *dev = local->dev; - struct hfa384x_hostscan_result *selected, *entry; - int i; - unsigned long flags; - - if (local->last_join_time && - time_before(jiffies, local->last_join_time + 10 * HZ)) { - PDEBUG(DEBUG_EXTRA, "%s: last join request has not yet been " - "completed - waiting for it before issuing new one\n", - dev->name); - return; - } - - /* ScanResults are sorted: first ESS results in decreasing signal - * quality then IBSS results in similar order. - * Trivial roaming policy: just select the first entry. - * This could probably be improved by adding hysteresis to limit - * number of handoffs, etc. - * - * Could do periodic RID_SCANREQUEST or Inquire F101 to get new - * ScanResults */ - spin_lock_irqsave(&local->lock, flags); - if (local->last_scan_results == NULL || - local->last_scan_results_count == 0) { - spin_unlock_irqrestore(&local->lock, flags); - PDEBUG(DEBUG_EXTRA, "%s: no scan results for host roaming\n", - dev->name); - return; - } - - selected = &local->last_scan_results[0]; - - if (local->preferred_ap[0] || local->preferred_ap[1] || - local->preferred_ap[2] || local->preferred_ap[3] || - local->preferred_ap[4] || local->preferred_ap[5]) { - /* Try to find preferred AP */ - PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID %pM\n", - dev->name, local->preferred_ap); - for (i = 0; i < local->last_scan_results_count; i++) { - entry = &local->last_scan_results[i]; - if (memcmp(local->preferred_ap, entry->bssid, 6) == 0) - { - PDEBUG(DEBUG_EXTRA, "%s: using preferred AP " - "selection\n", dev->name); - selected = entry; - break; - } - } - } - - memcpy(req.bssid, selected->bssid, ETH_ALEN); - req.channel = selected->chid; - spin_unlock_irqrestore(&local->lock, flags); - - PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=%pM" - " channel=%d\n", - dev->name, req.bssid, le16_to_cpu(req.channel)); - if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req, - sizeof(req))) { - printk(KERN_DEBUG "%s: JoinRequest failed\n", dev->name); - } - local->last_join_time = jiffies; -} - - -static void hostap_report_scan_complete(local_info_t *local) -{ - union iwreq_data wrqu; - - /* Inform user space about new scan results (just empty event, - * SIOCGIWSCAN can be used to fetch data */ - wrqu.data.length = 0; - wrqu.data.flags = 0; - wireless_send_event(local->dev, SIOCGIWSCAN, &wrqu, NULL); - - /* Allow SIOCGIWSCAN handling to occur since we have received - * scanning result */ - local->scan_timestamp = 0; -} - - -/* Called only as a tasklet (software IRQ) */ -static void prism2_info_scanresults(local_info_t *local, unsigned char *buf, - int left) -{ - u16 *pos; - int new_count, i; - unsigned long flags; - struct hfa384x_scan_result *res; - struct hfa384x_hostscan_result *results, *prev; - - if (left < 4) { - printk(KERN_DEBUG "%s: invalid scanresult info frame " - "length %d\n", local->dev->name, left); - return; - } - - pos = (u16 *) buf; - pos++; - pos++; - left -= 4; - - new_count = left / sizeof(struct hfa384x_scan_result); - results = kmalloc(new_count * sizeof(struct hfa384x_hostscan_result), - GFP_ATOMIC); - if (results == NULL) - return; - - /* Convert to hostscan result format. */ - res = (struct hfa384x_scan_result *) pos; - for (i = 0; i < new_count; i++) { - memcpy(&results[i], &res[i], - sizeof(struct hfa384x_scan_result)); - results[i].atim = 0; - } - - spin_lock_irqsave(&local->lock, flags); - local->last_scan_type = PRISM2_SCAN; - prev = local->last_scan_results; - local->last_scan_results = results; - local->last_scan_results_count = new_count; - spin_unlock_irqrestore(&local->lock, flags); - kfree(prev); - - hostap_report_scan_complete(local); - - /* Perform rest of ScanResults handling later in scheduled task */ - set_bit(PRISM2_INFO_PENDING_SCANRESULTS, &local->pending_info); - schedule_work(&local->info_queue); -} - - -/* Called only as a tasklet (software IRQ) */ -static void prism2_info_hostscanresults(local_info_t *local, - unsigned char *buf, int left) -{ - int i, result_size, copy_len, new_count; - struct hfa384x_hostscan_result *results, *prev; - unsigned long flags; - __le16 *pos; - u8 *ptr; - - wake_up_interruptible(&local->hostscan_wq); - - if (left < 4) { - printk(KERN_DEBUG "%s: invalid hostscanresult info frame " - "length %d\n", local->dev->name, left); - return; - } - - pos = (__le16 *) buf; - copy_len = result_size = le16_to_cpu(*pos); - if (result_size == 0) { - printk(KERN_DEBUG "%s: invalid result_size (0) in " - "hostscanresults\n", local->dev->name); - return; - } - if (copy_len > sizeof(struct hfa384x_hostscan_result)) - copy_len = sizeof(struct hfa384x_hostscan_result); - - pos++; - pos++; - left -= 4; - ptr = (u8 *) pos; - - new_count = left / result_size; - results = kcalloc(new_count, sizeof(struct hfa384x_hostscan_result), - GFP_ATOMIC); - if (results == NULL) - return; - - for (i = 0; i < new_count; i++) { - memcpy(&results[i], ptr, copy_len); - ptr += result_size; - left -= result_size; - } - - if (left) { - printk(KERN_DEBUG "%s: short HostScan result entry (%d/%d)\n", - local->dev->name, left, result_size); - } - - spin_lock_irqsave(&local->lock, flags); - local->last_scan_type = PRISM2_HOSTSCAN; - prev = local->last_scan_results; - local->last_scan_results = results; - local->last_scan_results_count = new_count; - spin_unlock_irqrestore(&local->lock, flags); - kfree(prev); - - hostap_report_scan_complete(local); -} -#endif /* PRISM2_NO_STATION_MODES */ - - -/* Called only as a tasklet (software IRQ) */ -void hostap_info_process(local_info_t *local, struct sk_buff *skb) -{ - struct hfa384x_info_frame *info; - unsigned char *buf; - int left; -#ifndef PRISM2_NO_DEBUG - int i; -#endif /* PRISM2_NO_DEBUG */ - - info = (struct hfa384x_info_frame *) skb->data; - buf = skb->data + sizeof(*info); - left = skb->len - sizeof(*info); - - switch (le16_to_cpu(info->type)) { - case HFA384X_INFO_COMMTALLIES: - prism2_info_commtallies(local, buf, left); - break; - -#ifndef PRISM2_NO_STATION_MODES - case HFA384X_INFO_LINKSTATUS: - prism2_info_linkstatus(local, buf, left); - break; - - case HFA384X_INFO_SCANRESULTS: - prism2_info_scanresults(local, buf, left); - break; - - case HFA384X_INFO_HOSTSCANRESULTS: - prism2_info_hostscanresults(local, buf, left); - break; -#endif /* PRISM2_NO_STATION_MODES */ - -#ifndef PRISM2_NO_DEBUG - default: - PDEBUG(DEBUG_EXTRA, "%s: INFO - len=%d type=0x%04x\n", - local->dev->name, le16_to_cpu(info->len), - le16_to_cpu(info->type)); - PDEBUG(DEBUG_EXTRA, "Unknown info frame:"); - for (i = 0; i < (left < 100 ? left : 100); i++) - PDEBUG2(DEBUG_EXTRA, " %02x", buf[i]); - PDEBUG2(DEBUG_EXTRA, "\n"); - break; -#endif /* PRISM2_NO_DEBUG */ - } -} - - -#ifndef PRISM2_NO_STATION_MODES -static void handle_info_queue_linkstatus(local_info_t *local) -{ - int val = local->prev_link_status; - int connected; - union iwreq_data wrqu; - - connected = - val == HFA384X_LINKSTATUS_CONNECTED || - val == HFA384X_LINKSTATUS_AP_CHANGE || - val == HFA384X_LINKSTATUS_AP_IN_RANGE; - - if (local->func->get_rid(local->dev, HFA384X_RID_CURRENTBSSID, - local->bssid, ETH_ALEN, 1) < 0) { - printk(KERN_DEBUG "%s: could not read CURRENTBSSID after " - "LinkStatus event\n", local->dev->name); - } else { - PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID=%pM\n", - local->dev->name, - (unsigned char *) local->bssid); - if (local->wds_type & HOSTAP_WDS_AP_CLIENT) - hostap_add_sta(local->ap, local->bssid); - } - - /* Get BSSID if we have a valid AP address */ - if (connected) { - netif_carrier_on(local->dev); - netif_carrier_on(local->ddev); - memcpy(wrqu.ap_addr.sa_data, local->bssid, ETH_ALEN); - } else { - netif_carrier_off(local->dev); - netif_carrier_off(local->ddev); - eth_zero_addr(wrqu.ap_addr.sa_data); - } - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - - /* - * Filter out sequential disconnect events in order not to cause a - * flood of SIOCGIWAP events that have a race condition with EAPOL - * frames and can confuse wpa_supplicant about the current association - * status. - */ - if (connected || local->prev_linkstatus_connected) - wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); - local->prev_linkstatus_connected = connected; -} - - -static void handle_info_queue_scanresults(local_info_t *local) -{ - if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA) - prism2_host_roaming(local); - - if (local->host_roaming == 2 && local->iw_mode == IW_MODE_INFRA && - !is_zero_ether_addr(local->preferred_ap)) { - /* - * Firmware seems to be getting into odd state in host_roaming - * mode 2 when hostscan is used without join command, so try - * to fix this by re-joining the current AP. This does not - * actually trigger a new association if the current AP is - * still in the scan results. - */ - prism2_host_roaming(local); - } -} - - -/* Called only as scheduled task after receiving info frames (used to avoid - * pending too much time in HW IRQ handler). */ -static void handle_info_queue(struct work_struct *work) -{ - local_info_t *local = container_of(work, local_info_t, info_queue); - - if (test_and_clear_bit(PRISM2_INFO_PENDING_LINKSTATUS, - &local->pending_info)) - handle_info_queue_linkstatus(local); - - if (test_and_clear_bit(PRISM2_INFO_PENDING_SCANRESULTS, - &local->pending_info)) - handle_info_queue_scanresults(local); -} -#endif /* PRISM2_NO_STATION_MODES */ - - -void hostap_info_init(local_info_t *local) -{ - skb_queue_head_init(&local->info_list); -#ifndef PRISM2_NO_STATION_MODES - INIT_WORK(&local->info_queue, handle_info_queue); -#endif /* PRISM2_NO_STATION_MODES */ -} - - -EXPORT_SYMBOL(hostap_info_init); -EXPORT_SYMBOL(hostap_info_process); diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c deleted file mode 100644 index 3e5fa7872b64..000000000000 --- a/drivers/net/wireless/hostap/hostap_ioctl.c +++ /dev/null @@ -1,4054 +0,0 @@ -/* ioctl() (mostly Linux Wireless Extensions) routines for Host AP driver */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hostap_wlan.h" -#include "hostap.h" -#include "hostap_ap.h" - -static struct iw_statistics *hostap_get_wireless_stats(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - struct iw_statistics *wstats; - - iface = netdev_priv(dev); - local = iface->local; - - /* Why are we doing that ? Jean II */ - if (iface->type != HOSTAP_INTERFACE_MAIN) - return NULL; - - wstats = &local->wstats; - - wstats->status = 0; - wstats->discard.code = - local->comm_tallies.rx_discards_wep_undecryptable; - wstats->discard.misc = - local->comm_tallies.rx_fcs_errors + - local->comm_tallies.rx_discards_no_buffer + - local->comm_tallies.tx_discards_wrong_sa; - - wstats->discard.retries = - local->comm_tallies.tx_retry_limit_exceeded; - wstats->discard.fragment = - local->comm_tallies.rx_message_in_bad_msg_fragments; - - if (local->iw_mode != IW_MODE_MASTER && - local->iw_mode != IW_MODE_REPEAT) { - int update = 1; -#ifdef in_atomic - /* RID reading might sleep and it must not be called in - * interrupt context or while atomic. However, this - * function seems to be called while atomic (at least in Linux - * 2.5.59). Update signal quality values only if in suitable - * context. Otherwise, previous values read from tick timer - * will be used. */ - if (in_atomic()) - update = 0; -#endif /* in_atomic */ - - if (update && prism2_update_comms_qual(dev) == 0) - wstats->qual.updated = IW_QUAL_ALL_UPDATED | - IW_QUAL_DBM; - - wstats->qual.qual = local->comms_qual; - wstats->qual.level = local->avg_signal; - wstats->qual.noise = local->avg_noise; - } else { - wstats->qual.qual = 0; - wstats->qual.level = 0; - wstats->qual.noise = 0; - wstats->qual.updated = IW_QUAL_ALL_INVALID; - } - - return wstats; -} - - -static int prism2_get_datarates(struct net_device *dev, u8 *rates) -{ - struct hostap_interface *iface; - local_info_t *local; - u8 buf[12]; - int len; - u16 val; - - iface = netdev_priv(dev); - local = iface->local; - - len = local->func->get_rid(dev, HFA384X_RID_SUPPORTEDDATARATES, buf, - sizeof(buf), 0); - if (len < 2) - return 0; - - val = le16_to_cpu(*(__le16 *) buf); /* string length */ - - if (len - 2 < val || val > 10) - return 0; - - memcpy(rates, buf + 2, val); - return val; -} - - -static int prism2_get_name(struct net_device *dev, - struct iw_request_info *info, - char *name, char *extra) -{ - u8 rates[10]; - int len, i, over2 = 0; - - len = prism2_get_datarates(dev, rates); - - for (i = 0; i < len; i++) { - if (rates[i] == 0x0b || rates[i] == 0x16) { - over2 = 1; - break; - } - } - - strcpy(name, over2 ? "IEEE 802.11b" : "IEEE 802.11-DS"); - - return 0; -} - - -static int prism2_ioctl_siwencode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *keybuf) -{ - struct hostap_interface *iface; - local_info_t *local; - int i; - struct lib80211_crypt_data **crypt; - - iface = netdev_priv(dev); - local = iface->local; - - i = erq->flags & IW_ENCODE_INDEX; - if (i < 1 || i > 4) - i = local->crypt_info.tx_keyidx; - else - i--; - if (i < 0 || i >= WEP_KEYS) - return -EINVAL; - - crypt = &local->crypt_info.crypt[i]; - - if (erq->flags & IW_ENCODE_DISABLED) { - if (*crypt) - lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); - goto done; - } - - if (*crypt != NULL && (*crypt)->ops != NULL && - strcmp((*crypt)->ops->name, "WEP") != 0) { - /* changing to use WEP; deinit previously used algorithm */ - lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); - } - - if (*crypt == NULL) { - struct lib80211_crypt_data *new_crypt; - - /* take WEP into use */ - new_crypt = kzalloc(sizeof(struct lib80211_crypt_data), - GFP_KERNEL); - if (new_crypt == NULL) - return -ENOMEM; - new_crypt->ops = lib80211_get_crypto_ops("WEP"); - if (!new_crypt->ops) { - request_module("lib80211_crypt_wep"); - new_crypt->ops = lib80211_get_crypto_ops("WEP"); - } - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) - new_crypt->priv = new_crypt->ops->init(i); - if (!new_crypt->ops || !new_crypt->priv) { - kfree(new_crypt); - new_crypt = NULL; - - printk(KERN_WARNING "%s: could not initialize WEP: " - "load module hostap_crypt_wep.o\n", - dev->name); - return -EOPNOTSUPP; - } - *crypt = new_crypt; - } - - if (erq->length > 0) { - int len = erq->length <= 5 ? 5 : 13; - int first = 1, j; - if (len > erq->length) - memset(keybuf + erq->length, 0, len - erq->length); - (*crypt)->ops->set_key(keybuf, len, NULL, (*crypt)->priv); - for (j = 0; j < WEP_KEYS; j++) { - if (j != i && local->crypt_info.crypt[j]) { - first = 0; - break; - } - } - if (first) - local->crypt_info.tx_keyidx = i; - } else { - /* No key data - just set the default TX key index */ - local->crypt_info.tx_keyidx = i; - } - - done: - local->open_wep = erq->flags & IW_ENCODE_OPEN; - - if (hostap_set_encryption(local)) { - printk(KERN_DEBUG "%s: set_encryption failed\n", dev->name); - return -EINVAL; - } - - /* Do not reset port0 if card is in Managed mode since resetting will - * generate new IEEE 802.11 authentication which may end up in looping - * with IEEE 802.1X. Prism2 documentation seem to require port reset - * after WEP configuration. However, keys are apparently changed at - * least in Managed mode. */ - if (local->iw_mode != IW_MODE_INFRA && local->func->reset_port(dev)) { - printk(KERN_DEBUG "%s: reset_port failed\n", dev->name); - return -EINVAL; - } - - return 0; -} - - -static int prism2_ioctl_giwencode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *key) -{ - struct hostap_interface *iface; - local_info_t *local; - int i, len; - u16 val; - struct lib80211_crypt_data *crypt; - - iface = netdev_priv(dev); - local = iface->local; - - i = erq->flags & IW_ENCODE_INDEX; - if (i < 1 || i > 4) - i = local->crypt_info.tx_keyidx; - else - i--; - if (i < 0 || i >= WEP_KEYS) - return -EINVAL; - - crypt = local->crypt_info.crypt[i]; - erq->flags = i + 1; - - if (crypt == NULL || crypt->ops == NULL) { - erq->length = 0; - erq->flags |= IW_ENCODE_DISABLED; - return 0; - } - - if (strcmp(crypt->ops->name, "WEP") != 0) { - /* only WEP is supported with wireless extensions, so just - * report that encryption is used */ - erq->length = 0; - erq->flags |= IW_ENCODE_ENABLED; - return 0; - } - - /* Reads from HFA384X_RID_CNFDEFAULTKEY* return bogus values, so show - * the keys from driver buffer */ - len = crypt->ops->get_key(key, WEP_KEY_LEN, NULL, crypt->priv); - erq->length = (len >= 0 ? len : 0); - - if (local->func->get_rid(dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, 1) < 0) - { - printk("CNFWEPFLAGS reading failed\n"); - return -EOPNOTSUPP; - } - le16_to_cpus(&val); - if (val & HFA384X_WEPFLAGS_PRIVACYINVOKED) - erq->flags |= IW_ENCODE_ENABLED; - else - erq->flags |= IW_ENCODE_DISABLED; - if (val & HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED) - erq->flags |= IW_ENCODE_RESTRICTED; - else - erq->flags |= IW_ENCODE_OPEN; - - return 0; -} - - -static int hostap_set_rate(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - int ret, basic_rates; - - iface = netdev_priv(dev); - local = iface->local; - - basic_rates = local->basic_rates & local->tx_rate_control; - if (!basic_rates || basic_rates != local->basic_rates) { - printk(KERN_INFO "%s: updating basic rate set automatically " - "to match with the new supported rate set\n", - dev->name); - if (!basic_rates) - basic_rates = local->tx_rate_control; - - local->basic_rates = basic_rates; - if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, - basic_rates)) - printk(KERN_WARNING "%s: failed to set " - "cnfBasicRates\n", dev->name); - } - - ret = (hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, - local->tx_rate_control) || - hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, - local->tx_rate_control) || - local->func->reset_port(dev)); - - if (ret) { - printk(KERN_WARNING "%s: TXRateControl/cnfSupportedRates " - "setting to 0x%x failed\n", - dev->name, local->tx_rate_control); - } - - /* Update TX rate configuration for all STAs based on new operational - * rate set. */ - hostap_update_rates(local); - - return ret; -} - - -static int prism2_ioctl_siwrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - if (rrq->fixed) { - switch (rrq->value) { - case 11000000: - local->tx_rate_control = HFA384X_RATES_11MBPS; - break; - case 5500000: - local->tx_rate_control = HFA384X_RATES_5MBPS; - break; - case 2000000: - local->tx_rate_control = HFA384X_RATES_2MBPS; - break; - case 1000000: - local->tx_rate_control = HFA384X_RATES_1MBPS; - break; - default: - local->tx_rate_control = HFA384X_RATES_1MBPS | - HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | - HFA384X_RATES_11MBPS; - break; - } - } else { - switch (rrq->value) { - case 11000000: - local->tx_rate_control = HFA384X_RATES_1MBPS | - HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | - HFA384X_RATES_11MBPS; - break; - case 5500000: - local->tx_rate_control = HFA384X_RATES_1MBPS | - HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS; - break; - case 2000000: - local->tx_rate_control = HFA384X_RATES_1MBPS | - HFA384X_RATES_2MBPS; - break; - case 1000000: - local->tx_rate_control = HFA384X_RATES_1MBPS; - break; - default: - local->tx_rate_control = HFA384X_RATES_1MBPS | - HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | - HFA384X_RATES_11MBPS; - break; - } - } - - return hostap_set_rate(dev); -} - - -static int prism2_ioctl_giwrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, char *extra) -{ - u16 val; - struct hostap_interface *iface; - local_info_t *local; - int ret = 0; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->func->get_rid(dev, HFA384X_RID_TXRATECONTROL, &val, 2, 1) < - 0) - return -EINVAL; - - if ((val & 0x1) && (val > 1)) - rrq->fixed = 0; - else - rrq->fixed = 1; - - if (local->iw_mode == IW_MODE_MASTER && local->ap != NULL && - !local->fw_tx_rate_control) { - /* HFA384X_RID_CURRENTTXRATE seems to always be 2 Mbps in - * Host AP mode, so use the recorded TX rate of the last sent - * frame */ - rrq->value = local->ap->last_tx_rate > 0 ? - local->ap->last_tx_rate * 100000 : 11000000; - return 0; - } - - if (local->func->get_rid(dev, HFA384X_RID_CURRENTTXRATE, &val, 2, 1) < - 0) - return -EINVAL; - - switch (val) { - case HFA384X_RATES_1MBPS: - rrq->value = 1000000; - break; - case HFA384X_RATES_2MBPS: - rrq->value = 2000000; - break; - case HFA384X_RATES_5MBPS: - rrq->value = 5500000; - break; - case HFA384X_RATES_11MBPS: - rrq->value = 11000000; - break; - default: - /* should not happen */ - rrq->value = 11000000; - ret = -EINVAL; - break; - } - - return ret; -} - - -static int prism2_ioctl_siwsens(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *sens, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - /* Set the desired AP density */ - if (sens->value < 1 || sens->value > 3) - return -EINVAL; - - if (hostap_set_word(dev, HFA384X_RID_CNFSYSTEMSCALE, sens->value) || - local->func->reset_port(dev)) - return -EINVAL; - - return 0; -} - -static int prism2_ioctl_giwsens(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *sens, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - __le16 val; - - iface = netdev_priv(dev); - local = iface->local; - - /* Get the current AP density */ - if (local->func->get_rid(dev, HFA384X_RID_CNFSYSTEMSCALE, &val, 2, 1) < - 0) - return -EINVAL; - - sens->value = le16_to_cpu(val); - sens->fixed = 1; - - return 0; -} - - -/* Deprecated in new wireless extension API */ -static int prism2_ioctl_giwaplist(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - struct sockaddr *addr; - struct iw_quality *qual; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->iw_mode != IW_MODE_MASTER) { - printk(KERN_DEBUG "SIOCGIWAPLIST is currently only supported " - "in Host AP mode\n"); - data->length = 0; - return -EOPNOTSUPP; - } - - addr = kmalloc(sizeof(struct sockaddr) * IW_MAX_AP, GFP_KERNEL); - qual = kmalloc(sizeof(struct iw_quality) * IW_MAX_AP, GFP_KERNEL); - if (addr == NULL || qual == NULL) { - kfree(addr); - kfree(qual); - data->length = 0; - return -ENOMEM; - } - - data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1); - - memcpy(extra, addr, sizeof(struct sockaddr) * data->length); - data->flags = 1; /* has quality information */ - memcpy(extra + sizeof(struct sockaddr) * data->length, qual, - sizeof(struct iw_quality) * data->length); - - kfree(addr); - kfree(qual); - return 0; -} - - -static int prism2_ioctl_siwrts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - __le16 val; - - iface = netdev_priv(dev); - local = iface->local; - - if (rts->disabled) - val = cpu_to_le16(2347); - else if (rts->value < 0 || rts->value > 2347) - return -EINVAL; - else - val = cpu_to_le16(rts->value); - - if (local->func->set_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2) || - local->func->reset_port(dev)) - return -EINVAL; - - local->rts_threshold = rts->value; - - return 0; -} - -static int prism2_ioctl_giwrts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - __le16 val; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->func->get_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2, 1) < - 0) - return -EINVAL; - - rts->value = le16_to_cpu(val); - rts->disabled = (rts->value == 2347); - rts->fixed = 1; - - return 0; -} - - -static int prism2_ioctl_siwfrag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - __le16 val; - - iface = netdev_priv(dev); - local = iface->local; - - if (rts->disabled) - val = cpu_to_le16(2346); - else if (rts->value < 256 || rts->value > 2346) - return -EINVAL; - else - val = cpu_to_le16(rts->value & ~0x1); /* even numbers only */ - - local->fragm_threshold = rts->value & ~0x1; - if (local->func->set_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, &val, - 2) - || local->func->reset_port(dev)) - return -EINVAL; - - return 0; -} - -static int prism2_ioctl_giwfrag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - __le16 val; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->func->get_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, - &val, 2, 1) < 0) - return -EINVAL; - - rts->value = le16_to_cpu(val); - rts->disabled = (rts->value == 2346); - rts->fixed = 1; - - return 0; -} - - -#ifndef PRISM2_NO_STATION_MODES -static int hostap_join_ap(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - struct hfa384x_join_request req; - unsigned long flags; - int i; - struct hfa384x_hostscan_result *entry; - - iface = netdev_priv(dev); - local = iface->local; - - memcpy(req.bssid, local->preferred_ap, ETH_ALEN); - req.channel = 0; - - spin_lock_irqsave(&local->lock, flags); - for (i = 0; i < local->last_scan_results_count; i++) { - if (!local->last_scan_results) - break; - entry = &local->last_scan_results[i]; - if (ether_addr_equal(local->preferred_ap, entry->bssid)) { - req.channel = entry->chid; - break; - } - } - spin_unlock_irqrestore(&local->lock, flags); - - if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req, - sizeof(req))) { - printk(KERN_DEBUG "%s: JoinRequest %pM failed\n", - dev->name, local->preferred_ap); - return -1; - } - - printk(KERN_DEBUG "%s: Trying to join BSSID %pM\n", - dev->name, local->preferred_ap); - - return 0; -} -#endif /* PRISM2_NO_STATION_MODES */ - - -static int prism2_ioctl_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ -#ifdef PRISM2_NO_STATION_MODES - return -EOPNOTSUPP; -#else /* PRISM2_NO_STATION_MODES */ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - memcpy(local->preferred_ap, &ap_addr->sa_data, ETH_ALEN); - - if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA) { - struct hfa384x_scan_request scan_req; - memset(&scan_req, 0, sizeof(scan_req)); - scan_req.channel_list = cpu_to_le16(0x3fff); - scan_req.txrate = cpu_to_le16(HFA384X_RATES_1MBPS); - if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, - &scan_req, sizeof(scan_req))) { - printk(KERN_DEBUG "%s: ScanResults request failed - " - "preferred AP delayed to next unsolicited " - "scan\n", dev->name); - } - } else if (local->host_roaming == 2 && - local->iw_mode == IW_MODE_INFRA) { - if (hostap_join_ap(dev)) - return -EINVAL; - } else { - printk(KERN_DEBUG "%s: Preferred AP (SIOCSIWAP) is used only " - "in Managed mode when host_roaming is enabled\n", - dev->name); - } - - return 0; -#endif /* PRISM2_NO_STATION_MODES */ -} - -static int prism2_ioctl_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - ap_addr->sa_family = ARPHRD_ETHER; - switch (iface->type) { - case HOSTAP_INTERFACE_AP: - memcpy(&ap_addr->sa_data, dev->dev_addr, ETH_ALEN); - break; - case HOSTAP_INTERFACE_STA: - memcpy(&ap_addr->sa_data, local->assoc_ap_addr, ETH_ALEN); - break; - case HOSTAP_INTERFACE_WDS: - memcpy(&ap_addr->sa_data, iface->u.wds.remote_addr, ETH_ALEN); - break; - default: - if (local->func->get_rid(dev, HFA384X_RID_CURRENTBSSID, - &ap_addr->sa_data, ETH_ALEN, 1) < 0) - return -EOPNOTSUPP; - - /* local->bssid is also updated in LinkStatus handler when in - * station mode */ - memcpy(local->bssid, &ap_addr->sa_data, ETH_ALEN); - break; - } - - return 0; -} - - -static int prism2_ioctl_siwnickn(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *nickname) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - memset(local->name, 0, sizeof(local->name)); - memcpy(local->name, nickname, data->length); - local->name_set = 1; - - if (hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, local->name) || - local->func->reset_port(dev)) - return -EINVAL; - - return 0; -} - -static int prism2_ioctl_giwnickn(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *nickname) -{ - struct hostap_interface *iface; - local_info_t *local; - int len; - char name[MAX_NAME_LEN + 3]; - u16 val; - - iface = netdev_priv(dev); - local = iface->local; - - len = local->func->get_rid(dev, HFA384X_RID_CNFOWNNAME, - &name, MAX_NAME_LEN + 2, 0); - val = le16_to_cpu(*(__le16 *) name); - if (len > MAX_NAME_LEN + 2 || len < 0 || val > MAX_NAME_LEN) - return -EOPNOTSUPP; - - name[val + 2] = '\0'; - data->length = val + 1; - memcpy(nickname, name + 2, val + 1); - - return 0; -} - - -static int prism2_ioctl_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - /* freq => chan. */ - if (freq->e == 1 && - freq->m / 100000 >= freq_list[0] && - freq->m / 100000 <= freq_list[FREQ_COUNT - 1]) { - int ch; - int fr = freq->m / 100000; - for (ch = 0; ch < FREQ_COUNT; ch++) { - if (fr == freq_list[ch]) { - freq->e = 0; - freq->m = ch + 1; - break; - } - } - } - - if (freq->e != 0 || freq->m < 1 || freq->m > FREQ_COUNT || - !(local->channel_mask & (1 << (freq->m - 1)))) - return -EINVAL; - - local->channel = freq->m; /* channel is used in prism2_setup_rids() */ - if (hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel) || - local->func->reset_port(dev)) - return -EINVAL; - - return 0; -} - -static int prism2_ioctl_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - u16 val; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->func->get_rid(dev, HFA384X_RID_CURRENTCHANNEL, &val, 2, 1) < - 0) - return -EINVAL; - - le16_to_cpus(&val); - if (val < 1 || val > FREQ_COUNT) - return -EINVAL; - - freq->m = freq_list[val - 1] * 100000; - freq->e = 1; - - return 0; -} - - -static void hostap_monitor_set_type(local_info_t *local) -{ - struct net_device *dev = local->ddev; - - if (dev == NULL) - return; - - if (local->monitor_type == PRISM2_MONITOR_PRISM || - local->monitor_type == PRISM2_MONITOR_CAPHDR) { - dev->type = ARPHRD_IEEE80211_PRISM; - } else if (local->monitor_type == PRISM2_MONITOR_RADIOTAP) { - dev->type = ARPHRD_IEEE80211_RADIOTAP; - } else { - dev->type = ARPHRD_IEEE80211; - } -} - - -static int prism2_ioctl_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - if (iface->type == HOSTAP_INTERFACE_WDS) - return -EOPNOTSUPP; - - if (data->flags == 0) - ssid[0] = '\0'; /* ANY */ - - if (local->iw_mode == IW_MODE_MASTER && ssid[0] == '\0') { - /* Setting SSID to empty string seems to kill the card in - * Host AP mode */ - printk(KERN_DEBUG "%s: Host AP mode does not support " - "'Any' essid\n", dev->name); - return -EINVAL; - } - - memcpy(local->essid, ssid, data->length); - local->essid[data->length] = '\0'; - - if ((!local->fw_ap && - hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, local->essid)) - || hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, local->essid) || - local->func->reset_port(dev)) - return -EINVAL; - - return 0; -} - -static int prism2_ioctl_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *essid) -{ - struct hostap_interface *iface; - local_info_t *local; - u16 val; - - iface = netdev_priv(dev); - local = iface->local; - - if (iface->type == HOSTAP_INTERFACE_WDS) - return -EOPNOTSUPP; - - data->flags = 1; /* active */ - if (local->iw_mode == IW_MODE_MASTER) { - data->length = strlen(local->essid); - memcpy(essid, local->essid, IW_ESSID_MAX_SIZE); - } else { - int len; - char ssid[MAX_SSID_LEN + 2]; - memset(ssid, 0, sizeof(ssid)); - len = local->func->get_rid(dev, HFA384X_RID_CURRENTSSID, - &ssid, MAX_SSID_LEN + 2, 0); - val = le16_to_cpu(*(__le16 *) ssid); - if (len > MAX_SSID_LEN + 2 || len < 0 || val > MAX_SSID_LEN) { - return -EOPNOTSUPP; - } - data->length = val; - memcpy(essid, ssid + 2, IW_ESSID_MAX_SIZE); - } - - return 0; -} - - -static int prism2_ioctl_giwrange(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - struct iw_range *range = (struct iw_range *) extra; - u8 rates[10]; - u16 val; - int i, len, over2; - - iface = netdev_priv(dev); - local = iface->local; - - data->length = sizeof(struct iw_range); - memset(range, 0, sizeof(struct iw_range)); - - /* TODO: could fill num_txpower and txpower array with - * something; however, there are 128 different values.. */ - - range->txpower_capa = IW_TXPOW_DBM; - - if (local->iw_mode == IW_MODE_INFRA || local->iw_mode == IW_MODE_ADHOC) - { - range->min_pmp = 1 * 1024; - range->max_pmp = 65535 * 1024; - range->min_pmt = 1 * 1024; - range->max_pmt = 1000 * 1024; - range->pmp_flags = IW_POWER_PERIOD; - range->pmt_flags = IW_POWER_TIMEOUT; - range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | - IW_POWER_UNICAST_R | IW_POWER_ALL_R; - } - - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 18; - - range->retry_capa = IW_RETRY_LIMIT; - range->retry_flags = IW_RETRY_LIMIT; - range->min_retry = 0; - range->max_retry = 255; - - range->num_channels = FREQ_COUNT; - - val = 0; - for (i = 0; i < FREQ_COUNT; i++) { - if (local->channel_mask & (1 << i)) { - range->freq[val].i = i + 1; - range->freq[val].m = freq_list[i] * 100000; - range->freq[val].e = 1; - val++; - } - if (val == IW_MAX_FREQUENCIES) - break; - } - range->num_frequency = val; - - if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) { - range->max_qual.qual = 70; /* what is correct max? This was not - * documented exactly. At least - * 69 has been observed. */ - range->max_qual.level = 0; /* dB */ - range->max_qual.noise = 0; /* dB */ - - /* What would be suitable values for "average/typical" qual? */ - range->avg_qual.qual = 20; - range->avg_qual.level = -60; - range->avg_qual.noise = -95; - } else { - range->max_qual.qual = 92; /* 0 .. 92 */ - range->max_qual.level = 154; /* 27 .. 154 */ - range->max_qual.noise = 154; /* 27 .. 154 */ - } - range->sensitivity = 3; - - range->max_encoding_tokens = WEP_KEYS; - range->num_encoding_sizes = 2; - range->encoding_size[0] = 5; - range->encoding_size[1] = 13; - - over2 = 0; - len = prism2_get_datarates(dev, rates); - range->num_bitrates = 0; - for (i = 0; i < len; i++) { - if (range->num_bitrates < IW_MAX_BITRATES) { - range->bitrate[range->num_bitrates] = - rates[i] * 500000; - range->num_bitrates++; - } - if (rates[i] == 0x0b || rates[i] == 0x16) - over2 = 1; - } - /* estimated maximum TCP throughput values (bps) */ - range->throughput = over2 ? 5500000 : 1500000; - - range->min_rts = 0; - range->max_rts = 2347; - range->min_frag = 256; - range->max_frag = 2346; - - /* Event capability (kernel + driver) */ - range->event_capa[0] = (IW_EVENT_CAPA_K_0 | - IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | - IW_EVENT_CAPA_MASK(SIOCGIWAP) | - IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); - range->event_capa[1] = IW_EVENT_CAPA_K_1; - range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVTXDROP) | - IW_EVENT_CAPA_MASK(IWEVCUSTOM) | - IW_EVENT_CAPA_MASK(IWEVREGISTERED) | - IW_EVENT_CAPA_MASK(IWEVEXPIRED)); - - range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | - IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; - - if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) - range->scan_capa = IW_SCAN_CAPA_ESSID; - - return 0; -} - - -static int hostap_monitor_mode_enable(local_info_t *local) -{ - struct net_device *dev = local->dev; - - printk(KERN_DEBUG "Enabling monitor mode\n"); - hostap_monitor_set_type(local); - - if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, - HFA384X_PORTTYPE_PSEUDO_IBSS)) { - printk(KERN_DEBUG "Port type setting for monitor mode " - "failed\n"); - return -EOPNOTSUPP; - } - - /* Host decrypt is needed to get the IV and ICV fields; - * however, monitor mode seems to remove WEP flag from frame - * control field */ - if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS, - HFA384X_WEPFLAGS_HOSTENCRYPT | - HFA384X_WEPFLAGS_HOSTDECRYPT)) { - printk(KERN_DEBUG "WEP flags setting failed\n"); - return -EOPNOTSUPP; - } - - if (local->func->reset_port(dev) || - local->func->cmd(dev, HFA384X_CMDCODE_TEST | - (HFA384X_TEST_MONITOR << 8), - 0, NULL, NULL)) { - printk(KERN_DEBUG "Setting monitor mode failed\n"); - return -EOPNOTSUPP; - } - - return 0; -} - - -static int hostap_monitor_mode_disable(local_info_t *local) -{ - struct net_device *dev = local->ddev; - - if (dev == NULL) - return -1; - - printk(KERN_DEBUG "%s: Disabling monitor mode\n", dev->name); - dev->type = ARPHRD_ETHER; - - if (local->func->cmd(dev, HFA384X_CMDCODE_TEST | - (HFA384X_TEST_STOP << 8), - 0, NULL, NULL)) - return -1; - return hostap_set_encryption(local); -} - - -static int prism2_ioctl_siwmode(struct net_device *dev, - struct iw_request_info *info, - __u32 *mode, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - int double_reset = 0; - - iface = netdev_priv(dev); - local = iface->local; - - if (*mode != IW_MODE_ADHOC && *mode != IW_MODE_INFRA && - *mode != IW_MODE_MASTER && *mode != IW_MODE_REPEAT && - *mode != IW_MODE_MONITOR) - return -EOPNOTSUPP; - -#ifdef PRISM2_NO_STATION_MODES - if (*mode == IW_MODE_ADHOC || *mode == IW_MODE_INFRA) - return -EOPNOTSUPP; -#endif /* PRISM2_NO_STATION_MODES */ - - if (*mode == local->iw_mode) - return 0; - - if (*mode == IW_MODE_MASTER && local->essid[0] == '\0') { - printk(KERN_WARNING "%s: empty SSID not allowed in Master " - "mode\n", dev->name); - return -EINVAL; - } - - if (local->iw_mode == IW_MODE_MONITOR) - hostap_monitor_mode_disable(local); - - if ((local->iw_mode == IW_MODE_ADHOC || - local->iw_mode == IW_MODE_MONITOR) && *mode == IW_MODE_MASTER) { - /* There seems to be a firmware bug in at least STA f/w v1.5.6 - * that leaves beacon frames to use IBSS type when moving from - * IBSS to Host AP mode. Doing double Port0 reset seems to be - * enough to workaround this. */ - double_reset = 1; - } - - printk(KERN_DEBUG "prism2: %s: operating mode changed " - "%d -> %d\n", dev->name, local->iw_mode, *mode); - local->iw_mode = *mode; - - if (local->iw_mode == IW_MODE_MONITOR) - hostap_monitor_mode_enable(local); - else if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt && - !local->fw_encrypt_ok) { - printk(KERN_DEBUG "%s: defaulting to host-based encryption as " - "a workaround for firmware bug in Host AP mode WEP\n", - dev->name); - local->host_encrypt = 1; - } - - if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, - hostap_get_porttype(local))) - return -EOPNOTSUPP; - - if (local->func->reset_port(dev)) - return -EINVAL; - if (double_reset && local->func->reset_port(dev)) - return -EINVAL; - - if (local->iw_mode != IW_MODE_INFRA && local->iw_mode != IW_MODE_ADHOC) - { - /* netif_carrier is used only in client modes for now, so make - * sure carrier is on when moving to non-client modes. */ - netif_carrier_on(local->dev); - netif_carrier_on(local->ddev); - } - return 0; -} - - -static int prism2_ioctl_giwmode(struct net_device *dev, - struct iw_request_info *info, - __u32 *mode, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - switch (iface->type) { - case HOSTAP_INTERFACE_STA: - *mode = IW_MODE_INFRA; - break; - case HOSTAP_INTERFACE_WDS: - *mode = IW_MODE_REPEAT; - break; - default: - *mode = local->iw_mode; - break; - } - return 0; -} - - -static int prism2_ioctl_siwpower(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *wrq, char *extra) -{ -#ifdef PRISM2_NO_STATION_MODES - return -EOPNOTSUPP; -#else /* PRISM2_NO_STATION_MODES */ - int ret = 0; - - if (wrq->disabled) - return hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 0); - - switch (wrq->flags & IW_POWER_MODE) { - case IW_POWER_UNICAST_R: - ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 0); - if (ret) - return ret; - ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); - if (ret) - return ret; - break; - case IW_POWER_ALL_R: - ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 1); - if (ret) - return ret; - ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); - if (ret) - return ret; - break; - case IW_POWER_ON: - break; - default: - return -EINVAL; - } - - if (wrq->flags & IW_POWER_TIMEOUT) { - ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); - if (ret) - return ret; - ret = hostap_set_word(dev, HFA384X_RID_CNFPMHOLDOVERDURATION, - wrq->value / 1024); - if (ret) - return ret; - } - if (wrq->flags & IW_POWER_PERIOD) { - ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); - if (ret) - return ret; - ret = hostap_set_word(dev, HFA384X_RID_CNFMAXSLEEPDURATION, - wrq->value / 1024); - if (ret) - return ret; - } - - return ret; -#endif /* PRISM2_NO_STATION_MODES */ -} - - -static int prism2_ioctl_giwpower(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, char *extra) -{ -#ifdef PRISM2_NO_STATION_MODES - return -EOPNOTSUPP; -#else /* PRISM2_NO_STATION_MODES */ - struct hostap_interface *iface; - local_info_t *local; - __le16 enable, mcast; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->func->get_rid(dev, HFA384X_RID_CNFPMENABLED, &enable, 2, 1) - < 0) - return -EINVAL; - - if (!le16_to_cpu(enable)) { - rrq->disabled = 1; - return 0; - } - - rrq->disabled = 0; - - if ((rrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { - __le16 timeout; - if (local->func->get_rid(dev, - HFA384X_RID_CNFPMHOLDOVERDURATION, - &timeout, 2, 1) < 0) - return -EINVAL; - - rrq->flags = IW_POWER_TIMEOUT; - rrq->value = le16_to_cpu(timeout) * 1024; - } else { - __le16 period; - if (local->func->get_rid(dev, HFA384X_RID_CNFMAXSLEEPDURATION, - &period, 2, 1) < 0) - return -EINVAL; - - rrq->flags = IW_POWER_PERIOD; - rrq->value = le16_to_cpu(period) * 1024; - } - - if (local->func->get_rid(dev, HFA384X_RID_CNFMULTICASTRECEIVE, &mcast, - 2, 1) < 0) - return -EINVAL; - - if (le16_to_cpu(mcast)) - rrq->flags |= IW_POWER_ALL_R; - else - rrq->flags |= IW_POWER_UNICAST_R; - - return 0; -#endif /* PRISM2_NO_STATION_MODES */ -} - - -static int prism2_ioctl_siwretry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - if (rrq->disabled) - return -EINVAL; - - /* setting retry limits is not supported with the current station - * firmware code; simulate this with alternative retry count for now */ - if (rrq->flags == IW_RETRY_LIMIT) { - if (rrq->value < 0) { - /* disable manual retry count setting and use firmware - * defaults */ - local->manual_retry_count = -1; - local->tx_control &= ~HFA384X_TX_CTRL_ALT_RTRY; - } else { - if (hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT, - rrq->value)) { - printk(KERN_DEBUG "%s: Alternate retry count " - "setting to %d failed\n", - dev->name, rrq->value); - return -EOPNOTSUPP; - } - - local->manual_retry_count = rrq->value; - local->tx_control |= HFA384X_TX_CTRL_ALT_RTRY; - } - return 0; - } - - return -EOPNOTSUPP; - -#if 0 - /* what could be done, if firmware would support this.. */ - - if (rrq->flags & IW_RETRY_LIMIT) { - if (rrq->flags & IW_RETRY_LONG) - HFA384X_RID_LONGRETRYLIMIT = rrq->value; - else if (rrq->flags & IW_RETRY_SHORT) - HFA384X_RID_SHORTRETRYLIMIT = rrq->value; - else { - HFA384X_RID_LONGRETRYLIMIT = rrq->value; - HFA384X_RID_SHORTRETRYLIMIT = rrq->value; - } - - } - - if (rrq->flags & IW_RETRY_LIFETIME) { - HFA384X_RID_MAXTRANSMITLIFETIME = rrq->value / 1024; - } - - return 0; -#endif /* 0 */ -} - -static int prism2_ioctl_giwretry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - __le16 shortretry, longretry, lifetime, altretry; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->func->get_rid(dev, HFA384X_RID_SHORTRETRYLIMIT, &shortretry, - 2, 1) < 0 || - local->func->get_rid(dev, HFA384X_RID_LONGRETRYLIMIT, &longretry, - 2, 1) < 0 || - local->func->get_rid(dev, HFA384X_RID_MAXTRANSMITLIFETIME, - &lifetime, 2, 1) < 0) - return -EINVAL; - - rrq->disabled = 0; - - if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { - rrq->flags = IW_RETRY_LIFETIME; - rrq->value = le16_to_cpu(lifetime) * 1024; - } else { - if (local->manual_retry_count >= 0) { - rrq->flags = IW_RETRY_LIMIT; - if (local->func->get_rid(dev, - HFA384X_RID_CNFALTRETRYCOUNT, - &altretry, 2, 1) >= 0) - rrq->value = le16_to_cpu(altretry); - else - rrq->value = local->manual_retry_count; - } else if ((rrq->flags & IW_RETRY_LONG)) { - rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; - rrq->value = le16_to_cpu(longretry); - } else { - rrq->flags = IW_RETRY_LIMIT; - rrq->value = le16_to_cpu(shortretry); - if (shortretry != longretry) - rrq->flags |= IW_RETRY_SHORT; - } - } - return 0; -} - - -/* Note! This TX power controlling is experimental and should not be used in - * production use. It just sets raw power register and does not use any kind of - * feedback information from the measured TX power (CR58). This is now - * commented out to make sure that it is not used by accident. TX power - * configuration will be enabled again after proper algorithm using feedback - * has been implemented. */ - -#ifdef RAW_TXPOWER_SETTING -/* Map HFA386x's CR31 to and from dBm with some sort of ad hoc mapping.. - * This version assumes following mapping: - * CR31 is 7-bit value with -64 to +63 range. - * -64 is mapped into +20dBm and +63 into -43dBm. - * This is certainly not an exact mapping for every card, but at least - * increasing dBm value should correspond to increasing TX power. - */ - -static int prism2_txpower_hfa386x_to_dBm(u16 val) -{ - signed char tmp; - - if (val > 255) - val = 255; - - tmp = val; - tmp >>= 2; - - return -12 - tmp; -} - -static u16 prism2_txpower_dBm_to_hfa386x(int val) -{ - signed char tmp; - - if (val > 20) - return 128; - else if (val < -43) - return 127; - - tmp = val; - tmp = -12 - tmp; - tmp <<= 2; - - return (unsigned char) tmp; -} -#endif /* RAW_TXPOWER_SETTING */ - - -static int prism2_ioctl_siwtxpow(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; -#ifdef RAW_TXPOWER_SETTING - char *tmp; -#endif - u16 val; - int ret = 0; - - iface = netdev_priv(dev); - local = iface->local; - - if (rrq->disabled) { - if (local->txpower_type != PRISM2_TXPOWER_OFF) { - val = 0xff; /* use all standby and sleep modes */ - ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, - HFA386X_CR_A_D_TEST_MODES2, - &val, NULL); - printk(KERN_DEBUG "%s: Turning radio off: %s\n", - dev->name, ret ? "failed" : "OK"); - local->txpower_type = PRISM2_TXPOWER_OFF; - } - return (ret ? -EOPNOTSUPP : 0); - } - - if (local->txpower_type == PRISM2_TXPOWER_OFF) { - val = 0; /* disable all standby and sleep modes */ - ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, - HFA386X_CR_A_D_TEST_MODES2, &val, NULL); - printk(KERN_DEBUG "%s: Turning radio on: %s\n", - dev->name, ret ? "failed" : "OK"); - local->txpower_type = PRISM2_TXPOWER_UNKNOWN; - } - -#ifdef RAW_TXPOWER_SETTING - if (!rrq->fixed && local->txpower_type != PRISM2_TXPOWER_AUTO) { - printk(KERN_DEBUG "Setting ALC on\n"); - val = HFA384X_TEST_CFG_BIT_ALC; - local->func->cmd(dev, HFA384X_CMDCODE_TEST | - (HFA384X_TEST_CFG_BITS << 8), 1, &val, NULL); - local->txpower_type = PRISM2_TXPOWER_AUTO; - return 0; - } - - if (local->txpower_type != PRISM2_TXPOWER_FIXED) { - printk(KERN_DEBUG "Setting ALC off\n"); - val = HFA384X_TEST_CFG_BIT_ALC; - local->func->cmd(dev, HFA384X_CMDCODE_TEST | - (HFA384X_TEST_CFG_BITS << 8), 0, &val, NULL); - local->txpower_type = PRISM2_TXPOWER_FIXED; - } - - if (rrq->flags == IW_TXPOW_DBM) - tmp = "dBm"; - else if (rrq->flags == IW_TXPOW_MWATT) - tmp = "mW"; - else - tmp = "UNKNOWN"; - printk(KERN_DEBUG "Setting TX power to %d %s\n", rrq->value, tmp); - - if (rrq->flags != IW_TXPOW_DBM) { - printk("SIOCSIWTXPOW with mW is not supported; use dBm\n"); - return -EOPNOTSUPP; - } - - local->txpower = rrq->value; - val = prism2_txpower_dBm_to_hfa386x(local->txpower); - if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, - HFA386X_CR_MANUAL_TX_POWER, &val, NULL)) - ret = -EOPNOTSUPP; -#else /* RAW_TXPOWER_SETTING */ - if (rrq->fixed) - ret = -EOPNOTSUPP; -#endif /* RAW_TXPOWER_SETTING */ - - return ret; -} - -static int prism2_ioctl_giwtxpow(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, char *extra) -{ -#ifdef RAW_TXPOWER_SETTING - struct hostap_interface *iface; - local_info_t *local; - u16 resp0; - - iface = netdev_priv(dev); - local = iface->local; - - rrq->flags = IW_TXPOW_DBM; - rrq->disabled = 0; - rrq->fixed = 0; - - if (local->txpower_type == PRISM2_TXPOWER_AUTO) { - if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, - HFA386X_CR_MANUAL_TX_POWER, - NULL, &resp0) == 0) { - rrq->value = prism2_txpower_hfa386x_to_dBm(resp0); - } else { - /* Could not get real txpower; guess 15 dBm */ - rrq->value = 15; - } - } else if (local->txpower_type == PRISM2_TXPOWER_OFF) { - rrq->value = 0; - rrq->disabled = 1; - } else if (local->txpower_type == PRISM2_TXPOWER_FIXED) { - rrq->value = local->txpower; - rrq->fixed = 1; - } else { - printk("SIOCGIWTXPOW - unknown txpower_type=%d\n", - local->txpower_type); - } - return 0; -#else /* RAW_TXPOWER_SETTING */ - return -EOPNOTSUPP; -#endif /* RAW_TXPOWER_SETTING */ -} - - -#ifndef PRISM2_NO_STATION_MODES - -/* HostScan request works with and without host_roaming mode. In addition, it - * does not break current association. However, it requires newer station - * firmware version (>= 1.3.1) than scan request. */ -static int prism2_request_hostscan(struct net_device *dev, - u8 *ssid, u8 ssid_len) -{ - struct hostap_interface *iface; - local_info_t *local; - struct hfa384x_hostscan_request scan_req; - - iface = netdev_priv(dev); - local = iface->local; - - memset(&scan_req, 0, sizeof(scan_req)); - scan_req.channel_list = cpu_to_le16(local->channel_mask & - local->scan_channel_mask); - scan_req.txrate = cpu_to_le16(HFA384X_RATES_1MBPS); - if (ssid) { - if (ssid_len > 32) - return -EINVAL; - scan_req.target_ssid_len = cpu_to_le16(ssid_len); - memcpy(scan_req.target_ssid, ssid, ssid_len); - } - - if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req, - sizeof(scan_req))) { - printk(KERN_DEBUG "%s: HOSTSCAN failed\n", dev->name); - return -EINVAL; - } - return 0; -} - - -static int prism2_request_scan(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - struct hfa384x_scan_request scan_req; - int ret = 0; - - iface = netdev_priv(dev); - local = iface->local; - - memset(&scan_req, 0, sizeof(scan_req)); - scan_req.channel_list = cpu_to_le16(local->channel_mask & - local->scan_channel_mask); - scan_req.txrate = cpu_to_le16(HFA384X_RATES_1MBPS); - - /* FIX: - * It seems to be enough to set roaming mode for a short moment to - * host-based and then setup scanrequest data and return the mode to - * firmware-based. - * - * Master mode would need to drop to Managed mode for a short while - * to make scanning work.. Or sweep through the different channels and - * use passive scan based on beacons. */ - - if (!local->host_roaming) - hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, - HFA384X_ROAMING_HOST); - - if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, &scan_req, - sizeof(scan_req))) { - printk(KERN_DEBUG "SCANREQUEST failed\n"); - ret = -EINVAL; - } - - if (!local->host_roaming) - hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, - HFA384X_ROAMING_FIRMWARE); - - return ret; -} - -#else /* !PRISM2_NO_STATION_MODES */ - -static inline int prism2_request_hostscan(struct net_device *dev, - u8 *ssid, u8 ssid_len) -{ - return -EOPNOTSUPP; -} - - -static inline int prism2_request_scan(struct net_device *dev) -{ - return -EOPNOTSUPP; -} - -#endif /* !PRISM2_NO_STATION_MODES */ - - -static int prism2_ioctl_siwscan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - int ret; - u8 *ssid = NULL, ssid_len = 0; - struct iw_scan_req *req = (struct iw_scan_req *) extra; - - iface = netdev_priv(dev); - local = iface->local; - - if (data->length < sizeof(struct iw_scan_req)) - req = NULL; - - if (local->iw_mode == IW_MODE_MASTER) { - /* In master mode, we just return the results of our local - * tables, so we don't need to start anything... - * Jean II */ - data->length = 0; - return 0; - } - - if (!local->dev_enabled) - return -ENETDOWN; - - if (req && data->flags & IW_SCAN_THIS_ESSID) { - ssid = req->essid; - ssid_len = req->essid_len; - - if (ssid_len && - ((local->iw_mode != IW_MODE_INFRA && - local->iw_mode != IW_MODE_ADHOC) || - (local->sta_fw_ver < PRISM2_FW_VER(1,3,1)))) - return -EOPNOTSUPP; - } - - if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) - ret = prism2_request_hostscan(dev, ssid, ssid_len); - else - ret = prism2_request_scan(dev); - - if (ret == 0) - local->scan_timestamp = jiffies; - - /* Could inquire F101, F103 or wait for SIOCGIWSCAN and read RID */ - - return ret; -} - - -#ifndef PRISM2_NO_STATION_MODES -static char * __prism2_translate_scan(local_info_t *local, - struct iw_request_info *info, - struct hfa384x_hostscan_result *scan, - struct hostap_bss_info *bss, - char *current_ev, char *end_buf) -{ - int i, chan; - struct iw_event iwe; - char *current_val; - u16 capabilities; - u8 *pos; - u8 *ssid, *bssid; - size_t ssid_len; - char *buf; - - if (bss) { - ssid = bss->ssid; - ssid_len = bss->ssid_len; - bssid = bss->bssid; - } else { - ssid = scan->ssid; - ssid_len = le16_to_cpu(scan->ssid_len); - bssid = scan->bssid; - } - if (ssid_len > 32) - ssid_len = 32; - - /* First entry *MUST* be the AP MAC address */ - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_ADDR_LEN); - - /* Other entries will be displayed in the order we give them */ - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWESSID; - iwe.u.data.length = ssid_len; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ssid); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWMODE; - if (bss) { - capabilities = bss->capab_info; - } else { - capabilities = le16_to_cpu(scan->capability); - } - if (capabilities & (WLAN_CAPABILITY_ESS | - WLAN_CAPABILITY_IBSS)) { - if (capabilities & WLAN_CAPABILITY_ESS) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_UINT_LEN); - } - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWFREQ; - if (scan) { - chan = le16_to_cpu(scan->chid); - } else if (bss) { - chan = bss->chan; - } else { - chan = 0; - } - - if (chan > 0) { - iwe.u.freq.m = freq_list[chan - 1] * 100000; - iwe.u.freq.e = 1; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_FREQ_LEN); - } - - if (scan) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVQUAL; - if (local->last_scan_type == PRISM2_HOSTSCAN) { - iwe.u.qual.level = le16_to_cpu(scan->sl); - iwe.u.qual.noise = le16_to_cpu(scan->anl); - } else { - iwe.u.qual.level = - HFA384X_LEVEL_TO_dBm(le16_to_cpu(scan->sl)); - iwe.u.qual.noise = - HFA384X_LEVEL_TO_dBm(le16_to_cpu(scan->anl)); - } - iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED - | IW_QUAL_NOISE_UPDATED - | IW_QUAL_QUAL_INVALID - | IW_QUAL_DBM; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_QUAL_LEN); - } - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWENCODE; - if (capabilities & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, ""); - - /* TODO: add SuppRates into BSS table */ - if (scan) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWRATE; - current_val = current_ev + iwe_stream_lcp_len(info); - pos = scan->sup_rates; - for (i = 0; i < sizeof(scan->sup_rates); i++) { - if (pos[i] == 0) - break; - /* Bit rate given in 500 kb/s units (+ 0x80) */ - iwe.u.bitrate.value = ((pos[i] & 0x7f) * 500000); - current_val = iwe_stream_add_value( - info, current_ev, current_val, end_buf, &iwe, - IW_EV_PARAM_LEN); - } - /* Check if we added any event */ - if ((current_val - current_ev) > iwe_stream_lcp_len(info)) - current_ev = current_val; - } - - /* TODO: add BeaconInt,resp_rate,atim into BSS table */ - buf = kmalloc(MAX_WPA_IE_LEN * 2 + 30, GFP_ATOMIC); - if (buf && scan) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "bcn_int=%d", le16_to_cpu(scan->beacon_interval)); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, buf); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "resp_rate=%d", le16_to_cpu(scan->rate)); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, buf); - - if (local->last_scan_type == PRISM2_HOSTSCAN && - (capabilities & WLAN_CAPABILITY_IBSS)) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "atim=%d", le16_to_cpu(scan->atim)); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, &iwe, buf); - } - } - kfree(buf); - - if (bss && bss->wpa_ie_len > 0 && bss->wpa_ie_len <= MAX_WPA_IE_LEN) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = bss->wpa_ie_len; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, bss->wpa_ie); - } - - if (bss && bss->rsn_ie_len > 0 && bss->rsn_ie_len <= MAX_WPA_IE_LEN) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = bss->rsn_ie_len; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, bss->rsn_ie); - } - - return current_ev; -} - - -/* Translate scan data returned from the card to a card independent - * format that the Wireless Tools will understand - Jean II */ -static inline int prism2_translate_scan(local_info_t *local, - struct iw_request_info *info, - char *buffer, int buflen) -{ - struct hfa384x_hostscan_result *scan; - int entry, hostscan; - char *current_ev = buffer; - char *end_buf = buffer + buflen; - struct list_head *ptr; - - spin_lock_bh(&local->lock); - - list_for_each(ptr, &local->bss_list) { - struct hostap_bss_info *bss; - bss = list_entry(ptr, struct hostap_bss_info, list); - bss->included = 0; - } - - hostscan = local->last_scan_type == PRISM2_HOSTSCAN; - for (entry = 0; entry < local->last_scan_results_count; entry++) { - int found = 0; - scan = &local->last_scan_results[entry]; - - /* Report every SSID if the AP is using multiple SSIDs. If no - * BSS record is found (e.g., when WPA mode is disabled), - * report the AP once. */ - list_for_each(ptr, &local->bss_list) { - struct hostap_bss_info *bss; - bss = list_entry(ptr, struct hostap_bss_info, list); - if (ether_addr_equal(bss->bssid, scan->bssid)) { - bss->included = 1; - current_ev = __prism2_translate_scan( - local, info, scan, bss, current_ev, - end_buf); - found++; - } - } - if (!found) { - current_ev = __prism2_translate_scan( - local, info, scan, NULL, current_ev, end_buf); - } - /* Check if there is space for one more entry */ - if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) { - /* Ask user space to try again with a bigger buffer */ - spin_unlock_bh(&local->lock); - return -E2BIG; - } - } - - /* Prism2 firmware has limits (32 at least in some versions) for number - * of BSSes in scan results. Extend this limit by using local BSS list. - */ - list_for_each(ptr, &local->bss_list) { - struct hostap_bss_info *bss; - bss = list_entry(ptr, struct hostap_bss_info, list); - if (bss->included) - continue; - current_ev = __prism2_translate_scan(local, info, NULL, bss, - current_ev, end_buf); - /* Check if there is space for one more entry */ - if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) { - /* Ask user space to try again with a bigger buffer */ - spin_unlock_bh(&local->lock); - return -E2BIG; - } - } - - spin_unlock_bh(&local->lock); - - return current_ev - buffer; -} -#endif /* PRISM2_NO_STATION_MODES */ - - -static inline int prism2_ioctl_giwscan_sta(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ -#ifdef PRISM2_NO_STATION_MODES - return -EOPNOTSUPP; -#else /* PRISM2_NO_STATION_MODES */ - struct hostap_interface *iface; - local_info_t *local; - int res; - - iface = netdev_priv(dev); - local = iface->local; - - /* Wait until the scan is finished. We can probably do better - * than that - Jean II */ - if (local->scan_timestamp && - time_before(jiffies, local->scan_timestamp + 3 * HZ)) { - /* Important note : we don't want to block the caller - * until results are ready for various reasons. - * First, managing wait queues is complex and racy - * (there may be multiple simultaneous callers). - * Second, we grab some rtnetlink lock before coming - * here (in dev_ioctl()). - * Third, the caller can wait on the Wireless Event - * - Jean II */ - return -EAGAIN; - } - local->scan_timestamp = 0; - - res = prism2_translate_scan(local, info, extra, data->length); - - if (res >= 0) { - data->length = res; - return 0; - } else { - data->length = 0; - return res; - } -#endif /* PRISM2_NO_STATION_MODES */ -} - - -static int prism2_ioctl_giwscan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - int res; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->iw_mode == IW_MODE_MASTER) { - /* In MASTER mode, it doesn't make sense to go around - * scanning the frequencies and make the stations we serve - * wait when what the user is really interested about is the - * list of stations and access points we are talking to. - * So, just extract results from our cache... - * Jean II */ - - /* Translate to WE format */ - res = prism2_ap_translate_scan(dev, info, extra); - if (res >= 0) { - printk(KERN_DEBUG "Scan result translation succeeded " - "(length=%d)\n", res); - data->length = res; - return 0; - } else { - printk(KERN_DEBUG - "Scan result translation failed (res=%d)\n", - res); - data->length = 0; - return res; - } - } else { - /* Station mode */ - return prism2_ioctl_giwscan_sta(dev, info, data, extra); - } -} - - -static const struct iw_priv_args prism2_priv[] = { - { PRISM2_IOCTL_MONITOR, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor" }, - { PRISM2_IOCTL_READMIF, - IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, - IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "readmif" }, - { PRISM2_IOCTL_WRITEMIF, - IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2, 0, "writemif" }, - { PRISM2_IOCTL_RESET, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "reset" }, - { PRISM2_IOCTL_INQUIRE, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inquire" }, - { PRISM2_IOCTL_SET_RID_WORD, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_rid_word" }, - { PRISM2_IOCTL_MACCMD, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maccmd" }, - { PRISM2_IOCTL_WDS_ADD, - IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_add" }, - { PRISM2_IOCTL_WDS_DEL, - IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_del" }, - { PRISM2_IOCTL_ADDMAC, - IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "addmac" }, - { PRISM2_IOCTL_DELMAC, - IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "delmac" }, - { PRISM2_IOCTL_KICKMAC, - IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac" }, - /* --- raw access to sub-ioctls --- */ - { PRISM2_IOCTL_PRISM2_PARAM, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "prism2_param" }, - { PRISM2_IOCTL_GET_PRISM2_PARAM, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprism2_param" }, - /* --- sub-ioctls handlers --- */ - { PRISM2_IOCTL_PRISM2_PARAM, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "" }, - { PRISM2_IOCTL_GET_PRISM2_PARAM, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" }, - /* --- sub-ioctls definitions --- */ - { PRISM2_PARAM_TXRATECTRL, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "txratectrl" }, - { PRISM2_PARAM_TXRATECTRL, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettxratectrl" }, - { PRISM2_PARAM_BEACON_INT, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "beacon_int" }, - { PRISM2_PARAM_BEACON_INT, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbeacon_int" }, -#ifndef PRISM2_NO_STATION_MODES - { PRISM2_PARAM_PSEUDO_IBSS, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pseudo_ibss" }, - { PRISM2_PARAM_PSEUDO_IBSS, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getpseudo_ibss" }, -#endif /* PRISM2_NO_STATION_MODES */ - { PRISM2_PARAM_ALC, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "alc" }, - { PRISM2_PARAM_ALC, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getalc" }, - { PRISM2_PARAM_DUMP, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dump" }, - { PRISM2_PARAM_DUMP, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdump" }, - { PRISM2_PARAM_OTHER_AP_POLICY, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "other_ap_policy" }, - { PRISM2_PARAM_OTHER_AP_POLICY, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getother_ap_pol" }, - { PRISM2_PARAM_AP_MAX_INACTIVITY, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_inactivity" }, - { PRISM2_PARAM_AP_MAX_INACTIVITY, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_inactivi" }, - { PRISM2_PARAM_AP_BRIDGE_PACKETS, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bridge_packets" }, - { PRISM2_PARAM_AP_BRIDGE_PACKETS, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbridge_packe" }, - { PRISM2_PARAM_DTIM_PERIOD, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dtim_period" }, - { PRISM2_PARAM_DTIM_PERIOD, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdtim_period" }, - { PRISM2_PARAM_AP_NULLFUNC_ACK, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "nullfunc_ack" }, - { PRISM2_PARAM_AP_NULLFUNC_ACK, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getnullfunc_ack" }, - { PRISM2_PARAM_MAX_WDS, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_wds" }, - { PRISM2_PARAM_MAX_WDS, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_wds" }, - { PRISM2_PARAM_AP_AUTOM_AP_WDS, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "autom_ap_wds" }, - { PRISM2_PARAM_AP_AUTOM_AP_WDS, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getautom_ap_wds" }, - { PRISM2_PARAM_AP_AUTH_ALGS, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_auth_algs" }, - { PRISM2_PARAM_AP_AUTH_ALGS, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_auth_algs" }, - { PRISM2_PARAM_MONITOR_ALLOW_FCSERR, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "allow_fcserr" }, - { PRISM2_PARAM_MONITOR_ALLOW_FCSERR, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getallow_fcserr" }, - { PRISM2_PARAM_HOST_ENCRYPT, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_encrypt" }, - { PRISM2_PARAM_HOST_ENCRYPT, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_encrypt" }, - { PRISM2_PARAM_HOST_DECRYPT, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_decrypt" }, - { PRISM2_PARAM_HOST_DECRYPT, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_decrypt" }, -#ifndef PRISM2_NO_STATION_MODES - { PRISM2_PARAM_HOST_ROAMING, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_roaming" }, - { PRISM2_PARAM_HOST_ROAMING, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_roaming" }, -#endif /* PRISM2_NO_STATION_MODES */ - { PRISM2_PARAM_BCRX_STA_KEY, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bcrx_sta_key" }, - { PRISM2_PARAM_BCRX_STA_KEY, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbcrx_sta_key" }, - { PRISM2_PARAM_IEEE_802_1X, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ieee_802_1x" }, - { PRISM2_PARAM_IEEE_802_1X, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getieee_802_1x" }, - { PRISM2_PARAM_ANTSEL_TX, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_tx" }, - { PRISM2_PARAM_ANTSEL_TX, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_tx" }, - { PRISM2_PARAM_ANTSEL_RX, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_rx" }, - { PRISM2_PARAM_ANTSEL_RX, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_rx" }, - { PRISM2_PARAM_MONITOR_TYPE, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor_type" }, - { PRISM2_PARAM_MONITOR_TYPE, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmonitor_type" }, - { PRISM2_PARAM_WDS_TYPE, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wds_type" }, - { PRISM2_PARAM_WDS_TYPE, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwds_type" }, - { PRISM2_PARAM_HOSTSCAN, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostscan" }, - { PRISM2_PARAM_HOSTSCAN, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostscan" }, - { PRISM2_PARAM_AP_SCAN, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_scan" }, - { PRISM2_PARAM_AP_SCAN, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_scan" }, - { PRISM2_PARAM_ENH_SEC, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "enh_sec" }, - { PRISM2_PARAM_ENH_SEC, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getenh_sec" }, -#ifdef PRISM2_IO_DEBUG - { PRISM2_PARAM_IO_DEBUG, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "io_debug" }, - { PRISM2_PARAM_IO_DEBUG, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getio_debug" }, -#endif /* PRISM2_IO_DEBUG */ - { PRISM2_PARAM_BASIC_RATES, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "basic_rates" }, - { PRISM2_PARAM_BASIC_RATES, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbasic_rates" }, - { PRISM2_PARAM_OPER_RATES, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "oper_rates" }, - { PRISM2_PARAM_OPER_RATES, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getoper_rates" }, - { PRISM2_PARAM_HOSTAPD, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd" }, - { PRISM2_PARAM_HOSTAPD, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd" }, - { PRISM2_PARAM_HOSTAPD_STA, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd_sta" }, - { PRISM2_PARAM_HOSTAPD_STA, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd_sta" }, - { PRISM2_PARAM_WPA, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wpa" }, - { PRISM2_PARAM_WPA, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwpa" }, - { PRISM2_PARAM_PRIVACY_INVOKED, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "privacy_invoked" }, - { PRISM2_PARAM_PRIVACY_INVOKED, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprivacy_invo" }, - { PRISM2_PARAM_TKIP_COUNTERMEASURES, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tkip_countermea" }, - { PRISM2_PARAM_TKIP_COUNTERMEASURES, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettkip_counter" }, - { PRISM2_PARAM_DROP_UNENCRYPTED, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "drop_unencrypte" }, - { PRISM2_PARAM_DROP_UNENCRYPTED, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdrop_unencry" }, - { PRISM2_PARAM_SCAN_CHANNEL_MASK, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "scan_channels" }, - { PRISM2_PARAM_SCAN_CHANNEL_MASK, - 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getscan_channel" }, -}; - - -static int prism2_ioctl_priv_inquire(struct net_device *dev, int *i) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->func->cmd(dev, HFA384X_CMDCODE_INQUIRE, *i, NULL, NULL)) - return -EOPNOTSUPP; - - return 0; -} - - -static int prism2_ioctl_priv_prism2_param(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - int *i = (int *) extra; - int param = *i; - int value = *(i + 1); - int ret = 0; - u16 val; - - iface = netdev_priv(dev); - local = iface->local; - - switch (param) { - case PRISM2_PARAM_TXRATECTRL: - local->fw_tx_rate_control = value; - break; - - case PRISM2_PARAM_BEACON_INT: - if (hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, value) || - local->func->reset_port(dev)) - ret = -EINVAL; - else - local->beacon_int = value; - break; - -#ifndef PRISM2_NO_STATION_MODES - case PRISM2_PARAM_PSEUDO_IBSS: - if (value == local->pseudo_adhoc) - break; - - if (value != 0 && value != 1) { - ret = -EINVAL; - break; - } - - printk(KERN_DEBUG "prism2: %s: pseudo IBSS change %d -> %d\n", - dev->name, local->pseudo_adhoc, value); - local->pseudo_adhoc = value; - if (local->iw_mode != IW_MODE_ADHOC) - break; - - if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, - hostap_get_porttype(local))) { - ret = -EOPNOTSUPP; - break; - } - - if (local->func->reset_port(dev)) - ret = -EINVAL; - break; -#endif /* PRISM2_NO_STATION_MODES */ - - case PRISM2_PARAM_ALC: - printk(KERN_DEBUG "%s: %s ALC\n", dev->name, - value == 0 ? "Disabling" : "Enabling"); - val = HFA384X_TEST_CFG_BIT_ALC; - local->func->cmd(dev, HFA384X_CMDCODE_TEST | - (HFA384X_TEST_CFG_BITS << 8), - value == 0 ? 0 : 1, &val, NULL); - break; - - case PRISM2_PARAM_DUMP: - local->frame_dump = value; - break; - - case PRISM2_PARAM_OTHER_AP_POLICY: - if (value < 0 || value > 3) { - ret = -EINVAL; - break; - } - if (local->ap != NULL) - local->ap->ap_policy = value; - break; - - case PRISM2_PARAM_AP_MAX_INACTIVITY: - if (value < 0 || value > 7 * 24 * 60 * 60) { - ret = -EINVAL; - break; - } - if (local->ap != NULL) - local->ap->max_inactivity = value * HZ; - break; - - case PRISM2_PARAM_AP_BRIDGE_PACKETS: - if (local->ap != NULL) - local->ap->bridge_packets = value; - break; - - case PRISM2_PARAM_DTIM_PERIOD: - if (value < 0 || value > 65535) { - ret = -EINVAL; - break; - } - if (hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, value) - || local->func->reset_port(dev)) - ret = -EINVAL; - else - local->dtim_period = value; - break; - - case PRISM2_PARAM_AP_NULLFUNC_ACK: - if (local->ap != NULL) - local->ap->nullfunc_ack = value; - break; - - case PRISM2_PARAM_MAX_WDS: - local->wds_max_connections = value; - break; - - case PRISM2_PARAM_AP_AUTOM_AP_WDS: - if (local->ap != NULL) { - if (!local->ap->autom_ap_wds && value) { - /* add WDS link to all APs in STA table */ - hostap_add_wds_links(local); - } - local->ap->autom_ap_wds = value; - } - break; - - case PRISM2_PARAM_AP_AUTH_ALGS: - local->auth_algs = value; - if (hostap_set_auth_algs(local)) - ret = -EINVAL; - break; - - case PRISM2_PARAM_MONITOR_ALLOW_FCSERR: - local->monitor_allow_fcserr = value; - break; - - case PRISM2_PARAM_HOST_ENCRYPT: - local->host_encrypt = value; - if (hostap_set_encryption(local) || - local->func->reset_port(dev)) - ret = -EINVAL; - break; - - case PRISM2_PARAM_HOST_DECRYPT: - local->host_decrypt = value; - if (hostap_set_encryption(local) || - local->func->reset_port(dev)) - ret = -EINVAL; - break; - -#ifndef PRISM2_NO_STATION_MODES - case PRISM2_PARAM_HOST_ROAMING: - if (value < 0 || value > 2) { - ret = -EINVAL; - break; - } - local->host_roaming = value; - if (hostap_set_roaming(local) || local->func->reset_port(dev)) - ret = -EINVAL; - break; -#endif /* PRISM2_NO_STATION_MODES */ - - case PRISM2_PARAM_BCRX_STA_KEY: - local->bcrx_sta_key = value; - break; - - case PRISM2_PARAM_IEEE_802_1X: - local->ieee_802_1x = value; - break; - - case PRISM2_PARAM_ANTSEL_TX: - if (value < 0 || value > HOSTAP_ANTSEL_HIGH) { - ret = -EINVAL; - break; - } - local->antsel_tx = value; - hostap_set_antsel(local); - break; - - case PRISM2_PARAM_ANTSEL_RX: - if (value < 0 || value > HOSTAP_ANTSEL_HIGH) { - ret = -EINVAL; - break; - } - local->antsel_rx = value; - hostap_set_antsel(local); - break; - - case PRISM2_PARAM_MONITOR_TYPE: - if (value != PRISM2_MONITOR_80211 && - value != PRISM2_MONITOR_CAPHDR && - value != PRISM2_MONITOR_PRISM && - value != PRISM2_MONITOR_RADIOTAP) { - ret = -EINVAL; - break; - } - local->monitor_type = value; - if (local->iw_mode == IW_MODE_MONITOR) - hostap_monitor_set_type(local); - break; - - case PRISM2_PARAM_WDS_TYPE: - local->wds_type = value; - break; - - case PRISM2_PARAM_HOSTSCAN: - { - struct hfa384x_hostscan_request scan_req; - u16 rate; - - memset(&scan_req, 0, sizeof(scan_req)); - scan_req.channel_list = cpu_to_le16(0x3fff); - switch (value) { - case 1: rate = HFA384X_RATES_1MBPS; break; - case 2: rate = HFA384X_RATES_2MBPS; break; - case 3: rate = HFA384X_RATES_5MBPS; break; - case 4: rate = HFA384X_RATES_11MBPS; break; - default: rate = HFA384X_RATES_1MBPS; break; - } - scan_req.txrate = cpu_to_le16(rate); - /* leave SSID empty to accept all SSIDs */ - - if (local->iw_mode == IW_MODE_MASTER) { - if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, - HFA384X_PORTTYPE_BSS) || - local->func->reset_port(dev)) - printk(KERN_DEBUG "Leaving Host AP mode " - "for HostScan failed\n"); - } - - if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req, - sizeof(scan_req))) { - printk(KERN_DEBUG "HOSTSCAN failed\n"); - ret = -EINVAL; - } - if (local->iw_mode == IW_MODE_MASTER) { - wait_queue_t __wait; - init_waitqueue_entry(&__wait, current); - add_wait_queue(&local->hostscan_wq, &__wait); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); - if (signal_pending(current)) - ret = -EINTR; - set_current_state(TASK_RUNNING); - remove_wait_queue(&local->hostscan_wq, &__wait); - - if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, - HFA384X_PORTTYPE_HOSTAP) || - local->func->reset_port(dev)) - printk(KERN_DEBUG "Returning to Host AP mode " - "after HostScan failed\n"); - } - break; - } - - case PRISM2_PARAM_AP_SCAN: - local->passive_scan_interval = value; - if (timer_pending(&local->passive_scan_timer)) - del_timer(&local->passive_scan_timer); - if (value > 0 && value < INT_MAX / HZ) { - local->passive_scan_timer.expires = jiffies + - local->passive_scan_interval * HZ; - add_timer(&local->passive_scan_timer); - } - break; - - case PRISM2_PARAM_ENH_SEC: - if (value < 0 || value > 3) { - ret = -EINVAL; - break; - } - local->enh_sec = value; - if (hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, - local->enh_sec) || - local->func->reset_port(dev)) { - printk(KERN_INFO "%s: cnfEnhSecurity requires STA f/w " - "1.6.3 or newer\n", dev->name); - ret = -EOPNOTSUPP; - } - break; - -#ifdef PRISM2_IO_DEBUG - case PRISM2_PARAM_IO_DEBUG: - local->io_debug_enabled = value; - break; -#endif /* PRISM2_IO_DEBUG */ - - case PRISM2_PARAM_BASIC_RATES: - if ((value & local->tx_rate_control) != value || value == 0) { - printk(KERN_INFO "%s: invalid basic rate set - basic " - "rates must be in supported rate set\n", - dev->name); - ret = -EINVAL; - break; - } - local->basic_rates = value; - if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, - local->basic_rates) || - local->func->reset_port(dev)) - ret = -EINVAL; - break; - - case PRISM2_PARAM_OPER_RATES: - local->tx_rate_control = value; - if (hostap_set_rate(dev)) - ret = -EINVAL; - break; - - case PRISM2_PARAM_HOSTAPD: - ret = hostap_set_hostapd(local, value, 1); - break; - - case PRISM2_PARAM_HOSTAPD_STA: - ret = hostap_set_hostapd_sta(local, value, 1); - break; - - case PRISM2_PARAM_WPA: - local->wpa = value; - if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) - ret = -EOPNOTSUPP; - else if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, - value ? 1 : 0)) - ret = -EINVAL; - break; - - case PRISM2_PARAM_PRIVACY_INVOKED: - local->privacy_invoked = value; - if (hostap_set_encryption(local) || - local->func->reset_port(dev)) - ret = -EINVAL; - break; - - case PRISM2_PARAM_TKIP_COUNTERMEASURES: - local->tkip_countermeasures = value; - break; - - case PRISM2_PARAM_DROP_UNENCRYPTED: - local->drop_unencrypted = value; - break; - - case PRISM2_PARAM_SCAN_CHANNEL_MASK: - local->scan_channel_mask = value; - break; - - default: - printk(KERN_DEBUG "%s: prism2_param: unknown param %d\n", - dev->name, param); - ret = -EOPNOTSUPP; - break; - } - - return ret; -} - - -static int prism2_ioctl_priv_get_prism2_param(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - int *param = (int *) extra; - int ret = 0; - - iface = netdev_priv(dev); - local = iface->local; - - switch (*param) { - case PRISM2_PARAM_TXRATECTRL: - *param = local->fw_tx_rate_control; - break; - - case PRISM2_PARAM_BEACON_INT: - *param = local->beacon_int; - break; - - case PRISM2_PARAM_PSEUDO_IBSS: - *param = local->pseudo_adhoc; - break; - - case PRISM2_PARAM_ALC: - ret = -EOPNOTSUPP; /* FIX */ - break; - - case PRISM2_PARAM_DUMP: - *param = local->frame_dump; - break; - - case PRISM2_PARAM_OTHER_AP_POLICY: - if (local->ap != NULL) - *param = local->ap->ap_policy; - else - ret = -EOPNOTSUPP; - break; - - case PRISM2_PARAM_AP_MAX_INACTIVITY: - if (local->ap != NULL) - *param = local->ap->max_inactivity / HZ; - else - ret = -EOPNOTSUPP; - break; - - case PRISM2_PARAM_AP_BRIDGE_PACKETS: - if (local->ap != NULL) - *param = local->ap->bridge_packets; - else - ret = -EOPNOTSUPP; - break; - - case PRISM2_PARAM_DTIM_PERIOD: - *param = local->dtim_period; - break; - - case PRISM2_PARAM_AP_NULLFUNC_ACK: - if (local->ap != NULL) - *param = local->ap->nullfunc_ack; - else - ret = -EOPNOTSUPP; - break; - - case PRISM2_PARAM_MAX_WDS: - *param = local->wds_max_connections; - break; - - case PRISM2_PARAM_AP_AUTOM_AP_WDS: - if (local->ap != NULL) - *param = local->ap->autom_ap_wds; - else - ret = -EOPNOTSUPP; - break; - - case PRISM2_PARAM_AP_AUTH_ALGS: - *param = local->auth_algs; - break; - - case PRISM2_PARAM_MONITOR_ALLOW_FCSERR: - *param = local->monitor_allow_fcserr; - break; - - case PRISM2_PARAM_HOST_ENCRYPT: - *param = local->host_encrypt; - break; - - case PRISM2_PARAM_HOST_DECRYPT: - *param = local->host_decrypt; - break; - - case PRISM2_PARAM_HOST_ROAMING: - *param = local->host_roaming; - break; - - case PRISM2_PARAM_BCRX_STA_KEY: - *param = local->bcrx_sta_key; - break; - - case PRISM2_PARAM_IEEE_802_1X: - *param = local->ieee_802_1x; - break; - - case PRISM2_PARAM_ANTSEL_TX: - *param = local->antsel_tx; - break; - - case PRISM2_PARAM_ANTSEL_RX: - *param = local->antsel_rx; - break; - - case PRISM2_PARAM_MONITOR_TYPE: - *param = local->monitor_type; - break; - - case PRISM2_PARAM_WDS_TYPE: - *param = local->wds_type; - break; - - case PRISM2_PARAM_HOSTSCAN: - ret = -EOPNOTSUPP; - break; - - case PRISM2_PARAM_AP_SCAN: - *param = local->passive_scan_interval; - break; - - case PRISM2_PARAM_ENH_SEC: - *param = local->enh_sec; - break; - -#ifdef PRISM2_IO_DEBUG - case PRISM2_PARAM_IO_DEBUG: - *param = local->io_debug_enabled; - break; -#endif /* PRISM2_IO_DEBUG */ - - case PRISM2_PARAM_BASIC_RATES: - *param = local->basic_rates; - break; - - case PRISM2_PARAM_OPER_RATES: - *param = local->tx_rate_control; - break; - - case PRISM2_PARAM_HOSTAPD: - *param = local->hostapd; - break; - - case PRISM2_PARAM_HOSTAPD_STA: - *param = local->hostapd_sta; - break; - - case PRISM2_PARAM_WPA: - if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) - ret = -EOPNOTSUPP; - *param = local->wpa; - break; - - case PRISM2_PARAM_PRIVACY_INVOKED: - *param = local->privacy_invoked; - break; - - case PRISM2_PARAM_TKIP_COUNTERMEASURES: - *param = local->tkip_countermeasures; - break; - - case PRISM2_PARAM_DROP_UNENCRYPTED: - *param = local->drop_unencrypted; - break; - - case PRISM2_PARAM_SCAN_CHANNEL_MASK: - *param = local->scan_channel_mask; - break; - - default: - printk(KERN_DEBUG "%s: get_prism2_param: unknown param %d\n", - dev->name, *param); - ret = -EOPNOTSUPP; - break; - } - - return ret; -} - - -static int prism2_ioctl_priv_readmif(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - u16 resp0; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, *extra, NULL, - &resp0)) - return -EOPNOTSUPP; - else - *extra = resp0; - - return 0; -} - - -static int prism2_ioctl_priv_writemif(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, char *extra) -{ - struct hostap_interface *iface; - local_info_t *local; - u16 cr, val; - - iface = netdev_priv(dev); - local = iface->local; - - cr = *extra; - val = *(extra + 1); - if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, cr, &val, NULL)) - return -EOPNOTSUPP; - - return 0; -} - - -static int prism2_ioctl_priv_monitor(struct net_device *dev, int *i) -{ - struct hostap_interface *iface; - local_info_t *local; - int ret = 0; - u32 mode; - - iface = netdev_priv(dev); - local = iface->local; - - printk(KERN_DEBUG "%s: process %d (%s) used deprecated iwpriv monitor " - "- update software to use iwconfig mode monitor\n", - dev->name, task_pid_nr(current), current->comm); - - /* Backward compatibility code - this can be removed at some point */ - - if (*i == 0) { - /* Disable monitor mode - old mode was not saved, so go to - * Master mode */ - mode = IW_MODE_MASTER; - ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); - } else if (*i == 1) { - /* netlink socket mode is not supported anymore since it did - * not separate different devices from each other and was not - * best method for delivering large amount of packets to - * user space */ - ret = -EOPNOTSUPP; - } else if (*i == 2 || *i == 3) { - switch (*i) { - case 2: - local->monitor_type = PRISM2_MONITOR_80211; - break; - case 3: - local->monitor_type = PRISM2_MONITOR_PRISM; - break; - } - mode = IW_MODE_MONITOR; - ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); - hostap_monitor_mode_enable(local); - } else - ret = -EINVAL; - - return ret; -} - - -static int prism2_ioctl_priv_reset(struct net_device *dev, int *i) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - printk(KERN_DEBUG "%s: manual reset request(%d)\n", dev->name, *i); - switch (*i) { - case 0: - /* Disable and enable card */ - local->func->hw_shutdown(dev, 1); - local->func->hw_config(dev, 0); - break; - - case 1: - /* COR sreset */ - local->func->hw_reset(dev); - break; - - case 2: - /* Disable and enable port 0 */ - local->func->reset_port(dev); - break; - - case 3: - prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING); - if (local->func->cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, - NULL)) - return -EINVAL; - break; - - case 4: - if (local->func->cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, - NULL)) - return -EINVAL; - break; - - default: - printk(KERN_DEBUG "Unknown reset request %d\n", *i); - return -EOPNOTSUPP; - } - - return 0; -} - - -static int prism2_ioctl_priv_set_rid_word(struct net_device *dev, int *i) -{ - int rid = *i; - int value = *(i + 1); - - printk(KERN_DEBUG "%s: Set RID[0x%X] = %d\n", dev->name, rid, value); - - if (hostap_set_word(dev, rid, value)) - return -EINVAL; - - return 0; -} - - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT -static int ap_mac_cmd_ioctl(local_info_t *local, int *cmd) -{ - int ret = 0; - - switch (*cmd) { - case AP_MAC_CMD_POLICY_OPEN: - local->ap->mac_restrictions.policy = MAC_POLICY_OPEN; - break; - case AP_MAC_CMD_POLICY_ALLOW: - local->ap->mac_restrictions.policy = MAC_POLICY_ALLOW; - break; - case AP_MAC_CMD_POLICY_DENY: - local->ap->mac_restrictions.policy = MAC_POLICY_DENY; - break; - case AP_MAC_CMD_FLUSH: - ap_control_flush_macs(&local->ap->mac_restrictions); - break; - case AP_MAC_CMD_KICKALL: - ap_control_kickall(local->ap); - hostap_deauth_all_stas(local->dev, local->ap, 0); - break; - default: - ret = -EOPNOTSUPP; - break; - } - - return ret; -} -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - -#ifdef PRISM2_DOWNLOAD_SUPPORT -static int prism2_ioctl_priv_download(local_info_t *local, struct iw_point *p) -{ - struct prism2_download_param *param; - int ret = 0; - - if (p->length < sizeof(struct prism2_download_param) || - p->length > 1024 || !p->pointer) - return -EINVAL; - - param = kmalloc(p->length, GFP_KERNEL); - if (param == NULL) - return -ENOMEM; - - if (copy_from_user(param, p->pointer, p->length)) { - ret = -EFAULT; - goto out; - } - - if (p->length < sizeof(struct prism2_download_param) + - param->num_areas * sizeof(struct prism2_download_area)) { - ret = -EINVAL; - goto out; - } - - ret = local->func->download(local, param); - - out: - kfree(param); - return ret; -} -#endif /* PRISM2_DOWNLOAD_SUPPORT */ - - -static int prism2_set_genericelement(struct net_device *dev, u8 *elem, - size_t len) -{ - struct hostap_interface *iface = netdev_priv(dev); - local_info_t *local = iface->local; - u8 *buf; - - /* - * Add 16-bit length in the beginning of the buffer because Prism2 RID - * includes it. - */ - buf = kmalloc(len + 2, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - *((__le16 *) buf) = cpu_to_le16(len); - memcpy(buf + 2, elem, len); - - kfree(local->generic_elem); - local->generic_elem = buf; - local->generic_elem_len = len + 2; - - return local->func->set_rid(local->dev, HFA384X_RID_GENERICELEMENT, - buf, len + 2); -} - - -static int prism2_ioctl_siwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - struct hostap_interface *iface = netdev_priv(dev); - local_info_t *local = iface->local; - - switch (data->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_PAIRWISE: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_KEY_MGMT: - /* - * Host AP driver does not use these parameters and allows - * wpa_supplicant to control them internally. - */ - break; - case IW_AUTH_TKIP_COUNTERMEASURES: - local->tkip_countermeasures = data->value; - break; - case IW_AUTH_DROP_UNENCRYPTED: - local->drop_unencrypted = data->value; - break; - case IW_AUTH_80211_AUTH_ALG: - local->auth_algs = data->value; - break; - case IW_AUTH_WPA_ENABLED: - if (data->value == 0) { - local->wpa = 0; - if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) - break; - prism2_set_genericelement(dev, "", 0); - local->host_roaming = 0; - local->privacy_invoked = 0; - if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, - 0) || - hostap_set_roaming(local) || - hostap_set_encryption(local) || - local->func->reset_port(dev)) - return -EINVAL; - break; - } - if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) - return -EOPNOTSUPP; - local->host_roaming = 2; - local->privacy_invoked = 1; - local->wpa = 1; - if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1) || - hostap_set_roaming(local) || - hostap_set_encryption(local) || - local->func->reset_port(dev)) - return -EINVAL; - break; - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - local->ieee_802_1x = data->value; - break; - case IW_AUTH_PRIVACY_INVOKED: - local->privacy_invoked = data->value; - break; - default: - return -EOPNOTSUPP; - } - return 0; -} - - -static int prism2_ioctl_giwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - struct hostap_interface *iface = netdev_priv(dev); - local_info_t *local = iface->local; - - switch (data->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_PAIRWISE: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_KEY_MGMT: - /* - * Host AP driver does not use these parameters and allows - * wpa_supplicant to control them internally. - */ - return -EOPNOTSUPP; - case IW_AUTH_TKIP_COUNTERMEASURES: - data->value = local->tkip_countermeasures; - break; - case IW_AUTH_DROP_UNENCRYPTED: - data->value = local->drop_unencrypted; - break; - case IW_AUTH_80211_AUTH_ALG: - data->value = local->auth_algs; - break; - case IW_AUTH_WPA_ENABLED: - data->value = local->wpa; - break; - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - data->value = local->ieee_802_1x; - break; - default: - return -EOPNOTSUPP; - } - return 0; -} - - -static int prism2_ioctl_siwencodeext(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *extra) -{ - struct hostap_interface *iface = netdev_priv(dev); - local_info_t *local = iface->local; - struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; - int i, ret = 0; - struct lib80211_crypto_ops *ops; - struct lib80211_crypt_data **crypt; - void *sta_ptr; - u8 *addr; - const char *alg, *module; - - i = erq->flags & IW_ENCODE_INDEX; - if (i > WEP_KEYS) - return -EINVAL; - if (i < 1 || i > WEP_KEYS) - i = local->crypt_info.tx_keyidx; - else - i--; - if (i < 0 || i >= WEP_KEYS) - return -EINVAL; - - addr = ext->addr.sa_data; - if (is_broadcast_ether_addr(addr)) { - sta_ptr = NULL; - crypt = &local->crypt_info.crypt[i]; - } else { - if (i != 0) - return -EINVAL; - sta_ptr = ap_crypt_get_ptrs(local->ap, addr, 0, &crypt); - if (sta_ptr == NULL) { - if (local->iw_mode == IW_MODE_INFRA) { - /* - * TODO: add STA entry for the current AP so - * that unicast key can be used. For now, this - * is emulated by using default key idx 0. - */ - i = 0; - crypt = &local->crypt_info.crypt[i]; - } else - return -EINVAL; - } - } - - if ((erq->flags & IW_ENCODE_DISABLED) || - ext->alg == IW_ENCODE_ALG_NONE) { - if (*crypt) - lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); - goto done; - } - - switch (ext->alg) { - case IW_ENCODE_ALG_WEP: - alg = "WEP"; - module = "lib80211_crypt_wep"; - break; - case IW_ENCODE_ALG_TKIP: - alg = "TKIP"; - module = "lib80211_crypt_tkip"; - break; - case IW_ENCODE_ALG_CCMP: - alg = "CCMP"; - module = "lib80211_crypt_ccmp"; - break; - default: - printk(KERN_DEBUG "%s: unsupported algorithm %d\n", - local->dev->name, ext->alg); - ret = -EOPNOTSUPP; - goto done; - } - - ops = lib80211_get_crypto_ops(alg); - if (ops == NULL) { - request_module(module); - ops = lib80211_get_crypto_ops(alg); - } - if (ops == NULL) { - printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n", - local->dev->name, alg); - ret = -EOPNOTSUPP; - goto done; - } - - if (sta_ptr || ext->alg != IW_ENCODE_ALG_WEP) { - /* - * Per station encryption and other than WEP algorithms - * require host-based encryption, so force them on - * automatically. - */ - local->host_decrypt = local->host_encrypt = 1; - } - - if (*crypt == NULL || (*crypt)->ops != ops) { - struct lib80211_crypt_data *new_crypt; - - lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); - - new_crypt = kzalloc(sizeof(struct lib80211_crypt_data), - GFP_KERNEL); - if (new_crypt == NULL) { - ret = -ENOMEM; - goto done; - } - new_crypt->ops = ops; - if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) - new_crypt->priv = new_crypt->ops->init(i); - if (new_crypt->priv == NULL) { - kfree(new_crypt); - ret = -EINVAL; - goto done; - } - - *crypt = new_crypt; - } - - /* - * TODO: if ext_flags does not have IW_ENCODE_EXT_RX_SEQ_VALID, the - * existing seq# should not be changed. - * TODO: if ext_flags has IW_ENCODE_EXT_TX_SEQ_VALID, next TX seq# - * should be changed to something else than zero. - */ - if ((!(ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) || ext->key_len > 0) - && (*crypt)->ops->set_key && - (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, - (*crypt)->priv) < 0) { - printk(KERN_DEBUG "%s: key setting failed\n", - local->dev->name); - ret = -EINVAL; - goto done; - } - - if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { - if (!sta_ptr) - local->crypt_info.tx_keyidx = i; - } - - - if (sta_ptr == NULL && ext->key_len > 0) { - int first = 1, j; - for (j = 0; j < WEP_KEYS; j++) { - if (j != i && local->crypt_info.crypt[j]) { - first = 0; - break; - } - } - if (first) - local->crypt_info.tx_keyidx = i; - } - - done: - if (sta_ptr) - hostap_handle_sta_release(sta_ptr); - - local->open_wep = erq->flags & IW_ENCODE_OPEN; - - /* - * Do not reset port0 if card is in Managed mode since resetting will - * generate new IEEE 802.11 authentication which may end up in looping - * with IEEE 802.1X. Prism2 documentation seem to require port reset - * after WEP configuration. However, keys are apparently changed at - * least in Managed mode. - */ - if (ret == 0 && - (hostap_set_encryption(local) || - (local->iw_mode != IW_MODE_INFRA && - local->func->reset_port(local->dev)))) - ret = -EINVAL; - - return ret; -} - - -static int prism2_ioctl_giwencodeext(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *extra) -{ - struct hostap_interface *iface = netdev_priv(dev); - local_info_t *local = iface->local; - struct lib80211_crypt_data **crypt; - void *sta_ptr; - int max_key_len, i; - struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; - u8 *addr; - - max_key_len = erq->length - sizeof(*ext); - if (max_key_len < 0) - return -EINVAL; - - i = erq->flags & IW_ENCODE_INDEX; - if (i < 1 || i > WEP_KEYS) - i = local->crypt_info.tx_keyidx; - else - i--; - - addr = ext->addr.sa_data; - if (is_broadcast_ether_addr(addr)) { - sta_ptr = NULL; - crypt = &local->crypt_info.crypt[i]; - } else { - i = 0; - sta_ptr = ap_crypt_get_ptrs(local->ap, addr, 0, &crypt); - if (sta_ptr == NULL) - return -EINVAL; - } - erq->flags = i + 1; - memset(ext, 0, sizeof(*ext)); - - if (*crypt == NULL || (*crypt)->ops == NULL) { - ext->alg = IW_ENCODE_ALG_NONE; - ext->key_len = 0; - erq->flags |= IW_ENCODE_DISABLED; - } else { - if (strcmp((*crypt)->ops->name, "WEP") == 0) - ext->alg = IW_ENCODE_ALG_WEP; - else if (strcmp((*crypt)->ops->name, "TKIP") == 0) - ext->alg = IW_ENCODE_ALG_TKIP; - else if (strcmp((*crypt)->ops->name, "CCMP") == 0) - ext->alg = IW_ENCODE_ALG_CCMP; - else - return -EINVAL; - - if ((*crypt)->ops->get_key) { - ext->key_len = - (*crypt)->ops->get_key(ext->key, - max_key_len, - ext->tx_seq, - (*crypt)->priv); - if (ext->key_len && - (ext->alg == IW_ENCODE_ALG_TKIP || - ext->alg == IW_ENCODE_ALG_CCMP)) - ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID; - } - } - - if (sta_ptr) - hostap_handle_sta_release(sta_ptr); - - return 0; -} - - -static int prism2_ioctl_set_encryption(local_info_t *local, - struct prism2_hostapd_param *param, - int param_len) -{ - int ret = 0; - struct lib80211_crypto_ops *ops; - struct lib80211_crypt_data **crypt; - void *sta_ptr; - - param->u.crypt.err = 0; - param->u.crypt.alg[HOSTAP_CRYPT_ALG_NAME_LEN - 1] = '\0'; - - if (param_len != - (int) ((char *) param->u.crypt.key - (char *) param) + - param->u.crypt.key_len) - return -EINVAL; - - if (is_broadcast_ether_addr(param->sta_addr)) { - if (param->u.crypt.idx >= WEP_KEYS) - return -EINVAL; - sta_ptr = NULL; - crypt = &local->crypt_info.crypt[param->u.crypt.idx]; - } else { - if (param->u.crypt.idx) - return -EINVAL; - sta_ptr = ap_crypt_get_ptrs( - local->ap, param->sta_addr, - (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_PERMANENT), - &crypt); - - if (sta_ptr == NULL) { - param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; - return -EINVAL; - } - } - - if (strcmp(param->u.crypt.alg, "none") == 0) { - if (crypt) - lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); - goto done; - } - - ops = lib80211_get_crypto_ops(param->u.crypt.alg); - if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) { - request_module("lib80211_crypt_wep"); - ops = lib80211_get_crypto_ops(param->u.crypt.alg); - } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) { - request_module("lib80211_crypt_tkip"); - ops = lib80211_get_crypto_ops(param->u.crypt.alg); - } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) { - request_module("lib80211_crypt_ccmp"); - ops = lib80211_get_crypto_ops(param->u.crypt.alg); - } - if (ops == NULL) { - printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n", - local->dev->name, param->u.crypt.alg); - param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ALG; - ret = -EINVAL; - goto done; - } - - /* station based encryption and other than WEP algorithms require - * host-based encryption, so force them on automatically */ - local->host_decrypt = local->host_encrypt = 1; - - if (*crypt == NULL || (*crypt)->ops != ops) { - struct lib80211_crypt_data *new_crypt; - - lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); - - new_crypt = kzalloc(sizeof(struct lib80211_crypt_data), - GFP_KERNEL); - if (new_crypt == NULL) { - ret = -ENOMEM; - goto done; - } - new_crypt->ops = ops; - new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx); - if (new_crypt->priv == NULL) { - kfree(new_crypt); - param->u.crypt.err = - HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED; - ret = -EINVAL; - goto done; - } - - *crypt = new_crypt; - } - - if ((!(param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) || - param->u.crypt.key_len > 0) && (*crypt)->ops->set_key && - (*crypt)->ops->set_key(param->u.crypt.key, - param->u.crypt.key_len, param->u.crypt.seq, - (*crypt)->priv) < 0) { - printk(KERN_DEBUG "%s: key setting failed\n", - local->dev->name); - param->u.crypt.err = HOSTAP_CRYPT_ERR_KEY_SET_FAILED; - ret = -EINVAL; - goto done; - } - - if (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) { - if (!sta_ptr) - local->crypt_info.tx_keyidx = param->u.crypt.idx; - else if (param->u.crypt.idx) { - printk(KERN_DEBUG "%s: TX key idx setting failed\n", - local->dev->name); - param->u.crypt.err = - HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED; - ret = -EINVAL; - goto done; - } - } - - done: - if (sta_ptr) - hostap_handle_sta_release(sta_ptr); - - /* Do not reset port0 if card is in Managed mode since resetting will - * generate new IEEE 802.11 authentication which may end up in looping - * with IEEE 802.1X. Prism2 documentation seem to require port reset - * after WEP configuration. However, keys are apparently changed at - * least in Managed mode. */ - if (ret == 0 && - (hostap_set_encryption(local) || - (local->iw_mode != IW_MODE_INFRA && - local->func->reset_port(local->dev)))) { - param->u.crypt.err = HOSTAP_CRYPT_ERR_CARD_CONF_FAILED; - return -EINVAL; - } - - return ret; -} - - -static int prism2_ioctl_get_encryption(local_info_t *local, - struct prism2_hostapd_param *param, - int param_len) -{ - struct lib80211_crypt_data **crypt; - void *sta_ptr; - int max_key_len; - - param->u.crypt.err = 0; - - max_key_len = param_len - - (int) ((char *) param->u.crypt.key - (char *) param); - if (max_key_len < 0) - return -EINVAL; - - if (is_broadcast_ether_addr(param->sta_addr)) { - sta_ptr = NULL; - if (param->u.crypt.idx >= WEP_KEYS) - param->u.crypt.idx = local->crypt_info.tx_keyidx; - crypt = &local->crypt_info.crypt[param->u.crypt.idx]; - } else { - param->u.crypt.idx = 0; - sta_ptr = ap_crypt_get_ptrs(local->ap, param->sta_addr, 0, - &crypt); - - if (sta_ptr == NULL) { - param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; - return -EINVAL; - } - } - - if (*crypt == NULL || (*crypt)->ops == NULL) { - memcpy(param->u.crypt.alg, "none", 5); - param->u.crypt.key_len = 0; - param->u.crypt.idx = 0xff; - } else { - strncpy(param->u.crypt.alg, (*crypt)->ops->name, - HOSTAP_CRYPT_ALG_NAME_LEN); - param->u.crypt.key_len = 0; - - memset(param->u.crypt.seq, 0, 8); - if ((*crypt)->ops->get_key) { - param->u.crypt.key_len = - (*crypt)->ops->get_key(param->u.crypt.key, - max_key_len, - param->u.crypt.seq, - (*crypt)->priv); - } - } - - if (sta_ptr) - hostap_handle_sta_release(sta_ptr); - - return 0; -} - - -static int prism2_ioctl_get_rid(local_info_t *local, - struct prism2_hostapd_param *param, - int param_len) -{ - int max_len, res; - - max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN; - if (max_len < 0) - return -EINVAL; - - res = local->func->get_rid(local->dev, param->u.rid.rid, - param->u.rid.data, param->u.rid.len, 0); - if (res >= 0) { - param->u.rid.len = res; - return 0; - } - - return res; -} - - -static int prism2_ioctl_set_rid(local_info_t *local, - struct prism2_hostapd_param *param, - int param_len) -{ - int max_len; - - max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN; - if (max_len < 0 || max_len < param->u.rid.len) - return -EINVAL; - - return local->func->set_rid(local->dev, param->u.rid.rid, - param->u.rid.data, param->u.rid.len); -} - - -static int prism2_ioctl_set_assoc_ap_addr(local_info_t *local, - struct prism2_hostapd_param *param, - int param_len) -{ - printk(KERN_DEBUG "%ssta: associated as client with AP %pM\n", - local->dev->name, param->sta_addr); - memcpy(local->assoc_ap_addr, param->sta_addr, ETH_ALEN); - return 0; -} - - -static int prism2_ioctl_siwgenie(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - return prism2_set_genericelement(dev, extra, data->length); -} - - -static int prism2_ioctl_giwgenie(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct hostap_interface *iface = netdev_priv(dev); - local_info_t *local = iface->local; - int len = local->generic_elem_len - 2; - - if (len <= 0 || local->generic_elem == NULL) { - data->length = 0; - return 0; - } - - if (data->length < len) - return -E2BIG; - - data->length = len; - memcpy(extra, local->generic_elem + 2, len); - - return 0; -} - - -static int prism2_ioctl_set_generic_element(local_info_t *local, - struct prism2_hostapd_param *param, - int param_len) -{ - int max_len, len; - - len = param->u.generic_elem.len; - max_len = param_len - PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; - if (max_len < 0 || max_len < len) - return -EINVAL; - - return prism2_set_genericelement(local->dev, - param->u.generic_elem.data, len); -} - - -static int prism2_ioctl_siwmlme(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct hostap_interface *iface = netdev_priv(dev); - local_info_t *local = iface->local; - struct iw_mlme *mlme = (struct iw_mlme *) extra; - __le16 reason; - - reason = cpu_to_le16(mlme->reason_code); - - switch (mlme->cmd) { - case IW_MLME_DEAUTH: - return prism2_sta_send_mgmt(local, mlme->addr.sa_data, - IEEE80211_STYPE_DEAUTH, - (u8 *) &reason, 2); - case IW_MLME_DISASSOC: - return prism2_sta_send_mgmt(local, mlme->addr.sa_data, - IEEE80211_STYPE_DISASSOC, - (u8 *) &reason, 2); - default: - return -EOPNOTSUPP; - } -} - - -static int prism2_ioctl_mlme(local_info_t *local, - struct prism2_hostapd_param *param) -{ - __le16 reason; - - reason = cpu_to_le16(param->u.mlme.reason_code); - switch (param->u.mlme.cmd) { - case MLME_STA_DEAUTH: - return prism2_sta_send_mgmt(local, param->sta_addr, - IEEE80211_STYPE_DEAUTH, - (u8 *) &reason, 2); - case MLME_STA_DISASSOC: - return prism2_sta_send_mgmt(local, param->sta_addr, - IEEE80211_STYPE_DISASSOC, - (u8 *) &reason, 2); - default: - return -EOPNOTSUPP; - } -} - - -static int prism2_ioctl_scan_req(local_info_t *local, - struct prism2_hostapd_param *param) -{ -#ifndef PRISM2_NO_STATION_MODES - if ((local->iw_mode != IW_MODE_INFRA && - local->iw_mode != IW_MODE_ADHOC) || - (local->sta_fw_ver < PRISM2_FW_VER(1,3,1))) - return -EOPNOTSUPP; - - if (!local->dev_enabled) - return -ENETDOWN; - - return prism2_request_hostscan(local->dev, param->u.scan_req.ssid, - param->u.scan_req.ssid_len); -#else /* PRISM2_NO_STATION_MODES */ - return -EOPNOTSUPP; -#endif /* PRISM2_NO_STATION_MODES */ -} - - -static int prism2_ioctl_priv_hostapd(local_info_t *local, struct iw_point *p) -{ - struct prism2_hostapd_param *param; - int ret = 0; - int ap_ioctl = 0; - - if (p->length < sizeof(struct prism2_hostapd_param) || - p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer) - return -EINVAL; - - param = kmalloc(p->length, GFP_KERNEL); - if (param == NULL) - return -ENOMEM; - - if (copy_from_user(param, p->pointer, p->length)) { - ret = -EFAULT; - goto out; - } - - switch (param->cmd) { - case PRISM2_SET_ENCRYPTION: - ret = prism2_ioctl_set_encryption(local, param, p->length); - break; - case PRISM2_GET_ENCRYPTION: - ret = prism2_ioctl_get_encryption(local, param, p->length); - break; - case PRISM2_HOSTAPD_GET_RID: - ret = prism2_ioctl_get_rid(local, param, p->length); - break; - case PRISM2_HOSTAPD_SET_RID: - ret = prism2_ioctl_set_rid(local, param, p->length); - break; - case PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR: - ret = prism2_ioctl_set_assoc_ap_addr(local, param, p->length); - break; - case PRISM2_HOSTAPD_SET_GENERIC_ELEMENT: - ret = prism2_ioctl_set_generic_element(local, param, - p->length); - break; - case PRISM2_HOSTAPD_MLME: - ret = prism2_ioctl_mlme(local, param); - break; - case PRISM2_HOSTAPD_SCAN_REQ: - ret = prism2_ioctl_scan_req(local, param); - break; - default: - ret = prism2_hostapd(local->ap, param); - ap_ioctl = 1; - break; - } - - if (ret == 1 || !ap_ioctl) { - if (copy_to_user(p->pointer, param, p->length)) { - ret = -EFAULT; - goto out; - } else if (ap_ioctl) - ret = 0; - } - - out: - kfree(param); - return ret; -} - - -static void prism2_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - - strlcpy(info->driver, "hostap", sizeof(info->driver)); - snprintf(info->fw_version, sizeof(info->fw_version), - "%d.%d.%d", (local->sta_fw_ver >> 16) & 0xff, - (local->sta_fw_ver >> 8) & 0xff, - local->sta_fw_ver & 0xff); -} - -const struct ethtool_ops prism2_ethtool_ops = { - .get_drvinfo = prism2_get_drvinfo -}; - - -/* Structures to export the Wireless Handlers */ - -static const iw_handler prism2_handler[] = -{ - (iw_handler) NULL, /* SIOCSIWCOMMIT */ - (iw_handler) prism2_get_name, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) prism2_ioctl_siwfreq, /* SIOCSIWFREQ */ - (iw_handler) prism2_ioctl_giwfreq, /* SIOCGIWFREQ */ - (iw_handler) prism2_ioctl_siwmode, /* SIOCSIWMODE */ - (iw_handler) prism2_ioctl_giwmode, /* SIOCGIWMODE */ - (iw_handler) prism2_ioctl_siwsens, /* SIOCSIWSENS */ - (iw_handler) prism2_ioctl_giwsens, /* SIOCGIWSENS */ - (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ - (iw_handler) prism2_ioctl_giwrange, /* SIOCGIWRANGE */ - (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ - (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ - (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ - (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ - iw_handler_set_spy, /* SIOCSIWSPY */ - iw_handler_get_spy, /* SIOCGIWSPY */ - iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ - iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ - (iw_handler) prism2_ioctl_siwap, /* SIOCSIWAP */ - (iw_handler) prism2_ioctl_giwap, /* SIOCGIWAP */ - (iw_handler) prism2_ioctl_siwmlme, /* SIOCSIWMLME */ - (iw_handler) prism2_ioctl_giwaplist, /* SIOCGIWAPLIST */ - (iw_handler) prism2_ioctl_siwscan, /* SIOCSIWSCAN */ - (iw_handler) prism2_ioctl_giwscan, /* SIOCGIWSCAN */ - (iw_handler) prism2_ioctl_siwessid, /* SIOCSIWESSID */ - (iw_handler) prism2_ioctl_giwessid, /* SIOCGIWESSID */ - (iw_handler) prism2_ioctl_siwnickn, /* SIOCSIWNICKN */ - (iw_handler) prism2_ioctl_giwnickn, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) prism2_ioctl_siwrate, /* SIOCSIWRATE */ - (iw_handler) prism2_ioctl_giwrate, /* SIOCGIWRATE */ - (iw_handler) prism2_ioctl_siwrts, /* SIOCSIWRTS */ - (iw_handler) prism2_ioctl_giwrts, /* SIOCGIWRTS */ - (iw_handler) prism2_ioctl_siwfrag, /* SIOCSIWFRAG */ - (iw_handler) prism2_ioctl_giwfrag, /* SIOCGIWFRAG */ - (iw_handler) prism2_ioctl_siwtxpow, /* SIOCSIWTXPOW */ - (iw_handler) prism2_ioctl_giwtxpow, /* SIOCGIWTXPOW */ - (iw_handler) prism2_ioctl_siwretry, /* SIOCSIWRETRY */ - (iw_handler) prism2_ioctl_giwretry, /* SIOCGIWRETRY */ - (iw_handler) prism2_ioctl_siwencode, /* SIOCSIWENCODE */ - (iw_handler) prism2_ioctl_giwencode, /* SIOCGIWENCODE */ - (iw_handler) prism2_ioctl_siwpower, /* SIOCSIWPOWER */ - (iw_handler) prism2_ioctl_giwpower, /* SIOCGIWPOWER */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) prism2_ioctl_siwgenie, /* SIOCSIWGENIE */ - (iw_handler) prism2_ioctl_giwgenie, /* SIOCGIWGENIE */ - (iw_handler) prism2_ioctl_siwauth, /* SIOCSIWAUTH */ - (iw_handler) prism2_ioctl_giwauth, /* SIOCGIWAUTH */ - (iw_handler) prism2_ioctl_siwencodeext, /* SIOCSIWENCODEEXT */ - (iw_handler) prism2_ioctl_giwencodeext, /* SIOCGIWENCODEEXT */ - (iw_handler) NULL, /* SIOCSIWPMKSA */ - (iw_handler) NULL, /* -- hole -- */ -}; - -static const iw_handler prism2_private_handler[] = -{ /* SIOCIWFIRSTPRIV + */ - (iw_handler) prism2_ioctl_priv_prism2_param, /* 0 */ - (iw_handler) prism2_ioctl_priv_get_prism2_param, /* 1 */ - (iw_handler) prism2_ioctl_priv_writemif, /* 2 */ - (iw_handler) prism2_ioctl_priv_readmif, /* 3 */ -}; - -const struct iw_handler_def hostap_iw_handler_def = -{ - .num_standard = ARRAY_SIZE(prism2_handler), - .num_private = ARRAY_SIZE(prism2_private_handler), - .num_private_args = ARRAY_SIZE(prism2_priv), - .standard = (iw_handler *) prism2_handler, - .private = (iw_handler *) prism2_private_handler, - .private_args = (struct iw_priv_args *) prism2_priv, - .get_wireless_stats = hostap_get_wireless_stats, -}; - - -int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct iwreq *wrq = (struct iwreq *) ifr; - struct hostap_interface *iface; - local_info_t *local; - int ret = 0; - - iface = netdev_priv(dev); - local = iface->local; - - switch (cmd) { - /* Private ioctls (iwpriv) that have not yet been converted - * into new wireless extensions API */ - - case PRISM2_IOCTL_INQUIRE: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = prism2_ioctl_priv_inquire(dev, (int *) wrq->u.name); - break; - - case PRISM2_IOCTL_MONITOR: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = prism2_ioctl_priv_monitor(dev, (int *) wrq->u.name); - break; - - case PRISM2_IOCTL_RESET: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = prism2_ioctl_priv_reset(dev, (int *) wrq->u.name); - break; - - case PRISM2_IOCTL_WDS_ADD: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = prism2_wds_add(local, wrq->u.ap_addr.sa_data, 1); - break; - - case PRISM2_IOCTL_WDS_DEL: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = prism2_wds_del(local, wrq->u.ap_addr.sa_data, 1, 0); - break; - - case PRISM2_IOCTL_SET_RID_WORD: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = prism2_ioctl_priv_set_rid_word(dev, - (int *) wrq->u.name); - break; - -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - case PRISM2_IOCTL_MACCMD: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = ap_mac_cmd_ioctl(local, (int *) wrq->u.name); - break; - - case PRISM2_IOCTL_ADDMAC: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = ap_control_add_mac(&local->ap->mac_restrictions, - wrq->u.ap_addr.sa_data); - break; - case PRISM2_IOCTL_DELMAC: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = ap_control_del_mac(&local->ap->mac_restrictions, - wrq->u.ap_addr.sa_data); - break; - case PRISM2_IOCTL_KICKMAC: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = ap_control_kick_mac(local->ap, local->dev, - wrq->u.ap_addr.sa_data); - break; -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - - /* Private ioctls that are not used with iwpriv; - * in SIOCDEVPRIVATE range */ - -#ifdef PRISM2_DOWNLOAD_SUPPORT - case PRISM2_IOCTL_DOWNLOAD: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = prism2_ioctl_priv_download(local, &wrq->u.data); - break; -#endif /* PRISM2_DOWNLOAD_SUPPORT */ - - case PRISM2_IOCTL_HOSTAPD: - if (!capable(CAP_NET_ADMIN)) ret = -EPERM; - else ret = prism2_ioctl_priv_hostapd(local, &wrq->u.data); - break; - - default: - ret = -EOPNOTSUPP; - break; - } - - return ret; -} diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c deleted file mode 100644 index 80d4228ba754..000000000000 --- a/drivers/net/wireless/hostap/hostap_main.c +++ /dev/null @@ -1,1140 +0,0 @@ -/* - * Host AP (software wireless LAN access point) driver for - * Intersil Prism2/2.5/3 - hostap.o module, common routines - * - * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. See README and COPYING for - * more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hostap_wlan.h" -#include "hostap_80211.h" -#include "hostap_ap.h" -#include "hostap.h" - -MODULE_AUTHOR("Jouni Malinen"); -MODULE_DESCRIPTION("Host AP common routines"); -MODULE_LICENSE("GPL"); - -#define TX_TIMEOUT (2 * HZ) - -#define PRISM2_MAX_FRAME_SIZE 2304 -#define PRISM2_MIN_MTU 256 -/* FIX: */ -#define PRISM2_MAX_MTU (PRISM2_MAX_FRAME_SIZE - (6 /* LLC */ + 8 /* WEP */)) - - -struct net_device * hostap_add_interface(struct local_info *local, - int type, int rtnl_locked, - const char *prefix, - const char *name) -{ - struct net_device *dev, *mdev; - struct hostap_interface *iface; - int ret; - - dev = alloc_etherdev(sizeof(struct hostap_interface)); - if (dev == NULL) - return NULL; - - iface = netdev_priv(dev); - iface->dev = dev; - iface->local = local; - iface->type = type; - list_add(&iface->list, &local->hostap_interfaces); - - mdev = local->dev; - eth_hw_addr_inherit(dev, mdev); - dev->base_addr = mdev->base_addr; - dev->irq = mdev->irq; - dev->mem_start = mdev->mem_start; - dev->mem_end = mdev->mem_end; - - hostap_setup_dev(dev, local, type); - dev->destructor = free_netdev; - - sprintf(dev->name, "%s%s", prefix, name); - if (!rtnl_locked) - rtnl_lock(); - - SET_NETDEV_DEV(dev, mdev->dev.parent); - ret = register_netdevice(dev); - - if (!rtnl_locked) - rtnl_unlock(); - - if (ret < 0) { - printk(KERN_WARNING "%s: failed to add new netdevice!\n", - dev->name); - free_netdev(dev); - return NULL; - } - - printk(KERN_DEBUG "%s: registered netdevice %s\n", - mdev->name, dev->name); - - return dev; -} - - -void hostap_remove_interface(struct net_device *dev, int rtnl_locked, - int remove_from_list) -{ - struct hostap_interface *iface; - - if (!dev) - return; - - iface = netdev_priv(dev); - - if (remove_from_list) { - list_del(&iface->list); - } - - if (dev == iface->local->ddev) - iface->local->ddev = NULL; - else if (dev == iface->local->apdev) - iface->local->apdev = NULL; - else if (dev == iface->local->stadev) - iface->local->stadev = NULL; - - if (rtnl_locked) - unregister_netdevice(dev); - else - unregister_netdev(dev); - - /* dev->destructor = free_netdev() will free the device data, including - * private data, when removing the device */ -} - - -static inline int prism2_wds_special_addr(u8 *addr) -{ - if (addr[0] || addr[1] || addr[2] || addr[3] || addr[4] || addr[5]) - return 0; - - return 1; -} - - -int prism2_wds_add(local_info_t *local, u8 *remote_addr, - int rtnl_locked) -{ - struct net_device *dev; - struct list_head *ptr; - struct hostap_interface *iface, *empty, *match; - - empty = match = NULL; - read_lock_bh(&local->iface_lock); - list_for_each(ptr, &local->hostap_interfaces) { - iface = list_entry(ptr, struct hostap_interface, list); - if (iface->type != HOSTAP_INTERFACE_WDS) - continue; - - if (prism2_wds_special_addr(iface->u.wds.remote_addr)) - empty = iface; - else if (ether_addr_equal(iface->u.wds.remote_addr, remote_addr)) { - match = iface; - break; - } - } - if (!match && empty && !prism2_wds_special_addr(remote_addr)) { - /* take pre-allocated entry into use */ - memcpy(empty->u.wds.remote_addr, remote_addr, ETH_ALEN); - read_unlock_bh(&local->iface_lock); - printk(KERN_DEBUG "%s: using pre-allocated WDS netdevice %s\n", - local->dev->name, empty->dev->name); - return 0; - } - read_unlock_bh(&local->iface_lock); - - if (!prism2_wds_special_addr(remote_addr)) { - if (match) - return -EEXIST; - hostap_add_sta(local->ap, remote_addr); - } - - if (local->wds_connections >= local->wds_max_connections) - return -ENOBUFS; - - /* verify that there is room for wds# postfix in the interface name */ - if (strlen(local->dev->name) >= IFNAMSIZ - 5) { - printk(KERN_DEBUG "'%s' too long base device name\n", - local->dev->name); - return -EINVAL; - } - - dev = hostap_add_interface(local, HOSTAP_INTERFACE_WDS, rtnl_locked, - local->ddev->name, "wds%d"); - if (dev == NULL) - return -ENOMEM; - - iface = netdev_priv(dev); - memcpy(iface->u.wds.remote_addr, remote_addr, ETH_ALEN); - - local->wds_connections++; - - return 0; -} - - -int prism2_wds_del(local_info_t *local, u8 *remote_addr, - int rtnl_locked, int do_not_remove) -{ - unsigned long flags; - struct list_head *ptr; - struct hostap_interface *iface, *selected = NULL; - - write_lock_irqsave(&local->iface_lock, flags); - list_for_each(ptr, &local->hostap_interfaces) { - iface = list_entry(ptr, struct hostap_interface, list); - if (iface->type != HOSTAP_INTERFACE_WDS) - continue; - - if (ether_addr_equal(iface->u.wds.remote_addr, remote_addr)) { - selected = iface; - break; - } - } - if (selected && !do_not_remove) - list_del(&selected->list); - write_unlock_irqrestore(&local->iface_lock, flags); - - if (selected) { - if (do_not_remove) - eth_zero_addr(selected->u.wds.remote_addr); - else { - hostap_remove_interface(selected->dev, rtnl_locked, 0); - local->wds_connections--; - } - } - - return selected ? 0 : -ENODEV; -} - - -u16 hostap_tx_callback_register(local_info_t *local, - void (*func)(struct sk_buff *, int ok, void *), - void *data) -{ - unsigned long flags; - struct hostap_tx_callback_info *entry; - - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (entry == NULL) - return 0; - - entry->func = func; - entry->data = data; - - spin_lock_irqsave(&local->lock, flags); - entry->idx = local->tx_callback ? local->tx_callback->idx + 1 : 1; - entry->next = local->tx_callback; - local->tx_callback = entry; - spin_unlock_irqrestore(&local->lock, flags); - - return entry->idx; -} - - -int hostap_tx_callback_unregister(local_info_t *local, u16 idx) -{ - unsigned long flags; - struct hostap_tx_callback_info *cb, *prev = NULL; - - spin_lock_irqsave(&local->lock, flags); - cb = local->tx_callback; - while (cb != NULL && cb->idx != idx) { - prev = cb; - cb = cb->next; - } - if (cb) { - if (prev == NULL) - local->tx_callback = cb->next; - else - prev->next = cb->next; - kfree(cb); - } - spin_unlock_irqrestore(&local->lock, flags); - - return cb ? 0 : -1; -} - - -/* val is in host byte order */ -int hostap_set_word(struct net_device *dev, int rid, u16 val) -{ - struct hostap_interface *iface; - __le16 tmp = cpu_to_le16(val); - iface = netdev_priv(dev); - return iface->local->func->set_rid(dev, rid, &tmp, 2); -} - - -int hostap_set_string(struct net_device *dev, int rid, const char *val) -{ - struct hostap_interface *iface; - char buf[MAX_SSID_LEN + 2]; - int len; - - iface = netdev_priv(dev); - len = strlen(val); - if (len > MAX_SSID_LEN) - return -1; - memset(buf, 0, sizeof(buf)); - buf[0] = len; /* little endian 16 bit word */ - memcpy(buf + 2, val, len); - - return iface->local->func->set_rid(dev, rid, &buf, MAX_SSID_LEN + 2); -} - - -u16 hostap_get_porttype(local_info_t *local) -{ - if (local->iw_mode == IW_MODE_ADHOC && local->pseudo_adhoc) - return HFA384X_PORTTYPE_PSEUDO_IBSS; - if (local->iw_mode == IW_MODE_ADHOC) - return HFA384X_PORTTYPE_IBSS; - if (local->iw_mode == IW_MODE_INFRA) - return HFA384X_PORTTYPE_BSS; - if (local->iw_mode == IW_MODE_REPEAT) - return HFA384X_PORTTYPE_WDS; - if (local->iw_mode == IW_MODE_MONITOR) - return HFA384X_PORTTYPE_PSEUDO_IBSS; - return HFA384X_PORTTYPE_HOSTAP; -} - - -int hostap_set_encryption(local_info_t *local) -{ - u16 val, old_val; - int i, keylen, len, idx; - char keybuf[WEP_KEY_LEN + 1]; - enum { NONE, WEP, OTHER } encrypt_type; - - idx = local->crypt_info.tx_keyidx; - if (local->crypt_info.crypt[idx] == NULL || - local->crypt_info.crypt[idx]->ops == NULL) - encrypt_type = NONE; - else if (strcmp(local->crypt_info.crypt[idx]->ops->name, "WEP") == 0) - encrypt_type = WEP; - else - encrypt_type = OTHER; - - if (local->func->get_rid(local->dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, - 1) < 0) { - printk(KERN_DEBUG "Could not read current WEP flags.\n"); - goto fail; - } - le16_to_cpus(&val); - old_val = val; - - if (encrypt_type != NONE || local->privacy_invoked) - val |= HFA384X_WEPFLAGS_PRIVACYINVOKED; - else - val &= ~HFA384X_WEPFLAGS_PRIVACYINVOKED; - - if (local->open_wep || encrypt_type == NONE || - ((local->ieee_802_1x || local->wpa) && local->host_decrypt)) - val &= ~HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; - else - val |= HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; - - if ((encrypt_type != NONE || local->privacy_invoked) && - (encrypt_type == OTHER || local->host_encrypt)) - val |= HFA384X_WEPFLAGS_HOSTENCRYPT; - else - val &= ~HFA384X_WEPFLAGS_HOSTENCRYPT; - if ((encrypt_type != NONE || local->privacy_invoked) && - (encrypt_type == OTHER || local->host_decrypt)) - val |= HFA384X_WEPFLAGS_HOSTDECRYPT; - else - val &= ~HFA384X_WEPFLAGS_HOSTDECRYPT; - - if (val != old_val && - hostap_set_word(local->dev, HFA384X_RID_CNFWEPFLAGS, val)) { - printk(KERN_DEBUG "Could not write new WEP flags (0x%x)\n", - val); - goto fail; - } - - if (encrypt_type != WEP) - return 0; - - /* 104-bit support seems to require that all the keys are set to the - * same keylen */ - keylen = 6; /* first 5 octets */ - len = local->crypt_info.crypt[idx]->ops->get_key(keybuf, sizeof(keybuf), NULL, - local->crypt_info.crypt[idx]->priv); - if (idx >= 0 && idx < WEP_KEYS && len > 5) - keylen = WEP_KEY_LEN + 1; /* first 13 octets */ - - for (i = 0; i < WEP_KEYS; i++) { - memset(keybuf, 0, sizeof(keybuf)); - if (local->crypt_info.crypt[i]) { - (void) local->crypt_info.crypt[i]->ops->get_key( - keybuf, sizeof(keybuf), - NULL, local->crypt_info.crypt[i]->priv); - } - if (local->func->set_rid(local->dev, - HFA384X_RID_CNFDEFAULTKEY0 + i, - keybuf, keylen)) { - printk(KERN_DEBUG "Could not set key %d (len=%d)\n", - i, keylen); - goto fail; - } - } - if (hostap_set_word(local->dev, HFA384X_RID_CNFWEPDEFAULTKEYID, idx)) { - printk(KERN_DEBUG "Could not set default keyid %d\n", idx); - goto fail; - } - - return 0; - - fail: - printk(KERN_DEBUG "%s: encryption setup failed\n", local->dev->name); - return -1; -} - - -int hostap_set_antsel(local_info_t *local) -{ - u16 val; - int ret = 0; - - if (local->antsel_tx != HOSTAP_ANTSEL_DO_NOT_TOUCH && - local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF, - HFA386X_CR_TX_CONFIGURE, - NULL, &val) == 0) { - val &= ~(BIT(2) | BIT(1)); - switch (local->antsel_tx) { - case HOSTAP_ANTSEL_DIVERSITY: - val |= BIT(1); - break; - case HOSTAP_ANTSEL_LOW: - break; - case HOSTAP_ANTSEL_HIGH: - val |= BIT(2); - break; - } - - if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF, - HFA386X_CR_TX_CONFIGURE, &val, NULL)) { - printk(KERN_INFO "%s: setting TX AntSel failed\n", - local->dev->name); - ret = -1; - } - } - - if (local->antsel_rx != HOSTAP_ANTSEL_DO_NOT_TOUCH && - local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF, - HFA386X_CR_RX_CONFIGURE, - NULL, &val) == 0) { - val &= ~(BIT(1) | BIT(0)); - switch (local->antsel_rx) { - case HOSTAP_ANTSEL_DIVERSITY: - break; - case HOSTAP_ANTSEL_LOW: - val |= BIT(0); - break; - case HOSTAP_ANTSEL_HIGH: - val |= BIT(0) | BIT(1); - break; - } - - if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF, - HFA386X_CR_RX_CONFIGURE, &val, NULL)) { - printk(KERN_INFO "%s: setting RX AntSel failed\n", - local->dev->name); - ret = -1; - } - } - - return ret; -} - - -int hostap_set_roaming(local_info_t *local) -{ - u16 val; - - switch (local->host_roaming) { - case 1: - val = HFA384X_ROAMING_HOST; - break; - case 2: - val = HFA384X_ROAMING_DISABLED; - break; - case 0: - default: - val = HFA384X_ROAMING_FIRMWARE; - break; - } - - return hostap_set_word(local->dev, HFA384X_RID_CNFROAMINGMODE, val); -} - - -int hostap_set_auth_algs(local_info_t *local) -{ - int val = local->auth_algs; - /* At least STA f/w v0.6.2 seems to have issues with cnfAuthentication - * set to include both Open and Shared Key flags. It tries to use - * Shared Key authentication in that case even if WEP keys are not - * configured.. STA f/w v0.7.6 is able to handle such configuration, - * but it is unknown when this was fixed between 0.6.2 .. 0.7.6. */ - if (local->sta_fw_ver < PRISM2_FW_VER(0,7,0) && - val != PRISM2_AUTH_OPEN && val != PRISM2_AUTH_SHARED_KEY) - val = PRISM2_AUTH_OPEN; - - if (hostap_set_word(local->dev, HFA384X_RID_CNFAUTHENTICATION, val)) { - printk(KERN_INFO "%s: cnfAuthentication setting to 0x%x " - "failed\n", local->dev->name, local->auth_algs); - return -EINVAL; - } - - return 0; -} - - -void hostap_dump_rx_header(const char *name, const struct hfa384x_rx_frame *rx) -{ - u16 status, fc; - - status = __le16_to_cpu(rx->status); - - printk(KERN_DEBUG "%s: RX status=0x%04x (port=%d, type=%d, " - "fcserr=%d) silence=%d signal=%d rate=%d rxflow=%d; " - "jiffies=%ld\n", - name, status, (status >> 8) & 0x07, status >> 13, status & 1, - rx->silence, rx->signal, rx->rate, rx->rxflow, jiffies); - - fc = __le16_to_cpu(rx->frame_control); - printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x " - "data_len=%d%s%s\n", - fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, - (fc & IEEE80211_FCTL_STYPE) >> 4, - __le16_to_cpu(rx->duration_id), __le16_to_cpu(rx->seq_ctrl), - __le16_to_cpu(rx->data_len), - fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", - fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); - - printk(KERN_DEBUG " A1=%pM A2=%pM A3=%pM A4=%pM\n", - rx->addr1, rx->addr2, rx->addr3, rx->addr4); - - printk(KERN_DEBUG " dst=%pM src=%pM len=%d\n", - rx->dst_addr, rx->src_addr, - __be16_to_cpu(rx->len)); -} - - -void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx) -{ - u16 fc; - - printk(KERN_DEBUG "%s: TX status=0x%04x retry_count=%d tx_rate=%d " - "tx_control=0x%04x; jiffies=%ld\n", - name, __le16_to_cpu(tx->status), tx->retry_count, tx->tx_rate, - __le16_to_cpu(tx->tx_control), jiffies); - - fc = __le16_to_cpu(tx->frame_control); - printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x " - "data_len=%d%s%s\n", - fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, - (fc & IEEE80211_FCTL_STYPE) >> 4, - __le16_to_cpu(tx->duration_id), __le16_to_cpu(tx->seq_ctrl), - __le16_to_cpu(tx->data_len), - fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", - fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); - - printk(KERN_DEBUG " A1=%pM A2=%pM A3=%pM A4=%pM\n", - tx->addr1, tx->addr2, tx->addr3, tx->addr4); - - printk(KERN_DEBUG " dst=%pM src=%pM len=%d\n", - tx->dst_addr, tx->src_addr, - __be16_to_cpu(tx->len)); -} - - -static int hostap_80211_header_parse(const struct sk_buff *skb, - unsigned char *haddr) -{ - memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */ - return ETH_ALEN; -} - - -int hostap_80211_get_hdrlen(__le16 fc) -{ - if (ieee80211_is_data(fc) && ieee80211_has_a4 (fc)) - return 30; /* Addr4 */ - else if (ieee80211_is_cts(fc) || ieee80211_is_ack(fc)) - return 10; - else if (ieee80211_is_ctl(fc)) - return 16; - - return 24; -} - - -static int prism2_close(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - - PDEBUG(DEBUG_FLOW, "%s: prism2_close\n", dev->name); - - iface = netdev_priv(dev); - local = iface->local; - - if (dev == local->ddev) { - prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING); - } -#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT - if (!local->hostapd && dev == local->dev && - (!local->func->card_present || local->func->card_present(local)) && - local->hw_ready && local->ap && local->iw_mode == IW_MODE_MASTER) - hostap_deauth_all_stas(dev, local->ap, 1); -#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ - - if (dev == local->dev) { - local->func->hw_shutdown(dev, HOSTAP_HW_ENABLE_CMDCOMPL); - } - - if (netif_running(dev)) { - netif_stop_queue(dev); - netif_device_detach(dev); - } - - cancel_work_sync(&local->reset_queue); - cancel_work_sync(&local->set_multicast_list_queue); - cancel_work_sync(&local->set_tim_queue); -#ifndef PRISM2_NO_STATION_MODES - cancel_work_sync(&local->info_queue); -#endif - cancel_work_sync(&local->comms_qual_update); - - module_put(local->hw_module); - - local->num_dev_open--; - - if (dev != local->dev && local->dev->flags & IFF_UP && - local->master_dev_auto_open && local->num_dev_open == 1) { - /* Close master radio interface automatically if it was also - * opened automatically and we are now closing the last - * remaining non-master device. */ - dev_close(local->dev); - } - - return 0; -} - - -static int prism2_open(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - - PDEBUG(DEBUG_FLOW, "%s: prism2_open\n", dev->name); - - iface = netdev_priv(dev); - local = iface->local; - - if (local->no_pri) { - printk(KERN_DEBUG "%s: could not set interface UP - no PRI " - "f/w\n", dev->name); - return -ENODEV; - } - - if ((local->func->card_present && !local->func->card_present(local)) || - local->hw_downloading) - return -ENODEV; - - if (!try_module_get(local->hw_module)) - return -ENODEV; - local->num_dev_open++; - - if (!local->dev_enabled && local->func->hw_enable(dev, 1)) { - printk(KERN_WARNING "%s: could not enable MAC port\n", - dev->name); - prism2_close(dev); - return -ENODEV; - } - if (!local->dev_enabled) - prism2_callback(local, PRISM2_CALLBACK_ENABLE); - local->dev_enabled = 1; - - if (dev != local->dev && !(local->dev->flags & IFF_UP)) { - /* Master radio interface is needed for all operation, so open - * it automatically when any virtual net_device is opened. */ - local->master_dev_auto_open = 1; - dev_open(local->dev); - } - - netif_device_attach(dev); - netif_start_queue(dev); - - return 0; -} - - -static int prism2_set_mac_address(struct net_device *dev, void *p) -{ - struct hostap_interface *iface; - local_info_t *local; - struct list_head *ptr; - struct sockaddr *addr = p; - - iface = netdev_priv(dev); - local = iface->local; - - if (local->func->set_rid(dev, HFA384X_RID_CNFOWNMACADDR, addr->sa_data, - ETH_ALEN) < 0 || local->func->reset_port(dev)) - return -EINVAL; - - read_lock_bh(&local->iface_lock); - list_for_each(ptr, &local->hostap_interfaces) { - iface = list_entry(ptr, struct hostap_interface, list); - memcpy(iface->dev->dev_addr, addr->sa_data, ETH_ALEN); - } - memcpy(local->dev->dev_addr, addr->sa_data, ETH_ALEN); - read_unlock_bh(&local->iface_lock); - - return 0; -} - - -/* TODO: to be further implemented as soon as Prism2 fully supports - * GroupAddresses and correct documentation is available */ -void hostap_set_multicast_list_queue(struct work_struct *work) -{ - local_info_t *local = - container_of(work, local_info_t, set_multicast_list_queue); - struct net_device *dev = local->dev; - - if (hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, - local->is_promisc)) { - printk(KERN_INFO "%s: %sabling promiscuous mode failed\n", - dev->name, local->is_promisc ? "en" : "dis"); - } -} - - -static void hostap_set_multicast_list(struct net_device *dev) -{ -#if 0 - /* FIX: promiscuous mode seems to be causing a lot of problems with - * some station firmware versions (FCSErr frames, invalid MACPort, etc. - * corrupted incoming frames). This code is now commented out while the - * problems are investigated. */ - struct hostap_interface *iface; - local_info_t *local; - - iface = netdev_priv(dev); - local = iface->local; - if ((dev->flags & IFF_ALLMULTI) || (dev->flags & IFF_PROMISC)) { - local->is_promisc = 1; - } else { - local->is_promisc = 0; - } - - schedule_work(&local->set_multicast_list_queue); -#endif -} - - -static int prism2_change_mtu(struct net_device *dev, int new_mtu) -{ - if (new_mtu < PRISM2_MIN_MTU || new_mtu > PRISM2_MAX_MTU) - return -EINVAL; - - dev->mtu = new_mtu; - return 0; -} - - -static void prism2_tx_timeout(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - struct hfa384x_regs regs; - - iface = netdev_priv(dev); - local = iface->local; - - printk(KERN_WARNING "%s Tx timed out! Resetting card\n", dev->name); - netif_stop_queue(local->dev); - - local->func->read_regs(dev, ®s); - printk(KERN_DEBUG "%s: CMD=%04x EVSTAT=%04x " - "OFFSET0=%04x OFFSET1=%04x SWSUPPORT0=%04x\n", - dev->name, regs.cmd, regs.evstat, regs.offset0, regs.offset1, - regs.swsupport0); - - local->func->schedule_reset(local); -} - -const struct header_ops hostap_80211_ops = { - .create = eth_header, - .cache = eth_header_cache, - .cache_update = eth_header_cache_update, - .parse = hostap_80211_header_parse, -}; -EXPORT_SYMBOL(hostap_80211_ops); - - -static const struct net_device_ops hostap_netdev_ops = { - .ndo_start_xmit = hostap_data_start_xmit, - - .ndo_open = prism2_open, - .ndo_stop = prism2_close, - .ndo_do_ioctl = hostap_ioctl, - .ndo_set_mac_address = prism2_set_mac_address, - .ndo_set_rx_mode = hostap_set_multicast_list, - .ndo_change_mtu = prism2_change_mtu, - .ndo_tx_timeout = prism2_tx_timeout, - .ndo_validate_addr = eth_validate_addr, -}; - -static const struct net_device_ops hostap_mgmt_netdev_ops = { - .ndo_start_xmit = hostap_mgmt_start_xmit, - - .ndo_open = prism2_open, - .ndo_stop = prism2_close, - .ndo_do_ioctl = hostap_ioctl, - .ndo_set_mac_address = prism2_set_mac_address, - .ndo_set_rx_mode = hostap_set_multicast_list, - .ndo_change_mtu = prism2_change_mtu, - .ndo_tx_timeout = prism2_tx_timeout, - .ndo_validate_addr = eth_validate_addr, -}; - -static const struct net_device_ops hostap_master_ops = { - .ndo_start_xmit = hostap_master_start_xmit, - - .ndo_open = prism2_open, - .ndo_stop = prism2_close, - .ndo_do_ioctl = hostap_ioctl, - .ndo_set_mac_address = prism2_set_mac_address, - .ndo_set_rx_mode = hostap_set_multicast_list, - .ndo_change_mtu = prism2_change_mtu, - .ndo_tx_timeout = prism2_tx_timeout, - .ndo_validate_addr = eth_validate_addr, -}; - -void hostap_setup_dev(struct net_device *dev, local_info_t *local, - int type) -{ - struct hostap_interface *iface; - - iface = netdev_priv(dev); - ether_setup(dev); - dev->priv_flags &= ~IFF_TX_SKB_SHARING; - - /* kernel callbacks */ - if (iface) { - /* Currently, we point to the proper spy_data only on - * the main_dev. This could be fixed. Jean II */ - iface->wireless_data.spy_data = &iface->spy_data; - dev->wireless_data = &iface->wireless_data; - } - dev->wireless_handlers = &hostap_iw_handler_def; - dev->watchdog_timeo = TX_TIMEOUT; - - switch(type) { - case HOSTAP_INTERFACE_AP: - dev->priv_flags |= IFF_NO_QUEUE; /* use main radio device queue */ - dev->netdev_ops = &hostap_mgmt_netdev_ops; - dev->type = ARPHRD_IEEE80211; - dev->header_ops = &hostap_80211_ops; - break; - case HOSTAP_INTERFACE_MASTER: - dev->netdev_ops = &hostap_master_ops; - break; - default: - dev->priv_flags |= IFF_NO_QUEUE; /* use main radio device queue */ - dev->netdev_ops = &hostap_netdev_ops; - } - - dev->mtu = local->mtu; - - - dev->ethtool_ops = &prism2_ethtool_ops; - -} - -static int hostap_enable_hostapd(local_info_t *local, int rtnl_locked) -{ - struct net_device *dev = local->dev; - - if (local->apdev) - return -EEXIST; - - printk(KERN_DEBUG "%s: enabling hostapd mode\n", dev->name); - - local->apdev = hostap_add_interface(local, HOSTAP_INTERFACE_AP, - rtnl_locked, local->ddev->name, - "ap"); - if (local->apdev == NULL) - return -ENOMEM; - - return 0; -} - - -static int hostap_disable_hostapd(local_info_t *local, int rtnl_locked) -{ - struct net_device *dev = local->dev; - - printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name); - - hostap_remove_interface(local->apdev, rtnl_locked, 1); - local->apdev = NULL; - - return 0; -} - - -static int hostap_enable_hostapd_sta(local_info_t *local, int rtnl_locked) -{ - struct net_device *dev = local->dev; - - if (local->stadev) - return -EEXIST; - - printk(KERN_DEBUG "%s: enabling hostapd STA mode\n", dev->name); - - local->stadev = hostap_add_interface(local, HOSTAP_INTERFACE_STA, - rtnl_locked, local->ddev->name, - "sta"); - if (local->stadev == NULL) - return -ENOMEM; - - return 0; -} - - -static int hostap_disable_hostapd_sta(local_info_t *local, int rtnl_locked) -{ - struct net_device *dev = local->dev; - - printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name); - - hostap_remove_interface(local->stadev, rtnl_locked, 1); - local->stadev = NULL; - - return 0; -} - - -int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked) -{ - int ret; - - if (val < 0 || val > 1) - return -EINVAL; - - if (local->hostapd == val) - return 0; - - if (val) { - ret = hostap_enable_hostapd(local, rtnl_locked); - if (ret == 0) - local->hostapd = 1; - } else { - local->hostapd = 0; - ret = hostap_disable_hostapd(local, rtnl_locked); - if (ret != 0) - local->hostapd = 1; - } - - return ret; -} - - -int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked) -{ - int ret; - - if (val < 0 || val > 1) - return -EINVAL; - - if (local->hostapd_sta == val) - return 0; - - if (val) { - ret = hostap_enable_hostapd_sta(local, rtnl_locked); - if (ret == 0) - local->hostapd_sta = 1; - } else { - local->hostapd_sta = 0; - ret = hostap_disable_hostapd_sta(local, rtnl_locked); - if (ret != 0) - local->hostapd_sta = 1; - } - - - return ret; -} - - -int prism2_update_comms_qual(struct net_device *dev) -{ - struct hostap_interface *iface; - local_info_t *local; - int ret = 0; - struct hfa384x_comms_quality sq; - - iface = netdev_priv(dev); - local = iface->local; - if (!local->sta_fw_ver) - ret = -1; - else if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) { - if (local->func->get_rid(local->dev, - HFA384X_RID_DBMCOMMSQUALITY, - &sq, sizeof(sq), 1) >= 0) { - local->comms_qual = (s16) le16_to_cpu(sq.comm_qual); - local->avg_signal = (s16) le16_to_cpu(sq.signal_level); - local->avg_noise = (s16) le16_to_cpu(sq.noise_level); - local->last_comms_qual_update = jiffies; - } else - ret = -1; - } else { - if (local->func->get_rid(local->dev, HFA384X_RID_COMMSQUALITY, - &sq, sizeof(sq), 1) >= 0) { - local->comms_qual = le16_to_cpu(sq.comm_qual); - local->avg_signal = HFA384X_LEVEL_TO_dBm( - le16_to_cpu(sq.signal_level)); - local->avg_noise = HFA384X_LEVEL_TO_dBm( - le16_to_cpu(sq.noise_level)); - local->last_comms_qual_update = jiffies; - } else - ret = -1; - } - - return ret; -} - - -int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype, - u8 *body, size_t bodylen) -{ - struct sk_buff *skb; - struct hostap_ieee80211_mgmt *mgmt; - struct hostap_skb_tx_data *meta; - struct net_device *dev = local->dev; - - skb = dev_alloc_skb(IEEE80211_MGMT_HDR_LEN + bodylen); - if (skb == NULL) - return -ENOMEM; - - mgmt = (struct hostap_ieee80211_mgmt *) - skb_put(skb, IEEE80211_MGMT_HDR_LEN); - memset(mgmt, 0, IEEE80211_MGMT_HDR_LEN); - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); - memcpy(mgmt->da, dst, ETH_ALEN); - memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); - memcpy(mgmt->bssid, dst, ETH_ALEN); - if (body) - memcpy(skb_put(skb, bodylen), body, bodylen); - - meta = (struct hostap_skb_tx_data *) skb->cb; - memset(meta, 0, sizeof(*meta)); - meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; - meta->iface = netdev_priv(dev); - - skb->dev = dev; - skb_reset_mac_header(skb); - skb_reset_network_header(skb); - dev_queue_xmit(skb); - - return 0; -} - - -int prism2_sta_deauth(local_info_t *local, u16 reason) -{ - union iwreq_data wrqu; - int ret; - __le16 val = cpu_to_le16(reason); - - if (local->iw_mode != IW_MODE_INFRA || - is_zero_ether_addr(local->bssid) || - ether_addr_equal(local->bssid, "\x44\x44\x44\x44\x44\x44")) - return 0; - - ret = prism2_sta_send_mgmt(local, local->bssid, IEEE80211_STYPE_DEAUTH, - (u8 *) &val, 2); - eth_zero_addr(wrqu.ap_addr.sa_data); - wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); - return ret; -} - - -struct proc_dir_entry *hostap_proc; - -static int __init hostap_init(void) -{ - if (init_net.proc_net != NULL) { - hostap_proc = proc_mkdir("hostap", init_net.proc_net); - if (!hostap_proc) - printk(KERN_WARNING "Failed to mkdir " - "/proc/net/hostap\n"); - } else - hostap_proc = NULL; - - return 0; -} - - -static void __exit hostap_exit(void) -{ - if (hostap_proc != NULL) { - hostap_proc = NULL; - remove_proc_entry("hostap", init_net.proc_net); - } -} - - -EXPORT_SYMBOL(hostap_set_word); -EXPORT_SYMBOL(hostap_set_string); -EXPORT_SYMBOL(hostap_get_porttype); -EXPORT_SYMBOL(hostap_set_encryption); -EXPORT_SYMBOL(hostap_set_antsel); -EXPORT_SYMBOL(hostap_set_roaming); -EXPORT_SYMBOL(hostap_set_auth_algs); -EXPORT_SYMBOL(hostap_dump_rx_header); -EXPORT_SYMBOL(hostap_dump_tx_header); -EXPORT_SYMBOL(hostap_80211_get_hdrlen); -EXPORT_SYMBOL(hostap_setup_dev); -EXPORT_SYMBOL(hostap_set_multicast_list_queue); -EXPORT_SYMBOL(hostap_set_hostapd); -EXPORT_SYMBOL(hostap_set_hostapd_sta); -EXPORT_SYMBOL(hostap_add_interface); -EXPORT_SYMBOL(hostap_remove_interface); -EXPORT_SYMBOL(prism2_update_comms_qual); - -module_init(hostap_init); -module_exit(hostap_exit); diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c deleted file mode 100644 index c864ef4b0015..000000000000 --- a/drivers/net/wireless/hostap/hostap_pci.c +++ /dev/null @@ -1,459 +0,0 @@ -#define PRISM2_PCI - -/* Host AP driver's support for Intersil Prism2.5 PCI cards is based on - * driver patches from Reyk Floeter and - * Andy Warner */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "hostap_wlan.h" - - -static char *dev_info = "hostap_pci"; - - -MODULE_AUTHOR("Jouni Malinen"); -MODULE_DESCRIPTION("Support for Intersil Prism2.5-based 802.11 wireless LAN " - "PCI cards."); -MODULE_SUPPORTED_DEVICE("Intersil Prism2.5-based WLAN PCI cards"); -MODULE_LICENSE("GPL"); - - -/* struct local_info::hw_priv */ -struct hostap_pci_priv { - void __iomem *mem_start; -}; - - -/* FIX: do we need mb/wmb/rmb with memory operations? */ - - -static const struct pci_device_id prism2_pci_id_table[] = { - /* Intersil Prism3 ISL3872 11Mb/s WLAN Controller */ - { 0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID }, - /* Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller */ - { 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID }, - /* Samsung MagicLAN SWL-2210P */ - { 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID }, - { 0 } -}; - - -#ifdef PRISM2_IO_DEBUG - -static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) -{ - struct hostap_interface *iface; - struct hostap_pci_priv *hw_priv; - local_info_t *local; - unsigned long flags; - - iface = netdev_priv(dev); - local = iface->local; - hw_priv = local->hw_priv; - - spin_lock_irqsave(&local->lock, flags); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); - writeb(v, hw_priv->mem_start + a); - spin_unlock_irqrestore(&local->lock, flags); -} - -static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) -{ - struct hostap_interface *iface; - struct hostap_pci_priv *hw_priv; - local_info_t *local; - unsigned long flags; - u8 v; - - iface = netdev_priv(dev); - local = iface->local; - hw_priv = local->hw_priv; - - spin_lock_irqsave(&local->lock, flags); - v = readb(hw_priv->mem_start + a); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); - spin_unlock_irqrestore(&local->lock, flags); - return v; -} - -static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) -{ - struct hostap_interface *iface; - struct hostap_pci_priv *hw_priv; - local_info_t *local; - unsigned long flags; - - iface = netdev_priv(dev); - local = iface->local; - hw_priv = local->hw_priv; - - spin_lock_irqsave(&local->lock, flags); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); - writew(v, hw_priv->mem_start + a); - spin_unlock_irqrestore(&local->lock, flags); -} - -static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) -{ - struct hostap_interface *iface; - struct hostap_pci_priv *hw_priv; - local_info_t *local; - unsigned long flags; - u16 v; - - iface = netdev_priv(dev); - local = iface->local; - hw_priv = local->hw_priv; - - spin_lock_irqsave(&local->lock, flags); - v = readw(hw_priv->mem_start + a); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); - spin_unlock_irqrestore(&local->lock, flags); - return v; -} - -#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) -#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) -#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) -#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) -#define HFA384X_OUTW_DATA(v,a) hfa384x_outw_debug(dev, (a), le16_to_cpu((v))) -#define HFA384X_INW_DATA(a) cpu_to_le16(hfa384x_inw_debug(dev, (a))) - -#else /* PRISM2_IO_DEBUG */ - -static inline void hfa384x_outb(struct net_device *dev, int a, u8 v) -{ - struct hostap_interface *iface; - struct hostap_pci_priv *hw_priv; - iface = netdev_priv(dev); - hw_priv = iface->local->hw_priv; - writeb(v, hw_priv->mem_start + a); -} - -static inline u8 hfa384x_inb(struct net_device *dev, int a) -{ - struct hostap_interface *iface; - struct hostap_pci_priv *hw_priv; - iface = netdev_priv(dev); - hw_priv = iface->local->hw_priv; - return readb(hw_priv->mem_start + a); -} - -static inline void hfa384x_outw(struct net_device *dev, int a, u16 v) -{ - struct hostap_interface *iface; - struct hostap_pci_priv *hw_priv; - iface = netdev_priv(dev); - hw_priv = iface->local->hw_priv; - writew(v, hw_priv->mem_start + a); -} - -static inline u16 hfa384x_inw(struct net_device *dev, int a) -{ - struct hostap_interface *iface; - struct hostap_pci_priv *hw_priv; - iface = netdev_priv(dev); - hw_priv = iface->local->hw_priv; - return readw(hw_priv->mem_start + a); -} - -#define HFA384X_OUTB(v,a) hfa384x_outb(dev, (a), (v)) -#define HFA384X_INB(a) hfa384x_inb(dev, (a)) -#define HFA384X_OUTW(v,a) hfa384x_outw(dev, (a), (v)) -#define HFA384X_INW(a) hfa384x_inw(dev, (a)) -#define HFA384X_OUTW_DATA(v,a) hfa384x_outw(dev, (a), le16_to_cpu((v))) -#define HFA384X_INW_DATA(a) cpu_to_le16(hfa384x_inw(dev, (a))) - -#endif /* PRISM2_IO_DEBUG */ - - -static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, - int len) -{ - u16 d_off; - __le16 *pos; - - d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; - pos = (__le16 *) buf; - - for ( ; len > 1; len -= 2) - *pos++ = HFA384X_INW_DATA(d_off); - - if (len & 1) - *((char *) pos) = HFA384X_INB(d_off); - - return 0; -} - - -static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) -{ - u16 d_off; - __le16 *pos; - - d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; - pos = (__le16 *) buf; - - for ( ; len > 1; len -= 2) - HFA384X_OUTW_DATA(*pos++, d_off); - - if (len & 1) - HFA384X_OUTB(*((char *) pos), d_off); - - return 0; -} - - -/* FIX: This might change at some point.. */ -#include "hostap_hw.c" - -static void prism2_pci_cor_sreset(local_info_t *local) -{ - struct net_device *dev = local->dev; - u16 reg; - - reg = HFA384X_INB(HFA384X_PCICOR_OFF); - printk(KERN_DEBUG "%s: Original COR value: 0x%0x\n", dev->name, reg); - - /* linux-wlan-ng uses extremely long hold and settle times for - * COR sreset. A comment in the driver code mentions that the long - * delays appear to be necessary. However, at least IBM 22P6901 seems - * to work fine with shorter delays. - * - * Longer delays can be configured by uncommenting following line: */ -/* #define PRISM2_PCI_USE_LONG_DELAYS */ - -#ifdef PRISM2_PCI_USE_LONG_DELAYS - int i; - - HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); - mdelay(250); - - HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); - mdelay(500); - - /* Wait for f/w to complete initialization (CMD:BUSY == 0) */ - i = 2000000 / 10; - while ((HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) && --i) - udelay(10); - -#else /* PRISM2_PCI_USE_LONG_DELAYS */ - - HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); - mdelay(2); - HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); - mdelay(2); - -#endif /* PRISM2_PCI_USE_LONG_DELAYS */ - - if (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) { - printk(KERN_DEBUG "%s: COR sreset timeout\n", dev->name); - } -} - - -static void prism2_pci_genesis_reset(local_info_t *local, int hcr) -{ - struct net_device *dev = local->dev; - - HFA384X_OUTW(0x00C5, HFA384X_PCICOR_OFF); - mdelay(10); - HFA384X_OUTW(hcr, HFA384X_PCIHCR_OFF); - mdelay(10); - HFA384X_OUTW(0x0045, HFA384X_PCICOR_OFF); - mdelay(10); -} - - -static struct prism2_helper_functions prism2_pci_funcs = -{ - .card_present = NULL, - .cor_sreset = prism2_pci_cor_sreset, - .genesis_reset = prism2_pci_genesis_reset, - .hw_type = HOSTAP_HW_PCI, -}; - - -static int prism2_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - unsigned long phymem; - void __iomem *mem = NULL; - local_info_t *local = NULL; - struct net_device *dev = NULL; - static int cards_found /* = 0 */; - int irq_registered = 0; - struct hostap_interface *iface; - struct hostap_pci_priv *hw_priv; - - hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL); - if (hw_priv == NULL) - return -ENOMEM; - - if (pci_enable_device(pdev)) - goto err_out_free; - - phymem = pci_resource_start(pdev, 0); - - if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) { - printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n"); - goto err_out_disable; - } - - mem = pci_ioremap_bar(pdev, 0); - if (mem == NULL) { - printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ; - goto fail; - } - - dev = prism2_init_local_data(&prism2_pci_funcs, cards_found, - &pdev->dev); - if (dev == NULL) - goto fail; - iface = netdev_priv(dev); - local = iface->local; - local->hw_priv = hw_priv; - cards_found++; - - dev->irq = pdev->irq; - hw_priv->mem_start = mem; - dev->base_addr = (unsigned long) mem; - - prism2_pci_cor_sreset(local); - - pci_set_drvdata(pdev, dev); - - if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name, - dev)) { - printk(KERN_WARNING "%s: request_irq failed\n", dev->name); - goto fail; - } else - irq_registered = 1; - - if (!local->pri_only && prism2_hw_config(dev, 1)) { - printk(KERN_DEBUG "%s: hardware initialization failed\n", - dev_info); - goto fail; - } - - printk(KERN_INFO "%s: Intersil Prism2.5 PCI: " - "mem=0x%lx, irq=%d\n", dev->name, phymem, dev->irq); - - return hostap_hw_ready(dev); - - fail: - if (irq_registered && dev) - free_irq(dev->irq, dev); - - if (mem) - iounmap(mem); - - release_mem_region(phymem, pci_resource_len(pdev, 0)); - - err_out_disable: - pci_disable_device(pdev); - prism2_free_local_data(dev); - - err_out_free: - kfree(hw_priv); - - return -ENODEV; -} - - -static void prism2_pci_remove(struct pci_dev *pdev) -{ - struct net_device *dev; - struct hostap_interface *iface; - void __iomem *mem_start; - struct hostap_pci_priv *hw_priv; - - dev = pci_get_drvdata(pdev); - iface = netdev_priv(dev); - hw_priv = iface->local->hw_priv; - - /* Reset the hardware, and ensure interrupts are disabled. */ - prism2_pci_cor_sreset(iface->local); - hfa384x_disable_interrupts(dev); - - if (dev->irq) - free_irq(dev->irq, dev); - - mem_start = hw_priv->mem_start; - prism2_free_local_data(dev); - kfree(hw_priv); - - iounmap(mem_start); - - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - pci_disable_device(pdev); -} - - -#ifdef CONFIG_PM -static int prism2_pci_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct net_device *dev = pci_get_drvdata(pdev); - - if (netif_running(dev)) { - netif_stop_queue(dev); - netif_device_detach(dev); - } - prism2_suspend(dev); - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D3hot); - - return 0; -} - -static int prism2_pci_resume(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - int err; - - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR "%s: pci_enable_device failed on resume\n", - dev->name); - return err; - } - pci_restore_state(pdev); - prism2_hw_config(dev, 0); - if (netif_running(dev)) { - netif_device_attach(dev); - netif_start_queue(dev); - } - - return 0; -} -#endif /* CONFIG_PM */ - - -MODULE_DEVICE_TABLE(pci, prism2_pci_id_table); - -static struct pci_driver prism2_pci_driver = { - .name = "hostap_pci", - .id_table = prism2_pci_id_table, - .probe = prism2_pci_probe, - .remove = prism2_pci_remove, -#ifdef CONFIG_PM - .suspend = prism2_pci_suspend, - .resume = prism2_pci_resume, -#endif /* CONFIG_PM */ -}; - -module_pci_driver(prism2_pci_driver); diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c deleted file mode 100644 index 4901a99c6c59..000000000000 --- a/drivers/net/wireless/hostap/hostap_plx.c +++ /dev/null @@ -1,618 +0,0 @@ -#define PRISM2_PLX - -/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is - * based on: - * - Host AP driver patch from james@madingley.org - * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "hostap_wlan.h" - - -static char *dev_info = "hostap_plx"; - - -MODULE_AUTHOR("Jouni Malinen"); -MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN " - "cards (PLX)."); -MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)"); -MODULE_LICENSE("GPL"); - - -static int ignore_cis; -module_param(ignore_cis, int, 0444); -MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS"); - - -/* struct local_info::hw_priv */ -struct hostap_plx_priv { - void __iomem *attr_mem; - unsigned int cor_offset; -}; - - -#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */ -#define COR_SRESET 0x80 -#define COR_LEVLREQ 0x40 -#define COR_ENABLE_FUNC 0x01 -/* PCI Configuration Registers */ -#define PLX_PCIIPR 0x3d /* PCI Interrupt Pin */ -/* Local Configuration Registers */ -#define PLX_INTCSR 0x4c /* Interrupt Control/Status Register */ -#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */ -#define PLX_CNTRL 0x50 -#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28) - - -#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID } - -static const struct pci_device_id prism2_plx_id_table[] = { - PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"), - PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"), - PLXDEV(0x126c, 0x8030, "Nortel emobility"), - PLXDEV(0x1562, 0x0001, "Symbol LA-4123"), - PLXDEV(0x1385, 0x4100, "Netgear MA301"), - PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"), - PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"), - PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"), - PLXDEV(0x16ab, 0x1100, "Global Sun Tech GL24110P"), - PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"), - PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"), - PLXDEV(0x16ab, 0x1103, "Longshine 8031"), - PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"), - PLXDEV(0xec80, 0xec00, "Belkin F5D6000"), - { 0 } -}; - - -/* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid - * is not listed here, you will need to add it here to get the driver - * initialized. */ -static struct prism2_plx_manfid { - u16 manfid1, manfid2; -} prism2_plx_known_manfids[] = { - { 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */, - { 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */, - { 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */, - { 0x0126, 0x8000 } /* Proxim RangeLAN */, - { 0x0138, 0x0002 } /* Compaq WL100 */, - { 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */, - { 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */, - { 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */, - { 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */, - { 0x028a, 0x0002 } /* D-Link DRC-650 */, - { 0x0250, 0x0002 } /* Samsung SWL2000-N */, - { 0xc250, 0x0002 } /* EMTAC A2424i */, - { 0xd601, 0x0002 } /* Z-Com XI300 */, - { 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */, - { 0, 0} -}; - - -#ifdef PRISM2_IO_DEBUG - -static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - - iface = netdev_priv(dev); - local = iface->local; - - spin_lock_irqsave(&local->lock, flags); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); - outb(v, dev->base_addr + a); - spin_unlock_irqrestore(&local->lock, flags); -} - -static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - u8 v; - - iface = netdev_priv(dev); - local = iface->local; - - spin_lock_irqsave(&local->lock, flags); - v = inb(dev->base_addr + a); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); - spin_unlock_irqrestore(&local->lock, flags); - return v; -} - -static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - - iface = netdev_priv(dev); - local = iface->local; - - spin_lock_irqsave(&local->lock, flags); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); - outw(v, dev->base_addr + a); - spin_unlock_irqrestore(&local->lock, flags); -} - -static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - u16 v; - - iface = netdev_priv(dev); - local = iface->local; - - spin_lock_irqsave(&local->lock, flags); - v = inw(dev->base_addr + a); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); - spin_unlock_irqrestore(&local->lock, flags); - return v; -} - -static inline void hfa384x_outsw_debug(struct net_device *dev, int a, - u8 *buf, int wc) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - - iface = netdev_priv(dev); - local = iface->local; - - spin_lock_irqsave(&local->lock, flags); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc); - outsw(dev->base_addr + a, buf, wc); - spin_unlock_irqrestore(&local->lock, flags); -} - -static inline void hfa384x_insw_debug(struct net_device *dev, int a, - u8 *buf, int wc) -{ - struct hostap_interface *iface; - local_info_t *local; - unsigned long flags; - - iface = netdev_priv(dev); - local = iface->local; - - spin_lock_irqsave(&local->lock, flags); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc); - insw(dev->base_addr + a, buf, wc); - spin_unlock_irqrestore(&local->lock, flags); -} - -#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) -#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) -#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) -#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) -#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc)) -#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc)) - -#else /* PRISM2_IO_DEBUG */ - -#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a)) -#define HFA384X_INB(a) inb(dev->base_addr + (a)) -#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a)) -#define HFA384X_INW(a) inw(dev->base_addr + (a)) -#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc) -#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc) - -#endif /* PRISM2_IO_DEBUG */ - - -static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, - int len) -{ - u16 d_off; - u16 *pos; - - d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; - pos = (u16 *) buf; - - if (len / 2) - HFA384X_INSW(d_off, buf, len / 2); - pos += len / 2; - - if (len & 1) - *((char *) pos) = HFA384X_INB(d_off); - - return 0; -} - - -static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) -{ - u16 d_off; - u16 *pos; - - d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; - pos = (u16 *) buf; - - if (len / 2) - HFA384X_OUTSW(d_off, buf, len / 2); - pos += len / 2; - - if (len & 1) - HFA384X_OUTB(*((char *) pos), d_off); - - return 0; -} - - -/* FIX: This might change at some point.. */ -#include "hostap_hw.c" - - -static void prism2_plx_cor_sreset(local_info_t *local) -{ - unsigned char corsave; - struct hostap_plx_priv *hw_priv = local->hw_priv; - - printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n", - dev_info); - - /* Set sreset bit of COR and clear it after hold time */ - - if (hw_priv->attr_mem == NULL) { - /* TMD7160 - COR at card's first I/O addr */ - corsave = inb(hw_priv->cor_offset); - outb(corsave | COR_SRESET, hw_priv->cor_offset); - mdelay(2); - outb(corsave & ~COR_SRESET, hw_priv->cor_offset); - mdelay(2); - } else { - /* PLX9052 */ - corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset); - writeb(corsave | COR_SRESET, - hw_priv->attr_mem + hw_priv->cor_offset); - mdelay(2); - writeb(corsave & ~COR_SRESET, - hw_priv->attr_mem + hw_priv->cor_offset); - mdelay(2); - } -} - - -static void prism2_plx_genesis_reset(local_info_t *local, int hcr) -{ - unsigned char corsave; - struct hostap_plx_priv *hw_priv = local->hw_priv; - - if (hw_priv->attr_mem == NULL) { - /* TMD7160 - COR at card's first I/O addr */ - corsave = inb(hw_priv->cor_offset); - outb(corsave | COR_SRESET, hw_priv->cor_offset); - mdelay(10); - outb(hcr, hw_priv->cor_offset + 2); - mdelay(10); - outb(corsave & ~COR_SRESET, hw_priv->cor_offset); - mdelay(10); - } else { - /* PLX9052 */ - corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset); - writeb(corsave | COR_SRESET, - hw_priv->attr_mem + hw_priv->cor_offset); - mdelay(10); - writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2); - mdelay(10); - writeb(corsave & ~COR_SRESET, - hw_priv->attr_mem + hw_priv->cor_offset); - mdelay(10); - } -} - - -static struct prism2_helper_functions prism2_plx_funcs = -{ - .card_present = NULL, - .cor_sreset = prism2_plx_cor_sreset, - .genesis_reset = prism2_plx_genesis_reset, - .hw_type = HOSTAP_HW_PLX, -}; - - -static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len, - unsigned int *cor_offset, - unsigned int *cor_index) -{ -#define CISTPL_CONFIG 0x1A -#define CISTPL_MANFID 0x20 -#define CISTPL_END 0xFF -#define CIS_MAX_LEN 256 - u8 *cis; - int i, pos; - unsigned int rmsz, rasz, manfid1, manfid2; - struct prism2_plx_manfid *manfid; - - cis = kmalloc(CIS_MAX_LEN, GFP_KERNEL); - if (cis == NULL) - return -ENOMEM; - - /* read CIS; it is in even offsets in the beginning of attr_mem */ - for (i = 0; i < CIS_MAX_LEN; i++) - cis[i] = readb(attr_mem + 2 * i); - printk(KERN_DEBUG "%s: CIS: %02x %02x %02x %02x %02x %02x ...\n", - dev_info, cis[0], cis[1], cis[2], cis[3], cis[4], cis[5]); - - /* set reasonable defaults for Prism2 cards just in case CIS parsing - * fails */ - *cor_offset = 0x3e0; - *cor_index = 0x01; - manfid1 = manfid2 = 0; - - pos = 0; - while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) { - if (pos + 2 + cis[pos + 1] > CIS_MAX_LEN) - goto cis_error; - - switch (cis[pos]) { - case CISTPL_CONFIG: - if (cis[pos + 1] < 2) - goto cis_error; - rmsz = (cis[pos + 2] & 0x3c) >> 2; - rasz = cis[pos + 2] & 0x03; - if (4 + rasz + rmsz > cis[pos + 1]) - goto cis_error; - *cor_index = cis[pos + 3] & 0x3F; - *cor_offset = 0; - for (i = 0; i <= rasz; i++) - *cor_offset += cis[pos + 4 + i] << (8 * i); - printk(KERN_DEBUG "%s: cor_index=0x%x " - "cor_offset=0x%x\n", dev_info, - *cor_index, *cor_offset); - if (*cor_offset > attr_len) { - printk(KERN_ERR "%s: COR offset not within " - "attr_mem\n", dev_info); - kfree(cis); - return -1; - } - break; - - case CISTPL_MANFID: - if (cis[pos + 1] < 4) - goto cis_error; - manfid1 = cis[pos + 2] + (cis[pos + 3] << 8); - manfid2 = cis[pos + 4] + (cis[pos + 5] << 8); - printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n", - dev_info, manfid1, manfid2); - break; - } - - pos += cis[pos + 1] + 2; - } - - if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END) - goto cis_error; - - for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++) - if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) { - kfree(cis); - return 0; - } - - printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is" - " not supported card\n", dev_info, manfid1, manfid2); - goto fail; - - cis_error: - printk(KERN_WARNING "%s: invalid CIS data\n", dev_info); - - fail: - kfree(cis); - if (ignore_cis) { - printk(KERN_INFO "%s: ignore_cis parameter set - ignoring " - "errors during CIS verification\n", dev_info); - return 0; - } - return -1; -} - - -static int prism2_plx_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - unsigned int pccard_ioaddr, plx_ioaddr; - unsigned long pccard_attr_mem; - unsigned int pccard_attr_len; - void __iomem *attr_mem = NULL; - unsigned int cor_offset = 0, cor_index = 0; - u32 reg; - local_info_t *local = NULL; - struct net_device *dev = NULL; - struct hostap_interface *iface; - static int cards_found /* = 0 */; - int irq_registered = 0; - int tmd7160; - struct hostap_plx_priv *hw_priv; - - hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL); - if (hw_priv == NULL) - return -ENOMEM; - - if (pci_enable_device(pdev)) - goto err_out_free; - - /* National Datacomm NCP130 based on TMD7160, not PLX9052. */ - tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131); - - plx_ioaddr = pci_resource_start(pdev, 1); - pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3); - - if (tmd7160) { - /* TMD7160 */ - attr_mem = NULL; /* no access to PC Card attribute memory */ - - printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, " - "irq=%d, pccard_io=0x%x\n", - plx_ioaddr, pdev->irq, pccard_ioaddr); - - cor_offset = plx_ioaddr; - cor_index = 0x04; - - outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr); - mdelay(1); - reg = inb(plx_ioaddr); - if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) { - printk(KERN_ERR "%s: Error setting COR (expected=" - "0x%02x, was=0x%02x)\n", dev_info, - cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg); - goto fail; - } - } else { - /* PLX9052 */ - pccard_attr_mem = pci_resource_start(pdev, 2); - pccard_attr_len = pci_resource_len(pdev, 2); - if (pccard_attr_len < PLX_MIN_ATTR_LEN) - goto fail; - - - attr_mem = ioremap(pccard_attr_mem, pccard_attr_len); - if (attr_mem == NULL) { - printk(KERN_ERR "%s: cannot remap attr_mem\n", - dev_info); - goto fail; - } - - printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: " - "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n", - pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr); - - if (prism2_plx_check_cis(attr_mem, pccard_attr_len, - &cor_offset, &cor_index)) { - printk(KERN_INFO "Unknown PC Card CIS - not a " - "Prism2/2.5 card?\n"); - goto fail; - } - - printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 " - "adapter\n"); - - /* Write COR to enable PC Card */ - writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, - attr_mem + cor_offset); - - /* Enable PCI interrupts if they are not already enabled */ - reg = inl(plx_ioaddr + PLX_INTCSR); - printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg); - if (!(reg & PLX_INTCSR_PCI_INTEN)) { - outl(reg | PLX_INTCSR_PCI_INTEN, - plx_ioaddr + PLX_INTCSR); - if (!(inl(plx_ioaddr + PLX_INTCSR) & - PLX_INTCSR_PCI_INTEN)) { - printk(KERN_WARNING "%s: Could not enable " - "Local Interrupts\n", dev_info); - goto fail; - } - } - - reg = inl(plx_ioaddr + PLX_CNTRL); - printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM " - "present=%d)\n", - reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0); - /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is - * not present; but are there really such cards in use(?) */ - } - - dev = prism2_init_local_data(&prism2_plx_funcs, cards_found, - &pdev->dev); - if (dev == NULL) - goto fail; - iface = netdev_priv(dev); - local = iface->local; - local->hw_priv = hw_priv; - cards_found++; - - dev->irq = pdev->irq; - dev->base_addr = pccard_ioaddr; - hw_priv->attr_mem = attr_mem; - hw_priv->cor_offset = cor_offset; - - pci_set_drvdata(pdev, dev); - - if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name, - dev)) { - printk(KERN_WARNING "%s: request_irq failed\n", dev->name); - goto fail; - } else - irq_registered = 1; - - if (prism2_hw_config(dev, 1)) { - printk(KERN_DEBUG "%s: hardware initialization failed\n", - dev_info); - goto fail; - } - - return hostap_hw_ready(dev); - - fail: - if (irq_registered && dev) - free_irq(dev->irq, dev); - - if (attr_mem) - iounmap(attr_mem); - - pci_disable_device(pdev); - prism2_free_local_data(dev); - - err_out_free: - kfree(hw_priv); - - return -ENODEV; -} - - -static void prism2_plx_remove(struct pci_dev *pdev) -{ - struct net_device *dev; - struct hostap_interface *iface; - struct hostap_plx_priv *hw_priv; - - dev = pci_get_drvdata(pdev); - iface = netdev_priv(dev); - hw_priv = iface->local->hw_priv; - - /* Reset the hardware, and ensure interrupts are disabled. */ - prism2_plx_cor_sreset(iface->local); - hfa384x_disable_interrupts(dev); - - if (hw_priv->attr_mem) - iounmap(hw_priv->attr_mem); - if (dev->irq) - free_irq(dev->irq, dev); - - prism2_free_local_data(dev); - kfree(hw_priv); - pci_disable_device(pdev); -} - - -MODULE_DEVICE_TABLE(pci, prism2_plx_id_table); - -static struct pci_driver prism2_plx_driver = { - .name = "hostap_plx", - .id_table = prism2_plx_id_table, - .probe = prism2_plx_probe, - .remove = prism2_plx_remove, -}; - -module_pci_driver(prism2_plx_driver); diff --git a/drivers/net/wireless/hostap/hostap_proc.c b/drivers/net/wireless/hostap/hostap_proc.c deleted file mode 100644 index dd84557cf957..000000000000 --- a/drivers/net/wireless/hostap/hostap_proc.c +++ /dev/null @@ -1,499 +0,0 @@ -/* /proc routines for Host AP driver */ - -#include -#include -#include -#include - -#include "hostap_wlan.h" -#include "hostap.h" - -#define PROC_LIMIT (PAGE_SIZE - 80) - - -#ifndef PRISM2_NO_PROCFS_DEBUG -static int prism2_debug_proc_show(struct seq_file *m, void *v) -{ - local_info_t *local = m->private; - int i; - - seq_printf(m, "next_txfid=%d next_alloc=%d\n", - local->next_txfid, local->next_alloc); - for (i = 0; i < PRISM2_TXFID_COUNT; i++) - seq_printf(m, "FID: tx=%04X intransmit=%04X\n", - local->txfid[i], local->intransmitfid[i]); - seq_printf(m, "FW TX rate control: %d\n", local->fw_tx_rate_control); - seq_printf(m, "beacon_int=%d\n", local->beacon_int); - seq_printf(m, "dtim_period=%d\n", local->dtim_period); - seq_printf(m, "wds_max_connections=%d\n", local->wds_max_connections); - seq_printf(m, "dev_enabled=%d\n", local->dev_enabled); - seq_printf(m, "sw_tick_stuck=%d\n", local->sw_tick_stuck); - for (i = 0; i < WEP_KEYS; i++) { - if (local->crypt_info.crypt[i] && - local->crypt_info.crypt[i]->ops) { - seq_printf(m, "crypt[%d]=%s\n", i, - local->crypt_info.crypt[i]->ops->name); - } - } - seq_printf(m, "pri_only=%d\n", local->pri_only); - seq_printf(m, "pci=%d\n", local->func->hw_type == HOSTAP_HW_PCI); - seq_printf(m, "sram_type=%d\n", local->sram_type); - seq_printf(m, "no_pri=%d\n", local->no_pri); - - return 0; -} - -static int prism2_debug_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, prism2_debug_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations prism2_debug_proc_fops = { - .open = prism2_debug_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif /* PRISM2_NO_PROCFS_DEBUG */ - - -static int prism2_stats_proc_show(struct seq_file *m, void *v) -{ - local_info_t *local = m->private; - struct comm_tallies_sums *sums = &local->comm_tallies; - - seq_printf(m, "TxUnicastFrames=%u\n", sums->tx_unicast_frames); - seq_printf(m, "TxMulticastframes=%u\n", sums->tx_multicast_frames); - seq_printf(m, "TxFragments=%u\n", sums->tx_fragments); - seq_printf(m, "TxUnicastOctets=%u\n", sums->tx_unicast_octets); - seq_printf(m, "TxMulticastOctets=%u\n", sums->tx_multicast_octets); - seq_printf(m, "TxDeferredTransmissions=%u\n", - sums->tx_deferred_transmissions); - seq_printf(m, "TxSingleRetryFrames=%u\n", sums->tx_single_retry_frames); - seq_printf(m, "TxMultipleRetryFrames=%u\n", - sums->tx_multiple_retry_frames); - seq_printf(m, "TxRetryLimitExceeded=%u\n", - sums->tx_retry_limit_exceeded); - seq_printf(m, "TxDiscards=%u\n", sums->tx_discards); - seq_printf(m, "RxUnicastFrames=%u\n", sums->rx_unicast_frames); - seq_printf(m, "RxMulticastFrames=%u\n", sums->rx_multicast_frames); - seq_printf(m, "RxFragments=%u\n", sums->rx_fragments); - seq_printf(m, "RxUnicastOctets=%u\n", sums->rx_unicast_octets); - seq_printf(m, "RxMulticastOctets=%u\n", sums->rx_multicast_octets); - seq_printf(m, "RxFCSErrors=%u\n", sums->rx_fcs_errors); - seq_printf(m, "RxDiscardsNoBuffer=%u\n", sums->rx_discards_no_buffer); - seq_printf(m, "TxDiscardsWrongSA=%u\n", sums->tx_discards_wrong_sa); - seq_printf(m, "RxDiscardsWEPUndecryptable=%u\n", - sums->rx_discards_wep_undecryptable); - seq_printf(m, "RxMessageInMsgFragments=%u\n", - sums->rx_message_in_msg_fragments); - seq_printf(m, "RxMessageInBadMsgFragments=%u\n", - sums->rx_message_in_bad_msg_fragments); - /* FIX: this may grow too long for one page(?) */ - - return 0; -} - -static int prism2_stats_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, prism2_stats_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations prism2_stats_proc_fops = { - .open = prism2_stats_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - - -static int prism2_wds_proc_show(struct seq_file *m, void *v) -{ - struct list_head *ptr = v; - struct hostap_interface *iface; - - iface = list_entry(ptr, struct hostap_interface, list); - if (iface->type == HOSTAP_INTERFACE_WDS) - seq_printf(m, "%s\t%pM\n", - iface->dev->name, iface->u.wds.remote_addr); - return 0; -} - -static void *prism2_wds_proc_start(struct seq_file *m, loff_t *_pos) -{ - local_info_t *local = m->private; - read_lock_bh(&local->iface_lock); - return seq_list_start(&local->hostap_interfaces, *_pos); -} - -static void *prism2_wds_proc_next(struct seq_file *m, void *v, loff_t *_pos) -{ - local_info_t *local = m->private; - return seq_list_next(v, &local->hostap_interfaces, _pos); -} - -static void prism2_wds_proc_stop(struct seq_file *m, void *v) -{ - local_info_t *local = m->private; - read_unlock_bh(&local->iface_lock); -} - -static const struct seq_operations prism2_wds_proc_seqops = { - .start = prism2_wds_proc_start, - .next = prism2_wds_proc_next, - .stop = prism2_wds_proc_stop, - .show = prism2_wds_proc_show, -}; - -static int prism2_wds_proc_open(struct inode *inode, struct file *file) -{ - int ret = seq_open(file, &prism2_wds_proc_seqops); - if (ret == 0) { - struct seq_file *m = file->private_data; - m->private = PDE_DATA(inode); - } - return ret; -} - -static const struct file_operations prism2_wds_proc_fops = { - .open = prism2_wds_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - - -static int prism2_bss_list_proc_show(struct seq_file *m, void *v) -{ - local_info_t *local = m->private; - struct list_head *ptr = v; - struct hostap_bss_info *bss; - - if (ptr == &local->bss_list) { - seq_printf(m, "#BSSID\tlast_update\tcount\tcapab_info\tSSID(txt)\t" - "SSID(hex)\tWPA IE\n"); - return 0; - } - - bss = list_entry(ptr, struct hostap_bss_info, list); - seq_printf(m, "%pM\t%lu\t%u\t0x%x\t", - bss->bssid, bss->last_update, - bss->count, bss->capab_info); - - seq_printf(m, "%*pE", (int)bss->ssid_len, bss->ssid); - - seq_putc(m, '\t'); - seq_printf(m, "%*phN", (int)bss->ssid_len, bss->ssid); - seq_putc(m, '\t'); - seq_printf(m, "%*phN", (int)bss->wpa_ie_len, bss->wpa_ie); - seq_putc(m, '\n'); - return 0; -} - -static void *prism2_bss_list_proc_start(struct seq_file *m, loff_t *_pos) -{ - local_info_t *local = m->private; - spin_lock_bh(&local->lock); - return seq_list_start_head(&local->bss_list, *_pos); -} - -static void *prism2_bss_list_proc_next(struct seq_file *m, void *v, loff_t *_pos) -{ - local_info_t *local = m->private; - return seq_list_next(v, &local->bss_list, _pos); -} - -static void prism2_bss_list_proc_stop(struct seq_file *m, void *v) -{ - local_info_t *local = m->private; - spin_unlock_bh(&local->lock); -} - -static const struct seq_operations prism2_bss_list_proc_seqops = { - .start = prism2_bss_list_proc_start, - .next = prism2_bss_list_proc_next, - .stop = prism2_bss_list_proc_stop, - .show = prism2_bss_list_proc_show, -}; - -static int prism2_bss_list_proc_open(struct inode *inode, struct file *file) -{ - int ret = seq_open(file, &prism2_bss_list_proc_seqops); - if (ret == 0) { - struct seq_file *m = file->private_data; - m->private = PDE_DATA(inode); - } - return ret; -} - -static const struct file_operations prism2_bss_list_proc_fops = { - .open = prism2_bss_list_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - - -static int prism2_crypt_proc_show(struct seq_file *m, void *v) -{ - local_info_t *local = m->private; - int i; - - seq_printf(m, "tx_keyidx=%d\n", local->crypt_info.tx_keyidx); - for (i = 0; i < WEP_KEYS; i++) { - if (local->crypt_info.crypt[i] && - local->crypt_info.crypt[i]->ops && - local->crypt_info.crypt[i]->ops->print_stats) { - local->crypt_info.crypt[i]->ops->print_stats( - m, local->crypt_info.crypt[i]->priv); - } - } - return 0; -} - -static int prism2_crypt_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, prism2_crypt_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations prism2_crypt_proc_fops = { - .open = prism2_crypt_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - - -static ssize_t prism2_pda_proc_read(struct file *file, char __user *buf, - size_t count, loff_t *_pos) -{ - local_info_t *local = PDE_DATA(file_inode(file)); - size_t off; - - if (local->pda == NULL || *_pos >= PRISM2_PDA_SIZE) - return 0; - - off = *_pos; - if (count > PRISM2_PDA_SIZE - off) - count = PRISM2_PDA_SIZE - off; - if (copy_to_user(buf, local->pda + off, count) != 0) - return -EFAULT; - *_pos += count; - return count; -} - -static const struct file_operations prism2_pda_proc_fops = { - .read = prism2_pda_proc_read, - .llseek = generic_file_llseek, -}; - - -static ssize_t prism2_aux_dump_proc_no_read(struct file *file, char __user *buf, - size_t bufsize, loff_t *_pos) -{ - return 0; -} - -static const struct file_operations prism2_aux_dump_proc_fops = { - .read = prism2_aux_dump_proc_no_read, -}; - - -#ifdef PRISM2_IO_DEBUG -static int prism2_io_debug_proc_read(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - local_info_t *local = (local_info_t *) data; - int head = local->io_debug_head; - int start_bytes, left, copy, copied; - - if (off + count > PRISM2_IO_DEBUG_SIZE * 4) { - *eof = 1; - if (off >= PRISM2_IO_DEBUG_SIZE * 4) - return 0; - count = PRISM2_IO_DEBUG_SIZE * 4 - off; - } - - copied = 0; - start_bytes = (PRISM2_IO_DEBUG_SIZE - head) * 4; - left = count; - - if (off < start_bytes) { - copy = start_bytes - off; - if (copy > count) - copy = count; - memcpy(page, ((u8 *) &local->io_debug[head]) + off, copy); - left -= copy; - if (left > 0) - memcpy(&page[copy], local->io_debug, left); - } else { - memcpy(page, ((u8 *) local->io_debug) + (off - start_bytes), - left); - } - - *start = page; - - return count; -} -#endif /* PRISM2_IO_DEBUG */ - - -#ifndef PRISM2_NO_STATION_MODES -static int prism2_scan_results_proc_show(struct seq_file *m, void *v) -{ - local_info_t *local = m->private; - unsigned long entry; - int i, len; - struct hfa384x_hostscan_result *scanres; - u8 *p; - - if (v == SEQ_START_TOKEN) { - seq_printf(m, - "CHID ANL SL BcnInt Capab Rate BSSID ATIM SupRates SSID\n"); - return 0; - } - - entry = (unsigned long)v - 2; - scanres = &local->last_scan_results[entry]; - - seq_printf(m, "%d %d %d %d 0x%02x %d %pM %d ", - le16_to_cpu(scanres->chid), - (s16) le16_to_cpu(scanres->anl), - (s16) le16_to_cpu(scanres->sl), - le16_to_cpu(scanres->beacon_interval), - le16_to_cpu(scanres->capability), - le16_to_cpu(scanres->rate), - scanres->bssid, - le16_to_cpu(scanres->atim)); - - p = scanres->sup_rates; - for (i = 0; i < sizeof(scanres->sup_rates); i++) { - if (p[i] == 0) - break; - seq_printf(m, "<%02x>", p[i]); - } - seq_putc(m, ' '); - - p = scanres->ssid; - len = le16_to_cpu(scanres->ssid_len); - if (len > 32) - len = 32; - for (i = 0; i < len; i++) { - unsigned char c = p[i]; - if (c >= 32 && c < 127) - seq_putc(m, c); - else - seq_printf(m, "<%02x>", c); - } - seq_putc(m, '\n'); - return 0; -} - -static void *prism2_scan_results_proc_start(struct seq_file *m, loff_t *_pos) -{ - local_info_t *local = m->private; - spin_lock_bh(&local->lock); - - /* We have a header (pos 0) + N results to show (pos 1...N) */ - if (*_pos > local->last_scan_results_count) - return NULL; - return (void *)(unsigned long)(*_pos + 1); /* 0 would be EOF */ -} - -static void *prism2_scan_results_proc_next(struct seq_file *m, void *v, loff_t *_pos) -{ - local_info_t *local = m->private; - - ++*_pos; - if (*_pos > local->last_scan_results_count) - return NULL; - return (void *)(unsigned long)(*_pos + 1); /* 0 would be EOF */ -} - -static void prism2_scan_results_proc_stop(struct seq_file *m, void *v) -{ - local_info_t *local = m->private; - spin_unlock_bh(&local->lock); -} - -static const struct seq_operations prism2_scan_results_proc_seqops = { - .start = prism2_scan_results_proc_start, - .next = prism2_scan_results_proc_next, - .stop = prism2_scan_results_proc_stop, - .show = prism2_scan_results_proc_show, -}; - -static int prism2_scan_results_proc_open(struct inode *inode, struct file *file) -{ - int ret = seq_open(file, &prism2_scan_results_proc_seqops); - if (ret == 0) { - struct seq_file *m = file->private_data; - m->private = PDE_DATA(inode); - } - return ret; -} - -static const struct file_operations prism2_scan_results_proc_fops = { - .open = prism2_scan_results_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - - -#endif /* PRISM2_NO_STATION_MODES */ - - -void hostap_init_proc(local_info_t *local) -{ - local->proc = NULL; - - if (hostap_proc == NULL) { - printk(KERN_WARNING "%s: hostap proc directory not created\n", - local->dev->name); - return; - } - - local->proc = proc_mkdir(local->ddev->name, hostap_proc); - if (local->proc == NULL) { - printk(KERN_INFO "/proc/net/hostap/%s creation failed\n", - local->ddev->name); - return; - } - -#ifndef PRISM2_NO_PROCFS_DEBUG - proc_create_data("debug", 0, local->proc, - &prism2_debug_proc_fops, local); -#endif /* PRISM2_NO_PROCFS_DEBUG */ - proc_create_data("stats", 0, local->proc, - &prism2_stats_proc_fops, local); - proc_create_data("wds", 0, local->proc, - &prism2_wds_proc_fops, local); - proc_create_data("pda", 0, local->proc, - &prism2_pda_proc_fops, local); - proc_create_data("aux_dump", 0, local->proc, - local->func->read_aux_fops ?: &prism2_aux_dump_proc_fops, - local); - proc_create_data("bss_list", 0, local->proc, - &prism2_bss_list_proc_fops, local); - proc_create_data("crypt", 0, local->proc, - &prism2_crypt_proc_fops, local); -#ifdef PRISM2_IO_DEBUG - proc_create_data("io_debug", 0, local->proc, - &prism2_io_debug_proc_fops, local); -#endif /* PRISM2_IO_DEBUG */ -#ifndef PRISM2_NO_STATION_MODES - proc_create_data("scan_results", 0, local->proc, - &prism2_scan_results_proc_fops, local); -#endif /* PRISM2_NO_STATION_MODES */ -} - - -void hostap_remove_proc(local_info_t *local) -{ - proc_remove(local->proc); -} - - -EXPORT_SYMBOL(hostap_init_proc); -EXPORT_SYMBOL(hostap_remove_proc); diff --git a/drivers/net/wireless/hostap/hostap_wlan.h b/drivers/net/wireless/hostap/hostap_wlan.h deleted file mode 100644 index ca25283e1c92..000000000000 --- a/drivers/net/wireless/hostap/hostap_wlan.h +++ /dev/null @@ -1,1047 +0,0 @@ -#ifndef HOSTAP_WLAN_H -#define HOSTAP_WLAN_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hostap_config.h" -#include "hostap_common.h" - -#define MAX_PARM_DEVICES 8 -#define PARM_MIN_MAX "1-" __MODULE_STRING(MAX_PARM_DEVICES) -#define DEF_INTS -1, -1, -1, -1, -1, -1, -1 -#define GET_INT_PARM(var,idx) var[var[idx] < 0 ? 0 : idx] - - -/* Specific skb->protocol value that indicates that the packet already contains - * txdesc header. - * FIX: This might need own value that would be allocated especially for Prism2 - * txdesc; ETH_P_CONTROL is commented as "Card specific control frames". - * However, these skb's should have only minimal path in the kernel side since - * prism2_send_mgmt() sends these with dev_queue_xmit() to prism2_tx(). */ -#define ETH_P_HOSTAP ETH_P_CONTROL - -/* ARPHRD_IEEE80211_PRISM uses a bloated version of Prism2 RX frame header - * (from linux-wlan-ng) */ -struct linux_wlan_ng_val { - u32 did; - u16 status, len; - u32 data; -} __packed; - -struct linux_wlan_ng_prism_hdr { - u32 msgcode, msglen; - char devname[16]; - struct linux_wlan_ng_val hosttime, mactime, channel, rssi, sq, signal, - noise, rate, istx, frmlen; -} __packed; - -struct linux_wlan_ng_cap_hdr { - __be32 version; - __be32 length; - __be64 mactime; - __be64 hosttime; - __be32 phytype; - __be32 channel; - __be32 datarate; - __be32 antenna; - __be32 priority; - __be32 ssi_type; - __be32 ssi_signal; - __be32 ssi_noise; - __be32 preamble; - __be32 encoding; -} __packed; - -struct hostap_radiotap_rx { - struct ieee80211_radiotap_header hdr; - __le64 tsft; - u8 rate; - u8 padding; - __le16 chan_freq; - __le16 chan_flags; - s8 dbm_antsignal; - s8 dbm_antnoise; -} __packed; - -#define LWNG_CAP_DID_BASE (4 | (1 << 6)) /* section 4, group 1 */ -#define LWNG_CAPHDR_VERSION 0x80211001 - -struct hfa384x_rx_frame { - /* HFA384X RX frame descriptor */ - __le16 status; /* HFA384X_RX_STATUS_ flags */ - __le32 time; /* timestamp, 1 microsecond resolution */ - u8 silence; /* 27 .. 154; seems to be 0 */ - u8 signal; /* 27 .. 154 */ - u8 rate; /* 10, 20, 55, or 110 */ - u8 rxflow; - __le32 reserved; - - /* 802.11 */ - __le16 frame_control; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - __le16 seq_ctrl; - u8 addr4[ETH_ALEN]; - __le16 data_len; - - /* 802.3 */ - u8 dst_addr[ETH_ALEN]; - u8 src_addr[ETH_ALEN]; - __be16 len; - - /* followed by frame data; max 2304 bytes */ -} __packed; - - -struct hfa384x_tx_frame { - /* HFA384X TX frame descriptor */ - __le16 status; /* HFA384X_TX_STATUS_ flags */ - __le16 reserved1; - __le16 reserved2; - __le32 sw_support; - u8 retry_count; /* not yet implemented */ - u8 tx_rate; /* Host AP only; 0 = firmware, or 10, 20, 55, 110 */ - __le16 tx_control; /* HFA384X_TX_CTRL_ flags */ - - /* 802.11 */ - __le16 frame_control; /* parts not used */ - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; /* filled by firmware */ - u8 addr3[ETH_ALEN]; - __le16 seq_ctrl; /* filled by firmware */ - u8 addr4[ETH_ALEN]; - __le16 data_len; - - /* 802.3 */ - u8 dst_addr[ETH_ALEN]; - u8 src_addr[ETH_ALEN]; - __be16 len; - - /* followed by frame data; max 2304 bytes */ -} __packed; - - -struct hfa384x_rid_hdr -{ - __le16 len; - __le16 rid; -} __packed; - - -/* Macro for converting signal levels (range 27 .. 154) to wireless ext - * dBm value with some accuracy */ -#define HFA384X_LEVEL_TO_dBm(v) 0x100 + (v) * 100 / 255 - 100 - -#define HFA384X_LEVEL_TO_dBm_sign(v) (v) * 100 / 255 - 100 - -struct hfa384x_scan_request { - __le16 channel_list; - __le16 txrate; /* HFA384X_RATES_* */ -} __packed; - -struct hfa384x_hostscan_request { - __le16 channel_list; - __le16 txrate; - __le16 target_ssid_len; - u8 target_ssid[32]; -} __packed; - -struct hfa384x_join_request { - u8 bssid[ETH_ALEN]; - __le16 channel; -} __packed; - -struct hfa384x_info_frame { - __le16 len; - __le16 type; -} __packed; - -struct hfa384x_comm_tallies { - __le16 tx_unicast_frames; - __le16 tx_multicast_frames; - __le16 tx_fragments; - __le16 tx_unicast_octets; - __le16 tx_multicast_octets; - __le16 tx_deferred_transmissions; - __le16 tx_single_retry_frames; - __le16 tx_multiple_retry_frames; - __le16 tx_retry_limit_exceeded; - __le16 tx_discards; - __le16 rx_unicast_frames; - __le16 rx_multicast_frames; - __le16 rx_fragments; - __le16 rx_unicast_octets; - __le16 rx_multicast_octets; - __le16 rx_fcs_errors; - __le16 rx_discards_no_buffer; - __le16 tx_discards_wrong_sa; - __le16 rx_discards_wep_undecryptable; - __le16 rx_message_in_msg_fragments; - __le16 rx_message_in_bad_msg_fragments; -} __packed; - -struct hfa384x_comm_tallies32 { - __le32 tx_unicast_frames; - __le32 tx_multicast_frames; - __le32 tx_fragments; - __le32 tx_unicast_octets; - __le32 tx_multicast_octets; - __le32 tx_deferred_transmissions; - __le32 tx_single_retry_frames; - __le32 tx_multiple_retry_frames; - __le32 tx_retry_limit_exceeded; - __le32 tx_discards; - __le32 rx_unicast_frames; - __le32 rx_multicast_frames; - __le32 rx_fragments; - __le32 rx_unicast_octets; - __le32 rx_multicast_octets; - __le32 rx_fcs_errors; - __le32 rx_discards_no_buffer; - __le32 tx_discards_wrong_sa; - __le32 rx_discards_wep_undecryptable; - __le32 rx_message_in_msg_fragments; - __le32 rx_message_in_bad_msg_fragments; -} __packed; - -struct hfa384x_scan_result_hdr { - __le16 reserved; - __le16 scan_reason; -#define HFA384X_SCAN_IN_PROGRESS 0 /* no results available yet */ -#define HFA384X_SCAN_HOST_INITIATED 1 -#define HFA384X_SCAN_FIRMWARE_INITIATED 2 -#define HFA384X_SCAN_INQUIRY_FROM_HOST 3 -} __packed; - -#define HFA384X_SCAN_MAX_RESULTS 32 - -struct hfa384x_scan_result { - __le16 chid; - __le16 anl; - __le16 sl; - u8 bssid[ETH_ALEN]; - __le16 beacon_interval; - __le16 capability; - __le16 ssid_len; - u8 ssid[32]; - u8 sup_rates[10]; - __le16 rate; -} __packed; - -struct hfa384x_hostscan_result { - __le16 chid; - __le16 anl; - __le16 sl; - u8 bssid[ETH_ALEN]; - __le16 beacon_interval; - __le16 capability; - __le16 ssid_len; - u8 ssid[32]; - u8 sup_rates[10]; - __le16 rate; - __le16 atim; -} __packed; - -struct comm_tallies_sums { - unsigned int tx_unicast_frames; - unsigned int tx_multicast_frames; - unsigned int tx_fragments; - unsigned int tx_unicast_octets; - unsigned int tx_multicast_octets; - unsigned int tx_deferred_transmissions; - unsigned int tx_single_retry_frames; - unsigned int tx_multiple_retry_frames; - unsigned int tx_retry_limit_exceeded; - unsigned int tx_discards; - unsigned int rx_unicast_frames; - unsigned int rx_multicast_frames; - unsigned int rx_fragments; - unsigned int rx_unicast_octets; - unsigned int rx_multicast_octets; - unsigned int rx_fcs_errors; - unsigned int rx_discards_no_buffer; - unsigned int tx_discards_wrong_sa; - unsigned int rx_discards_wep_undecryptable; - unsigned int rx_message_in_msg_fragments; - unsigned int rx_message_in_bad_msg_fragments; -}; - - -struct hfa384x_regs { - u16 cmd; - u16 evstat; - u16 offset0; - u16 offset1; - u16 swsupport0; -}; - - -#if defined(PRISM2_PCCARD) || defined(PRISM2_PLX) -/* I/O ports for HFA384X Controller access */ -#define HFA384X_CMD_OFF 0x00 -#define HFA384X_PARAM0_OFF 0x02 -#define HFA384X_PARAM1_OFF 0x04 -#define HFA384X_PARAM2_OFF 0x06 -#define HFA384X_STATUS_OFF 0x08 -#define HFA384X_RESP0_OFF 0x0A -#define HFA384X_RESP1_OFF 0x0C -#define HFA384X_RESP2_OFF 0x0E -#define HFA384X_INFOFID_OFF 0x10 -#define HFA384X_CONTROL_OFF 0x14 -#define HFA384X_SELECT0_OFF 0x18 -#define HFA384X_SELECT1_OFF 0x1A -#define HFA384X_OFFSET0_OFF 0x1C -#define HFA384X_OFFSET1_OFF 0x1E -#define HFA384X_RXFID_OFF 0x20 -#define HFA384X_ALLOCFID_OFF 0x22 -#define HFA384X_TXCOMPLFID_OFF 0x24 -#define HFA384X_SWSUPPORT0_OFF 0x28 -#define HFA384X_SWSUPPORT1_OFF 0x2A -#define HFA384X_SWSUPPORT2_OFF 0x2C -#define HFA384X_EVSTAT_OFF 0x30 -#define HFA384X_INTEN_OFF 0x32 -#define HFA384X_EVACK_OFF 0x34 -#define HFA384X_DATA0_OFF 0x36 -#define HFA384X_DATA1_OFF 0x38 -#define HFA384X_AUXPAGE_OFF 0x3A -#define HFA384X_AUXOFFSET_OFF 0x3C -#define HFA384X_AUXDATA_OFF 0x3E -#endif /* PRISM2_PCCARD || PRISM2_PLX */ - -#ifdef PRISM2_PCI -/* Memory addresses for ISL3874 controller access */ -#define HFA384X_CMD_OFF 0x00 -#define HFA384X_PARAM0_OFF 0x04 -#define HFA384X_PARAM1_OFF 0x08 -#define HFA384X_PARAM2_OFF 0x0C -#define HFA384X_STATUS_OFF 0x10 -#define HFA384X_RESP0_OFF 0x14 -#define HFA384X_RESP1_OFF 0x18 -#define HFA384X_RESP2_OFF 0x1C -#define HFA384X_INFOFID_OFF 0x20 -#define HFA384X_CONTROL_OFF 0x28 -#define HFA384X_SELECT0_OFF 0x30 -#define HFA384X_SELECT1_OFF 0x34 -#define HFA384X_OFFSET0_OFF 0x38 -#define HFA384X_OFFSET1_OFF 0x3C -#define HFA384X_RXFID_OFF 0x40 -#define HFA384X_ALLOCFID_OFF 0x44 -#define HFA384X_TXCOMPLFID_OFF 0x48 -#define HFA384X_PCICOR_OFF 0x4C -#define HFA384X_SWSUPPORT0_OFF 0x50 -#define HFA384X_SWSUPPORT1_OFF 0x54 -#define HFA384X_SWSUPPORT2_OFF 0x58 -#define HFA384X_PCIHCR_OFF 0x5C -#define HFA384X_EVSTAT_OFF 0x60 -#define HFA384X_INTEN_OFF 0x64 -#define HFA384X_EVACK_OFF 0x68 -#define HFA384X_DATA0_OFF 0x6C -#define HFA384X_DATA1_OFF 0x70 -#define HFA384X_AUXPAGE_OFF 0x74 -#define HFA384X_AUXOFFSET_OFF 0x78 -#define HFA384X_AUXDATA_OFF 0x7C -#define HFA384X_PCI_M0_ADDRH_OFF 0x80 -#define HFA384X_PCI_M0_ADDRL_OFF 0x84 -#define HFA384X_PCI_M0_LEN_OFF 0x88 -#define HFA384X_PCI_M0_CTL_OFF 0x8C -#define HFA384X_PCI_STATUS_OFF 0x98 -#define HFA384X_PCI_M1_ADDRH_OFF 0xA0 -#define HFA384X_PCI_M1_ADDRL_OFF 0xA4 -#define HFA384X_PCI_M1_LEN_OFF 0xA8 -#define HFA384X_PCI_M1_CTL_OFF 0xAC - -/* PCI bus master control bits (these are undocumented; based on guessing and - * experimenting..) */ -#define HFA384X_PCI_CTL_FROM_BAP (BIT(5) | BIT(1) | BIT(0)) -#define HFA384X_PCI_CTL_TO_BAP (BIT(5) | BIT(0)) - -#endif /* PRISM2_PCI */ - - -/* Command codes for CMD reg. */ -#define HFA384X_CMDCODE_INIT 0x00 -#define HFA384X_CMDCODE_ENABLE 0x01 -#define HFA384X_CMDCODE_DISABLE 0x02 -#define HFA384X_CMDCODE_ALLOC 0x0A -#define HFA384X_CMDCODE_TRANSMIT 0x0B -#define HFA384X_CMDCODE_INQUIRE 0x11 -#define HFA384X_CMDCODE_ACCESS 0x21 -#define HFA384X_CMDCODE_ACCESS_WRITE (0x21 | BIT(8)) -#define HFA384X_CMDCODE_DOWNLOAD 0x22 -#define HFA384X_CMDCODE_READMIF 0x30 -#define HFA384X_CMDCODE_WRITEMIF 0x31 -#define HFA384X_CMDCODE_TEST 0x38 - -#define HFA384X_CMDCODE_MASK 0x3F - -/* Test mode operations */ -#define HFA384X_TEST_CHANGE_CHANNEL 0x08 -#define HFA384X_TEST_MONITOR 0x0B -#define HFA384X_TEST_STOP 0x0F -#define HFA384X_TEST_CFG_BITS 0x15 -#define HFA384X_TEST_CFG_BIT_ALC BIT(3) - -#define HFA384X_CMD_BUSY BIT(15) - -#define HFA384X_CMD_TX_RECLAIM BIT(8) - -#define HFA384X_OFFSET_ERR BIT(14) -#define HFA384X_OFFSET_BUSY BIT(15) - - -/* ProgMode for download command */ -#define HFA384X_PROGMODE_DISABLE 0 -#define HFA384X_PROGMODE_ENABLE_VOLATILE 1 -#define HFA384X_PROGMODE_ENABLE_NON_VOLATILE 2 -#define HFA384X_PROGMODE_PROGRAM_NON_VOLATILE 3 - -#define HFA384X_AUX_MAGIC0 0xfe01 -#define HFA384X_AUX_MAGIC1 0xdc23 -#define HFA384X_AUX_MAGIC2 0xba45 - -#define HFA384X_AUX_PORT_DISABLED 0 -#define HFA384X_AUX_PORT_DISABLE BIT(14) -#define HFA384X_AUX_PORT_ENABLE BIT(15) -#define HFA384X_AUX_PORT_ENABLED (BIT(14) | BIT(15)) -#define HFA384X_AUX_PORT_MASK (BIT(14) | BIT(15)) - -#define PRISM2_PDA_SIZE 1024 - - -/* Events; EvStat, Interrupt mask (IntEn), and acknowledge bits (EvAck) */ -#define HFA384X_EV_TICK BIT(15) -#define HFA384X_EV_WTERR BIT(14) -#define HFA384X_EV_INFDROP BIT(13) -#ifdef PRISM2_PCI -#define HFA384X_EV_PCI_M1 BIT(9) -#define HFA384X_EV_PCI_M0 BIT(8) -#endif /* PRISM2_PCI */ -#define HFA384X_EV_INFO BIT(7) -#define HFA384X_EV_DTIM BIT(5) -#define HFA384X_EV_CMD BIT(4) -#define HFA384X_EV_ALLOC BIT(3) -#define HFA384X_EV_TXEXC BIT(2) -#define HFA384X_EV_TX BIT(1) -#define HFA384X_EV_RX BIT(0) - - -/* HFA384X Information frames */ -#define HFA384X_INFO_HANDOVERADDR 0xF000 /* AP f/w ? */ -#define HFA384X_INFO_HANDOVERDEAUTHADDR 0xF001 /* AP f/w 1.3.7 */ -#define HFA384X_INFO_COMMTALLIES 0xF100 -#define HFA384X_INFO_SCANRESULTS 0xF101 -#define HFA384X_INFO_CHANNELINFORESULTS 0xF102 /* AP f/w only */ -#define HFA384X_INFO_HOSTSCANRESULTS 0xF103 -#define HFA384X_INFO_LINKSTATUS 0xF200 -#define HFA384X_INFO_ASSOCSTATUS 0xF201 /* ? */ -#define HFA384X_INFO_AUTHREQ 0xF202 /* ? */ -#define HFA384X_INFO_PSUSERCNT 0xF203 /* ? */ -#define HFA384X_INFO_KEYIDCHANGED 0xF204 /* ? */ - -enum { HFA384X_LINKSTATUS_CONNECTED = 1, - HFA384X_LINKSTATUS_DISCONNECTED = 2, - HFA384X_LINKSTATUS_AP_CHANGE = 3, - HFA384X_LINKSTATUS_AP_OUT_OF_RANGE = 4, - HFA384X_LINKSTATUS_AP_IN_RANGE = 5, - HFA384X_LINKSTATUS_ASSOC_FAILED = 6 }; - -enum { HFA384X_PORTTYPE_BSS = 1, HFA384X_PORTTYPE_WDS = 2, - HFA384X_PORTTYPE_PSEUDO_IBSS = 3, HFA384X_PORTTYPE_IBSS = 0, - HFA384X_PORTTYPE_HOSTAP = 6 }; - -#define HFA384X_RATES_1MBPS BIT(0) -#define HFA384X_RATES_2MBPS BIT(1) -#define HFA384X_RATES_5MBPS BIT(2) -#define HFA384X_RATES_11MBPS BIT(3) - -#define HFA384X_ROAMING_FIRMWARE 1 -#define HFA384X_ROAMING_HOST 2 -#define HFA384X_ROAMING_DISABLED 3 - -#define HFA384X_WEPFLAGS_PRIVACYINVOKED BIT(0) -#define HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED BIT(1) -#define HFA384X_WEPFLAGS_HOSTENCRYPT BIT(4) -#define HFA384X_WEPFLAGS_HOSTDECRYPT BIT(7) - -#define HFA384X_RX_STATUS_MSGTYPE (BIT(15) | BIT(14) | BIT(13)) -#define HFA384X_RX_STATUS_PCF BIT(12) -#define HFA384X_RX_STATUS_MACPORT (BIT(10) | BIT(9) | BIT(8)) -#define HFA384X_RX_STATUS_UNDECR BIT(1) -#define HFA384X_RX_STATUS_FCSERR BIT(0) - -#define HFA384X_RX_STATUS_GET_MSGTYPE(s) \ -(((s) & HFA384X_RX_STATUS_MSGTYPE) >> 13) -#define HFA384X_RX_STATUS_GET_MACPORT(s) \ -(((s) & HFA384X_RX_STATUS_MACPORT) >> 8) - -enum { HFA384X_RX_MSGTYPE_NORMAL = 0, HFA384X_RX_MSGTYPE_RFC1042 = 1, - HFA384X_RX_MSGTYPE_BRIDGETUNNEL = 2, HFA384X_RX_MSGTYPE_MGMT = 4 }; - - -#define HFA384X_TX_CTRL_ALT_RTRY BIT(5) -#define HFA384X_TX_CTRL_802_11 BIT(3) -#define HFA384X_TX_CTRL_802_3 0 -#define HFA384X_TX_CTRL_TX_EX BIT(2) -#define HFA384X_TX_CTRL_TX_OK BIT(1) - -#define HFA384X_TX_STATUS_RETRYERR BIT(0) -#define HFA384X_TX_STATUS_AGEDERR BIT(1) -#define HFA384X_TX_STATUS_DISCON BIT(2) -#define HFA384X_TX_STATUS_FORMERR BIT(3) - -/* HFA3861/3863 (BBP) Control Registers */ -#define HFA386X_CR_TX_CONFIGURE 0x12 /* CR9 */ -#define HFA386X_CR_RX_CONFIGURE 0x14 /* CR10 */ -#define HFA386X_CR_A_D_TEST_MODES2 0x1A /* CR13 */ -#define HFA386X_CR_MANUAL_TX_POWER 0x3E /* CR31 */ -#define HFA386X_CR_MEASURED_TX_POWER 0x74 /* CR58 */ - - -#ifdef __KERNEL__ - -#define PRISM2_TXFID_COUNT 8 -#define PRISM2_DATA_MAXLEN 2304 -#define PRISM2_TXFID_LEN (PRISM2_DATA_MAXLEN + sizeof(struct hfa384x_tx_frame)) -#define PRISM2_TXFID_EMPTY 0xffff -#define PRISM2_TXFID_RESERVED 0xfffe -#define PRISM2_DUMMY_FID 0xffff -#define MAX_SSID_LEN 32 -#define MAX_NAME_LEN 32 /* this is assumed to be equal to MAX_SSID_LEN */ - -#define PRISM2_DUMP_RX_HDR BIT(0) -#define PRISM2_DUMP_TX_HDR BIT(1) -#define PRISM2_DUMP_TXEXC_HDR BIT(2) - -struct hostap_tx_callback_info { - u16 idx; - void (*func)(struct sk_buff *, int ok, void *); - void *data; - struct hostap_tx_callback_info *next; -}; - - -/* IEEE 802.11 requires that STA supports concurrent reception of at least - * three fragmented frames. This define can be increased to support more - * concurrent frames, but it should be noted that each entry can consume about - * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ -#define PRISM2_FRAG_CACHE_LEN 4 - -struct prism2_frag_entry { - unsigned long first_frag_time; - unsigned int seq; - unsigned int last_frag; - struct sk_buff *skb; - u8 src_addr[ETH_ALEN]; - u8 dst_addr[ETH_ALEN]; -}; - - -struct hostap_cmd_queue { - struct list_head list; - wait_queue_head_t compl; - volatile enum { CMD_SLEEP, CMD_CALLBACK, CMD_COMPLETED } type; - void (*callback)(struct net_device *dev, long context, u16 resp0, - u16 res); - long context; - u16 cmd, param0, param1; - u16 resp0, res; - volatile int issued, issuing; - - atomic_t usecnt; - int del_req; -}; - -/* options for hw_shutdown */ -#define HOSTAP_HW_NO_DISABLE BIT(0) -#define HOSTAP_HW_ENABLE_CMDCOMPL BIT(1) - -typedef struct local_info local_info_t; - -struct prism2_helper_functions { - /* these functions are defined in hardware model specific files - * (hostap_{cs,plx,pci}.c */ - int (*card_present)(local_info_t *local); - void (*cor_sreset)(local_info_t *local); - void (*genesis_reset)(local_info_t *local, int hcr); - - /* the following functions are from hostap_hw.c, but they may have some - * hardware model specific code */ - - /* FIX: low-level commands like cmd might disappear at some point to - * make it easier to change them if needed (e.g., cmd would be replaced - * with write_mif/read_mif/testcmd/inquire); at least get_rid and - * set_rid might move to hostap_{cs,plx,pci}.c */ - int (*cmd)(struct net_device *dev, u16 cmd, u16 param0, u16 *param1, - u16 *resp0); - void (*read_regs)(struct net_device *dev, struct hfa384x_regs *regs); - int (*get_rid)(struct net_device *dev, u16 rid, void *buf, int len, - int exact_len); - int (*set_rid)(struct net_device *dev, u16 rid, void *buf, int len); - int (*hw_enable)(struct net_device *dev, int initial); - int (*hw_config)(struct net_device *dev, int initial); - void (*hw_reset)(struct net_device *dev); - void (*hw_shutdown)(struct net_device *dev, int no_disable); - int (*reset_port)(struct net_device *dev); - void (*schedule_reset)(local_info_t *local); - int (*download)(local_info_t *local, - struct prism2_download_param *param); - int (*tx)(struct sk_buff *skb, struct net_device *dev); - int (*set_tim)(struct net_device *dev, int aid, int set); - const struct file_operations *read_aux_fops; - - int need_tx_headroom; /* number of bytes of headroom needed before - * IEEE 802.11 header */ - enum { HOSTAP_HW_PCCARD, HOSTAP_HW_PLX, HOSTAP_HW_PCI } hw_type; -}; - - -struct prism2_download_data { - u32 dl_cmd; - u32 start_addr; - u32 num_areas; - struct prism2_download_data_area { - u32 addr; /* wlan card address */ - u32 len; - u8 *data; /* allocated data */ - } data[0]; -}; - - -#define HOSTAP_MAX_BSS_COUNT 64 -#define MAX_WPA_IE_LEN 64 - -struct hostap_bss_info { - struct list_head list; - unsigned long last_update; - unsigned int count; - u8 bssid[ETH_ALEN]; - u16 capab_info; - u8 ssid[32]; - size_t ssid_len; - u8 wpa_ie[MAX_WPA_IE_LEN]; - size_t wpa_ie_len; - u8 rsn_ie[MAX_WPA_IE_LEN]; - size_t rsn_ie_len; - int chan; - int included; -}; - - -/* Per radio private Host AP data - shared by all net devices interfaces used - * by each radio (wlan#, wlan#ap, wlan#sta, WDS). - * ((struct hostap_interface *) netdev_priv(dev))->local points to this - * structure. */ -struct local_info { - struct module *hw_module; - int card_idx; - int dev_enabled; - int master_dev_auto_open; /* was master device opened automatically */ - int num_dev_open; /* number of open devices */ - struct net_device *dev; /* master radio device */ - struct net_device *ddev; /* main data device */ - struct list_head hostap_interfaces; /* Host AP interface list (contains - * struct hostap_interface entries) - */ - rwlock_t iface_lock; /* hostap_interfaces read lock; use write lock - * when removing entries from the list. - * TX and RX paths can use read lock. */ - spinlock_t cmdlock, baplock, lock, irq_init_lock; - struct mutex rid_bap_mtx; - u16 infofid; /* MAC buffer id for info frame */ - /* txfid, intransmitfid, next_txtid, and next_alloc are protected by - * txfidlock */ - spinlock_t txfidlock; - int txfid_len; /* length of allocated TX buffers */ - u16 txfid[PRISM2_TXFID_COUNT]; /* buffer IDs for TX frames */ - /* buffer IDs for intransmit frames or PRISM2_TXFID_EMPTY if - * corresponding txfid is free for next TX frame */ - u16 intransmitfid[PRISM2_TXFID_COUNT]; - int next_txfid; /* index to the next txfid to be checked for - * availability */ - int next_alloc; /* index to the next intransmitfid to be checked for - * allocation events */ - - /* bitfield for atomic bitops */ -#define HOSTAP_BITS_TRANSMIT 0 -#define HOSTAP_BITS_BAP_TASKLET 1 -#define HOSTAP_BITS_BAP_TASKLET2 2 - unsigned long bits; - - struct ap_data *ap; - - char essid[MAX_SSID_LEN + 1]; - char name[MAX_NAME_LEN + 1]; - int name_set; - u16 channel_mask; /* mask of allowed channels */ - u16 scan_channel_mask; /* mask of channels to be scanned */ - struct comm_tallies_sums comm_tallies; - struct proc_dir_entry *proc; - int iw_mode; /* operating mode (IW_MODE_*) */ - int pseudo_adhoc; /* 0: IW_MODE_ADHOC is real 802.11 compliant IBSS - * 1: IW_MODE_ADHOC is "pseudo IBSS" */ - char bssid[ETH_ALEN]; - int channel; - int beacon_int; - int dtim_period; - int mtu; - int frame_dump; /* dump RX/TX frame headers, PRISM2_DUMP_ flags */ - int fw_tx_rate_control; - u16 tx_rate_control; - u16 basic_rates; - int hw_resetting; - int hw_ready; - int hw_reset_tries; /* how many times reset has been tried */ - int hw_downloading; - int shutdown; - int pri_only; - int no_pri; /* no PRI f/w present */ - int sram_type; /* 8 = x8 SRAM, 16 = x16 SRAM, -1 = unknown */ - - enum { - PRISM2_TXPOWER_AUTO = 0, PRISM2_TXPOWER_OFF, - PRISM2_TXPOWER_FIXED, PRISM2_TXPOWER_UNKNOWN - } txpower_type; - int txpower; /* if txpower_type == PRISM2_TXPOWER_FIXED */ - - /* command queue for hfa384x_cmd(); protected with cmdlock */ - struct list_head cmd_queue; - /* max_len for cmd_queue; in addition, cmd_callback can use two - * additional entries to prevent sleeping commands from stopping - * transmits */ -#define HOSTAP_CMD_QUEUE_MAX_LEN 16 - int cmd_queue_len; /* number of entries in cmd_queue */ - - /* if card timeout is detected in interrupt context, reset_queue is - * used to schedule card reseting to be done in user context */ - struct work_struct reset_queue; - - /* For scheduling a change of the promiscuous mode RID */ - int is_promisc; - struct work_struct set_multicast_list_queue; - - struct work_struct set_tim_queue; - struct list_head set_tim_list; - spinlock_t set_tim_lock; - - int wds_max_connections; - int wds_connections; -#define HOSTAP_WDS_BROADCAST_RA BIT(0) -#define HOSTAP_WDS_AP_CLIENT BIT(1) -#define HOSTAP_WDS_STANDARD_FRAME BIT(2) - u32 wds_type; - u16 tx_control; /* flags to be used in TX description */ - int manual_retry_count; /* -1 = use f/w default; otherwise retry count - * to be used with all frames */ - - struct iw_statistics wstats; - unsigned long scan_timestamp; /* Time started to scan */ - enum { - PRISM2_MONITOR_80211 = 0, PRISM2_MONITOR_PRISM = 1, - PRISM2_MONITOR_CAPHDR = 2, PRISM2_MONITOR_RADIOTAP = 3 - } monitor_type; - int monitor_allow_fcserr; - - int hostapd; /* whether user space daemon, hostapd, is used for AP - * management */ - int hostapd_sta; /* whether hostapd is used with an extra STA interface - */ - struct net_device *apdev; - struct net_device_stats apdevstats; - - char assoc_ap_addr[ETH_ALEN]; - struct net_device *stadev; - struct net_device_stats stadevstats; - -#define WEP_KEYS 4 -#define WEP_KEY_LEN 13 - struct lib80211_crypt_info crypt_info; - - int open_wep; /* allow unencrypted frames */ - int host_encrypt; - int host_decrypt; - int privacy_invoked; /* force privacy invoked flag even if no keys are - * configured */ - int fw_encrypt_ok; /* whether firmware-based WEP encrypt is working - * in Host AP mode (STA f/w 1.4.9 or newer) */ - int bcrx_sta_key; /* use individual keys to override default keys even - * with RX of broad/multicast frames */ - - struct prism2_frag_entry frag_cache[PRISM2_FRAG_CACHE_LEN]; - unsigned int frag_next_idx; - - int ieee_802_1x; /* is IEEE 802.1X used */ - - int antsel_tx, antsel_rx; - int rts_threshold; /* dot11RTSThreshold */ - int fragm_threshold; /* dot11FragmentationThreshold */ - int auth_algs; /* PRISM2_AUTH_ flags */ - - int enh_sec; /* cnfEnhSecurity options (broadcast SSID hide/ignore) */ - int tallies32; /* 32-bit tallies in use */ - - struct prism2_helper_functions *func; - - u8 *pda; - int fw_ap; -#define PRISM2_FW_VER(major, minor, variant) \ -(((major) << 16) | ((minor) << 8) | variant) - u32 sta_fw_ver; - - /* Tasklets for handling hardware IRQ related operations outside hw IRQ - * handler */ - struct tasklet_struct bap_tasklet; - - struct tasklet_struct info_tasklet; - struct sk_buff_head info_list; /* info frames as skb's for - * info_tasklet */ - - struct hostap_tx_callback_info *tx_callback; /* registered TX callbacks - */ - - struct tasklet_struct rx_tasklet; - struct sk_buff_head rx_list; - - struct tasklet_struct sta_tx_exc_tasklet; - struct sk_buff_head sta_tx_exc_list; - - int host_roaming; - unsigned long last_join_time; /* time of last JoinRequest */ - struct hfa384x_hostscan_result *last_scan_results; - int last_scan_results_count; - enum { PRISM2_SCAN, PRISM2_HOSTSCAN } last_scan_type; - struct work_struct info_queue; - unsigned long pending_info; /* bit field of pending info_queue items */ -#define PRISM2_INFO_PENDING_LINKSTATUS 0 -#define PRISM2_INFO_PENDING_SCANRESULTS 1 - int prev_link_status; /* previous received LinkStatus info */ - int prev_linkstatus_connected; - u8 preferred_ap[ETH_ALEN]; /* use this AP if possible */ - -#ifdef PRISM2_CALLBACK - void *callback_data; /* Can be used in callbacks; e.g., allocate - * on enable event and free on disable event. - * Host AP driver code does not touch this. */ -#endif /* PRISM2_CALLBACK */ - - wait_queue_head_t hostscan_wq; - - /* Passive scan in Host AP mode */ - struct timer_list passive_scan_timer; - int passive_scan_interval; /* in seconds, 0 = disabled */ - int passive_scan_channel; - enum { PASSIVE_SCAN_WAIT, PASSIVE_SCAN_LISTEN } passive_scan_state; - - struct timer_list tick_timer; - unsigned long last_tick_timer; - unsigned int sw_tick_stuck; - - /* commsQuality / dBmCommsQuality data from periodic polling; only - * valid for Managed and Ad-hoc modes */ - unsigned long last_comms_qual_update; - int comms_qual; /* in some odd unit.. */ - int avg_signal; /* in dB (note: negative) */ - int avg_noise; /* in dB (note: negative) */ - struct work_struct comms_qual_update; - - /* RSSI to dBm adjustment (for RX descriptor fields) */ - int rssi_to_dBm; /* subtract from RSSI to get approximate dBm value */ - - /* BSS list / protected by local->lock */ - struct list_head bss_list; - int num_bss_info; - int wpa; /* WPA support enabled */ - int tkip_countermeasures; - int drop_unencrypted; - /* Generic IEEE 802.11 info element to be added to - * ProbeResp/Beacon/(Re)AssocReq */ - u8 *generic_elem; - size_t generic_elem_len; - -#ifdef PRISM2_DOWNLOAD_SUPPORT - /* Persistent volatile download data */ - struct prism2_download_data *dl_pri; - struct prism2_download_data *dl_sec; -#endif /* PRISM2_DOWNLOAD_SUPPORT */ - -#ifdef PRISM2_IO_DEBUG -#define PRISM2_IO_DEBUG_SIZE 10000 - u32 io_debug[PRISM2_IO_DEBUG_SIZE]; - int io_debug_head; - int io_debug_enabled; -#endif /* PRISM2_IO_DEBUG */ - - /* Pointer to hardware model specific (cs,pci,plx) private data. */ - void *hw_priv; -}; - - -/* Per interface private Host AP data - * Allocated for each net device that Host AP uses (wlan#, wlan#ap, wlan#sta, - * WDS) and netdev_priv(dev) points to this structure. */ -struct hostap_interface { - struct list_head list; /* list entry in Host AP interface list */ - struct net_device *dev; /* pointer to this device */ - struct local_info *local; /* pointer to shared private data */ - struct net_device_stats stats; - struct iw_spy_data spy_data; /* iwspy support */ - struct iw_public_data wireless_data; - - enum { - HOSTAP_INTERFACE_MASTER, - HOSTAP_INTERFACE_MAIN, - HOSTAP_INTERFACE_AP, - HOSTAP_INTERFACE_STA, - HOSTAP_INTERFACE_WDS, - } type; - - union { - struct hostap_interface_wds { - u8 remote_addr[ETH_ALEN]; - } wds; - } u; -}; - - -#define HOSTAP_SKB_TX_DATA_MAGIC 0xf08a36a2 - -/* - * TX meta data - stored in skb->cb buffer, so this must not be increased over - * the 48-byte limit. - * THE PADDING THIS STARTS WITH IS A HORRIBLE HACK THAT SHOULD NOT LIVE - * TO SEE THE DAY. - */ -struct hostap_skb_tx_data { - unsigned int __padding_for_default_qdiscs; - u32 magic; /* HOSTAP_SKB_TX_DATA_MAGIC */ - u8 rate; /* transmit rate */ -#define HOSTAP_TX_FLAGS_WDS BIT(0) -#define HOSTAP_TX_FLAGS_BUFFERED_FRAME BIT(1) -#define HOSTAP_TX_FLAGS_ADD_MOREDATA BIT(2) - u8 flags; /* HOSTAP_TX_FLAGS_* */ - u16 tx_cb_idx; - struct hostap_interface *iface; - unsigned long jiffies; /* queueing timestamp */ - unsigned short ethertype; -}; - - -#ifndef PRISM2_NO_DEBUG - -#define DEBUG_FID BIT(0) -#define DEBUG_PS BIT(1) -#define DEBUG_FLOW BIT(2) -#define DEBUG_AP BIT(3) -#define DEBUG_HW BIT(4) -#define DEBUG_EXTRA BIT(5) -#define DEBUG_EXTRA2 BIT(6) -#define DEBUG_PS2 BIT(7) -#define DEBUG_MASK (DEBUG_PS | DEBUG_AP | DEBUG_HW | DEBUG_EXTRA) -#define PDEBUG(n, args...) \ -do { if ((n) & DEBUG_MASK) printk(KERN_DEBUG args); } while (0) -#define PDEBUG2(n, args...) \ -do { if ((n) & DEBUG_MASK) printk(args); } while (0) - -#else /* PRISM2_NO_DEBUG */ - -#define PDEBUG(n, args...) -#define PDEBUG2(n, args...) - -#endif /* PRISM2_NO_DEBUG */ - -enum { BAP0 = 0, BAP1 = 1 }; - -#define PRISM2_IO_DEBUG_CMD_INB 0 -#define PRISM2_IO_DEBUG_CMD_INW 1 -#define PRISM2_IO_DEBUG_CMD_INSW 2 -#define PRISM2_IO_DEBUG_CMD_OUTB 3 -#define PRISM2_IO_DEBUG_CMD_OUTW 4 -#define PRISM2_IO_DEBUG_CMD_OUTSW 5 -#define PRISM2_IO_DEBUG_CMD_ERROR 6 -#define PRISM2_IO_DEBUG_CMD_INTERRUPT 7 - -#ifdef PRISM2_IO_DEBUG - -#define PRISM2_IO_DEBUG_ENTRY(cmd, reg, value) \ -(((cmd) << 24) | ((reg) << 16) | value) - -static inline void prism2_io_debug_add(struct net_device *dev, int cmd, - int reg, int value) -{ - struct hostap_interface *iface = netdev_priv(dev); - local_info_t *local = iface->local; - - if (!local->io_debug_enabled) - return; - - local->io_debug[local->io_debug_head] = jiffies & 0xffffffff; - if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE) - local->io_debug_head = 0; - local->io_debug[local->io_debug_head] = - PRISM2_IO_DEBUG_ENTRY(cmd, reg, value); - if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE) - local->io_debug_head = 0; -} - - -static inline void prism2_io_debug_error(struct net_device *dev, int err) -{ - struct hostap_interface *iface = netdev_priv(dev); - local_info_t *local = iface->local; - unsigned long flags; - - if (!local->io_debug_enabled) - return; - - spin_lock_irqsave(&local->lock, flags); - prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_ERROR, 0, err); - if (local->io_debug_enabled == 1) { - local->io_debug_enabled = 0; - printk(KERN_DEBUG "%s: I/O debug stopped\n", dev->name); - } - spin_unlock_irqrestore(&local->lock, flags); -} - -#else /* PRISM2_IO_DEBUG */ - -static inline void prism2_io_debug_add(struct net_device *dev, int cmd, - int reg, int value) -{ -} - -static inline void prism2_io_debug_error(struct net_device *dev, int err) -{ -} - -#endif /* PRISM2_IO_DEBUG */ - - -#ifdef PRISM2_CALLBACK -enum { - /* Called when card is enabled */ - PRISM2_CALLBACK_ENABLE, - - /* Called when card is disabled */ - PRISM2_CALLBACK_DISABLE, - - /* Called when RX/TX starts/ends */ - PRISM2_CALLBACK_RX_START, PRISM2_CALLBACK_RX_END, - PRISM2_CALLBACK_TX_START, PRISM2_CALLBACK_TX_END -}; -void prism2_callback(local_info_t *local, int event); -#else /* PRISM2_CALLBACK */ -#define prism2_callback(d, e) do { } while (0) -#endif /* PRISM2_CALLBACK */ - -#endif /* __KERNEL__ */ - -#endif /* HOSTAP_WLAN_H */ diff --git a/drivers/net/wireless/intersil/Kconfig b/drivers/net/wireless/intersil/Kconfig new file mode 100644 index 000000000000..ec80b9117fd2 --- /dev/null +++ b/drivers/net/wireless/intersil/Kconfig @@ -0,0 +1,16 @@ +config WLAN_VENDOR_INTERSIL + bool "Intersil devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_INTERSIL + +source "drivers/net/wireless/intersil/hostap/Kconfig" + +endif # WLAN_VENDOR_INTERSIL diff --git a/drivers/net/wireless/intersil/Makefile b/drivers/net/wireless/intersil/Makefile new file mode 100644 index 000000000000..4890ef79f280 --- /dev/null +++ b/drivers/net/wireless/intersil/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_HOSTAP) += hostap/ diff --git a/drivers/net/wireless/intersil/hostap/Kconfig b/drivers/net/wireless/intersil/hostap/Kconfig new file mode 100644 index 000000000000..287d82728bc3 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/Kconfig @@ -0,0 +1,98 @@ +config HOSTAP + tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)" + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + select CRYPTO + select CRYPTO_ARC4 + select CRYPTO_ECB + select CRYPTO_AES + select CRYPTO_MICHAEL_MIC + select CRYPTO_ECB + select CRC32 + select LIB80211 + select LIB80211_CRYPT_WEP + select LIB80211_CRYPT_TKIP + select LIB80211_CRYPT_CCMP + ---help--- + Shared driver code for IEEE 802.11b wireless cards based on + Intersil Prism2/2.5/3 chipset. This driver supports so called + Host AP mode that allows the card to act as an IEEE 802.11 + access point. + + See for more information about the + Host AP driver configuration and tools. This site includes + information and tools (hostapd and wpa_supplicant) for WPA/WPA2 + support. + + This option includes the base Host AP driver code that is shared by + different hardware models. You will also need to enable support for + PLX/PCI/CS version of the driver to actually use the driver. + + The driver can be compiled as a module and it will be called + hostap. + +config HOSTAP_FIRMWARE + bool "Support downloading firmware images with Host AP driver" + depends on HOSTAP + ---help--- + Configure Host AP driver to include support for firmware image + download. This option by itself only enables downloading to the + volatile memory, i.e. the card RAM. This option is required to + support cards that don't have firmware in flash, such as D-Link + DWL-520 rev E and D-Link DWL-650 rev P. + + Firmware image downloading needs a user space tool, prism2_srec. + It is available from http://hostap.epitest.fi/. + +config HOSTAP_FIRMWARE_NVRAM + bool "Support for non-volatile firmware download" + depends on HOSTAP_FIRMWARE + ---help--- + Allow Host AP driver to write firmware images to the non-volatile + card memory, i.e. flash memory that survives power cycling. + Enable this option if you want to be able to change card firmware + permanently. + + Firmware image downloading needs a user space tool, prism2_srec. + It is available from http://hostap.epitest.fi/. + +config HOSTAP_PLX + tristate "Host AP driver for Prism2/2.5/3 in PLX9052 PCI adaptors" + depends on PCI && HOSTAP + ---help--- + Host AP driver's version for Prism2/2.5/3 PC Cards in PLX9052 based + PCI adaptors. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named + hostap_plx. + +config HOSTAP_PCI + tristate "Host AP driver for Prism2.5 PCI adaptors" + depends on PCI && HOSTAP + ---help--- + Host AP driver's version for Prism2.5 PCI adaptors. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named + hostap_pci. + +config HOSTAP_CS + tristate "Host AP driver for Prism2/2.5/3 PC Cards" + depends on PCMCIA && HOSTAP + ---help--- + Host AP driver's version for Prism2/2.5/3 PC Cards. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named + hostap_cs. diff --git a/drivers/net/wireless/intersil/hostap/Makefile b/drivers/net/wireless/intersil/hostap/Makefile new file mode 100644 index 000000000000..b8e41a702c00 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/Makefile @@ -0,0 +1,7 @@ +hostap-y := hostap_80211_rx.o hostap_80211_tx.o hostap_ap.o hostap_info.o \ + hostap_ioctl.o hostap_main.o hostap_proc.o +obj-$(CONFIG_HOSTAP) += hostap.o + +obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o +obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o +obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o diff --git a/drivers/net/wireless/intersil/hostap/hostap.h b/drivers/net/wireless/intersil/hostap/hostap.h new file mode 100644 index 000000000000..ce8721fbc10e --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap.h @@ -0,0 +1,95 @@ +#ifndef HOSTAP_H +#define HOSTAP_H + +#include +#include + +#include "hostap_wlan.h" +#include "hostap_ap.h" + +static const long freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; +#define FREQ_COUNT ARRAY_SIZE(freq_list) + +/* hostap.c */ + +extern struct proc_dir_entry *hostap_proc; + +u16 hostap_tx_callback_register(local_info_t *local, + void (*func)(struct sk_buff *, int ok, void *), + void *data); +int hostap_tx_callback_unregister(local_info_t *local, u16 idx); +int hostap_set_word(struct net_device *dev, int rid, u16 val); +int hostap_set_string(struct net_device *dev, int rid, const char *val); +u16 hostap_get_porttype(local_info_t *local); +int hostap_set_encryption(local_info_t *local); +int hostap_set_antsel(local_info_t *local); +int hostap_set_roaming(local_info_t *local); +int hostap_set_auth_algs(local_info_t *local); +void hostap_dump_rx_header(const char *name, + const struct hfa384x_rx_frame *rx); +void hostap_dump_tx_header(const char *name, + const struct hfa384x_tx_frame *tx); +extern const struct header_ops hostap_80211_ops; +int hostap_80211_get_hdrlen(__le16 fc); +struct net_device_stats *hostap_get_stats(struct net_device *dev); +void hostap_setup_dev(struct net_device *dev, local_info_t *local, + int type); +void hostap_set_multicast_list_queue(struct work_struct *work); +int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked); +int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked); +void hostap_cleanup(local_info_t *local); +void hostap_cleanup_handler(void *data); +struct net_device * hostap_add_interface(struct local_info *local, + int type, int rtnl_locked, + const char *prefix, const char *name); +void hostap_remove_interface(struct net_device *dev, int rtnl_locked, + int remove_from_list); +int prism2_update_comms_qual(struct net_device *dev); +int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype, + u8 *body, size_t bodylen); +int prism2_sta_deauth(local_info_t *local, u16 reason); +int prism2_wds_add(local_info_t *local, u8 *remote_addr, + int rtnl_locked); +int prism2_wds_del(local_info_t *local, u8 *remote_addr, + int rtnl_locked, int do_not_remove); + + +/* hostap_ap.c */ + +int ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac); +int ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac); +void ap_control_flush_macs(struct mac_restrictions *mac_restrictions); +int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac); +void ap_control_kickall(struct ap_data *ap); +void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, + struct lib80211_crypt_data ***crypt); +int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], + struct iw_quality qual[], int buf_size, + int aplist); +int prism2_ap_translate_scan(struct net_device *dev, + struct iw_request_info *info, char *buffer); +int prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param); + + +/* hostap_proc.c */ + +void hostap_init_proc(local_info_t *local); +void hostap_remove_proc(local_info_t *local); + + +/* hostap_info.c */ + +void hostap_info_init(local_info_t *local); +void hostap_info_process(local_info_t *local, struct sk_buff *skb); + + +/* hostap_ioctl.c */ + +extern const struct iw_handler_def hostap_iw_handler_def; +extern const struct ethtool_ops prism2_ethtool_ops; + +int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); + + +#endif /* HOSTAP_H */ diff --git a/drivers/net/wireless/intersil/hostap/hostap_80211.h b/drivers/net/wireless/intersil/hostap/hostap_80211.h new file mode 100644 index 000000000000..ed98ce7c8f65 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_80211.h @@ -0,0 +1,96 @@ +#ifndef HOSTAP_80211_H +#define HOSTAP_80211_H + +#include +#include +#include + +struct hostap_ieee80211_mgmt { + __le16 frame_control; + __le16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + __le16 seq_ctrl; + union { + struct { + __le16 auth_alg; + __le16 auth_transaction; + __le16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; + } __packed auth; + struct { + __le16 reason_code; + } __packed deauth; + struct { + __le16 capab_info; + __le16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __packed assoc_req; + struct { + __le16 capab_info; + __le16 status_code; + __le16 aid; + /* followed by Supported rates */ + u8 variable[0]; + } __packed assoc_resp, reassoc_resp; + struct { + __le16 capab_info; + __le16 listen_interval; + u8 current_ap[6]; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __packed reassoc_req; + struct { + __le16 reason_code; + } __packed disassoc; + struct { + } __packed probe_req; + struct { + u8 timestamp[8]; + __le16 beacon_int; + __le16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params, TIM */ + u8 variable[0]; + } __packed beacon, probe_resp; + } u; +} __packed; + + +#define IEEE80211_MGMT_HDR_LEN 24 +#define IEEE80211_DATA_HDR3_LEN 24 +#define IEEE80211_DATA_HDR4_LEN 30 + + +struct hostap_80211_rx_status { + u32 mac_time; + u8 signal; + u8 noise; + u16 rate; /* in 100 kbps */ +}; + +/* prism2_rx_80211 'type' argument */ +enum { + PRISM2_RX_MONITOR, PRISM2_RX_MGMT, PRISM2_RX_NON_ASSOC, + PRISM2_RX_NULLFUNC_ACK +}; + +int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, int type); +void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats); +void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats); + +void hostap_dump_tx_80211(const char *name, struct sk_buff *skb); +netdev_tx_t hostap_data_start_xmit(struct sk_buff *skb, + struct net_device *dev); +netdev_tx_t hostap_mgmt_start_xmit(struct sk_buff *skb, + struct net_device *dev); +netdev_tx_t hostap_master_start_xmit(struct sk_buff *skb, + struct net_device *dev); + +#endif /* HOSTAP_80211_H */ diff --git a/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c b/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c new file mode 100644 index 000000000000..599f30f22841 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c @@ -0,0 +1,1117 @@ +#include +#include +#include +#include +#include + +#include "hostap_80211.h" +#include "hostap.h" +#include "hostap_ap.h" + +/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ +/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ +static unsigned char rfc1042_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; +/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ +static unsigned char bridge_tunnel_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; +/* No encapsulation header if EtherType < 0x600 (=length) */ + +void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct ieee80211_hdr *hdr; + u16 fc; + + hdr = (struct ieee80211_hdr *) skb->data; + + printk(KERN_DEBUG "%s: RX signal=%d noise=%d rate=%d len=%d " + "jiffies=%ld\n", + name, rx_stats->signal, rx_stats->noise, rx_stats->rate, + skb->len, jiffies); + + if (skb->len < 2) + return; + + fc = le16_to_cpu(hdr->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s", + fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, + (fc & IEEE80211_FCTL_STYPE) >> 4, + fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", + fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); + + if (skb->len < IEEE80211_DATA_HDR3_LEN) { + printk("\n"); + return; + } + + printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id), + le16_to_cpu(hdr->seq_ctrl)); + + printk(KERN_DEBUG " A1=%pM", hdr->addr1); + printk(" A2=%pM", hdr->addr2); + printk(" A3=%pM", hdr->addr3); + if (skb->len >= 30) + printk(" A4=%pM", hdr->addr4); + printk("\n"); +} + + +/* Send RX frame to netif with 802.11 (and possible prism) header. + * Called from hardware or software IRQ context. */ +int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, int type) +{ + struct hostap_interface *iface; + local_info_t *local; + int hdrlen, phdrlen, head_need, tail_need; + u16 fc; + int prism_header, ret; + struct ieee80211_hdr *fhdr; + + iface = netdev_priv(dev); + local = iface->local; + + if (dev->type == ARPHRD_IEEE80211_PRISM) { + if (local->monitor_type == PRISM2_MONITOR_PRISM) { + prism_header = 1; + phdrlen = sizeof(struct linux_wlan_ng_prism_hdr); + } else { /* local->monitor_type == PRISM2_MONITOR_CAPHDR */ + prism_header = 2; + phdrlen = sizeof(struct linux_wlan_ng_cap_hdr); + } + } else if (dev->type == ARPHRD_IEEE80211_RADIOTAP) { + prism_header = 3; + phdrlen = sizeof(struct hostap_radiotap_rx); + } else { + prism_header = 0; + phdrlen = 0; + } + + fhdr = (struct ieee80211_hdr *) skb->data; + fc = le16_to_cpu(fhdr->frame_control); + + if (type == PRISM2_RX_MGMT && (fc & IEEE80211_FCTL_VERS)) { + printk(KERN_DEBUG "%s: dropped management frame with header " + "version %d\n", dev->name, fc & IEEE80211_FCTL_VERS); + dev_kfree_skb_any(skb); + return 0; + } + + hdrlen = hostap_80211_get_hdrlen(fhdr->frame_control); + + /* check if there is enough room for extra data; if not, expand skb + * buffer to be large enough for the changes */ + head_need = phdrlen; + tail_need = 0; +#ifdef PRISM2_ADD_BOGUS_CRC + tail_need += 4; +#endif /* PRISM2_ADD_BOGUS_CRC */ + + head_need -= skb_headroom(skb); + tail_need -= skb_tailroom(skb); + + if (head_need > 0 || tail_need > 0) { + if (pskb_expand_head(skb, head_need > 0 ? head_need : 0, + tail_need > 0 ? tail_need : 0, + GFP_ATOMIC)) { + printk(KERN_DEBUG "%s: prism2_rx_80211 failed to " + "reallocate skb buffer\n", dev->name); + dev_kfree_skb_any(skb); + return 0; + } + } + + /* We now have an skb with enough head and tail room, so just insert + * the extra data */ + +#ifdef PRISM2_ADD_BOGUS_CRC + memset(skb_put(skb, 4), 0xff, 4); /* Prism2 strips CRC */ +#endif /* PRISM2_ADD_BOGUS_CRC */ + + if (prism_header == 1) { + struct linux_wlan_ng_prism_hdr *hdr; + hdr = (struct linux_wlan_ng_prism_hdr *) + skb_push(skb, phdrlen); + memset(hdr, 0, phdrlen); + hdr->msgcode = LWNG_CAP_DID_BASE; + hdr->msglen = sizeof(*hdr); + memcpy(hdr->devname, dev->name, sizeof(hdr->devname)); +#define LWNG_SETVAL(f,i,s,l,d) \ +hdr->f.did = LWNG_CAP_DID_BASE | (i << 12); \ +hdr->f.status = s; hdr->f.len = l; hdr->f.data = d + LWNG_SETVAL(hosttime, 1, 0, 4, jiffies); + LWNG_SETVAL(mactime, 2, 0, 4, rx_stats->mac_time); + LWNG_SETVAL(channel, 3, 1 /* no value */, 4, 0); + LWNG_SETVAL(rssi, 4, 1 /* no value */, 4, 0); + LWNG_SETVAL(sq, 5, 1 /* no value */, 4, 0); + LWNG_SETVAL(signal, 6, 0, 4, rx_stats->signal); + LWNG_SETVAL(noise, 7, 0, 4, rx_stats->noise); + LWNG_SETVAL(rate, 8, 0, 4, rx_stats->rate / 5); + LWNG_SETVAL(istx, 9, 0, 4, 0); + LWNG_SETVAL(frmlen, 10, 0, 4, skb->len - phdrlen); +#undef LWNG_SETVAL + } else if (prism_header == 2) { + struct linux_wlan_ng_cap_hdr *hdr; + hdr = (struct linux_wlan_ng_cap_hdr *) + skb_push(skb, phdrlen); + memset(hdr, 0, phdrlen); + hdr->version = htonl(LWNG_CAPHDR_VERSION); + hdr->length = htonl(phdrlen); + hdr->mactime = __cpu_to_be64(rx_stats->mac_time); + hdr->hosttime = __cpu_to_be64(jiffies); + hdr->phytype = htonl(4); /* dss_dot11_b */ + hdr->channel = htonl(local->channel); + hdr->datarate = htonl(rx_stats->rate); + hdr->antenna = htonl(0); /* unknown */ + hdr->priority = htonl(0); /* unknown */ + hdr->ssi_type = htonl(3); /* raw */ + hdr->ssi_signal = htonl(rx_stats->signal); + hdr->ssi_noise = htonl(rx_stats->noise); + hdr->preamble = htonl(0); /* unknown */ + hdr->encoding = htonl(1); /* cck */ + } else if (prism_header == 3) { + struct hostap_radiotap_rx *hdr; + hdr = (struct hostap_radiotap_rx *)skb_push(skb, phdrlen); + memset(hdr, 0, phdrlen); + hdr->hdr.it_len = cpu_to_le16(phdrlen); + hdr->hdr.it_present = + cpu_to_le32((1 << IEEE80211_RADIOTAP_TSFT) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_RATE) | + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)); + hdr->tsft = cpu_to_le64(rx_stats->mac_time); + hdr->chan_freq = cpu_to_le16(freq_list[local->channel - 1]); + hdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_CCK | + IEEE80211_CHAN_2GHZ); + hdr->rate = rx_stats->rate / 5; + hdr->dbm_antsignal = rx_stats->signal; + hdr->dbm_antnoise = rx_stats->noise; + } + + ret = skb->len - phdrlen; + skb->dev = dev; + skb_reset_mac_header(skb); + skb_pull(skb, hdrlen); + if (prism_header) + skb_pull(skb, phdrlen); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = cpu_to_be16(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +static void monitor_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + int len; + + len = prism2_rx_80211(dev, skb, rx_stats, PRISM2_RX_MONITOR); + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; +} + + +/* Called only as a tasklet (software IRQ) */ +static struct prism2_frag_entry * +prism2_frag_cache_find(local_info_t *local, unsigned int seq, + unsigned int frag, u8 *src, u8 *dst) +{ + struct prism2_frag_entry *entry; + int i; + + for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) { + entry = &local->frag_cache[i]; + if (entry->skb != NULL && + time_after(jiffies, entry->first_frag_time + 2 * HZ)) { + printk(KERN_DEBUG "%s: expiring fragment cache entry " + "seq=%u last_frag=%u\n", + local->dev->name, entry->seq, entry->last_frag); + dev_kfree_skb(entry->skb); + entry->skb = NULL; + } + + if (entry->skb != NULL && entry->seq == seq && + (entry->last_frag + 1 == frag || frag == -1) && + memcmp(entry->src_addr, src, ETH_ALEN) == 0 && + memcmp(entry->dst_addr, dst, ETH_ALEN) == 0) + return entry; + } + + return NULL; +} + + +/* Called only as a tasklet (software IRQ) */ +static struct sk_buff * +prism2_frag_cache_get(local_info_t *local, struct ieee80211_hdr *hdr) +{ + struct sk_buff *skb = NULL; + u16 sc; + unsigned int frag, seq; + struct prism2_frag_entry *entry; + + sc = le16_to_cpu(hdr->seq_ctrl); + frag = sc & IEEE80211_SCTL_FRAG; + seq = (sc & IEEE80211_SCTL_SEQ) >> 4; + + if (frag == 0) { + /* Reserve enough space to fit maximum frame length */ + skb = dev_alloc_skb(local->dev->mtu + + sizeof(struct ieee80211_hdr) + + 8 /* LLC */ + + 2 /* alignment */ + + 8 /* WEP */ + ETH_ALEN /* WDS */); + if (skb == NULL) + return NULL; + + entry = &local->frag_cache[local->frag_next_idx]; + local->frag_next_idx++; + if (local->frag_next_idx >= PRISM2_FRAG_CACHE_LEN) + local->frag_next_idx = 0; + + if (entry->skb != NULL) + dev_kfree_skb(entry->skb); + + entry->first_frag_time = jiffies; + entry->seq = seq; + entry->last_frag = frag; + entry->skb = skb; + memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); + memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); + } else { + /* received a fragment of a frame for which the head fragment + * should have already been received */ + entry = prism2_frag_cache_find(local, seq, frag, hdr->addr2, + hdr->addr1); + if (entry != NULL) { + entry->last_frag = frag; + skb = entry->skb; + } + } + + return skb; +} + + +/* Called only as a tasklet (software IRQ) */ +static int prism2_frag_cache_invalidate(local_info_t *local, + struct ieee80211_hdr *hdr) +{ + u16 sc; + unsigned int seq; + struct prism2_frag_entry *entry; + + sc = le16_to_cpu(hdr->seq_ctrl); + seq = (sc & IEEE80211_SCTL_SEQ) >> 4; + + entry = prism2_frag_cache_find(local, seq, -1, hdr->addr2, hdr->addr1); + + if (entry == NULL) { + printk(KERN_DEBUG "%s: could not invalidate fragment cache " + "entry (seq=%u)\n", + local->dev->name, seq); + return -1; + } + + entry->skb = NULL; + return 0; +} + + +static struct hostap_bss_info *__hostap_get_bss(local_info_t *local, u8 *bssid, + u8 *ssid, size_t ssid_len) +{ + struct list_head *ptr; + struct hostap_bss_info *bss; + + list_for_each(ptr, &local->bss_list) { + bss = list_entry(ptr, struct hostap_bss_info, list); + if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && + (ssid == NULL || + (ssid_len == bss->ssid_len && + memcmp(ssid, bss->ssid, ssid_len) == 0))) { + list_move(&bss->list, &local->bss_list); + return bss; + } + } + + return NULL; +} + + +static struct hostap_bss_info *__hostap_add_bss(local_info_t *local, u8 *bssid, + u8 *ssid, size_t ssid_len) +{ + struct hostap_bss_info *bss; + + if (local->num_bss_info >= HOSTAP_MAX_BSS_COUNT) { + bss = list_entry(local->bss_list.prev, + struct hostap_bss_info, list); + list_del(&bss->list); + local->num_bss_info--; + } else { + bss = kmalloc(sizeof(*bss), GFP_ATOMIC); + if (bss == NULL) + return NULL; + } + + memset(bss, 0, sizeof(*bss)); + memcpy(bss->bssid, bssid, ETH_ALEN); + memcpy(bss->ssid, ssid, ssid_len); + bss->ssid_len = ssid_len; + local->num_bss_info++; + list_add(&bss->list, &local->bss_list); + return bss; +} + + +static void __hostap_expire_bss(local_info_t *local) +{ + struct hostap_bss_info *bss; + + while (local->num_bss_info > 0) { + bss = list_entry(local->bss_list.prev, + struct hostap_bss_info, list); + if (!time_after(jiffies, bss->last_update + 60 * HZ)) + break; + + list_del(&bss->list); + local->num_bss_info--; + kfree(bss); + } +} + + +/* Both IEEE 802.11 Beacon and Probe Response frames have similar structure, so + * the same routine can be used to parse both of them. */ +static void hostap_rx_sta_beacon(local_info_t *local, struct sk_buff *skb, + int stype) +{ + struct hostap_ieee80211_mgmt *mgmt; + int left, chan = 0; + u8 *pos; + u8 *ssid = NULL, *wpa = NULL, *rsn = NULL; + size_t ssid_len = 0, wpa_len = 0, rsn_len = 0; + struct hostap_bss_info *bss; + + if (skb->len < IEEE80211_MGMT_HDR_LEN + sizeof(mgmt->u.beacon)) + return; + + mgmt = (struct hostap_ieee80211_mgmt *) skb->data; + pos = mgmt->u.beacon.variable; + left = skb->len - (pos - skb->data); + + while (left >= 2) { + if (2 + pos[1] > left) + return; /* parse failed */ + switch (*pos) { + case WLAN_EID_SSID: + ssid = pos + 2; + ssid_len = pos[1]; + break; + case WLAN_EID_VENDOR_SPECIFIC: + if (pos[1] >= 4 && + pos[2] == 0x00 && pos[3] == 0x50 && + pos[4] == 0xf2 && pos[5] == 1) { + wpa = pos; + wpa_len = pos[1] + 2; + } + break; + case WLAN_EID_RSN: + rsn = pos; + rsn_len = pos[1] + 2; + break; + case WLAN_EID_DS_PARAMS: + if (pos[1] >= 1) + chan = pos[2]; + break; + } + left -= 2 + pos[1]; + pos += 2 + pos[1]; + } + + if (wpa_len > MAX_WPA_IE_LEN) + wpa_len = MAX_WPA_IE_LEN; + if (rsn_len > MAX_WPA_IE_LEN) + rsn_len = MAX_WPA_IE_LEN; + if (ssid_len > sizeof(bss->ssid)) + ssid_len = sizeof(bss->ssid); + + spin_lock(&local->lock); + bss = __hostap_get_bss(local, mgmt->bssid, ssid, ssid_len); + if (bss == NULL) + bss = __hostap_add_bss(local, mgmt->bssid, ssid, ssid_len); + if (bss) { + bss->last_update = jiffies; + bss->count++; + bss->capab_info = le16_to_cpu(mgmt->u.beacon.capab_info); + if (wpa) { + memcpy(bss->wpa_ie, wpa, wpa_len); + bss->wpa_ie_len = wpa_len; + } else + bss->wpa_ie_len = 0; + if (rsn) { + memcpy(bss->rsn_ie, rsn, rsn_len); + bss->rsn_ie_len = rsn_len; + } else + bss->rsn_ie_len = 0; + bss->chan = chan; + } + __hostap_expire_bss(local); + spin_unlock(&local->lock); +} + + +static int +hostap_rx_frame_mgmt(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, u16 type, + u16 stype) +{ + if (local->iw_mode == IW_MODE_MASTER) + hostap_update_sta_ps(local, (struct ieee80211_hdr *) skb->data); + + if (local->hostapd && type == IEEE80211_FTYPE_MGMT) { + if (stype == IEEE80211_STYPE_BEACON && + local->iw_mode == IW_MODE_MASTER) { + struct sk_buff *skb2; + /* Process beacon frames also in kernel driver to + * update STA(AP) table statistics */ + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) + hostap_rx(skb2->dev, skb2, rx_stats); + } + + /* send management frames to the user space daemon for + * processing */ + local->apdevstats.rx_packets++; + local->apdevstats.rx_bytes += skb->len; + if (local->apdev == NULL) + return -1; + prism2_rx_80211(local->apdev, skb, rx_stats, PRISM2_RX_MGMT); + return 0; + } + + if (local->iw_mode == IW_MODE_MASTER) { + if (type != IEEE80211_FTYPE_MGMT && + type != IEEE80211_FTYPE_CTL) { + printk(KERN_DEBUG "%s: unknown management frame " + "(type=0x%02x, stype=0x%02x) dropped\n", + skb->dev->name, type >> 2, stype >> 4); + return -1; + } + + hostap_rx(skb->dev, skb, rx_stats); + return 0; + } else if (type == IEEE80211_FTYPE_MGMT && + (stype == IEEE80211_STYPE_BEACON || + stype == IEEE80211_STYPE_PROBE_RESP)) { + hostap_rx_sta_beacon(local, skb, stype); + return -1; + } else if (type == IEEE80211_FTYPE_MGMT && + (stype == IEEE80211_STYPE_ASSOC_RESP || + stype == IEEE80211_STYPE_REASSOC_RESP)) { + /* Ignore (Re)AssocResp silently since these are not currently + * needed but are still received when WPA/RSN mode is enabled. + */ + return -1; + } else { + printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: dropped unhandled" + " management frame in non-Host AP mode (type=%d:%d)\n", + skb->dev->name, type >> 2, stype >> 4); + return -1; + } +} + + +/* Called only as a tasklet (software IRQ) */ +static struct net_device *prism2_rx_get_wds(local_info_t *local, + u8 *addr) +{ + struct hostap_interface *iface = NULL; + struct list_head *ptr; + + read_lock_bh(&local->iface_lock); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type == HOSTAP_INTERFACE_WDS && + memcmp(iface->u.wds.remote_addr, addr, ETH_ALEN) == 0) + break; + iface = NULL; + } + read_unlock_bh(&local->iface_lock); + + return iface ? iface->dev : NULL; +} + + +static int +hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr *hdr, u16 fc, + struct net_device **wds) +{ + /* FIX: is this really supposed to accept WDS frames only in Master + * mode? What about Repeater or Managed with WDS frames? */ + if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) != + (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS) && + (local->iw_mode != IW_MODE_MASTER || !(fc & IEEE80211_FCTL_TODS))) + return 0; /* not a WDS frame */ + + /* Possible WDS frame: either IEEE 802.11 compliant (if FromDS) + * or own non-standard frame with 4th address after payload */ + if (!ether_addr_equal(hdr->addr1, local->dev->dev_addr) && + (hdr->addr1[0] != 0xff || hdr->addr1[1] != 0xff || + hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff || + hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) { + /* RA (or BSSID) is not ours - drop */ + PDEBUG(DEBUG_EXTRA2, "%s: received WDS frame with " + "not own or broadcast %s=%pM\n", + local->dev->name, + fc & IEEE80211_FCTL_FROMDS ? "RA" : "BSSID", + hdr->addr1); + return -1; + } + + /* check if the frame came from a registered WDS connection */ + *wds = prism2_rx_get_wds(local, hdr->addr2); + if (*wds == NULL && fc & IEEE80211_FCTL_FROMDS && + (local->iw_mode != IW_MODE_INFRA || + !(local->wds_type & HOSTAP_WDS_AP_CLIENT) || + memcmp(hdr->addr2, local->bssid, ETH_ALEN) != 0)) { + /* require that WDS link has been registered with TA or the + * frame is from current AP when using 'AP client mode' */ + PDEBUG(DEBUG_EXTRA, "%s: received WDS[4 addr] frame " + "from unknown TA=%pM\n", + local->dev->name, hdr->addr2); + if (local->ap && local->ap->autom_ap_wds) + hostap_wds_link_oper(local, hdr->addr2, WDS_ADD); + return -1; + } + + if (*wds && !(fc & IEEE80211_FCTL_FROMDS) && local->ap && + hostap_is_sta_assoc(local->ap, hdr->addr2)) { + /* STA is actually associated with us even though it has a + * registered WDS link. Assume it is in 'AP client' mode. + * Since this is a 3-addr frame, assume it is not (bogus) WDS + * frame and process it like any normal ToDS frame from + * associated STA. */ + *wds = NULL; + } + + return 0; +} + + +static int hostap_is_eapol_frame(local_info_t *local, struct sk_buff *skb) +{ + struct net_device *dev = local->dev; + u16 fc, ethertype; + struct ieee80211_hdr *hdr; + u8 *pos; + + if (skb->len < 24) + return 0; + + hdr = (struct ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + /* check that the frame is unicast frame to us */ + if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_TODS && + ether_addr_equal(hdr->addr1, dev->dev_addr) && + ether_addr_equal(hdr->addr3, dev->dev_addr)) { + /* ToDS frame with own addr BSSID and DA */ + } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_FROMDS && + ether_addr_equal(hdr->addr1, dev->dev_addr)) { + /* FromDS frame with own addr as DA */ + } else + return 0; + + if (skb->len < 24 + 8) + return 0; + + /* check for port access entity Ethernet type */ + pos = skb->data + 24; + ethertype = (pos[6] << 8) | pos[7]; + if (ethertype == ETH_P_PAE) + return 1; + + return 0; +} + + +/* Called only as a tasklet (software IRQ) */ +static int +hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb, + struct lib80211_crypt_data *crypt) +{ + struct ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) + return 0; + + hdr = (struct ieee80211_hdr *) skb->data; + hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); + + if (local->tkip_countermeasures && + strcmp(crypt->ops->name, "TKIP") == 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "received packet from %pM\n", + local->dev->name, hdr->addr2); + } + return -1; + } + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_DEBUG "%s: decryption failed (SA=%pM) res=%d\n", + local->dev->name, hdr->addr2, res); + local->comm_tallies.rx_discards_wep_undecryptable++; + return -1; + } + + return res; +} + + +/* Called only as a tasklet (software IRQ) */ +static int +hostap_rx_frame_decrypt_msdu(local_info_t *local, struct sk_buff *skb, + int keyidx, struct lib80211_crypt_data *crypt) +{ + struct ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) + return 0; + + hdr = (struct ieee80211_hdr *) skb->data; + hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" + " (SA=%pM keyidx=%d)\n", + local->dev->name, hdr->addr2, keyidx); + return -1; + } + + return 0; +} + + +/* All received frames are sent to this function. @skb contains the frame in + * IEEE 802.11 format, i.e., in the format it was sent over air. + * This function is called only as a tasklet (software IRQ). */ +void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct hostap_interface *iface; + local_info_t *local; + struct ieee80211_hdr *hdr; + size_t hdrlen; + u16 fc, type, stype, sc; + struct net_device *wds = NULL; + unsigned int frag; + u8 *payload; + struct sk_buff *skb2 = NULL; + u16 ethertype; + int frame_authorized = 0; + int from_assoc_ap = 0; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + struct lib80211_crypt_data *crypt = NULL; + void *sta = NULL; + int keyidx = 0; + + iface = netdev_priv(dev); + local = iface->local; + iface->stats.rx_packets++; + iface->stats.rx_bytes += skb->len; + + /* dev is the master radio device; change this to be the default + * virtual interface (this may be changed to WDS device below) */ + dev = local->ddev; + iface = netdev_priv(dev); + + hdr = (struct ieee80211_hdr *) skb->data; + + if (skb->len < 10) + goto rx_dropped; + + fc = le16_to_cpu(hdr->frame_control); + type = fc & IEEE80211_FCTL_FTYPE; + stype = fc & IEEE80211_FCTL_STYPE; + sc = le16_to_cpu(hdr->seq_ctrl); + frag = sc & IEEE80211_SCTL_FRAG; + hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); + + /* Put this code here so that we avoid duplicating it in all + * Rx paths. - Jean II */ +#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ + /* If spy monitoring on */ + if (iface->spy_data.spy_number > 0) { + struct iw_quality wstats; + wstats.level = rx_stats->signal; + wstats.noise = rx_stats->noise; + wstats.updated = IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_UPDATED + | IW_QUAL_QUAL_INVALID | IW_QUAL_DBM; + /* Update spy records */ + wireless_spy_update(dev, hdr->addr2, &wstats); + } +#endif /* IW_WIRELESS_SPY */ + hostap_update_rx_stats(local->ap, hdr, rx_stats); + + if (local->iw_mode == IW_MODE_MONITOR) { + monitor_rx(dev, skb, rx_stats); + return; + } + + if (local->host_decrypt) { + int idx = 0; + if (skb->len >= hdrlen + 3) + idx = skb->data[hdrlen + 3] >> 6; + crypt = local->crypt_info.crypt[idx]; + sta = NULL; + + /* Use station specific key to override default keys if the + * receiver address is a unicast address ("individual RA"). If + * bcrx_sta_key parameter is set, station specific key is used + * even with broad/multicast targets (this is against IEEE + * 802.11, but makes it easier to use different keys with + * stations that do not support WEP key mapping). */ + + if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key) + (void) hostap_handle_sta_crypto(local, hdr, &crypt, + &sta); + + /* allow NULL decrypt to indicate an station specific override + * for default encryption */ + if (crypt && (crypt->ops == NULL || + crypt->ops->decrypt_mpdu == NULL)) + crypt = NULL; + + if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) { +#if 0 + /* This seems to be triggered by some (multicast?) + * frames from other than current BSS, so just drop the + * frames silently instead of filling system log with + * these reports. */ + printk(KERN_DEBUG "%s: WEP decryption failed (not set)" + " (SA=%pM)\n", + local->dev->name, hdr->addr2); +#endif + local->comm_tallies.rx_discards_wep_undecryptable++; + goto rx_dropped; + } + } + + if (type != IEEE80211_FTYPE_DATA) { + if (type == IEEE80211_FTYPE_MGMT && + stype == IEEE80211_STYPE_AUTH && + fc & IEEE80211_FCTL_PROTECTED && local->host_decrypt && + (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0) + { + printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " + "from %pM\n", dev->name, hdr->addr2); + /* TODO: could inform hostapd about this so that it + * could send auth failure report */ + goto rx_dropped; + } + + if (hostap_rx_frame_mgmt(local, skb, rx_stats, type, stype)) + goto rx_dropped; + else + goto rx_exit; + } + + /* Data frame - extract src/dst addresses */ + if (skb->len < IEEE80211_DATA_HDR3_LEN) + goto rx_dropped; + + switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { + case IEEE80211_FCTL_FROMDS: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr3, ETH_ALEN); + break; + case IEEE80211_FCTL_TODS: + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + break; + case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: + if (skb->len < IEEE80211_DATA_HDR4_LEN) + goto rx_dropped; + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr4, ETH_ALEN); + break; + case 0: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + break; + } + + if (hostap_rx_frame_wds(local, hdr, fc, &wds)) + goto rx_dropped; + if (wds) + skb->dev = dev = wds; + + if (local->iw_mode == IW_MODE_MASTER && !wds && + (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_FROMDS && + local->stadev && + memcmp(hdr->addr2, local->assoc_ap_addr, ETH_ALEN) == 0) { + /* Frame from BSSID of the AP for which we are a client */ + skb->dev = dev = local->stadev; + from_assoc_ap = 1; + } + + if ((local->iw_mode == IW_MODE_MASTER || + local->iw_mode == IW_MODE_REPEAT) && + !from_assoc_ap) { + switch (hostap_handle_sta_rx(local, dev, skb, rx_stats, + wds != NULL)) { + case AP_RX_CONTINUE_NOT_AUTHORIZED: + frame_authorized = 0; + break; + case AP_RX_CONTINUE: + frame_authorized = 1; + break; + case AP_RX_DROP: + goto rx_dropped; + case AP_RX_EXIT: + goto rx_exit; + } + } + + /* Nullfunc frames may have PS-bit set, so they must be passed to + * hostap_handle_sta_rx() before being dropped here. */ + if (stype != IEEE80211_STYPE_DATA && + stype != IEEE80211_STYPE_DATA_CFACK && + stype != IEEE80211_STYPE_DATA_CFPOLL && + stype != IEEE80211_STYPE_DATA_CFACKPOLL) { + if (stype != IEEE80211_STYPE_NULLFUNC) + printk(KERN_DEBUG "%s: RX: dropped data frame " + "with no data (type=0x%02x, subtype=0x%02x)\n", + dev->name, type >> 2, stype >> 4); + goto rx_dropped; + } + + /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ + + if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && + (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0) + goto rx_dropped; + hdr = (struct ieee80211_hdr *) skb->data; + + /* skb: hdr + (possibly fragmented) plaintext payload */ + + if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && + (frag != 0 || (fc & IEEE80211_FCTL_MOREFRAGS))) { + int flen; + struct sk_buff *frag_skb = + prism2_frag_cache_get(local, hdr); + if (!frag_skb) { + printk(KERN_DEBUG "%s: Rx cannot get skb from " + "fragment cache (morefrag=%d seq=%u frag=%u)\n", + dev->name, (fc & IEEE80211_FCTL_MOREFRAGS) != 0, + (sc & IEEE80211_SCTL_SEQ) >> 4, frag); + goto rx_dropped; + } + + flen = skb->len; + if (frag != 0) + flen -= hdrlen; + + if (frag_skb->tail + flen > frag_skb->end) { + printk(KERN_WARNING "%s: host decrypted and " + "reassembled frame did not fit skb\n", + dev->name); + prism2_frag_cache_invalidate(local, hdr); + goto rx_dropped; + } + + if (frag == 0) { + /* copy first fragment (including full headers) into + * beginning of the fragment cache skb */ + skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), + flen); + } else { + /* append frame payload to the end of the fragment + * cache skb */ + skb_copy_from_linear_data_offset(skb, hdrlen, + skb_put(frag_skb, + flen), flen); + } + dev_kfree_skb(skb); + skb = NULL; + + if (fc & IEEE80211_FCTL_MOREFRAGS) { + /* more fragments expected - leave the skb in fragment + * cache for now; it will be delivered to upper layers + * after all fragments have been received */ + goto rx_exit; + } + + /* this was the last fragment and the frame will be + * delivered, so remove skb from fragment cache */ + skb = frag_skb; + hdr = (struct ieee80211_hdr *) skb->data; + prism2_frag_cache_invalidate(local, hdr); + } + + /* skb: hdr + (possible reassembled) full MSDU payload; possibly still + * encrypted/authenticated */ + + if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && + hostap_rx_frame_decrypt_msdu(local, skb, keyidx, crypt)) + goto rx_dropped; + + hdr = (struct ieee80211_hdr *) skb->data; + if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !local->open_wep) { + if (local->ieee_802_1x && + hostap_is_eapol_frame(local, skb)) { + /* pass unencrypted EAPOL frames even if encryption is + * configured */ + PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X - passing " + "unencrypted EAPOL frame\n", local->dev->name); + } else { + printk(KERN_DEBUG "%s: encryption configured, but RX " + "frame not encrypted (SA=%pM)\n", + local->dev->name, hdr->addr2); + goto rx_dropped; + } + } + + if (local->drop_unencrypted && !(fc & IEEE80211_FCTL_PROTECTED) && + !hostap_is_eapol_frame(local, skb)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: dropped unencrypted RX data " + "frame from %pM (drop_unencrypted=1)\n", + dev->name, hdr->addr2); + } + goto rx_dropped; + } + + /* skb: hdr + (possible reassembled) full plaintext payload */ + + payload = skb->data + hdrlen; + ethertype = (payload[6] << 8) | payload[7]; + + /* If IEEE 802.1X is used, check whether the port is authorized to send + * the received frame. */ + if (local->ieee_802_1x && local->iw_mode == IW_MODE_MASTER) { + if (ethertype == ETH_P_PAE) { + PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X frame\n", + dev->name); + if (local->hostapd && local->apdev) { + /* Send IEEE 802.1X frames to the user + * space daemon for processing */ + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_MGMT); + local->apdevstats.rx_packets++; + local->apdevstats.rx_bytes += skb->len; + goto rx_exit; + } + } else if (!frame_authorized) { + printk(KERN_DEBUG "%s: dropped frame from " + "unauthorized port (IEEE 802.1X): " + "ethertype=0x%04x\n", + dev->name, ethertype); + goto rx_dropped; + } + } + + /* convert hdr + possible LLC headers into Ethernet header */ + if (skb->len - hdrlen >= 8 && + ((memcmp(payload, rfc1042_header, 6) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + memcmp(payload, bridge_tunnel_header, 6) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation and + * replace EtherType */ + skb_pull(skb, hdrlen + 6); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } else { + __be16 len; + /* Leave Ethernet header part of hdr and full payload */ + skb_pull(skb, hdrlen); + len = htons(skb->len); + memcpy(skb_push(skb, 2), &len, 2); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } + + if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_TODS) && + skb->len >= ETH_HLEN + ETH_ALEN) { + /* Non-standard frame: get addr4 from its bogus location after + * the payload */ + skb_copy_from_linear_data_offset(skb, skb->len - ETH_ALEN, + skb->data + ETH_ALEN, + ETH_ALEN); + skb_trim(skb, skb->len - ETH_ALEN); + } + + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + if (local->iw_mode == IW_MODE_MASTER && !wds && + local->ap->bridge_packets) { + if (dst[0] & 0x01) { + /* copy multicast frame both to the higher layers and + * to the wireless media */ + local->ap->bridged_multicast++; + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2 == NULL) + printk(KERN_DEBUG "%s: skb_clone failed for " + "multicast frame\n", dev->name); + } else if (hostap_is_sta_authorized(local->ap, dst)) { + /* send frame directly to the associated STA using + * wireless media and not passing to higher layers */ + local->ap->bridged_unicast++; + skb2 = skb; + skb = NULL; + } + } + + if (skb2 != NULL) { + /* send to wireless media */ + skb2->dev = dev; + skb2->protocol = cpu_to_be16(ETH_P_802_3); + skb_reset_mac_header(skb2); + skb_reset_network_header(skb2); + /* skb2->network_header += ETH_HLEN; */ + dev_queue_xmit(skb2); + } + + if (skb) { + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); + } + + rx_exit: + if (sta) + hostap_handle_sta_release(sta); + return; + + rx_dropped: + dev_kfree_skb(skb); + + dev->stats.rx_dropped++; + goto rx_exit; +} + + +EXPORT_SYMBOL(hostap_80211_rx); diff --git a/drivers/net/wireless/intersil/hostap/hostap_80211_tx.c b/drivers/net/wireless/intersil/hostap/hostap_80211_tx.c new file mode 100644 index 000000000000..055e11d353ca --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_80211_tx.c @@ -0,0 +1,553 @@ +#include +#include +#include + +#include "hostap_80211.h" +#include "hostap_common.h" +#include "hostap_wlan.h" +#include "hostap.h" +#include "hostap_ap.h" + +/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ +/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ +static unsigned char rfc1042_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; +/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ +static unsigned char bridge_tunnel_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; +/* No encapsulation header if EtherType < 0x600 (=length) */ + +void hostap_dump_tx_80211(const char *name, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + u16 fc; + + hdr = (struct ieee80211_hdr *) skb->data; + + printk(KERN_DEBUG "%s: TX len=%d jiffies=%ld\n", + name, skb->len, jiffies); + + if (skb->len < 2) + return; + + fc = le16_to_cpu(hdr->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s", + fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, + (fc & IEEE80211_FCTL_STYPE) >> 4, + fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", + fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); + + if (skb->len < IEEE80211_DATA_HDR3_LEN) { + printk("\n"); + return; + } + + printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id), + le16_to_cpu(hdr->seq_ctrl)); + + printk(KERN_DEBUG " A1=%pM", hdr->addr1); + printk(" A2=%pM", hdr->addr2); + printk(" A3=%pM", hdr->addr3); + if (skb->len >= 30) + printk(" A4=%pM", hdr->addr4); + printk("\n"); +} + + +/* hard_start_xmit function for data interfaces (wlan#, wlan#wds#, wlan#sta) + * Convert Ethernet header into a suitable IEEE 802.11 header depending on + * device configuration. */ +netdev_tx_t hostap_data_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int need_headroom, need_tailroom = 0; + struct ieee80211_hdr hdr; + u16 fc, ethertype = 0; + enum { + WDS_NO = 0, WDS_OWN_FRAME, WDS_COMPLIANT_FRAME + } use_wds = WDS_NO; + u8 *encaps_data; + int hdr_len, encaps_len, skip_header_bytes; + int to_assoc_ap = 0; + struct hostap_skb_tx_data *meta; + + iface = netdev_priv(dev); + local = iface->local; + + if (skb->len < ETH_HLEN) { + printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb " + "(len=%d)\n", dev->name, skb->len); + kfree_skb(skb); + return NETDEV_TX_OK; + } + + if (local->ddev != dev) { + use_wds = (local->iw_mode == IW_MODE_MASTER && + !(local->wds_type & HOSTAP_WDS_STANDARD_FRAME)) ? + WDS_OWN_FRAME : WDS_COMPLIANT_FRAME; + if (dev == local->stadev) { + to_assoc_ap = 1; + use_wds = WDS_NO; + } else if (dev == local->apdev) { + printk(KERN_DEBUG "%s: prism2_tx: trying to use " + "AP device with Ethernet net dev\n", dev->name); + kfree_skb(skb); + return NETDEV_TX_OK; + } + } else { + if (local->iw_mode == IW_MODE_REPEAT) { + printk(KERN_DEBUG "%s: prism2_tx: trying to use " + "non-WDS link in Repeater mode\n", dev->name); + kfree_skb(skb); + return NETDEV_TX_OK; + } else if (local->iw_mode == IW_MODE_INFRA && + (local->wds_type & HOSTAP_WDS_AP_CLIENT) && + !ether_addr_equal(skb->data + ETH_ALEN, dev->dev_addr)) { + /* AP client mode: send frames with foreign src addr + * using 4-addr WDS frames */ + use_wds = WDS_COMPLIANT_FRAME; + } + } + + /* Incoming skb->data: dst_addr[6], src_addr[6], proto[2], payload + * ==> + * Prism2 TX frame with 802.11 header: + * txdesc (address order depending on used mode; includes dst_addr and + * src_addr), possible encapsulation (RFC1042/Bridge-Tunnel; + * proto[2], payload {, possible addr4[6]} */ + + ethertype = (skb->data[12] << 8) | skb->data[13]; + + memset(&hdr, 0, sizeof(hdr)); + + /* Length of data after IEEE 802.11 header */ + encaps_data = NULL; + encaps_len = 0; + skip_header_bytes = ETH_HLEN; + if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { + encaps_data = bridge_tunnel_header; + encaps_len = sizeof(bridge_tunnel_header); + skip_header_bytes -= 2; + } else if (ethertype >= 0x600) { + encaps_data = rfc1042_header; + encaps_len = sizeof(rfc1042_header); + skip_header_bytes -= 2; + } + + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; + hdr_len = IEEE80211_DATA_HDR3_LEN; + + if (use_wds != WDS_NO) { + /* Note! Prism2 station firmware has problems with sending real + * 802.11 frames with four addresses; until these problems can + * be fixed or worked around, 4-addr frames needed for WDS are + * using incompatible format: FromDS flag is not set and the + * fourth address is added after the frame payload; it is + * assumed, that the receiving station knows how to handle this + * frame format */ + + if (use_wds == WDS_COMPLIANT_FRAME) { + fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS; + /* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA, + * Addr4 = SA */ + skb_copy_from_linear_data_offset(skb, ETH_ALEN, + &hdr.addr4, ETH_ALEN); + hdr_len += ETH_ALEN; + } else { + /* bogus 4-addr format to workaround Prism2 station + * f/w bug */ + fc |= IEEE80211_FCTL_TODS; + /* From DS: Addr1 = DA (used as RA), + * Addr2 = BSSID (used as TA), Addr3 = SA (used as DA), + */ + + /* SA from skb->data + ETH_ALEN will be added after + * frame payload; use hdr.addr4 as a temporary buffer + */ + skb_copy_from_linear_data_offset(skb, ETH_ALEN, + &hdr.addr4, ETH_ALEN); + need_tailroom += ETH_ALEN; + } + + /* send broadcast and multicast frames to broadcast RA, if + * configured; otherwise, use unicast RA of the WDS link */ + if ((local->wds_type & HOSTAP_WDS_BROADCAST_RA) && + is_multicast_ether_addr(skb->data)) + eth_broadcast_addr(hdr.addr1); + else if (iface->type == HOSTAP_INTERFACE_WDS) + memcpy(&hdr.addr1, iface->u.wds.remote_addr, + ETH_ALEN); + else + memcpy(&hdr.addr1, local->bssid, ETH_ALEN); + memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); + skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN); + } else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) { + fc |= IEEE80211_FCTL_FROMDS; + /* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */ + skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN); + memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr3, + ETH_ALEN); + } else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) { + fc |= IEEE80211_FCTL_TODS; + /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ + memcpy(&hdr.addr1, to_assoc_ap ? + local->assoc_ap_addr : local->bssid, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2, + ETH_ALEN); + skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN); + } else if (local->iw_mode == IW_MODE_ADHOC) { + /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ + skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2, + ETH_ALEN); + memcpy(&hdr.addr3, local->bssid, ETH_ALEN); + } + + hdr.frame_control = cpu_to_le16(fc); + + skb_pull(skb, skip_header_bytes); + need_headroom = local->func->need_tx_headroom + hdr_len + encaps_len; + if (skb_tailroom(skb) < need_tailroom) { + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) { + iface->stats.tx_dropped++; + return NETDEV_TX_OK; + } + if (pskb_expand_head(skb, need_headroom, need_tailroom, + GFP_ATOMIC)) { + kfree_skb(skb); + iface->stats.tx_dropped++; + return NETDEV_TX_OK; + } + } else if (skb_headroom(skb) < need_headroom) { + struct sk_buff *tmp = skb; + skb = skb_realloc_headroom(skb, need_headroom); + kfree_skb(tmp); + if (skb == NULL) { + iface->stats.tx_dropped++; + return NETDEV_TX_OK; + } + } else { + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) { + iface->stats.tx_dropped++; + return NETDEV_TX_OK; + } + } + + if (encaps_data) + memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); + memcpy(skb_push(skb, hdr_len), &hdr, hdr_len); + if (use_wds == WDS_OWN_FRAME) { + memcpy(skb_put(skb, ETH_ALEN), &hdr.addr4, ETH_ALEN); + } + + iface->stats.tx_packets++; + iface->stats.tx_bytes += skb->len; + + skb_reset_mac_header(skb); + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + if (use_wds) + meta->flags |= HOSTAP_TX_FLAGS_WDS; + meta->ethertype = ethertype; + meta->iface = iface; + + /* Send IEEE 802.11 encapsulated frame using the master radio device */ + skb->dev = local->dev; + dev_queue_xmit(skb); + return NETDEV_TX_OK; +} + + +/* hard_start_xmit function for hostapd wlan#ap interfaces */ +netdev_tx_t hostap_mgmt_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hostap_skb_tx_data *meta; + struct ieee80211_hdr *hdr; + u16 fc; + + iface = netdev_priv(dev); + local = iface->local; + + if (skb->len < 10) { + printk(KERN_DEBUG "%s: hostap_mgmt_start_xmit: short skb " + "(len=%d)\n", dev->name, skb->len); + kfree_skb(skb); + return NETDEV_TX_OK; + } + + iface->stats.tx_packets++; + iface->stats.tx_bytes += skb->len; + + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + meta->iface = iface; + + if (skb->len >= IEEE80211_DATA_HDR3_LEN + sizeof(rfc1042_header) + 2) { + hdr = (struct ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + if (ieee80211_is_data(hdr->frame_control) && + (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DATA) { + u8 *pos = &skb->data[IEEE80211_DATA_HDR3_LEN + + sizeof(rfc1042_header)]; + meta->ethertype = (pos[0] << 8) | pos[1]; + } + } + + /* Send IEEE 802.11 encapsulated frame using the master radio device */ + skb->dev = local->dev; + dev_queue_xmit(skb); + return NETDEV_TX_OK; +} + + +/* Called only from software IRQ */ +static struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, + struct lib80211_crypt_data *crypt) +{ + struct hostap_interface *iface; + local_info_t *local; + struct ieee80211_hdr *hdr; + int prefix_len, postfix_len, hdr_len, res; + + iface = netdev_priv(skb->dev); + local = iface->local; + + if (skb->len < IEEE80211_DATA_HDR3_LEN) { + kfree_skb(skb); + return NULL; + } + + if (local->tkip_countermeasures && + strcmp(crypt->ops->name, "TKIP") == 0) { + hdr = (struct ieee80211_hdr *) skb->data; + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "TX packet to %pM\n", + local->dev->name, hdr->addr1); + } + kfree_skb(skb); + return NULL; + } + + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) + return NULL; + + prefix_len = crypt->ops->extra_mpdu_prefix_len + + crypt->ops->extra_msdu_prefix_len; + postfix_len = crypt->ops->extra_mpdu_postfix_len + + crypt->ops->extra_msdu_postfix_len; + if ((skb_headroom(skb) < prefix_len || + skb_tailroom(skb) < postfix_len) && + pskb_expand_head(skb, prefix_len, postfix_len, GFP_ATOMIC)) { + kfree_skb(skb); + return NULL; + } + + hdr = (struct ieee80211_hdr *) skb->data; + hdr_len = hostap_80211_get_hdrlen(hdr->frame_control); + + /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so + * call both MSDU and MPDU encryption functions from here. */ + atomic_inc(&crypt->refcnt); + res = 0; + if (crypt->ops->encrypt_msdu) + res = crypt->ops->encrypt_msdu(skb, hdr_len, crypt->priv); + if (res == 0 && crypt->ops->encrypt_mpdu) + res = crypt->ops->encrypt_mpdu(skb, hdr_len, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + kfree_skb(skb); + return NULL; + } + + return skb; +} + + +/* hard_start_xmit function for master radio interface wifi#. + * AP processing (TX rate control, power save buffering, etc.). + * Use hardware TX function to send the frame. */ +netdev_tx_t hostap_master_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + netdev_tx_t ret = NETDEV_TX_BUSY; + u16 fc; + struct hostap_tx_data tx; + ap_tx_ret tx_ret; + struct hostap_skb_tx_data *meta; + int no_encrypt = 0; + struct ieee80211_hdr *hdr; + + iface = netdev_priv(dev); + local = iface->local; + + tx.skb = skb; + tx.sta_ptr = NULL; + + meta = (struct hostap_skb_tx_data *) skb->cb; + if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) { + printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, " + "expected 0x%08x)\n", + dev->name, meta->magic, HOSTAP_SKB_TX_DATA_MAGIC); + ret = NETDEV_TX_OK; + iface->stats.tx_dropped++; + goto fail; + } + + if (local->host_encrypt) { + /* Set crypt to default algorithm and key; will be replaced in + * AP code if STA has own alg/key */ + tx.crypt = local->crypt_info.crypt[local->crypt_info.tx_keyidx]; + tx.host_encrypt = 1; + } else { + tx.crypt = NULL; + tx.host_encrypt = 0; + } + + if (skb->len < 24) { + printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb " + "(len=%d)\n", dev->name, skb->len); + ret = NETDEV_TX_OK; + iface->stats.tx_dropped++; + goto fail; + } + + /* FIX (?): + * Wi-Fi 802.11b test plan suggests that AP should ignore power save + * bit in authentication and (re)association frames and assume tha + * STA remains awake for the response. */ + tx_ret = hostap_handle_sta_tx(local, &tx); + skb = tx.skb; + meta = (struct hostap_skb_tx_data *) skb->cb; + hdr = (struct ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + switch (tx_ret) { + case AP_TX_CONTINUE: + break; + case AP_TX_CONTINUE_NOT_AUTHORIZED: + if (local->ieee_802_1x && + ieee80211_is_data(hdr->frame_control) && + meta->ethertype != ETH_P_PAE && + !(meta->flags & HOSTAP_TX_FLAGS_WDS)) { + printk(KERN_DEBUG "%s: dropped frame to unauthorized " + "port (IEEE 802.1X): ethertype=0x%04x\n", + dev->name, meta->ethertype); + hostap_dump_tx_80211(dev->name, skb); + + ret = NETDEV_TX_OK; /* drop packet */ + iface->stats.tx_dropped++; + goto fail; + } + break; + case AP_TX_DROP: + ret = NETDEV_TX_OK; /* drop packet */ + iface->stats.tx_dropped++; + goto fail; + case AP_TX_RETRY: + goto fail; + case AP_TX_BUFFERED: + /* do not free skb here, it will be freed when the + * buffered frame is sent/timed out */ + ret = NETDEV_TX_OK; + goto tx_exit; + } + + /* Request TX callback if protocol version is 2 in 802.11 header; + * this version 2 is a special case used between hostapd and kernel + * driver */ + if (((fc & IEEE80211_FCTL_VERS) == BIT(1)) && + local->ap && local->ap->tx_callback_idx && meta->tx_cb_idx == 0) { + meta->tx_cb_idx = local->ap->tx_callback_idx; + + /* remove special version from the frame header */ + fc &= ~IEEE80211_FCTL_VERS; + hdr->frame_control = cpu_to_le16(fc); + } + + if (!ieee80211_is_data(hdr->frame_control)) { + no_encrypt = 1; + tx.crypt = NULL; + } + + if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt && + !(fc & IEEE80211_FCTL_PROTECTED)) { + no_encrypt = 1; + PDEBUG(DEBUG_EXTRA2, "%s: TX: IEEE 802.1X - passing " + "unencrypted EAPOL frame\n", dev->name); + tx.crypt = NULL; /* no encryption for IEEE 802.1X frames */ + } + + if (tx.crypt && (!tx.crypt->ops || !tx.crypt->ops->encrypt_mpdu)) + tx.crypt = NULL; + else if ((tx.crypt || + local->crypt_info.crypt[local->crypt_info.tx_keyidx]) && + !no_encrypt) { + /* Add ISWEP flag both for firmware and host based encryption + */ + fc |= IEEE80211_FCTL_PROTECTED; + hdr->frame_control = cpu_to_le16(fc); + } else if (local->drop_unencrypted && + ieee80211_is_data(hdr->frame_control) && + meta->ethertype != ETH_P_PAE) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: dropped unencrypted TX data " + "frame (drop_unencrypted=1)\n", dev->name); + } + iface->stats.tx_dropped++; + ret = NETDEV_TX_OK; + goto fail; + } + + if (tx.crypt) { + skb = hostap_tx_encrypt(skb, tx.crypt); + if (skb == NULL) { + printk(KERN_DEBUG "%s: TX - encryption failed\n", + dev->name); + ret = NETDEV_TX_OK; + goto fail; + } + meta = (struct hostap_skb_tx_data *) skb->cb; + if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) { + printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, " + "expected 0x%08x) after hostap_tx_encrypt\n", + dev->name, meta->magic, + HOSTAP_SKB_TX_DATA_MAGIC); + ret = NETDEV_TX_OK; + iface->stats.tx_dropped++; + goto fail; + } + } + + if (local->func->tx == NULL || local->func->tx(skb, dev)) { + ret = NETDEV_TX_OK; + iface->stats.tx_dropped++; + } else { + ret = NETDEV_TX_OK; + iface->stats.tx_packets++; + iface->stats.tx_bytes += skb->len; + } + + fail: + if (ret == NETDEV_TX_OK && skb) + dev_kfree_skb(skb); + tx_exit: + if (tx.sta_ptr) + hostap_handle_sta_release(tx.sta_ptr); + return ret; +} + + +EXPORT_SYMBOL(hostap_master_start_xmit); diff --git a/drivers/net/wireless/intersil/hostap/hostap_ap.c b/drivers/net/wireless/intersil/hostap/hostap_ap.c new file mode 100644 index 000000000000..c995ace153ee --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_ap.c @@ -0,0 +1,3339 @@ +/* + * Intersil Prism2 driver with Host AP (software access point) support + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2005, Jouni Malinen + * + * This file is to be included into hostap.c when S/W AP functionality is + * compiled. + * + * AP: FIX: + * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from + * unauthenticated STA, send deauth. frame (8802.11: 5.5) + * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received + * from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5) + * - if unicast Class 3 received from unauthenticated STA, send deauth. frame + * (8802.11: 5.5) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_wlan.h" +#include "hostap.h" +#include "hostap_ap.h" + +static int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL, + DEF_INTS }; +module_param_array(other_ap_policy, int, NULL, 0444); +MODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)"); + +static int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC, + DEF_INTS }; +module_param_array(ap_max_inactivity, int, NULL, 0444); +MODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station " + "inactivity"); + +static int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS }; +module_param_array(ap_bridge_packets, int, NULL, 0444); +MODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between " + "stations"); + +static int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS }; +module_param_array(autom_ap_wds, int, NULL, 0444); +MODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs " + "automatically"); + + +static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta); +static void hostap_event_expired_sta(struct net_device *dev, + struct sta_info *sta); +static void handle_add_proc_queue(struct work_struct *work); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +static void handle_wds_oper_queue(struct work_struct *work); +static void prism2_send_mgmt(struct net_device *dev, + u16 type_subtype, char *body, + int body_len, u8 *addr, u16 tx_cb_idx); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +#ifndef PRISM2_NO_PROCFS_DEBUG +static int ap_debug_proc_show(struct seq_file *m, void *v) +{ + struct ap_data *ap = m->private; + + seq_printf(m, "BridgedUnicastFrames=%u\n", ap->bridged_unicast); + seq_printf(m, "BridgedMulticastFrames=%u\n", ap->bridged_multicast); + seq_printf(m, "max_inactivity=%u\n", ap->max_inactivity / HZ); + seq_printf(m, "bridge_packets=%u\n", ap->bridge_packets); + seq_printf(m, "nullfunc_ack=%u\n", ap->nullfunc_ack); + seq_printf(m, "autom_ap_wds=%u\n", ap->autom_ap_wds); + seq_printf(m, "auth_algs=%u\n", ap->local->auth_algs); + seq_printf(m, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc); + return 0; +} + +static int ap_debug_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, ap_debug_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations ap_debug_proc_fops = { + .open = ap_debug_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + +static void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta) +{ + sta->hnext = ap->sta_hash[STA_HASH(sta->addr)]; + ap->sta_hash[STA_HASH(sta->addr)] = sta; +} + +static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta) +{ + struct sta_info *s; + + s = ap->sta_hash[STA_HASH(sta->addr)]; + if (s == NULL) return; + if (ether_addr_equal(s->addr, sta->addr)) { + ap->sta_hash[STA_HASH(sta->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && !ether_addr_equal(s->hnext->addr, sta->addr)) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + printk("AP: could not remove STA %pM from hash table\n", + sta->addr); +} + +static void ap_free_sta(struct ap_data *ap, struct sta_info *sta) +{ + if (sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + + if (ap->proc != NULL) { + char name[20]; + sprintf(name, "%pM", sta->addr); + remove_proc_entry(name, ap->proc); + } + + if (sta->crypt) { + sta->crypt->ops->deinit(sta->crypt->priv); + kfree(sta->crypt); + sta->crypt = NULL; + } + + skb_queue_purge(&sta->tx_buf); + + ap->num_sta--; +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (sta->aid > 0) + ap->sta_aid[sta->aid - 1] = NULL; + + if (!sta->ap) + kfree(sta->u.sta.challenge); + del_timer_sync(&sta->timer); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + kfree(sta); +} + + +static void hostap_set_tim(local_info_t *local, int aid, int set) +{ + if (local->func->set_tim) + local->func->set_tim(local->dev, aid, set); +} + + +static void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta) +{ + union iwreq_data wrqu; + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL); +} + + +static void hostap_event_expired_sta(struct net_device *dev, + struct sta_info *sta) +{ + union iwreq_data wrqu; + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL); +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +static void ap_handle_timer(unsigned long data) +{ + struct sta_info *sta = (struct sta_info *) data; + local_info_t *local; + struct ap_data *ap; + unsigned long next_time = 0; + int was_assoc; + + if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) { + PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n"); + return; + } + + local = sta->local; + ap = local->ap; + was_assoc = sta->flags & WLAN_STA_ASSOC; + + if (atomic_read(&sta->users) != 0) + next_time = jiffies + HZ; + else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH)) + next_time = jiffies + ap->max_inactivity; + + if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) { + /* station activity detected; reset timeout state */ + sta->timeout_next = STA_NULLFUNC; + next_time = sta->last_rx + ap->max_inactivity; + } else if (sta->timeout_next == STA_DISASSOC && + !(sta->flags & WLAN_STA_PENDING_POLL)) { + /* STA ACKed data nullfunc frame poll */ + sta->timeout_next = STA_NULLFUNC; + next_time = jiffies + ap->max_inactivity; + } + + if (next_time) { + sta->timer.expires = next_time; + add_timer(&sta->timer); + return; + } + + if (sta->ap) + sta->timeout_next = STA_DEAUTH; + + if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) { + spin_lock(&ap->sta_table_lock); + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + spin_unlock(&ap->sta_table_lock); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + } else if (sta->timeout_next == STA_DISASSOC) + sta->flags &= ~WLAN_STA_ASSOC; + + if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(local->dev, sta); + + if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 && + !skb_queue_empty(&sta->tx_buf)) { + hostap_set_tim(local, sta->aid, 0); + sta->flags &= ~WLAN_STA_TIM; + } + + if (sta->ap) { + if (ap->autom_ap_wds) { + PDEBUG(DEBUG_AP, "%s: removing automatic WDS " + "connection to AP %pM\n", + local->dev->name, sta->addr); + hostap_wds_link_oper(local, sta->addr, WDS_DEL); + } + } else if (sta->timeout_next == STA_NULLFUNC) { + /* send data frame to poll STA and check whether this frame + * is ACKed */ + /* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but + * it is apparently not retried so TX Exc events are not + * received for it */ + sta->flags |= WLAN_STA_PENDING_POLL; + prism2_send_mgmt(local->dev, IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_DATA, NULL, 0, + sta->addr, ap->tx_callback_poll); + } else { + int deauth = sta->timeout_next == STA_DEAUTH; + __le16 resp; + PDEBUG(DEBUG_AP, "%s: sending %s info to STA %pM" + "(last=%lu, jiffies=%lu)\n", + local->dev->name, + deauth ? "deauthentication" : "disassociation", + sta->addr, sta->last_rx, jiffies); + + resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID : + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + prism2_send_mgmt(local->dev, IEEE80211_FTYPE_MGMT | + (deauth ? IEEE80211_STYPE_DEAUTH : + IEEE80211_STYPE_DISASSOC), + (char *) &resp, 2, sta->addr, 0); + } + + if (sta->timeout_next == STA_DEAUTH) { + if (sta->flags & WLAN_STA_PERM) { + PDEBUG(DEBUG_AP, "%s: STA %pM" + " would have been removed, " + "but it has 'perm' flag\n", + local->dev->name, sta->addr); + } else + ap_free_sta(ap, sta); + return; + } + + if (sta->timeout_next == STA_NULLFUNC) { + sta->timeout_next = STA_DISASSOC; + sta->timer.expires = jiffies + AP_DISASSOC_DELAY; + } else { + sta->timeout_next = STA_DEAUTH; + sta->timer.expires = jiffies + AP_DEAUTH_DELAY; + } + + add_timer(&sta->timer); +} + + +void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap, + int resend) +{ + u8 addr[ETH_ALEN]; + __le16 resp; + int i; + + PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name); + eth_broadcast_addr(addr); + + resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + + /* deauth message sent; try to resend it few times; the message is + * broadcast, so it may be delayed until next DTIM; there is not much + * else we can do at this point since the driver is going to be shut + * down */ + for (i = 0; i < 5; i++) { + prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_DEAUTH, + (char *) &resp, 2, addr, 0); + + if (!resend || ap->num_sta <= 0) + return; + + mdelay(50); + } +} + + +static int ap_control_proc_show(struct seq_file *m, void *v) +{ + struct ap_data *ap = m->private; + char *policy_txt; + struct mac_entry *entry; + + if (v == SEQ_START_TOKEN) { + switch (ap->mac_restrictions.policy) { + case MAC_POLICY_OPEN: + policy_txt = "open"; + break; + case MAC_POLICY_ALLOW: + policy_txt = "allow"; + break; + case MAC_POLICY_DENY: + policy_txt = "deny"; + break; + default: + policy_txt = "unknown"; + break; + } + seq_printf(m, "MAC policy: %s\n", policy_txt); + seq_printf(m, "MAC entries: %u\n", ap->mac_restrictions.entries); + seq_puts(m, "MAC list:\n"); + return 0; + } + + entry = v; + seq_printf(m, "%pM\n", entry->addr); + return 0; +} + +static void *ap_control_proc_start(struct seq_file *m, loff_t *_pos) +{ + struct ap_data *ap = m->private; + spin_lock_bh(&ap->mac_restrictions.lock); + return seq_list_start_head(&ap->mac_restrictions.mac_list, *_pos); +} + +static void *ap_control_proc_next(struct seq_file *m, void *v, loff_t *_pos) +{ + struct ap_data *ap = m->private; + return seq_list_next(v, &ap->mac_restrictions.mac_list, _pos); +} + +static void ap_control_proc_stop(struct seq_file *m, void *v) +{ + struct ap_data *ap = m->private; + spin_unlock_bh(&ap->mac_restrictions.lock); +} + +static const struct seq_operations ap_control_proc_seqops = { + .start = ap_control_proc_start, + .next = ap_control_proc_next, + .stop = ap_control_proc_stop, + .show = ap_control_proc_show, +}; + +static int ap_control_proc_open(struct inode *inode, struct file *file) +{ + int ret = seq_open(file, &ap_control_proc_seqops); + if (ret == 0) { + struct seq_file *m = file->private_data; + m->private = PDE_DATA(inode); + } + return ret; +} + +static const struct file_operations ap_control_proc_fops = { + .open = ap_control_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + + +int ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac) +{ + struct mac_entry *entry; + + entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL); + if (entry == NULL) + return -ENOMEM; + + memcpy(entry->addr, mac, ETH_ALEN); + + spin_lock_bh(&mac_restrictions->lock); + list_add_tail(&entry->list, &mac_restrictions->mac_list); + mac_restrictions->entries++; + spin_unlock_bh(&mac_restrictions->lock); + + return 0; +} + + +int ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac) +{ + struct list_head *ptr; + struct mac_entry *entry; + + spin_lock_bh(&mac_restrictions->lock); + for (ptr = mac_restrictions->mac_list.next; + ptr != &mac_restrictions->mac_list; ptr = ptr->next) { + entry = list_entry(ptr, struct mac_entry, list); + + if (ether_addr_equal(entry->addr, mac)) { + list_del(ptr); + kfree(entry); + mac_restrictions->entries--; + spin_unlock_bh(&mac_restrictions->lock); + return 0; + } + } + spin_unlock_bh(&mac_restrictions->lock); + return -1; +} + + +static int ap_control_mac_deny(struct mac_restrictions *mac_restrictions, + u8 *mac) +{ + struct mac_entry *entry; + int found = 0; + + if (mac_restrictions->policy == MAC_POLICY_OPEN) + return 0; + + spin_lock_bh(&mac_restrictions->lock); + list_for_each_entry(entry, &mac_restrictions->mac_list, list) { + if (ether_addr_equal(entry->addr, mac)) { + found = 1; + break; + } + } + spin_unlock_bh(&mac_restrictions->lock); + + if (mac_restrictions->policy == MAC_POLICY_ALLOW) + return !found; + else + return found; +} + + +void ap_control_flush_macs(struct mac_restrictions *mac_restrictions) +{ + struct list_head *ptr, *n; + struct mac_entry *entry; + + if (mac_restrictions->entries == 0) + return; + + spin_lock_bh(&mac_restrictions->lock); + for (ptr = mac_restrictions->mac_list.next, n = ptr->next; + ptr != &mac_restrictions->mac_list; + ptr = n, n = ptr->next) { + entry = list_entry(ptr, struct mac_entry, list); + list_del(ptr); + kfree(entry); + } + mac_restrictions->entries = 0; + spin_unlock_bh(&mac_restrictions->lock); +} + + +int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac) +{ + struct sta_info *sta; + __le16 resp; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, mac); + if (sta) { + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + } + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -EINVAL; + + resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH, + (char *) &resp, 2, sta->addr, 0); + + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(dev, sta); + + ap_free_sta(ap, sta); + + return 0; +} + +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +void ap_control_kickall(struct ap_data *ap) +{ + struct list_head *ptr, *n; + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list; + ptr = n, n = ptr->next) { + sta = list_entry(ptr, struct sta_info, list); + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + ap_free_sta(ap, sta); + } + spin_unlock_bh(&ap->sta_table_lock); +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +static int prism2_ap_proc_show(struct seq_file *m, void *v) +{ + struct sta_info *sta = v; + int i; + + if (v == SEQ_START_TOKEN) { + seq_printf(m, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n"); + return 0; + } + + if (!sta->ap) + return 0; + + seq_printf(m, "%pM %d %d %d %d '", + sta->addr, + sta->u.ap.channel, sta->last_rx_signal, + sta->last_rx_silence, sta->last_rx_rate); + + for (i = 0; i < sta->u.ap.ssid_len; i++) { + if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127) + seq_putc(m, sta->u.ap.ssid[i]); + else + seq_printf(m, "<%02x>", sta->u.ap.ssid[i]); + } + + seq_putc(m, '\''); + if (sta->capability & WLAN_CAPABILITY_ESS) + seq_puts(m, " [ESS]"); + if (sta->capability & WLAN_CAPABILITY_IBSS) + seq_puts(m, " [IBSS]"); + if (sta->capability & WLAN_CAPABILITY_PRIVACY) + seq_puts(m, " [WEP]"); + seq_putc(m, '\n'); + return 0; +} + +static void *prism2_ap_proc_start(struct seq_file *m, loff_t *_pos) +{ + struct ap_data *ap = m->private; + spin_lock_bh(&ap->sta_table_lock); + return seq_list_start_head(&ap->sta_list, *_pos); +} + +static void *prism2_ap_proc_next(struct seq_file *m, void *v, loff_t *_pos) +{ + struct ap_data *ap = m->private; + return seq_list_next(v, &ap->sta_list, _pos); +} + +static void prism2_ap_proc_stop(struct seq_file *m, void *v) +{ + struct ap_data *ap = m->private; + spin_unlock_bh(&ap->sta_table_lock); +} + +static const struct seq_operations prism2_ap_proc_seqops = { + .start = prism2_ap_proc_start, + .next = prism2_ap_proc_next, + .stop = prism2_ap_proc_stop, + .show = prism2_ap_proc_show, +}; + +static int prism2_ap_proc_open(struct inode *inode, struct file *file) +{ + int ret = seq_open(file, &prism2_ap_proc_seqops); + if (ret == 0) { + struct seq_file *m = file->private_data; + m->private = PDE_DATA(inode); + } + return ret; +} + +static const struct file_operations prism2_ap_proc_fops = { + .open = prism2_ap_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver) +{ + if (!ap) + return; + + if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) { + PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - " + "firmware upgrade recommended\n"); + ap->nullfunc_ack = 1; + } else + ap->nullfunc_ack = 0; + + if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) { + printk(KERN_WARNING "%s: Warning: secondary station firmware " + "version 1.4.2 does not seem to work in Host AP mode\n", + ap->local->dev->name); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + struct ieee80211_hdr *hdr; + + if (!ap->local->hostapd || !ap->local->apdev) { + dev_kfree_skb(skb); + return; + } + + /* Pass the TX callback frame to the hostapd; use 802.11 header version + * 1 to indicate failure (no ACK) and 2 success (frame ACKed) */ + + hdr = (struct ieee80211_hdr *) skb->data; + hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_VERS); + hdr->frame_control |= cpu_to_le16(ok ? BIT(1) : BIT(0)); + + skb->dev = ap->local->apdev; + skb_pull(skb, hostap_80211_get_hdrlen(hdr->frame_control)); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = cpu_to_be16(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +/* Called only as a tasklet (software IRQ) */ +static void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + struct net_device *dev = ap->local->dev; + struct ieee80211_hdr *hdr; + u16 auth_alg, auth_transaction, status; + __le16 *pos; + struct sta_info *sta = NULL; + char *txt = NULL; + + if (ap->local->hostapd) { + dev_kfree_skb(skb); + return; + } + + hdr = (struct ieee80211_hdr *) skb->data; + if (!ieee80211_is_auth(hdr->frame_control) || + skb->len < IEEE80211_MGMT_HDR_LEN + 6) { + printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid " + "frame\n", dev->name); + dev_kfree_skb(skb); + return; + } + + pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + auth_alg = le16_to_cpu(*pos++); + auth_transaction = le16_to_cpu(*pos++); + status = le16_to_cpu(*pos++); + + if (!ok) { + txt = "frame was not ACKed"; + goto done; + } + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr1); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&ap->sta_table_lock); + + if (!sta) { + txt = "STA not found"; + goto done; + } + + if (status == WLAN_STATUS_SUCCESS && + ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { + txt = "STA authenticated"; + sta->flags |= WLAN_STA_AUTH; + sta->last_auth = jiffies; + } else if (status != WLAN_STATUS_SUCCESS) + txt = "authentication failed"; + + done: + if (sta) + atomic_dec(&sta->users); + if (txt) { + PDEBUG(DEBUG_AP, "%s: %pM auth_cb - alg=%d " + "trans#=%d status=%d - %s\n", + dev->name, hdr->addr1, + auth_alg, auth_transaction, status, txt); + } + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + struct net_device *dev = ap->local->dev; + struct ieee80211_hdr *hdr; + u16 status; + __le16 *pos; + struct sta_info *sta = NULL; + char *txt = NULL; + + if (ap->local->hostapd) { + dev_kfree_skb(skb); + return; + } + + hdr = (struct ieee80211_hdr *) skb->data; + if ((!ieee80211_is_assoc_resp(hdr->frame_control) && + !ieee80211_is_reassoc_resp(hdr->frame_control)) || + skb->len < IEEE80211_MGMT_HDR_LEN + 4) { + printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid " + "frame\n", dev->name); + dev_kfree_skb(skb); + return; + } + + if (!ok) { + txt = "frame was not ACKed"; + goto done; + } + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr1); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&ap->sta_table_lock); + + if (!sta) { + txt = "STA not found"; + goto done; + } + + pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + pos++; + status = le16_to_cpu(*pos++); + if (status == WLAN_STATUS_SUCCESS) { + if (!(sta->flags & WLAN_STA_ASSOC)) + hostap_event_new_sta(dev, sta); + txt = "STA associated"; + sta->flags |= WLAN_STA_ASSOC; + sta->last_assoc = jiffies; + } else + txt = "association failed"; + + done: + if (sta) + atomic_dec(&sta->users); + if (txt) { + PDEBUG(DEBUG_AP, "%s: %pM assoc_cb - %s\n", + dev->name, hdr->addr1, txt); + } + dev_kfree_skb(skb); +} + +/* Called only as a tasklet (software IRQ); TX callback for poll frames used + * in verifying whether the STA is still present. */ +static void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + struct ieee80211_hdr *hdr; + struct sta_info *sta; + + if (skb->len < 24) + goto fail; + hdr = (struct ieee80211_hdr *) skb->data; + if (ok) { + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr1); + if (sta) + sta->flags &= ~WLAN_STA_PENDING_POLL; + spin_unlock(&ap->sta_table_lock); + } else { + PDEBUG(DEBUG_AP, + "%s: STA %pM did not ACK activity poll frame\n", + ap->local->dev->name, hdr->addr1); + } + + fail: + dev_kfree_skb(skb); +} +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +void hostap_init_data(local_info_t *local) +{ + struct ap_data *ap = local->ap; + + if (ap == NULL) { + printk(KERN_WARNING "hostap_init_data: ap == NULL\n"); + return; + } + memset(ap, 0, sizeof(struct ap_data)); + ap->local = local; + + ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx); + ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx); + ap->max_inactivity = + GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ; + ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx); + + spin_lock_init(&ap->sta_table_lock); + INIT_LIST_HEAD(&ap->sta_list); + + /* Initialize task queue structure for AP management */ + INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue); + + ap->tx_callback_idx = + hostap_tx_callback_register(local, hostap_ap_tx_cb, ap); + if (ap->tx_callback_idx == 0) + printk(KERN_WARNING "%s: failed to register TX callback for " + "AP\n", local->dev->name); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue); + + ap->tx_callback_auth = + hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap); + ap->tx_callback_assoc = + hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap); + ap->tx_callback_poll = + hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap); + if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 || + ap->tx_callback_poll == 0) + printk(KERN_WARNING "%s: failed to register TX callback for " + "AP\n", local->dev->name); + + spin_lock_init(&ap->mac_restrictions.lock); + INIT_LIST_HEAD(&ap->mac_restrictions.mac_list); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + ap->initialized = 1; +} + + +void hostap_init_ap_proc(local_info_t *local) +{ + struct ap_data *ap = local->ap; + + ap->proc = local->proc; + if (ap->proc == NULL) + return; + +#ifndef PRISM2_NO_PROCFS_DEBUG + proc_create_data("ap_debug", 0, ap->proc, &ap_debug_proc_fops, ap); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + proc_create_data("ap_control", 0, ap->proc, &ap_control_proc_fops, ap); + proc_create_data("ap", 0, ap->proc, &prism2_ap_proc_fops, ap); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + +} + + +void hostap_free_data(struct ap_data *ap) +{ + struct sta_info *n, *sta; + + if (ap == NULL || !ap->initialized) { + printk(KERN_DEBUG "hostap_free_data: ap has not yet been " + "initialized - skip resource freeing\n"); + return; + } + + flush_work(&ap->add_sta_proc_queue); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + flush_work(&ap->wds_oper_queue); + if (ap->crypt) + ap->crypt->deinit(ap->crypt_priv); + ap->crypt = ap->crypt_priv = NULL; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + list_for_each_entry_safe(sta, n, &ap->sta_list, list) { + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + ap_free_sta(ap, sta); + } + +#ifndef PRISM2_NO_PROCFS_DEBUG + if (ap->proc != NULL) { + remove_proc_entry("ap_debug", ap->proc); + } +#endif /* PRISM2_NO_PROCFS_DEBUG */ + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (ap->proc != NULL) { + remove_proc_entry("ap", ap->proc); + remove_proc_entry("ap_control", ap->proc); + } + ap_control_flush_macs(&ap->mac_restrictions); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + ap->initialized = 0; +} + + +/* caller should have mutex for AP STA list handling */ +static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta) +{ + struct sta_info *s; + + s = ap->sta_hash[STA_HASH(sta)]; + while (s != NULL && !ether_addr_equal(s->addr, sta)) + s = s->hnext; + return s; +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +/* Called from timer handler and from scheduled AP queue handlers */ +static void prism2_send_mgmt(struct net_device *dev, + u16 type_subtype, char *body, + int body_len, u8 *addr, u16 tx_cb_idx) +{ + struct hostap_interface *iface; + local_info_t *local; + struct ieee80211_hdr *hdr; + u16 fc; + struct sk_buff *skb; + struct hostap_skb_tx_data *meta; + int hdrlen; + + iface = netdev_priv(dev); + local = iface->local; + dev = local->dev; /* always use master radio device */ + iface = netdev_priv(dev); + + if (!(dev->flags & IFF_UP)) { + PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - " + "cannot send frame\n", dev->name); + return; + } + + skb = dev_alloc_skb(sizeof(*hdr) + body_len); + if (skb == NULL) { + PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate " + "skb\n", dev->name); + return; + } + + fc = type_subtype; + hdrlen = hostap_80211_get_hdrlen(cpu_to_le16(type_subtype)); + hdr = (struct ieee80211_hdr *) skb_put(skb, hdrlen); + if (body) + memcpy(skb_put(skb, body_len), body, body_len); + + memset(hdr, 0, hdrlen); + + /* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11 + * tx_control instead of using local->tx_control */ + + + memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */ + if (ieee80211_is_data(hdr->frame_control)) { + fc |= IEEE80211_FCTL_FROMDS; + memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */ + memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */ + } else if (ieee80211_is_ctl(hdr->frame_control)) { + /* control:ACK does not have addr2 or addr3 */ + eth_zero_addr(hdr->addr2); + eth_zero_addr(hdr->addr3); + } else { + memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */ + memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */ + } + + hdr->frame_control = cpu_to_le16(fc); + + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + meta->iface = iface; + meta->tx_cb_idx = tx_cb_idx; + + skb->dev = dev; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + dev_queue_xmit(skb); +} +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +static int prism2_sta_proc_show(struct seq_file *m, void *v) +{ + struct sta_info *sta = m->private; + int i; + + /* FIX: possible race condition.. the STA data could have just expired, + * but proc entry was still here so that the read could have started; + * some locking should be done here.. */ + + seq_printf(m, + "%s=%pM\nusers=%d\naid=%d\n" + "flags=0x%04x%s%s%s%s%s%s%s\n" + "capability=0x%02x\nlisten_interval=%d\nsupported_rates=", + sta->ap ? "AP" : "STA", + sta->addr, atomic_read(&sta->users), sta->aid, + sta->flags, + sta->flags & WLAN_STA_AUTH ? " AUTH" : "", + sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "", + sta->flags & WLAN_STA_PS ? " PS" : "", + sta->flags & WLAN_STA_TIM ? " TIM" : "", + sta->flags & WLAN_STA_PERM ? " PERM" : "", + sta->flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED" : "", + sta->flags & WLAN_STA_PENDING_POLL ? " POLL" : "", + sta->capability, sta->listen_interval); + /* supported_rates: 500 kbit/s units with msb ignored */ + for (i = 0; i < sizeof(sta->supported_rates); i++) + if (sta->supported_rates[i] != 0) + seq_printf(m, "%d%sMbps ", + (sta->supported_rates[i] & 0x7f) / 2, + sta->supported_rates[i] & 1 ? ".5" : ""); + seq_printf(m, + "\njiffies=%lu\nlast_auth=%lu\nlast_assoc=%lu\n" + "last_rx=%lu\nlast_tx=%lu\nrx_packets=%lu\n" + "tx_packets=%lu\n" + "rx_bytes=%lu\ntx_bytes=%lu\nbuffer_count=%d\n" + "last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps\n" + "tx_rate=%d\ntx[1M]=%d\ntx[2M]=%d\ntx[5.5M]=%d\n" + "tx[11M]=%d\n" + "rx[1M]=%d\nrx[2M]=%d\nrx[5.5M]=%d\nrx[11M]=%d\n", + jiffies, sta->last_auth, sta->last_assoc, sta->last_rx, + sta->last_tx, + sta->rx_packets, sta->tx_packets, sta->rx_bytes, + sta->tx_bytes, skb_queue_len(&sta->tx_buf), + sta->last_rx_silence, + sta->last_rx_signal, sta->last_rx_rate / 10, + sta->last_rx_rate % 10 ? ".5" : "", + sta->tx_rate, sta->tx_count[0], sta->tx_count[1], + sta->tx_count[2], sta->tx_count[3], sta->rx_count[0], + sta->rx_count[1], sta->rx_count[2], sta->rx_count[3]); + if (sta->crypt && sta->crypt->ops && sta->crypt->ops->print_stats) + sta->crypt->ops->print_stats(m, sta->crypt->priv); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (sta->ap) { + if (sta->u.ap.channel >= 0) + seq_printf(m, "channel=%d\n", sta->u.ap.channel); + seq_puts(m, "ssid="); + for (i = 0; i < sta->u.ap.ssid_len; i++) { + if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127) + seq_putc(m, sta->u.ap.ssid[i]); + else + seq_printf(m, "<%02x>", sta->u.ap.ssid[i]); + } + seq_putc(m, '\n'); + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + return 0; +} + +static int prism2_sta_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, prism2_sta_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations prism2_sta_proc_fops = { + .open = prism2_sta_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void handle_add_proc_queue(struct work_struct *work) +{ + struct ap_data *ap = container_of(work, struct ap_data, + add_sta_proc_queue); + struct sta_info *sta; + char name[20]; + struct add_sta_proc_data *entry, *prev; + + entry = ap->add_sta_proc_entries; + ap->add_sta_proc_entries = NULL; + + while (entry) { + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, entry->addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (sta) { + sprintf(name, "%pM", sta->addr); + sta->proc = proc_create_data( + name, 0, ap->proc, + &prism2_sta_proc_fops, sta); + + atomic_dec(&sta->users); + } + + prev = entry; + entry = entry->next; + kfree(prev); + } +} + + +static struct sta_info * ap_add_sta(struct ap_data *ap, u8 *addr) +{ + struct sta_info *sta; + + sta = kzalloc(sizeof(struct sta_info), GFP_ATOMIC); + if (sta == NULL) { + PDEBUG(DEBUG_AP, "AP: kmalloc failed\n"); + return NULL; + } + + /* initialize STA info data */ + sta->local = ap->local; + skb_queue_head_init(&sta->tx_buf); + memcpy(sta->addr, addr, ETH_ALEN); + + atomic_inc(&sta->users); + spin_lock_bh(&ap->sta_table_lock); + list_add(&sta->list, &ap->sta_list); + ap->num_sta++; + ap_sta_hash_add(ap, sta); + spin_unlock_bh(&ap->sta_table_lock); + + if (ap->proc) { + struct add_sta_proc_data *entry; + /* schedule a non-interrupt context process to add a procfs + * entry for the STA since procfs code use GFP_KERNEL */ + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry) { + memcpy(entry->addr, sta->addr, ETH_ALEN); + entry->next = ap->add_sta_proc_entries; + ap->add_sta_proc_entries = entry; + schedule_work(&ap->add_sta_proc_queue); + } else + printk(KERN_DEBUG "Failed to add STA proc data\n"); + } + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + init_timer(&sta->timer); + sta->timer.expires = jiffies + ap->max_inactivity; + sta->timer.data = (unsigned long) sta; + sta->timer.function = ap_handle_timer; + if (!ap->local->hostapd) + add_timer(&sta->timer); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + return sta; +} + + +static int ap_tx_rate_ok(int rateidx, struct sta_info *sta, + local_info_t *local) +{ + if (rateidx > sta->tx_max_rate || + !(sta->tx_supp_rates & (1 << rateidx))) + return 0; + + if (local->tx_rate_control != 0 && + !(local->tx_rate_control & (1 << rateidx))) + return 0; + + return 1; +} + + +static void prism2_check_tx_rates(struct sta_info *sta) +{ + int i; + + sta->tx_supp_rates = 0; + for (i = 0; i < sizeof(sta->supported_rates); i++) { + if ((sta->supported_rates[i] & 0x7f) == 2) + sta->tx_supp_rates |= WLAN_RATE_1M; + if ((sta->supported_rates[i] & 0x7f) == 4) + sta->tx_supp_rates |= WLAN_RATE_2M; + if ((sta->supported_rates[i] & 0x7f) == 11) + sta->tx_supp_rates |= WLAN_RATE_5M5; + if ((sta->supported_rates[i] & 0x7f) == 22) + sta->tx_supp_rates |= WLAN_RATE_11M; + } + sta->tx_max_rate = sta->tx_rate = sta->tx_rate_idx = 0; + if (sta->tx_supp_rates & WLAN_RATE_1M) { + sta->tx_max_rate = 0; + if (ap_tx_rate_ok(0, sta, sta->local)) { + sta->tx_rate = 10; + sta->tx_rate_idx = 0; + } + } + if (sta->tx_supp_rates & WLAN_RATE_2M) { + sta->tx_max_rate = 1; + if (ap_tx_rate_ok(1, sta, sta->local)) { + sta->tx_rate = 20; + sta->tx_rate_idx = 1; + } + } + if (sta->tx_supp_rates & WLAN_RATE_5M5) { + sta->tx_max_rate = 2; + if (ap_tx_rate_ok(2, sta, sta->local)) { + sta->tx_rate = 55; + sta->tx_rate_idx = 2; + } + } + if (sta->tx_supp_rates & WLAN_RATE_11M) { + sta->tx_max_rate = 3; + if (ap_tx_rate_ok(3, sta, sta->local)) { + sta->tx_rate = 110; + sta->tx_rate_idx = 3; + } + } +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +static void ap_crypt_init(struct ap_data *ap) +{ + ap->crypt = lib80211_get_crypto_ops("WEP"); + + if (ap->crypt) { + if (ap->crypt->init) { + ap->crypt_priv = ap->crypt->init(0); + if (ap->crypt_priv == NULL) + ap->crypt = NULL; + else { + u8 key[WEP_KEY_LEN]; + get_random_bytes(key, WEP_KEY_LEN); + ap->crypt->set_key(key, WEP_KEY_LEN, NULL, + ap->crypt_priv); + } + } + } + + if (ap->crypt == NULL) { + printk(KERN_WARNING "AP could not initialize WEP: load module " + "lib80211_crypt_wep.ko\n"); + } +} + + +/* Generate challenge data for shared key authentication. IEEE 802.11 specifies + * that WEP algorithm is used for generating challenge. This should be unique, + * but otherwise there is not really need for randomness etc. Initialize WEP + * with pseudo random key and then use increasing IV to get unique challenge + * streams. + * + * Called only as a scheduled task for pending AP frames. + */ +static char * ap_auth_make_challenge(struct ap_data *ap) +{ + char *tmpbuf; + struct sk_buff *skb; + + if (ap->crypt == NULL) { + ap_crypt_init(ap); + if (ap->crypt == NULL) + return NULL; + } + + tmpbuf = kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC); + if (tmpbuf == NULL) { + PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n"); + return NULL; + } + + skb = dev_alloc_skb(WLAN_AUTH_CHALLENGE_LEN + + ap->crypt->extra_mpdu_prefix_len + + ap->crypt->extra_mpdu_postfix_len); + if (skb == NULL) { + kfree(tmpbuf); + return NULL; + } + + skb_reserve(skb, ap->crypt->extra_mpdu_prefix_len); + memset(skb_put(skb, WLAN_AUTH_CHALLENGE_LEN), 0, + WLAN_AUTH_CHALLENGE_LEN); + if (ap->crypt->encrypt_mpdu(skb, 0, ap->crypt_priv)) { + dev_kfree_skb(skb); + kfree(tmpbuf); + return NULL; + } + + skb_copy_from_linear_data_offset(skb, ap->crypt->extra_mpdu_prefix_len, + tmpbuf, WLAN_AUTH_CHALLENGE_LEN); + dev_kfree_skb(skb); + + return tmpbuf; +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_authen(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + size_t hdrlen; + struct ap_data *ap = local->ap; + char body[8 + WLAN_AUTH_CHALLENGE_LEN], *challenge = NULL; + int len, olen; + u16 auth_alg, auth_transaction, status_code; + __le16 *pos; + u16 resp = WLAN_STATUS_SUCCESS; + struct sta_info *sta = NULL; + struct lib80211_crypt_data *crypt; + char *txt = ""; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); + + if (len < 6) { + PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload " + "(len=%d) from %pM\n", dev->name, len, hdr->addr2); + return; + } + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta && sta->crypt) + crypt = sta->crypt; + else { + int idx = 0; + if (skb->len >= hdrlen + 3) + idx = skb->data[hdrlen + 3] >> 6; + crypt = local->crypt_info.crypt[idx]; + } + + pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + auth_alg = __le16_to_cpu(*pos); + pos++; + auth_transaction = __le16_to_cpu(*pos); + pos++; + status_code = __le16_to_cpu(*pos); + pos++; + + if (ether_addr_equal(dev->dev_addr, hdr->addr2) || + ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) { + txt = "authentication denied"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (((local->auth_algs & PRISM2_AUTH_OPEN) && + auth_alg == WLAN_AUTH_OPEN) || + ((local->auth_algs & PRISM2_AUTH_SHARED_KEY) && + crypt && auth_alg == WLAN_AUTH_SHARED_KEY)) { + } else { + txt = "unsupported algorithm"; + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto fail; + } + + if (len >= 8) { + u8 *u = (u8 *) pos; + if (*u == WLAN_EID_CHALLENGE) { + if (*(u + 1) != WLAN_AUTH_CHALLENGE_LEN) { + txt = "invalid challenge len"; + resp = WLAN_STATUS_CHALLENGE_FAIL; + goto fail; + } + if (len - 8 < WLAN_AUTH_CHALLENGE_LEN) { + txt = "challenge underflow"; + resp = WLAN_STATUS_CHALLENGE_FAIL; + goto fail; + } + challenge = (char *) (u + 2); + } + } + + if (sta && sta->ap) { + if (time_after(jiffies, sta->u.ap.last_beacon + + (10 * sta->listen_interval * HZ) / 1024)) { + PDEBUG(DEBUG_AP, "%s: no beacons received for a while," + " assuming AP %pM is now STA\n", + dev->name, sta->addr); + sta->ap = 0; + sta->flags = 0; + sta->u.sta.challenge = NULL; + } else { + txt = "AP trying to authenticate?"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + + if ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) || + (auth_alg == WLAN_AUTH_SHARED_KEY && + (auth_transaction == 1 || + (auth_transaction == 3 && sta != NULL && + sta->u.sta.challenge != NULL)))) { + } else { + txt = "unknown authentication transaction number"; + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + goto fail; + } + + if (sta == NULL) { + txt = "new STA"; + + if (local->ap->num_sta >= MAX_STA_COUNT) { + /* FIX: might try to remove some old STAs first? */ + txt = "no more room for new STAs"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + sta = ap_add_sta(local->ap, hdr->addr2); + if (sta == NULL) { + txt = "ap_add_sta failed"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + + switch (auth_alg) { + case WLAN_AUTH_OPEN: + txt = "authOK"; + /* IEEE 802.11 standard is not completely clear about + * whether STA is considered authenticated after + * authentication OK frame has been send or after it + * has been ACKed. In order to reduce interoperability + * issues, mark the STA authenticated before ACK. */ + sta->flags |= WLAN_STA_AUTH; + break; + + case WLAN_AUTH_SHARED_KEY: + if (auth_transaction == 1) { + if (sta->u.sta.challenge == NULL) { + sta->u.sta.challenge = + ap_auth_make_challenge(local->ap); + if (sta->u.sta.challenge == NULL) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + } else { + if (sta->u.sta.challenge == NULL || + challenge == NULL || + memcmp(sta->u.sta.challenge, challenge, + WLAN_AUTH_CHALLENGE_LEN) != 0 || + !ieee80211_has_protected(hdr->frame_control)) { + txt = "challenge response incorrect"; + resp = WLAN_STATUS_CHALLENGE_FAIL; + goto fail; + } + + txt = "challenge OK - authOK"; + /* IEEE 802.11 standard is not completely clear about + * whether STA is considered authenticated after + * authentication OK frame has been send or after it + * has been ACKed. In order to reduce interoperability + * issues, mark the STA authenticated before ACK. */ + sta->flags |= WLAN_STA_AUTH; + kfree(sta->u.sta.challenge); + sta->u.sta.challenge = NULL; + } + break; + } + + fail: + pos = (__le16 *) body; + *pos = cpu_to_le16(auth_alg); + pos++; + *pos = cpu_to_le16(auth_transaction + 1); + pos++; + *pos = cpu_to_le16(resp); /* status_code */ + pos++; + olen = 6; + + if (resp == WLAN_STATUS_SUCCESS && sta != NULL && + sta->u.sta.challenge != NULL && + auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 1) { + u8 *tmp = (u8 *) pos; + *tmp++ = WLAN_EID_CHALLENGE; + *tmp++ = WLAN_AUTH_CHALLENGE_LEN; + pos++; + memcpy(pos, sta->u.sta.challenge, WLAN_AUTH_CHALLENGE_LEN); + olen += 2 + WLAN_AUTH_CHALLENGE_LEN; + } + + prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH, + body, olen, hdr->addr2, ap->tx_callback_auth); + + if (sta) { + sta->last_rx = jiffies; + atomic_dec(&sta->users); + } + + if (resp) { + PDEBUG(DEBUG_AP, "%s: %pM auth (alg=%d " + "trans#=%d stat=%d len=%d fc=%04x) ==> %d (%s)\n", + dev->name, hdr->addr2, + auth_alg, auth_transaction, status_code, len, + le16_to_cpu(hdr->frame_control), resp, txt); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_assoc(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, int reassoc) +{ + struct net_device *dev = local->dev; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + char body[12], *p, *lpos; + int len, left; + __le16 *pos; + u16 resp = WLAN_STATUS_SUCCESS; + struct sta_info *sta = NULL; + int send_deauth = 0; + char *txt = ""; + u8 prev_ap[ETH_ALEN]; + + left = len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < (reassoc ? 10 : 4)) { + PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload " + "(len=%d, reassoc=%d) from %pM\n", + dev->name, len, reassoc, hdr->addr2); + return; + } + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { + spin_unlock_bh(&local->ap->sta_table_lock); + txt = "trying to associate before authentication"; + send_deauth = 1; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + sta = NULL; /* do not decrement sta->users */ + goto fail; + } + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + sta->capability = __le16_to_cpu(*pos); + pos++; left -= 2; + sta->listen_interval = __le16_to_cpu(*pos); + pos++; left -= 2; + + if (reassoc) { + memcpy(prev_ap, pos, ETH_ALEN); + pos++; pos++; pos++; left -= 6; + } else + eth_zero_addr(prev_ap); + + if (left >= 2) { + unsigned int ileft; + unsigned char *u = (unsigned char *) pos; + + if (*u == WLAN_EID_SSID) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft > MAX_SSID_LEN) { + txt = "SSID overflow"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (ileft != strlen(local->essid) || + memcmp(local->essid, u, ileft) != 0) { + txt = "not our SSID"; + resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC; + goto fail; + } + + u += ileft; + left -= ileft; + } + + if (left >= 2 && *u == WLAN_EID_SUPP_RATES) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft == 0 || + ileft > WLAN_SUPP_RATES_MAX) { + txt = "SUPP_RATES len error"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + memset(sta->supported_rates, 0, + sizeof(sta->supported_rates)); + memcpy(sta->supported_rates, u, ileft); + prism2_check_tx_rates(sta); + + u += ileft; + left -= ileft; + } + + if (left > 0) { + PDEBUG(DEBUG_AP, "%s: assoc from %pM" + " with extra data (%d bytes) [", + dev->name, hdr->addr2, left); + while (left > 0) { + PDEBUG2(DEBUG_AP, "<%02x>", *u); + u++; left--; + } + PDEBUG2(DEBUG_AP, "]\n"); + } + } else { + txt = "frame underflow"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + /* get a unique AID */ + if (sta->aid > 0) + txt = "OK, old AID"; + else { + spin_lock_bh(&local->ap->sta_table_lock); + for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++) + if (local->ap->sta_aid[sta->aid - 1] == NULL) + break; + if (sta->aid > MAX_AID_TABLE_SIZE) { + sta->aid = 0; + spin_unlock_bh(&local->ap->sta_table_lock); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + txt = "no room for more AIDs"; + } else { + local->ap->sta_aid[sta->aid - 1] = sta; + spin_unlock_bh(&local->ap->sta_table_lock); + txt = "OK, new AID"; + } + } + + fail: + pos = (__le16 *) body; + + if (send_deauth) { + *pos = cpu_to_le16(WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH); + pos++; + } else { + /* FIX: CF-Pollable and CF-PollReq should be set to match the + * values in beacons/probe responses */ + /* FIX: how about privacy and WEP? */ + /* capability */ + *pos = cpu_to_le16(WLAN_CAPABILITY_ESS); + pos++; + + /* status_code */ + *pos = cpu_to_le16(resp); + pos++; + + *pos = cpu_to_le16((sta && sta->aid > 0 ? sta->aid : 0) | + BIT(14) | BIT(15)); /* AID */ + pos++; + + /* Supported rates (Information element) */ + p = (char *) pos; + *p++ = WLAN_EID_SUPP_RATES; + lpos = p; + *p++ = 0; /* len */ + if (local->tx_rate_control & WLAN_RATE_1M) { + *p++ = local->basic_rates & WLAN_RATE_1M ? 0x82 : 0x02; + (*lpos)++; + } + if (local->tx_rate_control & WLAN_RATE_2M) { + *p++ = local->basic_rates & WLAN_RATE_2M ? 0x84 : 0x04; + (*lpos)++; + } + if (local->tx_rate_control & WLAN_RATE_5M5) { + *p++ = local->basic_rates & WLAN_RATE_5M5 ? + 0x8b : 0x0b; + (*lpos)++; + } + if (local->tx_rate_control & WLAN_RATE_11M) { + *p++ = local->basic_rates & WLAN_RATE_11M ? + 0x96 : 0x16; + (*lpos)++; + } + pos = (__le16 *) p; + } + + prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | + (send_deauth ? IEEE80211_STYPE_DEAUTH : + (reassoc ? IEEE80211_STYPE_REASSOC_RESP : + IEEE80211_STYPE_ASSOC_RESP)), + body, (u8 *) pos - (u8 *) body, + hdr->addr2, + send_deauth ? 0 : local->ap->tx_callback_assoc); + + if (sta) { + if (resp == WLAN_STATUS_SUCCESS) { + sta->last_rx = jiffies; + /* STA will be marked associated from TX callback, if + * AssocResp is ACKed */ + } + atomic_dec(&sta->users); + } + +#if 0 + PDEBUG(DEBUG_AP, "%s: %pM %sassoc (len=%d " + "prev_ap=%pM) => %d(%d) (%s)\n", + dev->name, + hdr->addr2, + reassoc ? "re" : "", len, + prev_ap, + resp, send_deauth, txt); +#endif +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_deauth(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + char *body = (char *) (skb->data + IEEE80211_MGMT_HDR_LEN); + int len; + u16 reason_code; + __le16 *pos; + struct sta_info *sta = NULL; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < 2) { + printk("handle_deauth - too short payload (len=%d)\n", len); + return; + } + + pos = (__le16 *) body; + reason_code = le16_to_cpu(*pos); + + PDEBUG(DEBUG_AP, "%s: deauthentication: %pM len=%d, " + "reason_code=%d\n", dev->name, hdr->addr2, + len, reason_code); + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta != NULL) { + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(local->dev, sta); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + } + spin_unlock_bh(&local->ap->sta_table_lock); + if (sta == NULL) { + printk("%s: deauthentication from %pM, " + "reason_code=%d, but STA not authenticated\n", dev->name, + hdr->addr2, reason_code); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_disassoc(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + char *body = skb->data + IEEE80211_MGMT_HDR_LEN; + int len; + u16 reason_code; + __le16 *pos; + struct sta_info *sta = NULL; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < 2) { + printk("handle_disassoc - too short payload (len=%d)\n", len); + return; + } + + pos = (__le16 *) body; + reason_code = le16_to_cpu(*pos); + + PDEBUG(DEBUG_AP, "%s: disassociation: %pM len=%d, " + "reason_code=%d\n", dev->name, hdr->addr2, + len, reason_code); + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta != NULL) { + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(local->dev, sta); + sta->flags &= ~WLAN_STA_ASSOC; + } + spin_unlock_bh(&local->ap->sta_table_lock); + if (sta == NULL) { + printk("%s: disassociation from %pM, " + "reason_code=%d, but STA not authenticated\n", + dev->name, hdr->addr2, reason_code); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void ap_handle_data_nullfunc(local_info_t *local, + struct ieee80211_hdr *hdr) +{ + struct net_device *dev = local->dev; + + /* some STA f/w's seem to require control::ACK frame for + * data::nullfunc, but at least Prism2 station f/w version 0.8.0 does + * not send this.. + * send control::ACK for the data::nullfunc */ + + printk(KERN_DEBUG "Sending control::ACK for data::nullfunc\n"); + prism2_send_mgmt(dev, IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK, + NULL, 0, hdr->addr2, 0); +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void ap_handle_dropped_data(local_info_t *local, + struct ieee80211_hdr *hdr) +{ + struct net_device *dev = local->dev; + struct sta_info *sta; + __le16 reason; + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta != NULL && (sta->flags & WLAN_STA_ASSOC)) { + PDEBUG(DEBUG_AP, "ap_handle_dropped_data: STA is now okay?\n"); + atomic_dec(&sta->users); + return; + } + + reason = cpu_to_le16(WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | + ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ? + IEEE80211_STYPE_DEAUTH : IEEE80211_STYPE_DISASSOC), + (char *) &reason, sizeof(reason), hdr->addr2, 0); + + if (sta) + atomic_dec(&sta->users); +} + +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +/* Called only as a scheduled task for pending AP frames. */ +static void pspoll_send_buffered(local_info_t *local, struct sta_info *sta, + struct sk_buff *skb) +{ + struct hostap_skb_tx_data *meta; + + if (!(sta->flags & WLAN_STA_PS)) { + /* Station has moved to non-PS mode, so send all buffered + * frames using normal device queue. */ + dev_queue_xmit(skb); + return; + } + + /* add a flag for hostap_handle_sta_tx() to know that this skb should + * be passed through even though STA is using PS */ + meta = (struct hostap_skb_tx_data *) skb->cb; + meta->flags |= HOSTAP_TX_FLAGS_BUFFERED_FRAME; + if (!skb_queue_empty(&sta->tx_buf)) { + /* indicate to STA that more frames follow */ + meta->flags |= HOSTAP_TX_FLAGS_ADD_MOREDATA; + } + dev_queue_xmit(skb); +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_pspoll(local_info_t *local, + struct ieee80211_hdr *hdr, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct sta_info *sta; + u16 aid; + struct sk_buff *skb; + + PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=%pM, TA=%pM PWRMGT=%d\n", + hdr->addr1, hdr->addr2, !!ieee80211_has_pm(hdr->frame_control)); + + if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { + PDEBUG(DEBUG_AP, + "handle_pspoll - addr1(BSSID)=%pM not own MAC\n", + hdr->addr1); + return; + } + + aid = le16_to_cpu(hdr->duration_id); + if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) { + PDEBUG(DEBUG_PS, " PSPOLL and AID[15:14] not set\n"); + return; + } + aid &= ~(BIT(15) | BIT(14)); + if (aid == 0 || aid > MAX_AID_TABLE_SIZE) { + PDEBUG(DEBUG_PS, " invalid aid=%d\n", aid); + return; + } + PDEBUG(DEBUG_PS2, " aid=%d\n", aid); + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta == NULL) { + PDEBUG(DEBUG_PS, " STA not found\n"); + return; + } + if (sta->aid != aid) { + PDEBUG(DEBUG_PS, " received aid=%i does not match with " + "assoc.aid=%d\n", aid, sta->aid); + return; + } + + /* FIX: todo: + * - add timeout for buffering (clear aid in TIM vector if buffer timed + * out (expiry time must be longer than ListenInterval for + * the corresponding STA; "8802-11: 11.2.1.9 AP aging function" + * - what to do, if buffered, pspolled, and sent frame is not ACKed by + * sta; store buffer for later use and leave TIM aid bit set? use + * TX event to check whether frame was ACKed? + */ + + while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) { + /* send buffered frame .. */ + PDEBUG(DEBUG_PS2, "Sending buffered frame to STA after PS POLL" + " (buffer_count=%d)\n", skb_queue_len(&sta->tx_buf)); + + pspoll_send_buffered(local, sta, skb); + + if (sta->flags & WLAN_STA_PS) { + /* send only one buffered packet per PS Poll */ + /* FIX: should ignore further PS Polls until the + * buffered packet that was just sent is acknowledged + * (Tx or TxExc event) */ + break; + } + } + + if (skb_queue_empty(&sta->tx_buf)) { + /* try to clear aid from TIM */ + if (!(sta->flags & WLAN_STA_TIM)) + PDEBUG(DEBUG_PS2, "Re-unsetting TIM for aid %d\n", + aid); + hostap_set_tim(local, aid, 0); + sta->flags &= ~WLAN_STA_TIM; + } + + atomic_dec(&sta->users); +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +static void handle_wds_oper_queue(struct work_struct *work) +{ + struct ap_data *ap = container_of(work, struct ap_data, + wds_oper_queue); + local_info_t *local = ap->local; + struct wds_oper_data *entry, *prev; + + spin_lock_bh(&local->lock); + entry = local->ap->wds_oper_entries; + local->ap->wds_oper_entries = NULL; + spin_unlock_bh(&local->lock); + + while (entry) { + PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection " + "to AP %pM\n", + local->dev->name, + entry->type == WDS_ADD ? "adding" : "removing", + entry->addr); + if (entry->type == WDS_ADD) + prism2_wds_add(local, entry->addr, 0); + else if (entry->type == WDS_DEL) + prism2_wds_del(local, entry->addr, 0, 1); + + prev = entry; + entry = entry->next; + kfree(prev); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_beacon(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + char *body = skb->data + IEEE80211_MGMT_HDR_LEN; + int len, left; + u16 beacon_int, capability; + __le16 *pos; + char *ssid = NULL; + unsigned char *supp_rates = NULL; + int ssid_len = 0, supp_rates_len = 0; + struct sta_info *sta = NULL; + int new_sta = 0, channel = -1; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < 8 + 2 + 2) { + printk(KERN_DEBUG "handle_beacon - too short payload " + "(len=%d)\n", len); + return; + } + + pos = (__le16 *) body; + left = len; + + /* Timestamp (8 octets) */ + pos += 4; left -= 8; + /* Beacon interval (2 octets) */ + beacon_int = le16_to_cpu(*pos); + pos++; left -= 2; + /* Capability information (2 octets) */ + capability = le16_to_cpu(*pos); + pos++; left -= 2; + + if (local->ap->ap_policy != AP_OTHER_AP_EVEN_IBSS && + capability & WLAN_CAPABILITY_IBSS) + return; + + if (left >= 2) { + unsigned int ileft; + unsigned char *u = (unsigned char *) pos; + + if (*u == WLAN_EID_SSID) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft > MAX_SSID_LEN) { + PDEBUG(DEBUG_AP, "SSID: overflow\n"); + return; + } + + if (local->ap->ap_policy == AP_OTHER_AP_SAME_SSID && + (ileft != strlen(local->essid) || + memcmp(local->essid, u, ileft) != 0)) { + /* not our SSID */ + return; + } + + ssid = u; + ssid_len = ileft; + + u += ileft; + left -= ileft; + } + + if (*u == WLAN_EID_SUPP_RATES) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft == 0 || ileft > 8) { + PDEBUG(DEBUG_AP, " - SUPP_RATES len error\n"); + return; + } + + supp_rates = u; + supp_rates_len = ileft; + + u += ileft; + left -= ileft; + } + + if (*u == WLAN_EID_DS_PARAMS) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft != 1) { + PDEBUG(DEBUG_AP, " - DS_PARAMS len error\n"); + return; + } + + channel = *u; + + u += ileft; + left -= ileft; + } + } + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta != NULL) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta == NULL) { + /* add new AP */ + new_sta = 1; + sta = ap_add_sta(local->ap, hdr->addr2); + if (sta == NULL) { + printk(KERN_INFO "prism2: kmalloc failed for AP " + "data structure\n"); + return; + } + hostap_event_new_sta(local->dev, sta); + + /* mark APs authentication and associated for pseudo ad-hoc + * style communication */ + sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; + + if (local->ap->autom_ap_wds) { + hostap_wds_link_oper(local, sta->addr, WDS_ADD); + } + } + + sta->ap = 1; + if (ssid) { + sta->u.ap.ssid_len = ssid_len; + memcpy(sta->u.ap.ssid, ssid, ssid_len); + sta->u.ap.ssid[ssid_len] = '\0'; + } else { + sta->u.ap.ssid_len = 0; + sta->u.ap.ssid[0] = '\0'; + } + sta->u.ap.channel = channel; + sta->rx_packets++; + sta->rx_bytes += len; + sta->u.ap.last_beacon = sta->last_rx = jiffies; + sta->capability = capability; + sta->listen_interval = beacon_int; + + atomic_dec(&sta->users); + + if (new_sta) { + memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); + memcpy(sta->supported_rates, supp_rates, supp_rates_len); + prism2_check_tx_rates(sta); + } +} + +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +/* Called only as a tasklet. */ +static void handle_ap_item(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + struct net_device *dev = local->dev; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + u16 fc, type, stype; + struct ieee80211_hdr *hdr; + + /* FIX: should give skb->len to handler functions and check that the + * buffer is long enough */ + hdr = (struct ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + type = fc & IEEE80211_FCTL_FTYPE; + stype = fc & IEEE80211_FCTL_STYPE; + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (!local->hostapd && type == IEEE80211_FTYPE_DATA) { + PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n"); + + if (!(fc & IEEE80211_FCTL_TODS) || + (fc & IEEE80211_FCTL_FROMDS)) { + if (stype == IEEE80211_STYPE_NULLFUNC) { + /* no ToDS nullfunc seems to be used to check + * AP association; so send reject message to + * speed up re-association */ + ap_handle_dropped_data(local, hdr); + goto done; + } + PDEBUG(DEBUG_AP, " not ToDS frame (fc=0x%04x)\n", + fc); + goto done; + } + + if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)=%pM" + " not own MAC\n", hdr->addr1); + goto done; + } + + if (local->ap->nullfunc_ack && + stype == IEEE80211_STYPE_NULLFUNC) + ap_handle_data_nullfunc(local, hdr); + else + ap_handle_dropped_data(local, hdr); + goto done; + } + + if (type == IEEE80211_FTYPE_MGMT && stype == IEEE80211_STYPE_BEACON) { + handle_beacon(local, skb, rx_stats); + goto done; + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + if (type == IEEE80211_FTYPE_CTL && stype == IEEE80211_STYPE_PSPOLL) { + handle_pspoll(local, hdr, rx_stats); + goto done; + } + + if (local->hostapd) { + PDEBUG(DEBUG_AP, "Unknown frame in AP queue: type=0x%02x " + "subtype=0x%02x\n", type, stype); + goto done; + } + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (type != IEEE80211_FTYPE_MGMT) { + PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n"); + goto done; + } + + if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=%pM" + " not own MAC\n", hdr->addr1); + goto done; + } + + if (!ether_addr_equal(hdr->addr3, dev->dev_addr)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=%pM" + " not own MAC\n", hdr->addr3); + goto done; + } + + switch (stype) { + case IEEE80211_STYPE_ASSOC_REQ: + handle_assoc(local, skb, rx_stats, 0); + break; + case IEEE80211_STYPE_ASSOC_RESP: + PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n"); + break; + case IEEE80211_STYPE_REASSOC_REQ: + handle_assoc(local, skb, rx_stats, 1); + break; + case IEEE80211_STYPE_REASSOC_RESP: + PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n"); + break; + case IEEE80211_STYPE_ATIM: + PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n"); + break; + case IEEE80211_STYPE_DISASSOC: + handle_disassoc(local, skb, rx_stats); + break; + case IEEE80211_STYPE_AUTH: + handle_authen(local, skb, rx_stats); + break; + case IEEE80211_STYPE_DEAUTH: + handle_deauth(local, skb, rx_stats); + break; + default: + PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n", + stype >> 4); + break; + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + done: + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +void hostap_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct hostap_interface *iface; + local_info_t *local; + struct ieee80211_hdr *hdr; + + iface = netdev_priv(dev); + local = iface->local; + + if (skb->len < 16) + goto drop; + + dev->stats.rx_packets++; + + hdr = (struct ieee80211_hdr *) skb->data; + + if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL && + ieee80211_is_beacon(hdr->frame_control)) + goto drop; + + skb->protocol = cpu_to_be16(ETH_P_HOSTAP); + handle_ap_item(local, skb, rx_stats); + return; + + drop: + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +static void schedule_packet_send(local_info_t *local, struct sta_info *sta) +{ + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + struct hostap_80211_rx_status rx_stats; + + if (skb_queue_empty(&sta->tx_buf)) + return; + + skb = dev_alloc_skb(16); + if (skb == NULL) { + printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc " + "failed\n", local->dev->name); + return; + } + + hdr = (struct ieee80211_hdr *) skb_put(skb, 16); + + /* Generate a fake pspoll frame to start packet delivery */ + hdr->frame_control = cpu_to_le16( + IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); + memcpy(hdr->addr1, local->dev->dev_addr, ETH_ALEN); + memcpy(hdr->addr2, sta->addr, ETH_ALEN); + hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14)); + + PDEBUG(DEBUG_PS2, + "%s: Scheduling buffered packet delivery for STA %pM\n", + local->dev->name, sta->addr); + + skb->dev = local->dev; + + memset(&rx_stats, 0, sizeof(rx_stats)); + hostap_rx(local->dev, skb, &rx_stats); +} + + +int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], + struct iw_quality qual[], int buf_size, + int aplist) +{ + struct ap_data *ap = local->ap; + struct list_head *ptr; + int count = 0; + + spin_lock_bh(&ap->sta_table_lock); + + for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list; + ptr = ptr->next) { + struct sta_info *sta = (struct sta_info *) ptr; + + if (aplist && !sta->ap) + continue; + addr[count].sa_family = ARPHRD_ETHER; + memcpy(addr[count].sa_data, sta->addr, ETH_ALEN); + if (sta->last_rx_silence == 0) + qual[count].qual = sta->last_rx_signal < 27 ? + 0 : (sta->last_rx_signal - 27) * 92 / 127; + else + qual[count].qual = sta->last_rx_signal - + sta->last_rx_silence - 35; + qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); + qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); + qual[count].updated = sta->last_rx_updated; + + sta->last_rx_updated = IW_QUAL_DBM; + + count++; + if (count >= buf_size) + break; + } + spin_unlock_bh(&ap->sta_table_lock); + + return count; +} + + +/* Translate our list of Access Points & Stations to a card independent + * format that the Wireless Tools will understand - Jean II */ +int prism2_ap_translate_scan(struct net_device *dev, + struct iw_request_info *info, char *buffer) +{ + struct hostap_interface *iface; + local_info_t *local; + struct ap_data *ap; + struct list_head *ptr; + struct iw_event iwe; + char *current_ev = buffer; + char *end_buf = buffer + IW_SCAN_MAX_DATA; +#if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT) + char buf[64]; +#endif + + iface = netdev_priv(dev); + local = iface->local; + ap = local->ap; + + spin_lock_bh(&ap->sta_table_lock); + + for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list; + ptr = ptr->next) { + struct sta_info *sta = (struct sta_info *) ptr; + + /* First entry *MUST* be the AP MAC address */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN); + iwe.len = IW_EV_ADDR_LEN; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); + + /* Use the mode to indicate if it's a station or + * an Access Point */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + if (sta->ap) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_INFRA; + iwe.len = IW_EV_UINT_LEN; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); + + /* Some quality */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + if (sta->last_rx_silence == 0) + iwe.u.qual.qual = sta->last_rx_signal < 27 ? + 0 : (sta->last_rx_signal - 27) * 92 / 127; + else + iwe.u.qual.qual = sta->last_rx_signal - + sta->last_rx_silence - 35; + iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); + iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); + iwe.u.qual.updated = sta->last_rx_updated; + iwe.len = IW_EV_QUAL_LEN; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (sta->ap) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = sta->u.ap.ssid_len; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, + sta->u.ap.ssid); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (sta->capability & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = + IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, + sta->u.ap.ssid); + + if (sta->u.ap.channel > 0 && + sta->u.ap.channel <= FREQ_COUNT) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = freq_list[sta->u.ap.channel - 1] + * 100000; + iwe.u.freq.e = 1; + current_ev = iwe_stream_add_event( + info, current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "beacon_interval=%d", + sta->listen_interval); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + sta->last_rx_updated = IW_QUAL_DBM; + + /* To be continued, we should make good use of IWEVCUSTOM */ + } + + spin_unlock_bh(&ap->sta_table_lock); + + return current_ev - buffer; +} + + +static int prism2_hostapd_add_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (sta == NULL) { + sta = ap_add_sta(ap, param->sta_addr); + if (sta == NULL) + return -1; + } + + if (!(sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_new_sta(sta->local->dev, sta); + + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->last_rx = jiffies; + sta->aid = param->u.add_sta.aid; + sta->capability = param->u.add_sta.capability; + sta->tx_supp_rates = param->u.add_sta.tx_supp_rates; + if (sta->tx_supp_rates & WLAN_RATE_1M) + sta->supported_rates[0] = 2; + if (sta->tx_supp_rates & WLAN_RATE_2M) + sta->supported_rates[1] = 4; + if (sta->tx_supp_rates & WLAN_RATE_5M5) + sta->supported_rates[2] = 11; + if (sta->tx_supp_rates & WLAN_RATE_11M) + sta->supported_rates[3] = 22; + prism2_check_tx_rates(sta); + atomic_dec(&sta->users); + return 0; +} + + +static int prism2_hostapd_remove_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) { + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + } + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -ENOENT; + + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + ap_free_sta(ap, sta); + + return 0; +} + + +static int prism2_hostapd_get_info_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -ENOENT; + + param->u.get_info_sta.inactive_sec = (jiffies - sta->last_rx) / HZ; + + atomic_dec(&sta->users); + + return 1; +} + + +static int prism2_hostapd_set_flags_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) { + sta->flags |= param->u.set_flags_sta.flags_or; + sta->flags &= param->u.set_flags_sta.flags_and; + } + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -ENOENT; + + return 0; +} + + +static int prism2_hostapd_sta_clear_stats(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + int rate; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) { + sta->rx_packets = sta->tx_packets = 0; + sta->rx_bytes = sta->tx_bytes = 0; + for (rate = 0; rate < WLAN_RATE_COUNT; rate++) { + sta->tx_count[rate] = 0; + sta->rx_count[rate] = 0; + } + } + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -ENOENT; + + return 0; +} + + +int prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param) +{ + switch (param->cmd) { + case PRISM2_HOSTAPD_FLUSH: + ap_control_kickall(ap); + return 0; + case PRISM2_HOSTAPD_ADD_STA: + return prism2_hostapd_add_sta(ap, param); + case PRISM2_HOSTAPD_REMOVE_STA: + return prism2_hostapd_remove_sta(ap, param); + case PRISM2_HOSTAPD_GET_INFO_STA: + return prism2_hostapd_get_info_sta(ap, param); + case PRISM2_HOSTAPD_SET_FLAGS_STA: + return prism2_hostapd_set_flags_sta(ap, param); + case PRISM2_HOSTAPD_STA_CLEAR_STATS: + return prism2_hostapd_sta_clear_stats(ap, param); + default: + printk(KERN_WARNING "prism2_hostapd: unknown cmd=%d\n", + param->cmd); + return -EOPNOTSUPP; + } +} + + +/* Update station info for host-based TX rate control and return current + * TX rate */ +static int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev) +{ + int ret = sta->tx_rate; + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + sta->tx_count[sta->tx_rate_idx]++; + sta->tx_since_last_failure++; + sta->tx_consecutive_exc = 0; + if (sta->tx_since_last_failure >= WLAN_RATE_UPDATE_COUNT && + sta->tx_rate_idx < sta->tx_max_rate) { + /* use next higher rate */ + int old_rate, new_rate; + old_rate = new_rate = sta->tx_rate_idx; + while (new_rate < sta->tx_max_rate) { + new_rate++; + if (ap_tx_rate_ok(new_rate, sta, local)) { + sta->tx_rate_idx = new_rate; + break; + } + } + if (old_rate != sta->tx_rate_idx) { + switch (sta->tx_rate_idx) { + case 0: sta->tx_rate = 10; break; + case 1: sta->tx_rate = 20; break; + case 2: sta->tx_rate = 55; break; + case 3: sta->tx_rate = 110; break; + default: sta->tx_rate = 0; break; + } + PDEBUG(DEBUG_AP, "%s: STA %pM TX rate raised to %d\n", + dev->name, sta->addr, sta->tx_rate); + } + sta->tx_since_last_failure = 0; + } + + return ret; +} + + +/* Called only from software IRQ. Called for each TX frame prior possible + * encryption and transmit. */ +ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx) +{ + struct sta_info *sta = NULL; + struct sk_buff *skb = tx->skb; + int set_tim, ret; + struct ieee80211_hdr *hdr; + struct hostap_skb_tx_data *meta; + + meta = (struct hostap_skb_tx_data *) skb->cb; + ret = AP_TX_CONTINUE; + if (local->ap == NULL || skb->len < 10 || + meta->iface->type == HOSTAP_INTERFACE_STA) + goto out; + + hdr = (struct ieee80211_hdr *) skb->data; + + if (hdr->addr1[0] & 0x01) { + /* broadcast/multicast frame - no AP related processing */ + if (local->ap->num_sta <= 0) + ret = AP_TX_DROP; + goto out; + } + + /* unicast packet - check whether destination STA is associated */ + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr1); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (local->iw_mode == IW_MODE_MASTER && sta == NULL && + !(meta->flags & HOSTAP_TX_FLAGS_WDS) && + meta->iface->type != HOSTAP_INTERFACE_MASTER && + meta->iface->type != HOSTAP_INTERFACE_AP) { +#if 0 + /* This can happen, e.g., when wlan0 is added to a bridge and + * bridging code does not know which port is the correct target + * for a unicast frame. In this case, the packet is send to all + * ports of the bridge. Since this is a valid scenario, do not + * print out any errors here. */ + if (net_ratelimit()) { + printk(KERN_DEBUG "AP: drop packet to non-associated " + "STA %pM\n", hdr->addr1); + } +#endif + local->ap->tx_drop_nonassoc++; + ret = AP_TX_DROP; + goto out; + } + + if (sta == NULL) + goto out; + + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + ret = AP_TX_CONTINUE_NOT_AUTHORIZED; + + /* Set tx_rate if using host-based TX rate control */ + if (!local->fw_tx_rate_control) + local->ap->last_tx_rate = meta->rate = + ap_update_sta_tx_rate(sta, local->dev); + + if (local->iw_mode != IW_MODE_MASTER) + goto out; + + if (!(sta->flags & WLAN_STA_PS)) + goto out; + + if (meta->flags & HOSTAP_TX_FLAGS_ADD_MOREDATA) { + /* indicate to STA that more frames follow */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + + if (meta->flags & HOSTAP_TX_FLAGS_BUFFERED_FRAME) { + /* packet was already buffered and now send due to + * PS poll, so do not rebuffer it */ + goto out; + } + + if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) { + PDEBUG(DEBUG_PS, "%s: No more space in STA (%pM)'s" + "PS mode buffer\n", + local->dev->name, sta->addr); + /* Make sure that TIM is set for the station (it might not be + * after AP wlan hw reset). */ + /* FIX: should fix hw reset to restore bits based on STA + * buffer state.. */ + hostap_set_tim(local, sta->aid, 1); + sta->flags |= WLAN_STA_TIM; + ret = AP_TX_DROP; + goto out; + } + + /* STA in PS mode, buffer frame for later delivery */ + set_tim = skb_queue_empty(&sta->tx_buf); + skb_queue_tail(&sta->tx_buf, skb); + /* FIX: could save RX time to skb and expire buffered frames after + * some time if STA does not poll for them */ + + if (set_tim) { + if (sta->flags & WLAN_STA_TIM) + PDEBUG(DEBUG_PS2, "Re-setting TIM for aid %d\n", + sta->aid); + hostap_set_tim(local, sta->aid, 1); + sta->flags |= WLAN_STA_TIM; + } + + ret = AP_TX_BUFFERED; + + out: + if (sta != NULL) { + if (ret == AP_TX_CONTINUE || + ret == AP_TX_CONTINUE_NOT_AUTHORIZED) { + sta->tx_packets++; + sta->tx_bytes += skb->len; + sta->last_tx = jiffies; + } + + if ((ret == AP_TX_CONTINUE || + ret == AP_TX_CONTINUE_NOT_AUTHORIZED) && + sta->crypt && tx->host_encrypt) { + tx->crypt = sta->crypt; + tx->sta_ptr = sta; /* hostap_handle_sta_release() will + * be called to release sta info + * later */ + } else + atomic_dec(&sta->users); + } + + return ret; +} + + +void hostap_handle_sta_release(void *ptr) +{ + struct sta_info *sta = ptr; + atomic_dec(&sta->users); +} + + +/* Called only as a tasklet (software IRQ) */ +void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb) +{ + struct sta_info *sta; + struct ieee80211_hdr *hdr; + struct hostap_skb_tx_data *meta; + + hdr = (struct ieee80211_hdr *) skb->data; + meta = (struct hostap_skb_tx_data *) skb->cb; + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr1); + if (!sta) { + spin_unlock(&local->ap->sta_table_lock); + PDEBUG(DEBUG_AP, "%s: Could not find STA %pM" + " for this TX error (@%lu)\n", + local->dev->name, hdr->addr1, jiffies); + return; + } + + sta->tx_since_last_failure = 0; + sta->tx_consecutive_exc++; + + if (sta->tx_consecutive_exc >= WLAN_RATE_DECREASE_THRESHOLD && + sta->tx_rate_idx > 0 && meta->rate <= sta->tx_rate) { + /* use next lower rate */ + int old, rate; + old = rate = sta->tx_rate_idx; + while (rate > 0) { + rate--; + if (ap_tx_rate_ok(rate, sta, local)) { + sta->tx_rate_idx = rate; + break; + } + } + if (old != sta->tx_rate_idx) { + switch (sta->tx_rate_idx) { + case 0: sta->tx_rate = 10; break; + case 1: sta->tx_rate = 20; break; + case 2: sta->tx_rate = 55; break; + case 3: sta->tx_rate = 110; break; + default: sta->tx_rate = 0; break; + } + PDEBUG(DEBUG_AP, + "%s: STA %pM TX rate lowered to %d\n", + local->dev->name, sta->addr, sta->tx_rate); + } + sta->tx_consecutive_exc = 0; + } + spin_unlock(&local->ap->sta_table_lock); +} + + +static void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta, + int pwrmgt, int type, int stype) +{ + if (pwrmgt && !(sta->flags & WLAN_STA_PS)) { + sta->flags |= WLAN_STA_PS; + PDEBUG(DEBUG_PS2, "STA %pM changed to use PS " + "mode (type=0x%02X, stype=0x%02X)\n", + sta->addr, type >> 2, stype >> 4); + } else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) { + sta->flags &= ~WLAN_STA_PS; + PDEBUG(DEBUG_PS2, "STA %pM changed to not use " + "PS mode (type=0x%02X, stype=0x%02X)\n", + sta->addr, type >> 2, stype >> 4); + if (type != IEEE80211_FTYPE_CTL || + stype != IEEE80211_STYPE_PSPOLL) + schedule_packet_send(local, sta); + } +} + + +/* Called only as a tasklet (software IRQ). Called for each RX frame to update + * STA power saving state. pwrmgt is a flag from 802.11 frame_control field. */ +int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr) +{ + struct sta_info *sta; + u16 fc; + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (!sta) + return -1; + + fc = le16_to_cpu(hdr->frame_control); + hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM, + fc & IEEE80211_FCTL_FTYPE, + fc & IEEE80211_FCTL_STYPE); + + atomic_dec(&sta->users); + return 0; +} + + +/* Called only as a tasklet (software IRQ). Called for each RX frame after + * getting RX header and payload from hardware. */ +ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, + struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, + int wds) +{ + int ret; + struct sta_info *sta; + u16 fc, type, stype; + struct ieee80211_hdr *hdr; + + if (local->ap == NULL) + return AP_RX_CONTINUE; + + hdr = (struct ieee80211_hdr *) skb->data; + + fc = le16_to_cpu(hdr->frame_control); + type = fc & IEEE80211_FCTL_FTYPE; + stype = fc & IEEE80211_FCTL_STYPE; + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (sta && !(sta->flags & WLAN_STA_AUTHORIZED)) + ret = AP_RX_CONTINUE_NOT_AUTHORIZED; + else + ret = AP_RX_CONTINUE; + + + if (fc & IEEE80211_FCTL_TODS) { + if (!wds && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { + if (local->hostapd) { + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_NON_ASSOC); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + } else { + printk(KERN_DEBUG "%s: dropped received packet" + " from non-associated STA %pM" + " (type=0x%02x, subtype=0x%02x)\n", + dev->name, hdr->addr2, + type >> 2, stype >> 4); + hostap_rx(dev, skb, rx_stats); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + } + ret = AP_RX_EXIT; + goto out; + } + } else if (fc & IEEE80211_FCTL_FROMDS) { + if (!wds) { + /* FromDS frame - not for us; probably + * broadcast/multicast in another BSS - drop */ + if (ether_addr_equal(hdr->addr1, dev->dev_addr)) { + printk(KERN_DEBUG "Odd.. FromDS packet " + "received with own BSSID\n"); + hostap_dump_rx_80211(dev->name, skb, rx_stats); + } + ret = AP_RX_DROP; + goto out; + } + } else if (stype == IEEE80211_STYPE_NULLFUNC && sta == NULL && + ether_addr_equal(hdr->addr1, dev->dev_addr)) { + + if (local->hostapd) { + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_NON_ASSOC); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + } else { + /* At least Lucent f/w seems to send data::nullfunc + * frames with no ToDS flag when the current AP returns + * after being unavailable for some time. Speed up + * re-association by informing the station about it not + * being associated. */ + printk(KERN_DEBUG "%s: rejected received nullfunc frame" + " without ToDS from not associated STA %pM\n", + dev->name, hdr->addr2); + hostap_rx(dev, skb, rx_stats); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + } + ret = AP_RX_EXIT; + goto out; + } else if (stype == IEEE80211_STYPE_NULLFUNC) { + /* At least Lucent cards seem to send periodic nullfunc + * frames with ToDS. Let these through to update SQ + * stats and PS state. Nullfunc frames do not contain + * any data and they will be dropped below. */ + } else { + /* If BSSID (Addr3) is foreign, this frame is a normal + * broadcast frame from an IBSS network. Drop it silently. + * If BSSID is own, report the dropping of this frame. */ + if (ether_addr_equal(hdr->addr3, dev->dev_addr)) { + printk(KERN_DEBUG "%s: dropped received packet from %pM" + " with no ToDS flag " + "(type=0x%02x, subtype=0x%02x)\n", dev->name, + hdr->addr2, type >> 2, stype >> 4); + hostap_dump_rx_80211(dev->name, skb, rx_stats); + } + ret = AP_RX_DROP; + goto out; + } + + if (sta) { + hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM, + type, stype); + + sta->rx_packets++; + sta->rx_bytes += skb->len; + sta->last_rx = jiffies; + } + + if (local->ap->nullfunc_ack && stype == IEEE80211_STYPE_NULLFUNC && + fc & IEEE80211_FCTL_TODS) { + if (local->hostapd) { + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_NULLFUNC_ACK); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + } else { + /* some STA f/w's seem to require control::ACK frame + * for data::nullfunc, but Prism2 f/w 0.8.0 (at least + * from Compaq) does not send this.. Try to generate + * ACK for these frames from the host driver to make + * power saving work with, e.g., Lucent WaveLAN f/w */ + hostap_rx(dev, skb, rx_stats); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + } + ret = AP_RX_EXIT; + goto out; + } + + out: + if (sta) + atomic_dec(&sta->users); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_handle_sta_crypto(local_info_t *local, + struct ieee80211_hdr *hdr, + struct lib80211_crypt_data **crypt, + void **sta_ptr) +{ + struct sta_info *sta; + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (!sta) + return -1; + + if (sta->crypt) { + *crypt = sta->crypt; + *sta_ptr = sta; + /* hostap_handle_sta_release() will be called to release STA + * info */ + } else + atomic_dec(&sta->users); + + return 0; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr) +{ + struct sta_info *sta; + int ret = 0; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, sta_addr); + if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap) + ret = 1; + spin_unlock(&ap->sta_table_lock); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr) +{ + struct sta_info *sta; + int ret = 0; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, sta_addr); + if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap && + ((sta->flags & WLAN_STA_AUTHORIZED) || + ap->local->ieee_802_1x == 0)) + ret = 1; + spin_unlock(&ap->sta_table_lock); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_add_sta(struct ap_data *ap, u8 *sta_addr) +{ + struct sta_info *sta; + int ret = 1; + + if (!ap) + return -1; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, sta_addr); + if (sta) + ret = 0; + spin_unlock(&ap->sta_table_lock); + + if (ret == 1) { + sta = ap_add_sta(ap, sta_addr); + if (!sta) + return -1; + sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->ap = 1; + memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); + /* No way of knowing which rates are supported since we did not + * get supported rates element from beacon/assoc req. Assume + * that remote end supports all 802.11b rates. */ + sta->supported_rates[0] = 0x82; + sta->supported_rates[1] = 0x84; + sta->supported_rates[2] = 0x0b; + sta->supported_rates[3] = 0x16; + sta->tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M | + WLAN_RATE_5M5 | WLAN_RATE_11M; + sta->tx_rate = 110; + sta->tx_max_rate = sta->tx_rate_idx = 3; + } + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_update_rx_stats(struct ap_data *ap, + struct ieee80211_hdr *hdr, + struct hostap_80211_rx_status *rx_stats) +{ + struct sta_info *sta; + + if (!ap) + return -1; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr2); + if (sta) { + sta->last_rx_silence = rx_stats->noise; + sta->last_rx_signal = rx_stats->signal; + sta->last_rx_rate = rx_stats->rate; + sta->last_rx_updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + if (rx_stats->rate == 10) + sta->rx_count[0]++; + else if (rx_stats->rate == 20) + sta->rx_count[1]++; + else if (rx_stats->rate == 55) + sta->rx_count[2]++; + else if (rx_stats->rate == 110) + sta->rx_count[3]++; + } + spin_unlock(&ap->sta_table_lock); + + return sta ? 0 : -1; +} + + +void hostap_update_rates(local_info_t *local) +{ + struct sta_info *sta; + struct ap_data *ap = local->ap; + + if (!ap) + return; + + spin_lock_bh(&ap->sta_table_lock); + list_for_each_entry(sta, &ap->sta_list, list) { + prism2_check_tx_rates(sta); + } + spin_unlock_bh(&ap->sta_table_lock); +} + + +void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, + struct lib80211_crypt_data ***crypt) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta && permanent) + sta = ap_add_sta(ap, addr); + + if (!sta) + return NULL; + + if (permanent) + sta->flags |= WLAN_STA_PERM; + + *crypt = &sta->crypt; + + return sta; +} + + +void hostap_add_wds_links(local_info_t *local) +{ + struct ap_data *ap = local->ap; + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + list_for_each_entry(sta, &ap->sta_list, list) { + if (sta->ap) + hostap_wds_link_oper(local, sta->addr, WDS_ADD); + } + spin_unlock_bh(&ap->sta_table_lock); + + schedule_work(&local->ap->wds_oper_queue); +} + + +void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type) +{ + struct wds_oper_data *entry; + + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return; + memcpy(entry->addr, addr, ETH_ALEN); + entry->type = type; + spin_lock_bh(&local->lock); + entry->next = local->ap->wds_oper_entries; + local->ap->wds_oper_entries = entry; + spin_unlock_bh(&local->lock); + + schedule_work(&local->ap->wds_oper_queue); +} + + +EXPORT_SYMBOL(hostap_init_data); +EXPORT_SYMBOL(hostap_init_ap_proc); +EXPORT_SYMBOL(hostap_free_data); +EXPORT_SYMBOL(hostap_check_sta_fw_version); +EXPORT_SYMBOL(hostap_handle_sta_tx_exc); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ diff --git a/drivers/net/wireless/intersil/hostap/hostap_ap.h b/drivers/net/wireless/intersil/hostap/hostap_ap.h new file mode 100644 index 000000000000..334e2d0b8e11 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_ap.h @@ -0,0 +1,263 @@ +#ifndef HOSTAP_AP_H +#define HOSTAP_AP_H + +#include "hostap_80211.h" + +/* AP data structures for STAs */ + +/* maximum number of frames to buffer per STA */ +#define STA_MAX_TX_BUFFER 32 + +/* STA flags */ +#define WLAN_STA_AUTH BIT(0) +#define WLAN_STA_ASSOC BIT(1) +#define WLAN_STA_PS BIT(2) +#define WLAN_STA_TIM BIT(3) /* TIM bit is on for PS stations */ +#define WLAN_STA_PERM BIT(4) /* permanent; do not remove entry on expiration */ +#define WLAN_STA_AUTHORIZED BIT(5) /* If 802.1X is used, this flag is + * controlling whether STA is authorized to + * send and receive non-IEEE 802.1X frames + */ +#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ + +#define WLAN_RATE_1M BIT(0) +#define WLAN_RATE_2M BIT(1) +#define WLAN_RATE_5M5 BIT(2) +#define WLAN_RATE_11M BIT(3) +#define WLAN_RATE_COUNT 4 + +/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8, + * but some pre-standard IEEE 802.11g products use longer elements. */ +#define WLAN_SUPP_RATES_MAX 32 + +/* Try to increase TX rate after # successfully sent consecutive packets */ +#define WLAN_RATE_UPDATE_COUNT 50 + +/* Decrease TX rate after # consecutive dropped packets */ +#define WLAN_RATE_DECREASE_THRESHOLD 2 + +struct sta_info { + struct list_head list; + struct sta_info *hnext; /* next entry in hash table list */ + atomic_t users; /* number of users (do not remove if > 0) */ + struct proc_dir_entry *proc; + + u8 addr[6]; + u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ + u32 flags; + u16 capability; + u16 listen_interval; /* or beacon_int for APs */ + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + + unsigned long last_auth; + unsigned long last_assoc; + unsigned long last_rx; + unsigned long last_tx; + unsigned long rx_packets, tx_packets; + unsigned long rx_bytes, tx_bytes; + struct sk_buff_head tx_buf; + /* FIX: timeout buffers with an expiry time somehow derived from + * listen_interval */ + + s8 last_rx_silence; /* Noise in dBm */ + s8 last_rx_signal; /* Signal strength in dBm */ + u8 last_rx_rate; /* TX rate in 0.1 Mbps */ + u8 last_rx_updated; /* IWSPY's struct iw_quality::updated */ + + u8 tx_supp_rates; /* bit field of supported TX rates */ + u8 tx_rate; /* current TX rate (in 0.1 Mbps) */ + u8 tx_rate_idx; /* current TX rate (WLAN_RATE_*) */ + u8 tx_max_rate; /* max TX rate (WLAN_RATE_*) */ + u32 tx_count[WLAN_RATE_COUNT]; /* number of frames sent (per rate) */ + u32 rx_count[WLAN_RATE_COUNT]; /* number of frames received (per rate) + */ + u32 tx_since_last_failure; + u32 tx_consecutive_exc; + + struct lib80211_crypt_data *crypt; + + int ap; /* whether this station is an AP */ + + local_info_t *local; + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + union { + struct { + char *challenge; /* shared key authentication + * challenge */ + } sta; + struct { + int ssid_len; + unsigned char ssid[MAX_SSID_LEN + 1]; /* AP's ssid */ + int channel; + unsigned long last_beacon; /* last RX beacon time */ + } ap; + } u; + + struct timer_list timer; + enum { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH } timeout_next; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ +}; + + +#define MAX_STA_COUNT 1024 + +/* Maximum number of AIDs to use for STAs; must be 2007 or lower + * (8802.11 limitation) */ +#define MAX_AID_TABLE_SIZE 128 + +#define STA_HASH_SIZE 256 +#define STA_HASH(sta) (sta[5]) + + +/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY_SEC + * has passed since last received frame from the station, a nullfunc data + * frame is sent to the station. If this frame is not acknowledged and no other + * frames have been received, the station will be disassociated after + * AP_DISASSOC_DELAY. Similarly, a the station will be deauthenticated after + * AP_DEAUTH_DELAY. AP_TIMEOUT_RESOLUTION is the resolution that is used with + * max inactivity timer. */ +#define AP_MAX_INACTIVITY_SEC (5 * 60) +#define AP_DISASSOC_DELAY (HZ) +#define AP_DEAUTH_DELAY (HZ) + +/* ap_policy: whether to accept frames to/from other APs/IBSS */ +typedef enum { + AP_OTHER_AP_SKIP_ALL = 0, + AP_OTHER_AP_SAME_SSID = 1, + AP_OTHER_AP_ALL = 2, + AP_OTHER_AP_EVEN_IBSS = 3 +} ap_policy_enum; + +#define PRISM2_AUTH_OPEN BIT(0) +#define PRISM2_AUTH_SHARED_KEY BIT(1) + + +/* MAC address-based restrictions */ +struct mac_entry { + struct list_head list; + u8 addr[6]; +}; + +struct mac_restrictions { + enum { MAC_POLICY_OPEN = 0, MAC_POLICY_ALLOW, MAC_POLICY_DENY } policy; + unsigned int entries; + struct list_head mac_list; + spinlock_t lock; +}; + + +struct add_sta_proc_data { + u8 addr[ETH_ALEN]; + struct add_sta_proc_data *next; +}; + + +typedef enum { WDS_ADD, WDS_DEL } wds_oper_type; +struct wds_oper_data { + wds_oper_type type; + u8 addr[ETH_ALEN]; + struct wds_oper_data *next; +}; + + +struct ap_data { + int initialized; /* whether ap_data has been initialized */ + local_info_t *local; + int bridge_packets; /* send packet to associated STAs directly to the + * wireless media instead of higher layers in the + * kernel */ + unsigned int bridged_unicast; /* number of unicast frames bridged on + * wireless media */ + unsigned int bridged_multicast; /* number of non-unicast frames + * bridged on wireless media */ + unsigned int tx_drop_nonassoc; /* number of unicast TX packets dropped + * because they were to an address that + * was not associated */ + int nullfunc_ack; /* use workaround for nullfunc frame ACKs */ + + spinlock_t sta_table_lock; + int num_sta; /* number of entries in sta_list */ + struct list_head sta_list; /* STA info list head */ + struct sta_info *sta_hash[STA_HASH_SIZE]; + + struct proc_dir_entry *proc; + + ap_policy_enum ap_policy; + unsigned int max_inactivity; + int autom_ap_wds; + + struct mac_restrictions mac_restrictions; /* MAC-based auth */ + int last_tx_rate; + + struct work_struct add_sta_proc_queue; + struct add_sta_proc_data *add_sta_proc_entries; + + struct work_struct wds_oper_queue; + struct wds_oper_data *wds_oper_entries; + + u16 tx_callback_idx; + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + /* pointers to STA info; based on allocated AID or NULL if AID free + * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1 + * and so on + */ + struct sta_info *sta_aid[MAX_AID_TABLE_SIZE]; + + u16 tx_callback_auth, tx_callback_assoc, tx_callback_poll; + + /* WEP operations for generating challenges to be used with shared key + * authentication */ + struct lib80211_crypto_ops *crypt; + void *crypt_priv; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ +}; + + +void hostap_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats); +void hostap_init_data(local_info_t *local); +void hostap_init_ap_proc(local_info_t *local); +void hostap_free_data(struct ap_data *ap); +void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver); + +typedef enum { + AP_TX_CONTINUE, AP_TX_DROP, AP_TX_RETRY, AP_TX_BUFFERED, + AP_TX_CONTINUE_NOT_AUTHORIZED +} ap_tx_ret; +struct hostap_tx_data { + struct sk_buff *skb; + int host_encrypt; + struct lib80211_crypt_data *crypt; + void *sta_ptr; +}; +ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx); +void hostap_handle_sta_release(void *ptr); +void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb); +int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr); +typedef enum { + AP_RX_CONTINUE, AP_RX_DROP, AP_RX_EXIT, AP_RX_CONTINUE_NOT_AUTHORIZED +} ap_rx_ret; +ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, + struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, + int wds); +int hostap_handle_sta_crypto(local_info_t *local, struct ieee80211_hdr *hdr, + struct lib80211_crypt_data **crypt, + void **sta_ptr); +int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr); +int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr); +int hostap_add_sta(struct ap_data *ap, u8 *sta_addr); +int hostap_update_rx_stats(struct ap_data *ap, struct ieee80211_hdr *hdr, + struct hostap_80211_rx_status *rx_stats); +void hostap_update_rates(local_info_t *local); +void hostap_add_wds_links(local_info_t *local); +void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap, + int resend); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + +#endif /* HOSTAP_AP_H */ diff --git a/drivers/net/wireless/intersil/hostap/hostap_common.h b/drivers/net/wireless/intersil/hostap/hostap_common.h new file mode 100644 index 000000000000..4230102ac9e4 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_common.h @@ -0,0 +1,419 @@ +#ifndef HOSTAP_COMMON_H +#define HOSTAP_COMMON_H + +#include +#include + +/* IEEE 802.11 defines */ + +/* HFA384X Configuration RIDs */ +#define HFA384X_RID_CNFPORTTYPE 0xFC00 +#define HFA384X_RID_CNFOWNMACADDR 0xFC01 +#define HFA384X_RID_CNFDESIREDSSID 0xFC02 +#define HFA384X_RID_CNFOWNCHANNEL 0xFC03 +#define HFA384X_RID_CNFOWNSSID 0xFC04 +#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05 +#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06 +#define HFA384X_RID_CNFMAXDATALEN 0xFC07 +#define HFA384X_RID_CNFWDSADDRESS 0xFC08 +#define HFA384X_RID_CNFPMENABLED 0xFC09 +#define HFA384X_RID_CNFPMEPS 0xFC0A +#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B +#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C +#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D +#define HFA384X_RID_CNFOWNNAME 0xFC0E +#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10 +#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */ +#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */ +#define HFA384X_RID_UNKNOWN1 0xFC20 +#define HFA384X_RID_UNKNOWN2 0xFC21 +#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23 +#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24 +#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25 +#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26 +#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27 +#define HFA384X_RID_CNFWEPFLAGS 0xFC28 +#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 +#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A +#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */ +#define HFA384X_RID_CNFTXCONTROL 0xFC2C +#define HFA384X_RID_CNFROAMINGMODE 0xFC2D +#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */ +#define HFA384X_RID_CNFRCVCRCERROR 0xFC30 +#define HFA384X_RID_CNFMMLIFE 0xFC31 +#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32 +#define HFA384X_RID_CNFBEACONINT 0xFC33 +#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */ +#define HFA384X_RID_CNFSTAPCFINFO 0xFC35 +#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37 +#define HFA384X_RID_CNFTIMCTRL 0xFC40 +#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */ +#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */ +#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */ +#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0; + * write only */ +#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_GROUPADDRESSES 0xFC80 +#define HFA384X_RID_CREATEIBSS 0xFC81 +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82 +#define HFA384X_RID_RTSTHRESHOLD 0xFC83 +#define HFA384X_RID_TXRATECONTROL 0xFC84 +#define HFA384X_RID_PROMISCUOUSMODE 0xFC85 +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */ +#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0 +#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 +#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 +#define HFA384X_RID_CNFBASICRATES 0xFCB3 +#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4 +#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */ +#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */ +#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */ +#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */ +#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */ +#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */ +#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */ +#define HFA384X_RID_TICKTIME 0xFCE0 +#define HFA384X_RID_SCANREQUEST 0xFCE1 +#define HFA384X_RID_JOINREQUEST 0xFCE2 +#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */ +#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */ +#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */ + +/* HFA384X Information RIDs */ +#define HFA384X_RID_MAXLOADTIME 0xFD00 +#define HFA384X_RID_DOWNLOADBUFFER 0xFD01 +#define HFA384X_RID_PRIID 0xFD02 +#define HFA384X_RID_PRISUPRANGE 0xFD03 +#define HFA384X_RID_CFIACTRANGES 0xFD04 +#define HFA384X_RID_NICSERNUM 0xFD0A +#define HFA384X_RID_NICID 0xFD0B +#define HFA384X_RID_MFISUPRANGE 0xFD0C +#define HFA384X_RID_CFISUPRANGE 0xFD0D +#define HFA384X_RID_CHANNELLIST 0xFD10 +#define HFA384X_RID_REGULATORYDOMAINS 0xFD11 +#define HFA384X_RID_TEMPTYPE 0xFD12 +#define HFA384X_RID_CIS 0xFD13 +#define HFA384X_RID_STAID 0xFD20 +#define HFA384X_RID_STASUPRANGE 0xFD21 +#define HFA384X_RID_MFIACTRANGES 0xFD22 +#define HFA384X_RID_CFIACTRANGES2 0xFD23 +#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1; + * only Prism2.5(?) */ +#define HFA384X_RID_PORTSTATUS 0xFD40 +#define HFA384X_RID_CURRENTSSID 0xFD41 +#define HFA384X_RID_CURRENTBSSID 0xFD42 +#define HFA384X_RID_COMMSQUALITY 0xFD43 +#define HFA384X_RID_CURRENTTXRATE 0xFD44 +#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45 +#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46 +#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47 +#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48 +#define HFA384X_RID_LONGRETRYLIMIT 0xFD49 +#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A +#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B +#define HFA384X_RID_CFPOLLABLE 0xFD4C +#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D +#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F +#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */ +#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */ +#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */ +#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */ +#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */ +#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */ +#define HFA384X_RID_PHYTYPE 0xFDC0 +#define HFA384X_RID_CURRENTCHANNEL 0xFDC1 +#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2 +#define HFA384X_RID_CCAMODE 0xFDC3 +#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6 +#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */ +#define HFA384X_RID_BUILDSEQ 0xFFFE +#define HFA384X_RID_FWID 0xFFFF + + +struct hfa384x_comp_ident +{ + __le16 id; + __le16 variant; + __le16 major; + __le16 minor; +} __packed; + +#define HFA384X_COMP_ID_PRI 0x15 +#define HFA384X_COMP_ID_STA 0x1f +#define HFA384X_COMP_ID_FW_AP 0x14b + +struct hfa384x_sup_range +{ + __le16 role; + __le16 id; + __le16 variant; + __le16 bottom; + __le16 top; +} __packed; + + +struct hfa384x_build_id +{ + __le16 pri_seq; + __le16 sec_seq; +} __packed; + +/* FD01 - Download Buffer */ +struct hfa384x_rid_download_buffer +{ + __le16 page; + __le16 offset; + __le16 length; +} __packed; + +/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */ +struct hfa384x_comms_quality { + __le16 comm_qual; /* 0 .. 92 */ + __le16 signal_level; /* 27 .. 154 */ + __le16 noise_level; /* 27 .. 154 */ +} __packed; + + +/* netdevice private ioctls (used, e.g., with iwpriv from user space) */ + +/* New wireless extensions API - SET/GET convention (even ioctl numbers are + * root only) + */ +#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) +#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) +#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) +#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) +#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) +#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) +#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) +#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) +#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) +#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) +#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) +#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) +#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) +#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) + +/* following are not in SIOCGIWPRIV list; check permission in the driver code + */ +#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) +#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) + + +/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ +enum { + /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_TXRATECTRL = 2, + PRISM2_PARAM_BEACON_INT = 3, + PRISM2_PARAM_PSEUDO_IBSS = 4, + PRISM2_PARAM_ALC = 5, + /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_DUMP = 7, + PRISM2_PARAM_OTHER_AP_POLICY = 8, + PRISM2_PARAM_AP_MAX_INACTIVITY = 9, + PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, + PRISM2_PARAM_DTIM_PERIOD = 11, + PRISM2_PARAM_AP_NULLFUNC_ACK = 12, + PRISM2_PARAM_MAX_WDS = 13, + PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, + PRISM2_PARAM_AP_AUTH_ALGS = 15, + PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, + PRISM2_PARAM_HOST_ENCRYPT = 17, + PRISM2_PARAM_HOST_DECRYPT = 18, + /* PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, REMOVED 2005-08-14 */ + /* PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, REMOVED 2005-08-14 */ + PRISM2_PARAM_HOST_ROAMING = 21, + PRISM2_PARAM_BCRX_STA_KEY = 22, + PRISM2_PARAM_IEEE_802_1X = 23, + PRISM2_PARAM_ANTSEL_TX = 24, + PRISM2_PARAM_ANTSEL_RX = 25, + PRISM2_PARAM_MONITOR_TYPE = 26, + PRISM2_PARAM_WDS_TYPE = 27, + PRISM2_PARAM_HOSTSCAN = 28, + PRISM2_PARAM_AP_SCAN = 29, + PRISM2_PARAM_ENH_SEC = 30, + PRISM2_PARAM_IO_DEBUG = 31, + PRISM2_PARAM_BASIC_RATES = 32, + PRISM2_PARAM_OPER_RATES = 33, + PRISM2_PARAM_HOSTAPD = 34, + PRISM2_PARAM_HOSTAPD_STA = 35, + PRISM2_PARAM_WPA = 36, + PRISM2_PARAM_PRIVACY_INVOKED = 37, + PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, + PRISM2_PARAM_DROP_UNENCRYPTED = 39, + PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, +}; + +enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, + HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; + + +/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ +enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, + AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, + AP_MAC_CMD_KICKALL = 4 }; + + +/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ +enum { + PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, + /* Note! Old versions of prism2_srec have a fatal error in CRC-16 + * calculation, which will corrupt all non-volatile downloads. + * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to + * prevent use of old versions of prism2_srec for non-volatile + * download. */ + PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, + PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, + /* Persistent versions of volatile download commands (keep firmware + * data in memory and automatically re-download after hw_reset */ + PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, + PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, +}; + +struct prism2_download_param { + u32 dl_cmd; + u32 start_addr; + u32 num_areas; + struct prism2_download_area { + u32 addr; /* wlan card address */ + u32 len; + void __user *ptr; /* pointer to data in user space */ + } data[0]; +}; + +#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 +#define PRISM2_MAX_DOWNLOAD_LEN 262144 + + +/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ +enum { + PRISM2_HOSTAPD_FLUSH = 1, + PRISM2_HOSTAPD_ADD_STA = 2, + PRISM2_HOSTAPD_REMOVE_STA = 3, + PRISM2_HOSTAPD_GET_INFO_STA = 4, + /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ + PRISM2_SET_ENCRYPTION = 6, + PRISM2_GET_ENCRYPTION = 7, + PRISM2_HOSTAPD_SET_FLAGS_STA = 8, + PRISM2_HOSTAPD_GET_RID = 9, + PRISM2_HOSTAPD_SET_RID = 10, + PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, + PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, + PRISM2_HOSTAPD_MLME = 13, + PRISM2_HOSTAPD_SCAN_REQ = 14, + PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, +}; + +#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 +#define PRISM2_HOSTAPD_RID_HDR_LEN \ +offsetof(struct prism2_hostapd_param, u.rid.data) +#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ +offsetof(struct prism2_hostapd_param, u.generic_elem.data) + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u16 aid; + u16 capability; + u8 tx_supp_rates; + } add_sta; + struct { + u32 inactive_sec; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 rid; + u16 len; + u8 data[0]; + } rid; + struct { + u8 len; + u8 data[0]; + } generic_elem; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 ssid_len; + u8 ssid[32]; + } scan_req; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) +#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) + +#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 +#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 +#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 +#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 + + +#endif /* HOSTAP_COMMON_H */ diff --git a/drivers/net/wireless/intersil/hostap/hostap_config.h b/drivers/net/wireless/intersil/hostap/hostap_config.h new file mode 100644 index 000000000000..2c8f71f0ed45 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_config.h @@ -0,0 +1,48 @@ +#ifndef HOSTAP_CONFIG_H +#define HOSTAP_CONFIG_H + +/* In the previous versions of Host AP driver, support for user space version + * of IEEE 802.11 management (hostapd) used to be disabled in the default + * configuration. From now on, support for hostapd is always included and it is + * possible to disable kernel driver version of IEEE 802.11 management with a + * separate define, PRISM2_NO_KERNEL_IEEE80211_MGMT. */ +/* #define PRISM2_NO_KERNEL_IEEE80211_MGMT */ + +/* Maximum number of events handler per one interrupt */ +#define PRISM2_MAX_INTERRUPT_EVENTS 20 + +/* Include code for downloading firmware images into volatile RAM. */ +#define PRISM2_DOWNLOAD_SUPPORT + +/* Allow kernel configuration to enable download support. */ +#if !defined(PRISM2_DOWNLOAD_SUPPORT) && defined(CONFIG_HOSTAP_FIRMWARE) +#define PRISM2_DOWNLOAD_SUPPORT +#endif + +/* Allow kernel configuration to enable non-volatile download support. */ +#ifdef CONFIG_HOSTAP_FIRMWARE_NVRAM +#define PRISM2_NON_VOLATILE_DOWNLOAD +#endif + +/* Save low-level I/O for debugging. This should not be enabled in normal use. + */ +/* #define PRISM2_IO_DEBUG */ + +/* Following defines can be used to remove unneeded parts of the driver, e.g., + * to limit the size of the kernel module. Definitions can be added here in + * hostap_config.h or they can be added to make command with ccflags-y, + * e.g., + * 'make pccard ccflags-y="-DPRISM2_NO_DEBUG -DPRISM2_NO_PROCFS_DEBUG"' + */ + +/* Do not include debug messages into the driver */ +/* #define PRISM2_NO_DEBUG */ + +/* Do not include /proc/net/prism2/wlan#/{registers,debug} */ +/* #define PRISM2_NO_PROCFS_DEBUG */ + +/* Do not include station functionality (i.e., allow only Master (Host AP) mode + */ +/* #define PRISM2_NO_STATION_MODES */ + +#endif /* HOSTAP_CONFIG_H */ diff --git a/drivers/net/wireless/intersil/hostap/hostap_cs.c b/drivers/net/wireless/intersil/hostap/hostap_cs.c new file mode 100644 index 000000000000..50033aa7c7d5 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_cs.c @@ -0,0 +1,708 @@ +#define PRISM2_PCCARD + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "hostap_wlan.h" + + +static char *dev_info = "hostap_cs"; + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN " + "cards (PC Card)."); +MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PC Card)"); +MODULE_LICENSE("GPL"); + + +static int ignore_cis_vcc; +module_param(ignore_cis_vcc, int, 0444); +MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); + + +/* struct local_info::hw_priv */ +struct hostap_cs_priv { + struct pcmcia_device *link; + int sandisk_connectplus; +}; + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); + outb(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + u8 v; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + v = inb(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); + outw(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + u16 v; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + v = inw(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outsw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc); + outsw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline void hfa384x_insw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc); + insw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) +#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) +#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) +#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) +#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc)) +#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc)) + +#else /* PRISM2_IO_DEBUG */ + +#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a)) +#define HFA384X_INB(a) inb(dev->base_addr + (a)) +#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a)) +#define HFA384X_INW(a) inw(dev->base_addr + (a)) +#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc) +#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc) + +#endif /* PRISM2_IO_DEBUG */ + + +static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, + int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_INSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + *((char *) pos) = HFA384X_INB(d_off); + + return 0; +} + + +static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_OUTSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + HFA384X_OUTB(*((char *) pos), d_off); + + return 0; +} + + +/* FIX: This might change at some point.. */ +#include "hostap_hw.c" + + + +static void prism2_detach(struct pcmcia_device *p_dev); +static void prism2_release(u_long arg); +static int prism2_config(struct pcmcia_device *link); + + +static int prism2_pccard_card_present(local_info_t *local) +{ + struct hostap_cs_priv *hw_priv = local->hw_priv; + if (hw_priv != NULL && hw_priv->link != NULL && pcmcia_dev_present(hw_priv->link)) + return 1; + return 0; +} + + +/* + * SanDisk CompactFlash WLAN Flashcard - Product Manual v1.0 + * Document No. 20-10-00058, January 2004 + * http://www.sandisk.com/pdf/industrial/ProdManualCFWLANv1.0.pdf + */ +#define SANDISK_WLAN_ACTIVATION_OFF 0x40 +#define SANDISK_HCR_OFF 0x42 + + +static void sandisk_set_iobase(local_info_t *local) +{ + int res; + struct hostap_cs_priv *hw_priv = local->hw_priv; + + res = pcmcia_write_config_byte(hw_priv->link, 0x10, + hw_priv->link->resource[0]->start & 0x00ff); + if (res != 0) { + printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -" + " res=%d\n", res); + } + udelay(10); + + res = pcmcia_write_config_byte(hw_priv->link, 0x12, + (hw_priv->link->resource[0]->start >> 8) & 0x00ff); + if (res != 0) { + printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -" + " res=%d\n", res); + } +} + + +static void sandisk_write_hcr(local_info_t *local, int hcr) +{ + struct net_device *dev = local->dev; + int i; + + HFA384X_OUTB(0x80, SANDISK_WLAN_ACTIVATION_OFF); + udelay(50); + for (i = 0; i < 10; i++) { + HFA384X_OUTB(hcr, SANDISK_HCR_OFF); + } + udelay(55); + HFA384X_OUTB(0x45, SANDISK_WLAN_ACTIVATION_OFF); +} + + +static int sandisk_enable_wireless(struct net_device *dev) +{ + int res, ret = 0; + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + struct hostap_cs_priv *hw_priv = local->hw_priv; + + if (resource_size(hw_priv->link->resource[0]) < 0x42) { + /* Not enough ports to be SanDisk multi-function card */ + ret = -ENODEV; + goto done; + } + + if (hw_priv->link->manf_id != 0xd601 || hw_priv->link->card_id != 0x0101) { + /* No SanDisk manfid found */ + ret = -ENODEV; + goto done; + } + + if (hw_priv->link->socket->functions < 2) { + /* No multi-function links found */ + ret = -ENODEV; + goto done; + } + + printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected" + " - using vendor-specific initialization\n", dev->name); + hw_priv->sandisk_connectplus = 1; + + res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, + COR_SOFT_RESET); + if (res != 0) { + printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", + dev->name, res); + goto done; + } + mdelay(5); + + /* + * Do not enable interrupts here to avoid some bogus events. Interrupts + * will be enabled during the first cor_sreset call. + */ + res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, + (COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | + COR_FUNC_ENA)); + if (res != 0) { + printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", + dev->name, res); + goto done; + } + mdelay(5); + + sandisk_set_iobase(local); + + HFA384X_OUTB(0xc5, SANDISK_WLAN_ACTIVATION_OFF); + udelay(10); + HFA384X_OUTB(0x4b, SANDISK_WLAN_ACTIVATION_OFF); + udelay(10); + +done: + return ret; +} + + +static void prism2_pccard_cor_sreset(local_info_t *local) +{ + int res; + u8 val; + struct hostap_cs_priv *hw_priv = local->hw_priv; + + if (!prism2_pccard_card_present(local)) + return; + + res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &val); + if (res != 0) { + printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n", + res); + return; + } + printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n", + val); + + val |= COR_SOFT_RESET; + res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val); + if (res != 0) { + printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n", + res); + return; + } + + mdelay(hw_priv->sandisk_connectplus ? 5 : 2); + + val &= ~COR_SOFT_RESET; + if (hw_priv->sandisk_connectplus) + val |= COR_IREQ_ENA; + res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val); + if (res != 0) { + printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n", + res); + return; + } + + mdelay(hw_priv->sandisk_connectplus ? 5 : 2); + + if (hw_priv->sandisk_connectplus) + sandisk_set_iobase(local); +} + + +static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) +{ + int res; + u8 old_cor; + struct hostap_cs_priv *hw_priv = local->hw_priv; + + if (!prism2_pccard_card_present(local)) + return; + + if (hw_priv->sandisk_connectplus) { + sandisk_write_hcr(local, hcr); + return; + } + + res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &old_cor); + if (res != 0) { + printk(KERN_DEBUG "%s failed 1 (%d)\n", __func__, res); + return; + } + printk(KERN_DEBUG "%s: original COR %02x\n", __func__, old_cor); + + res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, + old_cor | COR_SOFT_RESET); + if (res != 0) { + printk(KERN_DEBUG "%s failed 2 (%d)\n", __func__, res); + return; + } + + mdelay(10); + + /* Setup Genesis mode */ + res = pcmcia_write_config_byte(hw_priv->link, CISREG_CCSR, hcr); + if (res != 0) { + printk(KERN_DEBUG "%s failed 3 (%d)\n", __func__, res); + return; + } + mdelay(10); + + res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, + old_cor & ~COR_SOFT_RESET); + if (res != 0) { + printk(KERN_DEBUG "%s failed 4 (%d)\n", __func__, res); + return; + } + + mdelay(10); +} + + +static struct prism2_helper_functions prism2_pccard_funcs = +{ + .card_present = prism2_pccard_card_present, + .cor_sreset = prism2_pccard_cor_sreset, + .genesis_reset = prism2_pccard_genesis_reset, + .hw_type = HOSTAP_HW_PCCARD, +}; + + +/* allocate local data and register with CardServices + * initialize dev_link structure, but do not configure the card yet */ +static int hostap_cs_probe(struct pcmcia_device *p_dev) +{ + int ret; + + PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info); + + ret = prism2_config(p_dev); + if (ret) { + PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n"); + } + + return ret; +} + + +static void prism2_detach(struct pcmcia_device *link) +{ + PDEBUG(DEBUG_FLOW, "prism2_detach\n"); + + prism2_release((u_long)link); + + /* release net devices */ + if (link->priv) { + struct hostap_cs_priv *hw_priv; + struct net_device *dev; + struct hostap_interface *iface; + dev = link->priv; + iface = netdev_priv(dev); + hw_priv = iface->local->hw_priv; + prism2_free_local_data(dev); + kfree(hw_priv); + } +} + + +static int prism2_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + if (p_dev->config_index == 0) + return -EINVAL; + + return pcmcia_request_io(p_dev); +} + +static int prism2_config(struct pcmcia_device *link) +{ + struct net_device *dev; + struct hostap_interface *iface; + local_info_t *local; + int ret = 1; + struct hostap_cs_priv *hw_priv; + unsigned long flags; + + PDEBUG(DEBUG_FLOW, "prism2_config()\n"); + + hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL); + if (hw_priv == NULL) { + ret = -ENOMEM; + goto failed; + } + + /* Look for an appropriate configuration table entry in the CIS */ + link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO | + CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; + if (ignore_cis_vcc) + link->config_flags &= ~CONF_AUTO_CHECK_VCC; + ret = pcmcia_loop_config(link, prism2_config_check, NULL); + if (ret) { + if (!ignore_cis_vcc) + printk(KERN_ERR "GetNextTuple(): No matching " + "CIS configuration. Maybe you need the " + "ignore_cis_vcc=1 parameter.\n"); + goto failed; + } + + /* Need to allocate net_device before requesting IRQ handler */ + dev = prism2_init_local_data(&prism2_pccard_funcs, 0, + &link->dev); + if (dev == NULL) + goto failed; + link->priv = dev; + + iface = netdev_priv(dev); + local = iface->local; + local->hw_priv = hw_priv; + hw_priv->link = link; + + /* + * We enable IRQ here, but IRQ handler will not proceed + * until dev->base_addr is set below. This protect us from + * receive interrupts when driver is not initialized. + */ + ret = pcmcia_request_irq(link, prism2_interrupt); + if (ret) + goto failed; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + spin_lock_irqsave(&local->irq_init_lock, flags); + dev->irq = link->irq; + dev->base_addr = link->resource[0]->start; + spin_unlock_irqrestore(&local->irq_init_lock, flags); + + local->shutdown = 0; + + sandisk_enable_wireless(dev); + + ret = prism2_hw_config(dev, 1); + if (!ret) + ret = hostap_hw_ready(dev); + + return ret; + + failed: + kfree(hw_priv); + prism2_release((u_long)link); + return ret; +} + + +static void prism2_release(u_long arg) +{ + struct pcmcia_device *link = (struct pcmcia_device *)arg; + + PDEBUG(DEBUG_FLOW, "prism2_release\n"); + + if (link->priv) { + struct net_device *dev = link->priv; + struct hostap_interface *iface; + + iface = netdev_priv(dev); + prism2_hw_shutdown(dev, 0); + iface->local->shutdown = 1; + } + + pcmcia_disable_device(link); + PDEBUG(DEBUG_FLOW, "release - done\n"); +} + +static int hostap_cs_suspend(struct pcmcia_device *link) +{ + struct net_device *dev = (struct net_device *) link->priv; + int dev_open = 0; + struct hostap_interface *iface = NULL; + + if (!dev) + return -ENODEV; + + iface = netdev_priv(dev); + + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info); + if (iface && iface->local) + dev_open = iface->local->num_dev_open > 0; + if (dev_open) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + prism2_suspend(dev); + + return 0; +} + +static int hostap_cs_resume(struct pcmcia_device *link) +{ + struct net_device *dev = (struct net_device *) link->priv; + int dev_open = 0; + struct hostap_interface *iface = NULL; + + if (!dev) + return -ENODEV; + + iface = netdev_priv(dev); + + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info); + + if (iface && iface->local) + dev_open = iface->local->num_dev_open > 0; + + prism2_hw_shutdown(dev, 1); + prism2_hw_config(dev, dev_open ? 0 : 1); + if (dev_open) { + netif_device_attach(dev); + netif_start_queue(dev); + } + + return 0; +} + +static const struct pcmcia_device_id hostap_cs_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), + PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), + PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x3301), + PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030b), + PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), + PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), + PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x02d2, 0x0001), + PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x0001), + PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), +/* PCMCIA_DEVICE_MANF_CARD(0xc00f, 0x0000), conflict with pcnet_cs */ + PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010), + PCMCIA_DEVICE_MANF_CARD(0x0126, 0x0002), + PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0xd601, 0x0005, "ADLINK 345 CF", + 0x2d858104), + PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "INTERSIL", + 0x74c5e40d), + PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "Intersil", + 0x4b801a17), + PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.02", + 0x4b74baa0), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "SanDisk", "ConnectPlus", + 0x7a954bd9, 0x74be00c6), + PCMCIA_DEVICE_PROD_ID123( + "Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02", + 0xe6ec52ce, 0x08649af2, 0x4b74baa0), + PCMCIA_DEVICE_PROD_ID123( + "Canon", "Wireless LAN CF Card K30225", "Version 01.00", + 0x96ef6fe2, 0x263fcbab, 0xa57adb8c), + PCMCIA_DEVICE_PROD_ID123( + "D", "Link DWL-650 11Mbps WLAN Card", "Version 01.02", + 0x71b18589, 0xb6f1b0ab, 0x4b74baa0), + PCMCIA_DEVICE_PROD_ID123( + "Instant Wireless ", " Network PC CARD", "Version 01.02", + 0x11d901af, 0x6e9bd926, 0x4b74baa0), + PCMCIA_DEVICE_PROD_ID123( + "SMC", "SMC2632W", "Version 01.02", + 0xc4f8b18b, 0x474a1f2a, 0x4b74baa0), + PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", + 0x2decece3, 0x82067c18), + PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card", + 0x54f7c49c, 0x15a75e5b), + PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", + 0x74c5e40d, 0xdb472a18), + PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card", + 0x0733cc81, 0x0c52f395), + PCMCIA_DEVICE_PROD_ID12( + "ZoomAir 11Mbps High", "Rate wireless Networking", + 0x273fe3db, 0x32a1eaee), + PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", + 0xa37434e9, 0x9762e8f1), + PCMCIA_DEVICE_PROD_ID123( + "Pretec", "CompactWLAN Card 802.11b", "2.5", + 0x1cadd3e5, 0xe697636c, 0x7a5bfcf1), + PCMCIA_DEVICE_PROD_ID123( + "U.S. Robotics", "IEEE 802.11b PC-CARD", "Version 01.02", + 0xc7b8df9d, 0x1700d087, 0x4b74baa0), + PCMCIA_DEVICE_PROD_ID123( + "Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", + "Ver. 1.00", + 0x5cd01705, 0x4271660f, 0x9d08ee12), + PCMCIA_DEVICE_PROD_ID123( + "Wireless LAN" , "11Mbps PC Card", "Version 01.02", + 0x4b8870ff, 0x70e946d1, 0x4b74baa0), + PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092), + PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2), + PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b), + PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids); + + +static struct pcmcia_driver hostap_driver = { + .name = "hostap_cs", + .probe = hostap_cs_probe, + .remove = prism2_detach, + .owner = THIS_MODULE, + .id_table = hostap_cs_ids, + .suspend = hostap_cs_suspend, + .resume = hostap_cs_resume, +}; +module_pcmcia_driver(hostap_driver); diff --git a/drivers/net/wireless/intersil/hostap/hostap_download.c b/drivers/net/wireless/intersil/hostap/hostap_download.c new file mode 100644 index 000000000000..705fe668b969 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_download.c @@ -0,0 +1,812 @@ +static int prism2_enable_aux_port(struct net_device *dev, int enable) +{ + u16 val, reg; + int i, tries; + unsigned long flags; + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->no_pri) { + if (enable) { + PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux " + "port is already enabled\n", dev->name); + } + return 0; + } + + spin_lock_irqsave(&local->cmdlock, flags); + + /* wait until busy bit is clear */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + spin_unlock_irqrestore(&local->cmdlock, flags); + printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n", + dev->name, reg); + return -ETIMEDOUT; + } + + val = HFA384X_INW(HFA384X_CONTROL_OFF); + + if (enable) { + HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF); + + if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED) + printk("prism2_enable_aux_port: was not disabled!?\n"); + val &= ~HFA384X_AUX_PORT_MASK; + val |= HFA384X_AUX_PORT_ENABLE; + } else { + HFA384X_OUTW(0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + + if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED) + printk("prism2_enable_aux_port: was not enabled!?\n"); + val &= ~HFA384X_AUX_PORT_MASK; + val |= HFA384X_AUX_PORT_DISABLE; + } + HFA384X_OUTW(val, HFA384X_CONTROL_OFF); + + udelay(5); + + i = 10000; + while (i > 0) { + val = HFA384X_INW(HFA384X_CONTROL_OFF); + val &= HFA384X_AUX_PORT_MASK; + + if ((enable && val == HFA384X_AUX_PORT_ENABLED) || + (!enable && val == HFA384X_AUX_PORT_DISABLED)) + break; + + udelay(10); + i--; + } + + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (i == 0) { + printk("prism2_enable_aux_port(%d) timed out\n", + enable); + return -ETIMEDOUT; + } + + return 0; +} + + +static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len, + void *buf) +{ + u16 page, offset; + if (addr & 1 || len & 1) + return -1; + + page = addr >> 7; + offset = addr & 0x7f; + + HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); + HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); + + udelay(5); + +#ifdef PRISM2_PCI + { + __le16 *pos = (__le16 *) buf; + while (len > 0) { + *pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF); + len -= 2; + } + } +#else /* PRISM2_PCI */ + HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2); +#endif /* PRISM2_PCI */ + + return 0; +} + + +static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len, + void *buf) +{ + u16 page, offset; + if (addr & 1 || len & 1) + return -1; + + page = addr >> 7; + offset = addr & 0x7f; + + HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); + HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); + + udelay(5); + +#ifdef PRISM2_PCI + { + __le16 *pos = (__le16 *) buf; + while (len > 0) { + HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF); + len -= 2; + } + } +#else /* PRISM2_PCI */ + HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2); +#endif /* PRISM2_PCI */ + + return 0; +} + + +static int prism2_pda_ok(u8 *buf) +{ + __le16 *pda = (__le16 *) buf; + int pos; + u16 len, pdr; + + if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff && + buf[3] == 0x00) + return 0; + + pos = 0; + while (pos + 1 < PRISM2_PDA_SIZE / 2) { + len = le16_to_cpu(pda[pos]); + pdr = le16_to_cpu(pda[pos + 1]); + if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2) + return 0; + + if (pdr == 0x0000 && len == 2) { + /* PDA end found */ + return 1; + } + + pos += len + 1; + } + + return 0; +} + + +#define prism2_download_aux_dump_npages 65536 + +struct prism2_download_aux_dump { + local_info_t *local; + u16 page[0x80]; +}; + +static int prism2_download_aux_dump_proc_show(struct seq_file *m, void *v) +{ + struct prism2_download_aux_dump *ctx = m->private; + + hfa384x_from_aux(ctx->local->dev, (unsigned long)v - 1, 0x80, ctx->page); + seq_write(m, ctx->page, 0x80); + return 0; +} + +static void *prism2_download_aux_dump_proc_start(struct seq_file *m, loff_t *_pos) +{ + struct prism2_download_aux_dump *ctx = m->private; + prism2_enable_aux_port(ctx->local->dev, 1); + if (*_pos >= prism2_download_aux_dump_npages) + return NULL; + return (void *)((unsigned long)*_pos + 1); +} + +static void *prism2_download_aux_dump_proc_next(struct seq_file *m, void *v, loff_t *_pos) +{ + ++*_pos; + if (*_pos >= prism2_download_aux_dump_npages) + return NULL; + return (void *)((unsigned long)*_pos + 1); +} + +static void prism2_download_aux_dump_proc_stop(struct seq_file *m, void *v) +{ + struct prism2_download_aux_dump *ctx = m->private; + prism2_enable_aux_port(ctx->local->dev, 0); +} + +static const struct seq_operations prism2_download_aux_dump_proc_seqops = { + .start = prism2_download_aux_dump_proc_start, + .next = prism2_download_aux_dump_proc_next, + .stop = prism2_download_aux_dump_proc_stop, + .show = prism2_download_aux_dump_proc_show, +}; + +static int prism2_download_aux_dump_proc_open(struct inode *inode, struct file *file) +{ + int ret = seq_open_private(file, &prism2_download_aux_dump_proc_seqops, + sizeof(struct prism2_download_aux_dump)); + if (ret == 0) { + struct seq_file *m = file->private_data; + m->private = PDE_DATA(inode); + } + return ret; +} + +static const struct file_operations prism2_download_aux_dump_proc_fops = { + .open = prism2_download_aux_dump_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + + +static u8 * prism2_read_pda(struct net_device *dev) +{ + u8 *buf; + int res, i, found = 0; +#define NUM_PDA_ADDRS 4 + unsigned int pda_addr[NUM_PDA_ADDRS] = { + 0x7f0000 /* others than HFA3841 */, + 0x3f0000 /* HFA3841 */, + 0x390000 /* apparently used in older cards */, + 0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */, + }; + + buf = kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL); + if (buf == NULL) + return NULL; + + /* Note: wlan card should be in initial state (just after init cmd) + * and no other operations should be performed concurrently. */ + + prism2_enable_aux_port(dev, 1); + + for (i = 0; i < NUM_PDA_ADDRS; i++) { + PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x", + dev->name, pda_addr[i]); + res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf); + if (res) + continue; + if (res == 0 && prism2_pda_ok(buf)) { + PDEBUG2(DEBUG_EXTRA2, ": OK\n"); + found = 1; + break; + } else { + PDEBUG2(DEBUG_EXTRA2, ": failed\n"); + } + } + + prism2_enable_aux_port(dev, 0); + + if (!found) { + printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name); + kfree(buf); + buf = NULL; + } + + return buf; +} + + +static int prism2_download_volatile(local_info_t *local, + struct prism2_download_data *param) +{ + struct net_device *dev = local->dev; + int ret = 0, i; + u16 param0, param1; + + if (local->hw_downloading) { + printk(KERN_WARNING "%s: Already downloading - aborting new " + "request\n", dev->name); + return -1; + } + + local->hw_downloading = 1; + if (local->pri_only) { + hfa384x_disable_interrupts(dev); + } else { + prism2_hw_shutdown(dev, 0); + + if (prism2_hw_init(dev, 0)) { + printk(KERN_WARNING "%s: Could not initialize card for" + " download\n", dev->name); + ret = -1; + goto out; + } + } + + if (prism2_enable_aux_port(dev, 1)) { + printk(KERN_WARNING "%s: Could not enable AUX port\n", + dev->name); + ret = -1; + goto out; + } + + param0 = param->start_addr & 0xffff; + param1 = param->start_addr >> 16; + + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_ENABLE_VOLATILE << 8), + param0)) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + + for (i = 0; i < param->num_areas; i++) { + PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", + dev->name, param->data[i].len, param->data[i].addr); + if (hfa384x_to_aux(dev, param->data[i].addr, + param->data[i].len, param->data[i].data)) { + printk(KERN_WARNING "%s: RAM download at 0x%08x " + "(len=%d) failed\n", dev->name, + param->data[i].addr, param->data[i].len); + ret = -1; + goto out; + } + } + + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_DISABLE << 8), param0)) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + /* ProgMode disable causes the hardware to restart itself from the + * given starting address. Give hw some time and ACK command just in + * case restart did not happen. */ + mdelay(5); + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + if (prism2_enable_aux_port(dev, 0)) { + printk(KERN_DEBUG "%s: Disabling AUX port failed\n", + dev->name); + /* continue anyway.. restart should have taken care of this */ + } + + mdelay(5); + local->hw_downloading = 0; + if (prism2_hw_config(dev, 2)) { + printk(KERN_WARNING "%s: Card configuration after RAM " + "download failed\n", dev->name); + ret = -1; + goto out; + } + + out: + local->hw_downloading = 0; + return ret; +} + + +static int prism2_enable_genesis(local_info_t *local, int hcr) +{ + struct net_device *dev = local->dev; + u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff }; + u8 readbuf[4]; + + printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n", + dev->name, hcr); + local->func->cor_sreset(local); + hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); + local->func->genesis_reset(local, hcr); + + /* Readback test */ + hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); + hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); + hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); + + if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) { + printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n", + hcr); + return 0; + } else { + printk(KERN_DEBUG "Readback test failed, HCR 0x%02x " + "write %02x %02x %02x %02x read %02x %02x %02x %02x\n", + hcr, initseq[0], initseq[1], initseq[2], initseq[3], + readbuf[0], readbuf[1], readbuf[2], readbuf[3]); + return 1; + } +} + + +static int prism2_get_ram_size(local_info_t *local) +{ + int ret; + + /* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */ + if (prism2_enable_genesis(local, 0x1f) == 0) + ret = 8; + else if (prism2_enable_genesis(local, 0x0f) == 0) + ret = 16; + else + ret = -1; + + /* Disable genesis mode */ + local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17); + + return ret; +} + + +static int prism2_download_genesis(local_info_t *local, + struct prism2_download_data *param) +{ + struct net_device *dev = local->dev; + int ram16 = 0, i; + int ret = 0; + + if (local->hw_downloading) { + printk(KERN_WARNING "%s: Already downloading - aborting new " + "request\n", dev->name); + return -EBUSY; + } + + if (!local->func->genesis_reset || !local->func->cor_sreset) { + printk(KERN_INFO "%s: Genesis mode downloading not supported " + "with this hwmodel\n", dev->name); + return -EOPNOTSUPP; + } + + local->hw_downloading = 1; + + if (prism2_enable_aux_port(dev, 1)) { + printk(KERN_DEBUG "%s: failed to enable AUX port\n", + dev->name); + ret = -EIO; + goto out; + } + + if (local->sram_type == -1) { + /* 0x1F for x8 SRAM or 0x0F for x16 SRAM */ + if (prism2_enable_genesis(local, 0x1f) == 0) { + ram16 = 0; + PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 " + "SRAM\n", dev->name); + } else if (prism2_enable_genesis(local, 0x0f) == 0) { + ram16 = 1; + PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 " + "SRAM\n", dev->name); + } else { + printk(KERN_DEBUG "%s: Could not initiate genesis " + "mode\n", dev->name); + ret = -EIO; + goto out; + } + } else { + if (prism2_enable_genesis(local, local->sram_type == 8 ? + 0x1f : 0x0f)) { + printk(KERN_DEBUG "%s: Failed to set Genesis " + "mode (sram_type=%d)\n", dev->name, + local->sram_type); + ret = -EIO; + goto out; + } + ram16 = local->sram_type != 8; + } + + for (i = 0; i < param->num_areas; i++) { + PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", + dev->name, param->data[i].len, param->data[i].addr); + if (hfa384x_to_aux(dev, param->data[i].addr, + param->data[i].len, param->data[i].data)) { + printk(KERN_WARNING "%s: RAM download at 0x%08x " + "(len=%d) failed\n", dev->name, + param->data[i].addr, param->data[i].len); + ret = -EIO; + goto out; + } + } + + PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n"); + local->func->genesis_reset(local, ram16 ? 0x07 : 0x17); + if (prism2_enable_aux_port(dev, 0)) { + printk(KERN_DEBUG "%s: Failed to disable AUX port\n", + dev->name); + } + + mdelay(5); + local->hw_downloading = 0; + + PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n"); + /* + * Make sure the INIT command does not generate a command completion + * event by disabling interrupts. + */ + hfa384x_disable_interrupts(dev); + if (prism2_hw_init(dev, 1)) { + printk(KERN_DEBUG "%s: Initialization after genesis mode " + "download failed\n", dev->name); + ret = -EIO; + goto out; + } + + PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n"); + if (prism2_hw_init2(dev, 1)) { + printk(KERN_DEBUG "%s: Initialization(2) after genesis mode " + "download failed\n", dev->name); + ret = -EIO; + goto out; + } + + out: + local->hw_downloading = 0; + return ret; +} + + +#ifdef PRISM2_NON_VOLATILE_DOWNLOAD +/* Note! Non-volatile downloading functionality has not yet been tested + * thoroughly and it may corrupt flash image and effectively kill the card that + * is being updated. You have been warned. */ + +static inline int prism2_download_block(struct net_device *dev, + u32 addr, u8 *data, + u32 bufaddr, int rest_len) +{ + u16 param0, param1; + int block_len; + + block_len = rest_len < 4096 ? rest_len : 4096; + + param0 = addr & 0xffff; + param1 = addr >> 16; + + HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF); + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8), + param0)) { + printk(KERN_WARNING "%s: Flash download command execution " + "failed\n", dev->name); + return -1; + } + + if (hfa384x_to_aux(dev, bufaddr, block_len, data)) { + printk(KERN_WARNING "%s: flash download at 0x%08x " + "(len=%d) failed\n", dev->name, addr, block_len); + return -1; + } + + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8), + 0)) { + printk(KERN_WARNING "%s: Flash write command execution " + "failed\n", dev->name); + return -1; + } + + return block_len; +} + + +static int prism2_download_nonvolatile(local_info_t *local, + struct prism2_download_data *dl) +{ + struct net_device *dev = local->dev; + int ret = 0, i; + struct { + __le16 page; + __le16 offset; + __le16 len; + } dlbuffer; + u32 bufaddr; + + if (local->hw_downloading) { + printk(KERN_WARNING "%s: Already downloading - aborting new " + "request\n", dev->name); + return -1; + } + + ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER, + &dlbuffer, 6, 0); + + if (ret < 0) { + printk(KERN_WARNING "%s: Could not read download buffer " + "parameters\n", dev->name); + goto out; + } + + printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n", + le16_to_cpu(dlbuffer.len), + le16_to_cpu(dlbuffer.page), + le16_to_cpu(dlbuffer.offset)); + + bufaddr = (le16_to_cpu(dlbuffer.page) << 7) + le16_to_cpu(dlbuffer.offset); + + local->hw_downloading = 1; + + if (!local->pri_only) { + prism2_hw_shutdown(dev, 0); + + if (prism2_hw_init(dev, 0)) { + printk(KERN_WARNING "%s: Could not initialize card for" + " download\n", dev->name); + ret = -1; + goto out; + } + } + + hfa384x_disable_interrupts(dev); + + if (prism2_enable_aux_port(dev, 1)) { + printk(KERN_WARNING "%s: Could not enable AUX port\n", + dev->name); + ret = -1; + goto out; + } + + printk(KERN_DEBUG "%s: starting flash download\n", dev->name); + for (i = 0; i < dl->num_areas; i++) { + int rest_len = dl->data[i].len; + int data_off = 0; + + while (rest_len > 0) { + int block_len; + + block_len = prism2_download_block( + dev, dl->data[i].addr + data_off, + dl->data[i].data + data_off, bufaddr, + rest_len); + + if (block_len < 0) { + ret = -1; + goto out; + } + + rest_len -= block_len; + data_off += block_len; + } + } + + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_DISABLE << 8), 0)) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + + if (prism2_enable_aux_port(dev, 0)) { + printk(KERN_DEBUG "%s: Disabling AUX port failed\n", + dev->name); + /* continue anyway.. restart should have taken care of this */ + } + + mdelay(5); + + local->func->hw_reset(dev); + local->hw_downloading = 0; + if (prism2_hw_config(dev, 2)) { + printk(KERN_WARNING "%s: Card configuration after flash " + "download failed\n", dev->name); + ret = -1; + } else { + printk(KERN_INFO "%s: Card initialized successfully after " + "flash download\n", dev->name); + } + + out: + local->hw_downloading = 0; + return ret; +} +#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ + + +static void prism2_download_free_data(struct prism2_download_data *dl) +{ + int i; + + if (dl == NULL) + return; + + for (i = 0; i < dl->num_areas; i++) + kfree(dl->data[i].data); + kfree(dl); +} + + +static int prism2_download(local_info_t *local, + struct prism2_download_param *param) +{ + int ret = 0; + int i; + u32 total_len = 0; + struct prism2_download_data *dl = NULL; + + printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x " + "num_areas=%d\n", + param->dl_cmd, param->start_addr, param->num_areas); + + if (param->num_areas > 100) { + ret = -EINVAL; + goto out; + } + + dl = kzalloc(sizeof(*dl) + param->num_areas * + sizeof(struct prism2_download_data_area), GFP_KERNEL); + if (dl == NULL) { + ret = -ENOMEM; + goto out; + } + dl->dl_cmd = param->dl_cmd; + dl->start_addr = param->start_addr; + dl->num_areas = param->num_areas; + for (i = 0; i < param->num_areas; i++) { + PDEBUG(DEBUG_EXTRA2, + " area %d: addr=0x%08x len=%d ptr=0x%p\n", + i, param->data[i].addr, param->data[i].len, + param->data[i].ptr); + + dl->data[i].addr = param->data[i].addr; + dl->data[i].len = param->data[i].len; + + total_len += param->data[i].len; + if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN || + total_len > PRISM2_MAX_DOWNLOAD_LEN) { + ret = -E2BIG; + goto out; + } + + dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL); + if (dl->data[i].data == NULL) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(dl->data[i].data, param->data[i].ptr, + param->data[i].len)) { + ret = -EFAULT; + goto out; + } + } + + switch (param->dl_cmd) { + case PRISM2_DOWNLOAD_VOLATILE: + case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT: + ret = prism2_download_volatile(local, dl); + break; + case PRISM2_DOWNLOAD_VOLATILE_GENESIS: + case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT: + ret = prism2_download_genesis(local, dl); + break; + case PRISM2_DOWNLOAD_NON_VOLATILE: +#ifdef PRISM2_NON_VOLATILE_DOWNLOAD + ret = prism2_download_nonvolatile(local, dl); +#else /* PRISM2_NON_VOLATILE_DOWNLOAD */ + printk(KERN_INFO "%s: non-volatile downloading not enabled\n", + local->dev->name); + ret = -EOPNOTSUPP; +#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ + break; + default: + printk(KERN_DEBUG "%s: unsupported download command %d\n", + local->dev->name, param->dl_cmd); + ret = -EINVAL; + break; + } + + out: + if (ret == 0 && dl && + param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) { + prism2_download_free_data(local->dl_pri); + local->dl_pri = dl; + } else if (ret == 0 && dl && + param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) { + prism2_download_free_data(local->dl_sec); + local->dl_sec = dl; + } else + prism2_download_free_data(dl); + + return ret; +} diff --git a/drivers/net/wireless/intersil/hostap/hostap_hw.c b/drivers/net/wireless/intersil/hostap/hostap_hw.c new file mode 100644 index 000000000000..6df3ee561d52 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_hw.c @@ -0,0 +1,3424 @@ +/* + * Host AP (software wireless LAN access point) driver for + * Intersil Prism2/2.5/3. + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + * + * FIX: + * - there is currently no way of associating TX packets to correct wds device + * when TX Exc/OK event occurs, so all tx_packets and some + * tx_errors/tx_dropped are added to the main netdevice; using sw_support + * field in txdesc might be used to fix this (using Alloc event to increment + * tx_packets would need some further info in txfid table) + * + * Buffer Access Path (BAP) usage: + * Prism2 cards have two separate BAPs for accessing the card memory. These + * should allow concurrent access to two different frames and the driver + * previously used BAP0 for sending data and BAP1 for receiving data. + * However, there seems to be number of issues with concurrent access and at + * least one know hardware bug in using BAP0 and BAP1 concurrently with PCI + * Prism2.5. Therefore, the driver now only uses BAP0 for moving data between + * host and card memories. BAP0 accesses are protected with local->baplock + * (spin_lock_bh) to prevent concurrent use. + */ + + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_80211.h" +#include "hostap.h" +#include "hostap_ap.h" + + +/* #define final_version */ + +static int mtu = 1500; +module_param(mtu, int, 0444); +MODULE_PARM_DESC(mtu, "Maximum transfer unit"); + +static int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS }; +module_param_array(channel, int, NULL, 0444); +MODULE_PARM_DESC(channel, "Initial channel"); + +static char essid[33] = "test"; +module_param_string(essid, essid, sizeof(essid), 0444); +MODULE_PARM_DESC(essid, "Host AP's ESSID"); + +static int iw_mode[MAX_PARM_DEVICES] = { IW_MODE_MASTER, DEF_INTS }; +module_param_array(iw_mode, int, NULL, 0444); +MODULE_PARM_DESC(iw_mode, "Initial operation mode"); + +static int beacon_int[MAX_PARM_DEVICES] = { 100, DEF_INTS }; +module_param_array(beacon_int, int, NULL, 0444); +MODULE_PARM_DESC(beacon_int, "Beacon interval (1 = 1024 usec)"); + +static int dtim_period[MAX_PARM_DEVICES] = { 1, DEF_INTS }; +module_param_array(dtim_period, int, NULL, 0444); +MODULE_PARM_DESC(dtim_period, "DTIM period"); + +static char dev_template[16] = "wlan%d"; +module_param_string(dev_template, dev_template, sizeof(dev_template), 0444); +MODULE_PARM_DESC(dev_template, "Prefix for network device name (default: " + "wlan%d)"); + +#ifdef final_version +#define EXTRA_EVENTS_WTERR 0 +#else +/* check WTERR events (Wait Time-out) in development versions */ +#define EXTRA_EVENTS_WTERR HFA384X_EV_WTERR +#endif + +/* Events that will be using BAP0 */ +#define HFA384X_BAP0_EVENTS \ + (HFA384X_EV_TXEXC | HFA384X_EV_RX | HFA384X_EV_INFO | HFA384X_EV_TX) + +/* event mask, i.e., events that will result in an interrupt */ +#define HFA384X_EVENT_MASK \ + (HFA384X_BAP0_EVENTS | HFA384X_EV_ALLOC | HFA384X_EV_INFDROP | \ + HFA384X_EV_CMD | HFA384X_EV_TICK | \ + EXTRA_EVENTS_WTERR) + +/* Default TX control flags: use 802.11 headers and request interrupt for + * failed transmits. Frames that request ACK callback, will add + * _TX_OK flag and _ALT_RTRY flag may be used to select different retry policy. + */ +#define HFA384X_TX_CTRL_FLAGS \ + (HFA384X_TX_CTRL_802_11 | HFA384X_TX_CTRL_TX_EX) + + +/* ca. 1 usec */ +#define HFA384X_CMD_BUSY_TIMEOUT 5000 +#define HFA384X_BAP_BUSY_TIMEOUT 50000 + +/* ca. 10 usec */ +#define HFA384X_CMD_COMPL_TIMEOUT 20000 +#define HFA384X_DL_COMPL_TIMEOUT 1000000 + +/* Wait times for initialization; yield to other processes to avoid busy + * waiting for long time. */ +#define HFA384X_INIT_TIMEOUT (HZ / 2) /* 500 ms */ +#define HFA384X_ALLOC_COMPL_TIMEOUT (HZ / 20) /* 50 ms */ + + +static void prism2_hw_reset(struct net_device *dev); +static void prism2_check_sta_fw_version(local_info_t *local); + +#ifdef PRISM2_DOWNLOAD_SUPPORT +/* hostap_download.c */ +static const struct file_operations prism2_download_aux_dump_proc_fops; +static u8 * prism2_read_pda(struct net_device *dev); +static int prism2_download(local_info_t *local, + struct prism2_download_param *param); +static void prism2_download_free_data(struct prism2_download_data *dl); +static int prism2_download_volatile(local_info_t *local, + struct prism2_download_data *param); +static int prism2_download_genesis(local_info_t *local, + struct prism2_download_data *param); +static int prism2_get_ram_size(local_info_t *local); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + + + +#ifndef final_version +/* magic value written to SWSUPPORT0 reg. for detecting whether card is still + * present */ +#define HFA384X_MAGIC 0x8A32 +#endif + + +static u16 hfa384x_read_reg(struct net_device *dev, u16 reg) +{ + return HFA384X_INW(reg); +} + + +static void hfa384x_read_regs(struct net_device *dev, + struct hfa384x_regs *regs) +{ + regs->cmd = HFA384X_INW(HFA384X_CMD_OFF); + regs->evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); + regs->offset0 = HFA384X_INW(HFA384X_OFFSET0_OFF); + regs->offset1 = HFA384X_INW(HFA384X_OFFSET1_OFF); + regs->swsupport0 = HFA384X_INW(HFA384X_SWSUPPORT0_OFF); +} + + +/** + * __hostap_cmd_queue_free - Free Prism2 command queue entry (private) + * @local: pointer to private Host AP driver data + * @entry: Prism2 command queue entry to be freed + * @del_req: request the entry to be removed + * + * Internal helper function for freeing Prism2 command queue entries. + * Caller must have acquired local->cmdlock before calling this function. + */ +static inline void __hostap_cmd_queue_free(local_info_t *local, + struct hostap_cmd_queue *entry, + int del_req) +{ + if (del_req) { + entry->del_req = 1; + if (!list_empty(&entry->list)) { + list_del_init(&entry->list); + local->cmd_queue_len--; + } + } + + if (atomic_dec_and_test(&entry->usecnt) && entry->del_req) + kfree(entry); +} + + +/** + * hostap_cmd_queue_free - Free Prism2 command queue entry + * @local: pointer to private Host AP driver data + * @entry: Prism2 command queue entry to be freed + * @del_req: request the entry to be removed + * + * Free a Prism2 command queue entry. + */ +static inline void hostap_cmd_queue_free(local_info_t *local, + struct hostap_cmd_queue *entry, + int del_req) +{ + unsigned long flags; + + spin_lock_irqsave(&local->cmdlock, flags); + __hostap_cmd_queue_free(local, entry, del_req); + spin_unlock_irqrestore(&local->cmdlock, flags); +} + + +/** + * prism2_clear_cmd_queue - Free all pending Prism2 command queue entries + * @local: pointer to private Host AP driver data + */ +static void prism2_clear_cmd_queue(local_info_t *local) +{ + struct list_head *ptr, *n; + unsigned long flags; + struct hostap_cmd_queue *entry; + + spin_lock_irqsave(&local->cmdlock, flags); + list_for_each_safe(ptr, n, &local->cmd_queue) { + entry = list_entry(ptr, struct hostap_cmd_queue, list); + atomic_inc(&entry->usecnt); + printk(KERN_DEBUG "%s: removed pending cmd_queue entry " + "(type=%d, cmd=0x%04x, param0=0x%04x)\n", + local->dev->name, entry->type, entry->cmd, + entry->param0); + __hostap_cmd_queue_free(local, entry, 1); + } + if (local->cmd_queue_len) { + /* This should not happen; print debug message and clear + * queue length. */ + printk(KERN_DEBUG "%s: cmd_queue_len (%d) not zero after " + "flush\n", local->dev->name, local->cmd_queue_len); + local->cmd_queue_len = 0; + } + spin_unlock_irqrestore(&local->cmdlock, flags); +} + + +/** + * hfa384x_cmd_issue - Issue a Prism2 command to the hardware + * @dev: pointer to net_device + * @entry: Prism2 command queue entry to be issued + */ +static int hfa384x_cmd_issue(struct net_device *dev, + struct hostap_cmd_queue *entry) +{ + struct hostap_interface *iface; + local_info_t *local; + int tries; + u16 reg; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->card_present && !local->func->card_present(local)) + return -ENODEV; + + if (entry->issued) { + printk(KERN_DEBUG "%s: driver bug - re-issuing command @%p\n", + dev->name, entry); + } + + /* wait until busy bit is clear; this should always be clear since the + * commands are serialized */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } +#ifndef final_version + if (tries != HFA384X_CMD_BUSY_TIMEOUT) { + prism2_io_debug_error(dev, 1); + printk(KERN_DEBUG "%s: hfa384x_cmd_issue: cmd reg was busy " + "for %d usec\n", dev->name, + HFA384X_CMD_BUSY_TIMEOUT - tries); + } +#endif + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + prism2_io_debug_error(dev, 2); + printk(KERN_DEBUG "%s: hfa384x_cmd_issue - timeout - " + "reg=0x%04x\n", dev->name, reg); + return -ETIMEDOUT; + } + + /* write command */ + spin_lock_irqsave(&local->cmdlock, flags); + HFA384X_OUTW(entry->param0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(entry->param1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(entry->cmd, HFA384X_CMD_OFF); + entry->issued = 1; + spin_unlock_irqrestore(&local->cmdlock, flags); + + return 0; +} + + +/** + * hfa384x_cmd - Issue a Prism2 command and wait (sleep) for completion + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + * @param1: value for Param1 register (pointer; %NULL if not used) + * @resp0: pointer for Resp0 data or %NULL if Resp0 is not needed + * + * Issue given command (possibly after waiting in command queue) and sleep + * until the command is completed (or timed out or interrupted). This can be + * called only from user process context. + */ +static int hfa384x_cmd(struct net_device *dev, u16 cmd, u16 param0, + u16 *param1, u16 *resp0) +{ + struct hostap_interface *iface; + local_info_t *local; + int err, res, issue, issued = 0; + unsigned long flags; + struct hostap_cmd_queue *entry; + DECLARE_WAITQUEUE(wait, current); + + iface = netdev_priv(dev); + local = iface->local; + + if (in_interrupt()) { + printk(KERN_DEBUG "%s: hfa384x_cmd called from interrupt " + "context\n", dev->name); + return -1; + } + + if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN) { + printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", + dev->name); + return -1; + } + + if (signal_pending(current)) + return -EINTR; + + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + if (entry == NULL) + return -ENOMEM; + + atomic_set(&entry->usecnt, 1); + entry->type = CMD_SLEEP; + entry->cmd = cmd; + entry->param0 = param0; + if (param1) + entry->param1 = *param1; + init_waitqueue_head(&entry->compl); + + /* prepare to wait for command completion event, but do not sleep yet + */ + add_wait_queue(&entry->compl, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&local->cmdlock, flags); + issue = list_empty(&local->cmd_queue); + if (issue) + entry->issuing = 1; + list_add_tail(&entry->list, &local->cmd_queue); + local->cmd_queue_len++; + spin_unlock_irqrestore(&local->cmdlock, flags); + + err = 0; + if (!issue) + goto wait_completion; + + if (signal_pending(current)) + err = -EINTR; + + if (!err) { + if (hfa384x_cmd_issue(dev, entry)) + err = -ETIMEDOUT; + else + issued = 1; + } + + wait_completion: + if (!err && entry->type != CMD_COMPLETED) { + /* sleep until command is completed or timed out */ + res = schedule_timeout(2 * HZ); + } else + res = -1; + + if (!err && signal_pending(current)) + err = -EINTR; + + if (err && issued) { + /* the command was issued, so a CmdCompl event should occur + * soon; however, there's a pending signal and + * schedule_timeout() would be interrupted; wait a short period + * of time to avoid removing entry from the list before + * CmdCompl event */ + udelay(300); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&entry->compl, &wait); + + /* If entry->list is still in the list, it must be removed + * first and in this case prism2_cmd_ev() does not yet have + * local reference to it, and the data can be kfree()'d + * here. If the command completion event is still generated, + * it will be assigned to next (possibly) pending command, but + * the driver will reset the card anyway due to timeout + * + * If the entry is not in the list prism2_cmd_ev() has a local + * reference to it, but keeps cmdlock as long as the data is + * needed, so the data can be kfree()'d here. */ + + /* FIX: if the entry->list is in the list, it has not been completed + * yet, so removing it here is somewhat wrong.. this could cause + * references to freed memory and next list_del() causing NULL pointer + * dereference.. it would probably be better to leave the entry in the + * list and the list should be emptied during hw reset */ + + spin_lock_irqsave(&local->cmdlock, flags); + if (!list_empty(&entry->list)) { + printk(KERN_DEBUG "%s: hfa384x_cmd: entry still in list? " + "(entry=%p, type=%d, res=%d)\n", dev->name, entry, + entry->type, res); + list_del_init(&entry->list); + local->cmd_queue_len--; + } + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (err) { + printk(KERN_DEBUG "%s: hfa384x_cmd: interrupted; err=%d\n", + dev->name, err); + res = err; + goto done; + } + + if (entry->type != CMD_COMPLETED) { + u16 reg = HFA384X_INW(HFA384X_EVSTAT_OFF); + printk(KERN_DEBUG "%s: hfa384x_cmd: command was not " + "completed (res=%d, entry=%p, type=%d, cmd=0x%04x, " + "param0=0x%04x, EVSTAT=%04x INTEN=%04x)\n", dev->name, + res, entry, entry->type, entry->cmd, entry->param0, reg, + HFA384X_INW(HFA384X_INTEN_OFF)); + if (reg & HFA384X_EV_CMD) { + /* Command completion event is pending, but the + * interrupt was not delivered - probably an issue + * with pcmcia-cs configuration. */ + printk(KERN_WARNING "%s: interrupt delivery does not " + "seem to work\n", dev->name); + } + prism2_io_debug_error(dev, 3); + res = -ETIMEDOUT; + goto done; + } + + if (resp0 != NULL) + *resp0 = entry->resp0; +#ifndef final_version + if (entry->res) { + printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x, " + "resp0=0x%04x\n", + dev->name, cmd, entry->res, entry->resp0); + } +#endif /* final_version */ + + res = entry->res; + done: + hostap_cmd_queue_free(local, entry, 1); + return res; +} + + +/** + * hfa384x_cmd_callback - Issue a Prism2 command; callback when completed + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + * @callback: command completion callback function (%NULL = no callback) + * @context: context data to be given to the callback function + * + * Issue given command (possibly after waiting in command queue) and use + * callback function to indicate command completion. This can be called both + * from user and interrupt context. The callback function will be called in + * hardware IRQ context. It can be %NULL, when no function is called when + * command is completed. + */ +static int hfa384x_cmd_callback(struct net_device *dev, u16 cmd, u16 param0, + void (*callback)(struct net_device *dev, + long context, u16 resp0, + u16 status), + long context) +{ + struct hostap_interface *iface; + local_info_t *local; + int issue, ret; + unsigned long flags; + struct hostap_cmd_queue *entry; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN + 2) { + printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", + dev->name); + return -1; + } + + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + if (entry == NULL) + return -ENOMEM; + + atomic_set(&entry->usecnt, 1); + entry->type = CMD_CALLBACK; + entry->cmd = cmd; + entry->param0 = param0; + entry->callback = callback; + entry->context = context; + + spin_lock_irqsave(&local->cmdlock, flags); + issue = list_empty(&local->cmd_queue); + if (issue) + entry->issuing = 1; + list_add_tail(&entry->list, &local->cmd_queue); + local->cmd_queue_len++; + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (issue && hfa384x_cmd_issue(dev, entry)) + ret = -ETIMEDOUT; + else + ret = 0; + + hostap_cmd_queue_free(local, entry, ret); + + return ret; +} + + +/** + * __hfa384x_cmd_no_wait - Issue a Prism2 command (private) + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + * @io_debug_num: I/O debug error number + * + * Shared helper function for hfa384x_cmd_wait() and hfa384x_cmd_no_wait(). + */ +static int __hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, u16 param0, + int io_debug_num) +{ + int tries; + u16 reg; + + /* wait until busy bit is clear; this should always be clear since the + * commands are serialized */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + prism2_io_debug_error(dev, io_debug_num); + printk(KERN_DEBUG "%s: __hfa384x_cmd_no_wait(%d) - timeout - " + "reg=0x%04x\n", dev->name, io_debug_num, reg); + return -ETIMEDOUT; + } + + /* write command */ + HFA384X_OUTW(param0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(cmd, HFA384X_CMD_OFF); + + return 0; +} + + +/** + * hfa384x_cmd_wait - Issue a Prism2 command and busy wait for completion + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + */ +static int hfa384x_cmd_wait(struct net_device *dev, u16 cmd, u16 param0) +{ + int res, tries; + u16 reg; + + res = __hfa384x_cmd_no_wait(dev, cmd, param0, 4); + if (res) + return res; + + /* wait for command completion */ + if ((cmd & HFA384X_CMDCODE_MASK) == HFA384X_CMDCODE_DOWNLOAD) + tries = HFA384X_DL_COMPL_TIMEOUT; + else + tries = HFA384X_CMD_COMPL_TIMEOUT; + + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && + tries > 0) { + tries--; + udelay(10); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_EVSTAT_OFF); + prism2_io_debug_error(dev, 5); + printk(KERN_DEBUG "%s: hfa384x_cmd_wait - timeout2 - " + "reg=0x%04x\n", dev->name, reg); + return -ETIMEDOUT; + } + + res = (HFA384X_INW(HFA384X_STATUS_OFF) & + (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | BIT(9) | + BIT(8))) >> 8; +#ifndef final_version + if (res) { + printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x\n", + dev->name, cmd, res); + } +#endif + + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + return res; +} + + +/** + * hfa384x_cmd_no_wait - Issue a Prism2 command; do not wait for completion + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + */ +static inline int hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, + u16 param0) +{ + return __hfa384x_cmd_no_wait(dev, cmd, param0, 6); +} + + +/** + * prism2_cmd_ev - Prism2 command completion event handler + * @dev: pointer to net_device + * + * Interrupt handler for command completion events. Called by the main + * interrupt handler in hardware IRQ context. Read Resp0 and status registers + * from the hardware and ACK the event. Depending on the issued command type + * either wake up the sleeping process that is waiting for command completion + * or call the callback function. Issue the next command, if one is pending. + */ +static void prism2_cmd_ev(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hostap_cmd_queue *entry = NULL; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock(&local->cmdlock); + if (!list_empty(&local->cmd_queue)) { + entry = list_entry(local->cmd_queue.next, + struct hostap_cmd_queue, list); + atomic_inc(&entry->usecnt); + list_del_init(&entry->list); + local->cmd_queue_len--; + + if (!entry->issued) { + printk(KERN_DEBUG "%s: Command completion event, but " + "cmd not issued\n", dev->name); + __hostap_cmd_queue_free(local, entry, 1); + entry = NULL; + } + } + spin_unlock(&local->cmdlock); + + if (!entry) { + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: Command completion event, but no " + "pending commands\n", dev->name); + return; + } + + entry->resp0 = HFA384X_INW(HFA384X_RESP0_OFF); + entry->res = (HFA384X_INW(HFA384X_STATUS_OFF) & + (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | + BIT(9) | BIT(8))) >> 8; + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + /* TODO: rest of the CmdEv handling could be moved to tasklet */ + if (entry->type == CMD_SLEEP) { + entry->type = CMD_COMPLETED; + wake_up_interruptible(&entry->compl); + } else if (entry->type == CMD_CALLBACK) { + if (entry->callback) + entry->callback(dev, entry->context, entry->resp0, + entry->res); + } else { + printk(KERN_DEBUG "%s: Invalid command completion type %d\n", + dev->name, entry->type); + } + hostap_cmd_queue_free(local, entry, 1); + + /* issue next command, if pending */ + entry = NULL; + spin_lock(&local->cmdlock); + if (!list_empty(&local->cmd_queue)) { + entry = list_entry(local->cmd_queue.next, + struct hostap_cmd_queue, list); + if (entry->issuing) { + /* hfa384x_cmd() has already started issuing this + * command, so do not start here */ + entry = NULL; + } + if (entry) + atomic_inc(&entry->usecnt); + } + spin_unlock(&local->cmdlock); + + if (entry) { + /* issue next command; if command issuing fails, remove the + * entry from cmd_queue */ + int res = hfa384x_cmd_issue(dev, entry); + spin_lock(&local->cmdlock); + __hostap_cmd_queue_free(local, entry, res); + spin_unlock(&local->cmdlock); + } +} + + +static int hfa384x_wait_offset(struct net_device *dev, u16 o_off) +{ + int tries = HFA384X_BAP_BUSY_TIMEOUT; + int res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY; + + while (res && tries > 0) { + tries--; + udelay(1); + res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY; + } + return res; +} + + +/* Offset must be even */ +static int hfa384x_setup_bap(struct net_device *dev, u16 bap, u16 id, + int offset) +{ + u16 o_off, s_off; + int ret = 0; + + if (offset % 2 || bap > 1) + return -EINVAL; + + if (bap == BAP1) { + o_off = HFA384X_OFFSET1_OFF; + s_off = HFA384X_SELECT1_OFF; + } else { + o_off = HFA384X_OFFSET0_OFF; + s_off = HFA384X_SELECT0_OFF; + } + + if (hfa384x_wait_offset(dev, o_off)) { + prism2_io_debug_error(dev, 7); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout before\n", + dev->name); + ret = -ETIMEDOUT; + goto out; + } + + HFA384X_OUTW(id, s_off); + HFA384X_OUTW(offset, o_off); + + if (hfa384x_wait_offset(dev, o_off)) { + prism2_io_debug_error(dev, 8); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout after\n", + dev->name); + ret = -ETIMEDOUT; + goto out; + } +#ifndef final_version + if (HFA384X_INW(o_off) & HFA384X_OFFSET_ERR) { + prism2_io_debug_error(dev, 9); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - offset error " + "(%d,0x04%x,%d); reg=0x%04x\n", + dev->name, bap, id, offset, HFA384X_INW(o_off)); + ret = -EINVAL; + } +#endif + + out: + return ret; +} + + +static int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len, + int exact_len) +{ + struct hostap_interface *iface; + local_info_t *local; + int res, rlen = 0; + struct hfa384x_rid_hdr rec; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->no_pri) { + printk(KERN_DEBUG "%s: cannot get RID %04x (len=%d) - no PRI " + "f/w\n", dev->name, rid, len); + return -ENOTTY; /* Well.. not really correct, but return + * something unique enough.. */ + } + + if ((local->func->card_present && !local->func->card_present(local)) || + local->hw_downloading) + return -ENODEV; + + res = mutex_lock_interruptible(&local->rid_bap_mtx); + if (res) + return res; + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS, rid, NULL, NULL); + if (res) { + printk(KERN_DEBUG "%s: hfa384x_get_rid: CMDCODE_ACCESS failed " + "(res=%d, rid=%04x, len=%d)\n", + dev->name, res, rid, len); + mutex_unlock(&local->rid_bap_mtx); + return res; + } + + spin_lock_bh(&local->baplock); + + res = hfa384x_setup_bap(dev, BAP0, rid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); + + if (le16_to_cpu(rec.len) == 0) { + /* RID not available */ + res = -ENODATA; + } + + rlen = (le16_to_cpu(rec.len) - 1) * 2; + if (!res && exact_len && rlen != len) { + printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: " + "rid=0x%04x, len=%d (expected %d)\n", + dev->name, rid, rlen, len); + res = -ENODATA; + } + + if (!res) + res = hfa384x_from_bap(dev, BAP0, buf, len); + + spin_unlock_bh(&local->baplock); + mutex_unlock(&local->rid_bap_mtx); + + if (res) { + if (res != -ENODATA) + printk(KERN_DEBUG "%s: hfa384x_get_rid (rid=%04x, " + "len=%d) - failed - res=%d\n", dev->name, rid, + len, res); + if (res == -ETIMEDOUT) + prism2_hw_reset(dev); + return res; + } + + return rlen; +} + + +static int hfa384x_set_rid(struct net_device *dev, u16 rid, void *buf, int len) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_rid_hdr rec; + int res; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->no_pri) { + printk(KERN_DEBUG "%s: cannot set RID %04x (len=%d) - no PRI " + "f/w\n", dev->name, rid, len); + return -ENOTTY; /* Well.. not really correct, but return + * something unique enough.. */ + } + + if ((local->func->card_present && !local->func->card_present(local)) || + local->hw_downloading) + return -ENODEV; + + rec.rid = cpu_to_le16(rid); + /* RID len in words and +1 for rec.rid */ + rec.len = cpu_to_le16(len / 2 + len % 2 + 1); + + res = mutex_lock_interruptible(&local->rid_bap_mtx); + if (res) + return res; + + spin_lock_bh(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, rid, 0); + if (!res) + res = hfa384x_to_bap(dev, BAP0, &rec, sizeof(rec)); + if (!res) + res = hfa384x_to_bap(dev, BAP0, buf, len); + spin_unlock_bh(&local->baplock); + + if (res) { + printk(KERN_DEBUG "%s: hfa384x_set_rid (rid=%04x, len=%d) - " + "failed - res=%d\n", dev->name, rid, len, res); + mutex_unlock(&local->rid_bap_mtx); + return res; + } + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS_WRITE, rid, NULL, NULL); + mutex_unlock(&local->rid_bap_mtx); + + if (res) { + printk(KERN_DEBUG "%s: hfa384x_set_rid: CMDCODE_ACCESS_WRITE " + "failed (res=%d, rid=%04x, len=%d)\n", + dev->name, res, rid, len); + + if (res == -ETIMEDOUT) + prism2_hw_reset(dev); + } + + return res; +} + + +static void hfa384x_disable_interrupts(struct net_device *dev) +{ + /* disable interrupts and clear event status */ + HFA384X_OUTW(0, HFA384X_INTEN_OFF); + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); +} + + +static void hfa384x_enable_interrupts(struct net_device *dev) +{ + /* ack pending events and enable interrupts from selected events */ + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_no_bap0(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EVENT_MASK & ~HFA384X_BAP0_EVENTS, + HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_all(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_only_cmd(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_INTEN_OFF); +} + + +static u16 hfa384x_allocate_fid(struct net_device *dev, int len) +{ + u16 fid; + unsigned long delay; + + /* FIX: this could be replace with hfa384x_cmd() if the Alloc event + * below would be handled like CmdCompl event (sleep here, wake up from + * interrupt handler */ + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_ALLOC, len)) { + printk(KERN_DEBUG "%s: cannot allocate fid, len=%d\n", + dev->name, len); + return 0xffff; + } + + delay = jiffies + HFA384X_ALLOC_COMPL_TIMEOUT; + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC) && + time_before(jiffies, delay)) + yield(); + if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC)) { + printk("%s: fid allocate, len=%d - timeout\n", dev->name, len); + return 0xffff; + } + + fid = HFA384X_INW(HFA384X_ALLOCFID_OFF); + HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF); + + return fid; +} + + +static int prism2_reset_port(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int res; + + iface = netdev_priv(dev); + local = iface->local; + + if (!local->dev_enabled) + return 0; + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, + NULL, NULL); + if (res) + printk(KERN_DEBUG "%s: reset port failed to disable port\n", + dev->name); + else { + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, + NULL, NULL); + if (res) + printk(KERN_DEBUG "%s: reset port failed to enable " + "port\n", dev->name); + } + + /* It looks like at least some STA firmware versions reset + * fragmentation threshold back to 2346 after enable command. Restore + * the configured value, if it differs from this default. */ + if (local->fragm_threshold != 2346 && + hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, + local->fragm_threshold)) { + printk(KERN_DEBUG "%s: failed to restore fragmentation " + "threshold (%d) after Port0 enable\n", + dev->name, local->fragm_threshold); + } + + /* Some firmwares lose antenna selection settings on reset */ + (void) hostap_set_antsel(local); + + return res; +} + + +static int prism2_get_version_info(struct net_device *dev, u16 rid, + const char *txt) +{ + struct hfa384x_comp_ident comp; + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->no_pri) { + /* PRI f/w not yet available - cannot read RIDs */ + return -1; + } + if (hfa384x_get_rid(dev, rid, &comp, sizeof(comp), 1) < 0) { + printk(KERN_DEBUG "Could not get RID for component %s\n", txt); + return -1; + } + + printk(KERN_INFO "%s: %s: id=0x%02x v%d.%d.%d\n", dev->name, txt, + __le16_to_cpu(comp.id), __le16_to_cpu(comp.major), + __le16_to_cpu(comp.minor), __le16_to_cpu(comp.variant)); + return 0; +} + + +static int prism2_setup_rids(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + __le16 tmp; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + hostap_set_word(dev, HFA384X_RID_TICKTIME, 2000); + + if (!local->fw_ap) { + u16 tmp1 = hostap_get_porttype(local); + ret = hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, tmp1); + if (ret) { + printk("%s: Port type setting to %d failed\n", + dev->name, tmp1); + goto fail; + } + } + + /* Setting SSID to empty string seems to kill the card in Host AP mode + */ + if (local->iw_mode != IW_MODE_MASTER || local->essid[0] != '\0') { + ret = hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, + local->essid); + if (ret) { + printk("%s: AP own SSID setting failed\n", dev->name); + goto fail; + } + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFMAXDATALEN, + PRISM2_DATA_MAXLEN); + if (ret) { + printk("%s: MAC data length setting to %d failed\n", + dev->name, PRISM2_DATA_MAXLEN); + goto fail; + } + + if (hfa384x_get_rid(dev, HFA384X_RID_CHANNELLIST, &tmp, 2, 1) < 0) { + printk("%s: Channel list read failed\n", dev->name); + ret = -EINVAL; + goto fail; + } + local->channel_mask = le16_to_cpu(tmp); + + if (local->channel < 1 || local->channel > 14 || + !(local->channel_mask & (1 << (local->channel - 1)))) { + printk(KERN_WARNING "%s: Channel setting out of range " + "(%d)!\n", dev->name, local->channel); + ret = -EBUSY; + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel); + if (ret) { + printk("%s: Channel setting to %d failed\n", + dev->name, local->channel); + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, + local->beacon_int); + if (ret) { + printk("%s: Beacon interval setting to %d failed\n", + dev->name, local->beacon_int); + /* this may fail with Symbol/Lucent firmware */ + if (ret == -ETIMEDOUT) + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, + local->dtim_period); + if (ret) { + printk("%s: DTIM period setting to %d failed\n", + dev->name, local->dtim_period); + /* this may fail with Symbol/Lucent firmware */ + if (ret == -ETIMEDOUT) + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, + local->is_promisc); + if (ret) + printk(KERN_INFO "%s: Setting promiscuous mode (%d) failed\n", + dev->name, local->is_promisc); + + if (!local->fw_ap) { + ret = hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, + local->essid); + if (ret) { + printk("%s: Desired SSID setting failed\n", dev->name); + goto fail; + } + } + + /* Setup TXRateControl, defaults to allow use of 1, 2, 5.5, and + * 11 Mbps in automatic TX rate fallback and 1 and 2 Mbps as basic + * rates */ + if (local->tx_rate_control == 0) { + local->tx_rate_control = + HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | + HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + } + if (local->basic_rates == 0) + local->basic_rates = HFA384X_RATES_1MBPS | HFA384X_RATES_2MBPS; + + if (!local->fw_ap) { + ret = hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, + local->tx_rate_control); + if (ret) { + printk("%s: TXRateControl setting to %d failed\n", + dev->name, local->tx_rate_control); + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, + local->tx_rate_control); + if (ret) { + printk("%s: cnfSupportedRates setting to %d failed\n", + dev->name, local->tx_rate_control); + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + local->basic_rates); + if (ret) { + printk("%s: cnfBasicRates setting to %d failed\n", + dev->name, local->basic_rates); + } + + ret = hostap_set_word(dev, HFA384X_RID_CREATEIBSS, 1); + if (ret) { + printk("%s: Create IBSS setting to 1 failed\n", + dev->name); + } + } + + if (local->name_set) + (void) hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, + local->name); + + if (hostap_set_encryption(local)) { + printk(KERN_INFO "%s: could not configure encryption\n", + dev->name); + } + + (void) hostap_set_antsel(local); + + if (hostap_set_roaming(local)) { + printk(KERN_INFO "%s: could not set host roaming\n", + dev->name); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,6,3) && + hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, local->enh_sec)) + printk(KERN_INFO "%s: cnfEnhSecurity setting to 0x%x failed\n", + dev->name, local->enh_sec); + + /* 32-bit tallies were added in STA f/w 0.8.0, but they were apparently + * not working correctly (last seven counters report bogus values). + * This has been fixed in 0.8.2, so enable 32-bit tallies only + * beginning with that firmware version. Another bug fix for 32-bit + * tallies in 1.4.0; should 16-bit tallies be used for some other + * versions, too? */ + if (local->sta_fw_ver >= PRISM2_FW_VER(0,8,2)) { + if (hostap_set_word(dev, HFA384X_RID_CNFTHIRTY2TALLY, 1)) { + printk(KERN_INFO "%s: cnfThirty2Tally setting " + "failed\n", dev->name); + local->tallies32 = 0; + } else + local->tallies32 = 1; + } else + local->tallies32 = 0; + + hostap_set_auth_algs(local); + + if (hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, + local->fragm_threshold)) { + printk(KERN_INFO "%s: setting FragmentationThreshold to %d " + "failed\n", dev->name, local->fragm_threshold); + } + + if (hostap_set_word(dev, HFA384X_RID_RTSTHRESHOLD, + local->rts_threshold)) { + printk(KERN_INFO "%s: setting RTSThreshold to %d failed\n", + dev->name, local->rts_threshold); + } + + if (local->manual_retry_count >= 0 && + hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT, + local->manual_retry_count)) { + printk(KERN_INFO "%s: setting cnfAltRetryCount to %d failed\n", + dev->name, local->manual_retry_count); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1) && + hfa384x_get_rid(dev, HFA384X_RID_CNFDBMADJUST, &tmp, 2, 1) == 2) { + local->rssi_to_dBm = le16_to_cpu(tmp); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->wpa && + hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1)) { + printk(KERN_INFO "%s: setting ssnHandlingMode to 1 failed\n", + dev->name); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->generic_elem && + hfa384x_set_rid(dev, HFA384X_RID_GENERICELEMENT, + local->generic_elem, local->generic_elem_len)) { + printk(KERN_INFO "%s: setting genericElement failed\n", + dev->name); + } + + fail: + return ret; +} + + +static int prism2_hw_init(struct net_device *dev, int initial) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret, first = 1; + unsigned long start, delay; + + PDEBUG(DEBUG_FLOW, "prism2_hw_init()\n"); + + iface = netdev_priv(dev); + local = iface->local; + + clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits); + + init: + /* initialize HFA 384x */ + ret = hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_INIT, 0); + if (ret) { + printk(KERN_INFO "%s: first command failed - assuming card " + "does not have primary firmware\n", dev_info); + } + + if (first && (HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) { + /* EvStat has Cmd bit set in some cases, so retry once if no + * wait was needed */ + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: init command completed too quickly - " + "retrying\n", dev->name); + first = 0; + goto init; + } + + start = jiffies; + delay = jiffies + HFA384X_INIT_TIMEOUT; + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && + time_before(jiffies, delay)) + yield(); + if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) { + printk(KERN_DEBUG "%s: assuming no Primary image in " + "flash - card initialization not completed\n", + dev_info); + local->no_pri = 1; +#ifdef PRISM2_DOWNLOAD_SUPPORT + if (local->sram_type == -1) + local->sram_type = prism2_get_ram_size(local); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + return 1; + } + local->no_pri = 0; + printk(KERN_DEBUG "prism2_hw_init: initialized in %lu ms\n", + (jiffies - start) * 1000 / HZ); + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + return 0; +} + + +static int prism2_hw_init2(struct net_device *dev, int initial) +{ + struct hostap_interface *iface; + local_info_t *local; + int i; + + iface = netdev_priv(dev); + local = iface->local; + +#ifdef PRISM2_DOWNLOAD_SUPPORT + kfree(local->pda); + if (local->no_pri) + local->pda = NULL; + else + local->pda = prism2_read_pda(dev); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + hfa384x_disable_interrupts(dev); + +#ifndef final_version + HFA384X_OUTW(HFA384X_MAGIC, HFA384X_SWSUPPORT0_OFF); + if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) { + printk("SWSUPPORT0 write/read failed: %04X != %04X\n", + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), HFA384X_MAGIC); + goto failed; + } +#endif + + if (initial || local->pri_only) { + hfa384x_events_only_cmd(dev); + /* get card version information */ + if (prism2_get_version_info(dev, HFA384X_RID_NICID, "NIC") || + prism2_get_version_info(dev, HFA384X_RID_PRIID, "PRI")) { + hfa384x_disable_interrupts(dev); + goto failed; + } + + if (prism2_get_version_info(dev, HFA384X_RID_STAID, "STA")) { + printk(KERN_DEBUG "%s: Failed to read STA f/w version " + "- only Primary f/w present\n", dev->name); + local->pri_only = 1; + return 0; + } + local->pri_only = 0; + hfa384x_disable_interrupts(dev); + } + + /* FIX: could convert allocate_fid to use sleeping CmdCompl wait and + * enable interrupts before this. This would also require some sort of + * sleeping AllocEv waiting */ + + /* allocate TX FIDs */ + local->txfid_len = PRISM2_TXFID_LEN; + for (i = 0; i < PRISM2_TXFID_COUNT; i++) { + local->txfid[i] = hfa384x_allocate_fid(dev, local->txfid_len); + if (local->txfid[i] == 0xffff && local->txfid_len > 1600) { + local->txfid[i] = hfa384x_allocate_fid(dev, 1600); + if (local->txfid[i] != 0xffff) { + printk(KERN_DEBUG "%s: Using shorter TX FID " + "(1600 bytes)\n", dev->name); + local->txfid_len = 1600; + } + } + if (local->txfid[i] == 0xffff) + goto failed; + local->intransmitfid[i] = PRISM2_TXFID_EMPTY; + } + + hfa384x_events_only_cmd(dev); + + if (initial) { + struct list_head *ptr; + prism2_check_sta_fw_version(local); + + if (hfa384x_get_rid(dev, HFA384X_RID_CNFOWNMACADDR, + dev->dev_addr, 6, 1) < 0) { + printk("%s: could not get own MAC address\n", + dev->name); + } + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + eth_hw_addr_inherit(iface->dev, dev); + } + } else if (local->fw_ap) + prism2_check_sta_fw_version(local); + + prism2_setup_rids(dev); + + /* MAC is now configured, but port 0 is not yet enabled */ + return 0; + + failed: + if (!local->no_pri) + printk(KERN_WARNING "%s: Initialization failed\n", dev_info); + return 1; +} + + +static int prism2_hw_enable(struct net_device *dev, int initial) +{ + struct hostap_interface *iface; + local_info_t *local; + int was_resetting; + + iface = netdev_priv(dev); + local = iface->local; + was_resetting = local->hw_resetting; + + if (hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, NULL)) { + printk("%s: MAC port 0 enabling failed\n", dev->name); + return 1; + } + + local->hw_ready = 1; + local->hw_reset_tries = 0; + local->hw_resetting = 0; + hfa384x_enable_interrupts(dev); + + /* at least D-Link DWL-650 seems to require additional port reset + * before it starts acting as an AP, so reset port automatically + * here just in case */ + if (initial && prism2_reset_port(dev)) { + printk("%s: MAC port 0 resetting failed\n", dev->name); + return 1; + } + + if (was_resetting && netif_queue_stopped(dev)) { + /* If hw_reset() was called during pending transmit, netif + * queue was stopped. Wake it up now since the wlan card has + * been resetted. */ + netif_wake_queue(dev); + } + + return 0; +} + + +static int prism2_hw_config(struct net_device *dev, int initial) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->hw_downloading) + return 1; + + if (prism2_hw_init(dev, initial)) { + return local->no_pri ? 0 : 1; + } + + if (prism2_hw_init2(dev, initial)) + return 1; + + /* Enable firmware if secondary image is loaded and at least one of the + * netdevices is up. */ + if (!local->pri_only && + (initial == 0 || (initial == 2 && local->num_dev_open > 0))) { + if (!local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_ENABLE); + local->dev_enabled = 1; + return prism2_hw_enable(dev, initial); + } + + return 0; +} + + +static void prism2_hw_shutdown(struct net_device *dev, int no_disable) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + /* Allow only command completion events during disable */ + hfa384x_events_only_cmd(dev); + + local->hw_ready = 0; + if (local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_DISABLE); + local->dev_enabled = 0; + + if (local->func->card_present && !local->func->card_present(local)) { + printk(KERN_DEBUG "%s: card already removed or not configured " + "during shutdown\n", dev->name); + return; + } + + if ((no_disable & HOSTAP_HW_NO_DISABLE) == 0 && + hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, NULL)) + printk(KERN_WARNING "%s: Shutdown failed\n", dev_info); + + hfa384x_disable_interrupts(dev); + + if (no_disable & HOSTAP_HW_ENABLE_CMDCOMPL) + hfa384x_events_only_cmd(dev); + else + prism2_clear_cmd_queue(local); +} + + +static void prism2_hw_reset(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + +#if 0 + static long last_reset = 0; + + /* do not reset card more than once per second to avoid ending up in a + * busy loop resetting the card */ + if (time_before_eq(jiffies, last_reset + HZ)) + return; + last_reset = jiffies; +#endif + + iface = netdev_priv(dev); + local = iface->local; + + if (in_interrupt()) { + printk(KERN_DEBUG "%s: driver bug - prism2_hw_reset() called " + "in interrupt context\n", dev->name); + return; + } + + if (local->hw_downloading) + return; + + if (local->hw_resetting) { + printk(KERN_WARNING "%s: %s: already resetting card - " + "ignoring reset request\n", dev_info, dev->name); + return; + } + + local->hw_reset_tries++; + if (local->hw_reset_tries > 10) { + printk(KERN_WARNING "%s: too many reset tries, skipping\n", + dev->name); + return; + } + + printk(KERN_WARNING "%s: %s: resetting card\n", dev_info, dev->name); + hfa384x_disable_interrupts(dev); + local->hw_resetting = 1; + if (local->func->cor_sreset) { + /* Host system seems to hang in some cases with high traffic + * load or shared interrupts during COR sreset. Disable shared + * interrupts during reset to avoid these crashes. COS sreset + * takes quite a long time, so it is unfortunate that this + * seems to be needed. Anyway, I do not know of any better way + * of avoiding the crash. */ + disable_irq(dev->irq); + local->func->cor_sreset(local); + enable_irq(dev->irq); + } + prism2_hw_shutdown(dev, 1); + prism2_hw_config(dev, 0); + local->hw_resetting = 0; + +#ifdef PRISM2_DOWNLOAD_SUPPORT + if (local->dl_pri) { + printk(KERN_DEBUG "%s: persistent download of primary " + "firmware\n", dev->name); + if (prism2_download_genesis(local, local->dl_pri) < 0) + printk(KERN_WARNING "%s: download (PRI) failed\n", + dev->name); + } + + if (local->dl_sec) { + printk(KERN_DEBUG "%s: persistent download of secondary " + "firmware\n", dev->name); + if (prism2_download_volatile(local, local->dl_sec) < 0) + printk(KERN_WARNING "%s: download (SEC) failed\n", + dev->name); + } +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + /* TODO: restore beacon TIM bits for STAs that have buffered frames */ +} + + +static void prism2_schedule_reset(local_info_t *local) +{ + schedule_work(&local->reset_queue); +} + + +/* Called only as scheduled task after noticing card timeout in interrupt + * context */ +static void handle_reset_queue(struct work_struct *work) +{ + local_info_t *local = container_of(work, local_info_t, reset_queue); + + printk(KERN_DEBUG "%s: scheduled card reset\n", local->dev->name); + prism2_hw_reset(local->dev); + + if (netif_queue_stopped(local->dev)) { + int i; + + for (i = 0; i < PRISM2_TXFID_COUNT; i++) + if (local->intransmitfid[i] == PRISM2_TXFID_EMPTY) { + PDEBUG(DEBUG_EXTRA, "prism2_tx_timeout: " + "wake up queue\n"); + netif_wake_queue(local->dev); + break; + } + } +} + + +static int prism2_get_txfid_idx(local_info_t *local) +{ + int idx, end; + unsigned long flags; + + spin_lock_irqsave(&local->txfidlock, flags); + end = idx = local->next_txfid; + do { + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { + local->intransmitfid[idx] = PRISM2_TXFID_RESERVED; + spin_unlock_irqrestore(&local->txfidlock, flags); + return idx; + } + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != end); + spin_unlock_irqrestore(&local->txfidlock, flags); + + PDEBUG(DEBUG_EXTRA2, "prism2_get_txfid_idx: no room in txfid buf: " + "packet dropped\n"); + local->dev->stats.tx_dropped++; + + return -1; +} + + +/* Called only from hardware IRQ */ +static void prism2_transmit_cb(struct net_device *dev, long context, + u16 resp0, u16 res) +{ + struct hostap_interface *iface; + local_info_t *local; + int idx = (int) context; + + iface = netdev_priv(dev); + local = iface->local; + + if (res) { + printk(KERN_DEBUG "%s: prism2_transmit_cb - res=0x%02x\n", + dev->name, res); + return; + } + + if (idx < 0 || idx >= PRISM2_TXFID_COUNT) { + printk(KERN_DEBUG "%s: prism2_transmit_cb called with invalid " + "idx=%d\n", dev->name, idx); + return; + } + + if (!test_and_clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: driver bug: prism2_transmit_cb called " + "with no pending transmit\n", dev->name); + } + + if (netif_queue_stopped(dev)) { + /* ready for next TX, so wake up queue that was stopped in + * prism2_transmit() */ + netif_wake_queue(dev); + } + + spin_lock(&local->txfidlock); + + /* With reclaim, Resp0 contains new txfid for transmit; the old txfid + * will be automatically allocated for the next TX frame */ + local->intransmitfid[idx] = resp0; + + PDEBUG(DEBUG_FID, "%s: prism2_transmit_cb: txfid[%d]=0x%04x, " + "resp0=0x%04x, transmit_txfid=0x%04x\n", + dev->name, idx, local->txfid[idx], + resp0, local->intransmitfid[local->next_txfid]); + + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + local->next_txfid = idx; + + /* check if all TX buffers are occupied */ + do { + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { + spin_unlock(&local->txfidlock); + return; + } + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != local->next_txfid); + spin_unlock(&local->txfidlock); + + /* no empty TX buffers, stop queue */ + netif_stop_queue(dev); +} + + +/* Called only from software IRQ if PCI bus master is not used (with bus master + * this can be called both from software and hardware IRQ) */ +static int prism2_transmit(struct net_device *dev, int idx) +{ + struct hostap_interface *iface; + local_info_t *local; + int res; + + iface = netdev_priv(dev); + local = iface->local; + + /* The driver tries to stop netif queue so that there would not be + * more than one attempt to transmit frames going on; check that this + * is really the case */ + + if (test_and_set_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: driver bug - prism2_transmit() called " + "when previous TX was pending\n", dev->name); + return -1; + } + + /* stop the queue for the time that transmit is pending */ + netif_stop_queue(dev); + + /* transmit packet */ + res = hfa384x_cmd_callback( + dev, + HFA384X_CMDCODE_TRANSMIT | HFA384X_CMD_TX_RECLAIM, + local->txfid[idx], + prism2_transmit_cb, (long) idx); + + if (res) { + printk(KERN_DEBUG "%s: prism2_transmit: CMDCODE_TRANSMIT " + "failed (res=%d)\n", dev->name, res); + dev->stats.tx_dropped++; + netif_wake_queue(dev); + return -1; + } + dev->trans_start = jiffies; + + /* Since we did not wait for command completion, the card continues + * to process on the background and we will finish handling when + * command completion event is handled (prism2_cmd_ev() function) */ + + return 0; +} + + +/* Send IEEE 802.11 frame (convert the header into Prism2 TX descriptor and + * send the payload with this descriptor) */ +/* Called only from software IRQ */ +static int prism2_tx_80211(struct sk_buff *skb, struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_tx_frame txdesc; + struct hostap_skb_tx_data *meta; + int hdr_len, data_len, idx, res, ret = -1; + u16 tx_control, fc; + + iface = netdev_priv(dev); + local = iface->local; + + meta = (struct hostap_skb_tx_data *) skb->cb; + + prism2_callback(local, PRISM2_CALLBACK_TX_START); + + if ((local->func->card_present && !local->func->card_present(local)) || + !local->hw_ready || local->hw_downloading || local->pri_only) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: prism2_tx_80211: hw not ready -" + " skipping\n", dev->name); + } + goto fail; + } + + memset(&txdesc, 0, sizeof(txdesc)); + + /* skb->data starts with txdesc->frame_control */ + hdr_len = 24; + skb_copy_from_linear_data(skb, &txdesc.frame_control, hdr_len); + fc = le16_to_cpu(txdesc.frame_control); + if (ieee80211_is_data(txdesc.frame_control) && + ieee80211_has_a4(txdesc.frame_control) && + skb->len >= 30) { + /* Addr4 */ + skb_copy_from_linear_data_offset(skb, hdr_len, txdesc.addr4, + ETH_ALEN); + hdr_len += ETH_ALEN; + } + + tx_control = local->tx_control; + if (meta->tx_cb_idx) { + tx_control |= HFA384X_TX_CTRL_TX_OK; + txdesc.sw_support = cpu_to_le32(meta->tx_cb_idx); + } + txdesc.tx_control = cpu_to_le16(tx_control); + txdesc.tx_rate = meta->rate; + + data_len = skb->len - hdr_len; + txdesc.data_len = cpu_to_le16(data_len); + txdesc.len = cpu_to_be16(data_len); + + idx = prism2_get_txfid_idx(local); + if (idx < 0) + goto fail; + + if (local->frame_dump & PRISM2_DUMP_TX_HDR) + hostap_dump_tx_header(dev->name, &txdesc); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, local->txfid[idx], 0); + + if (!res) + res = hfa384x_to_bap(dev, BAP0, &txdesc, sizeof(txdesc)); + if (!res) + res = hfa384x_to_bap(dev, BAP0, skb->data + hdr_len, + skb->len - hdr_len); + spin_unlock(&local->baplock); + + if (!res) + res = prism2_transmit(dev, idx); + if (res) { + printk(KERN_DEBUG "%s: prism2_tx_80211 - to BAP0 failed\n", + dev->name); + local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; + schedule_work(&local->reset_queue); + goto fail; + } + + ret = 0; + +fail: + prism2_callback(local, PRISM2_CALLBACK_TX_END); + return ret; +} + + +/* Some SMP systems have reported number of odd errors with hostap_pci. fid + * register has changed values between consecutive reads for an unknown reason. + * This should really not happen, so more debugging is needed. This test + * version is a bit slower, but it will detect most of such register changes + * and will try to get the correct fid eventually. */ +#define EXTRA_FID_READ_TESTS + +static u16 prism2_read_fid_reg(struct net_device *dev, u16 reg) +{ +#ifdef EXTRA_FID_READ_TESTS + u16 val, val2, val3; + int i; + + for (i = 0; i < 10; i++) { + val = HFA384X_INW(reg); + val2 = HFA384X_INW(reg); + val3 = HFA384X_INW(reg); + + if (val == val2 && val == val3) + return val; + + printk(KERN_DEBUG "%s: detected fid change (try=%d, reg=%04x):" + " %04x %04x %04x\n", + dev->name, i, reg, val, val2, val3); + if ((val == val2 || val == val3) && val != 0) + return val; + if (val2 == val3 && val2 != 0) + return val2; + } + printk(KERN_WARNING "%s: Uhhuh.. could not read good fid from reg " + "%04x (%04x %04x %04x)\n", dev->name, reg, val, val2, val3); + return val; +#else /* EXTRA_FID_READ_TESTS */ + return HFA384X_INW(reg); +#endif /* EXTRA_FID_READ_TESTS */ +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_rx(local_info_t *local) +{ + struct net_device *dev = local->dev; + int res, rx_pending = 0; + u16 len, hdr_len, rxfid, status, macport; + struct hfa384x_rx_frame rxdesc; + struct sk_buff *skb = NULL; + + prism2_callback(local, PRISM2_CALLBACK_RX_START); + + rxfid = prism2_read_fid_reg(dev, HFA384X_RXFID_OFF); +#ifndef final_version + if (rxfid == 0) { + rxfid = HFA384X_INW(HFA384X_RXFID_OFF); + printk(KERN_DEBUG "prism2_rx: rxfid=0 (next 0x%04x)\n", + rxfid); + if (rxfid == 0) { + schedule_work(&local->reset_queue); + goto rx_dropped; + } + /* try to continue with the new rxfid value */ + } +#endif + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, rxfid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &rxdesc, sizeof(rxdesc)); + + if (res) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: copy from BAP0 failed %d\n", dev->name, + res); + if (res == -ETIMEDOUT) { + schedule_work(&local->reset_queue); + } + goto rx_dropped; + } + + len = le16_to_cpu(rxdesc.data_len); + hdr_len = sizeof(rxdesc); + status = le16_to_cpu(rxdesc.status); + macport = (status >> 8) & 0x07; + + /* Drop frames with too large reported payload length. Monitor mode + * seems to sometimes pass frames (e.g., ctrl::ack) with signed and + * negative value, so allow also values 65522 .. 65534 (-14 .. -2) for + * macport 7 */ + if (len > PRISM2_DATA_MAXLEN + 8 /* WEP */) { + if (macport == 7 && local->iw_mode == IW_MODE_MONITOR) { + if (len >= (u16) -14) { + hdr_len -= 65535 - len; + hdr_len--; + } + len = 0; + } else { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Received frame with invalid " + "length 0x%04x\n", dev->name, len); + hostap_dump_rx_header(dev->name, &rxdesc); + goto rx_dropped; + } + } + + skb = dev_alloc_skb(len + hdr_len); + if (!skb) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: RX failed to allocate skb\n", + dev->name); + goto rx_dropped; + } + skb->dev = dev; + memcpy(skb_put(skb, hdr_len), &rxdesc, hdr_len); + + if (len > 0) + res = hfa384x_from_bap(dev, BAP0, skb_put(skb, len), len); + spin_unlock(&local->baplock); + if (res) { + printk(KERN_DEBUG "%s: RX failed to read " + "frame data\n", dev->name); + goto rx_dropped; + } + + skb_queue_tail(&local->rx_list, skb); + tasklet_schedule(&local->rx_tasklet); + + rx_exit: + prism2_callback(local, PRISM2_CALLBACK_RX_END); + if (!rx_pending) { + HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); + } + + return; + + rx_dropped: + dev->stats.rx_dropped++; + if (skb) + dev_kfree_skb(skb); + goto rx_exit; +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_rx_skb(local_info_t *local, struct sk_buff *skb) +{ + struct hfa384x_rx_frame *rxdesc; + struct net_device *dev = skb->dev; + struct hostap_80211_rx_status stats; + int hdrlen, rx_hdrlen; + + rx_hdrlen = sizeof(*rxdesc); + if (skb->len < sizeof(*rxdesc)) { + /* Allow monitor mode to receive shorter frames */ + if (local->iw_mode == IW_MODE_MONITOR && + skb->len >= sizeof(*rxdesc) - 30) { + rx_hdrlen = skb->len; + } else { + dev_kfree_skb(skb); + return; + } + } + + rxdesc = (struct hfa384x_rx_frame *) skb->data; + + if (local->frame_dump & PRISM2_DUMP_RX_HDR && + skb->len >= sizeof(*rxdesc)) + hostap_dump_rx_header(dev->name, rxdesc); + + if (le16_to_cpu(rxdesc->status) & HFA384X_RX_STATUS_FCSERR && + (!local->monitor_allow_fcserr || + local->iw_mode != IW_MODE_MONITOR)) + goto drop; + + if (skb->len > PRISM2_DATA_MAXLEN) { + printk(KERN_DEBUG "%s: RX: len(%d) > MAX(%d)\n", + dev->name, skb->len, PRISM2_DATA_MAXLEN); + goto drop; + } + + stats.mac_time = le32_to_cpu(rxdesc->time); + stats.signal = rxdesc->signal - local->rssi_to_dBm; + stats.noise = rxdesc->silence - local->rssi_to_dBm; + stats.rate = rxdesc->rate; + + /* Convert Prism2 RX structure into IEEE 802.11 header */ + hdrlen = hostap_80211_get_hdrlen(rxdesc->frame_control); + if (hdrlen > rx_hdrlen) + hdrlen = rx_hdrlen; + + memmove(skb_pull(skb, rx_hdrlen - hdrlen), + &rxdesc->frame_control, hdrlen); + + hostap_80211_rx(dev, skb, &stats); + return; + + drop: + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_rx_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->rx_list)) != NULL) + hostap_rx_skb(local, skb); +} + + +/* Called only from hardware IRQ */ +static void prism2_alloc_ev(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int idx; + u16 fid; + + iface = netdev_priv(dev); + local = iface->local; + + fid = prism2_read_fid_reg(dev, HFA384X_ALLOCFID_OFF); + + PDEBUG(DEBUG_FID, "FID: interrupt: ALLOC - fid=0x%04x\n", fid); + + spin_lock(&local->txfidlock); + idx = local->next_alloc; + + do { + if (local->txfid[idx] == fid) { + PDEBUG(DEBUG_FID, "FID: found matching txfid[%d]\n", + idx); + +#ifndef final_version + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) + printk("Already released txfid found at idx " + "%d\n", idx); + if (local->intransmitfid[idx] == PRISM2_TXFID_RESERVED) + printk("Already reserved txfid found at idx " + "%d\n", idx); +#endif + local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; + idx++; + local->next_alloc = idx >= PRISM2_TXFID_COUNT ? 0 : + idx; + + if (!test_bit(HOSTAP_BITS_TRANSMIT, &local->bits) && + netif_queue_stopped(dev)) + netif_wake_queue(dev); + + spin_unlock(&local->txfidlock); + return; + } + + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != local->next_alloc); + + printk(KERN_WARNING "%s: could not find matching txfid (0x%04x, new " + "read 0x%04x) for alloc event\n", dev->name, fid, + HFA384X_INW(HFA384X_ALLOCFID_OFF)); + printk(KERN_DEBUG "TXFIDs:"); + for (idx = 0; idx < PRISM2_TXFID_COUNT; idx++) + printk(" %04x[%04x]", local->txfid[idx], + local->intransmitfid[idx]); + printk("\n"); + spin_unlock(&local->txfidlock); + + /* FIX: should probably schedule reset; reference to one txfid was lost + * completely.. Bad things will happen if we run out of txfids + * Actually, this will cause netdev watchdog to notice TX timeout and + * then card reset after all txfids have been leaked. */ +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_tx_callback(local_info_t *local, + struct hfa384x_tx_frame *txdesc, int ok, + char *payload) +{ + u16 sw_support, hdrlen, len; + struct sk_buff *skb; + struct hostap_tx_callback_info *cb; + + /* Make sure that frame was from us. */ + if (!ether_addr_equal(txdesc->addr2, local->dev->dev_addr)) { + printk(KERN_DEBUG "%s: TX callback - foreign frame\n", + local->dev->name); + return; + } + + sw_support = le32_to_cpu(txdesc->sw_support); + + spin_lock(&local->lock); + cb = local->tx_callback; + while (cb != NULL && cb->idx != sw_support) + cb = cb->next; + spin_unlock(&local->lock); + + if (cb == NULL) { + printk(KERN_DEBUG "%s: could not find TX callback (idx %d)\n", + local->dev->name, sw_support); + return; + } + + hdrlen = hostap_80211_get_hdrlen(txdesc->frame_control); + len = le16_to_cpu(txdesc->data_len); + skb = dev_alloc_skb(hdrlen + len); + if (skb == NULL) { + printk(KERN_DEBUG "%s: hostap_tx_callback failed to allocate " + "skb\n", local->dev->name); + return; + } + + memcpy(skb_put(skb, hdrlen), (void *) &txdesc->frame_control, hdrlen); + if (payload) + memcpy(skb_put(skb, len), payload, len); + + skb->dev = local->dev; + skb_reset_mac_header(skb); + + cb->func(skb, ok, cb->data); +} + + +/* Called only as a tasklet (software IRQ) */ +static int hostap_tx_compl_read(local_info_t *local, int error, + struct hfa384x_tx_frame *txdesc, + char **payload) +{ + u16 fid, len; + int res, ret = 0; + struct net_device *dev = local->dev; + + fid = prism2_read_fid_reg(dev, HFA384X_TXCOMPLFID_OFF); + + PDEBUG(DEBUG_FID, "interrupt: TX (err=%d) - fid=0x%04x\n", fid, error); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, fid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, txdesc, sizeof(*txdesc)); + if (res) { + PDEBUG(DEBUG_EXTRA, "%s: TX (err=%d) - fid=0x%04x - could not " + "read txdesc\n", dev->name, error, fid); + if (res == -ETIMEDOUT) { + schedule_work(&local->reset_queue); + } + ret = -1; + goto fail; + } + if (txdesc->sw_support) { + len = le16_to_cpu(txdesc->data_len); + if (len < PRISM2_DATA_MAXLEN) { + *payload = kmalloc(len, GFP_ATOMIC); + if (*payload == NULL || + hfa384x_from_bap(dev, BAP0, *payload, len)) { + PDEBUG(DEBUG_EXTRA, "%s: could not read TX " + "frame payload\n", dev->name); + kfree(*payload); + *payload = NULL; + ret = -1; + goto fail; + } + } + } + + fail: + spin_unlock(&local->baplock); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_tx_ev(local_info_t *local) +{ + struct net_device *dev = local->dev; + char *payload = NULL; + struct hfa384x_tx_frame txdesc; + + if (hostap_tx_compl_read(local, 0, &txdesc, &payload)) + goto fail; + + if (local->frame_dump & PRISM2_DUMP_TX_HDR) { + PDEBUG(DEBUG_EXTRA, "%s: TX - status=0x%04x " + "retry_count=%d tx_rate=%d seq_ctrl=%d " + "duration_id=%d\n", + dev->name, le16_to_cpu(txdesc.status), + txdesc.retry_count, txdesc.tx_rate, + le16_to_cpu(txdesc.seq_ctrl), + le16_to_cpu(txdesc.duration_id)); + } + + if (txdesc.sw_support) + hostap_tx_callback(local, &txdesc, 1, payload); + kfree(payload); + + fail: + HFA384X_OUTW(HFA384X_EV_TX, HFA384X_EVACK_OFF); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_sta_tx_exc_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->sta_tx_exc_list)) != NULL) { + struct hfa384x_tx_frame *txdesc = + (struct hfa384x_tx_frame *) skb->data; + + if (skb->len >= sizeof(*txdesc)) { + /* Convert Prism2 RX structure into IEEE 802.11 header + */ + int hdrlen = hostap_80211_get_hdrlen(txdesc->frame_control); + memmove(skb_pull(skb, sizeof(*txdesc) - hdrlen), + &txdesc->frame_control, hdrlen); + + hostap_handle_sta_tx_exc(local, skb); + } + dev_kfree_skb(skb); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_txexc(local_info_t *local) +{ + struct net_device *dev = local->dev; + u16 status, fc; + int show_dump, res; + char *payload = NULL; + struct hfa384x_tx_frame txdesc; + + show_dump = local->frame_dump & PRISM2_DUMP_TXEXC_HDR; + dev->stats.tx_errors++; + + res = hostap_tx_compl_read(local, 1, &txdesc, &payload); + HFA384X_OUTW(HFA384X_EV_TXEXC, HFA384X_EVACK_OFF); + if (res) + return; + + status = le16_to_cpu(txdesc.status); + + /* We produce a TXDROP event only for retry or lifetime + * exceeded, because that's the only status that really mean + * that this particular node went away. + * Other errors means that *we* screwed up. - Jean II */ + if (status & (HFA384X_TX_STATUS_RETRYERR | HFA384X_TX_STATUS_AGEDERR)) + { + union iwreq_data wrqu; + + /* Copy 802.11 dest address. */ + memcpy(wrqu.addr.sa_data, txdesc.addr1, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); + } else + show_dump = 1; + + if (local->iw_mode == IW_MODE_MASTER || + local->iw_mode == IW_MODE_REPEAT || + local->wds_type & HOSTAP_WDS_AP_CLIENT) { + struct sk_buff *skb; + skb = dev_alloc_skb(sizeof(txdesc)); + if (skb) { + memcpy(skb_put(skb, sizeof(txdesc)), &txdesc, + sizeof(txdesc)); + skb_queue_tail(&local->sta_tx_exc_list, skb); + tasklet_schedule(&local->sta_tx_exc_tasklet); + } + } + + if (txdesc.sw_support) + hostap_tx_callback(local, &txdesc, 0, payload); + kfree(payload); + + if (!show_dump) + return; + + PDEBUG(DEBUG_EXTRA, "%s: TXEXC - status=0x%04x (%s%s%s%s)" + " tx_control=%04x\n", + dev->name, status, + status & HFA384X_TX_STATUS_RETRYERR ? "[RetryErr]" : "", + status & HFA384X_TX_STATUS_AGEDERR ? "[AgedErr]" : "", + status & HFA384X_TX_STATUS_DISCON ? "[Discon]" : "", + status & HFA384X_TX_STATUS_FORMERR ? "[FormErr]" : "", + le16_to_cpu(txdesc.tx_control)); + + fc = le16_to_cpu(txdesc.frame_control); + PDEBUG(DEBUG_EXTRA, " retry_count=%d tx_rate=%d fc=0x%04x " + "(%s%s%s::%d%s%s)\n", + txdesc.retry_count, txdesc.tx_rate, fc, + ieee80211_is_mgmt(txdesc.frame_control) ? "Mgmt" : "", + ieee80211_is_ctl(txdesc.frame_control) ? "Ctrl" : "", + ieee80211_is_data(txdesc.frame_control) ? "Data" : "", + (fc & IEEE80211_FCTL_STYPE) >> 4, + ieee80211_has_tods(txdesc.frame_control) ? " ToDS" : "", + ieee80211_has_fromds(txdesc.frame_control) ? " FromDS" : ""); + PDEBUG(DEBUG_EXTRA, " A1=%pM A2=%pM A3=%pM A4=%pM\n", + txdesc.addr1, txdesc.addr2, + txdesc.addr3, txdesc.addr4); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_info_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->info_list)) != NULL) { + hostap_info_process(local, skb); + dev_kfree_skb(skb); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info(local_info_t *local) +{ + struct net_device *dev = local->dev; + u16 fid; + int res, left; + struct hfa384x_info_frame info; + struct sk_buff *skb; + + fid = HFA384X_INW(HFA384X_INFOFID_OFF); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, fid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &info, sizeof(info)); + if (res) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "Could not get info frame (fid=0x%04x)\n", + fid); + if (res == -ETIMEDOUT) { + schedule_work(&local->reset_queue); + } + goto out; + } + + left = (le16_to_cpu(info.len) - 1) * 2; + + if (info.len & cpu_to_le16(0x8000) || info.len == 0 || left > 2060) { + /* data register seems to give 0x8000 in some error cases even + * though busy bit is not set in offset register; + * in addition, length must be at least 1 due to type field */ + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Received info frame with invalid " + "length 0x%04x (type 0x%04x)\n", dev->name, + le16_to_cpu(info.len), le16_to_cpu(info.type)); + goto out; + } + + skb = dev_alloc_skb(sizeof(info) + left); + if (skb == NULL) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Could not allocate skb for info " + "frame\n", dev->name); + goto out; + } + + memcpy(skb_put(skb, sizeof(info)), &info, sizeof(info)); + if (left > 0 && hfa384x_from_bap(dev, BAP0, skb_put(skb, left), left)) + { + spin_unlock(&local->baplock); + printk(KERN_WARNING "%s: Info frame read failed (fid=0x%04x, " + "len=0x%04x, type=0x%04x\n", dev->name, fid, + le16_to_cpu(info.len), le16_to_cpu(info.type)); + dev_kfree_skb(skb); + goto out; + } + spin_unlock(&local->baplock); + + skb_queue_tail(&local->info_list, skb); + tasklet_schedule(&local->info_tasklet); + + out: + HFA384X_OUTW(HFA384X_EV_INFO, HFA384X_EVACK_OFF); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_bap_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct net_device *dev = local->dev; + u16 ev; + int frames = 30; + + if (local->func->card_present && !local->func->card_present(local)) + return; + + set_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits); + + /* Process all pending BAP events without generating new interrupts + * for them */ + while (frames-- > 0) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev == 0xffff || !(ev & HFA384X_BAP0_EVENTS)) + break; + if (ev & HFA384X_EV_RX) + prism2_rx(local); + if (ev & HFA384X_EV_INFO) + prism2_info(local); + if (ev & HFA384X_EV_TX) + prism2_tx_ev(local); + if (ev & HFA384X_EV_TXEXC) + prism2_txexc(local); + } + + set_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits); + clear_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits); + + /* Enable interrupts for new BAP events */ + hfa384x_events_all(dev); + clear_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits); +} + + +/* Called only from hardware IRQ */ +static void prism2_infdrop(struct net_device *dev) +{ + static unsigned long last_inquire = 0; + + PDEBUG(DEBUG_EXTRA, "%s: INFDROP event\n", dev->name); + + /* some firmware versions seem to get stuck with + * full CommTallies in high traffic load cases; every + * packet will then cause INFDROP event and CommTallies + * info frame will not be sent automatically. Try to + * get out of this state by inquiring CommTallies. */ + if (!last_inquire || time_after(jiffies, last_inquire + HZ)) { + hfa384x_cmd_callback(dev, HFA384X_CMDCODE_INQUIRE, + HFA384X_INFO_COMMTALLIES, NULL, 0); + last_inquire = jiffies; + } +} + + +/* Called only from hardware IRQ */ +static void prism2_ev_tick(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 evstat, inten; + static int prev_stuck = 0; + + iface = netdev_priv(dev); + local = iface->local; + + if (time_after(jiffies, local->last_tick_timer + 5 * HZ) && + local->last_tick_timer) { + evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); + inten = HFA384X_INW(HFA384X_INTEN_OFF); + if (!prev_stuck) { + printk(KERN_INFO "%s: SW TICK stuck? " + "bits=0x%lx EvStat=%04x IntEn=%04x\n", + dev->name, local->bits, evstat, inten); + } + local->sw_tick_stuck++; + if ((evstat & HFA384X_BAP0_EVENTS) && + (inten & HFA384X_BAP0_EVENTS)) { + printk(KERN_INFO "%s: trying to recover from IRQ " + "hang\n", dev->name); + hfa384x_events_no_bap0(dev); + } + prev_stuck = 1; + } else + prev_stuck = 0; +} + + +/* Called only from hardware IRQ */ +static void prism2_check_magic(local_info_t *local) +{ + /* at least PCI Prism2.5 with bus mastering seems to sometimes + * return 0x0000 in SWSUPPORT0 for unknown reason, but re-reading the + * register once or twice seems to get the correct value.. PCI cards + * cannot anyway be removed during normal operation, so there is not + * really any need for this verification with them. */ + +#ifndef PRISM2_PCI +#ifndef final_version + static unsigned long last_magic_err = 0; + struct net_device *dev = local->dev; + + if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) { + if (!local->hw_ready) + return; + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + if (time_after(jiffies, last_magic_err + 10 * HZ)) { + printk("%s: Interrupt, but SWSUPPORT0 does not match: " + "%04X != %04X - card removed?\n", dev->name, + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), + HFA384X_MAGIC); + last_magic_err = jiffies; + } else if (net_ratelimit()) { + printk(KERN_DEBUG "%s: interrupt - SWSUPPORT0=%04x " + "MAGIC=%04x\n", dev->name, + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), + HFA384X_MAGIC); + } + if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != 0xffff) + schedule_work(&local->reset_queue); + return; + } +#endif /* final_version */ +#endif /* !PRISM2_PCI */ +} + + +/* Called only from hardware IRQ */ +static irqreturn_t prism2_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct hostap_interface *iface; + local_info_t *local; + int events = 0; + u16 ev; + + iface = netdev_priv(dev); + local = iface->local; + + /* Detect early interrupt before driver is fully configured */ + spin_lock(&local->irq_init_lock); + if (!dev->base_addr) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: Interrupt, but dev not configured\n", + dev->name); + } + spin_unlock(&local->irq_init_lock); + return IRQ_HANDLED; + } + spin_unlock(&local->irq_init_lock); + + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0); + + if (local->func->card_present && !local->func->card_present(local)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: Interrupt, but dev not OK\n", + dev->name); + } + return IRQ_HANDLED; + } + + prism2_check_magic(local); + + for (;;) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev == 0xffff) { + if (local->shutdown) + return IRQ_HANDLED; + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: prism2_interrupt: ev=0xffff\n", + dev->name); + return IRQ_HANDLED; + } + + ev &= HFA384X_INW(HFA384X_INTEN_OFF); + if (ev == 0) + break; + + if (ev & HFA384X_EV_CMD) { + prism2_cmd_ev(dev); + } + + /* Above events are needed even before hw is ready, but other + * events should be skipped during initialization. This may + * change for AllocEv if allocate_fid is implemented without + * busy waiting. */ + if (!local->hw_ready || local->hw_resetting || + !local->dev_enabled) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev & HFA384X_EV_CMD) + goto next_event; + if ((ev & HFA384X_EVENT_MASK) == 0) + return IRQ_HANDLED; + if (local->dev_enabled && (ev & ~HFA384X_EV_TICK) && + net_ratelimit()) { + printk(KERN_DEBUG "%s: prism2_interrupt: hw " + "not ready; skipping events 0x%04x " + "(IntEn=0x%04x)%s%s%s\n", + dev->name, ev, + HFA384X_INW(HFA384X_INTEN_OFF), + !local->hw_ready ? " (!hw_ready)" : "", + local->hw_resetting ? + " (hw_resetting)" : "", + !local->dev_enabled ? + " (!dev_enabled)" : ""); + } + HFA384X_OUTW(ev, HFA384X_EVACK_OFF); + return IRQ_HANDLED; + } + + if (ev & HFA384X_EV_TICK) { + prism2_ev_tick(dev); + HFA384X_OUTW(HFA384X_EV_TICK, HFA384X_EVACK_OFF); + } + + if (ev & HFA384X_EV_ALLOC) { + prism2_alloc_ev(dev); + HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF); + } + + /* Reading data from the card is quite time consuming, so do it + * in tasklets. TX, TXEXC, RX, and INFO events will be ACKed + * and unmasked after needed data has been read completely. */ + if (ev & HFA384X_BAP0_EVENTS) { + hfa384x_events_no_bap0(dev); + tasklet_schedule(&local->bap_tasklet); + } + +#ifndef final_version + if (ev & HFA384X_EV_WTERR) { + PDEBUG(DEBUG_EXTRA, "%s: WTERR event\n", dev->name); + HFA384X_OUTW(HFA384X_EV_WTERR, HFA384X_EVACK_OFF); + } +#endif /* final_version */ + + if (ev & HFA384X_EV_INFDROP) { + prism2_infdrop(dev); + HFA384X_OUTW(HFA384X_EV_INFDROP, HFA384X_EVACK_OFF); + } + + next_event: + events++; + if (events >= PRISM2_MAX_INTERRUPT_EVENTS) { + PDEBUG(DEBUG_EXTRA, "prism2_interrupt: >%d events " + "(EvStat=0x%04x)\n", + PRISM2_MAX_INTERRUPT_EVENTS, + HFA384X_INW(HFA384X_EVSTAT_OFF)); + break; + } + } + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 1); + return IRQ_RETVAL(events); +} + + +static void prism2_check_sta_fw_version(local_info_t *local) +{ + struct hfa384x_comp_ident comp; + int id, variant, major, minor; + + if (hfa384x_get_rid(local->dev, HFA384X_RID_STAID, + &comp, sizeof(comp), 1) < 0) + return; + + local->fw_ap = 0; + id = le16_to_cpu(comp.id); + if (id != HFA384X_COMP_ID_STA) { + if (id == HFA384X_COMP_ID_FW_AP) + local->fw_ap = 1; + return; + } + + major = __le16_to_cpu(comp.major); + minor = __le16_to_cpu(comp.minor); + variant = __le16_to_cpu(comp.variant); + local->sta_fw_ver = PRISM2_FW_VER(major, minor, variant); + + /* Station firmware versions before 1.4.x seem to have a bug in + * firmware-based WEP encryption when using Host AP mode, so use + * host_encrypt as a default for them. Firmware version 1.4.9 is the + * first one that has been seen to produce correct encryption, but the + * bug might be fixed before that (although, at least 1.4.2 is broken). + */ + local->fw_encrypt_ok = local->sta_fw_ver >= PRISM2_FW_VER(1,4,9); + + if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt && + !local->fw_encrypt_ok) { + printk(KERN_DEBUG "%s: defaulting to host-based encryption as " + "a workaround for firmware bug in Host AP mode WEP\n", + local->dev->name); + local->host_encrypt = 1; + } + + /* IEEE 802.11 standard compliant WDS frames (4 addresses) were broken + * in station firmware versions before 1.5.x. With these versions, the + * driver uses a workaround with bogus frame format (4th address after + * the payload). This is not compatible with other AP devices. Since + * the firmware bug is fixed in the latest station firmware versions, + * automatically enable standard compliant mode for cards using station + * firmware version 1.5.0 or newer. */ + if (local->sta_fw_ver >= PRISM2_FW_VER(1,5,0)) + local->wds_type |= HOSTAP_WDS_STANDARD_FRAME; + else { + printk(KERN_DEBUG "%s: defaulting to bogus WDS frame as a " + "workaround for firmware bug in Host AP mode WDS\n", + local->dev->name); + } + + hostap_check_sta_fw_version(local->ap, local->sta_fw_ver); +} + + +static void hostap_passive_scan(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct net_device *dev = local->dev; + u16 chan; + + if (local->passive_scan_interval <= 0) + return; + + if (local->passive_scan_state == PASSIVE_SCAN_LISTEN) { + int max_tries = 16; + + /* Even though host system does not really know when the WLAN + * MAC is sending frames, try to avoid changing channels for + * passive scanning when a host-generated frame is being + * transmitted */ + if (test_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: passive scan detected pending " + "TX - delaying\n", dev->name); + local->passive_scan_timer.expires = jiffies + HZ / 10; + add_timer(&local->passive_scan_timer); + return; + } + + do { + local->passive_scan_channel++; + if (local->passive_scan_channel > 14) + local->passive_scan_channel = 1; + max_tries--; + } while (!(local->channel_mask & + (1 << (local->passive_scan_channel - 1))) && + max_tries > 0); + + if (max_tries == 0) { + printk(KERN_INFO "%s: no allowed passive scan channels" + " found\n", dev->name); + return; + } + + printk(KERN_DEBUG "%s: passive scan channel %d\n", + dev->name, local->passive_scan_channel); + chan = local->passive_scan_channel; + local->passive_scan_state = PASSIVE_SCAN_WAIT; + local->passive_scan_timer.expires = jiffies + HZ / 10; + } else { + chan = local->channel; + local->passive_scan_state = PASSIVE_SCAN_LISTEN; + local->passive_scan_timer.expires = jiffies + + local->passive_scan_interval * HZ; + } + + if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CHANGE_CHANNEL << 8), + chan, NULL, 0)) + printk(KERN_ERR "%s: passive scan channel set %d " + "failed\n", dev->name, chan); + + add_timer(&local->passive_scan_timer); +} + + +/* Called only as a scheduled task when communications quality values should + * be updated. */ +static void handle_comms_qual_update(struct work_struct *work) +{ + local_info_t *local = + container_of(work, local_info_t, comms_qual_update); + prism2_update_comms_qual(local->dev); +} + + +/* Software watchdog - called as a timer. Hardware interrupt (Tick event) is + * used to monitor that local->last_tick_timer is being updated. If not, + * interrupt busy-loop is assumed and driver tries to recover by masking out + * some events. */ +static void hostap_tick_timer(unsigned long data) +{ + static unsigned long last_inquire = 0; + local_info_t *local = (local_info_t *) data; + local->last_tick_timer = jiffies; + + /* Inquire CommTallies every 10 seconds to keep the statistics updated + * more often during low load and when using 32-bit tallies. */ + if ((!last_inquire || time_after(jiffies, last_inquire + 10 * HZ)) && + !local->hw_downloading && local->hw_ready && + !local->hw_resetting && local->dev_enabled) { + hfa384x_cmd_callback(local->dev, HFA384X_CMDCODE_INQUIRE, + HFA384X_INFO_COMMTALLIES, NULL, 0); + last_inquire = jiffies; + } + + if ((local->last_comms_qual_update == 0 || + time_after(jiffies, local->last_comms_qual_update + 10 * HZ)) && + (local->iw_mode == IW_MODE_INFRA || + local->iw_mode == IW_MODE_ADHOC)) { + schedule_work(&local->comms_qual_update); + } + + local->tick_timer.expires = jiffies + 2 * HZ; + add_timer(&local->tick_timer); +} + + +#ifndef PRISM2_NO_PROCFS_DEBUG +static int prism2_registers_proc_show(struct seq_file *m, void *v) +{ + local_info_t *local = m->private; + +#define SHOW_REG(n) \ + seq_printf(m, #n "=%04x\n", hfa384x_read_reg(local->dev, HFA384X_##n##_OFF)) + + SHOW_REG(CMD); + SHOW_REG(PARAM0); + SHOW_REG(PARAM1); + SHOW_REG(PARAM2); + SHOW_REG(STATUS); + SHOW_REG(RESP0); + SHOW_REG(RESP1); + SHOW_REG(RESP2); + SHOW_REG(INFOFID); + SHOW_REG(CONTROL); + SHOW_REG(SELECT0); + SHOW_REG(SELECT1); + SHOW_REG(OFFSET0); + SHOW_REG(OFFSET1); + SHOW_REG(RXFID); + SHOW_REG(ALLOCFID); + SHOW_REG(TXCOMPLFID); + SHOW_REG(SWSUPPORT0); + SHOW_REG(SWSUPPORT1); + SHOW_REG(SWSUPPORT2); + SHOW_REG(EVSTAT); + SHOW_REG(INTEN); + SHOW_REG(EVACK); + /* Do not read data registers, because they change the state of the + * MAC (offset += 2) */ + /* SHOW_REG(DATA0); */ + /* SHOW_REG(DATA1); */ + SHOW_REG(AUXPAGE); + SHOW_REG(AUXOFFSET); + /* SHOW_REG(AUXDATA); */ +#ifdef PRISM2_PCI + SHOW_REG(PCICOR); + SHOW_REG(PCIHCR); + SHOW_REG(PCI_M0_ADDRH); + SHOW_REG(PCI_M0_ADDRL); + SHOW_REG(PCI_M0_LEN); + SHOW_REG(PCI_M0_CTL); + SHOW_REG(PCI_STATUS); + SHOW_REG(PCI_M1_ADDRH); + SHOW_REG(PCI_M1_ADDRL); + SHOW_REG(PCI_M1_LEN); + SHOW_REG(PCI_M1_CTL); +#endif /* PRISM2_PCI */ + + return 0; +} + +static int prism2_registers_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, prism2_registers_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations prism2_registers_proc_fops = { + .open = prism2_registers_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + +struct set_tim_data { + struct list_head list; + int aid; + int set; +}; + +static int prism2_set_tim(struct net_device *dev, int aid, int set) +{ + struct list_head *ptr; + struct set_tim_data *new_entry; + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + new_entry = kzalloc(sizeof(*new_entry), GFP_ATOMIC); + if (new_entry == NULL) + return -ENOMEM; + + new_entry->aid = aid; + new_entry->set = set; + + spin_lock_bh(&local->set_tim_lock); + list_for_each(ptr, &local->set_tim_list) { + struct set_tim_data *entry = + list_entry(ptr, struct set_tim_data, list); + if (entry->aid == aid) { + PDEBUG(DEBUG_PS2, "%s: prism2_set_tim: aid=%d " + "set=%d ==> %d\n", + local->dev->name, aid, entry->set, set); + entry->set = set; + kfree(new_entry); + new_entry = NULL; + break; + } + } + if (new_entry) + list_add_tail(&new_entry->list, &local->set_tim_list); + spin_unlock_bh(&local->set_tim_lock); + + schedule_work(&local->set_tim_queue); + + return 0; +} + + +static void handle_set_tim_queue(struct work_struct *work) +{ + local_info_t *local = container_of(work, local_info_t, set_tim_queue); + struct set_tim_data *entry; + u16 val; + + for (;;) { + entry = NULL; + spin_lock_bh(&local->set_tim_lock); + if (!list_empty(&local->set_tim_list)) { + entry = list_entry(local->set_tim_list.next, + struct set_tim_data, list); + list_del(&entry->list); + } + spin_unlock_bh(&local->set_tim_lock); + if (!entry) + break; + + PDEBUG(DEBUG_PS2, "%s: handle_set_tim_queue: aid=%d set=%d\n", + local->dev->name, entry->aid, entry->set); + + val = entry->aid; + if (entry->set) + val |= 0x8000; + if (hostap_set_word(local->dev, HFA384X_RID_CNFTIMCTRL, val)) { + printk(KERN_DEBUG "%s: set_tim failed (aid=%d " + "set=%d)\n", + local->dev->name, entry->aid, entry->set); + } + + kfree(entry); + } +} + + +static void prism2_clear_set_tim_queue(local_info_t *local) +{ + struct list_head *ptr, *n; + + list_for_each_safe(ptr, n, &local->set_tim_list) { + struct set_tim_data *entry; + entry = list_entry(ptr, struct set_tim_data, list); + list_del(&entry->list); + kfree(entry); + } +} + + +/* + * HostAP uses two layers of net devices, where the inner + * layer gets called all the time from the outer layer. + * This is a natural nesting, which needs a split lock type. + */ +static struct lock_class_key hostap_netdev_xmit_lock_key; +static struct lock_class_key hostap_netdev_addr_lock_key; + +static void prism2_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) +{ + lockdep_set_class(&txq->_xmit_lock, + &hostap_netdev_xmit_lock_key); +} + +static void prism2_set_lockdep_class(struct net_device *dev) +{ + lockdep_set_class(&dev->addr_list_lock, + &hostap_netdev_addr_lock_key); + netdev_for_each_tx_queue(dev, prism2_set_lockdep_class_one, NULL); +} + +static struct net_device * +prism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx, + struct device *sdev) +{ + struct net_device *dev; + struct hostap_interface *iface; + struct local_info *local; + int len, i, ret; + + if (funcs == NULL) + return NULL; + + len = strlen(dev_template); + if (len >= IFNAMSIZ || strstr(dev_template, "%d") == NULL) { + printk(KERN_WARNING "hostap: Invalid dev_template='%s'\n", + dev_template); + return NULL; + } + + len = sizeof(struct hostap_interface) + + 3 + sizeof(struct local_info) + + 3 + sizeof(struct ap_data); + + dev = alloc_etherdev(len); + if (dev == NULL) + return NULL; + + iface = netdev_priv(dev); + local = (struct local_info *) ((((long) (iface + 1)) + 3) & ~3); + local->ap = (struct ap_data *) ((((long) (local + 1)) + 3) & ~3); + local->dev = iface->dev = dev; + iface->local = local; + iface->type = HOSTAP_INTERFACE_MASTER; + INIT_LIST_HEAD(&local->hostap_interfaces); + + local->hw_module = THIS_MODULE; + +#ifdef PRISM2_IO_DEBUG + local->io_debug_enabled = 1; +#endif /* PRISM2_IO_DEBUG */ + + local->func = funcs; + local->func->cmd = hfa384x_cmd; + local->func->read_regs = hfa384x_read_regs; + local->func->get_rid = hfa384x_get_rid; + local->func->set_rid = hfa384x_set_rid; + local->func->hw_enable = prism2_hw_enable; + local->func->hw_config = prism2_hw_config; + local->func->hw_reset = prism2_hw_reset; + local->func->hw_shutdown = prism2_hw_shutdown; + local->func->reset_port = prism2_reset_port; + local->func->schedule_reset = prism2_schedule_reset; +#ifdef PRISM2_DOWNLOAD_SUPPORT + local->func->read_aux_fops = &prism2_download_aux_dump_proc_fops; + local->func->download = prism2_download; +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + local->func->tx = prism2_tx_80211; + local->func->set_tim = prism2_set_tim; + local->func->need_tx_headroom = 0; /* no need to add txdesc in + * skb->data (FIX: maybe for DMA bus + * mastering? */ + + local->mtu = mtu; + + rwlock_init(&local->iface_lock); + spin_lock_init(&local->txfidlock); + spin_lock_init(&local->cmdlock); + spin_lock_init(&local->baplock); + spin_lock_init(&local->lock); + spin_lock_init(&local->irq_init_lock); + mutex_init(&local->rid_bap_mtx); + + if (card_idx < 0 || card_idx >= MAX_PARM_DEVICES) + card_idx = 0; + local->card_idx = card_idx; + + len = strlen(essid); + memcpy(local->essid, essid, + len > MAX_SSID_LEN ? MAX_SSID_LEN : len); + local->essid[MAX_SSID_LEN] = '\0'; + i = GET_INT_PARM(iw_mode, card_idx); + if ((i >= IW_MODE_ADHOC && i <= IW_MODE_REPEAT) || + i == IW_MODE_MONITOR) { + local->iw_mode = i; + } else { + printk(KERN_WARNING "prism2: Unknown iw_mode %d; using " + "IW_MODE_MASTER\n", i); + local->iw_mode = IW_MODE_MASTER; + } + local->channel = GET_INT_PARM(channel, card_idx); + local->beacon_int = GET_INT_PARM(beacon_int, card_idx); + local->dtim_period = GET_INT_PARM(dtim_period, card_idx); + local->wds_max_connections = 16; + local->tx_control = HFA384X_TX_CTRL_FLAGS; + local->manual_retry_count = -1; + local->rts_threshold = 2347; + local->fragm_threshold = 2346; + local->rssi_to_dBm = 100; /* default; to be overriden by + * cnfDbmAdjust, if available */ + local->auth_algs = PRISM2_AUTH_OPEN | PRISM2_AUTH_SHARED_KEY; + local->sram_type = -1; + local->scan_channel_mask = 0xffff; + local->monitor_type = PRISM2_MONITOR_RADIOTAP; + + /* Initialize task queue structures */ + INIT_WORK(&local->reset_queue, handle_reset_queue); + INIT_WORK(&local->set_multicast_list_queue, + hostap_set_multicast_list_queue); + + INIT_WORK(&local->set_tim_queue, handle_set_tim_queue); + INIT_LIST_HEAD(&local->set_tim_list); + spin_lock_init(&local->set_tim_lock); + + INIT_WORK(&local->comms_qual_update, handle_comms_qual_update); + + /* Initialize tasklets for handling hardware IRQ related operations + * outside hw IRQ handler */ +#define HOSTAP_TASKLET_INIT(q, f, d) \ +do { memset((q), 0, sizeof(*(q))); (q)->func = (f); (q)->data = (d); } \ +while (0) + HOSTAP_TASKLET_INIT(&local->bap_tasklet, hostap_bap_tasklet, + (unsigned long) local); + + HOSTAP_TASKLET_INIT(&local->info_tasklet, hostap_info_tasklet, + (unsigned long) local); + hostap_info_init(local); + + HOSTAP_TASKLET_INIT(&local->rx_tasklet, + hostap_rx_tasklet, (unsigned long) local); + skb_queue_head_init(&local->rx_list); + + HOSTAP_TASKLET_INIT(&local->sta_tx_exc_tasklet, + hostap_sta_tx_exc_tasklet, (unsigned long) local); + skb_queue_head_init(&local->sta_tx_exc_list); + + INIT_LIST_HEAD(&local->cmd_queue); + init_waitqueue_head(&local->hostscan_wq); + + lib80211_crypt_info_init(&local->crypt_info, dev->name, &local->lock); + + init_timer(&local->passive_scan_timer); + local->passive_scan_timer.data = (unsigned long) local; + local->passive_scan_timer.function = hostap_passive_scan; + + init_timer(&local->tick_timer); + local->tick_timer.data = (unsigned long) local; + local->tick_timer.function = hostap_tick_timer; + local->tick_timer.expires = jiffies + 2 * HZ; + add_timer(&local->tick_timer); + + INIT_LIST_HEAD(&local->bss_list); + + hostap_setup_dev(dev, local, HOSTAP_INTERFACE_MASTER); + + dev->type = ARPHRD_IEEE80211; + dev->header_ops = &hostap_80211_ops; + + rtnl_lock(); + ret = dev_alloc_name(dev, "wifi%d"); + SET_NETDEV_DEV(dev, sdev); + if (ret >= 0) + ret = register_netdevice(dev); + + prism2_set_lockdep_class(dev); + rtnl_unlock(); + if (ret < 0) { + printk(KERN_WARNING "%s: register netdevice failed!\n", + dev_info); + goto fail; + } + printk(KERN_INFO "%s: Registered netdevice %s\n", dev_info, dev->name); + + hostap_init_data(local); + return dev; + + fail: + free_netdev(dev); + return NULL; +} + + +static int hostap_hw_ready(struct net_device *dev) +{ + struct hostap_interface *iface; + struct local_info *local; + + iface = netdev_priv(dev); + local = iface->local; + local->ddev = hostap_add_interface(local, HOSTAP_INTERFACE_MAIN, 0, + "", dev_template); + + if (local->ddev) { + if (local->iw_mode == IW_MODE_INFRA || + local->iw_mode == IW_MODE_ADHOC) { + netif_carrier_off(local->dev); + netif_carrier_off(local->ddev); + } + hostap_init_proc(local); +#ifndef PRISM2_NO_PROCFS_DEBUG + proc_create_data("registers", 0, local->proc, + &prism2_registers_proc_fops, local); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + hostap_init_ap_proc(local); + return 0; + } + + return -1; +} + + +static void prism2_free_local_data(struct net_device *dev) +{ + struct hostap_tx_callback_info *tx_cb, *tx_cb_prev; + int i; + struct hostap_interface *iface; + struct local_info *local; + struct list_head *ptr, *n; + + if (dev == NULL) + return; + + iface = netdev_priv(dev); + local = iface->local; + + /* Unregister all netdevs before freeing local data. */ + list_for_each_safe(ptr, n, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type == HOSTAP_INTERFACE_MASTER) { + /* special handling for this interface below */ + continue; + } + hostap_remove_interface(iface->dev, 0, 1); + } + + unregister_netdev(local->dev); + + flush_work(&local->reset_queue); + flush_work(&local->set_multicast_list_queue); + flush_work(&local->set_tim_queue); +#ifndef PRISM2_NO_STATION_MODES + flush_work(&local->info_queue); +#endif + flush_work(&local->comms_qual_update); + + lib80211_crypt_info_free(&local->crypt_info); + + if (timer_pending(&local->passive_scan_timer)) + del_timer(&local->passive_scan_timer); + + if (timer_pending(&local->tick_timer)) + del_timer(&local->tick_timer); + + prism2_clear_cmd_queue(local); + + skb_queue_purge(&local->info_list); + skb_queue_purge(&local->rx_list); + skb_queue_purge(&local->sta_tx_exc_list); + + if (local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_DISABLE); + + if (local->ap != NULL) + hostap_free_data(local->ap); + +#ifndef PRISM2_NO_PROCFS_DEBUG + if (local->proc != NULL) + remove_proc_entry("registers", local->proc); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + hostap_remove_proc(local); + + tx_cb = local->tx_callback; + while (tx_cb != NULL) { + tx_cb_prev = tx_cb; + tx_cb = tx_cb->next; + kfree(tx_cb_prev); + } + + hostap_set_hostapd(local, 0, 0); + hostap_set_hostapd_sta(local, 0, 0); + + for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) { + if (local->frag_cache[i].skb != NULL) + dev_kfree_skb(local->frag_cache[i].skb); + } + +#ifdef PRISM2_DOWNLOAD_SUPPORT + prism2_download_free_data(local->dl_pri); + prism2_download_free_data(local->dl_sec); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + prism2_clear_set_tim_queue(local); + + list_for_each_safe(ptr, n, &local->bss_list) { + struct hostap_bss_info *bss = + list_entry(ptr, struct hostap_bss_info, list); + kfree(bss); + } + + kfree(local->pda); + kfree(local->last_scan_results); + kfree(local->generic_elem); + + free_netdev(local->dev); +} + + +#if (defined(PRISM2_PCI) && defined(CONFIG_PM)) || defined(PRISM2_PCCARD) +static void prism2_suspend(struct net_device *dev) +{ + struct hostap_interface *iface; + struct local_info *local; + union iwreq_data wrqu; + + iface = netdev_priv(dev); + local = iface->local; + + /* Send disconnect event, e.g., to trigger reassociation after resume + * if wpa_supplicant is used. */ + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); + + /* Disable hardware and firmware */ + prism2_hw_shutdown(dev, 0); +} +#endif /* (PRISM2_PCI && CONFIG_PM) || PRISM2_PCCARD */ + + +/* These might at some point be compiled separately and used as separate + * kernel modules or linked into one */ +#ifdef PRISM2_DOWNLOAD_SUPPORT +#include "hostap_download.c" +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + +#ifdef PRISM2_CALLBACK +/* External hostap_callback.c file can be used to, e.g., blink activity led. + * This can use platform specific code and must define prism2_callback() + * function (if PRISM2_CALLBACK is not defined, these function calls are not + * used. */ +#include "hostap_callback.c" +#endif /* PRISM2_CALLBACK */ diff --git a/drivers/net/wireless/intersil/hostap/hostap_info.c b/drivers/net/wireless/intersil/hostap/hostap_info.c new file mode 100644 index 000000000000..7635ac4f6679 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_info.c @@ -0,0 +1,507 @@ +/* Host AP driver Info Frame processing (part of hostap.o module) */ + +#include +#include +#include +#include +#include +#include "hostap_wlan.h" +#include "hostap.h" +#include "hostap_ap.h" + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_commtallies16(local_info_t *local, unsigned char *buf, + int left) +{ + struct hfa384x_comm_tallies *tallies; + + if (left < sizeof(struct hfa384x_comm_tallies)) { + printk(KERN_DEBUG "%s: too short (len=%d) commtallies " + "info frame\n", local->dev->name, left); + return; + } + + tallies = (struct hfa384x_comm_tallies *) buf; +#define ADD_COMM_TALLIES(name) \ +local->comm_tallies.name += le16_to_cpu(tallies->name) + ADD_COMM_TALLIES(tx_unicast_frames); + ADD_COMM_TALLIES(tx_multicast_frames); + ADD_COMM_TALLIES(tx_fragments); + ADD_COMM_TALLIES(tx_unicast_octets); + ADD_COMM_TALLIES(tx_multicast_octets); + ADD_COMM_TALLIES(tx_deferred_transmissions); + ADD_COMM_TALLIES(tx_single_retry_frames); + ADD_COMM_TALLIES(tx_multiple_retry_frames); + ADD_COMM_TALLIES(tx_retry_limit_exceeded); + ADD_COMM_TALLIES(tx_discards); + ADD_COMM_TALLIES(rx_unicast_frames); + ADD_COMM_TALLIES(rx_multicast_frames); + ADD_COMM_TALLIES(rx_fragments); + ADD_COMM_TALLIES(rx_unicast_octets); + ADD_COMM_TALLIES(rx_multicast_octets); + ADD_COMM_TALLIES(rx_fcs_errors); + ADD_COMM_TALLIES(rx_discards_no_buffer); + ADD_COMM_TALLIES(tx_discards_wrong_sa); + ADD_COMM_TALLIES(rx_discards_wep_undecryptable); + ADD_COMM_TALLIES(rx_message_in_msg_fragments); + ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments); +#undef ADD_COMM_TALLIES +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_commtallies32(local_info_t *local, unsigned char *buf, + int left) +{ + struct hfa384x_comm_tallies32 *tallies; + + if (left < sizeof(struct hfa384x_comm_tallies32)) { + printk(KERN_DEBUG "%s: too short (len=%d) commtallies32 " + "info frame\n", local->dev->name, left); + return; + } + + tallies = (struct hfa384x_comm_tallies32 *) buf; +#define ADD_COMM_TALLIES(name) \ +local->comm_tallies.name += le32_to_cpu(tallies->name) + ADD_COMM_TALLIES(tx_unicast_frames); + ADD_COMM_TALLIES(tx_multicast_frames); + ADD_COMM_TALLIES(tx_fragments); + ADD_COMM_TALLIES(tx_unicast_octets); + ADD_COMM_TALLIES(tx_multicast_octets); + ADD_COMM_TALLIES(tx_deferred_transmissions); + ADD_COMM_TALLIES(tx_single_retry_frames); + ADD_COMM_TALLIES(tx_multiple_retry_frames); + ADD_COMM_TALLIES(tx_retry_limit_exceeded); + ADD_COMM_TALLIES(tx_discards); + ADD_COMM_TALLIES(rx_unicast_frames); + ADD_COMM_TALLIES(rx_multicast_frames); + ADD_COMM_TALLIES(rx_fragments); + ADD_COMM_TALLIES(rx_unicast_octets); + ADD_COMM_TALLIES(rx_multicast_octets); + ADD_COMM_TALLIES(rx_fcs_errors); + ADD_COMM_TALLIES(rx_discards_no_buffer); + ADD_COMM_TALLIES(tx_discards_wrong_sa); + ADD_COMM_TALLIES(rx_discards_wep_undecryptable); + ADD_COMM_TALLIES(rx_message_in_msg_fragments); + ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments); +#undef ADD_COMM_TALLIES +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_commtallies(local_info_t *local, unsigned char *buf, + int left) +{ + if (local->tallies32) + prism2_info_commtallies32(local, buf, left); + else + prism2_info_commtallies16(local, buf, left); +} + + +#ifndef PRISM2_NO_STATION_MODES +#ifndef PRISM2_NO_DEBUG +static const char* hfa384x_linkstatus_str(u16 linkstatus) +{ + switch (linkstatus) { + case HFA384X_LINKSTATUS_CONNECTED: + return "Connected"; + case HFA384X_LINKSTATUS_DISCONNECTED: + return "Disconnected"; + case HFA384X_LINKSTATUS_AP_CHANGE: + return "Access point change"; + case HFA384X_LINKSTATUS_AP_OUT_OF_RANGE: + return "Access point out of range"; + case HFA384X_LINKSTATUS_AP_IN_RANGE: + return "Access point in range"; + case HFA384X_LINKSTATUS_ASSOC_FAILED: + return "Association failed"; + default: + return "Unknown"; + } +} +#endif /* PRISM2_NO_DEBUG */ + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_linkstatus(local_info_t *local, unsigned char *buf, + int left) +{ + u16 val; + int non_sta_mode; + + /* Alloc new JoinRequests to occur since LinkStatus for the previous + * has been received */ + local->last_join_time = 0; + + if (left != 2) { + printk(KERN_DEBUG "%s: invalid linkstatus info frame " + "length %d\n", local->dev->name, left); + return; + } + + non_sta_mode = local->iw_mode == IW_MODE_MASTER || + local->iw_mode == IW_MODE_REPEAT || + local->iw_mode == IW_MODE_MONITOR; + + val = buf[0] | (buf[1] << 8); + if (!non_sta_mode || val != HFA384X_LINKSTATUS_DISCONNECTED) { + PDEBUG(DEBUG_EXTRA, "%s: LinkStatus=%d (%s)\n", + local->dev->name, val, hfa384x_linkstatus_str(val)); + } + + if (non_sta_mode) { + netif_carrier_on(local->dev); + netif_carrier_on(local->ddev); + return; + } + + /* Get current BSSID later in scheduled task */ + set_bit(PRISM2_INFO_PENDING_LINKSTATUS, &local->pending_info); + local->prev_link_status = val; + schedule_work(&local->info_queue); +} + + +static void prism2_host_roaming(local_info_t *local) +{ + struct hfa384x_join_request req; + struct net_device *dev = local->dev; + struct hfa384x_hostscan_result *selected, *entry; + int i; + unsigned long flags; + + if (local->last_join_time && + time_before(jiffies, local->last_join_time + 10 * HZ)) { + PDEBUG(DEBUG_EXTRA, "%s: last join request has not yet been " + "completed - waiting for it before issuing new one\n", + dev->name); + return; + } + + /* ScanResults are sorted: first ESS results in decreasing signal + * quality then IBSS results in similar order. + * Trivial roaming policy: just select the first entry. + * This could probably be improved by adding hysteresis to limit + * number of handoffs, etc. + * + * Could do periodic RID_SCANREQUEST or Inquire F101 to get new + * ScanResults */ + spin_lock_irqsave(&local->lock, flags); + if (local->last_scan_results == NULL || + local->last_scan_results_count == 0) { + spin_unlock_irqrestore(&local->lock, flags); + PDEBUG(DEBUG_EXTRA, "%s: no scan results for host roaming\n", + dev->name); + return; + } + + selected = &local->last_scan_results[0]; + + if (local->preferred_ap[0] || local->preferred_ap[1] || + local->preferred_ap[2] || local->preferred_ap[3] || + local->preferred_ap[4] || local->preferred_ap[5]) { + /* Try to find preferred AP */ + PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID %pM\n", + dev->name, local->preferred_ap); + for (i = 0; i < local->last_scan_results_count; i++) { + entry = &local->last_scan_results[i]; + if (memcmp(local->preferred_ap, entry->bssid, 6) == 0) + { + PDEBUG(DEBUG_EXTRA, "%s: using preferred AP " + "selection\n", dev->name); + selected = entry; + break; + } + } + } + + memcpy(req.bssid, selected->bssid, ETH_ALEN); + req.channel = selected->chid; + spin_unlock_irqrestore(&local->lock, flags); + + PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=%pM" + " channel=%d\n", + dev->name, req.bssid, le16_to_cpu(req.channel)); + if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req, + sizeof(req))) { + printk(KERN_DEBUG "%s: JoinRequest failed\n", dev->name); + } + local->last_join_time = jiffies; +} + + +static void hostap_report_scan_complete(local_info_t *local) +{ + union iwreq_data wrqu; + + /* Inform user space about new scan results (just empty event, + * SIOCGIWSCAN can be used to fetch data */ + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(local->dev, SIOCGIWSCAN, &wrqu, NULL); + + /* Allow SIOCGIWSCAN handling to occur since we have received + * scanning result */ + local->scan_timestamp = 0; +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_scanresults(local_info_t *local, unsigned char *buf, + int left) +{ + u16 *pos; + int new_count, i; + unsigned long flags; + struct hfa384x_scan_result *res; + struct hfa384x_hostscan_result *results, *prev; + + if (left < 4) { + printk(KERN_DEBUG "%s: invalid scanresult info frame " + "length %d\n", local->dev->name, left); + return; + } + + pos = (u16 *) buf; + pos++; + pos++; + left -= 4; + + new_count = left / sizeof(struct hfa384x_scan_result); + results = kmalloc(new_count * sizeof(struct hfa384x_hostscan_result), + GFP_ATOMIC); + if (results == NULL) + return; + + /* Convert to hostscan result format. */ + res = (struct hfa384x_scan_result *) pos; + for (i = 0; i < new_count; i++) { + memcpy(&results[i], &res[i], + sizeof(struct hfa384x_scan_result)); + results[i].atim = 0; + } + + spin_lock_irqsave(&local->lock, flags); + local->last_scan_type = PRISM2_SCAN; + prev = local->last_scan_results; + local->last_scan_results = results; + local->last_scan_results_count = new_count; + spin_unlock_irqrestore(&local->lock, flags); + kfree(prev); + + hostap_report_scan_complete(local); + + /* Perform rest of ScanResults handling later in scheduled task */ + set_bit(PRISM2_INFO_PENDING_SCANRESULTS, &local->pending_info); + schedule_work(&local->info_queue); +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_hostscanresults(local_info_t *local, + unsigned char *buf, int left) +{ + int i, result_size, copy_len, new_count; + struct hfa384x_hostscan_result *results, *prev; + unsigned long flags; + __le16 *pos; + u8 *ptr; + + wake_up_interruptible(&local->hostscan_wq); + + if (left < 4) { + printk(KERN_DEBUG "%s: invalid hostscanresult info frame " + "length %d\n", local->dev->name, left); + return; + } + + pos = (__le16 *) buf; + copy_len = result_size = le16_to_cpu(*pos); + if (result_size == 0) { + printk(KERN_DEBUG "%s: invalid result_size (0) in " + "hostscanresults\n", local->dev->name); + return; + } + if (copy_len > sizeof(struct hfa384x_hostscan_result)) + copy_len = sizeof(struct hfa384x_hostscan_result); + + pos++; + pos++; + left -= 4; + ptr = (u8 *) pos; + + new_count = left / result_size; + results = kcalloc(new_count, sizeof(struct hfa384x_hostscan_result), + GFP_ATOMIC); + if (results == NULL) + return; + + for (i = 0; i < new_count; i++) { + memcpy(&results[i], ptr, copy_len); + ptr += result_size; + left -= result_size; + } + + if (left) { + printk(KERN_DEBUG "%s: short HostScan result entry (%d/%d)\n", + local->dev->name, left, result_size); + } + + spin_lock_irqsave(&local->lock, flags); + local->last_scan_type = PRISM2_HOSTSCAN; + prev = local->last_scan_results; + local->last_scan_results = results; + local->last_scan_results_count = new_count; + spin_unlock_irqrestore(&local->lock, flags); + kfree(prev); + + hostap_report_scan_complete(local); +} +#endif /* PRISM2_NO_STATION_MODES */ + + +/* Called only as a tasklet (software IRQ) */ +void hostap_info_process(local_info_t *local, struct sk_buff *skb) +{ + struct hfa384x_info_frame *info; + unsigned char *buf; + int left; +#ifndef PRISM2_NO_DEBUG + int i; +#endif /* PRISM2_NO_DEBUG */ + + info = (struct hfa384x_info_frame *) skb->data; + buf = skb->data + sizeof(*info); + left = skb->len - sizeof(*info); + + switch (le16_to_cpu(info->type)) { + case HFA384X_INFO_COMMTALLIES: + prism2_info_commtallies(local, buf, left); + break; + +#ifndef PRISM2_NO_STATION_MODES + case HFA384X_INFO_LINKSTATUS: + prism2_info_linkstatus(local, buf, left); + break; + + case HFA384X_INFO_SCANRESULTS: + prism2_info_scanresults(local, buf, left); + break; + + case HFA384X_INFO_HOSTSCANRESULTS: + prism2_info_hostscanresults(local, buf, left); + break; +#endif /* PRISM2_NO_STATION_MODES */ + +#ifndef PRISM2_NO_DEBUG + default: + PDEBUG(DEBUG_EXTRA, "%s: INFO - len=%d type=0x%04x\n", + local->dev->name, le16_to_cpu(info->len), + le16_to_cpu(info->type)); + PDEBUG(DEBUG_EXTRA, "Unknown info frame:"); + for (i = 0; i < (left < 100 ? left : 100); i++) + PDEBUG2(DEBUG_EXTRA, " %02x", buf[i]); + PDEBUG2(DEBUG_EXTRA, "\n"); + break; +#endif /* PRISM2_NO_DEBUG */ + } +} + + +#ifndef PRISM2_NO_STATION_MODES +static void handle_info_queue_linkstatus(local_info_t *local) +{ + int val = local->prev_link_status; + int connected; + union iwreq_data wrqu; + + connected = + val == HFA384X_LINKSTATUS_CONNECTED || + val == HFA384X_LINKSTATUS_AP_CHANGE || + val == HFA384X_LINKSTATUS_AP_IN_RANGE; + + if (local->func->get_rid(local->dev, HFA384X_RID_CURRENTBSSID, + local->bssid, ETH_ALEN, 1) < 0) { + printk(KERN_DEBUG "%s: could not read CURRENTBSSID after " + "LinkStatus event\n", local->dev->name); + } else { + PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID=%pM\n", + local->dev->name, + (unsigned char *) local->bssid); + if (local->wds_type & HOSTAP_WDS_AP_CLIENT) + hostap_add_sta(local->ap, local->bssid); + } + + /* Get BSSID if we have a valid AP address */ + if (connected) { + netif_carrier_on(local->dev); + netif_carrier_on(local->ddev); + memcpy(wrqu.ap_addr.sa_data, local->bssid, ETH_ALEN); + } else { + netif_carrier_off(local->dev); + netif_carrier_off(local->ddev); + eth_zero_addr(wrqu.ap_addr.sa_data); + } + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* + * Filter out sequential disconnect events in order not to cause a + * flood of SIOCGIWAP events that have a race condition with EAPOL + * frames and can confuse wpa_supplicant about the current association + * status. + */ + if (connected || local->prev_linkstatus_connected) + wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); + local->prev_linkstatus_connected = connected; +} + + +static void handle_info_queue_scanresults(local_info_t *local) +{ + if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA) + prism2_host_roaming(local); + + if (local->host_roaming == 2 && local->iw_mode == IW_MODE_INFRA && + !is_zero_ether_addr(local->preferred_ap)) { + /* + * Firmware seems to be getting into odd state in host_roaming + * mode 2 when hostscan is used without join command, so try + * to fix this by re-joining the current AP. This does not + * actually trigger a new association if the current AP is + * still in the scan results. + */ + prism2_host_roaming(local); + } +} + + +/* Called only as scheduled task after receiving info frames (used to avoid + * pending too much time in HW IRQ handler). */ +static void handle_info_queue(struct work_struct *work) +{ + local_info_t *local = container_of(work, local_info_t, info_queue); + + if (test_and_clear_bit(PRISM2_INFO_PENDING_LINKSTATUS, + &local->pending_info)) + handle_info_queue_linkstatus(local); + + if (test_and_clear_bit(PRISM2_INFO_PENDING_SCANRESULTS, + &local->pending_info)) + handle_info_queue_scanresults(local); +} +#endif /* PRISM2_NO_STATION_MODES */ + + +void hostap_info_init(local_info_t *local) +{ + skb_queue_head_init(&local->info_list); +#ifndef PRISM2_NO_STATION_MODES + INIT_WORK(&local->info_queue, handle_info_queue); +#endif /* PRISM2_NO_STATION_MODES */ +} + + +EXPORT_SYMBOL(hostap_info_init); +EXPORT_SYMBOL(hostap_info_process); diff --git a/drivers/net/wireless/intersil/hostap/hostap_ioctl.c b/drivers/net/wireless/intersil/hostap/hostap_ioctl.c new file mode 100644 index 000000000000..3e5fa7872b64 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_ioctl.c @@ -0,0 +1,4054 @@ +/* ioctl() (mostly Linux Wireless Extensions) routines for Host AP driver */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_wlan.h" +#include "hostap.h" +#include "hostap_ap.h" + +static struct iw_statistics *hostap_get_wireless_stats(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct iw_statistics *wstats; + + iface = netdev_priv(dev); + local = iface->local; + + /* Why are we doing that ? Jean II */ + if (iface->type != HOSTAP_INTERFACE_MAIN) + return NULL; + + wstats = &local->wstats; + + wstats->status = 0; + wstats->discard.code = + local->comm_tallies.rx_discards_wep_undecryptable; + wstats->discard.misc = + local->comm_tallies.rx_fcs_errors + + local->comm_tallies.rx_discards_no_buffer + + local->comm_tallies.tx_discards_wrong_sa; + + wstats->discard.retries = + local->comm_tallies.tx_retry_limit_exceeded; + wstats->discard.fragment = + local->comm_tallies.rx_message_in_bad_msg_fragments; + + if (local->iw_mode != IW_MODE_MASTER && + local->iw_mode != IW_MODE_REPEAT) { + int update = 1; +#ifdef in_atomic + /* RID reading might sleep and it must not be called in + * interrupt context or while atomic. However, this + * function seems to be called while atomic (at least in Linux + * 2.5.59). Update signal quality values only if in suitable + * context. Otherwise, previous values read from tick timer + * will be used. */ + if (in_atomic()) + update = 0; +#endif /* in_atomic */ + + if (update && prism2_update_comms_qual(dev) == 0) + wstats->qual.updated = IW_QUAL_ALL_UPDATED | + IW_QUAL_DBM; + + wstats->qual.qual = local->comms_qual; + wstats->qual.level = local->avg_signal; + wstats->qual.noise = local->avg_noise; + } else { + wstats->qual.qual = 0; + wstats->qual.level = 0; + wstats->qual.noise = 0; + wstats->qual.updated = IW_QUAL_ALL_INVALID; + } + + return wstats; +} + + +static int prism2_get_datarates(struct net_device *dev, u8 *rates) +{ + struct hostap_interface *iface; + local_info_t *local; + u8 buf[12]; + int len; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + len = local->func->get_rid(dev, HFA384X_RID_SUPPORTEDDATARATES, buf, + sizeof(buf), 0); + if (len < 2) + return 0; + + val = le16_to_cpu(*(__le16 *) buf); /* string length */ + + if (len - 2 < val || val > 10) + return 0; + + memcpy(rates, buf + 2, val); + return val; +} + + +static int prism2_get_name(struct net_device *dev, + struct iw_request_info *info, + char *name, char *extra) +{ + u8 rates[10]; + int len, i, over2 = 0; + + len = prism2_get_datarates(dev, rates); + + for (i = 0; i < len; i++) { + if (rates[i] == 0x0b || rates[i] == 0x16) { + over2 = 1; + break; + } + } + + strcpy(name, over2 ? "IEEE 802.11b" : "IEEE 802.11-DS"); + + return 0; +} + + +static int prism2_ioctl_siwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *keybuf) +{ + struct hostap_interface *iface; + local_info_t *local; + int i; + struct lib80211_crypt_data **crypt; + + iface = netdev_priv(dev); + local = iface->local; + + i = erq->flags & IW_ENCODE_INDEX; + if (i < 1 || i > 4) + i = local->crypt_info.tx_keyidx; + else + i--; + if (i < 0 || i >= WEP_KEYS) + return -EINVAL; + + crypt = &local->crypt_info.crypt[i]; + + if (erq->flags & IW_ENCODE_DISABLED) { + if (*crypt) + lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); + goto done; + } + + if (*crypt != NULL && (*crypt)->ops != NULL && + strcmp((*crypt)->ops->name, "WEP") != 0) { + /* changing to use WEP; deinit previously used algorithm */ + lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); + } + + if (*crypt == NULL) { + struct lib80211_crypt_data *new_crypt; + + /* take WEP into use */ + new_crypt = kzalloc(sizeof(struct lib80211_crypt_data), + GFP_KERNEL); + if (new_crypt == NULL) + return -ENOMEM; + new_crypt->ops = lib80211_get_crypto_ops("WEP"); + if (!new_crypt->ops) { + request_module("lib80211_crypt_wep"); + new_crypt->ops = lib80211_get_crypto_ops("WEP"); + } + if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + new_crypt->priv = new_crypt->ops->init(i); + if (!new_crypt->ops || !new_crypt->priv) { + kfree(new_crypt); + new_crypt = NULL; + + printk(KERN_WARNING "%s: could not initialize WEP: " + "load module hostap_crypt_wep.o\n", + dev->name); + return -EOPNOTSUPP; + } + *crypt = new_crypt; + } + + if (erq->length > 0) { + int len = erq->length <= 5 ? 5 : 13; + int first = 1, j; + if (len > erq->length) + memset(keybuf + erq->length, 0, len - erq->length); + (*crypt)->ops->set_key(keybuf, len, NULL, (*crypt)->priv); + for (j = 0; j < WEP_KEYS; j++) { + if (j != i && local->crypt_info.crypt[j]) { + first = 0; + break; + } + } + if (first) + local->crypt_info.tx_keyidx = i; + } else { + /* No key data - just set the default TX key index */ + local->crypt_info.tx_keyidx = i; + } + + done: + local->open_wep = erq->flags & IW_ENCODE_OPEN; + + if (hostap_set_encryption(local)) { + printk(KERN_DEBUG "%s: set_encryption failed\n", dev->name); + return -EINVAL; + } + + /* Do not reset port0 if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. Prism2 documentation seem to require port reset + * after WEP configuration. However, keys are apparently changed at + * least in Managed mode. */ + if (local->iw_mode != IW_MODE_INFRA && local->func->reset_port(dev)) { + printk(KERN_DEBUG "%s: reset_port failed\n", dev->name); + return -EINVAL; + } + + return 0; +} + + +static int prism2_ioctl_giwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *key) +{ + struct hostap_interface *iface; + local_info_t *local; + int i, len; + u16 val; + struct lib80211_crypt_data *crypt; + + iface = netdev_priv(dev); + local = iface->local; + + i = erq->flags & IW_ENCODE_INDEX; + if (i < 1 || i > 4) + i = local->crypt_info.tx_keyidx; + else + i--; + if (i < 0 || i >= WEP_KEYS) + return -EINVAL; + + crypt = local->crypt_info.crypt[i]; + erq->flags = i + 1; + + if (crypt == NULL || crypt->ops == NULL) { + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + return 0; + } + + if (strcmp(crypt->ops->name, "WEP") != 0) { + /* only WEP is supported with wireless extensions, so just + * report that encryption is used */ + erq->length = 0; + erq->flags |= IW_ENCODE_ENABLED; + return 0; + } + + /* Reads from HFA384X_RID_CNFDEFAULTKEY* return bogus values, so show + * the keys from driver buffer */ + len = crypt->ops->get_key(key, WEP_KEY_LEN, NULL, crypt->priv); + erq->length = (len >= 0 ? len : 0); + + if (local->func->get_rid(dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, 1) < 0) + { + printk("CNFWEPFLAGS reading failed\n"); + return -EOPNOTSUPP; + } + le16_to_cpus(&val); + if (val & HFA384X_WEPFLAGS_PRIVACYINVOKED) + erq->flags |= IW_ENCODE_ENABLED; + else + erq->flags |= IW_ENCODE_DISABLED; + if (val & HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED) + erq->flags |= IW_ENCODE_RESTRICTED; + else + erq->flags |= IW_ENCODE_OPEN; + + return 0; +} + + +static int hostap_set_rate(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret, basic_rates; + + iface = netdev_priv(dev); + local = iface->local; + + basic_rates = local->basic_rates & local->tx_rate_control; + if (!basic_rates || basic_rates != local->basic_rates) { + printk(KERN_INFO "%s: updating basic rate set automatically " + "to match with the new supported rate set\n", + dev->name); + if (!basic_rates) + basic_rates = local->tx_rate_control; + + local->basic_rates = basic_rates; + if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + basic_rates)) + printk(KERN_WARNING "%s: failed to set " + "cnfBasicRates\n", dev->name); + } + + ret = (hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, + local->tx_rate_control) || + hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, + local->tx_rate_control) || + local->func->reset_port(dev)); + + if (ret) { + printk(KERN_WARNING "%s: TXRateControl/cnfSupportedRates " + "setting to 0x%x failed\n", + dev->name, local->tx_rate_control); + } + + /* Update TX rate configuration for all STAs based on new operational + * rate set. */ + hostap_update_rates(local); + + return ret; +} + + +static int prism2_ioctl_siwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (rrq->fixed) { + switch (rrq->value) { + case 11000000: + local->tx_rate_control = HFA384X_RATES_11MBPS; + break; + case 5500000: + local->tx_rate_control = HFA384X_RATES_5MBPS; + break; + case 2000000: + local->tx_rate_control = HFA384X_RATES_2MBPS; + break; + case 1000000: + local->tx_rate_control = HFA384X_RATES_1MBPS; + break; + default: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + } + } else { + switch (rrq->value) { + case 11000000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + case 5500000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS; + break; + case 2000000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS; + break; + case 1000000: + local->tx_rate_control = HFA384X_RATES_1MBPS; + break; + default: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + } + } + + return hostap_set_rate(dev); +} + + +static int prism2_ioctl_giwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + u16 val; + struct hostap_interface *iface; + local_info_t *local; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_TXRATECONTROL, &val, 2, 1) < + 0) + return -EINVAL; + + if ((val & 0x1) && (val > 1)) + rrq->fixed = 0; + else + rrq->fixed = 1; + + if (local->iw_mode == IW_MODE_MASTER && local->ap != NULL && + !local->fw_tx_rate_control) { + /* HFA384X_RID_CURRENTTXRATE seems to always be 2 Mbps in + * Host AP mode, so use the recorded TX rate of the last sent + * frame */ + rrq->value = local->ap->last_tx_rate > 0 ? + local->ap->last_tx_rate * 100000 : 11000000; + return 0; + } + + if (local->func->get_rid(dev, HFA384X_RID_CURRENTTXRATE, &val, 2, 1) < + 0) + return -EINVAL; + + switch (val) { + case HFA384X_RATES_1MBPS: + rrq->value = 1000000; + break; + case HFA384X_RATES_2MBPS: + rrq->value = 2000000; + break; + case HFA384X_RATES_5MBPS: + rrq->value = 5500000; + break; + case HFA384X_RATES_11MBPS: + rrq->value = 11000000; + break; + default: + /* should not happen */ + rrq->value = 11000000; + ret = -EINVAL; + break; + } + + return ret; +} + + +static int prism2_ioctl_siwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + /* Set the desired AP density */ + if (sens->value < 1 || sens->value > 3) + return -EINVAL; + + if (hostap_set_word(dev, HFA384X_RID_CNFSYSTEMSCALE, sens->value) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + __le16 val; + + iface = netdev_priv(dev); + local = iface->local; + + /* Get the current AP density */ + if (local->func->get_rid(dev, HFA384X_RID_CNFSYSTEMSCALE, &val, 2, 1) < + 0) + return -EINVAL; + + sens->value = le16_to_cpu(val); + sens->fixed = 1; + + return 0; +} + + +/* Deprecated in new wireless extension API */ +static int prism2_ioctl_giwaplist(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + struct sockaddr *addr; + struct iw_quality *qual; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->iw_mode != IW_MODE_MASTER) { + printk(KERN_DEBUG "SIOCGIWAPLIST is currently only supported " + "in Host AP mode\n"); + data->length = 0; + return -EOPNOTSUPP; + } + + addr = kmalloc(sizeof(struct sockaddr) * IW_MAX_AP, GFP_KERNEL); + qual = kmalloc(sizeof(struct iw_quality) * IW_MAX_AP, GFP_KERNEL); + if (addr == NULL || qual == NULL) { + kfree(addr); + kfree(qual); + data->length = 0; + return -ENOMEM; + } + + data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1); + + memcpy(extra, addr, sizeof(struct sockaddr) * data->length); + data->flags = 1; /* has quality information */ + memcpy(extra + sizeof(struct sockaddr) * data->length, qual, + sizeof(struct iw_quality) * data->length); + + kfree(addr); + kfree(qual); + return 0; +} + + +static int prism2_ioctl_siwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + __le16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (rts->disabled) + val = cpu_to_le16(2347); + else if (rts->value < 0 || rts->value > 2347) + return -EINVAL; + else + val = cpu_to_le16(rts->value); + + if (local->func->set_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2) || + local->func->reset_port(dev)) + return -EINVAL; + + local->rts_threshold = rts->value; + + return 0; +} + +static int prism2_ioctl_giwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + __le16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2, 1) < + 0) + return -EINVAL; + + rts->value = le16_to_cpu(val); + rts->disabled = (rts->value == 2347); + rts->fixed = 1; + + return 0; +} + + +static int prism2_ioctl_siwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + __le16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (rts->disabled) + val = cpu_to_le16(2346); + else if (rts->value < 256 || rts->value > 2346) + return -EINVAL; + else + val = cpu_to_le16(rts->value & ~0x1); /* even numbers only */ + + local->fragm_threshold = rts->value & ~0x1; + if (local->func->set_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, &val, + 2) + || local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + __le16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, + &val, 2, 1) < 0) + return -EINVAL; + + rts->value = le16_to_cpu(val); + rts->disabled = (rts->value == 2346); + rts->fixed = 1; + + return 0; +} + + +#ifndef PRISM2_NO_STATION_MODES +static int hostap_join_ap(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_join_request req; + unsigned long flags; + int i; + struct hfa384x_hostscan_result *entry; + + iface = netdev_priv(dev); + local = iface->local; + + memcpy(req.bssid, local->preferred_ap, ETH_ALEN); + req.channel = 0; + + spin_lock_irqsave(&local->lock, flags); + for (i = 0; i < local->last_scan_results_count; i++) { + if (!local->last_scan_results) + break; + entry = &local->last_scan_results[i]; + if (ether_addr_equal(local->preferred_ap, entry->bssid)) { + req.channel = entry->chid; + break; + } + } + spin_unlock_irqrestore(&local->lock, flags); + + if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req, + sizeof(req))) { + printk(KERN_DEBUG "%s: JoinRequest %pM failed\n", + dev->name, local->preferred_ap); + return -1; + } + + printk(KERN_DEBUG "%s: Trying to join BSSID %pM\n", + dev->name, local->preferred_ap); + + return 0; +} +#endif /* PRISM2_NO_STATION_MODES */ + + +static int prism2_ioctl_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + memcpy(local->preferred_ap, &ap_addr->sa_data, ETH_ALEN); + + if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA) { + struct hfa384x_scan_request scan_req; + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = cpu_to_le16(0x3fff); + scan_req.txrate = cpu_to_le16(HFA384X_RATES_1MBPS); + if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, + &scan_req, sizeof(scan_req))) { + printk(KERN_DEBUG "%s: ScanResults request failed - " + "preferred AP delayed to next unsolicited " + "scan\n", dev->name); + } + } else if (local->host_roaming == 2 && + local->iw_mode == IW_MODE_INFRA) { + if (hostap_join_ap(dev)) + return -EINVAL; + } else { + printk(KERN_DEBUG "%s: Preferred AP (SIOCSIWAP) is used only " + "in Managed mode when host_roaming is enabled\n", + dev->name); + } + + return 0; +#endif /* PRISM2_NO_STATION_MODES */ +} + +static int prism2_ioctl_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + ap_addr->sa_family = ARPHRD_ETHER; + switch (iface->type) { + case HOSTAP_INTERFACE_AP: + memcpy(&ap_addr->sa_data, dev->dev_addr, ETH_ALEN); + break; + case HOSTAP_INTERFACE_STA: + memcpy(&ap_addr->sa_data, local->assoc_ap_addr, ETH_ALEN); + break; + case HOSTAP_INTERFACE_WDS: + memcpy(&ap_addr->sa_data, iface->u.wds.remote_addr, ETH_ALEN); + break; + default: + if (local->func->get_rid(dev, HFA384X_RID_CURRENTBSSID, + &ap_addr->sa_data, ETH_ALEN, 1) < 0) + return -EOPNOTSUPP; + + /* local->bssid is also updated in LinkStatus handler when in + * station mode */ + memcpy(local->bssid, &ap_addr->sa_data, ETH_ALEN); + break; + } + + return 0; +} + + +static int prism2_ioctl_siwnickn(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + memset(local->name, 0, sizeof(local->name)); + memcpy(local->name, nickname, data->length); + local->name_set = 1; + + if (hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, local->name) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwnickn(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + struct hostap_interface *iface; + local_info_t *local; + int len; + char name[MAX_NAME_LEN + 3]; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + len = local->func->get_rid(dev, HFA384X_RID_CNFOWNNAME, + &name, MAX_NAME_LEN + 2, 0); + val = le16_to_cpu(*(__le16 *) name); + if (len > MAX_NAME_LEN + 2 || len < 0 || val > MAX_NAME_LEN) + return -EOPNOTSUPP; + + name[val + 2] = '\0'; + data->length = val + 1; + memcpy(nickname, name + 2, val + 1); + + return 0; +} + + +static int prism2_ioctl_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + /* freq => chan. */ + if (freq->e == 1 && + freq->m / 100000 >= freq_list[0] && + freq->m / 100000 <= freq_list[FREQ_COUNT - 1]) { + int ch; + int fr = freq->m / 100000; + for (ch = 0; ch < FREQ_COUNT; ch++) { + if (fr == freq_list[ch]) { + freq->e = 0; + freq->m = ch + 1; + break; + } + } + } + + if (freq->e != 0 || freq->m < 1 || freq->m > FREQ_COUNT || + !(local->channel_mask & (1 << (freq->m - 1)))) + return -EINVAL; + + local->channel = freq->m; /* channel is used in prism2_setup_rids() */ + if (hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_CURRENTCHANNEL, &val, 2, 1) < + 0) + return -EINVAL; + + le16_to_cpus(&val); + if (val < 1 || val > FREQ_COUNT) + return -EINVAL; + + freq->m = freq_list[val - 1] * 100000; + freq->e = 1; + + return 0; +} + + +static void hostap_monitor_set_type(local_info_t *local) +{ + struct net_device *dev = local->ddev; + + if (dev == NULL) + return; + + if (local->monitor_type == PRISM2_MONITOR_PRISM || + local->monitor_type == PRISM2_MONITOR_CAPHDR) { + dev->type = ARPHRD_IEEE80211_PRISM; + } else if (local->monitor_type == PRISM2_MONITOR_RADIOTAP) { + dev->type = ARPHRD_IEEE80211_RADIOTAP; + } else { + dev->type = ARPHRD_IEEE80211; + } +} + + +static int prism2_ioctl_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (iface->type == HOSTAP_INTERFACE_WDS) + return -EOPNOTSUPP; + + if (data->flags == 0) + ssid[0] = '\0'; /* ANY */ + + if (local->iw_mode == IW_MODE_MASTER && ssid[0] == '\0') { + /* Setting SSID to empty string seems to kill the card in + * Host AP mode */ + printk(KERN_DEBUG "%s: Host AP mode does not support " + "'Any' essid\n", dev->name); + return -EINVAL; + } + + memcpy(local->essid, ssid, data->length); + local->essid[data->length] = '\0'; + + if ((!local->fw_ap && + hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, local->essid)) + || hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, local->essid) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *essid) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (iface->type == HOSTAP_INTERFACE_WDS) + return -EOPNOTSUPP; + + data->flags = 1; /* active */ + if (local->iw_mode == IW_MODE_MASTER) { + data->length = strlen(local->essid); + memcpy(essid, local->essid, IW_ESSID_MAX_SIZE); + } else { + int len; + char ssid[MAX_SSID_LEN + 2]; + memset(ssid, 0, sizeof(ssid)); + len = local->func->get_rid(dev, HFA384X_RID_CURRENTSSID, + &ssid, MAX_SSID_LEN + 2, 0); + val = le16_to_cpu(*(__le16 *) ssid); + if (len > MAX_SSID_LEN + 2 || len < 0 || val > MAX_SSID_LEN) { + return -EOPNOTSUPP; + } + data->length = val; + memcpy(essid, ssid + 2, IW_ESSID_MAX_SIZE); + } + + return 0; +} + + +static int prism2_ioctl_giwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + struct iw_range *range = (struct iw_range *) extra; + u8 rates[10]; + u16 val; + int i, len, over2; + + iface = netdev_priv(dev); + local = iface->local; + + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + /* TODO: could fill num_txpower and txpower array with + * something; however, there are 128 different values.. */ + + range->txpower_capa = IW_TXPOW_DBM; + + if (local->iw_mode == IW_MODE_INFRA || local->iw_mode == IW_MODE_ADHOC) + { + range->min_pmp = 1 * 1024; + range->max_pmp = 65535 * 1024; + range->min_pmt = 1 * 1024; + range->max_pmt = 1000 * 1024; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | + IW_POWER_UNICAST_R | IW_POWER_ALL_R; + } + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 18; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 0; + range->max_retry = 255; + + range->num_channels = FREQ_COUNT; + + val = 0; + for (i = 0; i < FREQ_COUNT; i++) { + if (local->channel_mask & (1 << i)) { + range->freq[val].i = i + 1; + range->freq[val].m = freq_list[i] * 100000; + range->freq[val].e = 1; + val++; + } + if (val == IW_MAX_FREQUENCIES) + break; + } + range->num_frequency = val; + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) { + range->max_qual.qual = 70; /* what is correct max? This was not + * documented exactly. At least + * 69 has been observed. */ + range->max_qual.level = 0; /* dB */ + range->max_qual.noise = 0; /* dB */ + + /* What would be suitable values for "average/typical" qual? */ + range->avg_qual.qual = 20; + range->avg_qual.level = -60; + range->avg_qual.noise = -95; + } else { + range->max_qual.qual = 92; /* 0 .. 92 */ + range->max_qual.level = 154; /* 27 .. 154 */ + range->max_qual.noise = 154; /* 27 .. 154 */ + } + range->sensitivity = 3; + + range->max_encoding_tokens = WEP_KEYS; + range->num_encoding_sizes = 2; + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + + over2 = 0; + len = prism2_get_datarates(dev, rates); + range->num_bitrates = 0; + for (i = 0; i < len; i++) { + if (range->num_bitrates < IW_MAX_BITRATES) { + range->bitrate[range->num_bitrates] = + rates[i] * 500000; + range->num_bitrates++; + } + if (rates[i] == 0x0b || rates[i] == 0x16) + over2 = 1; + } + /* estimated maximum TCP throughput values (bps) */ + range->throughput = over2 ? 5500000 : 1500000; + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVTXDROP) | + IW_EVENT_CAPA_MASK(IWEVCUSTOM) | + IW_EVENT_CAPA_MASK(IWEVREGISTERED) | + IW_EVENT_CAPA_MASK(IWEVEXPIRED)); + + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) + range->scan_capa = IW_SCAN_CAPA_ESSID; + + return 0; +} + + +static int hostap_monitor_mode_enable(local_info_t *local) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "Enabling monitor mode\n"); + hostap_monitor_set_type(local); + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_PSEUDO_IBSS)) { + printk(KERN_DEBUG "Port type setting for monitor mode " + "failed\n"); + return -EOPNOTSUPP; + } + + /* Host decrypt is needed to get the IV and ICV fields; + * however, monitor mode seems to remove WEP flag from frame + * control field */ + if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS, + HFA384X_WEPFLAGS_HOSTENCRYPT | + HFA384X_WEPFLAGS_HOSTDECRYPT)) { + printk(KERN_DEBUG "WEP flags setting failed\n"); + return -EOPNOTSUPP; + } + + if (local->func->reset_port(dev) || + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_MONITOR << 8), + 0, NULL, NULL)) { + printk(KERN_DEBUG "Setting monitor mode failed\n"); + return -EOPNOTSUPP; + } + + return 0; +} + + +static int hostap_monitor_mode_disable(local_info_t *local) +{ + struct net_device *dev = local->ddev; + + if (dev == NULL) + return -1; + + printk(KERN_DEBUG "%s: Disabling monitor mode\n", dev->name); + dev->type = ARPHRD_ETHER; + + if (local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_STOP << 8), + 0, NULL, NULL)) + return -1; + return hostap_set_encryption(local); +} + + +static int prism2_ioctl_siwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + int double_reset = 0; + + iface = netdev_priv(dev); + local = iface->local; + + if (*mode != IW_MODE_ADHOC && *mode != IW_MODE_INFRA && + *mode != IW_MODE_MASTER && *mode != IW_MODE_REPEAT && + *mode != IW_MODE_MONITOR) + return -EOPNOTSUPP; + +#ifdef PRISM2_NO_STATION_MODES + if (*mode == IW_MODE_ADHOC || *mode == IW_MODE_INFRA) + return -EOPNOTSUPP; +#endif /* PRISM2_NO_STATION_MODES */ + + if (*mode == local->iw_mode) + return 0; + + if (*mode == IW_MODE_MASTER && local->essid[0] == '\0') { + printk(KERN_WARNING "%s: empty SSID not allowed in Master " + "mode\n", dev->name); + return -EINVAL; + } + + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_mode_disable(local); + + if ((local->iw_mode == IW_MODE_ADHOC || + local->iw_mode == IW_MODE_MONITOR) && *mode == IW_MODE_MASTER) { + /* There seems to be a firmware bug in at least STA f/w v1.5.6 + * that leaves beacon frames to use IBSS type when moving from + * IBSS to Host AP mode. Doing double Port0 reset seems to be + * enough to workaround this. */ + double_reset = 1; + } + + printk(KERN_DEBUG "prism2: %s: operating mode changed " + "%d -> %d\n", dev->name, local->iw_mode, *mode); + local->iw_mode = *mode; + + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_mode_enable(local); + else if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt && + !local->fw_encrypt_ok) { + printk(KERN_DEBUG "%s: defaulting to host-based encryption as " + "a workaround for firmware bug in Host AP mode WEP\n", + dev->name); + local->host_encrypt = 1; + } + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + hostap_get_porttype(local))) + return -EOPNOTSUPP; + + if (local->func->reset_port(dev)) + return -EINVAL; + if (double_reset && local->func->reset_port(dev)) + return -EINVAL; + + if (local->iw_mode != IW_MODE_INFRA && local->iw_mode != IW_MODE_ADHOC) + { + /* netif_carrier is used only in client modes for now, so make + * sure carrier is on when moving to non-client modes. */ + netif_carrier_on(local->dev); + netif_carrier_on(local->ddev); + } + return 0; +} + + +static int prism2_ioctl_giwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + switch (iface->type) { + case HOSTAP_INTERFACE_STA: + *mode = IW_MODE_INFRA; + break; + case HOSTAP_INTERFACE_WDS: + *mode = IW_MODE_REPEAT; + break; + default: + *mode = local->iw_mode; + break; + } + return 0; +} + + +static int prism2_ioctl_siwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *wrq, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + int ret = 0; + + if (wrq->disabled) + return hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 0); + + switch (wrq->flags & IW_POWER_MODE) { + case IW_POWER_UNICAST_R: + ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 0); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + break; + case IW_POWER_ALL_R: + ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + break; + case IW_POWER_ON: + break; + default: + return -EINVAL; + } + + if (wrq->flags & IW_POWER_TIMEOUT) { + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMHOLDOVERDURATION, + wrq->value / 1024); + if (ret) + return ret; + } + if (wrq->flags & IW_POWER_PERIOD) { + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFMAXSLEEPDURATION, + wrq->value / 1024); + if (ret) + return ret; + } + + return ret; +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_giwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + struct hostap_interface *iface; + local_info_t *local; + __le16 enable, mcast; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_CNFPMENABLED, &enable, 2, 1) + < 0) + return -EINVAL; + + if (!le16_to_cpu(enable)) { + rrq->disabled = 1; + return 0; + } + + rrq->disabled = 0; + + if ((rrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + __le16 timeout; + if (local->func->get_rid(dev, + HFA384X_RID_CNFPMHOLDOVERDURATION, + &timeout, 2, 1) < 0) + return -EINVAL; + + rrq->flags = IW_POWER_TIMEOUT; + rrq->value = le16_to_cpu(timeout) * 1024; + } else { + __le16 period; + if (local->func->get_rid(dev, HFA384X_RID_CNFMAXSLEEPDURATION, + &period, 2, 1) < 0) + return -EINVAL; + + rrq->flags = IW_POWER_PERIOD; + rrq->value = le16_to_cpu(period) * 1024; + } + + if (local->func->get_rid(dev, HFA384X_RID_CNFMULTICASTRECEIVE, &mcast, + 2, 1) < 0) + return -EINVAL; + + if (le16_to_cpu(mcast)) + rrq->flags |= IW_POWER_ALL_R; + else + rrq->flags |= IW_POWER_UNICAST_R; + + return 0; +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_siwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (rrq->disabled) + return -EINVAL; + + /* setting retry limits is not supported with the current station + * firmware code; simulate this with alternative retry count for now */ + if (rrq->flags == IW_RETRY_LIMIT) { + if (rrq->value < 0) { + /* disable manual retry count setting and use firmware + * defaults */ + local->manual_retry_count = -1; + local->tx_control &= ~HFA384X_TX_CTRL_ALT_RTRY; + } else { + if (hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT, + rrq->value)) { + printk(KERN_DEBUG "%s: Alternate retry count " + "setting to %d failed\n", + dev->name, rrq->value); + return -EOPNOTSUPP; + } + + local->manual_retry_count = rrq->value; + local->tx_control |= HFA384X_TX_CTRL_ALT_RTRY; + } + return 0; + } + + return -EOPNOTSUPP; + +#if 0 + /* what could be done, if firmware would support this.. */ + + if (rrq->flags & IW_RETRY_LIMIT) { + if (rrq->flags & IW_RETRY_LONG) + HFA384X_RID_LONGRETRYLIMIT = rrq->value; + else if (rrq->flags & IW_RETRY_SHORT) + HFA384X_RID_SHORTRETRYLIMIT = rrq->value; + else { + HFA384X_RID_LONGRETRYLIMIT = rrq->value; + HFA384X_RID_SHORTRETRYLIMIT = rrq->value; + } + + } + + if (rrq->flags & IW_RETRY_LIFETIME) { + HFA384X_RID_MAXTRANSMITLIFETIME = rrq->value / 1024; + } + + return 0; +#endif /* 0 */ +} + +static int prism2_ioctl_giwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + __le16 shortretry, longretry, lifetime, altretry; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_SHORTRETRYLIMIT, &shortretry, + 2, 1) < 0 || + local->func->get_rid(dev, HFA384X_RID_LONGRETRYLIMIT, &longretry, + 2, 1) < 0 || + local->func->get_rid(dev, HFA384X_RID_MAXTRANSMITLIFETIME, + &lifetime, 2, 1) < 0) + return -EINVAL; + + rrq->disabled = 0; + + if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { + rrq->flags = IW_RETRY_LIFETIME; + rrq->value = le16_to_cpu(lifetime) * 1024; + } else { + if (local->manual_retry_count >= 0) { + rrq->flags = IW_RETRY_LIMIT; + if (local->func->get_rid(dev, + HFA384X_RID_CNFALTRETRYCOUNT, + &altretry, 2, 1) >= 0) + rrq->value = le16_to_cpu(altretry); + else + rrq->value = local->manual_retry_count; + } else if ((rrq->flags & IW_RETRY_LONG)) { + rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; + rrq->value = le16_to_cpu(longretry); + } else { + rrq->flags = IW_RETRY_LIMIT; + rrq->value = le16_to_cpu(shortretry); + if (shortretry != longretry) + rrq->flags |= IW_RETRY_SHORT; + } + } + return 0; +} + + +/* Note! This TX power controlling is experimental and should not be used in + * production use. It just sets raw power register and does not use any kind of + * feedback information from the measured TX power (CR58). This is now + * commented out to make sure that it is not used by accident. TX power + * configuration will be enabled again after proper algorithm using feedback + * has been implemented. */ + +#ifdef RAW_TXPOWER_SETTING +/* Map HFA386x's CR31 to and from dBm with some sort of ad hoc mapping.. + * This version assumes following mapping: + * CR31 is 7-bit value with -64 to +63 range. + * -64 is mapped into +20dBm and +63 into -43dBm. + * This is certainly not an exact mapping for every card, but at least + * increasing dBm value should correspond to increasing TX power. + */ + +static int prism2_txpower_hfa386x_to_dBm(u16 val) +{ + signed char tmp; + + if (val > 255) + val = 255; + + tmp = val; + tmp >>= 2; + + return -12 - tmp; +} + +static u16 prism2_txpower_dBm_to_hfa386x(int val) +{ + signed char tmp; + + if (val > 20) + return 128; + else if (val < -43) + return 127; + + tmp = val; + tmp = -12 - tmp; + tmp <<= 2; + + return (unsigned char) tmp; +} +#endif /* RAW_TXPOWER_SETTING */ + + +static int prism2_ioctl_siwtxpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; +#ifdef RAW_TXPOWER_SETTING + char *tmp; +#endif + u16 val; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + if (rrq->disabled) { + if (local->txpower_type != PRISM2_TXPOWER_OFF) { + val = 0xff; /* use all standby and sleep modes */ + ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_A_D_TEST_MODES2, + &val, NULL); + printk(KERN_DEBUG "%s: Turning radio off: %s\n", + dev->name, ret ? "failed" : "OK"); + local->txpower_type = PRISM2_TXPOWER_OFF; + } + return (ret ? -EOPNOTSUPP : 0); + } + + if (local->txpower_type == PRISM2_TXPOWER_OFF) { + val = 0; /* disable all standby and sleep modes */ + ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_A_D_TEST_MODES2, &val, NULL); + printk(KERN_DEBUG "%s: Turning radio on: %s\n", + dev->name, ret ? "failed" : "OK"); + local->txpower_type = PRISM2_TXPOWER_UNKNOWN; + } + +#ifdef RAW_TXPOWER_SETTING + if (!rrq->fixed && local->txpower_type != PRISM2_TXPOWER_AUTO) { + printk(KERN_DEBUG "Setting ALC on\n"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), 1, &val, NULL); + local->txpower_type = PRISM2_TXPOWER_AUTO; + return 0; + } + + if (local->txpower_type != PRISM2_TXPOWER_FIXED) { + printk(KERN_DEBUG "Setting ALC off\n"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), 0, &val, NULL); + local->txpower_type = PRISM2_TXPOWER_FIXED; + } + + if (rrq->flags == IW_TXPOW_DBM) + tmp = "dBm"; + else if (rrq->flags == IW_TXPOW_MWATT) + tmp = "mW"; + else + tmp = "UNKNOWN"; + printk(KERN_DEBUG "Setting TX power to %d %s\n", rrq->value, tmp); + + if (rrq->flags != IW_TXPOW_DBM) { + printk("SIOCSIWTXPOW with mW is not supported; use dBm\n"); + return -EOPNOTSUPP; + } + + local->txpower = rrq->value; + val = prism2_txpower_dBm_to_hfa386x(local->txpower); + if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_MANUAL_TX_POWER, &val, NULL)) + ret = -EOPNOTSUPP; +#else /* RAW_TXPOWER_SETTING */ + if (rrq->fixed) + ret = -EOPNOTSUPP; +#endif /* RAW_TXPOWER_SETTING */ + + return ret; +} + +static int prism2_ioctl_giwtxpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ +#ifdef RAW_TXPOWER_SETTING + struct hostap_interface *iface; + local_info_t *local; + u16 resp0; + + iface = netdev_priv(dev); + local = iface->local; + + rrq->flags = IW_TXPOW_DBM; + rrq->disabled = 0; + rrq->fixed = 0; + + if (local->txpower_type == PRISM2_TXPOWER_AUTO) { + if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, + HFA386X_CR_MANUAL_TX_POWER, + NULL, &resp0) == 0) { + rrq->value = prism2_txpower_hfa386x_to_dBm(resp0); + } else { + /* Could not get real txpower; guess 15 dBm */ + rrq->value = 15; + } + } else if (local->txpower_type == PRISM2_TXPOWER_OFF) { + rrq->value = 0; + rrq->disabled = 1; + } else if (local->txpower_type == PRISM2_TXPOWER_FIXED) { + rrq->value = local->txpower; + rrq->fixed = 1; + } else { + printk("SIOCGIWTXPOW - unknown txpower_type=%d\n", + local->txpower_type); + } + return 0; +#else /* RAW_TXPOWER_SETTING */ + return -EOPNOTSUPP; +#endif /* RAW_TXPOWER_SETTING */ +} + + +#ifndef PRISM2_NO_STATION_MODES + +/* HostScan request works with and without host_roaming mode. In addition, it + * does not break current association. However, it requires newer station + * firmware version (>= 1.3.1) than scan request. */ +static int prism2_request_hostscan(struct net_device *dev, + u8 *ssid, u8 ssid_len) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_hostscan_request scan_req; + + iface = netdev_priv(dev); + local = iface->local; + + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = cpu_to_le16(local->channel_mask & + local->scan_channel_mask); + scan_req.txrate = cpu_to_le16(HFA384X_RATES_1MBPS); + if (ssid) { + if (ssid_len > 32) + return -EINVAL; + scan_req.target_ssid_len = cpu_to_le16(ssid_len); + memcpy(scan_req.target_ssid, ssid, ssid_len); + } + + if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "%s: HOSTSCAN failed\n", dev->name); + return -EINVAL; + } + return 0; +} + + +static int prism2_request_scan(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_scan_request scan_req; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = cpu_to_le16(local->channel_mask & + local->scan_channel_mask); + scan_req.txrate = cpu_to_le16(HFA384X_RATES_1MBPS); + + /* FIX: + * It seems to be enough to set roaming mode for a short moment to + * host-based and then setup scanrequest data and return the mode to + * firmware-based. + * + * Master mode would need to drop to Managed mode for a short while + * to make scanning work.. Or sweep through the different channels and + * use passive scan based on beacons. */ + + if (!local->host_roaming) + hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, + HFA384X_ROAMING_HOST); + + if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "SCANREQUEST failed\n"); + ret = -EINVAL; + } + + if (!local->host_roaming) + hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, + HFA384X_ROAMING_FIRMWARE); + + return ret; +} + +#else /* !PRISM2_NO_STATION_MODES */ + +static inline int prism2_request_hostscan(struct net_device *dev, + u8 *ssid, u8 ssid_len) +{ + return -EOPNOTSUPP; +} + + +static inline int prism2_request_scan(struct net_device *dev) +{ + return -EOPNOTSUPP; +} + +#endif /* !PRISM2_NO_STATION_MODES */ + + +static int prism2_ioctl_siwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret; + u8 *ssid = NULL, ssid_len = 0; + struct iw_scan_req *req = (struct iw_scan_req *) extra; + + iface = netdev_priv(dev); + local = iface->local; + + if (data->length < sizeof(struct iw_scan_req)) + req = NULL; + + if (local->iw_mode == IW_MODE_MASTER) { + /* In master mode, we just return the results of our local + * tables, so we don't need to start anything... + * Jean II */ + data->length = 0; + return 0; + } + + if (!local->dev_enabled) + return -ENETDOWN; + + if (req && data->flags & IW_SCAN_THIS_ESSID) { + ssid = req->essid; + ssid_len = req->essid_len; + + if (ssid_len && + ((local->iw_mode != IW_MODE_INFRA && + local->iw_mode != IW_MODE_ADHOC) || + (local->sta_fw_ver < PRISM2_FW_VER(1,3,1)))) + return -EOPNOTSUPP; + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) + ret = prism2_request_hostscan(dev, ssid, ssid_len); + else + ret = prism2_request_scan(dev); + + if (ret == 0) + local->scan_timestamp = jiffies; + + /* Could inquire F101, F103 or wait for SIOCGIWSCAN and read RID */ + + return ret; +} + + +#ifndef PRISM2_NO_STATION_MODES +static char * __prism2_translate_scan(local_info_t *local, + struct iw_request_info *info, + struct hfa384x_hostscan_result *scan, + struct hostap_bss_info *bss, + char *current_ev, char *end_buf) +{ + int i, chan; + struct iw_event iwe; + char *current_val; + u16 capabilities; + u8 *pos; + u8 *ssid, *bssid; + size_t ssid_len; + char *buf; + + if (bss) { + ssid = bss->ssid; + ssid_len = bss->ssid_len; + bssid = bss->bssid; + } else { + ssid = scan->ssid; + ssid_len = le16_to_cpu(scan->ssid_len); + bssid = scan->bssid; + } + if (ssid_len > 32) + ssid_len = 32; + + /* First entry *MUST* be the AP MAC address */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, bssid, ETH_ALEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, + IW_EV_ADDR_LEN); + + /* Other entries will be displayed in the order we give them */ + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = ssid_len; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ssid); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + if (bss) { + capabilities = bss->capab_info; + } else { + capabilities = le16_to_cpu(scan->capability); + } + if (capabilities & (WLAN_CAPABILITY_ESS | + WLAN_CAPABILITY_IBSS)) { + if (capabilities & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + if (scan) { + chan = le16_to_cpu(scan->chid); + } else if (bss) { + chan = bss->chan; + } else { + chan = 0; + } + + if (chan > 0) { + iwe.u.freq.m = freq_list[chan - 1] * 100000; + iwe.u.freq.e = 1; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); + } + + if (scan) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + if (local->last_scan_type == PRISM2_HOSTSCAN) { + iwe.u.qual.level = le16_to_cpu(scan->sl); + iwe.u.qual.noise = le16_to_cpu(scan->anl); + } else { + iwe.u.qual.level = + HFA384X_LEVEL_TO_dBm(le16_to_cpu(scan->sl)); + iwe.u.qual.noise = + HFA384X_LEVEL_TO_dBm(le16_to_cpu(scan->anl)); + } + iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED + | IW_QUAL_NOISE_UPDATED + | IW_QUAL_QUAL_INVALID + | IW_QUAL_DBM; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (capabilities & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, ""); + + /* TODO: add SuppRates into BSS table */ + if (scan) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWRATE; + current_val = current_ev + iwe_stream_lcp_len(info); + pos = scan->sup_rates; + for (i = 0; i < sizeof(scan->sup_rates); i++) { + if (pos[i] == 0) + break; + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe.u.bitrate.value = ((pos[i] & 0x7f) * 500000); + current_val = iwe_stream_add_value( + info, current_ev, current_val, end_buf, &iwe, + IW_EV_PARAM_LEN); + } + /* Check if we added any event */ + if ((current_val - current_ev) > iwe_stream_lcp_len(info)) + current_ev = current_val; + } + + /* TODO: add BeaconInt,resp_rate,atim into BSS table */ + buf = kmalloc(MAX_WPA_IE_LEN * 2 + 30, GFP_ATOMIC); + if (buf && scan) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "bcn_int=%d", le16_to_cpu(scan->beacon_interval)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "resp_rate=%d", le16_to_cpu(scan->rate)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); + + if (local->last_scan_type == PRISM2_HOSTSCAN && + (capabilities & WLAN_CAPABILITY_IBSS)) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "atim=%d", le16_to_cpu(scan->atim)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); + } + } + kfree(buf); + + if (bss && bss->wpa_ie_len > 0 && bss->wpa_ie_len <= MAX_WPA_IE_LEN) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = bss->wpa_ie_len; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->wpa_ie); + } + + if (bss && bss->rsn_ie_len > 0 && bss->rsn_ie_len <= MAX_WPA_IE_LEN) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = bss->rsn_ie_len; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->rsn_ie); + } + + return current_ev; +} + + +/* Translate scan data returned from the card to a card independent + * format that the Wireless Tools will understand - Jean II */ +static inline int prism2_translate_scan(local_info_t *local, + struct iw_request_info *info, + char *buffer, int buflen) +{ + struct hfa384x_hostscan_result *scan; + int entry, hostscan; + char *current_ev = buffer; + char *end_buf = buffer + buflen; + struct list_head *ptr; + + spin_lock_bh(&local->lock); + + list_for_each(ptr, &local->bss_list) { + struct hostap_bss_info *bss; + bss = list_entry(ptr, struct hostap_bss_info, list); + bss->included = 0; + } + + hostscan = local->last_scan_type == PRISM2_HOSTSCAN; + for (entry = 0; entry < local->last_scan_results_count; entry++) { + int found = 0; + scan = &local->last_scan_results[entry]; + + /* Report every SSID if the AP is using multiple SSIDs. If no + * BSS record is found (e.g., when WPA mode is disabled), + * report the AP once. */ + list_for_each(ptr, &local->bss_list) { + struct hostap_bss_info *bss; + bss = list_entry(ptr, struct hostap_bss_info, list); + if (ether_addr_equal(bss->bssid, scan->bssid)) { + bss->included = 1; + current_ev = __prism2_translate_scan( + local, info, scan, bss, current_ev, + end_buf); + found++; + } + } + if (!found) { + current_ev = __prism2_translate_scan( + local, info, scan, NULL, current_ev, end_buf); + } + /* Check if there is space for one more entry */ + if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) { + /* Ask user space to try again with a bigger buffer */ + spin_unlock_bh(&local->lock); + return -E2BIG; + } + } + + /* Prism2 firmware has limits (32 at least in some versions) for number + * of BSSes in scan results. Extend this limit by using local BSS list. + */ + list_for_each(ptr, &local->bss_list) { + struct hostap_bss_info *bss; + bss = list_entry(ptr, struct hostap_bss_info, list); + if (bss->included) + continue; + current_ev = __prism2_translate_scan(local, info, NULL, bss, + current_ev, end_buf); + /* Check if there is space for one more entry */ + if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) { + /* Ask user space to try again with a bigger buffer */ + spin_unlock_bh(&local->lock); + return -E2BIG; + } + } + + spin_unlock_bh(&local->lock); + + return current_ev - buffer; +} +#endif /* PRISM2_NO_STATION_MODES */ + + +static inline int prism2_ioctl_giwscan_sta(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + struct hostap_interface *iface; + local_info_t *local; + int res; + + iface = netdev_priv(dev); + local = iface->local; + + /* Wait until the scan is finished. We can probably do better + * than that - Jean II */ + if (local->scan_timestamp && + time_before(jiffies, local->scan_timestamp + 3 * HZ)) { + /* Important note : we don't want to block the caller + * until results are ready for various reasons. + * First, managing wait queues is complex and racy + * (there may be multiple simultaneous callers). + * Second, we grab some rtnetlink lock before coming + * here (in dev_ioctl()). + * Third, the caller can wait on the Wireless Event + * - Jean II */ + return -EAGAIN; + } + local->scan_timestamp = 0; + + res = prism2_translate_scan(local, info, extra, data->length); + + if (res >= 0) { + data->length = res; + return 0; + } else { + data->length = 0; + return res; + } +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + int res; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->iw_mode == IW_MODE_MASTER) { + /* In MASTER mode, it doesn't make sense to go around + * scanning the frequencies and make the stations we serve + * wait when what the user is really interested about is the + * list of stations and access points we are talking to. + * So, just extract results from our cache... + * Jean II */ + + /* Translate to WE format */ + res = prism2_ap_translate_scan(dev, info, extra); + if (res >= 0) { + printk(KERN_DEBUG "Scan result translation succeeded " + "(length=%d)\n", res); + data->length = res; + return 0; + } else { + printk(KERN_DEBUG + "Scan result translation failed (res=%d)\n", + res); + data->length = 0; + return res; + } + } else { + /* Station mode */ + return prism2_ioctl_giwscan_sta(dev, info, data, extra); + } +} + + +static const struct iw_priv_args prism2_priv[] = { + { PRISM2_IOCTL_MONITOR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor" }, + { PRISM2_IOCTL_READMIF, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "readmif" }, + { PRISM2_IOCTL_WRITEMIF, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2, 0, "writemif" }, + { PRISM2_IOCTL_RESET, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "reset" }, + { PRISM2_IOCTL_INQUIRE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inquire" }, + { PRISM2_IOCTL_SET_RID_WORD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_rid_word" }, + { PRISM2_IOCTL_MACCMD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maccmd" }, + { PRISM2_IOCTL_WDS_ADD, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_add" }, + { PRISM2_IOCTL_WDS_DEL, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_del" }, + { PRISM2_IOCTL_ADDMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "addmac" }, + { PRISM2_IOCTL_DELMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "delmac" }, + { PRISM2_IOCTL_KICKMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac" }, + /* --- raw access to sub-ioctls --- */ + { PRISM2_IOCTL_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "prism2_param" }, + { PRISM2_IOCTL_GET_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprism2_param" }, + /* --- sub-ioctls handlers --- */ + { PRISM2_IOCTL_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "" }, + { PRISM2_IOCTL_GET_PRISM2_PARAM, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" }, + /* --- sub-ioctls definitions --- */ + { PRISM2_PARAM_TXRATECTRL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "txratectrl" }, + { PRISM2_PARAM_TXRATECTRL, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettxratectrl" }, + { PRISM2_PARAM_BEACON_INT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "beacon_int" }, + { PRISM2_PARAM_BEACON_INT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbeacon_int" }, +#ifndef PRISM2_NO_STATION_MODES + { PRISM2_PARAM_PSEUDO_IBSS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pseudo_ibss" }, + { PRISM2_PARAM_PSEUDO_IBSS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getpseudo_ibss" }, +#endif /* PRISM2_NO_STATION_MODES */ + { PRISM2_PARAM_ALC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "alc" }, + { PRISM2_PARAM_ALC, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getalc" }, + { PRISM2_PARAM_DUMP, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dump" }, + { PRISM2_PARAM_DUMP, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdump" }, + { PRISM2_PARAM_OTHER_AP_POLICY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "other_ap_policy" }, + { PRISM2_PARAM_OTHER_AP_POLICY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getother_ap_pol" }, + { PRISM2_PARAM_AP_MAX_INACTIVITY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_inactivity" }, + { PRISM2_PARAM_AP_MAX_INACTIVITY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_inactivi" }, + { PRISM2_PARAM_AP_BRIDGE_PACKETS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bridge_packets" }, + { PRISM2_PARAM_AP_BRIDGE_PACKETS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbridge_packe" }, + { PRISM2_PARAM_DTIM_PERIOD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dtim_period" }, + { PRISM2_PARAM_DTIM_PERIOD, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdtim_period" }, + { PRISM2_PARAM_AP_NULLFUNC_ACK, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "nullfunc_ack" }, + { PRISM2_PARAM_AP_NULLFUNC_ACK, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getnullfunc_ack" }, + { PRISM2_PARAM_MAX_WDS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_wds" }, + { PRISM2_PARAM_MAX_WDS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_wds" }, + { PRISM2_PARAM_AP_AUTOM_AP_WDS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "autom_ap_wds" }, + { PRISM2_PARAM_AP_AUTOM_AP_WDS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getautom_ap_wds" }, + { PRISM2_PARAM_AP_AUTH_ALGS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_auth_algs" }, + { PRISM2_PARAM_AP_AUTH_ALGS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_auth_algs" }, + { PRISM2_PARAM_MONITOR_ALLOW_FCSERR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "allow_fcserr" }, + { PRISM2_PARAM_MONITOR_ALLOW_FCSERR, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getallow_fcserr" }, + { PRISM2_PARAM_HOST_ENCRYPT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_encrypt" }, + { PRISM2_PARAM_HOST_ENCRYPT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_encrypt" }, + { PRISM2_PARAM_HOST_DECRYPT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_decrypt" }, + { PRISM2_PARAM_HOST_DECRYPT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_decrypt" }, +#ifndef PRISM2_NO_STATION_MODES + { PRISM2_PARAM_HOST_ROAMING, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_roaming" }, + { PRISM2_PARAM_HOST_ROAMING, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_roaming" }, +#endif /* PRISM2_NO_STATION_MODES */ + { PRISM2_PARAM_BCRX_STA_KEY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bcrx_sta_key" }, + { PRISM2_PARAM_BCRX_STA_KEY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbcrx_sta_key" }, + { PRISM2_PARAM_IEEE_802_1X, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ieee_802_1x" }, + { PRISM2_PARAM_IEEE_802_1X, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getieee_802_1x" }, + { PRISM2_PARAM_ANTSEL_TX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_tx" }, + { PRISM2_PARAM_ANTSEL_TX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_tx" }, + { PRISM2_PARAM_ANTSEL_RX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_rx" }, + { PRISM2_PARAM_ANTSEL_RX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_rx" }, + { PRISM2_PARAM_MONITOR_TYPE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor_type" }, + { PRISM2_PARAM_MONITOR_TYPE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmonitor_type" }, + { PRISM2_PARAM_WDS_TYPE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wds_type" }, + { PRISM2_PARAM_WDS_TYPE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwds_type" }, + { PRISM2_PARAM_HOSTSCAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostscan" }, + { PRISM2_PARAM_HOSTSCAN, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostscan" }, + { PRISM2_PARAM_AP_SCAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_scan" }, + { PRISM2_PARAM_AP_SCAN, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_scan" }, + { PRISM2_PARAM_ENH_SEC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "enh_sec" }, + { PRISM2_PARAM_ENH_SEC, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getenh_sec" }, +#ifdef PRISM2_IO_DEBUG + { PRISM2_PARAM_IO_DEBUG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "io_debug" }, + { PRISM2_PARAM_IO_DEBUG, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getio_debug" }, +#endif /* PRISM2_IO_DEBUG */ + { PRISM2_PARAM_BASIC_RATES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "basic_rates" }, + { PRISM2_PARAM_BASIC_RATES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbasic_rates" }, + { PRISM2_PARAM_OPER_RATES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "oper_rates" }, + { PRISM2_PARAM_OPER_RATES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getoper_rates" }, + { PRISM2_PARAM_HOSTAPD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd" }, + { PRISM2_PARAM_HOSTAPD, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd" }, + { PRISM2_PARAM_HOSTAPD_STA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd_sta" }, + { PRISM2_PARAM_HOSTAPD_STA, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd_sta" }, + { PRISM2_PARAM_WPA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wpa" }, + { PRISM2_PARAM_WPA, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwpa" }, + { PRISM2_PARAM_PRIVACY_INVOKED, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "privacy_invoked" }, + { PRISM2_PARAM_PRIVACY_INVOKED, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprivacy_invo" }, + { PRISM2_PARAM_TKIP_COUNTERMEASURES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tkip_countermea" }, + { PRISM2_PARAM_TKIP_COUNTERMEASURES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettkip_counter" }, + { PRISM2_PARAM_DROP_UNENCRYPTED, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "drop_unencrypte" }, + { PRISM2_PARAM_DROP_UNENCRYPTED, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdrop_unencry" }, + { PRISM2_PARAM_SCAN_CHANNEL_MASK, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "scan_channels" }, + { PRISM2_PARAM_SCAN_CHANNEL_MASK, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getscan_channel" }, +}; + + +static int prism2_ioctl_priv_inquire(struct net_device *dev, int *i) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->cmd(dev, HFA384X_CMDCODE_INQUIRE, *i, NULL, NULL)) + return -EOPNOTSUPP; + + return 0; +} + + +static int prism2_ioctl_priv_prism2_param(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + int *i = (int *) extra; + int param = *i; + int value = *(i + 1); + int ret = 0; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + switch (param) { + case PRISM2_PARAM_TXRATECTRL: + local->fw_tx_rate_control = value; + break; + + case PRISM2_PARAM_BEACON_INT: + if (hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, value) || + local->func->reset_port(dev)) + ret = -EINVAL; + else + local->beacon_int = value; + break; + +#ifndef PRISM2_NO_STATION_MODES + case PRISM2_PARAM_PSEUDO_IBSS: + if (value == local->pseudo_adhoc) + break; + + if (value != 0 && value != 1) { + ret = -EINVAL; + break; + } + + printk(KERN_DEBUG "prism2: %s: pseudo IBSS change %d -> %d\n", + dev->name, local->pseudo_adhoc, value); + local->pseudo_adhoc = value; + if (local->iw_mode != IW_MODE_ADHOC) + break; + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + hostap_get_porttype(local))) { + ret = -EOPNOTSUPP; + break; + } + + if (local->func->reset_port(dev)) + ret = -EINVAL; + break; +#endif /* PRISM2_NO_STATION_MODES */ + + case PRISM2_PARAM_ALC: + printk(KERN_DEBUG "%s: %s ALC\n", dev->name, + value == 0 ? "Disabling" : "Enabling"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), + value == 0 ? 0 : 1, &val, NULL); + break; + + case PRISM2_PARAM_DUMP: + local->frame_dump = value; + break; + + case PRISM2_PARAM_OTHER_AP_POLICY: + if (value < 0 || value > 3) { + ret = -EINVAL; + break; + } + if (local->ap != NULL) + local->ap->ap_policy = value; + break; + + case PRISM2_PARAM_AP_MAX_INACTIVITY: + if (value < 0 || value > 7 * 24 * 60 * 60) { + ret = -EINVAL; + break; + } + if (local->ap != NULL) + local->ap->max_inactivity = value * HZ; + break; + + case PRISM2_PARAM_AP_BRIDGE_PACKETS: + if (local->ap != NULL) + local->ap->bridge_packets = value; + break; + + case PRISM2_PARAM_DTIM_PERIOD: + if (value < 0 || value > 65535) { + ret = -EINVAL; + break; + } + if (hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, value) + || local->func->reset_port(dev)) + ret = -EINVAL; + else + local->dtim_period = value; + break; + + case PRISM2_PARAM_AP_NULLFUNC_ACK: + if (local->ap != NULL) + local->ap->nullfunc_ack = value; + break; + + case PRISM2_PARAM_MAX_WDS: + local->wds_max_connections = value; + break; + + case PRISM2_PARAM_AP_AUTOM_AP_WDS: + if (local->ap != NULL) { + if (!local->ap->autom_ap_wds && value) { + /* add WDS link to all APs in STA table */ + hostap_add_wds_links(local); + } + local->ap->autom_ap_wds = value; + } + break; + + case PRISM2_PARAM_AP_AUTH_ALGS: + local->auth_algs = value; + if (hostap_set_auth_algs(local)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_MONITOR_ALLOW_FCSERR: + local->monitor_allow_fcserr = value; + break; + + case PRISM2_PARAM_HOST_ENCRYPT: + local->host_encrypt = value; + if (hostap_set_encryption(local) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_HOST_DECRYPT: + local->host_decrypt = value; + if (hostap_set_encryption(local) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + +#ifndef PRISM2_NO_STATION_MODES + case PRISM2_PARAM_HOST_ROAMING: + if (value < 0 || value > 2) { + ret = -EINVAL; + break; + } + local->host_roaming = value; + if (hostap_set_roaming(local) || local->func->reset_port(dev)) + ret = -EINVAL; + break; +#endif /* PRISM2_NO_STATION_MODES */ + + case PRISM2_PARAM_BCRX_STA_KEY: + local->bcrx_sta_key = value; + break; + + case PRISM2_PARAM_IEEE_802_1X: + local->ieee_802_1x = value; + break; + + case PRISM2_PARAM_ANTSEL_TX: + if (value < 0 || value > HOSTAP_ANTSEL_HIGH) { + ret = -EINVAL; + break; + } + local->antsel_tx = value; + hostap_set_antsel(local); + break; + + case PRISM2_PARAM_ANTSEL_RX: + if (value < 0 || value > HOSTAP_ANTSEL_HIGH) { + ret = -EINVAL; + break; + } + local->antsel_rx = value; + hostap_set_antsel(local); + break; + + case PRISM2_PARAM_MONITOR_TYPE: + if (value != PRISM2_MONITOR_80211 && + value != PRISM2_MONITOR_CAPHDR && + value != PRISM2_MONITOR_PRISM && + value != PRISM2_MONITOR_RADIOTAP) { + ret = -EINVAL; + break; + } + local->monitor_type = value; + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_set_type(local); + break; + + case PRISM2_PARAM_WDS_TYPE: + local->wds_type = value; + break; + + case PRISM2_PARAM_HOSTSCAN: + { + struct hfa384x_hostscan_request scan_req; + u16 rate; + + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = cpu_to_le16(0x3fff); + switch (value) { + case 1: rate = HFA384X_RATES_1MBPS; break; + case 2: rate = HFA384X_RATES_2MBPS; break; + case 3: rate = HFA384X_RATES_5MBPS; break; + case 4: rate = HFA384X_RATES_11MBPS; break; + default: rate = HFA384X_RATES_1MBPS; break; + } + scan_req.txrate = cpu_to_le16(rate); + /* leave SSID empty to accept all SSIDs */ + + if (local->iw_mode == IW_MODE_MASTER) { + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_BSS) || + local->func->reset_port(dev)) + printk(KERN_DEBUG "Leaving Host AP mode " + "for HostScan failed\n"); + } + + if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "HOSTSCAN failed\n"); + ret = -EINVAL; + } + if (local->iw_mode == IW_MODE_MASTER) { + wait_queue_t __wait; + init_waitqueue_entry(&__wait, current); + add_wait_queue(&local->hostscan_wq, &__wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + if (signal_pending(current)) + ret = -EINTR; + set_current_state(TASK_RUNNING); + remove_wait_queue(&local->hostscan_wq, &__wait); + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_HOSTAP) || + local->func->reset_port(dev)) + printk(KERN_DEBUG "Returning to Host AP mode " + "after HostScan failed\n"); + } + break; + } + + case PRISM2_PARAM_AP_SCAN: + local->passive_scan_interval = value; + if (timer_pending(&local->passive_scan_timer)) + del_timer(&local->passive_scan_timer); + if (value > 0 && value < INT_MAX / HZ) { + local->passive_scan_timer.expires = jiffies + + local->passive_scan_interval * HZ; + add_timer(&local->passive_scan_timer); + } + break; + + case PRISM2_PARAM_ENH_SEC: + if (value < 0 || value > 3) { + ret = -EINVAL; + break; + } + local->enh_sec = value; + if (hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, + local->enh_sec) || + local->func->reset_port(dev)) { + printk(KERN_INFO "%s: cnfEnhSecurity requires STA f/w " + "1.6.3 or newer\n", dev->name); + ret = -EOPNOTSUPP; + } + break; + +#ifdef PRISM2_IO_DEBUG + case PRISM2_PARAM_IO_DEBUG: + local->io_debug_enabled = value; + break; +#endif /* PRISM2_IO_DEBUG */ + + case PRISM2_PARAM_BASIC_RATES: + if ((value & local->tx_rate_control) != value || value == 0) { + printk(KERN_INFO "%s: invalid basic rate set - basic " + "rates must be in supported rate set\n", + dev->name); + ret = -EINVAL; + break; + } + local->basic_rates = value; + if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + local->basic_rates) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_OPER_RATES: + local->tx_rate_control = value; + if (hostap_set_rate(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_HOSTAPD: + ret = hostap_set_hostapd(local, value, 1); + break; + + case PRISM2_PARAM_HOSTAPD_STA: + ret = hostap_set_hostapd_sta(local, value, 1); + break; + + case PRISM2_PARAM_WPA: + local->wpa = value; + if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) + ret = -EOPNOTSUPP; + else if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, + value ? 1 : 0)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_PRIVACY_INVOKED: + local->privacy_invoked = value; + if (hostap_set_encryption(local) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_TKIP_COUNTERMEASURES: + local->tkip_countermeasures = value; + break; + + case PRISM2_PARAM_DROP_UNENCRYPTED: + local->drop_unencrypted = value; + break; + + case PRISM2_PARAM_SCAN_CHANNEL_MASK: + local->scan_channel_mask = value; + break; + + default: + printk(KERN_DEBUG "%s: prism2_param: unknown param %d\n", + dev->name, param); + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + + +static int prism2_ioctl_priv_get_prism2_param(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + int *param = (int *) extra; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + switch (*param) { + case PRISM2_PARAM_TXRATECTRL: + *param = local->fw_tx_rate_control; + break; + + case PRISM2_PARAM_BEACON_INT: + *param = local->beacon_int; + break; + + case PRISM2_PARAM_PSEUDO_IBSS: + *param = local->pseudo_adhoc; + break; + + case PRISM2_PARAM_ALC: + ret = -EOPNOTSUPP; /* FIX */ + break; + + case PRISM2_PARAM_DUMP: + *param = local->frame_dump; + break; + + case PRISM2_PARAM_OTHER_AP_POLICY: + if (local->ap != NULL) + *param = local->ap->ap_policy; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_MAX_INACTIVITY: + if (local->ap != NULL) + *param = local->ap->max_inactivity / HZ; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_BRIDGE_PACKETS: + if (local->ap != NULL) + *param = local->ap->bridge_packets; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_DTIM_PERIOD: + *param = local->dtim_period; + break; + + case PRISM2_PARAM_AP_NULLFUNC_ACK: + if (local->ap != NULL) + *param = local->ap->nullfunc_ack; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_MAX_WDS: + *param = local->wds_max_connections; + break; + + case PRISM2_PARAM_AP_AUTOM_AP_WDS: + if (local->ap != NULL) + *param = local->ap->autom_ap_wds; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_AUTH_ALGS: + *param = local->auth_algs; + break; + + case PRISM2_PARAM_MONITOR_ALLOW_FCSERR: + *param = local->monitor_allow_fcserr; + break; + + case PRISM2_PARAM_HOST_ENCRYPT: + *param = local->host_encrypt; + break; + + case PRISM2_PARAM_HOST_DECRYPT: + *param = local->host_decrypt; + break; + + case PRISM2_PARAM_HOST_ROAMING: + *param = local->host_roaming; + break; + + case PRISM2_PARAM_BCRX_STA_KEY: + *param = local->bcrx_sta_key; + break; + + case PRISM2_PARAM_IEEE_802_1X: + *param = local->ieee_802_1x; + break; + + case PRISM2_PARAM_ANTSEL_TX: + *param = local->antsel_tx; + break; + + case PRISM2_PARAM_ANTSEL_RX: + *param = local->antsel_rx; + break; + + case PRISM2_PARAM_MONITOR_TYPE: + *param = local->monitor_type; + break; + + case PRISM2_PARAM_WDS_TYPE: + *param = local->wds_type; + break; + + case PRISM2_PARAM_HOSTSCAN: + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_SCAN: + *param = local->passive_scan_interval; + break; + + case PRISM2_PARAM_ENH_SEC: + *param = local->enh_sec; + break; + +#ifdef PRISM2_IO_DEBUG + case PRISM2_PARAM_IO_DEBUG: + *param = local->io_debug_enabled; + break; +#endif /* PRISM2_IO_DEBUG */ + + case PRISM2_PARAM_BASIC_RATES: + *param = local->basic_rates; + break; + + case PRISM2_PARAM_OPER_RATES: + *param = local->tx_rate_control; + break; + + case PRISM2_PARAM_HOSTAPD: + *param = local->hostapd; + break; + + case PRISM2_PARAM_HOSTAPD_STA: + *param = local->hostapd_sta; + break; + + case PRISM2_PARAM_WPA: + if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) + ret = -EOPNOTSUPP; + *param = local->wpa; + break; + + case PRISM2_PARAM_PRIVACY_INVOKED: + *param = local->privacy_invoked; + break; + + case PRISM2_PARAM_TKIP_COUNTERMEASURES: + *param = local->tkip_countermeasures; + break; + + case PRISM2_PARAM_DROP_UNENCRYPTED: + *param = local->drop_unencrypted; + break; + + case PRISM2_PARAM_SCAN_CHANNEL_MASK: + *param = local->scan_channel_mask; + break; + + default: + printk(KERN_DEBUG "%s: get_prism2_param: unknown param %d\n", + dev->name, *param); + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + + +static int prism2_ioctl_priv_readmif(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 resp0; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, *extra, NULL, + &resp0)) + return -EOPNOTSUPP; + else + *extra = resp0; + + return 0; +} + + +static int prism2_ioctl_priv_writemif(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 cr, val; + + iface = netdev_priv(dev); + local = iface->local; + + cr = *extra; + val = *(extra + 1); + if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, cr, &val, NULL)) + return -EOPNOTSUPP; + + return 0; +} + + +static int prism2_ioctl_priv_monitor(struct net_device *dev, int *i) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret = 0; + u32 mode; + + iface = netdev_priv(dev); + local = iface->local; + + printk(KERN_DEBUG "%s: process %d (%s) used deprecated iwpriv monitor " + "- update software to use iwconfig mode monitor\n", + dev->name, task_pid_nr(current), current->comm); + + /* Backward compatibility code - this can be removed at some point */ + + if (*i == 0) { + /* Disable monitor mode - old mode was not saved, so go to + * Master mode */ + mode = IW_MODE_MASTER; + ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); + } else if (*i == 1) { + /* netlink socket mode is not supported anymore since it did + * not separate different devices from each other and was not + * best method for delivering large amount of packets to + * user space */ + ret = -EOPNOTSUPP; + } else if (*i == 2 || *i == 3) { + switch (*i) { + case 2: + local->monitor_type = PRISM2_MONITOR_80211; + break; + case 3: + local->monitor_type = PRISM2_MONITOR_PRISM; + break; + } + mode = IW_MODE_MONITOR; + ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); + hostap_monitor_mode_enable(local); + } else + ret = -EINVAL; + + return ret; +} + + +static int prism2_ioctl_priv_reset(struct net_device *dev, int *i) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + printk(KERN_DEBUG "%s: manual reset request(%d)\n", dev->name, *i); + switch (*i) { + case 0: + /* Disable and enable card */ + local->func->hw_shutdown(dev, 1); + local->func->hw_config(dev, 0); + break; + + case 1: + /* COR sreset */ + local->func->hw_reset(dev); + break; + + case 2: + /* Disable and enable port 0 */ + local->func->reset_port(dev); + break; + + case 3: + prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING); + if (local->func->cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, + NULL)) + return -EINVAL; + break; + + case 4: + if (local->func->cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, + NULL)) + return -EINVAL; + break; + + default: + printk(KERN_DEBUG "Unknown reset request %d\n", *i); + return -EOPNOTSUPP; + } + + return 0; +} + + +static int prism2_ioctl_priv_set_rid_word(struct net_device *dev, int *i) +{ + int rid = *i; + int value = *(i + 1); + + printk(KERN_DEBUG "%s: Set RID[0x%X] = %d\n", dev->name, rid, value); + + if (hostap_set_word(dev, rid, value)) + return -EINVAL; + + return 0; +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +static int ap_mac_cmd_ioctl(local_info_t *local, int *cmd) +{ + int ret = 0; + + switch (*cmd) { + case AP_MAC_CMD_POLICY_OPEN: + local->ap->mac_restrictions.policy = MAC_POLICY_OPEN; + break; + case AP_MAC_CMD_POLICY_ALLOW: + local->ap->mac_restrictions.policy = MAC_POLICY_ALLOW; + break; + case AP_MAC_CMD_POLICY_DENY: + local->ap->mac_restrictions.policy = MAC_POLICY_DENY; + break; + case AP_MAC_CMD_FLUSH: + ap_control_flush_macs(&local->ap->mac_restrictions); + break; + case AP_MAC_CMD_KICKALL: + ap_control_kickall(local->ap); + hostap_deauth_all_stas(local->dev, local->ap, 0); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +#ifdef PRISM2_DOWNLOAD_SUPPORT +static int prism2_ioctl_priv_download(local_info_t *local, struct iw_point *p) +{ + struct prism2_download_param *param; + int ret = 0; + + if (p->length < sizeof(struct prism2_download_param) || + p->length > 1024 || !p->pointer) + return -EINVAL; + + param = kmalloc(p->length, GFP_KERNEL); + if (param == NULL) + return -ENOMEM; + + if (copy_from_user(param, p->pointer, p->length)) { + ret = -EFAULT; + goto out; + } + + if (p->length < sizeof(struct prism2_download_param) + + param->num_areas * sizeof(struct prism2_download_area)) { + ret = -EINVAL; + goto out; + } + + ret = local->func->download(local, param); + + out: + kfree(param); + return ret; +} +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + +static int prism2_set_genericelement(struct net_device *dev, u8 *elem, + size_t len) +{ + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + u8 *buf; + + /* + * Add 16-bit length in the beginning of the buffer because Prism2 RID + * includes it. + */ + buf = kmalloc(len + 2, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + *((__le16 *) buf) = cpu_to_le16(len); + memcpy(buf + 2, elem, len); + + kfree(local->generic_elem); + local->generic_elem = buf; + local->generic_elem_len = len + 2; + + return local->func->set_rid(local->dev, HFA384X_RID_GENERICELEMENT, + buf, len + 2); +} + + +static int prism2_ioctl_siwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * Host AP driver does not use these parameters and allows + * wpa_supplicant to control them internally. + */ + break; + case IW_AUTH_TKIP_COUNTERMEASURES: + local->tkip_countermeasures = data->value; + break; + case IW_AUTH_DROP_UNENCRYPTED: + local->drop_unencrypted = data->value; + break; + case IW_AUTH_80211_AUTH_ALG: + local->auth_algs = data->value; + break; + case IW_AUTH_WPA_ENABLED: + if (data->value == 0) { + local->wpa = 0; + if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) + break; + prism2_set_genericelement(dev, "", 0); + local->host_roaming = 0; + local->privacy_invoked = 0; + if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, + 0) || + hostap_set_roaming(local) || + hostap_set_encryption(local) || + local->func->reset_port(dev)) + return -EINVAL; + break; + } + if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) + return -EOPNOTSUPP; + local->host_roaming = 2; + local->privacy_invoked = 1; + local->wpa = 1; + if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1) || + hostap_set_roaming(local) || + hostap_set_encryption(local) || + local->func->reset_port(dev)) + return -EINVAL; + break; + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + local->ieee_802_1x = data->value; + break; + case IW_AUTH_PRIVACY_INVOKED: + local->privacy_invoked = data->value; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + + +static int prism2_ioctl_giwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * Host AP driver does not use these parameters and allows + * wpa_supplicant to control them internally. + */ + return -EOPNOTSUPP; + case IW_AUTH_TKIP_COUNTERMEASURES: + data->value = local->tkip_countermeasures; + break; + case IW_AUTH_DROP_UNENCRYPTED: + data->value = local->drop_unencrypted; + break; + case IW_AUTH_80211_AUTH_ALG: + data->value = local->auth_algs; + break; + case IW_AUTH_WPA_ENABLED: + data->value = local->wpa; + break; + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + data->value = local->ieee_802_1x; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + + +static int prism2_ioctl_siwencodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *extra) +{ + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; + int i, ret = 0; + struct lib80211_crypto_ops *ops; + struct lib80211_crypt_data **crypt; + void *sta_ptr; + u8 *addr; + const char *alg, *module; + + i = erq->flags & IW_ENCODE_INDEX; + if (i > WEP_KEYS) + return -EINVAL; + if (i < 1 || i > WEP_KEYS) + i = local->crypt_info.tx_keyidx; + else + i--; + if (i < 0 || i >= WEP_KEYS) + return -EINVAL; + + addr = ext->addr.sa_data; + if (is_broadcast_ether_addr(addr)) { + sta_ptr = NULL; + crypt = &local->crypt_info.crypt[i]; + } else { + if (i != 0) + return -EINVAL; + sta_ptr = ap_crypt_get_ptrs(local->ap, addr, 0, &crypt); + if (sta_ptr == NULL) { + if (local->iw_mode == IW_MODE_INFRA) { + /* + * TODO: add STA entry for the current AP so + * that unicast key can be used. For now, this + * is emulated by using default key idx 0. + */ + i = 0; + crypt = &local->crypt_info.crypt[i]; + } else + return -EINVAL; + } + } + + if ((erq->flags & IW_ENCODE_DISABLED) || + ext->alg == IW_ENCODE_ALG_NONE) { + if (*crypt) + lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); + goto done; + } + + switch (ext->alg) { + case IW_ENCODE_ALG_WEP: + alg = "WEP"; + module = "lib80211_crypt_wep"; + break; + case IW_ENCODE_ALG_TKIP: + alg = "TKIP"; + module = "lib80211_crypt_tkip"; + break; + case IW_ENCODE_ALG_CCMP: + alg = "CCMP"; + module = "lib80211_crypt_ccmp"; + break; + default: + printk(KERN_DEBUG "%s: unsupported algorithm %d\n", + local->dev->name, ext->alg); + ret = -EOPNOTSUPP; + goto done; + } + + ops = lib80211_get_crypto_ops(alg); + if (ops == NULL) { + request_module(module); + ops = lib80211_get_crypto_ops(alg); + } + if (ops == NULL) { + printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n", + local->dev->name, alg); + ret = -EOPNOTSUPP; + goto done; + } + + if (sta_ptr || ext->alg != IW_ENCODE_ALG_WEP) { + /* + * Per station encryption and other than WEP algorithms + * require host-based encryption, so force them on + * automatically. + */ + local->host_decrypt = local->host_encrypt = 1; + } + + if (*crypt == NULL || (*crypt)->ops != ops) { + struct lib80211_crypt_data *new_crypt; + + lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); + + new_crypt = kzalloc(sizeof(struct lib80211_crypt_data), + GFP_KERNEL); + if (new_crypt == NULL) { + ret = -ENOMEM; + goto done; + } + new_crypt->ops = ops; + if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + new_crypt->priv = new_crypt->ops->init(i); + if (new_crypt->priv == NULL) { + kfree(new_crypt); + ret = -EINVAL; + goto done; + } + + *crypt = new_crypt; + } + + /* + * TODO: if ext_flags does not have IW_ENCODE_EXT_RX_SEQ_VALID, the + * existing seq# should not be changed. + * TODO: if ext_flags has IW_ENCODE_EXT_TX_SEQ_VALID, next TX seq# + * should be changed to something else than zero. + */ + if ((!(ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) || ext->key_len > 0) + && (*crypt)->ops->set_key && + (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, + (*crypt)->priv) < 0) { + printk(KERN_DEBUG "%s: key setting failed\n", + local->dev->name); + ret = -EINVAL; + goto done; + } + + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + if (!sta_ptr) + local->crypt_info.tx_keyidx = i; + } + + + if (sta_ptr == NULL && ext->key_len > 0) { + int first = 1, j; + for (j = 0; j < WEP_KEYS; j++) { + if (j != i && local->crypt_info.crypt[j]) { + first = 0; + break; + } + } + if (first) + local->crypt_info.tx_keyidx = i; + } + + done: + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + local->open_wep = erq->flags & IW_ENCODE_OPEN; + + /* + * Do not reset port0 if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. Prism2 documentation seem to require port reset + * after WEP configuration. However, keys are apparently changed at + * least in Managed mode. + */ + if (ret == 0 && + (hostap_set_encryption(local) || + (local->iw_mode != IW_MODE_INFRA && + local->func->reset_port(local->dev)))) + ret = -EINVAL; + + return ret; +} + + +static int prism2_ioctl_giwencodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *extra) +{ + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + struct lib80211_crypt_data **crypt; + void *sta_ptr; + int max_key_len, i; + struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; + u8 *addr; + + max_key_len = erq->length - sizeof(*ext); + if (max_key_len < 0) + return -EINVAL; + + i = erq->flags & IW_ENCODE_INDEX; + if (i < 1 || i > WEP_KEYS) + i = local->crypt_info.tx_keyidx; + else + i--; + + addr = ext->addr.sa_data; + if (is_broadcast_ether_addr(addr)) { + sta_ptr = NULL; + crypt = &local->crypt_info.crypt[i]; + } else { + i = 0; + sta_ptr = ap_crypt_get_ptrs(local->ap, addr, 0, &crypt); + if (sta_ptr == NULL) + return -EINVAL; + } + erq->flags = i + 1; + memset(ext, 0, sizeof(*ext)); + + if (*crypt == NULL || (*crypt)->ops == NULL) { + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + erq->flags |= IW_ENCODE_DISABLED; + } else { + if (strcmp((*crypt)->ops->name, "WEP") == 0) + ext->alg = IW_ENCODE_ALG_WEP; + else if (strcmp((*crypt)->ops->name, "TKIP") == 0) + ext->alg = IW_ENCODE_ALG_TKIP; + else if (strcmp((*crypt)->ops->name, "CCMP") == 0) + ext->alg = IW_ENCODE_ALG_CCMP; + else + return -EINVAL; + + if ((*crypt)->ops->get_key) { + ext->key_len = + (*crypt)->ops->get_key(ext->key, + max_key_len, + ext->tx_seq, + (*crypt)->priv); + if (ext->key_len && + (ext->alg == IW_ENCODE_ALG_TKIP || + ext->alg == IW_ENCODE_ALG_CCMP)) + ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID; + } + } + + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + return 0; +} + + +static int prism2_ioctl_set_encryption(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int ret = 0; + struct lib80211_crypto_ops *ops; + struct lib80211_crypt_data **crypt; + void *sta_ptr; + + param->u.crypt.err = 0; + param->u.crypt.alg[HOSTAP_CRYPT_ALG_NAME_LEN - 1] = '\0'; + + if (param_len != + (int) ((char *) param->u.crypt.key - (char *) param) + + param->u.crypt.key_len) + return -EINVAL; + + if (is_broadcast_ether_addr(param->sta_addr)) { + if (param->u.crypt.idx >= WEP_KEYS) + return -EINVAL; + sta_ptr = NULL; + crypt = &local->crypt_info.crypt[param->u.crypt.idx]; + } else { + if (param->u.crypt.idx) + return -EINVAL; + sta_ptr = ap_crypt_get_ptrs( + local->ap, param->sta_addr, + (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_PERMANENT), + &crypt); + + if (sta_ptr == NULL) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; + return -EINVAL; + } + } + + if (strcmp(param->u.crypt.alg, "none") == 0) { + if (crypt) + lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); + goto done; + } + + ops = lib80211_get_crypto_ops(param->u.crypt.alg); + if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) { + request_module("lib80211_crypt_wep"); + ops = lib80211_get_crypto_ops(param->u.crypt.alg); + } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) { + request_module("lib80211_crypt_tkip"); + ops = lib80211_get_crypto_ops(param->u.crypt.alg); + } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) { + request_module("lib80211_crypt_ccmp"); + ops = lib80211_get_crypto_ops(param->u.crypt.alg); + } + if (ops == NULL) { + printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n", + local->dev->name, param->u.crypt.alg); + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ALG; + ret = -EINVAL; + goto done; + } + + /* station based encryption and other than WEP algorithms require + * host-based encryption, so force them on automatically */ + local->host_decrypt = local->host_encrypt = 1; + + if (*crypt == NULL || (*crypt)->ops != ops) { + struct lib80211_crypt_data *new_crypt; + + lib80211_crypt_delayed_deinit(&local->crypt_info, crypt); + + new_crypt = kzalloc(sizeof(struct lib80211_crypt_data), + GFP_KERNEL); + if (new_crypt == NULL) { + ret = -ENOMEM; + goto done; + } + new_crypt->ops = ops; + new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx); + if (new_crypt->priv == NULL) { + kfree(new_crypt); + param->u.crypt.err = + HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED; + ret = -EINVAL; + goto done; + } + + *crypt = new_crypt; + } + + if ((!(param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) || + param->u.crypt.key_len > 0) && (*crypt)->ops->set_key && + (*crypt)->ops->set_key(param->u.crypt.key, + param->u.crypt.key_len, param->u.crypt.seq, + (*crypt)->priv) < 0) { + printk(KERN_DEBUG "%s: key setting failed\n", + local->dev->name); + param->u.crypt.err = HOSTAP_CRYPT_ERR_KEY_SET_FAILED; + ret = -EINVAL; + goto done; + } + + if (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) { + if (!sta_ptr) + local->crypt_info.tx_keyidx = param->u.crypt.idx; + else if (param->u.crypt.idx) { + printk(KERN_DEBUG "%s: TX key idx setting failed\n", + local->dev->name); + param->u.crypt.err = + HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED; + ret = -EINVAL; + goto done; + } + } + + done: + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + /* Do not reset port0 if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. Prism2 documentation seem to require port reset + * after WEP configuration. However, keys are apparently changed at + * least in Managed mode. */ + if (ret == 0 && + (hostap_set_encryption(local) || + (local->iw_mode != IW_MODE_INFRA && + local->func->reset_port(local->dev)))) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_CARD_CONF_FAILED; + return -EINVAL; + } + + return ret; +} + + +static int prism2_ioctl_get_encryption(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + struct lib80211_crypt_data **crypt; + void *sta_ptr; + int max_key_len; + + param->u.crypt.err = 0; + + max_key_len = param_len - + (int) ((char *) param->u.crypt.key - (char *) param); + if (max_key_len < 0) + return -EINVAL; + + if (is_broadcast_ether_addr(param->sta_addr)) { + sta_ptr = NULL; + if (param->u.crypt.idx >= WEP_KEYS) + param->u.crypt.idx = local->crypt_info.tx_keyidx; + crypt = &local->crypt_info.crypt[param->u.crypt.idx]; + } else { + param->u.crypt.idx = 0; + sta_ptr = ap_crypt_get_ptrs(local->ap, param->sta_addr, 0, + &crypt); + + if (sta_ptr == NULL) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; + return -EINVAL; + } + } + + if (*crypt == NULL || (*crypt)->ops == NULL) { + memcpy(param->u.crypt.alg, "none", 5); + param->u.crypt.key_len = 0; + param->u.crypt.idx = 0xff; + } else { + strncpy(param->u.crypt.alg, (*crypt)->ops->name, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.key_len = 0; + + memset(param->u.crypt.seq, 0, 8); + if ((*crypt)->ops->get_key) { + param->u.crypt.key_len = + (*crypt)->ops->get_key(param->u.crypt.key, + max_key_len, + param->u.crypt.seq, + (*crypt)->priv); + } + } + + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + return 0; +} + + +static int prism2_ioctl_get_rid(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int max_len, res; + + max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN; + if (max_len < 0) + return -EINVAL; + + res = local->func->get_rid(local->dev, param->u.rid.rid, + param->u.rid.data, param->u.rid.len, 0); + if (res >= 0) { + param->u.rid.len = res; + return 0; + } + + return res; +} + + +static int prism2_ioctl_set_rid(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int max_len; + + max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN; + if (max_len < 0 || max_len < param->u.rid.len) + return -EINVAL; + + return local->func->set_rid(local->dev, param->u.rid.rid, + param->u.rid.data, param->u.rid.len); +} + + +static int prism2_ioctl_set_assoc_ap_addr(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + printk(KERN_DEBUG "%ssta: associated as client with AP %pM\n", + local->dev->name, param->sta_addr); + memcpy(local->assoc_ap_addr, param->sta_addr, ETH_ALEN); + return 0; +} + + +static int prism2_ioctl_siwgenie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + return prism2_set_genericelement(dev, extra, data->length); +} + + +static int prism2_ioctl_giwgenie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + int len = local->generic_elem_len - 2; + + if (len <= 0 || local->generic_elem == NULL) { + data->length = 0; + return 0; + } + + if (data->length < len) + return -E2BIG; + + data->length = len; + memcpy(extra, local->generic_elem + 2, len); + + return 0; +} + + +static int prism2_ioctl_set_generic_element(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int max_len, len; + + len = param->u.generic_elem.len; + max_len = param_len - PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; + if (max_len < 0 || max_len < len) + return -EINVAL; + + return prism2_set_genericelement(local->dev, + param->u.generic_elem.data, len); +} + + +static int prism2_ioctl_siwmlme(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + struct iw_mlme *mlme = (struct iw_mlme *) extra; + __le16 reason; + + reason = cpu_to_le16(mlme->reason_code); + + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + return prism2_sta_send_mgmt(local, mlme->addr.sa_data, + IEEE80211_STYPE_DEAUTH, + (u8 *) &reason, 2); + case IW_MLME_DISASSOC: + return prism2_sta_send_mgmt(local, mlme->addr.sa_data, + IEEE80211_STYPE_DISASSOC, + (u8 *) &reason, 2); + default: + return -EOPNOTSUPP; + } +} + + +static int prism2_ioctl_mlme(local_info_t *local, + struct prism2_hostapd_param *param) +{ + __le16 reason; + + reason = cpu_to_le16(param->u.mlme.reason_code); + switch (param->u.mlme.cmd) { + case MLME_STA_DEAUTH: + return prism2_sta_send_mgmt(local, param->sta_addr, + IEEE80211_STYPE_DEAUTH, + (u8 *) &reason, 2); + case MLME_STA_DISASSOC: + return prism2_sta_send_mgmt(local, param->sta_addr, + IEEE80211_STYPE_DISASSOC, + (u8 *) &reason, 2); + default: + return -EOPNOTSUPP; + } +} + + +static int prism2_ioctl_scan_req(local_info_t *local, + struct prism2_hostapd_param *param) +{ +#ifndef PRISM2_NO_STATION_MODES + if ((local->iw_mode != IW_MODE_INFRA && + local->iw_mode != IW_MODE_ADHOC) || + (local->sta_fw_ver < PRISM2_FW_VER(1,3,1))) + return -EOPNOTSUPP; + + if (!local->dev_enabled) + return -ENETDOWN; + + return prism2_request_hostscan(local->dev, param->u.scan_req.ssid, + param->u.scan_req.ssid_len); +#else /* PRISM2_NO_STATION_MODES */ + return -EOPNOTSUPP; +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_priv_hostapd(local_info_t *local, struct iw_point *p) +{ + struct prism2_hostapd_param *param; + int ret = 0; + int ap_ioctl = 0; + + if (p->length < sizeof(struct prism2_hostapd_param) || + p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer) + return -EINVAL; + + param = kmalloc(p->length, GFP_KERNEL); + if (param == NULL) + return -ENOMEM; + + if (copy_from_user(param, p->pointer, p->length)) { + ret = -EFAULT; + goto out; + } + + switch (param->cmd) { + case PRISM2_SET_ENCRYPTION: + ret = prism2_ioctl_set_encryption(local, param, p->length); + break; + case PRISM2_GET_ENCRYPTION: + ret = prism2_ioctl_get_encryption(local, param, p->length); + break; + case PRISM2_HOSTAPD_GET_RID: + ret = prism2_ioctl_get_rid(local, param, p->length); + break; + case PRISM2_HOSTAPD_SET_RID: + ret = prism2_ioctl_set_rid(local, param, p->length); + break; + case PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR: + ret = prism2_ioctl_set_assoc_ap_addr(local, param, p->length); + break; + case PRISM2_HOSTAPD_SET_GENERIC_ELEMENT: + ret = prism2_ioctl_set_generic_element(local, param, + p->length); + break; + case PRISM2_HOSTAPD_MLME: + ret = prism2_ioctl_mlme(local, param); + break; + case PRISM2_HOSTAPD_SCAN_REQ: + ret = prism2_ioctl_scan_req(local, param); + break; + default: + ret = prism2_hostapd(local->ap, param); + ap_ioctl = 1; + break; + } + + if (ret == 1 || !ap_ioctl) { + if (copy_to_user(p->pointer, param, p->length)) { + ret = -EFAULT; + goto out; + } else if (ap_ioctl) + ret = 0; + } + + out: + kfree(param); + return ret; +} + + +static void prism2_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + strlcpy(info->driver, "hostap", sizeof(info->driver)); + snprintf(info->fw_version, sizeof(info->fw_version), + "%d.%d.%d", (local->sta_fw_ver >> 16) & 0xff, + (local->sta_fw_ver >> 8) & 0xff, + local->sta_fw_ver & 0xff); +} + +const struct ethtool_ops prism2_ethtool_ops = { + .get_drvinfo = prism2_get_drvinfo +}; + + +/* Structures to export the Wireless Handlers */ + +static const iw_handler prism2_handler[] = +{ + (iw_handler) NULL, /* SIOCSIWCOMMIT */ + (iw_handler) prism2_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) prism2_ioctl_siwfreq, /* SIOCSIWFREQ */ + (iw_handler) prism2_ioctl_giwfreq, /* SIOCGIWFREQ */ + (iw_handler) prism2_ioctl_siwmode, /* SIOCSIWMODE */ + (iw_handler) prism2_ioctl_giwmode, /* SIOCGIWMODE */ + (iw_handler) prism2_ioctl_siwsens, /* SIOCSIWSENS */ + (iw_handler) prism2_ioctl_giwsens, /* SIOCGIWSENS */ + (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ + (iw_handler) prism2_ioctl_giwrange, /* SIOCGIWRANGE */ + (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ + (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ + (iw_handler) prism2_ioctl_siwap, /* SIOCSIWAP */ + (iw_handler) prism2_ioctl_giwap, /* SIOCGIWAP */ + (iw_handler) prism2_ioctl_siwmlme, /* SIOCSIWMLME */ + (iw_handler) prism2_ioctl_giwaplist, /* SIOCGIWAPLIST */ + (iw_handler) prism2_ioctl_siwscan, /* SIOCSIWSCAN */ + (iw_handler) prism2_ioctl_giwscan, /* SIOCGIWSCAN */ + (iw_handler) prism2_ioctl_siwessid, /* SIOCSIWESSID */ + (iw_handler) prism2_ioctl_giwessid, /* SIOCGIWESSID */ + (iw_handler) prism2_ioctl_siwnickn, /* SIOCSIWNICKN */ + (iw_handler) prism2_ioctl_giwnickn, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) prism2_ioctl_siwrate, /* SIOCSIWRATE */ + (iw_handler) prism2_ioctl_giwrate, /* SIOCGIWRATE */ + (iw_handler) prism2_ioctl_siwrts, /* SIOCSIWRTS */ + (iw_handler) prism2_ioctl_giwrts, /* SIOCGIWRTS */ + (iw_handler) prism2_ioctl_siwfrag, /* SIOCSIWFRAG */ + (iw_handler) prism2_ioctl_giwfrag, /* SIOCGIWFRAG */ + (iw_handler) prism2_ioctl_siwtxpow, /* SIOCSIWTXPOW */ + (iw_handler) prism2_ioctl_giwtxpow, /* SIOCGIWTXPOW */ + (iw_handler) prism2_ioctl_siwretry, /* SIOCSIWRETRY */ + (iw_handler) prism2_ioctl_giwretry, /* SIOCGIWRETRY */ + (iw_handler) prism2_ioctl_siwencode, /* SIOCSIWENCODE */ + (iw_handler) prism2_ioctl_giwencode, /* SIOCGIWENCODE */ + (iw_handler) prism2_ioctl_siwpower, /* SIOCSIWPOWER */ + (iw_handler) prism2_ioctl_giwpower, /* SIOCGIWPOWER */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) prism2_ioctl_siwgenie, /* SIOCSIWGENIE */ + (iw_handler) prism2_ioctl_giwgenie, /* SIOCGIWGENIE */ + (iw_handler) prism2_ioctl_siwauth, /* SIOCSIWAUTH */ + (iw_handler) prism2_ioctl_giwauth, /* SIOCGIWAUTH */ + (iw_handler) prism2_ioctl_siwencodeext, /* SIOCSIWENCODEEXT */ + (iw_handler) prism2_ioctl_giwencodeext, /* SIOCGIWENCODEEXT */ + (iw_handler) NULL, /* SIOCSIWPMKSA */ + (iw_handler) NULL, /* -- hole -- */ +}; + +static const iw_handler prism2_private_handler[] = +{ /* SIOCIWFIRSTPRIV + */ + (iw_handler) prism2_ioctl_priv_prism2_param, /* 0 */ + (iw_handler) prism2_ioctl_priv_get_prism2_param, /* 1 */ + (iw_handler) prism2_ioctl_priv_writemif, /* 2 */ + (iw_handler) prism2_ioctl_priv_readmif, /* 3 */ +}; + +const struct iw_handler_def hostap_iw_handler_def = +{ + .num_standard = ARRAY_SIZE(prism2_handler), + .num_private = ARRAY_SIZE(prism2_private_handler), + .num_private_args = ARRAY_SIZE(prism2_priv), + .standard = (iw_handler *) prism2_handler, + .private = (iw_handler *) prism2_private_handler, + .private_args = (struct iw_priv_args *) prism2_priv, + .get_wireless_stats = hostap_get_wireless_stats, +}; + + +int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct iwreq *wrq = (struct iwreq *) ifr; + struct hostap_interface *iface; + local_info_t *local; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + switch (cmd) { + /* Private ioctls (iwpriv) that have not yet been converted + * into new wireless extensions API */ + + case PRISM2_IOCTL_INQUIRE: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_inquire(dev, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_MONITOR: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_monitor(dev, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_RESET: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_reset(dev, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_WDS_ADD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_wds_add(local, wrq->u.ap_addr.sa_data, 1); + break; + + case PRISM2_IOCTL_WDS_DEL: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_wds_del(local, wrq->u.ap_addr.sa_data, 1, 0); + break; + + case PRISM2_IOCTL_SET_RID_WORD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_set_rid_word(dev, + (int *) wrq->u.name); + break; + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + case PRISM2_IOCTL_MACCMD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_mac_cmd_ioctl(local, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_ADDMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_add_mac(&local->ap->mac_restrictions, + wrq->u.ap_addr.sa_data); + break; + case PRISM2_IOCTL_DELMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_del_mac(&local->ap->mac_restrictions, + wrq->u.ap_addr.sa_data); + break; + case PRISM2_IOCTL_KICKMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_kick_mac(local->ap, local->dev, + wrq->u.ap_addr.sa_data); + break; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + + /* Private ioctls that are not used with iwpriv; + * in SIOCDEVPRIVATE range */ + +#ifdef PRISM2_DOWNLOAD_SUPPORT + case PRISM2_IOCTL_DOWNLOAD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_download(local, &wrq->u.data); + break; +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + case PRISM2_IOCTL_HOSTAPD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_hostapd(local, &wrq->u.data); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} diff --git a/drivers/net/wireless/intersil/hostap/hostap_main.c b/drivers/net/wireless/intersil/hostap/hostap_main.c new file mode 100644 index 000000000000..80d4228ba754 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_main.c @@ -0,0 +1,1140 @@ +/* + * Host AP (software wireless LAN access point) driver for + * Intersil Prism2/2.5/3 - hostap.o module, common routines + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_wlan.h" +#include "hostap_80211.h" +#include "hostap_ap.h" +#include "hostap.h" + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP common routines"); +MODULE_LICENSE("GPL"); + +#define TX_TIMEOUT (2 * HZ) + +#define PRISM2_MAX_FRAME_SIZE 2304 +#define PRISM2_MIN_MTU 256 +/* FIX: */ +#define PRISM2_MAX_MTU (PRISM2_MAX_FRAME_SIZE - (6 /* LLC */ + 8 /* WEP */)) + + +struct net_device * hostap_add_interface(struct local_info *local, + int type, int rtnl_locked, + const char *prefix, + const char *name) +{ + struct net_device *dev, *mdev; + struct hostap_interface *iface; + int ret; + + dev = alloc_etherdev(sizeof(struct hostap_interface)); + if (dev == NULL) + return NULL; + + iface = netdev_priv(dev); + iface->dev = dev; + iface->local = local; + iface->type = type; + list_add(&iface->list, &local->hostap_interfaces); + + mdev = local->dev; + eth_hw_addr_inherit(dev, mdev); + dev->base_addr = mdev->base_addr; + dev->irq = mdev->irq; + dev->mem_start = mdev->mem_start; + dev->mem_end = mdev->mem_end; + + hostap_setup_dev(dev, local, type); + dev->destructor = free_netdev; + + sprintf(dev->name, "%s%s", prefix, name); + if (!rtnl_locked) + rtnl_lock(); + + SET_NETDEV_DEV(dev, mdev->dev.parent); + ret = register_netdevice(dev); + + if (!rtnl_locked) + rtnl_unlock(); + + if (ret < 0) { + printk(KERN_WARNING "%s: failed to add new netdevice!\n", + dev->name); + free_netdev(dev); + return NULL; + } + + printk(KERN_DEBUG "%s: registered netdevice %s\n", + mdev->name, dev->name); + + return dev; +} + + +void hostap_remove_interface(struct net_device *dev, int rtnl_locked, + int remove_from_list) +{ + struct hostap_interface *iface; + + if (!dev) + return; + + iface = netdev_priv(dev); + + if (remove_from_list) { + list_del(&iface->list); + } + + if (dev == iface->local->ddev) + iface->local->ddev = NULL; + else if (dev == iface->local->apdev) + iface->local->apdev = NULL; + else if (dev == iface->local->stadev) + iface->local->stadev = NULL; + + if (rtnl_locked) + unregister_netdevice(dev); + else + unregister_netdev(dev); + + /* dev->destructor = free_netdev() will free the device data, including + * private data, when removing the device */ +} + + +static inline int prism2_wds_special_addr(u8 *addr) +{ + if (addr[0] || addr[1] || addr[2] || addr[3] || addr[4] || addr[5]) + return 0; + + return 1; +} + + +int prism2_wds_add(local_info_t *local, u8 *remote_addr, + int rtnl_locked) +{ + struct net_device *dev; + struct list_head *ptr; + struct hostap_interface *iface, *empty, *match; + + empty = match = NULL; + read_lock_bh(&local->iface_lock); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type != HOSTAP_INTERFACE_WDS) + continue; + + if (prism2_wds_special_addr(iface->u.wds.remote_addr)) + empty = iface; + else if (ether_addr_equal(iface->u.wds.remote_addr, remote_addr)) { + match = iface; + break; + } + } + if (!match && empty && !prism2_wds_special_addr(remote_addr)) { + /* take pre-allocated entry into use */ + memcpy(empty->u.wds.remote_addr, remote_addr, ETH_ALEN); + read_unlock_bh(&local->iface_lock); + printk(KERN_DEBUG "%s: using pre-allocated WDS netdevice %s\n", + local->dev->name, empty->dev->name); + return 0; + } + read_unlock_bh(&local->iface_lock); + + if (!prism2_wds_special_addr(remote_addr)) { + if (match) + return -EEXIST; + hostap_add_sta(local->ap, remote_addr); + } + + if (local->wds_connections >= local->wds_max_connections) + return -ENOBUFS; + + /* verify that there is room for wds# postfix in the interface name */ + if (strlen(local->dev->name) >= IFNAMSIZ - 5) { + printk(KERN_DEBUG "'%s' too long base device name\n", + local->dev->name); + return -EINVAL; + } + + dev = hostap_add_interface(local, HOSTAP_INTERFACE_WDS, rtnl_locked, + local->ddev->name, "wds%d"); + if (dev == NULL) + return -ENOMEM; + + iface = netdev_priv(dev); + memcpy(iface->u.wds.remote_addr, remote_addr, ETH_ALEN); + + local->wds_connections++; + + return 0; +} + + +int prism2_wds_del(local_info_t *local, u8 *remote_addr, + int rtnl_locked, int do_not_remove) +{ + unsigned long flags; + struct list_head *ptr; + struct hostap_interface *iface, *selected = NULL; + + write_lock_irqsave(&local->iface_lock, flags); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type != HOSTAP_INTERFACE_WDS) + continue; + + if (ether_addr_equal(iface->u.wds.remote_addr, remote_addr)) { + selected = iface; + break; + } + } + if (selected && !do_not_remove) + list_del(&selected->list); + write_unlock_irqrestore(&local->iface_lock, flags); + + if (selected) { + if (do_not_remove) + eth_zero_addr(selected->u.wds.remote_addr); + else { + hostap_remove_interface(selected->dev, rtnl_locked, 0); + local->wds_connections--; + } + } + + return selected ? 0 : -ENODEV; +} + + +u16 hostap_tx_callback_register(local_info_t *local, + void (*func)(struct sk_buff *, int ok, void *), + void *data) +{ + unsigned long flags; + struct hostap_tx_callback_info *entry; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (entry == NULL) + return 0; + + entry->func = func; + entry->data = data; + + spin_lock_irqsave(&local->lock, flags); + entry->idx = local->tx_callback ? local->tx_callback->idx + 1 : 1; + entry->next = local->tx_callback; + local->tx_callback = entry; + spin_unlock_irqrestore(&local->lock, flags); + + return entry->idx; +} + + +int hostap_tx_callback_unregister(local_info_t *local, u16 idx) +{ + unsigned long flags; + struct hostap_tx_callback_info *cb, *prev = NULL; + + spin_lock_irqsave(&local->lock, flags); + cb = local->tx_callback; + while (cb != NULL && cb->idx != idx) { + prev = cb; + cb = cb->next; + } + if (cb) { + if (prev == NULL) + local->tx_callback = cb->next; + else + prev->next = cb->next; + kfree(cb); + } + spin_unlock_irqrestore(&local->lock, flags); + + return cb ? 0 : -1; +} + + +/* val is in host byte order */ +int hostap_set_word(struct net_device *dev, int rid, u16 val) +{ + struct hostap_interface *iface; + __le16 tmp = cpu_to_le16(val); + iface = netdev_priv(dev); + return iface->local->func->set_rid(dev, rid, &tmp, 2); +} + + +int hostap_set_string(struct net_device *dev, int rid, const char *val) +{ + struct hostap_interface *iface; + char buf[MAX_SSID_LEN + 2]; + int len; + + iface = netdev_priv(dev); + len = strlen(val); + if (len > MAX_SSID_LEN) + return -1; + memset(buf, 0, sizeof(buf)); + buf[0] = len; /* little endian 16 bit word */ + memcpy(buf + 2, val, len); + + return iface->local->func->set_rid(dev, rid, &buf, MAX_SSID_LEN + 2); +} + + +u16 hostap_get_porttype(local_info_t *local) +{ + if (local->iw_mode == IW_MODE_ADHOC && local->pseudo_adhoc) + return HFA384X_PORTTYPE_PSEUDO_IBSS; + if (local->iw_mode == IW_MODE_ADHOC) + return HFA384X_PORTTYPE_IBSS; + if (local->iw_mode == IW_MODE_INFRA) + return HFA384X_PORTTYPE_BSS; + if (local->iw_mode == IW_MODE_REPEAT) + return HFA384X_PORTTYPE_WDS; + if (local->iw_mode == IW_MODE_MONITOR) + return HFA384X_PORTTYPE_PSEUDO_IBSS; + return HFA384X_PORTTYPE_HOSTAP; +} + + +int hostap_set_encryption(local_info_t *local) +{ + u16 val, old_val; + int i, keylen, len, idx; + char keybuf[WEP_KEY_LEN + 1]; + enum { NONE, WEP, OTHER } encrypt_type; + + idx = local->crypt_info.tx_keyidx; + if (local->crypt_info.crypt[idx] == NULL || + local->crypt_info.crypt[idx]->ops == NULL) + encrypt_type = NONE; + else if (strcmp(local->crypt_info.crypt[idx]->ops->name, "WEP") == 0) + encrypt_type = WEP; + else + encrypt_type = OTHER; + + if (local->func->get_rid(local->dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, + 1) < 0) { + printk(KERN_DEBUG "Could not read current WEP flags.\n"); + goto fail; + } + le16_to_cpus(&val); + old_val = val; + + if (encrypt_type != NONE || local->privacy_invoked) + val |= HFA384X_WEPFLAGS_PRIVACYINVOKED; + else + val &= ~HFA384X_WEPFLAGS_PRIVACYINVOKED; + + if (local->open_wep || encrypt_type == NONE || + ((local->ieee_802_1x || local->wpa) && local->host_decrypt)) + val &= ~HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; + else + val |= HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; + + if ((encrypt_type != NONE || local->privacy_invoked) && + (encrypt_type == OTHER || local->host_encrypt)) + val |= HFA384X_WEPFLAGS_HOSTENCRYPT; + else + val &= ~HFA384X_WEPFLAGS_HOSTENCRYPT; + if ((encrypt_type != NONE || local->privacy_invoked) && + (encrypt_type == OTHER || local->host_decrypt)) + val |= HFA384X_WEPFLAGS_HOSTDECRYPT; + else + val &= ~HFA384X_WEPFLAGS_HOSTDECRYPT; + + if (val != old_val && + hostap_set_word(local->dev, HFA384X_RID_CNFWEPFLAGS, val)) { + printk(KERN_DEBUG "Could not write new WEP flags (0x%x)\n", + val); + goto fail; + } + + if (encrypt_type != WEP) + return 0; + + /* 104-bit support seems to require that all the keys are set to the + * same keylen */ + keylen = 6; /* first 5 octets */ + len = local->crypt_info.crypt[idx]->ops->get_key(keybuf, sizeof(keybuf), NULL, + local->crypt_info.crypt[idx]->priv); + if (idx >= 0 && idx < WEP_KEYS && len > 5) + keylen = WEP_KEY_LEN + 1; /* first 13 octets */ + + for (i = 0; i < WEP_KEYS; i++) { + memset(keybuf, 0, sizeof(keybuf)); + if (local->crypt_info.crypt[i]) { + (void) local->crypt_info.crypt[i]->ops->get_key( + keybuf, sizeof(keybuf), + NULL, local->crypt_info.crypt[i]->priv); + } + if (local->func->set_rid(local->dev, + HFA384X_RID_CNFDEFAULTKEY0 + i, + keybuf, keylen)) { + printk(KERN_DEBUG "Could not set key %d (len=%d)\n", + i, keylen); + goto fail; + } + } + if (hostap_set_word(local->dev, HFA384X_RID_CNFWEPDEFAULTKEYID, idx)) { + printk(KERN_DEBUG "Could not set default keyid %d\n", idx); + goto fail; + } + + return 0; + + fail: + printk(KERN_DEBUG "%s: encryption setup failed\n", local->dev->name); + return -1; +} + + +int hostap_set_antsel(local_info_t *local) +{ + u16 val; + int ret = 0; + + if (local->antsel_tx != HOSTAP_ANTSEL_DO_NOT_TOUCH && + local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF, + HFA386X_CR_TX_CONFIGURE, + NULL, &val) == 0) { + val &= ~(BIT(2) | BIT(1)); + switch (local->antsel_tx) { + case HOSTAP_ANTSEL_DIVERSITY: + val |= BIT(1); + break; + case HOSTAP_ANTSEL_LOW: + break; + case HOSTAP_ANTSEL_HIGH: + val |= BIT(2); + break; + } + + if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_TX_CONFIGURE, &val, NULL)) { + printk(KERN_INFO "%s: setting TX AntSel failed\n", + local->dev->name); + ret = -1; + } + } + + if (local->antsel_rx != HOSTAP_ANTSEL_DO_NOT_TOUCH && + local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF, + HFA386X_CR_RX_CONFIGURE, + NULL, &val) == 0) { + val &= ~(BIT(1) | BIT(0)); + switch (local->antsel_rx) { + case HOSTAP_ANTSEL_DIVERSITY: + break; + case HOSTAP_ANTSEL_LOW: + val |= BIT(0); + break; + case HOSTAP_ANTSEL_HIGH: + val |= BIT(0) | BIT(1); + break; + } + + if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_RX_CONFIGURE, &val, NULL)) { + printk(KERN_INFO "%s: setting RX AntSel failed\n", + local->dev->name); + ret = -1; + } + } + + return ret; +} + + +int hostap_set_roaming(local_info_t *local) +{ + u16 val; + + switch (local->host_roaming) { + case 1: + val = HFA384X_ROAMING_HOST; + break; + case 2: + val = HFA384X_ROAMING_DISABLED; + break; + case 0: + default: + val = HFA384X_ROAMING_FIRMWARE; + break; + } + + return hostap_set_word(local->dev, HFA384X_RID_CNFROAMINGMODE, val); +} + + +int hostap_set_auth_algs(local_info_t *local) +{ + int val = local->auth_algs; + /* At least STA f/w v0.6.2 seems to have issues with cnfAuthentication + * set to include both Open and Shared Key flags. It tries to use + * Shared Key authentication in that case even if WEP keys are not + * configured.. STA f/w v0.7.6 is able to handle such configuration, + * but it is unknown when this was fixed between 0.6.2 .. 0.7.6. */ + if (local->sta_fw_ver < PRISM2_FW_VER(0,7,0) && + val != PRISM2_AUTH_OPEN && val != PRISM2_AUTH_SHARED_KEY) + val = PRISM2_AUTH_OPEN; + + if (hostap_set_word(local->dev, HFA384X_RID_CNFAUTHENTICATION, val)) { + printk(KERN_INFO "%s: cnfAuthentication setting to 0x%x " + "failed\n", local->dev->name, local->auth_algs); + return -EINVAL; + } + + return 0; +} + + +void hostap_dump_rx_header(const char *name, const struct hfa384x_rx_frame *rx) +{ + u16 status, fc; + + status = __le16_to_cpu(rx->status); + + printk(KERN_DEBUG "%s: RX status=0x%04x (port=%d, type=%d, " + "fcserr=%d) silence=%d signal=%d rate=%d rxflow=%d; " + "jiffies=%ld\n", + name, status, (status >> 8) & 0x07, status >> 13, status & 1, + rx->silence, rx->signal, rx->rate, rx->rxflow, jiffies); + + fc = __le16_to_cpu(rx->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x " + "data_len=%d%s%s\n", + fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, + (fc & IEEE80211_FCTL_STYPE) >> 4, + __le16_to_cpu(rx->duration_id), __le16_to_cpu(rx->seq_ctrl), + __le16_to_cpu(rx->data_len), + fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", + fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); + + printk(KERN_DEBUG " A1=%pM A2=%pM A3=%pM A4=%pM\n", + rx->addr1, rx->addr2, rx->addr3, rx->addr4); + + printk(KERN_DEBUG " dst=%pM src=%pM len=%d\n", + rx->dst_addr, rx->src_addr, + __be16_to_cpu(rx->len)); +} + + +void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx) +{ + u16 fc; + + printk(KERN_DEBUG "%s: TX status=0x%04x retry_count=%d tx_rate=%d " + "tx_control=0x%04x; jiffies=%ld\n", + name, __le16_to_cpu(tx->status), tx->retry_count, tx->tx_rate, + __le16_to_cpu(tx->tx_control), jiffies); + + fc = __le16_to_cpu(tx->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x " + "data_len=%d%s%s\n", + fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, + (fc & IEEE80211_FCTL_STYPE) >> 4, + __le16_to_cpu(tx->duration_id), __le16_to_cpu(tx->seq_ctrl), + __le16_to_cpu(tx->data_len), + fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", + fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); + + printk(KERN_DEBUG " A1=%pM A2=%pM A3=%pM A4=%pM\n", + tx->addr1, tx->addr2, tx->addr3, tx->addr4); + + printk(KERN_DEBUG " dst=%pM src=%pM len=%d\n", + tx->dst_addr, tx->src_addr, + __be16_to_cpu(tx->len)); +} + + +static int hostap_80211_header_parse(const struct sk_buff *skb, + unsigned char *haddr) +{ + memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */ + return ETH_ALEN; +} + + +int hostap_80211_get_hdrlen(__le16 fc) +{ + if (ieee80211_is_data(fc) && ieee80211_has_a4 (fc)) + return 30; /* Addr4 */ + else if (ieee80211_is_cts(fc) || ieee80211_is_ack(fc)) + return 10; + else if (ieee80211_is_ctl(fc)) + return 16; + + return 24; +} + + +static int prism2_close(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + + PDEBUG(DEBUG_FLOW, "%s: prism2_close\n", dev->name); + + iface = netdev_priv(dev); + local = iface->local; + + if (dev == local->ddev) { + prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING); + } +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (!local->hostapd && dev == local->dev && + (!local->func->card_present || local->func->card_present(local)) && + local->hw_ready && local->ap && local->iw_mode == IW_MODE_MASTER) + hostap_deauth_all_stas(dev, local->ap, 1); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + if (dev == local->dev) { + local->func->hw_shutdown(dev, HOSTAP_HW_ENABLE_CMDCOMPL); + } + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + + cancel_work_sync(&local->reset_queue); + cancel_work_sync(&local->set_multicast_list_queue); + cancel_work_sync(&local->set_tim_queue); +#ifndef PRISM2_NO_STATION_MODES + cancel_work_sync(&local->info_queue); +#endif + cancel_work_sync(&local->comms_qual_update); + + module_put(local->hw_module); + + local->num_dev_open--; + + if (dev != local->dev && local->dev->flags & IFF_UP && + local->master_dev_auto_open && local->num_dev_open == 1) { + /* Close master radio interface automatically if it was also + * opened automatically and we are now closing the last + * remaining non-master device. */ + dev_close(local->dev); + } + + return 0; +} + + +static int prism2_open(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + + PDEBUG(DEBUG_FLOW, "%s: prism2_open\n", dev->name); + + iface = netdev_priv(dev); + local = iface->local; + + if (local->no_pri) { + printk(KERN_DEBUG "%s: could not set interface UP - no PRI " + "f/w\n", dev->name); + return -ENODEV; + } + + if ((local->func->card_present && !local->func->card_present(local)) || + local->hw_downloading) + return -ENODEV; + + if (!try_module_get(local->hw_module)) + return -ENODEV; + local->num_dev_open++; + + if (!local->dev_enabled && local->func->hw_enable(dev, 1)) { + printk(KERN_WARNING "%s: could not enable MAC port\n", + dev->name); + prism2_close(dev); + return -ENODEV; + } + if (!local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_ENABLE); + local->dev_enabled = 1; + + if (dev != local->dev && !(local->dev->flags & IFF_UP)) { + /* Master radio interface is needed for all operation, so open + * it automatically when any virtual net_device is opened. */ + local->master_dev_auto_open = 1; + dev_open(local->dev); + } + + netif_device_attach(dev); + netif_start_queue(dev); + + return 0; +} + + +static int prism2_set_mac_address(struct net_device *dev, void *p) +{ + struct hostap_interface *iface; + local_info_t *local; + struct list_head *ptr; + struct sockaddr *addr = p; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->set_rid(dev, HFA384X_RID_CNFOWNMACADDR, addr->sa_data, + ETH_ALEN) < 0 || local->func->reset_port(dev)) + return -EINVAL; + + read_lock_bh(&local->iface_lock); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + memcpy(iface->dev->dev_addr, addr->sa_data, ETH_ALEN); + } + memcpy(local->dev->dev_addr, addr->sa_data, ETH_ALEN); + read_unlock_bh(&local->iface_lock); + + return 0; +} + + +/* TODO: to be further implemented as soon as Prism2 fully supports + * GroupAddresses and correct documentation is available */ +void hostap_set_multicast_list_queue(struct work_struct *work) +{ + local_info_t *local = + container_of(work, local_info_t, set_multicast_list_queue); + struct net_device *dev = local->dev; + + if (hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, + local->is_promisc)) { + printk(KERN_INFO "%s: %sabling promiscuous mode failed\n", + dev->name, local->is_promisc ? "en" : "dis"); + } +} + + +static void hostap_set_multicast_list(struct net_device *dev) +{ +#if 0 + /* FIX: promiscuous mode seems to be causing a lot of problems with + * some station firmware versions (FCSErr frames, invalid MACPort, etc. + * corrupted incoming frames). This code is now commented out while the + * problems are investigated. */ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + if ((dev->flags & IFF_ALLMULTI) || (dev->flags & IFF_PROMISC)) { + local->is_promisc = 1; + } else { + local->is_promisc = 0; + } + + schedule_work(&local->set_multicast_list_queue); +#endif +} + + +static int prism2_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu < PRISM2_MIN_MTU || new_mtu > PRISM2_MAX_MTU) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + + +static void prism2_tx_timeout(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_regs regs; + + iface = netdev_priv(dev); + local = iface->local; + + printk(KERN_WARNING "%s Tx timed out! Resetting card\n", dev->name); + netif_stop_queue(local->dev); + + local->func->read_regs(dev, ®s); + printk(KERN_DEBUG "%s: CMD=%04x EVSTAT=%04x " + "OFFSET0=%04x OFFSET1=%04x SWSUPPORT0=%04x\n", + dev->name, regs.cmd, regs.evstat, regs.offset0, regs.offset1, + regs.swsupport0); + + local->func->schedule_reset(local); +} + +const struct header_ops hostap_80211_ops = { + .create = eth_header, + .cache = eth_header_cache, + .cache_update = eth_header_cache_update, + .parse = hostap_80211_header_parse, +}; +EXPORT_SYMBOL(hostap_80211_ops); + + +static const struct net_device_ops hostap_netdev_ops = { + .ndo_start_xmit = hostap_data_start_xmit, + + .ndo_open = prism2_open, + .ndo_stop = prism2_close, + .ndo_do_ioctl = hostap_ioctl, + .ndo_set_mac_address = prism2_set_mac_address, + .ndo_set_rx_mode = hostap_set_multicast_list, + .ndo_change_mtu = prism2_change_mtu, + .ndo_tx_timeout = prism2_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +static const struct net_device_ops hostap_mgmt_netdev_ops = { + .ndo_start_xmit = hostap_mgmt_start_xmit, + + .ndo_open = prism2_open, + .ndo_stop = prism2_close, + .ndo_do_ioctl = hostap_ioctl, + .ndo_set_mac_address = prism2_set_mac_address, + .ndo_set_rx_mode = hostap_set_multicast_list, + .ndo_change_mtu = prism2_change_mtu, + .ndo_tx_timeout = prism2_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +static const struct net_device_ops hostap_master_ops = { + .ndo_start_xmit = hostap_master_start_xmit, + + .ndo_open = prism2_open, + .ndo_stop = prism2_close, + .ndo_do_ioctl = hostap_ioctl, + .ndo_set_mac_address = prism2_set_mac_address, + .ndo_set_rx_mode = hostap_set_multicast_list, + .ndo_change_mtu = prism2_change_mtu, + .ndo_tx_timeout = prism2_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +void hostap_setup_dev(struct net_device *dev, local_info_t *local, + int type) +{ + struct hostap_interface *iface; + + iface = netdev_priv(dev); + ether_setup(dev); + dev->priv_flags &= ~IFF_TX_SKB_SHARING; + + /* kernel callbacks */ + if (iface) { + /* Currently, we point to the proper spy_data only on + * the main_dev. This could be fixed. Jean II */ + iface->wireless_data.spy_data = &iface->spy_data; + dev->wireless_data = &iface->wireless_data; + } + dev->wireless_handlers = &hostap_iw_handler_def; + dev->watchdog_timeo = TX_TIMEOUT; + + switch(type) { + case HOSTAP_INTERFACE_AP: + dev->priv_flags |= IFF_NO_QUEUE; /* use main radio device queue */ + dev->netdev_ops = &hostap_mgmt_netdev_ops; + dev->type = ARPHRD_IEEE80211; + dev->header_ops = &hostap_80211_ops; + break; + case HOSTAP_INTERFACE_MASTER: + dev->netdev_ops = &hostap_master_ops; + break; + default: + dev->priv_flags |= IFF_NO_QUEUE; /* use main radio device queue */ + dev->netdev_ops = &hostap_netdev_ops; + } + + dev->mtu = local->mtu; + + + dev->ethtool_ops = &prism2_ethtool_ops; + +} + +static int hostap_enable_hostapd(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + if (local->apdev) + return -EEXIST; + + printk(KERN_DEBUG "%s: enabling hostapd mode\n", dev->name); + + local->apdev = hostap_add_interface(local, HOSTAP_INTERFACE_AP, + rtnl_locked, local->ddev->name, + "ap"); + if (local->apdev == NULL) + return -ENOMEM; + + return 0; +} + + +static int hostap_disable_hostapd(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name); + + hostap_remove_interface(local->apdev, rtnl_locked, 1); + local->apdev = NULL; + + return 0; +} + + +static int hostap_enable_hostapd_sta(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + if (local->stadev) + return -EEXIST; + + printk(KERN_DEBUG "%s: enabling hostapd STA mode\n", dev->name); + + local->stadev = hostap_add_interface(local, HOSTAP_INTERFACE_STA, + rtnl_locked, local->ddev->name, + "sta"); + if (local->stadev == NULL) + return -ENOMEM; + + return 0; +} + + +static int hostap_disable_hostapd_sta(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name); + + hostap_remove_interface(local->stadev, rtnl_locked, 1); + local->stadev = NULL; + + return 0; +} + + +int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked) +{ + int ret; + + if (val < 0 || val > 1) + return -EINVAL; + + if (local->hostapd == val) + return 0; + + if (val) { + ret = hostap_enable_hostapd(local, rtnl_locked); + if (ret == 0) + local->hostapd = 1; + } else { + local->hostapd = 0; + ret = hostap_disable_hostapd(local, rtnl_locked); + if (ret != 0) + local->hostapd = 1; + } + + return ret; +} + + +int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked) +{ + int ret; + + if (val < 0 || val > 1) + return -EINVAL; + + if (local->hostapd_sta == val) + return 0; + + if (val) { + ret = hostap_enable_hostapd_sta(local, rtnl_locked); + if (ret == 0) + local->hostapd_sta = 1; + } else { + local->hostapd_sta = 0; + ret = hostap_disable_hostapd_sta(local, rtnl_locked); + if (ret != 0) + local->hostapd_sta = 1; + } + + + return ret; +} + + +int prism2_update_comms_qual(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret = 0; + struct hfa384x_comms_quality sq; + + iface = netdev_priv(dev); + local = iface->local; + if (!local->sta_fw_ver) + ret = -1; + else if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) { + if (local->func->get_rid(local->dev, + HFA384X_RID_DBMCOMMSQUALITY, + &sq, sizeof(sq), 1) >= 0) { + local->comms_qual = (s16) le16_to_cpu(sq.comm_qual); + local->avg_signal = (s16) le16_to_cpu(sq.signal_level); + local->avg_noise = (s16) le16_to_cpu(sq.noise_level); + local->last_comms_qual_update = jiffies; + } else + ret = -1; + } else { + if (local->func->get_rid(local->dev, HFA384X_RID_COMMSQUALITY, + &sq, sizeof(sq), 1) >= 0) { + local->comms_qual = le16_to_cpu(sq.comm_qual); + local->avg_signal = HFA384X_LEVEL_TO_dBm( + le16_to_cpu(sq.signal_level)); + local->avg_noise = HFA384X_LEVEL_TO_dBm( + le16_to_cpu(sq.noise_level)); + local->last_comms_qual_update = jiffies; + } else + ret = -1; + } + + return ret; +} + + +int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype, + u8 *body, size_t bodylen) +{ + struct sk_buff *skb; + struct hostap_ieee80211_mgmt *mgmt; + struct hostap_skb_tx_data *meta; + struct net_device *dev = local->dev; + + skb = dev_alloc_skb(IEEE80211_MGMT_HDR_LEN + bodylen); + if (skb == NULL) + return -ENOMEM; + + mgmt = (struct hostap_ieee80211_mgmt *) + skb_put(skb, IEEE80211_MGMT_HDR_LEN); + memset(mgmt, 0, IEEE80211_MGMT_HDR_LEN); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); + memcpy(mgmt->da, dst, ETH_ALEN); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + memcpy(mgmt->bssid, dst, ETH_ALEN); + if (body) + memcpy(skb_put(skb, bodylen), body, bodylen); + + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + meta->iface = netdev_priv(dev); + + skb->dev = dev; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + dev_queue_xmit(skb); + + return 0; +} + + +int prism2_sta_deauth(local_info_t *local, u16 reason) +{ + union iwreq_data wrqu; + int ret; + __le16 val = cpu_to_le16(reason); + + if (local->iw_mode != IW_MODE_INFRA || + is_zero_ether_addr(local->bssid) || + ether_addr_equal(local->bssid, "\x44\x44\x44\x44\x44\x44")) + return 0; + + ret = prism2_sta_send_mgmt(local, local->bssid, IEEE80211_STYPE_DEAUTH, + (u8 *) &val, 2); + eth_zero_addr(wrqu.ap_addr.sa_data); + wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); + return ret; +} + + +struct proc_dir_entry *hostap_proc; + +static int __init hostap_init(void) +{ + if (init_net.proc_net != NULL) { + hostap_proc = proc_mkdir("hostap", init_net.proc_net); + if (!hostap_proc) + printk(KERN_WARNING "Failed to mkdir " + "/proc/net/hostap\n"); + } else + hostap_proc = NULL; + + return 0; +} + + +static void __exit hostap_exit(void) +{ + if (hostap_proc != NULL) { + hostap_proc = NULL; + remove_proc_entry("hostap", init_net.proc_net); + } +} + + +EXPORT_SYMBOL(hostap_set_word); +EXPORT_SYMBOL(hostap_set_string); +EXPORT_SYMBOL(hostap_get_porttype); +EXPORT_SYMBOL(hostap_set_encryption); +EXPORT_SYMBOL(hostap_set_antsel); +EXPORT_SYMBOL(hostap_set_roaming); +EXPORT_SYMBOL(hostap_set_auth_algs); +EXPORT_SYMBOL(hostap_dump_rx_header); +EXPORT_SYMBOL(hostap_dump_tx_header); +EXPORT_SYMBOL(hostap_80211_get_hdrlen); +EXPORT_SYMBOL(hostap_setup_dev); +EXPORT_SYMBOL(hostap_set_multicast_list_queue); +EXPORT_SYMBOL(hostap_set_hostapd); +EXPORT_SYMBOL(hostap_set_hostapd_sta); +EXPORT_SYMBOL(hostap_add_interface); +EXPORT_SYMBOL(hostap_remove_interface); +EXPORT_SYMBOL(prism2_update_comms_qual); + +module_init(hostap_init); +module_exit(hostap_exit); diff --git a/drivers/net/wireless/intersil/hostap/hostap_pci.c b/drivers/net/wireless/intersil/hostap/hostap_pci.c new file mode 100644 index 000000000000..c864ef4b0015 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_pci.c @@ -0,0 +1,459 @@ +#define PRISM2_PCI + +/* Host AP driver's support for Intersil Prism2.5 PCI cards is based on + * driver patches from Reyk Floeter and + * Andy Warner */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hostap_wlan.h" + + +static char *dev_info = "hostap_pci"; + + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Support for Intersil Prism2.5-based 802.11 wireless LAN " + "PCI cards."); +MODULE_SUPPORTED_DEVICE("Intersil Prism2.5-based WLAN PCI cards"); +MODULE_LICENSE("GPL"); + + +/* struct local_info::hw_priv */ +struct hostap_pci_priv { + void __iomem *mem_start; +}; + + +/* FIX: do we need mb/wmb/rmb with memory operations? */ + + +static const struct pci_device_id prism2_pci_id_table[] = { + /* Intersil Prism3 ISL3872 11Mb/s WLAN Controller */ + { 0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID }, + /* Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller */ + { 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID }, + /* Samsung MagicLAN SWL-2210P */ + { 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID }, + { 0 } +}; + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + struct hostap_interface *iface; + struct hostap_pci_priv *hw_priv; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + hw_priv = local->hw_priv; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); + writeb(v, hw_priv->mem_start + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + struct hostap_pci_priv *hw_priv; + local_info_t *local; + unsigned long flags; + u8 v; + + iface = netdev_priv(dev); + local = iface->local; + hw_priv = local->hw_priv; + + spin_lock_irqsave(&local->lock, flags); + v = readb(hw_priv->mem_start + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) +{ + struct hostap_interface *iface; + struct hostap_pci_priv *hw_priv; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + hw_priv = local->hw_priv; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); + writew(v, hw_priv->mem_start + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + struct hostap_pci_priv *hw_priv; + local_info_t *local; + unsigned long flags; + u16 v; + + iface = netdev_priv(dev); + local = iface->local; + hw_priv = local->hw_priv; + + spin_lock_irqsave(&local->lock, flags); + v = readw(hw_priv->mem_start + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) +#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) +#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) +#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) +#define HFA384X_OUTW_DATA(v,a) hfa384x_outw_debug(dev, (a), le16_to_cpu((v))) +#define HFA384X_INW_DATA(a) cpu_to_le16(hfa384x_inw_debug(dev, (a))) + +#else /* PRISM2_IO_DEBUG */ + +static inline void hfa384x_outb(struct net_device *dev, int a, u8 v) +{ + struct hostap_interface *iface; + struct hostap_pci_priv *hw_priv; + iface = netdev_priv(dev); + hw_priv = iface->local->hw_priv; + writeb(v, hw_priv->mem_start + a); +} + +static inline u8 hfa384x_inb(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + struct hostap_pci_priv *hw_priv; + iface = netdev_priv(dev); + hw_priv = iface->local->hw_priv; + return readb(hw_priv->mem_start + a); +} + +static inline void hfa384x_outw(struct net_device *dev, int a, u16 v) +{ + struct hostap_interface *iface; + struct hostap_pci_priv *hw_priv; + iface = netdev_priv(dev); + hw_priv = iface->local->hw_priv; + writew(v, hw_priv->mem_start + a); +} + +static inline u16 hfa384x_inw(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + struct hostap_pci_priv *hw_priv; + iface = netdev_priv(dev); + hw_priv = iface->local->hw_priv; + return readw(hw_priv->mem_start + a); +} + +#define HFA384X_OUTB(v,a) hfa384x_outb(dev, (a), (v)) +#define HFA384X_INB(a) hfa384x_inb(dev, (a)) +#define HFA384X_OUTW(v,a) hfa384x_outw(dev, (a), (v)) +#define HFA384X_INW(a) hfa384x_inw(dev, (a)) +#define HFA384X_OUTW_DATA(v,a) hfa384x_outw(dev, (a), le16_to_cpu((v))) +#define HFA384X_INW_DATA(a) cpu_to_le16(hfa384x_inw(dev, (a))) + +#endif /* PRISM2_IO_DEBUG */ + + +static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, + int len) +{ + u16 d_off; + __le16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (__le16 *) buf; + + for ( ; len > 1; len -= 2) + *pos++ = HFA384X_INW_DATA(d_off); + + if (len & 1) + *((char *) pos) = HFA384X_INB(d_off); + + return 0; +} + + +static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) +{ + u16 d_off; + __le16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (__le16 *) buf; + + for ( ; len > 1; len -= 2) + HFA384X_OUTW_DATA(*pos++, d_off); + + if (len & 1) + HFA384X_OUTB(*((char *) pos), d_off); + + return 0; +} + + +/* FIX: This might change at some point.. */ +#include "hostap_hw.c" + +static void prism2_pci_cor_sreset(local_info_t *local) +{ + struct net_device *dev = local->dev; + u16 reg; + + reg = HFA384X_INB(HFA384X_PCICOR_OFF); + printk(KERN_DEBUG "%s: Original COR value: 0x%0x\n", dev->name, reg); + + /* linux-wlan-ng uses extremely long hold and settle times for + * COR sreset. A comment in the driver code mentions that the long + * delays appear to be necessary. However, at least IBM 22P6901 seems + * to work fine with shorter delays. + * + * Longer delays can be configured by uncommenting following line: */ +/* #define PRISM2_PCI_USE_LONG_DELAYS */ + +#ifdef PRISM2_PCI_USE_LONG_DELAYS + int i; + + HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); + mdelay(250); + + HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); + mdelay(500); + + /* Wait for f/w to complete initialization (CMD:BUSY == 0) */ + i = 2000000 / 10; + while ((HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) && --i) + udelay(10); + +#else /* PRISM2_PCI_USE_LONG_DELAYS */ + + HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); + mdelay(2); + HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); + mdelay(2); + +#endif /* PRISM2_PCI_USE_LONG_DELAYS */ + + if (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) { + printk(KERN_DEBUG "%s: COR sreset timeout\n", dev->name); + } +} + + +static void prism2_pci_genesis_reset(local_info_t *local, int hcr) +{ + struct net_device *dev = local->dev; + + HFA384X_OUTW(0x00C5, HFA384X_PCICOR_OFF); + mdelay(10); + HFA384X_OUTW(hcr, HFA384X_PCIHCR_OFF); + mdelay(10); + HFA384X_OUTW(0x0045, HFA384X_PCICOR_OFF); + mdelay(10); +} + + +static struct prism2_helper_functions prism2_pci_funcs = +{ + .card_present = NULL, + .cor_sreset = prism2_pci_cor_sreset, + .genesis_reset = prism2_pci_genesis_reset, + .hw_type = HOSTAP_HW_PCI, +}; + + +static int prism2_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned long phymem; + void __iomem *mem = NULL; + local_info_t *local = NULL; + struct net_device *dev = NULL; + static int cards_found /* = 0 */; + int irq_registered = 0; + struct hostap_interface *iface; + struct hostap_pci_priv *hw_priv; + + hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL); + if (hw_priv == NULL) + return -ENOMEM; + + if (pci_enable_device(pdev)) + goto err_out_free; + + phymem = pci_resource_start(pdev, 0); + + if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) { + printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n"); + goto err_out_disable; + } + + mem = pci_ioremap_bar(pdev, 0); + if (mem == NULL) { + printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ; + goto fail; + } + + dev = prism2_init_local_data(&prism2_pci_funcs, cards_found, + &pdev->dev); + if (dev == NULL) + goto fail; + iface = netdev_priv(dev); + local = iface->local; + local->hw_priv = hw_priv; + cards_found++; + + dev->irq = pdev->irq; + hw_priv->mem_start = mem; + dev->base_addr = (unsigned long) mem; + + prism2_pci_cor_sreset(local); + + pci_set_drvdata(pdev, dev); + + if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name, + dev)) { + printk(KERN_WARNING "%s: request_irq failed\n", dev->name); + goto fail; + } else + irq_registered = 1; + + if (!local->pri_only && prism2_hw_config(dev, 1)) { + printk(KERN_DEBUG "%s: hardware initialization failed\n", + dev_info); + goto fail; + } + + printk(KERN_INFO "%s: Intersil Prism2.5 PCI: " + "mem=0x%lx, irq=%d\n", dev->name, phymem, dev->irq); + + return hostap_hw_ready(dev); + + fail: + if (irq_registered && dev) + free_irq(dev->irq, dev); + + if (mem) + iounmap(mem); + + release_mem_region(phymem, pci_resource_len(pdev, 0)); + + err_out_disable: + pci_disable_device(pdev); + prism2_free_local_data(dev); + + err_out_free: + kfree(hw_priv); + + return -ENODEV; +} + + +static void prism2_pci_remove(struct pci_dev *pdev) +{ + struct net_device *dev; + struct hostap_interface *iface; + void __iomem *mem_start; + struct hostap_pci_priv *hw_priv; + + dev = pci_get_drvdata(pdev); + iface = netdev_priv(dev); + hw_priv = iface->local->hw_priv; + + /* Reset the hardware, and ensure interrupts are disabled. */ + prism2_pci_cor_sreset(iface->local); + hfa384x_disable_interrupts(dev); + + if (dev->irq) + free_irq(dev->irq, dev); + + mem_start = hw_priv->mem_start; + prism2_free_local_data(dev); + kfree(hw_priv); + + iounmap(mem_start); + + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + pci_disable_device(pdev); +} + + +#ifdef CONFIG_PM +static int prism2_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + prism2_suspend(dev); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int prism2_pci_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + int err; + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "%s: pci_enable_device failed on resume\n", + dev->name); + return err; + } + pci_restore_state(pdev); + prism2_hw_config(dev, 0); + if (netif_running(dev)) { + netif_device_attach(dev); + netif_start_queue(dev); + } + + return 0; +} +#endif /* CONFIG_PM */ + + +MODULE_DEVICE_TABLE(pci, prism2_pci_id_table); + +static struct pci_driver prism2_pci_driver = { + .name = "hostap_pci", + .id_table = prism2_pci_id_table, + .probe = prism2_pci_probe, + .remove = prism2_pci_remove, +#ifdef CONFIG_PM + .suspend = prism2_pci_suspend, + .resume = prism2_pci_resume, +#endif /* CONFIG_PM */ +}; + +module_pci_driver(prism2_pci_driver); diff --git a/drivers/net/wireless/intersil/hostap/hostap_plx.c b/drivers/net/wireless/intersil/hostap/hostap_plx.c new file mode 100644 index 000000000000..4901a99c6c59 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_plx.c @@ -0,0 +1,618 @@ +#define PRISM2_PLX + +/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is + * based on: + * - Host AP driver patch from james@madingley.org + * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hostap_wlan.h" + + +static char *dev_info = "hostap_plx"; + + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN " + "cards (PLX)."); +MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)"); +MODULE_LICENSE("GPL"); + + +static int ignore_cis; +module_param(ignore_cis, int, 0444); +MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS"); + + +/* struct local_info::hw_priv */ +struct hostap_plx_priv { + void __iomem *attr_mem; + unsigned int cor_offset; +}; + + +#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */ +#define COR_SRESET 0x80 +#define COR_LEVLREQ 0x40 +#define COR_ENABLE_FUNC 0x01 +/* PCI Configuration Registers */ +#define PLX_PCIIPR 0x3d /* PCI Interrupt Pin */ +/* Local Configuration Registers */ +#define PLX_INTCSR 0x4c /* Interrupt Control/Status Register */ +#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */ +#define PLX_CNTRL 0x50 +#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28) + + +#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID } + +static const struct pci_device_id prism2_plx_id_table[] = { + PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"), + PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"), + PLXDEV(0x126c, 0x8030, "Nortel emobility"), + PLXDEV(0x1562, 0x0001, "Symbol LA-4123"), + PLXDEV(0x1385, 0x4100, "Netgear MA301"), + PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"), + PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"), + PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"), + PLXDEV(0x16ab, 0x1100, "Global Sun Tech GL24110P"), + PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"), + PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"), + PLXDEV(0x16ab, 0x1103, "Longshine 8031"), + PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"), + PLXDEV(0xec80, 0xec00, "Belkin F5D6000"), + { 0 } +}; + + +/* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid + * is not listed here, you will need to add it here to get the driver + * initialized. */ +static struct prism2_plx_manfid { + u16 manfid1, manfid2; +} prism2_plx_known_manfids[] = { + { 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */, + { 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */, + { 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */, + { 0x0126, 0x8000 } /* Proxim RangeLAN */, + { 0x0138, 0x0002 } /* Compaq WL100 */, + { 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */, + { 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */, + { 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */, + { 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */, + { 0x028a, 0x0002 } /* D-Link DRC-650 */, + { 0x0250, 0x0002 } /* Samsung SWL2000-N */, + { 0xc250, 0x0002 } /* EMTAC A2424i */, + { 0xd601, 0x0002 } /* Z-Com XI300 */, + { 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */, + { 0, 0} +}; + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); + outb(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + u8 v; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + v = inb(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); + outw(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + u16 v; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + v = inw(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outsw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc); + outsw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline void hfa384x_insw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc); + insw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) +#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) +#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) +#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) +#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc)) +#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc)) + +#else /* PRISM2_IO_DEBUG */ + +#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a)) +#define HFA384X_INB(a) inb(dev->base_addr + (a)) +#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a)) +#define HFA384X_INW(a) inw(dev->base_addr + (a)) +#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc) +#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc) + +#endif /* PRISM2_IO_DEBUG */ + + +static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, + int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_INSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + *((char *) pos) = HFA384X_INB(d_off); + + return 0; +} + + +static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_OUTSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + HFA384X_OUTB(*((char *) pos), d_off); + + return 0; +} + + +/* FIX: This might change at some point.. */ +#include "hostap_hw.c" + + +static void prism2_plx_cor_sreset(local_info_t *local) +{ + unsigned char corsave; + struct hostap_plx_priv *hw_priv = local->hw_priv; + + printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n", + dev_info); + + /* Set sreset bit of COR and clear it after hold time */ + + if (hw_priv->attr_mem == NULL) { + /* TMD7160 - COR at card's first I/O addr */ + corsave = inb(hw_priv->cor_offset); + outb(corsave | COR_SRESET, hw_priv->cor_offset); + mdelay(2); + outb(corsave & ~COR_SRESET, hw_priv->cor_offset); + mdelay(2); + } else { + /* PLX9052 */ + corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset); + writeb(corsave | COR_SRESET, + hw_priv->attr_mem + hw_priv->cor_offset); + mdelay(2); + writeb(corsave & ~COR_SRESET, + hw_priv->attr_mem + hw_priv->cor_offset); + mdelay(2); + } +} + + +static void prism2_plx_genesis_reset(local_info_t *local, int hcr) +{ + unsigned char corsave; + struct hostap_plx_priv *hw_priv = local->hw_priv; + + if (hw_priv->attr_mem == NULL) { + /* TMD7160 - COR at card's first I/O addr */ + corsave = inb(hw_priv->cor_offset); + outb(corsave | COR_SRESET, hw_priv->cor_offset); + mdelay(10); + outb(hcr, hw_priv->cor_offset + 2); + mdelay(10); + outb(corsave & ~COR_SRESET, hw_priv->cor_offset); + mdelay(10); + } else { + /* PLX9052 */ + corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset); + writeb(corsave | COR_SRESET, + hw_priv->attr_mem + hw_priv->cor_offset); + mdelay(10); + writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2); + mdelay(10); + writeb(corsave & ~COR_SRESET, + hw_priv->attr_mem + hw_priv->cor_offset); + mdelay(10); + } +} + + +static struct prism2_helper_functions prism2_plx_funcs = +{ + .card_present = NULL, + .cor_sreset = prism2_plx_cor_sreset, + .genesis_reset = prism2_plx_genesis_reset, + .hw_type = HOSTAP_HW_PLX, +}; + + +static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len, + unsigned int *cor_offset, + unsigned int *cor_index) +{ +#define CISTPL_CONFIG 0x1A +#define CISTPL_MANFID 0x20 +#define CISTPL_END 0xFF +#define CIS_MAX_LEN 256 + u8 *cis; + int i, pos; + unsigned int rmsz, rasz, manfid1, manfid2; + struct prism2_plx_manfid *manfid; + + cis = kmalloc(CIS_MAX_LEN, GFP_KERNEL); + if (cis == NULL) + return -ENOMEM; + + /* read CIS; it is in even offsets in the beginning of attr_mem */ + for (i = 0; i < CIS_MAX_LEN; i++) + cis[i] = readb(attr_mem + 2 * i); + printk(KERN_DEBUG "%s: CIS: %02x %02x %02x %02x %02x %02x ...\n", + dev_info, cis[0], cis[1], cis[2], cis[3], cis[4], cis[5]); + + /* set reasonable defaults for Prism2 cards just in case CIS parsing + * fails */ + *cor_offset = 0x3e0; + *cor_index = 0x01; + manfid1 = manfid2 = 0; + + pos = 0; + while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) { + if (pos + 2 + cis[pos + 1] > CIS_MAX_LEN) + goto cis_error; + + switch (cis[pos]) { + case CISTPL_CONFIG: + if (cis[pos + 1] < 2) + goto cis_error; + rmsz = (cis[pos + 2] & 0x3c) >> 2; + rasz = cis[pos + 2] & 0x03; + if (4 + rasz + rmsz > cis[pos + 1]) + goto cis_error; + *cor_index = cis[pos + 3] & 0x3F; + *cor_offset = 0; + for (i = 0; i <= rasz; i++) + *cor_offset += cis[pos + 4 + i] << (8 * i); + printk(KERN_DEBUG "%s: cor_index=0x%x " + "cor_offset=0x%x\n", dev_info, + *cor_index, *cor_offset); + if (*cor_offset > attr_len) { + printk(KERN_ERR "%s: COR offset not within " + "attr_mem\n", dev_info); + kfree(cis); + return -1; + } + break; + + case CISTPL_MANFID: + if (cis[pos + 1] < 4) + goto cis_error; + manfid1 = cis[pos + 2] + (cis[pos + 3] << 8); + manfid2 = cis[pos + 4] + (cis[pos + 5] << 8); + printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n", + dev_info, manfid1, manfid2); + break; + } + + pos += cis[pos + 1] + 2; + } + + if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END) + goto cis_error; + + for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++) + if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) { + kfree(cis); + return 0; + } + + printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is" + " not supported card\n", dev_info, manfid1, manfid2); + goto fail; + + cis_error: + printk(KERN_WARNING "%s: invalid CIS data\n", dev_info); + + fail: + kfree(cis); + if (ignore_cis) { + printk(KERN_INFO "%s: ignore_cis parameter set - ignoring " + "errors during CIS verification\n", dev_info); + return 0; + } + return -1; +} + + +static int prism2_plx_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned int pccard_ioaddr, plx_ioaddr; + unsigned long pccard_attr_mem; + unsigned int pccard_attr_len; + void __iomem *attr_mem = NULL; + unsigned int cor_offset = 0, cor_index = 0; + u32 reg; + local_info_t *local = NULL; + struct net_device *dev = NULL; + struct hostap_interface *iface; + static int cards_found /* = 0 */; + int irq_registered = 0; + int tmd7160; + struct hostap_plx_priv *hw_priv; + + hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL); + if (hw_priv == NULL) + return -ENOMEM; + + if (pci_enable_device(pdev)) + goto err_out_free; + + /* National Datacomm NCP130 based on TMD7160, not PLX9052. */ + tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131); + + plx_ioaddr = pci_resource_start(pdev, 1); + pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3); + + if (tmd7160) { + /* TMD7160 */ + attr_mem = NULL; /* no access to PC Card attribute memory */ + + printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, " + "irq=%d, pccard_io=0x%x\n", + plx_ioaddr, pdev->irq, pccard_ioaddr); + + cor_offset = plx_ioaddr; + cor_index = 0x04; + + outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr); + mdelay(1); + reg = inb(plx_ioaddr); + if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) { + printk(KERN_ERR "%s: Error setting COR (expected=" + "0x%02x, was=0x%02x)\n", dev_info, + cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg); + goto fail; + } + } else { + /* PLX9052 */ + pccard_attr_mem = pci_resource_start(pdev, 2); + pccard_attr_len = pci_resource_len(pdev, 2); + if (pccard_attr_len < PLX_MIN_ATTR_LEN) + goto fail; + + + attr_mem = ioremap(pccard_attr_mem, pccard_attr_len); + if (attr_mem == NULL) { + printk(KERN_ERR "%s: cannot remap attr_mem\n", + dev_info); + goto fail; + } + + printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: " + "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n", + pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr); + + if (prism2_plx_check_cis(attr_mem, pccard_attr_len, + &cor_offset, &cor_index)) { + printk(KERN_INFO "Unknown PC Card CIS - not a " + "Prism2/2.5 card?\n"); + goto fail; + } + + printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 " + "adapter\n"); + + /* Write COR to enable PC Card */ + writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, + attr_mem + cor_offset); + + /* Enable PCI interrupts if they are not already enabled */ + reg = inl(plx_ioaddr + PLX_INTCSR); + printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg); + if (!(reg & PLX_INTCSR_PCI_INTEN)) { + outl(reg | PLX_INTCSR_PCI_INTEN, + plx_ioaddr + PLX_INTCSR); + if (!(inl(plx_ioaddr + PLX_INTCSR) & + PLX_INTCSR_PCI_INTEN)) { + printk(KERN_WARNING "%s: Could not enable " + "Local Interrupts\n", dev_info); + goto fail; + } + } + + reg = inl(plx_ioaddr + PLX_CNTRL); + printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM " + "present=%d)\n", + reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0); + /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is + * not present; but are there really such cards in use(?) */ + } + + dev = prism2_init_local_data(&prism2_plx_funcs, cards_found, + &pdev->dev); + if (dev == NULL) + goto fail; + iface = netdev_priv(dev); + local = iface->local; + local->hw_priv = hw_priv; + cards_found++; + + dev->irq = pdev->irq; + dev->base_addr = pccard_ioaddr; + hw_priv->attr_mem = attr_mem; + hw_priv->cor_offset = cor_offset; + + pci_set_drvdata(pdev, dev); + + if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, dev->name, + dev)) { + printk(KERN_WARNING "%s: request_irq failed\n", dev->name); + goto fail; + } else + irq_registered = 1; + + if (prism2_hw_config(dev, 1)) { + printk(KERN_DEBUG "%s: hardware initialization failed\n", + dev_info); + goto fail; + } + + return hostap_hw_ready(dev); + + fail: + if (irq_registered && dev) + free_irq(dev->irq, dev); + + if (attr_mem) + iounmap(attr_mem); + + pci_disable_device(pdev); + prism2_free_local_data(dev); + + err_out_free: + kfree(hw_priv); + + return -ENODEV; +} + + +static void prism2_plx_remove(struct pci_dev *pdev) +{ + struct net_device *dev; + struct hostap_interface *iface; + struct hostap_plx_priv *hw_priv; + + dev = pci_get_drvdata(pdev); + iface = netdev_priv(dev); + hw_priv = iface->local->hw_priv; + + /* Reset the hardware, and ensure interrupts are disabled. */ + prism2_plx_cor_sreset(iface->local); + hfa384x_disable_interrupts(dev); + + if (hw_priv->attr_mem) + iounmap(hw_priv->attr_mem); + if (dev->irq) + free_irq(dev->irq, dev); + + prism2_free_local_data(dev); + kfree(hw_priv); + pci_disable_device(pdev); +} + + +MODULE_DEVICE_TABLE(pci, prism2_plx_id_table); + +static struct pci_driver prism2_plx_driver = { + .name = "hostap_plx", + .id_table = prism2_plx_id_table, + .probe = prism2_plx_probe, + .remove = prism2_plx_remove, +}; + +module_pci_driver(prism2_plx_driver); diff --git a/drivers/net/wireless/intersil/hostap/hostap_proc.c b/drivers/net/wireless/intersil/hostap/hostap_proc.c new file mode 100644 index 000000000000..dd84557cf957 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_proc.c @@ -0,0 +1,499 @@ +/* /proc routines for Host AP driver */ + +#include +#include +#include +#include + +#include "hostap_wlan.h" +#include "hostap.h" + +#define PROC_LIMIT (PAGE_SIZE - 80) + + +#ifndef PRISM2_NO_PROCFS_DEBUG +static int prism2_debug_proc_show(struct seq_file *m, void *v) +{ + local_info_t *local = m->private; + int i; + + seq_printf(m, "next_txfid=%d next_alloc=%d\n", + local->next_txfid, local->next_alloc); + for (i = 0; i < PRISM2_TXFID_COUNT; i++) + seq_printf(m, "FID: tx=%04X intransmit=%04X\n", + local->txfid[i], local->intransmitfid[i]); + seq_printf(m, "FW TX rate control: %d\n", local->fw_tx_rate_control); + seq_printf(m, "beacon_int=%d\n", local->beacon_int); + seq_printf(m, "dtim_period=%d\n", local->dtim_period); + seq_printf(m, "wds_max_connections=%d\n", local->wds_max_connections); + seq_printf(m, "dev_enabled=%d\n", local->dev_enabled); + seq_printf(m, "sw_tick_stuck=%d\n", local->sw_tick_stuck); + for (i = 0; i < WEP_KEYS; i++) { + if (local->crypt_info.crypt[i] && + local->crypt_info.crypt[i]->ops) { + seq_printf(m, "crypt[%d]=%s\n", i, + local->crypt_info.crypt[i]->ops->name); + } + } + seq_printf(m, "pri_only=%d\n", local->pri_only); + seq_printf(m, "pci=%d\n", local->func->hw_type == HOSTAP_HW_PCI); + seq_printf(m, "sram_type=%d\n", local->sram_type); + seq_printf(m, "no_pri=%d\n", local->no_pri); + + return 0; +} + +static int prism2_debug_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, prism2_debug_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations prism2_debug_proc_fops = { + .open = prism2_debug_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + +static int prism2_stats_proc_show(struct seq_file *m, void *v) +{ + local_info_t *local = m->private; + struct comm_tallies_sums *sums = &local->comm_tallies; + + seq_printf(m, "TxUnicastFrames=%u\n", sums->tx_unicast_frames); + seq_printf(m, "TxMulticastframes=%u\n", sums->tx_multicast_frames); + seq_printf(m, "TxFragments=%u\n", sums->tx_fragments); + seq_printf(m, "TxUnicastOctets=%u\n", sums->tx_unicast_octets); + seq_printf(m, "TxMulticastOctets=%u\n", sums->tx_multicast_octets); + seq_printf(m, "TxDeferredTransmissions=%u\n", + sums->tx_deferred_transmissions); + seq_printf(m, "TxSingleRetryFrames=%u\n", sums->tx_single_retry_frames); + seq_printf(m, "TxMultipleRetryFrames=%u\n", + sums->tx_multiple_retry_frames); + seq_printf(m, "TxRetryLimitExceeded=%u\n", + sums->tx_retry_limit_exceeded); + seq_printf(m, "TxDiscards=%u\n", sums->tx_discards); + seq_printf(m, "RxUnicastFrames=%u\n", sums->rx_unicast_frames); + seq_printf(m, "RxMulticastFrames=%u\n", sums->rx_multicast_frames); + seq_printf(m, "RxFragments=%u\n", sums->rx_fragments); + seq_printf(m, "RxUnicastOctets=%u\n", sums->rx_unicast_octets); + seq_printf(m, "RxMulticastOctets=%u\n", sums->rx_multicast_octets); + seq_printf(m, "RxFCSErrors=%u\n", sums->rx_fcs_errors); + seq_printf(m, "RxDiscardsNoBuffer=%u\n", sums->rx_discards_no_buffer); + seq_printf(m, "TxDiscardsWrongSA=%u\n", sums->tx_discards_wrong_sa); + seq_printf(m, "RxDiscardsWEPUndecryptable=%u\n", + sums->rx_discards_wep_undecryptable); + seq_printf(m, "RxMessageInMsgFragments=%u\n", + sums->rx_message_in_msg_fragments); + seq_printf(m, "RxMessageInBadMsgFragments=%u\n", + sums->rx_message_in_bad_msg_fragments); + /* FIX: this may grow too long for one page(?) */ + + return 0; +} + +static int prism2_stats_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, prism2_stats_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations prism2_stats_proc_fops = { + .open = prism2_stats_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +static int prism2_wds_proc_show(struct seq_file *m, void *v) +{ + struct list_head *ptr = v; + struct hostap_interface *iface; + + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type == HOSTAP_INTERFACE_WDS) + seq_printf(m, "%s\t%pM\n", + iface->dev->name, iface->u.wds.remote_addr); + return 0; +} + +static void *prism2_wds_proc_start(struct seq_file *m, loff_t *_pos) +{ + local_info_t *local = m->private; + read_lock_bh(&local->iface_lock); + return seq_list_start(&local->hostap_interfaces, *_pos); +} + +static void *prism2_wds_proc_next(struct seq_file *m, void *v, loff_t *_pos) +{ + local_info_t *local = m->private; + return seq_list_next(v, &local->hostap_interfaces, _pos); +} + +static void prism2_wds_proc_stop(struct seq_file *m, void *v) +{ + local_info_t *local = m->private; + read_unlock_bh(&local->iface_lock); +} + +static const struct seq_operations prism2_wds_proc_seqops = { + .start = prism2_wds_proc_start, + .next = prism2_wds_proc_next, + .stop = prism2_wds_proc_stop, + .show = prism2_wds_proc_show, +}; + +static int prism2_wds_proc_open(struct inode *inode, struct file *file) +{ + int ret = seq_open(file, &prism2_wds_proc_seqops); + if (ret == 0) { + struct seq_file *m = file->private_data; + m->private = PDE_DATA(inode); + } + return ret; +} + +static const struct file_operations prism2_wds_proc_fops = { + .open = prism2_wds_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + + +static int prism2_bss_list_proc_show(struct seq_file *m, void *v) +{ + local_info_t *local = m->private; + struct list_head *ptr = v; + struct hostap_bss_info *bss; + + if (ptr == &local->bss_list) { + seq_printf(m, "#BSSID\tlast_update\tcount\tcapab_info\tSSID(txt)\t" + "SSID(hex)\tWPA IE\n"); + return 0; + } + + bss = list_entry(ptr, struct hostap_bss_info, list); + seq_printf(m, "%pM\t%lu\t%u\t0x%x\t", + bss->bssid, bss->last_update, + bss->count, bss->capab_info); + + seq_printf(m, "%*pE", (int)bss->ssid_len, bss->ssid); + + seq_putc(m, '\t'); + seq_printf(m, "%*phN", (int)bss->ssid_len, bss->ssid); + seq_putc(m, '\t'); + seq_printf(m, "%*phN", (int)bss->wpa_ie_len, bss->wpa_ie); + seq_putc(m, '\n'); + return 0; +} + +static void *prism2_bss_list_proc_start(struct seq_file *m, loff_t *_pos) +{ + local_info_t *local = m->private; + spin_lock_bh(&local->lock); + return seq_list_start_head(&local->bss_list, *_pos); +} + +static void *prism2_bss_list_proc_next(struct seq_file *m, void *v, loff_t *_pos) +{ + local_info_t *local = m->private; + return seq_list_next(v, &local->bss_list, _pos); +} + +static void prism2_bss_list_proc_stop(struct seq_file *m, void *v) +{ + local_info_t *local = m->private; + spin_unlock_bh(&local->lock); +} + +static const struct seq_operations prism2_bss_list_proc_seqops = { + .start = prism2_bss_list_proc_start, + .next = prism2_bss_list_proc_next, + .stop = prism2_bss_list_proc_stop, + .show = prism2_bss_list_proc_show, +}; + +static int prism2_bss_list_proc_open(struct inode *inode, struct file *file) +{ + int ret = seq_open(file, &prism2_bss_list_proc_seqops); + if (ret == 0) { + struct seq_file *m = file->private_data; + m->private = PDE_DATA(inode); + } + return ret; +} + +static const struct file_operations prism2_bss_list_proc_fops = { + .open = prism2_bss_list_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + + +static int prism2_crypt_proc_show(struct seq_file *m, void *v) +{ + local_info_t *local = m->private; + int i; + + seq_printf(m, "tx_keyidx=%d\n", local->crypt_info.tx_keyidx); + for (i = 0; i < WEP_KEYS; i++) { + if (local->crypt_info.crypt[i] && + local->crypt_info.crypt[i]->ops && + local->crypt_info.crypt[i]->ops->print_stats) { + local->crypt_info.crypt[i]->ops->print_stats( + m, local->crypt_info.crypt[i]->priv); + } + } + return 0; +} + +static int prism2_crypt_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, prism2_crypt_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations prism2_crypt_proc_fops = { + .open = prism2_crypt_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +static ssize_t prism2_pda_proc_read(struct file *file, char __user *buf, + size_t count, loff_t *_pos) +{ + local_info_t *local = PDE_DATA(file_inode(file)); + size_t off; + + if (local->pda == NULL || *_pos >= PRISM2_PDA_SIZE) + return 0; + + off = *_pos; + if (count > PRISM2_PDA_SIZE - off) + count = PRISM2_PDA_SIZE - off; + if (copy_to_user(buf, local->pda + off, count) != 0) + return -EFAULT; + *_pos += count; + return count; +} + +static const struct file_operations prism2_pda_proc_fops = { + .read = prism2_pda_proc_read, + .llseek = generic_file_llseek, +}; + + +static ssize_t prism2_aux_dump_proc_no_read(struct file *file, char __user *buf, + size_t bufsize, loff_t *_pos) +{ + return 0; +} + +static const struct file_operations prism2_aux_dump_proc_fops = { + .read = prism2_aux_dump_proc_no_read, +}; + + +#ifdef PRISM2_IO_DEBUG +static int prism2_io_debug_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + local_info_t *local = (local_info_t *) data; + int head = local->io_debug_head; + int start_bytes, left, copy, copied; + + if (off + count > PRISM2_IO_DEBUG_SIZE * 4) { + *eof = 1; + if (off >= PRISM2_IO_DEBUG_SIZE * 4) + return 0; + count = PRISM2_IO_DEBUG_SIZE * 4 - off; + } + + copied = 0; + start_bytes = (PRISM2_IO_DEBUG_SIZE - head) * 4; + left = count; + + if (off < start_bytes) { + copy = start_bytes - off; + if (copy > count) + copy = count; + memcpy(page, ((u8 *) &local->io_debug[head]) + off, copy); + left -= copy; + if (left > 0) + memcpy(&page[copy], local->io_debug, left); + } else { + memcpy(page, ((u8 *) local->io_debug) + (off - start_bytes), + left); + } + + *start = page; + + return count; +} +#endif /* PRISM2_IO_DEBUG */ + + +#ifndef PRISM2_NO_STATION_MODES +static int prism2_scan_results_proc_show(struct seq_file *m, void *v) +{ + local_info_t *local = m->private; + unsigned long entry; + int i, len; + struct hfa384x_hostscan_result *scanres; + u8 *p; + + if (v == SEQ_START_TOKEN) { + seq_printf(m, + "CHID ANL SL BcnInt Capab Rate BSSID ATIM SupRates SSID\n"); + return 0; + } + + entry = (unsigned long)v - 2; + scanres = &local->last_scan_results[entry]; + + seq_printf(m, "%d %d %d %d 0x%02x %d %pM %d ", + le16_to_cpu(scanres->chid), + (s16) le16_to_cpu(scanres->anl), + (s16) le16_to_cpu(scanres->sl), + le16_to_cpu(scanres->beacon_interval), + le16_to_cpu(scanres->capability), + le16_to_cpu(scanres->rate), + scanres->bssid, + le16_to_cpu(scanres->atim)); + + p = scanres->sup_rates; + for (i = 0; i < sizeof(scanres->sup_rates); i++) { + if (p[i] == 0) + break; + seq_printf(m, "<%02x>", p[i]); + } + seq_putc(m, ' '); + + p = scanres->ssid; + len = le16_to_cpu(scanres->ssid_len); + if (len > 32) + len = 32; + for (i = 0; i < len; i++) { + unsigned char c = p[i]; + if (c >= 32 && c < 127) + seq_putc(m, c); + else + seq_printf(m, "<%02x>", c); + } + seq_putc(m, '\n'); + return 0; +} + +static void *prism2_scan_results_proc_start(struct seq_file *m, loff_t *_pos) +{ + local_info_t *local = m->private; + spin_lock_bh(&local->lock); + + /* We have a header (pos 0) + N results to show (pos 1...N) */ + if (*_pos > local->last_scan_results_count) + return NULL; + return (void *)(unsigned long)(*_pos + 1); /* 0 would be EOF */ +} + +static void *prism2_scan_results_proc_next(struct seq_file *m, void *v, loff_t *_pos) +{ + local_info_t *local = m->private; + + ++*_pos; + if (*_pos > local->last_scan_results_count) + return NULL; + return (void *)(unsigned long)(*_pos + 1); /* 0 would be EOF */ +} + +static void prism2_scan_results_proc_stop(struct seq_file *m, void *v) +{ + local_info_t *local = m->private; + spin_unlock_bh(&local->lock); +} + +static const struct seq_operations prism2_scan_results_proc_seqops = { + .start = prism2_scan_results_proc_start, + .next = prism2_scan_results_proc_next, + .stop = prism2_scan_results_proc_stop, + .show = prism2_scan_results_proc_show, +}; + +static int prism2_scan_results_proc_open(struct inode *inode, struct file *file) +{ + int ret = seq_open(file, &prism2_scan_results_proc_seqops); + if (ret == 0) { + struct seq_file *m = file->private_data; + m->private = PDE_DATA(inode); + } + return ret; +} + +static const struct file_operations prism2_scan_results_proc_fops = { + .open = prism2_scan_results_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + + +#endif /* PRISM2_NO_STATION_MODES */ + + +void hostap_init_proc(local_info_t *local) +{ + local->proc = NULL; + + if (hostap_proc == NULL) { + printk(KERN_WARNING "%s: hostap proc directory not created\n", + local->dev->name); + return; + } + + local->proc = proc_mkdir(local->ddev->name, hostap_proc); + if (local->proc == NULL) { + printk(KERN_INFO "/proc/net/hostap/%s creation failed\n", + local->ddev->name); + return; + } + +#ifndef PRISM2_NO_PROCFS_DEBUG + proc_create_data("debug", 0, local->proc, + &prism2_debug_proc_fops, local); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + proc_create_data("stats", 0, local->proc, + &prism2_stats_proc_fops, local); + proc_create_data("wds", 0, local->proc, + &prism2_wds_proc_fops, local); + proc_create_data("pda", 0, local->proc, + &prism2_pda_proc_fops, local); + proc_create_data("aux_dump", 0, local->proc, + local->func->read_aux_fops ?: &prism2_aux_dump_proc_fops, + local); + proc_create_data("bss_list", 0, local->proc, + &prism2_bss_list_proc_fops, local); + proc_create_data("crypt", 0, local->proc, + &prism2_crypt_proc_fops, local); +#ifdef PRISM2_IO_DEBUG + proc_create_data("io_debug", 0, local->proc, + &prism2_io_debug_proc_fops, local); +#endif /* PRISM2_IO_DEBUG */ +#ifndef PRISM2_NO_STATION_MODES + proc_create_data("scan_results", 0, local->proc, + &prism2_scan_results_proc_fops, local); +#endif /* PRISM2_NO_STATION_MODES */ +} + + +void hostap_remove_proc(local_info_t *local) +{ + proc_remove(local->proc); +} + + +EXPORT_SYMBOL(hostap_init_proc); +EXPORT_SYMBOL(hostap_remove_proc); diff --git a/drivers/net/wireless/intersil/hostap/hostap_wlan.h b/drivers/net/wireless/intersil/hostap/hostap_wlan.h new file mode 100644 index 000000000000..ca25283e1c92 --- /dev/null +++ b/drivers/net/wireless/intersil/hostap/hostap_wlan.h @@ -0,0 +1,1047 @@ +#ifndef HOSTAP_WLAN_H +#define HOSTAP_WLAN_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_config.h" +#include "hostap_common.h" + +#define MAX_PARM_DEVICES 8 +#define PARM_MIN_MAX "1-" __MODULE_STRING(MAX_PARM_DEVICES) +#define DEF_INTS -1, -1, -1, -1, -1, -1, -1 +#define GET_INT_PARM(var,idx) var[var[idx] < 0 ? 0 : idx] + + +/* Specific skb->protocol value that indicates that the packet already contains + * txdesc header. + * FIX: This might need own value that would be allocated especially for Prism2 + * txdesc; ETH_P_CONTROL is commented as "Card specific control frames". + * However, these skb's should have only minimal path in the kernel side since + * prism2_send_mgmt() sends these with dev_queue_xmit() to prism2_tx(). */ +#define ETH_P_HOSTAP ETH_P_CONTROL + +/* ARPHRD_IEEE80211_PRISM uses a bloated version of Prism2 RX frame header + * (from linux-wlan-ng) */ +struct linux_wlan_ng_val { + u32 did; + u16 status, len; + u32 data; +} __packed; + +struct linux_wlan_ng_prism_hdr { + u32 msgcode, msglen; + char devname[16]; + struct linux_wlan_ng_val hosttime, mactime, channel, rssi, sq, signal, + noise, rate, istx, frmlen; +} __packed; + +struct linux_wlan_ng_cap_hdr { + __be32 version; + __be32 length; + __be64 mactime; + __be64 hosttime; + __be32 phytype; + __be32 channel; + __be32 datarate; + __be32 antenna; + __be32 priority; + __be32 ssi_type; + __be32 ssi_signal; + __be32 ssi_noise; + __be32 preamble; + __be32 encoding; +} __packed; + +struct hostap_radiotap_rx { + struct ieee80211_radiotap_header hdr; + __le64 tsft; + u8 rate; + u8 padding; + __le16 chan_freq; + __le16 chan_flags; + s8 dbm_antsignal; + s8 dbm_antnoise; +} __packed; + +#define LWNG_CAP_DID_BASE (4 | (1 << 6)) /* section 4, group 1 */ +#define LWNG_CAPHDR_VERSION 0x80211001 + +struct hfa384x_rx_frame { + /* HFA384X RX frame descriptor */ + __le16 status; /* HFA384X_RX_STATUS_ flags */ + __le32 time; /* timestamp, 1 microsecond resolution */ + u8 silence; /* 27 .. 154; seems to be 0 */ + u8 signal; /* 27 .. 154 */ + u8 rate; /* 10, 20, 55, or 110 */ + u8 rxflow; + __le32 reserved; + + /* 802.11 */ + __le16 frame_control; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctrl; + u8 addr4[ETH_ALEN]; + __le16 data_len; + + /* 802.3 */ + u8 dst_addr[ETH_ALEN]; + u8 src_addr[ETH_ALEN]; + __be16 len; + + /* followed by frame data; max 2304 bytes */ +} __packed; + + +struct hfa384x_tx_frame { + /* HFA384X TX frame descriptor */ + __le16 status; /* HFA384X_TX_STATUS_ flags */ + __le16 reserved1; + __le16 reserved2; + __le32 sw_support; + u8 retry_count; /* not yet implemented */ + u8 tx_rate; /* Host AP only; 0 = firmware, or 10, 20, 55, 110 */ + __le16 tx_control; /* HFA384X_TX_CTRL_ flags */ + + /* 802.11 */ + __le16 frame_control; /* parts not used */ + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; /* filled by firmware */ + u8 addr3[ETH_ALEN]; + __le16 seq_ctrl; /* filled by firmware */ + u8 addr4[ETH_ALEN]; + __le16 data_len; + + /* 802.3 */ + u8 dst_addr[ETH_ALEN]; + u8 src_addr[ETH_ALEN]; + __be16 len; + + /* followed by frame data; max 2304 bytes */ +} __packed; + + +struct hfa384x_rid_hdr +{ + __le16 len; + __le16 rid; +} __packed; + + +/* Macro for converting signal levels (range 27 .. 154) to wireless ext + * dBm value with some accuracy */ +#define HFA384X_LEVEL_TO_dBm(v) 0x100 + (v) * 100 / 255 - 100 + +#define HFA384X_LEVEL_TO_dBm_sign(v) (v) * 100 / 255 - 100 + +struct hfa384x_scan_request { + __le16 channel_list; + __le16 txrate; /* HFA384X_RATES_* */ +} __packed; + +struct hfa384x_hostscan_request { + __le16 channel_list; + __le16 txrate; + __le16 target_ssid_len; + u8 target_ssid[32]; +} __packed; + +struct hfa384x_join_request { + u8 bssid[ETH_ALEN]; + __le16 channel; +} __packed; + +struct hfa384x_info_frame { + __le16 len; + __le16 type; +} __packed; + +struct hfa384x_comm_tallies { + __le16 tx_unicast_frames; + __le16 tx_multicast_frames; + __le16 tx_fragments; + __le16 tx_unicast_octets; + __le16 tx_multicast_octets; + __le16 tx_deferred_transmissions; + __le16 tx_single_retry_frames; + __le16 tx_multiple_retry_frames; + __le16 tx_retry_limit_exceeded; + __le16 tx_discards; + __le16 rx_unicast_frames; + __le16 rx_multicast_frames; + __le16 rx_fragments; + __le16 rx_unicast_octets; + __le16 rx_multicast_octets; + __le16 rx_fcs_errors; + __le16 rx_discards_no_buffer; + __le16 tx_discards_wrong_sa; + __le16 rx_discards_wep_undecryptable; + __le16 rx_message_in_msg_fragments; + __le16 rx_message_in_bad_msg_fragments; +} __packed; + +struct hfa384x_comm_tallies32 { + __le32 tx_unicast_frames; + __le32 tx_multicast_frames; + __le32 tx_fragments; + __le32 tx_unicast_octets; + __le32 tx_multicast_octets; + __le32 tx_deferred_transmissions; + __le32 tx_single_retry_frames; + __le32 tx_multiple_retry_frames; + __le32 tx_retry_limit_exceeded; + __le32 tx_discards; + __le32 rx_unicast_frames; + __le32 rx_multicast_frames; + __le32 rx_fragments; + __le32 rx_unicast_octets; + __le32 rx_multicast_octets; + __le32 rx_fcs_errors; + __le32 rx_discards_no_buffer; + __le32 tx_discards_wrong_sa; + __le32 rx_discards_wep_undecryptable; + __le32 rx_message_in_msg_fragments; + __le32 rx_message_in_bad_msg_fragments; +} __packed; + +struct hfa384x_scan_result_hdr { + __le16 reserved; + __le16 scan_reason; +#define HFA384X_SCAN_IN_PROGRESS 0 /* no results available yet */ +#define HFA384X_SCAN_HOST_INITIATED 1 +#define HFA384X_SCAN_FIRMWARE_INITIATED 2 +#define HFA384X_SCAN_INQUIRY_FROM_HOST 3 +} __packed; + +#define HFA384X_SCAN_MAX_RESULTS 32 + +struct hfa384x_scan_result { + __le16 chid; + __le16 anl; + __le16 sl; + u8 bssid[ETH_ALEN]; + __le16 beacon_interval; + __le16 capability; + __le16 ssid_len; + u8 ssid[32]; + u8 sup_rates[10]; + __le16 rate; +} __packed; + +struct hfa384x_hostscan_result { + __le16 chid; + __le16 anl; + __le16 sl; + u8 bssid[ETH_ALEN]; + __le16 beacon_interval; + __le16 capability; + __le16 ssid_len; + u8 ssid[32]; + u8 sup_rates[10]; + __le16 rate; + __le16 atim; +} __packed; + +struct comm_tallies_sums { + unsigned int tx_unicast_frames; + unsigned int tx_multicast_frames; + unsigned int tx_fragments; + unsigned int tx_unicast_octets; + unsigned int tx_multicast_octets; + unsigned int tx_deferred_transmissions; + unsigned int tx_single_retry_frames; + unsigned int tx_multiple_retry_frames; + unsigned int tx_retry_limit_exceeded; + unsigned int tx_discards; + unsigned int rx_unicast_frames; + unsigned int rx_multicast_frames; + unsigned int rx_fragments; + unsigned int rx_unicast_octets; + unsigned int rx_multicast_octets; + unsigned int rx_fcs_errors; + unsigned int rx_discards_no_buffer; + unsigned int tx_discards_wrong_sa; + unsigned int rx_discards_wep_undecryptable; + unsigned int rx_message_in_msg_fragments; + unsigned int rx_message_in_bad_msg_fragments; +}; + + +struct hfa384x_regs { + u16 cmd; + u16 evstat; + u16 offset0; + u16 offset1; + u16 swsupport0; +}; + + +#if defined(PRISM2_PCCARD) || defined(PRISM2_PLX) +/* I/O ports for HFA384X Controller access */ +#define HFA384X_CMD_OFF 0x00 +#define HFA384X_PARAM0_OFF 0x02 +#define HFA384X_PARAM1_OFF 0x04 +#define HFA384X_PARAM2_OFF 0x06 +#define HFA384X_STATUS_OFF 0x08 +#define HFA384X_RESP0_OFF 0x0A +#define HFA384X_RESP1_OFF 0x0C +#define HFA384X_RESP2_OFF 0x0E +#define HFA384X_INFOFID_OFF 0x10 +#define HFA384X_CONTROL_OFF 0x14 +#define HFA384X_SELECT0_OFF 0x18 +#define HFA384X_SELECT1_OFF 0x1A +#define HFA384X_OFFSET0_OFF 0x1C +#define HFA384X_OFFSET1_OFF 0x1E +#define HFA384X_RXFID_OFF 0x20 +#define HFA384X_ALLOCFID_OFF 0x22 +#define HFA384X_TXCOMPLFID_OFF 0x24 +#define HFA384X_SWSUPPORT0_OFF 0x28 +#define HFA384X_SWSUPPORT1_OFF 0x2A +#define HFA384X_SWSUPPORT2_OFF 0x2C +#define HFA384X_EVSTAT_OFF 0x30 +#define HFA384X_INTEN_OFF 0x32 +#define HFA384X_EVACK_OFF 0x34 +#define HFA384X_DATA0_OFF 0x36 +#define HFA384X_DATA1_OFF 0x38 +#define HFA384X_AUXPAGE_OFF 0x3A +#define HFA384X_AUXOFFSET_OFF 0x3C +#define HFA384X_AUXDATA_OFF 0x3E +#endif /* PRISM2_PCCARD || PRISM2_PLX */ + +#ifdef PRISM2_PCI +/* Memory addresses for ISL3874 controller access */ +#define HFA384X_CMD_OFF 0x00 +#define HFA384X_PARAM0_OFF 0x04 +#define HFA384X_PARAM1_OFF 0x08 +#define HFA384X_PARAM2_OFF 0x0C +#define HFA384X_STATUS_OFF 0x10 +#define HFA384X_RESP0_OFF 0x14 +#define HFA384X_RESP1_OFF 0x18 +#define HFA384X_RESP2_OFF 0x1C +#define HFA384X_INFOFID_OFF 0x20 +#define HFA384X_CONTROL_OFF 0x28 +#define HFA384X_SELECT0_OFF 0x30 +#define HFA384X_SELECT1_OFF 0x34 +#define HFA384X_OFFSET0_OFF 0x38 +#define HFA384X_OFFSET1_OFF 0x3C +#define HFA384X_RXFID_OFF 0x40 +#define HFA384X_ALLOCFID_OFF 0x44 +#define HFA384X_TXCOMPLFID_OFF 0x48 +#define HFA384X_PCICOR_OFF 0x4C +#define HFA384X_SWSUPPORT0_OFF 0x50 +#define HFA384X_SWSUPPORT1_OFF 0x54 +#define HFA384X_SWSUPPORT2_OFF 0x58 +#define HFA384X_PCIHCR_OFF 0x5C +#define HFA384X_EVSTAT_OFF 0x60 +#define HFA384X_INTEN_OFF 0x64 +#define HFA384X_EVACK_OFF 0x68 +#define HFA384X_DATA0_OFF 0x6C +#define HFA384X_DATA1_OFF 0x70 +#define HFA384X_AUXPAGE_OFF 0x74 +#define HFA384X_AUXOFFSET_OFF 0x78 +#define HFA384X_AUXDATA_OFF 0x7C +#define HFA384X_PCI_M0_ADDRH_OFF 0x80 +#define HFA384X_PCI_M0_ADDRL_OFF 0x84 +#define HFA384X_PCI_M0_LEN_OFF 0x88 +#define HFA384X_PCI_M0_CTL_OFF 0x8C +#define HFA384X_PCI_STATUS_OFF 0x98 +#define HFA384X_PCI_M1_ADDRH_OFF 0xA0 +#define HFA384X_PCI_M1_ADDRL_OFF 0xA4 +#define HFA384X_PCI_M1_LEN_OFF 0xA8 +#define HFA384X_PCI_M1_CTL_OFF 0xAC + +/* PCI bus master control bits (these are undocumented; based on guessing and + * experimenting..) */ +#define HFA384X_PCI_CTL_FROM_BAP (BIT(5) | BIT(1) | BIT(0)) +#define HFA384X_PCI_CTL_TO_BAP (BIT(5) | BIT(0)) + +#endif /* PRISM2_PCI */ + + +/* Command codes for CMD reg. */ +#define HFA384X_CMDCODE_INIT 0x00 +#define HFA384X_CMDCODE_ENABLE 0x01 +#define HFA384X_CMDCODE_DISABLE 0x02 +#define HFA384X_CMDCODE_ALLOC 0x0A +#define HFA384X_CMDCODE_TRANSMIT 0x0B +#define HFA384X_CMDCODE_INQUIRE 0x11 +#define HFA384X_CMDCODE_ACCESS 0x21 +#define HFA384X_CMDCODE_ACCESS_WRITE (0x21 | BIT(8)) +#define HFA384X_CMDCODE_DOWNLOAD 0x22 +#define HFA384X_CMDCODE_READMIF 0x30 +#define HFA384X_CMDCODE_WRITEMIF 0x31 +#define HFA384X_CMDCODE_TEST 0x38 + +#define HFA384X_CMDCODE_MASK 0x3F + +/* Test mode operations */ +#define HFA384X_TEST_CHANGE_CHANNEL 0x08 +#define HFA384X_TEST_MONITOR 0x0B +#define HFA384X_TEST_STOP 0x0F +#define HFA384X_TEST_CFG_BITS 0x15 +#define HFA384X_TEST_CFG_BIT_ALC BIT(3) + +#define HFA384X_CMD_BUSY BIT(15) + +#define HFA384X_CMD_TX_RECLAIM BIT(8) + +#define HFA384X_OFFSET_ERR BIT(14) +#define HFA384X_OFFSET_BUSY BIT(15) + + +/* ProgMode for download command */ +#define HFA384X_PROGMODE_DISABLE 0 +#define HFA384X_PROGMODE_ENABLE_VOLATILE 1 +#define HFA384X_PROGMODE_ENABLE_NON_VOLATILE 2 +#define HFA384X_PROGMODE_PROGRAM_NON_VOLATILE 3 + +#define HFA384X_AUX_MAGIC0 0xfe01 +#define HFA384X_AUX_MAGIC1 0xdc23 +#define HFA384X_AUX_MAGIC2 0xba45 + +#define HFA384X_AUX_PORT_DISABLED 0 +#define HFA384X_AUX_PORT_DISABLE BIT(14) +#define HFA384X_AUX_PORT_ENABLE BIT(15) +#define HFA384X_AUX_PORT_ENABLED (BIT(14) | BIT(15)) +#define HFA384X_AUX_PORT_MASK (BIT(14) | BIT(15)) + +#define PRISM2_PDA_SIZE 1024 + + +/* Events; EvStat, Interrupt mask (IntEn), and acknowledge bits (EvAck) */ +#define HFA384X_EV_TICK BIT(15) +#define HFA384X_EV_WTERR BIT(14) +#define HFA384X_EV_INFDROP BIT(13) +#ifdef PRISM2_PCI +#define HFA384X_EV_PCI_M1 BIT(9) +#define HFA384X_EV_PCI_M0 BIT(8) +#endif /* PRISM2_PCI */ +#define HFA384X_EV_INFO BIT(7) +#define HFA384X_EV_DTIM BIT(5) +#define HFA384X_EV_CMD BIT(4) +#define HFA384X_EV_ALLOC BIT(3) +#define HFA384X_EV_TXEXC BIT(2) +#define HFA384X_EV_TX BIT(1) +#define HFA384X_EV_RX BIT(0) + + +/* HFA384X Information frames */ +#define HFA384X_INFO_HANDOVERADDR 0xF000 /* AP f/w ? */ +#define HFA384X_INFO_HANDOVERDEAUTHADDR 0xF001 /* AP f/w 1.3.7 */ +#define HFA384X_INFO_COMMTALLIES 0xF100 +#define HFA384X_INFO_SCANRESULTS 0xF101 +#define HFA384X_INFO_CHANNELINFORESULTS 0xF102 /* AP f/w only */ +#define HFA384X_INFO_HOSTSCANRESULTS 0xF103 +#define HFA384X_INFO_LINKSTATUS 0xF200 +#define HFA384X_INFO_ASSOCSTATUS 0xF201 /* ? */ +#define HFA384X_INFO_AUTHREQ 0xF202 /* ? */ +#define HFA384X_INFO_PSUSERCNT 0xF203 /* ? */ +#define HFA384X_INFO_KEYIDCHANGED 0xF204 /* ? */ + +enum { HFA384X_LINKSTATUS_CONNECTED = 1, + HFA384X_LINKSTATUS_DISCONNECTED = 2, + HFA384X_LINKSTATUS_AP_CHANGE = 3, + HFA384X_LINKSTATUS_AP_OUT_OF_RANGE = 4, + HFA384X_LINKSTATUS_AP_IN_RANGE = 5, + HFA384X_LINKSTATUS_ASSOC_FAILED = 6 }; + +enum { HFA384X_PORTTYPE_BSS = 1, HFA384X_PORTTYPE_WDS = 2, + HFA384X_PORTTYPE_PSEUDO_IBSS = 3, HFA384X_PORTTYPE_IBSS = 0, + HFA384X_PORTTYPE_HOSTAP = 6 }; + +#define HFA384X_RATES_1MBPS BIT(0) +#define HFA384X_RATES_2MBPS BIT(1) +#define HFA384X_RATES_5MBPS BIT(2) +#define HFA384X_RATES_11MBPS BIT(3) + +#define HFA384X_ROAMING_FIRMWARE 1 +#define HFA384X_ROAMING_HOST 2 +#define HFA384X_ROAMING_DISABLED 3 + +#define HFA384X_WEPFLAGS_PRIVACYINVOKED BIT(0) +#define HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED BIT(1) +#define HFA384X_WEPFLAGS_HOSTENCRYPT BIT(4) +#define HFA384X_WEPFLAGS_HOSTDECRYPT BIT(7) + +#define HFA384X_RX_STATUS_MSGTYPE (BIT(15) | BIT(14) | BIT(13)) +#define HFA384X_RX_STATUS_PCF BIT(12) +#define HFA384X_RX_STATUS_MACPORT (BIT(10) | BIT(9) | BIT(8)) +#define HFA384X_RX_STATUS_UNDECR BIT(1) +#define HFA384X_RX_STATUS_FCSERR BIT(0) + +#define HFA384X_RX_STATUS_GET_MSGTYPE(s) \ +(((s) & HFA384X_RX_STATUS_MSGTYPE) >> 13) +#define HFA384X_RX_STATUS_GET_MACPORT(s) \ +(((s) & HFA384X_RX_STATUS_MACPORT) >> 8) + +enum { HFA384X_RX_MSGTYPE_NORMAL = 0, HFA384X_RX_MSGTYPE_RFC1042 = 1, + HFA384X_RX_MSGTYPE_BRIDGETUNNEL = 2, HFA384X_RX_MSGTYPE_MGMT = 4 }; + + +#define HFA384X_TX_CTRL_ALT_RTRY BIT(5) +#define HFA384X_TX_CTRL_802_11 BIT(3) +#define HFA384X_TX_CTRL_802_3 0 +#define HFA384X_TX_CTRL_TX_EX BIT(2) +#define HFA384X_TX_CTRL_TX_OK BIT(1) + +#define HFA384X_TX_STATUS_RETRYERR BIT(0) +#define HFA384X_TX_STATUS_AGEDERR BIT(1) +#define HFA384X_TX_STATUS_DISCON BIT(2) +#define HFA384X_TX_STATUS_FORMERR BIT(3) + +/* HFA3861/3863 (BBP) Control Registers */ +#define HFA386X_CR_TX_CONFIGURE 0x12 /* CR9 */ +#define HFA386X_CR_RX_CONFIGURE 0x14 /* CR10 */ +#define HFA386X_CR_A_D_TEST_MODES2 0x1A /* CR13 */ +#define HFA386X_CR_MANUAL_TX_POWER 0x3E /* CR31 */ +#define HFA386X_CR_MEASURED_TX_POWER 0x74 /* CR58 */ + + +#ifdef __KERNEL__ + +#define PRISM2_TXFID_COUNT 8 +#define PRISM2_DATA_MAXLEN 2304 +#define PRISM2_TXFID_LEN (PRISM2_DATA_MAXLEN + sizeof(struct hfa384x_tx_frame)) +#define PRISM2_TXFID_EMPTY 0xffff +#define PRISM2_TXFID_RESERVED 0xfffe +#define PRISM2_DUMMY_FID 0xffff +#define MAX_SSID_LEN 32 +#define MAX_NAME_LEN 32 /* this is assumed to be equal to MAX_SSID_LEN */ + +#define PRISM2_DUMP_RX_HDR BIT(0) +#define PRISM2_DUMP_TX_HDR BIT(1) +#define PRISM2_DUMP_TXEXC_HDR BIT(2) + +struct hostap_tx_callback_info { + u16 idx; + void (*func)(struct sk_buff *, int ok, void *); + void *data; + struct hostap_tx_callback_info *next; +}; + + +/* IEEE 802.11 requires that STA supports concurrent reception of at least + * three fragmented frames. This define can be increased to support more + * concurrent frames, but it should be noted that each entry can consume about + * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ +#define PRISM2_FRAG_CACHE_LEN 4 + +struct prism2_frag_entry { + unsigned long first_frag_time; + unsigned int seq; + unsigned int last_frag; + struct sk_buff *skb; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; +}; + + +struct hostap_cmd_queue { + struct list_head list; + wait_queue_head_t compl; + volatile enum { CMD_SLEEP, CMD_CALLBACK, CMD_COMPLETED } type; + void (*callback)(struct net_device *dev, long context, u16 resp0, + u16 res); + long context; + u16 cmd, param0, param1; + u16 resp0, res; + volatile int issued, issuing; + + atomic_t usecnt; + int del_req; +}; + +/* options for hw_shutdown */ +#define HOSTAP_HW_NO_DISABLE BIT(0) +#define HOSTAP_HW_ENABLE_CMDCOMPL BIT(1) + +typedef struct local_info local_info_t; + +struct prism2_helper_functions { + /* these functions are defined in hardware model specific files + * (hostap_{cs,plx,pci}.c */ + int (*card_present)(local_info_t *local); + void (*cor_sreset)(local_info_t *local); + void (*genesis_reset)(local_info_t *local, int hcr); + + /* the following functions are from hostap_hw.c, but they may have some + * hardware model specific code */ + + /* FIX: low-level commands like cmd might disappear at some point to + * make it easier to change them if needed (e.g., cmd would be replaced + * with write_mif/read_mif/testcmd/inquire); at least get_rid and + * set_rid might move to hostap_{cs,plx,pci}.c */ + int (*cmd)(struct net_device *dev, u16 cmd, u16 param0, u16 *param1, + u16 *resp0); + void (*read_regs)(struct net_device *dev, struct hfa384x_regs *regs); + int (*get_rid)(struct net_device *dev, u16 rid, void *buf, int len, + int exact_len); + int (*set_rid)(struct net_device *dev, u16 rid, void *buf, int len); + int (*hw_enable)(struct net_device *dev, int initial); + int (*hw_config)(struct net_device *dev, int initial); + void (*hw_reset)(struct net_device *dev); + void (*hw_shutdown)(struct net_device *dev, int no_disable); + int (*reset_port)(struct net_device *dev); + void (*schedule_reset)(local_info_t *local); + int (*download)(local_info_t *local, + struct prism2_download_param *param); + int (*tx)(struct sk_buff *skb, struct net_device *dev); + int (*set_tim)(struct net_device *dev, int aid, int set); + const struct file_operations *read_aux_fops; + + int need_tx_headroom; /* number of bytes of headroom needed before + * IEEE 802.11 header */ + enum { HOSTAP_HW_PCCARD, HOSTAP_HW_PLX, HOSTAP_HW_PCI } hw_type; +}; + + +struct prism2_download_data { + u32 dl_cmd; + u32 start_addr; + u32 num_areas; + struct prism2_download_data_area { + u32 addr; /* wlan card address */ + u32 len; + u8 *data; /* allocated data */ + } data[0]; +}; + + +#define HOSTAP_MAX_BSS_COUNT 64 +#define MAX_WPA_IE_LEN 64 + +struct hostap_bss_info { + struct list_head list; + unsigned long last_update; + unsigned int count; + u8 bssid[ETH_ALEN]; + u16 capab_info; + u8 ssid[32]; + size_t ssid_len; + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + int chan; + int included; +}; + + +/* Per radio private Host AP data - shared by all net devices interfaces used + * by each radio (wlan#, wlan#ap, wlan#sta, WDS). + * ((struct hostap_interface *) netdev_priv(dev))->local points to this + * structure. */ +struct local_info { + struct module *hw_module; + int card_idx; + int dev_enabled; + int master_dev_auto_open; /* was master device opened automatically */ + int num_dev_open; /* number of open devices */ + struct net_device *dev; /* master radio device */ + struct net_device *ddev; /* main data device */ + struct list_head hostap_interfaces; /* Host AP interface list (contains + * struct hostap_interface entries) + */ + rwlock_t iface_lock; /* hostap_interfaces read lock; use write lock + * when removing entries from the list. + * TX and RX paths can use read lock. */ + spinlock_t cmdlock, baplock, lock, irq_init_lock; + struct mutex rid_bap_mtx; + u16 infofid; /* MAC buffer id for info frame */ + /* txfid, intransmitfid, next_txtid, and next_alloc are protected by + * txfidlock */ + spinlock_t txfidlock; + int txfid_len; /* length of allocated TX buffers */ + u16 txfid[PRISM2_TXFID_COUNT]; /* buffer IDs for TX frames */ + /* buffer IDs for intransmit frames or PRISM2_TXFID_EMPTY if + * corresponding txfid is free for next TX frame */ + u16 intransmitfid[PRISM2_TXFID_COUNT]; + int next_txfid; /* index to the next txfid to be checked for + * availability */ + int next_alloc; /* index to the next intransmitfid to be checked for + * allocation events */ + + /* bitfield for atomic bitops */ +#define HOSTAP_BITS_TRANSMIT 0 +#define HOSTAP_BITS_BAP_TASKLET 1 +#define HOSTAP_BITS_BAP_TASKLET2 2 + unsigned long bits; + + struct ap_data *ap; + + char essid[MAX_SSID_LEN + 1]; + char name[MAX_NAME_LEN + 1]; + int name_set; + u16 channel_mask; /* mask of allowed channels */ + u16 scan_channel_mask; /* mask of channels to be scanned */ + struct comm_tallies_sums comm_tallies; + struct proc_dir_entry *proc; + int iw_mode; /* operating mode (IW_MODE_*) */ + int pseudo_adhoc; /* 0: IW_MODE_ADHOC is real 802.11 compliant IBSS + * 1: IW_MODE_ADHOC is "pseudo IBSS" */ + char bssid[ETH_ALEN]; + int channel; + int beacon_int; + int dtim_period; + int mtu; + int frame_dump; /* dump RX/TX frame headers, PRISM2_DUMP_ flags */ + int fw_tx_rate_control; + u16 tx_rate_control; + u16 basic_rates; + int hw_resetting; + int hw_ready; + int hw_reset_tries; /* how many times reset has been tried */ + int hw_downloading; + int shutdown; + int pri_only; + int no_pri; /* no PRI f/w present */ + int sram_type; /* 8 = x8 SRAM, 16 = x16 SRAM, -1 = unknown */ + + enum { + PRISM2_TXPOWER_AUTO = 0, PRISM2_TXPOWER_OFF, + PRISM2_TXPOWER_FIXED, PRISM2_TXPOWER_UNKNOWN + } txpower_type; + int txpower; /* if txpower_type == PRISM2_TXPOWER_FIXED */ + + /* command queue for hfa384x_cmd(); protected with cmdlock */ + struct list_head cmd_queue; + /* max_len for cmd_queue; in addition, cmd_callback can use two + * additional entries to prevent sleeping commands from stopping + * transmits */ +#define HOSTAP_CMD_QUEUE_MAX_LEN 16 + int cmd_queue_len; /* number of entries in cmd_queue */ + + /* if card timeout is detected in interrupt context, reset_queue is + * used to schedule card reseting to be done in user context */ + struct work_struct reset_queue; + + /* For scheduling a change of the promiscuous mode RID */ + int is_promisc; + struct work_struct set_multicast_list_queue; + + struct work_struct set_tim_queue; + struct list_head set_tim_list; + spinlock_t set_tim_lock; + + int wds_max_connections; + int wds_connections; +#define HOSTAP_WDS_BROADCAST_RA BIT(0) +#define HOSTAP_WDS_AP_CLIENT BIT(1) +#define HOSTAP_WDS_STANDARD_FRAME BIT(2) + u32 wds_type; + u16 tx_control; /* flags to be used in TX description */ + int manual_retry_count; /* -1 = use f/w default; otherwise retry count + * to be used with all frames */ + + struct iw_statistics wstats; + unsigned long scan_timestamp; /* Time started to scan */ + enum { + PRISM2_MONITOR_80211 = 0, PRISM2_MONITOR_PRISM = 1, + PRISM2_MONITOR_CAPHDR = 2, PRISM2_MONITOR_RADIOTAP = 3 + } monitor_type; + int monitor_allow_fcserr; + + int hostapd; /* whether user space daemon, hostapd, is used for AP + * management */ + int hostapd_sta; /* whether hostapd is used with an extra STA interface + */ + struct net_device *apdev; + struct net_device_stats apdevstats; + + char assoc_ap_addr[ETH_ALEN]; + struct net_device *stadev; + struct net_device_stats stadevstats; + +#define WEP_KEYS 4 +#define WEP_KEY_LEN 13 + struct lib80211_crypt_info crypt_info; + + int open_wep; /* allow unencrypted frames */ + int host_encrypt; + int host_decrypt; + int privacy_invoked; /* force privacy invoked flag even if no keys are + * configured */ + int fw_encrypt_ok; /* whether firmware-based WEP encrypt is working + * in Host AP mode (STA f/w 1.4.9 or newer) */ + int bcrx_sta_key; /* use individual keys to override default keys even + * with RX of broad/multicast frames */ + + struct prism2_frag_entry frag_cache[PRISM2_FRAG_CACHE_LEN]; + unsigned int frag_next_idx; + + int ieee_802_1x; /* is IEEE 802.1X used */ + + int antsel_tx, antsel_rx; + int rts_threshold; /* dot11RTSThreshold */ + int fragm_threshold; /* dot11FragmentationThreshold */ + int auth_algs; /* PRISM2_AUTH_ flags */ + + int enh_sec; /* cnfEnhSecurity options (broadcast SSID hide/ignore) */ + int tallies32; /* 32-bit tallies in use */ + + struct prism2_helper_functions *func; + + u8 *pda; + int fw_ap; +#define PRISM2_FW_VER(major, minor, variant) \ +(((major) << 16) | ((minor) << 8) | variant) + u32 sta_fw_ver; + + /* Tasklets for handling hardware IRQ related operations outside hw IRQ + * handler */ + struct tasklet_struct bap_tasklet; + + struct tasklet_struct info_tasklet; + struct sk_buff_head info_list; /* info frames as skb's for + * info_tasklet */ + + struct hostap_tx_callback_info *tx_callback; /* registered TX callbacks + */ + + struct tasklet_struct rx_tasklet; + struct sk_buff_head rx_list; + + struct tasklet_struct sta_tx_exc_tasklet; + struct sk_buff_head sta_tx_exc_list; + + int host_roaming; + unsigned long last_join_time; /* time of last JoinRequest */ + struct hfa384x_hostscan_result *last_scan_results; + int last_scan_results_count; + enum { PRISM2_SCAN, PRISM2_HOSTSCAN } last_scan_type; + struct work_struct info_queue; + unsigned long pending_info; /* bit field of pending info_queue items */ +#define PRISM2_INFO_PENDING_LINKSTATUS 0 +#define PRISM2_INFO_PENDING_SCANRESULTS 1 + int prev_link_status; /* previous received LinkStatus info */ + int prev_linkstatus_connected; + u8 preferred_ap[ETH_ALEN]; /* use this AP if possible */ + +#ifdef PRISM2_CALLBACK + void *callback_data; /* Can be used in callbacks; e.g., allocate + * on enable event and free on disable event. + * Host AP driver code does not touch this. */ +#endif /* PRISM2_CALLBACK */ + + wait_queue_head_t hostscan_wq; + + /* Passive scan in Host AP mode */ + struct timer_list passive_scan_timer; + int passive_scan_interval; /* in seconds, 0 = disabled */ + int passive_scan_channel; + enum { PASSIVE_SCAN_WAIT, PASSIVE_SCAN_LISTEN } passive_scan_state; + + struct timer_list tick_timer; + unsigned long last_tick_timer; + unsigned int sw_tick_stuck; + + /* commsQuality / dBmCommsQuality data from periodic polling; only + * valid for Managed and Ad-hoc modes */ + unsigned long last_comms_qual_update; + int comms_qual; /* in some odd unit.. */ + int avg_signal; /* in dB (note: negative) */ + int avg_noise; /* in dB (note: negative) */ + struct work_struct comms_qual_update; + + /* RSSI to dBm adjustment (for RX descriptor fields) */ + int rssi_to_dBm; /* subtract from RSSI to get approximate dBm value */ + + /* BSS list / protected by local->lock */ + struct list_head bss_list; + int num_bss_info; + int wpa; /* WPA support enabled */ + int tkip_countermeasures; + int drop_unencrypted; + /* Generic IEEE 802.11 info element to be added to + * ProbeResp/Beacon/(Re)AssocReq */ + u8 *generic_elem; + size_t generic_elem_len; + +#ifdef PRISM2_DOWNLOAD_SUPPORT + /* Persistent volatile download data */ + struct prism2_download_data *dl_pri; + struct prism2_download_data *dl_sec; +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + +#ifdef PRISM2_IO_DEBUG +#define PRISM2_IO_DEBUG_SIZE 10000 + u32 io_debug[PRISM2_IO_DEBUG_SIZE]; + int io_debug_head; + int io_debug_enabled; +#endif /* PRISM2_IO_DEBUG */ + + /* Pointer to hardware model specific (cs,pci,plx) private data. */ + void *hw_priv; +}; + + +/* Per interface private Host AP data + * Allocated for each net device that Host AP uses (wlan#, wlan#ap, wlan#sta, + * WDS) and netdev_priv(dev) points to this structure. */ +struct hostap_interface { + struct list_head list; /* list entry in Host AP interface list */ + struct net_device *dev; /* pointer to this device */ + struct local_info *local; /* pointer to shared private data */ + struct net_device_stats stats; + struct iw_spy_data spy_data; /* iwspy support */ + struct iw_public_data wireless_data; + + enum { + HOSTAP_INTERFACE_MASTER, + HOSTAP_INTERFACE_MAIN, + HOSTAP_INTERFACE_AP, + HOSTAP_INTERFACE_STA, + HOSTAP_INTERFACE_WDS, + } type; + + union { + struct hostap_interface_wds { + u8 remote_addr[ETH_ALEN]; + } wds; + } u; +}; + + +#define HOSTAP_SKB_TX_DATA_MAGIC 0xf08a36a2 + +/* + * TX meta data - stored in skb->cb buffer, so this must not be increased over + * the 48-byte limit. + * THE PADDING THIS STARTS WITH IS A HORRIBLE HACK THAT SHOULD NOT LIVE + * TO SEE THE DAY. + */ +struct hostap_skb_tx_data { + unsigned int __padding_for_default_qdiscs; + u32 magic; /* HOSTAP_SKB_TX_DATA_MAGIC */ + u8 rate; /* transmit rate */ +#define HOSTAP_TX_FLAGS_WDS BIT(0) +#define HOSTAP_TX_FLAGS_BUFFERED_FRAME BIT(1) +#define HOSTAP_TX_FLAGS_ADD_MOREDATA BIT(2) + u8 flags; /* HOSTAP_TX_FLAGS_* */ + u16 tx_cb_idx; + struct hostap_interface *iface; + unsigned long jiffies; /* queueing timestamp */ + unsigned short ethertype; +}; + + +#ifndef PRISM2_NO_DEBUG + +#define DEBUG_FID BIT(0) +#define DEBUG_PS BIT(1) +#define DEBUG_FLOW BIT(2) +#define DEBUG_AP BIT(3) +#define DEBUG_HW BIT(4) +#define DEBUG_EXTRA BIT(5) +#define DEBUG_EXTRA2 BIT(6) +#define DEBUG_PS2 BIT(7) +#define DEBUG_MASK (DEBUG_PS | DEBUG_AP | DEBUG_HW | DEBUG_EXTRA) +#define PDEBUG(n, args...) \ +do { if ((n) & DEBUG_MASK) printk(KERN_DEBUG args); } while (0) +#define PDEBUG2(n, args...) \ +do { if ((n) & DEBUG_MASK) printk(args); } while (0) + +#else /* PRISM2_NO_DEBUG */ + +#define PDEBUG(n, args...) +#define PDEBUG2(n, args...) + +#endif /* PRISM2_NO_DEBUG */ + +enum { BAP0 = 0, BAP1 = 1 }; + +#define PRISM2_IO_DEBUG_CMD_INB 0 +#define PRISM2_IO_DEBUG_CMD_INW 1 +#define PRISM2_IO_DEBUG_CMD_INSW 2 +#define PRISM2_IO_DEBUG_CMD_OUTB 3 +#define PRISM2_IO_DEBUG_CMD_OUTW 4 +#define PRISM2_IO_DEBUG_CMD_OUTSW 5 +#define PRISM2_IO_DEBUG_CMD_ERROR 6 +#define PRISM2_IO_DEBUG_CMD_INTERRUPT 7 + +#ifdef PRISM2_IO_DEBUG + +#define PRISM2_IO_DEBUG_ENTRY(cmd, reg, value) \ +(((cmd) << 24) | ((reg) << 16) | value) + +static inline void prism2_io_debug_add(struct net_device *dev, int cmd, + int reg, int value) +{ + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + + if (!local->io_debug_enabled) + return; + + local->io_debug[local->io_debug_head] = jiffies & 0xffffffff; + if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE) + local->io_debug_head = 0; + local->io_debug[local->io_debug_head] = + PRISM2_IO_DEBUG_ENTRY(cmd, reg, value); + if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE) + local->io_debug_head = 0; +} + + +static inline void prism2_io_debug_error(struct net_device *dev, int err) +{ + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + unsigned long flags; + + if (!local->io_debug_enabled) + return; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_ERROR, 0, err); + if (local->io_debug_enabled == 1) { + local->io_debug_enabled = 0; + printk(KERN_DEBUG "%s: I/O debug stopped\n", dev->name); + } + spin_unlock_irqrestore(&local->lock, flags); +} + +#else /* PRISM2_IO_DEBUG */ + +static inline void prism2_io_debug_add(struct net_device *dev, int cmd, + int reg, int value) +{ +} + +static inline void prism2_io_debug_error(struct net_device *dev, int err) +{ +} + +#endif /* PRISM2_IO_DEBUG */ + + +#ifdef PRISM2_CALLBACK +enum { + /* Called when card is enabled */ + PRISM2_CALLBACK_ENABLE, + + /* Called when card is disabled */ + PRISM2_CALLBACK_DISABLE, + + /* Called when RX/TX starts/ends */ + PRISM2_CALLBACK_RX_START, PRISM2_CALLBACK_RX_END, + PRISM2_CALLBACK_TX_START, PRISM2_CALLBACK_TX_END +}; +void prism2_callback(local_info_t *local, int event); +#else /* PRISM2_CALLBACK */ +#define prism2_callback(d, e) do { } while (0) +#endif /* PRISM2_CALLBACK */ + +#endif /* __KERNEL__ */ + +#endif /* HOSTAP_WLAN_H */ -- cgit v1.2.3-59-g8ed1b From d3466830c165a298419788b88086ea99974e63ff Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 09:49:59 +0200 Subject: p54: move under intersil vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 1 - drivers/net/wireless/Makefile | 2 - drivers/net/wireless/intersil/Kconfig | 1 + drivers/net/wireless/intersil/Makefile | 1 + drivers/net/wireless/intersil/p54/Kconfig | 71 ++ drivers/net/wireless/intersil/p54/Makefile | 7 + drivers/net/wireless/intersil/p54/eeprom.c | 982 ++++++++++++++++++ drivers/net/wireless/intersil/p54/eeprom.h | 245 +++++ drivers/net/wireless/intersil/p54/fwio.c | 764 ++++++++++++++ drivers/net/wireless/intersil/p54/led.c | 161 +++ drivers/net/wireless/intersil/p54/lmac.h | 562 ++++++++++ drivers/net/wireless/intersil/p54/main.c | 867 ++++++++++++++++ drivers/net/wireless/intersil/p54/p54.h | 281 +++++ drivers/net/wireless/intersil/p54/p54pci.c | 703 +++++++++++++ drivers/net/wireless/intersil/p54/p54pci.h | 112 ++ drivers/net/wireless/intersil/p54/p54spi.c | 720 +++++++++++++ drivers/net/wireless/intersil/p54/p54spi.h | 125 +++ drivers/net/wireless/intersil/p54/p54spi_eeprom.h | 679 ++++++++++++ drivers/net/wireless/intersil/p54/p54usb.c | 1149 +++++++++++++++++++++ drivers/net/wireless/intersil/p54/p54usb.h | 162 +++ drivers/net/wireless/intersil/p54/txrx.c | 940 +++++++++++++++++ drivers/net/wireless/p54/Kconfig | 71 -- drivers/net/wireless/p54/Makefile | 7 - drivers/net/wireless/p54/eeprom.c | 982 ------------------ drivers/net/wireless/p54/eeprom.h | 245 ----- drivers/net/wireless/p54/fwio.c | 764 -------------- drivers/net/wireless/p54/led.c | 161 --- drivers/net/wireless/p54/lmac.h | 562 ---------- drivers/net/wireless/p54/main.c | 867 ---------------- drivers/net/wireless/p54/p54.h | 281 ----- drivers/net/wireless/p54/p54pci.c | 703 ------------- drivers/net/wireless/p54/p54pci.h | 112 -- drivers/net/wireless/p54/p54spi.c | 720 ------------- drivers/net/wireless/p54/p54spi.h | 125 --- drivers/net/wireless/p54/p54spi_eeprom.h | 679 ------------ drivers/net/wireless/p54/p54usb.c | 1149 --------------------- drivers/net/wireless/p54/p54usb.h | 162 --- drivers/net/wireless/p54/txrx.c | 940 ----------------- 39 files changed, 8533 insertions(+), 8534 deletions(-) create mode 100644 drivers/net/wireless/intersil/p54/Kconfig create mode 100644 drivers/net/wireless/intersil/p54/Makefile create mode 100644 drivers/net/wireless/intersil/p54/eeprom.c create mode 100644 drivers/net/wireless/intersil/p54/eeprom.h create mode 100644 drivers/net/wireless/intersil/p54/fwio.c create mode 100644 drivers/net/wireless/intersil/p54/led.c create mode 100644 drivers/net/wireless/intersil/p54/lmac.h create mode 100644 drivers/net/wireless/intersil/p54/main.c create mode 100644 drivers/net/wireless/intersil/p54/p54.h create mode 100644 drivers/net/wireless/intersil/p54/p54pci.c create mode 100644 drivers/net/wireless/intersil/p54/p54pci.h create mode 100644 drivers/net/wireless/intersil/p54/p54spi.c create mode 100644 drivers/net/wireless/intersil/p54/p54spi.h create mode 100644 drivers/net/wireless/intersil/p54/p54spi_eeprom.h create mode 100644 drivers/net/wireless/intersil/p54/p54usb.c create mode 100644 drivers/net/wireless/intersil/p54/p54usb.h create mode 100644 drivers/net/wireless/intersil/p54/txrx.c delete mode 100644 drivers/net/wireless/p54/Kconfig delete mode 100644 drivers/net/wireless/p54/Makefile delete mode 100644 drivers/net/wireless/p54/eeprom.c delete mode 100644 drivers/net/wireless/p54/eeprom.h delete mode 100644 drivers/net/wireless/p54/fwio.c delete mode 100644 drivers/net/wireless/p54/led.c delete mode 100644 drivers/net/wireless/p54/lmac.h delete mode 100644 drivers/net/wireless/p54/main.c delete mode 100644 drivers/net/wireless/p54/p54.h delete mode 100644 drivers/net/wireless/p54/p54pci.c delete mode 100644 drivers/net/wireless/p54/p54pci.h delete mode 100644 drivers/net/wireless/p54/p54spi.c delete mode 100644 drivers/net/wireless/p54/p54spi.h delete mode 100644 drivers/net/wireless/p54/p54spi_eeprom.h delete mode 100644 drivers/net/wireless/p54/p54usb.c delete mode 100644 drivers/net/wireless/p54/p54usb.h delete mode 100644 drivers/net/wireless/p54/txrx.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 63c601b04e68..818899a4c268 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7956,7 +7956,7 @@ M: Christian Lamparter L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/en/users/Drivers/p54 S: Maintained -F: drivers/net/wireless/p54/ +F: drivers/net/wireless/intersil/p54/ PA SEMI ETHERNET DRIVER M: Olof Johansson diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index da10b00d8af1..bea2fbb4a238 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -118,7 +118,6 @@ config MAC80211_HWSIM source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/orinoco/Kconfig" -source "drivers/net/wireless/p54/Kconfig" source "drivers/net/wireless/rt2x00/Kconfig" source "drivers/net/wireless/mediatek/Kconfig" source "drivers/net/wireless/realtek/rtlwifi/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index a60c5e55b55e..046963fc3958 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -28,8 +28,6 @@ obj-$(CONFIG_RT2X00) += rt2x00/ obj-$(CONFIG_WL_MEDIATEK) += mediatek/ -obj-$(CONFIG_P54_COMMON) += p54/ - obj-$(CONFIG_ATH_CARDS) += ath/ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o diff --git a/drivers/net/wireless/intersil/Kconfig b/drivers/net/wireless/intersil/Kconfig index ec80b9117fd2..4bc3688bfa93 100644 --- a/drivers/net/wireless/intersil/Kconfig +++ b/drivers/net/wireless/intersil/Kconfig @@ -12,5 +12,6 @@ config WLAN_VENDOR_INTERSIL if WLAN_VENDOR_INTERSIL source "drivers/net/wireless/intersil/hostap/Kconfig" +source "drivers/net/wireless/intersil/p54/Kconfig" endif # WLAN_VENDOR_INTERSIL diff --git a/drivers/net/wireless/intersil/Makefile b/drivers/net/wireless/intersil/Makefile index 4890ef79f280..90a72830b1ae 100644 --- a/drivers/net/wireless/intersil/Makefile +++ b/drivers/net/wireless/intersil/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_HOSTAP) += hostap/ +obj-$(CONFIG_P54_COMMON) += p54/ diff --git a/drivers/net/wireless/intersil/p54/Kconfig b/drivers/net/wireless/intersil/p54/Kconfig new file mode 100644 index 000000000000..cdafb8c73e82 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/Kconfig @@ -0,0 +1,71 @@ +config P54_COMMON + tristate "Softmac Prism54 support" + depends on MAC80211 + select FW_LOADER + select CRC_CCITT + ---help--- + This is common code for isl38xx/stlc45xx based modules. + This module does nothing by itself - the USB/PCI/SPI front-ends + also need to be enabled in order to support any devices. + + These devices require softmac firmware which can be found at + + + If you choose to build a module, it'll be called p54common. + +config P54_USB + tristate "Prism54 USB support" + depends on P54_COMMON && USB + select CRC32 + ---help--- + This driver is for USB isl38xx based wireless cards. + + These devices require softmac firmware which can be found at + + + If you choose to build a module, it'll be called p54usb. + +config P54_PCI + tristate "Prism54 PCI support" + depends on P54_COMMON && PCI + ---help--- + This driver is for PCI isl38xx based wireless cards. + This driver supports most devices that are supported by the + fullmac prism54 driver plus many devices which are not + supported by the fullmac driver/firmware. + + This driver requires softmac firmware which can be found at + + + If you choose to build a module, it'll be called p54pci. + +config P54_SPI + tristate "Prism54 SPI (stlc45xx) support" + depends on P54_COMMON && SPI_MASTER + ---help--- + This driver is for stlc4550 or stlc4560 based wireless chips + such as Nokia's N800/N810 Portable Internet Tablet. + + If you choose to build a module, it'll be called p54spi. + +config P54_SPI_DEFAULT_EEPROM + bool "Include fallback EEPROM blob" + depends on P54_SPI + default n + ---help--- + Unlike the PCI or USB devices, the SPI variants don't have + a dedicated EEPROM chip to store all device specific values + for calibration, country and interface settings. + + The driver will try to load the image "3826.eeprom", if the + file is put at the right place. (usually /lib/firmware.) + + Only if this request fails, this option will provide a + backup set of generic values to get the device working. + + Enabling this option adds about 4k to p54spi. + +config P54_LEDS + bool + depends on P54_COMMON && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = P54_COMMON) + default y diff --git a/drivers/net/wireless/intersil/p54/Makefile b/drivers/net/wireless/intersil/p54/Makefile new file mode 100644 index 000000000000..b542e68f1781 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/Makefile @@ -0,0 +1,7 @@ +p54common-objs := eeprom.o fwio.o txrx.o main.o +p54common-$(CONFIG_P54_LEDS) += led.o + +obj-$(CONFIG_P54_COMMON) += p54common.o +obj-$(CONFIG_P54_USB) += p54usb.o +obj-$(CONFIG_P54_PCI) += p54pci.o +obj-$(CONFIG_P54_SPI) += p54spi.o diff --git a/drivers/net/wireless/intersil/p54/eeprom.c b/drivers/net/wireless/intersil/p54/eeprom.c new file mode 100644 index 000000000000..2fe713eda7ad --- /dev/null +++ b/drivers/net/wireless/intersil/p54/eeprom.c @@ -0,0 +1,982 @@ +/* + * EEPROM parser code for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2007-2009, Christian Lamparter + * Copyright 2008, Johannes Berg + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "p54.h" +#include "eeprom.h" +#include "lmac.h" + +static struct ieee80211_rate p54_bgrates[] = { + { .bitrate = 10, .hw_value = 0, }, + { .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60, .hw_value = 4, }, + { .bitrate = 90, .hw_value = 5, }, + { .bitrate = 120, .hw_value = 6, }, + { .bitrate = 180, .hw_value = 7, }, + { .bitrate = 240, .hw_value = 8, }, + { .bitrate = 360, .hw_value = 9, }, + { .bitrate = 480, .hw_value = 10, }, + { .bitrate = 540, .hw_value = 11, }, +}; + +static struct ieee80211_rate p54_arates[] = { + { .bitrate = 60, .hw_value = 4, }, + { .bitrate = 90, .hw_value = 5, }, + { .bitrate = 120, .hw_value = 6, }, + { .bitrate = 180, .hw_value = 7, }, + { .bitrate = 240, .hw_value = 8, }, + { .bitrate = 360, .hw_value = 9, }, + { .bitrate = 480, .hw_value = 10, }, + { .bitrate = 540, .hw_value = 11, }, +}; + +static struct p54_rssi_db_entry p54_rssi_default = { + /* + * The defaults are taken from usb-logs of the + * vendor driver. So, they should be safe to + * use in case we can't get a match from the + * rssi <-> dBm conversion database. + */ + .mul = 130, + .add = -398, +}; + +#define CHAN_HAS_CAL BIT(0) +#define CHAN_HAS_LIMIT BIT(1) +#define CHAN_HAS_CURVE BIT(2) +#define CHAN_HAS_ALL (CHAN_HAS_CAL | CHAN_HAS_LIMIT | CHAN_HAS_CURVE) + +struct p54_channel_entry { + u16 freq; + u16 data; + int index; + int max_power; + enum ieee80211_band band; +}; + +struct p54_channel_list { + struct p54_channel_entry *channels; + size_t entries; + size_t max_entries; + size_t band_channel_num[IEEE80211_NUM_BANDS]; +}; + +static int p54_get_band_from_freq(u16 freq) +{ + /* FIXME: sync these values with the 802.11 spec */ + + if ((freq >= 2412) && (freq <= 2484)) + return IEEE80211_BAND_2GHZ; + + if ((freq >= 4920) && (freq <= 5825)) + return IEEE80211_BAND_5GHZ; + + return -1; +} + +static int same_band(u16 freq, u16 freq2) +{ + return p54_get_band_from_freq(freq) == p54_get_band_from_freq(freq2); +} + +static int p54_compare_channels(const void *_a, + const void *_b) +{ + const struct p54_channel_entry *a = _a; + const struct p54_channel_entry *b = _b; + + return a->freq - b->freq; +} + +static int p54_compare_rssichan(const void *_a, + const void *_b) +{ + const struct p54_rssi_db_entry *a = _a; + const struct p54_rssi_db_entry *b = _b; + + return a->freq - b->freq; +} + +static int p54_fill_band_bitrates(struct ieee80211_hw *dev, + struct ieee80211_supported_band *band_entry, + enum ieee80211_band band) +{ + /* TODO: generate rate array dynamically */ + + switch (band) { + case IEEE80211_BAND_2GHZ: + band_entry->bitrates = p54_bgrates; + band_entry->n_bitrates = ARRAY_SIZE(p54_bgrates); + break; + case IEEE80211_BAND_5GHZ: + band_entry->bitrates = p54_arates; + band_entry->n_bitrates = ARRAY_SIZE(p54_arates); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int p54_generate_band(struct ieee80211_hw *dev, + struct p54_channel_list *list, + unsigned int *chan_num, + enum ieee80211_band band) +{ + struct p54_common *priv = dev->priv; + struct ieee80211_supported_band *tmp, *old; + unsigned int i, j; + int ret = -ENOMEM; + + if ((!list->entries) || (!list->band_channel_num[band])) + return -EINVAL; + + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + goto err_out; + + tmp->channels = kzalloc(sizeof(struct ieee80211_channel) * + list->band_channel_num[band], GFP_KERNEL); + if (!tmp->channels) + goto err_out; + + ret = p54_fill_band_bitrates(dev, tmp, band); + if (ret) + goto err_out; + + for (i = 0, j = 0; (j < list->band_channel_num[band]) && + (i < list->entries); i++) { + struct p54_channel_entry *chan = &list->channels[i]; + struct ieee80211_channel *dest = &tmp->channels[j]; + + if (chan->band != band) + continue; + + if (chan->data != CHAN_HAS_ALL) { + wiphy_err(dev->wiphy, "%s%s%s is/are missing for " + "channel:%d [%d MHz].\n", + (chan->data & CHAN_HAS_CAL ? "" : + " [iqauto calibration data]"), + (chan->data & CHAN_HAS_LIMIT ? "" : + " [output power limits]"), + (chan->data & CHAN_HAS_CURVE ? "" : + " [curve data]"), + chan->index, chan->freq); + continue; + } + + dest->band = chan->band; + dest->center_freq = chan->freq; + dest->max_power = chan->max_power; + priv->survey[*chan_num].channel = &tmp->channels[j]; + priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_TX; + dest->hw_value = (*chan_num); + j++; + (*chan_num)++; + } + + if (j == 0) { + wiphy_err(dev->wiphy, "Disabling totally damaged %d GHz band\n", + (band == IEEE80211_BAND_2GHZ) ? 2 : 5); + + ret = -ENODATA; + goto err_out; + } + + tmp->n_channels = j; + old = priv->band_table[band]; + priv->band_table[band] = tmp; + if (old) { + kfree(old->channels); + kfree(old); + } + + return 0; + +err_out: + if (tmp) { + kfree(tmp->channels); + kfree(tmp); + } + + return ret; +} + +static struct p54_channel_entry *p54_update_channel_param(struct p54_channel_list *list, + u16 freq, u16 data) +{ + int i; + struct p54_channel_entry *entry = NULL; + + /* + * usually all lists in the eeprom are mostly sorted. + * so it's very likely that the entry we are looking for + * is right at the end of the list + */ + for (i = list->entries; i >= 0; i--) { + if (freq == list->channels[i].freq) { + entry = &list->channels[i]; + break; + } + } + + if ((i < 0) && (list->entries < list->max_entries)) { + /* entry does not exist yet. Initialize a new one. */ + int band = p54_get_band_from_freq(freq); + + /* + * filter out frequencies which don't belong into + * any supported band. + */ + if (band >= 0) { + i = list->entries++; + list->band_channel_num[band]++; + + entry = &list->channels[i]; + entry->freq = freq; + entry->band = band; + entry->index = ieee80211_frequency_to_channel(freq); + entry->max_power = 0; + entry->data = 0; + } + } + + if (entry) + entry->data |= data; + + return entry; +} + +static int p54_get_maxpower(struct p54_common *priv, void *data) +{ + switch (priv->rxhw & PDR_SYNTH_FRONTEND_MASK) { + case PDR_SYNTH_FRONTEND_LONGBOW: { + struct pda_channel_output_limit_longbow *pda = data; + int j; + u16 rawpower = 0; + pda = data; + for (j = 0; j < ARRAY_SIZE(pda->point); j++) { + struct pda_channel_output_limit_point_longbow *point = + &pda->point[j]; + rawpower = max_t(u16, + rawpower, le16_to_cpu(point->val_qpsk)); + rawpower = max_t(u16, + rawpower, le16_to_cpu(point->val_bpsk)); + rawpower = max_t(u16, + rawpower, le16_to_cpu(point->val_16qam)); + rawpower = max_t(u16, + rawpower, le16_to_cpu(point->val_64qam)); + } + /* longbow seems to use 1/16 dBm units */ + return rawpower / 16; + } + + case PDR_SYNTH_FRONTEND_DUETTE3: + case PDR_SYNTH_FRONTEND_DUETTE2: + case PDR_SYNTH_FRONTEND_FRISBEE: + case PDR_SYNTH_FRONTEND_XBOW: { + struct pda_channel_output_limit *pda = data; + u8 rawpower = 0; + rawpower = max(rawpower, pda->val_qpsk); + rawpower = max(rawpower, pda->val_bpsk); + rawpower = max(rawpower, pda->val_16qam); + rawpower = max(rawpower, pda->val_64qam); + /* raw values are in 1/4 dBm units */ + return rawpower / 4; + } + + default: + return 20; + } +} + +static int p54_generate_channel_lists(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + struct p54_channel_list *list; + unsigned int i, j, k, max_channel_num; + int ret = 0; + u16 freq; + + if ((priv->iq_autocal_len != priv->curve_data->entries) || + (priv->iq_autocal_len != priv->output_limit->entries)) + wiphy_err(dev->wiphy, + "Unsupported or damaged EEPROM detected. " + "You may not be able to use all channels.\n"); + + max_channel_num = max_t(unsigned int, priv->output_limit->entries, + priv->iq_autocal_len); + max_channel_num = max_t(unsigned int, max_channel_num, + priv->curve_data->entries); + + list = kzalloc(sizeof(*list), GFP_KERNEL); + if (!list) { + ret = -ENOMEM; + goto free; + } + priv->chan_num = max_channel_num; + priv->survey = kzalloc(sizeof(struct survey_info) * max_channel_num, + GFP_KERNEL); + if (!priv->survey) { + ret = -ENOMEM; + goto free; + } + + list->max_entries = max_channel_num; + list->channels = kzalloc(sizeof(struct p54_channel_entry) * + max_channel_num, GFP_KERNEL); + if (!list->channels) { + ret = -ENOMEM; + goto free; + } + + for (i = 0; i < max_channel_num; i++) { + if (i < priv->iq_autocal_len) { + freq = le16_to_cpu(priv->iq_autocal[i].freq); + p54_update_channel_param(list, freq, CHAN_HAS_CAL); + } + + if (i < priv->output_limit->entries) { + struct p54_channel_entry *tmp; + + void *data = (void *) ((unsigned long) i * + priv->output_limit->entry_size + + priv->output_limit->offset + + priv->output_limit->data); + + freq = le16_to_cpup((__le16 *) data); + tmp = p54_update_channel_param(list, freq, + CHAN_HAS_LIMIT); + if (tmp) { + tmp->max_power = p54_get_maxpower(priv, data); + } + } + + if (i < priv->curve_data->entries) { + freq = le16_to_cpup((__le16 *) (i * + priv->curve_data->entry_size + + priv->curve_data->offset + + priv->curve_data->data)); + + p54_update_channel_param(list, freq, CHAN_HAS_CURVE); + } + } + + /* sort the channel list by frequency */ + sort(list->channels, list->entries, sizeof(struct p54_channel_entry), + p54_compare_channels, NULL); + + k = 0; + for (i = 0, j = 0; i < IEEE80211_NUM_BANDS; i++) { + if (p54_generate_band(dev, list, &k, i) == 0) + j++; + } + if (j == 0) { + /* no useable band available. */ + ret = -EINVAL; + } + +free: + if (list) { + kfree(list->channels); + kfree(list); + } + if (ret) { + kfree(priv->survey); + priv->survey = NULL; + } + + return ret; +} + +static int p54_convert_rev0(struct ieee80211_hw *dev, + struct pda_pa_curve_data *curve_data) +{ + struct p54_common *priv = dev->priv; + struct p54_pa_curve_data_sample *dst; + struct pda_pa_curve_data_sample_rev0 *src; + size_t cd_len = sizeof(*curve_data) + + (curve_data->points_per_channel*sizeof(*dst) + 2) * + curve_data->channels; + unsigned int i, j; + void *source, *target; + + priv->curve_data = kmalloc(sizeof(*priv->curve_data) + cd_len, + GFP_KERNEL); + if (!priv->curve_data) + return -ENOMEM; + + priv->curve_data->entries = curve_data->channels; + priv->curve_data->entry_size = sizeof(__le16) + + sizeof(*dst) * curve_data->points_per_channel; + priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); + priv->curve_data->len = cd_len; + memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); + source = curve_data->data; + target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; + for (i = 0; i < curve_data->channels; i++) { + __le16 *freq = source; + source += sizeof(__le16); + *((__le16 *)target) = *freq; + target += sizeof(__le16); + for (j = 0; j < curve_data->points_per_channel; j++) { + dst = target; + src = source; + + dst->rf_power = src->rf_power; + dst->pa_detector = src->pa_detector; + dst->data_64qam = src->pcv; + /* "invent" the points for the other modulations */ +#define SUB(x, y) (u8)(((x) - (y)) > (x) ? 0 : (x) - (y)) + dst->data_16qam = SUB(src->pcv, 12); + dst->data_qpsk = SUB(dst->data_16qam, 12); + dst->data_bpsk = SUB(dst->data_qpsk, 12); + dst->data_barker = SUB(dst->data_bpsk, 14); +#undef SUB + target += sizeof(*dst); + source += sizeof(*src); + } + } + + return 0; +} + +static int p54_convert_rev1(struct ieee80211_hw *dev, + struct pda_pa_curve_data *curve_data) +{ + struct p54_common *priv = dev->priv; + struct p54_pa_curve_data_sample *dst; + struct pda_pa_curve_data_sample_rev1 *src; + size_t cd_len = sizeof(*curve_data) + + (curve_data->points_per_channel*sizeof(*dst) + 2) * + curve_data->channels; + unsigned int i, j; + void *source, *target; + + priv->curve_data = kzalloc(cd_len + sizeof(*priv->curve_data), + GFP_KERNEL); + if (!priv->curve_data) + return -ENOMEM; + + priv->curve_data->entries = curve_data->channels; + priv->curve_data->entry_size = sizeof(__le16) + + sizeof(*dst) * curve_data->points_per_channel; + priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); + priv->curve_data->len = cd_len; + memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); + source = curve_data->data; + target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; + for (i = 0; i < curve_data->channels; i++) { + __le16 *freq = source; + source += sizeof(__le16); + *((__le16 *)target) = *freq; + target += sizeof(__le16); + for (j = 0; j < curve_data->points_per_channel; j++) { + memcpy(target, source, sizeof(*src)); + + target += sizeof(*dst); + source += sizeof(*src); + } + source++; + } + + return 0; +} + +static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", + "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; + +static int p54_parse_rssical(struct ieee80211_hw *dev, + u8 *data, int len, u16 type) +{ + struct p54_common *priv = dev->priv; + struct p54_rssi_db_entry *entry; + size_t db_len, entries; + int offset = 0, i; + + if (type != PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { + entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; + if (len != sizeof(struct pda_rssi_cal_entry) * entries) { + wiphy_err(dev->wiphy, "rssical size mismatch.\n"); + goto err_data; + } + } else { + /* + * Some devices (Dell 1450 USB, Xbow 5GHz card, etc...) + * have an empty two byte header. + */ + if (*((__le16 *)&data[offset]) == cpu_to_le16(0)) + offset += 2; + + entries = (len - offset) / + sizeof(struct pda_rssi_cal_ext_entry); + + if (len < offset || + (len - offset) % sizeof(struct pda_rssi_cal_ext_entry) || + entries == 0) { + wiphy_err(dev->wiphy, "invalid rssi database.\n"); + goto err_data; + } + } + + db_len = sizeof(*entry) * entries; + priv->rssi_db = kzalloc(db_len + sizeof(*priv->rssi_db), GFP_KERNEL); + if (!priv->rssi_db) + return -ENOMEM; + + priv->rssi_db->offset = 0; + priv->rssi_db->entries = entries; + priv->rssi_db->entry_size = sizeof(*entry); + priv->rssi_db->len = db_len; + + entry = (void *)((unsigned long)priv->rssi_db->data + priv->rssi_db->offset); + if (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { + struct pda_rssi_cal_ext_entry *cal = (void *) &data[offset]; + + for (i = 0; i < entries; i++) { + entry[i].freq = le16_to_cpu(cal[i].freq); + entry[i].mul = (s16) le16_to_cpu(cal[i].mul); + entry[i].add = (s16) le16_to_cpu(cal[i].add); + } + } else { + struct pda_rssi_cal_entry *cal = (void *) &data[offset]; + + for (i = 0; i < entries; i++) { + u16 freq = 0; + switch (i) { + case IEEE80211_BAND_2GHZ: + freq = 2437; + break; + case IEEE80211_BAND_5GHZ: + freq = 5240; + break; + } + + entry[i].freq = freq; + entry[i].mul = (s16) le16_to_cpu(cal[i].mul); + entry[i].add = (s16) le16_to_cpu(cal[i].add); + } + } + + /* sort the list by channel frequency */ + sort(entry, entries, sizeof(*entry), p54_compare_rssichan, NULL); + return 0; + +err_data: + wiphy_err(dev->wiphy, + "rssi calibration data packing type:(%x) len:%d.\n", + type, len); + + print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, data, len); + + wiphy_err(dev->wiphy, "please report this issue.\n"); + return -EINVAL; +} + +struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *priv, const u16 freq) +{ + struct p54_rssi_db_entry *entry; + int i, found = -1; + + if (!priv->rssi_db) + return &p54_rssi_default; + + entry = (void *)(priv->rssi_db->data + priv->rssi_db->offset); + for (i = 0; i < priv->rssi_db->entries; i++) { + if (!same_band(freq, entry[i].freq)) + continue; + + if (found == -1) { + found = i; + continue; + } + + /* nearest match */ + if (abs(freq - entry[i].freq) < + abs(freq - entry[found].freq)) { + found = i; + continue; + } else { + break; + } + } + + return found < 0 ? &p54_rssi_default : &entry[found]; +} + +static void p54_parse_default_country(struct ieee80211_hw *dev, + void *data, int len) +{ + struct pda_country *country; + + if (len != sizeof(*country)) { + wiphy_err(dev->wiphy, + "found possible invalid default country eeprom entry. (entry size: %d)\n", + len); + + print_hex_dump_bytes("country:", DUMP_PREFIX_NONE, + data, len); + + wiphy_err(dev->wiphy, "please report this issue.\n"); + return; + } + + country = (struct pda_country *) data; + if (country->flags == PDR_COUNTRY_CERT_CODE_PSEUDO) + regulatory_hint(dev->wiphy, country->alpha2); + else { + /* TODO: + * write a shared/common function that converts + * "Regulatory domain codes" (802.11-2007 14.8.2.2) + * into ISO/IEC 3166-1 alpha2 for regulatory_hint. + */ + } +} + +static int p54_convert_output_limits(struct ieee80211_hw *dev, + u8 *data, size_t len) +{ + struct p54_common *priv = dev->priv; + + if (len < 2) + return -EINVAL; + + if (data[0] != 0) { + wiphy_err(dev->wiphy, "unknown output power db revision:%x\n", + data[0]); + return -EINVAL; + } + + if (2 + data[1] * sizeof(struct pda_channel_output_limit) > len) + return -EINVAL; + + priv->output_limit = kmalloc(data[1] * + sizeof(struct pda_channel_output_limit) + + sizeof(*priv->output_limit), GFP_KERNEL); + + if (!priv->output_limit) + return -ENOMEM; + + priv->output_limit->offset = 0; + priv->output_limit->entries = data[1]; + priv->output_limit->entry_size = + sizeof(struct pda_channel_output_limit); + priv->output_limit->len = priv->output_limit->entry_size * + priv->output_limit->entries + + priv->output_limit->offset; + + memcpy(priv->output_limit->data, &data[2], + data[1] * sizeof(struct pda_channel_output_limit)); + + return 0; +} + +static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src, + size_t total_len) +{ + struct p54_cal_database *dst; + size_t payload_len, entries, entry_size, offset; + + payload_len = le16_to_cpu(src->len); + entries = le16_to_cpu(src->entries); + entry_size = le16_to_cpu(src->entry_size); + offset = le16_to_cpu(src->offset); + if (((entries * entry_size + offset) != payload_len) || + (payload_len + sizeof(*src) != total_len)) + return NULL; + + dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL); + if (!dst) + return NULL; + + dst->entries = entries; + dst->entry_size = entry_size; + dst->offset = offset; + dst->len = payload_len; + + memcpy(dst->data, src->data, payload_len); + return dst; +} + +int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) +{ + struct p54_common *priv = dev->priv; + struct eeprom_pda_wrap *wrap; + struct pda_entry *entry; + unsigned int data_len, entry_len; + void *tmp; + int err; + u8 *end = (u8 *)eeprom + len; + u16 synth = 0; + u16 crc16 = ~0; + + wrap = (struct eeprom_pda_wrap *) eeprom; + entry = (void *)wrap->data + le16_to_cpu(wrap->len); + + /* verify that at least the entry length/code fits */ + while ((u8 *)entry <= end - sizeof(*entry)) { + entry_len = le16_to_cpu(entry->len); + data_len = ((entry_len - 1) << 1); + + /* abort if entry exceeds whole structure */ + if ((u8 *)entry + sizeof(*entry) + data_len > end) + break; + + switch (le16_to_cpu(entry->code)) { + case PDR_MAC_ADDRESS: + if (data_len != ETH_ALEN) + break; + SET_IEEE80211_PERM_ADDR(dev, entry->data); + break; + case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS: + if (priv->output_limit) + break; + err = p54_convert_output_limits(dev, entry->data, + data_len); + if (err) + goto err; + break; + case PDR_PRISM_PA_CAL_CURVE_DATA: { + struct pda_pa_curve_data *curve_data = + (struct pda_pa_curve_data *)entry->data; + if (data_len < sizeof(*curve_data)) { + err = -EINVAL; + goto err; + } + + switch (curve_data->cal_method_rev) { + case 0: + err = p54_convert_rev0(dev, curve_data); + break; + case 1: + err = p54_convert_rev1(dev, curve_data); + break; + default: + wiphy_err(dev->wiphy, + "unknown curve data revision %d\n", + curve_data->cal_method_rev); + err = -ENODEV; + break; + } + if (err) + goto err; + } + break; + case PDR_PRISM_ZIF_TX_IQ_CALIBRATION: + priv->iq_autocal = kmemdup(entry->data, data_len, + GFP_KERNEL); + if (!priv->iq_autocal) { + err = -ENOMEM; + goto err; + } + + priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry); + break; + case PDR_DEFAULT_COUNTRY: + p54_parse_default_country(dev, entry->data, data_len); + break; + case PDR_INTERFACE_LIST: + tmp = entry->data; + while ((u8 *)tmp < entry->data + data_len) { + struct exp_if *exp_if = tmp; + if (exp_if->if_id == cpu_to_le16(IF_ID_ISL39000)) + synth = le16_to_cpu(exp_if->variant); + tmp += sizeof(*exp_if); + } + break; + case PDR_HARDWARE_PLATFORM_COMPONENT_ID: + if (data_len < 2) + break; + priv->version = *(u8 *)(entry->data + 1); + break; + case PDR_RSSI_LINEAR_APPROXIMATION: + case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: + case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: + err = p54_parse_rssical(dev, entry->data, data_len, + le16_to_cpu(entry->code)); + if (err) + goto err; + break; + case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2: { + struct pda_custom_wrapper *pda = (void *) entry->data; + __le16 *src; + u16 *dst; + int i; + + if (priv->rssi_db || data_len < sizeof(*pda)) + break; + + priv->rssi_db = p54_convert_db(pda, data_len); + if (!priv->rssi_db) + break; + + src = (void *) priv->rssi_db->data; + dst = (void *) priv->rssi_db->data; + + for (i = 0; i < priv->rssi_db->entries; i++) + *(dst++) = (s16) le16_to_cpu(*(src++)); + + } + break; + case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { + struct pda_custom_wrapper *pda = (void *) entry->data; + if (priv->output_limit || data_len < sizeof(*pda)) + break; + priv->output_limit = p54_convert_db(pda, data_len); + } + break; + case PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM: { + struct pda_custom_wrapper *pda = (void *) entry->data; + if (priv->curve_data || data_len < sizeof(*pda)) + break; + priv->curve_data = p54_convert_db(pda, data_len); + } + break; + case PDR_END: + crc16 = ~crc_ccitt(crc16, (u8 *) entry, sizeof(*entry)); + if (crc16 != le16_to_cpup((__le16 *)entry->data)) { + wiphy_err(dev->wiphy, "eeprom failed checksum " + "test!\n"); + err = -ENOMSG; + goto err; + } else { + goto good_eeprom; + } + break; + default: + break; + } + + crc16 = crc_ccitt(crc16, (u8 *)entry, (entry_len + 1) * 2); + entry = (void *)entry + (entry_len + 1) * 2; + } + + wiphy_err(dev->wiphy, "unexpected end of eeprom data.\n"); + err = -ENODATA; + goto err; + +good_eeprom: + if (!synth || !priv->iq_autocal || !priv->output_limit || + !priv->curve_data) { + wiphy_err(dev->wiphy, + "not all required entries found in eeprom!\n"); + err = -EINVAL; + goto err; + } + + priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK; + + err = p54_generate_channel_lists(dev); + if (err) + goto err; + + if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW) + p54_init_xbow_synth(priv); + if (!(synth & PDR_SYNTH_24_GHZ_DISABLED)) + dev->wiphy->bands[IEEE80211_BAND_2GHZ] = + priv->band_table[IEEE80211_BAND_2GHZ]; + if (!(synth & PDR_SYNTH_5_GHZ_DISABLED)) + dev->wiphy->bands[IEEE80211_BAND_5GHZ] = + priv->band_table[IEEE80211_BAND_5GHZ]; + if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED) + priv->rx_diversity_mask = 3; + if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED) + priv->tx_diversity_mask = 3; + + if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { + u8 perm_addr[ETH_ALEN]; + + wiphy_warn(dev->wiphy, + "Invalid hwaddr! Using randomly generated MAC addr\n"); + eth_random_addr(perm_addr); + SET_IEEE80211_PERM_ADDR(dev, perm_addr); + } + + priv->cur_rssi = &p54_rssi_default; + + wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n", + dev->wiphy->perm_addr, priv->version, + p54_rf_chips[priv->rxhw]); + + return 0; + +err: + kfree(priv->iq_autocal); + kfree(priv->output_limit); + kfree(priv->curve_data); + kfree(priv->rssi_db); + kfree(priv->survey); + priv->iq_autocal = NULL; + priv->output_limit = NULL; + priv->curve_data = NULL; + priv->rssi_db = NULL; + priv->survey = NULL; + + wiphy_err(dev->wiphy, "eeprom parse failed!\n"); + return err; +} +EXPORT_SYMBOL_GPL(p54_parse_eeprom); + +int p54_read_eeprom(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + size_t eeprom_size = 0x2020, offset = 0, blocksize, maxblocksize; + int ret = -ENOMEM; + void *eeprom; + + maxblocksize = EEPROM_READBACK_LEN; + if (priv->fw_var >= 0x509) + maxblocksize -= 0xc; + else + maxblocksize -= 0x4; + + eeprom = kzalloc(eeprom_size, GFP_KERNEL); + if (unlikely(!eeprom)) + goto free; + + while (eeprom_size) { + blocksize = min(eeprom_size, maxblocksize); + ret = p54_download_eeprom(priv, eeprom + offset, + offset, blocksize); + if (unlikely(ret)) + goto free; + + offset += blocksize; + eeprom_size -= blocksize; + } + + ret = p54_parse_eeprom(dev, eeprom, offset); +free: + kfree(eeprom); + return ret; +} +EXPORT_SYMBOL_GPL(p54_read_eeprom); diff --git a/drivers/net/wireless/intersil/p54/eeprom.h b/drivers/net/wireless/intersil/p54/eeprom.h new file mode 100644 index 000000000000..20ebe39a3f4e --- /dev/null +++ b/drivers/net/wireless/intersil/p54/eeprom.h @@ -0,0 +1,245 @@ +/* + * eeprom specific definitions for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2007-2009, Christian Lamparter + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * - LMAC API interface header file for STLC4560 (lmac_longbow.h) + * Copyright (C) 2007 Conexant Systems, Inc. + * + * - islmvc driver + * Copyright (C) 2001 Intersil Americas Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef EEPROM_H +#define EEPROM_H + +/* PDA defines are Copyright (C) 2005 Nokia Corporation (taken from islsm_pda.h) */ + +struct pda_entry { + __le16 len; /* includes both code and data */ + __le16 code; + u8 data[0]; +} __packed; + +struct eeprom_pda_wrap { + __le32 magic; + __le16 pad; + __le16 len; + __le32 arm_opcode; + u8 data[0]; +} __packed; + +struct p54_iq_autocal_entry { + __le16 iq_param[4]; +} __packed; + +struct pda_iq_autocal_entry { + __le16 freq; + struct p54_iq_autocal_entry params; +} __packed; + +struct pda_channel_output_limit { + __le16 freq; + u8 val_bpsk; + u8 val_qpsk; + u8 val_16qam; + u8 val_64qam; + u8 rate_set_mask; + u8 rate_set_size; +} __packed; + +struct pda_channel_output_limit_point_longbow { + __le16 val_bpsk; + __le16 val_qpsk; + __le16 val_16qam; + __le16 val_64qam; +} __packed; + +struct pda_channel_output_limit_longbow { + __le16 freq; + struct pda_channel_output_limit_point_longbow point[3]; +} __packed; + +struct pda_pa_curve_data_sample_rev0 { + u8 rf_power; + u8 pa_detector; + u8 pcv; +} __packed; + +struct pda_pa_curve_data_sample_rev1 { + u8 rf_power; + u8 pa_detector; + u8 data_barker; + u8 data_bpsk; + u8 data_qpsk; + u8 data_16qam; + u8 data_64qam; +} __packed; + +struct pda_pa_curve_data { + u8 cal_method_rev; + u8 channels; + u8 points_per_channel; + u8 padding; + u8 data[0]; +} __packed; + +struct pda_rssi_cal_ext_entry { + __le16 freq; + __le16 mul; + __le16 add; +} __packed; + +struct pda_rssi_cal_entry { + __le16 mul; + __le16 add; +} __packed; + +struct pda_country { + u8 regdomain; + u8 alpha2[2]; + u8 flags; +} __packed; + +struct pda_antenna_gain { + struct { + u8 gain_5GHz; /* 0.25 dBi units */ + u8 gain_2GHz; /* 0.25 dBi units */ + } __packed antenna[0]; +} __packed; + +struct pda_custom_wrapper { + __le16 entries; + __le16 entry_size; + __le16 offset; + __le16 len; + u8 data[0]; +} __packed; + +/* + * this defines the PDR codes used to build PDAs as defined in document + * number 553155. The current implementation mirrors version 1.1 of the + * document and lists only PDRs supported by the ARM platform. + */ + +/* common and choice range (0x0000 - 0x0fff) */ +#define PDR_END 0x0000 +#define PDR_MANUFACTURING_PART_NUMBER 0x0001 +#define PDR_PDA_VERSION 0x0002 +#define PDR_NIC_SERIAL_NUMBER 0x0003 +#define PDR_NIC_RAM_SIZE 0x0005 +#define PDR_RFMODEM_SUP_RANGE 0x0006 +#define PDR_PRISM_MAC_SUP_RANGE 0x0007 +#define PDR_NIC_ID 0x0008 + +#define PDR_MAC_ADDRESS 0x0101 +#define PDR_REGULATORY_DOMAIN_LIST 0x0103 /* obsolete */ +#define PDR_ALLOWED_CHAN_SET 0x0104 +#define PDR_DEFAULT_CHAN 0x0105 +#define PDR_TEMPERATURE_TYPE 0x0107 + +#define PDR_IFR_SETTING 0x0200 +#define PDR_RFR_SETTING 0x0201 +#define PDR_3861_BASELINE_REG_SETTINGS 0x0202 +#define PDR_3861_SHADOW_REG_SETTINGS 0x0203 +#define PDR_3861_IFRF_REG_SETTINGS 0x0204 + +#define PDR_3861_CHAN_CALIB_SET_POINTS 0x0300 +#define PDR_3861_CHAN_CALIB_INTEGRATOR 0x0301 + +#define PDR_3842_PRISM_II_NIC_CONFIG 0x0400 +#define PDR_PRISM_USB_ID 0x0401 +#define PDR_PRISM_PCI_ID 0x0402 +#define PDR_PRISM_PCI_IF_CONFIG 0x0403 +#define PDR_PRISM_PCI_PM_CONFIG 0x0404 + +#define PDR_3861_MF_TEST_CHAN_SET_POINTS 0x0900 +#define PDR_3861_MF_TEST_CHAN_INTEGRATORS 0x0901 + +/* ARM range (0x1000 - 0x1fff) */ +#define PDR_COUNTRY_INFORMATION 0x1000 /* obsolete */ +#define PDR_INTERFACE_LIST 0x1001 +#define PDR_HARDWARE_PLATFORM_COMPONENT_ID 0x1002 +#define PDR_OEM_NAME 0x1003 +#define PDR_PRODUCT_NAME 0x1004 +#define PDR_UTF8_OEM_NAME 0x1005 +#define PDR_UTF8_PRODUCT_NAME 0x1006 +#define PDR_COUNTRY_LIST 0x1007 +#define PDR_DEFAULT_COUNTRY 0x1008 + +#define PDR_ANTENNA_GAIN 0x1100 + +#define PDR_PRISM_INDIGO_PA_CALIBRATION_DATA 0x1901 +#define PDR_RSSI_LINEAR_APPROXIMATION 0x1902 +#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS 0x1903 +#define PDR_PRISM_PA_CAL_CURVE_DATA 0x1904 +#define PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND 0x1905 +#define PDR_PRISM_ZIF_TX_IQ_CALIBRATION 0x1906 +#define PDR_REGULATORY_POWER_LIMITS 0x1907 +#define PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED 0x1908 +#define PDR_RADIATED_TRANSMISSION_CORRECTION 0x1909 +#define PDR_PRISM_TX_IQ_CALIBRATION 0x190a + +/* reserved range (0x2000 - 0x7fff) */ + +/* customer range (0x8000 - 0xffff) */ +#define PDR_BASEBAND_REGISTERS 0x8000 +#define PDR_PER_CHANNEL_BASEBAND_REGISTERS 0x8001 + +/* used by our modificated eeprom image */ +#define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM 0xDEAD +#define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2 0xCAFF +#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM 0xBEEF +#define PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM 0xB05D + +/* Interface Definitions */ +#define PDR_INTERFACE_ROLE_SERVER 0x0000 +#define PDR_INTERFACE_ROLE_CLIENT 0x0001 + +/* PDR definitions for default country & country list */ +#define PDR_COUNTRY_CERT_CODE 0x80 +#define PDR_COUNTRY_CERT_CODE_REAL 0x00 +#define PDR_COUNTRY_CERT_CODE_PSEUDO 0x80 +#define PDR_COUNTRY_CERT_BAND 0x40 +#define PDR_COUNTRY_CERT_BAND_2GHZ 0x00 +#define PDR_COUNTRY_CERT_BAND_5GHZ 0x40 +#define PDR_COUNTRY_CERT_IODOOR 0x30 +#define PDR_COUNTRY_CERT_IODOOR_BOTH 0x00 +#define PDR_COUNTRY_CERT_IODOOR_INDOOR 0x20 +#define PDR_COUNTRY_CERT_IODOOR_OUTDOOR 0x30 +#define PDR_COUNTRY_CERT_INDEX 0x0f + +/* Specific LMAC FW/HW variant definitions */ +#define PDR_SYNTH_FRONTEND_MASK 0x0007 +#define PDR_SYNTH_FRONTEND_DUETTE3 0x0001 +#define PDR_SYNTH_FRONTEND_DUETTE2 0x0002 +#define PDR_SYNTH_FRONTEND_FRISBEE 0x0003 +#define PDR_SYNTH_FRONTEND_XBOW 0x0004 +#define PDR_SYNTH_FRONTEND_LONGBOW 0x0005 +#define PDR_SYNTH_IQ_CAL_MASK 0x0018 +#define PDR_SYNTH_IQ_CAL_PA_DETECTOR 0x0000 +#define PDR_SYNTH_IQ_CAL_DISABLED 0x0008 +#define PDR_SYNTH_IQ_CAL_ZIF 0x0010 +#define PDR_SYNTH_FAA_SWITCH_MASK 0x0020 +#define PDR_SYNTH_FAA_SWITCH_ENABLED 0x0020 +#define PDR_SYNTH_24_GHZ_MASK 0x0040 +#define PDR_SYNTH_24_GHZ_DISABLED 0x0040 +#define PDR_SYNTH_5_GHZ_MASK 0x0080 +#define PDR_SYNTH_5_GHZ_DISABLED 0x0080 +#define PDR_SYNTH_RX_DIV_MASK 0x0100 +#define PDR_SYNTH_RX_DIV_SUPPORTED 0x0100 +#define PDR_SYNTH_TX_DIV_MASK 0x0200 +#define PDR_SYNTH_TX_DIV_SUPPORTED 0x0200 +#define PDR_SYNTH_ASM_MASK 0x0400 +#define PDR_SYNTH_ASM_XSWON 0x0400 + +#endif /* EEPROM_H */ diff --git a/drivers/net/wireless/intersil/p54/fwio.c b/drivers/net/wireless/intersil/p54/fwio.c new file mode 100644 index 000000000000..257a9eadd595 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/fwio.c @@ -0,0 +1,764 @@ +/* + * Firmware I/O code for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2007-2009, Christian Lamparter + * Copyright 2008, Johannes Berg + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include + +#include "p54.h" +#include "eeprom.h" +#include "lmac.h" + +int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) +{ + struct p54_common *priv = dev->priv; + struct exp_if *exp_if; + struct bootrec *bootrec; + u32 *data = (u32 *)fw->data; + u32 *end_data = (u32 *)fw->data + (fw->size >> 2); + u8 *fw_version = NULL; + size_t len; + int i; + int maxlen; + + if (priv->rx_start) + return 0; + + while (data < end_data && *data) + data++; + + while (data < end_data && !*data) + data++; + + bootrec = (struct bootrec *) data; + + while (bootrec->data <= end_data && (bootrec->data + + (len = le32_to_cpu(bootrec->len))) <= end_data) { + u32 code = le32_to_cpu(bootrec->code); + switch (code) { + case BR_CODE_COMPONENT_ID: + priv->fw_interface = be32_to_cpup((__be32 *) + bootrec->data); + switch (priv->fw_interface) { + case FW_LM86: + case FW_LM20: + case FW_LM87: { + char *iftype = (char *)bootrec->data; + wiphy_info(priv->hw->wiphy, + "p54 detected a LM%c%c firmware\n", + iftype[2], iftype[3]); + break; + } + case FW_FMAC: + default: + wiphy_err(priv->hw->wiphy, + "unsupported firmware\n"); + return -ENODEV; + } + break; + case BR_CODE_COMPONENT_VERSION: + /* 24 bytes should be enough for all firmwares */ + if (strnlen((unsigned char *) bootrec->data, 24) < 24) + fw_version = (unsigned char *) bootrec->data; + break; + case BR_CODE_DESCR: { + struct bootrec_desc *desc = + (struct bootrec_desc *)bootrec->data; + priv->rx_start = le32_to_cpu(desc->rx_start); + /* FIXME add sanity checking */ + priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500; + priv->headroom = desc->headroom; + priv->tailroom = desc->tailroom; + priv->privacy_caps = desc->privacy_caps; + priv->rx_keycache_size = desc->rx_keycache_size; + if (le32_to_cpu(bootrec->len) == 11) + priv->rx_mtu = le16_to_cpu(desc->rx_mtu); + else + priv->rx_mtu = (size_t) + 0x620 - priv->tx_hdr_len; + maxlen = priv->tx_hdr_len + /* USB devices */ + sizeof(struct p54_rx_data) + + 4 + /* rx alignment */ + IEEE80211_MAX_FRAG_THRESHOLD; + if (priv->rx_mtu > maxlen && PAGE_SIZE == 4096) { + printk(KERN_INFO "p54: rx_mtu reduced from %d " + "to %d\n", priv->rx_mtu, maxlen); + priv->rx_mtu = maxlen; + } + break; + } + case BR_CODE_EXPOSED_IF: + exp_if = (struct exp_if *) bootrec->data; + for (i = 0; i < (len * sizeof(*exp_if) / 4); i++) + if (exp_if[i].if_id == cpu_to_le16(IF_ID_LMAC)) + priv->fw_var = le16_to_cpu(exp_if[i].variant); + break; + case BR_CODE_DEPENDENT_IF: + break; + case BR_CODE_END_OF_BRA: + case LEGACY_BR_CODE_END_OF_BRA: + end_data = NULL; + break; + default: + break; + } + bootrec = (struct bootrec *)&bootrec->data[len]; + } + + if (fw_version) { + wiphy_info(priv->hw->wiphy, + "FW rev %s - Softmac protocol %x.%x\n", + fw_version, priv->fw_var >> 8, priv->fw_var & 0xff); + snprintf(dev->wiphy->fw_version, sizeof(dev->wiphy->fw_version), + "%s - %x.%x", fw_version, + priv->fw_var >> 8, priv->fw_var & 0xff); + } + + if (priv->fw_var < 0x500) + wiphy_info(priv->hw->wiphy, + "you are using an obsolete firmware. " + "visit http://wireless.kernel.org/en/users/Drivers/p54 " + "and grab one for \"kernel >= 2.6.28\"!\n"); + + if (priv->fw_var >= 0x300) { + /* Firmware supports QoS, use it! */ + + if (priv->fw_var >= 0x500) { + priv->tx_stats[P54_QUEUE_AC_VO].limit = 16; + priv->tx_stats[P54_QUEUE_AC_VI].limit = 16; + priv->tx_stats[P54_QUEUE_AC_BE].limit = 16; + priv->tx_stats[P54_QUEUE_AC_BK].limit = 16; + } else { + priv->tx_stats[P54_QUEUE_AC_VO].limit = 3; + priv->tx_stats[P54_QUEUE_AC_VI].limit = 4; + priv->tx_stats[P54_QUEUE_AC_BE].limit = 3; + priv->tx_stats[P54_QUEUE_AC_BK].limit = 2; + } + priv->hw->queues = P54_QUEUE_AC_NUM; + } + + wiphy_info(priv->hw->wiphy, + "cryptographic accelerator WEP:%s, TKIP:%s, CCMP:%s\n", + (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" : "no", + (priv->privacy_caps & + (BR_DESC_PRIV_CAP_TKIP | BR_DESC_PRIV_CAP_MICHAEL)) + ? "YES" : "no", + (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP) + ? "YES" : "no"); + + if (priv->rx_keycache_size) { + /* + * NOTE: + * + * The firmware provides at most 255 (0 - 254) slots + * for keys which are then used to offload decryption. + * As a result the 255 entry (aka 0xff) can be used + * safely by the driver to mark keys that didn't fit + * into the full cache. This trick saves us from + * keeping a extra list for uploaded keys. + */ + + priv->used_rxkeys = kzalloc(BITS_TO_LONGS( + priv->rx_keycache_size), GFP_KERNEL); + + if (!priv->used_rxkeys) + return -ENOMEM; + } + + return 0; +} +EXPORT_SYMBOL_GPL(p54_parse_firmware); + +static struct sk_buff *p54_alloc_skb(struct p54_common *priv, u16 hdr_flags, + u16 payload_len, u16 type, gfp_t memflags) +{ + struct p54_hdr *hdr; + struct sk_buff *skb; + size_t frame_len = sizeof(*hdr) + payload_len; + + if (frame_len > P54_MAX_CTRL_FRAME_LEN) + return NULL; + + if (unlikely(skb_queue_len(&priv->tx_pending) > 64)) + return NULL; + + skb = __dev_alloc_skb(priv->tx_hdr_len + frame_len, memflags); + if (!skb) + return NULL; + skb_reserve(skb, priv->tx_hdr_len); + + hdr = (struct p54_hdr *) skb_put(skb, sizeof(*hdr)); + hdr->flags = cpu_to_le16(hdr_flags); + hdr->len = cpu_to_le16(payload_len); + hdr->type = cpu_to_le16(type); + hdr->tries = hdr->rts_tries = 0; + return skb; +} + +int p54_download_eeprom(struct p54_common *priv, void *buf, + u16 offset, u16 len) +{ + struct p54_eeprom_lm86 *eeprom_hdr; + struct sk_buff *skb; + size_t eeprom_hdr_size; + int ret = 0; + long timeout; + + if (priv->fw_var >= 0x509) + eeprom_hdr_size = sizeof(*eeprom_hdr); + else + eeprom_hdr_size = 0x4; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, eeprom_hdr_size + + len, P54_CONTROL_TYPE_EEPROM_READBACK, + GFP_KERNEL); + if (unlikely(!skb)) + return -ENOMEM; + + mutex_lock(&priv->eeprom_mutex); + priv->eeprom = buf; + eeprom_hdr = (struct p54_eeprom_lm86 *) skb_put(skb, + eeprom_hdr_size + len); + + if (priv->fw_var < 0x509) { + eeprom_hdr->v1.offset = cpu_to_le16(offset); + eeprom_hdr->v1.len = cpu_to_le16(len); + } else { + eeprom_hdr->v2.offset = cpu_to_le32(offset); + eeprom_hdr->v2.len = cpu_to_le16(len); + eeprom_hdr->v2.magic2 = 0xf; + memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4); + } + + p54_tx(priv, skb); + + timeout = wait_for_completion_interruptible_timeout( + &priv->eeprom_comp, HZ); + if (timeout <= 0) { + wiphy_err(priv->hw->wiphy, + "device does not respond or signal received!\n"); + ret = -EBUSY; + } + priv->eeprom = NULL; + mutex_unlock(&priv->eeprom_mutex); + return ret; +} + +int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set) +{ + struct sk_buff *skb; + struct p54_tim *tim; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*tim), + P54_CONTROL_TYPE_TIM, GFP_ATOMIC); + if (unlikely(!skb)) + return -ENOMEM; + + tim = (struct p54_tim *) skb_put(skb, sizeof(*tim)); + tim->count = 1; + tim->entry[0] = cpu_to_le16(set ? (aid | 0x8000) : aid); + p54_tx(priv, skb); + return 0; +} + +int p54_sta_unlock(struct p54_common *priv, u8 *addr) +{ + struct sk_buff *skb; + struct p54_sta_unlock *sta; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*sta), + P54_CONTROL_TYPE_PSM_STA_UNLOCK, GFP_ATOMIC); + if (unlikely(!skb)) + return -ENOMEM; + + sta = (struct p54_sta_unlock *)skb_put(skb, sizeof(*sta)); + memcpy(sta->addr, addr, ETH_ALEN); + p54_tx(priv, skb); + return 0; +} + +int p54_tx_cancel(struct p54_common *priv, __le32 req_id) +{ + struct sk_buff *skb; + struct p54_txcancel *cancel; + u32 _req_id = le32_to_cpu(req_id); + + if (unlikely(_req_id < priv->rx_start || _req_id > priv->rx_end)) + return -EINVAL; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*cancel), + P54_CONTROL_TYPE_TXCANCEL, GFP_ATOMIC); + if (unlikely(!skb)) + return -ENOMEM; + + cancel = (struct p54_txcancel *)skb_put(skb, sizeof(*cancel)); + cancel->req_id = req_id; + p54_tx(priv, skb); + return 0; +} + +int p54_setup_mac(struct p54_common *priv) +{ + struct sk_buff *skb; + struct p54_setup_mac *setup; + u16 mode; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*setup), + P54_CONTROL_TYPE_SETUP, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + setup = (struct p54_setup_mac *) skb_put(skb, sizeof(*setup)); + if (!(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { + switch (priv->mode) { + case NL80211_IFTYPE_STATION: + mode = P54_FILTER_TYPE_STATION; + break; + case NL80211_IFTYPE_AP: + mode = P54_FILTER_TYPE_AP; + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + mode = P54_FILTER_TYPE_IBSS; + break; + case NL80211_IFTYPE_MONITOR: + mode = P54_FILTER_TYPE_PROMISCUOUS; + break; + default: + mode = P54_FILTER_TYPE_HIBERNATE; + break; + } + + /* + * "TRANSPARENT and PROMISCUOUS are mutually exclusive" + * STSW45X0C LMAC API - page 12 + */ + if (priv->filter_flags & FIF_OTHER_BSS && + (mode != P54_FILTER_TYPE_PROMISCUOUS)) + mode |= P54_FILTER_TYPE_TRANSPARENT; + } else { + mode = P54_FILTER_TYPE_HIBERNATE; + } + + setup->mac_mode = cpu_to_le16(mode); + memcpy(setup->mac_addr, priv->mac_addr, ETH_ALEN); + memcpy(setup->bssid, priv->bssid, ETH_ALEN); + setup->rx_antenna = 2 & priv->rx_diversity_mask; /* automatic */ + setup->rx_align = 0; + if (priv->fw_var < 0x500) { + setup->v1.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); + memset(setup->v1.rts_rates, 0, 8); + setup->v1.rx_addr = cpu_to_le32(priv->rx_end); + setup->v1.max_rx = cpu_to_le16(priv->rx_mtu); + setup->v1.rxhw = cpu_to_le16(priv->rxhw); + setup->v1.wakeup_timer = cpu_to_le16(priv->wakeup_timer); + setup->v1.unalloc0 = cpu_to_le16(0); + } else { + setup->v2.rx_addr = cpu_to_le32(priv->rx_end); + setup->v2.max_rx = cpu_to_le16(priv->rx_mtu); + setup->v2.rxhw = cpu_to_le16(priv->rxhw); + setup->v2.timer = cpu_to_le16(priv->wakeup_timer); + setup->v2.truncate = cpu_to_le16(48896); + setup->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); + setup->v2.sbss_offset = 0; + setup->v2.mcast_window = 0; + setup->v2.rx_rssi_threshold = 0; + setup->v2.rx_ed_threshold = 0; + setup->v2.ref_clock = cpu_to_le32(644245094); + setup->v2.lpf_bandwidth = cpu_to_le16(65535); + setup->v2.osc_start_delay = cpu_to_le16(65535); + } + p54_tx(priv, skb); + priv->phy_idle = mode == P54_FILTER_TYPE_HIBERNATE; + return 0; +} + +int p54_scan(struct p54_common *priv, u16 mode, u16 dwell) +{ + struct sk_buff *skb; + struct p54_hdr *hdr; + struct p54_scan_head *head; + struct p54_iq_autocal_entry *iq_autocal; + union p54_scan_body_union *body; + struct p54_scan_tail_rate *rate; + struct pda_rssi_cal_entry *rssi; + struct p54_rssi_db_entry *rssi_data; + unsigned int i; + void *entry; + __le16 freq = cpu_to_le16(priv->hw->conf.chandef.chan->center_freq); + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) + + 2 + sizeof(*iq_autocal) + sizeof(*body) + + sizeof(*rate) + 2 * sizeof(*rssi), + P54_CONTROL_TYPE_SCAN, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + head = (struct p54_scan_head *) skb_put(skb, sizeof(*head)); + memset(head->scan_params, 0, sizeof(head->scan_params)); + head->mode = cpu_to_le16(mode); + head->dwell = cpu_to_le16(dwell); + head->freq = freq; + + if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { + __le16 *pa_power_points = (__le16 *) skb_put(skb, 2); + *pa_power_points = cpu_to_le16(0x0c); + } + + iq_autocal = (void *) skb_put(skb, sizeof(*iq_autocal)); + for (i = 0; i < priv->iq_autocal_len; i++) { + if (priv->iq_autocal[i].freq != freq) + continue; + + memcpy(iq_autocal, &priv->iq_autocal[i].params, + sizeof(struct p54_iq_autocal_entry)); + break; + } + if (i == priv->iq_autocal_len) + goto err; + + if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) + body = (void *) skb_put(skb, sizeof(body->longbow)); + else + body = (void *) skb_put(skb, sizeof(body->normal)); + + for (i = 0; i < priv->output_limit->entries; i++) { + __le16 *entry_freq = (void *) (priv->output_limit->data + + priv->output_limit->entry_size * i); + + if (*entry_freq != freq) + continue; + + if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { + memcpy(&body->longbow.power_limits, + (void *) entry_freq + sizeof(__le16), + priv->output_limit->entry_size); + } else { + struct pda_channel_output_limit *limits = + (void *) entry_freq; + + body->normal.val_barker = 0x38; + body->normal.val_bpsk = body->normal.dup_bpsk = + limits->val_bpsk; + body->normal.val_qpsk = body->normal.dup_qpsk = + limits->val_qpsk; + body->normal.val_16qam = body->normal.dup_16qam = + limits->val_16qam; + body->normal.val_64qam = body->normal.dup_64qam = + limits->val_64qam; + } + break; + } + if (i == priv->output_limit->entries) + goto err; + + entry = (void *)(priv->curve_data->data + priv->curve_data->offset); + for (i = 0; i < priv->curve_data->entries; i++) { + if (*((__le16 *)entry) != freq) { + entry += priv->curve_data->entry_size; + continue; + } + + if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { + memcpy(&body->longbow.curve_data, + entry + sizeof(__le16), + priv->curve_data->entry_size); + } else { + struct p54_scan_body *chan = &body->normal; + struct pda_pa_curve_data *curve_data = + (void *) priv->curve_data->data; + + entry += sizeof(__le16); + chan->pa_points_per_curve = 8; + memset(chan->curve_data, 0, sizeof(*chan->curve_data)); + memcpy(chan->curve_data, entry, + sizeof(struct p54_pa_curve_data_sample) * + min((u8)8, curve_data->points_per_channel)); + } + break; + } + if (i == priv->curve_data->entries) + goto err; + + if ((priv->fw_var >= 0x500) && (priv->fw_var < 0x509)) { + rate = (void *) skb_put(skb, sizeof(*rate)); + rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); + for (i = 0; i < sizeof(rate->rts_rates); i++) + rate->rts_rates[i] = i; + } + + rssi = (struct pda_rssi_cal_entry *) skb_put(skb, sizeof(*rssi)); + rssi_data = p54_rssi_find(priv, le16_to_cpu(freq)); + rssi->mul = cpu_to_le16(rssi_data->mul); + rssi->add = cpu_to_le16(rssi_data->add); + if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { + /* Longbow frontend needs ever more */ + rssi = (void *) skb_put(skb, sizeof(*rssi)); + rssi->mul = cpu_to_le16(rssi_data->longbow_unkn); + rssi->add = cpu_to_le16(rssi_data->longbow_unk2); + } + + if (priv->fw_var >= 0x509) { + rate = (void *) skb_put(skb, sizeof(*rate)); + rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); + for (i = 0; i < sizeof(rate->rts_rates); i++) + rate->rts_rates[i] = i; + } + + hdr = (struct p54_hdr *) skb->data; + hdr->len = cpu_to_le16(skb->len - sizeof(*hdr)); + + p54_tx(priv, skb); + priv->cur_rssi = rssi_data; + return 0; + +err: + wiphy_err(priv->hw->wiphy, "frequency change to channel %d failed.\n", + ieee80211_frequency_to_channel( + priv->hw->conf.chandef.chan->center_freq)); + + dev_kfree_skb_any(skb); + return -EINVAL; +} + +int p54_set_leds(struct p54_common *priv) +{ + struct sk_buff *skb; + struct p54_led *led; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*led), + P54_CONTROL_TYPE_LED, GFP_ATOMIC); + if (unlikely(!skb)) + return -ENOMEM; + + led = (struct p54_led *) skb_put(skb, sizeof(*led)); + led->flags = cpu_to_le16(0x0003); + led->mask[0] = led->mask[1] = cpu_to_le16(priv->softled_state); + led->delay[0] = cpu_to_le16(1); + led->delay[1] = cpu_to_le16(0); + p54_tx(priv, skb); + return 0; +} + +int p54_set_edcf(struct p54_common *priv) +{ + struct sk_buff *skb; + struct p54_edcf *edcf; + u8 rtd; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf), + P54_CONTROL_TYPE_DCFINIT, GFP_ATOMIC); + if (unlikely(!skb)) + return -ENOMEM; + + edcf = (struct p54_edcf *)skb_put(skb, sizeof(*edcf)); + if (priv->use_short_slot) { + edcf->slottime = 9; + edcf->sifs = 0x10; + edcf->eofpad = 0x00; + } else { + edcf->slottime = 20; + edcf->sifs = 0x0a; + edcf->eofpad = 0x06; + } + /* + * calculate the extra round trip delay according to the + * formula from 802.11-2007 17.3.8.6. + */ + rtd = 3 * priv->coverage_class; + edcf->slottime += rtd; + edcf->round_trip_delay = cpu_to_le16(rtd); + /* (see prism54/isl_oid.h for further details) */ + edcf->frameburst = cpu_to_le16(0); + edcf->flags = 0; + memset(edcf->mapping, 0, sizeof(edcf->mapping)); + memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue)); + p54_tx(priv, skb); + return 0; +} + +int p54_set_ps(struct p54_common *priv) +{ + struct sk_buff *skb; + struct p54_psm *psm; + unsigned int i; + u16 mode; + + if (priv->hw->conf.flags & IEEE80211_CONF_PS && + !priv->powersave_override) + mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM | + P54_PSM_CHECKSUM | P54_PSM_MCBC; + else + mode = P54_PSM_CAM; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*psm), + P54_CONTROL_TYPE_PSM, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + psm = (struct p54_psm *)skb_put(skb, sizeof(*psm)); + psm->mode = cpu_to_le16(mode); + psm->aid = cpu_to_le16(priv->aid); + for (i = 0; i < ARRAY_SIZE(psm->intervals); i++) { + psm->intervals[i].interval = + cpu_to_le16(priv->hw->conf.listen_interval); + psm->intervals[i].periods = cpu_to_le16(1); + } + + psm->beacon_rssi_skip_max = 200; + psm->rssi_delta_threshold = 0; + psm->nr = 1; + psm->exclude[0] = WLAN_EID_TIM; + + p54_tx(priv, skb); + priv->phy_ps = mode != P54_PSM_CAM; + return 0; +} + +int p54_init_xbow_synth(struct p54_common *priv) +{ + struct sk_buff *skb; + struct p54_xbow_synth *xbow; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*xbow), + P54_CONTROL_TYPE_XBOW_SYNTH_CFG, GFP_KERNEL); + if (unlikely(!skb)) + return -ENOMEM; + + xbow = (struct p54_xbow_synth *)skb_put(skb, sizeof(*xbow)); + xbow->magic1 = cpu_to_le16(0x1); + xbow->magic2 = cpu_to_le16(0x2); + xbow->freq = cpu_to_le16(5390); + memset(xbow->padding, 0, sizeof(xbow->padding)); + p54_tx(priv, skb); + return 0; +} + +int p54_upload_key(struct p54_common *priv, u8 algo, int slot, u8 idx, u8 len, + u8 *addr, u8* key) +{ + struct sk_buff *skb; + struct p54_keycache *rxkey; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey), + P54_CONTROL_TYPE_RX_KEYCACHE, GFP_KERNEL); + if (unlikely(!skb)) + return -ENOMEM; + + rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey)); + rxkey->entry = slot; + rxkey->key_id = idx; + rxkey->key_type = algo; + if (addr) + memcpy(rxkey->mac, addr, ETH_ALEN); + else + eth_broadcast_addr(rxkey->mac); + + switch (algo) { + case P54_CRYPTO_WEP: + case P54_CRYPTO_AESCCMP: + rxkey->key_len = min_t(u8, 16, len); + memcpy(rxkey->key, key, rxkey->key_len); + break; + + case P54_CRYPTO_TKIPMICHAEL: + rxkey->key_len = 24; + memcpy(rxkey->key, key, 16); + memcpy(&(rxkey->key[16]), &(key + [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8); + break; + + case P54_CRYPTO_NONE: + rxkey->key_len = 0; + memset(rxkey->key, 0, sizeof(rxkey->key)); + break; + + default: + wiphy_err(priv->hw->wiphy, + "invalid cryptographic algorithm: %d\n", algo); + dev_kfree_skb(skb); + return -EINVAL; + } + + p54_tx(priv, skb); + return 0; +} + +int p54_fetch_statistics(struct p54_common *priv) +{ + struct ieee80211_tx_info *txinfo; + struct p54_tx_info *p54info; + struct sk_buff *skb; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, + sizeof(struct p54_statistics), + P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + /* + * The statistic feedback causes some extra headaches here, if it + * is not to crash/corrupt the firmware data structures. + * + * Unlike all other Control Get OIDs we can not use helpers like + * skb_put to reserve the space for the data we're requesting. + * Instead the extra frame length -which will hold the results later- + * will only be told to the p54_assign_address, so that following + * frames won't be placed into the allegedly empty area. + */ + txinfo = IEEE80211_SKB_CB(skb); + p54info = (void *) txinfo->rate_driver_data; + p54info->extra_len = sizeof(struct p54_statistics); + + p54_tx(priv, skb); + return 0; +} + +int p54_set_groupfilter(struct p54_common *priv) +{ + struct p54_group_address_table *grp; + struct sk_buff *skb; + bool on = false; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*grp), + P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + grp = (struct p54_group_address_table *)skb_put(skb, sizeof(*grp)); + + on = !(priv->filter_flags & FIF_ALLMULTI) && + (priv->mc_maclist_num > 0 && + priv->mc_maclist_num <= MC_FILTER_ADDRESS_NUM); + + if (on) { + grp->filter_enable = cpu_to_le16(1); + grp->num_address = cpu_to_le16(priv->mc_maclist_num); + memcpy(grp->mac_list, priv->mc_maclist, sizeof(grp->mac_list)); + } else { + grp->filter_enable = cpu_to_le16(0); + grp->num_address = cpu_to_le16(0); + memset(grp->mac_list, 0, sizeof(grp->mac_list)); + } + + p54_tx(priv, skb); + return 0; +} diff --git a/drivers/net/wireless/intersil/p54/led.c b/drivers/net/wireless/intersil/p54/led.c new file mode 100644 index 000000000000..9a8fedd3c0f5 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/led.c @@ -0,0 +1,161 @@ +/* + * Common code for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2007-2009, Christian Lamparter + * Copyright 2008, Johannes Berg + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include +#ifdef CONFIG_P54_LEDS +#include +#endif /* CONFIG_P54_LEDS */ + +#include "p54.h" +#include "lmac.h" + +static void p54_update_leds(struct work_struct *work) +{ + struct p54_common *priv = container_of(work, struct p54_common, + led_work.work); + int err, i, tmp, blink_delay = 400; + bool rerun = false; + + /* Don't toggle the LED, when the device is down. */ + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + return ; + + for (i = 0; i < ARRAY_SIZE(priv->leds); i++) + if (priv->leds[i].toggled) { + priv->softled_state |= BIT(i); + + tmp = 70 + 200 / (priv->leds[i].toggled); + if (tmp < blink_delay) + blink_delay = tmp; + + if (priv->leds[i].led_dev.brightness == LED_OFF) + rerun = true; + + priv->leds[i].toggled = + !!priv->leds[i].led_dev.brightness; + } else + priv->softled_state &= ~BIT(i); + + err = p54_set_leds(priv); + if (err && net_ratelimit()) + wiphy_err(priv->hw->wiphy, + "failed to update LEDs (%d).\n", err); + + if (rerun) + ieee80211_queue_delayed_work(priv->hw, &priv->led_work, + msecs_to_jiffies(blink_delay)); +} + +static void p54_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct p54_led_dev *led = container_of(led_dev, struct p54_led_dev, + led_dev); + struct ieee80211_hw *dev = led->hw_dev; + struct p54_common *priv = dev->priv; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + return ; + + if ((brightness) && (led->registered)) { + led->toggled++; + ieee80211_queue_delayed_work(priv->hw, &priv->led_work, HZ/10); + } +} + +static int p54_register_led(struct p54_common *priv, + unsigned int led_index, + char *name, const char *trigger) +{ + struct p54_led_dev *led = &priv->leds[led_index]; + int err; + + if (led->registered) + return -EEXIST; + + snprintf(led->name, sizeof(led->name), "p54-%s::%s", + wiphy_name(priv->hw->wiphy), name); + led->hw_dev = priv->hw; + led->index = led_index; + led->led_dev.name = led->name; + led->led_dev.default_trigger = trigger; + led->led_dev.brightness_set = p54_led_brightness_set; + + err = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_dev); + if (err) + wiphy_err(priv->hw->wiphy, + "Failed to register %s LED.\n", name); + else + led->registered = 1; + + return err; +} + +int p54_init_leds(struct p54_common *priv) +{ + int err; + + /* + * TODO: + * Figure out if the EEPROM contains some hints about the number + * of available/programmable LEDs of the device. + */ + + INIT_DELAYED_WORK(&priv->led_work, p54_update_leds); + + err = p54_register_led(priv, 0, "assoc", + ieee80211_get_assoc_led_name(priv->hw)); + if (err) + return err; + + err = p54_register_led(priv, 1, "tx", + ieee80211_get_tx_led_name(priv->hw)); + if (err) + return err; + + err = p54_register_led(priv, 2, "rx", + ieee80211_get_rx_led_name(priv->hw)); + if (err) + return err; + + err = p54_register_led(priv, 3, "radio", + ieee80211_get_radio_led_name(priv->hw)); + if (err) + return err; + + err = p54_set_leds(priv); + return err; +} + +void p54_unregister_leds(struct p54_common *priv) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(priv->leds); i++) { + if (priv->leds[i].registered) { + priv->leds[i].registered = false; + priv->leds[i].toggled = 0; + led_classdev_unregister(&priv->leds[i].led_dev); + } + } + + cancel_delayed_work_sync(&priv->led_work); +} diff --git a/drivers/net/wireless/intersil/p54/lmac.h b/drivers/net/wireless/intersil/p54/lmac.h new file mode 100644 index 000000000000..de1d46bf97df --- /dev/null +++ b/drivers/net/wireless/intersil/p54/lmac.h @@ -0,0 +1,562 @@ +/* + * LMAC Interface specific definitions for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2007 - 2009, Christian Lamparter + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * - LMAC API interface header file for STLC4560 (lmac_longbow.h) + * Copyright (C) 2007 Conexant Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef LMAC_H +#define LMAC_H + +enum p54_control_frame_types { + P54_CONTROL_TYPE_SETUP = 0, + P54_CONTROL_TYPE_SCAN, + P54_CONTROL_TYPE_TRAP, + P54_CONTROL_TYPE_DCFINIT, + P54_CONTROL_TYPE_RX_KEYCACHE, + P54_CONTROL_TYPE_TIM, + P54_CONTROL_TYPE_PSM, + P54_CONTROL_TYPE_TXCANCEL, + P54_CONTROL_TYPE_TXDONE, + P54_CONTROL_TYPE_BURST, + P54_CONTROL_TYPE_STAT_READBACK, + P54_CONTROL_TYPE_BBP, + P54_CONTROL_TYPE_EEPROM_READBACK, + P54_CONTROL_TYPE_LED, + P54_CONTROL_TYPE_GPIO, + P54_CONTROL_TYPE_TIMER, + P54_CONTROL_TYPE_MODULATION, + P54_CONTROL_TYPE_SYNTH_CONFIG, + P54_CONTROL_TYPE_DETECTOR_VALUE, + P54_CONTROL_TYPE_XBOW_SYNTH_CFG, + P54_CONTROL_TYPE_CCE_QUIET, + P54_CONTROL_TYPE_PSM_STA_UNLOCK, + P54_CONTROL_TYPE_PCS, + P54_CONTROL_TYPE_BT_BALANCER = 28, + P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE = 30, + P54_CONTROL_TYPE_ARPTABLE = 31, + P54_CONTROL_TYPE_BT_OPTIONS = 35, +}; + +#define P54_HDR_FLAG_CONTROL BIT(15) +#define P54_HDR_FLAG_CONTROL_OPSET (BIT(15) + BIT(0)) +#define P54_HDR_FLAG_DATA_ALIGN BIT(14) + +#define P54_HDR_FLAG_DATA_OUT_PROMISC BIT(0) +#define P54_HDR_FLAG_DATA_OUT_TIMESTAMP BIT(1) +#define P54_HDR_FLAG_DATA_OUT_SEQNR BIT(2) +#define P54_HDR_FLAG_DATA_OUT_BIT3 BIT(3) +#define P54_HDR_FLAG_DATA_OUT_BURST BIT(4) +#define P54_HDR_FLAG_DATA_OUT_NOCANCEL BIT(5) +#define P54_HDR_FLAG_DATA_OUT_CLEARTIM BIT(6) +#define P54_HDR_FLAG_DATA_OUT_HITCHHIKE BIT(7) +#define P54_HDR_FLAG_DATA_OUT_COMPRESS BIT(8) +#define P54_HDR_FLAG_DATA_OUT_CONCAT BIT(9) +#define P54_HDR_FLAG_DATA_OUT_PCS_ACCEPT BIT(10) +#define P54_HDR_FLAG_DATA_OUT_WAITEOSP BIT(11) + +#define P54_HDR_FLAG_DATA_IN_FCS_GOOD BIT(0) +#define P54_HDR_FLAG_DATA_IN_MATCH_MAC BIT(1) +#define P54_HDR_FLAG_DATA_IN_MCBC BIT(2) +#define P54_HDR_FLAG_DATA_IN_BEACON BIT(3) +#define P54_HDR_FLAG_DATA_IN_MATCH_BSS BIT(4) +#define P54_HDR_FLAG_DATA_IN_BCAST_BSS BIT(5) +#define P54_HDR_FLAG_DATA_IN_DATA BIT(6) +#define P54_HDR_FLAG_DATA_IN_TRUNCATED BIT(7) +#define P54_HDR_FLAG_DATA_IN_BIT8 BIT(8) +#define P54_HDR_FLAG_DATA_IN_TRANSPARENT BIT(9) + +struct p54_hdr { + __le16 flags; + __le16 len; + __le32 req_id; + __le16 type; /* enum p54_control_frame_types */ + u8 rts_tries; + u8 tries; + u8 data[0]; +} __packed; + +#define GET_REQ_ID(skb) \ + (((struct p54_hdr *) ((struct sk_buff *) skb)->data)->req_id) \ + +#define FREE_AFTER_TX(skb) \ + ((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ + flags) == cpu_to_le16(P54_HDR_FLAG_CONTROL_OPSET)) + +#define IS_DATA_FRAME(skb) \ + (!((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ + flags) & cpu_to_le16(P54_HDR_FLAG_CONTROL))) + +#define GET_HW_QUEUE(skb) \ + (((struct p54_tx_data *)((struct p54_hdr *) \ + skb->data)->data)->hw_queue) + +/* + * shared interface ID definitions + * The interface ID is a unique identification of a specific interface. + * The following values are reserved: 0x0000, 0x0002, 0x0012, 0x0014, 0x0015 + */ +#define IF_ID_ISL36356A 0x0001 /* ISL36356A <-> Firmware */ +#define IF_ID_MVC 0x0003 /* MAC Virtual Coprocessor */ +#define IF_ID_DEBUG 0x0008 /* PolDebug Interface */ +#define IF_ID_PRODUCT 0x0009 +#define IF_ID_OEM 0x000a +#define IF_ID_PCI3877 0x000b /* 3877 <-> Host PCI */ +#define IF_ID_ISL37704C 0x000c /* ISL37704C <-> Fw */ +#define IF_ID_ISL39000 0x000f /* ISL39000 <-> Fw */ +#define IF_ID_ISL39300A 0x0010 /* ISL39300A <-> Fw */ +#define IF_ID_ISL37700_UAP 0x0016 /* ISL37700 uAP Fw <-> Fw */ +#define IF_ID_ISL39000_UAP 0x0017 /* ISL39000 uAP Fw <-> Fw */ +#define IF_ID_LMAC 0x001a /* Interface exposed by LMAC */ + +struct exp_if { + __le16 role; + __le16 if_id; + __le16 variant; + __le16 btm_compat; + __le16 top_compat; +} __packed; + +struct dep_if { + __le16 role; + __le16 if_id; + __le16 variant; +} __packed; + +/* driver <-> lmac definitions */ +struct p54_eeprom_lm86 { + union { + struct { + __le16 offset; + __le16 len; + u8 data[0]; + } __packed v1; + struct { + __le32 offset; + __le16 len; + u8 magic2; + u8 pad; + u8 magic[4]; + u8 data[0]; + } __packed v2; + } __packed; +} __packed; + +enum p54_rx_decrypt_status { + P54_DECRYPT_NONE = 0, + P54_DECRYPT_OK, + P54_DECRYPT_NOKEY, + P54_DECRYPT_NOMICHAEL, + P54_DECRYPT_NOCKIPMIC, + P54_DECRYPT_FAIL_WEP, + P54_DECRYPT_FAIL_TKIP, + P54_DECRYPT_FAIL_MICHAEL, + P54_DECRYPT_FAIL_CKIPKP, + P54_DECRYPT_FAIL_CKIPMIC, + P54_DECRYPT_FAIL_AESCCMP +}; + +struct p54_rx_data { + __le16 flags; + __le16 len; + __le16 freq; + u8 antenna; + u8 rate; + u8 rssi; + u8 quality; + u8 decrypt_status; + u8 rssi_raw; + __le32 tsf32; + __le32 unalloc0; + u8 align[0]; +} __packed; + +enum p54_trap_type { + P54_TRAP_SCAN = 0, + P54_TRAP_TIMER, + P54_TRAP_BEACON_TX, + P54_TRAP_FAA_RADIO_ON, + P54_TRAP_FAA_RADIO_OFF, + P54_TRAP_RADAR, + P54_TRAP_NO_BEACON, + P54_TRAP_TBTT, + P54_TRAP_SCO_ENTER, + P54_TRAP_SCO_EXIT +}; + +struct p54_trap { + __le16 event; + __le16 frequency; +} __packed; + +enum p54_frame_sent_status { + P54_TX_OK = 0, + P54_TX_FAILED, + P54_TX_PSM, + P54_TX_PSM_CANCELLED = 4 +}; + +struct p54_frame_sent { + u8 status; + u8 tries; + u8 ack_rssi; + u8 quality; + __le16 seq; + u8 antenna; + u8 padding; +} __packed; + +enum p54_tx_data_crypt { + P54_CRYPTO_NONE = 0, + P54_CRYPTO_WEP, + P54_CRYPTO_TKIP, + P54_CRYPTO_TKIPMICHAEL, + P54_CRYPTO_CCX_WEPMIC, + P54_CRYPTO_CCX_KPMIC, + P54_CRYPTO_CCX_KP, + P54_CRYPTO_AESCCMP +}; + +enum p54_tx_data_queue { + P54_QUEUE_BEACON = 0, + P54_QUEUE_FWSCAN = 1, + P54_QUEUE_MGMT = 2, + P54_QUEUE_CAB = 3, + P54_QUEUE_DATA = 4, + + P54_QUEUE_AC_NUM = 4, + P54_QUEUE_AC_VO = 4, + P54_QUEUE_AC_VI = 5, + P54_QUEUE_AC_BE = 6, + P54_QUEUE_AC_BK = 7, + + /* keep last */ + P54_QUEUE_NUM = 8, +}; + +#define IS_QOS_QUEUE(n) (n >= P54_QUEUE_DATA) + +struct p54_tx_data { + u8 rateset[8]; + u8 rts_rate_idx; + u8 crypt_offset; + u8 key_type; + u8 key_len; + u8 key[16]; + u8 hw_queue; + u8 backlog; + __le16 durations[4]; + u8 tx_antenna; + union { + struct { + u8 cts_rate; + __le16 output_power; + } __packed longbow; + struct { + u8 output_power; + u8 cts_rate; + u8 unalloc; + } __packed normal; + } __packed; + u8 unalloc2[2]; + u8 align[0]; +} __packed; + +/* unit is ms */ +#define P54_TX_FRAME_LIFETIME 2000 +#define P54_TX_TIMEOUT 4000 +#define P54_STATISTICS_UPDATE 5000 + +#define P54_FILTER_TYPE_NONE 0 +#define P54_FILTER_TYPE_STATION BIT(0) +#define P54_FILTER_TYPE_IBSS BIT(1) +#define P54_FILTER_TYPE_AP BIT(2) +#define P54_FILTER_TYPE_TRANSPARENT BIT(3) +#define P54_FILTER_TYPE_PROMISCUOUS BIT(4) +#define P54_FILTER_TYPE_HIBERNATE BIT(5) +#define P54_FILTER_TYPE_NOACK BIT(6) +#define P54_FILTER_TYPE_RX_DISABLED BIT(7) + +struct p54_setup_mac { + __le16 mac_mode; + u8 mac_addr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u8 rx_antenna; + u8 rx_align; + union { + struct { + __le32 basic_rate_mask; + u8 rts_rates[8]; + __le32 rx_addr; + __le16 max_rx; + __le16 rxhw; + __le16 wakeup_timer; + __le16 unalloc0; + } __packed v1; + struct { + __le32 rx_addr; + __le16 max_rx; + __le16 rxhw; + __le16 timer; + __le16 truncate; + __le32 basic_rate_mask; + u8 sbss_offset; + u8 mcast_window; + u8 rx_rssi_threshold; + u8 rx_ed_threshold; + __le32 ref_clock; + __le16 lpf_bandwidth; + __le16 osc_start_delay; + } __packed v2; + } __packed; +} __packed; + +#define P54_SETUP_V1_LEN 40 +#define P54_SETUP_V2_LEN (sizeof(struct p54_setup_mac)) + +#define P54_SCAN_EXIT BIT(0) +#define P54_SCAN_TRAP BIT(1) +#define P54_SCAN_ACTIVE BIT(2) +#define P54_SCAN_FILTER BIT(3) + +struct p54_scan_head { + __le16 mode; + __le16 dwell; + u8 scan_params[20]; + __le16 freq; +} __packed; + +struct p54_pa_curve_data_sample { + u8 rf_power; + u8 pa_detector; + u8 data_barker; + u8 data_bpsk; + u8 data_qpsk; + u8 data_16qam; + u8 data_64qam; + u8 padding; +} __packed; + +struct p54_scan_body { + u8 pa_points_per_curve; + u8 val_barker; + u8 val_bpsk; + u8 val_qpsk; + u8 val_16qam; + u8 val_64qam; + struct p54_pa_curve_data_sample curve_data[8]; + u8 dup_bpsk; + u8 dup_qpsk; + u8 dup_16qam; + u8 dup_64qam; +} __packed; + +/* + * Warning: Longbow's structures are bogus. + */ +struct p54_channel_output_limit_longbow { + __le16 rf_power_points[12]; +} __packed; + +struct p54_pa_curve_data_sample_longbow { + __le16 rf_power; + __le16 pa_detector; + struct { + __le16 data[4]; + } points[3] __packed; +} __packed; + +struct p54_scan_body_longbow { + struct p54_channel_output_limit_longbow power_limits; + struct p54_pa_curve_data_sample_longbow curve_data[8]; + __le16 unkn[6]; /* maybe more power_limits or rate_mask */ +} __packed; + +union p54_scan_body_union { + struct p54_scan_body normal; + struct p54_scan_body_longbow longbow; +} __packed; + +struct p54_scan_tail_rate { + __le32 basic_rate_mask; + u8 rts_rates[8]; +} __packed; + +struct p54_led { + __le16 flags; + __le16 mask[2]; + __le16 delay[2]; +} __packed; + +struct p54_edcf { + u8 flags; + u8 slottime; + u8 sifs; + u8 eofpad; + struct p54_edcf_queue_param queue[8]; + u8 mapping[4]; + __le16 frameburst; + __le16 round_trip_delay; +} __packed; + +struct p54_statistics { + __le32 rx_success; + __le32 rx_bad_fcs; + __le32 rx_abort; + __le32 rx_abort_phy; + __le32 rts_success; + __le32 rts_fail; + __le32 tsf32; + __le32 airtime; + __le32 noise; + __le32 sample_noise[8]; + __le32 sample_cca; + __le32 sample_tx; +} __packed; + +struct p54_xbow_synth { + __le16 magic1; + __le16 magic2; + __le16 freq; + u32 padding[5]; +} __packed; + +struct p54_timer { + __le32 interval; +} __packed; + +struct p54_keycache { + u8 entry; + u8 key_id; + u8 mac[ETH_ALEN]; + u8 padding[2]; + u8 key_type; + u8 key_len; + u8 key[24]; +} __packed; + +struct p54_burst { + u8 flags; + u8 queue; + u8 backlog; + u8 pad; + __le16 durations[32]; +} __packed; + +struct p54_psm_interval { + __le16 interval; + __le16 periods; +} __packed; + +#define P54_PSM_CAM 0 +#define P54_PSM BIT(0) +#define P54_PSM_DTIM BIT(1) +#define P54_PSM_MCBC BIT(2) +#define P54_PSM_CHECKSUM BIT(3) +#define P54_PSM_SKIP_MORE_DATA BIT(4) +#define P54_PSM_BEACON_TIMEOUT BIT(5) +#define P54_PSM_HFOSLEEP BIT(6) +#define P54_PSM_AUTOSWITCH_SLEEP BIT(7) +#define P54_PSM_LPIT BIT(8) +#define P54_PSM_BF_UCAST_SKIP BIT(9) +#define P54_PSM_BF_MCAST_SKIP BIT(10) + +struct p54_psm { + __le16 mode; + __le16 aid; + struct p54_psm_interval intervals[4]; + u8 beacon_rssi_skip_max; + u8 rssi_delta_threshold; + u8 nr; + u8 exclude[1]; +} __packed; + +#define MC_FILTER_ADDRESS_NUM 4 + +struct p54_group_address_table { + __le16 filter_enable; + __le16 num_address; + u8 mac_list[MC_FILTER_ADDRESS_NUM][ETH_ALEN]; +} __packed; + +struct p54_txcancel { + __le32 req_id; +} __packed; + +struct p54_sta_unlock { + u8 addr[ETH_ALEN]; + u16 padding; +} __packed; + +#define P54_TIM_CLEAR BIT(15) +struct p54_tim { + u8 count; + u8 padding[3]; + __le16 entry[8]; +} __packed; + +struct p54_cce_quiet { + __le32 period; +} __packed; + +struct p54_bt_balancer { + __le16 prio_thresh; + __le16 acl_thresh; +} __packed; + +struct p54_arp_table { + __le16 filter_enable; + u8 ipv4_addr[4]; +} __packed; + +/* LED control */ +int p54_set_leds(struct p54_common *priv); +int p54_init_leds(struct p54_common *priv); +void p54_unregister_leds(struct p54_common *priv); + +/* xmit functions */ +void p54_tx_80211(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb); +int p54_tx_cancel(struct p54_common *priv, __le32 req_id); +void p54_tx(struct p54_common *priv, struct sk_buff *skb); + +/* synth/phy configuration */ +int p54_init_xbow_synth(struct p54_common *priv); +int p54_scan(struct p54_common *priv, u16 mode, u16 dwell); + +/* MAC */ +int p54_sta_unlock(struct p54_common *priv, u8 *addr); +int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set); +int p54_setup_mac(struct p54_common *priv); +int p54_set_ps(struct p54_common *priv); +int p54_fetch_statistics(struct p54_common *priv); +int p54_set_groupfilter(struct p54_common *priv); + +/* e/v DCF setup */ +int p54_set_edcf(struct p54_common *priv); + +/* cryptographic engine */ +int p54_upload_key(struct p54_common *priv, u8 algo, int slot, + u8 idx, u8 len, u8 *addr, u8* key); + +/* eeprom */ +int p54_download_eeprom(struct p54_common *priv, void *buf, + u16 offset, u16 len); +struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *p, const u16 freq); + +/* utility */ +u8 *p54_find_ie(struct sk_buff *skb, u8 ie); + +#endif /* LMAC_H */ diff --git a/drivers/net/wireless/intersil/p54/main.c b/drivers/net/wireless/intersil/p54/main.c new file mode 100644 index 000000000000..7805864e76f9 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/main.c @@ -0,0 +1,867 @@ +/* + * mac80211 glue code for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2007-2009, Christian Lamparter + * Copyright 2008, Johannes Berg + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include + +#include "p54.h" +#include "lmac.h" + +static bool modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); +MODULE_AUTHOR("Michael Wu "); +MODULE_DESCRIPTION("Softmac Prism54 common code"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("prism54common"); + +static int p54_sta_add_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct p54_common *priv = hw->priv; + + /* + * Notify the firmware that we don't want or we don't + * need to buffer frames for this station anymore. + */ + + p54_sta_unlock(priv, sta->addr); + + return 0; +} + +static void p54_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) +{ + struct p54_common *priv = dev->priv; + + switch (notify_cmd) { + case STA_NOTIFY_AWAKE: + /* update the firmware's filter table */ + p54_sta_unlock(priv, sta->addr); + break; + default: + break; + } +} + +static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set) +{ + struct p54_common *priv = dev->priv; + + return p54_update_beacon_tim(priv, sta->aid, set); +} + +u8 *p54_find_ie(struct sk_buff *skb, u8 ie) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + u8 *pos, *end; + + if (skb->len <= sizeof(mgmt)) + return NULL; + + pos = (u8 *)mgmt->u.beacon.variable; + end = skb->data + skb->len; + while (pos < end) { + if (pos + 2 + pos[1] > end) + return NULL; + + if (pos[0] == ie) + return pos; + + pos += 2 + pos[1]; + } + return NULL; +} + +static int p54_beacon_format_ie_tim(struct sk_buff *skb) +{ + /* + * the good excuse for this mess is ... the firmware. + * The dummy TIM MUST be at the end of the beacon frame, + * because it'll be overwritten! + */ + u8 *tim; + u8 dtim_len; + u8 dtim_period; + u8 *next; + + tim = p54_find_ie(skb, WLAN_EID_TIM); + if (!tim) + return 0; + + dtim_len = tim[1]; + dtim_period = tim[3]; + next = tim + 2 + dtim_len; + + if (dtim_len < 3) + return -EINVAL; + + memmove(tim, next, skb_tail_pointer(skb) - next); + tim = skb_tail_pointer(skb) - (dtim_len + 2); + + /* add the dummy at the end */ + tim[0] = WLAN_EID_TIM; + tim[1] = 3; + tim[2] = 0; + tim[3] = dtim_period; + tim[4] = 0; + + if (dtim_len > 3) + skb_trim(skb, skb->len - (dtim_len - 3)); + + return 0; +} + +static int p54_beacon_update(struct p54_common *priv, + struct ieee80211_vif *vif) +{ + struct ieee80211_tx_control control = { }; + struct sk_buff *beacon; + int ret; + + beacon = ieee80211_beacon_get(priv->hw, vif); + if (!beacon) + return -ENOMEM; + ret = p54_beacon_format_ie_tim(beacon); + if (ret) + return ret; + + /* + * During operation, the firmware takes care of beaconing. + * The driver only needs to upload a new beacon template, once + * the template was changed by the stack or userspace. + * + * LMAC API 3.2.2 also specifies that the driver does not need + * to cancel the old beacon template by hand, instead the firmware + * will release the previous one through the feedback mechanism. + */ + p54_tx_80211(priv->hw, &control, beacon); + priv->tsf_high32 = 0; + priv->tsf_low32 = 0; + + return 0; +} + +static int p54_start(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + int err; + + mutex_lock(&priv->conf_mutex); + err = priv->open(dev); + if (err) + goto out; + P54_SET_QUEUE(priv->qos_params[0], 0x0002, 0x0003, 0x0007, 47); + P54_SET_QUEUE(priv->qos_params[1], 0x0002, 0x0007, 0x000f, 94); + P54_SET_QUEUE(priv->qos_params[2], 0x0003, 0x000f, 0x03ff, 0); + P54_SET_QUEUE(priv->qos_params[3], 0x0007, 0x000f, 0x03ff, 0); + err = p54_set_edcf(priv); + if (err) + goto out; + + eth_broadcast_addr(priv->bssid); + priv->mode = NL80211_IFTYPE_MONITOR; + err = p54_setup_mac(priv); + if (err) { + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + goto out; + } + + ieee80211_queue_delayed_work(dev, &priv->work, 0); + + priv->softled_state = 0; + err = p54_set_leds(priv); + +out: + mutex_unlock(&priv->conf_mutex); + return err; +} + +static void p54_stop(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + int i; + + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->softled_state = 0; + cancel_delayed_work_sync(&priv->work); + mutex_lock(&priv->conf_mutex); + p54_set_leds(priv); + priv->stop(dev); + skb_queue_purge(&priv->tx_pending); + skb_queue_purge(&priv->tx_queue); + for (i = 0; i < P54_QUEUE_NUM; i++) { + priv->tx_stats[i].count = 0; + priv->tx_stats[i].len = 0; + } + + priv->beacon_req_id = cpu_to_le32(0); + priv->tsf_high32 = priv->tsf_low32 = 0; + mutex_unlock(&priv->conf_mutex); +} + +static int p54_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct p54_common *priv = dev->priv; + int err; + + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + + mutex_lock(&priv->conf_mutex); + if (priv->mode != NL80211_IFTYPE_MONITOR) { + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + priv->vif = vif; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + priv->mode = vif->type; + break; + default: + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + memcpy(priv->mac_addr, vif->addr, ETH_ALEN); + err = p54_setup_mac(priv); + mutex_unlock(&priv->conf_mutex); + return err; +} + +static void p54_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct p54_common *priv = dev->priv; + + mutex_lock(&priv->conf_mutex); + priv->vif = NULL; + + /* + * LMAC API 3.2.2 states that any active beacon template must be + * canceled by the driver before attempting a mode transition. + */ + if (le32_to_cpu(priv->beacon_req_id) != 0) { + p54_tx_cancel(priv, priv->beacon_req_id); + wait_for_completion_interruptible_timeout(&priv->beacon_comp, HZ); + } + priv->mode = NL80211_IFTYPE_MONITOR; + eth_zero_addr(priv->mac_addr); + eth_zero_addr(priv->bssid); + p54_setup_mac(priv); + mutex_unlock(&priv->conf_mutex); +} + +static int p54_wait_for_stats(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + int ret; + + priv->update_stats = true; + ret = p54_fetch_statistics(priv); + if (ret) + return ret; + + ret = wait_for_completion_interruptible_timeout(&priv->stat_comp, HZ); + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + +static void p54_reset_stats(struct p54_common *priv) +{ + struct ieee80211_channel *chan = priv->curchan; + + if (chan) { + struct survey_info *info = &priv->survey[chan->hw_value]; + + /* only reset channel statistics, don't touch .filled, etc. */ + info->time = 0; + info->time_busy = 0; + info->time_tx = 0; + } + + priv->update_stats = true; + priv->survey_raw.active = 0; + priv->survey_raw.cca = 0; + priv->survey_raw.tx = 0; +} + +static int p54_config(struct ieee80211_hw *dev, u32 changed) +{ + int ret = 0; + struct p54_common *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + + mutex_lock(&priv->conf_mutex); + if (changed & IEEE80211_CONF_CHANGE_POWER) + priv->output_power = conf->power_level << 2; + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + struct ieee80211_channel *oldchan; + WARN_ON(p54_wait_for_stats(dev)); + oldchan = priv->curchan; + priv->curchan = NULL; + ret = p54_scan(priv, P54_SCAN_EXIT, 0); + if (ret) { + priv->curchan = oldchan; + goto out; + } + /* + * TODO: Use the LM_SCAN_TRAP to determine the current + * operating channel. + */ + priv->curchan = priv->hw->conf.chandef.chan; + p54_reset_stats(priv); + WARN_ON(p54_fetch_statistics(priv)); + } + if (changed & IEEE80211_CONF_CHANGE_PS) { + WARN_ON(p54_wait_for_stats(dev)); + ret = p54_set_ps(priv); + if (ret) + goto out; + WARN_ON(p54_wait_for_stats(dev)); + } + if (changed & IEEE80211_CONF_CHANGE_IDLE) { + WARN_ON(p54_wait_for_stats(dev)); + ret = p54_setup_mac(priv); + if (ret) + goto out; + WARN_ON(p54_wait_for_stats(dev)); + } + +out: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +static u64 p54_prepare_multicast(struct ieee80211_hw *dev, + struct netdev_hw_addr_list *mc_list) +{ + struct p54_common *priv = dev->priv; + struct netdev_hw_addr *ha; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(priv->mc_maclist) != + ARRAY_SIZE(((struct p54_group_address_table *)NULL)->mac_list)); + /* + * The first entry is reserved for the global broadcast MAC. + * Otherwise the firmware will drop it and ARP will no longer work. + */ + i = 1; + priv->mc_maclist_num = netdev_hw_addr_list_count(mc_list) + i; + netdev_hw_addr_list_for_each(ha, mc_list) { + memcpy(&priv->mc_maclist[i], ha->addr, ETH_ALEN); + i++; + if (i >= ARRAY_SIZE(priv->mc_maclist)) + break; + } + + return 1; /* update */ +} + +static void p54_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct p54_common *priv = dev->priv; + + *total_flags &= FIF_ALLMULTI | FIF_OTHER_BSS; + + priv->filter_flags = *total_flags; + + if (changed_flags & FIF_OTHER_BSS) + p54_setup_mac(priv); + + if (changed_flags & FIF_ALLMULTI || multicast) + p54_set_groupfilter(priv); +} + +static int p54_conf_tx(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct p54_common *priv = dev->priv; + int ret; + + mutex_lock(&priv->conf_mutex); + if (queue < dev->queues) { + P54_SET_QUEUE(priv->qos_params[queue], params->aifs, + params->cw_min, params->cw_max, params->txop); + ret = p54_set_edcf(priv); + } else + ret = -EINVAL; + mutex_unlock(&priv->conf_mutex); + return ret; +} + +static void p54_work(struct work_struct *work) +{ + struct p54_common *priv = container_of(work, struct p54_common, + work.work); + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) + return ; + + /* + * TODO: walk through tx_queue and do the following tasks + * 1. initiate bursts. + * 2. cancel stuck frames / reset the device if necessary. + */ + + mutex_lock(&priv->conf_mutex); + WARN_ON_ONCE(p54_fetch_statistics(priv)); + mutex_unlock(&priv->conf_mutex); +} + +static int p54_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct p54_common *priv = dev->priv; + + memcpy(stats, &priv->stats, sizeof(*stats)); + return 0; +} + +static void p54_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct p54_common *priv = dev->priv; + + mutex_lock(&priv->conf_mutex); + if (changed & BSS_CHANGED_BSSID) { + memcpy(priv->bssid, info->bssid, ETH_ALEN); + p54_setup_mac(priv); + } + + if (changed & BSS_CHANGED_BEACON) { + p54_scan(priv, P54_SCAN_EXIT, 0); + p54_setup_mac(priv); + p54_beacon_update(priv, vif); + p54_set_edcf(priv); + } + + if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BEACON)) { + priv->use_short_slot = info->use_short_slot; + p54_set_edcf(priv); + } + if (changed & BSS_CHANGED_BASIC_RATES) { + if (dev->conf.chandef.chan->band == IEEE80211_BAND_5GHZ) + priv->basic_rate_mask = (info->basic_rates << 4); + else + priv->basic_rate_mask = info->basic_rates; + p54_setup_mac(priv); + if (priv->fw_var >= 0x500) + p54_scan(priv, P54_SCAN_EXIT, 0); + } + if (changed & BSS_CHANGED_ASSOC) { + if (info->assoc) { + priv->aid = info->aid; + priv->wakeup_timer = info->beacon_int * + info->dtim_period * 5; + p54_setup_mac(priv); + } else { + priv->wakeup_timer = 500; + priv->aid = 0; + } + } + + mutex_unlock(&priv->conf_mutex); +} + +static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct p54_common *priv = dev->priv; + int slot, ret = 0; + u8 algo = 0; + u8 *addr = NULL; + + if (modparam_nohwcrypt) + return -EOPNOTSUPP; + + if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) { + /* + * Unfortunately most/all firmwares are trying to decrypt + * incoming management frames if a suitable key can be found. + * However, in doing so the data in these frames gets + * corrupted. So, we can't have firmware supported crypto + * offload in this case. + */ + return -EOPNOTSUPP; + } + + mutex_lock(&priv->conf_mutex); + if (cmd == SET_KEY) { + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL | + BR_DESC_PRIV_CAP_TKIP))) { + ret = -EOPNOTSUPP; + goto out_unlock; + } + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + algo = P54_CRYPTO_TKIPMICHAEL; + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) { + ret = -EOPNOTSUPP; + goto out_unlock; + } + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + algo = P54_CRYPTO_WEP; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) { + ret = -EOPNOTSUPP; + goto out_unlock; + } + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + algo = P54_CRYPTO_AESCCMP; + break; + default: + ret = -EOPNOTSUPP; + goto out_unlock; + } + slot = bitmap_find_free_region(priv->used_rxkeys, + priv->rx_keycache_size, 0); + + if (slot < 0) { + /* + * The device supports the chosen algorithm, but the + * firmware does not provide enough key slots to store + * all of them. + * But encryption offload for outgoing frames is always + * possible, so we just pretend that the upload was + * successful and do the decryption in software. + */ + + /* mark the key as invalid. */ + key->hw_key_idx = 0xff; + goto out_unlock; + } + + key->flags |= IEEE80211_KEY_FLAG_RESERVE_TAILROOM; + } else { + slot = key->hw_key_idx; + + if (slot == 0xff) { + /* This key was not uploaded into the rx key cache. */ + + goto out_unlock; + } + + bitmap_release_region(priv->used_rxkeys, slot, 0); + algo = 0; + } + + if (sta) + addr = sta->addr; + + ret = p54_upload_key(priv, algo, slot, key->keyidx, + key->keylen, addr, key->key); + if (ret) { + bitmap_release_region(priv->used_rxkeys, slot, 0); + ret = -EOPNOTSUPP; + goto out_unlock; + } + + key->hw_key_idx = slot; + +out_unlock: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +static int p54_get_survey(struct ieee80211_hw *dev, int idx, + struct survey_info *survey) +{ + struct p54_common *priv = dev->priv; + struct ieee80211_channel *chan; + int err, tries; + bool in_use = false; + + if (idx >= priv->chan_num) + return -ENOENT; + +#define MAX_TRIES 1 + for (tries = 0; tries < MAX_TRIES; tries++) { + chan = priv->curchan; + if (chan && chan->hw_value == idx) { + mutex_lock(&priv->conf_mutex); + err = p54_wait_for_stats(dev); + mutex_unlock(&priv->conf_mutex); + if (err) + return err; + + in_use = true; + } + + memcpy(survey, &priv->survey[idx], sizeof(*survey)); + + if (in_use) { + /* test if the reported statistics are valid. */ + if (survey->time != 0) { + survey->filled |= SURVEY_INFO_IN_USE; + } else { + /* + * hw/fw has not accumulated enough sample sets. + * Wait for 100ms, this ought to be enough to + * to get at least one non-null set of channel + * usage statistics. + */ + msleep(100); + continue; + } + } + return 0; + } + return -ETIMEDOUT; +#undef MAX_TRIES +} + +static unsigned int p54_flush_count(struct p54_common *priv) +{ + unsigned int total = 0, i; + + BUILD_BUG_ON(P54_QUEUE_NUM > ARRAY_SIZE(priv->tx_stats)); + + /* + * Because the firmware has the sole control over any frames + * in the P54_QUEUE_BEACON or P54_QUEUE_SCAN queues, they + * don't really count as pending or active. + */ + for (i = P54_QUEUE_MGMT; i < P54_QUEUE_NUM; i++) + total += priv->tx_stats[i].len; + return total; +} + +static void p54_flush(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct p54_common *priv = dev->priv; + unsigned int total, i; + + /* + * Currently, it wouldn't really matter if we wait for one second + * or 15 minutes. But once someone gets around and completes the + * TODOs [ancel stuck frames / reset device] in p54_work, it will + * suddenly make sense to wait that long. + */ + i = P54_STATISTICS_UPDATE * 2 / 20; + + /* + * In this case no locking is required because as we speak the + * queues have already been stopped and no new frames can sneak + * up from behind. + */ + while ((total = p54_flush_count(priv) && i--)) { + /* waste time */ + msleep(20); + } + + WARN(total, "tx flush timeout, unresponsive firmware"); +} + +static void p54_set_coverage_class(struct ieee80211_hw *dev, + s16 coverage_class) +{ + struct p54_common *priv = dev->priv; + + mutex_lock(&priv->conf_mutex); + /* support all coverage class values as in 802.11-2007 Table 7-27 */ + priv->coverage_class = clamp_t(u8, coverage_class, 0, 31); + p54_set_edcf(priv); + mutex_unlock(&priv->conf_mutex); +} + +static const struct ieee80211_ops p54_ops = { + .tx = p54_tx_80211, + .start = p54_start, + .stop = p54_stop, + .add_interface = p54_add_interface, + .remove_interface = p54_remove_interface, + .set_tim = p54_set_tim, + .sta_notify = p54_sta_notify, + .sta_add = p54_sta_add_remove, + .sta_remove = p54_sta_add_remove, + .set_key = p54_set_key, + .config = p54_config, + .flush = p54_flush, + .bss_info_changed = p54_bss_info_changed, + .prepare_multicast = p54_prepare_multicast, + .configure_filter = p54_configure_filter, + .conf_tx = p54_conf_tx, + .get_stats = p54_get_stats, + .get_survey = p54_get_survey, + .set_coverage_class = p54_set_coverage_class, +}; + +struct ieee80211_hw *p54_init_common(size_t priv_data_len) +{ + struct ieee80211_hw *dev; + struct p54_common *priv; + + dev = ieee80211_alloc_hw(priv_data_len, &p54_ops); + if (!dev) + return NULL; + + priv = dev->priv; + priv->hw = dev; + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->basic_rate_mask = 0x15f; + spin_lock_init(&priv->tx_stats_lock); + skb_queue_head_init(&priv->tx_queue); + skb_queue_head_init(&priv->tx_pending); + ieee80211_hw_set(dev, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(dev, MFP_CAPABLE); + ieee80211_hw_set(dev, PS_NULLFUNC_STACK); + ieee80211_hw_set(dev, SUPPORTS_PS); + ieee80211_hw_set(dev, RX_INCLUDES_FCS); + ieee80211_hw_set(dev, SIGNAL_DBM); + + dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT); + + priv->beacon_req_id = cpu_to_le32(0); + priv->tx_stats[P54_QUEUE_BEACON].limit = 1; + priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1; + priv->tx_stats[P54_QUEUE_MGMT].limit = 3; + priv->tx_stats[P54_QUEUE_CAB].limit = 3; + priv->tx_stats[P54_QUEUE_DATA].limit = 5; + dev->queues = 1; + priv->noise = -94; + /* + * We support at most 8 tries no matter which rate they're at, + * we cannot support max_rates * max_rate_tries as we set it + * here, but setting it correctly to 4/2 or so would limit us + * artificially if the RC algorithm wants just two rates, so + * let's say 4/7, we'll redistribute it at TX time, see the + * comments there. + */ + dev->max_rates = 4; + dev->max_rate_tries = 7; + dev->extra_tx_headroom = sizeof(struct p54_hdr) + 4 + + sizeof(struct p54_tx_data); + + /* + * For now, disable PS by default because it affects + * link stability significantly. + */ + dev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + mutex_init(&priv->conf_mutex); + mutex_init(&priv->eeprom_mutex); + init_completion(&priv->stat_comp); + init_completion(&priv->eeprom_comp); + init_completion(&priv->beacon_comp); + INIT_DELAYED_WORK(&priv->work, p54_work); + + eth_broadcast_addr(priv->mc_maclist[0]); + priv->curchan = NULL; + p54_reset_stats(priv); + return dev; +} +EXPORT_SYMBOL_GPL(p54_init_common); + +int p54_register_common(struct ieee80211_hw *dev, struct device *pdev) +{ + struct p54_common __maybe_unused *priv = dev->priv; + int err; + + err = ieee80211_register_hw(dev); + if (err) { + dev_err(pdev, "Cannot register device (%d).\n", err); + return err; + } + priv->registered = true; + +#ifdef CONFIG_P54_LEDS + err = p54_init_leds(priv); + if (err) { + p54_unregister_common(dev); + return err; + } +#endif /* CONFIG_P54_LEDS */ + + dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy)); + return 0; +} +EXPORT_SYMBOL_GPL(p54_register_common); + +void p54_free_common(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + unsigned int i; + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + kfree(priv->band_table[i]); + + kfree(priv->iq_autocal); + kfree(priv->output_limit); + kfree(priv->curve_data); + kfree(priv->rssi_db); + kfree(priv->used_rxkeys); + kfree(priv->survey); + priv->iq_autocal = NULL; + priv->output_limit = NULL; + priv->curve_data = NULL; + priv->rssi_db = NULL; + priv->used_rxkeys = NULL; + priv->survey = NULL; + ieee80211_free_hw(dev); +} +EXPORT_SYMBOL_GPL(p54_free_common); + +void p54_unregister_common(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + +#ifdef CONFIG_P54_LEDS + p54_unregister_leds(priv); +#endif /* CONFIG_P54_LEDS */ + + if (priv->registered) { + priv->registered = false; + ieee80211_unregister_hw(dev); + } + + mutex_destroy(&priv->conf_mutex); + mutex_destroy(&priv->eeprom_mutex); +} +EXPORT_SYMBOL_GPL(p54_unregister_common); diff --git a/drivers/net/wireless/intersil/p54/p54.h b/drivers/net/wireless/intersil/p54/p54.h new file mode 100644 index 000000000000..40b401ed6845 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/p54.h @@ -0,0 +1,281 @@ +/* + * Shared defines for all mac80211 Prism54 code + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef P54_H +#define P54_H + +#ifdef CONFIG_P54_LEDS +#include +#endif /* CONFIG_P54_LEDS */ + +#define ISL38XX_DEV_FIRMWARE_ADDR 0x20000 + +#define BR_CODE_MIN 0x80000000 +#define BR_CODE_COMPONENT_ID 0x80000001 +#define BR_CODE_COMPONENT_VERSION 0x80000002 +#define BR_CODE_DEPENDENT_IF 0x80000003 +#define BR_CODE_EXPOSED_IF 0x80000004 +#define BR_CODE_DESCR 0x80000101 +#define BR_CODE_MAX 0x8FFFFFFF +#define BR_CODE_END_OF_BRA 0xFF0000FF +#define LEGACY_BR_CODE_END_OF_BRA 0xFFFFFFFF + +struct bootrec { + __le32 code; + __le32 len; + u32 data[10]; +} __packed; + +/* Interface role definitions */ +#define BR_INTERFACE_ROLE_SERVER 0x0000 +#define BR_INTERFACE_ROLE_CLIENT 0x8000 + +#define BR_DESC_PRIV_CAP_WEP BIT(0) +#define BR_DESC_PRIV_CAP_TKIP BIT(1) +#define BR_DESC_PRIV_CAP_MICHAEL BIT(2) +#define BR_DESC_PRIV_CAP_CCX_CP BIT(3) +#define BR_DESC_PRIV_CAP_CCX_MIC BIT(4) +#define BR_DESC_PRIV_CAP_AESCCMP BIT(5) + +struct bootrec_desc { + __le16 modes; + __le16 flags; + __le32 rx_start; + __le32 rx_end; + u8 headroom; + u8 tailroom; + u8 tx_queues; + u8 tx_depth; + u8 privacy_caps; + u8 rx_keycache_size; + u8 time_size; + u8 padding; + u8 rates[16]; + u8 padding2[4]; + __le16 rx_mtu; +} __packed; + +#define FW_FMAC 0x464d4143 +#define FW_LM86 0x4c4d3836 +#define FW_LM87 0x4c4d3837 +#define FW_LM20 0x4c4d3230 + +struct bootrec_comp_id { + __le32 fw_variant; +} __packed; + +struct bootrec_comp_ver { + char fw_version[24]; +} __packed; + +struct bootrec_end { + __le16 crc; + u8 padding[2]; + u8 md5[16]; +} __packed; + +/* provide 16 bytes for the transport back-end */ +#define P54_TX_INFO_DATA_SIZE 16 + +/* stored in ieee80211_tx_info's rate_driver_data */ +struct p54_tx_info { + u32 start_addr; + u32 end_addr; + union { + void *data[P54_TX_INFO_DATA_SIZE / sizeof(void *)]; + struct { + u32 extra_len; + }; + }; +}; + +#define P54_MAX_CTRL_FRAME_LEN 0x1000 + +#define P54_SET_QUEUE(queue, ai_fs, cw_min, cw_max, _txop) \ +do { \ + queue.aifs = cpu_to_le16(ai_fs); \ + queue.cwmin = cpu_to_le16(cw_min); \ + queue.cwmax = cpu_to_le16(cw_max); \ + queue.txop = cpu_to_le16(_txop); \ +} while (0) + +struct p54_edcf_queue_param { + __le16 aifs; + __le16 cwmin; + __le16 cwmax; + __le16 txop; +} __packed; + +struct p54_rssi_db_entry { + u16 freq; + s16 mul; + s16 add; + s16 longbow_unkn; + s16 longbow_unk2; +}; + +struct p54_cal_database { + size_t entries; + size_t entry_size; + size_t offset; + size_t len; + u8 data[0]; +}; + +#define EEPROM_READBACK_LEN 0x3fc + +enum fw_state { + FW_STATE_OFF, + FW_STATE_BOOTING, + FW_STATE_READY, + FW_STATE_RESET, + FW_STATE_RESETTING, +}; + +#ifdef CONFIG_P54_LEDS + +#define P54_LED_MAX_NAME_LEN 31 + +struct p54_led_dev { + struct ieee80211_hw *hw_dev; + struct led_classdev led_dev; + char name[P54_LED_MAX_NAME_LEN + 1]; + + unsigned int toggled; + unsigned int index; + unsigned int registered; +}; + +#endif /* CONFIG_P54_LEDS */ + +struct p54_tx_queue_stats { + unsigned int len; + unsigned int limit; + unsigned int count; +}; + +struct p54_common { + struct ieee80211_hw *hw; + struct ieee80211_vif *vif; + void (*tx)(struct ieee80211_hw *dev, struct sk_buff *skb); + int (*open)(struct ieee80211_hw *dev); + void (*stop)(struct ieee80211_hw *dev); + struct sk_buff_head tx_pending; + struct sk_buff_head tx_queue; + struct mutex conf_mutex; + bool registered; + + /* memory management (as seen by the firmware) */ + u32 rx_start; + u32 rx_end; + u16 rx_mtu; + u8 headroom; + u8 tailroom; + + /* firmware/hardware info */ + unsigned int tx_hdr_len; + unsigned int fw_var; + unsigned int fw_interface; + u8 version; + + /* (e)DCF / QOS state */ + bool use_short_slot; + spinlock_t tx_stats_lock; + struct p54_tx_queue_stats tx_stats[8]; + struct p54_edcf_queue_param qos_params[8]; + + /* Radio data */ + u16 rxhw; + u8 rx_diversity_mask; + u8 tx_diversity_mask; + unsigned int output_power; + struct p54_rssi_db_entry *cur_rssi; + struct ieee80211_channel *curchan; + struct survey_info *survey; + unsigned int chan_num; + struct completion stat_comp; + bool update_stats; + struct { + unsigned int timestamp; + unsigned int cached_cca; + unsigned int cached_tx; + unsigned int cached_rssi; + u64 active; + u64 cca; + u64 tx; + u64 rssi; + } survey_raw; + + int noise; + /* calibration, output power limit and rssi<->dBm conversation data */ + struct pda_iq_autocal_entry *iq_autocal; + unsigned int iq_autocal_len; + struct p54_cal_database *curve_data; + struct p54_cal_database *output_limit; + struct p54_cal_database *rssi_db; + struct ieee80211_supported_band *band_table[IEEE80211_NUM_BANDS]; + + /* BBP/MAC state */ + u8 mac_addr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u8 mc_maclist[4][ETH_ALEN]; + u16 wakeup_timer; + unsigned int filter_flags; + int mc_maclist_num; + int mode; + u32 tsf_low32, tsf_high32; + u32 basic_rate_mask; + u16 aid; + u8 coverage_class; + bool phy_idle; + bool phy_ps; + bool powersave_override; + __le32 beacon_req_id; + struct completion beacon_comp; + + /* cryptographic engine information */ + u8 privacy_caps; + u8 rx_keycache_size; + unsigned long *used_rxkeys; + + /* LED management */ +#ifdef CONFIG_P54_LEDS + struct p54_led_dev leds[4]; + struct delayed_work led_work; +#endif /* CONFIG_P54_LEDS */ + u16 softled_state; /* bit field of glowing LEDs */ + + /* statistics */ + struct ieee80211_low_level_stats stats; + struct delayed_work work; + + /* eeprom handling */ + void *eeprom; + struct completion eeprom_comp; + struct mutex eeprom_mutex; +}; + +/* interfaces for the drivers */ +int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb); +void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb); +int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw); +int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len); +int p54_read_eeprom(struct ieee80211_hw *dev); + +struct ieee80211_hw *p54_init_common(size_t priv_data_len); +int p54_register_common(struct ieee80211_hw *dev, struct device *pdev); +void p54_free_common(struct ieee80211_hw *dev); + +void p54_unregister_common(struct ieee80211_hw *dev); + +#endif /* P54_H */ diff --git a/drivers/net/wireless/intersil/p54/p54pci.c b/drivers/net/wireless/intersil/p54/p54pci.c new file mode 100644 index 000000000000..27a49068d32d --- /dev/null +++ b/drivers/net/wireless/intersil/p54/p54pci.c @@ -0,0 +1,703 @@ + +/* + * Linux device driver for PCI based Prism54 + * + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2008, Christian Lamparter + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "p54.h" +#include "lmac.h" +#include "p54pci.h" + +MODULE_AUTHOR("Michael Wu "); +MODULE_DESCRIPTION("Prism54 PCI wireless driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("prism54pci"); +MODULE_FIRMWARE("isl3886pci"); + +static const struct pci_device_id p54p_table[] = { + /* Intersil PRISM Duette/Prism GT Wireless LAN adapter */ + { PCI_DEVICE(0x1260, 0x3890) }, + /* 3COM 3CRWE154G72 Wireless LAN adapter */ + { PCI_DEVICE(0x10b7, 0x6001) }, + /* Intersil PRISM Indigo Wireless LAN adapter */ + { PCI_DEVICE(0x1260, 0x3877) }, + /* Intersil PRISM Javelin/Xbow Wireless LAN adapter */ + { PCI_DEVICE(0x1260, 0x3886) }, + /* Intersil PRISM Xbow Wireless LAN adapter (Symbol AP-300) */ + { PCI_DEVICE(0x1260, 0xffff) }, + { }, +}; + +MODULE_DEVICE_TABLE(pci, p54p_table); + +static int p54p_upload_firmware(struct ieee80211_hw *dev) +{ + struct p54p_priv *priv = dev->priv; + __le32 reg; + int err; + __le32 *data; + u32 remains, left, device_addr; + + P54P_WRITE(int_enable, cpu_to_le32(0)); + P54P_READ(int_enable); + udelay(10); + + reg = P54P_READ(ctrl_stat); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); + P54P_WRITE(ctrl_stat, reg); + P54P_READ(ctrl_stat); + udelay(10); + + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); + P54P_WRITE(ctrl_stat, reg); + wmb(); + udelay(10); + + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + P54P_WRITE(ctrl_stat, reg); + wmb(); + + /* wait for the firmware to reset properly */ + mdelay(10); + + err = p54_parse_firmware(dev, priv->firmware); + if (err) + return err; + + if (priv->common.fw_interface != FW_LM86) { + dev_err(&priv->pdev->dev, "wrong firmware, " + "please get a LM86(PCI) firmware a try again.\n"); + return -EINVAL; + } + + data = (__le32 *) priv->firmware->data; + remains = priv->firmware->size; + device_addr = ISL38XX_DEV_FIRMWARE_ADDR; + while (remains) { + u32 i = 0; + left = min((u32)0x1000, remains); + P54P_WRITE(direct_mem_base, cpu_to_le32(device_addr)); + P54P_READ(int_enable); + + device_addr += 0x1000; + while (i < left) { + P54P_WRITE(direct_mem_win[i], *data++); + i += sizeof(u32); + } + + remains -= left; + P54P_READ(int_enable); + } + + reg = P54P_READ(ctrl_stat); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); + P54P_WRITE(ctrl_stat, reg); + P54P_READ(ctrl_stat); + udelay(10); + + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); + P54P_WRITE(ctrl_stat, reg); + wmb(); + udelay(10); + + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + P54P_WRITE(ctrl_stat, reg); + wmb(); + udelay(10); + + /* wait for the firmware to boot properly */ + mdelay(100); + + return 0; +} + +static void p54p_refill_rx_ring(struct ieee80211_hw *dev, + int ring_index, struct p54p_desc *ring, u32 ring_limit, + struct sk_buff **rx_buf, u32 index) +{ + struct p54p_priv *priv = dev->priv; + struct p54p_ring_control *ring_control = priv->ring_control; + u32 limit, idx, i; + + idx = le32_to_cpu(ring_control->host_idx[ring_index]); + limit = idx; + limit -= index; + limit = ring_limit - limit; + + i = idx % ring_limit; + while (limit-- > 1) { + struct p54p_desc *desc = &ring[i]; + + if (!desc->host_addr) { + struct sk_buff *skb; + dma_addr_t mapping; + skb = dev_alloc_skb(priv->common.rx_mtu + 32); + if (!skb) + break; + + mapping = pci_map_single(priv->pdev, + skb_tail_pointer(skb), + priv->common.rx_mtu + 32, + PCI_DMA_FROMDEVICE); + + if (pci_dma_mapping_error(priv->pdev, mapping)) { + dev_kfree_skb_any(skb); + dev_err(&priv->pdev->dev, + "RX DMA Mapping error\n"); + break; + } + + desc->host_addr = cpu_to_le32(mapping); + desc->device_addr = 0; // FIXME: necessary? + desc->len = cpu_to_le16(priv->common.rx_mtu + 32); + desc->flags = 0; + rx_buf[i] = skb; + } + + i++; + idx++; + i %= ring_limit; + } + + wmb(); + ring_control->host_idx[ring_index] = cpu_to_le32(idx); +} + +static void p54p_check_rx_ring(struct ieee80211_hw *dev, u32 *index, + int ring_index, struct p54p_desc *ring, u32 ring_limit, + struct sk_buff **rx_buf) +{ + struct p54p_priv *priv = dev->priv; + struct p54p_ring_control *ring_control = priv->ring_control; + struct p54p_desc *desc; + u32 idx, i; + + i = (*index) % ring_limit; + (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]); + idx %= ring_limit; + while (i != idx) { + u16 len; + struct sk_buff *skb; + dma_addr_t dma_addr; + desc = &ring[i]; + len = le16_to_cpu(desc->len); + skb = rx_buf[i]; + + if (!skb) { + i++; + i %= ring_limit; + continue; + } + + if (unlikely(len > priv->common.rx_mtu)) { + if (net_ratelimit()) + dev_err(&priv->pdev->dev, "rx'd frame size " + "exceeds length threshold.\n"); + + len = priv->common.rx_mtu; + } + dma_addr = le32_to_cpu(desc->host_addr); + pci_dma_sync_single_for_cpu(priv->pdev, dma_addr, + priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); + skb_put(skb, len); + + if (p54_rx(dev, skb)) { + pci_unmap_single(priv->pdev, dma_addr, + priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); + rx_buf[i] = NULL; + desc->host_addr = cpu_to_le32(0); + } else { + skb_trim(skb, 0); + pci_dma_sync_single_for_device(priv->pdev, dma_addr, + priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); + desc->len = cpu_to_le16(priv->common.rx_mtu + 32); + } + + i++; + i %= ring_limit; + } + + p54p_refill_rx_ring(dev, ring_index, ring, ring_limit, rx_buf, *index); +} + +static void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index, + int ring_index, struct p54p_desc *ring, u32 ring_limit, + struct sk_buff **tx_buf) +{ + struct p54p_priv *priv = dev->priv; + struct p54p_ring_control *ring_control = priv->ring_control; + struct p54p_desc *desc; + struct sk_buff *skb; + u32 idx, i; + + i = (*index) % ring_limit; + (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]); + idx %= ring_limit; + + while (i != idx) { + desc = &ring[i]; + + skb = tx_buf[i]; + tx_buf[i] = NULL; + + pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr), + le16_to_cpu(desc->len), PCI_DMA_TODEVICE); + + desc->host_addr = 0; + desc->device_addr = 0; + desc->len = 0; + desc->flags = 0; + + if (skb && FREE_AFTER_TX(skb)) + p54_free_skb(dev, skb); + + i++; + i %= ring_limit; + } +} + +static void p54p_tasklet(unsigned long dev_id) +{ + struct ieee80211_hw *dev = (struct ieee80211_hw *)dev_id; + struct p54p_priv *priv = dev->priv; + struct p54p_ring_control *ring_control = priv->ring_control; + + p54p_check_tx_ring(dev, &priv->tx_idx_mgmt, 3, ring_control->tx_mgmt, + ARRAY_SIZE(ring_control->tx_mgmt), + priv->tx_buf_mgmt); + + p54p_check_tx_ring(dev, &priv->tx_idx_data, 1, ring_control->tx_data, + ARRAY_SIZE(ring_control->tx_data), + priv->tx_buf_data); + + p54p_check_rx_ring(dev, &priv->rx_idx_mgmt, 2, ring_control->rx_mgmt, + ARRAY_SIZE(ring_control->rx_mgmt), priv->rx_buf_mgmt); + + p54p_check_rx_ring(dev, &priv->rx_idx_data, 0, ring_control->rx_data, + ARRAY_SIZE(ring_control->rx_data), priv->rx_buf_data); + + wmb(); + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); +} + +static irqreturn_t p54p_interrupt(int irq, void *dev_id) +{ + struct ieee80211_hw *dev = dev_id; + struct p54p_priv *priv = dev->priv; + __le32 reg; + + reg = P54P_READ(int_ident); + if (unlikely(reg == cpu_to_le32(0xFFFFFFFF))) { + goto out; + } + P54P_WRITE(int_ack, reg); + + reg &= P54P_READ(int_enable); + + if (reg & cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)) + tasklet_schedule(&priv->tasklet); + else if (reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)) + complete(&priv->boot_comp); + +out: + return reg ? IRQ_HANDLED : IRQ_NONE; +} + +static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + unsigned long flags; + struct p54p_priv *priv = dev->priv; + struct p54p_ring_control *ring_control = priv->ring_control; + struct p54p_desc *desc; + dma_addr_t mapping; + u32 idx, i; + + spin_lock_irqsave(&priv->lock, flags); + idx = le32_to_cpu(ring_control->host_idx[1]); + i = idx % ARRAY_SIZE(ring_control->tx_data); + + mapping = pci_map_single(priv->pdev, skb->data, skb->len, + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(priv->pdev, mapping)) { + spin_unlock_irqrestore(&priv->lock, flags); + p54_free_skb(dev, skb); + dev_err(&priv->pdev->dev, "TX DMA mapping error\n"); + return ; + } + priv->tx_buf_data[i] = skb; + + desc = &ring_control->tx_data[i]; + desc->host_addr = cpu_to_le32(mapping); + desc->device_addr = ((struct p54_hdr *)skb->data)->req_id; + desc->len = cpu_to_le16(skb->len); + desc->flags = 0; + + wmb(); + ring_control->host_idx[1] = cpu_to_le32(idx + 1); + spin_unlock_irqrestore(&priv->lock, flags); + + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); + P54P_READ(dev_int); +} + +static void p54p_stop(struct ieee80211_hw *dev) +{ + struct p54p_priv *priv = dev->priv; + struct p54p_ring_control *ring_control = priv->ring_control; + unsigned int i; + struct p54p_desc *desc; + + P54P_WRITE(int_enable, cpu_to_le32(0)); + P54P_READ(int_enable); + udelay(10); + + free_irq(priv->pdev->irq, dev); + + tasklet_kill(&priv->tasklet); + + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); + + for (i = 0; i < ARRAY_SIZE(priv->rx_buf_data); i++) { + desc = &ring_control->rx_data[i]; + if (desc->host_addr) + pci_unmap_single(priv->pdev, + le32_to_cpu(desc->host_addr), + priv->common.rx_mtu + 32, + PCI_DMA_FROMDEVICE); + kfree_skb(priv->rx_buf_data[i]); + priv->rx_buf_data[i] = NULL; + } + + for (i = 0; i < ARRAY_SIZE(priv->rx_buf_mgmt); i++) { + desc = &ring_control->rx_mgmt[i]; + if (desc->host_addr) + pci_unmap_single(priv->pdev, + le32_to_cpu(desc->host_addr), + priv->common.rx_mtu + 32, + PCI_DMA_FROMDEVICE); + kfree_skb(priv->rx_buf_mgmt[i]); + priv->rx_buf_mgmt[i] = NULL; + } + + for (i = 0; i < ARRAY_SIZE(priv->tx_buf_data); i++) { + desc = &ring_control->tx_data[i]; + if (desc->host_addr) + pci_unmap_single(priv->pdev, + le32_to_cpu(desc->host_addr), + le16_to_cpu(desc->len), + PCI_DMA_TODEVICE); + + p54_free_skb(dev, priv->tx_buf_data[i]); + priv->tx_buf_data[i] = NULL; + } + + for (i = 0; i < ARRAY_SIZE(priv->tx_buf_mgmt); i++) { + desc = &ring_control->tx_mgmt[i]; + if (desc->host_addr) + pci_unmap_single(priv->pdev, + le32_to_cpu(desc->host_addr), + le16_to_cpu(desc->len), + PCI_DMA_TODEVICE); + + p54_free_skb(dev, priv->tx_buf_mgmt[i]); + priv->tx_buf_mgmt[i] = NULL; + } + + memset(ring_control, 0, sizeof(*ring_control)); +} + +static int p54p_open(struct ieee80211_hw *dev) +{ + struct p54p_priv *priv = dev->priv; + int err; + long timeout; + + init_completion(&priv->boot_comp); + err = request_irq(priv->pdev->irq, p54p_interrupt, + IRQF_SHARED, "p54pci", dev); + if (err) { + dev_err(&priv->pdev->dev, "failed to register IRQ handler\n"); + return err; + } + + memset(priv->ring_control, 0, sizeof(*priv->ring_control)); + err = p54p_upload_firmware(dev); + if (err) { + free_irq(priv->pdev->irq, dev); + return err; + } + priv->rx_idx_data = priv->tx_idx_data = 0; + priv->rx_idx_mgmt = priv->tx_idx_mgmt = 0; + + p54p_refill_rx_ring(dev, 0, priv->ring_control->rx_data, + ARRAY_SIZE(priv->ring_control->rx_data), priv->rx_buf_data, 0); + + p54p_refill_rx_ring(dev, 2, priv->ring_control->rx_mgmt, + ARRAY_SIZE(priv->ring_control->rx_mgmt), priv->rx_buf_mgmt, 0); + + P54P_WRITE(ring_control_base, cpu_to_le32(priv->ring_control_dma)); + P54P_READ(ring_control_base); + wmb(); + udelay(10); + + P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_INIT)); + P54P_READ(int_enable); + wmb(); + udelay(10); + + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); + P54P_READ(dev_int); + + timeout = wait_for_completion_interruptible_timeout( + &priv->boot_comp, HZ); + if (timeout <= 0) { + wiphy_err(dev->wiphy, "Cannot boot firmware!\n"); + p54p_stop(dev); + return timeout ? -ERESTARTSYS : -ETIMEDOUT; + } + + P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)); + P54P_READ(int_enable); + wmb(); + udelay(10); + + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); + P54P_READ(dev_int); + wmb(); + udelay(10); + + return 0; +} + +static void p54p_firmware_step2(const struct firmware *fw, + void *context) +{ + struct p54p_priv *priv = context; + struct ieee80211_hw *dev = priv->common.hw; + struct pci_dev *pdev = priv->pdev; + int err; + + if (!fw) { + dev_err(&pdev->dev, "Cannot find firmware (isl3886pci)\n"); + err = -ENOENT; + goto out; + } + + priv->firmware = fw; + + err = p54p_open(dev); + if (err) + goto out; + err = p54_read_eeprom(dev); + p54p_stop(dev); + if (err) + goto out; + + err = p54_register_common(dev, &pdev->dev); + if (err) + goto out; + +out: + + complete(&priv->fw_loaded); + + if (err) { + struct device *parent = pdev->dev.parent; + + if (parent) + device_lock(parent); + + /* + * This will indirectly result in a call to p54p_remove. + * Hence, we don't need to bother with freeing any + * allocated ressources at all. + */ + device_release_driver(&pdev->dev); + + if (parent) + device_unlock(parent); + } + + pci_dev_put(pdev); +} + +static int p54p_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct p54p_priv *priv; + struct ieee80211_hw *dev; + unsigned long mem_addr, mem_len; + int err; + + pci_dev_get(pdev); + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Cannot enable new PCI device\n"); + return err; + } + + mem_addr = pci_resource_start(pdev, 0); + mem_len = pci_resource_len(pdev, 0); + if (mem_len < sizeof(struct p54p_csr)) { + dev_err(&pdev->dev, "Too short PCI resources\n"); + err = -ENODEV; + goto err_disable_dev; + } + + err = pci_request_regions(pdev, "p54pci"); + if (err) { + dev_err(&pdev->dev, "Cannot obtain PCI resources\n"); + goto err_disable_dev; + } + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (!err) + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&pdev->dev, "No suitable DMA available\n"); + goto err_free_reg; + } + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + pci_write_config_byte(pdev, 0x40, 0); + pci_write_config_byte(pdev, 0x41, 0); + + dev = p54_init_common(sizeof(*priv)); + if (!dev) { + dev_err(&pdev->dev, "ieee80211 alloc failed\n"); + err = -ENOMEM; + goto err_free_reg; + } + + priv = dev->priv; + priv->pdev = pdev; + + init_completion(&priv->fw_loaded); + SET_IEEE80211_DEV(dev, &pdev->dev); + pci_set_drvdata(pdev, dev); + + priv->map = ioremap(mem_addr, mem_len); + if (!priv->map) { + dev_err(&pdev->dev, "Cannot map device memory\n"); + err = -ENOMEM; + goto err_free_dev; + } + + priv->ring_control = pci_alloc_consistent(pdev, sizeof(*priv->ring_control), + &priv->ring_control_dma); + if (!priv->ring_control) { + dev_err(&pdev->dev, "Cannot allocate rings\n"); + err = -ENOMEM; + goto err_iounmap; + } + priv->common.open = p54p_open; + priv->common.stop = p54p_stop; + priv->common.tx = p54p_tx; + + spin_lock_init(&priv->lock); + tasklet_init(&priv->tasklet, p54p_tasklet, (unsigned long)dev); + + err = request_firmware_nowait(THIS_MODULE, 1, "isl3886pci", + &priv->pdev->dev, GFP_KERNEL, + priv, p54p_firmware_step2); + if (!err) + return 0; + + pci_free_consistent(pdev, sizeof(*priv->ring_control), + priv->ring_control, priv->ring_control_dma); + + err_iounmap: + iounmap(priv->map); + + err_free_dev: + p54_free_common(dev); + + err_free_reg: + pci_release_regions(pdev); + err_disable_dev: + pci_disable_device(pdev); + pci_dev_put(pdev); + return err; +} + +static void p54p_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + struct p54p_priv *priv; + + if (!dev) + return; + + priv = dev->priv; + wait_for_completion(&priv->fw_loaded); + p54_unregister_common(dev); + release_firmware(priv->firmware); + pci_free_consistent(pdev, sizeof(*priv->ring_control), + priv->ring_control, priv->ring_control_dma); + iounmap(priv->map); + pci_release_regions(pdev); + pci_disable_device(pdev); + p54_free_common(dev); +} + +#ifdef CONFIG_PM_SLEEP +static int p54p_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + + pci_save_state(pdev); + pci_set_power_state(pdev, PCI_D3hot); + pci_disable_device(pdev); + return 0; +} + +static int p54p_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + int err; + + err = pci_reenable_device(pdev); + if (err) + return err; + return pci_set_power_state(pdev, PCI_D0); +} + +static SIMPLE_DEV_PM_OPS(p54pci_pm_ops, p54p_suspend, p54p_resume); + +#define P54P_PM_OPS (&p54pci_pm_ops) +#else +#define P54P_PM_OPS (NULL) +#endif /* CONFIG_PM_SLEEP */ + +static struct pci_driver p54p_driver = { + .name = "p54pci", + .id_table = p54p_table, + .probe = p54p_probe, + .remove = p54p_remove, + .driver.pm = P54P_PM_OPS, +}; + +module_pci_driver(p54p_driver); diff --git a/drivers/net/wireless/intersil/p54/p54pci.h b/drivers/net/wireless/intersil/p54/p54pci.h new file mode 100644 index 000000000000..68405c142f97 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/p54pci.h @@ -0,0 +1,112 @@ +#ifndef P54PCI_H +#define P54PCI_H +#include + +/* + * Defines for PCI based mac80211 Prism54 driver + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Device Interrupt register bits */ +#define ISL38XX_DEV_INT_RESET 0x0001 +#define ISL38XX_DEV_INT_UPDATE 0x0002 +#define ISL38XX_DEV_INT_WAKEUP 0x0008 +#define ISL38XX_DEV_INT_SLEEP 0x0010 +#define ISL38XX_DEV_INT_ABORT 0x0020 +/* these two only used in USB */ +#define ISL38XX_DEV_INT_DATA 0x0040 +#define ISL38XX_DEV_INT_MGMT 0x0080 + +#define ISL38XX_DEV_INT_PCIUART_CTS 0x4000 +#define ISL38XX_DEV_INT_PCIUART_DR 0x8000 + +/* Interrupt Identification/Acknowledge/Enable register bits */ +#define ISL38XX_INT_IDENT_UPDATE 0x0002 +#define ISL38XX_INT_IDENT_INIT 0x0004 +#define ISL38XX_INT_IDENT_WAKEUP 0x0008 +#define ISL38XX_INT_IDENT_SLEEP 0x0010 +#define ISL38XX_INT_IDENT_PCIUART_CTS 0x4000 +#define ISL38XX_INT_IDENT_PCIUART_DR 0x8000 + +/* Control/Status register bits */ +#define ISL38XX_CTRL_STAT_SLEEPMODE 0x00000200 +#define ISL38XX_CTRL_STAT_CLKRUN 0x00800000 +#define ISL38XX_CTRL_STAT_RESET 0x10000000 +#define ISL38XX_CTRL_STAT_RAMBOOT 0x20000000 +#define ISL38XX_CTRL_STAT_STARTHALTED 0x40000000 +#define ISL38XX_CTRL_STAT_HOST_OVERRIDE 0x80000000 + +struct p54p_csr { + __le32 dev_int; + u8 unused_1[12]; + __le32 int_ident; + __le32 int_ack; + __le32 int_enable; + u8 unused_2[4]; + union { + __le32 ring_control_base; + __le32 gen_purp_com[2]; + }; + u8 unused_3[8]; + __le32 direct_mem_base; + u8 unused_4[44]; + __le32 dma_addr; + __le32 dma_len; + __le32 dma_ctrl; + u8 unused_5[12]; + __le32 ctrl_stat; + u8 unused_6[1924]; + u8 cardbus_cis[0x800]; + u8 direct_mem_win[0x1000]; +} __packed; + +/* usb backend only needs the register defines above */ +#ifndef P54USB_H +struct p54p_desc { + __le32 host_addr; + __le32 device_addr; + __le16 len; + __le16 flags; +} __packed; + +struct p54p_ring_control { + __le32 host_idx[4]; + __le32 device_idx[4]; + struct p54p_desc rx_data[8]; + struct p54p_desc tx_data[32]; + struct p54p_desc rx_mgmt[4]; + struct p54p_desc tx_mgmt[4]; +} __packed; + +#define P54P_READ(r) (__force __le32)__raw_readl(&priv->map->r) +#define P54P_WRITE(r, val) __raw_writel((__force u32)(__le32)(val), &priv->map->r) + +struct p54p_priv { + struct p54_common common; + struct pci_dev *pdev; + struct p54p_csr __iomem *map; + struct tasklet_struct tasklet; + const struct firmware *firmware; + spinlock_t lock; + struct p54p_ring_control *ring_control; + dma_addr_t ring_control_dma; + u32 rx_idx_data, tx_idx_data; + u32 rx_idx_mgmt, tx_idx_mgmt; + struct sk_buff *rx_buf_data[8]; + struct sk_buff *rx_buf_mgmt[4]; + struct sk_buff *tx_buf_data[32]; + struct sk_buff *tx_buf_mgmt[4]; + struct completion boot_comp; + struct completion fw_loaded; +}; + +#endif /* P54USB_H */ +#endif /* P54PCI_H */ diff --git a/drivers/net/wireless/intersil/p54/p54spi.c b/drivers/net/wireless/intersil/p54/p54spi.c new file mode 100644 index 000000000000..7ab2f43ab425 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/p54spi.c @@ -0,0 +1,720 @@ +/* + * Copyright (C) 2008 Christian Lamparter + * Copyright 2008 Johannes Berg + * + * This driver is a port from stlc45xx: + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "p54spi.h" +#include "p54.h" + +#include "lmac.h" + +#ifdef CONFIG_P54_SPI_DEFAULT_EEPROM +#include "p54spi_eeprom.h" +#endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */ + +MODULE_FIRMWARE("3826.arm"); + +/* gpios should be handled in board files and provided via platform data, + * but because it's currently impossible for p54spi to have a header file + * in include/linux, let's use module paramaters for now + */ + +static int p54spi_gpio_power = 97; +module_param(p54spi_gpio_power, int, 0444); +MODULE_PARM_DESC(p54spi_gpio_power, "gpio number for power line"); + +static int p54spi_gpio_irq = 87; +module_param(p54spi_gpio_irq, int, 0444); +MODULE_PARM_DESC(p54spi_gpio_irq, "gpio number for irq line"); + +static void p54spi_spi_read(struct p54s_priv *priv, u8 address, + void *buf, size_t len) +{ + struct spi_transfer t[2]; + struct spi_message m; + __le16 addr; + + /* We first push the address */ + addr = cpu_to_le16(address << 8 | SPI_ADRS_READ_BIT_15); + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &addr; + t[0].len = sizeof(addr); + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); + + spi_sync(priv->spi, &m); +} + + +static void p54spi_spi_write(struct p54s_priv *priv, u8 address, + const void *buf, size_t len) +{ + struct spi_transfer t[3]; + struct spi_message m; + __le16 addr; + + /* We first push the address */ + addr = cpu_to_le16(address << 8); + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &addr; + t[0].len = sizeof(addr); + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = buf; + t[1].len = len & ~1; + spi_message_add_tail(&t[1], &m); + + if (len % 2) { + __le16 last_word; + last_word = cpu_to_le16(((u8 *)buf)[len - 1]); + + t[2].tx_buf = &last_word; + t[2].len = sizeof(last_word); + spi_message_add_tail(&t[2], &m); + } + + spi_sync(priv->spi, &m); +} + +static u32 p54spi_read32(struct p54s_priv *priv, u8 addr) +{ + __le32 val; + + p54spi_spi_read(priv, addr, &val, sizeof(val)); + + return le32_to_cpu(val); +} + +static inline void p54spi_write16(struct p54s_priv *priv, u8 addr, __le16 val) +{ + p54spi_spi_write(priv, addr, &val, sizeof(val)); +} + +static inline void p54spi_write32(struct p54s_priv *priv, u8 addr, __le32 val) +{ + p54spi_spi_write(priv, addr, &val, sizeof(val)); +} + +static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, u32 bits) +{ + int i; + + for (i = 0; i < 2000; i++) { + u32 buffer = p54spi_read32(priv, reg); + if ((buffer & bits) == bits) + return 1; + } + return 0; +} + +static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base, + const void *buf, size_t len) +{ + if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, HOST_ALLOWED)) { + dev_err(&priv->spi->dev, "spi_write_dma not allowed " + "to DMA write.\n"); + return -EAGAIN; + } + + p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL, + cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE)); + + p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(len)); + p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, base); + p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, buf, len); + return 0; +} + +static int p54spi_request_firmware(struct ieee80211_hw *dev) +{ + struct p54s_priv *priv = dev->priv; + int ret; + + /* FIXME: should driver use it's own struct device? */ + ret = request_firmware(&priv->firmware, "3826.arm", &priv->spi->dev); + + if (ret < 0) { + dev_err(&priv->spi->dev, "request_firmware() failed: %d", ret); + return ret; + } + + ret = p54_parse_firmware(dev, priv->firmware); + if (ret) { + release_firmware(priv->firmware); + return ret; + } + + return 0; +} + +static int p54spi_request_eeprom(struct ieee80211_hw *dev) +{ + struct p54s_priv *priv = dev->priv; + const struct firmware *eeprom; + int ret; + + /* allow users to customize their eeprom. + */ + + ret = request_firmware_direct(&eeprom, "3826.eeprom", &priv->spi->dev); + if (ret < 0) { +#ifdef CONFIG_P54_SPI_DEFAULT_EEPROM + dev_info(&priv->spi->dev, "loading default eeprom...\n"); + ret = p54_parse_eeprom(dev, (void *) p54spi_eeprom, + sizeof(p54spi_eeprom)); +#else + dev_err(&priv->spi->dev, "Failed to request user eeprom\n"); +#endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */ + } else { + dev_info(&priv->spi->dev, "loading user eeprom...\n"); + ret = p54_parse_eeprom(dev, (void *) eeprom->data, + (int)eeprom->size); + release_firmware(eeprom); + } + return ret; +} + +static int p54spi_upload_firmware(struct ieee80211_hw *dev) +{ + struct p54s_priv *priv = dev->priv; + unsigned long fw_len, _fw_len; + unsigned int offset = 0; + int err = 0; + u8 *fw; + + fw_len = priv->firmware->size; + fw = kmemdup(priv->firmware->data, fw_len, GFP_KERNEL); + if (!fw) + return -ENOMEM; + + /* stop the device */ + p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( + SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET | + SPI_CTRL_STAT_START_HALTED)); + + msleep(TARGET_BOOT_SLEEP); + + p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( + SPI_CTRL_STAT_HOST_OVERRIDE | + SPI_CTRL_STAT_START_HALTED)); + + msleep(TARGET_BOOT_SLEEP); + + while (fw_len > 0) { + _fw_len = min_t(long, fw_len, SPI_MAX_PACKET_SIZE); + + err = p54spi_spi_write_dma(priv, cpu_to_le32( + ISL38XX_DEV_FIRMWARE_ADDR + offset), + (fw + offset), _fw_len); + if (err < 0) + goto out; + + fw_len -= _fw_len; + offset += _fw_len; + } + + BUG_ON(fw_len != 0); + + /* enable host interrupts */ + p54spi_write32(priv, SPI_ADRS_HOST_INT_EN, + cpu_to_le32(SPI_HOST_INTS_DEFAULT)); + + /* boot the device */ + p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( + SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET | + SPI_CTRL_STAT_RAM_BOOT)); + + msleep(TARGET_BOOT_SLEEP); + + p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( + SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_RAM_BOOT)); + msleep(TARGET_BOOT_SLEEP); + +out: + kfree(fw); + return err; +} + +static void p54spi_power_off(struct p54s_priv *priv) +{ + disable_irq(gpio_to_irq(p54spi_gpio_irq)); + gpio_set_value(p54spi_gpio_power, 0); +} + +static void p54spi_power_on(struct p54s_priv *priv) +{ + gpio_set_value(p54spi_gpio_power, 1); + enable_irq(gpio_to_irq(p54spi_gpio_irq)); + + /* need to wait a while before device can be accessed, the length + * is just a guess + */ + msleep(10); +} + +static inline void p54spi_int_ack(struct p54s_priv *priv, u32 val) +{ + p54spi_write32(priv, SPI_ADRS_HOST_INT_ACK, cpu_to_le32(val)); +} + +static int p54spi_wakeup(struct p54s_priv *priv) +{ + /* wake the chip */ + p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS, + cpu_to_le32(SPI_TARGET_INT_WAKEUP)); + + /* And wait for the READY interrupt */ + if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, + SPI_HOST_INT_READY)) { + dev_err(&priv->spi->dev, "INT_READY timeout\n"); + return -EBUSY; + } + + p54spi_int_ack(priv, SPI_HOST_INT_READY); + return 0; +} + +static inline void p54spi_sleep(struct p54s_priv *priv) +{ + p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS, + cpu_to_le32(SPI_TARGET_INT_SLEEP)); +} + +static void p54spi_int_ready(struct p54s_priv *priv) +{ + p54spi_write32(priv, SPI_ADRS_HOST_INT_EN, cpu_to_le32( + SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE)); + + switch (priv->fw_state) { + case FW_STATE_BOOTING: + priv->fw_state = FW_STATE_READY; + complete(&priv->fw_comp); + break; + case FW_STATE_RESETTING: + priv->fw_state = FW_STATE_READY; + /* TODO: reinitialize state */ + break; + default: + break; + } +} + +static int p54spi_rx(struct p54s_priv *priv) +{ + struct sk_buff *skb; + u16 len; + u16 rx_head[2]; +#define READAHEAD_SZ (sizeof(rx_head)-sizeof(u16)) + + if (p54spi_wakeup(priv) < 0) + return -EBUSY; + + /* Read data size and first data word in one SPI transaction + * This is workaround for firmware/DMA bug, + * when first data word gets lost under high load. + */ + p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, rx_head, sizeof(rx_head)); + len = rx_head[0]; + + if (len == 0) { + p54spi_sleep(priv); + dev_err(&priv->spi->dev, "rx request of zero bytes\n"); + return 0; + } + + /* Firmware may insert up to 4 padding bytes after the lmac header, + * but it does not amend the size of SPI data transfer. + * Such packets has correct data size in header, thus referencing + * past the end of allocated skb. Reserve extra 4 bytes for this case + */ + skb = dev_alloc_skb(len + 4); + if (!skb) { + p54spi_sleep(priv); + dev_err(&priv->spi->dev, "could not alloc skb"); + return -ENOMEM; + } + + if (len <= READAHEAD_SZ) { + memcpy(skb_put(skb, len), rx_head + 1, len); + } else { + memcpy(skb_put(skb, READAHEAD_SZ), rx_head + 1, READAHEAD_SZ); + p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, + skb_put(skb, len - READAHEAD_SZ), + len - READAHEAD_SZ); + } + p54spi_sleep(priv); + /* Put additional bytes to compensate for the possible + * alignment-caused truncation + */ + skb_put(skb, 4); + + if (p54_rx(priv->hw, skb) == 0) + dev_kfree_skb(skb); + + return 0; +} + + +static irqreturn_t p54spi_interrupt(int irq, void *config) +{ + struct spi_device *spi = config; + struct p54s_priv *priv = spi_get_drvdata(spi); + + ieee80211_queue_work(priv->hw, &priv->work); + + return IRQ_HANDLED; +} + +static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb) +{ + struct p54_hdr *hdr = (struct p54_hdr *) skb->data; + int ret = 0; + + if (p54spi_wakeup(priv) < 0) + return -EBUSY; + + ret = p54spi_spi_write_dma(priv, hdr->req_id, skb->data, skb->len); + if (ret < 0) + goto out; + + if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, + SPI_HOST_INT_WR_READY)) { + dev_err(&priv->spi->dev, "WR_READY timeout\n"); + ret = -EAGAIN; + goto out; + } + + p54spi_int_ack(priv, SPI_HOST_INT_WR_READY); + + if (FREE_AFTER_TX(skb)) + p54_free_skb(priv->hw, skb); +out: + p54spi_sleep(priv); + return ret; +} + +static int p54spi_wq_tx(struct p54s_priv *priv) +{ + struct p54s_tx_info *entry; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + struct p54_tx_info *minfo; + struct p54s_tx_info *dinfo; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&priv->tx_lock, flags); + + while (!list_empty(&priv->tx_pending)) { + entry = list_entry(priv->tx_pending.next, + struct p54s_tx_info, tx_list); + + list_del_init(&entry->tx_list); + + spin_unlock_irqrestore(&priv->tx_lock, flags); + + dinfo = container_of((void *) entry, struct p54s_tx_info, + tx_list); + minfo = container_of((void *) dinfo, struct p54_tx_info, + data); + info = container_of((void *) minfo, struct ieee80211_tx_info, + rate_driver_data); + skb = container_of((void *) info, struct sk_buff, cb); + + ret = p54spi_tx_frame(priv, skb); + + if (ret < 0) { + p54_free_skb(priv->hw, skb); + return ret; + } + + spin_lock_irqsave(&priv->tx_lock, flags); + } + spin_unlock_irqrestore(&priv->tx_lock, flags); + return ret; +} + +static void p54spi_op_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct p54s_priv *priv = dev->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct p54_tx_info *mi = (struct p54_tx_info *) info->rate_driver_data; + struct p54s_tx_info *di = (struct p54s_tx_info *) mi->data; + unsigned long flags; + + BUILD_BUG_ON(sizeof(*di) > sizeof((mi->data))); + + spin_lock_irqsave(&priv->tx_lock, flags); + list_add_tail(&di->tx_list, &priv->tx_pending); + spin_unlock_irqrestore(&priv->tx_lock, flags); + + ieee80211_queue_work(priv->hw, &priv->work); +} + +static void p54spi_work(struct work_struct *work) +{ + struct p54s_priv *priv = container_of(work, struct p54s_priv, work); + u32 ints; + int ret; + + mutex_lock(&priv->mutex); + + if (priv->fw_state == FW_STATE_OFF) + goto out; + + ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS); + + if (ints & SPI_HOST_INT_READY) { + p54spi_int_ready(priv); + p54spi_int_ack(priv, SPI_HOST_INT_READY); + } + + if (priv->fw_state != FW_STATE_READY) + goto out; + + if (ints & SPI_HOST_INT_UPDATE) { + p54spi_int_ack(priv, SPI_HOST_INT_UPDATE); + ret = p54spi_rx(priv); + if (ret < 0) + goto out; + } + if (ints & SPI_HOST_INT_SW_UPDATE) { + p54spi_int_ack(priv, SPI_HOST_INT_SW_UPDATE); + ret = p54spi_rx(priv); + if (ret < 0) + goto out; + } + + ret = p54spi_wq_tx(priv); +out: + mutex_unlock(&priv->mutex); +} + +static int p54spi_op_start(struct ieee80211_hw *dev) +{ + struct p54s_priv *priv = dev->priv; + unsigned long timeout; + int ret = 0; + + if (mutex_lock_interruptible(&priv->mutex)) { + ret = -EINTR; + goto out; + } + + priv->fw_state = FW_STATE_BOOTING; + + p54spi_power_on(priv); + + ret = p54spi_upload_firmware(dev); + if (ret < 0) { + p54spi_power_off(priv); + goto out_unlock; + } + + mutex_unlock(&priv->mutex); + + timeout = msecs_to_jiffies(2000); + timeout = wait_for_completion_interruptible_timeout(&priv->fw_comp, + timeout); + if (!timeout) { + dev_err(&priv->spi->dev, "firmware boot failed"); + p54spi_power_off(priv); + ret = -1; + goto out; + } + + if (mutex_lock_interruptible(&priv->mutex)) { + ret = -EINTR; + p54spi_power_off(priv); + goto out; + } + + WARN_ON(priv->fw_state != FW_STATE_READY); + +out_unlock: + mutex_unlock(&priv->mutex); + +out: + return ret; +} + +static void p54spi_op_stop(struct ieee80211_hw *dev) +{ + struct p54s_priv *priv = dev->priv; + unsigned long flags; + + mutex_lock(&priv->mutex); + WARN_ON(priv->fw_state != FW_STATE_READY); + + p54spi_power_off(priv); + spin_lock_irqsave(&priv->tx_lock, flags); + INIT_LIST_HEAD(&priv->tx_pending); + spin_unlock_irqrestore(&priv->tx_lock, flags); + + priv->fw_state = FW_STATE_OFF; + mutex_unlock(&priv->mutex); + + cancel_work_sync(&priv->work); +} + +static int p54spi_probe(struct spi_device *spi) +{ + struct p54s_priv *priv = NULL; + struct ieee80211_hw *hw; + int ret = -EINVAL; + + hw = p54_init_common(sizeof(*priv)); + if (!hw) { + dev_err(&spi->dev, "could not alloc ieee80211_hw"); + return -ENOMEM; + } + + priv = hw->priv; + priv->hw = hw; + spi_set_drvdata(spi, priv); + priv->spi = spi; + + spi->bits_per_word = 16; + spi->max_speed_hz = 24000000; + + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&priv->spi->dev, "spi_setup failed"); + goto err_free; + } + + ret = gpio_request(p54spi_gpio_power, "p54spi power"); + if (ret < 0) { + dev_err(&priv->spi->dev, "power GPIO request failed: %d", ret); + goto err_free; + } + + ret = gpio_request(p54spi_gpio_irq, "p54spi irq"); + if (ret < 0) { + dev_err(&priv->spi->dev, "irq GPIO request failed: %d", ret); + goto err_free_gpio_power; + } + + gpio_direction_output(p54spi_gpio_power, 0); + gpio_direction_input(p54spi_gpio_irq); + + ret = request_irq(gpio_to_irq(p54spi_gpio_irq), + p54spi_interrupt, 0, "p54spi", + priv->spi); + if (ret < 0) { + dev_err(&priv->spi->dev, "request_irq() failed"); + goto err_free_gpio_irq; + } + + irq_set_irq_type(gpio_to_irq(p54spi_gpio_irq), IRQ_TYPE_EDGE_RISING); + + disable_irq(gpio_to_irq(p54spi_gpio_irq)); + + INIT_WORK(&priv->work, p54spi_work); + init_completion(&priv->fw_comp); + INIT_LIST_HEAD(&priv->tx_pending); + mutex_init(&priv->mutex); + spin_lock_init(&priv->tx_lock); + SET_IEEE80211_DEV(hw, &spi->dev); + priv->common.open = p54spi_op_start; + priv->common.stop = p54spi_op_stop; + priv->common.tx = p54spi_op_tx; + + ret = p54spi_request_firmware(hw); + if (ret < 0) + goto err_free_common; + + ret = p54spi_request_eeprom(hw); + if (ret) + goto err_free_common; + + ret = p54_register_common(hw, &priv->spi->dev); + if (ret) + goto err_free_common; + + return 0; + +err_free_common: + free_irq(gpio_to_irq(p54spi_gpio_irq), spi); +err_free_gpio_irq: + gpio_free(p54spi_gpio_irq); +err_free_gpio_power: + gpio_free(p54spi_gpio_power); +err_free: + p54_free_common(priv->hw); + return ret; +} + +static int p54spi_remove(struct spi_device *spi) +{ + struct p54s_priv *priv = spi_get_drvdata(spi); + + p54_unregister_common(priv->hw); + + free_irq(gpio_to_irq(p54spi_gpio_irq), spi); + + gpio_free(p54spi_gpio_power); + gpio_free(p54spi_gpio_irq); + release_firmware(priv->firmware); + + mutex_destroy(&priv->mutex); + + p54_free_common(priv->hw); + + return 0; +} + + +static struct spi_driver p54spi_driver = { + .driver = { + .name = "p54spi", + }, + + .probe = p54spi_probe, + .remove = p54spi_remove, +}; + +module_spi_driver(p54spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Lamparter "); +MODULE_ALIAS("spi:cx3110x"); +MODULE_ALIAS("spi:p54spi"); +MODULE_ALIAS("spi:stlc45xx"); diff --git a/drivers/net/wireless/intersil/p54/p54spi.h b/drivers/net/wireless/intersil/p54/p54spi.h new file mode 100644 index 000000000000..dfaa62aaeb07 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/p54spi.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2008 Christian Lamparter + * + * This driver is a port from stlc45xx: + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef P54SPI_H +#define P54SPI_H + +#include +#include +#include + +#include "p54.h" + +/* Bit 15 is read/write bit; ON = READ, OFF = WRITE */ +#define SPI_ADRS_READ_BIT_15 0x8000 + +#define SPI_ADRS_ARM_INTERRUPTS 0x00 +#define SPI_ADRS_ARM_INT_EN 0x04 + +#define SPI_ADRS_HOST_INTERRUPTS 0x08 +#define SPI_ADRS_HOST_INT_EN 0x0c +#define SPI_ADRS_HOST_INT_ACK 0x10 + +#define SPI_ADRS_GEN_PURP_1 0x14 +#define SPI_ADRS_GEN_PURP_2 0x18 + +#define SPI_ADRS_DEV_CTRL_STAT 0x26 /* high word */ + +#define SPI_ADRS_DMA_DATA 0x28 + +#define SPI_ADRS_DMA_WRITE_CTRL 0x2c +#define SPI_ADRS_DMA_WRITE_LEN 0x2e +#define SPI_ADRS_DMA_WRITE_BASE 0x30 + +#define SPI_ADRS_DMA_READ_CTRL 0x34 +#define SPI_ADRS_DMA_READ_LEN 0x36 +#define SPI_ADRS_DMA_READ_BASE 0x38 + +#define SPI_CTRL_STAT_HOST_OVERRIDE 0x8000 +#define SPI_CTRL_STAT_START_HALTED 0x4000 +#define SPI_CTRL_STAT_RAM_BOOT 0x2000 +#define SPI_CTRL_STAT_HOST_RESET 0x1000 +#define SPI_CTRL_STAT_HOST_CPU_EN 0x0800 + +#define SPI_DMA_WRITE_CTRL_ENABLE 0x0001 +#define SPI_DMA_READ_CTRL_ENABLE 0x0001 +#define HOST_ALLOWED (1 << 7) + +#define SPI_TIMEOUT 100 /* msec */ + +#define SPI_MAX_TX_PACKETS 32 + +#define SPI_MAX_PACKET_SIZE 32767 + +#define SPI_TARGET_INT_WAKEUP 0x00000001 +#define SPI_TARGET_INT_SLEEP 0x00000002 +#define SPI_TARGET_INT_RDDONE 0x00000004 + +#define SPI_TARGET_INT_CTS 0x00004000 +#define SPI_TARGET_INT_DR 0x00008000 + +#define SPI_HOST_INT_READY 0x00000001 +#define SPI_HOST_INT_WR_READY 0x00000002 +#define SPI_HOST_INT_SW_UPDATE 0x00000004 +#define SPI_HOST_INT_UPDATE 0x10000000 + +/* clear to send */ +#define SPI_HOST_INT_CR 0x00004000 + +/* data ready */ +#define SPI_HOST_INT_DR 0x00008000 + +#define SPI_HOST_INTS_DEFAULT \ + (SPI_HOST_INT_READY | SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE) + +#define TARGET_BOOT_SLEEP 50 + +struct p54s_dma_regs { + __le16 cmd; + __le16 len; + __le32 addr; +} __packed; + +struct p54s_tx_info { + struct list_head tx_list; +}; + +struct p54s_priv { + /* p54_common has to be the first entry */ + struct p54_common common; + struct ieee80211_hw *hw; + struct spi_device *spi; + + struct work_struct work; + + struct mutex mutex; + struct completion fw_comp; + + spinlock_t tx_lock; + + /* protected by tx_lock */ + struct list_head tx_pending; + + enum fw_state fw_state; + const struct firmware *firmware; +}; + +#endif /* P54SPI_H */ diff --git a/drivers/net/wireless/intersil/p54/p54spi_eeprom.h b/drivers/net/wireless/intersil/p54/p54spi_eeprom.h new file mode 100644 index 000000000000..0b7bfb0adcf2 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/p54spi_eeprom.h @@ -0,0 +1,679 @@ +/* + * Copyright (C) 2003 Conexant Americas Inc. All Rights Reserved. + * Copyright (C) 2004, 2005, 2006 Nokia Corporation + * Copyright 2008 Johannes Berg + * Copyright 2008 Christian Lamparter + * + * based on: + * - cx3110x's pda.h from Nokia + * - cx3110-transfer.log by Johannes Berg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef P54SPI_EEPROM_H +#define P54SPI_EEPROM_H + +static unsigned char p54spi_eeprom[] = { + +/* struct eeprom_pda_wrap */ +0x47, 0x4d, 0x55, 0xaa, /* magic */ +0x00, 0x00, /* pad */ +0x00, 0x00, /* eeprom_pda_data_wrap length */ +0x00, 0x00, 0x00, 0x00, /* arm opcode */ + +/* bogus MAC address */ +0x04, 0x00, 0x01, 0x01, /* PDR_MAC_ADDRESS */ + 0x00, 0x02, 0xee, 0xc0, 0xff, 0xee, + +/* struct bootrec_exp_if */ +0x06, 0x00, 0x01, 0x10, /* PDR_INTERFACE_LIST */ + 0x00, 0x00, /* role */ + 0x0f, 0x00, /* if_id */ + 0x85, 0x00, /* variant = Longbow RF, 2GHz */ + 0x01, 0x00, /* btm_compat */ + 0x1f, 0x00, /* top_compat */ + +0x03, 0x00, 0x02, 0x10, /* PDR_HARDWARE_PLATFORM_COMPONENT_ID */ + 0x03, 0x20, 0x00, 0x43, + +/* struct pda_country[6] */ +0x0d, 0x00, 0x07, 0x10, /* PDR_COUNTRY_LIST */ + 0x10, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, + +/* struct pda_country */ +0x03, 0x00, 0x08, 0x10, /* PDR_DEFAULT_COUNTRY */ + 0x30, 0x00, 0x00, 0x00, /* ETSI */ + +0x03, 0x00, 0x00, 0x11, /* PDR_ANTENNA_GAIN */ + 0x08, 0x08, 0x08, 0x08, + +0x0a, 0x00, 0xff, 0xca, /* PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2 */ + 0x01, 0x00, 0x0a, 0x00, + 0x00, 0x00, 0x0a, 0x00, + 0x85, 0x09, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, + +/* struct pda_custom_wrapper */ +0x10, 0x06, 0x5d, 0xb0, /* PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM */ + 0x0d, 0x00, 0xee, 0x00, /* 13 entries, 238 bytes per entry */ + 0x00, 0x00, 0x16, 0x0c, /* no offset, 3094 total len */ + /* 2412 MHz */ + 0x6c, 0x09, + 0x10, 0x01, 0x9a, 0x84, + 0xaa, 0x8a, 0xaa, 0x8a, 0xaa, 0x8a, 0xaa, 0x8a, + 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, + 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, + 0xf0, 0x00, 0x94, 0x6c, + 0x99, 0x82, 0x99, 0x82, 0x99, 0x82, 0x99, 0x82, + 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, + 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, + 0xd0, 0x00, 0xaa, 0x5a, + 0x88, 0x7a, 0x88, 0x7a, 0x88, 0x7a, 0x88, 0x7a, + 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, + 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, + 0xa0, 0x00, 0xf3, 0x47, + 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, + 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, + 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, + 0x50, 0x00, 0x59, 0x36, + 0x43, 0x5a, 0x43, 0x5a, 0x43, 0x5a, 0x43, 0x5a, + 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, + 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, + 0x00, 0x00, 0xe4, 0x2d, + 0x18, 0x46, 0x18, 0x46, 0x18, 0x46, 0x18, 0x46, + 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, + 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2417 MHz */ + 0x71, 0x09, + 0x10, 0x01, 0xb9, 0x83, + 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, + 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, + 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, + 0xf0, 0x00, 0x2e, 0x6c, + 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, + 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, + 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, + 0xd0, 0x00, 0x8d, 0x5a, + 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, + 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, + 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, + 0xa0, 0x00, 0x0a, 0x48, + 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, + 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, + 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, + 0x50, 0x00, 0x7c, 0x36, + 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, + 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, + 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, + 0x00, 0x00, 0xf5, 0x2d, + 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, + 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, + 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2422 MHz */ + 0x76, 0x09, + 0x10, 0x01, 0xb9, 0x83, + 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, + 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, + 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, + 0xf0, 0x00, 0x2e, 0x6c, + 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, + 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, + 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, + 0xd0, 0x00, 0x8d, 0x5a, + 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, + 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, + 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, + 0xa0, 0x00, 0x0a, 0x48, + 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, + 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, + 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, + 0x50, 0x00, 0x7c, 0x36, + 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, + 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, + 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, + 0x00, 0x00, 0xf5, 0x2d, + 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, + 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, + 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2427 MHz */ + 0x7b, 0x09, + 0x10, 0x01, 0x48, 0x83, + 0x67, 0x8a, 0x67, 0x8a, 0x67, 0x8a, 0x67, 0x8a, + 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, + 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, + 0xf0, 0x00, 0xfb, 0x6b, + 0x50, 0x82, 0x50, 0x82, 0x50, 0x82, 0x50, 0x82, + 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, + 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, + 0xd0, 0x00, 0x7e, 0x5a, + 0x38, 0x7a, 0x38, 0x7a, 0x38, 0x7a, 0x38, 0x7a, + 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, + 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, + 0xa0, 0x00, 0x15, 0x48, + 0x14, 0x6e, 0x14, 0x6e, 0x14, 0x6e, 0x14, 0x6e, + 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, + 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, + 0x50, 0x00, 0x8e, 0x36, + 0xd9, 0x59, 0xd9, 0x59, 0xd9, 0x59, 0xd9, 0x59, + 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, + 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, + 0x00, 0x00, 0xfe, 0x2d, + 0x9d, 0x45, 0x9d, 0x45, 0x9d, 0x45, 0x9d, 0x45, + 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, + 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2432 MHz */ + 0x80, 0x09, + 0x10, 0x01, 0xd7, 0x82, + 0x51, 0x8a, 0x51, 0x8a, 0x51, 0x8a, 0x51, 0x8a, + 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, + 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, + 0xf0, 0x00, 0xc8, 0x6b, + 0x37, 0x82, 0x37, 0x82, 0x37, 0x82, 0x37, 0x82, + 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, + 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, + 0xd0, 0x00, 0x6f, 0x5a, + 0x1d, 0x7a, 0x1d, 0x7a, 0x1d, 0x7a, 0x1d, 0x7a, + 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, + 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, + 0xa0, 0x00, 0x20, 0x48, + 0xf6, 0x6d, 0xf6, 0x6d, 0xf6, 0x6d, 0xf6, 0x6d, + 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, + 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, + 0x50, 0x00, 0x9f, 0x36, + 0xb5, 0x59, 0xb5, 0x59, 0xb5, 0x59, 0xb5, 0x59, + 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, + 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, + 0x00, 0x00, 0x06, 0x2e, + 0x74, 0x45, 0x74, 0x45, 0x74, 0x45, 0x74, 0x45, + 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, + 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2437 MHz */ + 0x85, 0x09, + 0x10, 0x01, 0x67, 0x82, + 0x3a, 0x8a, 0x3a, 0x8a, 0x3a, 0x8a, 0x3a, 0x8a, + 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, + 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, + 0xf0, 0x00, 0x95, 0x6b, + 0x1f, 0x82, 0x1f, 0x82, 0x1f, 0x82, 0x1f, 0x82, + 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, + 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, + 0xd0, 0x00, 0x61, 0x5a, + 0x02, 0x7a, 0x02, 0x7a, 0x02, 0x7a, 0x02, 0x7a, + 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, + 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, + 0xa0, 0x00, 0x2c, 0x48, + 0xd8, 0x6d, 0xd8, 0x6d, 0xd8, 0x6d, 0xd8, 0x6d, + 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, + 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, + 0x50, 0x00, 0xb1, 0x36, + 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, + 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, + 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, + 0x00, 0x00, 0x0f, 0x2e, + 0x4b, 0x45, 0x4b, 0x45, 0x4b, 0x45, 0x4b, 0x45, + 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, + 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2442 MHz */ + 0x8a, 0x09, + 0x10, 0x01, 0xf6, 0x81, + 0x24, 0x8a, 0x24, 0x8a, 0x24, 0x8a, 0x24, 0x8a, + 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, + 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, + 0xf0, 0x00, 0x62, 0x6b, + 0x06, 0x82, 0x06, 0x82, 0x06, 0x82, 0x06, 0x82, + 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, + 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, + 0xd0, 0x00, 0x52, 0x5a, + 0xe7, 0x79, 0xe7, 0x79, 0xe7, 0x79, 0xe7, 0x79, + 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, + 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, + 0xa0, 0x00, 0x37, 0x48, + 0xba, 0x6d, 0xba, 0x6d, 0xba, 0x6d, 0xba, 0x6d, + 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, + 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, + 0x50, 0x00, 0xc2, 0x36, + 0x6e, 0x59, 0x6e, 0x59, 0x6e, 0x59, 0x6e, 0x59, + 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, + 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, + 0x00, 0x00, 0x17, 0x2e, + 0x22, 0x45, 0x22, 0x45, 0x22, 0x45, 0x22, 0x45, + 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, + 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2447 MHz */ + 0x8f, 0x09, + 0x10, 0x01, 0x75, 0x83, + 0x61, 0x8a, 0x61, 0x8a, 0x61, 0x8a, 0x61, 0x8a, + 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, + 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, + 0xf0, 0x00, 0x4b, 0x6c, + 0x3f, 0x82, 0x3f, 0x82, 0x3f, 0x82, 0x3f, 0x82, + 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, + 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, + 0xd0, 0x00, 0xda, 0x5a, + 0x1c, 0x7a, 0x1c, 0x7a, 0x1c, 0x7a, 0x1c, 0x7a, + 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, + 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, + 0xa0, 0x00, 0x6d, 0x48, + 0xe9, 0x6d, 0xe9, 0x6d, 0xe9, 0x6d, 0xe9, 0x6d, + 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, + 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, + 0x50, 0x00, 0xc6, 0x36, + 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, + 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, + 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, + 0x00, 0x00, 0x15, 0x2e, + 0x3c, 0x45, 0x3c, 0x45, 0x3c, 0x45, 0x3c, 0x45, + 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, + 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2452 MHz */ + 0x94, 0x09, + 0x10, 0x01, 0xf4, 0x84, + 0x9e, 0x8a, 0x9e, 0x8a, 0x9e, 0x8a, 0x9e, 0x8a, + 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, + 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, + 0xf0, 0x00, 0x34, 0x6d, + 0x77, 0x82, 0x77, 0x82, 0x77, 0x82, 0x77, 0x82, + 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, + 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, + 0xd0, 0x00, 0x62, 0x5b, + 0x50, 0x7a, 0x50, 0x7a, 0x50, 0x7a, 0x50, 0x7a, + 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, + 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, + 0xa0, 0x00, 0xa2, 0x48, + 0x17, 0x6e, 0x17, 0x6e, 0x17, 0x6e, 0x17, 0x6e, + 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, + 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, + 0x50, 0x00, 0xc9, 0x36, + 0xb7, 0x59, 0xb7, 0x59, 0xb7, 0x59, 0xb7, 0x59, + 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, + 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, + 0x00, 0x00, 0x12, 0x2e, + 0x57, 0x45, 0x57, 0x45, 0x57, 0x45, 0x57, 0x45, + 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, + 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2452 MHz */ + 0x99, 0x09, + 0x10, 0x01, 0x74, 0x86, + 0xdb, 0x8a, 0xdb, 0x8a, 0xdb, 0x8a, 0xdb, 0x8a, + 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, + 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, + 0xf0, 0x00, 0x1e, 0x6e, + 0xb0, 0x82, 0xb0, 0x82, 0xb0, 0x82, 0xb0, 0x82, + 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, + 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, + 0xd0, 0x00, 0xeb, 0x5b, + 0x85, 0x7a, 0x85, 0x7a, 0x85, 0x7a, 0x85, 0x7a, + 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, + 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, + 0xa0, 0x00, 0xd8, 0x48, + 0x46, 0x6e, 0x46, 0x6e, 0x46, 0x6e, 0x46, 0x6e, + 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, + 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, + 0x50, 0x00, 0xcd, 0x36, + 0xdb, 0x59, 0xdb, 0x59, 0xdb, 0x59, 0xdb, 0x59, + 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, + 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, + 0x00, 0x00, 0x10, 0x2e, + 0x71, 0x45, 0x71, 0x45, 0x71, 0x45, 0x71, 0x45, + 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, + 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2557 MHz */ + 0x9e, 0x09, + 0x10, 0x01, 0xf3, 0x87, + 0x17, 0x8b, 0x17, 0x8b, 0x17, 0x8b, 0x17, 0x8b, + 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, + 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, + 0xf0, 0x00, 0x07, 0x6f, + 0xe9, 0x82, 0xe9, 0x82, 0xe9, 0x82, 0xe9, 0x82, + 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, + 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, + 0xd0, 0x00, 0x73, 0x5c, + 0xba, 0x7a, 0xba, 0x7a, 0xba, 0x7a, 0xba, 0x7a, + 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, + 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, + 0xa0, 0x00, 0x0d, 0x49, + 0x74, 0x6e, 0x74, 0x6e, 0x74, 0x6e, 0x74, 0x6e, + 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, + 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, + 0x50, 0x00, 0xd1, 0x36, + 0xff, 0x59, 0xff, 0x59, 0xff, 0x59, 0xff, 0x59, + 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, + 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, + 0x00, 0x00, 0x0e, 0x2e, + 0x8b, 0x45, 0x8b, 0x45, 0x8b, 0x45, 0x8b, 0x45, + 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, + 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2562 MHz */ + 0xa3, 0x09, + 0x10, 0x01, 0x72, 0x89, + 0x54, 0x8b, 0x54, 0x8b, 0x54, 0x8b, 0x54, 0x8b, + 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, + 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, + 0xf0, 0x00, 0xf0, 0x6f, + 0x21, 0x83, 0x21, 0x83, 0x21, 0x83, 0x21, 0x83, + 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, + 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, + 0xd0, 0x00, 0xfb, 0x5c, + 0xee, 0x7a, 0xee, 0x7a, 0xee, 0x7a, 0xee, 0x7a, + 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, + 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, + 0xa0, 0x00, 0x43, 0x49, + 0xa3, 0x6e, 0xa3, 0x6e, 0xa3, 0x6e, 0xa3, 0x6e, + 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, + 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, + 0x50, 0x00, 0xd4, 0x36, + 0x24, 0x5a, 0x24, 0x5a, 0x24, 0x5a, 0x24, 0x5a, + 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, + 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, + 0x00, 0x00, 0x0b, 0x2e, + 0xa6, 0x45, 0xa6, 0x45, 0xa6, 0x45, 0xa6, 0x45, + 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, + 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + + /* 2572 MHz */ + 0xa8, 0x09, + 0x10, 0x01, 0xf1, 0x8a, + 0x91, 0x8b, 0x91, 0x8b, 0x91, 0x8b, 0x91, 0x8b, + 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, + 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, + 0xf0, 0x00, 0xd9, 0x70, + 0x5a, 0x83, 0x5a, 0x83, 0x5a, 0x83, 0x5a, 0x83, + 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, + 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, + 0xd0, 0x00, 0x83, 0x5d, + 0x23, 0x7b, 0x23, 0x7b, 0x23, 0x7b, 0x23, 0x7b, + 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, + 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, + 0xa0, 0x00, 0x78, 0x49, + 0xd1, 0x6e, 0xd1, 0x6e, 0xd1, 0x6e, 0xd1, 0x6e, + 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, + 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, + 0x50, 0x00, 0xd8, 0x36, + 0x48, 0x5a, 0x48, 0x5a, 0x48, 0x5a, 0x48, 0x5a, + 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, + 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, + 0x00, 0x00, 0x09, 0x2e, + 0xc0, 0x45, 0xc0, 0x45, 0xc0, 0x45, 0xc0, 0x45, + 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, + 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, + +/* + * Not really sure if this is actually the power_limit database, + * it looks a bit "related" to PDR_PRISM_ZIF_TX_IQ_CALIBRATION + */ +/* struct pda_custom_wrapper */ +0xae, 0x00, 0xef, 0xbe, /* PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM */ + 0x0d, 0x00, 0x1a, 0x00, /* 13 entries, 26 bytes per entry */ + 0x00, 0x00, 0x52, 0x01, /* no offset, 338 bytes total */ + + /* 2412 MHz */ + 0x6c, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2417 MHz */ + 0x71, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2422 MHz */ + 0x76, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2427 MHz */ + 0x7b, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2432 MHz */ + 0x80, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2437 MHz */ + 0x85, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2442 MHz */ + 0x8a, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2447 MHz */ + 0x8f, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2452 MHz */ + 0x94, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2457 MHz */ + 0x99, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2462 MHz */ + 0x9e, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2467 MHz */ + 0xa3, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + + /* 2472 MHz */ + 0xa8, 0x09, + 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, + 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, + 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, + +/* struct pda_iq_autocal_entry[13] */ +0x42, 0x00, 0x06, 0x19, /* PDR_PRISM_ZIF_TX_IQ_CALIBRATION */ + /* 2412 MHz */ + 0x6c, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, + /* 2417 MHz */ + 0x71, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, + /* 2422 MHz */ + 0x76, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, + /* 2427 MHz */ + 0x7b, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, + /* 2432 MHz */ + 0x80, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, + /* 2437 MHz */ + 0x85, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, + /* 2442 MHz */ + 0x8a, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, + /* 2447 MHz */ + 0x8f, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, + /* 2452 MHz */ + 0x94, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, + /* 2457 MHz */ + 0x99, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, + /* 2462 MHz */ + 0x9e, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, + /* 2467 MHz */ + 0xa3, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, + /* 2472 MHz */ + 0xa8, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, + +0x02, 0x00, 0x00, 0x00, /* PDR_END */ + 0xb6, 0x04, +}; + +#endif /* P54SPI_EEPROM_H */ + diff --git a/drivers/net/wireless/intersil/p54/p54usb.c b/drivers/net/wireless/intersil/p54/p54usb.c new file mode 100644 index 000000000000..043bd1c23c19 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/p54usb.c @@ -0,0 +1,1149 @@ + +/* + * Linux device driver for USB based Prism54 + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "p54.h" +#include "lmac.h" +#include "p54usb.h" + +MODULE_AUTHOR("Michael Wu "); +MODULE_DESCRIPTION("Prism54 USB wireless driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("prism54usb"); +MODULE_FIRMWARE("isl3886usb"); +MODULE_FIRMWARE("isl3887usb"); + +/* + * Note: + * + * Always update our wiki's device list (located at: + * http://wireless.kernel.org/en/users/Drivers/p54/devices ), + * whenever you add a new device. + */ + +static struct usb_device_id p54u_table[] = { + /* Version 1 devices (pci chip + net2280) */ + {USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */ + {USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */ + {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */ + {USB_DEVICE(0x0675, 0x0530)}, /* DrayTek Vigor 530 */ + {USB_DEVICE(0x06b9, 0x0120)}, /* Thomson SpeedTouch 120g */ + {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */ + {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */ + {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */ + {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */ + {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */ + {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */ + {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */ + {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */ + {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */ + {USB_DEVICE(0x0bf8, 0x1007)}, /* Fujitsu E-5400 USB */ + {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */ + {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */ + {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */ + {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */ + {USB_DEVICE(0x1435, 0x0210)}, /* Inventel UR054G */ + {USB_DEVICE(0x15a9, 0x0002)}, /* Gemtek WUBI-100GW 802.11g */ + {USB_DEVICE(0x1630, 0x0005)}, /* 2Wire 802.11g USB (v1) / Z-Com */ + {USB_DEVICE(0x182d, 0x096b)}, /* Sitecom WL-107 */ + {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */ + {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */ + {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */ + {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */ + {USB_DEVICE(0x2001, 0x3762)}, /* Conceptronic C54U */ + {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */ + {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */ + + /* Version 2 devices (3887) */ + {USB_DEVICE(0x0471, 0x1230)}, /* Philips CPWUA054/00 */ + {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */ + {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */ + {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */ + {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */ + {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */ + {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ + {USB_DEVICE(0x07aa, 0x0020)}, /* Corega WLUSB2GTST USB */ + {USB_DEVICE(0x0803, 0x4310)}, /* Zoom 4410a */ + {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ + {USB_DEVICE(0x083a, 0x4531)}, /* T-Com Sinus 154 data II */ + {USB_DEVICE(0x083a, 0xc501)}, /* Zoom Wireless-G 4410 */ + {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */ + {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */ + {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */ + {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */ + {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/ + {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/ + /* {USB_DEVICE(0x0cde, 0x0006)}, * Medion MD40900 already listed above, + * just noting it here for clarity */ + {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */ + {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */ + {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */ + {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */ + {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */ + {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */ + {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */ + {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */ + {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */ + /* {USB_DEVICE(0x15a9, 0x0002)}, * Also SparkLAN WL-682 with 3887 */ + {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */ + {USB_DEVICE(0x1740, 0x1000)}, /* Senao NUB-350 */ + {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */ + {USB_DEVICE(0x2001, 0x3705)}, /* D-Link DWL-G120 rev C1 */ + {USB_DEVICE(0x413c, 0x5513)}, /* Dell WLA3310 USB Wireless Adapter */ + {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */ + {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */ + {} +}; + +MODULE_DEVICE_TABLE(usb, p54u_table); + +static const struct { + u32 intf; + enum p54u_hw_type type; + const char *fw; + char hw[20]; +} p54u_fwlist[__NUM_P54U_HWTYPES] = { + { + .type = P54U_NET2280, + .intf = FW_LM86, + .fw = "isl3886usb", + .hw = "ISL3886 + net2280", + }, + { + .type = P54U_3887, + .intf = FW_LM87, + .fw = "isl3887usb", + .hw = "ISL3887", + }, +}; + +static void p54u_rx_cb(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb; + struct ieee80211_hw *dev = info->dev; + struct p54u_priv *priv = dev->priv; + + skb_unlink(skb, &priv->rx_queue); + + if (unlikely(urb->status)) { + dev_kfree_skb_irq(skb); + return; + } + + skb_put(skb, urb->actual_length); + + if (priv->hw_type == P54U_NET2280) + skb_pull(skb, priv->common.tx_hdr_len); + if (priv->common.fw_interface == FW_LM87) { + skb_pull(skb, 4); + skb_put(skb, 4); + } + + if (p54_rx(dev, skb)) { + skb = dev_alloc_skb(priv->common.rx_mtu + 32); + if (unlikely(!skb)) { + /* TODO check rx queue length and refill *somewhere* */ + return; + } + + info = (struct p54u_rx_info *) skb->cb; + info->urb = urb; + info->dev = dev; + urb->transfer_buffer = skb_tail_pointer(skb); + urb->context = skb; + } else { + if (priv->hw_type == P54U_NET2280) + skb_push(skb, priv->common.tx_hdr_len); + if (priv->common.fw_interface == FW_LM87) { + skb_push(skb, 4); + skb_put(skb, 4); + } + skb_reset_tail_pointer(skb); + skb_trim(skb, 0); + urb->transfer_buffer = skb_tail_pointer(skb); + } + skb_queue_tail(&priv->rx_queue, skb); + usb_anchor_urb(urb, &priv->submitted); + if (usb_submit_urb(urb, GFP_ATOMIC)) { + skb_unlink(skb, &priv->rx_queue); + usb_unanchor_urb(urb); + dev_kfree_skb_irq(skb); + } +} + +static void p54u_tx_cb(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct ieee80211_hw *dev = + usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); + + p54_free_skb(dev, skb); +} + +static void p54u_tx_dummy_cb(struct urb *urb) { } + +static void p54u_free_urbs(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + usb_kill_anchored_urbs(&priv->submitted); +} + +static void p54u_stop(struct ieee80211_hw *dev) +{ + /* + * TODO: figure out how to reliably stop the 3887 and net2280 so + * the hardware is still usable next time we want to start it. + * until then, we just stop listening to the hardware.. + */ + p54u_free_urbs(dev); +} + +static int p54u_init_urbs(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + struct urb *entry = NULL; + struct sk_buff *skb; + struct p54u_rx_info *info; + int ret = 0; + + while (skb_queue_len(&priv->rx_queue) < 32) { + skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL); + if (!skb) { + ret = -ENOMEM; + goto err; + } + entry = usb_alloc_urb(0, GFP_KERNEL); + if (!entry) { + ret = -ENOMEM; + goto err; + } + + usb_fill_bulk_urb(entry, priv->udev, + usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), + skb_tail_pointer(skb), + priv->common.rx_mtu + 32, p54u_rx_cb, skb); + info = (struct p54u_rx_info *) skb->cb; + info->urb = entry; + info->dev = dev; + skb_queue_tail(&priv->rx_queue, skb); + + usb_anchor_urb(entry, &priv->submitted); + ret = usb_submit_urb(entry, GFP_KERNEL); + if (ret) { + skb_unlink(skb, &priv->rx_queue); + usb_unanchor_urb(entry); + goto err; + } + usb_free_urb(entry); + entry = NULL; + } + + return 0; + + err: + usb_free_urb(entry); + kfree_skb(skb); + p54u_free_urbs(dev); + return ret; +} + +static int p54u_open(struct ieee80211_hw *dev) +{ + /* + * TODO: Because we don't know how to reliably stop the 3887 and + * the isl3886+net2280, other than brutally cut off all + * communications. We have to reinitialize the urbs on every start. + */ + return p54u_init_urbs(dev); +} + +static __le32 p54u_lm87_chksum(const __le32 *data, size_t length) +{ + u32 chk = 0; + + length >>= 2; + while (length--) { + chk ^= le32_to_cpu(*data++); + chk = (chk >> 5) ^ (chk << 3); + } + + return cpu_to_le32(chk); +} + +static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct p54u_priv *priv = dev->priv; + struct urb *data_urb; + struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); + + data_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!data_urb) { + p54_free_skb(dev, skb); + return; + } + + hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); + hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id; + + usb_fill_bulk_urb(data_urb, priv->udev, + usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), + hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? + p54u_tx_cb : p54u_tx_dummy_cb, skb); + data_urb->transfer_flags |= URB_ZERO_PACKET; + + usb_anchor_urb(data_urb, &priv->submitted); + if (usb_submit_urb(data_urb, GFP_ATOMIC)) { + usb_unanchor_urb(data_urb); + p54_free_skb(dev, skb); + } + usb_free_urb(data_urb); +} + +static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct p54u_priv *priv = dev->priv; + struct urb *int_urb = NULL, *data_urb = NULL; + struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); + struct net2280_reg_write *reg = NULL; + int err = -ENOMEM; + + reg = kmalloc(sizeof(*reg), GFP_ATOMIC); + if (!reg) + goto out; + + int_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!int_urb) + goto out; + + data_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!data_urb) + goto out; + + reg->port = cpu_to_le16(NET2280_DEV_U32); + reg->addr = cpu_to_le32(P54U_DEV_BASE); + reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); + + memset(hdr, 0, sizeof(*hdr)); + hdr->len = cpu_to_le16(skb->len); + hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id; + + usb_fill_bulk_urb(int_urb, priv->udev, + usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), + p54u_tx_dummy_cb, dev); + + /* + * URB_FREE_BUFFER triggers a code path in the USB subsystem that will + * free what is inside the transfer_buffer after the last reference to + * the int_urb is dropped. + */ + int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET; + reg = NULL; + + usb_fill_bulk_urb(data_urb, priv->udev, + usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), + hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? + p54u_tx_cb : p54u_tx_dummy_cb, skb); + data_urb->transfer_flags |= URB_ZERO_PACKET; + + usb_anchor_urb(int_urb, &priv->submitted); + err = usb_submit_urb(int_urb, GFP_ATOMIC); + if (err) { + usb_unanchor_urb(int_urb); + goto out; + } + + usb_anchor_urb(data_urb, &priv->submitted); + err = usb_submit_urb(data_urb, GFP_ATOMIC); + if (err) { + usb_unanchor_urb(data_urb); + goto out; + } +out: + usb_free_urb(int_urb); + usb_free_urb(data_urb); + + if (err) { + kfree(reg); + p54_free_skb(dev, skb); + } +} + +static int p54u_write(struct p54u_priv *priv, + struct net2280_reg_write *buf, + enum net2280_op_type type, + __le32 addr, __le32 val) +{ + unsigned int ep; + int alen; + + if (type & 0x0800) + ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV); + else + ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG); + + buf->port = cpu_to_le16(type); + buf->addr = addr; + buf->val = val; + + return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000); +} + +static int p54u_read(struct p54u_priv *priv, void *buf, + enum net2280_op_type type, + __le32 addr, __le32 *val) +{ + struct net2280_reg_read *read = buf; + __le32 *reg = buf; + unsigned int ep; + int alen, err; + + if (type & 0x0800) + ep = P54U_PIPE_DEV; + else + ep = P54U_PIPE_BRG; + + read->port = cpu_to_le16(type); + read->addr = addr; + + err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), + read, sizeof(*read), &alen, 1000); + if (err) + return err; + + err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep), + reg, sizeof(*reg), &alen, 1000); + if (err) + return err; + + *val = *reg; + return 0; +} + +static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep, + void *data, size_t len) +{ + int alen; + return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), + data, len, &alen, 2000); +} + +static int p54u_device_reset(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING); + + if (lock) { + ret = usb_lock_device_for_reset(priv->udev, priv->intf); + if (ret < 0) { + dev_err(&priv->udev->dev, "(p54usb) unable to lock " + "device for reset (%d)!\n", ret); + return ret; + } + } + + ret = usb_reset_device(priv->udev); + if (lock) + usb_unlock_device(priv->udev); + + if (ret) + dev_err(&priv->udev->dev, "(p54usb) unable to reset " + "device (%d)!\n", ret); + + return ret; +} + +static const char p54u_romboot_3887[] = "~~~~"; +static int p54u_firmware_reset_3887(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + u8 *buf; + int ret; + + buf = kmemdup(p54u_romboot_3887, 4, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = p54u_bulk_msg(priv, P54U_PIPE_DATA, + buf, 4); + kfree(buf); + if (ret) + dev_err(&priv->udev->dev, "(p54usb) unable to jump to " + "boot ROM (%d)!\n", ret); + + return ret; +} + +static const char p54u_firmware_upload_3887[] = "<\r"; +static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + int err, alen; + u8 carry = 0; + u8 *buf, *tmp; + const u8 *data; + unsigned int left, remains, block_size; + struct x2_header *hdr; + unsigned long timeout; + + err = p54u_firmware_reset_3887(dev); + if (err) + return err; + + tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + left = block_size = min_t(size_t, P54U_FW_BLOCK, priv->fw->size); + strcpy(buf, p54u_firmware_upload_3887); + left -= strlen(p54u_firmware_upload_3887); + tmp += strlen(p54u_firmware_upload_3887); + + data = priv->fw->data; + remains = priv->fw->size; + + hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887)); + memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE); + hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR); + hdr->fw_length = cpu_to_le32(priv->fw->size); + hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr, + sizeof(u32)*2)); + left -= sizeof(*hdr); + tmp += sizeof(*hdr); + + while (remains) { + while (left--) { + if (carry) { + *tmp++ = carry; + carry = 0; + remains--; + continue; + } + switch (*data) { + case '~': + *tmp++ = '}'; + carry = '^'; + break; + case '}': + *tmp++ = '}'; + carry = ']'; + break; + default: + *tmp++ = *data; + remains--; + break; + } + data++; + } + + err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size); + if (err) { + dev_err(&priv->udev->dev, "(p54usb) firmware " + "upload failed!\n"); + goto err_upload_failed; + } + + tmp = buf; + left = block_size = min((unsigned int)P54U_FW_BLOCK, remains); + } + + *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data, + priv->fw->size)); + err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); + if (err) { + dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); + goto err_upload_failed; + } + timeout = jiffies + msecs_to_jiffies(1000); + while (!(err = usb_bulk_msg(priv->udev, + usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { + if (alen > 2 && !memcmp(buf, "OK", 2)) + break; + + if (alen > 5 && !memcmp(buf, "ERROR", 5)) { + err = -EINVAL; + break; + } + + if (time_after(jiffies, timeout)) { + dev_err(&priv->udev->dev, "(p54usb) firmware boot " + "timed out!\n"); + err = -ETIMEDOUT; + break; + } + } + if (err) { + dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); + goto err_upload_failed; + } + + buf[0] = 'g'; + buf[1] = '\r'; + err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2); + if (err) { + dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n"); + goto err_upload_failed; + } + + timeout = jiffies + msecs_to_jiffies(1000); + while (!(err = usb_bulk_msg(priv->udev, + usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { + if (alen > 0 && buf[0] == 'g') + break; + + if (time_after(jiffies, timeout)) { + err = -ETIMEDOUT; + break; + } + } + if (err) + goto err_upload_failed; + +err_upload_failed: + kfree(buf); + return err; +} + +static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE; + int err, alen; + void *buf; + __le32 reg; + unsigned int remains, offset; + const u8 *data; + + buf = kmalloc(512, GFP_KERNEL); + if (!buf) + return -ENOMEM; + +#define P54U_WRITE(type, addr, data) \ + do {\ + err = p54u_write(priv, buf, type,\ + cpu_to_le32((u32)(unsigned long)addr), data);\ + if (err) \ + goto fail;\ + } while (0) + +#define P54U_READ(type, addr) \ + do {\ + err = p54u_read(priv, buf, type,\ + cpu_to_le32((u32)(unsigned long)addr), ®);\ + if (err)\ + goto fail;\ + } while (0) + + /* power down net2280 bridge */ + P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL); + reg |= cpu_to_le32(P54U_BRG_POWER_DOWN); + reg &= cpu_to_le32(~P54U_BRG_POWER_UP); + P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); + + mdelay(100); + + /* power up bridge */ + reg |= cpu_to_le32(P54U_BRG_POWER_UP); + reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN); + P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); + + mdelay(100); + + P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT, + cpu_to_le32(NET2280_CLK_30Mhz | + NET2280_PCI_ENABLE | + NET2280_PCI_SOFT_RESET)); + + mdelay(20); + + P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND, + cpu_to_le32(PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER)); + + P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0, + cpu_to_le32(NET2280_BASE)); + + P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS); + reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT); + P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg); + + // TODO: we really need this? + P54U_READ(NET2280_BRG_U32, NET2280_RELNUM); + + P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP, + cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); + P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP, + cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); + + P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2, + cpu_to_le32(NET2280_BASE2)); + + /* finally done setting up the bridge */ + + P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND, + cpu_to_le32(PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER)); + + P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0); + P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0, + cpu_to_le32(P54U_DEV_BASE)); + + P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); + P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, + cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); + + /* do romboot */ + P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0); + + P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + mdelay(20); + + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + mdelay(20); + + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + mdelay(100); + + P54U_READ(NET2280_DEV_U32, &devreg->int_ident); + P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); + + /* finally, we can upload firmware now! */ + remains = priv->fw->size; + data = priv->fw->data; + offset = ISL38XX_DEV_FIRMWARE_ADDR; + + while (remains) { + unsigned int block_len = min(remains, (unsigned int)512); + memcpy(buf, data, block_len); + + err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len); + if (err) { + dev_err(&priv->udev->dev, "(p54usb) firmware block " + "upload failed\n"); + goto fail; + } + + P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base, + cpu_to_le32(0xc0000f00)); + + P54U_WRITE(NET2280_DEV_U32, + 0x0020 | (unsigned long)&devreg->direct_mem_win, 0); + P54U_WRITE(NET2280_DEV_U32, + 0x0020 | (unsigned long)&devreg->direct_mem_win, + cpu_to_le32(1)); + + P54U_WRITE(NET2280_DEV_U32, + 0x0024 | (unsigned long)&devreg->direct_mem_win, + cpu_to_le32(block_len)); + P54U_WRITE(NET2280_DEV_U32, + 0x0028 | (unsigned long)&devreg->direct_mem_win, + cpu_to_le32(offset)); + + P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr, + cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR)); + P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len, + cpu_to_le32(block_len >> 2)); + P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl, + cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER)); + + mdelay(10); + + P54U_READ(NET2280_DEV_U32, + 0x002C | (unsigned long)&devreg->direct_mem_win); + if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) || + !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) { + dev_err(&priv->udev->dev, "(p54usb) firmware DMA " + "transfer failed\n"); + goto fail; + } + + P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT, + cpu_to_le32(NET2280_FIFO_FLUSH)); + + remains -= block_len; + data += block_len; + offset += block_len; + } + + /* do ramboot */ + P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + mdelay(20); + + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + mdelay(100); + + P54U_READ(NET2280_DEV_U32, &devreg->int_ident); + P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); + + /* start up the firmware */ + P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, + cpu_to_le32(ISL38XX_INT_IDENT_INIT)); + + P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, + cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); + + P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, + cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE | + NET2280_USB_INTERRUPT_ENABLE)); + + P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int, + cpu_to_le32(ISL38XX_DEV_INT_RESET)); + + err = usb_interrupt_msg(priv->udev, + usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT), + buf, sizeof(__le32), &alen, 1000); + if (err || alen != sizeof(__le32)) + goto fail; + + P54U_READ(NET2280_DEV_U32, &devreg->int_ident); + P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); + + if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT))) + err = -EINVAL; + + P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); + P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, + cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); + +#undef P54U_WRITE +#undef P54U_READ + +fail: + kfree(buf); + return err; +} + +static int p54_find_type(struct p54u_priv *priv) +{ + int i; + + for (i = 0; i < __NUM_P54U_HWTYPES; i++) + if (p54u_fwlist[i].type == priv->hw_type) + break; + if (i == __NUM_P54U_HWTYPES) + return -EOPNOTSUPP; + + return i; +} + +static int p54u_start_ops(struct p54u_priv *priv) +{ + struct ieee80211_hw *dev = priv->common.hw; + int ret; + + ret = p54_parse_firmware(dev, priv->fw); + if (ret) + goto err_out; + + ret = p54_find_type(priv); + if (ret < 0) + goto err_out; + + if (priv->common.fw_interface != p54u_fwlist[ret].intf) { + dev_err(&priv->udev->dev, "wrong firmware, please get " + "a firmware for \"%s\" and try again.\n", + p54u_fwlist[ret].hw); + ret = -ENODEV; + goto err_out; + } + + ret = priv->upload_fw(dev); + if (ret) + goto err_out; + + ret = p54u_open(dev); + if (ret) + goto err_out; + + ret = p54_read_eeprom(dev); + if (ret) + goto err_stop; + + p54u_stop(dev); + + ret = p54_register_common(dev, &priv->udev->dev); + if (ret) + goto err_stop; + + return 0; + +err_stop: + p54u_stop(dev); + +err_out: + /* + * p54u_disconnect will do the rest of the + * cleanup + */ + return ret; +} + +static void p54u_load_firmware_cb(const struct firmware *firmware, + void *context) +{ + struct p54u_priv *priv = context; + struct usb_device *udev = priv->udev; + int err; + + complete(&priv->fw_wait_load); + if (firmware) { + priv->fw = firmware; + err = p54u_start_ops(priv); + } else { + err = -ENOENT; + dev_err(&udev->dev, "Firmware not found.\n"); + } + + if (err) { + struct device *parent = priv->udev->dev.parent; + + dev_err(&udev->dev, "failed to initialize device (%d)\n", err); + + if (parent) + device_lock(parent); + + device_release_driver(&udev->dev); + /* + * At this point p54u_disconnect has already freed + * the "priv" context. Do not use it anymore! + */ + priv = NULL; + + if (parent) + device_unlock(parent); + } + + usb_put_dev(udev); +} + +static int p54u_load_firmware(struct ieee80211_hw *dev, + struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct p54u_priv *priv = dev->priv; + struct device *device = &udev->dev; + int err, i; + + BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES); + + init_completion(&priv->fw_wait_load); + i = p54_find_type(priv); + if (i < 0) + return i; + + dev_info(&priv->udev->dev, "Loading firmware file %s\n", + p54u_fwlist[i].fw); + + usb_get_dev(udev); + err = request_firmware_nowait(THIS_MODULE, 1, p54u_fwlist[i].fw, + device, GFP_KERNEL, priv, + p54u_load_firmware_cb); + if (err) { + dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s " + "(%d)!\n", p54u_fwlist[i].fw, err); + usb_put_dev(udev); + } + + return err; +} + +static int p54u_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct ieee80211_hw *dev; + struct p54u_priv *priv; + int err; + unsigned int i, recognized_pipes; + + dev = p54_init_common(sizeof(*priv)); + + if (!dev) { + dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n"); + return -ENOMEM; + } + + priv = dev->priv; + priv->hw_type = P54U_INVALID_HW; + + SET_IEEE80211_DEV(dev, &intf->dev); + usb_set_intfdata(intf, dev); + priv->udev = udev; + priv->intf = intf; + skb_queue_head_init(&priv->rx_queue); + init_usb_anchor(&priv->submitted); + + usb_get_dev(udev); + + /* really lazy and simple way of figuring out if we're a 3887 */ + /* TODO: should just stick the identification in the device table */ + i = intf->altsetting->desc.bNumEndpoints; + recognized_pipes = 0; + while (i--) { + switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) { + case P54U_PIPE_DATA: + case P54U_PIPE_MGMT: + case P54U_PIPE_BRG: + case P54U_PIPE_DEV: + case P54U_PIPE_DATA | USB_DIR_IN: + case P54U_PIPE_MGMT | USB_DIR_IN: + case P54U_PIPE_BRG | USB_DIR_IN: + case P54U_PIPE_DEV | USB_DIR_IN: + case P54U_PIPE_INT | USB_DIR_IN: + recognized_pipes++; + } + } + priv->common.open = p54u_open; + priv->common.stop = p54u_stop; + if (recognized_pipes < P54U_PIPE_NUMBER) { +#ifdef CONFIG_PM + /* ISL3887 needs a full reset on resume */ + udev->reset_resume = 1; +#endif /* CONFIG_PM */ + err = p54u_device_reset(dev); + + priv->hw_type = P54U_3887; + dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr); + priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr); + priv->common.tx = p54u_tx_lm87; + priv->upload_fw = p54u_upload_firmware_3887; + } else { + priv->hw_type = P54U_NET2280; + dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr); + priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr); + priv->common.tx = p54u_tx_net2280; + priv->upload_fw = p54u_upload_firmware_net2280; + } + err = p54u_load_firmware(dev, intf); + if (err) { + usb_put_dev(udev); + p54_free_common(dev); + } + return err; +} + +static void p54u_disconnect(struct usb_interface *intf) +{ + struct ieee80211_hw *dev = usb_get_intfdata(intf); + struct p54u_priv *priv; + + if (!dev) + return; + + priv = dev->priv; + wait_for_completion(&priv->fw_wait_load); + p54_unregister_common(dev); + + usb_put_dev(interface_to_usbdev(intf)); + release_firmware(priv->fw); + p54_free_common(dev); +} + +static int p54u_pre_reset(struct usb_interface *intf) +{ + struct ieee80211_hw *dev = usb_get_intfdata(intf); + + if (!dev) + return -ENODEV; + + p54u_stop(dev); + return 0; +} + +static int p54u_resume(struct usb_interface *intf) +{ + struct ieee80211_hw *dev = usb_get_intfdata(intf); + struct p54u_priv *priv; + + if (!dev) + return -ENODEV; + + priv = dev->priv; + if (unlikely(!(priv->upload_fw && priv->fw))) + return 0; + + return priv->upload_fw(dev); +} + +static int p54u_post_reset(struct usb_interface *intf) +{ + struct ieee80211_hw *dev = usb_get_intfdata(intf); + struct p54u_priv *priv; + int err; + + err = p54u_resume(intf); + if (err) + return err; + + /* reinitialize old device state */ + priv = dev->priv; + if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED) + ieee80211_restart_hw(dev); + + return 0; +} + +#ifdef CONFIG_PM + +static int p54u_suspend(struct usb_interface *intf, pm_message_t message) +{ + return p54u_pre_reset(intf); +} + +#endif /* CONFIG_PM */ + +static struct usb_driver p54u_driver = { + .name = "p54usb", + .id_table = p54u_table, + .probe = p54u_probe, + .disconnect = p54u_disconnect, + .pre_reset = p54u_pre_reset, + .post_reset = p54u_post_reset, +#ifdef CONFIG_PM + .suspend = p54u_suspend, + .resume = p54u_resume, + .reset_resume = p54u_resume, +#endif /* CONFIG_PM */ + .soft_unbind = 1, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(p54u_driver); diff --git a/drivers/net/wireless/intersil/p54/p54usb.h b/drivers/net/wireless/intersil/p54/p54usb.h new file mode 100644 index 000000000000..a5f5f0fea3bd --- /dev/null +++ b/drivers/net/wireless/intersil/p54/p54usb.h @@ -0,0 +1,162 @@ +#ifndef P54USB_H +#define P54USB_H + +/* + * Defines for USB based mac80211 Prism54 driver + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* for isl3886 register definitions used on ver 1 devices */ +#include "p54pci.h" +#include + +/* pci */ +#define NET2280_BASE 0x10000000 +#define NET2280_BASE2 0x20000000 + +/* gpio */ +#define P54U_BRG_POWER_UP (1 << GPIO0_DATA) +#define P54U_BRG_POWER_DOWN (1 << GPIO1_DATA) + +/* devinit */ +#define NET2280_CLK_4Mhz (15 << LOCAL_CLOCK_FREQUENCY) +#define NET2280_CLK_30Mhz (2 << LOCAL_CLOCK_FREQUENCY) +#define NET2280_CLK_60Mhz (1 << LOCAL_CLOCK_FREQUENCY) +#define NET2280_CLK_STOP (0 << LOCAL_CLOCK_FREQUENCY) +#define NET2280_PCI_ENABLE (1 << PCI_ENABLE) +#define NET2280_PCI_SOFT_RESET (1 << PCI_SOFT_RESET) + +/* endpoints */ +#define NET2280_CLEAR_NAK_OUT_PACKETS_MODE (1 << CLEAR_NAK_OUT_PACKETS_MODE) +#define NET2280_FIFO_FLUSH (1 << FIFO_FLUSH) + +/* irq */ +#define NET2280_USB_INTERRUPT_ENABLE (1 << USB_INTERRUPT_ENABLE) +#define NET2280_PCI_INTA_INTERRUPT (1 << PCI_INTA_INTERRUPT) +#define NET2280_PCI_INTA_INTERRUPT_ENABLE (1 << PCI_INTA_INTERRUPT_ENABLE) + +/* registers */ +#define NET2280_DEVINIT 0x00 +#define NET2280_USBIRQENB1 0x24 +#define NET2280_IRQSTAT1 0x2c +#define NET2280_FIFOCTL 0x38 +#define NET2280_GPIOCTL 0x50 +#define NET2280_RELNUM 0x88 +#define NET2280_EPA_RSP 0x324 +#define NET2280_EPA_STAT 0x32c +#define NET2280_EPB_STAT 0x34c +#define NET2280_EPC_RSP 0x364 +#define NET2280_EPC_STAT 0x36c +#define NET2280_EPD_STAT 0x38c + +#define NET2280_EPA_CFG 0x320 +#define NET2280_EPB_CFG 0x340 +#define NET2280_EPC_CFG 0x360 +#define NET2280_EPD_CFG 0x380 +#define NET2280_EPE_CFG 0x3A0 +#define NET2280_EPF_CFG 0x3C0 +#define P54U_DEV_BASE 0x40000000 + +struct net2280_tx_hdr { + __le32 device_addr; + __le16 len; + __le16 follower; /* ? */ + u8 padding[8]; +} __packed; + +struct lm87_tx_hdr { + __le32 device_addr; + __le32 chksum; +} __packed; + +/* Some flags for the isl hardware registers controlling DMA inside the + * chip */ +#define ISL38XX_DMA_STATUS_DONE 0x00000001 +#define ISL38XX_DMA_STATUS_READY 0x00000002 +#define NET2280_EPA_FIFO_PCI_ADDR 0x20000000 +#define ISL38XX_DMA_MASTER_CONTROL_TRIGGER 0x00000004 + +enum net2280_op_type { + NET2280_BRG_U32 = 0x001F, + NET2280_BRG_CFG_U32 = 0x000F, + NET2280_BRG_CFG_U16 = 0x0003, + NET2280_DEV_U32 = 0x080F, + NET2280_DEV_CFG_U32 = 0x088F, + NET2280_DEV_CFG_U16 = 0x0883 +}; + +struct net2280_reg_write { + __le16 port; + __le32 addr; + __le32 val; +} __packed; + +struct net2280_reg_read { + __le16 port; + __le32 addr; +} __packed; + +#define P54U_FW_BLOCK 2048 + +#define X2_SIGNATURE "x2 " +#define X2_SIGNATURE_SIZE 4 + +struct x2_header { + u8 signature[X2_SIGNATURE_SIZE]; + __le32 fw_load_addr; + __le32 fw_length; + __le32 crc; +} __packed; + +/* pipes 3 and 4 are not used by the driver */ +#define P54U_PIPE_NUMBER 9 + +enum p54u_pipe_addr { + P54U_PIPE_DATA = 0x01, + P54U_PIPE_MGMT = 0x02, + P54U_PIPE_3 = 0x03, + P54U_PIPE_4 = 0x04, + P54U_PIPE_BRG = 0x0d, + P54U_PIPE_DEV = 0x0e, + P54U_PIPE_INT = 0x0f +}; + +struct p54u_rx_info { + struct urb *urb; + struct ieee80211_hw *dev; +}; + +enum p54u_hw_type { + P54U_INVALID_HW, + P54U_NET2280, + P54U_3887, + + /* keep last */ + __NUM_P54U_HWTYPES, +}; + +struct p54u_priv { + struct p54_common common; + struct usb_device *udev; + struct usb_interface *intf; + int (*upload_fw)(struct ieee80211_hw *dev); + + enum p54u_hw_type hw_type; + spinlock_t lock; + struct sk_buff_head rx_queue; + struct usb_anchor submitted; + const struct firmware *fw; + + /* asynchronous firmware callback */ + struct completion fw_wait_load; +}; + +#endif /* P54USB_H */ diff --git a/drivers/net/wireless/intersil/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c new file mode 100644 index 000000000000..24e5ff9a9272 --- /dev/null +++ b/drivers/net/wireless/intersil/p54/txrx.c @@ -0,0 +1,940 @@ +/* + * Common code for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2007-2009, Christian Lamparter + * Copyright 2008, Johannes Berg + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include + +#include "p54.h" +#include "lmac.h" + +#ifdef P54_MM_DEBUG +static void p54_dump_tx_queue(struct p54_common *priv) +{ + unsigned long flags; + struct ieee80211_tx_info *info; + struct p54_tx_info *range; + struct sk_buff *skb; + struct p54_hdr *hdr; + unsigned int i = 0; + u32 prev_addr; + u32 largest_hole = 0, free; + + spin_lock_irqsave(&priv->tx_queue.lock, flags); + wiphy_debug(priv->hw->wiphy, "/ --- tx queue dump (%d entries) ---\n", + skb_queue_len(&priv->tx_queue)); + + prev_addr = priv->rx_start; + skb_queue_walk(&priv->tx_queue, skb) { + info = IEEE80211_SKB_CB(skb); + range = (void *) info->rate_driver_data; + hdr = (void *) skb->data; + + free = range->start_addr - prev_addr; + wiphy_debug(priv->hw->wiphy, + "| [%02d] => [skb:%p skb_len:0x%04x " + "hdr:{flags:%02x len:%04x req_id:%04x type:%02x} " + "mem:{start:%04x end:%04x, free:%d}]\n", + i++, skb, skb->len, + le16_to_cpu(hdr->flags), le16_to_cpu(hdr->len), + le32_to_cpu(hdr->req_id), le16_to_cpu(hdr->type), + range->start_addr, range->end_addr, free); + + prev_addr = range->end_addr; + largest_hole = max(largest_hole, free); + } + free = priv->rx_end - prev_addr; + largest_hole = max(largest_hole, free); + wiphy_debug(priv->hw->wiphy, + "\\ --- [free: %d], largest free block: %d ---\n", + free, largest_hole); + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); +} +#endif /* P54_MM_DEBUG */ + +/* + * So, the firmware is somewhat stupid and doesn't know what places in its + * memory incoming data should go to. By poking around in the firmware, we + * can find some unused memory to upload our packets to. However, data that we + * want the card to TX needs to stay intact until the card has told us that + * it is done with it. This function finds empty places we can upload to and + * marks allocated areas as reserved if necessary. p54_find_and_unlink_skb or + * p54_free_skb frees allocated areas. + */ +static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb) +{ + struct sk_buff *entry, *target_skb = NULL; + struct ieee80211_tx_info *info; + struct p54_tx_info *range; + struct p54_hdr *data = (void *) skb->data; + unsigned long flags; + u32 last_addr = priv->rx_start; + u32 target_addr = priv->rx_start; + u16 len = priv->headroom + skb->len + priv->tailroom + 3; + + info = IEEE80211_SKB_CB(skb); + range = (void *) info->rate_driver_data; + len = (range->extra_len + len) & ~0x3; + + spin_lock_irqsave(&priv->tx_queue.lock, flags); + if (unlikely(skb_queue_len(&priv->tx_queue) == 32)) { + /* + * The tx_queue is now really full. + * + * TODO: check if the device has crashed and reset it. + */ + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + return -EBUSY; + } + + skb_queue_walk(&priv->tx_queue, entry) { + u32 hole_size; + info = IEEE80211_SKB_CB(entry); + range = (void *) info->rate_driver_data; + hole_size = range->start_addr - last_addr; + + if (!target_skb && hole_size >= len) { + target_skb = entry->prev; + hole_size -= len; + target_addr = last_addr; + break; + } + last_addr = range->end_addr; + } + if (unlikely(!target_skb)) { + if (priv->rx_end - last_addr >= len) { + target_skb = priv->tx_queue.prev; + if (!skb_queue_empty(&priv->tx_queue)) { + info = IEEE80211_SKB_CB(target_skb); + range = (void *)info->rate_driver_data; + target_addr = range->end_addr; + } + } else { + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + return -ENOSPC; + } + } + + info = IEEE80211_SKB_CB(skb); + range = (void *) info->rate_driver_data; + range->start_addr = target_addr; + range->end_addr = target_addr + len; + data->req_id = cpu_to_le32(target_addr + priv->headroom); + if (IS_DATA_FRAME(skb) && + unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) + priv->beacon_req_id = data->req_id; + + __skb_queue_after(&priv->tx_queue, target_skb, skb); + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + return 0; +} + +static void p54_tx_pending(struct p54_common *priv) +{ + struct sk_buff *skb; + int ret; + + skb = skb_dequeue(&priv->tx_pending); + if (unlikely(!skb)) + return ; + + ret = p54_assign_address(priv, skb); + if (unlikely(ret)) + skb_queue_head(&priv->tx_pending, skb); + else + priv->tx(priv->hw, skb); +} + +static void p54_wake_queues(struct p54_common *priv) +{ + unsigned long flags; + unsigned int i; + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) + return ; + + p54_tx_pending(priv); + + spin_lock_irqsave(&priv->tx_stats_lock, flags); + for (i = 0; i < priv->hw->queues; i++) { + if (priv->tx_stats[i + P54_QUEUE_DATA].len < + priv->tx_stats[i + P54_QUEUE_DATA].limit) + ieee80211_wake_queue(priv->hw, i); + } + spin_unlock_irqrestore(&priv->tx_stats_lock, flags); +} + +static int p54_tx_qos_accounting_alloc(struct p54_common *priv, + struct sk_buff *skb, + const u16 p54_queue) +{ + struct p54_tx_queue_stats *queue; + unsigned long flags; + + if (WARN_ON(p54_queue >= P54_QUEUE_NUM)) + return -EINVAL; + + queue = &priv->tx_stats[p54_queue]; + + spin_lock_irqsave(&priv->tx_stats_lock, flags); + if (unlikely(queue->len >= queue->limit && IS_QOS_QUEUE(p54_queue))) { + spin_unlock_irqrestore(&priv->tx_stats_lock, flags); + return -ENOSPC; + } + + queue->len++; + queue->count++; + + if (unlikely(queue->len == queue->limit && IS_QOS_QUEUE(p54_queue))) { + u16 ac_queue = p54_queue - P54_QUEUE_DATA; + ieee80211_stop_queue(priv->hw, ac_queue); + } + + spin_unlock_irqrestore(&priv->tx_stats_lock, flags); + return 0; +} + +static void p54_tx_qos_accounting_free(struct p54_common *priv, + struct sk_buff *skb) +{ + if (IS_DATA_FRAME(skb)) { + unsigned long flags; + + spin_lock_irqsave(&priv->tx_stats_lock, flags); + priv->tx_stats[GET_HW_QUEUE(skb)].len--; + spin_unlock_irqrestore(&priv->tx_stats_lock, flags); + + if (unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) { + if (priv->beacon_req_id == GET_REQ_ID(skb)) { + /* this is the active beacon set anymore */ + priv->beacon_req_id = 0; + } + complete(&priv->beacon_comp); + } + } + p54_wake_queues(priv); +} + +void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct p54_common *priv = dev->priv; + if (unlikely(!skb)) + return ; + + skb_unlink(skb, &priv->tx_queue); + p54_tx_qos_accounting_free(priv, skb); + ieee80211_free_txskb(dev, skb); +} +EXPORT_SYMBOL_GPL(p54_free_skb); + +static struct sk_buff *p54_find_and_unlink_skb(struct p54_common *priv, + const __le32 req_id) +{ + struct sk_buff *entry; + unsigned long flags; + + spin_lock_irqsave(&priv->tx_queue.lock, flags); + skb_queue_walk(&priv->tx_queue, entry) { + struct p54_hdr *hdr = (struct p54_hdr *) entry->data; + + if (hdr->req_id == req_id) { + __skb_unlink(entry, &priv->tx_queue); + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + p54_tx_qos_accounting_free(priv, entry); + return entry; + } + } + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + return NULL; +} + +void p54_tx(struct p54_common *priv, struct sk_buff *skb) +{ + skb_queue_tail(&priv->tx_pending, skb); + p54_tx_pending(priv); +} + +static int p54_rssi_to_dbm(struct p54_common *priv, int rssi) +{ + if (priv->rxhw != 5) { + return ((rssi * priv->cur_rssi->mul) / 64 + + priv->cur_rssi->add) / 4; + } else { + /* + * TODO: find the correct formula + */ + return rssi / 2 - 110; + } +} + +/* + * Even if the firmware is capable of dealing with incoming traffic, + * while dozing, we have to prepared in case mac80211 uses PS-POLL + * to retrieve outstanding frames from our AP. + * (see comment in net/mac80211/mlme.c @ line 1993) + */ +static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (void *) skb->data; + struct ieee80211_tim_ie *tim_ie; + u8 *tim; + u8 tim_len; + bool new_psm; + + /* only beacons have a TIM IE */ + if (!ieee80211_is_beacon(hdr->frame_control)) + return; + + if (!priv->aid) + return; + + /* only consider beacons from the associated BSSID */ + if (!ether_addr_equal_64bits(hdr->addr3, priv->bssid)) + return; + + tim = p54_find_ie(skb, WLAN_EID_TIM); + if (!tim) + return; + + tim_len = tim[1]; + tim_ie = (struct ieee80211_tim_ie *) &tim[2]; + + new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid); + if (new_psm != priv->powersave_override) { + priv->powersave_override = new_psm; + p54_set_ps(priv); + } +} + +static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb) +{ + struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data; + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + u16 freq = le16_to_cpu(hdr->freq); + size_t header_len = sizeof(*hdr); + u32 tsf32; + u8 rate = hdr->rate & 0xf; + + /* + * If the device is in a unspecified state we have to + * ignore all data frames. Else we could end up with a + * nasty crash. + */ + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) + return 0; + + if (!(hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_IN_FCS_GOOD))) + return 0; + + if (hdr->decrypt_status == P54_DECRYPT_OK) + rx_status->flag |= RX_FLAG_DECRYPTED; + if ((hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL) || + (hdr->decrypt_status == P54_DECRYPT_FAIL_TKIP)) + rx_status->flag |= RX_FLAG_MMIC_ERROR; + + rx_status->signal = p54_rssi_to_dbm(priv, hdr->rssi); + if (hdr->rate & 0x10) + rx_status->flag |= RX_FLAG_SHORTPRE; + if (priv->hw->conf.chandef.chan->band == IEEE80211_BAND_5GHZ) + rx_status->rate_idx = (rate < 4) ? 0 : rate - 4; + else + rx_status->rate_idx = rate; + + rx_status->freq = freq; + rx_status->band = priv->hw->conf.chandef.chan->band; + rx_status->antenna = hdr->antenna; + + tsf32 = le32_to_cpu(hdr->tsf32); + if (tsf32 < priv->tsf_low32) + priv->tsf_high32++; + rx_status->mactime = ((u64)priv->tsf_high32) << 32 | tsf32; + priv->tsf_low32 = tsf32; + + /* LMAC API Page 10/29 - s_lm_data_in - clock + * "usec accurate timestamp of hardware clock + * at end of frame (before OFDM SIFS EOF padding" + */ + rx_status->flag |= RX_FLAG_MACTIME_END; + + if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) + header_len += hdr->align[0]; + + skb_pull(skb, header_len); + skb_trim(skb, le16_to_cpu(hdr->len)); + if (unlikely(priv->hw->conf.flags & IEEE80211_CONF_PS)) + p54_pspoll_workaround(priv, skb); + + ieee80211_rx_irqsafe(priv->hw, skb); + + ieee80211_queue_delayed_work(priv->hw, &priv->work, + msecs_to_jiffies(P54_STATISTICS_UPDATE)); + + return -1; +} + +static void p54_rx_frame_sent(struct p54_common *priv, struct sk_buff *skb) +{ + struct p54_hdr *hdr = (struct p54_hdr *) skb->data; + struct p54_frame_sent *payload = (struct p54_frame_sent *) hdr->data; + struct ieee80211_tx_info *info; + struct p54_hdr *entry_hdr; + struct p54_tx_data *entry_data; + struct sk_buff *entry; + unsigned int pad = 0, frame_len; + int count, idx; + + entry = p54_find_and_unlink_skb(priv, hdr->req_id); + if (unlikely(!entry)) + return ; + + frame_len = entry->len; + info = IEEE80211_SKB_CB(entry); + entry_hdr = (struct p54_hdr *) entry->data; + entry_data = (struct p54_tx_data *) entry_hdr->data; + priv->stats.dot11ACKFailureCount += payload->tries - 1; + + /* + * Frames in P54_QUEUE_FWSCAN and P54_QUEUE_BEACON are + * generated by the driver. Therefore tx_status is bogus + * and we don't want to confuse the mac80211 stack. + */ + if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) { + dev_kfree_skb_any(entry); + return ; + } + + /* + * Clear manually, ieee80211_tx_info_clear_status would + * clear the counts too and we need them. + */ + memset(&info->status.ack_signal, 0, + sizeof(struct ieee80211_tx_info) - + offsetof(struct ieee80211_tx_info, status.ack_signal)); + BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, + status.ack_signal) != 20); + + if (entry_hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) + pad = entry_data->align[0]; + + /* walk through the rates array and adjust the counts */ + count = payload->tries; + for (idx = 0; idx < 4; idx++) { + if (count >= info->status.rates[idx].count) { + count -= info->status.rates[idx].count; + } else if (count > 0) { + info->status.rates[idx].count = count; + count = 0; + } else { + info->status.rates[idx].idx = -1; + info->status.rates[idx].count = 0; + } + } + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && + !(payload->status & P54_TX_FAILED)) + info->flags |= IEEE80211_TX_STAT_ACK; + if (payload->status & P54_TX_PSM_CANCELLED) + info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + info->status.ack_signal = p54_rssi_to_dbm(priv, + (int)payload->ack_rssi); + + /* Undo all changes to the frame. */ + switch (entry_data->key_type) { + case P54_CRYPTO_TKIPMICHAEL: { + u8 *iv = (u8 *)(entry_data->align + pad + + entry_data->crypt_offset); + + /* Restore the original TKIP IV. */ + iv[2] = iv[0]; + iv[0] = iv[1]; + iv[1] = (iv[0] | 0x20) & 0x7f; /* WEPSeed - 8.3.2.2 */ + + frame_len -= 12; /* remove TKIP_MMIC + TKIP_ICV */ + break; + } + case P54_CRYPTO_AESCCMP: + frame_len -= 8; /* remove CCMP_MIC */ + break; + case P54_CRYPTO_WEP: + frame_len -= 4; /* remove WEP_ICV */ + break; + } + + skb_trim(entry, frame_len); + skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data)); + ieee80211_tx_status_irqsafe(priv->hw, entry); +} + +static void p54_rx_eeprom_readback(struct p54_common *priv, + struct sk_buff *skb) +{ + struct p54_hdr *hdr = (struct p54_hdr *) skb->data; + struct p54_eeprom_lm86 *eeprom = (struct p54_eeprom_lm86 *) hdr->data; + struct sk_buff *tmp; + + if (!priv->eeprom) + return ; + + if (priv->fw_var >= 0x509) { + memcpy(priv->eeprom, eeprom->v2.data, + le16_to_cpu(eeprom->v2.len)); + } else { + memcpy(priv->eeprom, eeprom->v1.data, + le16_to_cpu(eeprom->v1.len)); + } + + priv->eeprom = NULL; + tmp = p54_find_and_unlink_skb(priv, hdr->req_id); + dev_kfree_skb_any(tmp); + complete(&priv->eeprom_comp); +} + +static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb) +{ + struct p54_hdr *hdr = (struct p54_hdr *) skb->data; + struct p54_statistics *stats = (struct p54_statistics *) hdr->data; + struct sk_buff *tmp; + struct ieee80211_channel *chan; + unsigned int i, rssi, tx, cca, dtime, dtotal, dcca, dtx, drssi, unit; + u32 tsf32; + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) + return ; + + tsf32 = le32_to_cpu(stats->tsf32); + if (tsf32 < priv->tsf_low32) + priv->tsf_high32++; + priv->tsf_low32 = tsf32; + + priv->stats.dot11RTSFailureCount = le32_to_cpu(stats->rts_fail); + priv->stats.dot11RTSSuccessCount = le32_to_cpu(stats->rts_success); + priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs); + + priv->noise = p54_rssi_to_dbm(priv, le32_to_cpu(stats->noise)); + + /* + * STSW450X LMAC API page 26 - 3.8 Statistics + * "The exact measurement period can be derived from the + * timestamp member". + */ + dtime = tsf32 - priv->survey_raw.timestamp; + + /* + * STSW450X LMAC API page 26 - 3.8.1 Noise histogram + * The LMAC samples RSSI, CCA and transmit state at regular + * periods (typically 8 times per 1k [as in 1024] usec). + */ + cca = le32_to_cpu(stats->sample_cca); + tx = le32_to_cpu(stats->sample_tx); + rssi = 0; + for (i = 0; i < ARRAY_SIZE(stats->sample_noise); i++) + rssi += le32_to_cpu(stats->sample_noise[i]); + + dcca = cca - priv->survey_raw.cached_cca; + drssi = rssi - priv->survey_raw.cached_rssi; + dtx = tx - priv->survey_raw.cached_tx; + dtotal = dcca + drssi + dtx; + + /* + * update statistics when more than a second is over since the + * last call, or when a update is badly needed. + */ + if (dtotal && (priv->update_stats || dtime >= USEC_PER_SEC) && + dtime >= dtotal) { + priv->survey_raw.timestamp = tsf32; + priv->update_stats = false; + unit = dtime / dtotal; + + if (dcca) { + priv->survey_raw.cca += dcca * unit; + priv->survey_raw.cached_cca = cca; + } + if (dtx) { + priv->survey_raw.tx += dtx * unit; + priv->survey_raw.cached_tx = tx; + } + if (drssi) { + priv->survey_raw.rssi += drssi * unit; + priv->survey_raw.cached_rssi = rssi; + } + + /* 1024 usec / 8 times = 128 usec / time */ + if (!(priv->phy_ps || priv->phy_idle)) + priv->survey_raw.active += dtotal * unit; + else + priv->survey_raw.active += (dcca + dtx) * unit; + } + + chan = priv->curchan; + if (chan) { + struct survey_info *survey = &priv->survey[chan->hw_value]; + survey->noise = clamp(priv->noise, -128, 127); + survey->time = priv->survey_raw.active; + survey->time_tx = priv->survey_raw.tx; + survey->time_busy = priv->survey_raw.tx + + priv->survey_raw.cca; + do_div(survey->time, 1024); + do_div(survey->time_tx, 1024); + do_div(survey->time_busy, 1024); + } + + tmp = p54_find_and_unlink_skb(priv, hdr->req_id); + dev_kfree_skb_any(tmp); + complete(&priv->stat_comp); +} + +static void p54_rx_trap(struct p54_common *priv, struct sk_buff *skb) +{ + struct p54_hdr *hdr = (struct p54_hdr *) skb->data; + struct p54_trap *trap = (struct p54_trap *) hdr->data; + u16 event = le16_to_cpu(trap->event); + u16 freq = le16_to_cpu(trap->frequency); + + switch (event) { + case P54_TRAP_BEACON_TX: + break; + case P54_TRAP_RADAR: + wiphy_info(priv->hw->wiphy, "radar (freq:%d MHz)\n", freq); + break; + case P54_TRAP_NO_BEACON: + if (priv->vif) + ieee80211_beacon_loss(priv->vif); + break; + case P54_TRAP_SCAN: + break; + case P54_TRAP_TBTT: + break; + case P54_TRAP_TIMER: + break; + case P54_TRAP_FAA_RADIO_OFF: + wiphy_rfkill_set_hw_state(priv->hw->wiphy, true); + break; + case P54_TRAP_FAA_RADIO_ON: + wiphy_rfkill_set_hw_state(priv->hw->wiphy, false); + break; + default: + wiphy_info(priv->hw->wiphy, "received event:%x freq:%d\n", + event, freq); + break; + } +} + +static int p54_rx_control(struct p54_common *priv, struct sk_buff *skb) +{ + struct p54_hdr *hdr = (struct p54_hdr *) skb->data; + + switch (le16_to_cpu(hdr->type)) { + case P54_CONTROL_TYPE_TXDONE: + p54_rx_frame_sent(priv, skb); + break; + case P54_CONTROL_TYPE_TRAP: + p54_rx_trap(priv, skb); + break; + case P54_CONTROL_TYPE_BBP: + break; + case P54_CONTROL_TYPE_STAT_READBACK: + p54_rx_stats(priv, skb); + break; + case P54_CONTROL_TYPE_EEPROM_READBACK: + p54_rx_eeprom_readback(priv, skb); + break; + default: + wiphy_debug(priv->hw->wiphy, + "not handling 0x%02x type control frame\n", + le16_to_cpu(hdr->type)); + break; + } + return 0; +} + +/* returns zero if skb can be reused */ +int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct p54_common *priv = dev->priv; + u16 type = le16_to_cpu(*((__le16 *)skb->data)); + + if (type & P54_HDR_FLAG_CONTROL) + return p54_rx_control(priv, skb); + else + return p54_rx_data(priv, skb); +} +EXPORT_SYMBOL_GPL(p54_rx); + +static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + u8 *queue, u32 *extra_len, u16 *flags, u16 *aid, + bool *burst_possible) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (ieee80211_is_data_qos(hdr->frame_control)) + *burst_possible = true; + else + *burst_possible = false; + + if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) + *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR; + + if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER) + *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; + + if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) + *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; + + *queue = skb_get_queue_mapping(skb) + P54_QUEUE_DATA; + + switch (priv->mode) { + case NL80211_IFTYPE_MONITOR: + /* + * We have to set P54_HDR_FLAG_DATA_OUT_PROMISC for + * every frame in promiscuous/monitor mode. + * see STSW45x0C LMAC API - page 12. + */ + *aid = 0; + *flags |= P54_HDR_FLAG_DATA_OUT_PROMISC; + break; + case NL80211_IFTYPE_STATION: + *aid = 1; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { + *aid = 0; + *queue = P54_QUEUE_CAB; + return; + } + + if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) { + if (ieee80211_is_probe_resp(hdr->frame_control)) { + *aid = 0; + *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP | + P54_HDR_FLAG_DATA_OUT_NOCANCEL; + return; + } else if (ieee80211_is_beacon(hdr->frame_control)) { + *aid = 0; + + if (info->flags & IEEE80211_TX_CTL_INJECTED) { + /* + * Injecting beacons on top of a AP is + * not a good idea... nevertheless, + * it should be doable. + */ + + return; + } + + *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP; + *queue = P54_QUEUE_BEACON; + *extra_len = IEEE80211_MAX_TIM_LEN; + return; + } + } + + if (sta) + *aid = sta->aid; + break; + } +} + +static u8 p54_convert_algo(u32 cipher) +{ + switch (cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + return P54_CRYPTO_WEP; + case WLAN_CIPHER_SUITE_TKIP: + return P54_CRYPTO_TKIPMICHAEL; + case WLAN_CIPHER_SUITE_CCMP: + return P54_CRYPTO_AESCCMP; + default: + return 0; + } +} + +void p54_tx_80211(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct p54_common *priv = dev->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct p54_tx_info *p54info; + struct p54_hdr *hdr; + struct p54_tx_data *txhdr; + unsigned int padding, len, extra_len = 0; + int i, j, ridx; + u16 hdr_flags = 0, aid = 0; + u8 rate, queue = 0, crypt_offset = 0; + u8 cts_rate = 0x20; + u8 rc_flags; + u8 calculated_tries[4]; + u8 nrates = 0, nremaining = 8; + bool burst_allowed = false; + + p54_tx_80211_header(priv, skb, info, control->sta, &queue, &extra_len, + &hdr_flags, &aid, &burst_allowed); + + if (p54_tx_qos_accounting_alloc(priv, skb, queue)) { + ieee80211_free_txskb(dev, skb); + return; + } + + padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3; + len = skb->len; + + if (info->control.hw_key) { + crypt_offset = ieee80211_get_hdrlen_from_skb(skb); + if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { + u8 *iv = (u8 *)(skb->data + crypt_offset); + /* + * The firmware excepts that the IV has to have + * this special format + */ + iv[1] = iv[0]; + iv[0] = iv[2]; + iv[2] = 0; + } + } + + txhdr = (struct p54_tx_data *) skb_push(skb, sizeof(*txhdr) + padding); + hdr = (struct p54_hdr *) skb_push(skb, sizeof(*hdr)); + + if (padding) + hdr_flags |= P54_HDR_FLAG_DATA_ALIGN; + hdr->type = cpu_to_le16(aid); + hdr->rts_tries = info->control.rates[0].count; + + /* + * we register the rates in perfect order, and + * RTS/CTS won't happen on 5 GHz + */ + cts_rate = info->control.rts_cts_rate_idx; + + memset(&txhdr->rateset, 0, sizeof(txhdr->rateset)); + + /* see how many rates got used */ + for (i = 0; i < dev->max_rates; i++) { + if (info->control.rates[i].idx < 0) + break; + nrates++; + } + + /* limit tries to 8/nrates per rate */ + for (i = 0; i < nrates; i++) { + /* + * The magic expression here is equivalent to 8/nrates for + * all values that matter, but avoids division and jumps. + * Note that nrates can only take the values 1 through 4. + */ + calculated_tries[i] = min_t(int, ((15 >> nrates) | 1) + 1, + info->control.rates[i].count); + nremaining -= calculated_tries[i]; + } + + /* if there are tries left, distribute from back to front */ + for (i = nrates - 1; nremaining > 0 && i >= 0; i--) { + int tmp = info->control.rates[i].count - calculated_tries[i]; + + if (tmp <= 0) + continue; + /* RC requested more tries at this rate */ + + tmp = min_t(int, tmp, nremaining); + calculated_tries[i] += tmp; + nremaining -= tmp; + } + + ridx = 0; + for (i = 0; i < nrates && ridx < 8; i++) { + /* we register the rates in perfect order */ + rate = info->control.rates[i].idx; + if (info->band == IEEE80211_BAND_5GHZ) + rate += 4; + + /* store the count we actually calculated for TX status */ + info->control.rates[i].count = calculated_tries[i]; + + rc_flags = info->control.rates[i].flags; + if (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) { + rate |= 0x10; + cts_rate |= 0x10; + } + if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { + burst_allowed = false; + rate |= 0x40; + } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + rate |= 0x20; + burst_allowed = false; + } + for (j = 0; j < calculated_tries[i] && ridx < 8; j++) { + txhdr->rateset[ridx] = rate; + ridx++; + } + } + + if (burst_allowed) + hdr_flags |= P54_HDR_FLAG_DATA_OUT_BURST; + + /* TODO: enable bursting */ + hdr->flags = cpu_to_le16(hdr_flags); + hdr->tries = ridx; + txhdr->rts_rate_idx = 0; + if (info->control.hw_key) { + txhdr->key_type = p54_convert_algo(info->control.hw_key->cipher); + txhdr->key_len = min((u8)16, info->control.hw_key->keylen); + memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len); + if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { + /* reserve space for the MIC key */ + len += 8; + memcpy(skb_put(skb, 8), &(info->control.hw_key->key + [NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]), 8); + } + /* reserve some space for ICV */ + len += info->control.hw_key->icv_len; + memset(skb_put(skb, info->control.hw_key->icv_len), 0, + info->control.hw_key->icv_len); + } else { + txhdr->key_type = 0; + txhdr->key_len = 0; + } + txhdr->crypt_offset = crypt_offset; + txhdr->hw_queue = queue; + txhdr->backlog = priv->tx_stats[queue].len - 1; + memset(txhdr->durations, 0, sizeof(txhdr->durations)); + txhdr->tx_antenna = 2 & priv->tx_diversity_mask; + if (priv->rxhw == 5) { + txhdr->longbow.cts_rate = cts_rate; + txhdr->longbow.output_power = cpu_to_le16(priv->output_power); + } else { + txhdr->normal.output_power = priv->output_power; + txhdr->normal.cts_rate = cts_rate; + } + if (padding) + txhdr->align[0] = padding; + + hdr->len = cpu_to_le16(len); + /* modifies skb->cb and with it info, so must be last! */ + p54info = (void *) info->rate_driver_data; + p54info->extra_len = extra_len; + + p54_tx(priv, skb); +} diff --git a/drivers/net/wireless/p54/Kconfig b/drivers/net/wireless/p54/Kconfig deleted file mode 100644 index cdafb8c73e82..000000000000 --- a/drivers/net/wireless/p54/Kconfig +++ /dev/null @@ -1,71 +0,0 @@ -config P54_COMMON - tristate "Softmac Prism54 support" - depends on MAC80211 - select FW_LOADER - select CRC_CCITT - ---help--- - This is common code for isl38xx/stlc45xx based modules. - This module does nothing by itself - the USB/PCI/SPI front-ends - also need to be enabled in order to support any devices. - - These devices require softmac firmware which can be found at - - - If you choose to build a module, it'll be called p54common. - -config P54_USB - tristate "Prism54 USB support" - depends on P54_COMMON && USB - select CRC32 - ---help--- - This driver is for USB isl38xx based wireless cards. - - These devices require softmac firmware which can be found at - - - If you choose to build a module, it'll be called p54usb. - -config P54_PCI - tristate "Prism54 PCI support" - depends on P54_COMMON && PCI - ---help--- - This driver is for PCI isl38xx based wireless cards. - This driver supports most devices that are supported by the - fullmac prism54 driver plus many devices which are not - supported by the fullmac driver/firmware. - - This driver requires softmac firmware which can be found at - - - If you choose to build a module, it'll be called p54pci. - -config P54_SPI - tristate "Prism54 SPI (stlc45xx) support" - depends on P54_COMMON && SPI_MASTER - ---help--- - This driver is for stlc4550 or stlc4560 based wireless chips - such as Nokia's N800/N810 Portable Internet Tablet. - - If you choose to build a module, it'll be called p54spi. - -config P54_SPI_DEFAULT_EEPROM - bool "Include fallback EEPROM blob" - depends on P54_SPI - default n - ---help--- - Unlike the PCI or USB devices, the SPI variants don't have - a dedicated EEPROM chip to store all device specific values - for calibration, country and interface settings. - - The driver will try to load the image "3826.eeprom", if the - file is put at the right place. (usually /lib/firmware.) - - Only if this request fails, this option will provide a - backup set of generic values to get the device working. - - Enabling this option adds about 4k to p54spi. - -config P54_LEDS - bool - depends on P54_COMMON && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = P54_COMMON) - default y diff --git a/drivers/net/wireless/p54/Makefile b/drivers/net/wireless/p54/Makefile deleted file mode 100644 index b542e68f1781..000000000000 --- a/drivers/net/wireless/p54/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -p54common-objs := eeprom.o fwio.o txrx.o main.o -p54common-$(CONFIG_P54_LEDS) += led.o - -obj-$(CONFIG_P54_COMMON) += p54common.o -obj-$(CONFIG_P54_USB) += p54usb.o -obj-$(CONFIG_P54_PCI) += p54pci.o -obj-$(CONFIG_P54_SPI) += p54spi.o diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c deleted file mode 100644 index 2fe713eda7ad..000000000000 --- a/drivers/net/wireless/p54/eeprom.c +++ /dev/null @@ -1,982 +0,0 @@ -/* - * EEPROM parser code for mac80211 Prism54 drivers - * - * Copyright (c) 2006, Michael Wu - * Copyright (c) 2007-2009, Christian Lamparter - * Copyright 2008, Johannes Berg - * - * Based on: - * - the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - stlc45xx driver - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include "p54.h" -#include "eeprom.h" -#include "lmac.h" - -static struct ieee80211_rate p54_bgrates[] = { - { .bitrate = 10, .hw_value = 0, }, - { .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 60, .hw_value = 4, }, - { .bitrate = 90, .hw_value = 5, }, - { .bitrate = 120, .hw_value = 6, }, - { .bitrate = 180, .hw_value = 7, }, - { .bitrate = 240, .hw_value = 8, }, - { .bitrate = 360, .hw_value = 9, }, - { .bitrate = 480, .hw_value = 10, }, - { .bitrate = 540, .hw_value = 11, }, -}; - -static struct ieee80211_rate p54_arates[] = { - { .bitrate = 60, .hw_value = 4, }, - { .bitrate = 90, .hw_value = 5, }, - { .bitrate = 120, .hw_value = 6, }, - { .bitrate = 180, .hw_value = 7, }, - { .bitrate = 240, .hw_value = 8, }, - { .bitrate = 360, .hw_value = 9, }, - { .bitrate = 480, .hw_value = 10, }, - { .bitrate = 540, .hw_value = 11, }, -}; - -static struct p54_rssi_db_entry p54_rssi_default = { - /* - * The defaults are taken from usb-logs of the - * vendor driver. So, they should be safe to - * use in case we can't get a match from the - * rssi <-> dBm conversion database. - */ - .mul = 130, - .add = -398, -}; - -#define CHAN_HAS_CAL BIT(0) -#define CHAN_HAS_LIMIT BIT(1) -#define CHAN_HAS_CURVE BIT(2) -#define CHAN_HAS_ALL (CHAN_HAS_CAL | CHAN_HAS_LIMIT | CHAN_HAS_CURVE) - -struct p54_channel_entry { - u16 freq; - u16 data; - int index; - int max_power; - enum ieee80211_band band; -}; - -struct p54_channel_list { - struct p54_channel_entry *channels; - size_t entries; - size_t max_entries; - size_t band_channel_num[IEEE80211_NUM_BANDS]; -}; - -static int p54_get_band_from_freq(u16 freq) -{ - /* FIXME: sync these values with the 802.11 spec */ - - if ((freq >= 2412) && (freq <= 2484)) - return IEEE80211_BAND_2GHZ; - - if ((freq >= 4920) && (freq <= 5825)) - return IEEE80211_BAND_5GHZ; - - return -1; -} - -static int same_band(u16 freq, u16 freq2) -{ - return p54_get_band_from_freq(freq) == p54_get_band_from_freq(freq2); -} - -static int p54_compare_channels(const void *_a, - const void *_b) -{ - const struct p54_channel_entry *a = _a; - const struct p54_channel_entry *b = _b; - - return a->freq - b->freq; -} - -static int p54_compare_rssichan(const void *_a, - const void *_b) -{ - const struct p54_rssi_db_entry *a = _a; - const struct p54_rssi_db_entry *b = _b; - - return a->freq - b->freq; -} - -static int p54_fill_band_bitrates(struct ieee80211_hw *dev, - struct ieee80211_supported_band *band_entry, - enum ieee80211_band band) -{ - /* TODO: generate rate array dynamically */ - - switch (band) { - case IEEE80211_BAND_2GHZ: - band_entry->bitrates = p54_bgrates; - band_entry->n_bitrates = ARRAY_SIZE(p54_bgrates); - break; - case IEEE80211_BAND_5GHZ: - band_entry->bitrates = p54_arates; - band_entry->n_bitrates = ARRAY_SIZE(p54_arates); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int p54_generate_band(struct ieee80211_hw *dev, - struct p54_channel_list *list, - unsigned int *chan_num, - enum ieee80211_band band) -{ - struct p54_common *priv = dev->priv; - struct ieee80211_supported_band *tmp, *old; - unsigned int i, j; - int ret = -ENOMEM; - - if ((!list->entries) || (!list->band_channel_num[band])) - return -EINVAL; - - tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); - if (!tmp) - goto err_out; - - tmp->channels = kzalloc(sizeof(struct ieee80211_channel) * - list->band_channel_num[band], GFP_KERNEL); - if (!tmp->channels) - goto err_out; - - ret = p54_fill_band_bitrates(dev, tmp, band); - if (ret) - goto err_out; - - for (i = 0, j = 0; (j < list->band_channel_num[band]) && - (i < list->entries); i++) { - struct p54_channel_entry *chan = &list->channels[i]; - struct ieee80211_channel *dest = &tmp->channels[j]; - - if (chan->band != band) - continue; - - if (chan->data != CHAN_HAS_ALL) { - wiphy_err(dev->wiphy, "%s%s%s is/are missing for " - "channel:%d [%d MHz].\n", - (chan->data & CHAN_HAS_CAL ? "" : - " [iqauto calibration data]"), - (chan->data & CHAN_HAS_LIMIT ? "" : - " [output power limits]"), - (chan->data & CHAN_HAS_CURVE ? "" : - " [curve data]"), - chan->index, chan->freq); - continue; - } - - dest->band = chan->band; - dest->center_freq = chan->freq; - dest->max_power = chan->max_power; - priv->survey[*chan_num].channel = &tmp->channels[j]; - priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_TIME | - SURVEY_INFO_TIME_BUSY | - SURVEY_INFO_TIME_TX; - dest->hw_value = (*chan_num); - j++; - (*chan_num)++; - } - - if (j == 0) { - wiphy_err(dev->wiphy, "Disabling totally damaged %d GHz band\n", - (band == IEEE80211_BAND_2GHZ) ? 2 : 5); - - ret = -ENODATA; - goto err_out; - } - - tmp->n_channels = j; - old = priv->band_table[band]; - priv->band_table[band] = tmp; - if (old) { - kfree(old->channels); - kfree(old); - } - - return 0; - -err_out: - if (tmp) { - kfree(tmp->channels); - kfree(tmp); - } - - return ret; -} - -static struct p54_channel_entry *p54_update_channel_param(struct p54_channel_list *list, - u16 freq, u16 data) -{ - int i; - struct p54_channel_entry *entry = NULL; - - /* - * usually all lists in the eeprom are mostly sorted. - * so it's very likely that the entry we are looking for - * is right at the end of the list - */ - for (i = list->entries; i >= 0; i--) { - if (freq == list->channels[i].freq) { - entry = &list->channels[i]; - break; - } - } - - if ((i < 0) && (list->entries < list->max_entries)) { - /* entry does not exist yet. Initialize a new one. */ - int band = p54_get_band_from_freq(freq); - - /* - * filter out frequencies which don't belong into - * any supported band. - */ - if (band >= 0) { - i = list->entries++; - list->band_channel_num[band]++; - - entry = &list->channels[i]; - entry->freq = freq; - entry->band = band; - entry->index = ieee80211_frequency_to_channel(freq); - entry->max_power = 0; - entry->data = 0; - } - } - - if (entry) - entry->data |= data; - - return entry; -} - -static int p54_get_maxpower(struct p54_common *priv, void *data) -{ - switch (priv->rxhw & PDR_SYNTH_FRONTEND_MASK) { - case PDR_SYNTH_FRONTEND_LONGBOW: { - struct pda_channel_output_limit_longbow *pda = data; - int j; - u16 rawpower = 0; - pda = data; - for (j = 0; j < ARRAY_SIZE(pda->point); j++) { - struct pda_channel_output_limit_point_longbow *point = - &pda->point[j]; - rawpower = max_t(u16, - rawpower, le16_to_cpu(point->val_qpsk)); - rawpower = max_t(u16, - rawpower, le16_to_cpu(point->val_bpsk)); - rawpower = max_t(u16, - rawpower, le16_to_cpu(point->val_16qam)); - rawpower = max_t(u16, - rawpower, le16_to_cpu(point->val_64qam)); - } - /* longbow seems to use 1/16 dBm units */ - return rawpower / 16; - } - - case PDR_SYNTH_FRONTEND_DUETTE3: - case PDR_SYNTH_FRONTEND_DUETTE2: - case PDR_SYNTH_FRONTEND_FRISBEE: - case PDR_SYNTH_FRONTEND_XBOW: { - struct pda_channel_output_limit *pda = data; - u8 rawpower = 0; - rawpower = max(rawpower, pda->val_qpsk); - rawpower = max(rawpower, pda->val_bpsk); - rawpower = max(rawpower, pda->val_16qam); - rawpower = max(rawpower, pda->val_64qam); - /* raw values are in 1/4 dBm units */ - return rawpower / 4; - } - - default: - return 20; - } -} - -static int p54_generate_channel_lists(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - struct p54_channel_list *list; - unsigned int i, j, k, max_channel_num; - int ret = 0; - u16 freq; - - if ((priv->iq_autocal_len != priv->curve_data->entries) || - (priv->iq_autocal_len != priv->output_limit->entries)) - wiphy_err(dev->wiphy, - "Unsupported or damaged EEPROM detected. " - "You may not be able to use all channels.\n"); - - max_channel_num = max_t(unsigned int, priv->output_limit->entries, - priv->iq_autocal_len); - max_channel_num = max_t(unsigned int, max_channel_num, - priv->curve_data->entries); - - list = kzalloc(sizeof(*list), GFP_KERNEL); - if (!list) { - ret = -ENOMEM; - goto free; - } - priv->chan_num = max_channel_num; - priv->survey = kzalloc(sizeof(struct survey_info) * max_channel_num, - GFP_KERNEL); - if (!priv->survey) { - ret = -ENOMEM; - goto free; - } - - list->max_entries = max_channel_num; - list->channels = kzalloc(sizeof(struct p54_channel_entry) * - max_channel_num, GFP_KERNEL); - if (!list->channels) { - ret = -ENOMEM; - goto free; - } - - for (i = 0; i < max_channel_num; i++) { - if (i < priv->iq_autocal_len) { - freq = le16_to_cpu(priv->iq_autocal[i].freq); - p54_update_channel_param(list, freq, CHAN_HAS_CAL); - } - - if (i < priv->output_limit->entries) { - struct p54_channel_entry *tmp; - - void *data = (void *) ((unsigned long) i * - priv->output_limit->entry_size + - priv->output_limit->offset + - priv->output_limit->data); - - freq = le16_to_cpup((__le16 *) data); - tmp = p54_update_channel_param(list, freq, - CHAN_HAS_LIMIT); - if (tmp) { - tmp->max_power = p54_get_maxpower(priv, data); - } - } - - if (i < priv->curve_data->entries) { - freq = le16_to_cpup((__le16 *) (i * - priv->curve_data->entry_size + - priv->curve_data->offset + - priv->curve_data->data)); - - p54_update_channel_param(list, freq, CHAN_HAS_CURVE); - } - } - - /* sort the channel list by frequency */ - sort(list->channels, list->entries, sizeof(struct p54_channel_entry), - p54_compare_channels, NULL); - - k = 0; - for (i = 0, j = 0; i < IEEE80211_NUM_BANDS; i++) { - if (p54_generate_band(dev, list, &k, i) == 0) - j++; - } - if (j == 0) { - /* no useable band available. */ - ret = -EINVAL; - } - -free: - if (list) { - kfree(list->channels); - kfree(list); - } - if (ret) { - kfree(priv->survey); - priv->survey = NULL; - } - - return ret; -} - -static int p54_convert_rev0(struct ieee80211_hw *dev, - struct pda_pa_curve_data *curve_data) -{ - struct p54_common *priv = dev->priv; - struct p54_pa_curve_data_sample *dst; - struct pda_pa_curve_data_sample_rev0 *src; - size_t cd_len = sizeof(*curve_data) + - (curve_data->points_per_channel*sizeof(*dst) + 2) * - curve_data->channels; - unsigned int i, j; - void *source, *target; - - priv->curve_data = kmalloc(sizeof(*priv->curve_data) + cd_len, - GFP_KERNEL); - if (!priv->curve_data) - return -ENOMEM; - - priv->curve_data->entries = curve_data->channels; - priv->curve_data->entry_size = sizeof(__le16) + - sizeof(*dst) * curve_data->points_per_channel; - priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); - priv->curve_data->len = cd_len; - memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); - source = curve_data->data; - target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; - for (i = 0; i < curve_data->channels; i++) { - __le16 *freq = source; - source += sizeof(__le16); - *((__le16 *)target) = *freq; - target += sizeof(__le16); - for (j = 0; j < curve_data->points_per_channel; j++) { - dst = target; - src = source; - - dst->rf_power = src->rf_power; - dst->pa_detector = src->pa_detector; - dst->data_64qam = src->pcv; - /* "invent" the points for the other modulations */ -#define SUB(x, y) (u8)(((x) - (y)) > (x) ? 0 : (x) - (y)) - dst->data_16qam = SUB(src->pcv, 12); - dst->data_qpsk = SUB(dst->data_16qam, 12); - dst->data_bpsk = SUB(dst->data_qpsk, 12); - dst->data_barker = SUB(dst->data_bpsk, 14); -#undef SUB - target += sizeof(*dst); - source += sizeof(*src); - } - } - - return 0; -} - -static int p54_convert_rev1(struct ieee80211_hw *dev, - struct pda_pa_curve_data *curve_data) -{ - struct p54_common *priv = dev->priv; - struct p54_pa_curve_data_sample *dst; - struct pda_pa_curve_data_sample_rev1 *src; - size_t cd_len = sizeof(*curve_data) + - (curve_data->points_per_channel*sizeof(*dst) + 2) * - curve_data->channels; - unsigned int i, j; - void *source, *target; - - priv->curve_data = kzalloc(cd_len + sizeof(*priv->curve_data), - GFP_KERNEL); - if (!priv->curve_data) - return -ENOMEM; - - priv->curve_data->entries = curve_data->channels; - priv->curve_data->entry_size = sizeof(__le16) + - sizeof(*dst) * curve_data->points_per_channel; - priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); - priv->curve_data->len = cd_len; - memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); - source = curve_data->data; - target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; - for (i = 0; i < curve_data->channels; i++) { - __le16 *freq = source; - source += sizeof(__le16); - *((__le16 *)target) = *freq; - target += sizeof(__le16); - for (j = 0; j < curve_data->points_per_channel; j++) { - memcpy(target, source, sizeof(*src)); - - target += sizeof(*dst); - source += sizeof(*src); - } - source++; - } - - return 0; -} - -static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", - "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; - -static int p54_parse_rssical(struct ieee80211_hw *dev, - u8 *data, int len, u16 type) -{ - struct p54_common *priv = dev->priv; - struct p54_rssi_db_entry *entry; - size_t db_len, entries; - int offset = 0, i; - - if (type != PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { - entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; - if (len != sizeof(struct pda_rssi_cal_entry) * entries) { - wiphy_err(dev->wiphy, "rssical size mismatch.\n"); - goto err_data; - } - } else { - /* - * Some devices (Dell 1450 USB, Xbow 5GHz card, etc...) - * have an empty two byte header. - */ - if (*((__le16 *)&data[offset]) == cpu_to_le16(0)) - offset += 2; - - entries = (len - offset) / - sizeof(struct pda_rssi_cal_ext_entry); - - if (len < offset || - (len - offset) % sizeof(struct pda_rssi_cal_ext_entry) || - entries == 0) { - wiphy_err(dev->wiphy, "invalid rssi database.\n"); - goto err_data; - } - } - - db_len = sizeof(*entry) * entries; - priv->rssi_db = kzalloc(db_len + sizeof(*priv->rssi_db), GFP_KERNEL); - if (!priv->rssi_db) - return -ENOMEM; - - priv->rssi_db->offset = 0; - priv->rssi_db->entries = entries; - priv->rssi_db->entry_size = sizeof(*entry); - priv->rssi_db->len = db_len; - - entry = (void *)((unsigned long)priv->rssi_db->data + priv->rssi_db->offset); - if (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) { - struct pda_rssi_cal_ext_entry *cal = (void *) &data[offset]; - - for (i = 0; i < entries; i++) { - entry[i].freq = le16_to_cpu(cal[i].freq); - entry[i].mul = (s16) le16_to_cpu(cal[i].mul); - entry[i].add = (s16) le16_to_cpu(cal[i].add); - } - } else { - struct pda_rssi_cal_entry *cal = (void *) &data[offset]; - - for (i = 0; i < entries; i++) { - u16 freq = 0; - switch (i) { - case IEEE80211_BAND_2GHZ: - freq = 2437; - break; - case IEEE80211_BAND_5GHZ: - freq = 5240; - break; - } - - entry[i].freq = freq; - entry[i].mul = (s16) le16_to_cpu(cal[i].mul); - entry[i].add = (s16) le16_to_cpu(cal[i].add); - } - } - - /* sort the list by channel frequency */ - sort(entry, entries, sizeof(*entry), p54_compare_rssichan, NULL); - return 0; - -err_data: - wiphy_err(dev->wiphy, - "rssi calibration data packing type:(%x) len:%d.\n", - type, len); - - print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, data, len); - - wiphy_err(dev->wiphy, "please report this issue.\n"); - return -EINVAL; -} - -struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *priv, const u16 freq) -{ - struct p54_rssi_db_entry *entry; - int i, found = -1; - - if (!priv->rssi_db) - return &p54_rssi_default; - - entry = (void *)(priv->rssi_db->data + priv->rssi_db->offset); - for (i = 0; i < priv->rssi_db->entries; i++) { - if (!same_band(freq, entry[i].freq)) - continue; - - if (found == -1) { - found = i; - continue; - } - - /* nearest match */ - if (abs(freq - entry[i].freq) < - abs(freq - entry[found].freq)) { - found = i; - continue; - } else { - break; - } - } - - return found < 0 ? &p54_rssi_default : &entry[found]; -} - -static void p54_parse_default_country(struct ieee80211_hw *dev, - void *data, int len) -{ - struct pda_country *country; - - if (len != sizeof(*country)) { - wiphy_err(dev->wiphy, - "found possible invalid default country eeprom entry. (entry size: %d)\n", - len); - - print_hex_dump_bytes("country:", DUMP_PREFIX_NONE, - data, len); - - wiphy_err(dev->wiphy, "please report this issue.\n"); - return; - } - - country = (struct pda_country *) data; - if (country->flags == PDR_COUNTRY_CERT_CODE_PSEUDO) - regulatory_hint(dev->wiphy, country->alpha2); - else { - /* TODO: - * write a shared/common function that converts - * "Regulatory domain codes" (802.11-2007 14.8.2.2) - * into ISO/IEC 3166-1 alpha2 for regulatory_hint. - */ - } -} - -static int p54_convert_output_limits(struct ieee80211_hw *dev, - u8 *data, size_t len) -{ - struct p54_common *priv = dev->priv; - - if (len < 2) - return -EINVAL; - - if (data[0] != 0) { - wiphy_err(dev->wiphy, "unknown output power db revision:%x\n", - data[0]); - return -EINVAL; - } - - if (2 + data[1] * sizeof(struct pda_channel_output_limit) > len) - return -EINVAL; - - priv->output_limit = kmalloc(data[1] * - sizeof(struct pda_channel_output_limit) + - sizeof(*priv->output_limit), GFP_KERNEL); - - if (!priv->output_limit) - return -ENOMEM; - - priv->output_limit->offset = 0; - priv->output_limit->entries = data[1]; - priv->output_limit->entry_size = - sizeof(struct pda_channel_output_limit); - priv->output_limit->len = priv->output_limit->entry_size * - priv->output_limit->entries + - priv->output_limit->offset; - - memcpy(priv->output_limit->data, &data[2], - data[1] * sizeof(struct pda_channel_output_limit)); - - return 0; -} - -static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src, - size_t total_len) -{ - struct p54_cal_database *dst; - size_t payload_len, entries, entry_size, offset; - - payload_len = le16_to_cpu(src->len); - entries = le16_to_cpu(src->entries); - entry_size = le16_to_cpu(src->entry_size); - offset = le16_to_cpu(src->offset); - if (((entries * entry_size + offset) != payload_len) || - (payload_len + sizeof(*src) != total_len)) - return NULL; - - dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL); - if (!dst) - return NULL; - - dst->entries = entries; - dst->entry_size = entry_size; - dst->offset = offset; - dst->len = payload_len; - - memcpy(dst->data, src->data, payload_len); - return dst; -} - -int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) -{ - struct p54_common *priv = dev->priv; - struct eeprom_pda_wrap *wrap; - struct pda_entry *entry; - unsigned int data_len, entry_len; - void *tmp; - int err; - u8 *end = (u8 *)eeprom + len; - u16 synth = 0; - u16 crc16 = ~0; - - wrap = (struct eeprom_pda_wrap *) eeprom; - entry = (void *)wrap->data + le16_to_cpu(wrap->len); - - /* verify that at least the entry length/code fits */ - while ((u8 *)entry <= end - sizeof(*entry)) { - entry_len = le16_to_cpu(entry->len); - data_len = ((entry_len - 1) << 1); - - /* abort if entry exceeds whole structure */ - if ((u8 *)entry + sizeof(*entry) + data_len > end) - break; - - switch (le16_to_cpu(entry->code)) { - case PDR_MAC_ADDRESS: - if (data_len != ETH_ALEN) - break; - SET_IEEE80211_PERM_ADDR(dev, entry->data); - break; - case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS: - if (priv->output_limit) - break; - err = p54_convert_output_limits(dev, entry->data, - data_len); - if (err) - goto err; - break; - case PDR_PRISM_PA_CAL_CURVE_DATA: { - struct pda_pa_curve_data *curve_data = - (struct pda_pa_curve_data *)entry->data; - if (data_len < sizeof(*curve_data)) { - err = -EINVAL; - goto err; - } - - switch (curve_data->cal_method_rev) { - case 0: - err = p54_convert_rev0(dev, curve_data); - break; - case 1: - err = p54_convert_rev1(dev, curve_data); - break; - default: - wiphy_err(dev->wiphy, - "unknown curve data revision %d\n", - curve_data->cal_method_rev); - err = -ENODEV; - break; - } - if (err) - goto err; - } - break; - case PDR_PRISM_ZIF_TX_IQ_CALIBRATION: - priv->iq_autocal = kmemdup(entry->data, data_len, - GFP_KERNEL); - if (!priv->iq_autocal) { - err = -ENOMEM; - goto err; - } - - priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry); - break; - case PDR_DEFAULT_COUNTRY: - p54_parse_default_country(dev, entry->data, data_len); - break; - case PDR_INTERFACE_LIST: - tmp = entry->data; - while ((u8 *)tmp < entry->data + data_len) { - struct exp_if *exp_if = tmp; - if (exp_if->if_id == cpu_to_le16(IF_ID_ISL39000)) - synth = le16_to_cpu(exp_if->variant); - tmp += sizeof(*exp_if); - } - break; - case PDR_HARDWARE_PLATFORM_COMPONENT_ID: - if (data_len < 2) - break; - priv->version = *(u8 *)(entry->data + 1); - break; - case PDR_RSSI_LINEAR_APPROXIMATION: - case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: - case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: - err = p54_parse_rssical(dev, entry->data, data_len, - le16_to_cpu(entry->code)); - if (err) - goto err; - break; - case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2: { - struct pda_custom_wrapper *pda = (void *) entry->data; - __le16 *src; - u16 *dst; - int i; - - if (priv->rssi_db || data_len < sizeof(*pda)) - break; - - priv->rssi_db = p54_convert_db(pda, data_len); - if (!priv->rssi_db) - break; - - src = (void *) priv->rssi_db->data; - dst = (void *) priv->rssi_db->data; - - for (i = 0; i < priv->rssi_db->entries; i++) - *(dst++) = (s16) le16_to_cpu(*(src++)); - - } - break; - case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { - struct pda_custom_wrapper *pda = (void *) entry->data; - if (priv->output_limit || data_len < sizeof(*pda)) - break; - priv->output_limit = p54_convert_db(pda, data_len); - } - break; - case PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM: { - struct pda_custom_wrapper *pda = (void *) entry->data; - if (priv->curve_data || data_len < sizeof(*pda)) - break; - priv->curve_data = p54_convert_db(pda, data_len); - } - break; - case PDR_END: - crc16 = ~crc_ccitt(crc16, (u8 *) entry, sizeof(*entry)); - if (crc16 != le16_to_cpup((__le16 *)entry->data)) { - wiphy_err(dev->wiphy, "eeprom failed checksum " - "test!\n"); - err = -ENOMSG; - goto err; - } else { - goto good_eeprom; - } - break; - default: - break; - } - - crc16 = crc_ccitt(crc16, (u8 *)entry, (entry_len + 1) * 2); - entry = (void *)entry + (entry_len + 1) * 2; - } - - wiphy_err(dev->wiphy, "unexpected end of eeprom data.\n"); - err = -ENODATA; - goto err; - -good_eeprom: - if (!synth || !priv->iq_autocal || !priv->output_limit || - !priv->curve_data) { - wiphy_err(dev->wiphy, - "not all required entries found in eeprom!\n"); - err = -EINVAL; - goto err; - } - - priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK; - - err = p54_generate_channel_lists(dev); - if (err) - goto err; - - if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW) - p54_init_xbow_synth(priv); - if (!(synth & PDR_SYNTH_24_GHZ_DISABLED)) - dev->wiphy->bands[IEEE80211_BAND_2GHZ] = - priv->band_table[IEEE80211_BAND_2GHZ]; - if (!(synth & PDR_SYNTH_5_GHZ_DISABLED)) - dev->wiphy->bands[IEEE80211_BAND_5GHZ] = - priv->band_table[IEEE80211_BAND_5GHZ]; - if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED) - priv->rx_diversity_mask = 3; - if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED) - priv->tx_diversity_mask = 3; - - if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { - u8 perm_addr[ETH_ALEN]; - - wiphy_warn(dev->wiphy, - "Invalid hwaddr! Using randomly generated MAC addr\n"); - eth_random_addr(perm_addr); - SET_IEEE80211_PERM_ADDR(dev, perm_addr); - } - - priv->cur_rssi = &p54_rssi_default; - - wiphy_info(dev->wiphy, "hwaddr %pM, MAC:isl38%02x RF:%s\n", - dev->wiphy->perm_addr, priv->version, - p54_rf_chips[priv->rxhw]); - - return 0; - -err: - kfree(priv->iq_autocal); - kfree(priv->output_limit); - kfree(priv->curve_data); - kfree(priv->rssi_db); - kfree(priv->survey); - priv->iq_autocal = NULL; - priv->output_limit = NULL; - priv->curve_data = NULL; - priv->rssi_db = NULL; - priv->survey = NULL; - - wiphy_err(dev->wiphy, "eeprom parse failed!\n"); - return err; -} -EXPORT_SYMBOL_GPL(p54_parse_eeprom); - -int p54_read_eeprom(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - size_t eeprom_size = 0x2020, offset = 0, blocksize, maxblocksize; - int ret = -ENOMEM; - void *eeprom; - - maxblocksize = EEPROM_READBACK_LEN; - if (priv->fw_var >= 0x509) - maxblocksize -= 0xc; - else - maxblocksize -= 0x4; - - eeprom = kzalloc(eeprom_size, GFP_KERNEL); - if (unlikely(!eeprom)) - goto free; - - while (eeprom_size) { - blocksize = min(eeprom_size, maxblocksize); - ret = p54_download_eeprom(priv, eeprom + offset, - offset, blocksize); - if (unlikely(ret)) - goto free; - - offset += blocksize; - eeprom_size -= blocksize; - } - - ret = p54_parse_eeprom(dev, eeprom, offset); -free: - kfree(eeprom); - return ret; -} -EXPORT_SYMBOL_GPL(p54_read_eeprom); diff --git a/drivers/net/wireless/p54/eeprom.h b/drivers/net/wireless/p54/eeprom.h deleted file mode 100644 index 20ebe39a3f4e..000000000000 --- a/drivers/net/wireless/p54/eeprom.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * eeprom specific definitions for mac80211 Prism54 drivers - * - * Copyright (c) 2006, Michael Wu - * Copyright (c) 2007-2009, Christian Lamparter - * - * Based on: - * - the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - * - LMAC API interface header file for STLC4560 (lmac_longbow.h) - * Copyright (C) 2007 Conexant Systems, Inc. - * - * - islmvc driver - * Copyright (C) 2001 Intersil Americas Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef EEPROM_H -#define EEPROM_H - -/* PDA defines are Copyright (C) 2005 Nokia Corporation (taken from islsm_pda.h) */ - -struct pda_entry { - __le16 len; /* includes both code and data */ - __le16 code; - u8 data[0]; -} __packed; - -struct eeprom_pda_wrap { - __le32 magic; - __le16 pad; - __le16 len; - __le32 arm_opcode; - u8 data[0]; -} __packed; - -struct p54_iq_autocal_entry { - __le16 iq_param[4]; -} __packed; - -struct pda_iq_autocal_entry { - __le16 freq; - struct p54_iq_autocal_entry params; -} __packed; - -struct pda_channel_output_limit { - __le16 freq; - u8 val_bpsk; - u8 val_qpsk; - u8 val_16qam; - u8 val_64qam; - u8 rate_set_mask; - u8 rate_set_size; -} __packed; - -struct pda_channel_output_limit_point_longbow { - __le16 val_bpsk; - __le16 val_qpsk; - __le16 val_16qam; - __le16 val_64qam; -} __packed; - -struct pda_channel_output_limit_longbow { - __le16 freq; - struct pda_channel_output_limit_point_longbow point[3]; -} __packed; - -struct pda_pa_curve_data_sample_rev0 { - u8 rf_power; - u8 pa_detector; - u8 pcv; -} __packed; - -struct pda_pa_curve_data_sample_rev1 { - u8 rf_power; - u8 pa_detector; - u8 data_barker; - u8 data_bpsk; - u8 data_qpsk; - u8 data_16qam; - u8 data_64qam; -} __packed; - -struct pda_pa_curve_data { - u8 cal_method_rev; - u8 channels; - u8 points_per_channel; - u8 padding; - u8 data[0]; -} __packed; - -struct pda_rssi_cal_ext_entry { - __le16 freq; - __le16 mul; - __le16 add; -} __packed; - -struct pda_rssi_cal_entry { - __le16 mul; - __le16 add; -} __packed; - -struct pda_country { - u8 regdomain; - u8 alpha2[2]; - u8 flags; -} __packed; - -struct pda_antenna_gain { - struct { - u8 gain_5GHz; /* 0.25 dBi units */ - u8 gain_2GHz; /* 0.25 dBi units */ - } __packed antenna[0]; -} __packed; - -struct pda_custom_wrapper { - __le16 entries; - __le16 entry_size; - __le16 offset; - __le16 len; - u8 data[0]; -} __packed; - -/* - * this defines the PDR codes used to build PDAs as defined in document - * number 553155. The current implementation mirrors version 1.1 of the - * document and lists only PDRs supported by the ARM platform. - */ - -/* common and choice range (0x0000 - 0x0fff) */ -#define PDR_END 0x0000 -#define PDR_MANUFACTURING_PART_NUMBER 0x0001 -#define PDR_PDA_VERSION 0x0002 -#define PDR_NIC_SERIAL_NUMBER 0x0003 -#define PDR_NIC_RAM_SIZE 0x0005 -#define PDR_RFMODEM_SUP_RANGE 0x0006 -#define PDR_PRISM_MAC_SUP_RANGE 0x0007 -#define PDR_NIC_ID 0x0008 - -#define PDR_MAC_ADDRESS 0x0101 -#define PDR_REGULATORY_DOMAIN_LIST 0x0103 /* obsolete */ -#define PDR_ALLOWED_CHAN_SET 0x0104 -#define PDR_DEFAULT_CHAN 0x0105 -#define PDR_TEMPERATURE_TYPE 0x0107 - -#define PDR_IFR_SETTING 0x0200 -#define PDR_RFR_SETTING 0x0201 -#define PDR_3861_BASELINE_REG_SETTINGS 0x0202 -#define PDR_3861_SHADOW_REG_SETTINGS 0x0203 -#define PDR_3861_IFRF_REG_SETTINGS 0x0204 - -#define PDR_3861_CHAN_CALIB_SET_POINTS 0x0300 -#define PDR_3861_CHAN_CALIB_INTEGRATOR 0x0301 - -#define PDR_3842_PRISM_II_NIC_CONFIG 0x0400 -#define PDR_PRISM_USB_ID 0x0401 -#define PDR_PRISM_PCI_ID 0x0402 -#define PDR_PRISM_PCI_IF_CONFIG 0x0403 -#define PDR_PRISM_PCI_PM_CONFIG 0x0404 - -#define PDR_3861_MF_TEST_CHAN_SET_POINTS 0x0900 -#define PDR_3861_MF_TEST_CHAN_INTEGRATORS 0x0901 - -/* ARM range (0x1000 - 0x1fff) */ -#define PDR_COUNTRY_INFORMATION 0x1000 /* obsolete */ -#define PDR_INTERFACE_LIST 0x1001 -#define PDR_HARDWARE_PLATFORM_COMPONENT_ID 0x1002 -#define PDR_OEM_NAME 0x1003 -#define PDR_PRODUCT_NAME 0x1004 -#define PDR_UTF8_OEM_NAME 0x1005 -#define PDR_UTF8_PRODUCT_NAME 0x1006 -#define PDR_COUNTRY_LIST 0x1007 -#define PDR_DEFAULT_COUNTRY 0x1008 - -#define PDR_ANTENNA_GAIN 0x1100 - -#define PDR_PRISM_INDIGO_PA_CALIBRATION_DATA 0x1901 -#define PDR_RSSI_LINEAR_APPROXIMATION 0x1902 -#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS 0x1903 -#define PDR_PRISM_PA_CAL_CURVE_DATA 0x1904 -#define PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND 0x1905 -#define PDR_PRISM_ZIF_TX_IQ_CALIBRATION 0x1906 -#define PDR_REGULATORY_POWER_LIMITS 0x1907 -#define PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED 0x1908 -#define PDR_RADIATED_TRANSMISSION_CORRECTION 0x1909 -#define PDR_PRISM_TX_IQ_CALIBRATION 0x190a - -/* reserved range (0x2000 - 0x7fff) */ - -/* customer range (0x8000 - 0xffff) */ -#define PDR_BASEBAND_REGISTERS 0x8000 -#define PDR_PER_CHANNEL_BASEBAND_REGISTERS 0x8001 - -/* used by our modificated eeprom image */ -#define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM 0xDEAD -#define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2 0xCAFF -#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM 0xBEEF -#define PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM 0xB05D - -/* Interface Definitions */ -#define PDR_INTERFACE_ROLE_SERVER 0x0000 -#define PDR_INTERFACE_ROLE_CLIENT 0x0001 - -/* PDR definitions for default country & country list */ -#define PDR_COUNTRY_CERT_CODE 0x80 -#define PDR_COUNTRY_CERT_CODE_REAL 0x00 -#define PDR_COUNTRY_CERT_CODE_PSEUDO 0x80 -#define PDR_COUNTRY_CERT_BAND 0x40 -#define PDR_COUNTRY_CERT_BAND_2GHZ 0x00 -#define PDR_COUNTRY_CERT_BAND_5GHZ 0x40 -#define PDR_COUNTRY_CERT_IODOOR 0x30 -#define PDR_COUNTRY_CERT_IODOOR_BOTH 0x00 -#define PDR_COUNTRY_CERT_IODOOR_INDOOR 0x20 -#define PDR_COUNTRY_CERT_IODOOR_OUTDOOR 0x30 -#define PDR_COUNTRY_CERT_INDEX 0x0f - -/* Specific LMAC FW/HW variant definitions */ -#define PDR_SYNTH_FRONTEND_MASK 0x0007 -#define PDR_SYNTH_FRONTEND_DUETTE3 0x0001 -#define PDR_SYNTH_FRONTEND_DUETTE2 0x0002 -#define PDR_SYNTH_FRONTEND_FRISBEE 0x0003 -#define PDR_SYNTH_FRONTEND_XBOW 0x0004 -#define PDR_SYNTH_FRONTEND_LONGBOW 0x0005 -#define PDR_SYNTH_IQ_CAL_MASK 0x0018 -#define PDR_SYNTH_IQ_CAL_PA_DETECTOR 0x0000 -#define PDR_SYNTH_IQ_CAL_DISABLED 0x0008 -#define PDR_SYNTH_IQ_CAL_ZIF 0x0010 -#define PDR_SYNTH_FAA_SWITCH_MASK 0x0020 -#define PDR_SYNTH_FAA_SWITCH_ENABLED 0x0020 -#define PDR_SYNTH_24_GHZ_MASK 0x0040 -#define PDR_SYNTH_24_GHZ_DISABLED 0x0040 -#define PDR_SYNTH_5_GHZ_MASK 0x0080 -#define PDR_SYNTH_5_GHZ_DISABLED 0x0080 -#define PDR_SYNTH_RX_DIV_MASK 0x0100 -#define PDR_SYNTH_RX_DIV_SUPPORTED 0x0100 -#define PDR_SYNTH_TX_DIV_MASK 0x0200 -#define PDR_SYNTH_TX_DIV_SUPPORTED 0x0200 -#define PDR_SYNTH_ASM_MASK 0x0400 -#define PDR_SYNTH_ASM_XSWON 0x0400 - -#endif /* EEPROM_H */ diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/p54/fwio.c deleted file mode 100644 index 257a9eadd595..000000000000 --- a/drivers/net/wireless/p54/fwio.c +++ /dev/null @@ -1,764 +0,0 @@ -/* - * Firmware I/O code for mac80211 Prism54 drivers - * - * Copyright (c) 2006, Michael Wu - * Copyright (c) 2007-2009, Christian Lamparter - * Copyright 2008, Johannes Berg - * - * Based on: - * - the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - stlc45xx driver - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include - -#include "p54.h" -#include "eeprom.h" -#include "lmac.h" - -int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) -{ - struct p54_common *priv = dev->priv; - struct exp_if *exp_if; - struct bootrec *bootrec; - u32 *data = (u32 *)fw->data; - u32 *end_data = (u32 *)fw->data + (fw->size >> 2); - u8 *fw_version = NULL; - size_t len; - int i; - int maxlen; - - if (priv->rx_start) - return 0; - - while (data < end_data && *data) - data++; - - while (data < end_data && !*data) - data++; - - bootrec = (struct bootrec *) data; - - while (bootrec->data <= end_data && (bootrec->data + - (len = le32_to_cpu(bootrec->len))) <= end_data) { - u32 code = le32_to_cpu(bootrec->code); - switch (code) { - case BR_CODE_COMPONENT_ID: - priv->fw_interface = be32_to_cpup((__be32 *) - bootrec->data); - switch (priv->fw_interface) { - case FW_LM86: - case FW_LM20: - case FW_LM87: { - char *iftype = (char *)bootrec->data; - wiphy_info(priv->hw->wiphy, - "p54 detected a LM%c%c firmware\n", - iftype[2], iftype[3]); - break; - } - case FW_FMAC: - default: - wiphy_err(priv->hw->wiphy, - "unsupported firmware\n"); - return -ENODEV; - } - break; - case BR_CODE_COMPONENT_VERSION: - /* 24 bytes should be enough for all firmwares */ - if (strnlen((unsigned char *) bootrec->data, 24) < 24) - fw_version = (unsigned char *) bootrec->data; - break; - case BR_CODE_DESCR: { - struct bootrec_desc *desc = - (struct bootrec_desc *)bootrec->data; - priv->rx_start = le32_to_cpu(desc->rx_start); - /* FIXME add sanity checking */ - priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500; - priv->headroom = desc->headroom; - priv->tailroom = desc->tailroom; - priv->privacy_caps = desc->privacy_caps; - priv->rx_keycache_size = desc->rx_keycache_size; - if (le32_to_cpu(bootrec->len) == 11) - priv->rx_mtu = le16_to_cpu(desc->rx_mtu); - else - priv->rx_mtu = (size_t) - 0x620 - priv->tx_hdr_len; - maxlen = priv->tx_hdr_len + /* USB devices */ - sizeof(struct p54_rx_data) + - 4 + /* rx alignment */ - IEEE80211_MAX_FRAG_THRESHOLD; - if (priv->rx_mtu > maxlen && PAGE_SIZE == 4096) { - printk(KERN_INFO "p54: rx_mtu reduced from %d " - "to %d\n", priv->rx_mtu, maxlen); - priv->rx_mtu = maxlen; - } - break; - } - case BR_CODE_EXPOSED_IF: - exp_if = (struct exp_if *) bootrec->data; - for (i = 0; i < (len * sizeof(*exp_if) / 4); i++) - if (exp_if[i].if_id == cpu_to_le16(IF_ID_LMAC)) - priv->fw_var = le16_to_cpu(exp_if[i].variant); - break; - case BR_CODE_DEPENDENT_IF: - break; - case BR_CODE_END_OF_BRA: - case LEGACY_BR_CODE_END_OF_BRA: - end_data = NULL; - break; - default: - break; - } - bootrec = (struct bootrec *)&bootrec->data[len]; - } - - if (fw_version) { - wiphy_info(priv->hw->wiphy, - "FW rev %s - Softmac protocol %x.%x\n", - fw_version, priv->fw_var >> 8, priv->fw_var & 0xff); - snprintf(dev->wiphy->fw_version, sizeof(dev->wiphy->fw_version), - "%s - %x.%x", fw_version, - priv->fw_var >> 8, priv->fw_var & 0xff); - } - - if (priv->fw_var < 0x500) - wiphy_info(priv->hw->wiphy, - "you are using an obsolete firmware. " - "visit http://wireless.kernel.org/en/users/Drivers/p54 " - "and grab one for \"kernel >= 2.6.28\"!\n"); - - if (priv->fw_var >= 0x300) { - /* Firmware supports QoS, use it! */ - - if (priv->fw_var >= 0x500) { - priv->tx_stats[P54_QUEUE_AC_VO].limit = 16; - priv->tx_stats[P54_QUEUE_AC_VI].limit = 16; - priv->tx_stats[P54_QUEUE_AC_BE].limit = 16; - priv->tx_stats[P54_QUEUE_AC_BK].limit = 16; - } else { - priv->tx_stats[P54_QUEUE_AC_VO].limit = 3; - priv->tx_stats[P54_QUEUE_AC_VI].limit = 4; - priv->tx_stats[P54_QUEUE_AC_BE].limit = 3; - priv->tx_stats[P54_QUEUE_AC_BK].limit = 2; - } - priv->hw->queues = P54_QUEUE_AC_NUM; - } - - wiphy_info(priv->hw->wiphy, - "cryptographic accelerator WEP:%s, TKIP:%s, CCMP:%s\n", - (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" : "no", - (priv->privacy_caps & - (BR_DESC_PRIV_CAP_TKIP | BR_DESC_PRIV_CAP_MICHAEL)) - ? "YES" : "no", - (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP) - ? "YES" : "no"); - - if (priv->rx_keycache_size) { - /* - * NOTE: - * - * The firmware provides at most 255 (0 - 254) slots - * for keys which are then used to offload decryption. - * As a result the 255 entry (aka 0xff) can be used - * safely by the driver to mark keys that didn't fit - * into the full cache. This trick saves us from - * keeping a extra list for uploaded keys. - */ - - priv->used_rxkeys = kzalloc(BITS_TO_LONGS( - priv->rx_keycache_size), GFP_KERNEL); - - if (!priv->used_rxkeys) - return -ENOMEM; - } - - return 0; -} -EXPORT_SYMBOL_GPL(p54_parse_firmware); - -static struct sk_buff *p54_alloc_skb(struct p54_common *priv, u16 hdr_flags, - u16 payload_len, u16 type, gfp_t memflags) -{ - struct p54_hdr *hdr; - struct sk_buff *skb; - size_t frame_len = sizeof(*hdr) + payload_len; - - if (frame_len > P54_MAX_CTRL_FRAME_LEN) - return NULL; - - if (unlikely(skb_queue_len(&priv->tx_pending) > 64)) - return NULL; - - skb = __dev_alloc_skb(priv->tx_hdr_len + frame_len, memflags); - if (!skb) - return NULL; - skb_reserve(skb, priv->tx_hdr_len); - - hdr = (struct p54_hdr *) skb_put(skb, sizeof(*hdr)); - hdr->flags = cpu_to_le16(hdr_flags); - hdr->len = cpu_to_le16(payload_len); - hdr->type = cpu_to_le16(type); - hdr->tries = hdr->rts_tries = 0; - return skb; -} - -int p54_download_eeprom(struct p54_common *priv, void *buf, - u16 offset, u16 len) -{ - struct p54_eeprom_lm86 *eeprom_hdr; - struct sk_buff *skb; - size_t eeprom_hdr_size; - int ret = 0; - long timeout; - - if (priv->fw_var >= 0x509) - eeprom_hdr_size = sizeof(*eeprom_hdr); - else - eeprom_hdr_size = 0x4; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, eeprom_hdr_size + - len, P54_CONTROL_TYPE_EEPROM_READBACK, - GFP_KERNEL); - if (unlikely(!skb)) - return -ENOMEM; - - mutex_lock(&priv->eeprom_mutex); - priv->eeprom = buf; - eeprom_hdr = (struct p54_eeprom_lm86 *) skb_put(skb, - eeprom_hdr_size + len); - - if (priv->fw_var < 0x509) { - eeprom_hdr->v1.offset = cpu_to_le16(offset); - eeprom_hdr->v1.len = cpu_to_le16(len); - } else { - eeprom_hdr->v2.offset = cpu_to_le32(offset); - eeprom_hdr->v2.len = cpu_to_le16(len); - eeprom_hdr->v2.magic2 = 0xf; - memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4); - } - - p54_tx(priv, skb); - - timeout = wait_for_completion_interruptible_timeout( - &priv->eeprom_comp, HZ); - if (timeout <= 0) { - wiphy_err(priv->hw->wiphy, - "device does not respond or signal received!\n"); - ret = -EBUSY; - } - priv->eeprom = NULL; - mutex_unlock(&priv->eeprom_mutex); - return ret; -} - -int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set) -{ - struct sk_buff *skb; - struct p54_tim *tim; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*tim), - P54_CONTROL_TYPE_TIM, GFP_ATOMIC); - if (unlikely(!skb)) - return -ENOMEM; - - tim = (struct p54_tim *) skb_put(skb, sizeof(*tim)); - tim->count = 1; - tim->entry[0] = cpu_to_le16(set ? (aid | 0x8000) : aid); - p54_tx(priv, skb); - return 0; -} - -int p54_sta_unlock(struct p54_common *priv, u8 *addr) -{ - struct sk_buff *skb; - struct p54_sta_unlock *sta; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*sta), - P54_CONTROL_TYPE_PSM_STA_UNLOCK, GFP_ATOMIC); - if (unlikely(!skb)) - return -ENOMEM; - - sta = (struct p54_sta_unlock *)skb_put(skb, sizeof(*sta)); - memcpy(sta->addr, addr, ETH_ALEN); - p54_tx(priv, skb); - return 0; -} - -int p54_tx_cancel(struct p54_common *priv, __le32 req_id) -{ - struct sk_buff *skb; - struct p54_txcancel *cancel; - u32 _req_id = le32_to_cpu(req_id); - - if (unlikely(_req_id < priv->rx_start || _req_id > priv->rx_end)) - return -EINVAL; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*cancel), - P54_CONTROL_TYPE_TXCANCEL, GFP_ATOMIC); - if (unlikely(!skb)) - return -ENOMEM; - - cancel = (struct p54_txcancel *)skb_put(skb, sizeof(*cancel)); - cancel->req_id = req_id; - p54_tx(priv, skb); - return 0; -} - -int p54_setup_mac(struct p54_common *priv) -{ - struct sk_buff *skb; - struct p54_setup_mac *setup; - u16 mode; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*setup), - P54_CONTROL_TYPE_SETUP, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - setup = (struct p54_setup_mac *) skb_put(skb, sizeof(*setup)); - if (!(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { - switch (priv->mode) { - case NL80211_IFTYPE_STATION: - mode = P54_FILTER_TYPE_STATION; - break; - case NL80211_IFTYPE_AP: - mode = P54_FILTER_TYPE_AP; - break; - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - mode = P54_FILTER_TYPE_IBSS; - break; - case NL80211_IFTYPE_MONITOR: - mode = P54_FILTER_TYPE_PROMISCUOUS; - break; - default: - mode = P54_FILTER_TYPE_HIBERNATE; - break; - } - - /* - * "TRANSPARENT and PROMISCUOUS are mutually exclusive" - * STSW45X0C LMAC API - page 12 - */ - if (priv->filter_flags & FIF_OTHER_BSS && - (mode != P54_FILTER_TYPE_PROMISCUOUS)) - mode |= P54_FILTER_TYPE_TRANSPARENT; - } else { - mode = P54_FILTER_TYPE_HIBERNATE; - } - - setup->mac_mode = cpu_to_le16(mode); - memcpy(setup->mac_addr, priv->mac_addr, ETH_ALEN); - memcpy(setup->bssid, priv->bssid, ETH_ALEN); - setup->rx_antenna = 2 & priv->rx_diversity_mask; /* automatic */ - setup->rx_align = 0; - if (priv->fw_var < 0x500) { - setup->v1.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); - memset(setup->v1.rts_rates, 0, 8); - setup->v1.rx_addr = cpu_to_le32(priv->rx_end); - setup->v1.max_rx = cpu_to_le16(priv->rx_mtu); - setup->v1.rxhw = cpu_to_le16(priv->rxhw); - setup->v1.wakeup_timer = cpu_to_le16(priv->wakeup_timer); - setup->v1.unalloc0 = cpu_to_le16(0); - } else { - setup->v2.rx_addr = cpu_to_le32(priv->rx_end); - setup->v2.max_rx = cpu_to_le16(priv->rx_mtu); - setup->v2.rxhw = cpu_to_le16(priv->rxhw); - setup->v2.timer = cpu_to_le16(priv->wakeup_timer); - setup->v2.truncate = cpu_to_le16(48896); - setup->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); - setup->v2.sbss_offset = 0; - setup->v2.mcast_window = 0; - setup->v2.rx_rssi_threshold = 0; - setup->v2.rx_ed_threshold = 0; - setup->v2.ref_clock = cpu_to_le32(644245094); - setup->v2.lpf_bandwidth = cpu_to_le16(65535); - setup->v2.osc_start_delay = cpu_to_le16(65535); - } - p54_tx(priv, skb); - priv->phy_idle = mode == P54_FILTER_TYPE_HIBERNATE; - return 0; -} - -int p54_scan(struct p54_common *priv, u16 mode, u16 dwell) -{ - struct sk_buff *skb; - struct p54_hdr *hdr; - struct p54_scan_head *head; - struct p54_iq_autocal_entry *iq_autocal; - union p54_scan_body_union *body; - struct p54_scan_tail_rate *rate; - struct pda_rssi_cal_entry *rssi; - struct p54_rssi_db_entry *rssi_data; - unsigned int i; - void *entry; - __le16 freq = cpu_to_le16(priv->hw->conf.chandef.chan->center_freq); - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) + - 2 + sizeof(*iq_autocal) + sizeof(*body) + - sizeof(*rate) + 2 * sizeof(*rssi), - P54_CONTROL_TYPE_SCAN, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - head = (struct p54_scan_head *) skb_put(skb, sizeof(*head)); - memset(head->scan_params, 0, sizeof(head->scan_params)); - head->mode = cpu_to_le16(mode); - head->dwell = cpu_to_le16(dwell); - head->freq = freq; - - if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { - __le16 *pa_power_points = (__le16 *) skb_put(skb, 2); - *pa_power_points = cpu_to_le16(0x0c); - } - - iq_autocal = (void *) skb_put(skb, sizeof(*iq_autocal)); - for (i = 0; i < priv->iq_autocal_len; i++) { - if (priv->iq_autocal[i].freq != freq) - continue; - - memcpy(iq_autocal, &priv->iq_autocal[i].params, - sizeof(struct p54_iq_autocal_entry)); - break; - } - if (i == priv->iq_autocal_len) - goto err; - - if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) - body = (void *) skb_put(skb, sizeof(body->longbow)); - else - body = (void *) skb_put(skb, sizeof(body->normal)); - - for (i = 0; i < priv->output_limit->entries; i++) { - __le16 *entry_freq = (void *) (priv->output_limit->data + - priv->output_limit->entry_size * i); - - if (*entry_freq != freq) - continue; - - if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { - memcpy(&body->longbow.power_limits, - (void *) entry_freq + sizeof(__le16), - priv->output_limit->entry_size); - } else { - struct pda_channel_output_limit *limits = - (void *) entry_freq; - - body->normal.val_barker = 0x38; - body->normal.val_bpsk = body->normal.dup_bpsk = - limits->val_bpsk; - body->normal.val_qpsk = body->normal.dup_qpsk = - limits->val_qpsk; - body->normal.val_16qam = body->normal.dup_16qam = - limits->val_16qam; - body->normal.val_64qam = body->normal.dup_64qam = - limits->val_64qam; - } - break; - } - if (i == priv->output_limit->entries) - goto err; - - entry = (void *)(priv->curve_data->data + priv->curve_data->offset); - for (i = 0; i < priv->curve_data->entries; i++) { - if (*((__le16 *)entry) != freq) { - entry += priv->curve_data->entry_size; - continue; - } - - if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { - memcpy(&body->longbow.curve_data, - entry + sizeof(__le16), - priv->curve_data->entry_size); - } else { - struct p54_scan_body *chan = &body->normal; - struct pda_pa_curve_data *curve_data = - (void *) priv->curve_data->data; - - entry += sizeof(__le16); - chan->pa_points_per_curve = 8; - memset(chan->curve_data, 0, sizeof(*chan->curve_data)); - memcpy(chan->curve_data, entry, - sizeof(struct p54_pa_curve_data_sample) * - min((u8)8, curve_data->points_per_channel)); - } - break; - } - if (i == priv->curve_data->entries) - goto err; - - if ((priv->fw_var >= 0x500) && (priv->fw_var < 0x509)) { - rate = (void *) skb_put(skb, sizeof(*rate)); - rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); - for (i = 0; i < sizeof(rate->rts_rates); i++) - rate->rts_rates[i] = i; - } - - rssi = (struct pda_rssi_cal_entry *) skb_put(skb, sizeof(*rssi)); - rssi_data = p54_rssi_find(priv, le16_to_cpu(freq)); - rssi->mul = cpu_to_le16(rssi_data->mul); - rssi->add = cpu_to_le16(rssi_data->add); - if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { - /* Longbow frontend needs ever more */ - rssi = (void *) skb_put(skb, sizeof(*rssi)); - rssi->mul = cpu_to_le16(rssi_data->longbow_unkn); - rssi->add = cpu_to_le16(rssi_data->longbow_unk2); - } - - if (priv->fw_var >= 0x509) { - rate = (void *) skb_put(skb, sizeof(*rate)); - rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); - for (i = 0; i < sizeof(rate->rts_rates); i++) - rate->rts_rates[i] = i; - } - - hdr = (struct p54_hdr *) skb->data; - hdr->len = cpu_to_le16(skb->len - sizeof(*hdr)); - - p54_tx(priv, skb); - priv->cur_rssi = rssi_data; - return 0; - -err: - wiphy_err(priv->hw->wiphy, "frequency change to channel %d failed.\n", - ieee80211_frequency_to_channel( - priv->hw->conf.chandef.chan->center_freq)); - - dev_kfree_skb_any(skb); - return -EINVAL; -} - -int p54_set_leds(struct p54_common *priv) -{ - struct sk_buff *skb; - struct p54_led *led; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*led), - P54_CONTROL_TYPE_LED, GFP_ATOMIC); - if (unlikely(!skb)) - return -ENOMEM; - - led = (struct p54_led *) skb_put(skb, sizeof(*led)); - led->flags = cpu_to_le16(0x0003); - led->mask[0] = led->mask[1] = cpu_to_le16(priv->softled_state); - led->delay[0] = cpu_to_le16(1); - led->delay[1] = cpu_to_le16(0); - p54_tx(priv, skb); - return 0; -} - -int p54_set_edcf(struct p54_common *priv) -{ - struct sk_buff *skb; - struct p54_edcf *edcf; - u8 rtd; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf), - P54_CONTROL_TYPE_DCFINIT, GFP_ATOMIC); - if (unlikely(!skb)) - return -ENOMEM; - - edcf = (struct p54_edcf *)skb_put(skb, sizeof(*edcf)); - if (priv->use_short_slot) { - edcf->slottime = 9; - edcf->sifs = 0x10; - edcf->eofpad = 0x00; - } else { - edcf->slottime = 20; - edcf->sifs = 0x0a; - edcf->eofpad = 0x06; - } - /* - * calculate the extra round trip delay according to the - * formula from 802.11-2007 17.3.8.6. - */ - rtd = 3 * priv->coverage_class; - edcf->slottime += rtd; - edcf->round_trip_delay = cpu_to_le16(rtd); - /* (see prism54/isl_oid.h for further details) */ - edcf->frameburst = cpu_to_le16(0); - edcf->flags = 0; - memset(edcf->mapping, 0, sizeof(edcf->mapping)); - memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue)); - p54_tx(priv, skb); - return 0; -} - -int p54_set_ps(struct p54_common *priv) -{ - struct sk_buff *skb; - struct p54_psm *psm; - unsigned int i; - u16 mode; - - if (priv->hw->conf.flags & IEEE80211_CONF_PS && - !priv->powersave_override) - mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM | - P54_PSM_CHECKSUM | P54_PSM_MCBC; - else - mode = P54_PSM_CAM; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*psm), - P54_CONTROL_TYPE_PSM, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - psm = (struct p54_psm *)skb_put(skb, sizeof(*psm)); - psm->mode = cpu_to_le16(mode); - psm->aid = cpu_to_le16(priv->aid); - for (i = 0; i < ARRAY_SIZE(psm->intervals); i++) { - psm->intervals[i].interval = - cpu_to_le16(priv->hw->conf.listen_interval); - psm->intervals[i].periods = cpu_to_le16(1); - } - - psm->beacon_rssi_skip_max = 200; - psm->rssi_delta_threshold = 0; - psm->nr = 1; - psm->exclude[0] = WLAN_EID_TIM; - - p54_tx(priv, skb); - priv->phy_ps = mode != P54_PSM_CAM; - return 0; -} - -int p54_init_xbow_synth(struct p54_common *priv) -{ - struct sk_buff *skb; - struct p54_xbow_synth *xbow; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*xbow), - P54_CONTROL_TYPE_XBOW_SYNTH_CFG, GFP_KERNEL); - if (unlikely(!skb)) - return -ENOMEM; - - xbow = (struct p54_xbow_synth *)skb_put(skb, sizeof(*xbow)); - xbow->magic1 = cpu_to_le16(0x1); - xbow->magic2 = cpu_to_le16(0x2); - xbow->freq = cpu_to_le16(5390); - memset(xbow->padding, 0, sizeof(xbow->padding)); - p54_tx(priv, skb); - return 0; -} - -int p54_upload_key(struct p54_common *priv, u8 algo, int slot, u8 idx, u8 len, - u8 *addr, u8* key) -{ - struct sk_buff *skb; - struct p54_keycache *rxkey; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey), - P54_CONTROL_TYPE_RX_KEYCACHE, GFP_KERNEL); - if (unlikely(!skb)) - return -ENOMEM; - - rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey)); - rxkey->entry = slot; - rxkey->key_id = idx; - rxkey->key_type = algo; - if (addr) - memcpy(rxkey->mac, addr, ETH_ALEN); - else - eth_broadcast_addr(rxkey->mac); - - switch (algo) { - case P54_CRYPTO_WEP: - case P54_CRYPTO_AESCCMP: - rxkey->key_len = min_t(u8, 16, len); - memcpy(rxkey->key, key, rxkey->key_len); - break; - - case P54_CRYPTO_TKIPMICHAEL: - rxkey->key_len = 24; - memcpy(rxkey->key, key, 16); - memcpy(&(rxkey->key[16]), &(key - [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8); - break; - - case P54_CRYPTO_NONE: - rxkey->key_len = 0; - memset(rxkey->key, 0, sizeof(rxkey->key)); - break; - - default: - wiphy_err(priv->hw->wiphy, - "invalid cryptographic algorithm: %d\n", algo); - dev_kfree_skb(skb); - return -EINVAL; - } - - p54_tx(priv, skb); - return 0; -} - -int p54_fetch_statistics(struct p54_common *priv) -{ - struct ieee80211_tx_info *txinfo; - struct p54_tx_info *p54info; - struct sk_buff *skb; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, - sizeof(struct p54_statistics), - P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - /* - * The statistic feedback causes some extra headaches here, if it - * is not to crash/corrupt the firmware data structures. - * - * Unlike all other Control Get OIDs we can not use helpers like - * skb_put to reserve the space for the data we're requesting. - * Instead the extra frame length -which will hold the results later- - * will only be told to the p54_assign_address, so that following - * frames won't be placed into the allegedly empty area. - */ - txinfo = IEEE80211_SKB_CB(skb); - p54info = (void *) txinfo->rate_driver_data; - p54info->extra_len = sizeof(struct p54_statistics); - - p54_tx(priv, skb); - return 0; -} - -int p54_set_groupfilter(struct p54_common *priv) -{ - struct p54_group_address_table *grp; - struct sk_buff *skb; - bool on = false; - - skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*grp), - P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - grp = (struct p54_group_address_table *)skb_put(skb, sizeof(*grp)); - - on = !(priv->filter_flags & FIF_ALLMULTI) && - (priv->mc_maclist_num > 0 && - priv->mc_maclist_num <= MC_FILTER_ADDRESS_NUM); - - if (on) { - grp->filter_enable = cpu_to_le16(1); - grp->num_address = cpu_to_le16(priv->mc_maclist_num); - memcpy(grp->mac_list, priv->mc_maclist, sizeof(grp->mac_list)); - } else { - grp->filter_enable = cpu_to_le16(0); - grp->num_address = cpu_to_le16(0); - memset(grp->mac_list, 0, sizeof(grp->mac_list)); - } - - p54_tx(priv, skb); - return 0; -} diff --git a/drivers/net/wireless/p54/led.c b/drivers/net/wireless/p54/led.c deleted file mode 100644 index 9a8fedd3c0f5..000000000000 --- a/drivers/net/wireless/p54/led.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Common code for mac80211 Prism54 drivers - * - * Copyright (c) 2006, Michael Wu - * Copyright (c) 2007-2009, Christian Lamparter - * Copyright 2008, Johannes Berg - * - * Based on: - * - the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - stlc45xx driver - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include - -#include -#ifdef CONFIG_P54_LEDS -#include -#endif /* CONFIG_P54_LEDS */ - -#include "p54.h" -#include "lmac.h" - -static void p54_update_leds(struct work_struct *work) -{ - struct p54_common *priv = container_of(work, struct p54_common, - led_work.work); - int err, i, tmp, blink_delay = 400; - bool rerun = false; - - /* Don't toggle the LED, when the device is down. */ - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) - return ; - - for (i = 0; i < ARRAY_SIZE(priv->leds); i++) - if (priv->leds[i].toggled) { - priv->softled_state |= BIT(i); - - tmp = 70 + 200 / (priv->leds[i].toggled); - if (tmp < blink_delay) - blink_delay = tmp; - - if (priv->leds[i].led_dev.brightness == LED_OFF) - rerun = true; - - priv->leds[i].toggled = - !!priv->leds[i].led_dev.brightness; - } else - priv->softled_state &= ~BIT(i); - - err = p54_set_leds(priv); - if (err && net_ratelimit()) - wiphy_err(priv->hw->wiphy, - "failed to update LEDs (%d).\n", err); - - if (rerun) - ieee80211_queue_delayed_work(priv->hw, &priv->led_work, - msecs_to_jiffies(blink_delay)); -} - -static void p54_led_brightness_set(struct led_classdev *led_dev, - enum led_brightness brightness) -{ - struct p54_led_dev *led = container_of(led_dev, struct p54_led_dev, - led_dev); - struct ieee80211_hw *dev = led->hw_dev; - struct p54_common *priv = dev->priv; - - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) - return ; - - if ((brightness) && (led->registered)) { - led->toggled++; - ieee80211_queue_delayed_work(priv->hw, &priv->led_work, HZ/10); - } -} - -static int p54_register_led(struct p54_common *priv, - unsigned int led_index, - char *name, const char *trigger) -{ - struct p54_led_dev *led = &priv->leds[led_index]; - int err; - - if (led->registered) - return -EEXIST; - - snprintf(led->name, sizeof(led->name), "p54-%s::%s", - wiphy_name(priv->hw->wiphy), name); - led->hw_dev = priv->hw; - led->index = led_index; - led->led_dev.name = led->name; - led->led_dev.default_trigger = trigger; - led->led_dev.brightness_set = p54_led_brightness_set; - - err = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_dev); - if (err) - wiphy_err(priv->hw->wiphy, - "Failed to register %s LED.\n", name); - else - led->registered = 1; - - return err; -} - -int p54_init_leds(struct p54_common *priv) -{ - int err; - - /* - * TODO: - * Figure out if the EEPROM contains some hints about the number - * of available/programmable LEDs of the device. - */ - - INIT_DELAYED_WORK(&priv->led_work, p54_update_leds); - - err = p54_register_led(priv, 0, "assoc", - ieee80211_get_assoc_led_name(priv->hw)); - if (err) - return err; - - err = p54_register_led(priv, 1, "tx", - ieee80211_get_tx_led_name(priv->hw)); - if (err) - return err; - - err = p54_register_led(priv, 2, "rx", - ieee80211_get_rx_led_name(priv->hw)); - if (err) - return err; - - err = p54_register_led(priv, 3, "radio", - ieee80211_get_radio_led_name(priv->hw)); - if (err) - return err; - - err = p54_set_leds(priv); - return err; -} - -void p54_unregister_leds(struct p54_common *priv) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(priv->leds); i++) { - if (priv->leds[i].registered) { - priv->leds[i].registered = false; - priv->leds[i].toggled = 0; - led_classdev_unregister(&priv->leds[i].led_dev); - } - } - - cancel_delayed_work_sync(&priv->led_work); -} diff --git a/drivers/net/wireless/p54/lmac.h b/drivers/net/wireless/p54/lmac.h deleted file mode 100644 index de1d46bf97df..000000000000 --- a/drivers/net/wireless/p54/lmac.h +++ /dev/null @@ -1,562 +0,0 @@ -/* - * LMAC Interface specific definitions for mac80211 Prism54 drivers - * - * Copyright (c) 2006, Michael Wu - * Copyright (c) 2007 - 2009, Christian Lamparter - * - * Based on: - * - the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - * - LMAC API interface header file for STLC4560 (lmac_longbow.h) - * Copyright (C) 2007 Conexant Systems, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef LMAC_H -#define LMAC_H - -enum p54_control_frame_types { - P54_CONTROL_TYPE_SETUP = 0, - P54_CONTROL_TYPE_SCAN, - P54_CONTROL_TYPE_TRAP, - P54_CONTROL_TYPE_DCFINIT, - P54_CONTROL_TYPE_RX_KEYCACHE, - P54_CONTROL_TYPE_TIM, - P54_CONTROL_TYPE_PSM, - P54_CONTROL_TYPE_TXCANCEL, - P54_CONTROL_TYPE_TXDONE, - P54_CONTROL_TYPE_BURST, - P54_CONTROL_TYPE_STAT_READBACK, - P54_CONTROL_TYPE_BBP, - P54_CONTROL_TYPE_EEPROM_READBACK, - P54_CONTROL_TYPE_LED, - P54_CONTROL_TYPE_GPIO, - P54_CONTROL_TYPE_TIMER, - P54_CONTROL_TYPE_MODULATION, - P54_CONTROL_TYPE_SYNTH_CONFIG, - P54_CONTROL_TYPE_DETECTOR_VALUE, - P54_CONTROL_TYPE_XBOW_SYNTH_CFG, - P54_CONTROL_TYPE_CCE_QUIET, - P54_CONTROL_TYPE_PSM_STA_UNLOCK, - P54_CONTROL_TYPE_PCS, - P54_CONTROL_TYPE_BT_BALANCER = 28, - P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE = 30, - P54_CONTROL_TYPE_ARPTABLE = 31, - P54_CONTROL_TYPE_BT_OPTIONS = 35, -}; - -#define P54_HDR_FLAG_CONTROL BIT(15) -#define P54_HDR_FLAG_CONTROL_OPSET (BIT(15) + BIT(0)) -#define P54_HDR_FLAG_DATA_ALIGN BIT(14) - -#define P54_HDR_FLAG_DATA_OUT_PROMISC BIT(0) -#define P54_HDR_FLAG_DATA_OUT_TIMESTAMP BIT(1) -#define P54_HDR_FLAG_DATA_OUT_SEQNR BIT(2) -#define P54_HDR_FLAG_DATA_OUT_BIT3 BIT(3) -#define P54_HDR_FLAG_DATA_OUT_BURST BIT(4) -#define P54_HDR_FLAG_DATA_OUT_NOCANCEL BIT(5) -#define P54_HDR_FLAG_DATA_OUT_CLEARTIM BIT(6) -#define P54_HDR_FLAG_DATA_OUT_HITCHHIKE BIT(7) -#define P54_HDR_FLAG_DATA_OUT_COMPRESS BIT(8) -#define P54_HDR_FLAG_DATA_OUT_CONCAT BIT(9) -#define P54_HDR_FLAG_DATA_OUT_PCS_ACCEPT BIT(10) -#define P54_HDR_FLAG_DATA_OUT_WAITEOSP BIT(11) - -#define P54_HDR_FLAG_DATA_IN_FCS_GOOD BIT(0) -#define P54_HDR_FLAG_DATA_IN_MATCH_MAC BIT(1) -#define P54_HDR_FLAG_DATA_IN_MCBC BIT(2) -#define P54_HDR_FLAG_DATA_IN_BEACON BIT(3) -#define P54_HDR_FLAG_DATA_IN_MATCH_BSS BIT(4) -#define P54_HDR_FLAG_DATA_IN_BCAST_BSS BIT(5) -#define P54_HDR_FLAG_DATA_IN_DATA BIT(6) -#define P54_HDR_FLAG_DATA_IN_TRUNCATED BIT(7) -#define P54_HDR_FLAG_DATA_IN_BIT8 BIT(8) -#define P54_HDR_FLAG_DATA_IN_TRANSPARENT BIT(9) - -struct p54_hdr { - __le16 flags; - __le16 len; - __le32 req_id; - __le16 type; /* enum p54_control_frame_types */ - u8 rts_tries; - u8 tries; - u8 data[0]; -} __packed; - -#define GET_REQ_ID(skb) \ - (((struct p54_hdr *) ((struct sk_buff *) skb)->data)->req_id) \ - -#define FREE_AFTER_TX(skb) \ - ((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ - flags) == cpu_to_le16(P54_HDR_FLAG_CONTROL_OPSET)) - -#define IS_DATA_FRAME(skb) \ - (!((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ - flags) & cpu_to_le16(P54_HDR_FLAG_CONTROL))) - -#define GET_HW_QUEUE(skb) \ - (((struct p54_tx_data *)((struct p54_hdr *) \ - skb->data)->data)->hw_queue) - -/* - * shared interface ID definitions - * The interface ID is a unique identification of a specific interface. - * The following values are reserved: 0x0000, 0x0002, 0x0012, 0x0014, 0x0015 - */ -#define IF_ID_ISL36356A 0x0001 /* ISL36356A <-> Firmware */ -#define IF_ID_MVC 0x0003 /* MAC Virtual Coprocessor */ -#define IF_ID_DEBUG 0x0008 /* PolDebug Interface */ -#define IF_ID_PRODUCT 0x0009 -#define IF_ID_OEM 0x000a -#define IF_ID_PCI3877 0x000b /* 3877 <-> Host PCI */ -#define IF_ID_ISL37704C 0x000c /* ISL37704C <-> Fw */ -#define IF_ID_ISL39000 0x000f /* ISL39000 <-> Fw */ -#define IF_ID_ISL39300A 0x0010 /* ISL39300A <-> Fw */ -#define IF_ID_ISL37700_UAP 0x0016 /* ISL37700 uAP Fw <-> Fw */ -#define IF_ID_ISL39000_UAP 0x0017 /* ISL39000 uAP Fw <-> Fw */ -#define IF_ID_LMAC 0x001a /* Interface exposed by LMAC */ - -struct exp_if { - __le16 role; - __le16 if_id; - __le16 variant; - __le16 btm_compat; - __le16 top_compat; -} __packed; - -struct dep_if { - __le16 role; - __le16 if_id; - __le16 variant; -} __packed; - -/* driver <-> lmac definitions */ -struct p54_eeprom_lm86 { - union { - struct { - __le16 offset; - __le16 len; - u8 data[0]; - } __packed v1; - struct { - __le32 offset; - __le16 len; - u8 magic2; - u8 pad; - u8 magic[4]; - u8 data[0]; - } __packed v2; - } __packed; -} __packed; - -enum p54_rx_decrypt_status { - P54_DECRYPT_NONE = 0, - P54_DECRYPT_OK, - P54_DECRYPT_NOKEY, - P54_DECRYPT_NOMICHAEL, - P54_DECRYPT_NOCKIPMIC, - P54_DECRYPT_FAIL_WEP, - P54_DECRYPT_FAIL_TKIP, - P54_DECRYPT_FAIL_MICHAEL, - P54_DECRYPT_FAIL_CKIPKP, - P54_DECRYPT_FAIL_CKIPMIC, - P54_DECRYPT_FAIL_AESCCMP -}; - -struct p54_rx_data { - __le16 flags; - __le16 len; - __le16 freq; - u8 antenna; - u8 rate; - u8 rssi; - u8 quality; - u8 decrypt_status; - u8 rssi_raw; - __le32 tsf32; - __le32 unalloc0; - u8 align[0]; -} __packed; - -enum p54_trap_type { - P54_TRAP_SCAN = 0, - P54_TRAP_TIMER, - P54_TRAP_BEACON_TX, - P54_TRAP_FAA_RADIO_ON, - P54_TRAP_FAA_RADIO_OFF, - P54_TRAP_RADAR, - P54_TRAP_NO_BEACON, - P54_TRAP_TBTT, - P54_TRAP_SCO_ENTER, - P54_TRAP_SCO_EXIT -}; - -struct p54_trap { - __le16 event; - __le16 frequency; -} __packed; - -enum p54_frame_sent_status { - P54_TX_OK = 0, - P54_TX_FAILED, - P54_TX_PSM, - P54_TX_PSM_CANCELLED = 4 -}; - -struct p54_frame_sent { - u8 status; - u8 tries; - u8 ack_rssi; - u8 quality; - __le16 seq; - u8 antenna; - u8 padding; -} __packed; - -enum p54_tx_data_crypt { - P54_CRYPTO_NONE = 0, - P54_CRYPTO_WEP, - P54_CRYPTO_TKIP, - P54_CRYPTO_TKIPMICHAEL, - P54_CRYPTO_CCX_WEPMIC, - P54_CRYPTO_CCX_KPMIC, - P54_CRYPTO_CCX_KP, - P54_CRYPTO_AESCCMP -}; - -enum p54_tx_data_queue { - P54_QUEUE_BEACON = 0, - P54_QUEUE_FWSCAN = 1, - P54_QUEUE_MGMT = 2, - P54_QUEUE_CAB = 3, - P54_QUEUE_DATA = 4, - - P54_QUEUE_AC_NUM = 4, - P54_QUEUE_AC_VO = 4, - P54_QUEUE_AC_VI = 5, - P54_QUEUE_AC_BE = 6, - P54_QUEUE_AC_BK = 7, - - /* keep last */ - P54_QUEUE_NUM = 8, -}; - -#define IS_QOS_QUEUE(n) (n >= P54_QUEUE_DATA) - -struct p54_tx_data { - u8 rateset[8]; - u8 rts_rate_idx; - u8 crypt_offset; - u8 key_type; - u8 key_len; - u8 key[16]; - u8 hw_queue; - u8 backlog; - __le16 durations[4]; - u8 tx_antenna; - union { - struct { - u8 cts_rate; - __le16 output_power; - } __packed longbow; - struct { - u8 output_power; - u8 cts_rate; - u8 unalloc; - } __packed normal; - } __packed; - u8 unalloc2[2]; - u8 align[0]; -} __packed; - -/* unit is ms */ -#define P54_TX_FRAME_LIFETIME 2000 -#define P54_TX_TIMEOUT 4000 -#define P54_STATISTICS_UPDATE 5000 - -#define P54_FILTER_TYPE_NONE 0 -#define P54_FILTER_TYPE_STATION BIT(0) -#define P54_FILTER_TYPE_IBSS BIT(1) -#define P54_FILTER_TYPE_AP BIT(2) -#define P54_FILTER_TYPE_TRANSPARENT BIT(3) -#define P54_FILTER_TYPE_PROMISCUOUS BIT(4) -#define P54_FILTER_TYPE_HIBERNATE BIT(5) -#define P54_FILTER_TYPE_NOACK BIT(6) -#define P54_FILTER_TYPE_RX_DISABLED BIT(7) - -struct p54_setup_mac { - __le16 mac_mode; - u8 mac_addr[ETH_ALEN]; - u8 bssid[ETH_ALEN]; - u8 rx_antenna; - u8 rx_align; - union { - struct { - __le32 basic_rate_mask; - u8 rts_rates[8]; - __le32 rx_addr; - __le16 max_rx; - __le16 rxhw; - __le16 wakeup_timer; - __le16 unalloc0; - } __packed v1; - struct { - __le32 rx_addr; - __le16 max_rx; - __le16 rxhw; - __le16 timer; - __le16 truncate; - __le32 basic_rate_mask; - u8 sbss_offset; - u8 mcast_window; - u8 rx_rssi_threshold; - u8 rx_ed_threshold; - __le32 ref_clock; - __le16 lpf_bandwidth; - __le16 osc_start_delay; - } __packed v2; - } __packed; -} __packed; - -#define P54_SETUP_V1_LEN 40 -#define P54_SETUP_V2_LEN (sizeof(struct p54_setup_mac)) - -#define P54_SCAN_EXIT BIT(0) -#define P54_SCAN_TRAP BIT(1) -#define P54_SCAN_ACTIVE BIT(2) -#define P54_SCAN_FILTER BIT(3) - -struct p54_scan_head { - __le16 mode; - __le16 dwell; - u8 scan_params[20]; - __le16 freq; -} __packed; - -struct p54_pa_curve_data_sample { - u8 rf_power; - u8 pa_detector; - u8 data_barker; - u8 data_bpsk; - u8 data_qpsk; - u8 data_16qam; - u8 data_64qam; - u8 padding; -} __packed; - -struct p54_scan_body { - u8 pa_points_per_curve; - u8 val_barker; - u8 val_bpsk; - u8 val_qpsk; - u8 val_16qam; - u8 val_64qam; - struct p54_pa_curve_data_sample curve_data[8]; - u8 dup_bpsk; - u8 dup_qpsk; - u8 dup_16qam; - u8 dup_64qam; -} __packed; - -/* - * Warning: Longbow's structures are bogus. - */ -struct p54_channel_output_limit_longbow { - __le16 rf_power_points[12]; -} __packed; - -struct p54_pa_curve_data_sample_longbow { - __le16 rf_power; - __le16 pa_detector; - struct { - __le16 data[4]; - } points[3] __packed; -} __packed; - -struct p54_scan_body_longbow { - struct p54_channel_output_limit_longbow power_limits; - struct p54_pa_curve_data_sample_longbow curve_data[8]; - __le16 unkn[6]; /* maybe more power_limits or rate_mask */ -} __packed; - -union p54_scan_body_union { - struct p54_scan_body normal; - struct p54_scan_body_longbow longbow; -} __packed; - -struct p54_scan_tail_rate { - __le32 basic_rate_mask; - u8 rts_rates[8]; -} __packed; - -struct p54_led { - __le16 flags; - __le16 mask[2]; - __le16 delay[2]; -} __packed; - -struct p54_edcf { - u8 flags; - u8 slottime; - u8 sifs; - u8 eofpad; - struct p54_edcf_queue_param queue[8]; - u8 mapping[4]; - __le16 frameburst; - __le16 round_trip_delay; -} __packed; - -struct p54_statistics { - __le32 rx_success; - __le32 rx_bad_fcs; - __le32 rx_abort; - __le32 rx_abort_phy; - __le32 rts_success; - __le32 rts_fail; - __le32 tsf32; - __le32 airtime; - __le32 noise; - __le32 sample_noise[8]; - __le32 sample_cca; - __le32 sample_tx; -} __packed; - -struct p54_xbow_synth { - __le16 magic1; - __le16 magic2; - __le16 freq; - u32 padding[5]; -} __packed; - -struct p54_timer { - __le32 interval; -} __packed; - -struct p54_keycache { - u8 entry; - u8 key_id; - u8 mac[ETH_ALEN]; - u8 padding[2]; - u8 key_type; - u8 key_len; - u8 key[24]; -} __packed; - -struct p54_burst { - u8 flags; - u8 queue; - u8 backlog; - u8 pad; - __le16 durations[32]; -} __packed; - -struct p54_psm_interval { - __le16 interval; - __le16 periods; -} __packed; - -#define P54_PSM_CAM 0 -#define P54_PSM BIT(0) -#define P54_PSM_DTIM BIT(1) -#define P54_PSM_MCBC BIT(2) -#define P54_PSM_CHECKSUM BIT(3) -#define P54_PSM_SKIP_MORE_DATA BIT(4) -#define P54_PSM_BEACON_TIMEOUT BIT(5) -#define P54_PSM_HFOSLEEP BIT(6) -#define P54_PSM_AUTOSWITCH_SLEEP BIT(7) -#define P54_PSM_LPIT BIT(8) -#define P54_PSM_BF_UCAST_SKIP BIT(9) -#define P54_PSM_BF_MCAST_SKIP BIT(10) - -struct p54_psm { - __le16 mode; - __le16 aid; - struct p54_psm_interval intervals[4]; - u8 beacon_rssi_skip_max; - u8 rssi_delta_threshold; - u8 nr; - u8 exclude[1]; -} __packed; - -#define MC_FILTER_ADDRESS_NUM 4 - -struct p54_group_address_table { - __le16 filter_enable; - __le16 num_address; - u8 mac_list[MC_FILTER_ADDRESS_NUM][ETH_ALEN]; -} __packed; - -struct p54_txcancel { - __le32 req_id; -} __packed; - -struct p54_sta_unlock { - u8 addr[ETH_ALEN]; - u16 padding; -} __packed; - -#define P54_TIM_CLEAR BIT(15) -struct p54_tim { - u8 count; - u8 padding[3]; - __le16 entry[8]; -} __packed; - -struct p54_cce_quiet { - __le32 period; -} __packed; - -struct p54_bt_balancer { - __le16 prio_thresh; - __le16 acl_thresh; -} __packed; - -struct p54_arp_table { - __le16 filter_enable; - u8 ipv4_addr[4]; -} __packed; - -/* LED control */ -int p54_set_leds(struct p54_common *priv); -int p54_init_leds(struct p54_common *priv); -void p54_unregister_leds(struct p54_common *priv); - -/* xmit functions */ -void p54_tx_80211(struct ieee80211_hw *dev, - struct ieee80211_tx_control *control, - struct sk_buff *skb); -int p54_tx_cancel(struct p54_common *priv, __le32 req_id); -void p54_tx(struct p54_common *priv, struct sk_buff *skb); - -/* synth/phy configuration */ -int p54_init_xbow_synth(struct p54_common *priv); -int p54_scan(struct p54_common *priv, u16 mode, u16 dwell); - -/* MAC */ -int p54_sta_unlock(struct p54_common *priv, u8 *addr); -int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set); -int p54_setup_mac(struct p54_common *priv); -int p54_set_ps(struct p54_common *priv); -int p54_fetch_statistics(struct p54_common *priv); -int p54_set_groupfilter(struct p54_common *priv); - -/* e/v DCF setup */ -int p54_set_edcf(struct p54_common *priv); - -/* cryptographic engine */ -int p54_upload_key(struct p54_common *priv, u8 algo, int slot, - u8 idx, u8 len, u8 *addr, u8* key); - -/* eeprom */ -int p54_download_eeprom(struct p54_common *priv, void *buf, - u16 offset, u16 len); -struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *p, const u16 freq); - -/* utility */ -u8 *p54_find_ie(struct sk_buff *skb, u8 ie); - -#endif /* LMAC_H */ diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c deleted file mode 100644 index 7805864e76f9..000000000000 --- a/drivers/net/wireless/p54/main.c +++ /dev/null @@ -1,867 +0,0 @@ -/* - * mac80211 glue code for mac80211 Prism54 drivers - * - * Copyright (c) 2006, Michael Wu - * Copyright (c) 2007-2009, Christian Lamparter - * Copyright 2008, Johannes Berg - * - * Based on: - * - the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - stlc45xx driver - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include - -#include "p54.h" -#include "lmac.h" - -static bool modparam_nohwcrypt; -module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); -MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); -MODULE_AUTHOR("Michael Wu "); -MODULE_DESCRIPTION("Softmac Prism54 common code"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("prism54common"); - -static int p54_sta_add_remove(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct p54_common *priv = hw->priv; - - /* - * Notify the firmware that we don't want or we don't - * need to buffer frames for this station anymore. - */ - - p54_sta_unlock(priv, sta->addr); - - return 0; -} - -static void p54_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, - enum sta_notify_cmd notify_cmd, - struct ieee80211_sta *sta) -{ - struct p54_common *priv = dev->priv; - - switch (notify_cmd) { - case STA_NOTIFY_AWAKE: - /* update the firmware's filter table */ - p54_sta_unlock(priv, sta->addr); - break; - default: - break; - } -} - -static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, - bool set) -{ - struct p54_common *priv = dev->priv; - - return p54_update_beacon_tim(priv, sta->aid, set); -} - -u8 *p54_find_ie(struct sk_buff *skb, u8 ie) -{ - struct ieee80211_mgmt *mgmt = (void *)skb->data; - u8 *pos, *end; - - if (skb->len <= sizeof(mgmt)) - return NULL; - - pos = (u8 *)mgmt->u.beacon.variable; - end = skb->data + skb->len; - while (pos < end) { - if (pos + 2 + pos[1] > end) - return NULL; - - if (pos[0] == ie) - return pos; - - pos += 2 + pos[1]; - } - return NULL; -} - -static int p54_beacon_format_ie_tim(struct sk_buff *skb) -{ - /* - * the good excuse for this mess is ... the firmware. - * The dummy TIM MUST be at the end of the beacon frame, - * because it'll be overwritten! - */ - u8 *tim; - u8 dtim_len; - u8 dtim_period; - u8 *next; - - tim = p54_find_ie(skb, WLAN_EID_TIM); - if (!tim) - return 0; - - dtim_len = tim[1]; - dtim_period = tim[3]; - next = tim + 2 + dtim_len; - - if (dtim_len < 3) - return -EINVAL; - - memmove(tim, next, skb_tail_pointer(skb) - next); - tim = skb_tail_pointer(skb) - (dtim_len + 2); - - /* add the dummy at the end */ - tim[0] = WLAN_EID_TIM; - tim[1] = 3; - tim[2] = 0; - tim[3] = dtim_period; - tim[4] = 0; - - if (dtim_len > 3) - skb_trim(skb, skb->len - (dtim_len - 3)); - - return 0; -} - -static int p54_beacon_update(struct p54_common *priv, - struct ieee80211_vif *vif) -{ - struct ieee80211_tx_control control = { }; - struct sk_buff *beacon; - int ret; - - beacon = ieee80211_beacon_get(priv->hw, vif); - if (!beacon) - return -ENOMEM; - ret = p54_beacon_format_ie_tim(beacon); - if (ret) - return ret; - - /* - * During operation, the firmware takes care of beaconing. - * The driver only needs to upload a new beacon template, once - * the template was changed by the stack or userspace. - * - * LMAC API 3.2.2 also specifies that the driver does not need - * to cancel the old beacon template by hand, instead the firmware - * will release the previous one through the feedback mechanism. - */ - p54_tx_80211(priv->hw, &control, beacon); - priv->tsf_high32 = 0; - priv->tsf_low32 = 0; - - return 0; -} - -static int p54_start(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - int err; - - mutex_lock(&priv->conf_mutex); - err = priv->open(dev); - if (err) - goto out; - P54_SET_QUEUE(priv->qos_params[0], 0x0002, 0x0003, 0x0007, 47); - P54_SET_QUEUE(priv->qos_params[1], 0x0002, 0x0007, 0x000f, 94); - P54_SET_QUEUE(priv->qos_params[2], 0x0003, 0x000f, 0x03ff, 0); - P54_SET_QUEUE(priv->qos_params[3], 0x0007, 0x000f, 0x03ff, 0); - err = p54_set_edcf(priv); - if (err) - goto out; - - eth_broadcast_addr(priv->bssid); - priv->mode = NL80211_IFTYPE_MONITOR; - err = p54_setup_mac(priv); - if (err) { - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - goto out; - } - - ieee80211_queue_delayed_work(dev, &priv->work, 0); - - priv->softled_state = 0; - err = p54_set_leds(priv); - -out: - mutex_unlock(&priv->conf_mutex); - return err; -} - -static void p54_stop(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - int i; - - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - priv->softled_state = 0; - cancel_delayed_work_sync(&priv->work); - mutex_lock(&priv->conf_mutex); - p54_set_leds(priv); - priv->stop(dev); - skb_queue_purge(&priv->tx_pending); - skb_queue_purge(&priv->tx_queue); - for (i = 0; i < P54_QUEUE_NUM; i++) { - priv->tx_stats[i].count = 0; - priv->tx_stats[i].len = 0; - } - - priv->beacon_req_id = cpu_to_le32(0); - priv->tsf_high32 = priv->tsf_low32 = 0; - mutex_unlock(&priv->conf_mutex); -} - -static int p54_add_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct p54_common *priv = dev->priv; - int err; - - vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; - - mutex_lock(&priv->conf_mutex); - if (priv->mode != NL80211_IFTYPE_MONITOR) { - mutex_unlock(&priv->conf_mutex); - return -EOPNOTSUPP; - } - - priv->vif = vif; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_MESH_POINT: - priv->mode = vif->type; - break; - default: - mutex_unlock(&priv->conf_mutex); - return -EOPNOTSUPP; - } - - memcpy(priv->mac_addr, vif->addr, ETH_ALEN); - err = p54_setup_mac(priv); - mutex_unlock(&priv->conf_mutex); - return err; -} - -static void p54_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct p54_common *priv = dev->priv; - - mutex_lock(&priv->conf_mutex); - priv->vif = NULL; - - /* - * LMAC API 3.2.2 states that any active beacon template must be - * canceled by the driver before attempting a mode transition. - */ - if (le32_to_cpu(priv->beacon_req_id) != 0) { - p54_tx_cancel(priv, priv->beacon_req_id); - wait_for_completion_interruptible_timeout(&priv->beacon_comp, HZ); - } - priv->mode = NL80211_IFTYPE_MONITOR; - eth_zero_addr(priv->mac_addr); - eth_zero_addr(priv->bssid); - p54_setup_mac(priv); - mutex_unlock(&priv->conf_mutex); -} - -static int p54_wait_for_stats(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - int ret; - - priv->update_stats = true; - ret = p54_fetch_statistics(priv); - if (ret) - return ret; - - ret = wait_for_completion_interruptible_timeout(&priv->stat_comp, HZ); - if (ret == 0) - return -ETIMEDOUT; - - return 0; -} - -static void p54_reset_stats(struct p54_common *priv) -{ - struct ieee80211_channel *chan = priv->curchan; - - if (chan) { - struct survey_info *info = &priv->survey[chan->hw_value]; - - /* only reset channel statistics, don't touch .filled, etc. */ - info->time = 0; - info->time_busy = 0; - info->time_tx = 0; - } - - priv->update_stats = true; - priv->survey_raw.active = 0; - priv->survey_raw.cca = 0; - priv->survey_raw.tx = 0; -} - -static int p54_config(struct ieee80211_hw *dev, u32 changed) -{ - int ret = 0; - struct p54_common *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; - - mutex_lock(&priv->conf_mutex); - if (changed & IEEE80211_CONF_CHANGE_POWER) - priv->output_power = conf->power_level << 2; - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - struct ieee80211_channel *oldchan; - WARN_ON(p54_wait_for_stats(dev)); - oldchan = priv->curchan; - priv->curchan = NULL; - ret = p54_scan(priv, P54_SCAN_EXIT, 0); - if (ret) { - priv->curchan = oldchan; - goto out; - } - /* - * TODO: Use the LM_SCAN_TRAP to determine the current - * operating channel. - */ - priv->curchan = priv->hw->conf.chandef.chan; - p54_reset_stats(priv); - WARN_ON(p54_fetch_statistics(priv)); - } - if (changed & IEEE80211_CONF_CHANGE_PS) { - WARN_ON(p54_wait_for_stats(dev)); - ret = p54_set_ps(priv); - if (ret) - goto out; - WARN_ON(p54_wait_for_stats(dev)); - } - if (changed & IEEE80211_CONF_CHANGE_IDLE) { - WARN_ON(p54_wait_for_stats(dev)); - ret = p54_setup_mac(priv); - if (ret) - goto out; - WARN_ON(p54_wait_for_stats(dev)); - } - -out: - mutex_unlock(&priv->conf_mutex); - return ret; -} - -static u64 p54_prepare_multicast(struct ieee80211_hw *dev, - struct netdev_hw_addr_list *mc_list) -{ - struct p54_common *priv = dev->priv; - struct netdev_hw_addr *ha; - int i; - - BUILD_BUG_ON(ARRAY_SIZE(priv->mc_maclist) != - ARRAY_SIZE(((struct p54_group_address_table *)NULL)->mac_list)); - /* - * The first entry is reserved for the global broadcast MAC. - * Otherwise the firmware will drop it and ARP will no longer work. - */ - i = 1; - priv->mc_maclist_num = netdev_hw_addr_list_count(mc_list) + i; - netdev_hw_addr_list_for_each(ha, mc_list) { - memcpy(&priv->mc_maclist[i], ha->addr, ETH_ALEN); - i++; - if (i >= ARRAY_SIZE(priv->mc_maclist)) - break; - } - - return 1; /* update */ -} - -static void p54_configure_filter(struct ieee80211_hw *dev, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct p54_common *priv = dev->priv; - - *total_flags &= FIF_ALLMULTI | FIF_OTHER_BSS; - - priv->filter_flags = *total_flags; - - if (changed_flags & FIF_OTHER_BSS) - p54_setup_mac(priv); - - if (changed_flags & FIF_ALLMULTI || multicast) - p54_set_groupfilter(priv); -} - -static int p54_conf_tx(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct p54_common *priv = dev->priv; - int ret; - - mutex_lock(&priv->conf_mutex); - if (queue < dev->queues) { - P54_SET_QUEUE(priv->qos_params[queue], params->aifs, - params->cw_min, params->cw_max, params->txop); - ret = p54_set_edcf(priv); - } else - ret = -EINVAL; - mutex_unlock(&priv->conf_mutex); - return ret; -} - -static void p54_work(struct work_struct *work) -{ - struct p54_common *priv = container_of(work, struct p54_common, - work.work); - - if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) - return ; - - /* - * TODO: walk through tx_queue and do the following tasks - * 1. initiate bursts. - * 2. cancel stuck frames / reset the device if necessary. - */ - - mutex_lock(&priv->conf_mutex); - WARN_ON_ONCE(p54_fetch_statistics(priv)); - mutex_unlock(&priv->conf_mutex); -} - -static int p54_get_stats(struct ieee80211_hw *dev, - struct ieee80211_low_level_stats *stats) -{ - struct p54_common *priv = dev->priv; - - memcpy(stats, &priv->stats, sizeof(*stats)); - return 0; -} - -static void p54_bss_info_changed(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) -{ - struct p54_common *priv = dev->priv; - - mutex_lock(&priv->conf_mutex); - if (changed & BSS_CHANGED_BSSID) { - memcpy(priv->bssid, info->bssid, ETH_ALEN); - p54_setup_mac(priv); - } - - if (changed & BSS_CHANGED_BEACON) { - p54_scan(priv, P54_SCAN_EXIT, 0); - p54_setup_mac(priv); - p54_beacon_update(priv, vif); - p54_set_edcf(priv); - } - - if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BEACON)) { - priv->use_short_slot = info->use_short_slot; - p54_set_edcf(priv); - } - if (changed & BSS_CHANGED_BASIC_RATES) { - if (dev->conf.chandef.chan->band == IEEE80211_BAND_5GHZ) - priv->basic_rate_mask = (info->basic_rates << 4); - else - priv->basic_rate_mask = info->basic_rates; - p54_setup_mac(priv); - if (priv->fw_var >= 0x500) - p54_scan(priv, P54_SCAN_EXIT, 0); - } - if (changed & BSS_CHANGED_ASSOC) { - if (info->assoc) { - priv->aid = info->aid; - priv->wakeup_timer = info->beacon_int * - info->dtim_period * 5; - p54_setup_mac(priv); - } else { - priv->wakeup_timer = 500; - priv->aid = 0; - } - } - - mutex_unlock(&priv->conf_mutex); -} - -static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct p54_common *priv = dev->priv; - int slot, ret = 0; - u8 algo = 0; - u8 *addr = NULL; - - if (modparam_nohwcrypt) - return -EOPNOTSUPP; - - if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) { - /* - * Unfortunately most/all firmwares are trying to decrypt - * incoming management frames if a suitable key can be found. - * However, in doing so the data in these frames gets - * corrupted. So, we can't have firmware supported crypto - * offload in this case. - */ - return -EOPNOTSUPP; - } - - mutex_lock(&priv->conf_mutex); - if (cmd == SET_KEY) { - switch (key->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL | - BR_DESC_PRIV_CAP_TKIP))) { - ret = -EOPNOTSUPP; - goto out_unlock; - } - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - algo = P54_CRYPTO_TKIPMICHAEL; - break; - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) { - ret = -EOPNOTSUPP; - goto out_unlock; - } - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - algo = P54_CRYPTO_WEP; - break; - case WLAN_CIPHER_SUITE_CCMP: - if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) { - ret = -EOPNOTSUPP; - goto out_unlock; - } - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - algo = P54_CRYPTO_AESCCMP; - break; - default: - ret = -EOPNOTSUPP; - goto out_unlock; - } - slot = bitmap_find_free_region(priv->used_rxkeys, - priv->rx_keycache_size, 0); - - if (slot < 0) { - /* - * The device supports the chosen algorithm, but the - * firmware does not provide enough key slots to store - * all of them. - * But encryption offload for outgoing frames is always - * possible, so we just pretend that the upload was - * successful and do the decryption in software. - */ - - /* mark the key as invalid. */ - key->hw_key_idx = 0xff; - goto out_unlock; - } - - key->flags |= IEEE80211_KEY_FLAG_RESERVE_TAILROOM; - } else { - slot = key->hw_key_idx; - - if (slot == 0xff) { - /* This key was not uploaded into the rx key cache. */ - - goto out_unlock; - } - - bitmap_release_region(priv->used_rxkeys, slot, 0); - algo = 0; - } - - if (sta) - addr = sta->addr; - - ret = p54_upload_key(priv, algo, slot, key->keyidx, - key->keylen, addr, key->key); - if (ret) { - bitmap_release_region(priv->used_rxkeys, slot, 0); - ret = -EOPNOTSUPP; - goto out_unlock; - } - - key->hw_key_idx = slot; - -out_unlock: - mutex_unlock(&priv->conf_mutex); - return ret; -} - -static int p54_get_survey(struct ieee80211_hw *dev, int idx, - struct survey_info *survey) -{ - struct p54_common *priv = dev->priv; - struct ieee80211_channel *chan; - int err, tries; - bool in_use = false; - - if (idx >= priv->chan_num) - return -ENOENT; - -#define MAX_TRIES 1 - for (tries = 0; tries < MAX_TRIES; tries++) { - chan = priv->curchan; - if (chan && chan->hw_value == idx) { - mutex_lock(&priv->conf_mutex); - err = p54_wait_for_stats(dev); - mutex_unlock(&priv->conf_mutex); - if (err) - return err; - - in_use = true; - } - - memcpy(survey, &priv->survey[idx], sizeof(*survey)); - - if (in_use) { - /* test if the reported statistics are valid. */ - if (survey->time != 0) { - survey->filled |= SURVEY_INFO_IN_USE; - } else { - /* - * hw/fw has not accumulated enough sample sets. - * Wait for 100ms, this ought to be enough to - * to get at least one non-null set of channel - * usage statistics. - */ - msleep(100); - continue; - } - } - return 0; - } - return -ETIMEDOUT; -#undef MAX_TRIES -} - -static unsigned int p54_flush_count(struct p54_common *priv) -{ - unsigned int total = 0, i; - - BUILD_BUG_ON(P54_QUEUE_NUM > ARRAY_SIZE(priv->tx_stats)); - - /* - * Because the firmware has the sole control over any frames - * in the P54_QUEUE_BEACON or P54_QUEUE_SCAN queues, they - * don't really count as pending or active. - */ - for (i = P54_QUEUE_MGMT; i < P54_QUEUE_NUM; i++) - total += priv->tx_stats[i].len; - return total; -} - -static void p54_flush(struct ieee80211_hw *dev, struct ieee80211_vif *vif, - u32 queues, bool drop) -{ - struct p54_common *priv = dev->priv; - unsigned int total, i; - - /* - * Currently, it wouldn't really matter if we wait for one second - * or 15 minutes. But once someone gets around and completes the - * TODOs [ancel stuck frames / reset device] in p54_work, it will - * suddenly make sense to wait that long. - */ - i = P54_STATISTICS_UPDATE * 2 / 20; - - /* - * In this case no locking is required because as we speak the - * queues have already been stopped and no new frames can sneak - * up from behind. - */ - while ((total = p54_flush_count(priv) && i--)) { - /* waste time */ - msleep(20); - } - - WARN(total, "tx flush timeout, unresponsive firmware"); -} - -static void p54_set_coverage_class(struct ieee80211_hw *dev, - s16 coverage_class) -{ - struct p54_common *priv = dev->priv; - - mutex_lock(&priv->conf_mutex); - /* support all coverage class values as in 802.11-2007 Table 7-27 */ - priv->coverage_class = clamp_t(u8, coverage_class, 0, 31); - p54_set_edcf(priv); - mutex_unlock(&priv->conf_mutex); -} - -static const struct ieee80211_ops p54_ops = { - .tx = p54_tx_80211, - .start = p54_start, - .stop = p54_stop, - .add_interface = p54_add_interface, - .remove_interface = p54_remove_interface, - .set_tim = p54_set_tim, - .sta_notify = p54_sta_notify, - .sta_add = p54_sta_add_remove, - .sta_remove = p54_sta_add_remove, - .set_key = p54_set_key, - .config = p54_config, - .flush = p54_flush, - .bss_info_changed = p54_bss_info_changed, - .prepare_multicast = p54_prepare_multicast, - .configure_filter = p54_configure_filter, - .conf_tx = p54_conf_tx, - .get_stats = p54_get_stats, - .get_survey = p54_get_survey, - .set_coverage_class = p54_set_coverage_class, -}; - -struct ieee80211_hw *p54_init_common(size_t priv_data_len) -{ - struct ieee80211_hw *dev; - struct p54_common *priv; - - dev = ieee80211_alloc_hw(priv_data_len, &p54_ops); - if (!dev) - return NULL; - - priv = dev->priv; - priv->hw = dev; - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - priv->basic_rate_mask = 0x15f; - spin_lock_init(&priv->tx_stats_lock); - skb_queue_head_init(&priv->tx_queue); - skb_queue_head_init(&priv->tx_pending); - ieee80211_hw_set(dev, REPORTS_TX_ACK_STATUS); - ieee80211_hw_set(dev, MFP_CAPABLE); - ieee80211_hw_set(dev, PS_NULLFUNC_STACK); - ieee80211_hw_set(dev, SUPPORTS_PS); - ieee80211_hw_set(dev, RX_INCLUDES_FCS); - ieee80211_hw_set(dev, SIGNAL_DBM); - - dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT); - - priv->beacon_req_id = cpu_to_le32(0); - priv->tx_stats[P54_QUEUE_BEACON].limit = 1; - priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1; - priv->tx_stats[P54_QUEUE_MGMT].limit = 3; - priv->tx_stats[P54_QUEUE_CAB].limit = 3; - priv->tx_stats[P54_QUEUE_DATA].limit = 5; - dev->queues = 1; - priv->noise = -94; - /* - * We support at most 8 tries no matter which rate they're at, - * we cannot support max_rates * max_rate_tries as we set it - * here, but setting it correctly to 4/2 or so would limit us - * artificially if the RC algorithm wants just two rates, so - * let's say 4/7, we'll redistribute it at TX time, see the - * comments there. - */ - dev->max_rates = 4; - dev->max_rate_tries = 7; - dev->extra_tx_headroom = sizeof(struct p54_hdr) + 4 + - sizeof(struct p54_tx_data); - - /* - * For now, disable PS by default because it affects - * link stability significantly. - */ - dev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - mutex_init(&priv->conf_mutex); - mutex_init(&priv->eeprom_mutex); - init_completion(&priv->stat_comp); - init_completion(&priv->eeprom_comp); - init_completion(&priv->beacon_comp); - INIT_DELAYED_WORK(&priv->work, p54_work); - - eth_broadcast_addr(priv->mc_maclist[0]); - priv->curchan = NULL; - p54_reset_stats(priv); - return dev; -} -EXPORT_SYMBOL_GPL(p54_init_common); - -int p54_register_common(struct ieee80211_hw *dev, struct device *pdev) -{ - struct p54_common __maybe_unused *priv = dev->priv; - int err; - - err = ieee80211_register_hw(dev); - if (err) { - dev_err(pdev, "Cannot register device (%d).\n", err); - return err; - } - priv->registered = true; - -#ifdef CONFIG_P54_LEDS - err = p54_init_leds(priv); - if (err) { - p54_unregister_common(dev); - return err; - } -#endif /* CONFIG_P54_LEDS */ - - dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy)); - return 0; -} -EXPORT_SYMBOL_GPL(p54_register_common); - -void p54_free_common(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - unsigned int i; - - for (i = 0; i < IEEE80211_NUM_BANDS; i++) - kfree(priv->band_table[i]); - - kfree(priv->iq_autocal); - kfree(priv->output_limit); - kfree(priv->curve_data); - kfree(priv->rssi_db); - kfree(priv->used_rxkeys); - kfree(priv->survey); - priv->iq_autocal = NULL; - priv->output_limit = NULL; - priv->curve_data = NULL; - priv->rssi_db = NULL; - priv->used_rxkeys = NULL; - priv->survey = NULL; - ieee80211_free_hw(dev); -} -EXPORT_SYMBOL_GPL(p54_free_common); - -void p54_unregister_common(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - -#ifdef CONFIG_P54_LEDS - p54_unregister_leds(priv); -#endif /* CONFIG_P54_LEDS */ - - if (priv->registered) { - priv->registered = false; - ieee80211_unregister_hw(dev); - } - - mutex_destroy(&priv->conf_mutex); - mutex_destroy(&priv->eeprom_mutex); -} -EXPORT_SYMBOL_GPL(p54_unregister_common); diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h deleted file mode 100644 index 40b401ed6845..000000000000 --- a/drivers/net/wireless/p54/p54.h +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Shared defines for all mac80211 Prism54 code - * - * Copyright (c) 2006, Michael Wu - * - * Based on the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef P54_H -#define P54_H - -#ifdef CONFIG_P54_LEDS -#include -#endif /* CONFIG_P54_LEDS */ - -#define ISL38XX_DEV_FIRMWARE_ADDR 0x20000 - -#define BR_CODE_MIN 0x80000000 -#define BR_CODE_COMPONENT_ID 0x80000001 -#define BR_CODE_COMPONENT_VERSION 0x80000002 -#define BR_CODE_DEPENDENT_IF 0x80000003 -#define BR_CODE_EXPOSED_IF 0x80000004 -#define BR_CODE_DESCR 0x80000101 -#define BR_CODE_MAX 0x8FFFFFFF -#define BR_CODE_END_OF_BRA 0xFF0000FF -#define LEGACY_BR_CODE_END_OF_BRA 0xFFFFFFFF - -struct bootrec { - __le32 code; - __le32 len; - u32 data[10]; -} __packed; - -/* Interface role definitions */ -#define BR_INTERFACE_ROLE_SERVER 0x0000 -#define BR_INTERFACE_ROLE_CLIENT 0x8000 - -#define BR_DESC_PRIV_CAP_WEP BIT(0) -#define BR_DESC_PRIV_CAP_TKIP BIT(1) -#define BR_DESC_PRIV_CAP_MICHAEL BIT(2) -#define BR_DESC_PRIV_CAP_CCX_CP BIT(3) -#define BR_DESC_PRIV_CAP_CCX_MIC BIT(4) -#define BR_DESC_PRIV_CAP_AESCCMP BIT(5) - -struct bootrec_desc { - __le16 modes; - __le16 flags; - __le32 rx_start; - __le32 rx_end; - u8 headroom; - u8 tailroom; - u8 tx_queues; - u8 tx_depth; - u8 privacy_caps; - u8 rx_keycache_size; - u8 time_size; - u8 padding; - u8 rates[16]; - u8 padding2[4]; - __le16 rx_mtu; -} __packed; - -#define FW_FMAC 0x464d4143 -#define FW_LM86 0x4c4d3836 -#define FW_LM87 0x4c4d3837 -#define FW_LM20 0x4c4d3230 - -struct bootrec_comp_id { - __le32 fw_variant; -} __packed; - -struct bootrec_comp_ver { - char fw_version[24]; -} __packed; - -struct bootrec_end { - __le16 crc; - u8 padding[2]; - u8 md5[16]; -} __packed; - -/* provide 16 bytes for the transport back-end */ -#define P54_TX_INFO_DATA_SIZE 16 - -/* stored in ieee80211_tx_info's rate_driver_data */ -struct p54_tx_info { - u32 start_addr; - u32 end_addr; - union { - void *data[P54_TX_INFO_DATA_SIZE / sizeof(void *)]; - struct { - u32 extra_len; - }; - }; -}; - -#define P54_MAX_CTRL_FRAME_LEN 0x1000 - -#define P54_SET_QUEUE(queue, ai_fs, cw_min, cw_max, _txop) \ -do { \ - queue.aifs = cpu_to_le16(ai_fs); \ - queue.cwmin = cpu_to_le16(cw_min); \ - queue.cwmax = cpu_to_le16(cw_max); \ - queue.txop = cpu_to_le16(_txop); \ -} while (0) - -struct p54_edcf_queue_param { - __le16 aifs; - __le16 cwmin; - __le16 cwmax; - __le16 txop; -} __packed; - -struct p54_rssi_db_entry { - u16 freq; - s16 mul; - s16 add; - s16 longbow_unkn; - s16 longbow_unk2; -}; - -struct p54_cal_database { - size_t entries; - size_t entry_size; - size_t offset; - size_t len; - u8 data[0]; -}; - -#define EEPROM_READBACK_LEN 0x3fc - -enum fw_state { - FW_STATE_OFF, - FW_STATE_BOOTING, - FW_STATE_READY, - FW_STATE_RESET, - FW_STATE_RESETTING, -}; - -#ifdef CONFIG_P54_LEDS - -#define P54_LED_MAX_NAME_LEN 31 - -struct p54_led_dev { - struct ieee80211_hw *hw_dev; - struct led_classdev led_dev; - char name[P54_LED_MAX_NAME_LEN + 1]; - - unsigned int toggled; - unsigned int index; - unsigned int registered; -}; - -#endif /* CONFIG_P54_LEDS */ - -struct p54_tx_queue_stats { - unsigned int len; - unsigned int limit; - unsigned int count; -}; - -struct p54_common { - struct ieee80211_hw *hw; - struct ieee80211_vif *vif; - void (*tx)(struct ieee80211_hw *dev, struct sk_buff *skb); - int (*open)(struct ieee80211_hw *dev); - void (*stop)(struct ieee80211_hw *dev); - struct sk_buff_head tx_pending; - struct sk_buff_head tx_queue; - struct mutex conf_mutex; - bool registered; - - /* memory management (as seen by the firmware) */ - u32 rx_start; - u32 rx_end; - u16 rx_mtu; - u8 headroom; - u8 tailroom; - - /* firmware/hardware info */ - unsigned int tx_hdr_len; - unsigned int fw_var; - unsigned int fw_interface; - u8 version; - - /* (e)DCF / QOS state */ - bool use_short_slot; - spinlock_t tx_stats_lock; - struct p54_tx_queue_stats tx_stats[8]; - struct p54_edcf_queue_param qos_params[8]; - - /* Radio data */ - u16 rxhw; - u8 rx_diversity_mask; - u8 tx_diversity_mask; - unsigned int output_power; - struct p54_rssi_db_entry *cur_rssi; - struct ieee80211_channel *curchan; - struct survey_info *survey; - unsigned int chan_num; - struct completion stat_comp; - bool update_stats; - struct { - unsigned int timestamp; - unsigned int cached_cca; - unsigned int cached_tx; - unsigned int cached_rssi; - u64 active; - u64 cca; - u64 tx; - u64 rssi; - } survey_raw; - - int noise; - /* calibration, output power limit and rssi<->dBm conversation data */ - struct pda_iq_autocal_entry *iq_autocal; - unsigned int iq_autocal_len; - struct p54_cal_database *curve_data; - struct p54_cal_database *output_limit; - struct p54_cal_database *rssi_db; - struct ieee80211_supported_band *band_table[IEEE80211_NUM_BANDS]; - - /* BBP/MAC state */ - u8 mac_addr[ETH_ALEN]; - u8 bssid[ETH_ALEN]; - u8 mc_maclist[4][ETH_ALEN]; - u16 wakeup_timer; - unsigned int filter_flags; - int mc_maclist_num; - int mode; - u32 tsf_low32, tsf_high32; - u32 basic_rate_mask; - u16 aid; - u8 coverage_class; - bool phy_idle; - bool phy_ps; - bool powersave_override; - __le32 beacon_req_id; - struct completion beacon_comp; - - /* cryptographic engine information */ - u8 privacy_caps; - u8 rx_keycache_size; - unsigned long *used_rxkeys; - - /* LED management */ -#ifdef CONFIG_P54_LEDS - struct p54_led_dev leds[4]; - struct delayed_work led_work; -#endif /* CONFIG_P54_LEDS */ - u16 softled_state; /* bit field of glowing LEDs */ - - /* statistics */ - struct ieee80211_low_level_stats stats; - struct delayed_work work; - - /* eeprom handling */ - void *eeprom; - struct completion eeprom_comp; - struct mutex eeprom_mutex; -}; - -/* interfaces for the drivers */ -int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb); -void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb); -int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw); -int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len); -int p54_read_eeprom(struct ieee80211_hw *dev); - -struct ieee80211_hw *p54_init_common(size_t priv_data_len); -int p54_register_common(struct ieee80211_hw *dev, struct device *pdev); -void p54_free_common(struct ieee80211_hw *dev); - -void p54_unregister_common(struct ieee80211_hw *dev); - -#endif /* P54_H */ diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c deleted file mode 100644 index 27a49068d32d..000000000000 --- a/drivers/net/wireless/p54/p54pci.c +++ /dev/null @@ -1,703 +0,0 @@ - -/* - * Linux device driver for PCI based Prism54 - * - * Copyright (c) 2006, Michael Wu - * Copyright (c) 2008, Christian Lamparter - * - * Based on the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "p54.h" -#include "lmac.h" -#include "p54pci.h" - -MODULE_AUTHOR("Michael Wu "); -MODULE_DESCRIPTION("Prism54 PCI wireless driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("prism54pci"); -MODULE_FIRMWARE("isl3886pci"); - -static const struct pci_device_id p54p_table[] = { - /* Intersil PRISM Duette/Prism GT Wireless LAN adapter */ - { PCI_DEVICE(0x1260, 0x3890) }, - /* 3COM 3CRWE154G72 Wireless LAN adapter */ - { PCI_DEVICE(0x10b7, 0x6001) }, - /* Intersil PRISM Indigo Wireless LAN adapter */ - { PCI_DEVICE(0x1260, 0x3877) }, - /* Intersil PRISM Javelin/Xbow Wireless LAN adapter */ - { PCI_DEVICE(0x1260, 0x3886) }, - /* Intersil PRISM Xbow Wireless LAN adapter (Symbol AP-300) */ - { PCI_DEVICE(0x1260, 0xffff) }, - { }, -}; - -MODULE_DEVICE_TABLE(pci, p54p_table); - -static int p54p_upload_firmware(struct ieee80211_hw *dev) -{ - struct p54p_priv *priv = dev->priv; - __le32 reg; - int err; - __le32 *data; - u32 remains, left, device_addr; - - P54P_WRITE(int_enable, cpu_to_le32(0)); - P54P_READ(int_enable); - udelay(10); - - reg = P54P_READ(ctrl_stat); - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); - P54P_WRITE(ctrl_stat, reg); - P54P_READ(ctrl_stat); - udelay(10); - - reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); - P54P_WRITE(ctrl_stat, reg); - wmb(); - udelay(10); - - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); - P54P_WRITE(ctrl_stat, reg); - wmb(); - - /* wait for the firmware to reset properly */ - mdelay(10); - - err = p54_parse_firmware(dev, priv->firmware); - if (err) - return err; - - if (priv->common.fw_interface != FW_LM86) { - dev_err(&priv->pdev->dev, "wrong firmware, " - "please get a LM86(PCI) firmware a try again.\n"); - return -EINVAL; - } - - data = (__le32 *) priv->firmware->data; - remains = priv->firmware->size; - device_addr = ISL38XX_DEV_FIRMWARE_ADDR; - while (remains) { - u32 i = 0; - left = min((u32)0x1000, remains); - P54P_WRITE(direct_mem_base, cpu_to_le32(device_addr)); - P54P_READ(int_enable); - - device_addr += 0x1000; - while (i < left) { - P54P_WRITE(direct_mem_win[i], *data++); - i += sizeof(u32); - } - - remains -= left; - P54P_READ(int_enable); - } - - reg = P54P_READ(ctrl_stat); - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); - reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); - P54P_WRITE(ctrl_stat, reg); - P54P_READ(ctrl_stat); - udelay(10); - - reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); - P54P_WRITE(ctrl_stat, reg); - wmb(); - udelay(10); - - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); - P54P_WRITE(ctrl_stat, reg); - wmb(); - udelay(10); - - /* wait for the firmware to boot properly */ - mdelay(100); - - return 0; -} - -static void p54p_refill_rx_ring(struct ieee80211_hw *dev, - int ring_index, struct p54p_desc *ring, u32 ring_limit, - struct sk_buff **rx_buf, u32 index) -{ - struct p54p_priv *priv = dev->priv; - struct p54p_ring_control *ring_control = priv->ring_control; - u32 limit, idx, i; - - idx = le32_to_cpu(ring_control->host_idx[ring_index]); - limit = idx; - limit -= index; - limit = ring_limit - limit; - - i = idx % ring_limit; - while (limit-- > 1) { - struct p54p_desc *desc = &ring[i]; - - if (!desc->host_addr) { - struct sk_buff *skb; - dma_addr_t mapping; - skb = dev_alloc_skb(priv->common.rx_mtu + 32); - if (!skb) - break; - - mapping = pci_map_single(priv->pdev, - skb_tail_pointer(skb), - priv->common.rx_mtu + 32, - PCI_DMA_FROMDEVICE); - - if (pci_dma_mapping_error(priv->pdev, mapping)) { - dev_kfree_skb_any(skb); - dev_err(&priv->pdev->dev, - "RX DMA Mapping error\n"); - break; - } - - desc->host_addr = cpu_to_le32(mapping); - desc->device_addr = 0; // FIXME: necessary? - desc->len = cpu_to_le16(priv->common.rx_mtu + 32); - desc->flags = 0; - rx_buf[i] = skb; - } - - i++; - idx++; - i %= ring_limit; - } - - wmb(); - ring_control->host_idx[ring_index] = cpu_to_le32(idx); -} - -static void p54p_check_rx_ring(struct ieee80211_hw *dev, u32 *index, - int ring_index, struct p54p_desc *ring, u32 ring_limit, - struct sk_buff **rx_buf) -{ - struct p54p_priv *priv = dev->priv; - struct p54p_ring_control *ring_control = priv->ring_control; - struct p54p_desc *desc; - u32 idx, i; - - i = (*index) % ring_limit; - (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]); - idx %= ring_limit; - while (i != idx) { - u16 len; - struct sk_buff *skb; - dma_addr_t dma_addr; - desc = &ring[i]; - len = le16_to_cpu(desc->len); - skb = rx_buf[i]; - - if (!skb) { - i++; - i %= ring_limit; - continue; - } - - if (unlikely(len > priv->common.rx_mtu)) { - if (net_ratelimit()) - dev_err(&priv->pdev->dev, "rx'd frame size " - "exceeds length threshold.\n"); - - len = priv->common.rx_mtu; - } - dma_addr = le32_to_cpu(desc->host_addr); - pci_dma_sync_single_for_cpu(priv->pdev, dma_addr, - priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); - skb_put(skb, len); - - if (p54_rx(dev, skb)) { - pci_unmap_single(priv->pdev, dma_addr, - priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); - rx_buf[i] = NULL; - desc->host_addr = cpu_to_le32(0); - } else { - skb_trim(skb, 0); - pci_dma_sync_single_for_device(priv->pdev, dma_addr, - priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); - desc->len = cpu_to_le16(priv->common.rx_mtu + 32); - } - - i++; - i %= ring_limit; - } - - p54p_refill_rx_ring(dev, ring_index, ring, ring_limit, rx_buf, *index); -} - -static void p54p_check_tx_ring(struct ieee80211_hw *dev, u32 *index, - int ring_index, struct p54p_desc *ring, u32 ring_limit, - struct sk_buff **tx_buf) -{ - struct p54p_priv *priv = dev->priv; - struct p54p_ring_control *ring_control = priv->ring_control; - struct p54p_desc *desc; - struct sk_buff *skb; - u32 idx, i; - - i = (*index) % ring_limit; - (*index) = idx = le32_to_cpu(ring_control->device_idx[ring_index]); - idx %= ring_limit; - - while (i != idx) { - desc = &ring[i]; - - skb = tx_buf[i]; - tx_buf[i] = NULL; - - pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr), - le16_to_cpu(desc->len), PCI_DMA_TODEVICE); - - desc->host_addr = 0; - desc->device_addr = 0; - desc->len = 0; - desc->flags = 0; - - if (skb && FREE_AFTER_TX(skb)) - p54_free_skb(dev, skb); - - i++; - i %= ring_limit; - } -} - -static void p54p_tasklet(unsigned long dev_id) -{ - struct ieee80211_hw *dev = (struct ieee80211_hw *)dev_id; - struct p54p_priv *priv = dev->priv; - struct p54p_ring_control *ring_control = priv->ring_control; - - p54p_check_tx_ring(dev, &priv->tx_idx_mgmt, 3, ring_control->tx_mgmt, - ARRAY_SIZE(ring_control->tx_mgmt), - priv->tx_buf_mgmt); - - p54p_check_tx_ring(dev, &priv->tx_idx_data, 1, ring_control->tx_data, - ARRAY_SIZE(ring_control->tx_data), - priv->tx_buf_data); - - p54p_check_rx_ring(dev, &priv->rx_idx_mgmt, 2, ring_control->rx_mgmt, - ARRAY_SIZE(ring_control->rx_mgmt), priv->rx_buf_mgmt); - - p54p_check_rx_ring(dev, &priv->rx_idx_data, 0, ring_control->rx_data, - ARRAY_SIZE(ring_control->rx_data), priv->rx_buf_data); - - wmb(); - P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); -} - -static irqreturn_t p54p_interrupt(int irq, void *dev_id) -{ - struct ieee80211_hw *dev = dev_id; - struct p54p_priv *priv = dev->priv; - __le32 reg; - - reg = P54P_READ(int_ident); - if (unlikely(reg == cpu_to_le32(0xFFFFFFFF))) { - goto out; - } - P54P_WRITE(int_ack, reg); - - reg &= P54P_READ(int_enable); - - if (reg & cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)) - tasklet_schedule(&priv->tasklet); - else if (reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)) - complete(&priv->boot_comp); - -out: - return reg ? IRQ_HANDLED : IRQ_NONE; -} - -static void p54p_tx(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - unsigned long flags; - struct p54p_priv *priv = dev->priv; - struct p54p_ring_control *ring_control = priv->ring_control; - struct p54p_desc *desc; - dma_addr_t mapping; - u32 idx, i; - - spin_lock_irqsave(&priv->lock, flags); - idx = le32_to_cpu(ring_control->host_idx[1]); - i = idx % ARRAY_SIZE(ring_control->tx_data); - - mapping = pci_map_single(priv->pdev, skb->data, skb->len, - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(priv->pdev, mapping)) { - spin_unlock_irqrestore(&priv->lock, flags); - p54_free_skb(dev, skb); - dev_err(&priv->pdev->dev, "TX DMA mapping error\n"); - return ; - } - priv->tx_buf_data[i] = skb; - - desc = &ring_control->tx_data[i]; - desc->host_addr = cpu_to_le32(mapping); - desc->device_addr = ((struct p54_hdr *)skb->data)->req_id; - desc->len = cpu_to_le16(skb->len); - desc->flags = 0; - - wmb(); - ring_control->host_idx[1] = cpu_to_le32(idx + 1); - spin_unlock_irqrestore(&priv->lock, flags); - - P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); - P54P_READ(dev_int); -} - -static void p54p_stop(struct ieee80211_hw *dev) -{ - struct p54p_priv *priv = dev->priv; - struct p54p_ring_control *ring_control = priv->ring_control; - unsigned int i; - struct p54p_desc *desc; - - P54P_WRITE(int_enable, cpu_to_le32(0)); - P54P_READ(int_enable); - udelay(10); - - free_irq(priv->pdev->irq, dev); - - tasklet_kill(&priv->tasklet); - - P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); - - for (i = 0; i < ARRAY_SIZE(priv->rx_buf_data); i++) { - desc = &ring_control->rx_data[i]; - if (desc->host_addr) - pci_unmap_single(priv->pdev, - le32_to_cpu(desc->host_addr), - priv->common.rx_mtu + 32, - PCI_DMA_FROMDEVICE); - kfree_skb(priv->rx_buf_data[i]); - priv->rx_buf_data[i] = NULL; - } - - for (i = 0; i < ARRAY_SIZE(priv->rx_buf_mgmt); i++) { - desc = &ring_control->rx_mgmt[i]; - if (desc->host_addr) - pci_unmap_single(priv->pdev, - le32_to_cpu(desc->host_addr), - priv->common.rx_mtu + 32, - PCI_DMA_FROMDEVICE); - kfree_skb(priv->rx_buf_mgmt[i]); - priv->rx_buf_mgmt[i] = NULL; - } - - for (i = 0; i < ARRAY_SIZE(priv->tx_buf_data); i++) { - desc = &ring_control->tx_data[i]; - if (desc->host_addr) - pci_unmap_single(priv->pdev, - le32_to_cpu(desc->host_addr), - le16_to_cpu(desc->len), - PCI_DMA_TODEVICE); - - p54_free_skb(dev, priv->tx_buf_data[i]); - priv->tx_buf_data[i] = NULL; - } - - for (i = 0; i < ARRAY_SIZE(priv->tx_buf_mgmt); i++) { - desc = &ring_control->tx_mgmt[i]; - if (desc->host_addr) - pci_unmap_single(priv->pdev, - le32_to_cpu(desc->host_addr), - le16_to_cpu(desc->len), - PCI_DMA_TODEVICE); - - p54_free_skb(dev, priv->tx_buf_mgmt[i]); - priv->tx_buf_mgmt[i] = NULL; - } - - memset(ring_control, 0, sizeof(*ring_control)); -} - -static int p54p_open(struct ieee80211_hw *dev) -{ - struct p54p_priv *priv = dev->priv; - int err; - long timeout; - - init_completion(&priv->boot_comp); - err = request_irq(priv->pdev->irq, p54p_interrupt, - IRQF_SHARED, "p54pci", dev); - if (err) { - dev_err(&priv->pdev->dev, "failed to register IRQ handler\n"); - return err; - } - - memset(priv->ring_control, 0, sizeof(*priv->ring_control)); - err = p54p_upload_firmware(dev); - if (err) { - free_irq(priv->pdev->irq, dev); - return err; - } - priv->rx_idx_data = priv->tx_idx_data = 0; - priv->rx_idx_mgmt = priv->tx_idx_mgmt = 0; - - p54p_refill_rx_ring(dev, 0, priv->ring_control->rx_data, - ARRAY_SIZE(priv->ring_control->rx_data), priv->rx_buf_data, 0); - - p54p_refill_rx_ring(dev, 2, priv->ring_control->rx_mgmt, - ARRAY_SIZE(priv->ring_control->rx_mgmt), priv->rx_buf_mgmt, 0); - - P54P_WRITE(ring_control_base, cpu_to_le32(priv->ring_control_dma)); - P54P_READ(ring_control_base); - wmb(); - udelay(10); - - P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_INIT)); - P54P_READ(int_enable); - wmb(); - udelay(10); - - P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); - P54P_READ(dev_int); - - timeout = wait_for_completion_interruptible_timeout( - &priv->boot_comp, HZ); - if (timeout <= 0) { - wiphy_err(dev->wiphy, "Cannot boot firmware!\n"); - p54p_stop(dev); - return timeout ? -ERESTARTSYS : -ETIMEDOUT; - } - - P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)); - P54P_READ(int_enable); - wmb(); - udelay(10); - - P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); - P54P_READ(dev_int); - wmb(); - udelay(10); - - return 0; -} - -static void p54p_firmware_step2(const struct firmware *fw, - void *context) -{ - struct p54p_priv *priv = context; - struct ieee80211_hw *dev = priv->common.hw; - struct pci_dev *pdev = priv->pdev; - int err; - - if (!fw) { - dev_err(&pdev->dev, "Cannot find firmware (isl3886pci)\n"); - err = -ENOENT; - goto out; - } - - priv->firmware = fw; - - err = p54p_open(dev); - if (err) - goto out; - err = p54_read_eeprom(dev); - p54p_stop(dev); - if (err) - goto out; - - err = p54_register_common(dev, &pdev->dev); - if (err) - goto out; - -out: - - complete(&priv->fw_loaded); - - if (err) { - struct device *parent = pdev->dev.parent; - - if (parent) - device_lock(parent); - - /* - * This will indirectly result in a call to p54p_remove. - * Hence, we don't need to bother with freeing any - * allocated ressources at all. - */ - device_release_driver(&pdev->dev); - - if (parent) - device_unlock(parent); - } - - pci_dev_put(pdev); -} - -static int p54p_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct p54p_priv *priv; - struct ieee80211_hw *dev; - unsigned long mem_addr, mem_len; - int err; - - pci_dev_get(pdev); - err = pci_enable_device(pdev); - if (err) { - dev_err(&pdev->dev, "Cannot enable new PCI device\n"); - return err; - } - - mem_addr = pci_resource_start(pdev, 0); - mem_len = pci_resource_len(pdev, 0); - if (mem_len < sizeof(struct p54p_csr)) { - dev_err(&pdev->dev, "Too short PCI resources\n"); - err = -ENODEV; - goto err_disable_dev; - } - - err = pci_request_regions(pdev, "p54pci"); - if (err) { - dev_err(&pdev->dev, "Cannot obtain PCI resources\n"); - goto err_disable_dev; - } - - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (!err) - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, "No suitable DMA available\n"); - goto err_free_reg; - } - - pci_set_master(pdev); - pci_try_set_mwi(pdev); - - pci_write_config_byte(pdev, 0x40, 0); - pci_write_config_byte(pdev, 0x41, 0); - - dev = p54_init_common(sizeof(*priv)); - if (!dev) { - dev_err(&pdev->dev, "ieee80211 alloc failed\n"); - err = -ENOMEM; - goto err_free_reg; - } - - priv = dev->priv; - priv->pdev = pdev; - - init_completion(&priv->fw_loaded); - SET_IEEE80211_DEV(dev, &pdev->dev); - pci_set_drvdata(pdev, dev); - - priv->map = ioremap(mem_addr, mem_len); - if (!priv->map) { - dev_err(&pdev->dev, "Cannot map device memory\n"); - err = -ENOMEM; - goto err_free_dev; - } - - priv->ring_control = pci_alloc_consistent(pdev, sizeof(*priv->ring_control), - &priv->ring_control_dma); - if (!priv->ring_control) { - dev_err(&pdev->dev, "Cannot allocate rings\n"); - err = -ENOMEM; - goto err_iounmap; - } - priv->common.open = p54p_open; - priv->common.stop = p54p_stop; - priv->common.tx = p54p_tx; - - spin_lock_init(&priv->lock); - tasklet_init(&priv->tasklet, p54p_tasklet, (unsigned long)dev); - - err = request_firmware_nowait(THIS_MODULE, 1, "isl3886pci", - &priv->pdev->dev, GFP_KERNEL, - priv, p54p_firmware_step2); - if (!err) - return 0; - - pci_free_consistent(pdev, sizeof(*priv->ring_control), - priv->ring_control, priv->ring_control_dma); - - err_iounmap: - iounmap(priv->map); - - err_free_dev: - p54_free_common(dev); - - err_free_reg: - pci_release_regions(pdev); - err_disable_dev: - pci_disable_device(pdev); - pci_dev_put(pdev); - return err; -} - -static void p54p_remove(struct pci_dev *pdev) -{ - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - struct p54p_priv *priv; - - if (!dev) - return; - - priv = dev->priv; - wait_for_completion(&priv->fw_loaded); - p54_unregister_common(dev); - release_firmware(priv->firmware); - pci_free_consistent(pdev, sizeof(*priv->ring_control), - priv->ring_control, priv->ring_control_dma); - iounmap(priv->map); - pci_release_regions(pdev); - pci_disable_device(pdev); - p54_free_common(dev); -} - -#ifdef CONFIG_PM_SLEEP -static int p54p_suspend(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - - pci_save_state(pdev); - pci_set_power_state(pdev, PCI_D3hot); - pci_disable_device(pdev); - return 0; -} - -static int p54p_resume(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - int err; - - err = pci_reenable_device(pdev); - if (err) - return err; - return pci_set_power_state(pdev, PCI_D0); -} - -static SIMPLE_DEV_PM_OPS(p54pci_pm_ops, p54p_suspend, p54p_resume); - -#define P54P_PM_OPS (&p54pci_pm_ops) -#else -#define P54P_PM_OPS (NULL) -#endif /* CONFIG_PM_SLEEP */ - -static struct pci_driver p54p_driver = { - .name = "p54pci", - .id_table = p54p_table, - .probe = p54p_probe, - .remove = p54p_remove, - .driver.pm = P54P_PM_OPS, -}; - -module_pci_driver(p54p_driver); diff --git a/drivers/net/wireless/p54/p54pci.h b/drivers/net/wireless/p54/p54pci.h deleted file mode 100644 index 68405c142f97..000000000000 --- a/drivers/net/wireless/p54/p54pci.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef P54PCI_H -#define P54PCI_H -#include - -/* - * Defines for PCI based mac80211 Prism54 driver - * - * Copyright (c) 2006, Michael Wu - * - * Based on the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* Device Interrupt register bits */ -#define ISL38XX_DEV_INT_RESET 0x0001 -#define ISL38XX_DEV_INT_UPDATE 0x0002 -#define ISL38XX_DEV_INT_WAKEUP 0x0008 -#define ISL38XX_DEV_INT_SLEEP 0x0010 -#define ISL38XX_DEV_INT_ABORT 0x0020 -/* these two only used in USB */ -#define ISL38XX_DEV_INT_DATA 0x0040 -#define ISL38XX_DEV_INT_MGMT 0x0080 - -#define ISL38XX_DEV_INT_PCIUART_CTS 0x4000 -#define ISL38XX_DEV_INT_PCIUART_DR 0x8000 - -/* Interrupt Identification/Acknowledge/Enable register bits */ -#define ISL38XX_INT_IDENT_UPDATE 0x0002 -#define ISL38XX_INT_IDENT_INIT 0x0004 -#define ISL38XX_INT_IDENT_WAKEUP 0x0008 -#define ISL38XX_INT_IDENT_SLEEP 0x0010 -#define ISL38XX_INT_IDENT_PCIUART_CTS 0x4000 -#define ISL38XX_INT_IDENT_PCIUART_DR 0x8000 - -/* Control/Status register bits */ -#define ISL38XX_CTRL_STAT_SLEEPMODE 0x00000200 -#define ISL38XX_CTRL_STAT_CLKRUN 0x00800000 -#define ISL38XX_CTRL_STAT_RESET 0x10000000 -#define ISL38XX_CTRL_STAT_RAMBOOT 0x20000000 -#define ISL38XX_CTRL_STAT_STARTHALTED 0x40000000 -#define ISL38XX_CTRL_STAT_HOST_OVERRIDE 0x80000000 - -struct p54p_csr { - __le32 dev_int; - u8 unused_1[12]; - __le32 int_ident; - __le32 int_ack; - __le32 int_enable; - u8 unused_2[4]; - union { - __le32 ring_control_base; - __le32 gen_purp_com[2]; - }; - u8 unused_3[8]; - __le32 direct_mem_base; - u8 unused_4[44]; - __le32 dma_addr; - __le32 dma_len; - __le32 dma_ctrl; - u8 unused_5[12]; - __le32 ctrl_stat; - u8 unused_6[1924]; - u8 cardbus_cis[0x800]; - u8 direct_mem_win[0x1000]; -} __packed; - -/* usb backend only needs the register defines above */ -#ifndef P54USB_H -struct p54p_desc { - __le32 host_addr; - __le32 device_addr; - __le16 len; - __le16 flags; -} __packed; - -struct p54p_ring_control { - __le32 host_idx[4]; - __le32 device_idx[4]; - struct p54p_desc rx_data[8]; - struct p54p_desc tx_data[32]; - struct p54p_desc rx_mgmt[4]; - struct p54p_desc tx_mgmt[4]; -} __packed; - -#define P54P_READ(r) (__force __le32)__raw_readl(&priv->map->r) -#define P54P_WRITE(r, val) __raw_writel((__force u32)(__le32)(val), &priv->map->r) - -struct p54p_priv { - struct p54_common common; - struct pci_dev *pdev; - struct p54p_csr __iomem *map; - struct tasklet_struct tasklet; - const struct firmware *firmware; - spinlock_t lock; - struct p54p_ring_control *ring_control; - dma_addr_t ring_control_dma; - u32 rx_idx_data, tx_idx_data; - u32 rx_idx_mgmt, tx_idx_mgmt; - struct sk_buff *rx_buf_data[8]; - struct sk_buff *rx_buf_mgmt[4]; - struct sk_buff *tx_buf_data[32]; - struct sk_buff *tx_buf_mgmt[4]; - struct completion boot_comp; - struct completion fw_loaded; -}; - -#endif /* P54USB_H */ -#endif /* P54PCI_H */ diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c deleted file mode 100644 index 7ab2f43ab425..000000000000 --- a/drivers/net/wireless/p54/p54spi.c +++ /dev/null @@ -1,720 +0,0 @@ -/* - * Copyright (C) 2008 Christian Lamparter - * Copyright 2008 Johannes Berg - * - * This driver is a port from stlc45xx: - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "p54spi.h" -#include "p54.h" - -#include "lmac.h" - -#ifdef CONFIG_P54_SPI_DEFAULT_EEPROM -#include "p54spi_eeprom.h" -#endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */ - -MODULE_FIRMWARE("3826.arm"); - -/* gpios should be handled in board files and provided via platform data, - * but because it's currently impossible for p54spi to have a header file - * in include/linux, let's use module paramaters for now - */ - -static int p54spi_gpio_power = 97; -module_param(p54spi_gpio_power, int, 0444); -MODULE_PARM_DESC(p54spi_gpio_power, "gpio number for power line"); - -static int p54spi_gpio_irq = 87; -module_param(p54spi_gpio_irq, int, 0444); -MODULE_PARM_DESC(p54spi_gpio_irq, "gpio number for irq line"); - -static void p54spi_spi_read(struct p54s_priv *priv, u8 address, - void *buf, size_t len) -{ - struct spi_transfer t[2]; - struct spi_message m; - __le16 addr; - - /* We first push the address */ - addr = cpu_to_le16(address << 8 | SPI_ADRS_READ_BIT_15); - - spi_message_init(&m); - memset(t, 0, sizeof(t)); - - t[0].tx_buf = &addr; - t[0].len = sizeof(addr); - spi_message_add_tail(&t[0], &m); - - t[1].rx_buf = buf; - t[1].len = len; - spi_message_add_tail(&t[1], &m); - - spi_sync(priv->spi, &m); -} - - -static void p54spi_spi_write(struct p54s_priv *priv, u8 address, - const void *buf, size_t len) -{ - struct spi_transfer t[3]; - struct spi_message m; - __le16 addr; - - /* We first push the address */ - addr = cpu_to_le16(address << 8); - - spi_message_init(&m); - memset(t, 0, sizeof(t)); - - t[0].tx_buf = &addr; - t[0].len = sizeof(addr); - spi_message_add_tail(&t[0], &m); - - t[1].tx_buf = buf; - t[1].len = len & ~1; - spi_message_add_tail(&t[1], &m); - - if (len % 2) { - __le16 last_word; - last_word = cpu_to_le16(((u8 *)buf)[len - 1]); - - t[2].tx_buf = &last_word; - t[2].len = sizeof(last_word); - spi_message_add_tail(&t[2], &m); - } - - spi_sync(priv->spi, &m); -} - -static u32 p54spi_read32(struct p54s_priv *priv, u8 addr) -{ - __le32 val; - - p54spi_spi_read(priv, addr, &val, sizeof(val)); - - return le32_to_cpu(val); -} - -static inline void p54spi_write16(struct p54s_priv *priv, u8 addr, __le16 val) -{ - p54spi_spi_write(priv, addr, &val, sizeof(val)); -} - -static inline void p54spi_write32(struct p54s_priv *priv, u8 addr, __le32 val) -{ - p54spi_spi_write(priv, addr, &val, sizeof(val)); -} - -static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, u32 bits) -{ - int i; - - for (i = 0; i < 2000; i++) { - u32 buffer = p54spi_read32(priv, reg); - if ((buffer & bits) == bits) - return 1; - } - return 0; -} - -static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base, - const void *buf, size_t len) -{ - if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, HOST_ALLOWED)) { - dev_err(&priv->spi->dev, "spi_write_dma not allowed " - "to DMA write.\n"); - return -EAGAIN; - } - - p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL, - cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE)); - - p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(len)); - p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, base); - p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, buf, len); - return 0; -} - -static int p54spi_request_firmware(struct ieee80211_hw *dev) -{ - struct p54s_priv *priv = dev->priv; - int ret; - - /* FIXME: should driver use it's own struct device? */ - ret = request_firmware(&priv->firmware, "3826.arm", &priv->spi->dev); - - if (ret < 0) { - dev_err(&priv->spi->dev, "request_firmware() failed: %d", ret); - return ret; - } - - ret = p54_parse_firmware(dev, priv->firmware); - if (ret) { - release_firmware(priv->firmware); - return ret; - } - - return 0; -} - -static int p54spi_request_eeprom(struct ieee80211_hw *dev) -{ - struct p54s_priv *priv = dev->priv; - const struct firmware *eeprom; - int ret; - - /* allow users to customize their eeprom. - */ - - ret = request_firmware_direct(&eeprom, "3826.eeprom", &priv->spi->dev); - if (ret < 0) { -#ifdef CONFIG_P54_SPI_DEFAULT_EEPROM - dev_info(&priv->spi->dev, "loading default eeprom...\n"); - ret = p54_parse_eeprom(dev, (void *) p54spi_eeprom, - sizeof(p54spi_eeprom)); -#else - dev_err(&priv->spi->dev, "Failed to request user eeprom\n"); -#endif /* CONFIG_P54_SPI_DEFAULT_EEPROM */ - } else { - dev_info(&priv->spi->dev, "loading user eeprom...\n"); - ret = p54_parse_eeprom(dev, (void *) eeprom->data, - (int)eeprom->size); - release_firmware(eeprom); - } - return ret; -} - -static int p54spi_upload_firmware(struct ieee80211_hw *dev) -{ - struct p54s_priv *priv = dev->priv; - unsigned long fw_len, _fw_len; - unsigned int offset = 0; - int err = 0; - u8 *fw; - - fw_len = priv->firmware->size; - fw = kmemdup(priv->firmware->data, fw_len, GFP_KERNEL); - if (!fw) - return -ENOMEM; - - /* stop the device */ - p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( - SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET | - SPI_CTRL_STAT_START_HALTED)); - - msleep(TARGET_BOOT_SLEEP); - - p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( - SPI_CTRL_STAT_HOST_OVERRIDE | - SPI_CTRL_STAT_START_HALTED)); - - msleep(TARGET_BOOT_SLEEP); - - while (fw_len > 0) { - _fw_len = min_t(long, fw_len, SPI_MAX_PACKET_SIZE); - - err = p54spi_spi_write_dma(priv, cpu_to_le32( - ISL38XX_DEV_FIRMWARE_ADDR + offset), - (fw + offset), _fw_len); - if (err < 0) - goto out; - - fw_len -= _fw_len; - offset += _fw_len; - } - - BUG_ON(fw_len != 0); - - /* enable host interrupts */ - p54spi_write32(priv, SPI_ADRS_HOST_INT_EN, - cpu_to_le32(SPI_HOST_INTS_DEFAULT)); - - /* boot the device */ - p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( - SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET | - SPI_CTRL_STAT_RAM_BOOT)); - - msleep(TARGET_BOOT_SLEEP); - - p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( - SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_RAM_BOOT)); - msleep(TARGET_BOOT_SLEEP); - -out: - kfree(fw); - return err; -} - -static void p54spi_power_off(struct p54s_priv *priv) -{ - disable_irq(gpio_to_irq(p54spi_gpio_irq)); - gpio_set_value(p54spi_gpio_power, 0); -} - -static void p54spi_power_on(struct p54s_priv *priv) -{ - gpio_set_value(p54spi_gpio_power, 1); - enable_irq(gpio_to_irq(p54spi_gpio_irq)); - - /* need to wait a while before device can be accessed, the length - * is just a guess - */ - msleep(10); -} - -static inline void p54spi_int_ack(struct p54s_priv *priv, u32 val) -{ - p54spi_write32(priv, SPI_ADRS_HOST_INT_ACK, cpu_to_le32(val)); -} - -static int p54spi_wakeup(struct p54s_priv *priv) -{ - /* wake the chip */ - p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS, - cpu_to_le32(SPI_TARGET_INT_WAKEUP)); - - /* And wait for the READY interrupt */ - if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, - SPI_HOST_INT_READY)) { - dev_err(&priv->spi->dev, "INT_READY timeout\n"); - return -EBUSY; - } - - p54spi_int_ack(priv, SPI_HOST_INT_READY); - return 0; -} - -static inline void p54spi_sleep(struct p54s_priv *priv) -{ - p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS, - cpu_to_le32(SPI_TARGET_INT_SLEEP)); -} - -static void p54spi_int_ready(struct p54s_priv *priv) -{ - p54spi_write32(priv, SPI_ADRS_HOST_INT_EN, cpu_to_le32( - SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE)); - - switch (priv->fw_state) { - case FW_STATE_BOOTING: - priv->fw_state = FW_STATE_READY; - complete(&priv->fw_comp); - break; - case FW_STATE_RESETTING: - priv->fw_state = FW_STATE_READY; - /* TODO: reinitialize state */ - break; - default: - break; - } -} - -static int p54spi_rx(struct p54s_priv *priv) -{ - struct sk_buff *skb; - u16 len; - u16 rx_head[2]; -#define READAHEAD_SZ (sizeof(rx_head)-sizeof(u16)) - - if (p54spi_wakeup(priv) < 0) - return -EBUSY; - - /* Read data size and first data word in one SPI transaction - * This is workaround for firmware/DMA bug, - * when first data word gets lost under high load. - */ - p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, rx_head, sizeof(rx_head)); - len = rx_head[0]; - - if (len == 0) { - p54spi_sleep(priv); - dev_err(&priv->spi->dev, "rx request of zero bytes\n"); - return 0; - } - - /* Firmware may insert up to 4 padding bytes after the lmac header, - * but it does not amend the size of SPI data transfer. - * Such packets has correct data size in header, thus referencing - * past the end of allocated skb. Reserve extra 4 bytes for this case - */ - skb = dev_alloc_skb(len + 4); - if (!skb) { - p54spi_sleep(priv); - dev_err(&priv->spi->dev, "could not alloc skb"); - return -ENOMEM; - } - - if (len <= READAHEAD_SZ) { - memcpy(skb_put(skb, len), rx_head + 1, len); - } else { - memcpy(skb_put(skb, READAHEAD_SZ), rx_head + 1, READAHEAD_SZ); - p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, - skb_put(skb, len - READAHEAD_SZ), - len - READAHEAD_SZ); - } - p54spi_sleep(priv); - /* Put additional bytes to compensate for the possible - * alignment-caused truncation - */ - skb_put(skb, 4); - - if (p54_rx(priv->hw, skb) == 0) - dev_kfree_skb(skb); - - return 0; -} - - -static irqreturn_t p54spi_interrupt(int irq, void *config) -{ - struct spi_device *spi = config; - struct p54s_priv *priv = spi_get_drvdata(spi); - - ieee80211_queue_work(priv->hw, &priv->work); - - return IRQ_HANDLED; -} - -static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb) -{ - struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - int ret = 0; - - if (p54spi_wakeup(priv) < 0) - return -EBUSY; - - ret = p54spi_spi_write_dma(priv, hdr->req_id, skb->data, skb->len); - if (ret < 0) - goto out; - - if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, - SPI_HOST_INT_WR_READY)) { - dev_err(&priv->spi->dev, "WR_READY timeout\n"); - ret = -EAGAIN; - goto out; - } - - p54spi_int_ack(priv, SPI_HOST_INT_WR_READY); - - if (FREE_AFTER_TX(skb)) - p54_free_skb(priv->hw, skb); -out: - p54spi_sleep(priv); - return ret; -} - -static int p54spi_wq_tx(struct p54s_priv *priv) -{ - struct p54s_tx_info *entry; - struct sk_buff *skb; - struct ieee80211_tx_info *info; - struct p54_tx_info *minfo; - struct p54s_tx_info *dinfo; - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&priv->tx_lock, flags); - - while (!list_empty(&priv->tx_pending)) { - entry = list_entry(priv->tx_pending.next, - struct p54s_tx_info, tx_list); - - list_del_init(&entry->tx_list); - - spin_unlock_irqrestore(&priv->tx_lock, flags); - - dinfo = container_of((void *) entry, struct p54s_tx_info, - tx_list); - minfo = container_of((void *) dinfo, struct p54_tx_info, - data); - info = container_of((void *) minfo, struct ieee80211_tx_info, - rate_driver_data); - skb = container_of((void *) info, struct sk_buff, cb); - - ret = p54spi_tx_frame(priv, skb); - - if (ret < 0) { - p54_free_skb(priv->hw, skb); - return ret; - } - - spin_lock_irqsave(&priv->tx_lock, flags); - } - spin_unlock_irqrestore(&priv->tx_lock, flags); - return ret; -} - -static void p54spi_op_tx(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct p54s_priv *priv = dev->priv; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct p54_tx_info *mi = (struct p54_tx_info *) info->rate_driver_data; - struct p54s_tx_info *di = (struct p54s_tx_info *) mi->data; - unsigned long flags; - - BUILD_BUG_ON(sizeof(*di) > sizeof((mi->data))); - - spin_lock_irqsave(&priv->tx_lock, flags); - list_add_tail(&di->tx_list, &priv->tx_pending); - spin_unlock_irqrestore(&priv->tx_lock, flags); - - ieee80211_queue_work(priv->hw, &priv->work); -} - -static void p54spi_work(struct work_struct *work) -{ - struct p54s_priv *priv = container_of(work, struct p54s_priv, work); - u32 ints; - int ret; - - mutex_lock(&priv->mutex); - - if (priv->fw_state == FW_STATE_OFF) - goto out; - - ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS); - - if (ints & SPI_HOST_INT_READY) { - p54spi_int_ready(priv); - p54spi_int_ack(priv, SPI_HOST_INT_READY); - } - - if (priv->fw_state != FW_STATE_READY) - goto out; - - if (ints & SPI_HOST_INT_UPDATE) { - p54spi_int_ack(priv, SPI_HOST_INT_UPDATE); - ret = p54spi_rx(priv); - if (ret < 0) - goto out; - } - if (ints & SPI_HOST_INT_SW_UPDATE) { - p54spi_int_ack(priv, SPI_HOST_INT_SW_UPDATE); - ret = p54spi_rx(priv); - if (ret < 0) - goto out; - } - - ret = p54spi_wq_tx(priv); -out: - mutex_unlock(&priv->mutex); -} - -static int p54spi_op_start(struct ieee80211_hw *dev) -{ - struct p54s_priv *priv = dev->priv; - unsigned long timeout; - int ret = 0; - - if (mutex_lock_interruptible(&priv->mutex)) { - ret = -EINTR; - goto out; - } - - priv->fw_state = FW_STATE_BOOTING; - - p54spi_power_on(priv); - - ret = p54spi_upload_firmware(dev); - if (ret < 0) { - p54spi_power_off(priv); - goto out_unlock; - } - - mutex_unlock(&priv->mutex); - - timeout = msecs_to_jiffies(2000); - timeout = wait_for_completion_interruptible_timeout(&priv->fw_comp, - timeout); - if (!timeout) { - dev_err(&priv->spi->dev, "firmware boot failed"); - p54spi_power_off(priv); - ret = -1; - goto out; - } - - if (mutex_lock_interruptible(&priv->mutex)) { - ret = -EINTR; - p54spi_power_off(priv); - goto out; - } - - WARN_ON(priv->fw_state != FW_STATE_READY); - -out_unlock: - mutex_unlock(&priv->mutex); - -out: - return ret; -} - -static void p54spi_op_stop(struct ieee80211_hw *dev) -{ - struct p54s_priv *priv = dev->priv; - unsigned long flags; - - mutex_lock(&priv->mutex); - WARN_ON(priv->fw_state != FW_STATE_READY); - - p54spi_power_off(priv); - spin_lock_irqsave(&priv->tx_lock, flags); - INIT_LIST_HEAD(&priv->tx_pending); - spin_unlock_irqrestore(&priv->tx_lock, flags); - - priv->fw_state = FW_STATE_OFF; - mutex_unlock(&priv->mutex); - - cancel_work_sync(&priv->work); -} - -static int p54spi_probe(struct spi_device *spi) -{ - struct p54s_priv *priv = NULL; - struct ieee80211_hw *hw; - int ret = -EINVAL; - - hw = p54_init_common(sizeof(*priv)); - if (!hw) { - dev_err(&spi->dev, "could not alloc ieee80211_hw"); - return -ENOMEM; - } - - priv = hw->priv; - priv->hw = hw; - spi_set_drvdata(spi, priv); - priv->spi = spi; - - spi->bits_per_word = 16; - spi->max_speed_hz = 24000000; - - ret = spi_setup(spi); - if (ret < 0) { - dev_err(&priv->spi->dev, "spi_setup failed"); - goto err_free; - } - - ret = gpio_request(p54spi_gpio_power, "p54spi power"); - if (ret < 0) { - dev_err(&priv->spi->dev, "power GPIO request failed: %d", ret); - goto err_free; - } - - ret = gpio_request(p54spi_gpio_irq, "p54spi irq"); - if (ret < 0) { - dev_err(&priv->spi->dev, "irq GPIO request failed: %d", ret); - goto err_free_gpio_power; - } - - gpio_direction_output(p54spi_gpio_power, 0); - gpio_direction_input(p54spi_gpio_irq); - - ret = request_irq(gpio_to_irq(p54spi_gpio_irq), - p54spi_interrupt, 0, "p54spi", - priv->spi); - if (ret < 0) { - dev_err(&priv->spi->dev, "request_irq() failed"); - goto err_free_gpio_irq; - } - - irq_set_irq_type(gpio_to_irq(p54spi_gpio_irq), IRQ_TYPE_EDGE_RISING); - - disable_irq(gpio_to_irq(p54spi_gpio_irq)); - - INIT_WORK(&priv->work, p54spi_work); - init_completion(&priv->fw_comp); - INIT_LIST_HEAD(&priv->tx_pending); - mutex_init(&priv->mutex); - spin_lock_init(&priv->tx_lock); - SET_IEEE80211_DEV(hw, &spi->dev); - priv->common.open = p54spi_op_start; - priv->common.stop = p54spi_op_stop; - priv->common.tx = p54spi_op_tx; - - ret = p54spi_request_firmware(hw); - if (ret < 0) - goto err_free_common; - - ret = p54spi_request_eeprom(hw); - if (ret) - goto err_free_common; - - ret = p54_register_common(hw, &priv->spi->dev); - if (ret) - goto err_free_common; - - return 0; - -err_free_common: - free_irq(gpio_to_irq(p54spi_gpio_irq), spi); -err_free_gpio_irq: - gpio_free(p54spi_gpio_irq); -err_free_gpio_power: - gpio_free(p54spi_gpio_power); -err_free: - p54_free_common(priv->hw); - return ret; -} - -static int p54spi_remove(struct spi_device *spi) -{ - struct p54s_priv *priv = spi_get_drvdata(spi); - - p54_unregister_common(priv->hw); - - free_irq(gpio_to_irq(p54spi_gpio_irq), spi); - - gpio_free(p54spi_gpio_power); - gpio_free(p54spi_gpio_irq); - release_firmware(priv->firmware); - - mutex_destroy(&priv->mutex); - - p54_free_common(priv->hw); - - return 0; -} - - -static struct spi_driver p54spi_driver = { - .driver = { - .name = "p54spi", - }, - - .probe = p54spi_probe, - .remove = p54spi_remove, -}; - -module_spi_driver(p54spi_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Lamparter "); -MODULE_ALIAS("spi:cx3110x"); -MODULE_ALIAS("spi:p54spi"); -MODULE_ALIAS("spi:stlc45xx"); diff --git a/drivers/net/wireless/p54/p54spi.h b/drivers/net/wireless/p54/p54spi.h deleted file mode 100644 index dfaa62aaeb07..000000000000 --- a/drivers/net/wireless/p54/p54spi.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2008 Christian Lamparter - * - * This driver is a port from stlc45xx: - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef P54SPI_H -#define P54SPI_H - -#include -#include -#include - -#include "p54.h" - -/* Bit 15 is read/write bit; ON = READ, OFF = WRITE */ -#define SPI_ADRS_READ_BIT_15 0x8000 - -#define SPI_ADRS_ARM_INTERRUPTS 0x00 -#define SPI_ADRS_ARM_INT_EN 0x04 - -#define SPI_ADRS_HOST_INTERRUPTS 0x08 -#define SPI_ADRS_HOST_INT_EN 0x0c -#define SPI_ADRS_HOST_INT_ACK 0x10 - -#define SPI_ADRS_GEN_PURP_1 0x14 -#define SPI_ADRS_GEN_PURP_2 0x18 - -#define SPI_ADRS_DEV_CTRL_STAT 0x26 /* high word */ - -#define SPI_ADRS_DMA_DATA 0x28 - -#define SPI_ADRS_DMA_WRITE_CTRL 0x2c -#define SPI_ADRS_DMA_WRITE_LEN 0x2e -#define SPI_ADRS_DMA_WRITE_BASE 0x30 - -#define SPI_ADRS_DMA_READ_CTRL 0x34 -#define SPI_ADRS_DMA_READ_LEN 0x36 -#define SPI_ADRS_DMA_READ_BASE 0x38 - -#define SPI_CTRL_STAT_HOST_OVERRIDE 0x8000 -#define SPI_CTRL_STAT_START_HALTED 0x4000 -#define SPI_CTRL_STAT_RAM_BOOT 0x2000 -#define SPI_CTRL_STAT_HOST_RESET 0x1000 -#define SPI_CTRL_STAT_HOST_CPU_EN 0x0800 - -#define SPI_DMA_WRITE_CTRL_ENABLE 0x0001 -#define SPI_DMA_READ_CTRL_ENABLE 0x0001 -#define HOST_ALLOWED (1 << 7) - -#define SPI_TIMEOUT 100 /* msec */ - -#define SPI_MAX_TX_PACKETS 32 - -#define SPI_MAX_PACKET_SIZE 32767 - -#define SPI_TARGET_INT_WAKEUP 0x00000001 -#define SPI_TARGET_INT_SLEEP 0x00000002 -#define SPI_TARGET_INT_RDDONE 0x00000004 - -#define SPI_TARGET_INT_CTS 0x00004000 -#define SPI_TARGET_INT_DR 0x00008000 - -#define SPI_HOST_INT_READY 0x00000001 -#define SPI_HOST_INT_WR_READY 0x00000002 -#define SPI_HOST_INT_SW_UPDATE 0x00000004 -#define SPI_HOST_INT_UPDATE 0x10000000 - -/* clear to send */ -#define SPI_HOST_INT_CR 0x00004000 - -/* data ready */ -#define SPI_HOST_INT_DR 0x00008000 - -#define SPI_HOST_INTS_DEFAULT \ - (SPI_HOST_INT_READY | SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE) - -#define TARGET_BOOT_SLEEP 50 - -struct p54s_dma_regs { - __le16 cmd; - __le16 len; - __le32 addr; -} __packed; - -struct p54s_tx_info { - struct list_head tx_list; -}; - -struct p54s_priv { - /* p54_common has to be the first entry */ - struct p54_common common; - struct ieee80211_hw *hw; - struct spi_device *spi; - - struct work_struct work; - - struct mutex mutex; - struct completion fw_comp; - - spinlock_t tx_lock; - - /* protected by tx_lock */ - struct list_head tx_pending; - - enum fw_state fw_state; - const struct firmware *firmware; -}; - -#endif /* P54SPI_H */ diff --git a/drivers/net/wireless/p54/p54spi_eeprom.h b/drivers/net/wireless/p54/p54spi_eeprom.h deleted file mode 100644 index 0b7bfb0adcf2..000000000000 --- a/drivers/net/wireless/p54/p54spi_eeprom.h +++ /dev/null @@ -1,679 +0,0 @@ -/* - * Copyright (C) 2003 Conexant Americas Inc. All Rights Reserved. - * Copyright (C) 2004, 2005, 2006 Nokia Corporation - * Copyright 2008 Johannes Berg - * Copyright 2008 Christian Lamparter - * - * based on: - * - cx3110x's pda.h from Nokia - * - cx3110-transfer.log by Johannes Berg - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef P54SPI_EEPROM_H -#define P54SPI_EEPROM_H - -static unsigned char p54spi_eeprom[] = { - -/* struct eeprom_pda_wrap */ -0x47, 0x4d, 0x55, 0xaa, /* magic */ -0x00, 0x00, /* pad */ -0x00, 0x00, /* eeprom_pda_data_wrap length */ -0x00, 0x00, 0x00, 0x00, /* arm opcode */ - -/* bogus MAC address */ -0x04, 0x00, 0x01, 0x01, /* PDR_MAC_ADDRESS */ - 0x00, 0x02, 0xee, 0xc0, 0xff, 0xee, - -/* struct bootrec_exp_if */ -0x06, 0x00, 0x01, 0x10, /* PDR_INTERFACE_LIST */ - 0x00, 0x00, /* role */ - 0x0f, 0x00, /* if_id */ - 0x85, 0x00, /* variant = Longbow RF, 2GHz */ - 0x01, 0x00, /* btm_compat */ - 0x1f, 0x00, /* top_compat */ - -0x03, 0x00, 0x02, 0x10, /* PDR_HARDWARE_PLATFORM_COMPONENT_ID */ - 0x03, 0x20, 0x00, 0x43, - -/* struct pda_country[6] */ -0x0d, 0x00, 0x07, 0x10, /* PDR_COUNTRY_LIST */ - 0x10, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, - -/* struct pda_country */ -0x03, 0x00, 0x08, 0x10, /* PDR_DEFAULT_COUNTRY */ - 0x30, 0x00, 0x00, 0x00, /* ETSI */ - -0x03, 0x00, 0x00, 0x11, /* PDR_ANTENNA_GAIN */ - 0x08, 0x08, 0x08, 0x08, - -0x0a, 0x00, 0xff, 0xca, /* PDR_RSSI_LINEAR_APPROXIMATION_CUSTOMV2 */ - 0x01, 0x00, 0x0a, 0x00, - 0x00, 0x00, 0x0a, 0x00, - 0x85, 0x09, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, - -/* struct pda_custom_wrapper */ -0x10, 0x06, 0x5d, 0xb0, /* PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM */ - 0x0d, 0x00, 0xee, 0x00, /* 13 entries, 238 bytes per entry */ - 0x00, 0x00, 0x16, 0x0c, /* no offset, 3094 total len */ - /* 2412 MHz */ - 0x6c, 0x09, - 0x10, 0x01, 0x9a, 0x84, - 0xaa, 0x8a, 0xaa, 0x8a, 0xaa, 0x8a, 0xaa, 0x8a, - 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, - 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, 0x3c, 0xb6, - 0xf0, 0x00, 0x94, 0x6c, - 0x99, 0x82, 0x99, 0x82, 0x99, 0x82, 0x99, 0x82, - 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, - 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, 0x2b, 0xae, - 0xd0, 0x00, 0xaa, 0x5a, - 0x88, 0x7a, 0x88, 0x7a, 0x88, 0x7a, 0x88, 0x7a, - 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, - 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, 0x1a, 0xa6, - 0xa0, 0x00, 0xf3, 0x47, - 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, - 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, - 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, 0x00, 0x9a, - 0x50, 0x00, 0x59, 0x36, - 0x43, 0x5a, 0x43, 0x5a, 0x43, 0x5a, 0x43, 0x5a, - 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, - 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, 0xd5, 0x85, - 0x00, 0x00, 0xe4, 0x2d, - 0x18, 0x46, 0x18, 0x46, 0x18, 0x46, 0x18, 0x46, - 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, - 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, 0xaa, 0x71, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2417 MHz */ - 0x71, 0x09, - 0x10, 0x01, 0xb9, 0x83, - 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, - 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, - 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, - 0xf0, 0x00, 0x2e, 0x6c, - 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, - 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, - 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, - 0xd0, 0x00, 0x8d, 0x5a, - 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, - 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, - 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, - 0xa0, 0x00, 0x0a, 0x48, - 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, - 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, - 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, - 0x50, 0x00, 0x7c, 0x36, - 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, - 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, - 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, - 0x00, 0x00, 0xf5, 0x2d, - 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, - 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, - 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2422 MHz */ - 0x76, 0x09, - 0x10, 0x01, 0xb9, 0x83, - 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, 0x7d, 0x8a, - 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, - 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, 0x0f, 0xb6, - 0xf0, 0x00, 0x2e, 0x6c, - 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, 0x68, 0x82, - 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, - 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, 0xfa, 0xad, - 0xd0, 0x00, 0x8d, 0x5a, - 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, 0x52, 0x7a, - 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, - 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, 0xe4, 0xa5, - 0xa0, 0x00, 0x0a, 0x48, - 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, 0x32, 0x6e, - 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, - 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, 0xc4, 0x99, - 0x50, 0x00, 0x7c, 0x36, - 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, 0xfc, 0x59, - 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, - 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, 0x8e, 0x85, - 0x00, 0x00, 0xf5, 0x2d, - 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, 0xc6, 0x45, - 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, - 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, 0x58, 0x71, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2427 MHz */ - 0x7b, 0x09, - 0x10, 0x01, 0x48, 0x83, - 0x67, 0x8a, 0x67, 0x8a, 0x67, 0x8a, 0x67, 0x8a, - 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, - 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, 0xf9, 0xb5, - 0xf0, 0x00, 0xfb, 0x6b, - 0x50, 0x82, 0x50, 0x82, 0x50, 0x82, 0x50, 0x82, - 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, - 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, 0xe2, 0xad, - 0xd0, 0x00, 0x7e, 0x5a, - 0x38, 0x7a, 0x38, 0x7a, 0x38, 0x7a, 0x38, 0x7a, - 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, - 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, 0xca, 0xa5, - 0xa0, 0x00, 0x15, 0x48, - 0x14, 0x6e, 0x14, 0x6e, 0x14, 0x6e, 0x14, 0x6e, - 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, - 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, 0xa6, 0x99, - 0x50, 0x00, 0x8e, 0x36, - 0xd9, 0x59, 0xd9, 0x59, 0xd9, 0x59, 0xd9, 0x59, - 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, - 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, 0x6b, 0x85, - 0x00, 0x00, 0xfe, 0x2d, - 0x9d, 0x45, 0x9d, 0x45, 0x9d, 0x45, 0x9d, 0x45, - 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, - 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, 0x2f, 0x71, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2432 MHz */ - 0x80, 0x09, - 0x10, 0x01, 0xd7, 0x82, - 0x51, 0x8a, 0x51, 0x8a, 0x51, 0x8a, 0x51, 0x8a, - 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, - 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, 0xe3, 0xb5, - 0xf0, 0x00, 0xc8, 0x6b, - 0x37, 0x82, 0x37, 0x82, 0x37, 0x82, 0x37, 0x82, - 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, - 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, 0xc9, 0xad, - 0xd0, 0x00, 0x6f, 0x5a, - 0x1d, 0x7a, 0x1d, 0x7a, 0x1d, 0x7a, 0x1d, 0x7a, - 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, - 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, 0xaf, 0xa5, - 0xa0, 0x00, 0x20, 0x48, - 0xf6, 0x6d, 0xf6, 0x6d, 0xf6, 0x6d, 0xf6, 0x6d, - 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, - 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, 0x88, 0x99, - 0x50, 0x00, 0x9f, 0x36, - 0xb5, 0x59, 0xb5, 0x59, 0xb5, 0x59, 0xb5, 0x59, - 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, - 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, 0x47, 0x85, - 0x00, 0x00, 0x06, 0x2e, - 0x74, 0x45, 0x74, 0x45, 0x74, 0x45, 0x74, 0x45, - 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, - 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, 0x06, 0x71, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2437 MHz */ - 0x85, 0x09, - 0x10, 0x01, 0x67, 0x82, - 0x3a, 0x8a, 0x3a, 0x8a, 0x3a, 0x8a, 0x3a, 0x8a, - 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, - 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, 0xcc, 0xb5, - 0xf0, 0x00, 0x95, 0x6b, - 0x1f, 0x82, 0x1f, 0x82, 0x1f, 0x82, 0x1f, 0x82, - 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, - 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, 0xb1, 0xad, - 0xd0, 0x00, 0x61, 0x5a, - 0x02, 0x7a, 0x02, 0x7a, 0x02, 0x7a, 0x02, 0x7a, - 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, - 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, 0x94, 0xa5, - 0xa0, 0x00, 0x2c, 0x48, - 0xd8, 0x6d, 0xd8, 0x6d, 0xd8, 0x6d, 0xd8, 0x6d, - 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, - 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, 0x6a, 0x99, - 0x50, 0x00, 0xb1, 0x36, - 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, - 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, - 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, - 0x00, 0x00, 0x0f, 0x2e, - 0x4b, 0x45, 0x4b, 0x45, 0x4b, 0x45, 0x4b, 0x45, - 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, - 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, 0xdd, 0x70, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2442 MHz */ - 0x8a, 0x09, - 0x10, 0x01, 0xf6, 0x81, - 0x24, 0x8a, 0x24, 0x8a, 0x24, 0x8a, 0x24, 0x8a, - 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, - 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, 0xb6, 0xb5, - 0xf0, 0x00, 0x62, 0x6b, - 0x06, 0x82, 0x06, 0x82, 0x06, 0x82, 0x06, 0x82, - 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, - 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, 0x98, 0xad, - 0xd0, 0x00, 0x52, 0x5a, - 0xe7, 0x79, 0xe7, 0x79, 0xe7, 0x79, 0xe7, 0x79, - 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, - 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, 0x79, 0xa5, - 0xa0, 0x00, 0x37, 0x48, - 0xba, 0x6d, 0xba, 0x6d, 0xba, 0x6d, 0xba, 0x6d, - 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, - 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, 0x4c, 0x99, - 0x50, 0x00, 0xc2, 0x36, - 0x6e, 0x59, 0x6e, 0x59, 0x6e, 0x59, 0x6e, 0x59, - 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, - 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, 0x00, 0x85, - 0x00, 0x00, 0x17, 0x2e, - 0x22, 0x45, 0x22, 0x45, 0x22, 0x45, 0x22, 0x45, - 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, - 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, 0xb4, 0x70, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2447 MHz */ - 0x8f, 0x09, - 0x10, 0x01, 0x75, 0x83, - 0x61, 0x8a, 0x61, 0x8a, 0x61, 0x8a, 0x61, 0x8a, - 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, - 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, 0xf3, 0xb5, - 0xf0, 0x00, 0x4b, 0x6c, - 0x3f, 0x82, 0x3f, 0x82, 0x3f, 0x82, 0x3f, 0x82, - 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, - 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, 0xd1, 0xad, - 0xd0, 0x00, 0xda, 0x5a, - 0x1c, 0x7a, 0x1c, 0x7a, 0x1c, 0x7a, 0x1c, 0x7a, - 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, - 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, 0xae, 0xa5, - 0xa0, 0x00, 0x6d, 0x48, - 0xe9, 0x6d, 0xe9, 0x6d, 0xe9, 0x6d, 0xe9, 0x6d, - 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, - 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, 0x7b, 0x99, - 0x50, 0x00, 0xc6, 0x36, - 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, 0x92, 0x59, - 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, - 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, 0x24, 0x85, - 0x00, 0x00, 0x15, 0x2e, - 0x3c, 0x45, 0x3c, 0x45, 0x3c, 0x45, 0x3c, 0x45, - 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, - 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, 0xce, 0x70, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2452 MHz */ - 0x94, 0x09, - 0x10, 0x01, 0xf4, 0x84, - 0x9e, 0x8a, 0x9e, 0x8a, 0x9e, 0x8a, 0x9e, 0x8a, - 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, - 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, 0x30, 0xb6, - 0xf0, 0x00, 0x34, 0x6d, - 0x77, 0x82, 0x77, 0x82, 0x77, 0x82, 0x77, 0x82, - 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, - 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, 0x09, 0xae, - 0xd0, 0x00, 0x62, 0x5b, - 0x50, 0x7a, 0x50, 0x7a, 0x50, 0x7a, 0x50, 0x7a, - 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, - 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, 0xe2, 0xa5, - 0xa0, 0x00, 0xa2, 0x48, - 0x17, 0x6e, 0x17, 0x6e, 0x17, 0x6e, 0x17, 0x6e, - 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, - 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, 0xa9, 0x99, - 0x50, 0x00, 0xc9, 0x36, - 0xb7, 0x59, 0xb7, 0x59, 0xb7, 0x59, 0xb7, 0x59, - 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, - 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, 0x49, 0x85, - 0x00, 0x00, 0x12, 0x2e, - 0x57, 0x45, 0x57, 0x45, 0x57, 0x45, 0x57, 0x45, - 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, - 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, 0xe9, 0x70, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2452 MHz */ - 0x99, 0x09, - 0x10, 0x01, 0x74, 0x86, - 0xdb, 0x8a, 0xdb, 0x8a, 0xdb, 0x8a, 0xdb, 0x8a, - 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, - 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, 0x6d, 0xb6, - 0xf0, 0x00, 0x1e, 0x6e, - 0xb0, 0x82, 0xb0, 0x82, 0xb0, 0x82, 0xb0, 0x82, - 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, - 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, 0x42, 0xae, - 0xd0, 0x00, 0xeb, 0x5b, - 0x85, 0x7a, 0x85, 0x7a, 0x85, 0x7a, 0x85, 0x7a, - 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, - 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, 0x17, 0xa6, - 0xa0, 0x00, 0xd8, 0x48, - 0x46, 0x6e, 0x46, 0x6e, 0x46, 0x6e, 0x46, 0x6e, - 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, - 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, 0xd8, 0x99, - 0x50, 0x00, 0xcd, 0x36, - 0xdb, 0x59, 0xdb, 0x59, 0xdb, 0x59, 0xdb, 0x59, - 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, - 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, 0x6d, 0x85, - 0x00, 0x00, 0x10, 0x2e, - 0x71, 0x45, 0x71, 0x45, 0x71, 0x45, 0x71, 0x45, - 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, - 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, 0x03, 0x71, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2557 MHz */ - 0x9e, 0x09, - 0x10, 0x01, 0xf3, 0x87, - 0x17, 0x8b, 0x17, 0x8b, 0x17, 0x8b, 0x17, 0x8b, - 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, - 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, 0xa9, 0xb6, - 0xf0, 0x00, 0x07, 0x6f, - 0xe9, 0x82, 0xe9, 0x82, 0xe9, 0x82, 0xe9, 0x82, - 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, - 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, 0x7b, 0xae, - 0xd0, 0x00, 0x73, 0x5c, - 0xba, 0x7a, 0xba, 0x7a, 0xba, 0x7a, 0xba, 0x7a, - 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, - 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, 0x4c, 0xa6, - 0xa0, 0x00, 0x0d, 0x49, - 0x74, 0x6e, 0x74, 0x6e, 0x74, 0x6e, 0x74, 0x6e, - 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, - 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, 0x06, 0x9a, - 0x50, 0x00, 0xd1, 0x36, - 0xff, 0x59, 0xff, 0x59, 0xff, 0x59, 0xff, 0x59, - 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, - 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, 0x91, 0x85, - 0x00, 0x00, 0x0e, 0x2e, - 0x8b, 0x45, 0x8b, 0x45, 0x8b, 0x45, 0x8b, 0x45, - 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, - 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, 0x1d, 0x71, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2562 MHz */ - 0xa3, 0x09, - 0x10, 0x01, 0x72, 0x89, - 0x54, 0x8b, 0x54, 0x8b, 0x54, 0x8b, 0x54, 0x8b, - 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, - 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, 0xe6, 0xb6, - 0xf0, 0x00, 0xf0, 0x6f, - 0x21, 0x83, 0x21, 0x83, 0x21, 0x83, 0x21, 0x83, - 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, - 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, 0xb3, 0xae, - 0xd0, 0x00, 0xfb, 0x5c, - 0xee, 0x7a, 0xee, 0x7a, 0xee, 0x7a, 0xee, 0x7a, - 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, - 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, 0x80, 0xa6, - 0xa0, 0x00, 0x43, 0x49, - 0xa3, 0x6e, 0xa3, 0x6e, 0xa3, 0x6e, 0xa3, 0x6e, - 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, - 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, 0x35, 0x9a, - 0x50, 0x00, 0xd4, 0x36, - 0x24, 0x5a, 0x24, 0x5a, 0x24, 0x5a, 0x24, 0x5a, - 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, - 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, 0xb6, 0x85, - 0x00, 0x00, 0x0b, 0x2e, - 0xa6, 0x45, 0xa6, 0x45, 0xa6, 0x45, 0xa6, 0x45, - 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, - 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, 0x38, 0x71, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - - /* 2572 MHz */ - 0xa8, 0x09, - 0x10, 0x01, 0xf1, 0x8a, - 0x91, 0x8b, 0x91, 0x8b, 0x91, 0x8b, 0x91, 0x8b, - 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, - 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, 0x23, 0xb7, - 0xf0, 0x00, 0xd9, 0x70, - 0x5a, 0x83, 0x5a, 0x83, 0x5a, 0x83, 0x5a, 0x83, - 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, - 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, 0xec, 0xae, - 0xd0, 0x00, 0x83, 0x5d, - 0x23, 0x7b, 0x23, 0x7b, 0x23, 0x7b, 0x23, 0x7b, - 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, - 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, 0xb5, 0xa6, - 0xa0, 0x00, 0x78, 0x49, - 0xd1, 0x6e, 0xd1, 0x6e, 0xd1, 0x6e, 0xd1, 0x6e, - 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, - 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, 0x63, 0x9a, - 0x50, 0x00, 0xd8, 0x36, - 0x48, 0x5a, 0x48, 0x5a, 0x48, 0x5a, 0x48, 0x5a, - 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, - 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, 0xda, 0x85, - 0x00, 0x00, 0x09, 0x2e, - 0xc0, 0x45, 0xc0, 0x45, 0xc0, 0x45, 0xc0, 0x45, - 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, - 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, 0x52, 0x71, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, - -/* - * Not really sure if this is actually the power_limit database, - * it looks a bit "related" to PDR_PRISM_ZIF_TX_IQ_CALIBRATION - */ -/* struct pda_custom_wrapper */ -0xae, 0x00, 0xef, 0xbe, /* PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM */ - 0x0d, 0x00, 0x1a, 0x00, /* 13 entries, 26 bytes per entry */ - 0x00, 0x00, 0x52, 0x01, /* no offset, 338 bytes total */ - - /* 2412 MHz */ - 0x6c, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0xe0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2417 MHz */ - 0x71, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2422 MHz */ - 0x76, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2427 MHz */ - 0x7b, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2432 MHz */ - 0x80, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2437 MHz */ - 0x85, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2442 MHz */ - 0x8a, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2447 MHz */ - 0x8f, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2452 MHz */ - 0x94, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2457 MHz */ - 0x99, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2462 MHz */ - 0x9e, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2467 MHz */ - 0xa3, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - - /* 2472 MHz */ - 0xa8, 0x09, - 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, - 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, - 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, - -/* struct pda_iq_autocal_entry[13] */ -0x42, 0x00, 0x06, 0x19, /* PDR_PRISM_ZIF_TX_IQ_CALIBRATION */ - /* 2412 MHz */ - 0x6c, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, - /* 2417 MHz */ - 0x71, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, - /* 2422 MHz */ - 0x76, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, - /* 2427 MHz */ - 0x7b, 0x09, 0x26, 0x00, 0xf8, 0xff, 0xf7, 0xff, 0xff, 0x00, - /* 2432 MHz */ - 0x80, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, - /* 2437 MHz */ - 0x85, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, - /* 2442 MHz */ - 0x8a, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, - /* 2447 MHz */ - 0x8f, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, - /* 2452 MHz */ - 0x94, 0x09, 0x25, 0x00, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0x00, - /* 2457 MHz */ - 0x99, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, - /* 2462 MHz */ - 0x9e, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, - /* 2467 MHz */ - 0xa3, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, - /* 2472 MHz */ - 0xa8, 0x09, 0x25, 0x00, 0xf5, 0xff, 0xf9, 0xff, 0x00, 0x01, - -0x02, 0x00, 0x00, 0x00, /* PDR_END */ - 0xb6, 0x04, -}; - -#endif /* P54SPI_EEPROM_H */ - diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c deleted file mode 100644 index 043bd1c23c19..000000000000 --- a/drivers/net/wireless/p54/p54usb.c +++ /dev/null @@ -1,1149 +0,0 @@ - -/* - * Linux device driver for USB based Prism54 - * - * Copyright (c) 2006, Michael Wu - * - * Based on the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "p54.h" -#include "lmac.h" -#include "p54usb.h" - -MODULE_AUTHOR("Michael Wu "); -MODULE_DESCRIPTION("Prism54 USB wireless driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("prism54usb"); -MODULE_FIRMWARE("isl3886usb"); -MODULE_FIRMWARE("isl3887usb"); - -/* - * Note: - * - * Always update our wiki's device list (located at: - * http://wireless.kernel.org/en/users/Drivers/p54/devices ), - * whenever you add a new device. - */ - -static struct usb_device_id p54u_table[] = { - /* Version 1 devices (pci chip + net2280) */ - {USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */ - {USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */ - {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */ - {USB_DEVICE(0x0675, 0x0530)}, /* DrayTek Vigor 530 */ - {USB_DEVICE(0x06b9, 0x0120)}, /* Thomson SpeedTouch 120g */ - {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */ - {USB_DEVICE(0x07aa, 0x001c)}, /* Corega CG-WLUSB2GT */ - {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */ - {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */ - {USB_DEVICE(0x083a, 0x5501)}, /* Phillips CPWUA054 */ - {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */ - {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */ - {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */ - {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */ - {USB_DEVICE(0x0bf8, 0x1007)}, /* Fujitsu E-5400 USB */ - {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */ - {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */ - {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */ - {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */ - {USB_DEVICE(0x1435, 0x0210)}, /* Inventel UR054G */ - {USB_DEVICE(0x15a9, 0x0002)}, /* Gemtek WUBI-100GW 802.11g */ - {USB_DEVICE(0x1630, 0x0005)}, /* 2Wire 802.11g USB (v1) / Z-Com */ - {USB_DEVICE(0x182d, 0x096b)}, /* Sitecom WL-107 */ - {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */ - {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */ - {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */ - {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */ - {USB_DEVICE(0x2001, 0x3762)}, /* Conceptronic C54U */ - {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */ - {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */ - - /* Version 2 devices (3887) */ - {USB_DEVICE(0x0471, 0x1230)}, /* Philips CPWUA054/00 */ - {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */ - {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */ - {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */ - {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */ - {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */ - {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ - {USB_DEVICE(0x07aa, 0x0020)}, /* Corega WLUSB2GTST USB */ - {USB_DEVICE(0x0803, 0x4310)}, /* Zoom 4410a */ - {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ - {USB_DEVICE(0x083a, 0x4531)}, /* T-Com Sinus 154 data II */ - {USB_DEVICE(0x083a, 0xc501)}, /* Zoom Wireless-G 4410 */ - {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */ - {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */ - {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */ - {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */ - {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/ - {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/ - /* {USB_DEVICE(0x0cde, 0x0006)}, * Medion MD40900 already listed above, - * just noting it here for clarity */ - {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */ - {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */ - {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */ - {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */ - {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */ - {USB_DEVICE(0x13b1, 0x000a)}, /* Linksys WUSB54G ver 2 */ - {USB_DEVICE(0x13B1, 0x000C)}, /* Linksys WUSB54AG */ - {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */ - {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */ - /* {USB_DEVICE(0x15a9, 0x0002)}, * Also SparkLAN WL-682 with 3887 */ - {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */ - {USB_DEVICE(0x1740, 0x1000)}, /* Senao NUB-350 */ - {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */ - {USB_DEVICE(0x2001, 0x3705)}, /* D-Link DWL-G120 rev C1 */ - {USB_DEVICE(0x413c, 0x5513)}, /* Dell WLA3310 USB Wireless Adapter */ - {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */ - {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */ - {} -}; - -MODULE_DEVICE_TABLE(usb, p54u_table); - -static const struct { - u32 intf; - enum p54u_hw_type type; - const char *fw; - char hw[20]; -} p54u_fwlist[__NUM_P54U_HWTYPES] = { - { - .type = P54U_NET2280, - .intf = FW_LM86, - .fw = "isl3886usb", - .hw = "ISL3886 + net2280", - }, - { - .type = P54U_3887, - .intf = FW_LM87, - .fw = "isl3887usb", - .hw = "ISL3887", - }, -}; - -static void p54u_rx_cb(struct urb *urb) -{ - struct sk_buff *skb = (struct sk_buff *) urb->context; - struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb; - struct ieee80211_hw *dev = info->dev; - struct p54u_priv *priv = dev->priv; - - skb_unlink(skb, &priv->rx_queue); - - if (unlikely(urb->status)) { - dev_kfree_skb_irq(skb); - return; - } - - skb_put(skb, urb->actual_length); - - if (priv->hw_type == P54U_NET2280) - skb_pull(skb, priv->common.tx_hdr_len); - if (priv->common.fw_interface == FW_LM87) { - skb_pull(skb, 4); - skb_put(skb, 4); - } - - if (p54_rx(dev, skb)) { - skb = dev_alloc_skb(priv->common.rx_mtu + 32); - if (unlikely(!skb)) { - /* TODO check rx queue length and refill *somewhere* */ - return; - } - - info = (struct p54u_rx_info *) skb->cb; - info->urb = urb; - info->dev = dev; - urb->transfer_buffer = skb_tail_pointer(skb); - urb->context = skb; - } else { - if (priv->hw_type == P54U_NET2280) - skb_push(skb, priv->common.tx_hdr_len); - if (priv->common.fw_interface == FW_LM87) { - skb_push(skb, 4); - skb_put(skb, 4); - } - skb_reset_tail_pointer(skb); - skb_trim(skb, 0); - urb->transfer_buffer = skb_tail_pointer(skb); - } - skb_queue_tail(&priv->rx_queue, skb); - usb_anchor_urb(urb, &priv->submitted); - if (usb_submit_urb(urb, GFP_ATOMIC)) { - skb_unlink(skb, &priv->rx_queue); - usb_unanchor_urb(urb); - dev_kfree_skb_irq(skb); - } -} - -static void p54u_tx_cb(struct urb *urb) -{ - struct sk_buff *skb = urb->context; - struct ieee80211_hw *dev = - usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); - - p54_free_skb(dev, skb); -} - -static void p54u_tx_dummy_cb(struct urb *urb) { } - -static void p54u_free_urbs(struct ieee80211_hw *dev) -{ - struct p54u_priv *priv = dev->priv; - usb_kill_anchored_urbs(&priv->submitted); -} - -static void p54u_stop(struct ieee80211_hw *dev) -{ - /* - * TODO: figure out how to reliably stop the 3887 and net2280 so - * the hardware is still usable next time we want to start it. - * until then, we just stop listening to the hardware.. - */ - p54u_free_urbs(dev); -} - -static int p54u_init_urbs(struct ieee80211_hw *dev) -{ - struct p54u_priv *priv = dev->priv; - struct urb *entry = NULL; - struct sk_buff *skb; - struct p54u_rx_info *info; - int ret = 0; - - while (skb_queue_len(&priv->rx_queue) < 32) { - skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL); - if (!skb) { - ret = -ENOMEM; - goto err; - } - entry = usb_alloc_urb(0, GFP_KERNEL); - if (!entry) { - ret = -ENOMEM; - goto err; - } - - usb_fill_bulk_urb(entry, priv->udev, - usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), - skb_tail_pointer(skb), - priv->common.rx_mtu + 32, p54u_rx_cb, skb); - info = (struct p54u_rx_info *) skb->cb; - info->urb = entry; - info->dev = dev; - skb_queue_tail(&priv->rx_queue, skb); - - usb_anchor_urb(entry, &priv->submitted); - ret = usb_submit_urb(entry, GFP_KERNEL); - if (ret) { - skb_unlink(skb, &priv->rx_queue); - usb_unanchor_urb(entry); - goto err; - } - usb_free_urb(entry); - entry = NULL; - } - - return 0; - - err: - usb_free_urb(entry); - kfree_skb(skb); - p54u_free_urbs(dev); - return ret; -} - -static int p54u_open(struct ieee80211_hw *dev) -{ - /* - * TODO: Because we don't know how to reliably stop the 3887 and - * the isl3886+net2280, other than brutally cut off all - * communications. We have to reinitialize the urbs on every start. - */ - return p54u_init_urbs(dev); -} - -static __le32 p54u_lm87_chksum(const __le32 *data, size_t length) -{ - u32 chk = 0; - - length >>= 2; - while (length--) { - chk ^= le32_to_cpu(*data++); - chk = (chk >> 5) ^ (chk << 3); - } - - return cpu_to_le32(chk); -} - -static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct p54u_priv *priv = dev->priv; - struct urb *data_urb; - struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); - - data_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!data_urb) { - p54_free_skb(dev, skb); - return; - } - - hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); - hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id; - - usb_fill_bulk_urb(data_urb, priv->udev, - usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), - hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? - p54u_tx_cb : p54u_tx_dummy_cb, skb); - data_urb->transfer_flags |= URB_ZERO_PACKET; - - usb_anchor_urb(data_urb, &priv->submitted); - if (usb_submit_urb(data_urb, GFP_ATOMIC)) { - usb_unanchor_urb(data_urb); - p54_free_skb(dev, skb); - } - usb_free_urb(data_urb); -} - -static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct p54u_priv *priv = dev->priv; - struct urb *int_urb = NULL, *data_urb = NULL; - struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); - struct net2280_reg_write *reg = NULL; - int err = -ENOMEM; - - reg = kmalloc(sizeof(*reg), GFP_ATOMIC); - if (!reg) - goto out; - - int_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!int_urb) - goto out; - - data_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!data_urb) - goto out; - - reg->port = cpu_to_le16(NET2280_DEV_U32); - reg->addr = cpu_to_le32(P54U_DEV_BASE); - reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); - - memset(hdr, 0, sizeof(*hdr)); - hdr->len = cpu_to_le16(skb->len); - hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id; - - usb_fill_bulk_urb(int_urb, priv->udev, - usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), - p54u_tx_dummy_cb, dev); - - /* - * URB_FREE_BUFFER triggers a code path in the USB subsystem that will - * free what is inside the transfer_buffer after the last reference to - * the int_urb is dropped. - */ - int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET; - reg = NULL; - - usb_fill_bulk_urb(data_urb, priv->udev, - usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), - hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? - p54u_tx_cb : p54u_tx_dummy_cb, skb); - data_urb->transfer_flags |= URB_ZERO_PACKET; - - usb_anchor_urb(int_urb, &priv->submitted); - err = usb_submit_urb(int_urb, GFP_ATOMIC); - if (err) { - usb_unanchor_urb(int_urb); - goto out; - } - - usb_anchor_urb(data_urb, &priv->submitted); - err = usb_submit_urb(data_urb, GFP_ATOMIC); - if (err) { - usb_unanchor_urb(data_urb); - goto out; - } -out: - usb_free_urb(int_urb); - usb_free_urb(data_urb); - - if (err) { - kfree(reg); - p54_free_skb(dev, skb); - } -} - -static int p54u_write(struct p54u_priv *priv, - struct net2280_reg_write *buf, - enum net2280_op_type type, - __le32 addr, __le32 val) -{ - unsigned int ep; - int alen; - - if (type & 0x0800) - ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV); - else - ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG); - - buf->port = cpu_to_le16(type); - buf->addr = addr; - buf->val = val; - - return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000); -} - -static int p54u_read(struct p54u_priv *priv, void *buf, - enum net2280_op_type type, - __le32 addr, __le32 *val) -{ - struct net2280_reg_read *read = buf; - __le32 *reg = buf; - unsigned int ep; - int alen, err; - - if (type & 0x0800) - ep = P54U_PIPE_DEV; - else - ep = P54U_PIPE_BRG; - - read->port = cpu_to_le16(type); - read->addr = addr; - - err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), - read, sizeof(*read), &alen, 1000); - if (err) - return err; - - err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep), - reg, sizeof(*reg), &alen, 1000); - if (err) - return err; - - *val = *reg; - return 0; -} - -static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep, - void *data, size_t len) -{ - int alen; - return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), - data, len, &alen, 2000); -} - -static int p54u_device_reset(struct ieee80211_hw *dev) -{ - struct p54u_priv *priv = dev->priv; - int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING); - - if (lock) { - ret = usb_lock_device_for_reset(priv->udev, priv->intf); - if (ret < 0) { - dev_err(&priv->udev->dev, "(p54usb) unable to lock " - "device for reset (%d)!\n", ret); - return ret; - } - } - - ret = usb_reset_device(priv->udev); - if (lock) - usb_unlock_device(priv->udev); - - if (ret) - dev_err(&priv->udev->dev, "(p54usb) unable to reset " - "device (%d)!\n", ret); - - return ret; -} - -static const char p54u_romboot_3887[] = "~~~~"; -static int p54u_firmware_reset_3887(struct ieee80211_hw *dev) -{ - struct p54u_priv *priv = dev->priv; - u8 *buf; - int ret; - - buf = kmemdup(p54u_romboot_3887, 4, GFP_KERNEL); - if (!buf) - return -ENOMEM; - ret = p54u_bulk_msg(priv, P54U_PIPE_DATA, - buf, 4); - kfree(buf); - if (ret) - dev_err(&priv->udev->dev, "(p54usb) unable to jump to " - "boot ROM (%d)!\n", ret); - - return ret; -} - -static const char p54u_firmware_upload_3887[] = "<\r"; -static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) -{ - struct p54u_priv *priv = dev->priv; - int err, alen; - u8 carry = 0; - u8 *buf, *tmp; - const u8 *data; - unsigned int left, remains, block_size; - struct x2_header *hdr; - unsigned long timeout; - - err = p54u_firmware_reset_3887(dev); - if (err) - return err; - - tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - left = block_size = min_t(size_t, P54U_FW_BLOCK, priv->fw->size); - strcpy(buf, p54u_firmware_upload_3887); - left -= strlen(p54u_firmware_upload_3887); - tmp += strlen(p54u_firmware_upload_3887); - - data = priv->fw->data; - remains = priv->fw->size; - - hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887)); - memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE); - hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR); - hdr->fw_length = cpu_to_le32(priv->fw->size); - hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr, - sizeof(u32)*2)); - left -= sizeof(*hdr); - tmp += sizeof(*hdr); - - while (remains) { - while (left--) { - if (carry) { - *tmp++ = carry; - carry = 0; - remains--; - continue; - } - switch (*data) { - case '~': - *tmp++ = '}'; - carry = '^'; - break; - case '}': - *tmp++ = '}'; - carry = ']'; - break; - default: - *tmp++ = *data; - remains--; - break; - } - data++; - } - - err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size); - if (err) { - dev_err(&priv->udev->dev, "(p54usb) firmware " - "upload failed!\n"); - goto err_upload_failed; - } - - tmp = buf; - left = block_size = min((unsigned int)P54U_FW_BLOCK, remains); - } - - *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data, - priv->fw->size)); - err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); - if (err) { - dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); - goto err_upload_failed; - } - timeout = jiffies + msecs_to_jiffies(1000); - while (!(err = usb_bulk_msg(priv->udev, - usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { - if (alen > 2 && !memcmp(buf, "OK", 2)) - break; - - if (alen > 5 && !memcmp(buf, "ERROR", 5)) { - err = -EINVAL; - break; - } - - if (time_after(jiffies, timeout)) { - dev_err(&priv->udev->dev, "(p54usb) firmware boot " - "timed out!\n"); - err = -ETIMEDOUT; - break; - } - } - if (err) { - dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n"); - goto err_upload_failed; - } - - buf[0] = 'g'; - buf[1] = '\r'; - err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2); - if (err) { - dev_err(&priv->udev->dev, "(p54usb) firmware boot failed!\n"); - goto err_upload_failed; - } - - timeout = jiffies + msecs_to_jiffies(1000); - while (!(err = usb_bulk_msg(priv->udev, - usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { - if (alen > 0 && buf[0] == 'g') - break; - - if (time_after(jiffies, timeout)) { - err = -ETIMEDOUT; - break; - } - } - if (err) - goto err_upload_failed; - -err_upload_failed: - kfree(buf); - return err; -} - -static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) -{ - struct p54u_priv *priv = dev->priv; - const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE; - int err, alen; - void *buf; - __le32 reg; - unsigned int remains, offset; - const u8 *data; - - buf = kmalloc(512, GFP_KERNEL); - if (!buf) - return -ENOMEM; - -#define P54U_WRITE(type, addr, data) \ - do {\ - err = p54u_write(priv, buf, type,\ - cpu_to_le32((u32)(unsigned long)addr), data);\ - if (err) \ - goto fail;\ - } while (0) - -#define P54U_READ(type, addr) \ - do {\ - err = p54u_read(priv, buf, type,\ - cpu_to_le32((u32)(unsigned long)addr), ®);\ - if (err)\ - goto fail;\ - } while (0) - - /* power down net2280 bridge */ - P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL); - reg |= cpu_to_le32(P54U_BRG_POWER_DOWN); - reg &= cpu_to_le32(~P54U_BRG_POWER_UP); - P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); - - mdelay(100); - - /* power up bridge */ - reg |= cpu_to_le32(P54U_BRG_POWER_UP); - reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN); - P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); - - mdelay(100); - - P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT, - cpu_to_le32(NET2280_CLK_30Mhz | - NET2280_PCI_ENABLE | - NET2280_PCI_SOFT_RESET)); - - mdelay(20); - - P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND, - cpu_to_le32(PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER)); - - P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0, - cpu_to_le32(NET2280_BASE)); - - P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS); - reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT); - P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg); - - // TODO: we really need this? - P54U_READ(NET2280_BRG_U32, NET2280_RELNUM); - - P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP, - cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); - P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP, - cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); - - P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2, - cpu_to_le32(NET2280_BASE2)); - - /* finally done setting up the bridge */ - - P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND, - cpu_to_le32(PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER)); - - P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0); - P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0, - cpu_to_le32(P54U_DEV_BASE)); - - P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); - P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, - cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); - - /* do romboot */ - P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0); - - P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); - P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); - - mdelay(20); - - reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); - P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); - - mdelay(20); - - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); - P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); - - mdelay(100); - - P54U_READ(NET2280_DEV_U32, &devreg->int_ident); - P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); - - /* finally, we can upload firmware now! */ - remains = priv->fw->size; - data = priv->fw->data; - offset = ISL38XX_DEV_FIRMWARE_ADDR; - - while (remains) { - unsigned int block_len = min(remains, (unsigned int)512); - memcpy(buf, data, block_len); - - err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len); - if (err) { - dev_err(&priv->udev->dev, "(p54usb) firmware block " - "upload failed\n"); - goto fail; - } - - P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base, - cpu_to_le32(0xc0000f00)); - - P54U_WRITE(NET2280_DEV_U32, - 0x0020 | (unsigned long)&devreg->direct_mem_win, 0); - P54U_WRITE(NET2280_DEV_U32, - 0x0020 | (unsigned long)&devreg->direct_mem_win, - cpu_to_le32(1)); - - P54U_WRITE(NET2280_DEV_U32, - 0x0024 | (unsigned long)&devreg->direct_mem_win, - cpu_to_le32(block_len)); - P54U_WRITE(NET2280_DEV_U32, - 0x0028 | (unsigned long)&devreg->direct_mem_win, - cpu_to_le32(offset)); - - P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr, - cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR)); - P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len, - cpu_to_le32(block_len >> 2)); - P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl, - cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER)); - - mdelay(10); - - P54U_READ(NET2280_DEV_U32, - 0x002C | (unsigned long)&devreg->direct_mem_win); - if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) || - !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) { - dev_err(&priv->udev->dev, "(p54usb) firmware DMA " - "transfer failed\n"); - goto fail; - } - - P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT, - cpu_to_le32(NET2280_FIFO_FLUSH)); - - remains -= block_len; - data += block_len; - offset += block_len; - } - - /* do ramboot */ - P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); - reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); - P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); - - mdelay(20); - - reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); - P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); - - reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); - P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); - - mdelay(100); - - P54U_READ(NET2280_DEV_U32, &devreg->int_ident); - P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); - - /* start up the firmware */ - P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, - cpu_to_le32(ISL38XX_INT_IDENT_INIT)); - - P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, - cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); - - P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, - cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE | - NET2280_USB_INTERRUPT_ENABLE)); - - P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int, - cpu_to_le32(ISL38XX_DEV_INT_RESET)); - - err = usb_interrupt_msg(priv->udev, - usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT), - buf, sizeof(__le32), &alen, 1000); - if (err || alen != sizeof(__le32)) - goto fail; - - P54U_READ(NET2280_DEV_U32, &devreg->int_ident); - P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); - - if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT))) - err = -EINVAL; - - P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); - P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, - cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); - -#undef P54U_WRITE -#undef P54U_READ - -fail: - kfree(buf); - return err; -} - -static int p54_find_type(struct p54u_priv *priv) -{ - int i; - - for (i = 0; i < __NUM_P54U_HWTYPES; i++) - if (p54u_fwlist[i].type == priv->hw_type) - break; - if (i == __NUM_P54U_HWTYPES) - return -EOPNOTSUPP; - - return i; -} - -static int p54u_start_ops(struct p54u_priv *priv) -{ - struct ieee80211_hw *dev = priv->common.hw; - int ret; - - ret = p54_parse_firmware(dev, priv->fw); - if (ret) - goto err_out; - - ret = p54_find_type(priv); - if (ret < 0) - goto err_out; - - if (priv->common.fw_interface != p54u_fwlist[ret].intf) { - dev_err(&priv->udev->dev, "wrong firmware, please get " - "a firmware for \"%s\" and try again.\n", - p54u_fwlist[ret].hw); - ret = -ENODEV; - goto err_out; - } - - ret = priv->upload_fw(dev); - if (ret) - goto err_out; - - ret = p54u_open(dev); - if (ret) - goto err_out; - - ret = p54_read_eeprom(dev); - if (ret) - goto err_stop; - - p54u_stop(dev); - - ret = p54_register_common(dev, &priv->udev->dev); - if (ret) - goto err_stop; - - return 0; - -err_stop: - p54u_stop(dev); - -err_out: - /* - * p54u_disconnect will do the rest of the - * cleanup - */ - return ret; -} - -static void p54u_load_firmware_cb(const struct firmware *firmware, - void *context) -{ - struct p54u_priv *priv = context; - struct usb_device *udev = priv->udev; - int err; - - complete(&priv->fw_wait_load); - if (firmware) { - priv->fw = firmware; - err = p54u_start_ops(priv); - } else { - err = -ENOENT; - dev_err(&udev->dev, "Firmware not found.\n"); - } - - if (err) { - struct device *parent = priv->udev->dev.parent; - - dev_err(&udev->dev, "failed to initialize device (%d)\n", err); - - if (parent) - device_lock(parent); - - device_release_driver(&udev->dev); - /* - * At this point p54u_disconnect has already freed - * the "priv" context. Do not use it anymore! - */ - priv = NULL; - - if (parent) - device_unlock(parent); - } - - usb_put_dev(udev); -} - -static int p54u_load_firmware(struct ieee80211_hw *dev, - struct usb_interface *intf) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct p54u_priv *priv = dev->priv; - struct device *device = &udev->dev; - int err, i; - - BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES); - - init_completion(&priv->fw_wait_load); - i = p54_find_type(priv); - if (i < 0) - return i; - - dev_info(&priv->udev->dev, "Loading firmware file %s\n", - p54u_fwlist[i].fw); - - usb_get_dev(udev); - err = request_firmware_nowait(THIS_MODULE, 1, p54u_fwlist[i].fw, - device, GFP_KERNEL, priv, - p54u_load_firmware_cb); - if (err) { - dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s " - "(%d)!\n", p54u_fwlist[i].fw, err); - usb_put_dev(udev); - } - - return err; -} - -static int p54u_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct ieee80211_hw *dev; - struct p54u_priv *priv; - int err; - unsigned int i, recognized_pipes; - - dev = p54_init_common(sizeof(*priv)); - - if (!dev) { - dev_err(&udev->dev, "(p54usb) ieee80211 alloc failed\n"); - return -ENOMEM; - } - - priv = dev->priv; - priv->hw_type = P54U_INVALID_HW; - - SET_IEEE80211_DEV(dev, &intf->dev); - usb_set_intfdata(intf, dev); - priv->udev = udev; - priv->intf = intf; - skb_queue_head_init(&priv->rx_queue); - init_usb_anchor(&priv->submitted); - - usb_get_dev(udev); - - /* really lazy and simple way of figuring out if we're a 3887 */ - /* TODO: should just stick the identification in the device table */ - i = intf->altsetting->desc.bNumEndpoints; - recognized_pipes = 0; - while (i--) { - switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) { - case P54U_PIPE_DATA: - case P54U_PIPE_MGMT: - case P54U_PIPE_BRG: - case P54U_PIPE_DEV: - case P54U_PIPE_DATA | USB_DIR_IN: - case P54U_PIPE_MGMT | USB_DIR_IN: - case P54U_PIPE_BRG | USB_DIR_IN: - case P54U_PIPE_DEV | USB_DIR_IN: - case P54U_PIPE_INT | USB_DIR_IN: - recognized_pipes++; - } - } - priv->common.open = p54u_open; - priv->common.stop = p54u_stop; - if (recognized_pipes < P54U_PIPE_NUMBER) { -#ifdef CONFIG_PM - /* ISL3887 needs a full reset on resume */ - udev->reset_resume = 1; -#endif /* CONFIG_PM */ - err = p54u_device_reset(dev); - - priv->hw_type = P54U_3887; - dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr); - priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr); - priv->common.tx = p54u_tx_lm87; - priv->upload_fw = p54u_upload_firmware_3887; - } else { - priv->hw_type = P54U_NET2280; - dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr); - priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr); - priv->common.tx = p54u_tx_net2280; - priv->upload_fw = p54u_upload_firmware_net2280; - } - err = p54u_load_firmware(dev, intf); - if (err) { - usb_put_dev(udev); - p54_free_common(dev); - } - return err; -} - -static void p54u_disconnect(struct usb_interface *intf) -{ - struct ieee80211_hw *dev = usb_get_intfdata(intf); - struct p54u_priv *priv; - - if (!dev) - return; - - priv = dev->priv; - wait_for_completion(&priv->fw_wait_load); - p54_unregister_common(dev); - - usb_put_dev(interface_to_usbdev(intf)); - release_firmware(priv->fw); - p54_free_common(dev); -} - -static int p54u_pre_reset(struct usb_interface *intf) -{ - struct ieee80211_hw *dev = usb_get_intfdata(intf); - - if (!dev) - return -ENODEV; - - p54u_stop(dev); - return 0; -} - -static int p54u_resume(struct usb_interface *intf) -{ - struct ieee80211_hw *dev = usb_get_intfdata(intf); - struct p54u_priv *priv; - - if (!dev) - return -ENODEV; - - priv = dev->priv; - if (unlikely(!(priv->upload_fw && priv->fw))) - return 0; - - return priv->upload_fw(dev); -} - -static int p54u_post_reset(struct usb_interface *intf) -{ - struct ieee80211_hw *dev = usb_get_intfdata(intf); - struct p54u_priv *priv; - int err; - - err = p54u_resume(intf); - if (err) - return err; - - /* reinitialize old device state */ - priv = dev->priv; - if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED) - ieee80211_restart_hw(dev); - - return 0; -} - -#ifdef CONFIG_PM - -static int p54u_suspend(struct usb_interface *intf, pm_message_t message) -{ - return p54u_pre_reset(intf); -} - -#endif /* CONFIG_PM */ - -static struct usb_driver p54u_driver = { - .name = "p54usb", - .id_table = p54u_table, - .probe = p54u_probe, - .disconnect = p54u_disconnect, - .pre_reset = p54u_pre_reset, - .post_reset = p54u_post_reset, -#ifdef CONFIG_PM - .suspend = p54u_suspend, - .resume = p54u_resume, - .reset_resume = p54u_resume, -#endif /* CONFIG_PM */ - .soft_unbind = 1, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(p54u_driver); diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/p54/p54usb.h deleted file mode 100644 index a5f5f0fea3bd..000000000000 --- a/drivers/net/wireless/p54/p54usb.h +++ /dev/null @@ -1,162 +0,0 @@ -#ifndef P54USB_H -#define P54USB_H - -/* - * Defines for USB based mac80211 Prism54 driver - * - * Copyright (c) 2006, Michael Wu - * - * Based on the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* for isl3886 register definitions used on ver 1 devices */ -#include "p54pci.h" -#include - -/* pci */ -#define NET2280_BASE 0x10000000 -#define NET2280_BASE2 0x20000000 - -/* gpio */ -#define P54U_BRG_POWER_UP (1 << GPIO0_DATA) -#define P54U_BRG_POWER_DOWN (1 << GPIO1_DATA) - -/* devinit */ -#define NET2280_CLK_4Mhz (15 << LOCAL_CLOCK_FREQUENCY) -#define NET2280_CLK_30Mhz (2 << LOCAL_CLOCK_FREQUENCY) -#define NET2280_CLK_60Mhz (1 << LOCAL_CLOCK_FREQUENCY) -#define NET2280_CLK_STOP (0 << LOCAL_CLOCK_FREQUENCY) -#define NET2280_PCI_ENABLE (1 << PCI_ENABLE) -#define NET2280_PCI_SOFT_RESET (1 << PCI_SOFT_RESET) - -/* endpoints */ -#define NET2280_CLEAR_NAK_OUT_PACKETS_MODE (1 << CLEAR_NAK_OUT_PACKETS_MODE) -#define NET2280_FIFO_FLUSH (1 << FIFO_FLUSH) - -/* irq */ -#define NET2280_USB_INTERRUPT_ENABLE (1 << USB_INTERRUPT_ENABLE) -#define NET2280_PCI_INTA_INTERRUPT (1 << PCI_INTA_INTERRUPT) -#define NET2280_PCI_INTA_INTERRUPT_ENABLE (1 << PCI_INTA_INTERRUPT_ENABLE) - -/* registers */ -#define NET2280_DEVINIT 0x00 -#define NET2280_USBIRQENB1 0x24 -#define NET2280_IRQSTAT1 0x2c -#define NET2280_FIFOCTL 0x38 -#define NET2280_GPIOCTL 0x50 -#define NET2280_RELNUM 0x88 -#define NET2280_EPA_RSP 0x324 -#define NET2280_EPA_STAT 0x32c -#define NET2280_EPB_STAT 0x34c -#define NET2280_EPC_RSP 0x364 -#define NET2280_EPC_STAT 0x36c -#define NET2280_EPD_STAT 0x38c - -#define NET2280_EPA_CFG 0x320 -#define NET2280_EPB_CFG 0x340 -#define NET2280_EPC_CFG 0x360 -#define NET2280_EPD_CFG 0x380 -#define NET2280_EPE_CFG 0x3A0 -#define NET2280_EPF_CFG 0x3C0 -#define P54U_DEV_BASE 0x40000000 - -struct net2280_tx_hdr { - __le32 device_addr; - __le16 len; - __le16 follower; /* ? */ - u8 padding[8]; -} __packed; - -struct lm87_tx_hdr { - __le32 device_addr; - __le32 chksum; -} __packed; - -/* Some flags for the isl hardware registers controlling DMA inside the - * chip */ -#define ISL38XX_DMA_STATUS_DONE 0x00000001 -#define ISL38XX_DMA_STATUS_READY 0x00000002 -#define NET2280_EPA_FIFO_PCI_ADDR 0x20000000 -#define ISL38XX_DMA_MASTER_CONTROL_TRIGGER 0x00000004 - -enum net2280_op_type { - NET2280_BRG_U32 = 0x001F, - NET2280_BRG_CFG_U32 = 0x000F, - NET2280_BRG_CFG_U16 = 0x0003, - NET2280_DEV_U32 = 0x080F, - NET2280_DEV_CFG_U32 = 0x088F, - NET2280_DEV_CFG_U16 = 0x0883 -}; - -struct net2280_reg_write { - __le16 port; - __le32 addr; - __le32 val; -} __packed; - -struct net2280_reg_read { - __le16 port; - __le32 addr; -} __packed; - -#define P54U_FW_BLOCK 2048 - -#define X2_SIGNATURE "x2 " -#define X2_SIGNATURE_SIZE 4 - -struct x2_header { - u8 signature[X2_SIGNATURE_SIZE]; - __le32 fw_load_addr; - __le32 fw_length; - __le32 crc; -} __packed; - -/* pipes 3 and 4 are not used by the driver */ -#define P54U_PIPE_NUMBER 9 - -enum p54u_pipe_addr { - P54U_PIPE_DATA = 0x01, - P54U_PIPE_MGMT = 0x02, - P54U_PIPE_3 = 0x03, - P54U_PIPE_4 = 0x04, - P54U_PIPE_BRG = 0x0d, - P54U_PIPE_DEV = 0x0e, - P54U_PIPE_INT = 0x0f -}; - -struct p54u_rx_info { - struct urb *urb; - struct ieee80211_hw *dev; -}; - -enum p54u_hw_type { - P54U_INVALID_HW, - P54U_NET2280, - P54U_3887, - - /* keep last */ - __NUM_P54U_HWTYPES, -}; - -struct p54u_priv { - struct p54_common common; - struct usb_device *udev; - struct usb_interface *intf; - int (*upload_fw)(struct ieee80211_hw *dev); - - enum p54u_hw_type hw_type; - spinlock_t lock; - struct sk_buff_head rx_queue; - struct usb_anchor submitted; - const struct firmware *fw; - - /* asynchronous firmware callback */ - struct completion fw_wait_load; -}; - -#endif /* P54USB_H */ diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c deleted file mode 100644 index 24e5ff9a9272..000000000000 --- a/drivers/net/wireless/p54/txrx.c +++ /dev/null @@ -1,940 +0,0 @@ -/* - * Common code for mac80211 Prism54 drivers - * - * Copyright (c) 2006, Michael Wu - * Copyright (c) 2007-2009, Christian Lamparter - * Copyright 2008, Johannes Berg - * - * Based on: - * - the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note , et al. - * - stlc45xx driver - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include - -#include "p54.h" -#include "lmac.h" - -#ifdef P54_MM_DEBUG -static void p54_dump_tx_queue(struct p54_common *priv) -{ - unsigned long flags; - struct ieee80211_tx_info *info; - struct p54_tx_info *range; - struct sk_buff *skb; - struct p54_hdr *hdr; - unsigned int i = 0; - u32 prev_addr; - u32 largest_hole = 0, free; - - spin_lock_irqsave(&priv->tx_queue.lock, flags); - wiphy_debug(priv->hw->wiphy, "/ --- tx queue dump (%d entries) ---\n", - skb_queue_len(&priv->tx_queue)); - - prev_addr = priv->rx_start; - skb_queue_walk(&priv->tx_queue, skb) { - info = IEEE80211_SKB_CB(skb); - range = (void *) info->rate_driver_data; - hdr = (void *) skb->data; - - free = range->start_addr - prev_addr; - wiphy_debug(priv->hw->wiphy, - "| [%02d] => [skb:%p skb_len:0x%04x " - "hdr:{flags:%02x len:%04x req_id:%04x type:%02x} " - "mem:{start:%04x end:%04x, free:%d}]\n", - i++, skb, skb->len, - le16_to_cpu(hdr->flags), le16_to_cpu(hdr->len), - le32_to_cpu(hdr->req_id), le16_to_cpu(hdr->type), - range->start_addr, range->end_addr, free); - - prev_addr = range->end_addr; - largest_hole = max(largest_hole, free); - } - free = priv->rx_end - prev_addr; - largest_hole = max(largest_hole, free); - wiphy_debug(priv->hw->wiphy, - "\\ --- [free: %d], largest free block: %d ---\n", - free, largest_hole); - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); -} -#endif /* P54_MM_DEBUG */ - -/* - * So, the firmware is somewhat stupid and doesn't know what places in its - * memory incoming data should go to. By poking around in the firmware, we - * can find some unused memory to upload our packets to. However, data that we - * want the card to TX needs to stay intact until the card has told us that - * it is done with it. This function finds empty places we can upload to and - * marks allocated areas as reserved if necessary. p54_find_and_unlink_skb or - * p54_free_skb frees allocated areas. - */ -static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb) -{ - struct sk_buff *entry, *target_skb = NULL; - struct ieee80211_tx_info *info; - struct p54_tx_info *range; - struct p54_hdr *data = (void *) skb->data; - unsigned long flags; - u32 last_addr = priv->rx_start; - u32 target_addr = priv->rx_start; - u16 len = priv->headroom + skb->len + priv->tailroom + 3; - - info = IEEE80211_SKB_CB(skb); - range = (void *) info->rate_driver_data; - len = (range->extra_len + len) & ~0x3; - - spin_lock_irqsave(&priv->tx_queue.lock, flags); - if (unlikely(skb_queue_len(&priv->tx_queue) == 32)) { - /* - * The tx_queue is now really full. - * - * TODO: check if the device has crashed and reset it. - */ - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - return -EBUSY; - } - - skb_queue_walk(&priv->tx_queue, entry) { - u32 hole_size; - info = IEEE80211_SKB_CB(entry); - range = (void *) info->rate_driver_data; - hole_size = range->start_addr - last_addr; - - if (!target_skb && hole_size >= len) { - target_skb = entry->prev; - hole_size -= len; - target_addr = last_addr; - break; - } - last_addr = range->end_addr; - } - if (unlikely(!target_skb)) { - if (priv->rx_end - last_addr >= len) { - target_skb = priv->tx_queue.prev; - if (!skb_queue_empty(&priv->tx_queue)) { - info = IEEE80211_SKB_CB(target_skb); - range = (void *)info->rate_driver_data; - target_addr = range->end_addr; - } - } else { - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - return -ENOSPC; - } - } - - info = IEEE80211_SKB_CB(skb); - range = (void *) info->rate_driver_data; - range->start_addr = target_addr; - range->end_addr = target_addr + len; - data->req_id = cpu_to_le32(target_addr + priv->headroom); - if (IS_DATA_FRAME(skb) && - unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) - priv->beacon_req_id = data->req_id; - - __skb_queue_after(&priv->tx_queue, target_skb, skb); - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - return 0; -} - -static void p54_tx_pending(struct p54_common *priv) -{ - struct sk_buff *skb; - int ret; - - skb = skb_dequeue(&priv->tx_pending); - if (unlikely(!skb)) - return ; - - ret = p54_assign_address(priv, skb); - if (unlikely(ret)) - skb_queue_head(&priv->tx_pending, skb); - else - priv->tx(priv->hw, skb); -} - -static void p54_wake_queues(struct p54_common *priv) -{ - unsigned long flags; - unsigned int i; - - if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) - return ; - - p54_tx_pending(priv); - - spin_lock_irqsave(&priv->tx_stats_lock, flags); - for (i = 0; i < priv->hw->queues; i++) { - if (priv->tx_stats[i + P54_QUEUE_DATA].len < - priv->tx_stats[i + P54_QUEUE_DATA].limit) - ieee80211_wake_queue(priv->hw, i); - } - spin_unlock_irqrestore(&priv->tx_stats_lock, flags); -} - -static int p54_tx_qos_accounting_alloc(struct p54_common *priv, - struct sk_buff *skb, - const u16 p54_queue) -{ - struct p54_tx_queue_stats *queue; - unsigned long flags; - - if (WARN_ON(p54_queue >= P54_QUEUE_NUM)) - return -EINVAL; - - queue = &priv->tx_stats[p54_queue]; - - spin_lock_irqsave(&priv->tx_stats_lock, flags); - if (unlikely(queue->len >= queue->limit && IS_QOS_QUEUE(p54_queue))) { - spin_unlock_irqrestore(&priv->tx_stats_lock, flags); - return -ENOSPC; - } - - queue->len++; - queue->count++; - - if (unlikely(queue->len == queue->limit && IS_QOS_QUEUE(p54_queue))) { - u16 ac_queue = p54_queue - P54_QUEUE_DATA; - ieee80211_stop_queue(priv->hw, ac_queue); - } - - spin_unlock_irqrestore(&priv->tx_stats_lock, flags); - return 0; -} - -static void p54_tx_qos_accounting_free(struct p54_common *priv, - struct sk_buff *skb) -{ - if (IS_DATA_FRAME(skb)) { - unsigned long flags; - - spin_lock_irqsave(&priv->tx_stats_lock, flags); - priv->tx_stats[GET_HW_QUEUE(skb)].len--; - spin_unlock_irqrestore(&priv->tx_stats_lock, flags); - - if (unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) { - if (priv->beacon_req_id == GET_REQ_ID(skb)) { - /* this is the active beacon set anymore */ - priv->beacon_req_id = 0; - } - complete(&priv->beacon_comp); - } - } - p54_wake_queues(priv); -} - -void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct p54_common *priv = dev->priv; - if (unlikely(!skb)) - return ; - - skb_unlink(skb, &priv->tx_queue); - p54_tx_qos_accounting_free(priv, skb); - ieee80211_free_txskb(dev, skb); -} -EXPORT_SYMBOL_GPL(p54_free_skb); - -static struct sk_buff *p54_find_and_unlink_skb(struct p54_common *priv, - const __le32 req_id) -{ - struct sk_buff *entry; - unsigned long flags; - - spin_lock_irqsave(&priv->tx_queue.lock, flags); - skb_queue_walk(&priv->tx_queue, entry) { - struct p54_hdr *hdr = (struct p54_hdr *) entry->data; - - if (hdr->req_id == req_id) { - __skb_unlink(entry, &priv->tx_queue); - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - p54_tx_qos_accounting_free(priv, entry); - return entry; - } - } - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - return NULL; -} - -void p54_tx(struct p54_common *priv, struct sk_buff *skb) -{ - skb_queue_tail(&priv->tx_pending, skb); - p54_tx_pending(priv); -} - -static int p54_rssi_to_dbm(struct p54_common *priv, int rssi) -{ - if (priv->rxhw != 5) { - return ((rssi * priv->cur_rssi->mul) / 64 + - priv->cur_rssi->add) / 4; - } else { - /* - * TODO: find the correct formula - */ - return rssi / 2 - 110; - } -} - -/* - * Even if the firmware is capable of dealing with incoming traffic, - * while dozing, we have to prepared in case mac80211 uses PS-POLL - * to retrieve outstanding frames from our AP. - * (see comment in net/mac80211/mlme.c @ line 1993) - */ -static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (void *) skb->data; - struct ieee80211_tim_ie *tim_ie; - u8 *tim; - u8 tim_len; - bool new_psm; - - /* only beacons have a TIM IE */ - if (!ieee80211_is_beacon(hdr->frame_control)) - return; - - if (!priv->aid) - return; - - /* only consider beacons from the associated BSSID */ - if (!ether_addr_equal_64bits(hdr->addr3, priv->bssid)) - return; - - tim = p54_find_ie(skb, WLAN_EID_TIM); - if (!tim) - return; - - tim_len = tim[1]; - tim_ie = (struct ieee80211_tim_ie *) &tim[2]; - - new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid); - if (new_psm != priv->powersave_override) { - priv->powersave_override = new_psm; - p54_set_ps(priv); - } -} - -static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb) -{ - struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data; - struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); - u16 freq = le16_to_cpu(hdr->freq); - size_t header_len = sizeof(*hdr); - u32 tsf32; - u8 rate = hdr->rate & 0xf; - - /* - * If the device is in a unspecified state we have to - * ignore all data frames. Else we could end up with a - * nasty crash. - */ - if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) - return 0; - - if (!(hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_IN_FCS_GOOD))) - return 0; - - if (hdr->decrypt_status == P54_DECRYPT_OK) - rx_status->flag |= RX_FLAG_DECRYPTED; - if ((hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL) || - (hdr->decrypt_status == P54_DECRYPT_FAIL_TKIP)) - rx_status->flag |= RX_FLAG_MMIC_ERROR; - - rx_status->signal = p54_rssi_to_dbm(priv, hdr->rssi); - if (hdr->rate & 0x10) - rx_status->flag |= RX_FLAG_SHORTPRE; - if (priv->hw->conf.chandef.chan->band == IEEE80211_BAND_5GHZ) - rx_status->rate_idx = (rate < 4) ? 0 : rate - 4; - else - rx_status->rate_idx = rate; - - rx_status->freq = freq; - rx_status->band = priv->hw->conf.chandef.chan->band; - rx_status->antenna = hdr->antenna; - - tsf32 = le32_to_cpu(hdr->tsf32); - if (tsf32 < priv->tsf_low32) - priv->tsf_high32++; - rx_status->mactime = ((u64)priv->tsf_high32) << 32 | tsf32; - priv->tsf_low32 = tsf32; - - /* LMAC API Page 10/29 - s_lm_data_in - clock - * "usec accurate timestamp of hardware clock - * at end of frame (before OFDM SIFS EOF padding" - */ - rx_status->flag |= RX_FLAG_MACTIME_END; - - if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) - header_len += hdr->align[0]; - - skb_pull(skb, header_len); - skb_trim(skb, le16_to_cpu(hdr->len)); - if (unlikely(priv->hw->conf.flags & IEEE80211_CONF_PS)) - p54_pspoll_workaround(priv, skb); - - ieee80211_rx_irqsafe(priv->hw, skb); - - ieee80211_queue_delayed_work(priv->hw, &priv->work, - msecs_to_jiffies(P54_STATISTICS_UPDATE)); - - return -1; -} - -static void p54_rx_frame_sent(struct p54_common *priv, struct sk_buff *skb) -{ - struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - struct p54_frame_sent *payload = (struct p54_frame_sent *) hdr->data; - struct ieee80211_tx_info *info; - struct p54_hdr *entry_hdr; - struct p54_tx_data *entry_data; - struct sk_buff *entry; - unsigned int pad = 0, frame_len; - int count, idx; - - entry = p54_find_and_unlink_skb(priv, hdr->req_id); - if (unlikely(!entry)) - return ; - - frame_len = entry->len; - info = IEEE80211_SKB_CB(entry); - entry_hdr = (struct p54_hdr *) entry->data; - entry_data = (struct p54_tx_data *) entry_hdr->data; - priv->stats.dot11ACKFailureCount += payload->tries - 1; - - /* - * Frames in P54_QUEUE_FWSCAN and P54_QUEUE_BEACON are - * generated by the driver. Therefore tx_status is bogus - * and we don't want to confuse the mac80211 stack. - */ - if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) { - dev_kfree_skb_any(entry); - return ; - } - - /* - * Clear manually, ieee80211_tx_info_clear_status would - * clear the counts too and we need them. - */ - memset(&info->status.ack_signal, 0, - sizeof(struct ieee80211_tx_info) - - offsetof(struct ieee80211_tx_info, status.ack_signal)); - BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, - status.ack_signal) != 20); - - if (entry_hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) - pad = entry_data->align[0]; - - /* walk through the rates array and adjust the counts */ - count = payload->tries; - for (idx = 0; idx < 4; idx++) { - if (count >= info->status.rates[idx].count) { - count -= info->status.rates[idx].count; - } else if (count > 0) { - info->status.rates[idx].count = count; - count = 0; - } else { - info->status.rates[idx].idx = -1; - info->status.rates[idx].count = 0; - } - } - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && - !(payload->status & P54_TX_FAILED)) - info->flags |= IEEE80211_TX_STAT_ACK; - if (payload->status & P54_TX_PSM_CANCELLED) - info->flags |= IEEE80211_TX_STAT_TX_FILTERED; - info->status.ack_signal = p54_rssi_to_dbm(priv, - (int)payload->ack_rssi); - - /* Undo all changes to the frame. */ - switch (entry_data->key_type) { - case P54_CRYPTO_TKIPMICHAEL: { - u8 *iv = (u8 *)(entry_data->align + pad + - entry_data->crypt_offset); - - /* Restore the original TKIP IV. */ - iv[2] = iv[0]; - iv[0] = iv[1]; - iv[1] = (iv[0] | 0x20) & 0x7f; /* WEPSeed - 8.3.2.2 */ - - frame_len -= 12; /* remove TKIP_MMIC + TKIP_ICV */ - break; - } - case P54_CRYPTO_AESCCMP: - frame_len -= 8; /* remove CCMP_MIC */ - break; - case P54_CRYPTO_WEP: - frame_len -= 4; /* remove WEP_ICV */ - break; - } - - skb_trim(entry, frame_len); - skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data)); - ieee80211_tx_status_irqsafe(priv->hw, entry); -} - -static void p54_rx_eeprom_readback(struct p54_common *priv, - struct sk_buff *skb) -{ - struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - struct p54_eeprom_lm86 *eeprom = (struct p54_eeprom_lm86 *) hdr->data; - struct sk_buff *tmp; - - if (!priv->eeprom) - return ; - - if (priv->fw_var >= 0x509) { - memcpy(priv->eeprom, eeprom->v2.data, - le16_to_cpu(eeprom->v2.len)); - } else { - memcpy(priv->eeprom, eeprom->v1.data, - le16_to_cpu(eeprom->v1.len)); - } - - priv->eeprom = NULL; - tmp = p54_find_and_unlink_skb(priv, hdr->req_id); - dev_kfree_skb_any(tmp); - complete(&priv->eeprom_comp); -} - -static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb) -{ - struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - struct p54_statistics *stats = (struct p54_statistics *) hdr->data; - struct sk_buff *tmp; - struct ieee80211_channel *chan; - unsigned int i, rssi, tx, cca, dtime, dtotal, dcca, dtx, drssi, unit; - u32 tsf32; - - if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) - return ; - - tsf32 = le32_to_cpu(stats->tsf32); - if (tsf32 < priv->tsf_low32) - priv->tsf_high32++; - priv->tsf_low32 = tsf32; - - priv->stats.dot11RTSFailureCount = le32_to_cpu(stats->rts_fail); - priv->stats.dot11RTSSuccessCount = le32_to_cpu(stats->rts_success); - priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs); - - priv->noise = p54_rssi_to_dbm(priv, le32_to_cpu(stats->noise)); - - /* - * STSW450X LMAC API page 26 - 3.8 Statistics - * "The exact measurement period can be derived from the - * timestamp member". - */ - dtime = tsf32 - priv->survey_raw.timestamp; - - /* - * STSW450X LMAC API page 26 - 3.8.1 Noise histogram - * The LMAC samples RSSI, CCA and transmit state at regular - * periods (typically 8 times per 1k [as in 1024] usec). - */ - cca = le32_to_cpu(stats->sample_cca); - tx = le32_to_cpu(stats->sample_tx); - rssi = 0; - for (i = 0; i < ARRAY_SIZE(stats->sample_noise); i++) - rssi += le32_to_cpu(stats->sample_noise[i]); - - dcca = cca - priv->survey_raw.cached_cca; - drssi = rssi - priv->survey_raw.cached_rssi; - dtx = tx - priv->survey_raw.cached_tx; - dtotal = dcca + drssi + dtx; - - /* - * update statistics when more than a second is over since the - * last call, or when a update is badly needed. - */ - if (dtotal && (priv->update_stats || dtime >= USEC_PER_SEC) && - dtime >= dtotal) { - priv->survey_raw.timestamp = tsf32; - priv->update_stats = false; - unit = dtime / dtotal; - - if (dcca) { - priv->survey_raw.cca += dcca * unit; - priv->survey_raw.cached_cca = cca; - } - if (dtx) { - priv->survey_raw.tx += dtx * unit; - priv->survey_raw.cached_tx = tx; - } - if (drssi) { - priv->survey_raw.rssi += drssi * unit; - priv->survey_raw.cached_rssi = rssi; - } - - /* 1024 usec / 8 times = 128 usec / time */ - if (!(priv->phy_ps || priv->phy_idle)) - priv->survey_raw.active += dtotal * unit; - else - priv->survey_raw.active += (dcca + dtx) * unit; - } - - chan = priv->curchan; - if (chan) { - struct survey_info *survey = &priv->survey[chan->hw_value]; - survey->noise = clamp(priv->noise, -128, 127); - survey->time = priv->survey_raw.active; - survey->time_tx = priv->survey_raw.tx; - survey->time_busy = priv->survey_raw.tx + - priv->survey_raw.cca; - do_div(survey->time, 1024); - do_div(survey->time_tx, 1024); - do_div(survey->time_busy, 1024); - } - - tmp = p54_find_and_unlink_skb(priv, hdr->req_id); - dev_kfree_skb_any(tmp); - complete(&priv->stat_comp); -} - -static void p54_rx_trap(struct p54_common *priv, struct sk_buff *skb) -{ - struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - struct p54_trap *trap = (struct p54_trap *) hdr->data; - u16 event = le16_to_cpu(trap->event); - u16 freq = le16_to_cpu(trap->frequency); - - switch (event) { - case P54_TRAP_BEACON_TX: - break; - case P54_TRAP_RADAR: - wiphy_info(priv->hw->wiphy, "radar (freq:%d MHz)\n", freq); - break; - case P54_TRAP_NO_BEACON: - if (priv->vif) - ieee80211_beacon_loss(priv->vif); - break; - case P54_TRAP_SCAN: - break; - case P54_TRAP_TBTT: - break; - case P54_TRAP_TIMER: - break; - case P54_TRAP_FAA_RADIO_OFF: - wiphy_rfkill_set_hw_state(priv->hw->wiphy, true); - break; - case P54_TRAP_FAA_RADIO_ON: - wiphy_rfkill_set_hw_state(priv->hw->wiphy, false); - break; - default: - wiphy_info(priv->hw->wiphy, "received event:%x freq:%d\n", - event, freq); - break; - } -} - -static int p54_rx_control(struct p54_common *priv, struct sk_buff *skb) -{ - struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - - switch (le16_to_cpu(hdr->type)) { - case P54_CONTROL_TYPE_TXDONE: - p54_rx_frame_sent(priv, skb); - break; - case P54_CONTROL_TYPE_TRAP: - p54_rx_trap(priv, skb); - break; - case P54_CONTROL_TYPE_BBP: - break; - case P54_CONTROL_TYPE_STAT_READBACK: - p54_rx_stats(priv, skb); - break; - case P54_CONTROL_TYPE_EEPROM_READBACK: - p54_rx_eeprom_readback(priv, skb); - break; - default: - wiphy_debug(priv->hw->wiphy, - "not handling 0x%02x type control frame\n", - le16_to_cpu(hdr->type)); - break; - } - return 0; -} - -/* returns zero if skb can be reused */ -int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct p54_common *priv = dev->priv; - u16 type = le16_to_cpu(*((__le16 *)skb->data)); - - if (type & P54_HDR_FLAG_CONTROL) - return p54_rx_control(priv, skb); - else - return p54_rx_data(priv, skb); -} -EXPORT_SYMBOL_GPL(p54_rx); - -static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, - u8 *queue, u32 *extra_len, u16 *flags, u16 *aid, - bool *burst_possible) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - - if (ieee80211_is_data_qos(hdr->frame_control)) - *burst_possible = true; - else - *burst_possible = false; - - if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) - *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR; - - if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER) - *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; - - if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) - *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; - - *queue = skb_get_queue_mapping(skb) + P54_QUEUE_DATA; - - switch (priv->mode) { - case NL80211_IFTYPE_MONITOR: - /* - * We have to set P54_HDR_FLAG_DATA_OUT_PROMISC for - * every frame in promiscuous/monitor mode. - * see STSW45x0C LMAC API - page 12. - */ - *aid = 0; - *flags |= P54_HDR_FLAG_DATA_OUT_PROMISC; - break; - case NL80211_IFTYPE_STATION: - *aid = 1; - break; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { - *aid = 0; - *queue = P54_QUEUE_CAB; - return; - } - - if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) { - if (ieee80211_is_probe_resp(hdr->frame_control)) { - *aid = 0; - *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP | - P54_HDR_FLAG_DATA_OUT_NOCANCEL; - return; - } else if (ieee80211_is_beacon(hdr->frame_control)) { - *aid = 0; - - if (info->flags & IEEE80211_TX_CTL_INJECTED) { - /* - * Injecting beacons on top of a AP is - * not a good idea... nevertheless, - * it should be doable. - */ - - return; - } - - *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP; - *queue = P54_QUEUE_BEACON; - *extra_len = IEEE80211_MAX_TIM_LEN; - return; - } - } - - if (sta) - *aid = sta->aid; - break; - } -} - -static u8 p54_convert_algo(u32 cipher) -{ - switch (cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - return P54_CRYPTO_WEP; - case WLAN_CIPHER_SUITE_TKIP: - return P54_CRYPTO_TKIPMICHAEL; - case WLAN_CIPHER_SUITE_CCMP: - return P54_CRYPTO_AESCCMP; - default: - return 0; - } -} - -void p54_tx_80211(struct ieee80211_hw *dev, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct p54_common *priv = dev->priv; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct p54_tx_info *p54info; - struct p54_hdr *hdr; - struct p54_tx_data *txhdr; - unsigned int padding, len, extra_len = 0; - int i, j, ridx; - u16 hdr_flags = 0, aid = 0; - u8 rate, queue = 0, crypt_offset = 0; - u8 cts_rate = 0x20; - u8 rc_flags; - u8 calculated_tries[4]; - u8 nrates = 0, nremaining = 8; - bool burst_allowed = false; - - p54_tx_80211_header(priv, skb, info, control->sta, &queue, &extra_len, - &hdr_flags, &aid, &burst_allowed); - - if (p54_tx_qos_accounting_alloc(priv, skb, queue)) { - ieee80211_free_txskb(dev, skb); - return; - } - - padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3; - len = skb->len; - - if (info->control.hw_key) { - crypt_offset = ieee80211_get_hdrlen_from_skb(skb); - if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { - u8 *iv = (u8 *)(skb->data + crypt_offset); - /* - * The firmware excepts that the IV has to have - * this special format - */ - iv[1] = iv[0]; - iv[0] = iv[2]; - iv[2] = 0; - } - } - - txhdr = (struct p54_tx_data *) skb_push(skb, sizeof(*txhdr) + padding); - hdr = (struct p54_hdr *) skb_push(skb, sizeof(*hdr)); - - if (padding) - hdr_flags |= P54_HDR_FLAG_DATA_ALIGN; - hdr->type = cpu_to_le16(aid); - hdr->rts_tries = info->control.rates[0].count; - - /* - * we register the rates in perfect order, and - * RTS/CTS won't happen on 5 GHz - */ - cts_rate = info->control.rts_cts_rate_idx; - - memset(&txhdr->rateset, 0, sizeof(txhdr->rateset)); - - /* see how many rates got used */ - for (i = 0; i < dev->max_rates; i++) { - if (info->control.rates[i].idx < 0) - break; - nrates++; - } - - /* limit tries to 8/nrates per rate */ - for (i = 0; i < nrates; i++) { - /* - * The magic expression here is equivalent to 8/nrates for - * all values that matter, but avoids division and jumps. - * Note that nrates can only take the values 1 through 4. - */ - calculated_tries[i] = min_t(int, ((15 >> nrates) | 1) + 1, - info->control.rates[i].count); - nremaining -= calculated_tries[i]; - } - - /* if there are tries left, distribute from back to front */ - for (i = nrates - 1; nremaining > 0 && i >= 0; i--) { - int tmp = info->control.rates[i].count - calculated_tries[i]; - - if (tmp <= 0) - continue; - /* RC requested more tries at this rate */ - - tmp = min_t(int, tmp, nremaining); - calculated_tries[i] += tmp; - nremaining -= tmp; - } - - ridx = 0; - for (i = 0; i < nrates && ridx < 8; i++) { - /* we register the rates in perfect order */ - rate = info->control.rates[i].idx; - if (info->band == IEEE80211_BAND_5GHZ) - rate += 4; - - /* store the count we actually calculated for TX status */ - info->control.rates[i].count = calculated_tries[i]; - - rc_flags = info->control.rates[i].flags; - if (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) { - rate |= 0x10; - cts_rate |= 0x10; - } - if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { - burst_allowed = false; - rate |= 0x40; - } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - rate |= 0x20; - burst_allowed = false; - } - for (j = 0; j < calculated_tries[i] && ridx < 8; j++) { - txhdr->rateset[ridx] = rate; - ridx++; - } - } - - if (burst_allowed) - hdr_flags |= P54_HDR_FLAG_DATA_OUT_BURST; - - /* TODO: enable bursting */ - hdr->flags = cpu_to_le16(hdr_flags); - hdr->tries = ridx; - txhdr->rts_rate_idx = 0; - if (info->control.hw_key) { - txhdr->key_type = p54_convert_algo(info->control.hw_key->cipher); - txhdr->key_len = min((u8)16, info->control.hw_key->keylen); - memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len); - if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { - /* reserve space for the MIC key */ - len += 8; - memcpy(skb_put(skb, 8), &(info->control.hw_key->key - [NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]), 8); - } - /* reserve some space for ICV */ - len += info->control.hw_key->icv_len; - memset(skb_put(skb, info->control.hw_key->icv_len), 0, - info->control.hw_key->icv_len); - } else { - txhdr->key_type = 0; - txhdr->key_len = 0; - } - txhdr->crypt_offset = crypt_offset; - txhdr->hw_queue = queue; - txhdr->backlog = priv->tx_stats[queue].len - 1; - memset(txhdr->durations, 0, sizeof(txhdr->durations)); - txhdr->tx_antenna = 2 & priv->tx_diversity_mask; - if (priv->rxhw == 5) { - txhdr->longbow.cts_rate = cts_rate; - txhdr->longbow.output_power = cpu_to_le16(priv->output_power); - } else { - txhdr->normal.output_power = priv->output_power; - txhdr->normal.cts_rate = cts_rate; - } - if (padding) - txhdr->align[0] = padding; - - hdr->len = cpu_to_le16(len); - /* modifies skb->cb and with it info, so must be last! */ - p54info = (void *) info->rate_driver_data; - p54info->extra_len = extra_len; - - p54_tx(priv, skb); -} -- cgit v1.2.3-59-g8ed1b From 2be45b66dee080326d0f240aa4f18ef932cc3deb Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 09:57:18 +0200 Subject: orinoco: move under intersil vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 1 - drivers/net/wireless/Makefile | 2 - drivers/net/wireless/intersil/Kconfig | 1 + drivers/net/wireless/intersil/Makefile | 1 + drivers/net/wireless/intersil/orinoco/Kconfig | 142 ++ drivers/net/wireless/intersil/orinoco/Makefile | 17 + drivers/net/wireless/intersil/orinoco/airport.c | 267 +++ drivers/net/wireless/intersil/orinoco/cfg.c | 291 +++ drivers/net/wireless/intersil/orinoco/cfg.h | 15 + drivers/net/wireless/intersil/orinoco/fw.c | 387 ++++ drivers/net/wireless/intersil/orinoco/fw.h | 21 + drivers/net/wireless/intersil/orinoco/hermes.c | 777 +++++++ drivers/net/wireless/intersil/orinoco/hermes.h | 520 +++++ drivers/net/wireless/intersil/orinoco/hermes_dld.c | 477 ++++ drivers/net/wireless/intersil/orinoco/hermes_dld.h | 52 + drivers/net/wireless/intersil/orinoco/hermes_rid.h | 165 ++ drivers/net/wireless/intersil/orinoco/hw.c | 1356 +++++++++++ drivers/net/wireless/intersil/orinoco/hw.h | 59 + drivers/net/wireless/intersil/orinoco/main.c | 2429 ++++++++++++++++++++ drivers/net/wireless/intersil/orinoco/main.h | 50 + drivers/net/wireless/intersil/orinoco/mic.c | 79 + drivers/net/wireless/intersil/orinoco/mic.h | 22 + drivers/net/wireless/intersil/orinoco/orinoco.h | 253 ++ drivers/net/wireless/intersil/orinoco/orinoco_cs.c | 341 +++ .../net/wireless/intersil/orinoco/orinoco_nortel.c | 323 +++ .../net/wireless/intersil/orinoco/orinoco_pci.c | 266 +++ .../net/wireless/intersil/orinoco/orinoco_pci.h | 68 + .../net/wireless/intersil/orinoco/orinoco_plx.c | 371 +++ .../net/wireless/intersil/orinoco/orinoco_tmd.c | 246 ++ .../net/wireless/intersil/orinoco/orinoco_usb.c | 1748 ++++++++++++++ drivers/net/wireless/intersil/orinoco/scan.c | 251 ++ drivers/net/wireless/intersil/orinoco/scan.h | 21 + .../net/wireless/intersil/orinoco/spectrum_cs.c | 320 +++ drivers/net/wireless/intersil/orinoco/wext.c | 1413 ++++++++++++ drivers/net/wireless/intersil/orinoco/wext.h | 13 + drivers/net/wireless/orinoco/Kconfig | 142 -- drivers/net/wireless/orinoco/Makefile | 17 - drivers/net/wireless/orinoco/airport.c | 267 --- drivers/net/wireless/orinoco/cfg.c | 291 --- drivers/net/wireless/orinoco/cfg.h | 15 - drivers/net/wireless/orinoco/fw.c | 387 ---- drivers/net/wireless/orinoco/fw.h | 21 - drivers/net/wireless/orinoco/hermes.c | 777 ------- drivers/net/wireless/orinoco/hermes.h | 520 ----- drivers/net/wireless/orinoco/hermes_dld.c | 477 ---- drivers/net/wireless/orinoco/hermes_dld.h | 52 - drivers/net/wireless/orinoco/hermes_rid.h | 165 -- drivers/net/wireless/orinoco/hw.c | 1356 ----------- drivers/net/wireless/orinoco/hw.h | 59 - drivers/net/wireless/orinoco/main.c | 2429 -------------------- drivers/net/wireless/orinoco/main.h | 50 - drivers/net/wireless/orinoco/mic.c | 79 - drivers/net/wireless/orinoco/mic.h | 22 - drivers/net/wireless/orinoco/orinoco.h | 253 -- drivers/net/wireless/orinoco/orinoco_cs.c | 341 --- drivers/net/wireless/orinoco/orinoco_nortel.c | 323 --- drivers/net/wireless/orinoco/orinoco_pci.c | 266 --- drivers/net/wireless/orinoco/orinoco_pci.h | 68 - drivers/net/wireless/orinoco/orinoco_plx.c | 371 --- drivers/net/wireless/orinoco/orinoco_tmd.c | 246 -- drivers/net/wireless/orinoco/orinoco_usb.c | 1748 -------------- drivers/net/wireless/orinoco/scan.c | 251 -- drivers/net/wireless/orinoco/scan.h | 21 - drivers/net/wireless/orinoco/spectrum_cs.c | 320 --- drivers/net/wireless/orinoco/wext.c | 1413 ------------ drivers/net/wireless/orinoco/wext.h | 13 - 67 files changed, 12763 insertions(+), 12764 deletions(-) create mode 100644 drivers/net/wireless/intersil/orinoco/Kconfig create mode 100644 drivers/net/wireless/intersil/orinoco/Makefile create mode 100644 drivers/net/wireless/intersil/orinoco/airport.c create mode 100644 drivers/net/wireless/intersil/orinoco/cfg.c create mode 100644 drivers/net/wireless/intersil/orinoco/cfg.h create mode 100644 drivers/net/wireless/intersil/orinoco/fw.c create mode 100644 drivers/net/wireless/intersil/orinoco/fw.h create mode 100644 drivers/net/wireless/intersil/orinoco/hermes.c create mode 100644 drivers/net/wireless/intersil/orinoco/hermes.h create mode 100644 drivers/net/wireless/intersil/orinoco/hermes_dld.c create mode 100644 drivers/net/wireless/intersil/orinoco/hermes_dld.h create mode 100644 drivers/net/wireless/intersil/orinoco/hermes_rid.h create mode 100644 drivers/net/wireless/intersil/orinoco/hw.c create mode 100644 drivers/net/wireless/intersil/orinoco/hw.h create mode 100644 drivers/net/wireless/intersil/orinoco/main.c create mode 100644 drivers/net/wireless/intersil/orinoco/main.h create mode 100644 drivers/net/wireless/intersil/orinoco/mic.c create mode 100644 drivers/net/wireless/intersil/orinoco/mic.h create mode 100644 drivers/net/wireless/intersil/orinoco/orinoco.h create mode 100644 drivers/net/wireless/intersil/orinoco/orinoco_cs.c create mode 100644 drivers/net/wireless/intersil/orinoco/orinoco_nortel.c create mode 100644 drivers/net/wireless/intersil/orinoco/orinoco_pci.c create mode 100644 drivers/net/wireless/intersil/orinoco/orinoco_pci.h create mode 100644 drivers/net/wireless/intersil/orinoco/orinoco_plx.c create mode 100644 drivers/net/wireless/intersil/orinoco/orinoco_tmd.c create mode 100644 drivers/net/wireless/intersil/orinoco/orinoco_usb.c create mode 100644 drivers/net/wireless/intersil/orinoco/scan.c create mode 100644 drivers/net/wireless/intersil/orinoco/scan.h create mode 100644 drivers/net/wireless/intersil/orinoco/spectrum_cs.c create mode 100644 drivers/net/wireless/intersil/orinoco/wext.c create mode 100644 drivers/net/wireless/intersil/orinoco/wext.h delete mode 100644 drivers/net/wireless/orinoco/Kconfig delete mode 100644 drivers/net/wireless/orinoco/Makefile delete mode 100644 drivers/net/wireless/orinoco/airport.c delete mode 100644 drivers/net/wireless/orinoco/cfg.c delete mode 100644 drivers/net/wireless/orinoco/cfg.h delete mode 100644 drivers/net/wireless/orinoco/fw.c delete mode 100644 drivers/net/wireless/orinoco/fw.h delete mode 100644 drivers/net/wireless/orinoco/hermes.c delete mode 100644 drivers/net/wireless/orinoco/hermes.h delete mode 100644 drivers/net/wireless/orinoco/hermes_dld.c delete mode 100644 drivers/net/wireless/orinoco/hermes_dld.h delete mode 100644 drivers/net/wireless/orinoco/hermes_rid.h delete mode 100644 drivers/net/wireless/orinoco/hw.c delete mode 100644 drivers/net/wireless/orinoco/hw.h delete mode 100644 drivers/net/wireless/orinoco/main.c delete mode 100644 drivers/net/wireless/orinoco/main.h delete mode 100644 drivers/net/wireless/orinoco/mic.c delete mode 100644 drivers/net/wireless/orinoco/mic.h delete mode 100644 drivers/net/wireless/orinoco/orinoco.h delete mode 100644 drivers/net/wireless/orinoco/orinoco_cs.c delete mode 100644 drivers/net/wireless/orinoco/orinoco_nortel.c delete mode 100644 drivers/net/wireless/orinoco/orinoco_pci.c delete mode 100644 drivers/net/wireless/orinoco/orinoco_pci.h delete mode 100644 drivers/net/wireless/orinoco/orinoco_plx.c delete mode 100644 drivers/net/wireless/orinoco/orinoco_tmd.c delete mode 100644 drivers/net/wireless/orinoco/orinoco_usb.c delete mode 100644 drivers/net/wireless/orinoco/scan.c delete mode 100644 drivers/net/wireless/orinoco/scan.h delete mode 100644 drivers/net/wireless/orinoco/spectrum_cs.c delete mode 100644 drivers/net/wireless/orinoco/wext.c delete mode 100644 drivers/net/wireless/orinoco/wext.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 818899a4c268..53c15af0d967 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7930,7 +7930,7 @@ L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/en/users/Drivers/orinoco W: http://www.nongnu.org/orinoco/ S: Orphan -F: drivers/net/wireless/orinoco/ +F: drivers/net/wireless/intersil/orinoco/ OSD LIBRARY and FILESYSTEM M: Boaz Harrosh diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index bea2fbb4a238..cc1f168603dc 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -117,7 +117,6 @@ config MAC80211_HWSIM called mac80211_hwsim. If unsure, say N. source "drivers/net/wireless/ath/Kconfig" -source "drivers/net/wireless/orinoco/Kconfig" source "drivers/net/wireless/rt2x00/Kconfig" source "drivers/net/wireless/mediatek/Kconfig" source "drivers/net/wireless/realtek/rtlwifi/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 046963fc3958..dd6d17cc4b7e 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -12,8 +12,6 @@ obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ -obj-$(CONFIG_HERMES) += orinoco/ - obj-$(CONFIG_PRISM54) += prism54/ obj-$(CONFIG_WLAN) += realtek/ diff --git a/drivers/net/wireless/intersil/Kconfig b/drivers/net/wireless/intersil/Kconfig index 4bc3688bfa93..2b056b6daef8 100644 --- a/drivers/net/wireless/intersil/Kconfig +++ b/drivers/net/wireless/intersil/Kconfig @@ -12,6 +12,7 @@ config WLAN_VENDOR_INTERSIL if WLAN_VENDOR_INTERSIL source "drivers/net/wireless/intersil/hostap/Kconfig" +source "drivers/net/wireless/intersil/orinoco/Kconfig" source "drivers/net/wireless/intersil/p54/Kconfig" endif # WLAN_VENDOR_INTERSIL diff --git a/drivers/net/wireless/intersil/Makefile b/drivers/net/wireless/intersil/Makefile index 90a72830b1ae..aedb713da746 100644 --- a/drivers/net/wireless/intersil/Makefile +++ b/drivers/net/wireless/intersil/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_HOSTAP) += hostap/ +obj-$(CONFIG_HERMES) += orinoco/ obj-$(CONFIG_P54_COMMON) += p54/ diff --git a/drivers/net/wireless/intersil/orinoco/Kconfig b/drivers/net/wireless/intersil/orinoco/Kconfig new file mode 100644 index 000000000000..f6fa3f4e294f --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/Kconfig @@ -0,0 +1,142 @@ +config HERMES + tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" + depends on (PPC_PMAC || PCI || PCMCIA) + depends on CFG80211 + select CFG80211_WEXT_EXPORT + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + select FW_LOADER + select CRYPTO + select CRYPTO_MICHAEL_MIC + ---help--- + A driver for 802.11b wireless cards based on the "Hermes" or + Intersil HFA384x (Prism 2) MAC controller. This includes the vast + majority of the PCMCIA 802.11b cards (which are nearly all rebadges) + - except for the Cisco/Aironet cards. Cards supported include the + Apple Airport (not a PCMCIA card), WavelanIEEE/Orinoco, + Cabletron/EnteraSys Roamabout, ELSA AirLancer, MELCO Buffalo, Avaya, + IBM High Rate Wireless, Farralon Syyline, Samsung MagicLAN, Netgear + MA401, LinkSys WPC-11, D-Link DWL-650, 3Com AirConnect, Intel + IPW2011, and Symbol Spectrum24 High Rate amongst others. + + This option includes the guts of the driver, but in order to + actually use a card you will also need to enable support for PCMCIA + Hermes cards, PLX9052 based PCI adaptors or the Apple Airport below. + + You will also very likely also need the Wireless Tools in order to + configure your card and that /etc/pcmcia/wireless.opts works : + + +config HERMES_PRISM + bool "Support Prism 2/2.5 chipset" + depends on HERMES + ---help--- + + Say Y to enable support for Prism 2 and 2.5 chipsets. These + chipsets are better handled by the hostap driver. This driver + would not support WPA or firmware download for Prism chipset. + + If you are not sure, say N. + +config HERMES_CACHE_FW_ON_INIT + bool "Cache Hermes firmware on driver initialisation" + depends on HERMES + default y + ---help--- + Say Y to cache any firmware required by the Hermes drivers + on startup. The firmware will remain cached until the + driver is unloaded. The cache uses 64K of RAM. + + Otherwise load the firmware from userspace as required. In + this case the driver should be unloaded and restarted + whenever the firmware is changed. + + If you are not sure, say Y. + +config APPLE_AIRPORT + tristate "Apple Airport support (built-in)" + depends on PPC_PMAC && HERMES + help + Say Y here to support the Airport 802.11b wireless Ethernet hardware + built into the Macintosh iBook and other recent PowerPC-based + Macintosh machines. This is essentially a Lucent Orinoco card with + a non-standard interface. + + This driver does not support the Airport Extreme (802.11b/g). Use + the BCM43xx driver for Airport Extreme cards. + +config PLX_HERMES + tristate "Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.)" + depends on PCI && HERMES + help + Enable support for PCMCIA cards supported by the "Hermes" (aka + orinoco) driver when used in PLX9052 based PCI adaptors. These + adaptors are not a full PCMCIA controller but act as a more limited + PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that + 802.11b PCMCIA cards can be used in desktop machines. The Netgear + MA301 is such an adaptor. + +config TMD_HERMES + tristate "Hermes in TMD7160 based PCI adaptor support" + depends on PCI && HERMES + help + Enable support for PCMCIA cards supported by the "Hermes" (aka + orinoco) driver when used in TMD7160 based PCI adaptors. These + adaptors are not a full PCMCIA controller but act as a more limited + PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that + 802.11b PCMCIA cards can be used in desktop machines. + +config NORTEL_HERMES + tristate "Nortel emobility PCI adaptor support" + depends on PCI && HERMES + help + Enable support for PCMCIA cards supported by the "Hermes" (aka + orinoco) driver when used in Nortel emobility PCI adaptors. These + adaptors are not full PCMCIA controllers, but act as a more limited + PCI <-> PCMCIA bridge. + +config PCI_HERMES + tristate "Prism 2.5 PCI 802.11b adaptor support" + depends on PCI && HERMES && HERMES_PRISM + help + Enable support for PCI and mini-PCI 802.11b wireless NICs based on + the Prism 2.5 chipset. These are true PCI cards, not the 802.11b + PCMCIA cards bundled with PCI<->PCMCIA adaptors which are also + common. Some of the built-in wireless adaptors in laptops are of + this variety. + +config PCMCIA_HERMES + tristate "Hermes PCMCIA card support" + depends on PCMCIA && HERMES && HAS_IOPORT_MAP + ---help--- + A driver for "Hermes" chipset based PCMCIA wireless adaptors, such + as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ + EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and + others). It should also be usable on various Prism II based cards + such as the Linksys, D-Link and Farallon Skyline. It should also + work on Symbol cards such as the 3Com AirConnect and Ericsson WLAN. + + You will very likely need the Wireless Tools in order to + configure your card and that /etc/pcmcia/wireless.opts works: + . + +config PCMCIA_SPECTRUM + tristate "Symbol Spectrum24 Trilogy PCMCIA card support" + depends on PCMCIA && HERMES && HAS_IOPORT_MAP + ---help--- + + This is a driver for 802.11b cards using RAM-loadable Symbol + firmware, such as Symbol Wireless Networker LA4100, CompactFlash + cards by Socket Communications and Intel PRO/Wireless 2011B. + + This driver requires firmware download on startup. Utilities + for downloading Symbol firmware are available at + + +config ORINOCO_USB + tristate "Agere Orinoco USB support" + depends on USB && HERMES + select FW_LOADER + ---help--- + This driver is for USB versions of the Agere Orinoco card. diff --git a/drivers/net/wireless/intersil/orinoco/Makefile b/drivers/net/wireless/intersil/orinoco/Makefile new file mode 100644 index 000000000000..bfdefb85abcd --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/Makefile @@ -0,0 +1,17 @@ +# +# Makefile for the orinoco wireless device drivers. +# +orinoco-objs := main.o fw.o hw.o mic.o scan.o wext.o hermes_dld.o hermes.o cfg.o + +obj-$(CONFIG_HERMES) += orinoco.o +obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o +obj-$(CONFIG_APPLE_AIRPORT) += airport.o +obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o +obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o +obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o +obj-$(CONFIG_NORTEL_HERMES) += orinoco_nortel.o +obj-$(CONFIG_PCMCIA_SPECTRUM) += spectrum_cs.o +obj-$(CONFIG_ORINOCO_USB) += orinoco_usb.o + +# Orinoco should be endian clean. +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/intersil/orinoco/airport.c b/drivers/net/wireless/intersil/orinoco/airport.c new file mode 100644 index 000000000000..77e6c53040a3 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/airport.c @@ -0,0 +1,267 @@ +/* airport.c + * + * A driver for "Hermes" chipset based Apple Airport wireless + * card. + * + * Copyright notice & release notes in file main.c + * + * Note specific to airport stub: + * + * 0.05 : first version of the new split driver + * 0.06 : fix possible hang on powerup, add sleep support + */ + +#define DRIVER_NAME "airport" +#define PFX DRIVER_NAME ": " + +#include +#include +#include +#include +#include + +#include "orinoco.h" + +#define AIRPORT_IO_LEN (0x1000) /* one page */ + +struct airport { + struct macio_dev *mdev; + void __iomem *vaddr; + unsigned int irq; + int irq_requested; + int ndev_registered; +}; + +static int +airport_suspend(struct macio_dev *mdev, pm_message_t state) +{ + struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); + struct net_device *dev = priv->ndev; + struct airport *card = priv->card; + unsigned long flags; + int err; + + printk(KERN_DEBUG "%s: Airport entering sleep mode\n", dev->name); + + err = orinoco_lock(priv, &flags); + if (err) { + printk(KERN_ERR "%s: hw_unavailable on PBOOK_SLEEP_NOW\n", + dev->name); + return 0; + } + + orinoco_down(priv); + orinoco_unlock(priv, &flags); + + disable_irq(card->irq); + pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, + macio_get_of_node(mdev), 0, 0); + + return 0; +} + +static int +airport_resume(struct macio_dev *mdev) +{ + struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); + struct net_device *dev = priv->ndev; + struct airport *card = priv->card; + unsigned long flags; + int err; + + printk(KERN_DEBUG "%s: Airport waking up\n", dev->name); + + pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, + macio_get_of_node(mdev), 0, 1); + msleep(200); + + enable_irq(card->irq); + + priv->hw.ops->lock_irqsave(&priv->lock, &flags); + err = orinoco_up(priv); + priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); + + return err; +} + +static int +airport_detach(struct macio_dev *mdev) +{ + struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); + struct airport *card = priv->card; + + if (card->ndev_registered) + orinoco_if_del(priv); + card->ndev_registered = 0; + + if (card->irq_requested) + free_irq(card->irq, priv); + card->irq_requested = 0; + + if (card->vaddr) + iounmap(card->vaddr); + card->vaddr = NULL; + + macio_release_resource(mdev, 0); + + pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, + macio_get_of_node(mdev), 0, 0); + ssleep(1); + + macio_set_drvdata(mdev, NULL); + free_orinocodev(priv); + + return 0; +} + +static int airport_hard_reset(struct orinoco_private *priv) +{ + /* It would be nice to power cycle the Airport for a real hard + * reset, but for some reason although it appears to + * re-initialize properly, it falls in a screaming heap + * shortly afterwards. */ +#if 0 + struct airport *card = priv->card; + + /* Vitally important. If we don't do this it seems we get an + * interrupt somewhere during the power cycle, since + * hw_unavailable is already set it doesn't get ACKed, we get + * into an interrupt loop and the PMU decides to turn us + * off. */ + disable_irq(card->irq); + + pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, + macio_get_of_node(card->mdev), 0, 0); + ssleep(1); + pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, + macio_get_of_node(card->mdev), 0, 1); + ssleep(1); + + enable_irq(card->irq); + ssleep(1); +#endif + + return 0; +} + +static int +airport_attach(struct macio_dev *mdev, const struct of_device_id *match) +{ + struct orinoco_private *priv; + struct airport *card; + unsigned long phys_addr; + struct hermes *hw; + + if (macio_resource_count(mdev) < 1 || macio_irq_count(mdev) < 1) { + printk(KERN_ERR PFX "Wrong interrupt/addresses in OF tree\n"); + return -ENODEV; + } + + /* Allocate space for private device-specific data */ + priv = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev, + airport_hard_reset, NULL); + if (!priv) { + printk(KERN_ERR PFX "Cannot allocate network device\n"); + return -ENODEV; + } + card = priv->card; + + hw = &priv->hw; + card->mdev = mdev; + + if (macio_request_resource(mdev, 0, DRIVER_NAME)) { + printk(KERN_ERR PFX "can't request IO resource !\n"); + free_orinocodev(priv); + return -EBUSY; + } + + macio_set_drvdata(mdev, priv); + + /* Setup interrupts & base address */ + card->irq = macio_irq(mdev, 0); + phys_addr = macio_resource_start(mdev, 0); /* Physical address */ + printk(KERN_DEBUG PFX "Physical address %lx\n", phys_addr); + card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN); + if (!card->vaddr) { + printk(KERN_ERR PFX "ioremap() failed\n"); + goto failed; + } + + hermes_struct_init(hw, card->vaddr, HERMES_16BIT_REGSPACING); + + /* Power up card */ + pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, + macio_get_of_node(mdev), 0, 1); + ssleep(1); + + /* Reset it before we get the interrupt */ + hw->ops->init(hw); + + if (request_irq(card->irq, orinoco_interrupt, 0, DRIVER_NAME, priv)) { + printk(KERN_ERR PFX "Couldn't get IRQ %d\n", card->irq); + goto failed; + } + card->irq_requested = 1; + + /* Initialise the main driver */ + if (orinoco_init(priv) != 0) { + printk(KERN_ERR PFX "orinoco_init() failed\n"); + goto failed; + } + + /* Register an interface with the stack */ + if (orinoco_if_add(priv, phys_addr, card->irq, NULL) != 0) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); + goto failed; + } + card->ndev_registered = 1; + return 0; + failed: + airport_detach(mdev); + return -ENODEV; +} /* airport_attach */ + + +static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION + " (Benjamin Herrenschmidt )"; +MODULE_AUTHOR("Benjamin Herrenschmidt "); +MODULE_DESCRIPTION("Driver for the Apple Airport wireless card."); +MODULE_LICENSE("Dual MPL/GPL"); + +static const struct of_device_id airport_match[] = { + { + .name = "radio", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, airport_match); + +static struct macio_driver airport_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = airport_match, + }, + .probe = airport_attach, + .remove = airport_detach, + .suspend = airport_suspend, + .resume = airport_resume, +}; + +static int __init +init_airport(void) +{ + printk(KERN_DEBUG "%s\n", version); + + return macio_register_driver(&airport_driver); +} + +static void __exit +exit_airport(void) +{ + macio_unregister_driver(&airport_driver); +} + +module_init(init_airport); +module_exit(exit_airport); diff --git a/drivers/net/wireless/intersil/orinoco/cfg.c b/drivers/net/wireless/intersil/orinoco/cfg.c new file mode 100644 index 000000000000..0f6ea316e38e --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/cfg.c @@ -0,0 +1,291 @@ +/* cfg80211 support + * + * See copyright notice in main.c + */ +#include +#include +#include "hw.h" +#include "main.h" +#include "orinoco.h" + +#include "cfg.h" + +/* Supported bitrates. Must agree with hw.c */ +static struct ieee80211_rate orinoco_rates[] = { + { .bitrate = 10 }, + { .bitrate = 20 }, + { .bitrate = 55 }, + { .bitrate = 110 }, +}; + +static const void * const orinoco_wiphy_privid = &orinoco_wiphy_privid; + +/* Called after orinoco_private is allocated. */ +void orinoco_wiphy_init(struct wiphy *wiphy) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + + wiphy->privid = orinoco_wiphy_privid; + + set_wiphy_dev(wiphy, priv->dev); +} + +/* Called after firmware is initialised */ +int orinoco_wiphy_register(struct wiphy *wiphy) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + int i, channels = 0; + + if (priv->firmware_type == FIRMWARE_TYPE_AGERE) + wiphy->max_scan_ssids = 1; + else + wiphy->max_scan_ssids = 0; + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + + /* TODO: should we set if we only have demo ad-hoc? + * (priv->has_port3) + */ + if (priv->has_ibss) + wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); + + if (!priv->broken_monitor || force_monitor) + wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); + + priv->band.bitrates = orinoco_rates; + priv->band.n_bitrates = ARRAY_SIZE(orinoco_rates); + + /* Only support channels allowed by the card EEPROM */ + for (i = 0; i < NUM_CHANNELS; i++) { + if (priv->channel_mask & (1 << i)) { + priv->channels[i].center_freq = + ieee80211_channel_to_frequency(i + 1, + IEEE80211_BAND_2GHZ); + channels++; + } + } + priv->band.channels = priv->channels; + priv->band.n_channels = channels; + + wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + i = 0; + if (priv->has_wep) { + priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP40; + i++; + + if (priv->has_big_wep) { + priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP104; + i++; + } + } + if (priv->has_wpa) { + priv->cipher_suites[i] = WLAN_CIPHER_SUITE_TKIP; + i++; + } + wiphy->cipher_suites = priv->cipher_suites; + wiphy->n_cipher_suites = i; + + wiphy->rts_threshold = priv->rts_thresh; + if (!priv->has_mwo) + wiphy->frag_threshold = priv->frag_thresh + 1; + wiphy->retry_short = priv->short_retry_limit; + wiphy->retry_long = priv->long_retry_limit; + + return wiphy_register(wiphy); +} + +static int orinoco_change_vif(struct wiphy *wiphy, struct net_device *dev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + int err = 0; + unsigned long lock; + + if (orinoco_lock(priv, &lock) != 0) + return -EBUSY; + + switch (type) { + case NL80211_IFTYPE_ADHOC: + if (!priv->has_ibss && !priv->has_port3) + err = -EINVAL; + break; + + case NL80211_IFTYPE_STATION: + break; + + case NL80211_IFTYPE_MONITOR: + if (priv->broken_monitor && !force_monitor) { + wiphy_warn(wiphy, + "Monitor mode support is buggy in this firmware, not enabling\n"); + err = -EINVAL; + } + break; + + default: + err = -EINVAL; + } + + if (!err) { + priv->iw_mode = type; + set_port_type(priv); + err = orinoco_commit(priv); + } + + orinoco_unlock(priv, &lock); + + return err; +} + +static int orinoco_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + int err; + + if (!request) + return -EINVAL; + + if (priv->scan_request && priv->scan_request != request) + return -EBUSY; + + priv->scan_request = request; + + err = orinoco_hw_trigger_scan(priv, request->ssids); + /* On error the we aren't processing the request */ + if (err) + priv->scan_request = NULL; + + return err; +} + +static int orinoco_set_monitor_channel(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + int err = 0; + unsigned long flags; + int channel; + + if (!chandef->chan) + return -EINVAL; + + if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) + return -EINVAL; + + if (chandef->chan->band != IEEE80211_BAND_2GHZ) + return -EINVAL; + + channel = ieee80211_frequency_to_channel(chandef->chan->center_freq); + + if ((channel < 1) || (channel > NUM_CHANNELS) || + !(priv->channel_mask & (1 << (channel - 1)))) + return -EINVAL; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + priv->channel = channel; + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { + /* Fast channel change - no commit if successful */ + struct hermes *hw = &priv->hw; + err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | + HERMES_TEST_SET_CHANNEL, + channel, NULL); + } + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + int frag_value = -1; + int rts_value = -1; + int err = 0; + + if (changed & WIPHY_PARAM_RETRY_SHORT) { + /* Setting short retry not supported */ + err = -EINVAL; + } + + if (changed & WIPHY_PARAM_RETRY_LONG) { + /* Setting long retry not supported */ + err = -EINVAL; + } + + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + /* Set fragmentation */ + if (priv->has_mwo) { + if (wiphy->frag_threshold == -1) + frag_value = 0; + else { + printk(KERN_WARNING "%s: Fixed fragmentation " + "is not supported on this firmware. " + "Using MWO robust instead.\n", + priv->ndev->name); + frag_value = 1; + } + } else { + if (wiphy->frag_threshold == -1) + frag_value = 2346; + else if ((wiphy->frag_threshold < 257) || + (wiphy->frag_threshold > 2347)) + err = -EINVAL; + else + /* cfg80211 value is 257-2347 (odd only) + * orinoco rid has range 256-2346 (even only) */ + frag_value = wiphy->frag_threshold & ~0x1; + } + } + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + /* Set RTS. + * + * Prism documentation suggests default of 2432, + * and a range of 0-3000. + * + * Current implementation uses 2347 as the default and + * the upper limit. + */ + + if (wiphy->rts_threshold == -1) + rts_value = 2347; + else if (wiphy->rts_threshold > 2347) + err = -EINVAL; + else + rts_value = wiphy->rts_threshold; + } + + if (!err) { + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + if (frag_value >= 0) { + if (priv->has_mwo) + priv->mwo_robust = frag_value; + else + priv->frag_thresh = frag_value; + } + if (rts_value >= 0) + priv->rts_thresh = rts_value; + + err = orinoco_commit(priv); + + orinoco_unlock(priv, &flags); + } + + return err; +} + +const struct cfg80211_ops orinoco_cfg_ops = { + .change_virtual_intf = orinoco_change_vif, + .set_monitor_channel = orinoco_set_monitor_channel, + .scan = orinoco_scan, + .set_wiphy_params = orinoco_set_wiphy_params, +}; diff --git a/drivers/net/wireless/intersil/orinoco/cfg.h b/drivers/net/wireless/intersil/orinoco/cfg.h new file mode 100644 index 000000000000..3ddc96a06cd7 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/cfg.h @@ -0,0 +1,15 @@ +/* cfg80211 support. + * + * See copyright notice in main.c + */ +#ifndef ORINOCO_CFG_H +#define ORINOCO_CFG_H + +#include + +extern const struct cfg80211_ops orinoco_cfg_ops; + +void orinoco_wiphy_init(struct wiphy *wiphy); +int orinoco_wiphy_register(struct wiphy *wiphy); + +#endif /* ORINOCO_CFG_H */ diff --git a/drivers/net/wireless/intersil/orinoco/fw.c b/drivers/net/wireless/intersil/orinoco/fw.c new file mode 100644 index 000000000000..400a35217644 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/fw.c @@ -0,0 +1,387 @@ +/* Firmware file reading and download helpers + * + * See copyright notice in main.c + */ +#include +#include +#include +#include +#include + +#include "hermes.h" +#include "hermes_dld.h" +#include "orinoco.h" + +#include "fw.h" + +/* End markers (for Symbol firmware only) */ +#define TEXT_END 0x1A /* End of text header */ + +struct fw_info { + char *pri_fw; + char *sta_fw; + char *ap_fw; + u32 pda_addr; + u16 pda_size; +}; + +static const struct fw_info orinoco_fw[] = { + { NULL, "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 }, + { NULL, "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 }, + { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", NULL, 0x00003100, 512 } +}; +MODULE_FIRMWARE("agere_sta_fw.bin"); +MODULE_FIRMWARE("agere_ap_fw.bin"); +MODULE_FIRMWARE("prism_sta_fw.bin"); +MODULE_FIRMWARE("prism_ap_fw.bin"); +MODULE_FIRMWARE("symbol_sp24t_prim_fw"); +MODULE_FIRMWARE("symbol_sp24t_sec_fw"); + +/* Structure used to access fields in FW + * Make sure LE decoding macros are used + */ +struct orinoco_fw_header { + char hdr_vers[6]; /* ASCII string for header version */ + __le16 headersize; /* Total length of header */ + __le32 entry_point; /* NIC entry point */ + __le32 blocks; /* Number of blocks to program */ + __le32 block_offset; /* Offset of block data from eof header */ + __le32 pdr_offset; /* Offset to PDR data from eof header */ + __le32 pri_offset; /* Offset to primary plug data */ + __le32 compat_offset; /* Offset to compatibility data*/ + char signature[0]; /* FW signature length headersize-20 */ +} __packed; + +/* Check the range of various header entries. Return a pointer to a + * description of the problem, or NULL if everything checks out. */ +static const char *validate_fw(const struct orinoco_fw_header *hdr, size_t len) +{ + u16 hdrsize; + + if (len < sizeof(*hdr)) + return "image too small"; + if (memcmp(hdr->hdr_vers, "HFW", 3) != 0) + return "format not recognised"; + + hdrsize = le16_to_cpu(hdr->headersize); + if (hdrsize > len) + return "bad headersize"; + if ((hdrsize + le32_to_cpu(hdr->block_offset)) > len) + return "bad block offset"; + if ((hdrsize + le32_to_cpu(hdr->pdr_offset)) > len) + return "bad PDR offset"; + if ((hdrsize + le32_to_cpu(hdr->pri_offset)) > len) + return "bad PRI offset"; + if ((hdrsize + le32_to_cpu(hdr->compat_offset)) > len) + return "bad compat offset"; + + /* TODO: consider adding a checksum or CRC to the firmware format */ + return NULL; +} + +#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) +static inline const struct firmware * +orinoco_cached_fw_get(struct orinoco_private *priv, bool primary) +{ + if (primary) + return priv->cached_pri_fw; + else + return priv->cached_fw; +} +#else +#define orinoco_cached_fw_get(priv, primary) (NULL) +#endif + +/* Download either STA or AP firmware into the card. */ +static int +orinoco_dl_firmware(struct orinoco_private *priv, + const struct fw_info *fw, + int ap) +{ + /* Plug Data Area (PDA) */ + __le16 *pda; + + struct hermes *hw = &priv->hw; + const struct firmware *fw_entry; + const struct orinoco_fw_header *hdr; + const unsigned char *first_block; + const void *end; + const char *firmware; + const char *fw_err; + struct device *dev = priv->dev; + int err = 0; + + pda = kzalloc(fw->pda_size, GFP_KERNEL); + if (!pda) + return -ENOMEM; + + if (ap) + firmware = fw->ap_fw; + else + firmware = fw->sta_fw; + + dev_dbg(dev, "Attempting to download firmware %s\n", firmware); + + /* Read current plug data */ + err = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size); + dev_dbg(dev, "Read PDA returned %d\n", err); + if (err) + goto free; + + if (!orinoco_cached_fw_get(priv, false)) { + err = request_firmware(&fw_entry, firmware, priv->dev); + + if (err) { + dev_err(dev, "Cannot find firmware %s\n", firmware); + err = -ENOENT; + goto free; + } + } else + fw_entry = orinoco_cached_fw_get(priv, false); + + hdr = (const struct orinoco_fw_header *) fw_entry->data; + + fw_err = validate_fw(hdr, fw_entry->size); + if (fw_err) { + dev_warn(dev, "Invalid firmware image detected (%s). " + "Aborting download\n", fw_err); + err = -EINVAL; + goto abort; + } + + /* Enable aux port to allow programming */ + err = hw->ops->program_init(hw, le32_to_cpu(hdr->entry_point)); + dev_dbg(dev, "Program init returned %d\n", err); + if (err != 0) + goto abort; + + /* Program data */ + first_block = (fw_entry->data + + le16_to_cpu(hdr->headersize) + + le32_to_cpu(hdr->block_offset)); + end = fw_entry->data + fw_entry->size; + + err = hermes_program(hw, first_block, end); + dev_dbg(dev, "Program returned %d\n", err); + if (err != 0) + goto abort; + + /* Update production data */ + first_block = (fw_entry->data + + le16_to_cpu(hdr->headersize) + + le32_to_cpu(hdr->pdr_offset)); + + err = hermes_apply_pda_with_defaults(hw, first_block, end, pda, + &pda[fw->pda_size / sizeof(*pda)]); + dev_dbg(dev, "Apply PDA returned %d\n", err); + if (err) + goto abort; + + /* Tell card we've finished */ + err = hw->ops->program_end(hw); + dev_dbg(dev, "Program end returned %d\n", err); + if (err != 0) + goto abort; + + /* Check if we're running */ + dev_dbg(dev, "hermes_present returned %d\n", hermes_present(hw)); + +abort: + /* If we requested the firmware, release it. */ + if (!orinoco_cached_fw_get(priv, false)) + release_firmware(fw_entry); + +free: + kfree(pda); + return err; +} + +/* + * Process a firmware image - stop the card, load the firmware, reset + * the card and make sure it responds. For the secondary firmware take + * care of the PDA - read it and then write it on top of the firmware. + */ +static int +symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw, + const unsigned char *image, const void *end, + int secondary) +{ + struct hermes *hw = &priv->hw; + int ret = 0; + const unsigned char *ptr; + const unsigned char *first_block; + + /* Plug Data Area (PDA) */ + __le16 *pda = NULL; + + /* Binary block begins after the 0x1A marker */ + ptr = image; + while (*ptr++ != TEXT_END); + first_block = ptr; + + /* Read the PDA from EEPROM */ + if (secondary) { + pda = kzalloc(fw->pda_size, GFP_KERNEL); + if (!pda) + return -ENOMEM; + + ret = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size); + if (ret) + goto free; + } + + /* Stop the firmware, so that it can be safely rewritten */ + if (priv->stop_fw) { + ret = priv->stop_fw(priv, 1); + if (ret) + goto free; + } + + /* Program the adapter with new firmware */ + ret = hermes_program(hw, first_block, end); + if (ret) + goto free; + + /* Write the PDA to the adapter */ + if (secondary) { + size_t len = hermes_blocks_length(first_block, end); + ptr = first_block + len; + ret = hermes_apply_pda(hw, ptr, end, pda, + &pda[fw->pda_size / sizeof(*pda)]); + kfree(pda); + if (ret) + return ret; + } + + /* Run the firmware */ + if (priv->stop_fw) { + ret = priv->stop_fw(priv, 0); + if (ret) + return ret; + } + + /* Reset hermes chip and make sure it responds */ + ret = hw->ops->init(hw); + + /* hermes_reset() should return 0 with the secondary firmware */ + if (secondary && ret != 0) + return -ENODEV; + + /* And this should work with any firmware */ + if (!hermes_present(hw)) + return -ENODEV; + + return 0; + +free: + kfree(pda); + return ret; +} + + +/* + * Download the firmware into the card, this also does a PCMCIA soft + * reset on the card, to make sure it's in a sane state. + */ +static int +symbol_dl_firmware(struct orinoco_private *priv, + const struct fw_info *fw) +{ + struct device *dev = priv->dev; + int ret; + const struct firmware *fw_entry; + + if (!orinoco_cached_fw_get(priv, true)) { + if (request_firmware(&fw_entry, fw->pri_fw, priv->dev) != 0) { + dev_err(dev, "Cannot find firmware: %s\n", fw->pri_fw); + return -ENOENT; + } + } else + fw_entry = orinoco_cached_fw_get(priv, true); + + /* Load primary firmware */ + ret = symbol_dl_image(priv, fw, fw_entry->data, + fw_entry->data + fw_entry->size, 0); + + if (!orinoco_cached_fw_get(priv, true)) + release_firmware(fw_entry); + if (ret) { + dev_err(dev, "Primary firmware download failed\n"); + return ret; + } + + if (!orinoco_cached_fw_get(priv, false)) { + if (request_firmware(&fw_entry, fw->sta_fw, priv->dev) != 0) { + dev_err(dev, "Cannot find firmware: %s\n", fw->sta_fw); + return -ENOENT; + } + } else + fw_entry = orinoco_cached_fw_get(priv, false); + + /* Load secondary firmware */ + ret = symbol_dl_image(priv, fw, fw_entry->data, + fw_entry->data + fw_entry->size, 1); + if (!orinoco_cached_fw_get(priv, false)) + release_firmware(fw_entry); + if (ret) + dev_err(dev, "Secondary firmware download failed\n"); + + return ret; +} + +int orinoco_download(struct orinoco_private *priv) +{ + int err = 0; + /* Reload firmware */ + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: + /* case FIRMWARE_TYPE_INTERSIL: */ + err = orinoco_dl_firmware(priv, + &orinoco_fw[priv->firmware_type], 0); + break; + + case FIRMWARE_TYPE_SYMBOL: + err = symbol_dl_firmware(priv, + &orinoco_fw[priv->firmware_type]); + break; + case FIRMWARE_TYPE_INTERSIL: + break; + } + /* TODO: if we fail we probably need to reinitialise + * the driver */ + + return err; +} + +#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) +void orinoco_cache_fw(struct orinoco_private *priv, int ap) +{ + const struct firmware *fw_entry = NULL; + const char *pri_fw; + const char *fw; + + pri_fw = orinoco_fw[priv->firmware_type].pri_fw; + if (ap) + fw = orinoco_fw[priv->firmware_type].ap_fw; + else + fw = orinoco_fw[priv->firmware_type].sta_fw; + + if (pri_fw) { + if (request_firmware(&fw_entry, pri_fw, priv->dev) == 0) + priv->cached_pri_fw = fw_entry; + } + + if (fw) { + if (request_firmware(&fw_entry, fw, priv->dev) == 0) + priv->cached_fw = fw_entry; + } +} + +void orinoco_uncache_fw(struct orinoco_private *priv) +{ + release_firmware(priv->cached_pri_fw); + release_firmware(priv->cached_fw); + priv->cached_pri_fw = NULL; + priv->cached_fw = NULL; +} +#endif diff --git a/drivers/net/wireless/intersil/orinoco/fw.h b/drivers/net/wireless/intersil/orinoco/fw.h new file mode 100644 index 000000000000..aca63e3c4b5b --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/fw.h @@ -0,0 +1,21 @@ +/* Firmware file reading and download helpers + * + * See copyright notice in main.c + */ +#ifndef _ORINOCO_FW_H_ +#define _ORINOCO_FW_H_ + +/* Forward declations */ +struct orinoco_private; + +int orinoco_download(struct orinoco_private *priv); + +#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) +void orinoco_cache_fw(struct orinoco_private *priv, int ap); +void orinoco_uncache_fw(struct orinoco_private *priv); +#else +#define orinoco_cache_fw(priv, ap) do { } while (0) +#define orinoco_uncache_fw(priv) do { } while (0) +#endif + +#endif /* _ORINOCO_FW_H_ */ diff --git a/drivers/net/wireless/intersil/orinoco/hermes.c b/drivers/net/wireless/intersil/orinoco/hermes.c new file mode 100644 index 000000000000..43790fbea0e0 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/hermes.c @@ -0,0 +1,777 @@ +/* hermes.c + * + * Driver core for the "Hermes" wireless MAC controller, as used in + * the Lucent Orinoco and Cabletron RoamAbout cards. It should also + * work on the hfa3841 and hfa3842 MAC controller chips used in the + * Prism II chipsets. + * + * This is not a complete driver, just low-level access routines for + * the MAC controller itself. + * + * Based on the prism2 driver from Absolute Value Systems' linux-wlan + * project, the Linux wvlan_cs driver, Lucent's HCF-Light + * (wvlan_hcf.c) library, and the NetBSD wireless driver (in no + * particular order). + * + * Copyright (C) 2000, David Gibson, Linuxcare Australia. + * (C) Copyright David Gibson, IBM Corp. 2001-2003. + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + */ + +#include +#include +#include + +#include "hermes.h" + +/* These are maximum timeouts. Most often, card wil react much faster */ +#define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */ +#define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */ +#define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */ +#define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */ + +/* + * AUX port access. To unlock the AUX port write the access keys to the + * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL + * register. Then read it and make sure it's HERMES_AUX_ENABLED. + */ +#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ +#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ +#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ +#define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */ + +#define HERMES_AUX_PW0 0xFE01 +#define HERMES_AUX_PW1 0xDC23 +#define HERMES_AUX_PW2 0xBA45 + +/* HERMES_CMD_DOWNLD */ +#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD) +#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD) + +/* + * Debugging helpers + */ + +#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ %p: " , hw->iobase); \ + printk(stuff); } while (0) + +#undef HERMES_DEBUG +#ifdef HERMES_DEBUG +#include + +#define DEBUG(lvl, stuff...) if ((lvl) <= HERMES_DEBUG) DMSG(stuff) + +#else /* ! HERMES_DEBUG */ + +#define DEBUG(lvl, stuff...) do { } while (0) + +#endif /* ! HERMES_DEBUG */ + +static const struct hermes_ops hermes_ops_local; + +/* + * Internal functions + */ + +/* Issue a command to the chip. Waiting for it to complete is the caller's + problem. + + Returns -EBUSY if the command register is busy, 0 on success. + + Callable from any context. +*/ +static int hermes_issue_cmd(struct hermes *hw, u16 cmd, u16 param0, + u16 param1, u16 param2) +{ + int k = CMD_BUSY_TIMEOUT; + u16 reg; + + /* First wait for the command register to unbusy */ + reg = hermes_read_regn(hw, CMD); + while ((reg & HERMES_CMD_BUSY) && k) { + k--; + udelay(1); + reg = hermes_read_regn(hw, CMD); + } + if (reg & HERMES_CMD_BUSY) + return -EBUSY; + + hermes_write_regn(hw, PARAM2, param2); + hermes_write_regn(hw, PARAM1, param1); + hermes_write_regn(hw, PARAM0, param0); + hermes_write_regn(hw, CMD, cmd); + + return 0; +} + +/* + * Function definitions + */ + +/* For doing cmds that wipe the magic constant in SWSUPPORT0 */ +static int hermes_doicmd_wait(struct hermes *hw, u16 cmd, + u16 parm0, u16 parm1, u16 parm2, + struct hermes_response *resp) +{ + int err = 0; + int k; + u16 status, reg; + + err = hermes_issue_cmd(hw, cmd, parm0, parm1, parm2); + if (err) + return err; + + reg = hermes_read_regn(hw, EVSTAT); + k = CMD_INIT_TIMEOUT; + while ((!(reg & HERMES_EV_CMD)) && k) { + k--; + udelay(10); + reg = hermes_read_regn(hw, EVSTAT); + } + + hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); + + if (!hermes_present(hw)) { + DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n", + hw->iobase); + err = -ENODEV; + goto out; + } + + if (!(reg & HERMES_EV_CMD)) { + printk(KERN_ERR "hermes @ %p: " + "Timeout waiting for card to reset (reg=0x%04x)!\n", + hw->iobase, reg); + err = -ETIMEDOUT; + goto out; + } + + status = hermes_read_regn(hw, STATUS); + if (resp) { + resp->status = status; + resp->resp0 = hermes_read_regn(hw, RESP0); + resp->resp1 = hermes_read_regn(hw, RESP1); + resp->resp2 = hermes_read_regn(hw, RESP2); + } + + hermes_write_regn(hw, EVACK, HERMES_EV_CMD); + + if (status & HERMES_STATUS_RESULT) + err = -EIO; +out: + return err; +} + +void hermes_struct_init(struct hermes *hw, void __iomem *address, + int reg_spacing) +{ + hw->iobase = address; + hw->reg_spacing = reg_spacing; + hw->inten = 0x0; + hw->eeprom_pda = false; + hw->ops = &hermes_ops_local; +} +EXPORT_SYMBOL(hermes_struct_init); + +static int hermes_init(struct hermes *hw) +{ + u16 reg; + int err = 0; + int k; + + /* We don't want to be interrupted while resetting the chipset */ + hw->inten = 0x0; + hermes_write_regn(hw, INTEN, 0); + hermes_write_regn(hw, EVACK, 0xffff); + + /* Normally it's a "can't happen" for the command register to + be busy when we go to issue a command because we are + serializing all commands. However we want to have some + chance of resetting the card even if it gets into a stupid + state, so we actually wait to see if the command register + will unbusy itself here. */ + k = CMD_BUSY_TIMEOUT; + reg = hermes_read_regn(hw, CMD); + while (k && (reg & HERMES_CMD_BUSY)) { + if (reg == 0xffff) /* Special case - the card has probably been + removed, so don't wait for the timeout */ + return -ENODEV; + + k--; + udelay(1); + reg = hermes_read_regn(hw, CMD); + } + + /* No need to explicitly handle the timeout - if we've timed + out hermes_issue_cmd() will probably return -EBUSY below */ + + /* According to the documentation, EVSTAT may contain + obsolete event occurrence information. We have to acknowledge + it by writing EVACK. */ + reg = hermes_read_regn(hw, EVSTAT); + hermes_write_regn(hw, EVACK, reg); + + /* We don't use hermes_docmd_wait here, because the reset wipes + the magic constant in SWSUPPORT0 away, and it gets confused */ + err = hermes_doicmd_wait(hw, HERMES_CMD_INIT, 0, 0, 0, NULL); + + return err; +} + +/* Issue a command to the chip, and (busy!) wait for it to + * complete. + * + * Returns: + * < 0 on internal error + * 0 on success + * > 0 on error returned by the firmware + * + * Callable from any context, but locking is your problem. */ +static int hermes_docmd_wait(struct hermes *hw, u16 cmd, u16 parm0, + struct hermes_response *resp) +{ + int err; + int k; + u16 reg; + u16 status; + + err = hermes_issue_cmd(hw, cmd, parm0, 0, 0); + if (err) { + if (!hermes_present(hw)) { + if (net_ratelimit()) + printk(KERN_WARNING "hermes @ %p: " + "Card removed while issuing command " + "0x%04x.\n", hw->iobase, cmd); + err = -ENODEV; + } else + if (net_ratelimit()) + printk(KERN_ERR "hermes @ %p: " + "Error %d issuing command 0x%04x.\n", + hw->iobase, err, cmd); + goto out; + } + + reg = hermes_read_regn(hw, EVSTAT); + k = CMD_COMPL_TIMEOUT; + while ((!(reg & HERMES_EV_CMD)) && k) { + k--; + udelay(10); + reg = hermes_read_regn(hw, EVSTAT); + } + + if (!hermes_present(hw)) { + printk(KERN_WARNING "hermes @ %p: Card removed " + "while waiting for command 0x%04x completion.\n", + hw->iobase, cmd); + err = -ENODEV; + goto out; + } + + if (!(reg & HERMES_EV_CMD)) { + printk(KERN_ERR "hermes @ %p: Timeout waiting for " + "command 0x%04x completion.\n", hw->iobase, cmd); + err = -ETIMEDOUT; + goto out; + } + + status = hermes_read_regn(hw, STATUS); + if (resp) { + resp->status = status; + resp->resp0 = hermes_read_regn(hw, RESP0); + resp->resp1 = hermes_read_regn(hw, RESP1); + resp->resp2 = hermes_read_regn(hw, RESP2); + } + + hermes_write_regn(hw, EVACK, HERMES_EV_CMD); + + if (status & HERMES_STATUS_RESULT) + err = -EIO; + + out: + return err; +} + +static int hermes_allocate(struct hermes *hw, u16 size, u16 *fid) +{ + int err = 0; + int k; + u16 reg; + + if ((size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX)) + return -EINVAL; + + err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, NULL); + if (err) + return err; + + reg = hermes_read_regn(hw, EVSTAT); + k = ALLOC_COMPL_TIMEOUT; + while ((!(reg & HERMES_EV_ALLOC)) && k) { + k--; + udelay(10); + reg = hermes_read_regn(hw, EVSTAT); + } + + if (!hermes_present(hw)) { + printk(KERN_WARNING "hermes @ %p: " + "Card removed waiting for frame allocation.\n", + hw->iobase); + return -ENODEV; + } + + if (!(reg & HERMES_EV_ALLOC)) { + printk(KERN_ERR "hermes @ %p: " + "Timeout waiting for frame allocation\n", + hw->iobase); + return -ETIMEDOUT; + } + + *fid = hermes_read_regn(hw, ALLOCFID); + hermes_write_regn(hw, EVACK, HERMES_EV_ALLOC); + + return 0; +} + +/* Set up a BAP to read a particular chunk of data from card's internal buffer. + * + * Returns: + * < 0 on internal failure (errno) + * 0 on success + * > 0 on error + * from firmware + * + * Callable from any context */ +static int hermes_bap_seek(struct hermes *hw, int bap, u16 id, u16 offset) +{ + int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0; + int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0; + int k; + u16 reg; + + /* Paranoia.. */ + if ((offset > HERMES_BAP_OFFSET_MAX) || (offset % 2)) + return -EINVAL; + + k = HERMES_BAP_BUSY_TIMEOUT; + reg = hermes_read_reg(hw, oreg); + while ((reg & HERMES_OFFSET_BUSY) && k) { + k--; + udelay(1); + reg = hermes_read_reg(hw, oreg); + } + + if (reg & HERMES_OFFSET_BUSY) + return -ETIMEDOUT; + + /* Now we actually set up the transfer */ + hermes_write_reg(hw, sreg, id); + hermes_write_reg(hw, oreg, offset); + + /* Wait for the BAP to be ready */ + k = HERMES_BAP_BUSY_TIMEOUT; + reg = hermes_read_reg(hw, oreg); + while ((reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) { + k--; + udelay(1); + reg = hermes_read_reg(hw, oreg); + } + + if (reg != offset) { + printk(KERN_ERR "hermes @ %p: BAP%d offset %s: " + "reg=0x%x id=0x%x offset=0x%x\n", hw->iobase, bap, + (reg & HERMES_OFFSET_BUSY) ? "timeout" : "error", + reg, id, offset); + + if (reg & HERMES_OFFSET_BUSY) + return -ETIMEDOUT; + + return -EIO; /* error or wrong offset */ + } + + return 0; +} + +/* Read a block of data from the chip's buffer, via the + * BAP. Synchronization/serialization is the caller's problem. len + * must be even. + * + * Returns: + * < 0 on internal failure (errno) + * 0 on success + * > 0 on error from firmware + */ +static int hermes_bap_pread(struct hermes *hw, int bap, void *buf, int len, + u16 id, u16 offset) +{ + int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; + int err = 0; + + if ((len < 0) || (len % 2)) + return -EINVAL; + + err = hermes_bap_seek(hw, bap, id, offset); + if (err) + goto out; + + /* Actually do the transfer */ + hermes_read_words(hw, dreg, buf, len / 2); + + out: + return err; +} + +/* Write a block of data to the chip's buffer, via the + * BAP. Synchronization/serialization is the caller's problem. + * + * Returns: + * < 0 on internal failure (errno) + * 0 on success + * > 0 on error from firmware + */ +static int hermes_bap_pwrite(struct hermes *hw, int bap, const void *buf, + int len, u16 id, u16 offset) +{ + int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; + int err = 0; + + if (len < 0) + return -EINVAL; + + err = hermes_bap_seek(hw, bap, id, offset); + if (err) + goto out; + + /* Actually do the transfer */ + hermes_write_bytes(hw, dreg, buf, len); + + out: + return err; +} + +/* Read a Length-Type-Value record from the card. + * + * If length is NULL, we ignore the length read from the card, and + * read the entire buffer regardless. This is useful because some of + * the configuration records appear to have incorrect lengths in + * practice. + * + * Callable from user or bh context. */ +static int hermes_read_ltv(struct hermes *hw, int bap, u16 rid, + unsigned bufsize, u16 *length, void *buf) +{ + int err = 0; + int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; + u16 rlength, rtype; + unsigned nwords; + + if (bufsize % 2) + return -EINVAL; + + err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL); + if (err) + return err; + + err = hermes_bap_seek(hw, bap, rid, 0); + if (err) + return err; + + rlength = hermes_read_reg(hw, dreg); + + if (!rlength) + return -ENODATA; + + rtype = hermes_read_reg(hw, dreg); + + if (length) + *length = rlength; + + if (rtype != rid) + printk(KERN_WARNING "hermes @ %p: %s(): " + "rid (0x%04x) does not match type (0x%04x)\n", + hw->iobase, __func__, rid, rtype); + if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize) + printk(KERN_WARNING "hermes @ %p: " + "Truncating LTV record from %d to %d bytes. " + "(rid=0x%04x, len=0x%04x)\n", hw->iobase, + HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength); + + nwords = min((unsigned)rlength - 1, bufsize / 2); + hermes_read_words(hw, dreg, buf, nwords); + + return 0; +} + +static int hermes_write_ltv(struct hermes *hw, int bap, u16 rid, + u16 length, const void *value) +{ + int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; + int err = 0; + unsigned count; + + if (length == 0) + return -EINVAL; + + err = hermes_bap_seek(hw, bap, rid, 0); + if (err) + return err; + + hermes_write_reg(hw, dreg, length); + hermes_write_reg(hw, dreg, rid); + + count = length - 1; + + hermes_write_bytes(hw, dreg, value, count << 1); + + err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, + rid, NULL); + + return err; +} + +/*** Hermes AUX control ***/ + +static inline void +hermes_aux_setaddr(struct hermes *hw, u32 addr) +{ + hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); + hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); +} + +static inline int +hermes_aux_control(struct hermes *hw, int enabled) +{ + int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED; + int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE; + int i; + + /* Already open? */ + if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state) + return 0; + + hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); + hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); + hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); + hermes_write_reg(hw, HERMES_CONTROL, action); + + for (i = 0; i < 20; i++) { + udelay(10); + if (hermes_read_reg(hw, HERMES_CONTROL) == + desired_state) + return 0; + } + + return -EBUSY; +} + +/*** Hermes programming ***/ + +/* About to start programming data (Hermes I) + * offset is the entry point + * + * Spectrum_cs' Symbol fw does not require this + * wl_lkm Agere fw does + * Don't know about intersil + */ +static int hermesi_program_init(struct hermes *hw, u32 offset) +{ + int err; + + /* Disable interrupts?*/ + /*hw->inten = 0x0;*/ + /*hermes_write_regn(hw, INTEN, 0);*/ + /*hermes_set_irqmask(hw, 0);*/ + + /* Acknowledge any outstanding command */ + hermes_write_regn(hw, EVACK, 0xFFFF); + + /* Using init_cmd_wait rather than cmd_wait */ + err = hw->ops->init_cmd_wait(hw, + 0x0100 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + if (err) + return err; + + err = hw->ops->init_cmd_wait(hw, + 0x0000 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + if (err) + return err; + + err = hermes_aux_control(hw, 1); + pr_debug("AUX enable returned %d\n", err); + + if (err) + return err; + + pr_debug("Enabling volatile, EP 0x%08x\n", offset); + err = hw->ops->init_cmd_wait(hw, + HERMES_PROGRAM_ENABLE_VOLATILE, + offset & 0xFFFFu, + offset >> 16, + 0, + NULL); + pr_debug("PROGRAM_ENABLE returned %d\n", err); + + return err; +} + +/* Done programming data (Hermes I) + * + * Spectrum_cs' Symbol fw does not require this + * wl_lkm Agere fw does + * Don't know about intersil + */ +static int hermesi_program_end(struct hermes *hw) +{ + struct hermes_response resp; + int rc = 0; + int err; + + rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp); + + pr_debug("PROGRAM_DISABLE returned %d, " + "r0 0x%04x, r1 0x%04x, r2 0x%04x\n", + rc, resp.resp0, resp.resp1, resp.resp2); + + if ((rc == 0) && + ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD)) + rc = -EIO; + + err = hermes_aux_control(hw, 0); + pr_debug("AUX disable returned %d\n", err); + + /* Acknowledge any outstanding command */ + hermes_write_regn(hw, EVACK, 0xFFFF); + + /* Reinitialise, ignoring return */ + (void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT, + 0, 0, 0, NULL); + + return rc ? rc : err; +} + +static int hermes_program_bytes(struct hermes *hw, const char *data, + u32 addr, u32 len) +{ + /* wl lkm splits the programming into chunks of 2000 bytes. + * This restriction appears to come from USB. The PCMCIA + * adapters can program the whole lot in one go */ + hermes_aux_setaddr(hw, addr); + hermes_write_bytes(hw, HERMES_AUXDATA, data, len); + return 0; +} + +/* Read PDA from the adapter */ +static int hermes_read_pda(struct hermes *hw, __le16 *pda, u32 pda_addr, + u16 pda_len) +{ + int ret; + u16 pda_size; + u16 data_len = pda_len; + __le16 *data = pda; + + if (hw->eeprom_pda) { + /* PDA of spectrum symbol is in eeprom */ + + /* Issue command to read EEPROM */ + ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); + if (ret) + return ret; + } else { + /* wl_lkm does not include PDA size in the PDA area. + * We will pad the information into pda, so other routines + * don't have to be modified */ + pda[0] = cpu_to_le16(pda_len - 2); + /* Includes CFG_PROD_DATA but not itself */ + pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ + data_len = pda_len - 4; + data = pda + 2; + } + + /* Open auxiliary port */ + ret = hermes_aux_control(hw, 1); + pr_debug("AUX enable returned %d\n", ret); + if (ret) + return ret; + + /* Read PDA */ + hermes_aux_setaddr(hw, pda_addr); + hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2); + + /* Close aux port */ + ret = hermes_aux_control(hw, 0); + pr_debug("AUX disable returned %d\n", ret); + + /* Check PDA length */ + pda_size = le16_to_cpu(pda[0]); + pr_debug("Actual PDA length %d, Max allowed %d\n", + pda_size, pda_len); + if (pda_size > pda_len) + return -EINVAL; + + return 0; +} + +static void hermes_lock_irqsave(spinlock_t *lock, + unsigned long *flags) __acquires(lock) +{ + spin_lock_irqsave(lock, *flags); +} + +static void hermes_unlock_irqrestore(spinlock_t *lock, + unsigned long *flags) __releases(lock) +{ + spin_unlock_irqrestore(lock, *flags); +} + +static void hermes_lock_irq(spinlock_t *lock) __acquires(lock) +{ + spin_lock_irq(lock); +} + +static void hermes_unlock_irq(spinlock_t *lock) __releases(lock) +{ + spin_unlock_irq(lock); +} + +/* Hermes operations for local buses */ +static const struct hermes_ops hermes_ops_local = { + .init = hermes_init, + .cmd_wait = hermes_docmd_wait, + .init_cmd_wait = hermes_doicmd_wait, + .allocate = hermes_allocate, + .read_ltv = hermes_read_ltv, + .write_ltv = hermes_write_ltv, + .bap_pread = hermes_bap_pread, + .bap_pwrite = hermes_bap_pwrite, + .read_pda = hermes_read_pda, + .program_init = hermesi_program_init, + .program_end = hermesi_program_end, + .program = hermes_program_bytes, + .lock_irqsave = hermes_lock_irqsave, + .unlock_irqrestore = hermes_unlock_irqrestore, + .lock_irq = hermes_lock_irq, + .unlock_irq = hermes_unlock_irq, +}; diff --git a/drivers/net/wireless/intersil/orinoco/hermes.h b/drivers/net/wireless/intersil/orinoco/hermes.h new file mode 100644 index 000000000000..28a42448d329 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/hermes.h @@ -0,0 +1,520 @@ +/* hermes.h + * + * Driver core for the "Hermes" wireless MAC controller, as used in + * the Lucent Orinoco and Cabletron RoamAbout cards. It should also + * work on the hfa3841 and hfa3842 MAC controller chips used in the + * Prism I & II chipsets. + * + * This is not a complete driver, just low-level access routines for + * the MAC controller itself. + * + * Based on the prism2 driver from Absolute Value Systems' linux-wlan + * project, the Linux wvlan_cs driver, Lucent's HCF-Light + * (wvlan_hcf.c) library, and the NetBSD wireless driver. + * + * Copyright (C) 2000, David Gibson, Linuxcare Australia. + * (C) Copyright David Gibson, IBM Corp. 2001-2003. + * + * Portions taken from hfa384x.h. + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * + * This file distributed under the GPL, version 2. + */ + +#ifndef _HERMES_H +#define _HERMES_H + +/* Notes on locking: + * + * As a module of low level hardware access routines, there is no + * locking. Users of this module should ensure that they serialize + * access to the hermes structure, and to the hardware +*/ + +#include +#include + +/* + * Limits and constants + */ +#define HERMES_ALLOC_LEN_MIN (4) +#define HERMES_ALLOC_LEN_MAX (2400) +#define HERMES_LTV_LEN_MAX (34) +#define HERMES_BAP_DATALEN_MAX (4096) +#define HERMES_BAP_OFFSET_MAX (4096) +#define HERMES_PORTID_MAX (7) +#define HERMES_NUMPORTS_MAX (HERMES_PORTID_MAX + 1) +#define HERMES_PDR_LEN_MAX (260) /* in bytes, from EK */ +#define HERMES_PDA_RECS_MAX (200) /* a guess */ +#define HERMES_PDA_LEN_MAX (1024) /* in bytes, from EK */ +#define HERMES_SCANRESULT_MAX (35) +#define HERMES_CHINFORESULT_MAX (8) +#define HERMES_MAX_MULTICAST (16) +#define HERMES_MAGIC (0x7d1f) + +/* + * Hermes register offsets + */ +#define HERMES_CMD (0x00) +#define HERMES_PARAM0 (0x02) +#define HERMES_PARAM1 (0x04) +#define HERMES_PARAM2 (0x06) +#define HERMES_STATUS (0x08) +#define HERMES_RESP0 (0x0A) +#define HERMES_RESP1 (0x0C) +#define HERMES_RESP2 (0x0E) +#define HERMES_INFOFID (0x10) +#define HERMES_RXFID (0x20) +#define HERMES_ALLOCFID (0x22) +#define HERMES_TXCOMPLFID (0x24) +#define HERMES_SELECT0 (0x18) +#define HERMES_OFFSET0 (0x1C) +#define HERMES_DATA0 (0x36) +#define HERMES_SELECT1 (0x1A) +#define HERMES_OFFSET1 (0x1E) +#define HERMES_DATA1 (0x38) +#define HERMES_EVSTAT (0x30) +#define HERMES_INTEN (0x32) +#define HERMES_EVACK (0x34) +#define HERMES_CONTROL (0x14) +#define HERMES_SWSUPPORT0 (0x28) +#define HERMES_SWSUPPORT1 (0x2A) +#define HERMES_SWSUPPORT2 (0x2C) +#define HERMES_AUXPAGE (0x3A) +#define HERMES_AUXOFFSET (0x3C) +#define HERMES_AUXDATA (0x3E) + +/* + * CMD register bitmasks + */ +#define HERMES_CMD_BUSY (0x8000) +#define HERMES_CMD_AINFO (0x7f00) +#define HERMES_CMD_MACPORT (0x0700) +#define HERMES_CMD_RECL (0x0100) +#define HERMES_CMD_WRITE (0x0100) +#define HERMES_CMD_PROGMODE (0x0300) +#define HERMES_CMD_CMDCODE (0x003f) + +/* + * STATUS register bitmasks + */ +#define HERMES_STATUS_RESULT (0x7f00) +#define HERMES_STATUS_CMDCODE (0x003f) + +/* + * OFFSET register bitmasks + */ +#define HERMES_OFFSET_BUSY (0x8000) +#define HERMES_OFFSET_ERR (0x4000) +#define HERMES_OFFSET_DATAOFF (0x0ffe) + +/* + * Event register bitmasks (INTEN, EVSTAT, EVACK) + */ +#define HERMES_EV_TICK (0x8000) +#define HERMES_EV_WTERR (0x4000) +#define HERMES_EV_INFDROP (0x2000) +#define HERMES_EV_INFO (0x0080) +#define HERMES_EV_DTIM (0x0020) +#define HERMES_EV_CMD (0x0010) +#define HERMES_EV_ALLOC (0x0008) +#define HERMES_EV_TXEXC (0x0004) +#define HERMES_EV_TX (0x0002) +#define HERMES_EV_RX (0x0001) + +/* + * Command codes + */ +/*--- Controller Commands ----------------------------*/ +#define HERMES_CMD_INIT (0x0000) +#define HERMES_CMD_ENABLE (0x0001) +#define HERMES_CMD_DISABLE (0x0002) +#define HERMES_CMD_DIAG (0x0003) + +/*--- Buffer Mgmt Commands ---------------------------*/ +#define HERMES_CMD_ALLOC (0x000A) +#define HERMES_CMD_TX (0x000B) + +/*--- Regulate Commands ------------------------------*/ +#define HERMES_CMD_NOTIFY (0x0010) +#define HERMES_CMD_INQUIRE (0x0011) + +/*--- Configure Commands -----------------------------*/ +#define HERMES_CMD_ACCESS (0x0021) +#define HERMES_CMD_DOWNLD (0x0022) + +/*--- Serial I/O Commands ----------------------------*/ +#define HERMES_CMD_READMIF (0x0030) +#define HERMES_CMD_WRITEMIF (0x0031) + +/*--- Debugging Commands -----------------------------*/ +#define HERMES_CMD_TEST (0x0038) + + +/* Test command arguments */ +#define HERMES_TEST_SET_CHANNEL 0x0800 +#define HERMES_TEST_MONITOR 0x0b00 +#define HERMES_TEST_STOP 0x0f00 + +/* Authentication algorithms */ +#define HERMES_AUTH_OPEN 1 +#define HERMES_AUTH_SHARED_KEY 2 + +/* WEP settings */ +#define HERMES_WEP_PRIVACY_INVOKED 0x0001 +#define HERMES_WEP_EXCL_UNENCRYPTED 0x0002 +#define HERMES_WEP_HOST_ENCRYPT 0x0010 +#define HERMES_WEP_HOST_DECRYPT 0x0080 + +/* Symbol hostscan options */ +#define HERMES_HOSTSCAN_SYMBOL_5SEC 0x0001 +#define HERMES_HOSTSCAN_SYMBOL_ONCE 0x0002 +#define HERMES_HOSTSCAN_SYMBOL_PASSIVE 0x0040 +#define HERMES_HOSTSCAN_SYMBOL_BCAST 0x0080 + +/* + * Frame structures and constants + */ + +#define HERMES_DESCRIPTOR_OFFSET 0 +#define HERMES_802_11_OFFSET (14) +#define HERMES_802_3_OFFSET (14 + 32) +#define HERMES_802_2_OFFSET (14 + 32 + 14) +#define HERMES_TXCNTL2_OFFSET (HERMES_802_3_OFFSET - 2) + +#define HERMES_RXSTAT_ERR (0x0003) +#define HERMES_RXSTAT_BADCRC (0x0001) +#define HERMES_RXSTAT_UNDECRYPTABLE (0x0002) +#define HERMES_RXSTAT_MIC (0x0010) /* Frame contains MIC */ +#define HERMES_RXSTAT_MACPORT (0x0700) +#define HERMES_RXSTAT_PCF (0x1000) /* Frame was received in CF period */ +#define HERMES_RXSTAT_MIC_KEY_ID (0x1800) /* MIC key used */ +#define HERMES_RXSTAT_MSGTYPE (0xE000) +#define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */ +#define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel encoded frame */ +#define HERMES_RXSTAT_WMP (0x6000) /* Wavelan-II Management Protocol frame */ + +/* Shift amount for key ID in RXSTAT and TXCTRL */ +#define HERMES_MIC_KEY_ID_SHIFT 11 + +struct hermes_tx_descriptor { + __le16 status; + __le16 reserved1; + __le16 reserved2; + __le32 sw_support; + u8 retry_count; + u8 tx_rate; + __le16 tx_control; +} __packed; + +#define HERMES_TXSTAT_RETRYERR (0x0001) +#define HERMES_TXSTAT_AGEDERR (0x0002) +#define HERMES_TXSTAT_DISCON (0x0004) +#define HERMES_TXSTAT_FORMERR (0x0008) + +#define HERMES_TXCTRL_TX_OK (0x0002) /* ?? interrupt on Tx complete */ +#define HERMES_TXCTRL_TX_EX (0x0004) /* ?? interrupt on Tx exception */ +#define HERMES_TXCTRL_802_11 (0x0008) /* We supply 802.11 header */ +#define HERMES_TXCTRL_MIC (0x0010) /* 802.3 + TKIP */ +#define HERMES_TXCTRL_MIC_KEY_ID (0x1800) /* MIC Key ID mask */ +#define HERMES_TXCTRL_ALT_RTRY (0x0020) + +/* Inquiry constants and data types */ + +#define HERMES_INQ_TALLIES (0xF100) +#define HERMES_INQ_SCAN (0xF101) +#define HERMES_INQ_CHANNELINFO (0xF102) +#define HERMES_INQ_HOSTSCAN (0xF103) +#define HERMES_INQ_HOSTSCAN_SYMBOL (0xF104) +#define HERMES_INQ_LINKSTATUS (0xF200) +#define HERMES_INQ_SEC_STAT_AGERE (0xF202) + +struct hermes_tallies_frame { + __le16 TxUnicastFrames; + __le16 TxMulticastFrames; + __le16 TxFragments; + __le16 TxUnicastOctets; + __le16 TxMulticastOctets; + __le16 TxDeferredTransmissions; + __le16 TxSingleRetryFrames; + __le16 TxMultipleRetryFrames; + __le16 TxRetryLimitExceeded; + __le16 TxDiscards; + __le16 RxUnicastFrames; + __le16 RxMulticastFrames; + __le16 RxFragments; + __le16 RxUnicastOctets; + __le16 RxMulticastOctets; + __le16 RxFCSErrors; + __le16 RxDiscards_NoBuffer; + __le16 TxDiscardsWrongSA; + __le16 RxWEPUndecryptable; + __le16 RxMsgInMsgFragments; + __le16 RxMsgInBadMsgFragments; + /* Those last are probably not available in very old firmwares */ + __le16 RxDiscards_WEPICVError; + __le16 RxDiscards_WEPExcluded; +} __packed; + +/* Grabbed from wlan-ng - Thanks Mark... - Jean II + * This is the result of a scan inquiry command */ +/* Structure describing info about an Access Point */ +struct prism2_scan_apinfo { + __le16 channel; /* Channel where the AP sits */ + __le16 noise; /* Noise level */ + __le16 level; /* Signal level */ + u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */ + __le16 beacon_interv; /* Beacon interval */ + __le16 capabilities; /* Capabilities */ + __le16 essid_len; /* ESSID length */ + u8 essid[32]; /* ESSID of the network */ + u8 rates[10]; /* Bit rate supported */ + __le16 proberesp_rate; /* Data rate of the response frame */ + __le16 atim; /* ATIM window time, Kus (hostscan only) */ +} __packed; + +/* Same stuff for the Lucent/Agere card. + * Thanks to h1kari - Jean II */ +struct agere_scan_apinfo { + __le16 channel; /* Channel where the AP sits */ + __le16 noise; /* Noise level */ + __le16 level; /* Signal level */ + u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */ + __le16 beacon_interv; /* Beacon interval */ + __le16 capabilities; /* Capabilities */ + /* bits: 0-ess, 1-ibss, 4-privacy [wep] */ + __le16 essid_len; /* ESSID length */ + u8 essid[32]; /* ESSID of the network */ +} __packed; + +/* Moustafa: Scan structure for Symbol cards */ +struct symbol_scan_apinfo { + u8 channel; /* Channel where the AP sits */ + u8 unknown1; /* 8 in 2.9x and 3.9x f/w, 0 otherwise */ + __le16 noise; /* Noise level */ + __le16 level; /* Signal level */ + u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */ + __le16 beacon_interv; /* Beacon interval */ + __le16 capabilities; /* Capabilities */ + /* bits: 0-ess, 1-ibss, 4-privacy [wep] */ + __le16 essid_len; /* ESSID length */ + u8 essid[32]; /* ESSID of the network */ + __le16 rates[5]; /* Bit rate supported */ + __le16 basic_rates; /* Basic rates bitmask */ + u8 unknown2[6]; /* Always FF:FF:FF:FF:00:00 */ + u8 unknown3[8]; /* Always 0, appeared in f/w 3.91-68 */ +} __packed; + +union hermes_scan_info { + struct agere_scan_apinfo a; + struct prism2_scan_apinfo p; + struct symbol_scan_apinfo s; +}; + +/* Extended scan struct for HERMES_INQ_CHANNELINFO. + * wl_lkm calls this an ACS scan (Automatic Channel Select). + * Keep out of union hermes_scan_info because it is much bigger than + * the older scan structures. */ +struct agere_ext_scan_info { + __le16 reserved0; + + u8 noise; + u8 level; + u8 rx_flow; + u8 rate; + __le16 reserved1[2]; + + __le16 frame_control; + __le16 dur_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + __le16 sequence; + u8 addr4[ETH_ALEN]; + + __le16 data_length; + + /* Next 3 fields do not get filled in. */ + u8 daddr[ETH_ALEN]; + u8 saddr[ETH_ALEN]; + __le16 len_type; + + __le64 timestamp; + __le16 beacon_interval; + __le16 capabilities; + u8 data[0]; +} __packed; + +#define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000) +#define HERMES_LINKSTATUS_CONNECTED (0x0001) +#define HERMES_LINKSTATUS_DISCONNECTED (0x0002) +#define HERMES_LINKSTATUS_AP_CHANGE (0x0003) +#define HERMES_LINKSTATUS_AP_OUT_OF_RANGE (0x0004) +#define HERMES_LINKSTATUS_AP_IN_RANGE (0x0005) +#define HERMES_LINKSTATUS_ASSOC_FAILED (0x0006) + +struct hermes_linkstatus { + __le16 linkstatus; /* Link status */ +} __packed; + +struct hermes_response { + u16 status, resp0, resp1, resp2; +}; + +/* "ID" structure - used for ESSID and station nickname */ +struct hermes_idstring { + __le16 len; + __le16 val[16]; +} __packed; + +struct hermes_multicast { + u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN]; +} __packed; + +/* Timeouts */ +#define HERMES_BAP_BUSY_TIMEOUT (10000) /* In iterations of ~1us */ + +struct hermes; + +/* Functions to access hardware */ +struct hermes_ops { + int (*init)(struct hermes *hw); + int (*cmd_wait)(struct hermes *hw, u16 cmd, u16 parm0, + struct hermes_response *resp); + int (*init_cmd_wait)(struct hermes *hw, u16 cmd, + u16 parm0, u16 parm1, u16 parm2, + struct hermes_response *resp); + int (*allocate)(struct hermes *hw, u16 size, u16 *fid); + int (*read_ltv)(struct hermes *hw, int bap, u16 rid, unsigned buflen, + u16 *length, void *buf); + int (*write_ltv)(struct hermes *hw, int bap, u16 rid, + u16 length, const void *value); + int (*bap_pread)(struct hermes *hw, int bap, void *buf, int len, + u16 id, u16 offset); + int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf, + int len, u16 id, u16 offset); + int (*read_pda)(struct hermes *hw, __le16 *pda, + u32 pda_addr, u16 pda_len); + int (*program_init)(struct hermes *hw, u32 entry_point); + int (*program_end)(struct hermes *hw); + int (*program)(struct hermes *hw, const char *buf, + u32 addr, u32 len); + void (*lock_irqsave)(spinlock_t *lock, unsigned long *flags); + void (*unlock_irqrestore)(spinlock_t *lock, unsigned long *flags); + void (*lock_irq)(spinlock_t *lock); + void (*unlock_irq)(spinlock_t *lock); +}; + +/* Basic control structure */ +struct hermes { + void __iomem *iobase; + int reg_spacing; +#define HERMES_16BIT_REGSPACING 0 +#define HERMES_32BIT_REGSPACING 1 + u16 inten; /* Which interrupts should be enabled? */ + bool eeprom_pda; + const struct hermes_ops *ops; + void *priv; +}; + +/* Register access convenience macros */ +#define hermes_read_reg(hw, off) \ + (ioread16((hw)->iobase + ((off) << (hw)->reg_spacing))) +#define hermes_write_reg(hw, off, val) \ + (iowrite16((val), (hw)->iobase + ((off) << (hw)->reg_spacing))) +#define hermes_read_regn(hw, name) hermes_read_reg((hw), HERMES_##name) +#define hermes_write_regn(hw, name, val) \ + hermes_write_reg((hw), HERMES_##name, (val)) + +/* Function prototypes */ +void hermes_struct_init(struct hermes *hw, void __iomem *address, + int reg_spacing); + +/* Inline functions */ + +static inline int hermes_present(struct hermes *hw) +{ + return hermes_read_regn(hw, SWSUPPORT0) == HERMES_MAGIC; +} + +static inline void hermes_set_irqmask(struct hermes *hw, u16 events) +{ + hw->inten = events; + hermes_write_regn(hw, INTEN, events); +} + +static inline int hermes_enable_port(struct hermes *hw, int port) +{ + return hw->ops->cmd_wait(hw, HERMES_CMD_ENABLE | (port << 8), + 0, NULL); +} + +static inline int hermes_disable_port(struct hermes *hw, int port) +{ + return hw->ops->cmd_wait(hw, HERMES_CMD_DISABLE | (port << 8), + 0, NULL); +} + +/* Initiate an INQUIRE command (tallies or scan). The result will come as an + * information frame in __orinoco_ev_info() */ +static inline int hermes_inquire(struct hermes *hw, u16 rid) +{ + return hw->ops->cmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL); +} + +#define HERMES_BYTES_TO_RECLEN(n) ((((n) + 1) / 2) + 1) +#define HERMES_RECLEN_TO_BYTES(n) (((n) - 1) * 2) + +/* Note that for the next two, the count is in 16-bit words, not bytes */ +static inline void hermes_read_words(struct hermes *hw, int off, + void *buf, unsigned count) +{ + off = off << hw->reg_spacing; + ioread16_rep(hw->iobase + off, buf, count); +} + +static inline void hermes_write_bytes(struct hermes *hw, int off, + const char *buf, unsigned count) +{ + off = off << hw->reg_spacing; + iowrite16_rep(hw->iobase + off, buf, count >> 1); + if (unlikely(count & 1)) + iowrite8(buf[count - 1], hw->iobase + off); +} + +static inline void hermes_clear_words(struct hermes *hw, int off, + unsigned count) +{ + unsigned i; + + off = off << hw->reg_spacing; + + for (i = 0; i < count; i++) + iowrite16(0, hw->iobase + off); +} + +#define HERMES_READ_RECORD(hw, bap, rid, buf) \ + (hw->ops->read_ltv((hw), (bap), (rid), sizeof(*buf), NULL, (buf))) +#define HERMES_WRITE_RECORD(hw, bap, rid, buf) \ + (hw->ops->write_ltv((hw), (bap), (rid), \ + HERMES_BYTES_TO_RECLEN(sizeof(*buf)), (buf))) + +static inline int hermes_read_wordrec(struct hermes *hw, int bap, u16 rid, + u16 *word) +{ + __le16 rec; + int err; + + err = HERMES_READ_RECORD(hw, bap, rid, &rec); + *word = le16_to_cpu(rec); + return err; +} + +static inline int hermes_write_wordrec(struct hermes *hw, int bap, u16 rid, + u16 word) +{ + __le16 rec = cpu_to_le16(word); + return HERMES_WRITE_RECORD(hw, bap, rid, &rec); +} + +#endif /* _HERMES_H */ diff --git a/drivers/net/wireless/intersil/orinoco/hermes_dld.c b/drivers/net/wireless/intersil/orinoco/hermes_dld.c new file mode 100644 index 000000000000..4a10b7aca043 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/hermes_dld.c @@ -0,0 +1,477 @@ +/* + * Hermes download helper. + * + * This helper: + * - is capable of writing to the volatile area of the hermes device + * - is currently not capable of writing to non-volatile areas + * - provide helpers to identify and update plugin data + * - is not capable of interpreting a fw image directly. That is up to + * the main card driver. + * - deals with Hermes I devices. It can probably be modified to deal + * with Hermes II devices + * + * Copyright (C) 2007, David Kilroy + * + * Plug data code slightly modified from spectrum_cs driver + * Copyright (C) 2002-2005 Pavel Roskin + * Portions based on information in wl_lkm_718 Agere driver + * COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + */ + +#include +#include +#include "hermes.h" +#include "hermes_dld.h" + +#define PFX "hermes_dld: " + +/* End markers used in dblocks */ +#define PDI_END 0x00000000 /* End of PDA */ +#define BLOCK_END 0xFFFFFFFF /* Last image block */ +#define TEXT_END 0x1A /* End of text header */ + +/* + * The following structures have little-endian fields denoted by + * the leading underscore. Don't access them directly - use inline + * functions defined below. + */ + +/* + * The binary image to be downloaded consists of series of data blocks. + * Each block has the following structure. + */ +struct dblock { + __le32 addr; /* adapter address where to write the block */ + __le16 len; /* length of the data only, in bytes */ + char data[0]; /* data to be written */ +} __packed; + +/* + * Plug Data References are located in the image after the last data + * block. They refer to areas in the adapter memory where the plug data + * items with matching ID should be written. + */ +struct pdr { + __le32 id; /* record ID */ + __le32 addr; /* adapter address where to write the data */ + __le32 len; /* expected length of the data, in bytes */ + char next[0]; /* next PDR starts here */ +} __packed; + +/* + * Plug Data Items are located in the EEPROM read from the adapter by + * primary firmware. They refer to the device-specific data that should + * be plugged into the secondary firmware. + */ +struct pdi { + __le16 len; /* length of ID and data, in words */ + __le16 id; /* record ID */ + char data[0]; /* plug data */ +} __packed; + +/*** FW data block access functions ***/ + +static inline u32 +dblock_addr(const struct dblock *blk) +{ + return le32_to_cpu(blk->addr); +} + +static inline u32 +dblock_len(const struct dblock *blk) +{ + return le16_to_cpu(blk->len); +} + +/*** PDR Access functions ***/ + +static inline u32 +pdr_id(const struct pdr *pdr) +{ + return le32_to_cpu(pdr->id); +} + +static inline u32 +pdr_addr(const struct pdr *pdr) +{ + return le32_to_cpu(pdr->addr); +} + +static inline u32 +pdr_len(const struct pdr *pdr) +{ + return le32_to_cpu(pdr->len); +} + +/*** PDI Access functions ***/ + +static inline u32 +pdi_id(const struct pdi *pdi) +{ + return le16_to_cpu(pdi->id); +} + +/* Return length of the data only, in bytes */ +static inline u32 +pdi_len(const struct pdi *pdi) +{ + return 2 * (le16_to_cpu(pdi->len) - 1); +} + +/*** Plug Data Functions ***/ + +/* + * Scan PDR for the record with the specified RECORD_ID. + * If it's not found, return NULL. + */ +static const struct pdr * +hermes_find_pdr(const struct pdr *first_pdr, u32 record_id, const void *end) +{ + const struct pdr *pdr = first_pdr; + + end -= sizeof(struct pdr); + + while (((void *) pdr <= end) && + (pdr_id(pdr) != PDI_END)) { + /* + * PDR area is currently not terminated by PDI_END. + * It's followed by CRC records, which have the type + * field where PDR has length. The type can be 0 or 1. + */ + if (pdr_len(pdr) < 2) + return NULL; + + /* If the record ID matches, we are done */ + if (pdr_id(pdr) == record_id) + return pdr; + + pdr = (struct pdr *) pdr->next; + } + return NULL; +} + +/* Scan production data items for a particular entry */ +static const struct pdi * +hermes_find_pdi(const struct pdi *first_pdi, u32 record_id, const void *end) +{ + const struct pdi *pdi = first_pdi; + + end -= sizeof(struct pdi); + + while (((void *) pdi <= end) && + (pdi_id(pdi) != PDI_END)) { + + /* If the record ID matches, we are done */ + if (pdi_id(pdi) == record_id) + return pdi; + + pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; + } + return NULL; +} + +/* Process one Plug Data Item - find corresponding PDR and plug it */ +static int +hermes_plug_pdi(struct hermes *hw, const struct pdr *first_pdr, + const struct pdi *pdi, const void *pdr_end) +{ + const struct pdr *pdr; + + /* Find the PDR corresponding to this PDI */ + pdr = hermes_find_pdr(first_pdr, pdi_id(pdi), pdr_end); + + /* No match is found, safe to ignore */ + if (!pdr) + return 0; + + /* Lengths of the data in PDI and PDR must match */ + if (pdi_len(pdi) != pdr_len(pdr)) + return -EINVAL; + + /* do the actual plugging */ + hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi)); + + return 0; +} + +/* Parse PDA and write the records into the adapter + * + * Attempt to write every records that is in the specified pda + * which also has a valid production data record for the firmware. + */ +int hermes_apply_pda(struct hermes *hw, + const char *first_pdr, + const void *pdr_end, + const __le16 *pda, + const void *pda_end) +{ + int ret; + const struct pdi *pdi; + const struct pdr *pdr; + + pdr = (const struct pdr *) first_pdr; + pda_end -= sizeof(struct pdi); + + /* Go through every PDI and plug them into the adapter */ + pdi = (const struct pdi *) (pda + 2); + while (((void *) pdi <= pda_end) && + (pdi_id(pdi) != PDI_END)) { + ret = hermes_plug_pdi(hw, pdr, pdi, pdr_end); + if (ret) + return ret; + + /* Increment to the next PDI */ + pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)]; + } + return 0; +} + +/* Identify the total number of bytes in all blocks + * including the header data. + */ +size_t +hermes_blocks_length(const char *first_block, const void *end) +{ + const struct dblock *blk = (const struct dblock *) first_block; + int total_len = 0; + int len; + + end -= sizeof(*blk); + + /* Skip all blocks to locate Plug Data References + * (Spectrum CS) */ + while (((void *) blk <= end) && + (dblock_addr(blk) != BLOCK_END)) { + len = dblock_len(blk); + total_len += sizeof(*blk) + len; + blk = (struct dblock *) &blk->data[len]; + } + + return total_len; +} + +/*** Hermes programming ***/ + +/* Program the data blocks */ +int hermes_program(struct hermes *hw, const char *first_block, const void *end) +{ + const struct dblock *blk; + u32 blkaddr; + u32 blklen; + int err = 0; + + blk = (const struct dblock *) first_block; + + if ((void *) blk > (end - sizeof(*blk))) + return -EIO; + + blkaddr = dblock_addr(blk); + blklen = dblock_len(blk); + + while ((blkaddr != BLOCK_END) && + (((void *) blk + blklen) <= end)) { + pr_debug(PFX "Programming block of length %d " + "to address 0x%08x\n", blklen, blkaddr); + + err = hw->ops->program(hw, blk->data, blkaddr, blklen); + if (err) + break; + + blk = (const struct dblock *) &blk->data[blklen]; + + if ((void *) blk > (end - sizeof(*blk))) + return -EIO; + + blkaddr = dblock_addr(blk); + blklen = dblock_len(blk); + } + return err; +} + +/*** Default plugging data for Hermes I ***/ +/* Values from wl_lkm_718/hcf/dhf.c */ + +#define DEFINE_DEFAULT_PDR(pid, length, data) \ +static const struct { \ + __le16 len; \ + __le16 id; \ + u8 val[length]; \ +} __packed default_pdr_data_##pid = { \ + cpu_to_le16((sizeof(default_pdr_data_##pid)/ \ + sizeof(__le16)) - 1), \ + cpu_to_le16(pid), \ + data \ +} + +#define DEFAULT_PDR(pid) default_pdr_data_##pid + +/* HWIF Compatibility */ +DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00"); + +/* PPPPSign */ +DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00"); + +/* PPPPProf */ +DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00"); + +/* Antenna diversity */ +DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F"); + +/* Modem VCO band Set-up */ +DEFINE_DEFAULT_PDR(0x0160, 28, + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00"); + +/* Modem Rx Gain Table Values */ +DEFINE_DEFAULT_PDR(0x0161, 256, + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3F\01\x3F\x01\x3F\x01" + "\x3F\x01\x3E\01\x3E\x01\x3D\x01" + "\x3D\x01\x3C\01\x3C\x01\x3B\x01" + "\x3B\x01\x3A\01\x3A\x01\x39\x01" + "\x39\x01\x38\01\x38\x01\x37\x01" + "\x37\x01\x36\01\x36\x01\x35\x01" + "\x35\x01\x34\01\x34\x01\x33\x01" + "\x33\x01\x32\x01\x32\x01\x31\x01" + "\x31\x01\x30\x01\x30\x01\x7B\x01" + "\x7B\x01\x7A\x01\x7A\x01\x79\x01" + "\x79\x01\x78\x01\x78\x01\x77\x01" + "\x77\x01\x76\x01\x76\x01\x75\x01" + "\x75\x01\x74\x01\x74\x01\x73\x01" + "\x73\x01\x72\x01\x72\x01\x71\x01" + "\x71\x01\x70\x01\x70\x01\x68\x01" + "\x68\x01\x67\x01\x67\x01\x66\x01" + "\x66\x01\x65\x01\x65\x01\x57\x01" + "\x57\x01\x56\x01\x56\x01\x55\x01" + "\x55\x01\x54\x01\x54\x01\x53\x01" + "\x53\x01\x52\x01\x52\x01\x51\x01" + "\x51\x01\x50\x01\x50\x01\x48\x01" + "\x48\x01\x47\x01\x47\x01\x46\x01" + "\x46\x01\x45\x01\x45\x01\x44\x01" + "\x44\x01\x43\x01\x43\x01\x42\x01" + "\x42\x01\x41\x01\x41\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01" + "\x40\x01\x40\x01\x40\x01\x40\x01"); + +/* Write PDA according to certain rules. + * + * For every production data record, look for a previous setting in + * the pda, and use that. + * + * For certain records, use defaults if they are not found in pda. + */ +int hermes_apply_pda_with_defaults(struct hermes *hw, + const char *first_pdr, + const void *pdr_end, + const __le16 *pda, + const void *pda_end) +{ + const struct pdr *pdr = (const struct pdr *) first_pdr; + const struct pdi *first_pdi = (const struct pdi *) &pda[2]; + const struct pdi *pdi; + const struct pdi *default_pdi = NULL; + const struct pdi *outdoor_pdi; + int record_id; + + pdr_end -= sizeof(struct pdr); + + while (((void *) pdr <= pdr_end) && + (pdr_id(pdr) != PDI_END)) { + /* + * For spectrum_cs firmwares, + * PDR area is currently not terminated by PDI_END. + * It's followed by CRC records, which have the type + * field where PDR has length. The type can be 0 or 1. + */ + if (pdr_len(pdr) < 2) + break; + record_id = pdr_id(pdr); + + pdi = hermes_find_pdi(first_pdi, record_id, pda_end); + if (pdi) + pr_debug(PFX "Found record 0x%04x at %p\n", + record_id, pdi); + + switch (record_id) { + case 0x110: /* Modem REFDAC values */ + case 0x120: /* Modem VGDAC values */ + outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1, + pda_end); + default_pdi = NULL; + if (outdoor_pdi) { + pdi = outdoor_pdi; + pr_debug(PFX + "Using outdoor record 0x%04x at %p\n", + record_id + 1, pdi); + } + break; + case 0x5: /* HWIF Compatibility */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005); + break; + case 0x108: /* PPPPSign */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108); + break; + case 0x109: /* PPPPProf */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109); + break; + case 0x150: /* Antenna diversity */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150); + break; + case 0x160: /* Modem VCO band Set-up */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160); + break; + case 0x161: /* Modem Rx Gain Table Values */ + default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161); + break; + default: + default_pdi = NULL; + break; + } + if (!pdi && default_pdi) { + /* Use default */ + pdi = default_pdi; + pr_debug(PFX "Using default record 0x%04x at %p\n", + record_id, pdi); + } + + if (pdi) { + /* Lengths of the data in PDI and PDR must match */ + if ((pdi_len(pdi) == pdr_len(pdr)) && + ((void *) pdi->data + pdi_len(pdi) < pda_end)) { + /* do the actual plugging */ + hw->ops->program(hw, pdi->data, pdr_addr(pdr), + pdi_len(pdi)); + } + } + + pdr++; + } + return 0; +} diff --git a/drivers/net/wireless/intersil/orinoco/hermes_dld.h b/drivers/net/wireless/intersil/orinoco/hermes_dld.h new file mode 100644 index 000000000000..b5377e232c63 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/hermes_dld.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2007, David Kilroy + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + */ +#ifndef _HERMES_DLD_H +#define _HERMES_DLD_H + +#include "hermes.h" + +int hermesi_program_init(struct hermes *hw, u32 offset); +int hermesi_program_end(struct hermes *hw); +int hermes_program(struct hermes *hw, const char *first_block, const void *end); + +int hermes_read_pda(struct hermes *hw, + __le16 *pda, + u32 pda_addr, + u16 pda_len, + int use_eeprom); +int hermes_apply_pda(struct hermes *hw, + const char *first_pdr, + const void *pdr_end, + const __le16 *pda, + const void *pda_end); +int hermes_apply_pda_with_defaults(struct hermes *hw, + const char *first_pdr, + const void *pdr_end, + const __le16 *pda, + const void *pda_end); + +size_t hermes_blocks_length(const char *first_block, const void *end); + +#endif /* _HERMES_DLD_H */ diff --git a/drivers/net/wireless/intersil/orinoco/hermes_rid.h b/drivers/net/wireless/intersil/orinoco/hermes_rid.h new file mode 100644 index 000000000000..42eb67dea1df --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/hermes_rid.h @@ -0,0 +1,165 @@ +#ifndef _HERMES_RID_H +#define _HERMES_RID_H + +/* + * Configuration RIDs + */ +#define HERMES_RID_CNFPORTTYPE 0xFC00 +#define HERMES_RID_CNFOWNMACADDR 0xFC01 +#define HERMES_RID_CNFDESIREDSSID 0xFC02 +#define HERMES_RID_CNFOWNCHANNEL 0xFC03 +#define HERMES_RID_CNFOWNSSID 0xFC04 +#define HERMES_RID_CNFOWNATIMWINDOW 0xFC05 +#define HERMES_RID_CNFSYSTEMSCALE 0xFC06 +#define HERMES_RID_CNFMAXDATALEN 0xFC07 +#define HERMES_RID_CNFWDSADDRESS 0xFC08 +#define HERMES_RID_CNFPMENABLED 0xFC09 +#define HERMES_RID_CNFPMEPS 0xFC0A +#define HERMES_RID_CNFMULTICASTRECEIVE 0xFC0B +#define HERMES_RID_CNFMAXSLEEPDURATION 0xFC0C +#define HERMES_RID_CNFPMHOLDOVERDURATION 0xFC0D +#define HERMES_RID_CNFOWNNAME 0xFC0E +#define HERMES_RID_CNFOWNDTIMPERIOD 0xFC10 +#define HERMES_RID_CNFWDSADDRESS1 0xFC11 +#define HERMES_RID_CNFWDSADDRESS2 0xFC12 +#define HERMES_RID_CNFWDSADDRESS3 0xFC13 +#define HERMES_RID_CNFWDSADDRESS4 0xFC14 +#define HERMES_RID_CNFWDSADDRESS5 0xFC15 +#define HERMES_RID_CNFWDSADDRESS6 0xFC16 +#define HERMES_RID_CNFMULTICASTPMBUFFERING 0xFC17 +#define HERMES_RID_CNFWEPENABLED_AGERE 0xFC20 +#define HERMES_RID_CNFAUTHENTICATION_AGERE 0xFC21 +#define HERMES_RID_CNFMANDATORYBSSID_SYMBOL 0xFC21 +#define HERMES_RID_CNFDROPUNENCRYPTED 0xFC22 +#define HERMES_RID_CNFWEPDEFAULTKEYID 0xFC23 +#define HERMES_RID_CNFDEFAULTKEY0 0xFC24 +#define HERMES_RID_CNFDEFAULTKEY1 0xFC25 +#define HERMES_RID_CNFMWOROBUST_AGERE 0xFC25 +#define HERMES_RID_CNFDEFAULTKEY2 0xFC26 +#define HERMES_RID_CNFDEFAULTKEY3 0xFC27 +#define HERMES_RID_CNFWEPFLAGS_INTERSIL 0xFC28 +#define HERMES_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 +#define HERMES_RID_CNFAUTHENTICATION 0xFC2A +#define HERMES_RID_CNFMAXASSOCSTA 0xFC2B +#define HERMES_RID_CNFKEYLENGTH_SYMBOL 0xFC2B +#define HERMES_RID_CNFTXCONTROL 0xFC2C +#define HERMES_RID_CNFROAMINGMODE 0xFC2D +#define HERMES_RID_CNFHOSTAUTHENTICATION 0xFC2E +#define HERMES_RID_CNFRCVCRCERROR 0xFC30 +#define HERMES_RID_CNFMMLIFE 0xFC31 +#define HERMES_RID_CNFALTRETRYCOUNT 0xFC32 +#define HERMES_RID_CNFBEACONINT 0xFC33 +#define HERMES_RID_CNFAPPCFINFO 0xFC34 +#define HERMES_RID_CNFSTAPCFINFO 0xFC35 +#define HERMES_RID_CNFPRIORITYQUSAGE 0xFC37 +#define HERMES_RID_CNFTIMCTRL 0xFC40 +#define HERMES_RID_CNFTHIRTY2TALLY 0xFC42 +#define HERMES_RID_CNFENHSECURITY 0xFC43 +#define HERMES_RID_CNFGROUPADDRESSES 0xFC80 +#define HERMES_RID_CNFCREATEIBSS 0xFC81 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD 0xFC82 +#define HERMES_RID_CNFRTSTHRESHOLD 0xFC83 +#define HERMES_RID_CNFTXRATECONTROL 0xFC84 +#define HERMES_RID_CNFPROMISCUOUSMODE 0xFC85 +#define HERMES_RID_CNFBASICRATES_SYMBOL 0xFC8A +#define HERMES_RID_CNFPREAMBLE_SYMBOL 0xFC8C +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD0 0xFC90 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD1 0xFC91 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD2 0xFC92 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD3 0xFC93 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD4 0xFC94 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD5 0xFC95 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD6 0xFC96 +#define HERMES_RID_CNFRTSTHRESHOLD0 0xFC97 +#define HERMES_RID_CNFRTSTHRESHOLD1 0xFC98 +#define HERMES_RID_CNFRTSTHRESHOLD2 0xFC99 +#define HERMES_RID_CNFRTSTHRESHOLD3 0xFC9A +#define HERMES_RID_CNFRTSTHRESHOLD4 0xFC9B +#define HERMES_RID_CNFRTSTHRESHOLD5 0xFC9C +#define HERMES_RID_CNFRTSTHRESHOLD6 0xFC9D +#define HERMES_RID_CNFHOSTSCAN_SYMBOL 0xFCAB +#define HERMES_RID_CNFSHORTPREAMBLE 0xFCB0 +#define HERMES_RID_CNFWEPKEYS_AGERE 0xFCB0 +#define HERMES_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 +#define HERMES_RID_CNFTXKEY_AGERE 0xFCB1 +#define HERMES_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 +#define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2 +#define HERMES_RID_CNFBASICRATES 0xFCB3 +#define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4 +#define HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE 0xFCB4 +#define HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE 0xFCB5 +#define HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE 0xFCB6 +#define HERMES_RID_CNFADDMAPPEDTKIPKEY_AGERE 0xFCB7 +#define HERMES_RID_CNFREMMAPPEDTKIPKEY_AGERE 0xFCB8 +#define HERMES_RID_CNFSETWPACAPABILITIES_AGERE 0xFCB9 +#define HERMES_RID_CNFCACHEDPMKADDRESS 0xFCBA +#define HERMES_RID_CNFREMOVEPMKADDRESS 0xFCBB +#define HERMES_RID_CNFSCANCHANNELS2GHZ 0xFCC2 +#define HERMES_RID_CNFDISASSOCIATE 0xFCC8 +#define HERMES_RID_CNFTICKTIME 0xFCE0 +#define HERMES_RID_CNFSCANREQUEST 0xFCE1 +#define HERMES_RID_CNFJOINREQUEST 0xFCE2 +#define HERMES_RID_CNFAUTHENTICATESTATION 0xFCE3 +#define HERMES_RID_CNFCHANNELINFOREQUEST 0xFCE4 +#define HERMES_RID_CNFHOSTSCAN 0xFCE5 + +/* + * Information RIDs + */ +#define HERMES_RID_MAXLOADTIME 0xFD00 +#define HERMES_RID_DOWNLOADBUFFER 0xFD01 +#define HERMES_RID_PRIID 0xFD02 +#define HERMES_RID_PRISUPRANGE 0xFD03 +#define HERMES_RID_CFIACTRANGES 0xFD04 +#define HERMES_RID_NICSERNUM 0xFD0A +#define HERMES_RID_NICID 0xFD0B +#define HERMES_RID_MFISUPRANGE 0xFD0C +#define HERMES_RID_CFISUPRANGE 0xFD0D +#define HERMES_RID_CHANNELLIST 0xFD10 +#define HERMES_RID_REGULATORYDOMAINS 0xFD11 +#define HERMES_RID_TEMPTYPE 0xFD12 +#define HERMES_RID_CIS 0xFD13 +#define HERMES_RID_STAID 0xFD20 +#define HERMES_RID_STASUPRANGE 0xFD21 +#define HERMES_RID_MFIACTRANGES 0xFD22 +#define HERMES_RID_CFIACTRANGES2 0xFD23 +#define HERMES_RID_SECONDARYVERSION_SYMBOL 0xFD24 +#define HERMES_RID_PORTSTATUS 0xFD40 +#define HERMES_RID_CURRENTSSID 0xFD41 +#define HERMES_RID_CURRENTBSSID 0xFD42 +#define HERMES_RID_COMMSQUALITY 0xFD43 +#define HERMES_RID_CURRENTTXRATE 0xFD44 +#define HERMES_RID_CURRENTBEACONINTERVAL 0xFD45 +#define HERMES_RID_CURRENTSCALETHRESHOLDS 0xFD46 +#define HERMES_RID_PROTOCOLRSPTIME 0xFD47 +#define HERMES_RID_SHORTRETRYLIMIT 0xFD48 +#define HERMES_RID_LONGRETRYLIMIT 0xFD49 +#define HERMES_RID_MAXTRANSMITLIFETIME 0xFD4A +#define HERMES_RID_MAXRECEIVELIFETIME 0xFD4B +#define HERMES_RID_CFPOLLABLE 0xFD4C +#define HERMES_RID_AUTHENTICATIONALGORITHMS 0xFD4D +#define HERMES_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F +#define HERMES_RID_DBMCOMMSQUALITY_INTERSIL 0xFD51 +#define HERMES_RID_CURRENTTXRATE1 0xFD80 +#define HERMES_RID_CURRENTTXRATE2 0xFD81 +#define HERMES_RID_CURRENTTXRATE3 0xFD82 +#define HERMES_RID_CURRENTTXRATE4 0xFD83 +#define HERMES_RID_CURRENTTXRATE5 0xFD84 +#define HERMES_RID_CURRENTTXRATE6 0xFD85 +#define HERMES_RID_OWNMACADDR 0xFD86 +#define HERMES_RID_SCANRESULTSTABLE 0xFD88 +#define HERMES_RID_CURRENT_COUNTRY_INFO 0xFD89 +#define HERMES_RID_CURRENT_WPA_IE 0xFD8A +#define HERMES_RID_CURRENT_TKIP_IV 0xFD8B +#define HERMES_RID_CURRENT_ASSOC_REQ_INFO 0xFD8C +#define HERMES_RID_CURRENT_ASSOC_RESP_INFO 0xFD8D +#define HERMES_RID_TXQUEUEEMPTY 0xFD91 +#define HERMES_RID_PHYTYPE 0xFDC0 +#define HERMES_RID_CURRENTCHANNEL 0xFDC1 +#define HERMES_RID_CURRENTPOWERSTATE 0xFDC2 +#define HERMES_RID_CCAMODE 0xFDC3 +#define HERMES_RID_SUPPORTEDDATARATES 0xFDC6 +#define HERMES_RID_BUILDSEQ 0xFFFE +#define HERMES_RID_FWID 0xFFFF + +#endif diff --git a/drivers/net/wireless/intersil/orinoco/hw.c b/drivers/net/wireless/intersil/orinoco/hw.c new file mode 100644 index 000000000000..e27e32851f1e --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/hw.c @@ -0,0 +1,1356 @@ +/* Encapsulate basic setting changes and retrieval on Hermes hardware + * + * See copyright notice in main.c + */ +#include +#include +#include +#include +#include +#include +#include "hermes.h" +#include "hermes_rid.h" +#include "orinoco.h" + +#include "hw.h" + +#define SYMBOL_MAX_VER_LEN (14) + +/* Symbol firmware has a bug allocating buffers larger than this */ +#define TX_NICBUF_SIZE_BUG 1585 + +/********************************************************************/ +/* Data tables */ +/********************************************************************/ + +/* This tables gives the actual meanings of the bitrate IDs returned + * by the firmware. */ +static const struct { + int bitrate; /* in 100s of kilobits */ + int automatic; + u16 agere_txratectrl; + u16 intersil_txratectrl; +} bitrate_table[] = { + {110, 1, 3, 15}, /* Entry 0 is the default */ + {10, 0, 1, 1}, + {10, 1, 1, 1}, + {20, 0, 2, 2}, + {20, 1, 6, 3}, + {55, 0, 4, 4}, + {55, 1, 7, 7}, + {110, 0, 5, 8}, +}; +#define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table) + +/* Firmware version encoding */ +struct comp_id { + u16 id, variant, major, minor; +} __packed; + +static inline enum fwtype determine_firmware_type(struct comp_id *nic_id) +{ + if (nic_id->id < 0x8000) + return FIRMWARE_TYPE_AGERE; + else if (nic_id->id == 0x8000 && nic_id->major == 0) + return FIRMWARE_TYPE_SYMBOL; + else + return FIRMWARE_TYPE_INTERSIL; +} + +/* Set priv->firmware type, determine firmware properties + * This function can be called before we have registerred with netdev, + * so all errors go out with dev_* rather than printk + * + * If non-NULL stores a firmware description in fw_name. + * If non-NULL stores a HW version in hw_ver + * + * These are output via generic cfg80211 ethtool support. + */ +int determine_fw_capabilities(struct orinoco_private *priv, + char *fw_name, size_t fw_name_len, + u32 *hw_ver) +{ + struct device *dev = priv->dev; + struct hermes *hw = &priv->hw; + int err; + struct comp_id nic_id, sta_id; + unsigned int firmver; + char tmp[SYMBOL_MAX_VER_LEN + 1] __attribute__((aligned(2))); + + /* Get the hardware version */ + err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id); + if (err) { + dev_err(dev, "Cannot read hardware identity: error %d\n", + err); + return err; + } + + le16_to_cpus(&nic_id.id); + le16_to_cpus(&nic_id.variant); + le16_to_cpus(&nic_id.major); + le16_to_cpus(&nic_id.minor); + dev_info(dev, "Hardware identity %04x:%04x:%04x:%04x\n", + nic_id.id, nic_id.variant, nic_id.major, nic_id.minor); + + if (hw_ver) + *hw_ver = (((nic_id.id & 0xff) << 24) | + ((nic_id.variant & 0xff) << 16) | + ((nic_id.major & 0xff) << 8) | + (nic_id.minor & 0xff)); + + priv->firmware_type = determine_firmware_type(&nic_id); + + /* Get the firmware version */ + err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id); + if (err) { + dev_err(dev, "Cannot read station identity: error %d\n", + err); + return err; + } + + le16_to_cpus(&sta_id.id); + le16_to_cpus(&sta_id.variant); + le16_to_cpus(&sta_id.major); + le16_to_cpus(&sta_id.minor); + dev_info(dev, "Station identity %04x:%04x:%04x:%04x\n", + sta_id.id, sta_id.variant, sta_id.major, sta_id.minor); + + switch (sta_id.id) { + case 0x15: + dev_err(dev, "Primary firmware is active\n"); + return -ENODEV; + case 0x14b: + dev_err(dev, "Tertiary firmware is active\n"); + return -ENODEV; + case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */ + case 0x21: /* Symbol Spectrum24 Trilogy */ + break; + default: + dev_notice(dev, "Unknown station ID, please report\n"); + break; + } + + /* Default capabilities */ + priv->has_sensitivity = 1; + priv->has_mwo = 0; + priv->has_preamble = 0; + priv->has_port3 = 1; + priv->has_ibss = 1; + priv->has_wep = 0; + priv->has_big_wep = 0; + priv->has_alt_txcntl = 0; + priv->has_ext_scan = 0; + priv->has_wpa = 0; + priv->do_fw_download = 0; + + /* Determine capabilities from the firmware version */ + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: + /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout, + ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */ + if (fw_name) + snprintf(fw_name, fw_name_len, "Lucent/Agere %d.%02d", + sta_id.major, sta_id.minor); + + firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; + + priv->has_ibss = (firmver >= 0x60006); + priv->has_wep = (firmver >= 0x40020); + priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell + Gold cards from the others? */ + priv->has_mwo = (firmver >= 0x60000); + priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ + priv->ibss_port = 1; + priv->has_hostscan = (firmver >= 0x8000a); + priv->do_fw_download = 1; + priv->broken_monitor = (firmver >= 0x80000); + priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ + priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */ + priv->has_wpa = (firmver >= 0x9002a); + /* Tested with Agere firmware : + * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II + * Tested CableTron firmware : 4.32 => Anton */ + break; + case FIRMWARE_TYPE_SYMBOL: + /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */ + /* Intel MAC : 00:02:B3:* */ + /* 3Com MAC : 00:50:DA:* */ + memset(tmp, 0, sizeof(tmp)); + /* Get the Symbol firmware version */ + err = hw->ops->read_ltv(hw, USER_BAP, + HERMES_RID_SECONDARYVERSION_SYMBOL, + SYMBOL_MAX_VER_LEN, NULL, &tmp); + if (err) { + dev_warn(dev, "Error %d reading Symbol firmware info. " + "Wildly guessing capabilities...\n", err); + firmver = 0; + tmp[0] = '\0'; + } else { + /* The firmware revision is a string, the format is + * something like : "V2.20-01". + * Quick and dirty parsing... - Jean II + */ + firmver = ((tmp[1] - '0') << 16) + | ((tmp[3] - '0') << 12) + | ((tmp[4] - '0') << 8) + | ((tmp[6] - '0') << 4) + | (tmp[7] - '0'); + + tmp[SYMBOL_MAX_VER_LEN] = '\0'; + } + + if (fw_name) + snprintf(fw_name, fw_name_len, "Symbol %s", tmp); + + priv->has_ibss = (firmver >= 0x20000); + priv->has_wep = (firmver >= 0x15012); + priv->has_big_wep = (firmver >= 0x20000); + priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) || + (firmver >= 0x29000 && firmver < 0x30000) || + firmver >= 0x31000; + priv->has_preamble = (firmver >= 0x20000); + priv->ibss_port = 4; + + /* Symbol firmware is found on various cards, but + * there has been no attempt to check firmware + * download on non-spectrum_cs based cards. + * + * Given that the Agere firmware download works + * differently, we should avoid doing a firmware + * download with the Symbol algorithm on non-spectrum + * cards. + * + * For now we can identify a spectrum_cs based card + * because it has a firmware reset function. + */ + priv->do_fw_download = (priv->stop_fw != NULL); + + priv->broken_disableport = (firmver == 0x25013) || + (firmver >= 0x30000 && firmver <= 0x31000); + priv->has_hostscan = (firmver >= 0x31001) || + (firmver >= 0x29057 && firmver < 0x30000); + /* Tested with Intel firmware : 0x20015 => Jean II */ + /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ + break; + case FIRMWARE_TYPE_INTERSIL: + /* D-Link, Linksys, Adtron, ZoomAir, and many others... + * Samsung, Compaq 100/200 and Proxim are slightly + * different and less well tested */ + /* D-Link MAC : 00:40:05:* */ + /* Addtron MAC : 00:90:D1:* */ + if (fw_name) + snprintf(fw_name, fw_name_len, "Intersil %d.%d.%d", + sta_id.major, sta_id.minor, sta_id.variant); + + firmver = ((unsigned long)sta_id.major << 16) | + ((unsigned long)sta_id.minor << 8) | sta_id.variant; + + priv->has_ibss = (firmver >= 0x000700); /* FIXME */ + priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); + priv->has_pm = (firmver >= 0x000700); + priv->has_hostscan = (firmver >= 0x010301); + + if (firmver >= 0x000800) + priv->ibss_port = 0; + else { + dev_notice(dev, "Intersil firmware earlier than v0.8.x" + " - several features not supported\n"); + priv->ibss_port = 1; + } + break; + } + if (fw_name) + dev_info(dev, "Firmware determined as %s\n", fw_name); + +#ifndef CONFIG_HERMES_PRISM + if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { + dev_err(dev, "Support for Prism chipset is not enabled\n"); + return -ENODEV; + } +#endif + + return 0; +} + +/* Read settings from EEPROM into our private structure. + * MAC address gets dropped into callers buffer + * Can be called before netdev registration. + */ +int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr) +{ + struct device *dev = priv->dev; + struct hermes_idstring nickbuf; + struct hermes *hw = &priv->hw; + int len; + int err; + u16 reclen; + + /* Get the MAC address */ + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, + ETH_ALEN, NULL, dev_addr); + if (err) { + dev_warn(dev, "Failed to read MAC address!\n"); + goto out; + } + + dev_dbg(dev, "MAC address %pM\n", dev_addr); + + /* Get the station name */ + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, + sizeof(nickbuf), &reclen, &nickbuf); + if (err) { + dev_err(dev, "failed to read station name\n"); + goto out; + } + if (nickbuf.len) + len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len)); + else + len = min(IW_ESSID_MAX_SIZE, 2 * reclen); + memcpy(priv->nick, &nickbuf.val, len); + priv->nick[len] = '\0'; + + dev_dbg(dev, "Station name \"%s\"\n", priv->nick); + + /* Get allowed channels */ + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST, + &priv->channel_mask); + if (err) { + dev_err(dev, "Failed to read channel list!\n"); + goto out; + } + + /* Get initial AP density */ + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, + &priv->ap_density); + if (err || priv->ap_density < 1 || priv->ap_density > 3) + priv->has_sensitivity = 0; + + /* Get initial RTS threshold */ + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, + &priv->rts_thresh); + if (err) { + dev_err(dev, "Failed to read RTS threshold!\n"); + goto out; + } + + /* Get initial fragmentation settings */ + if (priv->has_mwo) + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFMWOROBUST_AGERE, + &priv->mwo_robust); + else + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, + &priv->frag_thresh); + if (err) { + dev_err(dev, "Failed to read fragmentation settings!\n"); + goto out; + } + + /* Power management setup */ + if (priv->has_pm) { + priv->pm_on = 0; + priv->pm_mcast = 1; + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFMAXSLEEPDURATION, + &priv->pm_period); + if (err) { + dev_err(dev, "Failed to read power management " + "period!\n"); + goto out; + } + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFPMHOLDOVERDURATION, + &priv->pm_timeout); + if (err) { + dev_err(dev, "Failed to read power management " + "timeout!\n"); + goto out; + } + } + + /* Preamble setup */ + if (priv->has_preamble) { + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFPREAMBLE_SYMBOL, + &priv->preamble); + if (err) { + dev_err(dev, "Failed to read preamble setup\n"); + goto out; + } + } + + /* Retry settings */ + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, + &priv->short_retry_limit); + if (err) { + dev_err(dev, "Failed to read short retry limit\n"); + goto out; + } + + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT, + &priv->long_retry_limit); + if (err) { + dev_err(dev, "Failed to read long retry limit\n"); + goto out; + } + + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME, + &priv->retry_lifetime); + if (err) { + dev_err(dev, "Failed to read max retry lifetime\n"); + goto out; + } + +out: + return err; +} + +/* Can be called before netdev registration */ +int orinoco_hw_allocate_fid(struct orinoco_private *priv) +{ + struct device *dev = priv->dev; + struct hermes *hw = &priv->hw; + int err; + + err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid); + if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) { + /* Try workaround for old Symbol firmware bug */ + priv->nicbuf_size = TX_NICBUF_SIZE_BUG; + err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid); + + dev_warn(dev, "Firmware ALLOC bug detected " + "(old Symbol firmware?). Work around %s\n", + err ? "failed!" : "ok."); + } + + return err; +} + +int orinoco_get_bitratemode(int bitrate, int automatic) +{ + int ratemode = -1; + int i; + + if ((bitrate != 10) && (bitrate != 20) && + (bitrate != 55) && (bitrate != 110)) + return ratemode; + + for (i = 0; i < BITRATE_TABLE_SIZE; i++) { + if ((bitrate_table[i].bitrate == bitrate) && + (bitrate_table[i].automatic == automatic)) { + ratemode = i; + break; + } + } + return ratemode; +} + +void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic) +{ + BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE)); + + *bitrate = bitrate_table[ratemode].bitrate * 100000; + *automatic = bitrate_table[ratemode].automatic; +} + +int orinoco_hw_program_rids(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + struct wireless_dev *wdev = netdev_priv(dev); + struct hermes *hw = &priv->hw; + int err; + struct hermes_idstring idbuf; + + /* Set the MAC address */ + err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, + HERMES_BYTES_TO_RECLEN(ETH_ALEN), + dev->dev_addr); + if (err) { + printk(KERN_ERR "%s: Error %d setting MAC address\n", + dev->name, err); + return err; + } + + /* Set up the link mode */ + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, + priv->port_type); + if (err) { + printk(KERN_ERR "%s: Error %d setting port type\n", + dev->name, err); + return err; + } + /* Set the channel/frequency */ + if (priv->channel != 0 && priv->iw_mode != NL80211_IFTYPE_STATION) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFOWNCHANNEL, + priv->channel); + if (err) { + printk(KERN_ERR "%s: Error %d setting channel %d\n", + dev->name, err, priv->channel); + return err; + } + } + + if (priv->has_ibss) { + u16 createibss; + + if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) { + printk(KERN_WARNING "%s: This firmware requires an " + "ESSID in IBSS-Ad-Hoc mode.\n", dev->name); + /* With wvlan_cs, in this case, we would crash. + * hopefully, this driver will behave better... + * Jean II */ + createibss = 0; + } else { + createibss = priv->createibss; + } + + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFCREATEIBSS, + createibss); + if (err) { + printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", + dev->name, err); + return err; + } + } + + /* Set the desired BSSID */ + err = __orinoco_hw_set_wap(priv); + if (err) { + printk(KERN_ERR "%s: Error %d setting AP address\n", + dev->name, err); + return err; + } + + /* Set the desired ESSID */ + idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); + memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); + /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ + err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, + HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2), + &idbuf); + if (err) { + printk(KERN_ERR "%s: Error %d setting OWNSSID\n", + dev->name, err); + return err; + } + err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, + HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2), + &idbuf); + if (err) { + printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", + dev->name, err); + return err; + } + + /* Set the station name */ + idbuf.len = cpu_to_le16(strlen(priv->nick)); + memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val)); + err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, + HERMES_BYTES_TO_RECLEN(strlen(priv->nick) + 2), + &idbuf); + if (err) { + printk(KERN_ERR "%s: Error %d setting nickname\n", + dev->name, err); + return err; + } + + /* Set AP density */ + if (priv->has_sensitivity) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFSYSTEMSCALE, + priv->ap_density); + if (err) { + printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " + "Disabling sensitivity control\n", + dev->name, err); + + priv->has_sensitivity = 0; + } + } + + /* Set RTS threshold */ + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, + priv->rts_thresh); + if (err) { + printk(KERN_ERR "%s: Error %d setting RTS threshold\n", + dev->name, err); + return err; + } + + /* Set fragmentation threshold or MWO robustness */ + if (priv->has_mwo) + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFMWOROBUST_AGERE, + priv->mwo_robust); + else + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, + priv->frag_thresh); + if (err) { + printk(KERN_ERR "%s: Error %d setting fragmentation\n", + dev->name, err); + return err; + } + + /* Set bitrate */ + err = __orinoco_hw_set_bitrate(priv); + if (err) { + printk(KERN_ERR "%s: Error %d setting bitrate\n", + dev->name, err); + return err; + } + + /* Set power management */ + if (priv->has_pm) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFPMENABLED, + priv->pm_on); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFMULTICASTRECEIVE, + priv->pm_mcast); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFMAXSLEEPDURATION, + priv->pm_period); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFPMHOLDOVERDURATION, + priv->pm_timeout); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + } + + /* Set preamble - only for Symbol so far... */ + if (priv->has_preamble) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFPREAMBLE_SYMBOL, + priv->preamble); + if (err) { + printk(KERN_ERR "%s: Error %d setting preamble\n", + dev->name, err); + return err; + } + } + + /* Set up encryption */ + if (priv->has_wep || priv->has_wpa) { + err = __orinoco_hw_setup_enc(priv); + if (err) { + printk(KERN_ERR "%s: Error %d activating encryption\n", + dev->name, err); + return err; + } + } + + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { + /* Enable monitor mode */ + dev->type = ARPHRD_IEEE80211; + err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | + HERMES_TEST_MONITOR, 0, NULL); + } else { + /* Disable monitor mode */ + dev->type = ARPHRD_ETHER; + err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | + HERMES_TEST_STOP, 0, NULL); + } + if (err) + return err; + + /* Reset promiscuity / multicast*/ + priv->promiscuous = 0; + priv->mc_count = 0; + + /* Record mode change */ + wdev->iftype = priv->iw_mode; + + return 0; +} + +/* Get tsc from the firmware */ +int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc) +{ + struct hermes *hw = &priv->hw; + int err = 0; + u8 tsc_arr[4][ORINOCO_SEQ_LEN]; + + if ((key < 0) || (key >= 4)) + return -EINVAL; + + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV, + sizeof(tsc_arr), NULL, &tsc_arr); + if (!err) + memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0])); + + return err; +} + +int __orinoco_hw_set_bitrate(struct orinoco_private *priv) +{ + struct hermes *hw = &priv->hw; + int ratemode = priv->bitratemode; + int err = 0; + + if (ratemode >= BITRATE_TABLE_SIZE) { + printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", + priv->ndev->name, ratemode); + return -EINVAL; + } + + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFTXRATECONTROL, + bitrate_table[ratemode].agere_txratectrl); + break; + case FIRMWARE_TYPE_INTERSIL: + case FIRMWARE_TYPE_SYMBOL: + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFTXRATECONTROL, + bitrate_table[ratemode].intersil_txratectrl); + break; + default: + BUG(); + } + + return err; +} + +int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate) +{ + struct hermes *hw = &priv->hw; + int i; + int err = 0; + u16 val; + + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CURRENTTXRATE, &val); + if (err) + return err; + + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: /* Lucent style rate */ + /* Note : in Lucent firmware, the return value of + * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s, + * and therefore is totally different from the + * encoding of HERMES_RID_CNFTXRATECONTROL. + * Don't forget that 6Mb/s is really 5.5Mb/s */ + if (val == 6) + *bitrate = 5500000; + else + *bitrate = val * 1000000; + break; + case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */ + case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */ + for (i = 0; i < BITRATE_TABLE_SIZE; i++) + if (bitrate_table[i].intersil_txratectrl == val) { + *bitrate = bitrate_table[i].bitrate * 100000; + break; + } + + if (i >= BITRATE_TABLE_SIZE) { + printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n", + priv->ndev->name, val); + err = -EIO; + } + + break; + default: + BUG(); + } + + return err; +} + +/* Set fixed AP address */ +int __orinoco_hw_set_wap(struct orinoco_private *priv) +{ + int roaming_flag; + int err = 0; + struct hermes *hw = &priv->hw; + + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: + /* not supported */ + break; + case FIRMWARE_TYPE_INTERSIL: + if (priv->bssid_fixed) + roaming_flag = 2; + else + roaming_flag = 1; + + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFROAMINGMODE, + roaming_flag); + break; + case FIRMWARE_TYPE_SYMBOL: + err = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFMANDATORYBSSID_SYMBOL, + &priv->desired_bssid); + break; + } + return err; +} + +/* Change the WEP keys and/or the current keys. Can be called + * either from __orinoco_hw_setup_enc() or directly from + * orinoco_ioctl_setiwencode(). In the later case the association + * with the AP is not broken (if the firmware can handle it), + * which is needed for 802.1x implementations. */ +int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) +{ + struct hermes *hw = &priv->hw; + int err = 0; + int i; + + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: + { + struct orinoco_key keys[ORINOCO_MAX_KEYS]; + + memset(&keys, 0, sizeof(keys)); + for (i = 0; i < ORINOCO_MAX_KEYS; i++) { + int len = min(priv->keys[i].key_len, + ORINOCO_MAX_KEY_SIZE); + memcpy(&keys[i].data, priv->keys[i].key, len); + if (len > SMALL_KEY_SIZE) + keys[i].len = cpu_to_le16(LARGE_KEY_SIZE); + else if (len > 0) + keys[i].len = cpu_to_le16(SMALL_KEY_SIZE); + else + keys[i].len = cpu_to_le16(0); + } + + err = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFWEPKEYS_AGERE, + &keys); + if (err) + return err; + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFTXKEY_AGERE, + priv->tx_key); + if (err) + return err; + break; + } + case FIRMWARE_TYPE_INTERSIL: + case FIRMWARE_TYPE_SYMBOL: + { + int keylen; + + /* Force uniform key length to work around + * firmware bugs */ + keylen = priv->keys[priv->tx_key].key_len; + + if (keylen > LARGE_KEY_SIZE) { + printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", + priv->ndev->name, priv->tx_key, keylen); + return -E2BIG; + } else if (keylen > SMALL_KEY_SIZE) + keylen = LARGE_KEY_SIZE; + else if (keylen > 0) + keylen = SMALL_KEY_SIZE; + else + keylen = 0; + + /* Write all 4 keys */ + for (i = 0; i < ORINOCO_MAX_KEYS; i++) { + u8 key[LARGE_KEY_SIZE] = { 0 }; + + memcpy(key, priv->keys[i].key, + priv->keys[i].key_len); + + err = hw->ops->write_ltv(hw, USER_BAP, + HERMES_RID_CNFDEFAULTKEY0 + i, + HERMES_BYTES_TO_RECLEN(keylen), + key); + if (err) + return err; + } + + /* Write the index of the key used in transmission */ + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFWEPDEFAULTKEYID, + priv->tx_key); + if (err) + return err; + } + break; + } + + return 0; +} + +int __orinoco_hw_setup_enc(struct orinoco_private *priv) +{ + struct hermes *hw = &priv->hw; + int err = 0; + int master_wep_flag; + int auth_flag; + int enc_flag; + + /* Setup WEP keys */ + if (priv->encode_alg == ORINOCO_ALG_WEP) + __orinoco_hw_setup_wepkeys(priv); + + if (priv->wep_restrict) + auth_flag = HERMES_AUTH_SHARED_KEY; + else + auth_flag = HERMES_AUTH_OPEN; + + if (priv->wpa_enabled) + enc_flag = 2; + else if (priv->encode_alg == ORINOCO_ALG_WEP) + enc_flag = 1; + else + enc_flag = 0; + + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ + if (priv->encode_alg == ORINOCO_ALG_WEP) { + /* Enable the shared-key authentication. */ + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFAUTHENTICATION_AGERE, + auth_flag); + } + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFWEPENABLED_AGERE, + enc_flag); + if (err) + return err; + + if (priv->has_wpa) { + /* Set WPA key management */ + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE, + priv->key_mgmt); + if (err) + return err; + } + + break; + + case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */ + case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */ + if (priv->encode_alg == ORINOCO_ALG_WEP) { + if (priv->wep_restrict || + (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)) + master_wep_flag = HERMES_WEP_PRIVACY_INVOKED | + HERMES_WEP_EXCL_UNENCRYPTED; + else + master_wep_flag = HERMES_WEP_PRIVACY_INVOKED; + + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFAUTHENTICATION, + auth_flag); + if (err) + return err; + } else + master_wep_flag = 0; + + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) + master_wep_flag |= HERMES_WEP_HOST_DECRYPT; + + /* Master WEP setting : on/off */ + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFWEPFLAGS_INTERSIL, + master_wep_flag); + if (err) + return err; + + break; + } + + return 0; +} + +/* key must be 32 bytes, including the tx and rx MIC keys. + * rsc must be NULL or up to 8 bytes + * tsc must be NULL or up to 8 bytes + */ +int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, + int set_tx, const u8 *key, const u8 *rsc, + size_t rsc_len, const u8 *tsc, size_t tsc_len) +{ + struct { + __le16 idx; + u8 rsc[ORINOCO_SEQ_LEN]; + u8 key[TKIP_KEYLEN]; + u8 tx_mic[MIC_KEYLEN]; + u8 rx_mic[MIC_KEYLEN]; + u8 tsc[ORINOCO_SEQ_LEN]; + } __packed buf; + struct hermes *hw = &priv->hw; + int ret; + int err; + int k; + u16 xmitting; + + key_idx &= 0x3; + + if (set_tx) + key_idx |= 0x8000; + + buf.idx = cpu_to_le16(key_idx); + memcpy(buf.key, key, + sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic)); + + if (rsc_len > sizeof(buf.rsc)) + rsc_len = sizeof(buf.rsc); + + if (tsc_len > sizeof(buf.tsc)) + tsc_len = sizeof(buf.tsc); + + memset(buf.rsc, 0, sizeof(buf.rsc)); + memset(buf.tsc, 0, sizeof(buf.tsc)); + + if (rsc != NULL) + memcpy(buf.rsc, rsc, rsc_len); + + if (tsc != NULL) + memcpy(buf.tsc, tsc, tsc_len); + else + buf.tsc[4] = 0x10; + + /* Wait up to 100ms for tx queue to empty */ + for (k = 100; k > 0; k--) { + udelay(1000); + ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY, + &xmitting); + if (ret || !xmitting) + break; + } + + if (k == 0) + ret = -ETIMEDOUT; + + err = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE, + &buf); + + return ret ? ret : err; +} + +int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx) +{ + struct hermes *hw = &priv->hw; + int err; + + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE, + key_idx); + if (err) + printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n", + priv->ndev->name, err, key_idx); + return err; +} + +int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, + struct net_device *dev, + int mc_count, int promisc) +{ + struct hermes *hw = &priv->hw; + int err = 0; + + if (promisc != priv->promiscuous) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFPROMISCUOUSMODE, + promisc); + if (err) { + printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n", + priv->ndev->name, err); + } else + priv->promiscuous = promisc; + } + + /* If we're not in promiscuous mode, then we need to set the + * group address if either we want to multicast, or if we were + * multicasting and want to stop */ + if (!promisc && (mc_count || priv->mc_count)) { + struct netdev_hw_addr *ha; + struct hermes_multicast mclist; + int i = 0; + + netdev_for_each_mc_addr(ha, dev) { + if (i == mc_count) + break; + memcpy(mclist.addr[i++], ha->addr, ETH_ALEN); + } + + err = hw->ops->write_ltv(hw, USER_BAP, + HERMES_RID_CNFGROUPADDRESSES, + HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN), + &mclist); + if (err) + printk(KERN_ERR "%s: Error %d setting multicast list.\n", + priv->ndev->name, err); + else + priv->mc_count = mc_count; + } + return err; +} + +/* Return : < 0 -> error code ; >= 0 -> length */ +int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, + char buf[IW_ESSID_MAX_SIZE + 1]) +{ + struct hermes *hw = &priv->hw; + int err = 0; + struct hermes_idstring essidbuf; + char *p = (char *)(&essidbuf.val); + int len; + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + if (strlen(priv->desired_essid) > 0) { + /* We read the desired SSID from the hardware rather + than from priv->desired_essid, just in case the + firmware is allowed to change it on us. I'm not + sure about this */ + /* My guess is that the OWNSSID should always be whatever + * we set to the card, whereas CURRENT_SSID is the one that + * may change... - Jean II */ + u16 rid; + + *active = 1; + + rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID : + HERMES_RID_CNFDESIREDSSID; + + err = hw->ops->read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), + NULL, &essidbuf); + if (err) + goto fail_unlock; + } else { + *active = 0; + + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, + sizeof(essidbuf), NULL, &essidbuf); + if (err) + goto fail_unlock; + } + + len = le16_to_cpu(essidbuf.len); + BUG_ON(len > IW_ESSID_MAX_SIZE); + + memset(buf, 0, IW_ESSID_MAX_SIZE); + memcpy(buf, p, len); + err = len; + + fail_unlock: + orinoco_unlock(priv, &flags); + + return err; +} + +int orinoco_hw_get_freq(struct orinoco_private *priv) +{ + struct hermes *hw = &priv->hw; + int err = 0; + u16 channel; + int freq = 0; + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, + &channel); + if (err) + goto out; + + /* Intersil firmware 1.3.5 returns 0 when the interface is down */ + if (channel == 0) { + err = -EBUSY; + goto out; + } + + if ((channel < 1) || (channel > NUM_CHANNELS)) { + printk(KERN_WARNING "%s: Channel out of range (%d)!\n", + priv->ndev->name, channel); + err = -EBUSY; + goto out; + + } + freq = ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ); + + out: + orinoco_unlock(priv, &flags); + + if (err > 0) + err = -EBUSY; + return err ? err : freq; +} + +int orinoco_hw_get_bitratelist(struct orinoco_private *priv, + int *numrates, s32 *rates, int max) +{ + struct hermes *hw = &priv->hw; + struct hermes_idstring list; + unsigned char *p = (unsigned char *)&list.val; + int err = 0; + int num; + int i; + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, + sizeof(list), NULL, &list); + orinoco_unlock(priv, &flags); + + if (err) + return err; + + num = le16_to_cpu(list.len); + *numrates = num; + num = min(num, max); + + for (i = 0; i < num; i++) + rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */ + + return 0; +} + +int orinoco_hw_trigger_scan(struct orinoco_private *priv, + const struct cfg80211_ssid *ssid) +{ + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + unsigned long flags; + int err = 0; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + /* Scanning with port 0 disabled would fail */ + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + + /* In monitor mode, the scan results are always empty. + * Probe responses are passed to the driver as received + * frames and could be processed in software. */ + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { + err = -EOPNOTSUPP; + goto out; + } + + if (priv->has_hostscan) { + switch (priv->firmware_type) { + case FIRMWARE_TYPE_SYMBOL: + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFHOSTSCAN_SYMBOL, + HERMES_HOSTSCAN_SYMBOL_ONCE | + HERMES_HOSTSCAN_SYMBOL_BCAST); + break; + case FIRMWARE_TYPE_INTERSIL: { + __le16 req[3]; + + req[0] = cpu_to_le16(0x3fff); /* All channels */ + req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ + req[2] = 0; /* Any ESSID */ + err = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFHOSTSCAN, &req); + break; + } + case FIRMWARE_TYPE_AGERE: + if (ssid->ssid_len > 0) { + struct hermes_idstring idbuf; + size_t len = ssid->ssid_len; + + idbuf.len = cpu_to_le16(len); + memcpy(idbuf.val, ssid->ssid, len); + + err = hw->ops->write_ltv(hw, USER_BAP, + HERMES_RID_CNFSCANSSID_AGERE, + HERMES_BYTES_TO_RECLEN(len + 2), + &idbuf); + } else + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFSCANSSID_AGERE, + 0); /* Any ESSID */ + if (err) + break; + + if (priv->has_ext_scan) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFSCANCHANNELS2GHZ, + 0x7FFF); + if (err) + goto out; + + err = hermes_inquire(hw, + HERMES_INQ_CHANNELINFO); + } else + err = hermes_inquire(hw, HERMES_INQ_SCAN); + + break; + } + } else + err = hermes_inquire(hw, HERMES_INQ_SCAN); + + out: + orinoco_unlock(priv, &flags); + + return err; +} + +/* Disassociate from node with BSSID addr */ +int orinoco_hw_disassociate(struct orinoco_private *priv, + u8 *addr, u16 reason_code) +{ + struct hermes *hw = &priv->hw; + int err; + + struct { + u8 addr[ETH_ALEN]; + __le16 reason_code; + } __packed buf; + + /* Currently only supported by WPA enabled Agere fw */ + if (!priv->has_wpa) + return -EOPNOTSUPP; + + memcpy(buf.addr, addr, ETH_ALEN); + buf.reason_code = cpu_to_le16(reason_code); + err = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFDISASSOCIATE, + &buf); + return err; +} + +int orinoco_hw_get_current_bssid(struct orinoco_private *priv, + u8 *addr) +{ + struct hermes *hw = &priv->hw; + int err; + + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, + ETH_ALEN, NULL, addr); + + return err; +} diff --git a/drivers/net/wireless/intersil/orinoco/hw.h b/drivers/net/wireless/intersil/orinoco/hw.h new file mode 100644 index 000000000000..466d1ede76f1 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/hw.h @@ -0,0 +1,59 @@ +/* Encapsulate basic setting changes on Hermes hardware + * + * See copyright notice in main.c + */ +#ifndef _ORINOCO_HW_H_ +#define _ORINOCO_HW_H_ + +#include +#include +#include + +/* Hardware BAPs */ +#define USER_BAP 0 +#define IRQ_BAP 1 + +/* WEP key sizes */ +#define SMALL_KEY_SIZE 5 +#define LARGE_KEY_SIZE 13 + +/* Number of supported channels */ +#define NUM_CHANNELS 14 + +/* Forward declarations */ +struct orinoco_private; + +int determine_fw_capabilities(struct orinoco_private *priv, char *fw_name, + size_t fw_name_len, u32 *hw_ver); +int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr); +int orinoco_hw_allocate_fid(struct orinoco_private *priv); +int orinoco_get_bitratemode(int bitrate, int automatic); +void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic); + +int orinoco_hw_program_rids(struct orinoco_private *priv); +int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc); +int __orinoco_hw_set_bitrate(struct orinoco_private *priv); +int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate); +int __orinoco_hw_set_wap(struct orinoco_private *priv); +int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv); +int __orinoco_hw_setup_enc(struct orinoco_private *priv); +int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, + int set_tx, const u8 *key, const u8 *rsc, + size_t rsc_len, const u8 *tsc, size_t tsc_len); +int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx); +int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, + struct net_device *dev, + int mc_count, int promisc); +int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, + char buf[IW_ESSID_MAX_SIZE + 1]); +int orinoco_hw_get_freq(struct orinoco_private *priv); +int orinoco_hw_get_bitratelist(struct orinoco_private *priv, + int *numrates, s32 *rates, int max); +int orinoco_hw_trigger_scan(struct orinoco_private *priv, + const struct cfg80211_ssid *ssid); +int orinoco_hw_disassociate(struct orinoco_private *priv, + u8 *addr, u16 reason_code); +int orinoco_hw_get_current_bssid(struct orinoco_private *priv, + u8 *addr); + +#endif /* _ORINOCO_HW_H_ */ diff --git a/drivers/net/wireless/intersil/orinoco/main.c b/drivers/net/wireless/intersil/orinoco/main.c new file mode 100644 index 000000000000..7b5c554323c7 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/main.c @@ -0,0 +1,2429 @@ +/* main.c - (formerly known as dldwd_cs.c, orinoco_cs.c and orinoco.c) + * + * A driver for Hermes or Prism 2 chipset based PCMCIA wireless + * adaptors, with Lucent/Agere, Intersil or Symbol firmware. + * + * Current maintainers (as of 29 September 2003) are: + * Pavel Roskin + * and David Gibson + * + * (C) Copyright David Gibson, IBM Corporation 2001-2003. + * Copyright (C) 2000 David Gibson, Linuxcare Australia. + * With some help from : + * Copyright (C) 2001 Jean Tourrilhes, HP Labs + * Copyright (C) 2001 Benjamin Herrenschmidt + * + * Based on dummy_cs.c 1.27 2000/06/12 21:27:25 + * + * Portions based on wvlan_cs.c 1.0.6, Copyright Andreas Neuhaus + * http://www.stud.fh-dortmund.de/~andy/wvlan/ + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * The initial developer of the original code is David A. Hinds + * . Portions created by David + * A. Hinds are Copyright (C) 1999 David A. Hinds. All Rights + * Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. */ + +/* + * TODO + * o Handle de-encapsulation within network layer, provide 802.11 + * headers (patch from Thomas 'Dent' Mirlacher) + * o Fix possible races in SPY handling. + * o Disconnect wireless extensions from fundamental configuration. + * o (maybe) Software WEP support (patch from Stano Meduna). + * o (maybe) Use multiple Tx buffers - driver handling queue + * rather than firmware. + */ + +/* Locking and synchronization: + * + * The basic principle is that everything is serialized through a + * single spinlock, priv->lock. The lock is used in user, bh and irq + * context, so when taken outside hardirq context it should always be + * taken with interrupts disabled. The lock protects both the + * hardware and the struct orinoco_private. + * + * Another flag, priv->hw_unavailable indicates that the hardware is + * unavailable for an extended period of time (e.g. suspended, or in + * the middle of a hard reset). This flag is protected by the + * spinlock. All code which touches the hardware should check the + * flag after taking the lock, and if it is set, give up on whatever + * they are doing and drop the lock again. The orinoco_lock() + * function handles this (it unlocks and returns -EBUSY if + * hw_unavailable is non-zero). + */ + +#define DRIVER_NAME "orinoco" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hermes_rid.h" +#include "hermes_dld.h" +#include "hw.h" +#include "scan.h" +#include "mic.h" +#include "fw.h" +#include "wext.h" +#include "cfg.h" +#include "main.h" + +#include "orinoco.h" + +/********************************************************************/ +/* Module information */ +/********************************************************************/ + +MODULE_AUTHOR("Pavel Roskin & " + "David Gibson "); +MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based " + "and similar wireless cards"); +MODULE_LICENSE("Dual MPL/GPL"); + +/* Level of debugging. Used in the macros in orinoco.h */ +#ifdef ORINOCO_DEBUG +int orinoco_debug = ORINOCO_DEBUG; +EXPORT_SYMBOL(orinoco_debug); +module_param(orinoco_debug, int, 0644); +MODULE_PARM_DESC(orinoco_debug, "Debug level"); +#endif + +static bool suppress_linkstatus; /* = 0 */ +module_param(suppress_linkstatus, bool, 0644); +MODULE_PARM_DESC(suppress_linkstatus, "Don't log link status changes"); + +static int ignore_disconnect; /* = 0 */ +module_param(ignore_disconnect, int, 0644); +MODULE_PARM_DESC(ignore_disconnect, + "Don't report lost link to the network layer"); + +int force_monitor; /* = 0 */ +module_param(force_monitor, int, 0644); +MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions"); + +/********************************************************************/ +/* Internal constants */ +/********************************************************************/ + +/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ +static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; +#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) + +#define ORINOCO_MIN_MTU 256 +#define ORINOCO_MAX_MTU (IEEE80211_MAX_DATA_LEN - ENCAPS_OVERHEAD) + +#define MAX_IRQLOOPS_PER_IRQ 10 +#define MAX_IRQLOOPS_PER_JIFFY (20000 / HZ) /* Based on a guestimate of + * how many events the + * device could + * legitimately generate */ + +#define DUMMY_FID 0xFFFF + +/*#define MAX_MULTICAST(priv) (priv->firmware_type == FIRMWARE_TYPE_AGERE ? \ + HERMES_MAX_MULTICAST : 0)*/ +#define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST) + +#define ORINOCO_INTEN (HERMES_EV_RX | HERMES_EV_ALLOC \ + | HERMES_EV_TX | HERMES_EV_TXEXC \ + | HERMES_EV_WTERR | HERMES_EV_INFO \ + | HERMES_EV_INFDROP) + +/********************************************************************/ +/* Data types */ +/********************************************************************/ + +/* Beginning of the Tx descriptor, used in TxExc handling */ +struct hermes_txexc_data { + struct hermes_tx_descriptor desc; + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; +} __packed; + +/* Rx frame header except compatibility 802.3 header */ +struct hermes_rx_descriptor { + /* Control */ + __le16 status; + __le32 time; + u8 silence; + u8 signal; + u8 rate; + u8 rxflow; + __le32 reserved; + + /* 802.11 header */ + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctl; + u8 addr4[ETH_ALEN]; + + /* Data length */ + __le16 data_len; +} __packed; + +struct orinoco_rx_data { + struct hermes_rx_descriptor *desc; + struct sk_buff *skb; + struct list_head list; +}; + +struct orinoco_scan_data { + void *buf; + size_t len; + int type; + struct list_head list; +}; + +/********************************************************************/ +/* Function prototypes */ +/********************************************************************/ + +static int __orinoco_set_multicast_list(struct net_device *dev); +static int __orinoco_up(struct orinoco_private *priv); +static int __orinoco_down(struct orinoco_private *priv); +static int __orinoco_commit(struct orinoco_private *priv); + +/********************************************************************/ +/* Internal helper functions */ +/********************************************************************/ + +void set_port_type(struct orinoco_private *priv) +{ + switch (priv->iw_mode) { + case NL80211_IFTYPE_STATION: + priv->port_type = 1; + priv->createibss = 0; + break; + case NL80211_IFTYPE_ADHOC: + if (priv->prefer_port3) { + priv->port_type = 3; + priv->createibss = 0; + } else { + priv->port_type = priv->ibss_port; + priv->createibss = 1; + } + break; + case NL80211_IFTYPE_MONITOR: + priv->port_type = 3; + priv->createibss = 0; + break; + default: + printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", + priv->ndev->name); + } +} + +/********************************************************************/ +/* Device methods */ +/********************************************************************/ + +int orinoco_open(struct net_device *dev) +{ + struct orinoco_private *priv = ndev_priv(dev); + unsigned long flags; + int err; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + err = __orinoco_up(priv); + + if (!err) + priv->open = 1; + + orinoco_unlock(priv, &flags); + + return err; +} +EXPORT_SYMBOL(orinoco_open); + +int orinoco_stop(struct net_device *dev) +{ + struct orinoco_private *priv = ndev_priv(dev); + int err = 0; + + /* We mustn't use orinoco_lock() here, because we need to be + able to close the interface even if hw_unavailable is set + (e.g. as we're released after a PC Card removal) */ + orinoco_lock_irq(priv); + + priv->open = 0; + + err = __orinoco_down(priv); + + orinoco_unlock_irq(priv); + + return err; +} +EXPORT_SYMBOL(orinoco_stop); + +struct net_device_stats *orinoco_get_stats(struct net_device *dev) +{ + struct orinoco_private *priv = ndev_priv(dev); + + return &priv->stats; +} +EXPORT_SYMBOL(orinoco_get_stats); + +void orinoco_set_multicast_list(struct net_device *dev) +{ + struct orinoco_private *priv = ndev_priv(dev); + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) { + printk(KERN_DEBUG "%s: orinoco_set_multicast_list() " + "called when hw_unavailable\n", dev->name); + return; + } + + __orinoco_set_multicast_list(dev); + orinoco_unlock(priv, &flags); +} +EXPORT_SYMBOL(orinoco_set_multicast_list); + +int orinoco_change_mtu(struct net_device *dev, int new_mtu) +{ + struct orinoco_private *priv = ndev_priv(dev); + + if ((new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU)) + return -EINVAL; + + /* MTU + encapsulation + header length */ + if ((new_mtu + ENCAPS_OVERHEAD + sizeof(struct ieee80211_hdr)) > + (priv->nicbuf_size - ETH_HLEN)) + return -EINVAL; + + dev->mtu = new_mtu; + + return 0; +} +EXPORT_SYMBOL(orinoco_change_mtu); + +/********************************************************************/ +/* Tx path */ +/********************************************************************/ + +/* Add encapsulation and MIC to the existing SKB. + * The main xmit routine will then send the whole lot to the card. + * Need 8 bytes headroom + * Need 8 bytes tailroom + * + * With encapsulated ethernet II frame + * -------- + * 803.3 header (14 bytes) + * dst[6] + * -------- src[6] + * 803.3 header (14 bytes) len[2] + * dst[6] 803.2 header (8 bytes) + * src[6] encaps[6] + * len[2] <- leave alone -> len[2] + * -------- -------- <-- 0 + * Payload Payload + * ... ... + * + * -------- -------- + * MIC (8 bytes) + * -------- + * + * returns 0 on success, -ENOMEM on error. + */ +int orinoco_process_xmit_skb(struct sk_buff *skb, + struct net_device *dev, + struct orinoco_private *priv, + int *tx_control, + u8 *mic_buf) +{ + struct orinoco_tkip_key *key; + struct ethhdr *eh; + int do_mic; + + key = (struct orinoco_tkip_key *) priv->keys[priv->tx_key].key; + + do_mic = ((priv->encode_alg == ORINOCO_ALG_TKIP) && + (key != NULL)); + + if (do_mic) + *tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) | + HERMES_TXCTRL_MIC; + + eh = (struct ethhdr *)skb->data; + + /* Encapsulate Ethernet-II frames */ + if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */ + struct header_struct { + struct ethhdr eth; /* 802.3 header */ + u8 encap[6]; /* 802.2 header */ + } __packed hdr; + int len = skb->len + sizeof(encaps_hdr) - (2 * ETH_ALEN); + + if (skb_headroom(skb) < ENCAPS_OVERHEAD) { + if (net_ratelimit()) + printk(KERN_ERR + "%s: Not enough headroom for 802.2 headers %d\n", + dev->name, skb_headroom(skb)); + return -ENOMEM; + } + + /* Fill in new header */ + memcpy(&hdr.eth, eh, 2 * ETH_ALEN); + hdr.eth.h_proto = htons(len); + memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr)); + + /* Make room for the new header, and copy it in */ + eh = (struct ethhdr *) skb_push(skb, ENCAPS_OVERHEAD); + memcpy(eh, &hdr, sizeof(hdr)); + } + + /* Calculate Michael MIC */ + if (do_mic) { + size_t len = skb->len - ETH_HLEN; + u8 *mic = &mic_buf[0]; + + /* Have to write to an even address, so copy the spare + * byte across */ + if (skb->len % 2) { + *mic = skb->data[skb->len - 1]; + mic++; + } + + orinoco_mic(priv->tx_tfm_mic, key->tx_mic, + eh->h_dest, eh->h_source, 0 /* priority */, + skb->data + ETH_HLEN, + len, mic); + } + + return 0; +} +EXPORT_SYMBOL(orinoco_process_xmit_skb); + +static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct net_device_stats *stats = &priv->stats; + struct hermes *hw = &priv->hw; + int err = 0; + u16 txfid = priv->txfid; + int tx_control; + unsigned long flags; + u8 mic_buf[MICHAEL_MIC_LEN + 1]; + + if (!netif_running(dev)) { + printk(KERN_ERR "%s: Tx on stopped device!\n", + dev->name); + return NETDEV_TX_BUSY; + } + + if (netif_queue_stopped(dev)) { + printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", + dev->name); + return NETDEV_TX_BUSY; + } + + if (orinoco_lock(priv, &flags) != 0) { + printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n", + dev->name); + return NETDEV_TX_BUSY; + } + + if (!netif_carrier_ok(dev) || + (priv->iw_mode == NL80211_IFTYPE_MONITOR)) { + /* Oops, the firmware hasn't established a connection, + silently drop the packet (this seems to be the + safest approach). */ + goto drop; + } + + /* Check packet length */ + if (skb->len < ETH_HLEN) + goto drop; + + tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; + + err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control, + &mic_buf[0]); + if (err) + goto drop; + + if (priv->has_alt_txcntl) { + /* WPA enabled firmwares have tx_cntl at the end of + * the 802.11 header. So write zeroed descriptor and + * 802.11 header at the same time + */ + char desc[HERMES_802_3_OFFSET]; + __le16 *txcntl = (__le16 *) &desc[HERMES_TXCNTL2_OFFSET]; + + memset(&desc, 0, sizeof(desc)); + + *txcntl = cpu_to_le16(tx_control); + err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), + txfid, 0); + if (err) { + if (net_ratelimit()) + printk(KERN_ERR "%s: Error %d writing Tx " + "descriptor to BAP\n", dev->name, err); + goto busy; + } + } else { + struct hermes_tx_descriptor desc; + + memset(&desc, 0, sizeof(desc)); + + desc.tx_control = cpu_to_le16(tx_control); + err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), + txfid, 0); + if (err) { + if (net_ratelimit()) + printk(KERN_ERR "%s: Error %d writing Tx " + "descriptor to BAP\n", dev->name, err); + goto busy; + } + + /* Clear the 802.11 header and data length fields - some + * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused + * if this isn't done. */ + hermes_clear_words(hw, HERMES_DATA0, + HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); + } + + err = hw->ops->bap_pwrite(hw, USER_BAP, skb->data, skb->len, + txfid, HERMES_802_3_OFFSET); + if (err) { + printk(KERN_ERR "%s: Error %d writing packet to BAP\n", + dev->name, err); + goto busy; + } + + if (tx_control & HERMES_TXCTRL_MIC) { + size_t offset = HERMES_802_3_OFFSET + skb->len; + size_t len = MICHAEL_MIC_LEN; + + if (offset % 2) { + offset--; + len++; + } + err = hw->ops->bap_pwrite(hw, USER_BAP, &mic_buf[0], len, + txfid, offset); + if (err) { + printk(KERN_ERR "%s: Error %d writing MIC to BAP\n", + dev->name, err); + goto busy; + } + } + + /* Finally, we actually initiate the send */ + netif_stop_queue(dev); + + err = hw->ops->cmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, + txfid, NULL); + if (err) { + netif_start_queue(dev); + if (net_ratelimit()) + printk(KERN_ERR "%s: Error %d transmitting packet\n", + dev->name, err); + goto busy; + } + + stats->tx_bytes += HERMES_802_3_OFFSET + skb->len; + goto ok; + + drop: + stats->tx_errors++; + stats->tx_dropped++; + + ok: + orinoco_unlock(priv, &flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + + busy: + if (err == -EIO) + schedule_work(&priv->reset_work); + orinoco_unlock(priv, &flags); + return NETDEV_TX_BUSY; +} + +static void __orinoco_ev_alloc(struct net_device *dev, struct hermes *hw) +{ + struct orinoco_private *priv = ndev_priv(dev); + u16 fid = hermes_read_regn(hw, ALLOCFID); + + if (fid != priv->txfid) { + if (fid != DUMMY_FID) + printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n", + dev->name, fid); + return; + } + + hermes_write_regn(hw, ALLOCFID, DUMMY_FID); +} + +static void __orinoco_ev_tx(struct net_device *dev, struct hermes *hw) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct net_device_stats *stats = &priv->stats; + + stats->tx_packets++; + + netif_wake_queue(dev); + + hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); +} + +static void __orinoco_ev_txexc(struct net_device *dev, struct hermes *hw) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct net_device_stats *stats = &priv->stats; + u16 fid = hermes_read_regn(hw, TXCOMPLFID); + u16 status; + struct hermes_txexc_data hdr; + int err = 0; + + if (fid == DUMMY_FID) + return; /* Nothing's really happened */ + + /* Read part of the frame header - we need status and addr1 */ + err = hw->ops->bap_pread(hw, IRQ_BAP, &hdr, + sizeof(struct hermes_txexc_data), + fid, 0); + + hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); + stats->tx_errors++; + + if (err) { + printk(KERN_WARNING "%s: Unable to read descriptor on Tx error " + "(FID=%04X error %d)\n", + dev->name, fid, err); + return; + } + + DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name, + err, fid); + + /* We produce a TXDROP event only for retry or lifetime + * exceeded, because that's the only status that really mean + * that this particular node went away. + * Other errors means that *we* screwed up. - Jean II */ + status = le16_to_cpu(hdr.desc.status); + if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) { + union iwreq_data wrqu; + + /* Copy 802.11 dest address. + * We use the 802.11 header because the frame may + * not be 802.3 or may be mangled... + * In Ad-Hoc mode, it will be the node address. + * In managed mode, it will be most likely the AP addr + * User space will figure out how to convert it to + * whatever it needs (IP address or else). + * - Jean II */ + memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); + } + + netif_wake_queue(dev); +} + +void orinoco_tx_timeout(struct net_device *dev) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct net_device_stats *stats = &priv->stats; + struct hermes *hw = &priv->hw; + + printk(KERN_WARNING "%s: Tx timeout! " + "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n", + dev->name, hermes_read_regn(hw, ALLOCFID), + hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT)); + + stats->tx_errors++; + + schedule_work(&priv->reset_work); +} +EXPORT_SYMBOL(orinoco_tx_timeout); + +/********************************************************************/ +/* Rx path (data frames) */ +/********************************************************************/ + +/* Does the frame have a SNAP header indicating it should be + * de-encapsulated to Ethernet-II? */ +static inline int is_ethersnap(void *_hdr) +{ + u8 *hdr = _hdr; + + /* We de-encapsulate all packets which, a) have SNAP headers + * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header + * and where b) the OUI of the SNAP header is 00:00:00 or + * 00:00:f8 - we need both because different APs appear to use + * different OUIs for some reason */ + return (memcmp(hdr, &encaps_hdr, 5) == 0) + && ((hdr[5] == 0x00) || (hdr[5] == 0xf8)); +} + +static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac, + int level, int noise) +{ + struct iw_quality wstats; + wstats.level = level - 0x95; + wstats.noise = noise - 0x95; + wstats.qual = (level > noise) ? (level - noise) : 0; + wstats.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + /* Update spy records */ + wireless_spy_update(dev, mac, &wstats); +} + +static void orinoco_stat_gather(struct net_device *dev, + struct sk_buff *skb, + struct hermes_rx_descriptor *desc) +{ + struct orinoco_private *priv = ndev_priv(dev); + + /* Using spy support with lots of Rx packets, like in an + * infrastructure (AP), will really slow down everything, because + * the MAC address must be compared to each entry of the spy list. + * If the user really asks for it (set some address in the + * spy list), we do it, but he will pay the price. + * Note that to get here, you need both WIRELESS_SPY + * compiled in AND some addresses in the list !!! + */ + /* Note : gcc will optimise the whole section away if + * WIRELESS_SPY is not defined... - Jean II */ + if (SPY_NUMBER(priv)) { + orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN, + desc->signal, desc->silence); + } +} + +/* + * orinoco_rx_monitor - handle received monitor frames. + * + * Arguments: + * dev network device + * rxfid received FID + * desc rx descriptor of the frame + * + * Call context: interrupt + */ +static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, + struct hermes_rx_descriptor *desc) +{ + u32 hdrlen = 30; /* return full header by default */ + u32 datalen = 0; + u16 fc; + int err; + int len; + struct sk_buff *skb; + struct orinoco_private *priv = ndev_priv(dev); + struct net_device_stats *stats = &priv->stats; + struct hermes *hw = &priv->hw; + + len = le16_to_cpu(desc->data_len); + + /* Determine the size of the header and the data */ + fc = le16_to_cpu(desc->frame_ctl); + switch (fc & IEEE80211_FCTL_FTYPE) { + case IEEE80211_FTYPE_DATA: + if ((fc & IEEE80211_FCTL_TODS) + && (fc & IEEE80211_FCTL_FROMDS)) + hdrlen = 30; + else + hdrlen = 24; + datalen = len; + break; + case IEEE80211_FTYPE_MGMT: + hdrlen = 24; + datalen = len; + break; + case IEEE80211_FTYPE_CTL: + switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_PSPOLL: + case IEEE80211_STYPE_RTS: + case IEEE80211_STYPE_CFEND: + case IEEE80211_STYPE_CFENDACK: + hdrlen = 16; + break; + case IEEE80211_STYPE_CTS: + case IEEE80211_STYPE_ACK: + hdrlen = 10; + break; + } + break; + default: + /* Unknown frame type */ + break; + } + + /* sanity check the length */ + if (datalen > IEEE80211_MAX_DATA_LEN + 12) { + printk(KERN_DEBUG "%s: oversized monitor frame, " + "data length = %d\n", dev->name, datalen); + stats->rx_length_errors++; + goto update_stats; + } + + skb = dev_alloc_skb(hdrlen + datalen); + if (!skb) { + printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n", + dev->name); + goto update_stats; + } + + /* Copy the 802.11 header to the skb */ + memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen); + skb_reset_mac_header(skb); + + /* If any, copy the data from the card to the skb */ + if (datalen > 0) { + err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, datalen), + ALIGN(datalen, 2), rxfid, + HERMES_802_2_OFFSET); + if (err) { + printk(KERN_ERR "%s: error %d reading monitor frame\n", + dev->name, err); + goto drop; + } + } + + skb->dev = dev; + skb->ip_summed = CHECKSUM_NONE; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = cpu_to_be16(ETH_P_802_2); + + stats->rx_packets++; + stats->rx_bytes += skb->len; + + netif_rx(skb); + return; + + drop: + dev_kfree_skb_irq(skb); + update_stats: + stats->rx_errors++; + stats->rx_dropped++; +} + +void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct net_device_stats *stats = &priv->stats; + struct iw_statistics *wstats = &priv->wstats; + struct sk_buff *skb = NULL; + u16 rxfid, status; + int length; + struct hermes_rx_descriptor *desc; + struct orinoco_rx_data *rx_data; + int err; + + desc = kmalloc(sizeof(*desc), GFP_ATOMIC); + if (!desc) + goto update_stats; + + rxfid = hermes_read_regn(hw, RXFID); + + err = hw->ops->bap_pread(hw, IRQ_BAP, desc, sizeof(*desc), + rxfid, 0); + if (err) { + printk(KERN_ERR "%s: error %d reading Rx descriptor. " + "Frame dropped.\n", dev->name, err); + goto update_stats; + } + + status = le16_to_cpu(desc->status); + + if (status & HERMES_RXSTAT_BADCRC) { + DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", + dev->name); + stats->rx_crc_errors++; + goto update_stats; + } + + /* Handle frames in monitor mode */ + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { + orinoco_rx_monitor(dev, rxfid, desc); + goto out; + } + + if (status & HERMES_RXSTAT_UNDECRYPTABLE) { + DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n", + dev->name); + wstats->discard.code++; + goto update_stats; + } + + length = le16_to_cpu(desc->data_len); + + /* Sanity checks */ + if (length < 3) { /* No for even an 802.2 LLC header */ + /* At least on Symbol firmware with PCF we get quite a + lot of these legitimately - Poll frames with no + data. */ + goto out; + } + if (length > IEEE80211_MAX_DATA_LEN) { + printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n", + dev->name, length); + stats->rx_length_errors++; + goto update_stats; + } + + /* Payload size does not include Michael MIC. Increase payload + * size to read it together with the data. */ + if (status & HERMES_RXSTAT_MIC) + length += MICHAEL_MIC_LEN; + + /* We need space for the packet data itself, plus an ethernet + header, plus 2 bytes so we can align the IP header on a + 32bit boundary, plus 1 byte so we can read in odd length + packets from the card, which has an IO granularity of 16 + bits */ + skb = dev_alloc_skb(length + ETH_HLEN + 2 + 1); + if (!skb) { + printk(KERN_WARNING "%s: Can't allocate skb for Rx\n", + dev->name); + goto update_stats; + } + + /* We'll prepend the header, so reserve space for it. The worst + case is no decapsulation, when 802.3 header is prepended and + nothing is removed. 2 is for aligning the IP header. */ + skb_reserve(skb, ETH_HLEN + 2); + + err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, length), + ALIGN(length, 2), rxfid, + HERMES_802_2_OFFSET); + if (err) { + printk(KERN_ERR "%s: error %d reading frame. " + "Frame dropped.\n", dev->name, err); + goto drop; + } + + /* Add desc and skb to rx queue */ + rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC); + if (!rx_data) + goto drop; + + rx_data->desc = desc; + rx_data->skb = skb; + list_add_tail(&rx_data->list, &priv->rx_list); + tasklet_schedule(&priv->rx_tasklet); + + return; + +drop: + dev_kfree_skb_irq(skb); +update_stats: + stats->rx_errors++; + stats->rx_dropped++; +out: + kfree(desc); +} +EXPORT_SYMBOL(__orinoco_ev_rx); + +static void orinoco_rx(struct net_device *dev, + struct hermes_rx_descriptor *desc, + struct sk_buff *skb) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct net_device_stats *stats = &priv->stats; + u16 status, fc; + int length; + struct ethhdr *hdr; + + status = le16_to_cpu(desc->status); + length = le16_to_cpu(desc->data_len); + fc = le16_to_cpu(desc->frame_ctl); + + /* Calculate and check MIC */ + if (status & HERMES_RXSTAT_MIC) { + struct orinoco_tkip_key *key; + int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >> + HERMES_MIC_KEY_ID_SHIFT); + u8 mic[MICHAEL_MIC_LEN]; + u8 *rxmic; + u8 *src = (fc & IEEE80211_FCTL_FROMDS) ? + desc->addr3 : desc->addr2; + + /* Extract Michael MIC from payload */ + rxmic = skb->data + skb->len - MICHAEL_MIC_LEN; + + skb_trim(skb, skb->len - MICHAEL_MIC_LEN); + length -= MICHAEL_MIC_LEN; + + key = (struct orinoco_tkip_key *) priv->keys[key_id].key; + + if (!key) { + printk(KERN_WARNING "%s: Received encrypted frame from " + "%pM using key %i, but key is not installed\n", + dev->name, src, key_id); + goto drop; + } + + orinoco_mic(priv->rx_tfm_mic, key->rx_mic, desc->addr1, src, + 0, /* priority or QoS? */ + skb->data, skb->len, &mic[0]); + + if (memcmp(mic, rxmic, + MICHAEL_MIC_LEN)) { + union iwreq_data wrqu; + struct iw_michaelmicfailure wxmic; + + printk(KERN_WARNING "%s: " + "Invalid Michael MIC in data frame from %pM, " + "using key %i\n", + dev->name, src, key_id); + + /* TODO: update stats */ + + /* Notify userspace */ + memset(&wxmic, 0, sizeof(wxmic)); + wxmic.flags = key_id & IW_MICFAILURE_KEY_ID; + wxmic.flags |= (desc->addr1[0] & 1) ? + IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE; + wxmic.src_addr.sa_family = ARPHRD_ETHER; + memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN); + + (void) orinoco_hw_get_tkip_iv(priv, key_id, + &wxmic.tsc[0]); + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = sizeof(wxmic); + wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, + (char *) &wxmic); + + goto drop; + } + } + + /* Handle decapsulation + * In most cases, the firmware tell us about SNAP frames. + * For some reason, the SNAP frames sent by LinkSys APs + * are not properly recognised by most firmwares. + * So, check ourselves */ + if (length >= ENCAPS_OVERHEAD && + (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || + ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || + is_ethersnap(skb->data))) { + /* These indicate a SNAP within 802.2 LLC within + 802.11 frame which we'll need to de-encapsulate to + the original EthernetII frame. */ + hdr = (struct ethhdr *)skb_push(skb, + ETH_HLEN - ENCAPS_OVERHEAD); + } else { + /* 802.3 frame - prepend 802.3 header as is */ + hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN); + hdr->h_proto = htons(length); + } + memcpy(hdr->h_dest, desc->addr1, ETH_ALEN); + if (fc & IEEE80211_FCTL_FROMDS) + memcpy(hdr->h_source, desc->addr3, ETH_ALEN); + else + memcpy(hdr->h_source, desc->addr2, ETH_ALEN); + + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_NONE; + if (fc & IEEE80211_FCTL_TODS) + skb->pkt_type = PACKET_OTHERHOST; + + /* Process the wireless stats if needed */ + orinoco_stat_gather(dev, skb, desc); + + /* Pass the packet to the networking stack */ + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += length; + + return; + + drop: + dev_kfree_skb(skb); + stats->rx_errors++; + stats->rx_dropped++; +} + +static void orinoco_rx_isr_tasklet(unsigned long data) +{ + struct orinoco_private *priv = (struct orinoco_private *) data; + struct net_device *dev = priv->ndev; + struct orinoco_rx_data *rx_data, *temp; + struct hermes_rx_descriptor *desc; + struct sk_buff *skb; + unsigned long flags; + + /* orinoco_rx requires the driver lock, and we also need to + * protect priv->rx_list, so just hold the lock over the + * lot. + * + * If orinoco_lock fails, we've unplugged the card. In this + * case just abort. */ + if (orinoco_lock(priv, &flags) != 0) + return; + + /* extract desc and skb from queue */ + list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) { + desc = rx_data->desc; + skb = rx_data->skb; + list_del(&rx_data->list); + kfree(rx_data); + + orinoco_rx(dev, desc, skb); + + kfree(desc); + } + + orinoco_unlock(priv, &flags); +} + +/********************************************************************/ +/* Rx path (info frames) */ +/********************************************************************/ + +static void print_linkstatus(struct net_device *dev, u16 status) +{ + char *s; + + if (suppress_linkstatus) + return; + + switch (status) { + case HERMES_LINKSTATUS_NOT_CONNECTED: + s = "Not Connected"; + break; + case HERMES_LINKSTATUS_CONNECTED: + s = "Connected"; + break; + case HERMES_LINKSTATUS_DISCONNECTED: + s = "Disconnected"; + break; + case HERMES_LINKSTATUS_AP_CHANGE: + s = "AP Changed"; + break; + case HERMES_LINKSTATUS_AP_OUT_OF_RANGE: + s = "AP Out of Range"; + break; + case HERMES_LINKSTATUS_AP_IN_RANGE: + s = "AP In Range"; + break; + case HERMES_LINKSTATUS_ASSOC_FAILED: + s = "Association Failed"; + break; + default: + s = "UNKNOWN"; + } + + printk(KERN_DEBUG "%s: New link status: %s (%04x)\n", + dev->name, s, status); +} + +/* Search scan results for requested BSSID, join it if found */ +static void orinoco_join_ap(struct work_struct *work) +{ + struct orinoco_private *priv = + container_of(work, struct orinoco_private, join_work); + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + int err; + unsigned long flags; + struct join_req { + u8 bssid[ETH_ALEN]; + __le16 channel; + } __packed req; + const int atom_len = offsetof(struct prism2_scan_apinfo, atim); + struct prism2_scan_apinfo *atom = NULL; + int offset = 4; + int found = 0; + u8 *buf; + u16 len; + + /* Allocate buffer for scan results */ + buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL); + if (!buf) + return; + + if (orinoco_lock(priv, &flags) != 0) + goto fail_lock; + + /* Sanity checks in case user changed something in the meantime */ + if (!priv->bssid_fixed) + goto out; + + if (strlen(priv->desired_essid) == 0) + goto out; + + /* Read scan results from the firmware */ + err = hw->ops->read_ltv(hw, USER_BAP, + HERMES_RID_SCANRESULTSTABLE, + MAX_SCAN_LEN, &len, buf); + if (err) { + printk(KERN_ERR "%s: Cannot read scan results\n", + dev->name); + goto out; + } + + len = HERMES_RECLEN_TO_BYTES(len); + + /* Go through the scan results looking for the channel of the AP + * we were requested to join */ + for (; offset + atom_len <= len; offset += atom_len) { + atom = (struct prism2_scan_apinfo *) (buf + offset); + if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) { + found = 1; + break; + } + } + + if (!found) { + DEBUG(1, "%s: Requested AP not found in scan results\n", + dev->name); + goto out; + } + + memcpy(req.bssid, priv->desired_bssid, ETH_ALEN); + req.channel = atom->channel; /* both are little-endian */ + err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST, + &req); + if (err) + printk(KERN_ERR "%s: Error issuing join request\n", dev->name); + + out: + orinoco_unlock(priv, &flags); + + fail_lock: + kfree(buf); +} + +/* Send new BSSID to userspace */ +static void orinoco_send_bssid_wevent(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + union iwreq_data wrqu; + int err; + + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, + ETH_ALEN, NULL, wrqu.ap_addr.sa_data); + if (err != 0) + return; + + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* Send event to user space */ + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); +} + +static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + union iwreq_data wrqu; + int err; + u8 buf[88]; + u8 *ie; + + if (!priv->has_wpa) + return; + + err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO, + sizeof(buf), NULL, &buf); + if (err != 0) + return; + + ie = orinoco_get_wpa_ie(buf, sizeof(buf)); + if (ie) { + int rem = sizeof(buf) - (ie - &buf[0]); + wrqu.data.length = ie[1] + 2; + if (wrqu.data.length > rem) + wrqu.data.length = rem; + + if (wrqu.data.length) + /* Send event to user space */ + wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, ie); + } +} + +static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + union iwreq_data wrqu; + int err; + u8 buf[88]; /* TODO: verify max size or IW_GENERIC_IE_MAX */ + u8 *ie; + + if (!priv->has_wpa) + return; + + err = hw->ops->read_ltv(hw, USER_BAP, + HERMES_RID_CURRENT_ASSOC_RESP_INFO, + sizeof(buf), NULL, &buf); + if (err != 0) + return; + + ie = orinoco_get_wpa_ie(buf, sizeof(buf)); + if (ie) { + int rem = sizeof(buf) - (ie - &buf[0]); + wrqu.data.length = ie[1] + 2; + if (wrqu.data.length > rem) + wrqu.data.length = rem; + + if (wrqu.data.length) + /* Send event to user space */ + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie); + } +} + +static void orinoco_send_wevents(struct work_struct *work) +{ + struct orinoco_private *priv = + container_of(work, struct orinoco_private, wevent_work); + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return; + + orinoco_send_assocreqie_wevent(priv); + orinoco_send_assocrespie_wevent(priv); + orinoco_send_bssid_wevent(priv); + + orinoco_unlock(priv, &flags); +} + +static void qbuf_scan(struct orinoco_private *priv, void *buf, + int len, int type) +{ + struct orinoco_scan_data *sd; + unsigned long flags; + + sd = kmalloc(sizeof(*sd), GFP_ATOMIC); + if (!sd) + return; + + sd->buf = buf; + sd->len = len; + sd->type = type; + + spin_lock_irqsave(&priv->scan_lock, flags); + list_add_tail(&sd->list, &priv->scan_list); + spin_unlock_irqrestore(&priv->scan_lock, flags); + + schedule_work(&priv->process_scan); +} + +static void qabort_scan(struct orinoco_private *priv) +{ + struct orinoco_scan_data *sd; + unsigned long flags; + + sd = kmalloc(sizeof(*sd), GFP_ATOMIC); + if (!sd) + return; + + sd->len = -1; /* Abort */ + + spin_lock_irqsave(&priv->scan_lock, flags); + list_add_tail(&sd->list, &priv->scan_list); + spin_unlock_irqrestore(&priv->scan_lock, flags); + + schedule_work(&priv->process_scan); +} + +static void orinoco_process_scan_results(struct work_struct *work) +{ + struct orinoco_private *priv = + container_of(work, struct orinoco_private, process_scan); + struct orinoco_scan_data *sd, *temp; + unsigned long flags; + void *buf; + int len; + int type; + + spin_lock_irqsave(&priv->scan_lock, flags); + list_for_each_entry_safe(sd, temp, &priv->scan_list, list) { + + buf = sd->buf; + len = sd->len; + type = sd->type; + + list_del(&sd->list); + spin_unlock_irqrestore(&priv->scan_lock, flags); + kfree(sd); + + if (len > 0) { + if (type == HERMES_INQ_CHANNELINFO) + orinoco_add_extscan_result(priv, buf, len); + else + orinoco_add_hostscan_results(priv, buf, len); + + kfree(buf); + } else { + /* Either abort or complete the scan */ + orinoco_scan_done(priv, (len < 0)); + } + + spin_lock_irqsave(&priv->scan_lock, flags); + } + spin_unlock_irqrestore(&priv->scan_lock, flags); +} + +void __orinoco_ev_info(struct net_device *dev, struct hermes *hw) +{ + struct orinoco_private *priv = ndev_priv(dev); + u16 infofid; + struct { + __le16 len; + __le16 type; + } __packed info; + int len, type; + int err; + + /* This is an answer to an INQUIRE command that we did earlier, + * or an information "event" generated by the card + * The controller return to us a pseudo frame containing + * the information in question - Jean II */ + infofid = hermes_read_regn(hw, INFOFID); + + /* Read the info frame header - don't try too hard */ + err = hw->ops->bap_pread(hw, IRQ_BAP, &info, sizeof(info), + infofid, 0); + if (err) { + printk(KERN_ERR "%s: error %d reading info frame. " + "Frame dropped.\n", dev->name, err); + return; + } + + len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len)); + type = le16_to_cpu(info.type); + + switch (type) { + case HERMES_INQ_TALLIES: { + struct hermes_tallies_frame tallies; + struct iw_statistics *wstats = &priv->wstats; + + if (len > sizeof(tallies)) { + printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n", + dev->name, len); + len = sizeof(tallies); + } + + err = hw->ops->bap_pread(hw, IRQ_BAP, &tallies, len, + infofid, sizeof(info)); + if (err) + break; + + /* Increment our various counters */ + /* wstats->discard.nwid - no wrong BSSID stuff */ + wstats->discard.code += + le16_to_cpu(tallies.RxWEPUndecryptable); + if (len == sizeof(tallies)) + wstats->discard.code += + le16_to_cpu(tallies.RxDiscards_WEPICVError) + + le16_to_cpu(tallies.RxDiscards_WEPExcluded); + wstats->discard.misc += + le16_to_cpu(tallies.TxDiscardsWrongSA); + wstats->discard.fragment += + le16_to_cpu(tallies.RxMsgInBadMsgFragments); + wstats->discard.retries += + le16_to_cpu(tallies.TxRetryLimitExceeded); + /* wstats->miss.beacon - no match */ + } + break; + case HERMES_INQ_LINKSTATUS: { + struct hermes_linkstatus linkstatus; + u16 newstatus; + int connected; + + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) + break; + + if (len != sizeof(linkstatus)) { + printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n", + dev->name, len); + break; + } + + err = hw->ops->bap_pread(hw, IRQ_BAP, &linkstatus, len, + infofid, sizeof(info)); + if (err) + break; + newstatus = le16_to_cpu(linkstatus.linkstatus); + + /* Symbol firmware uses "out of range" to signal that + * the hostscan frame can be requested. */ + if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE && + priv->firmware_type == FIRMWARE_TYPE_SYMBOL && + priv->has_hostscan && priv->scan_request) { + hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL); + break; + } + + connected = (newstatus == HERMES_LINKSTATUS_CONNECTED) + || (newstatus == HERMES_LINKSTATUS_AP_CHANGE) + || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE); + + if (connected) + netif_carrier_on(dev); + else if (!ignore_disconnect) + netif_carrier_off(dev); + + if (newstatus != priv->last_linkstatus) { + priv->last_linkstatus = newstatus; + print_linkstatus(dev, newstatus); + /* The info frame contains only one word which is the + * status (see hermes.h). The status is pretty boring + * in itself, that's why we export the new BSSID... + * Jean II */ + schedule_work(&priv->wevent_work); + } + } + break; + case HERMES_INQ_SCAN: + if (!priv->scan_request && priv->bssid_fixed && + priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { + schedule_work(&priv->join_work); + break; + } + /* fall through */ + case HERMES_INQ_HOSTSCAN: + case HERMES_INQ_HOSTSCAN_SYMBOL: { + /* Result of a scanning. Contains information about + * cells in the vicinity - Jean II */ + unsigned char *buf; + + /* Sanity check */ + if (len > 4096) { + printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n", + dev->name, len); + qabort_scan(priv); + break; + } + + /* Allocate buffer for results */ + buf = kmalloc(len, GFP_ATOMIC); + if (buf == NULL) { + /* No memory, so can't printk()... */ + qabort_scan(priv); + break; + } + + /* Read scan data */ + err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) buf, len, + infofid, sizeof(info)); + if (err) { + kfree(buf); + qabort_scan(priv); + break; + } + +#ifdef ORINOCO_DEBUG + { + int i; + printk(KERN_DEBUG "Scan result [%02X", buf[0]); + for (i = 1; i < (len * 2); i++) + printk(":%02X", buf[i]); + printk("]\n"); + } +#endif /* ORINOCO_DEBUG */ + + qbuf_scan(priv, buf, len, type); + } + break; + case HERMES_INQ_CHANNELINFO: + { + struct agere_ext_scan_info *bss; + + if (!priv->scan_request) { + printk(KERN_DEBUG "%s: Got chaninfo without scan, " + "len=%d\n", dev->name, len); + break; + } + + /* An empty result indicates that the scan is complete */ + if (len == 0) { + qbuf_scan(priv, NULL, len, type); + break; + } + + /* Sanity check */ + else if (len < (offsetof(struct agere_ext_scan_info, + data) + 2)) { + /* Drop this result now so we don't have to + * keep checking later */ + printk(KERN_WARNING + "%s: Ext scan results too short (%d bytes)\n", + dev->name, len); + break; + } + + bss = kmalloc(len, GFP_ATOMIC); + if (bss == NULL) + break; + + /* Read scan data */ + err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) bss, len, + infofid, sizeof(info)); + if (err) + kfree(bss); + else + qbuf_scan(priv, bss, len, type); + + break; + } + case HERMES_INQ_SEC_STAT_AGERE: + /* Security status (Agere specific) */ + /* Ignore this frame for now */ + if (priv->firmware_type == FIRMWARE_TYPE_AGERE) + break; + /* fall through */ + default: + printk(KERN_DEBUG "%s: Unknown information frame received: " + "type 0x%04x, length %d\n", dev->name, type, len); + /* We don't actually do anything about it */ + break; + } +} +EXPORT_SYMBOL(__orinoco_ev_info); + +static void __orinoco_ev_infdrop(struct net_device *dev, struct hermes *hw) +{ + if (net_ratelimit()) + printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name); +} + +/********************************************************************/ +/* Internal hardware control routines */ +/********************************************************************/ + +static int __orinoco_up(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + int err; + + netif_carrier_off(dev); /* just to make sure */ + + err = __orinoco_commit(priv); + if (err) { + printk(KERN_ERR "%s: Error %d configuring card\n", + dev->name, err); + return err; + } + + /* Fire things up again */ + hermes_set_irqmask(hw, ORINOCO_INTEN); + err = hermes_enable_port(hw, 0); + if (err) { + printk(KERN_ERR "%s: Error %d enabling MAC port\n", + dev->name, err); + return err; + } + + netif_start_queue(dev); + + return 0; +} + +static int __orinoco_down(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + int err; + + netif_stop_queue(dev); + + if (!priv->hw_unavailable) { + if (!priv->broken_disableport) { + err = hermes_disable_port(hw, 0); + if (err) { + /* Some firmwares (e.g. Intersil 1.3.x) seem + * to have problems disabling the port, oh + * well, too bad. */ + printk(KERN_WARNING "%s: Error %d disabling MAC port\n", + dev->name, err); + priv->broken_disableport = 1; + } + } + hermes_set_irqmask(hw, 0); + hermes_write_regn(hw, EVACK, 0xffff); + } + + orinoco_scan_done(priv, true); + + /* firmware will have to reassociate */ + netif_carrier_off(dev); + priv->last_linkstatus = 0xffff; + + return 0; +} + +static int orinoco_reinit_firmware(struct orinoco_private *priv) +{ + struct hermes *hw = &priv->hw; + int err; + + err = hw->ops->init(hw); + if (priv->do_fw_download && !err) { + err = orinoco_download(priv); + if (err) + priv->do_fw_download = 0; + } + if (!err) + err = orinoco_hw_allocate_fid(priv); + + return err; +} + +static int +__orinoco_set_multicast_list(struct net_device *dev) +{ + struct orinoco_private *priv = ndev_priv(dev); + int err = 0; + int promisc, mc_count; + + /* The Hermes doesn't seem to have an allmulti mode, so we go + * into promiscuous mode and let the upper levels deal. */ + if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || + (netdev_mc_count(dev) > MAX_MULTICAST(priv))) { + promisc = 1; + mc_count = 0; + } else { + promisc = 0; + mc_count = netdev_mc_count(dev); + } + + err = __orinoco_hw_set_multicast_list(priv, dev, mc_count, promisc); + + return err; +} + +/* This must be called from user context, without locks held - use + * schedule_work() */ +void orinoco_reset(struct work_struct *work) +{ + struct orinoco_private *priv = + container_of(work, struct orinoco_private, reset_work); + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + int err; + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + /* When the hardware becomes available again, whatever + * detects that is responsible for re-initializing + * it. So no need for anything further */ + return; + + netif_stop_queue(dev); + + /* Shut off interrupts. Depending on what state the hardware + * is in, this might not work, but we'll try anyway */ + hermes_set_irqmask(hw, 0); + hermes_write_regn(hw, EVACK, 0xffff); + + priv->hw_unavailable++; + priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */ + netif_carrier_off(dev); + + orinoco_unlock(priv, &flags); + + /* Scanning support: Notify scan cancellation */ + orinoco_scan_done(priv, true); + + if (priv->hard_reset) { + err = (*priv->hard_reset)(priv); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d " + "performing hard reset\n", dev->name, err); + goto disable; + } + } + + err = orinoco_reinit_firmware(priv); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", + dev->name, err); + goto disable; + } + + /* This has to be called from user context */ + orinoco_lock_irq(priv); + + priv->hw_unavailable--; + + /* priv->open or priv->hw_unavailable might have changed while + * we dropped the lock */ + if (priv->open && (!priv->hw_unavailable)) { + err = __orinoco_up(priv); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", + dev->name, err); + } else + dev->trans_start = jiffies; + } + + orinoco_unlock_irq(priv); + + return; + disable: + hermes_set_irqmask(hw, 0); + netif_device_detach(dev); + printk(KERN_ERR "%s: Device has been disabled!\n", dev->name); +} + +static int __orinoco_commit(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + int err = 0; + + /* If we've called commit, we are reconfiguring or bringing the + * interface up. Maintaining countermeasures across this would + * be confusing, so note that we've disabled them. The port will + * be enabled later in orinoco_commit or __orinoco_up. */ + priv->tkip_cm_active = 0; + + err = orinoco_hw_program_rids(priv); + + /* FIXME: what about netif_tx_lock */ + (void) __orinoco_set_multicast_list(dev); + + return err; +} + +/* Ensures configuration changes are applied. May result in a reset. + * The caller should hold priv->lock + */ +int orinoco_commit(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + int err; + + if (priv->broken_disableport) { + schedule_work(&priv->reset_work); + return 0; + } + + err = hermes_disable_port(hw, 0); + if (err) { + printk(KERN_WARNING "%s: Unable to disable port " + "while reconfiguring card\n", dev->name); + priv->broken_disableport = 1; + goto out; + } + + err = __orinoco_commit(priv); + if (err) { + printk(KERN_WARNING "%s: Unable to reconfigure card\n", + dev->name); + goto out; + } + + err = hermes_enable_port(hw, 0); + if (err) { + printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", + dev->name); + goto out; + } + + out: + if (err) { + printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); + schedule_work(&priv->reset_work); + err = 0; + } + return err; +} + +/********************************************************************/ +/* Interrupt handler */ +/********************************************************************/ + +static void __orinoco_ev_tick(struct net_device *dev, struct hermes *hw) +{ + printk(KERN_DEBUG "%s: TICK\n", dev->name); +} + +static void __orinoco_ev_wterr(struct net_device *dev, struct hermes *hw) +{ + /* This seems to happen a fair bit under load, but ignoring it + seems to work fine...*/ + printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", + dev->name); +} + +irqreturn_t orinoco_interrupt(int irq, void *dev_id) +{ + struct orinoco_private *priv = dev_id; + struct net_device *dev = priv->ndev; + struct hermes *hw = &priv->hw; + int count = MAX_IRQLOOPS_PER_IRQ; + u16 evstat, events; + /* These are used to detect a runaway interrupt situation. + * + * If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy, + * we panic and shut down the hardware + */ + /* jiffies value the last time we were called */ + static int last_irq_jiffy; /* = 0 */ + static int loops_this_jiffy; /* = 0 */ + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) { + /* If hw is unavailable - we don't know if the irq was + * for us or not */ + return IRQ_HANDLED; + } + + evstat = hermes_read_regn(hw, EVSTAT); + events = evstat & hw->inten; + if (!events) { + orinoco_unlock(priv, &flags); + return IRQ_NONE; + } + + if (jiffies != last_irq_jiffy) + loops_this_jiffy = 0; + last_irq_jiffy = jiffies; + + while (events && count--) { + if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { + printk(KERN_WARNING "%s: IRQ handler is looping too " + "much! Resetting.\n", dev->name); + /* Disable interrupts for now */ + hermes_set_irqmask(hw, 0); + schedule_work(&priv->reset_work); + break; + } + + /* Check the card hasn't been removed */ + if (!hermes_present(hw)) { + DEBUG(0, "orinoco_interrupt(): card removed\n"); + break; + } + + if (events & HERMES_EV_TICK) + __orinoco_ev_tick(dev, hw); + if (events & HERMES_EV_WTERR) + __orinoco_ev_wterr(dev, hw); + if (events & HERMES_EV_INFDROP) + __orinoco_ev_infdrop(dev, hw); + if (events & HERMES_EV_INFO) + __orinoco_ev_info(dev, hw); + if (events & HERMES_EV_RX) + __orinoco_ev_rx(dev, hw); + if (events & HERMES_EV_TXEXC) + __orinoco_ev_txexc(dev, hw); + if (events & HERMES_EV_TX) + __orinoco_ev_tx(dev, hw); + if (events & HERMES_EV_ALLOC) + __orinoco_ev_alloc(dev, hw); + + hermes_write_regn(hw, EVACK, evstat); + + evstat = hermes_read_regn(hw, EVSTAT); + events = evstat & hw->inten; + } + + orinoco_unlock(priv, &flags); + return IRQ_HANDLED; +} +EXPORT_SYMBOL(orinoco_interrupt); + +/********************************************************************/ +/* Power management */ +/********************************************************************/ +#if defined(CONFIG_PM_SLEEP) && !defined(CONFIG_HERMES_CACHE_FW_ON_INIT) +static int orinoco_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, + void *unused) +{ + struct orinoco_private *priv = container_of(notifier, + struct orinoco_private, + pm_notifier); + + /* All we need to do is cache the firmware before suspend, and + * release it when we come out. + * + * Only need to do this if we're downloading firmware. */ + if (!priv->do_fw_download) + return NOTIFY_DONE; + + switch (pm_event) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + orinoco_cache_fw(priv, 0); + break; + + case PM_POST_RESTORE: + /* Restore from hibernation failed. We need to clean + * up in exactly the same way, so fall through. */ + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + orinoco_uncache_fw(priv); + break; + + case PM_RESTORE_PREPARE: + default: + break; + } + + return NOTIFY_DONE; +} + +static void orinoco_register_pm_notifier(struct orinoco_private *priv) +{ + priv->pm_notifier.notifier_call = orinoco_pm_notifier; + register_pm_notifier(&priv->pm_notifier); +} + +static void orinoco_unregister_pm_notifier(struct orinoco_private *priv) +{ + unregister_pm_notifier(&priv->pm_notifier); +} +#else /* !PM_SLEEP || HERMES_CACHE_FW_ON_INIT */ +#define orinoco_register_pm_notifier(priv) do { } while (0) +#define orinoco_unregister_pm_notifier(priv) do { } while (0) +#endif + +/********************************************************************/ +/* Initialization */ +/********************************************************************/ + +int orinoco_init(struct orinoco_private *priv) +{ + struct device *dev = priv->dev; + struct wiphy *wiphy = priv_to_wiphy(priv); + struct hermes *hw = &priv->hw; + int err = 0; + + /* No need to lock, the hw_unavailable flag is already set in + * alloc_orinocodev() */ + priv->nicbuf_size = IEEE80211_MAX_FRAME_LEN + ETH_HLEN; + + /* Initialize the firmware */ + err = hw->ops->init(hw); + if (err != 0) { + dev_err(dev, "Failed to initialize firmware (err = %d)\n", + err); + goto out; + } + + err = determine_fw_capabilities(priv, wiphy->fw_version, + sizeof(wiphy->fw_version), + &wiphy->hw_version); + if (err != 0) { + dev_err(dev, "Incompatible firmware, aborting\n"); + goto out; + } + + if (priv->do_fw_download) { +#ifdef CONFIG_HERMES_CACHE_FW_ON_INIT + orinoco_cache_fw(priv, 0); +#endif + + err = orinoco_download(priv); + if (err) + priv->do_fw_download = 0; + + /* Check firmware version again */ + err = determine_fw_capabilities(priv, wiphy->fw_version, + sizeof(wiphy->fw_version), + &wiphy->hw_version); + if (err != 0) { + dev_err(dev, "Incompatible firmware, aborting\n"); + goto out; + } + } + + if (priv->has_port3) + dev_info(dev, "Ad-hoc demo mode supported\n"); + if (priv->has_ibss) + dev_info(dev, "IEEE standard IBSS ad-hoc mode supported\n"); + if (priv->has_wep) + dev_info(dev, "WEP supported, %s-bit key\n", + priv->has_big_wep ? "104" : "40"); + if (priv->has_wpa) { + dev_info(dev, "WPA-PSK supported\n"); + if (orinoco_mic_init(priv)) { + dev_err(dev, "Failed to setup MIC crypto algorithm. " + "Disabling WPA support\n"); + priv->has_wpa = 0; + } + } + + err = orinoco_hw_read_card_settings(priv, wiphy->perm_addr); + if (err) + goto out; + + err = orinoco_hw_allocate_fid(priv); + if (err) { + dev_err(dev, "Failed to allocate NIC buffer!\n"); + goto out; + } + + /* Set up the default configuration */ + priv->iw_mode = NL80211_IFTYPE_STATION; + /* By default use IEEE/IBSS ad-hoc mode if we have it */ + priv->prefer_port3 = priv->has_port3 && (!priv->has_ibss); + set_port_type(priv); + priv->channel = 0; /* use firmware default */ + + priv->promiscuous = 0; + priv->encode_alg = ORINOCO_ALG_NONE; + priv->tx_key = 0; + priv->wpa_enabled = 0; + priv->tkip_cm_active = 0; + priv->key_mgmt = 0; + priv->wpa_ie_len = 0; + priv->wpa_ie = NULL; + + if (orinoco_wiphy_register(wiphy)) { + err = -ENODEV; + goto out; + } + + /* Make the hardware available, as long as it hasn't been + * removed elsewhere (e.g. by PCMCIA hot unplug) */ + orinoco_lock_irq(priv); + priv->hw_unavailable--; + orinoco_unlock_irq(priv); + + dev_dbg(dev, "Ready\n"); + + out: + return err; +} +EXPORT_SYMBOL(orinoco_init); + +static const struct net_device_ops orinoco_netdev_ops = { + .ndo_open = orinoco_open, + .ndo_stop = orinoco_stop, + .ndo_start_xmit = orinoco_xmit, + .ndo_set_rx_mode = orinoco_set_multicast_list, + .ndo_change_mtu = orinoco_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = orinoco_tx_timeout, + .ndo_get_stats = orinoco_get_stats, +}; + +/* Allocate private data. + * + * This driver has a number of structures associated with it + * netdev - Net device structure for each network interface + * wiphy - structure associated with wireless phy + * wireless_dev (wdev) - structure for each wireless interface + * hw - structure for hermes chip info + * card - card specific structure for use by the card driver + * (airport, orinoco_cs) + * priv - orinoco private data + * device - generic linux device structure + * + * +---------+ +---------+ + * | wiphy | | netdev | + * | +-------+ | +-------+ + * | | priv | | | wdev | + * | | +-----+ +-+-------+ + * | | | hw | + * | +-+-----+ + * | | card | + * +-+-------+ + * + * priv has a link to netdev and device + * wdev has a link to wiphy + */ +struct orinoco_private +*alloc_orinocodev(int sizeof_card, + struct device *device, + int (*hard_reset)(struct orinoco_private *), + int (*stop_fw)(struct orinoco_private *, int)) +{ + struct orinoco_private *priv; + struct wiphy *wiphy; + + /* allocate wiphy + * NOTE: We only support a single virtual interface + * but this may change when monitor mode is added + */ + wiphy = wiphy_new(&orinoco_cfg_ops, + sizeof(struct orinoco_private) + sizeof_card); + if (!wiphy) + return NULL; + + priv = wiphy_priv(wiphy); + priv->dev = device; + + if (sizeof_card) + priv->card = (void *)((unsigned long)priv + + sizeof(struct orinoco_private)); + else + priv->card = NULL; + + orinoco_wiphy_init(wiphy); + +#ifdef WIRELESS_SPY + priv->wireless_data.spy_data = &priv->spy_data; +#endif + + /* Set up default callbacks */ + priv->hard_reset = hard_reset; + priv->stop_fw = stop_fw; + + spin_lock_init(&priv->lock); + priv->open = 0; + priv->hw_unavailable = 1; /* orinoco_init() must clear this + * before anything else touches the + * hardware */ + INIT_WORK(&priv->reset_work, orinoco_reset); + INIT_WORK(&priv->join_work, orinoco_join_ap); + INIT_WORK(&priv->wevent_work, orinoco_send_wevents); + + INIT_LIST_HEAD(&priv->rx_list); + tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet, + (unsigned long) priv); + + spin_lock_init(&priv->scan_lock); + INIT_LIST_HEAD(&priv->scan_list); + INIT_WORK(&priv->process_scan, orinoco_process_scan_results); + + priv->last_linkstatus = 0xffff; + +#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) + priv->cached_pri_fw = NULL; + priv->cached_fw = NULL; +#endif + + /* Register PM notifiers */ + orinoco_register_pm_notifier(priv); + + return priv; +} +EXPORT_SYMBOL(alloc_orinocodev); + +/* We can only support a single interface. We provide a separate + * function to set it up to distinguish between hardware + * initialisation and interface setup. + * + * The base_addr and irq parameters are passed on to netdev for use + * with SIOCGIFMAP. + */ +int orinoco_if_add(struct orinoco_private *priv, + unsigned long base_addr, + unsigned int irq, + const struct net_device_ops *ops) +{ + struct wiphy *wiphy = priv_to_wiphy(priv); + struct wireless_dev *wdev; + struct net_device *dev; + int ret; + + dev = alloc_etherdev(sizeof(struct wireless_dev)); + + if (!dev) + return -ENOMEM; + + /* Initialise wireless_dev */ + wdev = netdev_priv(dev); + wdev->wiphy = wiphy; + wdev->iftype = NL80211_IFTYPE_STATION; + + /* Setup / override net_device fields */ + dev->ieee80211_ptr = wdev; + dev->watchdog_timeo = HZ; /* 1 second timeout */ + dev->wireless_handlers = &orinoco_handler_def; +#ifdef WIRELESS_SPY + dev->wireless_data = &priv->wireless_data; +#endif + /* Default to standard ops if not set */ + if (ops) + dev->netdev_ops = ops; + else + dev->netdev_ops = &orinoco_netdev_ops; + + /* we use the default eth_mac_addr for setting the MAC addr */ + + /* Reserve space in skb for the SNAP header */ + dev->needed_headroom = ENCAPS_OVERHEAD; + + netif_carrier_off(dev); + + memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); + + dev->base_addr = base_addr; + dev->irq = irq; + + SET_NETDEV_DEV(dev, priv->dev); + ret = register_netdev(dev); + if (ret) + goto fail; + + priv->ndev = dev; + + /* Report what we've done */ + dev_dbg(priv->dev, "Registerred interface %s.\n", dev->name); + + return 0; + + fail: + free_netdev(dev); + return ret; +} +EXPORT_SYMBOL(orinoco_if_add); + +void orinoco_if_del(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + + unregister_netdev(dev); + free_netdev(dev); +} +EXPORT_SYMBOL(orinoco_if_del); + +void free_orinocodev(struct orinoco_private *priv) +{ + struct wiphy *wiphy = priv_to_wiphy(priv); + struct orinoco_rx_data *rx_data, *temp; + struct orinoco_scan_data *sd, *sdtemp; + + /* If the tasklet is scheduled when we call tasklet_kill it + * will run one final time. However the tasklet will only + * drain priv->rx_list if the hw is still available. */ + tasklet_kill(&priv->rx_tasklet); + + /* Explicitly drain priv->rx_list */ + list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) { + list_del(&rx_data->list); + + dev_kfree_skb(rx_data->skb); + kfree(rx_data->desc); + kfree(rx_data); + } + + cancel_work_sync(&priv->process_scan); + /* Explicitly drain priv->scan_list */ + list_for_each_entry_safe(sd, sdtemp, &priv->scan_list, list) { + list_del(&sd->list); + + if (sd->len > 0) + kfree(sd->buf); + kfree(sd); + } + + orinoco_unregister_pm_notifier(priv); + orinoco_uncache_fw(priv); + + priv->wpa_ie_len = 0; + kfree(priv->wpa_ie); + orinoco_mic_free(priv); + wiphy_free(wiphy); +} +EXPORT_SYMBOL(free_orinocodev); + +int orinoco_up(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + unsigned long flags; + int err; + + priv->hw.ops->lock_irqsave(&priv->lock, &flags); + + err = orinoco_reinit_firmware(priv); + if (err) { + printk(KERN_ERR "%s: Error %d re-initializing firmware\n", + dev->name, err); + goto exit; + } + + netif_device_attach(dev); + priv->hw_unavailable--; + + if (priv->open && !priv->hw_unavailable) { + err = __orinoco_up(priv); + if (err) + printk(KERN_ERR "%s: Error %d restarting card\n", + dev->name, err); + } + +exit: + priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); + + return 0; +} +EXPORT_SYMBOL(orinoco_up); + +void orinoco_down(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + unsigned long flags; + int err; + + priv->hw.ops->lock_irqsave(&priv->lock, &flags); + err = __orinoco_down(priv); + if (err) + printk(KERN_WARNING "%s: Error %d downing interface\n", + dev->name, err); + + netif_device_detach(dev); + priv->hw_unavailable++; + priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); +} +EXPORT_SYMBOL(orinoco_down); + +/********************************************************************/ +/* Module initialization */ +/********************************************************************/ + +/* Can't be declared "const" or the whole __initdata section will + * become const */ +static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION + " (David Gibson , " + "Pavel Roskin , et al)"; + +static int __init init_orinoco(void) +{ + printk(KERN_DEBUG "%s\n", version); + return 0; +} + +static void __exit exit_orinoco(void) +{ +} + +module_init(init_orinoco); +module_exit(exit_orinoco); diff --git a/drivers/net/wireless/intersil/orinoco/main.h b/drivers/net/wireless/intersil/orinoco/main.h new file mode 100644 index 000000000000..5a8fec26136e --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/main.h @@ -0,0 +1,50 @@ +/* Exports from main to helper modules + * + * See copyright notice in main.c + */ +#ifndef _ORINOCO_MAIN_H_ +#define _ORINOCO_MAIN_H_ + +#include +#include "orinoco.h" + +/********************************************************************/ +/* Compile time configuration and compatibility stuff */ +/********************************************************************/ + +/* We do this this way to avoid ifdefs in the actual code */ +#ifdef WIRELESS_SPY +#define SPY_NUMBER(priv) (priv->spy_data.spy_number) +#else +#define SPY_NUMBER(priv) 0 +#endif /* WIRELESS_SPY */ + +/********************************************************************/ + +/* Export module parameter */ +extern int force_monitor; + +/* Forward declarations */ +struct net_device; +struct work_struct; + +void set_port_type(struct orinoco_private *priv); +int orinoco_commit(struct orinoco_private *priv); +void orinoco_reset(struct work_struct *work); + +/* Information element helpers - find a home for these... */ +#define WPA_OUI_TYPE "\x00\x50\xF2\x01" +#define WPA_SELECTOR_LEN 4 +static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len) +{ + u8 *p = data; + while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) { + if ((p[0] == WLAN_EID_VENDOR_SPECIFIC) && + (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0)) + return p; + p += p[1] + 2; + } + return NULL; +} + +#endif /* _ORINOCO_MAIN_H_ */ diff --git a/drivers/net/wireless/intersil/orinoco/mic.c b/drivers/net/wireless/intersil/orinoco/mic.c new file mode 100644 index 000000000000..fce4a843e656 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/mic.c @@ -0,0 +1,79 @@ +/* Orinoco MIC helpers + * + * See copyright notice in main.c + */ +#include +#include +#include +#include +#include + +#include "orinoco.h" +#include "mic.h" + +/********************************************************************/ +/* Michael MIC crypto setup */ +/********************************************************************/ +int orinoco_mic_init(struct orinoco_private *priv) +{ + priv->tx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0); + if (IS_ERR(priv->tx_tfm_mic)) { + printk(KERN_DEBUG "orinoco_mic_init: could not allocate " + "crypto API michael_mic\n"); + priv->tx_tfm_mic = NULL; + return -ENOMEM; + } + + priv->rx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0); + if (IS_ERR(priv->rx_tfm_mic)) { + printk(KERN_DEBUG "orinoco_mic_init: could not allocate " + "crypto API michael_mic\n"); + priv->rx_tfm_mic = NULL; + return -ENOMEM; + } + + return 0; +} + +void orinoco_mic_free(struct orinoco_private *priv) +{ + if (priv->tx_tfm_mic) + crypto_free_hash(priv->tx_tfm_mic); + if (priv->rx_tfm_mic) + crypto_free_hash(priv->rx_tfm_mic); +} + +int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key, + u8 *da, u8 *sa, u8 priority, + u8 *data, size_t data_len, u8 *mic) +{ + struct hash_desc desc; + struct scatterlist sg[2]; + u8 hdr[ETH_HLEN + 2]; /* size of header + padding */ + + if (tfm_michael == NULL) { + printk(KERN_WARNING "orinoco_mic: tfm_michael == NULL\n"); + return -1; + } + + /* Copy header into buffer. We need the padding on the end zeroed */ + memcpy(&hdr[0], da, ETH_ALEN); + memcpy(&hdr[ETH_ALEN], sa, ETH_ALEN); + hdr[ETH_ALEN * 2] = priority; + hdr[ETH_ALEN * 2 + 1] = 0; + hdr[ETH_ALEN * 2 + 2] = 0; + hdr[ETH_ALEN * 2 + 3] = 0; + + /* Use scatter gather to MIC header and data in one go */ + sg_init_table(sg, 2); + sg_set_buf(&sg[0], hdr, sizeof(hdr)); + sg_set_buf(&sg[1], data, data_len); + + if (crypto_hash_setkey(tfm_michael, key, MIC_KEYLEN)) + return -1; + + desc.tfm = tfm_michael; + desc.flags = 0; + return crypto_hash_digest(&desc, sg, data_len + sizeof(hdr), + mic); +} diff --git a/drivers/net/wireless/intersil/orinoco/mic.h b/drivers/net/wireless/intersil/orinoco/mic.h new file mode 100644 index 000000000000..04d05bc566d6 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/mic.h @@ -0,0 +1,22 @@ +/* Orinoco MIC helpers + * + * See copyright notice in main.c + */ +#ifndef _ORINOCO_MIC_H_ +#define _ORINOCO_MIC_H_ + +#include + +#define MICHAEL_MIC_LEN 8 + +/* Forward declarations */ +struct orinoco_private; +struct crypto_hash; + +int orinoco_mic_init(struct orinoco_private *priv); +void orinoco_mic_free(struct orinoco_private *priv); +int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key, + u8 *da, u8 *sa, u8 priority, + u8 *data, size_t data_len, u8 *mic); + +#endif /* ORINOCO_MIC_H */ diff --git a/drivers/net/wireless/intersil/orinoco/orinoco.h b/drivers/net/wireless/intersil/orinoco/orinoco.h new file mode 100644 index 000000000000..eebd2be21ee9 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/orinoco.h @@ -0,0 +1,253 @@ +/* orinoco.h + * + * Common definitions to all pieces of the various orinoco + * drivers + */ + +#ifndef _ORINOCO_H +#define _ORINOCO_H + +#define DRIVER_VERSION "0.15" + +#include +#include +#include +#include +#include +#include + +#include "hermes.h" + +/* To enable debug messages */ +/*#define ORINOCO_DEBUG 3*/ + +#define WIRELESS_SPY /* enable iwspy support */ + +#define MAX_SCAN_LEN 4096 + +#define ORINOCO_SEQ_LEN 8 +#define ORINOCO_MAX_KEY_SIZE 14 +#define ORINOCO_MAX_KEYS 4 + +struct orinoco_key { + __le16 len; /* always stored as little-endian */ + char data[ORINOCO_MAX_KEY_SIZE]; +} __packed; + +#define TKIP_KEYLEN 16 +#define MIC_KEYLEN 8 + +struct orinoco_tkip_key { + u8 tkip[TKIP_KEYLEN]; + u8 tx_mic[MIC_KEYLEN]; + u8 rx_mic[MIC_KEYLEN]; +}; + +enum orinoco_alg { + ORINOCO_ALG_NONE, + ORINOCO_ALG_WEP, + ORINOCO_ALG_TKIP +}; + +enum fwtype { + FIRMWARE_TYPE_AGERE, + FIRMWARE_TYPE_INTERSIL, + FIRMWARE_TYPE_SYMBOL +}; + +struct firmware; + +struct orinoco_private { + void *card; /* Pointer to card dependent structure */ + struct device *dev; + int (*hard_reset)(struct orinoco_private *); + int (*stop_fw)(struct orinoco_private *, int); + + struct ieee80211_supported_band band; + struct ieee80211_channel channels[14]; + u32 cipher_suites[3]; + + /* Synchronisation stuff */ + spinlock_t lock; + int hw_unavailable; + struct work_struct reset_work; + + /* Interrupt tasklets */ + struct tasklet_struct rx_tasklet; + struct list_head rx_list; + + /* driver state */ + int open; + u16 last_linkstatus; + struct work_struct join_work; + struct work_struct wevent_work; + + /* Net device stuff */ + struct net_device *ndev; + struct net_device_stats stats; + struct iw_statistics wstats; + + /* Hardware control variables */ + struct hermes hw; + u16 txfid; + + /* Capabilities of the hardware/firmware */ + enum fwtype firmware_type; + int ibss_port; + int nicbuf_size; + u16 channel_mask; + + /* Boolean capabilities */ + unsigned int has_ibss:1; + unsigned int has_port3:1; + unsigned int has_wep:1; + unsigned int has_big_wep:1; + unsigned int has_mwo:1; + unsigned int has_pm:1; + unsigned int has_preamble:1; + unsigned int has_sensitivity:1; + unsigned int has_hostscan:1; + unsigned int has_alt_txcntl:1; + unsigned int has_ext_scan:1; + unsigned int has_wpa:1; + unsigned int do_fw_download:1; + unsigned int broken_disableport:1; + unsigned int broken_monitor:1; + unsigned int prefer_port3:1; + + /* Configuration paramaters */ + enum nl80211_iftype iw_mode; + enum orinoco_alg encode_alg; + u16 wep_restrict, tx_key; + struct key_params keys[ORINOCO_MAX_KEYS]; + + int bitratemode; + char nick[IW_ESSID_MAX_SIZE + 1]; + char desired_essid[IW_ESSID_MAX_SIZE + 1]; + char desired_bssid[ETH_ALEN]; + int bssid_fixed; + u16 frag_thresh, mwo_robust; + u16 channel; + u16 ap_density, rts_thresh; + u16 pm_on, pm_mcast, pm_period, pm_timeout; + u16 preamble; + u16 short_retry_limit, long_retry_limit; + u16 retry_lifetime; +#ifdef WIRELESS_SPY + struct iw_spy_data spy_data; /* iwspy support */ + struct iw_public_data wireless_data; +#endif + + /* Configuration dependent variables */ + int port_type, createibss; + int promiscuous, mc_count; + + /* Scanning support */ + struct cfg80211_scan_request *scan_request; + struct work_struct process_scan; + struct list_head scan_list; + spinlock_t scan_lock; /* protects the scan list */ + + /* WPA support */ + u8 *wpa_ie; + int wpa_ie_len; + + struct crypto_hash *rx_tfm_mic; + struct crypto_hash *tx_tfm_mic; + + unsigned int wpa_enabled:1; + unsigned int tkip_cm_active:1; + unsigned int key_mgmt:3; + +#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) + /* Cached in memory firmware to use during ->resume. */ + const struct firmware *cached_pri_fw; + const struct firmware *cached_fw; +#endif + + struct notifier_block pm_notifier; +}; + +#ifdef ORINOCO_DEBUG +extern int orinoco_debug; +#define DEBUG(n, args...) do { \ + if (orinoco_debug > (n)) \ + printk(KERN_DEBUG args); \ +} while (0) +#else +#define DEBUG(n, args...) do { } while (0) +#endif /* ORINOCO_DEBUG */ + +/********************************************************************/ +/* Exported prototypes */ +/********************************************************************/ + +struct orinoco_private *alloc_orinocodev(int sizeof_card, struct device *device, + int (*hard_reset)(struct orinoco_private *), + int (*stop_fw)(struct orinoco_private *, int)); +void free_orinocodev(struct orinoco_private *priv); +int orinoco_init(struct orinoco_private *priv); +int orinoco_if_add(struct orinoco_private *priv, unsigned long base_addr, + unsigned int irq, const struct net_device_ops *ops); +void orinoco_if_del(struct orinoco_private *priv); +int orinoco_up(struct orinoco_private *priv); +void orinoco_down(struct orinoco_private *priv); +irqreturn_t orinoco_interrupt(int irq, void *dev_id); + +void __orinoco_ev_info(struct net_device *dev, struct hermes *hw); +void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw); + +int orinoco_process_xmit_skb(struct sk_buff *skb, + struct net_device *dev, + struct orinoco_private *priv, + int *tx_control, + u8 *mic); + +/* Common ndo functions exported for reuse by orinoco_usb */ +int orinoco_open(struct net_device *dev); +int orinoco_stop(struct net_device *dev); +struct net_device_stats *orinoco_get_stats(struct net_device *dev); +void orinoco_set_multicast_list(struct net_device *dev); +int orinoco_change_mtu(struct net_device *dev, int new_mtu); +void orinoco_tx_timeout(struct net_device *dev); + +/********************************************************************/ +/* Locking and synchronization functions */ +/********************************************************************/ + +static inline int orinoco_lock(struct orinoco_private *priv, + unsigned long *flags) +{ + priv->hw.ops->lock_irqsave(&priv->lock, flags); + if (priv->hw_unavailable) { + DEBUG(1, "orinoco_lock() called with hw_unavailable (dev=%p)\n", + priv->ndev); + priv->hw.ops->unlock_irqrestore(&priv->lock, flags); + return -EBUSY; + } + return 0; +} + +static inline void orinoco_unlock(struct orinoco_private *priv, + unsigned long *flags) +{ + priv->hw.ops->unlock_irqrestore(&priv->lock, flags); +} + +static inline void orinoco_lock_irq(struct orinoco_private *priv) +{ + priv->hw.ops->lock_irq(&priv->lock); +} + +static inline void orinoco_unlock_irq(struct orinoco_private *priv) +{ + priv->hw.ops->unlock_irq(&priv->lock); +} + +/*** Navigate from net_device to orinoco_private ***/ +static inline struct orinoco_private *ndev_priv(struct net_device *dev) +{ + struct wireless_dev *wdev = netdev_priv(dev); + return wdev_priv(wdev); +} +#endif /* _ORINOCO_H */ diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_cs.c b/drivers/net/wireless/intersil/orinoco/orinoco_cs.c new file mode 100644 index 000000000000..a956f965a1e5 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/orinoco_cs.c @@ -0,0 +1,341 @@ +/* orinoco_cs.c (formerly known as dldwd_cs.c) + * + * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such + * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ + * EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and others). + * It should also be usable on various Prism II based cards such as the + * Linksys, D-Link and Farallon Skyline. It should also work on Symbol + * cards such as the 3Com AirConnect and Ericsson WLAN. + * + * Copyright notice & release notes in file main.c + */ + +#define DRIVER_NAME "orinoco_cs" +#define PFX DRIVER_NAME ": " + +#include +#include +#include +#include +#include +#include + +#include "orinoco.h" + +/********************************************************************/ +/* Module stuff */ +/********************************************************************/ + +MODULE_AUTHOR("David Gibson "); +MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco," + " Prism II based and similar wireless cards"); +MODULE_LICENSE("Dual MPL/GPL"); + +/* Module parameters */ + +/* Some D-Link cards have buggy CIS. They do work at 5v properly, but + * don't have any CIS entry for it. This workaround it... */ +static int ignore_cis_vcc; /* = 0 */ +module_param(ignore_cis_vcc, int, 0); +MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket"); + +/********************************************************************/ +/* Data structures */ +/********************************************************************/ + +/* PCMCIA specific device information (goes in the card field of + * struct orinoco_private */ +struct orinoco_pccard { + struct pcmcia_device *p_dev; + + /* Used to handle hard reset */ + /* yuck, we need this hack to work around the insanity of the + * PCMCIA layer */ + unsigned long hard_reset_in_progress; +}; + + +/********************************************************************/ +/* Function prototypes */ +/********************************************************************/ + +static int orinoco_cs_config(struct pcmcia_device *link); +static void orinoco_cs_release(struct pcmcia_device *link); +static void orinoco_cs_detach(struct pcmcia_device *p_dev); + +/********************************************************************/ +/* Device methods */ +/********************************************************************/ + +static int +orinoco_cs_hard_reset(struct orinoco_private *priv) +{ + struct orinoco_pccard *card = priv->card; + struct pcmcia_device *link = card->p_dev; + int err; + + /* We need atomic ops here, because we're not holding the lock */ + set_bit(0, &card->hard_reset_in_progress); + + err = pcmcia_reset_card(link->socket); + if (err) + return err; + + msleep(100); + clear_bit(0, &card->hard_reset_in_progress); + + return 0; +} + +/********************************************************************/ +/* PCMCIA stuff */ +/********************************************************************/ + +static int +orinoco_cs_probe(struct pcmcia_device *link) +{ + struct orinoco_private *priv; + struct orinoco_pccard *card; + + priv = alloc_orinocodev(sizeof(*card), &link->dev, + orinoco_cs_hard_reset, NULL); + if (!priv) + return -ENOMEM; + card = priv->card; + + /* Link both structures together */ + card->p_dev = link; + link->priv = priv; + + return orinoco_cs_config(link); +} /* orinoco_cs_attach */ + +static void orinoco_cs_detach(struct pcmcia_device *link) +{ + struct orinoco_private *priv = link->priv; + + orinoco_if_del(priv); + + orinoco_cs_release(link); + + wiphy_unregister(priv_to_wiphy(priv)); + free_orinocodev(priv); +} /* orinoco_cs_detach */ + +static int orinoco_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + if (p_dev->config_index == 0) + return -EINVAL; + + return pcmcia_request_io(p_dev); +}; + +static int +orinoco_cs_config(struct pcmcia_device *link) +{ + struct orinoco_private *priv = link->priv; + struct hermes *hw = &priv->hw; + int ret; + void __iomem *mem; + + link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC | + CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; + if (ignore_cis_vcc) + link->config_flags &= ~CONF_AUTO_CHECK_VCC; + ret = pcmcia_loop_config(link, orinoco_cs_config_check, NULL); + if (ret) { + if (!ignore_cis_vcc) + printk(KERN_ERR PFX "GetNextTuple(): No matching " + "CIS configuration. Maybe you need the " + "ignore_cis_vcc=1 parameter.\n"); + goto failed; + } + + mem = ioport_map(link->resource[0]->start, + resource_size(link->resource[0])); + if (!mem) + goto failed; + + /* We initialize the hermes structure before completing PCMCIA + * configuration just in case the interrupt handler gets + * called. */ + hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING); + + ret = pcmcia_request_irq(link, orinoco_interrupt); + if (ret) + goto failed; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + /* Initialise the main driver */ + if (orinoco_init(priv) != 0) { + printk(KERN_ERR PFX "orinoco_init() failed\n"); + goto failed; + } + + /* Register an interface with the stack */ + if (orinoco_if_add(priv, link->resource[0]->start, + link->irq, NULL) != 0) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); + goto failed; + } + + return 0; + + failed: + orinoco_cs_release(link); + return -ENODEV; +} /* orinoco_cs_config */ + +static void +orinoco_cs_release(struct pcmcia_device *link) +{ + struct orinoco_private *priv = link->priv; + unsigned long flags; + + /* We're committed to taking the device away now, so mark the + * hardware as unavailable */ + priv->hw.ops->lock_irqsave(&priv->lock, &flags); + priv->hw_unavailable++; + priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); + + pcmcia_disable_device(link); + if (priv->hw.iobase) + ioport_unmap(priv->hw.iobase); +} /* orinoco_cs_release */ + +static int orinoco_cs_suspend(struct pcmcia_device *link) +{ + struct orinoco_private *priv = link->priv; + struct orinoco_pccard *card = priv->card; + + /* This is probably racy, but I can't think of + a better way, short of rewriting the PCMCIA + layer to not suck :-( */ + if (!test_bit(0, &card->hard_reset_in_progress)) + orinoco_down(priv); + + return 0; +} + +static int orinoco_cs_resume(struct pcmcia_device *link) +{ + struct orinoco_private *priv = link->priv; + struct orinoco_pccard *card = priv->card; + int err = 0; + + if (!test_bit(0, &card->hard_reset_in_progress)) + err = orinoco_up(priv); + + return err; +} + + +/********************************************************************/ +/* Module initialization */ +/********************************************************************/ + +static const struct pcmcia_device_id orinoco_cs_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), /* 3Com AirConnect PCI 777A */ + PCMCIA_DEVICE_MANF_CARD(0x016b, 0x0001), /* Ericsson WLAN Card C11 */ + PCMCIA_DEVICE_MANF_CARD(0x01eb, 0x080a), /* Nortel Networks eMobility 802.11 Wireless Adapter */ + PCMCIA_DEVICE_MANF_CARD(0x0261, 0x0002), /* AirWay 802.11 Adapter (PCMCIA) */ + PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0001), /* ARtem Onair */ + PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0003), /* ARtem Onair Comcard 11 */ + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0305), /* Buffalo WLI-PCM-S11 */ + PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002), /* ASUS SpaceLink WL-100 */ + PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x0002), /* SpeedStream SS1021 Wireless Adapter */ + PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x3021), /* SpeedStream Wireless Adapter */ + PCMCIA_DEVICE_MANF_CARD(0x14ea, 0xb001), /* PLANEX RoadLannerWave GW-NS11H */ + PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE737A AirConnect Wireless LAN PC Card", 0x41240e5b, 0x56010af3), + PCMCIA_DEVICE_PROD_ID12("Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", 0x5cd01705, 0x4271660f), + PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11B_CF_CARD_25", 0x78fc06ee, 0x45a50c1e), + PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11b_PC_CARD_25", 0x78fc06ee, 0xdb9aa842), + PCMCIA_DEVICE_PROD_ID12("Avaya Communication", "Avaya Wireless PC Card", 0xd8a43b78, 0x0d341169), + PCMCIA_DEVICE_PROD_ID12("BENQ", "AWL100 PCMCIA ADAPTER", 0x35dadc74, 0x01f7fedb), + PCMCIA_DEVICE_PROD_ID12("Cabletron", "RoamAbout 802.11 DS", 0x32d445f5, 0xedeffd90), + PCMCIA_DEVICE_PROD_ID12("D-Link Corporation", "D-Link DWL-650H 11Mbps WLAN Adapter", 0xef544d24, 0xcd8ea916), + PCMCIA_DEVICE_PROD_ID12("ELSA", "AirLancer MC-11", 0x4507a33a, 0xef54f0e3), + PCMCIA_DEVICE_PROD_ID12("HyperLink", "Wireless PC Card 11Mbps", 0x56cc3f1a, 0x0bcf220c), + PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless 2011 LAN PC Card", 0x816cc815, 0x07f58077), + PCMCIA_DEVICE_PROD_ID12("LeArtery", "SYNCBYAIR 11Mbps Wireless LAN PC Card", 0x7e3b326a, 0x49893e92), + PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/IEEE", 0x23eb9949, 0xc562e72a), + PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11", 0x481e0094, 0x7360e410), + PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11G", 0x481e0094, 0xf57ca4b3), + PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/IEEE", 0x24358cd4, 0xc562e72a), + PCMCIA_DEVICE_PROD_ID12("Nortel Networks", "emobility 802.11 Wireless LAN PC Card", 0x2d617ea0, 0x88cd5767), + PCMCIA_DEVICE_PROD_ID12("OTC", "Wireless AirEZY 2411-PCC WLAN Card", 0x4ac44287, 0x235a6bed), + PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PC CARD HARMONY 80211B", 0xc6536a5e, 0x090c3cd9), + PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PCI CARD HARMONY 80211B", 0xc6536a5e, 0x9f494e26), + PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "11Mbps WLAN Card", 0x43d74cb4, 0x579bd91b), + PCMCIA_DEVICE_PROD_ID12("Symbol Technologies", "LA4111 Spectrum24 Wireless LAN PC Card", 0x3f02b4d6, 0x3663cb0e), + PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.01", 0xd27deb1a), /* Lucent Orinoco */ +#ifdef CONFIG_HERMES_PRISM + /* Only entries that certainly identify Prism chipset */ + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), /* SonicWALL Long Range Wireless Card */ + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), /* Sohoware NCP110, Philips 802.11b */ + PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0002), /* AnyPoint(TM) Wireless II PC Card */ + PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), /* PROXIM RangeLAN-DS/LAN PC CARD */ + PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), /* Compaq WL100 11 Mbps Wireless Adapter */ + PCMCIA_DEVICE_MANF_CARD(0x01ff, 0x0008), /* Intermec MobileLAN 11Mbps 802.11b WLAN Card */ + PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), /* Samsung SWL2000-N 11Mb/s WLAN Card */ + PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), /* Linksys WPC11 Version 2.5 */ + PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), /* Linksys WPC11 Version 3 */ + PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), /* Compaq HNW-100 11 Mbps Wireless Adapter */ + PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0673), /* Linksys WCF12 Wireless CompactFlash Card */ + PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), /* Airvast WN-100 */ + PCMCIA_DEVICE_MANF_CARD(0x9005, 0x0021), /* Adaptec Ultra Wireless ANW-8030 */ + PCMCIA_DEVICE_MANF_CARD(0xc001, 0x0008), /* CONTEC FLEXSCAN/FX-DDS110-PCC */ + PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), /* Conceptronic CON11Cpro, EMTAC A2424i */ + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), /* Safeway 802.11b, ZCOMAX AirRunner/XI-300 */ + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), /* D-Link DCF660, Sandisk Connect SDWCFB-000 */ + PCMCIA_DEVICE_PROD_ID123("Instant Wireless ", " Network PC CARD", "Version 01.02", 0x11d901af, 0x6e9bd926, 0x4b74baa0), + PCMCIA_DEVICE_PROD_ID12("ACTIONTEC", "PRISM Wireless LAN PC Card", 0x393089da, 0xa71e69d5), + PCMCIA_DEVICE_PROD_ID12("Addtron", "AWP-100 Wireless PCMCIA", 0xe6ec52ce, 0x08649af2), + PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", 0x2decece3, 0x82067c18), + PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-PCM-L11G", 0x2decece3, 0xf57ca4b3), + PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card", 0x54f7c49c, 0x15a75e5b), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCC-11", 0x5261440f, 0xa6405584), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCCA-11", 0x5261440f, 0xdf6115f9), + PCMCIA_DEVICE_PROD_ID12("corega_K.K.", "Wireless_LAN_PCCB-11", 0x29e33311, 0xee7a27ae), + PCMCIA_DEVICE_PROD_ID12("Digital Data Communications", "WPC-0100", 0xfdd73470, 0xe0b6f146), + PCMCIA_DEVICE_PROD_ID12("D", "Link DRC-650 11Mbps WLAN Card", 0x71b18589, 0xf144e3ac), + PCMCIA_DEVICE_PROD_ID12("D", "Link DWL-650 11Mbps WLAN Card", 0x71b18589, 0xb6f1b0ab), + PCMCIA_DEVICE_PROD_ID12(" ", "IEEE 802.11 Wireless LAN/PC Card", 0x3b6e20c8, 0xefccafe9), + PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", 0x74c5e40d, 0xdb472a18), + PCMCIA_DEVICE_PROD_ID12("INTERSIL", "I-GATE 11M PC Card / PC Card plus", 0x74c5e40d, 0x8304ff77), + PCMCIA_DEVICE_PROD_ID12("Intersil", "PRISM 2_5 PCMCIA ADAPTER", 0x4b801a17, 0x6345a0bf), + PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card", 0x0733cc81, 0x0c52f395), + PCMCIA_DEVICE_PROD_ID12("Microsoft", "Wireless Notebook Adapter MN-520", 0x5961bf85, 0x6eec8c01), + PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401RA Wireless PC", "Card", 0x0306467f, 0x9762e8f1), + PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", 0xa37434e9, 0x9762e8f1), + PCMCIA_DEVICE_PROD_ID12("OEM", "PRISM2 IEEE 802.11 PC-Card", 0xfea54c90, 0x48f2bdd6), + PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-CF110", 0x209f40ab, 0xd9715264), + PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-NS110", 0x209f40ab, 0x46263178), + PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2532W-B EliteConnect Wireless Adapter", 0xc4f8b18b, 0x196bd757), + PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2632W", 0xc4f8b18b, 0x474a1f2a), + PCMCIA_DEVICE_PROD_ID12("ZoomAir 11Mbps High", "Rate wireless Networking", 0x273fe3db, 0x32a1eaee), + PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092), + PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2), + PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b), + PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39), + + /* This may be Agere or Intersil Firmware */ + PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002), +#endif + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, orinoco_cs_ids); + +static struct pcmcia_driver orinoco_driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .probe = orinoco_cs_probe, + .remove = orinoco_cs_detach, + .id_table = orinoco_cs_ids, + .suspend = orinoco_cs_suspend, + .resume = orinoco_cs_resume, +}; +module_pcmcia_driver(orinoco_driver); diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_nortel.c b/drivers/net/wireless/intersil/orinoco/orinoco_nortel.c new file mode 100644 index 000000000000..048693b6c6c2 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/orinoco_nortel.c @@ -0,0 +1,323 @@ +/* orinoco_nortel.c + * + * Driver for Prism II devices which would usually be driven by orinoco_cs, + * but are connected to the PCI bus by a PCI-to-PCMCIA adapter used in + * Nortel emobility, Symbol LA-4113 and Symbol LA-4123. + * + * Copyright (C) 2002 Tobias Hoffmann + * (C) 2003 Christoph Jungegger + * + * Some of this code is borrowed from orinoco_plx.c + * Copyright (C) 2001 Daniel Barlow + * Some of this code is borrowed from orinoco_pci.c + * Copyright (C) 2001 Jean Tourrilhes + * Some of this code is "inspired" by linux-wlan-ng-0.1.10, but nothing + * has been copied from it. linux-wlan-ng-0.1.10 is originally : + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + */ + +#define DRIVER_NAME "orinoco_nortel" +#define PFX DRIVER_NAME ": " + +#include +#include +#include +#include +#include +#include + +#include "orinoco.h" +#include "orinoco_pci.h" + +#define COR_OFFSET (0xe0) /* COR attribute offset of Prism2 PC card */ +#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */ + + +/* + * Do a soft reset of the card using the Configuration Option Register + * We need this to get going... + * This is the part of the code that is strongly inspired from wlan-ng + * + * Note bis : Don't try to access HERMES_CMD during the reset phase. + * It just won't work ! + */ +static int orinoco_nortel_cor_reset(struct orinoco_private *priv) +{ + struct orinoco_pci_card *card = priv->card; + + /* Assert the reset until the card notices */ + iowrite16(8, card->bridge_io + 2); + ioread16(card->attr_io + COR_OFFSET); + iowrite16(0x80, card->attr_io + COR_OFFSET); + mdelay(1); + + /* Give time for the card to recover from this hard effort */ + iowrite16(0, card->attr_io + COR_OFFSET); + iowrite16(0, card->attr_io + COR_OFFSET); + mdelay(1); + + /* Set COR as usual */ + iowrite16(COR_VALUE, card->attr_io + COR_OFFSET); + iowrite16(COR_VALUE, card->attr_io + COR_OFFSET); + mdelay(1); + + iowrite16(0x228, card->bridge_io + 2); + + return 0; +} + +static int orinoco_nortel_hw_init(struct orinoco_pci_card *card) +{ + int i; + u32 reg; + + /* Setup bridge */ + if (ioread16(card->bridge_io) & 1) { + printk(KERN_ERR PFX "brg1 answer1 wrong\n"); + return -EBUSY; + } + iowrite16(0x118, card->bridge_io + 2); + iowrite16(0x108, card->bridge_io + 2); + mdelay(30); + iowrite16(0x8, card->bridge_io + 2); + for (i = 0; i < 30; i++) { + mdelay(30); + if (ioread16(card->bridge_io) & 0x10) + break; + } + if (i == 30) { + printk(KERN_ERR PFX "brg1 timed out\n"); + return -EBUSY; + } + if (ioread16(card->attr_io + COR_OFFSET) & 1) { + printk(KERN_ERR PFX "brg2 answer1 wrong\n"); + return -EBUSY; + } + if (ioread16(card->attr_io + COR_OFFSET + 2) & 1) { + printk(KERN_ERR PFX "brg2 answer2 wrong\n"); + return -EBUSY; + } + if (ioread16(card->attr_io + COR_OFFSET + 4) & 1) { + printk(KERN_ERR PFX "brg2 answer3 wrong\n"); + return -EBUSY; + } + + /* Set the PCMCIA COR register */ + iowrite16(COR_VALUE, card->attr_io + COR_OFFSET); + mdelay(1); + reg = ioread16(card->attr_io + COR_OFFSET); + if (reg != COR_VALUE) { + printk(KERN_ERR PFX "Error setting COR value (reg=%x)\n", + reg); + return -EBUSY; + } + + /* Set LEDs */ + iowrite16(1, card->bridge_io + 10); + return 0; +} + +static int orinoco_nortel_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int err; + struct orinoco_private *priv; + struct orinoco_pci_card *card; + void __iomem *hermes_io, *bridge_io, *attr_io; + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR PFX "Cannot enable PCI device\n"); + return err; + } + + err = pci_request_regions(pdev, DRIVER_NAME); + if (err) { + printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); + goto fail_resources; + } + + bridge_io = pci_iomap(pdev, 0, 0); + if (!bridge_io) { + printk(KERN_ERR PFX "Cannot map bridge registers\n"); + err = -EIO; + goto fail_map_bridge; + } + + attr_io = pci_iomap(pdev, 1, 0); + if (!attr_io) { + printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n"); + err = -EIO; + goto fail_map_attr; + } + + hermes_io = pci_iomap(pdev, 2, 0); + if (!hermes_io) { + printk(KERN_ERR PFX "Cannot map chipset registers\n"); + err = -EIO; + goto fail_map_hermes; + } + + /* Allocate network device */ + priv = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_nortel_cor_reset, NULL); + if (!priv) { + printk(KERN_ERR PFX "Cannot allocate network device\n"); + err = -ENOMEM; + goto fail_alloc; + } + + card = priv->card; + card->bridge_io = bridge_io; + card->attr_io = attr_io; + + hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); + + err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, + DRIVER_NAME, priv); + if (err) { + printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); + err = -EBUSY; + goto fail_irq; + } + + err = orinoco_nortel_hw_init(card); + if (err) { + printk(KERN_ERR PFX "Hardware initialization failed\n"); + goto fail; + } + + err = orinoco_nortel_cor_reset(priv); + if (err) { + printk(KERN_ERR PFX "Initial reset failed\n"); + goto fail; + } + + err = orinoco_init(priv); + if (err) { + printk(KERN_ERR PFX "orinoco_init() failed\n"); + goto fail; + } + + err = orinoco_if_add(priv, 0, 0, NULL); + if (err) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); + goto fail_wiphy; + } + + pci_set_drvdata(pdev, priv); + + return 0; + + fail_wiphy: + wiphy_unregister(priv_to_wiphy(priv)); + fail: + free_irq(pdev->irq, priv); + + fail_irq: + free_orinocodev(priv); + + fail_alloc: + pci_iounmap(pdev, hermes_io); + + fail_map_hermes: + pci_iounmap(pdev, attr_io); + + fail_map_attr: + pci_iounmap(pdev, bridge_io); + + fail_map_bridge: + pci_release_regions(pdev); + + fail_resources: + pci_disable_device(pdev); + + return err; +} + +static void orinoco_nortel_remove_one(struct pci_dev *pdev) +{ + struct orinoco_private *priv = pci_get_drvdata(pdev); + struct orinoco_pci_card *card = priv->card; + + /* Clear LEDs */ + iowrite16(0, card->bridge_io + 10); + + orinoco_if_del(priv); + wiphy_unregister(priv_to_wiphy(priv)); + free_irq(pdev->irq, priv); + free_orinocodev(priv); + pci_iounmap(pdev, priv->hw.iobase); + pci_iounmap(pdev, card->attr_io); + pci_iounmap(pdev, card->bridge_io); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static const struct pci_device_id orinoco_nortel_id_table[] = { + /* Nortel emobility PCI */ + {0x126c, 0x8030, PCI_ANY_ID, PCI_ANY_ID,}, + /* Symbol LA-4123 PCI */ + {0x1562, 0x0001, PCI_ANY_ID, PCI_ANY_ID,}, + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, orinoco_nortel_id_table); + +static struct pci_driver orinoco_nortel_driver = { + .name = DRIVER_NAME, + .id_table = orinoco_nortel_id_table, + .probe = orinoco_nortel_init_one, + .remove = orinoco_nortel_remove_one, + .suspend = orinoco_pci_suspend, + .resume = orinoco_pci_resume, +}; + +static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION + " (Tobias Hoffmann & Christoph Jungegger )"; +MODULE_AUTHOR("Christoph Jungegger "); +MODULE_DESCRIPTION("Driver for wireless LAN cards using the Nortel PCI bridge"); +MODULE_LICENSE("Dual MPL/GPL"); + +static int __init orinoco_nortel_init(void) +{ + printk(KERN_DEBUG "%s\n", version); + return pci_register_driver(&orinoco_nortel_driver); +} + +static void __exit orinoco_nortel_exit(void) +{ + pci_unregister_driver(&orinoco_nortel_driver); +} + +module_init(orinoco_nortel_init); +module_exit(orinoco_nortel_exit); + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_pci.c b/drivers/net/wireless/intersil/orinoco/orinoco_pci.c new file mode 100644 index 000000000000..4938a2208a37 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/orinoco_pci.c @@ -0,0 +1,266 @@ +/* orinoco_pci.c + * + * Driver for Prism 2.5/3 devices that have a direct PCI interface + * (i.e. these are not PCMCIA cards in a PCMCIA-to-PCI bridge). + * The card contains only one PCI region, which contains all the usual + * hermes registers, as well as the COR register. + * + * Current maintainers are: + * Pavel Roskin + * and David Gibson + * + * Some of this code is borrowed from orinoco_plx.c + * Copyright (C) 2001 Daniel Barlow + * Some of this code is "inspired" by linux-wlan-ng-0.1.10, but nothing + * has been copied from it. linux-wlan-ng-0.1.10 is originally : + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * This file originally written by: + * Copyright (C) 2001 Jean Tourrilhes + * And is now maintained by: + * (C) Copyright David Gibson, IBM Corp. 2002-2003. + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + */ + +#define DRIVER_NAME "orinoco_pci" +#define PFX DRIVER_NAME ": " + +#include +#include +#include +#include +#include + +#include "orinoco.h" +#include "orinoco_pci.h" + +/* Offset of the COR register of the PCI card */ +#define HERMES_PCI_COR (0x26) + +/* Bitmask to reset the card */ +#define HERMES_PCI_COR_MASK (0x0080) + +/* Magic timeouts for doing the reset. + * Those times are straight from wlan-ng, and it is claimed that they + * are necessary. Alan will kill me. Take your time and grab a coffee. */ +#define HERMES_PCI_COR_ONT (250) /* ms */ +#define HERMES_PCI_COR_OFFT (500) /* ms */ +#define HERMES_PCI_COR_BUSYT (500) /* ms */ + +/* + * Do a soft reset of the card using the Configuration Option Register + * We need this to get going... + * This is the part of the code that is strongly inspired from wlan-ng + * + * Note : This code is done with irq enabled. This mean that many + * interrupts will occur while we are there. This is why we use the + * jiffies to regulate time instead of a straight mdelay(). Usually we + * need only around 245 iteration of the loop to do 250 ms delay. + * + * Note bis : Don't try to access HERMES_CMD during the reset phase. + * It just won't work ! + */ +static int orinoco_pci_cor_reset(struct orinoco_private *priv) +{ + struct hermes *hw = &priv->hw; + unsigned long timeout; + u16 reg; + + /* Assert the reset until the card notices */ + hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK); + mdelay(HERMES_PCI_COR_ONT); + + /* Give time for the card to recover from this hard effort */ + hermes_write_regn(hw, PCI_COR, 0x0000); + mdelay(HERMES_PCI_COR_OFFT); + + /* The card is ready when it's no longer busy */ + timeout = jiffies + msecs_to_jiffies(HERMES_PCI_COR_BUSYT); + reg = hermes_read_regn(hw, CMD); + while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { + mdelay(1); + reg = hermes_read_regn(hw, CMD); + } + + /* Still busy? */ + if (reg & HERMES_CMD_BUSY) { + printk(KERN_ERR PFX "Busy timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int orinoco_pci_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int err; + struct orinoco_private *priv; + struct orinoco_pci_card *card; + void __iomem *hermes_io; + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR PFX "Cannot enable PCI device\n"); + return err; + } + + err = pci_request_regions(pdev, DRIVER_NAME); + if (err) { + printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); + goto fail_resources; + } + + hermes_io = pci_iomap(pdev, 0, 0); + if (!hermes_io) { + printk(KERN_ERR PFX "Cannot remap chipset registers\n"); + err = -EIO; + goto fail_map_hermes; + } + + /* Allocate network device */ + priv = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_pci_cor_reset, NULL); + if (!priv) { + printk(KERN_ERR PFX "Cannot allocate network device\n"); + err = -ENOMEM; + goto fail_alloc; + } + + card = priv->card; + + hermes_struct_init(&priv->hw, hermes_io, HERMES_32BIT_REGSPACING); + + err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, + DRIVER_NAME, priv); + if (err) { + printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); + err = -EBUSY; + goto fail_irq; + } + + err = orinoco_pci_cor_reset(priv); + if (err) { + printk(KERN_ERR PFX "Initial reset failed\n"); + goto fail; + } + + err = orinoco_init(priv); + if (err) { + printk(KERN_ERR PFX "orinoco_init() failed\n"); + goto fail; + } + + err = orinoco_if_add(priv, 0, 0, NULL); + if (err) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); + goto fail_wiphy; + } + + pci_set_drvdata(pdev, priv); + + return 0; + + fail_wiphy: + wiphy_unregister(priv_to_wiphy(priv)); + fail: + free_irq(pdev->irq, priv); + + fail_irq: + free_orinocodev(priv); + + fail_alloc: + pci_iounmap(pdev, hermes_io); + + fail_map_hermes: + pci_release_regions(pdev); + + fail_resources: + pci_disable_device(pdev); + + return err; +} + +static void orinoco_pci_remove_one(struct pci_dev *pdev) +{ + struct orinoco_private *priv = pci_get_drvdata(pdev); + + orinoco_if_del(priv); + wiphy_unregister(priv_to_wiphy(priv)); + free_irq(pdev->irq, priv); + free_orinocodev(priv); + pci_iounmap(pdev, priv->hw.iobase); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static const struct pci_device_id orinoco_pci_id_table[] = { + /* Intersil Prism 3 */ + {0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,}, + /* Intersil Prism 2.5 */ + {0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,}, + /* Samsung MagicLAN SWL-2210P */ + {0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID,}, + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, orinoco_pci_id_table); + +static struct pci_driver orinoco_pci_driver = { + .name = DRIVER_NAME, + .id_table = orinoco_pci_id_table, + .probe = orinoco_pci_init_one, + .remove = orinoco_pci_remove_one, + .suspend = orinoco_pci_suspend, + .resume = orinoco_pci_resume, +}; + +static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION + " (Pavel Roskin ," + " David Gibson &" + " Jean Tourrilhes )"; +MODULE_AUTHOR("Pavel Roskin &" + " David Gibson "); +MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface"); +MODULE_LICENSE("Dual MPL/GPL"); + +static int __init orinoco_pci_init(void) +{ + printk(KERN_DEBUG "%s\n", version); + return pci_register_driver(&orinoco_pci_driver); +} + +static void __exit orinoco_pci_exit(void) +{ + pci_unregister_driver(&orinoco_pci_driver); +} + +module_init(orinoco_pci_init); +module_exit(orinoco_pci_exit); + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_pci.h b/drivers/net/wireless/intersil/orinoco/orinoco_pci.h new file mode 100644 index 000000000000..43f5b9f5a0b0 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/orinoco_pci.h @@ -0,0 +1,68 @@ +/* orinoco_pci.h + * + * Common code for all Orinoco drivers for PCI devices, including + * both native PCI and PCMCIA-to-PCI bridges. + * + * Copyright (C) 2005, Pavel Roskin. + * See main.c for license. + */ + +#ifndef _ORINOCO_PCI_H +#define _ORINOCO_PCI_H + +#include + +/* Driver specific data */ +struct orinoco_pci_card { + void __iomem *bridge_io; + void __iomem *attr_io; +}; + +#ifdef CONFIG_PM +static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct orinoco_private *priv = pci_get_drvdata(pdev); + + orinoco_down(priv); + free_irq(pdev->irq, priv); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int orinoco_pci_resume(struct pci_dev *pdev) +{ + struct orinoco_private *priv = pci_get_drvdata(pdev); + struct net_device *dev = priv->ndev; + int err; + + pci_set_power_state(pdev, PCI_D0); + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "%s: pci_enable_device failed on resume\n", + dev->name); + return err; + } + pci_restore_state(pdev); + + err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, + dev->name, priv); + if (err) { + printk(KERN_ERR "%s: cannot re-allocate IRQ on resume\n", + dev->name); + pci_disable_device(pdev); + return -EBUSY; + } + + err = orinoco_up(priv); + + return err; +} +#else +#define orinoco_pci_suspend NULL +#define orinoco_pci_resume NULL +#endif + +#endif /* _ORINOCO_PCI_H */ diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_plx.c b/drivers/net/wireless/intersil/orinoco/orinoco_plx.c new file mode 100644 index 000000000000..221352027779 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/orinoco_plx.c @@ -0,0 +1,371 @@ +/* orinoco_plx.c + * + * Driver for Prism II devices which would usually be driven by orinoco_cs, + * but are connected to the PCI bus by a PLX9052. + * + * Current maintainers are: + * Pavel Roskin + * and David Gibson + * + * (C) Copyright David Gibson, IBM Corp. 2001-2003. + * Copyright (C) 2001 Daniel Barlow + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + * + * Here's the general details on how the PLX9052 adapter works: + * + * - Two PCI I/O address spaces, one 0x80 long which contains the + * PLX9052 registers, and one that's 0x40 long mapped to the PCMCIA + * slot I/O address space. + * + * - One PCI memory address space, mapped to the PCMCIA attribute space + * (containing the CIS). + * + * Using the later, you can read through the CIS data to make sure the + * card is compatible with the driver. Keep in mind that the PCMCIA + * spec specifies the CIS as the lower 8 bits of each word read from + * the CIS, so to read the bytes of the CIS, read every other byte + * (0,2,4,...). Passing that test, you need to enable the I/O address + * space on the PCMCIA card via the PCMCIA COR register. This is the + * first byte following the CIS. In my case (which may not have any + * relation to what's on the PRISM2 cards), COR was at offset 0x800 + * within the PCI memory space. Write 0x41 to the COR register to + * enable I/O mode and to select level triggered interrupts. To + * confirm you actually succeeded, read the COR register back and make + * sure it actually got set to 0x41, in case you have an unexpected + * card inserted. + * + * Following that, you can treat the second PCI I/O address space (the + * one that's not 0x80 in length) as the PCMCIA I/O space. + * + * Note that in the Eumitcom's source for their drivers, they register + * the interrupt as edge triggered when registering it with the + * Windows kernel. I don't recall how to register edge triggered on + * Linux (if it can be done at all). But in some experimentation, I + * don't see much operational difference between using either + * interrupt mode. Don't mess with the interrupt mode in the COR + * register though, as the PLX9052 wants level triggers with the way + * the serial EEPROM configures it on the WL11000. + * + * There's some other little quirks related to timing that I bumped + * into, but I don't recall right now. Also, there's two variants of + * the WL11000 I've seen, revision A1 and T2. These seem to differ + * slightly in the timings configured in the wait-state generator in + * the PLX9052. There have also been some comments from Eumitcom that + * cards shouldn't be hot swapped, apparently due to risk of cooking + * the PLX9052. I'm unsure why they believe this, as I can't see + * anything in the design that would really cause a problem, except + * for crashing drivers not written to expect it. And having developed + * drivers for the WL11000, I'd say it's quite tricky to write code + * that will successfully deal with a hot unplug. Very odd things + * happen on the I/O side of things. But anyway, be warned. Despite + * that, I've hot-swapped a number of times during debugging and + * driver development for various reasons (stuck WAIT# line after the + * radio card's firmware locks up). + */ + +#define DRIVER_NAME "orinoco_plx" +#define PFX DRIVER_NAME ": " + +#include +#include +#include +#include +#include +#include + +#include "orinoco.h" +#include "orinoco_pci.h" + +#define COR_OFFSET (0x3e0) /* COR attribute offset of Prism2 PC card */ +#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */ +#define COR_RESET (0x80) /* reset bit in the COR register */ +#define PLX_RESET_TIME (500) /* milliseconds */ + +#define PLX_INTCSR 0x4c /* Interrupt Control & Status Register */ +#define PLX_INTCSR_INTEN (1 << 6) /* Interrupt Enable bit */ + +/* + * Do a soft reset of the card using the Configuration Option Register + */ +static int orinoco_plx_cor_reset(struct orinoco_private *priv) +{ + struct hermes *hw = &priv->hw; + struct orinoco_pci_card *card = priv->card; + unsigned long timeout; + u16 reg; + + iowrite8(COR_VALUE | COR_RESET, card->attr_io + COR_OFFSET); + mdelay(1); + + iowrite8(COR_VALUE, card->attr_io + COR_OFFSET); + mdelay(1); + + /* Just in case, wait more until the card is no longer busy */ + timeout = jiffies + msecs_to_jiffies(PLX_RESET_TIME); + reg = hermes_read_regn(hw, CMD); + while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { + mdelay(1); + reg = hermes_read_regn(hw, CMD); + } + + /* Still busy? */ + if (reg & HERMES_CMD_BUSY) { + printk(KERN_ERR PFX "Busy timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int orinoco_plx_hw_init(struct orinoco_pci_card *card) +{ + int i; + u32 csr_reg; + static const u8 cis_magic[] = { + 0x01, 0x03, 0x00, 0x00, 0xff, 0x17, 0x04, 0x67 + }; + + printk(KERN_DEBUG PFX "CIS: "); + for (i = 0; i < 16; i++) + printk("%02X:", ioread8(card->attr_io + (i << 1))); + printk("\n"); + + /* Verify whether a supported PC card is present */ + /* FIXME: we probably need to be smarted about this */ + for (i = 0; i < sizeof(cis_magic); i++) { + if (cis_magic[i] != ioread8(card->attr_io + (i << 1))) { + printk(KERN_ERR PFX "The CIS value of Prism2 PC " + "card is unexpected\n"); + return -ENODEV; + } + } + + /* bjoern: We need to tell the card to enable interrupts, in + case the serial eprom didn't do this already. See the + PLX9052 data book, p8-1 and 8-24 for reference. */ + csr_reg = ioread32(card->bridge_io + PLX_INTCSR); + if (!(csr_reg & PLX_INTCSR_INTEN)) { + csr_reg |= PLX_INTCSR_INTEN; + iowrite32(csr_reg, card->bridge_io + PLX_INTCSR); + csr_reg = ioread32(card->bridge_io + PLX_INTCSR); + if (!(csr_reg & PLX_INTCSR_INTEN)) { + printk(KERN_ERR PFX "Cannot enable interrupts\n"); + return -EIO; + } + } + + return 0; +} + +static int orinoco_plx_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int err; + struct orinoco_private *priv; + struct orinoco_pci_card *card; + void __iomem *hermes_io, *attr_io, *bridge_io; + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR PFX "Cannot enable PCI device\n"); + return err; + } + + err = pci_request_regions(pdev, DRIVER_NAME); + if (err) { + printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); + goto fail_resources; + } + + bridge_io = pci_iomap(pdev, 1, 0); + if (!bridge_io) { + printk(KERN_ERR PFX "Cannot map bridge registers\n"); + err = -EIO; + goto fail_map_bridge; + } + + attr_io = pci_iomap(pdev, 2, 0); + if (!attr_io) { + printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n"); + err = -EIO; + goto fail_map_attr; + } + + hermes_io = pci_iomap(pdev, 3, 0); + if (!hermes_io) { + printk(KERN_ERR PFX "Cannot map chipset registers\n"); + err = -EIO; + goto fail_map_hermes; + } + + /* Allocate network device */ + priv = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_plx_cor_reset, NULL); + if (!priv) { + printk(KERN_ERR PFX "Cannot allocate network device\n"); + err = -ENOMEM; + goto fail_alloc; + } + + card = priv->card; + card->bridge_io = bridge_io; + card->attr_io = attr_io; + + hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); + + err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, + DRIVER_NAME, priv); + if (err) { + printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); + err = -EBUSY; + goto fail_irq; + } + + err = orinoco_plx_hw_init(card); + if (err) { + printk(KERN_ERR PFX "Hardware initialization failed\n"); + goto fail; + } + + err = orinoco_plx_cor_reset(priv); + if (err) { + printk(KERN_ERR PFX "Initial reset failed\n"); + goto fail; + } + + err = orinoco_init(priv); + if (err) { + printk(KERN_ERR PFX "orinoco_init() failed\n"); + goto fail; + } + + err = orinoco_if_add(priv, 0, 0, NULL); + if (err) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); + goto fail_wiphy; + } + + pci_set_drvdata(pdev, priv); + + return 0; + + fail_wiphy: + wiphy_unregister(priv_to_wiphy(priv)); + fail: + free_irq(pdev->irq, priv); + + fail_irq: + free_orinocodev(priv); + + fail_alloc: + pci_iounmap(pdev, hermes_io); + + fail_map_hermes: + pci_iounmap(pdev, attr_io); + + fail_map_attr: + pci_iounmap(pdev, bridge_io); + + fail_map_bridge: + pci_release_regions(pdev); + + fail_resources: + pci_disable_device(pdev); + + return err; +} + +static void orinoco_plx_remove_one(struct pci_dev *pdev) +{ + struct orinoco_private *priv = pci_get_drvdata(pdev); + struct orinoco_pci_card *card = priv->card; + + orinoco_if_del(priv); + wiphy_unregister(priv_to_wiphy(priv)); + free_irq(pdev->irq, priv); + free_orinocodev(priv); + pci_iounmap(pdev, priv->hw.iobase); + pci_iounmap(pdev, card->attr_io); + pci_iounmap(pdev, card->bridge_io); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static const struct pci_device_id orinoco_plx_id_table[] = { + {0x111a, 0x1023, PCI_ANY_ID, PCI_ANY_ID,}, /* Siemens SpeedStream SS1023 */ + {0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */ + {0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga - does this work? */ + {0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* SMC EZConnect SMC2602W, + Eumitcom PCI WL11000, + Addtron AWA-100 */ + {0x16ab, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* Global Sun Tech GL24110P */ + {0x16ab, 0x1101, PCI_ANY_ID, PCI_ANY_ID,}, /* Reported working, but unknown */ + {0x16ab, 0x1102, PCI_ANY_ID, PCI_ANY_ID,}, /* Linksys WDT11 */ + {0x16ec, 0x3685, PCI_ANY_ID, PCI_ANY_ID,}, /* USR 2415 */ + {0xec80, 0xec00, PCI_ANY_ID, PCI_ANY_ID,}, /* Belkin F5D6000 tested by + Brendan W. McAdams */ + {0x10b7, 0x7770, PCI_ANY_ID, PCI_ANY_ID,}, /* 3Com AirConnect PCI tested by + Damien Persohn */ + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, orinoco_plx_id_table); + +static struct pci_driver orinoco_plx_driver = { + .name = DRIVER_NAME, + .id_table = orinoco_plx_id_table, + .probe = orinoco_plx_init_one, + .remove = orinoco_plx_remove_one, + .suspend = orinoco_pci_suspend, + .resume = orinoco_pci_resume, +}; + +static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION + " (Pavel Roskin ," + " David Gibson ," + " Daniel Barlow )"; +MODULE_AUTHOR("Daniel Barlow "); +MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge"); +MODULE_LICENSE("Dual MPL/GPL"); + +static int __init orinoco_plx_init(void) +{ + printk(KERN_DEBUG "%s\n", version); + return pci_register_driver(&orinoco_plx_driver); +} + +static void __exit orinoco_plx_exit(void) +{ + pci_unregister_driver(&orinoco_plx_driver); +} + +module_init(orinoco_plx_init); +module_exit(orinoco_plx_exit); + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_tmd.c b/drivers/net/wireless/intersil/orinoco/orinoco_tmd.c new file mode 100644 index 000000000000..20ce569b8a43 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/orinoco_tmd.c @@ -0,0 +1,246 @@ +/* orinoco_tmd.c + * + * Driver for Prism II devices which would usually be driven by orinoco_cs, + * but are connected to the PCI bus by a TMD7160. + * + * Copyright (C) 2003 Joerg Dorchain + * based heavily upon orinoco_plx.c Copyright (C) 2001 Daniel Barlow + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + * + * The actual driving is done by main.c, this is just resource + * allocation stuff. + * + * This driver is modeled after the orinoco_plx driver. The main + * difference is that the TMD chip has only IO port ranges and doesn't + * provide access to the PCMCIA attribute space. + * + * Pheecom sells cards with the TMD chip as "ASIC version" + */ + +#define DRIVER_NAME "orinoco_tmd" +#define PFX DRIVER_NAME ": " + +#include +#include +#include +#include +#include +#include + +#include "orinoco.h" +#include "orinoco_pci.h" + +#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */ +#define COR_RESET (0x80) /* reset bit in the COR register */ +#define TMD_RESET_TIME (500) /* milliseconds */ + +/* + * Do a soft reset of the card using the Configuration Option Register + */ +static int orinoco_tmd_cor_reset(struct orinoco_private *priv) +{ + struct hermes *hw = &priv->hw; + struct orinoco_pci_card *card = priv->card; + unsigned long timeout; + u16 reg; + + iowrite8(COR_VALUE | COR_RESET, card->bridge_io); + mdelay(1); + + iowrite8(COR_VALUE, card->bridge_io); + mdelay(1); + + /* Just in case, wait more until the card is no longer busy */ + timeout = jiffies + msecs_to_jiffies(TMD_RESET_TIME); + reg = hermes_read_regn(hw, CMD); + while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { + mdelay(1); + reg = hermes_read_regn(hw, CMD); + } + + /* Still busy? */ + if (reg & HERMES_CMD_BUSY) { + printk(KERN_ERR PFX "Busy timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + + +static int orinoco_tmd_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int err; + struct orinoco_private *priv; + struct orinoco_pci_card *card; + void __iomem *hermes_io, *bridge_io; + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR PFX "Cannot enable PCI device\n"); + return err; + } + + err = pci_request_regions(pdev, DRIVER_NAME); + if (err) { + printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); + goto fail_resources; + } + + bridge_io = pci_iomap(pdev, 1, 0); + if (!bridge_io) { + printk(KERN_ERR PFX "Cannot map bridge registers\n"); + err = -EIO; + goto fail_map_bridge; + } + + hermes_io = pci_iomap(pdev, 2, 0); + if (!hermes_io) { + printk(KERN_ERR PFX "Cannot map chipset registers\n"); + err = -EIO; + goto fail_map_hermes; + } + + /* Allocate network device */ + priv = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_tmd_cor_reset, NULL); + if (!priv) { + printk(KERN_ERR PFX "Cannot allocate network device\n"); + err = -ENOMEM; + goto fail_alloc; + } + + card = priv->card; + card->bridge_io = bridge_io; + + hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); + + err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, + DRIVER_NAME, priv); + if (err) { + printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); + err = -EBUSY; + goto fail_irq; + } + + err = orinoco_tmd_cor_reset(priv); + if (err) { + printk(KERN_ERR PFX "Initial reset failed\n"); + goto fail; + } + + err = orinoco_init(priv); + if (err) { + printk(KERN_ERR PFX "orinoco_init() failed\n"); + goto fail; + } + + err = orinoco_if_add(priv, 0, 0, NULL); + if (err) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); + goto fail; + } + + pci_set_drvdata(pdev, priv); + + return 0; + + fail: + free_irq(pdev->irq, priv); + + fail_irq: + free_orinocodev(priv); + + fail_alloc: + pci_iounmap(pdev, hermes_io); + + fail_map_hermes: + pci_iounmap(pdev, bridge_io); + + fail_map_bridge: + pci_release_regions(pdev); + + fail_resources: + pci_disable_device(pdev); + + return err; +} + +static void orinoco_tmd_remove_one(struct pci_dev *pdev) +{ + struct orinoco_private *priv = pci_get_drvdata(pdev); + struct orinoco_pci_card *card = priv->card; + + orinoco_if_del(priv); + free_irq(pdev->irq, priv); + free_orinocodev(priv); + pci_iounmap(pdev, priv->hw.iobase); + pci_iounmap(pdev, card->bridge_io); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static const struct pci_device_id orinoco_tmd_id_table[] = { + {0x15e8, 0x0131, PCI_ANY_ID, PCI_ANY_ID,}, /* NDC and OEMs, e.g. pheecom */ + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, orinoco_tmd_id_table); + +static struct pci_driver orinoco_tmd_driver = { + .name = DRIVER_NAME, + .id_table = orinoco_tmd_id_table, + .probe = orinoco_tmd_init_one, + .remove = orinoco_tmd_remove_one, + .suspend = orinoco_pci_suspend, + .resume = orinoco_pci_resume, +}; + +static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION + " (Joerg Dorchain )"; +MODULE_AUTHOR("Joerg Dorchain "); +MODULE_DESCRIPTION("Driver for wireless LAN cards using the TMD7160 PCI bridge"); +MODULE_LICENSE("Dual MPL/GPL"); + +static int __init orinoco_tmd_init(void) +{ + printk(KERN_DEBUG "%s\n", version); + return pci_register_driver(&orinoco_tmd_driver); +} + +static void __exit orinoco_tmd_exit(void) +{ + pci_unregister_driver(&orinoco_tmd_driver); +} + +module_init(orinoco_tmd_init); +module_exit(orinoco_tmd_exit); + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c new file mode 100644 index 000000000000..f2cd513d54b2 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c @@ -0,0 +1,1748 @@ +/* + * USB Orinoco driver + * + * Copyright (c) 2003 Manuel Estrada Sainz + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + * + * Queueing code based on linux-wlan-ng 0.2.1-pre5 + * + * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. + * + * The license is the same as above. + * + * Initialy based on USB Skeleton driver - 0.7 + * + * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * NOTE: The original USB Skeleton driver is GPL, but all that code is + * gone so MPL/GPL applies. + */ + +#define DRIVER_NAME "orinoco_usb" +#define PFX DRIVER_NAME ": " + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "mic.h" +#include "orinoco.h" + +#ifndef URB_ASYNC_UNLINK +#define URB_ASYNC_UNLINK 0 +#endif + +/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ +static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; +#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) + +struct header_struct { + /* 802.3 */ + u8 dest[ETH_ALEN]; + u8 src[ETH_ALEN]; + __be16 len; + /* 802.2 */ + u8 dsap; + u8 ssap; + u8 ctrl; + /* SNAP */ + u8 oui[3]; + __be16 ethertype; +} __packed; + +struct ez_usb_fw { + u16 size; + const u8 *code; +}; + +static struct ez_usb_fw firmware = { + .size = 0, + .code = NULL, +}; + +/* Debugging macros */ +#undef err +#define err(format, arg...) \ + do { printk(KERN_ERR PFX format "\n", ## arg); } while (0) + +MODULE_FIRMWARE("orinoco_ezusb_fw"); + +/* + * Under some conditions, the card gets stuck and stops paying attention + * to the world (i.e. data communication stalls) until we do something to + * it. Sending an INQ_TALLIES command seems to be enough and should be + * harmless otherwise. This behaviour has been observed when using the + * driver on a systemimager client during installation. In the past a + * timer was used to send INQ_TALLIES commands when there was no other + * activity, but it was troublesome and was removed. + */ + +#define USB_COMPAQ_VENDOR_ID 0x049f /* Compaq Computer Corp. */ +#define USB_COMPAQ_WL215_ID 0x001f /* Compaq WL215 USB Adapter */ +#define USB_COMPAQ_W200_ID 0x0076 /* Compaq W200 USB Adapter */ +#define USB_HP_WL215_ID 0x0082 /* Compaq WL215 USB Adapter */ + +#define USB_MELCO_VENDOR_ID 0x0411 +#define USB_BUFFALO_L11_ID 0x0006 /* BUFFALO WLI-USB-L11 */ +#define USB_BUFFALO_L11G_WR_ID 0x000B /* BUFFALO WLI-USB-L11G-WR */ +#define USB_BUFFALO_L11G_ID 0x000D /* BUFFALO WLI-USB-L11G */ + +#define USB_LUCENT_VENDOR_ID 0x047E /* Lucent Technologies */ +#define USB_LUCENT_ORINOCO_ID 0x0300 /* Lucent/Agere Orinoco USB Client */ + +#define USB_AVAYA8_VENDOR_ID 0x0D98 +#define USB_AVAYAE_VENDOR_ID 0x0D9E +#define USB_AVAYA_WIRELESS_ID 0x0300 /* Avaya Wireless USB Card */ + +#define USB_AGERE_VENDOR_ID 0x0D4E /* Agere Systems */ +#define USB_AGERE_MODEL0801_ID 0x1000 /* Wireless USB Card Model 0801 */ +#define USB_AGERE_MODEL0802_ID 0x1001 /* Wireless USB Card Model 0802 */ +#define USB_AGERE_REBRANDED_ID 0x047A /* WLAN USB Card */ + +#define USB_ELSA_VENDOR_ID 0x05CC +#define USB_ELSA_AIRLANCER_ID 0x3100 /* ELSA AirLancer USB-11 */ + +#define USB_LEGEND_VENDOR_ID 0x0E7C +#define USB_LEGEND_JOYNET_ID 0x0300 /* Joynet WLAN USB Card */ + +#define USB_SAMSUNG_VENDOR_ID 0x04E8 +#define USB_SAMSUNG_SEW2001U1_ID 0x5002 /* Samsung SEW-2001u Card */ +#define USB_SAMSUNG_SEW2001U2_ID 0x5B11 /* Samsung SEW-2001u Card */ +#define USB_SAMSUNG_SEW2003U_ID 0x7011 /* Samsung SEW-2003U Card */ + +#define USB_IGATE_VENDOR_ID 0x0681 +#define USB_IGATE_IGATE_11M_ID 0x0012 /* I-GATE 11M USB Card */ + +#define USB_FUJITSU_VENDOR_ID 0x0BF8 +#define USB_FUJITSU_E1100_ID 0x1002 /* connect2AIR WLAN E-1100 USB */ + +#define USB_2WIRE_VENDOR_ID 0x1630 +#define USB_2WIRE_WIRELESS_ID 0xff81 /* 2Wire Wireless USB adapter */ + + +#define EZUSB_REQUEST_FW_TRANS 0xA0 +#define EZUSB_REQUEST_TRIGER 0xAA +#define EZUSB_REQUEST_TRIG_AC 0xAC +#define EZUSB_CPUCS_REG 0x7F92 + +#define EZUSB_RID_TX 0x0700 +#define EZUSB_RID_RX 0x0701 +#define EZUSB_RID_INIT1 0x0702 +#define EZUSB_RID_ACK 0x0710 +#define EZUSB_RID_READ_PDA 0x0800 +#define EZUSB_RID_PROG_INIT 0x0852 +#define EZUSB_RID_PROG_SET_ADDR 0x0853 +#define EZUSB_RID_PROG_BYTES 0x0854 +#define EZUSB_RID_PROG_END 0x0855 +#define EZUSB_RID_DOCMD 0x0860 + +/* Recognize info frames */ +#define EZUSB_IS_INFO(id) ((id >= 0xF000) && (id <= 0xF2FF)) + +#define EZUSB_MAGIC 0x0210 + +#define EZUSB_FRAME_DATA 1 +#define EZUSB_FRAME_CONTROL 2 + +#define DEF_TIMEOUT (3 * HZ) + +#define BULK_BUF_SIZE 2048 + +#define MAX_DL_SIZE (BULK_BUF_SIZE - sizeof(struct ezusb_packet)) + +#define FW_BUF_SIZE 64 +#define FW_VAR_OFFSET_PTR 0x359 +#define FW_VAR_VALUE 0 +#define FW_HOLE_START 0x100 +#define FW_HOLE_END 0x300 + +struct ezusb_packet { + __le16 magic; /* 0x0210 */ + u8 req_reply_count; + u8 ans_reply_count; + __le16 frame_type; /* 0x01 for data frames, 0x02 otherwise */ + __le16 size; /* transport size */ + __le16 crc; /* CRC up to here */ + __le16 hermes_len; + __le16 hermes_rid; + u8 data[0]; +} __packed; + +/* Table of devices that work or may work with this driver */ +static struct usb_device_id ezusb_table[] = { + {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_WL215_ID)}, + {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_HP_WL215_ID)}, + {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_W200_ID)}, + {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11_ID)}, + {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_WR_ID)}, + {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_ID)}, + {USB_DEVICE(USB_LUCENT_VENDOR_ID, USB_LUCENT_ORINOCO_ID)}, + {USB_DEVICE(USB_AVAYA8_VENDOR_ID, USB_AVAYA_WIRELESS_ID)}, + {USB_DEVICE(USB_AVAYAE_VENDOR_ID, USB_AVAYA_WIRELESS_ID)}, + {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0801_ID)}, + {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0802_ID)}, + {USB_DEVICE(USB_ELSA_VENDOR_ID, USB_ELSA_AIRLANCER_ID)}, + {USB_DEVICE(USB_LEGEND_VENDOR_ID, USB_LEGEND_JOYNET_ID)}, + {USB_DEVICE_VER(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U1_ID, + 0, 0)}, + {USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U2_ID)}, + {USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2003U_ID)}, + {USB_DEVICE(USB_IGATE_VENDOR_ID, USB_IGATE_IGATE_11M_ID)}, + {USB_DEVICE(USB_FUJITSU_VENDOR_ID, USB_FUJITSU_E1100_ID)}, + {USB_DEVICE(USB_2WIRE_VENDOR_ID, USB_2WIRE_WIRELESS_ID)}, + {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_REBRANDED_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ezusb_table); + +/* Structure to hold all of our device specific stuff */ +struct ezusb_priv { + struct usb_device *udev; + struct net_device *dev; + struct mutex mtx; + spinlock_t req_lock; + struct list_head req_pending; + struct list_head req_active; + spinlock_t reply_count_lock; + u16 hermes_reg_fake[0x40]; + u8 *bap_buf; + struct urb *read_urb; + int read_pipe; + int write_pipe; + u8 reply_count; +}; + +enum ezusb_state { + EZUSB_CTX_START, + EZUSB_CTX_QUEUED, + EZUSB_CTX_REQ_SUBMITTED, + EZUSB_CTX_REQ_COMPLETE, + EZUSB_CTX_RESP_RECEIVED, + EZUSB_CTX_REQ_TIMEOUT, + EZUSB_CTX_REQ_FAILED, + EZUSB_CTX_RESP_TIMEOUT, + EZUSB_CTX_REQSUBMIT_FAIL, + EZUSB_CTX_COMPLETE, +}; + +struct request_context { + struct list_head list; + atomic_t refcount; + struct completion done; /* Signals that CTX is dead */ + int killed; + struct urb *outurb; /* OUT for req pkt */ + struct ezusb_priv *upriv; + struct ezusb_packet *buf; + int buf_length; + struct timer_list timer; /* Timeout handling */ + enum ezusb_state state; /* Current state */ + /* the RID that we will wait for */ + u16 out_rid; + u16 in_rid; +}; + + +/* Forward declarations */ +static void ezusb_ctx_complete(struct request_context *ctx); +static void ezusb_req_queue_run(struct ezusb_priv *upriv); +static void ezusb_bulk_in_callback(struct urb *urb); + +static inline u8 ezusb_reply_inc(u8 count) +{ + if (count < 0x7F) + return count + 1; + else + return 1; +} + +static void ezusb_request_context_put(struct request_context *ctx) +{ + if (!atomic_dec_and_test(&ctx->refcount)) + return; + + WARN_ON(!ctx->done.done); + BUG_ON(ctx->outurb->status == -EINPROGRESS); + BUG_ON(timer_pending(&ctx->timer)); + usb_free_urb(ctx->outurb); + kfree(ctx->buf); + kfree(ctx); +} + +static inline void ezusb_mod_timer(struct ezusb_priv *upriv, + struct timer_list *timer, + unsigned long expire) +{ + if (!upriv->udev) + return; + mod_timer(timer, expire); +} + +static void ezusb_request_timerfn(u_long _ctx) +{ + struct request_context *ctx = (void *) _ctx; + + ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; + if (usb_unlink_urb(ctx->outurb) == -EINPROGRESS) { + ctx->state = EZUSB_CTX_REQ_TIMEOUT; + } else { + ctx->state = EZUSB_CTX_RESP_TIMEOUT; + dev_dbg(&ctx->outurb->dev->dev, "couldn't unlink\n"); + atomic_inc(&ctx->refcount); + ctx->killed = 1; + ezusb_ctx_complete(ctx); + ezusb_request_context_put(ctx); + } +}; + +static struct request_context *ezusb_alloc_ctx(struct ezusb_priv *upriv, + u16 out_rid, u16 in_rid) +{ + struct request_context *ctx; + + ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return NULL; + + ctx->buf = kmalloc(BULK_BUF_SIZE, GFP_ATOMIC); + if (!ctx->buf) { + kfree(ctx); + return NULL; + } + ctx->outurb = usb_alloc_urb(0, GFP_ATOMIC); + if (!ctx->outurb) { + kfree(ctx->buf); + kfree(ctx); + return NULL; + } + + ctx->upriv = upriv; + ctx->state = EZUSB_CTX_START; + ctx->out_rid = out_rid; + ctx->in_rid = in_rid; + + atomic_set(&ctx->refcount, 1); + init_completion(&ctx->done); + + setup_timer(&ctx->timer, ezusb_request_timerfn, (u_long)ctx); + return ctx; +} + + +/* Hopefully the real complete_all will soon be exported, in the mean + * while this should work. */ +static inline void ezusb_complete_all(struct completion *comp) +{ + complete(comp); + complete(comp); + complete(comp); + complete(comp); +} + +static void ezusb_ctx_complete(struct request_context *ctx) +{ + struct ezusb_priv *upriv = ctx->upriv; + unsigned long flags; + + spin_lock_irqsave(&upriv->req_lock, flags); + + list_del_init(&ctx->list); + if (upriv->udev) { + spin_unlock_irqrestore(&upriv->req_lock, flags); + ezusb_req_queue_run(upriv); + spin_lock_irqsave(&upriv->req_lock, flags); + } + + switch (ctx->state) { + case EZUSB_CTX_COMPLETE: + case EZUSB_CTX_REQSUBMIT_FAIL: + case EZUSB_CTX_REQ_FAILED: + case EZUSB_CTX_REQ_TIMEOUT: + case EZUSB_CTX_RESP_TIMEOUT: + spin_unlock_irqrestore(&upriv->req_lock, flags); + + if ((ctx->out_rid == EZUSB_RID_TX) && upriv->dev) { + struct net_device *dev = upriv->dev; + struct orinoco_private *priv = ndev_priv(dev); + struct net_device_stats *stats = &priv->stats; + + if (ctx->state != EZUSB_CTX_COMPLETE) + stats->tx_errors++; + else + stats->tx_packets++; + + netif_wake_queue(dev); + } + ezusb_complete_all(&ctx->done); + ezusb_request_context_put(ctx); + break; + + default: + spin_unlock_irqrestore(&upriv->req_lock, flags); + if (!upriv->udev) { + /* This is normal, as all request contexts get flushed + * when the device is disconnected */ + err("Called, CTX not terminating, but device gone"); + ezusb_complete_all(&ctx->done); + ezusb_request_context_put(ctx); + break; + } + + err("Called, CTX not in terminating state."); + /* Things are really bad if this happens. Just leak + * the CTX because it may still be linked to the + * queue or the OUT urb may still be active. + * Just leaking at least prevents an Oops or Panic. + */ + break; + } +} + +/** + * ezusb_req_queue_run: + * Description: + * Note: Only one active CTX at any one time, because there's no + * other (reliable) way to match the response URB to the correct + * CTX. + **/ +static void ezusb_req_queue_run(struct ezusb_priv *upriv) +{ + unsigned long flags; + struct request_context *ctx; + int result; + + spin_lock_irqsave(&upriv->req_lock, flags); + + if (!list_empty(&upriv->req_active)) + goto unlock; + + if (list_empty(&upriv->req_pending)) + goto unlock; + + ctx = + list_entry(upriv->req_pending.next, struct request_context, + list); + + if (!ctx->upriv->udev) + goto unlock; + + /* We need to split this off to avoid a race condition */ + list_move_tail(&ctx->list, &upriv->req_active); + + if (ctx->state == EZUSB_CTX_QUEUED) { + atomic_inc(&ctx->refcount); + result = usb_submit_urb(ctx->outurb, GFP_ATOMIC); + if (result) { + ctx->state = EZUSB_CTX_REQSUBMIT_FAIL; + + spin_unlock_irqrestore(&upriv->req_lock, flags); + + err("Fatal, failed to submit command urb." + " error=%d\n", result); + + ezusb_ctx_complete(ctx); + ezusb_request_context_put(ctx); + goto done; + } + + ctx->state = EZUSB_CTX_REQ_SUBMITTED; + ezusb_mod_timer(ctx->upriv, &ctx->timer, + jiffies + DEF_TIMEOUT); + } + + unlock: + spin_unlock_irqrestore(&upriv->req_lock, flags); + + done: + return; +} + +static void ezusb_req_enqueue_run(struct ezusb_priv *upriv, + struct request_context *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&upriv->req_lock, flags); + + if (!ctx->upriv->udev) { + spin_unlock_irqrestore(&upriv->req_lock, flags); + goto done; + } + atomic_inc(&ctx->refcount); + list_add_tail(&ctx->list, &upriv->req_pending); + spin_unlock_irqrestore(&upriv->req_lock, flags); + + ctx->state = EZUSB_CTX_QUEUED; + ezusb_req_queue_run(upriv); + + done: + return; +} + +static void ezusb_request_out_callback(struct urb *urb) +{ + unsigned long flags; + enum ezusb_state state; + struct request_context *ctx = urb->context; + struct ezusb_priv *upriv = ctx->upriv; + + spin_lock_irqsave(&upriv->req_lock, flags); + + del_timer(&ctx->timer); + + if (ctx->killed) { + spin_unlock_irqrestore(&upriv->req_lock, flags); + pr_warn("interrupt called with dead ctx\n"); + goto out; + } + + state = ctx->state; + + if (urb->status == 0) { + switch (state) { + case EZUSB_CTX_REQ_SUBMITTED: + if (ctx->in_rid) { + ctx->state = EZUSB_CTX_REQ_COMPLETE; + /* reply URB still pending */ + ezusb_mod_timer(upriv, &ctx->timer, + jiffies + DEF_TIMEOUT); + spin_unlock_irqrestore(&upriv->req_lock, + flags); + break; + } + /* fall through */ + case EZUSB_CTX_RESP_RECEIVED: + /* IN already received before this OUT-ACK */ + ctx->state = EZUSB_CTX_COMPLETE; + spin_unlock_irqrestore(&upriv->req_lock, flags); + ezusb_ctx_complete(ctx); + break; + + default: + spin_unlock_irqrestore(&upriv->req_lock, flags); + err("Unexpected state(0x%x, %d) in OUT URB", + state, urb->status); + break; + } + } else { + /* If someone cancels the OUT URB then its status + * should be either -ECONNRESET or -ENOENT. + */ + switch (state) { + case EZUSB_CTX_REQ_SUBMITTED: + case EZUSB_CTX_RESP_RECEIVED: + ctx->state = EZUSB_CTX_REQ_FAILED; + /* fall through */ + + case EZUSB_CTX_REQ_FAILED: + case EZUSB_CTX_REQ_TIMEOUT: + spin_unlock_irqrestore(&upriv->req_lock, flags); + + ezusb_ctx_complete(ctx); + break; + + default: + spin_unlock_irqrestore(&upriv->req_lock, flags); + + err("Unexpected state(0x%x, %d) in OUT URB", + state, urb->status); + break; + } + } + out: + ezusb_request_context_put(ctx); +} + +static void ezusb_request_in_callback(struct ezusb_priv *upriv, + struct urb *urb) +{ + struct ezusb_packet *ans = urb->transfer_buffer; + struct request_context *ctx = NULL; + enum ezusb_state state; + unsigned long flags; + + /* Find the CTX on the active queue that requested this URB */ + spin_lock_irqsave(&upriv->req_lock, flags); + if (upriv->udev) { + struct list_head *item; + + list_for_each(item, &upriv->req_active) { + struct request_context *c; + int reply_count; + + c = list_entry(item, struct request_context, list); + reply_count = + ezusb_reply_inc(c->buf->req_reply_count); + if ((ans->ans_reply_count == reply_count) + && (le16_to_cpu(ans->hermes_rid) == c->in_rid)) { + ctx = c; + break; + } + netdev_dbg(upriv->dev, "Skipped (0x%x/0x%x) (%d/%d)\n", + le16_to_cpu(ans->hermes_rid), c->in_rid, + ans->ans_reply_count, reply_count); + } + } + + if (ctx == NULL) { + spin_unlock_irqrestore(&upriv->req_lock, flags); + err("%s: got unexpected RID: 0x%04X", __func__, + le16_to_cpu(ans->hermes_rid)); + ezusb_req_queue_run(upriv); + return; + } + + /* The data we want is in the in buffer, exchange */ + urb->transfer_buffer = ctx->buf; + ctx->buf = (void *) ans; + ctx->buf_length = urb->actual_length; + + state = ctx->state; + switch (state) { + case EZUSB_CTX_REQ_SUBMITTED: + /* We have received our response URB before + * our request has been acknowledged. Do NOT + * destroy our CTX yet, because our OUT URB + * is still alive ... + */ + ctx->state = EZUSB_CTX_RESP_RECEIVED; + spin_unlock_irqrestore(&upriv->req_lock, flags); + + /* Let the machine continue running. */ + break; + + case EZUSB_CTX_REQ_COMPLETE: + /* This is the usual path: our request + * has already been acknowledged, and + * we have now received the reply. + */ + ctx->state = EZUSB_CTX_COMPLETE; + + /* Stop the intimer */ + del_timer(&ctx->timer); + spin_unlock_irqrestore(&upriv->req_lock, flags); + + /* Call the completion handler */ + ezusb_ctx_complete(ctx); + break; + + default: + spin_unlock_irqrestore(&upriv->req_lock, flags); + + pr_warn("Matched IN URB, unexpected context state(0x%x)\n", + state); + /* Throw this CTX away and try submitting another */ + del_timer(&ctx->timer); + ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; + usb_unlink_urb(ctx->outurb); + ezusb_req_queue_run(upriv); + break; + } /* switch */ +} + + +static void ezusb_req_ctx_wait(struct ezusb_priv *upriv, + struct request_context *ctx) +{ + switch (ctx->state) { + case EZUSB_CTX_QUEUED: + case EZUSB_CTX_REQ_SUBMITTED: + case EZUSB_CTX_REQ_COMPLETE: + case EZUSB_CTX_RESP_RECEIVED: + if (in_softirq()) { + /* If we get called from a timer, timeout timers don't + * get the chance to run themselves. So we make sure + * that we don't sleep for ever */ + int msecs = DEF_TIMEOUT * (1000 / HZ); + while (!ctx->done.done && msecs--) + udelay(1000); + } else { + wait_event_interruptible(ctx->done.wait, + ctx->done.done); + } + break; + default: + /* Done or failed - nothing to wait for */ + break; + } +} + +static inline u16 build_crc(struct ezusb_packet *data) +{ + u16 crc = 0; + u8 *bytes = (u8 *)data; + int i; + + for (i = 0; i < 8; i++) + crc = (crc << 1) + bytes[i]; + + return crc; +} + +/** + * ezusb_fill_req: + * + * if data == NULL and length > 0 the data is assumed to be already in + * the target buffer and only the header is filled. + * + */ +static int ezusb_fill_req(struct ezusb_packet *req, u16 length, u16 rid, + const void *data, u16 frame_type, u8 reply_count) +{ + int total_size = sizeof(*req) + length; + + BUG_ON(total_size > BULK_BUF_SIZE); + + req->magic = cpu_to_le16(EZUSB_MAGIC); + req->req_reply_count = reply_count; + req->ans_reply_count = 0; + req->frame_type = cpu_to_le16(frame_type); + req->size = cpu_to_le16(length + 4); + req->crc = cpu_to_le16(build_crc(req)); + req->hermes_len = cpu_to_le16(HERMES_BYTES_TO_RECLEN(length)); + req->hermes_rid = cpu_to_le16(rid); + if (data) + memcpy(req->data, data, length); + return total_size; +} + +static int ezusb_submit_in_urb(struct ezusb_priv *upriv) +{ + int retval = 0; + void *cur_buf = upriv->read_urb->transfer_buffer; + + if (upriv->read_urb->status == -EINPROGRESS) { + netdev_dbg(upriv->dev, "urb busy, not resubmiting\n"); + retval = -EBUSY; + goto exit; + } + usb_fill_bulk_urb(upriv->read_urb, upriv->udev, upriv->read_pipe, + cur_buf, BULK_BUF_SIZE, + ezusb_bulk_in_callback, upriv); + upriv->read_urb->transfer_flags = 0; + retval = usb_submit_urb(upriv->read_urb, GFP_ATOMIC); + if (retval) + err("%s submit failed %d", __func__, retval); + + exit: + return retval; +} + +static inline int ezusb_8051_cpucs(struct ezusb_priv *upriv, int reset) +{ + u8 res_val = reset; /* avoid argument promotion */ + + if (!upriv->udev) { + err("%s: !upriv->udev", __func__); + return -EFAULT; + } + return usb_control_msg(upriv->udev, + usb_sndctrlpipe(upriv->udev, 0), + EZUSB_REQUEST_FW_TRANS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | + USB_DIR_OUT, EZUSB_CPUCS_REG, 0, &res_val, + sizeof(res_val), DEF_TIMEOUT); +} + +static int ezusb_firmware_download(struct ezusb_priv *upriv, + struct ez_usb_fw *fw) +{ + u8 *fw_buffer; + int retval, addr; + int variant_offset; + + fw_buffer = kmalloc(FW_BUF_SIZE, GFP_KERNEL); + if (!fw_buffer) { + printk(KERN_ERR PFX "Out of memory for firmware buffer.\n"); + return -ENOMEM; + } + /* + * This byte is 1 and should be replaced with 0. The offset is + * 0x10AD in version 0.0.6. The byte in question should follow + * the end of the code pointed to by the jump in the beginning + * of the firmware. Also, it is read by code located at 0x358. + */ + variant_offset = be16_to_cpup((__be16 *) &fw->code[FW_VAR_OFFSET_PTR]); + if (variant_offset >= fw->size) { + printk(KERN_ERR PFX "Invalid firmware variant offset: " + "0x%04x\n", variant_offset); + retval = -EINVAL; + goto fail; + } + + retval = ezusb_8051_cpucs(upriv, 1); + if (retval < 0) + goto fail; + for (addr = 0; addr < fw->size; addr += FW_BUF_SIZE) { + /* 0x100-0x300 should be left alone, it contains card + * specific data, like USB enumeration information */ + if ((addr >= FW_HOLE_START) && (addr < FW_HOLE_END)) + continue; + + memcpy(fw_buffer, &fw->code[addr], FW_BUF_SIZE); + if (variant_offset >= addr && + variant_offset < addr + FW_BUF_SIZE) { + netdev_dbg(upriv->dev, + "Patching card_variant byte at 0x%04X\n", + variant_offset); + fw_buffer[variant_offset - addr] = FW_VAR_VALUE; + } + retval = usb_control_msg(upriv->udev, + usb_sndctrlpipe(upriv->udev, 0), + EZUSB_REQUEST_FW_TRANS, + USB_TYPE_VENDOR | USB_RECIP_DEVICE + | USB_DIR_OUT, + addr, 0x0, + fw_buffer, FW_BUF_SIZE, + DEF_TIMEOUT); + + if (retval < 0) + goto fail; + } + retval = ezusb_8051_cpucs(upriv, 0); + if (retval < 0) + goto fail; + + goto exit; + fail: + printk(KERN_ERR PFX "Firmware download failed, error %d\n", + retval); + exit: + kfree(fw_buffer); + return retval; +} + +static int ezusb_access_ltv(struct ezusb_priv *upriv, + struct request_context *ctx, + u16 length, const void *data, u16 frame_type, + void *ans_buff, unsigned ans_size, u16 *ans_length) +{ + int req_size; + int retval = 0; + enum ezusb_state state; + + BUG_ON(in_irq()); + + if (!upriv->udev) { + retval = -ENODEV; + goto exit; + } + + if (upriv->read_urb->status != -EINPROGRESS) + err("%s: in urb not pending", __func__); + + /* protect upriv->reply_count, guarantee sequential numbers */ + spin_lock_bh(&upriv->reply_count_lock); + req_size = ezusb_fill_req(ctx->buf, length, ctx->out_rid, data, + frame_type, upriv->reply_count); + usb_fill_bulk_urb(ctx->outurb, upriv->udev, upriv->write_pipe, + ctx->buf, req_size, + ezusb_request_out_callback, ctx); + + if (ctx->in_rid) + upriv->reply_count = ezusb_reply_inc(upriv->reply_count); + + ezusb_req_enqueue_run(upriv, ctx); + + spin_unlock_bh(&upriv->reply_count_lock); + + if (ctx->in_rid) + ezusb_req_ctx_wait(upriv, ctx); + + state = ctx->state; + switch (state) { + case EZUSB_CTX_COMPLETE: + retval = ctx->outurb->status; + break; + + case EZUSB_CTX_QUEUED: + case EZUSB_CTX_REQ_SUBMITTED: + if (!ctx->in_rid) + break; + default: + err("%s: Unexpected context state %d", __func__, + state); + /* fall though */ + case EZUSB_CTX_REQ_TIMEOUT: + case EZUSB_CTX_REQ_FAILED: + case EZUSB_CTX_RESP_TIMEOUT: + case EZUSB_CTX_REQSUBMIT_FAIL: + printk(KERN_ERR PFX "Access failed, resetting (state %d," + " reply_count %d)\n", state, upriv->reply_count); + upriv->reply_count = 0; + if (state == EZUSB_CTX_REQ_TIMEOUT + || state == EZUSB_CTX_RESP_TIMEOUT) { + printk(KERN_ERR PFX "ctx timed out\n"); + retval = -ETIMEDOUT; + } else { + printk(KERN_ERR PFX "ctx failed\n"); + retval = -EFAULT; + } + goto exit; + } + if (ctx->in_rid) { + struct ezusb_packet *ans = ctx->buf; + unsigned exp_len; + + if (ans->hermes_len != 0) + exp_len = le16_to_cpu(ans->hermes_len) * 2 + 12; + else + exp_len = 14; + + if (exp_len != ctx->buf_length) { + err("%s: length mismatch for RID 0x%04x: " + "expected %d, got %d", __func__, + ctx->in_rid, exp_len, ctx->buf_length); + retval = -EIO; + goto exit; + } + + if (ans_buff) + memcpy(ans_buff, ans->data, min(exp_len, ans_size)); + if (ans_length) + *ans_length = le16_to_cpu(ans->hermes_len); + } + exit: + ezusb_request_context_put(ctx); + return retval; +} + +static int ezusb_write_ltv(struct hermes *hw, int bap, u16 rid, + u16 length, const void *data) +{ + struct ezusb_priv *upriv = hw->priv; + u16 frame_type; + struct request_context *ctx; + + if (length == 0) + return -EINVAL; + + length = HERMES_RECLEN_TO_BYTES(length); + + /* On memory mapped devices HERMES_RID_CNFGROUPADDRESSES can be + * set to be empty, but the USB bridge doesn't like it */ + if (length == 0) + return 0; + + ctx = ezusb_alloc_ctx(upriv, rid, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + if (rid == EZUSB_RID_TX) + frame_type = EZUSB_FRAME_DATA; + else + frame_type = EZUSB_FRAME_CONTROL; + + return ezusb_access_ltv(upriv, ctx, length, data, frame_type, + NULL, 0, NULL); +} + +static int ezusb_read_ltv(struct hermes *hw, int bap, u16 rid, + unsigned bufsize, u16 *length, void *buf) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + + if (bufsize % 2) + return -EINVAL; + + ctx = ezusb_alloc_ctx(upriv, rid, rid); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, 0, NULL, EZUSB_FRAME_CONTROL, + buf, bufsize, length); +} + +static int ezusb_doicmd_wait(struct hermes *hw, u16 cmd, u16 parm0, u16 parm1, + u16 parm2, struct hermes_response *resp) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + + __le16 data[4] = { + cpu_to_le16(cmd), + cpu_to_le16(parm0), + cpu_to_le16(parm1), + cpu_to_le16(parm2), + }; + netdev_dbg(upriv->dev, + "0x%04X, parm0 0x%04X, parm1 0x%04X, parm2 0x%04X\n", cmd, + parm0, parm1, parm2); + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); +} + +static int ezusb_docmd_wait(struct hermes *hw, u16 cmd, u16 parm0, + struct hermes_response *resp) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + + __le16 data[4] = { + cpu_to_le16(cmd), + cpu_to_le16(parm0), + 0, + 0, + }; + netdev_dbg(upriv->dev, "0x%04X, parm0 0x%04X\n", cmd, parm0); + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); +} + +static int ezusb_bap_pread(struct hermes *hw, int bap, + void *buf, int len, u16 id, u16 offset) +{ + struct ezusb_priv *upriv = hw->priv; + struct ezusb_packet *ans = (void *) upriv->read_urb->transfer_buffer; + int actual_length = upriv->read_urb->actual_length; + + if (id == EZUSB_RID_RX) { + if ((sizeof(*ans) + offset + len) > actual_length) { + printk(KERN_ERR PFX "BAP read beyond buffer end " + "in rx frame\n"); + return -EINVAL; + } + memcpy(buf, ans->data + offset, len); + return 0; + } + + if (EZUSB_IS_INFO(id)) { + /* Include 4 bytes for length/type */ + if ((sizeof(*ans) + offset + len - 4) > actual_length) { + printk(KERN_ERR PFX "BAP read beyond buffer end " + "in info frame\n"); + return -EFAULT; + } + memcpy(buf, ans->data + offset - 4, len); + } else { + printk(KERN_ERR PFX "Unexpected fid 0x%04x\n", id); + return -EINVAL; + } + + return 0; +} + +static int ezusb_read_pda(struct hermes *hw, __le16 *pda, + u32 pda_addr, u16 pda_len) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + __le16 data[] = { + cpu_to_le16(pda_addr & 0xffff), + cpu_to_le16(pda_len - 4) + }; + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_READ_PDA, EZUSB_RID_READ_PDA); + if (!ctx) + return -ENOMEM; + + /* wl_lkm does not include PDA size in the PDA area. + * We will pad the information into pda, so other routines + * don't have to be modified */ + pda[0] = cpu_to_le16(pda_len - 2); + /* Includes CFG_PROD_DATA but not itself */ + pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ + + return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, + EZUSB_FRAME_CONTROL, &pda[2], pda_len - 4, + NULL); +} + +static int ezusb_program_init(struct hermes *hw, u32 entry_point) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + __le32 data = cpu_to_le32(entry_point); + + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_INIT, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); +} + +static int ezusb_program_end(struct hermes *hw) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_END, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, 0, NULL, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); +} + +static int ezusb_program_bytes(struct hermes *hw, const char *buf, + u32 addr, u32 len) +{ + struct ezusb_priv *upriv = hw->priv; + struct request_context *ctx; + __le32 data = cpu_to_le32(addr); + int err; + + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_SET_ADDR, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + err = ezusb_access_ltv(upriv, ctx, sizeof(data), &data, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); + if (err) + return err; + + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_BYTES, EZUSB_RID_ACK); + if (!ctx) + return -ENOMEM; + + return ezusb_access_ltv(upriv, ctx, len, buf, + EZUSB_FRAME_CONTROL, NULL, 0, NULL); +} + +static int ezusb_program(struct hermes *hw, const char *buf, + u32 addr, u32 len) +{ + u32 ch_addr; + u32 ch_len; + int err = 0; + + /* We can only send 2048 bytes out of the bulk xmit at a time, + * so we have to split any programming into chunks of <2048 + * bytes. */ + + ch_len = (len < MAX_DL_SIZE) ? len : MAX_DL_SIZE; + ch_addr = addr; + + while (ch_addr < (addr + len)) { + pr_debug("Programming subblock of length %d " + "to address 0x%08x. Data @ %p\n", + ch_len, ch_addr, &buf[ch_addr - addr]); + + err = ezusb_program_bytes(hw, &buf[ch_addr - addr], + ch_addr, ch_len); + if (err) + break; + + ch_addr += ch_len; + ch_len = ((addr + len - ch_addr) < MAX_DL_SIZE) ? + (addr + len - ch_addr) : MAX_DL_SIZE; + } + + return err; +} + +static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct net_device_stats *stats = &priv->stats; + struct ezusb_priv *upriv = priv->card; + u8 mic[MICHAEL_MIC_LEN + 1]; + int err = 0; + int tx_control; + unsigned long flags; + struct request_context *ctx; + u8 *buf; + int tx_size; + + if (!netif_running(dev)) { + printk(KERN_ERR "%s: Tx on stopped device!\n", + dev->name); + return NETDEV_TX_BUSY; + } + + if (netif_queue_stopped(dev)) { + printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", + dev->name); + return NETDEV_TX_BUSY; + } + + if (orinoco_lock(priv, &flags) != 0) { + printk(KERN_ERR + "%s: ezusb_xmit() called while hw_unavailable\n", + dev->name); + return NETDEV_TX_BUSY; + } + + if (!netif_carrier_ok(dev) || + (priv->iw_mode == NL80211_IFTYPE_MONITOR)) { + /* Oops, the firmware hasn't established a connection, + silently drop the packet (this seems to be the + safest approach). */ + goto drop; + } + + /* Check packet length */ + if (skb->len < ETH_HLEN) + goto drop; + + ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_TX, 0); + if (!ctx) + goto busy; + + memset(ctx->buf, 0, BULK_BUF_SIZE); + buf = ctx->buf->data; + + tx_control = 0; + + err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control, + &mic[0]); + if (err) + goto drop; + + { + __le16 *tx_cntl = (__le16 *)buf; + *tx_cntl = cpu_to_le16(tx_control); + buf += sizeof(*tx_cntl); + } + + memcpy(buf, skb->data, skb->len); + buf += skb->len; + + if (tx_control & HERMES_TXCTRL_MIC) { + u8 *m = mic; + /* Mic has been offset so it can be copied to an even + * address. We're copying eveything anyway, so we + * don't need to copy that first byte. */ + if (skb->len % 2) + m++; + memcpy(buf, m, MICHAEL_MIC_LEN); + buf += MICHAEL_MIC_LEN; + } + + /* Finally, we actually initiate the send */ + netif_stop_queue(dev); + + /* The card may behave better if we send evenly sized usb transfers */ + tx_size = ALIGN(buf - ctx->buf->data, 2); + + err = ezusb_access_ltv(upriv, ctx, tx_size, NULL, + EZUSB_FRAME_DATA, NULL, 0, NULL); + + if (err) { + netif_start_queue(dev); + if (net_ratelimit()) + printk(KERN_ERR "%s: Error %d transmitting packet\n", + dev->name, err); + goto busy; + } + + dev->trans_start = jiffies; + stats->tx_bytes += skb->len; + goto ok; + + drop: + stats->tx_errors++; + stats->tx_dropped++; + + ok: + orinoco_unlock(priv, &flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + + busy: + orinoco_unlock(priv, &flags); + return NETDEV_TX_BUSY; +} + +static int ezusb_allocate(struct hermes *hw, u16 size, u16 *fid) +{ + *fid = EZUSB_RID_TX; + return 0; +} + + +static int ezusb_hard_reset(struct orinoco_private *priv) +{ + struct ezusb_priv *upriv = priv->card; + int retval = ezusb_8051_cpucs(upriv, 1); + + if (retval < 0) { + err("Failed to reset"); + return retval; + } + + retval = ezusb_8051_cpucs(upriv, 0); + if (retval < 0) { + err("Failed to unreset"); + return retval; + } + + netdev_dbg(upriv->dev, "sending control message\n"); + retval = usb_control_msg(upriv->udev, + usb_sndctrlpipe(upriv->udev, 0), + EZUSB_REQUEST_TRIGER, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | + USB_DIR_OUT, 0x0, 0x0, NULL, 0, + DEF_TIMEOUT); + if (retval < 0) { + err("EZUSB_REQUEST_TRIGER failed retval %d", retval); + return retval; + } +#if 0 + dbg("Sending EZUSB_REQUEST_TRIG_AC"); + retval = usb_control_msg(upriv->udev, + usb_sndctrlpipe(upriv->udev, 0), + EZUSB_REQUEST_TRIG_AC, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | + USB_DIR_OUT, 0x00FA, 0x0, NULL, 0, + DEF_TIMEOUT); + if (retval < 0) { + err("EZUSB_REQUEST_TRIG_AC failed retval %d", retval); + return retval; + } +#endif + + return 0; +} + + +static int ezusb_init(struct hermes *hw) +{ + struct ezusb_priv *upriv = hw->priv; + int retval; + + BUG_ON(in_interrupt()); + BUG_ON(!upriv); + + upriv->reply_count = 0; + /* Write the MAGIC number on the simulated registers to keep + * orinoco.c happy */ + hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); + hermes_write_regn(hw, RXFID, EZUSB_RID_RX); + + usb_kill_urb(upriv->read_urb); + ezusb_submit_in_urb(upriv); + + retval = ezusb_write_ltv(hw, 0, EZUSB_RID_INIT1, + HERMES_BYTES_TO_RECLEN(2), "\x10\x00"); + if (retval < 0) { + printk(KERN_ERR PFX "EZUSB_RID_INIT1 error %d\n", retval); + return retval; + } + + retval = ezusb_docmd_wait(hw, HERMES_CMD_INIT, 0, NULL); + if (retval < 0) { + printk(KERN_ERR PFX "HERMES_CMD_INIT error %d\n", retval); + return retval; + } + + return 0; +} + +static void ezusb_bulk_in_callback(struct urb *urb) +{ + struct ezusb_priv *upriv = (struct ezusb_priv *) urb->context; + struct ezusb_packet *ans = urb->transfer_buffer; + u16 crc; + u16 hermes_rid; + + if (upriv->udev == NULL) + return; + + if (urb->status == -ETIMEDOUT) { + /* When a device gets unplugged we get this every time + * we resubmit, flooding the logs. Since we don't use + * USB timeouts, it shouldn't happen any other time*/ + pr_warn("%s: urb timed out, not resubmitting\n", __func__); + return; + } + if (urb->status == -ECONNABORTED) { + pr_warn("%s: connection abort, resubmitting urb\n", + __func__); + goto resubmit; + } + if ((urb->status == -EILSEQ) + || (urb->status == -ENOENT) + || (urb->status == -ECONNRESET)) { + netdev_dbg(upriv->dev, "status %d, not resubmiting\n", + urb->status); + return; + } + if (urb->status) + netdev_dbg(upriv->dev, "status: %d length: %d\n", + urb->status, urb->actual_length); + if (urb->actual_length < sizeof(*ans)) { + err("%s: short read, ignoring", __func__); + goto resubmit; + } + crc = build_crc(ans); + if (le16_to_cpu(ans->crc) != crc) { + err("CRC error, ignoring packet"); + goto resubmit; + } + + hermes_rid = le16_to_cpu(ans->hermes_rid); + if ((hermes_rid != EZUSB_RID_RX) && !EZUSB_IS_INFO(hermes_rid)) { + ezusb_request_in_callback(upriv, urb); + } else if (upriv->dev) { + struct net_device *dev = upriv->dev; + struct orinoco_private *priv = ndev_priv(dev); + struct hermes *hw = &priv->hw; + + if (hermes_rid == EZUSB_RID_RX) { + __orinoco_ev_rx(dev, hw); + } else { + hermes_write_regn(hw, INFOFID, + le16_to_cpu(ans->hermes_rid)); + __orinoco_ev_info(dev, hw); + } + } + + resubmit: + if (upriv->udev) + ezusb_submit_in_urb(upriv); +} + +static inline void ezusb_delete(struct ezusb_priv *upriv) +{ + struct net_device *dev; + struct list_head *item; + struct list_head *tmp_item; + unsigned long flags; + + BUG_ON(in_interrupt()); + BUG_ON(!upriv); + + dev = upriv->dev; + mutex_lock(&upriv->mtx); + + upriv->udev = NULL; /* No timer will be rearmed from here */ + + usb_kill_urb(upriv->read_urb); + + spin_lock_irqsave(&upriv->req_lock, flags); + list_for_each_safe(item, tmp_item, &upriv->req_active) { + struct request_context *ctx; + int err; + + ctx = list_entry(item, struct request_context, list); + atomic_inc(&ctx->refcount); + + ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; + err = usb_unlink_urb(ctx->outurb); + + spin_unlock_irqrestore(&upriv->req_lock, flags); + if (err == -EINPROGRESS) + wait_for_completion(&ctx->done); + + del_timer_sync(&ctx->timer); + /* FIXME: there is an slight chance for the irq handler to + * be running */ + if (!list_empty(&ctx->list)) + ezusb_ctx_complete(ctx); + + ezusb_request_context_put(ctx); + spin_lock_irqsave(&upriv->req_lock, flags); + } + spin_unlock_irqrestore(&upriv->req_lock, flags); + + list_for_each_safe(item, tmp_item, &upriv->req_pending) + ezusb_ctx_complete(list_entry(item, + struct request_context, list)); + + if (upriv->read_urb && upriv->read_urb->status == -EINPROGRESS) + printk(KERN_ERR PFX "Some URB in progress\n"); + + mutex_unlock(&upriv->mtx); + + if (upriv->read_urb) { + kfree(upriv->read_urb->transfer_buffer); + usb_free_urb(upriv->read_urb); + } + kfree(upriv->bap_buf); + if (upriv->dev) { + struct orinoco_private *priv = ndev_priv(upriv->dev); + orinoco_if_del(priv); + wiphy_unregister(priv_to_wiphy(upriv)); + free_orinocodev(priv); + } +} + +static void ezusb_lock_irqsave(spinlock_t *lock, + unsigned long *flags) __acquires(lock) +{ + spin_lock_bh(lock); +} + +static void ezusb_unlock_irqrestore(spinlock_t *lock, + unsigned long *flags) __releases(lock) +{ + spin_unlock_bh(lock); +} + +static void ezusb_lock_irq(spinlock_t *lock) __acquires(lock) +{ + spin_lock_bh(lock); +} + +static void ezusb_unlock_irq(spinlock_t *lock) __releases(lock) +{ + spin_unlock_bh(lock); +} + +static const struct hermes_ops ezusb_ops = { + .init = ezusb_init, + .cmd_wait = ezusb_docmd_wait, + .init_cmd_wait = ezusb_doicmd_wait, + .allocate = ezusb_allocate, + .read_ltv = ezusb_read_ltv, + .write_ltv = ezusb_write_ltv, + .bap_pread = ezusb_bap_pread, + .read_pda = ezusb_read_pda, + .program_init = ezusb_program_init, + .program_end = ezusb_program_end, + .program = ezusb_program, + .lock_irqsave = ezusb_lock_irqsave, + .unlock_irqrestore = ezusb_unlock_irqrestore, + .lock_irq = ezusb_lock_irq, + .unlock_irq = ezusb_unlock_irq, +}; + +static const struct net_device_ops ezusb_netdev_ops = { + .ndo_open = orinoco_open, + .ndo_stop = orinoco_stop, + .ndo_start_xmit = ezusb_xmit, + .ndo_set_rx_mode = orinoco_set_multicast_list, + .ndo_change_mtu = orinoco_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = orinoco_tx_timeout, + .ndo_get_stats = orinoco_get_stats, +}; + +static int ezusb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct orinoco_private *priv; + struct hermes *hw; + struct ezusb_priv *upriv = NULL; + struct usb_interface_descriptor *iface_desc; + struct usb_endpoint_descriptor *ep; + const struct firmware *fw_entry = NULL; + int retval = 0; + int i; + + priv = alloc_orinocodev(sizeof(*upriv), &udev->dev, + ezusb_hard_reset, NULL); + if (!priv) { + err("Couldn't allocate orinocodev"); + retval = -ENOMEM; + goto exit; + } + + hw = &priv->hw; + + upriv = priv->card; + + mutex_init(&upriv->mtx); + spin_lock_init(&upriv->reply_count_lock); + + spin_lock_init(&upriv->req_lock); + INIT_LIST_HEAD(&upriv->req_pending); + INIT_LIST_HEAD(&upriv->req_active); + + upriv->udev = udev; + + hw->iobase = (void __force __iomem *) &upriv->hermes_reg_fake; + hw->reg_spacing = HERMES_16BIT_REGSPACING; + hw->priv = upriv; + hw->ops = &ezusb_ops; + + /* set up the endpoint information */ + /* check out the endpoints */ + + iface_desc = &interface->altsetting[0].desc; + for (i = 0; i < iface_desc->bNumEndpoints; ++i) { + ep = &interface->altsetting[0].endpoint[i].desc; + + if (usb_endpoint_is_bulk_in(ep)) { + /* we found a bulk in endpoint */ + if (upriv->read_urb != NULL) { + pr_warn("Found a second bulk in ep, ignored\n"); + continue; + } + + upriv->read_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!upriv->read_urb) { + err("No free urbs available"); + goto error; + } + if (le16_to_cpu(ep->wMaxPacketSize) != 64) + pr_warn("bulk in: wMaxPacketSize!= 64\n"); + if (ep->bEndpointAddress != (2 | USB_DIR_IN)) + pr_warn("bulk in: bEndpointAddress: %d\n", + ep->bEndpointAddress); + upriv->read_pipe = usb_rcvbulkpipe(udev, + ep-> + bEndpointAddress); + upriv->read_urb->transfer_buffer = + kmalloc(BULK_BUF_SIZE, GFP_KERNEL); + if (!upriv->read_urb->transfer_buffer) { + err("Couldn't allocate IN buffer"); + goto error; + } + } + + if (usb_endpoint_is_bulk_out(ep)) { + /* we found a bulk out endpoint */ + if (upriv->bap_buf != NULL) { + pr_warn("Found a second bulk out ep, ignored\n"); + continue; + } + + if (le16_to_cpu(ep->wMaxPacketSize) != 64) + pr_warn("bulk out: wMaxPacketSize != 64\n"); + if (ep->bEndpointAddress != 2) + pr_warn("bulk out: bEndpointAddress: %d\n", + ep->bEndpointAddress); + upriv->write_pipe = usb_sndbulkpipe(udev, + ep-> + bEndpointAddress); + upriv->bap_buf = kmalloc(BULK_BUF_SIZE, GFP_KERNEL); + if (!upriv->bap_buf) { + err("Couldn't allocate bulk_out_buffer"); + goto error; + } + } + } + if (!upriv->bap_buf || !upriv->read_urb) { + err("Didn't find the required bulk endpoints"); + goto error; + } + + if (request_firmware(&fw_entry, "orinoco_ezusb_fw", + &interface->dev) == 0) { + firmware.size = fw_entry->size; + firmware.code = fw_entry->data; + } + if (firmware.size && firmware.code) { + if (ezusb_firmware_download(upriv, &firmware) < 0) + goto error; + } else { + err("No firmware to download"); + goto error; + } + + if (ezusb_hard_reset(priv) < 0) { + err("Cannot reset the device"); + goto error; + } + + /* If the firmware is already downloaded orinoco.c will call + * ezusb_init but if the firmware is not already there, that will make + * the kernel very unstable, so we try initializing here and quit in + * case of error */ + if (ezusb_init(hw) < 0) { + err("Couldn't initialize the device"); + err("Firmware may not be downloaded or may be wrong."); + goto error; + } + + /* Initialise the main driver */ + if (orinoco_init(priv) != 0) { + err("orinoco_init() failed\n"); + goto error; + } + + if (orinoco_if_add(priv, 0, 0, &ezusb_netdev_ops) != 0) { + upriv->dev = NULL; + err("%s: orinoco_if_add() failed", __func__); + wiphy_unregister(priv_to_wiphy(priv)); + goto error; + } + upriv->dev = priv->ndev; + + goto exit; + + error: + ezusb_delete(upriv); + if (upriv->dev) { + /* upriv->dev was 0, so ezusb_delete() didn't free it */ + free_orinocodev(priv); + } + upriv = NULL; + retval = -EFAULT; + exit: + if (fw_entry) { + firmware.code = NULL; + firmware.size = 0; + release_firmware(fw_entry); + } + usb_set_intfdata(interface, upriv); + return retval; +} + + +static void ezusb_disconnect(struct usb_interface *intf) +{ + struct ezusb_priv *upriv = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + ezusb_delete(upriv); + printk(KERN_INFO PFX "Disconnected\n"); +} + + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver orinoco_driver = { + .name = DRIVER_NAME, + .probe = ezusb_probe, + .disconnect = ezusb_disconnect, + .id_table = ezusb_table, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(orinoco_driver); + +MODULE_AUTHOR("Manuel Estrada Sainz"); +MODULE_DESCRIPTION("Driver for Orinoco wireless LAN cards using EZUSB bridge"); +MODULE_LICENSE("Dual MPL/GPL"); diff --git a/drivers/net/wireless/intersil/orinoco/scan.c b/drivers/net/wireless/intersil/orinoco/scan.c new file mode 100644 index 000000000000..2c66166add70 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/scan.c @@ -0,0 +1,251 @@ +/* Helpers for managing scan queues + * + * See copyright notice in main.c + */ + +#include +#include +#include +#include +#include + +#include "hermes.h" +#include "orinoco.h" +#include "main.h" + +#include "scan.h" + +#define ZERO_DBM_OFFSET 0x95 +#define MAX_SIGNAL_LEVEL 0x8A +#define MIN_SIGNAL_LEVEL 0x2F + +#define SIGNAL_TO_DBM(x) \ + (clamp_t(s32, (x), MIN_SIGNAL_LEVEL, MAX_SIGNAL_LEVEL) \ + - ZERO_DBM_OFFSET) +#define SIGNAL_TO_MBM(x) (SIGNAL_TO_DBM(x) * 100) + +static int symbol_build_supp_rates(u8 *buf, const __le16 *rates) +{ + int i; + u8 rate; + + buf[0] = WLAN_EID_SUPP_RATES; + for (i = 0; i < 5; i++) { + rate = le16_to_cpu(rates[i]); + /* NULL terminated */ + if (rate == 0x0) + break; + buf[i + 2] = rate; + } + buf[1] = i; + + return i + 2; +} + +static int prism_build_supp_rates(u8 *buf, const u8 *rates) +{ + int i; + + buf[0] = WLAN_EID_SUPP_RATES; + for (i = 0; i < 8; i++) { + /* NULL terminated */ + if (rates[i] == 0x0) + break; + buf[i + 2] = rates[i]; + } + buf[1] = i; + + /* We might still have another 2 rates, which need to go in + * extended supported rates */ + if (i == 8 && rates[i] > 0) { + buf[10] = WLAN_EID_EXT_SUPP_RATES; + for (; i < 10; i++) { + /* NULL terminated */ + if (rates[i] == 0x0) + break; + buf[i + 2] = rates[i]; + } + buf[11] = i - 8; + } + + return (i < 8) ? i + 2 : i + 4; +} + +static void orinoco_add_hostscan_result(struct orinoco_private *priv, + const union hermes_scan_info *bss) +{ + struct wiphy *wiphy = priv_to_wiphy(priv); + struct ieee80211_channel *channel; + struct cfg80211_bss *cbss; + u8 *ie; + u8 ie_buf[46]; + u64 timestamp; + s32 signal; + u16 capability; + u16 beacon_interval; + int ie_len; + int freq; + int len; + + len = le16_to_cpu(bss->a.essid_len); + + /* Reconstruct SSID and bitrate IEs to pass up */ + ie_buf[0] = WLAN_EID_SSID; + ie_buf[1] = len; + memcpy(&ie_buf[2], bss->a.essid, len); + + ie = ie_buf + len + 2; + ie_len = ie_buf[1] + 2; + switch (priv->firmware_type) { + case FIRMWARE_TYPE_SYMBOL: + ie_len += symbol_build_supp_rates(ie, bss->s.rates); + break; + + case FIRMWARE_TYPE_INTERSIL: + ie_len += prism_build_supp_rates(ie, bss->p.rates); + break; + + case FIRMWARE_TYPE_AGERE: + default: + break; + } + + freq = ieee80211_channel_to_frequency( + le16_to_cpu(bss->a.channel), IEEE80211_BAND_2GHZ); + channel = ieee80211_get_channel(wiphy, freq); + if (!channel) { + printk(KERN_DEBUG "Invalid channel designation %04X(%04X)", + bss->a.channel, freq); + return; /* Then ignore it for now */ + } + timestamp = 0; + capability = le16_to_cpu(bss->a.capabilities); + beacon_interval = le16_to_cpu(bss->a.beacon_interv); + signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level)); + + cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN, + bss->a.bssid, timestamp, capability, + beacon_interval, ie_buf, ie_len, signal, + GFP_KERNEL); + cfg80211_put_bss(wiphy, cbss); +} + +void orinoco_add_extscan_result(struct orinoco_private *priv, + struct agere_ext_scan_info *bss, + size_t len) +{ + struct wiphy *wiphy = priv_to_wiphy(priv); + struct ieee80211_channel *channel; + struct cfg80211_bss *cbss; + const u8 *ie; + u64 timestamp; + s32 signal; + u16 capability; + u16 beacon_interval; + size_t ie_len; + int chan, freq; + + ie_len = len - sizeof(*bss); + ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len); + chan = ie ? ie[2] : 0; + freq = ieee80211_channel_to_frequency(chan, IEEE80211_BAND_2GHZ); + channel = ieee80211_get_channel(wiphy, freq); + + timestamp = le64_to_cpu(bss->timestamp); + capability = le16_to_cpu(bss->capabilities); + beacon_interval = le16_to_cpu(bss->beacon_interval); + ie = bss->data; + signal = SIGNAL_TO_MBM(bss->level); + + cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN, + bss->bssid, timestamp, capability, + beacon_interval, ie, ie_len, signal, + GFP_KERNEL); + cfg80211_put_bss(wiphy, cbss); +} + +void orinoco_add_hostscan_results(struct orinoco_private *priv, + unsigned char *buf, + size_t len) +{ + int offset; /* In the scan data */ + size_t atom_len; + bool abort = false; + + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: + atom_len = sizeof(struct agere_scan_apinfo); + offset = 0; + break; + + case FIRMWARE_TYPE_SYMBOL: + /* Lack of documentation necessitates this hack. + * Different firmwares have 68 or 76 byte long atoms. + * We try modulo first. If the length divides by both, + * we check what would be the channel in the second + * frame for a 68-byte atom. 76-byte atoms have 0 there. + * Valid channel cannot be 0. */ + if (len % 76) + atom_len = 68; + else if (len % 68) + atom_len = 76; + else if (len >= 1292 && buf[68] == 0) + atom_len = 76; + else + atom_len = 68; + offset = 0; + break; + + case FIRMWARE_TYPE_INTERSIL: + offset = 4; + if (priv->has_hostscan) { + atom_len = le16_to_cpup((__le16 *)buf); + /* Sanity check for atom_len */ + if (atom_len < sizeof(struct prism2_scan_apinfo)) { + printk(KERN_ERR "%s: Invalid atom_len in scan " + "data: %zu\n", priv->ndev->name, + atom_len); + abort = true; + goto scan_abort; + } + } else + atom_len = offsetof(struct prism2_scan_apinfo, atim); + break; + + default: + abort = true; + goto scan_abort; + } + + /* Check that we got an whole number of atoms */ + if ((len - offset) % atom_len) { + printk(KERN_ERR "%s: Unexpected scan data length %zu, " + "atom_len %zu, offset %d\n", priv->ndev->name, len, + atom_len, offset); + abort = true; + goto scan_abort; + } + + /* Process the entries one by one */ + for (; offset + atom_len <= len; offset += atom_len) { + union hermes_scan_info *atom; + + atom = (union hermes_scan_info *) (buf + offset); + + orinoco_add_hostscan_result(priv, atom); + } + + scan_abort: + if (priv->scan_request) { + cfg80211_scan_done(priv->scan_request, abort); + priv->scan_request = NULL; + } +} + +void orinoco_scan_done(struct orinoco_private *priv, bool abort) +{ + if (priv->scan_request) { + cfg80211_scan_done(priv->scan_request, abort); + priv->scan_request = NULL; + } +} diff --git a/drivers/net/wireless/intersil/orinoco/scan.h b/drivers/net/wireless/intersil/orinoco/scan.h new file mode 100644 index 000000000000..27281fb0a6dc --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/scan.h @@ -0,0 +1,21 @@ +/* Helpers for managing scan queues + * + * See copyright notice in main.c + */ +#ifndef _ORINOCO_SCAN_H_ +#define _ORINOCO_SCAN_H_ + +/* Forward declarations */ +struct orinoco_private; +struct agere_ext_scan_info; + +/* Add scan results */ +void orinoco_add_extscan_result(struct orinoco_private *priv, + struct agere_ext_scan_info *atom, + size_t len); +void orinoco_add_hostscan_results(struct orinoco_private *dev, + unsigned char *buf, + size_t len); +void orinoco_scan_done(struct orinoco_private *priv, bool abort); + +#endif /* _ORINOCO_SCAN_H_ */ diff --git a/drivers/net/wireless/intersil/orinoco/spectrum_cs.c b/drivers/net/wireless/intersil/orinoco/spectrum_cs.c new file mode 100644 index 000000000000..b60048c95e0a --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/spectrum_cs.c @@ -0,0 +1,320 @@ +/* + * Driver for 802.11b cards using RAM-loadable Symbol firmware, such as + * Symbol Wireless Networker LA4137, CompactFlash cards by Socket + * Communications and Intel PRO/Wireless 2011B. + * + * The driver implements Symbol firmware download. The rest is handled + * in hermes.c and main.c. + * + * Utilities for downloading the Symbol firmware are available at + * http://sourceforge.net/projects/orinoco/ + * + * Copyright (C) 2002-2005 Pavel Roskin + * Portions based on orinoco_cs.c: + * Copyright (C) David Gibson, Linuxcare Australia + * Portions based on Spectrum24tDnld.c from original spectrum24 driver: + * Copyright (C) Symbol Technologies. + * + * See copyright notice in file main.c. + */ + +#define DRIVER_NAME "spectrum_cs" +#define PFX DRIVER_NAME ": " + +#include +#include +#include +#include +#include +#include + +#include "orinoco.h" + +/********************************************************************/ +/* Module stuff */ +/********************************************************************/ + +MODULE_AUTHOR("Pavel Roskin "); +MODULE_DESCRIPTION("Driver for Symbol Spectrum24 Trilogy cards with firmware downloader"); +MODULE_LICENSE("Dual MPL/GPL"); + +/* Module parameters */ + +/* Some D-Link cards have buggy CIS. They do work at 5v properly, but + * don't have any CIS entry for it. This workaround it... */ +static int ignore_cis_vcc; /* = 0 */ +module_param(ignore_cis_vcc, int, 0); +MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket"); + +/********************************************************************/ +/* Data structures */ +/********************************************************************/ + +/* PCMCIA specific device information (goes in the card field of + * struct orinoco_private */ +struct orinoco_pccard { + struct pcmcia_device *p_dev; +}; + +/********************************************************************/ +/* Function prototypes */ +/********************************************************************/ + +static int spectrum_cs_config(struct pcmcia_device *link); +static void spectrum_cs_release(struct pcmcia_device *link); + +/* Constants for the CISREG_CCSR register */ +#define HCR_RUN 0x07 /* run firmware after reset */ +#define HCR_IDLE 0x0E /* don't run firmware after reset */ +#define HCR_MEM16 0x10 /* memory width bit, should be preserved */ + + +/* + * Reset the card using configuration registers COR and CCSR. + * If IDLE is 1, stop the firmware, so that it can be safely rewritten. + */ +static int +spectrum_reset(struct pcmcia_device *link, int idle) +{ + int ret; + u8 save_cor; + u8 ccsr; + + /* Doing it if hardware is gone is guaranteed crash */ + if (!pcmcia_dev_present(link)) + return -ENODEV; + + /* Save original COR value */ + ret = pcmcia_read_config_byte(link, CISREG_COR, &save_cor); + if (ret) + goto failed; + + /* Soft-Reset card */ + ret = pcmcia_write_config_byte(link, CISREG_COR, + (save_cor | COR_SOFT_RESET)); + if (ret) + goto failed; + udelay(1000); + + /* Read CCSR */ + ret = pcmcia_read_config_byte(link, CISREG_CCSR, &ccsr); + if (ret) + goto failed; + + /* + * Start or stop the firmware. Memory width bit should be + * preserved from the value we've just read. + */ + ccsr = (idle ? HCR_IDLE : HCR_RUN) | (ccsr & HCR_MEM16); + ret = pcmcia_write_config_byte(link, CISREG_CCSR, ccsr); + if (ret) + goto failed; + udelay(1000); + + /* Restore original COR configuration index */ + ret = pcmcia_write_config_byte(link, CISREG_COR, + (save_cor & ~COR_SOFT_RESET)); + if (ret) + goto failed; + udelay(1000); + return 0; + +failed: + return -ENODEV; +} + +/********************************************************************/ +/* Device methods */ +/********************************************************************/ + +static int +spectrum_cs_hard_reset(struct orinoco_private *priv) +{ + struct orinoco_pccard *card = priv->card; + struct pcmcia_device *link = card->p_dev; + + /* Soft reset using COR and HCR */ + spectrum_reset(link, 0); + + return 0; +} + +static int +spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle) +{ + struct orinoco_pccard *card = priv->card; + struct pcmcia_device *link = card->p_dev; + + return spectrum_reset(link, idle); +} + +/********************************************************************/ +/* PCMCIA stuff */ +/********************************************************************/ + +static int +spectrum_cs_probe(struct pcmcia_device *link) +{ + struct orinoco_private *priv; + struct orinoco_pccard *card; + + priv = alloc_orinocodev(sizeof(*card), &link->dev, + spectrum_cs_hard_reset, + spectrum_cs_stop_firmware); + if (!priv) + return -ENOMEM; + card = priv->card; + + /* Link both structures together */ + card->p_dev = link; + link->priv = priv; + + return spectrum_cs_config(link); +} /* spectrum_cs_attach */ + +static void spectrum_cs_detach(struct pcmcia_device *link) +{ + struct orinoco_private *priv = link->priv; + + orinoco_if_del(priv); + + spectrum_cs_release(link); + + free_orinocodev(priv); +} /* spectrum_cs_detach */ + +static int spectrum_cs_config_check(struct pcmcia_device *p_dev, + void *priv_data) +{ + if (p_dev->config_index == 0) + return -EINVAL; + + return pcmcia_request_io(p_dev); +}; + +static int +spectrum_cs_config(struct pcmcia_device *link) +{ + struct orinoco_private *priv = link->priv; + struct hermes *hw = &priv->hw; + int ret; + void __iomem *mem; + + link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC | + CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; + if (ignore_cis_vcc) + link->config_flags &= ~CONF_AUTO_CHECK_VCC; + ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL); + if (ret) { + if (!ignore_cis_vcc) + printk(KERN_ERR PFX "GetNextTuple(): No matching " + "CIS configuration. Maybe you need the " + "ignore_cis_vcc=1 parameter.\n"); + goto failed; + } + + mem = ioport_map(link->resource[0]->start, + resource_size(link->resource[0])); + if (!mem) + goto failed; + + /* We initialize the hermes structure before completing PCMCIA + * configuration just in case the interrupt handler gets + * called. */ + hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING); + hw->eeprom_pda = true; + + ret = pcmcia_request_irq(link, orinoco_interrupt); + if (ret) + goto failed; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + /* Reset card */ + if (spectrum_cs_hard_reset(priv) != 0) + goto failed; + + /* Initialise the main driver */ + if (orinoco_init(priv) != 0) { + printk(KERN_ERR PFX "orinoco_init() failed\n"); + goto failed; + } + + /* Register an interface with the stack */ + if (orinoco_if_add(priv, link->resource[0]->start, + link->irq, NULL) != 0) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); + goto failed; + } + + return 0; + + failed: + spectrum_cs_release(link); + return -ENODEV; +} /* spectrum_cs_config */ + +static void +spectrum_cs_release(struct pcmcia_device *link) +{ + struct orinoco_private *priv = link->priv; + unsigned long flags; + + /* We're committed to taking the device away now, so mark the + * hardware as unavailable */ + priv->hw.ops->lock_irqsave(&priv->lock, &flags); + priv->hw_unavailable++; + priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); + + pcmcia_disable_device(link); + if (priv->hw.iobase) + ioport_unmap(priv->hw.iobase); +} /* spectrum_cs_release */ + + +static int +spectrum_cs_suspend(struct pcmcia_device *link) +{ + struct orinoco_private *priv = link->priv; + int err = 0; + + /* Mark the device as stopped, to block IO until later */ + orinoco_down(priv); + + return err; +} + +static int +spectrum_cs_resume(struct pcmcia_device *link) +{ + struct orinoco_private *priv = link->priv; + int err = orinoco_up(priv); + + return err; +} + + +/********************************************************************/ +/* Module initialization */ +/********************************************************************/ + +static const struct pcmcia_device_id spectrum_cs_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */ + PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */ + PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */ + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, spectrum_cs_ids); + +static struct pcmcia_driver orinoco_driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .probe = spectrum_cs_probe, + .remove = spectrum_cs_detach, + .suspend = spectrum_cs_suspend, + .resume = spectrum_cs_resume, + .id_table = spectrum_cs_ids, +}; +module_pcmcia_driver(orinoco_driver); diff --git a/drivers/net/wireless/intersil/orinoco/wext.c b/drivers/net/wireless/intersil/orinoco/wext.c new file mode 100644 index 000000000000..1d4dae422106 --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/wext.c @@ -0,0 +1,1413 @@ +/* Wireless extensions support. + * + * See copyright notice in main.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hermes.h" +#include "hermes_rid.h" +#include "orinoco.h" + +#include "hw.h" +#include "mic.h" +#include "scan.h" +#include "main.h" + +#include "wext.h" + +#define MAX_RID_LEN 1024 + +/* Helper routine to record keys + * It is called under orinoco_lock so it may not sleep */ +static int orinoco_set_key(struct orinoco_private *priv, int index, + enum orinoco_alg alg, const u8 *key, int key_len, + const u8 *seq, int seq_len) +{ + kzfree(priv->keys[index].key); + kzfree(priv->keys[index].seq); + + if (key_len) { + priv->keys[index].key = kzalloc(key_len, GFP_ATOMIC); + if (!priv->keys[index].key) + goto nomem; + } else + priv->keys[index].key = NULL; + + if (seq_len) { + priv->keys[index].seq = kzalloc(seq_len, GFP_ATOMIC); + if (!priv->keys[index].seq) + goto free_key; + } else + priv->keys[index].seq = NULL; + + priv->keys[index].key_len = key_len; + priv->keys[index].seq_len = seq_len; + + if (key_len) + memcpy((void *)priv->keys[index].key, key, key_len); + if (seq_len) + memcpy((void *)priv->keys[index].seq, seq, seq_len); + + switch (alg) { + case ORINOCO_ALG_TKIP: + priv->keys[index].cipher = WLAN_CIPHER_SUITE_TKIP; + break; + + case ORINOCO_ALG_WEP: + priv->keys[index].cipher = (key_len > SMALL_KEY_SIZE) ? + WLAN_CIPHER_SUITE_WEP104 : WLAN_CIPHER_SUITE_WEP40; + break; + + case ORINOCO_ALG_NONE: + default: + priv->keys[index].cipher = 0; + break; + } + + return 0; + +free_key: + kfree(priv->keys[index].key); + priv->keys[index].key = NULL; + +nomem: + priv->keys[index].key_len = 0; + priv->keys[index].seq_len = 0; + priv->keys[index].cipher = 0; + + return -ENOMEM; +} + +static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct hermes *hw = &priv->hw; + struct iw_statistics *wstats = &priv->wstats; + int err; + unsigned long flags; + + if (!netif_device_present(dev)) { + printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n", + dev->name); + return NULL; /* FIXME: Can we do better than this? */ + } + + /* If busy, return the old stats. Returning NULL may cause + * the interface to disappear from /proc/net/wireless */ + if (orinoco_lock(priv, &flags) != 0) + return wstats; + + /* We can't really wait for the tallies inquiry command to + * complete, so we just use the previous results and trigger + * a new tallies inquiry command for next time - Jean II */ + /* FIXME: Really we should wait for the inquiry to come back - + * as it is the stats we give don't make a whole lot of sense. + * Unfortunately, it's not clear how to do that within the + * wireless extensions framework: I think we're in user + * context, but a lock seems to be held by the time we get in + * here so we're not safe to sleep here. */ + hermes_inquire(hw, HERMES_INQ_TALLIES); + + if (priv->iw_mode == NL80211_IFTYPE_ADHOC) { + memset(&wstats->qual, 0, sizeof(wstats->qual)); + /* If a spy address is defined, we report stats of the + * first spy address - Jean II */ + if (SPY_NUMBER(priv)) { + wstats->qual.qual = priv->spy_data.spy_stat[0].qual; + wstats->qual.level = priv->spy_data.spy_stat[0].level; + wstats->qual.noise = priv->spy_data.spy_stat[0].noise; + wstats->qual.updated = + priv->spy_data.spy_stat[0].updated; + } + } else { + struct { + __le16 qual, signal, noise, unused; + } __packed cq; + + err = HERMES_READ_RECORD(hw, USER_BAP, + HERMES_RID_COMMSQUALITY, &cq); + + if (!err) { + wstats->qual.qual = (int)le16_to_cpu(cq.qual); + wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95; + wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95; + wstats->qual.updated = + IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + } + } + + orinoco_unlock(priv, &flags); + return wstats; +} + +/********************************************************************/ +/* Wireless extensions */ +/********************************************************************/ + +static int orinoco_ioctl_setwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + int err = -EINPROGRESS; /* Call commit handler */ + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + /* Enable automatic roaming - no sanity checks are needed */ + if (is_zero_ether_addr(ap_addr->sa_data) || + is_broadcast_ether_addr(ap_addr->sa_data)) { + priv->bssid_fixed = 0; + eth_zero_addr(priv->desired_bssid); + + /* "off" means keep existing connection */ + if (ap_addr->sa_data[0] == 0) { + __orinoco_hw_set_wap(priv); + err = 0; + } + goto out; + } + + if (priv->firmware_type == FIRMWARE_TYPE_AGERE) { + printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't " + "support manual roaming\n", + dev->name); + err = -EOPNOTSUPP; + goto out; + } + + if (priv->iw_mode != NL80211_IFTYPE_STATION) { + printk(KERN_WARNING "%s: Manual roaming supported only in " + "managed mode\n", dev->name); + err = -EOPNOTSUPP; + goto out; + } + + /* Intersil firmware hangs without Desired ESSID */ + if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL && + strlen(priv->desired_essid) == 0) { + printk(KERN_WARNING "%s: Desired ESSID must be set for " + "manual roaming\n", dev->name); + err = -EOPNOTSUPP; + goto out; + } + + /* Finally, enable manual roaming */ + priv->bssid_fixed = 1; + memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN); + + out: + orinoco_unlock(priv, &flags); + return err; +} + +static int orinoco_ioctl_getwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + + int err = 0; + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + ap_addr->sa_family = ARPHRD_ETHER; + err = orinoco_hw_get_current_bssid(priv, ap_addr->sa_data); + + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_ioctl_setiwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *keybuf) +{ + struct orinoco_private *priv = ndev_priv(dev); + int index = (erq->flags & IW_ENCODE_INDEX) - 1; + int setindex = priv->tx_key; + enum orinoco_alg encode_alg = priv->encode_alg; + int restricted = priv->wep_restrict; + int err = -EINPROGRESS; /* Call commit handler */ + unsigned long flags; + + if (!priv->has_wep) + return -EOPNOTSUPP; + + if (erq->pointer) { + /* We actually have a key to set - check its length */ + if (erq->length > LARGE_KEY_SIZE) + return -E2BIG; + + if ((erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep) + return -E2BIG; + } + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + /* Clear any TKIP key we have */ + if ((priv->has_wpa) && (priv->encode_alg == ORINOCO_ALG_TKIP)) + (void) orinoco_clear_tkip_key(priv, setindex); + + if (erq->length > 0) { + if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) + index = priv->tx_key; + + /* Switch on WEP if off */ + if (encode_alg != ORINOCO_ALG_WEP) { + setindex = index; + encode_alg = ORINOCO_ALG_WEP; + } + } else { + /* Important note : if the user do "iwconfig eth0 enc off", + * we will arrive there with an index of -1. This is valid + * but need to be taken care off... Jean II */ + if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) { + if ((index != -1) || (erq->flags == 0)) { + err = -EINVAL; + goto out; + } + } else { + /* Set the index : Check that the key is valid */ + if (priv->keys[index].key_len == 0) { + err = -EINVAL; + goto out; + } + setindex = index; + } + } + + if (erq->flags & IW_ENCODE_DISABLED) + encode_alg = ORINOCO_ALG_NONE; + if (erq->flags & IW_ENCODE_OPEN) + restricted = 0; + if (erq->flags & IW_ENCODE_RESTRICTED) + restricted = 1; + + if (erq->pointer && erq->length > 0) { + err = orinoco_set_key(priv, index, ORINOCO_ALG_WEP, keybuf, + erq->length, NULL, 0); + } + priv->tx_key = setindex; + + /* Try fast key change if connected and only keys are changed */ + if ((priv->encode_alg == encode_alg) && + (priv->wep_restrict == restricted) && + netif_carrier_ok(dev)) { + err = __orinoco_hw_setup_wepkeys(priv); + /* No need to commit if successful */ + goto out; + } + + priv->encode_alg = encode_alg; + priv->wep_restrict = restricted; + + out: + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_ioctl_getiwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *keybuf) +{ + struct orinoco_private *priv = ndev_priv(dev); + int index = (erq->flags & IW_ENCODE_INDEX) - 1; + unsigned long flags; + + if (!priv->has_wep) + return -EOPNOTSUPP; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) + index = priv->tx_key; + + erq->flags = 0; + if (!priv->encode_alg) + erq->flags |= IW_ENCODE_DISABLED; + erq->flags |= index + 1; + + if (priv->wep_restrict) + erq->flags |= IW_ENCODE_RESTRICTED; + else + erq->flags |= IW_ENCODE_OPEN; + + erq->length = priv->keys[index].key_len; + + memcpy(keybuf, priv->keys[index].key, erq->length); + + orinoco_unlock(priv, &flags); + return 0; +} + +static int orinoco_ioctl_setessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *essidbuf) +{ + struct orinoco_private *priv = ndev_priv(dev); + unsigned long flags; + + /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it + * anyway... - Jean II */ + + /* Hum... Should not use Wireless Extension constant (may change), + * should use our own... - Jean II */ + if (erq->length > IW_ESSID_MAX_SIZE) + return -E2BIG; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */ + memset(priv->desired_essid, 0, sizeof(priv->desired_essid)); + + /* If not ANY, get the new ESSID */ + if (erq->flags) + memcpy(priv->desired_essid, essidbuf, erq->length); + + orinoco_unlock(priv, &flags); + + return -EINPROGRESS; /* Call commit handler */ +} + +static int orinoco_ioctl_getessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, + char *essidbuf) +{ + struct orinoco_private *priv = ndev_priv(dev); + int active; + int err = 0; + unsigned long flags; + + if (netif_running(dev)) { + err = orinoco_hw_get_essid(priv, &active, essidbuf); + if (err < 0) + return err; + erq->length = err; + } else { + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE); + erq->length = strlen(priv->desired_essid); + orinoco_unlock(priv, &flags); + } + + erq->flags = 1; + + return 0; +} + +static int orinoco_ioctl_setfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *frq, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + int chan = -1; + unsigned long flags; + int err = -EINPROGRESS; /* Call commit handler */ + + /* In infrastructure mode the AP sets the channel */ + if (priv->iw_mode == NL80211_IFTYPE_STATION) + return -EBUSY; + + if ((frq->e == 0) && (frq->m <= 1000)) { + /* Setting by channel number */ + chan = frq->m; + } else { + /* Setting by frequency */ + int denom = 1; + int i; + + /* Calculate denominator to rescale to MHz */ + for (i = 0; i < (6 - frq->e); i++) + denom *= 10; + + chan = ieee80211_frequency_to_channel(frq->m / denom); + } + + if ((chan < 1) || (chan > NUM_CHANNELS) || + !(priv->channel_mask & (1 << (chan - 1)))) + return -EINVAL; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + priv->channel = chan; + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { + /* Fast channel change - no commit if successful */ + struct hermes *hw = &priv->hw; + err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | + HERMES_TEST_SET_CHANNEL, + chan, NULL); + } + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_ioctl_getfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *frq, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + int tmp; + + /* Locking done in there */ + tmp = orinoco_hw_get_freq(priv); + if (tmp < 0) + return tmp; + + frq->m = tmp * 100000; + frq->e = 1; + + return 0; +} + +static int orinoco_ioctl_getsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *srq, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct hermes *hw = &priv->hw; + u16 val; + int err; + unsigned long flags; + + if (!priv->has_sensitivity) + return -EOPNOTSUPP; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFSYSTEMSCALE, &val); + orinoco_unlock(priv, &flags); + + if (err) + return err; + + srq->value = val; + srq->fixed = 0; /* auto */ + + return 0; +} + +static int orinoco_ioctl_setsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *srq, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + int val = srq->value; + unsigned long flags; + + if (!priv->has_sensitivity) + return -EOPNOTSUPP; + + if ((val < 1) || (val > 3)) + return -EINVAL; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + priv->ap_density = val; + orinoco_unlock(priv, &flags); + + return -EINPROGRESS; /* Call commit handler */ +} + +static int orinoco_ioctl_setrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + int ratemode; + int bitrate; /* 100s of kilobits */ + unsigned long flags; + + /* As the user space doesn't know our highest rate, it uses -1 + * to ask us to set the highest rate. Test it using "iwconfig + * ethX rate auto" - Jean II */ + if (rrq->value == -1) + bitrate = 110; + else { + if (rrq->value % 100000) + return -EINVAL; + bitrate = rrq->value / 100000; + } + + ratemode = orinoco_get_bitratemode(bitrate, !rrq->fixed); + + if (ratemode == -1) + return -EINVAL; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + priv->bitratemode = ratemode; + orinoco_unlock(priv, &flags); + + return -EINPROGRESS; +} + +static int orinoco_ioctl_getrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + int err = 0; + int bitrate, automatic; + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + orinoco_get_ratemode_cfg(priv->bitratemode, &bitrate, &automatic); + + /* If the interface is running we try to find more about the + current mode */ + if (netif_running(dev)) { + int act_bitrate; + int lerr; + + /* Ignore errors if we can't get the actual bitrate */ + lerr = orinoco_hw_get_act_bitrate(priv, &act_bitrate); + if (!lerr) + bitrate = act_bitrate; + } + + orinoco_unlock(priv, &flags); + + rrq->value = bitrate; + rrq->fixed = !automatic; + rrq->disabled = 0; + + return err; +} + +static int orinoco_ioctl_setpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *prq, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + int err = -EINPROGRESS; /* Call commit handler */ + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + if (prq->disabled) { + priv->pm_on = 0; + } else { + switch (prq->flags & IW_POWER_MODE) { + case IW_POWER_UNICAST_R: + priv->pm_mcast = 0; + priv->pm_on = 1; + break; + case IW_POWER_ALL_R: + priv->pm_mcast = 1; + priv->pm_on = 1; + break; + case IW_POWER_ON: + /* No flags : but we may have a value - Jean II */ + break; + default: + err = -EINVAL; + goto out; + } + + if (prq->flags & IW_POWER_TIMEOUT) { + priv->pm_on = 1; + priv->pm_timeout = prq->value / 1000; + } + if (prq->flags & IW_POWER_PERIOD) { + priv->pm_on = 1; + priv->pm_period = prq->value / 1000; + } + /* It's valid to not have a value if we are just toggling + * the flags... Jean II */ + if (!priv->pm_on) { + err = -EINVAL; + goto out; + } + } + + out: + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_ioctl_getpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *prq, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct hermes *hw = &priv->hw; + int err = 0; + u16 enable, period, timeout, mcast; + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFPMENABLED, &enable); + if (err) + goto out; + + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFMAXSLEEPDURATION, &period); + if (err) + goto out; + + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFPMHOLDOVERDURATION, &timeout); + if (err) + goto out; + + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFMULTICASTRECEIVE, &mcast); + if (err) + goto out; + + prq->disabled = !enable; + /* Note : by default, display the period */ + if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + prq->flags = IW_POWER_TIMEOUT; + prq->value = timeout * 1000; + } else { + prq->flags = IW_POWER_PERIOD; + prq->value = period * 1000; + } + if (mcast) + prq->flags |= IW_POWER_ALL_R; + else + prq->flags |= IW_POWER_UNICAST_R; + + out: + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_ioctl_set_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, alg = ext->alg, set_key = 1; + unsigned long flags; + int err = -EINVAL; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + /* Determine and validate the key index */ + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if ((idx < 1) || (idx > 4)) + goto out; + idx--; + } else + idx = priv->tx_key; + + if (encoding->flags & IW_ENCODE_DISABLED) + alg = IW_ENCODE_ALG_NONE; + + if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) { + /* Clear any TKIP TX key we had */ + (void) orinoco_clear_tkip_key(priv, priv->tx_key); + } + + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + priv->tx_key = idx; + set_key = ((alg == IW_ENCODE_ALG_TKIP) || + (ext->key_len > 0)) ? 1 : 0; + } + + if (set_key) { + /* Set the requested key first */ + switch (alg) { + case IW_ENCODE_ALG_NONE: + priv->encode_alg = ORINOCO_ALG_NONE; + err = orinoco_set_key(priv, idx, ORINOCO_ALG_NONE, + NULL, 0, NULL, 0); + break; + + case IW_ENCODE_ALG_WEP: + if (ext->key_len <= 0) + goto out; + + priv->encode_alg = ORINOCO_ALG_WEP; + err = orinoco_set_key(priv, idx, ORINOCO_ALG_WEP, + ext->key, ext->key_len, NULL, 0); + break; + + case IW_ENCODE_ALG_TKIP: + { + u8 *tkip_iv = NULL; + + if (!priv->has_wpa || + (ext->key_len > sizeof(struct orinoco_tkip_key))) + goto out; + + priv->encode_alg = ORINOCO_ALG_TKIP; + + if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) + tkip_iv = &ext->rx_seq[0]; + + err = orinoco_set_key(priv, idx, ORINOCO_ALG_TKIP, + ext->key, ext->key_len, tkip_iv, + ORINOCO_SEQ_LEN); + + err = __orinoco_hw_set_tkip_key(priv, idx, + ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, + priv->keys[idx].key, + tkip_iv, ORINOCO_SEQ_LEN, NULL, 0); + if (err) + printk(KERN_ERR "%s: Error %d setting TKIP key" + "\n", dev->name, err); + + goto out; + } + default: + goto out; + } + } + err = -EINPROGRESS; + out: + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_ioctl_get_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, max_key_len; + unsigned long flags; + int err; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + err = -EINVAL; + max_key_len = encoding->length - sizeof(*ext); + if (max_key_len < 0) + goto out; + + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if ((idx < 1) || (idx > 4)) + goto out; + idx--; + } else + idx = priv->tx_key; + + encoding->flags = idx + 1; + memset(ext, 0, sizeof(*ext)); + + switch (priv->encode_alg) { + case ORINOCO_ALG_NONE: + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + encoding->flags |= IW_ENCODE_DISABLED; + break; + case ORINOCO_ALG_WEP: + ext->alg = IW_ENCODE_ALG_WEP; + ext->key_len = min(priv->keys[idx].key_len, max_key_len); + memcpy(ext->key, priv->keys[idx].key, ext->key_len); + encoding->flags |= IW_ENCODE_ENABLED; + break; + case ORINOCO_ALG_TKIP: + ext->alg = IW_ENCODE_ALG_TKIP; + ext->key_len = min(priv->keys[idx].key_len, max_key_len); + memcpy(ext->key, priv->keys[idx].key, ext->key_len); + encoding->flags |= IW_ENCODE_ENABLED; + break; + } + + err = 0; + out: + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_ioctl_set_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct hermes *hw = &priv->hw; + struct iw_param *param = &wrqu->param; + unsigned long flags; + int ret = -EINPROGRESS; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_PRIVACY_INVOKED: + case IW_AUTH_DROP_UNENCRYPTED: + /* + * orinoco does not use these parameters + */ + break; + + case IW_AUTH_MFP: + /* Management Frame Protection not supported. + * Only fail if set to required. + */ + if (param->value == IW_AUTH_MFP_REQUIRED) + ret = -EINVAL; + break; + + case IW_AUTH_KEY_MGMT: + /* wl_lkm implies value 2 == PSK for Hermes I + * which ties in with WEXT + * no other hints tho :( + */ + priv->key_mgmt = param->value; + break; + + case IW_AUTH_TKIP_COUNTERMEASURES: + /* When countermeasures are enabled, shut down the + * card; when disabled, re-enable the card. This must + * take effect immediately. + * + * TODO: Make sure that the EAPOL message is getting + * out before card disabled + */ + if (param->value) { + priv->tkip_cm_active = 1; + ret = hermes_disable_port(hw, 0); + } else { + priv->tkip_cm_active = 0; + ret = hermes_enable_port(hw, 0); + } + break; + + case IW_AUTH_80211_AUTH_ALG: + if (param->value & IW_AUTH_ALG_SHARED_KEY) + priv->wep_restrict = 1; + else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) + priv->wep_restrict = 0; + else + ret = -EINVAL; + break; + + case IW_AUTH_WPA_ENABLED: + if (priv->has_wpa) { + priv->wpa_enabled = param->value ? 1 : 0; + } else { + if (param->value) + ret = -EOPNOTSUPP; + /* else silently accept disable of WPA */ + priv->wpa_enabled = 0; + } + break; + + default: + ret = -EOPNOTSUPP; + } + + orinoco_unlock(priv, &flags); + return ret; +} + +static int orinoco_ioctl_get_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct iw_param *param = &wrqu->param; + unsigned long flags; + int ret = 0; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_KEY_MGMT: + param->value = priv->key_mgmt; + break; + + case IW_AUTH_TKIP_COUNTERMEASURES: + param->value = priv->tkip_cm_active; + break; + + case IW_AUTH_80211_AUTH_ALG: + if (priv->wep_restrict) + param->value = IW_AUTH_ALG_SHARED_KEY; + else + param->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + + case IW_AUTH_WPA_ENABLED: + param->value = priv->wpa_enabled; + break; + + default: + ret = -EOPNOTSUPP; + } + + orinoco_unlock(priv, &flags); + return ret; +} + +static int orinoco_ioctl_set_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + u8 *buf; + unsigned long flags; + + /* cut off at IEEE80211_MAX_DATA_LEN */ + if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) || + (wrqu->data.length && (extra == NULL))) + return -EINVAL; + + if (wrqu->data.length) { + buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + } else + buf = NULL; + + if (orinoco_lock(priv, &flags) != 0) { + kfree(buf); + return -EBUSY; + } + + kfree(priv->wpa_ie); + priv->wpa_ie = buf; + priv->wpa_ie_len = wrqu->data.length; + + if (priv->wpa_ie) { + /* Looks like wl_lkm wants to check the auth alg, and + * somehow pass it to the firmware. + * Instead it just calls the key mgmt rid + * - we do this in set auth. + */ + } + + orinoco_unlock(priv, &flags); + return 0; +} + +static int orinoco_ioctl_get_genie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + unsigned long flags; + int err = 0; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) { + wrqu->data.length = 0; + goto out; + } + + if (wrqu->data.length < priv->wpa_ie_len) { + err = -E2BIG; + goto out; + } + + wrqu->data.length = priv->wpa_ie_len; + memcpy(extra, priv->wpa_ie, priv->wpa_ie_len); + +out: + orinoco_unlock(priv, &flags); + return err; +} + +static int orinoco_ioctl_set_mlme(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct iw_mlme *mlme = (struct iw_mlme *)extra; + unsigned long flags; + int ret = 0; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + /* silently ignore */ + break; + + case IW_MLME_DISASSOC: + + ret = orinoco_hw_disassociate(priv, mlme->addr.sa_data, + mlme->reason_code); + break; + + default: + ret = -EOPNOTSUPP; + } + + orinoco_unlock(priv, &flags); + return ret; +} + +static int orinoco_ioctl_reset(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) { + printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name); + + /* Firmware reset */ + orinoco_reset(&priv->reset_work); + } else { + printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); + + schedule_work(&priv->reset_work); + } + + return 0; +} + +static int orinoco_ioctl_setibssport(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) + +{ + struct orinoco_private *priv = ndev_priv(dev); + int val = *((int *) extra); + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + priv->ibss_port = val; + + /* Actually update the mode we are using */ + set_port_type(priv); + + orinoco_unlock(priv, &flags); + return -EINPROGRESS; /* Call commit handler */ +} + +static int orinoco_ioctl_getibssport(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + int *val = (int *) extra; + + *val = priv->ibss_port; + return 0; +} + +static int orinoco_ioctl_setport3(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + int val = *((int *) extra); + int err = 0; + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + switch (val) { + case 0: /* Try to do IEEE ad-hoc mode */ + if (!priv->has_ibss) { + err = -EINVAL; + break; + } + priv->prefer_port3 = 0; + + break; + + case 1: /* Try to do Lucent proprietary ad-hoc mode */ + if (!priv->has_port3) { + err = -EINVAL; + break; + } + priv->prefer_port3 = 1; + break; + + default: + err = -EINVAL; + } + + if (!err) { + /* Actually update the mode we are using */ + set_port_type(priv); + err = -EINPROGRESS; + } + + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_ioctl_getport3(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + int *val = (int *) extra; + + *val = priv->prefer_port3; + return 0; +} + +static int orinoco_ioctl_setpreamble(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + unsigned long flags; + int val; + + if (!priv->has_preamble) + return -EOPNOTSUPP; + + /* 802.11b has recently defined some short preamble. + * Basically, the Phy header has been reduced in size. + * This increase performance, especially at high rates + * (the preamble is transmitted at 1Mb/s), unfortunately + * this give compatibility troubles... - Jean II */ + val = *((int *) extra); + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + if (val) + priv->preamble = 1; + else + priv->preamble = 0; + + orinoco_unlock(priv, &flags); + + return -EINPROGRESS; /* Call commit handler */ +} + +static int orinoco_ioctl_getpreamble(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + int *val = (int *) extra; + + if (!priv->has_preamble) + return -EOPNOTSUPP; + + *val = priv->preamble; + return 0; +} + +/* ioctl interface to hermes_read_ltv() + * To use with iwpriv, pass the RID as the token argument, e.g. + * iwpriv get_rid [0xfc00] + * At least Wireless Tools 25 is required to use iwpriv. + * For Wireless Tools 25 and 26 append "dummy" are the end. */ +static int orinoco_ioctl_getrid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + struct hermes *hw = &priv->hw; + int rid = data->flags; + u16 length; + int err; + unsigned long flags; + + /* It's a "get" function, but we don't want users to access the + * WEP key and other raw firmware data */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (rid < 0xfc00 || rid > 0xffff) + return -EINVAL; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + err = hw->ops->read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length, + extra); + if (err) + goto out; + + data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length), + MAX_RID_LEN); + + out: + orinoco_unlock(priv, &flags); + return err; +} + + +/* Commit handler, called after set operations */ +static int orinoco_ioctl_commit(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + struct orinoco_private *priv = ndev_priv(dev); + unsigned long flags; + int err = 0; + + if (!priv->open) + return 0; + + if (orinoco_lock(priv, &flags) != 0) + return err; + + err = orinoco_commit(priv); + + orinoco_unlock(priv, &flags); + return err; +} + +static const struct iw_priv_args orinoco_privtab[] = { + { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, + { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, + { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_port3" }, + { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_port3" }, + { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_preamble" }, + { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_preamble" }, + { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + 0, "set_ibssport" }, + { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_ibssport" }, + { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN, + "get_rid" }, +}; + + +/* + * Structures to export the Wireless Handlers + */ + +static const iw_handler orinoco_handler[] = { + IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)orinoco_ioctl_commit), + IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), + IW_HANDLER(SIOCSIWFREQ, (iw_handler)orinoco_ioctl_setfreq), + IW_HANDLER(SIOCGIWFREQ, (iw_handler)orinoco_ioctl_getfreq), + IW_HANDLER(SIOCSIWMODE, (iw_handler)cfg80211_wext_siwmode), + IW_HANDLER(SIOCGIWMODE, (iw_handler)cfg80211_wext_giwmode), + IW_HANDLER(SIOCSIWSENS, (iw_handler)orinoco_ioctl_setsens), + IW_HANDLER(SIOCGIWSENS, (iw_handler)orinoco_ioctl_getsens), + IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWAP, (iw_handler)orinoco_ioctl_setwap), + IW_HANDLER(SIOCGIWAP, (iw_handler)orinoco_ioctl_getwap), + IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan), + IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan), + IW_HANDLER(SIOCSIWESSID, (iw_handler)orinoco_ioctl_setessid), + IW_HANDLER(SIOCGIWESSID, (iw_handler)orinoco_ioctl_getessid), + IW_HANDLER(SIOCSIWRATE, (iw_handler)orinoco_ioctl_setrate), + IW_HANDLER(SIOCGIWRATE, (iw_handler)orinoco_ioctl_getrate), + IW_HANDLER(SIOCSIWRTS, (iw_handler)cfg80211_wext_siwrts), + IW_HANDLER(SIOCGIWRTS, (iw_handler)cfg80211_wext_giwrts), + IW_HANDLER(SIOCSIWFRAG, (iw_handler)cfg80211_wext_siwfrag), + IW_HANDLER(SIOCGIWFRAG, (iw_handler)cfg80211_wext_giwfrag), + IW_HANDLER(SIOCGIWRETRY, (iw_handler)cfg80211_wext_giwretry), + IW_HANDLER(SIOCSIWENCODE, (iw_handler)orinoco_ioctl_setiwencode), + IW_HANDLER(SIOCGIWENCODE, (iw_handler)orinoco_ioctl_getiwencode), + IW_HANDLER(SIOCSIWPOWER, (iw_handler)orinoco_ioctl_setpower), + IW_HANDLER(SIOCGIWPOWER, (iw_handler)orinoco_ioctl_getpower), + IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), + IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), + IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), + IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), + IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), + IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), +}; + + +/* + Added typecasting since we no longer use iwreq_data -- Moustafa + */ +static const iw_handler orinoco_private_handler[] = { + [0] = (iw_handler)orinoco_ioctl_reset, + [1] = (iw_handler)orinoco_ioctl_reset, + [2] = (iw_handler)orinoco_ioctl_setport3, + [3] = (iw_handler)orinoco_ioctl_getport3, + [4] = (iw_handler)orinoco_ioctl_setpreamble, + [5] = (iw_handler)orinoco_ioctl_getpreamble, + [6] = (iw_handler)orinoco_ioctl_setibssport, + [7] = (iw_handler)orinoco_ioctl_getibssport, + [9] = (iw_handler)orinoco_ioctl_getrid, +}; + +const struct iw_handler_def orinoco_handler_def = { + .num_standard = ARRAY_SIZE(orinoco_handler), + .num_private = ARRAY_SIZE(orinoco_private_handler), + .num_private_args = ARRAY_SIZE(orinoco_privtab), + .standard = orinoco_handler, + .private = orinoco_private_handler, + .private_args = orinoco_privtab, + .get_wireless_stats = orinoco_get_wireless_stats, +}; diff --git a/drivers/net/wireless/intersil/orinoco/wext.h b/drivers/net/wireless/intersil/orinoco/wext.h new file mode 100644 index 000000000000..1479f4e26dde --- /dev/null +++ b/drivers/net/wireless/intersil/orinoco/wext.h @@ -0,0 +1,13 @@ +/* Wireless extensions support. + * + * See copyright notice in main.c + */ +#ifndef _ORINOCO_WEXT_H_ +#define _ORINOCO_WEXT_H_ + +#include + +/* Structure defining all our WEXT handlers */ +extern const struct iw_handler_def orinoco_handler_def; + +#endif /* _ORINOCO_WEXT_H_ */ diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig deleted file mode 100644 index f6fa3f4e294f..000000000000 --- a/drivers/net/wireless/orinoco/Kconfig +++ /dev/null @@ -1,142 +0,0 @@ -config HERMES - tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" - depends on (PPC_PMAC || PCI || PCMCIA) - depends on CFG80211 - select CFG80211_WEXT_EXPORT - select WIRELESS_EXT - select WEXT_SPY - select WEXT_PRIV - select FW_LOADER - select CRYPTO - select CRYPTO_MICHAEL_MIC - ---help--- - A driver for 802.11b wireless cards based on the "Hermes" or - Intersil HFA384x (Prism 2) MAC controller. This includes the vast - majority of the PCMCIA 802.11b cards (which are nearly all rebadges) - - except for the Cisco/Aironet cards. Cards supported include the - Apple Airport (not a PCMCIA card), WavelanIEEE/Orinoco, - Cabletron/EnteraSys Roamabout, ELSA AirLancer, MELCO Buffalo, Avaya, - IBM High Rate Wireless, Farralon Syyline, Samsung MagicLAN, Netgear - MA401, LinkSys WPC-11, D-Link DWL-650, 3Com AirConnect, Intel - IPW2011, and Symbol Spectrum24 High Rate amongst others. - - This option includes the guts of the driver, but in order to - actually use a card you will also need to enable support for PCMCIA - Hermes cards, PLX9052 based PCI adaptors or the Apple Airport below. - - You will also very likely also need the Wireless Tools in order to - configure your card and that /etc/pcmcia/wireless.opts works : - - -config HERMES_PRISM - bool "Support Prism 2/2.5 chipset" - depends on HERMES - ---help--- - - Say Y to enable support for Prism 2 and 2.5 chipsets. These - chipsets are better handled by the hostap driver. This driver - would not support WPA or firmware download for Prism chipset. - - If you are not sure, say N. - -config HERMES_CACHE_FW_ON_INIT - bool "Cache Hermes firmware on driver initialisation" - depends on HERMES - default y - ---help--- - Say Y to cache any firmware required by the Hermes drivers - on startup. The firmware will remain cached until the - driver is unloaded. The cache uses 64K of RAM. - - Otherwise load the firmware from userspace as required. In - this case the driver should be unloaded and restarted - whenever the firmware is changed. - - If you are not sure, say Y. - -config APPLE_AIRPORT - tristate "Apple Airport support (built-in)" - depends on PPC_PMAC && HERMES - help - Say Y here to support the Airport 802.11b wireless Ethernet hardware - built into the Macintosh iBook and other recent PowerPC-based - Macintosh machines. This is essentially a Lucent Orinoco card with - a non-standard interface. - - This driver does not support the Airport Extreme (802.11b/g). Use - the BCM43xx driver for Airport Extreme cards. - -config PLX_HERMES - tristate "Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.)" - depends on PCI && HERMES - help - Enable support for PCMCIA cards supported by the "Hermes" (aka - orinoco) driver when used in PLX9052 based PCI adaptors. These - adaptors are not a full PCMCIA controller but act as a more limited - PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that - 802.11b PCMCIA cards can be used in desktop machines. The Netgear - MA301 is such an adaptor. - -config TMD_HERMES - tristate "Hermes in TMD7160 based PCI adaptor support" - depends on PCI && HERMES - help - Enable support for PCMCIA cards supported by the "Hermes" (aka - orinoco) driver when used in TMD7160 based PCI adaptors. These - adaptors are not a full PCMCIA controller but act as a more limited - PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that - 802.11b PCMCIA cards can be used in desktop machines. - -config NORTEL_HERMES - tristate "Nortel emobility PCI adaptor support" - depends on PCI && HERMES - help - Enable support for PCMCIA cards supported by the "Hermes" (aka - orinoco) driver when used in Nortel emobility PCI adaptors. These - adaptors are not full PCMCIA controllers, but act as a more limited - PCI <-> PCMCIA bridge. - -config PCI_HERMES - tristate "Prism 2.5 PCI 802.11b adaptor support" - depends on PCI && HERMES && HERMES_PRISM - help - Enable support for PCI and mini-PCI 802.11b wireless NICs based on - the Prism 2.5 chipset. These are true PCI cards, not the 802.11b - PCMCIA cards bundled with PCI<->PCMCIA adaptors which are also - common. Some of the built-in wireless adaptors in laptops are of - this variety. - -config PCMCIA_HERMES - tristate "Hermes PCMCIA card support" - depends on PCMCIA && HERMES && HAS_IOPORT_MAP - ---help--- - A driver for "Hermes" chipset based PCMCIA wireless adaptors, such - as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ - EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and - others). It should also be usable on various Prism II based cards - such as the Linksys, D-Link and Farallon Skyline. It should also - work on Symbol cards such as the 3Com AirConnect and Ericsson WLAN. - - You will very likely need the Wireless Tools in order to - configure your card and that /etc/pcmcia/wireless.opts works: - . - -config PCMCIA_SPECTRUM - tristate "Symbol Spectrum24 Trilogy PCMCIA card support" - depends on PCMCIA && HERMES && HAS_IOPORT_MAP - ---help--- - - This is a driver for 802.11b cards using RAM-loadable Symbol - firmware, such as Symbol Wireless Networker LA4100, CompactFlash - cards by Socket Communications and Intel PRO/Wireless 2011B. - - This driver requires firmware download on startup. Utilities - for downloading Symbol firmware are available at - - -config ORINOCO_USB - tristate "Agere Orinoco USB support" - depends on USB && HERMES - select FW_LOADER - ---help--- - This driver is for USB versions of the Agere Orinoco card. diff --git a/drivers/net/wireless/orinoco/Makefile b/drivers/net/wireless/orinoco/Makefile deleted file mode 100644 index bfdefb85abcd..000000000000 --- a/drivers/net/wireless/orinoco/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# -# Makefile for the orinoco wireless device drivers. -# -orinoco-objs := main.o fw.o hw.o mic.o scan.o wext.o hermes_dld.o hermes.o cfg.o - -obj-$(CONFIG_HERMES) += orinoco.o -obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o -obj-$(CONFIG_APPLE_AIRPORT) += airport.o -obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o -obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o -obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o -obj-$(CONFIG_NORTEL_HERMES) += orinoco_nortel.o -obj-$(CONFIG_PCMCIA_SPECTRUM) += spectrum_cs.o -obj-$(CONFIG_ORINOCO_USB) += orinoco_usb.o - -# Orinoco should be endian clean. -ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/orinoco/airport.c b/drivers/net/wireless/orinoco/airport.c deleted file mode 100644 index 77e6c53040a3..000000000000 --- a/drivers/net/wireless/orinoco/airport.c +++ /dev/null @@ -1,267 +0,0 @@ -/* airport.c - * - * A driver for "Hermes" chipset based Apple Airport wireless - * card. - * - * Copyright notice & release notes in file main.c - * - * Note specific to airport stub: - * - * 0.05 : first version of the new split driver - * 0.06 : fix possible hang on powerup, add sleep support - */ - -#define DRIVER_NAME "airport" -#define PFX DRIVER_NAME ": " - -#include -#include -#include -#include -#include - -#include "orinoco.h" - -#define AIRPORT_IO_LEN (0x1000) /* one page */ - -struct airport { - struct macio_dev *mdev; - void __iomem *vaddr; - unsigned int irq; - int irq_requested; - int ndev_registered; -}; - -static int -airport_suspend(struct macio_dev *mdev, pm_message_t state) -{ - struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); - struct net_device *dev = priv->ndev; - struct airport *card = priv->card; - unsigned long flags; - int err; - - printk(KERN_DEBUG "%s: Airport entering sleep mode\n", dev->name); - - err = orinoco_lock(priv, &flags); - if (err) { - printk(KERN_ERR "%s: hw_unavailable on PBOOK_SLEEP_NOW\n", - dev->name); - return 0; - } - - orinoco_down(priv); - orinoco_unlock(priv, &flags); - - disable_irq(card->irq); - pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, - macio_get_of_node(mdev), 0, 0); - - return 0; -} - -static int -airport_resume(struct macio_dev *mdev) -{ - struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); - struct net_device *dev = priv->ndev; - struct airport *card = priv->card; - unsigned long flags; - int err; - - printk(KERN_DEBUG "%s: Airport waking up\n", dev->name); - - pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, - macio_get_of_node(mdev), 0, 1); - msleep(200); - - enable_irq(card->irq); - - priv->hw.ops->lock_irqsave(&priv->lock, &flags); - err = orinoco_up(priv); - priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); - - return err; -} - -static int -airport_detach(struct macio_dev *mdev) -{ - struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); - struct airport *card = priv->card; - - if (card->ndev_registered) - orinoco_if_del(priv); - card->ndev_registered = 0; - - if (card->irq_requested) - free_irq(card->irq, priv); - card->irq_requested = 0; - - if (card->vaddr) - iounmap(card->vaddr); - card->vaddr = NULL; - - macio_release_resource(mdev, 0); - - pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, - macio_get_of_node(mdev), 0, 0); - ssleep(1); - - macio_set_drvdata(mdev, NULL); - free_orinocodev(priv); - - return 0; -} - -static int airport_hard_reset(struct orinoco_private *priv) -{ - /* It would be nice to power cycle the Airport for a real hard - * reset, but for some reason although it appears to - * re-initialize properly, it falls in a screaming heap - * shortly afterwards. */ -#if 0 - struct airport *card = priv->card; - - /* Vitally important. If we don't do this it seems we get an - * interrupt somewhere during the power cycle, since - * hw_unavailable is already set it doesn't get ACKed, we get - * into an interrupt loop and the PMU decides to turn us - * off. */ - disable_irq(card->irq); - - pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, - macio_get_of_node(card->mdev), 0, 0); - ssleep(1); - pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, - macio_get_of_node(card->mdev), 0, 1); - ssleep(1); - - enable_irq(card->irq); - ssleep(1); -#endif - - return 0; -} - -static int -airport_attach(struct macio_dev *mdev, const struct of_device_id *match) -{ - struct orinoco_private *priv; - struct airport *card; - unsigned long phys_addr; - struct hermes *hw; - - if (macio_resource_count(mdev) < 1 || macio_irq_count(mdev) < 1) { - printk(KERN_ERR PFX "Wrong interrupt/addresses in OF tree\n"); - return -ENODEV; - } - - /* Allocate space for private device-specific data */ - priv = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev, - airport_hard_reset, NULL); - if (!priv) { - printk(KERN_ERR PFX "Cannot allocate network device\n"); - return -ENODEV; - } - card = priv->card; - - hw = &priv->hw; - card->mdev = mdev; - - if (macio_request_resource(mdev, 0, DRIVER_NAME)) { - printk(KERN_ERR PFX "can't request IO resource !\n"); - free_orinocodev(priv); - return -EBUSY; - } - - macio_set_drvdata(mdev, priv); - - /* Setup interrupts & base address */ - card->irq = macio_irq(mdev, 0); - phys_addr = macio_resource_start(mdev, 0); /* Physical address */ - printk(KERN_DEBUG PFX "Physical address %lx\n", phys_addr); - card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN); - if (!card->vaddr) { - printk(KERN_ERR PFX "ioremap() failed\n"); - goto failed; - } - - hermes_struct_init(hw, card->vaddr, HERMES_16BIT_REGSPACING); - - /* Power up card */ - pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, - macio_get_of_node(mdev), 0, 1); - ssleep(1); - - /* Reset it before we get the interrupt */ - hw->ops->init(hw); - - if (request_irq(card->irq, orinoco_interrupt, 0, DRIVER_NAME, priv)) { - printk(KERN_ERR PFX "Couldn't get IRQ %d\n", card->irq); - goto failed; - } - card->irq_requested = 1; - - /* Initialise the main driver */ - if (orinoco_init(priv) != 0) { - printk(KERN_ERR PFX "orinoco_init() failed\n"); - goto failed; - } - - /* Register an interface with the stack */ - if (orinoco_if_add(priv, phys_addr, card->irq, NULL) != 0) { - printk(KERN_ERR PFX "orinoco_if_add() failed\n"); - goto failed; - } - card->ndev_registered = 1; - return 0; - failed: - airport_detach(mdev); - return -ENODEV; -} /* airport_attach */ - - -static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION - " (Benjamin Herrenschmidt )"; -MODULE_AUTHOR("Benjamin Herrenschmidt "); -MODULE_DESCRIPTION("Driver for the Apple Airport wireless card."); -MODULE_LICENSE("Dual MPL/GPL"); - -static const struct of_device_id airport_match[] = { - { - .name = "radio", - }, - {}, -}; - -MODULE_DEVICE_TABLE(of, airport_match); - -static struct macio_driver airport_driver = { - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - .of_match_table = airport_match, - }, - .probe = airport_attach, - .remove = airport_detach, - .suspend = airport_suspend, - .resume = airport_resume, -}; - -static int __init -init_airport(void) -{ - printk(KERN_DEBUG "%s\n", version); - - return macio_register_driver(&airport_driver); -} - -static void __exit -exit_airport(void) -{ - macio_unregister_driver(&airport_driver); -} - -module_init(init_airport); -module_exit(exit_airport); diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c deleted file mode 100644 index 0f6ea316e38e..000000000000 --- a/drivers/net/wireless/orinoco/cfg.c +++ /dev/null @@ -1,291 +0,0 @@ -/* cfg80211 support - * - * See copyright notice in main.c - */ -#include -#include -#include "hw.h" -#include "main.h" -#include "orinoco.h" - -#include "cfg.h" - -/* Supported bitrates. Must agree with hw.c */ -static struct ieee80211_rate orinoco_rates[] = { - { .bitrate = 10 }, - { .bitrate = 20 }, - { .bitrate = 55 }, - { .bitrate = 110 }, -}; - -static const void * const orinoco_wiphy_privid = &orinoco_wiphy_privid; - -/* Called after orinoco_private is allocated. */ -void orinoco_wiphy_init(struct wiphy *wiphy) -{ - struct orinoco_private *priv = wiphy_priv(wiphy); - - wiphy->privid = orinoco_wiphy_privid; - - set_wiphy_dev(wiphy, priv->dev); -} - -/* Called after firmware is initialised */ -int orinoco_wiphy_register(struct wiphy *wiphy) -{ - struct orinoco_private *priv = wiphy_priv(wiphy); - int i, channels = 0; - - if (priv->firmware_type == FIRMWARE_TYPE_AGERE) - wiphy->max_scan_ssids = 1; - else - wiphy->max_scan_ssids = 0; - - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - - /* TODO: should we set if we only have demo ad-hoc? - * (priv->has_port3) - */ - if (priv->has_ibss) - wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); - - if (!priv->broken_monitor || force_monitor) - wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); - - priv->band.bitrates = orinoco_rates; - priv->band.n_bitrates = ARRAY_SIZE(orinoco_rates); - - /* Only support channels allowed by the card EEPROM */ - for (i = 0; i < NUM_CHANNELS; i++) { - if (priv->channel_mask & (1 << i)) { - priv->channels[i].center_freq = - ieee80211_channel_to_frequency(i + 1, - IEEE80211_BAND_2GHZ); - channels++; - } - } - priv->band.channels = priv->channels; - priv->band.n_channels = channels; - - wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - - i = 0; - if (priv->has_wep) { - priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP40; - i++; - - if (priv->has_big_wep) { - priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP104; - i++; - } - } - if (priv->has_wpa) { - priv->cipher_suites[i] = WLAN_CIPHER_SUITE_TKIP; - i++; - } - wiphy->cipher_suites = priv->cipher_suites; - wiphy->n_cipher_suites = i; - - wiphy->rts_threshold = priv->rts_thresh; - if (!priv->has_mwo) - wiphy->frag_threshold = priv->frag_thresh + 1; - wiphy->retry_short = priv->short_retry_limit; - wiphy->retry_long = priv->long_retry_limit; - - return wiphy_register(wiphy); -} - -static int orinoco_change_vif(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct orinoco_private *priv = wiphy_priv(wiphy); - int err = 0; - unsigned long lock; - - if (orinoco_lock(priv, &lock) != 0) - return -EBUSY; - - switch (type) { - case NL80211_IFTYPE_ADHOC: - if (!priv->has_ibss && !priv->has_port3) - err = -EINVAL; - break; - - case NL80211_IFTYPE_STATION: - break; - - case NL80211_IFTYPE_MONITOR: - if (priv->broken_monitor && !force_monitor) { - wiphy_warn(wiphy, - "Monitor mode support is buggy in this firmware, not enabling\n"); - err = -EINVAL; - } - break; - - default: - err = -EINVAL; - } - - if (!err) { - priv->iw_mode = type; - set_port_type(priv); - err = orinoco_commit(priv); - } - - orinoco_unlock(priv, &lock); - - return err; -} - -static int orinoco_scan(struct wiphy *wiphy, - struct cfg80211_scan_request *request) -{ - struct orinoco_private *priv = wiphy_priv(wiphy); - int err; - - if (!request) - return -EINVAL; - - if (priv->scan_request && priv->scan_request != request) - return -EBUSY; - - priv->scan_request = request; - - err = orinoco_hw_trigger_scan(priv, request->ssids); - /* On error the we aren't processing the request */ - if (err) - priv->scan_request = NULL; - - return err; -} - -static int orinoco_set_monitor_channel(struct wiphy *wiphy, - struct cfg80211_chan_def *chandef) -{ - struct orinoco_private *priv = wiphy_priv(wiphy); - int err = 0; - unsigned long flags; - int channel; - - if (!chandef->chan) - return -EINVAL; - - if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) - return -EINVAL; - - if (chandef->chan->band != IEEE80211_BAND_2GHZ) - return -EINVAL; - - channel = ieee80211_frequency_to_channel(chandef->chan->center_freq); - - if ((channel < 1) || (channel > NUM_CHANNELS) || - !(priv->channel_mask & (1 << (channel - 1)))) - return -EINVAL; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - priv->channel = channel; - if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { - /* Fast channel change - no commit if successful */ - struct hermes *hw = &priv->hw; - err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | - HERMES_TEST_SET_CHANNEL, - channel, NULL); - } - orinoco_unlock(priv, &flags); - - return err; -} - -static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed) -{ - struct orinoco_private *priv = wiphy_priv(wiphy); - int frag_value = -1; - int rts_value = -1; - int err = 0; - - if (changed & WIPHY_PARAM_RETRY_SHORT) { - /* Setting short retry not supported */ - err = -EINVAL; - } - - if (changed & WIPHY_PARAM_RETRY_LONG) { - /* Setting long retry not supported */ - err = -EINVAL; - } - - if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { - /* Set fragmentation */ - if (priv->has_mwo) { - if (wiphy->frag_threshold == -1) - frag_value = 0; - else { - printk(KERN_WARNING "%s: Fixed fragmentation " - "is not supported on this firmware. " - "Using MWO robust instead.\n", - priv->ndev->name); - frag_value = 1; - } - } else { - if (wiphy->frag_threshold == -1) - frag_value = 2346; - else if ((wiphy->frag_threshold < 257) || - (wiphy->frag_threshold > 2347)) - err = -EINVAL; - else - /* cfg80211 value is 257-2347 (odd only) - * orinoco rid has range 256-2346 (even only) */ - frag_value = wiphy->frag_threshold & ~0x1; - } - } - - if (changed & WIPHY_PARAM_RTS_THRESHOLD) { - /* Set RTS. - * - * Prism documentation suggests default of 2432, - * and a range of 0-3000. - * - * Current implementation uses 2347 as the default and - * the upper limit. - */ - - if (wiphy->rts_threshold == -1) - rts_value = 2347; - else if (wiphy->rts_threshold > 2347) - err = -EINVAL; - else - rts_value = wiphy->rts_threshold; - } - - if (!err) { - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - if (frag_value >= 0) { - if (priv->has_mwo) - priv->mwo_robust = frag_value; - else - priv->frag_thresh = frag_value; - } - if (rts_value >= 0) - priv->rts_thresh = rts_value; - - err = orinoco_commit(priv); - - orinoco_unlock(priv, &flags); - } - - return err; -} - -const struct cfg80211_ops orinoco_cfg_ops = { - .change_virtual_intf = orinoco_change_vif, - .set_monitor_channel = orinoco_set_monitor_channel, - .scan = orinoco_scan, - .set_wiphy_params = orinoco_set_wiphy_params, -}; diff --git a/drivers/net/wireless/orinoco/cfg.h b/drivers/net/wireless/orinoco/cfg.h deleted file mode 100644 index 3ddc96a06cd7..000000000000 --- a/drivers/net/wireless/orinoco/cfg.h +++ /dev/null @@ -1,15 +0,0 @@ -/* cfg80211 support. - * - * See copyright notice in main.c - */ -#ifndef ORINOCO_CFG_H -#define ORINOCO_CFG_H - -#include - -extern const struct cfg80211_ops orinoco_cfg_ops; - -void orinoco_wiphy_init(struct wiphy *wiphy); -int orinoco_wiphy_register(struct wiphy *wiphy); - -#endif /* ORINOCO_CFG_H */ diff --git a/drivers/net/wireless/orinoco/fw.c b/drivers/net/wireless/orinoco/fw.c deleted file mode 100644 index 400a35217644..000000000000 --- a/drivers/net/wireless/orinoco/fw.c +++ /dev/null @@ -1,387 +0,0 @@ -/* Firmware file reading and download helpers - * - * See copyright notice in main.c - */ -#include -#include -#include -#include -#include - -#include "hermes.h" -#include "hermes_dld.h" -#include "orinoco.h" - -#include "fw.h" - -/* End markers (for Symbol firmware only) */ -#define TEXT_END 0x1A /* End of text header */ - -struct fw_info { - char *pri_fw; - char *sta_fw; - char *ap_fw; - u32 pda_addr; - u16 pda_size; -}; - -static const struct fw_info orinoco_fw[] = { - { NULL, "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 }, - { NULL, "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 }, - { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", NULL, 0x00003100, 512 } -}; -MODULE_FIRMWARE("agere_sta_fw.bin"); -MODULE_FIRMWARE("agere_ap_fw.bin"); -MODULE_FIRMWARE("prism_sta_fw.bin"); -MODULE_FIRMWARE("prism_ap_fw.bin"); -MODULE_FIRMWARE("symbol_sp24t_prim_fw"); -MODULE_FIRMWARE("symbol_sp24t_sec_fw"); - -/* Structure used to access fields in FW - * Make sure LE decoding macros are used - */ -struct orinoco_fw_header { - char hdr_vers[6]; /* ASCII string for header version */ - __le16 headersize; /* Total length of header */ - __le32 entry_point; /* NIC entry point */ - __le32 blocks; /* Number of blocks to program */ - __le32 block_offset; /* Offset of block data from eof header */ - __le32 pdr_offset; /* Offset to PDR data from eof header */ - __le32 pri_offset; /* Offset to primary plug data */ - __le32 compat_offset; /* Offset to compatibility data*/ - char signature[0]; /* FW signature length headersize-20 */ -} __packed; - -/* Check the range of various header entries. Return a pointer to a - * description of the problem, or NULL if everything checks out. */ -static const char *validate_fw(const struct orinoco_fw_header *hdr, size_t len) -{ - u16 hdrsize; - - if (len < sizeof(*hdr)) - return "image too small"; - if (memcmp(hdr->hdr_vers, "HFW", 3) != 0) - return "format not recognised"; - - hdrsize = le16_to_cpu(hdr->headersize); - if (hdrsize > len) - return "bad headersize"; - if ((hdrsize + le32_to_cpu(hdr->block_offset)) > len) - return "bad block offset"; - if ((hdrsize + le32_to_cpu(hdr->pdr_offset)) > len) - return "bad PDR offset"; - if ((hdrsize + le32_to_cpu(hdr->pri_offset)) > len) - return "bad PRI offset"; - if ((hdrsize + le32_to_cpu(hdr->compat_offset)) > len) - return "bad compat offset"; - - /* TODO: consider adding a checksum or CRC to the firmware format */ - return NULL; -} - -#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) -static inline const struct firmware * -orinoco_cached_fw_get(struct orinoco_private *priv, bool primary) -{ - if (primary) - return priv->cached_pri_fw; - else - return priv->cached_fw; -} -#else -#define orinoco_cached_fw_get(priv, primary) (NULL) -#endif - -/* Download either STA or AP firmware into the card. */ -static int -orinoco_dl_firmware(struct orinoco_private *priv, - const struct fw_info *fw, - int ap) -{ - /* Plug Data Area (PDA) */ - __le16 *pda; - - struct hermes *hw = &priv->hw; - const struct firmware *fw_entry; - const struct orinoco_fw_header *hdr; - const unsigned char *first_block; - const void *end; - const char *firmware; - const char *fw_err; - struct device *dev = priv->dev; - int err = 0; - - pda = kzalloc(fw->pda_size, GFP_KERNEL); - if (!pda) - return -ENOMEM; - - if (ap) - firmware = fw->ap_fw; - else - firmware = fw->sta_fw; - - dev_dbg(dev, "Attempting to download firmware %s\n", firmware); - - /* Read current plug data */ - err = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size); - dev_dbg(dev, "Read PDA returned %d\n", err); - if (err) - goto free; - - if (!orinoco_cached_fw_get(priv, false)) { - err = request_firmware(&fw_entry, firmware, priv->dev); - - if (err) { - dev_err(dev, "Cannot find firmware %s\n", firmware); - err = -ENOENT; - goto free; - } - } else - fw_entry = orinoco_cached_fw_get(priv, false); - - hdr = (const struct orinoco_fw_header *) fw_entry->data; - - fw_err = validate_fw(hdr, fw_entry->size); - if (fw_err) { - dev_warn(dev, "Invalid firmware image detected (%s). " - "Aborting download\n", fw_err); - err = -EINVAL; - goto abort; - } - - /* Enable aux port to allow programming */ - err = hw->ops->program_init(hw, le32_to_cpu(hdr->entry_point)); - dev_dbg(dev, "Program init returned %d\n", err); - if (err != 0) - goto abort; - - /* Program data */ - first_block = (fw_entry->data + - le16_to_cpu(hdr->headersize) + - le32_to_cpu(hdr->block_offset)); - end = fw_entry->data + fw_entry->size; - - err = hermes_program(hw, first_block, end); - dev_dbg(dev, "Program returned %d\n", err); - if (err != 0) - goto abort; - - /* Update production data */ - first_block = (fw_entry->data + - le16_to_cpu(hdr->headersize) + - le32_to_cpu(hdr->pdr_offset)); - - err = hermes_apply_pda_with_defaults(hw, first_block, end, pda, - &pda[fw->pda_size / sizeof(*pda)]); - dev_dbg(dev, "Apply PDA returned %d\n", err); - if (err) - goto abort; - - /* Tell card we've finished */ - err = hw->ops->program_end(hw); - dev_dbg(dev, "Program end returned %d\n", err); - if (err != 0) - goto abort; - - /* Check if we're running */ - dev_dbg(dev, "hermes_present returned %d\n", hermes_present(hw)); - -abort: - /* If we requested the firmware, release it. */ - if (!orinoco_cached_fw_get(priv, false)) - release_firmware(fw_entry); - -free: - kfree(pda); - return err; -} - -/* - * Process a firmware image - stop the card, load the firmware, reset - * the card and make sure it responds. For the secondary firmware take - * care of the PDA - read it and then write it on top of the firmware. - */ -static int -symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw, - const unsigned char *image, const void *end, - int secondary) -{ - struct hermes *hw = &priv->hw; - int ret = 0; - const unsigned char *ptr; - const unsigned char *first_block; - - /* Plug Data Area (PDA) */ - __le16 *pda = NULL; - - /* Binary block begins after the 0x1A marker */ - ptr = image; - while (*ptr++ != TEXT_END); - first_block = ptr; - - /* Read the PDA from EEPROM */ - if (secondary) { - pda = kzalloc(fw->pda_size, GFP_KERNEL); - if (!pda) - return -ENOMEM; - - ret = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size); - if (ret) - goto free; - } - - /* Stop the firmware, so that it can be safely rewritten */ - if (priv->stop_fw) { - ret = priv->stop_fw(priv, 1); - if (ret) - goto free; - } - - /* Program the adapter with new firmware */ - ret = hermes_program(hw, first_block, end); - if (ret) - goto free; - - /* Write the PDA to the adapter */ - if (secondary) { - size_t len = hermes_blocks_length(first_block, end); - ptr = first_block + len; - ret = hermes_apply_pda(hw, ptr, end, pda, - &pda[fw->pda_size / sizeof(*pda)]); - kfree(pda); - if (ret) - return ret; - } - - /* Run the firmware */ - if (priv->stop_fw) { - ret = priv->stop_fw(priv, 0); - if (ret) - return ret; - } - - /* Reset hermes chip and make sure it responds */ - ret = hw->ops->init(hw); - - /* hermes_reset() should return 0 with the secondary firmware */ - if (secondary && ret != 0) - return -ENODEV; - - /* And this should work with any firmware */ - if (!hermes_present(hw)) - return -ENODEV; - - return 0; - -free: - kfree(pda); - return ret; -} - - -/* - * Download the firmware into the card, this also does a PCMCIA soft - * reset on the card, to make sure it's in a sane state. - */ -static int -symbol_dl_firmware(struct orinoco_private *priv, - const struct fw_info *fw) -{ - struct device *dev = priv->dev; - int ret; - const struct firmware *fw_entry; - - if (!orinoco_cached_fw_get(priv, true)) { - if (request_firmware(&fw_entry, fw->pri_fw, priv->dev) != 0) { - dev_err(dev, "Cannot find firmware: %s\n", fw->pri_fw); - return -ENOENT; - } - } else - fw_entry = orinoco_cached_fw_get(priv, true); - - /* Load primary firmware */ - ret = symbol_dl_image(priv, fw, fw_entry->data, - fw_entry->data + fw_entry->size, 0); - - if (!orinoco_cached_fw_get(priv, true)) - release_firmware(fw_entry); - if (ret) { - dev_err(dev, "Primary firmware download failed\n"); - return ret; - } - - if (!orinoco_cached_fw_get(priv, false)) { - if (request_firmware(&fw_entry, fw->sta_fw, priv->dev) != 0) { - dev_err(dev, "Cannot find firmware: %s\n", fw->sta_fw); - return -ENOENT; - } - } else - fw_entry = orinoco_cached_fw_get(priv, false); - - /* Load secondary firmware */ - ret = symbol_dl_image(priv, fw, fw_entry->data, - fw_entry->data + fw_entry->size, 1); - if (!orinoco_cached_fw_get(priv, false)) - release_firmware(fw_entry); - if (ret) - dev_err(dev, "Secondary firmware download failed\n"); - - return ret; -} - -int orinoco_download(struct orinoco_private *priv) -{ - int err = 0; - /* Reload firmware */ - switch (priv->firmware_type) { - case FIRMWARE_TYPE_AGERE: - /* case FIRMWARE_TYPE_INTERSIL: */ - err = orinoco_dl_firmware(priv, - &orinoco_fw[priv->firmware_type], 0); - break; - - case FIRMWARE_TYPE_SYMBOL: - err = symbol_dl_firmware(priv, - &orinoco_fw[priv->firmware_type]); - break; - case FIRMWARE_TYPE_INTERSIL: - break; - } - /* TODO: if we fail we probably need to reinitialise - * the driver */ - - return err; -} - -#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) -void orinoco_cache_fw(struct orinoco_private *priv, int ap) -{ - const struct firmware *fw_entry = NULL; - const char *pri_fw; - const char *fw; - - pri_fw = orinoco_fw[priv->firmware_type].pri_fw; - if (ap) - fw = orinoco_fw[priv->firmware_type].ap_fw; - else - fw = orinoco_fw[priv->firmware_type].sta_fw; - - if (pri_fw) { - if (request_firmware(&fw_entry, pri_fw, priv->dev) == 0) - priv->cached_pri_fw = fw_entry; - } - - if (fw) { - if (request_firmware(&fw_entry, fw, priv->dev) == 0) - priv->cached_fw = fw_entry; - } -} - -void orinoco_uncache_fw(struct orinoco_private *priv) -{ - release_firmware(priv->cached_pri_fw); - release_firmware(priv->cached_fw); - priv->cached_pri_fw = NULL; - priv->cached_fw = NULL; -} -#endif diff --git a/drivers/net/wireless/orinoco/fw.h b/drivers/net/wireless/orinoco/fw.h deleted file mode 100644 index aca63e3c4b5b..000000000000 --- a/drivers/net/wireless/orinoco/fw.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Firmware file reading and download helpers - * - * See copyright notice in main.c - */ -#ifndef _ORINOCO_FW_H_ -#define _ORINOCO_FW_H_ - -/* Forward declations */ -struct orinoco_private; - -int orinoco_download(struct orinoco_private *priv); - -#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) -void orinoco_cache_fw(struct orinoco_private *priv, int ap); -void orinoco_uncache_fw(struct orinoco_private *priv); -#else -#define orinoco_cache_fw(priv, ap) do { } while (0) -#define orinoco_uncache_fw(priv) do { } while (0) -#endif - -#endif /* _ORINOCO_FW_H_ */ diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/orinoco/hermes.c deleted file mode 100644 index 43790fbea0e0..000000000000 --- a/drivers/net/wireless/orinoco/hermes.c +++ /dev/null @@ -1,777 +0,0 @@ -/* hermes.c - * - * Driver core for the "Hermes" wireless MAC controller, as used in - * the Lucent Orinoco and Cabletron RoamAbout cards. It should also - * work on the hfa3841 and hfa3842 MAC controller chips used in the - * Prism II chipsets. - * - * This is not a complete driver, just low-level access routines for - * the MAC controller itself. - * - * Based on the prism2 driver from Absolute Value Systems' linux-wlan - * project, the Linux wvlan_cs driver, Lucent's HCF-Light - * (wvlan_hcf.c) library, and the NetBSD wireless driver (in no - * particular order). - * - * Copyright (C) 2000, David Gibson, Linuxcare Australia. - * (C) Copyright David Gibson, IBM Corp. 2001-2003. - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License - * at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and - * limitations under the License. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License version 2 (the "GPL"), in - * which case the provisions of the GPL are applicable instead of the - * above. If you wish to allow the use of your version of this file - * only under the terms of the GPL and not to allow others to use your - * version of this file under the MPL, indicate your decision by - * deleting the provisions above and replace them with the notice and - * other provisions required by the GPL. If you do not delete the - * provisions above, a recipient may use your version of this file - * under either the MPL or the GPL. - */ - -#include -#include -#include - -#include "hermes.h" - -/* These are maximum timeouts. Most often, card wil react much faster */ -#define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */ -#define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */ -#define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */ -#define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */ - -/* - * AUX port access. To unlock the AUX port write the access keys to the - * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL - * register. Then read it and make sure it's HERMES_AUX_ENABLED. - */ -#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ -#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ -#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ -#define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */ - -#define HERMES_AUX_PW0 0xFE01 -#define HERMES_AUX_PW1 0xDC23 -#define HERMES_AUX_PW2 0xBA45 - -/* HERMES_CMD_DOWNLD */ -#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD) -#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD) -#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD) -#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD) - -/* - * Debugging helpers - */ - -#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ %p: " , hw->iobase); \ - printk(stuff); } while (0) - -#undef HERMES_DEBUG -#ifdef HERMES_DEBUG -#include - -#define DEBUG(lvl, stuff...) if ((lvl) <= HERMES_DEBUG) DMSG(stuff) - -#else /* ! HERMES_DEBUG */ - -#define DEBUG(lvl, stuff...) do { } while (0) - -#endif /* ! HERMES_DEBUG */ - -static const struct hermes_ops hermes_ops_local; - -/* - * Internal functions - */ - -/* Issue a command to the chip. Waiting for it to complete is the caller's - problem. - - Returns -EBUSY if the command register is busy, 0 on success. - - Callable from any context. -*/ -static int hermes_issue_cmd(struct hermes *hw, u16 cmd, u16 param0, - u16 param1, u16 param2) -{ - int k = CMD_BUSY_TIMEOUT; - u16 reg; - - /* First wait for the command register to unbusy */ - reg = hermes_read_regn(hw, CMD); - while ((reg & HERMES_CMD_BUSY) && k) { - k--; - udelay(1); - reg = hermes_read_regn(hw, CMD); - } - if (reg & HERMES_CMD_BUSY) - return -EBUSY; - - hermes_write_regn(hw, PARAM2, param2); - hermes_write_regn(hw, PARAM1, param1); - hermes_write_regn(hw, PARAM0, param0); - hermes_write_regn(hw, CMD, cmd); - - return 0; -} - -/* - * Function definitions - */ - -/* For doing cmds that wipe the magic constant in SWSUPPORT0 */ -static int hermes_doicmd_wait(struct hermes *hw, u16 cmd, - u16 parm0, u16 parm1, u16 parm2, - struct hermes_response *resp) -{ - int err = 0; - int k; - u16 status, reg; - - err = hermes_issue_cmd(hw, cmd, parm0, parm1, parm2); - if (err) - return err; - - reg = hermes_read_regn(hw, EVSTAT); - k = CMD_INIT_TIMEOUT; - while ((!(reg & HERMES_EV_CMD)) && k) { - k--; - udelay(10); - reg = hermes_read_regn(hw, EVSTAT); - } - - hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); - - if (!hermes_present(hw)) { - DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n", - hw->iobase); - err = -ENODEV; - goto out; - } - - if (!(reg & HERMES_EV_CMD)) { - printk(KERN_ERR "hermes @ %p: " - "Timeout waiting for card to reset (reg=0x%04x)!\n", - hw->iobase, reg); - err = -ETIMEDOUT; - goto out; - } - - status = hermes_read_regn(hw, STATUS); - if (resp) { - resp->status = status; - resp->resp0 = hermes_read_regn(hw, RESP0); - resp->resp1 = hermes_read_regn(hw, RESP1); - resp->resp2 = hermes_read_regn(hw, RESP2); - } - - hermes_write_regn(hw, EVACK, HERMES_EV_CMD); - - if (status & HERMES_STATUS_RESULT) - err = -EIO; -out: - return err; -} - -void hermes_struct_init(struct hermes *hw, void __iomem *address, - int reg_spacing) -{ - hw->iobase = address; - hw->reg_spacing = reg_spacing; - hw->inten = 0x0; - hw->eeprom_pda = false; - hw->ops = &hermes_ops_local; -} -EXPORT_SYMBOL(hermes_struct_init); - -static int hermes_init(struct hermes *hw) -{ - u16 reg; - int err = 0; - int k; - - /* We don't want to be interrupted while resetting the chipset */ - hw->inten = 0x0; - hermes_write_regn(hw, INTEN, 0); - hermes_write_regn(hw, EVACK, 0xffff); - - /* Normally it's a "can't happen" for the command register to - be busy when we go to issue a command because we are - serializing all commands. However we want to have some - chance of resetting the card even if it gets into a stupid - state, so we actually wait to see if the command register - will unbusy itself here. */ - k = CMD_BUSY_TIMEOUT; - reg = hermes_read_regn(hw, CMD); - while (k && (reg & HERMES_CMD_BUSY)) { - if (reg == 0xffff) /* Special case - the card has probably been - removed, so don't wait for the timeout */ - return -ENODEV; - - k--; - udelay(1); - reg = hermes_read_regn(hw, CMD); - } - - /* No need to explicitly handle the timeout - if we've timed - out hermes_issue_cmd() will probably return -EBUSY below */ - - /* According to the documentation, EVSTAT may contain - obsolete event occurrence information. We have to acknowledge - it by writing EVACK. */ - reg = hermes_read_regn(hw, EVSTAT); - hermes_write_regn(hw, EVACK, reg); - - /* We don't use hermes_docmd_wait here, because the reset wipes - the magic constant in SWSUPPORT0 away, and it gets confused */ - err = hermes_doicmd_wait(hw, HERMES_CMD_INIT, 0, 0, 0, NULL); - - return err; -} - -/* Issue a command to the chip, and (busy!) wait for it to - * complete. - * - * Returns: - * < 0 on internal error - * 0 on success - * > 0 on error returned by the firmware - * - * Callable from any context, but locking is your problem. */ -static int hermes_docmd_wait(struct hermes *hw, u16 cmd, u16 parm0, - struct hermes_response *resp) -{ - int err; - int k; - u16 reg; - u16 status; - - err = hermes_issue_cmd(hw, cmd, parm0, 0, 0); - if (err) { - if (!hermes_present(hw)) { - if (net_ratelimit()) - printk(KERN_WARNING "hermes @ %p: " - "Card removed while issuing command " - "0x%04x.\n", hw->iobase, cmd); - err = -ENODEV; - } else - if (net_ratelimit()) - printk(KERN_ERR "hermes @ %p: " - "Error %d issuing command 0x%04x.\n", - hw->iobase, err, cmd); - goto out; - } - - reg = hermes_read_regn(hw, EVSTAT); - k = CMD_COMPL_TIMEOUT; - while ((!(reg & HERMES_EV_CMD)) && k) { - k--; - udelay(10); - reg = hermes_read_regn(hw, EVSTAT); - } - - if (!hermes_present(hw)) { - printk(KERN_WARNING "hermes @ %p: Card removed " - "while waiting for command 0x%04x completion.\n", - hw->iobase, cmd); - err = -ENODEV; - goto out; - } - - if (!(reg & HERMES_EV_CMD)) { - printk(KERN_ERR "hermes @ %p: Timeout waiting for " - "command 0x%04x completion.\n", hw->iobase, cmd); - err = -ETIMEDOUT; - goto out; - } - - status = hermes_read_regn(hw, STATUS); - if (resp) { - resp->status = status; - resp->resp0 = hermes_read_regn(hw, RESP0); - resp->resp1 = hermes_read_regn(hw, RESP1); - resp->resp2 = hermes_read_regn(hw, RESP2); - } - - hermes_write_regn(hw, EVACK, HERMES_EV_CMD); - - if (status & HERMES_STATUS_RESULT) - err = -EIO; - - out: - return err; -} - -static int hermes_allocate(struct hermes *hw, u16 size, u16 *fid) -{ - int err = 0; - int k; - u16 reg; - - if ((size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX)) - return -EINVAL; - - err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, NULL); - if (err) - return err; - - reg = hermes_read_regn(hw, EVSTAT); - k = ALLOC_COMPL_TIMEOUT; - while ((!(reg & HERMES_EV_ALLOC)) && k) { - k--; - udelay(10); - reg = hermes_read_regn(hw, EVSTAT); - } - - if (!hermes_present(hw)) { - printk(KERN_WARNING "hermes @ %p: " - "Card removed waiting for frame allocation.\n", - hw->iobase); - return -ENODEV; - } - - if (!(reg & HERMES_EV_ALLOC)) { - printk(KERN_ERR "hermes @ %p: " - "Timeout waiting for frame allocation\n", - hw->iobase); - return -ETIMEDOUT; - } - - *fid = hermes_read_regn(hw, ALLOCFID); - hermes_write_regn(hw, EVACK, HERMES_EV_ALLOC); - - return 0; -} - -/* Set up a BAP to read a particular chunk of data from card's internal buffer. - * - * Returns: - * < 0 on internal failure (errno) - * 0 on success - * > 0 on error - * from firmware - * - * Callable from any context */ -static int hermes_bap_seek(struct hermes *hw, int bap, u16 id, u16 offset) -{ - int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0; - int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0; - int k; - u16 reg; - - /* Paranoia.. */ - if ((offset > HERMES_BAP_OFFSET_MAX) || (offset % 2)) - return -EINVAL; - - k = HERMES_BAP_BUSY_TIMEOUT; - reg = hermes_read_reg(hw, oreg); - while ((reg & HERMES_OFFSET_BUSY) && k) { - k--; - udelay(1); - reg = hermes_read_reg(hw, oreg); - } - - if (reg & HERMES_OFFSET_BUSY) - return -ETIMEDOUT; - - /* Now we actually set up the transfer */ - hermes_write_reg(hw, sreg, id); - hermes_write_reg(hw, oreg, offset); - - /* Wait for the BAP to be ready */ - k = HERMES_BAP_BUSY_TIMEOUT; - reg = hermes_read_reg(hw, oreg); - while ((reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) { - k--; - udelay(1); - reg = hermes_read_reg(hw, oreg); - } - - if (reg != offset) { - printk(KERN_ERR "hermes @ %p: BAP%d offset %s: " - "reg=0x%x id=0x%x offset=0x%x\n", hw->iobase, bap, - (reg & HERMES_OFFSET_BUSY) ? "timeout" : "error", - reg, id, offset); - - if (reg & HERMES_OFFSET_BUSY) - return -ETIMEDOUT; - - return -EIO; /* error or wrong offset */ - } - - return 0; -} - -/* Read a block of data from the chip's buffer, via the - * BAP. Synchronization/serialization is the caller's problem. len - * must be even. - * - * Returns: - * < 0 on internal failure (errno) - * 0 on success - * > 0 on error from firmware - */ -static int hermes_bap_pread(struct hermes *hw, int bap, void *buf, int len, - u16 id, u16 offset) -{ - int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; - int err = 0; - - if ((len < 0) || (len % 2)) - return -EINVAL; - - err = hermes_bap_seek(hw, bap, id, offset); - if (err) - goto out; - - /* Actually do the transfer */ - hermes_read_words(hw, dreg, buf, len / 2); - - out: - return err; -} - -/* Write a block of data to the chip's buffer, via the - * BAP. Synchronization/serialization is the caller's problem. - * - * Returns: - * < 0 on internal failure (errno) - * 0 on success - * > 0 on error from firmware - */ -static int hermes_bap_pwrite(struct hermes *hw, int bap, const void *buf, - int len, u16 id, u16 offset) -{ - int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; - int err = 0; - - if (len < 0) - return -EINVAL; - - err = hermes_bap_seek(hw, bap, id, offset); - if (err) - goto out; - - /* Actually do the transfer */ - hermes_write_bytes(hw, dreg, buf, len); - - out: - return err; -} - -/* Read a Length-Type-Value record from the card. - * - * If length is NULL, we ignore the length read from the card, and - * read the entire buffer regardless. This is useful because some of - * the configuration records appear to have incorrect lengths in - * practice. - * - * Callable from user or bh context. */ -static int hermes_read_ltv(struct hermes *hw, int bap, u16 rid, - unsigned bufsize, u16 *length, void *buf) -{ - int err = 0; - int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; - u16 rlength, rtype; - unsigned nwords; - - if (bufsize % 2) - return -EINVAL; - - err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL); - if (err) - return err; - - err = hermes_bap_seek(hw, bap, rid, 0); - if (err) - return err; - - rlength = hermes_read_reg(hw, dreg); - - if (!rlength) - return -ENODATA; - - rtype = hermes_read_reg(hw, dreg); - - if (length) - *length = rlength; - - if (rtype != rid) - printk(KERN_WARNING "hermes @ %p: %s(): " - "rid (0x%04x) does not match type (0x%04x)\n", - hw->iobase, __func__, rid, rtype); - if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize) - printk(KERN_WARNING "hermes @ %p: " - "Truncating LTV record from %d to %d bytes. " - "(rid=0x%04x, len=0x%04x)\n", hw->iobase, - HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength); - - nwords = min((unsigned)rlength - 1, bufsize / 2); - hermes_read_words(hw, dreg, buf, nwords); - - return 0; -} - -static int hermes_write_ltv(struct hermes *hw, int bap, u16 rid, - u16 length, const void *value) -{ - int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; - int err = 0; - unsigned count; - - if (length == 0) - return -EINVAL; - - err = hermes_bap_seek(hw, bap, rid, 0); - if (err) - return err; - - hermes_write_reg(hw, dreg, length); - hermes_write_reg(hw, dreg, rid); - - count = length - 1; - - hermes_write_bytes(hw, dreg, value, count << 1); - - err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, - rid, NULL); - - return err; -} - -/*** Hermes AUX control ***/ - -static inline void -hermes_aux_setaddr(struct hermes *hw, u32 addr) -{ - hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); - hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); -} - -static inline int -hermes_aux_control(struct hermes *hw, int enabled) -{ - int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED; - int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE; - int i; - - /* Already open? */ - if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state) - return 0; - - hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); - hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); - hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); - hermes_write_reg(hw, HERMES_CONTROL, action); - - for (i = 0; i < 20; i++) { - udelay(10); - if (hermes_read_reg(hw, HERMES_CONTROL) == - desired_state) - return 0; - } - - return -EBUSY; -} - -/*** Hermes programming ***/ - -/* About to start programming data (Hermes I) - * offset is the entry point - * - * Spectrum_cs' Symbol fw does not require this - * wl_lkm Agere fw does - * Don't know about intersil - */ -static int hermesi_program_init(struct hermes *hw, u32 offset) -{ - int err; - - /* Disable interrupts?*/ - /*hw->inten = 0x0;*/ - /*hermes_write_regn(hw, INTEN, 0);*/ - /*hermes_set_irqmask(hw, 0);*/ - - /* Acknowledge any outstanding command */ - hermes_write_regn(hw, EVACK, 0xFFFF); - - /* Using init_cmd_wait rather than cmd_wait */ - err = hw->ops->init_cmd_wait(hw, - 0x0100 | HERMES_CMD_INIT, - 0, 0, 0, NULL); - if (err) - return err; - - err = hw->ops->init_cmd_wait(hw, - 0x0000 | HERMES_CMD_INIT, - 0, 0, 0, NULL); - if (err) - return err; - - err = hermes_aux_control(hw, 1); - pr_debug("AUX enable returned %d\n", err); - - if (err) - return err; - - pr_debug("Enabling volatile, EP 0x%08x\n", offset); - err = hw->ops->init_cmd_wait(hw, - HERMES_PROGRAM_ENABLE_VOLATILE, - offset & 0xFFFFu, - offset >> 16, - 0, - NULL); - pr_debug("PROGRAM_ENABLE returned %d\n", err); - - return err; -} - -/* Done programming data (Hermes I) - * - * Spectrum_cs' Symbol fw does not require this - * wl_lkm Agere fw does - * Don't know about intersil - */ -static int hermesi_program_end(struct hermes *hw) -{ - struct hermes_response resp; - int rc = 0; - int err; - - rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp); - - pr_debug("PROGRAM_DISABLE returned %d, " - "r0 0x%04x, r1 0x%04x, r2 0x%04x\n", - rc, resp.resp0, resp.resp1, resp.resp2); - - if ((rc == 0) && - ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD)) - rc = -EIO; - - err = hermes_aux_control(hw, 0); - pr_debug("AUX disable returned %d\n", err); - - /* Acknowledge any outstanding command */ - hermes_write_regn(hw, EVACK, 0xFFFF); - - /* Reinitialise, ignoring return */ - (void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT, - 0, 0, 0, NULL); - - return rc ? rc : err; -} - -static int hermes_program_bytes(struct hermes *hw, const char *data, - u32 addr, u32 len) -{ - /* wl lkm splits the programming into chunks of 2000 bytes. - * This restriction appears to come from USB. The PCMCIA - * adapters can program the whole lot in one go */ - hermes_aux_setaddr(hw, addr); - hermes_write_bytes(hw, HERMES_AUXDATA, data, len); - return 0; -} - -/* Read PDA from the adapter */ -static int hermes_read_pda(struct hermes *hw, __le16 *pda, u32 pda_addr, - u16 pda_len) -{ - int ret; - u16 pda_size; - u16 data_len = pda_len; - __le16 *data = pda; - - if (hw->eeprom_pda) { - /* PDA of spectrum symbol is in eeprom */ - - /* Issue command to read EEPROM */ - ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); - if (ret) - return ret; - } else { - /* wl_lkm does not include PDA size in the PDA area. - * We will pad the information into pda, so other routines - * don't have to be modified */ - pda[0] = cpu_to_le16(pda_len - 2); - /* Includes CFG_PROD_DATA but not itself */ - pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ - data_len = pda_len - 4; - data = pda + 2; - } - - /* Open auxiliary port */ - ret = hermes_aux_control(hw, 1); - pr_debug("AUX enable returned %d\n", ret); - if (ret) - return ret; - - /* Read PDA */ - hermes_aux_setaddr(hw, pda_addr); - hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2); - - /* Close aux port */ - ret = hermes_aux_control(hw, 0); - pr_debug("AUX disable returned %d\n", ret); - - /* Check PDA length */ - pda_size = le16_to_cpu(pda[0]); - pr_debug("Actual PDA length %d, Max allowed %d\n", - pda_size, pda_len); - if (pda_size > pda_len) - return -EINVAL; - - return 0; -} - -static void hermes_lock_irqsave(spinlock_t *lock, - unsigned long *flags) __acquires(lock) -{ - spin_lock_irqsave(lock, *flags); -} - -static void hermes_unlock_irqrestore(spinlock_t *lock, - unsigned long *flags) __releases(lock) -{ - spin_unlock_irqrestore(lock, *flags); -} - -static void hermes_lock_irq(spinlock_t *lock) __acquires(lock) -{ - spin_lock_irq(lock); -} - -static void hermes_unlock_irq(spinlock_t *lock) __releases(lock) -{ - spin_unlock_irq(lock); -} - -/* Hermes operations for local buses */ -static const struct hermes_ops hermes_ops_local = { - .init = hermes_init, - .cmd_wait = hermes_docmd_wait, - .init_cmd_wait = hermes_doicmd_wait, - .allocate = hermes_allocate, - .read_ltv = hermes_read_ltv, - .write_ltv = hermes_write_ltv, - .bap_pread = hermes_bap_pread, - .bap_pwrite = hermes_bap_pwrite, - .read_pda = hermes_read_pda, - .program_init = hermesi_program_init, - .program_end = hermesi_program_end, - .program = hermes_program_bytes, - .lock_irqsave = hermes_lock_irqsave, - .unlock_irqrestore = hermes_unlock_irqrestore, - .lock_irq = hermes_lock_irq, - .unlock_irq = hermes_unlock_irq, -}; diff --git a/drivers/net/wireless/orinoco/hermes.h b/drivers/net/wireless/orinoco/hermes.h deleted file mode 100644 index 28a42448d329..000000000000 --- a/drivers/net/wireless/orinoco/hermes.h +++ /dev/null @@ -1,520 +0,0 @@ -/* hermes.h - * - * Driver core for the "Hermes" wireless MAC controller, as used in - * the Lucent Orinoco and Cabletron RoamAbout cards. It should also - * work on the hfa3841 and hfa3842 MAC controller chips used in the - * Prism I & II chipsets. - * - * This is not a complete driver, just low-level access routines for - * the MAC controller itself. - * - * Based on the prism2 driver from Absolute Value Systems' linux-wlan - * project, the Linux wvlan_cs driver, Lucent's HCF-Light - * (wvlan_hcf.c) library, and the NetBSD wireless driver. - * - * Copyright (C) 2000, David Gibson, Linuxcare Australia. - * (C) Copyright David Gibson, IBM Corp. 2001-2003. - * - * Portions taken from hfa384x.h. - * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. - * - * This file distributed under the GPL, version 2. - */ - -#ifndef _HERMES_H -#define _HERMES_H - -/* Notes on locking: - * - * As a module of low level hardware access routines, there is no - * locking. Users of this module should ensure that they serialize - * access to the hermes structure, and to the hardware -*/ - -#include -#include - -/* - * Limits and constants - */ -#define HERMES_ALLOC_LEN_MIN (4) -#define HERMES_ALLOC_LEN_MAX (2400) -#define HERMES_LTV_LEN_MAX (34) -#define HERMES_BAP_DATALEN_MAX (4096) -#define HERMES_BAP_OFFSET_MAX (4096) -#define HERMES_PORTID_MAX (7) -#define HERMES_NUMPORTS_MAX (HERMES_PORTID_MAX + 1) -#define HERMES_PDR_LEN_MAX (260) /* in bytes, from EK */ -#define HERMES_PDA_RECS_MAX (200) /* a guess */ -#define HERMES_PDA_LEN_MAX (1024) /* in bytes, from EK */ -#define HERMES_SCANRESULT_MAX (35) -#define HERMES_CHINFORESULT_MAX (8) -#define HERMES_MAX_MULTICAST (16) -#define HERMES_MAGIC (0x7d1f) - -/* - * Hermes register offsets - */ -#define HERMES_CMD (0x00) -#define HERMES_PARAM0 (0x02) -#define HERMES_PARAM1 (0x04) -#define HERMES_PARAM2 (0x06) -#define HERMES_STATUS (0x08) -#define HERMES_RESP0 (0x0A) -#define HERMES_RESP1 (0x0C) -#define HERMES_RESP2 (0x0E) -#define HERMES_INFOFID (0x10) -#define HERMES_RXFID (0x20) -#define HERMES_ALLOCFID (0x22) -#define HERMES_TXCOMPLFID (0x24) -#define HERMES_SELECT0 (0x18) -#define HERMES_OFFSET0 (0x1C) -#define HERMES_DATA0 (0x36) -#define HERMES_SELECT1 (0x1A) -#define HERMES_OFFSET1 (0x1E) -#define HERMES_DATA1 (0x38) -#define HERMES_EVSTAT (0x30) -#define HERMES_INTEN (0x32) -#define HERMES_EVACK (0x34) -#define HERMES_CONTROL (0x14) -#define HERMES_SWSUPPORT0 (0x28) -#define HERMES_SWSUPPORT1 (0x2A) -#define HERMES_SWSUPPORT2 (0x2C) -#define HERMES_AUXPAGE (0x3A) -#define HERMES_AUXOFFSET (0x3C) -#define HERMES_AUXDATA (0x3E) - -/* - * CMD register bitmasks - */ -#define HERMES_CMD_BUSY (0x8000) -#define HERMES_CMD_AINFO (0x7f00) -#define HERMES_CMD_MACPORT (0x0700) -#define HERMES_CMD_RECL (0x0100) -#define HERMES_CMD_WRITE (0x0100) -#define HERMES_CMD_PROGMODE (0x0300) -#define HERMES_CMD_CMDCODE (0x003f) - -/* - * STATUS register bitmasks - */ -#define HERMES_STATUS_RESULT (0x7f00) -#define HERMES_STATUS_CMDCODE (0x003f) - -/* - * OFFSET register bitmasks - */ -#define HERMES_OFFSET_BUSY (0x8000) -#define HERMES_OFFSET_ERR (0x4000) -#define HERMES_OFFSET_DATAOFF (0x0ffe) - -/* - * Event register bitmasks (INTEN, EVSTAT, EVACK) - */ -#define HERMES_EV_TICK (0x8000) -#define HERMES_EV_WTERR (0x4000) -#define HERMES_EV_INFDROP (0x2000) -#define HERMES_EV_INFO (0x0080) -#define HERMES_EV_DTIM (0x0020) -#define HERMES_EV_CMD (0x0010) -#define HERMES_EV_ALLOC (0x0008) -#define HERMES_EV_TXEXC (0x0004) -#define HERMES_EV_TX (0x0002) -#define HERMES_EV_RX (0x0001) - -/* - * Command codes - */ -/*--- Controller Commands ----------------------------*/ -#define HERMES_CMD_INIT (0x0000) -#define HERMES_CMD_ENABLE (0x0001) -#define HERMES_CMD_DISABLE (0x0002) -#define HERMES_CMD_DIAG (0x0003) - -/*--- Buffer Mgmt Commands ---------------------------*/ -#define HERMES_CMD_ALLOC (0x000A) -#define HERMES_CMD_TX (0x000B) - -/*--- Regulate Commands ------------------------------*/ -#define HERMES_CMD_NOTIFY (0x0010) -#define HERMES_CMD_INQUIRE (0x0011) - -/*--- Configure Commands -----------------------------*/ -#define HERMES_CMD_ACCESS (0x0021) -#define HERMES_CMD_DOWNLD (0x0022) - -/*--- Serial I/O Commands ----------------------------*/ -#define HERMES_CMD_READMIF (0x0030) -#define HERMES_CMD_WRITEMIF (0x0031) - -/*--- Debugging Commands -----------------------------*/ -#define HERMES_CMD_TEST (0x0038) - - -/* Test command arguments */ -#define HERMES_TEST_SET_CHANNEL 0x0800 -#define HERMES_TEST_MONITOR 0x0b00 -#define HERMES_TEST_STOP 0x0f00 - -/* Authentication algorithms */ -#define HERMES_AUTH_OPEN 1 -#define HERMES_AUTH_SHARED_KEY 2 - -/* WEP settings */ -#define HERMES_WEP_PRIVACY_INVOKED 0x0001 -#define HERMES_WEP_EXCL_UNENCRYPTED 0x0002 -#define HERMES_WEP_HOST_ENCRYPT 0x0010 -#define HERMES_WEP_HOST_DECRYPT 0x0080 - -/* Symbol hostscan options */ -#define HERMES_HOSTSCAN_SYMBOL_5SEC 0x0001 -#define HERMES_HOSTSCAN_SYMBOL_ONCE 0x0002 -#define HERMES_HOSTSCAN_SYMBOL_PASSIVE 0x0040 -#define HERMES_HOSTSCAN_SYMBOL_BCAST 0x0080 - -/* - * Frame structures and constants - */ - -#define HERMES_DESCRIPTOR_OFFSET 0 -#define HERMES_802_11_OFFSET (14) -#define HERMES_802_3_OFFSET (14 + 32) -#define HERMES_802_2_OFFSET (14 + 32 + 14) -#define HERMES_TXCNTL2_OFFSET (HERMES_802_3_OFFSET - 2) - -#define HERMES_RXSTAT_ERR (0x0003) -#define HERMES_RXSTAT_BADCRC (0x0001) -#define HERMES_RXSTAT_UNDECRYPTABLE (0x0002) -#define HERMES_RXSTAT_MIC (0x0010) /* Frame contains MIC */ -#define HERMES_RXSTAT_MACPORT (0x0700) -#define HERMES_RXSTAT_PCF (0x1000) /* Frame was received in CF period */ -#define HERMES_RXSTAT_MIC_KEY_ID (0x1800) /* MIC key used */ -#define HERMES_RXSTAT_MSGTYPE (0xE000) -#define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */ -#define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel encoded frame */ -#define HERMES_RXSTAT_WMP (0x6000) /* Wavelan-II Management Protocol frame */ - -/* Shift amount for key ID in RXSTAT and TXCTRL */ -#define HERMES_MIC_KEY_ID_SHIFT 11 - -struct hermes_tx_descriptor { - __le16 status; - __le16 reserved1; - __le16 reserved2; - __le32 sw_support; - u8 retry_count; - u8 tx_rate; - __le16 tx_control; -} __packed; - -#define HERMES_TXSTAT_RETRYERR (0x0001) -#define HERMES_TXSTAT_AGEDERR (0x0002) -#define HERMES_TXSTAT_DISCON (0x0004) -#define HERMES_TXSTAT_FORMERR (0x0008) - -#define HERMES_TXCTRL_TX_OK (0x0002) /* ?? interrupt on Tx complete */ -#define HERMES_TXCTRL_TX_EX (0x0004) /* ?? interrupt on Tx exception */ -#define HERMES_TXCTRL_802_11 (0x0008) /* We supply 802.11 header */ -#define HERMES_TXCTRL_MIC (0x0010) /* 802.3 + TKIP */ -#define HERMES_TXCTRL_MIC_KEY_ID (0x1800) /* MIC Key ID mask */ -#define HERMES_TXCTRL_ALT_RTRY (0x0020) - -/* Inquiry constants and data types */ - -#define HERMES_INQ_TALLIES (0xF100) -#define HERMES_INQ_SCAN (0xF101) -#define HERMES_INQ_CHANNELINFO (0xF102) -#define HERMES_INQ_HOSTSCAN (0xF103) -#define HERMES_INQ_HOSTSCAN_SYMBOL (0xF104) -#define HERMES_INQ_LINKSTATUS (0xF200) -#define HERMES_INQ_SEC_STAT_AGERE (0xF202) - -struct hermes_tallies_frame { - __le16 TxUnicastFrames; - __le16 TxMulticastFrames; - __le16 TxFragments; - __le16 TxUnicastOctets; - __le16 TxMulticastOctets; - __le16 TxDeferredTransmissions; - __le16 TxSingleRetryFrames; - __le16 TxMultipleRetryFrames; - __le16 TxRetryLimitExceeded; - __le16 TxDiscards; - __le16 RxUnicastFrames; - __le16 RxMulticastFrames; - __le16 RxFragments; - __le16 RxUnicastOctets; - __le16 RxMulticastOctets; - __le16 RxFCSErrors; - __le16 RxDiscards_NoBuffer; - __le16 TxDiscardsWrongSA; - __le16 RxWEPUndecryptable; - __le16 RxMsgInMsgFragments; - __le16 RxMsgInBadMsgFragments; - /* Those last are probably not available in very old firmwares */ - __le16 RxDiscards_WEPICVError; - __le16 RxDiscards_WEPExcluded; -} __packed; - -/* Grabbed from wlan-ng - Thanks Mark... - Jean II - * This is the result of a scan inquiry command */ -/* Structure describing info about an Access Point */ -struct prism2_scan_apinfo { - __le16 channel; /* Channel where the AP sits */ - __le16 noise; /* Noise level */ - __le16 level; /* Signal level */ - u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */ - __le16 beacon_interv; /* Beacon interval */ - __le16 capabilities; /* Capabilities */ - __le16 essid_len; /* ESSID length */ - u8 essid[32]; /* ESSID of the network */ - u8 rates[10]; /* Bit rate supported */ - __le16 proberesp_rate; /* Data rate of the response frame */ - __le16 atim; /* ATIM window time, Kus (hostscan only) */ -} __packed; - -/* Same stuff for the Lucent/Agere card. - * Thanks to h1kari - Jean II */ -struct agere_scan_apinfo { - __le16 channel; /* Channel where the AP sits */ - __le16 noise; /* Noise level */ - __le16 level; /* Signal level */ - u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */ - __le16 beacon_interv; /* Beacon interval */ - __le16 capabilities; /* Capabilities */ - /* bits: 0-ess, 1-ibss, 4-privacy [wep] */ - __le16 essid_len; /* ESSID length */ - u8 essid[32]; /* ESSID of the network */ -} __packed; - -/* Moustafa: Scan structure for Symbol cards */ -struct symbol_scan_apinfo { - u8 channel; /* Channel where the AP sits */ - u8 unknown1; /* 8 in 2.9x and 3.9x f/w, 0 otherwise */ - __le16 noise; /* Noise level */ - __le16 level; /* Signal level */ - u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */ - __le16 beacon_interv; /* Beacon interval */ - __le16 capabilities; /* Capabilities */ - /* bits: 0-ess, 1-ibss, 4-privacy [wep] */ - __le16 essid_len; /* ESSID length */ - u8 essid[32]; /* ESSID of the network */ - __le16 rates[5]; /* Bit rate supported */ - __le16 basic_rates; /* Basic rates bitmask */ - u8 unknown2[6]; /* Always FF:FF:FF:FF:00:00 */ - u8 unknown3[8]; /* Always 0, appeared in f/w 3.91-68 */ -} __packed; - -union hermes_scan_info { - struct agere_scan_apinfo a; - struct prism2_scan_apinfo p; - struct symbol_scan_apinfo s; -}; - -/* Extended scan struct for HERMES_INQ_CHANNELINFO. - * wl_lkm calls this an ACS scan (Automatic Channel Select). - * Keep out of union hermes_scan_info because it is much bigger than - * the older scan structures. */ -struct agere_ext_scan_info { - __le16 reserved0; - - u8 noise; - u8 level; - u8 rx_flow; - u8 rate; - __le16 reserved1[2]; - - __le16 frame_control; - __le16 dur_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 bssid[ETH_ALEN]; - __le16 sequence; - u8 addr4[ETH_ALEN]; - - __le16 data_length; - - /* Next 3 fields do not get filled in. */ - u8 daddr[ETH_ALEN]; - u8 saddr[ETH_ALEN]; - __le16 len_type; - - __le64 timestamp; - __le16 beacon_interval; - __le16 capabilities; - u8 data[0]; -} __packed; - -#define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000) -#define HERMES_LINKSTATUS_CONNECTED (0x0001) -#define HERMES_LINKSTATUS_DISCONNECTED (0x0002) -#define HERMES_LINKSTATUS_AP_CHANGE (0x0003) -#define HERMES_LINKSTATUS_AP_OUT_OF_RANGE (0x0004) -#define HERMES_LINKSTATUS_AP_IN_RANGE (0x0005) -#define HERMES_LINKSTATUS_ASSOC_FAILED (0x0006) - -struct hermes_linkstatus { - __le16 linkstatus; /* Link status */ -} __packed; - -struct hermes_response { - u16 status, resp0, resp1, resp2; -}; - -/* "ID" structure - used for ESSID and station nickname */ -struct hermes_idstring { - __le16 len; - __le16 val[16]; -} __packed; - -struct hermes_multicast { - u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN]; -} __packed; - -/* Timeouts */ -#define HERMES_BAP_BUSY_TIMEOUT (10000) /* In iterations of ~1us */ - -struct hermes; - -/* Functions to access hardware */ -struct hermes_ops { - int (*init)(struct hermes *hw); - int (*cmd_wait)(struct hermes *hw, u16 cmd, u16 parm0, - struct hermes_response *resp); - int (*init_cmd_wait)(struct hermes *hw, u16 cmd, - u16 parm0, u16 parm1, u16 parm2, - struct hermes_response *resp); - int (*allocate)(struct hermes *hw, u16 size, u16 *fid); - int (*read_ltv)(struct hermes *hw, int bap, u16 rid, unsigned buflen, - u16 *length, void *buf); - int (*write_ltv)(struct hermes *hw, int bap, u16 rid, - u16 length, const void *value); - int (*bap_pread)(struct hermes *hw, int bap, void *buf, int len, - u16 id, u16 offset); - int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf, - int len, u16 id, u16 offset); - int (*read_pda)(struct hermes *hw, __le16 *pda, - u32 pda_addr, u16 pda_len); - int (*program_init)(struct hermes *hw, u32 entry_point); - int (*program_end)(struct hermes *hw); - int (*program)(struct hermes *hw, const char *buf, - u32 addr, u32 len); - void (*lock_irqsave)(spinlock_t *lock, unsigned long *flags); - void (*unlock_irqrestore)(spinlock_t *lock, unsigned long *flags); - void (*lock_irq)(spinlock_t *lock); - void (*unlock_irq)(spinlock_t *lock); -}; - -/* Basic control structure */ -struct hermes { - void __iomem *iobase; - int reg_spacing; -#define HERMES_16BIT_REGSPACING 0 -#define HERMES_32BIT_REGSPACING 1 - u16 inten; /* Which interrupts should be enabled? */ - bool eeprom_pda; - const struct hermes_ops *ops; - void *priv; -}; - -/* Register access convenience macros */ -#define hermes_read_reg(hw, off) \ - (ioread16((hw)->iobase + ((off) << (hw)->reg_spacing))) -#define hermes_write_reg(hw, off, val) \ - (iowrite16((val), (hw)->iobase + ((off) << (hw)->reg_spacing))) -#define hermes_read_regn(hw, name) hermes_read_reg((hw), HERMES_##name) -#define hermes_write_regn(hw, name, val) \ - hermes_write_reg((hw), HERMES_##name, (val)) - -/* Function prototypes */ -void hermes_struct_init(struct hermes *hw, void __iomem *address, - int reg_spacing); - -/* Inline functions */ - -static inline int hermes_present(struct hermes *hw) -{ - return hermes_read_regn(hw, SWSUPPORT0) == HERMES_MAGIC; -} - -static inline void hermes_set_irqmask(struct hermes *hw, u16 events) -{ - hw->inten = events; - hermes_write_regn(hw, INTEN, events); -} - -static inline int hermes_enable_port(struct hermes *hw, int port) -{ - return hw->ops->cmd_wait(hw, HERMES_CMD_ENABLE | (port << 8), - 0, NULL); -} - -static inline int hermes_disable_port(struct hermes *hw, int port) -{ - return hw->ops->cmd_wait(hw, HERMES_CMD_DISABLE | (port << 8), - 0, NULL); -} - -/* Initiate an INQUIRE command (tallies or scan). The result will come as an - * information frame in __orinoco_ev_info() */ -static inline int hermes_inquire(struct hermes *hw, u16 rid) -{ - return hw->ops->cmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL); -} - -#define HERMES_BYTES_TO_RECLEN(n) ((((n) + 1) / 2) + 1) -#define HERMES_RECLEN_TO_BYTES(n) (((n) - 1) * 2) - -/* Note that for the next two, the count is in 16-bit words, not bytes */ -static inline void hermes_read_words(struct hermes *hw, int off, - void *buf, unsigned count) -{ - off = off << hw->reg_spacing; - ioread16_rep(hw->iobase + off, buf, count); -} - -static inline void hermes_write_bytes(struct hermes *hw, int off, - const char *buf, unsigned count) -{ - off = off << hw->reg_spacing; - iowrite16_rep(hw->iobase + off, buf, count >> 1); - if (unlikely(count & 1)) - iowrite8(buf[count - 1], hw->iobase + off); -} - -static inline void hermes_clear_words(struct hermes *hw, int off, - unsigned count) -{ - unsigned i; - - off = off << hw->reg_spacing; - - for (i = 0; i < count; i++) - iowrite16(0, hw->iobase + off); -} - -#define HERMES_READ_RECORD(hw, bap, rid, buf) \ - (hw->ops->read_ltv((hw), (bap), (rid), sizeof(*buf), NULL, (buf))) -#define HERMES_WRITE_RECORD(hw, bap, rid, buf) \ - (hw->ops->write_ltv((hw), (bap), (rid), \ - HERMES_BYTES_TO_RECLEN(sizeof(*buf)), (buf))) - -static inline int hermes_read_wordrec(struct hermes *hw, int bap, u16 rid, - u16 *word) -{ - __le16 rec; - int err; - - err = HERMES_READ_RECORD(hw, bap, rid, &rec); - *word = le16_to_cpu(rec); - return err; -} - -static inline int hermes_write_wordrec(struct hermes *hw, int bap, u16 rid, - u16 word) -{ - __le16 rec = cpu_to_le16(word); - return HERMES_WRITE_RECORD(hw, bap, rid, &rec); -} - -#endif /* _HERMES_H */ diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/orinoco/hermes_dld.c deleted file mode 100644 index 4a10b7aca043..000000000000 --- a/drivers/net/wireless/orinoco/hermes_dld.c +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Hermes download helper. - * - * This helper: - * - is capable of writing to the volatile area of the hermes device - * - is currently not capable of writing to non-volatile areas - * - provide helpers to identify and update plugin data - * - is not capable of interpreting a fw image directly. That is up to - * the main card driver. - * - deals with Hermes I devices. It can probably be modified to deal - * with Hermes II devices - * - * Copyright (C) 2007, David Kilroy - * - * Plug data code slightly modified from spectrum_cs driver - * Copyright (C) 2002-2005 Pavel Roskin - * Portions based on information in wl_lkm_718 Agere driver - * COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License - * at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and - * limitations under the License. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License version 2 (the "GPL"), in - * which case the provisions of the GPL are applicable instead of the - * above. If you wish to allow the use of your version of this file - * only under the terms of the GPL and not to allow others to use your - * version of this file under the MPL, indicate your decision by - * deleting the provisions above and replace them with the notice and - * other provisions required by the GPL. If you do not delete the - * provisions above, a recipient may use your version of this file - * under either the MPL or the GPL. - */ - -#include -#include -#include "hermes.h" -#include "hermes_dld.h" - -#define PFX "hermes_dld: " - -/* End markers used in dblocks */ -#define PDI_END 0x00000000 /* End of PDA */ -#define BLOCK_END 0xFFFFFFFF /* Last image block */ -#define TEXT_END 0x1A /* End of text header */ - -/* - * The following structures have little-endian fields denoted by - * the leading underscore. Don't access them directly - use inline - * functions defined below. - */ - -/* - * The binary image to be downloaded consists of series of data blocks. - * Each block has the following structure. - */ -struct dblock { - __le32 addr; /* adapter address where to write the block */ - __le16 len; /* length of the data only, in bytes */ - char data[0]; /* data to be written */ -} __packed; - -/* - * Plug Data References are located in the image after the last data - * block. They refer to areas in the adapter memory where the plug data - * items with matching ID should be written. - */ -struct pdr { - __le32 id; /* record ID */ - __le32 addr; /* adapter address where to write the data */ - __le32 len; /* expected length of the data, in bytes */ - char next[0]; /* next PDR starts here */ -} __packed; - -/* - * Plug Data Items are located in the EEPROM read from the adapter by - * primary firmware. They refer to the device-specific data that should - * be plugged into the secondary firmware. - */ -struct pdi { - __le16 len; /* length of ID and data, in words */ - __le16 id; /* record ID */ - char data[0]; /* plug data */ -} __packed; - -/*** FW data block access functions ***/ - -static inline u32 -dblock_addr(const struct dblock *blk) -{ - return le32_to_cpu(blk->addr); -} - -static inline u32 -dblock_len(const struct dblock *blk) -{ - return le16_to_cpu(blk->len); -} - -/*** PDR Access functions ***/ - -static inline u32 -pdr_id(const struct pdr *pdr) -{ - return le32_to_cpu(pdr->id); -} - -static inline u32 -pdr_addr(const struct pdr *pdr) -{ - return le32_to_cpu(pdr->addr); -} - -static inline u32 -pdr_len(const struct pdr *pdr) -{ - return le32_to_cpu(pdr->len); -} - -/*** PDI Access functions ***/ - -static inline u32 -pdi_id(const struct pdi *pdi) -{ - return le16_to_cpu(pdi->id); -} - -/* Return length of the data only, in bytes */ -static inline u32 -pdi_len(const struct pdi *pdi) -{ - return 2 * (le16_to_cpu(pdi->len) - 1); -} - -/*** Plug Data Functions ***/ - -/* - * Scan PDR for the record with the specified RECORD_ID. - * If it's not found, return NULL. - */ -static const struct pdr * -hermes_find_pdr(const struct pdr *first_pdr, u32 record_id, const void *end) -{ - const struct pdr *pdr = first_pdr; - - end -= sizeof(struct pdr); - - while (((void *) pdr <= end) && - (pdr_id(pdr) != PDI_END)) { - /* - * PDR area is currently not terminated by PDI_END. - * It's followed by CRC records, which have the type - * field where PDR has length. The type can be 0 or 1. - */ - if (pdr_len(pdr) < 2) - return NULL; - - /* If the record ID matches, we are done */ - if (pdr_id(pdr) == record_id) - return pdr; - - pdr = (struct pdr *) pdr->next; - } - return NULL; -} - -/* Scan production data items for a particular entry */ -static const struct pdi * -hermes_find_pdi(const struct pdi *first_pdi, u32 record_id, const void *end) -{ - const struct pdi *pdi = first_pdi; - - end -= sizeof(struct pdi); - - while (((void *) pdi <= end) && - (pdi_id(pdi) != PDI_END)) { - - /* If the record ID matches, we are done */ - if (pdi_id(pdi) == record_id) - return pdi; - - pdi = (struct pdi *) &pdi->data[pdi_len(pdi)]; - } - return NULL; -} - -/* Process one Plug Data Item - find corresponding PDR and plug it */ -static int -hermes_plug_pdi(struct hermes *hw, const struct pdr *first_pdr, - const struct pdi *pdi, const void *pdr_end) -{ - const struct pdr *pdr; - - /* Find the PDR corresponding to this PDI */ - pdr = hermes_find_pdr(first_pdr, pdi_id(pdi), pdr_end); - - /* No match is found, safe to ignore */ - if (!pdr) - return 0; - - /* Lengths of the data in PDI and PDR must match */ - if (pdi_len(pdi) != pdr_len(pdr)) - return -EINVAL; - - /* do the actual plugging */ - hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi)); - - return 0; -} - -/* Parse PDA and write the records into the adapter - * - * Attempt to write every records that is in the specified pda - * which also has a valid production data record for the firmware. - */ -int hermes_apply_pda(struct hermes *hw, - const char *first_pdr, - const void *pdr_end, - const __le16 *pda, - const void *pda_end) -{ - int ret; - const struct pdi *pdi; - const struct pdr *pdr; - - pdr = (const struct pdr *) first_pdr; - pda_end -= sizeof(struct pdi); - - /* Go through every PDI and plug them into the adapter */ - pdi = (const struct pdi *) (pda + 2); - while (((void *) pdi <= pda_end) && - (pdi_id(pdi) != PDI_END)) { - ret = hermes_plug_pdi(hw, pdr, pdi, pdr_end); - if (ret) - return ret; - - /* Increment to the next PDI */ - pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)]; - } - return 0; -} - -/* Identify the total number of bytes in all blocks - * including the header data. - */ -size_t -hermes_blocks_length(const char *first_block, const void *end) -{ - const struct dblock *blk = (const struct dblock *) first_block; - int total_len = 0; - int len; - - end -= sizeof(*blk); - - /* Skip all blocks to locate Plug Data References - * (Spectrum CS) */ - while (((void *) blk <= end) && - (dblock_addr(blk) != BLOCK_END)) { - len = dblock_len(blk); - total_len += sizeof(*blk) + len; - blk = (struct dblock *) &blk->data[len]; - } - - return total_len; -} - -/*** Hermes programming ***/ - -/* Program the data blocks */ -int hermes_program(struct hermes *hw, const char *first_block, const void *end) -{ - const struct dblock *blk; - u32 blkaddr; - u32 blklen; - int err = 0; - - blk = (const struct dblock *) first_block; - - if ((void *) blk > (end - sizeof(*blk))) - return -EIO; - - blkaddr = dblock_addr(blk); - blklen = dblock_len(blk); - - while ((blkaddr != BLOCK_END) && - (((void *) blk + blklen) <= end)) { - pr_debug(PFX "Programming block of length %d " - "to address 0x%08x\n", blklen, blkaddr); - - err = hw->ops->program(hw, blk->data, blkaddr, blklen); - if (err) - break; - - blk = (const struct dblock *) &blk->data[blklen]; - - if ((void *) blk > (end - sizeof(*blk))) - return -EIO; - - blkaddr = dblock_addr(blk); - blklen = dblock_len(blk); - } - return err; -} - -/*** Default plugging data for Hermes I ***/ -/* Values from wl_lkm_718/hcf/dhf.c */ - -#define DEFINE_DEFAULT_PDR(pid, length, data) \ -static const struct { \ - __le16 len; \ - __le16 id; \ - u8 val[length]; \ -} __packed default_pdr_data_##pid = { \ - cpu_to_le16((sizeof(default_pdr_data_##pid)/ \ - sizeof(__le16)) - 1), \ - cpu_to_le16(pid), \ - data \ -} - -#define DEFAULT_PDR(pid) default_pdr_data_##pid - -/* HWIF Compatibility */ -DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00"); - -/* PPPPSign */ -DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00"); - -/* PPPPProf */ -DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00"); - -/* Antenna diversity */ -DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F"); - -/* Modem VCO band Set-up */ -DEFINE_DEFAULT_PDR(0x0160, 28, - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00"); - -/* Modem Rx Gain Table Values */ -DEFINE_DEFAULT_PDR(0x0161, 256, - "\x3F\x01\x3F\01\x3F\x01\x3F\x01" - "\x3F\x01\x3F\01\x3F\x01\x3F\x01" - "\x3F\x01\x3F\01\x3F\x01\x3F\x01" - "\x3F\x01\x3F\01\x3F\x01\x3F\x01" - "\x3F\x01\x3E\01\x3E\x01\x3D\x01" - "\x3D\x01\x3C\01\x3C\x01\x3B\x01" - "\x3B\x01\x3A\01\x3A\x01\x39\x01" - "\x39\x01\x38\01\x38\x01\x37\x01" - "\x37\x01\x36\01\x36\x01\x35\x01" - "\x35\x01\x34\01\x34\x01\x33\x01" - "\x33\x01\x32\x01\x32\x01\x31\x01" - "\x31\x01\x30\x01\x30\x01\x7B\x01" - "\x7B\x01\x7A\x01\x7A\x01\x79\x01" - "\x79\x01\x78\x01\x78\x01\x77\x01" - "\x77\x01\x76\x01\x76\x01\x75\x01" - "\x75\x01\x74\x01\x74\x01\x73\x01" - "\x73\x01\x72\x01\x72\x01\x71\x01" - "\x71\x01\x70\x01\x70\x01\x68\x01" - "\x68\x01\x67\x01\x67\x01\x66\x01" - "\x66\x01\x65\x01\x65\x01\x57\x01" - "\x57\x01\x56\x01\x56\x01\x55\x01" - "\x55\x01\x54\x01\x54\x01\x53\x01" - "\x53\x01\x52\x01\x52\x01\x51\x01" - "\x51\x01\x50\x01\x50\x01\x48\x01" - "\x48\x01\x47\x01\x47\x01\x46\x01" - "\x46\x01\x45\x01\x45\x01\x44\x01" - "\x44\x01\x43\x01\x43\x01\x42\x01" - "\x42\x01\x41\x01\x41\x01\x40\x01" - "\x40\x01\x40\x01\x40\x01\x40\x01" - "\x40\x01\x40\x01\x40\x01\x40\x01" - "\x40\x01\x40\x01\x40\x01\x40\x01" - "\x40\x01\x40\x01\x40\x01\x40\x01"); - -/* Write PDA according to certain rules. - * - * For every production data record, look for a previous setting in - * the pda, and use that. - * - * For certain records, use defaults if they are not found in pda. - */ -int hermes_apply_pda_with_defaults(struct hermes *hw, - const char *first_pdr, - const void *pdr_end, - const __le16 *pda, - const void *pda_end) -{ - const struct pdr *pdr = (const struct pdr *) first_pdr; - const struct pdi *first_pdi = (const struct pdi *) &pda[2]; - const struct pdi *pdi; - const struct pdi *default_pdi = NULL; - const struct pdi *outdoor_pdi; - int record_id; - - pdr_end -= sizeof(struct pdr); - - while (((void *) pdr <= pdr_end) && - (pdr_id(pdr) != PDI_END)) { - /* - * For spectrum_cs firmwares, - * PDR area is currently not terminated by PDI_END. - * It's followed by CRC records, which have the type - * field where PDR has length. The type can be 0 or 1. - */ - if (pdr_len(pdr) < 2) - break; - record_id = pdr_id(pdr); - - pdi = hermes_find_pdi(first_pdi, record_id, pda_end); - if (pdi) - pr_debug(PFX "Found record 0x%04x at %p\n", - record_id, pdi); - - switch (record_id) { - case 0x110: /* Modem REFDAC values */ - case 0x120: /* Modem VGDAC values */ - outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1, - pda_end); - default_pdi = NULL; - if (outdoor_pdi) { - pdi = outdoor_pdi; - pr_debug(PFX - "Using outdoor record 0x%04x at %p\n", - record_id + 1, pdi); - } - break; - case 0x5: /* HWIF Compatibility */ - default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005); - break; - case 0x108: /* PPPPSign */ - default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108); - break; - case 0x109: /* PPPPProf */ - default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109); - break; - case 0x150: /* Antenna diversity */ - default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150); - break; - case 0x160: /* Modem VCO band Set-up */ - default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160); - break; - case 0x161: /* Modem Rx Gain Table Values */ - default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161); - break; - default: - default_pdi = NULL; - break; - } - if (!pdi && default_pdi) { - /* Use default */ - pdi = default_pdi; - pr_debug(PFX "Using default record 0x%04x at %p\n", - record_id, pdi); - } - - if (pdi) { - /* Lengths of the data in PDI and PDR must match */ - if ((pdi_len(pdi) == pdr_len(pdr)) && - ((void *) pdi->data + pdi_len(pdi) < pda_end)) { - /* do the actual plugging */ - hw->ops->program(hw, pdi->data, pdr_addr(pdr), - pdi_len(pdi)); - } - } - - pdr++; - } - return 0; -} diff --git a/drivers/net/wireless/orinoco/hermes_dld.h b/drivers/net/wireless/orinoco/hermes_dld.h deleted file mode 100644 index b5377e232c63..000000000000 --- a/drivers/net/wireless/orinoco/hermes_dld.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2007, David Kilroy - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License - * at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and - * limitations under the License. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License version 2 (the "GPL"), in - * which case the provisions of the GPL are applicable instead of the - * above. If you wish to allow the use of your version of this file - * only under the terms of the GPL and not to allow others to use your - * version of this file under the MPL, indicate your decision by - * deleting the provisions above and replace them with the notice and - * other provisions required by the GPL. If you do not delete the - * provisions above, a recipient may use your version of this file - * under either the MPL or the GPL. - */ -#ifndef _HERMES_DLD_H -#define _HERMES_DLD_H - -#include "hermes.h" - -int hermesi_program_init(struct hermes *hw, u32 offset); -int hermesi_program_end(struct hermes *hw); -int hermes_program(struct hermes *hw, const char *first_block, const void *end); - -int hermes_read_pda(struct hermes *hw, - __le16 *pda, - u32 pda_addr, - u16 pda_len, - int use_eeprom); -int hermes_apply_pda(struct hermes *hw, - const char *first_pdr, - const void *pdr_end, - const __le16 *pda, - const void *pda_end); -int hermes_apply_pda_with_defaults(struct hermes *hw, - const char *first_pdr, - const void *pdr_end, - const __le16 *pda, - const void *pda_end); - -size_t hermes_blocks_length(const char *first_block, const void *end); - -#endif /* _HERMES_DLD_H */ diff --git a/drivers/net/wireless/orinoco/hermes_rid.h b/drivers/net/wireless/orinoco/hermes_rid.h deleted file mode 100644 index 42eb67dea1df..000000000000 --- a/drivers/net/wireless/orinoco/hermes_rid.h +++ /dev/null @@ -1,165 +0,0 @@ -#ifndef _HERMES_RID_H -#define _HERMES_RID_H - -/* - * Configuration RIDs - */ -#define HERMES_RID_CNFPORTTYPE 0xFC00 -#define HERMES_RID_CNFOWNMACADDR 0xFC01 -#define HERMES_RID_CNFDESIREDSSID 0xFC02 -#define HERMES_RID_CNFOWNCHANNEL 0xFC03 -#define HERMES_RID_CNFOWNSSID 0xFC04 -#define HERMES_RID_CNFOWNATIMWINDOW 0xFC05 -#define HERMES_RID_CNFSYSTEMSCALE 0xFC06 -#define HERMES_RID_CNFMAXDATALEN 0xFC07 -#define HERMES_RID_CNFWDSADDRESS 0xFC08 -#define HERMES_RID_CNFPMENABLED 0xFC09 -#define HERMES_RID_CNFPMEPS 0xFC0A -#define HERMES_RID_CNFMULTICASTRECEIVE 0xFC0B -#define HERMES_RID_CNFMAXSLEEPDURATION 0xFC0C -#define HERMES_RID_CNFPMHOLDOVERDURATION 0xFC0D -#define HERMES_RID_CNFOWNNAME 0xFC0E -#define HERMES_RID_CNFOWNDTIMPERIOD 0xFC10 -#define HERMES_RID_CNFWDSADDRESS1 0xFC11 -#define HERMES_RID_CNFWDSADDRESS2 0xFC12 -#define HERMES_RID_CNFWDSADDRESS3 0xFC13 -#define HERMES_RID_CNFWDSADDRESS4 0xFC14 -#define HERMES_RID_CNFWDSADDRESS5 0xFC15 -#define HERMES_RID_CNFWDSADDRESS6 0xFC16 -#define HERMES_RID_CNFMULTICASTPMBUFFERING 0xFC17 -#define HERMES_RID_CNFWEPENABLED_AGERE 0xFC20 -#define HERMES_RID_CNFAUTHENTICATION_AGERE 0xFC21 -#define HERMES_RID_CNFMANDATORYBSSID_SYMBOL 0xFC21 -#define HERMES_RID_CNFDROPUNENCRYPTED 0xFC22 -#define HERMES_RID_CNFWEPDEFAULTKEYID 0xFC23 -#define HERMES_RID_CNFDEFAULTKEY0 0xFC24 -#define HERMES_RID_CNFDEFAULTKEY1 0xFC25 -#define HERMES_RID_CNFMWOROBUST_AGERE 0xFC25 -#define HERMES_RID_CNFDEFAULTKEY2 0xFC26 -#define HERMES_RID_CNFDEFAULTKEY3 0xFC27 -#define HERMES_RID_CNFWEPFLAGS_INTERSIL 0xFC28 -#define HERMES_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 -#define HERMES_RID_CNFAUTHENTICATION 0xFC2A -#define HERMES_RID_CNFMAXASSOCSTA 0xFC2B -#define HERMES_RID_CNFKEYLENGTH_SYMBOL 0xFC2B -#define HERMES_RID_CNFTXCONTROL 0xFC2C -#define HERMES_RID_CNFROAMINGMODE 0xFC2D -#define HERMES_RID_CNFHOSTAUTHENTICATION 0xFC2E -#define HERMES_RID_CNFRCVCRCERROR 0xFC30 -#define HERMES_RID_CNFMMLIFE 0xFC31 -#define HERMES_RID_CNFALTRETRYCOUNT 0xFC32 -#define HERMES_RID_CNFBEACONINT 0xFC33 -#define HERMES_RID_CNFAPPCFINFO 0xFC34 -#define HERMES_RID_CNFSTAPCFINFO 0xFC35 -#define HERMES_RID_CNFPRIORITYQUSAGE 0xFC37 -#define HERMES_RID_CNFTIMCTRL 0xFC40 -#define HERMES_RID_CNFTHIRTY2TALLY 0xFC42 -#define HERMES_RID_CNFENHSECURITY 0xFC43 -#define HERMES_RID_CNFGROUPADDRESSES 0xFC80 -#define HERMES_RID_CNFCREATEIBSS 0xFC81 -#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD 0xFC82 -#define HERMES_RID_CNFRTSTHRESHOLD 0xFC83 -#define HERMES_RID_CNFTXRATECONTROL 0xFC84 -#define HERMES_RID_CNFPROMISCUOUSMODE 0xFC85 -#define HERMES_RID_CNFBASICRATES_SYMBOL 0xFC8A -#define HERMES_RID_CNFPREAMBLE_SYMBOL 0xFC8C -#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD0 0xFC90 -#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD1 0xFC91 -#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD2 0xFC92 -#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD3 0xFC93 -#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD4 0xFC94 -#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD5 0xFC95 -#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD6 0xFC96 -#define HERMES_RID_CNFRTSTHRESHOLD0 0xFC97 -#define HERMES_RID_CNFRTSTHRESHOLD1 0xFC98 -#define HERMES_RID_CNFRTSTHRESHOLD2 0xFC99 -#define HERMES_RID_CNFRTSTHRESHOLD3 0xFC9A -#define HERMES_RID_CNFRTSTHRESHOLD4 0xFC9B -#define HERMES_RID_CNFRTSTHRESHOLD5 0xFC9C -#define HERMES_RID_CNFRTSTHRESHOLD6 0xFC9D -#define HERMES_RID_CNFHOSTSCAN_SYMBOL 0xFCAB -#define HERMES_RID_CNFSHORTPREAMBLE 0xFCB0 -#define HERMES_RID_CNFWEPKEYS_AGERE 0xFCB0 -#define HERMES_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 -#define HERMES_RID_CNFTXKEY_AGERE 0xFCB1 -#define HERMES_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 -#define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2 -#define HERMES_RID_CNFBASICRATES 0xFCB3 -#define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4 -#define HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE 0xFCB4 -#define HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE 0xFCB5 -#define HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE 0xFCB6 -#define HERMES_RID_CNFADDMAPPEDTKIPKEY_AGERE 0xFCB7 -#define HERMES_RID_CNFREMMAPPEDTKIPKEY_AGERE 0xFCB8 -#define HERMES_RID_CNFSETWPACAPABILITIES_AGERE 0xFCB9 -#define HERMES_RID_CNFCACHEDPMKADDRESS 0xFCBA -#define HERMES_RID_CNFREMOVEPMKADDRESS 0xFCBB -#define HERMES_RID_CNFSCANCHANNELS2GHZ 0xFCC2 -#define HERMES_RID_CNFDISASSOCIATE 0xFCC8 -#define HERMES_RID_CNFTICKTIME 0xFCE0 -#define HERMES_RID_CNFSCANREQUEST 0xFCE1 -#define HERMES_RID_CNFJOINREQUEST 0xFCE2 -#define HERMES_RID_CNFAUTHENTICATESTATION 0xFCE3 -#define HERMES_RID_CNFCHANNELINFOREQUEST 0xFCE4 -#define HERMES_RID_CNFHOSTSCAN 0xFCE5 - -/* - * Information RIDs - */ -#define HERMES_RID_MAXLOADTIME 0xFD00 -#define HERMES_RID_DOWNLOADBUFFER 0xFD01 -#define HERMES_RID_PRIID 0xFD02 -#define HERMES_RID_PRISUPRANGE 0xFD03 -#define HERMES_RID_CFIACTRANGES 0xFD04 -#define HERMES_RID_NICSERNUM 0xFD0A -#define HERMES_RID_NICID 0xFD0B -#define HERMES_RID_MFISUPRANGE 0xFD0C -#define HERMES_RID_CFISUPRANGE 0xFD0D -#define HERMES_RID_CHANNELLIST 0xFD10 -#define HERMES_RID_REGULATORYDOMAINS 0xFD11 -#define HERMES_RID_TEMPTYPE 0xFD12 -#define HERMES_RID_CIS 0xFD13 -#define HERMES_RID_STAID 0xFD20 -#define HERMES_RID_STASUPRANGE 0xFD21 -#define HERMES_RID_MFIACTRANGES 0xFD22 -#define HERMES_RID_CFIACTRANGES2 0xFD23 -#define HERMES_RID_SECONDARYVERSION_SYMBOL 0xFD24 -#define HERMES_RID_PORTSTATUS 0xFD40 -#define HERMES_RID_CURRENTSSID 0xFD41 -#define HERMES_RID_CURRENTBSSID 0xFD42 -#define HERMES_RID_COMMSQUALITY 0xFD43 -#define HERMES_RID_CURRENTTXRATE 0xFD44 -#define HERMES_RID_CURRENTBEACONINTERVAL 0xFD45 -#define HERMES_RID_CURRENTSCALETHRESHOLDS 0xFD46 -#define HERMES_RID_PROTOCOLRSPTIME 0xFD47 -#define HERMES_RID_SHORTRETRYLIMIT 0xFD48 -#define HERMES_RID_LONGRETRYLIMIT 0xFD49 -#define HERMES_RID_MAXTRANSMITLIFETIME 0xFD4A -#define HERMES_RID_MAXRECEIVELIFETIME 0xFD4B -#define HERMES_RID_CFPOLLABLE 0xFD4C -#define HERMES_RID_AUTHENTICATIONALGORITHMS 0xFD4D -#define HERMES_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F -#define HERMES_RID_DBMCOMMSQUALITY_INTERSIL 0xFD51 -#define HERMES_RID_CURRENTTXRATE1 0xFD80 -#define HERMES_RID_CURRENTTXRATE2 0xFD81 -#define HERMES_RID_CURRENTTXRATE3 0xFD82 -#define HERMES_RID_CURRENTTXRATE4 0xFD83 -#define HERMES_RID_CURRENTTXRATE5 0xFD84 -#define HERMES_RID_CURRENTTXRATE6 0xFD85 -#define HERMES_RID_OWNMACADDR 0xFD86 -#define HERMES_RID_SCANRESULTSTABLE 0xFD88 -#define HERMES_RID_CURRENT_COUNTRY_INFO 0xFD89 -#define HERMES_RID_CURRENT_WPA_IE 0xFD8A -#define HERMES_RID_CURRENT_TKIP_IV 0xFD8B -#define HERMES_RID_CURRENT_ASSOC_REQ_INFO 0xFD8C -#define HERMES_RID_CURRENT_ASSOC_RESP_INFO 0xFD8D -#define HERMES_RID_TXQUEUEEMPTY 0xFD91 -#define HERMES_RID_PHYTYPE 0xFDC0 -#define HERMES_RID_CURRENTCHANNEL 0xFDC1 -#define HERMES_RID_CURRENTPOWERSTATE 0xFDC2 -#define HERMES_RID_CCAMODE 0xFDC3 -#define HERMES_RID_SUPPORTEDDATARATES 0xFDC6 -#define HERMES_RID_BUILDSEQ 0xFFFE -#define HERMES_RID_FWID 0xFFFF - -#endif diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c deleted file mode 100644 index e27e32851f1e..000000000000 --- a/drivers/net/wireless/orinoco/hw.c +++ /dev/null @@ -1,1356 +0,0 @@ -/* Encapsulate basic setting changes and retrieval on Hermes hardware - * - * See copyright notice in main.c - */ -#include -#include -#include -#include -#include -#include -#include "hermes.h" -#include "hermes_rid.h" -#include "orinoco.h" - -#include "hw.h" - -#define SYMBOL_MAX_VER_LEN (14) - -/* Symbol firmware has a bug allocating buffers larger than this */ -#define TX_NICBUF_SIZE_BUG 1585 - -/********************************************************************/ -/* Data tables */ -/********************************************************************/ - -/* This tables gives the actual meanings of the bitrate IDs returned - * by the firmware. */ -static const struct { - int bitrate; /* in 100s of kilobits */ - int automatic; - u16 agere_txratectrl; - u16 intersil_txratectrl; -} bitrate_table[] = { - {110, 1, 3, 15}, /* Entry 0 is the default */ - {10, 0, 1, 1}, - {10, 1, 1, 1}, - {20, 0, 2, 2}, - {20, 1, 6, 3}, - {55, 0, 4, 4}, - {55, 1, 7, 7}, - {110, 0, 5, 8}, -}; -#define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table) - -/* Firmware version encoding */ -struct comp_id { - u16 id, variant, major, minor; -} __packed; - -static inline enum fwtype determine_firmware_type(struct comp_id *nic_id) -{ - if (nic_id->id < 0x8000) - return FIRMWARE_TYPE_AGERE; - else if (nic_id->id == 0x8000 && nic_id->major == 0) - return FIRMWARE_TYPE_SYMBOL; - else - return FIRMWARE_TYPE_INTERSIL; -} - -/* Set priv->firmware type, determine firmware properties - * This function can be called before we have registerred with netdev, - * so all errors go out with dev_* rather than printk - * - * If non-NULL stores a firmware description in fw_name. - * If non-NULL stores a HW version in hw_ver - * - * These are output via generic cfg80211 ethtool support. - */ -int determine_fw_capabilities(struct orinoco_private *priv, - char *fw_name, size_t fw_name_len, - u32 *hw_ver) -{ - struct device *dev = priv->dev; - struct hermes *hw = &priv->hw; - int err; - struct comp_id nic_id, sta_id; - unsigned int firmver; - char tmp[SYMBOL_MAX_VER_LEN + 1] __attribute__((aligned(2))); - - /* Get the hardware version */ - err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id); - if (err) { - dev_err(dev, "Cannot read hardware identity: error %d\n", - err); - return err; - } - - le16_to_cpus(&nic_id.id); - le16_to_cpus(&nic_id.variant); - le16_to_cpus(&nic_id.major); - le16_to_cpus(&nic_id.minor); - dev_info(dev, "Hardware identity %04x:%04x:%04x:%04x\n", - nic_id.id, nic_id.variant, nic_id.major, nic_id.minor); - - if (hw_ver) - *hw_ver = (((nic_id.id & 0xff) << 24) | - ((nic_id.variant & 0xff) << 16) | - ((nic_id.major & 0xff) << 8) | - (nic_id.minor & 0xff)); - - priv->firmware_type = determine_firmware_type(&nic_id); - - /* Get the firmware version */ - err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id); - if (err) { - dev_err(dev, "Cannot read station identity: error %d\n", - err); - return err; - } - - le16_to_cpus(&sta_id.id); - le16_to_cpus(&sta_id.variant); - le16_to_cpus(&sta_id.major); - le16_to_cpus(&sta_id.minor); - dev_info(dev, "Station identity %04x:%04x:%04x:%04x\n", - sta_id.id, sta_id.variant, sta_id.major, sta_id.minor); - - switch (sta_id.id) { - case 0x15: - dev_err(dev, "Primary firmware is active\n"); - return -ENODEV; - case 0x14b: - dev_err(dev, "Tertiary firmware is active\n"); - return -ENODEV; - case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */ - case 0x21: /* Symbol Spectrum24 Trilogy */ - break; - default: - dev_notice(dev, "Unknown station ID, please report\n"); - break; - } - - /* Default capabilities */ - priv->has_sensitivity = 1; - priv->has_mwo = 0; - priv->has_preamble = 0; - priv->has_port3 = 1; - priv->has_ibss = 1; - priv->has_wep = 0; - priv->has_big_wep = 0; - priv->has_alt_txcntl = 0; - priv->has_ext_scan = 0; - priv->has_wpa = 0; - priv->do_fw_download = 0; - - /* Determine capabilities from the firmware version */ - switch (priv->firmware_type) { - case FIRMWARE_TYPE_AGERE: - /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout, - ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */ - if (fw_name) - snprintf(fw_name, fw_name_len, "Lucent/Agere %d.%02d", - sta_id.major, sta_id.minor); - - firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; - - priv->has_ibss = (firmver >= 0x60006); - priv->has_wep = (firmver >= 0x40020); - priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell - Gold cards from the others? */ - priv->has_mwo = (firmver >= 0x60000); - priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ - priv->ibss_port = 1; - priv->has_hostscan = (firmver >= 0x8000a); - priv->do_fw_download = 1; - priv->broken_monitor = (firmver >= 0x80000); - priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ - priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */ - priv->has_wpa = (firmver >= 0x9002a); - /* Tested with Agere firmware : - * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II - * Tested CableTron firmware : 4.32 => Anton */ - break; - case FIRMWARE_TYPE_SYMBOL: - /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */ - /* Intel MAC : 00:02:B3:* */ - /* 3Com MAC : 00:50:DA:* */ - memset(tmp, 0, sizeof(tmp)); - /* Get the Symbol firmware version */ - err = hw->ops->read_ltv(hw, USER_BAP, - HERMES_RID_SECONDARYVERSION_SYMBOL, - SYMBOL_MAX_VER_LEN, NULL, &tmp); - if (err) { - dev_warn(dev, "Error %d reading Symbol firmware info. " - "Wildly guessing capabilities...\n", err); - firmver = 0; - tmp[0] = '\0'; - } else { - /* The firmware revision is a string, the format is - * something like : "V2.20-01". - * Quick and dirty parsing... - Jean II - */ - firmver = ((tmp[1] - '0') << 16) - | ((tmp[3] - '0') << 12) - | ((tmp[4] - '0') << 8) - | ((tmp[6] - '0') << 4) - | (tmp[7] - '0'); - - tmp[SYMBOL_MAX_VER_LEN] = '\0'; - } - - if (fw_name) - snprintf(fw_name, fw_name_len, "Symbol %s", tmp); - - priv->has_ibss = (firmver >= 0x20000); - priv->has_wep = (firmver >= 0x15012); - priv->has_big_wep = (firmver >= 0x20000); - priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) || - (firmver >= 0x29000 && firmver < 0x30000) || - firmver >= 0x31000; - priv->has_preamble = (firmver >= 0x20000); - priv->ibss_port = 4; - - /* Symbol firmware is found on various cards, but - * there has been no attempt to check firmware - * download on non-spectrum_cs based cards. - * - * Given that the Agere firmware download works - * differently, we should avoid doing a firmware - * download with the Symbol algorithm on non-spectrum - * cards. - * - * For now we can identify a spectrum_cs based card - * because it has a firmware reset function. - */ - priv->do_fw_download = (priv->stop_fw != NULL); - - priv->broken_disableport = (firmver == 0x25013) || - (firmver >= 0x30000 && firmver <= 0x31000); - priv->has_hostscan = (firmver >= 0x31001) || - (firmver >= 0x29057 && firmver < 0x30000); - /* Tested with Intel firmware : 0x20015 => Jean II */ - /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ - break; - case FIRMWARE_TYPE_INTERSIL: - /* D-Link, Linksys, Adtron, ZoomAir, and many others... - * Samsung, Compaq 100/200 and Proxim are slightly - * different and less well tested */ - /* D-Link MAC : 00:40:05:* */ - /* Addtron MAC : 00:90:D1:* */ - if (fw_name) - snprintf(fw_name, fw_name_len, "Intersil %d.%d.%d", - sta_id.major, sta_id.minor, sta_id.variant); - - firmver = ((unsigned long)sta_id.major << 16) | - ((unsigned long)sta_id.minor << 8) | sta_id.variant; - - priv->has_ibss = (firmver >= 0x000700); /* FIXME */ - priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); - priv->has_pm = (firmver >= 0x000700); - priv->has_hostscan = (firmver >= 0x010301); - - if (firmver >= 0x000800) - priv->ibss_port = 0; - else { - dev_notice(dev, "Intersil firmware earlier than v0.8.x" - " - several features not supported\n"); - priv->ibss_port = 1; - } - break; - } - if (fw_name) - dev_info(dev, "Firmware determined as %s\n", fw_name); - -#ifndef CONFIG_HERMES_PRISM - if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { - dev_err(dev, "Support for Prism chipset is not enabled\n"); - return -ENODEV; - } -#endif - - return 0; -} - -/* Read settings from EEPROM into our private structure. - * MAC address gets dropped into callers buffer - * Can be called before netdev registration. - */ -int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr) -{ - struct device *dev = priv->dev; - struct hermes_idstring nickbuf; - struct hermes *hw = &priv->hw; - int len; - int err; - u16 reclen; - - /* Get the MAC address */ - err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, - ETH_ALEN, NULL, dev_addr); - if (err) { - dev_warn(dev, "Failed to read MAC address!\n"); - goto out; - } - - dev_dbg(dev, "MAC address %pM\n", dev_addr); - - /* Get the station name */ - err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, - sizeof(nickbuf), &reclen, &nickbuf); - if (err) { - dev_err(dev, "failed to read station name\n"); - goto out; - } - if (nickbuf.len) - len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len)); - else - len = min(IW_ESSID_MAX_SIZE, 2 * reclen); - memcpy(priv->nick, &nickbuf.val, len); - priv->nick[len] = '\0'; - - dev_dbg(dev, "Station name \"%s\"\n", priv->nick); - - /* Get allowed channels */ - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST, - &priv->channel_mask); - if (err) { - dev_err(dev, "Failed to read channel list!\n"); - goto out; - } - - /* Get initial AP density */ - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, - &priv->ap_density); - if (err || priv->ap_density < 1 || priv->ap_density > 3) - priv->has_sensitivity = 0; - - /* Get initial RTS threshold */ - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, - &priv->rts_thresh); - if (err) { - dev_err(dev, "Failed to read RTS threshold!\n"); - goto out; - } - - /* Get initial fragmentation settings */ - if (priv->has_mwo) - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFMWOROBUST_AGERE, - &priv->mwo_robust); - else - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, - &priv->frag_thresh); - if (err) { - dev_err(dev, "Failed to read fragmentation settings!\n"); - goto out; - } - - /* Power management setup */ - if (priv->has_pm) { - priv->pm_on = 0; - priv->pm_mcast = 1; - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFMAXSLEEPDURATION, - &priv->pm_period); - if (err) { - dev_err(dev, "Failed to read power management " - "period!\n"); - goto out; - } - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFPMHOLDOVERDURATION, - &priv->pm_timeout); - if (err) { - dev_err(dev, "Failed to read power management " - "timeout!\n"); - goto out; - } - } - - /* Preamble setup */ - if (priv->has_preamble) { - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFPREAMBLE_SYMBOL, - &priv->preamble); - if (err) { - dev_err(dev, "Failed to read preamble setup\n"); - goto out; - } - } - - /* Retry settings */ - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, - &priv->short_retry_limit); - if (err) { - dev_err(dev, "Failed to read short retry limit\n"); - goto out; - } - - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT, - &priv->long_retry_limit); - if (err) { - dev_err(dev, "Failed to read long retry limit\n"); - goto out; - } - - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME, - &priv->retry_lifetime); - if (err) { - dev_err(dev, "Failed to read max retry lifetime\n"); - goto out; - } - -out: - return err; -} - -/* Can be called before netdev registration */ -int orinoco_hw_allocate_fid(struct orinoco_private *priv) -{ - struct device *dev = priv->dev; - struct hermes *hw = &priv->hw; - int err; - - err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid); - if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) { - /* Try workaround for old Symbol firmware bug */ - priv->nicbuf_size = TX_NICBUF_SIZE_BUG; - err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid); - - dev_warn(dev, "Firmware ALLOC bug detected " - "(old Symbol firmware?). Work around %s\n", - err ? "failed!" : "ok."); - } - - return err; -} - -int orinoco_get_bitratemode(int bitrate, int automatic) -{ - int ratemode = -1; - int i; - - if ((bitrate != 10) && (bitrate != 20) && - (bitrate != 55) && (bitrate != 110)) - return ratemode; - - for (i = 0; i < BITRATE_TABLE_SIZE; i++) { - if ((bitrate_table[i].bitrate == bitrate) && - (bitrate_table[i].automatic == automatic)) { - ratemode = i; - break; - } - } - return ratemode; -} - -void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic) -{ - BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE)); - - *bitrate = bitrate_table[ratemode].bitrate * 100000; - *automatic = bitrate_table[ratemode].automatic; -} - -int orinoco_hw_program_rids(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - struct wireless_dev *wdev = netdev_priv(dev); - struct hermes *hw = &priv->hw; - int err; - struct hermes_idstring idbuf; - - /* Set the MAC address */ - err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, - HERMES_BYTES_TO_RECLEN(ETH_ALEN), - dev->dev_addr); - if (err) { - printk(KERN_ERR "%s: Error %d setting MAC address\n", - dev->name, err); - return err; - } - - /* Set up the link mode */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, - priv->port_type); - if (err) { - printk(KERN_ERR "%s: Error %d setting port type\n", - dev->name, err); - return err; - } - /* Set the channel/frequency */ - if (priv->channel != 0 && priv->iw_mode != NL80211_IFTYPE_STATION) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFOWNCHANNEL, - priv->channel); - if (err) { - printk(KERN_ERR "%s: Error %d setting channel %d\n", - dev->name, err, priv->channel); - return err; - } - } - - if (priv->has_ibss) { - u16 createibss; - - if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) { - printk(KERN_WARNING "%s: This firmware requires an " - "ESSID in IBSS-Ad-Hoc mode.\n", dev->name); - /* With wvlan_cs, in this case, we would crash. - * hopefully, this driver will behave better... - * Jean II */ - createibss = 0; - } else { - createibss = priv->createibss; - } - - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFCREATEIBSS, - createibss); - if (err) { - printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", - dev->name, err); - return err; - } - } - - /* Set the desired BSSID */ - err = __orinoco_hw_set_wap(priv); - if (err) { - printk(KERN_ERR "%s: Error %d setting AP address\n", - dev->name, err); - return err; - } - - /* Set the desired ESSID */ - idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); - memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); - /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ - err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, - HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2), - &idbuf); - if (err) { - printk(KERN_ERR "%s: Error %d setting OWNSSID\n", - dev->name, err); - return err; - } - err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, - HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid) + 2), - &idbuf); - if (err) { - printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", - dev->name, err); - return err; - } - - /* Set the station name */ - idbuf.len = cpu_to_le16(strlen(priv->nick)); - memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val)); - err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, - HERMES_BYTES_TO_RECLEN(strlen(priv->nick) + 2), - &idbuf); - if (err) { - printk(KERN_ERR "%s: Error %d setting nickname\n", - dev->name, err); - return err; - } - - /* Set AP density */ - if (priv->has_sensitivity) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFSYSTEMSCALE, - priv->ap_density); - if (err) { - printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " - "Disabling sensitivity control\n", - dev->name, err); - - priv->has_sensitivity = 0; - } - } - - /* Set RTS threshold */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, - priv->rts_thresh); - if (err) { - printk(KERN_ERR "%s: Error %d setting RTS threshold\n", - dev->name, err); - return err; - } - - /* Set fragmentation threshold or MWO robustness */ - if (priv->has_mwo) - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFMWOROBUST_AGERE, - priv->mwo_robust); - else - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, - priv->frag_thresh); - if (err) { - printk(KERN_ERR "%s: Error %d setting fragmentation\n", - dev->name, err); - return err; - } - - /* Set bitrate */ - err = __orinoco_hw_set_bitrate(priv); - if (err) { - printk(KERN_ERR "%s: Error %d setting bitrate\n", - dev->name, err); - return err; - } - - /* Set power management */ - if (priv->has_pm) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFPMENABLED, - priv->pm_on); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFMULTICASTRECEIVE, - priv->pm_mcast); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFMAXSLEEPDURATION, - priv->pm_period); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFPMHOLDOVERDURATION, - priv->pm_timeout); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - } - - /* Set preamble - only for Symbol so far... */ - if (priv->has_preamble) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFPREAMBLE_SYMBOL, - priv->preamble); - if (err) { - printk(KERN_ERR "%s: Error %d setting preamble\n", - dev->name, err); - return err; - } - } - - /* Set up encryption */ - if (priv->has_wep || priv->has_wpa) { - err = __orinoco_hw_setup_enc(priv); - if (err) { - printk(KERN_ERR "%s: Error %d activating encryption\n", - dev->name, err); - return err; - } - } - - if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { - /* Enable monitor mode */ - dev->type = ARPHRD_IEEE80211; - err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | - HERMES_TEST_MONITOR, 0, NULL); - } else { - /* Disable monitor mode */ - dev->type = ARPHRD_ETHER; - err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | - HERMES_TEST_STOP, 0, NULL); - } - if (err) - return err; - - /* Reset promiscuity / multicast*/ - priv->promiscuous = 0; - priv->mc_count = 0; - - /* Record mode change */ - wdev->iftype = priv->iw_mode; - - return 0; -} - -/* Get tsc from the firmware */ -int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc) -{ - struct hermes *hw = &priv->hw; - int err = 0; - u8 tsc_arr[4][ORINOCO_SEQ_LEN]; - - if ((key < 0) || (key >= 4)) - return -EINVAL; - - err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV, - sizeof(tsc_arr), NULL, &tsc_arr); - if (!err) - memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0])); - - return err; -} - -int __orinoco_hw_set_bitrate(struct orinoco_private *priv) -{ - struct hermes *hw = &priv->hw; - int ratemode = priv->bitratemode; - int err = 0; - - if (ratemode >= BITRATE_TABLE_SIZE) { - printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", - priv->ndev->name, ratemode); - return -EINVAL; - } - - switch (priv->firmware_type) { - case FIRMWARE_TYPE_AGERE: - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFTXRATECONTROL, - bitrate_table[ratemode].agere_txratectrl); - break; - case FIRMWARE_TYPE_INTERSIL: - case FIRMWARE_TYPE_SYMBOL: - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFTXRATECONTROL, - bitrate_table[ratemode].intersil_txratectrl); - break; - default: - BUG(); - } - - return err; -} - -int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate) -{ - struct hermes *hw = &priv->hw; - int i; - int err = 0; - u16 val; - - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CURRENTTXRATE, &val); - if (err) - return err; - - switch (priv->firmware_type) { - case FIRMWARE_TYPE_AGERE: /* Lucent style rate */ - /* Note : in Lucent firmware, the return value of - * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s, - * and therefore is totally different from the - * encoding of HERMES_RID_CNFTXRATECONTROL. - * Don't forget that 6Mb/s is really 5.5Mb/s */ - if (val == 6) - *bitrate = 5500000; - else - *bitrate = val * 1000000; - break; - case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */ - case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */ - for (i = 0; i < BITRATE_TABLE_SIZE; i++) - if (bitrate_table[i].intersil_txratectrl == val) { - *bitrate = bitrate_table[i].bitrate * 100000; - break; - } - - if (i >= BITRATE_TABLE_SIZE) { - printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n", - priv->ndev->name, val); - err = -EIO; - } - - break; - default: - BUG(); - } - - return err; -} - -/* Set fixed AP address */ -int __orinoco_hw_set_wap(struct orinoco_private *priv) -{ - int roaming_flag; - int err = 0; - struct hermes *hw = &priv->hw; - - switch (priv->firmware_type) { - case FIRMWARE_TYPE_AGERE: - /* not supported */ - break; - case FIRMWARE_TYPE_INTERSIL: - if (priv->bssid_fixed) - roaming_flag = 2; - else - roaming_flag = 1; - - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFROAMINGMODE, - roaming_flag); - break; - case FIRMWARE_TYPE_SYMBOL: - err = HERMES_WRITE_RECORD(hw, USER_BAP, - HERMES_RID_CNFMANDATORYBSSID_SYMBOL, - &priv->desired_bssid); - break; - } - return err; -} - -/* Change the WEP keys and/or the current keys. Can be called - * either from __orinoco_hw_setup_enc() or directly from - * orinoco_ioctl_setiwencode(). In the later case the association - * with the AP is not broken (if the firmware can handle it), - * which is needed for 802.1x implementations. */ -int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) -{ - struct hermes *hw = &priv->hw; - int err = 0; - int i; - - switch (priv->firmware_type) { - case FIRMWARE_TYPE_AGERE: - { - struct orinoco_key keys[ORINOCO_MAX_KEYS]; - - memset(&keys, 0, sizeof(keys)); - for (i = 0; i < ORINOCO_MAX_KEYS; i++) { - int len = min(priv->keys[i].key_len, - ORINOCO_MAX_KEY_SIZE); - memcpy(&keys[i].data, priv->keys[i].key, len); - if (len > SMALL_KEY_SIZE) - keys[i].len = cpu_to_le16(LARGE_KEY_SIZE); - else if (len > 0) - keys[i].len = cpu_to_le16(SMALL_KEY_SIZE); - else - keys[i].len = cpu_to_le16(0); - } - - err = HERMES_WRITE_RECORD(hw, USER_BAP, - HERMES_RID_CNFWEPKEYS_AGERE, - &keys); - if (err) - return err; - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFTXKEY_AGERE, - priv->tx_key); - if (err) - return err; - break; - } - case FIRMWARE_TYPE_INTERSIL: - case FIRMWARE_TYPE_SYMBOL: - { - int keylen; - - /* Force uniform key length to work around - * firmware bugs */ - keylen = priv->keys[priv->tx_key].key_len; - - if (keylen > LARGE_KEY_SIZE) { - printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", - priv->ndev->name, priv->tx_key, keylen); - return -E2BIG; - } else if (keylen > SMALL_KEY_SIZE) - keylen = LARGE_KEY_SIZE; - else if (keylen > 0) - keylen = SMALL_KEY_SIZE; - else - keylen = 0; - - /* Write all 4 keys */ - for (i = 0; i < ORINOCO_MAX_KEYS; i++) { - u8 key[LARGE_KEY_SIZE] = { 0 }; - - memcpy(key, priv->keys[i].key, - priv->keys[i].key_len); - - err = hw->ops->write_ltv(hw, USER_BAP, - HERMES_RID_CNFDEFAULTKEY0 + i, - HERMES_BYTES_TO_RECLEN(keylen), - key); - if (err) - return err; - } - - /* Write the index of the key used in transmission */ - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFWEPDEFAULTKEYID, - priv->tx_key); - if (err) - return err; - } - break; - } - - return 0; -} - -int __orinoco_hw_setup_enc(struct orinoco_private *priv) -{ - struct hermes *hw = &priv->hw; - int err = 0; - int master_wep_flag; - int auth_flag; - int enc_flag; - - /* Setup WEP keys */ - if (priv->encode_alg == ORINOCO_ALG_WEP) - __orinoco_hw_setup_wepkeys(priv); - - if (priv->wep_restrict) - auth_flag = HERMES_AUTH_SHARED_KEY; - else - auth_flag = HERMES_AUTH_OPEN; - - if (priv->wpa_enabled) - enc_flag = 2; - else if (priv->encode_alg == ORINOCO_ALG_WEP) - enc_flag = 1; - else - enc_flag = 0; - - switch (priv->firmware_type) { - case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ - if (priv->encode_alg == ORINOCO_ALG_WEP) { - /* Enable the shared-key authentication. */ - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFAUTHENTICATION_AGERE, - auth_flag); - } - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFWEPENABLED_AGERE, - enc_flag); - if (err) - return err; - - if (priv->has_wpa) { - /* Set WPA key management */ - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE, - priv->key_mgmt); - if (err) - return err; - } - - break; - - case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */ - case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */ - if (priv->encode_alg == ORINOCO_ALG_WEP) { - if (priv->wep_restrict || - (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)) - master_wep_flag = HERMES_WEP_PRIVACY_INVOKED | - HERMES_WEP_EXCL_UNENCRYPTED; - else - master_wep_flag = HERMES_WEP_PRIVACY_INVOKED; - - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFAUTHENTICATION, - auth_flag); - if (err) - return err; - } else - master_wep_flag = 0; - - if (priv->iw_mode == NL80211_IFTYPE_MONITOR) - master_wep_flag |= HERMES_WEP_HOST_DECRYPT; - - /* Master WEP setting : on/off */ - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFWEPFLAGS_INTERSIL, - master_wep_flag); - if (err) - return err; - - break; - } - - return 0; -} - -/* key must be 32 bytes, including the tx and rx MIC keys. - * rsc must be NULL or up to 8 bytes - * tsc must be NULL or up to 8 bytes - */ -int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, - int set_tx, const u8 *key, const u8 *rsc, - size_t rsc_len, const u8 *tsc, size_t tsc_len) -{ - struct { - __le16 idx; - u8 rsc[ORINOCO_SEQ_LEN]; - u8 key[TKIP_KEYLEN]; - u8 tx_mic[MIC_KEYLEN]; - u8 rx_mic[MIC_KEYLEN]; - u8 tsc[ORINOCO_SEQ_LEN]; - } __packed buf; - struct hermes *hw = &priv->hw; - int ret; - int err; - int k; - u16 xmitting; - - key_idx &= 0x3; - - if (set_tx) - key_idx |= 0x8000; - - buf.idx = cpu_to_le16(key_idx); - memcpy(buf.key, key, - sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic)); - - if (rsc_len > sizeof(buf.rsc)) - rsc_len = sizeof(buf.rsc); - - if (tsc_len > sizeof(buf.tsc)) - tsc_len = sizeof(buf.tsc); - - memset(buf.rsc, 0, sizeof(buf.rsc)); - memset(buf.tsc, 0, sizeof(buf.tsc)); - - if (rsc != NULL) - memcpy(buf.rsc, rsc, rsc_len); - - if (tsc != NULL) - memcpy(buf.tsc, tsc, tsc_len); - else - buf.tsc[4] = 0x10; - - /* Wait up to 100ms for tx queue to empty */ - for (k = 100; k > 0; k--) { - udelay(1000); - ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY, - &xmitting); - if (ret || !xmitting) - break; - } - - if (k == 0) - ret = -ETIMEDOUT; - - err = HERMES_WRITE_RECORD(hw, USER_BAP, - HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE, - &buf); - - return ret ? ret : err; -} - -int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx) -{ - struct hermes *hw = &priv->hw; - int err; - - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE, - key_idx); - if (err) - printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n", - priv->ndev->name, err, key_idx); - return err; -} - -int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, - struct net_device *dev, - int mc_count, int promisc) -{ - struct hermes *hw = &priv->hw; - int err = 0; - - if (promisc != priv->promiscuous) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFPROMISCUOUSMODE, - promisc); - if (err) { - printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n", - priv->ndev->name, err); - } else - priv->promiscuous = promisc; - } - - /* If we're not in promiscuous mode, then we need to set the - * group address if either we want to multicast, or if we were - * multicasting and want to stop */ - if (!promisc && (mc_count || priv->mc_count)) { - struct netdev_hw_addr *ha; - struct hermes_multicast mclist; - int i = 0; - - netdev_for_each_mc_addr(ha, dev) { - if (i == mc_count) - break; - memcpy(mclist.addr[i++], ha->addr, ETH_ALEN); - } - - err = hw->ops->write_ltv(hw, USER_BAP, - HERMES_RID_CNFGROUPADDRESSES, - HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN), - &mclist); - if (err) - printk(KERN_ERR "%s: Error %d setting multicast list.\n", - priv->ndev->name, err); - else - priv->mc_count = mc_count; - } - return err; -} - -/* Return : < 0 -> error code ; >= 0 -> length */ -int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, - char buf[IW_ESSID_MAX_SIZE + 1]) -{ - struct hermes *hw = &priv->hw; - int err = 0; - struct hermes_idstring essidbuf; - char *p = (char *)(&essidbuf.val); - int len; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - if (strlen(priv->desired_essid) > 0) { - /* We read the desired SSID from the hardware rather - than from priv->desired_essid, just in case the - firmware is allowed to change it on us. I'm not - sure about this */ - /* My guess is that the OWNSSID should always be whatever - * we set to the card, whereas CURRENT_SSID is the one that - * may change... - Jean II */ - u16 rid; - - *active = 1; - - rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID : - HERMES_RID_CNFDESIREDSSID; - - err = hw->ops->read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), - NULL, &essidbuf); - if (err) - goto fail_unlock; - } else { - *active = 0; - - err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, - sizeof(essidbuf), NULL, &essidbuf); - if (err) - goto fail_unlock; - } - - len = le16_to_cpu(essidbuf.len); - BUG_ON(len > IW_ESSID_MAX_SIZE); - - memset(buf, 0, IW_ESSID_MAX_SIZE); - memcpy(buf, p, len); - err = len; - - fail_unlock: - orinoco_unlock(priv, &flags); - - return err; -} - -int orinoco_hw_get_freq(struct orinoco_private *priv) -{ - struct hermes *hw = &priv->hw; - int err = 0; - u16 channel; - int freq = 0; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, - &channel); - if (err) - goto out; - - /* Intersil firmware 1.3.5 returns 0 when the interface is down */ - if (channel == 0) { - err = -EBUSY; - goto out; - } - - if ((channel < 1) || (channel > NUM_CHANNELS)) { - printk(KERN_WARNING "%s: Channel out of range (%d)!\n", - priv->ndev->name, channel); - err = -EBUSY; - goto out; - - } - freq = ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ); - - out: - orinoco_unlock(priv, &flags); - - if (err > 0) - err = -EBUSY; - return err ? err : freq; -} - -int orinoco_hw_get_bitratelist(struct orinoco_private *priv, - int *numrates, s32 *rates, int max) -{ - struct hermes *hw = &priv->hw; - struct hermes_idstring list; - unsigned char *p = (unsigned char *)&list.val; - int err = 0; - int num; - int i; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, - sizeof(list), NULL, &list); - orinoco_unlock(priv, &flags); - - if (err) - return err; - - num = le16_to_cpu(list.len); - *numrates = num; - num = min(num, max); - - for (i = 0; i < num; i++) - rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */ - - return 0; -} - -int orinoco_hw_trigger_scan(struct orinoco_private *priv, - const struct cfg80211_ssid *ssid) -{ - struct net_device *dev = priv->ndev; - struct hermes *hw = &priv->hw; - unsigned long flags; - int err = 0; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - /* Scanning with port 0 disabled would fail */ - if (!netif_running(dev)) { - err = -ENETDOWN; - goto out; - } - - /* In monitor mode, the scan results are always empty. - * Probe responses are passed to the driver as received - * frames and could be processed in software. */ - if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { - err = -EOPNOTSUPP; - goto out; - } - - if (priv->has_hostscan) { - switch (priv->firmware_type) { - case FIRMWARE_TYPE_SYMBOL: - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFHOSTSCAN_SYMBOL, - HERMES_HOSTSCAN_SYMBOL_ONCE | - HERMES_HOSTSCAN_SYMBOL_BCAST); - break; - case FIRMWARE_TYPE_INTERSIL: { - __le16 req[3]; - - req[0] = cpu_to_le16(0x3fff); /* All channels */ - req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ - req[2] = 0; /* Any ESSID */ - err = HERMES_WRITE_RECORD(hw, USER_BAP, - HERMES_RID_CNFHOSTSCAN, &req); - break; - } - case FIRMWARE_TYPE_AGERE: - if (ssid->ssid_len > 0) { - struct hermes_idstring idbuf; - size_t len = ssid->ssid_len; - - idbuf.len = cpu_to_le16(len); - memcpy(idbuf.val, ssid->ssid, len); - - err = hw->ops->write_ltv(hw, USER_BAP, - HERMES_RID_CNFSCANSSID_AGERE, - HERMES_BYTES_TO_RECLEN(len + 2), - &idbuf); - } else - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFSCANSSID_AGERE, - 0); /* Any ESSID */ - if (err) - break; - - if (priv->has_ext_scan) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFSCANCHANNELS2GHZ, - 0x7FFF); - if (err) - goto out; - - err = hermes_inquire(hw, - HERMES_INQ_CHANNELINFO); - } else - err = hermes_inquire(hw, HERMES_INQ_SCAN); - - break; - } - } else - err = hermes_inquire(hw, HERMES_INQ_SCAN); - - out: - orinoco_unlock(priv, &flags); - - return err; -} - -/* Disassociate from node with BSSID addr */ -int orinoco_hw_disassociate(struct orinoco_private *priv, - u8 *addr, u16 reason_code) -{ - struct hermes *hw = &priv->hw; - int err; - - struct { - u8 addr[ETH_ALEN]; - __le16 reason_code; - } __packed buf; - - /* Currently only supported by WPA enabled Agere fw */ - if (!priv->has_wpa) - return -EOPNOTSUPP; - - memcpy(buf.addr, addr, ETH_ALEN); - buf.reason_code = cpu_to_le16(reason_code); - err = HERMES_WRITE_RECORD(hw, USER_BAP, - HERMES_RID_CNFDISASSOCIATE, - &buf); - return err; -} - -int orinoco_hw_get_current_bssid(struct orinoco_private *priv, - u8 *addr) -{ - struct hermes *hw = &priv->hw; - int err; - - err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, - ETH_ALEN, NULL, addr); - - return err; -} diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/orinoco/hw.h deleted file mode 100644 index 466d1ede76f1..000000000000 --- a/drivers/net/wireless/orinoco/hw.h +++ /dev/null @@ -1,59 +0,0 @@ -/* Encapsulate basic setting changes on Hermes hardware - * - * See copyright notice in main.c - */ -#ifndef _ORINOCO_HW_H_ -#define _ORINOCO_HW_H_ - -#include -#include -#include - -/* Hardware BAPs */ -#define USER_BAP 0 -#define IRQ_BAP 1 - -/* WEP key sizes */ -#define SMALL_KEY_SIZE 5 -#define LARGE_KEY_SIZE 13 - -/* Number of supported channels */ -#define NUM_CHANNELS 14 - -/* Forward declarations */ -struct orinoco_private; - -int determine_fw_capabilities(struct orinoco_private *priv, char *fw_name, - size_t fw_name_len, u32 *hw_ver); -int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr); -int orinoco_hw_allocate_fid(struct orinoco_private *priv); -int orinoco_get_bitratemode(int bitrate, int automatic); -void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic); - -int orinoco_hw_program_rids(struct orinoco_private *priv); -int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc); -int __orinoco_hw_set_bitrate(struct orinoco_private *priv); -int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate); -int __orinoco_hw_set_wap(struct orinoco_private *priv); -int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv); -int __orinoco_hw_setup_enc(struct orinoco_private *priv); -int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, - int set_tx, const u8 *key, const u8 *rsc, - size_t rsc_len, const u8 *tsc, size_t tsc_len); -int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx); -int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, - struct net_device *dev, - int mc_count, int promisc); -int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, - char buf[IW_ESSID_MAX_SIZE + 1]); -int orinoco_hw_get_freq(struct orinoco_private *priv); -int orinoco_hw_get_bitratelist(struct orinoco_private *priv, - int *numrates, s32 *rates, int max); -int orinoco_hw_trigger_scan(struct orinoco_private *priv, - const struct cfg80211_ssid *ssid); -int orinoco_hw_disassociate(struct orinoco_private *priv, - u8 *addr, u16 reason_code); -int orinoco_hw_get_current_bssid(struct orinoco_private *priv, - u8 *addr); - -#endif /* _ORINOCO_HW_H_ */ diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c deleted file mode 100644 index 7b5c554323c7..000000000000 --- a/drivers/net/wireless/orinoco/main.c +++ /dev/null @@ -1,2429 +0,0 @@ -/* main.c - (formerly known as dldwd_cs.c, orinoco_cs.c and orinoco.c) - * - * A driver for Hermes or Prism 2 chipset based PCMCIA wireless - * adaptors, with Lucent/Agere, Intersil or Symbol firmware. - * - * Current maintainers (as of 29 September 2003) are: - * Pavel Roskin - * and David Gibson - * - * (C) Copyright David Gibson, IBM Corporation 2001-2003. - * Copyright (C) 2000 David Gibson, Linuxcare Australia. - * With some help from : - * Copyright (C) 2001 Jean Tourrilhes, HP Labs - * Copyright (C) 2001 Benjamin Herrenschmidt - * - * Based on dummy_cs.c 1.27 2000/06/12 21:27:25 - * - * Portions based on wvlan_cs.c 1.0.6, Copyright Andreas Neuhaus - * http://www.stud.fh-dortmund.de/~andy/wvlan/ - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License - * at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and - * limitations under the License. - * - * The initial developer of the original code is David A. Hinds - * . Portions created by David - * A. Hinds are Copyright (C) 1999 David A. Hinds. All Rights - * Reserved. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License version 2 (the "GPL"), in - * which case the provisions of the GPL are applicable instead of the - * above. If you wish to allow the use of your version of this file - * only under the terms of the GPL and not to allow others to use your - * version of this file under the MPL, indicate your decision by - * deleting the provisions above and replace them with the notice and - * other provisions required by the GPL. If you do not delete the - * provisions above, a recipient may use your version of this file - * under either the MPL or the GPL. */ - -/* - * TODO - * o Handle de-encapsulation within network layer, provide 802.11 - * headers (patch from Thomas 'Dent' Mirlacher) - * o Fix possible races in SPY handling. - * o Disconnect wireless extensions from fundamental configuration. - * o (maybe) Software WEP support (patch from Stano Meduna). - * o (maybe) Use multiple Tx buffers - driver handling queue - * rather than firmware. - */ - -/* Locking and synchronization: - * - * The basic principle is that everything is serialized through a - * single spinlock, priv->lock. The lock is used in user, bh and irq - * context, so when taken outside hardirq context it should always be - * taken with interrupts disabled. The lock protects both the - * hardware and the struct orinoco_private. - * - * Another flag, priv->hw_unavailable indicates that the hardware is - * unavailable for an extended period of time (e.g. suspended, or in - * the middle of a hard reset). This flag is protected by the - * spinlock. All code which touches the hardware should check the - * flag after taking the lock, and if it is set, give up on whatever - * they are doing and drop the lock again. The orinoco_lock() - * function handles this (it unlocks and returns -EBUSY if - * hw_unavailable is non-zero). - */ - -#define DRIVER_NAME "orinoco" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hermes_rid.h" -#include "hermes_dld.h" -#include "hw.h" -#include "scan.h" -#include "mic.h" -#include "fw.h" -#include "wext.h" -#include "cfg.h" -#include "main.h" - -#include "orinoco.h" - -/********************************************************************/ -/* Module information */ -/********************************************************************/ - -MODULE_AUTHOR("Pavel Roskin & " - "David Gibson "); -MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based " - "and similar wireless cards"); -MODULE_LICENSE("Dual MPL/GPL"); - -/* Level of debugging. Used in the macros in orinoco.h */ -#ifdef ORINOCO_DEBUG -int orinoco_debug = ORINOCO_DEBUG; -EXPORT_SYMBOL(orinoco_debug); -module_param(orinoco_debug, int, 0644); -MODULE_PARM_DESC(orinoco_debug, "Debug level"); -#endif - -static bool suppress_linkstatus; /* = 0 */ -module_param(suppress_linkstatus, bool, 0644); -MODULE_PARM_DESC(suppress_linkstatus, "Don't log link status changes"); - -static int ignore_disconnect; /* = 0 */ -module_param(ignore_disconnect, int, 0644); -MODULE_PARM_DESC(ignore_disconnect, - "Don't report lost link to the network layer"); - -int force_monitor; /* = 0 */ -module_param(force_monitor, int, 0644); -MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions"); - -/********************************************************************/ -/* Internal constants */ -/********************************************************************/ - -/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ -static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; -#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) - -#define ORINOCO_MIN_MTU 256 -#define ORINOCO_MAX_MTU (IEEE80211_MAX_DATA_LEN - ENCAPS_OVERHEAD) - -#define MAX_IRQLOOPS_PER_IRQ 10 -#define MAX_IRQLOOPS_PER_JIFFY (20000 / HZ) /* Based on a guestimate of - * how many events the - * device could - * legitimately generate */ - -#define DUMMY_FID 0xFFFF - -/*#define MAX_MULTICAST(priv) (priv->firmware_type == FIRMWARE_TYPE_AGERE ? \ - HERMES_MAX_MULTICAST : 0)*/ -#define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST) - -#define ORINOCO_INTEN (HERMES_EV_RX | HERMES_EV_ALLOC \ - | HERMES_EV_TX | HERMES_EV_TXEXC \ - | HERMES_EV_WTERR | HERMES_EV_INFO \ - | HERMES_EV_INFDROP) - -/********************************************************************/ -/* Data types */ -/********************************************************************/ - -/* Beginning of the Tx descriptor, used in TxExc handling */ -struct hermes_txexc_data { - struct hermes_tx_descriptor desc; - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; -} __packed; - -/* Rx frame header except compatibility 802.3 header */ -struct hermes_rx_descriptor { - /* Control */ - __le16 status; - __le32 time; - u8 silence; - u8 signal; - u8 rate; - u8 rxflow; - __le32 reserved; - - /* 802.11 header */ - __le16 frame_ctl; - __le16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - __le16 seq_ctl; - u8 addr4[ETH_ALEN]; - - /* Data length */ - __le16 data_len; -} __packed; - -struct orinoco_rx_data { - struct hermes_rx_descriptor *desc; - struct sk_buff *skb; - struct list_head list; -}; - -struct orinoco_scan_data { - void *buf; - size_t len; - int type; - struct list_head list; -}; - -/********************************************************************/ -/* Function prototypes */ -/********************************************************************/ - -static int __orinoco_set_multicast_list(struct net_device *dev); -static int __orinoco_up(struct orinoco_private *priv); -static int __orinoco_down(struct orinoco_private *priv); -static int __orinoco_commit(struct orinoco_private *priv); - -/********************************************************************/ -/* Internal helper functions */ -/********************************************************************/ - -void set_port_type(struct orinoco_private *priv) -{ - switch (priv->iw_mode) { - case NL80211_IFTYPE_STATION: - priv->port_type = 1; - priv->createibss = 0; - break; - case NL80211_IFTYPE_ADHOC: - if (priv->prefer_port3) { - priv->port_type = 3; - priv->createibss = 0; - } else { - priv->port_type = priv->ibss_port; - priv->createibss = 1; - } - break; - case NL80211_IFTYPE_MONITOR: - priv->port_type = 3; - priv->createibss = 0; - break; - default: - printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", - priv->ndev->name); - } -} - -/********************************************************************/ -/* Device methods */ -/********************************************************************/ - -int orinoco_open(struct net_device *dev) -{ - struct orinoco_private *priv = ndev_priv(dev); - unsigned long flags; - int err; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - err = __orinoco_up(priv); - - if (!err) - priv->open = 1; - - orinoco_unlock(priv, &flags); - - return err; -} -EXPORT_SYMBOL(orinoco_open); - -int orinoco_stop(struct net_device *dev) -{ - struct orinoco_private *priv = ndev_priv(dev); - int err = 0; - - /* We mustn't use orinoco_lock() here, because we need to be - able to close the interface even if hw_unavailable is set - (e.g. as we're released after a PC Card removal) */ - orinoco_lock_irq(priv); - - priv->open = 0; - - err = __orinoco_down(priv); - - orinoco_unlock_irq(priv); - - return err; -} -EXPORT_SYMBOL(orinoco_stop); - -struct net_device_stats *orinoco_get_stats(struct net_device *dev) -{ - struct orinoco_private *priv = ndev_priv(dev); - - return &priv->stats; -} -EXPORT_SYMBOL(orinoco_get_stats); - -void orinoco_set_multicast_list(struct net_device *dev) -{ - struct orinoco_private *priv = ndev_priv(dev); - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) { - printk(KERN_DEBUG "%s: orinoco_set_multicast_list() " - "called when hw_unavailable\n", dev->name); - return; - } - - __orinoco_set_multicast_list(dev); - orinoco_unlock(priv, &flags); -} -EXPORT_SYMBOL(orinoco_set_multicast_list); - -int orinoco_change_mtu(struct net_device *dev, int new_mtu) -{ - struct orinoco_private *priv = ndev_priv(dev); - - if ((new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU)) - return -EINVAL; - - /* MTU + encapsulation + header length */ - if ((new_mtu + ENCAPS_OVERHEAD + sizeof(struct ieee80211_hdr)) > - (priv->nicbuf_size - ETH_HLEN)) - return -EINVAL; - - dev->mtu = new_mtu; - - return 0; -} -EXPORT_SYMBOL(orinoco_change_mtu); - -/********************************************************************/ -/* Tx path */ -/********************************************************************/ - -/* Add encapsulation and MIC to the existing SKB. - * The main xmit routine will then send the whole lot to the card. - * Need 8 bytes headroom - * Need 8 bytes tailroom - * - * With encapsulated ethernet II frame - * -------- - * 803.3 header (14 bytes) - * dst[6] - * -------- src[6] - * 803.3 header (14 bytes) len[2] - * dst[6] 803.2 header (8 bytes) - * src[6] encaps[6] - * len[2] <- leave alone -> len[2] - * -------- -------- <-- 0 - * Payload Payload - * ... ... - * - * -------- -------- - * MIC (8 bytes) - * -------- - * - * returns 0 on success, -ENOMEM on error. - */ -int orinoco_process_xmit_skb(struct sk_buff *skb, - struct net_device *dev, - struct orinoco_private *priv, - int *tx_control, - u8 *mic_buf) -{ - struct orinoco_tkip_key *key; - struct ethhdr *eh; - int do_mic; - - key = (struct orinoco_tkip_key *) priv->keys[priv->tx_key].key; - - do_mic = ((priv->encode_alg == ORINOCO_ALG_TKIP) && - (key != NULL)); - - if (do_mic) - *tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) | - HERMES_TXCTRL_MIC; - - eh = (struct ethhdr *)skb->data; - - /* Encapsulate Ethernet-II frames */ - if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */ - struct header_struct { - struct ethhdr eth; /* 802.3 header */ - u8 encap[6]; /* 802.2 header */ - } __packed hdr; - int len = skb->len + sizeof(encaps_hdr) - (2 * ETH_ALEN); - - if (skb_headroom(skb) < ENCAPS_OVERHEAD) { - if (net_ratelimit()) - printk(KERN_ERR - "%s: Not enough headroom for 802.2 headers %d\n", - dev->name, skb_headroom(skb)); - return -ENOMEM; - } - - /* Fill in new header */ - memcpy(&hdr.eth, eh, 2 * ETH_ALEN); - hdr.eth.h_proto = htons(len); - memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr)); - - /* Make room for the new header, and copy it in */ - eh = (struct ethhdr *) skb_push(skb, ENCAPS_OVERHEAD); - memcpy(eh, &hdr, sizeof(hdr)); - } - - /* Calculate Michael MIC */ - if (do_mic) { - size_t len = skb->len - ETH_HLEN; - u8 *mic = &mic_buf[0]; - - /* Have to write to an even address, so copy the spare - * byte across */ - if (skb->len % 2) { - *mic = skb->data[skb->len - 1]; - mic++; - } - - orinoco_mic(priv->tx_tfm_mic, key->tx_mic, - eh->h_dest, eh->h_source, 0 /* priority */, - skb->data + ETH_HLEN, - len, mic); - } - - return 0; -} -EXPORT_SYMBOL(orinoco_process_xmit_skb); - -static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct net_device_stats *stats = &priv->stats; - struct hermes *hw = &priv->hw; - int err = 0; - u16 txfid = priv->txfid; - int tx_control; - unsigned long flags; - u8 mic_buf[MICHAEL_MIC_LEN + 1]; - - if (!netif_running(dev)) { - printk(KERN_ERR "%s: Tx on stopped device!\n", - dev->name); - return NETDEV_TX_BUSY; - } - - if (netif_queue_stopped(dev)) { - printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", - dev->name); - return NETDEV_TX_BUSY; - } - - if (orinoco_lock(priv, &flags) != 0) { - printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n", - dev->name); - return NETDEV_TX_BUSY; - } - - if (!netif_carrier_ok(dev) || - (priv->iw_mode == NL80211_IFTYPE_MONITOR)) { - /* Oops, the firmware hasn't established a connection, - silently drop the packet (this seems to be the - safest approach). */ - goto drop; - } - - /* Check packet length */ - if (skb->len < ETH_HLEN) - goto drop; - - tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; - - err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control, - &mic_buf[0]); - if (err) - goto drop; - - if (priv->has_alt_txcntl) { - /* WPA enabled firmwares have tx_cntl at the end of - * the 802.11 header. So write zeroed descriptor and - * 802.11 header at the same time - */ - char desc[HERMES_802_3_OFFSET]; - __le16 *txcntl = (__le16 *) &desc[HERMES_TXCNTL2_OFFSET]; - - memset(&desc, 0, sizeof(desc)); - - *txcntl = cpu_to_le16(tx_control); - err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), - txfid, 0); - if (err) { - if (net_ratelimit()) - printk(KERN_ERR "%s: Error %d writing Tx " - "descriptor to BAP\n", dev->name, err); - goto busy; - } - } else { - struct hermes_tx_descriptor desc; - - memset(&desc, 0, sizeof(desc)); - - desc.tx_control = cpu_to_le16(tx_control); - err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), - txfid, 0); - if (err) { - if (net_ratelimit()) - printk(KERN_ERR "%s: Error %d writing Tx " - "descriptor to BAP\n", dev->name, err); - goto busy; - } - - /* Clear the 802.11 header and data length fields - some - * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused - * if this isn't done. */ - hermes_clear_words(hw, HERMES_DATA0, - HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); - } - - err = hw->ops->bap_pwrite(hw, USER_BAP, skb->data, skb->len, - txfid, HERMES_802_3_OFFSET); - if (err) { - printk(KERN_ERR "%s: Error %d writing packet to BAP\n", - dev->name, err); - goto busy; - } - - if (tx_control & HERMES_TXCTRL_MIC) { - size_t offset = HERMES_802_3_OFFSET + skb->len; - size_t len = MICHAEL_MIC_LEN; - - if (offset % 2) { - offset--; - len++; - } - err = hw->ops->bap_pwrite(hw, USER_BAP, &mic_buf[0], len, - txfid, offset); - if (err) { - printk(KERN_ERR "%s: Error %d writing MIC to BAP\n", - dev->name, err); - goto busy; - } - } - - /* Finally, we actually initiate the send */ - netif_stop_queue(dev); - - err = hw->ops->cmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, - txfid, NULL); - if (err) { - netif_start_queue(dev); - if (net_ratelimit()) - printk(KERN_ERR "%s: Error %d transmitting packet\n", - dev->name, err); - goto busy; - } - - stats->tx_bytes += HERMES_802_3_OFFSET + skb->len; - goto ok; - - drop: - stats->tx_errors++; - stats->tx_dropped++; - - ok: - orinoco_unlock(priv, &flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - - busy: - if (err == -EIO) - schedule_work(&priv->reset_work); - orinoco_unlock(priv, &flags); - return NETDEV_TX_BUSY; -} - -static void __orinoco_ev_alloc(struct net_device *dev, struct hermes *hw) -{ - struct orinoco_private *priv = ndev_priv(dev); - u16 fid = hermes_read_regn(hw, ALLOCFID); - - if (fid != priv->txfid) { - if (fid != DUMMY_FID) - printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n", - dev->name, fid); - return; - } - - hermes_write_regn(hw, ALLOCFID, DUMMY_FID); -} - -static void __orinoco_ev_tx(struct net_device *dev, struct hermes *hw) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct net_device_stats *stats = &priv->stats; - - stats->tx_packets++; - - netif_wake_queue(dev); - - hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); -} - -static void __orinoco_ev_txexc(struct net_device *dev, struct hermes *hw) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct net_device_stats *stats = &priv->stats; - u16 fid = hermes_read_regn(hw, TXCOMPLFID); - u16 status; - struct hermes_txexc_data hdr; - int err = 0; - - if (fid == DUMMY_FID) - return; /* Nothing's really happened */ - - /* Read part of the frame header - we need status and addr1 */ - err = hw->ops->bap_pread(hw, IRQ_BAP, &hdr, - sizeof(struct hermes_txexc_data), - fid, 0); - - hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); - stats->tx_errors++; - - if (err) { - printk(KERN_WARNING "%s: Unable to read descriptor on Tx error " - "(FID=%04X error %d)\n", - dev->name, fid, err); - return; - } - - DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name, - err, fid); - - /* We produce a TXDROP event only for retry or lifetime - * exceeded, because that's the only status that really mean - * that this particular node went away. - * Other errors means that *we* screwed up. - Jean II */ - status = le16_to_cpu(hdr.desc.status); - if (status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) { - union iwreq_data wrqu; - - /* Copy 802.11 dest address. - * We use the 802.11 header because the frame may - * not be 802.3 or may be mangled... - * In Ad-Hoc mode, it will be the node address. - * In managed mode, it will be most likely the AP addr - * User space will figure out how to convert it to - * whatever it needs (IP address or else). - * - Jean II */ - memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN); - wrqu.addr.sa_family = ARPHRD_ETHER; - - /* Send event to user space */ - wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); - } - - netif_wake_queue(dev); -} - -void orinoco_tx_timeout(struct net_device *dev) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct net_device_stats *stats = &priv->stats; - struct hermes *hw = &priv->hw; - - printk(KERN_WARNING "%s: Tx timeout! " - "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n", - dev->name, hermes_read_regn(hw, ALLOCFID), - hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT)); - - stats->tx_errors++; - - schedule_work(&priv->reset_work); -} -EXPORT_SYMBOL(orinoco_tx_timeout); - -/********************************************************************/ -/* Rx path (data frames) */ -/********************************************************************/ - -/* Does the frame have a SNAP header indicating it should be - * de-encapsulated to Ethernet-II? */ -static inline int is_ethersnap(void *_hdr) -{ - u8 *hdr = _hdr; - - /* We de-encapsulate all packets which, a) have SNAP headers - * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header - * and where b) the OUI of the SNAP header is 00:00:00 or - * 00:00:f8 - we need both because different APs appear to use - * different OUIs for some reason */ - return (memcmp(hdr, &encaps_hdr, 5) == 0) - && ((hdr[5] == 0x00) || (hdr[5] == 0xf8)); -} - -static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac, - int level, int noise) -{ - struct iw_quality wstats; - wstats.level = level - 0x95; - wstats.noise = noise - 0x95; - wstats.qual = (level > noise) ? (level - noise) : 0; - wstats.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; - /* Update spy records */ - wireless_spy_update(dev, mac, &wstats); -} - -static void orinoco_stat_gather(struct net_device *dev, - struct sk_buff *skb, - struct hermes_rx_descriptor *desc) -{ - struct orinoco_private *priv = ndev_priv(dev); - - /* Using spy support with lots of Rx packets, like in an - * infrastructure (AP), will really slow down everything, because - * the MAC address must be compared to each entry of the spy list. - * If the user really asks for it (set some address in the - * spy list), we do it, but he will pay the price. - * Note that to get here, you need both WIRELESS_SPY - * compiled in AND some addresses in the list !!! - */ - /* Note : gcc will optimise the whole section away if - * WIRELESS_SPY is not defined... - Jean II */ - if (SPY_NUMBER(priv)) { - orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN, - desc->signal, desc->silence); - } -} - -/* - * orinoco_rx_monitor - handle received monitor frames. - * - * Arguments: - * dev network device - * rxfid received FID - * desc rx descriptor of the frame - * - * Call context: interrupt - */ -static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, - struct hermes_rx_descriptor *desc) -{ - u32 hdrlen = 30; /* return full header by default */ - u32 datalen = 0; - u16 fc; - int err; - int len; - struct sk_buff *skb; - struct orinoco_private *priv = ndev_priv(dev); - struct net_device_stats *stats = &priv->stats; - struct hermes *hw = &priv->hw; - - len = le16_to_cpu(desc->data_len); - - /* Determine the size of the header and the data */ - fc = le16_to_cpu(desc->frame_ctl); - switch (fc & IEEE80211_FCTL_FTYPE) { - case IEEE80211_FTYPE_DATA: - if ((fc & IEEE80211_FCTL_TODS) - && (fc & IEEE80211_FCTL_FROMDS)) - hdrlen = 30; - else - hdrlen = 24; - datalen = len; - break; - case IEEE80211_FTYPE_MGMT: - hdrlen = 24; - datalen = len; - break; - case IEEE80211_FTYPE_CTL: - switch (fc & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_PSPOLL: - case IEEE80211_STYPE_RTS: - case IEEE80211_STYPE_CFEND: - case IEEE80211_STYPE_CFENDACK: - hdrlen = 16; - break; - case IEEE80211_STYPE_CTS: - case IEEE80211_STYPE_ACK: - hdrlen = 10; - break; - } - break; - default: - /* Unknown frame type */ - break; - } - - /* sanity check the length */ - if (datalen > IEEE80211_MAX_DATA_LEN + 12) { - printk(KERN_DEBUG "%s: oversized monitor frame, " - "data length = %d\n", dev->name, datalen); - stats->rx_length_errors++; - goto update_stats; - } - - skb = dev_alloc_skb(hdrlen + datalen); - if (!skb) { - printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n", - dev->name); - goto update_stats; - } - - /* Copy the 802.11 header to the skb */ - memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen); - skb_reset_mac_header(skb); - - /* If any, copy the data from the card to the skb */ - if (datalen > 0) { - err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, datalen), - ALIGN(datalen, 2), rxfid, - HERMES_802_2_OFFSET); - if (err) { - printk(KERN_ERR "%s: error %d reading monitor frame\n", - dev->name, err); - goto drop; - } - } - - skb->dev = dev; - skb->ip_summed = CHECKSUM_NONE; - skb->pkt_type = PACKET_OTHERHOST; - skb->protocol = cpu_to_be16(ETH_P_802_2); - - stats->rx_packets++; - stats->rx_bytes += skb->len; - - netif_rx(skb); - return; - - drop: - dev_kfree_skb_irq(skb); - update_stats: - stats->rx_errors++; - stats->rx_dropped++; -} - -void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct net_device_stats *stats = &priv->stats; - struct iw_statistics *wstats = &priv->wstats; - struct sk_buff *skb = NULL; - u16 rxfid, status; - int length; - struct hermes_rx_descriptor *desc; - struct orinoco_rx_data *rx_data; - int err; - - desc = kmalloc(sizeof(*desc), GFP_ATOMIC); - if (!desc) - goto update_stats; - - rxfid = hermes_read_regn(hw, RXFID); - - err = hw->ops->bap_pread(hw, IRQ_BAP, desc, sizeof(*desc), - rxfid, 0); - if (err) { - printk(KERN_ERR "%s: error %d reading Rx descriptor. " - "Frame dropped.\n", dev->name, err); - goto update_stats; - } - - status = le16_to_cpu(desc->status); - - if (status & HERMES_RXSTAT_BADCRC) { - DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", - dev->name); - stats->rx_crc_errors++; - goto update_stats; - } - - /* Handle frames in monitor mode */ - if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { - orinoco_rx_monitor(dev, rxfid, desc); - goto out; - } - - if (status & HERMES_RXSTAT_UNDECRYPTABLE) { - DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n", - dev->name); - wstats->discard.code++; - goto update_stats; - } - - length = le16_to_cpu(desc->data_len); - - /* Sanity checks */ - if (length < 3) { /* No for even an 802.2 LLC header */ - /* At least on Symbol firmware with PCF we get quite a - lot of these legitimately - Poll frames with no - data. */ - goto out; - } - if (length > IEEE80211_MAX_DATA_LEN) { - printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n", - dev->name, length); - stats->rx_length_errors++; - goto update_stats; - } - - /* Payload size does not include Michael MIC. Increase payload - * size to read it together with the data. */ - if (status & HERMES_RXSTAT_MIC) - length += MICHAEL_MIC_LEN; - - /* We need space for the packet data itself, plus an ethernet - header, plus 2 bytes so we can align the IP header on a - 32bit boundary, plus 1 byte so we can read in odd length - packets from the card, which has an IO granularity of 16 - bits */ - skb = dev_alloc_skb(length + ETH_HLEN + 2 + 1); - if (!skb) { - printk(KERN_WARNING "%s: Can't allocate skb for Rx\n", - dev->name); - goto update_stats; - } - - /* We'll prepend the header, so reserve space for it. The worst - case is no decapsulation, when 802.3 header is prepended and - nothing is removed. 2 is for aligning the IP header. */ - skb_reserve(skb, ETH_HLEN + 2); - - err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, length), - ALIGN(length, 2), rxfid, - HERMES_802_2_OFFSET); - if (err) { - printk(KERN_ERR "%s: error %d reading frame. " - "Frame dropped.\n", dev->name, err); - goto drop; - } - - /* Add desc and skb to rx queue */ - rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC); - if (!rx_data) - goto drop; - - rx_data->desc = desc; - rx_data->skb = skb; - list_add_tail(&rx_data->list, &priv->rx_list); - tasklet_schedule(&priv->rx_tasklet); - - return; - -drop: - dev_kfree_skb_irq(skb); -update_stats: - stats->rx_errors++; - stats->rx_dropped++; -out: - kfree(desc); -} -EXPORT_SYMBOL(__orinoco_ev_rx); - -static void orinoco_rx(struct net_device *dev, - struct hermes_rx_descriptor *desc, - struct sk_buff *skb) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct net_device_stats *stats = &priv->stats; - u16 status, fc; - int length; - struct ethhdr *hdr; - - status = le16_to_cpu(desc->status); - length = le16_to_cpu(desc->data_len); - fc = le16_to_cpu(desc->frame_ctl); - - /* Calculate and check MIC */ - if (status & HERMES_RXSTAT_MIC) { - struct orinoco_tkip_key *key; - int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >> - HERMES_MIC_KEY_ID_SHIFT); - u8 mic[MICHAEL_MIC_LEN]; - u8 *rxmic; - u8 *src = (fc & IEEE80211_FCTL_FROMDS) ? - desc->addr3 : desc->addr2; - - /* Extract Michael MIC from payload */ - rxmic = skb->data + skb->len - MICHAEL_MIC_LEN; - - skb_trim(skb, skb->len - MICHAEL_MIC_LEN); - length -= MICHAEL_MIC_LEN; - - key = (struct orinoco_tkip_key *) priv->keys[key_id].key; - - if (!key) { - printk(KERN_WARNING "%s: Received encrypted frame from " - "%pM using key %i, but key is not installed\n", - dev->name, src, key_id); - goto drop; - } - - orinoco_mic(priv->rx_tfm_mic, key->rx_mic, desc->addr1, src, - 0, /* priority or QoS? */ - skb->data, skb->len, &mic[0]); - - if (memcmp(mic, rxmic, - MICHAEL_MIC_LEN)) { - union iwreq_data wrqu; - struct iw_michaelmicfailure wxmic; - - printk(KERN_WARNING "%s: " - "Invalid Michael MIC in data frame from %pM, " - "using key %i\n", - dev->name, src, key_id); - - /* TODO: update stats */ - - /* Notify userspace */ - memset(&wxmic, 0, sizeof(wxmic)); - wxmic.flags = key_id & IW_MICFAILURE_KEY_ID; - wxmic.flags |= (desc->addr1[0] & 1) ? - IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE; - wxmic.src_addr.sa_family = ARPHRD_ETHER; - memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN); - - (void) orinoco_hw_get_tkip_iv(priv, key_id, - &wxmic.tsc[0]); - - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = sizeof(wxmic); - wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, - (char *) &wxmic); - - goto drop; - } - } - - /* Handle decapsulation - * In most cases, the firmware tell us about SNAP frames. - * For some reason, the SNAP frames sent by LinkSys APs - * are not properly recognised by most firmwares. - * So, check ourselves */ - if (length >= ENCAPS_OVERHEAD && - (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || - ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || - is_ethersnap(skb->data))) { - /* These indicate a SNAP within 802.2 LLC within - 802.11 frame which we'll need to de-encapsulate to - the original EthernetII frame. */ - hdr = (struct ethhdr *)skb_push(skb, - ETH_HLEN - ENCAPS_OVERHEAD); - } else { - /* 802.3 frame - prepend 802.3 header as is */ - hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN); - hdr->h_proto = htons(length); - } - memcpy(hdr->h_dest, desc->addr1, ETH_ALEN); - if (fc & IEEE80211_FCTL_FROMDS) - memcpy(hdr->h_source, desc->addr3, ETH_ALEN); - else - memcpy(hdr->h_source, desc->addr2, ETH_ALEN); - - skb->protocol = eth_type_trans(skb, dev); - skb->ip_summed = CHECKSUM_NONE; - if (fc & IEEE80211_FCTL_TODS) - skb->pkt_type = PACKET_OTHERHOST; - - /* Process the wireless stats if needed */ - orinoco_stat_gather(dev, skb, desc); - - /* Pass the packet to the networking stack */ - netif_rx(skb); - stats->rx_packets++; - stats->rx_bytes += length; - - return; - - drop: - dev_kfree_skb(skb); - stats->rx_errors++; - stats->rx_dropped++; -} - -static void orinoco_rx_isr_tasklet(unsigned long data) -{ - struct orinoco_private *priv = (struct orinoco_private *) data; - struct net_device *dev = priv->ndev; - struct orinoco_rx_data *rx_data, *temp; - struct hermes_rx_descriptor *desc; - struct sk_buff *skb; - unsigned long flags; - - /* orinoco_rx requires the driver lock, and we also need to - * protect priv->rx_list, so just hold the lock over the - * lot. - * - * If orinoco_lock fails, we've unplugged the card. In this - * case just abort. */ - if (orinoco_lock(priv, &flags) != 0) - return; - - /* extract desc and skb from queue */ - list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) { - desc = rx_data->desc; - skb = rx_data->skb; - list_del(&rx_data->list); - kfree(rx_data); - - orinoco_rx(dev, desc, skb); - - kfree(desc); - } - - orinoco_unlock(priv, &flags); -} - -/********************************************************************/ -/* Rx path (info frames) */ -/********************************************************************/ - -static void print_linkstatus(struct net_device *dev, u16 status) -{ - char *s; - - if (suppress_linkstatus) - return; - - switch (status) { - case HERMES_LINKSTATUS_NOT_CONNECTED: - s = "Not Connected"; - break; - case HERMES_LINKSTATUS_CONNECTED: - s = "Connected"; - break; - case HERMES_LINKSTATUS_DISCONNECTED: - s = "Disconnected"; - break; - case HERMES_LINKSTATUS_AP_CHANGE: - s = "AP Changed"; - break; - case HERMES_LINKSTATUS_AP_OUT_OF_RANGE: - s = "AP Out of Range"; - break; - case HERMES_LINKSTATUS_AP_IN_RANGE: - s = "AP In Range"; - break; - case HERMES_LINKSTATUS_ASSOC_FAILED: - s = "Association Failed"; - break; - default: - s = "UNKNOWN"; - } - - printk(KERN_DEBUG "%s: New link status: %s (%04x)\n", - dev->name, s, status); -} - -/* Search scan results for requested BSSID, join it if found */ -static void orinoco_join_ap(struct work_struct *work) -{ - struct orinoco_private *priv = - container_of(work, struct orinoco_private, join_work); - struct net_device *dev = priv->ndev; - struct hermes *hw = &priv->hw; - int err; - unsigned long flags; - struct join_req { - u8 bssid[ETH_ALEN]; - __le16 channel; - } __packed req; - const int atom_len = offsetof(struct prism2_scan_apinfo, atim); - struct prism2_scan_apinfo *atom = NULL; - int offset = 4; - int found = 0; - u8 *buf; - u16 len; - - /* Allocate buffer for scan results */ - buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL); - if (!buf) - return; - - if (orinoco_lock(priv, &flags) != 0) - goto fail_lock; - - /* Sanity checks in case user changed something in the meantime */ - if (!priv->bssid_fixed) - goto out; - - if (strlen(priv->desired_essid) == 0) - goto out; - - /* Read scan results from the firmware */ - err = hw->ops->read_ltv(hw, USER_BAP, - HERMES_RID_SCANRESULTSTABLE, - MAX_SCAN_LEN, &len, buf); - if (err) { - printk(KERN_ERR "%s: Cannot read scan results\n", - dev->name); - goto out; - } - - len = HERMES_RECLEN_TO_BYTES(len); - - /* Go through the scan results looking for the channel of the AP - * we were requested to join */ - for (; offset + atom_len <= len; offset += atom_len) { - atom = (struct prism2_scan_apinfo *) (buf + offset); - if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) { - found = 1; - break; - } - } - - if (!found) { - DEBUG(1, "%s: Requested AP not found in scan results\n", - dev->name); - goto out; - } - - memcpy(req.bssid, priv->desired_bssid, ETH_ALEN); - req.channel = atom->channel; /* both are little-endian */ - err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST, - &req); - if (err) - printk(KERN_ERR "%s: Error issuing join request\n", dev->name); - - out: - orinoco_unlock(priv, &flags); - - fail_lock: - kfree(buf); -} - -/* Send new BSSID to userspace */ -static void orinoco_send_bssid_wevent(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - struct hermes *hw = &priv->hw; - union iwreq_data wrqu; - int err; - - err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, - ETH_ALEN, NULL, wrqu.ap_addr.sa_data); - if (err != 0) - return; - - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - - /* Send event to user space */ - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); -} - -static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - struct hermes *hw = &priv->hw; - union iwreq_data wrqu; - int err; - u8 buf[88]; - u8 *ie; - - if (!priv->has_wpa) - return; - - err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO, - sizeof(buf), NULL, &buf); - if (err != 0) - return; - - ie = orinoco_get_wpa_ie(buf, sizeof(buf)); - if (ie) { - int rem = sizeof(buf) - (ie - &buf[0]); - wrqu.data.length = ie[1] + 2; - if (wrqu.data.length > rem) - wrqu.data.length = rem; - - if (wrqu.data.length) - /* Send event to user space */ - wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, ie); - } -} - -static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - struct hermes *hw = &priv->hw; - union iwreq_data wrqu; - int err; - u8 buf[88]; /* TODO: verify max size or IW_GENERIC_IE_MAX */ - u8 *ie; - - if (!priv->has_wpa) - return; - - err = hw->ops->read_ltv(hw, USER_BAP, - HERMES_RID_CURRENT_ASSOC_RESP_INFO, - sizeof(buf), NULL, &buf); - if (err != 0) - return; - - ie = orinoco_get_wpa_ie(buf, sizeof(buf)); - if (ie) { - int rem = sizeof(buf) - (ie - &buf[0]); - wrqu.data.length = ie[1] + 2; - if (wrqu.data.length > rem) - wrqu.data.length = rem; - - if (wrqu.data.length) - /* Send event to user space */ - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie); - } -} - -static void orinoco_send_wevents(struct work_struct *work) -{ - struct orinoco_private *priv = - container_of(work, struct orinoco_private, wevent_work); - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return; - - orinoco_send_assocreqie_wevent(priv); - orinoco_send_assocrespie_wevent(priv); - orinoco_send_bssid_wevent(priv); - - orinoco_unlock(priv, &flags); -} - -static void qbuf_scan(struct orinoco_private *priv, void *buf, - int len, int type) -{ - struct orinoco_scan_data *sd; - unsigned long flags; - - sd = kmalloc(sizeof(*sd), GFP_ATOMIC); - if (!sd) - return; - - sd->buf = buf; - sd->len = len; - sd->type = type; - - spin_lock_irqsave(&priv->scan_lock, flags); - list_add_tail(&sd->list, &priv->scan_list); - spin_unlock_irqrestore(&priv->scan_lock, flags); - - schedule_work(&priv->process_scan); -} - -static void qabort_scan(struct orinoco_private *priv) -{ - struct orinoco_scan_data *sd; - unsigned long flags; - - sd = kmalloc(sizeof(*sd), GFP_ATOMIC); - if (!sd) - return; - - sd->len = -1; /* Abort */ - - spin_lock_irqsave(&priv->scan_lock, flags); - list_add_tail(&sd->list, &priv->scan_list); - spin_unlock_irqrestore(&priv->scan_lock, flags); - - schedule_work(&priv->process_scan); -} - -static void orinoco_process_scan_results(struct work_struct *work) -{ - struct orinoco_private *priv = - container_of(work, struct orinoco_private, process_scan); - struct orinoco_scan_data *sd, *temp; - unsigned long flags; - void *buf; - int len; - int type; - - spin_lock_irqsave(&priv->scan_lock, flags); - list_for_each_entry_safe(sd, temp, &priv->scan_list, list) { - - buf = sd->buf; - len = sd->len; - type = sd->type; - - list_del(&sd->list); - spin_unlock_irqrestore(&priv->scan_lock, flags); - kfree(sd); - - if (len > 0) { - if (type == HERMES_INQ_CHANNELINFO) - orinoco_add_extscan_result(priv, buf, len); - else - orinoco_add_hostscan_results(priv, buf, len); - - kfree(buf); - } else { - /* Either abort or complete the scan */ - orinoco_scan_done(priv, (len < 0)); - } - - spin_lock_irqsave(&priv->scan_lock, flags); - } - spin_unlock_irqrestore(&priv->scan_lock, flags); -} - -void __orinoco_ev_info(struct net_device *dev, struct hermes *hw) -{ - struct orinoco_private *priv = ndev_priv(dev); - u16 infofid; - struct { - __le16 len; - __le16 type; - } __packed info; - int len, type; - int err; - - /* This is an answer to an INQUIRE command that we did earlier, - * or an information "event" generated by the card - * The controller return to us a pseudo frame containing - * the information in question - Jean II */ - infofid = hermes_read_regn(hw, INFOFID); - - /* Read the info frame header - don't try too hard */ - err = hw->ops->bap_pread(hw, IRQ_BAP, &info, sizeof(info), - infofid, 0); - if (err) { - printk(KERN_ERR "%s: error %d reading info frame. " - "Frame dropped.\n", dev->name, err); - return; - } - - len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len)); - type = le16_to_cpu(info.type); - - switch (type) { - case HERMES_INQ_TALLIES: { - struct hermes_tallies_frame tallies; - struct iw_statistics *wstats = &priv->wstats; - - if (len > sizeof(tallies)) { - printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n", - dev->name, len); - len = sizeof(tallies); - } - - err = hw->ops->bap_pread(hw, IRQ_BAP, &tallies, len, - infofid, sizeof(info)); - if (err) - break; - - /* Increment our various counters */ - /* wstats->discard.nwid - no wrong BSSID stuff */ - wstats->discard.code += - le16_to_cpu(tallies.RxWEPUndecryptable); - if (len == sizeof(tallies)) - wstats->discard.code += - le16_to_cpu(tallies.RxDiscards_WEPICVError) + - le16_to_cpu(tallies.RxDiscards_WEPExcluded); - wstats->discard.misc += - le16_to_cpu(tallies.TxDiscardsWrongSA); - wstats->discard.fragment += - le16_to_cpu(tallies.RxMsgInBadMsgFragments); - wstats->discard.retries += - le16_to_cpu(tallies.TxRetryLimitExceeded); - /* wstats->miss.beacon - no match */ - } - break; - case HERMES_INQ_LINKSTATUS: { - struct hermes_linkstatus linkstatus; - u16 newstatus; - int connected; - - if (priv->iw_mode == NL80211_IFTYPE_MONITOR) - break; - - if (len != sizeof(linkstatus)) { - printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n", - dev->name, len); - break; - } - - err = hw->ops->bap_pread(hw, IRQ_BAP, &linkstatus, len, - infofid, sizeof(info)); - if (err) - break; - newstatus = le16_to_cpu(linkstatus.linkstatus); - - /* Symbol firmware uses "out of range" to signal that - * the hostscan frame can be requested. */ - if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE && - priv->firmware_type == FIRMWARE_TYPE_SYMBOL && - priv->has_hostscan && priv->scan_request) { - hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL); - break; - } - - connected = (newstatus == HERMES_LINKSTATUS_CONNECTED) - || (newstatus == HERMES_LINKSTATUS_AP_CHANGE) - || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE); - - if (connected) - netif_carrier_on(dev); - else if (!ignore_disconnect) - netif_carrier_off(dev); - - if (newstatus != priv->last_linkstatus) { - priv->last_linkstatus = newstatus; - print_linkstatus(dev, newstatus); - /* The info frame contains only one word which is the - * status (see hermes.h). The status is pretty boring - * in itself, that's why we export the new BSSID... - * Jean II */ - schedule_work(&priv->wevent_work); - } - } - break; - case HERMES_INQ_SCAN: - if (!priv->scan_request && priv->bssid_fixed && - priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { - schedule_work(&priv->join_work); - break; - } - /* fall through */ - case HERMES_INQ_HOSTSCAN: - case HERMES_INQ_HOSTSCAN_SYMBOL: { - /* Result of a scanning. Contains information about - * cells in the vicinity - Jean II */ - unsigned char *buf; - - /* Sanity check */ - if (len > 4096) { - printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n", - dev->name, len); - qabort_scan(priv); - break; - } - - /* Allocate buffer for results */ - buf = kmalloc(len, GFP_ATOMIC); - if (buf == NULL) { - /* No memory, so can't printk()... */ - qabort_scan(priv); - break; - } - - /* Read scan data */ - err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) buf, len, - infofid, sizeof(info)); - if (err) { - kfree(buf); - qabort_scan(priv); - break; - } - -#ifdef ORINOCO_DEBUG - { - int i; - printk(KERN_DEBUG "Scan result [%02X", buf[0]); - for (i = 1; i < (len * 2); i++) - printk(":%02X", buf[i]); - printk("]\n"); - } -#endif /* ORINOCO_DEBUG */ - - qbuf_scan(priv, buf, len, type); - } - break; - case HERMES_INQ_CHANNELINFO: - { - struct agere_ext_scan_info *bss; - - if (!priv->scan_request) { - printk(KERN_DEBUG "%s: Got chaninfo without scan, " - "len=%d\n", dev->name, len); - break; - } - - /* An empty result indicates that the scan is complete */ - if (len == 0) { - qbuf_scan(priv, NULL, len, type); - break; - } - - /* Sanity check */ - else if (len < (offsetof(struct agere_ext_scan_info, - data) + 2)) { - /* Drop this result now so we don't have to - * keep checking later */ - printk(KERN_WARNING - "%s: Ext scan results too short (%d bytes)\n", - dev->name, len); - break; - } - - bss = kmalloc(len, GFP_ATOMIC); - if (bss == NULL) - break; - - /* Read scan data */ - err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) bss, len, - infofid, sizeof(info)); - if (err) - kfree(bss); - else - qbuf_scan(priv, bss, len, type); - - break; - } - case HERMES_INQ_SEC_STAT_AGERE: - /* Security status (Agere specific) */ - /* Ignore this frame for now */ - if (priv->firmware_type == FIRMWARE_TYPE_AGERE) - break; - /* fall through */ - default: - printk(KERN_DEBUG "%s: Unknown information frame received: " - "type 0x%04x, length %d\n", dev->name, type, len); - /* We don't actually do anything about it */ - break; - } -} -EXPORT_SYMBOL(__orinoco_ev_info); - -static void __orinoco_ev_infdrop(struct net_device *dev, struct hermes *hw) -{ - if (net_ratelimit()) - printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name); -} - -/********************************************************************/ -/* Internal hardware control routines */ -/********************************************************************/ - -static int __orinoco_up(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - struct hermes *hw = &priv->hw; - int err; - - netif_carrier_off(dev); /* just to make sure */ - - err = __orinoco_commit(priv); - if (err) { - printk(KERN_ERR "%s: Error %d configuring card\n", - dev->name, err); - return err; - } - - /* Fire things up again */ - hermes_set_irqmask(hw, ORINOCO_INTEN); - err = hermes_enable_port(hw, 0); - if (err) { - printk(KERN_ERR "%s: Error %d enabling MAC port\n", - dev->name, err); - return err; - } - - netif_start_queue(dev); - - return 0; -} - -static int __orinoco_down(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - struct hermes *hw = &priv->hw; - int err; - - netif_stop_queue(dev); - - if (!priv->hw_unavailable) { - if (!priv->broken_disableport) { - err = hermes_disable_port(hw, 0); - if (err) { - /* Some firmwares (e.g. Intersil 1.3.x) seem - * to have problems disabling the port, oh - * well, too bad. */ - printk(KERN_WARNING "%s: Error %d disabling MAC port\n", - dev->name, err); - priv->broken_disableport = 1; - } - } - hermes_set_irqmask(hw, 0); - hermes_write_regn(hw, EVACK, 0xffff); - } - - orinoco_scan_done(priv, true); - - /* firmware will have to reassociate */ - netif_carrier_off(dev); - priv->last_linkstatus = 0xffff; - - return 0; -} - -static int orinoco_reinit_firmware(struct orinoco_private *priv) -{ - struct hermes *hw = &priv->hw; - int err; - - err = hw->ops->init(hw); - if (priv->do_fw_download && !err) { - err = orinoco_download(priv); - if (err) - priv->do_fw_download = 0; - } - if (!err) - err = orinoco_hw_allocate_fid(priv); - - return err; -} - -static int -__orinoco_set_multicast_list(struct net_device *dev) -{ - struct orinoco_private *priv = ndev_priv(dev); - int err = 0; - int promisc, mc_count; - - /* The Hermes doesn't seem to have an allmulti mode, so we go - * into promiscuous mode and let the upper levels deal. */ - if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || - (netdev_mc_count(dev) > MAX_MULTICAST(priv))) { - promisc = 1; - mc_count = 0; - } else { - promisc = 0; - mc_count = netdev_mc_count(dev); - } - - err = __orinoco_hw_set_multicast_list(priv, dev, mc_count, promisc); - - return err; -} - -/* This must be called from user context, without locks held - use - * schedule_work() */ -void orinoco_reset(struct work_struct *work) -{ - struct orinoco_private *priv = - container_of(work, struct orinoco_private, reset_work); - struct net_device *dev = priv->ndev; - struct hermes *hw = &priv->hw; - int err; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - /* When the hardware becomes available again, whatever - * detects that is responsible for re-initializing - * it. So no need for anything further */ - return; - - netif_stop_queue(dev); - - /* Shut off interrupts. Depending on what state the hardware - * is in, this might not work, but we'll try anyway */ - hermes_set_irqmask(hw, 0); - hermes_write_regn(hw, EVACK, 0xffff); - - priv->hw_unavailable++; - priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */ - netif_carrier_off(dev); - - orinoco_unlock(priv, &flags); - - /* Scanning support: Notify scan cancellation */ - orinoco_scan_done(priv, true); - - if (priv->hard_reset) { - err = (*priv->hard_reset)(priv); - if (err) { - printk(KERN_ERR "%s: orinoco_reset: Error %d " - "performing hard reset\n", dev->name, err); - goto disable; - } - } - - err = orinoco_reinit_firmware(priv); - if (err) { - printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", - dev->name, err); - goto disable; - } - - /* This has to be called from user context */ - orinoco_lock_irq(priv); - - priv->hw_unavailable--; - - /* priv->open or priv->hw_unavailable might have changed while - * we dropped the lock */ - if (priv->open && (!priv->hw_unavailable)) { - err = __orinoco_up(priv); - if (err) { - printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", - dev->name, err); - } else - dev->trans_start = jiffies; - } - - orinoco_unlock_irq(priv); - - return; - disable: - hermes_set_irqmask(hw, 0); - netif_device_detach(dev); - printk(KERN_ERR "%s: Device has been disabled!\n", dev->name); -} - -static int __orinoco_commit(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - int err = 0; - - /* If we've called commit, we are reconfiguring or bringing the - * interface up. Maintaining countermeasures across this would - * be confusing, so note that we've disabled them. The port will - * be enabled later in orinoco_commit or __orinoco_up. */ - priv->tkip_cm_active = 0; - - err = orinoco_hw_program_rids(priv); - - /* FIXME: what about netif_tx_lock */ - (void) __orinoco_set_multicast_list(dev); - - return err; -} - -/* Ensures configuration changes are applied. May result in a reset. - * The caller should hold priv->lock - */ -int orinoco_commit(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - struct hermes *hw = &priv->hw; - int err; - - if (priv->broken_disableport) { - schedule_work(&priv->reset_work); - return 0; - } - - err = hermes_disable_port(hw, 0); - if (err) { - printk(KERN_WARNING "%s: Unable to disable port " - "while reconfiguring card\n", dev->name); - priv->broken_disableport = 1; - goto out; - } - - err = __orinoco_commit(priv); - if (err) { - printk(KERN_WARNING "%s: Unable to reconfigure card\n", - dev->name); - goto out; - } - - err = hermes_enable_port(hw, 0); - if (err) { - printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", - dev->name); - goto out; - } - - out: - if (err) { - printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); - schedule_work(&priv->reset_work); - err = 0; - } - return err; -} - -/********************************************************************/ -/* Interrupt handler */ -/********************************************************************/ - -static void __orinoco_ev_tick(struct net_device *dev, struct hermes *hw) -{ - printk(KERN_DEBUG "%s: TICK\n", dev->name); -} - -static void __orinoco_ev_wterr(struct net_device *dev, struct hermes *hw) -{ - /* This seems to happen a fair bit under load, but ignoring it - seems to work fine...*/ - printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", - dev->name); -} - -irqreturn_t orinoco_interrupt(int irq, void *dev_id) -{ - struct orinoco_private *priv = dev_id; - struct net_device *dev = priv->ndev; - struct hermes *hw = &priv->hw; - int count = MAX_IRQLOOPS_PER_IRQ; - u16 evstat, events; - /* These are used to detect a runaway interrupt situation. - * - * If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy, - * we panic and shut down the hardware - */ - /* jiffies value the last time we were called */ - static int last_irq_jiffy; /* = 0 */ - static int loops_this_jiffy; /* = 0 */ - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) { - /* If hw is unavailable - we don't know if the irq was - * for us or not */ - return IRQ_HANDLED; - } - - evstat = hermes_read_regn(hw, EVSTAT); - events = evstat & hw->inten; - if (!events) { - orinoco_unlock(priv, &flags); - return IRQ_NONE; - } - - if (jiffies != last_irq_jiffy) - loops_this_jiffy = 0; - last_irq_jiffy = jiffies; - - while (events && count--) { - if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { - printk(KERN_WARNING "%s: IRQ handler is looping too " - "much! Resetting.\n", dev->name); - /* Disable interrupts for now */ - hermes_set_irqmask(hw, 0); - schedule_work(&priv->reset_work); - break; - } - - /* Check the card hasn't been removed */ - if (!hermes_present(hw)) { - DEBUG(0, "orinoco_interrupt(): card removed\n"); - break; - } - - if (events & HERMES_EV_TICK) - __orinoco_ev_tick(dev, hw); - if (events & HERMES_EV_WTERR) - __orinoco_ev_wterr(dev, hw); - if (events & HERMES_EV_INFDROP) - __orinoco_ev_infdrop(dev, hw); - if (events & HERMES_EV_INFO) - __orinoco_ev_info(dev, hw); - if (events & HERMES_EV_RX) - __orinoco_ev_rx(dev, hw); - if (events & HERMES_EV_TXEXC) - __orinoco_ev_txexc(dev, hw); - if (events & HERMES_EV_TX) - __orinoco_ev_tx(dev, hw); - if (events & HERMES_EV_ALLOC) - __orinoco_ev_alloc(dev, hw); - - hermes_write_regn(hw, EVACK, evstat); - - evstat = hermes_read_regn(hw, EVSTAT); - events = evstat & hw->inten; - } - - orinoco_unlock(priv, &flags); - return IRQ_HANDLED; -} -EXPORT_SYMBOL(orinoco_interrupt); - -/********************************************************************/ -/* Power management */ -/********************************************************************/ -#if defined(CONFIG_PM_SLEEP) && !defined(CONFIG_HERMES_CACHE_FW_ON_INIT) -static int orinoco_pm_notifier(struct notifier_block *notifier, - unsigned long pm_event, - void *unused) -{ - struct orinoco_private *priv = container_of(notifier, - struct orinoco_private, - pm_notifier); - - /* All we need to do is cache the firmware before suspend, and - * release it when we come out. - * - * Only need to do this if we're downloading firmware. */ - if (!priv->do_fw_download) - return NOTIFY_DONE; - - switch (pm_event) { - case PM_HIBERNATION_PREPARE: - case PM_SUSPEND_PREPARE: - orinoco_cache_fw(priv, 0); - break; - - case PM_POST_RESTORE: - /* Restore from hibernation failed. We need to clean - * up in exactly the same way, so fall through. */ - case PM_POST_HIBERNATION: - case PM_POST_SUSPEND: - orinoco_uncache_fw(priv); - break; - - case PM_RESTORE_PREPARE: - default: - break; - } - - return NOTIFY_DONE; -} - -static void orinoco_register_pm_notifier(struct orinoco_private *priv) -{ - priv->pm_notifier.notifier_call = orinoco_pm_notifier; - register_pm_notifier(&priv->pm_notifier); -} - -static void orinoco_unregister_pm_notifier(struct orinoco_private *priv) -{ - unregister_pm_notifier(&priv->pm_notifier); -} -#else /* !PM_SLEEP || HERMES_CACHE_FW_ON_INIT */ -#define orinoco_register_pm_notifier(priv) do { } while (0) -#define orinoco_unregister_pm_notifier(priv) do { } while (0) -#endif - -/********************************************************************/ -/* Initialization */ -/********************************************************************/ - -int orinoco_init(struct orinoco_private *priv) -{ - struct device *dev = priv->dev; - struct wiphy *wiphy = priv_to_wiphy(priv); - struct hermes *hw = &priv->hw; - int err = 0; - - /* No need to lock, the hw_unavailable flag is already set in - * alloc_orinocodev() */ - priv->nicbuf_size = IEEE80211_MAX_FRAME_LEN + ETH_HLEN; - - /* Initialize the firmware */ - err = hw->ops->init(hw); - if (err != 0) { - dev_err(dev, "Failed to initialize firmware (err = %d)\n", - err); - goto out; - } - - err = determine_fw_capabilities(priv, wiphy->fw_version, - sizeof(wiphy->fw_version), - &wiphy->hw_version); - if (err != 0) { - dev_err(dev, "Incompatible firmware, aborting\n"); - goto out; - } - - if (priv->do_fw_download) { -#ifdef CONFIG_HERMES_CACHE_FW_ON_INIT - orinoco_cache_fw(priv, 0); -#endif - - err = orinoco_download(priv); - if (err) - priv->do_fw_download = 0; - - /* Check firmware version again */ - err = determine_fw_capabilities(priv, wiphy->fw_version, - sizeof(wiphy->fw_version), - &wiphy->hw_version); - if (err != 0) { - dev_err(dev, "Incompatible firmware, aborting\n"); - goto out; - } - } - - if (priv->has_port3) - dev_info(dev, "Ad-hoc demo mode supported\n"); - if (priv->has_ibss) - dev_info(dev, "IEEE standard IBSS ad-hoc mode supported\n"); - if (priv->has_wep) - dev_info(dev, "WEP supported, %s-bit key\n", - priv->has_big_wep ? "104" : "40"); - if (priv->has_wpa) { - dev_info(dev, "WPA-PSK supported\n"); - if (orinoco_mic_init(priv)) { - dev_err(dev, "Failed to setup MIC crypto algorithm. " - "Disabling WPA support\n"); - priv->has_wpa = 0; - } - } - - err = orinoco_hw_read_card_settings(priv, wiphy->perm_addr); - if (err) - goto out; - - err = orinoco_hw_allocate_fid(priv); - if (err) { - dev_err(dev, "Failed to allocate NIC buffer!\n"); - goto out; - } - - /* Set up the default configuration */ - priv->iw_mode = NL80211_IFTYPE_STATION; - /* By default use IEEE/IBSS ad-hoc mode if we have it */ - priv->prefer_port3 = priv->has_port3 && (!priv->has_ibss); - set_port_type(priv); - priv->channel = 0; /* use firmware default */ - - priv->promiscuous = 0; - priv->encode_alg = ORINOCO_ALG_NONE; - priv->tx_key = 0; - priv->wpa_enabled = 0; - priv->tkip_cm_active = 0; - priv->key_mgmt = 0; - priv->wpa_ie_len = 0; - priv->wpa_ie = NULL; - - if (orinoco_wiphy_register(wiphy)) { - err = -ENODEV; - goto out; - } - - /* Make the hardware available, as long as it hasn't been - * removed elsewhere (e.g. by PCMCIA hot unplug) */ - orinoco_lock_irq(priv); - priv->hw_unavailable--; - orinoco_unlock_irq(priv); - - dev_dbg(dev, "Ready\n"); - - out: - return err; -} -EXPORT_SYMBOL(orinoco_init); - -static const struct net_device_ops orinoco_netdev_ops = { - .ndo_open = orinoco_open, - .ndo_stop = orinoco_stop, - .ndo_start_xmit = orinoco_xmit, - .ndo_set_rx_mode = orinoco_set_multicast_list, - .ndo_change_mtu = orinoco_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, - .ndo_tx_timeout = orinoco_tx_timeout, - .ndo_get_stats = orinoco_get_stats, -}; - -/* Allocate private data. - * - * This driver has a number of structures associated with it - * netdev - Net device structure for each network interface - * wiphy - structure associated with wireless phy - * wireless_dev (wdev) - structure for each wireless interface - * hw - structure for hermes chip info - * card - card specific structure for use by the card driver - * (airport, orinoco_cs) - * priv - orinoco private data - * device - generic linux device structure - * - * +---------+ +---------+ - * | wiphy | | netdev | - * | +-------+ | +-------+ - * | | priv | | | wdev | - * | | +-----+ +-+-------+ - * | | | hw | - * | +-+-----+ - * | | card | - * +-+-------+ - * - * priv has a link to netdev and device - * wdev has a link to wiphy - */ -struct orinoco_private -*alloc_orinocodev(int sizeof_card, - struct device *device, - int (*hard_reset)(struct orinoco_private *), - int (*stop_fw)(struct orinoco_private *, int)) -{ - struct orinoco_private *priv; - struct wiphy *wiphy; - - /* allocate wiphy - * NOTE: We only support a single virtual interface - * but this may change when monitor mode is added - */ - wiphy = wiphy_new(&orinoco_cfg_ops, - sizeof(struct orinoco_private) + sizeof_card); - if (!wiphy) - return NULL; - - priv = wiphy_priv(wiphy); - priv->dev = device; - - if (sizeof_card) - priv->card = (void *)((unsigned long)priv - + sizeof(struct orinoco_private)); - else - priv->card = NULL; - - orinoco_wiphy_init(wiphy); - -#ifdef WIRELESS_SPY - priv->wireless_data.spy_data = &priv->spy_data; -#endif - - /* Set up default callbacks */ - priv->hard_reset = hard_reset; - priv->stop_fw = stop_fw; - - spin_lock_init(&priv->lock); - priv->open = 0; - priv->hw_unavailable = 1; /* orinoco_init() must clear this - * before anything else touches the - * hardware */ - INIT_WORK(&priv->reset_work, orinoco_reset); - INIT_WORK(&priv->join_work, orinoco_join_ap); - INIT_WORK(&priv->wevent_work, orinoco_send_wevents); - - INIT_LIST_HEAD(&priv->rx_list); - tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet, - (unsigned long) priv); - - spin_lock_init(&priv->scan_lock); - INIT_LIST_HEAD(&priv->scan_list); - INIT_WORK(&priv->process_scan, orinoco_process_scan_results); - - priv->last_linkstatus = 0xffff; - -#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) - priv->cached_pri_fw = NULL; - priv->cached_fw = NULL; -#endif - - /* Register PM notifiers */ - orinoco_register_pm_notifier(priv); - - return priv; -} -EXPORT_SYMBOL(alloc_orinocodev); - -/* We can only support a single interface. We provide a separate - * function to set it up to distinguish between hardware - * initialisation and interface setup. - * - * The base_addr and irq parameters are passed on to netdev for use - * with SIOCGIFMAP. - */ -int orinoco_if_add(struct orinoco_private *priv, - unsigned long base_addr, - unsigned int irq, - const struct net_device_ops *ops) -{ - struct wiphy *wiphy = priv_to_wiphy(priv); - struct wireless_dev *wdev; - struct net_device *dev; - int ret; - - dev = alloc_etherdev(sizeof(struct wireless_dev)); - - if (!dev) - return -ENOMEM; - - /* Initialise wireless_dev */ - wdev = netdev_priv(dev); - wdev->wiphy = wiphy; - wdev->iftype = NL80211_IFTYPE_STATION; - - /* Setup / override net_device fields */ - dev->ieee80211_ptr = wdev; - dev->watchdog_timeo = HZ; /* 1 second timeout */ - dev->wireless_handlers = &orinoco_handler_def; -#ifdef WIRELESS_SPY - dev->wireless_data = &priv->wireless_data; -#endif - /* Default to standard ops if not set */ - if (ops) - dev->netdev_ops = ops; - else - dev->netdev_ops = &orinoco_netdev_ops; - - /* we use the default eth_mac_addr for setting the MAC addr */ - - /* Reserve space in skb for the SNAP header */ - dev->needed_headroom = ENCAPS_OVERHEAD; - - netif_carrier_off(dev); - - memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); - - dev->base_addr = base_addr; - dev->irq = irq; - - SET_NETDEV_DEV(dev, priv->dev); - ret = register_netdev(dev); - if (ret) - goto fail; - - priv->ndev = dev; - - /* Report what we've done */ - dev_dbg(priv->dev, "Registerred interface %s.\n", dev->name); - - return 0; - - fail: - free_netdev(dev); - return ret; -} -EXPORT_SYMBOL(orinoco_if_add); - -void orinoco_if_del(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - - unregister_netdev(dev); - free_netdev(dev); -} -EXPORT_SYMBOL(orinoco_if_del); - -void free_orinocodev(struct orinoco_private *priv) -{ - struct wiphy *wiphy = priv_to_wiphy(priv); - struct orinoco_rx_data *rx_data, *temp; - struct orinoco_scan_data *sd, *sdtemp; - - /* If the tasklet is scheduled when we call tasklet_kill it - * will run one final time. However the tasklet will only - * drain priv->rx_list if the hw is still available. */ - tasklet_kill(&priv->rx_tasklet); - - /* Explicitly drain priv->rx_list */ - list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) { - list_del(&rx_data->list); - - dev_kfree_skb(rx_data->skb); - kfree(rx_data->desc); - kfree(rx_data); - } - - cancel_work_sync(&priv->process_scan); - /* Explicitly drain priv->scan_list */ - list_for_each_entry_safe(sd, sdtemp, &priv->scan_list, list) { - list_del(&sd->list); - - if (sd->len > 0) - kfree(sd->buf); - kfree(sd); - } - - orinoco_unregister_pm_notifier(priv); - orinoco_uncache_fw(priv); - - priv->wpa_ie_len = 0; - kfree(priv->wpa_ie); - orinoco_mic_free(priv); - wiphy_free(wiphy); -} -EXPORT_SYMBOL(free_orinocodev); - -int orinoco_up(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - unsigned long flags; - int err; - - priv->hw.ops->lock_irqsave(&priv->lock, &flags); - - err = orinoco_reinit_firmware(priv); - if (err) { - printk(KERN_ERR "%s: Error %d re-initializing firmware\n", - dev->name, err); - goto exit; - } - - netif_device_attach(dev); - priv->hw_unavailable--; - - if (priv->open && !priv->hw_unavailable) { - err = __orinoco_up(priv); - if (err) - printk(KERN_ERR "%s: Error %d restarting card\n", - dev->name, err); - } - -exit: - priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); - - return 0; -} -EXPORT_SYMBOL(orinoco_up); - -void orinoco_down(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - unsigned long flags; - int err; - - priv->hw.ops->lock_irqsave(&priv->lock, &flags); - err = __orinoco_down(priv); - if (err) - printk(KERN_WARNING "%s: Error %d downing interface\n", - dev->name, err); - - netif_device_detach(dev); - priv->hw_unavailable++; - priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); -} -EXPORT_SYMBOL(orinoco_down); - -/********************************************************************/ -/* Module initialization */ -/********************************************************************/ - -/* Can't be declared "const" or the whole __initdata section will - * become const */ -static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION - " (David Gibson , " - "Pavel Roskin , et al)"; - -static int __init init_orinoco(void) -{ - printk(KERN_DEBUG "%s\n", version); - return 0; -} - -static void __exit exit_orinoco(void) -{ -} - -module_init(init_orinoco); -module_exit(exit_orinoco); diff --git a/drivers/net/wireless/orinoco/main.h b/drivers/net/wireless/orinoco/main.h deleted file mode 100644 index 5a8fec26136e..000000000000 --- a/drivers/net/wireless/orinoco/main.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Exports from main to helper modules - * - * See copyright notice in main.c - */ -#ifndef _ORINOCO_MAIN_H_ -#define _ORINOCO_MAIN_H_ - -#include -#include "orinoco.h" - -/********************************************************************/ -/* Compile time configuration and compatibility stuff */ -/********************************************************************/ - -/* We do this this way to avoid ifdefs in the actual code */ -#ifdef WIRELESS_SPY -#define SPY_NUMBER(priv) (priv->spy_data.spy_number) -#else -#define SPY_NUMBER(priv) 0 -#endif /* WIRELESS_SPY */ - -/********************************************************************/ - -/* Export module parameter */ -extern int force_monitor; - -/* Forward declarations */ -struct net_device; -struct work_struct; - -void set_port_type(struct orinoco_private *priv); -int orinoco_commit(struct orinoco_private *priv); -void orinoco_reset(struct work_struct *work); - -/* Information element helpers - find a home for these... */ -#define WPA_OUI_TYPE "\x00\x50\xF2\x01" -#define WPA_SELECTOR_LEN 4 -static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len) -{ - u8 *p = data; - while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) { - if ((p[0] == WLAN_EID_VENDOR_SPECIFIC) && - (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0)) - return p; - p += p[1] + 2; - } - return NULL; -} - -#endif /* _ORINOCO_MAIN_H_ */ diff --git a/drivers/net/wireless/orinoco/mic.c b/drivers/net/wireless/orinoco/mic.c deleted file mode 100644 index fce4a843e656..000000000000 --- a/drivers/net/wireless/orinoco/mic.c +++ /dev/null @@ -1,79 +0,0 @@ -/* Orinoco MIC helpers - * - * See copyright notice in main.c - */ -#include -#include -#include -#include -#include - -#include "orinoco.h" -#include "mic.h" - -/********************************************************************/ -/* Michael MIC crypto setup */ -/********************************************************************/ -int orinoco_mic_init(struct orinoco_private *priv) -{ - priv->tx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0); - if (IS_ERR(priv->tx_tfm_mic)) { - printk(KERN_DEBUG "orinoco_mic_init: could not allocate " - "crypto API michael_mic\n"); - priv->tx_tfm_mic = NULL; - return -ENOMEM; - } - - priv->rx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0); - if (IS_ERR(priv->rx_tfm_mic)) { - printk(KERN_DEBUG "orinoco_mic_init: could not allocate " - "crypto API michael_mic\n"); - priv->rx_tfm_mic = NULL; - return -ENOMEM; - } - - return 0; -} - -void orinoco_mic_free(struct orinoco_private *priv) -{ - if (priv->tx_tfm_mic) - crypto_free_hash(priv->tx_tfm_mic); - if (priv->rx_tfm_mic) - crypto_free_hash(priv->rx_tfm_mic); -} - -int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key, - u8 *da, u8 *sa, u8 priority, - u8 *data, size_t data_len, u8 *mic) -{ - struct hash_desc desc; - struct scatterlist sg[2]; - u8 hdr[ETH_HLEN + 2]; /* size of header + padding */ - - if (tfm_michael == NULL) { - printk(KERN_WARNING "orinoco_mic: tfm_michael == NULL\n"); - return -1; - } - - /* Copy header into buffer. We need the padding on the end zeroed */ - memcpy(&hdr[0], da, ETH_ALEN); - memcpy(&hdr[ETH_ALEN], sa, ETH_ALEN); - hdr[ETH_ALEN * 2] = priority; - hdr[ETH_ALEN * 2 + 1] = 0; - hdr[ETH_ALEN * 2 + 2] = 0; - hdr[ETH_ALEN * 2 + 3] = 0; - - /* Use scatter gather to MIC header and data in one go */ - sg_init_table(sg, 2); - sg_set_buf(&sg[0], hdr, sizeof(hdr)); - sg_set_buf(&sg[1], data, data_len); - - if (crypto_hash_setkey(tfm_michael, key, MIC_KEYLEN)) - return -1; - - desc.tfm = tfm_michael; - desc.flags = 0; - return crypto_hash_digest(&desc, sg, data_len + sizeof(hdr), - mic); -} diff --git a/drivers/net/wireless/orinoco/mic.h b/drivers/net/wireless/orinoco/mic.h deleted file mode 100644 index 04d05bc566d6..000000000000 --- a/drivers/net/wireless/orinoco/mic.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Orinoco MIC helpers - * - * See copyright notice in main.c - */ -#ifndef _ORINOCO_MIC_H_ -#define _ORINOCO_MIC_H_ - -#include - -#define MICHAEL_MIC_LEN 8 - -/* Forward declarations */ -struct orinoco_private; -struct crypto_hash; - -int orinoco_mic_init(struct orinoco_private *priv); -void orinoco_mic_free(struct orinoco_private *priv); -int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key, - u8 *da, u8 *sa, u8 priority, - u8 *data, size_t data_len, u8 *mic); - -#endif /* ORINOCO_MIC_H */ diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h deleted file mode 100644 index eebd2be21ee9..000000000000 --- a/drivers/net/wireless/orinoco/orinoco.h +++ /dev/null @@ -1,253 +0,0 @@ -/* orinoco.h - * - * Common definitions to all pieces of the various orinoco - * drivers - */ - -#ifndef _ORINOCO_H -#define _ORINOCO_H - -#define DRIVER_VERSION "0.15" - -#include -#include -#include -#include -#include -#include - -#include "hermes.h" - -/* To enable debug messages */ -/*#define ORINOCO_DEBUG 3*/ - -#define WIRELESS_SPY /* enable iwspy support */ - -#define MAX_SCAN_LEN 4096 - -#define ORINOCO_SEQ_LEN 8 -#define ORINOCO_MAX_KEY_SIZE 14 -#define ORINOCO_MAX_KEYS 4 - -struct orinoco_key { - __le16 len; /* always stored as little-endian */ - char data[ORINOCO_MAX_KEY_SIZE]; -} __packed; - -#define TKIP_KEYLEN 16 -#define MIC_KEYLEN 8 - -struct orinoco_tkip_key { - u8 tkip[TKIP_KEYLEN]; - u8 tx_mic[MIC_KEYLEN]; - u8 rx_mic[MIC_KEYLEN]; -}; - -enum orinoco_alg { - ORINOCO_ALG_NONE, - ORINOCO_ALG_WEP, - ORINOCO_ALG_TKIP -}; - -enum fwtype { - FIRMWARE_TYPE_AGERE, - FIRMWARE_TYPE_INTERSIL, - FIRMWARE_TYPE_SYMBOL -}; - -struct firmware; - -struct orinoco_private { - void *card; /* Pointer to card dependent structure */ - struct device *dev; - int (*hard_reset)(struct orinoco_private *); - int (*stop_fw)(struct orinoco_private *, int); - - struct ieee80211_supported_band band; - struct ieee80211_channel channels[14]; - u32 cipher_suites[3]; - - /* Synchronisation stuff */ - spinlock_t lock; - int hw_unavailable; - struct work_struct reset_work; - - /* Interrupt tasklets */ - struct tasklet_struct rx_tasklet; - struct list_head rx_list; - - /* driver state */ - int open; - u16 last_linkstatus; - struct work_struct join_work; - struct work_struct wevent_work; - - /* Net device stuff */ - struct net_device *ndev; - struct net_device_stats stats; - struct iw_statistics wstats; - - /* Hardware control variables */ - struct hermes hw; - u16 txfid; - - /* Capabilities of the hardware/firmware */ - enum fwtype firmware_type; - int ibss_port; - int nicbuf_size; - u16 channel_mask; - - /* Boolean capabilities */ - unsigned int has_ibss:1; - unsigned int has_port3:1; - unsigned int has_wep:1; - unsigned int has_big_wep:1; - unsigned int has_mwo:1; - unsigned int has_pm:1; - unsigned int has_preamble:1; - unsigned int has_sensitivity:1; - unsigned int has_hostscan:1; - unsigned int has_alt_txcntl:1; - unsigned int has_ext_scan:1; - unsigned int has_wpa:1; - unsigned int do_fw_download:1; - unsigned int broken_disableport:1; - unsigned int broken_monitor:1; - unsigned int prefer_port3:1; - - /* Configuration paramaters */ - enum nl80211_iftype iw_mode; - enum orinoco_alg encode_alg; - u16 wep_restrict, tx_key; - struct key_params keys[ORINOCO_MAX_KEYS]; - - int bitratemode; - char nick[IW_ESSID_MAX_SIZE + 1]; - char desired_essid[IW_ESSID_MAX_SIZE + 1]; - char desired_bssid[ETH_ALEN]; - int bssid_fixed; - u16 frag_thresh, mwo_robust; - u16 channel; - u16 ap_density, rts_thresh; - u16 pm_on, pm_mcast, pm_period, pm_timeout; - u16 preamble; - u16 short_retry_limit, long_retry_limit; - u16 retry_lifetime; -#ifdef WIRELESS_SPY - struct iw_spy_data spy_data; /* iwspy support */ - struct iw_public_data wireless_data; -#endif - - /* Configuration dependent variables */ - int port_type, createibss; - int promiscuous, mc_count; - - /* Scanning support */ - struct cfg80211_scan_request *scan_request; - struct work_struct process_scan; - struct list_head scan_list; - spinlock_t scan_lock; /* protects the scan list */ - - /* WPA support */ - u8 *wpa_ie; - int wpa_ie_len; - - struct crypto_hash *rx_tfm_mic; - struct crypto_hash *tx_tfm_mic; - - unsigned int wpa_enabled:1; - unsigned int tkip_cm_active:1; - unsigned int key_mgmt:3; - -#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) - /* Cached in memory firmware to use during ->resume. */ - const struct firmware *cached_pri_fw; - const struct firmware *cached_fw; -#endif - - struct notifier_block pm_notifier; -}; - -#ifdef ORINOCO_DEBUG -extern int orinoco_debug; -#define DEBUG(n, args...) do { \ - if (orinoco_debug > (n)) \ - printk(KERN_DEBUG args); \ -} while (0) -#else -#define DEBUG(n, args...) do { } while (0) -#endif /* ORINOCO_DEBUG */ - -/********************************************************************/ -/* Exported prototypes */ -/********************************************************************/ - -struct orinoco_private *alloc_orinocodev(int sizeof_card, struct device *device, - int (*hard_reset)(struct orinoco_private *), - int (*stop_fw)(struct orinoco_private *, int)); -void free_orinocodev(struct orinoco_private *priv); -int orinoco_init(struct orinoco_private *priv); -int orinoco_if_add(struct orinoco_private *priv, unsigned long base_addr, - unsigned int irq, const struct net_device_ops *ops); -void orinoco_if_del(struct orinoco_private *priv); -int orinoco_up(struct orinoco_private *priv); -void orinoco_down(struct orinoco_private *priv); -irqreturn_t orinoco_interrupt(int irq, void *dev_id); - -void __orinoco_ev_info(struct net_device *dev, struct hermes *hw); -void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw); - -int orinoco_process_xmit_skb(struct sk_buff *skb, - struct net_device *dev, - struct orinoco_private *priv, - int *tx_control, - u8 *mic); - -/* Common ndo functions exported for reuse by orinoco_usb */ -int orinoco_open(struct net_device *dev); -int orinoco_stop(struct net_device *dev); -struct net_device_stats *orinoco_get_stats(struct net_device *dev); -void orinoco_set_multicast_list(struct net_device *dev); -int orinoco_change_mtu(struct net_device *dev, int new_mtu); -void orinoco_tx_timeout(struct net_device *dev); - -/********************************************************************/ -/* Locking and synchronization functions */ -/********************************************************************/ - -static inline int orinoco_lock(struct orinoco_private *priv, - unsigned long *flags) -{ - priv->hw.ops->lock_irqsave(&priv->lock, flags); - if (priv->hw_unavailable) { - DEBUG(1, "orinoco_lock() called with hw_unavailable (dev=%p)\n", - priv->ndev); - priv->hw.ops->unlock_irqrestore(&priv->lock, flags); - return -EBUSY; - } - return 0; -} - -static inline void orinoco_unlock(struct orinoco_private *priv, - unsigned long *flags) -{ - priv->hw.ops->unlock_irqrestore(&priv->lock, flags); -} - -static inline void orinoco_lock_irq(struct orinoco_private *priv) -{ - priv->hw.ops->lock_irq(&priv->lock); -} - -static inline void orinoco_unlock_irq(struct orinoco_private *priv) -{ - priv->hw.ops->unlock_irq(&priv->lock); -} - -/*** Navigate from net_device to orinoco_private ***/ -static inline struct orinoco_private *ndev_priv(struct net_device *dev) -{ - struct wireless_dev *wdev = netdev_priv(dev); - return wdev_priv(wdev); -} -#endif /* _ORINOCO_H */ diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c deleted file mode 100644 index a956f965a1e5..000000000000 --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ /dev/null @@ -1,341 +0,0 @@ -/* orinoco_cs.c (formerly known as dldwd_cs.c) - * - * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such - * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ - * EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and others). - * It should also be usable on various Prism II based cards such as the - * Linksys, D-Link and Farallon Skyline. It should also work on Symbol - * cards such as the 3Com AirConnect and Ericsson WLAN. - * - * Copyright notice & release notes in file main.c - */ - -#define DRIVER_NAME "orinoco_cs" -#define PFX DRIVER_NAME ": " - -#include -#include -#include -#include -#include -#include - -#include "orinoco.h" - -/********************************************************************/ -/* Module stuff */ -/********************************************************************/ - -MODULE_AUTHOR("David Gibson "); -MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco," - " Prism II based and similar wireless cards"); -MODULE_LICENSE("Dual MPL/GPL"); - -/* Module parameters */ - -/* Some D-Link cards have buggy CIS. They do work at 5v properly, but - * don't have any CIS entry for it. This workaround it... */ -static int ignore_cis_vcc; /* = 0 */ -module_param(ignore_cis_vcc, int, 0); -MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket"); - -/********************************************************************/ -/* Data structures */ -/********************************************************************/ - -/* PCMCIA specific device information (goes in the card field of - * struct orinoco_private */ -struct orinoco_pccard { - struct pcmcia_device *p_dev; - - /* Used to handle hard reset */ - /* yuck, we need this hack to work around the insanity of the - * PCMCIA layer */ - unsigned long hard_reset_in_progress; -}; - - -/********************************************************************/ -/* Function prototypes */ -/********************************************************************/ - -static int orinoco_cs_config(struct pcmcia_device *link); -static void orinoco_cs_release(struct pcmcia_device *link); -static void orinoco_cs_detach(struct pcmcia_device *p_dev); - -/********************************************************************/ -/* Device methods */ -/********************************************************************/ - -static int -orinoco_cs_hard_reset(struct orinoco_private *priv) -{ - struct orinoco_pccard *card = priv->card; - struct pcmcia_device *link = card->p_dev; - int err; - - /* We need atomic ops here, because we're not holding the lock */ - set_bit(0, &card->hard_reset_in_progress); - - err = pcmcia_reset_card(link->socket); - if (err) - return err; - - msleep(100); - clear_bit(0, &card->hard_reset_in_progress); - - return 0; -} - -/********************************************************************/ -/* PCMCIA stuff */ -/********************************************************************/ - -static int -orinoco_cs_probe(struct pcmcia_device *link) -{ - struct orinoco_private *priv; - struct orinoco_pccard *card; - - priv = alloc_orinocodev(sizeof(*card), &link->dev, - orinoco_cs_hard_reset, NULL); - if (!priv) - return -ENOMEM; - card = priv->card; - - /* Link both structures together */ - card->p_dev = link; - link->priv = priv; - - return orinoco_cs_config(link); -} /* orinoco_cs_attach */ - -static void orinoco_cs_detach(struct pcmcia_device *link) -{ - struct orinoco_private *priv = link->priv; - - orinoco_if_del(priv); - - orinoco_cs_release(link); - - wiphy_unregister(priv_to_wiphy(priv)); - free_orinocodev(priv); -} /* orinoco_cs_detach */ - -static int orinoco_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - if (p_dev->config_index == 0) - return -EINVAL; - - return pcmcia_request_io(p_dev); -}; - -static int -orinoco_cs_config(struct pcmcia_device *link) -{ - struct orinoco_private *priv = link->priv; - struct hermes *hw = &priv->hw; - int ret; - void __iomem *mem; - - link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC | - CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; - if (ignore_cis_vcc) - link->config_flags &= ~CONF_AUTO_CHECK_VCC; - ret = pcmcia_loop_config(link, orinoco_cs_config_check, NULL); - if (ret) { - if (!ignore_cis_vcc) - printk(KERN_ERR PFX "GetNextTuple(): No matching " - "CIS configuration. Maybe you need the " - "ignore_cis_vcc=1 parameter.\n"); - goto failed; - } - - mem = ioport_map(link->resource[0]->start, - resource_size(link->resource[0])); - if (!mem) - goto failed; - - /* We initialize the hermes structure before completing PCMCIA - * configuration just in case the interrupt handler gets - * called. */ - hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING); - - ret = pcmcia_request_irq(link, orinoco_interrupt); - if (ret) - goto failed; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - /* Initialise the main driver */ - if (orinoco_init(priv) != 0) { - printk(KERN_ERR PFX "orinoco_init() failed\n"); - goto failed; - } - - /* Register an interface with the stack */ - if (orinoco_if_add(priv, link->resource[0]->start, - link->irq, NULL) != 0) { - printk(KERN_ERR PFX "orinoco_if_add() failed\n"); - goto failed; - } - - return 0; - - failed: - orinoco_cs_release(link); - return -ENODEV; -} /* orinoco_cs_config */ - -static void -orinoco_cs_release(struct pcmcia_device *link) -{ - struct orinoco_private *priv = link->priv; - unsigned long flags; - - /* We're committed to taking the device away now, so mark the - * hardware as unavailable */ - priv->hw.ops->lock_irqsave(&priv->lock, &flags); - priv->hw_unavailable++; - priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); - - pcmcia_disable_device(link); - if (priv->hw.iobase) - ioport_unmap(priv->hw.iobase); -} /* orinoco_cs_release */ - -static int orinoco_cs_suspend(struct pcmcia_device *link) -{ - struct orinoco_private *priv = link->priv; - struct orinoco_pccard *card = priv->card; - - /* This is probably racy, but I can't think of - a better way, short of rewriting the PCMCIA - layer to not suck :-( */ - if (!test_bit(0, &card->hard_reset_in_progress)) - orinoco_down(priv); - - return 0; -} - -static int orinoco_cs_resume(struct pcmcia_device *link) -{ - struct orinoco_private *priv = link->priv; - struct orinoco_pccard *card = priv->card; - int err = 0; - - if (!test_bit(0, &card->hard_reset_in_progress)) - err = orinoco_up(priv); - - return err; -} - - -/********************************************************************/ -/* Module initialization */ -/********************************************************************/ - -static const struct pcmcia_device_id orinoco_cs_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), /* 3Com AirConnect PCI 777A */ - PCMCIA_DEVICE_MANF_CARD(0x016b, 0x0001), /* Ericsson WLAN Card C11 */ - PCMCIA_DEVICE_MANF_CARD(0x01eb, 0x080a), /* Nortel Networks eMobility 802.11 Wireless Adapter */ - PCMCIA_DEVICE_MANF_CARD(0x0261, 0x0002), /* AirWay 802.11 Adapter (PCMCIA) */ - PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0001), /* ARtem Onair */ - PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0003), /* ARtem Onair Comcard 11 */ - PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0305), /* Buffalo WLI-PCM-S11 */ - PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002), /* ASUS SpaceLink WL-100 */ - PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x0002), /* SpeedStream SS1021 Wireless Adapter */ - PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x3021), /* SpeedStream Wireless Adapter */ - PCMCIA_DEVICE_MANF_CARD(0x14ea, 0xb001), /* PLANEX RoadLannerWave GW-NS11H */ - PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE737A AirConnect Wireless LAN PC Card", 0x41240e5b, 0x56010af3), - PCMCIA_DEVICE_PROD_ID12("Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", 0x5cd01705, 0x4271660f), - PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11B_CF_CARD_25", 0x78fc06ee, 0x45a50c1e), - PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11b_PC_CARD_25", 0x78fc06ee, 0xdb9aa842), - PCMCIA_DEVICE_PROD_ID12("Avaya Communication", "Avaya Wireless PC Card", 0xd8a43b78, 0x0d341169), - PCMCIA_DEVICE_PROD_ID12("BENQ", "AWL100 PCMCIA ADAPTER", 0x35dadc74, 0x01f7fedb), - PCMCIA_DEVICE_PROD_ID12("Cabletron", "RoamAbout 802.11 DS", 0x32d445f5, 0xedeffd90), - PCMCIA_DEVICE_PROD_ID12("D-Link Corporation", "D-Link DWL-650H 11Mbps WLAN Adapter", 0xef544d24, 0xcd8ea916), - PCMCIA_DEVICE_PROD_ID12("ELSA", "AirLancer MC-11", 0x4507a33a, 0xef54f0e3), - PCMCIA_DEVICE_PROD_ID12("HyperLink", "Wireless PC Card 11Mbps", 0x56cc3f1a, 0x0bcf220c), - PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless 2011 LAN PC Card", 0x816cc815, 0x07f58077), - PCMCIA_DEVICE_PROD_ID12("LeArtery", "SYNCBYAIR 11Mbps Wireless LAN PC Card", 0x7e3b326a, 0x49893e92), - PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/IEEE", 0x23eb9949, 0xc562e72a), - PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11", 0x481e0094, 0x7360e410), - PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11G", 0x481e0094, 0xf57ca4b3), - PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/IEEE", 0x24358cd4, 0xc562e72a), - PCMCIA_DEVICE_PROD_ID12("Nortel Networks", "emobility 802.11 Wireless LAN PC Card", 0x2d617ea0, 0x88cd5767), - PCMCIA_DEVICE_PROD_ID12("OTC", "Wireless AirEZY 2411-PCC WLAN Card", 0x4ac44287, 0x235a6bed), - PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PC CARD HARMONY 80211B", 0xc6536a5e, 0x090c3cd9), - PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PCI CARD HARMONY 80211B", 0xc6536a5e, 0x9f494e26), - PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "11Mbps WLAN Card", 0x43d74cb4, 0x579bd91b), - PCMCIA_DEVICE_PROD_ID12("Symbol Technologies", "LA4111 Spectrum24 Wireless LAN PC Card", 0x3f02b4d6, 0x3663cb0e), - PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.01", 0xd27deb1a), /* Lucent Orinoco */ -#ifdef CONFIG_HERMES_PRISM - /* Only entries that certainly identify Prism chipset */ - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), /* SonicWALL Long Range Wireless Card */ - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), /* Sohoware NCP110, Philips 802.11b */ - PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0002), /* AnyPoint(TM) Wireless II PC Card */ - PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), /* PROXIM RangeLAN-DS/LAN PC CARD */ - PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), /* Compaq WL100 11 Mbps Wireless Adapter */ - PCMCIA_DEVICE_MANF_CARD(0x01ff, 0x0008), /* Intermec MobileLAN 11Mbps 802.11b WLAN Card */ - PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), /* Samsung SWL2000-N 11Mb/s WLAN Card */ - PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), /* Linksys WPC11 Version 2.5 */ - PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), /* Linksys WPC11 Version 3 */ - PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), /* Compaq HNW-100 11 Mbps Wireless Adapter */ - PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0673), /* Linksys WCF12 Wireless CompactFlash Card */ - PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), /* Airvast WN-100 */ - PCMCIA_DEVICE_MANF_CARD(0x9005, 0x0021), /* Adaptec Ultra Wireless ANW-8030 */ - PCMCIA_DEVICE_MANF_CARD(0xc001, 0x0008), /* CONTEC FLEXSCAN/FX-DDS110-PCC */ - PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), /* Conceptronic CON11Cpro, EMTAC A2424i */ - PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), /* Safeway 802.11b, ZCOMAX AirRunner/XI-300 */ - PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), /* D-Link DCF660, Sandisk Connect SDWCFB-000 */ - PCMCIA_DEVICE_PROD_ID123("Instant Wireless ", " Network PC CARD", "Version 01.02", 0x11d901af, 0x6e9bd926, 0x4b74baa0), - PCMCIA_DEVICE_PROD_ID12("ACTIONTEC", "PRISM Wireless LAN PC Card", 0x393089da, 0xa71e69d5), - PCMCIA_DEVICE_PROD_ID12("Addtron", "AWP-100 Wireless PCMCIA", 0xe6ec52ce, 0x08649af2), - PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", 0x2decece3, 0x82067c18), - PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-PCM-L11G", 0x2decece3, 0xf57ca4b3), - PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card", 0x54f7c49c, 0x15a75e5b), - PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCC-11", 0x5261440f, 0xa6405584), - PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCCA-11", 0x5261440f, 0xdf6115f9), - PCMCIA_DEVICE_PROD_ID12("corega_K.K.", "Wireless_LAN_PCCB-11", 0x29e33311, 0xee7a27ae), - PCMCIA_DEVICE_PROD_ID12("Digital Data Communications", "WPC-0100", 0xfdd73470, 0xe0b6f146), - PCMCIA_DEVICE_PROD_ID12("D", "Link DRC-650 11Mbps WLAN Card", 0x71b18589, 0xf144e3ac), - PCMCIA_DEVICE_PROD_ID12("D", "Link DWL-650 11Mbps WLAN Card", 0x71b18589, 0xb6f1b0ab), - PCMCIA_DEVICE_PROD_ID12(" ", "IEEE 802.11 Wireless LAN/PC Card", 0x3b6e20c8, 0xefccafe9), - PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", 0x74c5e40d, 0xdb472a18), - PCMCIA_DEVICE_PROD_ID12("INTERSIL", "I-GATE 11M PC Card / PC Card plus", 0x74c5e40d, 0x8304ff77), - PCMCIA_DEVICE_PROD_ID12("Intersil", "PRISM 2_5 PCMCIA ADAPTER", 0x4b801a17, 0x6345a0bf), - PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card", 0x0733cc81, 0x0c52f395), - PCMCIA_DEVICE_PROD_ID12("Microsoft", "Wireless Notebook Adapter MN-520", 0x5961bf85, 0x6eec8c01), - PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401RA Wireless PC", "Card", 0x0306467f, 0x9762e8f1), - PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", 0xa37434e9, 0x9762e8f1), - PCMCIA_DEVICE_PROD_ID12("OEM", "PRISM2 IEEE 802.11 PC-Card", 0xfea54c90, 0x48f2bdd6), - PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-CF110", 0x209f40ab, 0xd9715264), - PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-NS110", 0x209f40ab, 0x46263178), - PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2532W-B EliteConnect Wireless Adapter", 0xc4f8b18b, 0x196bd757), - PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2632W", 0xc4f8b18b, 0x474a1f2a), - PCMCIA_DEVICE_PROD_ID12("ZoomAir 11Mbps High", "Rate wireless Networking", 0x273fe3db, 0x32a1eaee), - PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092), - PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2), - PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b), - PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39), - - /* This may be Agere or Intersil Firmware */ - PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002), -#endif - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, orinoco_cs_ids); - -static struct pcmcia_driver orinoco_driver = { - .owner = THIS_MODULE, - .name = DRIVER_NAME, - .probe = orinoco_cs_probe, - .remove = orinoco_cs_detach, - .id_table = orinoco_cs_ids, - .suspend = orinoco_cs_suspend, - .resume = orinoco_cs_resume, -}; -module_pcmcia_driver(orinoco_driver); diff --git a/drivers/net/wireless/orinoco/orinoco_nortel.c b/drivers/net/wireless/orinoco/orinoco_nortel.c deleted file mode 100644 index 048693b6c6c2..000000000000 --- a/drivers/net/wireless/orinoco/orinoco_nortel.c +++ /dev/null @@ -1,323 +0,0 @@ -/* orinoco_nortel.c - * - * Driver for Prism II devices which would usually be driven by orinoco_cs, - * but are connected to the PCI bus by a PCI-to-PCMCIA adapter used in - * Nortel emobility, Symbol LA-4113 and Symbol LA-4123. - * - * Copyright (C) 2002 Tobias Hoffmann - * (C) 2003 Christoph Jungegger - * - * Some of this code is borrowed from orinoco_plx.c - * Copyright (C) 2001 Daniel Barlow - * Some of this code is borrowed from orinoco_pci.c - * Copyright (C) 2001 Jean Tourrilhes - * Some of this code is "inspired" by linux-wlan-ng-0.1.10, but nothing - * has been copied from it. linux-wlan-ng-0.1.10 is originally : - * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License - * at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and - * limitations under the License. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License version 2 (the "GPL"), in - * which case the provisions of the GPL are applicable instead of the - * above. If you wish to allow the use of your version of this file - * only under the terms of the GPL and not to allow others to use your - * version of this file under the MPL, indicate your decision by - * deleting the provisions above and replace them with the notice and - * other provisions required by the GPL. If you do not delete the - * provisions above, a recipient may use your version of this file - * under either the MPL or the GPL. - */ - -#define DRIVER_NAME "orinoco_nortel" -#define PFX DRIVER_NAME ": " - -#include -#include -#include -#include -#include -#include - -#include "orinoco.h" -#include "orinoco_pci.h" - -#define COR_OFFSET (0xe0) /* COR attribute offset of Prism2 PC card */ -#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */ - - -/* - * Do a soft reset of the card using the Configuration Option Register - * We need this to get going... - * This is the part of the code that is strongly inspired from wlan-ng - * - * Note bis : Don't try to access HERMES_CMD during the reset phase. - * It just won't work ! - */ -static int orinoco_nortel_cor_reset(struct orinoco_private *priv) -{ - struct orinoco_pci_card *card = priv->card; - - /* Assert the reset until the card notices */ - iowrite16(8, card->bridge_io + 2); - ioread16(card->attr_io + COR_OFFSET); - iowrite16(0x80, card->attr_io + COR_OFFSET); - mdelay(1); - - /* Give time for the card to recover from this hard effort */ - iowrite16(0, card->attr_io + COR_OFFSET); - iowrite16(0, card->attr_io + COR_OFFSET); - mdelay(1); - - /* Set COR as usual */ - iowrite16(COR_VALUE, card->attr_io + COR_OFFSET); - iowrite16(COR_VALUE, card->attr_io + COR_OFFSET); - mdelay(1); - - iowrite16(0x228, card->bridge_io + 2); - - return 0; -} - -static int orinoco_nortel_hw_init(struct orinoco_pci_card *card) -{ - int i; - u32 reg; - - /* Setup bridge */ - if (ioread16(card->bridge_io) & 1) { - printk(KERN_ERR PFX "brg1 answer1 wrong\n"); - return -EBUSY; - } - iowrite16(0x118, card->bridge_io + 2); - iowrite16(0x108, card->bridge_io + 2); - mdelay(30); - iowrite16(0x8, card->bridge_io + 2); - for (i = 0; i < 30; i++) { - mdelay(30); - if (ioread16(card->bridge_io) & 0x10) - break; - } - if (i == 30) { - printk(KERN_ERR PFX "brg1 timed out\n"); - return -EBUSY; - } - if (ioread16(card->attr_io + COR_OFFSET) & 1) { - printk(KERN_ERR PFX "brg2 answer1 wrong\n"); - return -EBUSY; - } - if (ioread16(card->attr_io + COR_OFFSET + 2) & 1) { - printk(KERN_ERR PFX "brg2 answer2 wrong\n"); - return -EBUSY; - } - if (ioread16(card->attr_io + COR_OFFSET + 4) & 1) { - printk(KERN_ERR PFX "brg2 answer3 wrong\n"); - return -EBUSY; - } - - /* Set the PCMCIA COR register */ - iowrite16(COR_VALUE, card->attr_io + COR_OFFSET); - mdelay(1); - reg = ioread16(card->attr_io + COR_OFFSET); - if (reg != COR_VALUE) { - printk(KERN_ERR PFX "Error setting COR value (reg=%x)\n", - reg); - return -EBUSY; - } - - /* Set LEDs */ - iowrite16(1, card->bridge_io + 10); - return 0; -} - -static int orinoco_nortel_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int err; - struct orinoco_private *priv; - struct orinoco_pci_card *card; - void __iomem *hermes_io, *bridge_io, *attr_io; - - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR PFX "Cannot enable PCI device\n"); - return err; - } - - err = pci_request_regions(pdev, DRIVER_NAME); - if (err) { - printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); - goto fail_resources; - } - - bridge_io = pci_iomap(pdev, 0, 0); - if (!bridge_io) { - printk(KERN_ERR PFX "Cannot map bridge registers\n"); - err = -EIO; - goto fail_map_bridge; - } - - attr_io = pci_iomap(pdev, 1, 0); - if (!attr_io) { - printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n"); - err = -EIO; - goto fail_map_attr; - } - - hermes_io = pci_iomap(pdev, 2, 0); - if (!hermes_io) { - printk(KERN_ERR PFX "Cannot map chipset registers\n"); - err = -EIO; - goto fail_map_hermes; - } - - /* Allocate network device */ - priv = alloc_orinocodev(sizeof(*card), &pdev->dev, - orinoco_nortel_cor_reset, NULL); - if (!priv) { - printk(KERN_ERR PFX "Cannot allocate network device\n"); - err = -ENOMEM; - goto fail_alloc; - } - - card = priv->card; - card->bridge_io = bridge_io; - card->attr_io = attr_io; - - hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); - - err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, - DRIVER_NAME, priv); - if (err) { - printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); - err = -EBUSY; - goto fail_irq; - } - - err = orinoco_nortel_hw_init(card); - if (err) { - printk(KERN_ERR PFX "Hardware initialization failed\n"); - goto fail; - } - - err = orinoco_nortel_cor_reset(priv); - if (err) { - printk(KERN_ERR PFX "Initial reset failed\n"); - goto fail; - } - - err = orinoco_init(priv); - if (err) { - printk(KERN_ERR PFX "orinoco_init() failed\n"); - goto fail; - } - - err = orinoco_if_add(priv, 0, 0, NULL); - if (err) { - printk(KERN_ERR PFX "orinoco_if_add() failed\n"); - goto fail_wiphy; - } - - pci_set_drvdata(pdev, priv); - - return 0; - - fail_wiphy: - wiphy_unregister(priv_to_wiphy(priv)); - fail: - free_irq(pdev->irq, priv); - - fail_irq: - free_orinocodev(priv); - - fail_alloc: - pci_iounmap(pdev, hermes_io); - - fail_map_hermes: - pci_iounmap(pdev, attr_io); - - fail_map_attr: - pci_iounmap(pdev, bridge_io); - - fail_map_bridge: - pci_release_regions(pdev); - - fail_resources: - pci_disable_device(pdev); - - return err; -} - -static void orinoco_nortel_remove_one(struct pci_dev *pdev) -{ - struct orinoco_private *priv = pci_get_drvdata(pdev); - struct orinoco_pci_card *card = priv->card; - - /* Clear LEDs */ - iowrite16(0, card->bridge_io + 10); - - orinoco_if_del(priv); - wiphy_unregister(priv_to_wiphy(priv)); - free_irq(pdev->irq, priv); - free_orinocodev(priv); - pci_iounmap(pdev, priv->hw.iobase); - pci_iounmap(pdev, card->attr_io); - pci_iounmap(pdev, card->bridge_io); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -static const struct pci_device_id orinoco_nortel_id_table[] = { - /* Nortel emobility PCI */ - {0x126c, 0x8030, PCI_ANY_ID, PCI_ANY_ID,}, - /* Symbol LA-4123 PCI */ - {0x1562, 0x0001, PCI_ANY_ID, PCI_ANY_ID,}, - {0,}, -}; - -MODULE_DEVICE_TABLE(pci, orinoco_nortel_id_table); - -static struct pci_driver orinoco_nortel_driver = { - .name = DRIVER_NAME, - .id_table = orinoco_nortel_id_table, - .probe = orinoco_nortel_init_one, - .remove = orinoco_nortel_remove_one, - .suspend = orinoco_pci_suspend, - .resume = orinoco_pci_resume, -}; - -static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION - " (Tobias Hoffmann & Christoph Jungegger )"; -MODULE_AUTHOR("Christoph Jungegger "); -MODULE_DESCRIPTION("Driver for wireless LAN cards using the Nortel PCI bridge"); -MODULE_LICENSE("Dual MPL/GPL"); - -static int __init orinoco_nortel_init(void) -{ - printk(KERN_DEBUG "%s\n", version); - return pci_register_driver(&orinoco_nortel_driver); -} - -static void __exit orinoco_nortel_exit(void) -{ - pci_unregister_driver(&orinoco_nortel_driver); -} - -module_init(orinoco_nortel_init); -module_exit(orinoco_nortel_exit); - -/* - * Local variables: - * c-indent-level: 8 - * c-basic-offset: 8 - * tab-width: 8 - * End: - */ diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/orinoco/orinoco_pci.c deleted file mode 100644 index 4938a2208a37..000000000000 --- a/drivers/net/wireless/orinoco/orinoco_pci.c +++ /dev/null @@ -1,266 +0,0 @@ -/* orinoco_pci.c - * - * Driver for Prism 2.5/3 devices that have a direct PCI interface - * (i.e. these are not PCMCIA cards in a PCMCIA-to-PCI bridge). - * The card contains only one PCI region, which contains all the usual - * hermes registers, as well as the COR register. - * - * Current maintainers are: - * Pavel Roskin - * and David Gibson - * - * Some of this code is borrowed from orinoco_plx.c - * Copyright (C) 2001 Daniel Barlow - * Some of this code is "inspired" by linux-wlan-ng-0.1.10, but nothing - * has been copied from it. linux-wlan-ng-0.1.10 is originally : - * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. - * This file originally written by: - * Copyright (C) 2001 Jean Tourrilhes - * And is now maintained by: - * (C) Copyright David Gibson, IBM Corp. 2002-2003. - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License - * at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and - * limitations under the License. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License version 2 (the "GPL"), in - * which case the provisions of the GPL are applicable instead of the - * above. If you wish to allow the use of your version of this file - * only under the terms of the GPL and not to allow others to use your - * version of this file under the MPL, indicate your decision by - * deleting the provisions above and replace them with the notice and - * other provisions required by the GPL. If you do not delete the - * provisions above, a recipient may use your version of this file - * under either the MPL or the GPL. - */ - -#define DRIVER_NAME "orinoco_pci" -#define PFX DRIVER_NAME ": " - -#include -#include -#include -#include -#include - -#include "orinoco.h" -#include "orinoco_pci.h" - -/* Offset of the COR register of the PCI card */ -#define HERMES_PCI_COR (0x26) - -/* Bitmask to reset the card */ -#define HERMES_PCI_COR_MASK (0x0080) - -/* Magic timeouts for doing the reset. - * Those times are straight from wlan-ng, and it is claimed that they - * are necessary. Alan will kill me. Take your time and grab a coffee. */ -#define HERMES_PCI_COR_ONT (250) /* ms */ -#define HERMES_PCI_COR_OFFT (500) /* ms */ -#define HERMES_PCI_COR_BUSYT (500) /* ms */ - -/* - * Do a soft reset of the card using the Configuration Option Register - * We need this to get going... - * This is the part of the code that is strongly inspired from wlan-ng - * - * Note : This code is done with irq enabled. This mean that many - * interrupts will occur while we are there. This is why we use the - * jiffies to regulate time instead of a straight mdelay(). Usually we - * need only around 245 iteration of the loop to do 250 ms delay. - * - * Note bis : Don't try to access HERMES_CMD during the reset phase. - * It just won't work ! - */ -static int orinoco_pci_cor_reset(struct orinoco_private *priv) -{ - struct hermes *hw = &priv->hw; - unsigned long timeout; - u16 reg; - - /* Assert the reset until the card notices */ - hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK); - mdelay(HERMES_PCI_COR_ONT); - - /* Give time for the card to recover from this hard effort */ - hermes_write_regn(hw, PCI_COR, 0x0000); - mdelay(HERMES_PCI_COR_OFFT); - - /* The card is ready when it's no longer busy */ - timeout = jiffies + msecs_to_jiffies(HERMES_PCI_COR_BUSYT); - reg = hermes_read_regn(hw, CMD); - while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { - mdelay(1); - reg = hermes_read_regn(hw, CMD); - } - - /* Still busy? */ - if (reg & HERMES_CMD_BUSY) { - printk(KERN_ERR PFX "Busy timeout\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int orinoco_pci_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int err; - struct orinoco_private *priv; - struct orinoco_pci_card *card; - void __iomem *hermes_io; - - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR PFX "Cannot enable PCI device\n"); - return err; - } - - err = pci_request_regions(pdev, DRIVER_NAME); - if (err) { - printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); - goto fail_resources; - } - - hermes_io = pci_iomap(pdev, 0, 0); - if (!hermes_io) { - printk(KERN_ERR PFX "Cannot remap chipset registers\n"); - err = -EIO; - goto fail_map_hermes; - } - - /* Allocate network device */ - priv = alloc_orinocodev(sizeof(*card), &pdev->dev, - orinoco_pci_cor_reset, NULL); - if (!priv) { - printk(KERN_ERR PFX "Cannot allocate network device\n"); - err = -ENOMEM; - goto fail_alloc; - } - - card = priv->card; - - hermes_struct_init(&priv->hw, hermes_io, HERMES_32BIT_REGSPACING); - - err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, - DRIVER_NAME, priv); - if (err) { - printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); - err = -EBUSY; - goto fail_irq; - } - - err = orinoco_pci_cor_reset(priv); - if (err) { - printk(KERN_ERR PFX "Initial reset failed\n"); - goto fail; - } - - err = orinoco_init(priv); - if (err) { - printk(KERN_ERR PFX "orinoco_init() failed\n"); - goto fail; - } - - err = orinoco_if_add(priv, 0, 0, NULL); - if (err) { - printk(KERN_ERR PFX "orinoco_if_add() failed\n"); - goto fail_wiphy; - } - - pci_set_drvdata(pdev, priv); - - return 0; - - fail_wiphy: - wiphy_unregister(priv_to_wiphy(priv)); - fail: - free_irq(pdev->irq, priv); - - fail_irq: - free_orinocodev(priv); - - fail_alloc: - pci_iounmap(pdev, hermes_io); - - fail_map_hermes: - pci_release_regions(pdev); - - fail_resources: - pci_disable_device(pdev); - - return err; -} - -static void orinoco_pci_remove_one(struct pci_dev *pdev) -{ - struct orinoco_private *priv = pci_get_drvdata(pdev); - - orinoco_if_del(priv); - wiphy_unregister(priv_to_wiphy(priv)); - free_irq(pdev->irq, priv); - free_orinocodev(priv); - pci_iounmap(pdev, priv->hw.iobase); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -static const struct pci_device_id orinoco_pci_id_table[] = { - /* Intersil Prism 3 */ - {0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,}, - /* Intersil Prism 2.5 */ - {0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,}, - /* Samsung MagicLAN SWL-2210P */ - {0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID,}, - {0,}, -}; - -MODULE_DEVICE_TABLE(pci, orinoco_pci_id_table); - -static struct pci_driver orinoco_pci_driver = { - .name = DRIVER_NAME, - .id_table = orinoco_pci_id_table, - .probe = orinoco_pci_init_one, - .remove = orinoco_pci_remove_one, - .suspend = orinoco_pci_suspend, - .resume = orinoco_pci_resume, -}; - -static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION - " (Pavel Roskin ," - " David Gibson &" - " Jean Tourrilhes )"; -MODULE_AUTHOR("Pavel Roskin &" - " David Gibson "); -MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface"); -MODULE_LICENSE("Dual MPL/GPL"); - -static int __init orinoco_pci_init(void) -{ - printk(KERN_DEBUG "%s\n", version); - return pci_register_driver(&orinoco_pci_driver); -} - -static void __exit orinoco_pci_exit(void) -{ - pci_unregister_driver(&orinoco_pci_driver); -} - -module_init(orinoco_pci_init); -module_exit(orinoco_pci_exit); - -/* - * Local variables: - * c-indent-level: 8 - * c-basic-offset: 8 - * tab-width: 8 - * End: - */ diff --git a/drivers/net/wireless/orinoco/orinoco_pci.h b/drivers/net/wireless/orinoco/orinoco_pci.h deleted file mode 100644 index 43f5b9f5a0b0..000000000000 --- a/drivers/net/wireless/orinoco/orinoco_pci.h +++ /dev/null @@ -1,68 +0,0 @@ -/* orinoco_pci.h - * - * Common code for all Orinoco drivers for PCI devices, including - * both native PCI and PCMCIA-to-PCI bridges. - * - * Copyright (C) 2005, Pavel Roskin. - * See main.c for license. - */ - -#ifndef _ORINOCO_PCI_H -#define _ORINOCO_PCI_H - -#include - -/* Driver specific data */ -struct orinoco_pci_card { - void __iomem *bridge_io; - void __iomem *attr_io; -}; - -#ifdef CONFIG_PM -static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct orinoco_private *priv = pci_get_drvdata(pdev); - - orinoco_down(priv); - free_irq(pdev->irq, priv); - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D3hot); - - return 0; -} - -static int orinoco_pci_resume(struct pci_dev *pdev) -{ - struct orinoco_private *priv = pci_get_drvdata(pdev); - struct net_device *dev = priv->ndev; - int err; - - pci_set_power_state(pdev, PCI_D0); - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR "%s: pci_enable_device failed on resume\n", - dev->name); - return err; - } - pci_restore_state(pdev); - - err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, - dev->name, priv); - if (err) { - printk(KERN_ERR "%s: cannot re-allocate IRQ on resume\n", - dev->name); - pci_disable_device(pdev); - return -EBUSY; - } - - err = orinoco_up(priv); - - return err; -} -#else -#define orinoco_pci_suspend NULL -#define orinoco_pci_resume NULL -#endif - -#endif /* _ORINOCO_PCI_H */ diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/orinoco/orinoco_plx.c deleted file mode 100644 index 221352027779..000000000000 --- a/drivers/net/wireless/orinoco/orinoco_plx.c +++ /dev/null @@ -1,371 +0,0 @@ -/* orinoco_plx.c - * - * Driver for Prism II devices which would usually be driven by orinoco_cs, - * but are connected to the PCI bus by a PLX9052. - * - * Current maintainers are: - * Pavel Roskin - * and David Gibson - * - * (C) Copyright David Gibson, IBM Corp. 2001-2003. - * Copyright (C) 2001 Daniel Barlow - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License - * at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and - * limitations under the License. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License version 2 (the "GPL"), in - * which case the provisions of the GPL are applicable instead of the - * above. If you wish to allow the use of your version of this file - * only under the terms of the GPL and not to allow others to use your - * version of this file under the MPL, indicate your decision by - * deleting the provisions above and replace them with the notice and - * other provisions required by the GPL. If you do not delete the - * provisions above, a recipient may use your version of this file - * under either the MPL or the GPL. - * - * Here's the general details on how the PLX9052 adapter works: - * - * - Two PCI I/O address spaces, one 0x80 long which contains the - * PLX9052 registers, and one that's 0x40 long mapped to the PCMCIA - * slot I/O address space. - * - * - One PCI memory address space, mapped to the PCMCIA attribute space - * (containing the CIS). - * - * Using the later, you can read through the CIS data to make sure the - * card is compatible with the driver. Keep in mind that the PCMCIA - * spec specifies the CIS as the lower 8 bits of each word read from - * the CIS, so to read the bytes of the CIS, read every other byte - * (0,2,4,...). Passing that test, you need to enable the I/O address - * space on the PCMCIA card via the PCMCIA COR register. This is the - * first byte following the CIS. In my case (which may not have any - * relation to what's on the PRISM2 cards), COR was at offset 0x800 - * within the PCI memory space. Write 0x41 to the COR register to - * enable I/O mode and to select level triggered interrupts. To - * confirm you actually succeeded, read the COR register back and make - * sure it actually got set to 0x41, in case you have an unexpected - * card inserted. - * - * Following that, you can treat the second PCI I/O address space (the - * one that's not 0x80 in length) as the PCMCIA I/O space. - * - * Note that in the Eumitcom's source for their drivers, they register - * the interrupt as edge triggered when registering it with the - * Windows kernel. I don't recall how to register edge triggered on - * Linux (if it can be done at all). But in some experimentation, I - * don't see much operational difference between using either - * interrupt mode. Don't mess with the interrupt mode in the COR - * register though, as the PLX9052 wants level triggers with the way - * the serial EEPROM configures it on the WL11000. - * - * There's some other little quirks related to timing that I bumped - * into, but I don't recall right now. Also, there's two variants of - * the WL11000 I've seen, revision A1 and T2. These seem to differ - * slightly in the timings configured in the wait-state generator in - * the PLX9052. There have also been some comments from Eumitcom that - * cards shouldn't be hot swapped, apparently due to risk of cooking - * the PLX9052. I'm unsure why they believe this, as I can't see - * anything in the design that would really cause a problem, except - * for crashing drivers not written to expect it. And having developed - * drivers for the WL11000, I'd say it's quite tricky to write code - * that will successfully deal with a hot unplug. Very odd things - * happen on the I/O side of things. But anyway, be warned. Despite - * that, I've hot-swapped a number of times during debugging and - * driver development for various reasons (stuck WAIT# line after the - * radio card's firmware locks up). - */ - -#define DRIVER_NAME "orinoco_plx" -#define PFX DRIVER_NAME ": " - -#include -#include -#include -#include -#include -#include - -#include "orinoco.h" -#include "orinoco_pci.h" - -#define COR_OFFSET (0x3e0) /* COR attribute offset of Prism2 PC card */ -#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */ -#define COR_RESET (0x80) /* reset bit in the COR register */ -#define PLX_RESET_TIME (500) /* milliseconds */ - -#define PLX_INTCSR 0x4c /* Interrupt Control & Status Register */ -#define PLX_INTCSR_INTEN (1 << 6) /* Interrupt Enable bit */ - -/* - * Do a soft reset of the card using the Configuration Option Register - */ -static int orinoco_plx_cor_reset(struct orinoco_private *priv) -{ - struct hermes *hw = &priv->hw; - struct orinoco_pci_card *card = priv->card; - unsigned long timeout; - u16 reg; - - iowrite8(COR_VALUE | COR_RESET, card->attr_io + COR_OFFSET); - mdelay(1); - - iowrite8(COR_VALUE, card->attr_io + COR_OFFSET); - mdelay(1); - - /* Just in case, wait more until the card is no longer busy */ - timeout = jiffies + msecs_to_jiffies(PLX_RESET_TIME); - reg = hermes_read_regn(hw, CMD); - while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { - mdelay(1); - reg = hermes_read_regn(hw, CMD); - } - - /* Still busy? */ - if (reg & HERMES_CMD_BUSY) { - printk(KERN_ERR PFX "Busy timeout\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int orinoco_plx_hw_init(struct orinoco_pci_card *card) -{ - int i; - u32 csr_reg; - static const u8 cis_magic[] = { - 0x01, 0x03, 0x00, 0x00, 0xff, 0x17, 0x04, 0x67 - }; - - printk(KERN_DEBUG PFX "CIS: "); - for (i = 0; i < 16; i++) - printk("%02X:", ioread8(card->attr_io + (i << 1))); - printk("\n"); - - /* Verify whether a supported PC card is present */ - /* FIXME: we probably need to be smarted about this */ - for (i = 0; i < sizeof(cis_magic); i++) { - if (cis_magic[i] != ioread8(card->attr_io + (i << 1))) { - printk(KERN_ERR PFX "The CIS value of Prism2 PC " - "card is unexpected\n"); - return -ENODEV; - } - } - - /* bjoern: We need to tell the card to enable interrupts, in - case the serial eprom didn't do this already. See the - PLX9052 data book, p8-1 and 8-24 for reference. */ - csr_reg = ioread32(card->bridge_io + PLX_INTCSR); - if (!(csr_reg & PLX_INTCSR_INTEN)) { - csr_reg |= PLX_INTCSR_INTEN; - iowrite32(csr_reg, card->bridge_io + PLX_INTCSR); - csr_reg = ioread32(card->bridge_io + PLX_INTCSR); - if (!(csr_reg & PLX_INTCSR_INTEN)) { - printk(KERN_ERR PFX "Cannot enable interrupts\n"); - return -EIO; - } - } - - return 0; -} - -static int orinoco_plx_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int err; - struct orinoco_private *priv; - struct orinoco_pci_card *card; - void __iomem *hermes_io, *attr_io, *bridge_io; - - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR PFX "Cannot enable PCI device\n"); - return err; - } - - err = pci_request_regions(pdev, DRIVER_NAME); - if (err) { - printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); - goto fail_resources; - } - - bridge_io = pci_iomap(pdev, 1, 0); - if (!bridge_io) { - printk(KERN_ERR PFX "Cannot map bridge registers\n"); - err = -EIO; - goto fail_map_bridge; - } - - attr_io = pci_iomap(pdev, 2, 0); - if (!attr_io) { - printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n"); - err = -EIO; - goto fail_map_attr; - } - - hermes_io = pci_iomap(pdev, 3, 0); - if (!hermes_io) { - printk(KERN_ERR PFX "Cannot map chipset registers\n"); - err = -EIO; - goto fail_map_hermes; - } - - /* Allocate network device */ - priv = alloc_orinocodev(sizeof(*card), &pdev->dev, - orinoco_plx_cor_reset, NULL); - if (!priv) { - printk(KERN_ERR PFX "Cannot allocate network device\n"); - err = -ENOMEM; - goto fail_alloc; - } - - card = priv->card; - card->bridge_io = bridge_io; - card->attr_io = attr_io; - - hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); - - err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, - DRIVER_NAME, priv); - if (err) { - printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); - err = -EBUSY; - goto fail_irq; - } - - err = orinoco_plx_hw_init(card); - if (err) { - printk(KERN_ERR PFX "Hardware initialization failed\n"); - goto fail; - } - - err = orinoco_plx_cor_reset(priv); - if (err) { - printk(KERN_ERR PFX "Initial reset failed\n"); - goto fail; - } - - err = orinoco_init(priv); - if (err) { - printk(KERN_ERR PFX "orinoco_init() failed\n"); - goto fail; - } - - err = orinoco_if_add(priv, 0, 0, NULL); - if (err) { - printk(KERN_ERR PFX "orinoco_if_add() failed\n"); - goto fail_wiphy; - } - - pci_set_drvdata(pdev, priv); - - return 0; - - fail_wiphy: - wiphy_unregister(priv_to_wiphy(priv)); - fail: - free_irq(pdev->irq, priv); - - fail_irq: - free_orinocodev(priv); - - fail_alloc: - pci_iounmap(pdev, hermes_io); - - fail_map_hermes: - pci_iounmap(pdev, attr_io); - - fail_map_attr: - pci_iounmap(pdev, bridge_io); - - fail_map_bridge: - pci_release_regions(pdev); - - fail_resources: - pci_disable_device(pdev); - - return err; -} - -static void orinoco_plx_remove_one(struct pci_dev *pdev) -{ - struct orinoco_private *priv = pci_get_drvdata(pdev); - struct orinoco_pci_card *card = priv->card; - - orinoco_if_del(priv); - wiphy_unregister(priv_to_wiphy(priv)); - free_irq(pdev->irq, priv); - free_orinocodev(priv); - pci_iounmap(pdev, priv->hw.iobase); - pci_iounmap(pdev, card->attr_io); - pci_iounmap(pdev, card->bridge_io); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -static const struct pci_device_id orinoco_plx_id_table[] = { - {0x111a, 0x1023, PCI_ANY_ID, PCI_ANY_ID,}, /* Siemens SpeedStream SS1023 */ - {0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */ - {0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga - does this work? */ - {0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* SMC EZConnect SMC2602W, - Eumitcom PCI WL11000, - Addtron AWA-100 */ - {0x16ab, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* Global Sun Tech GL24110P */ - {0x16ab, 0x1101, PCI_ANY_ID, PCI_ANY_ID,}, /* Reported working, but unknown */ - {0x16ab, 0x1102, PCI_ANY_ID, PCI_ANY_ID,}, /* Linksys WDT11 */ - {0x16ec, 0x3685, PCI_ANY_ID, PCI_ANY_ID,}, /* USR 2415 */ - {0xec80, 0xec00, PCI_ANY_ID, PCI_ANY_ID,}, /* Belkin F5D6000 tested by - Brendan W. McAdams */ - {0x10b7, 0x7770, PCI_ANY_ID, PCI_ANY_ID,}, /* 3Com AirConnect PCI tested by - Damien Persohn */ - {0,}, -}; - -MODULE_DEVICE_TABLE(pci, orinoco_plx_id_table); - -static struct pci_driver orinoco_plx_driver = { - .name = DRIVER_NAME, - .id_table = orinoco_plx_id_table, - .probe = orinoco_plx_init_one, - .remove = orinoco_plx_remove_one, - .suspend = orinoco_pci_suspend, - .resume = orinoco_pci_resume, -}; - -static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION - " (Pavel Roskin ," - " David Gibson ," - " Daniel Barlow )"; -MODULE_AUTHOR("Daniel Barlow "); -MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge"); -MODULE_LICENSE("Dual MPL/GPL"); - -static int __init orinoco_plx_init(void) -{ - printk(KERN_DEBUG "%s\n", version); - return pci_register_driver(&orinoco_plx_driver); -} - -static void __exit orinoco_plx_exit(void) -{ - pci_unregister_driver(&orinoco_plx_driver); -} - -module_init(orinoco_plx_init); -module_exit(orinoco_plx_exit); - -/* - * Local variables: - * c-indent-level: 8 - * c-basic-offset: 8 - * tab-width: 8 - * End: - */ diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/orinoco/orinoco_tmd.c deleted file mode 100644 index 20ce569b8a43..000000000000 --- a/drivers/net/wireless/orinoco/orinoco_tmd.c +++ /dev/null @@ -1,246 +0,0 @@ -/* orinoco_tmd.c - * - * Driver for Prism II devices which would usually be driven by orinoco_cs, - * but are connected to the PCI bus by a TMD7160. - * - * Copyright (C) 2003 Joerg Dorchain - * based heavily upon orinoco_plx.c Copyright (C) 2001 Daniel Barlow - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License - * at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and - * limitations under the License. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License version 2 (the "GPL"), in - * which case the provisions of the GPL are applicable instead of the - * above. If you wish to allow the use of your version of this file - * only under the terms of the GPL and not to allow others to use your - * version of this file under the MPL, indicate your decision by - * deleting the provisions above and replace them with the notice and - * other provisions required by the GPL. If you do not delete the - * provisions above, a recipient may use your version of this file - * under either the MPL or the GPL. - * - * The actual driving is done by main.c, this is just resource - * allocation stuff. - * - * This driver is modeled after the orinoco_plx driver. The main - * difference is that the TMD chip has only IO port ranges and doesn't - * provide access to the PCMCIA attribute space. - * - * Pheecom sells cards with the TMD chip as "ASIC version" - */ - -#define DRIVER_NAME "orinoco_tmd" -#define PFX DRIVER_NAME ": " - -#include -#include -#include -#include -#include -#include - -#include "orinoco.h" -#include "orinoco_pci.h" - -#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */ -#define COR_RESET (0x80) /* reset bit in the COR register */ -#define TMD_RESET_TIME (500) /* milliseconds */ - -/* - * Do a soft reset of the card using the Configuration Option Register - */ -static int orinoco_tmd_cor_reset(struct orinoco_private *priv) -{ - struct hermes *hw = &priv->hw; - struct orinoco_pci_card *card = priv->card; - unsigned long timeout; - u16 reg; - - iowrite8(COR_VALUE | COR_RESET, card->bridge_io); - mdelay(1); - - iowrite8(COR_VALUE, card->bridge_io); - mdelay(1); - - /* Just in case, wait more until the card is no longer busy */ - timeout = jiffies + msecs_to_jiffies(TMD_RESET_TIME); - reg = hermes_read_regn(hw, CMD); - while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { - mdelay(1); - reg = hermes_read_regn(hw, CMD); - } - - /* Still busy? */ - if (reg & HERMES_CMD_BUSY) { - printk(KERN_ERR PFX "Busy timeout\n"); - return -ETIMEDOUT; - } - - return 0; -} - - -static int orinoco_tmd_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int err; - struct orinoco_private *priv; - struct orinoco_pci_card *card; - void __iomem *hermes_io, *bridge_io; - - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR PFX "Cannot enable PCI device\n"); - return err; - } - - err = pci_request_regions(pdev, DRIVER_NAME); - if (err) { - printk(KERN_ERR PFX "Cannot obtain PCI resources\n"); - goto fail_resources; - } - - bridge_io = pci_iomap(pdev, 1, 0); - if (!bridge_io) { - printk(KERN_ERR PFX "Cannot map bridge registers\n"); - err = -EIO; - goto fail_map_bridge; - } - - hermes_io = pci_iomap(pdev, 2, 0); - if (!hermes_io) { - printk(KERN_ERR PFX "Cannot map chipset registers\n"); - err = -EIO; - goto fail_map_hermes; - } - - /* Allocate network device */ - priv = alloc_orinocodev(sizeof(*card), &pdev->dev, - orinoco_tmd_cor_reset, NULL); - if (!priv) { - printk(KERN_ERR PFX "Cannot allocate network device\n"); - err = -ENOMEM; - goto fail_alloc; - } - - card = priv->card; - card->bridge_io = bridge_io; - - hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); - - err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, - DRIVER_NAME, priv); - if (err) { - printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); - err = -EBUSY; - goto fail_irq; - } - - err = orinoco_tmd_cor_reset(priv); - if (err) { - printk(KERN_ERR PFX "Initial reset failed\n"); - goto fail; - } - - err = orinoco_init(priv); - if (err) { - printk(KERN_ERR PFX "orinoco_init() failed\n"); - goto fail; - } - - err = orinoco_if_add(priv, 0, 0, NULL); - if (err) { - printk(KERN_ERR PFX "orinoco_if_add() failed\n"); - goto fail; - } - - pci_set_drvdata(pdev, priv); - - return 0; - - fail: - free_irq(pdev->irq, priv); - - fail_irq: - free_orinocodev(priv); - - fail_alloc: - pci_iounmap(pdev, hermes_io); - - fail_map_hermes: - pci_iounmap(pdev, bridge_io); - - fail_map_bridge: - pci_release_regions(pdev); - - fail_resources: - pci_disable_device(pdev); - - return err; -} - -static void orinoco_tmd_remove_one(struct pci_dev *pdev) -{ - struct orinoco_private *priv = pci_get_drvdata(pdev); - struct orinoco_pci_card *card = priv->card; - - orinoco_if_del(priv); - free_irq(pdev->irq, priv); - free_orinocodev(priv); - pci_iounmap(pdev, priv->hw.iobase); - pci_iounmap(pdev, card->bridge_io); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -static const struct pci_device_id orinoco_tmd_id_table[] = { - {0x15e8, 0x0131, PCI_ANY_ID, PCI_ANY_ID,}, /* NDC and OEMs, e.g. pheecom */ - {0,}, -}; - -MODULE_DEVICE_TABLE(pci, orinoco_tmd_id_table); - -static struct pci_driver orinoco_tmd_driver = { - .name = DRIVER_NAME, - .id_table = orinoco_tmd_id_table, - .probe = orinoco_tmd_init_one, - .remove = orinoco_tmd_remove_one, - .suspend = orinoco_pci_suspend, - .resume = orinoco_pci_resume, -}; - -static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION - " (Joerg Dorchain )"; -MODULE_AUTHOR("Joerg Dorchain "); -MODULE_DESCRIPTION("Driver for wireless LAN cards using the TMD7160 PCI bridge"); -MODULE_LICENSE("Dual MPL/GPL"); - -static int __init orinoco_tmd_init(void) -{ - printk(KERN_DEBUG "%s\n", version); - return pci_register_driver(&orinoco_tmd_driver); -} - -static void __exit orinoco_tmd_exit(void) -{ - pci_unregister_driver(&orinoco_tmd_driver); -} - -module_init(orinoco_tmd_init); -module_exit(orinoco_tmd_exit); - -/* - * Local variables: - * c-indent-level: 8 - * c-basic-offset: 8 - * tab-width: 8 - * End: - */ diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c deleted file mode 100644 index f2cd513d54b2..000000000000 --- a/drivers/net/wireless/orinoco/orinoco_usb.c +++ /dev/null @@ -1,1748 +0,0 @@ -/* - * USB Orinoco driver - * - * Copyright (c) 2003 Manuel Estrada Sainz - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License - * at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See - * the License for the specific language governing rights and - * limitations under the License. - * - * Alternatively, the contents of this file may be used under the - * terms of the GNU General Public License version 2 (the "GPL"), in - * which case the provisions of the GPL are applicable instead of the - * above. If you wish to allow the use of your version of this file - * only under the terms of the GPL and not to allow others to use your - * version of this file under the MPL, indicate your decision by - * deleting the provisions above and replace them with the notice and - * other provisions required by the GPL. If you do not delete the - * provisions above, a recipient may use your version of this file - * under either the MPL or the GPL. - * - * Queueing code based on linux-wlan-ng 0.2.1-pre5 - * - * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. - * - * The license is the same as above. - * - * Initialy based on USB Skeleton driver - 0.7 - * - * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * NOTE: The original USB Skeleton driver is GPL, but all that code is - * gone so MPL/GPL applies. - */ - -#define DRIVER_NAME "orinoco_usb" -#define PFX DRIVER_NAME ": " - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "mic.h" -#include "orinoco.h" - -#ifndef URB_ASYNC_UNLINK -#define URB_ASYNC_UNLINK 0 -#endif - -/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ -static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; -#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) - -struct header_struct { - /* 802.3 */ - u8 dest[ETH_ALEN]; - u8 src[ETH_ALEN]; - __be16 len; - /* 802.2 */ - u8 dsap; - u8 ssap; - u8 ctrl; - /* SNAP */ - u8 oui[3]; - __be16 ethertype; -} __packed; - -struct ez_usb_fw { - u16 size; - const u8 *code; -}; - -static struct ez_usb_fw firmware = { - .size = 0, - .code = NULL, -}; - -/* Debugging macros */ -#undef err -#define err(format, arg...) \ - do { printk(KERN_ERR PFX format "\n", ## arg); } while (0) - -MODULE_FIRMWARE("orinoco_ezusb_fw"); - -/* - * Under some conditions, the card gets stuck and stops paying attention - * to the world (i.e. data communication stalls) until we do something to - * it. Sending an INQ_TALLIES command seems to be enough and should be - * harmless otherwise. This behaviour has been observed when using the - * driver on a systemimager client during installation. In the past a - * timer was used to send INQ_TALLIES commands when there was no other - * activity, but it was troublesome and was removed. - */ - -#define USB_COMPAQ_VENDOR_ID 0x049f /* Compaq Computer Corp. */ -#define USB_COMPAQ_WL215_ID 0x001f /* Compaq WL215 USB Adapter */ -#define USB_COMPAQ_W200_ID 0x0076 /* Compaq W200 USB Adapter */ -#define USB_HP_WL215_ID 0x0082 /* Compaq WL215 USB Adapter */ - -#define USB_MELCO_VENDOR_ID 0x0411 -#define USB_BUFFALO_L11_ID 0x0006 /* BUFFALO WLI-USB-L11 */ -#define USB_BUFFALO_L11G_WR_ID 0x000B /* BUFFALO WLI-USB-L11G-WR */ -#define USB_BUFFALO_L11G_ID 0x000D /* BUFFALO WLI-USB-L11G */ - -#define USB_LUCENT_VENDOR_ID 0x047E /* Lucent Technologies */ -#define USB_LUCENT_ORINOCO_ID 0x0300 /* Lucent/Agere Orinoco USB Client */ - -#define USB_AVAYA8_VENDOR_ID 0x0D98 -#define USB_AVAYAE_VENDOR_ID 0x0D9E -#define USB_AVAYA_WIRELESS_ID 0x0300 /* Avaya Wireless USB Card */ - -#define USB_AGERE_VENDOR_ID 0x0D4E /* Agere Systems */ -#define USB_AGERE_MODEL0801_ID 0x1000 /* Wireless USB Card Model 0801 */ -#define USB_AGERE_MODEL0802_ID 0x1001 /* Wireless USB Card Model 0802 */ -#define USB_AGERE_REBRANDED_ID 0x047A /* WLAN USB Card */ - -#define USB_ELSA_VENDOR_ID 0x05CC -#define USB_ELSA_AIRLANCER_ID 0x3100 /* ELSA AirLancer USB-11 */ - -#define USB_LEGEND_VENDOR_ID 0x0E7C -#define USB_LEGEND_JOYNET_ID 0x0300 /* Joynet WLAN USB Card */ - -#define USB_SAMSUNG_VENDOR_ID 0x04E8 -#define USB_SAMSUNG_SEW2001U1_ID 0x5002 /* Samsung SEW-2001u Card */ -#define USB_SAMSUNG_SEW2001U2_ID 0x5B11 /* Samsung SEW-2001u Card */ -#define USB_SAMSUNG_SEW2003U_ID 0x7011 /* Samsung SEW-2003U Card */ - -#define USB_IGATE_VENDOR_ID 0x0681 -#define USB_IGATE_IGATE_11M_ID 0x0012 /* I-GATE 11M USB Card */ - -#define USB_FUJITSU_VENDOR_ID 0x0BF8 -#define USB_FUJITSU_E1100_ID 0x1002 /* connect2AIR WLAN E-1100 USB */ - -#define USB_2WIRE_VENDOR_ID 0x1630 -#define USB_2WIRE_WIRELESS_ID 0xff81 /* 2Wire Wireless USB adapter */ - - -#define EZUSB_REQUEST_FW_TRANS 0xA0 -#define EZUSB_REQUEST_TRIGER 0xAA -#define EZUSB_REQUEST_TRIG_AC 0xAC -#define EZUSB_CPUCS_REG 0x7F92 - -#define EZUSB_RID_TX 0x0700 -#define EZUSB_RID_RX 0x0701 -#define EZUSB_RID_INIT1 0x0702 -#define EZUSB_RID_ACK 0x0710 -#define EZUSB_RID_READ_PDA 0x0800 -#define EZUSB_RID_PROG_INIT 0x0852 -#define EZUSB_RID_PROG_SET_ADDR 0x0853 -#define EZUSB_RID_PROG_BYTES 0x0854 -#define EZUSB_RID_PROG_END 0x0855 -#define EZUSB_RID_DOCMD 0x0860 - -/* Recognize info frames */ -#define EZUSB_IS_INFO(id) ((id >= 0xF000) && (id <= 0xF2FF)) - -#define EZUSB_MAGIC 0x0210 - -#define EZUSB_FRAME_DATA 1 -#define EZUSB_FRAME_CONTROL 2 - -#define DEF_TIMEOUT (3 * HZ) - -#define BULK_BUF_SIZE 2048 - -#define MAX_DL_SIZE (BULK_BUF_SIZE - sizeof(struct ezusb_packet)) - -#define FW_BUF_SIZE 64 -#define FW_VAR_OFFSET_PTR 0x359 -#define FW_VAR_VALUE 0 -#define FW_HOLE_START 0x100 -#define FW_HOLE_END 0x300 - -struct ezusb_packet { - __le16 magic; /* 0x0210 */ - u8 req_reply_count; - u8 ans_reply_count; - __le16 frame_type; /* 0x01 for data frames, 0x02 otherwise */ - __le16 size; /* transport size */ - __le16 crc; /* CRC up to here */ - __le16 hermes_len; - __le16 hermes_rid; - u8 data[0]; -} __packed; - -/* Table of devices that work or may work with this driver */ -static struct usb_device_id ezusb_table[] = { - {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_WL215_ID)}, - {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_HP_WL215_ID)}, - {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_W200_ID)}, - {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11_ID)}, - {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_WR_ID)}, - {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_ID)}, - {USB_DEVICE(USB_LUCENT_VENDOR_ID, USB_LUCENT_ORINOCO_ID)}, - {USB_DEVICE(USB_AVAYA8_VENDOR_ID, USB_AVAYA_WIRELESS_ID)}, - {USB_DEVICE(USB_AVAYAE_VENDOR_ID, USB_AVAYA_WIRELESS_ID)}, - {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0801_ID)}, - {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0802_ID)}, - {USB_DEVICE(USB_ELSA_VENDOR_ID, USB_ELSA_AIRLANCER_ID)}, - {USB_DEVICE(USB_LEGEND_VENDOR_ID, USB_LEGEND_JOYNET_ID)}, - {USB_DEVICE_VER(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U1_ID, - 0, 0)}, - {USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U2_ID)}, - {USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2003U_ID)}, - {USB_DEVICE(USB_IGATE_VENDOR_ID, USB_IGATE_IGATE_11M_ID)}, - {USB_DEVICE(USB_FUJITSU_VENDOR_ID, USB_FUJITSU_E1100_ID)}, - {USB_DEVICE(USB_2WIRE_VENDOR_ID, USB_2WIRE_WIRELESS_ID)}, - {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_REBRANDED_ID)}, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, ezusb_table); - -/* Structure to hold all of our device specific stuff */ -struct ezusb_priv { - struct usb_device *udev; - struct net_device *dev; - struct mutex mtx; - spinlock_t req_lock; - struct list_head req_pending; - struct list_head req_active; - spinlock_t reply_count_lock; - u16 hermes_reg_fake[0x40]; - u8 *bap_buf; - struct urb *read_urb; - int read_pipe; - int write_pipe; - u8 reply_count; -}; - -enum ezusb_state { - EZUSB_CTX_START, - EZUSB_CTX_QUEUED, - EZUSB_CTX_REQ_SUBMITTED, - EZUSB_CTX_REQ_COMPLETE, - EZUSB_CTX_RESP_RECEIVED, - EZUSB_CTX_REQ_TIMEOUT, - EZUSB_CTX_REQ_FAILED, - EZUSB_CTX_RESP_TIMEOUT, - EZUSB_CTX_REQSUBMIT_FAIL, - EZUSB_CTX_COMPLETE, -}; - -struct request_context { - struct list_head list; - atomic_t refcount; - struct completion done; /* Signals that CTX is dead */ - int killed; - struct urb *outurb; /* OUT for req pkt */ - struct ezusb_priv *upriv; - struct ezusb_packet *buf; - int buf_length; - struct timer_list timer; /* Timeout handling */ - enum ezusb_state state; /* Current state */ - /* the RID that we will wait for */ - u16 out_rid; - u16 in_rid; -}; - - -/* Forward declarations */ -static void ezusb_ctx_complete(struct request_context *ctx); -static void ezusb_req_queue_run(struct ezusb_priv *upriv); -static void ezusb_bulk_in_callback(struct urb *urb); - -static inline u8 ezusb_reply_inc(u8 count) -{ - if (count < 0x7F) - return count + 1; - else - return 1; -} - -static void ezusb_request_context_put(struct request_context *ctx) -{ - if (!atomic_dec_and_test(&ctx->refcount)) - return; - - WARN_ON(!ctx->done.done); - BUG_ON(ctx->outurb->status == -EINPROGRESS); - BUG_ON(timer_pending(&ctx->timer)); - usb_free_urb(ctx->outurb); - kfree(ctx->buf); - kfree(ctx); -} - -static inline void ezusb_mod_timer(struct ezusb_priv *upriv, - struct timer_list *timer, - unsigned long expire) -{ - if (!upriv->udev) - return; - mod_timer(timer, expire); -} - -static void ezusb_request_timerfn(u_long _ctx) -{ - struct request_context *ctx = (void *) _ctx; - - ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; - if (usb_unlink_urb(ctx->outurb) == -EINPROGRESS) { - ctx->state = EZUSB_CTX_REQ_TIMEOUT; - } else { - ctx->state = EZUSB_CTX_RESP_TIMEOUT; - dev_dbg(&ctx->outurb->dev->dev, "couldn't unlink\n"); - atomic_inc(&ctx->refcount); - ctx->killed = 1; - ezusb_ctx_complete(ctx); - ezusb_request_context_put(ctx); - } -}; - -static struct request_context *ezusb_alloc_ctx(struct ezusb_priv *upriv, - u16 out_rid, u16 in_rid) -{ - struct request_context *ctx; - - ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC); - if (!ctx) - return NULL; - - ctx->buf = kmalloc(BULK_BUF_SIZE, GFP_ATOMIC); - if (!ctx->buf) { - kfree(ctx); - return NULL; - } - ctx->outurb = usb_alloc_urb(0, GFP_ATOMIC); - if (!ctx->outurb) { - kfree(ctx->buf); - kfree(ctx); - return NULL; - } - - ctx->upriv = upriv; - ctx->state = EZUSB_CTX_START; - ctx->out_rid = out_rid; - ctx->in_rid = in_rid; - - atomic_set(&ctx->refcount, 1); - init_completion(&ctx->done); - - setup_timer(&ctx->timer, ezusb_request_timerfn, (u_long)ctx); - return ctx; -} - - -/* Hopefully the real complete_all will soon be exported, in the mean - * while this should work. */ -static inline void ezusb_complete_all(struct completion *comp) -{ - complete(comp); - complete(comp); - complete(comp); - complete(comp); -} - -static void ezusb_ctx_complete(struct request_context *ctx) -{ - struct ezusb_priv *upriv = ctx->upriv; - unsigned long flags; - - spin_lock_irqsave(&upriv->req_lock, flags); - - list_del_init(&ctx->list); - if (upriv->udev) { - spin_unlock_irqrestore(&upriv->req_lock, flags); - ezusb_req_queue_run(upriv); - spin_lock_irqsave(&upriv->req_lock, flags); - } - - switch (ctx->state) { - case EZUSB_CTX_COMPLETE: - case EZUSB_CTX_REQSUBMIT_FAIL: - case EZUSB_CTX_REQ_FAILED: - case EZUSB_CTX_REQ_TIMEOUT: - case EZUSB_CTX_RESP_TIMEOUT: - spin_unlock_irqrestore(&upriv->req_lock, flags); - - if ((ctx->out_rid == EZUSB_RID_TX) && upriv->dev) { - struct net_device *dev = upriv->dev; - struct orinoco_private *priv = ndev_priv(dev); - struct net_device_stats *stats = &priv->stats; - - if (ctx->state != EZUSB_CTX_COMPLETE) - stats->tx_errors++; - else - stats->tx_packets++; - - netif_wake_queue(dev); - } - ezusb_complete_all(&ctx->done); - ezusb_request_context_put(ctx); - break; - - default: - spin_unlock_irqrestore(&upriv->req_lock, flags); - if (!upriv->udev) { - /* This is normal, as all request contexts get flushed - * when the device is disconnected */ - err("Called, CTX not terminating, but device gone"); - ezusb_complete_all(&ctx->done); - ezusb_request_context_put(ctx); - break; - } - - err("Called, CTX not in terminating state."); - /* Things are really bad if this happens. Just leak - * the CTX because it may still be linked to the - * queue or the OUT urb may still be active. - * Just leaking at least prevents an Oops or Panic. - */ - break; - } -} - -/** - * ezusb_req_queue_run: - * Description: - * Note: Only one active CTX at any one time, because there's no - * other (reliable) way to match the response URB to the correct - * CTX. - **/ -static void ezusb_req_queue_run(struct ezusb_priv *upriv) -{ - unsigned long flags; - struct request_context *ctx; - int result; - - spin_lock_irqsave(&upriv->req_lock, flags); - - if (!list_empty(&upriv->req_active)) - goto unlock; - - if (list_empty(&upriv->req_pending)) - goto unlock; - - ctx = - list_entry(upriv->req_pending.next, struct request_context, - list); - - if (!ctx->upriv->udev) - goto unlock; - - /* We need to split this off to avoid a race condition */ - list_move_tail(&ctx->list, &upriv->req_active); - - if (ctx->state == EZUSB_CTX_QUEUED) { - atomic_inc(&ctx->refcount); - result = usb_submit_urb(ctx->outurb, GFP_ATOMIC); - if (result) { - ctx->state = EZUSB_CTX_REQSUBMIT_FAIL; - - spin_unlock_irqrestore(&upriv->req_lock, flags); - - err("Fatal, failed to submit command urb." - " error=%d\n", result); - - ezusb_ctx_complete(ctx); - ezusb_request_context_put(ctx); - goto done; - } - - ctx->state = EZUSB_CTX_REQ_SUBMITTED; - ezusb_mod_timer(ctx->upriv, &ctx->timer, - jiffies + DEF_TIMEOUT); - } - - unlock: - spin_unlock_irqrestore(&upriv->req_lock, flags); - - done: - return; -} - -static void ezusb_req_enqueue_run(struct ezusb_priv *upriv, - struct request_context *ctx) -{ - unsigned long flags; - - spin_lock_irqsave(&upriv->req_lock, flags); - - if (!ctx->upriv->udev) { - spin_unlock_irqrestore(&upriv->req_lock, flags); - goto done; - } - atomic_inc(&ctx->refcount); - list_add_tail(&ctx->list, &upriv->req_pending); - spin_unlock_irqrestore(&upriv->req_lock, flags); - - ctx->state = EZUSB_CTX_QUEUED; - ezusb_req_queue_run(upriv); - - done: - return; -} - -static void ezusb_request_out_callback(struct urb *urb) -{ - unsigned long flags; - enum ezusb_state state; - struct request_context *ctx = urb->context; - struct ezusb_priv *upriv = ctx->upriv; - - spin_lock_irqsave(&upriv->req_lock, flags); - - del_timer(&ctx->timer); - - if (ctx->killed) { - spin_unlock_irqrestore(&upriv->req_lock, flags); - pr_warn("interrupt called with dead ctx\n"); - goto out; - } - - state = ctx->state; - - if (urb->status == 0) { - switch (state) { - case EZUSB_CTX_REQ_SUBMITTED: - if (ctx->in_rid) { - ctx->state = EZUSB_CTX_REQ_COMPLETE; - /* reply URB still pending */ - ezusb_mod_timer(upriv, &ctx->timer, - jiffies + DEF_TIMEOUT); - spin_unlock_irqrestore(&upriv->req_lock, - flags); - break; - } - /* fall through */ - case EZUSB_CTX_RESP_RECEIVED: - /* IN already received before this OUT-ACK */ - ctx->state = EZUSB_CTX_COMPLETE; - spin_unlock_irqrestore(&upriv->req_lock, flags); - ezusb_ctx_complete(ctx); - break; - - default: - spin_unlock_irqrestore(&upriv->req_lock, flags); - err("Unexpected state(0x%x, %d) in OUT URB", - state, urb->status); - break; - } - } else { - /* If someone cancels the OUT URB then its status - * should be either -ECONNRESET or -ENOENT. - */ - switch (state) { - case EZUSB_CTX_REQ_SUBMITTED: - case EZUSB_CTX_RESP_RECEIVED: - ctx->state = EZUSB_CTX_REQ_FAILED; - /* fall through */ - - case EZUSB_CTX_REQ_FAILED: - case EZUSB_CTX_REQ_TIMEOUT: - spin_unlock_irqrestore(&upriv->req_lock, flags); - - ezusb_ctx_complete(ctx); - break; - - default: - spin_unlock_irqrestore(&upriv->req_lock, flags); - - err("Unexpected state(0x%x, %d) in OUT URB", - state, urb->status); - break; - } - } - out: - ezusb_request_context_put(ctx); -} - -static void ezusb_request_in_callback(struct ezusb_priv *upriv, - struct urb *urb) -{ - struct ezusb_packet *ans = urb->transfer_buffer; - struct request_context *ctx = NULL; - enum ezusb_state state; - unsigned long flags; - - /* Find the CTX on the active queue that requested this URB */ - spin_lock_irqsave(&upriv->req_lock, flags); - if (upriv->udev) { - struct list_head *item; - - list_for_each(item, &upriv->req_active) { - struct request_context *c; - int reply_count; - - c = list_entry(item, struct request_context, list); - reply_count = - ezusb_reply_inc(c->buf->req_reply_count); - if ((ans->ans_reply_count == reply_count) - && (le16_to_cpu(ans->hermes_rid) == c->in_rid)) { - ctx = c; - break; - } - netdev_dbg(upriv->dev, "Skipped (0x%x/0x%x) (%d/%d)\n", - le16_to_cpu(ans->hermes_rid), c->in_rid, - ans->ans_reply_count, reply_count); - } - } - - if (ctx == NULL) { - spin_unlock_irqrestore(&upriv->req_lock, flags); - err("%s: got unexpected RID: 0x%04X", __func__, - le16_to_cpu(ans->hermes_rid)); - ezusb_req_queue_run(upriv); - return; - } - - /* The data we want is in the in buffer, exchange */ - urb->transfer_buffer = ctx->buf; - ctx->buf = (void *) ans; - ctx->buf_length = urb->actual_length; - - state = ctx->state; - switch (state) { - case EZUSB_CTX_REQ_SUBMITTED: - /* We have received our response URB before - * our request has been acknowledged. Do NOT - * destroy our CTX yet, because our OUT URB - * is still alive ... - */ - ctx->state = EZUSB_CTX_RESP_RECEIVED; - spin_unlock_irqrestore(&upriv->req_lock, flags); - - /* Let the machine continue running. */ - break; - - case EZUSB_CTX_REQ_COMPLETE: - /* This is the usual path: our request - * has already been acknowledged, and - * we have now received the reply. - */ - ctx->state = EZUSB_CTX_COMPLETE; - - /* Stop the intimer */ - del_timer(&ctx->timer); - spin_unlock_irqrestore(&upriv->req_lock, flags); - - /* Call the completion handler */ - ezusb_ctx_complete(ctx); - break; - - default: - spin_unlock_irqrestore(&upriv->req_lock, flags); - - pr_warn("Matched IN URB, unexpected context state(0x%x)\n", - state); - /* Throw this CTX away and try submitting another */ - del_timer(&ctx->timer); - ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; - usb_unlink_urb(ctx->outurb); - ezusb_req_queue_run(upriv); - break; - } /* switch */ -} - - -static void ezusb_req_ctx_wait(struct ezusb_priv *upriv, - struct request_context *ctx) -{ - switch (ctx->state) { - case EZUSB_CTX_QUEUED: - case EZUSB_CTX_REQ_SUBMITTED: - case EZUSB_CTX_REQ_COMPLETE: - case EZUSB_CTX_RESP_RECEIVED: - if (in_softirq()) { - /* If we get called from a timer, timeout timers don't - * get the chance to run themselves. So we make sure - * that we don't sleep for ever */ - int msecs = DEF_TIMEOUT * (1000 / HZ); - while (!ctx->done.done && msecs--) - udelay(1000); - } else { - wait_event_interruptible(ctx->done.wait, - ctx->done.done); - } - break; - default: - /* Done or failed - nothing to wait for */ - break; - } -} - -static inline u16 build_crc(struct ezusb_packet *data) -{ - u16 crc = 0; - u8 *bytes = (u8 *)data; - int i; - - for (i = 0; i < 8; i++) - crc = (crc << 1) + bytes[i]; - - return crc; -} - -/** - * ezusb_fill_req: - * - * if data == NULL and length > 0 the data is assumed to be already in - * the target buffer and only the header is filled. - * - */ -static int ezusb_fill_req(struct ezusb_packet *req, u16 length, u16 rid, - const void *data, u16 frame_type, u8 reply_count) -{ - int total_size = sizeof(*req) + length; - - BUG_ON(total_size > BULK_BUF_SIZE); - - req->magic = cpu_to_le16(EZUSB_MAGIC); - req->req_reply_count = reply_count; - req->ans_reply_count = 0; - req->frame_type = cpu_to_le16(frame_type); - req->size = cpu_to_le16(length + 4); - req->crc = cpu_to_le16(build_crc(req)); - req->hermes_len = cpu_to_le16(HERMES_BYTES_TO_RECLEN(length)); - req->hermes_rid = cpu_to_le16(rid); - if (data) - memcpy(req->data, data, length); - return total_size; -} - -static int ezusb_submit_in_urb(struct ezusb_priv *upriv) -{ - int retval = 0; - void *cur_buf = upriv->read_urb->transfer_buffer; - - if (upriv->read_urb->status == -EINPROGRESS) { - netdev_dbg(upriv->dev, "urb busy, not resubmiting\n"); - retval = -EBUSY; - goto exit; - } - usb_fill_bulk_urb(upriv->read_urb, upriv->udev, upriv->read_pipe, - cur_buf, BULK_BUF_SIZE, - ezusb_bulk_in_callback, upriv); - upriv->read_urb->transfer_flags = 0; - retval = usb_submit_urb(upriv->read_urb, GFP_ATOMIC); - if (retval) - err("%s submit failed %d", __func__, retval); - - exit: - return retval; -} - -static inline int ezusb_8051_cpucs(struct ezusb_priv *upriv, int reset) -{ - u8 res_val = reset; /* avoid argument promotion */ - - if (!upriv->udev) { - err("%s: !upriv->udev", __func__); - return -EFAULT; - } - return usb_control_msg(upriv->udev, - usb_sndctrlpipe(upriv->udev, 0), - EZUSB_REQUEST_FW_TRANS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_OUT, EZUSB_CPUCS_REG, 0, &res_val, - sizeof(res_val), DEF_TIMEOUT); -} - -static int ezusb_firmware_download(struct ezusb_priv *upriv, - struct ez_usb_fw *fw) -{ - u8 *fw_buffer; - int retval, addr; - int variant_offset; - - fw_buffer = kmalloc(FW_BUF_SIZE, GFP_KERNEL); - if (!fw_buffer) { - printk(KERN_ERR PFX "Out of memory for firmware buffer.\n"); - return -ENOMEM; - } - /* - * This byte is 1 and should be replaced with 0. The offset is - * 0x10AD in version 0.0.6. The byte in question should follow - * the end of the code pointed to by the jump in the beginning - * of the firmware. Also, it is read by code located at 0x358. - */ - variant_offset = be16_to_cpup((__be16 *) &fw->code[FW_VAR_OFFSET_PTR]); - if (variant_offset >= fw->size) { - printk(KERN_ERR PFX "Invalid firmware variant offset: " - "0x%04x\n", variant_offset); - retval = -EINVAL; - goto fail; - } - - retval = ezusb_8051_cpucs(upriv, 1); - if (retval < 0) - goto fail; - for (addr = 0; addr < fw->size; addr += FW_BUF_SIZE) { - /* 0x100-0x300 should be left alone, it contains card - * specific data, like USB enumeration information */ - if ((addr >= FW_HOLE_START) && (addr < FW_HOLE_END)) - continue; - - memcpy(fw_buffer, &fw->code[addr], FW_BUF_SIZE); - if (variant_offset >= addr && - variant_offset < addr + FW_BUF_SIZE) { - netdev_dbg(upriv->dev, - "Patching card_variant byte at 0x%04X\n", - variant_offset); - fw_buffer[variant_offset - addr] = FW_VAR_VALUE; - } - retval = usb_control_msg(upriv->udev, - usb_sndctrlpipe(upriv->udev, 0), - EZUSB_REQUEST_FW_TRANS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE - | USB_DIR_OUT, - addr, 0x0, - fw_buffer, FW_BUF_SIZE, - DEF_TIMEOUT); - - if (retval < 0) - goto fail; - } - retval = ezusb_8051_cpucs(upriv, 0); - if (retval < 0) - goto fail; - - goto exit; - fail: - printk(KERN_ERR PFX "Firmware download failed, error %d\n", - retval); - exit: - kfree(fw_buffer); - return retval; -} - -static int ezusb_access_ltv(struct ezusb_priv *upriv, - struct request_context *ctx, - u16 length, const void *data, u16 frame_type, - void *ans_buff, unsigned ans_size, u16 *ans_length) -{ - int req_size; - int retval = 0; - enum ezusb_state state; - - BUG_ON(in_irq()); - - if (!upriv->udev) { - retval = -ENODEV; - goto exit; - } - - if (upriv->read_urb->status != -EINPROGRESS) - err("%s: in urb not pending", __func__); - - /* protect upriv->reply_count, guarantee sequential numbers */ - spin_lock_bh(&upriv->reply_count_lock); - req_size = ezusb_fill_req(ctx->buf, length, ctx->out_rid, data, - frame_type, upriv->reply_count); - usb_fill_bulk_urb(ctx->outurb, upriv->udev, upriv->write_pipe, - ctx->buf, req_size, - ezusb_request_out_callback, ctx); - - if (ctx->in_rid) - upriv->reply_count = ezusb_reply_inc(upriv->reply_count); - - ezusb_req_enqueue_run(upriv, ctx); - - spin_unlock_bh(&upriv->reply_count_lock); - - if (ctx->in_rid) - ezusb_req_ctx_wait(upriv, ctx); - - state = ctx->state; - switch (state) { - case EZUSB_CTX_COMPLETE: - retval = ctx->outurb->status; - break; - - case EZUSB_CTX_QUEUED: - case EZUSB_CTX_REQ_SUBMITTED: - if (!ctx->in_rid) - break; - default: - err("%s: Unexpected context state %d", __func__, - state); - /* fall though */ - case EZUSB_CTX_REQ_TIMEOUT: - case EZUSB_CTX_REQ_FAILED: - case EZUSB_CTX_RESP_TIMEOUT: - case EZUSB_CTX_REQSUBMIT_FAIL: - printk(KERN_ERR PFX "Access failed, resetting (state %d," - " reply_count %d)\n", state, upriv->reply_count); - upriv->reply_count = 0; - if (state == EZUSB_CTX_REQ_TIMEOUT - || state == EZUSB_CTX_RESP_TIMEOUT) { - printk(KERN_ERR PFX "ctx timed out\n"); - retval = -ETIMEDOUT; - } else { - printk(KERN_ERR PFX "ctx failed\n"); - retval = -EFAULT; - } - goto exit; - } - if (ctx->in_rid) { - struct ezusb_packet *ans = ctx->buf; - unsigned exp_len; - - if (ans->hermes_len != 0) - exp_len = le16_to_cpu(ans->hermes_len) * 2 + 12; - else - exp_len = 14; - - if (exp_len != ctx->buf_length) { - err("%s: length mismatch for RID 0x%04x: " - "expected %d, got %d", __func__, - ctx->in_rid, exp_len, ctx->buf_length); - retval = -EIO; - goto exit; - } - - if (ans_buff) - memcpy(ans_buff, ans->data, min(exp_len, ans_size)); - if (ans_length) - *ans_length = le16_to_cpu(ans->hermes_len); - } - exit: - ezusb_request_context_put(ctx); - return retval; -} - -static int ezusb_write_ltv(struct hermes *hw, int bap, u16 rid, - u16 length, const void *data) -{ - struct ezusb_priv *upriv = hw->priv; - u16 frame_type; - struct request_context *ctx; - - if (length == 0) - return -EINVAL; - - length = HERMES_RECLEN_TO_BYTES(length); - - /* On memory mapped devices HERMES_RID_CNFGROUPADDRESSES can be - * set to be empty, but the USB bridge doesn't like it */ - if (length == 0) - return 0; - - ctx = ezusb_alloc_ctx(upriv, rid, EZUSB_RID_ACK); - if (!ctx) - return -ENOMEM; - - if (rid == EZUSB_RID_TX) - frame_type = EZUSB_FRAME_DATA; - else - frame_type = EZUSB_FRAME_CONTROL; - - return ezusb_access_ltv(upriv, ctx, length, data, frame_type, - NULL, 0, NULL); -} - -static int ezusb_read_ltv(struct hermes *hw, int bap, u16 rid, - unsigned bufsize, u16 *length, void *buf) -{ - struct ezusb_priv *upriv = hw->priv; - struct request_context *ctx; - - if (bufsize % 2) - return -EINVAL; - - ctx = ezusb_alloc_ctx(upriv, rid, rid); - if (!ctx) - return -ENOMEM; - - return ezusb_access_ltv(upriv, ctx, 0, NULL, EZUSB_FRAME_CONTROL, - buf, bufsize, length); -} - -static int ezusb_doicmd_wait(struct hermes *hw, u16 cmd, u16 parm0, u16 parm1, - u16 parm2, struct hermes_response *resp) -{ - struct ezusb_priv *upriv = hw->priv; - struct request_context *ctx; - - __le16 data[4] = { - cpu_to_le16(cmd), - cpu_to_le16(parm0), - cpu_to_le16(parm1), - cpu_to_le16(parm2), - }; - netdev_dbg(upriv->dev, - "0x%04X, parm0 0x%04X, parm1 0x%04X, parm2 0x%04X\n", cmd, - parm0, parm1, parm2); - ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK); - if (!ctx) - return -ENOMEM; - - return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, - EZUSB_FRAME_CONTROL, NULL, 0, NULL); -} - -static int ezusb_docmd_wait(struct hermes *hw, u16 cmd, u16 parm0, - struct hermes_response *resp) -{ - struct ezusb_priv *upriv = hw->priv; - struct request_context *ctx; - - __le16 data[4] = { - cpu_to_le16(cmd), - cpu_to_le16(parm0), - 0, - 0, - }; - netdev_dbg(upriv->dev, "0x%04X, parm0 0x%04X\n", cmd, parm0); - ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK); - if (!ctx) - return -ENOMEM; - - return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, - EZUSB_FRAME_CONTROL, NULL, 0, NULL); -} - -static int ezusb_bap_pread(struct hermes *hw, int bap, - void *buf, int len, u16 id, u16 offset) -{ - struct ezusb_priv *upriv = hw->priv; - struct ezusb_packet *ans = (void *) upriv->read_urb->transfer_buffer; - int actual_length = upriv->read_urb->actual_length; - - if (id == EZUSB_RID_RX) { - if ((sizeof(*ans) + offset + len) > actual_length) { - printk(KERN_ERR PFX "BAP read beyond buffer end " - "in rx frame\n"); - return -EINVAL; - } - memcpy(buf, ans->data + offset, len); - return 0; - } - - if (EZUSB_IS_INFO(id)) { - /* Include 4 bytes for length/type */ - if ((sizeof(*ans) + offset + len - 4) > actual_length) { - printk(KERN_ERR PFX "BAP read beyond buffer end " - "in info frame\n"); - return -EFAULT; - } - memcpy(buf, ans->data + offset - 4, len); - } else { - printk(KERN_ERR PFX "Unexpected fid 0x%04x\n", id); - return -EINVAL; - } - - return 0; -} - -static int ezusb_read_pda(struct hermes *hw, __le16 *pda, - u32 pda_addr, u16 pda_len) -{ - struct ezusb_priv *upriv = hw->priv; - struct request_context *ctx; - __le16 data[] = { - cpu_to_le16(pda_addr & 0xffff), - cpu_to_le16(pda_len - 4) - }; - ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_READ_PDA, EZUSB_RID_READ_PDA); - if (!ctx) - return -ENOMEM; - - /* wl_lkm does not include PDA size in the PDA area. - * We will pad the information into pda, so other routines - * don't have to be modified */ - pda[0] = cpu_to_le16(pda_len - 2); - /* Includes CFG_PROD_DATA but not itself */ - pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ - - return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, - EZUSB_FRAME_CONTROL, &pda[2], pda_len - 4, - NULL); -} - -static int ezusb_program_init(struct hermes *hw, u32 entry_point) -{ - struct ezusb_priv *upriv = hw->priv; - struct request_context *ctx; - __le32 data = cpu_to_le32(entry_point); - - ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_INIT, EZUSB_RID_ACK); - if (!ctx) - return -ENOMEM; - - return ezusb_access_ltv(upriv, ctx, sizeof(data), &data, - EZUSB_FRAME_CONTROL, NULL, 0, NULL); -} - -static int ezusb_program_end(struct hermes *hw) -{ - struct ezusb_priv *upriv = hw->priv; - struct request_context *ctx; - - ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_END, EZUSB_RID_ACK); - if (!ctx) - return -ENOMEM; - - return ezusb_access_ltv(upriv, ctx, 0, NULL, - EZUSB_FRAME_CONTROL, NULL, 0, NULL); -} - -static int ezusb_program_bytes(struct hermes *hw, const char *buf, - u32 addr, u32 len) -{ - struct ezusb_priv *upriv = hw->priv; - struct request_context *ctx; - __le32 data = cpu_to_le32(addr); - int err; - - ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_SET_ADDR, EZUSB_RID_ACK); - if (!ctx) - return -ENOMEM; - - err = ezusb_access_ltv(upriv, ctx, sizeof(data), &data, - EZUSB_FRAME_CONTROL, NULL, 0, NULL); - if (err) - return err; - - ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_BYTES, EZUSB_RID_ACK); - if (!ctx) - return -ENOMEM; - - return ezusb_access_ltv(upriv, ctx, len, buf, - EZUSB_FRAME_CONTROL, NULL, 0, NULL); -} - -static int ezusb_program(struct hermes *hw, const char *buf, - u32 addr, u32 len) -{ - u32 ch_addr; - u32 ch_len; - int err = 0; - - /* We can only send 2048 bytes out of the bulk xmit at a time, - * so we have to split any programming into chunks of <2048 - * bytes. */ - - ch_len = (len < MAX_DL_SIZE) ? len : MAX_DL_SIZE; - ch_addr = addr; - - while (ch_addr < (addr + len)) { - pr_debug("Programming subblock of length %d " - "to address 0x%08x. Data @ %p\n", - ch_len, ch_addr, &buf[ch_addr - addr]); - - err = ezusb_program_bytes(hw, &buf[ch_addr - addr], - ch_addr, ch_len); - if (err) - break; - - ch_addr += ch_len; - ch_len = ((addr + len - ch_addr) < MAX_DL_SIZE) ? - (addr + len - ch_addr) : MAX_DL_SIZE; - } - - return err; -} - -static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct net_device_stats *stats = &priv->stats; - struct ezusb_priv *upriv = priv->card; - u8 mic[MICHAEL_MIC_LEN + 1]; - int err = 0; - int tx_control; - unsigned long flags; - struct request_context *ctx; - u8 *buf; - int tx_size; - - if (!netif_running(dev)) { - printk(KERN_ERR "%s: Tx on stopped device!\n", - dev->name); - return NETDEV_TX_BUSY; - } - - if (netif_queue_stopped(dev)) { - printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", - dev->name); - return NETDEV_TX_BUSY; - } - - if (orinoco_lock(priv, &flags) != 0) { - printk(KERN_ERR - "%s: ezusb_xmit() called while hw_unavailable\n", - dev->name); - return NETDEV_TX_BUSY; - } - - if (!netif_carrier_ok(dev) || - (priv->iw_mode == NL80211_IFTYPE_MONITOR)) { - /* Oops, the firmware hasn't established a connection, - silently drop the packet (this seems to be the - safest approach). */ - goto drop; - } - - /* Check packet length */ - if (skb->len < ETH_HLEN) - goto drop; - - ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_TX, 0); - if (!ctx) - goto busy; - - memset(ctx->buf, 0, BULK_BUF_SIZE); - buf = ctx->buf->data; - - tx_control = 0; - - err = orinoco_process_xmit_skb(skb, dev, priv, &tx_control, - &mic[0]); - if (err) - goto drop; - - { - __le16 *tx_cntl = (__le16 *)buf; - *tx_cntl = cpu_to_le16(tx_control); - buf += sizeof(*tx_cntl); - } - - memcpy(buf, skb->data, skb->len); - buf += skb->len; - - if (tx_control & HERMES_TXCTRL_MIC) { - u8 *m = mic; - /* Mic has been offset so it can be copied to an even - * address. We're copying eveything anyway, so we - * don't need to copy that first byte. */ - if (skb->len % 2) - m++; - memcpy(buf, m, MICHAEL_MIC_LEN); - buf += MICHAEL_MIC_LEN; - } - - /* Finally, we actually initiate the send */ - netif_stop_queue(dev); - - /* The card may behave better if we send evenly sized usb transfers */ - tx_size = ALIGN(buf - ctx->buf->data, 2); - - err = ezusb_access_ltv(upriv, ctx, tx_size, NULL, - EZUSB_FRAME_DATA, NULL, 0, NULL); - - if (err) { - netif_start_queue(dev); - if (net_ratelimit()) - printk(KERN_ERR "%s: Error %d transmitting packet\n", - dev->name, err); - goto busy; - } - - dev->trans_start = jiffies; - stats->tx_bytes += skb->len; - goto ok; - - drop: - stats->tx_errors++; - stats->tx_dropped++; - - ok: - orinoco_unlock(priv, &flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - - busy: - orinoco_unlock(priv, &flags); - return NETDEV_TX_BUSY; -} - -static int ezusb_allocate(struct hermes *hw, u16 size, u16 *fid) -{ - *fid = EZUSB_RID_TX; - return 0; -} - - -static int ezusb_hard_reset(struct orinoco_private *priv) -{ - struct ezusb_priv *upriv = priv->card; - int retval = ezusb_8051_cpucs(upriv, 1); - - if (retval < 0) { - err("Failed to reset"); - return retval; - } - - retval = ezusb_8051_cpucs(upriv, 0); - if (retval < 0) { - err("Failed to unreset"); - return retval; - } - - netdev_dbg(upriv->dev, "sending control message\n"); - retval = usb_control_msg(upriv->udev, - usb_sndctrlpipe(upriv->udev, 0), - EZUSB_REQUEST_TRIGER, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_OUT, 0x0, 0x0, NULL, 0, - DEF_TIMEOUT); - if (retval < 0) { - err("EZUSB_REQUEST_TRIGER failed retval %d", retval); - return retval; - } -#if 0 - dbg("Sending EZUSB_REQUEST_TRIG_AC"); - retval = usb_control_msg(upriv->udev, - usb_sndctrlpipe(upriv->udev, 0), - EZUSB_REQUEST_TRIG_AC, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_OUT, 0x00FA, 0x0, NULL, 0, - DEF_TIMEOUT); - if (retval < 0) { - err("EZUSB_REQUEST_TRIG_AC failed retval %d", retval); - return retval; - } -#endif - - return 0; -} - - -static int ezusb_init(struct hermes *hw) -{ - struct ezusb_priv *upriv = hw->priv; - int retval; - - BUG_ON(in_interrupt()); - BUG_ON(!upriv); - - upriv->reply_count = 0; - /* Write the MAGIC number on the simulated registers to keep - * orinoco.c happy */ - hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); - hermes_write_regn(hw, RXFID, EZUSB_RID_RX); - - usb_kill_urb(upriv->read_urb); - ezusb_submit_in_urb(upriv); - - retval = ezusb_write_ltv(hw, 0, EZUSB_RID_INIT1, - HERMES_BYTES_TO_RECLEN(2), "\x10\x00"); - if (retval < 0) { - printk(KERN_ERR PFX "EZUSB_RID_INIT1 error %d\n", retval); - return retval; - } - - retval = ezusb_docmd_wait(hw, HERMES_CMD_INIT, 0, NULL); - if (retval < 0) { - printk(KERN_ERR PFX "HERMES_CMD_INIT error %d\n", retval); - return retval; - } - - return 0; -} - -static void ezusb_bulk_in_callback(struct urb *urb) -{ - struct ezusb_priv *upriv = (struct ezusb_priv *) urb->context; - struct ezusb_packet *ans = urb->transfer_buffer; - u16 crc; - u16 hermes_rid; - - if (upriv->udev == NULL) - return; - - if (urb->status == -ETIMEDOUT) { - /* When a device gets unplugged we get this every time - * we resubmit, flooding the logs. Since we don't use - * USB timeouts, it shouldn't happen any other time*/ - pr_warn("%s: urb timed out, not resubmitting\n", __func__); - return; - } - if (urb->status == -ECONNABORTED) { - pr_warn("%s: connection abort, resubmitting urb\n", - __func__); - goto resubmit; - } - if ((urb->status == -EILSEQ) - || (urb->status == -ENOENT) - || (urb->status == -ECONNRESET)) { - netdev_dbg(upriv->dev, "status %d, not resubmiting\n", - urb->status); - return; - } - if (urb->status) - netdev_dbg(upriv->dev, "status: %d length: %d\n", - urb->status, urb->actual_length); - if (urb->actual_length < sizeof(*ans)) { - err("%s: short read, ignoring", __func__); - goto resubmit; - } - crc = build_crc(ans); - if (le16_to_cpu(ans->crc) != crc) { - err("CRC error, ignoring packet"); - goto resubmit; - } - - hermes_rid = le16_to_cpu(ans->hermes_rid); - if ((hermes_rid != EZUSB_RID_RX) && !EZUSB_IS_INFO(hermes_rid)) { - ezusb_request_in_callback(upriv, urb); - } else if (upriv->dev) { - struct net_device *dev = upriv->dev; - struct orinoco_private *priv = ndev_priv(dev); - struct hermes *hw = &priv->hw; - - if (hermes_rid == EZUSB_RID_RX) { - __orinoco_ev_rx(dev, hw); - } else { - hermes_write_regn(hw, INFOFID, - le16_to_cpu(ans->hermes_rid)); - __orinoco_ev_info(dev, hw); - } - } - - resubmit: - if (upriv->udev) - ezusb_submit_in_urb(upriv); -} - -static inline void ezusb_delete(struct ezusb_priv *upriv) -{ - struct net_device *dev; - struct list_head *item; - struct list_head *tmp_item; - unsigned long flags; - - BUG_ON(in_interrupt()); - BUG_ON(!upriv); - - dev = upriv->dev; - mutex_lock(&upriv->mtx); - - upriv->udev = NULL; /* No timer will be rearmed from here */ - - usb_kill_urb(upriv->read_urb); - - spin_lock_irqsave(&upriv->req_lock, flags); - list_for_each_safe(item, tmp_item, &upriv->req_active) { - struct request_context *ctx; - int err; - - ctx = list_entry(item, struct request_context, list); - atomic_inc(&ctx->refcount); - - ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK; - err = usb_unlink_urb(ctx->outurb); - - spin_unlock_irqrestore(&upriv->req_lock, flags); - if (err == -EINPROGRESS) - wait_for_completion(&ctx->done); - - del_timer_sync(&ctx->timer); - /* FIXME: there is an slight chance for the irq handler to - * be running */ - if (!list_empty(&ctx->list)) - ezusb_ctx_complete(ctx); - - ezusb_request_context_put(ctx); - spin_lock_irqsave(&upriv->req_lock, flags); - } - spin_unlock_irqrestore(&upriv->req_lock, flags); - - list_for_each_safe(item, tmp_item, &upriv->req_pending) - ezusb_ctx_complete(list_entry(item, - struct request_context, list)); - - if (upriv->read_urb && upriv->read_urb->status == -EINPROGRESS) - printk(KERN_ERR PFX "Some URB in progress\n"); - - mutex_unlock(&upriv->mtx); - - if (upriv->read_urb) { - kfree(upriv->read_urb->transfer_buffer); - usb_free_urb(upriv->read_urb); - } - kfree(upriv->bap_buf); - if (upriv->dev) { - struct orinoco_private *priv = ndev_priv(upriv->dev); - orinoco_if_del(priv); - wiphy_unregister(priv_to_wiphy(upriv)); - free_orinocodev(priv); - } -} - -static void ezusb_lock_irqsave(spinlock_t *lock, - unsigned long *flags) __acquires(lock) -{ - spin_lock_bh(lock); -} - -static void ezusb_unlock_irqrestore(spinlock_t *lock, - unsigned long *flags) __releases(lock) -{ - spin_unlock_bh(lock); -} - -static void ezusb_lock_irq(spinlock_t *lock) __acquires(lock) -{ - spin_lock_bh(lock); -} - -static void ezusb_unlock_irq(spinlock_t *lock) __releases(lock) -{ - spin_unlock_bh(lock); -} - -static const struct hermes_ops ezusb_ops = { - .init = ezusb_init, - .cmd_wait = ezusb_docmd_wait, - .init_cmd_wait = ezusb_doicmd_wait, - .allocate = ezusb_allocate, - .read_ltv = ezusb_read_ltv, - .write_ltv = ezusb_write_ltv, - .bap_pread = ezusb_bap_pread, - .read_pda = ezusb_read_pda, - .program_init = ezusb_program_init, - .program_end = ezusb_program_end, - .program = ezusb_program, - .lock_irqsave = ezusb_lock_irqsave, - .unlock_irqrestore = ezusb_unlock_irqrestore, - .lock_irq = ezusb_lock_irq, - .unlock_irq = ezusb_unlock_irq, -}; - -static const struct net_device_ops ezusb_netdev_ops = { - .ndo_open = orinoco_open, - .ndo_stop = orinoco_stop, - .ndo_start_xmit = ezusb_xmit, - .ndo_set_rx_mode = orinoco_set_multicast_list, - .ndo_change_mtu = orinoco_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, - .ndo_tx_timeout = orinoco_tx_timeout, - .ndo_get_stats = orinoco_get_stats, -}; - -static int ezusb_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(interface); - struct orinoco_private *priv; - struct hermes *hw; - struct ezusb_priv *upriv = NULL; - struct usb_interface_descriptor *iface_desc; - struct usb_endpoint_descriptor *ep; - const struct firmware *fw_entry = NULL; - int retval = 0; - int i; - - priv = alloc_orinocodev(sizeof(*upriv), &udev->dev, - ezusb_hard_reset, NULL); - if (!priv) { - err("Couldn't allocate orinocodev"); - retval = -ENOMEM; - goto exit; - } - - hw = &priv->hw; - - upriv = priv->card; - - mutex_init(&upriv->mtx); - spin_lock_init(&upriv->reply_count_lock); - - spin_lock_init(&upriv->req_lock); - INIT_LIST_HEAD(&upriv->req_pending); - INIT_LIST_HEAD(&upriv->req_active); - - upriv->udev = udev; - - hw->iobase = (void __force __iomem *) &upriv->hermes_reg_fake; - hw->reg_spacing = HERMES_16BIT_REGSPACING; - hw->priv = upriv; - hw->ops = &ezusb_ops; - - /* set up the endpoint information */ - /* check out the endpoints */ - - iface_desc = &interface->altsetting[0].desc; - for (i = 0; i < iface_desc->bNumEndpoints; ++i) { - ep = &interface->altsetting[0].endpoint[i].desc; - - if (usb_endpoint_is_bulk_in(ep)) { - /* we found a bulk in endpoint */ - if (upriv->read_urb != NULL) { - pr_warn("Found a second bulk in ep, ignored\n"); - continue; - } - - upriv->read_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!upriv->read_urb) { - err("No free urbs available"); - goto error; - } - if (le16_to_cpu(ep->wMaxPacketSize) != 64) - pr_warn("bulk in: wMaxPacketSize!= 64\n"); - if (ep->bEndpointAddress != (2 | USB_DIR_IN)) - pr_warn("bulk in: bEndpointAddress: %d\n", - ep->bEndpointAddress); - upriv->read_pipe = usb_rcvbulkpipe(udev, - ep-> - bEndpointAddress); - upriv->read_urb->transfer_buffer = - kmalloc(BULK_BUF_SIZE, GFP_KERNEL); - if (!upriv->read_urb->transfer_buffer) { - err("Couldn't allocate IN buffer"); - goto error; - } - } - - if (usb_endpoint_is_bulk_out(ep)) { - /* we found a bulk out endpoint */ - if (upriv->bap_buf != NULL) { - pr_warn("Found a second bulk out ep, ignored\n"); - continue; - } - - if (le16_to_cpu(ep->wMaxPacketSize) != 64) - pr_warn("bulk out: wMaxPacketSize != 64\n"); - if (ep->bEndpointAddress != 2) - pr_warn("bulk out: bEndpointAddress: %d\n", - ep->bEndpointAddress); - upriv->write_pipe = usb_sndbulkpipe(udev, - ep-> - bEndpointAddress); - upriv->bap_buf = kmalloc(BULK_BUF_SIZE, GFP_KERNEL); - if (!upriv->bap_buf) { - err("Couldn't allocate bulk_out_buffer"); - goto error; - } - } - } - if (!upriv->bap_buf || !upriv->read_urb) { - err("Didn't find the required bulk endpoints"); - goto error; - } - - if (request_firmware(&fw_entry, "orinoco_ezusb_fw", - &interface->dev) == 0) { - firmware.size = fw_entry->size; - firmware.code = fw_entry->data; - } - if (firmware.size && firmware.code) { - if (ezusb_firmware_download(upriv, &firmware) < 0) - goto error; - } else { - err("No firmware to download"); - goto error; - } - - if (ezusb_hard_reset(priv) < 0) { - err("Cannot reset the device"); - goto error; - } - - /* If the firmware is already downloaded orinoco.c will call - * ezusb_init but if the firmware is not already there, that will make - * the kernel very unstable, so we try initializing here and quit in - * case of error */ - if (ezusb_init(hw) < 0) { - err("Couldn't initialize the device"); - err("Firmware may not be downloaded or may be wrong."); - goto error; - } - - /* Initialise the main driver */ - if (orinoco_init(priv) != 0) { - err("orinoco_init() failed\n"); - goto error; - } - - if (orinoco_if_add(priv, 0, 0, &ezusb_netdev_ops) != 0) { - upriv->dev = NULL; - err("%s: orinoco_if_add() failed", __func__); - wiphy_unregister(priv_to_wiphy(priv)); - goto error; - } - upriv->dev = priv->ndev; - - goto exit; - - error: - ezusb_delete(upriv); - if (upriv->dev) { - /* upriv->dev was 0, so ezusb_delete() didn't free it */ - free_orinocodev(priv); - } - upriv = NULL; - retval = -EFAULT; - exit: - if (fw_entry) { - firmware.code = NULL; - firmware.size = 0; - release_firmware(fw_entry); - } - usb_set_intfdata(interface, upriv); - return retval; -} - - -static void ezusb_disconnect(struct usb_interface *intf) -{ - struct ezusb_priv *upriv = usb_get_intfdata(intf); - usb_set_intfdata(intf, NULL); - ezusb_delete(upriv); - printk(KERN_INFO PFX "Disconnected\n"); -} - - -/* usb specific object needed to register this driver with the usb subsystem */ -static struct usb_driver orinoco_driver = { - .name = DRIVER_NAME, - .probe = ezusb_probe, - .disconnect = ezusb_disconnect, - .id_table = ezusb_table, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(orinoco_driver); - -MODULE_AUTHOR("Manuel Estrada Sainz"); -MODULE_DESCRIPTION("Driver for Orinoco wireless LAN cards using EZUSB bridge"); -MODULE_LICENSE("Dual MPL/GPL"); diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/orinoco/scan.c deleted file mode 100644 index 2c66166add70..000000000000 --- a/drivers/net/wireless/orinoco/scan.c +++ /dev/null @@ -1,251 +0,0 @@ -/* Helpers for managing scan queues - * - * See copyright notice in main.c - */ - -#include -#include -#include -#include -#include - -#include "hermes.h" -#include "orinoco.h" -#include "main.h" - -#include "scan.h" - -#define ZERO_DBM_OFFSET 0x95 -#define MAX_SIGNAL_LEVEL 0x8A -#define MIN_SIGNAL_LEVEL 0x2F - -#define SIGNAL_TO_DBM(x) \ - (clamp_t(s32, (x), MIN_SIGNAL_LEVEL, MAX_SIGNAL_LEVEL) \ - - ZERO_DBM_OFFSET) -#define SIGNAL_TO_MBM(x) (SIGNAL_TO_DBM(x) * 100) - -static int symbol_build_supp_rates(u8 *buf, const __le16 *rates) -{ - int i; - u8 rate; - - buf[0] = WLAN_EID_SUPP_RATES; - for (i = 0; i < 5; i++) { - rate = le16_to_cpu(rates[i]); - /* NULL terminated */ - if (rate == 0x0) - break; - buf[i + 2] = rate; - } - buf[1] = i; - - return i + 2; -} - -static int prism_build_supp_rates(u8 *buf, const u8 *rates) -{ - int i; - - buf[0] = WLAN_EID_SUPP_RATES; - for (i = 0; i < 8; i++) { - /* NULL terminated */ - if (rates[i] == 0x0) - break; - buf[i + 2] = rates[i]; - } - buf[1] = i; - - /* We might still have another 2 rates, which need to go in - * extended supported rates */ - if (i == 8 && rates[i] > 0) { - buf[10] = WLAN_EID_EXT_SUPP_RATES; - for (; i < 10; i++) { - /* NULL terminated */ - if (rates[i] == 0x0) - break; - buf[i + 2] = rates[i]; - } - buf[11] = i - 8; - } - - return (i < 8) ? i + 2 : i + 4; -} - -static void orinoco_add_hostscan_result(struct orinoco_private *priv, - const union hermes_scan_info *bss) -{ - struct wiphy *wiphy = priv_to_wiphy(priv); - struct ieee80211_channel *channel; - struct cfg80211_bss *cbss; - u8 *ie; - u8 ie_buf[46]; - u64 timestamp; - s32 signal; - u16 capability; - u16 beacon_interval; - int ie_len; - int freq; - int len; - - len = le16_to_cpu(bss->a.essid_len); - - /* Reconstruct SSID and bitrate IEs to pass up */ - ie_buf[0] = WLAN_EID_SSID; - ie_buf[1] = len; - memcpy(&ie_buf[2], bss->a.essid, len); - - ie = ie_buf + len + 2; - ie_len = ie_buf[1] + 2; - switch (priv->firmware_type) { - case FIRMWARE_TYPE_SYMBOL: - ie_len += symbol_build_supp_rates(ie, bss->s.rates); - break; - - case FIRMWARE_TYPE_INTERSIL: - ie_len += prism_build_supp_rates(ie, bss->p.rates); - break; - - case FIRMWARE_TYPE_AGERE: - default: - break; - } - - freq = ieee80211_channel_to_frequency( - le16_to_cpu(bss->a.channel), IEEE80211_BAND_2GHZ); - channel = ieee80211_get_channel(wiphy, freq); - if (!channel) { - printk(KERN_DEBUG "Invalid channel designation %04X(%04X)", - bss->a.channel, freq); - return; /* Then ignore it for now */ - } - timestamp = 0; - capability = le16_to_cpu(bss->a.capabilities); - beacon_interval = le16_to_cpu(bss->a.beacon_interv); - signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level)); - - cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN, - bss->a.bssid, timestamp, capability, - beacon_interval, ie_buf, ie_len, signal, - GFP_KERNEL); - cfg80211_put_bss(wiphy, cbss); -} - -void orinoco_add_extscan_result(struct orinoco_private *priv, - struct agere_ext_scan_info *bss, - size_t len) -{ - struct wiphy *wiphy = priv_to_wiphy(priv); - struct ieee80211_channel *channel; - struct cfg80211_bss *cbss; - const u8 *ie; - u64 timestamp; - s32 signal; - u16 capability; - u16 beacon_interval; - size_t ie_len; - int chan, freq; - - ie_len = len - sizeof(*bss); - ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len); - chan = ie ? ie[2] : 0; - freq = ieee80211_channel_to_frequency(chan, IEEE80211_BAND_2GHZ); - channel = ieee80211_get_channel(wiphy, freq); - - timestamp = le64_to_cpu(bss->timestamp); - capability = le16_to_cpu(bss->capabilities); - beacon_interval = le16_to_cpu(bss->beacon_interval); - ie = bss->data; - signal = SIGNAL_TO_MBM(bss->level); - - cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN, - bss->bssid, timestamp, capability, - beacon_interval, ie, ie_len, signal, - GFP_KERNEL); - cfg80211_put_bss(wiphy, cbss); -} - -void orinoco_add_hostscan_results(struct orinoco_private *priv, - unsigned char *buf, - size_t len) -{ - int offset; /* In the scan data */ - size_t atom_len; - bool abort = false; - - switch (priv->firmware_type) { - case FIRMWARE_TYPE_AGERE: - atom_len = sizeof(struct agere_scan_apinfo); - offset = 0; - break; - - case FIRMWARE_TYPE_SYMBOL: - /* Lack of documentation necessitates this hack. - * Different firmwares have 68 or 76 byte long atoms. - * We try modulo first. If the length divides by both, - * we check what would be the channel in the second - * frame for a 68-byte atom. 76-byte atoms have 0 there. - * Valid channel cannot be 0. */ - if (len % 76) - atom_len = 68; - else if (len % 68) - atom_len = 76; - else if (len >= 1292 && buf[68] == 0) - atom_len = 76; - else - atom_len = 68; - offset = 0; - break; - - case FIRMWARE_TYPE_INTERSIL: - offset = 4; - if (priv->has_hostscan) { - atom_len = le16_to_cpup((__le16 *)buf); - /* Sanity check for atom_len */ - if (atom_len < sizeof(struct prism2_scan_apinfo)) { - printk(KERN_ERR "%s: Invalid atom_len in scan " - "data: %zu\n", priv->ndev->name, - atom_len); - abort = true; - goto scan_abort; - } - } else - atom_len = offsetof(struct prism2_scan_apinfo, atim); - break; - - default: - abort = true; - goto scan_abort; - } - - /* Check that we got an whole number of atoms */ - if ((len - offset) % atom_len) { - printk(KERN_ERR "%s: Unexpected scan data length %zu, " - "atom_len %zu, offset %d\n", priv->ndev->name, len, - atom_len, offset); - abort = true; - goto scan_abort; - } - - /* Process the entries one by one */ - for (; offset + atom_len <= len; offset += atom_len) { - union hermes_scan_info *atom; - - atom = (union hermes_scan_info *) (buf + offset); - - orinoco_add_hostscan_result(priv, atom); - } - - scan_abort: - if (priv->scan_request) { - cfg80211_scan_done(priv->scan_request, abort); - priv->scan_request = NULL; - } -} - -void orinoco_scan_done(struct orinoco_private *priv, bool abort) -{ - if (priv->scan_request) { - cfg80211_scan_done(priv->scan_request, abort); - priv->scan_request = NULL; - } -} diff --git a/drivers/net/wireless/orinoco/scan.h b/drivers/net/wireless/orinoco/scan.h deleted file mode 100644 index 27281fb0a6dc..000000000000 --- a/drivers/net/wireless/orinoco/scan.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Helpers for managing scan queues - * - * See copyright notice in main.c - */ -#ifndef _ORINOCO_SCAN_H_ -#define _ORINOCO_SCAN_H_ - -/* Forward declarations */ -struct orinoco_private; -struct agere_ext_scan_info; - -/* Add scan results */ -void orinoco_add_extscan_result(struct orinoco_private *priv, - struct agere_ext_scan_info *atom, - size_t len); -void orinoco_add_hostscan_results(struct orinoco_private *dev, - unsigned char *buf, - size_t len); -void orinoco_scan_done(struct orinoco_private *priv, bool abort); - -#endif /* _ORINOCO_SCAN_H_ */ diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c deleted file mode 100644 index b60048c95e0a..000000000000 --- a/drivers/net/wireless/orinoco/spectrum_cs.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Driver for 802.11b cards using RAM-loadable Symbol firmware, such as - * Symbol Wireless Networker LA4137, CompactFlash cards by Socket - * Communications and Intel PRO/Wireless 2011B. - * - * The driver implements Symbol firmware download. The rest is handled - * in hermes.c and main.c. - * - * Utilities for downloading the Symbol firmware are available at - * http://sourceforge.net/projects/orinoco/ - * - * Copyright (C) 2002-2005 Pavel Roskin - * Portions based on orinoco_cs.c: - * Copyright (C) David Gibson, Linuxcare Australia - * Portions based on Spectrum24tDnld.c from original spectrum24 driver: - * Copyright (C) Symbol Technologies. - * - * See copyright notice in file main.c. - */ - -#define DRIVER_NAME "spectrum_cs" -#define PFX DRIVER_NAME ": " - -#include -#include -#include -#include -#include -#include - -#include "orinoco.h" - -/********************************************************************/ -/* Module stuff */ -/********************************************************************/ - -MODULE_AUTHOR("Pavel Roskin "); -MODULE_DESCRIPTION("Driver for Symbol Spectrum24 Trilogy cards with firmware downloader"); -MODULE_LICENSE("Dual MPL/GPL"); - -/* Module parameters */ - -/* Some D-Link cards have buggy CIS. They do work at 5v properly, but - * don't have any CIS entry for it. This workaround it... */ -static int ignore_cis_vcc; /* = 0 */ -module_param(ignore_cis_vcc, int, 0); -MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket"); - -/********************************************************************/ -/* Data structures */ -/********************************************************************/ - -/* PCMCIA specific device information (goes in the card field of - * struct orinoco_private */ -struct orinoco_pccard { - struct pcmcia_device *p_dev; -}; - -/********************************************************************/ -/* Function prototypes */ -/********************************************************************/ - -static int spectrum_cs_config(struct pcmcia_device *link); -static void spectrum_cs_release(struct pcmcia_device *link); - -/* Constants for the CISREG_CCSR register */ -#define HCR_RUN 0x07 /* run firmware after reset */ -#define HCR_IDLE 0x0E /* don't run firmware after reset */ -#define HCR_MEM16 0x10 /* memory width bit, should be preserved */ - - -/* - * Reset the card using configuration registers COR and CCSR. - * If IDLE is 1, stop the firmware, so that it can be safely rewritten. - */ -static int -spectrum_reset(struct pcmcia_device *link, int idle) -{ - int ret; - u8 save_cor; - u8 ccsr; - - /* Doing it if hardware is gone is guaranteed crash */ - if (!pcmcia_dev_present(link)) - return -ENODEV; - - /* Save original COR value */ - ret = pcmcia_read_config_byte(link, CISREG_COR, &save_cor); - if (ret) - goto failed; - - /* Soft-Reset card */ - ret = pcmcia_write_config_byte(link, CISREG_COR, - (save_cor | COR_SOFT_RESET)); - if (ret) - goto failed; - udelay(1000); - - /* Read CCSR */ - ret = pcmcia_read_config_byte(link, CISREG_CCSR, &ccsr); - if (ret) - goto failed; - - /* - * Start or stop the firmware. Memory width bit should be - * preserved from the value we've just read. - */ - ccsr = (idle ? HCR_IDLE : HCR_RUN) | (ccsr & HCR_MEM16); - ret = pcmcia_write_config_byte(link, CISREG_CCSR, ccsr); - if (ret) - goto failed; - udelay(1000); - - /* Restore original COR configuration index */ - ret = pcmcia_write_config_byte(link, CISREG_COR, - (save_cor & ~COR_SOFT_RESET)); - if (ret) - goto failed; - udelay(1000); - return 0; - -failed: - return -ENODEV; -} - -/********************************************************************/ -/* Device methods */ -/********************************************************************/ - -static int -spectrum_cs_hard_reset(struct orinoco_private *priv) -{ - struct orinoco_pccard *card = priv->card; - struct pcmcia_device *link = card->p_dev; - - /* Soft reset using COR and HCR */ - spectrum_reset(link, 0); - - return 0; -} - -static int -spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle) -{ - struct orinoco_pccard *card = priv->card; - struct pcmcia_device *link = card->p_dev; - - return spectrum_reset(link, idle); -} - -/********************************************************************/ -/* PCMCIA stuff */ -/********************************************************************/ - -static int -spectrum_cs_probe(struct pcmcia_device *link) -{ - struct orinoco_private *priv; - struct orinoco_pccard *card; - - priv = alloc_orinocodev(sizeof(*card), &link->dev, - spectrum_cs_hard_reset, - spectrum_cs_stop_firmware); - if (!priv) - return -ENOMEM; - card = priv->card; - - /* Link both structures together */ - card->p_dev = link; - link->priv = priv; - - return spectrum_cs_config(link); -} /* spectrum_cs_attach */ - -static void spectrum_cs_detach(struct pcmcia_device *link) -{ - struct orinoco_private *priv = link->priv; - - orinoco_if_del(priv); - - spectrum_cs_release(link); - - free_orinocodev(priv); -} /* spectrum_cs_detach */ - -static int spectrum_cs_config_check(struct pcmcia_device *p_dev, - void *priv_data) -{ - if (p_dev->config_index == 0) - return -EINVAL; - - return pcmcia_request_io(p_dev); -}; - -static int -spectrum_cs_config(struct pcmcia_device *link) -{ - struct orinoco_private *priv = link->priv; - struct hermes *hw = &priv->hw; - int ret; - void __iomem *mem; - - link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC | - CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; - if (ignore_cis_vcc) - link->config_flags &= ~CONF_AUTO_CHECK_VCC; - ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL); - if (ret) { - if (!ignore_cis_vcc) - printk(KERN_ERR PFX "GetNextTuple(): No matching " - "CIS configuration. Maybe you need the " - "ignore_cis_vcc=1 parameter.\n"); - goto failed; - } - - mem = ioport_map(link->resource[0]->start, - resource_size(link->resource[0])); - if (!mem) - goto failed; - - /* We initialize the hermes structure before completing PCMCIA - * configuration just in case the interrupt handler gets - * called. */ - hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING); - hw->eeprom_pda = true; - - ret = pcmcia_request_irq(link, orinoco_interrupt); - if (ret) - goto failed; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - /* Reset card */ - if (spectrum_cs_hard_reset(priv) != 0) - goto failed; - - /* Initialise the main driver */ - if (orinoco_init(priv) != 0) { - printk(KERN_ERR PFX "orinoco_init() failed\n"); - goto failed; - } - - /* Register an interface with the stack */ - if (orinoco_if_add(priv, link->resource[0]->start, - link->irq, NULL) != 0) { - printk(KERN_ERR PFX "orinoco_if_add() failed\n"); - goto failed; - } - - return 0; - - failed: - spectrum_cs_release(link); - return -ENODEV; -} /* spectrum_cs_config */ - -static void -spectrum_cs_release(struct pcmcia_device *link) -{ - struct orinoco_private *priv = link->priv; - unsigned long flags; - - /* We're committed to taking the device away now, so mark the - * hardware as unavailable */ - priv->hw.ops->lock_irqsave(&priv->lock, &flags); - priv->hw_unavailable++; - priv->hw.ops->unlock_irqrestore(&priv->lock, &flags); - - pcmcia_disable_device(link); - if (priv->hw.iobase) - ioport_unmap(priv->hw.iobase); -} /* spectrum_cs_release */ - - -static int -spectrum_cs_suspend(struct pcmcia_device *link) -{ - struct orinoco_private *priv = link->priv; - int err = 0; - - /* Mark the device as stopped, to block IO until later */ - orinoco_down(priv); - - return err; -} - -static int -spectrum_cs_resume(struct pcmcia_device *link) -{ - struct orinoco_private *priv = link->priv; - int err = orinoco_up(priv); - - return err; -} - - -/********************************************************************/ -/* Module initialization */ -/********************************************************************/ - -static const struct pcmcia_device_id spectrum_cs_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */ - PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */ - PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */ - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, spectrum_cs_ids); - -static struct pcmcia_driver orinoco_driver = { - .owner = THIS_MODULE, - .name = DRIVER_NAME, - .probe = spectrum_cs_probe, - .remove = spectrum_cs_detach, - .suspend = spectrum_cs_suspend, - .resume = spectrum_cs_resume, - .id_table = spectrum_cs_ids, -}; -module_pcmcia_driver(orinoco_driver); diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c deleted file mode 100644 index 1d4dae422106..000000000000 --- a/drivers/net/wireless/orinoco/wext.c +++ /dev/null @@ -1,1413 +0,0 @@ -/* Wireless extensions support. - * - * See copyright notice in main.c - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hermes.h" -#include "hermes_rid.h" -#include "orinoco.h" - -#include "hw.h" -#include "mic.h" -#include "scan.h" -#include "main.h" - -#include "wext.h" - -#define MAX_RID_LEN 1024 - -/* Helper routine to record keys - * It is called under orinoco_lock so it may not sleep */ -static int orinoco_set_key(struct orinoco_private *priv, int index, - enum orinoco_alg alg, const u8 *key, int key_len, - const u8 *seq, int seq_len) -{ - kzfree(priv->keys[index].key); - kzfree(priv->keys[index].seq); - - if (key_len) { - priv->keys[index].key = kzalloc(key_len, GFP_ATOMIC); - if (!priv->keys[index].key) - goto nomem; - } else - priv->keys[index].key = NULL; - - if (seq_len) { - priv->keys[index].seq = kzalloc(seq_len, GFP_ATOMIC); - if (!priv->keys[index].seq) - goto free_key; - } else - priv->keys[index].seq = NULL; - - priv->keys[index].key_len = key_len; - priv->keys[index].seq_len = seq_len; - - if (key_len) - memcpy((void *)priv->keys[index].key, key, key_len); - if (seq_len) - memcpy((void *)priv->keys[index].seq, seq, seq_len); - - switch (alg) { - case ORINOCO_ALG_TKIP: - priv->keys[index].cipher = WLAN_CIPHER_SUITE_TKIP; - break; - - case ORINOCO_ALG_WEP: - priv->keys[index].cipher = (key_len > SMALL_KEY_SIZE) ? - WLAN_CIPHER_SUITE_WEP104 : WLAN_CIPHER_SUITE_WEP40; - break; - - case ORINOCO_ALG_NONE: - default: - priv->keys[index].cipher = 0; - break; - } - - return 0; - -free_key: - kfree(priv->keys[index].key); - priv->keys[index].key = NULL; - -nomem: - priv->keys[index].key_len = 0; - priv->keys[index].seq_len = 0; - priv->keys[index].cipher = 0; - - return -ENOMEM; -} - -static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct hermes *hw = &priv->hw; - struct iw_statistics *wstats = &priv->wstats; - int err; - unsigned long flags; - - if (!netif_device_present(dev)) { - printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n", - dev->name); - return NULL; /* FIXME: Can we do better than this? */ - } - - /* If busy, return the old stats. Returning NULL may cause - * the interface to disappear from /proc/net/wireless */ - if (orinoco_lock(priv, &flags) != 0) - return wstats; - - /* We can't really wait for the tallies inquiry command to - * complete, so we just use the previous results and trigger - * a new tallies inquiry command for next time - Jean II */ - /* FIXME: Really we should wait for the inquiry to come back - - * as it is the stats we give don't make a whole lot of sense. - * Unfortunately, it's not clear how to do that within the - * wireless extensions framework: I think we're in user - * context, but a lock seems to be held by the time we get in - * here so we're not safe to sleep here. */ - hermes_inquire(hw, HERMES_INQ_TALLIES); - - if (priv->iw_mode == NL80211_IFTYPE_ADHOC) { - memset(&wstats->qual, 0, sizeof(wstats->qual)); - /* If a spy address is defined, we report stats of the - * first spy address - Jean II */ - if (SPY_NUMBER(priv)) { - wstats->qual.qual = priv->spy_data.spy_stat[0].qual; - wstats->qual.level = priv->spy_data.spy_stat[0].level; - wstats->qual.noise = priv->spy_data.spy_stat[0].noise; - wstats->qual.updated = - priv->spy_data.spy_stat[0].updated; - } - } else { - struct { - __le16 qual, signal, noise, unused; - } __packed cq; - - err = HERMES_READ_RECORD(hw, USER_BAP, - HERMES_RID_COMMSQUALITY, &cq); - - if (!err) { - wstats->qual.qual = (int)le16_to_cpu(cq.qual); - wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95; - wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95; - wstats->qual.updated = - IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; - } - } - - orinoco_unlock(priv, &flags); - return wstats; -} - -/********************************************************************/ -/* Wireless extensions */ -/********************************************************************/ - -static int orinoco_ioctl_setwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int err = -EINPROGRESS; /* Call commit handler */ - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - /* Enable automatic roaming - no sanity checks are needed */ - if (is_zero_ether_addr(ap_addr->sa_data) || - is_broadcast_ether_addr(ap_addr->sa_data)) { - priv->bssid_fixed = 0; - eth_zero_addr(priv->desired_bssid); - - /* "off" means keep existing connection */ - if (ap_addr->sa_data[0] == 0) { - __orinoco_hw_set_wap(priv); - err = 0; - } - goto out; - } - - if (priv->firmware_type == FIRMWARE_TYPE_AGERE) { - printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't " - "support manual roaming\n", - dev->name); - err = -EOPNOTSUPP; - goto out; - } - - if (priv->iw_mode != NL80211_IFTYPE_STATION) { - printk(KERN_WARNING "%s: Manual roaming supported only in " - "managed mode\n", dev->name); - err = -EOPNOTSUPP; - goto out; - } - - /* Intersil firmware hangs without Desired ESSID */ - if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL && - strlen(priv->desired_essid) == 0) { - printk(KERN_WARNING "%s: Desired ESSID must be set for " - "manual roaming\n", dev->name); - err = -EOPNOTSUPP; - goto out; - } - - /* Finally, enable manual roaming */ - priv->bssid_fixed = 1; - memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN); - - out: - orinoco_unlock(priv, &flags); - return err; -} - -static int orinoco_ioctl_getwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - - int err = 0; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - ap_addr->sa_family = ARPHRD_ETHER; - err = orinoco_hw_get_current_bssid(priv, ap_addr->sa_data); - - orinoco_unlock(priv, &flags); - - return err; -} - -static int orinoco_ioctl_setiwencode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, - char *keybuf) -{ - struct orinoco_private *priv = ndev_priv(dev); - int index = (erq->flags & IW_ENCODE_INDEX) - 1; - int setindex = priv->tx_key; - enum orinoco_alg encode_alg = priv->encode_alg; - int restricted = priv->wep_restrict; - int err = -EINPROGRESS; /* Call commit handler */ - unsigned long flags; - - if (!priv->has_wep) - return -EOPNOTSUPP; - - if (erq->pointer) { - /* We actually have a key to set - check its length */ - if (erq->length > LARGE_KEY_SIZE) - return -E2BIG; - - if ((erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep) - return -E2BIG; - } - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - /* Clear any TKIP key we have */ - if ((priv->has_wpa) && (priv->encode_alg == ORINOCO_ALG_TKIP)) - (void) orinoco_clear_tkip_key(priv, setindex); - - if (erq->length > 0) { - if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) - index = priv->tx_key; - - /* Switch on WEP if off */ - if (encode_alg != ORINOCO_ALG_WEP) { - setindex = index; - encode_alg = ORINOCO_ALG_WEP; - } - } else { - /* Important note : if the user do "iwconfig eth0 enc off", - * we will arrive there with an index of -1. This is valid - * but need to be taken care off... Jean II */ - if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) { - if ((index != -1) || (erq->flags == 0)) { - err = -EINVAL; - goto out; - } - } else { - /* Set the index : Check that the key is valid */ - if (priv->keys[index].key_len == 0) { - err = -EINVAL; - goto out; - } - setindex = index; - } - } - - if (erq->flags & IW_ENCODE_DISABLED) - encode_alg = ORINOCO_ALG_NONE; - if (erq->flags & IW_ENCODE_OPEN) - restricted = 0; - if (erq->flags & IW_ENCODE_RESTRICTED) - restricted = 1; - - if (erq->pointer && erq->length > 0) { - err = orinoco_set_key(priv, index, ORINOCO_ALG_WEP, keybuf, - erq->length, NULL, 0); - } - priv->tx_key = setindex; - - /* Try fast key change if connected and only keys are changed */ - if ((priv->encode_alg == encode_alg) && - (priv->wep_restrict == restricted) && - netif_carrier_ok(dev)) { - err = __orinoco_hw_setup_wepkeys(priv); - /* No need to commit if successful */ - goto out; - } - - priv->encode_alg = encode_alg; - priv->wep_restrict = restricted; - - out: - orinoco_unlock(priv, &flags); - - return err; -} - -static int orinoco_ioctl_getiwencode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, - char *keybuf) -{ - struct orinoco_private *priv = ndev_priv(dev); - int index = (erq->flags & IW_ENCODE_INDEX) - 1; - unsigned long flags; - - if (!priv->has_wep) - return -EOPNOTSUPP; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) - index = priv->tx_key; - - erq->flags = 0; - if (!priv->encode_alg) - erq->flags |= IW_ENCODE_DISABLED; - erq->flags |= index + 1; - - if (priv->wep_restrict) - erq->flags |= IW_ENCODE_RESTRICTED; - else - erq->flags |= IW_ENCODE_OPEN; - - erq->length = priv->keys[index].key_len; - - memcpy(keybuf, priv->keys[index].key, erq->length); - - orinoco_unlock(priv, &flags); - return 0; -} - -static int orinoco_ioctl_setessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, - char *essidbuf) -{ - struct orinoco_private *priv = ndev_priv(dev); - unsigned long flags; - - /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it - * anyway... - Jean II */ - - /* Hum... Should not use Wireless Extension constant (may change), - * should use our own... - Jean II */ - if (erq->length > IW_ESSID_MAX_SIZE) - return -E2BIG; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */ - memset(priv->desired_essid, 0, sizeof(priv->desired_essid)); - - /* If not ANY, get the new ESSID */ - if (erq->flags) - memcpy(priv->desired_essid, essidbuf, erq->length); - - orinoco_unlock(priv, &flags); - - return -EINPROGRESS; /* Call commit handler */ -} - -static int orinoco_ioctl_getessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, - char *essidbuf) -{ - struct orinoco_private *priv = ndev_priv(dev); - int active; - int err = 0; - unsigned long flags; - - if (netif_running(dev)) { - err = orinoco_hw_get_essid(priv, &active, essidbuf); - if (err < 0) - return err; - erq->length = err; - } else { - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE); - erq->length = strlen(priv->desired_essid); - orinoco_unlock(priv, &flags); - } - - erq->flags = 1; - - return 0; -} - -static int orinoco_ioctl_setfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *frq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int chan = -1; - unsigned long flags; - int err = -EINPROGRESS; /* Call commit handler */ - - /* In infrastructure mode the AP sets the channel */ - if (priv->iw_mode == NL80211_IFTYPE_STATION) - return -EBUSY; - - if ((frq->e == 0) && (frq->m <= 1000)) { - /* Setting by channel number */ - chan = frq->m; - } else { - /* Setting by frequency */ - int denom = 1; - int i; - - /* Calculate denominator to rescale to MHz */ - for (i = 0; i < (6 - frq->e); i++) - denom *= 10; - - chan = ieee80211_frequency_to_channel(frq->m / denom); - } - - if ((chan < 1) || (chan > NUM_CHANNELS) || - !(priv->channel_mask & (1 << (chan - 1)))) - return -EINVAL; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - priv->channel = chan; - if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { - /* Fast channel change - no commit if successful */ - struct hermes *hw = &priv->hw; - err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST | - HERMES_TEST_SET_CHANNEL, - chan, NULL); - } - orinoco_unlock(priv, &flags); - - return err; -} - -static int orinoco_ioctl_getfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *frq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int tmp; - - /* Locking done in there */ - tmp = orinoco_hw_get_freq(priv); - if (tmp < 0) - return tmp; - - frq->m = tmp * 100000; - frq->e = 1; - - return 0; -} - -static int orinoco_ioctl_getsens(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *srq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct hermes *hw = &priv->hw; - u16 val; - int err; - unsigned long flags; - - if (!priv->has_sensitivity) - return -EOPNOTSUPP; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFSYSTEMSCALE, &val); - orinoco_unlock(priv, &flags); - - if (err) - return err; - - srq->value = val; - srq->fixed = 0; /* auto */ - - return 0; -} - -static int orinoco_ioctl_setsens(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *srq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int val = srq->value; - unsigned long flags; - - if (!priv->has_sensitivity) - return -EOPNOTSUPP; - - if ((val < 1) || (val > 3)) - return -EINVAL; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - priv->ap_density = val; - orinoco_unlock(priv, &flags); - - return -EINPROGRESS; /* Call commit handler */ -} - -static int orinoco_ioctl_setrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int ratemode; - int bitrate; /* 100s of kilobits */ - unsigned long flags; - - /* As the user space doesn't know our highest rate, it uses -1 - * to ask us to set the highest rate. Test it using "iwconfig - * ethX rate auto" - Jean II */ - if (rrq->value == -1) - bitrate = 110; - else { - if (rrq->value % 100000) - return -EINVAL; - bitrate = rrq->value / 100000; - } - - ratemode = orinoco_get_bitratemode(bitrate, !rrq->fixed); - - if (ratemode == -1) - return -EINVAL; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - priv->bitratemode = ratemode; - orinoco_unlock(priv, &flags); - - return -EINPROGRESS; -} - -static int orinoco_ioctl_getrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rrq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int err = 0; - int bitrate, automatic; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - orinoco_get_ratemode_cfg(priv->bitratemode, &bitrate, &automatic); - - /* If the interface is running we try to find more about the - current mode */ - if (netif_running(dev)) { - int act_bitrate; - int lerr; - - /* Ignore errors if we can't get the actual bitrate */ - lerr = orinoco_hw_get_act_bitrate(priv, &act_bitrate); - if (!lerr) - bitrate = act_bitrate; - } - - orinoco_unlock(priv, &flags); - - rrq->value = bitrate; - rrq->fixed = !automatic; - rrq->disabled = 0; - - return err; -} - -static int orinoco_ioctl_setpower(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *prq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int err = -EINPROGRESS; /* Call commit handler */ - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - if (prq->disabled) { - priv->pm_on = 0; - } else { - switch (prq->flags & IW_POWER_MODE) { - case IW_POWER_UNICAST_R: - priv->pm_mcast = 0; - priv->pm_on = 1; - break; - case IW_POWER_ALL_R: - priv->pm_mcast = 1; - priv->pm_on = 1; - break; - case IW_POWER_ON: - /* No flags : but we may have a value - Jean II */ - break; - default: - err = -EINVAL; - goto out; - } - - if (prq->flags & IW_POWER_TIMEOUT) { - priv->pm_on = 1; - priv->pm_timeout = prq->value / 1000; - } - if (prq->flags & IW_POWER_PERIOD) { - priv->pm_on = 1; - priv->pm_period = prq->value / 1000; - } - /* It's valid to not have a value if we are just toggling - * the flags... Jean II */ - if (!priv->pm_on) { - err = -EINVAL; - goto out; - } - } - - out: - orinoco_unlock(priv, &flags); - - return err; -} - -static int orinoco_ioctl_getpower(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *prq, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct hermes *hw = &priv->hw; - int err = 0; - u16 enable, period, timeout, mcast; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFPMENABLED, &enable); - if (err) - goto out; - - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFMAXSLEEPDURATION, &period); - if (err) - goto out; - - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFPMHOLDOVERDURATION, &timeout); - if (err) - goto out; - - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFMULTICASTRECEIVE, &mcast); - if (err) - goto out; - - prq->disabled = !enable; - /* Note : by default, display the period */ - if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { - prq->flags = IW_POWER_TIMEOUT; - prq->value = timeout * 1000; - } else { - prq->flags = IW_POWER_PERIOD; - prq->value = period * 1000; - } - if (mcast) - prq->flags |= IW_POWER_ALL_R; - else - prq->flags |= IW_POWER_UNICAST_R; - - out: - orinoco_unlock(priv, &flags); - - return err; -} - -static int orinoco_ioctl_set_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int idx, alg = ext->alg, set_key = 1; - unsigned long flags; - int err = -EINVAL; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - /* Determine and validate the key index */ - idx = encoding->flags & IW_ENCODE_INDEX; - if (idx) { - if ((idx < 1) || (idx > 4)) - goto out; - idx--; - } else - idx = priv->tx_key; - - if (encoding->flags & IW_ENCODE_DISABLED) - alg = IW_ENCODE_ALG_NONE; - - if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) { - /* Clear any TKIP TX key we had */ - (void) orinoco_clear_tkip_key(priv, priv->tx_key); - } - - if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { - priv->tx_key = idx; - set_key = ((alg == IW_ENCODE_ALG_TKIP) || - (ext->key_len > 0)) ? 1 : 0; - } - - if (set_key) { - /* Set the requested key first */ - switch (alg) { - case IW_ENCODE_ALG_NONE: - priv->encode_alg = ORINOCO_ALG_NONE; - err = orinoco_set_key(priv, idx, ORINOCO_ALG_NONE, - NULL, 0, NULL, 0); - break; - - case IW_ENCODE_ALG_WEP: - if (ext->key_len <= 0) - goto out; - - priv->encode_alg = ORINOCO_ALG_WEP; - err = orinoco_set_key(priv, idx, ORINOCO_ALG_WEP, - ext->key, ext->key_len, NULL, 0); - break; - - case IW_ENCODE_ALG_TKIP: - { - u8 *tkip_iv = NULL; - - if (!priv->has_wpa || - (ext->key_len > sizeof(struct orinoco_tkip_key))) - goto out; - - priv->encode_alg = ORINOCO_ALG_TKIP; - - if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) - tkip_iv = &ext->rx_seq[0]; - - err = orinoco_set_key(priv, idx, ORINOCO_ALG_TKIP, - ext->key, ext->key_len, tkip_iv, - ORINOCO_SEQ_LEN); - - err = __orinoco_hw_set_tkip_key(priv, idx, - ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, - priv->keys[idx].key, - tkip_iv, ORINOCO_SEQ_LEN, NULL, 0); - if (err) - printk(KERN_ERR "%s: Error %d setting TKIP key" - "\n", dev->name, err); - - goto out; - } - default: - goto out; - } - } - err = -EINPROGRESS; - out: - orinoco_unlock(priv, &flags); - - return err; -} - -static int orinoco_ioctl_get_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int idx, max_key_len; - unsigned long flags; - int err; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - err = -EINVAL; - max_key_len = encoding->length - sizeof(*ext); - if (max_key_len < 0) - goto out; - - idx = encoding->flags & IW_ENCODE_INDEX; - if (idx) { - if ((idx < 1) || (idx > 4)) - goto out; - idx--; - } else - idx = priv->tx_key; - - encoding->flags = idx + 1; - memset(ext, 0, sizeof(*ext)); - - switch (priv->encode_alg) { - case ORINOCO_ALG_NONE: - ext->alg = IW_ENCODE_ALG_NONE; - ext->key_len = 0; - encoding->flags |= IW_ENCODE_DISABLED; - break; - case ORINOCO_ALG_WEP: - ext->alg = IW_ENCODE_ALG_WEP; - ext->key_len = min(priv->keys[idx].key_len, max_key_len); - memcpy(ext->key, priv->keys[idx].key, ext->key_len); - encoding->flags |= IW_ENCODE_ENABLED; - break; - case ORINOCO_ALG_TKIP: - ext->alg = IW_ENCODE_ALG_TKIP; - ext->key_len = min(priv->keys[idx].key_len, max_key_len); - memcpy(ext->key, priv->keys[idx].key, ext->key_len); - encoding->flags |= IW_ENCODE_ENABLED; - break; - } - - err = 0; - out: - orinoco_unlock(priv, &flags); - - return err; -} - -static int orinoco_ioctl_set_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct hermes *hw = &priv->hw; - struct iw_param *param = &wrqu->param; - unsigned long flags; - int ret = -EINPROGRESS; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_PAIRWISE: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - case IW_AUTH_PRIVACY_INVOKED: - case IW_AUTH_DROP_UNENCRYPTED: - /* - * orinoco does not use these parameters - */ - break; - - case IW_AUTH_MFP: - /* Management Frame Protection not supported. - * Only fail if set to required. - */ - if (param->value == IW_AUTH_MFP_REQUIRED) - ret = -EINVAL; - break; - - case IW_AUTH_KEY_MGMT: - /* wl_lkm implies value 2 == PSK for Hermes I - * which ties in with WEXT - * no other hints tho :( - */ - priv->key_mgmt = param->value; - break; - - case IW_AUTH_TKIP_COUNTERMEASURES: - /* When countermeasures are enabled, shut down the - * card; when disabled, re-enable the card. This must - * take effect immediately. - * - * TODO: Make sure that the EAPOL message is getting - * out before card disabled - */ - if (param->value) { - priv->tkip_cm_active = 1; - ret = hermes_disable_port(hw, 0); - } else { - priv->tkip_cm_active = 0; - ret = hermes_enable_port(hw, 0); - } - break; - - case IW_AUTH_80211_AUTH_ALG: - if (param->value & IW_AUTH_ALG_SHARED_KEY) - priv->wep_restrict = 1; - else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) - priv->wep_restrict = 0; - else - ret = -EINVAL; - break; - - case IW_AUTH_WPA_ENABLED: - if (priv->has_wpa) { - priv->wpa_enabled = param->value ? 1 : 0; - } else { - if (param->value) - ret = -EOPNOTSUPP; - /* else silently accept disable of WPA */ - priv->wpa_enabled = 0; - } - break; - - default: - ret = -EOPNOTSUPP; - } - - orinoco_unlock(priv, &flags); - return ret; -} - -static int orinoco_ioctl_get_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct iw_param *param = &wrqu->param; - unsigned long flags; - int ret = 0; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_KEY_MGMT: - param->value = priv->key_mgmt; - break; - - case IW_AUTH_TKIP_COUNTERMEASURES: - param->value = priv->tkip_cm_active; - break; - - case IW_AUTH_80211_AUTH_ALG: - if (priv->wep_restrict) - param->value = IW_AUTH_ALG_SHARED_KEY; - else - param->value = IW_AUTH_ALG_OPEN_SYSTEM; - break; - - case IW_AUTH_WPA_ENABLED: - param->value = priv->wpa_enabled; - break; - - default: - ret = -EOPNOTSUPP; - } - - orinoco_unlock(priv, &flags); - return ret; -} - -static int orinoco_ioctl_set_genie(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - u8 *buf; - unsigned long flags; - - /* cut off at IEEE80211_MAX_DATA_LEN */ - if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) || - (wrqu->data.length && (extra == NULL))) - return -EINVAL; - - if (wrqu->data.length) { - buf = kmemdup(extra, wrqu->data.length, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - } else - buf = NULL; - - if (orinoco_lock(priv, &flags) != 0) { - kfree(buf); - return -EBUSY; - } - - kfree(priv->wpa_ie); - priv->wpa_ie = buf; - priv->wpa_ie_len = wrqu->data.length; - - if (priv->wpa_ie) { - /* Looks like wl_lkm wants to check the auth alg, and - * somehow pass it to the firmware. - * Instead it just calls the key mgmt rid - * - we do this in set auth. - */ - } - - orinoco_unlock(priv, &flags); - return 0; -} - -static int orinoco_ioctl_get_genie(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - unsigned long flags; - int err = 0; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) { - wrqu->data.length = 0; - goto out; - } - - if (wrqu->data.length < priv->wpa_ie_len) { - err = -E2BIG; - goto out; - } - - wrqu->data.length = priv->wpa_ie_len; - memcpy(extra, priv->wpa_ie, priv->wpa_ie_len); - -out: - orinoco_unlock(priv, &flags); - return err; -} - -static int orinoco_ioctl_set_mlme(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct iw_mlme *mlme = (struct iw_mlme *)extra; - unsigned long flags; - int ret = 0; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - switch (mlme->cmd) { - case IW_MLME_DEAUTH: - /* silently ignore */ - break; - - case IW_MLME_DISASSOC: - - ret = orinoco_hw_disassociate(priv, mlme->addr.sa_data, - mlme->reason_code); - break; - - default: - ret = -EOPNOTSUPP; - } - - orinoco_unlock(priv, &flags); - return ret; -} - -static int orinoco_ioctl_reset(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) { - printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name); - - /* Firmware reset */ - orinoco_reset(&priv->reset_work); - } else { - printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); - - schedule_work(&priv->reset_work); - } - - return 0; -} - -static int orinoco_ioctl_setibssport(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, - char *extra) - -{ - struct orinoco_private *priv = ndev_priv(dev); - int val = *((int *) extra); - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - priv->ibss_port = val; - - /* Actually update the mode we are using */ - set_port_type(priv); - - orinoco_unlock(priv, &flags); - return -EINPROGRESS; /* Call commit handler */ -} - -static int orinoco_ioctl_getibssport(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int *val = (int *) extra; - - *val = priv->ibss_port; - return 0; -} - -static int orinoco_ioctl_setport3(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int val = *((int *) extra); - int err = 0; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - switch (val) { - case 0: /* Try to do IEEE ad-hoc mode */ - if (!priv->has_ibss) { - err = -EINVAL; - break; - } - priv->prefer_port3 = 0; - - break; - - case 1: /* Try to do Lucent proprietary ad-hoc mode */ - if (!priv->has_port3) { - err = -EINVAL; - break; - } - priv->prefer_port3 = 1; - break; - - default: - err = -EINVAL; - } - - if (!err) { - /* Actually update the mode we are using */ - set_port_type(priv); - err = -EINPROGRESS; - } - - orinoco_unlock(priv, &flags); - - return err; -} - -static int orinoco_ioctl_getport3(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int *val = (int *) extra; - - *val = priv->prefer_port3; - return 0; -} - -static int orinoco_ioctl_setpreamble(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - unsigned long flags; - int val; - - if (!priv->has_preamble) - return -EOPNOTSUPP; - - /* 802.11b has recently defined some short preamble. - * Basically, the Phy header has been reduced in size. - * This increase performance, especially at high rates - * (the preamble is transmitted at 1Mb/s), unfortunately - * this give compatibility troubles... - Jean II */ - val = *((int *) extra); - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - if (val) - priv->preamble = 1; - else - priv->preamble = 0; - - orinoco_unlock(priv, &flags); - - return -EINPROGRESS; /* Call commit handler */ -} - -static int orinoco_ioctl_getpreamble(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - int *val = (int *) extra; - - if (!priv->has_preamble) - return -EOPNOTSUPP; - - *val = priv->preamble; - return 0; -} - -/* ioctl interface to hermes_read_ltv() - * To use with iwpriv, pass the RID as the token argument, e.g. - * iwpriv get_rid [0xfc00] - * At least Wireless Tools 25 is required to use iwpriv. - * For Wireless Tools 25 and 26 append "dummy" are the end. */ -static int orinoco_ioctl_getrid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - struct hermes *hw = &priv->hw; - int rid = data->flags; - u16 length; - int err; - unsigned long flags; - - /* It's a "get" function, but we don't want users to access the - * WEP key and other raw firmware data */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (rid < 0xfc00 || rid > 0xffff) - return -EINVAL; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - err = hw->ops->read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length, - extra); - if (err) - goto out; - - data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length), - MAX_RID_LEN); - - out: - orinoco_unlock(priv, &flags); - return err; -} - - -/* Commit handler, called after set operations */ -static int orinoco_ioctl_commit(struct net_device *dev, - struct iw_request_info *info, - void *wrqu, - char *extra) -{ - struct orinoco_private *priv = ndev_priv(dev); - unsigned long flags; - int err = 0; - - if (!priv->open) - return 0; - - if (orinoco_lock(priv, &flags) != 0) - return err; - - err = orinoco_commit(priv); - - orinoco_unlock(priv, &flags); - return err; -} - -static const struct iw_priv_args orinoco_privtab[] = { - { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, - { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, - { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - 0, "set_port3" }, - { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_port3" }, - { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - 0, "set_preamble" }, - { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_preamble" }, - { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - 0, "set_ibssport" }, - { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_ibssport" }, - { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN, - "get_rid" }, -}; - - -/* - * Structures to export the Wireless Handlers - */ - -static const iw_handler orinoco_handler[] = { - IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)orinoco_ioctl_commit), - IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), - IW_HANDLER(SIOCSIWFREQ, (iw_handler)orinoco_ioctl_setfreq), - IW_HANDLER(SIOCGIWFREQ, (iw_handler)orinoco_ioctl_getfreq), - IW_HANDLER(SIOCSIWMODE, (iw_handler)cfg80211_wext_siwmode), - IW_HANDLER(SIOCGIWMODE, (iw_handler)cfg80211_wext_giwmode), - IW_HANDLER(SIOCSIWSENS, (iw_handler)orinoco_ioctl_setsens), - IW_HANDLER(SIOCGIWSENS, (iw_handler)orinoco_ioctl_getsens), - IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange), - IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), - IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), - IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), - IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), - IW_HANDLER(SIOCSIWAP, (iw_handler)orinoco_ioctl_setwap), - IW_HANDLER(SIOCGIWAP, (iw_handler)orinoco_ioctl_getwap), - IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan), - IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan), - IW_HANDLER(SIOCSIWESSID, (iw_handler)orinoco_ioctl_setessid), - IW_HANDLER(SIOCGIWESSID, (iw_handler)orinoco_ioctl_getessid), - IW_HANDLER(SIOCSIWRATE, (iw_handler)orinoco_ioctl_setrate), - IW_HANDLER(SIOCGIWRATE, (iw_handler)orinoco_ioctl_getrate), - IW_HANDLER(SIOCSIWRTS, (iw_handler)cfg80211_wext_siwrts), - IW_HANDLER(SIOCGIWRTS, (iw_handler)cfg80211_wext_giwrts), - IW_HANDLER(SIOCSIWFRAG, (iw_handler)cfg80211_wext_siwfrag), - IW_HANDLER(SIOCGIWFRAG, (iw_handler)cfg80211_wext_giwfrag), - IW_HANDLER(SIOCGIWRETRY, (iw_handler)cfg80211_wext_giwretry), - IW_HANDLER(SIOCSIWENCODE, (iw_handler)orinoco_ioctl_setiwencode), - IW_HANDLER(SIOCGIWENCODE, (iw_handler)orinoco_ioctl_getiwencode), - IW_HANDLER(SIOCSIWPOWER, (iw_handler)orinoco_ioctl_setpower), - IW_HANDLER(SIOCGIWPOWER, (iw_handler)orinoco_ioctl_getpower), - IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), - IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), - IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), - IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), - IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), - IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), - IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), -}; - - -/* - Added typecasting since we no longer use iwreq_data -- Moustafa - */ -static const iw_handler orinoco_private_handler[] = { - [0] = (iw_handler)orinoco_ioctl_reset, - [1] = (iw_handler)orinoco_ioctl_reset, - [2] = (iw_handler)orinoco_ioctl_setport3, - [3] = (iw_handler)orinoco_ioctl_getport3, - [4] = (iw_handler)orinoco_ioctl_setpreamble, - [5] = (iw_handler)orinoco_ioctl_getpreamble, - [6] = (iw_handler)orinoco_ioctl_setibssport, - [7] = (iw_handler)orinoco_ioctl_getibssport, - [9] = (iw_handler)orinoco_ioctl_getrid, -}; - -const struct iw_handler_def orinoco_handler_def = { - .num_standard = ARRAY_SIZE(orinoco_handler), - .num_private = ARRAY_SIZE(orinoco_private_handler), - .num_private_args = ARRAY_SIZE(orinoco_privtab), - .standard = orinoco_handler, - .private = orinoco_private_handler, - .private_args = orinoco_privtab, - .get_wireless_stats = orinoco_get_wireless_stats, -}; diff --git a/drivers/net/wireless/orinoco/wext.h b/drivers/net/wireless/orinoco/wext.h deleted file mode 100644 index 1479f4e26dde..000000000000 --- a/drivers/net/wireless/orinoco/wext.h +++ /dev/null @@ -1,13 +0,0 @@ -/* Wireless extensions support. - * - * See copyright notice in main.c - */ -#ifndef _ORINOCO_WEXT_H_ -#define _ORINOCO_WEXT_H_ - -#include - -/* Structure defining all our WEXT handlers */ -extern const struct iw_handler_def orinoco_handler_def; - -#endif /* _ORINOCO_WEXT_H_ */ -- cgit v1.2.3-59-g8ed1b From c12edfe27fcbdec7a654edfa1726d5555b558518 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 09:56:45 +0200 Subject: prism54: move under intersil vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 20 - drivers/net/wireless/Makefile | 2 - drivers/net/wireless/intersil/Kconfig | 20 + drivers/net/wireless/intersil/Makefile | 1 + drivers/net/wireless/intersil/prism54/Makefile | 8 + drivers/net/wireless/intersil/prism54/isl_38xx.c | 255 ++ drivers/net/wireless/intersil/prism54/isl_38xx.h | 170 ++ drivers/net/wireless/intersil/prism54/isl_ioctl.c | 2910 ++++++++++++++++++++ drivers/net/wireless/intersil/prism54/isl_ioctl.h | 47 + drivers/net/wireless/intersil/prism54/isl_oid.h | 504 ++++ drivers/net/wireless/intersil/prism54/islpci_dev.c | 963 +++++++ drivers/net/wireless/intersil/prism54/islpci_dev.h | 216 ++ drivers/net/wireless/intersil/prism54/islpci_eth.c | 506 ++++ drivers/net/wireless/intersil/prism54/islpci_eth.h | 71 + .../net/wireless/intersil/prism54/islpci_hotplug.c | 339 +++ drivers/net/wireless/intersil/prism54/islpci_mgt.c | 502 ++++ drivers/net/wireless/intersil/prism54/islpci_mgt.h | 138 + drivers/net/wireless/intersil/prism54/oid_mgt.c | 901 ++++++ drivers/net/wireless/intersil/prism54/oid_mgt.h | 58 + .../net/wireless/intersil/prism54/prismcompat.h | 42 + drivers/net/wireless/prism54/Makefile | 8 - drivers/net/wireless/prism54/isl_38xx.c | 255 -- drivers/net/wireless/prism54/isl_38xx.h | 170 -- drivers/net/wireless/prism54/isl_ioctl.c | 2910 -------------------- drivers/net/wireless/prism54/isl_ioctl.h | 47 - drivers/net/wireless/prism54/isl_oid.h | 504 ---- drivers/net/wireless/prism54/islpci_dev.c | 963 ------- drivers/net/wireless/prism54/islpci_dev.h | 216 -- drivers/net/wireless/prism54/islpci_eth.c | 506 ---- drivers/net/wireless/prism54/islpci_eth.h | 71 - drivers/net/wireless/prism54/islpci_hotplug.c | 339 --- drivers/net/wireless/prism54/islpci_mgt.c | 502 ---- drivers/net/wireless/prism54/islpci_mgt.h | 138 - drivers/net/wireless/prism54/oid_mgt.c | 901 ------ drivers/net/wireless/prism54/oid_mgt.h | 58 - drivers/net/wireless/prism54/prismcompat.h | 42 - 37 files changed, 7652 insertions(+), 7653 deletions(-) create mode 100644 drivers/net/wireless/intersil/prism54/Makefile create mode 100644 drivers/net/wireless/intersil/prism54/isl_38xx.c create mode 100644 drivers/net/wireless/intersil/prism54/isl_38xx.h create mode 100644 drivers/net/wireless/intersil/prism54/isl_ioctl.c create mode 100644 drivers/net/wireless/intersil/prism54/isl_ioctl.h create mode 100644 drivers/net/wireless/intersil/prism54/isl_oid.h create mode 100644 drivers/net/wireless/intersil/prism54/islpci_dev.c create mode 100644 drivers/net/wireless/intersil/prism54/islpci_dev.h create mode 100644 drivers/net/wireless/intersil/prism54/islpci_eth.c create mode 100644 drivers/net/wireless/intersil/prism54/islpci_eth.h create mode 100644 drivers/net/wireless/intersil/prism54/islpci_hotplug.c create mode 100644 drivers/net/wireless/intersil/prism54/islpci_mgt.c create mode 100644 drivers/net/wireless/intersil/prism54/islpci_mgt.h create mode 100644 drivers/net/wireless/intersil/prism54/oid_mgt.c create mode 100644 drivers/net/wireless/intersil/prism54/oid_mgt.h create mode 100644 drivers/net/wireless/intersil/prism54/prismcompat.h delete mode 100644 drivers/net/wireless/prism54/Makefile delete mode 100644 drivers/net/wireless/prism54/isl_38xx.c delete mode 100644 drivers/net/wireless/prism54/isl_38xx.h delete mode 100644 drivers/net/wireless/prism54/isl_ioctl.c delete mode 100644 drivers/net/wireless/prism54/isl_ioctl.h delete mode 100644 drivers/net/wireless/prism54/isl_oid.h delete mode 100644 drivers/net/wireless/prism54/islpci_dev.c delete mode 100644 drivers/net/wireless/prism54/islpci_dev.h delete mode 100644 drivers/net/wireless/prism54/islpci_eth.c delete mode 100644 drivers/net/wireless/prism54/islpci_eth.h delete mode 100644 drivers/net/wireless/prism54/islpci_hotplug.c delete mode 100644 drivers/net/wireless/prism54/islpci_mgt.c delete mode 100644 drivers/net/wireless/prism54/islpci_mgt.h delete mode 100644 drivers/net/wireless/prism54/oid_mgt.c delete mode 100644 drivers/net/wireless/prism54/oid_mgt.h delete mode 100644 drivers/net/wireless/prism54/prismcompat.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 53c15af0d967..2c41981e4fdd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8498,7 +8498,7 @@ M: "Luis R. Rodriguez" L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/en/users/Drivers/p54 S: Obsolete -F: drivers/net/wireless/prism54/ +F: drivers/net/wireless/intersil/prism54/ PS3 NETWORK SUPPORT M: Geoff Levand diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index cc1f168603dc..c92671f8b1c6 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -52,26 +52,6 @@ config PCMCIA_WL3501 It has basic support for Linux wireless extensions and initial micro support for ethtool. -config PRISM54 - tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus (DEPRECATED)' - depends on PCI - select WIRELESS_EXT - select WEXT_SPY - select WEXT_PRIV - select FW_LOADER - ---help--- - This enables support for FullMAC PCI/Cardbus prism54 devices. This - driver is now deprecated in favor for the SoftMAC driver, p54pci. - p54pci supports FullMAC PCI/Cardbus devices as well. - - For more information refer to the p54 wiki: - - http://wireless.kernel.org/en/users/Drivers/p54 - - Note: You need a motherboard with DMA support to use any of these cards - - When built as module you get the module prism54 - config USB_NET_RNDIS_WLAN tristate "Wireless RNDIS USB support" depends on USB diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index dd6d17cc4b7e..679a1a0a048d 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -12,8 +12,6 @@ obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ -obj-$(CONFIG_PRISM54) += prism54/ - obj-$(CONFIG_WLAN) += realtek/ # 16-bit wireless PCMCIA client drivers diff --git a/drivers/net/wireless/intersil/Kconfig b/drivers/net/wireless/intersil/Kconfig index 2b056b6daef8..9da136049955 100644 --- a/drivers/net/wireless/intersil/Kconfig +++ b/drivers/net/wireless/intersil/Kconfig @@ -15,4 +15,24 @@ source "drivers/net/wireless/intersil/hostap/Kconfig" source "drivers/net/wireless/intersil/orinoco/Kconfig" source "drivers/net/wireless/intersil/p54/Kconfig" +config PRISM54 + tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus (DEPRECATED)' + depends on PCI + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + select FW_LOADER + ---help--- + This enables support for FullMAC PCI/Cardbus prism54 devices. This + driver is now deprecated in favor for the SoftMAC driver, p54pci. + p54pci supports FullMAC PCI/Cardbus devices as well. + + For more information refer to the p54 wiki: + + http://wireless.kernel.org/en/users/Drivers/p54 + + Note: You need a motherboard with DMA support to use any of these cards + + When built as module you get the module prism54 + endif # WLAN_VENDOR_INTERSIL diff --git a/drivers/net/wireless/intersil/Makefile b/drivers/net/wireless/intersil/Makefile index aedb713da746..9a8cbfee3ea5 100644 --- a/drivers/net/wireless/intersil/Makefile +++ b/drivers/net/wireless/intersil/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_HOSTAP) += hostap/ obj-$(CONFIG_HERMES) += orinoco/ obj-$(CONFIG_P54_COMMON) += p54/ +obj-$(CONFIG_PRISM54) += prism54/ diff --git a/drivers/net/wireless/intersil/prism54/Makefile b/drivers/net/wireless/intersil/prism54/Makefile new file mode 100644 index 000000000000..fad305c76737 --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/Makefile @@ -0,0 +1,8 @@ +# $Id: Makefile.k26,v 1.7 2004/01/30 16:24:00 ajfa Exp $ + +prism54-objs := islpci_eth.o islpci_mgt.o \ + isl_38xx.o isl_ioctl.o islpci_dev.o \ + islpci_hotplug.o oid_mgt.o + +obj-$(CONFIG_PRISM54) += prism54.o + diff --git a/drivers/net/wireless/intersil/prism54/isl_38xx.c b/drivers/net/wireless/intersil/prism54/isl_38xx.c new file mode 100644 index 000000000000..333c1a2f882e --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/isl_38xx.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2002 Intersil Americas Inc. + * Copyright (C) 2003-2004 Luis R. Rodriguez _ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include +#include +#include + +#include +#include + +#include "prismcompat.h" +#include "isl_38xx.h" +#include "islpci_dev.h" +#include "islpci_mgt.h" + +/****************************************************************************** + Device Interface & Control functions +******************************************************************************/ + +/** + * isl38xx_disable_interrupts - disable all interrupts + * @device: pci memory base address + * + * Instructs the device to disable all interrupt reporting by asserting + * the IRQ line. New events may still show up in the interrupt identification + * register located at offset %ISL38XX_INT_IDENT_REG. + */ +void +isl38xx_disable_interrupts(void __iomem *device) +{ + isl38xx_w32_flush(device, 0x00000000, ISL38XX_INT_EN_REG); + udelay(ISL38XX_WRITEIO_DELAY); +} + +void +isl38xx_handle_sleep_request(isl38xx_control_block *control_block, + int *powerstate, void __iomem *device_base) +{ + /* device requests to go into sleep mode + * check whether the transmit queues for data and management are empty */ + if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ)) + /* data tx queue not empty */ + return; + + if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ)) + /* management tx queue not empty */ + return; + + /* check also whether received frames are pending */ + if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_DATA_LQ)) + /* data rx queue not empty */ + return; + + if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_MGMTQ)) + /* management rx queue not empty */ + return; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "Device going to sleep mode\n"); +#endif + + /* all queues are empty, allow the device to go into sleep mode */ + *powerstate = ISL38XX_PSM_POWERSAVE_STATE; + + /* assert the Sleep interrupt in the Device Interrupt Register */ + isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_SLEEP, + ISL38XX_DEV_INT_REG); + udelay(ISL38XX_WRITEIO_DELAY); +} + +void +isl38xx_handle_wakeup(isl38xx_control_block *control_block, + int *powerstate, void __iomem *device_base) +{ + /* device is in active state, update the powerstate flag */ + *powerstate = ISL38XX_PSM_ACTIVE_STATE; + + /* now check whether there are frames pending for the card */ + if (!isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ) + && !isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ)) + return; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_ANYTHING, "Wake up handler trigger the device\n"); +#endif + + /* either data or management transmit queue has a frame pending + * trigger the device by setting the Update bit in the Device Int reg */ + isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE, + ISL38XX_DEV_INT_REG); + udelay(ISL38XX_WRITEIO_DELAY); +} + +void +isl38xx_trigger_device(int asleep, void __iomem *device_base) +{ + u32 reg; + +#if VERBOSE > SHOW_ERROR_MESSAGES + u32 counter = 0; + struct timeval current_time; + DEBUG(SHOW_FUNCTION_CALLS, "isl38xx trigger device\n"); +#endif + + /* check whether the device is in power save mode */ + if (asleep) { + /* device is in powersave, trigger the device for wakeup */ +#if VERBOSE > SHOW_ERROR_MESSAGES + do_gettimeofday(¤t_time); + DEBUG(SHOW_TRACING, "%08li.%08li Device wakeup triggered\n", + current_time.tv_sec, (long)current_time.tv_usec); + + DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n", + current_time.tv_sec, (long)current_time.tv_usec, + readl(device_base + ISL38XX_CTRL_STAT_REG)); +#endif + + reg = readl(device_base + ISL38XX_INT_IDENT_REG); + if (reg == 0xabadface) { +#if VERBOSE > SHOW_ERROR_MESSAGES + do_gettimeofday(¤t_time); + DEBUG(SHOW_TRACING, + "%08li.%08li Device register abadface\n", + current_time.tv_sec, (long)current_time.tv_usec); +#endif + /* read the Device Status Register until Sleepmode bit is set */ + while (reg = readl(device_base + ISL38XX_CTRL_STAT_REG), + (reg & ISL38XX_CTRL_STAT_SLEEPMODE) == 0) { + udelay(ISL38XX_WRITEIO_DELAY); +#if VERBOSE > SHOW_ERROR_MESSAGES + counter++; +#endif + } + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, + "%08li.%08li Device register read %08x\n", + current_time.tv_sec, (long)current_time.tv_usec, + readl(device_base + ISL38XX_CTRL_STAT_REG)); + do_gettimeofday(¤t_time); + DEBUG(SHOW_TRACING, + "%08li.%08li Device asleep counter %i\n", + current_time.tv_sec, (long)current_time.tv_usec, + counter); +#endif + } + /* assert the Wakeup interrupt in the Device Interrupt Register */ + isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_WAKEUP, + ISL38XX_DEV_INT_REG); + +#if VERBOSE > SHOW_ERROR_MESSAGES + udelay(ISL38XX_WRITEIO_DELAY); + + /* perform another read on the Device Status Register */ + reg = readl(device_base + ISL38XX_CTRL_STAT_REG); + do_gettimeofday(¤t_time); + DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n", + current_time.tv_sec, (long)current_time.tv_usec, reg); +#endif + } else { + /* device is (still) awake */ +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "Device is in active state\n"); +#endif + /* trigger the device by setting the Update bit in the Device Int reg */ + + isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE, + ISL38XX_DEV_INT_REG); + } +} + +void +isl38xx_interface_reset(void __iomem *device_base, dma_addr_t host_address) +{ +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_FUNCTION_CALLS, "isl38xx_interface_reset\n"); +#endif + + /* load the address of the control block in the device */ + isl38xx_w32_flush(device_base, host_address, ISL38XX_CTRL_BLK_BASE_REG); + udelay(ISL38XX_WRITEIO_DELAY); + + /* set the reset bit in the Device Interrupt Register */ + isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_RESET, ISL38XX_DEV_INT_REG); + udelay(ISL38XX_WRITEIO_DELAY); + + /* enable the interrupt for detecting initialization */ + + /* Note: Do not enable other interrupts here. We want the + * device to have come up first 100% before allowing any other + * interrupts. */ + isl38xx_w32_flush(device_base, ISL38XX_INT_IDENT_INIT, ISL38XX_INT_EN_REG); + udelay(ISL38XX_WRITEIO_DELAY); /* allow complete full reset */ +} + +void +isl38xx_enable_common_interrupts(void __iomem *device_base) +{ + u32 reg; + + reg = ISL38XX_INT_IDENT_UPDATE | ISL38XX_INT_IDENT_SLEEP | + ISL38XX_INT_IDENT_WAKEUP; + isl38xx_w32_flush(device_base, reg, ISL38XX_INT_EN_REG); + udelay(ISL38XX_WRITEIO_DELAY); +} + +int +isl38xx_in_queue(isl38xx_control_block *cb, int queue) +{ + const s32 delta = (le32_to_cpu(cb->driver_curr_frag[queue]) - + le32_to_cpu(cb->device_curr_frag[queue])); + + /* determine the amount of fragments in the queue depending on the type + * of the queue, either transmit or receive */ + + BUG_ON(delta < 0); /* driver ptr must be ahead of device ptr */ + + switch (queue) { + /* send queues */ + case ISL38XX_CB_TX_MGMTQ: + BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE); + + case ISL38XX_CB_TX_DATA_LQ: + case ISL38XX_CB_TX_DATA_HQ: + BUG_ON(delta > ISL38XX_CB_TX_QSIZE); + return delta; + + /* receive queues */ + case ISL38XX_CB_RX_MGMTQ: + BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE); + return ISL38XX_CB_MGMT_QSIZE - delta; + + case ISL38XX_CB_RX_DATA_LQ: + case ISL38XX_CB_RX_DATA_HQ: + BUG_ON(delta > ISL38XX_CB_RX_QSIZE); + return ISL38XX_CB_RX_QSIZE - delta; + } + BUG(); + return 0; +} diff --git a/drivers/net/wireless/intersil/prism54/isl_38xx.h b/drivers/net/wireless/intersil/prism54/isl_38xx.h new file mode 100644 index 000000000000..547ab885610b --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/isl_38xx.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2002 Intersil Americas Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#ifndef _ISL_38XX_H +#define _ISL_38XX_H + +#include +#include + +#define ISL38XX_CB_RX_QSIZE 8 +#define ISL38XX_CB_TX_QSIZE 32 + +/* ISL38XX Access Point Specific definitions */ +#define ISL38XX_MAX_WDS_LINKS 8 + +/* ISL38xx Client Specific definitions */ +#define ISL38XX_PSM_ACTIVE_STATE 0 +#define ISL38XX_PSM_POWERSAVE_STATE 1 + +/* ISL38XX Host Interface Definitions */ +#define ISL38XX_PCI_MEM_SIZE 0x02000 +#define ISL38XX_MEMORY_WINDOW_SIZE 0x01000 +#define ISL38XX_DEV_FIRMWARE_ADDRES 0x20000 +#define ISL38XX_WRITEIO_DELAY 10 /* in us */ +#define ISL38XX_RESET_DELAY 50 /* in ms */ +#define ISL38XX_WAIT_CYCLE 10 /* in 10ms */ +#define ISL38XX_MAX_WAIT_CYCLES 10 + +/* PCI Memory Area */ +#define ISL38XX_HARDWARE_REG 0x0000 +#define ISL38XX_CARDBUS_CIS 0x0800 +#define ISL38XX_DIRECT_MEM_WIN 0x1000 + +/* Hardware registers */ +#define ISL38XX_DEV_INT_REG 0x0000 +#define ISL38XX_INT_IDENT_REG 0x0010 +#define ISL38XX_INT_ACK_REG 0x0014 +#define ISL38XX_INT_EN_REG 0x0018 +#define ISL38XX_GEN_PURP_COM_REG_1 0x0020 +#define ISL38XX_GEN_PURP_COM_REG_2 0x0024 +#define ISL38XX_CTRL_BLK_BASE_REG ISL38XX_GEN_PURP_COM_REG_1 +#define ISL38XX_DIR_MEM_BASE_REG 0x0030 +#define ISL38XX_CTRL_STAT_REG 0x0078 + +/* High end mobos queue up pci writes, the following + * is used to "read" from after a write to force flush */ +#define ISL38XX_PCI_POSTING_FLUSH ISL38XX_INT_EN_REG + +/** + * isl38xx_w32_flush - PCI iomem write helper + * @base: (host) memory base address of the device + * @val: 32bit value (host order) to write + * @offset: byte offset into @base to write value to + * + * This helper takes care of writing a 32bit datum to the + * specified offset into the device's pci memory space, and making sure + * the pci memory buffers get flushed by performing one harmless read + * from the %ISL38XX_PCI_POSTING_FLUSH offset. + */ +static inline void +isl38xx_w32_flush(void __iomem *base, u32 val, unsigned long offset) +{ + writel(val, base + offset); + (void) readl(base + ISL38XX_PCI_POSTING_FLUSH); +} + +/* Device Interrupt register bits */ +#define ISL38XX_DEV_INT_RESET 0x0001 +#define ISL38XX_DEV_INT_UPDATE 0x0002 +#define ISL38XX_DEV_INT_WAKEUP 0x0008 +#define ISL38XX_DEV_INT_SLEEP 0x0010 + +/* Interrupt Identification/Acknowledge/Enable register bits */ +#define ISL38XX_INT_IDENT_UPDATE 0x0002 +#define ISL38XX_INT_IDENT_INIT 0x0004 +#define ISL38XX_INT_IDENT_WAKEUP 0x0008 +#define ISL38XX_INT_IDENT_SLEEP 0x0010 +#define ISL38XX_INT_SOURCES 0x001E + +/* Control/Status register bits */ +/* Looks like there are other meaningful bits + 0x20004400 seen in normal operation, + 0x200044db at 'timeout waiting for mgmt response' +*/ +#define ISL38XX_CTRL_STAT_SLEEPMODE 0x00000200 +#define ISL38XX_CTRL_STAT_CLKRUN 0x00800000 +#define ISL38XX_CTRL_STAT_RESET 0x10000000 +#define ISL38XX_CTRL_STAT_RAMBOOT 0x20000000 +#define ISL38XX_CTRL_STAT_STARTHALTED 0x40000000 +#define ISL38XX_CTRL_STAT_HOST_OVERRIDE 0x80000000 + +/* Control Block definitions */ +#define ISL38XX_CB_RX_DATA_LQ 0 +#define ISL38XX_CB_TX_DATA_LQ 1 +#define ISL38XX_CB_RX_DATA_HQ 2 +#define ISL38XX_CB_TX_DATA_HQ 3 +#define ISL38XX_CB_RX_MGMTQ 4 +#define ISL38XX_CB_TX_MGMTQ 5 +#define ISL38XX_CB_QCOUNT 6 +#define ISL38XX_CB_MGMT_QSIZE 4 +#define ISL38XX_MIN_QTHRESHOLD 4 /* fragments */ + +/* Memory Manager definitions */ +#define MGMT_FRAME_SIZE 1500 /* >= size struct obj_bsslist */ +#define MGMT_TX_FRAME_COUNT 24 /* max 4 + spare 4 + 8 init */ +#define MGMT_RX_FRAME_COUNT 24 /* 4*4 + spare 8 */ +#define MGMT_FRAME_COUNT (MGMT_TX_FRAME_COUNT + MGMT_RX_FRAME_COUNT) +#define CONTROL_BLOCK_SIZE 1024 /* should be enough */ +#define PSM_FRAME_SIZE 1536 +#define PSM_MINIMAL_STATION_COUNT 64 +#define PSM_FRAME_COUNT PSM_MINIMAL_STATION_COUNT +#define PSM_BUFFER_SIZE PSM_FRAME_SIZE * PSM_FRAME_COUNT +#define MAX_TRAP_RX_QUEUE 4 +#define HOST_MEM_BLOCK CONTROL_BLOCK_SIZE + PSM_BUFFER_SIZE + +/* Fragment package definitions */ +#define FRAGMENT_FLAG_MF 0x0001 +#define MAX_FRAGMENT_SIZE 1536 + +/* In monitor mode frames have a header. I don't know exactly how big those + * frame can be but I've never seen any frame bigger than 1584... : + */ +#define MAX_FRAGMENT_SIZE_RX 1600 + +typedef struct { + __le32 address; /* physical address on host */ + __le16 size; /* packet size */ + __le16 flags; /* set of bit-wise flags */ +} isl38xx_fragment; + +struct isl38xx_cb { + __le32 driver_curr_frag[ISL38XX_CB_QCOUNT]; + __le32 device_curr_frag[ISL38XX_CB_QCOUNT]; + isl38xx_fragment rx_data_low[ISL38XX_CB_RX_QSIZE]; + isl38xx_fragment tx_data_low[ISL38XX_CB_TX_QSIZE]; + isl38xx_fragment rx_data_high[ISL38XX_CB_RX_QSIZE]; + isl38xx_fragment tx_data_high[ISL38XX_CB_TX_QSIZE]; + isl38xx_fragment rx_data_mgmt[ISL38XX_CB_MGMT_QSIZE]; + isl38xx_fragment tx_data_mgmt[ISL38XX_CB_MGMT_QSIZE]; +}; + +typedef struct isl38xx_cb isl38xx_control_block; + +/* determine number of entries currently in queue */ +int isl38xx_in_queue(isl38xx_control_block *cb, int queue); + +void isl38xx_disable_interrupts(void __iomem *); +void isl38xx_enable_common_interrupts(void __iomem *); + +void isl38xx_handle_sleep_request(isl38xx_control_block *, int *, + void __iomem *); +void isl38xx_handle_wakeup(isl38xx_control_block *, int *, void __iomem *); +void isl38xx_trigger_device(int, void __iomem *); +void isl38xx_interface_reset(void __iomem *, dma_addr_t); + +#endif /* _ISL_38XX_H */ diff --git a/drivers/net/wireless/intersil/prism54/isl_ioctl.c b/drivers/net/wireless/intersil/prism54/isl_ioctl.c new file mode 100644 index 000000000000..ecbb0546cf3e --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/isl_ioctl.c @@ -0,0 +1,2910 @@ +/* + * Copyright (C) 2002 Intersil Americas Inc. + * (C) 2003,2004 Aurelien Alleaume + * (C) 2003 Herbert Valerio Riedel + * (C) 2003 Luis R. Rodriguez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "prismcompat.h" +#include "isl_ioctl.h" +#include "islpci_mgt.h" +#include "isl_oid.h" /* additional types and defs for isl38xx fw */ +#include "oid_mgt.h" + +#include /* New driver API */ + +#define KEY_SIZE_WEP104 13 /* 104/128-bit WEP keys */ +#define KEY_SIZE_WEP40 5 /* 40/64-bit WEP keys */ +/* KEY_SIZE_TKIP should match isl_oid.h, struct obj_key.key[] size */ +#define KEY_SIZE_TKIP 32 /* TKIP keys */ + +static void prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid, + u8 *wpa_ie, size_t wpa_ie_len); +static size_t prism54_wpa_bss_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie); +static int prism54_set_wpa(struct net_device *, struct iw_request_info *, + __u32 *, char *); + +/* In 500 kbps */ +static const unsigned char scan_rate_list[] = { 2, 4, 11, 22, + 12, 18, 24, 36, + 48, 72, 96, 108 }; + +/** + * prism54_mib_mode_helper - MIB change mode helper function + * @mib: the &struct islpci_mib object to modify + * @iw_mode: new mode (%IW_MODE_*) + * + * This is a helper function, hence it does not lock. Make sure + * caller deals with locking *if* necessary. This function sets the + * mode-dependent mib values and does the mapping of the Linux + * Wireless API modes to Device firmware modes. It also checks for + * correct valid Linux wireless modes. + */ +static int +prism54_mib_mode_helper(islpci_private *priv, u32 iw_mode) +{ + u32 config = INL_CONFIG_MANUALRUN; + u32 mode, bsstype; + + /* For now, just catch early the Repeater and Secondary modes here */ + if (iw_mode == IW_MODE_REPEAT || iw_mode == IW_MODE_SECOND) { + printk(KERN_DEBUG + "%s(): Sorry, Repeater mode and Secondary mode " + "are not yet supported by this driver.\n", __func__); + return -EINVAL; + } + + priv->iw_mode = iw_mode; + + switch (iw_mode) { + case IW_MODE_AUTO: + mode = INL_MODE_CLIENT; + bsstype = DOT11_BSSTYPE_ANY; + break; + case IW_MODE_ADHOC: + mode = INL_MODE_CLIENT; + bsstype = DOT11_BSSTYPE_IBSS; + break; + case IW_MODE_INFRA: + mode = INL_MODE_CLIENT; + bsstype = DOT11_BSSTYPE_INFRA; + break; + case IW_MODE_MASTER: + mode = INL_MODE_AP; + bsstype = DOT11_BSSTYPE_INFRA; + break; + case IW_MODE_MONITOR: + mode = INL_MODE_PROMISCUOUS; + bsstype = DOT11_BSSTYPE_ANY; + config |= INL_CONFIG_RXANNEX; + break; + default: + return -EINVAL; + } + + if (init_wds) + config |= INL_CONFIG_WDS; + mgt_set(priv, DOT11_OID_BSSTYPE, &bsstype); + mgt_set(priv, OID_INL_CONFIG, &config); + mgt_set(priv, OID_INL_MODE, &mode); + + return 0; +} + +/** + * prism54_mib_init - fill MIB cache with defaults + * + * this function initializes the struct given as @mib with defaults, + * of which many are retrieved from the global module parameter + * variables. + */ + +void +prism54_mib_init(islpci_private *priv) +{ + u32 channel, authen, wep, filter, dot1x, mlme, conformance, power, mode; + struct obj_buffer psm_buffer = { + .size = PSM_BUFFER_SIZE, + .addr = priv->device_psm_buffer + }; + + channel = CARD_DEFAULT_CHANNEL; + authen = CARD_DEFAULT_AUTHEN; + wep = CARD_DEFAULT_WEP; + filter = CARD_DEFAULT_FILTER; /* (0) Do not filter un-encrypted data */ + dot1x = CARD_DEFAULT_DOT1X; + mlme = CARD_DEFAULT_MLME_MODE; + conformance = CARD_DEFAULT_CONFORMANCE; + power = 127; + mode = CARD_DEFAULT_IW_MODE; + + mgt_set(priv, DOT11_OID_CHANNEL, &channel); + mgt_set(priv, DOT11_OID_AUTHENABLE, &authen); + mgt_set(priv, DOT11_OID_PRIVACYINVOKED, &wep); + mgt_set(priv, DOT11_OID_PSMBUFFER, &psm_buffer); + mgt_set(priv, DOT11_OID_EXUNENCRYPTED, &filter); + mgt_set(priv, DOT11_OID_DOT1XENABLE, &dot1x); + mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlme); + mgt_set(priv, OID_INL_DOT11D_CONFORMANCE, &conformance); + mgt_set(priv, OID_INL_OUTPUTPOWER, &power); + + /* This sets all of the mode-dependent values */ + prism54_mib_mode_helper(priv, mode); +} + +/* this will be executed outside of atomic context thanks to + * schedule_work(), thus we can as well use sleeping semaphore + * locking */ +void +prism54_update_stats(struct work_struct *work) +{ + islpci_private *priv = container_of(work, islpci_private, stats_work); + char *data; + int j; + struct obj_bss bss, *bss2; + union oid_res_t r; + + mutex_lock(&priv->stats_lock); + +/* Noise floor. + * I'm not sure if the unit is dBm. + * Note : If we are not connected, this value seems to be irrelevant. */ + + mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r); + priv->local_iwstatistics.qual.noise = r.u; + +/* Get the rssi of the link. To do this we need to retrieve a bss. */ + + /* First get the MAC address of the AP we are associated with. */ + mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r); + data = r.ptr; + + /* copy this MAC to the bss */ + memcpy(bss.address, data, ETH_ALEN); + kfree(data); + + /* now ask for the corresponding bss */ + j = mgt_get_request(priv, DOT11_OID_BSSFIND, 0, (void *) &bss, &r); + bss2 = r.ptr; + /* report the rssi and use it to calculate + * link quality through a signal-noise + * ratio */ + priv->local_iwstatistics.qual.level = bss2->rssi; + priv->local_iwstatistics.qual.qual = + bss2->rssi - priv->iwstatistics.qual.noise; + + kfree(bss2); + + /* report that the stats are new */ + priv->local_iwstatistics.qual.updated = 0x7; + +/* Rx : unable to decrypt the MPDU */ + mgt_get_request(priv, DOT11_OID_PRIVRXFAILED, 0, NULL, &r); + priv->local_iwstatistics.discard.code = r.u; + +/* Tx : Max MAC retries num reached */ + mgt_get_request(priv, DOT11_OID_MPDUTXFAILED, 0, NULL, &r); + priv->local_iwstatistics.discard.retries = r.u; + + mutex_unlock(&priv->stats_lock); +} + +struct iw_statistics * +prism54_get_wireless_stats(struct net_device *ndev) +{ + islpci_private *priv = netdev_priv(ndev); + + /* If the stats are being updated return old data */ + if (mutex_trylock(&priv->stats_lock)) { + memcpy(&priv->iwstatistics, &priv->local_iwstatistics, + sizeof (struct iw_statistics)); + /* They won't be marked updated for the next time */ + priv->local_iwstatistics.qual.updated = 0; + mutex_unlock(&priv->stats_lock); + } else + priv->iwstatistics.qual.updated = 0; + + /* Update our wireless stats, but do not schedule to often + * (max 1 HZ) */ + if ((priv->stats_timestamp == 0) || + time_after(jiffies, priv->stats_timestamp + 1 * HZ)) { + schedule_work(&priv->stats_work); + priv->stats_timestamp = jiffies; + } + + return &priv->iwstatistics; +} + +static int +prism54_commit(struct net_device *ndev, struct iw_request_info *info, + char *cwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + + /* simply re-set the last set SSID, this should commit most stuff */ + + /* Commit in Monitor mode is not necessary, also setting essid + * in Monitor mode does not make sense and isn't allowed for this + * device's firmware */ + if (priv->iw_mode != IW_MODE_MONITOR) + return mgt_set_request(priv, DOT11_OID_SSID, 0, NULL); + return 0; +} + +static int +prism54_get_name(struct net_device *ndev, struct iw_request_info *info, + char *cwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + char *capabilities; + union oid_res_t r; + int rvalue; + + if (islpci_get_state(priv) < PRV_STATE_INIT) { + strncpy(cwrq, "NOT READY!", IFNAMSIZ); + return 0; + } + rvalue = mgt_get_request(priv, OID_INL_PHYCAPABILITIES, 0, NULL, &r); + + switch (r.u) { + case INL_PHYCAP_5000MHZ: + capabilities = "IEEE 802.11a/b/g"; + break; + case INL_PHYCAP_FAA: + capabilities = "IEEE 802.11b/g - FAA Support"; + break; + case INL_PHYCAP_2400MHZ: + default: + capabilities = "IEEE 802.11b/g"; /* Default */ + break; + } + strncpy(cwrq, capabilities, IFNAMSIZ); + return rvalue; +} + +static int +prism54_set_freq(struct net_device *ndev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + int rvalue; + u32 c; + + if (fwrq->m < 1000) + /* we have a channel number */ + c = fwrq->m; + else + c = (fwrq->e == 1) ? channel_of_freq(fwrq->m / 100000) : 0; + + rvalue = c ? mgt_set_request(priv, DOT11_OID_CHANNEL, 0, &c) : -EINVAL; + + /* Call commit handler */ + return (rvalue ? rvalue : -EINPROGRESS); +} + +static int +prism54_get_freq(struct net_device *ndev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + union oid_res_t r; + int rvalue; + + rvalue = mgt_get_request(priv, DOT11_OID_CHANNEL, 0, NULL, &r); + fwrq->i = r.u; + rvalue |= mgt_get_request(priv, DOT11_OID_FREQUENCY, 0, NULL, &r); + fwrq->m = r.u; + fwrq->e = 3; + + return rvalue; +} + +static int +prism54_set_mode(struct net_device *ndev, struct iw_request_info *info, + __u32 * uwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + u32 mlmeautolevel = CARD_DEFAULT_MLME_MODE; + + /* Let's see if the user passed a valid Linux Wireless mode */ + if (*uwrq > IW_MODE_MONITOR || *uwrq < IW_MODE_AUTO) { + printk(KERN_DEBUG + "%s: %s() You passed a non-valid init_mode.\n", + priv->ndev->name, __func__); + return -EINVAL; + } + + down_write(&priv->mib_sem); + + if (prism54_mib_mode_helper(priv, *uwrq)) { + up_write(&priv->mib_sem); + return -EOPNOTSUPP; + } + + /* the ACL code needs an intermediate mlmeautolevel. The wpa stuff an + * extended one. + */ + if ((*uwrq == IW_MODE_MASTER) && (priv->acl.policy != MAC_POLICY_OPEN)) + mlmeautolevel = DOT11_MLME_INTERMEDIATE; + if (priv->wpa) + mlmeautolevel = DOT11_MLME_EXTENDED; + + mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel); + + if (mgt_commit(priv)) { + up_write(&priv->mib_sem); + return -EIO; + } + priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR) + ? priv->monitor_type : ARPHRD_ETHER; + up_write(&priv->mib_sem); + + return 0; +} + +/* Use mib cache */ +static int +prism54_get_mode(struct net_device *ndev, struct iw_request_info *info, + __u32 * uwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + + BUG_ON((priv->iw_mode < IW_MODE_AUTO) || (priv->iw_mode > + IW_MODE_MONITOR)); + *uwrq = priv->iw_mode; + + return 0; +} + +/* we use DOT11_OID_EDTHRESHOLD. From what I guess the card will not try to + * emit data if (sensitivity > rssi - noise) (in dBm). + * prism54_set_sens does not seem to work. + */ + +static int +prism54_set_sens(struct net_device *ndev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + u32 sens; + + /* by default the card sets this to 20. */ + sens = vwrq->disabled ? 20 : vwrq->value; + + return mgt_set_request(priv, DOT11_OID_EDTHRESHOLD, 0, &sens); +} + +static int +prism54_get_sens(struct net_device *ndev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + union oid_res_t r; + int rvalue; + + rvalue = mgt_get_request(priv, DOT11_OID_EDTHRESHOLD, 0, NULL, &r); + + vwrq->value = r.u; + vwrq->disabled = (vwrq->value == 0); + vwrq->fixed = 1; + + return rvalue; +} + +static int +prism54_get_range(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + struct iw_range *range = (struct iw_range *) extra; + islpci_private *priv = netdev_priv(ndev); + u8 *data; + int i, m, rvalue; + struct obj_frequencies *freq; + union oid_res_t r; + + memset(range, 0, sizeof (struct iw_range)); + dwrq->length = sizeof (struct iw_range); + + /* set the wireless extension version number */ + range->we_version_source = SUPPORTED_WIRELESS_EXT; + range->we_version_compiled = WIRELESS_EXT; + + /* Now the encoding capabilities */ + range->num_encoding_sizes = 3; + /* 64(40) bits WEP */ + range->encoding_size[0] = 5; + /* 128(104) bits WEP */ + range->encoding_size[1] = 13; + /* 256 bits for WPA-PSK */ + range->encoding_size[2] = 32; + /* 4 keys are allowed */ + range->max_encoding_tokens = 4; + + /* we don't know the quality range... */ + range->max_qual.level = 0; + range->max_qual.noise = 0; + range->max_qual.qual = 0; + /* these value describe an average quality. Needs more tweaking... */ + range->avg_qual.level = -80; /* -80 dBm */ + range->avg_qual.noise = 0; /* don't know what to put here */ + range->avg_qual.qual = 0; + + range->sensitivity = 200; + + /* retry limit capabilities */ + range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; + range->retry_flags = IW_RETRY_LIMIT; + range->r_time_flags = IW_RETRY_LIFETIME; + + /* I don't know the range. Put stupid things here */ + range->min_retry = 1; + range->max_retry = 65535; + range->min_r_time = 1024; + range->max_r_time = 65535 * 1024; + + /* txpower is supported in dBm's */ + range->txpower_capa = IW_TXPOW_DBM; + + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | + IW_EVENT_CAPA_MASK(SIOCGIWAP)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVCUSTOM); + + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return 0; + + /* Request the device for the supported frequencies + * not really relevant since some devices will report the 5 GHz band + * frequencies even if they don't support them. + */ + rvalue = + mgt_get_request(priv, DOT11_OID_SUPPORTEDFREQUENCIES, 0, NULL, &r); + freq = r.ptr; + + range->num_channels = freq->nr; + range->num_frequency = freq->nr; + + m = min(IW_MAX_FREQUENCIES, (int) freq->nr); + for (i = 0; i < m; i++) { + range->freq[i].m = freq->mhz[i]; + range->freq[i].e = 6; + range->freq[i].i = channel_of_freq(freq->mhz[i]); + } + kfree(freq); + + rvalue |= mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r); + data = r.ptr; + + /* We got an array of char. It is NULL terminated. */ + i = 0; + while ((i < IW_MAX_BITRATES) && (*data != 0)) { + /* the result must be in bps. The card gives us 500Kbps */ + range->bitrate[i] = *data * 500000; + i++; + data++; + } + range->num_bitrates = i; + kfree(r.ptr); + + return rvalue; +} + +/* Set AP address*/ + +static int +prism54_set_wap(struct net_device *ndev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + char bssid[6]; + int rvalue; + + if (awrq->sa_family != ARPHRD_ETHER) + return -EINVAL; + + /* prepare the structure for the set object */ + memcpy(&bssid[0], awrq->sa_data, ETH_ALEN); + + /* set the bssid -- does this make sense when in AP mode? */ + rvalue = mgt_set_request(priv, DOT11_OID_BSSID, 0, &bssid); + + return (rvalue ? rvalue : -EINPROGRESS); /* Call commit handler */ +} + +/* get AP address*/ + +static int +prism54_get_wap(struct net_device *ndev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + union oid_res_t r; + int rvalue; + + rvalue = mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r); + memcpy(awrq->sa_data, r.ptr, ETH_ALEN); + awrq->sa_family = ARPHRD_ETHER; + kfree(r.ptr); + + return rvalue; +} + +static int +prism54_set_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + /* hehe the device does this automagicaly */ + return 0; +} + +/* a little helper that will translate our data into a card independent + * format that the Wireless Tools will understand. This was inspired by + * the "Aironet driver for 4500 and 4800 series cards" (GPL) + */ + +static char * +prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info, + char *current_ev, char *end_buf, struct obj_bss *bss, + char noise) +{ + struct iw_event iwe; /* Temporary buffer */ + short cap; + islpci_private *priv = netdev_priv(ndev); + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + + /* The first entry must be the MAC address */ + memcpy(iwe.u.ap_addr.sa_data, bss->address, ETH_ALEN); + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + iwe.cmd = SIOCGIWAP; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); + + /* The following entries will be displayed in the same order we give them */ + + /* The ESSID. */ + iwe.u.data.length = bss->ssid.length; + iwe.u.data.flags = 1; + iwe.cmd = SIOCGIWESSID; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ssid.octets); + + /* Capabilities */ +#define CAP_ESS 0x01 +#define CAP_IBSS 0x02 +#define CAP_CRYPT 0x10 + + /* Mode */ + cap = bss->capinfo; + iwe.u.mode = 0; + if (cap & CAP_ESS) + iwe.u.mode = IW_MODE_MASTER; + else if (cap & CAP_IBSS) + iwe.u.mode = IW_MODE_ADHOC; + iwe.cmd = SIOCGIWMODE; + if (iwe.u.mode) + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); + + /* Encryption capability */ + if (cap & CAP_CRYPT) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + iwe.cmd = SIOCGIWENCODE; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, NULL); + + /* Add frequency. (short) bss->channel is the frequency in MHz */ + iwe.u.freq.m = bss->channel; + iwe.u.freq.e = 6; + iwe.cmd = SIOCGIWFREQ; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); + + /* Add quality statistics */ + iwe.u.qual.level = bss->rssi; + iwe.u.qual.noise = noise; + /* do a simple SNR for quality */ + iwe.u.qual.qual = bss->rssi - noise; + iwe.cmd = IWEVQUAL; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); + + /* Add WPA/RSN Information Element, if any */ + wpa_ie_len = prism54_wpa_bss_ie_get(priv, bss->address, wpa_ie); + if (wpa_ie_len > 0) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = min_t(size_t, wpa_ie_len, MAX_WPA_IE_LEN); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, wpa_ie); + } + /* Do the bitrates */ + { + char *current_val = current_ev + iwe_stream_lcp_len(info); + int i; + int mask; + + iwe.cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + + /* Parse the bitmask */ + mask = 0x1; + for(i = 0; i < sizeof(scan_rate_list); i++) { + if(bss->rates & mask) { + iwe.u.bitrate.value = (scan_rate_list[i] * 500000); + current_val = iwe_stream_add_value( + info, current_ev, current_val, + end_buf, &iwe, IW_EV_PARAM_LEN); + } + mask <<= 1; + } + /* Check if we added any event */ + if ((current_val - current_ev) > iwe_stream_lcp_len(info)) + current_ev = current_val; + } + + return current_ev; +} + +static int +prism54_get_scan(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + int i, rvalue; + struct obj_bsslist *bsslist; + u32 noise = 0; + char *current_ev = extra; + union oid_res_t r; + + if (islpci_get_state(priv) < PRV_STATE_INIT) { + /* device is not ready, fail gently */ + dwrq->length = 0; + return 0; + } + + /* first get the noise value. We will use it to report the link quality */ + rvalue = mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r); + noise = r.u; + + /* Ask the device for a list of known bss. + * The old API, using SIOCGIWAPLIST, had a hard limit of IW_MAX_AP=64. + * The new API, using SIOCGIWSCAN, is only limited by the buffer size. + * WE-14->WE-16, the buffer is limited to IW_SCAN_MAX_DATA bytes. + * Starting with WE-17, the buffer can be as big as needed. + * But the device won't repport anything if you change the value + * of IWMAX_BSS=24. */ + + rvalue |= mgt_get_request(priv, DOT11_OID_BSSLIST, 0, NULL, &r); + bsslist = r.ptr; + + /* ok now, scan the list and translate its info */ + for (i = 0; i < (int) bsslist->nr; i++) { + current_ev = prism54_translate_bss(ndev, info, current_ev, + extra + dwrq->length, + &(bsslist->bsslist[i]), + noise); + + /* Check if there is space for one more entry */ + if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) { + /* Ask user space to try again with a bigger buffer */ + rvalue = -E2BIG; + break; + } + } + + kfree(bsslist); + dwrq->length = (current_ev - extra); + dwrq->flags = 0; /* todo */ + + return rvalue; +} + +static int +prism54_set_essid(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct obj_ssid essid; + + memset(essid.octets, 0, 33); + + /* Check if we were asked for `any' */ + if (dwrq->flags && dwrq->length) { + if (dwrq->length > 32) + return -E2BIG; + essid.length = dwrq->length; + memcpy(essid.octets, extra, dwrq->length); + } else + essid.length = 0; + + if (priv->iw_mode != IW_MODE_MONITOR) + return mgt_set_request(priv, DOT11_OID_SSID, 0, &essid); + + /* If in monitor mode, just save to mib */ + mgt_set(priv, DOT11_OID_SSID, &essid); + return 0; + +} + +static int +prism54_get_essid(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct obj_ssid *essid; + union oid_res_t r; + int rvalue; + + rvalue = mgt_get_request(priv, DOT11_OID_SSID, 0, NULL, &r); + essid = r.ptr; + + if (essid->length) { + dwrq->flags = 1; /* set ESSID to ON for Wireless Extensions */ + /* if it is too big, trunk it */ + dwrq->length = min((u8)IW_ESSID_MAX_SIZE, essid->length); + } else { + dwrq->flags = 0; + dwrq->length = 0; + } + essid->octets[dwrq->length] = '\0'; + memcpy(extra, essid->octets, dwrq->length); + kfree(essid); + + return rvalue; +} + +/* Provides no functionality, just completes the ioctl. In essence this is a + * just a cosmetic ioctl. + */ +static int +prism54_set_nick(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + + if (dwrq->length > IW_ESSID_MAX_SIZE) + return -E2BIG; + + down_write(&priv->mib_sem); + memset(priv->nickname, 0, sizeof (priv->nickname)); + memcpy(priv->nickname, extra, dwrq->length); + up_write(&priv->mib_sem); + + return 0; +} + +static int +prism54_get_nick(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + + dwrq->length = 0; + + down_read(&priv->mib_sem); + dwrq->length = strlen(priv->nickname); + memcpy(extra, priv->nickname, dwrq->length); + up_read(&priv->mib_sem); + + return 0; +} + +/* Set the allowed Bitrates */ + +static int +prism54_set_rate(struct net_device *ndev, + struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + + islpci_private *priv = netdev_priv(ndev); + u32 rate, profile; + char *data; + int ret, i; + union oid_res_t r; + + if (vwrq->value == -1) { + /* auto mode. No limit. */ + profile = 1; + return mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile); + } + + ret = mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r); + if (ret) { + kfree(r.ptr); + return ret; + } + + rate = (u32) (vwrq->value / 500000); + data = r.ptr; + i = 0; + + while (data[i]) { + if (rate && (data[i] == rate)) { + break; + } + if (vwrq->value == i) { + break; + } + data[i] |= 0x80; + i++; + } + + if (!data[i]) { + kfree(r.ptr); + return -EINVAL; + } + + data[i] |= 0x80; + data[i + 1] = 0; + + /* Now, check if we want a fixed or auto value */ + if (vwrq->fixed) { + data[0] = data[i]; + data[1] = 0; + } + +/* + i = 0; + printk("prism54 rate: "); + while(data[i]) { + printk("%u ", data[i]); + i++; + } + printk("0\n"); +*/ + profile = -1; + ret = mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile); + ret |= mgt_set_request(priv, DOT11_OID_EXTENDEDRATES, 0, data); + ret |= mgt_set_request(priv, DOT11_OID_RATES, 0, data); + + kfree(r.ptr); + + return ret; +} + +/* Get the current bit rate */ +static int +prism54_get_rate(struct net_device *ndev, + struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + int rvalue; + char *data; + union oid_res_t r; + + /* Get the current bit rate */ + if ((rvalue = mgt_get_request(priv, GEN_OID_LINKSTATE, 0, NULL, &r))) + return rvalue; + vwrq->value = r.u * 500000; + + /* request the device for the enabled rates */ + rvalue = mgt_get_request(priv, DOT11_OID_RATES, 0, NULL, &r); + if (rvalue) { + kfree(r.ptr); + return rvalue; + } + data = r.ptr; + vwrq->fixed = (data[0] != 0) && (data[1] == 0); + kfree(r.ptr); + + return 0; +} + +static int +prism54_set_rts(struct net_device *ndev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + + return mgt_set_request(priv, DOT11_OID_RTSTHRESH, 0, &vwrq->value); +} + +static int +prism54_get_rts(struct net_device *ndev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + union oid_res_t r; + int rvalue; + + /* get the rts threshold */ + rvalue = mgt_get_request(priv, DOT11_OID_RTSTHRESH, 0, NULL, &r); + vwrq->value = r.u; + + return rvalue; +} + +static int +prism54_set_frag(struct net_device *ndev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + + return mgt_set_request(priv, DOT11_OID_FRAGTHRESH, 0, &vwrq->value); +} + +static int +prism54_get_frag(struct net_device *ndev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + union oid_res_t r; + int rvalue; + + rvalue = mgt_get_request(priv, DOT11_OID_FRAGTHRESH, 0, NULL, &r); + vwrq->value = r.u; + + return rvalue; +} + +/* Here we have (min,max) = max retries for (small frames, big frames). Where + * big frame <=> bigger than the rts threshold + * small frame <=> smaller than the rts threshold + * This is not really the behavior expected by the wireless tool but it seems + * to be a common behavior in other drivers. + */ + +static int +prism54_set_retry(struct net_device *ndev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + u32 slimit = 0, llimit = 0; /* short and long limit */ + u32 lifetime = 0; + int rvalue = 0; + + if (vwrq->disabled) + /* we cannot disable this feature */ + return -EINVAL; + + if (vwrq->flags & IW_RETRY_LIMIT) { + if (vwrq->flags & IW_RETRY_SHORT) + slimit = vwrq->value; + else if (vwrq->flags & IW_RETRY_LONG) + llimit = vwrq->value; + else { + /* we are asked to set both */ + slimit = vwrq->value; + llimit = vwrq->value; + } + } + if (vwrq->flags & IW_RETRY_LIFETIME) + /* Wireless tools use us unit while the device uses 1024 us unit */ + lifetime = vwrq->value / 1024; + + /* now set what is requested */ + if (slimit) + rvalue = + mgt_set_request(priv, DOT11_OID_SHORTRETRIES, 0, &slimit); + if (llimit) + rvalue |= + mgt_set_request(priv, DOT11_OID_LONGRETRIES, 0, &llimit); + if (lifetime) + rvalue |= + mgt_set_request(priv, DOT11_OID_MAXTXLIFETIME, 0, + &lifetime); + return rvalue; +} + +static int +prism54_get_retry(struct net_device *ndev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + union oid_res_t r; + int rvalue = 0; + vwrq->disabled = 0; /* It cannot be disabled */ + + if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { + /* we are asked for the life time */ + rvalue = + mgt_get_request(priv, DOT11_OID_MAXTXLIFETIME, 0, NULL, &r); + vwrq->value = r.u * 1024; + vwrq->flags = IW_RETRY_LIFETIME; + } else if ((vwrq->flags & IW_RETRY_LONG)) { + /* we are asked for the long retry limit */ + rvalue |= + mgt_get_request(priv, DOT11_OID_LONGRETRIES, 0, NULL, &r); + vwrq->value = r.u; + vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; + } else { + /* default. get the short retry limit */ + rvalue |= + mgt_get_request(priv, DOT11_OID_SHORTRETRIES, 0, NULL, &r); + vwrq->value = r.u; + vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT; + } + + return rvalue; +} + +static int +prism54_set_encode(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + int rvalue = 0, force = 0; + int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0; + union oid_res_t r; + + /* with the new API, it's impossible to get a NULL pointer. + * New version of iwconfig set the IW_ENCODE_NOKEY flag + * when no key is given, but older versions don't. */ + + if (dwrq->length > 0) { + /* we have a key to set */ + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + int current_index; + struct obj_key key = { DOT11_PRIV_WEP, 0, "" }; + + /* get the current key index */ + rvalue = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r); + current_index = r.u; + /* Verify that the key is not marked as invalid */ + if (!(dwrq->flags & IW_ENCODE_NOKEY)) { + if (dwrq->length > KEY_SIZE_TKIP) { + /* User-provided key data too big */ + return -EINVAL; + } + if (dwrq->length > KEY_SIZE_WEP104) { + /* WPA-PSK TKIP */ + key.type = DOT11_PRIV_TKIP; + key.length = KEY_SIZE_TKIP; + } else if (dwrq->length > KEY_SIZE_WEP40) { + /* WEP 104/128 */ + key.length = KEY_SIZE_WEP104; + } else { + /* WEP 40/64 */ + key.length = KEY_SIZE_WEP40; + } + memset(key.key, 0, sizeof (key.key)); + memcpy(key.key, extra, dwrq->length); + + if ((index < 0) || (index > 3)) + /* no index provided use the current one */ + index = current_index; + + /* now send the key to the card */ + rvalue |= + mgt_set_request(priv, DOT11_OID_DEFKEYX, index, + &key); + } + /* + * If a valid key is set, encryption should be enabled + * (user may turn it off later). + * This is also how "iwconfig ethX key on" works + */ + if ((index == current_index) && (key.length > 0)) + force = 1; + } else { + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if ((index >= 0) && (index <= 3)) { + /* we want to set the key index */ + rvalue |= + mgt_set_request(priv, DOT11_OID_DEFKEYID, 0, + &index); + } else { + if (!(dwrq->flags & IW_ENCODE_MODE)) { + /* we cannot do anything. Complain. */ + return -EINVAL; + } + } + } + /* now read the flags */ + if (dwrq->flags & IW_ENCODE_DISABLED) { + /* Encoding disabled, + * authen = DOT11_AUTH_OS; + * invoke = 0; + * exunencrypt = 0; */ + } + if (dwrq->flags & IW_ENCODE_OPEN) + /* Encode but accept non-encoded packets. No auth */ + invoke = 1; + if ((dwrq->flags & IW_ENCODE_RESTRICTED) || force) { + /* Refuse non-encoded packets. Auth */ + authen = DOT11_AUTH_BOTH; + invoke = 1; + exunencrypt = 1; + } + /* do the change if requested */ + if ((dwrq->flags & IW_ENCODE_MODE) || force) { + rvalue |= + mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen); + rvalue |= + mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &invoke); + rvalue |= + mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, + &exunencrypt); + } + return rvalue; +} + +static int +prism54_get_encode(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct obj_key *key; + u32 devindex, index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + u32 authen = 0, invoke = 0, exunencrypt = 0; + int rvalue; + union oid_res_t r; + + /* first get the flags */ + rvalue = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r); + authen = r.u; + rvalue |= mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r); + invoke = r.u; + rvalue |= mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r); + exunencrypt = r.u; + + if (invoke && (authen == DOT11_AUTH_BOTH) && exunencrypt) + dwrq->flags = IW_ENCODE_RESTRICTED; + else if ((authen == DOT11_AUTH_OS) && !exunencrypt) { + if (invoke) + dwrq->flags = IW_ENCODE_OPEN; + else + dwrq->flags = IW_ENCODE_DISABLED; + } else + /* The card should not work in this state */ + dwrq->flags = 0; + + /* get the current device key index */ + rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r); + devindex = r.u; + /* Now get the key, return it */ + if (index == -1 || index > 3) + /* no index provided, use the current one */ + index = devindex; + rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYX, index, NULL, &r); + key = r.ptr; + dwrq->length = key->length; + memcpy(extra, key->key, dwrq->length); + kfree(key); + /* return the used key index */ + dwrq->flags |= devindex + 1; + + return rvalue; +} + +static int +prism54_get_txpower(struct net_device *ndev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + union oid_res_t r; + int rvalue; + + rvalue = mgt_get_request(priv, OID_INL_OUTPUTPOWER, 0, NULL, &r); + /* intersil firmware operates in 0.25 dBm (1/4 dBm) */ + vwrq->value = (s32) r.u / 4; + vwrq->fixed = 1; + /* radio is not turned of + * btw: how is possible to turn off only the radio + */ + vwrq->disabled = 0; + + return rvalue; +} + +static int +prism54_set_txpower(struct net_device *ndev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + s32 u = vwrq->value; + + /* intersil firmware operates in 0.25 dBm (1/4) */ + u *= 4; + if (vwrq->disabled) { + /* don't know how to disable radio */ + printk(KERN_DEBUG + "%s: %s() disabling radio is not yet supported.\n", + priv->ndev->name, __func__); + return -ENOTSUPP; + } else if (vwrq->fixed) + /* currently only fixed value is supported */ + return mgt_set_request(priv, OID_INL_OUTPUTPOWER, 0, &u); + else { + printk(KERN_DEBUG + "%s: %s() auto power will be implemented later.\n", + priv->ndev->name, __func__); + return -ENOTSUPP; + } +} + +static int prism54_set_genie(struct net_device *ndev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + int alen, ret = 0; + struct obj_attachment *attach; + + if (data->length > MAX_WPA_IE_LEN || + (data->length && extra == NULL)) + return -EINVAL; + + memcpy(priv->wpa_ie, extra, data->length); + priv->wpa_ie_len = data->length; + + alen = sizeof(*attach) + priv->wpa_ie_len; + attach = kzalloc(alen, GFP_KERNEL); + if (attach == NULL) + return -ENOMEM; + +#define WLAN_FC_TYPE_MGMT 0 +#define WLAN_FC_STYPE_ASSOC_REQ 0 +#define WLAN_FC_STYPE_REASSOC_REQ 2 + + /* Note: endianness is covered by mgt_set_varlen */ + attach->type = (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_ASSOC_REQ << 4); + attach->id = -1; + attach->size = priv->wpa_ie_len; + memcpy(attach->data, extra, priv->wpa_ie_len); + + ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach, + priv->wpa_ie_len); + if (ret == 0) { + attach->type = (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_REASSOC_REQ << 4); + + ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach, + priv->wpa_ie_len); + if (ret == 0) + printk(KERN_DEBUG "%s: WPA IE Attachment was set\n", + ndev->name); + } + + kfree(attach); + return ret; +} + + +static int prism54_get_genie(struct net_device *ndev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + int len = priv->wpa_ie_len; + + if (len <= 0) { + data->length = 0; + return 0; + } + + if (data->length < len) + return -E2BIG; + + data->length = len; + memcpy(extra, priv->wpa_ie, len); + + return 0; +} + +static int prism54_set_auth(struct net_device *ndev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct iw_param *param = &wrqu->param; + u32 mlmelevel = 0, authen = 0, dot1x = 0; + u32 exunencrypt = 0, privinvoked = 0, wpa = 0; + u32 old_wpa; + int ret = 0; + union oid_res_t r; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return 0; + + /* first get the flags */ + down_write(&priv->mib_sem); + wpa = old_wpa = priv->wpa; + up_write(&priv->mib_sem); + ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r); + authen = r.u; + ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r); + privinvoked = r.u; + ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r); + exunencrypt = r.u; + ret = mgt_get_request(priv, DOT11_OID_DOT1XENABLE, 0, NULL, &r); + dot1x = r.u; + ret = mgt_get_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, NULL, &r); + mlmelevel = r.u; + + if (ret < 0) + goto out; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + break; + + case IW_AUTH_WPA_ENABLED: + /* Do the same thing as IW_AUTH_WPA_VERSION */ + if (param->value) { + wpa = 1; + privinvoked = 1; /* For privacy invoked */ + exunencrypt = 1; /* Filter out all unencrypted frames */ + dot1x = 0x01; /* To enable eap filter */ + mlmelevel = DOT11_MLME_EXTENDED; + authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */ + } else { + wpa = 0; + privinvoked = 0; + exunencrypt = 0; /* Do not filter un-encrypted data */ + dot1x = 0; + mlmelevel = DOT11_MLME_AUTO; + } + break; + + case IW_AUTH_WPA_VERSION: + if (param->value & IW_AUTH_WPA_VERSION_DISABLED) { + wpa = 0; + privinvoked = 0; + exunencrypt = 0; /* Do not filter un-encrypted data */ + dot1x = 0; + mlmelevel = DOT11_MLME_AUTO; + } else { + if (param->value & IW_AUTH_WPA_VERSION_WPA) + wpa = 1; + else if (param->value & IW_AUTH_WPA_VERSION_WPA2) + wpa = 2; + privinvoked = 1; /* For privacy invoked */ + exunencrypt = 1; /* Filter out all unencrypted frames */ + dot1x = 0x01; /* To enable eap filter */ + mlmelevel = DOT11_MLME_EXTENDED; + authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */ + } + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + /* dot1x should be the opposite of RX_UNENCRYPTED_EAPOL; + * turn off dot1x when allowing receipt of unencrypted EAPOL + * frames, turn on dot1x when receipt should be disallowed + */ + dot1x = param->value ? 0 : 0x01; + break; + + case IW_AUTH_PRIVACY_INVOKED: + privinvoked = param->value ? 1 : 0; + break; + + case IW_AUTH_DROP_UNENCRYPTED: + exunencrypt = param->value ? 1 : 0; + break; + + case IW_AUTH_80211_AUTH_ALG: + if (param->value & IW_AUTH_ALG_SHARED_KEY) { + /* Only WEP uses _SK and _BOTH */ + if (wpa > 0) { + ret = -EINVAL; + goto out; + } + authen = DOT11_AUTH_SK; + } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { + authen = DOT11_AUTH_OS; + } else { + ret = -EINVAL; + goto out; + } + break; + + default: + return -EOPNOTSUPP; + } + + /* Set all the values */ + down_write(&priv->mib_sem); + priv->wpa = wpa; + up_write(&priv->mib_sem); + mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen); + mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &privinvoked); + mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, &exunencrypt); + mgt_set_request(priv, DOT11_OID_DOT1XENABLE, 0, &dot1x); + mgt_set_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, &mlmelevel); + +out: + return ret; +} + +static int prism54_get_auth(struct net_device *ndev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct iw_param *param = &wrqu->param; + u32 wpa = 0; + int ret = 0; + union oid_res_t r; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return 0; + + /* first get the flags */ + down_write(&priv->mib_sem); + wpa = priv->wpa; + up_write(&priv->mib_sem); + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * wpa_supplicant will control these internally + */ + ret = -EOPNOTSUPP; + break; + + case IW_AUTH_WPA_VERSION: + switch (wpa) { + case 1: + param->value = IW_AUTH_WPA_VERSION_WPA; + break; + case 2: + param->value = IW_AUTH_WPA_VERSION_WPA2; + break; + case 0: + default: + param->value = IW_AUTH_WPA_VERSION_DISABLED; + break; + } + break; + + case IW_AUTH_DROP_UNENCRYPTED: + ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r); + if (ret >= 0) + param->value = r.u > 0 ? 1 : 0; + break; + + case IW_AUTH_80211_AUTH_ALG: + ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r); + if (ret >= 0) { + switch (r.u) { + case DOT11_AUTH_OS: + param->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + case DOT11_AUTH_BOTH: + case DOT11_AUTH_SK: + param->value = IW_AUTH_ALG_SHARED_KEY; + break; + case DOT11_AUTH_NONE: + default: + param->value = 0; + break; + } + } + break; + + case IW_AUTH_WPA_ENABLED: + param->value = wpa > 0 ? 1 : 0; + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + ret = mgt_get_request(priv, DOT11_OID_DOT1XENABLE, 0, NULL, &r); + if (ret >= 0) + param->value = r.u > 0 ? 1 : 0; + break; + + case IW_AUTH_PRIVACY_INVOKED: + ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r); + if (ret >= 0) + param->value = r.u > 0 ? 1 : 0; + break; + + default: + return -EOPNOTSUPP; + } + return ret; +} + +static int prism54_set_encodeext(struct net_device *ndev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, alg = ext->alg, set_key = 1; + union oid_res_t r; + int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0; + int ret = 0; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return 0; + + /* Determine and validate the key index */ + idx = (encoding->flags & IW_ENCODE_INDEX) - 1; + if (idx) { + if (idx < 0 || idx > 3) + return -EINVAL; + } else { + ret = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r); + if (ret < 0) + goto out; + idx = r.u; + } + + if (encoding->flags & IW_ENCODE_DISABLED) + alg = IW_ENCODE_ALG_NONE; + + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + /* Only set transmit key index here, actual + * key is set below if needed. + */ + ret = mgt_set_request(priv, DOT11_OID_DEFKEYID, 0, &idx); + set_key = ext->key_len > 0 ? 1 : 0; + } + + if (set_key) { + struct obj_key key = { DOT11_PRIV_WEP, 0, "" }; + switch (alg) { + case IW_ENCODE_ALG_NONE: + break; + case IW_ENCODE_ALG_WEP: + if (ext->key_len > KEY_SIZE_WEP104) { + ret = -EINVAL; + goto out; + } + if (ext->key_len > KEY_SIZE_WEP40) + key.length = KEY_SIZE_WEP104; + else + key.length = KEY_SIZE_WEP40; + break; + case IW_ENCODE_ALG_TKIP: + if (ext->key_len > KEY_SIZE_TKIP) { + ret = -EINVAL; + goto out; + } + key.type = DOT11_PRIV_TKIP; + key.length = KEY_SIZE_TKIP; + break; + default: + return -EINVAL; + } + + if (key.length) { + memset(key.key, 0, sizeof(key.key)); + memcpy(key.key, ext->key, ext->key_len); + ret = mgt_set_request(priv, DOT11_OID_DEFKEYX, idx, + &key); + if (ret < 0) + goto out; + } + } + + /* Read the flags */ + if (encoding->flags & IW_ENCODE_DISABLED) { + /* Encoding disabled, + * authen = DOT11_AUTH_OS; + * invoke = 0; + * exunencrypt = 0; */ + } + if (encoding->flags & IW_ENCODE_OPEN) { + /* Encode but accept non-encoded packets. No auth */ + invoke = 1; + } + if (encoding->flags & IW_ENCODE_RESTRICTED) { + /* Refuse non-encoded packets. Auth */ + authen = DOT11_AUTH_BOTH; + invoke = 1; + exunencrypt = 1; + } + + /* do the change if requested */ + if (encoding->flags & IW_ENCODE_MODE) { + ret = mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, + &authen); + ret = mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, + &invoke); + ret = mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, + &exunencrypt); + } + +out: + return ret; +} + + +static int prism54_get_encodeext(struct net_device *ndev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, max_key_len; + union oid_res_t r; + int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0, wpa = 0; + int ret = 0; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return 0; + + /* first get the flags */ + ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r); + authen = r.u; + ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r); + invoke = r.u; + ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r); + exunencrypt = r.u; + if (ret < 0) + goto out; + + max_key_len = encoding->length - sizeof(*ext); + if (max_key_len < 0) + return -EINVAL; + + idx = (encoding->flags & IW_ENCODE_INDEX) - 1; + if (idx) { + if (idx < 0 || idx > 3) + return -EINVAL; + } else { + ret = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r); + if (ret < 0) + goto out; + idx = r.u; + } + + encoding->flags = idx + 1; + memset(ext, 0, sizeof(*ext)); + + switch (authen) { + case DOT11_AUTH_BOTH: + case DOT11_AUTH_SK: + wrqu->encoding.flags |= IW_ENCODE_RESTRICTED; + case DOT11_AUTH_OS: + default: + wrqu->encoding.flags |= IW_ENCODE_OPEN; + break; + } + + down_write(&priv->mib_sem); + wpa = priv->wpa; + up_write(&priv->mib_sem); + + if (authen == DOT11_AUTH_OS && !exunencrypt && !invoke && !wpa) { + /* No encryption */ + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + wrqu->encoding.flags |= IW_ENCODE_DISABLED; + } else { + struct obj_key *key; + + ret = mgt_get_request(priv, DOT11_OID_DEFKEYX, idx, NULL, &r); + if (ret < 0) + goto out; + key = r.ptr; + if (max_key_len < key->length) { + ret = -E2BIG; + goto out; + } + memcpy(ext->key, key->key, key->length); + ext->key_len = key->length; + + switch (key->type) { + case DOT11_PRIV_TKIP: + ext->alg = IW_ENCODE_ALG_TKIP; + break; + default: + case DOT11_PRIV_WEP: + ext->alg = IW_ENCODE_ALG_WEP; + break; + } + wrqu->encoding.flags |= IW_ENCODE_ENABLED; + } + +out: + return ret; +} + + +static int +prism54_reset(struct net_device *ndev, struct iw_request_info *info, + __u32 * uwrq, char *extra) +{ + islpci_reset(netdev_priv(ndev), 0); + + return 0; +} + +static int +prism54_get_oid(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + union oid_res_t r; + int rvalue; + enum oid_num_t n = dwrq->flags; + + rvalue = mgt_get_request(netdev_priv(ndev), n, 0, NULL, &r); + dwrq->length = mgt_response_to_str(n, &r, extra); + if ((isl_oid[n].flags & OID_FLAG_TYPE) != OID_TYPE_U32) + kfree(r.ptr); + return rvalue; +} + +static int +prism54_set_u32(struct net_device *ndev, struct iw_request_info *info, + __u32 * uwrq, char *extra) +{ + u32 oid = uwrq[0], u = uwrq[1]; + + return mgt_set_request(netdev_priv(ndev), oid, 0, &u); +} + +static int +prism54_set_raw(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + u32 oid = dwrq->flags; + + return mgt_set_request(netdev_priv(ndev), oid, 0, extra); +} + +void +prism54_acl_init(struct islpci_acl *acl) +{ + mutex_init(&acl->lock); + INIT_LIST_HEAD(&acl->mac_list); + acl->size = 0; + acl->policy = MAC_POLICY_OPEN; +} + +static void +prism54_clear_mac(struct islpci_acl *acl) +{ + struct list_head *ptr, *next; + struct mac_entry *entry; + + mutex_lock(&acl->lock); + + if (acl->size == 0) { + mutex_unlock(&acl->lock); + return; + } + + for (ptr = acl->mac_list.next, next = ptr->next; + ptr != &acl->mac_list; ptr = next, next = ptr->next) { + entry = list_entry(ptr, struct mac_entry, _list); + list_del(ptr); + kfree(entry); + } + acl->size = 0; + mutex_unlock(&acl->lock); +} + +void +prism54_acl_clean(struct islpci_acl *acl) +{ + prism54_clear_mac(acl); +} + +static int +prism54_add_mac(struct net_device *ndev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct islpci_acl *acl = &priv->acl; + struct mac_entry *entry; + struct sockaddr *addr = (struct sockaddr *) extra; + + if (addr->sa_family != ARPHRD_ETHER) + return -EOPNOTSUPP; + + entry = kmalloc(sizeof (struct mac_entry), GFP_KERNEL); + if (entry == NULL) + return -ENOMEM; + + memcpy(entry->addr, addr->sa_data, ETH_ALEN); + + if (mutex_lock_interruptible(&acl->lock)) { + kfree(entry); + return -ERESTARTSYS; + } + list_add_tail(&entry->_list, &acl->mac_list); + acl->size++; + mutex_unlock(&acl->lock); + + return 0; +} + +static int +prism54_del_mac(struct net_device *ndev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct islpci_acl *acl = &priv->acl; + struct mac_entry *entry; + struct sockaddr *addr = (struct sockaddr *) extra; + + if (addr->sa_family != ARPHRD_ETHER) + return -EOPNOTSUPP; + + if (mutex_lock_interruptible(&acl->lock)) + return -ERESTARTSYS; + list_for_each_entry(entry, &acl->mac_list, _list) { + if (ether_addr_equal(entry->addr, addr->sa_data)) { + list_del(&entry->_list); + acl->size--; + kfree(entry); + mutex_unlock(&acl->lock); + return 0; + } + } + mutex_unlock(&acl->lock); + return -EINVAL; +} + +static int +prism54_get_mac(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct islpci_acl *acl = &priv->acl; + struct mac_entry *entry; + struct sockaddr *dst = (struct sockaddr *) extra; + + dwrq->length = 0; + + if (mutex_lock_interruptible(&acl->lock)) + return -ERESTARTSYS; + + list_for_each_entry(entry, &acl->mac_list, _list) { + memcpy(dst->sa_data, entry->addr, ETH_ALEN); + dst->sa_family = ARPHRD_ETHER; + dwrq->length++; + dst++; + } + mutex_unlock(&acl->lock); + return 0; +} + +/* Setting policy also clears the MAC acl, even if we don't change the default + * policy + */ + +static int +prism54_set_policy(struct net_device *ndev, struct iw_request_info *info, + __u32 * uwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct islpci_acl *acl = &priv->acl; + u32 mlmeautolevel; + + prism54_clear_mac(acl); + + if ((*uwrq < MAC_POLICY_OPEN) || (*uwrq > MAC_POLICY_REJECT)) + return -EINVAL; + + down_write(&priv->mib_sem); + + acl->policy = *uwrq; + + /* the ACL code needs an intermediate mlmeautolevel */ + if ((priv->iw_mode == IW_MODE_MASTER) && + (acl->policy != MAC_POLICY_OPEN)) + mlmeautolevel = DOT11_MLME_INTERMEDIATE; + else + mlmeautolevel = CARD_DEFAULT_MLME_MODE; + if (priv->wpa) + mlmeautolevel = DOT11_MLME_EXTENDED; + mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel); + /* restart the card with our new policy */ + if (mgt_commit(priv)) { + up_write(&priv->mib_sem); + return -EIO; + } + up_write(&priv->mib_sem); + + return 0; +} + +static int +prism54_get_policy(struct net_device *ndev, struct iw_request_info *info, + __u32 * uwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct islpci_acl *acl = &priv->acl; + + *uwrq = acl->policy; + + return 0; +} + +/* Return 1 only if client should be accepted. */ + +static int +prism54_mac_accept(struct islpci_acl *acl, char *mac) +{ + struct mac_entry *entry; + int res = 0; + + if (mutex_lock_interruptible(&acl->lock)) + return -ERESTARTSYS; + + if (acl->policy == MAC_POLICY_OPEN) { + mutex_unlock(&acl->lock); + return 1; + } + + list_for_each_entry(entry, &acl->mac_list, _list) { + if (memcmp(entry->addr, mac, ETH_ALEN) == 0) { + res = 1; + break; + } + } + res = (acl->policy == MAC_POLICY_ACCEPT) ? !res : res; + mutex_unlock(&acl->lock); + + return res; +} + +static int +prism54_kick_all(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + struct obj_mlme *mlme; + int rvalue; + + mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL); + if (mlme == NULL) + return -ENOMEM; + + /* Tell the card to kick every client */ + mlme->id = 0; + rvalue = + mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme); + kfree(mlme); + + return rvalue; +} + +static int +prism54_kick_mac(struct net_device *ndev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + struct obj_mlme *mlme; + struct sockaddr *addr = (struct sockaddr *) extra; + int rvalue; + + if (addr->sa_family != ARPHRD_ETHER) + return -EOPNOTSUPP; + + mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL); + if (mlme == NULL) + return -ENOMEM; + + /* Tell the card to only kick the corresponding bastard */ + memcpy(mlme->address, addr->sa_data, ETH_ALEN); + mlme->id = -1; + rvalue = + mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme); + + kfree(mlme); + + return rvalue; +} + +/* Translate a TRAP oid into a wireless event. Called in islpci_mgt_receive. */ + +static void +format_event(islpci_private *priv, char *dest, const char *str, + const struct obj_mlme *mlme, u16 *length, int error) +{ + int n = snprintf(dest, IW_CUSTOM_MAX, + "%s %s %pM %s (%2.2X)", + str, + ((priv->iw_mode == IW_MODE_MASTER) ? "from" : "to"), + mlme->address, + (error ? (mlme->code ? " : REJECTED " : " : ACCEPTED ") + : ""), mlme->code); + BUG_ON(n > IW_CUSTOM_MAX); + *length = n; +} + +static void +send_formatted_event(islpci_private *priv, const char *str, + const struct obj_mlme *mlme, int error) +{ + union iwreq_data wrqu; + char *memptr; + + memptr = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL); + if (!memptr) + return; + wrqu.data.pointer = memptr; + wrqu.data.length = 0; + format_event(priv, memptr, str, mlme, &wrqu.data.length, + error); + wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, memptr); + kfree(memptr); +} + +static void +send_simple_event(islpci_private *priv, const char *str) +{ + union iwreq_data wrqu; + char *memptr; + int n = strlen(str); + + memptr = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL); + if (!memptr) + return; + BUG_ON(n >= IW_CUSTOM_MAX); + wrqu.data.pointer = memptr; + wrqu.data.length = n; + strcpy(memptr, str); + wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, memptr); + kfree(memptr); +} + +static void +link_changed(struct net_device *ndev, u32 bitrate) +{ + islpci_private *priv = netdev_priv(ndev); + + if (bitrate) { + netif_carrier_on(ndev); + if (priv->iw_mode == IW_MODE_INFRA) { + union iwreq_data uwrq; + prism54_get_wap(ndev, NULL, (struct sockaddr *) &uwrq, + NULL); + wireless_send_event(ndev, SIOCGIWAP, &uwrq, NULL); + } else + send_simple_event(netdev_priv(ndev), + "Link established"); + } else { + netif_carrier_off(ndev); + send_simple_event(netdev_priv(ndev), "Link lost"); + } +} + +/* Beacon/ProbeResp payload header */ +struct ieee80211_beacon_phdr { + u8 timestamp[8]; + u16 beacon_int; + u16 capab_info; +} __packed; + +#define WLAN_EID_GENERIC 0xdd +static u8 wpa_oid[4] = { 0x00, 0x50, 0xf2, 1 }; + +static void +prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid, + u8 *wpa_ie, size_t wpa_ie_len) +{ + struct list_head *ptr; + struct islpci_bss_wpa_ie *bss = NULL; + + if (wpa_ie_len > MAX_WPA_IE_LEN) + wpa_ie_len = MAX_WPA_IE_LEN; + + mutex_lock(&priv->wpa_lock); + + /* try to use existing entry */ + list_for_each(ptr, &priv->bss_wpa_list) { + bss = list_entry(ptr, struct islpci_bss_wpa_ie, list); + if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) { + list_move(&bss->list, &priv->bss_wpa_list); + break; + } + bss = NULL; + } + + if (bss == NULL) { + /* add a new BSS entry; if max number of entries is already + * reached, replace the least recently updated */ + if (priv->num_bss_wpa >= MAX_BSS_WPA_IE_COUNT) { + bss = list_entry(priv->bss_wpa_list.prev, + struct islpci_bss_wpa_ie, list); + list_del(&bss->list); + } else { + bss = kzalloc(sizeof (*bss), GFP_ATOMIC); + if (bss != NULL) + priv->num_bss_wpa++; + } + if (bss != NULL) { + memcpy(bss->bssid, bssid, ETH_ALEN); + list_add(&bss->list, &priv->bss_wpa_list); + } + } + + if (bss != NULL) { + memcpy(bss->wpa_ie, wpa_ie, wpa_ie_len); + bss->wpa_ie_len = wpa_ie_len; + bss->last_update = jiffies; + } else { + printk(KERN_DEBUG "Failed to add BSS WPA entry for " + "%pM\n", bssid); + } + + /* expire old entries from WPA list */ + while (priv->num_bss_wpa > 0) { + bss = list_entry(priv->bss_wpa_list.prev, + struct islpci_bss_wpa_ie, list); + if (!time_after(jiffies, bss->last_update + 60 * HZ)) + break; + + list_del(&bss->list); + priv->num_bss_wpa--; + kfree(bss); + } + + mutex_unlock(&priv->wpa_lock); +} + +static size_t +prism54_wpa_bss_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie) +{ + struct list_head *ptr; + struct islpci_bss_wpa_ie *bss = NULL; + size_t len = 0; + + mutex_lock(&priv->wpa_lock); + + list_for_each(ptr, &priv->bss_wpa_list) { + bss = list_entry(ptr, struct islpci_bss_wpa_ie, list); + if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) + break; + bss = NULL; + } + if (bss) { + len = bss->wpa_ie_len; + memcpy(wpa_ie, bss->wpa_ie, len); + } + mutex_unlock(&priv->wpa_lock); + + return len; +} + +void +prism54_wpa_bss_ie_init(islpci_private *priv) +{ + INIT_LIST_HEAD(&priv->bss_wpa_list); + mutex_init(&priv->wpa_lock); +} + +void +prism54_wpa_bss_ie_clean(islpci_private *priv) +{ + struct islpci_bss_wpa_ie *bss, *n; + + list_for_each_entry_safe(bss, n, &priv->bss_wpa_list, list) { + kfree(bss); + } +} + +static void +prism54_process_bss_data(islpci_private *priv, u32 oid, u8 *addr, + u8 *payload, size_t len) +{ + struct ieee80211_beacon_phdr *hdr; + u8 *pos, *end; + + if (!priv->wpa) + return; + + hdr = (struct ieee80211_beacon_phdr *) payload; + pos = (u8 *) (hdr + 1); + end = payload + len; + while (pos < end) { + if (pos + 2 + pos[1] > end) { + printk(KERN_DEBUG "Parsing Beacon/ProbeResp failed " + "for %pM\n", addr); + return; + } + if (pos[0] == WLAN_EID_GENERIC && pos[1] >= 4 && + memcmp(pos + 2, wpa_oid, 4) == 0) { + prism54_wpa_bss_ie_add(priv, addr, pos, pos[1] + 2); + return; + } + pos += 2 + pos[1]; + } +} + +static void +handle_request(islpci_private *priv, struct obj_mlme *mlme, enum oid_num_t oid) +{ + if (((mlme->state == DOT11_STATE_AUTHING) || + (mlme->state == DOT11_STATE_ASSOCING)) + && mgt_mlme_answer(priv)) { + /* Someone is requesting auth and we must respond. Just send back + * the trap with error code set accordingly. + */ + mlme->code = prism54_mac_accept(&priv->acl, + mlme->address) ? 0 : 1; + mgt_set_request(priv, oid, 0, mlme); + } +} + +static int +prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid, + char *data) +{ + struct obj_mlme *mlme = (struct obj_mlme *) data; + struct obj_mlmeex *mlmeex = (struct obj_mlmeex *) data; + struct obj_mlmeex *confirm; + u8 wpa_ie[MAX_WPA_IE_LEN]; + int wpa_ie_len; + size_t len = 0; /* u16, better? */ + u8 *payload = NULL, *pos = NULL; + int ret; + + /* I think all trapable objects are listed here. + * Some oids have a EX version. The difference is that they are emitted + * in DOT11_MLME_EXTENDED mode (set with DOT11_OID_MLMEAUTOLEVEL) + * with more info. + * The few events already defined by the wireless tools are not really + * suited. We use the more flexible custom event facility. + */ + + if (oid >= DOT11_OID_BEACON) { + len = mlmeex->size; + payload = pos = mlmeex->data; + } + + /* I fear prism54_process_bss_data won't work with big endian data */ + if ((oid == DOT11_OID_BEACON) || (oid == DOT11_OID_PROBE)) + prism54_process_bss_data(priv, oid, mlmeex->address, + payload, len); + + mgt_le_to_cpu(isl_oid[oid].flags & OID_FLAG_TYPE, (void *) mlme); + + switch (oid) { + + case GEN_OID_LINKSTATE: + link_changed(priv->ndev, (u32) *data); + break; + + case DOT11_OID_MICFAILURE: + send_simple_event(priv, "Mic failure"); + break; + + case DOT11_OID_DEAUTHENTICATE: + send_formatted_event(priv, "DeAuthenticate request", mlme, 0); + break; + + case DOT11_OID_AUTHENTICATE: + handle_request(priv, mlme, oid); + send_formatted_event(priv, "Authenticate request", mlme, 1); + break; + + case DOT11_OID_DISASSOCIATE: + send_formatted_event(priv, "Disassociate request", mlme, 0); + break; + + case DOT11_OID_ASSOCIATE: + handle_request(priv, mlme, oid); + send_formatted_event(priv, "Associate request", mlme, 1); + break; + + case DOT11_OID_REASSOCIATE: + handle_request(priv, mlme, oid); + send_formatted_event(priv, "ReAssociate request", mlme, 1); + break; + + case DOT11_OID_BEACON: + send_formatted_event(priv, + "Received a beacon from an unknown AP", + mlme, 0); + break; + + case DOT11_OID_PROBE: + /* we received a probe from a client. */ + send_formatted_event(priv, "Received a probe from client", mlme, + 0); + break; + + /* Note : "mlme" is actually a "struct obj_mlmeex *" here, but this + * is backward compatible layout-wise with "struct obj_mlme". + */ + + case DOT11_OID_DEAUTHENTICATEEX: + send_formatted_event(priv, "DeAuthenticate request", mlme, 0); + break; + + case DOT11_OID_AUTHENTICATEEX: + handle_request(priv, mlme, oid); + send_formatted_event(priv, "Authenticate request (ex)", mlme, 1); + + if (priv->iw_mode != IW_MODE_MASTER + && mlmeex->state != DOT11_STATE_AUTHING) + break; + + confirm = kmalloc(sizeof(struct obj_mlmeex) + 6, GFP_ATOMIC); + + if (!confirm) + break; + + memcpy(&confirm->address, mlmeex->address, ETH_ALEN); + printk(KERN_DEBUG "Authenticate from: address:\t%pM\n", + mlmeex->address); + confirm->id = -1; /* or mlmeex->id ? */ + confirm->state = 0; /* not used */ + confirm->code = 0; + confirm->size = 6; + confirm->data[0] = 0x00; + confirm->data[1] = 0x00; + confirm->data[2] = 0x02; + confirm->data[3] = 0x00; + confirm->data[4] = 0x00; + confirm->data[5] = 0x00; + + ret = mgt_set_varlen(priv, DOT11_OID_ASSOCIATEEX, confirm, 6); + + kfree(confirm); + if (ret) + return ret; + break; + + case DOT11_OID_DISASSOCIATEEX: + send_formatted_event(priv, "Disassociate request (ex)", mlme, 0); + break; + + case DOT11_OID_ASSOCIATEEX: + handle_request(priv, mlme, oid); + send_formatted_event(priv, "Associate request (ex)", mlme, 1); + + if (priv->iw_mode != IW_MODE_MASTER + && mlmeex->state != DOT11_STATE_ASSOCING) + break; + + confirm = kmalloc(sizeof(struct obj_mlmeex), GFP_ATOMIC); + + if (!confirm) + break; + + memcpy(&confirm->address, mlmeex->address, ETH_ALEN); + + confirm->id = ((struct obj_mlmeex *)mlme)->id; + confirm->state = 0; /* not used */ + confirm->code = 0; + + wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie); + + if (!wpa_ie_len) { + printk(KERN_DEBUG "No WPA IE found from address:\t%pM\n", + mlmeex->address); + kfree(confirm); + break; + } + + confirm->size = wpa_ie_len; + memcpy(&confirm->data, wpa_ie, wpa_ie_len); + + mgt_set_varlen(priv, oid, confirm, wpa_ie_len); + + kfree(confirm); + + break; + + case DOT11_OID_REASSOCIATEEX: + handle_request(priv, mlme, oid); + send_formatted_event(priv, "Reassociate request (ex)", mlme, 1); + + if (priv->iw_mode != IW_MODE_MASTER + && mlmeex->state != DOT11_STATE_ASSOCING) + break; + + confirm = kmalloc(sizeof(struct obj_mlmeex), GFP_ATOMIC); + + if (!confirm) + break; + + memcpy(&confirm->address, mlmeex->address, ETH_ALEN); + + confirm->id = mlmeex->id; + confirm->state = 0; /* not used */ + confirm->code = 0; + + wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie); + + if (!wpa_ie_len) { + printk(KERN_DEBUG "No WPA IE found from address:\t%pM\n", + mlmeex->address); + kfree(confirm); + break; + } + + confirm->size = wpa_ie_len; + memcpy(&confirm->data, wpa_ie, wpa_ie_len); + + mgt_set_varlen(priv, oid, confirm, wpa_ie_len); + + kfree(confirm); + + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * Process a device trap. This is called via schedule_work(), outside of + * interrupt context, no locks held. + */ +void +prism54_process_trap(struct work_struct *work) +{ + struct islpci_mgmtframe *frame = + container_of(work, struct islpci_mgmtframe, ws); + struct net_device *ndev = frame->ndev; + enum oid_num_t n = mgt_oidtonum(frame->header->oid); + + if (n != OID_NUM_LAST) + prism54_process_trap_helper(netdev_priv(ndev), n, frame->data); + islpci_mgt_release(frame); +} + +int +prism54_set_mac_address(struct net_device *ndev, void *addr) +{ + islpci_private *priv = netdev_priv(ndev); + int ret; + + if (ndev->addr_len != 6) + return -EINVAL; + ret = mgt_set_request(priv, GEN_OID_MACADDRESS, 0, + &((struct sockaddr *) addr)->sa_data); + if (!ret) + memcpy(priv->ndev->dev_addr, + &((struct sockaddr *) addr)->sa_data, ETH_ALEN); + + return ret; +} + +#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12 + +static int +prism54_set_wpa(struct net_device *ndev, struct iw_request_info *info, + __u32 * uwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + u32 mlme, authen, dot1x, filter, wep; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return 0; + + wep = 1; /* For privacy invoked */ + filter = 1; /* Filter out all unencrypted frames */ + dot1x = 0x01; /* To enable eap filter */ + mlme = DOT11_MLME_EXTENDED; + authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */ + + down_write(&priv->mib_sem); + priv->wpa = *uwrq; + + switch (priv->wpa) { + default: + case 0: /* Clears/disables WPA and friends */ + wep = 0; + filter = 0; /* Do not filter un-encrypted data */ + dot1x = 0; + mlme = DOT11_MLME_AUTO; + printk("%s: Disabling WPA\n", ndev->name); + break; + case 2: + case 1: /* WPA */ + printk("%s: Enabling WPA\n", ndev->name); + break; + } + up_write(&priv->mib_sem); + + mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen); + mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &wep); + mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, &filter); + mgt_set_request(priv, DOT11_OID_DOT1XENABLE, 0, &dot1x); + mgt_set_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, &mlme); + + return 0; +} + +static int +prism54_get_wpa(struct net_device *ndev, struct iw_request_info *info, + __u32 * uwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + *uwrq = priv->wpa; + return 0; +} + +static int +prism54_set_prismhdr(struct net_device *ndev, struct iw_request_info *info, + __u32 * uwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + priv->monitor_type = + (*uwrq ? ARPHRD_IEEE80211_PRISM : ARPHRD_IEEE80211); + if (priv->iw_mode == IW_MODE_MONITOR) + priv->ndev->type = priv->monitor_type; + + return 0; +} + +static int +prism54_get_prismhdr(struct net_device *ndev, struct iw_request_info *info, + __u32 * uwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + *uwrq = (priv->monitor_type == ARPHRD_IEEE80211_PRISM); + return 0; +} + +static int +prism54_debug_oid(struct net_device *ndev, struct iw_request_info *info, + __u32 * uwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + + priv->priv_oid = *uwrq; + printk("%s: oid 0x%08X\n", ndev->name, *uwrq); + + return 0; +} + +static int +prism54_debug_get_oid(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct islpci_mgmtframe *response; + int ret = -EIO; + + printk("%s: get_oid 0x%08X\n", ndev->name, priv->priv_oid); + data->length = 0; + + if (islpci_get_state(priv) >= PRV_STATE_INIT) { + ret = + islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET, + priv->priv_oid, extra, 256, + &response); + printk("%s: ret: %i\n", ndev->name, ret); + if (ret || !response + || response->header->operation == PIMFOR_OP_ERROR) { + if (response) { + islpci_mgt_release(response); + } + printk("%s: EIO\n", ndev->name); + ret = -EIO; + } + if (!ret) { + data->length = response->header->length; + memcpy(extra, response->data, data->length); + islpci_mgt_release(response); + printk("%s: len: %i\n", ndev->name, data->length); + } + } + + return ret; +} + +static int +prism54_debug_set_oid(struct net_device *ndev, struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + struct islpci_mgmtframe *response; + int ret = 0, response_op = PIMFOR_OP_ERROR; + + printk("%s: set_oid 0x%08X\tlen: %d\n", ndev->name, priv->priv_oid, + data->length); + + if (islpci_get_state(priv) >= PRV_STATE_INIT) { + ret = + islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, + priv->priv_oid, extra, data->length, + &response); + printk("%s: ret: %i\n", ndev->name, ret); + if (ret || !response + || response->header->operation == PIMFOR_OP_ERROR) { + if (response) { + islpci_mgt_release(response); + } + printk("%s: EIO\n", ndev->name); + ret = -EIO; + } + if (!ret) { + response_op = response->header->operation; + printk("%s: response_op: %i\n", ndev->name, + response_op); + islpci_mgt_release(response); + } + } + + return (ret ? ret : -EINPROGRESS); +} + +static int +prism54_set_spy(struct net_device *ndev, + struct iw_request_info *info, + union iwreq_data *uwrq, char *extra) +{ + islpci_private *priv = netdev_priv(ndev); + u32 u; + enum oid_num_t oid = OID_INL_CONFIG; + + down_write(&priv->mib_sem); + mgt_get(priv, OID_INL_CONFIG, &u); + + if ((uwrq->data.length == 0) && (priv->spy_data.spy_number > 0)) + /* disable spy */ + u &= ~INL_CONFIG_RXANNEX; + else if ((uwrq->data.length > 0) && (priv->spy_data.spy_number == 0)) + /* enable spy */ + u |= INL_CONFIG_RXANNEX; + + mgt_set(priv, OID_INL_CONFIG, &u); + mgt_commit_list(priv, &oid, 1); + up_write(&priv->mib_sem); + + return iw_handler_set_spy(ndev, info, uwrq, extra); +} + +static const iw_handler prism54_handler[] = { + (iw_handler) prism54_commit, /* SIOCSIWCOMMIT */ + (iw_handler) prism54_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) prism54_set_freq, /* SIOCSIWFREQ */ + (iw_handler) prism54_get_freq, /* SIOCGIWFREQ */ + (iw_handler) prism54_set_mode, /* SIOCSIWMODE */ + (iw_handler) prism54_get_mode, /* SIOCGIWMODE */ + (iw_handler) prism54_set_sens, /* SIOCSIWSENS */ + (iw_handler) prism54_get_sens, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) prism54_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ + prism54_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ + (iw_handler) prism54_set_wap, /* SIOCSIWAP */ + (iw_handler) prism54_get_wap, /* SIOCGIWAP */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* SIOCGIWAPLIST deprecated */ + (iw_handler) prism54_set_scan, /* SIOCSIWSCAN */ + (iw_handler) prism54_get_scan, /* SIOCGIWSCAN */ + (iw_handler) prism54_set_essid, /* SIOCSIWESSID */ + (iw_handler) prism54_get_essid, /* SIOCGIWESSID */ + (iw_handler) prism54_set_nick, /* SIOCSIWNICKN */ + (iw_handler) prism54_get_nick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) prism54_set_rate, /* SIOCSIWRATE */ + (iw_handler) prism54_get_rate, /* SIOCGIWRATE */ + (iw_handler) prism54_set_rts, /* SIOCSIWRTS */ + (iw_handler) prism54_get_rts, /* SIOCGIWRTS */ + (iw_handler) prism54_set_frag, /* SIOCSIWFRAG */ + (iw_handler) prism54_get_frag, /* SIOCGIWFRAG */ + (iw_handler) prism54_set_txpower, /* SIOCSIWTXPOW */ + (iw_handler) prism54_get_txpower, /* SIOCGIWTXPOW */ + (iw_handler) prism54_set_retry, /* SIOCSIWRETRY */ + (iw_handler) prism54_get_retry, /* SIOCGIWRETRY */ + (iw_handler) prism54_set_encode, /* SIOCSIWENCODE */ + (iw_handler) prism54_get_encode, /* SIOCGIWENCODE */ + (iw_handler) NULL, /* SIOCSIWPOWER */ + (iw_handler) NULL, /* SIOCGIWPOWER */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + (iw_handler) prism54_set_genie, /* SIOCSIWGENIE */ + (iw_handler) prism54_get_genie, /* SIOCGIWGENIE */ + (iw_handler) prism54_set_auth, /* SIOCSIWAUTH */ + (iw_handler) prism54_get_auth, /* SIOCGIWAUTH */ + (iw_handler) prism54_set_encodeext, /* SIOCSIWENCODEEXT */ + (iw_handler) prism54_get_encodeext, /* SIOCGIWENCODEEXT */ + NULL, /* SIOCSIWPMKSA */ +}; + +/* The low order bit identify a SET (0) or a GET (1) ioctl. */ + +#define PRISM54_RESET SIOCIWFIRSTPRIV +#define PRISM54_GET_POLICY SIOCIWFIRSTPRIV+1 +#define PRISM54_SET_POLICY SIOCIWFIRSTPRIV+2 +#define PRISM54_GET_MAC SIOCIWFIRSTPRIV+3 +#define PRISM54_ADD_MAC SIOCIWFIRSTPRIV+4 + +#define PRISM54_DEL_MAC SIOCIWFIRSTPRIV+6 + +#define PRISM54_KICK_MAC SIOCIWFIRSTPRIV+8 + +#define PRISM54_KICK_ALL SIOCIWFIRSTPRIV+10 + +#define PRISM54_GET_WPA SIOCIWFIRSTPRIV+11 +#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12 + +#define PRISM54_DBG_OID SIOCIWFIRSTPRIV+14 +#define PRISM54_DBG_GET_OID SIOCIWFIRSTPRIV+15 +#define PRISM54_DBG_SET_OID SIOCIWFIRSTPRIV+16 + +#define PRISM54_GET_OID SIOCIWFIRSTPRIV+17 +#define PRISM54_SET_OID_U32 SIOCIWFIRSTPRIV+18 +#define PRISM54_SET_OID_STR SIOCIWFIRSTPRIV+20 +#define PRISM54_SET_OID_ADDR SIOCIWFIRSTPRIV+22 + +#define PRISM54_GET_PRISMHDR SIOCIWFIRSTPRIV+23 +#define PRISM54_SET_PRISMHDR SIOCIWFIRSTPRIV+24 + +#define IWPRIV_SET_U32(n,x) { n, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x } +#define IWPRIV_SET_SSID(n,x) { n, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x } +#define IWPRIV_SET_ADDR(n,x) { n, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x } +#define IWPRIV_GET(n,x) { n, 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | PRIV_STR_SIZE, "g_"x } + +#define IWPRIV_U32(n,x) IWPRIV_SET_U32(n,x), IWPRIV_GET(n,x) +#define IWPRIV_SSID(n,x) IWPRIV_SET_SSID(n,x), IWPRIV_GET(n,x) +#define IWPRIV_ADDR(n,x) IWPRIV_SET_ADDR(n,x), IWPRIV_GET(n,x) + +/* Note : limited to 128 private ioctls (wireless tools 26) */ + +static const struct iw_priv_args prism54_private_args[] = { +/*{ cmd, set_args, get_args, name } */ + {PRISM54_RESET, 0, 0, "reset"}, + {PRISM54_GET_PRISMHDR, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_prismhdr"}, + {PRISM54_SET_PRISMHDR, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, + "set_prismhdr"}, + {PRISM54_GET_POLICY, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getPolicy"}, + {PRISM54_SET_POLICY, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, + "setPolicy"}, + {PRISM54_GET_MAC, 0, IW_PRIV_TYPE_ADDR | 64, "getMac"}, + {PRISM54_ADD_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, + "addMac"}, + {PRISM54_DEL_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, + "delMac"}, + {PRISM54_KICK_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, + "kickMac"}, + {PRISM54_KICK_ALL, 0, 0, "kickAll"}, + {PRISM54_GET_WPA, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_wpa"}, + {PRISM54_SET_WPA, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, + "set_wpa"}, + {PRISM54_DBG_OID, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, + "dbg_oid"}, + {PRISM54_DBG_GET_OID, 0, IW_PRIV_TYPE_BYTE | 256, "dbg_get_oid"}, + {PRISM54_DBG_SET_OID, IW_PRIV_TYPE_BYTE | 256, 0, "dbg_set_oid"}, + /* --- sub-ioctls handlers --- */ + {PRISM54_GET_OID, + 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | PRIV_STR_SIZE, ""}, + {PRISM54_SET_OID_U32, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, ""}, + {PRISM54_SET_OID_STR, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, ""}, + {PRISM54_SET_OID_ADDR, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, ""}, + /* --- sub-ioctls definitions --- */ + IWPRIV_ADDR(GEN_OID_MACADDRESS, "addr"), + IWPRIV_GET(GEN_OID_LINKSTATE, "linkstate"), + IWPRIV_U32(DOT11_OID_BSSTYPE, "bsstype"), + IWPRIV_ADDR(DOT11_OID_BSSID, "bssid"), + IWPRIV_U32(DOT11_OID_STATE, "state"), + IWPRIV_U32(DOT11_OID_AID, "aid"), + + IWPRIV_SSID(DOT11_OID_SSIDOVERRIDE, "ssidoverride"), + + IWPRIV_U32(DOT11_OID_MEDIUMLIMIT, "medlimit"), + IWPRIV_U32(DOT11_OID_BEACONPERIOD, "beacon"), + IWPRIV_U32(DOT11_OID_DTIMPERIOD, "dtimperiod"), + + IWPRIV_U32(DOT11_OID_AUTHENABLE, "authenable"), + IWPRIV_U32(DOT11_OID_PRIVACYINVOKED, "privinvok"), + IWPRIV_U32(DOT11_OID_EXUNENCRYPTED, "exunencrypt"), + + IWPRIV_U32(DOT11_OID_REKEYTHRESHOLD, "rekeythresh"), + + IWPRIV_U32(DOT11_OID_MAXTXLIFETIME, "maxtxlife"), + IWPRIV_U32(DOT11_OID_MAXRXLIFETIME, "maxrxlife"), + IWPRIV_U32(DOT11_OID_ALOFT_FIXEDRATE, "fixedrate"), + IWPRIV_U32(DOT11_OID_MAXFRAMEBURST, "frameburst"), + IWPRIV_U32(DOT11_OID_PSM, "psm"), + + IWPRIV_U32(DOT11_OID_BRIDGELOCAL, "bridge"), + IWPRIV_U32(DOT11_OID_CLIENTS, "clients"), + IWPRIV_U32(DOT11_OID_CLIENTSASSOCIATED, "clientassoc"), + IWPRIV_U32(DOT11_OID_DOT1XENABLE, "dot1xenable"), + IWPRIV_U32(DOT11_OID_ANTENNARX, "rxant"), + IWPRIV_U32(DOT11_OID_ANTENNATX, "txant"), + IWPRIV_U32(DOT11_OID_ANTENNADIVERSITY, "antdivers"), + IWPRIV_U32(DOT11_OID_EDTHRESHOLD, "edthresh"), + IWPRIV_U32(DOT11_OID_PREAMBLESETTINGS, "preamble"), + IWPRIV_GET(DOT11_OID_RATES, "rates"), + IWPRIV_U32(DOT11_OID_OUTPUTPOWER, ".11outpower"), + IWPRIV_GET(DOT11_OID_SUPPORTEDRATES, "supprates"), + IWPRIV_GET(DOT11_OID_SUPPORTEDFREQUENCIES, "suppfreq"), + + IWPRIV_U32(DOT11_OID_NOISEFLOOR, "noisefloor"), + IWPRIV_GET(DOT11_OID_FREQUENCYACTIVITY, "freqactivity"), + IWPRIV_U32(DOT11_OID_NONERPPROTECTION, "nonerpprotec"), + IWPRIV_U32(DOT11_OID_PROFILES, "profile"), + IWPRIV_GET(DOT11_OID_EXTENDEDRATES, "extrates"), + IWPRIV_U32(DOT11_OID_MLMEAUTOLEVEL, "mlmelevel"), + + IWPRIV_GET(DOT11_OID_BSSS, "bsss"), + IWPRIV_GET(DOT11_OID_BSSLIST, "bsslist"), + IWPRIV_U32(OID_INL_MODE, "mode"), + IWPRIV_U32(OID_INL_CONFIG, "config"), + IWPRIV_U32(OID_INL_DOT11D_CONFORMANCE, ".11dconform"), + IWPRIV_GET(OID_INL_PHYCAPABILITIES, "phycapa"), + IWPRIV_U32(OID_INL_OUTPUTPOWER, "outpower"), +}; + +static const iw_handler prism54_private_handler[] = { + (iw_handler) prism54_reset, + (iw_handler) prism54_get_policy, + (iw_handler) prism54_set_policy, + (iw_handler) prism54_get_mac, + (iw_handler) prism54_add_mac, + (iw_handler) NULL, + (iw_handler) prism54_del_mac, + (iw_handler) NULL, + (iw_handler) prism54_kick_mac, + (iw_handler) NULL, + (iw_handler) prism54_kick_all, + (iw_handler) prism54_get_wpa, + (iw_handler) prism54_set_wpa, + (iw_handler) NULL, + (iw_handler) prism54_debug_oid, + (iw_handler) prism54_debug_get_oid, + (iw_handler) prism54_debug_set_oid, + (iw_handler) prism54_get_oid, + (iw_handler) prism54_set_u32, + (iw_handler) NULL, + (iw_handler) prism54_set_raw, + (iw_handler) NULL, + (iw_handler) prism54_set_raw, + (iw_handler) prism54_get_prismhdr, + (iw_handler) prism54_set_prismhdr, +}; + +const struct iw_handler_def prism54_handler_def = { + .num_standard = ARRAY_SIZE(prism54_handler), + .num_private = ARRAY_SIZE(prism54_private_handler), + .num_private_args = ARRAY_SIZE(prism54_private_args), + .standard = (iw_handler *) prism54_handler, + .private = (iw_handler *) prism54_private_handler, + .private_args = (struct iw_priv_args *) prism54_private_args, + .get_wireless_stats = prism54_get_wireless_stats, +}; diff --git a/drivers/net/wireless/intersil/prism54/isl_ioctl.h b/drivers/net/wireless/intersil/prism54/isl_ioctl.h new file mode 100644 index 000000000000..842a2549facc --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/isl_ioctl.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2002 Intersil Americas Inc. + * (C) 2003 Aurelien Alleaume + * (C) 2003 Luis R. Rodriguez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#ifndef _ISL_IOCTL_H +#define _ISL_IOCTL_H + +#include "islpci_mgt.h" +#include "islpci_dev.h" + +#include /* New driver API */ + +#define SUPPORTED_WIRELESS_EXT 19 + +void prism54_mib_init(islpci_private *); + +struct iw_statistics *prism54_get_wireless_stats(struct net_device *); +void prism54_update_stats(struct work_struct *); + +void prism54_acl_init(struct islpci_acl *); +void prism54_acl_clean(struct islpci_acl *); + +void prism54_process_trap(struct work_struct *); + +void prism54_wpa_bss_ie_init(islpci_private *priv); +void prism54_wpa_bss_ie_clean(islpci_private *priv); + +int prism54_set_mac_address(struct net_device *, void *); + +extern const struct iw_handler_def prism54_handler_def; + +#endif /* _ISL_IOCTL_H */ diff --git a/drivers/net/wireless/intersil/prism54/isl_oid.h b/drivers/net/wireless/intersil/prism54/isl_oid.h new file mode 100644 index 000000000000..83fec557997e --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/isl_oid.h @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2003 Herbert Valerio Riedel + * Copyright (C) 2004 Luis R. Rodriguez + * Copyright (C) 2004 Aurelien Alleaume + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#if !defined(_ISL_OID_H) +#define _ISL_OID_H + +/* + * MIB related constant and structure definitions for communicating + * with the device firmware + */ + +struct obj_ssid { + u8 length; + char octets[33]; +} __packed; + +struct obj_key { + u8 type; /* dot11_priv_t */ + u8 length; + char key[32]; +} __packed; + +struct obj_mlme { + u8 address[6]; + u16 id; + u16 state; + u16 code; +} __packed; + +struct obj_mlmeex { + u8 address[6]; + u16 id; + u16 state; + u16 code; + u16 size; + u8 data[0]; +} __packed; + +struct obj_buffer { + u32 size; + u32 addr; /* 32bit bus address */ +} __packed; + +struct obj_bss { + u8 address[6]; + int:16; /* padding */ + + char state; + char reserved; + short age; + + char quality; + char rssi; + + struct obj_ssid ssid; + short channel; + char beacon_period; + char dtim_period; + short capinfo; + short rates; + short basic_rates; + int:16; /* padding */ +} __packed; + +struct obj_bsslist { + u32 nr; + struct obj_bss bsslist[0]; +} __packed; + +struct obj_frequencies { + u16 nr; + u16 mhz[0]; +} __packed; + +struct obj_attachment { + char type; + char reserved; + short id; + short size; + char data[0]; +} __packed; + +/* + * in case everything's ok, the inlined function below will be + * optimized away by the compiler... + */ +static inline void +__bug_on_wrong_struct_sizes(void) +{ + BUILD_BUG_ON(sizeof (struct obj_ssid) != 34); + BUILD_BUG_ON(sizeof (struct obj_key) != 34); + BUILD_BUG_ON(sizeof (struct obj_mlme) != 12); + BUILD_BUG_ON(sizeof (struct obj_mlmeex) != 14); + BUILD_BUG_ON(sizeof (struct obj_buffer) != 8); + BUILD_BUG_ON(sizeof (struct obj_bss) != 60); + BUILD_BUG_ON(sizeof (struct obj_bsslist) != 4); + BUILD_BUG_ON(sizeof (struct obj_frequencies) != 2); +} + +enum dot11_state_t { + DOT11_STATE_NONE = 0, + DOT11_STATE_AUTHING = 1, + DOT11_STATE_AUTH = 2, + DOT11_STATE_ASSOCING = 3, + + DOT11_STATE_ASSOC = 5, + DOT11_STATE_IBSS = 6, + DOT11_STATE_WDS = 7 +}; + +enum dot11_bsstype_t { + DOT11_BSSTYPE_NONE = 0, + DOT11_BSSTYPE_INFRA = 1, + DOT11_BSSTYPE_IBSS = 2, + DOT11_BSSTYPE_ANY = 3 +}; + +enum dot11_auth_t { + DOT11_AUTH_NONE = 0, + DOT11_AUTH_OS = 1, + DOT11_AUTH_SK = 2, + DOT11_AUTH_BOTH = 3 +}; + +enum dot11_mlme_t { + DOT11_MLME_AUTO = 0, + DOT11_MLME_INTERMEDIATE = 1, + DOT11_MLME_EXTENDED = 2 +}; + +enum dot11_priv_t { + DOT11_PRIV_WEP = 0, + DOT11_PRIV_TKIP = 1 +}; + +/* Prism "Nitro" / Frameburst / "Packet Frame Grouping" + * Value is in microseconds. Represents the # microseconds + * the firmware will take to group frames before sending out then out + * together with a CSMA contention. Without this all frames are + * sent with a CSMA contention. + * Bibliography: + * http://www.hpl.hp.com/personal/Jean_Tourrilhes/Papers/Packet.Frame.Grouping.html + */ +enum dot11_maxframeburst_t { + /* Values for DOT11_OID_MAXFRAMEBURST */ + DOT11_MAXFRAMEBURST_OFF = 0, /* Card firmware default */ + DOT11_MAXFRAMEBURST_MIXED_SAFE = 650, /* 802.11 a,b,g safe */ + DOT11_MAXFRAMEBURST_IDEAL = 1300, /* Theoretical ideal level */ + DOT11_MAXFRAMEBURST_MAX = 5000, /* Use this as max, + * Note: firmware allows for greater values. This is a + * recommended max. I'll update this as I find + * out what the real MAX is. Also note that you don't necessarily + * get better results with a greater value here. + */ +}; + +/* Support for 802.11 long and short frame preambles. + * Long preamble uses 128-bit sync field, 8-bit CRC + * Short preamble uses 56-bit sync field, 16-bit CRC + * + * 802.11a -- not sure, both optionally ? + * 802.11b supports long and optionally short + * 802.11g supports both */ +enum dot11_preamblesettings_t { + DOT11_PREAMBLESETTING_LONG = 0, + /* Allows *only* long 802.11 preambles */ + DOT11_PREAMBLESETTING_SHORT = 1, + /* Allows *only* short 802.11 preambles */ + DOT11_PREAMBLESETTING_DYNAMIC = 2 + /* AutomatiGically set */ +}; + +/* Support for 802.11 slot timing (time between packets). + * + * Long uses 802.11a slot timing (9 usec ?) + * Short uses 802.11b slot timing (20 use ?) */ +enum dot11_slotsettings_t { + DOT11_SLOTSETTINGS_LONG = 0, + /* Allows *only* long 802.11b slot timing */ + DOT11_SLOTSETTINGS_SHORT = 1, + /* Allows *only* long 802.11a slot timing */ + DOT11_SLOTSETTINGS_DYNAMIC = 2 + /* AutomatiGically set */ +}; + +/* All you need to know, ERP is "Extended Rate PHY". + * An Extended Rate PHY (ERP) STA or AP shall support three different + * preamble and header formats: + * Long preamble (refer to above) + * Short preamble (refer to above) + * OFDM preamble ( ? ) + * + * I'm assuming here Protection tells the AP + * to be careful, a STA which cannot handle the long pre-amble + * has joined. + */ +enum do11_nonerpstatus_t { + DOT11_ERPSTAT_NONEPRESENT = 0, + DOT11_ERPSTAT_USEPROTECTION = 1 +}; + +/* (ERP is "Extended Rate PHY") Way to read NONERP is NON-ERP-* + * The key here is DOT11 NON ERP NEVER protects against + * NON ERP STA's. You *don't* want this unless + * you know what you are doing. It means you will only + * get Extended Rate capabilities */ +enum dot11_nonerpprotection_t { + DOT11_NONERP_NEVER = 0, + DOT11_NONERP_ALWAYS = 1, + DOT11_NONERP_DYNAMIC = 2 +}; + +/* Preset OID configuration for 802.11 modes + * Note: DOT11_OID_CW[MIN|MAX] hold the values of the + * DCS MIN|MAX backoff used */ +enum dot11_profile_t { /* And set/allowed values */ + /* Allowed values for DOT11_OID_PROFILES */ + DOT11_PROFILE_B_ONLY = 0, + /* DOT11_OID_RATES: 1, 2, 5.5, 11Mbps + * DOT11_OID_PREAMBLESETTINGS: DOT11_PREAMBLESETTING_DYNAMIC + * DOT11_OID_CWMIN: 31 + * DOT11_OID_NONEPROTECTION: DOT11_NOERP_DYNAMIC + * DOT11_OID_SLOTSETTINGS: DOT11_SLOTSETTINGS_LONG + */ + DOT11_PROFILE_MIXED_G_WIFI = 1, + /* DOT11_OID_RATES: 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54Mbs + * DOT11_OID_PREAMBLESETTINGS: DOT11_PREAMBLESETTING_DYNAMIC + * DOT11_OID_CWMIN: 15 + * DOT11_OID_NONEPROTECTION: DOT11_NOERP_DYNAMIC + * DOT11_OID_SLOTSETTINGS: DOT11_SLOTSETTINGS_DYNAMIC + */ + DOT11_PROFILE_MIXED_LONG = 2, /* "Long range" */ + /* Same as Profile MIXED_G_WIFI */ + DOT11_PROFILE_G_ONLY = 3, + /* Same as Profile MIXED_G_WIFI */ + DOT11_PROFILE_TEST = 4, + /* Same as Profile MIXED_G_WIFI except: + * DOT11_OID_PREAMBLESETTINGS: DOT11_PREAMBLESETTING_SHORT + * DOT11_OID_NONEPROTECTION: DOT11_NOERP_NEVER + * DOT11_OID_SLOTSETTINGS: DOT11_SLOTSETTINGS_SHORT + */ + DOT11_PROFILE_B_WIFI = 5, + /* Same as Profile B_ONLY */ + DOT11_PROFILE_A_ONLY = 6, + /* Same as Profile MIXED_G_WIFI except: + * DOT11_OID_RATES: 6, 9, 12, 18, 24, 36, 48, 54Mbs + */ + DOT11_PROFILE_MIXED_SHORT = 7 + /* Same as MIXED_G_WIFI */ +}; + + +/* The dot11d conformance level configures the 802.11d conformance levels. + * The following conformance levels exist:*/ +enum oid_inl_conformance_t { + OID_INL_CONFORMANCE_NONE = 0, /* Perform active scanning */ + OID_INL_CONFORMANCE_STRICT = 1, /* Strictly adhere to 802.11d */ + OID_INL_CONFORMANCE_FLEXIBLE = 2, /* Use passed 802.11d info to + * determine channel AND/OR just make assumption that active + * channels are valid channels */ +}; + +enum oid_inl_mode_t { + INL_MODE_NONE = -1, + INL_MODE_PROMISCUOUS = 0, + INL_MODE_CLIENT = 1, + INL_MODE_AP = 2, + INL_MODE_SNIFFER = 3 +}; + +enum oid_inl_config_t { + INL_CONFIG_NOTHING = 0x00, + INL_CONFIG_MANUALRUN = 0x01, + INL_CONFIG_FRAMETRAP = 0x02, + INL_CONFIG_RXANNEX = 0x04, + INL_CONFIG_TXANNEX = 0x08, + INL_CONFIG_WDS = 0x10 +}; + +enum oid_inl_phycap_t { + INL_PHYCAP_2400MHZ = 1, + INL_PHYCAP_5000MHZ = 2, + INL_PHYCAP_FAA = 0x80000000, /* Means card supports the FAA switch */ +}; + + +enum oid_num_t { + GEN_OID_MACADDRESS = 0, + GEN_OID_LINKSTATE, + GEN_OID_WATCHDOG, + GEN_OID_MIBOP, + GEN_OID_OPTIONS, + GEN_OID_LEDCONFIG, + + /* 802.11 */ + DOT11_OID_BSSTYPE, + DOT11_OID_BSSID, + DOT11_OID_SSID, + DOT11_OID_STATE, + DOT11_OID_AID, + DOT11_OID_COUNTRYSTRING, + DOT11_OID_SSIDOVERRIDE, + + DOT11_OID_MEDIUMLIMIT, + DOT11_OID_BEACONPERIOD, + DOT11_OID_DTIMPERIOD, + DOT11_OID_ATIMWINDOW, + DOT11_OID_LISTENINTERVAL, + DOT11_OID_CFPPERIOD, + DOT11_OID_CFPDURATION, + + DOT11_OID_AUTHENABLE, + DOT11_OID_PRIVACYINVOKED, + DOT11_OID_EXUNENCRYPTED, + DOT11_OID_DEFKEYID, + DOT11_OID_DEFKEYX, /* DOT11_OID_DEFKEY1,...DOT11_OID_DEFKEY4 */ + DOT11_OID_STAKEY, + DOT11_OID_REKEYTHRESHOLD, + DOT11_OID_STASC, + + DOT11_OID_PRIVTXREJECTED, + DOT11_OID_PRIVRXPLAIN, + DOT11_OID_PRIVRXFAILED, + DOT11_OID_PRIVRXNOKEY, + + DOT11_OID_RTSTHRESH, + DOT11_OID_FRAGTHRESH, + DOT11_OID_SHORTRETRIES, + DOT11_OID_LONGRETRIES, + DOT11_OID_MAXTXLIFETIME, + DOT11_OID_MAXRXLIFETIME, + DOT11_OID_AUTHRESPTIMEOUT, + DOT11_OID_ASSOCRESPTIMEOUT, + + DOT11_OID_ALOFT_TABLE, + DOT11_OID_ALOFT_CTRL_TABLE, + DOT11_OID_ALOFT_RETREAT, + DOT11_OID_ALOFT_PROGRESS, + DOT11_OID_ALOFT_FIXEDRATE, + DOT11_OID_ALOFT_RSSIGRAPH, + DOT11_OID_ALOFT_CONFIG, + + DOT11_OID_VDCFX, + DOT11_OID_MAXFRAMEBURST, + + DOT11_OID_PSM, + DOT11_OID_CAMTIMEOUT, + DOT11_OID_RECEIVEDTIMS, + DOT11_OID_ROAMPREFERENCE, + + DOT11_OID_BRIDGELOCAL, + DOT11_OID_CLIENTS, + DOT11_OID_CLIENTSASSOCIATED, + DOT11_OID_CLIENTX, /* DOT11_OID_CLIENTX,...DOT11_OID_CLIENT2007 */ + + DOT11_OID_CLIENTFIND, + DOT11_OID_WDSLINKADD, + DOT11_OID_WDSLINKREMOVE, + DOT11_OID_EAPAUTHSTA, + DOT11_OID_EAPUNAUTHSTA, + DOT11_OID_DOT1XENABLE, + DOT11_OID_MICFAILURE, + DOT11_OID_REKEYINDICATE, + + DOT11_OID_MPDUTXSUCCESSFUL, + DOT11_OID_MPDUTXONERETRY, + DOT11_OID_MPDUTXMULTIPLERETRIES, + DOT11_OID_MPDUTXFAILED, + DOT11_OID_MPDURXSUCCESSFUL, + DOT11_OID_MPDURXDUPS, + DOT11_OID_RTSSUCCESSFUL, + DOT11_OID_RTSFAILED, + DOT11_OID_ACKFAILED, + DOT11_OID_FRAMERECEIVES, + DOT11_OID_FRAMEERRORS, + DOT11_OID_FRAMEABORTS, + DOT11_OID_FRAMEABORTSPHY, + + DOT11_OID_SLOTTIME, + DOT11_OID_CWMIN, /* MIN DCS backoff */ + DOT11_OID_CWMAX, /* MAX DCS backoff */ + DOT11_OID_ACKWINDOW, + DOT11_OID_ANTENNARX, + DOT11_OID_ANTENNATX, + DOT11_OID_ANTENNADIVERSITY, + DOT11_OID_CHANNEL, + DOT11_OID_EDTHRESHOLD, + DOT11_OID_PREAMBLESETTINGS, + DOT11_OID_RATES, + DOT11_OID_CCAMODESUPPORTED, + DOT11_OID_CCAMODE, + DOT11_OID_RSSIVECTOR, + DOT11_OID_OUTPUTPOWERTABLE, + DOT11_OID_OUTPUTPOWER, + DOT11_OID_SUPPORTEDRATES, + DOT11_OID_FREQUENCY, + DOT11_OID_SUPPORTEDFREQUENCIES, + DOT11_OID_NOISEFLOOR, + DOT11_OID_FREQUENCYACTIVITY, + DOT11_OID_IQCALIBRATIONTABLE, + DOT11_OID_NONERPPROTECTION, + DOT11_OID_SLOTSETTINGS, + DOT11_OID_NONERPTIMEOUT, + DOT11_OID_PROFILES, + DOT11_OID_EXTENDEDRATES, + + DOT11_OID_DEAUTHENTICATE, + DOT11_OID_AUTHENTICATE, + DOT11_OID_DISASSOCIATE, + DOT11_OID_ASSOCIATE, + DOT11_OID_SCAN, + DOT11_OID_BEACON, + DOT11_OID_PROBE, + DOT11_OID_DEAUTHENTICATEEX, + DOT11_OID_AUTHENTICATEEX, + DOT11_OID_DISASSOCIATEEX, + DOT11_OID_ASSOCIATEEX, + DOT11_OID_REASSOCIATE, + DOT11_OID_REASSOCIATEEX, + + DOT11_OID_NONERPSTATUS, + + DOT11_OID_STATIMEOUT, + DOT11_OID_MLMEAUTOLEVEL, + DOT11_OID_BSSTIMEOUT, + DOT11_OID_ATTACHMENT, + DOT11_OID_PSMBUFFER, + + DOT11_OID_BSSS, + DOT11_OID_BSSX, /*DOT11_OID_BSS1,...,DOT11_OID_BSS64 */ + DOT11_OID_BSSFIND, + DOT11_OID_BSSLIST, + + OID_INL_TUNNEL, + OID_INL_MEMADDR, + OID_INL_MEMORY, + OID_INL_MODE, + OID_INL_COMPONENT_NR, + OID_INL_VERSION, + OID_INL_INTERFACE_ID, + OID_INL_COMPONENT_ID, + OID_INL_CONFIG, + OID_INL_DOT11D_CONFORMANCE, + OID_INL_PHYCAPABILITIES, + OID_INL_OUTPUTPOWER, + + OID_NUM_LAST +}; + +#define OID_FLAG_CACHED 0x80 +#define OID_FLAG_TYPE 0x7f + +#define OID_TYPE_U32 0x01 +#define OID_TYPE_SSID 0x02 +#define OID_TYPE_KEY 0x03 +#define OID_TYPE_BUFFER 0x04 +#define OID_TYPE_BSS 0x05 +#define OID_TYPE_BSSLIST 0x06 +#define OID_TYPE_FREQUENCIES 0x07 +#define OID_TYPE_MLME 0x08 +#define OID_TYPE_MLMEEX 0x09 +#define OID_TYPE_ADDR 0x0A +#define OID_TYPE_RAW 0x0B +#define OID_TYPE_ATTACH 0x0C + +/* OID_TYPE_MLMEEX is special because of a variable size field when sending. + * Not yet implemented (not used in driver anyway). + */ + +struct oid_t { + enum oid_num_t oid; + short range; /* to define a range of oid */ + short size; /* max size of the associated data */ + char flags; +}; + +union oid_res_t { + void *ptr; + u32 u; +}; + +#define IWMAX_BITRATES 20 +#define IWMAX_BSS 24 +#define IWMAX_FREQ 30 +#define PRIV_STR_SIZE 1024 + +#endif /* !defined(_ISL_OID_H) */ +/* EOF */ diff --git a/drivers/net/wireless/intersil/prism54/islpci_dev.c b/drivers/net/wireless/intersil/prism54/islpci_dev.c new file mode 100644 index 000000000000..931cf440ff18 --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/islpci_dev.c @@ -0,0 +1,963 @@ +/* + * Copyright (C) 2002 Intersil Americas Inc. + * Copyright (C) 2003 Herbert Valerio Riedel + * Copyright (C) 2003 Luis R. Rodriguez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "prismcompat.h" +#include "isl_38xx.h" +#include "isl_ioctl.h" +#include "islpci_dev.h" +#include "islpci_mgt.h" +#include "islpci_eth.h" +#include "oid_mgt.h" + +#define ISL3877_IMAGE_FILE "isl3877" +#define ISL3886_IMAGE_FILE "isl3886" +#define ISL3890_IMAGE_FILE "isl3890" +MODULE_FIRMWARE(ISL3877_IMAGE_FILE); +MODULE_FIRMWARE(ISL3886_IMAGE_FILE); +MODULE_FIRMWARE(ISL3890_IMAGE_FILE); + +static int prism54_bring_down(islpci_private *); +static int islpci_alloc_memory(islpci_private *); + +/* Temporary dummy MAC address to use until firmware is loaded. + * The idea there is that some tools (such as nameif) may query + * the MAC address before the netdev is 'open'. By using a valid + * OUI prefix, they can process the netdev properly. + * Of course, this is not the final/real MAC address. It doesn't + * matter, as you are suppose to be able to change it anytime via + * ndev->set_mac_address. Jean II */ +static const unsigned char dummy_mac[6] = { 0x00, 0x30, 0xB4, 0x00, 0x00, 0x00 }; + +static int +isl_upload_firmware(islpci_private *priv) +{ + u32 reg, rc; + void __iomem *device_base = priv->device_base; + + /* clear the RAMBoot and the Reset bit */ + reg = readl(device_base + ISL38XX_CTRL_STAT_REG); + reg &= ~ISL38XX_CTRL_STAT_RESET; + reg &= ~ISL38XX_CTRL_STAT_RAMBOOT; + writel(reg, device_base + ISL38XX_CTRL_STAT_REG); + wmb(); + udelay(ISL38XX_WRITEIO_DELAY); + + /* set the Reset bit without reading the register ! */ + reg |= ISL38XX_CTRL_STAT_RESET; + writel(reg, device_base + ISL38XX_CTRL_STAT_REG); + wmb(); + udelay(ISL38XX_WRITEIO_DELAY); + + /* clear the Reset bit */ + reg &= ~ISL38XX_CTRL_STAT_RESET; + writel(reg, device_base + ISL38XX_CTRL_STAT_REG); + wmb(); + + /* wait a while for the device to reboot */ + mdelay(50); + + { + const struct firmware *fw_entry = NULL; + long fw_len; + const u32 *fw_ptr; + + rc = request_firmware(&fw_entry, priv->firmware, PRISM_FW_PDEV); + if (rc) { + printk(KERN_ERR + "%s: request_firmware() failed for '%s'\n", + "prism54", priv->firmware); + return rc; + } + /* prepare the Direct Memory Base register */ + reg = ISL38XX_DEV_FIRMWARE_ADDRES; + + fw_ptr = (u32 *) fw_entry->data; + fw_len = fw_entry->size; + + if (fw_len % 4) { + printk(KERN_ERR + "%s: firmware '%s' size is not multiple of 32bit, aborting!\n", + "prism54", priv->firmware); + release_firmware(fw_entry); + return -EILSEQ; /* Illegal byte sequence */; + } + + while (fw_len > 0) { + long _fw_len = + (fw_len > + ISL38XX_MEMORY_WINDOW_SIZE) ? + ISL38XX_MEMORY_WINDOW_SIZE : fw_len; + u32 __iomem *dev_fw_ptr = device_base + ISL38XX_DIRECT_MEM_WIN; + + /* set the card's base address for writing the data */ + isl38xx_w32_flush(device_base, reg, + ISL38XX_DIR_MEM_BASE_REG); + wmb(); /* be paranoid */ + + /* increment the write address for next iteration */ + reg += _fw_len; + fw_len -= _fw_len; + + /* write the data to the Direct Memory Window 32bit-wise */ + /* memcpy_toio() doesn't guarantee 32bit writes :-| */ + while (_fw_len > 0) { + /* use non-swapping writel() */ + __raw_writel(*fw_ptr, dev_fw_ptr); + fw_ptr++, dev_fw_ptr++; + _fw_len -= 4; + } + + /* flush PCI posting */ + (void) readl(device_base + ISL38XX_PCI_POSTING_FLUSH); + wmb(); /* be paranoid again */ + + BUG_ON(_fw_len != 0); + } + + BUG_ON(fw_len != 0); + + /* Firmware version is at offset 40 (also for "newmac") */ + printk(KERN_DEBUG "%s: firmware version: %.8s\n", + priv->ndev->name, fw_entry->data + 40); + + release_firmware(fw_entry); + } + + /* now reset the device + * clear the Reset & ClkRun bit, set the RAMBoot bit */ + reg = readl(device_base + ISL38XX_CTRL_STAT_REG); + reg &= ~ISL38XX_CTRL_STAT_CLKRUN; + reg &= ~ISL38XX_CTRL_STAT_RESET; + reg |= ISL38XX_CTRL_STAT_RAMBOOT; + isl38xx_w32_flush(device_base, reg, ISL38XX_CTRL_STAT_REG); + wmb(); + udelay(ISL38XX_WRITEIO_DELAY); + + /* set the reset bit latches the host override and RAMBoot bits + * into the device for operation when the reset bit is reset */ + reg |= ISL38XX_CTRL_STAT_RESET; + writel(reg, device_base + ISL38XX_CTRL_STAT_REG); + /* don't do flush PCI posting here! */ + wmb(); + udelay(ISL38XX_WRITEIO_DELAY); + + /* clear the reset bit should start the whole circus */ + reg &= ~ISL38XX_CTRL_STAT_RESET; + writel(reg, device_base + ISL38XX_CTRL_STAT_REG); + /* don't do flush PCI posting here! */ + wmb(); + udelay(ISL38XX_WRITEIO_DELAY); + + return 0; +} + +/****************************************************************************** + Device Interrupt Handler +******************************************************************************/ + +irqreturn_t +islpci_interrupt(int irq, void *config) +{ + u32 reg; + islpci_private *priv = config; + struct net_device *ndev = priv->ndev; + void __iomem *device = priv->device_base; + int powerstate = ISL38XX_PSM_POWERSAVE_STATE; + + /* lock the interrupt handler */ + spin_lock(&priv->slock); + + /* received an interrupt request on a shared IRQ line + * first check whether the device is in sleep mode */ + reg = readl(device + ISL38XX_CTRL_STAT_REG); + if (reg & ISL38XX_CTRL_STAT_SLEEPMODE) + /* device is in sleep mode, IRQ was generated by someone else */ + { +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n"); +#endif + spin_unlock(&priv->slock); + return IRQ_NONE; + } + + + /* check whether there is any source of interrupt on the device */ + reg = readl(device + ISL38XX_INT_IDENT_REG); + + /* also check the contents of the Interrupt Enable Register, because this + * will filter out interrupt sources from other devices on the same irq ! */ + reg &= readl(device + ISL38XX_INT_EN_REG); + reg &= ISL38XX_INT_SOURCES; + + if (reg != 0) { + if (islpci_get_state(priv) != PRV_STATE_SLEEP) + powerstate = ISL38XX_PSM_ACTIVE_STATE; + + /* reset the request bits in the Identification register */ + isl38xx_w32_flush(device, reg, ISL38XX_INT_ACK_REG); + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_FUNCTION_CALLS, + "IRQ: Identification register 0x%p 0x%x\n", device, reg); +#endif + + /* check for each bit in the register separately */ + if (reg & ISL38XX_INT_IDENT_UPDATE) { +#if VERBOSE > SHOW_ERROR_MESSAGES + /* Queue has been updated */ + DEBUG(SHOW_TRACING, "IRQ: Update flag\n"); + + DEBUG(SHOW_QUEUE_INDEXES, + "CB drv Qs: [%i][%i][%i][%i][%i][%i]\n", + le32_to_cpu(priv->control_block-> + driver_curr_frag[0]), + le32_to_cpu(priv->control_block-> + driver_curr_frag[1]), + le32_to_cpu(priv->control_block-> + driver_curr_frag[2]), + le32_to_cpu(priv->control_block-> + driver_curr_frag[3]), + le32_to_cpu(priv->control_block-> + driver_curr_frag[4]), + le32_to_cpu(priv->control_block-> + driver_curr_frag[5]) + ); + + DEBUG(SHOW_QUEUE_INDEXES, + "CB dev Qs: [%i][%i][%i][%i][%i][%i]\n", + le32_to_cpu(priv->control_block-> + device_curr_frag[0]), + le32_to_cpu(priv->control_block-> + device_curr_frag[1]), + le32_to_cpu(priv->control_block-> + device_curr_frag[2]), + le32_to_cpu(priv->control_block-> + device_curr_frag[3]), + le32_to_cpu(priv->control_block-> + device_curr_frag[4]), + le32_to_cpu(priv->control_block-> + device_curr_frag[5]) + ); +#endif + + /* cleanup the data low transmit queue */ + islpci_eth_cleanup_transmit(priv, priv->control_block); + + /* device is in active state, update the + * powerstate flag if necessary */ + powerstate = ISL38XX_PSM_ACTIVE_STATE; + + /* check all three queues in priority order + * call the PIMFOR receive function until the + * queue is empty */ + if (isl38xx_in_queue(priv->control_block, + ISL38XX_CB_RX_MGMTQ) != 0) { +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, + "Received frame in Management Queue\n"); +#endif + islpci_mgt_receive(ndev); + + islpci_mgt_cleanup_transmit(ndev); + + /* Refill slots in receive queue */ + islpci_mgmt_rx_fill(ndev); + + /* no need to trigger the device, next + islpci_mgt_transaction does it */ + } + + while (isl38xx_in_queue(priv->control_block, + ISL38XX_CB_RX_DATA_LQ) != 0) { +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, + "Received frame in Data Low Queue\n"); +#endif + islpci_eth_receive(priv); + } + + /* check whether the data transmit queues were full */ + if (priv->data_low_tx_full) { + /* check whether the transmit is not full anymore */ + if (ISL38XX_CB_TX_QSIZE - + isl38xx_in_queue(priv->control_block, + ISL38XX_CB_TX_DATA_LQ) >= + ISL38XX_MIN_QTHRESHOLD) { + /* nope, the driver is ready for more network frames */ + netif_wake_queue(priv->ndev); + + /* reset the full flag */ + priv->data_low_tx_full = 0; + } + } + } + + if (reg & ISL38XX_INT_IDENT_INIT) { + /* Device has been initialized */ +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, + "IRQ: Init flag, device initialized\n"); +#endif + wake_up(&priv->reset_done); + } + + if (reg & ISL38XX_INT_IDENT_SLEEP) { + /* Device intends to move to powersave state */ +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "IRQ: Sleep flag\n"); +#endif + isl38xx_handle_sleep_request(priv->control_block, + &powerstate, + priv->device_base); + } + + if (reg & ISL38XX_INT_IDENT_WAKEUP) { + /* Device has been woken up to active state */ +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "IRQ: Wakeup flag\n"); +#endif + + isl38xx_handle_wakeup(priv->control_block, + &powerstate, priv->device_base); + } + } else { +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n"); +#endif + spin_unlock(&priv->slock); + return IRQ_NONE; + } + + /* sleep -> ready */ + if (islpci_get_state(priv) == PRV_STATE_SLEEP + && powerstate == ISL38XX_PSM_ACTIVE_STATE) + islpci_set_state(priv, PRV_STATE_READY); + + /* !sleep -> sleep */ + if (islpci_get_state(priv) != PRV_STATE_SLEEP + && powerstate == ISL38XX_PSM_POWERSAVE_STATE) + islpci_set_state(priv, PRV_STATE_SLEEP); + + /* unlock the interrupt handler */ + spin_unlock(&priv->slock); + + return IRQ_HANDLED; +} + +/****************************************************************************** + Network Interface Control & Statistical functions +******************************************************************************/ +static int +islpci_open(struct net_device *ndev) +{ + u32 rc; + islpci_private *priv = netdev_priv(ndev); + + /* reset data structures, upload firmware and reset device */ + rc = islpci_reset(priv,1); + if (rc) { + prism54_bring_down(priv); + return rc; /* Returns informative message */ + } + + netif_start_queue(ndev); + + /* Turn off carrier if in STA or Ad-hoc mode. It will be turned on + * once the firmware receives a trap of being associated + * (GEN_OID_LINKSTATE). In other modes (AP or WDS or monitor) we + * should just leave the carrier on as its expected the firmware + * won't send us a trigger. */ + if (priv->iw_mode == IW_MODE_INFRA || priv->iw_mode == IW_MODE_ADHOC) + netif_carrier_off(ndev); + else + netif_carrier_on(ndev); + + return 0; +} + +static int +islpci_close(struct net_device *ndev) +{ + islpci_private *priv = netdev_priv(ndev); + + printk(KERN_DEBUG "%s: islpci_close ()\n", ndev->name); + + netif_stop_queue(ndev); + + return prism54_bring_down(priv); +} + +static int +prism54_bring_down(islpci_private *priv) +{ + void __iomem *device_base = priv->device_base; + u32 reg; + /* we are going to shutdown the device */ + islpci_set_state(priv, PRV_STATE_PREBOOT); + + /* disable all device interrupts in case they weren't */ + isl38xx_disable_interrupts(priv->device_base); + + /* For safety reasons, we may want to ensure that no DMA transfer is + * currently in progress by emptying the TX and RX queues. */ + + /* wait until interrupts have finished executing on other CPUs */ + synchronize_irq(priv->pdev->irq); + + reg = readl(device_base + ISL38XX_CTRL_STAT_REG); + reg &= ~(ISL38XX_CTRL_STAT_RESET | ISL38XX_CTRL_STAT_RAMBOOT); + writel(reg, device_base + ISL38XX_CTRL_STAT_REG); + wmb(); + udelay(ISL38XX_WRITEIO_DELAY); + + reg |= ISL38XX_CTRL_STAT_RESET; + writel(reg, device_base + ISL38XX_CTRL_STAT_REG); + wmb(); + udelay(ISL38XX_WRITEIO_DELAY); + + /* clear the Reset bit */ + reg &= ~ISL38XX_CTRL_STAT_RESET; + writel(reg, device_base + ISL38XX_CTRL_STAT_REG); + wmb(); + + /* wait a while for the device to reset */ + schedule_timeout_uninterruptible(msecs_to_jiffies(50)); + + return 0; +} + +static int +islpci_upload_fw(islpci_private *priv) +{ + islpci_state_t old_state; + u32 rc; + + old_state = islpci_set_state(priv, PRV_STATE_BOOT); + + printk(KERN_DEBUG "%s: uploading firmware...\n", priv->ndev->name); + + rc = isl_upload_firmware(priv); + if (rc) { + /* error uploading the firmware */ + printk(KERN_ERR "%s: could not upload firmware ('%s')\n", + priv->ndev->name, priv->firmware); + + islpci_set_state(priv, old_state); + return rc; + } + + printk(KERN_DEBUG "%s: firmware upload complete\n", + priv->ndev->name); + + islpci_set_state(priv, PRV_STATE_POSTBOOT); + + return 0; +} + +static int +islpci_reset_if(islpci_private *priv) +{ + long remaining; + int result = -ETIME; + int count; + + DEFINE_WAIT(wait); + prepare_to_wait(&priv->reset_done, &wait, TASK_UNINTERRUPTIBLE); + + /* now the last step is to reset the interface */ + isl38xx_interface_reset(priv->device_base, priv->device_host_address); + islpci_set_state(priv, PRV_STATE_PREINIT); + + for(count = 0; count < 2 && result; count++) { + /* The software reset acknowledge needs about 220 msec here. + * Be conservative and wait for up to one second. */ + + remaining = schedule_timeout_uninterruptible(HZ); + + if(remaining > 0) { + result = 0; + break; + } + + /* If we're here it's because our IRQ hasn't yet gone through. + * Retry a bit more... + */ + printk(KERN_ERR "%s: no 'reset complete' IRQ seen - retrying\n", + priv->ndev->name); + } + + finish_wait(&priv->reset_done, &wait); + + if (result) { + printk(KERN_ERR "%s: interface reset failure\n", priv->ndev->name); + return result; + } + + islpci_set_state(priv, PRV_STATE_INIT); + + /* Now that the device is 100% up, let's allow + * for the other interrupts -- + * NOTE: this is not *yet* true since we've only allowed the + * INIT interrupt on the IRQ line. We can perhaps poll + * the IRQ line until we know for sure the reset went through */ + isl38xx_enable_common_interrupts(priv->device_base); + + down_write(&priv->mib_sem); + result = mgt_commit(priv); + if (result) { + printk(KERN_ERR "%s: interface reset failure\n", priv->ndev->name); + up_write(&priv->mib_sem); + return result; + } + up_write(&priv->mib_sem); + + islpci_set_state(priv, PRV_STATE_READY); + + printk(KERN_DEBUG "%s: interface reset complete\n", priv->ndev->name); + return 0; +} + +int +islpci_reset(islpci_private *priv, int reload_firmware) +{ + isl38xx_control_block *cb = /* volatile not needed */ + (isl38xx_control_block *) priv->control_block; + unsigned counter; + int rc; + + if (reload_firmware) + islpci_set_state(priv, PRV_STATE_PREBOOT); + else + islpci_set_state(priv, PRV_STATE_POSTBOOT); + + printk(KERN_DEBUG "%s: resetting device...\n", priv->ndev->name); + + /* disable all device interrupts in case they weren't */ + isl38xx_disable_interrupts(priv->device_base); + + /* flush all management queues */ + priv->index_mgmt_tx = 0; + priv->index_mgmt_rx = 0; + + /* clear the indexes in the frame pointer */ + for (counter = 0; counter < ISL38XX_CB_QCOUNT; counter++) { + cb->driver_curr_frag[counter] = cpu_to_le32(0); + cb->device_curr_frag[counter] = cpu_to_le32(0); + } + + /* reset the mgmt receive queue */ + for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) { + isl38xx_fragment *frag = &cb->rx_data_mgmt[counter]; + frag->size = cpu_to_le16(MGMT_FRAME_SIZE); + frag->flags = 0; + frag->address = cpu_to_le32(priv->mgmt_rx[counter].pci_addr); + } + + for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) { + cb->rx_data_low[counter].address = + cpu_to_le32((u32) priv->pci_map_rx_address[counter]); + } + + /* since the receive queues are filled with empty fragments, now we can + * set the corresponding indexes in the Control Block */ + priv->control_block->driver_curr_frag[ISL38XX_CB_RX_DATA_LQ] = + cpu_to_le32(ISL38XX_CB_RX_QSIZE); + priv->control_block->driver_curr_frag[ISL38XX_CB_RX_MGMTQ] = + cpu_to_le32(ISL38XX_CB_MGMT_QSIZE); + + /* reset the remaining real index registers and full flags */ + priv->free_data_rx = 0; + priv->free_data_tx = 0; + priv->data_low_tx_full = 0; + + if (reload_firmware) { /* Should we load the firmware ? */ + /* now that the data structures are cleaned up, upload + * firmware and reset interface */ + rc = islpci_upload_fw(priv); + if (rc) { + printk(KERN_ERR "%s: islpci_reset: failure\n", + priv->ndev->name); + return rc; + } + } + + /* finally reset interface */ + rc = islpci_reset_if(priv); + if (rc) + printk(KERN_ERR "prism54: Your card/socket may be faulty, or IRQ line too busy :(\n"); + return rc; +} + +/****************************************************************************** + Network device configuration functions +******************************************************************************/ +static int +islpci_alloc_memory(islpci_private *priv) +{ + int counter; + +#if VERBOSE > SHOW_ERROR_MESSAGES + printk(KERN_DEBUG "islpci_alloc_memory\n"); +#endif + + /* remap the PCI device base address to accessible */ + if (!(priv->device_base = + ioremap(pci_resource_start(priv->pdev, 0), + ISL38XX_PCI_MEM_SIZE))) { + /* error in remapping the PCI device memory address range */ + printk(KERN_ERR "PCI memory remapping failed\n"); + return -1; + } + + /* memory layout for consistent DMA region: + * + * Area 1: Control Block for the device interface + * Area 2: Power Save Mode Buffer for temporary frame storage. Be aware that + * the number of supported stations in the AP determines the minimal + * size of the buffer ! + */ + + /* perform the allocation */ + priv->driver_mem_address = pci_alloc_consistent(priv->pdev, + HOST_MEM_BLOCK, + &priv-> + device_host_address); + + if (!priv->driver_mem_address) { + /* error allocating the block of PCI memory */ + printk(KERN_ERR "%s: could not allocate DMA memory, aborting!", + "prism54"); + return -1; + } + + /* assign the Control Block to the first address of the allocated area */ + priv->control_block = + (isl38xx_control_block *) priv->driver_mem_address; + + /* set the Power Save Buffer pointer directly behind the CB */ + priv->device_psm_buffer = + priv->device_host_address + CONTROL_BLOCK_SIZE; + + /* make sure all buffer pointers are initialized */ + for (counter = 0; counter < ISL38XX_CB_QCOUNT; counter++) { + priv->control_block->driver_curr_frag[counter] = cpu_to_le32(0); + priv->control_block->device_curr_frag[counter] = cpu_to_le32(0); + } + + priv->index_mgmt_rx = 0; + memset(priv->mgmt_rx, 0, sizeof(priv->mgmt_rx)); + memset(priv->mgmt_tx, 0, sizeof(priv->mgmt_tx)); + + /* allocate rx queue for management frames */ + if (islpci_mgmt_rx_fill(priv->ndev) < 0) + goto out_free; + + /* now get the data rx skb's */ + memset(priv->data_low_rx, 0, sizeof (priv->data_low_rx)); + memset(priv->pci_map_rx_address, 0, sizeof (priv->pci_map_rx_address)); + + for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) { + struct sk_buff *skb; + + /* allocate an sk_buff for received data frames storage + * each frame on receive size consists of 1 fragment + * include any required allignment operations */ + if (!(skb = dev_alloc_skb(MAX_FRAGMENT_SIZE_RX + 2))) { + /* error allocating an sk_buff structure elements */ + printk(KERN_ERR "Error allocating skb.\n"); + skb = NULL; + goto out_free; + } + skb_reserve(skb, (4 - (long) skb->data) & 0x03); + /* add the new allocated sk_buff to the buffer array */ + priv->data_low_rx[counter] = skb; + + /* map the allocated skb data area to pci */ + priv->pci_map_rx_address[counter] = + pci_map_single(priv->pdev, (void *) skb->data, + MAX_FRAGMENT_SIZE_RX + 2, + PCI_DMA_FROMDEVICE); + if (!priv->pci_map_rx_address[counter]) { + /* error mapping the buffer to device + accessible memory address */ + printk(KERN_ERR "failed to map skb DMA'able\n"); + goto out_free; + } + } + + prism54_acl_init(&priv->acl); + prism54_wpa_bss_ie_init(priv); + if (mgt_init(priv)) + goto out_free; + + return 0; + out_free: + islpci_free_memory(priv); + return -1; +} + +int +islpci_free_memory(islpci_private *priv) +{ + int counter; + + if (priv->device_base) + iounmap(priv->device_base); + priv->device_base = NULL; + + /* free consistent DMA area... */ + if (priv->driver_mem_address) + pci_free_consistent(priv->pdev, HOST_MEM_BLOCK, + priv->driver_mem_address, + priv->device_host_address); + + /* clear some dangling pointers */ + priv->driver_mem_address = NULL; + priv->device_host_address = 0; + priv->device_psm_buffer = 0; + priv->control_block = NULL; + + /* clean up mgmt rx buffers */ + for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) { + struct islpci_membuf *buf = &priv->mgmt_rx[counter]; + if (buf->pci_addr) + pci_unmap_single(priv->pdev, buf->pci_addr, + buf->size, PCI_DMA_FROMDEVICE); + buf->pci_addr = 0; + kfree(buf->mem); + buf->size = 0; + buf->mem = NULL; + } + + /* clean up data rx buffers */ + for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) { + if (priv->pci_map_rx_address[counter]) + pci_unmap_single(priv->pdev, + priv->pci_map_rx_address[counter], + MAX_FRAGMENT_SIZE_RX + 2, + PCI_DMA_FROMDEVICE); + priv->pci_map_rx_address[counter] = 0; + + if (priv->data_low_rx[counter]) + dev_kfree_skb(priv->data_low_rx[counter]); + priv->data_low_rx[counter] = NULL; + } + + /* Free the access control list and the WPA list */ + prism54_acl_clean(&priv->acl); + prism54_wpa_bss_ie_clean(priv); + mgt_clean(priv); + + return 0; +} + +#if 0 +static void +islpci_set_multicast_list(struct net_device *dev) +{ + /* put device into promisc mode and let network layer handle it */ +} +#endif + +static void islpci_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); +} + +static const struct ethtool_ops islpci_ethtool_ops = { + .get_drvinfo = islpci_ethtool_get_drvinfo, +}; + +static const struct net_device_ops islpci_netdev_ops = { + .ndo_open = islpci_open, + .ndo_stop = islpci_close, + .ndo_start_xmit = islpci_eth_transmit, + .ndo_tx_timeout = islpci_eth_tx_timeout, + .ndo_set_mac_address = prism54_set_mac_address, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +static struct device_type wlan_type = { + .name = "wlan", +}; + +struct net_device * +islpci_setup(struct pci_dev *pdev) +{ + islpci_private *priv; + struct net_device *ndev = alloc_etherdev(sizeof (islpci_private)); + + if (!ndev) + return ndev; + + pci_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + SET_NETDEV_DEVTYPE(ndev, &wlan_type); + + /* setup the structure members */ + ndev->base_addr = pci_resource_start(pdev, 0); + ndev->irq = pdev->irq; + + /* initialize the function pointers */ + ndev->netdev_ops = &islpci_netdev_ops; + ndev->wireless_handlers = &prism54_handler_def; + ndev->ethtool_ops = &islpci_ethtool_ops; + + /* ndev->set_multicast_list = &islpci_set_multicast_list; */ + ndev->addr_len = ETH_ALEN; + /* Get a non-zero dummy MAC address for nameif. Jean II */ + memcpy(ndev->dev_addr, dummy_mac, ETH_ALEN); + + ndev->watchdog_timeo = ISLPCI_TX_TIMEOUT; + + /* allocate a private device structure to the network device */ + priv = netdev_priv(ndev); + priv->ndev = ndev; + priv->pdev = pdev; + priv->monitor_type = ARPHRD_IEEE80211; + priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR) ? + priv->monitor_type : ARPHRD_ETHER; + + /* Add pointers to enable iwspy support. */ + priv->wireless_data.spy_data = &priv->spy_data; + ndev->wireless_data = &priv->wireless_data; + + /* save the start and end address of the PCI memory area */ + ndev->mem_start = (unsigned long) priv->device_base; + ndev->mem_end = ndev->mem_start + ISL38XX_PCI_MEM_SIZE; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "PCI Memory remapped to 0x%p\n", priv->device_base); +#endif + + init_waitqueue_head(&priv->reset_done); + + /* init the queue read locks, process wait counter */ + mutex_init(&priv->mgmt_lock); + priv->mgmt_received = NULL; + init_waitqueue_head(&priv->mgmt_wqueue); + mutex_init(&priv->stats_lock); + spin_lock_init(&priv->slock); + + /* init state machine with off#1 state */ + priv->state = PRV_STATE_OFF; + priv->state_off = 1; + + /* initialize workqueue's */ + INIT_WORK(&priv->stats_work, prism54_update_stats); + priv->stats_timestamp = 0; + + INIT_WORK(&priv->reset_task, islpci_do_reset_and_wake); + priv->reset_task_pending = 0; + + /* allocate various memory areas */ + if (islpci_alloc_memory(priv)) + goto do_free_netdev; + + /* select the firmware file depending on the device id */ + switch (pdev->device) { + case 0x3877: + strcpy(priv->firmware, ISL3877_IMAGE_FILE); + break; + + case 0x3886: + strcpy(priv->firmware, ISL3886_IMAGE_FILE); + break; + + default: + strcpy(priv->firmware, ISL3890_IMAGE_FILE); + break; + } + + if (register_netdev(ndev)) { + DEBUG(SHOW_ERROR_MESSAGES, + "ERROR: register_netdev() failed\n"); + goto do_islpci_free_memory; + } + + return ndev; + + do_islpci_free_memory: + islpci_free_memory(priv); + do_free_netdev: + free_netdev(ndev); + priv = NULL; + return NULL; +} + +islpci_state_t +islpci_set_state(islpci_private *priv, islpci_state_t new_state) +{ + islpci_state_t old_state; + + /* lock */ + old_state = priv->state; + + /* this means either a race condition or some serious error in + * the driver code */ + switch (new_state) { + case PRV_STATE_OFF: + priv->state_off++; + default: + priv->state = new_state; + break; + + case PRV_STATE_PREBOOT: + /* there are actually many off-states, enumerated by + * state_off */ + if (old_state == PRV_STATE_OFF) + priv->state_off--; + + /* only if hw_unavailable is zero now it means we either + * were in off#1 state, or came here from + * somewhere else */ + if (!priv->state_off) + priv->state = new_state; + break; + } +#if 0 + printk(KERN_DEBUG "%s: state transition %d -> %d (off#%d)\n", + priv->ndev->name, old_state, new_state, priv->state_off); +#endif + + /* invariants */ + BUG_ON(priv->state_off < 0); + BUG_ON(priv->state_off && (priv->state != PRV_STATE_OFF)); + BUG_ON(!priv->state_off && (priv->state == PRV_STATE_OFF)); + + /* unlock */ + return old_state; +} diff --git a/drivers/net/wireless/intersil/prism54/islpci_dev.h b/drivers/net/wireless/intersil/prism54/islpci_dev.h new file mode 100644 index 000000000000..f6f088e05fe4 --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/islpci_dev.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2002 Intersil Americas Inc. + * Copyright (C) 2003 Herbert Valerio Riedel + * Copyright (C) 2003 Luis R. Rodriguez + * Copyright (C) 2003 Aurelien Alleaume + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#ifndef _ISLPCI_DEV_H +#define _ISLPCI_DEV_H + +#include +#include +#include +#include +#include +#include + +#include "isl_38xx.h" +#include "isl_oid.h" +#include "islpci_mgt.h" + +/* some states might not be superflous and may be removed when + design is finalized (hvr) */ +typedef enum { + PRV_STATE_OFF = 0, /* this means hw_unavailable is != 0 */ + PRV_STATE_PREBOOT, /* we are in a pre-boot state (empty RAM) */ + PRV_STATE_BOOT, /* boot state (fw upload, run fw) */ + PRV_STATE_POSTBOOT, /* after boot state, need reset now */ + PRV_STATE_PREINIT, /* pre-init state */ + PRV_STATE_INIT, /* init state (restore MIB backup to device) */ + PRV_STATE_READY, /* driver&device are in operational state */ + PRV_STATE_SLEEP /* device in sleep mode */ +} islpci_state_t; + +/* ACL using MAC address */ +struct mac_entry { + struct list_head _list; + char addr[ETH_ALEN]; +}; + +struct islpci_acl { + enum { MAC_POLICY_OPEN=0, MAC_POLICY_ACCEPT=1, MAC_POLICY_REJECT=2 } policy; + struct list_head mac_list; /* a list of mac_entry */ + int size; /* size of queue */ + struct mutex lock; /* accessed in ioctls and trap_work */ +}; + +struct islpci_membuf { + int size; /* size of memory */ + void *mem; /* address of memory as seen by CPU */ + dma_addr_t pci_addr; /* address of memory as seen by device */ +}; + +#define MAX_BSS_WPA_IE_COUNT 64 +#define MAX_WPA_IE_LEN 64 +struct islpci_bss_wpa_ie { + struct list_head list; + unsigned long last_update; + u8 bssid[ETH_ALEN]; + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + +}; + +typedef struct { + spinlock_t slock; /* generic spinlock; */ + + u32 priv_oid; + + /* our mib cache */ + u32 iw_mode; + struct rw_semaphore mib_sem; + void **mib; + char nickname[IW_ESSID_MAX_SIZE+1]; + + /* Take care of the wireless stats */ + struct work_struct stats_work; + struct mutex stats_lock; + /* remember when we last updated the stats */ + unsigned long stats_timestamp; + /* The first is accessed under semaphore locking. + * The second is the clean one we return to iwconfig. + */ + struct iw_statistics local_iwstatistics; + struct iw_statistics iwstatistics; + + struct iw_spy_data spy_data; /* iwspy support */ + + struct iw_public_data wireless_data; + + int monitor_type; /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_PRISM */ + + struct islpci_acl acl; + + /* PCI bus allocation & configuration members */ + struct pci_dev *pdev; /* PCI structure information */ + char firmware[33]; + + void __iomem *device_base; /* ioremapped device base address */ + + /* consistent DMA region */ + void *driver_mem_address; /* base DMA address */ + dma_addr_t device_host_address; /* base DMA address (bus address) */ + dma_addr_t device_psm_buffer; /* host memory for PSM buffering (bus address) */ + + /* our network_device structure */ + struct net_device *ndev; + + /* device queue interface members */ + struct isl38xx_cb *control_block; /* device control block + (== driver_mem_address!) */ + + /* Each queue has three indexes: + * free/index_mgmt/data_rx/tx (called index, see below), + * driver_curr_frag, and device_curr_frag (in the control block) + * All indexes are ever-increasing, but interpreted modulo the + * device queue size when used. + * index <= device_curr_frag <= driver_curr_frag at all times + * For rx queues, [index, device_curr_frag) contains fragments + * that the interrupt processing needs to handle (owned by driver). + * [device_curr_frag, driver_curr_frag) is the free space in the + * rx queue, waiting for data (owned by device). The driver + * increments driver_curr_frag to indicate to the device that more + * buffers are available. + * If device_curr_frag == driver_curr_frag, no more rx buffers are + * available, and the rx DMA engine of the device is halted. + * For tx queues, [index, device_curr_frag) contains fragments + * where tx is done; they need to be freed (owned by driver). + * [device_curr_frag, driver_curr_frag) contains the frames + * that are being transferred (owned by device). The driver + * increments driver_curr_frag to indicate that more tx work + * needs to be done. + */ + u32 index_mgmt_rx; /* real index mgmt rx queue */ + u32 index_mgmt_tx; /* read index mgmt tx queue */ + u32 free_data_rx; /* free pointer data rx queue */ + u32 free_data_tx; /* free pointer data tx queue */ + u32 data_low_tx_full; /* full detected flag */ + + /* frame memory buffers for the device queues */ + struct islpci_membuf mgmt_tx[ISL38XX_CB_MGMT_QSIZE]; + struct islpci_membuf mgmt_rx[ISL38XX_CB_MGMT_QSIZE]; + struct sk_buff *data_low_tx[ISL38XX_CB_TX_QSIZE]; + struct sk_buff *data_low_rx[ISL38XX_CB_RX_QSIZE]; + dma_addr_t pci_map_tx_address[ISL38XX_CB_TX_QSIZE]; + dma_addr_t pci_map_rx_address[ISL38XX_CB_RX_QSIZE]; + + /* wait for a reset interrupt */ + wait_queue_head_t reset_done; + + /* used by islpci_mgt_transaction */ + struct mutex mgmt_lock; /* serialize access to mailbox and wqueue */ + struct islpci_mgmtframe *mgmt_received; /* mbox for incoming frame */ + wait_queue_head_t mgmt_wqueue; /* waitqueue for mbox */ + + /* state machine */ + islpci_state_t state; + int state_off; /* enumeration of off-state, if 0 then + * we're not in any off-state */ + + /* WPA stuff */ + int wpa; /* WPA mode enabled */ + struct list_head bss_wpa_list; + int num_bss_wpa; + struct mutex wpa_lock; + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + + struct work_struct reset_task; + int reset_task_pending; +} islpci_private; + +static inline islpci_state_t +islpci_get_state(islpci_private *priv) +{ + /* lock */ + return priv->state; + /* unlock */ +} + +islpci_state_t islpci_set_state(islpci_private *priv, islpci_state_t new_state); + +#define ISLPCI_TX_TIMEOUT (2*HZ) + +irqreturn_t islpci_interrupt(int, void *); + +int prism54_post_setup(islpci_private *, int); +int islpci_reset(islpci_private *, int); + +static inline void +islpci_trigger(islpci_private *priv) +{ + isl38xx_trigger_device(islpci_get_state(priv) == PRV_STATE_SLEEP, + priv->device_base); +} + +int islpci_free_memory(islpci_private *); +struct net_device *islpci_setup(struct pci_dev *); + +#define DRV_NAME "prism54" +#define DRV_VERSION "1.2" + +#endif /* _ISLPCI_DEV_H */ diff --git a/drivers/net/wireless/intersil/prism54/islpci_eth.c b/drivers/net/wireless/intersil/prism54/islpci_eth.c new file mode 100644 index 000000000000..674658f2e6ef --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/islpci_eth.c @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2002 Intersil Americas Inc. + * Copyright (C) 2004 Aurelien Alleaume + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "prismcompat.h" +#include "isl_38xx.h" +#include "islpci_eth.h" +#include "islpci_mgt.h" +#include "oid_mgt.h" + +/****************************************************************************** + Network Interface functions +******************************************************************************/ +void +islpci_eth_cleanup_transmit(islpci_private *priv, + isl38xx_control_block *control_block) +{ + struct sk_buff *skb; + u32 index; + + /* compare the control block read pointer with the free pointer */ + while (priv->free_data_tx != + le32_to_cpu(control_block-> + device_curr_frag[ISL38XX_CB_TX_DATA_LQ])) { + /* read the index of the first fragment to be freed */ + index = priv->free_data_tx % ISL38XX_CB_TX_QSIZE; + + /* check for holes in the arrays caused by multi fragment frames + * searching for the last fragment of a frame */ + if (priv->pci_map_tx_address[index]) { + /* entry is the last fragment of a frame + * free the skb structure and unmap pci memory */ + skb = priv->data_low_tx[index]; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, + "cleanup skb %p skb->data %p skb->len %u truesize %u\n ", + skb, skb->data, skb->len, skb->truesize); +#endif + + pci_unmap_single(priv->pdev, + priv->pci_map_tx_address[index], + skb->len, PCI_DMA_TODEVICE); + dev_kfree_skb_irq(skb); + skb = NULL; + } + /* increment the free data low queue pointer */ + priv->free_data_tx++; + } +} + +netdev_tx_t +islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev) +{ + islpci_private *priv = netdev_priv(ndev); + isl38xx_control_block *cb = priv->control_block; + u32 index; + dma_addr_t pci_map_address; + int frame_size; + isl38xx_fragment *fragment; + int offset; + struct sk_buff *newskb; + int newskb_offset; + unsigned long flags; + unsigned char wds_mac[6]; + u32 curr_frag; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_transmit\n"); +#endif + + /* lock the driver code */ + spin_lock_irqsave(&priv->slock, flags); + + /* check whether the destination queue has enough fragments for the frame */ + curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ]); + if (unlikely(curr_frag - priv->free_data_tx >= ISL38XX_CB_TX_QSIZE)) { + printk(KERN_ERR "%s: transmit device queue full when awake\n", + ndev->name); + netif_stop_queue(ndev); + + /* trigger the device */ + isl38xx_w32_flush(priv->device_base, ISL38XX_DEV_INT_UPDATE, + ISL38XX_DEV_INT_REG); + udelay(ISL38XX_WRITEIO_DELAY); + goto drop_free; + } + /* Check alignment and WDS frame formatting. The start of the packet should + * be aligned on a 4-byte boundary. If WDS is enabled add another 6 bytes + * and add WDS address information */ + if (likely(((long) skb->data & 0x03) | init_wds)) { + /* get the number of bytes to add and re-align */ + offset = (4 - (long) skb->data) & 0x03; + offset += init_wds ? 6 : 0; + + /* check whether the current skb can be used */ + if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) { + unsigned char *src = skb->data; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "skb offset %i wds %i\n", offset, + init_wds); +#endif + + /* align the buffer on 4-byte boundary */ + skb_reserve(skb, (4 - (long) skb->data) & 0x03); + if (init_wds) { + /* wds requires an additional address field of 6 bytes */ + skb_put(skb, 6); +#ifdef ISLPCI_ETH_DEBUG + printk("islpci_eth_transmit:wds_mac\n"); +#endif + memmove(skb->data + 6, src, skb->len); + skb_copy_to_linear_data(skb, wds_mac, 6); + } else { + memmove(skb->data, src, skb->len); + } + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "memmove %p %p %i\n", skb->data, + src, skb->len); +#endif + } else { + newskb = + dev_alloc_skb(init_wds ? skb->len + 6 : skb->len); + if (unlikely(newskb == NULL)) { + printk(KERN_ERR "%s: Cannot allocate skb\n", + ndev->name); + goto drop_free; + } + newskb_offset = (4 - (long) newskb->data) & 0x03; + + /* Check if newskb->data is aligned */ + if (newskb_offset) + skb_reserve(newskb, newskb_offset); + + skb_put(newskb, init_wds ? skb->len + 6 : skb->len); + if (init_wds) { + skb_copy_from_linear_data(skb, + newskb->data + 6, + skb->len); + skb_copy_to_linear_data(newskb, wds_mac, 6); +#ifdef ISLPCI_ETH_DEBUG + printk("islpci_eth_transmit:wds_mac\n"); +#endif + } else + skb_copy_from_linear_data(skb, newskb->data, + skb->len); + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "memcpy %p %p %i wds %i\n", + newskb->data, skb->data, skb->len, init_wds); +#endif + + newskb->dev = skb->dev; + dev_kfree_skb_irq(skb); + skb = newskb; + } + } + /* display the buffer contents for debugging */ +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_BUFFER_CONTENTS, "\ntx %p ", skb->data); + display_buffer((char *) skb->data, skb->len); +#endif + + /* map the skb buffer to pci memory for DMA operation */ + pci_map_address = pci_map_single(priv->pdev, + (void *) skb->data, skb->len, + PCI_DMA_TODEVICE); + if (unlikely(pci_map_address == 0)) { + printk(KERN_WARNING "%s: cannot map buffer to PCI\n", + ndev->name); + goto drop_free; + } + /* Place the fragment in the control block structure. */ + index = curr_frag % ISL38XX_CB_TX_QSIZE; + fragment = &cb->tx_data_low[index]; + + priv->pci_map_tx_address[index] = pci_map_address; + /* store the skb address for future freeing */ + priv->data_low_tx[index] = skb; + /* set the proper fragment start address and size information */ + frame_size = skb->len; + fragment->size = cpu_to_le16(frame_size); + fragment->flags = cpu_to_le16(0); /* set to 1 if more fragments */ + fragment->address = cpu_to_le32(pci_map_address); + curr_frag++; + + /* The fragment address in the control block must have been + * written before announcing the frame buffer to device. */ + wmb(); + cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ] = cpu_to_le32(curr_frag); + + if (curr_frag - priv->free_data_tx + ISL38XX_MIN_QTHRESHOLD + > ISL38XX_CB_TX_QSIZE) { + /* stop sends from upper layers */ + netif_stop_queue(ndev); + + /* set the full flag for the transmission queue */ + priv->data_low_tx_full = 1; + } + + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + + /* trigger the device */ + islpci_trigger(priv); + + /* unlock the driver code */ + spin_unlock_irqrestore(&priv->slock, flags); + + return NETDEV_TX_OK; + + drop_free: + ndev->stats.tx_dropped++; + spin_unlock_irqrestore(&priv->slock, flags); + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static inline int +islpci_monitor_rx(islpci_private *priv, struct sk_buff **skb) +{ + /* The card reports full 802.11 packets but with a 20 bytes + * header and without the FCS. But there a is a bit that + * indicates if the packet is corrupted :-) */ + struct rfmon_header *hdr = (struct rfmon_header *) (*skb)->data; + + if (hdr->flags & 0x01) + /* This one is bad. Drop it ! */ + return -1; + if (priv->ndev->type == ARPHRD_IEEE80211_PRISM) { + struct avs_80211_1_header *avs; + /* extract the relevant data from the header */ + u32 clock = le32_to_cpu(hdr->clock); + u8 rate = hdr->rate; + u16 freq = le16_to_cpu(hdr->freq); + u8 rssi = hdr->rssi; + + skb_pull(*skb, sizeof (struct rfmon_header)); + + if (skb_headroom(*skb) < sizeof (struct avs_80211_1_header)) { + struct sk_buff *newskb = skb_copy_expand(*skb, + sizeof (struct + avs_80211_1_header), + 0, GFP_ATOMIC); + if (newskb) { + dev_kfree_skb_irq(*skb); + *skb = newskb; + } else + return -1; + /* This behavior is not very subtile... */ + } + + /* make room for the new header and fill it. */ + avs = + (struct avs_80211_1_header *) skb_push(*skb, + sizeof (struct + avs_80211_1_header)); + + avs->version = cpu_to_be32(P80211CAPTURE_VERSION); + avs->length = cpu_to_be32(sizeof (struct avs_80211_1_header)); + avs->mactime = cpu_to_be64(clock); + avs->hosttime = cpu_to_be64(jiffies); + avs->phytype = cpu_to_be32(6); /*OFDM: 6 for (g), 8 for (a) */ + avs->channel = cpu_to_be32(channel_of_freq(freq)); + avs->datarate = cpu_to_be32(rate * 5); + avs->antenna = cpu_to_be32(0); /*unknown */ + avs->priority = cpu_to_be32(0); /*unknown */ + avs->ssi_type = cpu_to_be32(3); /*2: dBm, 3: raw RSSI */ + avs->ssi_signal = cpu_to_be32(rssi & 0x7f); + avs->ssi_noise = cpu_to_be32(priv->local_iwstatistics.qual.noise); /*better than 'undefined', I assume */ + avs->preamble = cpu_to_be32(0); /*unknown */ + avs->encoding = cpu_to_be32(0); /*unknown */ + } else + skb_pull(*skb, sizeof (struct rfmon_header)); + + (*skb)->protocol = htons(ETH_P_802_2); + skb_reset_mac_header(*skb); + (*skb)->pkt_type = PACKET_OTHERHOST; + + return 0; +} + +int +islpci_eth_receive(islpci_private *priv) +{ + struct net_device *ndev = priv->ndev; + isl38xx_control_block *control_block = priv->control_block; + struct sk_buff *skb; + u16 size; + u32 index, offset; + unsigned char *src; + int discard = 0; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_receive\n"); +#endif + + /* the device has written an Ethernet frame in the data area + * of the sk_buff without updating the structure, do it now */ + index = priv->free_data_rx % ISL38XX_CB_RX_QSIZE; + size = le16_to_cpu(control_block->rx_data_low[index].size); + skb = priv->data_low_rx[index]; + offset = ((unsigned long) + le32_to_cpu(control_block->rx_data_low[index].address) - + (unsigned long) skb->data) & 3; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, + "frq->addr %x skb->data %p skb->len %u offset %u truesize %u\n ", + control_block->rx_data_low[priv->free_data_rx].address, skb->data, + skb->len, offset, skb->truesize); +#endif + + /* delete the streaming DMA mapping before processing the skb */ + pci_unmap_single(priv->pdev, + priv->pci_map_rx_address[index], + MAX_FRAGMENT_SIZE_RX + 2, PCI_DMA_FROMDEVICE); + + /* update the skb structure and align the buffer */ + skb_put(skb, size); + if (offset) { + /* shift the buffer allocation offset bytes to get the right frame */ + skb_pull(skb, 2); + skb_put(skb, 2); + } +#if VERBOSE > SHOW_ERROR_MESSAGES + /* display the buffer contents for debugging */ + DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data); + display_buffer((char *) skb->data, skb->len); +#endif + + /* check whether WDS is enabled and whether the data frame is a WDS frame */ + + if (init_wds) { + /* WDS enabled, check for the wds address on the first 6 bytes of the buffer */ + src = skb->data + 6; + memmove(skb->data, src, skb->len - 6); + skb_trim(skb, skb->len - 6); + } +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "Fragment size %i in skb at %p\n", size, skb); + DEBUG(SHOW_TRACING, "Skb data at %p, length %i\n", skb->data, skb->len); + + /* display the buffer contents for debugging */ + DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data); + display_buffer((char *) skb->data, skb->len); +#endif + /* take care of monitor mode and spy monitoring. */ + if (unlikely(priv->iw_mode == IW_MODE_MONITOR)) { + skb->dev = ndev; + discard = islpci_monitor_rx(priv, &skb); + } else { + if (unlikely(skb->data[2 * ETH_ALEN] == 0)) { + /* The packet has a rx_annex. Read it for spy monitoring, Then + * remove it, while keeping the 2 leading MAC addr. + */ + struct iw_quality wstats; + struct rx_annex_header *annex = + (struct rx_annex_header *) skb->data; + wstats.level = annex->rfmon.rssi; + /* The noise value can be a bit outdated if nobody's + * reading wireless stats... */ + wstats.noise = priv->local_iwstatistics.qual.noise; + wstats.qual = wstats.level - wstats.noise; + wstats.updated = 0x07; + /* Update spy records */ + wireless_spy_update(ndev, annex->addr2, &wstats); + + skb_copy_from_linear_data(skb, + (skb->data + + sizeof(struct rfmon_header)), + 2 * ETH_ALEN); + skb_pull(skb, sizeof (struct rfmon_header)); + } + skb->protocol = eth_type_trans(skb, ndev); + } + skb->ip_summed = CHECKSUM_NONE; + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += size; + + /* deliver the skb to the network layer */ +#ifdef ISLPCI_ETH_DEBUG + printk + ("islpci_eth_receive:netif_rx %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + skb->data[0], skb->data[1], skb->data[2], skb->data[3], + skb->data[4], skb->data[5]); +#endif + if (unlikely(discard)) { + dev_kfree_skb_irq(skb); + skb = NULL; + } else + netif_rx(skb); + + /* increment the read index for the rx data low queue */ + priv->free_data_rx++; + + /* add one or more sk_buff structures */ + while (index = + le32_to_cpu(control_block-> + driver_curr_frag[ISL38XX_CB_RX_DATA_LQ]), + index - priv->free_data_rx < ISL38XX_CB_RX_QSIZE) { + /* allocate an sk_buff for received data frames storage + * include any required allignment operations */ + skb = dev_alloc_skb(MAX_FRAGMENT_SIZE_RX + 2); + if (unlikely(skb == NULL)) { + /* error allocating an sk_buff structure elements */ + DEBUG(SHOW_ERROR_MESSAGES, "Error allocating skb\n"); + break; + } + skb_reserve(skb, (4 - (long) skb->data) & 0x03); + /* store the new skb structure pointer */ + index = index % ISL38XX_CB_RX_QSIZE; + priv->data_low_rx[index] = skb; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, + "new alloc skb %p skb->data %p skb->len %u index %u truesize %u\n ", + skb, skb->data, skb->len, index, skb->truesize); +#endif + + /* set the streaming DMA mapping for proper PCI bus operation */ + priv->pci_map_rx_address[index] = + pci_map_single(priv->pdev, (void *) skb->data, + MAX_FRAGMENT_SIZE_RX + 2, + PCI_DMA_FROMDEVICE); + if (unlikely(!priv->pci_map_rx_address[index])) { + /* error mapping the buffer to device accessible memory address */ + DEBUG(SHOW_ERROR_MESSAGES, + "Error mapping DMA address\n"); + + /* free the skbuf structure before aborting */ + dev_kfree_skb_irq(skb); + skb = NULL; + break; + } + /* update the fragment address */ + control_block->rx_data_low[index].address = + cpu_to_le32((u32)priv->pci_map_rx_address[index]); + wmb(); + + /* increment the driver read pointer */ + le32_add_cpu(&control_block-> + driver_curr_frag[ISL38XX_CB_RX_DATA_LQ], 1); + } + + /* trigger the device */ + islpci_trigger(priv); + + return 0; +} + +void +islpci_do_reset_and_wake(struct work_struct *work) +{ + islpci_private *priv = container_of(work, islpci_private, reset_task); + + islpci_reset(priv, 1); + priv->reset_task_pending = 0; + smp_wmb(); + netif_wake_queue(priv->ndev); +} + +void +islpci_eth_tx_timeout(struct net_device *ndev) +{ + islpci_private *priv = netdev_priv(ndev); + + /* increment the transmit error counter */ + ndev->stats.tx_errors++; + + if (!priv->reset_task_pending) { + printk(KERN_WARNING + "%s: tx_timeout, scheduling reset", ndev->name); + netif_stop_queue(ndev); + priv->reset_task_pending = 1; + schedule_work(&priv->reset_task); + } else { + printk(KERN_WARNING + "%s: tx_timeout, waiting for reset", ndev->name); + } +} diff --git a/drivers/net/wireless/intersil/prism54/islpci_eth.h b/drivers/net/wireless/intersil/prism54/islpci_eth.h new file mode 100644 index 000000000000..80f50f1bc6f2 --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/islpci_eth.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2002 Intersil Americas Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#ifndef _ISLPCI_ETH_H +#define _ISLPCI_ETH_H + +#include "isl_38xx.h" +#include "islpci_dev.h" + +struct rfmon_header { + __le16 unk0; /* = 0x0000 */ + __le16 length; /* = 0x1400 */ + __le32 clock; /* 1MHz clock */ + u8 flags; + u8 unk1; + u8 rate; + u8 unk2; + __le16 freq; + __le16 unk3; + u8 rssi; + u8 padding[3]; +} __packed; + +struct rx_annex_header { + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + struct rfmon_header rfmon; +} __packed; + +/* wlan-ng (and hopefully others) AVS header, version one. Fields in + * network byte order. */ +#define P80211CAPTURE_VERSION 0x80211001 + +struct avs_80211_1_header { + __be32 version; + __be32 length; + __be64 mactime; + __be64 hosttime; + __be32 phytype; + __be32 channel; + __be32 datarate; + __be32 antenna; + __be32 priority; + __be32 ssi_type; + __be32 ssi_signal; + __be32 ssi_noise; + __be32 preamble; + __be32 encoding; +}; + +void islpci_eth_cleanup_transmit(islpci_private *, isl38xx_control_block *); +netdev_tx_t islpci_eth_transmit(struct sk_buff *, struct net_device *); +int islpci_eth_receive(islpci_private *); +void islpci_eth_tx_timeout(struct net_device *); +void islpci_do_reset_and_wake(struct work_struct *); + +#endif /* _ISL_GEN_H */ diff --git a/drivers/net/wireless/intersil/prism54/islpci_hotplug.c b/drivers/net/wireless/intersil/prism54/islpci_hotplug.c new file mode 100644 index 000000000000..300c846ea087 --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/islpci_hotplug.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2002 Intersil Americas Inc. + * Copyright (C) 2003 Herbert Valerio Riedel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include +#include +#include +#include +#include /* For __init, __exit */ +#include + +#include "prismcompat.h" +#include "islpci_dev.h" +#include "islpci_mgt.h" /* for pc_debug */ +#include "isl_oid.h" + +MODULE_AUTHOR("[Intersil] R.Bastings and W.Termorshuizen, The prism54.org Development Team "); +MODULE_DESCRIPTION("The Prism54 802.11 Wireless LAN adapter"); +MODULE_LICENSE("GPL"); + +static int init_pcitm = 0; +module_param(init_pcitm, int, 0); + +/* In this order: vendor, device, subvendor, subdevice, class, class_mask, + * driver_data + * If you have an update for this please contact prism54-devel@prism54.org + * The latest list can be found at http://wireless.kernel.org/en/users/Drivers/p54 */ +static const struct pci_device_id prism54_id_tbl[] = { + /* Intersil PRISM Duette/Prism GT Wireless LAN adapter */ + { + 0x1260, 0x3890, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0 + }, + + /* 3COM 3CRWE154G72 Wireless LAN adapter */ + { + PCI_VDEVICE(3COM, 0x6001), 0 + }, + + /* Intersil PRISM Indigo Wireless LAN adapter */ + { + 0x1260, 0x3877, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0 + }, + + /* Intersil PRISM Javelin/Xbow Wireless LAN adapter */ + { + 0x1260, 0x3886, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0 + }, + + /* End of list */ + {0,0,0,0,0,0,0} +}; + +/* register the device with the Hotplug facilities of the kernel */ +MODULE_DEVICE_TABLE(pci, prism54_id_tbl); + +static int prism54_probe(struct pci_dev *, const struct pci_device_id *); +static void prism54_remove(struct pci_dev *); +static int prism54_suspend(struct pci_dev *, pm_message_t state); +static int prism54_resume(struct pci_dev *); + +static struct pci_driver prism54_driver = { + .name = DRV_NAME, + .id_table = prism54_id_tbl, + .probe = prism54_probe, + .remove = prism54_remove, + .suspend = prism54_suspend, + .resume = prism54_resume, +}; + +/****************************************************************************** + Module initialization functions +******************************************************************************/ + +static int +prism54_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct net_device *ndev; + u8 latency_tmr; + u32 mem_addr; + islpci_private *priv; + int rvalue; + + /* Enable the pci device */ + if (pci_enable_device(pdev)) { + printk(KERN_ERR "%s: pci_enable_device() failed.\n", DRV_NAME); + return -ENODEV; + } + + /* check whether the latency timer is set correctly */ + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_tmr); +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "latency timer: %x\n", latency_tmr); +#endif + if (latency_tmr < PCIDEVICE_LATENCY_TIMER_MIN) { + /* set the latency timer */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, + PCIDEVICE_LATENCY_TIMER_VAL); + } + + /* enable PCI DMA */ + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { + printk(KERN_ERR "%s: 32-bit PCI DMA not supported", DRV_NAME); + goto do_pci_disable_device; + } + + /* 0x40 is the programmable timer to configure the response timeout (TRDY_TIMEOUT) + * 0x41 is the programmable timer to configure the retry timeout (RETRY_TIMEOUT) + * The RETRY_TIMEOUT is used to set the number of retries that the core, as a + * Master, will perform before abandoning a cycle. The default value for + * RETRY_TIMEOUT is 0x80, which far exceeds the PCI 2.1 requirement for new + * devices. A write of zero to the RETRY_TIMEOUT register disables this + * function to allow use with any non-compliant legacy devices that may + * execute more retries. + * + * Writing zero to both these two registers will disable both timeouts and + * *can* solve problems caused by devices that are slow to respond. + * Make this configurable - MSW + */ + if ( init_pcitm >= 0 ) { + pci_write_config_byte(pdev, 0x40, (u8)init_pcitm); + pci_write_config_byte(pdev, 0x41, (u8)init_pcitm); + } else { + printk(KERN_INFO "PCI TRDY/RETRY unchanged\n"); + } + + /* request the pci device I/O regions */ + rvalue = pci_request_regions(pdev, DRV_NAME); + if (rvalue) { + printk(KERN_ERR "%s: pci_request_regions failure (rc=%d)\n", + DRV_NAME, rvalue); + goto do_pci_disable_device; + } + + /* check if the memory window is indeed set */ + rvalue = pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &mem_addr); + if (rvalue || !mem_addr) { + printk(KERN_ERR "%s: PCI device memory region not configured; fix your BIOS or CardBus bridge/drivers\n", + DRV_NAME); + goto do_pci_release_regions; + } + + /* enable PCI bus-mastering */ + DEBUG(SHOW_TRACING, "%s: pci_set_master(pdev)\n", DRV_NAME); + pci_set_master(pdev); + + /* enable MWI */ + pci_try_set_mwi(pdev); + + /* setup the network device interface and its structure */ + if (!(ndev = islpci_setup(pdev))) { + /* error configuring the driver as a network device */ + printk(KERN_ERR "%s: could not configure network device\n", + DRV_NAME); + goto do_pci_clear_mwi; + } + + priv = netdev_priv(ndev); + islpci_set_state(priv, PRV_STATE_PREBOOT); /* we are attempting to boot */ + + /* card is in unknown state yet, might have some interrupts pending */ + isl38xx_disable_interrupts(priv->device_base); + + /* request for the interrupt before uploading the firmware */ + rvalue = request_irq(pdev->irq, islpci_interrupt, + IRQF_SHARED, ndev->name, priv); + + if (rvalue) { + /* error, could not hook the handler to the irq */ + printk(KERN_ERR "%s: could not install IRQ handler\n", + ndev->name); + goto do_unregister_netdev; + } + + /* firmware upload is triggered in islpci_open */ + + return 0; + + do_unregister_netdev: + unregister_netdev(ndev); + islpci_free_memory(priv); + free_netdev(ndev); + priv = NULL; + do_pci_clear_mwi: + pci_clear_mwi(pdev); + do_pci_release_regions: + pci_release_regions(pdev); + do_pci_disable_device: + pci_disable_device(pdev); + return -EIO; +} + +/* set by cleanup_module */ +static volatile int __in_cleanup_module = 0; + +/* this one removes one(!!) instance only */ +static void +prism54_remove(struct pci_dev *pdev) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + islpci_private *priv = ndev ? netdev_priv(ndev) : NULL; + BUG_ON(!priv); + + if (!__in_cleanup_module) { + printk(KERN_DEBUG "%s: hot unplug detected\n", ndev->name); + islpci_set_state(priv, PRV_STATE_OFF); + } + + printk(KERN_DEBUG "%s: removing device\n", ndev->name); + + unregister_netdev(ndev); + + /* free the interrupt request */ + + if (islpci_get_state(priv) != PRV_STATE_OFF) { + isl38xx_disable_interrupts(priv->device_base); + islpci_set_state(priv, PRV_STATE_OFF); + /* This bellow causes a lockup at rmmod time. It might be + * because some interrupts still linger after rmmod time, + * see bug #17 */ + /* pci_set_power_state(pdev, 3);*/ /* try to power-off */ + } + + free_irq(pdev->irq, priv); + + /* free the PCI memory and unmap the remapped page */ + islpci_free_memory(priv); + + free_netdev(ndev); + priv = NULL; + + pci_clear_mwi(pdev); + + pci_release_regions(pdev); + + pci_disable_device(pdev); +} + +static int +prism54_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + islpci_private *priv = ndev ? netdev_priv(ndev) : NULL; + BUG_ON(!priv); + + + pci_save_state(pdev); + + /* tell the device not to trigger interrupts for now... */ + isl38xx_disable_interrupts(priv->device_base); + + /* from now on assume the hardware was already powered down + and don't touch it anymore */ + islpci_set_state(priv, PRV_STATE_OFF); + + netif_stop_queue(ndev); + netif_device_detach(ndev); + + return 0; +} + +static int +prism54_resume(struct pci_dev *pdev) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + islpci_private *priv = ndev ? netdev_priv(ndev) : NULL; + int err; + + BUG_ON(!priv); + + printk(KERN_NOTICE "%s: got resume request\n", ndev->name); + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "%s: pci_enable_device failed on resume\n", + ndev->name); + return err; + } + + pci_restore_state(pdev); + + /* alright let's go into the PREBOOT state */ + islpci_reset(priv, 1); + + netif_device_attach(ndev); + netif_start_queue(ndev); + + return 0; +} + +static int __init +prism54_module_init(void) +{ + printk(KERN_INFO "Loaded %s driver, version %s\n", + DRV_NAME, DRV_VERSION); + + __bug_on_wrong_struct_sizes (); + + return pci_register_driver(&prism54_driver); +} + +/* by the time prism54_module_exit() terminates, as a postcondition + * all instances will have been destroyed by calls to + * prism54_remove() */ +static void __exit +prism54_module_exit(void) +{ + __in_cleanup_module = 1; + + pci_unregister_driver(&prism54_driver); + + printk(KERN_INFO "Unloaded %s driver\n", DRV_NAME); + + __in_cleanup_module = 0; +} + +/* register entry points */ +module_init(prism54_module_init); +module_exit(prism54_module_exit); +/* EOF */ diff --git a/drivers/net/wireless/intersil/prism54/islpci_mgt.c b/drivers/net/wireless/intersil/prism54/islpci_mgt.c new file mode 100644 index 000000000000..0de14dfa68cc --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/islpci_mgt.c @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2002 Intersil Americas Inc. + * Copyright 2004 Jens Maurer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "prismcompat.h" +#include "isl_38xx.h" +#include "islpci_mgt.h" +#include "isl_oid.h" /* additional types and defs for isl38xx fw */ +#include "isl_ioctl.h" + +#include + +/****************************************************************************** + Global variable definition section +******************************************************************************/ +int pc_debug = VERBOSE; +module_param(pc_debug, int, 0); + +/****************************************************************************** + Driver general functions +******************************************************************************/ +#if VERBOSE > SHOW_ERROR_MESSAGES +void +display_buffer(char *buffer, int length) +{ + if ((pc_debug & SHOW_BUFFER_CONTENTS) == 0) + return; + + while (length > 0) { + printk("[%02x]", *buffer & 255); + length--; + buffer++; + } + + printk("\n"); +} +#endif + +/***************************************************************************** + Queue handling for management frames +******************************************************************************/ + +/* + * Helper function to create a PIMFOR management frame header. + */ +static void +pimfor_encode_header(int operation, u32 oid, u32 length, pimfor_header_t *h) +{ + h->version = PIMFOR_VERSION; + h->operation = operation; + h->device_id = PIMFOR_DEV_ID_MHLI_MIB; + h->flags = 0; + h->oid = cpu_to_be32(oid); + h->length = cpu_to_be32(length); +} + +/* + * Helper function to analyze a PIMFOR management frame header. + */ +static pimfor_header_t * +pimfor_decode_header(void *data, int len) +{ + pimfor_header_t *h = data; + + while ((void *) h < data + len) { + if (h->flags & PIMFOR_FLAG_LITTLE_ENDIAN) { + le32_to_cpus(&h->oid); + le32_to_cpus(&h->length); + } else { + be32_to_cpus(&h->oid); + be32_to_cpus(&h->length); + } + if (h->oid != OID_INL_TUNNEL) + return h; + h++; + } + return NULL; +} + +/* + * Fill the receive queue for management frames with fresh buffers. + */ +int +islpci_mgmt_rx_fill(struct net_device *ndev) +{ + islpci_private *priv = netdev_priv(ndev); + isl38xx_control_block *cb = /* volatile not needed */ + (isl38xx_control_block *) priv->control_block; + u32 curr = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ]); + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgmt_rx_fill\n"); +#endif + + while (curr - priv->index_mgmt_rx < ISL38XX_CB_MGMT_QSIZE) { + u32 index = curr % ISL38XX_CB_MGMT_QSIZE; + struct islpci_membuf *buf = &priv->mgmt_rx[index]; + isl38xx_fragment *frag = &cb->rx_data_mgmt[index]; + + if (buf->mem == NULL) { + buf->mem = kmalloc(MGMT_FRAME_SIZE, GFP_ATOMIC); + if (!buf->mem) + return -ENOMEM; + buf->size = MGMT_FRAME_SIZE; + } + if (buf->pci_addr == 0) { + buf->pci_addr = pci_map_single(priv->pdev, buf->mem, + MGMT_FRAME_SIZE, + PCI_DMA_FROMDEVICE); + if (!buf->pci_addr) { + printk(KERN_WARNING + "Failed to make memory DMA'able.\n"); + return -ENOMEM; + } + } + + /* be safe: always reset control block information */ + frag->size = cpu_to_le16(MGMT_FRAME_SIZE); + frag->flags = 0; + frag->address = cpu_to_le32(buf->pci_addr); + curr++; + + /* The fragment address in the control block must have + * been written before announcing the frame buffer to + * device */ + wmb(); + cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ] = cpu_to_le32(curr); + } + return 0; +} + +/* + * Create and transmit a management frame using "operation" and "oid", + * with arguments data/length. + * We either return an error and free the frame, or we return 0 and + * islpci_mgt_cleanup_transmit() frees the frame in the tx-done + * interrupt. + */ +static int +islpci_mgt_transmit(struct net_device *ndev, int operation, unsigned long oid, + void *data, int length) +{ + islpci_private *priv = netdev_priv(ndev); + isl38xx_control_block *cb = + (isl38xx_control_block *) priv->control_block; + void *p; + int err = -EINVAL; + unsigned long flags; + isl38xx_fragment *frag; + struct islpci_membuf buf; + u32 curr_frag; + int index; + int frag_len = length + PIMFOR_HEADER_SIZE; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_transmit\n"); +#endif + + if (frag_len > MGMT_FRAME_SIZE) { + printk(KERN_DEBUG "%s: mgmt frame too large %d\n", + ndev->name, frag_len); + goto error; + } + + err = -ENOMEM; + p = buf.mem = kmalloc(frag_len, GFP_KERNEL); + if (!buf.mem) + goto error; + + buf.size = frag_len; + + /* create the header directly in the fragment data area */ + pimfor_encode_header(operation, oid, length, (pimfor_header_t *) p); + p += PIMFOR_HEADER_SIZE; + + if (data) + memcpy(p, data, length); + else + memset(p, 0, length); + +#if VERBOSE > SHOW_ERROR_MESSAGES + { + pimfor_header_t *h = buf.mem; + DEBUG(SHOW_PIMFOR_FRAMES, + "PIMFOR: op %i, oid 0x%08lx, device %i, flags 0x%x length 0x%x\n", + h->operation, oid, h->device_id, h->flags, length); + + /* display the buffer contents for debugging */ + display_buffer((char *) h, sizeof (pimfor_header_t)); + display_buffer(p, length); + } +#endif + + err = -ENOMEM; + buf.pci_addr = pci_map_single(priv->pdev, buf.mem, frag_len, + PCI_DMA_TODEVICE); + if (!buf.pci_addr) { + printk(KERN_WARNING "%s: cannot map PCI memory for mgmt\n", + ndev->name); + goto error_free; + } + + /* Protect the control block modifications against interrupts. */ + spin_lock_irqsave(&priv->slock, flags); + curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ]); + if (curr_frag - priv->index_mgmt_tx >= ISL38XX_CB_MGMT_QSIZE) { + printk(KERN_WARNING "%s: mgmt tx queue is still full\n", + ndev->name); + goto error_unlock; + } + + /* commit the frame to the tx device queue */ + index = curr_frag % ISL38XX_CB_MGMT_QSIZE; + priv->mgmt_tx[index] = buf; + frag = &cb->tx_data_mgmt[index]; + frag->size = cpu_to_le16(frag_len); + frag->flags = 0; /* for any other than the last fragment, set to 1 */ + frag->address = cpu_to_le32(buf.pci_addr); + + /* The fragment address in the control block must have + * been written before announcing the frame buffer to + * device */ + wmb(); + cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ] = cpu_to_le32(curr_frag + 1); + spin_unlock_irqrestore(&priv->slock, flags); + + /* trigger the device */ + islpci_trigger(priv); + return 0; + + error_unlock: + spin_unlock_irqrestore(&priv->slock, flags); + error_free: + kfree(buf.mem); + error: + return err; +} + +/* + * Receive a management frame from the device. + * This can be an arbitrary number of traps, and at most one response + * frame for a previous request sent via islpci_mgt_transmit(). + */ +int +islpci_mgt_receive(struct net_device *ndev) +{ + islpci_private *priv = netdev_priv(ndev); + isl38xx_control_block *cb = + (isl38xx_control_block *) priv->control_block; + u32 curr_frag; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_receive\n"); +#endif + + /* Only once per interrupt, determine fragment range to + * process. This avoids an endless loop (i.e. lockup) if + * frames come in faster than we can process them. */ + curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_RX_MGMTQ]); + barrier(); + + for (; priv->index_mgmt_rx < curr_frag; priv->index_mgmt_rx++) { + pimfor_header_t *header; + u32 index = priv->index_mgmt_rx % ISL38XX_CB_MGMT_QSIZE; + struct islpci_membuf *buf = &priv->mgmt_rx[index]; + u16 frag_len; + int size; + struct islpci_mgmtframe *frame; + + /* I have no idea (and no documentation) if flags != 0 + * is possible. Drop the frame, reuse the buffer. */ + if (le16_to_cpu(cb->rx_data_mgmt[index].flags) != 0) { + printk(KERN_WARNING "%s: unknown flags 0x%04x\n", + ndev->name, + le16_to_cpu(cb->rx_data_mgmt[index].flags)); + continue; + } + + /* The device only returns the size of the header(s) here. */ + frag_len = le16_to_cpu(cb->rx_data_mgmt[index].size); + + /* + * We appear to have no way to tell the device the + * size of a receive buffer. Thus, if this check + * triggers, we likely have kernel heap corruption. */ + if (frag_len > MGMT_FRAME_SIZE) { + printk(KERN_WARNING + "%s: Bogus packet size of %d (%#x).\n", + ndev->name, frag_len, frag_len); + frag_len = MGMT_FRAME_SIZE; + } + + /* Ensure the results of device DMA are visible to the CPU. */ + pci_dma_sync_single_for_cpu(priv->pdev, buf->pci_addr, + buf->size, PCI_DMA_FROMDEVICE); + + /* Perform endianess conversion for PIMFOR header in-place. */ + header = pimfor_decode_header(buf->mem, frag_len); + if (!header) { + printk(KERN_WARNING "%s: no PIMFOR header found\n", + ndev->name); + continue; + } + + /* The device ID from the PIMFOR packet received from + * the MVC is always 0. We forward a sensible device_id. + * Not that anyone upstream would care... */ + header->device_id = priv->ndev->ifindex; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_PIMFOR_FRAMES, + "PIMFOR: op %i, oid 0x%08x, device %i, flags 0x%x length 0x%x\n", + header->operation, header->oid, header->device_id, + header->flags, header->length); + + /* display the buffer contents for debugging */ + display_buffer((char *) header, PIMFOR_HEADER_SIZE); + display_buffer((char *) header + PIMFOR_HEADER_SIZE, + header->length); +#endif + + /* nobody sends these */ + if (header->flags & PIMFOR_FLAG_APPLIC_ORIGIN) { + printk(KERN_DEBUG + "%s: errant PIMFOR application frame\n", + ndev->name); + continue; + } + + /* Determine frame size, skipping OID_INL_TUNNEL headers. */ + size = PIMFOR_HEADER_SIZE + header->length; + frame = kmalloc(sizeof(struct islpci_mgmtframe) + size, + GFP_ATOMIC); + if (!frame) + continue; + + frame->ndev = ndev; + memcpy(&frame->buf, header, size); + frame->header = (pimfor_header_t *) frame->buf; + frame->data = frame->buf + PIMFOR_HEADER_SIZE; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_PIMFOR_FRAMES, + "frame: header: %p, data: %p, size: %d\n", + frame->header, frame->data, size); +#endif + + if (header->operation == PIMFOR_OP_TRAP) { +#if VERBOSE > SHOW_ERROR_MESSAGES + printk(KERN_DEBUG + "TRAP: oid 0x%x, device %i, flags 0x%x length %i\n", + header->oid, header->device_id, header->flags, + header->length); +#endif + + /* Create work to handle trap out of interrupt + * context. */ + INIT_WORK(&frame->ws, prism54_process_trap); + schedule_work(&frame->ws); + + } else { + /* Signal the one waiting process that a response + * has been received. */ + if ((frame = xchg(&priv->mgmt_received, frame)) != NULL) { + printk(KERN_WARNING + "%s: mgmt response not collected\n", + ndev->name); + kfree(frame); + } +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_TRACING, "Wake up Mgmt Queue\n"); +#endif + wake_up(&priv->mgmt_wqueue); + } + + } + + return 0; +} + +/* + * Cleanup the transmit queue by freeing all frames handled by the device. + */ +void +islpci_mgt_cleanup_transmit(struct net_device *ndev) +{ + islpci_private *priv = netdev_priv(ndev); + isl38xx_control_block *cb = /* volatile not needed */ + (isl38xx_control_block *) priv->control_block; + u32 curr_frag; + +#if VERBOSE > SHOW_ERROR_MESSAGES + DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_cleanup_transmit\n"); +#endif + + /* Only once per cleanup, determine fragment range to + * process. This avoids an endless loop (i.e. lockup) if + * the device became confused, incrementing device_curr_frag + * rapidly. */ + curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_TX_MGMTQ]); + barrier(); + + for (; priv->index_mgmt_tx < curr_frag; priv->index_mgmt_tx++) { + int index = priv->index_mgmt_tx % ISL38XX_CB_MGMT_QSIZE; + struct islpci_membuf *buf = &priv->mgmt_tx[index]; + pci_unmap_single(priv->pdev, buf->pci_addr, buf->size, + PCI_DMA_TODEVICE); + buf->pci_addr = 0; + kfree(buf->mem); + buf->mem = NULL; + buf->size = 0; + } +} + +/* + * Perform one request-response transaction to the device. + */ +int +islpci_mgt_transaction(struct net_device *ndev, + int operation, unsigned long oid, + void *senddata, int sendlen, + struct islpci_mgmtframe **recvframe) +{ + islpci_private *priv = netdev_priv(ndev); + const long wait_cycle_jiffies = msecs_to_jiffies(ISL38XX_WAIT_CYCLE * 10); + long timeout_left = ISL38XX_MAX_WAIT_CYCLES * wait_cycle_jiffies; + int err; + DEFINE_WAIT(wait); + + *recvframe = NULL; + + if (mutex_lock_interruptible(&priv->mgmt_lock)) + return -ERESTARTSYS; + + prepare_to_wait(&priv->mgmt_wqueue, &wait, TASK_UNINTERRUPTIBLE); + err = islpci_mgt_transmit(ndev, operation, oid, senddata, sendlen); + if (err) + goto out; + + err = -ETIMEDOUT; + while (timeout_left > 0) { + int timeleft; + struct islpci_mgmtframe *frame; + + timeleft = schedule_timeout_uninterruptible(wait_cycle_jiffies); + frame = xchg(&priv->mgmt_received, NULL); + if (frame) { + if (frame->header->oid == oid) { + *recvframe = frame; + err = 0; + goto out; + } else { + printk(KERN_DEBUG + "%s: expecting oid 0x%x, received 0x%x.\n", + ndev->name, (unsigned int) oid, + frame->header->oid); + kfree(frame); + frame = NULL; + } + } + if (timeleft == 0) { + printk(KERN_DEBUG + "%s: timeout waiting for mgmt response %lu, " + "triggering device\n", + ndev->name, timeout_left); + islpci_trigger(priv); + } + timeout_left += timeleft - wait_cycle_jiffies; + } + printk(KERN_WARNING "%s: timeout waiting for mgmt response\n", + ndev->name); + + /* TODO: we should reset the device here */ + out: + finish_wait(&priv->mgmt_wqueue, &wait); + mutex_unlock(&priv->mgmt_lock); + return err; +} + diff --git a/drivers/net/wireless/intersil/prism54/islpci_mgt.h b/drivers/net/wireless/intersil/prism54/islpci_mgt.h new file mode 100644 index 000000000000..700c434c8803 --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/islpci_mgt.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2002 Intersil Americas Inc. + * Copyright (C) 2003 Luis R. Rodriguez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#ifndef _ISLPCI_MGT_H +#define _ISLPCI_MGT_H + +#include +#include +#include + +/* + * Function definitions + */ + +#define K_DEBUG(f, m, args...) do { if(f & m) printk(KERN_DEBUG args); } while(0) +#define DEBUG(f, args...) K_DEBUG(f, pc_debug, args) + +extern int pc_debug; +#define init_wds 0 /* help compiler optimize away dead code */ + + +/* General driver definitions */ +#define PCIDEVICE_LATENCY_TIMER_MIN 0x40 +#define PCIDEVICE_LATENCY_TIMER_VAL 0x50 + +/* Debugging verbose definitions */ +#define SHOW_NOTHING 0x00 /* overrules everything */ +#define SHOW_ANYTHING 0xFF +#define SHOW_ERROR_MESSAGES 0x01 +#define SHOW_TRAPS 0x02 +#define SHOW_FUNCTION_CALLS 0x04 +#define SHOW_TRACING 0x08 +#define SHOW_QUEUE_INDEXES 0x10 +#define SHOW_PIMFOR_FRAMES 0x20 +#define SHOW_BUFFER_CONTENTS 0x40 +#define VERBOSE 0x01 + +/* Default card definitions */ +#define CARD_DEFAULT_CHANNEL 6 +#define CARD_DEFAULT_MODE INL_MODE_CLIENT +#define CARD_DEFAULT_IW_MODE IW_MODE_INFRA +#define CARD_DEFAULT_BSSTYPE DOT11_BSSTYPE_INFRA +#define CARD_DEFAULT_CLIENT_SSID "" +#define CARD_DEFAULT_AP_SSID "default" +#define CARD_DEFAULT_KEY1 "default_key_1" +#define CARD_DEFAULT_KEY2 "default_key_2" +#define CARD_DEFAULT_KEY3 "default_key_3" +#define CARD_DEFAULT_KEY4 "default_key_4" +#define CARD_DEFAULT_WEP 0 +#define CARD_DEFAULT_FILTER 0 +#define CARD_DEFAULT_WDS 0 +#define CARD_DEFAULT_AUTHEN DOT11_AUTH_OS +#define CARD_DEFAULT_DOT1X 0 +#define CARD_DEFAULT_MLME_MODE DOT11_MLME_AUTO +#define CARD_DEFAULT_CONFORMANCE OID_INL_CONFORMANCE_NONE +#define CARD_DEFAULT_PROFILE DOT11_PROFILE_MIXED_G_WIFI +#define CARD_DEFAULT_MAXFRAMEBURST DOT11_MAXFRAMEBURST_MIXED_SAFE + +/* PIMFOR package definitions */ +#define PIMFOR_ETHERTYPE 0x8828 +#define PIMFOR_HEADER_SIZE 12 +#define PIMFOR_VERSION 1 +#define PIMFOR_OP_GET 0 +#define PIMFOR_OP_SET 1 +#define PIMFOR_OP_RESPONSE 2 +#define PIMFOR_OP_ERROR 3 +#define PIMFOR_OP_TRAP 4 +#define PIMFOR_OP_RESERVED 5 /* till 255 */ +#define PIMFOR_DEV_ID_MHLI_MIB 0 +#define PIMFOR_FLAG_APPLIC_ORIGIN 0x01 +#define PIMFOR_FLAG_LITTLE_ENDIAN 0x02 + +void display_buffer(char *, int); + +/* + * Type definition section + * + * the structure defines only the header allowing copyless + * frame handling + */ +typedef struct { + u8 version; + u8 operation; + u32 oid; + u8 device_id; + u8 flags; + u32 length; +} __packed +pimfor_header_t; + +/* A received and interrupt-processed management frame, either for + * schedule_work(prism54_process_trap) or for priv->mgmt_received, + * processed by islpci_mgt_transaction(). */ +struct islpci_mgmtframe { + struct net_device *ndev; /* pointer to network device */ + pimfor_header_t *header; /* payload header, points into buf */ + void *data; /* payload ex header, points into buf */ + struct work_struct ws; /* argument for schedule_work() */ + char buf[0]; /* fragment buffer */ +}; + +int +islpci_mgt_receive(struct net_device *ndev); + +int +islpci_mgmt_rx_fill(struct net_device *ndev); + +void +islpci_mgt_cleanup_transmit(struct net_device *ndev); + +int +islpci_mgt_transaction(struct net_device *ndev, + int operation, unsigned long oid, + void *senddata, int sendlen, + struct islpci_mgmtframe **recvframe); + +static inline void +islpci_mgt_release(struct islpci_mgmtframe *frame) +{ + kfree(frame); +} + +#endif /* _ISLPCI_MGT_H */ diff --git a/drivers/net/wireless/intersil/prism54/oid_mgt.c b/drivers/net/wireless/intersil/prism54/oid_mgt.c new file mode 100644 index 000000000000..3a8d2dbcfecd --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/oid_mgt.c @@ -0,0 +1,901 @@ +/* + * Copyright (C) 2003,2004 Aurelien Alleaume + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include +#include + +#include "prismcompat.h" +#include "islpci_dev.h" +#include "islpci_mgt.h" +#include "isl_oid.h" +#include "oid_mgt.h" +#include "isl_ioctl.h" + +/* to convert between channel and freq */ +static const int frequency_list_bg[] = { 2412, 2417, 2422, 2427, 2432, + 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 +}; + +int +channel_of_freq(int f) +{ + int c = 0; + + if ((f >= 2412) && (f <= 2484)) { + while ((c < 14) && (f != frequency_list_bg[c])) + c++; + return (c >= 14) ? 0 : ++c; + } else if ((f >= (int) 5000) && (f <= (int) 6000)) { + return ( (f - 5000) / 5 ); + } else + return 0; +} + +#define OID_STRUCT(name,oid,s,t) [name] = {oid, 0, sizeof(s), t} +#define OID_STRUCT_C(name,oid,s,t) OID_STRUCT(name,oid,s,t | OID_FLAG_CACHED) +#define OID_U32(name,oid) OID_STRUCT(name,oid,u32,OID_TYPE_U32) +#define OID_U32_C(name,oid) OID_STRUCT_C(name,oid,u32,OID_TYPE_U32) +#define OID_STRUCT_MLME(name,oid) OID_STRUCT(name,oid,struct obj_mlme,OID_TYPE_MLME) +#define OID_STRUCT_MLMEEX(name,oid) OID_STRUCT(name,oid,struct obj_mlmeex,OID_TYPE_MLMEEX) + +#define OID_UNKNOWN(name,oid) OID_STRUCT(name,oid,0,0) + +struct oid_t isl_oid[] = { + OID_STRUCT(GEN_OID_MACADDRESS, 0x00000000, u8[6], OID_TYPE_ADDR), + OID_U32(GEN_OID_LINKSTATE, 0x00000001), + OID_UNKNOWN(GEN_OID_WATCHDOG, 0x00000002), + OID_UNKNOWN(GEN_OID_MIBOP, 0x00000003), + OID_UNKNOWN(GEN_OID_OPTIONS, 0x00000004), + OID_UNKNOWN(GEN_OID_LEDCONFIG, 0x00000005), + + /* 802.11 */ + OID_U32_C(DOT11_OID_BSSTYPE, 0x10000000), + OID_STRUCT_C(DOT11_OID_BSSID, 0x10000001, u8[6], OID_TYPE_RAW), + OID_STRUCT_C(DOT11_OID_SSID, 0x10000002, struct obj_ssid, + OID_TYPE_SSID), + OID_U32(DOT11_OID_STATE, 0x10000003), + OID_U32(DOT11_OID_AID, 0x10000004), + OID_STRUCT(DOT11_OID_COUNTRYSTRING, 0x10000005, u8[4], OID_TYPE_RAW), + OID_STRUCT_C(DOT11_OID_SSIDOVERRIDE, 0x10000006, struct obj_ssid, + OID_TYPE_SSID), + + OID_U32(DOT11_OID_MEDIUMLIMIT, 0x11000000), + OID_U32_C(DOT11_OID_BEACONPERIOD, 0x11000001), + OID_U32(DOT11_OID_DTIMPERIOD, 0x11000002), + OID_U32(DOT11_OID_ATIMWINDOW, 0x11000003), + OID_U32(DOT11_OID_LISTENINTERVAL, 0x11000004), + OID_U32(DOT11_OID_CFPPERIOD, 0x11000005), + OID_U32(DOT11_OID_CFPDURATION, 0x11000006), + + OID_U32_C(DOT11_OID_AUTHENABLE, 0x12000000), + OID_U32_C(DOT11_OID_PRIVACYINVOKED, 0x12000001), + OID_U32_C(DOT11_OID_EXUNENCRYPTED, 0x12000002), + OID_U32_C(DOT11_OID_DEFKEYID, 0x12000003), + [DOT11_OID_DEFKEYX] = {0x12000004, 3, sizeof (struct obj_key), + OID_FLAG_CACHED | OID_TYPE_KEY}, /* DOT11_OID_DEFKEY1,...DOT11_OID_DEFKEY4 */ + OID_UNKNOWN(DOT11_OID_STAKEY, 0x12000008), + OID_U32(DOT11_OID_REKEYTHRESHOLD, 0x12000009), + OID_UNKNOWN(DOT11_OID_STASC, 0x1200000a), + + OID_U32(DOT11_OID_PRIVTXREJECTED, 0x1a000000), + OID_U32(DOT11_OID_PRIVRXPLAIN, 0x1a000001), + OID_U32(DOT11_OID_PRIVRXFAILED, 0x1a000002), + OID_U32(DOT11_OID_PRIVRXNOKEY, 0x1a000003), + + OID_U32_C(DOT11_OID_RTSTHRESH, 0x13000000), + OID_U32_C(DOT11_OID_FRAGTHRESH, 0x13000001), + OID_U32_C(DOT11_OID_SHORTRETRIES, 0x13000002), + OID_U32_C(DOT11_OID_LONGRETRIES, 0x13000003), + OID_U32_C(DOT11_OID_MAXTXLIFETIME, 0x13000004), + OID_U32(DOT11_OID_MAXRXLIFETIME, 0x13000005), + OID_U32(DOT11_OID_AUTHRESPTIMEOUT, 0x13000006), + OID_U32(DOT11_OID_ASSOCRESPTIMEOUT, 0x13000007), + + OID_UNKNOWN(DOT11_OID_ALOFT_TABLE, 0x1d000000), + OID_UNKNOWN(DOT11_OID_ALOFT_CTRL_TABLE, 0x1d000001), + OID_UNKNOWN(DOT11_OID_ALOFT_RETREAT, 0x1d000002), + OID_UNKNOWN(DOT11_OID_ALOFT_PROGRESS, 0x1d000003), + OID_U32(DOT11_OID_ALOFT_FIXEDRATE, 0x1d000004), + OID_UNKNOWN(DOT11_OID_ALOFT_RSSIGRAPH, 0x1d000005), + OID_UNKNOWN(DOT11_OID_ALOFT_CONFIG, 0x1d000006), + + [DOT11_OID_VDCFX] = {0x1b000000, 7, 0, 0}, + OID_U32(DOT11_OID_MAXFRAMEBURST, 0x1b000008), + + OID_U32(DOT11_OID_PSM, 0x14000000), + OID_U32(DOT11_OID_CAMTIMEOUT, 0x14000001), + OID_U32(DOT11_OID_RECEIVEDTIMS, 0x14000002), + OID_U32(DOT11_OID_ROAMPREFERENCE, 0x14000003), + + OID_U32(DOT11_OID_BRIDGELOCAL, 0x15000000), + OID_U32(DOT11_OID_CLIENTS, 0x15000001), + OID_U32(DOT11_OID_CLIENTSASSOCIATED, 0x15000002), + [DOT11_OID_CLIENTX] = {0x15000003, 2006, 0, 0}, /* DOT11_OID_CLIENTX,...DOT11_OID_CLIENT2007 */ + + OID_STRUCT(DOT11_OID_CLIENTFIND, 0x150007DB, u8[6], OID_TYPE_ADDR), + OID_STRUCT(DOT11_OID_WDSLINKADD, 0x150007DC, u8[6], OID_TYPE_ADDR), + OID_STRUCT(DOT11_OID_WDSLINKREMOVE, 0x150007DD, u8[6], OID_TYPE_ADDR), + OID_STRUCT(DOT11_OID_EAPAUTHSTA, 0x150007DE, u8[6], OID_TYPE_ADDR), + OID_STRUCT(DOT11_OID_EAPUNAUTHSTA, 0x150007DF, u8[6], OID_TYPE_ADDR), + OID_U32_C(DOT11_OID_DOT1XENABLE, 0x150007E0), + OID_UNKNOWN(DOT11_OID_MICFAILURE, 0x150007E1), + OID_UNKNOWN(DOT11_OID_REKEYINDICATE, 0x150007E2), + + OID_U32(DOT11_OID_MPDUTXSUCCESSFUL, 0x16000000), + OID_U32(DOT11_OID_MPDUTXONERETRY, 0x16000001), + OID_U32(DOT11_OID_MPDUTXMULTIPLERETRIES, 0x16000002), + OID_U32(DOT11_OID_MPDUTXFAILED, 0x16000003), + OID_U32(DOT11_OID_MPDURXSUCCESSFUL, 0x16000004), + OID_U32(DOT11_OID_MPDURXDUPS, 0x16000005), + OID_U32(DOT11_OID_RTSSUCCESSFUL, 0x16000006), + OID_U32(DOT11_OID_RTSFAILED, 0x16000007), + OID_U32(DOT11_OID_ACKFAILED, 0x16000008), + OID_U32(DOT11_OID_FRAMERECEIVES, 0x16000009), + OID_U32(DOT11_OID_FRAMEERRORS, 0x1600000A), + OID_U32(DOT11_OID_FRAMEABORTS, 0x1600000B), + OID_U32(DOT11_OID_FRAMEABORTSPHY, 0x1600000C), + + OID_U32(DOT11_OID_SLOTTIME, 0x17000000), + OID_U32(DOT11_OID_CWMIN, 0x17000001), + OID_U32(DOT11_OID_CWMAX, 0x17000002), + OID_U32(DOT11_OID_ACKWINDOW, 0x17000003), + OID_U32(DOT11_OID_ANTENNARX, 0x17000004), + OID_U32(DOT11_OID_ANTENNATX, 0x17000005), + OID_U32(DOT11_OID_ANTENNADIVERSITY, 0x17000006), + OID_U32_C(DOT11_OID_CHANNEL, 0x17000007), + OID_U32_C(DOT11_OID_EDTHRESHOLD, 0x17000008), + OID_U32(DOT11_OID_PREAMBLESETTINGS, 0x17000009), + OID_STRUCT(DOT11_OID_RATES, 0x1700000A, u8[IWMAX_BITRATES + 1], + OID_TYPE_RAW), + OID_U32(DOT11_OID_CCAMODESUPPORTED, 0x1700000B), + OID_U32(DOT11_OID_CCAMODE, 0x1700000C), + OID_UNKNOWN(DOT11_OID_RSSIVECTOR, 0x1700000D), + OID_UNKNOWN(DOT11_OID_OUTPUTPOWERTABLE, 0x1700000E), + OID_U32(DOT11_OID_OUTPUTPOWER, 0x1700000F), + OID_STRUCT(DOT11_OID_SUPPORTEDRATES, 0x17000010, + u8[IWMAX_BITRATES + 1], OID_TYPE_RAW), + OID_U32_C(DOT11_OID_FREQUENCY, 0x17000011), + [DOT11_OID_SUPPORTEDFREQUENCIES] = + {0x17000012, 0, sizeof (struct obj_frequencies) + + sizeof (u16) * IWMAX_FREQ, OID_TYPE_FREQUENCIES}, + + OID_U32(DOT11_OID_NOISEFLOOR, 0x17000013), + OID_STRUCT(DOT11_OID_FREQUENCYACTIVITY, 0x17000014, u8[IWMAX_FREQ + 1], + OID_TYPE_RAW), + OID_UNKNOWN(DOT11_OID_IQCALIBRATIONTABLE, 0x17000015), + OID_U32(DOT11_OID_NONERPPROTECTION, 0x17000016), + OID_U32(DOT11_OID_SLOTSETTINGS, 0x17000017), + OID_U32(DOT11_OID_NONERPTIMEOUT, 0x17000018), + OID_U32(DOT11_OID_PROFILES, 0x17000019), + OID_STRUCT(DOT11_OID_EXTENDEDRATES, 0x17000020, + u8[IWMAX_BITRATES + 1], OID_TYPE_RAW), + + OID_STRUCT_MLME(DOT11_OID_DEAUTHENTICATE, 0x18000000), + OID_STRUCT_MLME(DOT11_OID_AUTHENTICATE, 0x18000001), + OID_STRUCT_MLME(DOT11_OID_DISASSOCIATE, 0x18000002), + OID_STRUCT_MLME(DOT11_OID_ASSOCIATE, 0x18000003), + OID_UNKNOWN(DOT11_OID_SCAN, 0x18000004), + OID_STRUCT_MLMEEX(DOT11_OID_BEACON, 0x18000005), + OID_STRUCT_MLMEEX(DOT11_OID_PROBE, 0x18000006), + OID_STRUCT_MLMEEX(DOT11_OID_DEAUTHENTICATEEX, 0x18000007), + OID_STRUCT_MLMEEX(DOT11_OID_AUTHENTICATEEX, 0x18000008), + OID_STRUCT_MLMEEX(DOT11_OID_DISASSOCIATEEX, 0x18000009), + OID_STRUCT_MLMEEX(DOT11_OID_ASSOCIATEEX, 0x1800000A), + OID_STRUCT_MLMEEX(DOT11_OID_REASSOCIATE, 0x1800000B), + OID_STRUCT_MLMEEX(DOT11_OID_REASSOCIATEEX, 0x1800000C), + + OID_U32(DOT11_OID_NONERPSTATUS, 0x1E000000), + + OID_U32(DOT11_OID_STATIMEOUT, 0x19000000), + OID_U32_C(DOT11_OID_MLMEAUTOLEVEL, 0x19000001), + OID_U32(DOT11_OID_BSSTIMEOUT, 0x19000002), + [DOT11_OID_ATTACHMENT] = {0x19000003, 0, + sizeof(struct obj_attachment), OID_TYPE_ATTACH}, + OID_STRUCT_C(DOT11_OID_PSMBUFFER, 0x19000004, struct obj_buffer, + OID_TYPE_BUFFER), + + OID_U32(DOT11_OID_BSSS, 0x1C000000), + [DOT11_OID_BSSX] = {0x1C000001, 63, sizeof (struct obj_bss), + OID_TYPE_BSS}, /*DOT11_OID_BSS1,...,DOT11_OID_BSS64 */ + OID_STRUCT(DOT11_OID_BSSFIND, 0x1C000042, struct obj_bss, OID_TYPE_BSS), + [DOT11_OID_BSSLIST] = {0x1C000043, 0, sizeof (struct + obj_bsslist) + + sizeof (struct obj_bss[IWMAX_BSS]), + OID_TYPE_BSSLIST}, + + OID_UNKNOWN(OID_INL_TUNNEL, 0xFF020000), + OID_UNKNOWN(OID_INL_MEMADDR, 0xFF020001), + OID_UNKNOWN(OID_INL_MEMORY, 0xFF020002), + OID_U32_C(OID_INL_MODE, 0xFF020003), + OID_UNKNOWN(OID_INL_COMPONENT_NR, 0xFF020004), + OID_STRUCT(OID_INL_VERSION, 0xFF020005, u8[8], OID_TYPE_RAW), + OID_UNKNOWN(OID_INL_INTERFACE_ID, 0xFF020006), + OID_UNKNOWN(OID_INL_COMPONENT_ID, 0xFF020007), + OID_U32_C(OID_INL_CONFIG, 0xFF020008), + OID_U32_C(OID_INL_DOT11D_CONFORMANCE, 0xFF02000C), + OID_U32(OID_INL_PHYCAPABILITIES, 0xFF02000D), + OID_U32_C(OID_INL_OUTPUTPOWER, 0xFF02000F), + +}; + +int +mgt_init(islpci_private *priv) +{ + int i; + + priv->mib = kcalloc(OID_NUM_LAST, sizeof (void *), GFP_KERNEL); + if (!priv->mib) + return -ENOMEM; + + /* Alloc the cache */ + for (i = 0; i < OID_NUM_LAST; i++) { + if (isl_oid[i].flags & OID_FLAG_CACHED) { + priv->mib[i] = kzalloc(isl_oid[i].size * + (isl_oid[i].range + 1), + GFP_KERNEL); + if (!priv->mib[i]) + return -ENOMEM; + } else + priv->mib[i] = NULL; + } + + init_rwsem(&priv->mib_sem); + prism54_mib_init(priv); + + return 0; +} + +void +mgt_clean(islpci_private *priv) +{ + int i; + + if (!priv->mib) + return; + for (i = 0; i < OID_NUM_LAST; i++) { + kfree(priv->mib[i]); + priv->mib[i] = NULL; + } + kfree(priv->mib); + priv->mib = NULL; +} + +void +mgt_le_to_cpu(int type, void *data) +{ + switch (type) { + case OID_TYPE_U32: + *(u32 *) data = le32_to_cpu(*(u32 *) data); + break; + case OID_TYPE_BUFFER:{ + struct obj_buffer *buff = data; + buff->size = le32_to_cpu(buff->size); + buff->addr = le32_to_cpu(buff->addr); + break; + } + case OID_TYPE_BSS:{ + struct obj_bss *bss = data; + bss->age = le16_to_cpu(bss->age); + bss->channel = le16_to_cpu(bss->channel); + bss->capinfo = le16_to_cpu(bss->capinfo); + bss->rates = le16_to_cpu(bss->rates); + bss->basic_rates = le16_to_cpu(bss->basic_rates); + break; + } + case OID_TYPE_BSSLIST:{ + struct obj_bsslist *list = data; + int i; + list->nr = le32_to_cpu(list->nr); + for (i = 0; i < list->nr; i++) + mgt_le_to_cpu(OID_TYPE_BSS, &list->bsslist[i]); + break; + } + case OID_TYPE_FREQUENCIES:{ + struct obj_frequencies *freq = data; + int i; + freq->nr = le16_to_cpu(freq->nr); + for (i = 0; i < freq->nr; i++) + freq->mhz[i] = le16_to_cpu(freq->mhz[i]); + break; + } + case OID_TYPE_MLME:{ + struct obj_mlme *mlme = data; + mlme->id = le16_to_cpu(mlme->id); + mlme->state = le16_to_cpu(mlme->state); + mlme->code = le16_to_cpu(mlme->code); + break; + } + case OID_TYPE_MLMEEX:{ + struct obj_mlmeex *mlme = data; + mlme->id = le16_to_cpu(mlme->id); + mlme->state = le16_to_cpu(mlme->state); + mlme->code = le16_to_cpu(mlme->code); + mlme->size = le16_to_cpu(mlme->size); + break; + } + case OID_TYPE_ATTACH:{ + struct obj_attachment *attach = data; + attach->id = le16_to_cpu(attach->id); + attach->size = le16_to_cpu(attach->size); + break; + } + case OID_TYPE_SSID: + case OID_TYPE_KEY: + case OID_TYPE_ADDR: + case OID_TYPE_RAW: + break; + default: + BUG(); + } +} + +static void +mgt_cpu_to_le(int type, void *data) +{ + switch (type) { + case OID_TYPE_U32: + *(u32 *) data = cpu_to_le32(*(u32 *) data); + break; + case OID_TYPE_BUFFER:{ + struct obj_buffer *buff = data; + buff->size = cpu_to_le32(buff->size); + buff->addr = cpu_to_le32(buff->addr); + break; + } + case OID_TYPE_BSS:{ + struct obj_bss *bss = data; + bss->age = cpu_to_le16(bss->age); + bss->channel = cpu_to_le16(bss->channel); + bss->capinfo = cpu_to_le16(bss->capinfo); + bss->rates = cpu_to_le16(bss->rates); + bss->basic_rates = cpu_to_le16(bss->basic_rates); + break; + } + case OID_TYPE_BSSLIST:{ + struct obj_bsslist *list = data; + int i; + list->nr = cpu_to_le32(list->nr); + for (i = 0; i < list->nr; i++) + mgt_cpu_to_le(OID_TYPE_BSS, &list->bsslist[i]); + break; + } + case OID_TYPE_FREQUENCIES:{ + struct obj_frequencies *freq = data; + int i; + freq->nr = cpu_to_le16(freq->nr); + for (i = 0; i < freq->nr; i++) + freq->mhz[i] = cpu_to_le16(freq->mhz[i]); + break; + } + case OID_TYPE_MLME:{ + struct obj_mlme *mlme = data; + mlme->id = cpu_to_le16(mlme->id); + mlme->state = cpu_to_le16(mlme->state); + mlme->code = cpu_to_le16(mlme->code); + break; + } + case OID_TYPE_MLMEEX:{ + struct obj_mlmeex *mlme = data; + mlme->id = cpu_to_le16(mlme->id); + mlme->state = cpu_to_le16(mlme->state); + mlme->code = cpu_to_le16(mlme->code); + mlme->size = cpu_to_le16(mlme->size); + break; + } + case OID_TYPE_ATTACH:{ + struct obj_attachment *attach = data; + attach->id = cpu_to_le16(attach->id); + attach->size = cpu_to_le16(attach->size); + break; + } + case OID_TYPE_SSID: + case OID_TYPE_KEY: + case OID_TYPE_ADDR: + case OID_TYPE_RAW: + break; + default: + BUG(); + } +} + +/* Note : data is modified during this function */ + +int +mgt_set_request(islpci_private *priv, enum oid_num_t n, int extra, void *data) +{ + int ret = 0; + struct islpci_mgmtframe *response = NULL; + int response_op = PIMFOR_OP_ERROR; + int dlen; + void *cache, *_data = data; + u32 oid; + + BUG_ON(OID_NUM_LAST <= n); + BUG_ON(extra > isl_oid[n].range); + + if (!priv->mib) + /* memory has been freed */ + return -1; + + dlen = isl_oid[n].size; + cache = priv->mib[n]; + cache += (cache ? extra * dlen : 0); + oid = isl_oid[n].oid + extra; + + if (_data == NULL) + /* we are requested to re-set a cached value */ + _data = cache; + else + mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, _data); + /* If we are going to write to the cache, we don't want anyone to read + * it -> acquire write lock. + * Else we could acquire a read lock to be sure we don't bother the + * commit process (which takes a write lock). But I'm not sure if it's + * needed. + */ + if (cache) + down_write(&priv->mib_sem); + + if (islpci_get_state(priv) >= PRV_STATE_READY) { + ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, oid, + _data, dlen, &response); + if (!ret) { + response_op = response->header->operation; + islpci_mgt_release(response); + } + if (ret || response_op == PIMFOR_OP_ERROR) + ret = -EIO; + } else if (!cache) + ret = -EIO; + + if (cache) { + if (!ret && data) + memcpy(cache, _data, dlen); + up_write(&priv->mib_sem); + } + + /* re-set given data to what it was */ + if (data) + mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, data); + + return ret; +} + +/* None of these are cached */ +int +mgt_set_varlen(islpci_private *priv, enum oid_num_t n, void *data, int extra_len) +{ + int ret = 0; + struct islpci_mgmtframe *response; + int response_op = PIMFOR_OP_ERROR; + int dlen; + u32 oid; + + BUG_ON(OID_NUM_LAST <= n); + + dlen = isl_oid[n].size; + oid = isl_oid[n].oid; + + mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, data); + + if (islpci_get_state(priv) >= PRV_STATE_READY) { + ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, oid, + data, dlen + extra_len, &response); + if (!ret) { + response_op = response->header->operation; + islpci_mgt_release(response); + } + if (ret || response_op == PIMFOR_OP_ERROR) + ret = -EIO; + } else + ret = -EIO; + + /* re-set given data to what it was */ + if (data) + mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, data); + + return ret; +} + +int +mgt_get_request(islpci_private *priv, enum oid_num_t n, int extra, void *data, + union oid_res_t *res) +{ + + int ret = -EIO; + int reslen = 0; + struct islpci_mgmtframe *response = NULL; + + int dlen; + void *cache, *_res = NULL; + u32 oid; + + BUG_ON(OID_NUM_LAST <= n); + BUG_ON(extra > isl_oid[n].range); + + res->ptr = NULL; + + if (!priv->mib) + /* memory has been freed */ + return -1; + + dlen = isl_oid[n].size; + cache = priv->mib[n]; + cache += cache ? extra * dlen : 0; + oid = isl_oid[n].oid + extra; + reslen = dlen; + + if (cache) + down_read(&priv->mib_sem); + + if (islpci_get_state(priv) >= PRV_STATE_READY) { + ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET, + oid, data, dlen, &response); + if (ret || !response || + response->header->operation == PIMFOR_OP_ERROR) { + if (response) + islpci_mgt_release(response); + ret = -EIO; + } + if (!ret) { + _res = response->data; + reslen = response->header->length; + } + } else if (cache) { + _res = cache; + ret = 0; + } + if ((isl_oid[n].flags & OID_FLAG_TYPE) == OID_TYPE_U32) + res->u = ret ? 0 : le32_to_cpu(*(u32 *) _res); + else { + res->ptr = kmalloc(reslen, GFP_KERNEL); + BUG_ON(res->ptr == NULL); + if (ret) + memset(res->ptr, 0, reslen); + else { + memcpy(res->ptr, _res, reslen); + mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, + res->ptr); + } + } + if (cache) + up_read(&priv->mib_sem); + + if (response && !ret) + islpci_mgt_release(response); + + if (reslen > isl_oid[n].size) + printk(KERN_DEBUG + "mgt_get_request(0x%x): received data length was bigger " + "than expected (%d > %d). Memory is probably corrupted...", + oid, reslen, isl_oid[n].size); + + return ret; +} + +/* lock outside */ +int +mgt_commit_list(islpci_private *priv, enum oid_num_t *l, int n) +{ + int i, ret = 0; + struct islpci_mgmtframe *response; + + for (i = 0; i < n; i++) { + struct oid_t *t = &(isl_oid[l[i]]); + void *data = priv->mib[l[i]]; + int j = 0; + u32 oid = t->oid; + BUG_ON(data == NULL); + while (j <= t->range) { + int r = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, + oid, data, t->size, + &response); + if (response) { + r |= (response->header->operation == PIMFOR_OP_ERROR); + islpci_mgt_release(response); + } + if (r) + printk(KERN_ERR "%s: mgt_commit_list: failure. " + "oid=%08x err=%d\n", + priv->ndev->name, oid, r); + ret |= r; + j++; + oid++; + data += t->size; + } + } + return ret; +} + +/* Lock outside */ + +void +mgt_set(islpci_private *priv, enum oid_num_t n, void *data) +{ + BUG_ON(OID_NUM_LAST <= n); + BUG_ON(priv->mib[n] == NULL); + + memcpy(priv->mib[n], data, isl_oid[n].size); + mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, priv->mib[n]); +} + +void +mgt_get(islpci_private *priv, enum oid_num_t n, void *res) +{ + BUG_ON(OID_NUM_LAST <= n); + BUG_ON(priv->mib[n] == NULL); + BUG_ON(res == NULL); + + memcpy(res, priv->mib[n], isl_oid[n].size); + mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, res); +} + +/* Commits the cache. Lock outside. */ + +static enum oid_num_t commit_part1[] = { + OID_INL_CONFIG, + OID_INL_MODE, + DOT11_OID_BSSTYPE, + DOT11_OID_CHANNEL, + DOT11_OID_MLMEAUTOLEVEL +}; + +static enum oid_num_t commit_part2[] = { + DOT11_OID_SSID, + DOT11_OID_PSMBUFFER, + DOT11_OID_AUTHENABLE, + DOT11_OID_PRIVACYINVOKED, + DOT11_OID_EXUNENCRYPTED, + DOT11_OID_DEFKEYX, /* MULTIPLE */ + DOT11_OID_DEFKEYID, + DOT11_OID_DOT1XENABLE, + OID_INL_DOT11D_CONFORMANCE, + /* Do not initialize this - fw < 1.0.4.3 rejects it + OID_INL_OUTPUTPOWER, + */ +}; + +/* update the MAC addr. */ +static int +mgt_update_addr(islpci_private *priv) +{ + struct islpci_mgmtframe *res; + int ret; + + ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET, + isl_oid[GEN_OID_MACADDRESS].oid, NULL, + isl_oid[GEN_OID_MACADDRESS].size, &res); + + if ((ret == 0) && res && (res->header->operation != PIMFOR_OP_ERROR)) + memcpy(priv->ndev->dev_addr, res->data, ETH_ALEN); + else + ret = -EIO; + if (res) + islpci_mgt_release(res); + + if (ret) + printk(KERN_ERR "%s: mgt_update_addr: failure\n", priv->ndev->name); + return ret; +} + +int +mgt_commit(islpci_private *priv) +{ + int rvalue; + enum oid_num_t u; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return 0; + + rvalue = mgt_commit_list(priv, commit_part1, ARRAY_SIZE(commit_part1)); + + if (priv->iw_mode != IW_MODE_MONITOR) + rvalue |= mgt_commit_list(priv, commit_part2, ARRAY_SIZE(commit_part2)); + + u = OID_INL_MODE; + rvalue |= mgt_commit_list(priv, &u, 1); + rvalue |= mgt_update_addr(priv); + + if (rvalue) { + /* some request have failed. The device might be in an + incoherent state. We should reset it ! */ + printk(KERN_DEBUG "%s: mgt_commit: failure\n", priv->ndev->name); + } + return rvalue; +} + +/* The following OIDs need to be "unlatched": + * + * MEDIUMLIMIT,BEACONPERIOD,DTIMPERIOD,ATIMWINDOW,LISTENINTERVAL + * FREQUENCY,EXTENDEDRATES. + * + * The way to do this is to set ESSID. Note though that they may get + * unlatch before though by setting another OID. */ +#if 0 +void +mgt_unlatch_all(islpci_private *priv) +{ + u32 u; + int rvalue = 0; + + if (islpci_get_state(priv) < PRV_STATE_INIT) + return; + + u = DOT11_OID_SSID; + rvalue = mgt_commit_list(priv, &u, 1); + /* Necessary if in MANUAL RUN mode? */ +#if 0 + u = OID_INL_MODE; + rvalue |= mgt_commit_list(priv, &u, 1); + + u = DOT11_OID_MLMEAUTOLEVEL; + rvalue |= mgt_commit_list(priv, &u, 1); + + u = OID_INL_MODE; + rvalue |= mgt_commit_list(priv, &u, 1); +#endif + + if (rvalue) + printk(KERN_DEBUG "%s: Unlatching OIDs failed\n", priv->ndev->name); +} +#endif + +/* This will tell you if you are allowed to answer a mlme(ex) request .*/ + +int +mgt_mlme_answer(islpci_private *priv) +{ + u32 mlmeautolevel; + /* Acquire a read lock because if we are in a mode change, it's + * possible to answer true, while the card is leaving master to managed + * mode. Answering to a mlme in this situation could hang the card. + */ + down_read(&priv->mib_sem); + mlmeautolevel = + le32_to_cpu(*(u32 *) priv->mib[DOT11_OID_MLMEAUTOLEVEL]); + up_read(&priv->mib_sem); + + return ((priv->iw_mode == IW_MODE_MASTER) && + (mlmeautolevel >= DOT11_MLME_INTERMEDIATE)); +} + +enum oid_num_t +mgt_oidtonum(u32 oid) +{ + int i; + + for (i = 0; i < OID_NUM_LAST; i++) + if (isl_oid[i].oid == oid) + return i; + + printk(KERN_DEBUG "looking for an unknown oid 0x%x", oid); + + return OID_NUM_LAST; +} + +int +mgt_response_to_str(enum oid_num_t n, union oid_res_t *r, char *str) +{ + switch (isl_oid[n].flags & OID_FLAG_TYPE) { + case OID_TYPE_U32: + return snprintf(str, PRIV_STR_SIZE, "%u\n", r->u); + case OID_TYPE_BUFFER:{ + struct obj_buffer *buff = r->ptr; + return snprintf(str, PRIV_STR_SIZE, + "size=%u\naddr=0x%X\n", buff->size, + buff->addr); + } + break; + case OID_TYPE_BSS:{ + struct obj_bss *bss = r->ptr; + return snprintf(str, PRIV_STR_SIZE, + "age=%u\nchannel=%u\n" + "capinfo=0x%X\nrates=0x%X\n" + "basic_rates=0x%X\n", bss->age, + bss->channel, bss->capinfo, + bss->rates, bss->basic_rates); + } + break; + case OID_TYPE_BSSLIST:{ + struct obj_bsslist *list = r->ptr; + int i, k; + k = snprintf(str, PRIV_STR_SIZE, "nr=%u\n", list->nr); + for (i = 0; i < list->nr; i++) + k += snprintf(str + k, PRIV_STR_SIZE - k, + "bss[%u] :\nage=%u\nchannel=%u\n" + "capinfo=0x%X\nrates=0x%X\n" + "basic_rates=0x%X\n", + i, list->bsslist[i].age, + list->bsslist[i].channel, + list->bsslist[i].capinfo, + list->bsslist[i].rates, + list->bsslist[i].basic_rates); + return k; + } + break; + case OID_TYPE_FREQUENCIES:{ + struct obj_frequencies *freq = r->ptr; + int i, t; + printk("nr : %u\n", freq->nr); + t = snprintf(str, PRIV_STR_SIZE, "nr=%u\n", freq->nr); + for (i = 0; i < freq->nr; i++) + t += snprintf(str + t, PRIV_STR_SIZE - t, + "mhz[%u]=%u\n", i, freq->mhz[i]); + return t; + } + break; + case OID_TYPE_MLME:{ + struct obj_mlme *mlme = r->ptr; + return snprintf(str, PRIV_STR_SIZE, + "id=0x%X\nstate=0x%X\ncode=0x%X\n", + mlme->id, mlme->state, mlme->code); + } + break; + case OID_TYPE_MLMEEX:{ + struct obj_mlmeex *mlme = r->ptr; + return snprintf(str, PRIV_STR_SIZE, + "id=0x%X\nstate=0x%X\n" + "code=0x%X\nsize=0x%X\n", mlme->id, + mlme->state, mlme->code, mlme->size); + } + break; + case OID_TYPE_ATTACH:{ + struct obj_attachment *attach = r->ptr; + return snprintf(str, PRIV_STR_SIZE, + "id=%d\nsize=%d\n", + attach->id, + attach->size); + } + break; + case OID_TYPE_SSID:{ + struct obj_ssid *ssid = r->ptr; + return snprintf(str, PRIV_STR_SIZE, + "length=%u\noctets=%.*s\n", + ssid->length, ssid->length, + ssid->octets); + } + break; + case OID_TYPE_KEY:{ + struct obj_key *key = r->ptr; + int t, i; + t = snprintf(str, PRIV_STR_SIZE, + "type=0x%X\nlength=0x%X\nkey=0x", + key->type, key->length); + for (i = 0; i < key->length; i++) + t += snprintf(str + t, PRIV_STR_SIZE - t, + "%02X:", key->key[i]); + t += snprintf(str + t, PRIV_STR_SIZE - t, "\n"); + return t; + } + break; + case OID_TYPE_RAW: + case OID_TYPE_ADDR:{ + unsigned char *buff = r->ptr; + int t, i; + t = snprintf(str, PRIV_STR_SIZE, "hex data="); + for (i = 0; i < isl_oid[n].size; i++) + t += snprintf(str + t, PRIV_STR_SIZE - t, + "%02X:", buff[i]); + t += snprintf(str + t, PRIV_STR_SIZE - t, "\n"); + return t; + } + break; + default: + BUG(); + } + return 0; +} diff --git a/drivers/net/wireless/intersil/prism54/oid_mgt.h b/drivers/net/wireless/intersil/prism54/oid_mgt.h new file mode 100644 index 000000000000..cf5141df8474 --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/oid_mgt.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2003 Aurelien Alleaume + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#if !defined(_OID_MGT_H) +#define _OID_MGT_H + +#include "isl_oid.h" +#include "islpci_dev.h" + +extern struct oid_t isl_oid[]; + +int mgt_init(islpci_private *); + +void mgt_clean(islpci_private *); + +/* I don't know where to put these 2 */ +extern const int frequency_list_a[]; +int channel_of_freq(int); + +void mgt_le_to_cpu(int, void *); + +int mgt_set_request(islpci_private *, enum oid_num_t, int, void *); +int mgt_set_varlen(islpci_private *, enum oid_num_t, void *, int); + + +int mgt_get_request(islpci_private *, enum oid_num_t, int, void *, + union oid_res_t *); + +int mgt_commit_list(islpci_private *, enum oid_num_t *, int); + +void mgt_set(islpci_private *, enum oid_num_t, void *); + +void mgt_get(islpci_private *, enum oid_num_t, void *); + +int mgt_commit(islpci_private *); + +int mgt_mlme_answer(islpci_private *); + +enum oid_num_t mgt_oidtonum(u32 oid); + +int mgt_response_to_str(enum oid_num_t, union oid_res_t *, char *); + +#endif /* !defined(_OID_MGT_H) */ +/* EOF */ diff --git a/drivers/net/wireless/intersil/prism54/prismcompat.h b/drivers/net/wireless/intersil/prism54/prismcompat.h new file mode 100644 index 000000000000..bc1401eb4b9d --- /dev/null +++ b/drivers/net/wireless/intersil/prism54/prismcompat.h @@ -0,0 +1,42 @@ +/* + * (C) 2004 Margit Schubert-While + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +/* + * Compatibility header file to aid support of different kernel versions + */ + +#ifdef PRISM54_COMPAT24 +#include "prismcompat24.h" +#else /* PRISM54_COMPAT24 */ + +#ifndef _PRISM_COMPAT_H +#define _PRISM_COMPAT_H + +#include +#include +#include +#include +#include + +#ifndef __iomem +#define __iomem +#endif + +#define PRISM_FW_PDEV &priv->pdev->dev + +#endif /* _PRISM_COMPAT_H */ +#endif /* PRISM54_COMPAT24 */ diff --git a/drivers/net/wireless/prism54/Makefile b/drivers/net/wireless/prism54/Makefile deleted file mode 100644 index fad305c76737..000000000000 --- a/drivers/net/wireless/prism54/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# $Id: Makefile.k26,v 1.7 2004/01/30 16:24:00 ajfa Exp $ - -prism54-objs := islpci_eth.o islpci_mgt.o \ - isl_38xx.o isl_ioctl.o islpci_dev.o \ - islpci_hotplug.o oid_mgt.o - -obj-$(CONFIG_PRISM54) += prism54.o - diff --git a/drivers/net/wireless/prism54/isl_38xx.c b/drivers/net/wireless/prism54/isl_38xx.c deleted file mode 100644 index 333c1a2f882e..000000000000 --- a/drivers/net/wireless/prism54/isl_38xx.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (C) 2002 Intersil Americas Inc. - * Copyright (C) 2003-2004 Luis R. Rodriguez _ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#include -#include -#include - -#include -#include - -#include "prismcompat.h" -#include "isl_38xx.h" -#include "islpci_dev.h" -#include "islpci_mgt.h" - -/****************************************************************************** - Device Interface & Control functions -******************************************************************************/ - -/** - * isl38xx_disable_interrupts - disable all interrupts - * @device: pci memory base address - * - * Instructs the device to disable all interrupt reporting by asserting - * the IRQ line. New events may still show up in the interrupt identification - * register located at offset %ISL38XX_INT_IDENT_REG. - */ -void -isl38xx_disable_interrupts(void __iomem *device) -{ - isl38xx_w32_flush(device, 0x00000000, ISL38XX_INT_EN_REG); - udelay(ISL38XX_WRITEIO_DELAY); -} - -void -isl38xx_handle_sleep_request(isl38xx_control_block *control_block, - int *powerstate, void __iomem *device_base) -{ - /* device requests to go into sleep mode - * check whether the transmit queues for data and management are empty */ - if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ)) - /* data tx queue not empty */ - return; - - if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ)) - /* management tx queue not empty */ - return; - - /* check also whether received frames are pending */ - if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_DATA_LQ)) - /* data rx queue not empty */ - return; - - if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_MGMTQ)) - /* management rx queue not empty */ - return; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "Device going to sleep mode\n"); -#endif - - /* all queues are empty, allow the device to go into sleep mode */ - *powerstate = ISL38XX_PSM_POWERSAVE_STATE; - - /* assert the Sleep interrupt in the Device Interrupt Register */ - isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_SLEEP, - ISL38XX_DEV_INT_REG); - udelay(ISL38XX_WRITEIO_DELAY); -} - -void -isl38xx_handle_wakeup(isl38xx_control_block *control_block, - int *powerstate, void __iomem *device_base) -{ - /* device is in active state, update the powerstate flag */ - *powerstate = ISL38XX_PSM_ACTIVE_STATE; - - /* now check whether there are frames pending for the card */ - if (!isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ) - && !isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ)) - return; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_ANYTHING, "Wake up handler trigger the device\n"); -#endif - - /* either data or management transmit queue has a frame pending - * trigger the device by setting the Update bit in the Device Int reg */ - isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE, - ISL38XX_DEV_INT_REG); - udelay(ISL38XX_WRITEIO_DELAY); -} - -void -isl38xx_trigger_device(int asleep, void __iomem *device_base) -{ - u32 reg; - -#if VERBOSE > SHOW_ERROR_MESSAGES - u32 counter = 0; - struct timeval current_time; - DEBUG(SHOW_FUNCTION_CALLS, "isl38xx trigger device\n"); -#endif - - /* check whether the device is in power save mode */ - if (asleep) { - /* device is in powersave, trigger the device for wakeup */ -#if VERBOSE > SHOW_ERROR_MESSAGES - do_gettimeofday(¤t_time); - DEBUG(SHOW_TRACING, "%08li.%08li Device wakeup triggered\n", - current_time.tv_sec, (long)current_time.tv_usec); - - DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n", - current_time.tv_sec, (long)current_time.tv_usec, - readl(device_base + ISL38XX_CTRL_STAT_REG)); -#endif - - reg = readl(device_base + ISL38XX_INT_IDENT_REG); - if (reg == 0xabadface) { -#if VERBOSE > SHOW_ERROR_MESSAGES - do_gettimeofday(¤t_time); - DEBUG(SHOW_TRACING, - "%08li.%08li Device register abadface\n", - current_time.tv_sec, (long)current_time.tv_usec); -#endif - /* read the Device Status Register until Sleepmode bit is set */ - while (reg = readl(device_base + ISL38XX_CTRL_STAT_REG), - (reg & ISL38XX_CTRL_STAT_SLEEPMODE) == 0) { - udelay(ISL38XX_WRITEIO_DELAY); -#if VERBOSE > SHOW_ERROR_MESSAGES - counter++; -#endif - } - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, - "%08li.%08li Device register read %08x\n", - current_time.tv_sec, (long)current_time.tv_usec, - readl(device_base + ISL38XX_CTRL_STAT_REG)); - do_gettimeofday(¤t_time); - DEBUG(SHOW_TRACING, - "%08li.%08li Device asleep counter %i\n", - current_time.tv_sec, (long)current_time.tv_usec, - counter); -#endif - } - /* assert the Wakeup interrupt in the Device Interrupt Register */ - isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_WAKEUP, - ISL38XX_DEV_INT_REG); - -#if VERBOSE > SHOW_ERROR_MESSAGES - udelay(ISL38XX_WRITEIO_DELAY); - - /* perform another read on the Device Status Register */ - reg = readl(device_base + ISL38XX_CTRL_STAT_REG); - do_gettimeofday(¤t_time); - DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n", - current_time.tv_sec, (long)current_time.tv_usec, reg); -#endif - } else { - /* device is (still) awake */ -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "Device is in active state\n"); -#endif - /* trigger the device by setting the Update bit in the Device Int reg */ - - isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE, - ISL38XX_DEV_INT_REG); - } -} - -void -isl38xx_interface_reset(void __iomem *device_base, dma_addr_t host_address) -{ -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_FUNCTION_CALLS, "isl38xx_interface_reset\n"); -#endif - - /* load the address of the control block in the device */ - isl38xx_w32_flush(device_base, host_address, ISL38XX_CTRL_BLK_BASE_REG); - udelay(ISL38XX_WRITEIO_DELAY); - - /* set the reset bit in the Device Interrupt Register */ - isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_RESET, ISL38XX_DEV_INT_REG); - udelay(ISL38XX_WRITEIO_DELAY); - - /* enable the interrupt for detecting initialization */ - - /* Note: Do not enable other interrupts here. We want the - * device to have come up first 100% before allowing any other - * interrupts. */ - isl38xx_w32_flush(device_base, ISL38XX_INT_IDENT_INIT, ISL38XX_INT_EN_REG); - udelay(ISL38XX_WRITEIO_DELAY); /* allow complete full reset */ -} - -void -isl38xx_enable_common_interrupts(void __iomem *device_base) -{ - u32 reg; - - reg = ISL38XX_INT_IDENT_UPDATE | ISL38XX_INT_IDENT_SLEEP | - ISL38XX_INT_IDENT_WAKEUP; - isl38xx_w32_flush(device_base, reg, ISL38XX_INT_EN_REG); - udelay(ISL38XX_WRITEIO_DELAY); -} - -int -isl38xx_in_queue(isl38xx_control_block *cb, int queue) -{ - const s32 delta = (le32_to_cpu(cb->driver_curr_frag[queue]) - - le32_to_cpu(cb->device_curr_frag[queue])); - - /* determine the amount of fragments in the queue depending on the type - * of the queue, either transmit or receive */ - - BUG_ON(delta < 0); /* driver ptr must be ahead of device ptr */ - - switch (queue) { - /* send queues */ - case ISL38XX_CB_TX_MGMTQ: - BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE); - - case ISL38XX_CB_TX_DATA_LQ: - case ISL38XX_CB_TX_DATA_HQ: - BUG_ON(delta > ISL38XX_CB_TX_QSIZE); - return delta; - - /* receive queues */ - case ISL38XX_CB_RX_MGMTQ: - BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE); - return ISL38XX_CB_MGMT_QSIZE - delta; - - case ISL38XX_CB_RX_DATA_LQ: - case ISL38XX_CB_RX_DATA_HQ: - BUG_ON(delta > ISL38XX_CB_RX_QSIZE); - return ISL38XX_CB_RX_QSIZE - delta; - } - BUG(); - return 0; -} diff --git a/drivers/net/wireless/prism54/isl_38xx.h b/drivers/net/wireless/prism54/isl_38xx.h deleted file mode 100644 index 547ab885610b..000000000000 --- a/drivers/net/wireless/prism54/isl_38xx.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2002 Intersil Americas Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#ifndef _ISL_38XX_H -#define _ISL_38XX_H - -#include -#include - -#define ISL38XX_CB_RX_QSIZE 8 -#define ISL38XX_CB_TX_QSIZE 32 - -/* ISL38XX Access Point Specific definitions */ -#define ISL38XX_MAX_WDS_LINKS 8 - -/* ISL38xx Client Specific definitions */ -#define ISL38XX_PSM_ACTIVE_STATE 0 -#define ISL38XX_PSM_POWERSAVE_STATE 1 - -/* ISL38XX Host Interface Definitions */ -#define ISL38XX_PCI_MEM_SIZE 0x02000 -#define ISL38XX_MEMORY_WINDOW_SIZE 0x01000 -#define ISL38XX_DEV_FIRMWARE_ADDRES 0x20000 -#define ISL38XX_WRITEIO_DELAY 10 /* in us */ -#define ISL38XX_RESET_DELAY 50 /* in ms */ -#define ISL38XX_WAIT_CYCLE 10 /* in 10ms */ -#define ISL38XX_MAX_WAIT_CYCLES 10 - -/* PCI Memory Area */ -#define ISL38XX_HARDWARE_REG 0x0000 -#define ISL38XX_CARDBUS_CIS 0x0800 -#define ISL38XX_DIRECT_MEM_WIN 0x1000 - -/* Hardware registers */ -#define ISL38XX_DEV_INT_REG 0x0000 -#define ISL38XX_INT_IDENT_REG 0x0010 -#define ISL38XX_INT_ACK_REG 0x0014 -#define ISL38XX_INT_EN_REG 0x0018 -#define ISL38XX_GEN_PURP_COM_REG_1 0x0020 -#define ISL38XX_GEN_PURP_COM_REG_2 0x0024 -#define ISL38XX_CTRL_BLK_BASE_REG ISL38XX_GEN_PURP_COM_REG_1 -#define ISL38XX_DIR_MEM_BASE_REG 0x0030 -#define ISL38XX_CTRL_STAT_REG 0x0078 - -/* High end mobos queue up pci writes, the following - * is used to "read" from after a write to force flush */ -#define ISL38XX_PCI_POSTING_FLUSH ISL38XX_INT_EN_REG - -/** - * isl38xx_w32_flush - PCI iomem write helper - * @base: (host) memory base address of the device - * @val: 32bit value (host order) to write - * @offset: byte offset into @base to write value to - * - * This helper takes care of writing a 32bit datum to the - * specified offset into the device's pci memory space, and making sure - * the pci memory buffers get flushed by performing one harmless read - * from the %ISL38XX_PCI_POSTING_FLUSH offset. - */ -static inline void -isl38xx_w32_flush(void __iomem *base, u32 val, unsigned long offset) -{ - writel(val, base + offset); - (void) readl(base + ISL38XX_PCI_POSTING_FLUSH); -} - -/* Device Interrupt register bits */ -#define ISL38XX_DEV_INT_RESET 0x0001 -#define ISL38XX_DEV_INT_UPDATE 0x0002 -#define ISL38XX_DEV_INT_WAKEUP 0x0008 -#define ISL38XX_DEV_INT_SLEEP 0x0010 - -/* Interrupt Identification/Acknowledge/Enable register bits */ -#define ISL38XX_INT_IDENT_UPDATE 0x0002 -#define ISL38XX_INT_IDENT_INIT 0x0004 -#define ISL38XX_INT_IDENT_WAKEUP 0x0008 -#define ISL38XX_INT_IDENT_SLEEP 0x0010 -#define ISL38XX_INT_SOURCES 0x001E - -/* Control/Status register bits */ -/* Looks like there are other meaningful bits - 0x20004400 seen in normal operation, - 0x200044db at 'timeout waiting for mgmt response' -*/ -#define ISL38XX_CTRL_STAT_SLEEPMODE 0x00000200 -#define ISL38XX_CTRL_STAT_CLKRUN 0x00800000 -#define ISL38XX_CTRL_STAT_RESET 0x10000000 -#define ISL38XX_CTRL_STAT_RAMBOOT 0x20000000 -#define ISL38XX_CTRL_STAT_STARTHALTED 0x40000000 -#define ISL38XX_CTRL_STAT_HOST_OVERRIDE 0x80000000 - -/* Control Block definitions */ -#define ISL38XX_CB_RX_DATA_LQ 0 -#define ISL38XX_CB_TX_DATA_LQ 1 -#define ISL38XX_CB_RX_DATA_HQ 2 -#define ISL38XX_CB_TX_DATA_HQ 3 -#define ISL38XX_CB_RX_MGMTQ 4 -#define ISL38XX_CB_TX_MGMTQ 5 -#define ISL38XX_CB_QCOUNT 6 -#define ISL38XX_CB_MGMT_QSIZE 4 -#define ISL38XX_MIN_QTHRESHOLD 4 /* fragments */ - -/* Memory Manager definitions */ -#define MGMT_FRAME_SIZE 1500 /* >= size struct obj_bsslist */ -#define MGMT_TX_FRAME_COUNT 24 /* max 4 + spare 4 + 8 init */ -#define MGMT_RX_FRAME_COUNT 24 /* 4*4 + spare 8 */ -#define MGMT_FRAME_COUNT (MGMT_TX_FRAME_COUNT + MGMT_RX_FRAME_COUNT) -#define CONTROL_BLOCK_SIZE 1024 /* should be enough */ -#define PSM_FRAME_SIZE 1536 -#define PSM_MINIMAL_STATION_COUNT 64 -#define PSM_FRAME_COUNT PSM_MINIMAL_STATION_COUNT -#define PSM_BUFFER_SIZE PSM_FRAME_SIZE * PSM_FRAME_COUNT -#define MAX_TRAP_RX_QUEUE 4 -#define HOST_MEM_BLOCK CONTROL_BLOCK_SIZE + PSM_BUFFER_SIZE - -/* Fragment package definitions */ -#define FRAGMENT_FLAG_MF 0x0001 -#define MAX_FRAGMENT_SIZE 1536 - -/* In monitor mode frames have a header. I don't know exactly how big those - * frame can be but I've never seen any frame bigger than 1584... : - */ -#define MAX_FRAGMENT_SIZE_RX 1600 - -typedef struct { - __le32 address; /* physical address on host */ - __le16 size; /* packet size */ - __le16 flags; /* set of bit-wise flags */ -} isl38xx_fragment; - -struct isl38xx_cb { - __le32 driver_curr_frag[ISL38XX_CB_QCOUNT]; - __le32 device_curr_frag[ISL38XX_CB_QCOUNT]; - isl38xx_fragment rx_data_low[ISL38XX_CB_RX_QSIZE]; - isl38xx_fragment tx_data_low[ISL38XX_CB_TX_QSIZE]; - isl38xx_fragment rx_data_high[ISL38XX_CB_RX_QSIZE]; - isl38xx_fragment tx_data_high[ISL38XX_CB_TX_QSIZE]; - isl38xx_fragment rx_data_mgmt[ISL38XX_CB_MGMT_QSIZE]; - isl38xx_fragment tx_data_mgmt[ISL38XX_CB_MGMT_QSIZE]; -}; - -typedef struct isl38xx_cb isl38xx_control_block; - -/* determine number of entries currently in queue */ -int isl38xx_in_queue(isl38xx_control_block *cb, int queue); - -void isl38xx_disable_interrupts(void __iomem *); -void isl38xx_enable_common_interrupts(void __iomem *); - -void isl38xx_handle_sleep_request(isl38xx_control_block *, int *, - void __iomem *); -void isl38xx_handle_wakeup(isl38xx_control_block *, int *, void __iomem *); -void isl38xx_trigger_device(int, void __iomem *); -void isl38xx_interface_reset(void __iomem *, dma_addr_t); - -#endif /* _ISL_38XX_H */ diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c deleted file mode 100644 index ecbb0546cf3e..000000000000 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ /dev/null @@ -1,2910 +0,0 @@ -/* - * Copyright (C) 2002 Intersil Americas Inc. - * (C) 2003,2004 Aurelien Alleaume - * (C) 2003 Herbert Valerio Riedel - * (C) 2003 Luis R. Rodriguez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "prismcompat.h" -#include "isl_ioctl.h" -#include "islpci_mgt.h" -#include "isl_oid.h" /* additional types and defs for isl38xx fw */ -#include "oid_mgt.h" - -#include /* New driver API */ - -#define KEY_SIZE_WEP104 13 /* 104/128-bit WEP keys */ -#define KEY_SIZE_WEP40 5 /* 40/64-bit WEP keys */ -/* KEY_SIZE_TKIP should match isl_oid.h, struct obj_key.key[] size */ -#define KEY_SIZE_TKIP 32 /* TKIP keys */ - -static void prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid, - u8 *wpa_ie, size_t wpa_ie_len); -static size_t prism54_wpa_bss_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie); -static int prism54_set_wpa(struct net_device *, struct iw_request_info *, - __u32 *, char *); - -/* In 500 kbps */ -static const unsigned char scan_rate_list[] = { 2, 4, 11, 22, - 12, 18, 24, 36, - 48, 72, 96, 108 }; - -/** - * prism54_mib_mode_helper - MIB change mode helper function - * @mib: the &struct islpci_mib object to modify - * @iw_mode: new mode (%IW_MODE_*) - * - * This is a helper function, hence it does not lock. Make sure - * caller deals with locking *if* necessary. This function sets the - * mode-dependent mib values and does the mapping of the Linux - * Wireless API modes to Device firmware modes. It also checks for - * correct valid Linux wireless modes. - */ -static int -prism54_mib_mode_helper(islpci_private *priv, u32 iw_mode) -{ - u32 config = INL_CONFIG_MANUALRUN; - u32 mode, bsstype; - - /* For now, just catch early the Repeater and Secondary modes here */ - if (iw_mode == IW_MODE_REPEAT || iw_mode == IW_MODE_SECOND) { - printk(KERN_DEBUG - "%s(): Sorry, Repeater mode and Secondary mode " - "are not yet supported by this driver.\n", __func__); - return -EINVAL; - } - - priv->iw_mode = iw_mode; - - switch (iw_mode) { - case IW_MODE_AUTO: - mode = INL_MODE_CLIENT; - bsstype = DOT11_BSSTYPE_ANY; - break; - case IW_MODE_ADHOC: - mode = INL_MODE_CLIENT; - bsstype = DOT11_BSSTYPE_IBSS; - break; - case IW_MODE_INFRA: - mode = INL_MODE_CLIENT; - bsstype = DOT11_BSSTYPE_INFRA; - break; - case IW_MODE_MASTER: - mode = INL_MODE_AP; - bsstype = DOT11_BSSTYPE_INFRA; - break; - case IW_MODE_MONITOR: - mode = INL_MODE_PROMISCUOUS; - bsstype = DOT11_BSSTYPE_ANY; - config |= INL_CONFIG_RXANNEX; - break; - default: - return -EINVAL; - } - - if (init_wds) - config |= INL_CONFIG_WDS; - mgt_set(priv, DOT11_OID_BSSTYPE, &bsstype); - mgt_set(priv, OID_INL_CONFIG, &config); - mgt_set(priv, OID_INL_MODE, &mode); - - return 0; -} - -/** - * prism54_mib_init - fill MIB cache with defaults - * - * this function initializes the struct given as @mib with defaults, - * of which many are retrieved from the global module parameter - * variables. - */ - -void -prism54_mib_init(islpci_private *priv) -{ - u32 channel, authen, wep, filter, dot1x, mlme, conformance, power, mode; - struct obj_buffer psm_buffer = { - .size = PSM_BUFFER_SIZE, - .addr = priv->device_psm_buffer - }; - - channel = CARD_DEFAULT_CHANNEL; - authen = CARD_DEFAULT_AUTHEN; - wep = CARD_DEFAULT_WEP; - filter = CARD_DEFAULT_FILTER; /* (0) Do not filter un-encrypted data */ - dot1x = CARD_DEFAULT_DOT1X; - mlme = CARD_DEFAULT_MLME_MODE; - conformance = CARD_DEFAULT_CONFORMANCE; - power = 127; - mode = CARD_DEFAULT_IW_MODE; - - mgt_set(priv, DOT11_OID_CHANNEL, &channel); - mgt_set(priv, DOT11_OID_AUTHENABLE, &authen); - mgt_set(priv, DOT11_OID_PRIVACYINVOKED, &wep); - mgt_set(priv, DOT11_OID_PSMBUFFER, &psm_buffer); - mgt_set(priv, DOT11_OID_EXUNENCRYPTED, &filter); - mgt_set(priv, DOT11_OID_DOT1XENABLE, &dot1x); - mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlme); - mgt_set(priv, OID_INL_DOT11D_CONFORMANCE, &conformance); - mgt_set(priv, OID_INL_OUTPUTPOWER, &power); - - /* This sets all of the mode-dependent values */ - prism54_mib_mode_helper(priv, mode); -} - -/* this will be executed outside of atomic context thanks to - * schedule_work(), thus we can as well use sleeping semaphore - * locking */ -void -prism54_update_stats(struct work_struct *work) -{ - islpci_private *priv = container_of(work, islpci_private, stats_work); - char *data; - int j; - struct obj_bss bss, *bss2; - union oid_res_t r; - - mutex_lock(&priv->stats_lock); - -/* Noise floor. - * I'm not sure if the unit is dBm. - * Note : If we are not connected, this value seems to be irrelevant. */ - - mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r); - priv->local_iwstatistics.qual.noise = r.u; - -/* Get the rssi of the link. To do this we need to retrieve a bss. */ - - /* First get the MAC address of the AP we are associated with. */ - mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r); - data = r.ptr; - - /* copy this MAC to the bss */ - memcpy(bss.address, data, ETH_ALEN); - kfree(data); - - /* now ask for the corresponding bss */ - j = mgt_get_request(priv, DOT11_OID_BSSFIND, 0, (void *) &bss, &r); - bss2 = r.ptr; - /* report the rssi and use it to calculate - * link quality through a signal-noise - * ratio */ - priv->local_iwstatistics.qual.level = bss2->rssi; - priv->local_iwstatistics.qual.qual = - bss2->rssi - priv->iwstatistics.qual.noise; - - kfree(bss2); - - /* report that the stats are new */ - priv->local_iwstatistics.qual.updated = 0x7; - -/* Rx : unable to decrypt the MPDU */ - mgt_get_request(priv, DOT11_OID_PRIVRXFAILED, 0, NULL, &r); - priv->local_iwstatistics.discard.code = r.u; - -/* Tx : Max MAC retries num reached */ - mgt_get_request(priv, DOT11_OID_MPDUTXFAILED, 0, NULL, &r); - priv->local_iwstatistics.discard.retries = r.u; - - mutex_unlock(&priv->stats_lock); -} - -struct iw_statistics * -prism54_get_wireless_stats(struct net_device *ndev) -{ - islpci_private *priv = netdev_priv(ndev); - - /* If the stats are being updated return old data */ - if (mutex_trylock(&priv->stats_lock)) { - memcpy(&priv->iwstatistics, &priv->local_iwstatistics, - sizeof (struct iw_statistics)); - /* They won't be marked updated for the next time */ - priv->local_iwstatistics.qual.updated = 0; - mutex_unlock(&priv->stats_lock); - } else - priv->iwstatistics.qual.updated = 0; - - /* Update our wireless stats, but do not schedule to often - * (max 1 HZ) */ - if ((priv->stats_timestamp == 0) || - time_after(jiffies, priv->stats_timestamp + 1 * HZ)) { - schedule_work(&priv->stats_work); - priv->stats_timestamp = jiffies; - } - - return &priv->iwstatistics; -} - -static int -prism54_commit(struct net_device *ndev, struct iw_request_info *info, - char *cwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - - /* simply re-set the last set SSID, this should commit most stuff */ - - /* Commit in Monitor mode is not necessary, also setting essid - * in Monitor mode does not make sense and isn't allowed for this - * device's firmware */ - if (priv->iw_mode != IW_MODE_MONITOR) - return mgt_set_request(priv, DOT11_OID_SSID, 0, NULL); - return 0; -} - -static int -prism54_get_name(struct net_device *ndev, struct iw_request_info *info, - char *cwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - char *capabilities; - union oid_res_t r; - int rvalue; - - if (islpci_get_state(priv) < PRV_STATE_INIT) { - strncpy(cwrq, "NOT READY!", IFNAMSIZ); - return 0; - } - rvalue = mgt_get_request(priv, OID_INL_PHYCAPABILITIES, 0, NULL, &r); - - switch (r.u) { - case INL_PHYCAP_5000MHZ: - capabilities = "IEEE 802.11a/b/g"; - break; - case INL_PHYCAP_FAA: - capabilities = "IEEE 802.11b/g - FAA Support"; - break; - case INL_PHYCAP_2400MHZ: - default: - capabilities = "IEEE 802.11b/g"; /* Default */ - break; - } - strncpy(cwrq, capabilities, IFNAMSIZ); - return rvalue; -} - -static int -prism54_set_freq(struct net_device *ndev, struct iw_request_info *info, - struct iw_freq *fwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - int rvalue; - u32 c; - - if (fwrq->m < 1000) - /* we have a channel number */ - c = fwrq->m; - else - c = (fwrq->e == 1) ? channel_of_freq(fwrq->m / 100000) : 0; - - rvalue = c ? mgt_set_request(priv, DOT11_OID_CHANNEL, 0, &c) : -EINVAL; - - /* Call commit handler */ - return (rvalue ? rvalue : -EINPROGRESS); -} - -static int -prism54_get_freq(struct net_device *ndev, struct iw_request_info *info, - struct iw_freq *fwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - union oid_res_t r; - int rvalue; - - rvalue = mgt_get_request(priv, DOT11_OID_CHANNEL, 0, NULL, &r); - fwrq->i = r.u; - rvalue |= mgt_get_request(priv, DOT11_OID_FREQUENCY, 0, NULL, &r); - fwrq->m = r.u; - fwrq->e = 3; - - return rvalue; -} - -static int -prism54_set_mode(struct net_device *ndev, struct iw_request_info *info, - __u32 * uwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - u32 mlmeautolevel = CARD_DEFAULT_MLME_MODE; - - /* Let's see if the user passed a valid Linux Wireless mode */ - if (*uwrq > IW_MODE_MONITOR || *uwrq < IW_MODE_AUTO) { - printk(KERN_DEBUG - "%s: %s() You passed a non-valid init_mode.\n", - priv->ndev->name, __func__); - return -EINVAL; - } - - down_write(&priv->mib_sem); - - if (prism54_mib_mode_helper(priv, *uwrq)) { - up_write(&priv->mib_sem); - return -EOPNOTSUPP; - } - - /* the ACL code needs an intermediate mlmeautolevel. The wpa stuff an - * extended one. - */ - if ((*uwrq == IW_MODE_MASTER) && (priv->acl.policy != MAC_POLICY_OPEN)) - mlmeautolevel = DOT11_MLME_INTERMEDIATE; - if (priv->wpa) - mlmeautolevel = DOT11_MLME_EXTENDED; - - mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel); - - if (mgt_commit(priv)) { - up_write(&priv->mib_sem); - return -EIO; - } - priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR) - ? priv->monitor_type : ARPHRD_ETHER; - up_write(&priv->mib_sem); - - return 0; -} - -/* Use mib cache */ -static int -prism54_get_mode(struct net_device *ndev, struct iw_request_info *info, - __u32 * uwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - - BUG_ON((priv->iw_mode < IW_MODE_AUTO) || (priv->iw_mode > - IW_MODE_MONITOR)); - *uwrq = priv->iw_mode; - - return 0; -} - -/* we use DOT11_OID_EDTHRESHOLD. From what I guess the card will not try to - * emit data if (sensitivity > rssi - noise) (in dBm). - * prism54_set_sens does not seem to work. - */ - -static int -prism54_set_sens(struct net_device *ndev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - u32 sens; - - /* by default the card sets this to 20. */ - sens = vwrq->disabled ? 20 : vwrq->value; - - return mgt_set_request(priv, DOT11_OID_EDTHRESHOLD, 0, &sens); -} - -static int -prism54_get_sens(struct net_device *ndev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - union oid_res_t r; - int rvalue; - - rvalue = mgt_get_request(priv, DOT11_OID_EDTHRESHOLD, 0, NULL, &r); - - vwrq->value = r.u; - vwrq->disabled = (vwrq->value == 0); - vwrq->fixed = 1; - - return rvalue; -} - -static int -prism54_get_range(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - struct iw_range *range = (struct iw_range *) extra; - islpci_private *priv = netdev_priv(ndev); - u8 *data; - int i, m, rvalue; - struct obj_frequencies *freq; - union oid_res_t r; - - memset(range, 0, sizeof (struct iw_range)); - dwrq->length = sizeof (struct iw_range); - - /* set the wireless extension version number */ - range->we_version_source = SUPPORTED_WIRELESS_EXT; - range->we_version_compiled = WIRELESS_EXT; - - /* Now the encoding capabilities */ - range->num_encoding_sizes = 3; - /* 64(40) bits WEP */ - range->encoding_size[0] = 5; - /* 128(104) bits WEP */ - range->encoding_size[1] = 13; - /* 256 bits for WPA-PSK */ - range->encoding_size[2] = 32; - /* 4 keys are allowed */ - range->max_encoding_tokens = 4; - - /* we don't know the quality range... */ - range->max_qual.level = 0; - range->max_qual.noise = 0; - range->max_qual.qual = 0; - /* these value describe an average quality. Needs more tweaking... */ - range->avg_qual.level = -80; /* -80 dBm */ - range->avg_qual.noise = 0; /* don't know what to put here */ - range->avg_qual.qual = 0; - - range->sensitivity = 200; - - /* retry limit capabilities */ - range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; - range->retry_flags = IW_RETRY_LIMIT; - range->r_time_flags = IW_RETRY_LIFETIME; - - /* I don't know the range. Put stupid things here */ - range->min_retry = 1; - range->max_retry = 65535; - range->min_r_time = 1024; - range->max_r_time = 65535 * 1024; - - /* txpower is supported in dBm's */ - range->txpower_capa = IW_TXPOW_DBM; - - /* Event capability (kernel + driver) */ - range->event_capa[0] = (IW_EVENT_CAPA_K_0 | - IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | - IW_EVENT_CAPA_MASK(SIOCGIWAP)); - range->event_capa[1] = IW_EVENT_CAPA_K_1; - range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVCUSTOM); - - range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | - IW_ENC_CAPA_CIPHER_TKIP; - - if (islpci_get_state(priv) < PRV_STATE_INIT) - return 0; - - /* Request the device for the supported frequencies - * not really relevant since some devices will report the 5 GHz band - * frequencies even if they don't support them. - */ - rvalue = - mgt_get_request(priv, DOT11_OID_SUPPORTEDFREQUENCIES, 0, NULL, &r); - freq = r.ptr; - - range->num_channels = freq->nr; - range->num_frequency = freq->nr; - - m = min(IW_MAX_FREQUENCIES, (int) freq->nr); - for (i = 0; i < m; i++) { - range->freq[i].m = freq->mhz[i]; - range->freq[i].e = 6; - range->freq[i].i = channel_of_freq(freq->mhz[i]); - } - kfree(freq); - - rvalue |= mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r); - data = r.ptr; - - /* We got an array of char. It is NULL terminated. */ - i = 0; - while ((i < IW_MAX_BITRATES) && (*data != 0)) { - /* the result must be in bps. The card gives us 500Kbps */ - range->bitrate[i] = *data * 500000; - i++; - data++; - } - range->num_bitrates = i; - kfree(r.ptr); - - return rvalue; -} - -/* Set AP address*/ - -static int -prism54_set_wap(struct net_device *ndev, struct iw_request_info *info, - struct sockaddr *awrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - char bssid[6]; - int rvalue; - - if (awrq->sa_family != ARPHRD_ETHER) - return -EINVAL; - - /* prepare the structure for the set object */ - memcpy(&bssid[0], awrq->sa_data, ETH_ALEN); - - /* set the bssid -- does this make sense when in AP mode? */ - rvalue = mgt_set_request(priv, DOT11_OID_BSSID, 0, &bssid); - - return (rvalue ? rvalue : -EINPROGRESS); /* Call commit handler */ -} - -/* get AP address*/ - -static int -prism54_get_wap(struct net_device *ndev, struct iw_request_info *info, - struct sockaddr *awrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - union oid_res_t r; - int rvalue; - - rvalue = mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r); - memcpy(awrq->sa_data, r.ptr, ETH_ALEN); - awrq->sa_family = ARPHRD_ETHER; - kfree(r.ptr); - - return rvalue; -} - -static int -prism54_set_scan(struct net_device *dev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - /* hehe the device does this automagicaly */ - return 0; -} - -/* a little helper that will translate our data into a card independent - * format that the Wireless Tools will understand. This was inspired by - * the "Aironet driver for 4500 and 4800 series cards" (GPL) - */ - -static char * -prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info, - char *current_ev, char *end_buf, struct obj_bss *bss, - char noise) -{ - struct iw_event iwe; /* Temporary buffer */ - short cap; - islpci_private *priv = netdev_priv(ndev); - u8 wpa_ie[MAX_WPA_IE_LEN]; - size_t wpa_ie_len; - - /* The first entry must be the MAC address */ - memcpy(iwe.u.ap_addr.sa_data, bss->address, ETH_ALEN); - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - iwe.cmd = SIOCGIWAP; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_ADDR_LEN); - - /* The following entries will be displayed in the same order we give them */ - - /* The ESSID. */ - iwe.u.data.length = bss->ssid.length; - iwe.u.data.flags = 1; - iwe.cmd = SIOCGIWESSID; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, bss->ssid.octets); - - /* Capabilities */ -#define CAP_ESS 0x01 -#define CAP_IBSS 0x02 -#define CAP_CRYPT 0x10 - - /* Mode */ - cap = bss->capinfo; - iwe.u.mode = 0; - if (cap & CAP_ESS) - iwe.u.mode = IW_MODE_MASTER; - else if (cap & CAP_IBSS) - iwe.u.mode = IW_MODE_ADHOC; - iwe.cmd = SIOCGIWMODE; - if (iwe.u.mode) - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_UINT_LEN); - - /* Encryption capability */ - if (cap & CAP_CRYPT) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - iwe.cmd = SIOCGIWENCODE; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, NULL); - - /* Add frequency. (short) bss->channel is the frequency in MHz */ - iwe.u.freq.m = bss->channel; - iwe.u.freq.e = 6; - iwe.cmd = SIOCGIWFREQ; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_FREQ_LEN); - - /* Add quality statistics */ - iwe.u.qual.level = bss->rssi; - iwe.u.qual.noise = noise; - /* do a simple SNR for quality */ - iwe.u.qual.qual = bss->rssi - noise; - iwe.cmd = IWEVQUAL; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_QUAL_LEN); - - /* Add WPA/RSN Information Element, if any */ - wpa_ie_len = prism54_wpa_bss_ie_get(priv, bss->address, wpa_ie); - if (wpa_ie_len > 0) { - iwe.cmd = IWEVGENIE; - iwe.u.data.length = min_t(size_t, wpa_ie_len, MAX_WPA_IE_LEN); - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, wpa_ie); - } - /* Do the bitrates */ - { - char *current_val = current_ev + iwe_stream_lcp_len(info); - int i; - int mask; - - iwe.cmd = SIOCGIWRATE; - /* Those two flags are ignored... */ - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; - - /* Parse the bitmask */ - mask = 0x1; - for(i = 0; i < sizeof(scan_rate_list); i++) { - if(bss->rates & mask) { - iwe.u.bitrate.value = (scan_rate_list[i] * 500000); - current_val = iwe_stream_add_value( - info, current_ev, current_val, - end_buf, &iwe, IW_EV_PARAM_LEN); - } - mask <<= 1; - } - /* Check if we added any event */ - if ((current_val - current_ev) > iwe_stream_lcp_len(info)) - current_ev = current_val; - } - - return current_ev; -} - -static int -prism54_get_scan(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - int i, rvalue; - struct obj_bsslist *bsslist; - u32 noise = 0; - char *current_ev = extra; - union oid_res_t r; - - if (islpci_get_state(priv) < PRV_STATE_INIT) { - /* device is not ready, fail gently */ - dwrq->length = 0; - return 0; - } - - /* first get the noise value. We will use it to report the link quality */ - rvalue = mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r); - noise = r.u; - - /* Ask the device for a list of known bss. - * The old API, using SIOCGIWAPLIST, had a hard limit of IW_MAX_AP=64. - * The new API, using SIOCGIWSCAN, is only limited by the buffer size. - * WE-14->WE-16, the buffer is limited to IW_SCAN_MAX_DATA bytes. - * Starting with WE-17, the buffer can be as big as needed. - * But the device won't repport anything if you change the value - * of IWMAX_BSS=24. */ - - rvalue |= mgt_get_request(priv, DOT11_OID_BSSLIST, 0, NULL, &r); - bsslist = r.ptr; - - /* ok now, scan the list and translate its info */ - for (i = 0; i < (int) bsslist->nr; i++) { - current_ev = prism54_translate_bss(ndev, info, current_ev, - extra + dwrq->length, - &(bsslist->bsslist[i]), - noise); - - /* Check if there is space for one more entry */ - if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) { - /* Ask user space to try again with a bigger buffer */ - rvalue = -E2BIG; - break; - } - } - - kfree(bsslist); - dwrq->length = (current_ev - extra); - dwrq->flags = 0; /* todo */ - - return rvalue; -} - -static int -prism54_set_essid(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct obj_ssid essid; - - memset(essid.octets, 0, 33); - - /* Check if we were asked for `any' */ - if (dwrq->flags && dwrq->length) { - if (dwrq->length > 32) - return -E2BIG; - essid.length = dwrq->length; - memcpy(essid.octets, extra, dwrq->length); - } else - essid.length = 0; - - if (priv->iw_mode != IW_MODE_MONITOR) - return mgt_set_request(priv, DOT11_OID_SSID, 0, &essid); - - /* If in monitor mode, just save to mib */ - mgt_set(priv, DOT11_OID_SSID, &essid); - return 0; - -} - -static int -prism54_get_essid(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct obj_ssid *essid; - union oid_res_t r; - int rvalue; - - rvalue = mgt_get_request(priv, DOT11_OID_SSID, 0, NULL, &r); - essid = r.ptr; - - if (essid->length) { - dwrq->flags = 1; /* set ESSID to ON for Wireless Extensions */ - /* if it is too big, trunk it */ - dwrq->length = min((u8)IW_ESSID_MAX_SIZE, essid->length); - } else { - dwrq->flags = 0; - dwrq->length = 0; - } - essid->octets[dwrq->length] = '\0'; - memcpy(extra, essid->octets, dwrq->length); - kfree(essid); - - return rvalue; -} - -/* Provides no functionality, just completes the ioctl. In essence this is a - * just a cosmetic ioctl. - */ -static int -prism54_set_nick(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - - if (dwrq->length > IW_ESSID_MAX_SIZE) - return -E2BIG; - - down_write(&priv->mib_sem); - memset(priv->nickname, 0, sizeof (priv->nickname)); - memcpy(priv->nickname, extra, dwrq->length); - up_write(&priv->mib_sem); - - return 0; -} - -static int -prism54_get_nick(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - - dwrq->length = 0; - - down_read(&priv->mib_sem); - dwrq->length = strlen(priv->nickname); - memcpy(extra, priv->nickname, dwrq->length); - up_read(&priv->mib_sem); - - return 0; -} - -/* Set the allowed Bitrates */ - -static int -prism54_set_rate(struct net_device *ndev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - - islpci_private *priv = netdev_priv(ndev); - u32 rate, profile; - char *data; - int ret, i; - union oid_res_t r; - - if (vwrq->value == -1) { - /* auto mode. No limit. */ - profile = 1; - return mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile); - } - - ret = mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r); - if (ret) { - kfree(r.ptr); - return ret; - } - - rate = (u32) (vwrq->value / 500000); - data = r.ptr; - i = 0; - - while (data[i]) { - if (rate && (data[i] == rate)) { - break; - } - if (vwrq->value == i) { - break; - } - data[i] |= 0x80; - i++; - } - - if (!data[i]) { - kfree(r.ptr); - return -EINVAL; - } - - data[i] |= 0x80; - data[i + 1] = 0; - - /* Now, check if we want a fixed or auto value */ - if (vwrq->fixed) { - data[0] = data[i]; - data[1] = 0; - } - -/* - i = 0; - printk("prism54 rate: "); - while(data[i]) { - printk("%u ", data[i]); - i++; - } - printk("0\n"); -*/ - profile = -1; - ret = mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile); - ret |= mgt_set_request(priv, DOT11_OID_EXTENDEDRATES, 0, data); - ret |= mgt_set_request(priv, DOT11_OID_RATES, 0, data); - - kfree(r.ptr); - - return ret; -} - -/* Get the current bit rate */ -static int -prism54_get_rate(struct net_device *ndev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - int rvalue; - char *data; - union oid_res_t r; - - /* Get the current bit rate */ - if ((rvalue = mgt_get_request(priv, GEN_OID_LINKSTATE, 0, NULL, &r))) - return rvalue; - vwrq->value = r.u * 500000; - - /* request the device for the enabled rates */ - rvalue = mgt_get_request(priv, DOT11_OID_RATES, 0, NULL, &r); - if (rvalue) { - kfree(r.ptr); - return rvalue; - } - data = r.ptr; - vwrq->fixed = (data[0] != 0) && (data[1] == 0); - kfree(r.ptr); - - return 0; -} - -static int -prism54_set_rts(struct net_device *ndev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - - return mgt_set_request(priv, DOT11_OID_RTSTHRESH, 0, &vwrq->value); -} - -static int -prism54_get_rts(struct net_device *ndev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - union oid_res_t r; - int rvalue; - - /* get the rts threshold */ - rvalue = mgt_get_request(priv, DOT11_OID_RTSTHRESH, 0, NULL, &r); - vwrq->value = r.u; - - return rvalue; -} - -static int -prism54_set_frag(struct net_device *ndev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - - return mgt_set_request(priv, DOT11_OID_FRAGTHRESH, 0, &vwrq->value); -} - -static int -prism54_get_frag(struct net_device *ndev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - union oid_res_t r; - int rvalue; - - rvalue = mgt_get_request(priv, DOT11_OID_FRAGTHRESH, 0, NULL, &r); - vwrq->value = r.u; - - return rvalue; -} - -/* Here we have (min,max) = max retries for (small frames, big frames). Where - * big frame <=> bigger than the rts threshold - * small frame <=> smaller than the rts threshold - * This is not really the behavior expected by the wireless tool but it seems - * to be a common behavior in other drivers. - */ - -static int -prism54_set_retry(struct net_device *ndev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - u32 slimit = 0, llimit = 0; /* short and long limit */ - u32 lifetime = 0; - int rvalue = 0; - - if (vwrq->disabled) - /* we cannot disable this feature */ - return -EINVAL; - - if (vwrq->flags & IW_RETRY_LIMIT) { - if (vwrq->flags & IW_RETRY_SHORT) - slimit = vwrq->value; - else if (vwrq->flags & IW_RETRY_LONG) - llimit = vwrq->value; - else { - /* we are asked to set both */ - slimit = vwrq->value; - llimit = vwrq->value; - } - } - if (vwrq->flags & IW_RETRY_LIFETIME) - /* Wireless tools use us unit while the device uses 1024 us unit */ - lifetime = vwrq->value / 1024; - - /* now set what is requested */ - if (slimit) - rvalue = - mgt_set_request(priv, DOT11_OID_SHORTRETRIES, 0, &slimit); - if (llimit) - rvalue |= - mgt_set_request(priv, DOT11_OID_LONGRETRIES, 0, &llimit); - if (lifetime) - rvalue |= - mgt_set_request(priv, DOT11_OID_MAXTXLIFETIME, 0, - &lifetime); - return rvalue; -} - -static int -prism54_get_retry(struct net_device *ndev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - union oid_res_t r; - int rvalue = 0; - vwrq->disabled = 0; /* It cannot be disabled */ - - if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { - /* we are asked for the life time */ - rvalue = - mgt_get_request(priv, DOT11_OID_MAXTXLIFETIME, 0, NULL, &r); - vwrq->value = r.u * 1024; - vwrq->flags = IW_RETRY_LIFETIME; - } else if ((vwrq->flags & IW_RETRY_LONG)) { - /* we are asked for the long retry limit */ - rvalue |= - mgt_get_request(priv, DOT11_OID_LONGRETRIES, 0, NULL, &r); - vwrq->value = r.u; - vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; - } else { - /* default. get the short retry limit */ - rvalue |= - mgt_get_request(priv, DOT11_OID_SHORTRETRIES, 0, NULL, &r); - vwrq->value = r.u; - vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT; - } - - return rvalue; -} - -static int -prism54_set_encode(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - int rvalue = 0, force = 0; - int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0; - union oid_res_t r; - - /* with the new API, it's impossible to get a NULL pointer. - * New version of iwconfig set the IW_ENCODE_NOKEY flag - * when no key is given, but older versions don't. */ - - if (dwrq->length > 0) { - /* we have a key to set */ - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - int current_index; - struct obj_key key = { DOT11_PRIV_WEP, 0, "" }; - - /* get the current key index */ - rvalue = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r); - current_index = r.u; - /* Verify that the key is not marked as invalid */ - if (!(dwrq->flags & IW_ENCODE_NOKEY)) { - if (dwrq->length > KEY_SIZE_TKIP) { - /* User-provided key data too big */ - return -EINVAL; - } - if (dwrq->length > KEY_SIZE_WEP104) { - /* WPA-PSK TKIP */ - key.type = DOT11_PRIV_TKIP; - key.length = KEY_SIZE_TKIP; - } else if (dwrq->length > KEY_SIZE_WEP40) { - /* WEP 104/128 */ - key.length = KEY_SIZE_WEP104; - } else { - /* WEP 40/64 */ - key.length = KEY_SIZE_WEP40; - } - memset(key.key, 0, sizeof (key.key)); - memcpy(key.key, extra, dwrq->length); - - if ((index < 0) || (index > 3)) - /* no index provided use the current one */ - index = current_index; - - /* now send the key to the card */ - rvalue |= - mgt_set_request(priv, DOT11_OID_DEFKEYX, index, - &key); - } - /* - * If a valid key is set, encryption should be enabled - * (user may turn it off later). - * This is also how "iwconfig ethX key on" works - */ - if ((index == current_index) && (key.length > 0)) - force = 1; - } else { - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - if ((index >= 0) && (index <= 3)) { - /* we want to set the key index */ - rvalue |= - mgt_set_request(priv, DOT11_OID_DEFKEYID, 0, - &index); - } else { - if (!(dwrq->flags & IW_ENCODE_MODE)) { - /* we cannot do anything. Complain. */ - return -EINVAL; - } - } - } - /* now read the flags */ - if (dwrq->flags & IW_ENCODE_DISABLED) { - /* Encoding disabled, - * authen = DOT11_AUTH_OS; - * invoke = 0; - * exunencrypt = 0; */ - } - if (dwrq->flags & IW_ENCODE_OPEN) - /* Encode but accept non-encoded packets. No auth */ - invoke = 1; - if ((dwrq->flags & IW_ENCODE_RESTRICTED) || force) { - /* Refuse non-encoded packets. Auth */ - authen = DOT11_AUTH_BOTH; - invoke = 1; - exunencrypt = 1; - } - /* do the change if requested */ - if ((dwrq->flags & IW_ENCODE_MODE) || force) { - rvalue |= - mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen); - rvalue |= - mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &invoke); - rvalue |= - mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, - &exunencrypt); - } - return rvalue; -} - -static int -prism54_get_encode(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct obj_key *key; - u32 devindex, index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - u32 authen = 0, invoke = 0, exunencrypt = 0; - int rvalue; - union oid_res_t r; - - /* first get the flags */ - rvalue = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r); - authen = r.u; - rvalue |= mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r); - invoke = r.u; - rvalue |= mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r); - exunencrypt = r.u; - - if (invoke && (authen == DOT11_AUTH_BOTH) && exunencrypt) - dwrq->flags = IW_ENCODE_RESTRICTED; - else if ((authen == DOT11_AUTH_OS) && !exunencrypt) { - if (invoke) - dwrq->flags = IW_ENCODE_OPEN; - else - dwrq->flags = IW_ENCODE_DISABLED; - } else - /* The card should not work in this state */ - dwrq->flags = 0; - - /* get the current device key index */ - rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r); - devindex = r.u; - /* Now get the key, return it */ - if (index == -1 || index > 3) - /* no index provided, use the current one */ - index = devindex; - rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYX, index, NULL, &r); - key = r.ptr; - dwrq->length = key->length; - memcpy(extra, key->key, dwrq->length); - kfree(key); - /* return the used key index */ - dwrq->flags |= devindex + 1; - - return rvalue; -} - -static int -prism54_get_txpower(struct net_device *ndev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - union oid_res_t r; - int rvalue; - - rvalue = mgt_get_request(priv, OID_INL_OUTPUTPOWER, 0, NULL, &r); - /* intersil firmware operates in 0.25 dBm (1/4 dBm) */ - vwrq->value = (s32) r.u / 4; - vwrq->fixed = 1; - /* radio is not turned of - * btw: how is possible to turn off only the radio - */ - vwrq->disabled = 0; - - return rvalue; -} - -static int -prism54_set_txpower(struct net_device *ndev, struct iw_request_info *info, - struct iw_param *vwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - s32 u = vwrq->value; - - /* intersil firmware operates in 0.25 dBm (1/4) */ - u *= 4; - if (vwrq->disabled) { - /* don't know how to disable radio */ - printk(KERN_DEBUG - "%s: %s() disabling radio is not yet supported.\n", - priv->ndev->name, __func__); - return -ENOTSUPP; - } else if (vwrq->fixed) - /* currently only fixed value is supported */ - return mgt_set_request(priv, OID_INL_OUTPUTPOWER, 0, &u); - else { - printk(KERN_DEBUG - "%s: %s() auto power will be implemented later.\n", - priv->ndev->name, __func__); - return -ENOTSUPP; - } -} - -static int prism54_set_genie(struct net_device *ndev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - int alen, ret = 0; - struct obj_attachment *attach; - - if (data->length > MAX_WPA_IE_LEN || - (data->length && extra == NULL)) - return -EINVAL; - - memcpy(priv->wpa_ie, extra, data->length); - priv->wpa_ie_len = data->length; - - alen = sizeof(*attach) + priv->wpa_ie_len; - attach = kzalloc(alen, GFP_KERNEL); - if (attach == NULL) - return -ENOMEM; - -#define WLAN_FC_TYPE_MGMT 0 -#define WLAN_FC_STYPE_ASSOC_REQ 0 -#define WLAN_FC_STYPE_REASSOC_REQ 2 - - /* Note: endianness is covered by mgt_set_varlen */ - attach->type = (WLAN_FC_TYPE_MGMT << 2) | - (WLAN_FC_STYPE_ASSOC_REQ << 4); - attach->id = -1; - attach->size = priv->wpa_ie_len; - memcpy(attach->data, extra, priv->wpa_ie_len); - - ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach, - priv->wpa_ie_len); - if (ret == 0) { - attach->type = (WLAN_FC_TYPE_MGMT << 2) | - (WLAN_FC_STYPE_REASSOC_REQ << 4); - - ret = mgt_set_varlen(priv, DOT11_OID_ATTACHMENT, attach, - priv->wpa_ie_len); - if (ret == 0) - printk(KERN_DEBUG "%s: WPA IE Attachment was set\n", - ndev->name); - } - - kfree(attach); - return ret; -} - - -static int prism54_get_genie(struct net_device *ndev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - int len = priv->wpa_ie_len; - - if (len <= 0) { - data->length = 0; - return 0; - } - - if (data->length < len) - return -E2BIG; - - data->length = len; - memcpy(extra, priv->wpa_ie, len); - - return 0; -} - -static int prism54_set_auth(struct net_device *ndev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct iw_param *param = &wrqu->param; - u32 mlmelevel = 0, authen = 0, dot1x = 0; - u32 exunencrypt = 0, privinvoked = 0, wpa = 0; - u32 old_wpa; - int ret = 0; - union oid_res_t r; - - if (islpci_get_state(priv) < PRV_STATE_INIT) - return 0; - - /* first get the flags */ - down_write(&priv->mib_sem); - wpa = old_wpa = priv->wpa; - up_write(&priv->mib_sem); - ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r); - authen = r.u; - ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r); - privinvoked = r.u; - ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r); - exunencrypt = r.u; - ret = mgt_get_request(priv, DOT11_OID_DOT1XENABLE, 0, NULL, &r); - dot1x = r.u; - ret = mgt_get_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, NULL, &r); - mlmelevel = r.u; - - if (ret < 0) - goto out; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_CIPHER_PAIRWISE: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_KEY_MGMT: - break; - - case IW_AUTH_WPA_ENABLED: - /* Do the same thing as IW_AUTH_WPA_VERSION */ - if (param->value) { - wpa = 1; - privinvoked = 1; /* For privacy invoked */ - exunencrypt = 1; /* Filter out all unencrypted frames */ - dot1x = 0x01; /* To enable eap filter */ - mlmelevel = DOT11_MLME_EXTENDED; - authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */ - } else { - wpa = 0; - privinvoked = 0; - exunencrypt = 0; /* Do not filter un-encrypted data */ - dot1x = 0; - mlmelevel = DOT11_MLME_AUTO; - } - break; - - case IW_AUTH_WPA_VERSION: - if (param->value & IW_AUTH_WPA_VERSION_DISABLED) { - wpa = 0; - privinvoked = 0; - exunencrypt = 0; /* Do not filter un-encrypted data */ - dot1x = 0; - mlmelevel = DOT11_MLME_AUTO; - } else { - if (param->value & IW_AUTH_WPA_VERSION_WPA) - wpa = 1; - else if (param->value & IW_AUTH_WPA_VERSION_WPA2) - wpa = 2; - privinvoked = 1; /* For privacy invoked */ - exunencrypt = 1; /* Filter out all unencrypted frames */ - dot1x = 0x01; /* To enable eap filter */ - mlmelevel = DOT11_MLME_EXTENDED; - authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */ - } - break; - - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - /* dot1x should be the opposite of RX_UNENCRYPTED_EAPOL; - * turn off dot1x when allowing receipt of unencrypted EAPOL - * frames, turn on dot1x when receipt should be disallowed - */ - dot1x = param->value ? 0 : 0x01; - break; - - case IW_AUTH_PRIVACY_INVOKED: - privinvoked = param->value ? 1 : 0; - break; - - case IW_AUTH_DROP_UNENCRYPTED: - exunencrypt = param->value ? 1 : 0; - break; - - case IW_AUTH_80211_AUTH_ALG: - if (param->value & IW_AUTH_ALG_SHARED_KEY) { - /* Only WEP uses _SK and _BOTH */ - if (wpa > 0) { - ret = -EINVAL; - goto out; - } - authen = DOT11_AUTH_SK; - } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { - authen = DOT11_AUTH_OS; - } else { - ret = -EINVAL; - goto out; - } - break; - - default: - return -EOPNOTSUPP; - } - - /* Set all the values */ - down_write(&priv->mib_sem); - priv->wpa = wpa; - up_write(&priv->mib_sem); - mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen); - mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &privinvoked); - mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, &exunencrypt); - mgt_set_request(priv, DOT11_OID_DOT1XENABLE, 0, &dot1x); - mgt_set_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, &mlmelevel); - -out: - return ret; -} - -static int prism54_get_auth(struct net_device *ndev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct iw_param *param = &wrqu->param; - u32 wpa = 0; - int ret = 0; - union oid_res_t r; - - if (islpci_get_state(priv) < PRV_STATE_INIT) - return 0; - - /* first get the flags */ - down_write(&priv->mib_sem); - wpa = priv->wpa; - up_write(&priv->mib_sem); - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_CIPHER_PAIRWISE: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_KEY_MGMT: - /* - * wpa_supplicant will control these internally - */ - ret = -EOPNOTSUPP; - break; - - case IW_AUTH_WPA_VERSION: - switch (wpa) { - case 1: - param->value = IW_AUTH_WPA_VERSION_WPA; - break; - case 2: - param->value = IW_AUTH_WPA_VERSION_WPA2; - break; - case 0: - default: - param->value = IW_AUTH_WPA_VERSION_DISABLED; - break; - } - break; - - case IW_AUTH_DROP_UNENCRYPTED: - ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r); - if (ret >= 0) - param->value = r.u > 0 ? 1 : 0; - break; - - case IW_AUTH_80211_AUTH_ALG: - ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r); - if (ret >= 0) { - switch (r.u) { - case DOT11_AUTH_OS: - param->value = IW_AUTH_ALG_OPEN_SYSTEM; - break; - case DOT11_AUTH_BOTH: - case DOT11_AUTH_SK: - param->value = IW_AUTH_ALG_SHARED_KEY; - break; - case DOT11_AUTH_NONE: - default: - param->value = 0; - break; - } - } - break; - - case IW_AUTH_WPA_ENABLED: - param->value = wpa > 0 ? 1 : 0; - break; - - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - ret = mgt_get_request(priv, DOT11_OID_DOT1XENABLE, 0, NULL, &r); - if (ret >= 0) - param->value = r.u > 0 ? 1 : 0; - break; - - case IW_AUTH_PRIVACY_INVOKED: - ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r); - if (ret >= 0) - param->value = r.u > 0 ? 1 : 0; - break; - - default: - return -EOPNOTSUPP; - } - return ret; -} - -static int prism54_set_encodeext(struct net_device *ndev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int idx, alg = ext->alg, set_key = 1; - union oid_res_t r; - int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0; - int ret = 0; - - if (islpci_get_state(priv) < PRV_STATE_INIT) - return 0; - - /* Determine and validate the key index */ - idx = (encoding->flags & IW_ENCODE_INDEX) - 1; - if (idx) { - if (idx < 0 || idx > 3) - return -EINVAL; - } else { - ret = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r); - if (ret < 0) - goto out; - idx = r.u; - } - - if (encoding->flags & IW_ENCODE_DISABLED) - alg = IW_ENCODE_ALG_NONE; - - if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { - /* Only set transmit key index here, actual - * key is set below if needed. - */ - ret = mgt_set_request(priv, DOT11_OID_DEFKEYID, 0, &idx); - set_key = ext->key_len > 0 ? 1 : 0; - } - - if (set_key) { - struct obj_key key = { DOT11_PRIV_WEP, 0, "" }; - switch (alg) { - case IW_ENCODE_ALG_NONE: - break; - case IW_ENCODE_ALG_WEP: - if (ext->key_len > KEY_SIZE_WEP104) { - ret = -EINVAL; - goto out; - } - if (ext->key_len > KEY_SIZE_WEP40) - key.length = KEY_SIZE_WEP104; - else - key.length = KEY_SIZE_WEP40; - break; - case IW_ENCODE_ALG_TKIP: - if (ext->key_len > KEY_SIZE_TKIP) { - ret = -EINVAL; - goto out; - } - key.type = DOT11_PRIV_TKIP; - key.length = KEY_SIZE_TKIP; - break; - default: - return -EINVAL; - } - - if (key.length) { - memset(key.key, 0, sizeof(key.key)); - memcpy(key.key, ext->key, ext->key_len); - ret = mgt_set_request(priv, DOT11_OID_DEFKEYX, idx, - &key); - if (ret < 0) - goto out; - } - } - - /* Read the flags */ - if (encoding->flags & IW_ENCODE_DISABLED) { - /* Encoding disabled, - * authen = DOT11_AUTH_OS; - * invoke = 0; - * exunencrypt = 0; */ - } - if (encoding->flags & IW_ENCODE_OPEN) { - /* Encode but accept non-encoded packets. No auth */ - invoke = 1; - } - if (encoding->flags & IW_ENCODE_RESTRICTED) { - /* Refuse non-encoded packets. Auth */ - authen = DOT11_AUTH_BOTH; - invoke = 1; - exunencrypt = 1; - } - - /* do the change if requested */ - if (encoding->flags & IW_ENCODE_MODE) { - ret = mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, - &authen); - ret = mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, - &invoke); - ret = mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, - &exunencrypt); - } - -out: - return ret; -} - - -static int prism54_get_encodeext(struct net_device *ndev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int idx, max_key_len; - union oid_res_t r; - int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0, wpa = 0; - int ret = 0; - - if (islpci_get_state(priv) < PRV_STATE_INIT) - return 0; - - /* first get the flags */ - ret = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r); - authen = r.u; - ret = mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r); - invoke = r.u; - ret = mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r); - exunencrypt = r.u; - if (ret < 0) - goto out; - - max_key_len = encoding->length - sizeof(*ext); - if (max_key_len < 0) - return -EINVAL; - - idx = (encoding->flags & IW_ENCODE_INDEX) - 1; - if (idx) { - if (idx < 0 || idx > 3) - return -EINVAL; - } else { - ret = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r); - if (ret < 0) - goto out; - idx = r.u; - } - - encoding->flags = idx + 1; - memset(ext, 0, sizeof(*ext)); - - switch (authen) { - case DOT11_AUTH_BOTH: - case DOT11_AUTH_SK: - wrqu->encoding.flags |= IW_ENCODE_RESTRICTED; - case DOT11_AUTH_OS: - default: - wrqu->encoding.flags |= IW_ENCODE_OPEN; - break; - } - - down_write(&priv->mib_sem); - wpa = priv->wpa; - up_write(&priv->mib_sem); - - if (authen == DOT11_AUTH_OS && !exunencrypt && !invoke && !wpa) { - /* No encryption */ - ext->alg = IW_ENCODE_ALG_NONE; - ext->key_len = 0; - wrqu->encoding.flags |= IW_ENCODE_DISABLED; - } else { - struct obj_key *key; - - ret = mgt_get_request(priv, DOT11_OID_DEFKEYX, idx, NULL, &r); - if (ret < 0) - goto out; - key = r.ptr; - if (max_key_len < key->length) { - ret = -E2BIG; - goto out; - } - memcpy(ext->key, key->key, key->length); - ext->key_len = key->length; - - switch (key->type) { - case DOT11_PRIV_TKIP: - ext->alg = IW_ENCODE_ALG_TKIP; - break; - default: - case DOT11_PRIV_WEP: - ext->alg = IW_ENCODE_ALG_WEP; - break; - } - wrqu->encoding.flags |= IW_ENCODE_ENABLED; - } - -out: - return ret; -} - - -static int -prism54_reset(struct net_device *ndev, struct iw_request_info *info, - __u32 * uwrq, char *extra) -{ - islpci_reset(netdev_priv(ndev), 0); - - return 0; -} - -static int -prism54_get_oid(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - union oid_res_t r; - int rvalue; - enum oid_num_t n = dwrq->flags; - - rvalue = mgt_get_request(netdev_priv(ndev), n, 0, NULL, &r); - dwrq->length = mgt_response_to_str(n, &r, extra); - if ((isl_oid[n].flags & OID_FLAG_TYPE) != OID_TYPE_U32) - kfree(r.ptr); - return rvalue; -} - -static int -prism54_set_u32(struct net_device *ndev, struct iw_request_info *info, - __u32 * uwrq, char *extra) -{ - u32 oid = uwrq[0], u = uwrq[1]; - - return mgt_set_request(netdev_priv(ndev), oid, 0, &u); -} - -static int -prism54_set_raw(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - u32 oid = dwrq->flags; - - return mgt_set_request(netdev_priv(ndev), oid, 0, extra); -} - -void -prism54_acl_init(struct islpci_acl *acl) -{ - mutex_init(&acl->lock); - INIT_LIST_HEAD(&acl->mac_list); - acl->size = 0; - acl->policy = MAC_POLICY_OPEN; -} - -static void -prism54_clear_mac(struct islpci_acl *acl) -{ - struct list_head *ptr, *next; - struct mac_entry *entry; - - mutex_lock(&acl->lock); - - if (acl->size == 0) { - mutex_unlock(&acl->lock); - return; - } - - for (ptr = acl->mac_list.next, next = ptr->next; - ptr != &acl->mac_list; ptr = next, next = ptr->next) { - entry = list_entry(ptr, struct mac_entry, _list); - list_del(ptr); - kfree(entry); - } - acl->size = 0; - mutex_unlock(&acl->lock); -} - -void -prism54_acl_clean(struct islpci_acl *acl) -{ - prism54_clear_mac(acl); -} - -static int -prism54_add_mac(struct net_device *ndev, struct iw_request_info *info, - struct sockaddr *awrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct islpci_acl *acl = &priv->acl; - struct mac_entry *entry; - struct sockaddr *addr = (struct sockaddr *) extra; - - if (addr->sa_family != ARPHRD_ETHER) - return -EOPNOTSUPP; - - entry = kmalloc(sizeof (struct mac_entry), GFP_KERNEL); - if (entry == NULL) - return -ENOMEM; - - memcpy(entry->addr, addr->sa_data, ETH_ALEN); - - if (mutex_lock_interruptible(&acl->lock)) { - kfree(entry); - return -ERESTARTSYS; - } - list_add_tail(&entry->_list, &acl->mac_list); - acl->size++; - mutex_unlock(&acl->lock); - - return 0; -} - -static int -prism54_del_mac(struct net_device *ndev, struct iw_request_info *info, - struct sockaddr *awrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct islpci_acl *acl = &priv->acl; - struct mac_entry *entry; - struct sockaddr *addr = (struct sockaddr *) extra; - - if (addr->sa_family != ARPHRD_ETHER) - return -EOPNOTSUPP; - - if (mutex_lock_interruptible(&acl->lock)) - return -ERESTARTSYS; - list_for_each_entry(entry, &acl->mac_list, _list) { - if (ether_addr_equal(entry->addr, addr->sa_data)) { - list_del(&entry->_list); - acl->size--; - kfree(entry); - mutex_unlock(&acl->lock); - return 0; - } - } - mutex_unlock(&acl->lock); - return -EINVAL; -} - -static int -prism54_get_mac(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct islpci_acl *acl = &priv->acl; - struct mac_entry *entry; - struct sockaddr *dst = (struct sockaddr *) extra; - - dwrq->length = 0; - - if (mutex_lock_interruptible(&acl->lock)) - return -ERESTARTSYS; - - list_for_each_entry(entry, &acl->mac_list, _list) { - memcpy(dst->sa_data, entry->addr, ETH_ALEN); - dst->sa_family = ARPHRD_ETHER; - dwrq->length++; - dst++; - } - mutex_unlock(&acl->lock); - return 0; -} - -/* Setting policy also clears the MAC acl, even if we don't change the default - * policy - */ - -static int -prism54_set_policy(struct net_device *ndev, struct iw_request_info *info, - __u32 * uwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct islpci_acl *acl = &priv->acl; - u32 mlmeautolevel; - - prism54_clear_mac(acl); - - if ((*uwrq < MAC_POLICY_OPEN) || (*uwrq > MAC_POLICY_REJECT)) - return -EINVAL; - - down_write(&priv->mib_sem); - - acl->policy = *uwrq; - - /* the ACL code needs an intermediate mlmeautolevel */ - if ((priv->iw_mode == IW_MODE_MASTER) && - (acl->policy != MAC_POLICY_OPEN)) - mlmeautolevel = DOT11_MLME_INTERMEDIATE; - else - mlmeautolevel = CARD_DEFAULT_MLME_MODE; - if (priv->wpa) - mlmeautolevel = DOT11_MLME_EXTENDED; - mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel); - /* restart the card with our new policy */ - if (mgt_commit(priv)) { - up_write(&priv->mib_sem); - return -EIO; - } - up_write(&priv->mib_sem); - - return 0; -} - -static int -prism54_get_policy(struct net_device *ndev, struct iw_request_info *info, - __u32 * uwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct islpci_acl *acl = &priv->acl; - - *uwrq = acl->policy; - - return 0; -} - -/* Return 1 only if client should be accepted. */ - -static int -prism54_mac_accept(struct islpci_acl *acl, char *mac) -{ - struct mac_entry *entry; - int res = 0; - - if (mutex_lock_interruptible(&acl->lock)) - return -ERESTARTSYS; - - if (acl->policy == MAC_POLICY_OPEN) { - mutex_unlock(&acl->lock); - return 1; - } - - list_for_each_entry(entry, &acl->mac_list, _list) { - if (memcmp(entry->addr, mac, ETH_ALEN) == 0) { - res = 1; - break; - } - } - res = (acl->policy == MAC_POLICY_ACCEPT) ? !res : res; - mutex_unlock(&acl->lock); - - return res; -} - -static int -prism54_kick_all(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *dwrq, char *extra) -{ - struct obj_mlme *mlme; - int rvalue; - - mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL); - if (mlme == NULL) - return -ENOMEM; - - /* Tell the card to kick every client */ - mlme->id = 0; - rvalue = - mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme); - kfree(mlme); - - return rvalue; -} - -static int -prism54_kick_mac(struct net_device *ndev, struct iw_request_info *info, - struct sockaddr *awrq, char *extra) -{ - struct obj_mlme *mlme; - struct sockaddr *addr = (struct sockaddr *) extra; - int rvalue; - - if (addr->sa_family != ARPHRD_ETHER) - return -EOPNOTSUPP; - - mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL); - if (mlme == NULL) - return -ENOMEM; - - /* Tell the card to only kick the corresponding bastard */ - memcpy(mlme->address, addr->sa_data, ETH_ALEN); - mlme->id = -1; - rvalue = - mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme); - - kfree(mlme); - - return rvalue; -} - -/* Translate a TRAP oid into a wireless event. Called in islpci_mgt_receive. */ - -static void -format_event(islpci_private *priv, char *dest, const char *str, - const struct obj_mlme *mlme, u16 *length, int error) -{ - int n = snprintf(dest, IW_CUSTOM_MAX, - "%s %s %pM %s (%2.2X)", - str, - ((priv->iw_mode == IW_MODE_MASTER) ? "from" : "to"), - mlme->address, - (error ? (mlme->code ? " : REJECTED " : " : ACCEPTED ") - : ""), mlme->code); - BUG_ON(n > IW_CUSTOM_MAX); - *length = n; -} - -static void -send_formatted_event(islpci_private *priv, const char *str, - const struct obj_mlme *mlme, int error) -{ - union iwreq_data wrqu; - char *memptr; - - memptr = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL); - if (!memptr) - return; - wrqu.data.pointer = memptr; - wrqu.data.length = 0; - format_event(priv, memptr, str, mlme, &wrqu.data.length, - error); - wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, memptr); - kfree(memptr); -} - -static void -send_simple_event(islpci_private *priv, const char *str) -{ - union iwreq_data wrqu; - char *memptr; - int n = strlen(str); - - memptr = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL); - if (!memptr) - return; - BUG_ON(n >= IW_CUSTOM_MAX); - wrqu.data.pointer = memptr; - wrqu.data.length = n; - strcpy(memptr, str); - wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, memptr); - kfree(memptr); -} - -static void -link_changed(struct net_device *ndev, u32 bitrate) -{ - islpci_private *priv = netdev_priv(ndev); - - if (bitrate) { - netif_carrier_on(ndev); - if (priv->iw_mode == IW_MODE_INFRA) { - union iwreq_data uwrq; - prism54_get_wap(ndev, NULL, (struct sockaddr *) &uwrq, - NULL); - wireless_send_event(ndev, SIOCGIWAP, &uwrq, NULL); - } else - send_simple_event(netdev_priv(ndev), - "Link established"); - } else { - netif_carrier_off(ndev); - send_simple_event(netdev_priv(ndev), "Link lost"); - } -} - -/* Beacon/ProbeResp payload header */ -struct ieee80211_beacon_phdr { - u8 timestamp[8]; - u16 beacon_int; - u16 capab_info; -} __packed; - -#define WLAN_EID_GENERIC 0xdd -static u8 wpa_oid[4] = { 0x00, 0x50, 0xf2, 1 }; - -static void -prism54_wpa_bss_ie_add(islpci_private *priv, u8 *bssid, - u8 *wpa_ie, size_t wpa_ie_len) -{ - struct list_head *ptr; - struct islpci_bss_wpa_ie *bss = NULL; - - if (wpa_ie_len > MAX_WPA_IE_LEN) - wpa_ie_len = MAX_WPA_IE_LEN; - - mutex_lock(&priv->wpa_lock); - - /* try to use existing entry */ - list_for_each(ptr, &priv->bss_wpa_list) { - bss = list_entry(ptr, struct islpci_bss_wpa_ie, list); - if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) { - list_move(&bss->list, &priv->bss_wpa_list); - break; - } - bss = NULL; - } - - if (bss == NULL) { - /* add a new BSS entry; if max number of entries is already - * reached, replace the least recently updated */ - if (priv->num_bss_wpa >= MAX_BSS_WPA_IE_COUNT) { - bss = list_entry(priv->bss_wpa_list.prev, - struct islpci_bss_wpa_ie, list); - list_del(&bss->list); - } else { - bss = kzalloc(sizeof (*bss), GFP_ATOMIC); - if (bss != NULL) - priv->num_bss_wpa++; - } - if (bss != NULL) { - memcpy(bss->bssid, bssid, ETH_ALEN); - list_add(&bss->list, &priv->bss_wpa_list); - } - } - - if (bss != NULL) { - memcpy(bss->wpa_ie, wpa_ie, wpa_ie_len); - bss->wpa_ie_len = wpa_ie_len; - bss->last_update = jiffies; - } else { - printk(KERN_DEBUG "Failed to add BSS WPA entry for " - "%pM\n", bssid); - } - - /* expire old entries from WPA list */ - while (priv->num_bss_wpa > 0) { - bss = list_entry(priv->bss_wpa_list.prev, - struct islpci_bss_wpa_ie, list); - if (!time_after(jiffies, bss->last_update + 60 * HZ)) - break; - - list_del(&bss->list); - priv->num_bss_wpa--; - kfree(bss); - } - - mutex_unlock(&priv->wpa_lock); -} - -static size_t -prism54_wpa_bss_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie) -{ - struct list_head *ptr; - struct islpci_bss_wpa_ie *bss = NULL; - size_t len = 0; - - mutex_lock(&priv->wpa_lock); - - list_for_each(ptr, &priv->bss_wpa_list) { - bss = list_entry(ptr, struct islpci_bss_wpa_ie, list); - if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) - break; - bss = NULL; - } - if (bss) { - len = bss->wpa_ie_len; - memcpy(wpa_ie, bss->wpa_ie, len); - } - mutex_unlock(&priv->wpa_lock); - - return len; -} - -void -prism54_wpa_bss_ie_init(islpci_private *priv) -{ - INIT_LIST_HEAD(&priv->bss_wpa_list); - mutex_init(&priv->wpa_lock); -} - -void -prism54_wpa_bss_ie_clean(islpci_private *priv) -{ - struct islpci_bss_wpa_ie *bss, *n; - - list_for_each_entry_safe(bss, n, &priv->bss_wpa_list, list) { - kfree(bss); - } -} - -static void -prism54_process_bss_data(islpci_private *priv, u32 oid, u8 *addr, - u8 *payload, size_t len) -{ - struct ieee80211_beacon_phdr *hdr; - u8 *pos, *end; - - if (!priv->wpa) - return; - - hdr = (struct ieee80211_beacon_phdr *) payload; - pos = (u8 *) (hdr + 1); - end = payload + len; - while (pos < end) { - if (pos + 2 + pos[1] > end) { - printk(KERN_DEBUG "Parsing Beacon/ProbeResp failed " - "for %pM\n", addr); - return; - } - if (pos[0] == WLAN_EID_GENERIC && pos[1] >= 4 && - memcmp(pos + 2, wpa_oid, 4) == 0) { - prism54_wpa_bss_ie_add(priv, addr, pos, pos[1] + 2); - return; - } - pos += 2 + pos[1]; - } -} - -static void -handle_request(islpci_private *priv, struct obj_mlme *mlme, enum oid_num_t oid) -{ - if (((mlme->state == DOT11_STATE_AUTHING) || - (mlme->state == DOT11_STATE_ASSOCING)) - && mgt_mlme_answer(priv)) { - /* Someone is requesting auth and we must respond. Just send back - * the trap with error code set accordingly. - */ - mlme->code = prism54_mac_accept(&priv->acl, - mlme->address) ? 0 : 1; - mgt_set_request(priv, oid, 0, mlme); - } -} - -static int -prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid, - char *data) -{ - struct obj_mlme *mlme = (struct obj_mlme *) data; - struct obj_mlmeex *mlmeex = (struct obj_mlmeex *) data; - struct obj_mlmeex *confirm; - u8 wpa_ie[MAX_WPA_IE_LEN]; - int wpa_ie_len; - size_t len = 0; /* u16, better? */ - u8 *payload = NULL, *pos = NULL; - int ret; - - /* I think all trapable objects are listed here. - * Some oids have a EX version. The difference is that they are emitted - * in DOT11_MLME_EXTENDED mode (set with DOT11_OID_MLMEAUTOLEVEL) - * with more info. - * The few events already defined by the wireless tools are not really - * suited. We use the more flexible custom event facility. - */ - - if (oid >= DOT11_OID_BEACON) { - len = mlmeex->size; - payload = pos = mlmeex->data; - } - - /* I fear prism54_process_bss_data won't work with big endian data */ - if ((oid == DOT11_OID_BEACON) || (oid == DOT11_OID_PROBE)) - prism54_process_bss_data(priv, oid, mlmeex->address, - payload, len); - - mgt_le_to_cpu(isl_oid[oid].flags & OID_FLAG_TYPE, (void *) mlme); - - switch (oid) { - - case GEN_OID_LINKSTATE: - link_changed(priv->ndev, (u32) *data); - break; - - case DOT11_OID_MICFAILURE: - send_simple_event(priv, "Mic failure"); - break; - - case DOT11_OID_DEAUTHENTICATE: - send_formatted_event(priv, "DeAuthenticate request", mlme, 0); - break; - - case DOT11_OID_AUTHENTICATE: - handle_request(priv, mlme, oid); - send_formatted_event(priv, "Authenticate request", mlme, 1); - break; - - case DOT11_OID_DISASSOCIATE: - send_formatted_event(priv, "Disassociate request", mlme, 0); - break; - - case DOT11_OID_ASSOCIATE: - handle_request(priv, mlme, oid); - send_formatted_event(priv, "Associate request", mlme, 1); - break; - - case DOT11_OID_REASSOCIATE: - handle_request(priv, mlme, oid); - send_formatted_event(priv, "ReAssociate request", mlme, 1); - break; - - case DOT11_OID_BEACON: - send_formatted_event(priv, - "Received a beacon from an unknown AP", - mlme, 0); - break; - - case DOT11_OID_PROBE: - /* we received a probe from a client. */ - send_formatted_event(priv, "Received a probe from client", mlme, - 0); - break; - - /* Note : "mlme" is actually a "struct obj_mlmeex *" here, but this - * is backward compatible layout-wise with "struct obj_mlme". - */ - - case DOT11_OID_DEAUTHENTICATEEX: - send_formatted_event(priv, "DeAuthenticate request", mlme, 0); - break; - - case DOT11_OID_AUTHENTICATEEX: - handle_request(priv, mlme, oid); - send_formatted_event(priv, "Authenticate request (ex)", mlme, 1); - - if (priv->iw_mode != IW_MODE_MASTER - && mlmeex->state != DOT11_STATE_AUTHING) - break; - - confirm = kmalloc(sizeof(struct obj_mlmeex) + 6, GFP_ATOMIC); - - if (!confirm) - break; - - memcpy(&confirm->address, mlmeex->address, ETH_ALEN); - printk(KERN_DEBUG "Authenticate from: address:\t%pM\n", - mlmeex->address); - confirm->id = -1; /* or mlmeex->id ? */ - confirm->state = 0; /* not used */ - confirm->code = 0; - confirm->size = 6; - confirm->data[0] = 0x00; - confirm->data[1] = 0x00; - confirm->data[2] = 0x02; - confirm->data[3] = 0x00; - confirm->data[4] = 0x00; - confirm->data[5] = 0x00; - - ret = mgt_set_varlen(priv, DOT11_OID_ASSOCIATEEX, confirm, 6); - - kfree(confirm); - if (ret) - return ret; - break; - - case DOT11_OID_DISASSOCIATEEX: - send_formatted_event(priv, "Disassociate request (ex)", mlme, 0); - break; - - case DOT11_OID_ASSOCIATEEX: - handle_request(priv, mlme, oid); - send_formatted_event(priv, "Associate request (ex)", mlme, 1); - - if (priv->iw_mode != IW_MODE_MASTER - && mlmeex->state != DOT11_STATE_ASSOCING) - break; - - confirm = kmalloc(sizeof(struct obj_mlmeex), GFP_ATOMIC); - - if (!confirm) - break; - - memcpy(&confirm->address, mlmeex->address, ETH_ALEN); - - confirm->id = ((struct obj_mlmeex *)mlme)->id; - confirm->state = 0; /* not used */ - confirm->code = 0; - - wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie); - - if (!wpa_ie_len) { - printk(KERN_DEBUG "No WPA IE found from address:\t%pM\n", - mlmeex->address); - kfree(confirm); - break; - } - - confirm->size = wpa_ie_len; - memcpy(&confirm->data, wpa_ie, wpa_ie_len); - - mgt_set_varlen(priv, oid, confirm, wpa_ie_len); - - kfree(confirm); - - break; - - case DOT11_OID_REASSOCIATEEX: - handle_request(priv, mlme, oid); - send_formatted_event(priv, "Reassociate request (ex)", mlme, 1); - - if (priv->iw_mode != IW_MODE_MASTER - && mlmeex->state != DOT11_STATE_ASSOCING) - break; - - confirm = kmalloc(sizeof(struct obj_mlmeex), GFP_ATOMIC); - - if (!confirm) - break; - - memcpy(&confirm->address, mlmeex->address, ETH_ALEN); - - confirm->id = mlmeex->id; - confirm->state = 0; /* not used */ - confirm->code = 0; - - wpa_ie_len = prism54_wpa_bss_ie_get(priv, mlmeex->address, wpa_ie); - - if (!wpa_ie_len) { - printk(KERN_DEBUG "No WPA IE found from address:\t%pM\n", - mlmeex->address); - kfree(confirm); - break; - } - - confirm->size = wpa_ie_len; - memcpy(&confirm->data, wpa_ie, wpa_ie_len); - - mgt_set_varlen(priv, oid, confirm, wpa_ie_len); - - kfree(confirm); - - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* - * Process a device trap. This is called via schedule_work(), outside of - * interrupt context, no locks held. - */ -void -prism54_process_trap(struct work_struct *work) -{ - struct islpci_mgmtframe *frame = - container_of(work, struct islpci_mgmtframe, ws); - struct net_device *ndev = frame->ndev; - enum oid_num_t n = mgt_oidtonum(frame->header->oid); - - if (n != OID_NUM_LAST) - prism54_process_trap_helper(netdev_priv(ndev), n, frame->data); - islpci_mgt_release(frame); -} - -int -prism54_set_mac_address(struct net_device *ndev, void *addr) -{ - islpci_private *priv = netdev_priv(ndev); - int ret; - - if (ndev->addr_len != 6) - return -EINVAL; - ret = mgt_set_request(priv, GEN_OID_MACADDRESS, 0, - &((struct sockaddr *) addr)->sa_data); - if (!ret) - memcpy(priv->ndev->dev_addr, - &((struct sockaddr *) addr)->sa_data, ETH_ALEN); - - return ret; -} - -#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12 - -static int -prism54_set_wpa(struct net_device *ndev, struct iw_request_info *info, - __u32 * uwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - u32 mlme, authen, dot1x, filter, wep; - - if (islpci_get_state(priv) < PRV_STATE_INIT) - return 0; - - wep = 1; /* For privacy invoked */ - filter = 1; /* Filter out all unencrypted frames */ - dot1x = 0x01; /* To enable eap filter */ - mlme = DOT11_MLME_EXTENDED; - authen = DOT11_AUTH_OS; /* Only WEP uses _SK and _BOTH */ - - down_write(&priv->mib_sem); - priv->wpa = *uwrq; - - switch (priv->wpa) { - default: - case 0: /* Clears/disables WPA and friends */ - wep = 0; - filter = 0; /* Do not filter un-encrypted data */ - dot1x = 0; - mlme = DOT11_MLME_AUTO; - printk("%s: Disabling WPA\n", ndev->name); - break; - case 2: - case 1: /* WPA */ - printk("%s: Enabling WPA\n", ndev->name); - break; - } - up_write(&priv->mib_sem); - - mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen); - mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &wep); - mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0, &filter); - mgt_set_request(priv, DOT11_OID_DOT1XENABLE, 0, &dot1x); - mgt_set_request(priv, DOT11_OID_MLMEAUTOLEVEL, 0, &mlme); - - return 0; -} - -static int -prism54_get_wpa(struct net_device *ndev, struct iw_request_info *info, - __u32 * uwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - *uwrq = priv->wpa; - return 0; -} - -static int -prism54_set_prismhdr(struct net_device *ndev, struct iw_request_info *info, - __u32 * uwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - priv->monitor_type = - (*uwrq ? ARPHRD_IEEE80211_PRISM : ARPHRD_IEEE80211); - if (priv->iw_mode == IW_MODE_MONITOR) - priv->ndev->type = priv->monitor_type; - - return 0; -} - -static int -prism54_get_prismhdr(struct net_device *ndev, struct iw_request_info *info, - __u32 * uwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - *uwrq = (priv->monitor_type == ARPHRD_IEEE80211_PRISM); - return 0; -} - -static int -prism54_debug_oid(struct net_device *ndev, struct iw_request_info *info, - __u32 * uwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - - priv->priv_oid = *uwrq; - printk("%s: oid 0x%08X\n", ndev->name, *uwrq); - - return 0; -} - -static int -prism54_debug_get_oid(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct islpci_mgmtframe *response; - int ret = -EIO; - - printk("%s: get_oid 0x%08X\n", ndev->name, priv->priv_oid); - data->length = 0; - - if (islpci_get_state(priv) >= PRV_STATE_INIT) { - ret = - islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET, - priv->priv_oid, extra, 256, - &response); - printk("%s: ret: %i\n", ndev->name, ret); - if (ret || !response - || response->header->operation == PIMFOR_OP_ERROR) { - if (response) { - islpci_mgt_release(response); - } - printk("%s: EIO\n", ndev->name); - ret = -EIO; - } - if (!ret) { - data->length = response->header->length; - memcpy(extra, response->data, data->length); - islpci_mgt_release(response); - printk("%s: len: %i\n", ndev->name, data->length); - } - } - - return ret; -} - -static int -prism54_debug_set_oid(struct net_device *ndev, struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - struct islpci_mgmtframe *response; - int ret = 0, response_op = PIMFOR_OP_ERROR; - - printk("%s: set_oid 0x%08X\tlen: %d\n", ndev->name, priv->priv_oid, - data->length); - - if (islpci_get_state(priv) >= PRV_STATE_INIT) { - ret = - islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, - priv->priv_oid, extra, data->length, - &response); - printk("%s: ret: %i\n", ndev->name, ret); - if (ret || !response - || response->header->operation == PIMFOR_OP_ERROR) { - if (response) { - islpci_mgt_release(response); - } - printk("%s: EIO\n", ndev->name); - ret = -EIO; - } - if (!ret) { - response_op = response->header->operation; - printk("%s: response_op: %i\n", ndev->name, - response_op); - islpci_mgt_release(response); - } - } - - return (ret ? ret : -EINPROGRESS); -} - -static int -prism54_set_spy(struct net_device *ndev, - struct iw_request_info *info, - union iwreq_data *uwrq, char *extra) -{ - islpci_private *priv = netdev_priv(ndev); - u32 u; - enum oid_num_t oid = OID_INL_CONFIG; - - down_write(&priv->mib_sem); - mgt_get(priv, OID_INL_CONFIG, &u); - - if ((uwrq->data.length == 0) && (priv->spy_data.spy_number > 0)) - /* disable spy */ - u &= ~INL_CONFIG_RXANNEX; - else if ((uwrq->data.length > 0) && (priv->spy_data.spy_number == 0)) - /* enable spy */ - u |= INL_CONFIG_RXANNEX; - - mgt_set(priv, OID_INL_CONFIG, &u); - mgt_commit_list(priv, &oid, 1); - up_write(&priv->mib_sem); - - return iw_handler_set_spy(ndev, info, uwrq, extra); -} - -static const iw_handler prism54_handler[] = { - (iw_handler) prism54_commit, /* SIOCSIWCOMMIT */ - (iw_handler) prism54_get_name, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) prism54_set_freq, /* SIOCSIWFREQ */ - (iw_handler) prism54_get_freq, /* SIOCGIWFREQ */ - (iw_handler) prism54_set_mode, /* SIOCSIWMODE */ - (iw_handler) prism54_get_mode, /* SIOCGIWMODE */ - (iw_handler) prism54_set_sens, /* SIOCSIWSENS */ - (iw_handler) prism54_get_sens, /* SIOCGIWSENS */ - (iw_handler) NULL, /* SIOCSIWRANGE */ - (iw_handler) prism54_get_range, /* SIOCGIWRANGE */ - (iw_handler) NULL, /* SIOCSIWPRIV */ - (iw_handler) NULL, /* SIOCGIWPRIV */ - (iw_handler) NULL, /* SIOCSIWSTATS */ - (iw_handler) NULL, /* SIOCGIWSTATS */ - prism54_set_spy, /* SIOCSIWSPY */ - iw_handler_get_spy, /* SIOCGIWSPY */ - iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ - iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ - (iw_handler) prism54_set_wap, /* SIOCSIWAP */ - (iw_handler) prism54_get_wap, /* SIOCGIWAP */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCGIWAPLIST deprecated */ - (iw_handler) prism54_set_scan, /* SIOCSIWSCAN */ - (iw_handler) prism54_get_scan, /* SIOCGIWSCAN */ - (iw_handler) prism54_set_essid, /* SIOCSIWESSID */ - (iw_handler) prism54_get_essid, /* SIOCGIWESSID */ - (iw_handler) prism54_set_nick, /* SIOCSIWNICKN */ - (iw_handler) prism54_get_nick, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) prism54_set_rate, /* SIOCSIWRATE */ - (iw_handler) prism54_get_rate, /* SIOCGIWRATE */ - (iw_handler) prism54_set_rts, /* SIOCSIWRTS */ - (iw_handler) prism54_get_rts, /* SIOCGIWRTS */ - (iw_handler) prism54_set_frag, /* SIOCSIWFRAG */ - (iw_handler) prism54_get_frag, /* SIOCGIWFRAG */ - (iw_handler) prism54_set_txpower, /* SIOCSIWTXPOW */ - (iw_handler) prism54_get_txpower, /* SIOCGIWTXPOW */ - (iw_handler) prism54_set_retry, /* SIOCSIWRETRY */ - (iw_handler) prism54_get_retry, /* SIOCGIWRETRY */ - (iw_handler) prism54_set_encode, /* SIOCSIWENCODE */ - (iw_handler) prism54_get_encode, /* SIOCGIWENCODE */ - (iw_handler) NULL, /* SIOCSIWPOWER */ - (iw_handler) NULL, /* SIOCGIWPOWER */ - NULL, /* -- hole -- */ - NULL, /* -- hole -- */ - (iw_handler) prism54_set_genie, /* SIOCSIWGENIE */ - (iw_handler) prism54_get_genie, /* SIOCGIWGENIE */ - (iw_handler) prism54_set_auth, /* SIOCSIWAUTH */ - (iw_handler) prism54_get_auth, /* SIOCGIWAUTH */ - (iw_handler) prism54_set_encodeext, /* SIOCSIWENCODEEXT */ - (iw_handler) prism54_get_encodeext, /* SIOCGIWENCODEEXT */ - NULL, /* SIOCSIWPMKSA */ -}; - -/* The low order bit identify a SET (0) or a GET (1) ioctl. */ - -#define PRISM54_RESET SIOCIWFIRSTPRIV -#define PRISM54_GET_POLICY SIOCIWFIRSTPRIV+1 -#define PRISM54_SET_POLICY SIOCIWFIRSTPRIV+2 -#define PRISM54_GET_MAC SIOCIWFIRSTPRIV+3 -#define PRISM54_ADD_MAC SIOCIWFIRSTPRIV+4 - -#define PRISM54_DEL_MAC SIOCIWFIRSTPRIV+6 - -#define PRISM54_KICK_MAC SIOCIWFIRSTPRIV+8 - -#define PRISM54_KICK_ALL SIOCIWFIRSTPRIV+10 - -#define PRISM54_GET_WPA SIOCIWFIRSTPRIV+11 -#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12 - -#define PRISM54_DBG_OID SIOCIWFIRSTPRIV+14 -#define PRISM54_DBG_GET_OID SIOCIWFIRSTPRIV+15 -#define PRISM54_DBG_SET_OID SIOCIWFIRSTPRIV+16 - -#define PRISM54_GET_OID SIOCIWFIRSTPRIV+17 -#define PRISM54_SET_OID_U32 SIOCIWFIRSTPRIV+18 -#define PRISM54_SET_OID_STR SIOCIWFIRSTPRIV+20 -#define PRISM54_SET_OID_ADDR SIOCIWFIRSTPRIV+22 - -#define PRISM54_GET_PRISMHDR SIOCIWFIRSTPRIV+23 -#define PRISM54_SET_PRISMHDR SIOCIWFIRSTPRIV+24 - -#define IWPRIV_SET_U32(n,x) { n, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x } -#define IWPRIV_SET_SSID(n,x) { n, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x } -#define IWPRIV_SET_ADDR(n,x) { n, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "s_"x } -#define IWPRIV_GET(n,x) { n, 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | PRIV_STR_SIZE, "g_"x } - -#define IWPRIV_U32(n,x) IWPRIV_SET_U32(n,x), IWPRIV_GET(n,x) -#define IWPRIV_SSID(n,x) IWPRIV_SET_SSID(n,x), IWPRIV_GET(n,x) -#define IWPRIV_ADDR(n,x) IWPRIV_SET_ADDR(n,x), IWPRIV_GET(n,x) - -/* Note : limited to 128 private ioctls (wireless tools 26) */ - -static const struct iw_priv_args prism54_private_args[] = { -/*{ cmd, set_args, get_args, name } */ - {PRISM54_RESET, 0, 0, "reset"}, - {PRISM54_GET_PRISMHDR, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_prismhdr"}, - {PRISM54_SET_PRISMHDR, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, - "set_prismhdr"}, - {PRISM54_GET_POLICY, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "getPolicy"}, - {PRISM54_SET_POLICY, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, - "setPolicy"}, - {PRISM54_GET_MAC, 0, IW_PRIV_TYPE_ADDR | 64, "getMac"}, - {PRISM54_ADD_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, - "addMac"}, - {PRISM54_DEL_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, - "delMac"}, - {PRISM54_KICK_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, - "kickMac"}, - {PRISM54_KICK_ALL, 0, 0, "kickAll"}, - {PRISM54_GET_WPA, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_wpa"}, - {PRISM54_SET_WPA, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, - "set_wpa"}, - {PRISM54_DBG_OID, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, - "dbg_oid"}, - {PRISM54_DBG_GET_OID, 0, IW_PRIV_TYPE_BYTE | 256, "dbg_get_oid"}, - {PRISM54_DBG_SET_OID, IW_PRIV_TYPE_BYTE | 256, 0, "dbg_set_oid"}, - /* --- sub-ioctls handlers --- */ - {PRISM54_GET_OID, - 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | PRIV_STR_SIZE, ""}, - {PRISM54_SET_OID_U32, - IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, ""}, - {PRISM54_SET_OID_STR, - IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, ""}, - {PRISM54_SET_OID_ADDR, - IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, ""}, - /* --- sub-ioctls definitions --- */ - IWPRIV_ADDR(GEN_OID_MACADDRESS, "addr"), - IWPRIV_GET(GEN_OID_LINKSTATE, "linkstate"), - IWPRIV_U32(DOT11_OID_BSSTYPE, "bsstype"), - IWPRIV_ADDR(DOT11_OID_BSSID, "bssid"), - IWPRIV_U32(DOT11_OID_STATE, "state"), - IWPRIV_U32(DOT11_OID_AID, "aid"), - - IWPRIV_SSID(DOT11_OID_SSIDOVERRIDE, "ssidoverride"), - - IWPRIV_U32(DOT11_OID_MEDIUMLIMIT, "medlimit"), - IWPRIV_U32(DOT11_OID_BEACONPERIOD, "beacon"), - IWPRIV_U32(DOT11_OID_DTIMPERIOD, "dtimperiod"), - - IWPRIV_U32(DOT11_OID_AUTHENABLE, "authenable"), - IWPRIV_U32(DOT11_OID_PRIVACYINVOKED, "privinvok"), - IWPRIV_U32(DOT11_OID_EXUNENCRYPTED, "exunencrypt"), - - IWPRIV_U32(DOT11_OID_REKEYTHRESHOLD, "rekeythresh"), - - IWPRIV_U32(DOT11_OID_MAXTXLIFETIME, "maxtxlife"), - IWPRIV_U32(DOT11_OID_MAXRXLIFETIME, "maxrxlife"), - IWPRIV_U32(DOT11_OID_ALOFT_FIXEDRATE, "fixedrate"), - IWPRIV_U32(DOT11_OID_MAXFRAMEBURST, "frameburst"), - IWPRIV_U32(DOT11_OID_PSM, "psm"), - - IWPRIV_U32(DOT11_OID_BRIDGELOCAL, "bridge"), - IWPRIV_U32(DOT11_OID_CLIENTS, "clients"), - IWPRIV_U32(DOT11_OID_CLIENTSASSOCIATED, "clientassoc"), - IWPRIV_U32(DOT11_OID_DOT1XENABLE, "dot1xenable"), - IWPRIV_U32(DOT11_OID_ANTENNARX, "rxant"), - IWPRIV_U32(DOT11_OID_ANTENNATX, "txant"), - IWPRIV_U32(DOT11_OID_ANTENNADIVERSITY, "antdivers"), - IWPRIV_U32(DOT11_OID_EDTHRESHOLD, "edthresh"), - IWPRIV_U32(DOT11_OID_PREAMBLESETTINGS, "preamble"), - IWPRIV_GET(DOT11_OID_RATES, "rates"), - IWPRIV_U32(DOT11_OID_OUTPUTPOWER, ".11outpower"), - IWPRIV_GET(DOT11_OID_SUPPORTEDRATES, "supprates"), - IWPRIV_GET(DOT11_OID_SUPPORTEDFREQUENCIES, "suppfreq"), - - IWPRIV_U32(DOT11_OID_NOISEFLOOR, "noisefloor"), - IWPRIV_GET(DOT11_OID_FREQUENCYACTIVITY, "freqactivity"), - IWPRIV_U32(DOT11_OID_NONERPPROTECTION, "nonerpprotec"), - IWPRIV_U32(DOT11_OID_PROFILES, "profile"), - IWPRIV_GET(DOT11_OID_EXTENDEDRATES, "extrates"), - IWPRIV_U32(DOT11_OID_MLMEAUTOLEVEL, "mlmelevel"), - - IWPRIV_GET(DOT11_OID_BSSS, "bsss"), - IWPRIV_GET(DOT11_OID_BSSLIST, "bsslist"), - IWPRIV_U32(OID_INL_MODE, "mode"), - IWPRIV_U32(OID_INL_CONFIG, "config"), - IWPRIV_U32(OID_INL_DOT11D_CONFORMANCE, ".11dconform"), - IWPRIV_GET(OID_INL_PHYCAPABILITIES, "phycapa"), - IWPRIV_U32(OID_INL_OUTPUTPOWER, "outpower"), -}; - -static const iw_handler prism54_private_handler[] = { - (iw_handler) prism54_reset, - (iw_handler) prism54_get_policy, - (iw_handler) prism54_set_policy, - (iw_handler) prism54_get_mac, - (iw_handler) prism54_add_mac, - (iw_handler) NULL, - (iw_handler) prism54_del_mac, - (iw_handler) NULL, - (iw_handler) prism54_kick_mac, - (iw_handler) NULL, - (iw_handler) prism54_kick_all, - (iw_handler) prism54_get_wpa, - (iw_handler) prism54_set_wpa, - (iw_handler) NULL, - (iw_handler) prism54_debug_oid, - (iw_handler) prism54_debug_get_oid, - (iw_handler) prism54_debug_set_oid, - (iw_handler) prism54_get_oid, - (iw_handler) prism54_set_u32, - (iw_handler) NULL, - (iw_handler) prism54_set_raw, - (iw_handler) NULL, - (iw_handler) prism54_set_raw, - (iw_handler) prism54_get_prismhdr, - (iw_handler) prism54_set_prismhdr, -}; - -const struct iw_handler_def prism54_handler_def = { - .num_standard = ARRAY_SIZE(prism54_handler), - .num_private = ARRAY_SIZE(prism54_private_handler), - .num_private_args = ARRAY_SIZE(prism54_private_args), - .standard = (iw_handler *) prism54_handler, - .private = (iw_handler *) prism54_private_handler, - .private_args = (struct iw_priv_args *) prism54_private_args, - .get_wireless_stats = prism54_get_wireless_stats, -}; diff --git a/drivers/net/wireless/prism54/isl_ioctl.h b/drivers/net/wireless/prism54/isl_ioctl.h deleted file mode 100644 index 842a2549facc..000000000000 --- a/drivers/net/wireless/prism54/isl_ioctl.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2002 Intersil Americas Inc. - * (C) 2003 Aurelien Alleaume - * (C) 2003 Luis R. Rodriguez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#ifndef _ISL_IOCTL_H -#define _ISL_IOCTL_H - -#include "islpci_mgt.h" -#include "islpci_dev.h" - -#include /* New driver API */ - -#define SUPPORTED_WIRELESS_EXT 19 - -void prism54_mib_init(islpci_private *); - -struct iw_statistics *prism54_get_wireless_stats(struct net_device *); -void prism54_update_stats(struct work_struct *); - -void prism54_acl_init(struct islpci_acl *); -void prism54_acl_clean(struct islpci_acl *); - -void prism54_process_trap(struct work_struct *); - -void prism54_wpa_bss_ie_init(islpci_private *priv); -void prism54_wpa_bss_ie_clean(islpci_private *priv); - -int prism54_set_mac_address(struct net_device *, void *); - -extern const struct iw_handler_def prism54_handler_def; - -#endif /* _ISL_IOCTL_H */ diff --git a/drivers/net/wireless/prism54/isl_oid.h b/drivers/net/wireless/prism54/isl_oid.h deleted file mode 100644 index 83fec557997e..000000000000 --- a/drivers/net/wireless/prism54/isl_oid.h +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Copyright (C) 2003 Herbert Valerio Riedel - * Copyright (C) 2004 Luis R. Rodriguez - * Copyright (C) 2004 Aurelien Alleaume - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#if !defined(_ISL_OID_H) -#define _ISL_OID_H - -/* - * MIB related constant and structure definitions for communicating - * with the device firmware - */ - -struct obj_ssid { - u8 length; - char octets[33]; -} __packed; - -struct obj_key { - u8 type; /* dot11_priv_t */ - u8 length; - char key[32]; -} __packed; - -struct obj_mlme { - u8 address[6]; - u16 id; - u16 state; - u16 code; -} __packed; - -struct obj_mlmeex { - u8 address[6]; - u16 id; - u16 state; - u16 code; - u16 size; - u8 data[0]; -} __packed; - -struct obj_buffer { - u32 size; - u32 addr; /* 32bit bus address */ -} __packed; - -struct obj_bss { - u8 address[6]; - int:16; /* padding */ - - char state; - char reserved; - short age; - - char quality; - char rssi; - - struct obj_ssid ssid; - short channel; - char beacon_period; - char dtim_period; - short capinfo; - short rates; - short basic_rates; - int:16; /* padding */ -} __packed; - -struct obj_bsslist { - u32 nr; - struct obj_bss bsslist[0]; -} __packed; - -struct obj_frequencies { - u16 nr; - u16 mhz[0]; -} __packed; - -struct obj_attachment { - char type; - char reserved; - short id; - short size; - char data[0]; -} __packed; - -/* - * in case everything's ok, the inlined function below will be - * optimized away by the compiler... - */ -static inline void -__bug_on_wrong_struct_sizes(void) -{ - BUILD_BUG_ON(sizeof (struct obj_ssid) != 34); - BUILD_BUG_ON(sizeof (struct obj_key) != 34); - BUILD_BUG_ON(sizeof (struct obj_mlme) != 12); - BUILD_BUG_ON(sizeof (struct obj_mlmeex) != 14); - BUILD_BUG_ON(sizeof (struct obj_buffer) != 8); - BUILD_BUG_ON(sizeof (struct obj_bss) != 60); - BUILD_BUG_ON(sizeof (struct obj_bsslist) != 4); - BUILD_BUG_ON(sizeof (struct obj_frequencies) != 2); -} - -enum dot11_state_t { - DOT11_STATE_NONE = 0, - DOT11_STATE_AUTHING = 1, - DOT11_STATE_AUTH = 2, - DOT11_STATE_ASSOCING = 3, - - DOT11_STATE_ASSOC = 5, - DOT11_STATE_IBSS = 6, - DOT11_STATE_WDS = 7 -}; - -enum dot11_bsstype_t { - DOT11_BSSTYPE_NONE = 0, - DOT11_BSSTYPE_INFRA = 1, - DOT11_BSSTYPE_IBSS = 2, - DOT11_BSSTYPE_ANY = 3 -}; - -enum dot11_auth_t { - DOT11_AUTH_NONE = 0, - DOT11_AUTH_OS = 1, - DOT11_AUTH_SK = 2, - DOT11_AUTH_BOTH = 3 -}; - -enum dot11_mlme_t { - DOT11_MLME_AUTO = 0, - DOT11_MLME_INTERMEDIATE = 1, - DOT11_MLME_EXTENDED = 2 -}; - -enum dot11_priv_t { - DOT11_PRIV_WEP = 0, - DOT11_PRIV_TKIP = 1 -}; - -/* Prism "Nitro" / Frameburst / "Packet Frame Grouping" - * Value is in microseconds. Represents the # microseconds - * the firmware will take to group frames before sending out then out - * together with a CSMA contention. Without this all frames are - * sent with a CSMA contention. - * Bibliography: - * http://www.hpl.hp.com/personal/Jean_Tourrilhes/Papers/Packet.Frame.Grouping.html - */ -enum dot11_maxframeburst_t { - /* Values for DOT11_OID_MAXFRAMEBURST */ - DOT11_MAXFRAMEBURST_OFF = 0, /* Card firmware default */ - DOT11_MAXFRAMEBURST_MIXED_SAFE = 650, /* 802.11 a,b,g safe */ - DOT11_MAXFRAMEBURST_IDEAL = 1300, /* Theoretical ideal level */ - DOT11_MAXFRAMEBURST_MAX = 5000, /* Use this as max, - * Note: firmware allows for greater values. This is a - * recommended max. I'll update this as I find - * out what the real MAX is. Also note that you don't necessarily - * get better results with a greater value here. - */ -}; - -/* Support for 802.11 long and short frame preambles. - * Long preamble uses 128-bit sync field, 8-bit CRC - * Short preamble uses 56-bit sync field, 16-bit CRC - * - * 802.11a -- not sure, both optionally ? - * 802.11b supports long and optionally short - * 802.11g supports both */ -enum dot11_preamblesettings_t { - DOT11_PREAMBLESETTING_LONG = 0, - /* Allows *only* long 802.11 preambles */ - DOT11_PREAMBLESETTING_SHORT = 1, - /* Allows *only* short 802.11 preambles */ - DOT11_PREAMBLESETTING_DYNAMIC = 2 - /* AutomatiGically set */ -}; - -/* Support for 802.11 slot timing (time between packets). - * - * Long uses 802.11a slot timing (9 usec ?) - * Short uses 802.11b slot timing (20 use ?) */ -enum dot11_slotsettings_t { - DOT11_SLOTSETTINGS_LONG = 0, - /* Allows *only* long 802.11b slot timing */ - DOT11_SLOTSETTINGS_SHORT = 1, - /* Allows *only* long 802.11a slot timing */ - DOT11_SLOTSETTINGS_DYNAMIC = 2 - /* AutomatiGically set */ -}; - -/* All you need to know, ERP is "Extended Rate PHY". - * An Extended Rate PHY (ERP) STA or AP shall support three different - * preamble and header formats: - * Long preamble (refer to above) - * Short preamble (refer to above) - * OFDM preamble ( ? ) - * - * I'm assuming here Protection tells the AP - * to be careful, a STA which cannot handle the long pre-amble - * has joined. - */ -enum do11_nonerpstatus_t { - DOT11_ERPSTAT_NONEPRESENT = 0, - DOT11_ERPSTAT_USEPROTECTION = 1 -}; - -/* (ERP is "Extended Rate PHY") Way to read NONERP is NON-ERP-* - * The key here is DOT11 NON ERP NEVER protects against - * NON ERP STA's. You *don't* want this unless - * you know what you are doing. It means you will only - * get Extended Rate capabilities */ -enum dot11_nonerpprotection_t { - DOT11_NONERP_NEVER = 0, - DOT11_NONERP_ALWAYS = 1, - DOT11_NONERP_DYNAMIC = 2 -}; - -/* Preset OID configuration for 802.11 modes - * Note: DOT11_OID_CW[MIN|MAX] hold the values of the - * DCS MIN|MAX backoff used */ -enum dot11_profile_t { /* And set/allowed values */ - /* Allowed values for DOT11_OID_PROFILES */ - DOT11_PROFILE_B_ONLY = 0, - /* DOT11_OID_RATES: 1, 2, 5.5, 11Mbps - * DOT11_OID_PREAMBLESETTINGS: DOT11_PREAMBLESETTING_DYNAMIC - * DOT11_OID_CWMIN: 31 - * DOT11_OID_NONEPROTECTION: DOT11_NOERP_DYNAMIC - * DOT11_OID_SLOTSETTINGS: DOT11_SLOTSETTINGS_LONG - */ - DOT11_PROFILE_MIXED_G_WIFI = 1, - /* DOT11_OID_RATES: 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54Mbs - * DOT11_OID_PREAMBLESETTINGS: DOT11_PREAMBLESETTING_DYNAMIC - * DOT11_OID_CWMIN: 15 - * DOT11_OID_NONEPROTECTION: DOT11_NOERP_DYNAMIC - * DOT11_OID_SLOTSETTINGS: DOT11_SLOTSETTINGS_DYNAMIC - */ - DOT11_PROFILE_MIXED_LONG = 2, /* "Long range" */ - /* Same as Profile MIXED_G_WIFI */ - DOT11_PROFILE_G_ONLY = 3, - /* Same as Profile MIXED_G_WIFI */ - DOT11_PROFILE_TEST = 4, - /* Same as Profile MIXED_G_WIFI except: - * DOT11_OID_PREAMBLESETTINGS: DOT11_PREAMBLESETTING_SHORT - * DOT11_OID_NONEPROTECTION: DOT11_NOERP_NEVER - * DOT11_OID_SLOTSETTINGS: DOT11_SLOTSETTINGS_SHORT - */ - DOT11_PROFILE_B_WIFI = 5, - /* Same as Profile B_ONLY */ - DOT11_PROFILE_A_ONLY = 6, - /* Same as Profile MIXED_G_WIFI except: - * DOT11_OID_RATES: 6, 9, 12, 18, 24, 36, 48, 54Mbs - */ - DOT11_PROFILE_MIXED_SHORT = 7 - /* Same as MIXED_G_WIFI */ -}; - - -/* The dot11d conformance level configures the 802.11d conformance levels. - * The following conformance levels exist:*/ -enum oid_inl_conformance_t { - OID_INL_CONFORMANCE_NONE = 0, /* Perform active scanning */ - OID_INL_CONFORMANCE_STRICT = 1, /* Strictly adhere to 802.11d */ - OID_INL_CONFORMANCE_FLEXIBLE = 2, /* Use passed 802.11d info to - * determine channel AND/OR just make assumption that active - * channels are valid channels */ -}; - -enum oid_inl_mode_t { - INL_MODE_NONE = -1, - INL_MODE_PROMISCUOUS = 0, - INL_MODE_CLIENT = 1, - INL_MODE_AP = 2, - INL_MODE_SNIFFER = 3 -}; - -enum oid_inl_config_t { - INL_CONFIG_NOTHING = 0x00, - INL_CONFIG_MANUALRUN = 0x01, - INL_CONFIG_FRAMETRAP = 0x02, - INL_CONFIG_RXANNEX = 0x04, - INL_CONFIG_TXANNEX = 0x08, - INL_CONFIG_WDS = 0x10 -}; - -enum oid_inl_phycap_t { - INL_PHYCAP_2400MHZ = 1, - INL_PHYCAP_5000MHZ = 2, - INL_PHYCAP_FAA = 0x80000000, /* Means card supports the FAA switch */ -}; - - -enum oid_num_t { - GEN_OID_MACADDRESS = 0, - GEN_OID_LINKSTATE, - GEN_OID_WATCHDOG, - GEN_OID_MIBOP, - GEN_OID_OPTIONS, - GEN_OID_LEDCONFIG, - - /* 802.11 */ - DOT11_OID_BSSTYPE, - DOT11_OID_BSSID, - DOT11_OID_SSID, - DOT11_OID_STATE, - DOT11_OID_AID, - DOT11_OID_COUNTRYSTRING, - DOT11_OID_SSIDOVERRIDE, - - DOT11_OID_MEDIUMLIMIT, - DOT11_OID_BEACONPERIOD, - DOT11_OID_DTIMPERIOD, - DOT11_OID_ATIMWINDOW, - DOT11_OID_LISTENINTERVAL, - DOT11_OID_CFPPERIOD, - DOT11_OID_CFPDURATION, - - DOT11_OID_AUTHENABLE, - DOT11_OID_PRIVACYINVOKED, - DOT11_OID_EXUNENCRYPTED, - DOT11_OID_DEFKEYID, - DOT11_OID_DEFKEYX, /* DOT11_OID_DEFKEY1,...DOT11_OID_DEFKEY4 */ - DOT11_OID_STAKEY, - DOT11_OID_REKEYTHRESHOLD, - DOT11_OID_STASC, - - DOT11_OID_PRIVTXREJECTED, - DOT11_OID_PRIVRXPLAIN, - DOT11_OID_PRIVRXFAILED, - DOT11_OID_PRIVRXNOKEY, - - DOT11_OID_RTSTHRESH, - DOT11_OID_FRAGTHRESH, - DOT11_OID_SHORTRETRIES, - DOT11_OID_LONGRETRIES, - DOT11_OID_MAXTXLIFETIME, - DOT11_OID_MAXRXLIFETIME, - DOT11_OID_AUTHRESPTIMEOUT, - DOT11_OID_ASSOCRESPTIMEOUT, - - DOT11_OID_ALOFT_TABLE, - DOT11_OID_ALOFT_CTRL_TABLE, - DOT11_OID_ALOFT_RETREAT, - DOT11_OID_ALOFT_PROGRESS, - DOT11_OID_ALOFT_FIXEDRATE, - DOT11_OID_ALOFT_RSSIGRAPH, - DOT11_OID_ALOFT_CONFIG, - - DOT11_OID_VDCFX, - DOT11_OID_MAXFRAMEBURST, - - DOT11_OID_PSM, - DOT11_OID_CAMTIMEOUT, - DOT11_OID_RECEIVEDTIMS, - DOT11_OID_ROAMPREFERENCE, - - DOT11_OID_BRIDGELOCAL, - DOT11_OID_CLIENTS, - DOT11_OID_CLIENTSASSOCIATED, - DOT11_OID_CLIENTX, /* DOT11_OID_CLIENTX,...DOT11_OID_CLIENT2007 */ - - DOT11_OID_CLIENTFIND, - DOT11_OID_WDSLINKADD, - DOT11_OID_WDSLINKREMOVE, - DOT11_OID_EAPAUTHSTA, - DOT11_OID_EAPUNAUTHSTA, - DOT11_OID_DOT1XENABLE, - DOT11_OID_MICFAILURE, - DOT11_OID_REKEYINDICATE, - - DOT11_OID_MPDUTXSUCCESSFUL, - DOT11_OID_MPDUTXONERETRY, - DOT11_OID_MPDUTXMULTIPLERETRIES, - DOT11_OID_MPDUTXFAILED, - DOT11_OID_MPDURXSUCCESSFUL, - DOT11_OID_MPDURXDUPS, - DOT11_OID_RTSSUCCESSFUL, - DOT11_OID_RTSFAILED, - DOT11_OID_ACKFAILED, - DOT11_OID_FRAMERECEIVES, - DOT11_OID_FRAMEERRORS, - DOT11_OID_FRAMEABORTS, - DOT11_OID_FRAMEABORTSPHY, - - DOT11_OID_SLOTTIME, - DOT11_OID_CWMIN, /* MIN DCS backoff */ - DOT11_OID_CWMAX, /* MAX DCS backoff */ - DOT11_OID_ACKWINDOW, - DOT11_OID_ANTENNARX, - DOT11_OID_ANTENNATX, - DOT11_OID_ANTENNADIVERSITY, - DOT11_OID_CHANNEL, - DOT11_OID_EDTHRESHOLD, - DOT11_OID_PREAMBLESETTINGS, - DOT11_OID_RATES, - DOT11_OID_CCAMODESUPPORTED, - DOT11_OID_CCAMODE, - DOT11_OID_RSSIVECTOR, - DOT11_OID_OUTPUTPOWERTABLE, - DOT11_OID_OUTPUTPOWER, - DOT11_OID_SUPPORTEDRATES, - DOT11_OID_FREQUENCY, - DOT11_OID_SUPPORTEDFREQUENCIES, - DOT11_OID_NOISEFLOOR, - DOT11_OID_FREQUENCYACTIVITY, - DOT11_OID_IQCALIBRATIONTABLE, - DOT11_OID_NONERPPROTECTION, - DOT11_OID_SLOTSETTINGS, - DOT11_OID_NONERPTIMEOUT, - DOT11_OID_PROFILES, - DOT11_OID_EXTENDEDRATES, - - DOT11_OID_DEAUTHENTICATE, - DOT11_OID_AUTHENTICATE, - DOT11_OID_DISASSOCIATE, - DOT11_OID_ASSOCIATE, - DOT11_OID_SCAN, - DOT11_OID_BEACON, - DOT11_OID_PROBE, - DOT11_OID_DEAUTHENTICATEEX, - DOT11_OID_AUTHENTICATEEX, - DOT11_OID_DISASSOCIATEEX, - DOT11_OID_ASSOCIATEEX, - DOT11_OID_REASSOCIATE, - DOT11_OID_REASSOCIATEEX, - - DOT11_OID_NONERPSTATUS, - - DOT11_OID_STATIMEOUT, - DOT11_OID_MLMEAUTOLEVEL, - DOT11_OID_BSSTIMEOUT, - DOT11_OID_ATTACHMENT, - DOT11_OID_PSMBUFFER, - - DOT11_OID_BSSS, - DOT11_OID_BSSX, /*DOT11_OID_BSS1,...,DOT11_OID_BSS64 */ - DOT11_OID_BSSFIND, - DOT11_OID_BSSLIST, - - OID_INL_TUNNEL, - OID_INL_MEMADDR, - OID_INL_MEMORY, - OID_INL_MODE, - OID_INL_COMPONENT_NR, - OID_INL_VERSION, - OID_INL_INTERFACE_ID, - OID_INL_COMPONENT_ID, - OID_INL_CONFIG, - OID_INL_DOT11D_CONFORMANCE, - OID_INL_PHYCAPABILITIES, - OID_INL_OUTPUTPOWER, - - OID_NUM_LAST -}; - -#define OID_FLAG_CACHED 0x80 -#define OID_FLAG_TYPE 0x7f - -#define OID_TYPE_U32 0x01 -#define OID_TYPE_SSID 0x02 -#define OID_TYPE_KEY 0x03 -#define OID_TYPE_BUFFER 0x04 -#define OID_TYPE_BSS 0x05 -#define OID_TYPE_BSSLIST 0x06 -#define OID_TYPE_FREQUENCIES 0x07 -#define OID_TYPE_MLME 0x08 -#define OID_TYPE_MLMEEX 0x09 -#define OID_TYPE_ADDR 0x0A -#define OID_TYPE_RAW 0x0B -#define OID_TYPE_ATTACH 0x0C - -/* OID_TYPE_MLMEEX is special because of a variable size field when sending. - * Not yet implemented (not used in driver anyway). - */ - -struct oid_t { - enum oid_num_t oid; - short range; /* to define a range of oid */ - short size; /* max size of the associated data */ - char flags; -}; - -union oid_res_t { - void *ptr; - u32 u; -}; - -#define IWMAX_BITRATES 20 -#define IWMAX_BSS 24 -#define IWMAX_FREQ 30 -#define PRIV_STR_SIZE 1024 - -#endif /* !defined(_ISL_OID_H) */ -/* EOF */ diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c deleted file mode 100644 index 931cf440ff18..000000000000 --- a/drivers/net/wireless/prism54/islpci_dev.c +++ /dev/null @@ -1,963 +0,0 @@ -/* - * Copyright (C) 2002 Intersil Americas Inc. - * Copyright (C) 2003 Herbert Valerio Riedel - * Copyright (C) 2003 Luis R. Rodriguez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "prismcompat.h" -#include "isl_38xx.h" -#include "isl_ioctl.h" -#include "islpci_dev.h" -#include "islpci_mgt.h" -#include "islpci_eth.h" -#include "oid_mgt.h" - -#define ISL3877_IMAGE_FILE "isl3877" -#define ISL3886_IMAGE_FILE "isl3886" -#define ISL3890_IMAGE_FILE "isl3890" -MODULE_FIRMWARE(ISL3877_IMAGE_FILE); -MODULE_FIRMWARE(ISL3886_IMAGE_FILE); -MODULE_FIRMWARE(ISL3890_IMAGE_FILE); - -static int prism54_bring_down(islpci_private *); -static int islpci_alloc_memory(islpci_private *); - -/* Temporary dummy MAC address to use until firmware is loaded. - * The idea there is that some tools (such as nameif) may query - * the MAC address before the netdev is 'open'. By using a valid - * OUI prefix, they can process the netdev properly. - * Of course, this is not the final/real MAC address. It doesn't - * matter, as you are suppose to be able to change it anytime via - * ndev->set_mac_address. Jean II */ -static const unsigned char dummy_mac[6] = { 0x00, 0x30, 0xB4, 0x00, 0x00, 0x00 }; - -static int -isl_upload_firmware(islpci_private *priv) -{ - u32 reg, rc; - void __iomem *device_base = priv->device_base; - - /* clear the RAMBoot and the Reset bit */ - reg = readl(device_base + ISL38XX_CTRL_STAT_REG); - reg &= ~ISL38XX_CTRL_STAT_RESET; - reg &= ~ISL38XX_CTRL_STAT_RAMBOOT; - writel(reg, device_base + ISL38XX_CTRL_STAT_REG); - wmb(); - udelay(ISL38XX_WRITEIO_DELAY); - - /* set the Reset bit without reading the register ! */ - reg |= ISL38XX_CTRL_STAT_RESET; - writel(reg, device_base + ISL38XX_CTRL_STAT_REG); - wmb(); - udelay(ISL38XX_WRITEIO_DELAY); - - /* clear the Reset bit */ - reg &= ~ISL38XX_CTRL_STAT_RESET; - writel(reg, device_base + ISL38XX_CTRL_STAT_REG); - wmb(); - - /* wait a while for the device to reboot */ - mdelay(50); - - { - const struct firmware *fw_entry = NULL; - long fw_len; - const u32 *fw_ptr; - - rc = request_firmware(&fw_entry, priv->firmware, PRISM_FW_PDEV); - if (rc) { - printk(KERN_ERR - "%s: request_firmware() failed for '%s'\n", - "prism54", priv->firmware); - return rc; - } - /* prepare the Direct Memory Base register */ - reg = ISL38XX_DEV_FIRMWARE_ADDRES; - - fw_ptr = (u32 *) fw_entry->data; - fw_len = fw_entry->size; - - if (fw_len % 4) { - printk(KERN_ERR - "%s: firmware '%s' size is not multiple of 32bit, aborting!\n", - "prism54", priv->firmware); - release_firmware(fw_entry); - return -EILSEQ; /* Illegal byte sequence */; - } - - while (fw_len > 0) { - long _fw_len = - (fw_len > - ISL38XX_MEMORY_WINDOW_SIZE) ? - ISL38XX_MEMORY_WINDOW_SIZE : fw_len; - u32 __iomem *dev_fw_ptr = device_base + ISL38XX_DIRECT_MEM_WIN; - - /* set the card's base address for writing the data */ - isl38xx_w32_flush(device_base, reg, - ISL38XX_DIR_MEM_BASE_REG); - wmb(); /* be paranoid */ - - /* increment the write address for next iteration */ - reg += _fw_len; - fw_len -= _fw_len; - - /* write the data to the Direct Memory Window 32bit-wise */ - /* memcpy_toio() doesn't guarantee 32bit writes :-| */ - while (_fw_len > 0) { - /* use non-swapping writel() */ - __raw_writel(*fw_ptr, dev_fw_ptr); - fw_ptr++, dev_fw_ptr++; - _fw_len -= 4; - } - - /* flush PCI posting */ - (void) readl(device_base + ISL38XX_PCI_POSTING_FLUSH); - wmb(); /* be paranoid again */ - - BUG_ON(_fw_len != 0); - } - - BUG_ON(fw_len != 0); - - /* Firmware version is at offset 40 (also for "newmac") */ - printk(KERN_DEBUG "%s: firmware version: %.8s\n", - priv->ndev->name, fw_entry->data + 40); - - release_firmware(fw_entry); - } - - /* now reset the device - * clear the Reset & ClkRun bit, set the RAMBoot bit */ - reg = readl(device_base + ISL38XX_CTRL_STAT_REG); - reg &= ~ISL38XX_CTRL_STAT_CLKRUN; - reg &= ~ISL38XX_CTRL_STAT_RESET; - reg |= ISL38XX_CTRL_STAT_RAMBOOT; - isl38xx_w32_flush(device_base, reg, ISL38XX_CTRL_STAT_REG); - wmb(); - udelay(ISL38XX_WRITEIO_DELAY); - - /* set the reset bit latches the host override and RAMBoot bits - * into the device for operation when the reset bit is reset */ - reg |= ISL38XX_CTRL_STAT_RESET; - writel(reg, device_base + ISL38XX_CTRL_STAT_REG); - /* don't do flush PCI posting here! */ - wmb(); - udelay(ISL38XX_WRITEIO_DELAY); - - /* clear the reset bit should start the whole circus */ - reg &= ~ISL38XX_CTRL_STAT_RESET; - writel(reg, device_base + ISL38XX_CTRL_STAT_REG); - /* don't do flush PCI posting here! */ - wmb(); - udelay(ISL38XX_WRITEIO_DELAY); - - return 0; -} - -/****************************************************************************** - Device Interrupt Handler -******************************************************************************/ - -irqreturn_t -islpci_interrupt(int irq, void *config) -{ - u32 reg; - islpci_private *priv = config; - struct net_device *ndev = priv->ndev; - void __iomem *device = priv->device_base; - int powerstate = ISL38XX_PSM_POWERSAVE_STATE; - - /* lock the interrupt handler */ - spin_lock(&priv->slock); - - /* received an interrupt request on a shared IRQ line - * first check whether the device is in sleep mode */ - reg = readl(device + ISL38XX_CTRL_STAT_REG); - if (reg & ISL38XX_CTRL_STAT_SLEEPMODE) - /* device is in sleep mode, IRQ was generated by someone else */ - { -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n"); -#endif - spin_unlock(&priv->slock); - return IRQ_NONE; - } - - - /* check whether there is any source of interrupt on the device */ - reg = readl(device + ISL38XX_INT_IDENT_REG); - - /* also check the contents of the Interrupt Enable Register, because this - * will filter out interrupt sources from other devices on the same irq ! */ - reg &= readl(device + ISL38XX_INT_EN_REG); - reg &= ISL38XX_INT_SOURCES; - - if (reg != 0) { - if (islpci_get_state(priv) != PRV_STATE_SLEEP) - powerstate = ISL38XX_PSM_ACTIVE_STATE; - - /* reset the request bits in the Identification register */ - isl38xx_w32_flush(device, reg, ISL38XX_INT_ACK_REG); - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_FUNCTION_CALLS, - "IRQ: Identification register 0x%p 0x%x\n", device, reg); -#endif - - /* check for each bit in the register separately */ - if (reg & ISL38XX_INT_IDENT_UPDATE) { -#if VERBOSE > SHOW_ERROR_MESSAGES - /* Queue has been updated */ - DEBUG(SHOW_TRACING, "IRQ: Update flag\n"); - - DEBUG(SHOW_QUEUE_INDEXES, - "CB drv Qs: [%i][%i][%i][%i][%i][%i]\n", - le32_to_cpu(priv->control_block-> - driver_curr_frag[0]), - le32_to_cpu(priv->control_block-> - driver_curr_frag[1]), - le32_to_cpu(priv->control_block-> - driver_curr_frag[2]), - le32_to_cpu(priv->control_block-> - driver_curr_frag[3]), - le32_to_cpu(priv->control_block-> - driver_curr_frag[4]), - le32_to_cpu(priv->control_block-> - driver_curr_frag[5]) - ); - - DEBUG(SHOW_QUEUE_INDEXES, - "CB dev Qs: [%i][%i][%i][%i][%i][%i]\n", - le32_to_cpu(priv->control_block-> - device_curr_frag[0]), - le32_to_cpu(priv->control_block-> - device_curr_frag[1]), - le32_to_cpu(priv->control_block-> - device_curr_frag[2]), - le32_to_cpu(priv->control_block-> - device_curr_frag[3]), - le32_to_cpu(priv->control_block-> - device_curr_frag[4]), - le32_to_cpu(priv->control_block-> - device_curr_frag[5]) - ); -#endif - - /* cleanup the data low transmit queue */ - islpci_eth_cleanup_transmit(priv, priv->control_block); - - /* device is in active state, update the - * powerstate flag if necessary */ - powerstate = ISL38XX_PSM_ACTIVE_STATE; - - /* check all three queues in priority order - * call the PIMFOR receive function until the - * queue is empty */ - if (isl38xx_in_queue(priv->control_block, - ISL38XX_CB_RX_MGMTQ) != 0) { -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, - "Received frame in Management Queue\n"); -#endif - islpci_mgt_receive(ndev); - - islpci_mgt_cleanup_transmit(ndev); - - /* Refill slots in receive queue */ - islpci_mgmt_rx_fill(ndev); - - /* no need to trigger the device, next - islpci_mgt_transaction does it */ - } - - while (isl38xx_in_queue(priv->control_block, - ISL38XX_CB_RX_DATA_LQ) != 0) { -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, - "Received frame in Data Low Queue\n"); -#endif - islpci_eth_receive(priv); - } - - /* check whether the data transmit queues were full */ - if (priv->data_low_tx_full) { - /* check whether the transmit is not full anymore */ - if (ISL38XX_CB_TX_QSIZE - - isl38xx_in_queue(priv->control_block, - ISL38XX_CB_TX_DATA_LQ) >= - ISL38XX_MIN_QTHRESHOLD) { - /* nope, the driver is ready for more network frames */ - netif_wake_queue(priv->ndev); - - /* reset the full flag */ - priv->data_low_tx_full = 0; - } - } - } - - if (reg & ISL38XX_INT_IDENT_INIT) { - /* Device has been initialized */ -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, - "IRQ: Init flag, device initialized\n"); -#endif - wake_up(&priv->reset_done); - } - - if (reg & ISL38XX_INT_IDENT_SLEEP) { - /* Device intends to move to powersave state */ -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "IRQ: Sleep flag\n"); -#endif - isl38xx_handle_sleep_request(priv->control_block, - &powerstate, - priv->device_base); - } - - if (reg & ISL38XX_INT_IDENT_WAKEUP) { - /* Device has been woken up to active state */ -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "IRQ: Wakeup flag\n"); -#endif - - isl38xx_handle_wakeup(priv->control_block, - &powerstate, priv->device_base); - } - } else { -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "Assuming someone else called the IRQ\n"); -#endif - spin_unlock(&priv->slock); - return IRQ_NONE; - } - - /* sleep -> ready */ - if (islpci_get_state(priv) == PRV_STATE_SLEEP - && powerstate == ISL38XX_PSM_ACTIVE_STATE) - islpci_set_state(priv, PRV_STATE_READY); - - /* !sleep -> sleep */ - if (islpci_get_state(priv) != PRV_STATE_SLEEP - && powerstate == ISL38XX_PSM_POWERSAVE_STATE) - islpci_set_state(priv, PRV_STATE_SLEEP); - - /* unlock the interrupt handler */ - spin_unlock(&priv->slock); - - return IRQ_HANDLED; -} - -/****************************************************************************** - Network Interface Control & Statistical functions -******************************************************************************/ -static int -islpci_open(struct net_device *ndev) -{ - u32 rc; - islpci_private *priv = netdev_priv(ndev); - - /* reset data structures, upload firmware and reset device */ - rc = islpci_reset(priv,1); - if (rc) { - prism54_bring_down(priv); - return rc; /* Returns informative message */ - } - - netif_start_queue(ndev); - - /* Turn off carrier if in STA or Ad-hoc mode. It will be turned on - * once the firmware receives a trap of being associated - * (GEN_OID_LINKSTATE). In other modes (AP or WDS or monitor) we - * should just leave the carrier on as its expected the firmware - * won't send us a trigger. */ - if (priv->iw_mode == IW_MODE_INFRA || priv->iw_mode == IW_MODE_ADHOC) - netif_carrier_off(ndev); - else - netif_carrier_on(ndev); - - return 0; -} - -static int -islpci_close(struct net_device *ndev) -{ - islpci_private *priv = netdev_priv(ndev); - - printk(KERN_DEBUG "%s: islpci_close ()\n", ndev->name); - - netif_stop_queue(ndev); - - return prism54_bring_down(priv); -} - -static int -prism54_bring_down(islpci_private *priv) -{ - void __iomem *device_base = priv->device_base; - u32 reg; - /* we are going to shutdown the device */ - islpci_set_state(priv, PRV_STATE_PREBOOT); - - /* disable all device interrupts in case they weren't */ - isl38xx_disable_interrupts(priv->device_base); - - /* For safety reasons, we may want to ensure that no DMA transfer is - * currently in progress by emptying the TX and RX queues. */ - - /* wait until interrupts have finished executing on other CPUs */ - synchronize_irq(priv->pdev->irq); - - reg = readl(device_base + ISL38XX_CTRL_STAT_REG); - reg &= ~(ISL38XX_CTRL_STAT_RESET | ISL38XX_CTRL_STAT_RAMBOOT); - writel(reg, device_base + ISL38XX_CTRL_STAT_REG); - wmb(); - udelay(ISL38XX_WRITEIO_DELAY); - - reg |= ISL38XX_CTRL_STAT_RESET; - writel(reg, device_base + ISL38XX_CTRL_STAT_REG); - wmb(); - udelay(ISL38XX_WRITEIO_DELAY); - - /* clear the Reset bit */ - reg &= ~ISL38XX_CTRL_STAT_RESET; - writel(reg, device_base + ISL38XX_CTRL_STAT_REG); - wmb(); - - /* wait a while for the device to reset */ - schedule_timeout_uninterruptible(msecs_to_jiffies(50)); - - return 0; -} - -static int -islpci_upload_fw(islpci_private *priv) -{ - islpci_state_t old_state; - u32 rc; - - old_state = islpci_set_state(priv, PRV_STATE_BOOT); - - printk(KERN_DEBUG "%s: uploading firmware...\n", priv->ndev->name); - - rc = isl_upload_firmware(priv); - if (rc) { - /* error uploading the firmware */ - printk(KERN_ERR "%s: could not upload firmware ('%s')\n", - priv->ndev->name, priv->firmware); - - islpci_set_state(priv, old_state); - return rc; - } - - printk(KERN_DEBUG "%s: firmware upload complete\n", - priv->ndev->name); - - islpci_set_state(priv, PRV_STATE_POSTBOOT); - - return 0; -} - -static int -islpci_reset_if(islpci_private *priv) -{ - long remaining; - int result = -ETIME; - int count; - - DEFINE_WAIT(wait); - prepare_to_wait(&priv->reset_done, &wait, TASK_UNINTERRUPTIBLE); - - /* now the last step is to reset the interface */ - isl38xx_interface_reset(priv->device_base, priv->device_host_address); - islpci_set_state(priv, PRV_STATE_PREINIT); - - for(count = 0; count < 2 && result; count++) { - /* The software reset acknowledge needs about 220 msec here. - * Be conservative and wait for up to one second. */ - - remaining = schedule_timeout_uninterruptible(HZ); - - if(remaining > 0) { - result = 0; - break; - } - - /* If we're here it's because our IRQ hasn't yet gone through. - * Retry a bit more... - */ - printk(KERN_ERR "%s: no 'reset complete' IRQ seen - retrying\n", - priv->ndev->name); - } - - finish_wait(&priv->reset_done, &wait); - - if (result) { - printk(KERN_ERR "%s: interface reset failure\n", priv->ndev->name); - return result; - } - - islpci_set_state(priv, PRV_STATE_INIT); - - /* Now that the device is 100% up, let's allow - * for the other interrupts -- - * NOTE: this is not *yet* true since we've only allowed the - * INIT interrupt on the IRQ line. We can perhaps poll - * the IRQ line until we know for sure the reset went through */ - isl38xx_enable_common_interrupts(priv->device_base); - - down_write(&priv->mib_sem); - result = mgt_commit(priv); - if (result) { - printk(KERN_ERR "%s: interface reset failure\n", priv->ndev->name); - up_write(&priv->mib_sem); - return result; - } - up_write(&priv->mib_sem); - - islpci_set_state(priv, PRV_STATE_READY); - - printk(KERN_DEBUG "%s: interface reset complete\n", priv->ndev->name); - return 0; -} - -int -islpci_reset(islpci_private *priv, int reload_firmware) -{ - isl38xx_control_block *cb = /* volatile not needed */ - (isl38xx_control_block *) priv->control_block; - unsigned counter; - int rc; - - if (reload_firmware) - islpci_set_state(priv, PRV_STATE_PREBOOT); - else - islpci_set_state(priv, PRV_STATE_POSTBOOT); - - printk(KERN_DEBUG "%s: resetting device...\n", priv->ndev->name); - - /* disable all device interrupts in case they weren't */ - isl38xx_disable_interrupts(priv->device_base); - - /* flush all management queues */ - priv->index_mgmt_tx = 0; - priv->index_mgmt_rx = 0; - - /* clear the indexes in the frame pointer */ - for (counter = 0; counter < ISL38XX_CB_QCOUNT; counter++) { - cb->driver_curr_frag[counter] = cpu_to_le32(0); - cb->device_curr_frag[counter] = cpu_to_le32(0); - } - - /* reset the mgmt receive queue */ - for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) { - isl38xx_fragment *frag = &cb->rx_data_mgmt[counter]; - frag->size = cpu_to_le16(MGMT_FRAME_SIZE); - frag->flags = 0; - frag->address = cpu_to_le32(priv->mgmt_rx[counter].pci_addr); - } - - for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) { - cb->rx_data_low[counter].address = - cpu_to_le32((u32) priv->pci_map_rx_address[counter]); - } - - /* since the receive queues are filled with empty fragments, now we can - * set the corresponding indexes in the Control Block */ - priv->control_block->driver_curr_frag[ISL38XX_CB_RX_DATA_LQ] = - cpu_to_le32(ISL38XX_CB_RX_QSIZE); - priv->control_block->driver_curr_frag[ISL38XX_CB_RX_MGMTQ] = - cpu_to_le32(ISL38XX_CB_MGMT_QSIZE); - - /* reset the remaining real index registers and full flags */ - priv->free_data_rx = 0; - priv->free_data_tx = 0; - priv->data_low_tx_full = 0; - - if (reload_firmware) { /* Should we load the firmware ? */ - /* now that the data structures are cleaned up, upload - * firmware and reset interface */ - rc = islpci_upload_fw(priv); - if (rc) { - printk(KERN_ERR "%s: islpci_reset: failure\n", - priv->ndev->name); - return rc; - } - } - - /* finally reset interface */ - rc = islpci_reset_if(priv); - if (rc) - printk(KERN_ERR "prism54: Your card/socket may be faulty, or IRQ line too busy :(\n"); - return rc; -} - -/****************************************************************************** - Network device configuration functions -******************************************************************************/ -static int -islpci_alloc_memory(islpci_private *priv) -{ - int counter; - -#if VERBOSE > SHOW_ERROR_MESSAGES - printk(KERN_DEBUG "islpci_alloc_memory\n"); -#endif - - /* remap the PCI device base address to accessible */ - if (!(priv->device_base = - ioremap(pci_resource_start(priv->pdev, 0), - ISL38XX_PCI_MEM_SIZE))) { - /* error in remapping the PCI device memory address range */ - printk(KERN_ERR "PCI memory remapping failed\n"); - return -1; - } - - /* memory layout for consistent DMA region: - * - * Area 1: Control Block for the device interface - * Area 2: Power Save Mode Buffer for temporary frame storage. Be aware that - * the number of supported stations in the AP determines the minimal - * size of the buffer ! - */ - - /* perform the allocation */ - priv->driver_mem_address = pci_alloc_consistent(priv->pdev, - HOST_MEM_BLOCK, - &priv-> - device_host_address); - - if (!priv->driver_mem_address) { - /* error allocating the block of PCI memory */ - printk(KERN_ERR "%s: could not allocate DMA memory, aborting!", - "prism54"); - return -1; - } - - /* assign the Control Block to the first address of the allocated area */ - priv->control_block = - (isl38xx_control_block *) priv->driver_mem_address; - - /* set the Power Save Buffer pointer directly behind the CB */ - priv->device_psm_buffer = - priv->device_host_address + CONTROL_BLOCK_SIZE; - - /* make sure all buffer pointers are initialized */ - for (counter = 0; counter < ISL38XX_CB_QCOUNT; counter++) { - priv->control_block->driver_curr_frag[counter] = cpu_to_le32(0); - priv->control_block->device_curr_frag[counter] = cpu_to_le32(0); - } - - priv->index_mgmt_rx = 0; - memset(priv->mgmt_rx, 0, sizeof(priv->mgmt_rx)); - memset(priv->mgmt_tx, 0, sizeof(priv->mgmt_tx)); - - /* allocate rx queue for management frames */ - if (islpci_mgmt_rx_fill(priv->ndev) < 0) - goto out_free; - - /* now get the data rx skb's */ - memset(priv->data_low_rx, 0, sizeof (priv->data_low_rx)); - memset(priv->pci_map_rx_address, 0, sizeof (priv->pci_map_rx_address)); - - for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) { - struct sk_buff *skb; - - /* allocate an sk_buff for received data frames storage - * each frame on receive size consists of 1 fragment - * include any required allignment operations */ - if (!(skb = dev_alloc_skb(MAX_FRAGMENT_SIZE_RX + 2))) { - /* error allocating an sk_buff structure elements */ - printk(KERN_ERR "Error allocating skb.\n"); - skb = NULL; - goto out_free; - } - skb_reserve(skb, (4 - (long) skb->data) & 0x03); - /* add the new allocated sk_buff to the buffer array */ - priv->data_low_rx[counter] = skb; - - /* map the allocated skb data area to pci */ - priv->pci_map_rx_address[counter] = - pci_map_single(priv->pdev, (void *) skb->data, - MAX_FRAGMENT_SIZE_RX + 2, - PCI_DMA_FROMDEVICE); - if (!priv->pci_map_rx_address[counter]) { - /* error mapping the buffer to device - accessible memory address */ - printk(KERN_ERR "failed to map skb DMA'able\n"); - goto out_free; - } - } - - prism54_acl_init(&priv->acl); - prism54_wpa_bss_ie_init(priv); - if (mgt_init(priv)) - goto out_free; - - return 0; - out_free: - islpci_free_memory(priv); - return -1; -} - -int -islpci_free_memory(islpci_private *priv) -{ - int counter; - - if (priv->device_base) - iounmap(priv->device_base); - priv->device_base = NULL; - - /* free consistent DMA area... */ - if (priv->driver_mem_address) - pci_free_consistent(priv->pdev, HOST_MEM_BLOCK, - priv->driver_mem_address, - priv->device_host_address); - - /* clear some dangling pointers */ - priv->driver_mem_address = NULL; - priv->device_host_address = 0; - priv->device_psm_buffer = 0; - priv->control_block = NULL; - - /* clean up mgmt rx buffers */ - for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) { - struct islpci_membuf *buf = &priv->mgmt_rx[counter]; - if (buf->pci_addr) - pci_unmap_single(priv->pdev, buf->pci_addr, - buf->size, PCI_DMA_FROMDEVICE); - buf->pci_addr = 0; - kfree(buf->mem); - buf->size = 0; - buf->mem = NULL; - } - - /* clean up data rx buffers */ - for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) { - if (priv->pci_map_rx_address[counter]) - pci_unmap_single(priv->pdev, - priv->pci_map_rx_address[counter], - MAX_FRAGMENT_SIZE_RX + 2, - PCI_DMA_FROMDEVICE); - priv->pci_map_rx_address[counter] = 0; - - if (priv->data_low_rx[counter]) - dev_kfree_skb(priv->data_low_rx[counter]); - priv->data_low_rx[counter] = NULL; - } - - /* Free the access control list and the WPA list */ - prism54_acl_clean(&priv->acl); - prism54_wpa_bss_ie_clean(priv); - mgt_clean(priv); - - return 0; -} - -#if 0 -static void -islpci_set_multicast_list(struct net_device *dev) -{ - /* put device into promisc mode and let network layer handle it */ -} -#endif - -static void islpci_ethtool_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_VERSION, sizeof(info->version)); -} - -static const struct ethtool_ops islpci_ethtool_ops = { - .get_drvinfo = islpci_ethtool_get_drvinfo, -}; - -static const struct net_device_ops islpci_netdev_ops = { - .ndo_open = islpci_open, - .ndo_stop = islpci_close, - .ndo_start_xmit = islpci_eth_transmit, - .ndo_tx_timeout = islpci_eth_tx_timeout, - .ndo_set_mac_address = prism54_set_mac_address, - .ndo_change_mtu = eth_change_mtu, - .ndo_validate_addr = eth_validate_addr, -}; - -static struct device_type wlan_type = { - .name = "wlan", -}; - -struct net_device * -islpci_setup(struct pci_dev *pdev) -{ - islpci_private *priv; - struct net_device *ndev = alloc_etherdev(sizeof (islpci_private)); - - if (!ndev) - return ndev; - - pci_set_drvdata(pdev, ndev); - SET_NETDEV_DEV(ndev, &pdev->dev); - SET_NETDEV_DEVTYPE(ndev, &wlan_type); - - /* setup the structure members */ - ndev->base_addr = pci_resource_start(pdev, 0); - ndev->irq = pdev->irq; - - /* initialize the function pointers */ - ndev->netdev_ops = &islpci_netdev_ops; - ndev->wireless_handlers = &prism54_handler_def; - ndev->ethtool_ops = &islpci_ethtool_ops; - - /* ndev->set_multicast_list = &islpci_set_multicast_list; */ - ndev->addr_len = ETH_ALEN; - /* Get a non-zero dummy MAC address for nameif. Jean II */ - memcpy(ndev->dev_addr, dummy_mac, ETH_ALEN); - - ndev->watchdog_timeo = ISLPCI_TX_TIMEOUT; - - /* allocate a private device structure to the network device */ - priv = netdev_priv(ndev); - priv->ndev = ndev; - priv->pdev = pdev; - priv->monitor_type = ARPHRD_IEEE80211; - priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR) ? - priv->monitor_type : ARPHRD_ETHER; - - /* Add pointers to enable iwspy support. */ - priv->wireless_data.spy_data = &priv->spy_data; - ndev->wireless_data = &priv->wireless_data; - - /* save the start and end address of the PCI memory area */ - ndev->mem_start = (unsigned long) priv->device_base; - ndev->mem_end = ndev->mem_start + ISL38XX_PCI_MEM_SIZE; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "PCI Memory remapped to 0x%p\n", priv->device_base); -#endif - - init_waitqueue_head(&priv->reset_done); - - /* init the queue read locks, process wait counter */ - mutex_init(&priv->mgmt_lock); - priv->mgmt_received = NULL; - init_waitqueue_head(&priv->mgmt_wqueue); - mutex_init(&priv->stats_lock); - spin_lock_init(&priv->slock); - - /* init state machine with off#1 state */ - priv->state = PRV_STATE_OFF; - priv->state_off = 1; - - /* initialize workqueue's */ - INIT_WORK(&priv->stats_work, prism54_update_stats); - priv->stats_timestamp = 0; - - INIT_WORK(&priv->reset_task, islpci_do_reset_and_wake); - priv->reset_task_pending = 0; - - /* allocate various memory areas */ - if (islpci_alloc_memory(priv)) - goto do_free_netdev; - - /* select the firmware file depending on the device id */ - switch (pdev->device) { - case 0x3877: - strcpy(priv->firmware, ISL3877_IMAGE_FILE); - break; - - case 0x3886: - strcpy(priv->firmware, ISL3886_IMAGE_FILE); - break; - - default: - strcpy(priv->firmware, ISL3890_IMAGE_FILE); - break; - } - - if (register_netdev(ndev)) { - DEBUG(SHOW_ERROR_MESSAGES, - "ERROR: register_netdev() failed\n"); - goto do_islpci_free_memory; - } - - return ndev; - - do_islpci_free_memory: - islpci_free_memory(priv); - do_free_netdev: - free_netdev(ndev); - priv = NULL; - return NULL; -} - -islpci_state_t -islpci_set_state(islpci_private *priv, islpci_state_t new_state) -{ - islpci_state_t old_state; - - /* lock */ - old_state = priv->state; - - /* this means either a race condition or some serious error in - * the driver code */ - switch (new_state) { - case PRV_STATE_OFF: - priv->state_off++; - default: - priv->state = new_state; - break; - - case PRV_STATE_PREBOOT: - /* there are actually many off-states, enumerated by - * state_off */ - if (old_state == PRV_STATE_OFF) - priv->state_off--; - - /* only if hw_unavailable is zero now it means we either - * were in off#1 state, or came here from - * somewhere else */ - if (!priv->state_off) - priv->state = new_state; - break; - } -#if 0 - printk(KERN_DEBUG "%s: state transition %d -> %d (off#%d)\n", - priv->ndev->name, old_state, new_state, priv->state_off); -#endif - - /* invariants */ - BUG_ON(priv->state_off < 0); - BUG_ON(priv->state_off && (priv->state != PRV_STATE_OFF)); - BUG_ON(!priv->state_off && (priv->state == PRV_STATE_OFF)); - - /* unlock */ - return old_state; -} diff --git a/drivers/net/wireless/prism54/islpci_dev.h b/drivers/net/wireless/prism54/islpci_dev.h deleted file mode 100644 index f6f088e05fe4..000000000000 --- a/drivers/net/wireless/prism54/islpci_dev.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2002 Intersil Americas Inc. - * Copyright (C) 2003 Herbert Valerio Riedel - * Copyright (C) 2003 Luis R. Rodriguez - * Copyright (C) 2003 Aurelien Alleaume - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#ifndef _ISLPCI_DEV_H -#define _ISLPCI_DEV_H - -#include -#include -#include -#include -#include -#include - -#include "isl_38xx.h" -#include "isl_oid.h" -#include "islpci_mgt.h" - -/* some states might not be superflous and may be removed when - design is finalized (hvr) */ -typedef enum { - PRV_STATE_OFF = 0, /* this means hw_unavailable is != 0 */ - PRV_STATE_PREBOOT, /* we are in a pre-boot state (empty RAM) */ - PRV_STATE_BOOT, /* boot state (fw upload, run fw) */ - PRV_STATE_POSTBOOT, /* after boot state, need reset now */ - PRV_STATE_PREINIT, /* pre-init state */ - PRV_STATE_INIT, /* init state (restore MIB backup to device) */ - PRV_STATE_READY, /* driver&device are in operational state */ - PRV_STATE_SLEEP /* device in sleep mode */ -} islpci_state_t; - -/* ACL using MAC address */ -struct mac_entry { - struct list_head _list; - char addr[ETH_ALEN]; -}; - -struct islpci_acl { - enum { MAC_POLICY_OPEN=0, MAC_POLICY_ACCEPT=1, MAC_POLICY_REJECT=2 } policy; - struct list_head mac_list; /* a list of mac_entry */ - int size; /* size of queue */ - struct mutex lock; /* accessed in ioctls and trap_work */ -}; - -struct islpci_membuf { - int size; /* size of memory */ - void *mem; /* address of memory as seen by CPU */ - dma_addr_t pci_addr; /* address of memory as seen by device */ -}; - -#define MAX_BSS_WPA_IE_COUNT 64 -#define MAX_WPA_IE_LEN 64 -struct islpci_bss_wpa_ie { - struct list_head list; - unsigned long last_update; - u8 bssid[ETH_ALEN]; - u8 wpa_ie[MAX_WPA_IE_LEN]; - size_t wpa_ie_len; - -}; - -typedef struct { - spinlock_t slock; /* generic spinlock; */ - - u32 priv_oid; - - /* our mib cache */ - u32 iw_mode; - struct rw_semaphore mib_sem; - void **mib; - char nickname[IW_ESSID_MAX_SIZE+1]; - - /* Take care of the wireless stats */ - struct work_struct stats_work; - struct mutex stats_lock; - /* remember when we last updated the stats */ - unsigned long stats_timestamp; - /* The first is accessed under semaphore locking. - * The second is the clean one we return to iwconfig. - */ - struct iw_statistics local_iwstatistics; - struct iw_statistics iwstatistics; - - struct iw_spy_data spy_data; /* iwspy support */ - - struct iw_public_data wireless_data; - - int monitor_type; /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_PRISM */ - - struct islpci_acl acl; - - /* PCI bus allocation & configuration members */ - struct pci_dev *pdev; /* PCI structure information */ - char firmware[33]; - - void __iomem *device_base; /* ioremapped device base address */ - - /* consistent DMA region */ - void *driver_mem_address; /* base DMA address */ - dma_addr_t device_host_address; /* base DMA address (bus address) */ - dma_addr_t device_psm_buffer; /* host memory for PSM buffering (bus address) */ - - /* our network_device structure */ - struct net_device *ndev; - - /* device queue interface members */ - struct isl38xx_cb *control_block; /* device control block - (== driver_mem_address!) */ - - /* Each queue has three indexes: - * free/index_mgmt/data_rx/tx (called index, see below), - * driver_curr_frag, and device_curr_frag (in the control block) - * All indexes are ever-increasing, but interpreted modulo the - * device queue size when used. - * index <= device_curr_frag <= driver_curr_frag at all times - * For rx queues, [index, device_curr_frag) contains fragments - * that the interrupt processing needs to handle (owned by driver). - * [device_curr_frag, driver_curr_frag) is the free space in the - * rx queue, waiting for data (owned by device). The driver - * increments driver_curr_frag to indicate to the device that more - * buffers are available. - * If device_curr_frag == driver_curr_frag, no more rx buffers are - * available, and the rx DMA engine of the device is halted. - * For tx queues, [index, device_curr_frag) contains fragments - * where tx is done; they need to be freed (owned by driver). - * [device_curr_frag, driver_curr_frag) contains the frames - * that are being transferred (owned by device). The driver - * increments driver_curr_frag to indicate that more tx work - * needs to be done. - */ - u32 index_mgmt_rx; /* real index mgmt rx queue */ - u32 index_mgmt_tx; /* read index mgmt tx queue */ - u32 free_data_rx; /* free pointer data rx queue */ - u32 free_data_tx; /* free pointer data tx queue */ - u32 data_low_tx_full; /* full detected flag */ - - /* frame memory buffers for the device queues */ - struct islpci_membuf mgmt_tx[ISL38XX_CB_MGMT_QSIZE]; - struct islpci_membuf mgmt_rx[ISL38XX_CB_MGMT_QSIZE]; - struct sk_buff *data_low_tx[ISL38XX_CB_TX_QSIZE]; - struct sk_buff *data_low_rx[ISL38XX_CB_RX_QSIZE]; - dma_addr_t pci_map_tx_address[ISL38XX_CB_TX_QSIZE]; - dma_addr_t pci_map_rx_address[ISL38XX_CB_RX_QSIZE]; - - /* wait for a reset interrupt */ - wait_queue_head_t reset_done; - - /* used by islpci_mgt_transaction */ - struct mutex mgmt_lock; /* serialize access to mailbox and wqueue */ - struct islpci_mgmtframe *mgmt_received; /* mbox for incoming frame */ - wait_queue_head_t mgmt_wqueue; /* waitqueue for mbox */ - - /* state machine */ - islpci_state_t state; - int state_off; /* enumeration of off-state, if 0 then - * we're not in any off-state */ - - /* WPA stuff */ - int wpa; /* WPA mode enabled */ - struct list_head bss_wpa_list; - int num_bss_wpa; - struct mutex wpa_lock; - u8 wpa_ie[MAX_WPA_IE_LEN]; - size_t wpa_ie_len; - - struct work_struct reset_task; - int reset_task_pending; -} islpci_private; - -static inline islpci_state_t -islpci_get_state(islpci_private *priv) -{ - /* lock */ - return priv->state; - /* unlock */ -} - -islpci_state_t islpci_set_state(islpci_private *priv, islpci_state_t new_state); - -#define ISLPCI_TX_TIMEOUT (2*HZ) - -irqreturn_t islpci_interrupt(int, void *); - -int prism54_post_setup(islpci_private *, int); -int islpci_reset(islpci_private *, int); - -static inline void -islpci_trigger(islpci_private *priv) -{ - isl38xx_trigger_device(islpci_get_state(priv) == PRV_STATE_SLEEP, - priv->device_base); -} - -int islpci_free_memory(islpci_private *); -struct net_device *islpci_setup(struct pci_dev *); - -#define DRV_NAME "prism54" -#define DRV_VERSION "1.2" - -#endif /* _ISLPCI_DEV_H */ diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c deleted file mode 100644 index 674658f2e6ef..000000000000 --- a/drivers/net/wireless/prism54/islpci_eth.c +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright (C) 2002 Intersil Americas Inc. - * Copyright (C) 2004 Aurelien Alleaume - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "prismcompat.h" -#include "isl_38xx.h" -#include "islpci_eth.h" -#include "islpci_mgt.h" -#include "oid_mgt.h" - -/****************************************************************************** - Network Interface functions -******************************************************************************/ -void -islpci_eth_cleanup_transmit(islpci_private *priv, - isl38xx_control_block *control_block) -{ - struct sk_buff *skb; - u32 index; - - /* compare the control block read pointer with the free pointer */ - while (priv->free_data_tx != - le32_to_cpu(control_block-> - device_curr_frag[ISL38XX_CB_TX_DATA_LQ])) { - /* read the index of the first fragment to be freed */ - index = priv->free_data_tx % ISL38XX_CB_TX_QSIZE; - - /* check for holes in the arrays caused by multi fragment frames - * searching for the last fragment of a frame */ - if (priv->pci_map_tx_address[index]) { - /* entry is the last fragment of a frame - * free the skb structure and unmap pci memory */ - skb = priv->data_low_tx[index]; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, - "cleanup skb %p skb->data %p skb->len %u truesize %u\n ", - skb, skb->data, skb->len, skb->truesize); -#endif - - pci_unmap_single(priv->pdev, - priv->pci_map_tx_address[index], - skb->len, PCI_DMA_TODEVICE); - dev_kfree_skb_irq(skb); - skb = NULL; - } - /* increment the free data low queue pointer */ - priv->free_data_tx++; - } -} - -netdev_tx_t -islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev) -{ - islpci_private *priv = netdev_priv(ndev); - isl38xx_control_block *cb = priv->control_block; - u32 index; - dma_addr_t pci_map_address; - int frame_size; - isl38xx_fragment *fragment; - int offset; - struct sk_buff *newskb; - int newskb_offset; - unsigned long flags; - unsigned char wds_mac[6]; - u32 curr_frag; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_transmit\n"); -#endif - - /* lock the driver code */ - spin_lock_irqsave(&priv->slock, flags); - - /* check whether the destination queue has enough fragments for the frame */ - curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ]); - if (unlikely(curr_frag - priv->free_data_tx >= ISL38XX_CB_TX_QSIZE)) { - printk(KERN_ERR "%s: transmit device queue full when awake\n", - ndev->name); - netif_stop_queue(ndev); - - /* trigger the device */ - isl38xx_w32_flush(priv->device_base, ISL38XX_DEV_INT_UPDATE, - ISL38XX_DEV_INT_REG); - udelay(ISL38XX_WRITEIO_DELAY); - goto drop_free; - } - /* Check alignment and WDS frame formatting. The start of the packet should - * be aligned on a 4-byte boundary. If WDS is enabled add another 6 bytes - * and add WDS address information */ - if (likely(((long) skb->data & 0x03) | init_wds)) { - /* get the number of bytes to add and re-align */ - offset = (4 - (long) skb->data) & 0x03; - offset += init_wds ? 6 : 0; - - /* check whether the current skb can be used */ - if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) { - unsigned char *src = skb->data; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "skb offset %i wds %i\n", offset, - init_wds); -#endif - - /* align the buffer on 4-byte boundary */ - skb_reserve(skb, (4 - (long) skb->data) & 0x03); - if (init_wds) { - /* wds requires an additional address field of 6 bytes */ - skb_put(skb, 6); -#ifdef ISLPCI_ETH_DEBUG - printk("islpci_eth_transmit:wds_mac\n"); -#endif - memmove(skb->data + 6, src, skb->len); - skb_copy_to_linear_data(skb, wds_mac, 6); - } else { - memmove(skb->data, src, skb->len); - } - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "memmove %p %p %i\n", skb->data, - src, skb->len); -#endif - } else { - newskb = - dev_alloc_skb(init_wds ? skb->len + 6 : skb->len); - if (unlikely(newskb == NULL)) { - printk(KERN_ERR "%s: Cannot allocate skb\n", - ndev->name); - goto drop_free; - } - newskb_offset = (4 - (long) newskb->data) & 0x03; - - /* Check if newskb->data is aligned */ - if (newskb_offset) - skb_reserve(newskb, newskb_offset); - - skb_put(newskb, init_wds ? skb->len + 6 : skb->len); - if (init_wds) { - skb_copy_from_linear_data(skb, - newskb->data + 6, - skb->len); - skb_copy_to_linear_data(newskb, wds_mac, 6); -#ifdef ISLPCI_ETH_DEBUG - printk("islpci_eth_transmit:wds_mac\n"); -#endif - } else - skb_copy_from_linear_data(skb, newskb->data, - skb->len); - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "memcpy %p %p %i wds %i\n", - newskb->data, skb->data, skb->len, init_wds); -#endif - - newskb->dev = skb->dev; - dev_kfree_skb_irq(skb); - skb = newskb; - } - } - /* display the buffer contents for debugging */ -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_BUFFER_CONTENTS, "\ntx %p ", skb->data); - display_buffer((char *) skb->data, skb->len); -#endif - - /* map the skb buffer to pci memory for DMA operation */ - pci_map_address = pci_map_single(priv->pdev, - (void *) skb->data, skb->len, - PCI_DMA_TODEVICE); - if (unlikely(pci_map_address == 0)) { - printk(KERN_WARNING "%s: cannot map buffer to PCI\n", - ndev->name); - goto drop_free; - } - /* Place the fragment in the control block structure. */ - index = curr_frag % ISL38XX_CB_TX_QSIZE; - fragment = &cb->tx_data_low[index]; - - priv->pci_map_tx_address[index] = pci_map_address; - /* store the skb address for future freeing */ - priv->data_low_tx[index] = skb; - /* set the proper fragment start address and size information */ - frame_size = skb->len; - fragment->size = cpu_to_le16(frame_size); - fragment->flags = cpu_to_le16(0); /* set to 1 if more fragments */ - fragment->address = cpu_to_le32(pci_map_address); - curr_frag++; - - /* The fragment address in the control block must have been - * written before announcing the frame buffer to device. */ - wmb(); - cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ] = cpu_to_le32(curr_frag); - - if (curr_frag - priv->free_data_tx + ISL38XX_MIN_QTHRESHOLD - > ISL38XX_CB_TX_QSIZE) { - /* stop sends from upper layers */ - netif_stop_queue(ndev); - - /* set the full flag for the transmission queue */ - priv->data_low_tx_full = 1; - } - - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += skb->len; - - /* trigger the device */ - islpci_trigger(priv); - - /* unlock the driver code */ - spin_unlock_irqrestore(&priv->slock, flags); - - return NETDEV_TX_OK; - - drop_free: - ndev->stats.tx_dropped++; - spin_unlock_irqrestore(&priv->slock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -static inline int -islpci_monitor_rx(islpci_private *priv, struct sk_buff **skb) -{ - /* The card reports full 802.11 packets but with a 20 bytes - * header and without the FCS. But there a is a bit that - * indicates if the packet is corrupted :-) */ - struct rfmon_header *hdr = (struct rfmon_header *) (*skb)->data; - - if (hdr->flags & 0x01) - /* This one is bad. Drop it ! */ - return -1; - if (priv->ndev->type == ARPHRD_IEEE80211_PRISM) { - struct avs_80211_1_header *avs; - /* extract the relevant data from the header */ - u32 clock = le32_to_cpu(hdr->clock); - u8 rate = hdr->rate; - u16 freq = le16_to_cpu(hdr->freq); - u8 rssi = hdr->rssi; - - skb_pull(*skb, sizeof (struct rfmon_header)); - - if (skb_headroom(*skb) < sizeof (struct avs_80211_1_header)) { - struct sk_buff *newskb = skb_copy_expand(*skb, - sizeof (struct - avs_80211_1_header), - 0, GFP_ATOMIC); - if (newskb) { - dev_kfree_skb_irq(*skb); - *skb = newskb; - } else - return -1; - /* This behavior is not very subtile... */ - } - - /* make room for the new header and fill it. */ - avs = - (struct avs_80211_1_header *) skb_push(*skb, - sizeof (struct - avs_80211_1_header)); - - avs->version = cpu_to_be32(P80211CAPTURE_VERSION); - avs->length = cpu_to_be32(sizeof (struct avs_80211_1_header)); - avs->mactime = cpu_to_be64(clock); - avs->hosttime = cpu_to_be64(jiffies); - avs->phytype = cpu_to_be32(6); /*OFDM: 6 for (g), 8 for (a) */ - avs->channel = cpu_to_be32(channel_of_freq(freq)); - avs->datarate = cpu_to_be32(rate * 5); - avs->antenna = cpu_to_be32(0); /*unknown */ - avs->priority = cpu_to_be32(0); /*unknown */ - avs->ssi_type = cpu_to_be32(3); /*2: dBm, 3: raw RSSI */ - avs->ssi_signal = cpu_to_be32(rssi & 0x7f); - avs->ssi_noise = cpu_to_be32(priv->local_iwstatistics.qual.noise); /*better than 'undefined', I assume */ - avs->preamble = cpu_to_be32(0); /*unknown */ - avs->encoding = cpu_to_be32(0); /*unknown */ - } else - skb_pull(*skb, sizeof (struct rfmon_header)); - - (*skb)->protocol = htons(ETH_P_802_2); - skb_reset_mac_header(*skb); - (*skb)->pkt_type = PACKET_OTHERHOST; - - return 0; -} - -int -islpci_eth_receive(islpci_private *priv) -{ - struct net_device *ndev = priv->ndev; - isl38xx_control_block *control_block = priv->control_block; - struct sk_buff *skb; - u16 size; - u32 index, offset; - unsigned char *src; - int discard = 0; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_receive\n"); -#endif - - /* the device has written an Ethernet frame in the data area - * of the sk_buff without updating the structure, do it now */ - index = priv->free_data_rx % ISL38XX_CB_RX_QSIZE; - size = le16_to_cpu(control_block->rx_data_low[index].size); - skb = priv->data_low_rx[index]; - offset = ((unsigned long) - le32_to_cpu(control_block->rx_data_low[index].address) - - (unsigned long) skb->data) & 3; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, - "frq->addr %x skb->data %p skb->len %u offset %u truesize %u\n ", - control_block->rx_data_low[priv->free_data_rx].address, skb->data, - skb->len, offset, skb->truesize); -#endif - - /* delete the streaming DMA mapping before processing the skb */ - pci_unmap_single(priv->pdev, - priv->pci_map_rx_address[index], - MAX_FRAGMENT_SIZE_RX + 2, PCI_DMA_FROMDEVICE); - - /* update the skb structure and align the buffer */ - skb_put(skb, size); - if (offset) { - /* shift the buffer allocation offset bytes to get the right frame */ - skb_pull(skb, 2); - skb_put(skb, 2); - } -#if VERBOSE > SHOW_ERROR_MESSAGES - /* display the buffer contents for debugging */ - DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data); - display_buffer((char *) skb->data, skb->len); -#endif - - /* check whether WDS is enabled and whether the data frame is a WDS frame */ - - if (init_wds) { - /* WDS enabled, check for the wds address on the first 6 bytes of the buffer */ - src = skb->data + 6; - memmove(skb->data, src, skb->len - 6); - skb_trim(skb, skb->len - 6); - } -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "Fragment size %i in skb at %p\n", size, skb); - DEBUG(SHOW_TRACING, "Skb data at %p, length %i\n", skb->data, skb->len); - - /* display the buffer contents for debugging */ - DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data); - display_buffer((char *) skb->data, skb->len); -#endif - /* take care of monitor mode and spy monitoring. */ - if (unlikely(priv->iw_mode == IW_MODE_MONITOR)) { - skb->dev = ndev; - discard = islpci_monitor_rx(priv, &skb); - } else { - if (unlikely(skb->data[2 * ETH_ALEN] == 0)) { - /* The packet has a rx_annex. Read it for spy monitoring, Then - * remove it, while keeping the 2 leading MAC addr. - */ - struct iw_quality wstats; - struct rx_annex_header *annex = - (struct rx_annex_header *) skb->data; - wstats.level = annex->rfmon.rssi; - /* The noise value can be a bit outdated if nobody's - * reading wireless stats... */ - wstats.noise = priv->local_iwstatistics.qual.noise; - wstats.qual = wstats.level - wstats.noise; - wstats.updated = 0x07; - /* Update spy records */ - wireless_spy_update(ndev, annex->addr2, &wstats); - - skb_copy_from_linear_data(skb, - (skb->data + - sizeof(struct rfmon_header)), - 2 * ETH_ALEN); - skb_pull(skb, sizeof (struct rfmon_header)); - } - skb->protocol = eth_type_trans(skb, ndev); - } - skb->ip_summed = CHECKSUM_NONE; - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += size; - - /* deliver the skb to the network layer */ -#ifdef ISLPCI_ETH_DEBUG - printk - ("islpci_eth_receive:netif_rx %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", - skb->data[0], skb->data[1], skb->data[2], skb->data[3], - skb->data[4], skb->data[5]); -#endif - if (unlikely(discard)) { - dev_kfree_skb_irq(skb); - skb = NULL; - } else - netif_rx(skb); - - /* increment the read index for the rx data low queue */ - priv->free_data_rx++; - - /* add one or more sk_buff structures */ - while (index = - le32_to_cpu(control_block-> - driver_curr_frag[ISL38XX_CB_RX_DATA_LQ]), - index - priv->free_data_rx < ISL38XX_CB_RX_QSIZE) { - /* allocate an sk_buff for received data frames storage - * include any required allignment operations */ - skb = dev_alloc_skb(MAX_FRAGMENT_SIZE_RX + 2); - if (unlikely(skb == NULL)) { - /* error allocating an sk_buff structure elements */ - DEBUG(SHOW_ERROR_MESSAGES, "Error allocating skb\n"); - break; - } - skb_reserve(skb, (4 - (long) skb->data) & 0x03); - /* store the new skb structure pointer */ - index = index % ISL38XX_CB_RX_QSIZE; - priv->data_low_rx[index] = skb; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, - "new alloc skb %p skb->data %p skb->len %u index %u truesize %u\n ", - skb, skb->data, skb->len, index, skb->truesize); -#endif - - /* set the streaming DMA mapping for proper PCI bus operation */ - priv->pci_map_rx_address[index] = - pci_map_single(priv->pdev, (void *) skb->data, - MAX_FRAGMENT_SIZE_RX + 2, - PCI_DMA_FROMDEVICE); - if (unlikely(!priv->pci_map_rx_address[index])) { - /* error mapping the buffer to device accessible memory address */ - DEBUG(SHOW_ERROR_MESSAGES, - "Error mapping DMA address\n"); - - /* free the skbuf structure before aborting */ - dev_kfree_skb_irq(skb); - skb = NULL; - break; - } - /* update the fragment address */ - control_block->rx_data_low[index].address = - cpu_to_le32((u32)priv->pci_map_rx_address[index]); - wmb(); - - /* increment the driver read pointer */ - le32_add_cpu(&control_block-> - driver_curr_frag[ISL38XX_CB_RX_DATA_LQ], 1); - } - - /* trigger the device */ - islpci_trigger(priv); - - return 0; -} - -void -islpci_do_reset_and_wake(struct work_struct *work) -{ - islpci_private *priv = container_of(work, islpci_private, reset_task); - - islpci_reset(priv, 1); - priv->reset_task_pending = 0; - smp_wmb(); - netif_wake_queue(priv->ndev); -} - -void -islpci_eth_tx_timeout(struct net_device *ndev) -{ - islpci_private *priv = netdev_priv(ndev); - - /* increment the transmit error counter */ - ndev->stats.tx_errors++; - - if (!priv->reset_task_pending) { - printk(KERN_WARNING - "%s: tx_timeout, scheduling reset", ndev->name); - netif_stop_queue(ndev); - priv->reset_task_pending = 1; - schedule_work(&priv->reset_task); - } else { - printk(KERN_WARNING - "%s: tx_timeout, waiting for reset", ndev->name); - } -} diff --git a/drivers/net/wireless/prism54/islpci_eth.h b/drivers/net/wireless/prism54/islpci_eth.h deleted file mode 100644 index 80f50f1bc6f2..000000000000 --- a/drivers/net/wireless/prism54/islpci_eth.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2002 Intersil Americas Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#ifndef _ISLPCI_ETH_H -#define _ISLPCI_ETH_H - -#include "isl_38xx.h" -#include "islpci_dev.h" - -struct rfmon_header { - __le16 unk0; /* = 0x0000 */ - __le16 length; /* = 0x1400 */ - __le32 clock; /* 1MHz clock */ - u8 flags; - u8 unk1; - u8 rate; - u8 unk2; - __le16 freq; - __le16 unk3; - u8 rssi; - u8 padding[3]; -} __packed; - -struct rx_annex_header { - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - struct rfmon_header rfmon; -} __packed; - -/* wlan-ng (and hopefully others) AVS header, version one. Fields in - * network byte order. */ -#define P80211CAPTURE_VERSION 0x80211001 - -struct avs_80211_1_header { - __be32 version; - __be32 length; - __be64 mactime; - __be64 hosttime; - __be32 phytype; - __be32 channel; - __be32 datarate; - __be32 antenna; - __be32 priority; - __be32 ssi_type; - __be32 ssi_signal; - __be32 ssi_noise; - __be32 preamble; - __be32 encoding; -}; - -void islpci_eth_cleanup_transmit(islpci_private *, isl38xx_control_block *); -netdev_tx_t islpci_eth_transmit(struct sk_buff *, struct net_device *); -int islpci_eth_receive(islpci_private *); -void islpci_eth_tx_timeout(struct net_device *); -void islpci_do_reset_and_wake(struct work_struct *); - -#endif /* _ISL_GEN_H */ diff --git a/drivers/net/wireless/prism54/islpci_hotplug.c b/drivers/net/wireless/prism54/islpci_hotplug.c deleted file mode 100644 index 300c846ea087..000000000000 --- a/drivers/net/wireless/prism54/islpci_hotplug.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (C) 2002 Intersil Americas Inc. - * Copyright (C) 2003 Herbert Valerio Riedel - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#include -#include -#include -#include -#include /* For __init, __exit */ -#include - -#include "prismcompat.h" -#include "islpci_dev.h" -#include "islpci_mgt.h" /* for pc_debug */ -#include "isl_oid.h" - -MODULE_AUTHOR("[Intersil] R.Bastings and W.Termorshuizen, The prism54.org Development Team "); -MODULE_DESCRIPTION("The Prism54 802.11 Wireless LAN adapter"); -MODULE_LICENSE("GPL"); - -static int init_pcitm = 0; -module_param(init_pcitm, int, 0); - -/* In this order: vendor, device, subvendor, subdevice, class, class_mask, - * driver_data - * If you have an update for this please contact prism54-devel@prism54.org - * The latest list can be found at http://wireless.kernel.org/en/users/Drivers/p54 */ -static const struct pci_device_id prism54_id_tbl[] = { - /* Intersil PRISM Duette/Prism GT Wireless LAN adapter */ - { - 0x1260, 0x3890, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, 0 - }, - - /* 3COM 3CRWE154G72 Wireless LAN adapter */ - { - PCI_VDEVICE(3COM, 0x6001), 0 - }, - - /* Intersil PRISM Indigo Wireless LAN adapter */ - { - 0x1260, 0x3877, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, 0 - }, - - /* Intersil PRISM Javelin/Xbow Wireless LAN adapter */ - { - 0x1260, 0x3886, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, 0 - }, - - /* End of list */ - {0,0,0,0,0,0,0} -}; - -/* register the device with the Hotplug facilities of the kernel */ -MODULE_DEVICE_TABLE(pci, prism54_id_tbl); - -static int prism54_probe(struct pci_dev *, const struct pci_device_id *); -static void prism54_remove(struct pci_dev *); -static int prism54_suspend(struct pci_dev *, pm_message_t state); -static int prism54_resume(struct pci_dev *); - -static struct pci_driver prism54_driver = { - .name = DRV_NAME, - .id_table = prism54_id_tbl, - .probe = prism54_probe, - .remove = prism54_remove, - .suspend = prism54_suspend, - .resume = prism54_resume, -}; - -/****************************************************************************** - Module initialization functions -******************************************************************************/ - -static int -prism54_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct net_device *ndev; - u8 latency_tmr; - u32 mem_addr; - islpci_private *priv; - int rvalue; - - /* Enable the pci device */ - if (pci_enable_device(pdev)) { - printk(KERN_ERR "%s: pci_enable_device() failed.\n", DRV_NAME); - return -ENODEV; - } - - /* check whether the latency timer is set correctly */ - pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_tmr); -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "latency timer: %x\n", latency_tmr); -#endif - if (latency_tmr < PCIDEVICE_LATENCY_TIMER_MIN) { - /* set the latency timer */ - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, - PCIDEVICE_LATENCY_TIMER_VAL); - } - - /* enable PCI DMA */ - if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { - printk(KERN_ERR "%s: 32-bit PCI DMA not supported", DRV_NAME); - goto do_pci_disable_device; - } - - /* 0x40 is the programmable timer to configure the response timeout (TRDY_TIMEOUT) - * 0x41 is the programmable timer to configure the retry timeout (RETRY_TIMEOUT) - * The RETRY_TIMEOUT is used to set the number of retries that the core, as a - * Master, will perform before abandoning a cycle. The default value for - * RETRY_TIMEOUT is 0x80, which far exceeds the PCI 2.1 requirement for new - * devices. A write of zero to the RETRY_TIMEOUT register disables this - * function to allow use with any non-compliant legacy devices that may - * execute more retries. - * - * Writing zero to both these two registers will disable both timeouts and - * *can* solve problems caused by devices that are slow to respond. - * Make this configurable - MSW - */ - if ( init_pcitm >= 0 ) { - pci_write_config_byte(pdev, 0x40, (u8)init_pcitm); - pci_write_config_byte(pdev, 0x41, (u8)init_pcitm); - } else { - printk(KERN_INFO "PCI TRDY/RETRY unchanged\n"); - } - - /* request the pci device I/O regions */ - rvalue = pci_request_regions(pdev, DRV_NAME); - if (rvalue) { - printk(KERN_ERR "%s: pci_request_regions failure (rc=%d)\n", - DRV_NAME, rvalue); - goto do_pci_disable_device; - } - - /* check if the memory window is indeed set */ - rvalue = pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &mem_addr); - if (rvalue || !mem_addr) { - printk(KERN_ERR "%s: PCI device memory region not configured; fix your BIOS or CardBus bridge/drivers\n", - DRV_NAME); - goto do_pci_release_regions; - } - - /* enable PCI bus-mastering */ - DEBUG(SHOW_TRACING, "%s: pci_set_master(pdev)\n", DRV_NAME); - pci_set_master(pdev); - - /* enable MWI */ - pci_try_set_mwi(pdev); - - /* setup the network device interface and its structure */ - if (!(ndev = islpci_setup(pdev))) { - /* error configuring the driver as a network device */ - printk(KERN_ERR "%s: could not configure network device\n", - DRV_NAME); - goto do_pci_clear_mwi; - } - - priv = netdev_priv(ndev); - islpci_set_state(priv, PRV_STATE_PREBOOT); /* we are attempting to boot */ - - /* card is in unknown state yet, might have some interrupts pending */ - isl38xx_disable_interrupts(priv->device_base); - - /* request for the interrupt before uploading the firmware */ - rvalue = request_irq(pdev->irq, islpci_interrupt, - IRQF_SHARED, ndev->name, priv); - - if (rvalue) { - /* error, could not hook the handler to the irq */ - printk(KERN_ERR "%s: could not install IRQ handler\n", - ndev->name); - goto do_unregister_netdev; - } - - /* firmware upload is triggered in islpci_open */ - - return 0; - - do_unregister_netdev: - unregister_netdev(ndev); - islpci_free_memory(priv); - free_netdev(ndev); - priv = NULL; - do_pci_clear_mwi: - pci_clear_mwi(pdev); - do_pci_release_regions: - pci_release_regions(pdev); - do_pci_disable_device: - pci_disable_device(pdev); - return -EIO; -} - -/* set by cleanup_module */ -static volatile int __in_cleanup_module = 0; - -/* this one removes one(!!) instance only */ -static void -prism54_remove(struct pci_dev *pdev) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - islpci_private *priv = ndev ? netdev_priv(ndev) : NULL; - BUG_ON(!priv); - - if (!__in_cleanup_module) { - printk(KERN_DEBUG "%s: hot unplug detected\n", ndev->name); - islpci_set_state(priv, PRV_STATE_OFF); - } - - printk(KERN_DEBUG "%s: removing device\n", ndev->name); - - unregister_netdev(ndev); - - /* free the interrupt request */ - - if (islpci_get_state(priv) != PRV_STATE_OFF) { - isl38xx_disable_interrupts(priv->device_base); - islpci_set_state(priv, PRV_STATE_OFF); - /* This bellow causes a lockup at rmmod time. It might be - * because some interrupts still linger after rmmod time, - * see bug #17 */ - /* pci_set_power_state(pdev, 3);*/ /* try to power-off */ - } - - free_irq(pdev->irq, priv); - - /* free the PCI memory and unmap the remapped page */ - islpci_free_memory(priv); - - free_netdev(ndev); - priv = NULL; - - pci_clear_mwi(pdev); - - pci_release_regions(pdev); - - pci_disable_device(pdev); -} - -static int -prism54_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - islpci_private *priv = ndev ? netdev_priv(ndev) : NULL; - BUG_ON(!priv); - - - pci_save_state(pdev); - - /* tell the device not to trigger interrupts for now... */ - isl38xx_disable_interrupts(priv->device_base); - - /* from now on assume the hardware was already powered down - and don't touch it anymore */ - islpci_set_state(priv, PRV_STATE_OFF); - - netif_stop_queue(ndev); - netif_device_detach(ndev); - - return 0; -} - -static int -prism54_resume(struct pci_dev *pdev) -{ - struct net_device *ndev = pci_get_drvdata(pdev); - islpci_private *priv = ndev ? netdev_priv(ndev) : NULL; - int err; - - BUG_ON(!priv); - - printk(KERN_NOTICE "%s: got resume request\n", ndev->name); - - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR "%s: pci_enable_device failed on resume\n", - ndev->name); - return err; - } - - pci_restore_state(pdev); - - /* alright let's go into the PREBOOT state */ - islpci_reset(priv, 1); - - netif_device_attach(ndev); - netif_start_queue(ndev); - - return 0; -} - -static int __init -prism54_module_init(void) -{ - printk(KERN_INFO "Loaded %s driver, version %s\n", - DRV_NAME, DRV_VERSION); - - __bug_on_wrong_struct_sizes (); - - return pci_register_driver(&prism54_driver); -} - -/* by the time prism54_module_exit() terminates, as a postcondition - * all instances will have been destroyed by calls to - * prism54_remove() */ -static void __exit -prism54_module_exit(void) -{ - __in_cleanup_module = 1; - - pci_unregister_driver(&prism54_driver); - - printk(KERN_INFO "Unloaded %s driver\n", DRV_NAME); - - __in_cleanup_module = 0; -} - -/* register entry points */ -module_init(prism54_module_init); -module_exit(prism54_module_exit); -/* EOF */ diff --git a/drivers/net/wireless/prism54/islpci_mgt.c b/drivers/net/wireless/prism54/islpci_mgt.c deleted file mode 100644 index 0de14dfa68cc..000000000000 --- a/drivers/net/wireless/prism54/islpci_mgt.c +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Copyright (C) 2002 Intersil Americas Inc. - * Copyright 2004 Jens Maurer - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include "prismcompat.h" -#include "isl_38xx.h" -#include "islpci_mgt.h" -#include "isl_oid.h" /* additional types and defs for isl38xx fw */ -#include "isl_ioctl.h" - -#include - -/****************************************************************************** - Global variable definition section -******************************************************************************/ -int pc_debug = VERBOSE; -module_param(pc_debug, int, 0); - -/****************************************************************************** - Driver general functions -******************************************************************************/ -#if VERBOSE > SHOW_ERROR_MESSAGES -void -display_buffer(char *buffer, int length) -{ - if ((pc_debug & SHOW_BUFFER_CONTENTS) == 0) - return; - - while (length > 0) { - printk("[%02x]", *buffer & 255); - length--; - buffer++; - } - - printk("\n"); -} -#endif - -/***************************************************************************** - Queue handling for management frames -******************************************************************************/ - -/* - * Helper function to create a PIMFOR management frame header. - */ -static void -pimfor_encode_header(int operation, u32 oid, u32 length, pimfor_header_t *h) -{ - h->version = PIMFOR_VERSION; - h->operation = operation; - h->device_id = PIMFOR_DEV_ID_MHLI_MIB; - h->flags = 0; - h->oid = cpu_to_be32(oid); - h->length = cpu_to_be32(length); -} - -/* - * Helper function to analyze a PIMFOR management frame header. - */ -static pimfor_header_t * -pimfor_decode_header(void *data, int len) -{ - pimfor_header_t *h = data; - - while ((void *) h < data + len) { - if (h->flags & PIMFOR_FLAG_LITTLE_ENDIAN) { - le32_to_cpus(&h->oid); - le32_to_cpus(&h->length); - } else { - be32_to_cpus(&h->oid); - be32_to_cpus(&h->length); - } - if (h->oid != OID_INL_TUNNEL) - return h; - h++; - } - return NULL; -} - -/* - * Fill the receive queue for management frames with fresh buffers. - */ -int -islpci_mgmt_rx_fill(struct net_device *ndev) -{ - islpci_private *priv = netdev_priv(ndev); - isl38xx_control_block *cb = /* volatile not needed */ - (isl38xx_control_block *) priv->control_block; - u32 curr = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ]); - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgmt_rx_fill\n"); -#endif - - while (curr - priv->index_mgmt_rx < ISL38XX_CB_MGMT_QSIZE) { - u32 index = curr % ISL38XX_CB_MGMT_QSIZE; - struct islpci_membuf *buf = &priv->mgmt_rx[index]; - isl38xx_fragment *frag = &cb->rx_data_mgmt[index]; - - if (buf->mem == NULL) { - buf->mem = kmalloc(MGMT_FRAME_SIZE, GFP_ATOMIC); - if (!buf->mem) - return -ENOMEM; - buf->size = MGMT_FRAME_SIZE; - } - if (buf->pci_addr == 0) { - buf->pci_addr = pci_map_single(priv->pdev, buf->mem, - MGMT_FRAME_SIZE, - PCI_DMA_FROMDEVICE); - if (!buf->pci_addr) { - printk(KERN_WARNING - "Failed to make memory DMA'able.\n"); - return -ENOMEM; - } - } - - /* be safe: always reset control block information */ - frag->size = cpu_to_le16(MGMT_FRAME_SIZE); - frag->flags = 0; - frag->address = cpu_to_le32(buf->pci_addr); - curr++; - - /* The fragment address in the control block must have - * been written before announcing the frame buffer to - * device */ - wmb(); - cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ] = cpu_to_le32(curr); - } - return 0; -} - -/* - * Create and transmit a management frame using "operation" and "oid", - * with arguments data/length. - * We either return an error and free the frame, or we return 0 and - * islpci_mgt_cleanup_transmit() frees the frame in the tx-done - * interrupt. - */ -static int -islpci_mgt_transmit(struct net_device *ndev, int operation, unsigned long oid, - void *data, int length) -{ - islpci_private *priv = netdev_priv(ndev); - isl38xx_control_block *cb = - (isl38xx_control_block *) priv->control_block; - void *p; - int err = -EINVAL; - unsigned long flags; - isl38xx_fragment *frag; - struct islpci_membuf buf; - u32 curr_frag; - int index; - int frag_len = length + PIMFOR_HEADER_SIZE; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_transmit\n"); -#endif - - if (frag_len > MGMT_FRAME_SIZE) { - printk(KERN_DEBUG "%s: mgmt frame too large %d\n", - ndev->name, frag_len); - goto error; - } - - err = -ENOMEM; - p = buf.mem = kmalloc(frag_len, GFP_KERNEL); - if (!buf.mem) - goto error; - - buf.size = frag_len; - - /* create the header directly in the fragment data area */ - pimfor_encode_header(operation, oid, length, (pimfor_header_t *) p); - p += PIMFOR_HEADER_SIZE; - - if (data) - memcpy(p, data, length); - else - memset(p, 0, length); - -#if VERBOSE > SHOW_ERROR_MESSAGES - { - pimfor_header_t *h = buf.mem; - DEBUG(SHOW_PIMFOR_FRAMES, - "PIMFOR: op %i, oid 0x%08lx, device %i, flags 0x%x length 0x%x\n", - h->operation, oid, h->device_id, h->flags, length); - - /* display the buffer contents for debugging */ - display_buffer((char *) h, sizeof (pimfor_header_t)); - display_buffer(p, length); - } -#endif - - err = -ENOMEM; - buf.pci_addr = pci_map_single(priv->pdev, buf.mem, frag_len, - PCI_DMA_TODEVICE); - if (!buf.pci_addr) { - printk(KERN_WARNING "%s: cannot map PCI memory for mgmt\n", - ndev->name); - goto error_free; - } - - /* Protect the control block modifications against interrupts. */ - spin_lock_irqsave(&priv->slock, flags); - curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ]); - if (curr_frag - priv->index_mgmt_tx >= ISL38XX_CB_MGMT_QSIZE) { - printk(KERN_WARNING "%s: mgmt tx queue is still full\n", - ndev->name); - goto error_unlock; - } - - /* commit the frame to the tx device queue */ - index = curr_frag % ISL38XX_CB_MGMT_QSIZE; - priv->mgmt_tx[index] = buf; - frag = &cb->tx_data_mgmt[index]; - frag->size = cpu_to_le16(frag_len); - frag->flags = 0; /* for any other than the last fragment, set to 1 */ - frag->address = cpu_to_le32(buf.pci_addr); - - /* The fragment address in the control block must have - * been written before announcing the frame buffer to - * device */ - wmb(); - cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ] = cpu_to_le32(curr_frag + 1); - spin_unlock_irqrestore(&priv->slock, flags); - - /* trigger the device */ - islpci_trigger(priv); - return 0; - - error_unlock: - spin_unlock_irqrestore(&priv->slock, flags); - error_free: - kfree(buf.mem); - error: - return err; -} - -/* - * Receive a management frame from the device. - * This can be an arbitrary number of traps, and at most one response - * frame for a previous request sent via islpci_mgt_transmit(). - */ -int -islpci_mgt_receive(struct net_device *ndev) -{ - islpci_private *priv = netdev_priv(ndev); - isl38xx_control_block *cb = - (isl38xx_control_block *) priv->control_block; - u32 curr_frag; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_receive\n"); -#endif - - /* Only once per interrupt, determine fragment range to - * process. This avoids an endless loop (i.e. lockup) if - * frames come in faster than we can process them. */ - curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_RX_MGMTQ]); - barrier(); - - for (; priv->index_mgmt_rx < curr_frag; priv->index_mgmt_rx++) { - pimfor_header_t *header; - u32 index = priv->index_mgmt_rx % ISL38XX_CB_MGMT_QSIZE; - struct islpci_membuf *buf = &priv->mgmt_rx[index]; - u16 frag_len; - int size; - struct islpci_mgmtframe *frame; - - /* I have no idea (and no documentation) if flags != 0 - * is possible. Drop the frame, reuse the buffer. */ - if (le16_to_cpu(cb->rx_data_mgmt[index].flags) != 0) { - printk(KERN_WARNING "%s: unknown flags 0x%04x\n", - ndev->name, - le16_to_cpu(cb->rx_data_mgmt[index].flags)); - continue; - } - - /* The device only returns the size of the header(s) here. */ - frag_len = le16_to_cpu(cb->rx_data_mgmt[index].size); - - /* - * We appear to have no way to tell the device the - * size of a receive buffer. Thus, if this check - * triggers, we likely have kernel heap corruption. */ - if (frag_len > MGMT_FRAME_SIZE) { - printk(KERN_WARNING - "%s: Bogus packet size of %d (%#x).\n", - ndev->name, frag_len, frag_len); - frag_len = MGMT_FRAME_SIZE; - } - - /* Ensure the results of device DMA are visible to the CPU. */ - pci_dma_sync_single_for_cpu(priv->pdev, buf->pci_addr, - buf->size, PCI_DMA_FROMDEVICE); - - /* Perform endianess conversion for PIMFOR header in-place. */ - header = pimfor_decode_header(buf->mem, frag_len); - if (!header) { - printk(KERN_WARNING "%s: no PIMFOR header found\n", - ndev->name); - continue; - } - - /* The device ID from the PIMFOR packet received from - * the MVC is always 0. We forward a sensible device_id. - * Not that anyone upstream would care... */ - header->device_id = priv->ndev->ifindex; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_PIMFOR_FRAMES, - "PIMFOR: op %i, oid 0x%08x, device %i, flags 0x%x length 0x%x\n", - header->operation, header->oid, header->device_id, - header->flags, header->length); - - /* display the buffer contents for debugging */ - display_buffer((char *) header, PIMFOR_HEADER_SIZE); - display_buffer((char *) header + PIMFOR_HEADER_SIZE, - header->length); -#endif - - /* nobody sends these */ - if (header->flags & PIMFOR_FLAG_APPLIC_ORIGIN) { - printk(KERN_DEBUG - "%s: errant PIMFOR application frame\n", - ndev->name); - continue; - } - - /* Determine frame size, skipping OID_INL_TUNNEL headers. */ - size = PIMFOR_HEADER_SIZE + header->length; - frame = kmalloc(sizeof(struct islpci_mgmtframe) + size, - GFP_ATOMIC); - if (!frame) - continue; - - frame->ndev = ndev; - memcpy(&frame->buf, header, size); - frame->header = (pimfor_header_t *) frame->buf; - frame->data = frame->buf + PIMFOR_HEADER_SIZE; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_PIMFOR_FRAMES, - "frame: header: %p, data: %p, size: %d\n", - frame->header, frame->data, size); -#endif - - if (header->operation == PIMFOR_OP_TRAP) { -#if VERBOSE > SHOW_ERROR_MESSAGES - printk(KERN_DEBUG - "TRAP: oid 0x%x, device %i, flags 0x%x length %i\n", - header->oid, header->device_id, header->flags, - header->length); -#endif - - /* Create work to handle trap out of interrupt - * context. */ - INIT_WORK(&frame->ws, prism54_process_trap); - schedule_work(&frame->ws); - - } else { - /* Signal the one waiting process that a response - * has been received. */ - if ((frame = xchg(&priv->mgmt_received, frame)) != NULL) { - printk(KERN_WARNING - "%s: mgmt response not collected\n", - ndev->name); - kfree(frame); - } -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_TRACING, "Wake up Mgmt Queue\n"); -#endif - wake_up(&priv->mgmt_wqueue); - } - - } - - return 0; -} - -/* - * Cleanup the transmit queue by freeing all frames handled by the device. - */ -void -islpci_mgt_cleanup_transmit(struct net_device *ndev) -{ - islpci_private *priv = netdev_priv(ndev); - isl38xx_control_block *cb = /* volatile not needed */ - (isl38xx_control_block *) priv->control_block; - u32 curr_frag; - -#if VERBOSE > SHOW_ERROR_MESSAGES - DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_cleanup_transmit\n"); -#endif - - /* Only once per cleanup, determine fragment range to - * process. This avoids an endless loop (i.e. lockup) if - * the device became confused, incrementing device_curr_frag - * rapidly. */ - curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_TX_MGMTQ]); - barrier(); - - for (; priv->index_mgmt_tx < curr_frag; priv->index_mgmt_tx++) { - int index = priv->index_mgmt_tx % ISL38XX_CB_MGMT_QSIZE; - struct islpci_membuf *buf = &priv->mgmt_tx[index]; - pci_unmap_single(priv->pdev, buf->pci_addr, buf->size, - PCI_DMA_TODEVICE); - buf->pci_addr = 0; - kfree(buf->mem); - buf->mem = NULL; - buf->size = 0; - } -} - -/* - * Perform one request-response transaction to the device. - */ -int -islpci_mgt_transaction(struct net_device *ndev, - int operation, unsigned long oid, - void *senddata, int sendlen, - struct islpci_mgmtframe **recvframe) -{ - islpci_private *priv = netdev_priv(ndev); - const long wait_cycle_jiffies = msecs_to_jiffies(ISL38XX_WAIT_CYCLE * 10); - long timeout_left = ISL38XX_MAX_WAIT_CYCLES * wait_cycle_jiffies; - int err; - DEFINE_WAIT(wait); - - *recvframe = NULL; - - if (mutex_lock_interruptible(&priv->mgmt_lock)) - return -ERESTARTSYS; - - prepare_to_wait(&priv->mgmt_wqueue, &wait, TASK_UNINTERRUPTIBLE); - err = islpci_mgt_transmit(ndev, operation, oid, senddata, sendlen); - if (err) - goto out; - - err = -ETIMEDOUT; - while (timeout_left > 0) { - int timeleft; - struct islpci_mgmtframe *frame; - - timeleft = schedule_timeout_uninterruptible(wait_cycle_jiffies); - frame = xchg(&priv->mgmt_received, NULL); - if (frame) { - if (frame->header->oid == oid) { - *recvframe = frame; - err = 0; - goto out; - } else { - printk(KERN_DEBUG - "%s: expecting oid 0x%x, received 0x%x.\n", - ndev->name, (unsigned int) oid, - frame->header->oid); - kfree(frame); - frame = NULL; - } - } - if (timeleft == 0) { - printk(KERN_DEBUG - "%s: timeout waiting for mgmt response %lu, " - "triggering device\n", - ndev->name, timeout_left); - islpci_trigger(priv); - } - timeout_left += timeleft - wait_cycle_jiffies; - } - printk(KERN_WARNING "%s: timeout waiting for mgmt response\n", - ndev->name); - - /* TODO: we should reset the device here */ - out: - finish_wait(&priv->mgmt_wqueue, &wait); - mutex_unlock(&priv->mgmt_lock); - return err; -} - diff --git a/drivers/net/wireless/prism54/islpci_mgt.h b/drivers/net/wireless/prism54/islpci_mgt.h deleted file mode 100644 index 700c434c8803..000000000000 --- a/drivers/net/wireless/prism54/islpci_mgt.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2002 Intersil Americas Inc. - * Copyright (C) 2003 Luis R. Rodriguez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#ifndef _ISLPCI_MGT_H -#define _ISLPCI_MGT_H - -#include -#include -#include - -/* - * Function definitions - */ - -#define K_DEBUG(f, m, args...) do { if(f & m) printk(KERN_DEBUG args); } while(0) -#define DEBUG(f, args...) K_DEBUG(f, pc_debug, args) - -extern int pc_debug; -#define init_wds 0 /* help compiler optimize away dead code */ - - -/* General driver definitions */ -#define PCIDEVICE_LATENCY_TIMER_MIN 0x40 -#define PCIDEVICE_LATENCY_TIMER_VAL 0x50 - -/* Debugging verbose definitions */ -#define SHOW_NOTHING 0x00 /* overrules everything */ -#define SHOW_ANYTHING 0xFF -#define SHOW_ERROR_MESSAGES 0x01 -#define SHOW_TRAPS 0x02 -#define SHOW_FUNCTION_CALLS 0x04 -#define SHOW_TRACING 0x08 -#define SHOW_QUEUE_INDEXES 0x10 -#define SHOW_PIMFOR_FRAMES 0x20 -#define SHOW_BUFFER_CONTENTS 0x40 -#define VERBOSE 0x01 - -/* Default card definitions */ -#define CARD_DEFAULT_CHANNEL 6 -#define CARD_DEFAULT_MODE INL_MODE_CLIENT -#define CARD_DEFAULT_IW_MODE IW_MODE_INFRA -#define CARD_DEFAULT_BSSTYPE DOT11_BSSTYPE_INFRA -#define CARD_DEFAULT_CLIENT_SSID "" -#define CARD_DEFAULT_AP_SSID "default" -#define CARD_DEFAULT_KEY1 "default_key_1" -#define CARD_DEFAULT_KEY2 "default_key_2" -#define CARD_DEFAULT_KEY3 "default_key_3" -#define CARD_DEFAULT_KEY4 "default_key_4" -#define CARD_DEFAULT_WEP 0 -#define CARD_DEFAULT_FILTER 0 -#define CARD_DEFAULT_WDS 0 -#define CARD_DEFAULT_AUTHEN DOT11_AUTH_OS -#define CARD_DEFAULT_DOT1X 0 -#define CARD_DEFAULT_MLME_MODE DOT11_MLME_AUTO -#define CARD_DEFAULT_CONFORMANCE OID_INL_CONFORMANCE_NONE -#define CARD_DEFAULT_PROFILE DOT11_PROFILE_MIXED_G_WIFI -#define CARD_DEFAULT_MAXFRAMEBURST DOT11_MAXFRAMEBURST_MIXED_SAFE - -/* PIMFOR package definitions */ -#define PIMFOR_ETHERTYPE 0x8828 -#define PIMFOR_HEADER_SIZE 12 -#define PIMFOR_VERSION 1 -#define PIMFOR_OP_GET 0 -#define PIMFOR_OP_SET 1 -#define PIMFOR_OP_RESPONSE 2 -#define PIMFOR_OP_ERROR 3 -#define PIMFOR_OP_TRAP 4 -#define PIMFOR_OP_RESERVED 5 /* till 255 */ -#define PIMFOR_DEV_ID_MHLI_MIB 0 -#define PIMFOR_FLAG_APPLIC_ORIGIN 0x01 -#define PIMFOR_FLAG_LITTLE_ENDIAN 0x02 - -void display_buffer(char *, int); - -/* - * Type definition section - * - * the structure defines only the header allowing copyless - * frame handling - */ -typedef struct { - u8 version; - u8 operation; - u32 oid; - u8 device_id; - u8 flags; - u32 length; -} __packed -pimfor_header_t; - -/* A received and interrupt-processed management frame, either for - * schedule_work(prism54_process_trap) or for priv->mgmt_received, - * processed by islpci_mgt_transaction(). */ -struct islpci_mgmtframe { - struct net_device *ndev; /* pointer to network device */ - pimfor_header_t *header; /* payload header, points into buf */ - void *data; /* payload ex header, points into buf */ - struct work_struct ws; /* argument for schedule_work() */ - char buf[0]; /* fragment buffer */ -}; - -int -islpci_mgt_receive(struct net_device *ndev); - -int -islpci_mgmt_rx_fill(struct net_device *ndev); - -void -islpci_mgt_cleanup_transmit(struct net_device *ndev); - -int -islpci_mgt_transaction(struct net_device *ndev, - int operation, unsigned long oid, - void *senddata, int sendlen, - struct islpci_mgmtframe **recvframe); - -static inline void -islpci_mgt_release(struct islpci_mgmtframe *frame) -{ - kfree(frame); -} - -#endif /* _ISLPCI_MGT_H */ diff --git a/drivers/net/wireless/prism54/oid_mgt.c b/drivers/net/wireless/prism54/oid_mgt.c deleted file mode 100644 index 3a8d2dbcfecd..000000000000 --- a/drivers/net/wireless/prism54/oid_mgt.c +++ /dev/null @@ -1,901 +0,0 @@ -/* - * Copyright (C) 2003,2004 Aurelien Alleaume - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#include -#include - -#include "prismcompat.h" -#include "islpci_dev.h" -#include "islpci_mgt.h" -#include "isl_oid.h" -#include "oid_mgt.h" -#include "isl_ioctl.h" - -/* to convert between channel and freq */ -static const int frequency_list_bg[] = { 2412, 2417, 2422, 2427, 2432, - 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 -}; - -int -channel_of_freq(int f) -{ - int c = 0; - - if ((f >= 2412) && (f <= 2484)) { - while ((c < 14) && (f != frequency_list_bg[c])) - c++; - return (c >= 14) ? 0 : ++c; - } else if ((f >= (int) 5000) && (f <= (int) 6000)) { - return ( (f - 5000) / 5 ); - } else - return 0; -} - -#define OID_STRUCT(name,oid,s,t) [name] = {oid, 0, sizeof(s), t} -#define OID_STRUCT_C(name,oid,s,t) OID_STRUCT(name,oid,s,t | OID_FLAG_CACHED) -#define OID_U32(name,oid) OID_STRUCT(name,oid,u32,OID_TYPE_U32) -#define OID_U32_C(name,oid) OID_STRUCT_C(name,oid,u32,OID_TYPE_U32) -#define OID_STRUCT_MLME(name,oid) OID_STRUCT(name,oid,struct obj_mlme,OID_TYPE_MLME) -#define OID_STRUCT_MLMEEX(name,oid) OID_STRUCT(name,oid,struct obj_mlmeex,OID_TYPE_MLMEEX) - -#define OID_UNKNOWN(name,oid) OID_STRUCT(name,oid,0,0) - -struct oid_t isl_oid[] = { - OID_STRUCT(GEN_OID_MACADDRESS, 0x00000000, u8[6], OID_TYPE_ADDR), - OID_U32(GEN_OID_LINKSTATE, 0x00000001), - OID_UNKNOWN(GEN_OID_WATCHDOG, 0x00000002), - OID_UNKNOWN(GEN_OID_MIBOP, 0x00000003), - OID_UNKNOWN(GEN_OID_OPTIONS, 0x00000004), - OID_UNKNOWN(GEN_OID_LEDCONFIG, 0x00000005), - - /* 802.11 */ - OID_U32_C(DOT11_OID_BSSTYPE, 0x10000000), - OID_STRUCT_C(DOT11_OID_BSSID, 0x10000001, u8[6], OID_TYPE_RAW), - OID_STRUCT_C(DOT11_OID_SSID, 0x10000002, struct obj_ssid, - OID_TYPE_SSID), - OID_U32(DOT11_OID_STATE, 0x10000003), - OID_U32(DOT11_OID_AID, 0x10000004), - OID_STRUCT(DOT11_OID_COUNTRYSTRING, 0x10000005, u8[4], OID_TYPE_RAW), - OID_STRUCT_C(DOT11_OID_SSIDOVERRIDE, 0x10000006, struct obj_ssid, - OID_TYPE_SSID), - - OID_U32(DOT11_OID_MEDIUMLIMIT, 0x11000000), - OID_U32_C(DOT11_OID_BEACONPERIOD, 0x11000001), - OID_U32(DOT11_OID_DTIMPERIOD, 0x11000002), - OID_U32(DOT11_OID_ATIMWINDOW, 0x11000003), - OID_U32(DOT11_OID_LISTENINTERVAL, 0x11000004), - OID_U32(DOT11_OID_CFPPERIOD, 0x11000005), - OID_U32(DOT11_OID_CFPDURATION, 0x11000006), - - OID_U32_C(DOT11_OID_AUTHENABLE, 0x12000000), - OID_U32_C(DOT11_OID_PRIVACYINVOKED, 0x12000001), - OID_U32_C(DOT11_OID_EXUNENCRYPTED, 0x12000002), - OID_U32_C(DOT11_OID_DEFKEYID, 0x12000003), - [DOT11_OID_DEFKEYX] = {0x12000004, 3, sizeof (struct obj_key), - OID_FLAG_CACHED | OID_TYPE_KEY}, /* DOT11_OID_DEFKEY1,...DOT11_OID_DEFKEY4 */ - OID_UNKNOWN(DOT11_OID_STAKEY, 0x12000008), - OID_U32(DOT11_OID_REKEYTHRESHOLD, 0x12000009), - OID_UNKNOWN(DOT11_OID_STASC, 0x1200000a), - - OID_U32(DOT11_OID_PRIVTXREJECTED, 0x1a000000), - OID_U32(DOT11_OID_PRIVRXPLAIN, 0x1a000001), - OID_U32(DOT11_OID_PRIVRXFAILED, 0x1a000002), - OID_U32(DOT11_OID_PRIVRXNOKEY, 0x1a000003), - - OID_U32_C(DOT11_OID_RTSTHRESH, 0x13000000), - OID_U32_C(DOT11_OID_FRAGTHRESH, 0x13000001), - OID_U32_C(DOT11_OID_SHORTRETRIES, 0x13000002), - OID_U32_C(DOT11_OID_LONGRETRIES, 0x13000003), - OID_U32_C(DOT11_OID_MAXTXLIFETIME, 0x13000004), - OID_U32(DOT11_OID_MAXRXLIFETIME, 0x13000005), - OID_U32(DOT11_OID_AUTHRESPTIMEOUT, 0x13000006), - OID_U32(DOT11_OID_ASSOCRESPTIMEOUT, 0x13000007), - - OID_UNKNOWN(DOT11_OID_ALOFT_TABLE, 0x1d000000), - OID_UNKNOWN(DOT11_OID_ALOFT_CTRL_TABLE, 0x1d000001), - OID_UNKNOWN(DOT11_OID_ALOFT_RETREAT, 0x1d000002), - OID_UNKNOWN(DOT11_OID_ALOFT_PROGRESS, 0x1d000003), - OID_U32(DOT11_OID_ALOFT_FIXEDRATE, 0x1d000004), - OID_UNKNOWN(DOT11_OID_ALOFT_RSSIGRAPH, 0x1d000005), - OID_UNKNOWN(DOT11_OID_ALOFT_CONFIG, 0x1d000006), - - [DOT11_OID_VDCFX] = {0x1b000000, 7, 0, 0}, - OID_U32(DOT11_OID_MAXFRAMEBURST, 0x1b000008), - - OID_U32(DOT11_OID_PSM, 0x14000000), - OID_U32(DOT11_OID_CAMTIMEOUT, 0x14000001), - OID_U32(DOT11_OID_RECEIVEDTIMS, 0x14000002), - OID_U32(DOT11_OID_ROAMPREFERENCE, 0x14000003), - - OID_U32(DOT11_OID_BRIDGELOCAL, 0x15000000), - OID_U32(DOT11_OID_CLIENTS, 0x15000001), - OID_U32(DOT11_OID_CLIENTSASSOCIATED, 0x15000002), - [DOT11_OID_CLIENTX] = {0x15000003, 2006, 0, 0}, /* DOT11_OID_CLIENTX,...DOT11_OID_CLIENT2007 */ - - OID_STRUCT(DOT11_OID_CLIENTFIND, 0x150007DB, u8[6], OID_TYPE_ADDR), - OID_STRUCT(DOT11_OID_WDSLINKADD, 0x150007DC, u8[6], OID_TYPE_ADDR), - OID_STRUCT(DOT11_OID_WDSLINKREMOVE, 0x150007DD, u8[6], OID_TYPE_ADDR), - OID_STRUCT(DOT11_OID_EAPAUTHSTA, 0x150007DE, u8[6], OID_TYPE_ADDR), - OID_STRUCT(DOT11_OID_EAPUNAUTHSTA, 0x150007DF, u8[6], OID_TYPE_ADDR), - OID_U32_C(DOT11_OID_DOT1XENABLE, 0x150007E0), - OID_UNKNOWN(DOT11_OID_MICFAILURE, 0x150007E1), - OID_UNKNOWN(DOT11_OID_REKEYINDICATE, 0x150007E2), - - OID_U32(DOT11_OID_MPDUTXSUCCESSFUL, 0x16000000), - OID_U32(DOT11_OID_MPDUTXONERETRY, 0x16000001), - OID_U32(DOT11_OID_MPDUTXMULTIPLERETRIES, 0x16000002), - OID_U32(DOT11_OID_MPDUTXFAILED, 0x16000003), - OID_U32(DOT11_OID_MPDURXSUCCESSFUL, 0x16000004), - OID_U32(DOT11_OID_MPDURXDUPS, 0x16000005), - OID_U32(DOT11_OID_RTSSUCCESSFUL, 0x16000006), - OID_U32(DOT11_OID_RTSFAILED, 0x16000007), - OID_U32(DOT11_OID_ACKFAILED, 0x16000008), - OID_U32(DOT11_OID_FRAMERECEIVES, 0x16000009), - OID_U32(DOT11_OID_FRAMEERRORS, 0x1600000A), - OID_U32(DOT11_OID_FRAMEABORTS, 0x1600000B), - OID_U32(DOT11_OID_FRAMEABORTSPHY, 0x1600000C), - - OID_U32(DOT11_OID_SLOTTIME, 0x17000000), - OID_U32(DOT11_OID_CWMIN, 0x17000001), - OID_U32(DOT11_OID_CWMAX, 0x17000002), - OID_U32(DOT11_OID_ACKWINDOW, 0x17000003), - OID_U32(DOT11_OID_ANTENNARX, 0x17000004), - OID_U32(DOT11_OID_ANTENNATX, 0x17000005), - OID_U32(DOT11_OID_ANTENNADIVERSITY, 0x17000006), - OID_U32_C(DOT11_OID_CHANNEL, 0x17000007), - OID_U32_C(DOT11_OID_EDTHRESHOLD, 0x17000008), - OID_U32(DOT11_OID_PREAMBLESETTINGS, 0x17000009), - OID_STRUCT(DOT11_OID_RATES, 0x1700000A, u8[IWMAX_BITRATES + 1], - OID_TYPE_RAW), - OID_U32(DOT11_OID_CCAMODESUPPORTED, 0x1700000B), - OID_U32(DOT11_OID_CCAMODE, 0x1700000C), - OID_UNKNOWN(DOT11_OID_RSSIVECTOR, 0x1700000D), - OID_UNKNOWN(DOT11_OID_OUTPUTPOWERTABLE, 0x1700000E), - OID_U32(DOT11_OID_OUTPUTPOWER, 0x1700000F), - OID_STRUCT(DOT11_OID_SUPPORTEDRATES, 0x17000010, - u8[IWMAX_BITRATES + 1], OID_TYPE_RAW), - OID_U32_C(DOT11_OID_FREQUENCY, 0x17000011), - [DOT11_OID_SUPPORTEDFREQUENCIES] = - {0x17000012, 0, sizeof (struct obj_frequencies) - + sizeof (u16) * IWMAX_FREQ, OID_TYPE_FREQUENCIES}, - - OID_U32(DOT11_OID_NOISEFLOOR, 0x17000013), - OID_STRUCT(DOT11_OID_FREQUENCYACTIVITY, 0x17000014, u8[IWMAX_FREQ + 1], - OID_TYPE_RAW), - OID_UNKNOWN(DOT11_OID_IQCALIBRATIONTABLE, 0x17000015), - OID_U32(DOT11_OID_NONERPPROTECTION, 0x17000016), - OID_U32(DOT11_OID_SLOTSETTINGS, 0x17000017), - OID_U32(DOT11_OID_NONERPTIMEOUT, 0x17000018), - OID_U32(DOT11_OID_PROFILES, 0x17000019), - OID_STRUCT(DOT11_OID_EXTENDEDRATES, 0x17000020, - u8[IWMAX_BITRATES + 1], OID_TYPE_RAW), - - OID_STRUCT_MLME(DOT11_OID_DEAUTHENTICATE, 0x18000000), - OID_STRUCT_MLME(DOT11_OID_AUTHENTICATE, 0x18000001), - OID_STRUCT_MLME(DOT11_OID_DISASSOCIATE, 0x18000002), - OID_STRUCT_MLME(DOT11_OID_ASSOCIATE, 0x18000003), - OID_UNKNOWN(DOT11_OID_SCAN, 0x18000004), - OID_STRUCT_MLMEEX(DOT11_OID_BEACON, 0x18000005), - OID_STRUCT_MLMEEX(DOT11_OID_PROBE, 0x18000006), - OID_STRUCT_MLMEEX(DOT11_OID_DEAUTHENTICATEEX, 0x18000007), - OID_STRUCT_MLMEEX(DOT11_OID_AUTHENTICATEEX, 0x18000008), - OID_STRUCT_MLMEEX(DOT11_OID_DISASSOCIATEEX, 0x18000009), - OID_STRUCT_MLMEEX(DOT11_OID_ASSOCIATEEX, 0x1800000A), - OID_STRUCT_MLMEEX(DOT11_OID_REASSOCIATE, 0x1800000B), - OID_STRUCT_MLMEEX(DOT11_OID_REASSOCIATEEX, 0x1800000C), - - OID_U32(DOT11_OID_NONERPSTATUS, 0x1E000000), - - OID_U32(DOT11_OID_STATIMEOUT, 0x19000000), - OID_U32_C(DOT11_OID_MLMEAUTOLEVEL, 0x19000001), - OID_U32(DOT11_OID_BSSTIMEOUT, 0x19000002), - [DOT11_OID_ATTACHMENT] = {0x19000003, 0, - sizeof(struct obj_attachment), OID_TYPE_ATTACH}, - OID_STRUCT_C(DOT11_OID_PSMBUFFER, 0x19000004, struct obj_buffer, - OID_TYPE_BUFFER), - - OID_U32(DOT11_OID_BSSS, 0x1C000000), - [DOT11_OID_BSSX] = {0x1C000001, 63, sizeof (struct obj_bss), - OID_TYPE_BSS}, /*DOT11_OID_BSS1,...,DOT11_OID_BSS64 */ - OID_STRUCT(DOT11_OID_BSSFIND, 0x1C000042, struct obj_bss, OID_TYPE_BSS), - [DOT11_OID_BSSLIST] = {0x1C000043, 0, sizeof (struct - obj_bsslist) + - sizeof (struct obj_bss[IWMAX_BSS]), - OID_TYPE_BSSLIST}, - - OID_UNKNOWN(OID_INL_TUNNEL, 0xFF020000), - OID_UNKNOWN(OID_INL_MEMADDR, 0xFF020001), - OID_UNKNOWN(OID_INL_MEMORY, 0xFF020002), - OID_U32_C(OID_INL_MODE, 0xFF020003), - OID_UNKNOWN(OID_INL_COMPONENT_NR, 0xFF020004), - OID_STRUCT(OID_INL_VERSION, 0xFF020005, u8[8], OID_TYPE_RAW), - OID_UNKNOWN(OID_INL_INTERFACE_ID, 0xFF020006), - OID_UNKNOWN(OID_INL_COMPONENT_ID, 0xFF020007), - OID_U32_C(OID_INL_CONFIG, 0xFF020008), - OID_U32_C(OID_INL_DOT11D_CONFORMANCE, 0xFF02000C), - OID_U32(OID_INL_PHYCAPABILITIES, 0xFF02000D), - OID_U32_C(OID_INL_OUTPUTPOWER, 0xFF02000F), - -}; - -int -mgt_init(islpci_private *priv) -{ - int i; - - priv->mib = kcalloc(OID_NUM_LAST, sizeof (void *), GFP_KERNEL); - if (!priv->mib) - return -ENOMEM; - - /* Alloc the cache */ - for (i = 0; i < OID_NUM_LAST; i++) { - if (isl_oid[i].flags & OID_FLAG_CACHED) { - priv->mib[i] = kzalloc(isl_oid[i].size * - (isl_oid[i].range + 1), - GFP_KERNEL); - if (!priv->mib[i]) - return -ENOMEM; - } else - priv->mib[i] = NULL; - } - - init_rwsem(&priv->mib_sem); - prism54_mib_init(priv); - - return 0; -} - -void -mgt_clean(islpci_private *priv) -{ - int i; - - if (!priv->mib) - return; - for (i = 0; i < OID_NUM_LAST; i++) { - kfree(priv->mib[i]); - priv->mib[i] = NULL; - } - kfree(priv->mib); - priv->mib = NULL; -} - -void -mgt_le_to_cpu(int type, void *data) -{ - switch (type) { - case OID_TYPE_U32: - *(u32 *) data = le32_to_cpu(*(u32 *) data); - break; - case OID_TYPE_BUFFER:{ - struct obj_buffer *buff = data; - buff->size = le32_to_cpu(buff->size); - buff->addr = le32_to_cpu(buff->addr); - break; - } - case OID_TYPE_BSS:{ - struct obj_bss *bss = data; - bss->age = le16_to_cpu(bss->age); - bss->channel = le16_to_cpu(bss->channel); - bss->capinfo = le16_to_cpu(bss->capinfo); - bss->rates = le16_to_cpu(bss->rates); - bss->basic_rates = le16_to_cpu(bss->basic_rates); - break; - } - case OID_TYPE_BSSLIST:{ - struct obj_bsslist *list = data; - int i; - list->nr = le32_to_cpu(list->nr); - for (i = 0; i < list->nr; i++) - mgt_le_to_cpu(OID_TYPE_BSS, &list->bsslist[i]); - break; - } - case OID_TYPE_FREQUENCIES:{ - struct obj_frequencies *freq = data; - int i; - freq->nr = le16_to_cpu(freq->nr); - for (i = 0; i < freq->nr; i++) - freq->mhz[i] = le16_to_cpu(freq->mhz[i]); - break; - } - case OID_TYPE_MLME:{ - struct obj_mlme *mlme = data; - mlme->id = le16_to_cpu(mlme->id); - mlme->state = le16_to_cpu(mlme->state); - mlme->code = le16_to_cpu(mlme->code); - break; - } - case OID_TYPE_MLMEEX:{ - struct obj_mlmeex *mlme = data; - mlme->id = le16_to_cpu(mlme->id); - mlme->state = le16_to_cpu(mlme->state); - mlme->code = le16_to_cpu(mlme->code); - mlme->size = le16_to_cpu(mlme->size); - break; - } - case OID_TYPE_ATTACH:{ - struct obj_attachment *attach = data; - attach->id = le16_to_cpu(attach->id); - attach->size = le16_to_cpu(attach->size); - break; - } - case OID_TYPE_SSID: - case OID_TYPE_KEY: - case OID_TYPE_ADDR: - case OID_TYPE_RAW: - break; - default: - BUG(); - } -} - -static void -mgt_cpu_to_le(int type, void *data) -{ - switch (type) { - case OID_TYPE_U32: - *(u32 *) data = cpu_to_le32(*(u32 *) data); - break; - case OID_TYPE_BUFFER:{ - struct obj_buffer *buff = data; - buff->size = cpu_to_le32(buff->size); - buff->addr = cpu_to_le32(buff->addr); - break; - } - case OID_TYPE_BSS:{ - struct obj_bss *bss = data; - bss->age = cpu_to_le16(bss->age); - bss->channel = cpu_to_le16(bss->channel); - bss->capinfo = cpu_to_le16(bss->capinfo); - bss->rates = cpu_to_le16(bss->rates); - bss->basic_rates = cpu_to_le16(bss->basic_rates); - break; - } - case OID_TYPE_BSSLIST:{ - struct obj_bsslist *list = data; - int i; - list->nr = cpu_to_le32(list->nr); - for (i = 0; i < list->nr; i++) - mgt_cpu_to_le(OID_TYPE_BSS, &list->bsslist[i]); - break; - } - case OID_TYPE_FREQUENCIES:{ - struct obj_frequencies *freq = data; - int i; - freq->nr = cpu_to_le16(freq->nr); - for (i = 0; i < freq->nr; i++) - freq->mhz[i] = cpu_to_le16(freq->mhz[i]); - break; - } - case OID_TYPE_MLME:{ - struct obj_mlme *mlme = data; - mlme->id = cpu_to_le16(mlme->id); - mlme->state = cpu_to_le16(mlme->state); - mlme->code = cpu_to_le16(mlme->code); - break; - } - case OID_TYPE_MLMEEX:{ - struct obj_mlmeex *mlme = data; - mlme->id = cpu_to_le16(mlme->id); - mlme->state = cpu_to_le16(mlme->state); - mlme->code = cpu_to_le16(mlme->code); - mlme->size = cpu_to_le16(mlme->size); - break; - } - case OID_TYPE_ATTACH:{ - struct obj_attachment *attach = data; - attach->id = cpu_to_le16(attach->id); - attach->size = cpu_to_le16(attach->size); - break; - } - case OID_TYPE_SSID: - case OID_TYPE_KEY: - case OID_TYPE_ADDR: - case OID_TYPE_RAW: - break; - default: - BUG(); - } -} - -/* Note : data is modified during this function */ - -int -mgt_set_request(islpci_private *priv, enum oid_num_t n, int extra, void *data) -{ - int ret = 0; - struct islpci_mgmtframe *response = NULL; - int response_op = PIMFOR_OP_ERROR; - int dlen; - void *cache, *_data = data; - u32 oid; - - BUG_ON(OID_NUM_LAST <= n); - BUG_ON(extra > isl_oid[n].range); - - if (!priv->mib) - /* memory has been freed */ - return -1; - - dlen = isl_oid[n].size; - cache = priv->mib[n]; - cache += (cache ? extra * dlen : 0); - oid = isl_oid[n].oid + extra; - - if (_data == NULL) - /* we are requested to re-set a cached value */ - _data = cache; - else - mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, _data); - /* If we are going to write to the cache, we don't want anyone to read - * it -> acquire write lock. - * Else we could acquire a read lock to be sure we don't bother the - * commit process (which takes a write lock). But I'm not sure if it's - * needed. - */ - if (cache) - down_write(&priv->mib_sem); - - if (islpci_get_state(priv) >= PRV_STATE_READY) { - ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, oid, - _data, dlen, &response); - if (!ret) { - response_op = response->header->operation; - islpci_mgt_release(response); - } - if (ret || response_op == PIMFOR_OP_ERROR) - ret = -EIO; - } else if (!cache) - ret = -EIO; - - if (cache) { - if (!ret && data) - memcpy(cache, _data, dlen); - up_write(&priv->mib_sem); - } - - /* re-set given data to what it was */ - if (data) - mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, data); - - return ret; -} - -/* None of these are cached */ -int -mgt_set_varlen(islpci_private *priv, enum oid_num_t n, void *data, int extra_len) -{ - int ret = 0; - struct islpci_mgmtframe *response; - int response_op = PIMFOR_OP_ERROR; - int dlen; - u32 oid; - - BUG_ON(OID_NUM_LAST <= n); - - dlen = isl_oid[n].size; - oid = isl_oid[n].oid; - - mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, data); - - if (islpci_get_state(priv) >= PRV_STATE_READY) { - ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, oid, - data, dlen + extra_len, &response); - if (!ret) { - response_op = response->header->operation; - islpci_mgt_release(response); - } - if (ret || response_op == PIMFOR_OP_ERROR) - ret = -EIO; - } else - ret = -EIO; - - /* re-set given data to what it was */ - if (data) - mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, data); - - return ret; -} - -int -mgt_get_request(islpci_private *priv, enum oid_num_t n, int extra, void *data, - union oid_res_t *res) -{ - - int ret = -EIO; - int reslen = 0; - struct islpci_mgmtframe *response = NULL; - - int dlen; - void *cache, *_res = NULL; - u32 oid; - - BUG_ON(OID_NUM_LAST <= n); - BUG_ON(extra > isl_oid[n].range); - - res->ptr = NULL; - - if (!priv->mib) - /* memory has been freed */ - return -1; - - dlen = isl_oid[n].size; - cache = priv->mib[n]; - cache += cache ? extra * dlen : 0; - oid = isl_oid[n].oid + extra; - reslen = dlen; - - if (cache) - down_read(&priv->mib_sem); - - if (islpci_get_state(priv) >= PRV_STATE_READY) { - ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET, - oid, data, dlen, &response); - if (ret || !response || - response->header->operation == PIMFOR_OP_ERROR) { - if (response) - islpci_mgt_release(response); - ret = -EIO; - } - if (!ret) { - _res = response->data; - reslen = response->header->length; - } - } else if (cache) { - _res = cache; - ret = 0; - } - if ((isl_oid[n].flags & OID_FLAG_TYPE) == OID_TYPE_U32) - res->u = ret ? 0 : le32_to_cpu(*(u32 *) _res); - else { - res->ptr = kmalloc(reslen, GFP_KERNEL); - BUG_ON(res->ptr == NULL); - if (ret) - memset(res->ptr, 0, reslen); - else { - memcpy(res->ptr, _res, reslen); - mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, - res->ptr); - } - } - if (cache) - up_read(&priv->mib_sem); - - if (response && !ret) - islpci_mgt_release(response); - - if (reslen > isl_oid[n].size) - printk(KERN_DEBUG - "mgt_get_request(0x%x): received data length was bigger " - "than expected (%d > %d). Memory is probably corrupted...", - oid, reslen, isl_oid[n].size); - - return ret; -} - -/* lock outside */ -int -mgt_commit_list(islpci_private *priv, enum oid_num_t *l, int n) -{ - int i, ret = 0; - struct islpci_mgmtframe *response; - - for (i = 0; i < n; i++) { - struct oid_t *t = &(isl_oid[l[i]]); - void *data = priv->mib[l[i]]; - int j = 0; - u32 oid = t->oid; - BUG_ON(data == NULL); - while (j <= t->range) { - int r = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, - oid, data, t->size, - &response); - if (response) { - r |= (response->header->operation == PIMFOR_OP_ERROR); - islpci_mgt_release(response); - } - if (r) - printk(KERN_ERR "%s: mgt_commit_list: failure. " - "oid=%08x err=%d\n", - priv->ndev->name, oid, r); - ret |= r; - j++; - oid++; - data += t->size; - } - } - return ret; -} - -/* Lock outside */ - -void -mgt_set(islpci_private *priv, enum oid_num_t n, void *data) -{ - BUG_ON(OID_NUM_LAST <= n); - BUG_ON(priv->mib[n] == NULL); - - memcpy(priv->mib[n], data, isl_oid[n].size); - mgt_cpu_to_le(isl_oid[n].flags & OID_FLAG_TYPE, priv->mib[n]); -} - -void -mgt_get(islpci_private *priv, enum oid_num_t n, void *res) -{ - BUG_ON(OID_NUM_LAST <= n); - BUG_ON(priv->mib[n] == NULL); - BUG_ON(res == NULL); - - memcpy(res, priv->mib[n], isl_oid[n].size); - mgt_le_to_cpu(isl_oid[n].flags & OID_FLAG_TYPE, res); -} - -/* Commits the cache. Lock outside. */ - -static enum oid_num_t commit_part1[] = { - OID_INL_CONFIG, - OID_INL_MODE, - DOT11_OID_BSSTYPE, - DOT11_OID_CHANNEL, - DOT11_OID_MLMEAUTOLEVEL -}; - -static enum oid_num_t commit_part2[] = { - DOT11_OID_SSID, - DOT11_OID_PSMBUFFER, - DOT11_OID_AUTHENABLE, - DOT11_OID_PRIVACYINVOKED, - DOT11_OID_EXUNENCRYPTED, - DOT11_OID_DEFKEYX, /* MULTIPLE */ - DOT11_OID_DEFKEYID, - DOT11_OID_DOT1XENABLE, - OID_INL_DOT11D_CONFORMANCE, - /* Do not initialize this - fw < 1.0.4.3 rejects it - OID_INL_OUTPUTPOWER, - */ -}; - -/* update the MAC addr. */ -static int -mgt_update_addr(islpci_private *priv) -{ - struct islpci_mgmtframe *res; - int ret; - - ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET, - isl_oid[GEN_OID_MACADDRESS].oid, NULL, - isl_oid[GEN_OID_MACADDRESS].size, &res); - - if ((ret == 0) && res && (res->header->operation != PIMFOR_OP_ERROR)) - memcpy(priv->ndev->dev_addr, res->data, ETH_ALEN); - else - ret = -EIO; - if (res) - islpci_mgt_release(res); - - if (ret) - printk(KERN_ERR "%s: mgt_update_addr: failure\n", priv->ndev->name); - return ret; -} - -int -mgt_commit(islpci_private *priv) -{ - int rvalue; - enum oid_num_t u; - - if (islpci_get_state(priv) < PRV_STATE_INIT) - return 0; - - rvalue = mgt_commit_list(priv, commit_part1, ARRAY_SIZE(commit_part1)); - - if (priv->iw_mode != IW_MODE_MONITOR) - rvalue |= mgt_commit_list(priv, commit_part2, ARRAY_SIZE(commit_part2)); - - u = OID_INL_MODE; - rvalue |= mgt_commit_list(priv, &u, 1); - rvalue |= mgt_update_addr(priv); - - if (rvalue) { - /* some request have failed. The device might be in an - incoherent state. We should reset it ! */ - printk(KERN_DEBUG "%s: mgt_commit: failure\n", priv->ndev->name); - } - return rvalue; -} - -/* The following OIDs need to be "unlatched": - * - * MEDIUMLIMIT,BEACONPERIOD,DTIMPERIOD,ATIMWINDOW,LISTENINTERVAL - * FREQUENCY,EXTENDEDRATES. - * - * The way to do this is to set ESSID. Note though that they may get - * unlatch before though by setting another OID. */ -#if 0 -void -mgt_unlatch_all(islpci_private *priv) -{ - u32 u; - int rvalue = 0; - - if (islpci_get_state(priv) < PRV_STATE_INIT) - return; - - u = DOT11_OID_SSID; - rvalue = mgt_commit_list(priv, &u, 1); - /* Necessary if in MANUAL RUN mode? */ -#if 0 - u = OID_INL_MODE; - rvalue |= mgt_commit_list(priv, &u, 1); - - u = DOT11_OID_MLMEAUTOLEVEL; - rvalue |= mgt_commit_list(priv, &u, 1); - - u = OID_INL_MODE; - rvalue |= mgt_commit_list(priv, &u, 1); -#endif - - if (rvalue) - printk(KERN_DEBUG "%s: Unlatching OIDs failed\n", priv->ndev->name); -} -#endif - -/* This will tell you if you are allowed to answer a mlme(ex) request .*/ - -int -mgt_mlme_answer(islpci_private *priv) -{ - u32 mlmeautolevel; - /* Acquire a read lock because if we are in a mode change, it's - * possible to answer true, while the card is leaving master to managed - * mode. Answering to a mlme in this situation could hang the card. - */ - down_read(&priv->mib_sem); - mlmeautolevel = - le32_to_cpu(*(u32 *) priv->mib[DOT11_OID_MLMEAUTOLEVEL]); - up_read(&priv->mib_sem); - - return ((priv->iw_mode == IW_MODE_MASTER) && - (mlmeautolevel >= DOT11_MLME_INTERMEDIATE)); -} - -enum oid_num_t -mgt_oidtonum(u32 oid) -{ - int i; - - for (i = 0; i < OID_NUM_LAST; i++) - if (isl_oid[i].oid == oid) - return i; - - printk(KERN_DEBUG "looking for an unknown oid 0x%x", oid); - - return OID_NUM_LAST; -} - -int -mgt_response_to_str(enum oid_num_t n, union oid_res_t *r, char *str) -{ - switch (isl_oid[n].flags & OID_FLAG_TYPE) { - case OID_TYPE_U32: - return snprintf(str, PRIV_STR_SIZE, "%u\n", r->u); - case OID_TYPE_BUFFER:{ - struct obj_buffer *buff = r->ptr; - return snprintf(str, PRIV_STR_SIZE, - "size=%u\naddr=0x%X\n", buff->size, - buff->addr); - } - break; - case OID_TYPE_BSS:{ - struct obj_bss *bss = r->ptr; - return snprintf(str, PRIV_STR_SIZE, - "age=%u\nchannel=%u\n" - "capinfo=0x%X\nrates=0x%X\n" - "basic_rates=0x%X\n", bss->age, - bss->channel, bss->capinfo, - bss->rates, bss->basic_rates); - } - break; - case OID_TYPE_BSSLIST:{ - struct obj_bsslist *list = r->ptr; - int i, k; - k = snprintf(str, PRIV_STR_SIZE, "nr=%u\n", list->nr); - for (i = 0; i < list->nr; i++) - k += snprintf(str + k, PRIV_STR_SIZE - k, - "bss[%u] :\nage=%u\nchannel=%u\n" - "capinfo=0x%X\nrates=0x%X\n" - "basic_rates=0x%X\n", - i, list->bsslist[i].age, - list->bsslist[i].channel, - list->bsslist[i].capinfo, - list->bsslist[i].rates, - list->bsslist[i].basic_rates); - return k; - } - break; - case OID_TYPE_FREQUENCIES:{ - struct obj_frequencies *freq = r->ptr; - int i, t; - printk("nr : %u\n", freq->nr); - t = snprintf(str, PRIV_STR_SIZE, "nr=%u\n", freq->nr); - for (i = 0; i < freq->nr; i++) - t += snprintf(str + t, PRIV_STR_SIZE - t, - "mhz[%u]=%u\n", i, freq->mhz[i]); - return t; - } - break; - case OID_TYPE_MLME:{ - struct obj_mlme *mlme = r->ptr; - return snprintf(str, PRIV_STR_SIZE, - "id=0x%X\nstate=0x%X\ncode=0x%X\n", - mlme->id, mlme->state, mlme->code); - } - break; - case OID_TYPE_MLMEEX:{ - struct obj_mlmeex *mlme = r->ptr; - return snprintf(str, PRIV_STR_SIZE, - "id=0x%X\nstate=0x%X\n" - "code=0x%X\nsize=0x%X\n", mlme->id, - mlme->state, mlme->code, mlme->size); - } - break; - case OID_TYPE_ATTACH:{ - struct obj_attachment *attach = r->ptr; - return snprintf(str, PRIV_STR_SIZE, - "id=%d\nsize=%d\n", - attach->id, - attach->size); - } - break; - case OID_TYPE_SSID:{ - struct obj_ssid *ssid = r->ptr; - return snprintf(str, PRIV_STR_SIZE, - "length=%u\noctets=%.*s\n", - ssid->length, ssid->length, - ssid->octets); - } - break; - case OID_TYPE_KEY:{ - struct obj_key *key = r->ptr; - int t, i; - t = snprintf(str, PRIV_STR_SIZE, - "type=0x%X\nlength=0x%X\nkey=0x", - key->type, key->length); - for (i = 0; i < key->length; i++) - t += snprintf(str + t, PRIV_STR_SIZE - t, - "%02X:", key->key[i]); - t += snprintf(str + t, PRIV_STR_SIZE - t, "\n"); - return t; - } - break; - case OID_TYPE_RAW: - case OID_TYPE_ADDR:{ - unsigned char *buff = r->ptr; - int t, i; - t = snprintf(str, PRIV_STR_SIZE, "hex data="); - for (i = 0; i < isl_oid[n].size; i++) - t += snprintf(str + t, PRIV_STR_SIZE - t, - "%02X:", buff[i]); - t += snprintf(str + t, PRIV_STR_SIZE - t, "\n"); - return t; - } - break; - default: - BUG(); - } - return 0; -} diff --git a/drivers/net/wireless/prism54/oid_mgt.h b/drivers/net/wireless/prism54/oid_mgt.h deleted file mode 100644 index cf5141df8474..000000000000 --- a/drivers/net/wireless/prism54/oid_mgt.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2003 Aurelien Alleaume - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -#if !defined(_OID_MGT_H) -#define _OID_MGT_H - -#include "isl_oid.h" -#include "islpci_dev.h" - -extern struct oid_t isl_oid[]; - -int mgt_init(islpci_private *); - -void mgt_clean(islpci_private *); - -/* I don't know where to put these 2 */ -extern const int frequency_list_a[]; -int channel_of_freq(int); - -void mgt_le_to_cpu(int, void *); - -int mgt_set_request(islpci_private *, enum oid_num_t, int, void *); -int mgt_set_varlen(islpci_private *, enum oid_num_t, void *, int); - - -int mgt_get_request(islpci_private *, enum oid_num_t, int, void *, - union oid_res_t *); - -int mgt_commit_list(islpci_private *, enum oid_num_t *, int); - -void mgt_set(islpci_private *, enum oid_num_t, void *); - -void mgt_get(islpci_private *, enum oid_num_t, void *); - -int mgt_commit(islpci_private *); - -int mgt_mlme_answer(islpci_private *); - -enum oid_num_t mgt_oidtonum(u32 oid); - -int mgt_response_to_str(enum oid_num_t, union oid_res_t *, char *); - -#endif /* !defined(_OID_MGT_H) */ -/* EOF */ diff --git a/drivers/net/wireless/prism54/prismcompat.h b/drivers/net/wireless/prism54/prismcompat.h deleted file mode 100644 index bc1401eb4b9d..000000000000 --- a/drivers/net/wireless/prism54/prismcompat.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * (C) 2004 Margit Schubert-While - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * - */ - -/* - * Compatibility header file to aid support of different kernel versions - */ - -#ifdef PRISM54_COMPAT24 -#include "prismcompat24.h" -#else /* PRISM54_COMPAT24 */ - -#ifndef _PRISM_COMPAT_H -#define _PRISM_COMPAT_H - -#include -#include -#include -#include -#include - -#ifndef __iomem -#define __iomem -#endif - -#define PRISM_FW_PDEV &priv->pdev->dev - -#endif /* _PRISM_COMPAT_H */ -#endif /* PRISM54_COMPAT24 */ -- cgit v1.2.3-59-g8ed1b From 621417268569de9616c6c8e8e98dbd3209cd885a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 10:09:24 +0200 Subject: realtek: create separate Kconfig file Add new a Kconfig file and a vendor config for realtek. Also update MAINTAINERS which we missed to do when earlier moving rtlwifi. Signed-off-by: Kalle Valo --- MAINTAINERS | 8 ++++---- drivers/net/wireless/Kconfig | 5 +---- drivers/net/wireless/Makefile | 3 +-- drivers/net/wireless/realtek/Kconfig | 18 ++++++++++++++++++ 4 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 drivers/net/wireless/realtek/Kconfig (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 2c41981e4fdd..a9f7f968f64e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9035,7 +9035,7 @@ L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git S: Orphan -F: drivers/net/wireless/rtl818x/rtl8180/ +F: drivers/net/wireless/realtek/rtl818x/rtl8180/ RTL8187 WIRELESS DRIVER M: Herton Ronaldo Krzesinski @@ -9045,7 +9045,7 @@ L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git S: Maintained -F: drivers/net/wireless/rtl818x/rtl8187/ +F: drivers/net/wireless/realtek/rtl818x/rtl8187/ RTL8192CE WIRELESS DRIVER M: Larry Finger @@ -9054,8 +9054,8 @@ L: linux-wireless@vger.kernel.org W: http://wireless.kernel.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git S: Maintained -F: drivers/net/wireless/rtlwifi/ -F: drivers/net/wireless/rtlwifi/rtl8192ce/ +F: drivers/net/wireless/realtek/rtlwifi/ +F: drivers/net/wireless/realtek/rtlwifi/rtl8192ce/ RTL8XXXU WIRELESS DRIVER (rtl8xxxu) M: Jes Sorensen diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index c92671f8b1c6..28af8b10a66e 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -24,6 +24,7 @@ source "drivers/net/wireless/cisco/Kconfig" source "drivers/net/wireless/intel/Kconfig" source "drivers/net/wireless/intersil/Kconfig" source "drivers/net/wireless/marvell/Kconfig" +source "drivers/net/wireless/realtek/Kconfig" source "drivers/net/wireless/st/Kconfig" source "drivers/net/wireless/zydas/Kconfig" @@ -81,8 +82,6 @@ config USB_NET_RNDIS_WLAN If you choose to build a module, it'll be called rndis_wlan. -source "drivers/net/wireless/realtek/rtl818x/Kconfig" - config MAC80211_HWSIM tristate "Simulated radio testing tool for mac80211" depends on MAC80211 @@ -99,8 +98,6 @@ config MAC80211_HWSIM source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/rt2x00/Kconfig" source "drivers/net/wireless/mediatek/Kconfig" -source "drivers/net/wireless/realtek/rtlwifi/Kconfig" -source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig" source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/rsi/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 679a1a0a048d..12f6c59668a5 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -9,11 +9,10 @@ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/ obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ +obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ -obj-$(CONFIG_WLAN) += realtek/ - # 16-bit wireless PCMCIA client drivers obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o diff --git a/drivers/net/wireless/realtek/Kconfig b/drivers/net/wireless/realtek/Kconfig new file mode 100644 index 000000000000..8a8ba2003964 --- /dev/null +++ b/drivers/net/wireless/realtek/Kconfig @@ -0,0 +1,18 @@ +config WLAN_VENDOR_REALTEK + bool "Realtek devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_REALTEK + +source "drivers/net/wireless/realtek/rtl818x/Kconfig" +source "drivers/net/wireless/realtek/rtlwifi/Kconfig" +source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig" + +endif # WLAN_VENDOR_REALTEK -- cgit v1.2.3-59-g8ed1b From 941a967c98362d6ba9a5d8bb6fdc1b70d7179602 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 10:15:33 +0200 Subject: rsi: add vendor Kconfig entry Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- drivers/net/wireless/Kconfig | 2 +- drivers/net/wireless/Makefile | 3 +-- drivers/net/wireless/rsi/Kconfig | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 28af8b10a66e..518608403bd3 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -25,6 +25,7 @@ source "drivers/net/wireless/intel/Kconfig" source "drivers/net/wireless/intersil/Kconfig" source "drivers/net/wireless/marvell/Kconfig" source "drivers/net/wireless/realtek/Kconfig" +source "drivers/net/wireless/rsi/Kconfig" source "drivers/net/wireless/st/Kconfig" source "drivers/net/wireless/zydas/Kconfig" @@ -99,6 +100,5 @@ source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/rt2x00/Kconfig" source "drivers/net/wireless/mediatek/Kconfig" source "drivers/net/wireless/ti/Kconfig" -source "drivers/net/wireless/rsi/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 12f6c59668a5..d38b6cc338df 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/ obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/ +obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ @@ -28,5 +29,3 @@ obj-$(CONFIG_ATH_CARDS) += ath/ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o obj-$(CONFIG_WL_TI) += ti/ - -obj-$(CONFIG_RSI_91X) += rsi/ diff --git a/drivers/net/wireless/rsi/Kconfig b/drivers/net/wireless/rsi/Kconfig index 35245f994c10..7c5e4ca4e3d0 100644 --- a/drivers/net/wireless/rsi/Kconfig +++ b/drivers/net/wireless/rsi/Kconfig @@ -1,3 +1,16 @@ +config WLAN_VENDOR_RSI + bool "Redpine Signals Inc devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_RSI + config RSI_91X tristate "Redpine Signals Inc 91x WLAN driver support" depends on MAC80211 @@ -28,3 +41,5 @@ config RSI_USB ---help--- This option enables the USB bus support in rsi drivers. Select M (recommended), if you have a RSI 1x1 wireless module. + +endif # WLAN_VENDOR_RSI -- cgit v1.2.3-59-g8ed1b From 33aca94d797d7a8b6b4911ba02060c4fa9a0c47d Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 10:18:44 +0200 Subject: rt2x00: move under ralink vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- drivers/net/wireless/Kconfig | 2 +- drivers/net/wireless/Makefile | 3 +- drivers/net/wireless/ralink/Kconfig | 16 + drivers/net/wireless/ralink/Makefile | 1 + drivers/net/wireless/ralink/rt2x00/Kconfig | 269 + drivers/net/wireless/ralink/rt2x00/Makefile | 25 + drivers/net/wireless/ralink/rt2x00/rt2400pci.c | 1850 +++++ drivers/net/wireless/ralink/rt2x00/rt2400pci.h | 961 +++ drivers/net/wireless/ralink/rt2x00/rt2500pci.c | 2148 ++++++ drivers/net/wireless/ralink/rt2x00/rt2500pci.h | 1235 +++ drivers/net/wireless/ralink/rt2x00/rt2500usb.c | 2001 +++++ drivers/net/wireless/ralink/rt2x00/rt2500usb.h | 855 +++ drivers/net/wireless/ralink/rt2x00/rt2800.h | 2986 ++++++++ drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 8021 ++++++++++++++++++++ drivers/net/wireless/ralink/rt2x00/rt2800lib.h | 232 + drivers/net/wireless/ralink/rt2x00/rt2800mmio.c | 871 +++ drivers/net/wireless/ralink/rt2x00/rt2800mmio.h | 163 + drivers/net/wireless/ralink/rt2x00/rt2800pci.c | 471 ++ drivers/net/wireless/ralink/rt2x00/rt2800pci.h | 42 + drivers/net/wireless/ralink/rt2x00/rt2800soc.c | 260 + drivers/net/wireless/ralink/rt2x00/rt2800usb.c | 1462 ++++ drivers/net/wireless/ralink/rt2x00/rt2800usb.h | 110 + drivers/net/wireless/ralink/rt2x00/rt2x00.h | 1478 ++++ drivers/net/wireless/ralink/rt2x00/rt2x00config.c | 285 + drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c | 256 + drivers/net/wireless/ralink/rt2x00/rt2x00debug.c | 800 ++ drivers/net/wireless/ralink/rt2x00/rt2x00debug.h | 69 + drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 1549 ++++ drivers/net/wireless/ralink/rt2x00/rt2x00dump.h | 127 + .../net/wireless/ralink/rt2x00/rt2x00firmware.c | 129 + drivers/net/wireless/ralink/rt2x00/rt2x00leds.c | 244 + drivers/net/wireless/ralink/rt2x00/rt2x00leds.h | 44 + drivers/net/wireless/ralink/rt2x00/rt2x00lib.h | 468 ++ drivers/net/wireless/ralink/rt2x00/rt2x00link.c | 492 ++ drivers/net/wireless/ralink/rt2x00/rt2x00mac.c | 846 +++ drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c | 212 + drivers/net/wireless/ralink/rt2x00/rt2x00mmio.h | 117 + drivers/net/wireless/ralink/rt2x00/rt2x00pci.c | 221 + drivers/net/wireless/ralink/rt2x00/rt2x00pci.h | 49 + drivers/net/wireless/ralink/rt2x00/rt2x00queue.c | 1290 ++++ drivers/net/wireless/ralink/rt2x00/rt2x00queue.h | 686 ++ drivers/net/wireless/ralink/rt2x00/rt2x00reg.h | 277 + drivers/net/wireless/ralink/rt2x00/rt2x00soc.c | 160 + drivers/net/wireless/ralink/rt2x00/rt2x00soc.h | 40 + drivers/net/wireless/ralink/rt2x00/rt2x00usb.c | 884 +++ drivers/net/wireless/ralink/rt2x00/rt2x00usb.h | 424 ++ drivers/net/wireless/ralink/rt2x00/rt61pci.c | 3111 ++++++++ drivers/net/wireless/ralink/rt2x00/rt61pci.h | 1500 ++++ drivers/net/wireless/ralink/rt2x00/rt73usb.c | 2548 +++++++ drivers/net/wireless/ralink/rt2x00/rt73usb.h | 1079 +++ drivers/net/wireless/rt2x00/Kconfig | 269 - drivers/net/wireless/rt2x00/Makefile | 25 - drivers/net/wireless/rt2x00/rt2400pci.c | 1850 ----- drivers/net/wireless/rt2x00/rt2400pci.h | 961 --- drivers/net/wireless/rt2x00/rt2500pci.c | 2148 ------ drivers/net/wireless/rt2x00/rt2500pci.h | 1235 --- drivers/net/wireless/rt2x00/rt2500usb.c | 2001 ----- drivers/net/wireless/rt2x00/rt2500usb.h | 855 --- drivers/net/wireless/rt2x00/rt2800.h | 2986 -------- drivers/net/wireless/rt2x00/rt2800lib.c | 8021 -------------------- drivers/net/wireless/rt2x00/rt2800lib.h | 232 - drivers/net/wireless/rt2x00/rt2800mmio.c | 871 --- drivers/net/wireless/rt2x00/rt2800mmio.h | 163 - drivers/net/wireless/rt2x00/rt2800pci.c | 471 -- drivers/net/wireless/rt2x00/rt2800pci.h | 42 - drivers/net/wireless/rt2x00/rt2800soc.c | 260 - drivers/net/wireless/rt2x00/rt2800usb.c | 1462 ---- drivers/net/wireless/rt2x00/rt2800usb.h | 110 - drivers/net/wireless/rt2x00/rt2x00.h | 1478 ---- drivers/net/wireless/rt2x00/rt2x00config.c | 285 - drivers/net/wireless/rt2x00/rt2x00crypto.c | 256 - drivers/net/wireless/rt2x00/rt2x00debug.c | 800 -- drivers/net/wireless/rt2x00/rt2x00debug.h | 69 - drivers/net/wireless/rt2x00/rt2x00dev.c | 1549 ---- drivers/net/wireless/rt2x00/rt2x00dump.h | 127 - drivers/net/wireless/rt2x00/rt2x00firmware.c | 129 - drivers/net/wireless/rt2x00/rt2x00leds.c | 244 - drivers/net/wireless/rt2x00/rt2x00leds.h | 44 - drivers/net/wireless/rt2x00/rt2x00lib.h | 468 -- drivers/net/wireless/rt2x00/rt2x00link.c | 492 -- drivers/net/wireless/rt2x00/rt2x00mac.c | 846 --- drivers/net/wireless/rt2x00/rt2x00mmio.c | 212 - drivers/net/wireless/rt2x00/rt2x00mmio.h | 117 - drivers/net/wireless/rt2x00/rt2x00pci.c | 221 - drivers/net/wireless/rt2x00/rt2x00pci.h | 49 - drivers/net/wireless/rt2x00/rt2x00queue.c | 1290 ---- drivers/net/wireless/rt2x00/rt2x00queue.h | 686 -- drivers/net/wireless/rt2x00/rt2x00reg.h | 277 - drivers/net/wireless/rt2x00/rt2x00soc.c | 160 - drivers/net/wireless/rt2x00/rt2x00soc.h | 40 - drivers/net/wireless/rt2x00/rt2x00usb.c | 884 --- drivers/net/wireless/rt2x00/rt2x00usb.h | 424 -- drivers/net/wireless/rt2x00/rt61pci.c | 3111 -------- drivers/net/wireless/rt2x00/rt61pci.h | 1500 ---- drivers/net/wireless/rt2x00/rt73usb.c | 2548 ------- drivers/net/wireless/rt2x00/rt73usb.h | 1079 --- 97 files changed, 43367 insertions(+), 43351 deletions(-) create mode 100644 drivers/net/wireless/ralink/Kconfig create mode 100644 drivers/net/wireless/ralink/Makefile create mode 100644 drivers/net/wireless/ralink/rt2x00/Kconfig create mode 100644 drivers/net/wireless/ralink/rt2x00/Makefile create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2400pci.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2400pci.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2500pci.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2500pci.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2500usb.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2500usb.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2800.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2800lib.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2800lib.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2800mmio.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2800mmio.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2800pci.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2800pci.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2800soc.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2800usb.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2800usb.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00config.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00debug.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00debug.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00dev.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00dump.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00firmware.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00leds.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00leds.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00lib.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00link.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00mac.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00mmio.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00pci.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00pci.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00queue.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00queue.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00reg.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00soc.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00soc.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00usb.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt2x00usb.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt61pci.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt61pci.h create mode 100644 drivers/net/wireless/ralink/rt2x00/rt73usb.c create mode 100644 drivers/net/wireless/ralink/rt2x00/rt73usb.h delete mode 100644 drivers/net/wireless/rt2x00/Kconfig delete mode 100644 drivers/net/wireless/rt2x00/Makefile delete mode 100644 drivers/net/wireless/rt2x00/rt2400pci.c delete mode 100644 drivers/net/wireless/rt2x00/rt2400pci.h delete mode 100644 drivers/net/wireless/rt2x00/rt2500pci.c delete mode 100644 drivers/net/wireless/rt2x00/rt2500pci.h delete mode 100644 drivers/net/wireless/rt2x00/rt2500usb.c delete mode 100644 drivers/net/wireless/rt2x00/rt2500usb.h delete mode 100644 drivers/net/wireless/rt2x00/rt2800.h delete mode 100644 drivers/net/wireless/rt2x00/rt2800lib.c delete mode 100644 drivers/net/wireless/rt2x00/rt2800lib.h delete mode 100644 drivers/net/wireless/rt2x00/rt2800mmio.c delete mode 100644 drivers/net/wireless/rt2x00/rt2800mmio.h delete mode 100644 drivers/net/wireless/rt2x00/rt2800pci.c delete mode 100644 drivers/net/wireless/rt2x00/rt2800pci.h delete mode 100644 drivers/net/wireless/rt2x00/rt2800soc.c delete mode 100644 drivers/net/wireless/rt2x00/rt2800usb.c delete mode 100644 drivers/net/wireless/rt2x00/rt2800usb.h delete mode 100644 drivers/net/wireless/rt2x00/rt2x00.h delete mode 100644 drivers/net/wireless/rt2x00/rt2x00config.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00crypto.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00debug.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00debug.h delete mode 100644 drivers/net/wireless/rt2x00/rt2x00dev.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00dump.h delete mode 100644 drivers/net/wireless/rt2x00/rt2x00firmware.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00leds.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00leds.h delete mode 100644 drivers/net/wireless/rt2x00/rt2x00lib.h delete mode 100644 drivers/net/wireless/rt2x00/rt2x00link.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00mac.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00mmio.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00mmio.h delete mode 100644 drivers/net/wireless/rt2x00/rt2x00pci.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00pci.h delete mode 100644 drivers/net/wireless/rt2x00/rt2x00queue.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00queue.h delete mode 100644 drivers/net/wireless/rt2x00/rt2x00reg.h delete mode 100644 drivers/net/wireless/rt2x00/rt2x00soc.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00soc.h delete mode 100644 drivers/net/wireless/rt2x00/rt2x00usb.c delete mode 100644 drivers/net/wireless/rt2x00/rt2x00usb.h delete mode 100644 drivers/net/wireless/rt2x00/rt61pci.c delete mode 100644 drivers/net/wireless/rt2x00/rt61pci.h delete mode 100644 drivers/net/wireless/rt2x00/rt73usb.c delete mode 100644 drivers/net/wireless/rt2x00/rt73usb.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index a9f7f968f64e..38c049d07966 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8801,7 +8801,7 @@ M: Stanislaw Gruszka M: Helmut Schaa L: linux-wireless@vger.kernel.org S: Maintained -F: drivers/net/wireless/rt2x00/ +F: drivers/net/wireless/ralink/rt2x00/ RAMDISK RAM BLOCK DEVICE DRIVER M: Jens Axboe diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 518608403bd3..c58e244b5079 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -24,6 +24,7 @@ source "drivers/net/wireless/cisco/Kconfig" source "drivers/net/wireless/intel/Kconfig" source "drivers/net/wireless/intersil/Kconfig" source "drivers/net/wireless/marvell/Kconfig" +source "drivers/net/wireless/ralink/Kconfig" source "drivers/net/wireless/realtek/Kconfig" source "drivers/net/wireless/rsi/Kconfig" source "drivers/net/wireless/st/Kconfig" @@ -97,7 +98,6 @@ config MAC80211_HWSIM called mac80211_hwsim. If unsure, say N. source "drivers/net/wireless/ath/Kconfig" -source "drivers/net/wireless/rt2x00/Kconfig" source "drivers/net/wireless/mediatek/Kconfig" source "drivers/net/wireless/ti/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index d38b6cc338df..8ac72fd44d17 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/ obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ +obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/ obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/ obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ @@ -20,8 +21,6 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o -obj-$(CONFIG_RT2X00) += rt2x00/ - obj-$(CONFIG_WL_MEDIATEK) += mediatek/ obj-$(CONFIG_ATH_CARDS) += ath/ diff --git a/drivers/net/wireless/ralink/Kconfig b/drivers/net/wireless/ralink/Kconfig new file mode 100644 index 000000000000..41dbf3130e2b --- /dev/null +++ b/drivers/net/wireless/ralink/Kconfig @@ -0,0 +1,16 @@ +config WLAN_VENDOR_RALINK + bool "Ralink devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_RALINK + +source "drivers/net/wireless/ralink/rt2x00/Kconfig" + +endif # WLAN_VENDOR_RALINK diff --git a/drivers/net/wireless/ralink/Makefile b/drivers/net/wireless/ralink/Makefile new file mode 100644 index 000000000000..f84c0a2e4f4d --- /dev/null +++ b/drivers/net/wireless/ralink/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_RT2X00) += rt2x00/ diff --git a/drivers/net/wireless/ralink/rt2x00/Kconfig b/drivers/net/wireless/ralink/rt2x00/Kconfig new file mode 100644 index 000000000000..de62f5dcb62f --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/Kconfig @@ -0,0 +1,269 @@ +menuconfig RT2X00 + tristate "Ralink driver support" + depends on MAC80211 && HAS_DMA + ---help--- + This will enable the support for the Ralink drivers, + developed in the rt2x00 project . + + These drivers make use of the mac80211 stack. + + When building one of the individual drivers, the rt2x00 library + will also be created. That library (when the driver is built as + a module) will be called rt2x00lib. + + Additionally PCI and USB libraries will also be build depending + on the types of drivers being selected, these libraries will be + called rt2x00pci and rt2x00usb. + +if RT2X00 + +config RT2400PCI + tristate "Ralink rt2400 (PCI/PCMCIA) support" + depends on PCI + select RT2X00_LIB_MMIO + select RT2X00_LIB_PCI + select EEPROM_93CX6 + ---help--- + This adds support for rt2400 wireless chipset family. + Supported chips: RT2460. + + When compiled as a module, this driver will be called rt2400pci. + +config RT2500PCI + tristate "Ralink rt2500 (PCI/PCMCIA) support" + depends on PCI + select RT2X00_LIB_MMIO + select RT2X00_LIB_PCI + select EEPROM_93CX6 + ---help--- + This adds support for rt2500 wireless chipset family. + Supported chips: RT2560. + + When compiled as a module, this driver will be called rt2500pci. + +config RT61PCI + tristate "Ralink rt2501/rt61 (PCI/PCMCIA) support" + depends on PCI + select RT2X00_LIB_PCI + select RT2X00_LIB_MMIO + select RT2X00_LIB_FIRMWARE + select RT2X00_LIB_CRYPTO + select CRC_ITU_T + select EEPROM_93CX6 + ---help--- + This adds support for rt2501 wireless chipset family. + Supported chips: RT2561, RT2561S & RT2661. + + When compiled as a module, this driver will be called rt61pci. + +config RT2800PCI + tristate "Ralink rt27xx/rt28xx/rt30xx (PCI/PCIe/PCMCIA) support" + depends on PCI + select RT2800_LIB + select RT2800_LIB_MMIO + select RT2X00_LIB_MMIO + select RT2X00_LIB_PCI + select RT2X00_LIB_FIRMWARE + select RT2X00_LIB_CRYPTO + select CRC_CCITT + select EEPROM_93CX6 + ---help--- + This adds support for rt27xx/rt28xx/rt30xx wireless chipset family. + Supported chips: RT2760, RT2790, RT2860, RT2880, RT2890, RT3052, + RT3090, RT3091 & RT3092 + + When compiled as a module, this driver will be called "rt2800pci.ko". + +if RT2800PCI + +config RT2800PCI_RT33XX + bool "rt2800pci - Include support for rt33xx devices" + default y + ---help--- + This adds support for rt33xx wireless chipset family to the + rt2800pci driver. + Supported chips: RT3390 + +config RT2800PCI_RT35XX + bool "rt2800pci - Include support for rt35xx devices (EXPERIMENTAL)" + default y + ---help--- + This adds support for rt35xx wireless chipset family to the + rt2800pci driver. + Supported chips: RT3060, RT3062, RT3562, RT3592 + + +config RT2800PCI_RT53XX + bool "rt2800pci - Include support for rt53xx devices (EXPERIMENTAL)" + default y + ---help--- + This adds support for rt53xx wireless chipset family to the + rt2800pci driver. + Supported chips: RT5390 + +config RT2800PCI_RT3290 + bool "rt2800pci - Include support for rt3290 devices (EXPERIMENTAL)" + default y + ---help--- + This adds support for rt3290 wireless chipset family to the + rt2800pci driver. + Supported chips: RT3290 +endif + +config RT2500USB + tristate "Ralink rt2500 (USB) support" + depends on USB + select RT2X00_LIB_USB + select RT2X00_LIB_CRYPTO + ---help--- + This adds support for rt2500 wireless chipset family. + Supported chips: RT2571 & RT2572. + + When compiled as a module, this driver will be called rt2500usb. + +config RT73USB + tristate "Ralink rt2501/rt73 (USB) support" + depends on USB + select RT2X00_LIB_USB + select RT2X00_LIB_FIRMWARE + select RT2X00_LIB_CRYPTO + select CRC_ITU_T + ---help--- + This adds support for rt2501 wireless chipset family. + Supported chips: RT2571W, RT2573 & RT2671. + + When compiled as a module, this driver will be called rt73usb. + +config RT2800USB + tristate "Ralink rt27xx/rt28xx/rt30xx (USB) support" + depends on USB + select RT2800_LIB + select RT2X00_LIB_USB + select RT2X00_LIB_FIRMWARE + select RT2X00_LIB_CRYPTO + select CRC_CCITT + ---help--- + This adds support for rt27xx/rt28xx/rt30xx wireless chipset family. + Supported chips: RT2770, RT2870 & RT3070, RT3071 & RT3072 + + When compiled as a module, this driver will be called "rt2800usb.ko". + +if RT2800USB + +config RT2800USB_RT33XX + bool "rt2800usb - Include support for rt33xx devices" + default y + ---help--- + This adds support for rt33xx wireless chipset family to the + rt2800usb driver. + Supported chips: RT3370 + +config RT2800USB_RT35XX + bool "rt2800usb - Include support for rt35xx devices (EXPERIMENTAL)" + default y + ---help--- + This adds support for rt35xx wireless chipset family to the + rt2800usb driver. + Supported chips: RT3572 + +config RT2800USB_RT3573 + bool "rt2800usb - Include support for rt3573 devices (EXPERIMENTAL)" + ---help--- + This enables support for RT3573 chipset based wireless USB devices + in the rt2800usb driver. + +config RT2800USB_RT53XX + bool "rt2800usb - Include support for rt53xx devices (EXPERIMENTAL)" + ---help--- + This adds support for rt53xx wireless chipset family to the + rt2800usb driver. + Supported chips: RT5370 + +config RT2800USB_RT55XX + bool "rt2800usb - Include support for rt55xx devices (EXPERIMENTAL)" + ---help--- + This adds support for rt55xx wireless chipset family to the + rt2800usb driver. + Supported chips: RT5572 + +config RT2800USB_UNKNOWN + bool "rt2800usb - Include support for unknown (USB) devices" + default n + ---help--- + This adds support for rt2800usb devices that are known to + have a rt28xx family compatible chipset, but for which the exact + chipset is unknown. + + Support status for these devices is unknown, and enabling these + devices may or may not work. + +endif + +config RT2800SOC + tristate "Ralink WiSoC support" + depends on SOC_RT288X || SOC_RT305X + select RT2X00_LIB_SOC + select RT2X00_LIB_MMIO + select RT2X00_LIB_CRYPTO + select RT2X00_LIB_FIRMWARE + select RT2800_LIB + select RT2800_LIB_MMIO + ---help--- + This adds support for Ralink WiSoC devices. + Supported chips: RT2880, RT3050, RT3052, RT3350, RT3352. + + When compiled as a module, this driver will be called rt2800soc. + + +config RT2800_LIB + tristate + +config RT2800_LIB_MMIO + tristate + select RT2X00_LIB_MMIO + select RT2800_LIB + +config RT2X00_LIB_MMIO + tristate + +config RT2X00_LIB_PCI + tristate + select RT2X00_LIB + +config RT2X00_LIB_SOC + tristate + select RT2X00_LIB + +config RT2X00_LIB_USB + tristate + select RT2X00_LIB + +config RT2X00_LIB + tristate + +config RT2X00_LIB_FIRMWARE + bool + select FW_LOADER + +config RT2X00_LIB_CRYPTO + bool + +config RT2X00_LIB_LEDS + bool + default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n) + +config RT2X00_LIB_DEBUGFS + bool "Ralink debugfs support" + depends on RT2X00_LIB && MAC80211_DEBUGFS + ---help--- + Enable creation of debugfs files for the rt2x00 drivers. + These debugfs files support both reading and writing of the + most important register types of the rt2x00 hardware. + +config RT2X00_DEBUG + bool "Ralink debug output" + depends on RT2X00_LIB + ---help--- + Enable debugging output for all rt2x00 modules + +endif diff --git a/drivers/net/wireless/ralink/rt2x00/Makefile b/drivers/net/wireless/ralink/rt2x00/Makefile new file mode 100644 index 000000000000..24a66015a495 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/Makefile @@ -0,0 +1,25 @@ +rt2x00lib-y += rt2x00dev.o +rt2x00lib-y += rt2x00mac.o +rt2x00lib-y += rt2x00config.o +rt2x00lib-y += rt2x00queue.o +rt2x00lib-y += rt2x00link.o +rt2x00lib-$(CONFIG_RT2X00_LIB_DEBUGFS) += rt2x00debug.o +rt2x00lib-$(CONFIG_RT2X00_LIB_CRYPTO) += rt2x00crypto.o +rt2x00lib-$(CONFIG_RT2X00_LIB_FIRMWARE) += rt2x00firmware.o +rt2x00lib-$(CONFIG_RT2X00_LIB_LEDS) += rt2x00leds.o + +obj-$(CONFIG_RT2X00_LIB) += rt2x00lib.o +obj-$(CONFIG_RT2X00_LIB_MMIO) += rt2x00mmio.o +obj-$(CONFIG_RT2X00_LIB_PCI) += rt2x00pci.o +obj-$(CONFIG_RT2X00_LIB_SOC) += rt2x00soc.o +obj-$(CONFIG_RT2X00_LIB_USB) += rt2x00usb.o +obj-$(CONFIG_RT2800_LIB) += rt2800lib.o +obj-$(CONFIG_RT2800_LIB_MMIO) += rt2800mmio.o +obj-$(CONFIG_RT2400PCI) += rt2400pci.o +obj-$(CONFIG_RT2500PCI) += rt2500pci.o +obj-$(CONFIG_RT61PCI) += rt61pci.o +obj-$(CONFIG_RT2800PCI) += rt2800pci.o +obj-$(CONFIG_RT2500USB) += rt2500usb.o +obj-$(CONFIG_RT73USB) += rt73usb.o +obj-$(CONFIG_RT2800USB) += rt2800usb.o +obj-$(CONFIG_RT2800SOC) += rt2800soc.o diff --git a/drivers/net/wireless/ralink/rt2x00/rt2400pci.c b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c new file mode 100644 index 000000000000..9a3966cd6fbe --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c @@ -0,0 +1,1850 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2400pci + Abstract: rt2400pci device specific routines. + Supported chipsets: RT2460. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2x00pci.h" +#include "rt2400pci.h" + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2x00mmio_register_read and rt2x00mmio_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers BBPCSR and RFCSR to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attempt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + */ +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), RFCSR, RFCSR_BUSY, (__reg)) + +static void rt2400pci_bbp_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBPCSR_VALUE, value); + rt2x00_set_field32(®, BBPCSR_REGNUM, word); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); + + rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2400pci_bbp_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u8 *value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBPCSR_REGNUM, word); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); + + rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); + + WAIT_FOR_BBP(rt2x00dev, ®); + } + + *value = rt2x00_get_field32(reg, BBPCSR_VALUE); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2400pci_rf_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u32 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RFCSR_VALUE, value); + rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); + rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); + rt2x00_set_field32(®, RFCSR_BUSY, 1); + + rt2x00mmio_register_write(rt2x00dev, RFCSR, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2400pci_eepromregister_read(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, CSR21, ®); + + eeprom->reg_data_in = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_IN); + eeprom->reg_data_out = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_OUT); + eeprom->reg_data_clock = + !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_CLOCK); + eeprom->reg_chip_select = + !!rt2x00_get_field32(reg, CSR21_EEPROM_CHIP_SELECT); +} + +static void rt2400pci_eepromregister_write(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg = 0; + + rt2x00_set_field32(®, CSR21_EEPROM_DATA_IN, !!eeprom->reg_data_in); + rt2x00_set_field32(®, CSR21_EEPROM_DATA_OUT, !!eeprom->reg_data_out); + rt2x00_set_field32(®, CSR21_EEPROM_DATA_CLOCK, + !!eeprom->reg_data_clock); + rt2x00_set_field32(®, CSR21_EEPROM_CHIP_SELECT, + !!eeprom->reg_chip_select); + + rt2x00mmio_register_write(rt2x00dev, CSR21, reg); +} + +#ifdef CONFIG_RT2X00_LIB_DEBUGFS +static const struct rt2x00debug rt2400pci_rt2x00debug = { + .owner = THIS_MODULE, + .csr = { + .read = rt2x00mmio_register_read, + .write = rt2x00mmio_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, + .word_size = sizeof(u32), + .word_count = CSR_REG_SIZE / sizeof(u32), + }, + .eeprom = { + .read = rt2x00_eeprom_read, + .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .bbp = { + .read = rt2400pci_bbp_read, + .write = rt2400pci_bbp_write, + .word_base = BBP_BASE, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, + .rf = { + .read = rt2x00_rf_read, + .write = rt2400pci_rf_write, + .word_base = RF_BASE, + .word_size = sizeof(u32), + .word_count = RF_SIZE / sizeof(u32), + }, +}; +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ + +static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); + return rt2x00_get_field32(reg, GPIOCSR_VAL0); +} + +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt2400pci_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + u32 reg; + + rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); + + if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) + rt2x00_set_field32(®, LEDCSR_LINK, enabled); + else if (led->type == LED_TYPE_ACTIVITY) + rt2x00_set_field32(®, LEDCSR_ACTIVITY, enabled); + + rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); +} + +static int rt2400pci_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + u32 reg; + + rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); + rt2x00_set_field32(®, LEDCSR_ON_PERIOD, *delay_on); + rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, *delay_off); + rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); + + return 0; +} + +static void rt2400pci_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + enum led_type type) +{ + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt2400pci_brightness_set; + led->led_dev.blink_set = rt2400pci_blink_set; + led->flags = LED_INITIALIZED; +} +#endif /* CONFIG_RT2X00_LIB_LEDS */ + +/* + * Configuration handlers. + */ +static void rt2400pci_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) +{ + u32 reg; + + /* + * Start configuration steps. + * Note that the version error will always be dropped + * since there is no filter for it at this time. + */ + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DROP_CRC, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, 1); + rt2x00_set_field32(®, RXCSR0_DROP_TODS, + !rt2x00dev->intf_ap_count); + rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); +} + +static void rt2400pci_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags) +{ + unsigned int bcn_preload; + u32 reg; + + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable beacon config + */ + bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); + rt2x00mmio_register_read(rt2x00dev, BCNCSR1, ®); + rt2x00_set_field32(®, BCNCSR1_PRELOAD, bcn_preload); + rt2x00mmio_register_write(rt2x00dev, BCNCSR1, reg); + + /* + * Enable synchronisation. + */ + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_SYNC, conf->sync); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + } + + if (flags & CONFIG_UPDATE_MAC) + rt2x00mmio_register_multiwrite(rt2x00dev, CSR3, + conf->mac, sizeof(conf->mac)); + + if (flags & CONFIG_UPDATE_BSSID) + rt2x00mmio_register_multiwrite(rt2x00dev, CSR5, + conf->bssid, + sizeof(conf->bssid)); +} + +static void rt2400pci_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed) +{ + int preamble_mask; + u32 reg; + + /* + * When short preamble is enabled, we should set bit 0x08 + */ + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + preamble_mask = erp->short_preamble << 3; + + rt2x00mmio_register_read(rt2x00dev, TXCSR1, ®); + rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, 0x1ff); + rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, 0x13a); + rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR1, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR2, ®); + rt2x00_set_field32(®, ARCSR2_SIGNAL, 0x00); + rt2x00_set_field32(®, ARCSR2_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 10)); + rt2x00mmio_register_write(rt2x00dev, ARCSR2, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR3, ®); + rt2x00_set_field32(®, ARCSR3_SIGNAL, 0x01 | preamble_mask); + rt2x00_set_field32(®, ARCSR3_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 20)); + rt2x00mmio_register_write(rt2x00dev, ARCSR3, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR4, ®); + rt2x00_set_field32(®, ARCSR4_SIGNAL, 0x02 | preamble_mask); + rt2x00_set_field32(®, ARCSR4_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 55)); + rt2x00mmio_register_write(rt2x00dev, ARCSR4, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR5, ®); + rt2x00_set_field32(®, ARCSR5_SIGNAL, 0x03 | preamble_mask); + rt2x00_set_field32(®, ARCSR5_SERVICE, 0x84); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 110)); + rt2x00mmio_register_write(rt2x00dev, ARCSR5, reg); + } + + if (changed & BSS_CHANGED_BASIC_RATES) + rt2x00mmio_register_write(rt2x00dev, ARCSR1, erp->basic_rates); + + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_SLOT_TIME, erp->slot_time); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR18, ®); + rt2x00_set_field32(®, CSR18_SIFS, erp->sifs); + rt2x00_set_field32(®, CSR18_PIFS, erp->pifs); + rt2x00mmio_register_write(rt2x00dev, CSR18, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR19, ®); + rt2x00_set_field32(®, CSR19_DIFS, erp->difs); + rt2x00_set_field32(®, CSR19_EIFS, erp->eifs); + rt2x00mmio_register_write(rt2x00dev, CSR19, reg); + } + + if (changed & BSS_CHANGED_BEACON_INT) { + rt2x00mmio_register_read(rt2x00dev, CSR12, ®); + rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, + erp->beacon_int * 16); + rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, + erp->beacon_int * 16); + rt2x00mmio_register_write(rt2x00dev, CSR12, reg); + } +} + +static void rt2400pci_config_ant(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) +{ + u8 r1; + u8 r4; + + /* + * We should never come here because rt2x00lib is supposed + * to catch this and send us the correct antenna explicitely. + */ + BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || + ant->tx == ANTENNA_SW_DIVERSITY); + + rt2400pci_bbp_read(rt2x00dev, 4, &r4); + rt2400pci_bbp_read(rt2x00dev, 1, &r1); + + /* + * Configure the TX antenna. + */ + switch (ant->tx) { + case ANTENNA_HW_DIVERSITY: + rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 1); + break; + case ANTENNA_A: + rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 0); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 2); + break; + } + + /* + * Configure the RX antenna. + */ + switch (ant->rx) { + case ANTENNA_HW_DIVERSITY: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 1); + break; + case ANTENNA_A: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 0); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 2); + break; + } + + rt2400pci_bbp_write(rt2x00dev, 4, r4); + rt2400pci_bbp_write(rt2x00dev, 1, r1); +} + +static void rt2400pci_config_channel(struct rt2x00_dev *rt2x00dev, + struct rf_channel *rf) +{ + /* + * Switch on tuning bits. + */ + rt2x00_set_field32(&rf->rf1, RF1_TUNER, 1); + rt2x00_set_field32(&rf->rf3, RF3_TUNER, 1); + + rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); + rt2400pci_rf_write(rt2x00dev, 2, rf->rf2); + rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); + + /* + * RF2420 chipset don't need any additional actions. + */ + if (rt2x00_rf(rt2x00dev, RF2420)) + return; + + /* + * For the RT2421 chipsets we need to write an invalid + * reference clock rate to activate auto_tune. + * After that we set the value back to the correct channel. + */ + rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); + rt2400pci_rf_write(rt2x00dev, 2, 0x000c2a32); + rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); + + msleep(1); + + rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); + rt2400pci_rf_write(rt2x00dev, 2, rf->rf2); + rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); + + msleep(1); + + /* + * Switch off tuning bits. + */ + rt2x00_set_field32(&rf->rf1, RF1_TUNER, 0); + rt2x00_set_field32(&rf->rf3, RF3_TUNER, 0); + + rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); + rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); + + /* + * Clear false CRC during channel switch. + */ + rt2x00mmio_register_read(rt2x00dev, CNT0, &rf->rf1); +} + +static void rt2400pci_config_txpower(struct rt2x00_dev *rt2x00dev, int txpower) +{ + rt2400pci_bbp_write(rt2x00dev, 3, TXPOWER_TO_DEV(txpower)); +} + +static void rt2400pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_LONG_RETRY, + libconf->conf->long_frame_max_tx_count); + rt2x00_set_field32(®, CSR11_SHORT_RETRY, + libconf->conf->short_frame_max_tx_count); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); +} + +static void rt2400pci_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; + u32 reg; + + if (state == STATE_SLEEP) { + rt2x00mmio_register_read(rt2x00dev, CSR20, ®); + rt2x00_set_field32(®, CSR20_DELAY_AFTER_TBCN, + (rt2x00dev->beacon_int - 20) * 16); + rt2x00_set_field32(®, CSR20_TBCN_BEFORE_WAKEUP, + libconf->conf->listen_interval - 1); + + /* We must first disable autowake before it can be enabled */ + rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); + + rt2x00_set_field32(®, CSR20_AUTOWAKE, 1); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); + } else { + rt2x00mmio_register_read(rt2x00dev, CSR20, ®); + rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); + } + + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); +} + +static void rt2400pci_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags) +{ + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) + rt2400pci_config_channel(rt2x00dev, &libconf->rf); + if (flags & IEEE80211_CONF_CHANGE_POWER) + rt2400pci_config_txpower(rt2x00dev, + libconf->conf->power_level); + if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + rt2400pci_config_retry_limit(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt2400pci_config_ps(rt2x00dev, libconf); +} + +static void rt2400pci_config_cw(struct rt2x00_dev *rt2x00dev, + const int cw_min, const int cw_max) +{ + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_CWMIN, cw_min); + rt2x00_set_field32(®, CSR11_CWMAX, cw_max); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); +} + +/* + * Link tuning + */ +static void rt2400pci_link_stats(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) +{ + u32 reg; + u8 bbp; + + /* + * Update FCS error count from register. + */ + rt2x00mmio_register_read(rt2x00dev, CNT0, ®); + qual->rx_failed = rt2x00_get_field32(reg, CNT0_FCS_ERROR); + + /* + * Update False CCA count from register. + */ + rt2400pci_bbp_read(rt2x00dev, 39, &bbp); + qual->false_cca = bbp; +} + +static inline void rt2400pci_set_vgc(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, u8 vgc_level) +{ + if (qual->vgc_level_reg != vgc_level) { + rt2400pci_bbp_write(rt2x00dev, 13, vgc_level); + qual->vgc_level = vgc_level; + qual->vgc_level_reg = vgc_level; + } +} + +static void rt2400pci_reset_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) +{ + rt2400pci_set_vgc(rt2x00dev, qual, 0x08); +} + +static void rt2400pci_link_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, const u32 count) +{ + /* + * The link tuner should not run longer then 60 seconds, + * and should run once every 2 seconds. + */ + if (count > 60 || !(count & 1)) + return; + + /* + * Base r13 link tuning on the false cca count. + */ + if ((qual->false_cca > 512) && (qual->vgc_level < 0x20)) + rt2400pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level); + else if ((qual->false_cca < 100) && (qual->vgc_level > 0x08)) + rt2400pci_set_vgc(rt2x00dev, qual, --qual->vgc_level); +} + +/* + * Queue handlers. + */ +static void rt2400pci_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 0); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); + rt2x00_set_field32(®, CSR14_TBCN, 1); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + break; + default: + break; + } +} + +static void rt2400pci_kick_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_PRIO, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_AC_VI: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_TX, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_ATIM: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_ATIM, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + default: + break; + } +} + +static void rt2400pci_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_ATIM: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_ABORT, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 1); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); + rt2x00_set_field32(®, CSR14_TBCN, 0); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + + /* + * Wait for possibly running tbtt tasklets. + */ + tasklet_kill(&rt2x00dev->tbtt_tasklet); + break; + default: + break; + } +} + +/* + * Initialization functions. + */ +static bool rt2400pci_get_entry_state(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 0, &word); + + return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); + + return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + rt2x00_get_field32(word, TXD_W0_VALID)); + } +} + +static void rt2400pci_clear_entry(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 2, &word); + rt2x00_set_field32(&word, RXD_W2_BUFFER_LENGTH, entry->skb->len); + rt2x00_desc_write(entry_priv->desc, 2, word); + + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); + rt2x00_desc_write(entry_priv->desc, 1, word); + + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(entry_priv->desc, 0, word); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); + rt2x00_desc_write(entry_priv->desc, 0, word); + } +} + +static int rt2400pci_init_queues(struct rt2x00_dev *rt2x00dev) +{ + struct queue_entry_priv_mmio *entry_priv; + u32 reg; + + /* + * Initialize registers. + */ + rt2x00mmio_register_read(rt2x00dev, TXCSR2, ®); + rt2x00_set_field32(®, TXCSR2_TXD_SIZE, rt2x00dev->tx[0].desc_size); + rt2x00_set_field32(®, TXCSR2_NUM_TXD, rt2x00dev->tx[1].limit); + rt2x00_set_field32(®, TXCSR2_NUM_ATIM, rt2x00dev->atim->limit); + rt2x00_set_field32(®, TXCSR2_NUM_PRIO, rt2x00dev->tx[0].limit); + rt2x00mmio_register_write(rt2x00dev, TXCSR2, reg); + + entry_priv = rt2x00dev->tx[1].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR3, ®); + rt2x00_set_field32(®, TXCSR3_TX_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR3, reg); + + entry_priv = rt2x00dev->tx[0].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR5, ®); + rt2x00_set_field32(®, TXCSR5_PRIO_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR5, reg); + + entry_priv = rt2x00dev->atim->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR4, ®); + rt2x00_set_field32(®, TXCSR4_ATIM_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR4, reg); + + entry_priv = rt2x00dev->bcn->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR6, ®); + rt2x00_set_field32(®, TXCSR6_BEACON_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR6, reg); + + rt2x00mmio_register_read(rt2x00dev, RXCSR1, ®); + rt2x00_set_field32(®, RXCSR1_RXD_SIZE, rt2x00dev->rx->desc_size); + rt2x00_set_field32(®, RXCSR1_NUM_RXD, rt2x00dev->rx->limit); + rt2x00mmio_register_write(rt2x00dev, RXCSR1, reg); + + entry_priv = rt2x00dev->rx->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, RXCSR2, ®); + rt2x00_set_field32(®, RXCSR2_RX_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, RXCSR2, reg); + + return 0; +} + +static int rt2400pci_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00mmio_register_write(rt2x00dev, PSCSR0, 0x00020002); + rt2x00mmio_register_write(rt2x00dev, PSCSR1, 0x00000002); + rt2x00mmio_register_write(rt2x00dev, PSCSR2, 0x00023f20); + rt2x00mmio_register_write(rt2x00dev, PSCSR3, 0x00000002); + + rt2x00mmio_register_read(rt2x00dev, TIMECSR, ®); + rt2x00_set_field32(®, TIMECSR_US_COUNT, 33); + rt2x00_set_field32(®, TIMECSR_US_64_COUNT, 63); + rt2x00_set_field32(®, TIMECSR_BEACON_EXPECT, 0); + rt2x00mmio_register_write(rt2x00dev, TIMECSR, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR9, ®); + rt2x00_set_field32(®, CSR9_MAX_FRAME_UNIT, + (rt2x00dev->rx->data_size / 128)); + rt2x00mmio_register_write(rt2x00dev, CSR9, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); + rt2x00_set_field32(®, CSR14_TSF_SYNC, 0); + rt2x00_set_field32(®, CSR14_TBCN, 0); + rt2x00_set_field32(®, CSR14_TCFP, 0); + rt2x00_set_field32(®, CSR14_TATIMW, 0); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00_set_field32(®, CSR14_CFP_COUNT_PRELOAD, 0); + rt2x00_set_field32(®, CSR14_TBCM_PRELOAD, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + + rt2x00mmio_register_write(rt2x00dev, CNT3, 0x3f080000); + + rt2x00mmio_register_read(rt2x00dev, ARCSR0, ®); + rt2x00_set_field32(®, ARCSR0_AR_BBP_DATA0, 133); + rt2x00_set_field32(®, ARCSR0_AR_BBP_ID0, 134); + rt2x00_set_field32(®, ARCSR0_AR_BBP_DATA1, 136); + rt2x00_set_field32(®, ARCSR0_AR_BBP_ID1, 135); + rt2x00mmio_register_write(rt2x00dev, ARCSR0, reg); + + rt2x00mmio_register_read(rt2x00dev, RXCSR3, ®); + rt2x00_set_field32(®, RXCSR3_BBP_ID0, 3); /* Tx power.*/ + rt2x00_set_field32(®, RXCSR3_BBP_ID0_VALID, 1); + rt2x00_set_field32(®, RXCSR3_BBP_ID1, 32); /* Signal */ + rt2x00_set_field32(®, RXCSR3_BBP_ID1_VALID, 1); + rt2x00_set_field32(®, RXCSR3_BBP_ID2, 36); /* Rssi */ + rt2x00_set_field32(®, RXCSR3_BBP_ID2_VALID, 1); + rt2x00mmio_register_write(rt2x00dev, RXCSR3, reg); + + rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); + + if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) + return -EBUSY; + + rt2x00mmio_register_write(rt2x00dev, MACCSR0, 0x00217223); + rt2x00mmio_register_write(rt2x00dev, MACCSR1, 0x00235518); + + rt2x00mmio_register_read(rt2x00dev, MACCSR2, ®); + rt2x00_set_field32(®, MACCSR2_DELAY, 64); + rt2x00mmio_register_write(rt2x00dev, MACCSR2, reg); + + rt2x00mmio_register_read(rt2x00dev, RALINKCSR, ®); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA0, 17); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID0, 154); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA1, 0); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID1, 154); + rt2x00mmio_register_write(rt2x00dev, RALINKCSR, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR1, ®); + rt2x00_set_field32(®, CSR1_SOFT_RESET, 1); + rt2x00_set_field32(®, CSR1_BBP_RESET, 0); + rt2x00_set_field32(®, CSR1_HOST_READY, 0); + rt2x00mmio_register_write(rt2x00dev, CSR1, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR1, ®); + rt2x00_set_field32(®, CSR1_SOFT_RESET, 0); + rt2x00_set_field32(®, CSR1_HOST_READY, 1); + rt2x00mmio_register_write(rt2x00dev, CSR1, reg); + + /* + * We must clear the FCS and FIFO error count. + * These registers are cleared on read, + * so we may pass a useless variable to store the value. + */ + rt2x00mmio_register_read(rt2x00dev, CNT0, ®); + rt2x00mmio_register_read(rt2x00dev, CNT4, ®); + + return 0; +} + +static int rt2400pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u8 value; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2400pci_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + return 0; + udelay(REGISTER_BUSY_DELAY); + } + + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); + return -EACCES; +} + +static int rt2400pci_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + if (unlikely(rt2400pci_wait_bbp_ready(rt2x00dev))) + return -EACCES; + + rt2400pci_bbp_write(rt2x00dev, 1, 0x00); + rt2400pci_bbp_write(rt2x00dev, 3, 0x27); + rt2400pci_bbp_write(rt2x00dev, 4, 0x08); + rt2400pci_bbp_write(rt2x00dev, 10, 0x0f); + rt2400pci_bbp_write(rt2x00dev, 15, 0x72); + rt2400pci_bbp_write(rt2x00dev, 16, 0x74); + rt2400pci_bbp_write(rt2x00dev, 17, 0x20); + rt2400pci_bbp_write(rt2x00dev, 18, 0x72); + rt2400pci_bbp_write(rt2x00dev, 19, 0x0b); + rt2400pci_bbp_write(rt2x00dev, 20, 0x00); + rt2400pci_bbp_write(rt2x00dev, 28, 0x11); + rt2400pci_bbp_write(rt2x00dev, 29, 0x04); + rt2400pci_bbp_write(rt2x00dev, 30, 0x21); + rt2400pci_bbp_write(rt2x00dev, 31, 0x00); + + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + rt2400pci_bbp_write(rt2x00dev, reg_id, value); + } + } + + return 0; +} + +/* + * Device state switch handlers. + */ +static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int mask = (state == STATE_RADIO_IRQ_OFF); + u32 reg; + unsigned long flags; + + /* + * When interrupts are being enabled, the interrupt registers + * should clear the register to assure a clean state. + */ + if (state == STATE_RADIO_IRQ_ON) { + rt2x00mmio_register_read(rt2x00dev, CSR7, ®); + rt2x00mmio_register_write(rt2x00dev, CSR7, reg); + } + + /* + * Only toggle the interrupts bits we are going to use. + * Non-checked interrupt bits are disabled by default. + */ + spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); + rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); + rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, mask); + rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); + rt2x00_set_field32(®, CSR8_RXDONE, mask); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); + + if (state == STATE_RADIO_IRQ_OFF) { + /* + * Ensure that all tasklets are finished before + * disabling the interrupts. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + } +} + +static int rt2400pci_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + /* + * Initialize all registers. + */ + if (unlikely(rt2400pci_init_queues(rt2x00dev) || + rt2400pci_init_registers(rt2x00dev) || + rt2400pci_init_bbp(rt2x00dev))) + return -EIO; + + return 0; +} + +static void rt2400pci_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + /* + * Disable power + */ + rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0); +} + +static int rt2400pci_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u32 reg, reg2; + unsigned int i; + char put_to_sleep; + char bbp_state; + char rf_state; + + put_to_sleep = (state != STATE_AWAKE); + + rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®); + rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1); + rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state); + rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state); + rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep); + rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); + + /* + * Device is not guaranteed to be in the requested state yet. + * We must wait until the register indicates that the + * device has entered the correct state. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®2); + bbp_state = rt2x00_get_field32(reg2, PWRCSR1_BBP_CURR_STATE); + rf_state = rt2x00_get_field32(reg2, PWRCSR1_RF_CURR_STATE); + if (bbp_state == state && rf_state == state) + return 0; + rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); + msleep(10); + } + + return -EBUSY; +} + +static int rt2400pci_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + retval = rt2400pci_enable_radio(rt2x00dev); + break; + case STATE_RADIO_OFF: + rt2400pci_disable_radio(rt2x00dev); + break; + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt2400pci_toggle_irq(rt2x00dev, state); + break; + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + retval = rt2400pci_set_state(rt2x00dev, state); + break; + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +/* + * TX descriptor initialization + */ +static void rt2400pci_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *txd = entry_priv->desc; + u32 word; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_BUFFER_LENGTH, txdesc->length); + rt2x00_set_field32(&word, TXD_W2_DATABYTE_COUNT, txdesc->length); + rt2x00_desc_write(txd, 2, word); + + rt2x00_desc_read(txd, 3, &word); + rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, txdesc->u.plcp.signal); + rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL_REGNUM, 5); + rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL_BUSY, 1); + rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, txdesc->u.plcp.service); + rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE_REGNUM, 6); + rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE_BUSY, 1); + rt2x00_desc_write(txd, 3, word); + + rt2x00_desc_read(txd, 4, &word); + rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_LOW, + txdesc->u.plcp.length_low); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW_REGNUM, 8); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW_BUSY, 1); + rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_HIGH, + txdesc->u.plcp.length_high); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH_REGNUM, 7); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH_BUSY, 1); + rt2x00_desc_write(txd, 4, word); + + /* + * Writing TXD word 0 must the last to prevent a race condition with + * the device, whereby the device may take hold of the TXD before we + * finished updating it. + */ + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); + rt2x00_set_field32(&word, TXD_W0_VALID, 1); + rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_ACK, + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_RTS, + test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); + rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, + test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); + rt2x00_desc_write(txd, 0, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; +} + +/* + * TX data initialization + */ +static void rt2400pci_write_beacon(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + u32 reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + + if (rt2x00queue_map_txskb(entry)) { + rt2x00_err(rt2x00dev, "Fail to map beacon, aborting\n"); + goto out; + } + /* + * Enable beaconing again. + */ + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + /* + * Write the TX descriptor for the beacon. + */ + rt2400pci_write_tx_desc(entry, txdesc); + + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); +out: + /* + * Enable beaconing again. + */ + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); +} + +/* + * RX control handlers + */ +static void rt2400pci_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + u32 word0; + u32 word2; + u32 word3; + u32 word4; + u64 tsf; + u32 rx_low; + u32 rx_high; + + rt2x00_desc_read(entry_priv->desc, 0, &word0); + rt2x00_desc_read(entry_priv->desc, 2, &word2); + rt2x00_desc_read(entry_priv->desc, 3, &word3); + rt2x00_desc_read(entry_priv->desc, 4, &word4); + + if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; + + /* + * We only get the lower 32bits from the timestamp, + * to get the full 64bits we must complement it with + * the timestamp from get_tsf(). + * Note that when a wraparound of the lower 32bits + * has occurred between the frame arrival and the get_tsf() + * call, we must decrease the higher 32bits with 1 to get + * to correct value. + */ + tsf = rt2x00dev->ops->hw->get_tsf(rt2x00dev->hw, NULL); + rx_low = rt2x00_get_field32(word4, RXD_W4_RX_END_TIME); + rx_high = upper_32_bits(tsf); + + if ((u32)tsf <= rx_low) + rx_high--; + + /* + * Obtain the status about this packet. + * The signal is the PLCP value, and needs to be stripped + * of the preamble bit (0x08). + */ + rxdesc->timestamp = ((u64)rx_high << 32) | rx_low; + rxdesc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL) & ~0x08; + rxdesc->rssi = rt2x00_get_field32(word3, RXD_W3_RSSI) - + entry->queue->rt2x00dev->rssi_offset; + rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; + if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; +} + +/* + * Interrupt functions. + */ +static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev, + const enum data_queue_qid queue_idx) +{ + struct data_queue *queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); + struct queue_entry_priv_mmio *entry_priv; + struct queue_entry *entry; + struct txdone_entry_desc txdesc; + u32 word; + + while (!rt2x00queue_empty(queue)) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + entry_priv = entry->priv_data; + rt2x00_desc_read(entry_priv->desc, 0, &word); + + if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + !rt2x00_get_field32(word, TXD_W0_VALID)) + break; + + /* + * Obtain the status about this packet. + */ + txdesc.flags = 0; + switch (rt2x00_get_field32(word, TXD_W0_RESULT)) { + case 0: /* Success */ + case 1: /* Success with retry */ + __set_bit(TXDONE_SUCCESS, &txdesc.flags); + break; + case 2: /* Failure, excessive retries */ + __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); + /* Don't break, this is a failed frame! */ + default: /* Failure */ + __set_bit(TXDONE_FAILURE, &txdesc.flags); + } + txdesc.retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); + + rt2x00lib_txdone(entry, &txdesc); + } +} + +static inline void rt2400pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, irq_field, 0); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +static void rt2400pci_txstatus_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + u32 reg; + + /* + * Handle all tx queues. + */ + rt2400pci_txdone(rt2x00dev, QID_ATIM); + rt2400pci_txdone(rt2x00dev, QID_AC_VO); + rt2400pci_txdone(rt2x00dev, QID_AC_VI); + + /* + * Enable all TXDONE interrupts again. + */ + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) { + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); + } +} + +static void rt2400pci_tbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2x00lib_beacondone(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2400pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); +} + +static void rt2400pci_rxdone_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2x00mmio_rxdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2400pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); +} + +static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + u32 reg, mask; + + /* + * Get the interrupt sources & saved to local variable. + * Write register value back to clear pending interrupts. + */ + rt2x00mmio_register_read(rt2x00dev, CSR7, ®); + rt2x00mmio_register_write(rt2x00dev, CSR7, reg); + + if (!reg) + return IRQ_NONE; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return IRQ_HANDLED; + + mask = reg; + + /* + * Schedule tasklets for interrupt handling. + */ + if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) + tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); + + if (rt2x00_get_field32(reg, CSR7_RXDONE)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + + if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) || + rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) || + rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) { + tasklet_schedule(&rt2x00dev->txstatus_tasklet); + /* + * Mask out all txdone interrupts. + */ + rt2x00_set_field32(&mask, CSR8_TXDONE_TXRING, 1); + rt2x00_set_field32(&mask, CSR8_TXDONE_ATIMRING, 1); + rt2x00_set_field32(&mask, CSR8_TXDONE_PRIORING, 1); + } + + /* + * Disable all interrupts for which a tasklet was scheduled right now, + * the tasklet will reenable the appropriate interrupts. + */ + spin_lock(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + reg |= mask; + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock(&rt2x00dev->irqmask_lock); + + + + return IRQ_HANDLED; +} + +/* + * Device probe functions. + */ +static int rt2400pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) +{ + struct eeprom_93cx6 eeprom; + u32 reg; + u16 word; + u8 *mac; + + rt2x00mmio_register_read(rt2x00dev, CSR21, ®); + + eeprom.data = rt2x00dev; + eeprom.register_read = rt2400pci_eepromregister_read; + eeprom.register_write = rt2400pci_eepromregister_write; + eeprom.width = rt2x00_get_field32(reg, CSR21_TYPE_93C46) ? + PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; + eeprom.reg_data_in = 0; + eeprom.reg_data_out = 0; + eeprom.reg_data_clock = 0; + eeprom.reg_chip_select = 0; + + eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, + EEPROM_SIZE / sizeof(u16)); + + /* + * Start validation of the data that has been read. + */ + mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + if (!is_valid_ether_addr(mac)) { + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); + if (word == 0xffff) { + rt2x00_err(rt2x00dev, "Invalid EEPROM data detected\n"); + return -EINVAL; + } + + return 0; +} + +static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 value; + u16 eeprom; + + /* + * Read EEPROM word for configuration. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Identify RF chipset. + */ + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); + rt2x00mmio_register_read(rt2x00dev, CSR0, ®); + rt2x00_set_chip(rt2x00dev, RT2460, value, + rt2x00_get_field32(reg, CSR0_REVISION)); + + if (!rt2x00_rf(rt2x00dev, RF2420) && !rt2x00_rf(rt2x00dev, RF2421)) { + rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); + return -ENODEV; + } + + /* + * Identify default antenna configuration. + */ + rt2x00dev->default_ant.tx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); + rt2x00dev->default_ant.rx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); + + /* + * When the eeprom indicates SW_DIVERSITY use HW_DIVERSITY instead. + * I am not 100% sure about this, but the legacy drivers do not + * indicate antenna swapping in software is required when + * diversity is enabled. + */ + if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY) + rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY; + if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY) + rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY; + + /* + * Store led mode, for correct led behaviour. + */ +#ifdef CONFIG_RT2X00_LIB_LEDS + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); + + rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + if (value == LED_MODE_TXRX_ACTIVITY || + value == LED_MODE_DEFAULT || + value == LED_MODE_ASUS) + rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_qual, + LED_TYPE_ACTIVITY); +#endif /* CONFIG_RT2X00_LIB_LEDS */ + + /* + * Detect if this device has an hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); + + /* + * Check if the BBP tuning should be enabled. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_AGCVGC_TUNING)) + __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); + + return 0; +} + +/* + * RF value list for RF2420 & RF2421 + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_b[] = { + { 1, 0x00022058, 0x000c1fda, 0x00000101, 0 }, + { 2, 0x00022058, 0x000c1fee, 0x00000101, 0 }, + { 3, 0x00022058, 0x000c2002, 0x00000101, 0 }, + { 4, 0x00022058, 0x000c2016, 0x00000101, 0 }, + { 5, 0x00022058, 0x000c202a, 0x00000101, 0 }, + { 6, 0x00022058, 0x000c203e, 0x00000101, 0 }, + { 7, 0x00022058, 0x000c2052, 0x00000101, 0 }, + { 8, 0x00022058, 0x000c2066, 0x00000101, 0 }, + { 9, 0x00022058, 0x000c207a, 0x00000101, 0 }, + { 10, 0x00022058, 0x000c208e, 0x00000101, 0 }, + { 11, 0x00022058, 0x000c20a2, 0x00000101, 0 }, + { 12, 0x00022058, 0x000c20b6, 0x00000101, 0 }, + { 13, 0x00022058, 0x000c20ca, 0x00000101, 0 }, + { 14, 0x00022058, 0x000c20fa, 0x00000101, 0 }, +}; + +static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + struct channel_info *info; + char *tx_power; + unsigned int i; + + /* + * Initialize all hw fields. + */ + ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); + ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); + ieee80211_hw_set(rt2x00dev->hw, HOST_BROADCAST_PS_BUFFERING); + ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); + + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, + rt2x00_eeprom_addr(rt2x00dev, + EEPROM_MAC_ADDR_0)); + + /* + * Initialize hw_mode information. + */ + spec->supported_bands = SUPPORT_BAND_2GHZ; + spec->supported_rates = SUPPORT_RATE_CCK; + + spec->num_channels = ARRAY_SIZE(rf_vals_b); + spec->channels = rf_vals_b; + + /* + * Create channel information array + */ + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) { + info[i].max_power = TXPOWER_FROM_DEV(MAX_TXPOWER); + info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); + } + + return 0; +} + +static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + u32 reg; + + /* + * Allocate eeprom data. + */ + retval = rt2400pci_validate_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt2400pci_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. + */ + rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); + rt2x00_set_field32(®, GPIOCSR_DIR0, 1); + rt2x00mmio_register_write(rt2x00dev, GPIOCSR, reg); + + /* + * Initialize hw specifications. + */ + retval = rt2400pci_probe_hw_mode(rt2x00dev); + if (retval) + return retval; + + /* + * This device requires the atim queue and DMA-mapped skbs. + */ + __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); + + /* + * Set the rssi offset. + */ + rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; + + return 0; +} + +/* + * IEEE80211 stack callback functions. + */ +static int rt2400pci_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * We don't support variating cw_min and cw_max variables + * per queue. So by default we only configure the TX queue, + * and ignore all other configurations. + */ + if (queue != 0) + return -EINVAL; + + if (rt2x00mac_conf_tx(hw, vif, queue, params)) + return -EINVAL; + + /* + * Write configuration to register. + */ + rt2400pci_config_cw(rt2x00dev, + rt2x00dev->tx->cw_min, rt2x00dev->tx->cw_max); + + return 0; +} + +static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u64 tsf; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, CSR17, ®); + tsf = (u64) rt2x00_get_field32(reg, CSR17_HIGH_TSFTIMER) << 32; + rt2x00mmio_register_read(rt2x00dev, CSR16, ®); + tsf |= rt2x00_get_field32(reg, CSR16_LOW_TSFTIMER); + + return tsf; +} + +static int rt2400pci_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, CSR15, ®); + return rt2x00_get_field32(reg, CSR15_BEACON_SENT); +} + +static const struct ieee80211_ops rt2400pci_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2400pci_conf_tx, + .get_tsf = rt2400pci_get_tsf, + .tx_last_beacon = rt2400pci_tx_last_beacon, + .rfkill_poll = rt2x00mac_rfkill_poll, + .flush = rt2x00mac_flush, + .set_antenna = rt2x00mac_set_antenna, + .get_antenna = rt2x00mac_get_antenna, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { + .irq_handler = rt2400pci_interrupt, + .txstatus_tasklet = rt2400pci_txstatus_tasklet, + .tbtt_tasklet = rt2400pci_tbtt_tasklet, + .rxdone_tasklet = rt2400pci_rxdone_tasklet, + .probe_hw = rt2400pci_probe_hw, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt2400pci_get_entry_state, + .clear_entry = rt2400pci_clear_entry, + .set_device_state = rt2400pci_set_device_state, + .rfkill_poll = rt2400pci_rfkill_poll, + .link_stats = rt2400pci_link_stats, + .reset_tuner = rt2400pci_reset_tuner, + .link_tuner = rt2400pci_link_tuner, + .start_queue = rt2400pci_start_queue, + .kick_queue = rt2400pci_kick_queue, + .stop_queue = rt2400pci_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, + .write_tx_desc = rt2400pci_write_tx_desc, + .write_beacon = rt2400pci_write_beacon, + .fill_rxdone = rt2400pci_fill_rxdone, + .config_filter = rt2400pci_config_filter, + .config_intf = rt2400pci_config_intf, + .config_erp = rt2400pci_config_erp, + .config_ant = rt2400pci_config_ant, + .config = rt2400pci_config, +}; + +static void rt2400pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 24; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 24; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + default: + BUG(); + break; + } +} + +static const struct rt2x00_ops rt2400pci_ops = { + .name = KBUILD_MODNAME, + .max_ap_intf = 1, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2400pci_queue_init, + .lib = &rt2400pci_rt2x00_ops, + .hw = &rt2400pci_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2400pci_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +/* + * RT2400pci module information. + */ +static const struct pci_device_id rt2400pci_device_table[] = { + { PCI_DEVICE(0x1814, 0x0101) }, + { 0, } +}; + + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2400 PCI & PCMCIA Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2460 PCI & PCMCIA chipset based cards"); +MODULE_DEVICE_TABLE(pci, rt2400pci_device_table); +MODULE_LICENSE("GPL"); + +static int rt2400pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + return rt2x00pci_probe(pci_dev, &rt2400pci_ops); +} + +static struct pci_driver rt2400pci_driver = { + .name = KBUILD_MODNAME, + .id_table = rt2400pci_device_table, + .probe = rt2400pci_probe, + .remove = rt2x00pci_remove, + .suspend = rt2x00pci_suspend, + .resume = rt2x00pci_resume, +}; + +module_pci_driver(rt2400pci_driver); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2400pci.h b/drivers/net/wireless/ralink/rt2x00/rt2400pci.h new file mode 100644 index 000000000000..0fd3a9d01a60 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.h @@ -0,0 +1,961 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2400pci + Abstract: Data structures and registers for the rt2400pci module. + Supported chipsets: RT2460. + */ + +#ifndef RT2400PCI_H +#define RT2400PCI_H + +/* + * RF chip defines. + */ +#define RF2420 0x0000 +#define RF2421 0x0001 + +/* + * Signal information. + * Default offset is required for RSSI <-> dBm conversion. + */ +#define DEFAULT_RSSI_OFFSET 100 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x0000 +#define CSR_REG_SIZE 0x014c +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x0100 +#define BBP_BASE 0x0000 +#define BBP_SIZE 0x0020 +#define RF_BASE 0x0004 +#define RF_SIZE 0x000c + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 2 + +/* + * Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * CSR0: ASIC revision number. + */ +#define CSR0 0x0000 +#define CSR0_REVISION FIELD32(0x0000ffff) + +/* + * CSR1: System control register. + * SOFT_RESET: Software reset, 1: reset, 0: normal. + * BBP_RESET: Hardware reset, 1: reset, 0, release. + * HOST_READY: Host ready after initialization. + */ +#define CSR1 0x0004 +#define CSR1_SOFT_RESET FIELD32(0x00000001) +#define CSR1_BBP_RESET FIELD32(0x00000002) +#define CSR1_HOST_READY FIELD32(0x00000004) + +/* + * CSR2: System admin status register (invalid). + */ +#define CSR2 0x0008 + +/* + * CSR3: STA MAC address register 0. + */ +#define CSR3 0x000c +#define CSR3_BYTE0 FIELD32(0x000000ff) +#define CSR3_BYTE1 FIELD32(0x0000ff00) +#define CSR3_BYTE2 FIELD32(0x00ff0000) +#define CSR3_BYTE3 FIELD32(0xff000000) + +/* + * CSR4: STA MAC address register 1. + */ +#define CSR4 0x0010 +#define CSR4_BYTE4 FIELD32(0x000000ff) +#define CSR4_BYTE5 FIELD32(0x0000ff00) + +/* + * CSR5: BSSID register 0. + */ +#define CSR5 0x0014 +#define CSR5_BYTE0 FIELD32(0x000000ff) +#define CSR5_BYTE1 FIELD32(0x0000ff00) +#define CSR5_BYTE2 FIELD32(0x00ff0000) +#define CSR5_BYTE3 FIELD32(0xff000000) + +/* + * CSR6: BSSID register 1. + */ +#define CSR6 0x0018 +#define CSR6_BYTE4 FIELD32(0x000000ff) +#define CSR6_BYTE5 FIELD32(0x0000ff00) + +/* + * CSR7: Interrupt source register. + * Write 1 to clear interrupt. + * TBCN_EXPIRE: Beacon timer expired interrupt. + * TWAKE_EXPIRE: Wakeup timer expired interrupt. + * TATIMW_EXPIRE: Timer of atim window expired interrupt. + * TXDONE_TXRING: Tx ring transmit done interrupt. + * TXDONE_ATIMRING: Atim ring transmit done interrupt. + * TXDONE_PRIORING: Priority ring transmit done interrupt. + * RXDONE: Receive done interrupt. + */ +#define CSR7 0x001c +#define CSR7_TBCN_EXPIRE FIELD32(0x00000001) +#define CSR7_TWAKE_EXPIRE FIELD32(0x00000002) +#define CSR7_TATIMW_EXPIRE FIELD32(0x00000004) +#define CSR7_TXDONE_TXRING FIELD32(0x00000008) +#define CSR7_TXDONE_ATIMRING FIELD32(0x00000010) +#define CSR7_TXDONE_PRIORING FIELD32(0x00000020) +#define CSR7_RXDONE FIELD32(0x00000040) + +/* + * CSR8: Interrupt mask register. + * Write 1 to mask interrupt. + * TBCN_EXPIRE: Beacon timer expired interrupt. + * TWAKE_EXPIRE: Wakeup timer expired interrupt. + * TATIMW_EXPIRE: Timer of atim window expired interrupt. + * TXDONE_TXRING: Tx ring transmit done interrupt. + * TXDONE_ATIMRING: Atim ring transmit done interrupt. + * TXDONE_PRIORING: Priority ring transmit done interrupt. + * RXDONE: Receive done interrupt. + */ +#define CSR8 0x0020 +#define CSR8_TBCN_EXPIRE FIELD32(0x00000001) +#define CSR8_TWAKE_EXPIRE FIELD32(0x00000002) +#define CSR8_TATIMW_EXPIRE FIELD32(0x00000004) +#define CSR8_TXDONE_TXRING FIELD32(0x00000008) +#define CSR8_TXDONE_ATIMRING FIELD32(0x00000010) +#define CSR8_TXDONE_PRIORING FIELD32(0x00000020) +#define CSR8_RXDONE FIELD32(0x00000040) + +/* + * CSR9: Maximum frame length register. + * MAX_FRAME_UNIT: Maximum frame length in 128b unit, default: 12. + */ +#define CSR9 0x0024 +#define CSR9_MAX_FRAME_UNIT FIELD32(0x00000f80) + +/* + * CSR11: Back-off control register. + * CWMIN: CWmin. Default cwmin is 31 (2^5 - 1). + * CWMAX: CWmax. Default cwmax is 1023 (2^10 - 1). + * SLOT_TIME: Slot time, default is 20us for 802.11b. + * LONG_RETRY: Long retry count. + * SHORT_RETRY: Short retry count. + */ +#define CSR11 0x002c +#define CSR11_CWMIN FIELD32(0x0000000f) +#define CSR11_CWMAX FIELD32(0x000000f0) +#define CSR11_SLOT_TIME FIELD32(0x00001f00) +#define CSR11_LONG_RETRY FIELD32(0x00ff0000) +#define CSR11_SHORT_RETRY FIELD32(0xff000000) + +/* + * CSR12: Synchronization configuration register 0. + * All units in 1/16 TU. + * BEACON_INTERVAL: Beacon interval, default is 100 TU. + * CFPMAX_DURATION: Cfp maximum duration, default is 100 TU. + */ +#define CSR12 0x0030 +#define CSR12_BEACON_INTERVAL FIELD32(0x0000ffff) +#define CSR12_CFP_MAX_DURATION FIELD32(0xffff0000) + +/* + * CSR13: Synchronization configuration register 1. + * All units in 1/16 TU. + * ATIMW_DURATION: Atim window duration. + * CFP_PERIOD: Cfp period, default is 0 TU. + */ +#define CSR13 0x0034 +#define CSR13_ATIMW_DURATION FIELD32(0x0000ffff) +#define CSR13_CFP_PERIOD FIELD32(0x00ff0000) + +/* + * CSR14: Synchronization control register. + * TSF_COUNT: Enable tsf auto counting. + * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. + * TBCN: Enable tbcn with reload value. + * TCFP: Enable tcfp & cfp / cp switching. + * TATIMW: Enable tatimw & atim window switching. + * BEACON_GEN: Enable beacon generator. + * CFP_COUNT_PRELOAD: Cfp count preload value. + * TBCM_PRELOAD: Tbcn preload value in units of 64us. + */ +#define CSR14 0x0038 +#define CSR14_TSF_COUNT FIELD32(0x00000001) +#define CSR14_TSF_SYNC FIELD32(0x00000006) +#define CSR14_TBCN FIELD32(0x00000008) +#define CSR14_TCFP FIELD32(0x00000010) +#define CSR14_TATIMW FIELD32(0x00000020) +#define CSR14_BEACON_GEN FIELD32(0x00000040) +#define CSR14_CFP_COUNT_PRELOAD FIELD32(0x0000ff00) +#define CSR14_TBCM_PRELOAD FIELD32(0xffff0000) + +/* + * CSR15: Synchronization status register. + * CFP: ASIC is in contention-free period. + * ATIMW: ASIC is in ATIM window. + * BEACON_SENT: Beacon is send. + */ +#define CSR15 0x003c +#define CSR15_CFP FIELD32(0x00000001) +#define CSR15_ATIMW FIELD32(0x00000002) +#define CSR15_BEACON_SENT FIELD32(0x00000004) + +/* + * CSR16: TSF timer register 0. + */ +#define CSR16 0x0040 +#define CSR16_LOW_TSFTIMER FIELD32(0xffffffff) + +/* + * CSR17: TSF timer register 1. + */ +#define CSR17 0x0044 +#define CSR17_HIGH_TSFTIMER FIELD32(0xffffffff) + +/* + * CSR18: IFS timer register 0. + * SIFS: Sifs, default is 10 us. + * PIFS: Pifs, default is 30 us. + */ +#define CSR18 0x0048 +#define CSR18_SIFS FIELD32(0x0000ffff) +#define CSR18_PIFS FIELD32(0xffff0000) + +/* + * CSR19: IFS timer register 1. + * DIFS: Difs, default is 50 us. + * EIFS: Eifs, default is 364 us. + */ +#define CSR19 0x004c +#define CSR19_DIFS FIELD32(0x0000ffff) +#define CSR19_EIFS FIELD32(0xffff0000) + +/* + * CSR20: Wakeup timer register. + * DELAY_AFTER_TBCN: Delay after tbcn expired in units of 1/16 TU. + * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. + * AUTOWAKE: Enable auto wakeup / sleep mechanism. + */ +#define CSR20 0x0050 +#define CSR20_DELAY_AFTER_TBCN FIELD32(0x0000ffff) +#define CSR20_TBCN_BEFORE_WAKEUP FIELD32(0x00ff0000) +#define CSR20_AUTOWAKE FIELD32(0x01000000) + +/* + * CSR21: EEPROM control register. + * RELOAD: Write 1 to reload eeprom content. + * TYPE_93C46: 1: 93c46, 0:93c66. + */ +#define CSR21 0x0054 +#define CSR21_RELOAD FIELD32(0x00000001) +#define CSR21_EEPROM_DATA_CLOCK FIELD32(0x00000002) +#define CSR21_EEPROM_CHIP_SELECT FIELD32(0x00000004) +#define CSR21_EEPROM_DATA_IN FIELD32(0x00000008) +#define CSR21_EEPROM_DATA_OUT FIELD32(0x00000010) +#define CSR21_TYPE_93C46 FIELD32(0x00000020) + +/* + * CSR22: CFP control register. + * CFP_DURATION_REMAIN: Cfp duration remain, in units of TU. + * RELOAD_CFP_DURATION: Write 1 to reload cfp duration remain. + */ +#define CSR22 0x0058 +#define CSR22_CFP_DURATION_REMAIN FIELD32(0x0000ffff) +#define CSR22_RELOAD_CFP_DURATION FIELD32(0x00010000) + +/* + * Transmit related CSRs. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * TXCSR0: TX Control Register. + * KICK_TX: Kick tx ring. + * KICK_ATIM: Kick atim ring. + * KICK_PRIO: Kick priority ring. + * ABORT: Abort all transmit related ring operation. + */ +#define TXCSR0 0x0060 +#define TXCSR0_KICK_TX FIELD32(0x00000001) +#define TXCSR0_KICK_ATIM FIELD32(0x00000002) +#define TXCSR0_KICK_PRIO FIELD32(0x00000004) +#define TXCSR0_ABORT FIELD32(0x00000008) + +/* + * TXCSR1: TX Configuration Register. + * ACK_TIMEOUT: Ack timeout, default = sifs + 2*slottime + acktime @ 1mbps. + * ACK_CONSUME_TIME: Ack consume time, default = sifs + acktime @ 1mbps. + * TSF_OFFSET: Insert tsf offset. + * AUTORESPONDER: Enable auto responder which include ack & cts. + */ +#define TXCSR1 0x0064 +#define TXCSR1_ACK_TIMEOUT FIELD32(0x000001ff) +#define TXCSR1_ACK_CONSUME_TIME FIELD32(0x0003fe00) +#define TXCSR1_TSF_OFFSET FIELD32(0x00fc0000) +#define TXCSR1_AUTORESPONDER FIELD32(0x01000000) + +/* + * TXCSR2: Tx descriptor configuration register. + * TXD_SIZE: Tx descriptor size, default is 48. + * NUM_TXD: Number of tx entries in ring. + * NUM_ATIM: Number of atim entries in ring. + * NUM_PRIO: Number of priority entries in ring. + */ +#define TXCSR2 0x0068 +#define TXCSR2_TXD_SIZE FIELD32(0x000000ff) +#define TXCSR2_NUM_TXD FIELD32(0x0000ff00) +#define TXCSR2_NUM_ATIM FIELD32(0x00ff0000) +#define TXCSR2_NUM_PRIO FIELD32(0xff000000) + +/* + * TXCSR3: TX Ring Base address register. + */ +#define TXCSR3 0x006c +#define TXCSR3_TX_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR4: TX Atim Ring Base address register. + */ +#define TXCSR4 0x0070 +#define TXCSR4_ATIM_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR5: TX Prio Ring Base address register. + */ +#define TXCSR5 0x0074 +#define TXCSR5_PRIO_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR6: Beacon Base address register. + */ +#define TXCSR6 0x0078 +#define TXCSR6_BEACON_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR7: Auto responder control register. + * AR_POWERMANAGEMENT: Auto responder power management bit. + */ +#define TXCSR7 0x007c +#define TXCSR7_AR_POWERMANAGEMENT FIELD32(0x00000001) + +/* + * Receive related CSRs. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * RXCSR0: RX Control Register. + * DISABLE_RX: Disable rx engine. + * DROP_CRC: Drop crc error. + * DROP_PHYSICAL: Drop physical error. + * DROP_CONTROL: Drop control frame. + * DROP_NOT_TO_ME: Drop not to me unicast frame. + * DROP_TODS: Drop frame tods bit is true. + * DROP_VERSION_ERROR: Drop version error frame. + * PASS_CRC: Pass all packets with crc attached. + */ +#define RXCSR0 0x0080 +#define RXCSR0_DISABLE_RX FIELD32(0x00000001) +#define RXCSR0_DROP_CRC FIELD32(0x00000002) +#define RXCSR0_DROP_PHYSICAL FIELD32(0x00000004) +#define RXCSR0_DROP_CONTROL FIELD32(0x00000008) +#define RXCSR0_DROP_NOT_TO_ME FIELD32(0x00000010) +#define RXCSR0_DROP_TODS FIELD32(0x00000020) +#define RXCSR0_DROP_VERSION_ERROR FIELD32(0x00000040) +#define RXCSR0_PASS_CRC FIELD32(0x00000080) + +/* + * RXCSR1: RX descriptor configuration register. + * RXD_SIZE: Rx descriptor size, default is 32b. + * NUM_RXD: Number of rx entries in ring. + */ +#define RXCSR1 0x0084 +#define RXCSR1_RXD_SIZE FIELD32(0x000000ff) +#define RXCSR1_NUM_RXD FIELD32(0x0000ff00) + +/* + * RXCSR2: RX Ring base address register. + */ +#define RXCSR2 0x0088 +#define RXCSR2_RX_RING_REGISTER FIELD32(0xffffffff) + +/* + * RXCSR3: BBP ID register for Rx operation. + * BBP_ID#: BBP register # id. + * BBP_ID#_VALID: BBP register # id is valid or not. + */ +#define RXCSR3 0x0090 +#define RXCSR3_BBP_ID0 FIELD32(0x0000007f) +#define RXCSR3_BBP_ID0_VALID FIELD32(0x00000080) +#define RXCSR3_BBP_ID1 FIELD32(0x00007f00) +#define RXCSR3_BBP_ID1_VALID FIELD32(0x00008000) +#define RXCSR3_BBP_ID2 FIELD32(0x007f0000) +#define RXCSR3_BBP_ID2_VALID FIELD32(0x00800000) +#define RXCSR3_BBP_ID3 FIELD32(0x7f000000) +#define RXCSR3_BBP_ID3_VALID FIELD32(0x80000000) + +/* + * RXCSR4: BBP ID register for Rx operation. + * BBP_ID#: BBP register # id. + * BBP_ID#_VALID: BBP register # id is valid or not. + */ +#define RXCSR4 0x0094 +#define RXCSR4_BBP_ID4 FIELD32(0x0000007f) +#define RXCSR4_BBP_ID4_VALID FIELD32(0x00000080) +#define RXCSR4_BBP_ID5 FIELD32(0x00007f00) +#define RXCSR4_BBP_ID5_VALID FIELD32(0x00008000) + +/* + * ARCSR0: Auto Responder PLCP config register 0. + * ARCSR0_AR_BBP_DATA#: Auto responder BBP register # data. + * ARCSR0_AR_BBP_ID#: Auto responder BBP register # Id. + */ +#define ARCSR0 0x0098 +#define ARCSR0_AR_BBP_DATA0 FIELD32(0x000000ff) +#define ARCSR0_AR_BBP_ID0 FIELD32(0x0000ff00) +#define ARCSR0_AR_BBP_DATA1 FIELD32(0x00ff0000) +#define ARCSR0_AR_BBP_ID1 FIELD32(0xff000000) + +/* + * ARCSR1: Auto Responder PLCP config register 1. + * ARCSR0_AR_BBP_DATA#: Auto responder BBP register # data. + * ARCSR0_AR_BBP_ID#: Auto responder BBP register # Id. + */ +#define ARCSR1 0x009c +#define ARCSR1_AR_BBP_DATA2 FIELD32(0x000000ff) +#define ARCSR1_AR_BBP_ID2 FIELD32(0x0000ff00) +#define ARCSR1_AR_BBP_DATA3 FIELD32(0x00ff0000) +#define ARCSR1_AR_BBP_ID3 FIELD32(0xff000000) + +/* + * Miscellaneous Registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * PCICSR: PCI control register. + * BIG_ENDIAN: 1: big endian, 0: little endian. + * RX_TRESHOLD: Rx threshold in dw to start pci access + * 0: 16dw (default), 1: 8dw, 2: 4dw, 3: 32dw. + * TX_TRESHOLD: Tx threshold in dw to start pci access + * 0: 0dw (default), 1: 1dw, 2: 4dw, 3: forward. + * BURST_LENTH: Pci burst length 0: 4dw (default, 1: 8dw, 2: 16dw, 3:32dw. + * ENABLE_CLK: Enable clk_run, pci clock can't going down to non-operational. + */ +#define PCICSR 0x008c +#define PCICSR_BIG_ENDIAN FIELD32(0x00000001) +#define PCICSR_RX_TRESHOLD FIELD32(0x00000006) +#define PCICSR_TX_TRESHOLD FIELD32(0x00000018) +#define PCICSR_BURST_LENTH FIELD32(0x00000060) +#define PCICSR_ENABLE_CLK FIELD32(0x00000080) + +/* + * CNT0: FCS error count. + * FCS_ERROR: FCS error count, cleared when read. + */ +#define CNT0 0x00a0 +#define CNT0_FCS_ERROR FIELD32(0x0000ffff) + +/* + * Statistic Register. + * CNT1: PLCP error count. + * CNT2: Long error count. + * CNT3: CCA false alarm count. + * CNT4: Rx FIFO overflow count. + * CNT5: Tx FIFO underrun count. + */ +#define TIMECSR2 0x00a8 +#define CNT1 0x00ac +#define CNT2 0x00b0 +#define TIMECSR3 0x00b4 +#define CNT3 0x00b8 +#define CNT4 0x00bc +#define CNT5 0x00c0 + +/* + * Baseband Control Register. + */ + +/* + * PWRCSR0: Power mode configuration register. + */ +#define PWRCSR0 0x00c4 + +/* + * Power state transition time registers. + */ +#define PSCSR0 0x00c8 +#define PSCSR1 0x00cc +#define PSCSR2 0x00d0 +#define PSCSR3 0x00d4 + +/* + * PWRCSR1: Manual power control / status register. + * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. + * SET_STATE: Set state. Write 1 to trigger, self cleared. + * BBP_DESIRE_STATE: BBP desired state. + * RF_DESIRE_STATE: RF desired state. + * BBP_CURR_STATE: BBP current state. + * RF_CURR_STATE: RF current state. + * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. + */ +#define PWRCSR1 0x00d8 +#define PWRCSR1_SET_STATE FIELD32(0x00000001) +#define PWRCSR1_BBP_DESIRE_STATE FIELD32(0x00000006) +#define PWRCSR1_RF_DESIRE_STATE FIELD32(0x00000018) +#define PWRCSR1_BBP_CURR_STATE FIELD32(0x00000060) +#define PWRCSR1_RF_CURR_STATE FIELD32(0x00000180) +#define PWRCSR1_PUT_TO_SLEEP FIELD32(0x00000200) + +/* + * TIMECSR: Timer control register. + * US_COUNT: 1 us timer count in units of clock cycles. + * US_64_COUNT: 64 us timer count in units of 1 us timer. + * BEACON_EXPECT: Beacon expect window. + */ +#define TIMECSR 0x00dc +#define TIMECSR_US_COUNT FIELD32(0x000000ff) +#define TIMECSR_US_64_COUNT FIELD32(0x0000ff00) +#define TIMECSR_BEACON_EXPECT FIELD32(0x00070000) + +/* + * MACCSR0: MAC configuration register 0. + */ +#define MACCSR0 0x00e0 + +/* + * MACCSR1: MAC configuration register 1. + * KICK_RX: Kick one-shot rx in one-shot rx mode. + * ONESHOT_RXMODE: Enable one-shot rx mode for debugging. + * BBPRX_RESET_MODE: Ralink bbp rx reset mode. + * AUTO_TXBBP: Auto tx logic access bbp control register. + * AUTO_RXBBP: Auto rx logic access bbp control register. + * LOOPBACK: Loopback mode. 0: normal, 1: internal, 2: external, 3:rsvd. + * INTERSIL_IF: Intersil if calibration pin. + */ +#define MACCSR1 0x00e4 +#define MACCSR1_KICK_RX FIELD32(0x00000001) +#define MACCSR1_ONESHOT_RXMODE FIELD32(0x00000002) +#define MACCSR1_BBPRX_RESET_MODE FIELD32(0x00000004) +#define MACCSR1_AUTO_TXBBP FIELD32(0x00000008) +#define MACCSR1_AUTO_RXBBP FIELD32(0x00000010) +#define MACCSR1_LOOPBACK FIELD32(0x00000060) +#define MACCSR1_INTERSIL_IF FIELD32(0x00000080) + +/* + * RALINKCSR: Ralink Rx auto-reset BBCR. + * AR_BBP_DATA#: Auto reset BBP register # data. + * AR_BBP_ID#: Auto reset BBP register # id. + */ +#define RALINKCSR 0x00e8 +#define RALINKCSR_AR_BBP_DATA0 FIELD32(0x000000ff) +#define RALINKCSR_AR_BBP_ID0 FIELD32(0x0000ff00) +#define RALINKCSR_AR_BBP_DATA1 FIELD32(0x00ff0000) +#define RALINKCSR_AR_BBP_ID1 FIELD32(0xff000000) + +/* + * BCNCSR: Beacon interval control register. + * CHANGE: Write one to change beacon interval. + * DELTATIME: The delta time value. + * NUM_BEACON: Number of beacon according to mode. + * MODE: Please refer to asic specs. + * PLUS: Plus or minus delta time value. + */ +#define BCNCSR 0x00ec +#define BCNCSR_CHANGE FIELD32(0x00000001) +#define BCNCSR_DELTATIME FIELD32(0x0000001e) +#define BCNCSR_NUM_BEACON FIELD32(0x00001fe0) +#define BCNCSR_MODE FIELD32(0x00006000) +#define BCNCSR_PLUS FIELD32(0x00008000) + +/* + * BBP / RF / IF Control Register. + */ + +/* + * BBPCSR: BBP serial control register. + * VALUE: Register value to program into BBP. + * REGNUM: Selected BBP register. + * BUSY: 1: asic is busy execute BBP programming. + * WRITE_CONTROL: 1: write BBP, 0: read BBP. + */ +#define BBPCSR 0x00f0 +#define BBPCSR_VALUE FIELD32(0x000000ff) +#define BBPCSR_REGNUM FIELD32(0x00007f00) +#define BBPCSR_BUSY FIELD32(0x00008000) +#define BBPCSR_WRITE_CONTROL FIELD32(0x00010000) + +/* + * RFCSR: RF serial control register. + * VALUE: Register value + id to program into rf/if. + * NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). + * IF_SELECT: Chip to program: 0: rf, 1: if. + * PLL_LD: Rf pll_ld status. + * BUSY: 1: asic is busy execute rf programming. + */ +#define RFCSR 0x00f4 +#define RFCSR_VALUE FIELD32(0x00ffffff) +#define RFCSR_NUMBER_OF_BITS FIELD32(0x1f000000) +#define RFCSR_IF_SELECT FIELD32(0x20000000) +#define RFCSR_PLL_LD FIELD32(0x40000000) +#define RFCSR_BUSY FIELD32(0x80000000) + +/* + * LEDCSR: LED control register. + * ON_PERIOD: On period, default 70ms. + * OFF_PERIOD: Off period, default 30ms. + * LINK: 0: linkoff, 1: linkup. + * ACTIVITY: 0: idle, 1: active. + */ +#define LEDCSR 0x00f8 +#define LEDCSR_ON_PERIOD FIELD32(0x000000ff) +#define LEDCSR_OFF_PERIOD FIELD32(0x0000ff00) +#define LEDCSR_LINK FIELD32(0x00010000) +#define LEDCSR_ACTIVITY FIELD32(0x00020000) + +/* + * ASIC pointer information. + * RXPTR: Current RX ring address. + * TXPTR: Current Tx ring address. + * PRIPTR: Current Priority ring address. + * ATIMPTR: Current ATIM ring address. + */ +#define RXPTR 0x0100 +#define TXPTR 0x0104 +#define PRIPTR 0x0108 +#define ATIMPTR 0x010c + +/* + * GPIO and others. + */ + +/* + * GPIOCSR: GPIO control register. + * GPIOCSR_VALx: Actual GPIO pin x value + * GPIOCSR_DIRx: GPIO direction: 0 = output; 1 = input + */ +#define GPIOCSR 0x0120 +#define GPIOCSR_VAL0 FIELD32(0x00000001) +#define GPIOCSR_VAL1 FIELD32(0x00000002) +#define GPIOCSR_VAL2 FIELD32(0x00000004) +#define GPIOCSR_VAL3 FIELD32(0x00000008) +#define GPIOCSR_VAL4 FIELD32(0x00000010) +#define GPIOCSR_VAL5 FIELD32(0x00000020) +#define GPIOCSR_VAL6 FIELD32(0x00000040) +#define GPIOCSR_VAL7 FIELD32(0x00000080) +#define GPIOCSR_DIR0 FIELD32(0x00000100) +#define GPIOCSR_DIR1 FIELD32(0x00000200) +#define GPIOCSR_DIR2 FIELD32(0x00000400) +#define GPIOCSR_DIR3 FIELD32(0x00000800) +#define GPIOCSR_DIR4 FIELD32(0x00001000) +#define GPIOCSR_DIR5 FIELD32(0x00002000) +#define GPIOCSR_DIR6 FIELD32(0x00004000) +#define GPIOCSR_DIR7 FIELD32(0x00008000) + +/* + * BBPPCSR: BBP Pin control register. + */ +#define BBPPCSR 0x0124 + +/* + * BCNCSR1: Tx BEACON offset time control register. + * PRELOAD: Beacon timer offset in units of usec. + */ +#define BCNCSR1 0x0130 +#define BCNCSR1_PRELOAD FIELD32(0x0000ffff) + +/* + * MACCSR2: TX_PE to RX_PE turn-around time control register + * DELAY: RX_PE low width, in units of pci clock cycle. + */ +#define MACCSR2 0x0134 +#define MACCSR2_DELAY FIELD32(0x000000ff) + +/* + * ARCSR2: 1 Mbps ACK/CTS PLCP. + */ +#define ARCSR2 0x013c +#define ARCSR2_SIGNAL FIELD32(0x000000ff) +#define ARCSR2_SERVICE FIELD32(0x0000ff00) +#define ARCSR2_LENGTH_LOW FIELD32(0x00ff0000) +#define ARCSR2_LENGTH FIELD32(0xffff0000) + +/* + * ARCSR3: 2 Mbps ACK/CTS PLCP. + */ +#define ARCSR3 0x0140 +#define ARCSR3_SIGNAL FIELD32(0x000000ff) +#define ARCSR3_SERVICE FIELD32(0x0000ff00) +#define ARCSR3_LENGTH FIELD32(0xffff0000) + +/* + * ARCSR4: 5.5 Mbps ACK/CTS PLCP. + */ +#define ARCSR4 0x0144 +#define ARCSR4_SIGNAL FIELD32(0x000000ff) +#define ARCSR4_SERVICE FIELD32(0x0000ff00) +#define ARCSR4_LENGTH FIELD32(0xffff0000) + +/* + * ARCSR5: 11 Mbps ACK/CTS PLCP. + */ +#define ARCSR5 0x0148 +#define ARCSR5_SIGNAL FIELD32(0x000000ff) +#define ARCSR5_SERVICE FIELD32(0x0000ff00) +#define ARCSR5_LENGTH FIELD32(0xffff0000) + +/* + * BBP registers. + * The wordsize of the BBP is 8 bits. + */ + +/* + * R1: TX antenna control + */ +#define BBP_R1_TX_ANTENNA FIELD8(0x03) + +/* + * R4: RX antenna control + */ +#define BBP_R4_RX_ANTENNA FIELD8(0x06) + +/* + * RF registers + */ + +/* + * RF 1 + */ +#define RF1_TUNER FIELD32(0x00020000) + +/* + * RF 3 + */ +#define RF3_TUNER FIELD32(0x00000100) +#define RF3_TXPOWER FIELD32(0x00003e00) + +/* + * EEPROM content. + * The wordsize of the EEPROM is 16 bits. + */ + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_0 0x0002 +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR1 0x0003 +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_2 0x0004 +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM antenna. + * ANTENNA_NUM: Number of antenna's. + * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RF_TYPE: Rf_type of this adapter. + * LED_MODE: 0: default, 1: TX/RX activity,2: Single (ignore link), 3: rsvd. + * RX_AGCVGC: 0: disable, 1:enable BBP R13 tuning. + * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. + */ +#define EEPROM_ANTENNA 0x0b +#define EEPROM_ANTENNA_NUM FIELD16(0x0003) +#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) +#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) +#define EEPROM_ANTENNA_RF_TYPE FIELD16(0x0040) +#define EEPROM_ANTENNA_LED_MODE FIELD16(0x0180) +#define EEPROM_ANTENNA_RX_AGCVGC_TUNING FIELD16(0x0200) +#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_START 0x0c +#define EEPROM_BBP_SIZE 7 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* + * EEPROM TXPOWER + */ +#define EEPROM_TXPOWER_START 0x13 +#define EEPROM_TXPOWER_SIZE 7 +#define EEPROM_TXPOWER_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_2 FIELD16(0xff00) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE (8 * sizeof(__le32)) +#define RXD_DESC_SIZE (8 * sizeof(__le32)) + +/* + * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. + */ + +/* + * Word0 + */ +#define TXD_W0_OWNER_NIC FIELD32(0x00000001) +#define TXD_W0_VALID FIELD32(0x00000002) +#define TXD_W0_RESULT FIELD32(0x0000001c) +#define TXD_W0_RETRY_COUNT FIELD32(0x000000e0) +#define TXD_W0_MORE_FRAG FIELD32(0x00000100) +#define TXD_W0_ACK FIELD32(0x00000200) +#define TXD_W0_TIMESTAMP FIELD32(0x00000400) +#define TXD_W0_RTS FIELD32(0x00000800) +#define TXD_W0_IFS FIELD32(0x00006000) +#define TXD_W0_RETRY_MODE FIELD32(0x00008000) +#define TXD_W0_AGC FIELD32(0x00ff0000) +#define TXD_W0_R2 FIELD32(0xff000000) + +/* + * Word1 + */ +#define TXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) + +/* + * Word2 + */ +#define TXD_W2_BUFFER_LENGTH FIELD32(0x0000ffff) +#define TXD_W2_DATABYTE_COUNT FIELD32(0xffff0000) + +/* + * Word3 & 4: PLCP information + * The PLCP values should be treated as if they were BBP values. + */ +#define TXD_W3_PLCP_SIGNAL FIELD32(0x000000ff) +#define TXD_W3_PLCP_SIGNAL_REGNUM FIELD32(0x00007f00) +#define TXD_W3_PLCP_SIGNAL_BUSY FIELD32(0x00008000) +#define TXD_W3_PLCP_SERVICE FIELD32(0x00ff0000) +#define TXD_W3_PLCP_SERVICE_REGNUM FIELD32(0x7f000000) +#define TXD_W3_PLCP_SERVICE_BUSY FIELD32(0x80000000) + +#define TXD_W4_PLCP_LENGTH_LOW FIELD32(0x000000ff) +#define TXD_W3_PLCP_LENGTH_LOW_REGNUM FIELD32(0x00007f00) +#define TXD_W3_PLCP_LENGTH_LOW_BUSY FIELD32(0x00008000) +#define TXD_W4_PLCP_LENGTH_HIGH FIELD32(0x00ff0000) +#define TXD_W3_PLCP_LENGTH_HIGH_REGNUM FIELD32(0x7f000000) +#define TXD_W3_PLCP_LENGTH_HIGH_BUSY FIELD32(0x80000000) + +/* + * Word5 + */ +#define TXD_W5_BBCR4 FIELD32(0x0000ffff) +#define TXD_W5_AGC_REG FIELD32(0x007f0000) +#define TXD_W5_AGC_REG_VALID FIELD32(0x00800000) +#define TXD_W5_XXX_REG FIELD32(0x7f000000) +#define TXD_W5_XXX_REG_VALID FIELD32(0x80000000) + +/* + * Word6 + */ +#define TXD_W6_SK_BUFF FIELD32(0xffffffff) + +/* + * Word7 + */ +#define TXD_W7_RESERVED FIELD32(0xffffffff) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + */ +#define RXD_W0_OWNER_NIC FIELD32(0x00000001) +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) +#define RXD_W0_MULTICAST FIELD32(0x00000004) +#define RXD_W0_BROADCAST FIELD32(0x00000008) +#define RXD_W0_MY_BSS FIELD32(0x00000010) +#define RXD_W0_CRC_ERROR FIELD32(0x00000020) +#define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) +#define RXD_W0_DATABYTE_COUNT FIELD32(0xffff0000) + +/* + * Word1 + */ +#define RXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) + +/* + * Word2 + */ +#define RXD_W2_BUFFER_LENGTH FIELD32(0x0000ffff) +#define RXD_W2_BBR0 FIELD32(0x00ff0000) +#define RXD_W2_SIGNAL FIELD32(0xff000000) + +/* + * Word3 + */ +#define RXD_W3_RSSI FIELD32(0x000000ff) +#define RXD_W3_BBR3 FIELD32(0x0000ff00) +#define RXD_W3_BBR4 FIELD32(0x00ff0000) +#define RXD_W3_BBR5 FIELD32(0xff000000) + +/* + * Word4 + */ +#define RXD_W4_RX_END_TIME FIELD32(0xffffffff) + +/* + * Word5 & 6 & 7: Reserved + */ +#define RXD_W5_RESERVED FIELD32(0xffffffff) +#define RXD_W6_RESERVED FIELD32(0xffffffff) +#define RXD_W7_RESERVED FIELD32(0xffffffff) + +/* + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. + * NOTE: Logics in rt2400pci for txpower are reversed + * compared to the other rt2x00 drivers. A higher txpower + * value means that the txpower must be lowered. This is + * important when converting the value coming from the + * mac80211 stack to the rt2400 acceptable value. + */ +#define MIN_TXPOWER 31 +#define MAX_TXPOWER 62 +#define DEFAULT_TXPOWER 39 + +#define __CLAMP_TX(__txpower) \ + clamp_t(char, (__txpower), MIN_TXPOWER, MAX_TXPOWER) + +#define TXPOWER_FROM_DEV(__txpower) \ + ((__CLAMP_TX(__txpower) - MAX_TXPOWER) + MIN_TXPOWER) + +#define TXPOWER_TO_DEV(__txpower) \ + (MAX_TXPOWER - (__CLAMP_TX(__txpower) - MIN_TXPOWER)) + +#endif /* RT2400PCI_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500pci.c b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c new file mode 100644 index 000000000000..1a6740b4d396 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c @@ -0,0 +1,2148 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2500pci + Abstract: rt2500pci device specific routines. + Supported chipsets: RT2560. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2x00pci.h" +#include "rt2500pci.h" + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2x00mmio_register_read and rt2x00mmio_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers BBPCSR and RFCSR to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attampt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + */ +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), RFCSR, RFCSR_BUSY, (__reg)) + +static void rt2500pci_bbp_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBPCSR_VALUE, value); + rt2x00_set_field32(®, BBPCSR_REGNUM, word); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); + + rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2500pci_bbp_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u8 *value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBPCSR_REGNUM, word); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); + + rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); + + WAIT_FOR_BBP(rt2x00dev, ®); + } + + *value = rt2x00_get_field32(reg, BBPCSR_VALUE); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2500pci_rf_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u32 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RFCSR_VALUE, value); + rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); + rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); + rt2x00_set_field32(®, RFCSR_BUSY, 1); + + rt2x00mmio_register_write(rt2x00dev, RFCSR, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2500pci_eepromregister_read(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, CSR21, ®); + + eeprom->reg_data_in = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_IN); + eeprom->reg_data_out = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_OUT); + eeprom->reg_data_clock = + !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_CLOCK); + eeprom->reg_chip_select = + !!rt2x00_get_field32(reg, CSR21_EEPROM_CHIP_SELECT); +} + +static void rt2500pci_eepromregister_write(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg = 0; + + rt2x00_set_field32(®, CSR21_EEPROM_DATA_IN, !!eeprom->reg_data_in); + rt2x00_set_field32(®, CSR21_EEPROM_DATA_OUT, !!eeprom->reg_data_out); + rt2x00_set_field32(®, CSR21_EEPROM_DATA_CLOCK, + !!eeprom->reg_data_clock); + rt2x00_set_field32(®, CSR21_EEPROM_CHIP_SELECT, + !!eeprom->reg_chip_select); + + rt2x00mmio_register_write(rt2x00dev, CSR21, reg); +} + +#ifdef CONFIG_RT2X00_LIB_DEBUGFS +static const struct rt2x00debug rt2500pci_rt2x00debug = { + .owner = THIS_MODULE, + .csr = { + .read = rt2x00mmio_register_read, + .write = rt2x00mmio_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, + .word_size = sizeof(u32), + .word_count = CSR_REG_SIZE / sizeof(u32), + }, + .eeprom = { + .read = rt2x00_eeprom_read, + .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .bbp = { + .read = rt2500pci_bbp_read, + .write = rt2500pci_bbp_write, + .word_base = BBP_BASE, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, + .rf = { + .read = rt2x00_rf_read, + .write = rt2500pci_rf_write, + .word_base = RF_BASE, + .word_size = sizeof(u32), + .word_count = RF_SIZE / sizeof(u32), + }, +}; +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ + +static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); + return rt2x00_get_field32(reg, GPIOCSR_VAL0); +} + +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt2500pci_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + u32 reg; + + rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); + + if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) + rt2x00_set_field32(®, LEDCSR_LINK, enabled); + else if (led->type == LED_TYPE_ACTIVITY) + rt2x00_set_field32(®, LEDCSR_ACTIVITY, enabled); + + rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); +} + +static int rt2500pci_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + u32 reg; + + rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); + rt2x00_set_field32(®, LEDCSR_ON_PERIOD, *delay_on); + rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, *delay_off); + rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); + + return 0; +} + +static void rt2500pci_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + enum led_type type) +{ + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt2500pci_brightness_set; + led->led_dev.blink_set = rt2500pci_blink_set; + led->flags = LED_INITIALIZED; +} +#endif /* CONFIG_RT2X00_LIB_LEDS */ + +/* + * Configuration handlers. + */ +static void rt2500pci_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) +{ + u32 reg; + + /* + * Start configuration steps. + * Note that the version error will always be dropped + * and broadcast frames will always be accepted since + * there is no filter for it at this time. + */ + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DROP_CRC, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, 1); + rt2x00_set_field32(®, RXCSR0_DROP_TODS, + !rt2x00dev->intf_ap_count); + rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); + rt2x00_set_field32(®, RXCSR0_DROP_MCAST, + !(filter_flags & FIF_ALLMULTI)); + rt2x00_set_field32(®, RXCSR0_DROP_BCAST, 0); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); +} + +static void rt2500pci_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags) +{ + struct data_queue *queue = rt2x00dev->bcn; + unsigned int bcn_preload; + u32 reg; + + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable beacon config + */ + bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); + rt2x00mmio_register_read(rt2x00dev, BCNCSR1, ®); + rt2x00_set_field32(®, BCNCSR1_PRELOAD, bcn_preload); + rt2x00_set_field32(®, BCNCSR1_BEACON_CWMIN, queue->cw_min); + rt2x00mmio_register_write(rt2x00dev, BCNCSR1, reg); + + /* + * Enable synchronisation. + */ + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_SYNC, conf->sync); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + } + + if (flags & CONFIG_UPDATE_MAC) + rt2x00mmio_register_multiwrite(rt2x00dev, CSR3, + conf->mac, sizeof(conf->mac)); + + if (flags & CONFIG_UPDATE_BSSID) + rt2x00mmio_register_multiwrite(rt2x00dev, CSR5, + conf->bssid, sizeof(conf->bssid)); +} + +static void rt2500pci_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed) +{ + int preamble_mask; + u32 reg; + + /* + * When short preamble is enabled, we should set bit 0x08 + */ + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + preamble_mask = erp->short_preamble << 3; + + rt2x00mmio_register_read(rt2x00dev, TXCSR1, ®); + rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, 0x162); + rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, 0xa2); + rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR1, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR2, ®); + rt2x00_set_field32(®, ARCSR2_SIGNAL, 0x00); + rt2x00_set_field32(®, ARCSR2_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 10)); + rt2x00mmio_register_write(rt2x00dev, ARCSR2, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR3, ®); + rt2x00_set_field32(®, ARCSR3_SIGNAL, 0x01 | preamble_mask); + rt2x00_set_field32(®, ARCSR3_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 20)); + rt2x00mmio_register_write(rt2x00dev, ARCSR3, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR4, ®); + rt2x00_set_field32(®, ARCSR4_SIGNAL, 0x02 | preamble_mask); + rt2x00_set_field32(®, ARCSR4_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 55)); + rt2x00mmio_register_write(rt2x00dev, ARCSR4, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR5, ®); + rt2x00_set_field32(®, ARCSR5_SIGNAL, 0x03 | preamble_mask); + rt2x00_set_field32(®, ARCSR5_SERVICE, 0x84); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 110)); + rt2x00mmio_register_write(rt2x00dev, ARCSR5, reg); + } + + if (changed & BSS_CHANGED_BASIC_RATES) + rt2x00mmio_register_write(rt2x00dev, ARCSR1, erp->basic_rates); + + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_SLOT_TIME, erp->slot_time); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR18, ®); + rt2x00_set_field32(®, CSR18_SIFS, erp->sifs); + rt2x00_set_field32(®, CSR18_PIFS, erp->pifs); + rt2x00mmio_register_write(rt2x00dev, CSR18, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR19, ®); + rt2x00_set_field32(®, CSR19_DIFS, erp->difs); + rt2x00_set_field32(®, CSR19_EIFS, erp->eifs); + rt2x00mmio_register_write(rt2x00dev, CSR19, reg); + } + + if (changed & BSS_CHANGED_BEACON_INT) { + rt2x00mmio_register_read(rt2x00dev, CSR12, ®); + rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, + erp->beacon_int * 16); + rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, + erp->beacon_int * 16); + rt2x00mmio_register_write(rt2x00dev, CSR12, reg); + } + +} + +static void rt2500pci_config_ant(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) +{ + u32 reg; + u8 r14; + u8 r2; + + /* + * We should never come here because rt2x00lib is supposed + * to catch this and send us the correct antenna explicitely. + */ + BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || + ant->tx == ANTENNA_SW_DIVERSITY); + + rt2x00mmio_register_read(rt2x00dev, BBPCSR1, ®); + rt2500pci_bbp_read(rt2x00dev, 14, &r14); + rt2500pci_bbp_read(rt2x00dev, 2, &r2); + + /* + * Configure the TX antenna. + */ + switch (ant->tx) { + case ANTENNA_A: + rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0); + rt2x00_set_field32(®, BBPCSR1_CCK, 0); + rt2x00_set_field32(®, BBPCSR1_OFDM, 0); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2); + rt2x00_set_field32(®, BBPCSR1_CCK, 2); + rt2x00_set_field32(®, BBPCSR1_OFDM, 2); + break; + } + + /* + * Configure the RX antenna. + */ + switch (ant->rx) { + case ANTENNA_A: + rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2); + break; + } + + /* + * RT2525E and RT5222 need to flip TX I/Q + */ + if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) { + rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1); + rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 1); + rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 1); + + /* + * RT2525E does not need RX I/Q Flip. + */ + if (rt2x00_rf(rt2x00dev, RF2525E)) + rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0); + } else { + rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 0); + rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 0); + } + + rt2x00mmio_register_write(rt2x00dev, BBPCSR1, reg); + rt2500pci_bbp_write(rt2x00dev, 14, r14); + rt2500pci_bbp_write(rt2x00dev, 2, r2); +} + +static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev, + struct rf_channel *rf, const int txpower) +{ + u8 r70; + + /* + * Set TXpower. + */ + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); + + /* + * Switch on tuning bits. + * For RT2523 devices we do not need to update the R1 register. + */ + if (!rt2x00_rf(rt2x00dev, RF2523)) + rt2x00_set_field32(&rf->rf1, RF1_TUNER, 1); + rt2x00_set_field32(&rf->rf3, RF3_TUNER, 1); + + /* + * For RT2525 we should first set the channel to half band higher. + */ + if (rt2x00_rf(rt2x00dev, RF2525)) { + static const u32 vals[] = { + 0x00080cbe, 0x00080d02, 0x00080d06, 0x00080d0a, + 0x00080d0e, 0x00080d12, 0x00080d16, 0x00080d1a, + 0x00080d1e, 0x00080d22, 0x00080d26, 0x00080d2a, + 0x00080d2e, 0x00080d3a + }; + + rt2500pci_rf_write(rt2x00dev, 1, rf->rf1); + rt2500pci_rf_write(rt2x00dev, 2, vals[rf->channel - 1]); + rt2500pci_rf_write(rt2x00dev, 3, rf->rf3); + if (rf->rf4) + rt2500pci_rf_write(rt2x00dev, 4, rf->rf4); + } + + rt2500pci_rf_write(rt2x00dev, 1, rf->rf1); + rt2500pci_rf_write(rt2x00dev, 2, rf->rf2); + rt2500pci_rf_write(rt2x00dev, 3, rf->rf3); + if (rf->rf4) + rt2500pci_rf_write(rt2x00dev, 4, rf->rf4); + + /* + * Channel 14 requires the Japan filter bit to be set. + */ + r70 = 0x46; + rt2x00_set_field8(&r70, BBP_R70_JAPAN_FILTER, rf->channel == 14); + rt2500pci_bbp_write(rt2x00dev, 70, r70); + + msleep(1); + + /* + * Switch off tuning bits. + * For RT2523 devices we do not need to update the R1 register. + */ + if (!rt2x00_rf(rt2x00dev, RF2523)) { + rt2x00_set_field32(&rf->rf1, RF1_TUNER, 0); + rt2500pci_rf_write(rt2x00dev, 1, rf->rf1); + } + + rt2x00_set_field32(&rf->rf3, RF3_TUNER, 0); + rt2500pci_rf_write(rt2x00dev, 3, rf->rf3); + + /* + * Clear false CRC during channel switch. + */ + rt2x00mmio_register_read(rt2x00dev, CNT0, &rf->rf1); +} + +static void rt2500pci_config_txpower(struct rt2x00_dev *rt2x00dev, + const int txpower) +{ + u32 rf3; + + rt2x00_rf_read(rt2x00dev, 3, &rf3); + rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); + rt2500pci_rf_write(rt2x00dev, 3, rf3); +} + +static void rt2500pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_LONG_RETRY, + libconf->conf->long_frame_max_tx_count); + rt2x00_set_field32(®, CSR11_SHORT_RETRY, + libconf->conf->short_frame_max_tx_count); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); +} + +static void rt2500pci_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; + u32 reg; + + if (state == STATE_SLEEP) { + rt2x00mmio_register_read(rt2x00dev, CSR20, ®); + rt2x00_set_field32(®, CSR20_DELAY_AFTER_TBCN, + (rt2x00dev->beacon_int - 20) * 16); + rt2x00_set_field32(®, CSR20_TBCN_BEFORE_WAKEUP, + libconf->conf->listen_interval - 1); + + /* We must first disable autowake before it can be enabled */ + rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); + + rt2x00_set_field32(®, CSR20_AUTOWAKE, 1); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); + } else { + rt2x00mmio_register_read(rt2x00dev, CSR20, ®); + rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); + } + + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); +} + +static void rt2500pci_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags) +{ + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) + rt2500pci_config_channel(rt2x00dev, &libconf->rf, + libconf->conf->power_level); + if ((flags & IEEE80211_CONF_CHANGE_POWER) && + !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) + rt2500pci_config_txpower(rt2x00dev, + libconf->conf->power_level); + if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + rt2500pci_config_retry_limit(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt2500pci_config_ps(rt2x00dev, libconf); +} + +/* + * Link tuning + */ +static void rt2500pci_link_stats(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) +{ + u32 reg; + + /* + * Update FCS error count from register. + */ + rt2x00mmio_register_read(rt2x00dev, CNT0, ®); + qual->rx_failed = rt2x00_get_field32(reg, CNT0_FCS_ERROR); + + /* + * Update False CCA count from register. + */ + rt2x00mmio_register_read(rt2x00dev, CNT3, ®); + qual->false_cca = rt2x00_get_field32(reg, CNT3_FALSE_CCA); +} + +static inline void rt2500pci_set_vgc(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, u8 vgc_level) +{ + if (qual->vgc_level_reg != vgc_level) { + rt2500pci_bbp_write(rt2x00dev, 17, vgc_level); + qual->vgc_level = vgc_level; + qual->vgc_level_reg = vgc_level; + } +} + +static void rt2500pci_reset_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) +{ + rt2500pci_set_vgc(rt2x00dev, qual, 0x48); +} + +static void rt2500pci_link_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, const u32 count) +{ + /* + * To prevent collisions with MAC ASIC on chipsets + * up to version C the link tuning should halt after 20 + * seconds while being associated. + */ + if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D && + rt2x00dev->intf_associated && count > 20) + return; + + /* + * Chipset versions C and lower should directly continue + * to the dynamic CCA tuning. Chipset version D and higher + * should go straight to dynamic CCA tuning when they + * are not associated. + */ + if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D || + !rt2x00dev->intf_associated) + goto dynamic_cca_tune; + + /* + * A too low RSSI will cause too much false CCA which will + * then corrupt the R17 tuning. To remidy this the tuning should + * be stopped (While making sure the R17 value will not exceed limits) + */ + if (qual->rssi < -80 && count > 20) { + if (qual->vgc_level_reg >= 0x41) + rt2500pci_set_vgc(rt2x00dev, qual, qual->vgc_level); + return; + } + + /* + * Special big-R17 for short distance + */ + if (qual->rssi >= -58) { + rt2500pci_set_vgc(rt2x00dev, qual, 0x50); + return; + } + + /* + * Special mid-R17 for middle distance + */ + if (qual->rssi >= -74) { + rt2500pci_set_vgc(rt2x00dev, qual, 0x41); + return; + } + + /* + * Leave short or middle distance condition, restore r17 + * to the dynamic tuning range. + */ + if (qual->vgc_level_reg >= 0x41) { + rt2500pci_set_vgc(rt2x00dev, qual, qual->vgc_level); + return; + } + +dynamic_cca_tune: + + /* + * R17 is inside the dynamic tuning range, + * start tuning the link based on the false cca counter. + */ + if (qual->false_cca > 512 && qual->vgc_level_reg < 0x40) + rt2500pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level_reg); + else if (qual->false_cca < 100 && qual->vgc_level_reg > 0x32) + rt2500pci_set_vgc(rt2x00dev, qual, --qual->vgc_level_reg); +} + +/* + * Queue handlers. + */ +static void rt2500pci_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 0); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); + rt2x00_set_field32(®, CSR14_TBCN, 1); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + break; + default: + break; + } +} + +static void rt2500pci_kick_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_PRIO, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_AC_VI: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_TX, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_ATIM: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_ATIM, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + default: + break; + } +} + +static void rt2500pci_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_ATIM: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_ABORT, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 1); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); + rt2x00_set_field32(®, CSR14_TBCN, 0); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + + /* + * Wait for possibly running tbtt tasklets. + */ + tasklet_kill(&rt2x00dev->tbtt_tasklet); + break; + default: + break; + } +} + +/* + * Initialization functions. + */ +static bool rt2500pci_get_entry_state(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 0, &word); + + return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); + + return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + rt2x00_get_field32(word, TXD_W0_VALID)); + } +} + +static void rt2500pci_clear_entry(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); + rt2x00_desc_write(entry_priv->desc, 1, word); + + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(entry_priv->desc, 0, word); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); + rt2x00_desc_write(entry_priv->desc, 0, word); + } +} + +static int rt2500pci_init_queues(struct rt2x00_dev *rt2x00dev) +{ + struct queue_entry_priv_mmio *entry_priv; + u32 reg; + + /* + * Initialize registers. + */ + rt2x00mmio_register_read(rt2x00dev, TXCSR2, ®); + rt2x00_set_field32(®, TXCSR2_TXD_SIZE, rt2x00dev->tx[0].desc_size); + rt2x00_set_field32(®, TXCSR2_NUM_TXD, rt2x00dev->tx[1].limit); + rt2x00_set_field32(®, TXCSR2_NUM_ATIM, rt2x00dev->atim->limit); + rt2x00_set_field32(®, TXCSR2_NUM_PRIO, rt2x00dev->tx[0].limit); + rt2x00mmio_register_write(rt2x00dev, TXCSR2, reg); + + entry_priv = rt2x00dev->tx[1].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR3, ®); + rt2x00_set_field32(®, TXCSR3_TX_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR3, reg); + + entry_priv = rt2x00dev->tx[0].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR5, ®); + rt2x00_set_field32(®, TXCSR5_PRIO_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR5, reg); + + entry_priv = rt2x00dev->atim->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR4, ®); + rt2x00_set_field32(®, TXCSR4_ATIM_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR4, reg); + + entry_priv = rt2x00dev->bcn->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR6, ®); + rt2x00_set_field32(®, TXCSR6_BEACON_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR6, reg); + + rt2x00mmio_register_read(rt2x00dev, RXCSR1, ®); + rt2x00_set_field32(®, RXCSR1_RXD_SIZE, rt2x00dev->rx->desc_size); + rt2x00_set_field32(®, RXCSR1_NUM_RXD, rt2x00dev->rx->limit); + rt2x00mmio_register_write(rt2x00dev, RXCSR1, reg); + + entry_priv = rt2x00dev->rx->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, RXCSR2, ®); + rt2x00_set_field32(®, RXCSR2_RX_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, RXCSR2, reg); + + return 0; +} + +static int rt2500pci_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00mmio_register_write(rt2x00dev, PSCSR0, 0x00020002); + rt2x00mmio_register_write(rt2x00dev, PSCSR1, 0x00000002); + rt2x00mmio_register_write(rt2x00dev, PSCSR2, 0x00020002); + rt2x00mmio_register_write(rt2x00dev, PSCSR3, 0x00000002); + + rt2x00mmio_register_read(rt2x00dev, TIMECSR, ®); + rt2x00_set_field32(®, TIMECSR_US_COUNT, 33); + rt2x00_set_field32(®, TIMECSR_US_64_COUNT, 63); + rt2x00_set_field32(®, TIMECSR_BEACON_EXPECT, 0); + rt2x00mmio_register_write(rt2x00dev, TIMECSR, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR9, ®); + rt2x00_set_field32(®, CSR9_MAX_FRAME_UNIT, + rt2x00dev->rx->data_size / 128); + rt2x00mmio_register_write(rt2x00dev, CSR9, reg); + + /* + * Always use CWmin and CWmax set in descriptor. + */ + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_CW_SELECT, 0); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); + rt2x00_set_field32(®, CSR14_TSF_SYNC, 0); + rt2x00_set_field32(®, CSR14_TBCN, 0); + rt2x00_set_field32(®, CSR14_TCFP, 0); + rt2x00_set_field32(®, CSR14_TATIMW, 0); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00_set_field32(®, CSR14_CFP_COUNT_PRELOAD, 0); + rt2x00_set_field32(®, CSR14_TBCM_PRELOAD, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + + rt2x00mmio_register_write(rt2x00dev, CNT3, 0); + + rt2x00mmio_register_read(rt2x00dev, TXCSR8, ®); + rt2x00_set_field32(®, TXCSR8_BBP_ID0, 10); + rt2x00_set_field32(®, TXCSR8_BBP_ID0_VALID, 1); + rt2x00_set_field32(®, TXCSR8_BBP_ID1, 11); + rt2x00_set_field32(®, TXCSR8_BBP_ID1_VALID, 1); + rt2x00_set_field32(®, TXCSR8_BBP_ID2, 13); + rt2x00_set_field32(®, TXCSR8_BBP_ID2_VALID, 1); + rt2x00_set_field32(®, TXCSR8_BBP_ID3, 12); + rt2x00_set_field32(®, TXCSR8_BBP_ID3_VALID, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR8, reg); + + rt2x00mmio_register_read(rt2x00dev, ARTCSR0, ®); + rt2x00_set_field32(®, ARTCSR0_ACK_CTS_1MBS, 112); + rt2x00_set_field32(®, ARTCSR0_ACK_CTS_2MBS, 56); + rt2x00_set_field32(®, ARTCSR0_ACK_CTS_5_5MBS, 20); + rt2x00_set_field32(®, ARTCSR0_ACK_CTS_11MBS, 10); + rt2x00mmio_register_write(rt2x00dev, ARTCSR0, reg); + + rt2x00mmio_register_read(rt2x00dev, ARTCSR1, ®); + rt2x00_set_field32(®, ARTCSR1_ACK_CTS_6MBS, 45); + rt2x00_set_field32(®, ARTCSR1_ACK_CTS_9MBS, 37); + rt2x00_set_field32(®, ARTCSR1_ACK_CTS_12MBS, 33); + rt2x00_set_field32(®, ARTCSR1_ACK_CTS_18MBS, 29); + rt2x00mmio_register_write(rt2x00dev, ARTCSR1, reg); + + rt2x00mmio_register_read(rt2x00dev, ARTCSR2, ®); + rt2x00_set_field32(®, ARTCSR2_ACK_CTS_24MBS, 29); + rt2x00_set_field32(®, ARTCSR2_ACK_CTS_36MBS, 25); + rt2x00_set_field32(®, ARTCSR2_ACK_CTS_48MBS, 25); + rt2x00_set_field32(®, ARTCSR2_ACK_CTS_54MBS, 25); + rt2x00mmio_register_write(rt2x00dev, ARTCSR2, reg); + + rt2x00mmio_register_read(rt2x00dev, RXCSR3, ®); + rt2x00_set_field32(®, RXCSR3_BBP_ID0, 47); /* CCK Signal */ + rt2x00_set_field32(®, RXCSR3_BBP_ID0_VALID, 1); + rt2x00_set_field32(®, RXCSR3_BBP_ID1, 51); /* Rssi */ + rt2x00_set_field32(®, RXCSR3_BBP_ID1_VALID, 1); + rt2x00_set_field32(®, RXCSR3_BBP_ID2, 42); /* OFDM Rate */ + rt2x00_set_field32(®, RXCSR3_BBP_ID2_VALID, 1); + rt2x00_set_field32(®, RXCSR3_BBP_ID3, 51); /* RSSI */ + rt2x00_set_field32(®, RXCSR3_BBP_ID3_VALID, 1); + rt2x00mmio_register_write(rt2x00dev, RXCSR3, reg); + + rt2x00mmio_register_read(rt2x00dev, PCICSR, ®); + rt2x00_set_field32(®, PCICSR_BIG_ENDIAN, 0); + rt2x00_set_field32(®, PCICSR_RX_TRESHOLD, 0); + rt2x00_set_field32(®, PCICSR_TX_TRESHOLD, 3); + rt2x00_set_field32(®, PCICSR_BURST_LENTH, 1); + rt2x00_set_field32(®, PCICSR_ENABLE_CLK, 1); + rt2x00_set_field32(®, PCICSR_READ_MULTIPLE, 1); + rt2x00_set_field32(®, PCICSR_WRITE_INVALID, 1); + rt2x00mmio_register_write(rt2x00dev, PCICSR, reg); + + rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); + + rt2x00mmio_register_write(rt2x00dev, GPIOCSR, 0x0000ff00); + rt2x00mmio_register_write(rt2x00dev, TESTCSR, 0x000000f0); + + if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) + return -EBUSY; + + rt2x00mmio_register_write(rt2x00dev, MACCSR0, 0x00213223); + rt2x00mmio_register_write(rt2x00dev, MACCSR1, 0x00235518); + + rt2x00mmio_register_read(rt2x00dev, MACCSR2, ®); + rt2x00_set_field32(®, MACCSR2_DELAY, 64); + rt2x00mmio_register_write(rt2x00dev, MACCSR2, reg); + + rt2x00mmio_register_read(rt2x00dev, RALINKCSR, ®); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA0, 17); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID0, 26); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_VALID0, 1); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA1, 0); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID1, 26); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_VALID1, 1); + rt2x00mmio_register_write(rt2x00dev, RALINKCSR, reg); + + rt2x00mmio_register_write(rt2x00dev, BBPCSR1, 0x82188200); + + rt2x00mmio_register_write(rt2x00dev, TXACKCSR0, 0x00000020); + + rt2x00mmio_register_read(rt2x00dev, CSR1, ®); + rt2x00_set_field32(®, CSR1_SOFT_RESET, 1); + rt2x00_set_field32(®, CSR1_BBP_RESET, 0); + rt2x00_set_field32(®, CSR1_HOST_READY, 0); + rt2x00mmio_register_write(rt2x00dev, CSR1, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR1, ®); + rt2x00_set_field32(®, CSR1_SOFT_RESET, 0); + rt2x00_set_field32(®, CSR1_HOST_READY, 1); + rt2x00mmio_register_write(rt2x00dev, CSR1, reg); + + /* + * We must clear the FCS and FIFO error count. + * These registers are cleared on read, + * so we may pass a useless variable to store the value. + */ + rt2x00mmio_register_read(rt2x00dev, CNT0, ®); + rt2x00mmio_register_read(rt2x00dev, CNT4, ®); + + return 0; +} + +static int rt2500pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u8 value; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2500pci_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + return 0; + udelay(REGISTER_BUSY_DELAY); + } + + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); + return -EACCES; +} + +static int rt2500pci_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + if (unlikely(rt2500pci_wait_bbp_ready(rt2x00dev))) + return -EACCES; + + rt2500pci_bbp_write(rt2x00dev, 3, 0x02); + rt2500pci_bbp_write(rt2x00dev, 4, 0x19); + rt2500pci_bbp_write(rt2x00dev, 14, 0x1c); + rt2500pci_bbp_write(rt2x00dev, 15, 0x30); + rt2500pci_bbp_write(rt2x00dev, 16, 0xac); + rt2500pci_bbp_write(rt2x00dev, 18, 0x18); + rt2500pci_bbp_write(rt2x00dev, 19, 0xff); + rt2500pci_bbp_write(rt2x00dev, 20, 0x1e); + rt2500pci_bbp_write(rt2x00dev, 21, 0x08); + rt2500pci_bbp_write(rt2x00dev, 22, 0x08); + rt2500pci_bbp_write(rt2x00dev, 23, 0x08); + rt2500pci_bbp_write(rt2x00dev, 24, 0x70); + rt2500pci_bbp_write(rt2x00dev, 25, 0x40); + rt2500pci_bbp_write(rt2x00dev, 26, 0x08); + rt2500pci_bbp_write(rt2x00dev, 27, 0x23); + rt2500pci_bbp_write(rt2x00dev, 30, 0x10); + rt2500pci_bbp_write(rt2x00dev, 31, 0x2b); + rt2500pci_bbp_write(rt2x00dev, 32, 0xb9); + rt2500pci_bbp_write(rt2x00dev, 34, 0x12); + rt2500pci_bbp_write(rt2x00dev, 35, 0x50); + rt2500pci_bbp_write(rt2x00dev, 39, 0xc4); + rt2500pci_bbp_write(rt2x00dev, 40, 0x02); + rt2500pci_bbp_write(rt2x00dev, 41, 0x60); + rt2500pci_bbp_write(rt2x00dev, 53, 0x10); + rt2500pci_bbp_write(rt2x00dev, 54, 0x18); + rt2500pci_bbp_write(rt2x00dev, 56, 0x08); + rt2500pci_bbp_write(rt2x00dev, 57, 0x10); + rt2500pci_bbp_write(rt2x00dev, 58, 0x08); + rt2500pci_bbp_write(rt2x00dev, 61, 0x6d); + rt2500pci_bbp_write(rt2x00dev, 62, 0x10); + + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + rt2500pci_bbp_write(rt2x00dev, reg_id, value); + } + } + + return 0; +} + +/* + * Device state switch handlers. + */ +static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int mask = (state == STATE_RADIO_IRQ_OFF); + u32 reg; + unsigned long flags; + + /* + * When interrupts are being enabled, the interrupt registers + * should clear the register to assure a clean state. + */ + if (state == STATE_RADIO_IRQ_ON) { + rt2x00mmio_register_read(rt2x00dev, CSR7, ®); + rt2x00mmio_register_write(rt2x00dev, CSR7, reg); + } + + /* + * Only toggle the interrupts bits we are going to use. + * Non-checked interrupt bits are disabled by default. + */ + spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); + rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); + rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, mask); + rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); + rt2x00_set_field32(®, CSR8_RXDONE, mask); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); + + if (state == STATE_RADIO_IRQ_OFF) { + /* + * Ensure that all tasklets are finished. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + } +} + +static int rt2500pci_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + /* + * Initialize all registers. + */ + if (unlikely(rt2500pci_init_queues(rt2x00dev) || + rt2500pci_init_registers(rt2x00dev) || + rt2500pci_init_bbp(rt2x00dev))) + return -EIO; + + return 0; +} + +static void rt2500pci_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + /* + * Disable power + */ + rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0); +} + +static int rt2500pci_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u32 reg, reg2; + unsigned int i; + char put_to_sleep; + char bbp_state; + char rf_state; + + put_to_sleep = (state != STATE_AWAKE); + + rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®); + rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1); + rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state); + rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state); + rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep); + rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); + + /* + * Device is not guaranteed to be in the requested state yet. + * We must wait until the register indicates that the + * device has entered the correct state. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®2); + bbp_state = rt2x00_get_field32(reg2, PWRCSR1_BBP_CURR_STATE); + rf_state = rt2x00_get_field32(reg2, PWRCSR1_RF_CURR_STATE); + if (bbp_state == state && rf_state == state) + return 0; + rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); + msleep(10); + } + + return -EBUSY; +} + +static int rt2500pci_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + retval = rt2500pci_enable_radio(rt2x00dev); + break; + case STATE_RADIO_OFF: + rt2500pci_disable_radio(rt2x00dev); + break; + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt2500pci_toggle_irq(rt2x00dev, state); + break; + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + retval = rt2500pci_set_state(rt2x00dev, state); + break; + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +/* + * TX descriptor initialization + */ +static void rt2500pci_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *txd = entry_priv->desc; + u32 word; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_IV_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(&word, TXD_W2_AIFS, entry->queue->aifs); + rt2x00_set_field32(&word, TXD_W2_CWMIN, entry->queue->cw_min); + rt2x00_set_field32(&word, TXD_W2_CWMAX, entry->queue->cw_max); + rt2x00_desc_write(txd, 2, word); + + rt2x00_desc_read(txd, 3, &word); + rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, txdesc->u.plcp.signal); + rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, txdesc->u.plcp.service); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW, + txdesc->u.plcp.length_low); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH, + txdesc->u.plcp.length_high); + rt2x00_desc_write(txd, 3, word); + + rt2x00_desc_read(txd, 10, &word); + rt2x00_set_field32(&word, TXD_W10_RTS, + test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)); + rt2x00_desc_write(txd, 10, word); + + /* + * Writing TXD word 0 must the last to prevent a race condition with + * the device, whereby the device may take hold of the TXD before we + * finished updating it. + */ + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); + rt2x00_set_field32(&word, TXD_W0_VALID, 1); + rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_ACK, + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_OFDM, + (txdesc->rate_mode == RATE_MODE_OFDM)); + rt2x00_set_field32(&word, TXD_W0_CIPHER_OWNER, 1); + rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); + rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, + test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); + rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); + rt2x00_desc_write(txd, 0, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; +} + +/* + * TX data initialization + */ +static void rt2500pci_write_beacon(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + u32 reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + + if (rt2x00queue_map_txskb(entry)) { + rt2x00_err(rt2x00dev, "Fail to map beacon, aborting\n"); + goto out; + } + + /* + * Write the TX descriptor for the beacon. + */ + rt2500pci_write_tx_desc(entry, txdesc); + + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); +out: + /* + * Enable beaconing again. + */ + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); +} + +/* + * RX control handlers + */ +static void rt2500pci_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + u32 word0; + u32 word2; + + rt2x00_desc_read(entry_priv->desc, 0, &word0); + rt2x00_desc_read(entry_priv->desc, 2, &word2); + + if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; + + /* + * Obtain the status about this packet. + * When frame was received with an OFDM bitrate, + * the signal is the PLCP value. If it was received with + * a CCK bitrate the signal is the rate in 100kbit/s. + */ + rxdesc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL); + rxdesc->rssi = rt2x00_get_field32(word2, RXD_W2_RSSI) - + entry->queue->rt2x00dev->rssi_offset; + rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + if (rt2x00_get_field32(word0, RXD_W0_OFDM)) + rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; + else + rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; + if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; +} + +/* + * Interrupt functions. + */ +static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev, + const enum data_queue_qid queue_idx) +{ + struct data_queue *queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); + struct queue_entry_priv_mmio *entry_priv; + struct queue_entry *entry; + struct txdone_entry_desc txdesc; + u32 word; + + while (!rt2x00queue_empty(queue)) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + entry_priv = entry->priv_data; + rt2x00_desc_read(entry_priv->desc, 0, &word); + + if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + !rt2x00_get_field32(word, TXD_W0_VALID)) + break; + + /* + * Obtain the status about this packet. + */ + txdesc.flags = 0; + switch (rt2x00_get_field32(word, TXD_W0_RESULT)) { + case 0: /* Success */ + case 1: /* Success with retry */ + __set_bit(TXDONE_SUCCESS, &txdesc.flags); + break; + case 2: /* Failure, excessive retries */ + __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); + /* Don't break, this is a failed frame! */ + default: /* Failure */ + __set_bit(TXDONE_FAILURE, &txdesc.flags); + } + txdesc.retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); + + rt2x00lib_txdone(entry, &txdesc); + } +} + +static inline void rt2500pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, irq_field, 0); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +static void rt2500pci_txstatus_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + u32 reg; + + /* + * Handle all tx queues. + */ + rt2500pci_txdone(rt2x00dev, QID_ATIM); + rt2500pci_txdone(rt2x00dev, QID_AC_VO); + rt2500pci_txdone(rt2x00dev, QID_AC_VI); + + /* + * Enable all TXDONE interrupts again. + */ + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) { + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); + } +} + +static void rt2500pci_tbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2x00lib_beacondone(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2500pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); +} + +static void rt2500pci_rxdone_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2x00mmio_rxdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2500pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); +} + +static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + u32 reg, mask; + + /* + * Get the interrupt sources & saved to local variable. + * Write register value back to clear pending interrupts. + */ + rt2x00mmio_register_read(rt2x00dev, CSR7, ®); + rt2x00mmio_register_write(rt2x00dev, CSR7, reg); + + if (!reg) + return IRQ_NONE; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return IRQ_HANDLED; + + mask = reg; + + /* + * Schedule tasklets for interrupt handling. + */ + if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) + tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); + + if (rt2x00_get_field32(reg, CSR7_RXDONE)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + + if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) || + rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) || + rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) { + tasklet_schedule(&rt2x00dev->txstatus_tasklet); + /* + * Mask out all txdone interrupts. + */ + rt2x00_set_field32(&mask, CSR8_TXDONE_TXRING, 1); + rt2x00_set_field32(&mask, CSR8_TXDONE_ATIMRING, 1); + rt2x00_set_field32(&mask, CSR8_TXDONE_PRIORING, 1); + } + + /* + * Disable all interrupts for which a tasklet was scheduled right now, + * the tasklet will reenable the appropriate interrupts. + */ + spin_lock(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + reg |= mask; + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock(&rt2x00dev->irqmask_lock); + + return IRQ_HANDLED; +} + +/* + * Device probe functions. + */ +static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) +{ + struct eeprom_93cx6 eeprom; + u32 reg; + u16 word; + u8 *mac; + + rt2x00mmio_register_read(rt2x00dev, CSR21, ®); + + eeprom.data = rt2x00dev; + eeprom.register_read = rt2500pci_eepromregister_read; + eeprom.register_write = rt2500pci_eepromregister_write; + eeprom.width = rt2x00_get_field32(reg, CSR21_TYPE_93C46) ? + PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; + eeprom.reg_data_in = 0; + eeprom.reg_data_out = 0; + eeprom.reg_data_clock = 0; + eeprom.reg_chip_select = 0; + + eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, + EEPROM_SIZE / sizeof(u16)); + + /* + * Start validation of the data that has been read. + */ + mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + if (!is_valid_ether_addr(mac)) { + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); + rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, + ANTENNA_SW_DIVERSITY); + rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, + ANTENNA_SW_DIVERSITY); + rt2x00_set_field16(&word, EEPROM_ANTENNA_LED_MODE, + LED_MODE_DEFAULT); + rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); + rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); + rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2522); + rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); + rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); + rt2x00_set_field16(&word, EEPROM_NIC_DYN_BBP_TUNE, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CCK_TX_POWER, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); + rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_CALIBRATE_OFFSET_RSSI, + DEFAULT_RSSI_OFFSET); + rt2x00_eeprom_write(rt2x00dev, EEPROM_CALIBRATE_OFFSET, word); + rt2x00_eeprom_dbg(rt2x00dev, "Calibrate offset: 0x%04x\n", + word); + } + + return 0; +} + +static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 value; + u16 eeprom; + + /* + * Read EEPROM word for configuration. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Identify RF chipset. + */ + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); + rt2x00mmio_register_read(rt2x00dev, CSR0, ®); + rt2x00_set_chip(rt2x00dev, RT2560, value, + rt2x00_get_field32(reg, CSR0_REVISION)); + + if (!rt2x00_rf(rt2x00dev, RF2522) && + !rt2x00_rf(rt2x00dev, RF2523) && + !rt2x00_rf(rt2x00dev, RF2524) && + !rt2x00_rf(rt2x00dev, RF2525) && + !rt2x00_rf(rt2x00dev, RF2525E) && + !rt2x00_rf(rt2x00dev, RF5222)) { + rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); + return -ENODEV; + } + + /* + * Identify default antenna configuration. + */ + rt2x00dev->default_ant.tx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); + rt2x00dev->default_ant.rx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); + + /* + * Store led mode, for correct led behaviour. + */ +#ifdef CONFIG_RT2X00_LIB_LEDS + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); + + rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + if (value == LED_MODE_TXRX_ACTIVITY || + value == LED_MODE_DEFAULT || + value == LED_MODE_ASUS) + rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_qual, + LED_TYPE_ACTIVITY); +#endif /* CONFIG_RT2X00_LIB_LEDS */ + + /* + * Detect if this device has an hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) { + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); + /* + * On this device RFKILL initialized during probe does not work. + */ + __set_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags); + } + + /* + * Check if the BBP tuning should be enabled. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); + if (!rt2x00_get_field16(eeprom, EEPROM_NIC_DYN_BBP_TUNE)) + __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); + + /* + * Read the RSSI <-> dBm offset information. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &eeprom); + rt2x00dev->rssi_offset = + rt2x00_get_field16(eeprom, EEPROM_CALIBRATE_OFFSET_RSSI); + + return 0; +} + +/* + * RF value list for RF2522 + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_bg_2522[] = { + { 1, 0x00002050, 0x000c1fda, 0x00000101, 0 }, + { 2, 0x00002050, 0x000c1fee, 0x00000101, 0 }, + { 3, 0x00002050, 0x000c2002, 0x00000101, 0 }, + { 4, 0x00002050, 0x000c2016, 0x00000101, 0 }, + { 5, 0x00002050, 0x000c202a, 0x00000101, 0 }, + { 6, 0x00002050, 0x000c203e, 0x00000101, 0 }, + { 7, 0x00002050, 0x000c2052, 0x00000101, 0 }, + { 8, 0x00002050, 0x000c2066, 0x00000101, 0 }, + { 9, 0x00002050, 0x000c207a, 0x00000101, 0 }, + { 10, 0x00002050, 0x000c208e, 0x00000101, 0 }, + { 11, 0x00002050, 0x000c20a2, 0x00000101, 0 }, + { 12, 0x00002050, 0x000c20b6, 0x00000101, 0 }, + { 13, 0x00002050, 0x000c20ca, 0x00000101, 0 }, + { 14, 0x00002050, 0x000c20fa, 0x00000101, 0 }, +}; + +/* + * RF value list for RF2523 + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_bg_2523[] = { + { 1, 0x00022010, 0x00000c9e, 0x000e0111, 0x00000a1b }, + { 2, 0x00022010, 0x00000ca2, 0x000e0111, 0x00000a1b }, + { 3, 0x00022010, 0x00000ca6, 0x000e0111, 0x00000a1b }, + { 4, 0x00022010, 0x00000caa, 0x000e0111, 0x00000a1b }, + { 5, 0x00022010, 0x00000cae, 0x000e0111, 0x00000a1b }, + { 6, 0x00022010, 0x00000cb2, 0x000e0111, 0x00000a1b }, + { 7, 0x00022010, 0x00000cb6, 0x000e0111, 0x00000a1b }, + { 8, 0x00022010, 0x00000cba, 0x000e0111, 0x00000a1b }, + { 9, 0x00022010, 0x00000cbe, 0x000e0111, 0x00000a1b }, + { 10, 0x00022010, 0x00000d02, 0x000e0111, 0x00000a1b }, + { 11, 0x00022010, 0x00000d06, 0x000e0111, 0x00000a1b }, + { 12, 0x00022010, 0x00000d0a, 0x000e0111, 0x00000a1b }, + { 13, 0x00022010, 0x00000d0e, 0x000e0111, 0x00000a1b }, + { 14, 0x00022010, 0x00000d1a, 0x000e0111, 0x00000a03 }, +}; + +/* + * RF value list for RF2524 + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_bg_2524[] = { + { 1, 0x00032020, 0x00000c9e, 0x00000101, 0x00000a1b }, + { 2, 0x00032020, 0x00000ca2, 0x00000101, 0x00000a1b }, + { 3, 0x00032020, 0x00000ca6, 0x00000101, 0x00000a1b }, + { 4, 0x00032020, 0x00000caa, 0x00000101, 0x00000a1b }, + { 5, 0x00032020, 0x00000cae, 0x00000101, 0x00000a1b }, + { 6, 0x00032020, 0x00000cb2, 0x00000101, 0x00000a1b }, + { 7, 0x00032020, 0x00000cb6, 0x00000101, 0x00000a1b }, + { 8, 0x00032020, 0x00000cba, 0x00000101, 0x00000a1b }, + { 9, 0x00032020, 0x00000cbe, 0x00000101, 0x00000a1b }, + { 10, 0x00032020, 0x00000d02, 0x00000101, 0x00000a1b }, + { 11, 0x00032020, 0x00000d06, 0x00000101, 0x00000a1b }, + { 12, 0x00032020, 0x00000d0a, 0x00000101, 0x00000a1b }, + { 13, 0x00032020, 0x00000d0e, 0x00000101, 0x00000a1b }, + { 14, 0x00032020, 0x00000d1a, 0x00000101, 0x00000a03 }, +}; + +/* + * RF value list for RF2525 + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_bg_2525[] = { + { 1, 0x00022020, 0x00080c9e, 0x00060111, 0x00000a1b }, + { 2, 0x00022020, 0x00080ca2, 0x00060111, 0x00000a1b }, + { 3, 0x00022020, 0x00080ca6, 0x00060111, 0x00000a1b }, + { 4, 0x00022020, 0x00080caa, 0x00060111, 0x00000a1b }, + { 5, 0x00022020, 0x00080cae, 0x00060111, 0x00000a1b }, + { 6, 0x00022020, 0x00080cb2, 0x00060111, 0x00000a1b }, + { 7, 0x00022020, 0x00080cb6, 0x00060111, 0x00000a1b }, + { 8, 0x00022020, 0x00080cba, 0x00060111, 0x00000a1b }, + { 9, 0x00022020, 0x00080cbe, 0x00060111, 0x00000a1b }, + { 10, 0x00022020, 0x00080d02, 0x00060111, 0x00000a1b }, + { 11, 0x00022020, 0x00080d06, 0x00060111, 0x00000a1b }, + { 12, 0x00022020, 0x00080d0a, 0x00060111, 0x00000a1b }, + { 13, 0x00022020, 0x00080d0e, 0x00060111, 0x00000a1b }, + { 14, 0x00022020, 0x00080d1a, 0x00060111, 0x00000a03 }, +}; + +/* + * RF value list for RF2525e + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_bg_2525e[] = { + { 1, 0x00022020, 0x00081136, 0x00060111, 0x00000a0b }, + { 2, 0x00022020, 0x0008113a, 0x00060111, 0x00000a0b }, + { 3, 0x00022020, 0x0008113e, 0x00060111, 0x00000a0b }, + { 4, 0x00022020, 0x00081182, 0x00060111, 0x00000a0b }, + { 5, 0x00022020, 0x00081186, 0x00060111, 0x00000a0b }, + { 6, 0x00022020, 0x0008118a, 0x00060111, 0x00000a0b }, + { 7, 0x00022020, 0x0008118e, 0x00060111, 0x00000a0b }, + { 8, 0x00022020, 0x00081192, 0x00060111, 0x00000a0b }, + { 9, 0x00022020, 0x00081196, 0x00060111, 0x00000a0b }, + { 10, 0x00022020, 0x0008119a, 0x00060111, 0x00000a0b }, + { 11, 0x00022020, 0x0008119e, 0x00060111, 0x00000a0b }, + { 12, 0x00022020, 0x000811a2, 0x00060111, 0x00000a0b }, + { 13, 0x00022020, 0x000811a6, 0x00060111, 0x00000a0b }, + { 14, 0x00022020, 0x000811ae, 0x00060111, 0x00000a1b }, +}; + +/* + * RF value list for RF5222 + * Supports: 2.4 GHz & 5.2 GHz + */ +static const struct rf_channel rf_vals_5222[] = { + { 1, 0x00022020, 0x00001136, 0x00000101, 0x00000a0b }, + { 2, 0x00022020, 0x0000113a, 0x00000101, 0x00000a0b }, + { 3, 0x00022020, 0x0000113e, 0x00000101, 0x00000a0b }, + { 4, 0x00022020, 0x00001182, 0x00000101, 0x00000a0b }, + { 5, 0x00022020, 0x00001186, 0x00000101, 0x00000a0b }, + { 6, 0x00022020, 0x0000118a, 0x00000101, 0x00000a0b }, + { 7, 0x00022020, 0x0000118e, 0x00000101, 0x00000a0b }, + { 8, 0x00022020, 0x00001192, 0x00000101, 0x00000a0b }, + { 9, 0x00022020, 0x00001196, 0x00000101, 0x00000a0b }, + { 10, 0x00022020, 0x0000119a, 0x00000101, 0x00000a0b }, + { 11, 0x00022020, 0x0000119e, 0x00000101, 0x00000a0b }, + { 12, 0x00022020, 0x000011a2, 0x00000101, 0x00000a0b }, + { 13, 0x00022020, 0x000011a6, 0x00000101, 0x00000a0b }, + { 14, 0x00022020, 0x000011ae, 0x00000101, 0x00000a1b }, + + /* 802.11 UNI / HyperLan 2 */ + { 36, 0x00022010, 0x00018896, 0x00000101, 0x00000a1f }, + { 40, 0x00022010, 0x0001889a, 0x00000101, 0x00000a1f }, + { 44, 0x00022010, 0x0001889e, 0x00000101, 0x00000a1f }, + { 48, 0x00022010, 0x000188a2, 0x00000101, 0x00000a1f }, + { 52, 0x00022010, 0x000188a6, 0x00000101, 0x00000a1f }, + { 66, 0x00022010, 0x000188aa, 0x00000101, 0x00000a1f }, + { 60, 0x00022010, 0x000188ae, 0x00000101, 0x00000a1f }, + { 64, 0x00022010, 0x000188b2, 0x00000101, 0x00000a1f }, + + /* 802.11 HyperLan 2 */ + { 100, 0x00022010, 0x00008802, 0x00000101, 0x00000a0f }, + { 104, 0x00022010, 0x00008806, 0x00000101, 0x00000a0f }, + { 108, 0x00022010, 0x0000880a, 0x00000101, 0x00000a0f }, + { 112, 0x00022010, 0x0000880e, 0x00000101, 0x00000a0f }, + { 116, 0x00022010, 0x00008812, 0x00000101, 0x00000a0f }, + { 120, 0x00022010, 0x00008816, 0x00000101, 0x00000a0f }, + { 124, 0x00022010, 0x0000881a, 0x00000101, 0x00000a0f }, + { 128, 0x00022010, 0x0000881e, 0x00000101, 0x00000a0f }, + { 132, 0x00022010, 0x00008822, 0x00000101, 0x00000a0f }, + { 136, 0x00022010, 0x00008826, 0x00000101, 0x00000a0f }, + + /* 802.11 UNII */ + { 140, 0x00022010, 0x0000882a, 0x00000101, 0x00000a0f }, + { 149, 0x00022020, 0x000090a6, 0x00000101, 0x00000a07 }, + { 153, 0x00022020, 0x000090ae, 0x00000101, 0x00000a07 }, + { 157, 0x00022020, 0x000090b6, 0x00000101, 0x00000a07 }, + { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 }, +}; + +static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + struct channel_info *info; + char *tx_power; + unsigned int i; + + /* + * Initialize all hw fields. + */ + ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); + ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); + ieee80211_hw_set(rt2x00dev->hw, HOST_BROADCAST_PS_BUFFERING); + ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); + + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, + rt2x00_eeprom_addr(rt2x00dev, + EEPROM_MAC_ADDR_0)); + + /* + * Disable powersaving as default. + */ + rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + /* + * Initialize hw_mode information. + */ + spec->supported_bands = SUPPORT_BAND_2GHZ; + spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; + + if (rt2x00_rf(rt2x00dev, RF2522)) { + spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); + spec->channels = rf_vals_bg_2522; + } else if (rt2x00_rf(rt2x00dev, RF2523)) { + spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523); + spec->channels = rf_vals_bg_2523; + } else if (rt2x00_rf(rt2x00dev, RF2524)) { + spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524); + spec->channels = rf_vals_bg_2524; + } else if (rt2x00_rf(rt2x00dev, RF2525)) { + spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525); + spec->channels = rf_vals_bg_2525; + } else if (rt2x00_rf(rt2x00dev, RF2525E)) { + spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e); + spec->channels = rf_vals_bg_2525e; + } else if (rt2x00_rf(rt2x00dev, RF5222)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; + spec->num_channels = ARRAY_SIZE(rf_vals_5222); + spec->channels = rf_vals_5222; + } + + /* + * Create channel information array + */ + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); + } + + if (spec->num_channels > 14) { + for (i = 14; i < spec->num_channels; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = DEFAULT_TXPOWER; + } + } + + return 0; +} + +static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + u32 reg; + + /* + * Allocate eeprom data. + */ + retval = rt2500pci_validate_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt2500pci_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. + */ + rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); + rt2x00_set_field32(®, GPIOCSR_DIR0, 1); + rt2x00mmio_register_write(rt2x00dev, GPIOCSR, reg); + + /* + * Initialize hw specifications. + */ + retval = rt2500pci_probe_hw_mode(rt2x00dev); + if (retval) + return retval; + + /* + * This device requires the atim queue and DMA-mapped skbs. + */ + __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); + + /* + * Set the rssi offset. + */ + rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; + + return 0; +} + +/* + * IEEE80211 stack callback functions. + */ +static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u64 tsf; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, CSR17, ®); + tsf = (u64) rt2x00_get_field32(reg, CSR17_HIGH_TSFTIMER) << 32; + rt2x00mmio_register_read(rt2x00dev, CSR16, ®); + tsf |= rt2x00_get_field32(reg, CSR16_LOW_TSFTIMER); + + return tsf; +} + +static int rt2500pci_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, CSR15, ®); + return rt2x00_get_field32(reg, CSR15_BEACON_SENT); +} + +static const struct ieee80211_ops rt2500pci_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2x00mac_conf_tx, + .get_tsf = rt2500pci_get_tsf, + .tx_last_beacon = rt2500pci_tx_last_beacon, + .rfkill_poll = rt2x00mac_rfkill_poll, + .flush = rt2x00mac_flush, + .set_antenna = rt2x00mac_set_antenna, + .get_antenna = rt2x00mac_get_antenna, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { + .irq_handler = rt2500pci_interrupt, + .txstatus_tasklet = rt2500pci_txstatus_tasklet, + .tbtt_tasklet = rt2500pci_tbtt_tasklet, + .rxdone_tasklet = rt2500pci_rxdone_tasklet, + .probe_hw = rt2500pci_probe_hw, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt2500pci_get_entry_state, + .clear_entry = rt2500pci_clear_entry, + .set_device_state = rt2500pci_set_device_state, + .rfkill_poll = rt2500pci_rfkill_poll, + .link_stats = rt2500pci_link_stats, + .reset_tuner = rt2500pci_reset_tuner, + .link_tuner = rt2500pci_link_tuner, + .start_queue = rt2500pci_start_queue, + .kick_queue = rt2500pci_kick_queue, + .stop_queue = rt2500pci_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, + .write_tx_desc = rt2500pci_write_tx_desc, + .write_beacon = rt2500pci_write_beacon, + .fill_rxdone = rt2500pci_fill_rxdone, + .config_filter = rt2500pci_config_filter, + .config_intf = rt2500pci_config_intf, + .config_erp = rt2500pci_config_erp, + .config_ant = rt2500pci_config_ant, + .config = rt2500pci_config, +}; + +static void rt2500pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + default: + BUG(); + break; + } +} + +static const struct rt2x00_ops rt2500pci_ops = { + .name = KBUILD_MODNAME, + .max_ap_intf = 1, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2500pci_queue_init, + .lib = &rt2500pci_rt2x00_ops, + .hw = &rt2500pci_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2500pci_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +/* + * RT2500pci module information. + */ +static const struct pci_device_id rt2500pci_device_table[] = { + { PCI_DEVICE(0x1814, 0x0201) }, + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2500 PCI & PCMCIA Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2560 PCI & PCMCIA chipset based cards"); +MODULE_DEVICE_TABLE(pci, rt2500pci_device_table); +MODULE_LICENSE("GPL"); + +static int rt2500pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + return rt2x00pci_probe(pci_dev, &rt2500pci_ops); +} + +static struct pci_driver rt2500pci_driver = { + .name = KBUILD_MODNAME, + .id_table = rt2500pci_device_table, + .probe = rt2500pci_probe, + .remove = rt2x00pci_remove, + .suspend = rt2x00pci_suspend, + .resume = rt2x00pci_resume, +}; + +module_pci_driver(rt2500pci_driver); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500pci.h b/drivers/net/wireless/ralink/rt2x00/rt2500pci.h new file mode 100644 index 000000000000..573e87bcc553 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.h @@ -0,0 +1,1235 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2500pci + Abstract: Data structures and registers for the rt2500pci module. + Supported chipsets: RT2560. + */ + +#ifndef RT2500PCI_H +#define RT2500PCI_H + +/* + * RF chip defines. + */ +#define RF2522 0x0000 +#define RF2523 0x0001 +#define RF2524 0x0002 +#define RF2525 0x0003 +#define RF2525E 0x0004 +#define RF5222 0x0010 + +/* + * RT2560 version + */ +#define RT2560_VERSION_B 2 +#define RT2560_VERSION_C 3 +#define RT2560_VERSION_D 4 + +/* + * Signal information. + * Default offset is required for RSSI <-> dBm conversion. + */ +#define DEFAULT_RSSI_OFFSET 121 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x0000 +#define CSR_REG_SIZE 0x0174 +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x0200 +#define BBP_BASE 0x0000 +#define BBP_SIZE 0x0040 +#define RF_BASE 0x0004 +#define RF_SIZE 0x0010 + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 2 + +/* + * Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * CSR0: ASIC revision number. + */ +#define CSR0 0x0000 +#define CSR0_REVISION FIELD32(0x0000ffff) + +/* + * CSR1: System control register. + * SOFT_RESET: Software reset, 1: reset, 0: normal. + * BBP_RESET: Hardware reset, 1: reset, 0, release. + * HOST_READY: Host ready after initialization. + */ +#define CSR1 0x0004 +#define CSR1_SOFT_RESET FIELD32(0x00000001) +#define CSR1_BBP_RESET FIELD32(0x00000002) +#define CSR1_HOST_READY FIELD32(0x00000004) + +/* + * CSR2: System admin status register (invalid). + */ +#define CSR2 0x0008 + +/* + * CSR3: STA MAC address register 0. + */ +#define CSR3 0x000c +#define CSR3_BYTE0 FIELD32(0x000000ff) +#define CSR3_BYTE1 FIELD32(0x0000ff00) +#define CSR3_BYTE2 FIELD32(0x00ff0000) +#define CSR3_BYTE3 FIELD32(0xff000000) + +/* + * CSR4: STA MAC address register 1. + */ +#define CSR4 0x0010 +#define CSR4_BYTE4 FIELD32(0x000000ff) +#define CSR4_BYTE5 FIELD32(0x0000ff00) + +/* + * CSR5: BSSID register 0. + */ +#define CSR5 0x0014 +#define CSR5_BYTE0 FIELD32(0x000000ff) +#define CSR5_BYTE1 FIELD32(0x0000ff00) +#define CSR5_BYTE2 FIELD32(0x00ff0000) +#define CSR5_BYTE3 FIELD32(0xff000000) + +/* + * CSR6: BSSID register 1. + */ +#define CSR6 0x0018 +#define CSR6_BYTE4 FIELD32(0x000000ff) +#define CSR6_BYTE5 FIELD32(0x0000ff00) + +/* + * CSR7: Interrupt source register. + * Write 1 to clear. + * TBCN_EXPIRE: Beacon timer expired interrupt. + * TWAKE_EXPIRE: Wakeup timer expired interrupt. + * TATIMW_EXPIRE: Timer of atim window expired interrupt. + * TXDONE_TXRING: Tx ring transmit done interrupt. + * TXDONE_ATIMRING: Atim ring transmit done interrupt. + * TXDONE_PRIORING: Priority ring transmit done interrupt. + * RXDONE: Receive done interrupt. + * DECRYPTION_DONE: Decryption done interrupt. + * ENCRYPTION_DONE: Encryption done interrupt. + * UART1_TX_TRESHOLD: UART1 TX reaches threshold. + * UART1_RX_TRESHOLD: UART1 RX reaches threshold. + * UART1_IDLE_TRESHOLD: UART1 IDLE over threshold. + * UART1_TX_BUFF_ERROR: UART1 TX buffer error. + * UART1_RX_BUFF_ERROR: UART1 RX buffer error. + * UART2_TX_TRESHOLD: UART2 TX reaches threshold. + * UART2_RX_TRESHOLD: UART2 RX reaches threshold. + * UART2_IDLE_TRESHOLD: UART2 IDLE over threshold. + * UART2_TX_BUFF_ERROR: UART2 TX buffer error. + * UART2_RX_BUFF_ERROR: UART2 RX buffer error. + * TIMER_CSR3_EXPIRE: TIMECSR3 timer expired (802.1H quiet period). + + */ +#define CSR7 0x001c +#define CSR7_TBCN_EXPIRE FIELD32(0x00000001) +#define CSR7_TWAKE_EXPIRE FIELD32(0x00000002) +#define CSR7_TATIMW_EXPIRE FIELD32(0x00000004) +#define CSR7_TXDONE_TXRING FIELD32(0x00000008) +#define CSR7_TXDONE_ATIMRING FIELD32(0x00000010) +#define CSR7_TXDONE_PRIORING FIELD32(0x00000020) +#define CSR7_RXDONE FIELD32(0x00000040) +#define CSR7_DECRYPTION_DONE FIELD32(0x00000080) +#define CSR7_ENCRYPTION_DONE FIELD32(0x00000100) +#define CSR7_UART1_TX_TRESHOLD FIELD32(0x00000200) +#define CSR7_UART1_RX_TRESHOLD FIELD32(0x00000400) +#define CSR7_UART1_IDLE_TRESHOLD FIELD32(0x00000800) +#define CSR7_UART1_TX_BUFF_ERROR FIELD32(0x00001000) +#define CSR7_UART1_RX_BUFF_ERROR FIELD32(0x00002000) +#define CSR7_UART2_TX_TRESHOLD FIELD32(0x00004000) +#define CSR7_UART2_RX_TRESHOLD FIELD32(0x00008000) +#define CSR7_UART2_IDLE_TRESHOLD FIELD32(0x00010000) +#define CSR7_UART2_TX_BUFF_ERROR FIELD32(0x00020000) +#define CSR7_UART2_RX_BUFF_ERROR FIELD32(0x00040000) +#define CSR7_TIMER_CSR3_EXPIRE FIELD32(0x00080000) + +/* + * CSR8: Interrupt mask register. + * Write 1 to mask interrupt. + * TBCN_EXPIRE: Beacon timer expired interrupt. + * TWAKE_EXPIRE: Wakeup timer expired interrupt. + * TATIMW_EXPIRE: Timer of atim window expired interrupt. + * TXDONE_TXRING: Tx ring transmit done interrupt. + * TXDONE_ATIMRING: Atim ring transmit done interrupt. + * TXDONE_PRIORING: Priority ring transmit done interrupt. + * RXDONE: Receive done interrupt. + * DECRYPTION_DONE: Decryption done interrupt. + * ENCRYPTION_DONE: Encryption done interrupt. + * UART1_TX_TRESHOLD: UART1 TX reaches threshold. + * UART1_RX_TRESHOLD: UART1 RX reaches threshold. + * UART1_IDLE_TRESHOLD: UART1 IDLE over threshold. + * UART1_TX_BUFF_ERROR: UART1 TX buffer error. + * UART1_RX_BUFF_ERROR: UART1 RX buffer error. + * UART2_TX_TRESHOLD: UART2 TX reaches threshold. + * UART2_RX_TRESHOLD: UART2 RX reaches threshold. + * UART2_IDLE_TRESHOLD: UART2 IDLE over threshold. + * UART2_TX_BUFF_ERROR: UART2 TX buffer error. + * UART2_RX_BUFF_ERROR: UART2 RX buffer error. + * TIMER_CSR3_EXPIRE: TIMECSR3 timer expired (802.1H quiet period). + */ +#define CSR8 0x0020 +#define CSR8_TBCN_EXPIRE FIELD32(0x00000001) +#define CSR8_TWAKE_EXPIRE FIELD32(0x00000002) +#define CSR8_TATIMW_EXPIRE FIELD32(0x00000004) +#define CSR8_TXDONE_TXRING FIELD32(0x00000008) +#define CSR8_TXDONE_ATIMRING FIELD32(0x00000010) +#define CSR8_TXDONE_PRIORING FIELD32(0x00000020) +#define CSR8_RXDONE FIELD32(0x00000040) +#define CSR8_DECRYPTION_DONE FIELD32(0x00000080) +#define CSR8_ENCRYPTION_DONE FIELD32(0x00000100) +#define CSR8_UART1_TX_TRESHOLD FIELD32(0x00000200) +#define CSR8_UART1_RX_TRESHOLD FIELD32(0x00000400) +#define CSR8_UART1_IDLE_TRESHOLD FIELD32(0x00000800) +#define CSR8_UART1_TX_BUFF_ERROR FIELD32(0x00001000) +#define CSR8_UART1_RX_BUFF_ERROR FIELD32(0x00002000) +#define CSR8_UART2_TX_TRESHOLD FIELD32(0x00004000) +#define CSR8_UART2_RX_TRESHOLD FIELD32(0x00008000) +#define CSR8_UART2_IDLE_TRESHOLD FIELD32(0x00010000) +#define CSR8_UART2_TX_BUFF_ERROR FIELD32(0x00020000) +#define CSR8_UART2_RX_BUFF_ERROR FIELD32(0x00040000) +#define CSR8_TIMER_CSR3_EXPIRE FIELD32(0x00080000) + +/* + * CSR9: Maximum frame length register. + * MAX_FRAME_UNIT: Maximum frame length in 128b unit, default: 12. + */ +#define CSR9 0x0024 +#define CSR9_MAX_FRAME_UNIT FIELD32(0x00000f80) + +/* + * SECCSR0: WEP control register. + * KICK_DECRYPT: Kick decryption engine, self-clear. + * ONE_SHOT: 0: ring mode, 1: One shot only mode. + * DESC_ADDRESS: Descriptor physical address of frame. + */ +#define SECCSR0 0x0028 +#define SECCSR0_KICK_DECRYPT FIELD32(0x00000001) +#define SECCSR0_ONE_SHOT FIELD32(0x00000002) +#define SECCSR0_DESC_ADDRESS FIELD32(0xfffffffc) + +/* + * CSR11: Back-off control register. + * CWMIN: CWmin. Default cwmin is 31 (2^5 - 1). + * CWMAX: CWmax. Default cwmax is 1023 (2^10 - 1). + * SLOT_TIME: Slot time, default is 20us for 802.11b + * CW_SELECT: CWmin/CWmax selection, 1: Register, 0: TXD. + * LONG_RETRY: Long retry count. + * SHORT_RETRY: Short retry count. + */ +#define CSR11 0x002c +#define CSR11_CWMIN FIELD32(0x0000000f) +#define CSR11_CWMAX FIELD32(0x000000f0) +#define CSR11_SLOT_TIME FIELD32(0x00001f00) +#define CSR11_CW_SELECT FIELD32(0x00002000) +#define CSR11_LONG_RETRY FIELD32(0x00ff0000) +#define CSR11_SHORT_RETRY FIELD32(0xff000000) + +/* + * CSR12: Synchronization configuration register 0. + * All units in 1/16 TU. + * BEACON_INTERVAL: Beacon interval, default is 100 TU. + * CFP_MAX_DURATION: Cfp maximum duration, default is 100 TU. + */ +#define CSR12 0x0030 +#define CSR12_BEACON_INTERVAL FIELD32(0x0000ffff) +#define CSR12_CFP_MAX_DURATION FIELD32(0xffff0000) + +/* + * CSR13: Synchronization configuration register 1. + * All units in 1/16 TU. + * ATIMW_DURATION: Atim window duration. + * CFP_PERIOD: Cfp period, default is 0 TU. + */ +#define CSR13 0x0034 +#define CSR13_ATIMW_DURATION FIELD32(0x0000ffff) +#define CSR13_CFP_PERIOD FIELD32(0x00ff0000) + +/* + * CSR14: Synchronization control register. + * TSF_COUNT: Enable tsf auto counting. + * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. + * TBCN: Enable tbcn with reload value. + * TCFP: Enable tcfp & cfp / cp switching. + * TATIMW: Enable tatimw & atim window switching. + * BEACON_GEN: Enable beacon generator. + * CFP_COUNT_PRELOAD: Cfp count preload value. + * TBCM_PRELOAD: Tbcn preload value in units of 64us. + */ +#define CSR14 0x0038 +#define CSR14_TSF_COUNT FIELD32(0x00000001) +#define CSR14_TSF_SYNC FIELD32(0x00000006) +#define CSR14_TBCN FIELD32(0x00000008) +#define CSR14_TCFP FIELD32(0x00000010) +#define CSR14_TATIMW FIELD32(0x00000020) +#define CSR14_BEACON_GEN FIELD32(0x00000040) +#define CSR14_CFP_COUNT_PRELOAD FIELD32(0x0000ff00) +#define CSR14_TBCM_PRELOAD FIELD32(0xffff0000) + +/* + * CSR15: Synchronization status register. + * CFP: ASIC is in contention-free period. + * ATIMW: ASIC is in ATIM window. + * BEACON_SENT: Beacon is send. + */ +#define CSR15 0x003c +#define CSR15_CFP FIELD32(0x00000001) +#define CSR15_ATIMW FIELD32(0x00000002) +#define CSR15_BEACON_SENT FIELD32(0x00000004) + +/* + * CSR16: TSF timer register 0. + */ +#define CSR16 0x0040 +#define CSR16_LOW_TSFTIMER FIELD32(0xffffffff) + +/* + * CSR17: TSF timer register 1. + */ +#define CSR17 0x0044 +#define CSR17_HIGH_TSFTIMER FIELD32(0xffffffff) + +/* + * CSR18: IFS timer register 0. + * SIFS: Sifs, default is 10 us. + * PIFS: Pifs, default is 30 us. + */ +#define CSR18 0x0048 +#define CSR18_SIFS FIELD32(0x000001ff) +#define CSR18_PIFS FIELD32(0x001f0000) + +/* + * CSR19: IFS timer register 1. + * DIFS: Difs, default is 50 us. + * EIFS: Eifs, default is 364 us. + */ +#define CSR19 0x004c +#define CSR19_DIFS FIELD32(0x0000ffff) +#define CSR19_EIFS FIELD32(0xffff0000) + +/* + * CSR20: Wakeup timer register. + * DELAY_AFTER_TBCN: Delay after tbcn expired in units of 1/16 TU. + * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. + * AUTOWAKE: Enable auto wakeup / sleep mechanism. + */ +#define CSR20 0x0050 +#define CSR20_DELAY_AFTER_TBCN FIELD32(0x0000ffff) +#define CSR20_TBCN_BEFORE_WAKEUP FIELD32(0x00ff0000) +#define CSR20_AUTOWAKE FIELD32(0x01000000) + +/* + * CSR21: EEPROM control register. + * RELOAD: Write 1 to reload eeprom content. + * TYPE_93C46: 1: 93c46, 0:93c66. + */ +#define CSR21 0x0054 +#define CSR21_RELOAD FIELD32(0x00000001) +#define CSR21_EEPROM_DATA_CLOCK FIELD32(0x00000002) +#define CSR21_EEPROM_CHIP_SELECT FIELD32(0x00000004) +#define CSR21_EEPROM_DATA_IN FIELD32(0x00000008) +#define CSR21_EEPROM_DATA_OUT FIELD32(0x00000010) +#define CSR21_TYPE_93C46 FIELD32(0x00000020) + +/* + * CSR22: CFP control register. + * CFP_DURATION_REMAIN: Cfp duration remain, in units of TU. + * RELOAD_CFP_DURATION: Write 1 to reload cfp duration remain. + */ +#define CSR22 0x0058 +#define CSR22_CFP_DURATION_REMAIN FIELD32(0x0000ffff) +#define CSR22_RELOAD_CFP_DURATION FIELD32(0x00010000) + +/* + * Transmit related CSRs. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * TXCSR0: TX Control Register. + * KICK_TX: Kick tx ring. + * KICK_ATIM: Kick atim ring. + * KICK_PRIO: Kick priority ring. + * ABORT: Abort all transmit related ring operation. + */ +#define TXCSR0 0x0060 +#define TXCSR0_KICK_TX FIELD32(0x00000001) +#define TXCSR0_KICK_ATIM FIELD32(0x00000002) +#define TXCSR0_KICK_PRIO FIELD32(0x00000004) +#define TXCSR0_ABORT FIELD32(0x00000008) + +/* + * TXCSR1: TX Configuration Register. + * ACK_TIMEOUT: Ack timeout, default = sifs + 2*slottime + acktime @ 1mbps. + * ACK_CONSUME_TIME: Ack consume time, default = sifs + acktime @ 1mbps. + * TSF_OFFSET: Insert tsf offset. + * AUTORESPONDER: Enable auto responder which include ack & cts. + */ +#define TXCSR1 0x0064 +#define TXCSR1_ACK_TIMEOUT FIELD32(0x000001ff) +#define TXCSR1_ACK_CONSUME_TIME FIELD32(0x0003fe00) +#define TXCSR1_TSF_OFFSET FIELD32(0x00fc0000) +#define TXCSR1_AUTORESPONDER FIELD32(0x01000000) + +/* + * TXCSR2: Tx descriptor configuration register. + * TXD_SIZE: Tx descriptor size, default is 48. + * NUM_TXD: Number of tx entries in ring. + * NUM_ATIM: Number of atim entries in ring. + * NUM_PRIO: Number of priority entries in ring. + */ +#define TXCSR2 0x0068 +#define TXCSR2_TXD_SIZE FIELD32(0x000000ff) +#define TXCSR2_NUM_TXD FIELD32(0x0000ff00) +#define TXCSR2_NUM_ATIM FIELD32(0x00ff0000) +#define TXCSR2_NUM_PRIO FIELD32(0xff000000) + +/* + * TXCSR3: TX Ring Base address register. + */ +#define TXCSR3 0x006c +#define TXCSR3_TX_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR4: TX Atim Ring Base address register. + */ +#define TXCSR4 0x0070 +#define TXCSR4_ATIM_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR5: TX Prio Ring Base address register. + */ +#define TXCSR5 0x0074 +#define TXCSR5_PRIO_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR6: Beacon Base address register. + */ +#define TXCSR6 0x0078 +#define TXCSR6_BEACON_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR7: Auto responder control register. + * AR_POWERMANAGEMENT: Auto responder power management bit. + */ +#define TXCSR7 0x007c +#define TXCSR7_AR_POWERMANAGEMENT FIELD32(0x00000001) + +/* + * TXCSR8: CCK Tx BBP register. + */ +#define TXCSR8 0x0098 +#define TXCSR8_BBP_ID0 FIELD32(0x0000007f) +#define TXCSR8_BBP_ID0_VALID FIELD32(0x00000080) +#define TXCSR8_BBP_ID1 FIELD32(0x00007f00) +#define TXCSR8_BBP_ID1_VALID FIELD32(0x00008000) +#define TXCSR8_BBP_ID2 FIELD32(0x007f0000) +#define TXCSR8_BBP_ID2_VALID FIELD32(0x00800000) +#define TXCSR8_BBP_ID3 FIELD32(0x7f000000) +#define TXCSR8_BBP_ID3_VALID FIELD32(0x80000000) + +/* + * TXCSR9: OFDM TX BBP registers + * OFDM_SIGNAL: BBP rate field address for OFDM. + * OFDM_SERVICE: BBP service field address for OFDM. + * OFDM_LENGTH_LOW: BBP length low byte address for OFDM. + * OFDM_LENGTH_HIGH: BBP length high byte address for OFDM. + */ +#define TXCSR9 0x0094 +#define TXCSR9_OFDM_RATE FIELD32(0x000000ff) +#define TXCSR9_OFDM_SERVICE FIELD32(0x0000ff00) +#define TXCSR9_OFDM_LENGTH_LOW FIELD32(0x00ff0000) +#define TXCSR9_OFDM_LENGTH_HIGH FIELD32(0xff000000) + +/* + * Receive related CSRs. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * RXCSR0: RX Control Register. + * DISABLE_RX: Disable rx engine. + * DROP_CRC: Drop crc error. + * DROP_PHYSICAL: Drop physical error. + * DROP_CONTROL: Drop control frame. + * DROP_NOT_TO_ME: Drop not to me unicast frame. + * DROP_TODS: Drop frame tods bit is true. + * DROP_VERSION_ERROR: Drop version error frame. + * PASS_CRC: Pass all packets with crc attached. + * PASS_CRC: Pass all packets with crc attached. + * PASS_PLCP: Pass all packets with 4 bytes PLCP attached. + * DROP_MCAST: Drop multicast frames. + * DROP_BCAST: Drop broadcast frames. + * ENABLE_QOS: Accept QOS data frame and parse QOS field. + */ +#define RXCSR0 0x0080 +#define RXCSR0_DISABLE_RX FIELD32(0x00000001) +#define RXCSR0_DROP_CRC FIELD32(0x00000002) +#define RXCSR0_DROP_PHYSICAL FIELD32(0x00000004) +#define RXCSR0_DROP_CONTROL FIELD32(0x00000008) +#define RXCSR0_DROP_NOT_TO_ME FIELD32(0x00000010) +#define RXCSR0_DROP_TODS FIELD32(0x00000020) +#define RXCSR0_DROP_VERSION_ERROR FIELD32(0x00000040) +#define RXCSR0_PASS_CRC FIELD32(0x00000080) +#define RXCSR0_PASS_PLCP FIELD32(0x00000100) +#define RXCSR0_DROP_MCAST FIELD32(0x00000200) +#define RXCSR0_DROP_BCAST FIELD32(0x00000400) +#define RXCSR0_ENABLE_QOS FIELD32(0x00000800) + +/* + * RXCSR1: RX descriptor configuration register. + * RXD_SIZE: Rx descriptor size, default is 32b. + * NUM_RXD: Number of rx entries in ring. + */ +#define RXCSR1 0x0084 +#define RXCSR1_RXD_SIZE FIELD32(0x000000ff) +#define RXCSR1_NUM_RXD FIELD32(0x0000ff00) + +/* + * RXCSR2: RX Ring base address register. + */ +#define RXCSR2 0x0088 +#define RXCSR2_RX_RING_REGISTER FIELD32(0xffffffff) + +/* + * RXCSR3: BBP ID register for Rx operation. + * BBP_ID#: BBP register # id. + * BBP_ID#_VALID: BBP register # id is valid or not. + */ +#define RXCSR3 0x0090 +#define RXCSR3_BBP_ID0 FIELD32(0x0000007f) +#define RXCSR3_BBP_ID0_VALID FIELD32(0x00000080) +#define RXCSR3_BBP_ID1 FIELD32(0x00007f00) +#define RXCSR3_BBP_ID1_VALID FIELD32(0x00008000) +#define RXCSR3_BBP_ID2 FIELD32(0x007f0000) +#define RXCSR3_BBP_ID2_VALID FIELD32(0x00800000) +#define RXCSR3_BBP_ID3 FIELD32(0x7f000000) +#define RXCSR3_BBP_ID3_VALID FIELD32(0x80000000) + +/* + * ARCSR1: Auto Responder PLCP config register 1. + * AR_BBP_DATA#: Auto responder BBP register # data. + * AR_BBP_ID#: Auto responder BBP register # Id. + */ +#define ARCSR1 0x009c +#define ARCSR1_AR_BBP_DATA2 FIELD32(0x000000ff) +#define ARCSR1_AR_BBP_ID2 FIELD32(0x0000ff00) +#define ARCSR1_AR_BBP_DATA3 FIELD32(0x00ff0000) +#define ARCSR1_AR_BBP_ID3 FIELD32(0xff000000) + +/* + * Miscellaneous Registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + + */ + +/* + * PCICSR: PCI control register. + * BIG_ENDIAN: 1: big endian, 0: little endian. + * RX_TRESHOLD: Rx threshold in dw to start pci access + * 0: 16dw (default), 1: 8dw, 2: 4dw, 3: 32dw. + * TX_TRESHOLD: Tx threshold in dw to start pci access + * 0: 0dw (default), 1: 1dw, 2: 4dw, 3: forward. + * BURST_LENTH: Pci burst length 0: 4dw (default, 1: 8dw, 2: 16dw, 3:32dw. + * ENABLE_CLK: Enable clk_run, pci clock can't going down to non-operational. + * READ_MULTIPLE: Enable memory read multiple. + * WRITE_INVALID: Enable memory write & invalid. + */ +#define PCICSR 0x008c +#define PCICSR_BIG_ENDIAN FIELD32(0x00000001) +#define PCICSR_RX_TRESHOLD FIELD32(0x00000006) +#define PCICSR_TX_TRESHOLD FIELD32(0x00000018) +#define PCICSR_BURST_LENTH FIELD32(0x00000060) +#define PCICSR_ENABLE_CLK FIELD32(0x00000080) +#define PCICSR_READ_MULTIPLE FIELD32(0x00000100) +#define PCICSR_WRITE_INVALID FIELD32(0x00000200) + +/* + * CNT0: FCS error count. + * FCS_ERROR: FCS error count, cleared when read. + */ +#define CNT0 0x00a0 +#define CNT0_FCS_ERROR FIELD32(0x0000ffff) + +/* + * Statistic Register. + * CNT1: PLCP error count. + * CNT2: Long error count. + */ +#define TIMECSR2 0x00a8 +#define CNT1 0x00ac +#define CNT2 0x00b0 +#define TIMECSR3 0x00b4 + +/* + * CNT3: CCA false alarm count. + */ +#define CNT3 0x00b8 +#define CNT3_FALSE_CCA FIELD32(0x0000ffff) + +/* + * Statistic Register. + * CNT4: Rx FIFO overflow count. + * CNT5: Tx FIFO underrun count. + */ +#define CNT4 0x00bc +#define CNT5 0x00c0 + +/* + * Baseband Control Register. + */ + +/* + * PWRCSR0: Power mode configuration register. + */ +#define PWRCSR0 0x00c4 + +/* + * Power state transition time registers. + */ +#define PSCSR0 0x00c8 +#define PSCSR1 0x00cc +#define PSCSR2 0x00d0 +#define PSCSR3 0x00d4 + +/* + * PWRCSR1: Manual power control / status register. + * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. + * SET_STATE: Set state. Write 1 to trigger, self cleared. + * BBP_DESIRE_STATE: BBP desired state. + * RF_DESIRE_STATE: RF desired state. + * BBP_CURR_STATE: BBP current state. + * RF_CURR_STATE: RF current state. + * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. + */ +#define PWRCSR1 0x00d8 +#define PWRCSR1_SET_STATE FIELD32(0x00000001) +#define PWRCSR1_BBP_DESIRE_STATE FIELD32(0x00000006) +#define PWRCSR1_RF_DESIRE_STATE FIELD32(0x00000018) +#define PWRCSR1_BBP_CURR_STATE FIELD32(0x00000060) +#define PWRCSR1_RF_CURR_STATE FIELD32(0x00000180) +#define PWRCSR1_PUT_TO_SLEEP FIELD32(0x00000200) + +/* + * TIMECSR: Timer control register. + * US_COUNT: 1 us timer count in units of clock cycles. + * US_64_COUNT: 64 us timer count in units of 1 us timer. + * BEACON_EXPECT: Beacon expect window. + */ +#define TIMECSR 0x00dc +#define TIMECSR_US_COUNT FIELD32(0x000000ff) +#define TIMECSR_US_64_COUNT FIELD32(0x0000ff00) +#define TIMECSR_BEACON_EXPECT FIELD32(0x00070000) + +/* + * MACCSR0: MAC configuration register 0. + */ +#define MACCSR0 0x00e0 + +/* + * MACCSR1: MAC configuration register 1. + * KICK_RX: Kick one-shot rx in one-shot rx mode. + * ONESHOT_RXMODE: Enable one-shot rx mode for debugging. + * BBPRX_RESET_MODE: Ralink bbp rx reset mode. + * AUTO_TXBBP: Auto tx logic access bbp control register. + * AUTO_RXBBP: Auto rx logic access bbp control register. + * LOOPBACK: Loopback mode. 0: normal, 1: internal, 2: external, 3:rsvd. + * INTERSIL_IF: Intersil if calibration pin. + */ +#define MACCSR1 0x00e4 +#define MACCSR1_KICK_RX FIELD32(0x00000001) +#define MACCSR1_ONESHOT_RXMODE FIELD32(0x00000002) +#define MACCSR1_BBPRX_RESET_MODE FIELD32(0x00000004) +#define MACCSR1_AUTO_TXBBP FIELD32(0x00000008) +#define MACCSR1_AUTO_RXBBP FIELD32(0x00000010) +#define MACCSR1_LOOPBACK FIELD32(0x00000060) +#define MACCSR1_INTERSIL_IF FIELD32(0x00000080) + +/* + * RALINKCSR: Ralink Rx auto-reset BBCR. + * AR_BBP_DATA#: Auto reset BBP register # data. + * AR_BBP_ID#: Auto reset BBP register # id. + */ +#define RALINKCSR 0x00e8 +#define RALINKCSR_AR_BBP_DATA0 FIELD32(0x000000ff) +#define RALINKCSR_AR_BBP_ID0 FIELD32(0x00007f00) +#define RALINKCSR_AR_BBP_VALID0 FIELD32(0x00008000) +#define RALINKCSR_AR_BBP_DATA1 FIELD32(0x00ff0000) +#define RALINKCSR_AR_BBP_ID1 FIELD32(0x7f000000) +#define RALINKCSR_AR_BBP_VALID1 FIELD32(0x80000000) + +/* + * BCNCSR: Beacon interval control register. + * CHANGE: Write one to change beacon interval. + * DELTATIME: The delta time value. + * NUM_BEACON: Number of beacon according to mode. + * MODE: Please refer to asic specs. + * PLUS: Plus or minus delta time value. + */ +#define BCNCSR 0x00ec +#define BCNCSR_CHANGE FIELD32(0x00000001) +#define BCNCSR_DELTATIME FIELD32(0x0000001e) +#define BCNCSR_NUM_BEACON FIELD32(0x00001fe0) +#define BCNCSR_MODE FIELD32(0x00006000) +#define BCNCSR_PLUS FIELD32(0x00008000) + +/* + * BBP / RF / IF Control Register. + */ + +/* + * BBPCSR: BBP serial control register. + * VALUE: Register value to program into BBP. + * REGNUM: Selected BBP register. + * BUSY: 1: asic is busy execute BBP programming. + * WRITE_CONTROL: 1: write BBP, 0: read BBP. + */ +#define BBPCSR 0x00f0 +#define BBPCSR_VALUE FIELD32(0x000000ff) +#define BBPCSR_REGNUM FIELD32(0x00007f00) +#define BBPCSR_BUSY FIELD32(0x00008000) +#define BBPCSR_WRITE_CONTROL FIELD32(0x00010000) + +/* + * RFCSR: RF serial control register. + * VALUE: Register value + id to program into rf/if. + * NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). + * IF_SELECT: Chip to program: 0: rf, 1: if. + * PLL_LD: Rf pll_ld status. + * BUSY: 1: asic is busy execute rf programming. + */ +#define RFCSR 0x00f4 +#define RFCSR_VALUE FIELD32(0x00ffffff) +#define RFCSR_NUMBER_OF_BITS FIELD32(0x1f000000) +#define RFCSR_IF_SELECT FIELD32(0x20000000) +#define RFCSR_PLL_LD FIELD32(0x40000000) +#define RFCSR_BUSY FIELD32(0x80000000) + +/* + * LEDCSR: LED control register. + * ON_PERIOD: On period, default 70ms. + * OFF_PERIOD: Off period, default 30ms. + * LINK: 0: linkoff, 1: linkup. + * ACTIVITY: 0: idle, 1: active. + * LINK_POLARITY: 0: active low, 1: active high. + * ACTIVITY_POLARITY: 0: active low, 1: active high. + * LED_DEFAULT: LED state for "enable" 0: ON, 1: OFF. + */ +#define LEDCSR 0x00f8 +#define LEDCSR_ON_PERIOD FIELD32(0x000000ff) +#define LEDCSR_OFF_PERIOD FIELD32(0x0000ff00) +#define LEDCSR_LINK FIELD32(0x00010000) +#define LEDCSR_ACTIVITY FIELD32(0x00020000) +#define LEDCSR_LINK_POLARITY FIELD32(0x00040000) +#define LEDCSR_ACTIVITY_POLARITY FIELD32(0x00080000) +#define LEDCSR_LED_DEFAULT FIELD32(0x00100000) + +/* + * SECCSR3: AES control register. + */ +#define SECCSR3 0x00fc + +/* + * ASIC pointer information. + * RXPTR: Current RX ring address. + * TXPTR: Current Tx ring address. + * PRIPTR: Current Priority ring address. + * ATIMPTR: Current ATIM ring address. + */ +#define RXPTR 0x0100 +#define TXPTR 0x0104 +#define PRIPTR 0x0108 +#define ATIMPTR 0x010c + +/* + * TXACKCSR0: TX ACK timeout. + */ +#define TXACKCSR0 0x0110 + +/* + * ACK timeout count registers. + * ACKCNT0: TX ACK timeout count. + * ACKCNT1: RX ACK timeout count. + */ +#define ACKCNT0 0x0114 +#define ACKCNT1 0x0118 + +/* + * GPIO and others. + */ + +/* + * GPIOCSR: GPIO control register. + * GPIOCSR_VALx: GPIO value + * GPIOCSR_DIRx: GPIO direction: 0 = output; 1 = input + */ +#define GPIOCSR 0x0120 +#define GPIOCSR_VAL0 FIELD32(0x00000001) +#define GPIOCSR_VAL1 FIELD32(0x00000002) +#define GPIOCSR_VAL2 FIELD32(0x00000004) +#define GPIOCSR_VAL3 FIELD32(0x00000008) +#define GPIOCSR_VAL4 FIELD32(0x00000010) +#define GPIOCSR_VAL5 FIELD32(0x00000020) +#define GPIOCSR_VAL6 FIELD32(0x00000040) +#define GPIOCSR_VAL7 FIELD32(0x00000080) +#define GPIOCSR_DIR0 FIELD32(0x00000100) +#define GPIOCSR_DIR1 FIELD32(0x00000200) +#define GPIOCSR_DIR2 FIELD32(0x00000400) +#define GPIOCSR_DIR3 FIELD32(0x00000800) +#define GPIOCSR_DIR4 FIELD32(0x00001000) +#define GPIOCSR_DIR5 FIELD32(0x00002000) +#define GPIOCSR_DIR6 FIELD32(0x00004000) +#define GPIOCSR_DIR7 FIELD32(0x00008000) + +/* + * FIFO pointer registers. + * FIFOCSR0: TX FIFO pointer. + * FIFOCSR1: RX FIFO pointer. + */ +#define FIFOCSR0 0x0128 +#define FIFOCSR1 0x012c + +/* + * BCNCSR1: Tx BEACON offset time control register. + * PRELOAD: Beacon timer offset in units of usec. + * BEACON_CWMIN: 2^CwMin. + */ +#define BCNCSR1 0x0130 +#define BCNCSR1_PRELOAD FIELD32(0x0000ffff) +#define BCNCSR1_BEACON_CWMIN FIELD32(0x000f0000) + +/* + * MACCSR2: TX_PE to RX_PE turn-around time control register + * DELAY: RX_PE low width, in units of pci clock cycle. + */ +#define MACCSR2 0x0134 +#define MACCSR2_DELAY FIELD32(0x000000ff) + +/* + * TESTCSR: TEST mode selection register. + */ +#define TESTCSR 0x0138 + +/* + * ARCSR2: 1 Mbps ACK/CTS PLCP. + */ +#define ARCSR2 0x013c +#define ARCSR2_SIGNAL FIELD32(0x000000ff) +#define ARCSR2_SERVICE FIELD32(0x0000ff00) +#define ARCSR2_LENGTH FIELD32(0xffff0000) + +/* + * ARCSR3: 2 Mbps ACK/CTS PLCP. + */ +#define ARCSR3 0x0140 +#define ARCSR3_SIGNAL FIELD32(0x000000ff) +#define ARCSR3_SERVICE FIELD32(0x0000ff00) +#define ARCSR3_LENGTH FIELD32(0xffff0000) + +/* + * ARCSR4: 5.5 Mbps ACK/CTS PLCP. + */ +#define ARCSR4 0x0144 +#define ARCSR4_SIGNAL FIELD32(0x000000ff) +#define ARCSR4_SERVICE FIELD32(0x0000ff00) +#define ARCSR4_LENGTH FIELD32(0xffff0000) + +/* + * ARCSR5: 11 Mbps ACK/CTS PLCP. + */ +#define ARCSR5 0x0148 +#define ARCSR5_SIGNAL FIELD32(0x000000ff) +#define ARCSR5_SERVICE FIELD32(0x0000ff00) +#define ARCSR5_LENGTH FIELD32(0xffff0000) + +/* + * ARTCSR0: CCK ACK/CTS payload consumed time for 1/2/5.5/11 mbps. + */ +#define ARTCSR0 0x014c +#define ARTCSR0_ACK_CTS_11MBS FIELD32(0x000000ff) +#define ARTCSR0_ACK_CTS_5_5MBS FIELD32(0x0000ff00) +#define ARTCSR0_ACK_CTS_2MBS FIELD32(0x00ff0000) +#define ARTCSR0_ACK_CTS_1MBS FIELD32(0xff000000) + + +/* + * ARTCSR1: OFDM ACK/CTS payload consumed time for 6/9/12/18 mbps. + */ +#define ARTCSR1 0x0150 +#define ARTCSR1_ACK_CTS_6MBS FIELD32(0x000000ff) +#define ARTCSR1_ACK_CTS_9MBS FIELD32(0x0000ff00) +#define ARTCSR1_ACK_CTS_12MBS FIELD32(0x00ff0000) +#define ARTCSR1_ACK_CTS_18MBS FIELD32(0xff000000) + +/* + * ARTCSR2: OFDM ACK/CTS payload consumed time for 24/36/48/54 mbps. + */ +#define ARTCSR2 0x0154 +#define ARTCSR2_ACK_CTS_24MBS FIELD32(0x000000ff) +#define ARTCSR2_ACK_CTS_36MBS FIELD32(0x0000ff00) +#define ARTCSR2_ACK_CTS_48MBS FIELD32(0x00ff0000) +#define ARTCSR2_ACK_CTS_54MBS FIELD32(0xff000000) + +/* + * SECCSR1: WEP control register. + * KICK_ENCRYPT: Kick encryption engine, self-clear. + * ONE_SHOT: 0: ring mode, 1: One shot only mode. + * DESC_ADDRESS: Descriptor physical address of frame. + */ +#define SECCSR1 0x0158 +#define SECCSR1_KICK_ENCRYPT FIELD32(0x00000001) +#define SECCSR1_ONE_SHOT FIELD32(0x00000002) +#define SECCSR1_DESC_ADDRESS FIELD32(0xfffffffc) + +/* + * BBPCSR1: BBP TX configuration. + */ +#define BBPCSR1 0x015c +#define BBPCSR1_CCK FIELD32(0x00000003) +#define BBPCSR1_CCK_FLIP FIELD32(0x00000004) +#define BBPCSR1_OFDM FIELD32(0x00030000) +#define BBPCSR1_OFDM_FLIP FIELD32(0x00040000) + +/* + * Dual band configuration registers. + * DBANDCSR0: Dual band configuration register 0. + * DBANDCSR1: Dual band configuration register 1. + */ +#define DBANDCSR0 0x0160 +#define DBANDCSR1 0x0164 + +/* + * BBPPCSR: BBP Pin control register. + */ +#define BBPPCSR 0x0168 + +/* + * MAC special debug mode selection registers. + * DBGSEL0: MAC special debug mode selection register 0. + * DBGSEL1: MAC special debug mode selection register 1. + */ +#define DBGSEL0 0x016c +#define DBGSEL1 0x0170 + +/* + * BISTCSR: BBP BIST register. + */ +#define BISTCSR 0x0174 + +/* + * Multicast filter registers. + * MCAST0: Multicast filter register 0. + * MCAST1: Multicast filter register 1. + */ +#define MCAST0 0x0178 +#define MCAST1 0x017c + +/* + * UART registers. + * UARTCSR0: UART1 TX register. + * UARTCSR1: UART1 RX register. + * UARTCSR3: UART1 frame control register. + * UARTCSR4: UART1 buffer control register. + * UART2CSR0: UART2 TX register. + * UART2CSR1: UART2 RX register. + * UART2CSR3: UART2 frame control register. + * UART2CSR4: UART2 buffer control register. + */ +#define UARTCSR0 0x0180 +#define UARTCSR1 0x0184 +#define UARTCSR3 0x0188 +#define UARTCSR4 0x018c +#define UART2CSR0 0x0190 +#define UART2CSR1 0x0194 +#define UART2CSR3 0x0198 +#define UART2CSR4 0x019c + +/* + * BBP registers. + * The wordsize of the BBP is 8 bits. + */ + +/* + * R2: TX antenna control + */ +#define BBP_R2_TX_ANTENNA FIELD8(0x03) +#define BBP_R2_TX_IQ_FLIP FIELD8(0x04) + +/* + * R14: RX antenna control + */ +#define BBP_R14_RX_ANTENNA FIELD8(0x03) +#define BBP_R14_RX_IQ_FLIP FIELD8(0x04) + +/* + * BBP_R70 + */ +#define BBP_R70_JAPAN_FILTER FIELD8(0x08) + +/* + * RF registers + */ + +/* + * RF 1 + */ +#define RF1_TUNER FIELD32(0x00020000) + +/* + * RF 3 + */ +#define RF3_TUNER FIELD32(0x00000100) +#define RF3_TXPOWER FIELD32(0x00003e00) + +/* + * EEPROM content. + * The wordsize of the EEPROM is 16 bits. + */ + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_0 0x0002 +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR1 0x0003 +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_2 0x0004 +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM antenna. + * ANTENNA_NUM: Number of antenna's. + * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * LED_MODE: 0: default, 1: TX/RX activity,2: Single (ignore link), 3: rsvd. + * DYN_TXAGC: Dynamic TX AGC control. + * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. + * RF_TYPE: Rf_type of this adapter. + */ +#define EEPROM_ANTENNA 0x10 +#define EEPROM_ANTENNA_NUM FIELD16(0x0003) +#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) +#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) +#define EEPROM_ANTENNA_LED_MODE FIELD16(0x01c0) +#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) +#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) +#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) + +/* + * EEPROM NIC config. + * CARDBUS_ACCEL: 0: enable, 1: disable. + * DYN_BBP_TUNE: 0: enable, 1: disable. + * CCK_TX_POWER: CCK TX power compensation. + */ +#define EEPROM_NIC 0x11 +#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0001) +#define EEPROM_NIC_DYN_BBP_TUNE FIELD16(0x0002) +#define EEPROM_NIC_CCK_TX_POWER FIELD16(0x000c) + +/* + * EEPROM geography. + * GEO: Default geography setting for device. + */ +#define EEPROM_GEOGRAPHY 0x12 +#define EEPROM_GEOGRAPHY_GEO FIELD16(0x0f00) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_START 0x13 +#define EEPROM_BBP_SIZE 16 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* + * EEPROM TXPOWER + */ +#define EEPROM_TXPOWER_START 0x23 +#define EEPROM_TXPOWER_SIZE 7 +#define EEPROM_TXPOWER_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_2 FIELD16(0xff00) + +/* + * RSSI <-> dBm offset calibration + */ +#define EEPROM_CALIBRATE_OFFSET 0x3e +#define EEPROM_CALIBRATE_OFFSET_RSSI FIELD16(0x00ff) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE (11 * sizeof(__le32)) +#define RXD_DESC_SIZE (11 * sizeof(__le32)) + +/* + * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. + */ + +/* + * Word0 + */ +#define TXD_W0_OWNER_NIC FIELD32(0x00000001) +#define TXD_W0_VALID FIELD32(0x00000002) +#define TXD_W0_RESULT FIELD32(0x0000001c) +#define TXD_W0_RETRY_COUNT FIELD32(0x000000e0) +#define TXD_W0_MORE_FRAG FIELD32(0x00000100) +#define TXD_W0_ACK FIELD32(0x00000200) +#define TXD_W0_TIMESTAMP FIELD32(0x00000400) +#define TXD_W0_OFDM FIELD32(0x00000800) +#define TXD_W0_CIPHER_OWNER FIELD32(0x00001000) +#define TXD_W0_IFS FIELD32(0x00006000) +#define TXD_W0_RETRY_MODE FIELD32(0x00008000) +#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * Word1 + */ +#define TXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) + +/* + * Word2 + */ +#define TXD_W2_IV_OFFSET FIELD32(0x0000003f) +#define TXD_W2_AIFS FIELD32(0x000000c0) +#define TXD_W2_CWMIN FIELD32(0x00000f00) +#define TXD_W2_CWMAX FIELD32(0x0000f000) + +/* + * Word3: PLCP information + */ +#define TXD_W3_PLCP_SIGNAL FIELD32(0x000000ff) +#define TXD_W3_PLCP_SERVICE FIELD32(0x0000ff00) +#define TXD_W3_PLCP_LENGTH_LOW FIELD32(0x00ff0000) +#define TXD_W3_PLCP_LENGTH_HIGH FIELD32(0xff000000) + +/* + * Word4 + */ +#define TXD_W4_IV FIELD32(0xffffffff) + +/* + * Word5 + */ +#define TXD_W5_EIV FIELD32(0xffffffff) + +/* + * Word6-9: Key + */ +#define TXD_W6_KEY FIELD32(0xffffffff) +#define TXD_W7_KEY FIELD32(0xffffffff) +#define TXD_W8_KEY FIELD32(0xffffffff) +#define TXD_W9_KEY FIELD32(0xffffffff) + +/* + * Word10 + */ +#define TXD_W10_RTS FIELD32(0x00000001) +#define TXD_W10_TX_RATE FIELD32(0x000000fe) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + */ +#define RXD_W0_OWNER_NIC FIELD32(0x00000001) +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) +#define RXD_W0_MULTICAST FIELD32(0x00000004) +#define RXD_W0_BROADCAST FIELD32(0x00000008) +#define RXD_W0_MY_BSS FIELD32(0x00000010) +#define RXD_W0_CRC_ERROR FIELD32(0x00000020) +#define RXD_W0_OFDM FIELD32(0x00000040) +#define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) +#define RXD_W0_CIPHER_OWNER FIELD32(0x00000100) +#define RXD_W0_ICV_ERROR FIELD32(0x00000200) +#define RXD_W0_IV_OFFSET FIELD32(0x0000fc00) +#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * Word1 + */ +#define RXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) + +/* + * Word2 + */ +#define RXD_W2_SIGNAL FIELD32(0x000000ff) +#define RXD_W2_RSSI FIELD32(0x0000ff00) +#define RXD_W2_TA FIELD32(0xffff0000) + +/* + * Word3 + */ +#define RXD_W3_TA FIELD32(0xffffffff) + +/* + * Word4 + */ +#define RXD_W4_IV FIELD32(0xffffffff) + +/* + * Word5 + */ +#define RXD_W5_EIV FIELD32(0xffffffff) + +/* + * Word6-9: Key + */ +#define RXD_W6_KEY FIELD32(0xffffffff) +#define RXD_W7_KEY FIELD32(0xffffffff) +#define RXD_W8_KEY FIELD32(0xffffffff) +#define RXD_W9_KEY FIELD32(0xffffffff) + +/* + * Word10 + */ +#define RXD_W10_DROP FIELD32(0x00000001) + +/* + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. + */ +#define MIN_TXPOWER 0 +#define MAX_TXPOWER 31 +#define DEFAULT_TXPOWER 24 + +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) + +#endif /* RT2500PCI_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c new file mode 100644 index 000000000000..b50d873145d5 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c @@ -0,0 +1,2001 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2500usb + Abstract: rt2500usb device specific routines. + Supported chipsets: RT2570. + */ + +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00usb.h" +#include "rt2500usb.h" + +/* + * Allow hardware encryption to be disabled. + */ +static bool modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2500usb_register_read and rt2500usb_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers BBPCSR and RFCSR to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_USB_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attampt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + * If the csr_mutex is already held then the _lock variants must + * be used instead. + */ +static inline void rt2500usb_register_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u16 *value) +{ + __le16 reg; + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, + USB_VENDOR_REQUEST_IN, offset, + ®, sizeof(reg)); + *value = le16_to_cpu(reg); +} + +static inline void rt2500usb_register_read_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u16 *value) +{ + __le16 reg; + rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ, + USB_VENDOR_REQUEST_IN, offset, + ®, sizeof(reg), REGISTER_TIMEOUT); + *value = le16_to_cpu(reg); +} + +static inline void rt2500usb_register_multiread(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u16 length) +{ + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, + USB_VENDOR_REQUEST_IN, offset, + value, length); +} + +static inline void rt2500usb_register_write(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u16 value) +{ + __le16 reg = cpu_to_le16(value); + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, offset, + ®, sizeof(reg)); +} + +static inline void rt2500usb_register_write_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u16 value) +{ + __le16 reg = cpu_to_le16(value); + rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, offset, + ®, sizeof(reg), REGISTER_TIMEOUT); +} + +static inline void rt2500usb_register_multiwrite(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u16 length) +{ + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, offset, + value, length); +} + +static int rt2500usb_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + struct rt2x00_field16 field, + u16 *reg) +{ + unsigned int i; + + for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) { + rt2500usb_register_read_lock(rt2x00dev, offset, reg); + if (!rt2x00_get_field16(*reg, field)) + return 1; + udelay(REGISTER_BUSY_DELAY); + } + + rt2x00_err(rt2x00dev, "Indirect register access failed: offset=0x%.08x, value=0x%.08x\n", + offset, *reg); + *reg = ~0; + + return 0; +} + +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2500usb_regbusy_read((__dev), PHY_CSR8, PHY_CSR8_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2500usb_regbusy_read((__dev), PHY_CSR10, PHY_CSR10_RF_BUSY, (__reg)) + +static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +{ + u16 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field16(®, PHY_CSR7_DATA, value); + rt2x00_set_field16(®, PHY_CSR7_REG_ID, word); + rt2x00_set_field16(®, PHY_CSR7_READ_CONTROL, 0); + + rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u8 *value) +{ + u16 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field16(®, PHY_CSR7_REG_ID, word); + rt2x00_set_field16(®, PHY_CSR7_READ_CONTROL, 1); + + rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); + + if (WAIT_FOR_BBP(rt2x00dev, ®)) + rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, ®); + } + + *value = rt2x00_get_field16(reg, PHY_CSR7_DATA); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2500usb_rf_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u32 value) +{ + u16 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field16(®, PHY_CSR9_RF_VALUE, value); + rt2500usb_register_write_lock(rt2x00dev, PHY_CSR9, reg); + + reg = 0; + rt2x00_set_field16(®, PHY_CSR10_RF_VALUE, value >> 16); + rt2x00_set_field16(®, PHY_CSR10_RF_NUMBER_OF_BITS, 20); + rt2x00_set_field16(®, PHY_CSR10_RF_IF_SELECT, 0); + rt2x00_set_field16(®, PHY_CSR10_RF_BUSY, 1); + + rt2500usb_register_write_lock(rt2x00dev, PHY_CSR10, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +#ifdef CONFIG_RT2X00_LIB_DEBUGFS +static void _rt2500usb_register_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + rt2500usb_register_read(rt2x00dev, offset, (u16 *)value); +} + +static void _rt2500usb_register_write(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + rt2500usb_register_write(rt2x00dev, offset, value); +} + +static const struct rt2x00debug rt2500usb_rt2x00debug = { + .owner = THIS_MODULE, + .csr = { + .read = _rt2500usb_register_read, + .write = _rt2500usb_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, + .word_size = sizeof(u16), + .word_count = CSR_REG_SIZE / sizeof(u16), + }, + .eeprom = { + .read = rt2x00_eeprom_read, + .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .bbp = { + .read = rt2500usb_bbp_read, + .write = rt2500usb_bbp_write, + .word_base = BBP_BASE, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, + .rf = { + .read = rt2x00_rf_read, + .write = rt2500usb_rf_write, + .word_base = RF_BASE, + .word_size = sizeof(u32), + .word_count = RF_SIZE / sizeof(u32), + }, +}; +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ + +static int rt2500usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) +{ + u16 reg; + + rt2500usb_register_read(rt2x00dev, MAC_CSR19, ®); + return rt2x00_get_field16(reg, MAC_CSR19_VAL7); +} + +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt2500usb_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + u16 reg; + + rt2500usb_register_read(led->rt2x00dev, MAC_CSR20, ®); + + if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) + rt2x00_set_field16(®, MAC_CSR20_LINK, enabled); + else if (led->type == LED_TYPE_ACTIVITY) + rt2x00_set_field16(®, MAC_CSR20_ACTIVITY, enabled); + + rt2500usb_register_write(led->rt2x00dev, MAC_CSR20, reg); +} + +static int rt2500usb_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + u16 reg; + + rt2500usb_register_read(led->rt2x00dev, MAC_CSR21, ®); + rt2x00_set_field16(®, MAC_CSR21_ON_PERIOD, *delay_on); + rt2x00_set_field16(®, MAC_CSR21_OFF_PERIOD, *delay_off); + rt2500usb_register_write(led->rt2x00dev, MAC_CSR21, reg); + + return 0; +} + +static void rt2500usb_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + enum led_type type) +{ + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt2500usb_brightness_set; + led->led_dev.blink_set = rt2500usb_blink_set; + led->flags = LED_INITIALIZED; +} +#endif /* CONFIG_RT2X00_LIB_LEDS */ + +/* + * Configuration handlers. + */ + +/* + * rt2500usb does not differentiate between shared and pairwise + * keys, so we should use the same function for both key types. + */ +static int rt2500usb_config_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + u32 mask; + u16 reg; + enum cipher curr_cipher; + + if (crypto->cmd == SET_KEY) { + /* + * Disallow to set WEP key other than with index 0, + * it is known that not work at least on some hardware. + * SW crypto will be used in that case. + */ + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && + key->keyidx != 0) + return -EOPNOTSUPP; + + /* + * Pairwise key will always be entry 0, but this + * could collide with a shared key on the same + * position... + */ + mask = TXRX_CSR0_KEY_ID.bit_mask; + + rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); + curr_cipher = rt2x00_get_field16(reg, TXRX_CSR0_ALGORITHM); + reg &= mask; + + if (reg && reg == mask) + return -ENOSPC; + + reg = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID); + + key->hw_key_idx += reg ? ffz(reg) : 0; + /* + * Hardware requires that all keys use the same cipher + * (e.g. TKIP-only, AES-only, but not TKIP+AES). + * If this is not the first key, compare the cipher with the + * first one and fall back to SW crypto if not the same. + */ + if (key->hw_key_idx > 0 && crypto->cipher != curr_cipher) + return -EOPNOTSUPP; + + rt2500usb_register_multiwrite(rt2x00dev, KEY_ENTRY(key->hw_key_idx), + crypto->key, sizeof(crypto->key)); + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it demands the data to be provided + * both separately as well as inside the frame. + * We already provided the CONFIG_CRYPTO_COPY_IV to rt2x00lib + * to ensure rt2x00lib will not strip the data from the + * frame after the copy, now we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + } + + /* + * TXRX_CSR0_KEY_ID contains only single-bit fields to indicate + * a particular key is valid. + */ + rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field16(®, TXRX_CSR0_ALGORITHM, crypto->cipher); + rt2x00_set_field16(®, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER); + + mask = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID); + if (crypto->cmd == SET_KEY) + mask |= 1 << key->hw_key_idx; + else if (crypto->cmd == DISABLE_KEY) + mask &= ~(1 << key->hw_key_idx); + rt2x00_set_field16(®, TXRX_CSR0_KEY_ID, mask); + rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg); + + return 0; +} + +static void rt2500usb_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) +{ + u16 reg; + + /* + * Start configuration steps. + * Note that the version error will always be dropped + * and broadcast frames will always be accepted since + * there is no filter for it at this time. + */ + rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00_set_field16(®, TXRX_CSR2_DROP_CRC, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field16(®, TXRX_CSR2_DROP_PHYSICAL, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field16(®, TXRX_CSR2_DROP_CONTROL, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field16(®, TXRX_CSR2_DROP_NOT_TO_ME, 1); + rt2x00_set_field16(®, TXRX_CSR2_DROP_TODS, + !rt2x00dev->intf_ap_count); + rt2x00_set_field16(®, TXRX_CSR2_DROP_VERSION_ERROR, 1); + rt2x00_set_field16(®, TXRX_CSR2_DROP_MULTICAST, + !(filter_flags & FIF_ALLMULTI)); + rt2x00_set_field16(®, TXRX_CSR2_DROP_BROADCAST, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); +} + +static void rt2500usb_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags) +{ + unsigned int bcn_preload; + u16 reg; + + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable beacon config + */ + bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); + rt2500usb_register_read(rt2x00dev, TXRX_CSR20, ®); + rt2x00_set_field16(®, TXRX_CSR20_OFFSET, bcn_preload >> 6); + rt2x00_set_field16(®, TXRX_CSR20_BCN_EXPECT_WINDOW, + 2 * (conf->type != NL80211_IFTYPE_STATION)); + rt2500usb_register_write(rt2x00dev, TXRX_CSR20, reg); + + /* + * Enable synchronisation. + */ + rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®); + rt2x00_set_field16(®, TXRX_CSR18_OFFSET, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg); + + rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); + rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, conf->sync); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + } + + if (flags & CONFIG_UPDATE_MAC) + rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR2, conf->mac, + (3 * sizeof(__le16))); + + if (flags & CONFIG_UPDATE_BSSID) + rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR5, conf->bssid, + (3 * sizeof(__le16))); +} + +static void rt2500usb_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed) +{ + u16 reg; + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rt2500usb_register_read(rt2x00dev, TXRX_CSR10, ®); + rt2x00_set_field16(®, TXRX_CSR10_AUTORESPOND_PREAMBLE, + !!erp->short_preamble); + rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg); + } + + if (changed & BSS_CHANGED_BASIC_RATES) + rt2500usb_register_write(rt2x00dev, TXRX_CSR11, + erp->basic_rates); + + if (changed & BSS_CHANGED_BEACON_INT) { + rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®); + rt2x00_set_field16(®, TXRX_CSR18_INTERVAL, + erp->beacon_int * 4); + rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time); + rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs); + rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs); + } +} + +static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) +{ + u8 r2; + u8 r14; + u16 csr5; + u16 csr6; + + /* + * We should never come here because rt2x00lib is supposed + * to catch this and send us the correct antenna explicitely. + */ + BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || + ant->tx == ANTENNA_SW_DIVERSITY); + + rt2500usb_bbp_read(rt2x00dev, 2, &r2); + rt2500usb_bbp_read(rt2x00dev, 14, &r14); + rt2500usb_register_read(rt2x00dev, PHY_CSR5, &csr5); + rt2500usb_register_read(rt2x00dev, PHY_CSR6, &csr6); + + /* + * Configure the TX antenna. + */ + switch (ant->tx) { + case ANTENNA_HW_DIVERSITY: + rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 1); + rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 1); + rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 1); + break; + case ANTENNA_A: + rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0); + rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 0); + rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 0); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2); + rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 2); + rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 2); + break; + } + + /* + * Configure the RX antenna. + */ + switch (ant->rx) { + case ANTENNA_HW_DIVERSITY: + rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 1); + break; + case ANTENNA_A: + rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2); + break; + } + + /* + * RT2525E and RT5222 need to flip TX I/Q + */ + if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) { + rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1); + rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 1); + rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 1); + + /* + * RT2525E does not need RX I/Q Flip. + */ + if (rt2x00_rf(rt2x00dev, RF2525E)) + rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0); + } else { + rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 0); + rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 0); + } + + rt2500usb_bbp_write(rt2x00dev, 2, r2); + rt2500usb_bbp_write(rt2x00dev, 14, r14); + rt2500usb_register_write(rt2x00dev, PHY_CSR5, csr5); + rt2500usb_register_write(rt2x00dev, PHY_CSR6, csr6); +} + +static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev, + struct rf_channel *rf, const int txpower) +{ + /* + * Set TXpower. + */ + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); + + /* + * For RT2525E we should first set the channel to half band higher. + */ + if (rt2x00_rf(rt2x00dev, RF2525E)) { + static const u32 vals[] = { + 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2, + 0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba, + 0x000008ba, 0x000008be, 0x000008b7, 0x00000902, + 0x00000902, 0x00000906 + }; + + rt2500usb_rf_write(rt2x00dev, 2, vals[rf->channel - 1]); + if (rf->rf4) + rt2500usb_rf_write(rt2x00dev, 4, rf->rf4); + } + + rt2500usb_rf_write(rt2x00dev, 1, rf->rf1); + rt2500usb_rf_write(rt2x00dev, 2, rf->rf2); + rt2500usb_rf_write(rt2x00dev, 3, rf->rf3); + if (rf->rf4) + rt2500usb_rf_write(rt2x00dev, 4, rf->rf4); +} + +static void rt2500usb_config_txpower(struct rt2x00_dev *rt2x00dev, + const int txpower) +{ + u32 rf3; + + rt2x00_rf_read(rt2x00dev, 3, &rf3); + rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); + rt2500usb_rf_write(rt2x00dev, 3, rf3); +} + +static void rt2500usb_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; + u16 reg; + + if (state == STATE_SLEEP) { + rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); + rt2x00_set_field16(®, MAC_CSR18_DELAY_AFTER_BEACON, + rt2x00dev->beacon_int - 20); + rt2x00_set_field16(®, MAC_CSR18_BEACONS_BEFORE_WAKEUP, + libconf->conf->listen_interval - 1); + + /* We must first disable autowake before it can be enabled */ + rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 0); + rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); + + rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 1); + rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); + } else { + rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); + rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 0); + rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); + } + + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); +} + +static void rt2500usb_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags) +{ + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) + rt2500usb_config_channel(rt2x00dev, &libconf->rf, + libconf->conf->power_level); + if ((flags & IEEE80211_CONF_CHANGE_POWER) && + !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) + rt2500usb_config_txpower(rt2x00dev, + libconf->conf->power_level); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt2500usb_config_ps(rt2x00dev, libconf); +} + +/* + * Link tuning + */ +static void rt2500usb_link_stats(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) +{ + u16 reg; + + /* + * Update FCS error count from register. + */ + rt2500usb_register_read(rt2x00dev, STA_CSR0, ®); + qual->rx_failed = rt2x00_get_field16(reg, STA_CSR0_FCS_ERROR); + + /* + * Update False CCA count from register. + */ + rt2500usb_register_read(rt2x00dev, STA_CSR3, ®); + qual->false_cca = rt2x00_get_field16(reg, STA_CSR3_FALSE_CCA_ERROR); +} + +static void rt2500usb_reset_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) +{ + u16 eeprom; + u16 value; + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &eeprom); + value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R24_LOW); + rt2500usb_bbp_write(rt2x00dev, 24, value); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &eeprom); + value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R25_LOW); + rt2500usb_bbp_write(rt2x00dev, 25, value); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &eeprom); + value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R61_LOW); + rt2500usb_bbp_write(rt2x00dev, 61, value); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &eeprom); + value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_VGCUPPER); + rt2500usb_bbp_write(rt2x00dev, 17, value); + + qual->vgc_level = value; +} + +/* + * Queue handlers. + */ +static void rt2500usb_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u16 reg; + + switch (queue->qid) { + case QID_RX: + rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); + break; + case QID_BEACON: + rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); + rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 1); + rt2x00_set_field16(®, TXRX_CSR19_TBCN, 1); + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 1); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + break; + default: + break; + } +} + +static void rt2500usb_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u16 reg; + + switch (queue->qid) { + case QID_RX: + rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, 1); + rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); + break; + case QID_BEACON: + rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); + rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 0); + rt2x00_set_field16(®, TXRX_CSR19_TBCN, 0); + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + break; + default: + break; + } +} + +/* + * Initialization functions. + */ +static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u16 reg; + + rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0x0001, + USB_MODE_TEST, REGISTER_TIMEOUT); + rt2x00usb_vendor_request_sw(rt2x00dev, USB_SINGLE_WRITE, 0x0308, + 0x00f0, REGISTER_TIMEOUT); + + rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, 1); + rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); + + rt2500usb_register_write(rt2x00dev, MAC_CSR13, 0x1111); + rt2500usb_register_write(rt2x00dev, MAC_CSR14, 0x1e11); + + rt2500usb_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field16(®, MAC_CSR1_SOFT_RESET, 1); + rt2x00_set_field16(®, MAC_CSR1_BBP_RESET, 1); + rt2x00_set_field16(®, MAC_CSR1_HOST_READY, 0); + rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2500usb_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field16(®, MAC_CSR1_SOFT_RESET, 0); + rt2x00_set_field16(®, MAC_CSR1_BBP_RESET, 0); + rt2x00_set_field16(®, MAC_CSR1_HOST_READY, 0); + rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2500usb_register_read(rt2x00dev, TXRX_CSR5, ®); + rt2x00_set_field16(®, TXRX_CSR5_BBP_ID0, 13); + rt2x00_set_field16(®, TXRX_CSR5_BBP_ID0_VALID, 1); + rt2x00_set_field16(®, TXRX_CSR5_BBP_ID1, 12); + rt2x00_set_field16(®, TXRX_CSR5_BBP_ID1_VALID, 1); + rt2500usb_register_write(rt2x00dev, TXRX_CSR5, reg); + + rt2500usb_register_read(rt2x00dev, TXRX_CSR6, ®); + rt2x00_set_field16(®, TXRX_CSR6_BBP_ID0, 10); + rt2x00_set_field16(®, TXRX_CSR6_BBP_ID0_VALID, 1); + rt2x00_set_field16(®, TXRX_CSR6_BBP_ID1, 11); + rt2x00_set_field16(®, TXRX_CSR6_BBP_ID1_VALID, 1); + rt2500usb_register_write(rt2x00dev, TXRX_CSR6, reg); + + rt2500usb_register_read(rt2x00dev, TXRX_CSR7, ®); + rt2x00_set_field16(®, TXRX_CSR7_BBP_ID0, 7); + rt2x00_set_field16(®, TXRX_CSR7_BBP_ID0_VALID, 1); + rt2x00_set_field16(®, TXRX_CSR7_BBP_ID1, 6); + rt2x00_set_field16(®, TXRX_CSR7_BBP_ID1_VALID, 1); + rt2500usb_register_write(rt2x00dev, TXRX_CSR7, reg); + + rt2500usb_register_read(rt2x00dev, TXRX_CSR8, ®); + rt2x00_set_field16(®, TXRX_CSR8_BBP_ID0, 5); + rt2x00_set_field16(®, TXRX_CSR8_BBP_ID0_VALID, 1); + rt2x00_set_field16(®, TXRX_CSR8_BBP_ID1, 0); + rt2x00_set_field16(®, TXRX_CSR8_BBP_ID1_VALID, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR8, reg); + + rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); + rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 0); + rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, 0); + rt2x00_set_field16(®, TXRX_CSR19_TBCN, 0); + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + + rt2500usb_register_write(rt2x00dev, TXRX_CSR21, 0xe78f); + rt2500usb_register_write(rt2x00dev, MAC_CSR9, 0xff1d); + + if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) + return -EBUSY; + + rt2500usb_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field16(®, MAC_CSR1_SOFT_RESET, 0); + rt2x00_set_field16(®, MAC_CSR1_BBP_RESET, 0); + rt2x00_set_field16(®, MAC_CSR1_HOST_READY, 1); + rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg); + + if (rt2x00_rev(rt2x00dev) >= RT2570_VERSION_C) { + rt2500usb_register_read(rt2x00dev, PHY_CSR2, ®); + rt2x00_set_field16(®, PHY_CSR2_LNA, 0); + } else { + reg = 0; + rt2x00_set_field16(®, PHY_CSR2_LNA, 1); + rt2x00_set_field16(®, PHY_CSR2_LNA_MODE, 3); + } + rt2500usb_register_write(rt2x00dev, PHY_CSR2, reg); + + rt2500usb_register_write(rt2x00dev, MAC_CSR11, 0x0002); + rt2500usb_register_write(rt2x00dev, MAC_CSR22, 0x0053); + rt2500usb_register_write(rt2x00dev, MAC_CSR15, 0x01ee); + rt2500usb_register_write(rt2x00dev, MAC_CSR16, 0x0000); + + rt2500usb_register_read(rt2x00dev, MAC_CSR8, ®); + rt2x00_set_field16(®, MAC_CSR8_MAX_FRAME_UNIT, + rt2x00dev->rx->data_size); + rt2500usb_register_write(rt2x00dev, MAC_CSR8, reg); + + rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field16(®, TXRX_CSR0_ALGORITHM, CIPHER_NONE); + rt2x00_set_field16(®, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER); + rt2x00_set_field16(®, TXRX_CSR0_KEY_ID, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg); + + rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); + rt2x00_set_field16(®, MAC_CSR18_DELAY_AFTER_BEACON, 90); + rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); + + rt2500usb_register_read(rt2x00dev, PHY_CSR4, ®); + rt2x00_set_field16(®, PHY_CSR4_LOW_RF_LE, 1); + rt2500usb_register_write(rt2x00dev, PHY_CSR4, reg); + + rt2500usb_register_read(rt2x00dev, TXRX_CSR1, ®); + rt2x00_set_field16(®, TXRX_CSR1_AUTO_SEQUENCE, 1); + rt2500usb_register_write(rt2x00dev, TXRX_CSR1, reg); + + return 0; +} + +static int rt2500usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u8 value; + + for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) { + rt2500usb_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + return 0; + udelay(REGISTER_BUSY_DELAY); + } + + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); + return -EACCES; +} + +static int rt2500usb_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 value; + u8 reg_id; + + if (unlikely(rt2500usb_wait_bbp_ready(rt2x00dev))) + return -EACCES; + + rt2500usb_bbp_write(rt2x00dev, 3, 0x02); + rt2500usb_bbp_write(rt2x00dev, 4, 0x19); + rt2500usb_bbp_write(rt2x00dev, 14, 0x1c); + rt2500usb_bbp_write(rt2x00dev, 15, 0x30); + rt2500usb_bbp_write(rt2x00dev, 16, 0xac); + rt2500usb_bbp_write(rt2x00dev, 18, 0x18); + rt2500usb_bbp_write(rt2x00dev, 19, 0xff); + rt2500usb_bbp_write(rt2x00dev, 20, 0x1e); + rt2500usb_bbp_write(rt2x00dev, 21, 0x08); + rt2500usb_bbp_write(rt2x00dev, 22, 0x08); + rt2500usb_bbp_write(rt2x00dev, 23, 0x08); + rt2500usb_bbp_write(rt2x00dev, 24, 0x80); + rt2500usb_bbp_write(rt2x00dev, 25, 0x50); + rt2500usb_bbp_write(rt2x00dev, 26, 0x08); + rt2500usb_bbp_write(rt2x00dev, 27, 0x23); + rt2500usb_bbp_write(rt2x00dev, 30, 0x10); + rt2500usb_bbp_write(rt2x00dev, 31, 0x2b); + rt2500usb_bbp_write(rt2x00dev, 32, 0xb9); + rt2500usb_bbp_write(rt2x00dev, 34, 0x12); + rt2500usb_bbp_write(rt2x00dev, 35, 0x50); + rt2500usb_bbp_write(rt2x00dev, 39, 0xc4); + rt2500usb_bbp_write(rt2x00dev, 40, 0x02); + rt2500usb_bbp_write(rt2x00dev, 41, 0x60); + rt2500usb_bbp_write(rt2x00dev, 53, 0x10); + rt2500usb_bbp_write(rt2x00dev, 54, 0x18); + rt2500usb_bbp_write(rt2x00dev, 56, 0x08); + rt2500usb_bbp_write(rt2x00dev, 57, 0x10); + rt2500usb_bbp_write(rt2x00dev, 58, 0x08); + rt2500usb_bbp_write(rt2x00dev, 61, 0x60); + rt2500usb_bbp_write(rt2x00dev, 62, 0x10); + rt2500usb_bbp_write(rt2x00dev, 75, 0xff); + + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + rt2500usb_bbp_write(rt2x00dev, reg_id, value); + } + } + + return 0; +} + +/* + * Device state switch handlers. + */ +static int rt2500usb_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + /* + * Initialize all registers. + */ + if (unlikely(rt2500usb_init_registers(rt2x00dev) || + rt2500usb_init_bbp(rt2x00dev))) + return -EIO; + + return 0; +} + +static void rt2500usb_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + rt2500usb_register_write(rt2x00dev, MAC_CSR13, 0x2121); + rt2500usb_register_write(rt2x00dev, MAC_CSR14, 0x2121); + + /* + * Disable synchronisation. + */ + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, 0); + + rt2x00usb_disable_radio(rt2x00dev); +} + +static int rt2500usb_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u16 reg; + u16 reg2; + unsigned int i; + char put_to_sleep; + char bbp_state; + char rf_state; + + put_to_sleep = (state != STATE_AWAKE); + + reg = 0; + rt2x00_set_field16(®, MAC_CSR17_BBP_DESIRE_STATE, state); + rt2x00_set_field16(®, MAC_CSR17_RF_DESIRE_STATE, state); + rt2x00_set_field16(®, MAC_CSR17_PUT_TO_SLEEP, put_to_sleep); + rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg); + rt2x00_set_field16(®, MAC_CSR17_SET_STATE, 1); + rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg); + + /* + * Device is not guaranteed to be in the requested state yet. + * We must wait until the register indicates that the + * device has entered the correct state. + */ + for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) { + rt2500usb_register_read(rt2x00dev, MAC_CSR17, ®2); + bbp_state = rt2x00_get_field16(reg2, MAC_CSR17_BBP_CURR_STATE); + rf_state = rt2x00_get_field16(reg2, MAC_CSR17_RF_CURR_STATE); + if (bbp_state == state && rf_state == state) + return 0; + rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg); + msleep(30); + } + + return -EBUSY; +} + +static int rt2500usb_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + retval = rt2500usb_enable_radio(rt2x00dev); + break; + case STATE_RADIO_OFF: + rt2500usb_disable_radio(rt2x00dev); + break; + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + /* No support, but no error either */ + break; + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + retval = rt2500usb_set_state(rt2x00dev, state); + break; + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +/* + * TX descriptor initialization + */ +static void rt2500usb_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + __le32 *txd = (__le32 *) entry->skb->data; + u32 word; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_RETRY_LIMIT, txdesc->retry_limit); + rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_ACK, + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_OFDM, + (txdesc->rate_mode == RATE_MODE_OFDM)); + rt2x00_set_field32(&word, TXD_W0_NEW_SEQ, + test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); + rt2x00_set_field32(&word, TXD_W0_CIPHER, !!txdesc->cipher); + rt2x00_set_field32(&word, TXD_W0_KEY_ID, txdesc->key_idx); + rt2x00_desc_write(txd, 0, word); + + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); + rt2x00_set_field32(&word, TXD_W1_AIFS, entry->queue->aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, + txdesc->u.plcp.length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, + txdesc->u.plcp.length_high); + rt2x00_desc_write(txd, 2, word); + + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { + _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); + _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); + } + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->flags |= SKBDESC_DESC_IN_SKB; + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; +} + +/* + * TX data initialization + */ +static void rt2500usb_beacondone(struct urb *urb); + +static void rt2500usb_write_beacon(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); + struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; + int pipe = usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint); + int length; + u16 reg, reg0; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + + /* + * Add space for the descriptor in front of the skb. + */ + skb_push(entry->skb, TXD_DESC_SIZE); + memset(entry->skb->data, 0, TXD_DESC_SIZE); + + /* + * Write the TX descriptor for the beacon. + */ + rt2500usb_write_tx_desc(entry, txdesc); + + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); + + /* + * USB devices cannot blindly pass the skb->len as the + * length of the data to usb_fill_bulk_urb. Pass the skb + * to the driver to determine what the length should be. + */ + length = rt2x00dev->ops->lib->get_tx_data_len(entry); + + usb_fill_bulk_urb(bcn_priv->urb, usb_dev, pipe, + entry->skb->data, length, rt2500usb_beacondone, + entry); + + /* + * Second we need to create the guardian byte. + * We only need a single byte, so lets recycle + * the 'flags' field we are not using for beacons. + */ + bcn_priv->guardian_data = 0; + usb_fill_bulk_urb(bcn_priv->guardian_urb, usb_dev, pipe, + &bcn_priv->guardian_data, 1, rt2500usb_beacondone, + entry); + + /* + * Send out the guardian byte. + */ + usb_submit_urb(bcn_priv->guardian_urb, GFP_ATOMIC); + + /* + * Enable beaconing again. + */ + rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 1); + rt2x00_set_field16(®, TXRX_CSR19_TBCN, 1); + reg0 = reg; + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 1); + /* + * Beacon generation will fail initially. + * To prevent this we need to change the TXRX_CSR19 + * register several times (reg0 is the same as reg + * except for TXRX_CSR19_BEACON_GEN, which is 0 in reg0 + * and 1 in reg). + */ + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); +} + +static int rt2500usb_get_tx_data_len(struct queue_entry *entry) +{ + int length; + + /* + * The length _must_ be a multiple of 2, + * but it must _not_ be a multiple of the USB packet size. + */ + length = roundup(entry->skb->len, 2); + length += (2 * !(length % entry->queue->usb_maxpacket)); + + return length; +} + +/* + * RX control handlers + */ +static void rt2500usb_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct queue_entry_priv_usb *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + __le32 *rxd = + (__le32 *)(entry->skb->data + + (entry_priv->urb->actual_length - + entry->queue->desc_size)); + u32 word0; + u32 word1; + + /* + * Copy descriptor to the skbdesc->desc buffer, making it safe from moving of + * frame data in rt2x00usb. + */ + memcpy(skbdesc->desc, rxd, skbdesc->desc_len); + rxd = (__le32 *)skbdesc->desc; + + /* + * It is now safe to read the descriptor on all architectures. + */ + rt2x00_desc_read(rxd, 0, &word0); + rt2x00_desc_read(rxd, 1, &word1); + + if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; + + rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER); + if (rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR)) + rxdesc->cipher_status = RX_CRYPTO_FAIL_KEY; + + if (rxdesc->cipher != CIPHER_NONE) { + _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]); + _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]); + rxdesc->dev_flags |= RXDONE_CRYPTO_IV; + + /* ICV is located at the end of frame */ + + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + + /* + * Obtain the status about this packet. + * When frame was received with an OFDM bitrate, + * the signal is the PLCP value. If it was received with + * a CCK bitrate the signal is the rate in 100kbit/s. + */ + rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); + rxdesc->rssi = + rt2x00_get_field32(word1, RXD_W1_RSSI) - rt2x00dev->rssi_offset; + rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + if (rt2x00_get_field32(word0, RXD_W0_OFDM)) + rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; + else + rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; + if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; + + /* + * Adjust the skb memory window to the frame boundaries. + */ + skb_trim(entry->skb, rxdesc->size); +} + +/* + * Interrupt functions. + */ +static void rt2500usb_beacondone(struct urb *urb) +{ + struct queue_entry *entry = (struct queue_entry *)urb->context; + struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &entry->queue->rt2x00dev->flags)) + return; + + /* + * Check if this was the guardian beacon, + * if that was the case we need to send the real beacon now. + * Otherwise we should free the sk_buffer, the device + * should be doing the rest of the work now. + */ + if (bcn_priv->guardian_urb == urb) { + usb_submit_urb(bcn_priv->urb, GFP_ATOMIC); + } else if (bcn_priv->urb == urb) { + dev_kfree_skb(entry->skb); + entry->skb = NULL; + } +} + +/* + * Device probe functions. + */ +static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u16 word; + u8 *mac; + u8 bbp; + + rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE); + + /* + * Start validation of the data that has been read. + */ + mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + if (!is_valid_ether_addr(mac)) { + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); + rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, + ANTENNA_SW_DIVERSITY); + rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, + ANTENNA_SW_DIVERSITY); + rt2x00_set_field16(&word, EEPROM_ANTENNA_LED_MODE, + LED_MODE_DEFAULT); + rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); + rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); + rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2522); + rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); + rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); + rt2x00_set_field16(&word, EEPROM_NIC_DYN_BBP_TUNE, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CCK_TX_POWER, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); + rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_CALIBRATE_OFFSET_RSSI, + DEFAULT_RSSI_OFFSET); + rt2x00_eeprom_write(rt2x00dev, EEPROM_CALIBRATE_OFFSET, word); + rt2x00_eeprom_dbg(rt2x00dev, "Calibrate offset: 0x%04x\n", + word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_BBPTUNE_THRESHOLD, 45); + rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE, word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune: 0x%04x\n", word); + } + + /* + * Switch lower vgc bound to current BBP R17 value, + * lower the value a bit for better quality. + */ + rt2500usb_bbp_read(rt2x00dev, 17, &bbp); + bbp -= 6; + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCUPPER, 0x40); + rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp); + rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune vgc: 0x%04x\n", word); + } else { + rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp); + rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R17, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_LOW, 0x48); + rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_HIGH, 0x41); + rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R17, word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r17: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_LOW, 0x40); + rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_HIGH, 0x80); + rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R24, word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r24: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_LOW, 0x40); + rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_HIGH, 0x50); + rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R25, word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r25: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_LOW, 0x60); + rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_HIGH, 0x6d); + rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R61, word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r61: 0x%04x\n", word); + } + + return 0; +} + +static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u16 reg; + u16 value; + u16 eeprom; + + /* + * Read EEPROM word for configuration. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Identify RF chipset. + */ + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); + rt2500usb_register_read(rt2x00dev, MAC_CSR0, ®); + rt2x00_set_chip(rt2x00dev, RT2570, value, reg); + + if (((reg & 0xfff0) != 0) || ((reg & 0x0000000f) == 0)) { + rt2x00_err(rt2x00dev, "Invalid RT chipset detected\n"); + return -ENODEV; + } + + if (!rt2x00_rf(rt2x00dev, RF2522) && + !rt2x00_rf(rt2x00dev, RF2523) && + !rt2x00_rf(rt2x00dev, RF2524) && + !rt2x00_rf(rt2x00dev, RF2525) && + !rt2x00_rf(rt2x00dev, RF2525E) && + !rt2x00_rf(rt2x00dev, RF5222)) { + rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); + return -ENODEV; + } + + /* + * Identify default antenna configuration. + */ + rt2x00dev->default_ant.tx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); + rt2x00dev->default_ant.rx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); + + /* + * When the eeprom indicates SW_DIVERSITY use HW_DIVERSITY instead. + * I am not 100% sure about this, but the legacy drivers do not + * indicate antenna swapping in software is required when + * diversity is enabled. + */ + if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY) + rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY; + if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY) + rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY; + + /* + * Store led mode, for correct led behaviour. + */ +#ifdef CONFIG_RT2X00_LIB_LEDS + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); + + rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + if (value == LED_MODE_TXRX_ACTIVITY || + value == LED_MODE_DEFAULT || + value == LED_MODE_ASUS) + rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_qual, + LED_TYPE_ACTIVITY); +#endif /* CONFIG_RT2X00_LIB_LEDS */ + + /* + * Detect if this device has an hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); + + /* + * Read the RSSI <-> dBm offset information. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &eeprom); + rt2x00dev->rssi_offset = + rt2x00_get_field16(eeprom, EEPROM_CALIBRATE_OFFSET_RSSI); + + return 0; +} + +/* + * RF value list for RF2522 + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_bg_2522[] = { + { 1, 0x00002050, 0x000c1fda, 0x00000101, 0 }, + { 2, 0x00002050, 0x000c1fee, 0x00000101, 0 }, + { 3, 0x00002050, 0x000c2002, 0x00000101, 0 }, + { 4, 0x00002050, 0x000c2016, 0x00000101, 0 }, + { 5, 0x00002050, 0x000c202a, 0x00000101, 0 }, + { 6, 0x00002050, 0x000c203e, 0x00000101, 0 }, + { 7, 0x00002050, 0x000c2052, 0x00000101, 0 }, + { 8, 0x00002050, 0x000c2066, 0x00000101, 0 }, + { 9, 0x00002050, 0x000c207a, 0x00000101, 0 }, + { 10, 0x00002050, 0x000c208e, 0x00000101, 0 }, + { 11, 0x00002050, 0x000c20a2, 0x00000101, 0 }, + { 12, 0x00002050, 0x000c20b6, 0x00000101, 0 }, + { 13, 0x00002050, 0x000c20ca, 0x00000101, 0 }, + { 14, 0x00002050, 0x000c20fa, 0x00000101, 0 }, +}; + +/* + * RF value list for RF2523 + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_bg_2523[] = { + { 1, 0x00022010, 0x00000c9e, 0x000e0111, 0x00000a1b }, + { 2, 0x00022010, 0x00000ca2, 0x000e0111, 0x00000a1b }, + { 3, 0x00022010, 0x00000ca6, 0x000e0111, 0x00000a1b }, + { 4, 0x00022010, 0x00000caa, 0x000e0111, 0x00000a1b }, + { 5, 0x00022010, 0x00000cae, 0x000e0111, 0x00000a1b }, + { 6, 0x00022010, 0x00000cb2, 0x000e0111, 0x00000a1b }, + { 7, 0x00022010, 0x00000cb6, 0x000e0111, 0x00000a1b }, + { 8, 0x00022010, 0x00000cba, 0x000e0111, 0x00000a1b }, + { 9, 0x00022010, 0x00000cbe, 0x000e0111, 0x00000a1b }, + { 10, 0x00022010, 0x00000d02, 0x000e0111, 0x00000a1b }, + { 11, 0x00022010, 0x00000d06, 0x000e0111, 0x00000a1b }, + { 12, 0x00022010, 0x00000d0a, 0x000e0111, 0x00000a1b }, + { 13, 0x00022010, 0x00000d0e, 0x000e0111, 0x00000a1b }, + { 14, 0x00022010, 0x00000d1a, 0x000e0111, 0x00000a03 }, +}; + +/* + * RF value list for RF2524 + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_bg_2524[] = { + { 1, 0x00032020, 0x00000c9e, 0x00000101, 0x00000a1b }, + { 2, 0x00032020, 0x00000ca2, 0x00000101, 0x00000a1b }, + { 3, 0x00032020, 0x00000ca6, 0x00000101, 0x00000a1b }, + { 4, 0x00032020, 0x00000caa, 0x00000101, 0x00000a1b }, + { 5, 0x00032020, 0x00000cae, 0x00000101, 0x00000a1b }, + { 6, 0x00032020, 0x00000cb2, 0x00000101, 0x00000a1b }, + { 7, 0x00032020, 0x00000cb6, 0x00000101, 0x00000a1b }, + { 8, 0x00032020, 0x00000cba, 0x00000101, 0x00000a1b }, + { 9, 0x00032020, 0x00000cbe, 0x00000101, 0x00000a1b }, + { 10, 0x00032020, 0x00000d02, 0x00000101, 0x00000a1b }, + { 11, 0x00032020, 0x00000d06, 0x00000101, 0x00000a1b }, + { 12, 0x00032020, 0x00000d0a, 0x00000101, 0x00000a1b }, + { 13, 0x00032020, 0x00000d0e, 0x00000101, 0x00000a1b }, + { 14, 0x00032020, 0x00000d1a, 0x00000101, 0x00000a03 }, +}; + +/* + * RF value list for RF2525 + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_bg_2525[] = { + { 1, 0x00022020, 0x00080c9e, 0x00060111, 0x00000a1b }, + { 2, 0x00022020, 0x00080ca2, 0x00060111, 0x00000a1b }, + { 3, 0x00022020, 0x00080ca6, 0x00060111, 0x00000a1b }, + { 4, 0x00022020, 0x00080caa, 0x00060111, 0x00000a1b }, + { 5, 0x00022020, 0x00080cae, 0x00060111, 0x00000a1b }, + { 6, 0x00022020, 0x00080cb2, 0x00060111, 0x00000a1b }, + { 7, 0x00022020, 0x00080cb6, 0x00060111, 0x00000a1b }, + { 8, 0x00022020, 0x00080cba, 0x00060111, 0x00000a1b }, + { 9, 0x00022020, 0x00080cbe, 0x00060111, 0x00000a1b }, + { 10, 0x00022020, 0x00080d02, 0x00060111, 0x00000a1b }, + { 11, 0x00022020, 0x00080d06, 0x00060111, 0x00000a1b }, + { 12, 0x00022020, 0x00080d0a, 0x00060111, 0x00000a1b }, + { 13, 0x00022020, 0x00080d0e, 0x00060111, 0x00000a1b }, + { 14, 0x00022020, 0x00080d1a, 0x00060111, 0x00000a03 }, +}; + +/* + * RF value list for RF2525e + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_bg_2525e[] = { + { 1, 0x00022010, 0x0000089a, 0x00060111, 0x00000e1b }, + { 2, 0x00022010, 0x0000089e, 0x00060111, 0x00000e07 }, + { 3, 0x00022010, 0x0000089e, 0x00060111, 0x00000e1b }, + { 4, 0x00022010, 0x000008a2, 0x00060111, 0x00000e07 }, + { 5, 0x00022010, 0x000008a2, 0x00060111, 0x00000e1b }, + { 6, 0x00022010, 0x000008a6, 0x00060111, 0x00000e07 }, + { 7, 0x00022010, 0x000008a6, 0x00060111, 0x00000e1b }, + { 8, 0x00022010, 0x000008aa, 0x00060111, 0x00000e07 }, + { 9, 0x00022010, 0x000008aa, 0x00060111, 0x00000e1b }, + { 10, 0x00022010, 0x000008ae, 0x00060111, 0x00000e07 }, + { 11, 0x00022010, 0x000008ae, 0x00060111, 0x00000e1b }, + { 12, 0x00022010, 0x000008b2, 0x00060111, 0x00000e07 }, + { 13, 0x00022010, 0x000008b2, 0x00060111, 0x00000e1b }, + { 14, 0x00022010, 0x000008b6, 0x00060111, 0x00000e23 }, +}; + +/* + * RF value list for RF5222 + * Supports: 2.4 GHz & 5.2 GHz + */ +static const struct rf_channel rf_vals_5222[] = { + { 1, 0x00022020, 0x00001136, 0x00000101, 0x00000a0b }, + { 2, 0x00022020, 0x0000113a, 0x00000101, 0x00000a0b }, + { 3, 0x00022020, 0x0000113e, 0x00000101, 0x00000a0b }, + { 4, 0x00022020, 0x00001182, 0x00000101, 0x00000a0b }, + { 5, 0x00022020, 0x00001186, 0x00000101, 0x00000a0b }, + { 6, 0x00022020, 0x0000118a, 0x00000101, 0x00000a0b }, + { 7, 0x00022020, 0x0000118e, 0x00000101, 0x00000a0b }, + { 8, 0x00022020, 0x00001192, 0x00000101, 0x00000a0b }, + { 9, 0x00022020, 0x00001196, 0x00000101, 0x00000a0b }, + { 10, 0x00022020, 0x0000119a, 0x00000101, 0x00000a0b }, + { 11, 0x00022020, 0x0000119e, 0x00000101, 0x00000a0b }, + { 12, 0x00022020, 0x000011a2, 0x00000101, 0x00000a0b }, + { 13, 0x00022020, 0x000011a6, 0x00000101, 0x00000a0b }, + { 14, 0x00022020, 0x000011ae, 0x00000101, 0x00000a1b }, + + /* 802.11 UNI / HyperLan 2 */ + { 36, 0x00022010, 0x00018896, 0x00000101, 0x00000a1f }, + { 40, 0x00022010, 0x0001889a, 0x00000101, 0x00000a1f }, + { 44, 0x00022010, 0x0001889e, 0x00000101, 0x00000a1f }, + { 48, 0x00022010, 0x000188a2, 0x00000101, 0x00000a1f }, + { 52, 0x00022010, 0x000188a6, 0x00000101, 0x00000a1f }, + { 66, 0x00022010, 0x000188aa, 0x00000101, 0x00000a1f }, + { 60, 0x00022010, 0x000188ae, 0x00000101, 0x00000a1f }, + { 64, 0x00022010, 0x000188b2, 0x00000101, 0x00000a1f }, + + /* 802.11 HyperLan 2 */ + { 100, 0x00022010, 0x00008802, 0x00000101, 0x00000a0f }, + { 104, 0x00022010, 0x00008806, 0x00000101, 0x00000a0f }, + { 108, 0x00022010, 0x0000880a, 0x00000101, 0x00000a0f }, + { 112, 0x00022010, 0x0000880e, 0x00000101, 0x00000a0f }, + { 116, 0x00022010, 0x00008812, 0x00000101, 0x00000a0f }, + { 120, 0x00022010, 0x00008816, 0x00000101, 0x00000a0f }, + { 124, 0x00022010, 0x0000881a, 0x00000101, 0x00000a0f }, + { 128, 0x00022010, 0x0000881e, 0x00000101, 0x00000a0f }, + { 132, 0x00022010, 0x00008822, 0x00000101, 0x00000a0f }, + { 136, 0x00022010, 0x00008826, 0x00000101, 0x00000a0f }, + + /* 802.11 UNII */ + { 140, 0x00022010, 0x0000882a, 0x00000101, 0x00000a0f }, + { 149, 0x00022020, 0x000090a6, 0x00000101, 0x00000a07 }, + { 153, 0x00022020, 0x000090ae, 0x00000101, 0x00000a07 }, + { 157, 0x00022020, 0x000090b6, 0x00000101, 0x00000a07 }, + { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 }, +}; + +static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + struct channel_info *info; + char *tx_power; + unsigned int i; + + /* + * Initialize all hw fields. + * + * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are + * capable of sending the buffered frames out after the DTIM + * transmission using rt2x00lib_beacondone. This will send out + * multicast and broadcast traffic immediately instead of buffering it + * infinitly and thus dropping it after some time. + */ + ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); + ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); + ieee80211_hw_set(rt2x00dev->hw, RX_INCLUDES_FCS); + ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); + + /* + * Disable powersaving as default. + */ + rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, + rt2x00_eeprom_addr(rt2x00dev, + EEPROM_MAC_ADDR_0)); + + /* + * Initialize hw_mode information. + */ + spec->supported_bands = SUPPORT_BAND_2GHZ; + spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; + + if (rt2x00_rf(rt2x00dev, RF2522)) { + spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); + spec->channels = rf_vals_bg_2522; + } else if (rt2x00_rf(rt2x00dev, RF2523)) { + spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523); + spec->channels = rf_vals_bg_2523; + } else if (rt2x00_rf(rt2x00dev, RF2524)) { + spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524); + spec->channels = rf_vals_bg_2524; + } else if (rt2x00_rf(rt2x00dev, RF2525)) { + spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525); + spec->channels = rf_vals_bg_2525; + } else if (rt2x00_rf(rt2x00dev, RF2525E)) { + spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e); + spec->channels = rf_vals_bg_2525e; + } else if (rt2x00_rf(rt2x00dev, RF5222)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; + spec->num_channels = ARRAY_SIZE(rf_vals_5222); + spec->channels = rf_vals_5222; + } + + /* + * Create channel information array + */ + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); + } + + if (spec->num_channels > 14) { + for (i = 14; i < spec->num_channels; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = DEFAULT_TXPOWER; + } + } + + return 0; +} + +static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + u16 reg; + + /* + * Allocate eeprom data. + */ + retval = rt2500usb_validate_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt2500usb_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. + */ + rt2500usb_register_read(rt2x00dev, MAC_CSR19, ®); + rt2x00_set_field16(®, MAC_CSR19_DIR0, 0); + rt2500usb_register_write(rt2x00dev, MAC_CSR19, reg); + + /* + * Initialize hw specifications. + */ + retval = rt2500usb_probe_hw_mode(rt2x00dev); + if (retval) + return retval; + + /* + * This device requires the atim queue + */ + __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags); + if (!modparam_nohwcrypt) { + __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_COPY_IV, &rt2x00dev->cap_flags); + } + __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); + + /* + * Set the rssi offset. + */ + rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; + + return 0; +} + +static const struct ieee80211_ops rt2500usb_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2x00mac_conf_tx, + .rfkill_poll = rt2x00mac_rfkill_poll, + .flush = rt2x00mac_flush, + .set_antenna = rt2x00mac_set_antenna, + .get_antenna = rt2x00mac_get_antenna, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = { + .probe_hw = rt2500usb_probe_hw, + .initialize = rt2x00usb_initialize, + .uninitialize = rt2x00usb_uninitialize, + .clear_entry = rt2x00usb_clear_entry, + .set_device_state = rt2500usb_set_device_state, + .rfkill_poll = rt2500usb_rfkill_poll, + .link_stats = rt2500usb_link_stats, + .reset_tuner = rt2500usb_reset_tuner, + .watchdog = rt2x00usb_watchdog, + .start_queue = rt2500usb_start_queue, + .kick_queue = rt2x00usb_kick_queue, + .stop_queue = rt2500usb_stop_queue, + .flush_queue = rt2x00usb_flush_queue, + .write_tx_desc = rt2500usb_write_tx_desc, + .write_beacon = rt2500usb_write_beacon, + .get_tx_data_len = rt2500usb_get_tx_data_len, + .fill_rxdone = rt2500usb_fill_rxdone, + .config_shared_key = rt2500usb_config_key, + .config_pairwise_key = rt2500usb_config_key, + .config_filter = rt2500usb_config_filter, + .config_intf = rt2500usb_config_intf, + .config_erp = rt2500usb_config_erp, + .config_ant = rt2500usb_config_ant, + .config = rt2500usb_config, +}; + +static void rt2500usb_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb_bcn); + break; + + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + default: + BUG(); + break; + } +} + +static const struct rt2x00_ops rt2500usb_ops = { + .name = KBUILD_MODNAME, + .max_ap_intf = 1, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2500usb_queue_init, + .lib = &rt2500usb_rt2x00_ops, + .hw = &rt2500usb_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2500usb_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +/* + * rt2500usb module information. + */ +static struct usb_device_id rt2500usb_device_table[] = { + /* ASUS */ + { USB_DEVICE(0x0b05, 0x1706) }, + { USB_DEVICE(0x0b05, 0x1707) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x7050) }, /* FCC ID: K7SF5D7050A ver. 2.x */ + { USB_DEVICE(0x050d, 0x7051) }, + /* Cisco Systems */ + { USB_DEVICE(0x13b1, 0x000d) }, + { USB_DEVICE(0x13b1, 0x0011) }, + { USB_DEVICE(0x13b1, 0x001a) }, + /* Conceptronic */ + { USB_DEVICE(0x14b2, 0x3c02) }, + /* D-LINK */ + { USB_DEVICE(0x2001, 0x3c00) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x8001) }, + { USB_DEVICE(0x1044, 0x8007) }, + /* Hercules */ + { USB_DEVICE(0x06f8, 0xe000) }, + /* Melco */ + { USB_DEVICE(0x0411, 0x005e) }, + { USB_DEVICE(0x0411, 0x0066) }, + { USB_DEVICE(0x0411, 0x0067) }, + { USB_DEVICE(0x0411, 0x008b) }, + { USB_DEVICE(0x0411, 0x0097) }, + /* MSI */ + { USB_DEVICE(0x0db0, 0x6861) }, + { USB_DEVICE(0x0db0, 0x6865) }, + { USB_DEVICE(0x0db0, 0x6869) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x1706) }, + { USB_DEVICE(0x148f, 0x2570) }, + { USB_DEVICE(0x148f, 0x9020) }, + /* Sagem */ + { USB_DEVICE(0x079b, 0x004b) }, + /* Siemens */ + { USB_DEVICE(0x0681, 0x3c06) }, + /* SMC */ + { USB_DEVICE(0x0707, 0xee13) }, + /* Spairon */ + { USB_DEVICE(0x114b, 0x0110) }, + /* SURECOM */ + { USB_DEVICE(0x0769, 0x11f3) }, + /* Trust */ + { USB_DEVICE(0x0eb0, 0x9020) }, + /* VTech */ + { USB_DEVICE(0x0f88, 0x3012) }, + /* Zinwell */ + { USB_DEVICE(0x5a57, 0x0260) }, + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2500 USB Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2570 USB chipset based cards"); +MODULE_DEVICE_TABLE(usb, rt2500usb_device_table); +MODULE_LICENSE("GPL"); + +static int rt2500usb_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + return rt2x00usb_probe(usb_intf, &rt2500usb_ops); +} + +static struct usb_driver rt2500usb_driver = { + .name = KBUILD_MODNAME, + .id_table = rt2500usb_device_table, + .probe = rt2500usb_probe, + .disconnect = rt2x00usb_disconnect, + .suspend = rt2x00usb_suspend, + .resume = rt2x00usb_resume, + .reset_resume = rt2x00usb_resume, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(rt2500usb_driver); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500usb.h b/drivers/net/wireless/ralink/rt2x00/rt2500usb.h new file mode 100644 index 000000000000..78cc035b2d17 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.h @@ -0,0 +1,855 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2500usb + Abstract: Data structures and registers for the rt2500usb module. + Supported chipsets: RT2570. + */ + +#ifndef RT2500USB_H +#define RT2500USB_H + +/* + * RF chip defines. + */ +#define RF2522 0x0000 +#define RF2523 0x0001 +#define RF2524 0x0002 +#define RF2525 0x0003 +#define RF2525E 0x0005 +#define RF5222 0x0010 + +/* + * RT2570 version + */ +#define RT2570_VERSION_B 2 +#define RT2570_VERSION_C 3 +#define RT2570_VERSION_D 4 + +/* + * Signal information. + * Default offset is required for RSSI <-> dBm conversion. + */ +#define DEFAULT_RSSI_OFFSET 120 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x0400 +#define CSR_REG_SIZE 0x0100 +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x006e +#define BBP_BASE 0x0000 +#define BBP_SIZE 0x0060 +#define RF_BASE 0x0004 +#define RF_SIZE 0x0010 + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 2 + +/* + * Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * MAC_CSR0: ASIC revision number. + */ +#define MAC_CSR0 0x0400 + +/* + * MAC_CSR1: System control. + * SOFT_RESET: Software reset, 1: reset, 0: normal. + * BBP_RESET: Hardware reset, 1: reset, 0, release. + * HOST_READY: Host ready after initialization. + */ +#define MAC_CSR1 0x0402 +#define MAC_CSR1_SOFT_RESET FIELD16(0x00000001) +#define MAC_CSR1_BBP_RESET FIELD16(0x00000002) +#define MAC_CSR1_HOST_READY FIELD16(0x00000004) + +/* + * MAC_CSR2: STA MAC register 0. + */ +#define MAC_CSR2 0x0404 +#define MAC_CSR2_BYTE0 FIELD16(0x00ff) +#define MAC_CSR2_BYTE1 FIELD16(0xff00) + +/* + * MAC_CSR3: STA MAC register 1. + */ +#define MAC_CSR3 0x0406 +#define MAC_CSR3_BYTE2 FIELD16(0x00ff) +#define MAC_CSR3_BYTE3 FIELD16(0xff00) + +/* + * MAC_CSR4: STA MAC register 2. + */ +#define MAC_CSR4 0X0408 +#define MAC_CSR4_BYTE4 FIELD16(0x00ff) +#define MAC_CSR4_BYTE5 FIELD16(0xff00) + +/* + * MAC_CSR5: BSSID register 0. + */ +#define MAC_CSR5 0x040a +#define MAC_CSR5_BYTE0 FIELD16(0x00ff) +#define MAC_CSR5_BYTE1 FIELD16(0xff00) + +/* + * MAC_CSR6: BSSID register 1. + */ +#define MAC_CSR6 0x040c +#define MAC_CSR6_BYTE2 FIELD16(0x00ff) +#define MAC_CSR6_BYTE3 FIELD16(0xff00) + +/* + * MAC_CSR7: BSSID register 2. + */ +#define MAC_CSR7 0x040e +#define MAC_CSR7_BYTE4 FIELD16(0x00ff) +#define MAC_CSR7_BYTE5 FIELD16(0xff00) + +/* + * MAC_CSR8: Max frame length. + */ +#define MAC_CSR8 0x0410 +#define MAC_CSR8_MAX_FRAME_UNIT FIELD16(0x0fff) + +/* + * Misc MAC_CSR registers. + * MAC_CSR9: Timer control. + * MAC_CSR10: Slot time. + * MAC_CSR11: SIFS. + * MAC_CSR12: EIFS. + * MAC_CSR13: Power mode0. + * MAC_CSR14: Power mode1. + * MAC_CSR15: Power saving transition0 + * MAC_CSR16: Power saving transition1 + */ +#define MAC_CSR9 0x0412 +#define MAC_CSR10 0x0414 +#define MAC_CSR11 0x0416 +#define MAC_CSR12 0x0418 +#define MAC_CSR13 0x041a +#define MAC_CSR14 0x041c +#define MAC_CSR15 0x041e +#define MAC_CSR16 0x0420 + +/* + * MAC_CSR17: Manual power control / status register. + * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. + * SET_STATE: Set state. Write 1 to trigger, self cleared. + * BBP_DESIRE_STATE: BBP desired state. + * RF_DESIRE_STATE: RF desired state. + * BBP_CURRENT_STATE: BBP current state. + * RF_CURRENT_STATE: RF current state. + * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. + */ +#define MAC_CSR17 0x0422 +#define MAC_CSR17_SET_STATE FIELD16(0x0001) +#define MAC_CSR17_BBP_DESIRE_STATE FIELD16(0x0006) +#define MAC_CSR17_RF_DESIRE_STATE FIELD16(0x0018) +#define MAC_CSR17_BBP_CURR_STATE FIELD16(0x0060) +#define MAC_CSR17_RF_CURR_STATE FIELD16(0x0180) +#define MAC_CSR17_PUT_TO_SLEEP FIELD16(0x0200) + +/* + * MAC_CSR18: Wakeup timer register. + * DELAY_AFTER_BEACON: Delay after Tbcn expired in units of 1/16 TU. + * BEACONS_BEFORE_WAKEUP: Number of beacon before wakeup. + * AUTO_WAKE: Enable auto wakeup / sleep mechanism. + */ +#define MAC_CSR18 0x0424 +#define MAC_CSR18_DELAY_AFTER_BEACON FIELD16(0x00ff) +#define MAC_CSR18_BEACONS_BEFORE_WAKEUP FIELD16(0x7f00) +#define MAC_CSR18_AUTO_WAKE FIELD16(0x8000) + +/* + * MAC_CSR19: GPIO control register. + * MAC_CSR19_VALx: GPIO value + * MAC_CSR19_DIRx: GPIO direction: 0 = input; 1 = output + */ +#define MAC_CSR19 0x0426 +#define MAC_CSR19_VAL0 FIELD16(0x0001) +#define MAC_CSR19_VAL1 FIELD16(0x0002) +#define MAC_CSR19_VAL2 FIELD16(0x0004) +#define MAC_CSR19_VAL3 FIELD16(0x0008) +#define MAC_CSR19_VAL4 FIELD16(0x0010) +#define MAC_CSR19_VAL5 FIELD16(0x0020) +#define MAC_CSR19_VAL6 FIELD16(0x0040) +#define MAC_CSR19_VAL7 FIELD16(0x0080) +#define MAC_CSR19_DIR0 FIELD16(0x0100) +#define MAC_CSR19_DIR1 FIELD16(0x0200) +#define MAC_CSR19_DIR2 FIELD16(0x0400) +#define MAC_CSR19_DIR3 FIELD16(0x0800) +#define MAC_CSR19_DIR4 FIELD16(0x1000) +#define MAC_CSR19_DIR5 FIELD16(0x2000) +#define MAC_CSR19_DIR6 FIELD16(0x4000) +#define MAC_CSR19_DIR7 FIELD16(0x8000) + +/* + * MAC_CSR20: LED control register. + * ACTIVITY: 0: idle, 1: active. + * LINK: 0: linkoff, 1: linkup. + * ACTIVITY_POLARITY: 0: active low, 1: active high. + */ +#define MAC_CSR20 0x0428 +#define MAC_CSR20_ACTIVITY FIELD16(0x0001) +#define MAC_CSR20_LINK FIELD16(0x0002) +#define MAC_CSR20_ACTIVITY_POLARITY FIELD16(0x0004) + +/* + * MAC_CSR21: LED control register. + * ON_PERIOD: On period, default 70ms. + * OFF_PERIOD: Off period, default 30ms. + */ +#define MAC_CSR21 0x042a +#define MAC_CSR21_ON_PERIOD FIELD16(0x00ff) +#define MAC_CSR21_OFF_PERIOD FIELD16(0xff00) + +/* + * MAC_CSR22: Collision window control register. + */ +#define MAC_CSR22 0x042c + +/* + * Transmit related CSRs. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * TXRX_CSR0: Security control register. + */ +#define TXRX_CSR0 0x0440 +#define TXRX_CSR0_ALGORITHM FIELD16(0x0007) +#define TXRX_CSR0_IV_OFFSET FIELD16(0x01f8) +#define TXRX_CSR0_KEY_ID FIELD16(0x1e00) + +/* + * TXRX_CSR1: TX configuration. + * ACK_TIMEOUT: ACK Timeout in unit of 1-us. + * TSF_OFFSET: TSF offset in MAC header. + * AUTO_SEQUENCE: Let ASIC control frame sequence number. + */ +#define TXRX_CSR1 0x0442 +#define TXRX_CSR1_ACK_TIMEOUT FIELD16(0x00ff) +#define TXRX_CSR1_TSF_OFFSET FIELD16(0x7f00) +#define TXRX_CSR1_AUTO_SEQUENCE FIELD16(0x8000) + +/* + * TXRX_CSR2: RX control. + * DISABLE_RX: Disable rx engine. + * DROP_CRC: Drop crc error. + * DROP_PHYSICAL: Drop physical error. + * DROP_CONTROL: Drop control frame. + * DROP_NOT_TO_ME: Drop not to me unicast frame. + * DROP_TODS: Drop frame tods bit is true. + * DROP_VERSION_ERROR: Drop version error frame. + * DROP_MCAST: Drop multicast frames. + * DROP_BCAST: Drop broadcast frames. + */ +#define TXRX_CSR2 0x0444 +#define TXRX_CSR2_DISABLE_RX FIELD16(0x0001) +#define TXRX_CSR2_DROP_CRC FIELD16(0x0002) +#define TXRX_CSR2_DROP_PHYSICAL FIELD16(0x0004) +#define TXRX_CSR2_DROP_CONTROL FIELD16(0x0008) +#define TXRX_CSR2_DROP_NOT_TO_ME FIELD16(0x0010) +#define TXRX_CSR2_DROP_TODS FIELD16(0x0020) +#define TXRX_CSR2_DROP_VERSION_ERROR FIELD16(0x0040) +#define TXRX_CSR2_DROP_MULTICAST FIELD16(0x0200) +#define TXRX_CSR2_DROP_BROADCAST FIELD16(0x0400) + +/* + * RX BBP ID registers + * TXRX_CSR3: CCK RX BBP ID. + * TXRX_CSR4: OFDM RX BBP ID. + */ +#define TXRX_CSR3 0x0446 +#define TXRX_CSR4 0x0448 + +/* + * TXRX_CSR5: CCK TX BBP ID0. + */ +#define TXRX_CSR5 0x044a +#define TXRX_CSR5_BBP_ID0 FIELD16(0x007f) +#define TXRX_CSR5_BBP_ID0_VALID FIELD16(0x0080) +#define TXRX_CSR5_BBP_ID1 FIELD16(0x7f00) +#define TXRX_CSR5_BBP_ID1_VALID FIELD16(0x8000) + +/* + * TXRX_CSR6: CCK TX BBP ID1. + */ +#define TXRX_CSR6 0x044c +#define TXRX_CSR6_BBP_ID0 FIELD16(0x007f) +#define TXRX_CSR6_BBP_ID0_VALID FIELD16(0x0080) +#define TXRX_CSR6_BBP_ID1 FIELD16(0x7f00) +#define TXRX_CSR6_BBP_ID1_VALID FIELD16(0x8000) + +/* + * TXRX_CSR7: OFDM TX BBP ID0. + */ +#define TXRX_CSR7 0x044e +#define TXRX_CSR7_BBP_ID0 FIELD16(0x007f) +#define TXRX_CSR7_BBP_ID0_VALID FIELD16(0x0080) +#define TXRX_CSR7_BBP_ID1 FIELD16(0x7f00) +#define TXRX_CSR7_BBP_ID1_VALID FIELD16(0x8000) + +/* + * TXRX_CSR8: OFDM TX BBP ID1. + */ +#define TXRX_CSR8 0x0450 +#define TXRX_CSR8_BBP_ID0 FIELD16(0x007f) +#define TXRX_CSR8_BBP_ID0_VALID FIELD16(0x0080) +#define TXRX_CSR8_BBP_ID1 FIELD16(0x7f00) +#define TXRX_CSR8_BBP_ID1_VALID FIELD16(0x8000) + +/* + * TXRX_CSR9: TX ACK time-out. + */ +#define TXRX_CSR9 0x0452 + +/* + * TXRX_CSR10: Auto responder control. + */ +#define TXRX_CSR10 0x0454 +#define TXRX_CSR10_AUTORESPOND_PREAMBLE FIELD16(0x0004) + +/* + * TXRX_CSR11: Auto responder basic rate. + */ +#define TXRX_CSR11 0x0456 + +/* + * ACK/CTS time registers. + */ +#define TXRX_CSR12 0x0458 +#define TXRX_CSR13 0x045a +#define TXRX_CSR14 0x045c +#define TXRX_CSR15 0x045e +#define TXRX_CSR16 0x0460 +#define TXRX_CSR17 0x0462 + +/* + * TXRX_CSR18: Synchronization control register. + */ +#define TXRX_CSR18 0x0464 +#define TXRX_CSR18_OFFSET FIELD16(0x000f) +#define TXRX_CSR18_INTERVAL FIELD16(0xfff0) + +/* + * TXRX_CSR19: Synchronization control register. + * TSF_COUNT: Enable TSF auto counting. + * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. + * TBCN: Enable Tbcn with reload value. + * BEACON_GEN: Enable beacon generator. + */ +#define TXRX_CSR19 0x0466 +#define TXRX_CSR19_TSF_COUNT FIELD16(0x0001) +#define TXRX_CSR19_TSF_SYNC FIELD16(0x0006) +#define TXRX_CSR19_TBCN FIELD16(0x0008) +#define TXRX_CSR19_BEACON_GEN FIELD16(0x0010) + +/* + * TXRX_CSR20: Tx BEACON offset time control register. + * OFFSET: In units of usec. + * BCN_EXPECT_WINDOW: Default: 2^CWmin + */ +#define TXRX_CSR20 0x0468 +#define TXRX_CSR20_OFFSET FIELD16(0x1fff) +#define TXRX_CSR20_BCN_EXPECT_WINDOW FIELD16(0xe000) + +/* + * TXRX_CSR21 + */ +#define TXRX_CSR21 0x046a + +/* + * Encryption related CSRs. + * + */ + +/* + * SEC_CSR0: Shared key 0, word 0 + * SEC_CSR1: Shared key 0, word 1 + * SEC_CSR2: Shared key 0, word 2 + * SEC_CSR3: Shared key 0, word 3 + * SEC_CSR4: Shared key 0, word 4 + * SEC_CSR5: Shared key 0, word 5 + * SEC_CSR6: Shared key 0, word 6 + * SEC_CSR7: Shared key 0, word 7 + */ +#define SEC_CSR0 0x0480 +#define SEC_CSR1 0x0482 +#define SEC_CSR2 0x0484 +#define SEC_CSR3 0x0486 +#define SEC_CSR4 0x0488 +#define SEC_CSR5 0x048a +#define SEC_CSR6 0x048c +#define SEC_CSR7 0x048e + +/* + * SEC_CSR8: Shared key 1, word 0 + * SEC_CSR9: Shared key 1, word 1 + * SEC_CSR10: Shared key 1, word 2 + * SEC_CSR11: Shared key 1, word 3 + * SEC_CSR12: Shared key 1, word 4 + * SEC_CSR13: Shared key 1, word 5 + * SEC_CSR14: Shared key 1, word 6 + * SEC_CSR15: Shared key 1, word 7 + */ +#define SEC_CSR8 0x0490 +#define SEC_CSR9 0x0492 +#define SEC_CSR10 0x0494 +#define SEC_CSR11 0x0496 +#define SEC_CSR12 0x0498 +#define SEC_CSR13 0x049a +#define SEC_CSR14 0x049c +#define SEC_CSR15 0x049e + +/* + * SEC_CSR16: Shared key 2, word 0 + * SEC_CSR17: Shared key 2, word 1 + * SEC_CSR18: Shared key 2, word 2 + * SEC_CSR19: Shared key 2, word 3 + * SEC_CSR20: Shared key 2, word 4 + * SEC_CSR21: Shared key 2, word 5 + * SEC_CSR22: Shared key 2, word 6 + * SEC_CSR23: Shared key 2, word 7 + */ +#define SEC_CSR16 0x04a0 +#define SEC_CSR17 0x04a2 +#define SEC_CSR18 0X04A4 +#define SEC_CSR19 0x04a6 +#define SEC_CSR20 0x04a8 +#define SEC_CSR21 0x04aa +#define SEC_CSR22 0x04ac +#define SEC_CSR23 0x04ae + +/* + * SEC_CSR24: Shared key 3, word 0 + * SEC_CSR25: Shared key 3, word 1 + * SEC_CSR26: Shared key 3, word 2 + * SEC_CSR27: Shared key 3, word 3 + * SEC_CSR28: Shared key 3, word 4 + * SEC_CSR29: Shared key 3, word 5 + * SEC_CSR30: Shared key 3, word 6 + * SEC_CSR31: Shared key 3, word 7 + */ +#define SEC_CSR24 0x04b0 +#define SEC_CSR25 0x04b2 +#define SEC_CSR26 0x04b4 +#define SEC_CSR27 0x04b6 +#define SEC_CSR28 0x04b8 +#define SEC_CSR29 0x04ba +#define SEC_CSR30 0x04bc +#define SEC_CSR31 0x04be + +#define KEY_ENTRY(__idx) \ + ( SEC_CSR0 + ((__idx) * 16) ) + +/* + * PHY control registers. + */ + +/* + * PHY_CSR0: RF switching timing control. + */ +#define PHY_CSR0 0x04c0 + +/* + * PHY_CSR1: TX PA configuration. + */ +#define PHY_CSR1 0x04c2 + +/* + * MAC configuration registers. + */ + +/* + * PHY_CSR2: TX MAC configuration. + * NOTE: Both register fields are complete dummy, + * documentation and legacy drivers are unclear un + * what this register means or what fields exists. + */ +#define PHY_CSR2 0x04c4 +#define PHY_CSR2_LNA FIELD16(0x0002) +#define PHY_CSR2_LNA_MODE FIELD16(0x3000) + +/* + * PHY_CSR3: RX MAC configuration. + */ +#define PHY_CSR3 0x04c6 + +/* + * PHY_CSR4: Interface configuration. + */ +#define PHY_CSR4 0x04c8 +#define PHY_CSR4_LOW_RF_LE FIELD16(0x0001) + +/* + * BBP pre-TX registers. + * PHY_CSR5: BBP pre-TX CCK. + */ +#define PHY_CSR5 0x04ca +#define PHY_CSR5_CCK FIELD16(0x0003) +#define PHY_CSR5_CCK_FLIP FIELD16(0x0004) + +/* + * BBP pre-TX registers. + * PHY_CSR6: BBP pre-TX OFDM. + */ +#define PHY_CSR6 0x04cc +#define PHY_CSR6_OFDM FIELD16(0x0003) +#define PHY_CSR6_OFDM_FLIP FIELD16(0x0004) + +/* + * PHY_CSR7: BBP access register 0. + * BBP_DATA: BBP data. + * BBP_REG_ID: BBP register ID. + * BBP_READ_CONTROL: 0: write, 1: read. + */ +#define PHY_CSR7 0x04ce +#define PHY_CSR7_DATA FIELD16(0x00ff) +#define PHY_CSR7_REG_ID FIELD16(0x7f00) +#define PHY_CSR7_READ_CONTROL FIELD16(0x8000) + +/* + * PHY_CSR8: BBP access register 1. + * BBP_BUSY: ASIC is busy execute BBP programming. + */ +#define PHY_CSR8 0x04d0 +#define PHY_CSR8_BUSY FIELD16(0x0001) + +/* + * PHY_CSR9: RF access register. + * RF_VALUE: Register value + id to program into rf/if. + */ +#define PHY_CSR9 0x04d2 +#define PHY_CSR9_RF_VALUE FIELD16(0xffff) + +/* + * PHY_CSR10: RF access register. + * RF_VALUE: Register value + id to program into rf/if. + * RF_NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). + * RF_IF_SELECT: Chip to program: 0: rf, 1: if. + * RF_PLL_LD: Rf pll_ld status. + * RF_BUSY: 1: asic is busy execute rf programming. + */ +#define PHY_CSR10 0x04d4 +#define PHY_CSR10_RF_VALUE FIELD16(0x00ff) +#define PHY_CSR10_RF_NUMBER_OF_BITS FIELD16(0x1f00) +#define PHY_CSR10_RF_IF_SELECT FIELD16(0x2000) +#define PHY_CSR10_RF_PLL_LD FIELD16(0x4000) +#define PHY_CSR10_RF_BUSY FIELD16(0x8000) + +/* + * STA_CSR0: FCS error count. + * FCS_ERROR: FCS error count, cleared when read. + */ +#define STA_CSR0 0x04e0 +#define STA_CSR0_FCS_ERROR FIELD16(0xffff) + +/* + * STA_CSR1: PLCP error count. + */ +#define STA_CSR1 0x04e2 + +/* + * STA_CSR2: LONG error count. + */ +#define STA_CSR2 0x04e4 + +/* + * STA_CSR3: CCA false alarm. + * FALSE_CCA_ERROR: False CCA error count, cleared when read. + */ +#define STA_CSR3 0x04e6 +#define STA_CSR3_FALSE_CCA_ERROR FIELD16(0xffff) + +/* + * STA_CSR4: RX FIFO overflow. + */ +#define STA_CSR4 0x04e8 + +/* + * STA_CSR5: Beacon sent counter. + */ +#define STA_CSR5 0x04ea + +/* + * Statistics registers + */ +#define STA_CSR6 0x04ec +#define STA_CSR7 0x04ee +#define STA_CSR8 0x04f0 +#define STA_CSR9 0x04f2 +#define STA_CSR10 0x04f4 + +/* + * BBP registers. + * The wordsize of the BBP is 8 bits. + */ + +/* + * R2: TX antenna control + */ +#define BBP_R2_TX_ANTENNA FIELD8(0x03) +#define BBP_R2_TX_IQ_FLIP FIELD8(0x04) + +/* + * R14: RX antenna control + */ +#define BBP_R14_RX_ANTENNA FIELD8(0x03) +#define BBP_R14_RX_IQ_FLIP FIELD8(0x04) + +/* + * RF registers. + */ + +/* + * RF 1 + */ +#define RF1_TUNER FIELD32(0x00020000) + +/* + * RF 3 + */ +#define RF3_TUNER FIELD32(0x00000100) +#define RF3_TXPOWER FIELD32(0x00003e00) + +/* + * EEPROM contents. + */ + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_0 0x0002 +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR1 0x0003 +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_2 0x0004 +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM antenna. + * ANTENNA_NUM: Number of antenna's. + * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * LED_MODE: 0: default, 1: TX/RX activity, 2: Single (ignore link), 3: rsvd. + * DYN_TXAGC: Dynamic TX AGC control. + * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. + * RF_TYPE: Rf_type of this adapter. + */ +#define EEPROM_ANTENNA 0x000b +#define EEPROM_ANTENNA_NUM FIELD16(0x0003) +#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) +#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) +#define EEPROM_ANTENNA_LED_MODE FIELD16(0x01c0) +#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) +#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) +#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) + +/* + * EEPROM NIC config. + * CARDBUS_ACCEL: 0: enable, 1: disable. + * DYN_BBP_TUNE: 0: enable, 1: disable. + * CCK_TX_POWER: CCK TX power compensation. + */ +#define EEPROM_NIC 0x000c +#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0001) +#define EEPROM_NIC_DYN_BBP_TUNE FIELD16(0x0002) +#define EEPROM_NIC_CCK_TX_POWER FIELD16(0x000c) + +/* + * EEPROM geography. + * GEO: Default geography setting for device. + */ +#define EEPROM_GEOGRAPHY 0x000d +#define EEPROM_GEOGRAPHY_GEO FIELD16(0x0f00) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_START 0x000e +#define EEPROM_BBP_SIZE 16 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* + * EEPROM TXPOWER + */ +#define EEPROM_TXPOWER_START 0x001e +#define EEPROM_TXPOWER_SIZE 7 +#define EEPROM_TXPOWER_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_2 FIELD16(0xff00) + +/* + * EEPROM Tuning threshold + */ +#define EEPROM_BBPTUNE 0x0030 +#define EEPROM_BBPTUNE_THRESHOLD FIELD16(0x00ff) + +/* + * EEPROM BBP R24 Tuning. + */ +#define EEPROM_BBPTUNE_R24 0x0031 +#define EEPROM_BBPTUNE_R24_LOW FIELD16(0x00ff) +#define EEPROM_BBPTUNE_R24_HIGH FIELD16(0xff00) + +/* + * EEPROM BBP R25 Tuning. + */ +#define EEPROM_BBPTUNE_R25 0x0032 +#define EEPROM_BBPTUNE_R25_LOW FIELD16(0x00ff) +#define EEPROM_BBPTUNE_R25_HIGH FIELD16(0xff00) + +/* + * EEPROM BBP R24 Tuning. + */ +#define EEPROM_BBPTUNE_R61 0x0033 +#define EEPROM_BBPTUNE_R61_LOW FIELD16(0x00ff) +#define EEPROM_BBPTUNE_R61_HIGH FIELD16(0xff00) + +/* + * EEPROM BBP VGC Tuning. + */ +#define EEPROM_BBPTUNE_VGC 0x0034 +#define EEPROM_BBPTUNE_VGCUPPER FIELD16(0x00ff) +#define EEPROM_BBPTUNE_VGCLOWER FIELD16(0xff00) + +/* + * EEPROM BBP R17 Tuning. + */ +#define EEPROM_BBPTUNE_R17 0x0035 +#define EEPROM_BBPTUNE_R17_LOW FIELD16(0x00ff) +#define EEPROM_BBPTUNE_R17_HIGH FIELD16(0xff00) + +/* + * RSSI <-> dBm offset calibration + */ +#define EEPROM_CALIBRATE_OFFSET 0x0036 +#define EEPROM_CALIBRATE_OFFSET_RSSI FIELD16(0x00ff) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE ( 5 * sizeof(__le32) ) +#define RXD_DESC_SIZE ( 4 * sizeof(__le32) ) + +/* + * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. + */ + +/* + * Word0 + */ +#define TXD_W0_PACKET_ID FIELD32(0x0000000f) +#define TXD_W0_RETRY_LIMIT FIELD32(0x000000f0) +#define TXD_W0_MORE_FRAG FIELD32(0x00000100) +#define TXD_W0_ACK FIELD32(0x00000200) +#define TXD_W0_TIMESTAMP FIELD32(0x00000400) +#define TXD_W0_OFDM FIELD32(0x00000800) +#define TXD_W0_NEW_SEQ FIELD32(0x00001000) +#define TXD_W0_IFS FIELD32(0x00006000) +#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define TXD_W0_CIPHER FIELD32(0x20000000) +#define TXD_W0_KEY_ID FIELD32(0xc0000000) + +/* + * Word1 + */ +#define TXD_W1_IV_OFFSET FIELD32(0x0000003f) +#define TXD_W1_AIFS FIELD32(0x000000c0) +#define TXD_W1_CWMIN FIELD32(0x00000f00) +#define TXD_W1_CWMAX FIELD32(0x0000f000) + +/* + * Word2: PLCP information + */ +#define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) +#define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) +#define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) +#define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) + +/* + * Word3 + */ +#define TXD_W3_IV FIELD32(0xffffffff) + +/* + * Word4 + */ +#define TXD_W4_EIV FIELD32(0xffffffff) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + */ +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) +#define RXD_W0_MULTICAST FIELD32(0x00000004) +#define RXD_W0_BROADCAST FIELD32(0x00000008) +#define RXD_W0_MY_BSS FIELD32(0x00000010) +#define RXD_W0_CRC_ERROR FIELD32(0x00000020) +#define RXD_W0_OFDM FIELD32(0x00000040) +#define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) +#define RXD_W0_CIPHER FIELD32(0x00000100) +#define RXD_W0_CIPHER_ERROR FIELD32(0x00000200) +#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) + +/* + * Word1 + */ +#define RXD_W1_RSSI FIELD32(0x000000ff) +#define RXD_W1_SIGNAL FIELD32(0x0000ff00) + +/* + * Word2 + */ +#define RXD_W2_IV FIELD32(0xffffffff) + +/* + * Word3 + */ +#define RXD_W3_EIV FIELD32(0xffffffff) + +/* + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. + */ +#define MIN_TXPOWER 0 +#define MAX_TXPOWER 31 +#define DEFAULT_TXPOWER 24 + +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) + +#endif /* RT2500USB_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h new file mode 100644 index 000000000000..95c1d7c0a2f3 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h @@ -0,0 +1,2986 @@ +/* + Copyright (C) 2004 - 2010 Ivo van Doorn + Copyright (C) 2010 Willow Garage + Copyright (C) 2009 Alban Browaeys + Copyright (C) 2009 Felix Fietkau + Copyright (C) 2009 Luis Correia + Copyright (C) 2009 Mattias Nissler + Copyright (C) 2009 Mark Asselstine + Copyright (C) 2009 Xose Vazquez Perez + Copyright (C) 2009 Bart Zolnierkiewicz + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2800 + Abstract: Data structures and registers for the rt2800 modules. + Supported chipsets: RT2800E, RT2800ED & RT2800U. + */ + +#ifndef RT2800_H +#define RT2800_H + +/* + * RF chip defines. + * + * RF2820 2.4G 2T3R + * RF2850 2.4G/5G 2T3R + * RF2720 2.4G 1T2R + * RF2750 2.4G/5G 1T2R + * RF3020 2.4G 1T1R + * RF2020 2.4G B/G + * RF3021 2.4G 1T2R + * RF3022 2.4G 2T2R + * RF3052 2.4G/5G 2T2R + * RF2853 2.4G/5G 3T3R + * RF3320 2.4G 1T1R(RT3350/RT3370/RT3390) + * RF3322 2.4G 2T2R(RT3352/RT3371/RT3372/RT3391/RT3392) + * RF3053 2.4G/5G 3T3R(RT3883/RT3563/RT3573/RT3593/RT3662) + * RF5592 2.4G/5G 2T2R + * RF3070 2.4G 1T1R + * RF5360 2.4G 1T1R + * RF5362 2.4G 1T1R + * RF5370 2.4G 1T1R + * RF5390 2.4G 1T1R + */ +#define RF2820 0x0001 +#define RF2850 0x0002 +#define RF2720 0x0003 +#define RF2750 0x0004 +#define RF3020 0x0005 +#define RF2020 0x0006 +#define RF3021 0x0007 +#define RF3022 0x0008 +#define RF3052 0x0009 +#define RF2853 0x000a +#define RF3320 0x000b +#define RF3322 0x000c +#define RF3053 0x000d +#define RF5592 0x000f +#define RF3070 0x3070 +#define RF3290 0x3290 +#define RF5360 0x5360 +#define RF5362 0x5362 +#define RF5370 0x5370 +#define RF5372 0x5372 +#define RF5390 0x5390 +#define RF5392 0x5392 + +/* + * Chipset revisions. + */ +#define REV_RT2860C 0x0100 +#define REV_RT2860D 0x0101 +#define REV_RT2872E 0x0200 +#define REV_RT3070E 0x0200 +#define REV_RT3070F 0x0201 +#define REV_RT3071E 0x0211 +#define REV_RT3090E 0x0211 +#define REV_RT3390E 0x0211 +#define REV_RT3593E 0x0211 +#define REV_RT5390F 0x0502 +#define REV_RT5390R 0x1502 +#define REV_RT5592C 0x0221 + +#define DEFAULT_RSSI_OFFSET 120 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x1000 +#define CSR_REG_SIZE 0x0800 +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x0200 +#define BBP_BASE 0x0000 +#define BBP_SIZE 0x00ff +#define RF_BASE 0x0004 +#define RF_SIZE 0x0010 +#define RFCSR_BASE 0x0000 +#define RFCSR_SIZE 0x0040 + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 4 + +/* + * Registers. + */ + + +/* + * MAC_CSR0_3290: MAC_CSR0 for RT3290 to identity MAC version number. + */ +#define MAC_CSR0_3290 0x0000 + +/* + * E2PROM_CSR: PCI EEPROM control register. + * RELOAD: Write 1 to reload eeprom content. + * TYPE: 0: 93c46, 1:93c66. + * LOAD_STATUS: 1:loading, 0:done. + */ +#define E2PROM_CSR 0x0004 +#define E2PROM_CSR_DATA_CLOCK FIELD32(0x00000001) +#define E2PROM_CSR_CHIP_SELECT FIELD32(0x00000002) +#define E2PROM_CSR_DATA_IN FIELD32(0x00000004) +#define E2PROM_CSR_DATA_OUT FIELD32(0x00000008) +#define E2PROM_CSR_TYPE FIELD32(0x00000030) +#define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040) +#define E2PROM_CSR_RELOAD FIELD32(0x00000080) + +/* + * CMB_CTRL_CFG + */ +#define CMB_CTRL 0x0020 +#define AUX_OPT_BIT0 FIELD32(0x00000001) +#define AUX_OPT_BIT1 FIELD32(0x00000002) +#define AUX_OPT_BIT2 FIELD32(0x00000004) +#define AUX_OPT_BIT3 FIELD32(0x00000008) +#define AUX_OPT_BIT4 FIELD32(0x00000010) +#define AUX_OPT_BIT5 FIELD32(0x00000020) +#define AUX_OPT_BIT6 FIELD32(0x00000040) +#define AUX_OPT_BIT7 FIELD32(0x00000080) +#define AUX_OPT_BIT8 FIELD32(0x00000100) +#define AUX_OPT_BIT9 FIELD32(0x00000200) +#define AUX_OPT_BIT10 FIELD32(0x00000400) +#define AUX_OPT_BIT11 FIELD32(0x00000800) +#define AUX_OPT_BIT12 FIELD32(0x00001000) +#define AUX_OPT_BIT13 FIELD32(0x00002000) +#define AUX_OPT_BIT14 FIELD32(0x00004000) +#define AUX_OPT_BIT15 FIELD32(0x00008000) +#define LDO25_LEVEL FIELD32(0x00030000) +#define LDO25_LARGEA FIELD32(0x00040000) +#define LDO25_FRC_ON FIELD32(0x00080000) +#define CMB_RSV FIELD32(0x00300000) +#define XTAL_RDY FIELD32(0x00400000) +#define PLL_LD FIELD32(0x00800000) +#define LDO_CORE_LEVEL FIELD32(0x0F000000) +#define LDO_BGSEL FIELD32(0x30000000) +#define LDO3_EN FIELD32(0x40000000) +#define LDO0_EN FIELD32(0x80000000) + +/* + * EFUSE_CSR_3290: RT3290 EEPROM + */ +#define EFUSE_CTRL_3290 0x0024 + +/* + * EFUSE_DATA3 of 3290 + */ +#define EFUSE_DATA3_3290 0x0028 + +/* + * EFUSE_DATA2 of 3290 + */ +#define EFUSE_DATA2_3290 0x002c + +/* + * EFUSE_DATA1 of 3290 + */ +#define EFUSE_DATA1_3290 0x0030 + +/* + * EFUSE_DATA0 of 3290 + */ +#define EFUSE_DATA0_3290 0x0034 + +/* + * OSC_CTRL_CFG + * Ring oscillator configuration + */ +#define OSC_CTRL 0x0038 +#define OSC_REF_CYCLE FIELD32(0x00001fff) +#define OSC_RSV FIELD32(0x0000e000) +#define OSC_CAL_CNT FIELD32(0x0fff0000) +#define OSC_CAL_ACK FIELD32(0x10000000) +#define OSC_CLK_32K_VLD FIELD32(0x20000000) +#define OSC_CAL_REQ FIELD32(0x40000000) +#define OSC_ROSC_EN FIELD32(0x80000000) + +/* + * COEX_CFG_0 + */ +#define COEX_CFG0 0x0040 +#define COEX_CFG_ANT FIELD32(0xff000000) +/* + * COEX_CFG_1 + */ +#define COEX_CFG1 0x0044 + +/* + * COEX_CFG_2 + */ +#define COEX_CFG2 0x0048 +#define BT_COEX_CFG1 FIELD32(0xff000000) +#define BT_COEX_CFG0 FIELD32(0x00ff0000) +#define WL_COEX_CFG1 FIELD32(0x0000ff00) +#define WL_COEX_CFG0 FIELD32(0x000000ff) +/* + * PLL_CTRL_CFG + * PLL configuration register + */ +#define PLL_CTRL 0x0050 +#define PLL_RESERVED_INPUT1 FIELD32(0x000000ff) +#define PLL_RESERVED_INPUT2 FIELD32(0x0000ff00) +#define PLL_CONTROL FIELD32(0x00070000) +#define PLL_LPF_R1 FIELD32(0x00080000) +#define PLL_LPF_C1_CTRL FIELD32(0x00300000) +#define PLL_LPF_C2_CTRL FIELD32(0x00c00000) +#define PLL_CP_CURRENT_CTRL FIELD32(0x03000000) +#define PLL_PFD_DELAY_CTRL FIELD32(0x0c000000) +#define PLL_LOCK_CTRL FIELD32(0x70000000) +#define PLL_VBGBK_EN FIELD32(0x80000000) + + +/* + * WLAN_CTRL_CFG + * RT3290 wlan configuration + */ +#define WLAN_FUN_CTRL 0x0080 +#define WLAN_EN FIELD32(0x00000001) +#define WLAN_CLK_EN FIELD32(0x00000002) +#define WLAN_RSV1 FIELD32(0x00000004) +#define WLAN_RESET FIELD32(0x00000008) +#define PCIE_APP0_CLK_REQ FIELD32(0x00000010) +#define FRC_WL_ANT_SET FIELD32(0x00000020) +#define INV_TR_SW0 FIELD32(0x00000040) +#define WLAN_GPIO_IN_BIT0 FIELD32(0x00000100) +#define WLAN_GPIO_IN_BIT1 FIELD32(0x00000200) +#define WLAN_GPIO_IN_BIT2 FIELD32(0x00000400) +#define WLAN_GPIO_IN_BIT3 FIELD32(0x00000800) +#define WLAN_GPIO_IN_BIT4 FIELD32(0x00001000) +#define WLAN_GPIO_IN_BIT5 FIELD32(0x00002000) +#define WLAN_GPIO_IN_BIT6 FIELD32(0x00004000) +#define WLAN_GPIO_IN_BIT7 FIELD32(0x00008000) +#define WLAN_GPIO_IN_BIT_ALL FIELD32(0x0000ff00) +#define WLAN_GPIO_OUT_BIT0 FIELD32(0x00010000) +#define WLAN_GPIO_OUT_BIT1 FIELD32(0x00020000) +#define WLAN_GPIO_OUT_BIT2 FIELD32(0x00040000) +#define WLAN_GPIO_OUT_BIT3 FIELD32(0x00050000) +#define WLAN_GPIO_OUT_BIT4 FIELD32(0x00100000) +#define WLAN_GPIO_OUT_BIT5 FIELD32(0x00200000) +#define WLAN_GPIO_OUT_BIT6 FIELD32(0x00400000) +#define WLAN_GPIO_OUT_BIT7 FIELD32(0x00800000) +#define WLAN_GPIO_OUT_BIT_ALL FIELD32(0x00ff0000) +#define WLAN_GPIO_OUT_OE_BIT0 FIELD32(0x01000000) +#define WLAN_GPIO_OUT_OE_BIT1 FIELD32(0x02000000) +#define WLAN_GPIO_OUT_OE_BIT2 FIELD32(0x04000000) +#define WLAN_GPIO_OUT_OE_BIT3 FIELD32(0x08000000) +#define WLAN_GPIO_OUT_OE_BIT4 FIELD32(0x10000000) +#define WLAN_GPIO_OUT_OE_BIT5 FIELD32(0x20000000) +#define WLAN_GPIO_OUT_OE_BIT6 FIELD32(0x40000000) +#define WLAN_GPIO_OUT_OE_BIT7 FIELD32(0x80000000) +#define WLAN_GPIO_OUT_OE_BIT_ALL FIELD32(0xff000000) + +/* + * AUX_CTRL: Aux/PCI-E related configuration + */ +#define AUX_CTRL 0x10c +#define AUX_CTRL_WAKE_PCIE_EN FIELD32(0x00000002) +#define AUX_CTRL_FORCE_PCIE_CLK FIELD32(0x00000400) + +/* + * OPT_14: Unknown register used by rt3xxx devices. + */ +#define OPT_14_CSR 0x0114 +#define OPT_14_CSR_BIT0 FIELD32(0x00000001) + +/* + * INT_SOURCE_CSR: Interrupt source register. + * Write one to clear corresponding bit. + * TX_FIFO_STATUS: FIFO Statistics is full, sw should read TX_STA_FIFO + */ +#define INT_SOURCE_CSR 0x0200 +#define INT_SOURCE_CSR_RXDELAYINT FIELD32(0x00000001) +#define INT_SOURCE_CSR_TXDELAYINT FIELD32(0x00000002) +#define INT_SOURCE_CSR_RX_DONE FIELD32(0x00000004) +#define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00000008) +#define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00000010) +#define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00000020) +#define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00000040) +#define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00000080) +#define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00000100) +#define INT_SOURCE_CSR_MCU_COMMAND FIELD32(0x00000200) +#define INT_SOURCE_CSR_RXTX_COHERENT FIELD32(0x00000400) +#define INT_SOURCE_CSR_TBTT FIELD32(0x00000800) +#define INT_SOURCE_CSR_PRE_TBTT FIELD32(0x00001000) +#define INT_SOURCE_CSR_TX_FIFO_STATUS FIELD32(0x00002000) +#define INT_SOURCE_CSR_AUTO_WAKEUP FIELD32(0x00004000) +#define INT_SOURCE_CSR_GPTIMER FIELD32(0x00008000) +#define INT_SOURCE_CSR_RX_COHERENT FIELD32(0x00010000) +#define INT_SOURCE_CSR_TX_COHERENT FIELD32(0x00020000) + +/* + * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF. + */ +#define INT_MASK_CSR 0x0204 +#define INT_MASK_CSR_RXDELAYINT FIELD32(0x00000001) +#define INT_MASK_CSR_TXDELAYINT FIELD32(0x00000002) +#define INT_MASK_CSR_RX_DONE FIELD32(0x00000004) +#define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00000008) +#define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00000010) +#define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00000020) +#define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00000040) +#define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00000080) +#define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00000100) +#define INT_MASK_CSR_MCU_COMMAND FIELD32(0x00000200) +#define INT_MASK_CSR_RXTX_COHERENT FIELD32(0x00000400) +#define INT_MASK_CSR_TBTT FIELD32(0x00000800) +#define INT_MASK_CSR_PRE_TBTT FIELD32(0x00001000) +#define INT_MASK_CSR_TX_FIFO_STATUS FIELD32(0x00002000) +#define INT_MASK_CSR_AUTO_WAKEUP FIELD32(0x00004000) +#define INT_MASK_CSR_GPTIMER FIELD32(0x00008000) +#define INT_MASK_CSR_RX_COHERENT FIELD32(0x00010000) +#define INT_MASK_CSR_TX_COHERENT FIELD32(0x00020000) + +/* + * WPDMA_GLO_CFG + */ +#define WPDMA_GLO_CFG 0x0208 +#define WPDMA_GLO_CFG_ENABLE_TX_DMA FIELD32(0x00000001) +#define WPDMA_GLO_CFG_TX_DMA_BUSY FIELD32(0x00000002) +#define WPDMA_GLO_CFG_ENABLE_RX_DMA FIELD32(0x00000004) +#define WPDMA_GLO_CFG_RX_DMA_BUSY FIELD32(0x00000008) +#define WPDMA_GLO_CFG_WP_DMA_BURST_SIZE FIELD32(0x00000030) +#define WPDMA_GLO_CFG_TX_WRITEBACK_DONE FIELD32(0x00000040) +#define WPDMA_GLO_CFG_BIG_ENDIAN FIELD32(0x00000080) +#define WPDMA_GLO_CFG_RX_HDR_SCATTER FIELD32(0x0000ff00) +#define WPDMA_GLO_CFG_HDR_SEG_LEN FIELD32(0xffff0000) + +/* + * WPDMA_RST_IDX + */ +#define WPDMA_RST_IDX 0x020c +#define WPDMA_RST_IDX_DTX_IDX0 FIELD32(0x00000001) +#define WPDMA_RST_IDX_DTX_IDX1 FIELD32(0x00000002) +#define WPDMA_RST_IDX_DTX_IDX2 FIELD32(0x00000004) +#define WPDMA_RST_IDX_DTX_IDX3 FIELD32(0x00000008) +#define WPDMA_RST_IDX_DTX_IDX4 FIELD32(0x00000010) +#define WPDMA_RST_IDX_DTX_IDX5 FIELD32(0x00000020) +#define WPDMA_RST_IDX_DRX_IDX0 FIELD32(0x00010000) + +/* + * DELAY_INT_CFG + */ +#define DELAY_INT_CFG 0x0210 +#define DELAY_INT_CFG_RXMAX_PTIME FIELD32(0x000000ff) +#define DELAY_INT_CFG_RXMAX_PINT FIELD32(0x00007f00) +#define DELAY_INT_CFG_RXDLY_INT_EN FIELD32(0x00008000) +#define DELAY_INT_CFG_TXMAX_PTIME FIELD32(0x00ff0000) +#define DELAY_INT_CFG_TXMAX_PINT FIELD32(0x7f000000) +#define DELAY_INT_CFG_TXDLY_INT_EN FIELD32(0x80000000) + +/* + * WMM_AIFSN_CFG: Aifsn for each EDCA AC + * AIFSN0: AC_VO + * AIFSN1: AC_VI + * AIFSN2: AC_BE + * AIFSN3: AC_BK + */ +#define WMM_AIFSN_CFG 0x0214 +#define WMM_AIFSN_CFG_AIFSN0 FIELD32(0x0000000f) +#define WMM_AIFSN_CFG_AIFSN1 FIELD32(0x000000f0) +#define WMM_AIFSN_CFG_AIFSN2 FIELD32(0x00000f00) +#define WMM_AIFSN_CFG_AIFSN3 FIELD32(0x0000f000) + +/* + * WMM_CWMIN_CSR: CWmin for each EDCA AC + * CWMIN0: AC_VO + * CWMIN1: AC_VI + * CWMIN2: AC_BE + * CWMIN3: AC_BK + */ +#define WMM_CWMIN_CFG 0x0218 +#define WMM_CWMIN_CFG_CWMIN0 FIELD32(0x0000000f) +#define WMM_CWMIN_CFG_CWMIN1 FIELD32(0x000000f0) +#define WMM_CWMIN_CFG_CWMIN2 FIELD32(0x00000f00) +#define WMM_CWMIN_CFG_CWMIN3 FIELD32(0x0000f000) + +/* + * WMM_CWMAX_CSR: CWmax for each EDCA AC + * CWMAX0: AC_VO + * CWMAX1: AC_VI + * CWMAX2: AC_BE + * CWMAX3: AC_BK + */ +#define WMM_CWMAX_CFG 0x021c +#define WMM_CWMAX_CFG_CWMAX0 FIELD32(0x0000000f) +#define WMM_CWMAX_CFG_CWMAX1 FIELD32(0x000000f0) +#define WMM_CWMAX_CFG_CWMAX2 FIELD32(0x00000f00) +#define WMM_CWMAX_CFG_CWMAX3 FIELD32(0x0000f000) + +/* + * AC_TXOP0: AC_VO/AC_VI TXOP register + * AC0TXOP: AC_VO in unit of 32us + * AC1TXOP: AC_VI in unit of 32us + */ +#define WMM_TXOP0_CFG 0x0220 +#define WMM_TXOP0_CFG_AC0TXOP FIELD32(0x0000ffff) +#define WMM_TXOP0_CFG_AC1TXOP FIELD32(0xffff0000) + +/* + * AC_TXOP1: AC_BE/AC_BK TXOP register + * AC2TXOP: AC_BE in unit of 32us + * AC3TXOP: AC_BK in unit of 32us + */ +#define WMM_TXOP1_CFG 0x0224 +#define WMM_TXOP1_CFG_AC2TXOP FIELD32(0x0000ffff) +#define WMM_TXOP1_CFG_AC3TXOP FIELD32(0xffff0000) + +/* + * GPIO_CTRL: + * GPIO_CTRL_VALx: GPIO value + * GPIO_CTRL_DIRx: GPIO direction: 0 = output; 1 = input + */ +#define GPIO_CTRL 0x0228 +#define GPIO_CTRL_VAL0 FIELD32(0x00000001) +#define GPIO_CTRL_VAL1 FIELD32(0x00000002) +#define GPIO_CTRL_VAL2 FIELD32(0x00000004) +#define GPIO_CTRL_VAL3 FIELD32(0x00000008) +#define GPIO_CTRL_VAL4 FIELD32(0x00000010) +#define GPIO_CTRL_VAL5 FIELD32(0x00000020) +#define GPIO_CTRL_VAL6 FIELD32(0x00000040) +#define GPIO_CTRL_VAL7 FIELD32(0x00000080) +#define GPIO_CTRL_DIR0 FIELD32(0x00000100) +#define GPIO_CTRL_DIR1 FIELD32(0x00000200) +#define GPIO_CTRL_DIR2 FIELD32(0x00000400) +#define GPIO_CTRL_DIR3 FIELD32(0x00000800) +#define GPIO_CTRL_DIR4 FIELD32(0x00001000) +#define GPIO_CTRL_DIR5 FIELD32(0x00002000) +#define GPIO_CTRL_DIR6 FIELD32(0x00004000) +#define GPIO_CTRL_DIR7 FIELD32(0x00008000) +#define GPIO_CTRL_VAL8 FIELD32(0x00010000) +#define GPIO_CTRL_VAL9 FIELD32(0x00020000) +#define GPIO_CTRL_VAL10 FIELD32(0x00040000) +#define GPIO_CTRL_DIR8 FIELD32(0x01000000) +#define GPIO_CTRL_DIR9 FIELD32(0x02000000) +#define GPIO_CTRL_DIR10 FIELD32(0x04000000) + +/* + * MCU_CMD_CFG + */ +#define MCU_CMD_CFG 0x022c + +/* + * AC_VO register offsets + */ +#define TX_BASE_PTR0 0x0230 +#define TX_MAX_CNT0 0x0234 +#define TX_CTX_IDX0 0x0238 +#define TX_DTX_IDX0 0x023c + +/* + * AC_VI register offsets + */ +#define TX_BASE_PTR1 0x0240 +#define TX_MAX_CNT1 0x0244 +#define TX_CTX_IDX1 0x0248 +#define TX_DTX_IDX1 0x024c + +/* + * AC_BE register offsets + */ +#define TX_BASE_PTR2 0x0250 +#define TX_MAX_CNT2 0x0254 +#define TX_CTX_IDX2 0x0258 +#define TX_DTX_IDX2 0x025c + +/* + * AC_BK register offsets + */ +#define TX_BASE_PTR3 0x0260 +#define TX_MAX_CNT3 0x0264 +#define TX_CTX_IDX3 0x0268 +#define TX_DTX_IDX3 0x026c + +/* + * HCCA register offsets + */ +#define TX_BASE_PTR4 0x0270 +#define TX_MAX_CNT4 0x0274 +#define TX_CTX_IDX4 0x0278 +#define TX_DTX_IDX4 0x027c + +/* + * MGMT register offsets + */ +#define TX_BASE_PTR5 0x0280 +#define TX_MAX_CNT5 0x0284 +#define TX_CTX_IDX5 0x0288 +#define TX_DTX_IDX5 0x028c + +/* + * RX register offsets + */ +#define RX_BASE_PTR 0x0290 +#define RX_MAX_CNT 0x0294 +#define RX_CRX_IDX 0x0298 +#define RX_DRX_IDX 0x029c + +/* + * USB_DMA_CFG + * RX_BULK_AGG_TIMEOUT: Rx Bulk Aggregation TimeOut in unit of 33ns. + * RX_BULK_AGG_LIMIT: Rx Bulk Aggregation Limit in unit of 256 bytes. + * PHY_CLEAR: phy watch dog enable. + * TX_CLEAR: Clear USB DMA TX path. + * TXOP_HALT: Halt TXOP count down when TX buffer is full. + * RX_BULK_AGG_EN: Enable Rx Bulk Aggregation. + * RX_BULK_EN: Enable USB DMA Rx. + * TX_BULK_EN: Enable USB DMA Tx. + * EP_OUT_VALID: OUT endpoint data valid. + * RX_BUSY: USB DMA RX FSM busy. + * TX_BUSY: USB DMA TX FSM busy. + */ +#define USB_DMA_CFG 0x02a0 +#define USB_DMA_CFG_RX_BULK_AGG_TIMEOUT FIELD32(0x000000ff) +#define USB_DMA_CFG_RX_BULK_AGG_LIMIT FIELD32(0x0000ff00) +#define USB_DMA_CFG_PHY_CLEAR FIELD32(0x00010000) +#define USB_DMA_CFG_TX_CLEAR FIELD32(0x00080000) +#define USB_DMA_CFG_TXOP_HALT FIELD32(0x00100000) +#define USB_DMA_CFG_RX_BULK_AGG_EN FIELD32(0x00200000) +#define USB_DMA_CFG_RX_BULK_EN FIELD32(0x00400000) +#define USB_DMA_CFG_TX_BULK_EN FIELD32(0x00800000) +#define USB_DMA_CFG_EP_OUT_VALID FIELD32(0x3f000000) +#define USB_DMA_CFG_RX_BUSY FIELD32(0x40000000) +#define USB_DMA_CFG_TX_BUSY FIELD32(0x80000000) + +/* + * US_CYC_CNT + * BT_MODE_EN: Bluetooth mode enable + * CLOCK CYCLE: Clock cycle count in 1us. + * PCI:0x21, PCIE:0x7d, USB:0x1e + */ +#define US_CYC_CNT 0x02a4 +#define US_CYC_CNT_BT_MODE_EN FIELD32(0x00000100) +#define US_CYC_CNT_CLOCK_CYCLE FIELD32(0x000000ff) + +/* + * PBF_SYS_CTRL + * HOST_RAM_WRITE: enable Host program ram write selection + */ +#define PBF_SYS_CTRL 0x0400 +#define PBF_SYS_CTRL_READY FIELD32(0x00000080) +#define PBF_SYS_CTRL_HOST_RAM_WRITE FIELD32(0x00010000) + +/* + * HOST-MCU shared memory + */ +#define HOST_CMD_CSR 0x0404 +#define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x000000ff) + +/* + * PBF registers + * Most are for debug. Driver doesn't touch PBF register. + */ +#define PBF_CFG 0x0408 +#define PBF_MAX_PCNT 0x040c +#define PBF_CTRL 0x0410 +#define PBF_INT_STA 0x0414 +#define PBF_INT_ENA 0x0418 + +/* + * BCN_OFFSET0: + */ +#define BCN_OFFSET0 0x042c +#define BCN_OFFSET0_BCN0 FIELD32(0x000000ff) +#define BCN_OFFSET0_BCN1 FIELD32(0x0000ff00) +#define BCN_OFFSET0_BCN2 FIELD32(0x00ff0000) +#define BCN_OFFSET0_BCN3 FIELD32(0xff000000) + +/* + * BCN_OFFSET1: + */ +#define BCN_OFFSET1 0x0430 +#define BCN_OFFSET1_BCN4 FIELD32(0x000000ff) +#define BCN_OFFSET1_BCN5 FIELD32(0x0000ff00) +#define BCN_OFFSET1_BCN6 FIELD32(0x00ff0000) +#define BCN_OFFSET1_BCN7 FIELD32(0xff000000) + +/* + * TXRXQ_PCNT: PBF register + * PCNT_TX0Q: Page count for TX hardware queue 0 + * PCNT_TX1Q: Page count for TX hardware queue 1 + * PCNT_TX2Q: Page count for TX hardware queue 2 + * PCNT_RX0Q: Page count for RX hardware queue + */ +#define TXRXQ_PCNT 0x0438 +#define TXRXQ_PCNT_TX0Q FIELD32(0x000000ff) +#define TXRXQ_PCNT_TX1Q FIELD32(0x0000ff00) +#define TXRXQ_PCNT_TX2Q FIELD32(0x00ff0000) +#define TXRXQ_PCNT_RX0Q FIELD32(0xff000000) + +/* + * PBF register + * Debug. Driver doesn't touch PBF register. + */ +#define PBF_DBG 0x043c + +/* + * RF registers + */ +#define RF_CSR_CFG 0x0500 +#define RF_CSR_CFG_DATA FIELD32(0x000000ff) +#define RF_CSR_CFG_REGNUM FIELD32(0x00003f00) +#define RF_CSR_CFG_WRITE FIELD32(0x00010000) +#define RF_CSR_CFG_BUSY FIELD32(0x00020000) + +/* + * EFUSE_CSR: RT30x0 EEPROM + */ +#define EFUSE_CTRL 0x0580 +#define EFUSE_CTRL_ADDRESS_IN FIELD32(0x03fe0000) +#define EFUSE_CTRL_MODE FIELD32(0x000000c0) +#define EFUSE_CTRL_KICK FIELD32(0x40000000) +#define EFUSE_CTRL_PRESENT FIELD32(0x80000000) + +/* + * EFUSE_DATA0 + */ +#define EFUSE_DATA0 0x0590 + +/* + * EFUSE_DATA1 + */ +#define EFUSE_DATA1 0x0594 + +/* + * EFUSE_DATA2 + */ +#define EFUSE_DATA2 0x0598 + +/* + * EFUSE_DATA3 + */ +#define EFUSE_DATA3 0x059c + +/* + * LDO_CFG0 + */ +#define LDO_CFG0 0x05d4 +#define LDO_CFG0_DELAY3 FIELD32(0x000000ff) +#define LDO_CFG0_DELAY2 FIELD32(0x0000ff00) +#define LDO_CFG0_DELAY1 FIELD32(0x00ff0000) +#define LDO_CFG0_BGSEL FIELD32(0x03000000) +#define LDO_CFG0_LDO_CORE_VLEVEL FIELD32(0x1c000000) +#define LD0_CFG0_LDO25_LEVEL FIELD32(0x60000000) +#define LDO_CFG0_LDO25_LARGEA FIELD32(0x80000000) + +/* + * GPIO_SWITCH + */ +#define GPIO_SWITCH 0x05dc +#define GPIO_SWITCH_0 FIELD32(0x00000001) +#define GPIO_SWITCH_1 FIELD32(0x00000002) +#define GPIO_SWITCH_2 FIELD32(0x00000004) +#define GPIO_SWITCH_3 FIELD32(0x00000008) +#define GPIO_SWITCH_4 FIELD32(0x00000010) +#define GPIO_SWITCH_5 FIELD32(0x00000020) +#define GPIO_SWITCH_6 FIELD32(0x00000040) +#define GPIO_SWITCH_7 FIELD32(0x00000080) + +/* + * FIXME: where the DEBUG_INDEX name come from? + */ +#define MAC_DEBUG_INDEX 0x05e8 +#define MAC_DEBUG_INDEX_XTAL FIELD32(0x80000000) + +/* + * MAC Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * MAC_CSR0: ASIC revision number. + * ASIC_REV: 0 + * ASIC_VER: 2860 or 2870 + */ +#define MAC_CSR0 0x1000 +#define MAC_CSR0_REVISION FIELD32(0x0000ffff) +#define MAC_CSR0_CHIPSET FIELD32(0xffff0000) + +/* + * MAC_SYS_CTRL: + */ +#define MAC_SYS_CTRL 0x1004 +#define MAC_SYS_CTRL_RESET_CSR FIELD32(0x00000001) +#define MAC_SYS_CTRL_RESET_BBP FIELD32(0x00000002) +#define MAC_SYS_CTRL_ENABLE_TX FIELD32(0x00000004) +#define MAC_SYS_CTRL_ENABLE_RX FIELD32(0x00000008) +#define MAC_SYS_CTRL_CONTINUOUS_TX FIELD32(0x00000010) +#define MAC_SYS_CTRL_LOOPBACK FIELD32(0x00000020) +#define MAC_SYS_CTRL_WLAN_HALT FIELD32(0x00000040) +#define MAC_SYS_CTRL_RX_TIMESTAMP FIELD32(0x00000080) + +/* + * MAC_ADDR_DW0: STA MAC register 0 + */ +#define MAC_ADDR_DW0 0x1008 +#define MAC_ADDR_DW0_BYTE0 FIELD32(0x000000ff) +#define MAC_ADDR_DW0_BYTE1 FIELD32(0x0000ff00) +#define MAC_ADDR_DW0_BYTE2 FIELD32(0x00ff0000) +#define MAC_ADDR_DW0_BYTE3 FIELD32(0xff000000) + +/* + * MAC_ADDR_DW1: STA MAC register 1 + * UNICAST_TO_ME_MASK: + * Used to mask off bits from byte 5 of the MAC address + * to determine the UNICAST_TO_ME bit for RX frames. + * The full mask is complemented by BSS_ID_MASK: + * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK + */ +#define MAC_ADDR_DW1 0x100c +#define MAC_ADDR_DW1_BYTE4 FIELD32(0x000000ff) +#define MAC_ADDR_DW1_BYTE5 FIELD32(0x0000ff00) +#define MAC_ADDR_DW1_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) + +/* + * MAC_BSSID_DW0: BSSID register 0 + */ +#define MAC_BSSID_DW0 0x1010 +#define MAC_BSSID_DW0_BYTE0 FIELD32(0x000000ff) +#define MAC_BSSID_DW0_BYTE1 FIELD32(0x0000ff00) +#define MAC_BSSID_DW0_BYTE2 FIELD32(0x00ff0000) +#define MAC_BSSID_DW0_BYTE3 FIELD32(0xff000000) + +/* + * MAC_BSSID_DW1: BSSID register 1 + * BSS_ID_MASK: + * 0: 1-BSSID mode (BSS index = 0) + * 1: 2-BSSID mode (BSS index: Byte5, bit 0) + * 2: 4-BSSID mode (BSS index: byte5, bit 0 - 1) + * 3: 8-BSSID mode (BSS index: byte5, bit 0 - 2) + * This mask is used to mask off bits 0, 1 and 2 of byte 5 of the + * BSSID. This will make sure that those bits will be ignored + * when determining the MY_BSS of RX frames. + */ +#define MAC_BSSID_DW1 0x1014 +#define MAC_BSSID_DW1_BYTE4 FIELD32(0x000000ff) +#define MAC_BSSID_DW1_BYTE5 FIELD32(0x0000ff00) +#define MAC_BSSID_DW1_BSS_ID_MASK FIELD32(0x00030000) +#define MAC_BSSID_DW1_BSS_BCN_NUM FIELD32(0x001c0000) + +/* + * MAX_LEN_CFG: Maximum frame length register. + * MAX_MPDU: rt2860b max 16k bytes + * MAX_PSDU: Maximum PSDU length + * (power factor) 0:2^13, 1:2^14, 2:2^15, 3:2^16 + */ +#define MAX_LEN_CFG 0x1018 +#define MAX_LEN_CFG_MAX_MPDU FIELD32(0x00000fff) +#define MAX_LEN_CFG_MAX_PSDU FIELD32(0x00003000) +#define MAX_LEN_CFG_MIN_PSDU FIELD32(0x0000c000) +#define MAX_LEN_CFG_MIN_MPDU FIELD32(0x000f0000) + +/* + * BBP_CSR_CFG: BBP serial control register + * VALUE: Register value to program into BBP + * REG_NUM: Selected BBP register + * READ_CONTROL: 0 write BBP, 1 read BBP + * BUSY: ASIC is busy executing BBP commands + * BBP_PAR_DUR: 0 4 MAC clocks, 1 8 MAC clocks + * BBP_RW_MODE: 0 serial, 1 parallel + */ +#define BBP_CSR_CFG 0x101c +#define BBP_CSR_CFG_VALUE FIELD32(0x000000ff) +#define BBP_CSR_CFG_REGNUM FIELD32(0x0000ff00) +#define BBP_CSR_CFG_READ_CONTROL FIELD32(0x00010000) +#define BBP_CSR_CFG_BUSY FIELD32(0x00020000) +#define BBP_CSR_CFG_BBP_PAR_DUR FIELD32(0x00040000) +#define BBP_CSR_CFG_BBP_RW_MODE FIELD32(0x00080000) + +/* + * RF_CSR_CFG0: RF control register + * REGID_AND_VALUE: Register value to program into RF + * BITWIDTH: Selected RF register + * STANDBYMODE: 0 high when standby, 1 low when standby + * SEL: 0 RF_LE0 activate, 1 RF_LE1 activate + * BUSY: ASIC is busy executing RF commands + */ +#define RF_CSR_CFG0 0x1020 +#define RF_CSR_CFG0_REGID_AND_VALUE FIELD32(0x00ffffff) +#define RF_CSR_CFG0_BITWIDTH FIELD32(0x1f000000) +#define RF_CSR_CFG0_REG_VALUE_BW FIELD32(0x1fffffff) +#define RF_CSR_CFG0_STANDBYMODE FIELD32(0x20000000) +#define RF_CSR_CFG0_SEL FIELD32(0x40000000) +#define RF_CSR_CFG0_BUSY FIELD32(0x80000000) + +/* + * RF_CSR_CFG1: RF control register + * REGID_AND_VALUE: Register value to program into RF + * RFGAP: Gap between BB_CONTROL_RF and RF_LE + * 0: 3 system clock cycle (37.5usec) + * 1: 5 system clock cycle (62.5usec) + */ +#define RF_CSR_CFG1 0x1024 +#define RF_CSR_CFG1_REGID_AND_VALUE FIELD32(0x00ffffff) +#define RF_CSR_CFG1_RFGAP FIELD32(0x1f000000) + +/* + * RF_CSR_CFG2: RF control register + * VALUE: Register value to program into RF + */ +#define RF_CSR_CFG2 0x1028 +#define RF_CSR_CFG2_VALUE FIELD32(0x00ffffff) + +/* + * LED_CFG: LED control + * ON_PERIOD: LED active time (ms) during TX (only used for LED mode 1) + * OFF_PERIOD: LED inactive time (ms) during TX (only used for LED mode 1) + * SLOW_BLINK_PERIOD: LED blink interval in seconds (only used for LED mode 2) + * color LED's: + * 0: off + * 1: blinking upon TX2 + * 2: periodic slow blinking + * 3: always on + * LED polarity: + * 0: active low + * 1: active high + */ +#define LED_CFG 0x102c +#define LED_CFG_ON_PERIOD FIELD32(0x000000ff) +#define LED_CFG_OFF_PERIOD FIELD32(0x0000ff00) +#define LED_CFG_SLOW_BLINK_PERIOD FIELD32(0x003f0000) +#define LED_CFG_R_LED_MODE FIELD32(0x03000000) +#define LED_CFG_G_LED_MODE FIELD32(0x0c000000) +#define LED_CFG_Y_LED_MODE FIELD32(0x30000000) +#define LED_CFG_LED_POLAR FIELD32(0x40000000) + +/* + * AMPDU_BA_WINSIZE: Force BlockAck window size + * FORCE_WINSIZE_ENABLE: + * 0: Disable forcing of BlockAck window size + * 1: Enable forcing of BlockAck window size, overwrites values BlockAck + * window size values in the TXWI + * FORCE_WINSIZE: BlockAck window size + */ +#define AMPDU_BA_WINSIZE 0x1040 +#define AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE FIELD32(0x00000020) +#define AMPDU_BA_WINSIZE_FORCE_WINSIZE FIELD32(0x0000001f) + +/* + * XIFS_TIME_CFG: MAC timing + * CCKM_SIFS_TIME: unit 1us. Applied after CCK RX/TX + * OFDM_SIFS_TIME: unit 1us. Applied after OFDM RX/TX + * OFDM_XIFS_TIME: unit 1us. Applied after OFDM RX + * when MAC doesn't reference BBP signal BBRXEND + * EIFS: unit 1us + * BB_RXEND_ENABLE: reference RXEND signal to begin XIFS defer + * + */ +#define XIFS_TIME_CFG 0x1100 +#define XIFS_TIME_CFG_CCKM_SIFS_TIME FIELD32(0x000000ff) +#define XIFS_TIME_CFG_OFDM_SIFS_TIME FIELD32(0x0000ff00) +#define XIFS_TIME_CFG_OFDM_XIFS_TIME FIELD32(0x000f0000) +#define XIFS_TIME_CFG_EIFS FIELD32(0x1ff00000) +#define XIFS_TIME_CFG_BB_RXEND_ENABLE FIELD32(0x20000000) + +/* + * BKOFF_SLOT_CFG: + */ +#define BKOFF_SLOT_CFG 0x1104 +#define BKOFF_SLOT_CFG_SLOT_TIME FIELD32(0x000000ff) +#define BKOFF_SLOT_CFG_CC_DELAY_TIME FIELD32(0x0000ff00) + +/* + * NAV_TIME_CFG: + */ +#define NAV_TIME_CFG 0x1108 +#define NAV_TIME_CFG_SIFS FIELD32(0x000000ff) +#define NAV_TIME_CFG_SLOT_TIME FIELD32(0x0000ff00) +#define NAV_TIME_CFG_EIFS FIELD32(0x01ff0000) +#define NAV_TIME_ZERO_SIFS FIELD32(0x02000000) + +/* + * CH_TIME_CFG: count as channel busy + * EIFS_BUSY: Count EIFS as channel busy + * NAV_BUSY: Count NAS as channel busy + * RX_BUSY: Count RX as channel busy + * TX_BUSY: Count TX as channel busy + * TMR_EN: Enable channel statistics timer + */ +#define CH_TIME_CFG 0x110c +#define CH_TIME_CFG_EIFS_BUSY FIELD32(0x00000010) +#define CH_TIME_CFG_NAV_BUSY FIELD32(0x00000008) +#define CH_TIME_CFG_RX_BUSY FIELD32(0x00000004) +#define CH_TIME_CFG_TX_BUSY FIELD32(0x00000002) +#define CH_TIME_CFG_TMR_EN FIELD32(0x00000001) + +/* + * PBF_LIFE_TIMER: TX/RX MPDU timestamp timer (free run) Unit: 1us + */ +#define PBF_LIFE_TIMER 0x1110 + +/* + * BCN_TIME_CFG: + * BEACON_INTERVAL: in unit of 1/16 TU + * TSF_TICKING: Enable TSF auto counting + * TSF_SYNC: Enable TSF sync, 00: disable, 01: infra mode, 10: ad-hoc mode + * BEACON_GEN: Enable beacon generator + */ +#define BCN_TIME_CFG 0x1114 +#define BCN_TIME_CFG_BEACON_INTERVAL FIELD32(0x0000ffff) +#define BCN_TIME_CFG_TSF_TICKING FIELD32(0x00010000) +#define BCN_TIME_CFG_TSF_SYNC FIELD32(0x00060000) +#define BCN_TIME_CFG_TBTT_ENABLE FIELD32(0x00080000) +#define BCN_TIME_CFG_BEACON_GEN FIELD32(0x00100000) +#define BCN_TIME_CFG_TX_TIME_COMPENSATE FIELD32(0xf0000000) + +/* + * TBTT_SYNC_CFG: + * BCN_AIFSN: Beacon AIFSN after TBTT interrupt in slots + * BCN_CWMIN: Beacon CWMin after TBTT interrupt in slots + */ +#define TBTT_SYNC_CFG 0x1118 +#define TBTT_SYNC_CFG_TBTT_ADJUST FIELD32(0x000000ff) +#define TBTT_SYNC_CFG_BCN_EXP_WIN FIELD32(0x0000ff00) +#define TBTT_SYNC_CFG_BCN_AIFSN FIELD32(0x000f0000) +#define TBTT_SYNC_CFG_BCN_CWMIN FIELD32(0x00f00000) + +/* + * TSF_TIMER_DW0: Local lsb TSF timer, read-only + */ +#define TSF_TIMER_DW0 0x111c +#define TSF_TIMER_DW0_LOW_WORD FIELD32(0xffffffff) + +/* + * TSF_TIMER_DW1: Local msb TSF timer, read-only + */ +#define TSF_TIMER_DW1 0x1120 +#define TSF_TIMER_DW1_HIGH_WORD FIELD32(0xffffffff) + +/* + * TBTT_TIMER: TImer remains till next TBTT, read-only + */ +#define TBTT_TIMER 0x1124 + +/* + * INT_TIMER_CFG: timer configuration + * PRE_TBTT_TIMER: leadtime to tbtt for pretbtt interrupt in units of 1/16 TU + * GP_TIMER: period of general purpose timer in units of 1/16 TU + */ +#define INT_TIMER_CFG 0x1128 +#define INT_TIMER_CFG_PRE_TBTT_TIMER FIELD32(0x0000ffff) +#define INT_TIMER_CFG_GP_TIMER FIELD32(0xffff0000) + +/* + * INT_TIMER_EN: GP-timer and pre-tbtt Int enable + */ +#define INT_TIMER_EN 0x112c +#define INT_TIMER_EN_PRE_TBTT_TIMER FIELD32(0x00000001) +#define INT_TIMER_EN_GP_TIMER FIELD32(0x00000002) + +/* + * CH_IDLE_STA: channel idle time (in us) + */ +#define CH_IDLE_STA 0x1130 + +/* + * CH_BUSY_STA: channel busy time on primary channel (in us) + */ +#define CH_BUSY_STA 0x1134 + +/* + * CH_BUSY_STA_SEC: channel busy time on secondary channel in HT40 mode (in us) + */ +#define CH_BUSY_STA_SEC 0x1138 + +/* + * MAC_STATUS_CFG: + * BBP_RF_BUSY: When set to 0, BBP and RF are stable. + * if 1 or higher one of the 2 registers is busy. + */ +#define MAC_STATUS_CFG 0x1200 +#define MAC_STATUS_CFG_BBP_RF_BUSY FIELD32(0x00000003) + +/* + * PWR_PIN_CFG: + */ +#define PWR_PIN_CFG 0x1204 + +/* + * AUTOWAKEUP_CFG: Manual power control / status register + * TBCN_BEFORE_WAKE: ForceWake has high privilege than PutToSleep when both set + * AUTOWAKE: 0:sleep, 1:awake + */ +#define AUTOWAKEUP_CFG 0x1208 +#define AUTOWAKEUP_CFG_AUTO_LEAD_TIME FIELD32(0x000000ff) +#define AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE FIELD32(0x00007f00) +#define AUTOWAKEUP_CFG_AUTOWAKE FIELD32(0x00008000) + +/* + * EDCA_AC0_CFG: + */ +#define EDCA_AC0_CFG 0x1300 +#define EDCA_AC0_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC0_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC0_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC0_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_AC1_CFG: + */ +#define EDCA_AC1_CFG 0x1304 +#define EDCA_AC1_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC1_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC1_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC1_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_AC2_CFG: + */ +#define EDCA_AC2_CFG 0x1308 +#define EDCA_AC2_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC2_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC2_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC2_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_AC3_CFG: + */ +#define EDCA_AC3_CFG 0x130c +#define EDCA_AC3_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC3_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC3_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC3_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_TID_AC_MAP: + */ +#define EDCA_TID_AC_MAP 0x1310 + +/* + * TX_PWR_CFG: + */ +#define TX_PWR_CFG_RATE0 FIELD32(0x0000000f) +#define TX_PWR_CFG_RATE1 FIELD32(0x000000f0) +#define TX_PWR_CFG_RATE2 FIELD32(0x00000f00) +#define TX_PWR_CFG_RATE3 FIELD32(0x0000f000) +#define TX_PWR_CFG_RATE4 FIELD32(0x000f0000) +#define TX_PWR_CFG_RATE5 FIELD32(0x00f00000) +#define TX_PWR_CFG_RATE6 FIELD32(0x0f000000) +#define TX_PWR_CFG_RATE7 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_0: + */ +#define TX_PWR_CFG_0 0x1314 +#define TX_PWR_CFG_0_1MBS FIELD32(0x0000000f) +#define TX_PWR_CFG_0_2MBS FIELD32(0x000000f0) +#define TX_PWR_CFG_0_55MBS FIELD32(0x00000f00) +#define TX_PWR_CFG_0_11MBS FIELD32(0x0000f000) +#define TX_PWR_CFG_0_6MBS FIELD32(0x000f0000) +#define TX_PWR_CFG_0_9MBS FIELD32(0x00f00000) +#define TX_PWR_CFG_0_12MBS FIELD32(0x0f000000) +#define TX_PWR_CFG_0_18MBS FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_0_CCK1_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_0_CCK1_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_0_CCK5_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_0_CCK5_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_0_OFDM6_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_0_OFDM6_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_0_OFDM12_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_0_OFDM12_CH1 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_1: + */ +#define TX_PWR_CFG_1 0x1318 +#define TX_PWR_CFG_1_24MBS FIELD32(0x0000000f) +#define TX_PWR_CFG_1_36MBS FIELD32(0x000000f0) +#define TX_PWR_CFG_1_48MBS FIELD32(0x00000f00) +#define TX_PWR_CFG_1_54MBS FIELD32(0x0000f000) +#define TX_PWR_CFG_1_MCS0 FIELD32(0x000f0000) +#define TX_PWR_CFG_1_MCS1 FIELD32(0x00f00000) +#define TX_PWR_CFG_1_MCS2 FIELD32(0x0f000000) +#define TX_PWR_CFG_1_MCS3 FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_1_OFDM24_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_1_OFDM24_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_1_OFDM48_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_1_OFDM48_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_1_MCS0_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_1_MCS0_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_1_MCS2_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_1_MCS2_CH1 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_2: + */ +#define TX_PWR_CFG_2 0x131c +#define TX_PWR_CFG_2_MCS4 FIELD32(0x0000000f) +#define TX_PWR_CFG_2_MCS5 FIELD32(0x000000f0) +#define TX_PWR_CFG_2_MCS6 FIELD32(0x00000f00) +#define TX_PWR_CFG_2_MCS7 FIELD32(0x0000f000) +#define TX_PWR_CFG_2_MCS8 FIELD32(0x000f0000) +#define TX_PWR_CFG_2_MCS9 FIELD32(0x00f00000) +#define TX_PWR_CFG_2_MCS10 FIELD32(0x0f000000) +#define TX_PWR_CFG_2_MCS11 FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_2_MCS4_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_2_MCS4_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_2_MCS6_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_2_MCS6_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_2_MCS8_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_2_MCS8_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_2_MCS10_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_2_MCS10_CH1 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_3: + */ +#define TX_PWR_CFG_3 0x1320 +#define TX_PWR_CFG_3_MCS12 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_MCS13 FIELD32(0x000000f0) +#define TX_PWR_CFG_3_MCS14 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_MCS15 FIELD32(0x0000f000) +#define TX_PWR_CFG_3_UKNOWN1 FIELD32(0x000f0000) +#define TX_PWR_CFG_3_UKNOWN2 FIELD32(0x00f00000) +#define TX_PWR_CFG_3_UKNOWN3 FIELD32(0x0f000000) +#define TX_PWR_CFG_3_UKNOWN4 FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_3_MCS12_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_MCS12_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_3_MCS14_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_MCS14_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_3_STBC0_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_3_STBC0_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_3_STBC2_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_3_STBC2_CH1 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_4: + */ +#define TX_PWR_CFG_4 0x1324 +#define TX_PWR_CFG_4_UKNOWN5 FIELD32(0x0000000f) +#define TX_PWR_CFG_4_UKNOWN6 FIELD32(0x000000f0) +#define TX_PWR_CFG_4_UKNOWN7 FIELD32(0x00000f00) +#define TX_PWR_CFG_4_UKNOWN8 FIELD32(0x0000f000) +/* bits for 3T devices */ +#define TX_PWR_CFG_3_STBC4_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_STBC4_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_3_STBC6_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_STBC6_CH1 FIELD32(0x0000f000) + +/* + * TX_PIN_CFG: + */ +#define TX_PIN_CFG 0x1328 +#define TX_PIN_CFG_PA_PE_DISABLE 0xfcfffff0 +#define TX_PIN_CFG_PA_PE_A0_EN FIELD32(0x00000001) +#define TX_PIN_CFG_PA_PE_G0_EN FIELD32(0x00000002) +#define TX_PIN_CFG_PA_PE_A1_EN FIELD32(0x00000004) +#define TX_PIN_CFG_PA_PE_G1_EN FIELD32(0x00000008) +#define TX_PIN_CFG_PA_PE_A0_POL FIELD32(0x00000010) +#define TX_PIN_CFG_PA_PE_G0_POL FIELD32(0x00000020) +#define TX_PIN_CFG_PA_PE_A1_POL FIELD32(0x00000040) +#define TX_PIN_CFG_PA_PE_G1_POL FIELD32(0x00000080) +#define TX_PIN_CFG_LNA_PE_A0_EN FIELD32(0x00000100) +#define TX_PIN_CFG_LNA_PE_G0_EN FIELD32(0x00000200) +#define TX_PIN_CFG_LNA_PE_A1_EN FIELD32(0x00000400) +#define TX_PIN_CFG_LNA_PE_G1_EN FIELD32(0x00000800) +#define TX_PIN_CFG_LNA_PE_A0_POL FIELD32(0x00001000) +#define TX_PIN_CFG_LNA_PE_G0_POL FIELD32(0x00002000) +#define TX_PIN_CFG_LNA_PE_A1_POL FIELD32(0x00004000) +#define TX_PIN_CFG_LNA_PE_G1_POL FIELD32(0x00008000) +#define TX_PIN_CFG_RFTR_EN FIELD32(0x00010000) +#define TX_PIN_CFG_RFTR_POL FIELD32(0x00020000) +#define TX_PIN_CFG_TRSW_EN FIELD32(0x00040000) +#define TX_PIN_CFG_TRSW_POL FIELD32(0x00080000) +#define TX_PIN_CFG_PA_PE_A2_EN FIELD32(0x01000000) +#define TX_PIN_CFG_PA_PE_G2_EN FIELD32(0x02000000) +#define TX_PIN_CFG_PA_PE_A2_POL FIELD32(0x04000000) +#define TX_PIN_CFG_PA_PE_G2_POL FIELD32(0x08000000) +#define TX_PIN_CFG_LNA_PE_A2_EN FIELD32(0x10000000) +#define TX_PIN_CFG_LNA_PE_G2_EN FIELD32(0x20000000) +#define TX_PIN_CFG_LNA_PE_A2_POL FIELD32(0x40000000) +#define TX_PIN_CFG_LNA_PE_G2_POL FIELD32(0x80000000) + +/* + * TX_BAND_CFG: 0x1 use upper 20MHz, 0x0 use lower 20MHz + */ +#define TX_BAND_CFG 0x132c +#define TX_BAND_CFG_HT40_MINUS FIELD32(0x00000001) +#define TX_BAND_CFG_A FIELD32(0x00000002) +#define TX_BAND_CFG_BG FIELD32(0x00000004) + +/* + * TX_SW_CFG0: + */ +#define TX_SW_CFG0 0x1330 + +/* + * TX_SW_CFG1: + */ +#define TX_SW_CFG1 0x1334 + +/* + * TX_SW_CFG2: + */ +#define TX_SW_CFG2 0x1338 + +/* + * TXOP_THRES_CFG: + */ +#define TXOP_THRES_CFG 0x133c + +/* + * TXOP_CTRL_CFG: + * TIMEOUT_TRUN_EN: Enable/Disable TXOP timeout truncation + * AC_TRUN_EN: Enable/Disable truncation for AC change + * TXRATEGRP_TRUN_EN: Enable/Disable truncation for TX rate group change + * USER_MODE_TRUN_EN: Enable/Disable truncation for user TXOP mode + * MIMO_PS_TRUN_EN: Enable/Disable truncation for MIMO PS RTS/CTS + * RESERVED_TRUN_EN: Reserved + * LSIG_TXOP_EN: Enable/Disable L-SIG TXOP protection + * EXT_CCA_EN: Enable/Disable extension channel CCA reference (Defer 40Mhz + * transmissions if extension CCA is clear). + * EXT_CCA_DLY: Extension CCA signal delay time (unit: us) + * EXT_CWMIN: CwMin for extension channel backoff + * 0: Disabled + * + */ +#define TXOP_CTRL_CFG 0x1340 +#define TXOP_CTRL_CFG_TIMEOUT_TRUN_EN FIELD32(0x00000001) +#define TXOP_CTRL_CFG_AC_TRUN_EN FIELD32(0x00000002) +#define TXOP_CTRL_CFG_TXRATEGRP_TRUN_EN FIELD32(0x00000004) +#define TXOP_CTRL_CFG_USER_MODE_TRUN_EN FIELD32(0x00000008) +#define TXOP_CTRL_CFG_MIMO_PS_TRUN_EN FIELD32(0x00000010) +#define TXOP_CTRL_CFG_RESERVED_TRUN_EN FIELD32(0x00000020) +#define TXOP_CTRL_CFG_LSIG_TXOP_EN FIELD32(0x00000040) +#define TXOP_CTRL_CFG_EXT_CCA_EN FIELD32(0x00000080) +#define TXOP_CTRL_CFG_EXT_CCA_DLY FIELD32(0x0000ff00) +#define TXOP_CTRL_CFG_EXT_CWMIN FIELD32(0x000f0000) + +/* + * TX_RTS_CFG: + * RTS_THRES: unit:byte + * RTS_FBK_EN: enable rts rate fallback + */ +#define TX_RTS_CFG 0x1344 +#define TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT FIELD32(0x000000ff) +#define TX_RTS_CFG_RTS_THRES FIELD32(0x00ffff00) +#define TX_RTS_CFG_RTS_FBK_EN FIELD32(0x01000000) + +/* + * TX_TIMEOUT_CFG: + * MPDU_LIFETIME: expiration time = 2^(9+MPDU LIFE TIME) us + * RX_ACK_TIMEOUT: unit:slot. Used for TX procedure + * TX_OP_TIMEOUT: TXOP timeout value for TXOP truncation. + * it is recommended that: + * (SLOT_TIME) > (TX_OP_TIMEOUT) > (RX_ACK_TIMEOUT) + */ +#define TX_TIMEOUT_CFG 0x1348 +#define TX_TIMEOUT_CFG_MPDU_LIFETIME FIELD32(0x000000f0) +#define TX_TIMEOUT_CFG_RX_ACK_TIMEOUT FIELD32(0x0000ff00) +#define TX_TIMEOUT_CFG_TX_OP_TIMEOUT FIELD32(0x00ff0000) + +/* + * TX_RTY_CFG: + * SHORT_RTY_LIMIT: short retry limit + * LONG_RTY_LIMIT: long retry limit + * LONG_RTY_THRE: Long retry threshoold + * NON_AGG_RTY_MODE: Non-Aggregate MPDU retry mode + * 0:expired by retry limit, 1: expired by mpdu life timer + * AGG_RTY_MODE: Aggregate MPDU retry mode + * 0:expired by retry limit, 1: expired by mpdu life timer + * TX_AUTO_FB_ENABLE: Tx retry PHY rate auto fallback enable + */ +#define TX_RTY_CFG 0x134c +#define TX_RTY_CFG_SHORT_RTY_LIMIT FIELD32(0x000000ff) +#define TX_RTY_CFG_LONG_RTY_LIMIT FIELD32(0x0000ff00) +#define TX_RTY_CFG_LONG_RTY_THRE FIELD32(0x0fff0000) +#define TX_RTY_CFG_NON_AGG_RTY_MODE FIELD32(0x10000000) +#define TX_RTY_CFG_AGG_RTY_MODE FIELD32(0x20000000) +#define TX_RTY_CFG_TX_AUTO_FB_ENABLE FIELD32(0x40000000) + +/* + * TX_LINK_CFG: + * REMOTE_MFB_LIFETIME: remote MFB life time. unit: 32us + * MFB_ENABLE: TX apply remote MFB 1:enable + * REMOTE_UMFS_ENABLE: remote unsolicit MFB enable + * 0: not apply remote remote unsolicit (MFS=7) + * TX_MRQ_EN: MCS request TX enable + * TX_RDG_EN: RDG TX enable + * TX_CF_ACK_EN: Piggyback CF-ACK enable + * REMOTE_MFB: remote MCS feedback + * REMOTE_MFS: remote MCS feedback sequence number + */ +#define TX_LINK_CFG 0x1350 +#define TX_LINK_CFG_REMOTE_MFB_LIFETIME FIELD32(0x000000ff) +#define TX_LINK_CFG_MFB_ENABLE FIELD32(0x00000100) +#define TX_LINK_CFG_REMOTE_UMFS_ENABLE FIELD32(0x00000200) +#define TX_LINK_CFG_TX_MRQ_EN FIELD32(0x00000400) +#define TX_LINK_CFG_TX_RDG_EN FIELD32(0x00000800) +#define TX_LINK_CFG_TX_CF_ACK_EN FIELD32(0x00001000) +#define TX_LINK_CFG_REMOTE_MFB FIELD32(0x00ff0000) +#define TX_LINK_CFG_REMOTE_MFS FIELD32(0xff000000) + +/* + * HT_FBK_CFG0: + */ +#define HT_FBK_CFG0 0x1354 +#define HT_FBK_CFG0_HTMCS0FBK FIELD32(0x0000000f) +#define HT_FBK_CFG0_HTMCS1FBK FIELD32(0x000000f0) +#define HT_FBK_CFG0_HTMCS2FBK FIELD32(0x00000f00) +#define HT_FBK_CFG0_HTMCS3FBK FIELD32(0x0000f000) +#define HT_FBK_CFG0_HTMCS4FBK FIELD32(0x000f0000) +#define HT_FBK_CFG0_HTMCS5FBK FIELD32(0x00f00000) +#define HT_FBK_CFG0_HTMCS6FBK FIELD32(0x0f000000) +#define HT_FBK_CFG0_HTMCS7FBK FIELD32(0xf0000000) + +/* + * HT_FBK_CFG1: + */ +#define HT_FBK_CFG1 0x1358 +#define HT_FBK_CFG1_HTMCS8FBK FIELD32(0x0000000f) +#define HT_FBK_CFG1_HTMCS9FBK FIELD32(0x000000f0) +#define HT_FBK_CFG1_HTMCS10FBK FIELD32(0x00000f00) +#define HT_FBK_CFG1_HTMCS11FBK FIELD32(0x0000f000) +#define HT_FBK_CFG1_HTMCS12FBK FIELD32(0x000f0000) +#define HT_FBK_CFG1_HTMCS13FBK FIELD32(0x00f00000) +#define HT_FBK_CFG1_HTMCS14FBK FIELD32(0x0f000000) +#define HT_FBK_CFG1_HTMCS15FBK FIELD32(0xf0000000) + +/* + * LG_FBK_CFG0: + */ +#define LG_FBK_CFG0 0x135c +#define LG_FBK_CFG0_OFDMMCS0FBK FIELD32(0x0000000f) +#define LG_FBK_CFG0_OFDMMCS1FBK FIELD32(0x000000f0) +#define LG_FBK_CFG0_OFDMMCS2FBK FIELD32(0x00000f00) +#define LG_FBK_CFG0_OFDMMCS3FBK FIELD32(0x0000f000) +#define LG_FBK_CFG0_OFDMMCS4FBK FIELD32(0x000f0000) +#define LG_FBK_CFG0_OFDMMCS5FBK FIELD32(0x00f00000) +#define LG_FBK_CFG0_OFDMMCS6FBK FIELD32(0x0f000000) +#define LG_FBK_CFG0_OFDMMCS7FBK FIELD32(0xf0000000) + +/* + * LG_FBK_CFG1: + */ +#define LG_FBK_CFG1 0x1360 +#define LG_FBK_CFG0_CCKMCS0FBK FIELD32(0x0000000f) +#define LG_FBK_CFG0_CCKMCS1FBK FIELD32(0x000000f0) +#define LG_FBK_CFG0_CCKMCS2FBK FIELD32(0x00000f00) +#define LG_FBK_CFG0_CCKMCS3FBK FIELD32(0x0000f000) + +/* + * CCK_PROT_CFG: CCK Protection + * PROTECT_RATE: Protection control frame rate for CCK TX(RTS/CTS/CFEnd) + * PROTECT_CTRL: Protection control frame type for CCK TX + * 0:none, 1:RTS/CTS, 2:CTS-to-self + * PROTECT_NAV_SHORT: TXOP protection type for CCK TX with short NAV + * PROTECT_NAV_LONG: TXOP protection type for CCK TX with long NAV + * TX_OP_ALLOW_CCK: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_OFDM: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_MM20: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_MM40: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_GF20: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_GF40: CCK TXOP allowance, 0:disallow + * RTS_TH_EN: RTS threshold enable on CCK TX + */ +#define CCK_PROT_CFG 0x1364 +#define CCK_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define CCK_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define CCK_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define CCK_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define CCK_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define CCK_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define CCK_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define CCK_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define CCK_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define CCK_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define CCK_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * OFDM_PROT_CFG: OFDM Protection + */ +#define OFDM_PROT_CFG 0x1368 +#define OFDM_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define OFDM_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define OFDM_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define OFDM_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define OFDM_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * MM20_PROT_CFG: MM20 Protection + */ +#define MM20_PROT_CFG 0x136c +#define MM20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define MM20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define MM20_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define MM20_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define MM20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define MM20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define MM20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define MM20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define MM20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define MM20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define MM20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * MM40_PROT_CFG: MM40 Protection + */ +#define MM40_PROT_CFG 0x1370 +#define MM40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define MM40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define MM40_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define MM40_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define MM40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define MM40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define MM40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define MM40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define MM40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define MM40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define MM40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * GF20_PROT_CFG: GF20 Protection + */ +#define GF20_PROT_CFG 0x1374 +#define GF20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define GF20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define GF20_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define GF20_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define GF20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define GF20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define GF20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define GF20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define GF20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define GF20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define GF20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * GF40_PROT_CFG: GF40 Protection + */ +#define GF40_PROT_CFG 0x1378 +#define GF40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define GF40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define GF40_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define GF40_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define GF40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define GF40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define GF40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define GF40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define GF40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define GF40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define GF40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * EXP_CTS_TIME: + */ +#define EXP_CTS_TIME 0x137c + +/* + * EXP_ACK_TIME: + */ +#define EXP_ACK_TIME 0x1380 + +/* TX_PWR_CFG_5 */ +#define TX_PWR_CFG_5 0x1384 +#define TX_PWR_CFG_5_MCS16_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_5_MCS16_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_5_MCS16_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_5_MCS18_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_5_MCS18_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_5_MCS18_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_6 */ +#define TX_PWR_CFG_6 0x1388 +#define TX_PWR_CFG_6_MCS20_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_6_MCS20_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_6_MCS20_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_6_MCS22_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_6_MCS22_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_6_MCS22_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_0_EXT */ +#define TX_PWR_CFG_0_EXT 0x1390 +#define TX_PWR_CFG_0_EXT_CCK1_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_0_EXT_CCK5_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_0_EXT_OFDM6_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_0_EXT_OFDM12_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_1_EXT */ +#define TX_PWR_CFG_1_EXT 0x1394 +#define TX_PWR_CFG_1_EXT_OFDM24_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_1_EXT_OFDM48_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_1_EXT_MCS0_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_1_EXT_MCS2_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_2_EXT */ +#define TX_PWR_CFG_2_EXT 0x1398 +#define TX_PWR_CFG_2_EXT_MCS4_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_2_EXT_MCS6_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_2_EXT_MCS8_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_2_EXT_MCS10_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_3_EXT */ +#define TX_PWR_CFG_3_EXT 0x139c +#define TX_PWR_CFG_3_EXT_MCS12_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_EXT_MCS14_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_EXT_STBC0_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_3_EXT_STBC2_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_4_EXT */ +#define TX_PWR_CFG_4_EXT 0x13a0 +#define TX_PWR_CFG_4_EXT_STBC4_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_4_EXT_STBC6_CH2 FIELD32(0x00000f00) + +/* TX_PWR_CFG_7 */ +#define TX_PWR_CFG_7 0x13d4 +#define TX_PWR_CFG_7_OFDM54_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_7_OFDM54_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_7_OFDM54_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_7_MCS7_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_7_MCS7_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_7_MCS7_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_8 */ +#define TX_PWR_CFG_8 0x13d8 +#define TX_PWR_CFG_8_MCS15_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_8_MCS15_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_8_MCS15_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_8_MCS23_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_8_MCS23_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_8_MCS23_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_9 */ +#define TX_PWR_CFG_9 0x13dc +#define TX_PWR_CFG_9_STBC7_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_9_STBC7_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_9_STBC7_CH2 FIELD32(0x00000f00) + +/* + * RX_FILTER_CFG: RX configuration register. + */ +#define RX_FILTER_CFG 0x1400 +#define RX_FILTER_CFG_DROP_CRC_ERROR FIELD32(0x00000001) +#define RX_FILTER_CFG_DROP_PHY_ERROR FIELD32(0x00000002) +#define RX_FILTER_CFG_DROP_NOT_TO_ME FIELD32(0x00000004) +#define RX_FILTER_CFG_DROP_NOT_MY_BSSD FIELD32(0x00000008) +#define RX_FILTER_CFG_DROP_VER_ERROR FIELD32(0x00000010) +#define RX_FILTER_CFG_DROP_MULTICAST FIELD32(0x00000020) +#define RX_FILTER_CFG_DROP_BROADCAST FIELD32(0x00000040) +#define RX_FILTER_CFG_DROP_DUPLICATE FIELD32(0x00000080) +#define RX_FILTER_CFG_DROP_CF_END_ACK FIELD32(0x00000100) +#define RX_FILTER_CFG_DROP_CF_END FIELD32(0x00000200) +#define RX_FILTER_CFG_DROP_ACK FIELD32(0x00000400) +#define RX_FILTER_CFG_DROP_CTS FIELD32(0x00000800) +#define RX_FILTER_CFG_DROP_RTS FIELD32(0x00001000) +#define RX_FILTER_CFG_DROP_PSPOLL FIELD32(0x00002000) +#define RX_FILTER_CFG_DROP_BA FIELD32(0x00004000) +#define RX_FILTER_CFG_DROP_BAR FIELD32(0x00008000) +#define RX_FILTER_CFG_DROP_CNTL FIELD32(0x00010000) + +/* + * AUTO_RSP_CFG: + * AUTORESPONDER: 0: disable, 1: enable + * BAC_ACK_POLICY: 0:long, 1:short preamble + * CTS_40_MMODE: Response CTS 40MHz duplicate mode + * CTS_40_MREF: Response CTS 40MHz duplicate mode + * AR_PREAMBLE: Auto responder preamble 0:long, 1:short preamble + * DUAL_CTS_EN: Power bit value in control frame + * ACK_CTS_PSM_BIT:Power bit value in control frame + */ +#define AUTO_RSP_CFG 0x1404 +#define AUTO_RSP_CFG_AUTORESPONDER FIELD32(0x00000001) +#define AUTO_RSP_CFG_BAC_ACK_POLICY FIELD32(0x00000002) +#define AUTO_RSP_CFG_CTS_40_MMODE FIELD32(0x00000004) +#define AUTO_RSP_CFG_CTS_40_MREF FIELD32(0x00000008) +#define AUTO_RSP_CFG_AR_PREAMBLE FIELD32(0x00000010) +#define AUTO_RSP_CFG_DUAL_CTS_EN FIELD32(0x00000040) +#define AUTO_RSP_CFG_ACK_CTS_PSM_BIT FIELD32(0x00000080) + +/* + * LEGACY_BASIC_RATE: + */ +#define LEGACY_BASIC_RATE 0x1408 + +/* + * HT_BASIC_RATE: + */ +#define HT_BASIC_RATE 0x140c + +/* + * HT_CTRL_CFG: + */ +#define HT_CTRL_CFG 0x1410 + +/* + * SIFS_COST_CFG: + */ +#define SIFS_COST_CFG 0x1414 + +/* + * RX_PARSER_CFG: + * Set NAV for all received frames + */ +#define RX_PARSER_CFG 0x1418 + +/* + * TX_SEC_CNT0: + */ +#define TX_SEC_CNT0 0x1500 + +/* + * RX_SEC_CNT0: + */ +#define RX_SEC_CNT0 0x1504 + +/* + * CCMP_FC_MUTE: + */ +#define CCMP_FC_MUTE 0x1508 + +/* + * TXOP_HLDR_ADDR0: + */ +#define TXOP_HLDR_ADDR0 0x1600 + +/* + * TXOP_HLDR_ADDR1: + */ +#define TXOP_HLDR_ADDR1 0x1604 + +/* + * TXOP_HLDR_ET: + */ +#define TXOP_HLDR_ET 0x1608 + +/* + * QOS_CFPOLL_RA_DW0: + */ +#define QOS_CFPOLL_RA_DW0 0x160c + +/* + * QOS_CFPOLL_RA_DW1: + */ +#define QOS_CFPOLL_RA_DW1 0x1610 + +/* + * QOS_CFPOLL_QC: + */ +#define QOS_CFPOLL_QC 0x1614 + +/* + * RX_STA_CNT0: RX PLCP error count & RX CRC error count + */ +#define RX_STA_CNT0 0x1700 +#define RX_STA_CNT0_CRC_ERR FIELD32(0x0000ffff) +#define RX_STA_CNT0_PHY_ERR FIELD32(0xffff0000) + +/* + * RX_STA_CNT1: RX False CCA count & RX LONG frame count + */ +#define RX_STA_CNT1 0x1704 +#define RX_STA_CNT1_FALSE_CCA FIELD32(0x0000ffff) +#define RX_STA_CNT1_PLCP_ERR FIELD32(0xffff0000) + +/* + * RX_STA_CNT2: + */ +#define RX_STA_CNT2 0x1708 +#define RX_STA_CNT2_RX_DUPLI_COUNT FIELD32(0x0000ffff) +#define RX_STA_CNT2_RX_FIFO_OVERFLOW FIELD32(0xffff0000) + +/* + * TX_STA_CNT0: TX Beacon count + */ +#define TX_STA_CNT0 0x170c +#define TX_STA_CNT0_TX_FAIL_COUNT FIELD32(0x0000ffff) +#define TX_STA_CNT0_TX_BEACON_COUNT FIELD32(0xffff0000) + +/* + * TX_STA_CNT1: TX tx count + */ +#define TX_STA_CNT1 0x1710 +#define TX_STA_CNT1_TX_SUCCESS FIELD32(0x0000ffff) +#define TX_STA_CNT1_TX_RETRANSMIT FIELD32(0xffff0000) + +/* + * TX_STA_CNT2: TX tx count + */ +#define TX_STA_CNT2 0x1714 +#define TX_STA_CNT2_TX_ZERO_LEN_COUNT FIELD32(0x0000ffff) +#define TX_STA_CNT2_TX_UNDER_FLOW_COUNT FIELD32(0xffff0000) + +/* + * TX_STA_FIFO: TX Result for specific PID status fifo register. + * + * This register is implemented as FIFO with 16 entries in the HW. Each + * register read fetches the next tx result. If the FIFO is full because + * it wasn't read fast enough after the according interrupt (TX_FIFO_STATUS) + * triggered, the hw seems to simply drop further tx results. + * + * VALID: 1: this tx result is valid + * 0: no valid tx result -> driver should stop reading + * PID_TYPE: The PID latched from the PID field in the TXWI, can be used + * to match a frame with its tx result (even though the PID is + * only 4 bits wide). + * PID_QUEUE: Part of PID_TYPE, this is the queue index number (0-3) + * PID_ENTRY: Part of PID_TYPE, this is the queue entry index number (1-3) + * This identification number is calculated by ((idx % 3) + 1). + * TX_SUCCESS: Indicates tx success (1) or failure (0) + * TX_AGGRE: Indicates if the frame was part of an aggregate (1) or not (0) + * TX_ACK_REQUIRED: Indicates if the frame needed to get ack'ed (1) or not (0) + * WCID: The wireless client ID. + * MCS: The tx rate used during the last transmission of this frame, be it + * successful or not. + * PHYMODE: The phymode used for the transmission. + */ +#define TX_STA_FIFO 0x1718 +#define TX_STA_FIFO_VALID FIELD32(0x00000001) +#define TX_STA_FIFO_PID_TYPE FIELD32(0x0000001e) +#define TX_STA_FIFO_PID_QUEUE FIELD32(0x00000006) +#define TX_STA_FIFO_PID_ENTRY FIELD32(0x00000018) +#define TX_STA_FIFO_TX_SUCCESS FIELD32(0x00000020) +#define TX_STA_FIFO_TX_AGGRE FIELD32(0x00000040) +#define TX_STA_FIFO_TX_ACK_REQUIRED FIELD32(0x00000080) +#define TX_STA_FIFO_WCID FIELD32(0x0000ff00) +#define TX_STA_FIFO_SUCCESS_RATE FIELD32(0xffff0000) +#define TX_STA_FIFO_MCS FIELD32(0x007f0000) +#define TX_STA_FIFO_PHYMODE FIELD32(0xc0000000) + +/* + * TX_AGG_CNT: Debug counter + */ +#define TX_AGG_CNT 0x171c +#define TX_AGG_CNT_NON_AGG_TX_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT_AGG_TX_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT0: + */ +#define TX_AGG_CNT0 0x1720 +#define TX_AGG_CNT0_AGG_SIZE_1_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT0_AGG_SIZE_2_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT1: + */ +#define TX_AGG_CNT1 0x1724 +#define TX_AGG_CNT1_AGG_SIZE_3_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT1_AGG_SIZE_4_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT2: + */ +#define TX_AGG_CNT2 0x1728 +#define TX_AGG_CNT2_AGG_SIZE_5_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT2_AGG_SIZE_6_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT3: + */ +#define TX_AGG_CNT3 0x172c +#define TX_AGG_CNT3_AGG_SIZE_7_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT3_AGG_SIZE_8_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT4: + */ +#define TX_AGG_CNT4 0x1730 +#define TX_AGG_CNT4_AGG_SIZE_9_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT4_AGG_SIZE_10_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT5: + */ +#define TX_AGG_CNT5 0x1734 +#define TX_AGG_CNT5_AGG_SIZE_11_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT5_AGG_SIZE_12_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT6: + */ +#define TX_AGG_CNT6 0x1738 +#define TX_AGG_CNT6_AGG_SIZE_13_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT6_AGG_SIZE_14_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT7: + */ +#define TX_AGG_CNT7 0x173c +#define TX_AGG_CNT7_AGG_SIZE_15_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT7_AGG_SIZE_16_COUNT FIELD32(0xffff0000) + +/* + * MPDU_DENSITY_CNT: + * TX_ZERO_DEL: TX zero length delimiter count + * RX_ZERO_DEL: RX zero length delimiter count + */ +#define MPDU_DENSITY_CNT 0x1740 +#define MPDU_DENSITY_CNT_TX_ZERO_DEL FIELD32(0x0000ffff) +#define MPDU_DENSITY_CNT_RX_ZERO_DEL FIELD32(0xffff0000) + +/* + * Security key table memory. + * + * The pairwise key table shares some memory with the beacon frame + * buffers 6 and 7. That basically means that when beacon 6 & 7 + * are used we should only use the reduced pairwise key table which + * has a maximum of 222 entries. + * + * --------------------------------------------- + * |0x4000 | Pairwise Key | Reduced Pairwise | + * | | Table | Key Table | + * | | Size: 256 * 32 | Size: 222 * 32 | + * |0x5BC0 | |------------------- + * | | | Beacon 6 | + * |0x5DC0 | |------------------- + * | | | Beacon 7 | + * |0x5FC0 | |------------------- + * |0x5FFF | | + * -------------------------- + * + * MAC_WCID_BASE: 8-bytes (use only 6 bytes) * 256 entry + * PAIRWISE_KEY_TABLE_BASE: 32-byte * 256 entry + * MAC_IVEIV_TABLE_BASE: 8-byte * 256-entry + * MAC_WCID_ATTRIBUTE_BASE: 4-byte * 256-entry + * SHARED_KEY_TABLE_BASE: 32-byte * 16-entry + * SHARED_KEY_MODE_BASE: 4-byte * 16-entry + */ +#define MAC_WCID_BASE 0x1800 +#define PAIRWISE_KEY_TABLE_BASE 0x4000 +#define MAC_IVEIV_TABLE_BASE 0x6000 +#define MAC_WCID_ATTRIBUTE_BASE 0x6800 +#define SHARED_KEY_TABLE_BASE 0x6c00 +#define SHARED_KEY_MODE_BASE 0x7000 + +#define MAC_WCID_ENTRY(__idx) \ + (MAC_WCID_BASE + ((__idx) * sizeof(struct mac_wcid_entry))) +#define PAIRWISE_KEY_ENTRY(__idx) \ + (PAIRWISE_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry))) +#define MAC_IVEIV_ENTRY(__idx) \ + (MAC_IVEIV_TABLE_BASE + ((__idx) * sizeof(struct mac_iveiv_entry))) +#define MAC_WCID_ATTR_ENTRY(__idx) \ + (MAC_WCID_ATTRIBUTE_BASE + ((__idx) * sizeof(u32))) +#define SHARED_KEY_ENTRY(__idx) \ + (SHARED_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry))) +#define SHARED_KEY_MODE_ENTRY(__idx) \ + (SHARED_KEY_MODE_BASE + ((__idx) * sizeof(u32))) + +struct mac_wcid_entry { + u8 mac[6]; + u8 reserved[2]; +} __packed; + +struct hw_key_entry { + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; +} __packed; + +struct mac_iveiv_entry { + u8 iv[8]; +} __packed; + +/* + * MAC_WCID_ATTRIBUTE: + */ +#define MAC_WCID_ATTRIBUTE_KEYTAB FIELD32(0x00000001) +#define MAC_WCID_ATTRIBUTE_CIPHER FIELD32(0x0000000e) +#define MAC_WCID_ATTRIBUTE_BSS_IDX FIELD32(0x00000070) +#define MAC_WCID_ATTRIBUTE_RX_WIUDF FIELD32(0x00000380) +#define MAC_WCID_ATTRIBUTE_CIPHER_EXT FIELD32(0x00000400) +#define MAC_WCID_ATTRIBUTE_BSS_IDX_EXT FIELD32(0x00000800) +#define MAC_WCID_ATTRIBUTE_WAPI_MCBC FIELD32(0x00008000) +#define MAC_WCID_ATTRIBUTE_WAPI_KEY_IDX FIELD32(0xff000000) + +/* + * SHARED_KEY_MODE: + */ +#define SHARED_KEY_MODE_BSS0_KEY0 FIELD32(0x00000007) +#define SHARED_KEY_MODE_BSS0_KEY1 FIELD32(0x00000070) +#define SHARED_KEY_MODE_BSS0_KEY2 FIELD32(0x00000700) +#define SHARED_KEY_MODE_BSS0_KEY3 FIELD32(0x00007000) +#define SHARED_KEY_MODE_BSS1_KEY0 FIELD32(0x00070000) +#define SHARED_KEY_MODE_BSS1_KEY1 FIELD32(0x00700000) +#define SHARED_KEY_MODE_BSS1_KEY2 FIELD32(0x07000000) +#define SHARED_KEY_MODE_BSS1_KEY3 FIELD32(0x70000000) + +/* + * HOST-MCU communication + */ + +/* + * H2M_MAILBOX_CSR: Host-to-MCU Mailbox. + * CMD_TOKEN: Command id, 0xff disable status reporting. + */ +#define H2M_MAILBOX_CSR 0x7010 +#define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff) +#define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00) +#define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000) +#define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000) + +/* + * H2M_MAILBOX_CID: + * Free slots contain 0xff. MCU will store command's token to lowest free slot. + * If all slots are occupied status will be dropped. + */ +#define H2M_MAILBOX_CID 0x7014 +#define H2M_MAILBOX_CID_CMD0 FIELD32(0x000000ff) +#define H2M_MAILBOX_CID_CMD1 FIELD32(0x0000ff00) +#define H2M_MAILBOX_CID_CMD2 FIELD32(0x00ff0000) +#define H2M_MAILBOX_CID_CMD3 FIELD32(0xff000000) + +/* + * H2M_MAILBOX_STATUS: + * Command status will be saved to same slot as command id. + */ +#define H2M_MAILBOX_STATUS 0x701c + +/* + * H2M_INT_SRC: + */ +#define H2M_INT_SRC 0x7024 + +/* + * H2M_BBP_AGENT: + */ +#define H2M_BBP_AGENT 0x7028 + +/* + * MCU_LEDCS: LED control for MCU Mailbox. + */ +#define MCU_LEDCS_LED_MODE FIELD8(0x1f) +#define MCU_LEDCS_POLARITY FIELD8(0x01) + +/* + * HW_CS_CTS_BASE: + * Carrier-sense CTS frame base address. + * It's where mac stores carrier-sense frame for carrier-sense function. + */ +#define HW_CS_CTS_BASE 0x7700 + +/* + * HW_DFS_CTS_BASE: + * DFS CTS frame base address. It's where mac stores CTS frame for DFS. + */ +#define HW_DFS_CTS_BASE 0x7780 + +/* + * TXRX control registers - base address 0x3000 + */ + +/* + * TXRX_CSR1: + * rt2860b UNKNOWN reg use R/O Reg Addr 0x77d0 first.. + */ +#define TXRX_CSR1 0x77d0 + +/* + * HW_DEBUG_SETTING_BASE: + * since NULL frame won't be that long (256 byte) + * We steal 16 tail bytes to save debugging settings + */ +#define HW_DEBUG_SETTING_BASE 0x77f0 +#define HW_DEBUG_SETTING_BASE2 0x7770 + +/* + * HW_BEACON_BASE + * In order to support maximum 8 MBSS and its maximum length + * is 512 bytes for each beacon + * Three section discontinue memory segments will be used. + * 1. The original region for BCN 0~3 + * 2. Extract memory from FCE table for BCN 4~5 + * 3. Extract memory from Pair-wise key table for BCN 6~7 + * It occupied those memory of wcid 238~253 for BCN 6 + * and wcid 222~237 for BCN 7 (see Security key table memory + * for more info). + * + * IMPORTANT NOTE: Not sure why legacy driver does this, + * but HW_BEACON_BASE7 is 0x0200 bytes below HW_BEACON_BASE6. + */ +#define HW_BEACON_BASE0 0x7800 +#define HW_BEACON_BASE1 0x7a00 +#define HW_BEACON_BASE2 0x7c00 +#define HW_BEACON_BASE3 0x7e00 +#define HW_BEACON_BASE4 0x7200 +#define HW_BEACON_BASE5 0x7400 +#define HW_BEACON_BASE6 0x5dc0 +#define HW_BEACON_BASE7 0x5bc0 + +#define HW_BEACON_BASE(__index) \ + (((__index) < 4) ? (HW_BEACON_BASE0 + (__index * 0x0200)) : \ + (((__index) < 6) ? (HW_BEACON_BASE4 + ((__index - 4) * 0x0200)) : \ + (HW_BEACON_BASE6 - ((__index - 6) * 0x0200)))) + +#define BEACON_BASE_TO_OFFSET(_base) (((_base) - 0x4000) / 64) + +/* + * BBP registers. + * The wordsize of the BBP is 8 bits. + */ + +/* + * BBP 1: TX Antenna & Power Control + * POWER_CTRL: + * 0 - normal, + * 1 - drop tx power by 6dBm, + * 2 - drop tx power by 12dBm, + * 3 - increase tx power by 6dBm + */ +#define BBP1_TX_POWER_CTRL FIELD8(0x03) +#define BBP1_TX_ANTENNA FIELD8(0x18) + +/* + * BBP 3: RX Antenna + */ +#define BBP3_RX_ADC FIELD8(0x03) +#define BBP3_RX_ANTENNA FIELD8(0x18) +#define BBP3_HT40_MINUS FIELD8(0x20) +#define BBP3_ADC_MODE_SWITCH FIELD8(0x40) +#define BBP3_ADC_INIT_MODE FIELD8(0x80) + +/* + * BBP 4: Bandwidth + */ +#define BBP4_TX_BF FIELD8(0x01) +#define BBP4_BANDWIDTH FIELD8(0x18) +#define BBP4_MAC_IF_CTRL FIELD8(0x40) + +/* BBP27 */ +#define BBP27_RX_CHAIN_SEL FIELD8(0x60) + +/* + * BBP 47: Bandwidth + */ +#define BBP47_TSSI_REPORT_SEL FIELD8(0x03) +#define BBP47_TSSI_UPDATE_REQ FIELD8(0x04) +#define BBP47_TSSI_TSSI_MODE FIELD8(0x18) +#define BBP47_TSSI_ADC6 FIELD8(0x80) + +/* + * BBP 49 + */ +#define BBP49_UPDATE_FLAG FIELD8(0x01) + +/* + * BBP 105: + * - bit0: detect SIG on primary channel only (on 40MHz bandwidth) + * - bit1: FEQ (Feed Forward Compensation) for independend streams + * - bit2: MLD (Maximum Likehood Detection) for 2 streams (reserved on single + * stream) + * - bit4: channel estimation updates based on remodulation of + * L-SIG and HT-SIG symbols + */ +#define BBP105_DETECT_SIG_ON_PRIMARY FIELD8(0x01) +#define BBP105_FEQ FIELD8(0x02) +#define BBP105_MLD FIELD8(0x04) +#define BBP105_SIG_REMODULATION FIELD8(0x08) + +/* + * BBP 109 + */ +#define BBP109_TX0_POWER FIELD8(0x0f) +#define BBP109_TX1_POWER FIELD8(0xf0) + +/* BBP 110 */ +#define BBP110_TX2_POWER FIELD8(0x0f) + + +/* + * BBP 138: Unknown + */ +#define BBP138_RX_ADC1 FIELD8(0x02) +#define BBP138_RX_ADC2 FIELD8(0x04) +#define BBP138_TX_DAC1 FIELD8(0x20) +#define BBP138_TX_DAC2 FIELD8(0x40) + +/* + * BBP 152: Rx Ant + */ +#define BBP152_RX_DEFAULT_ANT FIELD8(0x80) + +/* + * BBP 254: unknown + */ +#define BBP254_BIT7 FIELD8(0x80) + +/* + * RFCSR registers + * The wordsize of the RFCSR is 8 bits. + */ + +/* + * RFCSR 1: + */ +#define RFCSR1_RF_BLOCK_EN FIELD8(0x01) +#define RFCSR1_PLL_PD FIELD8(0x02) +#define RFCSR1_RX0_PD FIELD8(0x04) +#define RFCSR1_TX0_PD FIELD8(0x08) +#define RFCSR1_RX1_PD FIELD8(0x10) +#define RFCSR1_TX1_PD FIELD8(0x20) +#define RFCSR1_RX2_PD FIELD8(0x40) +#define RFCSR1_TX2_PD FIELD8(0x80) + +/* + * RFCSR 2: + */ +#define RFCSR2_RESCAL_EN FIELD8(0x80) + +/* + * RFCSR 3: + */ +#define RFCSR3_K FIELD8(0x0f) +/* Bits [7-4] for RF3320 (RT3370/RT3390), on other chipsets reserved */ +#define RFCSR3_PA1_BIAS_CCK FIELD8(0x70) +#define RFCSR3_PA2_CASCODE_BIAS_CCKK FIELD8(0x80) +/* Bits for RF3290/RF5360/RF5362/RF5370/RF5372/RF5390/RF5392 */ +#define RFCSR3_VCOCAL_EN FIELD8(0x80) +/* Bits for RF3050 */ +#define RFCSR3_BIT1 FIELD8(0x02) +#define RFCSR3_BIT2 FIELD8(0x04) +#define RFCSR3_BIT3 FIELD8(0x08) +#define RFCSR3_BIT4 FIELD8(0x10) +#define RFCSR3_BIT5 FIELD8(0x20) + +/* + * FRCSR 5: + */ +#define RFCSR5_R1 FIELD8(0x0c) + +/* + * RFCSR 6: + */ +#define RFCSR6_R1 FIELD8(0x03) +#define RFCSR6_R2 FIELD8(0x40) +#define RFCSR6_TXDIV FIELD8(0x0c) +/* bits for RF3053 */ +#define RFCSR6_VCO_IC FIELD8(0xc0) + +/* + * RFCSR 7: + */ +#define RFCSR7_RF_TUNING FIELD8(0x01) +#define RFCSR7_BIT1 FIELD8(0x02) +#define RFCSR7_BIT2 FIELD8(0x04) +#define RFCSR7_BIT3 FIELD8(0x08) +#define RFCSR7_BIT4 FIELD8(0x10) +#define RFCSR7_BIT5 FIELD8(0x20) +#define RFCSR7_BITS67 FIELD8(0xc0) + +/* + * RFCSR 9: + */ +#define RFCSR9_K FIELD8(0x0f) +#define RFCSR9_N FIELD8(0x10) +#define RFCSR9_UNKNOWN FIELD8(0x60) +#define RFCSR9_MOD FIELD8(0x80) + +/* + * RFCSR 11: + */ +#define RFCSR11_R FIELD8(0x03) +#define RFCSR11_PLL_MOD FIELD8(0x0c) +#define RFCSR11_MOD FIELD8(0xc0) +/* bits for RF3053 */ +/* TODO: verify RFCSR11_MOD usage on other chips */ +#define RFCSR11_PLL_IDOH FIELD8(0x40) + + +/* + * RFCSR 12: + */ +#define RFCSR12_TX_POWER FIELD8(0x1f) +#define RFCSR12_DR0 FIELD8(0xe0) + +/* + * RFCSR 13: + */ +#define RFCSR13_TX_POWER FIELD8(0x1f) +#define RFCSR13_DR0 FIELD8(0xe0) + +/* + * RFCSR 15: + */ +#define RFCSR15_TX_LO2_EN FIELD8(0x08) + +/* + * RFCSR 16: + */ +#define RFCSR16_TXMIXER_GAIN FIELD8(0x07) + +/* + * RFCSR 17: + */ +#define RFCSR17_TXMIXER_GAIN FIELD8(0x07) +#define RFCSR17_TX_LO1_EN FIELD8(0x08) +#define RFCSR17_R FIELD8(0x20) +#define RFCSR17_CODE FIELD8(0x7f) + +/* RFCSR 18 */ +#define RFCSR18_XO_TUNE_BYPASS FIELD8(0x40) + + +/* + * RFCSR 20: + */ +#define RFCSR20_RX_LO1_EN FIELD8(0x08) + +/* + * RFCSR 21: + */ +#define RFCSR21_RX_LO2_EN FIELD8(0x08) + +/* + * RFCSR 22: + */ +#define RFCSR22_BASEBAND_LOOPBACK FIELD8(0x01) + +/* + * RFCSR 23: + */ +#define RFCSR23_FREQ_OFFSET FIELD8(0x7f) + +/* + * RFCSR 24: + */ +#define RFCSR24_TX_AGC_FC FIELD8(0x1f) +#define RFCSR24_TX_H20M FIELD8(0x20) +#define RFCSR24_TX_CALIB FIELD8(0x7f) + +/* + * RFCSR 27: + */ +#define RFCSR27_R1 FIELD8(0x03) +#define RFCSR27_R2 FIELD8(0x04) +#define RFCSR27_R3 FIELD8(0x30) +#define RFCSR27_R4 FIELD8(0x40) + +/* + * RFCSR 29: + */ +#define RFCSR29_ADC6_TEST FIELD8(0x01) +#define RFCSR29_ADC6_INT_TEST FIELD8(0x02) +#define RFCSR29_RSSI_RESET FIELD8(0x04) +#define RFCSR29_RSSI_ON FIELD8(0x08) +#define RFCSR29_RSSI_RIP_CTRL FIELD8(0x30) +#define RFCSR29_RSSI_GAIN FIELD8(0xc0) + +/* + * RFCSR 30: + */ +#define RFCSR30_TX_H20M FIELD8(0x02) +#define RFCSR30_RX_H20M FIELD8(0x04) +#define RFCSR30_RX_VCM FIELD8(0x18) +#define RFCSR30_RF_CALIBRATION FIELD8(0x80) + +/* + * RFCSR 31: + */ +#define RFCSR31_RX_AGC_FC FIELD8(0x1f) +#define RFCSR31_RX_H20M FIELD8(0x20) +#define RFCSR31_RX_CALIB FIELD8(0x7f) + +/* RFCSR 32 bits for RF3053 */ +#define RFCSR32_TX_AGC_FC FIELD8(0xf8) + +/* RFCSR 36 bits for RF3053 */ +#define RFCSR36_RF_BS FIELD8(0x80) + +/* + * RFCSR 38: + */ +#define RFCSR38_RX_LO1_EN FIELD8(0x20) + +/* + * RFCSR 39: + */ +#define RFCSR39_RX_DIV FIELD8(0x40) +#define RFCSR39_RX_LO2_EN FIELD8(0x80) + +/* + * RFCSR 49: + */ +#define RFCSR49_TX FIELD8(0x3f) +#define RFCSR49_EP FIELD8(0xc0) +/* bits for RT3593 */ +#define RFCSR49_TX_LO1_IC FIELD8(0x1c) +#define RFCSR49_TX_DIV FIELD8(0x20) + +/* + * RFCSR 50: + */ +#define RFCSR50_TX FIELD8(0x3f) +#define RFCSR50_EP FIELD8(0xc0) +/* bits for RT3593 */ +#define RFCSR50_TX_LO1_EN FIELD8(0x20) +#define RFCSR50_TX_LO2_EN FIELD8(0x10) + +/* RFCSR 51 */ +/* bits for RT3593 */ +#define RFCSR51_BITS01 FIELD8(0x03) +#define RFCSR51_BITS24 FIELD8(0x1c) +#define RFCSR51_BITS57 FIELD8(0xe0) + +#define RFCSR53_TX_POWER FIELD8(0x3f) +#define RFCSR53_UNKNOWN FIELD8(0xc0) + +#define RFCSR54_TX_POWER FIELD8(0x3f) +#define RFCSR54_UNKNOWN FIELD8(0xc0) + +#define RFCSR55_TX_POWER FIELD8(0x3f) +#define RFCSR55_UNKNOWN FIELD8(0xc0) + +#define RFCSR57_DRV_CC FIELD8(0xfc) + + +/* + * RF registers + */ + +/* + * RF 2 + */ +#define RF2_ANTENNA_RX2 FIELD32(0x00000040) +#define RF2_ANTENNA_TX1 FIELD32(0x00004000) +#define RF2_ANTENNA_RX1 FIELD32(0x00020000) + +/* + * RF 3 + */ +#define RF3_TXPOWER_G FIELD32(0x00003e00) +#define RF3_TXPOWER_A_7DBM_BOOST FIELD32(0x00000200) +#define RF3_TXPOWER_A FIELD32(0x00003c00) + +/* + * RF 4 + */ +#define RF4_TXPOWER_G FIELD32(0x000007c0) +#define RF4_TXPOWER_A_7DBM_BOOST FIELD32(0x00000040) +#define RF4_TXPOWER_A FIELD32(0x00000780) +#define RF4_FREQ_OFFSET FIELD32(0x001f8000) +#define RF4_HT40 FIELD32(0x00200000) + +/* + * EEPROM content. + * The wordsize of the EEPROM is 16 bits. + */ + +enum rt2800_eeprom_word { + EEPROM_CHIP_ID = 0, + EEPROM_VERSION, + EEPROM_MAC_ADDR_0, + EEPROM_MAC_ADDR_1, + EEPROM_MAC_ADDR_2, + EEPROM_NIC_CONF0, + EEPROM_NIC_CONF1, + EEPROM_FREQ, + EEPROM_LED_AG_CONF, + EEPROM_LED_ACT_CONF, + EEPROM_LED_POLARITY, + EEPROM_NIC_CONF2, + EEPROM_LNA, + EEPROM_RSSI_BG, + EEPROM_RSSI_BG2, + EEPROM_TXMIXER_GAIN_BG, + EEPROM_RSSI_A, + EEPROM_RSSI_A2, + EEPROM_TXMIXER_GAIN_A, + EEPROM_EIRP_MAX_TX_POWER, + EEPROM_TXPOWER_DELTA, + EEPROM_TXPOWER_BG1, + EEPROM_TXPOWER_BG2, + EEPROM_TSSI_BOUND_BG1, + EEPROM_TSSI_BOUND_BG2, + EEPROM_TSSI_BOUND_BG3, + EEPROM_TSSI_BOUND_BG4, + EEPROM_TSSI_BOUND_BG5, + EEPROM_TXPOWER_A1, + EEPROM_TXPOWER_A2, + EEPROM_TSSI_BOUND_A1, + EEPROM_TSSI_BOUND_A2, + EEPROM_TSSI_BOUND_A3, + EEPROM_TSSI_BOUND_A4, + EEPROM_TSSI_BOUND_A5, + EEPROM_TXPOWER_BYRATE, + EEPROM_BBP_START, + + /* IDs for extended EEPROM format used by three-chain devices */ + EEPROM_EXT_LNA2, + EEPROM_EXT_TXPOWER_BG3, + EEPROM_EXT_TXPOWER_A3, + + /* New values must be added before this */ + EEPROM_WORD_COUNT +}; + +/* + * EEPROM Version + */ +#define EEPROM_VERSION_FAE FIELD16(0x00ff) +#define EEPROM_VERSION_VERSION FIELD16(0xff00) + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM NIC Configuration 0 + * RXPATH: 1: 1R, 2: 2R, 3: 3R + * TXPATH: 1: 1T, 2: 2T, 3: 3T + * RF_TYPE: RFIC type + */ +#define EEPROM_NIC_CONF0_RXPATH FIELD16(0x000f) +#define EEPROM_NIC_CONF0_TXPATH FIELD16(0x00f0) +#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00) + +/* + * EEPROM NIC Configuration 1 + * HW_RADIO: 0: disable, 1: enable + * EXTERNAL_TX_ALC: 0: disable, 1: enable + * EXTERNAL_LNA_2G: 0: disable, 1: enable + * EXTERNAL_LNA_5G: 0: disable, 1: enable + * CARDBUS_ACCEL: 0: enable, 1: disable + * BW40M_SB_2G: 0: disable, 1: enable + * BW40M_SB_5G: 0: disable, 1: enable + * WPS_PBC: 0: disable, 1: enable + * BW40M_2G: 0: enable, 1: disable + * BW40M_5G: 0: enable, 1: disable + * BROADBAND_EXT_LNA: 0: disable, 1: enable + * ANT_DIVERSITY: 00: Disable, 01: Diversity, + * 10: Main antenna, 11: Aux antenna + * INTERNAL_TX_ALC: 0: disable, 1: enable + * BT_COEXIST: 0: disable, 1: enable + * DAC_TEST: 0: disable, 1: enable + */ +#define EEPROM_NIC_CONF1_HW_RADIO FIELD16(0x0001) +#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002) +#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004) +#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008) +#define EEPROM_NIC_CONF1_CARDBUS_ACCEL FIELD16(0x0010) +#define EEPROM_NIC_CONF1_BW40M_SB_2G FIELD16(0x0020) +#define EEPROM_NIC_CONF1_BW40M_SB_5G FIELD16(0x0040) +#define EEPROM_NIC_CONF1_WPS_PBC FIELD16(0x0080) +#define EEPROM_NIC_CONF1_BW40M_2G FIELD16(0x0100) +#define EEPROM_NIC_CONF1_BW40M_5G FIELD16(0x0200) +#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400) +#define EEPROM_NIC_CONF1_ANT_DIVERSITY FIELD16(0x1800) +#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000) +#define EEPROM_NIC_CONF1_BT_COEXIST FIELD16(0x4000) +#define EEPROM_NIC_CONF1_DAC_TEST FIELD16(0x8000) + +/* + * EEPROM frequency + */ +#define EEPROM_FREQ_OFFSET FIELD16(0x00ff) +#define EEPROM_FREQ_LED_MODE FIELD16(0x7f00) +#define EEPROM_FREQ_LED_POLARITY FIELD16(0x1000) + +/* + * EEPROM LED + * POLARITY_RDY_G: Polarity RDY_G setting. + * POLARITY_RDY_A: Polarity RDY_A setting. + * POLARITY_ACT: Polarity ACT setting. + * POLARITY_GPIO_0: Polarity GPIO0 setting. + * POLARITY_GPIO_1: Polarity GPIO1 setting. + * POLARITY_GPIO_2: Polarity GPIO2 setting. + * POLARITY_GPIO_3: Polarity GPIO3 setting. + * POLARITY_GPIO_4: Polarity GPIO4 setting. + * LED_MODE: Led mode. + */ +#define EEPROM_LED_POLARITY_RDY_BG FIELD16(0x0001) +#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) +#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) +#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) +#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) +#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) +#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) +#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) +#define EEPROM_LED_LED_MODE FIELD16(0x1f00) + +/* + * EEPROM NIC Configuration 2 + * RX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream + * TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream + * CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved + */ +#define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f) +#define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0) +#define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600) + +/* + * EEPROM LNA + */ +#define EEPROM_LNA_BG FIELD16(0x00ff) +#define EEPROM_LNA_A0 FIELD16(0xff00) + +/* + * EEPROM RSSI BG offset + */ +#define EEPROM_RSSI_BG_OFFSET0 FIELD16(0x00ff) +#define EEPROM_RSSI_BG_OFFSET1 FIELD16(0xff00) + +/* + * EEPROM RSSI BG2 offset + */ +#define EEPROM_RSSI_BG2_OFFSET2 FIELD16(0x00ff) +#define EEPROM_RSSI_BG2_LNA_A1 FIELD16(0xff00) + +/* + * EEPROM TXMIXER GAIN BG offset (note overlaps with EEPROM RSSI BG2). + */ +#define EEPROM_TXMIXER_GAIN_BG_VAL FIELD16(0x0007) + +/* + * EEPROM RSSI A offset + */ +#define EEPROM_RSSI_A_OFFSET0 FIELD16(0x00ff) +#define EEPROM_RSSI_A_OFFSET1 FIELD16(0xff00) + +/* + * EEPROM RSSI A2 offset + */ +#define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff) +#define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00) + +/* + * EEPROM TXMIXER GAIN A offset (note overlaps with EEPROM RSSI A2). + */ +#define EEPROM_TXMIXER_GAIN_A_VAL FIELD16(0x0007) + +/* + * EEPROM EIRP Maximum TX power values(unit: dbm) + */ +#define EEPROM_EIRP_MAX_TX_POWER_2GHZ FIELD16(0x00ff) +#define EEPROM_EIRP_MAX_TX_POWER_5GHZ FIELD16(0xff00) + +/* + * EEPROM TXpower delta: 20MHZ AND 40 MHZ use different power. + * This is delta in 40MHZ. + * VALUE: Tx Power dalta value, MAX=4(unit: dbm) + * TYPE: 1: Plus the delta value, 0: minus the delta value + * ENABLE: enable tx power compensation for 40BW + */ +#define EEPROM_TXPOWER_DELTA_VALUE_2G FIELD16(0x003f) +#define EEPROM_TXPOWER_DELTA_TYPE_2G FIELD16(0x0040) +#define EEPROM_TXPOWER_DELTA_ENABLE_2G FIELD16(0x0080) +#define EEPROM_TXPOWER_DELTA_VALUE_5G FIELD16(0x3f00) +#define EEPROM_TXPOWER_DELTA_TYPE_5G FIELD16(0x4000) +#define EEPROM_TXPOWER_DELTA_ENABLE_5G FIELD16(0x8000) + +/* + * EEPROM TXPOWER 802.11BG + */ +#define EEPROM_TXPOWER_BG_SIZE 7 +#define EEPROM_TXPOWER_BG_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_BG_2 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11BG + * MINUS4: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -4) + * MINUS3: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -3) + */ +#define EEPROM_TSSI_BOUND_BG1_MINUS4 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_BG1_MINUS3 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11BG + * MINUS2: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -2) + * MINUS1: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -1) + */ +#define EEPROM_TSSI_BOUND_BG2_MINUS2 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_BG2_MINUS1 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11BG + * REF: Reference TSSI value, no tx power changes needed + * PLUS1: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 1) + */ +#define EEPROM_TSSI_BOUND_BG3_REF FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_BG3_PLUS1 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11BG + * PLUS2: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 2) + * PLUS3: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 3) + */ +#define EEPROM_TSSI_BOUND_BG4_PLUS2 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_BG4_PLUS3 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11BG + * PLUS4: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 4) + * AGC_STEP: Temperature compensation step. + */ +#define EEPROM_TSSI_BOUND_BG5_PLUS4 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_BG5_AGC_STEP FIELD16(0xff00) + +/* + * EEPROM TXPOWER 802.11A + */ +#define EEPROM_TXPOWER_A_SIZE 6 +#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) + +/* EEPROM_TXPOWER_{A,G} fields for RT3593 */ +#define EEPROM_TXPOWER_ALC FIELD8(0x1f) +#define EEPROM_TXPOWER_FINE_CTRL FIELD8(0xe0) + +/* + * EEPROM temperature compensation boundaries 802.11A + * MINUS4: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -4) + * MINUS3: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -3) + */ +#define EEPROM_TSSI_BOUND_A1_MINUS4 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_A1_MINUS3 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11A + * MINUS2: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -2) + * MINUS1: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -1) + */ +#define EEPROM_TSSI_BOUND_A2_MINUS2 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_A2_MINUS1 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11A + * REF: Reference TSSI value, no tx power changes needed + * PLUS1: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 1) + */ +#define EEPROM_TSSI_BOUND_A3_REF FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_A3_PLUS1 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11A + * PLUS2: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 2) + * PLUS3: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 3) + */ +#define EEPROM_TSSI_BOUND_A4_PLUS2 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_A4_PLUS3 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11A + * PLUS4: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 4) + * AGC_STEP: Temperature compensation step. + */ +#define EEPROM_TSSI_BOUND_A5_PLUS4 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_A5_AGC_STEP FIELD16(0xff00) + +/* + * EEPROM TXPOWER by rate: tx power per tx rate for HT20 mode + */ +#define EEPROM_TXPOWER_BYRATE_SIZE 9 + +#define EEPROM_TXPOWER_BYRATE_RATE0 FIELD16(0x000f) +#define EEPROM_TXPOWER_BYRATE_RATE1 FIELD16(0x00f0) +#define EEPROM_TXPOWER_BYRATE_RATE2 FIELD16(0x0f00) +#define EEPROM_TXPOWER_BYRATE_RATE3 FIELD16(0xf000) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_SIZE 16 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* EEPROM_EXT_LNA2 */ +#define EEPROM_EXT_LNA2_A1 FIELD16(0x00ff) +#define EEPROM_EXT_LNA2_A2 FIELD16(0xff00) + +/* + * EEPROM IQ Calibration, unlike other entries those are byte addresses. + */ + +#define EEPROM_IQ_GAIN_CAL_TX0_2G 0x130 +#define EEPROM_IQ_PHASE_CAL_TX0_2G 0x131 +#define EEPROM_IQ_GROUPDELAY_CAL_TX0_2G 0x132 +#define EEPROM_IQ_GAIN_CAL_TX1_2G 0x133 +#define EEPROM_IQ_PHASE_CAL_TX1_2G 0x134 +#define EEPROM_IQ_GROUPDELAY_CAL_TX1_2G 0x135 +#define EEPROM_IQ_GAIN_CAL_RX0_2G 0x136 +#define EEPROM_IQ_PHASE_CAL_RX0_2G 0x137 +#define EEPROM_IQ_GROUPDELAY_CAL_RX0_2G 0x138 +#define EEPROM_IQ_GAIN_CAL_RX1_2G 0x139 +#define EEPROM_IQ_PHASE_CAL_RX1_2G 0x13A +#define EEPROM_IQ_GROUPDELAY_CAL_RX1_2G 0x13B +#define EEPROM_RF_IQ_COMPENSATION_CONTROL 0x13C +#define EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CONTROL 0x13D +#define EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5G 0x144 +#define EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5G 0x145 +#define EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5G 0X146 +#define EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5G 0x147 +#define EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5G 0x148 +#define EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5G 0x149 +#define EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5G 0x14A +#define EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5G 0x14B +#define EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5G 0X14C +#define EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5G 0x14D +#define EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5G 0x14E +#define EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5G 0x14F +#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH36_TO_CH64_5G 0x150 +#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH36_TO_CH64_5G 0x151 +#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH100_TO_CH138_5G 0x152 +#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH100_TO_CH138_5G 0x153 +#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH140_TO_CH165_5G 0x154 +#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH140_TO_CH165_5G 0x155 +#define EEPROM_IQ_GAIN_CAL_RX0_CH36_TO_CH64_5G 0x156 +#define EEPROM_IQ_PHASE_CAL_RX0_CH36_TO_CH64_5G 0x157 +#define EEPROM_IQ_GAIN_CAL_RX0_CH100_TO_CH138_5G 0X158 +#define EEPROM_IQ_PHASE_CAL_RX0_CH100_TO_CH138_5G 0x159 +#define EEPROM_IQ_GAIN_CAL_RX0_CH140_TO_CH165_5G 0x15A +#define EEPROM_IQ_PHASE_CAL_RX0_CH140_TO_CH165_5G 0x15B +#define EEPROM_IQ_GAIN_CAL_RX1_CH36_TO_CH64_5G 0x15C +#define EEPROM_IQ_PHASE_CAL_RX1_CH36_TO_CH64_5G 0x15D +#define EEPROM_IQ_GAIN_CAL_RX1_CH100_TO_CH138_5G 0X15E +#define EEPROM_IQ_PHASE_CAL_RX1_CH100_TO_CH138_5G 0x15F +#define EEPROM_IQ_GAIN_CAL_RX1_CH140_TO_CH165_5G 0x160 +#define EEPROM_IQ_PHASE_CAL_RX1_CH140_TO_CH165_5G 0x161 +#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH36_TO_CH64_5G 0x162 +#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH36_TO_CH64_5G 0x163 +#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH100_TO_CH138_5G 0x164 +#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH100_TO_CH138_5G 0x165 +#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH140_TO_CH165_5G 0x166 +#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH140_TO_CH165_5G 0x167 + +/* + * MCU mailbox commands. + * MCU_SLEEP - go to power-save mode. + * arg1: 1: save as much power as possible, 0: save less power. + * status: 1: success, 2: already asleep, + * 3: maybe MAC is busy so can't finish this task. + * MCU_RADIO_OFF + * arg0: 0: do power-saving, NOT turn off radio. + */ +#define MCU_SLEEP 0x30 +#define MCU_WAKEUP 0x31 +#define MCU_RADIO_OFF 0x35 +#define MCU_CURRENT 0x36 +#define MCU_LED 0x50 +#define MCU_LED_STRENGTH 0x51 +#define MCU_LED_AG_CONF 0x52 +#define MCU_LED_ACT_CONF 0x53 +#define MCU_LED_LED_POLARITY 0x54 +#define MCU_RADAR 0x60 +#define MCU_BOOT_SIGNAL 0x72 +#define MCU_ANT_SELECT 0X73 +#define MCU_FREQ_OFFSET 0x74 +#define MCU_BBP_SIGNAL 0x80 +#define MCU_POWER_SAVE 0x83 +#define MCU_BAND_SELECT 0x91 + +/* + * MCU mailbox tokens + */ +#define TOKEN_SLEEP 1 +#define TOKEN_RADIO_OFF 2 +#define TOKEN_WAKEUP 3 + + +/* + * DMA descriptor defines. + */ + +#define TXWI_DESC_SIZE_4WORDS (4 * sizeof(__le32)) +#define TXWI_DESC_SIZE_5WORDS (5 * sizeof(__le32)) + +#define RXWI_DESC_SIZE_4WORDS (4 * sizeof(__le32)) +#define RXWI_DESC_SIZE_5WORDS (5 * sizeof(__le32)) +#define RXWI_DESC_SIZE_6WORDS (6 * sizeof(__le32)) + +/* + * TX WI structure + */ + +/* + * Word0 + * FRAG: 1 To inform TKIP engine this is a fragment. + * MIMO_PS: The remote peer is in dynamic MIMO-PS mode + * TX_OP: 0:HT TXOP rule , 1:PIFS TX ,2:Backoff, 3:sifs + * BW: Channel bandwidth 0:20MHz, 1:40 MHz (for legacy rates this will + * duplicate the frame to both channels). + * STBC: 1: STBC support MCS =0-7, 2,3 : RESERVED + * AMPDU: 1: this frame is eligible for AMPDU aggregation, the hw will + * aggregate consecutive frames with the same RA and QoS TID. If + * a frame A with the same RA and QoS TID but AMPDU=0 is queued + * directly after a frame B with AMPDU=1, frame A might still + * get aggregated into the AMPDU started by frame B. So, setting + * AMPDU to 0 does _not_ necessarily mean the frame is sent as + * MPDU, it can still end up in an AMPDU if the previous frame + * was tagged as AMPDU. + */ +#define TXWI_W0_FRAG FIELD32(0x00000001) +#define TXWI_W0_MIMO_PS FIELD32(0x00000002) +#define TXWI_W0_CF_ACK FIELD32(0x00000004) +#define TXWI_W0_TS FIELD32(0x00000008) +#define TXWI_W0_AMPDU FIELD32(0x00000010) +#define TXWI_W0_MPDU_DENSITY FIELD32(0x000000e0) +#define TXWI_W0_TX_OP FIELD32(0x00000300) +#define TXWI_W0_MCS FIELD32(0x007f0000) +#define TXWI_W0_BW FIELD32(0x00800000) +#define TXWI_W0_SHORT_GI FIELD32(0x01000000) +#define TXWI_W0_STBC FIELD32(0x06000000) +#define TXWI_W0_IFS FIELD32(0x08000000) +#define TXWI_W0_PHYMODE FIELD32(0xc0000000) + +/* + * Word1 + * ACK: 0: No Ack needed, 1: Ack needed + * NSEQ: 0: Don't assign hw sequence number, 1: Assign hw sequence number + * BW_WIN_SIZE: BA windows size of the recipient + * WIRELESS_CLI_ID: Client ID for WCID table access + * MPDU_TOTAL_BYTE_COUNT: Length of 802.11 frame + * PACKETID: Will be latched into the TX_STA_FIFO register once the according + * frame was processed. If multiple frames are aggregated together + * (AMPDU==1) the reported tx status will always contain the packet + * id of the first frame. 0: Don't report tx status for this frame. + * PACKETID_QUEUE: Part of PACKETID, This is the queue index (0-3) + * PACKETID_ENTRY: Part of PACKETID, THis is the queue entry index (1-3) + * This identification number is calculated by ((idx % 3) + 1). + * The (+1) is required to prevent PACKETID to become 0. + */ +#define TXWI_W1_ACK FIELD32(0x00000001) +#define TXWI_W1_NSEQ FIELD32(0x00000002) +#define TXWI_W1_BW_WIN_SIZE FIELD32(0x000000fc) +#define TXWI_W1_WIRELESS_CLI_ID FIELD32(0x0000ff00) +#define TXWI_W1_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) +#define TXWI_W1_PACKETID FIELD32(0xf0000000) +#define TXWI_W1_PACKETID_QUEUE FIELD32(0x30000000) +#define TXWI_W1_PACKETID_ENTRY FIELD32(0xc0000000) + +/* + * Word2 + */ +#define TXWI_W2_IV FIELD32(0xffffffff) + +/* + * Word3 + */ +#define TXWI_W3_EIV FIELD32(0xffffffff) + +/* + * RX WI structure + */ + +/* + * Word0 + */ +#define RXWI_W0_WIRELESS_CLI_ID FIELD32(0x000000ff) +#define RXWI_W0_KEY_INDEX FIELD32(0x00000300) +#define RXWI_W0_BSSID FIELD32(0x00001c00) +#define RXWI_W0_UDF FIELD32(0x0000e000) +#define RXWI_W0_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) +#define RXWI_W0_TID FIELD32(0xf0000000) + +/* + * Word1 + */ +#define RXWI_W1_FRAG FIELD32(0x0000000f) +#define RXWI_W1_SEQUENCE FIELD32(0x0000fff0) +#define RXWI_W1_MCS FIELD32(0x007f0000) +#define RXWI_W1_BW FIELD32(0x00800000) +#define RXWI_W1_SHORT_GI FIELD32(0x01000000) +#define RXWI_W1_STBC FIELD32(0x06000000) +#define RXWI_W1_PHYMODE FIELD32(0xc0000000) + +/* + * Word2 + */ +#define RXWI_W2_RSSI0 FIELD32(0x000000ff) +#define RXWI_W2_RSSI1 FIELD32(0x0000ff00) +#define RXWI_W2_RSSI2 FIELD32(0x00ff0000) + +/* + * Word3 + */ +#define RXWI_W3_SNR0 FIELD32(0x000000ff) +#define RXWI_W3_SNR1 FIELD32(0x0000ff00) + +/* + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. + */ +#define MIN_G_TXPOWER 0 +#define MIN_A_TXPOWER -7 +#define MAX_G_TXPOWER 31 +#define MAX_A_TXPOWER 15 +#define DEFAULT_TXPOWER 5 + +#define MIN_A_TXPOWER_3593 0 +#define MAX_A_TXPOWER_3593 31 + +#define TXPOWER_G_FROM_DEV(__txpower) \ + ((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_A_FROM_DEV(__txpower) \ + ((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +/* + * Board's maximun TX power limitation + */ +#define EIRP_MAX_TX_POWER_LIMIT 0x50 + +/* + * Number of TBTT intervals after which we have to adjust + * the hw beacon timer. + */ +#define BCN_TBTT_OFFSET 64 + +/* + * Hardware has 255 WCID table entries. First 32 entries are reserved for + * shared keys. Since parts of the pairwise key table might be shared with + * the beacon frame buffers 6 & 7 we could only use the first 222 entries. + */ +#define WCID_START 33 +#define WCID_END 222 +#define STA_IDS_SIZE (WCID_END - WCID_START + 2) + +/* + * RT2800 driver data structure + */ +struct rt2800_drv_data { + u8 calibration_bw20; + u8 calibration_bw40; + u8 bbp25; + u8 bbp26; + u8 txmixer_gain_24g; + u8 txmixer_gain_5g; + unsigned int tbtt_tick; + DECLARE_BITMAP(sta_ids, STA_IDS_SIZE); +}; + +#endif /* RT2800_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c new file mode 100644 index 000000000000..9733b31a780d --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -0,0 +1,8021 @@ +/* + Copyright (C) 2010 Willow Garage + Copyright (C) 2010 Ivo van Doorn + Copyright (C) 2009 Bartlomiej Zolnierkiewicz + Copyright (C) 2009 Gertjan van Wingerde + + Based on the original rt2800pci.c and rt2800usb.c. + Copyright (C) 2009 Alban Browaeys + Copyright (C) 2009 Felix Fietkau + Copyright (C) 2009 Luis Correia + Copyright (C) 2009 Mattias Nissler + Copyright (C) 2009 Mark Asselstine + Copyright (C) 2009 Xose Vazquez Perez + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2800lib + Abstract: rt2800 generic device routines. + */ + +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2800lib.h" +#include "rt2800.h" + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2800_register_read and rt2800_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers BBPCSR and RFCSR to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attampt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + * The _lock versions must be used if you already hold the csr_mutex + */ +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2800_regbusy_read((__dev), BBP_CSR_CFG, BBP_CSR_CFG_BUSY, (__reg)) +#define WAIT_FOR_RFCSR(__dev, __reg) \ + rt2800_regbusy_read((__dev), RF_CSR_CFG, RF_CSR_CFG_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2800_regbusy_read((__dev), RF_CSR_CFG0, RF_CSR_CFG0_BUSY, (__reg)) +#define WAIT_FOR_MCU(__dev, __reg) \ + rt2800_regbusy_read((__dev), H2M_MAILBOX_CSR, \ + H2M_MAILBOX_CSR_OWNER, (__reg)) + +static inline bool rt2800_is_305x_soc(struct rt2x00_dev *rt2x00dev) +{ + /* check for rt2872 on SoC */ + if (!rt2x00_is_soc(rt2x00dev) || + !rt2x00_rt(rt2x00dev, RT2872)) + return false; + + /* we know for sure that these rf chipsets are used on rt305x boards */ + if (rt2x00_rf(rt2x00dev, RF3020) || + rt2x00_rf(rt2x00dev, RF3021) || + rt2x00_rf(rt2x00dev, RF3022)) + return true; + + rt2x00_warn(rt2x00dev, "Unknown RF chipset on rt305x\n"); + return false; +} + +static void rt2800_bbp_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBP_CSR_CFG_VALUE, value); + rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); + rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 0); + rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); + + rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_bbp_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u8 *value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); + rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 1); + rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); + + rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); + + WAIT_FOR_BBP(rt2x00dev, ®); + } + + *value = rt2x00_get_field32(reg, BBP_CSR_CFG_VALUE); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_rfcsr_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RFCSR becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG_DATA, value); + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 1); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_rfcsr_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u8 *value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RFCSR becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. + */ + if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 0); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + + WAIT_FOR_RFCSR(rt2x00dev, ®); + } + + *value = rt2x00_get_field32(reg, RF_CSR_CFG_DATA); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u32 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG0_REG_VALUE_BW, value); + rt2x00_set_field32(®, RF_CSR_CFG0_STANDBYMODE, 0); + rt2x00_set_field32(®, RF_CSR_CFG0_SEL, 0); + rt2x00_set_field32(®, RF_CSR_CFG0_BUSY, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG0, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = { + [EEPROM_CHIP_ID] = 0x0000, + [EEPROM_VERSION] = 0x0001, + [EEPROM_MAC_ADDR_0] = 0x0002, + [EEPROM_MAC_ADDR_1] = 0x0003, + [EEPROM_MAC_ADDR_2] = 0x0004, + [EEPROM_NIC_CONF0] = 0x001a, + [EEPROM_NIC_CONF1] = 0x001b, + [EEPROM_FREQ] = 0x001d, + [EEPROM_LED_AG_CONF] = 0x001e, + [EEPROM_LED_ACT_CONF] = 0x001f, + [EEPROM_LED_POLARITY] = 0x0020, + [EEPROM_NIC_CONF2] = 0x0021, + [EEPROM_LNA] = 0x0022, + [EEPROM_RSSI_BG] = 0x0023, + [EEPROM_RSSI_BG2] = 0x0024, + [EEPROM_TXMIXER_GAIN_BG] = 0x0024, /* overlaps with RSSI_BG2 */ + [EEPROM_RSSI_A] = 0x0025, + [EEPROM_RSSI_A2] = 0x0026, + [EEPROM_TXMIXER_GAIN_A] = 0x0026, /* overlaps with RSSI_A2 */ + [EEPROM_EIRP_MAX_TX_POWER] = 0x0027, + [EEPROM_TXPOWER_DELTA] = 0x0028, + [EEPROM_TXPOWER_BG1] = 0x0029, + [EEPROM_TXPOWER_BG2] = 0x0030, + [EEPROM_TSSI_BOUND_BG1] = 0x0037, + [EEPROM_TSSI_BOUND_BG2] = 0x0038, + [EEPROM_TSSI_BOUND_BG3] = 0x0039, + [EEPROM_TSSI_BOUND_BG4] = 0x003a, + [EEPROM_TSSI_BOUND_BG5] = 0x003b, + [EEPROM_TXPOWER_A1] = 0x003c, + [EEPROM_TXPOWER_A2] = 0x0053, + [EEPROM_TSSI_BOUND_A1] = 0x006a, + [EEPROM_TSSI_BOUND_A2] = 0x006b, + [EEPROM_TSSI_BOUND_A3] = 0x006c, + [EEPROM_TSSI_BOUND_A4] = 0x006d, + [EEPROM_TSSI_BOUND_A5] = 0x006e, + [EEPROM_TXPOWER_BYRATE] = 0x006f, + [EEPROM_BBP_START] = 0x0078, +}; + +static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = { + [EEPROM_CHIP_ID] = 0x0000, + [EEPROM_VERSION] = 0x0001, + [EEPROM_MAC_ADDR_0] = 0x0002, + [EEPROM_MAC_ADDR_1] = 0x0003, + [EEPROM_MAC_ADDR_2] = 0x0004, + [EEPROM_NIC_CONF0] = 0x001a, + [EEPROM_NIC_CONF1] = 0x001b, + [EEPROM_NIC_CONF2] = 0x001c, + [EEPROM_EIRP_MAX_TX_POWER] = 0x0020, + [EEPROM_FREQ] = 0x0022, + [EEPROM_LED_AG_CONF] = 0x0023, + [EEPROM_LED_ACT_CONF] = 0x0024, + [EEPROM_LED_POLARITY] = 0x0025, + [EEPROM_LNA] = 0x0026, + [EEPROM_EXT_LNA2] = 0x0027, + [EEPROM_RSSI_BG] = 0x0028, + [EEPROM_RSSI_BG2] = 0x0029, + [EEPROM_RSSI_A] = 0x002a, + [EEPROM_RSSI_A2] = 0x002b, + [EEPROM_TXPOWER_BG1] = 0x0030, + [EEPROM_TXPOWER_BG2] = 0x0037, + [EEPROM_EXT_TXPOWER_BG3] = 0x003e, + [EEPROM_TSSI_BOUND_BG1] = 0x0045, + [EEPROM_TSSI_BOUND_BG2] = 0x0046, + [EEPROM_TSSI_BOUND_BG3] = 0x0047, + [EEPROM_TSSI_BOUND_BG4] = 0x0048, + [EEPROM_TSSI_BOUND_BG5] = 0x0049, + [EEPROM_TXPOWER_A1] = 0x004b, + [EEPROM_TXPOWER_A2] = 0x0065, + [EEPROM_EXT_TXPOWER_A3] = 0x007f, + [EEPROM_TSSI_BOUND_A1] = 0x009a, + [EEPROM_TSSI_BOUND_A2] = 0x009b, + [EEPROM_TSSI_BOUND_A3] = 0x009c, + [EEPROM_TSSI_BOUND_A4] = 0x009d, + [EEPROM_TSSI_BOUND_A5] = 0x009e, + [EEPROM_TXPOWER_BYRATE] = 0x00a0, +}; + +static unsigned int rt2800_eeprom_word_index(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word) +{ + const unsigned int *map; + unsigned int index; + + if (WARN_ONCE(word >= EEPROM_WORD_COUNT, + "%s: invalid EEPROM word %d\n", + wiphy_name(rt2x00dev->hw->wiphy), word)) + return 0; + + if (rt2x00_rt(rt2x00dev, RT3593)) + map = rt2800_eeprom_map_ext; + else + map = rt2800_eeprom_map; + + index = map[word]; + + /* Index 0 is valid only for EEPROM_CHIP_ID. + * Otherwise it means that the offset of the + * given word is not initialized in the map, + * or that the field is not usable on the + * actual chipset. + */ + WARN_ONCE(word != EEPROM_CHIP_ID && index == 0, + "%s: invalid access of EEPROM word %d\n", + wiphy_name(rt2x00dev->hw->wiphy), word); + + return index; +} + +static void *rt2800_eeprom_addr(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, word); + return rt2x00_eeprom_addr(rt2x00dev, index); +} + +static void rt2800_eeprom_read(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word, u16 *data) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, word); + rt2x00_eeprom_read(rt2x00dev, index, data); +} + +static void rt2800_eeprom_write(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word, u16 data) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, word); + rt2x00_eeprom_write(rt2x00dev, index, data); +} + +static void rt2800_eeprom_read_from_array(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word array, + unsigned int offset, + u16 *data) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, array); + rt2x00_eeprom_read(rt2x00dev, index + offset, data); +} + +static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + int i, count; + + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + if (rt2x00_get_field32(reg, WLAN_EN)) + return 0; + + rt2x00_set_field32(®, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff); + rt2x00_set_field32(®, FRC_WL_ANT_SET, 1); + rt2x00_set_field32(®, WLAN_CLK_EN, 0); + rt2x00_set_field32(®, WLAN_EN, 1); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + + udelay(REGISTER_BUSY_DELAY); + + count = 0; + do { + /* + * Check PLL_LD & XTAL_RDY. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, CMB_CTRL, ®); + if (rt2x00_get_field32(reg, PLL_LD) && + rt2x00_get_field32(reg, XTAL_RDY)) + break; + udelay(REGISTER_BUSY_DELAY); + } + + if (i >= REGISTER_BUSY_COUNT) { + + if (count >= 10) + return -EIO; + + rt2800_register_write(rt2x00dev, 0x58, 0x018); + udelay(REGISTER_BUSY_DELAY); + rt2800_register_write(rt2x00dev, 0x58, 0x418); + udelay(REGISTER_BUSY_DELAY); + rt2800_register_write(rt2x00dev, 0x58, 0x618); + udelay(REGISTER_BUSY_DELAY); + count++; + } else { + count = 0; + } + + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 0); + rt2x00_set_field32(®, WLAN_CLK_EN, 1); + rt2x00_set_field32(®, WLAN_RESET, 1); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + udelay(10); + rt2x00_set_field32(®, WLAN_RESET, 0); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + udelay(10); + rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff); + } while (count != 0); + + return 0; +} + +void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, + const u8 command, const u8 token, + const u8 arg0, const u8 arg1) +{ + u32 reg; + + /* + * SOC devices don't support MCU requests. + */ + if (rt2x00_is_soc(rt2x00dev)) + return; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the MCU becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_MCU(rt2x00dev, ®)) { + rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); + rt2800_register_write_lock(rt2x00dev, H2M_MAILBOX_CSR, reg); + + reg = 0; + rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); + rt2800_register_write_lock(rt2x00dev, HOST_CMD_CSR, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} +EXPORT_SYMBOL_GPL(rt2800_mcu_request); + +int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i = 0; + u32 reg; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, MAC_CSR0, ®); + if (reg && reg != ~0) + return 0; + msleep(1); + } + + rt2x00_err(rt2x00dev, "Unstable hardware\n"); + return -EBUSY; +} +EXPORT_SYMBOL_GPL(rt2800_wait_csr_ready); + +int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u32 reg; + + /* + * Some devices are really slow to respond here. Wait a whole second + * before timing out. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && + !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) + return 0; + + msleep(10); + } + + rt2x00_err(rt2x00dev, "WPDMA TX/RX busy [0x%08x]\n", reg); + return -EACCES; +} +EXPORT_SYMBOL_GPL(rt2800_wait_wpdma_ready); + +void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); +} +EXPORT_SYMBOL_GPL(rt2800_disable_wpdma); + +void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev, + unsigned short *txwi_size, + unsigned short *rxwi_size) +{ + switch (rt2x00dev->chip.rt) { + case RT3593: + *txwi_size = TXWI_DESC_SIZE_4WORDS; + *rxwi_size = RXWI_DESC_SIZE_5WORDS; + break; + + case RT5592: + *txwi_size = TXWI_DESC_SIZE_5WORDS; + *rxwi_size = RXWI_DESC_SIZE_6WORDS; + break; + + default: + *txwi_size = TXWI_DESC_SIZE_4WORDS; + *rxwi_size = RXWI_DESC_SIZE_4WORDS; + break; + } +} +EXPORT_SYMBOL_GPL(rt2800_get_txwi_rxwi_size); + +static bool rt2800_check_firmware_crc(const u8 *data, const size_t len) +{ + u16 fw_crc; + u16 crc; + + /* + * The last 2 bytes in the firmware array are the crc checksum itself, + * this means that we should never pass those 2 bytes to the crc + * algorithm. + */ + fw_crc = (data[len - 2] << 8 | data[len - 1]); + + /* + * Use the crc ccitt algorithm. + * This will return the same value as the legacy driver which + * used bit ordering reversion on the both the firmware bytes + * before input input as well as on the final output. + * Obviously using crc ccitt directly is much more efficient. + */ + crc = crc_ccitt(~0, data, len - 2); + + /* + * There is a small difference between the crc-itu-t + bitrev and + * the crc-ccitt crc calculation. In the latter method the 2 bytes + * will be swapped, use swab16 to convert the crc to the correct + * value. + */ + crc = swab16(crc); + + return fw_crc == crc; +} + +int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + size_t offset = 0; + size_t fw_len; + bool multiple; + + /* + * PCI(e) & SOC devices require firmware with a length + * of 8kb. USB devices require firmware files with a length + * of 4kb. Certain USB chipsets however require different firmware, + * which Ralink only provides attached to the original firmware + * file. Thus for USB devices, firmware files have a length + * which is a multiple of 4kb. The firmware for rt3290 chip also + * have a length which is a multiple of 4kb. + */ + if (rt2x00_is_usb(rt2x00dev) || rt2x00_rt(rt2x00dev, RT3290)) + fw_len = 4096; + else + fw_len = 8192; + + multiple = true; + /* + * Validate the firmware length + */ + if (len != fw_len && (!multiple || (len % fw_len) != 0)) + return FW_BAD_LENGTH; + + /* + * Check if the chipset requires one of the upper parts + * of the firmware. + */ + if (rt2x00_is_usb(rt2x00dev) && + !rt2x00_rt(rt2x00dev, RT2860) && + !rt2x00_rt(rt2x00dev, RT2872) && + !rt2x00_rt(rt2x00dev, RT3070) && + ((len / fw_len) == 1)) + return FW_BAD_VERSION; + + /* + * 8kb firmware files must be checked as if it were + * 2 separate firmware files. + */ + while (offset < len) { + if (!rt2800_check_firmware_crc(data + offset, fw_len)) + return FW_BAD_CRC; + + offset += fw_len; + } + + return FW_OK; +} +EXPORT_SYMBOL_GPL(rt2800_check_firmware); + +int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + unsigned int i; + u32 reg; + int retval; + + if (rt2x00_rt(rt2x00dev, RT3290)) { + retval = rt2800_enable_wlan_rt3290(rt2x00dev); + if (retval) + return -EBUSY; + } + + /* + * If driver doesn't wake up firmware here, + * rt2800_load_firmware will hang forever when interface is up again. + */ + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000); + + /* + * Wait for stable hardware. + */ + if (rt2800_wait_csr_ready(rt2x00dev)) + return -EBUSY; + + if (rt2x00_is_pci(rt2x00dev)) { + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) { + rt2800_register_read(rt2x00dev, AUX_CTRL, ®); + rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); + rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); + rt2800_register_write(rt2x00dev, AUX_CTRL, reg); + } + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002); + } + + rt2800_disable_wpdma(rt2x00dev); + + /* + * Write firmware to the device. + */ + rt2800_drv_write_firmware(rt2x00dev, data, len); + + /* + * Wait for device to stabilize. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, ®); + if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY)) + break; + msleep(1); + } + + if (i == REGISTER_BUSY_COUNT) { + rt2x00_err(rt2x00dev, "PBF system register not ready\n"); + return -EBUSY; + } + + /* + * Disable DMA, will be reenabled later when enabling + * the radio. + */ + rt2800_disable_wpdma(rt2x00dev); + + /* + * Initialize firmware. + */ + rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + if (rt2x00_is_usb(rt2x00dev)) { + rt2800_register_write(rt2x00dev, H2M_INT_SRC, 0); + rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0); + } + msleep(1); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_load_firmware); + +void rt2800_write_tx_data(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + __le32 *txwi = rt2800_drv_get_txwi(entry); + u32 word; + int i; + + /* + * Initialize TX Info descriptor + */ + rt2x00_desc_read(txwi, 0, &word); + rt2x00_set_field32(&word, TXWI_W0_FRAG, + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, + test_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0); + rt2x00_set_field32(&word, TXWI_W0_TS, + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_AMPDU, + test_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_MPDU_DENSITY, + txdesc->u.ht.mpdu_density); + rt2x00_set_field32(&word, TXWI_W0_TX_OP, txdesc->u.ht.txop); + rt2x00_set_field32(&word, TXWI_W0_MCS, txdesc->u.ht.mcs); + rt2x00_set_field32(&word, TXWI_W0_BW, + test_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_SHORT_GI, + test_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_STBC, txdesc->u.ht.stbc); + rt2x00_set_field32(&word, TXWI_W0_PHYMODE, txdesc->rate_mode); + rt2x00_desc_write(txwi, 0, word); + + rt2x00_desc_read(txwi, 1, &word); + rt2x00_set_field32(&word, TXWI_W1_ACK, + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W1_NSEQ, + test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->u.ht.ba_size); + rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID, + test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ? + txdesc->key_idx : txdesc->u.ht.wcid); + rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT, + txdesc->length); + rt2x00_set_field32(&word, TXWI_W1_PACKETID_QUEUE, entry->queue->qid); + rt2x00_set_field32(&word, TXWI_W1_PACKETID_ENTRY, (entry->entry_idx % 3) + 1); + rt2x00_desc_write(txwi, 1, word); + + /* + * Always write 0 to IV/EIV fields (word 2 and 3), hardware will insert + * the IV from the IVEIV register when TXD_W3_WIV is set to 0. + * When TXD_W3_WIV is set to 1 it will use the IV data + * from the descriptor. The TXWI_W1_WIRELESS_CLI_ID indicates which + * crypto entry in the registers should be used to encrypt the frame. + * + * Nulify all remaining words as well, we don't know how to program them. + */ + for (i = 2; i < entry->queue->winfo_size / sizeof(__le32); i++) + _rt2x00_desc_write(txwi, i, 0); +} +EXPORT_SYMBOL_GPL(rt2800_write_tx_data); + +static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2) +{ + s8 rssi0 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI0); + s8 rssi1 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI1); + s8 rssi2 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI2); + u16 eeprom; + u8 offset0; + u8 offset1; + u8 offset2; + + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom); + offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0); + offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1); + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); + offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_OFFSET2); + } else { + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom); + offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET0); + offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET1); + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); + offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_OFFSET2); + } + + /* + * Convert the value from the descriptor into the RSSI value + * If the value in the descriptor is 0, it is considered invalid + * and the default (extremely low) rssi value is assumed + */ + rssi0 = (rssi0) ? (-12 - offset0 - rt2x00dev->lna_gain - rssi0) : -128; + rssi1 = (rssi1) ? (-12 - offset1 - rt2x00dev->lna_gain - rssi1) : -128; + rssi2 = (rssi2) ? (-12 - offset2 - rt2x00dev->lna_gain - rssi2) : -128; + + /* + * mac80211 only accepts a single RSSI value. Calculating the + * average doesn't deliver a fair answer either since -60:-60 would + * be considered equally good as -50:-70 while the second is the one + * which gives less energy... + */ + rssi0 = max(rssi0, rssi1); + return (int)max(rssi0, rssi2); +} + +void rt2800_process_rxwi(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + __le32 *rxwi = (__le32 *) entry->skb->data; + u32 word; + + rt2x00_desc_read(rxwi, 0, &word); + + rxdesc->cipher = rt2x00_get_field32(word, RXWI_W0_UDF); + rxdesc->size = rt2x00_get_field32(word, RXWI_W0_MPDU_TOTAL_BYTE_COUNT); + + rt2x00_desc_read(rxwi, 1, &word); + + if (rt2x00_get_field32(word, RXWI_W1_SHORT_GI)) + rxdesc->flags |= RX_FLAG_SHORT_GI; + + if (rt2x00_get_field32(word, RXWI_W1_BW)) + rxdesc->flags |= RX_FLAG_40MHZ; + + /* + * Detect RX rate, always use MCS as signal type. + */ + rxdesc->dev_flags |= RXDONE_SIGNAL_MCS; + rxdesc->signal = rt2x00_get_field32(word, RXWI_W1_MCS); + rxdesc->rate_mode = rt2x00_get_field32(word, RXWI_W1_PHYMODE); + + /* + * Mask of 0x8 bit to remove the short preamble flag. + */ + if (rxdesc->rate_mode == RATE_MODE_CCK) + rxdesc->signal &= ~0x8; + + rt2x00_desc_read(rxwi, 2, &word); + + /* + * Convert descriptor AGC value to RSSI value. + */ + rxdesc->rssi = rt2800_agc_to_rssi(entry->queue->rt2x00dev, word); + /* + * Remove RXWI descriptor from start of the buffer. + */ + skb_pull(entry->skb, entry->queue->winfo_size); +} +EXPORT_SYMBOL_GPL(rt2800_process_rxwi); + +void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct txdone_entry_desc txdesc; + u32 word; + u16 mcs, real_mcs; + int aggr, ampdu; + + /* + * Obtain the status about this packet. + */ + txdesc.flags = 0; + rt2x00_desc_read(txwi, 0, &word); + + mcs = rt2x00_get_field32(word, TXWI_W0_MCS); + ampdu = rt2x00_get_field32(word, TXWI_W0_AMPDU); + + real_mcs = rt2x00_get_field32(status, TX_STA_FIFO_MCS); + aggr = rt2x00_get_field32(status, TX_STA_FIFO_TX_AGGRE); + + /* + * If a frame was meant to be sent as a single non-aggregated MPDU + * but ended up in an aggregate the used tx rate doesn't correlate + * with the one specified in the TXWI as the whole aggregate is sent + * with the same rate. + * + * For example: two frames are sent to rt2x00, the first one sets + * AMPDU=1 and requests MCS7 whereas the second frame sets AMDPU=0 + * and requests MCS15. If the hw aggregates both frames into one + * AMDPU the tx status for both frames will contain MCS7 although + * the frame was sent successfully. + * + * Hence, replace the requested rate with the real tx rate to not + * confuse the rate control algortihm by providing clearly wrong + * data. + */ + if (unlikely(aggr == 1 && ampdu == 0 && real_mcs != mcs)) { + skbdesc->tx_rate_idx = real_mcs; + mcs = real_mcs; + } + + if (aggr == 1 || ampdu == 1) + __set_bit(TXDONE_AMPDU, &txdesc.flags); + + /* + * Ralink has a retry mechanism using a global fallback + * table. We setup this fallback table to try the immediate + * lower rate for all rates. In the TX_STA_FIFO, the MCS field + * always contains the MCS used for the last transmission, be + * it successful or not. + */ + if (rt2x00_get_field32(status, TX_STA_FIFO_TX_SUCCESS)) { + /* + * Transmission succeeded. The number of retries is + * mcs - real_mcs + */ + __set_bit(TXDONE_SUCCESS, &txdesc.flags); + txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0); + } else { + /* + * Transmission failed. The number of retries is + * always 7 in this case (for a total number of 8 + * frames sent). + */ + __set_bit(TXDONE_FAILURE, &txdesc.flags); + txdesc.retry = rt2x00dev->long_retry; + } + + /* + * the frame was retried at least once + * -> hw used fallback rates + */ + if (txdesc.retry) + __set_bit(TXDONE_FALLBACK, &txdesc.flags); + + rt2x00lib_txdone(entry, &txdesc); +} +EXPORT_SYMBOL_GPL(rt2800_txdone_entry); + +static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev, + unsigned int index) +{ + return HW_BEACON_BASE(index); +} + +static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev, + unsigned int index) +{ + return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index)); +} + +static void rt2800_update_beacons_setup(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue = rt2x00dev->bcn; + struct queue_entry *entry; + int i, bcn_num = 0; + u64 off, reg = 0; + u32 bssid_dw1; + + /* + * Setup offsets of all active beacons in BCN_OFFSET{0,1} registers. + */ + for (i = 0; i < queue->limit; i++) { + entry = &queue->entries[i]; + if (!test_bit(ENTRY_BCN_ENABLED, &entry->flags)) + continue; + off = rt2800_get_beacon_offset(rt2x00dev, entry->entry_idx); + reg |= off << (8 * bcn_num); + bcn_num++; + } + + WARN_ON_ONCE(bcn_num != rt2x00dev->intf_beaconing); + + rt2800_register_write(rt2x00dev, BCN_OFFSET0, (u32) reg); + rt2800_register_write(rt2x00dev, BCN_OFFSET1, (u32) (reg >> 32)); + + /* + * H/W sends up to MAC_BSSID_DW1_BSS_BCN_NUM + 1 consecutive beacons. + */ + rt2800_register_read(rt2x00dev, MAC_BSSID_DW1, &bssid_dw1); + rt2x00_set_field32(&bssid_dw1, MAC_BSSID_DW1_BSS_BCN_NUM, + bcn_num > 0 ? bcn_num - 1 : 0); + rt2800_register_write(rt2x00dev, MAC_BSSID_DW1, bssid_dw1); +} + +void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + unsigned int beacon_base; + unsigned int padding_len; + u32 orig_reg, reg; + const int txwi_desc_size = entry->queue->winfo_size; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + orig_reg = reg; + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + /* + * Add space for the TXWI in front of the skb. + */ + memset(skb_push(entry->skb, txwi_desc_size), 0, txwi_desc_size); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->flags |= SKBDESC_DESC_IN_SKB; + skbdesc->desc = entry->skb->data; + skbdesc->desc_len = txwi_desc_size; + + /* + * Add the TXWI for the beacon to the skb. + */ + rt2800_write_tx_data(entry, txdesc); + + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); + + /* + * Write entire beacon with TXWI and padding to register. + */ + padding_len = roundup(entry->skb->len, 4) - entry->skb->len; + if (padding_len && skb_pad(entry->skb, padding_len)) { + rt2x00_err(rt2x00dev, "Failure padding beacon, aborting\n"); + /* skb freed by skb_pad() on failure */ + entry->skb = NULL; + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); + return; + } + + beacon_base = rt2800_hw_beacon_base(rt2x00dev, entry->entry_idx); + + rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data, + entry->skb->len + padding_len); + __set_bit(ENTRY_BCN_ENABLED, &entry->flags); + + /* + * Change global beacons settings. + */ + rt2800_update_beacons_setup(rt2x00dev); + + /* + * Restore beaconing state. + */ + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); + + /* + * Clean up beacon skb. + */ + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; +} +EXPORT_SYMBOL_GPL(rt2800_write_beacon); + +static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev, + unsigned int index) +{ + int i; + const int txwi_desc_size = rt2x00dev->bcn->winfo_size; + unsigned int beacon_base; + + beacon_base = rt2800_hw_beacon_base(rt2x00dev, index); + + /* + * For the Beacon base registers we only need to clear + * the whole TXWI which (when set to 0) will invalidate + * the entire beacon. + */ + for (i = 0; i < txwi_desc_size; i += sizeof(__le32)) + rt2800_register_write(rt2x00dev, beacon_base + i, 0); +} + +void rt2800_clear_beacon(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + u32 orig_reg, reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &orig_reg); + reg = orig_reg; + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + /* + * Clear beacon. + */ + rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx); + __clear_bit(ENTRY_BCN_ENABLED, &entry->flags); + + /* + * Change global beacons settings. + */ + rt2800_update_beacons_setup(rt2x00dev); + /* + * Restore beaconing state. + */ + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); +} +EXPORT_SYMBOL_GPL(rt2800_clear_beacon); + +#ifdef CONFIG_RT2X00_LIB_DEBUGFS +const struct rt2x00debug rt2800_rt2x00debug = { + .owner = THIS_MODULE, + .csr = { + .read = rt2800_register_read, + .write = rt2800_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, + .word_size = sizeof(u32), + .word_count = CSR_REG_SIZE / sizeof(u32), + }, + .eeprom = { + /* NOTE: The local EEPROM access functions can't + * be used here, use the generic versions instead. + */ + .read = rt2x00_eeprom_read, + .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .bbp = { + .read = rt2800_bbp_read, + .write = rt2800_bbp_write, + .word_base = BBP_BASE, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, + .rf = { + .read = rt2x00_rf_read, + .write = rt2800_rf_write, + .word_base = RF_BASE, + .word_size = sizeof(u32), + .word_count = RF_SIZE / sizeof(u32), + }, + .rfcsr = { + .read = rt2800_rfcsr_read, + .write = rt2800_rfcsr_write, + .word_base = RFCSR_BASE, + .word_size = sizeof(u8), + .word_count = RFCSR_SIZE / sizeof(u8), + }, +}; +EXPORT_SYMBOL_GPL(rt2800_rt2x00debug); +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ + +int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + if (rt2x00_rt(rt2x00dev, RT3290)) { + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + return rt2x00_get_field32(reg, WLAN_GPIO_IN_BIT0); + } else { + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + return rt2x00_get_field32(reg, GPIO_CTRL_VAL2); + } +} +EXPORT_SYMBOL_GPL(rt2800_rfkill_poll); + +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt2800_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + unsigned int bg_mode = + (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); + unsigned int polarity = + rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, + EEPROM_FREQ_LED_POLARITY); + unsigned int ledmode = + rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, + EEPROM_FREQ_LED_MODE); + u32 reg; + + /* Check for SoC (SOC devices don't support MCU requests) */ + if (rt2x00_is_soc(led->rt2x00dev)) { + rt2800_register_read(led->rt2x00dev, LED_CFG, ®); + + /* Set LED Polarity */ + rt2x00_set_field32(®, LED_CFG_LED_POLAR, polarity); + + /* Set LED Mode */ + if (led->type == LED_TYPE_RADIO) { + rt2x00_set_field32(®, LED_CFG_G_LED_MODE, + enabled ? 3 : 0); + } else if (led->type == LED_TYPE_ASSOC) { + rt2x00_set_field32(®, LED_CFG_Y_LED_MODE, + enabled ? 3 : 0); + } else if (led->type == LED_TYPE_QUALITY) { + rt2x00_set_field32(®, LED_CFG_R_LED_MODE, + enabled ? 3 : 0); + } + + rt2800_register_write(led->rt2x00dev, LED_CFG, reg); + + } else { + if (led->type == LED_TYPE_RADIO) { + rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, + enabled ? 0x20 : 0); + } else if (led->type == LED_TYPE_ASSOC) { + rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, + enabled ? (bg_mode ? 0x60 : 0xa0) : 0x20); + } else if (led->type == LED_TYPE_QUALITY) { + /* + * The brightness is divided into 6 levels (0 - 5), + * The specs tell us the following levels: + * 0, 1 ,3, 7, 15, 31 + * to determine the level in a simple way we can simply + * work with bitshifting: + * (1 << level) - 1 + */ + rt2800_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff, + (1 << brightness / (LED_FULL / 6)) - 1, + polarity); + } + } +} + +static void rt2800_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, enum led_type type) +{ + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt2800_brightness_set; + led->flags = LED_INITIALIZED; +} +#endif /* CONFIG_RT2X00_LIB_LEDS */ + +/* + * Configuration handlers. + */ +static void rt2800_config_wcid(struct rt2x00_dev *rt2x00dev, + const u8 *address, + int wcid) +{ + struct mac_wcid_entry wcid_entry; + u32 offset; + + offset = MAC_WCID_ENTRY(wcid); + + memset(&wcid_entry, 0xff, sizeof(wcid_entry)); + if (address) + memcpy(wcid_entry.mac, address, ETH_ALEN); + + rt2800_register_multiwrite(rt2x00dev, offset, + &wcid_entry, sizeof(wcid_entry)); +} + +static void rt2800_delete_wcid_attr(struct rt2x00_dev *rt2x00dev, int wcid) +{ + u32 offset; + offset = MAC_WCID_ATTR_ENTRY(wcid); + rt2800_register_write(rt2x00dev, offset, 0); +} + +static void rt2800_config_wcid_attr_bssidx(struct rt2x00_dev *rt2x00dev, + int wcid, u32 bssidx) +{ + u32 offset = MAC_WCID_ATTR_ENTRY(wcid); + u32 reg; + + /* + * The BSS Idx numbers is split in a main value of 3 bits, + * and a extended field for adding one additional bit to the value. + */ + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_BSS_IDX, (bssidx & 0x7)); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_BSS_IDX_EXT, + (bssidx & 0x8) >> 3); + rt2800_register_write(rt2x00dev, offset, reg); +} + +static void rt2800_config_wcid_attr_cipher(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct mac_iveiv_entry iveiv_entry; + u32 offset; + u32 reg; + + offset = MAC_WCID_ATTR_ENTRY(key->hw_key_idx); + + if (crypto->cmd == SET_KEY) { + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_KEYTAB, + !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)); + /* + * Both the cipher as the BSS Idx numbers are split in a main + * value of 3 bits, and a extended field for adding one additional + * bit to the value. + */ + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER, + (crypto->cipher & 0x7)); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER_EXT, + (crypto->cipher & 0x8) >> 3); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_RX_WIUDF, crypto->cipher); + rt2800_register_write(rt2x00dev, offset, reg); + } else { + /* Delete the cipher without touching the bssidx */ + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_KEYTAB, 0); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER, 0); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER_EXT, 0); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_RX_WIUDF, 0); + rt2800_register_write(rt2x00dev, offset, reg); + } + + offset = MAC_IVEIV_ENTRY(key->hw_key_idx); + + memset(&iveiv_entry, 0, sizeof(iveiv_entry)); + if ((crypto->cipher == CIPHER_TKIP) || + (crypto->cipher == CIPHER_TKIP_NO_MIC) || + (crypto->cipher == CIPHER_AES)) + iveiv_entry.iv[3] |= 0x20; + iveiv_entry.iv[3] |= key->keyidx << 6; + rt2800_register_multiwrite(rt2x00dev, offset, + &iveiv_entry, sizeof(iveiv_entry)); +} + +int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_key_entry key_entry; + struct rt2x00_field32 field; + u32 offset; + u32 reg; + + if (crypto->cmd == SET_KEY) { + key->hw_key_idx = (4 * crypto->bssidx) + key->keyidx; + + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + offset = SHARED_KEY_ENTRY(key->hw_key_idx); + rt2800_register_multiwrite(rt2x00dev, offset, + &key_entry, sizeof(key_entry)); + } + + /* + * The cipher types are stored over multiple registers + * starting with SHARED_KEY_MODE_BASE each word will have + * 32 bits and contains the cipher types for 2 bssidx each. + * Using the correct defines correctly will cause overhead, + * so just calculate the correct offset. + */ + field.bit_offset = 4 * (key->hw_key_idx % 8); + field.bit_mask = 0x7 << field.bit_offset; + + offset = SHARED_KEY_MODE_ENTRY(key->hw_key_idx / 8); + + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, field, + (crypto->cmd == SET_KEY) * crypto->cipher); + rt2800_register_write(rt2x00dev, offset, reg); + + /* + * Update WCID information + */ + rt2800_config_wcid(rt2x00dev, crypto->address, key->hw_key_idx); + rt2800_config_wcid_attr_bssidx(rt2x00dev, key->hw_key_idx, + crypto->bssidx); + rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_config_shared_key); + +int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_key_entry key_entry; + u32 offset; + + if (crypto->cmd == SET_KEY) { + /* + * Allow key configuration only for STAs that are + * known by the hw. + */ + if (crypto->wcid > WCID_END) + return -ENOSPC; + key->hw_key_idx = crypto->wcid; + + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + offset = PAIRWISE_KEY_ENTRY(key->hw_key_idx); + rt2800_register_multiwrite(rt2x00dev, offset, + &key_entry, sizeof(key_entry)); + } + + /* + * Update WCID information + */ + rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_config_pairwise_key); + +int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + int wcid; + struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta); + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + + /* + * Search for the first free WCID entry and return the corresponding + * index. + */ + wcid = find_first_zero_bit(drv_data->sta_ids, STA_IDS_SIZE) + WCID_START; + + /* + * Store selected wcid even if it is invalid so that we can + * later decide if the STA is uploaded into the hw. + */ + sta_priv->wcid = wcid; + + /* + * No space left in the device, however, we can still communicate + * with the STA -> No error. + */ + if (wcid > WCID_END) + return 0; + + __set_bit(wcid - WCID_START, drv_data->sta_ids); + + /* + * Clean up WCID attributes and write STA address to the device. + */ + rt2800_delete_wcid_attr(rt2x00dev, wcid); + rt2800_config_wcid(rt2x00dev, sta->addr, wcid); + rt2800_config_wcid_attr_bssidx(rt2x00dev, wcid, + rt2x00lib_get_bssidx(rt2x00dev, vif)); + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_sta_add); + +int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, int wcid) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + + if (wcid > WCID_END) + return 0; + /* + * Remove WCID entry, no need to clean the attributes as they will + * get renewed when the WCID is reused. + */ + rt2800_config_wcid(rt2x00dev, NULL, wcid); + __clear_bit(wcid - WCID_START, drv_data->sta_ids); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_sta_remove); + +void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) +{ + u32 reg; + + /* + * Start configuration steps. + * Note that the version error will always be dropped + * and broadcast frames will always be accepted since + * there is no filter for it at this time. + */ + rt2800_register_read(rt2x00dev, RX_FILTER_CFG, ®); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CRC_ERROR, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PHY_ERROR, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_TO_ME, 1); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_MY_BSSD, 0); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_VER_ERROR, 1); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_MULTICAST, + !(filter_flags & FIF_ALLMULTI)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BROADCAST, 0); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_DUPLICATE, 1); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END_ACK, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_ACK, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CTS, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_RTS, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PSPOLL, + !(filter_flags & FIF_PSPOLL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BA, 0); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BAR, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CNTL, + !(filter_flags & FIF_CONTROL)); + rt2800_register_write(rt2x00dev, RX_FILTER_CFG, reg); +} +EXPORT_SYMBOL_GPL(rt2800_config_filter); + +void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, const unsigned int flags) +{ + u32 reg; + bool update_bssid = false; + + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable synchronisation. + */ + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, conf->sync); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + if (conf->sync == TSF_SYNC_AP_NONE) { + /* + * Tune beacon queue transmit parameters for AP mode + */ + rt2800_register_read(rt2x00dev, TBTT_SYNC_CFG, ®); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_CWMIN, 0); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_AIFSN, 1); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_EXP_WIN, 32); + rt2x00_set_field32(®, TBTT_SYNC_CFG_TBTT_ADJUST, 0); + rt2800_register_write(rt2x00dev, TBTT_SYNC_CFG, reg); + } else { + rt2800_register_read(rt2x00dev, TBTT_SYNC_CFG, ®); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_CWMIN, 4); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_AIFSN, 2); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_EXP_WIN, 32); + rt2x00_set_field32(®, TBTT_SYNC_CFG_TBTT_ADJUST, 16); + rt2800_register_write(rt2x00dev, TBTT_SYNC_CFG, reg); + } + } + + if (flags & CONFIG_UPDATE_MAC) { + if (flags & CONFIG_UPDATE_TYPE && + conf->sync == TSF_SYNC_AP_NONE) { + /* + * The BSSID register has to be set to our own mac + * address in AP mode. + */ + memcpy(conf->bssid, conf->mac, sizeof(conf->mac)); + update_bssid = true; + } + + if (!is_zero_ether_addr((const u8 *)conf->mac)) { + reg = le32_to_cpu(conf->mac[1]); + rt2x00_set_field32(®, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff); + conf->mac[1] = cpu_to_le32(reg); + } + + rt2800_register_multiwrite(rt2x00dev, MAC_ADDR_DW0, + conf->mac, sizeof(conf->mac)); + } + + if ((flags & CONFIG_UPDATE_BSSID) || update_bssid) { + if (!is_zero_ether_addr((const u8 *)conf->bssid)) { + reg = le32_to_cpu(conf->bssid[1]); + rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_ID_MASK, 3); + rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_BCN_NUM, 0); + conf->bssid[1] = cpu_to_le32(reg); + } + + rt2800_register_multiwrite(rt2x00dev, MAC_BSSID_DW0, + conf->bssid, sizeof(conf->bssid)); + } +} +EXPORT_SYMBOL_GPL(rt2800_config_intf); + +static void rt2800_config_ht_opmode(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp) +{ + bool any_sta_nongf = !!(erp->ht_opmode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + u8 protection = erp->ht_opmode & IEEE80211_HT_OP_MODE_PROTECTION; + u8 mm20_mode, mm40_mode, gf20_mode, gf40_mode; + u16 mm20_rate, mm40_rate, gf20_rate, gf40_rate; + u32 reg; + + /* default protection rate for HT20: OFDM 24M */ + mm20_rate = gf20_rate = 0x4004; + + /* default protection rate for HT40: duplicate OFDM 24M */ + mm40_rate = gf40_rate = 0x4084; + + switch (protection) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONE: + /* + * All STAs in this BSS are HT20/40 but there might be + * STAs not supporting greenfield mode. + * => Disable protection for HT transmissions. + */ + mm20_mode = mm40_mode = gf20_mode = gf40_mode = 0; + + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + /* + * All STAs in this BSS are HT20 or HT20/40 but there + * might be STAs not supporting greenfield mode. + * => Protect all HT40 transmissions. + */ + mm20_mode = gf20_mode = 0; + mm40_mode = gf40_mode = 2; + + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + /* + * Nonmember protection: + * According to 802.11n we _should_ protect all + * HT transmissions (but we don't have to). + * + * But if cts_protection is enabled we _shall_ protect + * all HT transmissions using a CCK rate. + * + * And if any station is non GF we _shall_ protect + * GF transmissions. + * + * We decide to protect everything + * -> fall through to mixed mode. + */ + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + /* + * Legacy STAs are present + * => Protect all HT transmissions. + */ + mm20_mode = mm40_mode = gf20_mode = gf40_mode = 2; + + /* + * If erp protection is needed we have to protect HT + * transmissions with CCK 11M long preamble. + */ + if (erp->cts_protection) { + /* don't duplicate RTS/CTS in CCK mode */ + mm20_rate = mm40_rate = 0x0003; + gf20_rate = gf40_rate = 0x0003; + } + break; + } + + /* check for STAs not supporting greenfield mode */ + if (any_sta_nongf) + gf20_mode = gf40_mode = 2; + + /* Update HT protection config */ + rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_RATE, mm20_rate); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, mm20_mode); + rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_RATE, mm40_rate); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, mm40_mode); + rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_RATE, gf20_rate); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, gf20_mode); + rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_RATE, gf40_rate); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, gf40_mode); + rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); +} + +void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, + u32 changed) +{ + u32 reg; + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); + rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, + !!erp->short_preamble); + rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, + !!erp->short_preamble); + rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); + } + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, + erp->cts_protection ? 2 : 0); + rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, + erp->basic_rates); + rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, ®); + rt2x00_set_field32(®, BKOFF_SLOT_CFG_SLOT_TIME, + erp->slot_time); + rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg); + + rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, ®); + rt2x00_set_field32(®, XIFS_TIME_CFG_EIFS, erp->eifs); + rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg); + } + + if (changed & BSS_CHANGED_BEACON_INT) { + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, + erp->beacon_int * 16); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } + + if (changed & BSS_CHANGED_HT) + rt2800_config_ht_opmode(rt2x00dev, erp); +} +EXPORT_SYMBOL_GPL(rt2800_config_erp); + +static void rt2800_config_3572bt_ant(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 eeprom; + u8 led_ctrl, led_g_mode, led_r_mode; + + rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { + rt2x00_set_field32(®, GPIO_SWITCH_0, 1); + rt2x00_set_field32(®, GPIO_SWITCH_1, 1); + } else { + rt2x00_set_field32(®, GPIO_SWITCH_0, 0); + rt2x00_set_field32(®, GPIO_SWITCH_1, 0); + } + rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); + + rt2800_register_read(rt2x00dev, LED_CFG, ®); + led_g_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 3 : 0; + led_r_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 0 : 3; + if (led_g_mode != rt2x00_get_field32(reg, LED_CFG_G_LED_MODE) || + led_r_mode != rt2x00_get_field32(reg, LED_CFG_R_LED_MODE)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); + led_ctrl = rt2x00_get_field16(eeprom, EEPROM_FREQ_LED_MODE); + if (led_ctrl == 0 || led_ctrl > 0x40) { + rt2x00_set_field32(®, LED_CFG_G_LED_MODE, led_g_mode); + rt2x00_set_field32(®, LED_CFG_R_LED_MODE, led_r_mode); + rt2800_register_write(rt2x00dev, LED_CFG, reg); + } else { + rt2800_mcu_request(rt2x00dev, MCU_BAND_SELECT, 0xff, + (led_g_mode << 2) | led_r_mode, 1); + } + } +} + +static void rt2800_set_ant_diversity(struct rt2x00_dev *rt2x00dev, + enum antenna ant) +{ + u32 reg; + u8 eesk_pin = (ant == ANTENNA_A) ? 1 : 0; + u8 gpio_bit3 = (ant == ANTENNA_A) ? 0 : 1; + + if (rt2x00_is_pci(rt2x00dev)) { + rt2800_register_read(rt2x00dev, E2PROM_CSR, ®); + rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, eesk_pin); + rt2800_register_write(rt2x00dev, E2PROM_CSR, reg); + } else if (rt2x00_is_usb(rt2x00dev)) + rt2800_mcu_request(rt2x00dev, MCU_ANT_SELECT, 0xff, + eesk_pin, 0); + + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL3, gpio_bit3); + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); +} + +void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) +{ + u8 r1; + u8 r3; + u16 eeprom; + + rt2800_bbp_read(rt2x00dev, 1, &r1); + rt2800_bbp_read(rt2x00dev, 3, &r3); + + if (rt2x00_rt(rt2x00dev, RT3572) && + rt2x00_has_cap_bt_coexist(rt2x00dev)) + rt2800_config_3572bt_ant(rt2x00dev); + + /* + * Configure the TX antenna. + */ + switch (ant->tx_chain_num) { + case 1: + rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0); + break; + case 2: + if (rt2x00_rt(rt2x00dev, RT3572) && + rt2x00_has_cap_bt_coexist(rt2x00dev)) + rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1); + else + rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); + break; + case 3: + rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); + break; + } + + /* + * Configure the RX antenna. + */ + switch (ant->rx_chain_num) { + case 1: + if (rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3352) || + rt2x00_rt(rt2x00dev, RT3390)) { + rt2800_eeprom_read(rt2x00dev, + EEPROM_NIC_CONF1, &eeprom); + if (rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_ANT_DIVERSITY)) + rt2800_set_ant_diversity(rt2x00dev, + rt2x00dev->default_ant.rx); + } + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0); + break; + case 2: + if (rt2x00_rt(rt2x00dev, RT3572) && + rt2x00_has_cap_bt_coexist(rt2x00dev)) { + rt2x00_set_field8(&r3, BBP3_RX_ADC, 1); + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, + rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); + rt2800_set_ant_diversity(rt2x00dev, ANTENNA_B); + } else { + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 1); + } + break; + case 3: + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 2); + break; + } + + rt2800_bbp_write(rt2x00dev, 3, r3); + rt2800_bbp_write(rt2x00dev, 1, r1); + + if (rt2x00_rt(rt2x00dev, RT3593)) { + if (ant->rx_chain_num == 1) + rt2800_bbp_write(rt2x00dev, 86, 0x00); + else + rt2800_bbp_write(rt2x00dev, 86, 0x46); + } +} +EXPORT_SYMBOL_GPL(rt2800_config_ant); + +static void rt2800_config_lna_gain(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u16 eeprom; + short lna_gain; + + if (libconf->rf.channel <= 14) { + rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG); + } else if (libconf->rf.channel <= 64) { + rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0); + } else if (libconf->rf.channel <= 128) { + if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_EXT_LNA2_A1); + } else { + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_RSSI_BG2_LNA_A1); + } + } else { + if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_EXT_LNA2_A2); + } else { + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_RSSI_A2_LNA_A2); + } + } + + rt2x00dev->lna_gain = lna_gain; +} + +#define FREQ_OFFSET_BOUND 0x5f + +static void rt2800_adjust_freq_offset(struct rt2x00_dev *rt2x00dev) +{ + u8 freq_offset, prev_freq_offset; + u8 rfcsr, prev_rfcsr; + + freq_offset = rt2x00_get_field8(rt2x00dev->freq_offset, RFCSR17_CODE); + freq_offset = min_t(u8, freq_offset, FREQ_OFFSET_BOUND); + + rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); + prev_rfcsr = rfcsr; + + rt2x00_set_field8(&rfcsr, RFCSR17_CODE, freq_offset); + if (rfcsr == prev_rfcsr) + return; + + if (rt2x00_is_usb(rt2x00dev)) { + rt2800_mcu_request(rt2x00dev, MCU_FREQ_OFFSET, 0xff, + freq_offset, prev_rfcsr); + return; + } + + prev_freq_offset = rt2x00_get_field8(prev_rfcsr, RFCSR17_CODE); + while (prev_freq_offset != freq_offset) { + if (prev_freq_offset < freq_offset) + prev_freq_offset++; + else + prev_freq_offset--; + + rt2x00_set_field8(&rfcsr, RFCSR17_CODE, prev_freq_offset); + rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); + + usleep_range(1000, 1500); + } +} + +static void rt2800_config_channel_rf2xxx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); + + if (rt2x00dev->default_ant.tx_chain_num == 1) + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_TX1, 1); + + if (rt2x00dev->default_ant.rx_chain_num == 1) { + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX1, 1); + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); + } else if (rt2x00dev->default_ant.rx_chain_num == 2) + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); + + if (rf->channel > 14) { + /* + * When TX power is below 0, we should increase it by 7 to + * make it a positive value (Minimum value is -7). + * However this means that values between 0 and 7 have + * double meaning, and we should set a 7DBm boost flag. + */ + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST, + (info->default_power1 >= 0)); + + if (info->default_power1 < 0) + info->default_power1 += 7; + + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A, info->default_power1); + + rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST, + (info->default_power2 >= 0)); + + if (info->default_power2 < 0) + info->default_power2 += 7; + + rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A, info->default_power2); + } else { + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G, info->default_power1); + rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G, info->default_power2); + } + + rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf)); + + rt2800_rf_write(rt2x00dev, 1, rf->rf1); + rt2800_rf_write(rt2x00dev, 2, rf->rf2); + rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt2800_rf_write(rt2x00dev, 4, rf->rf4); + + udelay(200); + + rt2800_rf_write(rt2x00dev, 1, rf->rf1); + rt2800_rf_write(rt2x00dev, 2, rf->rf2); + rt2800_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); + rt2800_rf_write(rt2x00dev, 4, rf->rf4); + + udelay(200); + + rt2800_rf_write(rt2x00dev, 1, rf->rf1); + rt2800_rf_write(rt2x00dev, 2, rf->rf2); + rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt2800_rf_write(rt2x00dev, 4, rf->rf4); +} + +static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 rfcsr, calib_tx, calib_rx; + + rt2800_rfcsr_write(rt2x00dev, 2, rf->rf1); + + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR3_K, rf->rf3); + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR6_R1, rf->rf2); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, info->default_power1); + rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, info->default_power2); + rt2800_rfcsr_write(rt2x00dev, 13, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, + rt2x00dev->default_ant.rx_chain_num <= 1); + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, + rt2x00dev->default_ant.rx_chain_num <= 2); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, + rt2x00dev->default_ant.tx_chain_num <= 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, + rt2x00dev->default_ant.tx_chain_num <= 2); + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); + rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); + + if (rt2x00_rt(rt2x00dev, RT3390)) { + calib_tx = conf_is_ht40(conf) ? 0x68 : 0x4f; + calib_rx = conf_is_ht40(conf) ? 0x6f : 0x4f; + } else { + if (conf_is_ht40(conf)) { + calib_tx = drv_data->calibration_bw40; + calib_rx = drv_data->calibration_bw40; + } else { + calib_tx = drv_data->calibration_bw20; + calib_rx = drv_data->calibration_bw20; + } + } + + rt2800_rfcsr_read(rt2x00dev, 24, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR24_TX_CALIB, calib_tx); + rt2800_rfcsr_write(rt2x00dev, 24, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 31, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR31_RX_CALIB, calib_rx); + rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); + rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + msleep(1); + rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); +} + +static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 rfcsr; + u32 reg; + + if (rf->channel <= 14) { + rt2800_bbp_write(rt2x00dev, 25, drv_data->bbp25); + rt2800_bbp_write(rt2x00dev, 26, drv_data->bbp26); + } else { + rt2800_bbp_write(rt2x00dev, 25, 0x09); + rt2800_bbp_write(rt2x00dev, 26, 0xff); + } + + rt2800_rfcsr_write(rt2x00dev, 2, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 3, rf->rf3); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR6_R1, rf->rf2); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR6_TXDIV, 2); + else + rt2x00_set_field8(&rfcsr, RFCSR6_TXDIV, 1); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 5, &rfcsr); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR5_R1, 1); + else + rt2x00_set_field8(&rfcsr, RFCSR5_R1, 2); + rt2800_rfcsr_write(rt2x00dev, 5, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR12_DR0, 3); + rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, + info->default_power1); + } else { + rt2x00_set_field8(&rfcsr, RFCSR12_DR0, 7); + rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, + (info->default_power1 & 0x3) | + ((info->default_power1 & 0xC) << 1)); + } + rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr); + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR13_DR0, 3); + rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, + info->default_power2); + } else { + rt2x00_set_field8(&rfcsr, RFCSR13_DR0, 7); + rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, + (info->default_power2 & 0x3) | + ((info->default_power2 & 0xC) << 1)); + } + rt2800_rfcsr_write(rt2x00dev, 13, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); + } + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); + } else { + switch (rt2x00dev->default_ant.tx_chain_num) { + case 1: + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + case 2: + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); + break; + } + + switch (rt2x00dev->default_ant.rx_chain_num) { + case 1: + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + case 2: + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); + break; + } + } + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); + rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); + + if (conf_is_ht40(conf)) { + rt2800_rfcsr_write(rt2x00dev, 24, drv_data->calibration_bw40); + rt2800_rfcsr_write(rt2x00dev, 31, drv_data->calibration_bw40); + } else { + rt2800_rfcsr_write(rt2x00dev, 24, drv_data->calibration_bw20); + rt2800_rfcsr_write(rt2x00dev, 31, drv_data->calibration_bw20); + } + + if (rf->channel <= 14) { + rt2800_rfcsr_write(rt2x00dev, 7, 0xd8); + rt2800_rfcsr_write(rt2x00dev, 9, 0xc3); + rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 11, 0xb9); + rt2800_rfcsr_write(rt2x00dev, 15, 0x53); + rfcsr = 0x4c; + rt2x00_set_field8(&rfcsr, RFCSR16_TXMIXER_GAIN, + drv_data->txmixer_gain_24g); + rt2800_rfcsr_write(rt2x00dev, 16, rfcsr); + rt2800_rfcsr_write(rt2x00dev, 17, 0x23); + rt2800_rfcsr_write(rt2x00dev, 19, 0x93); + rt2800_rfcsr_write(rt2x00dev, 20, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 25, 0x15); + rt2800_rfcsr_write(rt2x00dev, 26, 0x85); + rt2800_rfcsr_write(rt2x00dev, 27, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x9b); + } else { + rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR7_BIT2, 1); + rt2x00_set_field8(&rfcsr, RFCSR7_BIT3, 0); + rt2x00_set_field8(&rfcsr, RFCSR7_BIT4, 1); + rt2x00_set_field8(&rfcsr, RFCSR7_BITS67, 0); + rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); + rt2800_rfcsr_write(rt2x00dev, 9, 0xc0); + rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 11, 0x00); + rt2800_rfcsr_write(rt2x00dev, 15, 0x43); + rfcsr = 0x7a; + rt2x00_set_field8(&rfcsr, RFCSR16_TXMIXER_GAIN, + drv_data->txmixer_gain_5g); + rt2800_rfcsr_write(rt2x00dev, 16, rfcsr); + rt2800_rfcsr_write(rt2x00dev, 17, 0x23); + if (rf->channel <= 64) { + rt2800_rfcsr_write(rt2x00dev, 19, 0xb7); + rt2800_rfcsr_write(rt2x00dev, 20, 0xf6); + rt2800_rfcsr_write(rt2x00dev, 25, 0x3d); + } else if (rf->channel <= 128) { + rt2800_rfcsr_write(rt2x00dev, 19, 0x74); + rt2800_rfcsr_write(rt2x00dev, 20, 0xf4); + rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + } else { + rt2800_rfcsr_write(rt2x00dev, 19, 0x72); + rt2800_rfcsr_write(rt2x00dev, 20, 0xf3); + rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + } + rt2800_rfcsr_write(rt2x00dev, 26, 0x87); + rt2800_rfcsr_write(rt2x00dev, 27, 0x01); + rt2800_rfcsr_write(rt2x00dev, 29, 0x9f); + } + + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2x00_set_field32(®, GPIO_CTRL_DIR7, 0); + if (rf->channel <= 14) + rt2x00_set_field32(®, GPIO_CTRL_VAL7, 1); + else + rt2x00_set_field32(®, GPIO_CTRL_VAL7, 0); + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + + rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); + rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); +} + +static void rt2800_config_channel_rf3053(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 txrx_agc_fc; + u8 txrx_h20m; + u8 rfcsr; + u8 bbp; + const bool txbf_enabled = false; /* TODO */ + + /* TODO: use TX{0,1,2}FinePowerControl values from EEPROM */ + rt2800_bbp_read(rt2x00dev, 109, &bbp); + rt2x00_set_field8(&bbp, BBP109_TX0_POWER, 0); + rt2x00_set_field8(&bbp, BBP109_TX1_POWER, 0); + rt2800_bbp_write(rt2x00dev, 109, bbp); + + rt2800_bbp_read(rt2x00dev, 110, &bbp); + rt2x00_set_field8(&bbp, BBP110_TX2_POWER, 0); + rt2800_bbp_write(rt2x00dev, 110, bbp); + + if (rf->channel <= 14) { + /* Restore BBP 25 & 26 for 2.4 GHz */ + rt2800_bbp_write(rt2x00dev, 25, drv_data->bbp25); + rt2800_bbp_write(rt2x00dev, 26, drv_data->bbp26); + } else { + /* Hard code BBP 25 & 26 for 5GHz */ + + /* Enable IQ Phase correction */ + rt2800_bbp_write(rt2x00dev, 25, 0x09); + /* Setup IQ Phase correction value */ + rt2800_bbp_write(rt2x00dev, 26, 0xff); + } + + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3 & 0xf); + + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_R, (rf->rf2 & 0x3)); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_PLL_IDOH, 1); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 1); + else + rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 2); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 53, &rfcsr); + if (rf->channel <= 14) { + rfcsr = 0; + rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER, + info->default_power1 & 0x1f); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rfcsr = 0x40; + + rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER, + ((info->default_power1 & 0x18) << 1) | + (info->default_power1 & 7)); + } + rt2800_rfcsr_write(rt2x00dev, 53, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 55, &rfcsr); + if (rf->channel <= 14) { + rfcsr = 0; + rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER, + info->default_power2 & 0x1f); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rfcsr = 0x40; + + rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER, + ((info->default_power2 & 0x18) << 1) | + (info->default_power2 & 7)); + } + rt2800_rfcsr_write(rt2x00dev, 55, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 54, &rfcsr); + if (rf->channel <= 14) { + rfcsr = 0; + rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER, + info->default_power3 & 0x1f); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rfcsr = 0x40; + + rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER, + ((info->default_power3 & 0x18) << 1) | + (info->default_power3 & 7)); + } + rt2800_rfcsr_write(rt2x00dev, 54, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); + + switch (rt2x00dev->default_ant.tx_chain_num) { + case 3: + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); + /* fallthrough */ + case 2: + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + /* fallthrough */ + case 1: + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); + break; + } + + switch (rt2x00dev->default_ant.rx_chain_num) { + case 3: + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); + /* fallthrough */ + case 2: + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + /* fallthrough */ + case 1: + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); + break; + } + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_adjust_freq_offset(rt2x00dev); + + if (conf_is_ht40(conf)) { + txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw40, + RFCSR24_TX_AGC_FC); + txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw40, + RFCSR24_TX_H20M); + } else { + txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw20, + RFCSR24_TX_AGC_FC); + txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw20, + RFCSR24_TX_H20M); + } + + /* NOTE: the reference driver does not writes the new value + * back to RFCSR 32 + */ + rt2800_rfcsr_read(rt2x00dev, 32, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR32_TX_AGC_FC, txrx_agc_fc); + + if (rf->channel <= 14) + rfcsr = 0xa0; + else + rfcsr = 0x80; + rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, txrx_h20m); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, txrx_h20m); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + /* Band selection */ + rt2800_rfcsr_read(rt2x00dev, 36, &rfcsr); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 1); + else + rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 0); + rt2800_rfcsr_write(rt2x00dev, 36, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 34, &rfcsr); + if (rf->channel <= 14) + rfcsr = 0x3c; + else + rfcsr = 0x20; + rt2800_rfcsr_write(rt2x00dev, 34, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); + if (rf->channel <= 14) + rfcsr = 0x1a; + else + rfcsr = 0x12; + rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + if (rf->channel >= 1 && rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1); + else if (rf->channel >= 36 && rf->channel <= 64) + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2); + else if (rf->channel >= 100 && rf->channel <= 128) + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2); + else + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 46, 0x60); + + if (rf->channel <= 14) { + rt2800_rfcsr_write(rt2x00dev, 10, 0xd3); + rt2800_rfcsr_write(rt2x00dev, 13, 0x12); + } else { + rt2800_rfcsr_write(rt2x00dev, 10, 0xd8); + rt2800_rfcsr_write(rt2x00dev, 13, 0x23); + } + + rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS01, 1); + rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 5); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 3); + } else { + rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 4); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 2); + } + rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 3); + else + rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 2); + + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR49_TX_DIV, 1); + + rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO1_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 57, &rfcsr); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x1b); + else + rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 57, rfcsr); + + if (rf->channel <= 14) { + rt2800_rfcsr_write(rt2x00dev, 44, 0x93); + rt2800_rfcsr_write(rt2x00dev, 52, 0x45); + } else { + rt2800_rfcsr_write(rt2x00dev, 44, 0x9b); + rt2800_rfcsr_write(rt2x00dev, 52, 0x05); + } + + /* Initiate VCO calibration */ + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + } else { + rt2x00_set_field8(&rfcsr, RFCSR3_BIT1, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT2, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT3, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT4, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT5, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + } + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + + if (rf->channel >= 1 && rf->channel <= 14) { + rfcsr = 0x23; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 45, 0xbb); + } else if (rf->channel >= 36 && rf->channel <= 64) { + rfcsr = 0x36; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, 0x36); + + rt2800_rfcsr_write(rt2x00dev, 45, 0xeb); + } else if (rf->channel >= 100 && rf->channel <= 128) { + rfcsr = 0x32; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 45, 0xb3); + } else { + rfcsr = 0x30; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 45, 0x9b); + } +} + +#define POWER_BOUND 0x27 +#define POWER_BOUND_5G 0x2b + +static void rt2800_config_channel_rf3290(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u8 rfcsr; + + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf2); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); + if (info->default_power1 > POWER_BOUND) + rt2x00_set_field8(&rfcsr, RFCSR49_TX, POWER_BOUND); + else + rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); + rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); + + rt2800_adjust_freq_offset(rt2x00dev); + + if (rf->channel <= 14) { + if (rf->channel == 6) + rt2800_bbp_write(rt2x00dev, 68, 0x0c); + else + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + if (rf->channel >= 1 && rf->channel <= 6) + rt2800_bbp_write(rt2x00dev, 59, 0x0f); + else if (rf->channel >= 7 && rf->channel <= 11) + rt2800_bbp_write(rt2x00dev, 59, 0x0e); + else if (rf->channel >= 12 && rf->channel <= 14) + rt2800_bbp_write(rt2x00dev, 59, 0x0d); + } +} + +static void rt2800_config_channel_rf3322(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u8 rfcsr; + + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); + + rt2800_rfcsr_write(rt2x00dev, 11, 0x42); + rt2800_rfcsr_write(rt2x00dev, 12, 0x1c); + rt2800_rfcsr_write(rt2x00dev, 13, 0x00); + + if (info->default_power1 > POWER_BOUND) + rt2800_rfcsr_write(rt2x00dev, 47, POWER_BOUND); + else + rt2800_rfcsr_write(rt2x00dev, 47, info->default_power1); + + if (info->default_power2 > POWER_BOUND) + rt2800_rfcsr_write(rt2x00dev, 48, POWER_BOUND); + else + rt2800_rfcsr_write(rt2x00dev, 48, info->default_power2); + + rt2800_adjust_freq_offset(rt2x00dev); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); + + if ( rt2x00dev->default_ant.tx_chain_num == 2 ) + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + else + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); + + if ( rt2x00dev->default_ant.rx_chain_num == 2 ) + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + else + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); + + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); + + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 31, 80); +} + +static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u8 rfcsr; + + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf2); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); + if (info->default_power1 > POWER_BOUND) + rt2x00_set_field8(&rfcsr, RFCSR49_TX, POWER_BOUND); + else + rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); + rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); + + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); + if (info->default_power2 > POWER_BOUND) + rt2x00_set_field8(&rfcsr, RFCSR50_TX, POWER_BOUND); + else + rt2x00_set_field8(&rfcsr, RFCSR50_TX, + info->default_power2); + rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); + } + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + } + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_adjust_freq_offset(rt2x00dev); + + if (rf->channel <= 14) { + int idx = rf->channel-1; + + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { + /* r55/r59 value array of channel 1~14 */ + static const char r55_bt_rev[] = {0x83, 0x83, + 0x83, 0x73, 0x73, 0x63, 0x53, 0x53, + 0x53, 0x43, 0x43, 0x43, 0x43, 0x43}; + static const char r59_bt_rev[] = {0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0b, 0x0a, 0x09, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07}; + + rt2800_rfcsr_write(rt2x00dev, 55, + r55_bt_rev[idx]); + rt2800_rfcsr_write(rt2x00dev, 59, + r59_bt_rev[idx]); + } else { + static const char r59_bt[] = {0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x89, + 0x88, 0x88, 0x86, 0x85, 0x84}; + + rt2800_rfcsr_write(rt2x00dev, 59, r59_bt[idx]); + } + } else { + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { + static const char r55_nonbt_rev[] = {0x23, 0x23, + 0x23, 0x23, 0x13, 0x13, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}; + static const char r59_nonbt_rev[] = {0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x06, 0x05, 0x04, 0x04}; + + rt2800_rfcsr_write(rt2x00dev, 55, + r55_nonbt_rev[idx]); + rt2800_rfcsr_write(rt2x00dev, 59, + r59_nonbt_rev[idx]); + } else if (rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) { + static const char r59_non_bt[] = {0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8d, + 0x8a, 0x88, 0x88, 0x87, 0x87, 0x86}; + + rt2800_rfcsr_write(rt2x00dev, 59, + r59_non_bt[idx]); + } + } + } +} + +static void rt2800_config_channel_rf55xx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u8 rfcsr, ep_reg; + u32 reg; + int power_bound; + + /* TODO */ + const bool is_11b = false; + const bool is_type_ep = false; + + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, + (rf->channel > 14 || conf_is_ht40(conf)) ? 5 : 0); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + + /* Order of values on rf_channel entry: N, K, mod, R */ + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1 & 0xff); + + rt2800_rfcsr_read(rt2x00dev, 9, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR9_K, rf->rf2 & 0xf); + rt2x00_set_field8(&rfcsr, RFCSR9_N, (rf->rf1 & 0x100) >> 8); + rt2x00_set_field8(&rfcsr, RFCSR9_MOD, ((rf->rf3 - 8) & 0x4) >> 2); + rt2800_rfcsr_write(rt2x00dev, 9, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf4 - 1); + rt2x00_set_field8(&rfcsr, RFCSR11_MOD, (rf->rf3 - 8) & 0x3); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + if (rf->channel <= 14) { + rt2800_rfcsr_write(rt2x00dev, 10, 0x90); + /* FIXME: RF11 owerwrite ? */ + rt2800_rfcsr_write(rt2x00dev, 11, 0x4A); + rt2800_rfcsr_write(rt2x00dev, 12, 0x52); + rt2800_rfcsr_write(rt2x00dev, 13, 0x42); + rt2800_rfcsr_write(rt2x00dev, 22, 0x40); + rt2800_rfcsr_write(rt2x00dev, 24, 0x4A); + rt2800_rfcsr_write(rt2x00dev, 25, 0x80); + rt2800_rfcsr_write(rt2x00dev, 27, 0x42); + rt2800_rfcsr_write(rt2x00dev, 36, 0x80); + rt2800_rfcsr_write(rt2x00dev, 37, 0x08); + rt2800_rfcsr_write(rt2x00dev, 38, 0x89); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1B); + rt2800_rfcsr_write(rt2x00dev, 40, 0x0D); + rt2800_rfcsr_write(rt2x00dev, 41, 0x9B); + rt2800_rfcsr_write(rt2x00dev, 42, 0xD5); + rt2800_rfcsr_write(rt2x00dev, 43, 0x72); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0E); + rt2800_rfcsr_write(rt2x00dev, 45, 0xA2); + rt2800_rfcsr_write(rt2x00dev, 46, 0x6B); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 51, 0x3E); + rt2800_rfcsr_write(rt2x00dev, 52, 0x48); + rt2800_rfcsr_write(rt2x00dev, 54, 0x38); + rt2800_rfcsr_write(rt2x00dev, 56, 0xA1); + rt2800_rfcsr_write(rt2x00dev, 57, 0x00); + rt2800_rfcsr_write(rt2x00dev, 58, 0x39); + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + rt2800_rfcsr_write(rt2x00dev, 61, 0x91); + rt2800_rfcsr_write(rt2x00dev, 62, 0x39); + + /* TODO RF27 <- tssi */ + + rfcsr = rf->channel <= 10 ? 0x07 : 0x06; + rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); + rt2800_rfcsr_write(rt2x00dev, 59, rfcsr); + + if (is_11b) { + /* CCK */ + rt2800_rfcsr_write(rt2x00dev, 31, 0xF8); + rt2800_rfcsr_write(rt2x00dev, 32, 0xC0); + if (is_type_ep) + rt2800_rfcsr_write(rt2x00dev, 55, 0x06); + else + rt2800_rfcsr_write(rt2x00dev, 55, 0x47); + } else { + /* OFDM */ + if (is_type_ep) + rt2800_rfcsr_write(rt2x00dev, 55, 0x03); + else + rt2800_rfcsr_write(rt2x00dev, 55, 0x43); + } + + power_bound = POWER_BOUND; + ep_reg = 0x2; + } else { + rt2800_rfcsr_write(rt2x00dev, 10, 0x97); + /* FIMXE: RF11 overwrite */ + rt2800_rfcsr_write(rt2x00dev, 11, 0x40); + rt2800_rfcsr_write(rt2x00dev, 25, 0xBF); + rt2800_rfcsr_write(rt2x00dev, 27, 0x42); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 37, 0x04); + rt2800_rfcsr_write(rt2x00dev, 38, 0x85); + rt2800_rfcsr_write(rt2x00dev, 40, 0x42); + rt2800_rfcsr_write(rt2x00dev, 41, 0xBB); + rt2800_rfcsr_write(rt2x00dev, 42, 0xD7); + rt2800_rfcsr_write(rt2x00dev, 45, 0x41); + rt2800_rfcsr_write(rt2x00dev, 48, 0x00); + rt2800_rfcsr_write(rt2x00dev, 57, 0x77); + rt2800_rfcsr_write(rt2x00dev, 60, 0x05); + rt2800_rfcsr_write(rt2x00dev, 61, 0x01); + + /* TODO RF27 <- tssi */ + + if (rf->channel >= 36 && rf->channel <= 64) { + + rt2800_rfcsr_write(rt2x00dev, 12, 0x2E); + rt2800_rfcsr_write(rt2x00dev, 13, 0x22); + rt2800_rfcsr_write(rt2x00dev, 22, 0x60); + rt2800_rfcsr_write(rt2x00dev, 23, 0x7F); + if (rf->channel <= 50) + rt2800_rfcsr_write(rt2x00dev, 24, 0x09); + else if (rf->channel >= 52) + rt2800_rfcsr_write(rt2x00dev, 24, 0x07); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1C); + rt2800_rfcsr_write(rt2x00dev, 43, 0x5B); + rt2800_rfcsr_write(rt2x00dev, 44, 0X40); + rt2800_rfcsr_write(rt2x00dev, 46, 0X00); + rt2800_rfcsr_write(rt2x00dev, 51, 0xFE); + rt2800_rfcsr_write(rt2x00dev, 52, 0x0C); + rt2800_rfcsr_write(rt2x00dev, 54, 0xF8); + if (rf->channel <= 50) { + rt2800_rfcsr_write(rt2x00dev, 55, 0x06), + rt2800_rfcsr_write(rt2x00dev, 56, 0xD3); + } else if (rf->channel >= 52) { + rt2800_rfcsr_write(rt2x00dev, 55, 0x04); + rt2800_rfcsr_write(rt2x00dev, 56, 0xBB); + } + + rt2800_rfcsr_write(rt2x00dev, 58, 0x15); + rt2800_rfcsr_write(rt2x00dev, 59, 0x7F); + rt2800_rfcsr_write(rt2x00dev, 62, 0x15); + + } else if (rf->channel >= 100 && rf->channel <= 165) { + + rt2800_rfcsr_write(rt2x00dev, 12, 0x0E); + rt2800_rfcsr_write(rt2x00dev, 13, 0x42); + rt2800_rfcsr_write(rt2x00dev, 22, 0x40); + if (rf->channel <= 153) { + rt2800_rfcsr_write(rt2x00dev, 23, 0x3C); + rt2800_rfcsr_write(rt2x00dev, 24, 0x06); + } else if (rf->channel >= 155) { + rt2800_rfcsr_write(rt2x00dev, 23, 0x38); + rt2800_rfcsr_write(rt2x00dev, 24, 0x05); + } + if (rf->channel <= 138) { + rt2800_rfcsr_write(rt2x00dev, 39, 0x1A); + rt2800_rfcsr_write(rt2x00dev, 43, 0x3B); + rt2800_rfcsr_write(rt2x00dev, 44, 0x20); + rt2800_rfcsr_write(rt2x00dev, 46, 0x18); + } else if (rf->channel >= 140) { + rt2800_rfcsr_write(rt2x00dev, 39, 0x18); + rt2800_rfcsr_write(rt2x00dev, 43, 0x1B); + rt2800_rfcsr_write(rt2x00dev, 44, 0x10); + rt2800_rfcsr_write(rt2x00dev, 46, 0X08); + } + if (rf->channel <= 124) + rt2800_rfcsr_write(rt2x00dev, 51, 0xFC); + else if (rf->channel >= 126) + rt2800_rfcsr_write(rt2x00dev, 51, 0xEC); + if (rf->channel <= 138) + rt2800_rfcsr_write(rt2x00dev, 52, 0x06); + else if (rf->channel >= 140) + rt2800_rfcsr_write(rt2x00dev, 52, 0x06); + rt2800_rfcsr_write(rt2x00dev, 54, 0xEB); + if (rf->channel <= 138) + rt2800_rfcsr_write(rt2x00dev, 55, 0x01); + else if (rf->channel >= 140) + rt2800_rfcsr_write(rt2x00dev, 55, 0x00); + if (rf->channel <= 128) + rt2800_rfcsr_write(rt2x00dev, 56, 0xBB); + else if (rf->channel >= 130) + rt2800_rfcsr_write(rt2x00dev, 56, 0xAB); + if (rf->channel <= 116) + rt2800_rfcsr_write(rt2x00dev, 58, 0x1D); + else if (rf->channel >= 118) + rt2800_rfcsr_write(rt2x00dev, 58, 0x15); + if (rf->channel <= 138) + rt2800_rfcsr_write(rt2x00dev, 59, 0x3F); + else if (rf->channel >= 140) + rt2800_rfcsr_write(rt2x00dev, 59, 0x7C); + if (rf->channel <= 116) + rt2800_rfcsr_write(rt2x00dev, 62, 0x1D); + else if (rf->channel >= 118) + rt2800_rfcsr_write(rt2x00dev, 62, 0x15); + } + + power_bound = POWER_BOUND_5G; + ep_reg = 0x3; + } + + rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); + if (info->default_power1 > power_bound) + rt2x00_set_field8(&rfcsr, RFCSR49_TX, power_bound); + else + rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); + if (is_type_ep) + rt2x00_set_field8(&rfcsr, RFCSR49_EP, ep_reg); + rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); + if (info->default_power2 > power_bound) + rt2x00_set_field8(&rfcsr, RFCSR50_TX, power_bound); + else + rt2x00_set_field8(&rfcsr, RFCSR50_TX, info->default_power2); + if (is_type_ep) + rt2x00_set_field8(&rfcsr, RFCSR50_EP, ep_reg); + rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); + + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, + rt2x00dev->default_ant.tx_chain_num >= 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, + rt2x00dev->default_ant.tx_chain_num == 2); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); + + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, + rt2x00dev->default_ant.rx_chain_num >= 1); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, + rt2x00dev->default_ant.rx_chain_num == 2); + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); + + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + rt2800_rfcsr_write(rt2x00dev, 6, 0xe4); + + if (conf_is_ht40(conf)) + rt2800_rfcsr_write(rt2x00dev, 30, 0x16); + else + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + + if (!is_11b) { + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + } + + /* TODO proper frequency adjustment */ + rt2800_adjust_freq_offset(rt2x00dev); + + /* TODO merge with others */ + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + + /* BBP settings */ + rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); + + rt2800_bbp_write(rt2x00dev, 79, (rf->channel <= 14) ? 0x1C : 0x18); + rt2800_bbp_write(rt2x00dev, 80, (rf->channel <= 14) ? 0x0E : 0x08); + rt2800_bbp_write(rt2x00dev, 81, (rf->channel <= 14) ? 0x3A : 0x38); + rt2800_bbp_write(rt2x00dev, 82, (rf->channel <= 14) ? 0x62 : 0x92); + + /* GLRT band configuration */ + rt2800_bbp_write(rt2x00dev, 195, 128); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0xE0 : 0xF0); + rt2800_bbp_write(rt2x00dev, 195, 129); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x1F : 0x1E); + rt2800_bbp_write(rt2x00dev, 195, 130); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x38 : 0x28); + rt2800_bbp_write(rt2x00dev, 195, 131); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x32 : 0x20); + rt2800_bbp_write(rt2x00dev, 195, 133); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x28 : 0x7F); + rt2800_bbp_write(rt2x00dev, 195, 124); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x19 : 0x7F); +} + +static void rt2800_bbp_write_with_rx_chain(struct rt2x00_dev *rt2x00dev, + const unsigned int word, + const u8 value) +{ + u8 chain, reg; + + for (chain = 0; chain < rt2x00dev->default_ant.rx_chain_num; chain++) { + rt2800_bbp_read(rt2x00dev, 27, ®); + rt2x00_set_field8(®, BBP27_RX_CHAIN_SEL, chain); + rt2800_bbp_write(rt2x00dev, 27, reg); + + rt2800_bbp_write(rt2x00dev, word, value); + } +} + +static void rt2800_iq_calibrate(struct rt2x00_dev *rt2x00dev, int channel) +{ + u8 cal; + + /* TX0 IQ Gain */ + rt2800_bbp_write(rt2x00dev, 158, 0x2c); + if (channel <= 14) + cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_GAIN_CAL_TX0_2G); + else if (channel >= 36 && channel <= 64) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5G); + else if (channel >= 100 && channel <= 138) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5G); + else if (channel >= 140 && channel <= 165) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5G); + else + cal = 0; + rt2800_bbp_write(rt2x00dev, 159, cal); + + /* TX0 IQ Phase */ + rt2800_bbp_write(rt2x00dev, 158, 0x2d); + if (channel <= 14) + cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_PHASE_CAL_TX0_2G); + else if (channel >= 36 && channel <= 64) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5G); + else if (channel >= 100 && channel <= 138) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5G); + else if (channel >= 140 && channel <= 165) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5G); + else + cal = 0; + rt2800_bbp_write(rt2x00dev, 159, cal); + + /* TX1 IQ Gain */ + rt2800_bbp_write(rt2x00dev, 158, 0x4a); + if (channel <= 14) + cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_GAIN_CAL_TX1_2G); + else if (channel >= 36 && channel <= 64) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5G); + else if (channel >= 100 && channel <= 138) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5G); + else if (channel >= 140 && channel <= 165) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5G); + else + cal = 0; + rt2800_bbp_write(rt2x00dev, 159, cal); + + /* TX1 IQ Phase */ + rt2800_bbp_write(rt2x00dev, 158, 0x4b); + if (channel <= 14) + cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_PHASE_CAL_TX1_2G); + else if (channel >= 36 && channel <= 64) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5G); + else if (channel >= 100 && channel <= 138) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5G); + else if (channel >= 140 && channel <= 165) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5G); + else + cal = 0; + rt2800_bbp_write(rt2x00dev, 159, cal); + + /* FIXME: possible RX0, RX1 callibration ? */ + + /* RF IQ compensation control */ + rt2800_bbp_write(rt2x00dev, 158, 0x04); + cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_RF_IQ_COMPENSATION_CONTROL); + rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0); + + /* RF IQ imbalance compensation control */ + rt2800_bbp_write(rt2x00dev, 158, 0x03); + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CONTROL); + rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0); +} + +static char rt2800_txpower_to_dev(struct rt2x00_dev *rt2x00dev, + unsigned int channel, + char txpower) +{ + if (rt2x00_rt(rt2x00dev, RT3593)) + txpower = rt2x00_get_field8(txpower, EEPROM_TXPOWER_ALC); + + if (channel <= 14) + return clamp_t(char, txpower, MIN_G_TXPOWER, MAX_G_TXPOWER); + + if (rt2x00_rt(rt2x00dev, RT3593)) + return clamp_t(char, txpower, MIN_A_TXPOWER_3593, + MAX_A_TXPOWER_3593); + else + return clamp_t(char, txpower, MIN_A_TXPOWER, MAX_A_TXPOWER); +} + +static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u32 reg; + unsigned int tx_pin; + u8 bbp, rfcsr; + + info->default_power1 = rt2800_txpower_to_dev(rt2x00dev, rf->channel, + info->default_power1); + info->default_power2 = rt2800_txpower_to_dev(rt2x00dev, rf->channel, + info->default_power2); + if (rt2x00dev->default_ant.tx_chain_num > 2) + info->default_power3 = + rt2800_txpower_to_dev(rt2x00dev, rf->channel, + info->default_power3); + + switch (rt2x00dev->chip.rf) { + case RF2020: + case RF3020: + case RF3021: + case RF3022: + case RF3320: + rt2800_config_channel_rf3xxx(rt2x00dev, conf, rf, info); + break; + case RF3052: + rt2800_config_channel_rf3052(rt2x00dev, conf, rf, info); + break; + case RF3053: + rt2800_config_channel_rf3053(rt2x00dev, conf, rf, info); + break; + case RF3290: + rt2800_config_channel_rf3290(rt2x00dev, conf, rf, info); + break; + case RF3322: + rt2800_config_channel_rf3322(rt2x00dev, conf, rf, info); + break; + case RF3070: + case RF5360: + case RF5362: + case RF5370: + case RF5372: + case RF5390: + case RF5392: + rt2800_config_channel_rf53xx(rt2x00dev, conf, rf, info); + break; + case RF5592: + rt2800_config_channel_rf55xx(rt2x00dev, conf, rf, info); + break; + default: + rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info); + } + + if (rt2x00_rf(rt2x00dev, RF3070) || + rt2x00_rf(rt2x00dev, RF3290) || + rt2x00_rf(rt2x00dev, RF3322) || + rt2x00_rf(rt2x00dev, RF5360) || + rt2x00_rf(rt2x00dev, RF5362) || + rt2x00_rf(rt2x00dev, RF5370) || + rt2x00_rf(rt2x00dev, RF5372) || + rt2x00_rf(rt2x00dev, RF5390) || + rt2x00_rf(rt2x00dev, RF5392)) { + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, 0); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, 0); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + } + + /* + * Change BBP settings + */ + if (rt2x00_rt(rt2x00dev, RT3352)) { + rt2800_bbp_write(rt2x00dev, 27, 0x0); + rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 27, 0x20); + rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain); + } else if (rt2x00_rt(rt2x00dev, RT3593)) { + if (rf->channel > 14) { + /* Disable CCK Packet detection on 5GHz */ + rt2800_bbp_write(rt2x00dev, 70, 0x00); + } else { + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + } + + if (conf_is_ht40(conf)) + rt2800_bbp_write(rt2x00dev, 105, 0x04); + else + rt2800_bbp_write(rt2x00dev, 105, 0x34); + + rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 77, 0x98); + } else { + rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 86, 0); + } + + if (rf->channel <= 14) { + if (!rt2x00_rt(rt2x00dev, RT5390) && + !rt2x00_rt(rt2x00dev, RT5392)) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { + rt2800_bbp_write(rt2x00dev, 82, 0x62); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + } else { + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 82, 0x62); + else + rt2800_bbp_write(rt2x00dev, 82, 0x84); + rt2800_bbp_write(rt2x00dev, 75, 0x50); + } + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 83, 0x8a); + } + + } else { + if (rt2x00_rt(rt2x00dev, RT3572)) + rt2800_bbp_write(rt2x00dev, 82, 0x94); + else if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 82, 0x82); + else + rt2800_bbp_write(rt2x00dev, 82, 0xf2); + + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 83, 0x9a); + + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) + rt2800_bbp_write(rt2x00dev, 75, 0x46); + else + rt2800_bbp_write(rt2x00dev, 75, 0x50); + } + + rt2800_register_read(rt2x00dev, TX_BAND_CFG, ®); + rt2x00_set_field32(®, TX_BAND_CFG_HT40_MINUS, conf_is_ht40_minus(conf)); + rt2x00_set_field32(®, TX_BAND_CFG_A, rf->channel > 14); + rt2x00_set_field32(®, TX_BAND_CFG_BG, rf->channel <= 14); + rt2800_register_write(rt2x00dev, TX_BAND_CFG, reg); + + if (rt2x00_rt(rt2x00dev, RT3572)) + rt2800_rfcsr_write(rt2x00dev, 8, 0); + + tx_pin = 0; + + switch (rt2x00dev->default_ant.tx_chain_num) { + case 3: + /* Turn on tertiary PAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A2_EN, + rf->channel > 14); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G2_EN, + rf->channel <= 14); + /* fall-through */ + case 2: + /* Turn on secondary PAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, + rf->channel > 14); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, + rf->channel <= 14); + /* fall-through */ + case 1: + /* Turn on primary PAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, + rf->channel > 14); + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); + else + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, + rf->channel <= 14); + break; + } + + switch (rt2x00dev->default_ant.rx_chain_num) { + case 3: + /* Turn on tertiary LNAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A2_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G2_EN, 1); + /* fall-through */ + case 2: + /* Turn on secondary LNAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, 1); + /* fall-through */ + case 1: + /* Turn on primary LNAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1); + break; + } + + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1); + + rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); + + if (rt2x00_rt(rt2x00dev, RT3572)) { + rt2800_rfcsr_write(rt2x00dev, 8, 0x80); + + /* AGC init */ + if (rf->channel <= 14) + reg = 0x1c + (2 * rt2x00dev->lna_gain); + else + reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3); + + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); + } + + if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + + /* Band selection */ + if (rt2x00_is_usb(rt2x00dev) || + rt2x00_is_pcie(rt2x00dev)) { + /* GPIO #8 controls all paths */ + rt2x00_set_field32(®, GPIO_CTRL_DIR8, 0); + if (rf->channel <= 14) + rt2x00_set_field32(®, GPIO_CTRL_VAL8, 1); + else + rt2x00_set_field32(®, GPIO_CTRL_VAL8, 0); + } + + /* LNA PE control. */ + if (rt2x00_is_usb(rt2x00dev)) { + /* GPIO #4 controls PE0 and PE1, + * GPIO #7 controls PE2 + */ + rt2x00_set_field32(®, GPIO_CTRL_DIR4, 0); + rt2x00_set_field32(®, GPIO_CTRL_DIR7, 0); + + rt2x00_set_field32(®, GPIO_CTRL_VAL4, 1); + rt2x00_set_field32(®, GPIO_CTRL_VAL7, 1); + } else if (rt2x00_is_pcie(rt2x00dev)) { + /* GPIO #4 controls PE0, PE1 and PE2 */ + rt2x00_set_field32(®, GPIO_CTRL_DIR4, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL4, 1); + } + + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + + /* AGC init */ + if (rf->channel <= 14) + reg = 0x1c + 2 * rt2x00dev->lna_gain; + else + reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3); + + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); + + usleep_range(1000, 1500); + } + + if (rt2x00_rt(rt2x00dev, RT5592)) { + rt2800_bbp_write(rt2x00dev, 195, 141); + rt2800_bbp_write(rt2x00dev, 196, conf_is_ht40(conf) ? 0x10 : 0x1a); + + /* AGC init */ + reg = (rf->channel <= 14 ? 0x1c : 0x24) + 2 * rt2x00dev->lna_gain; + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); + + rt2800_iq_calibrate(rt2x00dev, rf->channel); + } + + rt2800_bbp_read(rt2x00dev, 4, &bbp); + rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf)); + rt2800_bbp_write(rt2x00dev, 4, bbp); + + rt2800_bbp_read(rt2x00dev, 3, &bbp); + rt2x00_set_field8(&bbp, BBP3_HT40_MINUS, conf_is_ht40_minus(conf)); + rt2800_bbp_write(rt2x00dev, 3, bbp); + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { + if (conf_is_ht40(conf)) { + rt2800_bbp_write(rt2x00dev, 69, 0x1a); + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + rt2800_bbp_write(rt2x00dev, 73, 0x16); + } else { + rt2800_bbp_write(rt2x00dev, 69, 0x16); + rt2800_bbp_write(rt2x00dev, 70, 0x08); + rt2800_bbp_write(rt2x00dev, 73, 0x11); + } + } + + msleep(1); + + /* + * Clear channel statistic counters + */ + rt2800_register_read(rt2x00dev, CH_IDLE_STA, ®); + rt2800_register_read(rt2x00dev, CH_BUSY_STA, ®); + rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, ®); + + /* + * Clear update flag + */ + if (rt2x00_rt(rt2x00dev, RT3352)) { + rt2800_bbp_read(rt2x00dev, 49, &bbp); + rt2x00_set_field8(&bbp, BBP49_UPDATE_FLAG, 0); + rt2800_bbp_write(rt2x00dev, 49, bbp); + } +} + +static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev) +{ + u8 tssi_bounds[9]; + u8 current_tssi; + u16 eeprom; + u8 step; + int i; + + /* + * First check if temperature compensation is supported. + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + if (!rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC)) + return 0; + + /* + * Read TSSI boundaries for temperature compensation from + * the EEPROM. + * + * Array idx 0 1 2 3 4 5 6 7 8 + * Matching Delta value -4 -3 -2 -1 0 +1 +2 +3 +4 + * Example TSSI bounds 0xF0 0xD0 0xB5 0xA0 0x88 0x45 0x25 0x15 0x00 + */ + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom); + tssi_bounds[0] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG1_MINUS4); + tssi_bounds[1] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG1_MINUS3); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom); + tssi_bounds[2] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG2_MINUS2); + tssi_bounds[3] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG2_MINUS1); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom); + tssi_bounds[4] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG3_REF); + tssi_bounds[5] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG3_PLUS1); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom); + tssi_bounds[6] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG4_PLUS2); + tssi_bounds[7] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG4_PLUS3); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom); + tssi_bounds[8] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG5_PLUS4); + + step = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG5_AGC_STEP); + } else { + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom); + tssi_bounds[0] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A1_MINUS4); + tssi_bounds[1] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A1_MINUS3); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom); + tssi_bounds[2] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A2_MINUS2); + tssi_bounds[3] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A2_MINUS1); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom); + tssi_bounds[4] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A3_REF); + tssi_bounds[5] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A3_PLUS1); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom); + tssi_bounds[6] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A4_PLUS2); + tssi_bounds[7] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A4_PLUS3); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom); + tssi_bounds[8] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A5_PLUS4); + + step = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A5_AGC_STEP); + } + + /* + * Check if temperature compensation is supported. + */ + if (tssi_bounds[4] == 0xff || step == 0xff) + return 0; + + /* + * Read current TSSI (BBP 49). + */ + rt2800_bbp_read(rt2x00dev, 49, ¤t_tssi); + + /* + * Compare TSSI value (BBP49) with the compensation boundaries + * from the EEPROM and increase or decrease tx power. + */ + for (i = 0; i <= 3; i++) { + if (current_tssi > tssi_bounds[i]) + break; + } + + if (i == 4) { + for (i = 8; i >= 5; i--) { + if (current_tssi < tssi_bounds[i]) + break; + } + } + + return (i - 4) * step; +} + +static int rt2800_get_txpower_bw_comp(struct rt2x00_dev *rt2x00dev, + enum ieee80211_band band) +{ + u16 eeprom; + u8 comp_en; + u8 comp_type; + int comp_value = 0; + + rt2800_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom); + + /* + * HT40 compensation not required. + */ + if (eeprom == 0xffff || + !test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) + return 0; + + if (band == IEEE80211_BAND_2GHZ) { + comp_en = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_ENABLE_2G); + if (comp_en) { + comp_type = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_TYPE_2G); + comp_value = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_VALUE_2G); + if (!comp_type) + comp_value = -comp_value; + } + } else { + comp_en = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_ENABLE_5G); + if (comp_en) { + comp_type = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_TYPE_5G); + comp_value = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_VALUE_5G); + if (!comp_type) + comp_value = -comp_value; + } + } + + return comp_value; +} + +static int rt2800_get_txpower_reg_delta(struct rt2x00_dev *rt2x00dev, + int power_level, int max_power) +{ + int delta; + + if (rt2x00_has_cap_power_limit(rt2x00dev)) + return 0; + + /* + * XXX: We don't know the maximum transmit power of our hardware since + * the EEPROM doesn't expose it. We only know that we are calibrated + * to 100% tx power. + * + * Hence, we assume the regulatory limit that cfg80211 calulated for + * the current channel is our maximum and if we are requested to lower + * the value we just reduce our tx power accordingly. + */ + delta = power_level - max_power; + return min(delta, 0); +} + +static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b, + enum ieee80211_band band, int power_level, + u8 txpower, int delta) +{ + u16 eeprom; + u8 criterion; + u8 eirp_txpower; + u8 eirp_txpower_criterion; + u8 reg_limit; + + if (rt2x00_rt(rt2x00dev, RT3593)) + return min_t(u8, txpower, 0xc); + + if (rt2x00_has_cap_power_limit(rt2x00dev)) { + /* + * Check if eirp txpower exceed txpower_limit. + * We use OFDM 6M as criterion and its eirp txpower + * is stored at EEPROM_EIRP_MAX_TX_POWER. + * .11b data rate need add additional 4dbm + * when calculating eirp txpower. + */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + 1, &eeprom); + criterion = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE0); + + rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, + &eeprom); + + if (band == IEEE80211_BAND_2GHZ) + eirp_txpower_criterion = rt2x00_get_field16(eeprom, + EEPROM_EIRP_MAX_TX_POWER_2GHZ); + else + eirp_txpower_criterion = rt2x00_get_field16(eeprom, + EEPROM_EIRP_MAX_TX_POWER_5GHZ); + + eirp_txpower = eirp_txpower_criterion + (txpower - criterion) + + (is_rate_b ? 4 : 0) + delta; + + reg_limit = (eirp_txpower > power_level) ? + (eirp_txpower - power_level) : 0; + } else + reg_limit = 0; + + txpower = max(0, txpower + delta - reg_limit); + return min_t(u8, txpower, 0xc); +} + + +enum { + TX_PWR_CFG_0_IDX, + TX_PWR_CFG_1_IDX, + TX_PWR_CFG_2_IDX, + TX_PWR_CFG_3_IDX, + TX_PWR_CFG_4_IDX, + TX_PWR_CFG_5_IDX, + TX_PWR_CFG_6_IDX, + TX_PWR_CFG_7_IDX, + TX_PWR_CFG_8_IDX, + TX_PWR_CFG_9_IDX, + TX_PWR_CFG_0_EXT_IDX, + TX_PWR_CFG_1_EXT_IDX, + TX_PWR_CFG_2_EXT_IDX, + TX_PWR_CFG_3_EXT_IDX, + TX_PWR_CFG_4_EXT_IDX, + TX_PWR_CFG_IDX_COUNT, +}; + +static void rt2800_config_txpower_rt3593(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *chan, + int power_level) +{ + u8 txpower; + u16 eeprom; + u32 regs[TX_PWR_CFG_IDX_COUNT]; + unsigned int offset; + enum ieee80211_band band = chan->band; + int delta; + int i; + + memset(regs, '\0', sizeof(regs)); + + /* TODO: adapt TX power reduction from the rt28xx code */ + + /* calculate temperature compensation delta */ + delta = rt2800_get_gain_calibration_delta(rt2x00dev); + + if (band == IEEE80211_BAND_5GHZ) + offset = 16; + else + offset = 0; + + if (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) + offset += 8; + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset, &eeprom); + + /* CCK 1MBS,2MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK1_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK1_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_CCK1_CH2, txpower); + + /* CCK 5.5MBS,11MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK5_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK5_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_CCK5_CH2, txpower); + + /* OFDM 6MBS,9MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM6_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM6_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_OFDM6_CH2, txpower); + + /* OFDM 12MBS,18MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM12_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM12_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_OFDM12_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 1, &eeprom); + + /* OFDM 24MBS,36MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM24_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM24_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_OFDM24_CH2, txpower); + + /* OFDM 48MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM48_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM48_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_OFDM48_CH2, txpower); + + /* OFDM 54MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_OFDM54_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_OFDM54_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_OFDM54_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 2, &eeprom); + + /* MCS 0,1 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS0_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS0_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_MCS0_CH2, txpower); + + /* MCS 2,3 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS2_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS2_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_MCS2_CH2, txpower); + + /* MCS 4,5 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS4_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS4_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS4_CH2, txpower); + + /* MCS 6 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS6_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS6_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS6_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 3, &eeprom); + + /* MCS 7 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_MCS7_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_MCS7_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_MCS7_CH2, txpower); + + /* MCS 8,9 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS8_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS8_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS8_CH2, txpower); + + /* MCS 10,11 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS10_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS10_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS10_CH2, txpower); + + /* MCS 12,13 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS12_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS12_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_MCS12_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 4, &eeprom); + + /* MCS 14 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS14_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS14_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_MCS14_CH2, txpower); + + /* MCS 15 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS15_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS15_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS15_CH2, txpower); + + /* MCS 16,17 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS16_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS16_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS16_CH2, txpower); + + /* MCS 18,19 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS18_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS18_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS18_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 5, &eeprom); + + /* MCS 20,21 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS20_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS20_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS20_CH2, txpower); + + /* MCS 22 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS22_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS22_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS22_CH2, txpower); + + /* MCS 23 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS23_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS23_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS23_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 6, &eeprom); + + /* STBC, MCS 0,1 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC0_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC0_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_STBC0_CH2, txpower); + + /* STBC, MCS 2,3 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC2_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC2_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_STBC2_CH2, txpower); + + /* STBC, MCS 4,5 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE0, + txpower); + + /* STBC, MCS 6 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE2, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE3, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE2, + txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 7, &eeprom); + + /* STBC, MCS 7 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], + TX_PWR_CFG_9_STBC7_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], + TX_PWR_CFG_9_STBC7_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], + TX_PWR_CFG_9_STBC7_CH2, txpower); + + rt2800_register_write(rt2x00dev, TX_PWR_CFG_0, regs[TX_PWR_CFG_0_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_1, regs[TX_PWR_CFG_1_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_2, regs[TX_PWR_CFG_2_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_3, regs[TX_PWR_CFG_3_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_4, regs[TX_PWR_CFG_4_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_5, regs[TX_PWR_CFG_5_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_6, regs[TX_PWR_CFG_6_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_7, regs[TX_PWR_CFG_7_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_8, regs[TX_PWR_CFG_8_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_9, regs[TX_PWR_CFG_9_IDX]); + + rt2800_register_write(rt2x00dev, TX_PWR_CFG_0_EXT, + regs[TX_PWR_CFG_0_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_1_EXT, + regs[TX_PWR_CFG_1_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_2_EXT, + regs[TX_PWR_CFG_2_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_3_EXT, + regs[TX_PWR_CFG_3_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_4_EXT, + regs[TX_PWR_CFG_4_EXT_IDX]); + + for (i = 0; i < TX_PWR_CFG_IDX_COUNT; i++) + rt2x00_dbg(rt2x00dev, + "band:%cGHz, BW:%c0MHz, TX_PWR_CFG_%d%s = %08lx\n", + (band == IEEE80211_BAND_5GHZ) ? '5' : '2', + (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) ? + '4' : '2', + (i > TX_PWR_CFG_9_IDX) ? + (i - TX_PWR_CFG_9_IDX - 1) : i, + (i > TX_PWR_CFG_9_IDX) ? "_EXT" : "", + (unsigned long) regs[i]); +} + +/* + * We configure transmit power using MAC TX_PWR_CFG_{0,...,N} registers and + * BBP R1 register. TX_PWR_CFG_X allow to configure per rate TX power values, + * 4 bits for each rate (tune from 0 to 15 dBm). BBP_R1 controls transmit power + * for all rates, but allow to set only 4 discrete values: -12, -6, 0 and 6 dBm. + * Reference per rate transmit power values are located in the EEPROM at + * EEPROM_TXPOWER_BYRATE offset. We adjust them and BBP R1 settings according to + * current conditions (i.e. band, bandwidth, temperature, user settings). + */ +static void rt2800_config_txpower_rt28xx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *chan, + int power_level) +{ + u8 txpower, r1; + u16 eeprom; + u32 reg, offset; + int i, is_rate_b, delta, power_ctrl; + enum ieee80211_band band = chan->band; + + /* + * Calculate HT40 compensation. For 40MHz we need to add or subtract + * value read from EEPROM (different for 2GHz and for 5GHz). + */ + delta = rt2800_get_txpower_bw_comp(rt2x00dev, band); + + /* + * Calculate temperature compensation. Depends on measurement of current + * TSSI (Transmitter Signal Strength Indication) we know TX power (due + * to temperature or maybe other factors) is smaller or bigger than + * expected. We adjust it, based on TSSI reference and boundaries values + * provided in EEPROM. + */ + switch (rt2x00dev->chip.rt) { + case RT2860: + case RT2872: + case RT2883: + case RT3070: + case RT3071: + case RT3090: + case RT3572: + delta += rt2800_get_gain_calibration_delta(rt2x00dev); + break; + default: + /* TODO: temperature compensation code for other chips. */ + break; + } + + /* + * Decrease power according to user settings, on devices with unknown + * maximum tx power. For other devices we take user power_level into + * consideration on rt2800_compensate_txpower(). + */ + delta += rt2800_get_txpower_reg_delta(rt2x00dev, power_level, + chan->max_power); + + /* + * BBP_R1 controls TX power for all rates, it allow to set the following + * gains -12, -6, 0, +6 dBm by setting values 2, 1, 0, 3 respectively. + * + * TODO: we do not use +6 dBm option to do not increase power beyond + * regulatory limit, however this could be utilized for devices with + * CAPABILITY_POWER_LIMIT. + */ + if (delta <= -12) { + power_ctrl = 2; + delta += 12; + } else if (delta <= -6) { + power_ctrl = 1; + delta += 6; + } else { + power_ctrl = 0; + } + rt2800_bbp_read(rt2x00dev, 1, &r1); + rt2x00_set_field8(&r1, BBP1_TX_POWER_CTRL, power_ctrl); + rt2800_bbp_write(rt2x00dev, 1, r1); + + offset = TX_PWR_CFG_0; + + for (i = 0; i < EEPROM_TXPOWER_BYRATE_SIZE; i += 2) { + /* just to be safe */ + if (offset > TX_PWR_CFG_4) + break; + + rt2800_register_read(rt2x00dev, offset, ®); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + i, &eeprom); + + is_rate_b = i ? 0 : 1; + /* + * TX_PWR_CFG_0: 1MBS, TX_PWR_CFG_1: 24MBS, + * TX_PWR_CFG_2: MCS4, TX_PWR_CFG_3: MCS12, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE0, txpower); + + /* + * TX_PWR_CFG_0: 2MBS, TX_PWR_CFG_1: 36MBS, + * TX_PWR_CFG_2: MCS5, TX_PWR_CFG_3: MCS13, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE1, txpower); + + /* + * TX_PWR_CFG_0: 5.5MBS, TX_PWR_CFG_1: 48MBS, + * TX_PWR_CFG_2: MCS6, TX_PWR_CFG_3: MCS14, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE2, txpower); + + /* + * TX_PWR_CFG_0: 11MBS, TX_PWR_CFG_1: 54MBS, + * TX_PWR_CFG_2: MCS7, TX_PWR_CFG_3: MCS15, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE3, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + i + 1, &eeprom); + + is_rate_b = 0; + /* + * TX_PWR_CFG_0: 6MBS, TX_PWR_CFG_1: MCS0, + * TX_PWR_CFG_2: MCS8, TX_PWR_CFG_3: unknown, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE4, txpower); + + /* + * TX_PWR_CFG_0: 9MBS, TX_PWR_CFG_1: MCS1, + * TX_PWR_CFG_2: MCS9, TX_PWR_CFG_3: unknown, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE5, txpower); + + /* + * TX_PWR_CFG_0: 12MBS, TX_PWR_CFG_1: MCS2, + * TX_PWR_CFG_2: MCS10, TX_PWR_CFG_3: unknown, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE6, txpower); + + /* + * TX_PWR_CFG_0: 18MBS, TX_PWR_CFG_1: MCS3, + * TX_PWR_CFG_2: MCS11, TX_PWR_CFG_3: unknown, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE7, txpower); + + rt2800_register_write(rt2x00dev, offset, reg); + + /* next TX_PWR_CFG register */ + offset += 4; + } +} + +static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *chan, + int power_level) +{ + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_config_txpower_rt3593(rt2x00dev, chan, power_level); + else + rt2800_config_txpower_rt28xx(rt2x00dev, chan, power_level); +} + +void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev) +{ + rt2800_config_txpower(rt2x00dev, rt2x00dev->hw->conf.chandef.chan, + rt2x00dev->tx_power); +} +EXPORT_SYMBOL_GPL(rt2800_gain_calibration); + +void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) +{ + u32 tx_pin; + u8 rfcsr; + + /* + * A voltage-controlled oscillator(VCO) is an electronic oscillator + * designed to be controlled in oscillation frequency by a voltage + * input. Maybe the temperature will affect the frequency of + * oscillation to be shifted. The VCO calibration will be called + * periodically to adjust the frequency to be precision. + */ + + rt2800_register_read(rt2x00dev, TX_PIN_CFG, &tx_pin); + tx_pin &= TX_PIN_CFG_PA_PE_DISABLE; + rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); + + switch (rt2x00dev->chip.rf) { + case RF2020: + case RF3020: + case RF3021: + case RF3022: + case RF3320: + case RF3052: + rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); + rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); + break; + case RF3053: + case RF3070: + case RF3290: + case RF5360: + case RF5362: + case RF5370: + case RF5372: + case RF5390: + case RF5392: + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + break; + default: + return; + } + + mdelay(1); + + rt2800_register_read(rt2x00dev, TX_PIN_CFG, &tx_pin); + if (rt2x00dev->rf_channel <= 14) { + switch (rt2x00dev->default_ant.tx_chain_num) { + case 3: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G2_EN, 1); + /* fall through */ + case 2: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, 1); + /* fall through */ + case 1: + default: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); + break; + } + } else { + switch (rt2x00dev->default_ant.tx_chain_num) { + case 3: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A2_EN, 1); + /* fall through */ + case 2: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, 1); + /* fall through */ + case 1: + default: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, 1); + break; + } + } + rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); + +} +EXPORT_SYMBOL_GPL(rt2800_vco_calibration); + +static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, TX_RTY_CFG, ®); + rt2x00_set_field32(®, TX_RTY_CFG_SHORT_RTY_LIMIT, + libconf->conf->short_frame_max_tx_count); + rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_LIMIT, + libconf->conf->long_frame_max_tx_count); + rt2800_register_write(rt2x00dev, TX_RTY_CFG, reg); +} + +static void rt2800_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; + u32 reg; + + if (state == STATE_SLEEP) { + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0); + + rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 5); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, + libconf->conf->listen_interval - 1); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 1); + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); + + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); + } else { + rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 0); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, 0); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 0); + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); + + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); + } +} + +void rt2800_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags) +{ + /* Always recalculate LNA gain before changing configuration */ + rt2800_config_lna_gain(rt2x00dev, libconf); + + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) { + rt2800_config_channel(rt2x00dev, libconf->conf, + &libconf->rf, &libconf->channel); + rt2800_config_txpower(rt2x00dev, libconf->conf->chandef.chan, + libconf->conf->power_level); + } + if (flags & IEEE80211_CONF_CHANGE_POWER) + rt2800_config_txpower(rt2x00dev, libconf->conf->chandef.chan, + libconf->conf->power_level); + if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + rt2800_config_retry_limit(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt2800_config_ps(rt2x00dev, libconf); +} +EXPORT_SYMBOL_GPL(rt2800_config); + +/* + * Link tuning + */ +void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) +{ + u32 reg; + + /* + * Update FCS error count from register. + */ + rt2800_register_read(rt2x00dev, RX_STA_CNT0, ®); + qual->rx_failed = rt2x00_get_field32(reg, RX_STA_CNT0_CRC_ERR); +} +EXPORT_SYMBOL_GPL(rt2800_link_stats); + +static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) +{ + u8 vgc; + + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { + if (rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT3390) || + rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT5592)) + vgc = 0x1c + (2 * rt2x00dev->lna_gain); + else + vgc = 0x2e + rt2x00dev->lna_gain; + } else { /* 5GHZ band */ + if (rt2x00_rt(rt2x00dev, RT3593)) + vgc = 0x20 + (rt2x00dev->lna_gain * 5) / 3; + else if (rt2x00_rt(rt2x00dev, RT5592)) + vgc = 0x24 + (2 * rt2x00dev->lna_gain); + else { + if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) + vgc = 0x32 + (rt2x00dev->lna_gain * 5) / 3; + else + vgc = 0x3a + (rt2x00dev->lna_gain * 5) / 3; + } + } + + return vgc; +} + +static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, u8 vgc_level) +{ + if (qual->vgc_level != vgc_level) { + if (rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, + vgc_level); + } else if (rt2x00_rt(rt2x00dev, RT5592)) { + rt2800_bbp_write(rt2x00dev, 83, qual->rssi > -65 ? 0x4a : 0x7a); + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, vgc_level); + } else { + rt2800_bbp_write(rt2x00dev, 66, vgc_level); + } + + qual->vgc_level = vgc_level; + qual->vgc_level_reg = vgc_level; + } +} + +void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) +{ + rt2800_set_vgc(rt2x00dev, qual, rt2800_get_default_vgc(rt2x00dev)); +} +EXPORT_SYMBOL_GPL(rt2800_reset_tuner); + +void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, + const u32 count) +{ + u8 vgc; + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) + return; + + /* When RSSI is better than a certain threshold, increase VGC + * with a chip specific value in order to improve the balance + * between sensibility and noise isolation. + */ + + vgc = rt2800_get_default_vgc(rt2x00dev); + + switch (rt2x00dev->chip.rt) { + case RT3572: + case RT3593: + if (qual->rssi > -65) { + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) + vgc += 0x20; + else + vgc += 0x10; + } + break; + + case RT5592: + if (qual->rssi > -65) + vgc += 0x20; + break; + + default: + if (qual->rssi > -80) + vgc += 0x10; + break; + } + + rt2800_set_vgc(rt2x00dev, qual, vgc); +} +EXPORT_SYMBOL_GPL(rt2800_link_tuner); + +/* + * Initialization functions. + */ +static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 eeprom; + unsigned int i; + int ret; + + rt2800_disable_wpdma(rt2x00dev); + + ret = rt2800_drv_init_registers(rt2x00dev); + if (ret) + return ret; + + rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f); + rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); + + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); + + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, 1600); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TX_TIME_COMPENSATE, 0); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + rt2800_config_filter(rt2x00dev, FIF_ALLMULTI); + + rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, ®); + rt2x00_set_field32(®, BKOFF_SLOT_CFG_SLOT_TIME, 9); + rt2x00_set_field32(®, BKOFF_SLOT_CFG_CC_DELAY_TIME, 2); + rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg); + + if (rt2x00_rt(rt2x00dev, RT3290)) { + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + if (rt2x00_get_field32(reg, WLAN_EN) == 1) { + rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 1); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + } + + rt2800_register_read(rt2x00dev, CMB_CTRL, ®); + if (!(rt2x00_get_field32(reg, LDO0_EN) == 1)) { + rt2x00_set_field32(®, LDO0_EN, 1); + rt2x00_set_field32(®, LDO_BGSEL, 3); + rt2800_register_write(rt2x00dev, CMB_CTRL, reg); + } + + rt2800_register_read(rt2x00dev, OSC_CTRL, ®); + rt2x00_set_field32(®, OSC_ROSC_EN, 1); + rt2x00_set_field32(®, OSC_CAL_REQ, 1); + rt2x00_set_field32(®, OSC_REF_CYCLE, 0x27); + rt2800_register_write(rt2x00dev, OSC_CTRL, reg); + + rt2800_register_read(rt2x00dev, COEX_CFG0, ®); + rt2x00_set_field32(®, COEX_CFG_ANT, 0x5e); + rt2800_register_write(rt2x00dev, COEX_CFG0, reg); + + rt2800_register_read(rt2x00dev, COEX_CFG2, ®); + rt2x00_set_field32(®, BT_COEX_CFG1, 0x00); + rt2x00_set_field32(®, BT_COEX_CFG0, 0x17); + rt2x00_set_field32(®, WL_COEX_CFG1, 0x93); + rt2x00_set_field32(®, WL_COEX_CFG0, 0x7f); + rt2800_register_write(rt2x00dev, COEX_CFG2, reg); + + rt2800_register_read(rt2x00dev, PLL_CTRL, ®); + rt2x00_set_field32(®, PLL_CONTROL, 1); + rt2800_register_write(rt2x00dev, PLL_CTRL, reg); + } + + if (rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT3390)) { + + if (rt2x00_rt(rt2x00dev, RT3290)) + rt2800_register_write(rt2x00dev, TX_SW_CFG0, + 0x00000404); + else + rt2800_register_write(rt2x00dev, TX_SW_CFG0, + 0x00000400); + + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); + if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, + &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST)) + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x0000002c); + else + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x0000000f); + } else { + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + } + } else if (rt2x00_rt(rt2x00dev, RT3070)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); + + if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x0000002c); + } else { + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + } + } else if (rt2800_is_305x_soc(rt2x00dev)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000030); + } else if (rt2x00_rt(rt2x00dev, RT3352)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000402); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + } else if (rt2x00_rt(rt2x00dev, RT3572)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + } else if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000402); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); + if (rt2x00_rt_rev_lt(rt2x00dev, RT3593, REV_RT3593E)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, + &eeprom); + if (rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_DAC_TEST)) + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x0000001f); + else + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x0000000f); + } else { + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x00000000); + } + } else if (rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT5592)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + } else { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000000); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + } + + rt2800_register_read(rt2x00dev, TX_LINK_CFG, ®); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB_LIFETIME, 32); + rt2x00_set_field32(®, TX_LINK_CFG_MFB_ENABLE, 0); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_UMFS_ENABLE, 0); + rt2x00_set_field32(®, TX_LINK_CFG_TX_MRQ_EN, 0); + rt2x00_set_field32(®, TX_LINK_CFG_TX_RDG_EN, 0); + rt2x00_set_field32(®, TX_LINK_CFG_TX_CF_ACK_EN, 1); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB, 0); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFS, 0); + rt2800_register_write(rt2x00dev, TX_LINK_CFG, reg); + + rt2800_register_read(rt2x00dev, TX_TIMEOUT_CFG, ®); + rt2x00_set_field32(®, TX_TIMEOUT_CFG_MPDU_LIFETIME, 9); + rt2x00_set_field32(®, TX_TIMEOUT_CFG_RX_ACK_TIMEOUT, 32); + rt2x00_set_field32(®, TX_TIMEOUT_CFG_TX_OP_TIMEOUT, 10); + rt2800_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg); + + rt2800_register_read(rt2x00dev, MAX_LEN_CFG, ®); + rt2x00_set_field32(®, MAX_LEN_CFG_MAX_MPDU, AGGREGATION_SIZE); + if (rt2x00_rt_rev_gte(rt2x00dev, RT2872, REV_RT2872E) || + rt2x00_rt(rt2x00dev, RT2883) || + rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070E)) + rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 2); + else + rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 1); + rt2x00_set_field32(®, MAX_LEN_CFG_MIN_PSDU, 0); + rt2x00_set_field32(®, MAX_LEN_CFG_MIN_MPDU, 0); + rt2800_register_write(rt2x00dev, MAX_LEN_CFG, reg); + + rt2800_register_read(rt2x00dev, LED_CFG, ®); + rt2x00_set_field32(®, LED_CFG_ON_PERIOD, 70); + rt2x00_set_field32(®, LED_CFG_OFF_PERIOD, 30); + rt2x00_set_field32(®, LED_CFG_SLOW_BLINK_PERIOD, 3); + rt2x00_set_field32(®, LED_CFG_R_LED_MODE, 3); + rt2x00_set_field32(®, LED_CFG_G_LED_MODE, 3); + rt2x00_set_field32(®, LED_CFG_Y_LED_MODE, 3); + rt2x00_set_field32(®, LED_CFG_LED_POLAR, 1); + rt2800_register_write(rt2x00dev, LED_CFG, reg); + + rt2800_register_write(rt2x00dev, PBF_MAX_PCNT, 0x1f3fbf9f); + + rt2800_register_read(rt2x00dev, TX_RTY_CFG, ®); + rt2x00_set_field32(®, TX_RTY_CFG_SHORT_RTY_LIMIT, 15); + rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_LIMIT, 31); + rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_THRE, 2000); + rt2x00_set_field32(®, TX_RTY_CFG_NON_AGG_RTY_MODE, 0); + rt2x00_set_field32(®, TX_RTY_CFG_AGG_RTY_MODE, 0); + rt2x00_set_field32(®, TX_RTY_CFG_TX_AUTO_FB_ENABLE, 1); + rt2800_register_write(rt2x00dev, TX_RTY_CFG, reg); + + rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); + rt2x00_set_field32(®, AUTO_RSP_CFG_AUTORESPONDER, 1); + rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, 1); + rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MMODE, 0); + rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MREF, 0); + rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, 1); + rt2x00_set_field32(®, AUTO_RSP_CFG_DUAL_CTS_EN, 0); + rt2x00_set_field32(®, AUTO_RSP_CFG_ACK_CTS_PSM_BIT, 0); + rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); + + rt2800_register_read(rt2x00dev, CCK_PROT_CFG, ®); + rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_RATE, 3); + rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM40, 0); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF40, 0); + rt2x00_set_field32(®, CCK_PROT_CFG_RTS_TH_EN, 1); + rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_RATE, 3); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM40, 0); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF40, 0); + rt2x00_set_field32(®, OFDM_PROT_CFG_RTS_TH_EN, 1); + rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_RATE, 0x4004); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM40, 0); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF40, 0); + rt2x00_set_field32(®, MM20_PROT_CFG_RTS_TH_EN, 0); + rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_RATE, 0x4084); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM40, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF40, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_RTS_TH_EN, 0); + rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_RATE, 0x4004); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM40, 0); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF40, 0); + rt2x00_set_field32(®, GF20_PROT_CFG_RTS_TH_EN, 0); + rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_RATE, 0x4084); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM40, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF40, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_RTS_TH_EN, 0); + rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); + + if (rt2x00_is_usb(rt2x00dev)) { + rt2800_register_write(rt2x00dev, PBF_CFG, 0xf40006); + + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 3); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_BIG_ENDIAN, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_HDR_SCATTER, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_HDR_SEG_LEN, 0); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); + } + + /* + * The legacy driver also sets TXOP_CTRL_CFG_RESERVED_TRUN_EN to 1 + * although it is reserved. + */ + rt2800_register_read(rt2x00dev, TXOP_CTRL_CFG, ®); + rt2x00_set_field32(®, TXOP_CTRL_CFG_TIMEOUT_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_AC_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_TXRATEGRP_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_USER_MODE_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_MIMO_PS_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_RESERVED_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_LSIG_TXOP_EN, 0); + rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CCA_EN, 0); + rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CCA_DLY, 88); + rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CWMIN, 0); + rt2800_register_write(rt2x00dev, TXOP_CTRL_CFG, reg); + + reg = rt2x00_rt(rt2x00dev, RT5592) ? 0x00000082 : 0x00000002; + rt2800_register_write(rt2x00dev, TXOP_HLDR_ET, reg); + + rt2800_register_read(rt2x00dev, TX_RTS_CFG, ®); + rt2x00_set_field32(®, TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT, 32); + rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, + IEEE80211_MAX_RTS_THRESHOLD); + rt2x00_set_field32(®, TX_RTS_CFG_RTS_FBK_EN, 0); + rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg); + + rt2800_register_write(rt2x00dev, EXP_ACK_TIME, 0x002400ca); + + /* + * Usually the CCK SIFS time should be set to 10 and the OFDM SIFS + * time should be set to 16. However, the original Ralink driver uses + * 16 for both and indeed using a value of 10 for CCK SIFS results in + * connection problems with 11g + CTS protection. Hence, use the same + * defaults as the Ralink driver: 16 for both, CCK and OFDM SIFS. + */ + rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, ®); + rt2x00_set_field32(®, XIFS_TIME_CFG_CCKM_SIFS_TIME, 16); + rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_SIFS_TIME, 16); + rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_XIFS_TIME, 4); + rt2x00_set_field32(®, XIFS_TIME_CFG_EIFS, 314); + rt2x00_set_field32(®, XIFS_TIME_CFG_BB_RXEND_ENABLE, 1); + rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg); + + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); + + /* + * ASIC will keep garbage value after boot, clear encryption keys. + */ + for (i = 0; i < 4; i++) + rt2800_register_write(rt2x00dev, + SHARED_KEY_MODE_ENTRY(i), 0); + + for (i = 0; i < 256; i++) { + rt2800_config_wcid(rt2x00dev, NULL, i); + rt2800_delete_wcid_attr(rt2x00dev, i); + rt2800_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0); + } + + /* + * Clear all beacons + */ + for (i = 0; i < 8; i++) + rt2800_clear_beacon_register(rt2x00dev, i); + + if (rt2x00_is_usb(rt2x00dev)) { + rt2800_register_read(rt2x00dev, US_CYC_CNT, ®); + rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, 30); + rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); + } else if (rt2x00_is_pcie(rt2x00dev)) { + rt2800_register_read(rt2x00dev, US_CYC_CNT, ®); + rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, 125); + rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); + } + + rt2800_register_read(rt2x00dev, HT_FBK_CFG0, ®); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS0FBK, 0); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS1FBK, 0); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS2FBK, 1); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS3FBK, 2); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS4FBK, 3); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS5FBK, 4); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS6FBK, 5); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS7FBK, 6); + rt2800_register_write(rt2x00dev, HT_FBK_CFG0, reg); + + rt2800_register_read(rt2x00dev, HT_FBK_CFG1, ®); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS8FBK, 8); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS9FBK, 8); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS10FBK, 9); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS11FBK, 10); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS12FBK, 11); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS13FBK, 12); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS14FBK, 13); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS15FBK, 14); + rt2800_register_write(rt2x00dev, HT_FBK_CFG1, reg); + + rt2800_register_read(rt2x00dev, LG_FBK_CFG0, ®); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS0FBK, 8); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS1FBK, 8); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS2FBK, 9); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS3FBK, 10); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS4FBK, 11); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS5FBK, 12); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS6FBK, 13); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS7FBK, 14); + rt2800_register_write(rt2x00dev, LG_FBK_CFG0, reg); + + rt2800_register_read(rt2x00dev, LG_FBK_CFG1, ®); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS0FBK, 0); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS1FBK, 0); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS2FBK, 1); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS3FBK, 2); + rt2800_register_write(rt2x00dev, LG_FBK_CFG1, reg); + + /* + * Do not force the BA window size, we use the TXWI to set it + */ + rt2800_register_read(rt2x00dev, AMPDU_BA_WINSIZE, ®); + rt2x00_set_field32(®, AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE, 0); + rt2x00_set_field32(®, AMPDU_BA_WINSIZE_FORCE_WINSIZE, 0); + rt2800_register_write(rt2x00dev, AMPDU_BA_WINSIZE, reg); + + /* + * We must clear the error counters. + * These registers are cleared on read, + * so we may pass a useless variable to store the value. + */ + rt2800_register_read(rt2x00dev, RX_STA_CNT0, ®); + rt2800_register_read(rt2x00dev, RX_STA_CNT1, ®); + rt2800_register_read(rt2x00dev, RX_STA_CNT2, ®); + rt2800_register_read(rt2x00dev, TX_STA_CNT0, ®); + rt2800_register_read(rt2x00dev, TX_STA_CNT1, ®); + rt2800_register_read(rt2x00dev, TX_STA_CNT2, ®); + + /* + * Setup leadtime for pre tbtt interrupt to 6ms + */ + rt2800_register_read(rt2x00dev, INT_TIMER_CFG, ®); + rt2x00_set_field32(®, INT_TIMER_CFG_PRE_TBTT_TIMER, 6 << 4); + rt2800_register_write(rt2x00dev, INT_TIMER_CFG, reg); + + /* + * Set up channel statistics timer + */ + rt2800_register_read(rt2x00dev, CH_TIME_CFG, ®); + rt2x00_set_field32(®, CH_TIME_CFG_EIFS_BUSY, 1); + rt2x00_set_field32(®, CH_TIME_CFG_NAV_BUSY, 1); + rt2x00_set_field32(®, CH_TIME_CFG_RX_BUSY, 1); + rt2x00_set_field32(®, CH_TIME_CFG_TX_BUSY, 1); + rt2x00_set_field32(®, CH_TIME_CFG_TMR_EN, 1); + rt2800_register_write(rt2x00dev, CH_TIME_CFG, reg); + + return 0; +} + +static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u32 reg; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, MAC_STATUS_CFG, ®); + if (!rt2x00_get_field32(reg, MAC_STATUS_CFG_BBP_RF_BUSY)) + return 0; + + udelay(REGISTER_BUSY_DELAY); + } + + rt2x00_err(rt2x00dev, "BBP/RF register access failed, aborting\n"); + return -EACCES; +} + +static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u8 value; + + /* + * BBP was enabled after firmware was loaded, + * but we need to reactivate it now. + */ + rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + msleep(1); + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + return 0; + udelay(REGISTER_BUSY_DELAY); + } + + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); + return -EACCES; +} + +static void rt2800_bbp4_mac_if_ctrl(struct rt2x00_dev *rt2x00dev) +{ + u8 value; + + rt2800_bbp_read(rt2x00dev, 4, &value); + rt2x00_set_field8(&value, BBP4_MAC_IF_CTRL, 1); + rt2800_bbp_write(rt2x00dev, 4, value); +} + +static void rt2800_init_freq_calibration(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 142, 1); + rt2800_bbp_write(rt2x00dev, 143, 57); +} + +static void rt2800_init_bbp_5592_glrt(struct rt2x00_dev *rt2x00dev) +{ + const u8 glrt_table[] = { + 0xE0, 0x1F, 0X38, 0x32, 0x08, 0x28, 0x19, 0x0A, 0xFF, 0x00, /* 128 ~ 137 */ + 0x16, 0x10, 0x10, 0x0B, 0x36, 0x2C, 0x26, 0x24, 0x42, 0x36, /* 138 ~ 147 */ + 0x30, 0x2D, 0x4C, 0x46, 0x3D, 0x40, 0x3E, 0x42, 0x3D, 0x40, /* 148 ~ 157 */ + 0X3C, 0x34, 0x2C, 0x2F, 0x3C, 0x35, 0x2E, 0x2A, 0x49, 0x41, /* 158 ~ 167 */ + 0x36, 0x31, 0x30, 0x30, 0x0E, 0x0D, 0x28, 0x21, 0x1C, 0x16, /* 168 ~ 177 */ + 0x50, 0x4A, 0x43, 0x40, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, /* 178 ~ 187 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 188 ~ 197 */ + 0x00, 0x00, 0x7D, 0x14, 0x32, 0x2C, 0x36, 0x4C, 0x43, 0x2C, /* 198 ~ 207 */ + 0x2E, 0x36, 0x30, 0x6E, /* 208 ~ 211 */ + }; + int i; + + for (i = 0; i < ARRAY_SIZE(glrt_table); i++) { + rt2800_bbp_write(rt2x00dev, 195, 128 + i); + rt2800_bbp_write(rt2x00dev, 196, glrt_table[i]); + } +}; + +static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 65, 0x2C); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + rt2800_bbp_write(rt2x00dev, 68, 0x0B); + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + rt2800_bbp_write(rt2x00dev, 81, 0x37); + rt2800_bbp_write(rt2x00dev, 82, 0x62); + rt2800_bbp_write(rt2x00dev, 83, 0x6A); + rt2800_bbp_write(rt2x00dev, 84, 0x99); + rt2800_bbp_write(rt2x00dev, 86, 0x00); + rt2800_bbp_write(rt2x00dev, 91, 0x04); + rt2800_bbp_write(rt2x00dev, 92, 0x00); + rt2800_bbp_write(rt2x00dev, 103, 0x00); + rt2800_bbp_write(rt2x00dev, 105, 0x05); + rt2800_bbp_write(rt2x00dev, 106, 0x35); +} + +static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev) +{ + u16 eeprom; + u8 value; + + rt2800_bbp_read(rt2x00dev, 138, &value); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) + value |= 0x20; + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) + value &= ~0x02; + rt2800_bbp_write(rt2x00dev, 138, value); +} + +static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 78, 0x0e); + rt2800_bbp_write(rt2x00dev, 80, 0x08); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 105, 0x01); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); +} + +static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { + rt2800_bbp_write(rt2x00dev, 69, 0x16); + rt2800_bbp_write(rt2x00dev, 73, 0x12); + } else { + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + } + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 81, 0x37); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D)) + rt2800_bbp_write(rt2x00dev, 84, 0x19); + else + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); +} + +static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) || + rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + else + rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); + + if (rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090)) + rt2800_disable_unused_dac_adc(rt2x00dev); +} + +static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) +{ + u8 value; + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + + rt2800_bbp_write(rt2x00dev, 77, 0x58); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 74, 0x0b); + rt2800_bbp_write(rt2x00dev, 79, 0x18); + rt2800_bbp_write(rt2x00dev, 80, 0x09); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x7a); + + rt2800_bbp_write(rt2x00dev, 84, 0x9a); + + rt2800_bbp_write(rt2x00dev, 86, 0x38); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 104, 0x92); + + rt2800_bbp_write(rt2x00dev, 105, 0x1c); + + rt2800_bbp_write(rt2x00dev, 106, 0x03); + + rt2800_bbp_write(rt2x00dev, 128, 0x12); + + rt2800_bbp_write(rt2x00dev, 67, 0x24); + rt2800_bbp_write(rt2x00dev, 143, 0x04); + rt2800_bbp_write(rt2x00dev, 142, 0x99); + rt2800_bbp_write(rt2x00dev, 150, 0x30); + rt2800_bbp_write(rt2x00dev, 151, 0x2e); + rt2800_bbp_write(rt2x00dev, 152, 0x20); + rt2800_bbp_write(rt2x00dev, 153, 0x34); + rt2800_bbp_write(rt2x00dev, 154, 0x40); + rt2800_bbp_write(rt2x00dev, 155, 0x3b); + rt2800_bbp_write(rt2x00dev, 253, 0x04); + + rt2800_bbp_read(rt2x00dev, 47, &value); + rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1); + rt2800_bbp_write(rt2x00dev, 47, value); + + /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */ + rt2800_bbp_read(rt2x00dev, 3, &value); + rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1); + rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1); + rt2800_bbp_write(rt2x00dev, 3, value); +} + +static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 3, 0x00); + rt2800_bbp_write(rt2x00dev, 4, 0x50); + + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 47, 0x48); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + + rt2800_bbp_write(rt2x00dev, 77, 0x59); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 78, 0x0e); + rt2800_bbp_write(rt2x00dev, 80, 0x08); + rt2800_bbp_write(rt2x00dev, 81, 0x37); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x38); + + rt2800_bbp_write(rt2x00dev, 88, 0x90); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 104, 0x92); + + rt2800_bbp_write(rt2x00dev, 105, 0x34); + + rt2800_bbp_write(rt2x00dev, 106, 0x05); + + rt2800_bbp_write(rt2x00dev, 120, 0x50); + + rt2800_bbp_write(rt2x00dev, 137, 0x0f); + + rt2800_bbp_write(rt2x00dev, 163, 0xbd); + /* Set ITxBF timeout to 0x9c40=1000msec */ + rt2800_bbp_write(rt2x00dev, 179, 0x02); + rt2800_bbp_write(rt2x00dev, 180, 0x00); + rt2800_bbp_write(rt2x00dev, 182, 0x40); + rt2800_bbp_write(rt2x00dev, 180, 0x01); + rt2800_bbp_write(rt2x00dev, 182, 0x9c); + rt2800_bbp_write(rt2x00dev, 179, 0x00); + /* Reprogram the inband interface to put right values in RXWI */ + rt2800_bbp_write(rt2x00dev, 142, 0x04); + rt2800_bbp_write(rt2x00dev, 143, 0x3b); + rt2800_bbp_write(rt2x00dev, 142, 0x06); + rt2800_bbp_write(rt2x00dev, 143, 0xa0); + rt2800_bbp_write(rt2x00dev, 142, 0x07); + rt2800_bbp_write(rt2x00dev, 143, 0xa1); + rt2800_bbp_write(rt2x00dev, 142, 0x08); + rt2800_bbp_write(rt2x00dev, 143, 0xa2); + + rt2800_bbp_write(rt2x00dev, 148, 0xc8); +} + +static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + if (rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + else + rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); + + rt2800_disable_unused_dac_adc(rt2x00dev); +} + +static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); + + rt2800_disable_unused_dac_adc(rt2x00dev); +} + +static void rt2800_init_bbp_3593(struct rt2x00_dev *rt2x00dev) +{ + rt2800_init_bbp_early(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + rt2800_bbp_write(rt2x00dev, 137, 0x0f); + + rt2800_bbp_write(rt2x00dev, 84, 0x19); + + /* Enable DC filter */ + if (rt2x00_rt_rev_gte(rt2x00dev, RT3593, REV_RT3593E)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); +} + +static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) +{ + int ant, div_mode; + u16 eeprom; + u8 value; + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + + rt2800_bbp_write(rt2x00dev, 77, 0x59); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x7a); + + rt2800_bbp_write(rt2x00dev, 84, 0x9a); + + rt2800_bbp_write(rt2x00dev, 86, 0x38); + + if (rt2x00_rt(rt2x00dev, RT5392)) + rt2800_bbp_write(rt2x00dev, 88, 0x90); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); + + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2800_bbp_write(rt2x00dev, 95, 0x9a); + rt2800_bbp_write(rt2x00dev, 98, 0x12); + } + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 104, 0x92); + + rt2800_bbp_write(rt2x00dev, 105, 0x3c); + + if (rt2x00_rt(rt2x00dev, RT5390)) + rt2800_bbp_write(rt2x00dev, 106, 0x03); + else if (rt2x00_rt(rt2x00dev, RT5392)) + rt2800_bbp_write(rt2x00dev, 106, 0x12); + else + WARN_ON(1); + + rt2800_bbp_write(rt2x00dev, 128, 0x12); + + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2800_bbp_write(rt2x00dev, 134, 0xd0); + rt2800_bbp_write(rt2x00dev, 135, 0xf6); + } + + rt2800_disable_unused_dac_adc(rt2x00dev); + + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + div_mode = rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_ANT_DIVERSITY); + ant = (div_mode == 3) ? 1 : 0; + + /* check if this is a Bluetooth combo card */ + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { + u32 reg; + + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); + rt2x00_set_field32(®, GPIO_CTRL_DIR6, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL3, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL6, 0); + if (ant == 0) + rt2x00_set_field32(®, GPIO_CTRL_VAL3, 1); + else if (ant == 1) + rt2x00_set_field32(®, GPIO_CTRL_VAL6, 1); + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + } + + /* This chip has hardware antenna diversity*/ + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { + rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */ + rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */ + rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */ + } + + rt2800_bbp_read(rt2x00dev, 152, &value); + if (ant == 0) + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); + else + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); + rt2800_bbp_write(rt2x00dev, 152, value); + + rt2800_init_freq_calibration(rt2x00dev); +} + +static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) +{ + int ant, div_mode; + u16 eeprom; + u8 value; + + rt2800_init_bbp_early(rt2x00dev); + + rt2800_bbp_read(rt2x00dev, 105, &value); + rt2x00_set_field8(&value, BBP105_MLD, + rt2x00dev->default_ant.rx_chain_num == 2); + rt2800_bbp_write(rt2x00dev, 105, value); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 20, 0x06); + rt2800_bbp_write(rt2x00dev, 31, 0x08); + rt2800_bbp_write(rt2x00dev, 65, 0x2C); + rt2800_bbp_write(rt2x00dev, 68, 0xDD); + rt2800_bbp_write(rt2x00dev, 69, 0x1A); + rt2800_bbp_write(rt2x00dev, 70, 0x05); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 74, 0x0F); + rt2800_bbp_write(rt2x00dev, 75, 0x4F); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + rt2800_bbp_write(rt2x00dev, 77, 0x59); + rt2800_bbp_write(rt2x00dev, 84, 0x9A); + rt2800_bbp_write(rt2x00dev, 86, 0x38); + rt2800_bbp_write(rt2x00dev, 88, 0x90); + rt2800_bbp_write(rt2x00dev, 91, 0x04); + rt2800_bbp_write(rt2x00dev, 92, 0x02); + rt2800_bbp_write(rt2x00dev, 95, 0x9a); + rt2800_bbp_write(rt2x00dev, 98, 0x12); + rt2800_bbp_write(rt2x00dev, 103, 0xC0); + rt2800_bbp_write(rt2x00dev, 104, 0x92); + /* FIXME BBP105 owerwrite */ + rt2800_bbp_write(rt2x00dev, 105, 0x3C); + rt2800_bbp_write(rt2x00dev, 106, 0x35); + rt2800_bbp_write(rt2x00dev, 128, 0x12); + rt2800_bbp_write(rt2x00dev, 134, 0xD0); + rt2800_bbp_write(rt2x00dev, 135, 0xF6); + rt2800_bbp_write(rt2x00dev, 137, 0x0F); + + /* Initialize GLRT (Generalized Likehood Radio Test) */ + rt2800_init_bbp_5592_glrt(rt2x00dev); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY); + ant = (div_mode == 3) ? 1 : 0; + rt2800_bbp_read(rt2x00dev, 152, &value); + if (ant == 0) { + /* Main antenna */ + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); + } else { + /* Auxiliary antenna */ + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); + } + rt2800_bbp_write(rt2x00dev, 152, value); + + if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) { + rt2800_bbp_read(rt2x00dev, 254, &value); + rt2x00_set_field8(&value, BBP254_BIT7, 1); + rt2800_bbp_write(rt2x00dev, 254, value); + } + + rt2800_init_freq_calibration(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 84, 0x19); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); +} + +static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + if (rt2800_is_305x_soc(rt2x00dev)) + rt2800_init_bbp_305x_soc(rt2x00dev); + + switch (rt2x00dev->chip.rt) { + case RT2860: + case RT2872: + case RT2883: + rt2800_init_bbp_28xx(rt2x00dev); + break; + case RT3070: + case RT3071: + case RT3090: + rt2800_init_bbp_30xx(rt2x00dev); + break; + case RT3290: + rt2800_init_bbp_3290(rt2x00dev); + break; + case RT3352: + rt2800_init_bbp_3352(rt2x00dev); + break; + case RT3390: + rt2800_init_bbp_3390(rt2x00dev); + break; + case RT3572: + rt2800_init_bbp_3572(rt2x00dev); + break; + case RT3593: + rt2800_init_bbp_3593(rt2x00dev); + return; + case RT5390: + case RT5392: + rt2800_init_bbp_53xx(rt2x00dev); + break; + case RT5592: + rt2800_init_bbp_5592(rt2x00dev); + return; + } + + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_BBP_START, i, + &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + rt2800_bbp_write(rt2x00dev, reg_id, value); + } + } +} + +static void rt2800_led_open_drain_enable(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, OPT_14_CSR, ®); + rt2x00_set_field32(®, OPT_14_CSR_BIT0, 1); + rt2800_register_write(rt2x00dev, OPT_14_CSR, reg); +} + +static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev, bool bw40, + u8 filter_target) +{ + unsigned int i; + u8 bbp; + u8 rfcsr; + u8 passband; + u8 stopband; + u8 overtuned = 0; + u8 rfcsr24 = (bw40) ? 0x27 : 0x07; + + rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); + + rt2800_bbp_read(rt2x00dev, 4, &bbp); + rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * bw40); + rt2800_bbp_write(rt2x00dev, 4, bbp); + + rt2800_rfcsr_read(rt2x00dev, 31, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR31_RX_H20M, bw40); + rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 1); + rt2800_rfcsr_write(rt2x00dev, 22, rfcsr); + + /* + * Set power & frequency of passband test tone + */ + rt2800_bbp_write(rt2x00dev, 24, 0); + + for (i = 0; i < 100; i++) { + rt2800_bbp_write(rt2x00dev, 25, 0x90); + msleep(1); + + rt2800_bbp_read(rt2x00dev, 55, &passband); + if (passband) + break; + } + + /* + * Set power & frequency of stopband test tone + */ + rt2800_bbp_write(rt2x00dev, 24, 0x06); + + for (i = 0; i < 100; i++) { + rt2800_bbp_write(rt2x00dev, 25, 0x90); + msleep(1); + + rt2800_bbp_read(rt2x00dev, 55, &stopband); + + if ((passband - stopband) <= filter_target) { + rfcsr24++; + overtuned += ((passband - stopband) == filter_target); + } else + break; + + rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); + } + + rfcsr24 -= !!overtuned; + + rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); + return rfcsr24; +} + +static void rt2800_rf_init_calibration(struct rt2x00_dev *rt2x00dev, + const unsigned int rf_reg) +{ + u8 rfcsr; + + rt2800_rfcsr_read(rt2x00dev, rf_reg, &rfcsr); + rt2x00_set_field8(&rfcsr, FIELD8(0x80), 1); + rt2800_rfcsr_write(rt2x00dev, rf_reg, rfcsr); + msleep(1); + rt2x00_set_field8(&rfcsr, FIELD8(0x80), 0); + rt2800_rfcsr_write(rt2x00dev, rf_reg, rfcsr); +} + +static void rt2800_rx_filter_calibration(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 filter_tgt_bw20; + u8 filter_tgt_bw40; + u8 rfcsr, bbp; + + /* + * TODO: sync filter_tgt values with vendor driver + */ + if (rt2x00_rt(rt2x00dev, RT3070)) { + filter_tgt_bw20 = 0x16; + filter_tgt_bw40 = 0x19; + } else { + filter_tgt_bw20 = 0x13; + filter_tgt_bw40 = 0x15; + } + + drv_data->calibration_bw20 = + rt2800_init_rx_filter(rt2x00dev, false, filter_tgt_bw20); + drv_data->calibration_bw40 = + rt2800_init_rx_filter(rt2x00dev, true, filter_tgt_bw40); + + /* + * Save BBP 25 & 26 values for later use in channel switching (for 3052) + */ + rt2800_bbp_read(rt2x00dev, 25, &drv_data->bbp25); + rt2800_bbp_read(rt2x00dev, 26, &drv_data->bbp26); + + /* + * Set back to initial state + */ + rt2800_bbp_write(rt2x00dev, 24, 0); + + rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 0); + rt2800_rfcsr_write(rt2x00dev, 22, rfcsr); + + /* + * Set BBP back to BW20 + */ + rt2800_bbp_read(rt2x00dev, 4, &bbp); + rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 0); + rt2800_bbp_write(rt2x00dev, 4, bbp); +} + +static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 min_gain, rfcsr, bbp; + u16 eeprom; + + rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); + + rt2x00_set_field8(&rfcsr, RFCSR17_TX_LO1_EN, 0); + if (rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { + if (!rt2x00_has_cap_external_lna_bg(rt2x00dev)) + rt2x00_set_field8(&rfcsr, RFCSR17_R, 1); + } + + min_gain = rt2x00_rt(rt2x00dev, RT3070) ? 1 : 2; + if (drv_data->txmixer_gain_24g >= min_gain) { + rt2x00_set_field8(&rfcsr, RFCSR17_TXMIXER_GAIN, + drv_data->txmixer_gain_24g); + } + + rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); + + if (rt2x00_rt(rt2x00dev, RT3090)) { + /* Turn off unused DAC1 and ADC1 to reduce power consumption */ + rt2800_bbp_read(rt2x00dev, 138, &bbp); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) + rt2x00_set_field8(&bbp, BBP138_RX_ADC1, 0); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) + rt2x00_set_field8(&bbp, BBP138_TX_DAC1, 1); + rt2800_bbp_write(rt2x00dev, 138, bbp); + } + + if (rt2x00_rt(rt2x00dev, RT3070)) { + rt2800_rfcsr_read(rt2x00dev, 27, &rfcsr); + if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) + rt2x00_set_field8(&rfcsr, RFCSR27_R1, 3); + else + rt2x00_set_field8(&rfcsr, RFCSR27_R1, 0); + rt2x00_set_field8(&rfcsr, RFCSR27_R2, 0); + rt2x00_set_field8(&rfcsr, RFCSR27_R3, 0); + rt2x00_set_field8(&rfcsr, RFCSR27_R4, 0); + rt2800_rfcsr_write(rt2x00dev, 27, rfcsr); + } else if (rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3390)) { + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 15, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR15_TX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 15, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 20, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR20_RX_LO1_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 20, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 21, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR21_RX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 21, rfcsr); + } +} + +static void rt2800_normal_mode_setup_3593(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 rfcsr; + u8 tx_gain; + + rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); + tx_gain = rt2x00_get_field8(drv_data->txmixer_gain_24g, + RFCSR17_TXMIXER_GAIN); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, tx_gain); + rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 38, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR38_RX_LO1_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 38, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 39, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR39_RX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + /* TODO: enable stream mode */ +} + +static void rt2800_normal_mode_setup_5xxx(struct rt2x00_dev *rt2x00dev) +{ + u8 reg; + u16 eeprom; + + /* Turn off unused DAC1 and ADC1 to reduce power consumption */ + rt2800_bbp_read(rt2x00dev, 138, ®); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) + rt2x00_set_field8(®, BBP138_RX_ADC1, 0); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) + rt2x00_set_field8(®, BBP138_TX_DAC1, 1); + rt2800_bbp_write(rt2x00dev, 138, reg); + + rt2800_rfcsr_read(rt2x00dev, 38, ®); + rt2x00_set_field8(®, RFCSR38_RX_LO1_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 38, reg); + + rt2800_rfcsr_read(rt2x00dev, 39, ®); + rt2x00_set_field8(®, RFCSR39_RX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 39, reg); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_rfcsr_read(rt2x00dev, 30, ®); + rt2x00_set_field8(®, RFCSR30_RX_VCM, 2); + rt2800_rfcsr_write(rt2x00dev, 30, reg); +} + +static void rt2800_init_rfcsr_305x_soc(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 0, 0x50); + rt2800_rfcsr_write(rt2x00dev, 1, 0x01); + rt2800_rfcsr_write(rt2x00dev, 2, 0xf7); + rt2800_rfcsr_write(rt2x00dev, 3, 0x75); + rt2800_rfcsr_write(rt2x00dev, 4, 0x40); + rt2800_rfcsr_write(rt2x00dev, 5, 0x03); + rt2800_rfcsr_write(rt2x00dev, 6, 0x02); + rt2800_rfcsr_write(rt2x00dev, 7, 0x50); + rt2800_rfcsr_write(rt2x00dev, 8, 0x39); + rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 10, 0x60); + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + rt2800_rfcsr_write(rt2x00dev, 12, 0x75); + rt2800_rfcsr_write(rt2x00dev, 13, 0x75); + rt2800_rfcsr_write(rt2x00dev, 14, 0x90); + rt2800_rfcsr_write(rt2x00dev, 15, 0x58); + rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 17, 0x92); + rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); + rt2800_rfcsr_write(rt2x00dev, 19, 0x02); + rt2800_rfcsr_write(rt2x00dev, 20, 0xba); + rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x31); + rt2800_rfcsr_write(rt2x00dev, 24, 0x08); + rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + rt2800_rfcsr_write(rt2x00dev, 26, 0x25); + rt2800_rfcsr_write(rt2x00dev, 27, 0x23); + rt2800_rfcsr_write(rt2x00dev, 28, 0x13); + rt2800_rfcsr_write(rt2x00dev, 29, 0x83); + rt2800_rfcsr_write(rt2x00dev, 30, 0x00); + rt2800_rfcsr_write(rt2x00dev, 31, 0x00); +} + +static void rt2800_init_rfcsr_30xx(struct rt2x00_dev *rt2x00dev) +{ + u8 rfcsr; + u16 eeprom; + u32 reg; + + /* XXX vendor driver do this only for 3070 */ + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 4, 0x40); + rt2800_rfcsr_write(rt2x00dev, 5, 0x03); + rt2800_rfcsr_write(rt2x00dev, 6, 0x02); + rt2800_rfcsr_write(rt2x00dev, 7, 0x60); + rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 10, 0x41); + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + rt2800_rfcsr_write(rt2x00dev, 12, 0x7b); + rt2800_rfcsr_write(rt2x00dev, 14, 0x90); + rt2800_rfcsr_write(rt2x00dev, 15, 0x58); + rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 17, 0x92); + rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); + rt2800_rfcsr_write(rt2x00dev, 19, 0x02); + rt2800_rfcsr_write(rt2x00dev, 20, 0xba); + rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 24, 0x16); + rt2800_rfcsr_write(rt2x00dev, 25, 0x03); + rt2800_rfcsr_write(rt2x00dev, 29, 0x1f); + + if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) { + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + } else if (rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090)) { + rt2800_rfcsr_write(rt2x00dev, 31, 0x14); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR6_R2, 1); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); + if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, + &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST)) + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); + else + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); + } + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + + rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); + rt2x00_set_field32(®, GPIO_SWITCH_5, 0); + rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); + } + + rt2800_rx_filter_calibration(rt2x00dev); + + if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F) || + rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E)) + rt2800_rfcsr_write(rt2x00dev, 27, 0x03); + + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3xxx(rt2x00dev); +} + +static void rt2800_init_rfcsr_3290(struct rt2x00_dev *rt2x00dev) +{ + u8 rfcsr; + + rt2800_rf_init_calibration(rt2x00dev, 2); + + rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 2, 0x80); + rt2800_rfcsr_write(rt2x00dev, 3, 0x08); + rt2800_rfcsr_write(rt2x00dev, 4, 0x00); + rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 8, 0xf3); + rt2800_rfcsr_write(rt2x00dev, 9, 0x02); + rt2800_rfcsr_write(rt2x00dev, 10, 0x53); + rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 12, 0x46); + rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); + rt2800_rfcsr_write(rt2x00dev, 18, 0x02); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 25, 0x83); + rt2800_rfcsr_write(rt2x00dev, 26, 0x82); + rt2800_rfcsr_write(rt2x00dev, 27, 0x09); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + rt2800_rfcsr_write(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write(rt2x00dev, 34, 0x05); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 38, 0x85); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); + rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); + rt2800_rfcsr_write(rt2x00dev, 43, 0x7b); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); + rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); + rt2800_rfcsr_write(rt2x00dev, 46, 0x73); + rt2800_rfcsr_write(rt2x00dev, 47, 0x00); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 49, 0x98); + rt2800_rfcsr_write(rt2x00dev, 52, 0x38); + rt2800_rfcsr_write(rt2x00dev, 53, 0x00); + rt2800_rfcsr_write(rt2x00dev, 54, 0x78); + rt2800_rfcsr_write(rt2x00dev, 55, 0x43); + rt2800_rfcsr_write(rt2x00dev, 56, 0x02); + rt2800_rfcsr_write(rt2x00dev, 57, 0x80); + rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 59, 0x09); + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + rt2800_rfcsr_write(rt2x00dev, 61, 0xc1); + + rt2800_rfcsr_read(rt2x00dev, 29, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR29_RSSI_GAIN, 3); + rt2800_rfcsr_write(rt2x00dev, 29, rfcsr); + + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3xxx(rt2x00dev); +} + +static void rt2800_init_rfcsr_3352(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 0, 0xf0); + rt2800_rfcsr_write(rt2x00dev, 1, 0x23); + rt2800_rfcsr_write(rt2x00dev, 2, 0x50); + rt2800_rfcsr_write(rt2x00dev, 3, 0x18); + rt2800_rfcsr_write(rt2x00dev, 4, 0x00); + rt2800_rfcsr_write(rt2x00dev, 5, 0x00); + rt2800_rfcsr_write(rt2x00dev, 6, 0x33); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 8, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 9, 0x02); + rt2800_rfcsr_write(rt2x00dev, 10, 0xd2); + rt2800_rfcsr_write(rt2x00dev, 11, 0x42); + rt2800_rfcsr_write(rt2x00dev, 12, 0x1c); + rt2800_rfcsr_write(rt2x00dev, 13, 0x00); + rt2800_rfcsr_write(rt2x00dev, 14, 0x5a); + rt2800_rfcsr_write(rt2x00dev, 15, 0x00); + rt2800_rfcsr_write(rt2x00dev, 16, 0x01); + rt2800_rfcsr_write(rt2x00dev, 18, 0x45); + rt2800_rfcsr_write(rt2x00dev, 19, 0x02); + rt2800_rfcsr_write(rt2x00dev, 20, 0x00); + rt2800_rfcsr_write(rt2x00dev, 21, 0x00); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x00); + rt2800_rfcsr_write(rt2x00dev, 24, 0x00); + rt2800_rfcsr_write(rt2x00dev, 25, 0x80); + rt2800_rfcsr_write(rt2x00dev, 26, 0x00); + rt2800_rfcsr_write(rt2x00dev, 27, 0x03); + rt2800_rfcsr_write(rt2x00dev, 28, 0x03); + rt2800_rfcsr_write(rt2x00dev, 29, 0x00); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + rt2800_rfcsr_write(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write(rt2x00dev, 34, 0x01); + rt2800_rfcsr_write(rt2x00dev, 35, 0x03); + rt2800_rfcsr_write(rt2x00dev, 36, 0xbd); + rt2800_rfcsr_write(rt2x00dev, 37, 0x3c); + rt2800_rfcsr_write(rt2x00dev, 38, 0x5f); + rt2800_rfcsr_write(rt2x00dev, 39, 0xc5); + rt2800_rfcsr_write(rt2x00dev, 40, 0x33); + rt2800_rfcsr_write(rt2x00dev, 41, 0x5b); + rt2800_rfcsr_write(rt2x00dev, 42, 0x5b); + rt2800_rfcsr_write(rt2x00dev, 43, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 44, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 45, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 46, 0xdd); + rt2800_rfcsr_write(rt2x00dev, 47, 0x0d); + rt2800_rfcsr_write(rt2x00dev, 48, 0x14); + rt2800_rfcsr_write(rt2x00dev, 49, 0x00); + rt2800_rfcsr_write(rt2x00dev, 50, 0x2d); + rt2800_rfcsr_write(rt2x00dev, 51, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 52, 0x00); + rt2800_rfcsr_write(rt2x00dev, 53, 0x52); + rt2800_rfcsr_write(rt2x00dev, 54, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 55, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 56, 0x00); + rt2800_rfcsr_write(rt2x00dev, 57, 0x52); + rt2800_rfcsr_write(rt2x00dev, 58, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 59, 0x00); + rt2800_rfcsr_write(rt2x00dev, 60, 0x00); + rt2800_rfcsr_write(rt2x00dev, 61, 0x00); + rt2800_rfcsr_write(rt2x00dev, 62, 0x00); + rt2800_rfcsr_write(rt2x00dev, 63, 0x00); + + rt2800_rx_filter_calibration(rt2x00dev); + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3xxx(rt2x00dev); +} + +static void rt2800_init_rfcsr_3390(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 0, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 1, 0xe1); + rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 3, 0x62); + rt2800_rfcsr_write(rt2x00dev, 4, 0x40); + rt2800_rfcsr_write(rt2x00dev, 5, 0x8b); + rt2800_rfcsr_write(rt2x00dev, 6, 0x42); + rt2800_rfcsr_write(rt2x00dev, 7, 0x34); + rt2800_rfcsr_write(rt2x00dev, 8, 0x00); + rt2800_rfcsr_write(rt2x00dev, 9, 0xc0); + rt2800_rfcsr_write(rt2x00dev, 10, 0x61); + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + rt2800_rfcsr_write(rt2x00dev, 12, 0x3b); + rt2800_rfcsr_write(rt2x00dev, 13, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 14, 0x90); + rt2800_rfcsr_write(rt2x00dev, 15, 0x53); + rt2800_rfcsr_write(rt2x00dev, 16, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 17, 0x94); + rt2800_rfcsr_write(rt2x00dev, 18, 0x5c); + rt2800_rfcsr_write(rt2x00dev, 19, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 20, 0xb2); + rt2800_rfcsr_write(rt2x00dev, 21, 0xf6); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x14); + rt2800_rfcsr_write(rt2x00dev, 24, 0x08); + rt2800_rfcsr_write(rt2x00dev, 25, 0x3d); + rt2800_rfcsr_write(rt2x00dev, 26, 0x85); + rt2800_rfcsr_write(rt2x00dev, 27, 0x00); + rt2800_rfcsr_write(rt2x00dev, 28, 0x41); + rt2800_rfcsr_write(rt2x00dev, 29, 0x8f); + rt2800_rfcsr_write(rt2x00dev, 30, 0x20); + rt2800_rfcsr_write(rt2x00dev, 31, 0x0f); + + rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); + rt2x00_set_field32(®, GPIO_SWITCH_5, 0); + rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); + + rt2800_rx_filter_calibration(rt2x00dev); + + if (rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) + rt2800_rfcsr_write(rt2x00dev, 27, 0x03); + + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3xxx(rt2x00dev); +} + +static void rt2800_init_rfcsr_3572(struct rt2x00_dev *rt2x00dev) +{ + u8 rfcsr; + u32 reg; + + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 0, 0x70); + rt2800_rfcsr_write(rt2x00dev, 1, 0x81); + rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 3, 0x02); + rt2800_rfcsr_write(rt2x00dev, 4, 0x4c); + rt2800_rfcsr_write(rt2x00dev, 5, 0x05); + rt2800_rfcsr_write(rt2x00dev, 6, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 7, 0xd8); + rt2800_rfcsr_write(rt2x00dev, 9, 0xc3); + rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 11, 0xb9); + rt2800_rfcsr_write(rt2x00dev, 12, 0x70); + rt2800_rfcsr_write(rt2x00dev, 13, 0x65); + rt2800_rfcsr_write(rt2x00dev, 14, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 15, 0x53); + rt2800_rfcsr_write(rt2x00dev, 16, 0x4c); + rt2800_rfcsr_write(rt2x00dev, 17, 0x23); + rt2800_rfcsr_write(rt2x00dev, 18, 0xac); + rt2800_rfcsr_write(rt2x00dev, 19, 0x93); + rt2800_rfcsr_write(rt2x00dev, 20, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 21, 0xd0); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x3c); + rt2800_rfcsr_write(rt2x00dev, 24, 0x16); + rt2800_rfcsr_write(rt2x00dev, 25, 0x15); + rt2800_rfcsr_write(rt2x00dev, 26, 0x85); + rt2800_rfcsr_write(rt2x00dev, 27, 0x00); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x9b); + rt2800_rfcsr_write(rt2x00dev, 30, 0x09); + rt2800_rfcsr_write(rt2x00dev, 31, 0x10); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR6_R2, 1); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); + rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + msleep(1); + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); + rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + + rt2800_rx_filter_calibration(rt2x00dev); + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3xxx(rt2x00dev); +} + +static void rt3593_post_bbp_init(struct rt2x00_dev *rt2x00dev) +{ + u8 bbp; + bool txbf_enabled = false; /* FIXME */ + + rt2800_bbp_read(rt2x00dev, 105, &bbp); + if (rt2x00dev->default_ant.rx_chain_num == 1) + rt2x00_set_field8(&bbp, BBP105_MLD, 0); + else + rt2x00_set_field8(&bbp, BBP105_MLD, 1); + rt2800_bbp_write(rt2x00dev, 105, bbp); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); + rt2800_bbp_write(rt2x00dev, 82, 0x82); + rt2800_bbp_write(rt2x00dev, 106, 0x05); + rt2800_bbp_write(rt2x00dev, 104, 0x92); + rt2800_bbp_write(rt2x00dev, 88, 0x90); + rt2800_bbp_write(rt2x00dev, 148, 0xc8); + rt2800_bbp_write(rt2x00dev, 47, 0x48); + rt2800_bbp_write(rt2x00dev, 120, 0x50); + + if (txbf_enabled) + rt2800_bbp_write(rt2x00dev, 163, 0xbd); + else + rt2800_bbp_write(rt2x00dev, 163, 0x9d); + + /* SNR mapping */ + rt2800_bbp_write(rt2x00dev, 142, 6); + rt2800_bbp_write(rt2x00dev, 143, 160); + rt2800_bbp_write(rt2x00dev, 142, 7); + rt2800_bbp_write(rt2x00dev, 143, 161); + rt2800_bbp_write(rt2x00dev, 142, 8); + rt2800_bbp_write(rt2x00dev, 143, 162); + + /* ADC/DAC control */ + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + /* RX AGC energy lower bound in log2 */ + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + /* FIXME: BBP 105 owerwrite? */ + rt2800_bbp_write(rt2x00dev, 105, 0x04); + +} + +static void rt2800_init_rfcsr_3593(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u32 reg; + u8 rfcsr; + + /* Disable GPIO #4 and #7 function for LAN PE control */ + rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); + rt2x00_set_field32(®, GPIO_SWITCH_4, 0); + rt2x00_set_field32(®, GPIO_SWITCH_7, 0); + rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); + + /* Initialize default register values */ + rt2800_rfcsr_write(rt2x00dev, 1, 0x03); + rt2800_rfcsr_write(rt2x00dev, 3, 0x80); + rt2800_rfcsr_write(rt2x00dev, 5, 0x00); + rt2800_rfcsr_write(rt2x00dev, 6, 0x40); + rt2800_rfcsr_write(rt2x00dev, 8, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 9, 0x02); + rt2800_rfcsr_write(rt2x00dev, 10, 0xd3); + rt2800_rfcsr_write(rt2x00dev, 11, 0x40); + rt2800_rfcsr_write(rt2x00dev, 12, 0x4e); + rt2800_rfcsr_write(rt2x00dev, 13, 0x12); + rt2800_rfcsr_write(rt2x00dev, 18, 0x40); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x78); + rt2800_rfcsr_write(rt2x00dev, 33, 0x3b); + rt2800_rfcsr_write(rt2x00dev, 34, 0x3c); + rt2800_rfcsr_write(rt2x00dev, 35, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 38, 0x86); + rt2800_rfcsr_write(rt2x00dev, 39, 0x23); + rt2800_rfcsr_write(rt2x00dev, 44, 0xd3); + rt2800_rfcsr_write(rt2x00dev, 45, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 46, 0x60); + rt2800_rfcsr_write(rt2x00dev, 49, 0x8e); + rt2800_rfcsr_write(rt2x00dev, 50, 0x86); + rt2800_rfcsr_write(rt2x00dev, 51, 0x75); + rt2800_rfcsr_write(rt2x00dev, 52, 0x45); + rt2800_rfcsr_write(rt2x00dev, 53, 0x18); + rt2800_rfcsr_write(rt2x00dev, 54, 0x18); + rt2800_rfcsr_write(rt2x00dev, 55, 0x18); + rt2800_rfcsr_write(rt2x00dev, 56, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 57, 0x6e); + + /* Initiate calibration */ + /* TODO: use rt2800_rf_init_calibration ? */ + rt2800_rfcsr_read(rt2x00dev, 2, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR2_RESCAL_EN, 1); + rt2800_rfcsr_write(rt2x00dev, 2, rfcsr); + + rt2800_adjust_freq_offset(rt2x00dev); + + rt2800_rfcsr_read(rt2x00dev, 18, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR18_XO_TUNE_BYPASS, 1); + rt2800_rfcsr_write(rt2x00dev, 18, rfcsr); + + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); + rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + usleep_range(1000, 1500); + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + + /* Set initial values for RX filter calibration */ + drv_data->calibration_bw20 = 0x1f; + drv_data->calibration_bw40 = 0x2f; + + /* Save BBP 25 & 26 values for later use in channel switching */ + rt2800_bbp_read(rt2x00dev, 25, &drv_data->bbp25); + rt2800_bbp_read(rt2x00dev, 26, &drv_data->bbp26); + + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3593(rt2x00dev); + + rt3593_post_bbp_init(rt2x00dev); + + /* TODO: enable stream mode support */ +} + +static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rf_init_calibration(rt2x00dev, 2); + + rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 2, 0x80); + rt2800_rfcsr_write(rt2x00dev, 3, 0x88); + rt2800_rfcsr_write(rt2x00dev, 5, 0x10); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); + else + rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 10, 0x53); + rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 12, 0x46); + rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); + rt2800_rfcsr_write(rt2x00dev, 14, 0x00); + rt2800_rfcsr_write(rt2x00dev, 15, 0x00); + rt2800_rfcsr_write(rt2x00dev, 16, 0x00); + rt2800_rfcsr_write(rt2x00dev, 18, 0x03); + rt2800_rfcsr_write(rt2x00dev, 19, 0x00); + + rt2800_rfcsr_write(rt2x00dev, 20, 0x00); + rt2800_rfcsr_write(rt2x00dev, 21, 0x00); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 23, 0x00); + rt2800_rfcsr_write(rt2x00dev, 24, 0x00); + if (rt2x00_is_usb(rt2x00dev) && + rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 25, 0x80); + else + rt2800_rfcsr_write(rt2x00dev, 25, 0xc0); + rt2800_rfcsr_write(rt2x00dev, 26, 0x00); + rt2800_rfcsr_write(rt2x00dev, 27, 0x09); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + rt2800_rfcsr_write(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write(rt2x00dev, 34, 0x07); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 37, 0x08); + rt2800_rfcsr_write(rt2x00dev, 38, 0x85); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); + + rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); + rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 42, 0xd2); + rt2800_rfcsr_write(rt2x00dev, 43, 0x9a); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); + rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 46, 0x73); + else + rt2800_rfcsr_write(rt2x00dev, 46, 0x7b); + rt2800_rfcsr_write(rt2x00dev, 47, 0x00); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 49, 0x94); + + rt2800_rfcsr_write(rt2x00dev, 52, 0x38); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 53, 0x00); + else + rt2800_rfcsr_write(rt2x00dev, 53, 0x84); + rt2800_rfcsr_write(rt2x00dev, 54, 0x78); + rt2800_rfcsr_write(rt2x00dev, 55, 0x44); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 56, 0x42); + else + rt2800_rfcsr_write(rt2x00dev, 56, 0x22); + rt2800_rfcsr_write(rt2x00dev, 57, 0x80); + rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 59, 0x8f); + + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { + if (rt2x00_is_usb(rt2x00dev)) + rt2800_rfcsr_write(rt2x00dev, 61, 0xd1); + else + rt2800_rfcsr_write(rt2x00dev, 61, 0xd5); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rt2800_rfcsr_write(rt2x00dev, 61, 0xdd); + else + rt2800_rfcsr_write(rt2x00dev, 61, 0xb5); + } + rt2800_rfcsr_write(rt2x00dev, 62, 0x00); + rt2800_rfcsr_write(rt2x00dev, 63, 0x00); + + rt2800_normal_mode_setup_5xxx(rt2x00dev); + + rt2800_led_open_drain_enable(rt2x00dev); +} + +static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rf_init_calibration(rt2x00dev, 2); + + rt2800_rfcsr_write(rt2x00dev, 1, 0x17); + rt2800_rfcsr_write(rt2x00dev, 3, 0x88); + rt2800_rfcsr_write(rt2x00dev, 5, 0x10); + rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 10, 0x53); + rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 12, 0x46); + rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); + rt2800_rfcsr_write(rt2x00dev, 14, 0x00); + rt2800_rfcsr_write(rt2x00dev, 15, 0x00); + rt2800_rfcsr_write(rt2x00dev, 16, 0x00); + rt2800_rfcsr_write(rt2x00dev, 18, 0x03); + rt2800_rfcsr_write(rt2x00dev, 19, 0x4d); + rt2800_rfcsr_write(rt2x00dev, 20, 0x00); + rt2800_rfcsr_write(rt2x00dev, 21, 0x8d); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 23, 0x0b); + rt2800_rfcsr_write(rt2x00dev, 24, 0x44); + rt2800_rfcsr_write(rt2x00dev, 25, 0x80); + rt2800_rfcsr_write(rt2x00dev, 26, 0x82); + rt2800_rfcsr_write(rt2x00dev, 27, 0x09); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x20); + rt2800_rfcsr_write(rt2x00dev, 33, 0xC0); + rt2800_rfcsr_write(rt2x00dev, 34, 0x07); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 37, 0x08); + rt2800_rfcsr_write(rt2x00dev, 38, 0x89); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 40, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); + rt2800_rfcsr_write(rt2x00dev, 43, 0x9b); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); + rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); + rt2800_rfcsr_write(rt2x00dev, 46, 0x73); + rt2800_rfcsr_write(rt2x00dev, 47, 0x0c); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 49, 0x94); + rt2800_rfcsr_write(rt2x00dev, 50, 0x94); + rt2800_rfcsr_write(rt2x00dev, 51, 0x3a); + rt2800_rfcsr_write(rt2x00dev, 52, 0x48); + rt2800_rfcsr_write(rt2x00dev, 53, 0x44); + rt2800_rfcsr_write(rt2x00dev, 54, 0x38); + rt2800_rfcsr_write(rt2x00dev, 55, 0x43); + rt2800_rfcsr_write(rt2x00dev, 56, 0xa1); + rt2800_rfcsr_write(rt2x00dev, 57, 0x00); + rt2800_rfcsr_write(rt2x00dev, 58, 0x39); + rt2800_rfcsr_write(rt2x00dev, 59, 0x07); + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + rt2800_rfcsr_write(rt2x00dev, 61, 0x91); + rt2800_rfcsr_write(rt2x00dev, 62, 0x39); + rt2800_rfcsr_write(rt2x00dev, 63, 0x07); + + rt2800_normal_mode_setup_5xxx(rt2x00dev); + + rt2800_led_open_drain_enable(rt2x00dev); +} + +static void rt2800_init_rfcsr_5592(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 1, 0x3F); + rt2800_rfcsr_write(rt2x00dev, 3, 0x08); + rt2800_rfcsr_write(rt2x00dev, 5, 0x10); + rt2800_rfcsr_write(rt2x00dev, 6, 0xE4); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 14, 0x00); + rt2800_rfcsr_write(rt2x00dev, 15, 0x00); + rt2800_rfcsr_write(rt2x00dev, 16, 0x00); + rt2800_rfcsr_write(rt2x00dev, 18, 0x03); + rt2800_rfcsr_write(rt2x00dev, 19, 0x4D); + rt2800_rfcsr_write(rt2x00dev, 20, 0x10); + rt2800_rfcsr_write(rt2x00dev, 21, 0x8D); + rt2800_rfcsr_write(rt2x00dev, 26, 0x82); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + rt2800_rfcsr_write(rt2x00dev, 33, 0xC0); + rt2800_rfcsr_write(rt2x00dev, 34, 0x07); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 47, 0x0C); + rt2800_rfcsr_write(rt2x00dev, 53, 0x22); + rt2800_rfcsr_write(rt2x00dev, 63, 0x07); + + rt2800_rfcsr_write(rt2x00dev, 2, 0x80); + msleep(1); + + rt2800_adjust_freq_offset(rt2x00dev); + + /* Enable DC filter */ + if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_normal_mode_setup_5xxx(rt2x00dev); + + if (rt2x00_rt_rev_lt(rt2x00dev, RT5592, REV_RT5592C)) + rt2800_rfcsr_write(rt2x00dev, 27, 0x03); + + rt2800_led_open_drain_enable(rt2x00dev); +} + +static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) +{ + if (rt2800_is_305x_soc(rt2x00dev)) { + rt2800_init_rfcsr_305x_soc(rt2x00dev); + return; + } + + switch (rt2x00dev->chip.rt) { + case RT3070: + case RT3071: + case RT3090: + rt2800_init_rfcsr_30xx(rt2x00dev); + break; + case RT3290: + rt2800_init_rfcsr_3290(rt2x00dev); + break; + case RT3352: + rt2800_init_rfcsr_3352(rt2x00dev); + break; + case RT3390: + rt2800_init_rfcsr_3390(rt2x00dev); + break; + case RT3572: + rt2800_init_rfcsr_3572(rt2x00dev); + break; + case RT3593: + rt2800_init_rfcsr_3593(rt2x00dev); + break; + case RT5390: + rt2800_init_rfcsr_5390(rt2x00dev); + break; + case RT5392: + rt2800_init_rfcsr_5392(rt2x00dev); + break; + case RT5592: + rt2800_init_rfcsr_5592(rt2x00dev); + break; + } +} + +int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 word; + + /* + * Initialize MAC registers. + */ + if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) || + rt2800_init_registers(rt2x00dev))) + return -EIO; + + /* + * Wait BBP/RF to wake up. + */ + if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev))) + return -EIO; + + /* + * Send signal during boot time to initialize firmware. + */ + rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + if (rt2x00_is_usb(rt2x00dev)) + rt2800_register_write(rt2x00dev, H2M_INT_SRC, 0); + rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0); + msleep(1); + + /* + * Make sure BBP is up and running. + */ + if (unlikely(rt2800_wait_bbp_ready(rt2x00dev))) + return -EIO; + + /* + * Initialize BBP/RF registers. + */ + rt2800_init_bbp(rt2x00dev); + rt2800_init_rfcsr(rt2x00dev); + + if (rt2x00_is_usb(rt2x00dev) && + (rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3572))) { + udelay(200); + rt2800_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0); + udelay(10); + } + + /* + * Enable RX. + */ + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + udelay(50); + + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1); + rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 2); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); + + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + /* + * Initialize LED control + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word); + rt2800_mcu_request(rt2x00dev, MCU_LED_AG_CONF, 0xff, + word & 0xff, (word >> 8) & 0xff); + + rt2800_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word); + rt2800_mcu_request(rt2x00dev, MCU_LED_ACT_CONF, 0xff, + word & 0xff, (word >> 8) & 0xff); + + rt2800_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word); + rt2800_mcu_request(rt2x00dev, MCU_LED_LED_POLARITY, 0xff, + word & 0xff, (word >> 8) & 0xff); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_enable_radio); + +void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2800_disable_wpdma(rt2x00dev); + + /* Wait for DMA, ignore error */ + rt2800_wait_wpdma_ready(rt2x00dev); + + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 0); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); +} +EXPORT_SYMBOL_GPL(rt2800_disable_radio); + +int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 efuse_ctrl_reg; + + if (rt2x00_rt(rt2x00dev, RT3290)) + efuse_ctrl_reg = EFUSE_CTRL_3290; + else + efuse_ctrl_reg = EFUSE_CTRL; + + rt2800_register_read(rt2x00dev, efuse_ctrl_reg, ®); + return rt2x00_get_field32(reg, EFUSE_CTRL_PRESENT); +} +EXPORT_SYMBOL_GPL(rt2800_efuse_detect); + +static void rt2800_efuse_read(struct rt2x00_dev *rt2x00dev, unsigned int i) +{ + u32 reg; + u16 efuse_ctrl_reg; + u16 efuse_data0_reg; + u16 efuse_data1_reg; + u16 efuse_data2_reg; + u16 efuse_data3_reg; + + if (rt2x00_rt(rt2x00dev, RT3290)) { + efuse_ctrl_reg = EFUSE_CTRL_3290; + efuse_data0_reg = EFUSE_DATA0_3290; + efuse_data1_reg = EFUSE_DATA1_3290; + efuse_data2_reg = EFUSE_DATA2_3290; + efuse_data3_reg = EFUSE_DATA3_3290; + } else { + efuse_ctrl_reg = EFUSE_CTRL; + efuse_data0_reg = EFUSE_DATA0; + efuse_data1_reg = EFUSE_DATA1; + efuse_data2_reg = EFUSE_DATA2; + efuse_data3_reg = EFUSE_DATA3; + } + mutex_lock(&rt2x00dev->csr_mutex); + + rt2800_register_read_lock(rt2x00dev, efuse_ctrl_reg, ®); + rt2x00_set_field32(®, EFUSE_CTRL_ADDRESS_IN, i); + rt2x00_set_field32(®, EFUSE_CTRL_MODE, 0); + rt2x00_set_field32(®, EFUSE_CTRL_KICK, 1); + rt2800_register_write_lock(rt2x00dev, efuse_ctrl_reg, reg); + + /* Wait until the EEPROM has been loaded */ + rt2800_regbusy_read(rt2x00dev, efuse_ctrl_reg, EFUSE_CTRL_KICK, ®); + /* Apparently the data is read from end to start */ + rt2800_register_read_lock(rt2x00dev, efuse_data3_reg, ®); + /* The returned value is in CPU order, but eeprom is le */ + *(u32 *)&rt2x00dev->eeprom[i] = cpu_to_le32(reg); + rt2800_register_read_lock(rt2x00dev, efuse_data2_reg, ®); + *(u32 *)&rt2x00dev->eeprom[i + 2] = cpu_to_le32(reg); + rt2800_register_read_lock(rt2x00dev, efuse_data1_reg, ®); + *(u32 *)&rt2x00dev->eeprom[i + 4] = cpu_to_le32(reg); + rt2800_register_read_lock(rt2x00dev, efuse_data0_reg, ®); + *(u32 *)&rt2x00dev->eeprom[i + 6] = cpu_to_le32(reg); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + + for (i = 0; i < EEPROM_SIZE / sizeof(u16); i += 8) + rt2800_efuse_read(rt2x00dev, i); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse); + +static u8 rt2800_get_txmixer_gain_24g(struct rt2x00_dev *rt2x00dev) +{ + u16 word; + + if (rt2x00_rt(rt2x00dev, RT3593)) + return 0; + + rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word); + if ((word & 0x00ff) != 0x00ff) + return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL); + + return 0; +} + +static u8 rt2800_get_txmixer_gain_5g(struct rt2x00_dev *rt2x00dev) +{ + u16 word; + + if (rt2x00_rt(rt2x00dev, RT3593)) + return 0; + + rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word); + if ((word & 0x00ff) != 0x00ff) + return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL); + + return 0; +} + +static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u16 word; + u8 *mac; + u8 default_lna_gain; + int retval; + + /* + * Read the EEPROM. + */ + retval = rt2800_read_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Start validation of the data that has been read. + */ + mac = rt2800_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + if (!is_valid_ether_addr(mac)) { + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); + } + + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2); + rt2x00_set_field16(&word, EEPROM_NIC_CONF0_TXPATH, 1); + rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RF_TYPE, RF2820); + rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); + rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); + } else if (rt2x00_rt(rt2x00dev, RT2860) || + rt2x00_rt(rt2x00dev, RT2872)) { + /* + * There is a max of 2 RX streams for RT28x0 series + */ + if (rt2x00_get_field16(word, EEPROM_NIC_CONF0_RXPATH) > 2) + rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2); + rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); + } + + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_HW_RADIO, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_LNA_2G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_LNA_5G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_CARDBUS_ACCEL, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_SB_2G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_SB_5G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_WPS_PBC, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_2G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_5G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BROADBAND_EXT_LNA, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_ANT_DIVERSITY, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_INTERNAL_TX_ALC, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BT_COEXIST, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_DAC_TEST, 0); + rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word); + rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); + } + + rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); + if ((word & 0x00ff) == 0x00ff) { + rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); + rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word); + rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word); + } + if ((word & 0xff00) == 0xff00) { + rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE, + LED_MODE_TXRX_ACTIVITY); + rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0); + rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word); + rt2800_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555); + rt2800_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221); + rt2800_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8); + rt2x00_eeprom_dbg(rt2x00dev, "Led Mode: 0x%04x\n", word); + } + + /* + * During the LNA validation we are going to use + * lna0 as correct value. Note that EEPROM_LNA + * is never validated. + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &word); + default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0); + + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0); + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word); + + drv_data->txmixer_gain_24g = rt2800_get_txmixer_gain_24g(rt2x00dev); + + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0); + if (!rt2x00_rt(rt2x00dev, RT3593)) { + if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 || + rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff) + rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1, + default_lna_gain); + } + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word); + + drv_data->txmixer_gain_5g = rt2800_get_txmixer_gain_5g(rt2x00dev); + + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0); + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word); + + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0); + if (!rt2x00_rt(rt2x00dev, RT3593)) { + if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 || + rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff) + rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2, + default_lna_gain); + } + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word); + + if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &word); + if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0x00 || + rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0xff) + rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1, + default_lna_gain); + if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0x00 || + rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0xff) + rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1, + default_lna_gain); + rt2800_eeprom_write(rt2x00dev, EEPROM_EXT_LNA2, word); + } + + return 0; +} + +static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u16 value; + u16 eeprom; + u16 rf; + + /* + * Read EEPROM word for configuration. + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + + /* + * Identify RF chipset by EEPROM value + * RT28xx/RT30xx: defined in "EEPROM_NIC_CONF0_RF_TYPE" field + * RT53xx: defined in "EEPROM_CHIP_ID" field + */ + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) + rt2800_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf); + else + rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE); + + switch (rf) { + case RF2820: + case RF2850: + case RF2720: + case RF2750: + case RF3020: + case RF2020: + case RF3021: + case RF3022: + case RF3052: + case RF3053: + case RF3070: + case RF3290: + case RF3320: + case RF3322: + case RF5360: + case RF5362: + case RF5370: + case RF5372: + case RF5390: + case RF5392: + case RF5592: + break; + default: + rt2x00_err(rt2x00dev, "Invalid RF chipset 0x%04x detected\n", + rf); + return -ENODEV; + } + + rt2x00_set_rf(rt2x00dev, rf); + + /* + * Identify default antenna configuration. + */ + rt2x00dev->default_ant.tx_chain_num = + rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH); + rt2x00dev->default_ant.rx_chain_num = + rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH); + + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + + if (rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3352) || + rt2x00_rt(rt2x00dev, RT3390)) { + value = rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_ANT_DIVERSITY); + switch (value) { + case 0: + case 1: + case 2: + rt2x00dev->default_ant.tx = ANTENNA_A; + rt2x00dev->default_ant.rx = ANTENNA_A; + break; + case 3: + rt2x00dev->default_ant.tx = ANTENNA_A; + rt2x00dev->default_ant.rx = ANTENNA_B; + break; + } + } else { + rt2x00dev->default_ant.tx = ANTENNA_A; + rt2x00dev->default_ant.rx = ANTENNA_A; + } + + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { + rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY; /* Unused */ + rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY; /* Unused */ + } + + /* + * Determine external LNA informations. + */ + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_LNA_5G)) + __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_LNA_2G)) + __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); + + /* + * Detect if this device has an hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_HW_RADIO)) + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); + + /* + * Detect if this device has Bluetooth co-existence. + */ + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_BT_COEXIST)) + __set_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags); + + /* + * Read frequency offset and RF programming sequence. + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); + rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); + + /* + * Store led settings, for correct led behaviour. + */ +#ifdef CONFIG_RT2X00_LIB_LEDS + rt2800_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + rt2800_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); + rt2800_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY); + + rt2x00dev->led_mcu_reg = eeprom; +#endif /* CONFIG_RT2X00_LIB_LEDS */ + + /* + * Check if support EIRP tx power limit feature. + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom); + + if (rt2x00_get_field16(eeprom, EEPROM_EIRP_MAX_TX_POWER_2GHZ) < + EIRP_MAX_TX_POWER_LIMIT) + __set_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags); + + return 0; +} + +/* + * RF value list for rt28xx + * Supports: 2.4 GHz (all) & 5.2 GHz (RF2850 & RF2750) + */ +static const struct rf_channel rf_vals[] = { + { 1, 0x18402ecc, 0x184c0786, 0x1816b455, 0x1800510b }, + { 2, 0x18402ecc, 0x184c0786, 0x18168a55, 0x1800519f }, + { 3, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800518b }, + { 4, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800519f }, + { 5, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800518b }, + { 6, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800519f }, + { 7, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800518b }, + { 8, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800519f }, + { 9, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800518b }, + { 10, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800519f }, + { 11, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800518b }, + { 12, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800519f }, + { 13, 0x18402ecc, 0x184c079e, 0x18168a55, 0x1800518b }, + { 14, 0x18402ecc, 0x184c07a2, 0x18168a55, 0x18005193 }, + + /* 802.11 UNI / HyperLan 2 */ + { 36, 0x18402ecc, 0x184c099a, 0x18158a55, 0x180ed1a3 }, + { 38, 0x18402ecc, 0x184c099e, 0x18158a55, 0x180ed193 }, + { 40, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed183 }, + { 44, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed1a3 }, + { 46, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed18b }, + { 48, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed19b }, + { 52, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed193 }, + { 54, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed1a3 }, + { 56, 0x18402ec8, 0x184c068e, 0x18158a55, 0x180ed18b }, + { 60, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed183 }, + { 62, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed193 }, + { 64, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed1a3 }, + + /* 802.11 HyperLan 2 */ + { 100, 0x18402ec8, 0x184c06b2, 0x18178a55, 0x180ed783 }, + { 102, 0x18402ec8, 0x184c06b2, 0x18578a55, 0x180ed793 }, + { 104, 0x18402ec8, 0x185c06b2, 0x18578a55, 0x180ed1a3 }, + { 108, 0x18402ecc, 0x185c0a32, 0x18578a55, 0x180ed193 }, + { 110, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed183 }, + { 112, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed19b }, + { 116, 0x18402ecc, 0x184c0a3a, 0x18178a55, 0x180ed1a3 }, + { 118, 0x18402ecc, 0x184c0a3e, 0x18178a55, 0x180ed193 }, + { 120, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed183 }, + { 124, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed193 }, + { 126, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed15b }, + { 128, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed1a3 }, + { 132, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed18b }, + { 134, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed193 }, + { 136, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed19b }, + { 140, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed183 }, + + /* 802.11 UNII */ + { 149, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed1a7 }, + { 151, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed187 }, + { 153, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed18f }, + { 157, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed19f }, + { 159, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed1a7 }, + { 161, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed187 }, + { 165, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed197 }, + { 167, 0x18402ec4, 0x184c03d2, 0x18179855, 0x1815531f }, + { 169, 0x18402ec4, 0x184c03d2, 0x18179855, 0x18155327 }, + { 171, 0x18402ec4, 0x184c03d6, 0x18179855, 0x18155307 }, + { 173, 0x18402ec4, 0x184c03d6, 0x18179855, 0x1815530f }, + + /* 802.11 Japan */ + { 184, 0x15002ccc, 0x1500491e, 0x1509be55, 0x150c0a0b }, + { 188, 0x15002ccc, 0x15004922, 0x1509be55, 0x150c0a13 }, + { 192, 0x15002ccc, 0x15004926, 0x1509be55, 0x150c0a1b }, + { 196, 0x15002ccc, 0x1500492a, 0x1509be55, 0x150c0a23 }, + { 208, 0x15002ccc, 0x1500493a, 0x1509be55, 0x150c0a13 }, + { 212, 0x15002ccc, 0x1500493e, 0x1509be55, 0x150c0a1b }, + { 216, 0x15002ccc, 0x15004982, 0x1509be55, 0x150c0a23 }, +}; + +/* + * RF value list for rt3xxx + * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052 & RF3053) + */ +static const struct rf_channel rf_vals_3x[] = { + {1, 241, 2, 2 }, + {2, 241, 2, 7 }, + {3, 242, 2, 2 }, + {4, 242, 2, 7 }, + {5, 243, 2, 2 }, + {6, 243, 2, 7 }, + {7, 244, 2, 2 }, + {8, 244, 2, 7 }, + {9, 245, 2, 2 }, + {10, 245, 2, 7 }, + {11, 246, 2, 2 }, + {12, 246, 2, 7 }, + {13, 247, 2, 2 }, + {14, 248, 2, 4 }, + + /* 802.11 UNI / HyperLan 2 */ + {36, 0x56, 0, 4}, + {38, 0x56, 0, 6}, + {40, 0x56, 0, 8}, + {44, 0x57, 0, 0}, + {46, 0x57, 0, 2}, + {48, 0x57, 0, 4}, + {52, 0x57, 0, 8}, + {54, 0x57, 0, 10}, + {56, 0x58, 0, 0}, + {60, 0x58, 0, 4}, + {62, 0x58, 0, 6}, + {64, 0x58, 0, 8}, + + /* 802.11 HyperLan 2 */ + {100, 0x5b, 0, 8}, + {102, 0x5b, 0, 10}, + {104, 0x5c, 0, 0}, + {108, 0x5c, 0, 4}, + {110, 0x5c, 0, 6}, + {112, 0x5c, 0, 8}, + {116, 0x5d, 0, 0}, + {118, 0x5d, 0, 2}, + {120, 0x5d, 0, 4}, + {124, 0x5d, 0, 8}, + {126, 0x5d, 0, 10}, + {128, 0x5e, 0, 0}, + {132, 0x5e, 0, 4}, + {134, 0x5e, 0, 6}, + {136, 0x5e, 0, 8}, + {140, 0x5f, 0, 0}, + + /* 802.11 UNII */ + {149, 0x5f, 0, 9}, + {151, 0x5f, 0, 11}, + {153, 0x60, 0, 1}, + {157, 0x60, 0, 5}, + {159, 0x60, 0, 7}, + {161, 0x60, 0, 9}, + {165, 0x61, 0, 1}, + {167, 0x61, 0, 3}, + {169, 0x61, 0, 5}, + {171, 0x61, 0, 7}, + {173, 0x61, 0, 9}, +}; + +static const struct rf_channel rf_vals_5592_xtal20[] = { + /* Channel, N, K, mod, R */ + {1, 482, 4, 10, 3}, + {2, 483, 4, 10, 3}, + {3, 484, 4, 10, 3}, + {4, 485, 4, 10, 3}, + {5, 486, 4, 10, 3}, + {6, 487, 4, 10, 3}, + {7, 488, 4, 10, 3}, + {8, 489, 4, 10, 3}, + {9, 490, 4, 10, 3}, + {10, 491, 4, 10, 3}, + {11, 492, 4, 10, 3}, + {12, 493, 4, 10, 3}, + {13, 494, 4, 10, 3}, + {14, 496, 8, 10, 3}, + {36, 172, 8, 12, 1}, + {38, 173, 0, 12, 1}, + {40, 173, 4, 12, 1}, + {42, 173, 8, 12, 1}, + {44, 174, 0, 12, 1}, + {46, 174, 4, 12, 1}, + {48, 174, 8, 12, 1}, + {50, 175, 0, 12, 1}, + {52, 175, 4, 12, 1}, + {54, 175, 8, 12, 1}, + {56, 176, 0, 12, 1}, + {58, 176, 4, 12, 1}, + {60, 176, 8, 12, 1}, + {62, 177, 0, 12, 1}, + {64, 177, 4, 12, 1}, + {100, 183, 4, 12, 1}, + {102, 183, 8, 12, 1}, + {104, 184, 0, 12, 1}, + {106, 184, 4, 12, 1}, + {108, 184, 8, 12, 1}, + {110, 185, 0, 12, 1}, + {112, 185, 4, 12, 1}, + {114, 185, 8, 12, 1}, + {116, 186, 0, 12, 1}, + {118, 186, 4, 12, 1}, + {120, 186, 8, 12, 1}, + {122, 187, 0, 12, 1}, + {124, 187, 4, 12, 1}, + {126, 187, 8, 12, 1}, + {128, 188, 0, 12, 1}, + {130, 188, 4, 12, 1}, + {132, 188, 8, 12, 1}, + {134, 189, 0, 12, 1}, + {136, 189, 4, 12, 1}, + {138, 189, 8, 12, 1}, + {140, 190, 0, 12, 1}, + {149, 191, 6, 12, 1}, + {151, 191, 10, 12, 1}, + {153, 192, 2, 12, 1}, + {155, 192, 6, 12, 1}, + {157, 192, 10, 12, 1}, + {159, 193, 2, 12, 1}, + {161, 193, 6, 12, 1}, + {165, 194, 2, 12, 1}, + {184, 164, 0, 12, 1}, + {188, 164, 4, 12, 1}, + {192, 165, 8, 12, 1}, + {196, 166, 0, 12, 1}, +}; + +static const struct rf_channel rf_vals_5592_xtal40[] = { + /* Channel, N, K, mod, R */ + {1, 241, 2, 10, 3}, + {2, 241, 7, 10, 3}, + {3, 242, 2, 10, 3}, + {4, 242, 7, 10, 3}, + {5, 243, 2, 10, 3}, + {6, 243, 7, 10, 3}, + {7, 244, 2, 10, 3}, + {8, 244, 7, 10, 3}, + {9, 245, 2, 10, 3}, + {10, 245, 7, 10, 3}, + {11, 246, 2, 10, 3}, + {12, 246, 7, 10, 3}, + {13, 247, 2, 10, 3}, + {14, 248, 4, 10, 3}, + {36, 86, 4, 12, 1}, + {38, 86, 6, 12, 1}, + {40, 86, 8, 12, 1}, + {42, 86, 10, 12, 1}, + {44, 87, 0, 12, 1}, + {46, 87, 2, 12, 1}, + {48, 87, 4, 12, 1}, + {50, 87, 6, 12, 1}, + {52, 87, 8, 12, 1}, + {54, 87, 10, 12, 1}, + {56, 88, 0, 12, 1}, + {58, 88, 2, 12, 1}, + {60, 88, 4, 12, 1}, + {62, 88, 6, 12, 1}, + {64, 88, 8, 12, 1}, + {100, 91, 8, 12, 1}, + {102, 91, 10, 12, 1}, + {104, 92, 0, 12, 1}, + {106, 92, 2, 12, 1}, + {108, 92, 4, 12, 1}, + {110, 92, 6, 12, 1}, + {112, 92, 8, 12, 1}, + {114, 92, 10, 12, 1}, + {116, 93, 0, 12, 1}, + {118, 93, 2, 12, 1}, + {120, 93, 4, 12, 1}, + {122, 93, 6, 12, 1}, + {124, 93, 8, 12, 1}, + {126, 93, 10, 12, 1}, + {128, 94, 0, 12, 1}, + {130, 94, 2, 12, 1}, + {132, 94, 4, 12, 1}, + {134, 94, 6, 12, 1}, + {136, 94, 8, 12, 1}, + {138, 94, 10, 12, 1}, + {140, 95, 0, 12, 1}, + {149, 95, 9, 12, 1}, + {151, 95, 11, 12, 1}, + {153, 96, 1, 12, 1}, + {155, 96, 3, 12, 1}, + {157, 96, 5, 12, 1}, + {159, 96, 7, 12, 1}, + {161, 96, 9, 12, 1}, + {165, 97, 1, 12, 1}, + {184, 82, 0, 12, 1}, + {188, 82, 4, 12, 1}, + {192, 82, 8, 12, 1}, + {196, 83, 0, 12, 1}, +}; + +static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + struct channel_info *info; + char *default_power1; + char *default_power2; + char *default_power3; + unsigned int i; + u32 reg; + + /* + * Disable powersaving as default. + */ + rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + /* + * Initialize all hw fields. + */ + ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_HT_CCK_RATES); + ieee80211_hw_set(rt2x00dev->hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(rt2x00dev->hw, AMPDU_AGGREGATION); + ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); + ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); + ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); + + /* + * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices + * unless we are capable of sending the buffered frames out after the + * DTIM transmission using rt2x00lib_beacondone. This will send out + * multicast and broadcast traffic immediately instead of buffering it + * infinitly and thus dropping it after some time. + */ + if (!rt2x00_is_usb(rt2x00dev)) + ieee80211_hw_set(rt2x00dev->hw, HOST_BROADCAST_PS_BUFFERING); + + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, + rt2800_eeprom_addr(rt2x00dev, + EEPROM_MAC_ADDR_0)); + + /* + * As rt2800 has a global fallback table we cannot specify + * more then one tx rate per frame but since the hw will + * try several rates (based on the fallback table) we should + * initialize max_report_rates to the maximum number of rates + * we are going to try. Otherwise mac80211 will truncate our + * reported tx rates and the rc algortihm will end up with + * incorrect data. + */ + rt2x00dev->hw->max_rates = 1; + rt2x00dev->hw->max_report_rates = 7; + rt2x00dev->hw->max_rate_tries = 1; + + /* + * Initialize hw_mode information. + */ + spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; + + switch (rt2x00dev->chip.rf) { + case RF2720: + case RF2820: + spec->num_channels = 14; + spec->channels = rf_vals; + break; + + case RF2750: + case RF2850: + spec->num_channels = ARRAY_SIZE(rf_vals); + spec->channels = rf_vals; + break; + + case RF2020: + case RF3020: + case RF3021: + case RF3022: + case RF3070: + case RF3290: + case RF3320: + case RF3322: + case RF5360: + case RF5362: + case RF5370: + case RF5372: + case RF5390: + case RF5392: + spec->num_channels = 14; + spec->channels = rf_vals_3x; + break; + + case RF3052: + case RF3053: + spec->num_channels = ARRAY_SIZE(rf_vals_3x); + spec->channels = rf_vals_3x; + break; + + case RF5592: + rt2800_register_read(rt2x00dev, MAC_DEBUG_INDEX, ®); + if (rt2x00_get_field32(reg, MAC_DEBUG_INDEX_XTAL)) { + spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal40); + spec->channels = rf_vals_5592_xtal40; + } else { + spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal20); + spec->channels = rf_vals_5592_xtal20; + } + break; + } + + if (WARN_ON_ONCE(!spec->channels)) + return -ENODEV; + + spec->supported_bands = SUPPORT_BAND_2GHZ; + if (spec->num_channels > 14) + spec->supported_bands |= SUPPORT_BAND_5GHZ; + + /* + * Initialize HT information. + */ + if (!rt2x00_rf(rt2x00dev, RF2020)) + spec->ht.ht_supported = true; + else + spec->ht.ht_supported = false; + + spec->ht.cap = + IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40; + + if (rt2x00dev->default_ant.tx_chain_num >= 2) + spec->ht.cap |= IEEE80211_HT_CAP_TX_STBC; + + spec->ht.cap |= rt2x00dev->default_ant.rx_chain_num << + IEEE80211_HT_CAP_RX_STBC_SHIFT; + + spec->ht.ampdu_factor = 3; + spec->ht.ampdu_density = 4; + spec->ht.mcs.tx_params = + IEEE80211_HT_MCS_TX_DEFINED | + IEEE80211_HT_MCS_TX_RX_DIFF | + ((rt2x00dev->default_ant.tx_chain_num - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + + switch (rt2x00dev->default_ant.rx_chain_num) { + case 3: + spec->ht.mcs.rx_mask[2] = 0xff; + case 2: + spec->ht.mcs.rx_mask[1] = 0xff; + case 1: + spec->ht.mcs.rx_mask[0] = 0xff; + spec->ht.mcs.rx_mask[4] = 0x1; /* MCS32 */ + break; + } + + /* + * Create channel information array + */ + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + default_power1 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1); + default_power2 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2); + + if (rt2x00dev->default_ant.tx_chain_num > 2) + default_power3 = rt2800_eeprom_addr(rt2x00dev, + EEPROM_EXT_TXPOWER_BG3); + else + default_power3 = NULL; + + for (i = 0; i < 14; i++) { + info[i].default_power1 = default_power1[i]; + info[i].default_power2 = default_power2[i]; + if (default_power3) + info[i].default_power3 = default_power3[i]; + } + + if (spec->num_channels > 14) { + default_power1 = rt2800_eeprom_addr(rt2x00dev, + EEPROM_TXPOWER_A1); + default_power2 = rt2800_eeprom_addr(rt2x00dev, + EEPROM_TXPOWER_A2); + + if (rt2x00dev->default_ant.tx_chain_num > 2) + default_power3 = + rt2800_eeprom_addr(rt2x00dev, + EEPROM_EXT_TXPOWER_A3); + else + default_power3 = NULL; + + for (i = 14; i < spec->num_channels; i++) { + info[i].default_power1 = default_power1[i - 14]; + info[i].default_power2 = default_power2[i - 14]; + if (default_power3) + info[i].default_power3 = default_power3[i - 14]; + } + } + + switch (rt2x00dev->chip.rf) { + case RF2020: + case RF3020: + case RF3021: + case RF3022: + case RF3320: + case RF3052: + case RF3053: + case RF3070: + case RF3290: + case RF5360: + case RF5362: + case RF5370: + case RF5372: + case RF5390: + case RF5392: + __set_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags); + break; + } + + return 0; +} + +static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u32 rt; + u32 rev; + + if (rt2x00_rt(rt2x00dev, RT3290)) + rt2800_register_read(rt2x00dev, MAC_CSR0_3290, ®); + else + rt2800_register_read(rt2x00dev, MAC_CSR0, ®); + + rt = rt2x00_get_field32(reg, MAC_CSR0_CHIPSET); + rev = rt2x00_get_field32(reg, MAC_CSR0_REVISION); + + switch (rt) { + case RT2860: + case RT2872: + case RT2883: + case RT3070: + case RT3071: + case RT3090: + case RT3290: + case RT3352: + case RT3390: + case RT3572: + case RT3593: + case RT5390: + case RT5392: + case RT5592: + break; + default: + rt2x00_err(rt2x00dev, "Invalid RT chipset 0x%04x, rev %04x detected\n", + rt, rev); + return -ENODEV; + } + + rt2x00_set_rt(rt2x00dev, rt, rev); + + return 0; +} + +int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + u32 reg; + + retval = rt2800_probe_rt(rt2x00dev); + if (retval) + return retval; + + /* + * Allocate eeprom data. + */ + retval = rt2800_validate_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt2800_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. + */ + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2x00_set_field32(®, GPIO_CTRL_DIR2, 1); + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + + /* + * Initialize hw specifications. + */ + retval = rt2800_probe_hw_mode(rt2x00dev); + if (retval) + return retval; + + /* + * Set device capabilities. + */ + __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); + __set_bit(CAPABILITY_CONTROL_FILTER_PSPOLL, &rt2x00dev->cap_flags); + if (!rt2x00_is_usb(rt2x00dev)) + __set_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags); + + /* + * Set device requirements. + */ + if (!rt2x00_is_soc(rt2x00dev)) + __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags); + if (!rt2800_hwcrypt_disabled(rt2x00dev)) + __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); + __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags); + if (rt2x00_is_usb(rt2x00dev)) + __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); + else { + __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags); + } + + /* + * Set the rssi offset. + */ + rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_probe_hw); + +/* + * IEEE80211 stack callback functions. + */ +void rt2800_get_key_seq(struct ieee80211_hw *hw, + struct ieee80211_key_conf *key, + struct ieee80211_key_seq *seq) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct mac_iveiv_entry iveiv_entry; + u32 offset; + + if (key->cipher != WLAN_CIPHER_SUITE_TKIP) + return; + + offset = MAC_IVEIV_ENTRY(key->hw_key_idx); + rt2800_register_multiread(rt2x00dev, offset, + &iveiv_entry, sizeof(iveiv_entry)); + + memcpy(&seq->tkip.iv16, &iveiv_entry.iv[0], 2); + memcpy(&seq->tkip.iv32, &iveiv_entry.iv[4], 4); +} +EXPORT_SYMBOL_GPL(rt2800_get_key_seq); + +int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + bool enabled = (value < IEEE80211_MAX_RTS_THRESHOLD); + + rt2800_register_read(rt2x00dev, TX_RTS_CFG, ®); + rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, value); + rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg); + + rt2800_register_read(rt2x00dev, CCK_PROT_CFG, ®); + rt2x00_set_field32(®, CCK_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); + rt2x00_set_field32(®, OFDM_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); + rt2x00_set_field32(®, MM20_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); + rt2x00_set_field32(®, MM40_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); + rt2x00_set_field32(®, GF20_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); + rt2x00_set_field32(®, GF40_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_set_rts_threshold); + +int rt2800_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, + const struct ieee80211_tx_queue_params *params) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + struct rt2x00_field32 field; + int retval; + u32 reg; + u32 offset; + + /* + * First pass the configuration through rt2x00lib, that will + * update the queue settings and validate the input. After that + * we are free to update the registers based on the value + * in the queue parameter. + */ + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); + if (retval) + return retval; + + /* + * We only need to perform additional register initialization + * for WMM queues/ + */ + if (queue_idx >= 4) + return 0; + + queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); + + /* Update WMM TXOP register */ + offset = WMM_TXOP0_CFG + (sizeof(u32) * (!!(queue_idx & 2))); + field.bit_offset = (queue_idx & 1) * 16; + field.bit_mask = 0xffff << field.bit_offset; + + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, field, queue->txop); + rt2800_register_write(rt2x00dev, offset, reg); + + /* Update WMM registers */ + field.bit_offset = queue_idx * 4; + field.bit_mask = 0xf << field.bit_offset; + + rt2800_register_read(rt2x00dev, WMM_AIFSN_CFG, ®); + rt2x00_set_field32(®, field, queue->aifs); + rt2800_register_write(rt2x00dev, WMM_AIFSN_CFG, reg); + + rt2800_register_read(rt2x00dev, WMM_CWMIN_CFG, ®); + rt2x00_set_field32(®, field, queue->cw_min); + rt2800_register_write(rt2x00dev, WMM_CWMIN_CFG, reg); + + rt2800_register_read(rt2x00dev, WMM_CWMAX_CFG, ®); + rt2x00_set_field32(®, field, queue->cw_max); + rt2800_register_write(rt2x00dev, WMM_CWMAX_CFG, reg); + + /* Update EDCA registers */ + offset = EDCA_AC0_CFG + (sizeof(u32) * queue_idx); + + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, EDCA_AC0_CFG_TX_OP, queue->txop); + rt2x00_set_field32(®, EDCA_AC0_CFG_AIFSN, queue->aifs); + rt2x00_set_field32(®, EDCA_AC0_CFG_CWMIN, queue->cw_min); + rt2x00_set_field32(®, EDCA_AC0_CFG_CWMAX, queue->cw_max); + rt2800_register_write(rt2x00dev, offset, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_conf_tx); + +u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u64 tsf; + u32 reg; + + rt2800_register_read(rt2x00dev, TSF_TIMER_DW1, ®); + tsf = (u64) rt2x00_get_field32(reg, TSF_TIMER_DW1_HIGH_WORD) << 32; + rt2800_register_read(rt2x00dev, TSF_TIMER_DW0, ®); + tsf |= rt2x00_get_field32(reg, TSF_TIMER_DW0_LOW_WORD); + + return tsf; +} +EXPORT_SYMBOL_GPL(rt2800_get_tsf); + +int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size, bool amsdu) +{ + struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv; + int ret = 0; + + /* + * Don't allow aggregation for stations the hardware isn't aware + * of because tx status reports for frames to an unknown station + * always contain wcid=WCID_END+1 and thus we can't distinguish + * between multiple stations which leads to unwanted situations + * when the hw reorders frames due to aggregation. + */ + if (sta_priv->wcid > WCID_END) + return 1; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + /* + * The hw itself takes care of setting up BlockAck mechanisms. + * So, we only have to allow mac80211 to nagotiate a BlockAck + * agreement. Once that is done, the hw will BlockAck incoming + * AMPDUs without further setup. + */ + break; + case IEEE80211_AMPDU_TX_START: + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + break; + default: + rt2x00_warn((struct rt2x00_dev *)hw->priv, + "Unknown AMPDU action\n"); + } + + return ret; +} +EXPORT_SYMBOL_GPL(rt2800_ampdu_action); + +int rt2800_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + u32 idle, busy, busy_ext; + + if (idx != 0) + return -ENOENT; + + survey->channel = conf->chandef.chan; + + rt2800_register_read(rt2x00dev, CH_IDLE_STA, &idle); + rt2800_register_read(rt2x00dev, CH_BUSY_STA, &busy); + rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, &busy_ext); + + if (idle || busy) { + survey->filled = SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_EXT_BUSY; + + survey->time = (idle + busy) / 1000; + survey->time_busy = busy / 1000; + survey->time_ext_busy = busy_ext / 1000; + } + + if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) + survey->filled |= SURVEY_INFO_IN_USE; + + return 0; + +} +EXPORT_SYMBOL_GPL(rt2800_get_survey); + +MODULE_AUTHOR(DRV_PROJECT ", Bartlomiej Zolnierkiewicz"); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2800 library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h new file mode 100644 index 000000000000..440790b92b19 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h @@ -0,0 +1,232 @@ +/* + Copyright (C) 2010 Willow Garage + Copyright (C) 2010 Ivo van Doorn + Copyright (C) 2009 Bartlomiej Zolnierkiewicz + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +#ifndef RT2800LIB_H +#define RT2800LIB_H + +struct rt2800_ops { + void (*register_read)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 *value); + void (*register_read_lock)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 *value); + void (*register_write)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 value); + void (*register_write_lock)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 value); + + void (*register_multiread)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u32 length); + void (*register_multiwrite)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const void *value, const u32 length); + + int (*regbusy_read)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, u32 *reg); + + int (*read_eeprom)(struct rt2x00_dev *rt2x00dev); + bool (*hwcrypt_disabled)(struct rt2x00_dev *rt2x00dev); + + int (*drv_write_firmware)(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len); + int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev); + __le32 *(*drv_get_txwi)(struct queue_entry *entry); +}; + +static inline void rt2800_register_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_read(rt2x00dev, offset, value); +} + +static inline void rt2800_register_read_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_read_lock(rt2x00dev, offset, value); +} + +static inline void rt2800_register_write(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_write(rt2x00dev, offset, value); +} + +static inline void rt2800_register_write_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_write_lock(rt2x00dev, offset, value); +} + +static inline void rt2800_register_multiread(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u32 length) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_multiread(rt2x00dev, offset, value, length); +} + +static inline void rt2800_register_multiwrite(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const void *value, + const u32 length) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_multiwrite(rt2x00dev, offset, value, length); +} + +static inline int rt2800_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, + u32 *reg) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + return rt2800ops->regbusy_read(rt2x00dev, offset, field, reg); +} + +static inline int rt2800_read_eeprom(struct rt2x00_dev *rt2x00dev) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + return rt2800ops->read_eeprom(rt2x00dev); +} + +static inline bool rt2800_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + return rt2800ops->hwcrypt_disabled(rt2x00dev); +} + +static inline int rt2800_drv_write_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + return rt2800ops->drv_write_firmware(rt2x00dev, data, len); +} + +static inline int rt2800_drv_init_registers(struct rt2x00_dev *rt2x00dev) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + return rt2800ops->drv_init_registers(rt2x00dev); +} + +static inline __le32 *rt2800_drv_get_txwi(struct queue_entry *entry) +{ + const struct rt2800_ops *rt2800ops = entry->queue->rt2x00dev->ops->drv; + + return rt2800ops->drv_get_txwi(entry); +} + +void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, + const u8 command, const u8 token, + const u8 arg0, const u8 arg1); + +int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev); +int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev); + +int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len); +int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len); + +void rt2800_write_tx_data(struct queue_entry *entry, + struct txentry_desc *txdesc); +void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc); + +void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32* txwi); + +void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc); +void rt2800_clear_beacon(struct queue_entry *entry); + +extern const struct rt2x00debug rt2800_rt2x00debug; + +int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev); +int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); +int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); +int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, int wcid); +void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags); +void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, const unsigned int flags); +void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, + u32 changed); +void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant); +void rt2800_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags); +void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual); +void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual); +void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, + const u32 count); +void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev); +void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev); + +int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev); +void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev); + +int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev); +int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev); + +int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev); + +void rt2800_get_key_seq(struct ieee80211_hw *hw, + struct ieee80211_key_conf *key, + struct ieee80211_key_seq *seq); +int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value); +int rt2800_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, + const struct ieee80211_tx_queue_params *params); +u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size, bool amsdu); +int rt2800_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey); +void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev); + +void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev, + unsigned short *txwi_size, + unsigned short *rxwi_size); + +#endif /* RT2800LIB_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c new file mode 100644 index 000000000000..de4790b41be7 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c @@ -0,0 +1,871 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn + * Copyright (C) 2009 Alban Browaeys + * Copyright (C) 2009 Felix Fietkau + * Copyright (C) 2009 Luis Correia + * Copyright (C) 2009 Mattias Nissler + * Copyright (C) 2009 Mark Asselstine + * Copyright (C) 2009 Xose Vazquez Perez + * Copyright (C) 2009 Bart Zolnierkiewicz + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* Module: rt2800mmio + * Abstract: rt2800 MMIO device routines. + */ + +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2800.h" +#include "rt2800lib.h" +#include "rt2800mmio.h" + +/* + * TX descriptor initialization + */ +__le32 *rt2800mmio_get_txwi(struct queue_entry *entry) +{ + return (__le32 *) entry->skb->data; +} +EXPORT_SYMBOL_GPL(rt2800mmio_get_txwi); + +void rt2800mmio_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *txd = entry_priv->desc; + u32 word; + const unsigned int txwi_size = entry->queue->winfo_size; + + /* + * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 + * must contains a TXWI structure + 802.11 header + padding + 802.11 + * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and + * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11 + * data. It means that LAST_SEC0 is always 0. + */ + + /* + * Initialize TX descriptor + */ + word = 0; + rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma); + rt2x00_desc_write(txd, 0, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len); + rt2x00_set_field32(&word, TXD_W1_LAST_SEC1, + !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_BURST, + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size); + rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); + rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); + rt2x00_desc_write(txd, 1, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W2_SD_PTR1, + skbdesc->skb_dma + txwi_size); + rt2x00_desc_write(txd, 2, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W3_WIV, + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W3_QSEL, 2); + rt2x00_desc_write(txd, 3, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; +} +EXPORT_SYMBOL_GPL(rt2800mmio_write_tx_desc); + +/* + * RX control handlers + */ +void rt2800mmio_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *rxd = entry_priv->desc; + u32 word; + + rt2x00_desc_read(rxd, 3, &word); + + if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + + /* + * Unfortunately we don't know the cipher type used during + * decryption. This prevents us from correct providing + * correct statistics through debugfs. + */ + rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR); + + if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) { + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. Unfortunately the descriptor doesn't contain + * any fields with the EIV/IV data either, so they can't + * be restored by rt2x00lib. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * The hardware has already checked the Michael Mic and has + * stripped it from the frame. Signal this to mac80211. + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + + if (rt2x00_get_field32(word, RXD_W3_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; + + if (rt2x00_get_field32(word, RXD_W3_L2PAD)) + rxdesc->dev_flags |= RXDONE_L2PAD; + + /* + * Process the RXWI structure that is at the start of the buffer. + */ + rt2800_process_rxwi(entry, rxdesc); +} +EXPORT_SYMBOL_GPL(rt2800mmio_fill_rxdone); + +/* + * Interrupt functions. + */ +static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_conf conf = { .flags = 0 }; + struct rt2x00lib_conf libconf = { .conf = &conf }; + + rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); +} + +static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status) +{ + __le32 *txwi; + u32 word; + int wcid, tx_wcid; + + wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); + + txwi = rt2800_drv_get_txwi(entry); + rt2x00_desc_read(txwi, 1, &word); + tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); + + return (tx_wcid == wcid); +} + +static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data) +{ + u32 status = *(u32 *)data; + + /* + * rt2800pci hardware might reorder frames when exchanging traffic + * with multiple BA enabled STAs. + * + * For example, a tx queue + * [ STA1 | STA2 | STA1 | STA2 ] + * can result in tx status reports + * [ STA1 | STA1 | STA2 | STA2 ] + * when the hw decides to aggregate the frames for STA1 into one AMPDU. + * + * To mitigate this effect, associate the tx status to the first frame + * in the tx queue with a matching wcid. + */ + if (rt2800mmio_txdone_entry_check(entry, status) && + !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + /* + * Got a matching frame, associate the tx status with + * the frame + */ + entry->status = status; + set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); + return true; + } + + /* Check the next frame */ + return false; +} + +static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data) +{ + u32 status = *(u32 *)data; + + /* + * Find the first frame without tx status and assign this status to it + * regardless if it matches or not. + */ + if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + /* + * Got a matching frame, associate the tx status with + * the frame + */ + entry->status = status; + set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); + return true; + } + + /* Check the next frame */ + return false; +} +static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry, + void *data) +{ + if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + rt2800_txdone_entry(entry, entry->status, + rt2800mmio_get_txwi(entry)); + return false; + } + + /* No more frames to release */ + return true; +} + +static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + u32 status; + u8 qid; + int max_tx_done = 16; + + while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { + qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); + if (unlikely(qid >= QID_RX)) { + /* + * Unknown queue, this shouldn't happen. Just drop + * this tx status. + */ + rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n", + qid); + break; + } + + queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); + if (unlikely(queue == NULL)) { + /* + * The queue is NULL, this shouldn't happen. Stop + * processing here and drop the tx status + */ + rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n", + qid); + break; + } + + if (unlikely(rt2x00queue_empty(queue))) { + /* + * The queue is empty. Stop processing here + * and drop the tx status. + */ + rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", + qid); + break; + } + + /* + * Let's associate this tx status with the first + * matching frame. + */ + if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, &status, + rt2800mmio_txdone_find_entry)) { + /* + * We cannot match the tx status to any frame, so just + * use the first one. + */ + if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, &status, + rt2800mmio_txdone_match_first)) { + rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n", + qid); + break; + } + } + + /* + * Release all frames with a valid tx status. + */ + rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, NULL, + rt2800mmio_txdone_release_entries); + + if (--max_tx_done == 0) + break; + } + + return !max_tx_done; +} + +static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + rt2x00_set_field32(®, irq_field, 1); + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +void rt2800mmio_txstatus_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2800mmio_txdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->txstatus_tasklet); + + /* + * No need to enable the tx status interrupt here as we always + * leave it enabled to minimize the possibility of a tx status + * register overflow. See comment in interrupt handler. + */ +} +EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet); + +void rt2800mmio_pretbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2x00lib_pretbtt(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); +} +EXPORT_SYMBOL_GPL(rt2800mmio_pretbtt_tasklet); + +void rt2800mmio_tbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u32 reg; + + rt2x00lib_beacondone(rt2x00dev); + + if (rt2x00dev->intf_ap_count) { + /* + * The rt2800pci hardware tbtt timer is off by 1us per tbtt + * causing beacon skew and as a result causing problems with + * some powersaving clients over time. Shorten the beacon + * interval every 64 beacons by 64us to mitigate this effect. + */ + if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, + (rt2x00dev->beacon_int * 16) - 1); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, + (rt2x00dev->beacon_int * 16)); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } + drv_data->tbtt_tick++; + drv_data->tbtt_tick %= BCN_TBTT_OFFSET; + } + + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); +} +EXPORT_SYMBOL_GPL(rt2800mmio_tbtt_tasklet); + +void rt2800mmio_rxdone_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2x00mmio_rxdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); +} +EXPORT_SYMBOL_GPL(rt2800mmio_rxdone_tasklet); + +void rt2800mmio_autowake_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2800mmio_wakeup(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, + INT_MASK_CSR_AUTO_WAKEUP); +} +EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet); + +static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) +{ + u32 status; + int i; + + /* + * The TX_FIFO_STATUS interrupt needs special care. We should + * read TX_STA_FIFO but we should do it immediately as otherwise + * the register can overflow and we would lose status reports. + * + * Hence, read the TX_STA_FIFO register and copy all tx status + * reports into a kernel FIFO which is handled in the txstatus + * tasklet. We use a tasklet to process the tx status reports + * because we can schedule the tasklet multiple times (when the + * interrupt fires again during tx status processing). + * + * Furthermore we don't disable the TX_FIFO_STATUS + * interrupt here but leave it enabled so that the TX_STA_FIFO + * can also be read while the tx status tasklet gets executed. + * + * Since we have only one producer and one consumer we don't + * need to lock the kfifo. + */ + for (i = 0; i < rt2x00dev->tx->limit; i++) { + rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); + + if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) + break; + + if (!kfifo_put(&rt2x00dev->txstatus_fifo, status)) { + rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n"); + break; + } + } + + /* Schedule the tasklet for processing the tx status. */ + tasklet_schedule(&rt2x00dev->txstatus_tasklet); +} + +irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + u32 reg, mask; + + /* Read status and ACK all interrupts */ + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + + if (!reg) + return IRQ_NONE; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return IRQ_HANDLED; + + /* + * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits + * for interrupts and interrupt masks we can just use the value of + * INT_SOURCE_CSR to create the interrupt mask. + */ + mask = ~reg; + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { + rt2800mmio_txstatus_interrupt(rt2x00dev); + /* + * Never disable the TX_FIFO_STATUS interrupt. + */ + rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); + } + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) + tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) + tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) + tasklet_schedule(&rt2x00dev->autowake_tasklet); + + /* + * Disable all interrupts for which a tasklet was scheduled right now, + * the tasklet will reenable the appropriate interrupts. + */ + spin_lock(&rt2x00dev->irqmask_lock); + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + reg &= mask; + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock(&rt2x00dev->irqmask_lock); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(rt2800mmio_interrupt); + +void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u32 reg; + unsigned long flags; + + /* + * When interrupts are being enabled, the interrupt registers + * should clear the register to assure a clean state. + */ + if (state == STATE_RADIO_IRQ_ON) { + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + } + + spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); + reg = 0; + if (state == STATE_RADIO_IRQ_ON) { + rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); + rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); + } + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); + + if (state == STATE_RADIO_IRQ_OFF) { + /* + * Wait for possibly running tasklets to finish. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->autowake_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + tasklet_kill(&rt2x00dev->pretbtt_tasklet); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_toggle_irq); + +/* + * Queue handlers. + */ +void rt2800mmio_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); + rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 1); + rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_start_queue); + +void rt2800mmio_kick_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + struct queue_entry *entry; + + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + entry = rt2x00queue_get_entry(queue, Q_INDEX); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid), + entry->entry_idx); + break; + case QID_MGMT: + entry = rt2x00queue_get_entry(queue, Q_INDEX); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5), + entry->entry_idx); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_kick_queue); + +void rt2800mmio_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); + rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 0); + rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); + + /* + * Wait for current invocation to finish. The tasklet + * won't be scheduled anymore afterwards since we disabled + * the TBTT and PRE TBTT timer. + */ + tasklet_kill(&rt2x00dev->tbtt_tasklet); + tasklet_kill(&rt2x00dev->pretbtt_tasklet); + + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_stop_queue); + +void rt2800mmio_queue_init(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + unsigned short txwi_size, rxwi_size; + + rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); + + switch (queue->qid) { + case QID_RX: + queue->limit = 128; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->winfo_size = rxwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 64; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_BEACON: + queue->limit = 8; + queue->data_size = 0; /* No DMA required for beacons */ + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_queue_init); + +/* + * Initialization functions. + */ +bool rt2800mmio_get_entry_state(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 1, &word); + + return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE)); + } else { + rt2x00_desc_read(entry_priv->desc, 1, &word); + + return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE)); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_get_entry_state); + +void rt2800mmio_clear_entry(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma); + rt2x00_desc_write(entry_priv->desc, 0, word); + + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0); + rt2x00_desc_write(entry_priv->desc, 1, word); + + /* + * Set RX IDX in register to inform hardware that we have + * handled this entry and it is available for reuse again. + */ + rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, + entry->entry_idx); + } else { + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1); + rt2x00_desc_write(entry_priv->desc, 1, word); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_clear_entry); + +int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev) +{ + struct queue_entry_priv_mmio *entry_priv; + + /* + * Initialize registers. + */ + entry_priv = rt2x00dev->tx[0].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0, + rt2x00dev->tx[0].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0); + + entry_priv = rt2x00dev->tx[1].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1, + rt2x00dev->tx[1].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0); + + entry_priv = rt2x00dev->tx[2].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2, + rt2x00dev->tx[2].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0); + + entry_priv = rt2x00dev->tx[3].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3, + rt2x00dev->tx[3].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0); + + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0); + + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0); + + entry_priv = rt2x00dev->rx->entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT, + rt2x00dev->rx[0].limit); + rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, + rt2x00dev->rx[0].limit - 1); + rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0); + + rt2800_disable_wpdma(rt2x00dev); + + rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800mmio_init_queues); + +int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Reset DMA indexes + */ + rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX, ®); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); + rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg); + + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); + + if (rt2x00_is_pcie(rt2x00dev) && + (rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3390) || + rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT5592))) { + rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, ®); + rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); + rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); + rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg); + } + + rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); + + reg = 0; + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800mmio_init_registers); + +/* + * Device state switch handlers. + */ +int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + /* Wait for DMA, ignore error until we initialize queues. */ + rt2800_wait_wpdma_ready(rt2x00dev); + + if (unlikely(rt2800mmio_init_queues(rt2x00dev))) + return -EIO; + + return rt2800_enable_radio(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2800mmio_enable_radio); + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2800 MMIO library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h new file mode 100644 index 000000000000..b63312ce3f27 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h @@ -0,0 +1,163 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn + * Copyright (C) 2009 Alban Browaeys + * Copyright (C) 2009 Felix Fietkau + * Copyright (C) 2009 Luis Correia + * Copyright (C) 2009 Mattias Nissler + * Copyright (C) 2009 Mark Asselstine + * Copyright (C) 2009 Xose Vazquez Perez + * Copyright (C) 2009 Bart Zolnierkiewicz + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* Module: rt2800mmio + * Abstract: forward declarations for the rt2800mmio module. + */ + +#ifndef RT2800MMIO_H +#define RT2800MMIO_H + +/* + * Queue register offset macros + */ +#define TX_QUEUE_REG_OFFSET 0x10 +#define TX_BASE_PTR(__x) (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_MAX_CNT(__x) (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_CTX_IDX(__x) (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_DTX_IDX(__x) (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE (4 * sizeof(__le32)) +#define RXD_DESC_SIZE (4 * sizeof(__le32)) + +/* + * TX descriptor format for TX, PRIO and Beacon Ring. + */ + +/* + * Word0 + */ +#define TXD_W0_SD_PTR0 FIELD32(0xffffffff) + +/* + * Word1 + */ +#define TXD_W1_SD_LEN1 FIELD32(0x00003fff) +#define TXD_W1_LAST_SEC1 FIELD32(0x00004000) +#define TXD_W1_BURST FIELD32(0x00008000) +#define TXD_W1_SD_LEN0 FIELD32(0x3fff0000) +#define TXD_W1_LAST_SEC0 FIELD32(0x40000000) +#define TXD_W1_DMA_DONE FIELD32(0x80000000) + +/* + * Word2 + */ +#define TXD_W2_SD_PTR1 FIELD32(0xffffffff) + +/* + * Word3 + * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI + * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. + * 0:MGMT, 1:HCCA 2:EDCA + */ +#define TXD_W3_WIV FIELD32(0x01000000) +#define TXD_W3_QSEL FIELD32(0x06000000) +#define TXD_W3_TCO FIELD32(0x20000000) +#define TXD_W3_UCO FIELD32(0x40000000) +#define TXD_W3_ICO FIELD32(0x80000000) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + */ +#define RXD_W0_SDP0 FIELD32(0xffffffff) + +/* + * Word1 + */ +#define RXD_W1_SDL1 FIELD32(0x00003fff) +#define RXD_W1_SDL0 FIELD32(0x3fff0000) +#define RXD_W1_LS0 FIELD32(0x40000000) +#define RXD_W1_DMA_DONE FIELD32(0x80000000) + +/* + * Word2 + */ +#define RXD_W2_SDP1 FIELD32(0xffffffff) + +/* + * Word3 + * AMSDU: RX with 802.3 header, not 802.11 header. + * DECRYPTED: This frame is being decrypted. + */ +#define RXD_W3_BA FIELD32(0x00000001) +#define RXD_W3_DATA FIELD32(0x00000002) +#define RXD_W3_NULLDATA FIELD32(0x00000004) +#define RXD_W3_FRAG FIELD32(0x00000008) +#define RXD_W3_UNICAST_TO_ME FIELD32(0x00000010) +#define RXD_W3_MULTICAST FIELD32(0x00000020) +#define RXD_W3_BROADCAST FIELD32(0x00000040) +#define RXD_W3_MY_BSS FIELD32(0x00000080) +#define RXD_W3_CRC_ERROR FIELD32(0x00000100) +#define RXD_W3_CIPHER_ERROR FIELD32(0x00000600) +#define RXD_W3_AMSDU FIELD32(0x00000800) +#define RXD_W3_HTC FIELD32(0x00001000) +#define RXD_W3_RSSI FIELD32(0x00002000) +#define RXD_W3_L2PAD FIELD32(0x00004000) +#define RXD_W3_AMPDU FIELD32(0x00008000) +#define RXD_W3_DECRYPTED FIELD32(0x00010000) +#define RXD_W3_PLCP_SIGNAL FIELD32(0x00020000) +#define RXD_W3_PLCP_RSSI FIELD32(0x00040000) + +/* TX descriptor initialization */ +__le32 *rt2800mmio_get_txwi(struct queue_entry *entry); +void rt2800mmio_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc); + +/* RX control handlers */ +void rt2800mmio_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc); + +/* Interrupt functions */ +void rt2800mmio_txstatus_tasklet(unsigned long data); +void rt2800mmio_pretbtt_tasklet(unsigned long data); +void rt2800mmio_tbtt_tasklet(unsigned long data); +void rt2800mmio_rxdone_tasklet(unsigned long data); +void rt2800mmio_autowake_tasklet(unsigned long data); +irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance); +void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state); + +/* Queue handlers */ +void rt2800mmio_start_queue(struct data_queue *queue); +void rt2800mmio_kick_queue(struct data_queue *queue); +void rt2800mmio_stop_queue(struct data_queue *queue); +void rt2800mmio_queue_init(struct data_queue *queue); + +/* Initialization functions */ +bool rt2800mmio_get_entry_state(struct queue_entry *entry); +void rt2800mmio_clear_entry(struct queue_entry *entry); +int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev); +int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev); + +/* Device state switch handlers. */ +int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev); + +#endif /* RT2800MMIO_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c new file mode 100644 index 000000000000..0af22573a2eb --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c @@ -0,0 +1,471 @@ +/* + Copyright (C) 2009 - 2010 Ivo van Doorn + Copyright (C) 2009 Alban Browaeys + Copyright (C) 2009 Felix Fietkau + Copyright (C) 2009 Luis Correia + Copyright (C) 2009 Mattias Nissler + Copyright (C) 2009 Mark Asselstine + Copyright (C) 2009 Xose Vazquez Perez + Copyright (C) 2009 Bart Zolnierkiewicz + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2800pci + Abstract: rt2800pci device specific routines. + Supported chipsets: RT2800E & RT2800ED. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2x00pci.h" +#include "rt2800lib.h" +#include "rt2800mmio.h" +#include "rt2800.h" +#include "rt2800pci.h" + +/* + * Allow hardware encryption to be disabled. + */ +static bool modparam_nohwcrypt = false; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +static bool rt2800pci_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) +{ + return modparam_nohwcrypt; +} + +static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token) +{ + unsigned int i; + u32 reg; + + /* + * SOC devices don't support MCU requests. + */ + if (rt2x00_is_soc(rt2x00dev)) + return; + + for (i = 0; i < 200; i++) { + rt2x00mmio_register_read(rt2x00dev, H2M_MAILBOX_CID, ®); + + if ((rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD0) == token) || + (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD1) == token) || + (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD2) == token) || + (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD3) == token)) + break; + + udelay(REGISTER_BUSY_DELAY); + } + + if (i == 200) + rt2x00_err(rt2x00dev, "MCU request failed, no response from hardware\n"); + + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); +} + +static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); + + eeprom->reg_data_in = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_IN); + eeprom->reg_data_out = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_OUT); + eeprom->reg_data_clock = + !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_CLOCK); + eeprom->reg_chip_select = + !!rt2x00_get_field32(reg, E2PROM_CSR_CHIP_SELECT); +} + +static void rt2800pci_eepromregister_write(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg = 0; + + rt2x00_set_field32(®, E2PROM_CSR_DATA_IN, !!eeprom->reg_data_in); + rt2x00_set_field32(®, E2PROM_CSR_DATA_OUT, !!eeprom->reg_data_out); + rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, + !!eeprom->reg_data_clock); + rt2x00_set_field32(®, E2PROM_CSR_CHIP_SELECT, + !!eeprom->reg_chip_select); + + rt2x00mmio_register_write(rt2x00dev, E2PROM_CSR, reg); +} + +static int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) +{ + struct eeprom_93cx6 eeprom; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); + + eeprom.data = rt2x00dev; + eeprom.register_read = rt2800pci_eepromregister_read; + eeprom.register_write = rt2800pci_eepromregister_write; + switch (rt2x00_get_field32(reg, E2PROM_CSR_TYPE)) + { + case 0: + eeprom.width = PCI_EEPROM_WIDTH_93C46; + break; + case 1: + eeprom.width = PCI_EEPROM_WIDTH_93C66; + break; + default: + eeprom.width = PCI_EEPROM_WIDTH_93C86; + break; + } + eeprom.reg_data_in = 0; + eeprom.reg_data_out = 0; + eeprom.reg_data_clock = 0; + eeprom.reg_chip_select = 0; + + eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, + EEPROM_SIZE / sizeof(u16)); + + return 0; +} + +static int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) +{ + return rt2800_efuse_detect(rt2x00dev); +} + +static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) +{ + return rt2800_read_eeprom_efuse(rt2x00dev); +} + +/* + * Firmware functions + */ +static char *rt2800pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) +{ + /* + * Chip rt3290 use specific 4KB firmware named rt3290.bin. + */ + if (rt2x00_rt(rt2x00dev, RT3290)) + return FIRMWARE_RT3290; + else + return FIRMWARE_RT2860; +} + +static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + u32 reg; + + /* + * enable Host program ram write selection + */ + reg = 0; + rt2x00_set_field32(®, PBF_SYS_CTRL_HOST_RAM_WRITE, 1); + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, reg); + + /* + * Write firmware to device. + */ + rt2x00mmio_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, + data, len); + + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000); + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001); + + rt2x00mmio_register_write(rt2x00dev, H2M_BBP_AGENT, 0); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + + return 0; +} + +/* + * Device state switch handlers. + */ +static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + retval = rt2800mmio_enable_radio(rt2x00dev); + if (retval) + return retval; + + /* After resume MCU_BOOT_SIGNAL will trash these. */ + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); + + rt2800_mcu_request(rt2x00dev, MCU_SLEEP, TOKEN_RADIO_OFF, 0xff, 0x02); + rt2800pci_mcu_status(rt2x00dev, TOKEN_RADIO_OFF); + + rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKEUP, 0, 0); + rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKEUP); + + return retval; +} + +static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + if (state == STATE_AWAKE) { + rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKEUP, + 0, 0x02); + rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKEUP); + } else if (state == STATE_SLEEP) { + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_STATUS, + 0xffffffff); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, + 0xffffffff); + rt2800_mcu_request(rt2x00dev, MCU_SLEEP, TOKEN_SLEEP, + 0xff, 0x01); + } + + return 0; +} + +static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + retval = rt2800pci_enable_radio(rt2x00dev); + break; + case STATE_RADIO_OFF: + /* + * After the radio has been disabled, the device should + * be put to sleep for powersaving. + */ + rt2800pci_set_state(rt2x00dev, STATE_SLEEP); + break; + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt2800mmio_toggle_irq(rt2x00dev, state); + break; + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + retval = rt2800pci_set_state(rt2x00dev, state); + break; + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +/* + * Device probe functions. + */ +static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + if (rt2800pci_efuse_detect(rt2x00dev)) + retval = rt2800pci_read_eeprom_efuse(rt2x00dev); + else + retval = rt2800pci_read_eeprom_pci(rt2x00dev); + + return retval; +} + +static const struct ieee80211_ops rt2800pci_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .get_key_seq = rt2800_get_key_seq, + .set_rts_threshold = rt2800_set_rts_threshold, + .sta_add = rt2x00mac_sta_add, + .sta_remove = rt2x00mac_sta_remove, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2800_conf_tx, + .get_tsf = rt2800_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, + .ampdu_action = rt2800_ampdu_action, + .flush = rt2x00mac_flush, + .get_survey = rt2800_get_survey, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2800_ops rt2800pci_rt2800_ops = { + .register_read = rt2x00mmio_register_read, + .register_read_lock = rt2x00mmio_register_read, /* same for PCI */ + .register_write = rt2x00mmio_register_write, + .register_write_lock = rt2x00mmio_register_write, /* same for PCI */ + .register_multiread = rt2x00mmio_register_multiread, + .register_multiwrite = rt2x00mmio_register_multiwrite, + .regbusy_read = rt2x00mmio_regbusy_read, + .read_eeprom = rt2800pci_read_eeprom, + .hwcrypt_disabled = rt2800pci_hwcrypt_disabled, + .drv_write_firmware = rt2800pci_write_firmware, + .drv_init_registers = rt2800mmio_init_registers, + .drv_get_txwi = rt2800mmio_get_txwi, +}; + +static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { + .irq_handler = rt2800mmio_interrupt, + .txstatus_tasklet = rt2800mmio_txstatus_tasklet, + .pretbtt_tasklet = rt2800mmio_pretbtt_tasklet, + .tbtt_tasklet = rt2800mmio_tbtt_tasklet, + .rxdone_tasklet = rt2800mmio_rxdone_tasklet, + .autowake_tasklet = rt2800mmio_autowake_tasklet, + .probe_hw = rt2800_probe_hw, + .get_firmware_name = rt2800pci_get_firmware_name, + .check_firmware = rt2800_check_firmware, + .load_firmware = rt2800_load_firmware, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt2800mmio_get_entry_state, + .clear_entry = rt2800mmio_clear_entry, + .set_device_state = rt2800pci_set_device_state, + .rfkill_poll = rt2800_rfkill_poll, + .link_stats = rt2800_link_stats, + .reset_tuner = rt2800_reset_tuner, + .link_tuner = rt2800_link_tuner, + .gain_calibration = rt2800_gain_calibration, + .vco_calibration = rt2800_vco_calibration, + .start_queue = rt2800mmio_start_queue, + .kick_queue = rt2800mmio_kick_queue, + .stop_queue = rt2800mmio_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, + .write_tx_desc = rt2800mmio_write_tx_desc, + .write_tx_data = rt2800_write_tx_data, + .write_beacon = rt2800_write_beacon, + .clear_beacon = rt2800_clear_beacon, + .fill_rxdone = rt2800mmio_fill_rxdone, + .config_shared_key = rt2800_config_shared_key, + .config_pairwise_key = rt2800_config_pairwise_key, + .config_filter = rt2800_config_filter, + .config_intf = rt2800_config_intf, + .config_erp = rt2800_config_erp, + .config_ant = rt2800_config_ant, + .config = rt2800_config, + .sta_add = rt2800_sta_add, + .sta_remove = rt2800_sta_remove, +}; + +static const struct rt2x00_ops rt2800pci_ops = { + .name = KBUILD_MODNAME, + .drv_data_size = sizeof(struct rt2800_drv_data), + .max_ap_intf = 8, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2800mmio_queue_init, + .lib = &rt2800pci_rt2x00_ops, + .drv = &rt2800pci_rt2800_ops, + .hw = &rt2800pci_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2800_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +/* + * RT2800pci module information. + */ +static const struct pci_device_id rt2800pci_device_table[] = { + { PCI_DEVICE(0x1814, 0x0601) }, + { PCI_DEVICE(0x1814, 0x0681) }, + { PCI_DEVICE(0x1814, 0x0701) }, + { PCI_DEVICE(0x1814, 0x0781) }, + { PCI_DEVICE(0x1814, 0x3090) }, + { PCI_DEVICE(0x1814, 0x3091) }, + { PCI_DEVICE(0x1814, 0x3092) }, + { PCI_DEVICE(0x1432, 0x7708) }, + { PCI_DEVICE(0x1432, 0x7727) }, + { PCI_DEVICE(0x1432, 0x7728) }, + { PCI_DEVICE(0x1432, 0x7738) }, + { PCI_DEVICE(0x1432, 0x7748) }, + { PCI_DEVICE(0x1432, 0x7758) }, + { PCI_DEVICE(0x1432, 0x7768) }, + { PCI_DEVICE(0x1462, 0x891a) }, + { PCI_DEVICE(0x1a3b, 0x1059) }, +#ifdef CONFIG_RT2800PCI_RT3290 + { PCI_DEVICE(0x1814, 0x3290) }, +#endif +#ifdef CONFIG_RT2800PCI_RT33XX + { PCI_DEVICE(0x1814, 0x3390) }, +#endif +#ifdef CONFIG_RT2800PCI_RT35XX + { PCI_DEVICE(0x1432, 0x7711) }, + { PCI_DEVICE(0x1432, 0x7722) }, + { PCI_DEVICE(0x1814, 0x3060) }, + { PCI_DEVICE(0x1814, 0x3062) }, + { PCI_DEVICE(0x1814, 0x3562) }, + { PCI_DEVICE(0x1814, 0x3592) }, + { PCI_DEVICE(0x1814, 0x3593) }, + { PCI_DEVICE(0x1814, 0x359f) }, +#endif +#ifdef CONFIG_RT2800PCI_RT53XX + { PCI_DEVICE(0x1814, 0x5360) }, + { PCI_DEVICE(0x1814, 0x5362) }, + { PCI_DEVICE(0x1814, 0x5390) }, + { PCI_DEVICE(0x1814, 0x5392) }, + { PCI_DEVICE(0x1814, 0x539a) }, + { PCI_DEVICE(0x1814, 0x539b) }, + { PCI_DEVICE(0x1814, 0x539f) }, +#endif + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2800 PCI & PCMCIA Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2860 PCI & PCMCIA chipset based cards"); +MODULE_FIRMWARE(FIRMWARE_RT2860); +MODULE_DEVICE_TABLE(pci, rt2800pci_device_table); +MODULE_LICENSE("GPL"); + +static int rt2800pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + return rt2x00pci_probe(pci_dev, &rt2800pci_ops); +} + +static struct pci_driver rt2800pci_driver = { + .name = KBUILD_MODNAME, + .id_table = rt2800pci_device_table, + .probe = rt2800pci_probe, + .remove = rt2x00pci_remove, + .suspend = rt2x00pci_suspend, + .resume = rt2x00pci_resume, +}; + +module_pci_driver(rt2800pci_driver); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.h b/drivers/net/wireless/ralink/rt2x00/rt2800pci.h new file mode 100644 index 000000000000..9dfef4607d6b --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2009 Ivo van Doorn + Copyright (C) 2009 Alban Browaeys + Copyright (C) 2009 Felix Fietkau + Copyright (C) 2009 Luis Correia + Copyright (C) 2009 Mattias Nissler + Copyright (C) 2009 Mark Asselstine + Copyright (C) 2009 Xose Vazquez Perez + Copyright (C) 2009 Bart Zolnierkiewicz + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2800pci + Abstract: Data structures and registers for the rt2800pci module. + Supported chipsets: RT2800E & RT2800ED. + */ + +#ifndef RT2800PCI_H +#define RT2800PCI_H + +/* + * 8051 firmware image. + */ +#define FIRMWARE_RT2860 "rt2860.bin" +#define FIRMWARE_RT3290 "rt3290.bin" +#define FIRMWARE_IMAGE_BASE 0x2000 + +#endif /* RT2800PCI_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c new file mode 100644 index 000000000000..a985a5a7945e --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c @@ -0,0 +1,260 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn + * Copyright (C) 2009 Alban Browaeys + * Copyright (C) 2009 Felix Fietkau + * Copyright (C) 2009 Luis Correia + * Copyright (C) 2009 Mattias Nissler + * Copyright (C) 2009 Mark Asselstine + * Copyright (C) 2009 Xose Vazquez Perez + * Copyright (C) 2009 Bart Zolnierkiewicz + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* Module: rt2800soc + * Abstract: rt2800 WiSoC specific routines. + */ + +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2x00soc.h" +#include "rt2800.h" +#include "rt2800lib.h" +#include "rt2800mmio.h" + +/* Allow hardware encryption to be disabled. */ +static bool modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +static bool rt2800soc_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) +{ + return modparam_nohwcrypt; +} + +static void rt2800soc_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + rt2800_disable_radio(rt2x00dev); + rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0); + rt2x00mmio_register_write(rt2x00dev, TX_PIN_CFG, 0); +} + +static int rt2800soc_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + retval = rt2800mmio_enable_radio(rt2x00dev); + break; + + case STATE_RADIO_OFF: + rt2800soc_disable_radio(rt2x00dev); + break; + + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt2800mmio_toggle_irq(rt2x00dev, state); + break; + + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + /* These states are not supported, but don't report an error */ + retval = 0; + break; + + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +static int rt2800soc_read_eeprom(struct rt2x00_dev *rt2x00dev) +{ + void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE); + + if (!base_addr) + return -ENOMEM; + + memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE); + + iounmap(base_addr); + return 0; +} + +/* Firmware functions */ +static char *rt2800soc_get_firmware_name(struct rt2x00_dev *rt2x00dev) +{ + WARN_ON_ONCE(1); + return NULL; +} + +static int rt2800soc_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static int rt2800soc_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static const struct ieee80211_ops rt2800soc_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .get_key_seq = rt2800_get_key_seq, + .set_rts_threshold = rt2800_set_rts_threshold, + .sta_add = rt2x00mac_sta_add, + .sta_remove = rt2x00mac_sta_remove, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2800_conf_tx, + .get_tsf = rt2800_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, + .ampdu_action = rt2800_ampdu_action, + .flush = rt2x00mac_flush, + .get_survey = rt2800_get_survey, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2800_ops rt2800soc_rt2800_ops = { + .register_read = rt2x00mmio_register_read, + .register_read_lock = rt2x00mmio_register_read, /* same for SoCs */ + .register_write = rt2x00mmio_register_write, + .register_write_lock = rt2x00mmio_register_write, /* same for SoCs */ + .register_multiread = rt2x00mmio_register_multiread, + .register_multiwrite = rt2x00mmio_register_multiwrite, + .regbusy_read = rt2x00mmio_regbusy_read, + .read_eeprom = rt2800soc_read_eeprom, + .hwcrypt_disabled = rt2800soc_hwcrypt_disabled, + .drv_write_firmware = rt2800soc_write_firmware, + .drv_init_registers = rt2800mmio_init_registers, + .drv_get_txwi = rt2800mmio_get_txwi, +}; + +static const struct rt2x00lib_ops rt2800soc_rt2x00_ops = { + .irq_handler = rt2800mmio_interrupt, + .txstatus_tasklet = rt2800mmio_txstatus_tasklet, + .pretbtt_tasklet = rt2800mmio_pretbtt_tasklet, + .tbtt_tasklet = rt2800mmio_tbtt_tasklet, + .rxdone_tasklet = rt2800mmio_rxdone_tasklet, + .autowake_tasklet = rt2800mmio_autowake_tasklet, + .probe_hw = rt2800_probe_hw, + .get_firmware_name = rt2800soc_get_firmware_name, + .check_firmware = rt2800soc_check_firmware, + .load_firmware = rt2800soc_load_firmware, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt2800mmio_get_entry_state, + .clear_entry = rt2800mmio_clear_entry, + .set_device_state = rt2800soc_set_device_state, + .rfkill_poll = rt2800_rfkill_poll, + .link_stats = rt2800_link_stats, + .reset_tuner = rt2800_reset_tuner, + .link_tuner = rt2800_link_tuner, + .gain_calibration = rt2800_gain_calibration, + .vco_calibration = rt2800_vco_calibration, + .start_queue = rt2800mmio_start_queue, + .kick_queue = rt2800mmio_kick_queue, + .stop_queue = rt2800mmio_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, + .write_tx_desc = rt2800mmio_write_tx_desc, + .write_tx_data = rt2800_write_tx_data, + .write_beacon = rt2800_write_beacon, + .clear_beacon = rt2800_clear_beacon, + .fill_rxdone = rt2800mmio_fill_rxdone, + .config_shared_key = rt2800_config_shared_key, + .config_pairwise_key = rt2800_config_pairwise_key, + .config_filter = rt2800_config_filter, + .config_intf = rt2800_config_intf, + .config_erp = rt2800_config_erp, + .config_ant = rt2800_config_ant, + .config = rt2800_config, + .sta_add = rt2800_sta_add, + .sta_remove = rt2800_sta_remove, +}; + +static const struct rt2x00_ops rt2800soc_ops = { + .name = KBUILD_MODNAME, + .drv_data_size = sizeof(struct rt2800_drv_data), + .max_ap_intf = 8, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2800mmio_queue_init, + .lib = &rt2800soc_rt2x00_ops, + .drv = &rt2800soc_rt2800_ops, + .hw = &rt2800soc_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2800_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +static int rt2800soc_probe(struct platform_device *pdev) +{ + return rt2x00soc_probe(pdev, &rt2800soc_ops); +} + +static struct platform_driver rt2800soc_driver = { + .driver = { + .name = "rt2800_wmac", + .mod_name = KBUILD_MODNAME, + }, + .probe = rt2800soc_probe, + .remove = rt2x00soc_remove, + .suspend = rt2x00soc_suspend, + .resume = rt2x00soc_resume, +}; + +module_platform_driver(rt2800soc_driver); + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink WiSoC Wireless LAN driver."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c new file mode 100644 index 000000000000..bf9afbf46c1b --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c @@ -0,0 +1,1462 @@ +/* + Copyright (C) 2010 Willow Garage + Copyright (C) 2009 - 2010 Ivo van Doorn + Copyright (C) 2009 Mattias Nissler + Copyright (C) 2009 Felix Fietkau + Copyright (C) 2009 Xose Vazquez Perez + Copyright (C) 2009 Axel Kollhofer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2800usb + Abstract: rt2800usb device specific routines. + Supported chipsets: RT2800U. + */ + +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00usb.h" +#include "rt2800lib.h" +#include "rt2800.h" +#include "rt2800usb.h" + +/* + * Allow hardware encryption to be disabled. + */ +static bool modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +static bool rt2800usb_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) +{ + return modparam_nohwcrypt; +} + +/* + * Queue handlers. + */ +static void rt2800usb_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); + rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); + rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); + break; + default: + break; + } +} + +static void rt2800usb_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); + rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); + break; + default: + break; + } +} + +/* + * test if there is an entry in any TX queue for which DMA is done + * but the TX status has not been returned yet + */ +static bool rt2800usb_txstatus_pending(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + tx_queue_for_each(rt2x00dev, queue) { + if (rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE) != + rt2x00queue_get_entry(queue, Q_INDEX_DONE)) + return true; + } + return false; +} + +static inline bool rt2800usb_entry_txstatus_timeout(struct queue_entry *entry) +{ + bool tout; + + if (!test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + return false; + + tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(100)); + if (unlikely(tout)) + rt2x00_dbg(entry->queue->rt2x00dev, + "TX status timeout for entry %d in queue %d\n", + entry->entry_idx, entry->queue->qid); + return tout; + +} + +static bool rt2800usb_txstatus_timeout(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + struct queue_entry *entry; + + tx_queue_for_each(rt2x00dev, queue) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + if (rt2800usb_entry_txstatus_timeout(entry)) + return true; + } + return false; +} + +#define TXSTATUS_READ_INTERVAL 1000000 + +static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, + int urb_status, u32 tx_status) +{ + bool valid; + + if (urb_status) { + rt2x00_warn(rt2x00dev, "TX status read failed %d\n", + urb_status); + + goto stop_reading; + } + + valid = rt2x00_get_field32(tx_status, TX_STA_FIFO_VALID); + if (valid) { + if (!kfifo_put(&rt2x00dev->txstatus_fifo, tx_status)) + rt2x00_warn(rt2x00dev, "TX status FIFO overrun\n"); + + queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); + + /* Reschedule urb to read TX status again instantly */ + return true; + } + + /* Check if there is any entry that timedout waiting on TX status */ + if (rt2800usb_txstatus_timeout(rt2x00dev)) + queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); + + if (rt2800usb_txstatus_pending(rt2x00dev)) { + /* Read register after 1 ms */ + hrtimer_start(&rt2x00dev->txstatus_timer, + ktime_set(0, TXSTATUS_READ_INTERVAL), + HRTIMER_MODE_REL); + return false; + } + +stop_reading: + clear_bit(TX_STATUS_READING, &rt2x00dev->flags); + /* + * There is small race window above, between txstatus pending check and + * clear_bit someone could do rt2x00usb_interrupt_txdone, so recheck + * here again if status reading is needed. + */ + if (rt2800usb_txstatus_pending(rt2x00dev) && + !test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) + return true; + else + return false; +} + +static void rt2800usb_async_read_tx_status(struct rt2x00_dev *rt2x00dev) +{ + + if (test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) + return; + + /* Read TX_STA_FIFO register after 2 ms */ + hrtimer_start(&rt2x00dev->txstatus_timer, + ktime_set(0, 2*TXSTATUS_READ_INTERVAL), + HRTIMER_MODE_REL); +} + +static void rt2800usb_tx_dma_done(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + + rt2800usb_async_read_tx_status(rt2x00dev); +} + +static enum hrtimer_restart rt2800usb_tx_sta_fifo_timeout(struct hrtimer *timer) +{ + struct rt2x00_dev *rt2x00dev = + container_of(timer, struct rt2x00_dev, txstatus_timer); + + rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO, + rt2800usb_tx_sta_fifo_read_completed); + + return HRTIMER_NORESTART; +} + +/* + * Firmware functions + */ +static int rt2800usb_autorun_detect(struct rt2x00_dev *rt2x00dev) +{ + __le32 *reg; + u32 fw_mode; + int ret; + + reg = kmalloc(sizeof(*reg), GFP_KERNEL); + if (reg == NULL) + return -ENOMEM; + /* cannot use rt2x00usb_register_read here as it uses different + * mode (MULTI_READ vs. DEVICE_MODE) and does not pass the + * magic value USB_MODE_AUTORUN (0x11) to the device, thus the + * returned value would be invalid. + */ + ret = rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE, + USB_VENDOR_REQUEST_IN, 0, + USB_MODE_AUTORUN, reg, sizeof(*reg), + REGISTER_TIMEOUT_FIRMWARE); + fw_mode = le32_to_cpu(*reg); + kfree(reg); + if (ret < 0) + return ret; + + if ((fw_mode & 0x00000003) == 2) + return 1; + + return 0; +} + +static char *rt2800usb_get_firmware_name(struct rt2x00_dev *rt2x00dev) +{ + return FIRMWARE_RT2870; +} + +static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + int status; + u32 offset; + u32 length; + int retval; + + /* + * Check which section of the firmware we need. + */ + if (rt2x00_rt(rt2x00dev, RT2860) || + rt2x00_rt(rt2x00dev, RT2872) || + rt2x00_rt(rt2x00dev, RT3070)) { + offset = 0; + length = 4096; + } else { + offset = 4096; + length = 4096; + } + + /* + * Write firmware to device. + */ + retval = rt2800usb_autorun_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) { + rt2x00_info(rt2x00dev, + "Firmware loading not required - NIC in AutoRun mode\n"); + __clear_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); + } else { + rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, + data + offset, length); + } + + rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); + rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); + + /* + * Send firmware request to device to load firmware, + * we need to specify a long timeout time. + */ + status = rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, + 0, USB_MODE_FIRMWARE, + REGISTER_TIMEOUT_FIRMWARE); + if (status < 0) { + rt2x00_err(rt2x00dev, "Failed to write Firmware to device\n"); + return status; + } + + msleep(10); + rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + + return 0; +} + +/* + * Device state switch handlers. + */ +static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Wait until BBP and RF are ready. + */ + if (rt2800_wait_csr_ready(rt2x00dev)) + return -EBUSY; + + rt2x00usb_register_read(rt2x00dev, PBF_SYS_CTRL, ®); + rt2x00usb_register_write(rt2x00dev, PBF_SYS_CTRL, reg & ~0x00002000); + + reg = 0; + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); + rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, 0x00000000); + + rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, + USB_MODE_RESET, REGISTER_TIMEOUT); + + rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); + + return 0; +} + +static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev))) + return -EIO; + + rt2x00usb_register_read(rt2x00dev, USB_DMA_CFG, ®); + rt2x00_set_field32(®, USB_DMA_CFG_PHY_CLEAR, 0); + rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN, 0); + rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_TIMEOUT, 128); + /* + * Total room for RX frames in kilobytes, PBF might still exceed + * this limit so reduce the number to prevent errors. + */ + rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_LIMIT, + ((rt2x00dev->rx->limit * DATA_FRAME_SIZE) + / 1024) - 3); + rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_EN, 1); + rt2x00_set_field32(®, USB_DMA_CFG_TX_BULK_EN, 1); + rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, reg); + + return rt2800_enable_radio(rt2x00dev); +} + +static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + rt2800_disable_radio(rt2x00dev); +} + +static int rt2800usb_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + if (state == STATE_AWAKE) + rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 2); + else + rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0xff, 2); + + return 0; +} + +static int rt2800usb_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + /* + * Before the radio can be enabled, the device first has + * to be woken up. After that it needs a bit of time + * to be fully awake and then the radio can be enabled. + */ + rt2800usb_set_state(rt2x00dev, STATE_AWAKE); + msleep(1); + retval = rt2800usb_enable_radio(rt2x00dev); + break; + case STATE_RADIO_OFF: + /* + * After the radio has been disabled, the device should + * be put to sleep for powersaving. + */ + rt2800usb_disable_radio(rt2x00dev); + rt2800usb_set_state(rt2x00dev, STATE_SLEEP); + break; + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + /* No support, but no error either */ + break; + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + retval = rt2800usb_set_state(rt2x00dev, state); + break; + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +/* + * Watchdog handlers + */ +static void rt2800usb_watchdog(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u32 reg; + + rt2x00usb_register_read(rt2x00dev, TXRXQ_PCNT, ®); + if (rt2x00_get_field32(reg, TXRXQ_PCNT_TX0Q)) { + rt2x00_warn(rt2x00dev, "TX HW queue 0 timed out, invoke forced kick\n"); + + rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40012); + + for (i = 0; i < 10; i++) { + udelay(10); + if (!rt2x00_get_field32(reg, TXRXQ_PCNT_TX0Q)) + break; + } + + rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40006); + } + + rt2x00usb_register_read(rt2x00dev, TXRXQ_PCNT, ®); + if (rt2x00_get_field32(reg, TXRXQ_PCNT_TX1Q)) { + rt2x00_warn(rt2x00dev, "TX HW queue 1 timed out, invoke forced kick\n"); + + rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf4000a); + + for (i = 0; i < 10; i++) { + udelay(10); + if (!rt2x00_get_field32(reg, TXRXQ_PCNT_TX1Q)) + break; + } + + rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40006); + } + + rt2x00usb_watchdog(rt2x00dev); +} + +/* + * TX descriptor initialization + */ +static __le32 *rt2800usb_get_txwi(struct queue_entry *entry) +{ + if (entry->queue->qid == QID_BEACON) + return (__le32 *) (entry->skb->data); + else + return (__le32 *) (entry->skb->data + TXINFO_DESC_SIZE); +} + +static void rt2800usb_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + __le32 *txi = (__le32 *) entry->skb->data; + u32 word; + + /* + * Initialize TXINFO descriptor + */ + rt2x00_desc_read(txi, 0, &word); + + /* + * The size of TXINFO_W0_USB_DMA_TX_PKT_LEN is + * TXWI + 802.11 header + L2 pad + payload + pad, + * so need to decrease size of TXINFO. + */ + rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_TX_PKT_LEN, + roundup(entry->skb->len, 4) - TXINFO_DESC_SIZE); + rt2x00_set_field32(&word, TXINFO_W0_WIV, + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); + rt2x00_set_field32(&word, TXINFO_W0_QSEL, 2); + rt2x00_set_field32(&word, TXINFO_W0_SW_USE_LAST_ROUND, 0); + rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_NEXT_VALID, 0); + rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_TX_BURST, + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_desc_write(txi, 0, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->flags |= SKBDESC_DESC_IN_SKB; + skbdesc->desc = txi; + skbdesc->desc_len = TXINFO_DESC_SIZE + entry->queue->winfo_size; +} + +/* + * TX data initialization + */ +static int rt2800usb_get_tx_data_len(struct queue_entry *entry) +{ + /* + * pad(1~3 bytes) is needed after each 802.11 payload. + * USB end pad(4 bytes) is needed at each USB bulk out packet end. + * TX frame format is : + * | TXINFO | TXWI | 802.11 header | L2 pad | payload | pad | USB end pad | + * |<------------- tx_pkt_len ------------->| + */ + + return roundup(entry->skb->len, 4) + 4; +} + +/* + * TX control handlers + */ +static enum txdone_entry_desc_flags +rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg) +{ + __le32 *txwi; + u32 word; + int wcid, ack, pid; + int tx_wcid, tx_ack, tx_pid, is_agg; + + /* + * This frames has returned with an IO error, + * so the status report is not intended for this + * frame. + */ + if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) + return TXDONE_FAILURE; + + wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID); + ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED); + pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE); + is_agg = rt2x00_get_field32(reg, TX_STA_FIFO_TX_AGGRE); + + /* + * Validate if this TX status report is intended for + * this entry by comparing the WCID/ACK/PID fields. + */ + txwi = rt2800usb_get_txwi(entry); + + rt2x00_desc_read(txwi, 1, &word); + tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); + tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK); + tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID); + + if (wcid != tx_wcid || ack != tx_ack || (!is_agg && pid != tx_pid)) { + rt2x00_dbg(entry->queue->rt2x00dev, + "TX status report missed for queue %d entry %d\n", + entry->queue->qid, entry->entry_idx); + return TXDONE_UNKNOWN; + } + + return TXDONE_SUCCESS; +} + +static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + struct queue_entry *entry; + u32 reg; + u8 qid; + enum txdone_entry_desc_flags done_status; + + while (kfifo_get(&rt2x00dev->txstatus_fifo, ®)) { + /* + * TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus qid is + * guaranteed to be one of the TX QIDs . + */ + qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE); + queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); + + if (unlikely(rt2x00queue_empty(queue))) { + rt2x00_dbg(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", + qid); + break; + } + + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + + if (unlikely(test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))) { + rt2x00_warn(rt2x00dev, "Data pending for entry %u in queue %u\n", + entry->entry_idx, qid); + break; + } + + done_status = rt2800usb_txdone_entry_check(entry, reg); + if (likely(done_status == TXDONE_SUCCESS)) + rt2800_txdone_entry(entry, reg, rt2800usb_get_txwi(entry)); + else + rt2x00lib_txdone_noinfo(entry, done_status); + } +} + +static void rt2800usb_txdone_nostatus(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + struct queue_entry *entry; + + /* + * Process any trailing TX status reports for IO failures, + * we loop until we find the first non-IO error entry. This + * can either be a frame which is free, is being uploaded, + * or has completed the upload but didn't have an entry + * in the TX_STAT_FIFO register yet. + */ + tx_queue_for_each(rt2x00dev, queue) { + while (!rt2x00queue_empty(queue)) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + + if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + break; + + if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) + rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE); + else if (rt2800usb_entry_txstatus_timeout(entry)) + rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN); + else + break; + } + } +} + +static void rt2800usb_work_txdone(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, txdone_work); + + while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo) || + rt2800usb_txstatus_timeout(rt2x00dev)) { + + rt2800usb_txdone(rt2x00dev); + + rt2800usb_txdone_nostatus(rt2x00dev); + + /* + * The hw may delay sending the packet after DMA complete + * if the medium is busy, thus the TX_STA_FIFO entry is + * also delayed -> use a timer to retrieve it. + */ + if (rt2800usb_txstatus_pending(rt2x00dev)) + rt2800usb_async_read_tx_status(rt2x00dev); + } +} + +/* + * RX control handlers + */ +static void rt2800usb_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + __le32 *rxi = (__le32 *)entry->skb->data; + __le32 *rxd; + u32 word; + int rx_pkt_len; + + /* + * Copy descriptor to the skbdesc->desc buffer, making it safe from + * moving of frame data in rt2x00usb. + */ + memcpy(skbdesc->desc, rxi, skbdesc->desc_len); + + /* + * RX frame format is : + * | RXINFO | RXWI | header | L2 pad | payload | pad | RXD | USB pad | + * |<------------ rx_pkt_len -------------->| + */ + rt2x00_desc_read(rxi, 0, &word); + rx_pkt_len = rt2x00_get_field32(word, RXINFO_W0_USB_DMA_RX_PKT_LEN); + + /* + * Remove the RXINFO structure from the sbk. + */ + skb_pull(entry->skb, RXINFO_DESC_SIZE); + + /* + * Check for rx_pkt_len validity. Return if invalid, leaving + * rxdesc->size zeroed out by the upper level. + */ + if (unlikely(rx_pkt_len == 0 || + rx_pkt_len > entry->queue->data_size)) { + rt2x00_err(entry->queue->rt2x00dev, + "Bad frame size %d, forcing to 0\n", rx_pkt_len); + return; + } + + rxd = (__le32 *)(entry->skb->data + rx_pkt_len); + + /* + * It is now safe to read the descriptor on all architectures. + */ + rt2x00_desc_read(rxd, 0, &word); + + if (rt2x00_get_field32(word, RXD_W0_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + + rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W0_CIPHER_ERROR); + + if (rt2x00_get_field32(word, RXD_W0_DECRYPTED)) { + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. Unfortunately the descriptor doesn't contain + * any fields with the EIV/IV data either, so they can't + * be restored by rt2x00lib. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * The hardware has already checked the Michael Mic and has + * stripped it from the frame. Signal this to mac80211. + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + + if (rt2x00_get_field32(word, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; + + if (rt2x00_get_field32(word, RXD_W0_L2PAD)) + rxdesc->dev_flags |= RXDONE_L2PAD; + + /* + * Remove RXD descriptor from end of buffer. + */ + skb_trim(entry->skb, rx_pkt_len); + + /* + * Process the RXWI structure. + */ + rt2800_process_rxwi(entry, rxdesc); +} + +/* + * Device probe functions. + */ +static int rt2800usb_efuse_detect(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + retval = rt2800usb_autorun_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) + return 1; + return rt2800_efuse_detect(rt2x00dev); +} + +static int rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + retval = rt2800usb_efuse_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) + retval = rt2800_read_eeprom_efuse(rt2x00dev); + else + retval = rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, + EEPROM_SIZE); + + return retval; +} + +static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + retval = rt2800_probe_hw(rt2x00dev); + if (retval) + return retval; + + /* + * Set txstatus timer function. + */ + rt2x00dev->txstatus_timer.function = rt2800usb_tx_sta_fifo_timeout; + + /* + * Overwrite TX done handler + */ + INIT_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone); + + return 0; +} + +static const struct ieee80211_ops rt2800usb_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .get_key_seq = rt2800_get_key_seq, + .set_rts_threshold = rt2800_set_rts_threshold, + .sta_add = rt2x00mac_sta_add, + .sta_remove = rt2x00mac_sta_remove, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2800_conf_tx, + .get_tsf = rt2800_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, + .ampdu_action = rt2800_ampdu_action, + .flush = rt2x00mac_flush, + .get_survey = rt2800_get_survey, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2800_ops rt2800usb_rt2800_ops = { + .register_read = rt2x00usb_register_read, + .register_read_lock = rt2x00usb_register_read_lock, + .register_write = rt2x00usb_register_write, + .register_write_lock = rt2x00usb_register_write_lock, + .register_multiread = rt2x00usb_register_multiread, + .register_multiwrite = rt2x00usb_register_multiwrite, + .regbusy_read = rt2x00usb_regbusy_read, + .read_eeprom = rt2800usb_read_eeprom, + .hwcrypt_disabled = rt2800usb_hwcrypt_disabled, + .drv_write_firmware = rt2800usb_write_firmware, + .drv_init_registers = rt2800usb_init_registers, + .drv_get_txwi = rt2800usb_get_txwi, +}; + +static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { + .probe_hw = rt2800usb_probe_hw, + .get_firmware_name = rt2800usb_get_firmware_name, + .check_firmware = rt2800_check_firmware, + .load_firmware = rt2800_load_firmware, + .initialize = rt2x00usb_initialize, + .uninitialize = rt2x00usb_uninitialize, + .clear_entry = rt2x00usb_clear_entry, + .set_device_state = rt2800usb_set_device_state, + .rfkill_poll = rt2800_rfkill_poll, + .link_stats = rt2800_link_stats, + .reset_tuner = rt2800_reset_tuner, + .link_tuner = rt2800_link_tuner, + .gain_calibration = rt2800_gain_calibration, + .vco_calibration = rt2800_vco_calibration, + .watchdog = rt2800usb_watchdog, + .start_queue = rt2800usb_start_queue, + .kick_queue = rt2x00usb_kick_queue, + .stop_queue = rt2800usb_stop_queue, + .flush_queue = rt2x00usb_flush_queue, + .tx_dma_done = rt2800usb_tx_dma_done, + .write_tx_desc = rt2800usb_write_tx_desc, + .write_tx_data = rt2800_write_tx_data, + .write_beacon = rt2800_write_beacon, + .clear_beacon = rt2800_clear_beacon, + .get_tx_data_len = rt2800usb_get_tx_data_len, + .fill_rxdone = rt2800usb_fill_rxdone, + .config_shared_key = rt2800_config_shared_key, + .config_pairwise_key = rt2800_config_pairwise_key, + .config_filter = rt2800_config_filter, + .config_intf = rt2800_config_intf, + .config_erp = rt2800_config_erp, + .config_ant = rt2800_config_ant, + .config = rt2800_config, + .sta_add = rt2800_sta_add, + .sta_remove = rt2800_sta_remove, +}; + +static void rt2800usb_queue_init(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + unsigned short txwi_size, rxwi_size; + + rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); + + switch (queue->qid) { + case QID_RX: + queue->limit = 128; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = RXINFO_DESC_SIZE; + queue->winfo_size = rxwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 16; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = TXINFO_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_BEACON: + queue->limit = 8; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXINFO_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} + +static const struct rt2x00_ops rt2800usb_ops = { + .name = KBUILD_MODNAME, + .drv_data_size = sizeof(struct rt2800_drv_data), + .max_ap_intf = 8, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2800usb_queue_init, + .lib = &rt2800usb_rt2x00_ops, + .drv = &rt2800usb_rt2800_ops, + .hw = &rt2800usb_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2800_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +/* + * rt2800usb module information. + */ +static struct usb_device_id rt2800usb_device_table[] = { + /* Abocom */ + { USB_DEVICE(0x07b8, 0x2870) }, + { USB_DEVICE(0x07b8, 0x2770) }, + { USB_DEVICE(0x07b8, 0x3070) }, + { USB_DEVICE(0x07b8, 0x3071) }, + { USB_DEVICE(0x07b8, 0x3072) }, + { USB_DEVICE(0x1482, 0x3c09) }, + /* AirTies */ + { USB_DEVICE(0x1eda, 0x2012) }, + { USB_DEVICE(0x1eda, 0x2210) }, + { USB_DEVICE(0x1eda, 0x2310) }, + /* Allwin */ + { USB_DEVICE(0x8516, 0x2070) }, + { USB_DEVICE(0x8516, 0x2770) }, + { USB_DEVICE(0x8516, 0x2870) }, + { USB_DEVICE(0x8516, 0x3070) }, + { USB_DEVICE(0x8516, 0x3071) }, + { USB_DEVICE(0x8516, 0x3072) }, + /* Alpha Networks */ + { USB_DEVICE(0x14b2, 0x3c06) }, + { USB_DEVICE(0x14b2, 0x3c07) }, + { USB_DEVICE(0x14b2, 0x3c09) }, + { USB_DEVICE(0x14b2, 0x3c12) }, + { USB_DEVICE(0x14b2, 0x3c23) }, + { USB_DEVICE(0x14b2, 0x3c25) }, + { USB_DEVICE(0x14b2, 0x3c27) }, + { USB_DEVICE(0x14b2, 0x3c28) }, + { USB_DEVICE(0x14b2, 0x3c2c) }, + /* Amit */ + { USB_DEVICE(0x15c5, 0x0008) }, + /* Askey */ + { USB_DEVICE(0x1690, 0x0740) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x1731) }, + { USB_DEVICE(0x0b05, 0x1732) }, + { USB_DEVICE(0x0b05, 0x1742) }, + { USB_DEVICE(0x0b05, 0x1784) }, + { USB_DEVICE(0x1761, 0x0b05) }, + /* AzureWave */ + { USB_DEVICE(0x13d3, 0x3247) }, + { USB_DEVICE(0x13d3, 0x3273) }, + { USB_DEVICE(0x13d3, 0x3305) }, + { USB_DEVICE(0x13d3, 0x3307) }, + { USB_DEVICE(0x13d3, 0x3321) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x8053) }, + { USB_DEVICE(0x050d, 0x805c) }, + { USB_DEVICE(0x050d, 0x815c) }, + { USB_DEVICE(0x050d, 0x825a) }, + { USB_DEVICE(0x050d, 0x825b) }, + { USB_DEVICE(0x050d, 0x935a) }, + { USB_DEVICE(0x050d, 0x935b) }, + /* Buffalo */ + { USB_DEVICE(0x0411, 0x00e8) }, + { USB_DEVICE(0x0411, 0x0158) }, + { USB_DEVICE(0x0411, 0x015d) }, + { USB_DEVICE(0x0411, 0x016f) }, + { USB_DEVICE(0x0411, 0x01a2) }, + { USB_DEVICE(0x0411, 0x01ee) }, + { USB_DEVICE(0x0411, 0x01a8) }, + /* Corega */ + { USB_DEVICE(0x07aa, 0x002f) }, + { USB_DEVICE(0x07aa, 0x003c) }, + { USB_DEVICE(0x07aa, 0x003f) }, + { USB_DEVICE(0x18c5, 0x0012) }, + /* D-Link */ + { USB_DEVICE(0x07d1, 0x3c09) }, + { USB_DEVICE(0x07d1, 0x3c0a) }, + { USB_DEVICE(0x07d1, 0x3c0d) }, + { USB_DEVICE(0x07d1, 0x3c0e) }, + { USB_DEVICE(0x07d1, 0x3c0f) }, + { USB_DEVICE(0x07d1, 0x3c11) }, + { USB_DEVICE(0x07d1, 0x3c13) }, + { USB_DEVICE(0x07d1, 0x3c15) }, + { USB_DEVICE(0x07d1, 0x3c16) }, + { USB_DEVICE(0x07d1, 0x3c17) }, + { USB_DEVICE(0x2001, 0x3317) }, + { USB_DEVICE(0x2001, 0x3c1b) }, + { USB_DEVICE(0x2001, 0x3c25) }, + /* Draytek */ + { USB_DEVICE(0x07fa, 0x7712) }, + /* DVICO */ + { USB_DEVICE(0x0fe9, 0xb307) }, + /* Edimax */ + { USB_DEVICE(0x7392, 0x4085) }, + { USB_DEVICE(0x7392, 0x7711) }, + { USB_DEVICE(0x7392, 0x7717) }, + { USB_DEVICE(0x7392, 0x7718) }, + { USB_DEVICE(0x7392, 0x7722) }, + /* Encore */ + { USB_DEVICE(0x203d, 0x1480) }, + { USB_DEVICE(0x203d, 0x14a9) }, + /* EnGenius */ + { USB_DEVICE(0x1740, 0x9701) }, + { USB_DEVICE(0x1740, 0x9702) }, + { USB_DEVICE(0x1740, 0x9703) }, + { USB_DEVICE(0x1740, 0x9705) }, + { USB_DEVICE(0x1740, 0x9706) }, + { USB_DEVICE(0x1740, 0x9707) }, + { USB_DEVICE(0x1740, 0x9708) }, + { USB_DEVICE(0x1740, 0x9709) }, + /* Gemtek */ + { USB_DEVICE(0x15a9, 0x0012) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x800b) }, + { USB_DEVICE(0x1044, 0x800d) }, + /* Hawking */ + { USB_DEVICE(0x0e66, 0x0001) }, + { USB_DEVICE(0x0e66, 0x0003) }, + { USB_DEVICE(0x0e66, 0x0009) }, + { USB_DEVICE(0x0e66, 0x000b) }, + { USB_DEVICE(0x0e66, 0x0013) }, + { USB_DEVICE(0x0e66, 0x0017) }, + { USB_DEVICE(0x0e66, 0x0018) }, + /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x0945) }, + { USB_DEVICE(0x04bb, 0x0947) }, + { USB_DEVICE(0x04bb, 0x0948) }, + /* Linksys */ + { USB_DEVICE(0x13b1, 0x0031) }, + { USB_DEVICE(0x1737, 0x0070) }, + { USB_DEVICE(0x1737, 0x0071) }, + { USB_DEVICE(0x1737, 0x0077) }, + { USB_DEVICE(0x1737, 0x0078) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x0162) }, + { USB_DEVICE(0x0789, 0x0163) }, + { USB_DEVICE(0x0789, 0x0164) }, + { USB_DEVICE(0x0789, 0x0166) }, + /* Motorola */ + { USB_DEVICE(0x100d, 0x9031) }, + /* MSI */ + { USB_DEVICE(0x0db0, 0x3820) }, + { USB_DEVICE(0x0db0, 0x3821) }, + { USB_DEVICE(0x0db0, 0x3822) }, + { USB_DEVICE(0x0db0, 0x3870) }, + { USB_DEVICE(0x0db0, 0x3871) }, + { USB_DEVICE(0x0db0, 0x6899) }, + { USB_DEVICE(0x0db0, 0x821a) }, + { USB_DEVICE(0x0db0, 0x822a) }, + { USB_DEVICE(0x0db0, 0x822b) }, + { USB_DEVICE(0x0db0, 0x822c) }, + { USB_DEVICE(0x0db0, 0x870a) }, + { USB_DEVICE(0x0db0, 0x871a) }, + { USB_DEVICE(0x0db0, 0x871b) }, + { USB_DEVICE(0x0db0, 0x871c) }, + { USB_DEVICE(0x0db0, 0x899a) }, + /* Ovislink */ + { USB_DEVICE(0x1b75, 0x3070) }, + { USB_DEVICE(0x1b75, 0x3071) }, + { USB_DEVICE(0x1b75, 0x3072) }, + { USB_DEVICE(0x1b75, 0xa200) }, + /* Para */ + { USB_DEVICE(0x20b8, 0x8888) }, + /* Pegatron */ + { USB_DEVICE(0x1d4d, 0x0002) }, + { USB_DEVICE(0x1d4d, 0x000c) }, + { USB_DEVICE(0x1d4d, 0x000e) }, + { USB_DEVICE(0x1d4d, 0x0011) }, + /* Philips */ + { USB_DEVICE(0x0471, 0x200f) }, + /* Planex */ + { USB_DEVICE(0x2019, 0x5201) }, + { USB_DEVICE(0x2019, 0xab25) }, + { USB_DEVICE(0x2019, 0xed06) }, + /* Quanta */ + { USB_DEVICE(0x1a32, 0x0304) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x2070) }, + { USB_DEVICE(0x148f, 0x2770) }, + { USB_DEVICE(0x148f, 0x2870) }, + { USB_DEVICE(0x148f, 0x3070) }, + { USB_DEVICE(0x148f, 0x3071) }, + { USB_DEVICE(0x148f, 0x3072) }, + /* Samsung */ + { USB_DEVICE(0x04e8, 0x2018) }, + /* Siemens */ + { USB_DEVICE(0x129b, 0x1828) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0017) }, + { USB_DEVICE(0x0df6, 0x002b) }, + { USB_DEVICE(0x0df6, 0x002c) }, + { USB_DEVICE(0x0df6, 0x002d) }, + { USB_DEVICE(0x0df6, 0x0039) }, + { USB_DEVICE(0x0df6, 0x003b) }, + { USB_DEVICE(0x0df6, 0x003d) }, + { USB_DEVICE(0x0df6, 0x003e) }, + { USB_DEVICE(0x0df6, 0x003f) }, + { USB_DEVICE(0x0df6, 0x0040) }, + { USB_DEVICE(0x0df6, 0x0042) }, + { USB_DEVICE(0x0df6, 0x0047) }, + { USB_DEVICE(0x0df6, 0x0048) }, + { USB_DEVICE(0x0df6, 0x0051) }, + { USB_DEVICE(0x0df6, 0x005f) }, + { USB_DEVICE(0x0df6, 0x0060) }, + /* SMC */ + { USB_DEVICE(0x083a, 0x6618) }, + { USB_DEVICE(0x083a, 0x7511) }, + { USB_DEVICE(0x083a, 0x7512) }, + { USB_DEVICE(0x083a, 0x7522) }, + { USB_DEVICE(0x083a, 0x8522) }, + { USB_DEVICE(0x083a, 0xa618) }, + { USB_DEVICE(0x083a, 0xa701) }, + { USB_DEVICE(0x083a, 0xa702) }, + { USB_DEVICE(0x083a, 0xa703) }, + { USB_DEVICE(0x083a, 0xb522) }, + /* Sparklan */ + { USB_DEVICE(0x15a9, 0x0006) }, + /* Sweex */ + { USB_DEVICE(0x177f, 0x0153) }, + { USB_DEVICE(0x177f, 0x0164) }, + { USB_DEVICE(0x177f, 0x0302) }, + { USB_DEVICE(0x177f, 0x0313) }, + { USB_DEVICE(0x177f, 0x0323) }, + { USB_DEVICE(0x177f, 0x0324) }, + /* U-Media */ + { USB_DEVICE(0x157e, 0x300e) }, + { USB_DEVICE(0x157e, 0x3013) }, + /* ZCOM */ + { USB_DEVICE(0x0cde, 0x0022) }, + { USB_DEVICE(0x0cde, 0x0025) }, + /* Zinwell */ + { USB_DEVICE(0x5a57, 0x0280) }, + { USB_DEVICE(0x5a57, 0x0282) }, + { USB_DEVICE(0x5a57, 0x0283) }, + { USB_DEVICE(0x5a57, 0x5257) }, + /* Zyxel */ + { USB_DEVICE(0x0586, 0x3416) }, + { USB_DEVICE(0x0586, 0x3418) }, + { USB_DEVICE(0x0586, 0x341a) }, + { USB_DEVICE(0x0586, 0x341e) }, + { USB_DEVICE(0x0586, 0x343e) }, +#ifdef CONFIG_RT2800USB_RT33XX + /* Belkin */ + { USB_DEVICE(0x050d, 0x945b) }, + /* D-Link */ + { USB_DEVICE(0x2001, 0x3c17) }, + /* Panasonic */ + { USB_DEVICE(0x083a, 0xb511) }, + /* Philips */ + { USB_DEVICE(0x0471, 0x20dd) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x3370) }, + { USB_DEVICE(0x148f, 0x8070) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0050) }, + /* Sweex */ + { USB_DEVICE(0x177f, 0x0163) }, + { USB_DEVICE(0x177f, 0x0165) }, +#endif +#ifdef CONFIG_RT2800USB_RT35XX + /* Allwin */ + { USB_DEVICE(0x8516, 0x3572) }, + /* Askey */ + { USB_DEVICE(0x1690, 0x0744) }, + { USB_DEVICE(0x1690, 0x0761) }, + { USB_DEVICE(0x1690, 0x0764) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x179d) }, + /* Cisco */ + { USB_DEVICE(0x167b, 0x4001) }, + /* EnGenius */ + { USB_DEVICE(0x1740, 0x9801) }, + /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x0944) }, + /* Linksys */ + { USB_DEVICE(0x13b1, 0x002f) }, + { USB_DEVICE(0x1737, 0x0079) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x0170) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x3572) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0041) }, + { USB_DEVICE(0x0df6, 0x0062) }, + { USB_DEVICE(0x0df6, 0x0065) }, + { USB_DEVICE(0x0df6, 0x0066) }, + { USB_DEVICE(0x0df6, 0x0068) }, + /* Toshiba */ + { USB_DEVICE(0x0930, 0x0a07) }, + /* Zinwell */ + { USB_DEVICE(0x5a57, 0x0284) }, +#endif +#ifdef CONFIG_RT2800USB_RT3573 + /* AirLive */ + { USB_DEVICE(0x1b75, 0x7733) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x17bc) }, + { USB_DEVICE(0x0b05, 0x17ad) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x1103) }, + /* Cameo */ + { USB_DEVICE(0x148f, 0xf301) }, + /* D-Link */ + { USB_DEVICE(0x2001, 0x3c1f) }, + /* Edimax */ + { USB_DEVICE(0x7392, 0x7733) }, + /* Hawking */ + { USB_DEVICE(0x0e66, 0x0020) }, + { USB_DEVICE(0x0e66, 0x0021) }, + /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x094e) }, + /* Linksys */ + { USB_DEVICE(0x13b1, 0x003b) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x016b) }, + /* NETGEAR */ + { USB_DEVICE(0x0846, 0x9012) }, + { USB_DEVICE(0x0846, 0x9013) }, + { USB_DEVICE(0x0846, 0x9019) }, + /* Planex */ + { USB_DEVICE(0x2019, 0xed19) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x3573) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0067) }, + { USB_DEVICE(0x0df6, 0x006a) }, + { USB_DEVICE(0x0df6, 0x006e) }, + /* ZyXEL */ + { USB_DEVICE(0x0586, 0x3421) }, +#endif +#ifdef CONFIG_RT2800USB_RT53XX + /* Arcadyan */ + { USB_DEVICE(0x043e, 0x7a12) }, + { USB_DEVICE(0x043e, 0x7a32) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x17e8) }, + /* Azurewave */ + { USB_DEVICE(0x13d3, 0x3329) }, + { USB_DEVICE(0x13d3, 0x3365) }, + /* D-Link */ + { USB_DEVICE(0x2001, 0x3c15) }, + { USB_DEVICE(0x2001, 0x3c19) }, + { USB_DEVICE(0x2001, 0x3c1c) }, + { USB_DEVICE(0x2001, 0x3c1d) }, + { USB_DEVICE(0x2001, 0x3c1e) }, + { USB_DEVICE(0x2001, 0x3c20) }, + { USB_DEVICE(0x2001, 0x3c22) }, + { USB_DEVICE(0x2001, 0x3c23) }, + /* LG innotek */ + { USB_DEVICE(0x043e, 0x7a22) }, + { USB_DEVICE(0x043e, 0x7a42) }, + /* Panasonic */ + { USB_DEVICE(0x04da, 0x1801) }, + { USB_DEVICE(0x04da, 0x1800) }, + { USB_DEVICE(0x04da, 0x23f6) }, + /* Philips */ + { USB_DEVICE(0x0471, 0x2104) }, + { USB_DEVICE(0x0471, 0x2126) }, + { USB_DEVICE(0x0471, 0x2180) }, + { USB_DEVICE(0x0471, 0x2181) }, + { USB_DEVICE(0x0471, 0x2182) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x5370) }, + { USB_DEVICE(0x148f, 0x5372) }, +#endif +#ifdef CONFIG_RT2800USB_RT55XX + /* Arcadyan */ + { USB_DEVICE(0x043e, 0x7a32) }, + /* AVM GmbH */ + { USB_DEVICE(0x057c, 0x8501) }, + /* Buffalo */ + { USB_DEVICE(0x0411, 0x0241) }, + { USB_DEVICE(0x0411, 0x0253) }, + /* D-Link */ + { USB_DEVICE(0x2001, 0x3c1a) }, + { USB_DEVICE(0x2001, 0x3c21) }, + /* Proware */ + { USB_DEVICE(0x043e, 0x7a13) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x5572) }, + /* TRENDnet */ + { USB_DEVICE(0x20f4, 0x724a) }, +#endif +#ifdef CONFIG_RT2800USB_UNKNOWN + /* + * Unclear what kind of devices these are (they aren't supported by the + * vendor linux driver). + */ + /* Abocom */ + { USB_DEVICE(0x07b8, 0x3073) }, + { USB_DEVICE(0x07b8, 0x3074) }, + /* Alpha Networks */ + { USB_DEVICE(0x14b2, 0x3c08) }, + { USB_DEVICE(0x14b2, 0x3c11) }, + /* Amigo */ + { USB_DEVICE(0x0e0b, 0x9031) }, + { USB_DEVICE(0x0e0b, 0x9041) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x166a) }, + { USB_DEVICE(0x0b05, 0x1760) }, + { USB_DEVICE(0x0b05, 0x1761) }, + { USB_DEVICE(0x0b05, 0x1790) }, + { USB_DEVICE(0x0b05, 0x17a7) }, + /* AzureWave */ + { USB_DEVICE(0x13d3, 0x3262) }, + { USB_DEVICE(0x13d3, 0x3284) }, + { USB_DEVICE(0x13d3, 0x3322) }, + { USB_DEVICE(0x13d3, 0x3340) }, + { USB_DEVICE(0x13d3, 0x3399) }, + { USB_DEVICE(0x13d3, 0x3400) }, + { USB_DEVICE(0x13d3, 0x3401) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x1003) }, + /* Buffalo */ + { USB_DEVICE(0x0411, 0x012e) }, + { USB_DEVICE(0x0411, 0x0148) }, + { USB_DEVICE(0x0411, 0x0150) }, + /* Corega */ + { USB_DEVICE(0x07aa, 0x0041) }, + { USB_DEVICE(0x07aa, 0x0042) }, + { USB_DEVICE(0x18c5, 0x0008) }, + /* D-Link */ + { USB_DEVICE(0x07d1, 0x3c0b) }, + /* Encore */ + { USB_DEVICE(0x203d, 0x14a1) }, + /* EnGenius */ + { USB_DEVICE(0x1740, 0x0600) }, + { USB_DEVICE(0x1740, 0x0602) }, + /* Gemtek */ + { USB_DEVICE(0x15a9, 0x0010) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x800c) }, + /* Hercules */ + { USB_DEVICE(0x06f8, 0xe036) }, + /* Huawei */ + { USB_DEVICE(0x148f, 0xf101) }, + /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x094b) }, + /* LevelOne */ + { USB_DEVICE(0x1740, 0x0605) }, + { USB_DEVICE(0x1740, 0x0615) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x0168) }, + { USB_DEVICE(0x0789, 0x0169) }, + /* Motorola */ + { USB_DEVICE(0x100d, 0x9032) }, + /* Pegatron */ + { USB_DEVICE(0x05a6, 0x0101) }, + { USB_DEVICE(0x1d4d, 0x0010) }, + /* Planex */ + { USB_DEVICE(0x2019, 0xab24) }, + { USB_DEVICE(0x2019, 0xab29) }, + /* Qcom */ + { USB_DEVICE(0x18e8, 0x6259) }, + /* RadioShack */ + { USB_DEVICE(0x08b9, 0x1197) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x003c) }, + { USB_DEVICE(0x0df6, 0x004a) }, + { USB_DEVICE(0x0df6, 0x004d) }, + { USB_DEVICE(0x0df6, 0x0053) }, + { USB_DEVICE(0x0df6, 0x0069) }, + { USB_DEVICE(0x0df6, 0x006f) }, + { USB_DEVICE(0x0df6, 0x0078) }, + /* SMC */ + { USB_DEVICE(0x083a, 0xa512) }, + { USB_DEVICE(0x083a, 0xc522) }, + { USB_DEVICE(0x083a, 0xd522) }, + { USB_DEVICE(0x083a, 0xf511) }, + /* Sweex */ + { USB_DEVICE(0x177f, 0x0254) }, + /* TP-LINK */ + { USB_DEVICE(0xf201, 0x5370) }, +#endif + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2800 USB Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2870 USB chipset based cards"); +MODULE_DEVICE_TABLE(usb, rt2800usb_device_table); +MODULE_FIRMWARE(FIRMWARE_RT2870); +MODULE_LICENSE("GPL"); + +static int rt2800usb_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + return rt2x00usb_probe(usb_intf, &rt2800usb_ops); +} + +static struct usb_driver rt2800usb_driver = { + .name = KBUILD_MODNAME, + .id_table = rt2800usb_device_table, + .probe = rt2800usb_probe, + .disconnect = rt2x00usb_disconnect, + .suspend = rt2x00usb_suspend, + .resume = rt2x00usb_resume, + .reset_resume = rt2x00usb_resume, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(rt2800usb_driver); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.h b/drivers/net/wireless/ralink/rt2x00/rt2800usb.h new file mode 100644 index 000000000000..ea7cac095997 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.h @@ -0,0 +1,110 @@ +/* + Copyright (C) 2009 Ivo van Doorn + Copyright (C) 2009 Mattias Nissler + Copyright (C) 2009 Felix Fietkau + Copyright (C) 2009 Xose Vazquez Perez + Copyright (C) 2009 Axel Kollhofer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2800usb + Abstract: Data structures and registers for the rt2800usb module. + Supported chipsets: RT2800U. + */ + +#ifndef RT2800USB_H +#define RT2800USB_H + +/* + * 8051 firmware image. + */ +#define FIRMWARE_RT2870 "rt2870.bin" +#define FIRMWARE_IMAGE_BASE 0x3000 + +/* + * DMA descriptor defines. + */ +#define TXINFO_DESC_SIZE (1 * sizeof(__le32)) +#define RXINFO_DESC_SIZE (1 * sizeof(__le32)) + +/* + * TX Info structure + */ + +/* + * Word0 + * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI + * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. + * 0:MGMT, 1:HCCA 2:EDCA + * USB_DMA_NEXT_VALID: Used ONLY in USB bulk Aggregation, NextValid + * DMA_TX_BURST: used ONLY in USB bulk Aggregation. + * Force USB DMA transmit frame from current selected endpoint + */ +#define TXINFO_W0_USB_DMA_TX_PKT_LEN FIELD32(0x0000ffff) +#define TXINFO_W0_WIV FIELD32(0x01000000) +#define TXINFO_W0_QSEL FIELD32(0x06000000) +#define TXINFO_W0_SW_USE_LAST_ROUND FIELD32(0x08000000) +#define TXINFO_W0_USB_DMA_NEXT_VALID FIELD32(0x40000000) +#define TXINFO_W0_USB_DMA_TX_BURST FIELD32(0x80000000) + +/* + * RX Info structure + */ + +/* + * Word 0 + */ + +#define RXINFO_W0_USB_DMA_RX_PKT_LEN FIELD32(0x0000ffff) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + * UNICAST_TO_ME: This RX frame is unicast to me. + * MULTICAST: This is a multicast frame. + * BROADCAST: This is a broadcast frame. + * MY_BSS: this frame belongs to the same BSSID. + * CRC_ERROR: CRC error. + * CIPHER_ERROR: 0: decryption okay, 1:ICV error, 2:MIC error, 3:KEY not valid. + * AMSDU: rx with 802.3 header, not 802.11 header. + */ + +#define RXD_W0_BA FIELD32(0x00000001) +#define RXD_W0_DATA FIELD32(0x00000002) +#define RXD_W0_NULLDATA FIELD32(0x00000004) +#define RXD_W0_FRAG FIELD32(0x00000008) +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000010) +#define RXD_W0_MULTICAST FIELD32(0x00000020) +#define RXD_W0_BROADCAST FIELD32(0x00000040) +#define RXD_W0_MY_BSS FIELD32(0x00000080) +#define RXD_W0_CRC_ERROR FIELD32(0x00000100) +#define RXD_W0_CIPHER_ERROR FIELD32(0x00000600) +#define RXD_W0_AMSDU FIELD32(0x00000800) +#define RXD_W0_HTC FIELD32(0x00001000) +#define RXD_W0_RSSI FIELD32(0x00002000) +#define RXD_W0_L2PAD FIELD32(0x00004000) +#define RXD_W0_AMPDU FIELD32(0x00008000) +#define RXD_W0_DECRYPTED FIELD32(0x00010000) +#define RXD_W0_PLCP_RSSI FIELD32(0x00020000) +#define RXD_W0_CIPHER_ALG FIELD32(0x00040000) +#define RXD_W0_LAST_AMSDU FIELD32(0x00080000) +#define RXD_W0_PLCP_SIGNAL FIELD32(0xfff00000) + +#endif /* RT2800USB_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h new file mode 100644 index 000000000000..3282ddb766f4 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -0,0 +1,1478 @@ +/* + Copyright (C) 2010 Willow Garage + Copyright (C) 2004 - 2010 Ivo van Doorn + Copyright (C) 2004 - 2009 Gertjan van Wingerde + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00 + Abstract: rt2x00 global information. + */ + +#ifndef RT2X00_H +#define RT2X00_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rt2x00debug.h" +#include "rt2x00dump.h" +#include "rt2x00leds.h" +#include "rt2x00reg.h" +#include "rt2x00queue.h" + +/* + * Module information. + */ +#define DRV_VERSION "2.3.0" +#define DRV_PROJECT "http://rt2x00.serialmonkey.com" + +/* Debug definitions. + * Debug output has to be enabled during compile time. + */ +#ifdef CONFIG_RT2X00_DEBUG +#define DEBUG +#endif /* CONFIG_RT2X00_DEBUG */ + +/* Utility printing macros + * rt2x00_probe_err is for messages when rt2x00_dev is uninitialized + */ +#define rt2x00_probe_err(fmt, ...) \ + printk(KERN_ERR KBUILD_MODNAME ": %s: Error - " fmt, \ + __func__, ##__VA_ARGS__) +#define rt2x00_err(dev, fmt, ...) \ + wiphy_err((dev)->hw->wiphy, "%s: Error - " fmt, \ + __func__, ##__VA_ARGS__) +#define rt2x00_warn(dev, fmt, ...) \ + wiphy_warn((dev)->hw->wiphy, "%s: Warning - " fmt, \ + __func__, ##__VA_ARGS__) +#define rt2x00_info(dev, fmt, ...) \ + wiphy_info((dev)->hw->wiphy, "%s: Info - " fmt, \ + __func__, ##__VA_ARGS__) + +/* Various debug levels */ +#define rt2x00_dbg(dev, fmt, ...) \ + wiphy_dbg((dev)->hw->wiphy, "%s: Debug - " fmt, \ + __func__, ##__VA_ARGS__) +#define rt2x00_eeprom_dbg(dev, fmt, ...) \ + wiphy_dbg((dev)->hw->wiphy, "%s: EEPROM recovery - " fmt, \ + __func__, ##__VA_ARGS__) + +/* + * Duration calculations + * The rate variable passed is: 100kbs. + * To convert from bytes to bits we multiply size with 8, + * then the size is multiplied with 10 to make the + * real rate -> rate argument correction. + */ +#define GET_DURATION(__size, __rate) (((__size) * 8 * 10) / (__rate)) +#define GET_DURATION_RES(__size, __rate)(((__size) * 8 * 10) % (__rate)) + +/* + * Determine the number of L2 padding bytes required between the header and + * the payload. + */ +#define L2PAD_SIZE(__hdrlen) (-(__hdrlen) & 3) + +/* + * Determine the alignment requirement, + * to make sure the 802.11 payload is padded to a 4-byte boundrary + * we must determine the address of the payload and calculate the + * amount of bytes needed to move the data. + */ +#define ALIGN_SIZE(__skb, __header) \ + ( ((unsigned long)((__skb)->data + (__header))) & 3 ) + +/* + * Constants for extra TX headroom for alignment purposes. + */ +#define RT2X00_ALIGN_SIZE 4 /* Only whole frame needs alignment */ +#define RT2X00_L2PAD_SIZE 8 /* Both header & payload need alignment */ + +/* + * Standard timing and size defines. + * These values should follow the ieee80211 specifications. + */ +#define ACK_SIZE 14 +#define IEEE80211_HEADER 24 +#define PLCP 48 +#define BEACON 100 +#define PREAMBLE 144 +#define SHORT_PREAMBLE 72 +#define SLOT_TIME 20 +#define SHORT_SLOT_TIME 9 +#define SIFS 10 +#define PIFS ( SIFS + SLOT_TIME ) +#define SHORT_PIFS ( SIFS + SHORT_SLOT_TIME ) +#define DIFS ( PIFS + SLOT_TIME ) +#define SHORT_DIFS ( SHORT_PIFS + SHORT_SLOT_TIME ) +#define EIFS ( SIFS + DIFS + \ + GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) +#define SHORT_EIFS ( SIFS + SHORT_DIFS + \ + GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) + +enum rt2x00_chip_intf { + RT2X00_CHIP_INTF_PCI, + RT2X00_CHIP_INTF_PCIE, + RT2X00_CHIP_INTF_USB, + RT2X00_CHIP_INTF_SOC, +}; + +/* + * Chipset identification + * The chipset on the device is composed of a RT and RF chip. + * The chipset combination is important for determining device capabilities. + */ +struct rt2x00_chip { + u16 rt; +#define RT2460 0x2460 +#define RT2560 0x2560 +#define RT2570 0x2570 +#define RT2661 0x2661 +#define RT2573 0x2573 +#define RT2860 0x2860 /* 2.4GHz */ +#define RT2872 0x2872 /* WSOC */ +#define RT2883 0x2883 /* WSOC */ +#define RT3070 0x3070 +#define RT3071 0x3071 +#define RT3090 0x3090 /* 2.4GHz PCIe */ +#define RT3290 0x3290 +#define RT3352 0x3352 /* WSOC */ +#define RT3390 0x3390 +#define RT3572 0x3572 +#define RT3593 0x3593 +#define RT3883 0x3883 /* WSOC */ +#define RT5390 0x5390 /* 2.4GHz */ +#define RT5392 0x5392 /* 2.4GHz */ +#define RT5592 0x5592 + + u16 rf; + u16 rev; + + enum rt2x00_chip_intf intf; +}; + +/* + * RF register values that belong to a particular channel. + */ +struct rf_channel { + int channel; + u32 rf1; + u32 rf2; + u32 rf3; + u32 rf4; +}; + +/* + * Channel information structure + */ +struct channel_info { + unsigned int flags; +#define GEOGRAPHY_ALLOWED 0x00000001 + + short max_power; + short default_power1; + short default_power2; + short default_power3; +}; + +/* + * Antenna setup values. + */ +struct antenna_setup { + enum antenna rx; + enum antenna tx; + u8 rx_chain_num; + u8 tx_chain_num; +}; + +/* + * Quality statistics about the currently active link. + */ +struct link_qual { + /* + * Statistics required for Link tuning by driver + * The rssi value is provided by rt2x00lib during the + * link_tuner() callback function. + * The false_cca field is filled during the link_stats() + * callback function and could be used during the + * link_tuner() callback function. + */ + int rssi; + int false_cca; + + /* + * VGC levels + * Hardware driver will tune the VGC level during each call + * to the link_tuner() callback function. This vgc_level is + * is determined based on the link quality statistics like + * average RSSI and the false CCA count. + * + * In some cases the drivers need to differentiate between + * the currently "desired" VGC level and the level configured + * in the hardware. The latter is important to reduce the + * number of BBP register reads to reduce register access + * overhead. For this reason we store both values here. + */ + u8 vgc_level; + u8 vgc_level_reg; + + /* + * Statistics required for Signal quality calculation. + * These fields might be changed during the link_stats() + * callback function. + */ + int rx_success; + int rx_failed; + int tx_success; + int tx_failed; +}; + +DECLARE_EWMA(rssi, 1024, 8) + +/* + * Antenna settings about the currently active link. + */ +struct link_ant { + /* + * Antenna flags + */ + unsigned int flags; +#define ANTENNA_RX_DIVERSITY 0x00000001 +#define ANTENNA_TX_DIVERSITY 0x00000002 +#define ANTENNA_MODE_SAMPLE 0x00000004 + + /* + * Currently active TX/RX antenna setup. + * When software diversity is used, this will indicate + * which antenna is actually used at this time. + */ + struct antenna_setup active; + + /* + * RSSI history information for the antenna. + * Used to determine when to switch antenna + * when using software diversity. + */ + int rssi_history; + + /* + * Current RSSI average of the currently active antenna. + * Similar to the avg_rssi in the link_qual structure + * this value is updated by using the walking average. + */ + struct ewma_rssi rssi_ant; +}; + +/* + * To optimize the quality of the link we need to store + * the quality of received frames and periodically + * optimize the link. + */ +struct link { + /* + * Link tuner counter + * The number of times the link has been tuned + * since the radio has been switched on. + */ + u32 count; + + /* + * Quality measurement values. + */ + struct link_qual qual; + + /* + * TX/RX antenna setup. + */ + struct link_ant ant; + + /* + * Currently active average RSSI value + */ + struct ewma_rssi avg_rssi; + + /* + * Work structure for scheduling periodic link tuning. + */ + struct delayed_work work; + + /* + * Work structure for scheduling periodic watchdog monitoring. + * This work must be scheduled on the kernel workqueue, while + * all other work structures must be queued on the mac80211 + * workqueue. This guarantees that the watchdog can schedule + * other work structures and wait for their completion in order + * to bring the device/driver back into the desired state. + */ + struct delayed_work watchdog_work; + + /* + * Work structure for scheduling periodic AGC adjustments. + */ + struct delayed_work agc_work; + + /* + * Work structure for scheduling periodic VCO calibration. + */ + struct delayed_work vco_work; +}; + +enum rt2x00_delayed_flags { + DELAYED_UPDATE_BEACON, +}; + +/* + * Interface structure + * Per interface configuration details, this structure + * is allocated as the private data for ieee80211_vif. + */ +struct rt2x00_intf { + /* + * beacon->skb must be protected with the mutex. + */ + struct mutex beacon_skb_mutex; + + /* + * Entry in the beacon queue which belongs to + * this interface. Each interface has its own + * dedicated beacon entry. + */ + struct queue_entry *beacon; + bool enable_beacon; + + /* + * Actions that needed rescheduling. + */ + unsigned long delayed_flags; + + /* + * Software sequence counter, this is only required + * for hardware which doesn't support hardware + * sequence counting. + */ + atomic_t seqno; +}; + +static inline struct rt2x00_intf* vif_to_intf(struct ieee80211_vif *vif) +{ + return (struct rt2x00_intf *)vif->drv_priv; +} + +/** + * struct hw_mode_spec: Hardware specifications structure + * + * Details about the supported modes, rates and channels + * of a particular chipset. This is used by rt2x00lib + * to build the ieee80211_hw_mode array for mac80211. + * + * @supported_bands: Bitmask contained the supported bands (2.4GHz, 5.2GHz). + * @supported_rates: Rate types which are supported (CCK, OFDM). + * @num_channels: Number of supported channels. This is used as array size + * for @tx_power_a, @tx_power_bg and @channels. + * @channels: Device/chipset specific channel values (See &struct rf_channel). + * @channels_info: Additional information for channels (See &struct channel_info). + * @ht: Driver HT Capabilities (See &ieee80211_sta_ht_cap). + */ +struct hw_mode_spec { + unsigned int supported_bands; +#define SUPPORT_BAND_2GHZ 0x00000001 +#define SUPPORT_BAND_5GHZ 0x00000002 + + unsigned int supported_rates; +#define SUPPORT_RATE_CCK 0x00000001 +#define SUPPORT_RATE_OFDM 0x00000002 + + unsigned int num_channels; + const struct rf_channel *channels; + const struct channel_info *channels_info; + + struct ieee80211_sta_ht_cap ht; +}; + +/* + * Configuration structure wrapper around the + * mac80211 configuration structure. + * When mac80211 configures the driver, rt2x00lib + * can precalculate values which are equal for all + * rt2x00 drivers. Those values can be stored in here. + */ +struct rt2x00lib_conf { + struct ieee80211_conf *conf; + + struct rf_channel rf; + struct channel_info channel; +}; + +/* + * Configuration structure for erp settings. + */ +struct rt2x00lib_erp { + int short_preamble; + int cts_protection; + + u32 basic_rates; + + int slot_time; + + short sifs; + short pifs; + short difs; + short eifs; + + u16 beacon_int; + u16 ht_opmode; +}; + +/* + * Configuration structure for hardware encryption. + */ +struct rt2x00lib_crypto { + enum cipher cipher; + + enum set_key_cmd cmd; + const u8 *address; + + u32 bssidx; + + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; + + int wcid; +}; + +/* + * Configuration structure wrapper around the + * rt2x00 interface configuration handler. + */ +struct rt2x00intf_conf { + /* + * Interface type + */ + enum nl80211_iftype type; + + /* + * TSF sync value, this is dependent on the operation type. + */ + enum tsf_sync sync; + + /* + * The MAC and BSSID addresses are simple array of bytes, + * these arrays are little endian, so when sending the addresses + * to the drivers, copy the it into a endian-signed variable. + * + * Note that all devices (except rt2500usb) have 32 bits + * register word sizes. This means that whatever variable we + * pass _must_ be a multiple of 32 bits. Otherwise the device + * might not accept what we are sending to it. + * This will also make it easier for the driver to write + * the data to the device. + */ + __le32 mac[2]; + __le32 bssid[2]; +}; + +/* + * Private structure for storing STA details + * wcid: Wireless Client ID + */ +struct rt2x00_sta { + int wcid; +}; + +static inline struct rt2x00_sta* sta_to_rt2x00_sta(struct ieee80211_sta *sta) +{ + return (struct rt2x00_sta *)sta->drv_priv; +} + +/* + * rt2x00lib callback functions. + */ +struct rt2x00lib_ops { + /* + * Interrupt handlers. + */ + irq_handler_t irq_handler; + + /* + * TX status tasklet handler. + */ + void (*txstatus_tasklet) (unsigned long data); + void (*pretbtt_tasklet) (unsigned long data); + void (*tbtt_tasklet) (unsigned long data); + void (*rxdone_tasklet) (unsigned long data); + void (*autowake_tasklet) (unsigned long data); + + /* + * Device init handlers. + */ + int (*probe_hw) (struct rt2x00_dev *rt2x00dev); + char *(*get_firmware_name) (struct rt2x00_dev *rt2x00dev); + int (*check_firmware) (struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len); + int (*load_firmware) (struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len); + + /* + * Device initialization/deinitialization handlers. + */ + int (*initialize) (struct rt2x00_dev *rt2x00dev); + void (*uninitialize) (struct rt2x00_dev *rt2x00dev); + + /* + * queue initialization handlers + */ + bool (*get_entry_state) (struct queue_entry *entry); + void (*clear_entry) (struct queue_entry *entry); + + /* + * Radio control handlers. + */ + int (*set_device_state) (struct rt2x00_dev *rt2x00dev, + enum dev_state state); + int (*rfkill_poll) (struct rt2x00_dev *rt2x00dev); + void (*link_stats) (struct rt2x00_dev *rt2x00dev, + struct link_qual *qual); + void (*reset_tuner) (struct rt2x00_dev *rt2x00dev, + struct link_qual *qual); + void (*link_tuner) (struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, const u32 count); + void (*gain_calibration) (struct rt2x00_dev *rt2x00dev); + void (*vco_calibration) (struct rt2x00_dev *rt2x00dev); + + /* + * Data queue handlers. + */ + void (*watchdog) (struct rt2x00_dev *rt2x00dev); + void (*start_queue) (struct data_queue *queue); + void (*kick_queue) (struct data_queue *queue); + void (*stop_queue) (struct data_queue *queue); + void (*flush_queue) (struct data_queue *queue, bool drop); + void (*tx_dma_done) (struct queue_entry *entry); + + /* + * TX control handlers + */ + void (*write_tx_desc) (struct queue_entry *entry, + struct txentry_desc *txdesc); + void (*write_tx_data) (struct queue_entry *entry, + struct txentry_desc *txdesc); + void (*write_beacon) (struct queue_entry *entry, + struct txentry_desc *txdesc); + void (*clear_beacon) (struct queue_entry *entry); + int (*get_tx_data_len) (struct queue_entry *entry); + + /* + * RX control handlers + */ + void (*fill_rxdone) (struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc); + + /* + * Configuration handlers. + */ + int (*config_shared_key) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); + int (*config_pairwise_key) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); + void (*config_filter) (struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags); + void (*config_intf) (struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags); +#define CONFIG_UPDATE_TYPE ( 1 << 1 ) +#define CONFIG_UPDATE_MAC ( 1 << 2 ) +#define CONFIG_UPDATE_BSSID ( 1 << 3 ) + + void (*config_erp) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed); + void (*config_ant) (struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant); + void (*config) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int changed_flags); + int (*sta_add) (struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + int (*sta_remove) (struct rt2x00_dev *rt2x00dev, + int wcid); +}; + +/* + * rt2x00 driver callback operation structure. + */ +struct rt2x00_ops { + const char *name; + const unsigned int drv_data_size; + const unsigned int max_ap_intf; + const unsigned int eeprom_size; + const unsigned int rf_size; + const unsigned int tx_queues; + void (*queue_init)(struct data_queue *queue); + const struct rt2x00lib_ops *lib; + const void *drv; + const struct ieee80211_ops *hw; +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + const struct rt2x00debug *debugfs; +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +/* + * rt2x00 state flags + */ +enum rt2x00_state_flags { + /* + * Device flags + */ + DEVICE_STATE_PRESENT, + DEVICE_STATE_REGISTERED_HW, + DEVICE_STATE_INITIALIZED, + DEVICE_STATE_STARTED, + DEVICE_STATE_ENABLED_RADIO, + DEVICE_STATE_SCANNING, + + /* + * Driver configuration + */ + CONFIG_CHANNEL_HT40, + CONFIG_POWERSAVING, + CONFIG_HT_DISABLED, + CONFIG_QOS_DISABLED, + + /* + * Mark we currently are sequentially reading TX_STA_FIFO register + * FIXME: this is for only rt2800usb, should go to private data + */ + TX_STATUS_READING, +}; + +/* + * rt2x00 capability flags + */ +enum rt2x00_capability_flags { + /* + * Requirements + */ + REQUIRE_FIRMWARE, + REQUIRE_BEACON_GUARD, + REQUIRE_ATIM_QUEUE, + REQUIRE_DMA, + REQUIRE_COPY_IV, + REQUIRE_L2PAD, + REQUIRE_TXSTATUS_FIFO, + REQUIRE_TASKLET_CONTEXT, + REQUIRE_SW_SEQNO, + REQUIRE_HT_TX_DESC, + REQUIRE_PS_AUTOWAKE, + REQUIRE_DELAYED_RFKILL, + + /* + * Capabilities + */ + CAPABILITY_HW_BUTTON, + CAPABILITY_HW_CRYPTO, + CAPABILITY_POWER_LIMIT, + CAPABILITY_CONTROL_FILTERS, + CAPABILITY_CONTROL_FILTER_PSPOLL, + CAPABILITY_PRE_TBTT_INTERRUPT, + CAPABILITY_LINK_TUNING, + CAPABILITY_FRAME_TYPE, + CAPABILITY_RF_SEQUENCE, + CAPABILITY_EXTERNAL_LNA_A, + CAPABILITY_EXTERNAL_LNA_BG, + CAPABILITY_DOUBLE_ANTENNA, + CAPABILITY_BT_COEXIST, + CAPABILITY_VCO_RECALIBRATION, +}; + +/* + * Interface combinations + */ +enum { + IF_COMB_AP = 0, + NUM_IF_COMB, +}; + +/* + * rt2x00 device structure. + */ +struct rt2x00_dev { + /* + * Device structure. + * The structure stored in here depends on the + * system bus (PCI or USB). + * When accessing this variable, the rt2x00dev_{pci,usb} + * macros should be used for correct typecasting. + */ + struct device *dev; + + /* + * Callback functions. + */ + const struct rt2x00_ops *ops; + + /* + * Driver data. + */ + void *drv_data; + + /* + * IEEE80211 control structure. + */ + struct ieee80211_hw *hw; + struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; + enum ieee80211_band curr_band; + int curr_freq; + + /* + * If enabled, the debugfs interface structures + * required for deregistration of debugfs. + */ +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + struct rt2x00debug_intf *debugfs_intf; +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ + + /* + * LED structure for changing the LED status + * by mac8011 or the kernel. + */ +#ifdef CONFIG_RT2X00_LIB_LEDS + struct rt2x00_led led_radio; + struct rt2x00_led led_assoc; + struct rt2x00_led led_qual; + u16 led_mcu_reg; +#endif /* CONFIG_RT2X00_LIB_LEDS */ + + /* + * Device state flags. + * In these flags the current status is stored. + * Access to these flags should occur atomically. + */ + unsigned long flags; + + /* + * Device capabiltiy flags. + * In these flags the device/driver capabilities are stored. + * Access to these flags should occur non-atomically. + */ + unsigned long cap_flags; + + /* + * Device information, Bus IRQ and name (PCI, SoC) + */ + int irq; + const char *name; + + /* + * Chipset identification. + */ + struct rt2x00_chip chip; + + /* + * hw capability specifications. + */ + struct hw_mode_spec spec; + + /* + * This is the default TX/RX antenna setup as indicated + * by the device's EEPROM. + */ + struct antenna_setup default_ant; + + /* + * Register pointers + * csr.base: CSR base register address. (PCI) + * csr.cache: CSR cache for usb_control_msg. (USB) + */ + union csr { + void __iomem *base; + void *cache; + } csr; + + /* + * Mutex to protect register accesses. + * For PCI and USB devices it protects against concurrent indirect + * register access (BBP, RF, MCU) since accessing those + * registers require multiple calls to the CSR registers. + * For USB devices it also protects the csr_cache since that + * field is used for normal CSR access and it cannot support + * multiple callers simultaneously. + */ + struct mutex csr_mutex; + + /* + * Current packet filter configuration for the device. + * This contains all currently active FIF_* flags send + * to us by mac80211 during configure_filter(). + */ + unsigned int packet_filter; + + /* + * Interface details: + * - Open ap interface count. + * - Open sta interface count. + * - Association count. + * - Beaconing enabled count. + */ + unsigned int intf_ap_count; + unsigned int intf_sta_count; + unsigned int intf_associated; + unsigned int intf_beaconing; + + /* + * Interface combinations + */ + struct ieee80211_iface_limit if_limits_ap; + struct ieee80211_iface_combination if_combinations[NUM_IF_COMB]; + + /* + * Link quality + */ + struct link link; + + /* + * EEPROM data. + */ + __le16 *eeprom; + + /* + * Active RF register values. + * These are stored here so we don't need + * to read the rf registers and can directly + * use this value instead. + * This field should be accessed by using + * rt2x00_rf_read() and rt2x00_rf_write(). + */ + u32 *rf; + + /* + * LNA gain + */ + short lna_gain; + + /* + * Current TX power value. + */ + u16 tx_power; + + /* + * Current retry values. + */ + u8 short_retry; + u8 long_retry; + + /* + * Rssi <-> Dbm offset + */ + u8 rssi_offset; + + /* + * Frequency offset. + */ + u8 freq_offset; + + /* + * Association id. + */ + u16 aid; + + /* + * Beacon interval. + */ + u16 beacon_int; + + /** + * Timestamp of last received beacon + */ + unsigned long last_beacon; + + /* + * Low level statistics which will have + * to be kept up to date while device is running. + */ + struct ieee80211_low_level_stats low_level_stats; + + /** + * Work queue for all work which should not be placed + * on the mac80211 workqueue (because of dependencies + * between various work structures). + */ + struct workqueue_struct *workqueue; + + /* + * Scheduled work. + * NOTE: intf_work will use ieee80211_iterate_active_interfaces() + * which means it cannot be placed on the hw->workqueue + * due to RTNL locking requirements. + */ + struct work_struct intf_work; + + /** + * Scheduled work for TX/RX done handling (USB devices) + */ + struct work_struct rxdone_work; + struct work_struct txdone_work; + + /* + * Powersaving work + */ + struct delayed_work autowakeup_work; + struct work_struct sleep_work; + + /* + * Data queue arrays for RX, TX, Beacon and ATIM. + */ + unsigned int data_queues; + struct data_queue *rx; + struct data_queue *tx; + struct data_queue *bcn; + struct data_queue *atim; + + /* + * Firmware image. + */ + const struct firmware *fw; + + /* + * FIFO for storing tx status reports between isr and tasklet. + */ + DECLARE_KFIFO_PTR(txstatus_fifo, u32); + + /* + * Timer to ensure tx status reports are read (rt2800usb). + */ + struct hrtimer txstatus_timer; + + /* + * Tasklet for processing tx status reports (rt2800pci). + */ + struct tasklet_struct txstatus_tasklet; + struct tasklet_struct pretbtt_tasklet; + struct tasklet_struct tbtt_tasklet; + struct tasklet_struct rxdone_tasklet; + struct tasklet_struct autowake_tasklet; + + /* + * Used for VCO periodic calibration. + */ + int rf_channel; + + /* + * Protect the interrupt mask register. + */ + spinlock_t irqmask_lock; + + /* + * List of BlockAckReq TX entries that need driver BlockAck processing. + */ + struct list_head bar_list; + spinlock_t bar_list_lock; + + /* Extra TX headroom required for alignment purposes. */ + unsigned int extra_tx_headroom; +}; + +struct rt2x00_bar_list_entry { + struct list_head list; + struct rcu_head head; + + struct queue_entry *entry; + int block_acked; + + /* Relevant parts of the IEEE80211 BAR header */ + __u8 ra[6]; + __u8 ta[6]; + __le16 control; + __le16 start_seq_num; +}; + +/* + * Register defines. + * Some registers require multiple attempts before success, + * in those cases REGISTER_BUSY_COUNT attempts should be + * taken with a REGISTER_BUSY_DELAY interval. Due to USB + * bus delays, we do not have to loop so many times to wait + * for valid register value on that bus. + */ +#define REGISTER_BUSY_COUNT 100 +#define REGISTER_USB_BUSY_COUNT 20 +#define REGISTER_BUSY_DELAY 100 + +/* + * Generic RF access. + * The RF is being accessed by word index. + */ +static inline void rt2x00_rf_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u32 *data) +{ + BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32)); + *data = rt2x00dev->rf[word - 1]; +} + +static inline void rt2x00_rf_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u32 data) +{ + BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32)); + rt2x00dev->rf[word - 1] = data; +} + +/* + * Generic EEPROM access. The EEPROM is being accessed by word or byte index. + */ +static inline void *rt2x00_eeprom_addr(struct rt2x00_dev *rt2x00dev, + const unsigned int word) +{ + return (void *)&rt2x00dev->eeprom[word]; +} + +static inline void rt2x00_eeprom_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u16 *data) +{ + *data = le16_to_cpu(rt2x00dev->eeprom[word]); +} + +static inline void rt2x00_eeprom_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u16 data) +{ + rt2x00dev->eeprom[word] = cpu_to_le16(data); +} + +static inline u8 rt2x00_eeprom_byte(struct rt2x00_dev *rt2x00dev, + const unsigned int byte) +{ + return *(((u8 *)rt2x00dev->eeprom) + byte); +} + +/* + * Chipset handlers + */ +static inline void rt2x00_set_chip(struct rt2x00_dev *rt2x00dev, + const u16 rt, const u16 rf, const u16 rev) +{ + rt2x00dev->chip.rt = rt; + rt2x00dev->chip.rf = rf; + rt2x00dev->chip.rev = rev; + + rt2x00_info(rt2x00dev, "Chipset detected - rt: %04x, rf: %04x, rev: %04x\n", + rt2x00dev->chip.rt, rt2x00dev->chip.rf, + rt2x00dev->chip.rev); +} + +static inline void rt2x00_set_rt(struct rt2x00_dev *rt2x00dev, + const u16 rt, const u16 rev) +{ + rt2x00dev->chip.rt = rt; + rt2x00dev->chip.rev = rev; + + rt2x00_info(rt2x00dev, "RT chipset %04x, rev %04x detected\n", + rt2x00dev->chip.rt, rt2x00dev->chip.rev); +} + +static inline void rt2x00_set_rf(struct rt2x00_dev *rt2x00dev, const u16 rf) +{ + rt2x00dev->chip.rf = rf; + + rt2x00_info(rt2x00dev, "RF chipset %04x detected\n", + rt2x00dev->chip.rf); +} + +static inline bool rt2x00_rt(struct rt2x00_dev *rt2x00dev, const u16 rt) +{ + return (rt2x00dev->chip.rt == rt); +} + +static inline bool rt2x00_rf(struct rt2x00_dev *rt2x00dev, const u16 rf) +{ + return (rt2x00dev->chip.rf == rf); +} + +static inline u16 rt2x00_rev(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00dev->chip.rev; +} + +static inline bool rt2x00_rt_rev(struct rt2x00_dev *rt2x00dev, + const u16 rt, const u16 rev) +{ + return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) == rev); +} + +static inline bool rt2x00_rt_rev_lt(struct rt2x00_dev *rt2x00dev, + const u16 rt, const u16 rev) +{ + return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) < rev); +} + +static inline bool rt2x00_rt_rev_gte(struct rt2x00_dev *rt2x00dev, + const u16 rt, const u16 rev) +{ + return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) >= rev); +} + +static inline void rt2x00_set_chip_intf(struct rt2x00_dev *rt2x00dev, + enum rt2x00_chip_intf intf) +{ + rt2x00dev->chip.intf = intf; +} + +static inline bool rt2x00_intf(struct rt2x00_dev *rt2x00dev, + enum rt2x00_chip_intf intf) +{ + return (rt2x00dev->chip.intf == intf); +} + +static inline bool rt2x00_is_pci(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI) || + rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); +} + +static inline bool rt2x00_is_pcie(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); +} + +static inline bool rt2x00_is_usb(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); +} + +static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); +} + +/* Helpers for capability flags */ + +static inline bool +rt2x00_has_cap_flag(struct rt2x00_dev *rt2x00dev, + enum rt2x00_capability_flags cap_flag) +{ + return test_bit(cap_flag, &rt2x00dev->cap_flags); +} + +static inline bool +rt2x00_has_cap_hw_crypto(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_HW_CRYPTO); +} + +static inline bool +rt2x00_has_cap_power_limit(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_POWER_LIMIT); +} + +static inline bool +rt2x00_has_cap_control_filters(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTERS); +} + +static inline bool +rt2x00_has_cap_control_filter_pspoll(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTER_PSPOLL); +} + +static inline bool +rt2x00_has_cap_pre_tbtt_interrupt(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_PRE_TBTT_INTERRUPT); +} + +static inline bool +rt2x00_has_cap_link_tuning(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_LINK_TUNING); +} + +static inline bool +rt2x00_has_cap_frame_type(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_FRAME_TYPE); +} + +static inline bool +rt2x00_has_cap_rf_sequence(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_RF_SEQUENCE); +} + +static inline bool +rt2x00_has_cap_external_lna_a(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_A); +} + +static inline bool +rt2x00_has_cap_external_lna_bg(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_BG); +} + +static inline bool +rt2x00_has_cap_double_antenna(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_DOUBLE_ANTENNA); +} + +static inline bool +rt2x00_has_cap_bt_coexist(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_BT_COEXIST); +} + +static inline bool +rt2x00_has_cap_vco_recalibration(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_VCO_RECALIBRATION); +} + +/** + * rt2x00queue_map_txskb - Map a skb into DMA for TX purposes. + * @entry: Pointer to &struct queue_entry + * + * Returns -ENOMEM if mapping fail, 0 otherwise. + */ +int rt2x00queue_map_txskb(struct queue_entry *entry); + +/** + * rt2x00queue_unmap_skb - Unmap a skb from DMA. + * @entry: Pointer to &struct queue_entry + */ +void rt2x00queue_unmap_skb(struct queue_entry *entry); + +/** + * rt2x00queue_get_tx_queue - Convert tx queue index to queue pointer + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @queue: rt2x00 queue index (see &enum data_queue_qid). + * + * Returns NULL for non tx queues. + */ +static inline struct data_queue * +rt2x00queue_get_tx_queue(struct rt2x00_dev *rt2x00dev, + const enum data_queue_qid queue) +{ + if (queue < rt2x00dev->ops->tx_queues && rt2x00dev->tx) + return &rt2x00dev->tx[queue]; + + if (queue == QID_ATIM) + return rt2x00dev->atim; + + return NULL; +} + +/** + * rt2x00queue_get_entry - Get queue entry where the given index points to. + * @queue: Pointer to &struct data_queue from where we obtain the entry. + * @index: Index identifier for obtaining the correct index. + */ +struct queue_entry *rt2x00queue_get_entry(struct data_queue *queue, + enum queue_index index); + +/** + * rt2x00queue_pause_queue - Pause a data queue + * @queue: Pointer to &struct data_queue. + * + * This function will pause the data queue locally, preventing + * new frames to be added to the queue (while the hardware is + * still allowed to run). + */ +void rt2x00queue_pause_queue(struct data_queue *queue); + +/** + * rt2x00queue_unpause_queue - unpause a data queue + * @queue: Pointer to &struct data_queue. + * + * This function will unpause the data queue locally, allowing + * new frames to be added to the queue again. + */ +void rt2x00queue_unpause_queue(struct data_queue *queue); + +/** + * rt2x00queue_start_queue - Start a data queue + * @queue: Pointer to &struct data_queue. + * + * This function will start handling all pending frames in the queue. + */ +void rt2x00queue_start_queue(struct data_queue *queue); + +/** + * rt2x00queue_stop_queue - Halt a data queue + * @queue: Pointer to &struct data_queue. + * + * This function will stop all pending frames in the queue. + */ +void rt2x00queue_stop_queue(struct data_queue *queue); + +/** + * rt2x00queue_flush_queue - Flush a data queue + * @queue: Pointer to &struct data_queue. + * @drop: True to drop all pending frames. + * + * This function will flush the queue. After this call + * the queue is guaranteed to be empty. + */ +void rt2x00queue_flush_queue(struct data_queue *queue, bool drop); + +/** + * rt2x00queue_start_queues - Start all data queues + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * This function will loop through all available queues to start them + */ +void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00queue_stop_queues - Halt all data queues + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * This function will loop through all available queues to stop + * any pending frames. + */ +void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00queue_flush_queues - Flush all data queues + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @drop: True to drop all pending frames. + * + * This function will loop through all available queues to flush + * any pending frames. + */ +void rt2x00queue_flush_queues(struct rt2x00_dev *rt2x00dev, bool drop); + +/* + * Debugfs handlers. + */ +/** + * rt2x00debug_dump_frame - Dump a frame to userspace through debugfs. + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @type: The type of frame that is being dumped. + * @skb: The skb containing the frame to be dumped. + */ +#ifdef CONFIG_RT2X00_LIB_DEBUGFS +void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, + enum rt2x00_dump_type type, struct sk_buff *skb); +#else +static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, + enum rt2x00_dump_type type, + struct sk_buff *skb) +{ +} +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ + +/* + * Utility functions. + */ +u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif); + +/* + * Interrupt context handlers. + */ +void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev); +void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev); +void rt2x00lib_dmastart(struct queue_entry *entry); +void rt2x00lib_dmadone(struct queue_entry *entry); +void rt2x00lib_txdone(struct queue_entry *entry, + struct txdone_entry_desc *txdesc); +void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status); +void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp); + +/* + * mac80211 handlers. + */ +void rt2x00mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb); +int rt2x00mac_start(struct ieee80211_hw *hw); +void rt2x00mac_stop(struct ieee80211_hw *hw); +int rt2x00mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +void rt2x00mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed); +void rt2x00mac_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast); +int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + bool set); +#ifdef CONFIG_RT2X00_LIB_CRYPTO +int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +#else +#define rt2x00mac_set_key NULL +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ +int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr); +void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +int rt2x00mac_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats); +void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes); +int rt2x00mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params); +void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw); +void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); +int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); +int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); +void rt2x00mac_get_ringparam(struct ieee80211_hw *hw, + u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); +bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw); + +/* + * Driver allocation handlers. + */ +int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev); +void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev); +#ifdef CONFIG_PM +int rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev, pm_message_t state); +int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev); +#endif /* CONFIG_PM */ + +#endif /* RT2X00_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00config.c b/drivers/net/wireless/ralink/rt2x00/rt2x00config.c new file mode 100644 index 000000000000..7e8bb1198ae9 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00config.c @@ -0,0 +1,285 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 generic configuration routines. + */ + +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" + +void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + enum nl80211_iftype type, + const u8 *mac, const u8 *bssid) +{ + struct rt2x00intf_conf conf; + unsigned int flags = 0; + + conf.type = type; + + switch (type) { + case NL80211_IFTYPE_ADHOC: + conf.sync = TSF_SYNC_ADHOC; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_WDS: + conf.sync = TSF_SYNC_AP_NONE; + break; + case NL80211_IFTYPE_STATION: + conf.sync = TSF_SYNC_INFRA; + break; + default: + conf.sync = TSF_SYNC_NONE; + break; + } + + /* + * Note that when NULL is passed as address we will send + * 00:00:00:00:00 to the device to clear the address. + * This will prevent the device being confused when it wants + * to ACK frames or considers itself associated. + */ + memset(conf.mac, 0, sizeof(conf.mac)); + if (mac) + memcpy(conf.mac, mac, ETH_ALEN); + + memset(conf.bssid, 0, sizeof(conf.bssid)); + if (bssid) + memcpy(conf.bssid, bssid, ETH_ALEN); + + flags |= CONFIG_UPDATE_TYPE; + if (mac || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)) + flags |= CONFIG_UPDATE_MAC; + if (bssid || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)) + flags |= CONFIG_UPDATE_BSSID; + + rt2x00dev->ops->lib->config_intf(rt2x00dev, intf, &conf, flags); +} + +void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct rt2x00lib_erp erp; + + memset(&erp, 0, sizeof(erp)); + + erp.short_preamble = bss_conf->use_short_preamble; + erp.cts_protection = bss_conf->use_cts_prot; + + erp.slot_time = bss_conf->use_short_slot ? SHORT_SLOT_TIME : SLOT_TIME; + erp.sifs = SIFS; + erp.pifs = bss_conf->use_short_slot ? SHORT_PIFS : PIFS; + erp.difs = bss_conf->use_short_slot ? SHORT_DIFS : DIFS; + erp.eifs = bss_conf->use_short_slot ? SHORT_EIFS : EIFS; + + erp.basic_rates = bss_conf->basic_rates; + erp.beacon_int = bss_conf->beacon_int; + + /* Update the AID, this is needed for dynamic PS support */ + rt2x00dev->aid = bss_conf->assoc ? bss_conf->aid : 0; + rt2x00dev->last_beacon = bss_conf->sync_tsf; + + /* Update global beacon interval time, this is needed for PS support */ + rt2x00dev->beacon_int = bss_conf->beacon_int; + + if (changed & BSS_CHANGED_HT) + erp.ht_opmode = bss_conf->ht_operation_mode; + + rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed); +} + +void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, + struct antenna_setup config) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + struct antenna_setup *def = &rt2x00dev->default_ant; + struct antenna_setup *active = &rt2x00dev->link.ant.active; + + /* + * When the caller tries to send the SW diversity, + * we must update the ANTENNA_RX_DIVERSITY flag to + * enable the antenna diversity in the link tuner. + * + * Secondly, we must guarentee we never send the + * software antenna diversity command to the driver. + */ + if (!(ant->flags & ANTENNA_RX_DIVERSITY)) { + if (config.rx == ANTENNA_SW_DIVERSITY) { + ant->flags |= ANTENNA_RX_DIVERSITY; + + if (def->rx == ANTENNA_SW_DIVERSITY) + config.rx = ANTENNA_B; + else + config.rx = def->rx; + } + } else if (config.rx == ANTENNA_SW_DIVERSITY) + config.rx = active->rx; + + if (!(ant->flags & ANTENNA_TX_DIVERSITY)) { + if (config.tx == ANTENNA_SW_DIVERSITY) { + ant->flags |= ANTENNA_TX_DIVERSITY; + + if (def->tx == ANTENNA_SW_DIVERSITY) + config.tx = ANTENNA_B; + else + config.tx = def->tx; + } + } else if (config.tx == ANTENNA_SW_DIVERSITY) + config.tx = active->tx; + + /* + * Antenna setup changes require the RX to be disabled, + * else the changes will be ignored by the device. + */ + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2x00queue_stop_queue(rt2x00dev->rx); + + /* + * Write new antenna setup to device and reset the link tuner. + * The latter is required since we need to recalibrate the + * noise-sensitivity ratio for the new setup. + */ + rt2x00dev->ops->lib->config_ant(rt2x00dev, &config); + + rt2x00link_reset_tuner(rt2x00dev, true); + + memcpy(active, &config, sizeof(config)); + + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2x00queue_start_queue(rt2x00dev->rx); +} + +static u16 rt2x00ht_center_channel(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + int center_channel; + u16 i; + + /* + * Initialize center channel to current channel. + */ + center_channel = spec->channels[conf->chandef.chan->hw_value].channel; + + /* + * Adjust center channel to HT40+ and HT40- operation. + */ + if (conf_is_ht40_plus(conf)) + center_channel += 2; + else if (conf_is_ht40_minus(conf)) + center_channel -= (center_channel == 14) ? 1 : 2; + + for (i = 0; i < spec->num_channels; i++) + if (spec->channels[i].channel == center_channel) + return i; + + WARN_ON(1); + return conf->chandef.chan->hw_value; +} + +void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + unsigned int ieee80211_flags) +{ + struct rt2x00lib_conf libconf; + u16 hw_value; + u16 autowake_timeout; + u16 beacon_int; + u16 beacon_diff; + + memset(&libconf, 0, sizeof(libconf)); + + libconf.conf = conf; + + if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) { + if (!conf_is_ht(conf)) + set_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags); + else + clear_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags); + + if (conf_is_ht40(conf)) { + set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); + hw_value = rt2x00ht_center_channel(rt2x00dev, conf); + } else { + clear_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); + hw_value = conf->chandef.chan->hw_value; + } + + memcpy(&libconf.rf, + &rt2x00dev->spec.channels[hw_value], + sizeof(libconf.rf)); + + memcpy(&libconf.channel, + &rt2x00dev->spec.channels_info[hw_value], + sizeof(libconf.channel)); + + /* Used for VCO periodic calibration */ + rt2x00dev->rf_channel = libconf.rf.channel; + } + + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) && + (ieee80211_flags & IEEE80211_CONF_CHANGE_PS)) + cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); + + /* + * Start configuration. + */ + rt2x00dev->ops->lib->config(rt2x00dev, &libconf, ieee80211_flags); + + /* + * Some configuration changes affect the link quality + * which means we need to reset the link tuner. + */ + if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) + rt2x00link_reset_tuner(rt2x00dev, false); + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) && + (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) && + (conf->flags & IEEE80211_CONF_PS)) { + beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon; + beacon_int = msecs_to_jiffies(rt2x00dev->beacon_int); + + if (beacon_diff > beacon_int) + beacon_diff = 0; + + autowake_timeout = (conf->ps_dtim_period * beacon_int) - beacon_diff; + queue_delayed_work(rt2x00dev->workqueue, + &rt2x00dev->autowakeup_work, + autowake_timeout - 15); + } + + if (conf->flags & IEEE80211_CONF_PS) + set_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); + else + clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); + + rt2x00dev->curr_band = conf->chandef.chan->band; + rt2x00dev->curr_freq = conf->chandef.chan->center_freq; + rt2x00dev->tx_power = conf->power_level; + rt2x00dev->short_retry = conf->short_frame_max_tx_count; + rt2x00dev->long_retry = conf->long_frame_max_tx_count; +} diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c b/drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c new file mode 100644 index 000000000000..a2fd05ba25ca --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c @@ -0,0 +1,256 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 crypto specific routines. + */ + +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" + +enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) +{ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return CIPHER_WEP64; + case WLAN_CIPHER_SUITE_WEP104: + return CIPHER_WEP128; + case WLAN_CIPHER_SUITE_TKIP: + return CIPHER_TKIP; + case WLAN_CIPHER_SUITE_CCMP: + return CIPHER_AES; + default: + return CIPHER_NONE; + } +} + +void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; + + if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !hw_key) + return; + + __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); + + txdesc->cipher = rt2x00crypto_key_to_cipher(hw_key); + + if (hw_key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + __set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags); + + txdesc->key_idx = hw_key->hw_key_idx; + txdesc->iv_offset = txdesc->header_length; + txdesc->iv_len = hw_key->iv_len; + + if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) + __set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags); + + if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) + __set_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags); +} + +unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_key_conf *key = tx_info->control.hw_key; + unsigned int overhead = 0; + + if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key) + return overhead; + + /* + * Extend frame length to include IV/EIV/ICV/MMIC, + * note that these lengths should only be added when + * mac80211 does not generate it. + */ + overhead += key->icv_len; + + if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) + overhead += key->iv_len; + + if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + overhead += 8; + } + + return overhead; +} + +void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); + + if (unlikely(!txdesc->iv_len)) + return; + + /* Copy IV/EIV data */ + memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); +} + +void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); + + if (unlikely(!txdesc->iv_len)) + return; + + /* Copy IV/EIV data */ + memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); + + /* Move ieee80211 header */ + memmove(skb->data + txdesc->iv_len, skb->data, txdesc->iv_offset); + + /* Pull buffer to correct size */ + skb_pull(skb, txdesc->iv_len); + txdesc->length -= txdesc->iv_len; + + /* IV/EIV data has officially been stripped */ + skbdesc->flags |= SKBDESC_IV_STRIPPED; +} + +void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); + const unsigned int iv_len = + ((!!(skbdesc->iv[0])) * 4) + ((!!(skbdesc->iv[1])) * 4); + + if (!(skbdesc->flags & SKBDESC_IV_STRIPPED)) + return; + + skb_push(skb, iv_len); + + /* Move ieee80211 header */ + memmove(skb->data, skb->data + iv_len, header_length); + + /* Copy IV/EIV data */ + memcpy(skb->data + header_length, skbdesc->iv, iv_len); + + /* IV/EIV data has returned into the frame */ + skbdesc->flags &= ~SKBDESC_IV_STRIPPED; +} + +void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, + unsigned int header_length, + struct rxdone_entry_desc *rxdesc) +{ + unsigned int payload_len = rxdesc->size - header_length; + unsigned int align = ALIGN_SIZE(skb, header_length); + unsigned int iv_len; + unsigned int icv_len; + unsigned int transfer = 0; + + /* + * WEP64/WEP128: Provides IV & ICV + * TKIP: Provides IV/EIV & ICV + * AES: Provies IV/EIV & ICV + */ + switch (rxdesc->cipher) { + case CIPHER_WEP64: + case CIPHER_WEP128: + iv_len = 4; + icv_len = 4; + break; + case CIPHER_TKIP: + iv_len = 8; + icv_len = 4; + break; + case CIPHER_AES: + iv_len = 8; + icv_len = 8; + break; + default: + /* Unsupport type */ + return; + } + + /* + * Make room for new data. There are 2 possibilities + * either the alignment is already present between + * the 802.11 header and payload. In that case we + * we have to move the header less then the iv_len + * since we can use the already available l2pad bytes + * for the iv data. + * When the alignment must be added manually we must + * move the header more then iv_len since we must + * make room for the payload move as well. + */ + if (rxdesc->dev_flags & RXDONE_L2PAD) { + skb_push(skb, iv_len - align); + skb_put(skb, icv_len); + + /* Move ieee80211 header */ + memmove(skb->data + transfer, + skb->data + transfer + (iv_len - align), + header_length); + transfer += header_length; + } else { + skb_push(skb, iv_len + align); + if (align < icv_len) + skb_put(skb, icv_len - align); + else if (align > icv_len) + skb_trim(skb, rxdesc->size + iv_len + icv_len); + + /* Move ieee80211 header */ + memmove(skb->data + transfer, + skb->data + transfer + iv_len + align, + header_length); + transfer += header_length; + } + + /* Copy IV/EIV data */ + memcpy(skb->data + transfer, rxdesc->iv, iv_len); + transfer += iv_len; + + /* + * Move payload for alignment purposes. Note that + * this is only needed when no l2 padding is present. + */ + if (!(rxdesc->dev_flags & RXDONE_L2PAD)) { + memmove(skb->data + transfer, + skb->data + transfer + align, + payload_len); + } + + /* + * NOTE: Always count the payload as transferred, + * even when alignment was set to zero. This is required + * for determining the correct offset for the ICV data. + */ + transfer += payload_len; + + /* + * Copy ICV data + * AES appends 8 bytes, we can't fill the upper + * 4 bytes, but mac80211 doesn't care about what + * we provide here anyway and strips it immediately. + */ + memcpy(skb->data + transfer, &rxdesc->icv, 4); + transfer += icv_len; + + /* IV/EIV/ICV has been inserted into frame */ + rxdesc->size = transfer; + rxdesc->flags &= ~RX_FLAG_IV_STRIPPED; +} diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c new file mode 100644 index 000000000000..90fdb02b55e7 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c @@ -0,0 +1,800 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 debugfs specific routines. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" +#include "rt2x00dump.h" + +#define MAX_LINE_LENGTH 64 + +struct rt2x00debug_crypto { + unsigned long success; + unsigned long icv_error; + unsigned long mic_error; + unsigned long key_error; +}; + +struct rt2x00debug_intf { + /* + * Pointer to driver structure where + * this debugfs entry belongs to. + */ + struct rt2x00_dev *rt2x00dev; + + /* + * Reference to the rt2x00debug structure + * which can be used to communicate with + * the registers. + */ + const struct rt2x00debug *debug; + + /* + * Debugfs entries for: + * - driver folder + * - driver file + * - chipset file + * - device state flags file + * - device capability flags file + * - register folder + * - csr offset/value files + * - eeprom offset/value files + * - bbp offset/value files + * - rf offset/value files + * - rfcsr offset/value files + * - queue folder + * - frame dump file + * - queue stats file + * - crypto stats file + */ + struct dentry *driver_folder; + struct dentry *driver_entry; + struct dentry *chipset_entry; + struct dentry *dev_flags; + struct dentry *cap_flags; + struct dentry *register_folder; + struct dentry *csr_off_entry; + struct dentry *csr_val_entry; + struct dentry *eeprom_off_entry; + struct dentry *eeprom_val_entry; + struct dentry *bbp_off_entry; + struct dentry *bbp_val_entry; + struct dentry *rf_off_entry; + struct dentry *rf_val_entry; + struct dentry *rfcsr_off_entry; + struct dentry *rfcsr_val_entry; + struct dentry *queue_folder; + struct dentry *queue_frame_dump_entry; + struct dentry *queue_stats_entry; + struct dentry *crypto_stats_entry; + + /* + * The frame dump file only allows a single reader, + * so we need to store the current state here. + */ + unsigned long frame_dump_flags; +#define FRAME_DUMP_FILE_OPEN 1 + + /* + * We queue each frame before dumping it to the user, + * per read command we will pass a single skb structure + * so we should be prepared to queue multiple sk buffers + * before sending it to userspace. + */ + struct sk_buff_head frame_dump_skbqueue; + wait_queue_head_t frame_dump_waitqueue; + + /* + * HW crypto statistics. + * All statistics are stored separately per cipher type. + */ + struct rt2x00debug_crypto crypto_stats[CIPHER_MAX]; + + /* + * Driver and chipset files will use a data buffer + * that has been created in advance. This will simplify + * the code since we can use the debugfs functions. + */ + struct debugfs_blob_wrapper driver_blob; + struct debugfs_blob_wrapper chipset_blob; + + /* + * Requested offset for each register type. + */ + unsigned int offset_csr; + unsigned int offset_eeprom; + unsigned int offset_bbp; + unsigned int offset_rf; + unsigned int offset_rfcsr; +}; + +void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, + struct rxdone_entry_desc *rxdesc) +{ + struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; + enum cipher cipher = rxdesc->cipher; + enum rx_crypto status = rxdesc->cipher_status; + + if (cipher == CIPHER_TKIP_NO_MIC) + cipher = CIPHER_TKIP; + if (cipher == CIPHER_NONE || cipher >= CIPHER_MAX) + return; + + /* Remove CIPHER_NONE index */ + cipher--; + + intf->crypto_stats[cipher].success += (status == RX_CRYPTO_SUCCESS); + intf->crypto_stats[cipher].icv_error += (status == RX_CRYPTO_FAIL_ICV); + intf->crypto_stats[cipher].mic_error += (status == RX_CRYPTO_FAIL_MIC); + intf->crypto_stats[cipher].key_error += (status == RX_CRYPTO_FAIL_KEY); +} + +void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, + enum rt2x00_dump_type type, struct sk_buff *skb) +{ + struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); + struct sk_buff *skbcopy; + struct rt2x00dump_hdr *dump_hdr; + struct timeval timestamp; + u32 data_len; + + if (likely(!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags))) + return; + + do_gettimeofday(×tamp); + + if (skb_queue_len(&intf->frame_dump_skbqueue) > 20) { + rt2x00_dbg(rt2x00dev, "txrx dump queue length exceeded\n"); + return; + } + + data_len = skb->len; + if (skbdesc->flags & SKBDESC_DESC_IN_SKB) + data_len -= skbdesc->desc_len; + + skbcopy = alloc_skb(sizeof(*dump_hdr) + skbdesc->desc_len + data_len, + GFP_ATOMIC); + if (!skbcopy) { + rt2x00_dbg(rt2x00dev, "Failed to copy skb for dump\n"); + return; + } + + dump_hdr = (struct rt2x00dump_hdr *)skb_put(skbcopy, sizeof(*dump_hdr)); + dump_hdr->version = cpu_to_le32(DUMP_HEADER_VERSION); + dump_hdr->header_length = cpu_to_le32(sizeof(*dump_hdr)); + dump_hdr->desc_length = cpu_to_le32(skbdesc->desc_len); + dump_hdr->data_length = cpu_to_le32(data_len); + dump_hdr->chip_rt = cpu_to_le16(rt2x00dev->chip.rt); + dump_hdr->chip_rf = cpu_to_le16(rt2x00dev->chip.rf); + dump_hdr->chip_rev = cpu_to_le16(rt2x00dev->chip.rev); + dump_hdr->type = cpu_to_le16(type); + dump_hdr->queue_index = skbdesc->entry->queue->qid; + dump_hdr->entry_index = skbdesc->entry->entry_idx; + dump_hdr->timestamp_sec = cpu_to_le32(timestamp.tv_sec); + dump_hdr->timestamp_usec = cpu_to_le32(timestamp.tv_usec); + + if (!(skbdesc->flags & SKBDESC_DESC_IN_SKB)) + memcpy(skb_put(skbcopy, skbdesc->desc_len), skbdesc->desc, + skbdesc->desc_len); + memcpy(skb_put(skbcopy, skb->len), skb->data, skb->len); + + skb_queue_tail(&intf->frame_dump_skbqueue, skbcopy); + wake_up_interruptible(&intf->frame_dump_waitqueue); + + /* + * Verify that the file has not been closed while we were working. + */ + if (!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) + skb_queue_purge(&intf->frame_dump_skbqueue); +} +EXPORT_SYMBOL_GPL(rt2x00debug_dump_frame); + +static int rt2x00debug_file_open(struct inode *inode, struct file *file) +{ + struct rt2x00debug_intf *intf = inode->i_private; + + file->private_data = inode->i_private; + + if (!try_module_get(intf->debug->owner)) + return -EBUSY; + + return 0; +} + +static int rt2x00debug_file_release(struct inode *inode, struct file *file) +{ + struct rt2x00debug_intf *intf = file->private_data; + + module_put(intf->debug->owner); + + return 0; +} + +static int rt2x00debug_open_queue_dump(struct inode *inode, struct file *file) +{ + struct rt2x00debug_intf *intf = inode->i_private; + int retval; + + retval = rt2x00debug_file_open(inode, file); + if (retval) + return retval; + + if (test_and_set_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) { + rt2x00debug_file_release(inode, file); + return -EBUSY; + } + + return 0; +} + +static int rt2x00debug_release_queue_dump(struct inode *inode, struct file *file) +{ + struct rt2x00debug_intf *intf = inode->i_private; + + skb_queue_purge(&intf->frame_dump_skbqueue); + + clear_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags); + + return rt2x00debug_file_release(inode, file); +} + +static ssize_t rt2x00debug_read_queue_dump(struct file *file, + char __user *buf, + size_t length, + loff_t *offset) +{ + struct rt2x00debug_intf *intf = file->private_data; + struct sk_buff *skb; + size_t status; + int retval; + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + retval = + wait_event_interruptible(intf->frame_dump_waitqueue, + (skb = + skb_dequeue(&intf->frame_dump_skbqueue))); + if (retval) + return retval; + + status = min_t(size_t, skb->len, length); + if (copy_to_user(buf, skb->data, status)) { + status = -EFAULT; + goto exit; + } + + *offset += status; + +exit: + kfree_skb(skb); + + return status; +} + +static unsigned int rt2x00debug_poll_queue_dump(struct file *file, + poll_table *wait) +{ + struct rt2x00debug_intf *intf = file->private_data; + + poll_wait(file, &intf->frame_dump_waitqueue, wait); + + if (!skb_queue_empty(&intf->frame_dump_skbqueue)) + return POLLOUT | POLLWRNORM; + + return 0; +} + +static const struct file_operations rt2x00debug_fop_queue_dump = { + .owner = THIS_MODULE, + .read = rt2x00debug_read_queue_dump, + .poll = rt2x00debug_poll_queue_dump, + .open = rt2x00debug_open_queue_dump, + .release = rt2x00debug_release_queue_dump, + .llseek = default_llseek, +}; + +static ssize_t rt2x00debug_read_queue_stats(struct file *file, + char __user *buf, + size_t length, + loff_t *offset) +{ + struct rt2x00debug_intf *intf = file->private_data; + struct data_queue *queue; + unsigned long irqflags; + unsigned int lines = 1 + intf->rt2x00dev->data_queues; + size_t size; + char *data; + char *temp; + + if (*offset) + return 0; + + data = kcalloc(lines, MAX_LINE_LENGTH, GFP_KERNEL); + if (!data) + return -ENOMEM; + + temp = data + + sprintf(data, "qid\tflags\t\tcount\tlimit\tlength\tindex\tdma done\tdone\n"); + + queue_for_each(intf->rt2x00dev, queue) { + spin_lock_irqsave(&queue->index_lock, irqflags); + + temp += sprintf(temp, "%d\t0x%.8x\t%d\t%d\t%d\t%d\t%d\t\t%d\n", + queue->qid, (unsigned int)queue->flags, + queue->count, queue->limit, queue->length, + queue->index[Q_INDEX], + queue->index[Q_INDEX_DMA_DONE], + queue->index[Q_INDEX_DONE]); + + spin_unlock_irqrestore(&queue->index_lock, irqflags); + } + + size = strlen(data); + size = min(size, length); + + if (copy_to_user(buf, data, size)) { + kfree(data); + return -EFAULT; + } + + kfree(data); + + *offset += size; + return size; +} + +static const struct file_operations rt2x00debug_fop_queue_stats = { + .owner = THIS_MODULE, + .read = rt2x00debug_read_queue_stats, + .open = rt2x00debug_file_open, + .release = rt2x00debug_file_release, + .llseek = default_llseek, +}; + +#ifdef CONFIG_RT2X00_LIB_CRYPTO +static ssize_t rt2x00debug_read_crypto_stats(struct file *file, + char __user *buf, + size_t length, + loff_t *offset) +{ + struct rt2x00debug_intf *intf = file->private_data; + static const char * const name[] = { "WEP64", "WEP128", "TKIP", "AES" }; + char *data; + char *temp; + size_t size; + unsigned int i; + + if (*offset) + return 0; + + data = kzalloc((1 + CIPHER_MAX) * MAX_LINE_LENGTH, GFP_KERNEL); + if (!data) + return -ENOMEM; + + temp = data; + temp += sprintf(data, "cipher\tsuccess\ticv err\tmic err\tkey err\n"); + + for (i = 0; i < CIPHER_MAX; i++) { + temp += sprintf(temp, "%s\t%lu\t%lu\t%lu\t%lu\n", name[i], + intf->crypto_stats[i].success, + intf->crypto_stats[i].icv_error, + intf->crypto_stats[i].mic_error, + intf->crypto_stats[i].key_error); + } + + size = strlen(data); + size = min(size, length); + + if (copy_to_user(buf, data, size)) { + kfree(data); + return -EFAULT; + } + + kfree(data); + + *offset += size; + return size; +} + +static const struct file_operations rt2x00debug_fop_crypto_stats = { + .owner = THIS_MODULE, + .read = rt2x00debug_read_crypto_stats, + .open = rt2x00debug_file_open, + .release = rt2x00debug_file_release, + .llseek = default_llseek, +}; +#endif + +#define RT2X00DEBUGFS_OPS_READ(__name, __format, __type) \ +static ssize_t rt2x00debug_read_##__name(struct file *file, \ + char __user *buf, \ + size_t length, \ + loff_t *offset) \ +{ \ + struct rt2x00debug_intf *intf = file->private_data; \ + const struct rt2x00debug *debug = intf->debug; \ + char line[16]; \ + size_t size; \ + unsigned int index = intf->offset_##__name; \ + __type value; \ + \ + if (*offset) \ + return 0; \ + \ + if (index >= debug->__name.word_count) \ + return -EINVAL; \ + \ + index += (debug->__name.word_base / \ + debug->__name.word_size); \ + \ + if (debug->__name.flags & RT2X00DEBUGFS_OFFSET) \ + index *= debug->__name.word_size; \ + \ + debug->__name.read(intf->rt2x00dev, index, &value); \ + \ + size = sprintf(line, __format, value); \ + \ + if (copy_to_user(buf, line, size)) \ + return -EFAULT; \ + \ + *offset += size; \ + return size; \ +} + +#define RT2X00DEBUGFS_OPS_WRITE(__name, __type) \ +static ssize_t rt2x00debug_write_##__name(struct file *file, \ + const char __user *buf,\ + size_t length, \ + loff_t *offset) \ +{ \ + struct rt2x00debug_intf *intf = file->private_data; \ + const struct rt2x00debug *debug = intf->debug; \ + char line[16]; \ + size_t size; \ + unsigned int index = intf->offset_##__name; \ + __type value; \ + \ + if (*offset) \ + return 0; \ + \ + if (index >= debug->__name.word_count) \ + return -EINVAL; \ + \ + if (length > sizeof(line)) \ + return -EINVAL; \ + \ + if (copy_from_user(line, buf, length)) \ + return -EFAULT; \ + \ + size = strlen(line); \ + value = simple_strtoul(line, NULL, 0); \ + \ + index += (debug->__name.word_base / \ + debug->__name.word_size); \ + \ + if (debug->__name.flags & RT2X00DEBUGFS_OFFSET) \ + index *= debug->__name.word_size; \ + \ + debug->__name.write(intf->rt2x00dev, index, value); \ + \ + *offset += size; \ + return size; \ +} + +#define RT2X00DEBUGFS_OPS(__name, __format, __type) \ +RT2X00DEBUGFS_OPS_READ(__name, __format, __type); \ +RT2X00DEBUGFS_OPS_WRITE(__name, __type); \ + \ +static const struct file_operations rt2x00debug_fop_##__name = {\ + .owner = THIS_MODULE, \ + .read = rt2x00debug_read_##__name, \ + .write = rt2x00debug_write_##__name, \ + .open = rt2x00debug_file_open, \ + .release = rt2x00debug_file_release, \ + .llseek = generic_file_llseek, \ +}; + +RT2X00DEBUGFS_OPS(csr, "0x%.8x\n", u32); +RT2X00DEBUGFS_OPS(eeprom, "0x%.4x\n", u16); +RT2X00DEBUGFS_OPS(bbp, "0x%.2x\n", u8); +RT2X00DEBUGFS_OPS(rf, "0x%.8x\n", u32); +RT2X00DEBUGFS_OPS(rfcsr, "0x%.2x\n", u8); + +static ssize_t rt2x00debug_read_dev_flags(struct file *file, + char __user *buf, + size_t length, + loff_t *offset) +{ + struct rt2x00debug_intf *intf = file->private_data; + char line[16]; + size_t size; + + if (*offset) + return 0; + + size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->flags); + + if (copy_to_user(buf, line, size)) + return -EFAULT; + + *offset += size; + return size; +} + +static const struct file_operations rt2x00debug_fop_dev_flags = { + .owner = THIS_MODULE, + .read = rt2x00debug_read_dev_flags, + .open = rt2x00debug_file_open, + .release = rt2x00debug_file_release, + .llseek = default_llseek, +}; + +static ssize_t rt2x00debug_read_cap_flags(struct file *file, + char __user *buf, + size_t length, + loff_t *offset) +{ + struct rt2x00debug_intf *intf = file->private_data; + char line[16]; + size_t size; + + if (*offset) + return 0; + + size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->cap_flags); + + if (copy_to_user(buf, line, size)) + return -EFAULT; + + *offset += size; + return size; +} + +static const struct file_operations rt2x00debug_fop_cap_flags = { + .owner = THIS_MODULE, + .read = rt2x00debug_read_cap_flags, + .open = rt2x00debug_file_open, + .release = rt2x00debug_file_release, + .llseek = default_llseek, +}; + +static struct dentry *rt2x00debug_create_file_driver(const char *name, + struct rt2x00debug_intf + *intf, + struct debugfs_blob_wrapper + *blob) +{ + char *data; + + data = kzalloc(3 * MAX_LINE_LENGTH, GFP_KERNEL); + if (!data) + return NULL; + + blob->data = data; + data += sprintf(data, "driver:\t%s\n", intf->rt2x00dev->ops->name); + data += sprintf(data, "version:\t%s\n", DRV_VERSION); + blob->size = strlen(blob->data); + + return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob); +} + +static struct dentry *rt2x00debug_create_file_chipset(const char *name, + struct rt2x00debug_intf + *intf, + struct + debugfs_blob_wrapper + *blob) +{ + const struct rt2x00debug *debug = intf->debug; + char *data; + + data = kzalloc(9 * MAX_LINE_LENGTH, GFP_KERNEL); + if (!data) + return NULL; + + blob->data = data; + data += sprintf(data, "rt chip:\t%04x\n", intf->rt2x00dev->chip.rt); + data += sprintf(data, "rf chip:\t%04x\n", intf->rt2x00dev->chip.rf); + data += sprintf(data, "revision:\t%04x\n", intf->rt2x00dev->chip.rev); + data += sprintf(data, "\n"); + data += sprintf(data, "register\tbase\twords\twordsize\n"); +#define RT2X00DEBUGFS_SPRINTF_REGISTER(__name) \ +{ \ + if(debug->__name.read) \ + data += sprintf(data, __stringify(__name) \ + "\t%d\t%d\t%d\n", \ + debug->__name.word_base, \ + debug->__name.word_count, \ + debug->__name.word_size); \ +} + RT2X00DEBUGFS_SPRINTF_REGISTER(csr); + RT2X00DEBUGFS_SPRINTF_REGISTER(eeprom); + RT2X00DEBUGFS_SPRINTF_REGISTER(bbp); + RT2X00DEBUGFS_SPRINTF_REGISTER(rf); + RT2X00DEBUGFS_SPRINTF_REGISTER(rfcsr); +#undef RT2X00DEBUGFS_SPRINTF_REGISTER + + blob->size = strlen(blob->data); + + return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob); +} + +void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) +{ + const struct rt2x00debug *debug = rt2x00dev->ops->debugfs; + struct rt2x00debug_intf *intf; + + intf = kzalloc(sizeof(struct rt2x00debug_intf), GFP_KERNEL); + if (!intf) { + rt2x00_err(rt2x00dev, "Failed to allocate debug handler\n"); + return; + } + + intf->debug = debug; + intf->rt2x00dev = rt2x00dev; + rt2x00dev->debugfs_intf = intf; + + intf->driver_folder = + debugfs_create_dir(intf->rt2x00dev->ops->name, + rt2x00dev->hw->wiphy->debugfsdir); + if (IS_ERR(intf->driver_folder) || !intf->driver_folder) + goto exit; + + intf->driver_entry = + rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob); + if (IS_ERR(intf->driver_entry) || !intf->driver_entry) + goto exit; + + intf->chipset_entry = + rt2x00debug_create_file_chipset("chipset", + intf, &intf->chipset_blob); + if (IS_ERR(intf->chipset_entry) || !intf->chipset_entry) + goto exit; + + intf->dev_flags = debugfs_create_file("dev_flags", S_IRUSR, + intf->driver_folder, intf, + &rt2x00debug_fop_dev_flags); + if (IS_ERR(intf->dev_flags) || !intf->dev_flags) + goto exit; + + intf->cap_flags = debugfs_create_file("cap_flags", S_IRUSR, + intf->driver_folder, intf, + &rt2x00debug_fop_cap_flags); + if (IS_ERR(intf->cap_flags) || !intf->cap_flags) + goto exit; + + intf->register_folder = + debugfs_create_dir("register", intf->driver_folder); + if (IS_ERR(intf->register_folder) || !intf->register_folder) + goto exit; + +#define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \ +({ \ + if(debug->__name.read) { \ + (__intf)->__name##_off_entry = \ + debugfs_create_u32(__stringify(__name) "_offset", \ + S_IRUSR | S_IWUSR, \ + (__intf)->register_folder, \ + &(__intf)->offset_##__name); \ + if (IS_ERR((__intf)->__name##_off_entry) \ + || !(__intf)->__name##_off_entry) \ + goto exit; \ + \ + (__intf)->__name##_val_entry = \ + debugfs_create_file(__stringify(__name) "_value", \ + S_IRUSR | S_IWUSR, \ + (__intf)->register_folder, \ + (__intf), &rt2x00debug_fop_##__name); \ + if (IS_ERR((__intf)->__name##_val_entry) \ + || !(__intf)->__name##_val_entry) \ + goto exit; \ + } \ +}) + + RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, csr); + RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, eeprom); + RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, bbp); + RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rf); + RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rfcsr); + +#undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY + + intf->queue_folder = + debugfs_create_dir("queue", intf->driver_folder); + if (IS_ERR(intf->queue_folder) || !intf->queue_folder) + goto exit; + + intf->queue_frame_dump_entry = + debugfs_create_file("dump", S_IRUSR, intf->queue_folder, + intf, &rt2x00debug_fop_queue_dump); + if (IS_ERR(intf->queue_frame_dump_entry) + || !intf->queue_frame_dump_entry) + goto exit; + + skb_queue_head_init(&intf->frame_dump_skbqueue); + init_waitqueue_head(&intf->frame_dump_waitqueue); + + intf->queue_stats_entry = + debugfs_create_file("queue", S_IRUSR, intf->queue_folder, + intf, &rt2x00debug_fop_queue_stats); + +#ifdef CONFIG_RT2X00_LIB_CRYPTO + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) + intf->crypto_stats_entry = + debugfs_create_file("crypto", S_IRUGO, intf->queue_folder, + intf, &rt2x00debug_fop_crypto_stats); +#endif + + return; + +exit: + rt2x00debug_deregister(rt2x00dev); + rt2x00_err(rt2x00dev, "Failed to register debug handler\n"); +} + +void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) +{ + struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; + + if (unlikely(!intf)) + return; + + skb_queue_purge(&intf->frame_dump_skbqueue); + +#ifdef CONFIG_RT2X00_LIB_CRYPTO + debugfs_remove(intf->crypto_stats_entry); +#endif + debugfs_remove(intf->queue_stats_entry); + debugfs_remove(intf->queue_frame_dump_entry); + debugfs_remove(intf->queue_folder); + debugfs_remove(intf->rfcsr_val_entry); + debugfs_remove(intf->rfcsr_off_entry); + debugfs_remove(intf->rf_val_entry); + debugfs_remove(intf->rf_off_entry); + debugfs_remove(intf->bbp_val_entry); + debugfs_remove(intf->bbp_off_entry); + debugfs_remove(intf->eeprom_val_entry); + debugfs_remove(intf->eeprom_off_entry); + debugfs_remove(intf->csr_val_entry); + debugfs_remove(intf->csr_off_entry); + debugfs_remove(intf->register_folder); + debugfs_remove(intf->dev_flags); + debugfs_remove(intf->cap_flags); + debugfs_remove(intf->chipset_entry); + debugfs_remove(intf->driver_entry); + debugfs_remove(intf->driver_folder); + kfree(intf->chipset_blob.data); + kfree(intf->driver_blob.data); + kfree(intf); + + rt2x00dev->debugfs_intf = NULL; +} diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.h b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.h new file mode 100644 index 000000000000..e65712c235bd --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.h @@ -0,0 +1,69 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00debug + Abstract: Data structures for the rt2x00debug. + */ + +#ifndef RT2X00DEBUG_H +#define RT2X00DEBUG_H + +struct rt2x00_dev; + +/** + * enum rt2x00debugfs_entry_flags: Flags for debugfs registry entry + * + * @RT2X00DEBUGFS_OFFSET: rt2x00lib should pass the register offset + * as argument when using the callback function read()/write() + */ +enum rt2x00debugfs_entry_flags { + RT2X00DEBUGFS_OFFSET = (1 << 0), +}; + +#define RT2X00DEBUGFS_REGISTER_ENTRY(__name, __type) \ +struct reg##__name { \ + void (*read)(struct rt2x00_dev *rt2x00dev, \ + const unsigned int word, __type *data); \ + void (*write)(struct rt2x00_dev *rt2x00dev, \ + const unsigned int word, __type data); \ + \ + unsigned int flags; \ + \ + unsigned int word_base; \ + unsigned int word_size; \ + unsigned int word_count; \ +} __name + +struct rt2x00debug { + /* + * Reference to the modules structure. + */ + struct module *owner; + + /* + * Register access entries. + */ + RT2X00DEBUGFS_REGISTER_ENTRY(csr, u32); + RT2X00DEBUGFS_REGISTER_ENTRY(eeprom, u16); + RT2X00DEBUGFS_REGISTER_ENTRY(bbp, u8); + RT2X00DEBUGFS_REGISTER_ENTRY(rf, u32); + RT2X00DEBUGFS_REGISTER_ENTRY(rfcsr, u8); +}; + +#endif /* RT2X00DEBUG_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c new file mode 100644 index 000000000000..5639ed816813 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -0,0 +1,1549 @@ +/* + Copyright (C) 2010 Willow Garage + Copyright (C) 2004 - 2010 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 generic device routines. + */ + +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" + +/* + * Utility functions. + */ +u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif) +{ + /* + * When in STA mode, bssidx is always 0 otherwise local_address[5] + * contains the bss number, see BSS_ID_MASK comments for details. + */ + if (rt2x00dev->intf_sta_count) + return 0; + return vif->addr[5] & (rt2x00dev->ops->max_ap_intf - 1); +} +EXPORT_SYMBOL_GPL(rt2x00lib_get_bssidx); + +/* + * Radio control handlers. + */ +int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + int status; + + /* + * Don't enable the radio twice. + * And check if the hardware button has been disabled. + */ + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return 0; + + /* + * Initialize all data queues. + */ + rt2x00queue_init_queues(rt2x00dev); + + /* + * Enable radio. + */ + status = + rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_ON); + if (status) + return status; + + rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_ON); + + rt2x00leds_led_radio(rt2x00dev, true); + rt2x00led_led_activity(rt2x00dev, true); + + set_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags); + + /* + * Enable queues. + */ + rt2x00queue_start_queues(rt2x00dev); + rt2x00link_start_tuner(rt2x00dev); + rt2x00link_start_agc(rt2x00dev); + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) + rt2x00link_start_vcocal(rt2x00dev); + + /* + * Start watchdog monitoring. + */ + rt2x00link_start_watchdog(rt2x00dev); + + return 0; +} + +void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + if (!test_and_clear_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + /* + * Stop watchdog monitoring. + */ + rt2x00link_stop_watchdog(rt2x00dev); + + /* + * Stop all queues + */ + rt2x00link_stop_agc(rt2x00dev); + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) + rt2x00link_stop_vcocal(rt2x00dev); + rt2x00link_stop_tuner(rt2x00dev); + rt2x00queue_stop_queues(rt2x00dev); + rt2x00queue_flush_queues(rt2x00dev, true); + + /* + * Disable radio. + */ + rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_OFF); + rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_OFF); + rt2x00led_led_activity(rt2x00dev, false); + rt2x00leds_led_radio(rt2x00dev, false); +} + +static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = data; + struct rt2x00_intf *intf = vif_to_intf(vif); + + /* + * It is possible the radio was disabled while the work had been + * scheduled. If that happens we should return here immediately, + * note that in the spinlock protected area above the delayed_flags + * have been cleared correctly. + */ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags)) { + mutex_lock(&intf->beacon_skb_mutex); + rt2x00queue_update_beacon(rt2x00dev, vif); + mutex_unlock(&intf->beacon_skb_mutex); + } +} + +static void rt2x00lib_intf_scheduled(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, intf_work); + + /* + * Iterate over each interface and perform the + * requested configurations. + */ + ieee80211_iterate_active_interfaces(rt2x00dev->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_intf_scheduled_iter, + rt2x00dev); +} + +static void rt2x00lib_autowakeup(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, autowakeup_work.work); + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return; + + if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) + rt2x00_err(rt2x00dev, "Device failed to wakeup\n"); + clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); +} + +/* + * Interrupt context handlers. + */ +static void rt2x00lib_bc_buffer_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ieee80211_tx_control control = {}; + struct rt2x00_dev *rt2x00dev = data; + struct sk_buff *skb; + + /* + * Only AP mode interfaces do broad- and multicast buffering + */ + if (vif->type != NL80211_IFTYPE_AP) + return; + + /* + * Send out buffered broad- and multicast frames + */ + skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); + while (skb) { + rt2x00mac_tx(rt2x00dev->hw, &control, skb); + skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); + } +} + +static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = data; + + if (vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC && + vif->type != NL80211_IFTYPE_MESH_POINT && + vif->type != NL80211_IFTYPE_WDS) + return; + + /* + * Update the beacon without locking. This is safe on PCI devices + * as they only update the beacon periodically here. This should + * never be called for USB devices. + */ + WARN_ON(rt2x00_is_usb(rt2x00dev)); + rt2x00queue_update_beacon(rt2x00dev, vif); +} + +void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) +{ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + /* send buffered bc/mc frames out for every bssid */ + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_bc_buffer_iter, rt2x00dev); + /* + * Devices with pre tbtt interrupt don't need to update the beacon + * here as they will fetch the next beacon directly prior to + * transmission. + */ + if (rt2x00_has_cap_pre_tbtt_interrupt(rt2x00dev)) + return; + + /* fetch next beacon */ + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_beaconupdate_iter, rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); + +void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev) +{ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + /* fetch next beacon */ + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_beaconupdate_iter, rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt); + +void rt2x00lib_dmastart(struct queue_entry *entry) +{ + set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); + rt2x00queue_index_inc(entry, Q_INDEX); +} +EXPORT_SYMBOL_GPL(rt2x00lib_dmastart); + +void rt2x00lib_dmadone(struct queue_entry *entry) +{ + set_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags); + clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); + rt2x00queue_index_inc(entry, Q_INDEX_DMA_DONE); +} +EXPORT_SYMBOL_GPL(rt2x00lib_dmadone); + +static inline int rt2x00lib_txdone_bar_status(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct ieee80211_bar *bar = (void *) entry->skb->data; + struct rt2x00_bar_list_entry *bar_entry; + int ret; + + if (likely(!ieee80211_is_back_req(bar->frame_control))) + return 0; + + /* + * Unlike all other frames, the status report for BARs does + * not directly come from the hardware as it is incapable of + * matching a BA to a previously send BAR. The hardware will + * report all BARs as if they weren't acked at all. + * + * Instead the RX-path will scan for incoming BAs and set the + * block_acked flag if it sees one that was likely caused by + * a BAR from us. + * + * Remove remaining BARs here and return their status for + * TX done processing. + */ + ret = 0; + rcu_read_lock(); + list_for_each_entry_rcu(bar_entry, &rt2x00dev->bar_list, list) { + if (bar_entry->entry != entry) + continue; + + spin_lock_bh(&rt2x00dev->bar_list_lock); + /* Return whether this BAR was blockacked or not */ + ret = bar_entry->block_acked; + /* Remove the BAR from our checklist */ + list_del_rcu(&bar_entry->list); + spin_unlock_bh(&rt2x00dev->bar_list_lock); + kfree_rcu(bar_entry, head); + + break; + } + rcu_read_unlock(); + + return ret; +} + +void rt2x00lib_txdone(struct queue_entry *entry, + struct txdone_entry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + unsigned int header_length, i; + u8 rate_idx, rate_flags, retry_rates; + u8 skbdesc_flags = skbdesc->flags; + bool success; + + /* + * Unmap the skb. + */ + rt2x00queue_unmap_skb(entry); + + /* + * Remove the extra tx headroom from the skb. + */ + skb_pull(entry->skb, rt2x00dev->extra_tx_headroom); + + /* + * Signal that the TX descriptor is no longer in the skb. + */ + skbdesc->flags &= ~SKBDESC_DESC_IN_SKB; + + /* + * Determine the length of 802.11 header. + */ + header_length = ieee80211_get_hdrlen_from_skb(entry->skb); + + /* + * Remove L2 padding which was added during + */ + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) + rt2x00queue_remove_l2pad(entry->skb, header_length); + + /* + * If the IV/EIV data was stripped from the frame before it was + * passed to the hardware, we should now reinsert it again because + * mac80211 will expect the same data to be present it the + * frame as it was passed to us. + */ + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) + rt2x00crypto_tx_insert_iv(entry->skb, header_length); + + /* + * Send frame to debugfs immediately, after this call is completed + * we are going to overwrite the skb->cb array. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry->skb); + + /* + * Determine if the frame has been successfully transmitted and + * remove BARs from our check list while checking for their + * TX status. + */ + success = + rt2x00lib_txdone_bar_status(entry) || + test_bit(TXDONE_SUCCESS, &txdesc->flags) || + test_bit(TXDONE_UNKNOWN, &txdesc->flags); + + /* + * Update TX statistics. + */ + rt2x00dev->link.qual.tx_success += success; + rt2x00dev->link.qual.tx_failed += !success; + + rate_idx = skbdesc->tx_rate_idx; + rate_flags = skbdesc->tx_rate_flags; + retry_rates = test_bit(TXDONE_FALLBACK, &txdesc->flags) ? + (txdesc->retry + 1) : 1; + + /* + * Initialize TX status + */ + memset(&tx_info->status, 0, sizeof(tx_info->status)); + tx_info->status.ack_signal = 0; + + /* + * Frame was send with retries, hardware tried + * different rates to send out the frame, at each + * retry it lowered the rate 1 step except when the + * lowest rate was used. + */ + for (i = 0; i < retry_rates && i < IEEE80211_TX_MAX_RATES; i++) { + tx_info->status.rates[i].idx = rate_idx - i; + tx_info->status.rates[i].flags = rate_flags; + + if (rate_idx - i == 0) { + /* + * The lowest rate (index 0) was used until the + * number of max retries was reached. + */ + tx_info->status.rates[i].count = retry_rates - i; + i++; + break; + } + tx_info->status.rates[i].count = 1; + } + if (i < (IEEE80211_TX_MAX_RATES - 1)) + tx_info->status.rates[i].idx = -1; /* terminate */ + + if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) { + if (success) + tx_info->flags |= IEEE80211_TX_STAT_ACK; + else + rt2x00dev->low_level_stats.dot11ACKFailureCount++; + } + + /* + * Every single frame has it's own tx status, hence report + * every frame as ampdu of size 1. + * + * TODO: if we can find out how many frames were aggregated + * by the hw we could provide the real ampdu_len to mac80211 + * which would allow the rc algorithm to better decide on + * which rates are suitable. + */ + if (test_bit(TXDONE_AMPDU, &txdesc->flags) || + tx_info->flags & IEEE80211_TX_CTL_AMPDU) { + tx_info->flags |= IEEE80211_TX_STAT_AMPDU; + tx_info->status.ampdu_len = 1; + tx_info->status.ampdu_ack_len = success ? 1 : 0; + + if (!success) + tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; + } + + if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) { + if (success) + rt2x00dev->low_level_stats.dot11RTSSuccessCount++; + else + rt2x00dev->low_level_stats.dot11RTSFailureCount++; + } + + /* + * Only send the status report to mac80211 when it's a frame + * that originated in mac80211. If this was a extra frame coming + * through a mac80211 library call (RTS/CTS) then we should not + * send the status report back. + */ + if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) { + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TASKLET_CONTEXT)) + ieee80211_tx_status(rt2x00dev->hw, entry->skb); + else + ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb); + } else + dev_kfree_skb_any(entry->skb); + + /* + * Make this entry available for reuse. + */ + entry->skb = NULL; + entry->flags = 0; + + rt2x00dev->ops->lib->clear_entry(entry); + + rt2x00queue_index_inc(entry, Q_INDEX_DONE); + + /* + * If the data queue was below the threshold before the txdone + * handler we must make sure the packet queue in the mac80211 stack + * is reenabled when the txdone handler has finished. This has to be + * serialized with rt2x00mac_tx(), otherwise we can wake up queue + * before it was stopped. + */ + spin_lock_bh(&entry->queue->tx_lock); + if (!rt2x00queue_threshold(entry->queue)) + rt2x00queue_unpause_queue(entry->queue); + spin_unlock_bh(&entry->queue->tx_lock); +} +EXPORT_SYMBOL_GPL(rt2x00lib_txdone); + +void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status) +{ + struct txdone_entry_desc txdesc; + + txdesc.flags = 0; + __set_bit(status, &txdesc.flags); + txdesc.retry = 0; + + rt2x00lib_txdone(entry, &txdesc); +} +EXPORT_SYMBOL_GPL(rt2x00lib_txdone_noinfo); + +static u8 *rt2x00lib_find_ie(u8 *data, unsigned int len, u8 ie) +{ + struct ieee80211_mgmt *mgmt = (void *)data; + u8 *pos, *end; + + pos = (u8 *)mgmt->u.beacon.variable; + end = data + len; + while (pos < end) { + if (pos + 2 + pos[1] > end) + return NULL; + + if (pos[0] == ie) + return pos; + + pos += 2 + pos[1]; + } + + return NULL; +} + +static void rt2x00lib_sleep(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, sleep_work); + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return; + + /* + * Check again is powersaving is enabled, to prevent races from delayed + * work execution. + */ + if (!test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) + rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf, + IEEE80211_CONF_CHANGE_PS); +} + +static void rt2x00lib_rxdone_check_ba(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct rxdone_entry_desc *rxdesc) +{ + struct rt2x00_bar_list_entry *entry; + struct ieee80211_bar *ba = (void *)skb->data; + + if (likely(!ieee80211_is_back(ba->frame_control))) + return; + + if (rxdesc->size < sizeof(*ba) + FCS_LEN) + return; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, &rt2x00dev->bar_list, list) { + + if (ba->start_seq_num != entry->start_seq_num) + continue; + +#define TID_CHECK(a, b) ( \ + ((a) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK)) == \ + ((b) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK))) \ + + if (!TID_CHECK(ba->control, entry->control)) + continue; + +#undef TID_CHECK + + if (!ether_addr_equal_64bits(ba->ra, entry->ta)) + continue; + + if (!ether_addr_equal_64bits(ba->ta, entry->ra)) + continue; + + /* Mark BAR since we received the according BA */ + spin_lock_bh(&rt2x00dev->bar_list_lock); + entry->block_acked = 1; + spin_unlock_bh(&rt2x00dev->bar_list_lock); + break; + } + rcu_read_unlock(); + +} + +static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct rxdone_entry_desc *rxdesc) +{ + struct ieee80211_hdr *hdr = (void *) skb->data; + struct ieee80211_tim_ie *tim_ie; + u8 *tim; + u8 tim_len; + bool cam; + + /* If this is not a beacon, or if mac80211 has no powersaving + * configured, or if the device is already in powersaving mode + * we can exit now. */ + if (likely(!ieee80211_is_beacon(hdr->frame_control) || + !(rt2x00dev->hw->conf.flags & IEEE80211_CONF_PS))) + return; + + /* min. beacon length + FCS_LEN */ + if (skb->len <= 40 + FCS_LEN) + return; + + /* and only beacons from the associated BSSID, please */ + if (!(rxdesc->dev_flags & RXDONE_MY_BSS) || + !rt2x00dev->aid) + return; + + rt2x00dev->last_beacon = jiffies; + + tim = rt2x00lib_find_ie(skb->data, skb->len - FCS_LEN, WLAN_EID_TIM); + if (!tim) + return; + + if (tim[1] < sizeof(*tim_ie)) + return; + + tim_len = tim[1]; + tim_ie = (struct ieee80211_tim_ie *) &tim[2]; + + /* Check whenever the PHY can be turned off again. */ + + /* 1. What about buffered unicast traffic for our AID? */ + cam = ieee80211_check_tim(tim_ie, tim_len, rt2x00dev->aid); + + /* 2. Maybe the AP wants to send multicast/broadcast data? */ + cam |= (tim_ie->bitmap_ctrl & 0x01); + + if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) + queue_work(rt2x00dev->workqueue, &rt2x00dev->sleep_work); +} + +static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev, + struct rxdone_entry_desc *rxdesc) +{ + struct ieee80211_supported_band *sband; + const struct rt2x00_rate *rate; + unsigned int i; + int signal = rxdesc->signal; + int type = (rxdesc->dev_flags & RXDONE_SIGNAL_MASK); + + switch (rxdesc->rate_mode) { + case RATE_MODE_CCK: + case RATE_MODE_OFDM: + /* + * For non-HT rates the MCS value needs to contain the + * actually used rate modulation (CCK or OFDM). + */ + if (rxdesc->dev_flags & RXDONE_SIGNAL_MCS) + signal = RATE_MCS(rxdesc->rate_mode, signal); + + sband = &rt2x00dev->bands[rt2x00dev->curr_band]; + for (i = 0; i < sband->n_bitrates; i++) { + rate = rt2x00_get_rate(sband->bitrates[i].hw_value); + if (((type == RXDONE_SIGNAL_PLCP) && + (rate->plcp == signal)) || + ((type == RXDONE_SIGNAL_BITRATE) && + (rate->bitrate == signal)) || + ((type == RXDONE_SIGNAL_MCS) && + (rate->mcs == signal))) { + return i; + } + } + break; + case RATE_MODE_HT_MIX: + case RATE_MODE_HT_GREENFIELD: + if (signal >= 0 && signal <= 76) + return signal; + break; + default: + break; + } + + rt2x00_warn(rt2x00dev, "Frame received with unrecognized signal, mode=0x%.4x, signal=0x%.4x, type=%d\n", + rxdesc->rate_mode, signal, type); + return 0; +} + +void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct rxdone_entry_desc rxdesc; + struct sk_buff *skb; + struct ieee80211_rx_status *rx_status; + unsigned int header_length; + int rate_idx; + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || + !test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + goto submit_entry; + + if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) + goto submit_entry; + + /* + * Allocate a new sk_buffer. If no new buffer available, drop the + * received frame and reuse the existing buffer. + */ + skb = rt2x00queue_alloc_rxskb(entry, gfp); + if (!skb) + goto submit_entry; + + /* + * Unmap the skb. + */ + rt2x00queue_unmap_skb(entry); + + /* + * Extract the RXD details. + */ + memset(&rxdesc, 0, sizeof(rxdesc)); + rt2x00dev->ops->lib->fill_rxdone(entry, &rxdesc); + + /* + * Check for valid size in case we get corrupted descriptor from + * hardware. + */ + if (unlikely(rxdesc.size == 0 || + rxdesc.size > entry->queue->data_size)) { + rt2x00_err(rt2x00dev, "Wrong frame size %d max %d\n", + rxdesc.size, entry->queue->data_size); + dev_kfree_skb(entry->skb); + goto renew_skb; + } + + /* + * The data behind the ieee80211 header must be + * aligned on a 4 byte boundary. + */ + header_length = ieee80211_get_hdrlen_from_skb(entry->skb); + + /* + * Hardware might have stripped the IV/EIV/ICV data, + * in that case it is possible that the data was + * provided separately (through hardware descriptor) + * in which case we should reinsert the data into the frame. + */ + if ((rxdesc.dev_flags & RXDONE_CRYPTO_IV) && + (rxdesc.flags & RX_FLAG_IV_STRIPPED)) + rt2x00crypto_rx_insert_iv(entry->skb, header_length, + &rxdesc); + else if (header_length && + (rxdesc.size > header_length) && + (rxdesc.dev_flags & RXDONE_L2PAD)) + rt2x00queue_remove_l2pad(entry->skb, header_length); + + /* Trim buffer to correct size */ + skb_trim(entry->skb, rxdesc.size); + + /* + * Translate the signal to the correct bitrate index. + */ + rate_idx = rt2x00lib_rxdone_read_signal(rt2x00dev, &rxdesc); + if (rxdesc.rate_mode == RATE_MODE_HT_MIX || + rxdesc.rate_mode == RATE_MODE_HT_GREENFIELD) + rxdesc.flags |= RX_FLAG_HT; + + /* + * Check if this is a beacon, and more frames have been + * buffered while we were in powersaving mode. + */ + rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc); + + /* + * Check for incoming BlockAcks to match to the BlockAckReqs + * we've send out. + */ + rt2x00lib_rxdone_check_ba(rt2x00dev, entry->skb, &rxdesc); + + /* + * Update extra components + */ + rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc); + rt2x00debug_update_crypto(rt2x00dev, &rxdesc); + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb); + + /* + * Initialize RX status information, and send frame + * to mac80211. + */ + rx_status = IEEE80211_SKB_RXCB(entry->skb); + + /* Ensure that all fields of rx_status are initialized + * properly. The skb->cb array was used for driver + * specific informations, so rx_status might contain + * garbage. + */ + memset(rx_status, 0, sizeof(*rx_status)); + + rx_status->mactime = rxdesc.timestamp; + rx_status->band = rt2x00dev->curr_band; + rx_status->freq = rt2x00dev->curr_freq; + rx_status->rate_idx = rate_idx; + rx_status->signal = rxdesc.rssi; + rx_status->flag = rxdesc.flags; + rx_status->antenna = rt2x00dev->link.ant.active.rx; + + ieee80211_rx_ni(rt2x00dev->hw, entry->skb); + +renew_skb: + /* + * Replace the skb with the freshly allocated one. + */ + entry->skb = skb; + +submit_entry: + entry->flags = 0; + rt2x00queue_index_inc(entry, Q_INDEX_DONE); + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2x00dev->ops->lib->clear_entry(entry); +} +EXPORT_SYMBOL_GPL(rt2x00lib_rxdone); + +/* + * Driver initialization handlers. + */ +const struct rt2x00_rate rt2x00_supported_rates[12] = { + { + .flags = DEV_RATE_CCK, + .bitrate = 10, + .ratemask = BIT(0), + .plcp = 0x00, + .mcs = RATE_MCS(RATE_MODE_CCK, 0), + }, + { + .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, + .bitrate = 20, + .ratemask = BIT(1), + .plcp = 0x01, + .mcs = RATE_MCS(RATE_MODE_CCK, 1), + }, + { + .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, + .bitrate = 55, + .ratemask = BIT(2), + .plcp = 0x02, + .mcs = RATE_MCS(RATE_MODE_CCK, 2), + }, + { + .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, + .bitrate = 110, + .ratemask = BIT(3), + .plcp = 0x03, + .mcs = RATE_MCS(RATE_MODE_CCK, 3), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 60, + .ratemask = BIT(4), + .plcp = 0x0b, + .mcs = RATE_MCS(RATE_MODE_OFDM, 0), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 90, + .ratemask = BIT(5), + .plcp = 0x0f, + .mcs = RATE_MCS(RATE_MODE_OFDM, 1), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 120, + .ratemask = BIT(6), + .plcp = 0x0a, + .mcs = RATE_MCS(RATE_MODE_OFDM, 2), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 180, + .ratemask = BIT(7), + .plcp = 0x0e, + .mcs = RATE_MCS(RATE_MODE_OFDM, 3), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 240, + .ratemask = BIT(8), + .plcp = 0x09, + .mcs = RATE_MCS(RATE_MODE_OFDM, 4), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 360, + .ratemask = BIT(9), + .plcp = 0x0d, + .mcs = RATE_MCS(RATE_MODE_OFDM, 5), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 480, + .ratemask = BIT(10), + .plcp = 0x08, + .mcs = RATE_MCS(RATE_MODE_OFDM, 6), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 540, + .ratemask = BIT(11), + .plcp = 0x0c, + .mcs = RATE_MCS(RATE_MODE_OFDM, 7), + }, +}; + +static void rt2x00lib_channel(struct ieee80211_channel *entry, + const int channel, const int tx_power, + const int value) +{ + /* XXX: this assumption about the band is wrong for 802.11j */ + entry->band = channel <= 14 ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + entry->center_freq = ieee80211_channel_to_frequency(channel, + entry->band); + entry->hw_value = value; + entry->max_power = tx_power; + entry->max_antenna_gain = 0xff; +} + +static void rt2x00lib_rate(struct ieee80211_rate *entry, + const u16 index, const struct rt2x00_rate *rate) +{ + entry->flags = 0; + entry->bitrate = rate->bitrate; + entry->hw_value = index; + entry->hw_value_short = index; + + if (rate->flags & DEV_RATE_SHORT_PREAMBLE) + entry->flags |= IEEE80211_RATE_SHORT_PREAMBLE; +} + +static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, + struct hw_mode_spec *spec) +{ + struct ieee80211_hw *hw = rt2x00dev->hw; + struct ieee80211_channel *channels; + struct ieee80211_rate *rates; + unsigned int num_rates; + unsigned int i; + + num_rates = 0; + if (spec->supported_rates & SUPPORT_RATE_CCK) + num_rates += 4; + if (spec->supported_rates & SUPPORT_RATE_OFDM) + num_rates += 8; + + channels = kcalloc(spec->num_channels, sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL); + if (!rates) + goto exit_free_channels; + + /* + * Initialize Rate list. + */ + for (i = 0; i < num_rates; i++) + rt2x00lib_rate(&rates[i], i, rt2x00_get_rate(i)); + + /* + * Initialize Channel list. + */ + for (i = 0; i < spec->num_channels; i++) { + rt2x00lib_channel(&channels[i], + spec->channels[i].channel, + spec->channels_info[i].max_power, i); + } + + /* + * Intitialize 802.11b, 802.11g + * Rates: CCK, OFDM. + * Channels: 2.4 GHz + */ + if (spec->supported_bands & SUPPORT_BAND_2GHZ) { + rt2x00dev->bands[IEEE80211_BAND_2GHZ].n_channels = 14; + rt2x00dev->bands[IEEE80211_BAND_2GHZ].n_bitrates = num_rates; + rt2x00dev->bands[IEEE80211_BAND_2GHZ].channels = channels; + rt2x00dev->bands[IEEE80211_BAND_2GHZ].bitrates = rates; + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &rt2x00dev->bands[IEEE80211_BAND_2GHZ]; + memcpy(&rt2x00dev->bands[IEEE80211_BAND_2GHZ].ht_cap, + &spec->ht, sizeof(spec->ht)); + } + + /* + * Intitialize 802.11a + * Rates: OFDM. + * Channels: OFDM, UNII, HiperLAN2. + */ + if (spec->supported_bands & SUPPORT_BAND_5GHZ) { + rt2x00dev->bands[IEEE80211_BAND_5GHZ].n_channels = + spec->num_channels - 14; + rt2x00dev->bands[IEEE80211_BAND_5GHZ].n_bitrates = + num_rates - 4; + rt2x00dev->bands[IEEE80211_BAND_5GHZ].channels = &channels[14]; + rt2x00dev->bands[IEEE80211_BAND_5GHZ].bitrates = &rates[4]; + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &rt2x00dev->bands[IEEE80211_BAND_5GHZ]; + memcpy(&rt2x00dev->bands[IEEE80211_BAND_5GHZ].ht_cap, + &spec->ht, sizeof(spec->ht)); + } + + return 0; + + exit_free_channels: + kfree(channels); + rt2x00_err(rt2x00dev, "Allocation ieee80211 modes failed\n"); + return -ENOMEM; +} + +static void rt2x00lib_remove_hw(struct rt2x00_dev *rt2x00dev) +{ + if (test_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags)) + ieee80211_unregister_hw(rt2x00dev->hw); + + if (likely(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ])) { + kfree(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels); + kfree(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->bitrates); + rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; + rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; + } + + kfree(rt2x00dev->spec.channels_info); +} + +static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + int status; + + if (test_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags)) + return 0; + + /* + * Initialize HW modes. + */ + status = rt2x00lib_probe_hw_modes(rt2x00dev, spec); + if (status) + return status; + + /* + * Initialize HW fields. + */ + rt2x00dev->hw->queues = rt2x00dev->ops->tx_queues; + + /* + * Initialize extra TX headroom required. + */ + rt2x00dev->hw->extra_tx_headroom = + max_t(unsigned int, IEEE80211_TX_STATUS_HEADROOM, + rt2x00dev->extra_tx_headroom); + + /* + * Take TX headroom required for alignment into account. + */ + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) + rt2x00dev->hw->extra_tx_headroom += RT2X00_L2PAD_SIZE; + else if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA)) + rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE; + + /* + * Tell mac80211 about the size of our private STA structure. + */ + rt2x00dev->hw->sta_data_size = sizeof(struct rt2x00_sta); + + /* + * Allocate tx status FIFO for driver use. + */ + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TXSTATUS_FIFO)) { + /* + * Allocate the txstatus fifo. In the worst case the tx + * status fifo has to hold the tx status of all entries + * in all tx queues. Hence, calculate the kfifo size as + * tx_queues * entry_num and round up to the nearest + * power of 2. + */ + int kfifo_size = + roundup_pow_of_two(rt2x00dev->ops->tx_queues * + rt2x00dev->tx->limit * + sizeof(u32)); + + status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size, + GFP_KERNEL); + if (status) + return status; + } + + /* + * Initialize tasklets if used by the driver. Tasklets are + * disabled until the interrupts are turned on. The driver + * has to handle that. + */ +#define RT2X00_TASKLET_INIT(taskletname) \ + if (rt2x00dev->ops->lib->taskletname) { \ + tasklet_init(&rt2x00dev->taskletname, \ + rt2x00dev->ops->lib->taskletname, \ + (unsigned long)rt2x00dev); \ + } + + RT2X00_TASKLET_INIT(txstatus_tasklet); + RT2X00_TASKLET_INIT(pretbtt_tasklet); + RT2X00_TASKLET_INIT(tbtt_tasklet); + RT2X00_TASKLET_INIT(rxdone_tasklet); + RT2X00_TASKLET_INIT(autowake_tasklet); + +#undef RT2X00_TASKLET_INIT + + /* + * Register HW. + */ + status = ieee80211_register_hw(rt2x00dev->hw); + if (status) + return status; + + set_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags); + + return 0; +} + +/* + * Initialization/uninitialization handlers. + */ +static void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + if (!test_and_clear_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags)) + return; + + /* + * Stop rfkill polling. + */ + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) + rt2x00rfkill_unregister(rt2x00dev); + + /* + * Allow the HW to uninitialize. + */ + rt2x00dev->ops->lib->uninitialize(rt2x00dev); + + /* + * Free allocated queue entries. + */ + rt2x00queue_uninitialize(rt2x00dev); +} + +static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) +{ + int status; + + if (test_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags)) + return 0; + + /* + * Allocate all queue entries. + */ + status = rt2x00queue_initialize(rt2x00dev); + if (status) + return status; + + /* + * Initialize the device. + */ + status = rt2x00dev->ops->lib->initialize(rt2x00dev); + if (status) { + rt2x00queue_uninitialize(rt2x00dev); + return status; + } + + set_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags); + + /* + * Start rfkill polling. + */ + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) + rt2x00rfkill_register(rt2x00dev); + + return 0; +} + +int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + if (test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) + return 0; + + /* + * If this is the first interface which is added, + * we should load the firmware now. + */ + retval = rt2x00lib_load_firmware(rt2x00dev); + if (retval) + return retval; + + /* + * Initialize the device. + */ + retval = rt2x00lib_initialize(rt2x00dev); + if (retval) + return retval; + + rt2x00dev->intf_ap_count = 0; + rt2x00dev->intf_sta_count = 0; + rt2x00dev->intf_associated = 0; + + /* Enable the radio */ + retval = rt2x00lib_enable_radio(rt2x00dev); + if (retval) + return retval; + + set_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags); + + return 0; +} + +void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev) +{ + if (!test_and_clear_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) + return; + + /* + * Perhaps we can add something smarter here, + * but for now just disabling the radio should do. + */ + rt2x00lib_disable_radio(rt2x00dev); + + rt2x00dev->intf_ap_count = 0; + rt2x00dev->intf_sta_count = 0; + rt2x00dev->intf_associated = 0; +} + +static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_iface_limit *if_limit; + struct ieee80211_iface_combination *if_combination; + + if (rt2x00dev->ops->max_ap_intf < 2) + return; + + /* + * Build up AP interface limits structure. + */ + if_limit = &rt2x00dev->if_limits_ap; + if_limit->max = rt2x00dev->ops->max_ap_intf; + if_limit->types = BIT(NL80211_IFTYPE_AP); +#ifdef CONFIG_MAC80211_MESH + if_limit->types |= BIT(NL80211_IFTYPE_MESH_POINT); +#endif + + /* + * Build up AP interface combinations structure. + */ + if_combination = &rt2x00dev->if_combinations[IF_COMB_AP]; + if_combination->limits = if_limit; + if_combination->n_limits = 1; + if_combination->max_interfaces = if_limit->max; + if_combination->num_different_channels = 1; + + /* + * Finally, specify the possible combinations to mac80211. + */ + rt2x00dev->hw->wiphy->iface_combinations = rt2x00dev->if_combinations; + rt2x00dev->hw->wiphy->n_iface_combinations = 1; +} + +static unsigned int rt2x00dev_extra_tx_headroom(struct rt2x00_dev *rt2x00dev) +{ + if (WARN_ON(!rt2x00dev->tx)) + return 0; + + if (rt2x00_is_usb(rt2x00dev)) + return rt2x00dev->tx[0].winfo_size + rt2x00dev->tx[0].desc_size; + + return rt2x00dev->tx[0].winfo_size; +} + +/* + * driver allocation handlers. + */ +int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) +{ + int retval = -ENOMEM; + + /* + * Set possible interface combinations. + */ + rt2x00lib_set_if_combinations(rt2x00dev); + + /* + * Allocate the driver data memory, if necessary. + */ + if (rt2x00dev->ops->drv_data_size > 0) { + rt2x00dev->drv_data = kzalloc(rt2x00dev->ops->drv_data_size, + GFP_KERNEL); + if (!rt2x00dev->drv_data) { + retval = -ENOMEM; + goto exit; + } + } + + spin_lock_init(&rt2x00dev->irqmask_lock); + mutex_init(&rt2x00dev->csr_mutex); + INIT_LIST_HEAD(&rt2x00dev->bar_list); + spin_lock_init(&rt2x00dev->bar_list_lock); + + set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + + /* + * Make room for rt2x00_intf inside the per-interface + * structure ieee80211_vif. + */ + rt2x00dev->hw->vif_data_size = sizeof(struct rt2x00_intf); + + /* + * rt2x00 devices can only use the last n bits of the MAC address + * for virtual interfaces. + */ + rt2x00dev->hw->wiphy->addr_mask[ETH_ALEN - 1] = + (rt2x00dev->ops->max_ap_intf - 1); + + /* + * Initialize work. + */ + rt2x00dev->workqueue = + alloc_ordered_workqueue("%s", 0, wiphy_name(rt2x00dev->hw->wiphy)); + if (!rt2x00dev->workqueue) { + retval = -ENOMEM; + goto exit; + } + + INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); + INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup); + INIT_WORK(&rt2x00dev->sleep_work, rt2x00lib_sleep); + + /* + * Let the driver probe the device to detect the capabilities. + */ + retval = rt2x00dev->ops->lib->probe_hw(rt2x00dev); + if (retval) { + rt2x00_err(rt2x00dev, "Failed to allocate device\n"); + goto exit; + } + + /* + * Allocate queue array. + */ + retval = rt2x00queue_allocate(rt2x00dev); + if (retval) + goto exit; + + /* Cache TX headroom value */ + rt2x00dev->extra_tx_headroom = rt2x00dev_extra_tx_headroom(rt2x00dev); + + /* + * Determine which operating modes are supported, all modes + * which require beaconing, depend on the availability of + * beacon entries. + */ + rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + if (rt2x00dev->bcn->limit > 0) + rt2x00dev->hw->wiphy->interface_modes |= + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_WDS); + + rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + + /* + * Initialize ieee80211 structure. + */ + retval = rt2x00lib_probe_hw(rt2x00dev); + if (retval) { + rt2x00_err(rt2x00dev, "Failed to initialize hw\n"); + goto exit; + } + + /* + * Register extra components. + */ + rt2x00link_register(rt2x00dev); + rt2x00leds_register(rt2x00dev); + rt2x00debug_register(rt2x00dev); + + /* + * Start rfkill polling. + */ + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) + rt2x00rfkill_register(rt2x00dev); + + return 0; + +exit: + rt2x00lib_remove_dev(rt2x00dev); + + return retval; +} +EXPORT_SYMBOL_GPL(rt2x00lib_probe_dev); + +void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) +{ + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + + /* + * Stop rfkill polling. + */ + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) + rt2x00rfkill_unregister(rt2x00dev); + + /* + * Disable radio. + */ + rt2x00lib_disable_radio(rt2x00dev); + + /* + * Stop all work. + */ + cancel_work_sync(&rt2x00dev->intf_work); + cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); + cancel_work_sync(&rt2x00dev->sleep_work); + if (rt2x00_is_usb(rt2x00dev)) { + hrtimer_cancel(&rt2x00dev->txstatus_timer); + cancel_work_sync(&rt2x00dev->rxdone_work); + cancel_work_sync(&rt2x00dev->txdone_work); + } + if (rt2x00dev->workqueue) + destroy_workqueue(rt2x00dev->workqueue); + + /* + * Free the tx status fifo. + */ + kfifo_free(&rt2x00dev->txstatus_fifo); + + /* + * Kill the tx status tasklet. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->pretbtt_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->autowake_tasklet); + + /* + * Uninitialize device. + */ + rt2x00lib_uninitialize(rt2x00dev); + + /* + * Free extra components + */ + rt2x00debug_deregister(rt2x00dev); + rt2x00leds_unregister(rt2x00dev); + + /* + * Free ieee80211_hw memory. + */ + rt2x00lib_remove_hw(rt2x00dev); + + /* + * Free firmware image. + */ + rt2x00lib_free_firmware(rt2x00dev); + + /* + * Free queue structures. + */ + rt2x00queue_free(rt2x00dev); + + /* + * Free the driver data. + */ + kfree(rt2x00dev->drv_data); +} +EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev); + +/* + * Device state handlers + */ +#ifdef CONFIG_PM +int rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev, pm_message_t state) +{ + rt2x00_dbg(rt2x00dev, "Going to sleep\n"); + + /* + * Prevent mac80211 from accessing driver while suspended. + */ + if (!test_and_clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return 0; + + /* + * Cleanup as much as possible. + */ + rt2x00lib_uninitialize(rt2x00dev); + + /* + * Suspend/disable extra components. + */ + rt2x00leds_suspend(rt2x00dev); + rt2x00debug_deregister(rt2x00dev); + + /* + * Set device mode to sleep for power management, + * on some hardware this call seems to consistently fail. + * From the specifications it is hard to tell why it fails, + * and if this is a "bad thing". + * Overall it is safe to just ignore the failure and + * continue suspending. The only downside is that the + * device will not be in optimal power save mode, but with + * the radio and the other components already disabled the + * device is as good as disabled. + */ + if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_SLEEP)) + rt2x00_warn(rt2x00dev, "Device failed to enter sleep state, continue suspending\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00lib_suspend); + +int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev) +{ + rt2x00_dbg(rt2x00dev, "Waking up\n"); + + /* + * Restore/enable extra components. + */ + rt2x00debug_register(rt2x00dev); + rt2x00leds_resume(rt2x00dev); + + /* + * We are ready again to receive requests from mac80211. + */ + set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00lib_resume); +#endif /* CONFIG_PM */ + +/* + * rt2x00lib module information. + */ +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2x00 library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dump.h b/drivers/net/wireless/ralink/rt2x00/rt2x00dump.h new file mode 100644 index 000000000000..4c0e01b5d515 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dump.h @@ -0,0 +1,127 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00dump + Abstract: + Data structures for the rt2x00debug & userspace. + + The declarations in this file can be used by both rt2x00 + and userspace and therefore should be kept together in + this file. + */ + +#ifndef RT2X00DUMP_H +#define RT2X00DUMP_H + +/** + * DOC: Introduction + * + * This header is intended to be exported to userspace, + * to make the structures and enumerations available to userspace + * applications. This means that all data types should be exportable. + * + * When rt2x00 is compiled with debugfs support enabled, + * it is possible to capture all data coming in and out of the device + * by reading the frame dump file. This file can have only a single reader. + * The following frames will be reported: + * - All incoming frames (rx) + * - All outgoing frames (tx, including beacon and atim) + * - All completed frames (txdone including atim) + * + * The data is send to the file using the following format: + * + * [rt2x00dump header][hardware descriptor][ieee802.11 frame] + * + * rt2x00dump header: The description of the dumped frame, as well as + * additional information useful for debugging. See &rt2x00dump_hdr. + * hardware descriptor: Descriptor that was used to receive or transmit + * the frame. + * ieee802.11 frame: The actual frame that was received or transmitted. + */ + +/** + * enum rt2x00_dump_type - Frame type + * + * These values are used for the @type member of &rt2x00dump_hdr. + * @DUMP_FRAME_RXDONE: This frame has been received by the hardware. + * @DUMP_FRAME_TX: This frame is queued for transmission to the hardware. + * @DUMP_FRAME_TXDONE: This frame indicates the device has handled + * the tx event which has either succeeded or failed. A frame + * with this type should also have been reported with as a + * %DUMP_FRAME_TX frame. + * @DUMP_FRAME_BEACON: This beacon frame is queued for transmission to the + * hardware. + */ +enum rt2x00_dump_type { + DUMP_FRAME_RXDONE = 1, + DUMP_FRAME_TX = 2, + DUMP_FRAME_TXDONE = 3, + DUMP_FRAME_BEACON = 4, +}; + +/** + * struct rt2x00dump_hdr - Dump frame header + * + * Each frame dumped to the debugfs file starts with this header + * attached. This header contains the description of the actual + * frame which was dumped. + * + * New fields inside the structure must be appended to the end of + * the structure. This way userspace tools compiled for earlier + * header versions can still correctly handle the frame dump + * (although they will not handle all data passed to them in the dump). + * + * @version: Header version should always be set to %DUMP_HEADER_VERSION. + * This field must be checked by userspace to determine if it can + * handle this frame. + * @header_length: The length of the &rt2x00dump_hdr structure. This is + * used for compatibility reasons so userspace can easily determine + * the location of the next field in the dump. + * @desc_length: The length of the device descriptor. + * @data_length: The length of the frame data (including the ieee802.11 header. + * @chip_rt: RT chipset + * @chip_rf: RF chipset + * @chip_rev: Chipset revision + * @type: The frame type (&rt2x00_dump_type) + * @queue_index: The index number of the data queue. + * @entry_index: The index number of the entry inside the data queue. + * @timestamp_sec: Timestamp - seconds + * @timestamp_usec: Timestamp - microseconds + */ +struct rt2x00dump_hdr { + __le32 version; +#define DUMP_HEADER_VERSION 2 + + __le32 header_length; + __le32 desc_length; + __le32 data_length; + + __le16 chip_rt; + __le16 chip_rf; + __le16 chip_rev; + + __le16 type; + __u8 queue_index; + __u8 entry_index; + + __le32 timestamp_sec; + __le32 timestamp_usec; +}; + +#endif /* RT2X00DUMP_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00firmware.c b/drivers/net/wireless/ralink/rt2x00/rt2x00firmware.c new file mode 100644 index 000000000000..5813300f68a2 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00firmware.c @@ -0,0 +1,129 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + Copyright (C) 2004 - 2009 Gertjan van Wingerde + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 firmware loading routines. + */ + +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" + +static int rt2x00lib_request_firmware(struct rt2x00_dev *rt2x00dev) +{ + struct device *device = wiphy_dev(rt2x00dev->hw->wiphy); + const struct firmware *fw; + char *fw_name; + int retval; + + /* + * Read correct firmware from harddisk. + */ + fw_name = rt2x00dev->ops->lib->get_firmware_name(rt2x00dev); + if (!fw_name) { + rt2x00_err(rt2x00dev, + "Invalid firmware filename\n" + "Please file bug report to %s\n", DRV_PROJECT); + return -EINVAL; + } + + rt2x00_info(rt2x00dev, "Loading firmware file '%s'\n", fw_name); + + retval = request_firmware(&fw, fw_name, device); + if (retval) { + rt2x00_err(rt2x00dev, "Failed to request Firmware\n"); + return retval; + } + + if (!fw || !fw->size || !fw->data) { + rt2x00_err(rt2x00dev, "Failed to read Firmware\n"); + release_firmware(fw); + return -ENOENT; + } + + rt2x00_info(rt2x00dev, "Firmware detected - version: %d.%d\n", + fw->data[fw->size - 4], fw->data[fw->size - 3]); + snprintf(rt2x00dev->hw->wiphy->fw_version, + sizeof(rt2x00dev->hw->wiphy->fw_version), "%d.%d", + fw->data[fw->size - 4], fw->data[fw->size - 3]); + + retval = rt2x00dev->ops->lib->check_firmware(rt2x00dev, fw->data, fw->size); + switch (retval) { + case FW_OK: + break; + case FW_BAD_CRC: + rt2x00_err(rt2x00dev, "Firmware checksum error\n"); + goto exit; + case FW_BAD_LENGTH: + rt2x00_err(rt2x00dev, "Invalid firmware file length (len=%zu)\n", + fw->size); + goto exit; + case FW_BAD_VERSION: + rt2x00_err(rt2x00dev, "Current firmware does not support detected chipset\n"); + goto exit; + } + + rt2x00dev->fw = fw; + + return 0; + +exit: + release_firmware(fw); + + return -ENOENT; +} + +int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_FIRMWARE)) + return 0; + + if (!rt2x00dev->fw) { + retval = rt2x00lib_request_firmware(rt2x00dev); + if (retval) + return retval; + } + + /* + * Send firmware to the device. + */ + retval = rt2x00dev->ops->lib->load_firmware(rt2x00dev, + rt2x00dev->fw->data, + rt2x00dev->fw->size); + + /* + * When the firmware is uploaded to the hardware the LED + * association status might have been triggered, for correct + * LED handling it should now be reset. + */ + rt2x00leds_led_assoc(rt2x00dev, false); + + return retval; +} + +void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev) +{ + release_firmware(rt2x00dev->fw); + rt2x00dev->fw = NULL; +} diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c new file mode 100644 index 000000000000..c681d04b506c --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c @@ -0,0 +1,244 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 led specific routines. + */ + +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" + +void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi) +{ + struct rt2x00_led *led = &rt2x00dev->led_qual; + unsigned int brightness; + + if ((led->type != LED_TYPE_QUALITY) || !(led->flags & LED_REGISTERED)) + return; + + /* + * Led handling requires a positive value for the rssi, + * to do that correctly we need to add the correction. + */ + rssi += rt2x00dev->rssi_offset; + + /* + * Get the rssi level, this is used to convert the rssi + * to a LED value inside the range LED_OFF - LED_FULL. + */ + if (rssi <= 30) + rssi = 0; + else if (rssi <= 39) + rssi = 1; + else if (rssi <= 49) + rssi = 2; + else if (rssi <= 53) + rssi = 3; + else if (rssi <= 63) + rssi = 4; + else + rssi = 5; + + /* + * Note that we must _not_ send LED_OFF since the driver + * is going to calculate the value and might use it in a + * division. + */ + brightness = ((LED_FULL / 6) * rssi) + 1; + if (brightness != led->led_dev.brightness) { + led->led_dev.brightness_set(&led->led_dev, brightness); + led->led_dev.brightness = brightness; + } +} + +static void rt2x00led_led_simple(struct rt2x00_led *led, bool enabled) +{ + unsigned int brightness = enabled ? LED_FULL : LED_OFF; + + if (!(led->flags & LED_REGISTERED)) + return; + + led->led_dev.brightness_set(&led->led_dev, brightness); + led->led_dev.brightness = brightness; +} + +void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled) +{ + if (rt2x00dev->led_qual.type == LED_TYPE_ACTIVITY) + rt2x00led_led_simple(&rt2x00dev->led_qual, enabled); +} + +void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled) +{ + if (rt2x00dev->led_assoc.type == LED_TYPE_ASSOC) + rt2x00led_led_simple(&rt2x00dev->led_assoc, enabled); +} + +void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled) +{ + if (rt2x00dev->led_radio.type == LED_TYPE_RADIO) + rt2x00led_led_simple(&rt2x00dev->led_radio, enabled); +} + +static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + const char *name) +{ + struct device *device = wiphy_dev(rt2x00dev->hw->wiphy); + int retval; + + led->led_dev.name = name; + led->led_dev.brightness = LED_OFF; + + retval = led_classdev_register(device, &led->led_dev); + if (retval) { + rt2x00_err(rt2x00dev, "Failed to register led handler\n"); + return retval; + } + + led->flags |= LED_REGISTERED; + + return 0; +} + +void rt2x00leds_register(struct rt2x00_dev *rt2x00dev) +{ + char name[36]; + int retval; + unsigned long on_period; + unsigned long off_period; + const char *phy_name = wiphy_name(rt2x00dev->hw->wiphy); + + if (rt2x00dev->led_radio.flags & LED_INITIALIZED) { + snprintf(name, sizeof(name), "%s-%s::radio", + rt2x00dev->ops->name, phy_name); + + retval = rt2x00leds_register_led(rt2x00dev, + &rt2x00dev->led_radio, + name); + if (retval) + goto exit_fail; + } + + if (rt2x00dev->led_assoc.flags & LED_INITIALIZED) { + snprintf(name, sizeof(name), "%s-%s::assoc", + rt2x00dev->ops->name, phy_name); + + retval = rt2x00leds_register_led(rt2x00dev, + &rt2x00dev->led_assoc, + name); + if (retval) + goto exit_fail; + } + + if (rt2x00dev->led_qual.flags & LED_INITIALIZED) { + snprintf(name, sizeof(name), "%s-%s::quality", + rt2x00dev->ops->name, phy_name); + + retval = rt2x00leds_register_led(rt2x00dev, + &rt2x00dev->led_qual, + name); + if (retval) + goto exit_fail; + } + + /* + * Initialize blink time to default value: + * On period: 70ms + * Off period: 30ms + */ + if (rt2x00dev->led_radio.led_dev.blink_set) { + on_period = 70; + off_period = 30; + rt2x00dev->led_radio.led_dev.blink_set( + &rt2x00dev->led_radio.led_dev, &on_period, &off_period); + } + + return; + +exit_fail: + rt2x00leds_unregister(rt2x00dev); +} + +static void rt2x00leds_unregister_led(struct rt2x00_led *led) +{ + led_classdev_unregister(&led->led_dev); + + /* + * This might look weird, but when we are unregistering while + * suspended the led is already off, and since we haven't + * fully resumed yet, access to the device might not be + * possible yet. + */ + if (!(led->led_dev.flags & LED_SUSPENDED)) + led->led_dev.brightness_set(&led->led_dev, LED_OFF); + + led->flags &= ~LED_REGISTERED; +} + +void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev) +{ + if (rt2x00dev->led_qual.flags & LED_REGISTERED) + rt2x00leds_unregister_led(&rt2x00dev->led_qual); + if (rt2x00dev->led_assoc.flags & LED_REGISTERED) + rt2x00leds_unregister_led(&rt2x00dev->led_assoc); + if (rt2x00dev->led_radio.flags & LED_REGISTERED) + rt2x00leds_unregister_led(&rt2x00dev->led_radio); +} + +static inline void rt2x00leds_suspend_led(struct rt2x00_led *led) +{ + led_classdev_suspend(&led->led_dev); + + /* This shouldn't be needed, but just to be safe */ + led->led_dev.brightness_set(&led->led_dev, LED_OFF); + led->led_dev.brightness = LED_OFF; +} + +void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev) +{ + if (rt2x00dev->led_qual.flags & LED_REGISTERED) + rt2x00leds_suspend_led(&rt2x00dev->led_qual); + if (rt2x00dev->led_assoc.flags & LED_REGISTERED) + rt2x00leds_suspend_led(&rt2x00dev->led_assoc); + if (rt2x00dev->led_radio.flags & LED_REGISTERED) + rt2x00leds_suspend_led(&rt2x00dev->led_radio); +} + +static inline void rt2x00leds_resume_led(struct rt2x00_led *led) +{ + led_classdev_resume(&led->led_dev); + + /* Device might have enabled the LEDS during resume */ + led->led_dev.brightness_set(&led->led_dev, LED_OFF); + led->led_dev.brightness = LED_OFF; +} + +void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev) +{ + if (rt2x00dev->led_radio.flags & LED_REGISTERED) + rt2x00leds_resume_led(&rt2x00dev->led_radio); + if (rt2x00dev->led_assoc.flags & LED_REGISTERED) + rt2x00leds_resume_led(&rt2x00dev->led_assoc); + if (rt2x00dev->led_qual.flags & LED_REGISTERED) + rt2x00leds_resume_led(&rt2x00dev->led_qual); +} diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00leds.h b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.h new file mode 100644 index 000000000000..b2c5269570da --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.h @@ -0,0 +1,44 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 led datastructures and routines + */ + +#ifndef RT2X00LEDS_H +#define RT2X00LEDS_H + +enum led_type { + LED_TYPE_RADIO, + LED_TYPE_ASSOC, + LED_TYPE_ACTIVITY, + LED_TYPE_QUALITY, +}; + +struct rt2x00_led { + struct rt2x00_dev *rt2x00dev; + struct led_classdev led_dev; + + enum led_type type; + unsigned int flags; +#define LED_INITIALIZED ( 1 << 0 ) +#define LED_REGISTERED ( 1 << 1 ) +}; + +#endif /* RT2X00LEDS_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h new file mode 100644 index 000000000000..fb7c349ccc9c --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h @@ -0,0 +1,468 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + Copyright (C) 2004 - 2009 Gertjan van Wingerde + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00lib + Abstract: Data structures and definitions for the rt2x00lib module. + */ + +#ifndef RT2X00LIB_H +#define RT2X00LIB_H + +/* + * Interval defines + */ +#define WATCHDOG_INTERVAL round_jiffies_relative(HZ) +#define LINK_TUNE_INTERVAL round_jiffies_relative(HZ) +#define AGC_INTERVAL round_jiffies_relative(4 * HZ) +#define VCO_INTERVAL round_jiffies_relative(10 * HZ) /* 10 sec */ + +/* + * rt2x00_rate: Per rate device information + */ +struct rt2x00_rate { + unsigned short flags; +#define DEV_RATE_CCK 0x0001 +#define DEV_RATE_OFDM 0x0002 +#define DEV_RATE_SHORT_PREAMBLE 0x0004 + + unsigned short bitrate; /* In 100kbit/s */ + unsigned short ratemask; + + unsigned short plcp; + unsigned short mcs; +}; + +extern const struct rt2x00_rate rt2x00_supported_rates[12]; + +static inline const struct rt2x00_rate *rt2x00_get_rate(const u16 hw_value) +{ + return &rt2x00_supported_rates[hw_value & 0xff]; +} + +#define RATE_MCS(__mode, __mcs) \ + ((((__mode) & 0x00ff) << 8) | ((__mcs) & 0x00ff)) + +static inline int rt2x00_get_rate_mcs(const u16 mcs_value) +{ + return (mcs_value & 0x00ff); +} + +/* + * Radio control handlers. + */ +int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev); +void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev); + +/* + * Initialization handlers. + */ +int rt2x00lib_start(struct rt2x00_dev *rt2x00dev); +void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev); + +/* + * Configuration handlers. + */ +void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + enum nl80211_iftype type, + const u8 *mac, const u8 *bssid); +void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct ieee80211_bss_conf *conf, + u32 changed); +void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, + struct antenna_setup ant); +void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + const unsigned int changed_flags); + +/** + * DOC: Queue handlers + */ + +/** + * rt2x00queue_alloc_rxskb - allocate a skb for RX purposes. + * @entry: The entry for which the skb will be applicable. + */ +struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp); + +/** + * rt2x00queue_free_skb - free a skb + * @entry: The entry for which the skb will be applicable. + */ +void rt2x00queue_free_skb(struct queue_entry *entry); + +/** + * rt2x00queue_align_frame - Align 802.11 frame to 4-byte boundary + * @skb: The skb to align + * + * Align the start of the 802.11 frame to a 4-byte boundary, this could + * mean the payload is not aligned properly though. + */ +void rt2x00queue_align_frame(struct sk_buff *skb); + +/** + * rt2x00queue_insert_l2pad - Align 802.11 header & payload to 4-byte boundary + * @skb: The skb to align + * @header_length: Length of 802.11 header + * + * Apply L2 padding to align both header and payload to 4-byte boundary + */ +void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length); + +/** + * rt2x00queue_insert_l2pad - Remove L2 padding from 802.11 frame + * @skb: The skb to align + * @header_length: Length of 802.11 header + * + * Remove L2 padding used to align both header and payload to 4-byte boundary, + * by removing the L2 padding the header will no longer be 4-byte aligned. + */ +void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length); + +/** + * rt2x00queue_write_tx_frame - Write TX frame to hardware + * @queue: Queue over which the frame should be send + * @skb: The skb to send + * @local: frame is not from mac80211 + */ +int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, + struct ieee80211_sta *sta, bool local); + +/** + * rt2x00queue_update_beacon - Send new beacon from mac80211 + * to hardware. Handles locking by itself (mutex). + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @vif: Interface for which the beacon should be updated. + */ +int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif); + +/** + * rt2x00queue_update_beacon_locked - Send new beacon from mac80211 + * to hardware. Caller needs to ensure locking. + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @vif: Interface for which the beacon should be updated. + */ +int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif); + +/** + * rt2x00queue_clear_beacon - Clear beacon in hardware + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @vif: Interface for which the beacon should be updated. + */ +int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif); + +/** + * rt2x00queue_index_inc - Index incrementation function + * @entry: Queue entry (&struct queue_entry) to perform the action on. + * @index: Index type (&enum queue_index) to perform the action on. + * + * This function will increase the requested index on the entry's queue, + * it will grab the appropriate locks and handle queue overflow events by + * resetting the index to the start of the queue. + */ +void rt2x00queue_index_inc(struct queue_entry *entry, enum queue_index index); + +/** + * rt2x00queue_init_queues - Initialize all data queues + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * This function will loop through all available queues to clear all + * index numbers and set the queue entry to the correct initialization + * state. + */ +void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev); + +int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev); +void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev); +int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev); +void rt2x00queue_free(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_update_stats - Update link statistics from RX frame + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @skb: Received frame + * @rxdesc: Received frame descriptor + * + * Update link statistics based on the information from the + * received frame descriptor. + */ +void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct rxdone_entry_desc *rxdesc); + +/** + * rt2x00link_start_tuner - Start periodic link tuner work + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * This start the link tuner periodic work, this work will + * be executed periodically until &rt2x00link_stop_tuner has + * been called. + */ +void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_stop_tuner - Stop periodic link tuner work + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * After this function completed the link tuner will not + * be running until &rt2x00link_start_tuner is called. + */ +void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_reset_tuner - Reset periodic link tuner work + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @antenna: Should the antenna tuning also be reset + * + * The VGC limit configured in the hardware will be reset to 0 + * which forces the driver to rediscover the correct value for + * the current association. This is needed when configuration + * options have changed which could drastically change the + * SNR level or link quality (i.e. changing the antenna setting). + * + * Resetting the link tuner will also cause the periodic work counter + * to be reset. Any driver which has a fixed limit on the number + * of rounds the link tuner is supposed to work will accept the + * tuner actions again if this limit was previously reached. + * + * If @antenna is set to true a the software antenna diversity + * tuning will also be reset. + */ +void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna); + +/** + * rt2x00link_start_watchdog - Start periodic watchdog monitoring + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * This start the watchdog periodic work, this work will + *be executed periodically until &rt2x00link_stop_watchdog has + * been called. + */ +void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_stop_watchdog - Stop periodic watchdog monitoring + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * After this function completed the watchdog monitoring will not + * be running until &rt2x00link_start_watchdog is called. + */ +void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_start_agc - Start periodic gain calibration + * @rt2x00dev: Pointer to &struct rt2x00_dev. + */ +void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_start_vcocal - Start periodic VCO calibration + * @rt2x00dev: Pointer to &struct rt2x00_dev. + */ +void rt2x00link_start_vcocal(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_stop_agc - Stop periodic gain calibration + * @rt2x00dev: Pointer to &struct rt2x00_dev. + */ +void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_stop_vcocal - Stop periodic VCO calibration + * @rt2x00dev: Pointer to &struct rt2x00_dev. + */ +void rt2x00link_stop_vcocal(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_register - Initialize link tuning & watchdog functionality + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * Initialize work structure and all link tuning and watchdog related + * parameters. This will not start the periodic work itself. + */ +void rt2x00link_register(struct rt2x00_dev *rt2x00dev); + +/* + * Firmware handlers. + */ +#ifdef CONFIG_RT2X00_LIB_FIRMWARE +int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev); +void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev); +#else +static inline int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) +{ + return 0; +} +static inline void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev) +{ +} +#endif /* CONFIG_RT2X00_LIB_FIRMWARE */ + +/* + * Debugfs handlers. + */ +#ifdef CONFIG_RT2X00_LIB_DEBUGFS +void rt2x00debug_register(struct rt2x00_dev *rt2x00dev); +void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev); +void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, + struct rxdone_entry_desc *rxdesc); +#else +static inline void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) +{ +} + +static inline void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) +{ +} + +static inline void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, + struct rxdone_entry_desc *rxdesc) +{ +} +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ + +/* + * Crypto handlers. + */ +#ifdef CONFIG_RT2X00_LIB_CRYPTO +enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key); +void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc); +unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb); +void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, + struct txentry_desc *txdesc); +void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, + struct txentry_desc *txdesc); +void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length); +void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, + unsigned int header_length, + struct rxdone_entry_desc *rxdesc); +#else +static inline enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) +{ + return CIPHER_NONE; +} + +static inline void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc) +{ +} + +static inline unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb) +{ + return 0; +} + +static inline void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, + struct txentry_desc *txdesc) +{ +} + +static inline void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, + struct txentry_desc *txdesc) +{ +} + +static inline void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, + unsigned int header_length) +{ +} + +static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, + unsigned int header_length, + struct rxdone_entry_desc *rxdesc) +{ +} +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ + +/* + * RFkill handlers. + */ +static inline void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev) +{ + if (test_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags)) + wiphy_rfkill_start_polling(rt2x00dev->hw->wiphy); +} + +static inline void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev) +{ + if (test_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags)) + wiphy_rfkill_stop_polling(rt2x00dev->hw->wiphy); +} + +/* + * LED handlers + */ +#ifdef CONFIG_RT2X00_LIB_LEDS +void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi); +void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled); +void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled); +void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled); +void rt2x00leds_register(struct rt2x00_dev *rt2x00dev); +void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev); +void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev); +void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev); +#else +static inline void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, + int rssi) +{ +} + +static inline void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, + bool enabled) +{ +} + +static inline void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, + bool enabled) +{ +} + +static inline void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, + bool enabled) +{ +} + +static inline void rt2x00leds_register(struct rt2x00_dev *rt2x00dev) +{ +} + +static inline void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev) +{ +} + +static inline void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev) +{ +} + +static inline void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev) +{ +} +#endif /* CONFIG_RT2X00_LIB_LEDS */ + +#endif /* RT2X00LIB_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00link.c b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c new file mode 100644 index 000000000000..017188e5a736 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c @@ -0,0 +1,492 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 generic link tuning routines. + */ + +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" + +/* + * When we lack RSSI information return something less then -80 to + * tell the driver to tune the device to maximum sensitivity. + */ +#define DEFAULT_RSSI -128 + +static inline int rt2x00link_get_avg_rssi(struct ewma_rssi *ewma) +{ + unsigned long avg; + + avg = ewma_rssi_read(ewma); + if (avg) + return -avg; + + return DEFAULT_RSSI; +} + +static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + + if (rt2x00dev->link.qual.rx_success) + return rt2x00link_get_avg_rssi(&ant->rssi_ant); + + return DEFAULT_RSSI; +} + +static int rt2x00link_antenna_get_rssi_history(struct rt2x00_dev *rt2x00dev) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + + if (ant->rssi_history) + return ant->rssi_history; + return DEFAULT_RSSI; +} + +static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev, + int rssi) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + ant->rssi_history = rssi; +} + +static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev) +{ + ewma_rssi_init(&rt2x00dev->link.ant.rssi_ant); +} + +static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + struct antenna_setup new_ant; + int other_antenna; + + int sample_current = rt2x00link_antenna_get_link_rssi(rt2x00dev); + int sample_other = rt2x00link_antenna_get_rssi_history(rt2x00dev); + + memcpy(&new_ant, &ant->active, sizeof(new_ant)); + + /* + * We are done sampling. Now we should evaluate the results. + */ + ant->flags &= ~ANTENNA_MODE_SAMPLE; + + /* + * During the last period we have sampled the RSSI + * from both antennas. It now is time to determine + * which antenna demonstrated the best performance. + * When we are already on the antenna with the best + * performance, just create a good starting point + * for the history and we are done. + */ + if (sample_current >= sample_other) { + rt2x00link_antenna_update_rssi_history(rt2x00dev, + sample_current); + return; + } + + other_antenna = (ant->active.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; + + if (ant->flags & ANTENNA_RX_DIVERSITY) + new_ant.rx = other_antenna; + + if (ant->flags & ANTENNA_TX_DIVERSITY) + new_ant.tx = other_antenna; + + rt2x00lib_config_antenna(rt2x00dev, new_ant); +} + +static void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + struct antenna_setup new_ant; + int rssi_curr; + int rssi_old; + + memcpy(&new_ant, &ant->active, sizeof(new_ant)); + + /* + * Get current RSSI value along with the historical value, + * after that update the history with the current value. + */ + rssi_curr = rt2x00link_antenna_get_link_rssi(rt2x00dev); + rssi_old = rt2x00link_antenna_get_rssi_history(rt2x00dev); + rt2x00link_antenna_update_rssi_history(rt2x00dev, rssi_curr); + + /* + * Legacy driver indicates that we should swap antenna's + * when the difference in RSSI is greater that 5. This + * also should be done when the RSSI was actually better + * then the previous sample. + * When the difference exceeds the threshold we should + * sample the rssi from the other antenna to make a valid + * comparison between the 2 antennas. + */ + if (abs(rssi_curr - rssi_old) < 5) + return; + + ant->flags |= ANTENNA_MODE_SAMPLE; + + if (ant->flags & ANTENNA_RX_DIVERSITY) + new_ant.rx = (new_ant.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; + + if (ant->flags & ANTENNA_TX_DIVERSITY) + new_ant.tx = (new_ant.tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; + + rt2x00lib_config_antenna(rt2x00dev, new_ant); +} + +static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + + /* + * Determine if software diversity is enabled for + * either the TX or RX antenna (or both). + */ + if (!(ant->flags & ANTENNA_RX_DIVERSITY) && + !(ant->flags & ANTENNA_TX_DIVERSITY)) { + ant->flags = 0; + return true; + } + + /* + * If we have only sampled the data over the last period + * we should now harvest the data. Otherwise just evaluate + * the data. The latter should only be performed once + * every 2 seconds. + */ + if (ant->flags & ANTENNA_MODE_SAMPLE) { + rt2x00lib_antenna_diversity_sample(rt2x00dev); + return true; + } else if (rt2x00dev->link.count & 1) { + rt2x00lib_antenna_diversity_eval(rt2x00dev); + return true; + } + + return false; +} + +void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct rxdone_entry_desc *rxdesc) +{ + struct link *link = &rt2x00dev->link; + struct link_qual *qual = &rt2x00dev->link.qual; + struct link_ant *ant = &rt2x00dev->link.ant; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + /* + * No need to update the stats for !=STA interfaces + */ + if (!rt2x00dev->intf_sta_count) + return; + + /* + * Frame was received successfully since non-succesfull + * frames would have been dropped by the hardware. + */ + qual->rx_success++; + + /* + * We are only interested in quality statistics from + * beacons which came from the BSS which we are + * associated with. + */ + if (!ieee80211_is_beacon(hdr->frame_control) || + !(rxdesc->dev_flags & RXDONE_MY_BSS)) + return; + + /* + * Update global RSSI + */ + ewma_rssi_add(&link->avg_rssi, -rxdesc->rssi); + + /* + * Update antenna RSSI + */ + ewma_rssi_add(&ant->rssi_ant, -rxdesc->rssi); +} + +void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) +{ + struct link *link = &rt2x00dev->link; + + /* + * Link tuning should only be performed when + * an active sta interface exists. AP interfaces + * don't need link tuning and monitor mode interfaces + * should never have to work with link tuners. + */ + if (!rt2x00dev->intf_sta_count) + return; + + /** + * While scanning, link tuning is disabled. By default + * the most sensitive settings will be used to make sure + * that all beacons and probe responses will be received + * during the scan. + */ + if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) + return; + + rt2x00link_reset_tuner(rt2x00dev, false); + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->work, LINK_TUNE_INTERVAL); +} + +void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev) +{ + cancel_delayed_work_sync(&rt2x00dev->link.work); +} + +void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna) +{ + struct link_qual *qual = &rt2x00dev->link.qual; + u8 vgc_level = qual->vgc_level_reg; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + /* + * Reset link information. + * Both the currently active vgc level as well as + * the link tuner counter should be reset. Resetting + * the counter is important for devices where the + * device should only perform link tuning during the + * first minute after being enabled. + */ + rt2x00dev->link.count = 0; + memset(qual, 0, sizeof(*qual)); + ewma_rssi_init(&rt2x00dev->link.avg_rssi); + + /* + * Restore the VGC level as stored in the registers, + * the driver can use this to determine if the register + * must be updated during reset or not. + */ + qual->vgc_level_reg = vgc_level; + + /* + * Reset the link tuner. + */ + rt2x00dev->ops->lib->reset_tuner(rt2x00dev, qual); + + if (antenna) + rt2x00link_antenna_reset(rt2x00dev); +} + +static void rt2x00link_reset_qual(struct rt2x00_dev *rt2x00dev) +{ + struct link_qual *qual = &rt2x00dev->link.qual; + + qual->rx_success = 0; + qual->rx_failed = 0; + qual->tx_success = 0; + qual->tx_failed = 0; +} + +static void rt2x00link_tuner(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.work.work); + struct link *link = &rt2x00dev->link; + struct link_qual *qual = &rt2x00dev->link.qual; + + /* + * When the radio is shutting down we should + * immediately cease all link tuning. + */ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) || + test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) + return; + + /* + * Update statistics. + */ + rt2x00dev->ops->lib->link_stats(rt2x00dev, qual); + rt2x00dev->low_level_stats.dot11FCSErrorCount += qual->rx_failed; + + /* + * Update quality RSSI for link tuning, + * when we have received some frames and we managed to + * collect the RSSI data we could use this. Otherwise we + * must fallback to the default RSSI value. + */ + if (!qual->rx_success) + qual->rssi = DEFAULT_RSSI; + else + qual->rssi = rt2x00link_get_avg_rssi(&link->avg_rssi); + + /* + * Check if link tuning is supported by the hardware, some hardware + * do not support link tuning at all, while other devices can disable + * the feature from the EEPROM. + */ + if (rt2x00_has_cap_link_tuning(rt2x00dev)) + rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count); + + /* + * Send a signal to the led to update the led signal strength. + */ + rt2x00leds_led_quality(rt2x00dev, qual->rssi); + + /* + * Evaluate antenna setup, make this the last step when + * rt2x00lib_antenna_diversity made changes the quality + * statistics will be reset. + */ + if (rt2x00lib_antenna_diversity(rt2x00dev)) + rt2x00link_reset_qual(rt2x00dev); + + /* + * Increase tuner counter, and reschedule the next link tuner run. + */ + link->count++; + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->work, LINK_TUNE_INTERVAL); +} + +void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev) +{ + struct link *link = &rt2x00dev->link; + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + rt2x00dev->ops->lib->watchdog) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->watchdog_work, + WATCHDOG_INTERVAL); +} + +void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev) +{ + cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work); +} + +static void rt2x00link_watchdog(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.watchdog_work.work); + struct link *link = &rt2x00dev->link; + + /* + * When the radio is shutting down we should + * immediately cease the watchdog monitoring. + */ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + rt2x00dev->ops->lib->watchdog(rt2x00dev); + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->watchdog_work, + WATCHDOG_INTERVAL); +} + +void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev) +{ + struct link *link = &rt2x00dev->link; + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + rt2x00dev->ops->lib->gain_calibration) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->agc_work, + AGC_INTERVAL); +} + +void rt2x00link_start_vcocal(struct rt2x00_dev *rt2x00dev) +{ + struct link *link = &rt2x00dev->link; + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + rt2x00dev->ops->lib->vco_calibration) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->vco_work, + VCO_INTERVAL); +} + +void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev) +{ + cancel_delayed_work_sync(&rt2x00dev->link.agc_work); +} + +void rt2x00link_stop_vcocal(struct rt2x00_dev *rt2x00dev) +{ + cancel_delayed_work_sync(&rt2x00dev->link.vco_work); +} + +static void rt2x00link_agc(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.agc_work.work); + struct link *link = &rt2x00dev->link; + + /* + * When the radio is shutting down we should + * immediately cease the watchdog monitoring. + */ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + rt2x00dev->ops->lib->gain_calibration(rt2x00dev); + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->agc_work, + AGC_INTERVAL); +} + +static void rt2x00link_vcocal(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.vco_work.work); + struct link *link = &rt2x00dev->link; + + /* + * When the radio is shutting down we should + * immediately cease the VCO calibration. + */ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + rt2x00dev->ops->lib->vco_calibration(rt2x00dev); + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->vco_work, + VCO_INTERVAL); +} + +void rt2x00link_register(struct rt2x00_dev *rt2x00dev) +{ + INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc); + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) + INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal); + INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog); + INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner); +} diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c new file mode 100644 index 000000000000..3c26ee65a415 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c @@ -0,0 +1,846 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00mac + Abstract: rt2x00 generic mac80211 routines. + */ + +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" + +static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, + struct data_queue *queue, + struct sk_buff *frag_skb) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(frag_skb); + struct ieee80211_tx_info *rts_info; + struct sk_buff *skb; + unsigned int data_length; + int retval = 0; + + if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) + data_length = sizeof(struct ieee80211_cts); + else + data_length = sizeof(struct ieee80211_rts); + + skb = dev_alloc_skb(data_length + rt2x00dev->hw->extra_tx_headroom); + if (unlikely(!skb)) { + rt2x00_warn(rt2x00dev, "Failed to create RTS/CTS frame\n"); + return -ENOMEM; + } + + skb_reserve(skb, rt2x00dev->hw->extra_tx_headroom); + skb_put(skb, data_length); + + /* + * Copy TX information over from original frame to + * RTS/CTS frame. Note that we set the no encryption flag + * since we don't want this frame to be encrypted. + * RTS frames should be acked, while CTS-to-self frames + * should not. The ready for TX flag is cleared to prevent + * it being automatically send when the descriptor is + * written to the hardware. + */ + memcpy(skb->cb, frag_skb->cb, sizeof(skb->cb)); + rts_info = IEEE80211_SKB_CB(skb); + rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_RTS_CTS; + rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_CTS_PROTECT; + + if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) + rts_info->flags |= IEEE80211_TX_CTL_NO_ACK; + else + rts_info->flags &= ~IEEE80211_TX_CTL_NO_ACK; + + /* Disable hardware encryption */ + rts_info->control.hw_key = NULL; + + /* + * RTS/CTS frame should use the length of the frame plus any + * encryption overhead that will be added by the hardware. + */ + data_length += rt2x00crypto_tx_overhead(rt2x00dev, skb); + + if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) + ieee80211_ctstoself_get(rt2x00dev->hw, tx_info->control.vif, + frag_skb->data, data_length, tx_info, + (struct ieee80211_cts *)(skb->data)); + else + ieee80211_rts_get(rt2x00dev->hw, tx_info->control.vif, + frag_skb->data, data_length, tx_info, + (struct ieee80211_rts *)(skb->data)); + + retval = rt2x00queue_write_tx_frame(queue, skb, NULL, true); + if (retval) { + dev_kfree_skb_any(skb); + rt2x00_warn(rt2x00dev, "Failed to send RTS/CTS frame\n"); + } + + return retval; +} + +void rt2x00mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + enum data_queue_qid qid = skb_get_queue_mapping(skb); + struct data_queue *queue = NULL; + + /* + * Mac80211 might be calling this function while we are trying + * to remove the device or perhaps suspending it. + * Note that we can only stop the TX queues inside the TX path + * due to possible race conditions in mac80211. + */ + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + goto exit_free_skb; + + /* + * Use the ATIM queue if appropriate and present. + */ + if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && + rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE)) + qid = QID_ATIM; + + queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); + if (unlikely(!queue)) { + rt2x00_err(rt2x00dev, + "Attempt to send packet over invalid queue %d\n" + "Please file bug report to %s\n", qid, DRV_PROJECT); + goto exit_free_skb; + } + + /* + * If CTS/RTS is required. create and queue that frame first. + * Make sure we have at least enough entries available to send + * this CTS/RTS frame as well as the data frame. + * Note that when the driver has set the set_rts_threshold() + * callback function it doesn't need software generation of + * either RTS or CTS-to-self frame and handles everything + * inside the hardware. + */ + if (!rt2x00dev->ops->hw->set_rts_threshold && + (tx_info->control.rates[0].flags & (IEEE80211_TX_RC_USE_RTS_CTS | + IEEE80211_TX_RC_USE_CTS_PROTECT))) { + if (rt2x00queue_available(queue) <= 1) + goto exit_fail; + + if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb)) + goto exit_fail; + } + + if (unlikely(rt2x00queue_write_tx_frame(queue, skb, control->sta, false))) + goto exit_fail; + + /* + * Pausing queue has to be serialized with rt2x00lib_txdone(). Note + * we should not use spin_lock_bh variant as bottom halve was already + * disabled before ieee80211_xmit() call. + */ + spin_lock(&queue->tx_lock); + if (rt2x00queue_threshold(queue)) + rt2x00queue_pause_queue(queue); + spin_unlock(&queue->tx_lock); + + return; + + exit_fail: + spin_lock(&queue->tx_lock); + rt2x00queue_pause_queue(queue); + spin_unlock(&queue->tx_lock); + exit_free_skb: + ieee80211_free_txskb(hw, skb); +} +EXPORT_SYMBOL_GPL(rt2x00mac_tx); + +int rt2x00mac_start(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return 0; + + return rt2x00lib_start(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00mac_start); + +void rt2x00mac_stop(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return; + + rt2x00lib_stop(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00mac_stop); + +int rt2x00mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct rt2x00_intf *intf = vif_to_intf(vif); + struct data_queue *queue = rt2x00dev->bcn; + struct queue_entry *entry = NULL; + unsigned int i; + + /* + * Don't allow interfaces to be added + * the device has disappeared. + */ + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || + !test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) + return -ENODEV; + + /* + * Loop through all beacon queues to find a free + * entry. Since there are as much beacon entries + * as the maximum interfaces, this search shouldn't + * fail. + */ + for (i = 0; i < queue->limit; i++) { + entry = &queue->entries[i]; + if (!test_and_set_bit(ENTRY_BCN_ASSIGNED, &entry->flags)) + break; + } + + if (unlikely(i == queue->limit)) + return -ENOBUFS; + + /* + * We are now absolutely sure the interface can be created, + * increase interface count and start initialization. + */ + + if (vif->type == NL80211_IFTYPE_AP) + rt2x00dev->intf_ap_count++; + else + rt2x00dev->intf_sta_count++; + + mutex_init(&intf->beacon_skb_mutex); + intf->beacon = entry; + + /* + * The MAC address must be configured after the device + * has been initialized. Otherwise the device can reset + * the MAC registers. + * The BSSID address must only be configured in AP mode, + * however we should not send an empty BSSID address for + * STA interfaces at this time, since this can cause + * invalid behavior in the device. + */ + rt2x00lib_config_intf(rt2x00dev, intf, vif->type, + vif->addr, NULL); + + /* + * Some filters depend on the current working mode. We can force + * an update during the next configure_filter() run by mac80211 by + * resetting the current packet_filter state. + */ + rt2x00dev->packet_filter = 0; + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_add_interface); + +void rt2x00mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct rt2x00_intf *intf = vif_to_intf(vif); + + /* + * Don't allow interfaces to be remove while + * either the device has disappeared or when + * no interface is present. + */ + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || + (vif->type == NL80211_IFTYPE_AP && !rt2x00dev->intf_ap_count) || + (vif->type != NL80211_IFTYPE_AP && !rt2x00dev->intf_sta_count)) + return; + + if (vif->type == NL80211_IFTYPE_AP) + rt2x00dev->intf_ap_count--; + else + rt2x00dev->intf_sta_count--; + + /* + * Release beacon entry so it is available for + * new interfaces again. + */ + clear_bit(ENTRY_BCN_ASSIGNED, &intf->beacon->flags); + + /* + * Make sure the bssid and mac address registers + * are cleared to prevent false ACKing of frames. + */ + rt2x00lib_config_intf(rt2x00dev, intf, + NL80211_IFTYPE_UNSPECIFIED, NULL, NULL); +} +EXPORT_SYMBOL_GPL(rt2x00mac_remove_interface); + +int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + + /* + * mac80211 might be calling this function while we are trying + * to remove the device or perhaps suspending it. + */ + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return 0; + + /* + * Some configuration parameters (e.g. channel and antenna values) can + * only be set when the radio is enabled, but do require the RX to + * be off. During this period we should keep link tuning enabled, + * if for any reason the link tuner must be reset, this will be + * handled by rt2x00lib_config(). + */ + rt2x00queue_stop_queue(rt2x00dev->rx); + + /* + * When we've just turned on the radio, we want to reprogram + * everything to ensure a consistent state + */ + rt2x00lib_config(rt2x00dev, conf, changed); + + /* + * After the radio has been enabled we need to configure + * the antenna to the default settings. rt2x00lib_config_antenna() + * should determine if any action should be taken based on + * checking if diversity has been enabled or no antenna changes + * have been made since the last configuration change. + */ + rt2x00lib_config_antenna(rt2x00dev, rt2x00dev->default_ant); + + /* Turn RX back on */ + rt2x00queue_start_queue(rt2x00dev->rx); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_config); + +void rt2x00mac_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Mask off any flags we are going to ignore + * from the total_flags field. + */ + *total_flags &= + FIF_ALLMULTI | + FIF_FCSFAIL | + FIF_PLCPFAIL | + FIF_CONTROL | + FIF_PSPOLL | + FIF_OTHER_BSS; + + /* + * Apply some rules to the filters: + * - Some filters imply different filters to be set. + * - Some things we can't filter out at all. + * - Multicast filter seems to kill broadcast traffic so never use it. + */ + *total_flags |= FIF_ALLMULTI; + + /* + * If the device has a single filter for all control frames, + * FIF_CONTROL and FIF_PSPOLL flags imply each other. + * And if the device has more than one filter for control frames + * of different types, but has no a separate filter for PS Poll frames, + * FIF_CONTROL flag implies FIF_PSPOLL. + */ + if (!rt2x00_has_cap_control_filters(rt2x00dev)) { + if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL) + *total_flags |= FIF_CONTROL | FIF_PSPOLL; + } + if (!rt2x00_has_cap_control_filter_pspoll(rt2x00dev)) { + if (*total_flags & FIF_CONTROL) + *total_flags |= FIF_PSPOLL; + } + + /* + * Check if there is any work left for us. + */ + if (rt2x00dev->packet_filter == *total_flags) + return; + rt2x00dev->packet_filter = *total_flags; + + rt2x00dev->ops->lib->config_filter(rt2x00dev, *total_flags); +} +EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter); + +static void rt2x00mac_set_tim_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct rt2x00_intf *intf = vif_to_intf(vif); + + if (vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC && + vif->type != NL80211_IFTYPE_MESH_POINT && + vif->type != NL80211_IFTYPE_WDS) + return; + + set_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags); +} + +int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + bool set) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return 0; + + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00mac_set_tim_iter, rt2x00dev); + + /* queue work to upodate the beacon template */ + ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_set_tim); + +#ifdef CONFIG_RT2X00_LIB_CRYPTO +static void memcpy_tkip(struct rt2x00lib_crypto *crypto, u8 *key, u8 key_len) +{ + if (key_len > NL80211_TKIP_DATA_OFFSET_ENCR_KEY) + memcpy(crypto->key, + &key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY], + sizeof(crypto->key)); + + if (key_len > NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY) + memcpy(crypto->tx_mic, + &key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], + sizeof(crypto->tx_mic)); + + if (key_len > NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY) + memcpy(crypto->rx_mic, + &key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], + sizeof(crypto->rx_mic)); +} + +int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + int (*set_key) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); + struct rt2x00lib_crypto crypto; + static const u8 bcast_addr[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; + struct rt2x00_sta *sta_priv = NULL; + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return 0; + + if (!rt2x00_has_cap_hw_crypto(rt2x00dev)) + return -EOPNOTSUPP; + + /* + * To support IBSS RSN, don't program group keys in IBSS, the + * hardware will then not attempt to decrypt the frames. + */ + if (vif->type == NL80211_IFTYPE_ADHOC && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + if (key->keylen > 32) + return -ENOSPC; + + memset(&crypto, 0, sizeof(crypto)); + + crypto.bssidx = rt2x00lib_get_bssidx(rt2x00dev, vif); + crypto.cipher = rt2x00crypto_key_to_cipher(key); + if (crypto.cipher == CIPHER_NONE) + return -EOPNOTSUPP; + if (crypto.cipher == CIPHER_TKIP && rt2x00_is_usb(rt2x00dev)) + return -EOPNOTSUPP; + + crypto.cmd = cmd; + + if (sta) { + crypto.address = sta->addr; + sta_priv = sta_to_rt2x00_sta(sta); + crypto.wcid = sta_priv->wcid; + } else + crypto.address = bcast_addr; + + if (crypto.cipher == CIPHER_TKIP) + memcpy_tkip(&crypto, &key->key[0], key->keylen); + else + memcpy(crypto.key, &key->key[0], key->keylen); + /* + * Each BSS has a maximum of 4 shared keys. + * Shared key index values: + * 0) BSS0 key0 + * 1) BSS0 key1 + * ... + * 4) BSS1 key0 + * ... + * 8) BSS2 key0 + * ... + * Both pairwise as shared key indeces are determined by + * driver. This is required because the hardware requires + * keys to be assigned in correct order (When key 1 is + * provided but key 0 is not, then the key is not found + * by the hardware during RX). + */ + if (cmd == SET_KEY) + key->hw_key_idx = 0; + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + set_key = rt2x00dev->ops->lib->config_pairwise_key; + else + set_key = rt2x00dev->ops->lib->config_shared_key; + + if (!set_key) + return -EOPNOTSUPP; + + return set_key(rt2x00dev, &crypto, key); +} +EXPORT_SYMBOL_GPL(rt2x00mac_set_key); +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ + +int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + return rt2x00dev->ops->lib->sta_add(rt2x00dev, vif, sta); +} +EXPORT_SYMBOL_GPL(rt2x00mac_sta_add); + +int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta); + + return rt2x00dev->ops->lib->sta_remove(rt2x00dev, sta_priv->wcid); +} +EXPORT_SYMBOL_GPL(rt2x00mac_sta_remove); + +void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + set_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags); + rt2x00link_stop_tuner(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_start); + +void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + clear_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags); + rt2x00link_start_tuner(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_complete); + +int rt2x00mac_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * The dot11ACKFailureCount, dot11RTSFailureCount and + * dot11RTSSuccessCount are updated in interrupt time. + * dot11FCSErrorCount is updated in the link tuner. + */ + memcpy(stats, &rt2x00dev->low_level_stats, sizeof(*stats)); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_get_stats); + +void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct rt2x00_intf *intf = vif_to_intf(vif); + + /* + * mac80211 might be calling this function while we are trying + * to remove the device or perhaps suspending it. + */ + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return; + + /* + * Update the BSSID. + */ + if (changes & BSS_CHANGED_BSSID) + rt2x00lib_config_intf(rt2x00dev, intf, vif->type, NULL, + bss_conf->bssid); + + /* + * Start/stop beaconing. + */ + if (changes & BSS_CHANGED_BEACON_ENABLED) { + mutex_lock(&intf->beacon_skb_mutex); + if (!bss_conf->enable_beacon && intf->enable_beacon) { + rt2x00dev->intf_beaconing--; + intf->enable_beacon = false; + + if (rt2x00dev->intf_beaconing == 0) { + /* + * Last beaconing interface disabled + * -> stop beacon queue. + */ + rt2x00queue_stop_queue(rt2x00dev->bcn); + } + /* + * Clear beacon in the H/W for this vif. This is needed + * to disable beaconing on this particular interface + * and keep it running on other interfaces. + */ + rt2x00queue_clear_beacon(rt2x00dev, vif); + } else if (bss_conf->enable_beacon && !intf->enable_beacon) { + rt2x00dev->intf_beaconing++; + intf->enable_beacon = true; + /* + * Upload beacon to the H/W. This is only required on + * USB devices. PCI devices fetch beacons periodically. + */ + if (rt2x00_is_usb(rt2x00dev)) + rt2x00queue_update_beacon(rt2x00dev, vif); + + if (rt2x00dev->intf_beaconing == 1) { + /* + * First beaconing interface enabled + * -> start beacon queue. + */ + rt2x00queue_start_queue(rt2x00dev->bcn); + } + } + mutex_unlock(&intf->beacon_skb_mutex); + } + + /* + * When the association status has changed we must reset the link + * tuner counter. This is because some drivers determine if they + * should perform link tuning based on the number of seconds + * while associated or not associated. + */ + if (changes & BSS_CHANGED_ASSOC) { + rt2x00dev->link.count = 0; + + if (bss_conf->assoc) + rt2x00dev->intf_associated++; + else + rt2x00dev->intf_associated--; + + rt2x00leds_led_assoc(rt2x00dev, !!rt2x00dev->intf_associated); + + clear_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags); + } + + /* + * Check for access point which do not support 802.11e . We have to + * generate data frames sequence number in S/W for such AP, because + * of H/W bug. + */ + if (changes & BSS_CHANGED_QOS && !bss_conf->qos) + set_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags); + + /* + * When the erp information has changed, we should perform + * additional configuration steps. For all other changes we are done. + */ + if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE | + BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_BEACON_INT | BSS_CHANGED_HT)) + rt2x00lib_config_erp(rt2x00dev, intf, bss_conf, changes); +} +EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed); + +int rt2x00mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, + const struct ieee80211_tx_queue_params *params) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + + queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); + if (unlikely(!queue)) + return -EINVAL; + + /* + * The passed variables are stored as real value ((2^n)-1). + * Ralink registers require to know the bit number 'n'. + */ + if (params->cw_min > 0) + queue->cw_min = fls(params->cw_min); + else + queue->cw_min = 5; /* cw_min: 2^5 = 32. */ + + if (params->cw_max > 0) + queue->cw_max = fls(params->cw_max); + else + queue->cw_max = 10; /* cw_min: 2^10 = 1024. */ + + queue->aifs = params->aifs; + queue->txop = params->txop; + + rt2x00_dbg(rt2x00dev, + "Configured TX queue %d - CWmin: %d, CWmax: %d, Aifs: %d, TXop: %d\n", + queue_idx, queue->cw_min, queue->cw_max, queue->aifs, + queue->txop); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_conf_tx); + +void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + bool active = !!rt2x00dev->ops->lib->rfkill_poll(rt2x00dev); + + wiphy_rfkill_set_hw_state(hw->wiphy, !active); +} +EXPORT_SYMBOL_GPL(rt2x00mac_rfkill_poll); + +void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return; + + tx_queue_for_each(rt2x00dev, queue) + rt2x00queue_flush_queue(queue, drop); +} +EXPORT_SYMBOL_GPL(rt2x00mac_flush); + +int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct link_ant *ant = &rt2x00dev->link.ant; + struct antenna_setup *def = &rt2x00dev->default_ant; + struct antenna_setup setup; + + // The antenna value is not supposed to be 0, + // or exceed the maximum number of antenna's. + if (!tx_ant || (tx_ant & ~3) || !rx_ant || (rx_ant & ~3)) + return -EINVAL; + + // When the client tried to configure the antenna to or from + // diversity mode, we must reset the default antenna as well + // as that controls the diversity switch. + if (ant->flags & ANTENNA_TX_DIVERSITY && tx_ant != 3) + ant->flags &= ~ANTENNA_TX_DIVERSITY; + if (ant->flags & ANTENNA_RX_DIVERSITY && rx_ant != 3) + ant->flags &= ~ANTENNA_RX_DIVERSITY; + + // If diversity is being enabled, check if we need hardware + // or software diversity. In the latter case, reset the value, + // and make sure we update the antenna flags to have the + // link tuner pick up the diversity tuning. + if (tx_ant == 3 && def->tx == ANTENNA_SW_DIVERSITY) { + tx_ant = ANTENNA_SW_DIVERSITY; + ant->flags |= ANTENNA_TX_DIVERSITY; + } + + if (rx_ant == 3 && def->rx == ANTENNA_SW_DIVERSITY) { + rx_ant = ANTENNA_SW_DIVERSITY; + ant->flags |= ANTENNA_RX_DIVERSITY; + } + + setup.tx = tx_ant; + setup.rx = rx_ant; + setup.rx_chain_num = 0; + setup.tx_chain_num = 0; + + rt2x00lib_config_antenna(rt2x00dev, setup); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_set_antenna); + +int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct link_ant *ant = &rt2x00dev->link.ant; + struct antenna_setup *active = &rt2x00dev->link.ant.active; + + // When software diversity is active, we must report this to the + // client and not the current active antenna state. + if (ant->flags & ANTENNA_TX_DIVERSITY) + *tx_ant = ANTENNA_HW_DIVERSITY; + else + *tx_ant = active->tx; + + if (ant->flags & ANTENNA_RX_DIVERSITY) + *rx_ant = ANTENNA_HW_DIVERSITY; + else + *rx_ant = active->rx; + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_get_antenna); + +void rt2x00mac_get_ringparam(struct ieee80211_hw *hw, + u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + + tx_queue_for_each(rt2x00dev, queue) { + *tx += queue->length; + *tx_max += queue->limit; + } + + *rx = rt2x00dev->rx->length; + *rx_max = rt2x00dev->rx->limit; +} +EXPORT_SYMBOL_GPL(rt2x00mac_get_ringparam); + +bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + + tx_queue_for_each(rt2x00dev, queue) { + if (!rt2x00queue_empty(queue)) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(rt2x00mac_tx_frames_pending); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c new file mode 100644 index 000000000000..f0178fd4fe5f --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c @@ -0,0 +1,212 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00mmio + Abstract: rt2x00 generic mmio device routines. + */ + +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00mmio.h" + +/* + * Register access. + */ +int rt2x00mmio_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, + u32 *reg) +{ + unsigned int i; + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return 0; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00mmio_register_read(rt2x00dev, offset, reg); + if (!rt2x00_get_field32(*reg, field)) + return 1; + udelay(REGISTER_BUSY_DELAY); + } + + printk_once(KERN_ERR "%s() Indirect register access failed: " + "offset=0x%.08x, value=0x%.08x\n", __func__, offset, *reg); + *reg = ~0; + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mmio_regbusy_read); + +bool rt2x00mmio_rxdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue = rt2x00dev->rx; + struct queue_entry *entry; + struct queue_entry_priv_mmio *entry_priv; + struct skb_frame_desc *skbdesc; + int max_rx = 16; + + while (--max_rx) { + entry = rt2x00queue_get_entry(queue, Q_INDEX); + entry_priv = entry->priv_data; + + if (rt2x00dev->ops->lib->get_entry_state(entry)) + break; + + /* + * Fill in desc fields of the skb descriptor + */ + skbdesc = get_skb_frame_desc(entry->skb); + skbdesc->desc = entry_priv->desc; + skbdesc->desc_len = entry->queue->desc_size; + + /* + * DMA is already done, notify rt2x00lib that + * it finished successfully. + */ + rt2x00lib_dmastart(entry); + rt2x00lib_dmadone(entry); + + /* + * Send the frame to rt2x00lib for further processing. + */ + rt2x00lib_rxdone(entry, GFP_ATOMIC); + } + + return !max_rx; +} +EXPORT_SYMBOL_GPL(rt2x00mmio_rxdone); + +void rt2x00mmio_flush_queue(struct data_queue *queue, bool drop) +{ + unsigned int i; + + for (i = 0; !rt2x00queue_empty(queue) && i < 10; i++) + msleep(10); +} +EXPORT_SYMBOL_GPL(rt2x00mmio_flush_queue); + +/* + * Device initialization handlers. + */ +static int rt2x00mmio_alloc_queue_dma(struct rt2x00_dev *rt2x00dev, + struct data_queue *queue) +{ + struct queue_entry_priv_mmio *entry_priv; + void *addr; + dma_addr_t dma; + unsigned int i; + + /* + * Allocate DMA memory for descriptor and buffer. + */ + addr = dma_zalloc_coherent(rt2x00dev->dev, + queue->limit * queue->desc_size, &dma, + GFP_KERNEL); + if (!addr) + return -ENOMEM; + + /* + * Initialize all queue entries to contain valid addresses. + */ + for (i = 0; i < queue->limit; i++) { + entry_priv = queue->entries[i].priv_data; + entry_priv->desc = addr + i * queue->desc_size; + entry_priv->desc_dma = dma + i * queue->desc_size; + } + + return 0; +} + +static void rt2x00mmio_free_queue_dma(struct rt2x00_dev *rt2x00dev, + struct data_queue *queue) +{ + struct queue_entry_priv_mmio *entry_priv = + queue->entries[0].priv_data; + + if (entry_priv->desc) + dma_free_coherent(rt2x00dev->dev, + queue->limit * queue->desc_size, + entry_priv->desc, entry_priv->desc_dma); + entry_priv->desc = NULL; +} + +int rt2x00mmio_initialize(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + int status; + + /* + * Allocate DMA + */ + queue_for_each(rt2x00dev, queue) { + status = rt2x00mmio_alloc_queue_dma(rt2x00dev, queue); + if (status) + goto exit; + } + + /* + * Register interrupt handler. + */ + status = request_irq(rt2x00dev->irq, + rt2x00dev->ops->lib->irq_handler, + IRQF_SHARED, rt2x00dev->name, rt2x00dev); + if (status) { + rt2x00_err(rt2x00dev, "IRQ %d allocation failed (error %d)\n", + rt2x00dev->irq, status); + goto exit; + } + + return 0; + +exit: + queue_for_each(rt2x00dev, queue) + rt2x00mmio_free_queue_dma(rt2x00dev, queue); + + return status; +} +EXPORT_SYMBOL_GPL(rt2x00mmio_initialize); + +void rt2x00mmio_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + /* + * Free irq line. + */ + free_irq(rt2x00dev->irq, rt2x00dev); + + /* + * Free DMA + */ + queue_for_each(rt2x00dev, queue) + rt2x00mmio_free_queue_dma(rt2x00dev, queue); +} +EXPORT_SYMBOL_GPL(rt2x00mmio_uninitialize); + +/* + * rt2x00mmio module information. + */ +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2x00 mmio library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.h b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.h new file mode 100644 index 000000000000..701c3127efb9 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.h @@ -0,0 +1,117 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00mmio + Abstract: Data structures for the rt2x00mmio module. + */ + +#ifndef RT2X00MMIO_H +#define RT2X00MMIO_H + +#include + +/* + * Register access. + */ +static inline void rt2x00mmio_register_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + *value = readl(rt2x00dev->csr.base + offset); +} + +static inline void rt2x00mmio_register_multiread(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u32 length) +{ + memcpy_fromio(value, rt2x00dev->csr.base + offset, length); +} + +static inline void rt2x00mmio_register_write(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + writel(value, rt2x00dev->csr.base + offset); +} + +static inline void rt2x00mmio_register_multiwrite(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const void *value, + const u32 length) +{ + __iowrite32_copy(rt2x00dev->csr.base + offset, value, length >> 2); +} + +/** + * rt2x00mmio_regbusy_read - Read from register with busy check + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @field: Field to check if register is busy + * @reg: Pointer to where register contents should be stored + * + * This function will read the given register, and checks if the + * register is busy. If it is, it will sleep for a couple of + * microseconds before reading the register again. If the register + * is not read after a certain timeout, this function will return + * FALSE. + */ +int rt2x00mmio_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, + u32 *reg); + +/** + * struct queue_entry_priv_mmio: Per entry PCI specific information + * + * @desc: Pointer to device descriptor + * @desc_dma: DMA pointer to &desc. + * @data: Pointer to device's entry memory. + * @data_dma: DMA pointer to &data. + */ +struct queue_entry_priv_mmio { + __le32 *desc; + dma_addr_t desc_dma; +}; + +/** + * rt2x00mmio_rxdone - Handle RX done events + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * + * Returns true if there are still rx frames pending and false if all + * pending rx frames were processed. + */ +bool rt2x00mmio_rxdone(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00mmio_flush_queue - Flush data queue + * @queue: Data queue to stop + * @drop: True to drop all pending frames. + * + * This will wait for a maximum of 100ms, waiting for the queues + * to become empty. + */ +void rt2x00mmio_flush_queue(struct data_queue *queue, bool drop); + +/* + * Device initialization handlers. + */ +int rt2x00mmio_initialize(struct rt2x00_dev *rt2x00dev); +void rt2x00mmio_uninitialize(struct rt2x00_dev *rt2x00dev); + +#endif /* RT2X00MMIO_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c new file mode 100644 index 000000000000..d93db4b0371b --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c @@ -0,0 +1,221 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00pci + Abstract: rt2x00 generic pci device routines. + */ + +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00pci.h" + +/* + * PCI driver handlers. + */ +static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev) +{ + kfree(rt2x00dev->rf); + rt2x00dev->rf = NULL; + + kfree(rt2x00dev->eeprom); + rt2x00dev->eeprom = NULL; + + if (rt2x00dev->csr.base) { + iounmap(rt2x00dev->csr.base); + rt2x00dev->csr.base = NULL; + } +} + +static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev) +{ + struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev); + + rt2x00dev->csr.base = pci_ioremap_bar(pci_dev, 0); + if (!rt2x00dev->csr.base) + goto exit; + + rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); + if (!rt2x00dev->eeprom) + goto exit; + + rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); + if (!rt2x00dev->rf) + goto exit; + + return 0; + +exit: + rt2x00_probe_err("Failed to allocate registers\n"); + + rt2x00pci_free_reg(rt2x00dev); + + return -ENOMEM; +} + +int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) +{ + struct ieee80211_hw *hw; + struct rt2x00_dev *rt2x00dev; + int retval; + u16 chip; + + retval = pci_enable_device(pci_dev); + if (retval) { + rt2x00_probe_err("Enable device failed\n"); + return retval; + } + + retval = pci_request_regions(pci_dev, pci_name(pci_dev)); + if (retval) { + rt2x00_probe_err("PCI request regions failed\n"); + goto exit_disable_device; + } + + pci_set_master(pci_dev); + + if (pci_set_mwi(pci_dev)) + rt2x00_probe_err("MWI not available\n"); + + if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) { + rt2x00_probe_err("PCI DMA not supported\n"); + retval = -EIO; + goto exit_release_regions; + } + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); + if (!hw) { + rt2x00_probe_err("Failed to allocate hardware\n"); + retval = -ENOMEM; + goto exit_release_regions; + } + + pci_set_drvdata(pci_dev, hw); + + rt2x00dev = hw->priv; + rt2x00dev->dev = &pci_dev->dev; + rt2x00dev->ops = ops; + rt2x00dev->hw = hw; + rt2x00dev->irq = pci_dev->irq; + rt2x00dev->name = ops->name; + + if (pci_is_pcie(pci_dev)) + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); + else + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); + + retval = rt2x00pci_alloc_reg(rt2x00dev); + if (retval) + goto exit_free_device; + + /* + * Because rt3290 chip use different efuse offset to read efuse data. + * So before read efuse it need to indicate it is the + * rt3290 or not. + */ + pci_read_config_word(pci_dev, PCI_DEVICE_ID, &chip); + rt2x00dev->chip.rt = chip; + + retval = rt2x00lib_probe_dev(rt2x00dev); + if (retval) + goto exit_free_reg; + + return 0; + +exit_free_reg: + rt2x00pci_free_reg(rt2x00dev); + +exit_free_device: + ieee80211_free_hw(hw); + +exit_release_regions: + pci_release_regions(pci_dev); + +exit_disable_device: + pci_disable_device(pci_dev); + + return retval; +} +EXPORT_SYMBOL_GPL(rt2x00pci_probe); + +void rt2x00pci_remove(struct pci_dev *pci_dev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Free all allocated data. + */ + rt2x00lib_remove_dev(rt2x00dev); + rt2x00pci_free_reg(rt2x00dev); + ieee80211_free_hw(hw); + + /* + * Free the PCI device data. + */ + pci_disable_device(pci_dev); + pci_release_regions(pci_dev); +} +EXPORT_SYMBOL_GPL(rt2x00pci_remove); + +#ifdef CONFIG_PM +int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + int retval; + + retval = rt2x00lib_suspend(rt2x00dev, state); + if (retval) + return retval; + + pci_save_state(pci_dev); + pci_disable_device(pci_dev); + return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); +} +EXPORT_SYMBOL_GPL(rt2x00pci_suspend); + +int rt2x00pci_resume(struct pci_dev *pci_dev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + if (pci_set_power_state(pci_dev, PCI_D0) || + pci_enable_device(pci_dev)) { + rt2x00_err(rt2x00dev, "Failed to resume device\n"); + return -EIO; + } + + pci_restore_state(pci_dev); + return rt2x00lib_resume(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00pci_resume); +#endif /* CONFIG_PM */ + +/* + * rt2x00pci module information. + */ +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2x00 pci library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00pci.h b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.h new file mode 100644 index 000000000000..bc0ca5f58f38 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.h @@ -0,0 +1,49 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00pci + Abstract: Data structures for the rt2x00pci module. + */ + +#ifndef RT2X00PCI_H +#define RT2X00PCI_H + +#include +#include + +/* + * This variable should be used with the + * pci_driver structure initialization. + */ +#define PCI_DEVICE_DATA(__ops) .driver_data = (kernel_ulong_t)(__ops) + +/* + * PCI driver handlers. + */ +int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops); +void rt2x00pci_remove(struct pci_dev *pci_dev); +#ifdef CONFIG_PM +int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state); +int rt2x00pci_resume(struct pci_dev *pci_dev); +#else +#define rt2x00pci_suspend NULL +#define rt2x00pci_resume NULL +#endif /* CONFIG_PM */ + +#endif /* RT2X00PCI_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c new file mode 100644 index 000000000000..68b620b2462f --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c @@ -0,0 +1,1290 @@ +/* + Copyright (C) 2010 Willow Garage + Copyright (C) 2004 - 2010 Ivo van Doorn + Copyright (C) 2004 - 2009 Gertjan van Wingerde + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 queue specific routines. + */ + +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" + +struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp) +{ + struct data_queue *queue = entry->queue; + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + struct sk_buff *skb; + struct skb_frame_desc *skbdesc; + unsigned int frame_size; + unsigned int head_size = 0; + unsigned int tail_size = 0; + + /* + * The frame size includes descriptor size, because the + * hardware directly receive the frame into the skbuffer. + */ + frame_size = queue->data_size + queue->desc_size + queue->winfo_size; + + /* + * The payload should be aligned to a 4-byte boundary, + * this means we need at least 3 bytes for moving the frame + * into the correct offset. + */ + head_size = 4; + + /* + * For IV/EIV/ICV assembly we must make sure there is + * at least 8 bytes bytes available in headroom for IV/EIV + * and 8 bytes for ICV data as tailroon. + */ + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) { + head_size += 8; + tail_size += 8; + } + + /* + * Allocate skbuffer. + */ + skb = __dev_alloc_skb(frame_size + head_size + tail_size, gfp); + if (!skb) + return NULL; + + /* + * Make sure we not have a frame with the requested bytes + * available in the head and tail. + */ + skb_reserve(skb, head_size); + skb_put(skb, frame_size); + + /* + * Populate skbdesc. + */ + skbdesc = get_skb_frame_desc(skb); + memset(skbdesc, 0, sizeof(*skbdesc)); + skbdesc->entry = entry; + + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA)) { + dma_addr_t skb_dma; + + skb_dma = dma_map_single(rt2x00dev->dev, skb->data, skb->len, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(rt2x00dev->dev, skb_dma))) { + dev_kfree_skb_any(skb); + return NULL; + } + + skbdesc->skb_dma = skb_dma; + skbdesc->flags |= SKBDESC_DMA_MAPPED_RX; + } + + return skb; +} + +int rt2x00queue_map_txskb(struct queue_entry *entry) +{ + struct device *dev = entry->queue->rt2x00dev->dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + + skbdesc->skb_dma = + dma_map_single(dev, entry->skb->data, entry->skb->len, DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(dev, skbdesc->skb_dma))) + return -ENOMEM; + + skbdesc->flags |= SKBDESC_DMA_MAPPED_TX; + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00queue_map_txskb); + +void rt2x00queue_unmap_skb(struct queue_entry *entry) +{ + struct device *dev = entry->queue->rt2x00dev->dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + + if (skbdesc->flags & SKBDESC_DMA_MAPPED_RX) { + dma_unmap_single(dev, skbdesc->skb_dma, entry->skb->len, + DMA_FROM_DEVICE); + skbdesc->flags &= ~SKBDESC_DMA_MAPPED_RX; + } else if (skbdesc->flags & SKBDESC_DMA_MAPPED_TX) { + dma_unmap_single(dev, skbdesc->skb_dma, entry->skb->len, + DMA_TO_DEVICE); + skbdesc->flags &= ~SKBDESC_DMA_MAPPED_TX; + } +} +EXPORT_SYMBOL_GPL(rt2x00queue_unmap_skb); + +void rt2x00queue_free_skb(struct queue_entry *entry) +{ + if (!entry->skb) + return; + + rt2x00queue_unmap_skb(entry); + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; +} + +void rt2x00queue_align_frame(struct sk_buff *skb) +{ + unsigned int frame_length = skb->len; + unsigned int align = ALIGN_SIZE(skb, 0); + + if (!align) + return; + + skb_push(skb, align); + memmove(skb->data, skb->data + align, frame_length); + skb_trim(skb, frame_length); +} + +/* + * H/W needs L2 padding between the header and the paylod if header size + * is not 4 bytes aligned. + */ +void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int hdr_len) +{ + unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0; + + if (!l2pad) + return; + + skb_push(skb, l2pad); + memmove(skb->data, skb->data + l2pad, hdr_len); +} + +void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int hdr_len) +{ + unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0; + + if (!l2pad) + return; + + memmove(skb->data + l2pad, skb->data, hdr_len); + skb_pull(skb, l2pad); +} + +static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif); + u16 seqno; + + if (!(tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) + return; + + __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); + + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_SW_SEQNO)) { + /* + * rt2800 has a H/W (or F/W) bug, device incorrectly increase + * seqno on retransmited data (non-QOS) frames. To workaround + * the problem let's generate seqno in software if QOS is + * disabled. + */ + if (test_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags)) + __clear_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); + else + /* H/W will generate sequence number */ + return; + } + + /* + * The hardware is not able to insert a sequence number. Assign a + * software generated one here. + * + * This is wrong because beacons are not getting sequence + * numbers assigned properly. + * + * A secondary problem exists for drivers that cannot toggle + * sequence counting per-frame, since those will override the + * sequence counter given by mac80211. + */ + if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) + seqno = atomic_add_return(0x10, &intf->seqno); + else + seqno = atomic_read(&intf->seqno); + + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(seqno); +} + +static void rt2x00queue_create_tx_descriptor_plcp(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc, + const struct rt2x00_rate *hwrate) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; + unsigned int data_length; + unsigned int duration; + unsigned int residual; + + /* + * Determine with what IFS priority this frame should be send. + * Set ifs to IFS_SIFS when the this is not the first fragment, + * or this fragment came after RTS/CTS. + */ + if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) + txdesc->u.plcp.ifs = IFS_BACKOFF; + else + txdesc->u.plcp.ifs = IFS_SIFS; + + /* Data length + CRC + Crypto overhead (IV/EIV/ICV/MIC) */ + data_length = skb->len + 4; + data_length += rt2x00crypto_tx_overhead(rt2x00dev, skb); + + /* + * PLCP setup + * Length calculation depends on OFDM/CCK rate. + */ + txdesc->u.plcp.signal = hwrate->plcp; + txdesc->u.plcp.service = 0x04; + + if (hwrate->flags & DEV_RATE_OFDM) { + txdesc->u.plcp.length_high = (data_length >> 6) & 0x3f; + txdesc->u.plcp.length_low = data_length & 0x3f; + } else { + /* + * Convert length to microseconds. + */ + residual = GET_DURATION_RES(data_length, hwrate->bitrate); + duration = GET_DURATION(data_length, hwrate->bitrate); + + if (residual != 0) { + duration++; + + /* + * Check if we need to set the Length Extension + */ + if (hwrate->bitrate == 110 && residual <= 30) + txdesc->u.plcp.service |= 0x80; + } + + txdesc->u.plcp.length_high = (duration >> 8) & 0xff; + txdesc->u.plcp.length_low = duration & 0xff; + + /* + * When preamble is enabled we should set the + * preamble bit for the signal. + */ + if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + txdesc->u.plcp.signal |= 0x08; + } +} + +static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc, + struct ieee80211_sta *sta, + const struct rt2x00_rate *hwrate) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct rt2x00_sta *sta_priv = NULL; + + if (sta) { + txdesc->u.ht.mpdu_density = + sta->ht_cap.ampdu_density; + + sta_priv = sta_to_rt2x00_sta(sta); + txdesc->u.ht.wcid = sta_priv->wcid; + } + + /* + * If IEEE80211_TX_RC_MCS is set txrate->idx just contains the + * mcs rate to be used + */ + if (txrate->flags & IEEE80211_TX_RC_MCS) { + txdesc->u.ht.mcs = txrate->idx; + + /* + * MIMO PS should be set to 1 for STA's using dynamic SM PS + * when using more then one tx stream (>MCS7). + */ + if (sta && txdesc->u.ht.mcs > 7 && + sta->smps_mode == IEEE80211_SMPS_DYNAMIC) + __set_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags); + } else { + txdesc->u.ht.mcs = rt2x00_get_rate_mcs(hwrate->mcs); + if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + txdesc->u.ht.mcs |= 0x08; + } + + if (test_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags)) { + if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)) + txdesc->u.ht.txop = TXOP_SIFS; + else + txdesc->u.ht.txop = TXOP_BACKOFF; + + /* Left zero on all other settings. */ + return; + } + + txdesc->u.ht.ba_size = 7; /* FIXME: What value is needed? */ + + /* + * Only one STBC stream is supported for now. + */ + if (tx_info->flags & IEEE80211_TX_CTL_STBC) + txdesc->u.ht.stbc = 1; + + /* + * This frame is eligible for an AMPDU, however, don't aggregate + * frames that are intended to probe a specific tx rate. + */ + if (tx_info->flags & IEEE80211_TX_CTL_AMPDU && + !(tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) + __set_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags); + + /* + * Set 40Mhz mode if necessary (for legacy rates this will + * duplicate the frame to both channels). + */ + if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH || + txrate->flags & IEEE80211_TX_RC_DUP_DATA) + __set_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags); + if (txrate->flags & IEEE80211_TX_RC_SHORT_GI) + __set_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags); + + /* + * Determine IFS values + * - Use TXOP_BACKOFF for management frames except beacons + * - Use TXOP_SIFS for fragment bursts + * - Use TXOP_HTTXOP for everything else + * + * Note: rt2800 devices won't use CTS protection (if used) + * for frames not transmitted with TXOP_HTTXOP + */ + if (ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_is_beacon(hdr->frame_control)) + txdesc->u.ht.txop = TXOP_BACKOFF; + else if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)) + txdesc->u.ht.txop = TXOP_SIFS; + else + txdesc->u.ht.txop = TXOP_HTTXOP; +} + +static void rt2x00queue_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc, + struct ieee80211_sta *sta) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; + struct ieee80211_rate *rate; + const struct rt2x00_rate *hwrate = NULL; + + memset(txdesc, 0, sizeof(*txdesc)); + + /* + * Header and frame information. + */ + txdesc->length = skb->len; + txdesc->header_length = ieee80211_get_hdrlen_from_skb(skb); + + /* + * Check whether this frame is to be acked. + */ + if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) + __set_bit(ENTRY_TXD_ACK, &txdesc->flags); + + /* + * Check if this is a RTS/CTS frame + */ + if (ieee80211_is_rts(hdr->frame_control) || + ieee80211_is_cts(hdr->frame_control)) { + __set_bit(ENTRY_TXD_BURST, &txdesc->flags); + if (ieee80211_is_rts(hdr->frame_control)) + __set_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags); + else + __set_bit(ENTRY_TXD_CTS_FRAME, &txdesc->flags); + if (tx_info->control.rts_cts_rate_idx >= 0) + rate = + ieee80211_get_rts_cts_rate(rt2x00dev->hw, tx_info); + } + + /* + * Determine retry information. + */ + txdesc->retry_limit = tx_info->control.rates[0].count - 1; + if (txdesc->retry_limit >= rt2x00dev->long_retry) + __set_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags); + + /* + * Check if more fragments are pending + */ + if (ieee80211_has_morefrags(hdr->frame_control)) { + __set_bit(ENTRY_TXD_BURST, &txdesc->flags); + __set_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags); + } + + /* + * Check if more frames (!= fragments) are pending + */ + if (tx_info->flags & IEEE80211_TX_CTL_MORE_FRAMES) + __set_bit(ENTRY_TXD_BURST, &txdesc->flags); + + /* + * Beacons and probe responses require the tsf timestamp + * to be inserted into the frame. + */ + if (ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control)) + __set_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags); + + if ((tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) && + !test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)) + __set_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags); + + /* + * Determine rate modulation. + */ + if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD) + txdesc->rate_mode = RATE_MODE_HT_GREENFIELD; + else if (txrate->flags & IEEE80211_TX_RC_MCS) + txdesc->rate_mode = RATE_MODE_HT_MIX; + else { + rate = ieee80211_get_tx_rate(rt2x00dev->hw, tx_info); + hwrate = rt2x00_get_rate(rate->hw_value); + if (hwrate->flags & DEV_RATE_OFDM) + txdesc->rate_mode = RATE_MODE_OFDM; + else + txdesc->rate_mode = RATE_MODE_CCK; + } + + /* + * Apply TX descriptor handling by components + */ + rt2x00crypto_create_tx_descriptor(rt2x00dev, skb, txdesc); + rt2x00queue_create_tx_descriptor_seq(rt2x00dev, skb, txdesc); + + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_HT_TX_DESC)) + rt2x00queue_create_tx_descriptor_ht(rt2x00dev, skb, txdesc, + sta, hwrate); + else + rt2x00queue_create_tx_descriptor_plcp(rt2x00dev, skb, txdesc, + hwrate); +} + +static int rt2x00queue_write_tx_data(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + + /* + * This should not happen, we already checked the entry + * was ours. When the hardware disagrees there has been + * a queue corruption! + */ + if (unlikely(rt2x00dev->ops->lib->get_entry_state && + rt2x00dev->ops->lib->get_entry_state(entry))) { + rt2x00_err(rt2x00dev, + "Corrupt queue %d, accessing entry which is not ours\n" + "Please file bug report to %s\n", + entry->queue->qid, DRV_PROJECT); + return -EINVAL; + } + + /* + * Add the requested extra tx headroom in front of the skb. + */ + skb_push(entry->skb, rt2x00dev->extra_tx_headroom); + memset(entry->skb->data, 0, rt2x00dev->extra_tx_headroom); + + /* + * Call the driver's write_tx_data function, if it exists. + */ + if (rt2x00dev->ops->lib->write_tx_data) + rt2x00dev->ops->lib->write_tx_data(entry, txdesc); + + /* + * Map the skb to DMA. + */ + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA) && + rt2x00queue_map_txskb(entry)) + return -ENOMEM; + + return 0; +} + +static void rt2x00queue_write_tx_descriptor(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct data_queue *queue = entry->queue; + + queue->rt2x00dev->ops->lib->write_tx_desc(entry, txdesc); + + /* + * All processing on the frame has been completed, this means + * it is now ready to be dumped to userspace through debugfs. + */ + rt2x00debug_dump_frame(queue->rt2x00dev, DUMP_FRAME_TX, entry->skb); +} + +static void rt2x00queue_kick_tx_queue(struct data_queue *queue, + struct txentry_desc *txdesc) +{ + /* + * Check if we need to kick the queue, there are however a few rules + * 1) Don't kick unless this is the last in frame in a burst. + * When the burst flag is set, this frame is always followed + * by another frame which in some way are related to eachother. + * This is true for fragments, RTS or CTS-to-self frames. + * 2) Rule 1 can be broken when the available entries + * in the queue are less then a certain threshold. + */ + if (rt2x00queue_threshold(queue) || + !test_bit(ENTRY_TXD_BURST, &txdesc->flags)) + queue->rt2x00dev->ops->lib->kick_queue(queue); +} + +static void rt2x00queue_bar_check(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct ieee80211_bar *bar = (void *) (entry->skb->data + + rt2x00dev->extra_tx_headroom); + struct rt2x00_bar_list_entry *bar_entry; + + if (likely(!ieee80211_is_back_req(bar->frame_control))) + return; + + bar_entry = kmalloc(sizeof(*bar_entry), GFP_ATOMIC); + + /* + * If the alloc fails we still send the BAR out but just don't track + * it in our bar list. And as a result we will report it to mac80211 + * back as failed. + */ + if (!bar_entry) + return; + + bar_entry->entry = entry; + bar_entry->block_acked = 0; + + /* + * Copy the relevant parts of the 802.11 BAR into out check list + * such that we can use RCU for less-overhead in the RX path since + * sending BARs and processing the according BlockAck should be + * the exception. + */ + memcpy(bar_entry->ra, bar->ra, sizeof(bar->ra)); + memcpy(bar_entry->ta, bar->ta, sizeof(bar->ta)); + bar_entry->control = bar->control; + bar_entry->start_seq_num = bar->start_seq_num; + + /* + * Insert BAR into our BAR check list. + */ + spin_lock_bh(&rt2x00dev->bar_list_lock); + list_add_tail_rcu(&bar_entry->list, &rt2x00dev->bar_list); + spin_unlock_bh(&rt2x00dev->bar_list_lock); +} + +int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, + struct ieee80211_sta *sta, bool local) +{ + struct ieee80211_tx_info *tx_info; + struct queue_entry *entry; + struct txentry_desc txdesc; + struct skb_frame_desc *skbdesc; + u8 rate_idx, rate_flags; + int ret = 0; + + /* + * Copy all TX descriptor information into txdesc, + * after that we are free to use the skb->cb array + * for our information. + */ + rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc, sta); + + /* + * All information is retrieved from the skb->cb array, + * now we should claim ownership of the driver part of that + * array, preserving the bitrate index and flags. + */ + tx_info = IEEE80211_SKB_CB(skb); + rate_idx = tx_info->control.rates[0].idx; + rate_flags = tx_info->control.rates[0].flags; + skbdesc = get_skb_frame_desc(skb); + memset(skbdesc, 0, sizeof(*skbdesc)); + skbdesc->tx_rate_idx = rate_idx; + skbdesc->tx_rate_flags = rate_flags; + + if (local) + skbdesc->flags |= SKBDESC_NOT_MAC80211; + + /* + * When hardware encryption is supported, and this frame + * is to be encrypted, we should strip the IV/EIV data from + * the frame so we can provide it to the driver separately. + */ + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) && + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) { + if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_COPY_IV)) + rt2x00crypto_tx_copy_iv(skb, &txdesc); + else + rt2x00crypto_tx_remove_iv(skb, &txdesc); + } + + /* + * When DMA allocation is required we should guarantee to the + * driver that the DMA is aligned to a 4-byte boundary. + * However some drivers require L2 padding to pad the payload + * rather then the header. This could be a requirement for + * PCI and USB devices, while header alignment only is valid + * for PCI devices. + */ + if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_L2PAD)) + rt2x00queue_insert_l2pad(skb, txdesc.header_length); + else if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_DMA)) + rt2x00queue_align_frame(skb); + + /* + * That function must be called with bh disabled. + */ + spin_lock(&queue->tx_lock); + + if (unlikely(rt2x00queue_full(queue))) { + rt2x00_err(queue->rt2x00dev, "Dropping frame due to full tx queue %d\n", + queue->qid); + ret = -ENOBUFS; + goto out; + } + + entry = rt2x00queue_get_entry(queue, Q_INDEX); + + if (unlikely(test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, + &entry->flags))) { + rt2x00_err(queue->rt2x00dev, + "Arrived at non-free entry in the non-full queue %d\n" + "Please file bug report to %s\n", + queue->qid, DRV_PROJECT); + ret = -EINVAL; + goto out; + } + + skbdesc->entry = entry; + entry->skb = skb; + + /* + * It could be possible that the queue was corrupted and this + * call failed. Since we always return NETDEV_TX_OK to mac80211, + * this frame will simply be dropped. + */ + if (unlikely(rt2x00queue_write_tx_data(entry, &txdesc))) { + clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); + entry->skb = NULL; + ret = -EIO; + goto out; + } + + /* + * Put BlockAckReqs into our check list for driver BA processing. + */ + rt2x00queue_bar_check(entry); + + set_bit(ENTRY_DATA_PENDING, &entry->flags); + + rt2x00queue_index_inc(entry, Q_INDEX); + rt2x00queue_write_tx_descriptor(entry, &txdesc); + rt2x00queue_kick_tx_queue(queue, &txdesc); + +out: + spin_unlock(&queue->tx_lock); + return ret; +} + +int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif) +{ + struct rt2x00_intf *intf = vif_to_intf(vif); + + if (unlikely(!intf->beacon)) + return -ENOBUFS; + + /* + * Clean up the beacon skb. + */ + rt2x00queue_free_skb(intf->beacon); + + /* + * Clear beacon (single bssid devices don't need to clear the beacon + * since the beacon queue will get stopped anyway). + */ + if (rt2x00dev->ops->lib->clear_beacon) + rt2x00dev->ops->lib->clear_beacon(intf->beacon); + + return 0; +} + +int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif) +{ + struct rt2x00_intf *intf = vif_to_intf(vif); + struct skb_frame_desc *skbdesc; + struct txentry_desc txdesc; + + if (unlikely(!intf->beacon)) + return -ENOBUFS; + + /* + * Clean up the beacon skb. + */ + rt2x00queue_free_skb(intf->beacon); + + intf->beacon->skb = ieee80211_beacon_get(rt2x00dev->hw, vif); + if (!intf->beacon->skb) + return -ENOMEM; + + /* + * Copy all TX descriptor information into txdesc, + * after that we are free to use the skb->cb array + * for our information. + */ + rt2x00queue_create_tx_descriptor(rt2x00dev, intf->beacon->skb, &txdesc, NULL); + + /* + * Fill in skb descriptor + */ + skbdesc = get_skb_frame_desc(intf->beacon->skb); + memset(skbdesc, 0, sizeof(*skbdesc)); + skbdesc->entry = intf->beacon; + + /* + * Send beacon to hardware. + */ + rt2x00dev->ops->lib->write_beacon(intf->beacon, &txdesc); + + return 0; + +} + +bool rt2x00queue_for_each_entry(struct data_queue *queue, + enum queue_index start, + enum queue_index end, + void *data, + bool (*fn)(struct queue_entry *entry, + void *data)) +{ + unsigned long irqflags; + unsigned int index_start; + unsigned int index_end; + unsigned int i; + + if (unlikely(start >= Q_INDEX_MAX || end >= Q_INDEX_MAX)) { + rt2x00_err(queue->rt2x00dev, + "Entry requested from invalid index range (%d - %d)\n", + start, end); + return true; + } + + /* + * Only protect the range we are going to loop over, + * if during our loop a extra entry is set to pending + * it should not be kicked during this run, since it + * is part of another TX operation. + */ + spin_lock_irqsave(&queue->index_lock, irqflags); + index_start = queue->index[start]; + index_end = queue->index[end]; + spin_unlock_irqrestore(&queue->index_lock, irqflags); + + /* + * Start from the TX done pointer, this guarantees that we will + * send out all frames in the correct order. + */ + if (index_start < index_end) { + for (i = index_start; i < index_end; i++) { + if (fn(&queue->entries[i], data)) + return true; + } + } else { + for (i = index_start; i < queue->limit; i++) { + if (fn(&queue->entries[i], data)) + return true; + } + + for (i = 0; i < index_end; i++) { + if (fn(&queue->entries[i], data)) + return true; + } + } + + return false; +} +EXPORT_SYMBOL_GPL(rt2x00queue_for_each_entry); + +struct queue_entry *rt2x00queue_get_entry(struct data_queue *queue, + enum queue_index index) +{ + struct queue_entry *entry; + unsigned long irqflags; + + if (unlikely(index >= Q_INDEX_MAX)) { + rt2x00_err(queue->rt2x00dev, "Entry requested from invalid index type (%d)\n", + index); + return NULL; + } + + spin_lock_irqsave(&queue->index_lock, irqflags); + + entry = &queue->entries[queue->index[index]]; + + spin_unlock_irqrestore(&queue->index_lock, irqflags); + + return entry; +} +EXPORT_SYMBOL_GPL(rt2x00queue_get_entry); + +void rt2x00queue_index_inc(struct queue_entry *entry, enum queue_index index) +{ + struct data_queue *queue = entry->queue; + unsigned long irqflags; + + if (unlikely(index >= Q_INDEX_MAX)) { + rt2x00_err(queue->rt2x00dev, + "Index change on invalid index type (%d)\n", index); + return; + } + + spin_lock_irqsave(&queue->index_lock, irqflags); + + queue->index[index]++; + if (queue->index[index] >= queue->limit) + queue->index[index] = 0; + + entry->last_action = jiffies; + + if (index == Q_INDEX) { + queue->length++; + } else if (index == Q_INDEX_DONE) { + queue->length--; + queue->count++; + } + + spin_unlock_irqrestore(&queue->index_lock, irqflags); +} + +static void rt2x00queue_pause_queue_nocheck(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + /* + * For TX queues, we have to disable the queue + * inside mac80211. + */ + ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid); + break; + default: + break; + } +} +void rt2x00queue_pause_queue(struct data_queue *queue) +{ + if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || + !test_bit(QUEUE_STARTED, &queue->flags) || + test_and_set_bit(QUEUE_PAUSED, &queue->flags)) + return; + + rt2x00queue_pause_queue_nocheck(queue); +} +EXPORT_SYMBOL_GPL(rt2x00queue_pause_queue); + +void rt2x00queue_unpause_queue(struct data_queue *queue) +{ + if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || + !test_bit(QUEUE_STARTED, &queue->flags) || + !test_and_clear_bit(QUEUE_PAUSED, &queue->flags)) + return; + + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + /* + * For TX queues, we have to enable the queue + * inside mac80211. + */ + ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid); + break; + case QID_RX: + /* + * For RX we need to kick the queue now in order to + * receive frames. + */ + queue->rt2x00dev->ops->lib->kick_queue(queue); + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2x00queue_unpause_queue); + +void rt2x00queue_start_queue(struct data_queue *queue) +{ + mutex_lock(&queue->status_lock); + + if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || + test_and_set_bit(QUEUE_STARTED, &queue->flags)) { + mutex_unlock(&queue->status_lock); + return; + } + + set_bit(QUEUE_PAUSED, &queue->flags); + + queue->rt2x00dev->ops->lib->start_queue(queue); + + rt2x00queue_unpause_queue(queue); + + mutex_unlock(&queue->status_lock); +} +EXPORT_SYMBOL_GPL(rt2x00queue_start_queue); + +void rt2x00queue_stop_queue(struct data_queue *queue) +{ + mutex_lock(&queue->status_lock); + + if (!test_and_clear_bit(QUEUE_STARTED, &queue->flags)) { + mutex_unlock(&queue->status_lock); + return; + } + + rt2x00queue_pause_queue_nocheck(queue); + + queue->rt2x00dev->ops->lib->stop_queue(queue); + + mutex_unlock(&queue->status_lock); +} +EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue); + +void rt2x00queue_flush_queue(struct data_queue *queue, bool drop) +{ + bool tx_queue = + (queue->qid == QID_AC_VO) || + (queue->qid == QID_AC_VI) || + (queue->qid == QID_AC_BE) || + (queue->qid == QID_AC_BK); + + + /* + * If we are not supposed to drop any pending + * frames, this means we must force a start (=kick) + * to the queue to make sure the hardware will + * start transmitting. + */ + if (!drop && tx_queue) + queue->rt2x00dev->ops->lib->kick_queue(queue); + + /* + * Check if driver supports flushing, if that is the case we can + * defer the flushing to the driver. Otherwise we must use the + * alternative which just waits for the queue to become empty. + */ + if (likely(queue->rt2x00dev->ops->lib->flush_queue)) + queue->rt2x00dev->ops->lib->flush_queue(queue, drop); + + /* + * The queue flush has failed... + */ + if (unlikely(!rt2x00queue_empty(queue))) + rt2x00_warn(queue->rt2x00dev, "Queue %d failed to flush\n", + queue->qid); +} +EXPORT_SYMBOL_GPL(rt2x00queue_flush_queue); + +void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + /* + * rt2x00queue_start_queue will call ieee80211_wake_queue + * for each queue after is has been properly initialized. + */ + tx_queue_for_each(rt2x00dev, queue) + rt2x00queue_start_queue(queue); + + rt2x00queue_start_queue(rt2x00dev->rx); +} +EXPORT_SYMBOL_GPL(rt2x00queue_start_queues); + +void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + /* + * rt2x00queue_stop_queue will call ieee80211_stop_queue + * as well, but we are completely shutting doing everything + * now, so it is much safer to stop all TX queues at once, + * and use rt2x00queue_stop_queue for cleaning up. + */ + ieee80211_stop_queues(rt2x00dev->hw); + + tx_queue_for_each(rt2x00dev, queue) + rt2x00queue_stop_queue(queue); + + rt2x00queue_stop_queue(rt2x00dev->rx); +} +EXPORT_SYMBOL_GPL(rt2x00queue_stop_queues); + +void rt2x00queue_flush_queues(struct rt2x00_dev *rt2x00dev, bool drop) +{ + struct data_queue *queue; + + tx_queue_for_each(rt2x00dev, queue) + rt2x00queue_flush_queue(queue, drop); + + rt2x00queue_flush_queue(rt2x00dev->rx, drop); +} +EXPORT_SYMBOL_GPL(rt2x00queue_flush_queues); + +static void rt2x00queue_reset(struct data_queue *queue) +{ + unsigned long irqflags; + unsigned int i; + + spin_lock_irqsave(&queue->index_lock, irqflags); + + queue->count = 0; + queue->length = 0; + + for (i = 0; i < Q_INDEX_MAX; i++) + queue->index[i] = 0; + + spin_unlock_irqrestore(&queue->index_lock, irqflags); +} + +void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + unsigned int i; + + queue_for_each(rt2x00dev, queue) { + rt2x00queue_reset(queue); + + for (i = 0; i < queue->limit; i++) + rt2x00dev->ops->lib->clear_entry(&queue->entries[i]); + } +} + +static int rt2x00queue_alloc_entries(struct data_queue *queue) +{ + struct queue_entry *entries; + unsigned int entry_size; + unsigned int i; + + rt2x00queue_reset(queue); + + /* + * Allocate all queue entries. + */ + entry_size = sizeof(*entries) + queue->priv_size; + entries = kcalloc(queue->limit, entry_size, GFP_KERNEL); + if (!entries) + return -ENOMEM; + +#define QUEUE_ENTRY_PRIV_OFFSET(__base, __index, __limit, __esize, __psize) \ + (((char *)(__base)) + ((__limit) * (__esize)) + \ + ((__index) * (__psize))) + + for (i = 0; i < queue->limit; i++) { + entries[i].flags = 0; + entries[i].queue = queue; + entries[i].skb = NULL; + entries[i].entry_idx = i; + entries[i].priv_data = + QUEUE_ENTRY_PRIV_OFFSET(entries, i, queue->limit, + sizeof(*entries), queue->priv_size); + } + +#undef QUEUE_ENTRY_PRIV_OFFSET + + queue->entries = entries; + + return 0; +} + +static void rt2x00queue_free_skbs(struct data_queue *queue) +{ + unsigned int i; + + if (!queue->entries) + return; + + for (i = 0; i < queue->limit; i++) { + rt2x00queue_free_skb(&queue->entries[i]); + } +} + +static int rt2x00queue_alloc_rxskbs(struct data_queue *queue) +{ + unsigned int i; + struct sk_buff *skb; + + for (i = 0; i < queue->limit; i++) { + skb = rt2x00queue_alloc_rxskb(&queue->entries[i], GFP_KERNEL); + if (!skb) + return -ENOMEM; + queue->entries[i].skb = skb; + } + + return 0; +} + +int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + int status; + + status = rt2x00queue_alloc_entries(rt2x00dev->rx); + if (status) + goto exit; + + tx_queue_for_each(rt2x00dev, queue) { + status = rt2x00queue_alloc_entries(queue); + if (status) + goto exit; + } + + status = rt2x00queue_alloc_entries(rt2x00dev->bcn); + if (status) + goto exit; + + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE)) { + status = rt2x00queue_alloc_entries(rt2x00dev->atim); + if (status) + goto exit; + } + + status = rt2x00queue_alloc_rxskbs(rt2x00dev->rx); + if (status) + goto exit; + + return 0; + +exit: + rt2x00_err(rt2x00dev, "Queue entries allocation failed\n"); + + rt2x00queue_uninitialize(rt2x00dev); + + return status; +} + +void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + rt2x00queue_free_skbs(rt2x00dev->rx); + + queue_for_each(rt2x00dev, queue) { + kfree(queue->entries); + queue->entries = NULL; + } +} + +static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, + struct data_queue *queue, enum data_queue_qid qid) +{ + mutex_init(&queue->status_lock); + spin_lock_init(&queue->tx_lock); + spin_lock_init(&queue->index_lock); + + queue->rt2x00dev = rt2x00dev; + queue->qid = qid; + queue->txop = 0; + queue->aifs = 2; + queue->cw_min = 5; + queue->cw_max = 10; + + rt2x00dev->ops->queue_init(queue); + + queue->threshold = DIV_ROUND_UP(queue->limit, 10); +} + +int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + enum data_queue_qid qid; + unsigned int req_atim = + rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE); + + /* + * We need the following queues: + * RX: 1 + * TX: ops->tx_queues + * Beacon: 1 + * Atim: 1 (if required) + */ + rt2x00dev->data_queues = 2 + rt2x00dev->ops->tx_queues + req_atim; + + queue = kcalloc(rt2x00dev->data_queues, sizeof(*queue), GFP_KERNEL); + if (!queue) { + rt2x00_err(rt2x00dev, "Queue allocation failed\n"); + return -ENOMEM; + } + + /* + * Initialize pointers + */ + rt2x00dev->rx = queue; + rt2x00dev->tx = &queue[1]; + rt2x00dev->bcn = &queue[1 + rt2x00dev->ops->tx_queues]; + rt2x00dev->atim = req_atim ? &queue[2 + rt2x00dev->ops->tx_queues] : NULL; + + /* + * Initialize queue parameters. + * RX: qid = QID_RX + * TX: qid = QID_AC_VO + index + * TX: cw_min: 2^5 = 32. + * TX: cw_max: 2^10 = 1024. + * BCN: qid = QID_BEACON + * ATIM: qid = QID_ATIM + */ + rt2x00queue_init(rt2x00dev, rt2x00dev->rx, QID_RX); + + qid = QID_AC_VO; + tx_queue_for_each(rt2x00dev, queue) + rt2x00queue_init(rt2x00dev, queue, qid++); + + rt2x00queue_init(rt2x00dev, rt2x00dev->bcn, QID_BEACON); + if (req_atim) + rt2x00queue_init(rt2x00dev, rt2x00dev->atim, QID_ATIM); + + return 0; +} + +void rt2x00queue_free(struct rt2x00_dev *rt2x00dev) +{ + kfree(rt2x00dev->rx); + rt2x00dev->rx = NULL; + rt2x00dev->tx = NULL; + rt2x00dev->bcn = NULL; +} diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h new file mode 100644 index 000000000000..2233b911a1d7 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h @@ -0,0 +1,686 @@ +/* + Copyright (C) 2004 - 2010 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00 + Abstract: rt2x00 queue datastructures and routines + */ + +#ifndef RT2X00QUEUE_H +#define RT2X00QUEUE_H + +#include + +/** + * DOC: Entry frame size + * + * Ralink PCI devices demand the Frame size to be a multiple of 128 bytes, + * for USB devices this restriction does not apply, but the value of + * 2432 makes sense since it is big enough to contain the maximum fragment + * size according to the ieee802.11 specs. + * The aggregation size depends on support from the driver, but should + * be something around 3840 bytes. + */ +#define DATA_FRAME_SIZE 2432 +#define MGMT_FRAME_SIZE 256 +#define AGGREGATION_SIZE 3840 + +/** + * enum data_queue_qid: Queue identification + * + * @QID_AC_VO: AC VO queue + * @QID_AC_VI: AC VI queue + * @QID_AC_BE: AC BE queue + * @QID_AC_BK: AC BK queue + * @QID_HCCA: HCCA queue + * @QID_MGMT: MGMT queue (prio queue) + * @QID_RX: RX queue + * @QID_OTHER: None of the above (don't use, only present for completeness) + * @QID_BEACON: Beacon queue (value unspecified, don't send it to device) + * @QID_ATIM: Atim queue (value unspecified, don't send it to device) + */ +enum data_queue_qid { + QID_AC_VO = 0, + QID_AC_VI = 1, + QID_AC_BE = 2, + QID_AC_BK = 3, + QID_HCCA = 4, + QID_MGMT = 13, + QID_RX = 14, + QID_OTHER = 15, + QID_BEACON, + QID_ATIM, +}; + +/** + * enum skb_frame_desc_flags: Flags for &struct skb_frame_desc + * + * @SKBDESC_DMA_MAPPED_RX: &skb_dma field has been mapped for RX + * @SKBDESC_DMA_MAPPED_TX: &skb_dma field has been mapped for TX + * @SKBDESC_IV_STRIPPED: Frame contained a IV/EIV provided by + * mac80211 but was stripped for processing by the driver. + * @SKBDESC_NOT_MAC80211: Frame didn't originate from mac80211, + * don't try to pass it back. + * @SKBDESC_DESC_IN_SKB: The descriptor is at the start of the + * skb, instead of in the desc field. + */ +enum skb_frame_desc_flags { + SKBDESC_DMA_MAPPED_RX = 1 << 0, + SKBDESC_DMA_MAPPED_TX = 1 << 1, + SKBDESC_IV_STRIPPED = 1 << 2, + SKBDESC_NOT_MAC80211 = 1 << 3, + SKBDESC_DESC_IN_SKB = 1 << 4, +}; + +/** + * struct skb_frame_desc: Descriptor information for the skb buffer + * + * This structure is placed over the driver_data array, this means that + * this structure should not exceed the size of that array (40 bytes). + * + * @flags: Frame flags, see &enum skb_frame_desc_flags. + * @desc_len: Length of the frame descriptor. + * @tx_rate_idx: the index of the TX rate, used for TX status reporting + * @tx_rate_flags: the TX rate flags, used for TX status reporting + * @desc: Pointer to descriptor part of the frame. + * Note that this pointer could point to something outside + * of the scope of the skb->data pointer. + * @iv: IV/EIV data used during encryption/decryption. + * @skb_dma: (PCI-only) the DMA address associated with the sk buffer. + * @entry: The entry to which this sk buffer belongs. + */ +struct skb_frame_desc { + u8 flags; + + u8 desc_len; + u8 tx_rate_idx; + u8 tx_rate_flags; + + void *desc; + + __le32 iv[2]; + + dma_addr_t skb_dma; + + struct queue_entry *entry; +}; + +/** + * get_skb_frame_desc - Obtain the rt2x00 frame descriptor from a sk_buff. + * @skb: &struct sk_buff from where we obtain the &struct skb_frame_desc + */ +static inline struct skb_frame_desc* get_skb_frame_desc(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct skb_frame_desc) > + IEEE80211_TX_INFO_DRIVER_DATA_SIZE); + return (struct skb_frame_desc *)&IEEE80211_SKB_CB(skb)->driver_data; +} + +/** + * enum rxdone_entry_desc_flags: Flags for &struct rxdone_entry_desc + * + * @RXDONE_SIGNAL_PLCP: Signal field contains the plcp value. + * @RXDONE_SIGNAL_BITRATE: Signal field contains the bitrate value. + * @RXDONE_SIGNAL_MCS: Signal field contains the mcs value. + * @RXDONE_MY_BSS: Does this frame originate from device's BSS. + * @RXDONE_CRYPTO_IV: Driver provided IV/EIV data. + * @RXDONE_CRYPTO_ICV: Driver provided ICV data. + * @RXDONE_L2PAD: 802.11 payload has been padded to 4-byte boundary. + */ +enum rxdone_entry_desc_flags { + RXDONE_SIGNAL_PLCP = BIT(0), + RXDONE_SIGNAL_BITRATE = BIT(1), + RXDONE_SIGNAL_MCS = BIT(2), + RXDONE_MY_BSS = BIT(3), + RXDONE_CRYPTO_IV = BIT(4), + RXDONE_CRYPTO_ICV = BIT(5), + RXDONE_L2PAD = BIT(6), +}; + +/** + * RXDONE_SIGNAL_MASK - Define to mask off all &rxdone_entry_desc_flags flags + * except for the RXDONE_SIGNAL_* flags. This is useful to convert the dev_flags + * from &rxdone_entry_desc to a signal value type. + */ +#define RXDONE_SIGNAL_MASK \ + ( RXDONE_SIGNAL_PLCP | RXDONE_SIGNAL_BITRATE | RXDONE_SIGNAL_MCS ) + +/** + * struct rxdone_entry_desc: RX Entry descriptor + * + * Summary of information that has been read from the RX frame descriptor. + * + * @timestamp: RX Timestamp + * @signal: Signal of the received frame. + * @rssi: RSSI of the received frame. + * @size: Data size of the received frame. + * @flags: MAC80211 receive flags (See &enum mac80211_rx_flags). + * @dev_flags: Ralink receive flags (See &enum rxdone_entry_desc_flags). + * @rate_mode: Rate mode (See @enum rate_modulation). + * @cipher: Cipher type used during decryption. + * @cipher_status: Decryption status. + * @iv: IV/EIV data used during decryption. + * @icv: ICV data used during decryption. + */ +struct rxdone_entry_desc { + u64 timestamp; + int signal; + int rssi; + int size; + int flags; + int dev_flags; + u16 rate_mode; + u8 cipher; + u8 cipher_status; + + __le32 iv[2]; + __le32 icv; +}; + +/** + * enum txdone_entry_desc_flags: Flags for &struct txdone_entry_desc + * + * Every txdone report has to contain the basic result of the + * transmission, either &TXDONE_UNKNOWN, &TXDONE_SUCCESS or + * &TXDONE_FAILURE. The flag &TXDONE_FALLBACK can be used in + * conjunction with all of these flags but should only be set + * if retires > 0. The flag &TXDONE_EXCESSIVE_RETRY can only be used + * in conjunction with &TXDONE_FAILURE. + * + * @TXDONE_UNKNOWN: Hardware could not determine success of transmission. + * @TXDONE_SUCCESS: Frame was successfully send + * @TXDONE_FALLBACK: Hardware used fallback rates for retries + * @TXDONE_FAILURE: Frame was not successfully send + * @TXDONE_EXCESSIVE_RETRY: In addition to &TXDONE_FAILURE, the + * frame transmission failed due to excessive retries. + */ +enum txdone_entry_desc_flags { + TXDONE_UNKNOWN, + TXDONE_SUCCESS, + TXDONE_FALLBACK, + TXDONE_FAILURE, + TXDONE_EXCESSIVE_RETRY, + TXDONE_AMPDU, +}; + +/** + * struct txdone_entry_desc: TX done entry descriptor + * + * Summary of information that has been read from the TX frame descriptor + * after the device is done with transmission. + * + * @flags: TX done flags (See &enum txdone_entry_desc_flags). + * @retry: Retry count. + */ +struct txdone_entry_desc { + unsigned long flags; + int retry; +}; + +/** + * enum txentry_desc_flags: Status flags for TX entry descriptor + * + * @ENTRY_TXD_RTS_FRAME: This frame is a RTS frame. + * @ENTRY_TXD_CTS_FRAME: This frame is a CTS-to-self frame. + * @ENTRY_TXD_GENERATE_SEQ: This frame requires sequence counter. + * @ENTRY_TXD_FIRST_FRAGMENT: This is the first frame. + * @ENTRY_TXD_MORE_FRAG: This frame is followed by another fragment. + * @ENTRY_TXD_REQ_TIMESTAMP: Require timestamp to be inserted. + * @ENTRY_TXD_BURST: This frame belongs to the same burst event. + * @ENTRY_TXD_ACK: An ACK is required for this frame. + * @ENTRY_TXD_RETRY_MODE: When set, the long retry count is used. + * @ENTRY_TXD_ENCRYPT: This frame should be encrypted. + * @ENTRY_TXD_ENCRYPT_PAIRWISE: Use pairwise key table (instead of shared). + * @ENTRY_TXD_ENCRYPT_IV: Generate IV/EIV in hardware. + * @ENTRY_TXD_ENCRYPT_MMIC: Generate MIC in hardware. + * @ENTRY_TXD_HT_AMPDU: This frame is part of an AMPDU. + * @ENTRY_TXD_HT_BW_40: Use 40MHz Bandwidth. + * @ENTRY_TXD_HT_SHORT_GI: Use short GI. + * @ENTRY_TXD_HT_MIMO_PS: The receiving STA is in dynamic SM PS mode. + */ +enum txentry_desc_flags { + ENTRY_TXD_RTS_FRAME, + ENTRY_TXD_CTS_FRAME, + ENTRY_TXD_GENERATE_SEQ, + ENTRY_TXD_FIRST_FRAGMENT, + ENTRY_TXD_MORE_FRAG, + ENTRY_TXD_REQ_TIMESTAMP, + ENTRY_TXD_BURST, + ENTRY_TXD_ACK, + ENTRY_TXD_RETRY_MODE, + ENTRY_TXD_ENCRYPT, + ENTRY_TXD_ENCRYPT_PAIRWISE, + ENTRY_TXD_ENCRYPT_IV, + ENTRY_TXD_ENCRYPT_MMIC, + ENTRY_TXD_HT_AMPDU, + ENTRY_TXD_HT_BW_40, + ENTRY_TXD_HT_SHORT_GI, + ENTRY_TXD_HT_MIMO_PS, +}; + +/** + * struct txentry_desc: TX Entry descriptor + * + * Summary of information for the frame descriptor before sending a TX frame. + * + * @flags: Descriptor flags (See &enum queue_entry_flags). + * @length: Length of the entire frame. + * @header_length: Length of 802.11 header. + * @length_high: PLCP length high word. + * @length_low: PLCP length low word. + * @signal: PLCP signal. + * @service: PLCP service. + * @msc: MCS. + * @stbc: Use Space Time Block Coding (only available for MCS rates < 8). + * @ba_size: Size of the recepients RX reorder buffer - 1. + * @rate_mode: Rate mode (See @enum rate_modulation). + * @mpdu_density: MDPU density. + * @retry_limit: Max number of retries. + * @ifs: IFS value. + * @txop: IFS value for 11n capable chips. + * @cipher: Cipher type used for encryption. + * @key_idx: Key index used for encryption. + * @iv_offset: Position where IV should be inserted by hardware. + * @iv_len: Length of IV data. + */ +struct txentry_desc { + unsigned long flags; + + u16 length; + u16 header_length; + + union { + struct { + u16 length_high; + u16 length_low; + u16 signal; + u16 service; + enum ifs ifs; + } plcp; + + struct { + u16 mcs; + u8 stbc; + u8 ba_size; + u8 mpdu_density; + enum txop txop; + int wcid; + } ht; + } u; + + enum rate_modulation rate_mode; + + short retry_limit; + + enum cipher cipher; + u16 key_idx; + u16 iv_offset; + u16 iv_len; +}; + +/** + * enum queue_entry_flags: Status flags for queue entry + * + * @ENTRY_BCN_ASSIGNED: This entry has been assigned to an interface. + * As long as this bit is set, this entry may only be touched + * through the interface structure. + * @ENTRY_OWNER_DEVICE_DATA: This entry is owned by the device for data + * transfer (either TX or RX depending on the queue). The entry should + * only be touched after the device has signaled it is done with it. + * @ENTRY_DATA_PENDING: This entry contains a valid frame and is waiting + * for the signal to start sending. + * @ENTRY_DATA_IO_FAILED: Hardware indicated that an IO error occurred + * while transferring the data to the hardware. No TX status report will + * be expected from the hardware. + * @ENTRY_DATA_STATUS_PENDING: The entry has been send to the device and + * returned. It is now waiting for the status reporting before the + * entry can be reused again. + */ +enum queue_entry_flags { + ENTRY_BCN_ASSIGNED, + ENTRY_BCN_ENABLED, + ENTRY_OWNER_DEVICE_DATA, + ENTRY_DATA_PENDING, + ENTRY_DATA_IO_FAILED, + ENTRY_DATA_STATUS_PENDING, + ENTRY_DATA_STATUS_SET, +}; + +/** + * struct queue_entry: Entry inside the &struct data_queue + * + * @flags: Entry flags, see &enum queue_entry_flags. + * @last_action: Timestamp of last change. + * @queue: The data queue (&struct data_queue) to which this entry belongs. + * @skb: The buffer which is currently being transmitted (for TX queue), + * or used to directly receive data in (for RX queue). + * @entry_idx: The entry index number. + * @priv_data: Private data belonging to this queue entry. The pointer + * points to data specific to a particular driver and queue type. + * @status: Device specific status + */ +struct queue_entry { + unsigned long flags; + unsigned long last_action; + + struct data_queue *queue; + + struct sk_buff *skb; + + unsigned int entry_idx; + + u32 status; + + void *priv_data; +}; + +/** + * enum queue_index: Queue index type + * + * @Q_INDEX: Index pointer to the current entry in the queue, if this entry is + * owned by the hardware then the queue is considered to be full. + * @Q_INDEX_DMA_DONE: Index pointer for the next entry which will have been + * transferred to the hardware. + * @Q_INDEX_DONE: Index pointer to the next entry which will be completed by + * the hardware and for which we need to run the txdone handler. If this + * entry is not owned by the hardware the queue is considered to be empty. + * @Q_INDEX_MAX: Keep last, used in &struct data_queue to determine the size + * of the index array. + */ +enum queue_index { + Q_INDEX, + Q_INDEX_DMA_DONE, + Q_INDEX_DONE, + Q_INDEX_MAX, +}; + +/** + * enum data_queue_flags: Status flags for data queues + * + * @QUEUE_STARTED: The queue has been started. Fox RX queues this means the + * device might be DMA'ing skbuffers. TX queues will accept skbuffers to + * be transmitted and beacon queues will start beaconing the configured + * beacons. + * @QUEUE_PAUSED: The queue has been started but is currently paused. + * When this bit is set, the queue has been stopped in mac80211, + * preventing new frames to be enqueued. However, a few frames + * might still appear shortly after the pausing... + */ +enum data_queue_flags { + QUEUE_STARTED, + QUEUE_PAUSED, +}; + +/** + * struct data_queue: Data queue + * + * @rt2x00dev: Pointer to main &struct rt2x00dev where this queue belongs to. + * @entries: Base address of the &struct queue_entry which are + * part of this queue. + * @qid: The queue identification, see &enum data_queue_qid. + * @flags: Entry flags, see &enum queue_entry_flags. + * @status_lock: The mutex for protecting the start/stop/flush + * handling on this queue. + * @tx_lock: Spinlock to serialize tx operations on this queue. + * @index_lock: Spinlock to protect index handling. Whenever @index, @index_done or + * @index_crypt needs to be changed this lock should be grabbed to prevent + * index corruption due to concurrency. + * @count: Number of frames handled in the queue. + * @limit: Maximum number of entries in the queue. + * @threshold: Minimum number of free entries before queue is kicked by force. + * @length: Number of frames in queue. + * @index: Index pointers to entry positions in the queue, + * use &enum queue_index to get a specific index field. + * @txop: maximum burst time. + * @aifs: The aifs value for outgoing frames (field ignored in RX queue). + * @cw_min: The cw min value for outgoing frames (field ignored in RX queue). + * @cw_max: The cw max value for outgoing frames (field ignored in RX queue). + * @data_size: Maximum data size for the frames in this queue. + * @desc_size: Hardware descriptor size for the data in this queue. + * @priv_size: Size of per-queue_entry private data. + * @usb_endpoint: Device endpoint used for communication (USB only) + * @usb_maxpacket: Max packet size for given endpoint (USB only) + */ +struct data_queue { + struct rt2x00_dev *rt2x00dev; + struct queue_entry *entries; + + enum data_queue_qid qid; + unsigned long flags; + + struct mutex status_lock; + spinlock_t tx_lock; + spinlock_t index_lock; + + unsigned int count; + unsigned short limit; + unsigned short threshold; + unsigned short length; + unsigned short index[Q_INDEX_MAX]; + + unsigned short txop; + unsigned short aifs; + unsigned short cw_min; + unsigned short cw_max; + + unsigned short data_size; + unsigned char desc_size; + unsigned char winfo_size; + unsigned short priv_size; + + unsigned short usb_endpoint; + unsigned short usb_maxpacket; +}; + +/** + * queue_end - Return pointer to the last queue (HELPER MACRO). + * @__dev: Pointer to &struct rt2x00_dev + * + * Using the base rx pointer and the maximum number of available queues, + * this macro will return the address of 1 position beyond the end of the + * queues array. + */ +#define queue_end(__dev) \ + &(__dev)->rx[(__dev)->data_queues] + +/** + * tx_queue_end - Return pointer to the last TX queue (HELPER MACRO). + * @__dev: Pointer to &struct rt2x00_dev + * + * Using the base tx pointer and the maximum number of available TX + * queues, this macro will return the address of 1 position beyond + * the end of the TX queue array. + */ +#define tx_queue_end(__dev) \ + &(__dev)->tx[(__dev)->ops->tx_queues] + +/** + * queue_next - Return pointer to next queue in list (HELPER MACRO). + * @__queue: Current queue for which we need the next queue + * + * Using the current queue address we take the address directly + * after the queue to take the next queue. Note that this macro + * should be used carefully since it does not protect against + * moving past the end of the list. (See macros &queue_end and + * &tx_queue_end for determining the end of the queue). + */ +#define queue_next(__queue) \ + &(__queue)[1] + +/** + * queue_loop - Loop through the queues within a specific range (HELPER MACRO). + * @__entry: Pointer where the current queue entry will be stored in. + * @__start: Start queue pointer. + * @__end: End queue pointer. + * + * This macro will loop through all queues between &__start and &__end. + */ +#define queue_loop(__entry, __start, __end) \ + for ((__entry) = (__start); \ + prefetch(queue_next(__entry)), (__entry) != (__end);\ + (__entry) = queue_next(__entry)) + +/** + * queue_for_each - Loop through all queues + * @__dev: Pointer to &struct rt2x00_dev + * @__entry: Pointer where the current queue entry will be stored in. + * + * This macro will loop through all available queues. + */ +#define queue_for_each(__dev, __entry) \ + queue_loop(__entry, (__dev)->rx, queue_end(__dev)) + +/** + * tx_queue_for_each - Loop through the TX queues + * @__dev: Pointer to &struct rt2x00_dev + * @__entry: Pointer where the current queue entry will be stored in. + * + * This macro will loop through all TX related queues excluding + * the Beacon and Atim queues. + */ +#define tx_queue_for_each(__dev, __entry) \ + queue_loop(__entry, (__dev)->tx, tx_queue_end(__dev)) + +/** + * txall_queue_for_each - Loop through all TX related queues + * @__dev: Pointer to &struct rt2x00_dev + * @__entry: Pointer where the current queue entry will be stored in. + * + * This macro will loop through all TX related queues including + * the Beacon and Atim queues. + */ +#define txall_queue_for_each(__dev, __entry) \ + queue_loop(__entry, (__dev)->tx, queue_end(__dev)) + +/** + * rt2x00queue_for_each_entry - Loop through all entries in the queue + * @queue: Pointer to @data_queue + * @start: &enum queue_index Pointer to start index + * @end: &enum queue_index Pointer to end index + * @data: Data to pass to the callback function + * @fn: The function to call for each &struct queue_entry + * + * This will walk through all entries in the queue, in chronological + * order. This means it will start at the current @start pointer + * and will walk through the queue until it reaches the @end pointer. + * + * If fn returns true for an entry rt2x00queue_for_each_entry will stop + * processing and return true as well. + */ +bool rt2x00queue_for_each_entry(struct data_queue *queue, + enum queue_index start, + enum queue_index end, + void *data, + bool (*fn)(struct queue_entry *entry, + void *data)); + +/** + * rt2x00queue_empty - Check if the queue is empty. + * @queue: Queue to check if empty. + */ +static inline int rt2x00queue_empty(struct data_queue *queue) +{ + return queue->length == 0; +} + +/** + * rt2x00queue_full - Check if the queue is full. + * @queue: Queue to check if full. + */ +static inline int rt2x00queue_full(struct data_queue *queue) +{ + return queue->length == queue->limit; +} + +/** + * rt2x00queue_free - Check the number of available entries in queue. + * @queue: Queue to check. + */ +static inline int rt2x00queue_available(struct data_queue *queue) +{ + return queue->limit - queue->length; +} + +/** + * rt2x00queue_threshold - Check if the queue is below threshold + * @queue: Queue to check. + */ +static inline int rt2x00queue_threshold(struct data_queue *queue) +{ + return rt2x00queue_available(queue) < queue->threshold; +} +/** + * rt2x00queue_dma_timeout - Check if a timeout occurred for DMA transfers + * @entry: Queue entry to check. + */ +static inline int rt2x00queue_dma_timeout(struct queue_entry *entry) +{ + if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + return false; + return time_after(jiffies, entry->last_action + msecs_to_jiffies(100)); +} + +/** + * _rt2x00_desc_read - Read a word from the hardware descriptor. + * @desc: Base descriptor address + * @word: Word index from where the descriptor should be read. + * @value: Address where the descriptor value should be written into. + */ +static inline void _rt2x00_desc_read(__le32 *desc, const u8 word, __le32 *value) +{ + *value = desc[word]; +} + +/** + * rt2x00_desc_read - Read a word from the hardware descriptor, this + * function will take care of the byte ordering. + * @desc: Base descriptor address + * @word: Word index from where the descriptor should be read. + * @value: Address where the descriptor value should be written into. + */ +static inline void rt2x00_desc_read(__le32 *desc, const u8 word, u32 *value) +{ + __le32 tmp; + _rt2x00_desc_read(desc, word, &tmp); + *value = le32_to_cpu(tmp); +} + +/** + * rt2x00_desc_write - write a word to the hardware descriptor, this + * function will take care of the byte ordering. + * @desc: Base descriptor address + * @word: Word index from where the descriptor should be written. + * @value: Value that should be written into the descriptor. + */ +static inline void _rt2x00_desc_write(__le32 *desc, const u8 word, __le32 value) +{ + desc[word] = value; +} + +/** + * rt2x00_desc_write - write a word to the hardware descriptor. + * @desc: Base descriptor address + * @word: Word index from where the descriptor should be written. + * @value: Value that should be written into the descriptor. + */ +static inline void rt2x00_desc_write(__le32 *desc, const u8 word, u32 value) +{ + _rt2x00_desc_write(desc, word, cpu_to_le32(value)); +} + +#endif /* RT2X00QUEUE_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00reg.h b/drivers/net/wireless/ralink/rt2x00/rt2x00reg.h new file mode 100644 index 000000000000..3cc541d13d67 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00reg.h @@ -0,0 +1,277 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00 + Abstract: rt2x00 generic register information. + */ + +#ifndef RT2X00REG_H +#define RT2X00REG_H + +/* + * RX crypto status + */ +enum rx_crypto { + RX_CRYPTO_SUCCESS = 0, + RX_CRYPTO_FAIL_ICV = 1, + RX_CRYPTO_FAIL_MIC = 2, + RX_CRYPTO_FAIL_KEY = 3, +}; + +/* + * Antenna values + */ +enum antenna { + ANTENNA_SW_DIVERSITY = 0, + ANTENNA_A = 1, + ANTENNA_B = 2, + ANTENNA_HW_DIVERSITY = 3, +}; + +/* + * Led mode values. + */ +enum led_mode { + LED_MODE_DEFAULT = 0, + LED_MODE_TXRX_ACTIVITY = 1, + LED_MODE_SIGNAL_STRENGTH = 2, + LED_MODE_ASUS = 3, + LED_MODE_ALPHA = 4, +}; + +/* + * TSF sync values + */ +enum tsf_sync { + TSF_SYNC_NONE = 0, + TSF_SYNC_INFRA = 1, + TSF_SYNC_ADHOC = 2, + TSF_SYNC_AP_NONE = 3, +}; + +/* + * Device states + */ +enum dev_state { + STATE_DEEP_SLEEP = 0, + STATE_SLEEP = 1, + STATE_STANDBY = 2, + STATE_AWAKE = 3, + +/* + * Additional device states, these values are + * not strict since they are not directly passed + * into the device. + */ + STATE_RADIO_ON, + STATE_RADIO_OFF, + STATE_RADIO_IRQ_ON, + STATE_RADIO_IRQ_OFF, +}; + +/* + * IFS backoff values + */ +enum ifs { + IFS_BACKOFF = 0, + IFS_SIFS = 1, + IFS_NEW_BACKOFF = 2, + IFS_NONE = 3, +}; + +/* + * IFS backoff values for HT devices + */ +enum txop { + TXOP_HTTXOP = 0, + TXOP_PIFS = 1, + TXOP_SIFS = 2, + TXOP_BACKOFF = 3, +}; + +/* + * Cipher types for hardware encryption + */ +enum cipher { + CIPHER_NONE = 0, + CIPHER_WEP64 = 1, + CIPHER_WEP128 = 2, + CIPHER_TKIP = 3, + CIPHER_AES = 4, +/* + * The following fields were added by rt61pci and rt73usb. + */ + CIPHER_CKIP64 = 5, + CIPHER_CKIP128 = 6, + CIPHER_TKIP_NO_MIC = 7, /* Don't send to device */ + +/* + * Max cipher type. + * Note that CIPHER_NONE isn't counted, and CKIP64 and CKIP128 + * are excluded due to limitations in mac80211. + */ + CIPHER_MAX = 4, +}; + +/* + * Rate modulations + */ +enum rate_modulation { + RATE_MODE_CCK = 0, + RATE_MODE_OFDM = 1, + RATE_MODE_HT_MIX = 2, + RATE_MODE_HT_GREENFIELD = 3, +}; + +/* + * Firmware validation error codes + */ +enum firmware_errors { + FW_OK, + FW_BAD_CRC, + FW_BAD_LENGTH, + FW_BAD_VERSION, +}; + +/* + * Register handlers. + * We store the position of a register field inside a field structure, + * This will simplify the process of setting and reading a certain field + * inside the register while making sure the process remains byte order safe. + */ +struct rt2x00_field8 { + u8 bit_offset; + u8 bit_mask; +}; + +struct rt2x00_field16 { + u16 bit_offset; + u16 bit_mask; +}; + +struct rt2x00_field32 { + u32 bit_offset; + u32 bit_mask; +}; + +/* + * Power of two check, this will check + * if the mask that has been given contains and contiguous set of bits. + * Note that we cannot use the is_power_of_2() function since this + * check must be done at compile-time. + */ +#define is_power_of_two(x) ( !((x) & ((x)-1)) ) +#define low_bit_mask(x) ( ((x)-1) & ~(x) ) +#define is_valid_mask(x) is_power_of_two(1LU + (x) + low_bit_mask(x)) + +/* + * Macros to find first set bit in a variable. + * These macros behave the same as the __ffs() functions but + * the most important difference that this is done during + * compile-time rather then run-time. + */ +#define compile_ffs2(__x) \ + __builtin_choose_expr(((__x) & 0x1), 0, 1) + +#define compile_ffs4(__x) \ + __builtin_choose_expr(((__x) & 0x3), \ + (compile_ffs2((__x))), \ + (compile_ffs2((__x) >> 2) + 2)) + +#define compile_ffs8(__x) \ + __builtin_choose_expr(((__x) & 0xf), \ + (compile_ffs4((__x))), \ + (compile_ffs4((__x) >> 4) + 4)) + +#define compile_ffs16(__x) \ + __builtin_choose_expr(((__x) & 0xff), \ + (compile_ffs8((__x))), \ + (compile_ffs8((__x) >> 8) + 8)) + +#define compile_ffs32(__x) \ + __builtin_choose_expr(((__x) & 0xffff), \ + (compile_ffs16((__x))), \ + (compile_ffs16((__x) >> 16) + 16)) + +/* + * This macro will check the requirements for the FIELD{8,16,32} macros + * The mask should be a constant non-zero contiguous set of bits which + * does not exceed the given typelimit. + */ +#define FIELD_CHECK(__mask, __type) \ + BUILD_BUG_ON(!(__mask) || \ + !is_valid_mask(__mask) || \ + (__mask) != (__type)(__mask)) \ + +#define FIELD8(__mask) \ +({ \ + FIELD_CHECK(__mask, u8); \ + (struct rt2x00_field8) { \ + compile_ffs8(__mask), (__mask) \ + }; \ +}) + +#define FIELD16(__mask) \ +({ \ + FIELD_CHECK(__mask, u16); \ + (struct rt2x00_field16) { \ + compile_ffs16(__mask), (__mask) \ + }; \ +}) + +#define FIELD32(__mask) \ +({ \ + FIELD_CHECK(__mask, u32); \ + (struct rt2x00_field32) { \ + compile_ffs32(__mask), (__mask) \ + }; \ +}) + +#define SET_FIELD(__reg, __type, __field, __value)\ +({ \ + typecheck(__type, __field); \ + *(__reg) &= ~((__field).bit_mask); \ + *(__reg) |= ((__value) << \ + ((__field).bit_offset)) & \ + ((__field).bit_mask); \ +}) + +#define GET_FIELD(__reg, __type, __field) \ +({ \ + typecheck(__type, __field); \ + ((__reg) & ((__field).bit_mask)) >> \ + ((__field).bit_offset); \ +}) + +#define rt2x00_set_field32(__reg, __field, __value) \ + SET_FIELD(__reg, struct rt2x00_field32, __field, __value) +#define rt2x00_get_field32(__reg, __field) \ + GET_FIELD(__reg, struct rt2x00_field32, __field) + +#define rt2x00_set_field16(__reg, __field, __value) \ + SET_FIELD(__reg, struct rt2x00_field16, __field, __value) +#define rt2x00_get_field16(__reg, __field) \ + GET_FIELD(__reg, struct rt2x00_field16, __field) + +#define rt2x00_set_field8(__reg, __field, __value) \ + SET_FIELD(__reg, struct rt2x00_field8, __field, __value) +#define rt2x00_get_field8(__reg, __field) \ + GET_FIELD(__reg, struct rt2x00_field8, __field) + +#endif /* RT2X00REG_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c new file mode 100644 index 000000000000..69a0cdadb07f --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c @@ -0,0 +1,160 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + Copyright (C) 2004 - 2009 Felix Fietkau + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00soc + Abstract: rt2x00 generic soc device routines. + */ + +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00soc.h" + +static void rt2x00soc_free_reg(struct rt2x00_dev *rt2x00dev) +{ + kfree(rt2x00dev->rf); + rt2x00dev->rf = NULL; + + kfree(rt2x00dev->eeprom); + rt2x00dev->eeprom = NULL; + + iounmap(rt2x00dev->csr.base); +} + +static int rt2x00soc_alloc_reg(struct rt2x00_dev *rt2x00dev) +{ + struct platform_device *pdev = to_platform_device(rt2x00dev->dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + rt2x00dev->csr.base = ioremap(res->start, resource_size(res)); + if (!rt2x00dev->csr.base) + return -ENOMEM; + + rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); + if (!rt2x00dev->eeprom) + goto exit; + + rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); + if (!rt2x00dev->rf) + goto exit; + + return 0; + +exit: + rt2x00_probe_err("Failed to allocate registers\n"); + rt2x00soc_free_reg(rt2x00dev); + + return -ENOMEM; +} + +int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops) +{ + struct ieee80211_hw *hw; + struct rt2x00_dev *rt2x00dev; + int retval; + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); + if (!hw) { + rt2x00_probe_err("Failed to allocate hardware\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, hw); + + rt2x00dev = hw->priv; + rt2x00dev->dev = &pdev->dev; + rt2x00dev->ops = ops; + rt2x00dev->hw = hw; + rt2x00dev->irq = platform_get_irq(pdev, 0); + rt2x00dev->name = pdev->dev.driver->name; + + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); + + retval = rt2x00soc_alloc_reg(rt2x00dev); + if (retval) + goto exit_free_device; + + retval = rt2x00lib_probe_dev(rt2x00dev); + if (retval) + goto exit_free_reg; + + return 0; + +exit_free_reg: + rt2x00soc_free_reg(rt2x00dev); + +exit_free_device: + ieee80211_free_hw(hw); + + return retval; +} +EXPORT_SYMBOL_GPL(rt2x00soc_probe); + +int rt2x00soc_remove(struct platform_device *pdev) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Free all allocated data. + */ + rt2x00lib_remove_dev(rt2x00dev); + rt2x00soc_free_reg(rt2x00dev); + ieee80211_free_hw(hw); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00soc_remove); + +#ifdef CONFIG_PM +int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + return rt2x00lib_suspend(rt2x00dev, state); +} +EXPORT_SYMBOL_GPL(rt2x00soc_suspend); + +int rt2x00soc_resume(struct platform_device *pdev) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + return rt2x00lib_resume(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00soc_resume); +#endif /* CONFIG_PM */ + +/* + * rt2x00soc module information. + */ +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2x00 soc library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h new file mode 100644 index 000000000000..9948d355e9a4 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h @@ -0,0 +1,40 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00soc + Abstract: Data structures for the rt2x00soc module. + */ + +#ifndef RT2X00SOC_H +#define RT2X00SOC_H + +/* + * SoC driver handlers. + */ +int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops); +int rt2x00soc_remove(struct platform_device *pdev); +#ifdef CONFIG_PM +int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state); +int rt2x00soc_resume(struct platform_device *pdev); +#else +#define rt2x00soc_suspend NULL +#define rt2x00soc_resume NULL +#endif /* CONFIG_PM */ + +#endif /* RT2X00SOC_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c new file mode 100644 index 000000000000..7627af6098eb --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c @@ -0,0 +1,884 @@ +/* + Copyright (C) 2010 Willow Garage + Copyright (C) 2004 - 2010 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00usb + Abstract: rt2x00 generic usb device routines. + */ + +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00usb.h" + +/* + * Interfacing with the HW. + */ +int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, + const u8 request, const u8 requesttype, + const u16 offset, const u16 value, + void *buffer, const u16 buffer_length, + const int timeout) +{ + struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); + int status; + unsigned int pipe = + (requesttype == USB_VENDOR_REQUEST_IN) ? + usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0); + unsigned long expire = jiffies + msecs_to_jiffies(timeout); + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return -ENODEV; + + do { + status = usb_control_msg(usb_dev, pipe, request, requesttype, + value, offset, buffer, buffer_length, + timeout / 2); + if (status >= 0) + return 0; + + if (status == -ENODEV) { + /* Device has disappeared. */ + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + break; + } + } while (time_before(jiffies, expire)); + + rt2x00_err(rt2x00dev, + "Vendor Request 0x%02x failed for offset 0x%04x with error %d\n", + request, offset, status); + + return status; +} +EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request); + +int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev, + const u8 request, const u8 requesttype, + const u16 offset, void *buffer, + const u16 buffer_length, const int timeout) +{ + int status; + + BUG_ON(!mutex_is_locked(&rt2x00dev->csr_mutex)); + + /* + * Check for Cache availability. + */ + if (unlikely(!rt2x00dev->csr.cache || buffer_length > CSR_CACHE_SIZE)) { + rt2x00_err(rt2x00dev, "CSR cache not available\n"); + return -ENOMEM; + } + + if (requesttype == USB_VENDOR_REQUEST_OUT) + memcpy(rt2x00dev->csr.cache, buffer, buffer_length); + + status = rt2x00usb_vendor_request(rt2x00dev, request, requesttype, + offset, 0, rt2x00dev->csr.cache, + buffer_length, timeout); + + if (!status && requesttype == USB_VENDOR_REQUEST_IN) + memcpy(buffer, rt2x00dev->csr.cache, buffer_length); + + return status; +} +EXPORT_SYMBOL_GPL(rt2x00usb_vendor_req_buff_lock); + +int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev, + const u8 request, const u8 requesttype, + const u16 offset, void *buffer, + const u16 buffer_length) +{ + int status = 0; + unsigned char *tb; + u16 off, len, bsize; + + mutex_lock(&rt2x00dev->csr_mutex); + + tb = (char *)buffer; + off = offset; + len = buffer_length; + while (len && !status) { + bsize = min_t(u16, CSR_CACHE_SIZE, len); + status = rt2x00usb_vendor_req_buff_lock(rt2x00dev, request, + requesttype, off, tb, + bsize, REGISTER_TIMEOUT); + + tb += bsize; + len -= bsize; + off += bsize; + } + + mutex_unlock(&rt2x00dev->csr_mutex); + + return status; +} +EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_buff); + +int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, + u32 *reg) +{ + unsigned int i; + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return -ENODEV; + + for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) { + rt2x00usb_register_read_lock(rt2x00dev, offset, reg); + if (!rt2x00_get_field32(*reg, field)) + return 1; + udelay(REGISTER_BUSY_DELAY); + } + + rt2x00_err(rt2x00dev, "Indirect register access failed: offset=0x%.08x, value=0x%.08x\n", + offset, *reg); + *reg = ~0; + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00usb_regbusy_read); + + +struct rt2x00_async_read_data { + __le32 reg; + struct usb_ctrlrequest cr; + struct rt2x00_dev *rt2x00dev; + bool (*callback)(struct rt2x00_dev *, int, u32); +}; + +static void rt2x00usb_register_read_async_cb(struct urb *urb) +{ + struct rt2x00_async_read_data *rd = urb->context; + if (rd->callback(rd->rt2x00dev, urb->status, le32_to_cpu(rd->reg))) { + if (usb_submit_urb(urb, GFP_ATOMIC) < 0) + kfree(rd); + } else + kfree(rd); +} + +void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + bool (*callback)(struct rt2x00_dev*, int, u32)) +{ + struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); + struct urb *urb; + struct rt2x00_async_read_data *rd; + + rd = kmalloc(sizeof(*rd), GFP_ATOMIC); + if (!rd) + return; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + kfree(rd); + return; + } + + rd->rt2x00dev = rt2x00dev; + rd->callback = callback; + rd->cr.bRequestType = USB_VENDOR_REQUEST_IN; + rd->cr.bRequest = USB_MULTI_READ; + rd->cr.wValue = 0; + rd->cr.wIndex = cpu_to_le16(offset); + rd->cr.wLength = cpu_to_le16(sizeof(u32)); + + usb_fill_control_urb(urb, usb_dev, usb_rcvctrlpipe(usb_dev, 0), + (unsigned char *)(&rd->cr), &rd->reg, sizeof(rd->reg), + rt2x00usb_register_read_async_cb, rd); + if (usb_submit_urb(urb, GFP_ATOMIC) < 0) + kfree(rd); + usb_free_urb(urb); +} +EXPORT_SYMBOL_GPL(rt2x00usb_register_read_async); + +/* + * TX data handlers. + */ +static void rt2x00usb_work_txdone_entry(struct queue_entry *entry) +{ + /* + * If the transfer to hardware succeeded, it does not mean the + * frame was send out correctly. It only means the frame + * was successfully pushed to the hardware, we have no + * way to determine the transmission status right now. + * (Only indirectly by looking at the failed TX counters + * in the register). + */ + if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) + rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE); + else + rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN); +} + +static void rt2x00usb_work_txdone(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, txdone_work); + struct data_queue *queue; + struct queue_entry *entry; + + tx_queue_for_each(rt2x00dev, queue) { + while (!rt2x00queue_empty(queue)) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + + if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + break; + + rt2x00usb_work_txdone_entry(entry); + } + } +} + +static void rt2x00usb_interrupt_txdone(struct urb *urb) +{ + struct queue_entry *entry = (struct queue_entry *)urb->context; + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + + if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + return; + /* + * Check if the frame was correctly uploaded + */ + if (urb->status) + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + /* + * Report the frame as DMA done + */ + rt2x00lib_dmadone(entry); + + if (rt2x00dev->ops->lib->tx_dma_done) + rt2x00dev->ops->lib->tx_dma_done(entry); + /* + * Schedule the delayed work for reading the TX status + * from the device. + */ + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TXSTATUS_FIFO) || + !kfifo_is_empty(&rt2x00dev->txstatus_fifo)) + queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); +} + +static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void *data) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); + struct queue_entry_priv_usb *entry_priv = entry->priv_data; + u32 length; + int status; + + if (!test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags) || + test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + return false; + + /* + * USB devices require certain padding at the end of each frame + * and urb. Those paddings are not included in skbs. Pass entry + * to the driver to determine what the overall length should be. + */ + length = rt2x00dev->ops->lib->get_tx_data_len(entry); + + status = skb_padto(entry->skb, length); + if (unlikely(status)) { + /* TODO: report something more appropriate than IO_FAILED. */ + rt2x00_warn(rt2x00dev, "TX SKB padding error, out of memory\n"); + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + rt2x00lib_dmadone(entry); + + return false; + } + + usb_fill_bulk_urb(entry_priv->urb, usb_dev, + usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint), + entry->skb->data, length, + rt2x00usb_interrupt_txdone, entry); + + status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); + if (status) { + if (status == -ENODEV) + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + rt2x00lib_dmadone(entry); + } + + return false; +} + +/* + * RX data handlers. + */ +static void rt2x00usb_work_rxdone(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, rxdone_work); + struct queue_entry *entry; + struct skb_frame_desc *skbdesc; + u8 rxd[32]; + + while (!rt2x00queue_empty(rt2x00dev->rx)) { + entry = rt2x00queue_get_entry(rt2x00dev->rx, Q_INDEX_DONE); + + if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + break; + + /* + * Fill in desc fields of the skb descriptor + */ + skbdesc = get_skb_frame_desc(entry->skb); + skbdesc->desc = rxd; + skbdesc->desc_len = entry->queue->desc_size; + + /* + * Send the frame to rt2x00lib for further processing. + */ + rt2x00lib_rxdone(entry, GFP_KERNEL); + } +} + +static void rt2x00usb_interrupt_rxdone(struct urb *urb) +{ + struct queue_entry *entry = (struct queue_entry *)urb->context; + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + + if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + return; + + /* + * Report the frame as DMA done + */ + rt2x00lib_dmadone(entry); + + /* + * Check if the received data is simply too small + * to be actually valid, or if the urb is signaling + * a problem. + */ + if (urb->actual_length < entry->queue->desc_size || urb->status) + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + + /* + * Schedule the delayed work for reading the RX status + * from the device. + */ + queue_work(rt2x00dev->workqueue, &rt2x00dev->rxdone_work); +} + +static bool rt2x00usb_kick_rx_entry(struct queue_entry *entry, void *data) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); + struct queue_entry_priv_usb *entry_priv = entry->priv_data; + int status; + + if (test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + return false; + + rt2x00lib_dmastart(entry); + + usb_fill_bulk_urb(entry_priv->urb, usb_dev, + usb_rcvbulkpipe(usb_dev, entry->queue->usb_endpoint), + entry->skb->data, entry->skb->len, + rt2x00usb_interrupt_rxdone, entry); + + status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); + if (status) { + if (status == -ENODEV) + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + rt2x00lib_dmadone(entry); + } + + return false; +} + +void rt2x00usb_kick_queue(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + if (!rt2x00queue_empty(queue)) + rt2x00queue_for_each_entry(queue, + Q_INDEX_DONE, + Q_INDEX, + NULL, + rt2x00usb_kick_tx_entry); + break; + case QID_RX: + if (!rt2x00queue_full(queue)) + rt2x00queue_for_each_entry(queue, + Q_INDEX, + Q_INDEX_DONE, + NULL, + rt2x00usb_kick_rx_entry); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2x00usb_kick_queue); + +static bool rt2x00usb_flush_entry(struct queue_entry *entry, void *data) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct queue_entry_priv_usb *entry_priv = entry->priv_data; + struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; + + if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + return false; + + usb_kill_urb(entry_priv->urb); + + /* + * Kill guardian urb (if required by driver). + */ + if ((entry->queue->qid == QID_BEACON) && + (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD))) + usb_kill_urb(bcn_priv->guardian_urb); + + return false; +} + +void rt2x00usb_flush_queue(struct data_queue *queue, bool drop) +{ + struct work_struct *completion; + unsigned int i; + + if (drop) + rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, NULL, + rt2x00usb_flush_entry); + + /* + * Obtain the queue completion handler + */ + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + completion = &queue->rt2x00dev->txdone_work; + break; + case QID_RX: + completion = &queue->rt2x00dev->rxdone_work; + break; + default: + return; + } + + for (i = 0; i < 10; i++) { + /* + * Check if the driver is already done, otherwise we + * have to sleep a little while to give the driver/hw + * the oppurtunity to complete interrupt process itself. + */ + if (rt2x00queue_empty(queue)) + break; + + /* + * Schedule the completion handler manually, when this + * worker function runs, it should cleanup the queue. + */ + queue_work(queue->rt2x00dev->workqueue, completion); + + /* + * Wait for a little while to give the driver + * the oppurtunity to recover itself. + */ + msleep(10); + } +} +EXPORT_SYMBOL_GPL(rt2x00usb_flush_queue); + +static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) +{ + rt2x00_warn(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced forced reset\n", + queue->qid); + + rt2x00queue_stop_queue(queue); + rt2x00queue_flush_queue(queue, true); + rt2x00queue_start_queue(queue); +} + +static int rt2x00usb_dma_timeout(struct data_queue *queue) +{ + struct queue_entry *entry; + + entry = rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE); + return rt2x00queue_dma_timeout(entry); +} + +void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + tx_queue_for_each(rt2x00dev, queue) { + if (!rt2x00queue_empty(queue)) { + if (rt2x00usb_dma_timeout(queue)) + rt2x00usb_watchdog_tx_dma(queue); + } + } +} +EXPORT_SYMBOL_GPL(rt2x00usb_watchdog); + +/* + * Radio handlers + */ +void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + rt2x00usb_vendor_request_sw(rt2x00dev, USB_RX_CONTROL, 0, 0, + REGISTER_TIMEOUT); +} +EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio); + +/* + * Device initialization handlers. + */ +void rt2x00usb_clear_entry(struct queue_entry *entry) +{ + entry->flags = 0; + + if (entry->queue->qid == QID_RX) + rt2x00usb_kick_rx_entry(entry, NULL); +} +EXPORT_SYMBOL_GPL(rt2x00usb_clear_entry); + +static void rt2x00usb_assign_endpoint(struct data_queue *queue, + struct usb_endpoint_descriptor *ep_desc) +{ + struct usb_device *usb_dev = to_usb_device_intf(queue->rt2x00dev->dev); + int pipe; + + queue->usb_endpoint = usb_endpoint_num(ep_desc); + + if (queue->qid == QID_RX) { + pipe = usb_rcvbulkpipe(usb_dev, queue->usb_endpoint); + queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 0); + } else { + pipe = usb_sndbulkpipe(usb_dev, queue->usb_endpoint); + queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 1); + } + + if (!queue->usb_maxpacket) + queue->usb_maxpacket = 1; +} + +static int rt2x00usb_find_endpoints(struct rt2x00_dev *rt2x00dev) +{ + struct usb_interface *intf = to_usb_interface(rt2x00dev->dev); + struct usb_host_interface *intf_desc = intf->cur_altsetting; + struct usb_endpoint_descriptor *ep_desc; + struct data_queue *queue = rt2x00dev->tx; + struct usb_endpoint_descriptor *tx_ep_desc = NULL; + unsigned int i; + + /* + * Walk through all available endpoints to search for "bulk in" + * and "bulk out" endpoints. When we find such endpoints collect + * the information we need from the descriptor and assign it + * to the queue. + */ + for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { + ep_desc = &intf_desc->endpoint[i].desc; + + if (usb_endpoint_is_bulk_in(ep_desc)) { + rt2x00usb_assign_endpoint(rt2x00dev->rx, ep_desc); + } else if (usb_endpoint_is_bulk_out(ep_desc) && + (queue != queue_end(rt2x00dev))) { + rt2x00usb_assign_endpoint(queue, ep_desc); + queue = queue_next(queue); + + tx_ep_desc = ep_desc; + } + } + + /* + * At least 1 endpoint for RX and 1 endpoint for TX must be available. + */ + if (!rt2x00dev->rx->usb_endpoint || !rt2x00dev->tx->usb_endpoint) { + rt2x00_err(rt2x00dev, "Bulk-in/Bulk-out endpoints not found\n"); + return -EPIPE; + } + + /* + * It might be possible not all queues have a dedicated endpoint. + * Loop through all TX queues and copy the endpoint information + * which we have gathered from already assigned endpoints. + */ + txall_queue_for_each(rt2x00dev, queue) { + if (!queue->usb_endpoint) + rt2x00usb_assign_endpoint(queue, tx_ep_desc); + } + + return 0; +} + +static int rt2x00usb_alloc_entries(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + struct queue_entry_priv_usb *entry_priv; + struct queue_entry_priv_usb_bcn *bcn_priv; + unsigned int i; + + for (i = 0; i < queue->limit; i++) { + entry_priv = queue->entries[i].priv_data; + entry_priv->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!entry_priv->urb) + return -ENOMEM; + } + + /* + * If this is not the beacon queue or + * no guardian byte was required for the beacon, + * then we are done. + */ + if (queue->qid != QID_BEACON || + !rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD)) + return 0; + + for (i = 0; i < queue->limit; i++) { + bcn_priv = queue->entries[i].priv_data; + bcn_priv->guardian_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!bcn_priv->guardian_urb) + return -ENOMEM; + } + + return 0; +} + +static void rt2x00usb_free_entries(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + struct queue_entry_priv_usb *entry_priv; + struct queue_entry_priv_usb_bcn *bcn_priv; + unsigned int i; + + if (!queue->entries) + return; + + for (i = 0; i < queue->limit; i++) { + entry_priv = queue->entries[i].priv_data; + usb_kill_urb(entry_priv->urb); + usb_free_urb(entry_priv->urb); + } + + /* + * If this is not the beacon queue or + * no guardian byte was required for the beacon, + * then we are done. + */ + if (queue->qid != QID_BEACON || + !rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD)) + return; + + for (i = 0; i < queue->limit; i++) { + bcn_priv = queue->entries[i].priv_data; + usb_kill_urb(bcn_priv->guardian_urb); + usb_free_urb(bcn_priv->guardian_urb); + } +} + +int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + int status; + + /* + * Find endpoints for each queue + */ + status = rt2x00usb_find_endpoints(rt2x00dev); + if (status) + goto exit; + + /* + * Allocate DMA + */ + queue_for_each(rt2x00dev, queue) { + status = rt2x00usb_alloc_entries(queue); + if (status) + goto exit; + } + + return 0; + +exit: + rt2x00usb_uninitialize(rt2x00dev); + + return status; +} +EXPORT_SYMBOL_GPL(rt2x00usb_initialize); + +void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + queue_for_each(rt2x00dev, queue) + rt2x00usb_free_entries(queue); +} +EXPORT_SYMBOL_GPL(rt2x00usb_uninitialize); + +/* + * USB driver handlers. + */ +static void rt2x00usb_free_reg(struct rt2x00_dev *rt2x00dev) +{ + kfree(rt2x00dev->rf); + rt2x00dev->rf = NULL; + + kfree(rt2x00dev->eeprom); + rt2x00dev->eeprom = NULL; + + kfree(rt2x00dev->csr.cache); + rt2x00dev->csr.cache = NULL; +} + +static int rt2x00usb_alloc_reg(struct rt2x00_dev *rt2x00dev) +{ + rt2x00dev->csr.cache = kzalloc(CSR_CACHE_SIZE, GFP_KERNEL); + if (!rt2x00dev->csr.cache) + goto exit; + + rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); + if (!rt2x00dev->eeprom) + goto exit; + + rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); + if (!rt2x00dev->rf) + goto exit; + + return 0; + +exit: + rt2x00_probe_err("Failed to allocate registers\n"); + + rt2x00usb_free_reg(rt2x00dev); + + return -ENOMEM; +} + +int rt2x00usb_probe(struct usb_interface *usb_intf, + const struct rt2x00_ops *ops) +{ + struct usb_device *usb_dev = interface_to_usbdev(usb_intf); + struct ieee80211_hw *hw; + struct rt2x00_dev *rt2x00dev; + int retval; + + usb_dev = usb_get_dev(usb_dev); + usb_reset_device(usb_dev); + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); + if (!hw) { + rt2x00_probe_err("Failed to allocate hardware\n"); + retval = -ENOMEM; + goto exit_put_device; + } + + usb_set_intfdata(usb_intf, hw); + + rt2x00dev = hw->priv; + rt2x00dev->dev = &usb_intf->dev; + rt2x00dev->ops = ops; + rt2x00dev->hw = hw; + + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); + + INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone); + INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone); + hrtimer_init(&rt2x00dev->txstatus_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + + retval = rt2x00usb_alloc_reg(rt2x00dev); + if (retval) + goto exit_free_device; + + retval = rt2x00lib_probe_dev(rt2x00dev); + if (retval) + goto exit_free_reg; + + return 0; + +exit_free_reg: + rt2x00usb_free_reg(rt2x00dev); + +exit_free_device: + ieee80211_free_hw(hw); + +exit_put_device: + usb_put_dev(usb_dev); + + usb_set_intfdata(usb_intf, NULL); + + return retval; +} +EXPORT_SYMBOL_GPL(rt2x00usb_probe); + +void rt2x00usb_disconnect(struct usb_interface *usb_intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Free all allocated data. + */ + rt2x00lib_remove_dev(rt2x00dev); + rt2x00usb_free_reg(rt2x00dev); + ieee80211_free_hw(hw); + + /* + * Free the USB device data. + */ + usb_set_intfdata(usb_intf, NULL); + usb_put_dev(interface_to_usbdev(usb_intf)); +} +EXPORT_SYMBOL_GPL(rt2x00usb_disconnect); + +#ifdef CONFIG_PM +int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state) +{ + struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); + struct rt2x00_dev *rt2x00dev = hw->priv; + + return rt2x00lib_suspend(rt2x00dev, state); +} +EXPORT_SYMBOL_GPL(rt2x00usb_suspend); + +int rt2x00usb_resume(struct usb_interface *usb_intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); + struct rt2x00_dev *rt2x00dev = hw->priv; + + return rt2x00lib_resume(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00usb_resume); +#endif /* CONFIG_PM */ + +/* + * rt2x00usb module information. + */ +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2x00 usb library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.h b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.h new file mode 100644 index 000000000000..569363da00a2 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.h @@ -0,0 +1,424 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt2x00usb + Abstract: Data structures for the rt2x00usb module. + */ + +#ifndef RT2X00USB_H +#define RT2X00USB_H + +#include + +#define to_usb_device_intf(d) \ +({ \ + struct usb_interface *intf = to_usb_interface(d); \ + interface_to_usbdev(intf); \ +}) + +/* + * For USB vendor requests we need to pass a timeout time in ms, for this we + * use the REGISTER_TIMEOUT, however when loading firmware or read EEPROM + * a higher value is required. In that case we use the REGISTER_TIMEOUT_FIRMWARE + * and EEPROM_TIMEOUT. + */ +#define REGISTER_TIMEOUT 100 +#define REGISTER_TIMEOUT_FIRMWARE 1000 +#define EEPROM_TIMEOUT 2000 + +/* + * Cache size + */ +#define CSR_CACHE_SIZE 64 + +/* + * USB request types. + */ +#define USB_VENDOR_REQUEST ( USB_TYPE_VENDOR | USB_RECIP_DEVICE ) +#define USB_VENDOR_REQUEST_IN ( USB_DIR_IN | USB_VENDOR_REQUEST ) +#define USB_VENDOR_REQUEST_OUT ( USB_DIR_OUT | USB_VENDOR_REQUEST ) + +/** + * enum rt2x00usb_vendor_request: USB vendor commands. + */ +enum rt2x00usb_vendor_request { + USB_DEVICE_MODE = 1, + USB_SINGLE_WRITE = 2, + USB_SINGLE_READ = 3, + USB_MULTI_WRITE = 6, + USB_MULTI_READ = 7, + USB_EEPROM_WRITE = 8, + USB_EEPROM_READ = 9, + USB_LED_CONTROL = 10, /* RT73USB */ + USB_RX_CONTROL = 12, +}; + +/** + * enum rt2x00usb_mode_offset: Device modes offset. + */ +enum rt2x00usb_mode_offset { + USB_MODE_RESET = 1, + USB_MODE_UNPLUG = 2, + USB_MODE_FUNCTION = 3, + USB_MODE_TEST = 4, + USB_MODE_SLEEP = 7, /* RT73USB */ + USB_MODE_FIRMWARE = 8, /* RT73USB */ + USB_MODE_WAKEUP = 9, /* RT73USB */ + USB_MODE_AUTORUN = 17, /* RT2800USB */ +}; + +/** + * rt2x00usb_vendor_request - Send register command to device + * @rt2x00dev: Pointer to &struct rt2x00_dev + * @request: USB vendor command (See &enum rt2x00usb_vendor_request) + * @requesttype: Request type &USB_VENDOR_REQUEST_* + * @offset: Register offset to perform action on + * @value: Value to write to device + * @buffer: Buffer where information will be read/written to by device + * @buffer_length: Size of &buffer + * @timeout: Operation timeout + * + * This is the main function to communicate with the device, + * the &buffer argument _must_ either be NULL or point to + * a buffer allocated by kmalloc. Failure to do so can lead + * to unexpected behavior depending on the architecture. + */ +int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, + const u8 request, const u8 requesttype, + const u16 offset, const u16 value, + void *buffer, const u16 buffer_length, + const int timeout); + +/** + * rt2x00usb_vendor_request_buff - Send register command to device (buffered) + * @rt2x00dev: Pointer to &struct rt2x00_dev + * @request: USB vendor command (See &enum rt2x00usb_vendor_request) + * @requesttype: Request type &USB_VENDOR_REQUEST_* + * @offset: Register offset to perform action on + * @buffer: Buffer where information will be read/written to by device + * @buffer_length: Size of &buffer + * + * This function will use a previously with kmalloc allocated cache + * to communicate with the device. The contents of the buffer pointer + * will be copied to this cache when writing, or read from the cache + * when reading. + * Buffers send to &rt2x00usb_vendor_request _must_ be allocated with + * kmalloc. Hence the reason for using a previously allocated cache + * which has been allocated properly. + */ +int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev, + const u8 request, const u8 requesttype, + const u16 offset, void *buffer, + const u16 buffer_length); + +/** + * rt2x00usb_vendor_request_buff - Send register command to device (buffered) + * @rt2x00dev: Pointer to &struct rt2x00_dev + * @request: USB vendor command (See &enum rt2x00usb_vendor_request) + * @requesttype: Request type &USB_VENDOR_REQUEST_* + * @offset: Register offset to perform action on + * @buffer: Buffer where information will be read/written to by device + * @buffer_length: Size of &buffer + * @timeout: Operation timeout + * + * A version of &rt2x00usb_vendor_request_buff which must be called + * if the usb_cache_mutex is already held. + */ +int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev, + const u8 request, const u8 requesttype, + const u16 offset, void *buffer, + const u16 buffer_length, const int timeout); + +/** + * rt2x00usb_vendor_request_sw - Send single register command to device + * @rt2x00dev: Pointer to &struct rt2x00_dev + * @request: USB vendor command (See &enum rt2x00usb_vendor_request) + * @offset: Register offset to perform action on + * @value: Value to write to device + * @timeout: Operation timeout + * + * Simple wrapper around rt2x00usb_vendor_request to write a single + * command to the device. Since we don't use the buffer argument we + * don't have to worry about kmalloc here. + */ +static inline int rt2x00usb_vendor_request_sw(struct rt2x00_dev *rt2x00dev, + const u8 request, + const u16 offset, + const u16 value, + const int timeout) +{ + return rt2x00usb_vendor_request(rt2x00dev, request, + USB_VENDOR_REQUEST_OUT, offset, + value, NULL, 0, timeout); +} + +/** + * rt2x00usb_eeprom_read - Read eeprom from device + * @rt2x00dev: Pointer to &struct rt2x00_dev + * @eeprom: Pointer to eeprom array to store the information in + * @length: Number of bytes to read from the eeprom + * + * Simple wrapper around rt2x00usb_vendor_request to read the eeprom + * from the device. Note that the eeprom argument _must_ be allocated using + * kmalloc for correct handling inside the kernel USB layer. + */ +static inline int rt2x00usb_eeprom_read(struct rt2x00_dev *rt2x00dev, + __le16 *eeprom, const u16 length) +{ + return rt2x00usb_vendor_request(rt2x00dev, USB_EEPROM_READ, + USB_VENDOR_REQUEST_IN, 0, 0, + eeprom, length, EEPROM_TIMEOUT); +} + +/** + * rt2x00usb_register_read - Read 32bit register word + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Pointer to where register contents should be stored + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_request_buff(). + */ +static inline void rt2x00usb_register_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + __le32 reg = 0; + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, + USB_VENDOR_REQUEST_IN, offset, + ®, sizeof(reg)); + *value = le32_to_cpu(reg); +} + +/** + * rt2x00usb_register_read_lock - Read 32bit register word + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Pointer to where register contents should be stored + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_req_buff_lock(). + */ +static inline void rt2x00usb_register_read_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + __le32 reg = 0; + rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ, + USB_VENDOR_REQUEST_IN, offset, + ®, sizeof(reg), REGISTER_TIMEOUT); + *value = le32_to_cpu(reg); +} + +/** + * rt2x00usb_register_multiread - Read 32bit register words + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Pointer to where register contents should be stored + * @length: Length of the data + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_request_buff(). + */ +static inline void rt2x00usb_register_multiread(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u32 length) +{ + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, + USB_VENDOR_REQUEST_IN, offset, + value, length); +} + +/** + * rt2x00usb_register_write - Write 32bit register word + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Data which should be written + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_request_buff(). + */ +static inline void rt2x00usb_register_write(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + __le32 reg = cpu_to_le32(value); + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, offset, + ®, sizeof(reg)); +} + +/** + * rt2x00usb_register_write_lock - Write 32bit register word + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Data which should be written + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_req_buff_lock(). + */ +static inline void rt2x00usb_register_write_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + __le32 reg = cpu_to_le32(value); + rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, offset, + ®, sizeof(reg), REGISTER_TIMEOUT); +} + +/** + * rt2x00usb_register_multiwrite - Write 32bit register words + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Data which should be written + * @length: Length of the data + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_request_buff(). + */ +static inline void rt2x00usb_register_multiwrite(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const void *value, + const u32 length) +{ + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, offset, + (void *)value, length); +} + +/** + * rt2x00usb_regbusy_read - Read from register with busy check + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @field: Field to check if register is busy + * @reg: Pointer to where register contents should be stored + * + * This function will read the given register, and checks if the + * register is busy. If it is, it will sleep for a couple of + * microseconds before reading the register again. If the register + * is not read after a certain timeout, this function will return + * FALSE. + */ +int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, + u32 *reg); + +/** + * rt2x00usb_register_read_async - Asynchronously read 32bit register word + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @callback: Functon to call when read completes. + * + * Submit a control URB to read a 32bit register. This safe to + * be called from atomic context. The callback will be called + * when the URB completes. Otherwise the function is similar + * to rt2x00usb_register_read(). + * When the callback function returns false, the memory will be cleaned up, + * when it returns true, the urb will be fired again. + */ +void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + bool (*callback)(struct rt2x00_dev*, int, u32)); + +/* + * Radio handlers + */ +void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev); + +/** + * struct queue_entry_priv_usb: Per entry USB specific information + * + * @urb: Urb structure used for device communication. + */ +struct queue_entry_priv_usb { + struct urb *urb; +}; + +/** + * struct queue_entry_priv_usb_bcn: Per TX entry USB specific information + * + * The first section should match &struct queue_entry_priv_usb exactly. + * rt2500usb can use this structure to send a guardian byte when working + * with beacons. + * + * @urb: Urb structure used for device communication. + * @guardian_data: Set to 0, used for sending the guardian data. + * @guardian_urb: Urb structure used to send the guardian data. + */ +struct queue_entry_priv_usb_bcn { + struct urb *urb; + + unsigned int guardian_data; + struct urb *guardian_urb; +}; + +/** + * rt2x00usb_kick_queue - Kick data queue + * @queue: Data queue to kick + * + * This will walk through all entries of the queue and push all pending + * frames to the hardware as a single burst. + */ +void rt2x00usb_kick_queue(struct data_queue *queue); + +/** + * rt2x00usb_flush_queue - Flush data queue + * @queue: Data queue to stop + * @drop: True to drop all pending frames. + * + * This will walk through all entries of the queue and will optionally + * kill all URB's which were send to the device, or at least wait until + * they have been returned from the device.. + */ +void rt2x00usb_flush_queue(struct data_queue *queue, bool drop); + +/** + * rt2x00usb_watchdog - Watchdog for USB communication + * @rt2x00dev: Pointer to &struct rt2x00_dev + * + * Check the health of the USB communication and determine + * if timeouts have occurred. If this is the case, this function + * will reset all communication to restore functionality again. + */ +void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev); + +/* + * Device initialization handlers. + */ +void rt2x00usb_clear_entry(struct queue_entry *entry); +int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev); +void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev); + +/* + * USB driver handlers. + */ +int rt2x00usb_probe(struct usb_interface *usb_intf, + const struct rt2x00_ops *ops); +void rt2x00usb_disconnect(struct usb_interface *usb_intf); +#ifdef CONFIG_PM +int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state); +int rt2x00usb_resume(struct usb_interface *usb_intf); +#else +#define rt2x00usb_suspend NULL +#define rt2x00usb_resume NULL +#endif /* CONFIG_PM */ + +#endif /* RT2X00USB_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt61pci.c b/drivers/net/wireless/ralink/rt2x00/rt61pci.c new file mode 100644 index 000000000000..c0e730ea1b69 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c @@ -0,0 +1,3111 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt61pci + Abstract: rt61pci device specific routines. + Supported chipsets: RT2561, RT2561s, RT2661. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2x00pci.h" +#include "rt61pci.h" + +/* + * Allow hardware encryption to be disabled. + */ +static bool modparam_nohwcrypt = false; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +/* + * Register access. + * BBP and RF register require indirect register access, + * and use the CSR registers PHY_CSR3 and PHY_CSR4 to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attempt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + */ +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), PHY_CSR3, PHY_CSR3_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), PHY_CSR4, PHY_CSR4_BUSY, (__reg)) +#define WAIT_FOR_MCU(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), H2M_MAILBOX_CSR, \ + H2M_MAILBOX_CSR_OWNER, (__reg)) + +static void rt61pci_bbp_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR3_VALUE, value); + rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); + + rt2x00mmio_register_write(rt2x00dev, PHY_CSR3, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt61pci_bbp_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u8 *value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); + + rt2x00mmio_register_write(rt2x00dev, PHY_CSR3, reg); + + WAIT_FOR_BBP(rt2x00dev, ®); + } + + *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt61pci_rf_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u32 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR4_VALUE, value); + rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, 21); + rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); + rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); + + rt2x00mmio_register_write(rt2x00dev, PHY_CSR4, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt61pci_mcu_request(struct rt2x00_dev *rt2x00dev, + const u8 command, const u8 token, + const u8 arg0, const u8 arg1) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the MCU becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_MCU(rt2x00dev, ®)) { + rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, HOST_CMD_CSR, ®); + rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); + rt2x00_set_field32(®, HOST_CMD_CSR_INTERRUPT_MCU, 1); + rt2x00mmio_register_write(rt2x00dev, HOST_CMD_CSR, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); + +} + +static void rt61pci_eepromregister_read(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); + + eeprom->reg_data_in = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_IN); + eeprom->reg_data_out = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_OUT); + eeprom->reg_data_clock = + !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_CLOCK); + eeprom->reg_chip_select = + !!rt2x00_get_field32(reg, E2PROM_CSR_CHIP_SELECT); +} + +static void rt61pci_eepromregister_write(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg = 0; + + rt2x00_set_field32(®, E2PROM_CSR_DATA_IN, !!eeprom->reg_data_in); + rt2x00_set_field32(®, E2PROM_CSR_DATA_OUT, !!eeprom->reg_data_out); + rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, + !!eeprom->reg_data_clock); + rt2x00_set_field32(®, E2PROM_CSR_CHIP_SELECT, + !!eeprom->reg_chip_select); + + rt2x00mmio_register_write(rt2x00dev, E2PROM_CSR, reg); +} + +#ifdef CONFIG_RT2X00_LIB_DEBUGFS +static const struct rt2x00debug rt61pci_rt2x00debug = { + .owner = THIS_MODULE, + .csr = { + .read = rt2x00mmio_register_read, + .write = rt2x00mmio_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, + .word_size = sizeof(u32), + .word_count = CSR_REG_SIZE / sizeof(u32), + }, + .eeprom = { + .read = rt2x00_eeprom_read, + .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .bbp = { + .read = rt61pci_bbp_read, + .write = rt61pci_bbp_write, + .word_base = BBP_BASE, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, + .rf = { + .read = rt2x00_rf_read, + .write = rt61pci_rf_write, + .word_base = RF_BASE, + .word_size = sizeof(u32), + .word_count = RF_SIZE / sizeof(u32), + }, +}; +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ + +static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, MAC_CSR13, ®); + return rt2x00_get_field32(reg, MAC_CSR13_VAL5); +} + +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt61pci_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + unsigned int a_mode = + (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); + unsigned int bg_mode = + (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); + + if (led->type == LED_TYPE_RADIO) { + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_RADIO_STATUS, enabled); + + rt61pci_mcu_request(led->rt2x00dev, MCU_LED, 0xff, + (led->rt2x00dev->led_mcu_reg & 0xff), + ((led->rt2x00dev->led_mcu_reg >> 8))); + } else if (led->type == LED_TYPE_ASSOC) { + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_LINK_BG_STATUS, bg_mode); + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_LINK_A_STATUS, a_mode); + + rt61pci_mcu_request(led->rt2x00dev, MCU_LED, 0xff, + (led->rt2x00dev->led_mcu_reg & 0xff), + ((led->rt2x00dev->led_mcu_reg >> 8))); + } else if (led->type == LED_TYPE_QUALITY) { + /* + * The brightness is divided into 6 levels (0 - 5), + * this means we need to convert the brightness + * argument into the matching level within that range. + */ + rt61pci_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff, + brightness / (LED_FULL / 6), 0); + } +} + +static int rt61pci_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + u32 reg; + + rt2x00mmio_register_read(led->rt2x00dev, MAC_CSR14, ®); + rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, *delay_on); + rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, *delay_off); + rt2x00mmio_register_write(led->rt2x00dev, MAC_CSR14, reg); + + return 0; +} + +static void rt61pci_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + enum led_type type) +{ + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt61pci_brightness_set; + led->led_dev.blink_set = rt61pci_blink_set; + led->flags = LED_INITIALIZED; +} +#endif /* CONFIG_RT2X00_LIB_LEDS */ + +/* + * Configuration handlers. + */ +static int rt61pci_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_key_entry key_entry; + struct rt2x00_field32 field; + u32 mask; + u32 reg; + + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for shared keys. We have 1 register + * with key valid bits. The goal is simple, read + * the register, if that is full we have no slots + * left. + * Note that each BSS is allowed to have up to 4 + * shared keys, so put a mask over the allowed + * entries. + */ + mask = (0xf << crypto->bssidx); + + rt2x00mmio_register_read(rt2x00dev, SEC_CSR0, ®); + reg &= mask; + + if (reg && reg == mask) + return -ENOSPC; + + key->hw_key_idx += reg ? ffz(reg) : 0; + + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + reg = SHARED_KEY_ENTRY(key->hw_key_idx); + rt2x00mmio_register_multiwrite(rt2x00dev, reg, + &key_entry, sizeof(key_entry)); + + /* + * The cipher types are stored over 2 registers. + * bssidx 0 and 1 keys are stored in SEC_CSR1 and + * bssidx 1 and 2 keys are stored in SEC_CSR5. + * Using the correct defines correctly will cause overhead, + * so just calculate the correct offset. + */ + if (key->hw_key_idx < 8) { + field.bit_offset = (3 * key->hw_key_idx); + field.bit_mask = 0x7 << field.bit_offset; + + rt2x00mmio_register_read(rt2x00dev, SEC_CSR1, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt2x00mmio_register_write(rt2x00dev, SEC_CSR1, reg); + } else { + field.bit_offset = (3 * (key->hw_key_idx - 8)); + field.bit_mask = 0x7 << field.bit_offset; + + rt2x00mmio_register_read(rt2x00dev, SEC_CSR5, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt2x00mmio_register_write(rt2x00dev, SEC_CSR5, reg); + } + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided separately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } + + /* + * SEC_CSR0 contains only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead, we use + * a calculation to determine the correct bit directly. + */ + mask = 1 << key->hw_key_idx; + + rt2x00mmio_register_read(rt2x00dev, SEC_CSR0, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00mmio_register_write(rt2x00dev, SEC_CSR0, reg); + + return 0; +} + +static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_pairwise_ta_entry addr_entry; + struct hw_key_entry key_entry; + u32 mask; + u32 reg; + + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for pairwise keys. We have 2 registers + * with key valid bits. The goal is simple: read + * the first register. If that is full, move to + * the next register. + * When both registers are full, we drop the key. + * Otherwise, we use the first invalid entry. + */ + rt2x00mmio_register_read(rt2x00dev, SEC_CSR2, ®); + if (reg && reg == ~0) { + key->hw_key_idx = 32; + rt2x00mmio_register_read(rt2x00dev, SEC_CSR3, ®); + if (reg && reg == ~0) + return -ENOSPC; + } + + key->hw_key_idx += reg ? ffz(reg) : 0; + + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + memset(&addr_entry, 0, sizeof(addr_entry)); + memcpy(&addr_entry, crypto->address, ETH_ALEN); + addr_entry.cipher = crypto->cipher; + + reg = PAIRWISE_KEY_ENTRY(key->hw_key_idx); + rt2x00mmio_register_multiwrite(rt2x00dev, reg, + &key_entry, sizeof(key_entry)); + + reg = PAIRWISE_TA_ENTRY(key->hw_key_idx); + rt2x00mmio_register_multiwrite(rt2x00dev, reg, + &addr_entry, sizeof(addr_entry)); + + /* + * Enable pairwise lookup table for given BSS idx. + * Without this, received frames will not be decrypted + * by the hardware. + */ + rt2x00mmio_register_read(rt2x00dev, SEC_CSR4, ®); + reg |= (1 << crypto->bssidx); + rt2x00mmio_register_write(rt2x00dev, SEC_CSR4, reg); + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided separately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } + + /* + * SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead, we use + * a calculation to determine the correct bit directly. + */ + if (key->hw_key_idx < 32) { + mask = 1 << key->hw_key_idx; + + rt2x00mmio_register_read(rt2x00dev, SEC_CSR2, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00mmio_register_write(rt2x00dev, SEC_CSR2, reg); + } else { + mask = 1 << (key->hw_key_idx - 32); + + rt2x00mmio_register_read(rt2x00dev, SEC_CSR3, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00mmio_register_write(rt2x00dev, SEC_CSR3, reg); + } + + return 0; +} + +static void rt61pci_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) +{ + u32 reg; + + /* + * Start configuration steps. + * Note that the version error will always be dropped + * and broadcast frames will always be accepted since + * there is no filter for it at this time. + */ + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, + !(filter_flags & (FIF_CONTROL | FIF_PSPOLL))); + rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, 1); + rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, + !rt2x00dev->intf_ap_count); + rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); + rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, + !(filter_flags & FIF_ALLMULTI)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_BROADCAST, 0); + rt2x00_set_field32(®, TXRX_CSR0_DROP_ACK_CTS, + !(filter_flags & FIF_CONTROL)); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); +} + +static void rt61pci_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags) +{ + u32 reg; + + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable synchronisation. + */ + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, conf->sync); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + } + + if (flags & CONFIG_UPDATE_MAC) { + reg = le32_to_cpu(conf->mac[1]); + rt2x00_set_field32(®, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); + conf->mac[1] = cpu_to_le32(reg); + + rt2x00mmio_register_multiwrite(rt2x00dev, MAC_CSR2, + conf->mac, sizeof(conf->mac)); + } + + if (flags & CONFIG_UPDATE_BSSID) { + reg = le32_to_cpu(conf->bssid[1]); + rt2x00_set_field32(®, MAC_CSR5_BSS_ID_MASK, 3); + conf->bssid[1] = cpu_to_le32(reg); + + rt2x00mmio_register_multiwrite(rt2x00dev, MAC_CSR4, + conf->bssid, + sizeof(conf->bssid)); + } +} + +static void rt61pci_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed) +{ + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, 0x32); + rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, + !!erp->short_preamble); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR4, reg); + } + + if (changed & BSS_CHANGED_BASIC_RATES) + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR5, + erp->basic_rates); + + if (changed & BSS_CHANGED_BEACON_INT) { + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, + erp->beacon_int * 16); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2x00mmio_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, erp->slot_time); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR9, reg); + + rt2x00mmio_register_read(rt2x00dev, MAC_CSR8, ®); + rt2x00_set_field32(®, MAC_CSR8_SIFS, erp->sifs); + rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); + rt2x00_set_field32(®, MAC_CSR8_EIFS, erp->eifs); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR8, reg); + } +} + +static void rt61pci_config_antenna_5x(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) +{ + u8 r3; + u8 r4; + u8 r77; + + rt61pci_bbp_read(rt2x00dev, 3, &r3); + rt61pci_bbp_read(rt2x00dev, 4, &r4); + rt61pci_bbp_read(rt2x00dev, 77, &r77); + + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF5325)); + + /* + * Configure the RX antenna. + */ + switch (ant->rx) { + case ANTENNA_HW_DIVERSITY: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); + rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, + (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ)); + break; + case ANTENNA_A: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); + rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); + else + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); + rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); + else + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); + break; + } + + rt61pci_bbp_write(rt2x00dev, 77, r77); + rt61pci_bbp_write(rt2x00dev, 3, r3); + rt61pci_bbp_write(rt2x00dev, 4, r4); +} + +static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) +{ + u8 r3; + u8 r4; + u8 r77; + + rt61pci_bbp_read(rt2x00dev, 3, &r3); + rt61pci_bbp_read(rt2x00dev, 4, &r4); + rt61pci_bbp_read(rt2x00dev, 77, &r77); + + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529)); + rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, + !rt2x00_has_cap_frame_type(rt2x00dev)); + + /* + * Configure the RX antenna. + */ + switch (ant->rx) { + case ANTENNA_HW_DIVERSITY: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); + break; + case ANTENNA_A: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); + break; + } + + rt61pci_bbp_write(rt2x00dev, 77, r77); + rt61pci_bbp_write(rt2x00dev, 3, r3); + rt61pci_bbp_write(rt2x00dev, 4, r4); +} + +static void rt61pci_config_antenna_2529_rx(struct rt2x00_dev *rt2x00dev, + const int p1, const int p2) +{ + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, MAC_CSR13, ®); + + rt2x00_set_field32(®, MAC_CSR13_DIR4, 0); + rt2x00_set_field32(®, MAC_CSR13_VAL4, p1); + + rt2x00_set_field32(®, MAC_CSR13_DIR3, 0); + rt2x00_set_field32(®, MAC_CSR13_VAL3, !p2); + + rt2x00mmio_register_write(rt2x00dev, MAC_CSR13, reg); +} + +static void rt61pci_config_antenna_2529(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) +{ + u8 r3; + u8 r4; + u8 r77; + + rt61pci_bbp_read(rt2x00dev, 3, &r3); + rt61pci_bbp_read(rt2x00dev, 4, &r4); + rt61pci_bbp_read(rt2x00dev, 77, &r77); + + /* + * Configure the RX antenna. + */ + switch (ant->rx) { + case ANTENNA_A: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); + rt61pci_config_antenna_2529_rx(rt2x00dev, 0, 0); + break; + case ANTENNA_HW_DIVERSITY: + /* + * FIXME: Antenna selection for the rf 2529 is very confusing + * in the legacy driver. Just default to antenna B until the + * legacy code can be properly translated into rt2x00 code. + */ + case ANTENNA_B: + default: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); + rt61pci_config_antenna_2529_rx(rt2x00dev, 1, 1); + break; + } + + rt61pci_bbp_write(rt2x00dev, 77, r77); + rt61pci_bbp_write(rt2x00dev, 3, r3); + rt61pci_bbp_write(rt2x00dev, 4, r4); +} + +struct antenna_sel { + u8 word; + /* + * value[0] -> non-LNA + * value[1] -> LNA + */ + u8 value[2]; +}; + +static const struct antenna_sel antenna_sel_a[] = { + { 96, { 0x58, 0x78 } }, + { 104, { 0x38, 0x48 } }, + { 75, { 0xfe, 0x80 } }, + { 86, { 0xfe, 0x80 } }, + { 88, { 0xfe, 0x80 } }, + { 35, { 0x60, 0x60 } }, + { 97, { 0x58, 0x58 } }, + { 98, { 0x58, 0x58 } }, +}; + +static const struct antenna_sel antenna_sel_bg[] = { + { 96, { 0x48, 0x68 } }, + { 104, { 0x2c, 0x3c } }, + { 75, { 0xfe, 0x80 } }, + { 86, { 0xfe, 0x80 } }, + { 88, { 0xfe, 0x80 } }, + { 35, { 0x50, 0x50 } }, + { 97, { 0x48, 0x48 } }, + { 98, { 0x48, 0x48 } }, +}; + +static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) +{ + const struct antenna_sel *sel; + unsigned int lna; + unsigned int i; + u32 reg; + + /* + * We should never come here because rt2x00lib is supposed + * to catch this and send us the correct antenna explicitely. + */ + BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || + ant->tx == ANTENNA_SW_DIVERSITY); + + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { + sel = antenna_sel_a; + lna = rt2x00_has_cap_external_lna_a(rt2x00dev); + } else { + sel = antenna_sel_bg; + lna = rt2x00_has_cap_external_lna_bg(rt2x00dev); + } + + for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) + rt61pci_bbp_write(rt2x00dev, sel[i].word, sel[i].value[lna]); + + rt2x00mmio_register_read(rt2x00dev, PHY_CSR0, ®); + + rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, + rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); + rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, + rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); + + rt2x00mmio_register_write(rt2x00dev, PHY_CSR0, reg); + + if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325)) + rt61pci_config_antenna_5x(rt2x00dev, ant); + else if (rt2x00_rf(rt2x00dev, RF2527)) + rt61pci_config_antenna_2x(rt2x00dev, ant); + else if (rt2x00_rf(rt2x00dev, RF2529)) { + if (rt2x00_has_cap_double_antenna(rt2x00dev)) + rt61pci_config_antenna_2x(rt2x00dev, ant); + else + rt61pci_config_antenna_2529(rt2x00dev, ant); + } +} + +static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u16 eeprom; + short lna_gain = 0; + + if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) + lna_gain += 14; + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); + } else { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) + lna_gain += 14; + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); + } + + rt2x00dev->lna_gain = lna_gain; +} + +static void rt61pci_config_channel(struct rt2x00_dev *rt2x00dev, + struct rf_channel *rf, const int txpower) +{ + u8 r3; + u8 r94; + u8 smart; + + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); + rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); + + smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)); + + rt61pci_bbp_read(rt2x00dev, 3, &r3); + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); + rt61pci_bbp_write(rt2x00dev, 3, r3); + + r94 = 6; + if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94)) + r94 += txpower - MAX_TXPOWER; + else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94)) + r94 += txpower; + rt61pci_bbp_write(rt2x00dev, 94, r94); + + rt61pci_rf_write(rt2x00dev, 1, rf->rf1); + rt61pci_rf_write(rt2x00dev, 2, rf->rf2); + rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt61pci_rf_write(rt2x00dev, 4, rf->rf4); + + udelay(200); + + rt61pci_rf_write(rt2x00dev, 1, rf->rf1); + rt61pci_rf_write(rt2x00dev, 2, rf->rf2); + rt61pci_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); + rt61pci_rf_write(rt2x00dev, 4, rf->rf4); + + udelay(200); + + rt61pci_rf_write(rt2x00dev, 1, rf->rf1); + rt61pci_rf_write(rt2x00dev, 2, rf->rf2); + rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt61pci_rf_write(rt2x00dev, 4, rf->rf4); + + msleep(1); +} + +static void rt61pci_config_txpower(struct rt2x00_dev *rt2x00dev, + const int txpower) +{ + struct rf_channel rf; + + rt2x00_rf_read(rt2x00dev, 1, &rf.rf1); + rt2x00_rf_read(rt2x00dev, 2, &rf.rf2); + rt2x00_rf_read(rt2x00dev, 3, &rf.rf3); + rt2x00_rf_read(rt2x00dev, 4, &rf.rf4); + + rt61pci_config_channel(rt2x00dev, &rf, txpower); +} + +static void rt61pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_DOWN, 1); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_STEP, 0); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_FALLBACK_CCK, 0); + rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, + libconf->conf->long_frame_max_tx_count); + rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, + libconf->conf->short_frame_max_tx_count); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR4, reg); +} + +static void rt61pci_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; + u32 reg; + + if (state == STATE_SLEEP) { + rt2x00mmio_register_read(rt2x00dev, MAC_CSR11, ®); + rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, + rt2x00dev->beacon_int - 10); + rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, + libconf->conf->listen_interval - 1); + rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 5); + + /* We must first disable autowake before it can be enabled */ + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR11, reg); + + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR11, reg); + + rt2x00mmio_register_write(rt2x00dev, SOFT_RESET_CSR, + 0x00000005); + rt2x00mmio_register_write(rt2x00dev, IO_CNTL_CSR, 0x0000001c); + rt2x00mmio_register_write(rt2x00dev, PCI_USEC_CSR, 0x00000060); + + rt61pci_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0, 0); + } else { + rt2x00mmio_register_read(rt2x00dev, MAC_CSR11, ®); + rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, 0); + rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, 0); + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); + rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 0); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR11, reg); + + rt2x00mmio_register_write(rt2x00dev, SOFT_RESET_CSR, + 0x00000007); + rt2x00mmio_register_write(rt2x00dev, IO_CNTL_CSR, 0x00000018); + rt2x00mmio_register_write(rt2x00dev, PCI_USEC_CSR, 0x00000020); + + rt61pci_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 0); + } +} + +static void rt61pci_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags) +{ + /* Always recalculate LNA gain before changing configuration */ + rt61pci_config_lna_gain(rt2x00dev, libconf); + + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) + rt61pci_config_channel(rt2x00dev, &libconf->rf, + libconf->conf->power_level); + if ((flags & IEEE80211_CONF_CHANGE_POWER) && + !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) + rt61pci_config_txpower(rt2x00dev, libconf->conf->power_level); + if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + rt61pci_config_retry_limit(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt61pci_config_ps(rt2x00dev, libconf); +} + +/* + * Link tuning + */ +static void rt61pci_link_stats(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) +{ + u32 reg; + + /* + * Update FCS error count from register. + */ + rt2x00mmio_register_read(rt2x00dev, STA_CSR0, ®); + qual->rx_failed = rt2x00_get_field32(reg, STA_CSR0_FCS_ERROR); + + /* + * Update False CCA count from register. + */ + rt2x00mmio_register_read(rt2x00dev, STA_CSR1, ®); + qual->false_cca = rt2x00_get_field32(reg, STA_CSR1_FALSE_CCA_ERROR); +} + +static inline void rt61pci_set_vgc(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, u8 vgc_level) +{ + if (qual->vgc_level != vgc_level) { + rt61pci_bbp_write(rt2x00dev, 17, vgc_level); + qual->vgc_level = vgc_level; + qual->vgc_level_reg = vgc_level; + } +} + +static void rt61pci_reset_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) +{ + rt61pci_set_vgc(rt2x00dev, qual, 0x20); +} + +static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, const u32 count) +{ + u8 up_bound; + u8 low_bound; + + /* + * Determine r17 bounds. + */ + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { + low_bound = 0x28; + up_bound = 0x48; + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { + low_bound += 0x10; + up_bound += 0x10; + } + } else { + low_bound = 0x20; + up_bound = 0x40; + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { + low_bound += 0x10; + up_bound += 0x10; + } + } + + /* + * If we are not associated, we should go straight to the + * dynamic CCA tuning. + */ + if (!rt2x00dev->intf_associated) + goto dynamic_cca_tune; + + /* + * Special big-R17 for very short distance + */ + if (qual->rssi >= -35) { + rt61pci_set_vgc(rt2x00dev, qual, 0x60); + return; + } + + /* + * Special big-R17 for short distance + */ + if (qual->rssi >= -58) { + rt61pci_set_vgc(rt2x00dev, qual, up_bound); + return; + } + + /* + * Special big-R17 for middle-short distance + */ + if (qual->rssi >= -66) { + rt61pci_set_vgc(rt2x00dev, qual, low_bound + 0x10); + return; + } + + /* + * Special mid-R17 for middle distance + */ + if (qual->rssi >= -74) { + rt61pci_set_vgc(rt2x00dev, qual, low_bound + 0x08); + return; + } + + /* + * Special case: Change up_bound based on the rssi. + * Lower up_bound when rssi is weaker then -74 dBm. + */ + up_bound -= 2 * (-74 - qual->rssi); + if (low_bound > up_bound) + up_bound = low_bound; + + if (qual->vgc_level > up_bound) { + rt61pci_set_vgc(rt2x00dev, qual, up_bound); + return; + } + +dynamic_cca_tune: + + /* + * r17 does not yet exceed upper limit, continue and base + * the r17 tuning on the false CCA count. + */ + if ((qual->false_cca > 512) && (qual->vgc_level < up_bound)) + rt61pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level); + else if ((qual->false_cca < 100) && (qual->vgc_level > low_bound)) + rt61pci_set_vgc(rt2x00dev, qual, --qual->vgc_level); +} + +/* + * Queue handlers. + */ +static void rt61pci_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + break; + default: + break; + } +} + +static void rt61pci_kick_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC0, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_VI: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC1, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_BE: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC2, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_BK: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC3, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + default: + break; + } +} + +static void rt61pci_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC0, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_VI: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC1, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_BE: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC2, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_BK: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC3, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 1); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Wait for possibly running tbtt tasklets. + */ + tasklet_kill(&rt2x00dev->tbtt_tasklet); + break; + default: + break; + } +} + +/* + * Firmware functions + */ +static char *rt61pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) +{ + u16 chip; + char *fw_name; + + pci_read_config_word(to_pci_dev(rt2x00dev->dev), PCI_DEVICE_ID, &chip); + switch (chip) { + case RT2561_PCI_ID: + fw_name = FIRMWARE_RT2561; + break; + case RT2561s_PCI_ID: + fw_name = FIRMWARE_RT2561s; + break; + case RT2661_PCI_ID: + fw_name = FIRMWARE_RT2661; + break; + default: + fw_name = NULL; + break; + } + + return fw_name; +} + +static int rt61pci_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + u16 fw_crc; + u16 crc; + + /* + * Only support 8kb firmware files. + */ + if (len != 8192) + return FW_BAD_LENGTH; + + /* + * The last 2 bytes in the firmware array are the crc checksum itself. + * This means that we should never pass those 2 bytes to the crc + * algorithm. + */ + fw_crc = (data[len - 2] << 8 | data[len - 1]); + + /* + * Use the crc itu-t algorithm. + */ + crc = crc_itu_t(0, data, len - 2); + crc = crc_itu_t_byte(crc, 0); + crc = crc_itu_t_byte(crc, 0); + + return (fw_crc == crc) ? FW_OK : FW_BAD_CRC; +} + +static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + int i; + u32 reg; + + /* + * Wait for stable hardware. + */ + for (i = 0; i < 100; i++) { + rt2x00mmio_register_read(rt2x00dev, MAC_CSR0, ®); + if (reg) + break; + msleep(1); + } + + if (!reg) { + rt2x00_err(rt2x00dev, "Unstable hardware\n"); + return -EBUSY; + } + + /* + * Prepare MCU and mailbox for firmware loading. + */ + reg = 0; + rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 1); + rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + rt2x00mmio_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + rt2x00mmio_register_write(rt2x00dev, HOST_CMD_CSR, 0); + + /* + * Write firmware to device. + */ + reg = 0; + rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 1); + rt2x00_set_field32(®, MCU_CNTL_CSR_SELECT_BANK, 1); + rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + + rt2x00mmio_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, + data, len); + + rt2x00_set_field32(®, MCU_CNTL_CSR_SELECT_BANK, 0); + rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + + rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 0); + rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + + for (i = 0; i < 100; i++) { + rt2x00mmio_register_read(rt2x00dev, MCU_CNTL_CSR, ®); + if (rt2x00_get_field32(reg, MCU_CNTL_CSR_READY)) + break; + msleep(1); + } + + if (i == 100) { + rt2x00_err(rt2x00dev, "MCU Control register not ready\n"); + return -EBUSY; + } + + /* + * Hardware needs another millisecond before it is ready. + */ + msleep(1); + + /* + * Reset MAC and BBP registers. + */ + reg = 0; + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); + + return 0; +} + +/* + * Initialization functions. + */ +static bool rt61pci_get_entry_state(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 0, &word); + + return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); + + return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + rt2x00_get_field32(word, TXD_W0_VALID)); + } +} + +static void rt61pci_clear_entry(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 5, &word); + rt2x00_set_field32(&word, RXD_W5_BUFFER_PHYSICAL_ADDRESS, + skbdesc->skb_dma); + rt2x00_desc_write(entry_priv->desc, 5, word); + + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(entry_priv->desc, 0, word); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); + rt2x00_desc_write(entry_priv->desc, 0, word); + } +} + +static int rt61pci_init_queues(struct rt2x00_dev *rt2x00dev) +{ + struct queue_entry_priv_mmio *entry_priv; + u32 reg; + + /* + * Initialize registers. + */ + rt2x00mmio_register_read(rt2x00dev, TX_RING_CSR0, ®); + rt2x00_set_field32(®, TX_RING_CSR0_AC0_RING_SIZE, + rt2x00dev->tx[0].limit); + rt2x00_set_field32(®, TX_RING_CSR0_AC1_RING_SIZE, + rt2x00dev->tx[1].limit); + rt2x00_set_field32(®, TX_RING_CSR0_AC2_RING_SIZE, + rt2x00dev->tx[2].limit); + rt2x00_set_field32(®, TX_RING_CSR0_AC3_RING_SIZE, + rt2x00dev->tx[3].limit); + rt2x00mmio_register_write(rt2x00dev, TX_RING_CSR0, reg); + + rt2x00mmio_register_read(rt2x00dev, TX_RING_CSR1, ®); + rt2x00_set_field32(®, TX_RING_CSR1_TXD_SIZE, + rt2x00dev->tx[0].desc_size / 4); + rt2x00mmio_register_write(rt2x00dev, TX_RING_CSR1, reg); + + entry_priv = rt2x00dev->tx[0].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, AC0_BASE_CSR, ®); + rt2x00_set_field32(®, AC0_BASE_CSR_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, AC0_BASE_CSR, reg); + + entry_priv = rt2x00dev->tx[1].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, AC1_BASE_CSR, ®); + rt2x00_set_field32(®, AC1_BASE_CSR_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, AC1_BASE_CSR, reg); + + entry_priv = rt2x00dev->tx[2].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, AC2_BASE_CSR, ®); + rt2x00_set_field32(®, AC2_BASE_CSR_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, AC2_BASE_CSR, reg); + + entry_priv = rt2x00dev->tx[3].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, AC3_BASE_CSR, ®); + rt2x00_set_field32(®, AC3_BASE_CSR_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, AC3_BASE_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, RX_RING_CSR, ®); + rt2x00_set_field32(®, RX_RING_CSR_RING_SIZE, rt2x00dev->rx->limit); + rt2x00_set_field32(®, RX_RING_CSR_RXD_SIZE, + rt2x00dev->rx->desc_size / 4); + rt2x00_set_field32(®, RX_RING_CSR_RXD_WRITEBACK_SIZE, 4); + rt2x00mmio_register_write(rt2x00dev, RX_RING_CSR, reg); + + entry_priv = rt2x00dev->rx->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, RX_BASE_CSR, ®); + rt2x00_set_field32(®, RX_BASE_CSR_RING_REGISTER, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, RX_BASE_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, TX_DMA_DST_CSR, ®); + rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC0, 2); + rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC1, 2); + rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC2, 2); + rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC3, 2); + rt2x00mmio_register_write(rt2x00dev, TX_DMA_DST_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, LOAD_TX_RING_CSR, ®); + rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC0, 1); + rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC1, 1); + rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC2, 1); + rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC3, 1); + rt2x00mmio_register_write(rt2x00dev, LOAD_TX_RING_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, RX_CNTL_CSR, ®); + rt2x00_set_field32(®, RX_CNTL_CSR_LOAD_RXD, 1); + rt2x00mmio_register_write(rt2x00dev, RX_CNTL_CSR, reg); + + return 0; +} + +static int rt61pci_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_AUTO_TX_SEQ, 1); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); + rt2x00_set_field32(®, TXRX_CSR0_TX_WITHOUT_WAITING, 0); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); + + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR1, ®); + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0, 47); /* CCK Signal */ + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1, 30); /* Rssi */ + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2, 42); /* OFDM Rate */ + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3, 30); /* Rssi */ + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3_VALID, 1); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR1, reg); + + /* + * CCK TXD BBP registers + */ + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0, 13); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1, 12); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2, 11); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3, 10); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3_VALID, 1); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR2, reg); + + /* + * OFDM TXD BBP registers + */ + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR3, ®); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0, 7); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1, 6); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2, 5); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2_VALID, 1); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR3, reg); + + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR7, ®); + rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_6MBS, 59); + rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_9MBS, 53); + rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_12MBS, 49); + rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_18MBS, 46); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR7, reg); + + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR8, ®); + rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_24MBS, 44); + rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_36MBS, 42); + rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_48MBS, 42); + rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_54MBS, 42); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR8, reg); + + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, 0); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 0); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00_set_field32(®, TXRX_CSR9_TIMESTAMP_COMPENSATE, 0); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); + + rt2x00mmio_register_write(rt2x00dev, MAC_CSR6, 0x00000fff); + + rt2x00mmio_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00_set_field32(®, MAC_CSR9_CW_SELECT, 0); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR9, reg); + + rt2x00mmio_register_write(rt2x00dev, MAC_CSR10, 0x0000071c); + + if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) + return -EBUSY; + + rt2x00mmio_register_write(rt2x00dev, MAC_CSR13, 0x0000e000); + + /* + * Invalidate all Shared Keys (SEC_CSR0), + * and clear the Shared key Cipher algorithms (SEC_CSR1 & SEC_CSR5) + */ + rt2x00mmio_register_write(rt2x00dev, SEC_CSR0, 0x00000000); + rt2x00mmio_register_write(rt2x00dev, SEC_CSR1, 0x00000000); + rt2x00mmio_register_write(rt2x00dev, SEC_CSR5, 0x00000000); + + rt2x00mmio_register_write(rt2x00dev, PHY_CSR1, 0x000023b0); + rt2x00mmio_register_write(rt2x00dev, PHY_CSR5, 0x060a100c); + rt2x00mmio_register_write(rt2x00dev, PHY_CSR6, 0x00080606); + rt2x00mmio_register_write(rt2x00dev, PHY_CSR7, 0x00000a08); + + rt2x00mmio_register_write(rt2x00dev, PCI_CFG_CSR, 0x28ca4404); + + rt2x00mmio_register_write(rt2x00dev, TEST_MODE_CSR, 0x00000200); + + rt2x00mmio_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); + + /* + * Clear all beacons + * For the Beacon base registers we only need to clear + * the first byte since that byte contains the VALID and OWNER + * bits which (when set to 0) will invalidate the entire beacon. + */ + rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE0, 0); + rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE1, 0); + rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE2, 0); + rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE3, 0); + + /* + * We must clear the error counters. + * These registers are cleared on read, + * so we may pass a useless variable to store the value. + */ + rt2x00mmio_register_read(rt2x00dev, STA_CSR0, ®); + rt2x00mmio_register_read(rt2x00dev, STA_CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, STA_CSR2, ®); + + /* + * Reset MAC and BBP registers. + */ + rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); + + return 0; +} + +static int rt61pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u8 value; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt61pci_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + return 0; + udelay(REGISTER_BUSY_DELAY); + } + + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); + return -EACCES; +} + +static int rt61pci_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + if (unlikely(rt61pci_wait_bbp_ready(rt2x00dev))) + return -EACCES; + + rt61pci_bbp_write(rt2x00dev, 3, 0x00); + rt61pci_bbp_write(rt2x00dev, 15, 0x30); + rt61pci_bbp_write(rt2x00dev, 21, 0xc8); + rt61pci_bbp_write(rt2x00dev, 22, 0x38); + rt61pci_bbp_write(rt2x00dev, 23, 0x06); + rt61pci_bbp_write(rt2x00dev, 24, 0xfe); + rt61pci_bbp_write(rt2x00dev, 25, 0x0a); + rt61pci_bbp_write(rt2x00dev, 26, 0x0d); + rt61pci_bbp_write(rt2x00dev, 34, 0x12); + rt61pci_bbp_write(rt2x00dev, 37, 0x07); + rt61pci_bbp_write(rt2x00dev, 39, 0xf8); + rt61pci_bbp_write(rt2x00dev, 41, 0x60); + rt61pci_bbp_write(rt2x00dev, 53, 0x10); + rt61pci_bbp_write(rt2x00dev, 54, 0x18); + rt61pci_bbp_write(rt2x00dev, 60, 0x10); + rt61pci_bbp_write(rt2x00dev, 61, 0x04); + rt61pci_bbp_write(rt2x00dev, 62, 0x04); + rt61pci_bbp_write(rt2x00dev, 75, 0xfe); + rt61pci_bbp_write(rt2x00dev, 86, 0xfe); + rt61pci_bbp_write(rt2x00dev, 88, 0xfe); + rt61pci_bbp_write(rt2x00dev, 90, 0x0f); + rt61pci_bbp_write(rt2x00dev, 99, 0x00); + rt61pci_bbp_write(rt2x00dev, 102, 0x16); + rt61pci_bbp_write(rt2x00dev, 107, 0x04); + + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + rt61pci_bbp_write(rt2x00dev, reg_id, value); + } + } + + return 0; +} + +/* + * Device state switch handlers. + */ +static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int mask = (state == STATE_RADIO_IRQ_OFF); + u32 reg; + unsigned long flags; + + /* + * When interrupts are being enabled, the interrupt registers + * should clear the register to assure a clean state. + */ + if (state == STATE_RADIO_IRQ_ON) { + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg); + } + + /* + * Only toggle the interrupts bits we are going to use. + * Non-checked interrupt bits are disabled by default. + */ + spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); + + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + rt2x00_set_field32(®, INT_MASK_CSR_TXDONE, mask); + rt2x00_set_field32(®, INT_MASK_CSR_RXDONE, mask); + rt2x00_set_field32(®, INT_MASK_CSR_BEACON_DONE, mask); + rt2x00_set_field32(®, INT_MASK_CSR_ENABLE_MITIGATION, mask); + rt2x00_set_field32(®, INT_MASK_CSR_MITIGATION_PERIOD, 0xff); + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); + rt2x00_set_field32(®, MCU_INT_MASK_CSR_0, mask); + rt2x00_set_field32(®, MCU_INT_MASK_CSR_1, mask); + rt2x00_set_field32(®, MCU_INT_MASK_CSR_2, mask); + rt2x00_set_field32(®, MCU_INT_MASK_CSR_3, mask); + rt2x00_set_field32(®, MCU_INT_MASK_CSR_4, mask); + rt2x00_set_field32(®, MCU_INT_MASK_CSR_5, mask); + rt2x00_set_field32(®, MCU_INT_MASK_CSR_6, mask); + rt2x00_set_field32(®, MCU_INT_MASK_CSR_7, mask); + rt2x00_set_field32(®, MCU_INT_MASK_CSR_TWAKEUP, mask); + rt2x00mmio_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); + + spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); + + if (state == STATE_RADIO_IRQ_OFF) { + /* + * Ensure that all tasklets are finished. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->autowake_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + } +} + +static int rt61pci_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Initialize all registers. + */ + if (unlikely(rt61pci_init_queues(rt2x00dev) || + rt61pci_init_registers(rt2x00dev) || + rt61pci_init_bbp(rt2x00dev))) + return -EIO; + + /* + * Enable RX. + */ + rt2x00mmio_register_read(rt2x00dev, RX_CNTL_CSR, ®); + rt2x00_set_field32(®, RX_CNTL_CSR_ENABLE_RX_DMA, 1); + rt2x00mmio_register_write(rt2x00dev, RX_CNTL_CSR, reg); + + return 0; +} + +static void rt61pci_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + /* + * Disable power + */ + rt2x00mmio_register_write(rt2x00dev, MAC_CSR10, 0x00001818); +} + +static int rt61pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) +{ + u32 reg, reg2; + unsigned int i; + char put_to_sleep; + + put_to_sleep = (state != STATE_AWAKE); + + rt2x00mmio_register_read(rt2x00dev, MAC_CSR12, ®); + rt2x00_set_field32(®, MAC_CSR12_FORCE_WAKEUP, !put_to_sleep); + rt2x00_set_field32(®, MAC_CSR12_PUT_TO_SLEEP, put_to_sleep); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR12, reg); + + /* + * Device is not guaranteed to be in the requested state yet. + * We must wait until the register indicates that the + * device has entered the correct state. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00mmio_register_read(rt2x00dev, MAC_CSR12, ®2); + state = rt2x00_get_field32(reg2, MAC_CSR12_BBP_CURRENT_STATE); + if (state == !put_to_sleep) + return 0; + rt2x00mmio_register_write(rt2x00dev, MAC_CSR12, reg); + msleep(10); + } + + return -EBUSY; +} + +static int rt61pci_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + retval = rt61pci_enable_radio(rt2x00dev); + break; + case STATE_RADIO_OFF: + rt61pci_disable_radio(rt2x00dev); + break; + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt61pci_toggle_irq(rt2x00dev, state); + break; + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + retval = rt61pci_set_state(rt2x00dev, state); + break; + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +/* + * TX descriptor initialization + */ +static void rt61pci_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *txd = entry_priv->desc; + u32 word; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, entry->queue->qid); + rt2x00_set_field32(&word, TXD_W1_AIFSN, entry->queue->aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); + rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); + rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, + test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_BUFFER_COUNT, 1); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, + txdesc->u.plcp.length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, + txdesc->u.plcp.length_high); + rt2x00_desc_write(txd, 2, word); + + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { + _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); + _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); + } + + rt2x00_desc_read(txd, 5, &word); + rt2x00_set_field32(&word, TXD_W5_PID_TYPE, entry->queue->qid); + rt2x00_set_field32(&word, TXD_W5_PID_SUBTYPE, + skbdesc->entry->entry_idx); + rt2x00_set_field32(&word, TXD_W5_TX_POWER, + TXPOWER_TO_DEV(entry->queue->rt2x00dev->tx_power)); + rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1); + rt2x00_desc_write(txd, 5, word); + + if (entry->queue->qid != QID_BEACON) { + rt2x00_desc_read(txd, 6, &word); + rt2x00_set_field32(&word, TXD_W6_BUFFER_PHYSICAL_ADDRESS, + skbdesc->skb_dma); + rt2x00_desc_write(txd, 6, word); + + rt2x00_desc_read(txd, 11, &word); + rt2x00_set_field32(&word, TXD_W11_BUFFER_LENGTH0, + txdesc->length); + rt2x00_desc_write(txd, 11, word); + } + + /* + * Writing TXD word 0 must the last to prevent a race condition with + * the device, whereby the device may take hold of the TXD before we + * finished updating it. + */ + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); + rt2x00_set_field32(&word, TXD_W0_VALID, 1); + rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_ACK, + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_OFDM, + (txdesc->rate_mode == RATE_MODE_OFDM)); + rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); + rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, + test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, + test_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_TABLE, + test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); + rt2x00_set_field32(&word, TXD_W0_BURST, + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher); + rt2x00_desc_write(txd, 0, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->desc = txd; + skbdesc->desc_len = (entry->queue->qid == QID_BEACON) ? TXINFO_SIZE : + TXD_DESC_SIZE; +} + +/* + * TX data initialization + */ +static void rt61pci_write_beacon(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + unsigned int beacon_base; + unsigned int padding_len; + u32 orig_reg, reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + orig_reg = reg; + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Write the TX descriptor for the beacon. + */ + rt61pci_write_tx_desc(entry, txdesc); + + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); + + /* + * Write entire beacon with descriptor and padding to register. + */ + padding_len = roundup(entry->skb->len, 4) - entry->skb->len; + if (padding_len && skb_pad(entry->skb, padding_len)) { + rt2x00_err(rt2x00dev, "Failure padding beacon, aborting\n"); + /* skb freed by skb_pad() on failure */ + entry->skb = NULL; + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, orig_reg); + return; + } + + beacon_base = HW_BEACON_OFFSET(entry->entry_idx); + rt2x00mmio_register_multiwrite(rt2x00dev, beacon_base, + entry_priv->desc, TXINFO_SIZE); + rt2x00mmio_register_multiwrite(rt2x00dev, beacon_base + TXINFO_SIZE, + entry->skb->data, + entry->skb->len + padding_len); + + /* + * Enable beaconing again. + * + * For Wi-Fi faily generated beacons between participating + * stations. Set TBTT phase adaptive adjustment step to 8us. + */ + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR10, 0x00001008); + + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Clean up beacon skb. + */ + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; +} + +static void rt61pci_clear_beacon(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + u32 orig_reg, reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, &orig_reg); + reg = orig_reg; + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Clear beacon. + */ + rt2x00mmio_register_write(rt2x00dev, + HW_BEACON_OFFSET(entry->entry_idx), 0); + + /* + * Restore global beaconing state. + */ + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, orig_reg); +} + +/* + * RX control handlers + */ +static int rt61pci_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) +{ + u8 offset = rt2x00dev->lna_gain; + u8 lna; + + lna = rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_LNA); + switch (lna) { + case 3: + offset += 90; + break; + case 2: + offset += 74; + break; + case 1: + offset += 64; + break; + default: + return 0; + } + + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { + if (lna == 3 || lna == 2) + offset += 10; + } + + return rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_AGC) * 2 - offset; +} + +static void rt61pci_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + u32 word0; + u32 word1; + + rt2x00_desc_read(entry_priv->desc, 0, &word0); + rt2x00_desc_read(entry_priv->desc, 1, &word1); + + if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + + rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG); + rxdesc->cipher_status = rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR); + + if (rxdesc->cipher != CIPHER_NONE) { + _rt2x00_desc_read(entry_priv->desc, 2, &rxdesc->iv[0]); + _rt2x00_desc_read(entry_priv->desc, 3, &rxdesc->iv[1]); + rxdesc->dev_flags |= RXDONE_CRYPTO_IV; + + _rt2x00_desc_read(entry_priv->desc, 4, &rxdesc->icv); + rxdesc->dev_flags |= RXDONE_CRYPTO_ICV; + + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. It has provided the data separately but rt2x00lib + * should decide if it should be reinserted. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * The hardware has already checked the Michael Mic and has + * stripped it from the frame. Signal this to mac80211. + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + + /* + * Obtain the status about this packet. + * When frame was received with an OFDM bitrate, + * the signal is the PLCP value. If it was received with + * a CCK bitrate the signal is the rate in 100kbit/s. + */ + rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); + rxdesc->rssi = rt61pci_agc_to_rssi(rt2x00dev, word1); + rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + if (rt2x00_get_field32(word0, RXD_W0_OFDM)) + rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; + else + rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; + if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; +} + +/* + * Interrupt functions. + */ +static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + struct queue_entry *entry; + struct queue_entry *entry_done; + struct queue_entry_priv_mmio *entry_priv; + struct txdone_entry_desc txdesc; + u32 word; + u32 reg; + int type; + int index; + int i; + + /* + * TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO + * at most X times and also stop processing once the TX_STA_FIFO_VALID + * flag is not set anymore. + * + * The legacy drivers use X=TX_RING_SIZE but state in a comment + * that the TX_STA_FIFO stack has a size of 16. We stick to our + * tx ring size for now. + */ + for (i = 0; i < rt2x00dev->tx->limit; i++) { + rt2x00mmio_register_read(rt2x00dev, STA_CSR4, ®); + if (!rt2x00_get_field32(reg, STA_CSR4_VALID)) + break; + + /* + * Skip this entry when it contains an invalid + * queue identication number. + */ + type = rt2x00_get_field32(reg, STA_CSR4_PID_TYPE); + queue = rt2x00queue_get_tx_queue(rt2x00dev, type); + if (unlikely(!queue)) + continue; + + /* + * Skip this entry when it contains an invalid + * index number. + */ + index = rt2x00_get_field32(reg, STA_CSR4_PID_SUBTYPE); + if (unlikely(index >= queue->limit)) + continue; + + entry = &queue->entries[index]; + entry_priv = entry->priv_data; + rt2x00_desc_read(entry_priv->desc, 0, &word); + + if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + !rt2x00_get_field32(word, TXD_W0_VALID)) + return; + + entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + while (entry != entry_done) { + /* Catch up. + * Just report any entries we missed as failed. + */ + rt2x00_warn(rt2x00dev, "TX status report missed for entry %d\n", + entry_done->entry_idx); + + rt2x00lib_txdone_noinfo(entry_done, TXDONE_UNKNOWN); + entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + } + + /* + * Obtain the status about this packet. + */ + txdesc.flags = 0; + switch (rt2x00_get_field32(reg, STA_CSR4_TX_RESULT)) { + case 0: /* Success, maybe with retry */ + __set_bit(TXDONE_SUCCESS, &txdesc.flags); + break; + case 6: /* Failure, excessive retries */ + __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); + /* Don't break, this is a failed frame! */ + default: /* Failure */ + __set_bit(TXDONE_FAILURE, &txdesc.flags); + } + txdesc.retry = rt2x00_get_field32(reg, STA_CSR4_RETRY_COUNT); + + /* + * the frame was retried at least once + * -> hw used fallback rates + */ + if (txdesc.retry) + __set_bit(TXDONE_FALLBACK, &txdesc.flags); + + rt2x00lib_txdone(entry, &txdesc); + } +} + +static void rt61pci_wakeup(struct rt2x00_dev *rt2x00dev) +{ + struct rt2x00lib_conf libconf = { .conf = &rt2x00dev->hw->conf }; + + rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); +} + +static inline void rt61pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + rt2x00_set_field32(®, irq_field, 0); + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +static void rt61pci_enable_mcu_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single MCU interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); + rt2x00_set_field32(®, irq_field, 0); + rt2x00mmio_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +static void rt61pci_txstatus_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt61pci_txdone(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TXDONE); +} + +static void rt61pci_tbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2x00lib_beacondone(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_BEACON_DONE); +} + +static void rt61pci_rxdone_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2x00mmio_rxdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RXDONE); +} + +static void rt61pci_autowake_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt61pci_wakeup(rt2x00dev); + rt2x00mmio_register_write(rt2x00dev, + M2H_CMD_DONE_CSR, 0xffffffff); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt61pci_enable_mcu_interrupt(rt2x00dev, MCU_INT_MASK_CSR_TWAKEUP); +} + +static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + u32 reg_mcu, mask_mcu; + u32 reg, mask; + + /* + * Get the interrupt sources & saved to local variable. + * Write register value back to clear pending interrupts. + */ + rt2x00mmio_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®_mcu); + rt2x00mmio_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg_mcu); + + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + + if (!reg && !reg_mcu) + return IRQ_NONE; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return IRQ_HANDLED; + + /* + * Schedule tasklets for interrupt handling. + */ + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RXDONE)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE)) + tasklet_schedule(&rt2x00dev->txstatus_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_BEACON_DONE)) + tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); + + if (rt2x00_get_field32(reg_mcu, MCU_INT_SOURCE_CSR_TWAKEUP)) + tasklet_schedule(&rt2x00dev->autowake_tasklet); + + /* + * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits + * for interrupts and interrupt masks we can just use the value of + * INT_SOURCE_CSR to create the interrupt mask. + */ + mask = reg; + mask_mcu = reg_mcu; + + /* + * Disable all interrupts for which a tasklet was scheduled right now, + * the tasklet will reenable the appropriate interrupts. + */ + spin_lock(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + reg |= mask; + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); + reg |= mask_mcu; + rt2x00mmio_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); + + spin_unlock(&rt2x00dev->irqmask_lock); + + return IRQ_HANDLED; +} + +/* + * Device probe functions. + */ +static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) +{ + struct eeprom_93cx6 eeprom; + u32 reg; + u16 word; + u8 *mac; + s8 value; + + rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); + + eeprom.data = rt2x00dev; + eeprom.register_read = rt61pci_eepromregister_read; + eeprom.register_write = rt61pci_eepromregister_write; + eeprom.width = rt2x00_get_field32(reg, E2PROM_CSR_TYPE_93C46) ? + PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; + eeprom.reg_data_in = 0; + eeprom.reg_data_out = 0; + eeprom.reg_data_clock = 0; + eeprom.reg_chip_select = 0; + + eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, + EEPROM_SIZE / sizeof(u16)); + + /* + * Start validation of the data that has been read. + */ + mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + if (!is_valid_ether_addr(mac)) { + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); + rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, + ANTENNA_B); + rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, + ANTENNA_B); + rt2x00_set_field16(&word, EEPROM_ANTENNA_FRAME_TYPE, 0); + rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); + rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); + rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF5225); + rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); + rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_NIC_ENABLE_DIVERSITY, 0); + rt2x00_set_field16(&word, EEPROM_NIC_TX_DIVERSITY, 0); + rt2x00_set_field16(&word, EEPROM_NIC_RX_FIXED, 0); + rt2x00_set_field16(&word, EEPROM_NIC_TX_FIXED, 0); + rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_BG, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); + rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_A, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); + rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_LED_LED_MODE, + LED_MODE_DEFAULT); + rt2x00_eeprom_write(rt2x00dev, EEPROM_LED, word); + rt2x00_eeprom_dbg(rt2x00dev, "Led: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); + rt2x00_set_field16(&word, EEPROM_FREQ_SEQ, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); + rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); + rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); + } else { + value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_1); + if (value < -10 || value > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); + value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_2); + if (value < -10 || value > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); + rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET A: 0x%04x\n", word); + } else { + value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_1); + if (value < -10 || value > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); + value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_2); + if (value < -10 || value > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); + } + + return 0; +} + +static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 value; + u16 eeprom; + + /* + * Read EEPROM word for configuration. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Identify RF chipset. + */ + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); + rt2x00mmio_register_read(rt2x00dev, MAC_CSR0, ®); + rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), + value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); + + if (!rt2x00_rf(rt2x00dev, RF5225) && + !rt2x00_rf(rt2x00dev, RF5325) && + !rt2x00_rf(rt2x00dev, RF2527) && + !rt2x00_rf(rt2x00dev, RF2529)) { + rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); + return -ENODEV; + } + + /* + * Determine number of antennas. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_NUM) == 2) + __set_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags); + + /* + * Identify default antenna configuration. + */ + rt2x00dev->default_ant.tx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); + rt2x00dev->default_ant.rx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); + + /* + * Read the Frame type. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_FRAME_TYPE)) + __set_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags); + + /* + * Detect if this device has a hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); + + /* + * Read frequency offset and RF programming sequence. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_FREQ_SEQ)) + __set_bit(CAPABILITY_RF_SEQUENCE, &rt2x00dev->cap_flags); + + rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); + + /* + * Read external LNA informations. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); + + if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_A)) + __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_BG)) + __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); + + /* + * When working with a RF2529 chip without double antenna, + * the antenna settings should be gathered from the NIC + * eeprom word. + */ + if (rt2x00_rf(rt2x00dev, RF2529) && + !rt2x00_has_cap_double_antenna(rt2x00dev)) { + rt2x00dev->default_ant.rx = + ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED); + rt2x00dev->default_ant.tx = + ANTENNA_B - rt2x00_get_field16(eeprom, EEPROM_NIC_TX_FIXED); + + if (rt2x00_get_field16(eeprom, EEPROM_NIC_TX_DIVERSITY)) + rt2x00dev->default_ant.tx = ANTENNA_SW_DIVERSITY; + if (rt2x00_get_field16(eeprom, EEPROM_NIC_ENABLE_DIVERSITY)) + rt2x00dev->default_ant.rx = ANTENNA_SW_DIVERSITY; + } + + /* + * Store led settings, for correct led behaviour. + * If the eeprom value is invalid, + * switch to default led mode. + */ +#ifdef CONFIG_RT2X00_LIB_LEDS + rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); + value = rt2x00_get_field16(eeprom, EEPROM_LED_LED_MODE); + + rt61pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + rt61pci_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); + if (value == LED_MODE_SIGNAL_STRENGTH) + rt61pci_init_led(rt2x00dev, &rt2x00dev->led_qual, + LED_TYPE_QUALITY); + + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_LED_MODE, value); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_0, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_GPIO_0)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_1, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_GPIO_1)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_2, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_GPIO_2)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_3, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_GPIO_3)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_4, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_GPIO_4)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_ACT, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_ACT)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_BG, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_RDY_G)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_RDY_A)); +#endif /* CONFIG_RT2X00_LIB_LEDS */ + + return 0; +} + +/* + * RF value list for RF5225 & RF5325 + * Supports: 2.4 GHz & 5.2 GHz, rf_sequence disabled + */ +static const struct rf_channel rf_vals_noseq[] = { + { 1, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa0b }, + { 2, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa1f }, + { 3, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa0b }, + { 4, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa1f }, + { 5, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa0b }, + { 6, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa1f }, + { 7, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa0b }, + { 8, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa1f }, + { 9, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa0b }, + { 10, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa1f }, + { 11, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa0b }, + { 12, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa1f }, + { 13, 0x00002ccc, 0x0000479e, 0x00068455, 0x000ffa0b }, + { 14, 0x00002ccc, 0x000047a2, 0x00068455, 0x000ffa13 }, + + /* 802.11 UNI / HyperLan 2 */ + { 36, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa23 }, + { 40, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa03 }, + { 44, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa0b }, + { 48, 0x00002ccc, 0x000049aa, 0x0009be55, 0x000ffa13 }, + { 52, 0x00002ccc, 0x000049ae, 0x0009ae55, 0x000ffa1b }, + { 56, 0x00002ccc, 0x000049b2, 0x0009ae55, 0x000ffa23 }, + { 60, 0x00002ccc, 0x000049ba, 0x0009ae55, 0x000ffa03 }, + { 64, 0x00002ccc, 0x000049be, 0x0009ae55, 0x000ffa0b }, + + /* 802.11 HyperLan 2 */ + { 100, 0x00002ccc, 0x00004a2a, 0x000bae55, 0x000ffa03 }, + { 104, 0x00002ccc, 0x00004a2e, 0x000bae55, 0x000ffa0b }, + { 108, 0x00002ccc, 0x00004a32, 0x000bae55, 0x000ffa13 }, + { 112, 0x00002ccc, 0x00004a36, 0x000bae55, 0x000ffa1b }, + { 116, 0x00002ccc, 0x00004a3a, 0x000bbe55, 0x000ffa23 }, + { 120, 0x00002ccc, 0x00004a82, 0x000bbe55, 0x000ffa03 }, + { 124, 0x00002ccc, 0x00004a86, 0x000bbe55, 0x000ffa0b }, + { 128, 0x00002ccc, 0x00004a8a, 0x000bbe55, 0x000ffa13 }, + { 132, 0x00002ccc, 0x00004a8e, 0x000bbe55, 0x000ffa1b }, + { 136, 0x00002ccc, 0x00004a92, 0x000bbe55, 0x000ffa23 }, + + /* 802.11 UNII */ + { 140, 0x00002ccc, 0x00004a9a, 0x000bbe55, 0x000ffa03 }, + { 149, 0x00002ccc, 0x00004aa2, 0x000bbe55, 0x000ffa1f }, + { 153, 0x00002ccc, 0x00004aa6, 0x000bbe55, 0x000ffa27 }, + { 157, 0x00002ccc, 0x00004aae, 0x000bbe55, 0x000ffa07 }, + { 161, 0x00002ccc, 0x00004ab2, 0x000bbe55, 0x000ffa0f }, + { 165, 0x00002ccc, 0x00004ab6, 0x000bbe55, 0x000ffa17 }, + + /* MMAC(Japan)J52 ch 34,38,42,46 */ + { 34, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa0b }, + { 38, 0x00002ccc, 0x0000499e, 0x0009be55, 0x000ffa13 }, + { 42, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa1b }, + { 46, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa23 }, +}; + +/* + * RF value list for RF5225 & RF5325 + * Supports: 2.4 GHz & 5.2 GHz, rf_sequence enabled + */ +static const struct rf_channel rf_vals_seq[] = { + { 1, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa0b }, + { 2, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa1f }, + { 3, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa0b }, + { 4, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa1f }, + { 5, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa0b }, + { 6, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa1f }, + { 7, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa0b }, + { 8, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa1f }, + { 9, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa0b }, + { 10, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa1f }, + { 11, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa0b }, + { 12, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa1f }, + { 13, 0x00002ccc, 0x0000479e, 0x00068455, 0x000ffa0b }, + { 14, 0x00002ccc, 0x000047a2, 0x00068455, 0x000ffa13 }, + + /* 802.11 UNI / HyperLan 2 */ + { 36, 0x00002cd4, 0x0004481a, 0x00098455, 0x000c0a03 }, + { 40, 0x00002cd0, 0x00044682, 0x00098455, 0x000c0a03 }, + { 44, 0x00002cd0, 0x00044686, 0x00098455, 0x000c0a1b }, + { 48, 0x00002cd0, 0x0004468e, 0x00098655, 0x000c0a0b }, + { 52, 0x00002cd0, 0x00044692, 0x00098855, 0x000c0a23 }, + { 56, 0x00002cd0, 0x0004469a, 0x00098c55, 0x000c0a13 }, + { 60, 0x00002cd0, 0x000446a2, 0x00098e55, 0x000c0a03 }, + { 64, 0x00002cd0, 0x000446a6, 0x00099255, 0x000c0a1b }, + + /* 802.11 HyperLan 2 */ + { 100, 0x00002cd4, 0x0004489a, 0x000b9855, 0x000c0a03 }, + { 104, 0x00002cd4, 0x000448a2, 0x000b9855, 0x000c0a03 }, + { 108, 0x00002cd4, 0x000448aa, 0x000b9855, 0x000c0a03 }, + { 112, 0x00002cd4, 0x000448b2, 0x000b9a55, 0x000c0a03 }, + { 116, 0x00002cd4, 0x000448ba, 0x000b9a55, 0x000c0a03 }, + { 120, 0x00002cd0, 0x00044702, 0x000b9a55, 0x000c0a03 }, + { 124, 0x00002cd0, 0x00044706, 0x000b9a55, 0x000c0a1b }, + { 128, 0x00002cd0, 0x0004470e, 0x000b9c55, 0x000c0a0b }, + { 132, 0x00002cd0, 0x00044712, 0x000b9c55, 0x000c0a23 }, + { 136, 0x00002cd0, 0x0004471a, 0x000b9e55, 0x000c0a13 }, + + /* 802.11 UNII */ + { 140, 0x00002cd0, 0x00044722, 0x000b9e55, 0x000c0a03 }, + { 149, 0x00002cd0, 0x0004472e, 0x000ba255, 0x000c0a1b }, + { 153, 0x00002cd0, 0x00044736, 0x000ba255, 0x000c0a0b }, + { 157, 0x00002cd4, 0x0004490a, 0x000ba255, 0x000c0a17 }, + { 161, 0x00002cd4, 0x00044912, 0x000ba255, 0x000c0a17 }, + { 165, 0x00002cd4, 0x0004491a, 0x000ba255, 0x000c0a17 }, + + /* MMAC(Japan)J52 ch 34,38,42,46 */ + { 34, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000c0a0b }, + { 38, 0x00002ccc, 0x0000499e, 0x0009be55, 0x000c0a13 }, + { 42, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000c0a1b }, + { 46, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000c0a23 }, +}; + +static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + struct channel_info *info; + char *tx_power; + unsigned int i; + + /* + * Disable powersaving as default. + */ + rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + /* + * Initialize all hw fields. + */ + ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); + ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); + ieee80211_hw_set(rt2x00dev->hw, HOST_BROADCAST_PS_BUFFERING); + ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); + + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, + rt2x00_eeprom_addr(rt2x00dev, + EEPROM_MAC_ADDR_0)); + + /* + * As rt61 has a global fallback table we cannot specify + * more then one tx rate per frame but since the hw will + * try several rates (based on the fallback table) we should + * initialize max_report_rates to the maximum number of rates + * we are going to try. Otherwise mac80211 will truncate our + * reported tx rates and the rc algortihm will end up with + * incorrect data. + */ + rt2x00dev->hw->max_rates = 1; + rt2x00dev->hw->max_report_rates = 7; + rt2x00dev->hw->max_rate_tries = 1; + + /* + * Initialize hw_mode information. + */ + spec->supported_bands = SUPPORT_BAND_2GHZ; + spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; + + if (!rt2x00_has_cap_rf_sequence(rt2x00dev)) { + spec->num_channels = 14; + spec->channels = rf_vals_noseq; + } else { + spec->num_channels = 14; + spec->channels = rf_vals_seq; + } + + if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; + spec->num_channels = ARRAY_SIZE(rf_vals_seq); + } + + /* + * Create channel information array + */ + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); + for (i = 0; i < 14; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); + } + + if (spec->num_channels > 14) { + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); + for (i = 14; i < spec->num_channels; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = + TXPOWER_FROM_DEV(tx_power[i - 14]); + } + } + + return 0; +} + +static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + u32 reg; + + /* + * Disable power saving. + */ + rt2x00mmio_register_write(rt2x00dev, SOFT_RESET_CSR, 0x00000007); + + /* + * Allocate eeprom data. + */ + retval = rt61pci_validate_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt61pci_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. + */ + rt2x00mmio_register_read(rt2x00dev, MAC_CSR13, ®); + rt2x00_set_field32(®, MAC_CSR13_DIR5, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR13, reg); + + /* + * Initialize hw specifications. + */ + retval = rt61pci_probe_hw_mode(rt2x00dev); + if (retval) + return retval; + + /* + * This device has multiple filters for control frames, + * but has no a separate filter for PS Poll frames. + */ + __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); + + /* + * This device requires firmware and DMA mapped skbs. + */ + __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); + if (!modparam_nohwcrypt) + __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); + __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); + + /* + * Set the rssi offset. + */ + rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; + + return 0; +} + +/* + * IEEE80211 stack callback functions. + */ +static int rt61pci_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, + const struct ieee80211_tx_queue_params *params) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + struct rt2x00_field32 field; + int retval; + u32 reg; + u32 offset; + + /* + * First pass the configuration through rt2x00lib, that will + * update the queue settings and validate the input. After that + * we are free to update the registers based on the value + * in the queue parameter. + */ + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); + if (retval) + return retval; + + /* + * We only need to perform additional register initialization + * for WMM queues. + */ + if (queue_idx >= 4) + return 0; + + queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); + + /* Update WMM TXOP register */ + offset = AC_TXOP_CSR0 + (sizeof(u32) * (!!(queue_idx & 2))); + field.bit_offset = (queue_idx & 1) * 16; + field.bit_mask = 0xffff << field.bit_offset; + + rt2x00mmio_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, field, queue->txop); + rt2x00mmio_register_write(rt2x00dev, offset, reg); + + /* Update WMM registers */ + field.bit_offset = queue_idx * 4; + field.bit_mask = 0xf << field.bit_offset; + + rt2x00mmio_register_read(rt2x00dev, AIFSN_CSR, ®); + rt2x00_set_field32(®, field, queue->aifs); + rt2x00mmio_register_write(rt2x00dev, AIFSN_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, CWMIN_CSR, ®); + rt2x00_set_field32(®, field, queue->cw_min); + rt2x00mmio_register_write(rt2x00dev, CWMIN_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, CWMAX_CSR, ®); + rt2x00_set_field32(®, field, queue->cw_max); + rt2x00mmio_register_write(rt2x00dev, CWMAX_CSR, reg); + + return 0; +} + +static u64 rt61pci_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u64 tsf; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR13, ®); + tsf = (u64) rt2x00_get_field32(reg, TXRX_CSR13_HIGH_TSFTIMER) << 32; + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR12, ®); + tsf |= rt2x00_get_field32(reg, TXRX_CSR12_LOW_TSFTIMER); + + return tsf; +} + +static const struct ieee80211_ops rt61pci_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt61pci_conf_tx, + .get_tsf = rt61pci_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, + .flush = rt2x00mac_flush, + .set_antenna = rt2x00mac_set_antenna, + .get_antenna = rt2x00mac_get_antenna, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { + .irq_handler = rt61pci_interrupt, + .txstatus_tasklet = rt61pci_txstatus_tasklet, + .tbtt_tasklet = rt61pci_tbtt_tasklet, + .rxdone_tasklet = rt61pci_rxdone_tasklet, + .autowake_tasklet = rt61pci_autowake_tasklet, + .probe_hw = rt61pci_probe_hw, + .get_firmware_name = rt61pci_get_firmware_name, + .check_firmware = rt61pci_check_firmware, + .load_firmware = rt61pci_load_firmware, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt61pci_get_entry_state, + .clear_entry = rt61pci_clear_entry, + .set_device_state = rt61pci_set_device_state, + .rfkill_poll = rt61pci_rfkill_poll, + .link_stats = rt61pci_link_stats, + .reset_tuner = rt61pci_reset_tuner, + .link_tuner = rt61pci_link_tuner, + .start_queue = rt61pci_start_queue, + .kick_queue = rt61pci_kick_queue, + .stop_queue = rt61pci_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, + .write_tx_desc = rt61pci_write_tx_desc, + .write_beacon = rt61pci_write_beacon, + .clear_beacon = rt61pci_clear_beacon, + .fill_rxdone = rt61pci_fill_rxdone, + .config_shared_key = rt61pci_config_shared_key, + .config_pairwise_key = rt61pci_config_pairwise_key, + .config_filter = rt61pci_config_filter, + .config_intf = rt61pci_config_intf, + .config_erp = rt61pci_config_erp, + .config_ant = rt61pci_config_ant, + .config = rt61pci_config, +}; + +static void rt61pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_BEACON: + queue->limit = 4; + queue->data_size = 0; /* No DMA required for beacons */ + queue->desc_size = TXINFO_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} + +static const struct rt2x00_ops rt61pci_ops = { + .name = KBUILD_MODNAME, + .max_ap_intf = 4, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt61pci_queue_init, + .lib = &rt61pci_rt2x00_ops, + .hw = &rt61pci_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt61pci_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +/* + * RT61pci module information. + */ +static const struct pci_device_id rt61pci_device_table[] = { + /* RT2561s */ + { PCI_DEVICE(0x1814, 0x0301) }, + /* RT2561 v2 */ + { PCI_DEVICE(0x1814, 0x0302) }, + /* RT2661 */ + { PCI_DEVICE(0x1814, 0x0401) }, + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT61 PCI & PCMCIA Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2561, RT2561s & RT2661 " + "PCI & PCMCIA chipset based cards"); +MODULE_DEVICE_TABLE(pci, rt61pci_device_table); +MODULE_FIRMWARE(FIRMWARE_RT2561); +MODULE_FIRMWARE(FIRMWARE_RT2561s); +MODULE_FIRMWARE(FIRMWARE_RT2661); +MODULE_LICENSE("GPL"); + +static int rt61pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + return rt2x00pci_probe(pci_dev, &rt61pci_ops); +} + +static struct pci_driver rt61pci_driver = { + .name = KBUILD_MODNAME, + .id_table = rt61pci_device_table, + .probe = rt61pci_probe, + .remove = rt2x00pci_remove, + .suspend = rt2x00pci_suspend, + .resume = rt2x00pci_resume, +}; + +module_pci_driver(rt61pci_driver); diff --git a/drivers/net/wireless/ralink/rt2x00/rt61pci.h b/drivers/net/wireless/ralink/rt2x00/rt61pci.h new file mode 100644 index 000000000000..1442075a8382 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.h @@ -0,0 +1,1500 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt61pci + Abstract: Data structures and registers for the rt61pci module. + Supported chipsets: RT2561, RT2561s, RT2661. + */ + +#ifndef RT61PCI_H +#define RT61PCI_H + +/* + * RT chip PCI IDs. + */ +#define RT2561s_PCI_ID 0x0301 +#define RT2561_PCI_ID 0x0302 +#define RT2661_PCI_ID 0x0401 + +/* + * RF chip defines. + */ +#define RF5225 0x0001 +#define RF5325 0x0002 +#define RF2527 0x0003 +#define RF2529 0x0004 + +/* + * Signal information. + * Default offset is required for RSSI <-> dBm conversion. + */ +#define DEFAULT_RSSI_OFFSET 120 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x3000 +#define CSR_REG_SIZE 0x04b0 +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x0100 +#define BBP_BASE 0x0000 +#define BBP_SIZE 0x0080 +#define RF_BASE 0x0004 +#define RF_SIZE 0x0010 + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 4 + +/* + * PCI registers. + */ + +/* + * HOST_CMD_CSR: For HOST to interrupt embedded processor + */ +#define HOST_CMD_CSR 0x0008 +#define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x0000007f) +#define HOST_CMD_CSR_INTERRUPT_MCU FIELD32(0x00000080) + +/* + * MCU_CNTL_CSR + * SELECT_BANK: Select 8051 program bank. + * RESET: Enable 8051 reset state. + * READY: Ready state for 8051. + */ +#define MCU_CNTL_CSR 0x000c +#define MCU_CNTL_CSR_SELECT_BANK FIELD32(0x00000001) +#define MCU_CNTL_CSR_RESET FIELD32(0x00000002) +#define MCU_CNTL_CSR_READY FIELD32(0x00000004) + +/* + * SOFT_RESET_CSR + * FORCE_CLOCK_ON: Host force MAC clock ON + */ +#define SOFT_RESET_CSR 0x0010 +#define SOFT_RESET_CSR_FORCE_CLOCK_ON FIELD32(0x00000002) + +/* + * MCU_INT_SOURCE_CSR: MCU interrupt source/mask register. + */ +#define MCU_INT_SOURCE_CSR 0x0014 +#define MCU_INT_SOURCE_CSR_0 FIELD32(0x00000001) +#define MCU_INT_SOURCE_CSR_1 FIELD32(0x00000002) +#define MCU_INT_SOURCE_CSR_2 FIELD32(0x00000004) +#define MCU_INT_SOURCE_CSR_3 FIELD32(0x00000008) +#define MCU_INT_SOURCE_CSR_4 FIELD32(0x00000010) +#define MCU_INT_SOURCE_CSR_5 FIELD32(0x00000020) +#define MCU_INT_SOURCE_CSR_6 FIELD32(0x00000040) +#define MCU_INT_SOURCE_CSR_7 FIELD32(0x00000080) +#define MCU_INT_SOURCE_CSR_TWAKEUP FIELD32(0x00000100) +#define MCU_INT_SOURCE_CSR_TBTT_EXPIRE FIELD32(0x00000200) + +/* + * MCU_INT_MASK_CSR: MCU interrupt source/mask register. + */ +#define MCU_INT_MASK_CSR 0x0018 +#define MCU_INT_MASK_CSR_0 FIELD32(0x00000001) +#define MCU_INT_MASK_CSR_1 FIELD32(0x00000002) +#define MCU_INT_MASK_CSR_2 FIELD32(0x00000004) +#define MCU_INT_MASK_CSR_3 FIELD32(0x00000008) +#define MCU_INT_MASK_CSR_4 FIELD32(0x00000010) +#define MCU_INT_MASK_CSR_5 FIELD32(0x00000020) +#define MCU_INT_MASK_CSR_6 FIELD32(0x00000040) +#define MCU_INT_MASK_CSR_7 FIELD32(0x00000080) +#define MCU_INT_MASK_CSR_TWAKEUP FIELD32(0x00000100) +#define MCU_INT_MASK_CSR_TBTT_EXPIRE FIELD32(0x00000200) + +/* + * PCI_USEC_CSR + */ +#define PCI_USEC_CSR 0x001c + +/* + * Security key table memory. + * 16 entries 32-byte for shared key table + * 64 entries 32-byte for pairwise key table + * 64 entries 8-byte for pairwise ta key table + */ +#define SHARED_KEY_TABLE_BASE 0x1000 +#define PAIRWISE_KEY_TABLE_BASE 0x1200 +#define PAIRWISE_TA_TABLE_BASE 0x1a00 + +#define SHARED_KEY_ENTRY(__idx) \ + ( SHARED_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_KEY_ENTRY(__idx) \ + ( PAIRWISE_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_TA_ENTRY(__idx) \ + ( PAIRWISE_TA_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_pairwise_ta_entry)) ) + +struct hw_key_entry { + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; +} __packed; + +struct hw_pairwise_ta_entry { + u8 address[6]; + u8 cipher; + u8 reserved; +} __packed; + +/* + * Other on-chip shared memory space. + */ +#define HW_CIS_BASE 0x2000 +#define HW_NULL_BASE 0x2b00 + +/* + * Since NULL frame won't be that long (256 byte), + * We steal 16 tail bytes to save debugging settings. + */ +#define HW_DEBUG_SETTING_BASE 0x2bf0 + +/* + * On-chip BEACON frame space. + */ +#define HW_BEACON_BASE0 0x2c00 +#define HW_BEACON_BASE1 0x2d00 +#define HW_BEACON_BASE2 0x2e00 +#define HW_BEACON_BASE3 0x2f00 + +#define HW_BEACON_OFFSET(__index) \ + ( HW_BEACON_BASE0 + (__index * 0x0100) ) + +/* + * HOST-MCU shared memory. + */ + +/* + * H2M_MAILBOX_CSR: Host-to-MCU Mailbox. + */ +#define H2M_MAILBOX_CSR 0x2100 +#define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff) +#define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00) +#define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000) +#define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000) + +/* + * MCU_LEDCS: LED control for MCU Mailbox. + */ +#define MCU_LEDCS_LED_MODE FIELD16(0x001f) +#define MCU_LEDCS_RADIO_STATUS FIELD16(0x0020) +#define MCU_LEDCS_LINK_BG_STATUS FIELD16(0x0040) +#define MCU_LEDCS_LINK_A_STATUS FIELD16(0x0080) +#define MCU_LEDCS_POLARITY_GPIO_0 FIELD16(0x0100) +#define MCU_LEDCS_POLARITY_GPIO_1 FIELD16(0x0200) +#define MCU_LEDCS_POLARITY_GPIO_2 FIELD16(0x0400) +#define MCU_LEDCS_POLARITY_GPIO_3 FIELD16(0x0800) +#define MCU_LEDCS_POLARITY_GPIO_4 FIELD16(0x1000) +#define MCU_LEDCS_POLARITY_ACT FIELD16(0x2000) +#define MCU_LEDCS_POLARITY_READY_BG FIELD16(0x4000) +#define MCU_LEDCS_POLARITY_READY_A FIELD16(0x8000) + +/* + * M2H_CMD_DONE_CSR. + */ +#define M2H_CMD_DONE_CSR 0x2104 + +/* + * MCU_TXOP_ARRAY_BASE. + */ +#define MCU_TXOP_ARRAY_BASE 0x2110 + +/* + * MAC Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * MAC_CSR0: ASIC revision number. + */ +#define MAC_CSR0 0x3000 +#define MAC_CSR0_REVISION FIELD32(0x0000000f) +#define MAC_CSR0_CHIPSET FIELD32(0x000ffff0) + +/* + * MAC_CSR1: System control register. + * SOFT_RESET: Software reset bit, 1: reset, 0: normal. + * BBP_RESET: Hardware reset BBP. + * HOST_READY: Host is ready after initialization, 1: ready. + */ +#define MAC_CSR1 0x3004 +#define MAC_CSR1_SOFT_RESET FIELD32(0x00000001) +#define MAC_CSR1_BBP_RESET FIELD32(0x00000002) +#define MAC_CSR1_HOST_READY FIELD32(0x00000004) + +/* + * MAC_CSR2: STA MAC register 0. + */ +#define MAC_CSR2 0x3008 +#define MAC_CSR2_BYTE0 FIELD32(0x000000ff) +#define MAC_CSR2_BYTE1 FIELD32(0x0000ff00) +#define MAC_CSR2_BYTE2 FIELD32(0x00ff0000) +#define MAC_CSR2_BYTE3 FIELD32(0xff000000) + +/* + * MAC_CSR3: STA MAC register 1. + * UNICAST_TO_ME_MASK: + * Used to mask off bits from byte 5 of the MAC address + * to determine the UNICAST_TO_ME bit for RX frames. + * The full mask is complemented by BSS_ID_MASK: + * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK + */ +#define MAC_CSR3 0x300c +#define MAC_CSR3_BYTE4 FIELD32(0x000000ff) +#define MAC_CSR3_BYTE5 FIELD32(0x0000ff00) +#define MAC_CSR3_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) + +/* + * MAC_CSR4: BSSID register 0. + */ +#define MAC_CSR4 0x3010 +#define MAC_CSR4_BYTE0 FIELD32(0x000000ff) +#define MAC_CSR4_BYTE1 FIELD32(0x0000ff00) +#define MAC_CSR4_BYTE2 FIELD32(0x00ff0000) +#define MAC_CSR4_BYTE3 FIELD32(0xff000000) + +/* + * MAC_CSR5: BSSID register 1. + * BSS_ID_MASK: + * This mask is used to mask off bits 0 and 1 of byte 5 of the + * BSSID. This will make sure that those bits will be ignored + * when determining the MY_BSS of RX frames. + * 0: 1-BSSID mode (BSS index = 0) + * 1: 2-BSSID mode (BSS index: Byte5, bit 0) + * 2: 2-BSSID mode (BSS index: byte5, bit 1) + * 3: 4-BSSID mode (BSS index: byte5, bit 0 - 1) + */ +#define MAC_CSR5 0x3014 +#define MAC_CSR5_BYTE4 FIELD32(0x000000ff) +#define MAC_CSR5_BYTE5 FIELD32(0x0000ff00) +#define MAC_CSR5_BSS_ID_MASK FIELD32(0x00ff0000) + +/* + * MAC_CSR6: Maximum frame length register. + */ +#define MAC_CSR6 0x3018 +#define MAC_CSR6_MAX_FRAME_UNIT FIELD32(0x00000fff) + +/* + * MAC_CSR7: Reserved + */ +#define MAC_CSR7 0x301c + +/* + * MAC_CSR8: SIFS/EIFS register. + * All units are in US. + */ +#define MAC_CSR8 0x3020 +#define MAC_CSR8_SIFS FIELD32(0x000000ff) +#define MAC_CSR8_SIFS_AFTER_RX_OFDM FIELD32(0x0000ff00) +#define MAC_CSR8_EIFS FIELD32(0xffff0000) + +/* + * MAC_CSR9: Back-Off control register. + * SLOT_TIME: Slot time, default is 20us for 802.11BG. + * CWMIN: Bit for Cwmin. default Cwmin is 31 (2^5 - 1). + * CWMAX: Bit for Cwmax, default Cwmax is 1023 (2^10 - 1). + * CW_SELECT: 1: CWmin/Cwmax select from register, 0:select from TxD. + */ +#define MAC_CSR9 0x3024 +#define MAC_CSR9_SLOT_TIME FIELD32(0x000000ff) +#define MAC_CSR9_CWMIN FIELD32(0x00000f00) +#define MAC_CSR9_CWMAX FIELD32(0x0000f000) +#define MAC_CSR9_CW_SELECT FIELD32(0x00010000) + +/* + * MAC_CSR10: Power state configuration. + */ +#define MAC_CSR10 0x3028 + +/* + * MAC_CSR11: Power saving transition time register. + * DELAY_AFTER_TBCN: Delay after Tbcn expired in units of TU. + * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. + * WAKEUP_LATENCY: In unit of TU. + */ +#define MAC_CSR11 0x302c +#define MAC_CSR11_DELAY_AFTER_TBCN FIELD32(0x000000ff) +#define MAC_CSR11_TBCN_BEFORE_WAKEUP FIELD32(0x00007f00) +#define MAC_CSR11_AUTOWAKE FIELD32(0x00008000) +#define MAC_CSR11_WAKEUP_LATENCY FIELD32(0x000f0000) + +/* + * MAC_CSR12: Manual power control / status register (merge CSR20 & PWRCSR1). + * CURRENT_STATE: 0:sleep, 1:awake. + * FORCE_WAKEUP: This has higher priority than PUT_TO_SLEEP. + * BBP_CURRENT_STATE: 0: BBP sleep, 1: BBP awake. + */ +#define MAC_CSR12 0x3030 +#define MAC_CSR12_CURRENT_STATE FIELD32(0x00000001) +#define MAC_CSR12_PUT_TO_SLEEP FIELD32(0x00000002) +#define MAC_CSR12_FORCE_WAKEUP FIELD32(0x00000004) +#define MAC_CSR12_BBP_CURRENT_STATE FIELD32(0x00000008) + +/* + * MAC_CSR13: GPIO. + * MAC_CSR13_VALx: GPIO value + * MAC_CSR13_DIRx: GPIO direction: 0 = output; 1 = input + */ +#define MAC_CSR13 0x3034 +#define MAC_CSR13_VAL0 FIELD32(0x00000001) +#define MAC_CSR13_VAL1 FIELD32(0x00000002) +#define MAC_CSR13_VAL2 FIELD32(0x00000004) +#define MAC_CSR13_VAL3 FIELD32(0x00000008) +#define MAC_CSR13_VAL4 FIELD32(0x00000010) +#define MAC_CSR13_VAL5 FIELD32(0x00000020) +#define MAC_CSR13_DIR0 FIELD32(0x00000100) +#define MAC_CSR13_DIR1 FIELD32(0x00000200) +#define MAC_CSR13_DIR2 FIELD32(0x00000400) +#define MAC_CSR13_DIR3 FIELD32(0x00000800) +#define MAC_CSR13_DIR4 FIELD32(0x00001000) +#define MAC_CSR13_DIR5 FIELD32(0x00002000) + +/* + * MAC_CSR14: LED control register. + * ON_PERIOD: On period, default 70ms. + * OFF_PERIOD: Off period, default 30ms. + * HW_LED: HW TX activity, 1: normal OFF, 0: normal ON. + * SW_LED: s/w LED, 1: ON, 0: OFF. + * HW_LED_POLARITY: 0: active low, 1: active high. + */ +#define MAC_CSR14 0x3038 +#define MAC_CSR14_ON_PERIOD FIELD32(0x000000ff) +#define MAC_CSR14_OFF_PERIOD FIELD32(0x0000ff00) +#define MAC_CSR14_HW_LED FIELD32(0x00010000) +#define MAC_CSR14_SW_LED FIELD32(0x00020000) +#define MAC_CSR14_HW_LED_POLARITY FIELD32(0x00040000) +#define MAC_CSR14_SW_LED2 FIELD32(0x00080000) + +/* + * MAC_CSR15: NAV control. + */ +#define MAC_CSR15 0x303c + +/* + * TXRX control registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * TXRX_CSR0: TX/RX configuration register. + * TSF_OFFSET: Default is 24. + * AUTO_TX_SEQ: 1: ASIC auto replace sequence nr in outgoing frame. + * DISABLE_RX: Disable Rx engine. + * DROP_CRC: Drop CRC error. + * DROP_PHYSICAL: Drop physical error. + * DROP_CONTROL: Drop control frame. + * DROP_NOT_TO_ME: Drop not to me unicast frame. + * DROP_TO_DS: Drop fram ToDs bit is true. + * DROP_VERSION_ERROR: Drop version error frame. + * DROP_MULTICAST: Drop multicast frames. + * DROP_BORADCAST: Drop broadcast frames. + * DROP_ACK_CTS: Drop received ACK and CTS. + */ +#define TXRX_CSR0 0x3040 +#define TXRX_CSR0_RX_ACK_TIMEOUT FIELD32(0x000001ff) +#define TXRX_CSR0_TSF_OFFSET FIELD32(0x00007e00) +#define TXRX_CSR0_AUTO_TX_SEQ FIELD32(0x00008000) +#define TXRX_CSR0_DISABLE_RX FIELD32(0x00010000) +#define TXRX_CSR0_DROP_CRC FIELD32(0x00020000) +#define TXRX_CSR0_DROP_PHYSICAL FIELD32(0x00040000) +#define TXRX_CSR0_DROP_CONTROL FIELD32(0x00080000) +#define TXRX_CSR0_DROP_NOT_TO_ME FIELD32(0x00100000) +#define TXRX_CSR0_DROP_TO_DS FIELD32(0x00200000) +#define TXRX_CSR0_DROP_VERSION_ERROR FIELD32(0x00400000) +#define TXRX_CSR0_DROP_MULTICAST FIELD32(0x00800000) +#define TXRX_CSR0_DROP_BROADCAST FIELD32(0x01000000) +#define TXRX_CSR0_DROP_ACK_CTS FIELD32(0x02000000) +#define TXRX_CSR0_TX_WITHOUT_WAITING FIELD32(0x04000000) + +/* + * TXRX_CSR1 + */ +#define TXRX_CSR1 0x3044 +#define TXRX_CSR1_BBP_ID0 FIELD32(0x0000007f) +#define TXRX_CSR1_BBP_ID0_VALID FIELD32(0x00000080) +#define TXRX_CSR1_BBP_ID1 FIELD32(0x00007f00) +#define TXRX_CSR1_BBP_ID1_VALID FIELD32(0x00008000) +#define TXRX_CSR1_BBP_ID2 FIELD32(0x007f0000) +#define TXRX_CSR1_BBP_ID2_VALID FIELD32(0x00800000) +#define TXRX_CSR1_BBP_ID3 FIELD32(0x7f000000) +#define TXRX_CSR1_BBP_ID3_VALID FIELD32(0x80000000) + +/* + * TXRX_CSR2 + */ +#define TXRX_CSR2 0x3048 +#define TXRX_CSR2_BBP_ID0 FIELD32(0x0000007f) +#define TXRX_CSR2_BBP_ID0_VALID FIELD32(0x00000080) +#define TXRX_CSR2_BBP_ID1 FIELD32(0x00007f00) +#define TXRX_CSR2_BBP_ID1_VALID FIELD32(0x00008000) +#define TXRX_CSR2_BBP_ID2 FIELD32(0x007f0000) +#define TXRX_CSR2_BBP_ID2_VALID FIELD32(0x00800000) +#define TXRX_CSR2_BBP_ID3 FIELD32(0x7f000000) +#define TXRX_CSR2_BBP_ID3_VALID FIELD32(0x80000000) + +/* + * TXRX_CSR3 + */ +#define TXRX_CSR3 0x304c +#define TXRX_CSR3_BBP_ID0 FIELD32(0x0000007f) +#define TXRX_CSR3_BBP_ID0_VALID FIELD32(0x00000080) +#define TXRX_CSR3_BBP_ID1 FIELD32(0x00007f00) +#define TXRX_CSR3_BBP_ID1_VALID FIELD32(0x00008000) +#define TXRX_CSR3_BBP_ID2 FIELD32(0x007f0000) +#define TXRX_CSR3_BBP_ID2_VALID FIELD32(0x00800000) +#define TXRX_CSR3_BBP_ID3 FIELD32(0x7f000000) +#define TXRX_CSR3_BBP_ID3_VALID FIELD32(0x80000000) + +/* + * TXRX_CSR4: Auto-Responder/Tx-retry register. + * AUTORESPOND_PREAMBLE: 0:long, 1:short preamble. + * OFDM_TX_RATE_DOWN: 1:enable. + * OFDM_TX_RATE_STEP: 0:1-step, 1: 2-step, 2:3-step, 3:4-step. + * OFDM_TX_FALLBACK_CCK: 0: Fallback to OFDM 6M only, 1: Fallback to CCK 1M,2M. + */ +#define TXRX_CSR4 0x3050 +#define TXRX_CSR4_TX_ACK_TIMEOUT FIELD32(0x000000ff) +#define TXRX_CSR4_CNTL_ACK_POLICY FIELD32(0x00000700) +#define TXRX_CSR4_ACK_CTS_PSM FIELD32(0x00010000) +#define TXRX_CSR4_AUTORESPOND_ENABLE FIELD32(0x00020000) +#define TXRX_CSR4_AUTORESPOND_PREAMBLE FIELD32(0x00040000) +#define TXRX_CSR4_OFDM_TX_RATE_DOWN FIELD32(0x00080000) +#define TXRX_CSR4_OFDM_TX_RATE_STEP FIELD32(0x00300000) +#define TXRX_CSR4_OFDM_TX_FALLBACK_CCK FIELD32(0x00400000) +#define TXRX_CSR4_LONG_RETRY_LIMIT FIELD32(0x0f000000) +#define TXRX_CSR4_SHORT_RETRY_LIMIT FIELD32(0xf0000000) + +/* + * TXRX_CSR5 + */ +#define TXRX_CSR5 0x3054 + +/* + * TXRX_CSR6: ACK/CTS payload consumed time + */ +#define TXRX_CSR6 0x3058 + +/* + * TXRX_CSR7: OFDM ACK/CTS payload consumed time for 6/9/12/18 mbps. + */ +#define TXRX_CSR7 0x305c +#define TXRX_CSR7_ACK_CTS_6MBS FIELD32(0x000000ff) +#define TXRX_CSR7_ACK_CTS_9MBS FIELD32(0x0000ff00) +#define TXRX_CSR7_ACK_CTS_12MBS FIELD32(0x00ff0000) +#define TXRX_CSR7_ACK_CTS_18MBS FIELD32(0xff000000) + +/* + * TXRX_CSR8: OFDM ACK/CTS payload consumed time for 24/36/48/54 mbps. + */ +#define TXRX_CSR8 0x3060 +#define TXRX_CSR8_ACK_CTS_24MBS FIELD32(0x000000ff) +#define TXRX_CSR8_ACK_CTS_36MBS FIELD32(0x0000ff00) +#define TXRX_CSR8_ACK_CTS_48MBS FIELD32(0x00ff0000) +#define TXRX_CSR8_ACK_CTS_54MBS FIELD32(0xff000000) + +/* + * TXRX_CSR9: Synchronization control register. + * BEACON_INTERVAL: In unit of 1/16 TU. + * TSF_TICKING: Enable TSF auto counting. + * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. + * BEACON_GEN: Enable beacon generator. + */ +#define TXRX_CSR9 0x3064 +#define TXRX_CSR9_BEACON_INTERVAL FIELD32(0x0000ffff) +#define TXRX_CSR9_TSF_TICKING FIELD32(0x00010000) +#define TXRX_CSR9_TSF_SYNC FIELD32(0x00060000) +#define TXRX_CSR9_TBTT_ENABLE FIELD32(0x00080000) +#define TXRX_CSR9_BEACON_GEN FIELD32(0x00100000) +#define TXRX_CSR9_TIMESTAMP_COMPENSATE FIELD32(0xff000000) + +/* + * TXRX_CSR10: BEACON alignment. + */ +#define TXRX_CSR10 0x3068 + +/* + * TXRX_CSR11: AES mask. + */ +#define TXRX_CSR11 0x306c + +/* + * TXRX_CSR12: TSF low 32. + */ +#define TXRX_CSR12 0x3070 +#define TXRX_CSR12_LOW_TSFTIMER FIELD32(0xffffffff) + +/* + * TXRX_CSR13: TSF high 32. + */ +#define TXRX_CSR13 0x3074 +#define TXRX_CSR13_HIGH_TSFTIMER FIELD32(0xffffffff) + +/* + * TXRX_CSR14: TBTT timer. + */ +#define TXRX_CSR14 0x3078 + +/* + * TXRX_CSR15: TKIP MIC priority byte "AND" mask. + */ +#define TXRX_CSR15 0x307c + +/* + * PHY control registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * PHY_CSR0: RF/PS control. + */ +#define PHY_CSR0 0x3080 +#define PHY_CSR0_PA_PE_BG FIELD32(0x00010000) +#define PHY_CSR0_PA_PE_A FIELD32(0x00020000) + +/* + * PHY_CSR1 + */ +#define PHY_CSR1 0x3084 + +/* + * PHY_CSR2: Pre-TX BBP control. + */ +#define PHY_CSR2 0x3088 + +/* + * PHY_CSR3: BBP serial control register. + * VALUE: Register value to program into BBP. + * REG_NUM: Selected BBP register. + * READ_CONTROL: 0: Write BBP, 1: Read BBP. + * BUSY: 1: ASIC is busy execute BBP programming. + */ +#define PHY_CSR3 0x308c +#define PHY_CSR3_VALUE FIELD32(0x000000ff) +#define PHY_CSR3_REGNUM FIELD32(0x00007f00) +#define PHY_CSR3_READ_CONTROL FIELD32(0x00008000) +#define PHY_CSR3_BUSY FIELD32(0x00010000) + +/* + * PHY_CSR4: RF serial control register + * VALUE: Register value (include register id) serial out to RF/IF chip. + * NUMBER_OF_BITS: Number of bits used in RFRegValue (I:20, RFMD:22). + * IF_SELECT: 1: select IF to program, 0: select RF to program. + * PLL_LD: RF PLL_LD status. + * BUSY: 1: ASIC is busy execute RF programming. + */ +#define PHY_CSR4 0x3090 +#define PHY_CSR4_VALUE FIELD32(0x00ffffff) +#define PHY_CSR4_NUMBER_OF_BITS FIELD32(0x1f000000) +#define PHY_CSR4_IF_SELECT FIELD32(0x20000000) +#define PHY_CSR4_PLL_LD FIELD32(0x40000000) +#define PHY_CSR4_BUSY FIELD32(0x80000000) + +/* + * PHY_CSR5: RX to TX signal switch timing control. + */ +#define PHY_CSR5 0x3094 +#define PHY_CSR5_IQ_FLIP FIELD32(0x00000004) + +/* + * PHY_CSR6: TX to RX signal timing control. + */ +#define PHY_CSR6 0x3098 +#define PHY_CSR6_IQ_FLIP FIELD32(0x00000004) + +/* + * PHY_CSR7: TX DAC switching timing control. + */ +#define PHY_CSR7 0x309c + +/* + * Security control register. + */ + +/* + * SEC_CSR0: Shared key table control. + */ +#define SEC_CSR0 0x30a0 +#define SEC_CSR0_BSS0_KEY0_VALID FIELD32(0x00000001) +#define SEC_CSR0_BSS0_KEY1_VALID FIELD32(0x00000002) +#define SEC_CSR0_BSS0_KEY2_VALID FIELD32(0x00000004) +#define SEC_CSR0_BSS0_KEY3_VALID FIELD32(0x00000008) +#define SEC_CSR0_BSS1_KEY0_VALID FIELD32(0x00000010) +#define SEC_CSR0_BSS1_KEY1_VALID FIELD32(0x00000020) +#define SEC_CSR0_BSS1_KEY2_VALID FIELD32(0x00000040) +#define SEC_CSR0_BSS1_KEY3_VALID FIELD32(0x00000080) +#define SEC_CSR0_BSS2_KEY0_VALID FIELD32(0x00000100) +#define SEC_CSR0_BSS2_KEY1_VALID FIELD32(0x00000200) +#define SEC_CSR0_BSS2_KEY2_VALID FIELD32(0x00000400) +#define SEC_CSR0_BSS2_KEY3_VALID FIELD32(0x00000800) +#define SEC_CSR0_BSS3_KEY0_VALID FIELD32(0x00001000) +#define SEC_CSR0_BSS3_KEY1_VALID FIELD32(0x00002000) +#define SEC_CSR0_BSS3_KEY2_VALID FIELD32(0x00004000) +#define SEC_CSR0_BSS3_KEY3_VALID FIELD32(0x00008000) + +/* + * SEC_CSR1: Shared key table security mode register. + */ +#define SEC_CSR1 0x30a4 +#define SEC_CSR1_BSS0_KEY0_CIPHER_ALG FIELD32(0x00000007) +#define SEC_CSR1_BSS0_KEY1_CIPHER_ALG FIELD32(0x00000070) +#define SEC_CSR1_BSS0_KEY2_CIPHER_ALG FIELD32(0x00000700) +#define SEC_CSR1_BSS0_KEY3_CIPHER_ALG FIELD32(0x00007000) +#define SEC_CSR1_BSS1_KEY0_CIPHER_ALG FIELD32(0x00070000) +#define SEC_CSR1_BSS1_KEY1_CIPHER_ALG FIELD32(0x00700000) +#define SEC_CSR1_BSS1_KEY2_CIPHER_ALG FIELD32(0x07000000) +#define SEC_CSR1_BSS1_KEY3_CIPHER_ALG FIELD32(0x70000000) + +/* + * Pairwise key table valid bitmap registers. + * SEC_CSR2: pairwise key table valid bitmap 0. + * SEC_CSR3: pairwise key table valid bitmap 1. + */ +#define SEC_CSR2 0x30a8 +#define SEC_CSR3 0x30ac + +/* + * SEC_CSR4: Pairwise key table lookup control. + */ +#define SEC_CSR4 0x30b0 +#define SEC_CSR4_ENABLE_BSS0 FIELD32(0x00000001) +#define SEC_CSR4_ENABLE_BSS1 FIELD32(0x00000002) +#define SEC_CSR4_ENABLE_BSS2 FIELD32(0x00000004) +#define SEC_CSR4_ENABLE_BSS3 FIELD32(0x00000008) + +/* + * SEC_CSR5: shared key table security mode register. + */ +#define SEC_CSR5 0x30b4 +#define SEC_CSR5_BSS2_KEY0_CIPHER_ALG FIELD32(0x00000007) +#define SEC_CSR5_BSS2_KEY1_CIPHER_ALG FIELD32(0x00000070) +#define SEC_CSR5_BSS2_KEY2_CIPHER_ALG FIELD32(0x00000700) +#define SEC_CSR5_BSS2_KEY3_CIPHER_ALG FIELD32(0x00007000) +#define SEC_CSR5_BSS3_KEY0_CIPHER_ALG FIELD32(0x00070000) +#define SEC_CSR5_BSS3_KEY1_CIPHER_ALG FIELD32(0x00700000) +#define SEC_CSR5_BSS3_KEY2_CIPHER_ALG FIELD32(0x07000000) +#define SEC_CSR5_BSS3_KEY3_CIPHER_ALG FIELD32(0x70000000) + +/* + * STA control registers. + */ + +/* + * STA_CSR0: RX PLCP error count & RX FCS error count. + */ +#define STA_CSR0 0x30c0 +#define STA_CSR0_FCS_ERROR FIELD32(0x0000ffff) +#define STA_CSR0_PLCP_ERROR FIELD32(0xffff0000) + +/* + * STA_CSR1: RX False CCA count & RX LONG frame count. + */ +#define STA_CSR1 0x30c4 +#define STA_CSR1_PHYSICAL_ERROR FIELD32(0x0000ffff) +#define STA_CSR1_FALSE_CCA_ERROR FIELD32(0xffff0000) + +/* + * STA_CSR2: TX Beacon count and RX FIFO overflow count. + */ +#define STA_CSR2 0x30c8 +#define STA_CSR2_RX_FIFO_OVERFLOW_COUNT FIELD32(0x0000ffff) +#define STA_CSR2_RX_OVERFLOW_COUNT FIELD32(0xffff0000) + +/* + * STA_CSR3: TX Beacon count. + */ +#define STA_CSR3 0x30cc +#define STA_CSR3_TX_BEACON_COUNT FIELD32(0x0000ffff) + +/* + * STA_CSR4: TX Result status register. + * VALID: 1:This register contains a valid TX result. + */ +#define STA_CSR4 0x30d0 +#define STA_CSR4_VALID FIELD32(0x00000001) +#define STA_CSR4_TX_RESULT FIELD32(0x0000000e) +#define STA_CSR4_RETRY_COUNT FIELD32(0x000000f0) +#define STA_CSR4_PID_SUBTYPE FIELD32(0x00001f00) +#define STA_CSR4_PID_TYPE FIELD32(0x0000e000) +#define STA_CSR4_TXRATE FIELD32(0x000f0000) + +/* + * QOS control registers. + */ + +/* + * QOS_CSR0: TXOP holder MAC address register. + */ +#define QOS_CSR0 0x30e0 +#define QOS_CSR0_BYTE0 FIELD32(0x000000ff) +#define QOS_CSR0_BYTE1 FIELD32(0x0000ff00) +#define QOS_CSR0_BYTE2 FIELD32(0x00ff0000) +#define QOS_CSR0_BYTE3 FIELD32(0xff000000) + +/* + * QOS_CSR1: TXOP holder MAC address register. + */ +#define QOS_CSR1 0x30e4 +#define QOS_CSR1_BYTE4 FIELD32(0x000000ff) +#define QOS_CSR1_BYTE5 FIELD32(0x0000ff00) + +/* + * QOS_CSR2: TXOP holder timeout register. + */ +#define QOS_CSR2 0x30e8 + +/* + * RX QOS-CFPOLL MAC address register. + * QOS_CSR3: RX QOS-CFPOLL MAC address 0. + * QOS_CSR4: RX QOS-CFPOLL MAC address 1. + */ +#define QOS_CSR3 0x30ec +#define QOS_CSR4 0x30f0 + +/* + * QOS_CSR5: "QosControl" field of the RX QOS-CFPOLL. + */ +#define QOS_CSR5 0x30f4 + +/* + * Host DMA registers. + */ + +/* + * AC0_BASE_CSR: AC_VO base address. + */ +#define AC0_BASE_CSR 0x3400 +#define AC0_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * AC1_BASE_CSR: AC_VI base address. + */ +#define AC1_BASE_CSR 0x3404 +#define AC1_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * AC2_BASE_CSR: AC_BE base address. + */ +#define AC2_BASE_CSR 0x3408 +#define AC2_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * AC3_BASE_CSR: AC_BK base address. + */ +#define AC3_BASE_CSR 0x340c +#define AC3_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * MGMT_BASE_CSR: MGMT ring base address. + */ +#define MGMT_BASE_CSR 0x3410 +#define MGMT_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * TX_RING_CSR0: TX Ring size for AC_VO, AC_VI, AC_BE, AC_BK. + */ +#define TX_RING_CSR0 0x3418 +#define TX_RING_CSR0_AC0_RING_SIZE FIELD32(0x000000ff) +#define TX_RING_CSR0_AC1_RING_SIZE FIELD32(0x0000ff00) +#define TX_RING_CSR0_AC2_RING_SIZE FIELD32(0x00ff0000) +#define TX_RING_CSR0_AC3_RING_SIZE FIELD32(0xff000000) + +/* + * TX_RING_CSR1: TX Ring size for MGMT Ring, HCCA Ring + * TXD_SIZE: In unit of 32-bit. + */ +#define TX_RING_CSR1 0x341c +#define TX_RING_CSR1_MGMT_RING_SIZE FIELD32(0x000000ff) +#define TX_RING_CSR1_HCCA_RING_SIZE FIELD32(0x0000ff00) +#define TX_RING_CSR1_TXD_SIZE FIELD32(0x003f0000) + +/* + * AIFSN_CSR: AIFSN for each EDCA AC. + * AIFSN0: For AC_VO. + * AIFSN1: For AC_VI. + * AIFSN2: For AC_BE. + * AIFSN3: For AC_BK. + */ +#define AIFSN_CSR 0x3420 +#define AIFSN_CSR_AIFSN0 FIELD32(0x0000000f) +#define AIFSN_CSR_AIFSN1 FIELD32(0x000000f0) +#define AIFSN_CSR_AIFSN2 FIELD32(0x00000f00) +#define AIFSN_CSR_AIFSN3 FIELD32(0x0000f000) + +/* + * CWMIN_CSR: CWmin for each EDCA AC. + * CWMIN0: For AC_VO. + * CWMIN1: For AC_VI. + * CWMIN2: For AC_BE. + * CWMIN3: For AC_BK. + */ +#define CWMIN_CSR 0x3424 +#define CWMIN_CSR_CWMIN0 FIELD32(0x0000000f) +#define CWMIN_CSR_CWMIN1 FIELD32(0x000000f0) +#define CWMIN_CSR_CWMIN2 FIELD32(0x00000f00) +#define CWMIN_CSR_CWMIN3 FIELD32(0x0000f000) + +/* + * CWMAX_CSR: CWmax for each EDCA AC. + * CWMAX0: For AC_VO. + * CWMAX1: For AC_VI. + * CWMAX2: For AC_BE. + * CWMAX3: For AC_BK. + */ +#define CWMAX_CSR 0x3428 +#define CWMAX_CSR_CWMAX0 FIELD32(0x0000000f) +#define CWMAX_CSR_CWMAX1 FIELD32(0x000000f0) +#define CWMAX_CSR_CWMAX2 FIELD32(0x00000f00) +#define CWMAX_CSR_CWMAX3 FIELD32(0x0000f000) + +/* + * TX_DMA_DST_CSR: TX DMA destination + * 0: TX ring0, 1: TX ring1, 2: TX ring2 3: invalid + */ +#define TX_DMA_DST_CSR 0x342c +#define TX_DMA_DST_CSR_DEST_AC0 FIELD32(0x00000003) +#define TX_DMA_DST_CSR_DEST_AC1 FIELD32(0x0000000c) +#define TX_DMA_DST_CSR_DEST_AC2 FIELD32(0x00000030) +#define TX_DMA_DST_CSR_DEST_AC3 FIELD32(0x000000c0) +#define TX_DMA_DST_CSR_DEST_MGMT FIELD32(0x00000300) + +/* + * TX_CNTL_CSR: KICK/Abort TX. + * KICK_TX_AC0: For AC_VO. + * KICK_TX_AC1: For AC_VI. + * KICK_TX_AC2: For AC_BE. + * KICK_TX_AC3: For AC_BK. + * ABORT_TX_AC0: For AC_VO. + * ABORT_TX_AC1: For AC_VI. + * ABORT_TX_AC2: For AC_BE. + * ABORT_TX_AC3: For AC_BK. + */ +#define TX_CNTL_CSR 0x3430 +#define TX_CNTL_CSR_KICK_TX_AC0 FIELD32(0x00000001) +#define TX_CNTL_CSR_KICK_TX_AC1 FIELD32(0x00000002) +#define TX_CNTL_CSR_KICK_TX_AC2 FIELD32(0x00000004) +#define TX_CNTL_CSR_KICK_TX_AC3 FIELD32(0x00000008) +#define TX_CNTL_CSR_KICK_TX_MGMT FIELD32(0x00000010) +#define TX_CNTL_CSR_ABORT_TX_AC0 FIELD32(0x00010000) +#define TX_CNTL_CSR_ABORT_TX_AC1 FIELD32(0x00020000) +#define TX_CNTL_CSR_ABORT_TX_AC2 FIELD32(0x00040000) +#define TX_CNTL_CSR_ABORT_TX_AC3 FIELD32(0x00080000) +#define TX_CNTL_CSR_ABORT_TX_MGMT FIELD32(0x00100000) + +/* + * LOAD_TX_RING_CSR: Load RX desriptor + */ +#define LOAD_TX_RING_CSR 0x3434 +#define LOAD_TX_RING_CSR_LOAD_TXD_AC0 FIELD32(0x00000001) +#define LOAD_TX_RING_CSR_LOAD_TXD_AC1 FIELD32(0x00000002) +#define LOAD_TX_RING_CSR_LOAD_TXD_AC2 FIELD32(0x00000004) +#define LOAD_TX_RING_CSR_LOAD_TXD_AC3 FIELD32(0x00000008) +#define LOAD_TX_RING_CSR_LOAD_TXD_MGMT FIELD32(0x00000010) + +/* + * Several read-only registers, for debugging. + */ +#define AC0_TXPTR_CSR 0x3438 +#define AC1_TXPTR_CSR 0x343c +#define AC2_TXPTR_CSR 0x3440 +#define AC3_TXPTR_CSR 0x3444 +#define MGMT_TXPTR_CSR 0x3448 + +/* + * RX_BASE_CSR + */ +#define RX_BASE_CSR 0x3450 +#define RX_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * RX_RING_CSR. + * RXD_SIZE: In unit of 32-bit. + */ +#define RX_RING_CSR 0x3454 +#define RX_RING_CSR_RING_SIZE FIELD32(0x000000ff) +#define RX_RING_CSR_RXD_SIZE FIELD32(0x00003f00) +#define RX_RING_CSR_RXD_WRITEBACK_SIZE FIELD32(0x00070000) + +/* + * RX_CNTL_CSR + */ +#define RX_CNTL_CSR 0x3458 +#define RX_CNTL_CSR_ENABLE_RX_DMA FIELD32(0x00000001) +#define RX_CNTL_CSR_LOAD_RXD FIELD32(0x00000002) + +/* + * RXPTR_CSR: Read-only, for debugging. + */ +#define RXPTR_CSR 0x345c + +/* + * PCI_CFG_CSR + */ +#define PCI_CFG_CSR 0x3460 + +/* + * BUF_FORMAT_CSR + */ +#define BUF_FORMAT_CSR 0x3464 + +/* + * INT_SOURCE_CSR: Interrupt source register. + * Write one to clear corresponding bit. + */ +#define INT_SOURCE_CSR 0x3468 +#define INT_SOURCE_CSR_TXDONE FIELD32(0x00000001) +#define INT_SOURCE_CSR_RXDONE FIELD32(0x00000002) +#define INT_SOURCE_CSR_BEACON_DONE FIELD32(0x00000004) +#define INT_SOURCE_CSR_TX_ABORT_DONE FIELD32(0x00000010) +#define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00010000) +#define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00020000) +#define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00040000) +#define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00080000) +#define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00100000) +#define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00200000) + +/* + * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF. + * MITIGATION_PERIOD: Interrupt mitigation in unit of 32 PCI clock. + */ +#define INT_MASK_CSR 0x346c +#define INT_MASK_CSR_TXDONE FIELD32(0x00000001) +#define INT_MASK_CSR_RXDONE FIELD32(0x00000002) +#define INT_MASK_CSR_BEACON_DONE FIELD32(0x00000004) +#define INT_MASK_CSR_TX_ABORT_DONE FIELD32(0x00000010) +#define INT_MASK_CSR_ENABLE_MITIGATION FIELD32(0x00000080) +#define INT_MASK_CSR_MITIGATION_PERIOD FIELD32(0x0000ff00) +#define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00010000) +#define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00020000) +#define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00040000) +#define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00080000) +#define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00100000) +#define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00200000) + +/* + * E2PROM_CSR: EEPROM control register. + * RELOAD: Write 1 to reload eeprom content. + * TYPE_93C46: 1: 93c46, 0:93c66. + * LOAD_STATUS: 1:loading, 0:done. + */ +#define E2PROM_CSR 0x3470 +#define E2PROM_CSR_RELOAD FIELD32(0x00000001) +#define E2PROM_CSR_DATA_CLOCK FIELD32(0x00000002) +#define E2PROM_CSR_CHIP_SELECT FIELD32(0x00000004) +#define E2PROM_CSR_DATA_IN FIELD32(0x00000008) +#define E2PROM_CSR_DATA_OUT FIELD32(0x00000010) +#define E2PROM_CSR_TYPE_93C46 FIELD32(0x00000020) +#define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040) + +/* + * AC_TXOP_CSR0: AC_VO/AC_VI TXOP register. + * AC0_TX_OP: For AC_VO, in unit of 32us. + * AC1_TX_OP: For AC_VI, in unit of 32us. + */ +#define AC_TXOP_CSR0 0x3474 +#define AC_TXOP_CSR0_AC0_TX_OP FIELD32(0x0000ffff) +#define AC_TXOP_CSR0_AC1_TX_OP FIELD32(0xffff0000) + +/* + * AC_TXOP_CSR1: AC_BE/AC_BK TXOP register. + * AC2_TX_OP: For AC_BE, in unit of 32us. + * AC3_TX_OP: For AC_BK, in unit of 32us. + */ +#define AC_TXOP_CSR1 0x3478 +#define AC_TXOP_CSR1_AC2_TX_OP FIELD32(0x0000ffff) +#define AC_TXOP_CSR1_AC3_TX_OP FIELD32(0xffff0000) + +/* + * DMA_STATUS_CSR + */ +#define DMA_STATUS_CSR 0x3480 + +/* + * TEST_MODE_CSR + */ +#define TEST_MODE_CSR 0x3484 + +/* + * UART0_TX_CSR + */ +#define UART0_TX_CSR 0x3488 + +/* + * UART0_RX_CSR + */ +#define UART0_RX_CSR 0x348c + +/* + * UART0_FRAME_CSR + */ +#define UART0_FRAME_CSR 0x3490 + +/* + * UART0_BUFFER_CSR + */ +#define UART0_BUFFER_CSR 0x3494 + +/* + * IO_CNTL_CSR + * RF_PS: Set RF interface value to power save + */ +#define IO_CNTL_CSR 0x3498 +#define IO_CNTL_CSR_RF_PS FIELD32(0x00000004) + +/* + * UART_INT_SOURCE_CSR + */ +#define UART_INT_SOURCE_CSR 0x34a8 + +/* + * UART_INT_MASK_CSR + */ +#define UART_INT_MASK_CSR 0x34ac + +/* + * PBF_QUEUE_CSR + */ +#define PBF_QUEUE_CSR 0x34b0 + +/* + * Firmware DMA registers. + * Firmware DMA registers are dedicated for MCU usage + * and should not be touched by host driver. + * Therefore we skip the definition of these registers. + */ +#define FW_TX_BASE_CSR 0x34c0 +#define FW_TX_START_CSR 0x34c4 +#define FW_TX_LAST_CSR 0x34c8 +#define FW_MODE_CNTL_CSR 0x34cc +#define FW_TXPTR_CSR 0x34d0 + +/* + * 8051 firmware image. + */ +#define FIRMWARE_RT2561 "rt2561.bin" +#define FIRMWARE_RT2561s "rt2561s.bin" +#define FIRMWARE_RT2661 "rt2661.bin" +#define FIRMWARE_IMAGE_BASE 0x4000 + +/* + * BBP registers. + * The wordsize of the BBP is 8 bits. + */ + +/* + * R2 + */ +#define BBP_R2_BG_MODE FIELD8(0x20) + +/* + * R3 + */ +#define BBP_R3_SMART_MODE FIELD8(0x01) + +/* + * R4: RX antenna control + * FRAME_END: 1 - DPDT, 0 - SPDT (Only valid for 802.11G, RF2527 & RF2529) + */ + +/* + * ANTENNA_CONTROL semantics (guessed): + * 0x1: Software controlled antenna switching (fixed or SW diversity) + * 0x2: Hardware diversity. + */ +#define BBP_R4_RX_ANTENNA_CONTROL FIELD8(0x03) +#define BBP_R4_RX_FRAME_END FIELD8(0x20) + +/* + * R77 + */ +#define BBP_R77_RX_ANTENNA FIELD8(0x03) + +/* + * RF registers + */ + +/* + * RF 3 + */ +#define RF3_TXPOWER FIELD32(0x00003e00) + +/* + * RF 4 + */ +#define RF4_FREQ_OFFSET FIELD32(0x0003f000) + +/* + * EEPROM content. + * The wordsize of the EEPROM is 16 bits. + */ + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_0 0x0002 +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR1 0x0003 +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_2 0x0004 +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM antenna. + * ANTENNA_NUM: Number of antenna's. + * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * FRAME_TYPE: 0: DPDT , 1: SPDT , noted this bit is valid for g only. + * DYN_TXAGC: Dynamic TX AGC control. + * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. + * RF_TYPE: Rf_type of this adapter. + */ +#define EEPROM_ANTENNA 0x0010 +#define EEPROM_ANTENNA_NUM FIELD16(0x0003) +#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) +#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) +#define EEPROM_ANTENNA_FRAME_TYPE FIELD16(0x0040) +#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) +#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) +#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) + +/* + * EEPROM NIC config. + * ENABLE_DIVERSITY: 1:enable, 0:disable. + * EXTERNAL_LNA_BG: External LNA enable for 2.4G. + * CARDBUS_ACCEL: 0:enable, 1:disable. + * EXTERNAL_LNA_A: External LNA enable for 5G. + */ +#define EEPROM_NIC 0x0011 +#define EEPROM_NIC_ENABLE_DIVERSITY FIELD16(0x0001) +#define EEPROM_NIC_TX_DIVERSITY FIELD16(0x0002) +#define EEPROM_NIC_RX_FIXED FIELD16(0x0004) +#define EEPROM_NIC_TX_FIXED FIELD16(0x0008) +#define EEPROM_NIC_EXTERNAL_LNA_BG FIELD16(0x0010) +#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0020) +#define EEPROM_NIC_EXTERNAL_LNA_A FIELD16(0x0040) + +/* + * EEPROM geography. + * GEO_A: Default geographical setting for 5GHz band + * GEO: Default geographical setting. + */ +#define EEPROM_GEOGRAPHY 0x0012 +#define EEPROM_GEOGRAPHY_GEO_A FIELD16(0x00ff) +#define EEPROM_GEOGRAPHY_GEO FIELD16(0xff00) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_START 0x0013 +#define EEPROM_BBP_SIZE 16 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* + * EEPROM TXPOWER 802.11G + */ +#define EEPROM_TXPOWER_G_START 0x0023 +#define EEPROM_TXPOWER_G_SIZE 7 +#define EEPROM_TXPOWER_G_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_G_2 FIELD16(0xff00) + +/* + * EEPROM Frequency + */ +#define EEPROM_FREQ 0x002f +#define EEPROM_FREQ_OFFSET FIELD16(0x00ff) +#define EEPROM_FREQ_SEQ_MASK FIELD16(0xff00) +#define EEPROM_FREQ_SEQ FIELD16(0x0300) + +/* + * EEPROM LED. + * POLARITY_RDY_G: Polarity RDY_G setting. + * POLARITY_RDY_A: Polarity RDY_A setting. + * POLARITY_ACT: Polarity ACT setting. + * POLARITY_GPIO_0: Polarity GPIO0 setting. + * POLARITY_GPIO_1: Polarity GPIO1 setting. + * POLARITY_GPIO_2: Polarity GPIO2 setting. + * POLARITY_GPIO_3: Polarity GPIO3 setting. + * POLARITY_GPIO_4: Polarity GPIO4 setting. + * LED_MODE: Led mode. + */ +#define EEPROM_LED 0x0030 +#define EEPROM_LED_POLARITY_RDY_G FIELD16(0x0001) +#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) +#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) +#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) +#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) +#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) +#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) +#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) +#define EEPROM_LED_LED_MODE FIELD16(0x1f00) + +/* + * EEPROM TXPOWER 802.11A + */ +#define EEPROM_TXPOWER_A_START 0x0031 +#define EEPROM_TXPOWER_A_SIZE 12 +#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) + +/* + * EEPROM RSSI offset 802.11BG + */ +#define EEPROM_RSSI_OFFSET_BG 0x004d +#define EEPROM_RSSI_OFFSET_BG_1 FIELD16(0x00ff) +#define EEPROM_RSSI_OFFSET_BG_2 FIELD16(0xff00) + +/* + * EEPROM RSSI offset 802.11A + */ +#define EEPROM_RSSI_OFFSET_A 0x004e +#define EEPROM_RSSI_OFFSET_A_1 FIELD16(0x00ff) +#define EEPROM_RSSI_OFFSET_A_2 FIELD16(0xff00) + +/* + * MCU mailbox commands. + */ +#define MCU_SLEEP 0x30 +#define MCU_WAKEUP 0x31 +#define MCU_LED 0x50 +#define MCU_LED_STRENGTH 0x52 + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE ( 16 * sizeof(__le32) ) +#define TXINFO_SIZE ( 6 * sizeof(__le32) ) +#define RXD_DESC_SIZE ( 16 * sizeof(__le32) ) + +/* + * TX descriptor format for TX, PRIO and Beacon Ring. + */ + +/* + * Word0 + * TKIP_MIC: ASIC appends TKIP MIC if TKIP is used. + * KEY_TABLE: Use per-client pairwise KEY table. + * KEY_INDEX: + * Key index (0~31) to the pairwise KEY table. + * 0~3 to shared KEY table 0 (BSS0). + * 4~7 to shared KEY table 1 (BSS1). + * 8~11 to shared KEY table 2 (BSS2). + * 12~15 to shared KEY table 3 (BSS3). + * BURST: Next frame belongs to same "burst" event. + */ +#define TXD_W0_OWNER_NIC FIELD32(0x00000001) +#define TXD_W0_VALID FIELD32(0x00000002) +#define TXD_W0_MORE_FRAG FIELD32(0x00000004) +#define TXD_W0_ACK FIELD32(0x00000008) +#define TXD_W0_TIMESTAMP FIELD32(0x00000010) +#define TXD_W0_OFDM FIELD32(0x00000020) +#define TXD_W0_IFS FIELD32(0x00000040) +#define TXD_W0_RETRY_MODE FIELD32(0x00000080) +#define TXD_W0_TKIP_MIC FIELD32(0x00000100) +#define TXD_W0_KEY_TABLE FIELD32(0x00000200) +#define TXD_W0_KEY_INDEX FIELD32(0x0000fc00) +#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define TXD_W0_BURST FIELD32(0x10000000) +#define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * Word1 + * HOST_Q_ID: EDCA/HCCA queue ID. + * HW_SEQUENCE: MAC overwrites the frame sequence number. + * BUFFER_COUNT: Number of buffers in this TXD. + */ +#define TXD_W1_HOST_Q_ID FIELD32(0x0000000f) +#define TXD_W1_AIFSN FIELD32(0x000000f0) +#define TXD_W1_CWMIN FIELD32(0x00000f00) +#define TXD_W1_CWMAX FIELD32(0x0000f000) +#define TXD_W1_IV_OFFSET FIELD32(0x003f0000) +#define TXD_W1_PIGGY_BACK FIELD32(0x01000000) +#define TXD_W1_HW_SEQUENCE FIELD32(0x10000000) +#define TXD_W1_BUFFER_COUNT FIELD32(0xe0000000) + +/* + * Word2: PLCP information + */ +#define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) +#define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) +#define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) +#define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) + +/* + * Word3 + */ +#define TXD_W3_IV FIELD32(0xffffffff) + +/* + * Word4 + */ +#define TXD_W4_EIV FIELD32(0xffffffff) + +/* + * Word5 + * FRAME_OFFSET: Frame start offset inside ASIC TXFIFO (after TXINFO field). + * TXD_W5_PID_SUBTYPE: Driver assigned packet ID index for txdone handler. + * TXD_W5_PID_TYPE: Driver assigned packet ID type for txdone handler. + * WAITING_DMA_DONE_INT: TXD been filled with data + * and waiting for TxDoneISR housekeeping. + */ +#define TXD_W5_FRAME_OFFSET FIELD32(0x000000ff) +#define TXD_W5_PID_SUBTYPE FIELD32(0x00001f00) +#define TXD_W5_PID_TYPE FIELD32(0x0000e000) +#define TXD_W5_TX_POWER FIELD32(0x00ff0000) +#define TXD_W5_WAITING_DMA_DONE_INT FIELD32(0x01000000) + +/* + * the above 24-byte is called TXINFO and will be DMAed to MAC block + * through TXFIFO. MAC block use this TXINFO to control the transmission + * behavior of this frame. + * The following fields are not used by MAC block. + * They are used by DMA block and HOST driver only. + * Once a frame has been DMA to ASIC, all the following fields are useless + * to ASIC. + */ + +/* + * Word6-10: Buffer physical address + */ +#define TXD_W6_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) +#define TXD_W7_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) +#define TXD_W8_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) +#define TXD_W9_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) +#define TXD_W10_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) + +/* + * Word11-13: Buffer length + */ +#define TXD_W11_BUFFER_LENGTH0 FIELD32(0x00000fff) +#define TXD_W11_BUFFER_LENGTH1 FIELD32(0x0fff0000) +#define TXD_W12_BUFFER_LENGTH2 FIELD32(0x00000fff) +#define TXD_W12_BUFFER_LENGTH3 FIELD32(0x0fff0000) +#define TXD_W13_BUFFER_LENGTH4 FIELD32(0x00000fff) + +/* + * Word14 + */ +#define TXD_W14_SK_BUFFER FIELD32(0xffffffff) + +/* + * Word15 + */ +#define TXD_W15_NEXT_SK_BUFFER FIELD32(0xffffffff) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + * CIPHER_ERROR: 1:ICV error, 2:MIC error, 3:invalid key. + * KEY_INDEX: Decryption key actually used. + */ +#define RXD_W0_OWNER_NIC FIELD32(0x00000001) +#define RXD_W0_DROP FIELD32(0x00000002) +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000004) +#define RXD_W0_MULTICAST FIELD32(0x00000008) +#define RXD_W0_BROADCAST FIELD32(0x00000010) +#define RXD_W0_MY_BSS FIELD32(0x00000020) +#define RXD_W0_CRC_ERROR FIELD32(0x00000040) +#define RXD_W0_OFDM FIELD32(0x00000080) +#define RXD_W0_CIPHER_ERROR FIELD32(0x00000300) +#define RXD_W0_KEY_INDEX FIELD32(0x0000fc00) +#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * Word1 + * SIGNAL: RX raw data rate reported by BBP. + */ +#define RXD_W1_SIGNAL FIELD32(0x000000ff) +#define RXD_W1_RSSI_AGC FIELD32(0x00001f00) +#define RXD_W1_RSSI_LNA FIELD32(0x00006000) +#define RXD_W1_FRAME_OFFSET FIELD32(0x7f000000) + +/* + * Word2 + * IV: Received IV of originally encrypted. + */ +#define RXD_W2_IV FIELD32(0xffffffff) + +/* + * Word3 + * EIV: Received EIV of originally encrypted. + */ +#define RXD_W3_EIV FIELD32(0xffffffff) + +/* + * Word4 + * ICV: Received ICV of originally encrypted. + * NOTE: This is a guess, the official definition is "reserved" + */ +#define RXD_W4_ICV FIELD32(0xffffffff) + +/* + * the above 20-byte is called RXINFO and will be DMAed to MAC RX block + * and passed to the HOST driver. + * The following fields are for DMA block and HOST usage only. + * Can't be touched by ASIC MAC block. + */ + +/* + * Word5 + */ +#define RXD_W5_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) + +/* + * Word6-15: Reserved + */ +#define RXD_W6_RESERVED FIELD32(0xffffffff) +#define RXD_W7_RESERVED FIELD32(0xffffffff) +#define RXD_W8_RESERVED FIELD32(0xffffffff) +#define RXD_W9_RESERVED FIELD32(0xffffffff) +#define RXD_W10_RESERVED FIELD32(0xffffffff) +#define RXD_W11_RESERVED FIELD32(0xffffffff) +#define RXD_W12_RESERVED FIELD32(0xffffffff) +#define RXD_W13_RESERVED FIELD32(0xffffffff) +#define RXD_W14_RESERVED FIELD32(0xffffffff) +#define RXD_W15_RESERVED FIELD32(0xffffffff) + +/* + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. + */ +#define MIN_TXPOWER 0 +#define MAX_TXPOWER 31 +#define DEFAULT_TXPOWER 24 + +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) + +#endif /* RT61PCI_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt73usb.c b/drivers/net/wireless/ralink/rt2x00/rt73usb.c new file mode 100644 index 000000000000..7081e13b4fd6 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c @@ -0,0 +1,2548 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt73usb + Abstract: rt73usb device specific routines. + Supported chipsets: rt2571W & rt2671. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00usb.h" +#include "rt73usb.h" + +/* + * Allow hardware encryption to be disabled. + */ +static bool modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2x00usb_register_read and rt2x00usb_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers BBPCSR and RFCSR to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attampt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + * The _lock versions must be used if you already hold the csr_mutex + */ +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2x00usb_regbusy_read((__dev), PHY_CSR3, PHY_CSR3_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2x00usb_regbusy_read((__dev), PHY_CSR4, PHY_CSR4_BUSY, (__reg)) + +static void rt73usb_bbp_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR3_VALUE, value); + rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); + + rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u8 *value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); + + rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); + + WAIT_FOR_BBP(rt2x00dev, ®); + } + + *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt73usb_rf_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u32 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR4_VALUE, value); + /* + * RF5225 and RF2527 contain 21 bits per RF register value, + * all others contain 20 bits. + */ + rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, + 20 + (rt2x00_rf(rt2x00dev, RF5225) || + rt2x00_rf(rt2x00dev, RF2527))); + rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); + rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); + + rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR4, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +#ifdef CONFIG_RT2X00_LIB_DEBUGFS +static const struct rt2x00debug rt73usb_rt2x00debug = { + .owner = THIS_MODULE, + .csr = { + .read = rt2x00usb_register_read, + .write = rt2x00usb_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, + .word_size = sizeof(u32), + .word_count = CSR_REG_SIZE / sizeof(u32), + }, + .eeprom = { + .read = rt2x00_eeprom_read, + .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .bbp = { + .read = rt73usb_bbp_read, + .write = rt73usb_bbp_write, + .word_base = BBP_BASE, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, + .rf = { + .read = rt2x00_rf_read, + .write = rt73usb_rf_write, + .word_base = RF_BASE, + .word_size = sizeof(u32), + .word_count = RF_SIZE / sizeof(u32), + }, +}; +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ + +static int rt73usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00usb_register_read(rt2x00dev, MAC_CSR13, ®); + return rt2x00_get_field32(reg, MAC_CSR13_VAL7); +} + +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt73usb_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + unsigned int a_mode = + (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); + unsigned int bg_mode = + (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); + + if (led->type == LED_TYPE_RADIO) { + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_RADIO_STATUS, enabled); + + rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, + 0, led->rt2x00dev->led_mcu_reg, + REGISTER_TIMEOUT); + } else if (led->type == LED_TYPE_ASSOC) { + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_LINK_BG_STATUS, bg_mode); + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_LINK_A_STATUS, a_mode); + + rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, + 0, led->rt2x00dev->led_mcu_reg, + REGISTER_TIMEOUT); + } else if (led->type == LED_TYPE_QUALITY) { + /* + * The brightness is divided into 6 levels (0 - 5), + * this means we need to convert the brightness + * argument into the matching level within that range. + */ + rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, + brightness / (LED_FULL / 6), + led->rt2x00dev->led_mcu_reg, + REGISTER_TIMEOUT); + } +} + +static int rt73usb_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + u32 reg; + + rt2x00usb_register_read(led->rt2x00dev, MAC_CSR14, ®); + rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, *delay_on); + rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, *delay_off); + rt2x00usb_register_write(led->rt2x00dev, MAC_CSR14, reg); + + return 0; +} + +static void rt73usb_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + enum led_type type) +{ + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt73usb_brightness_set; + led->led_dev.blink_set = rt73usb_blink_set; + led->flags = LED_INITIALIZED; +} +#endif /* CONFIG_RT2X00_LIB_LEDS */ + +/* + * Configuration handlers. + */ +static int rt73usb_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_key_entry key_entry; + struct rt2x00_field32 field; + u32 mask; + u32 reg; + + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for shared keys. We have 1 register + * with key valid bits. The goal is simple, read + * the register, if that is full we have no slots + * left. + * Note that each BSS is allowed to have up to 4 + * shared keys, so put a mask over the allowed + * entries. + */ + mask = (0xf << crypto->bssidx); + + rt2x00usb_register_read(rt2x00dev, SEC_CSR0, ®); + reg &= mask; + + if (reg && reg == mask) + return -ENOSPC; + + key->hw_key_idx += reg ? ffz(reg) : 0; + + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + reg = SHARED_KEY_ENTRY(key->hw_key_idx); + rt2x00usb_register_multiwrite(rt2x00dev, reg, + &key_entry, sizeof(key_entry)); + + /* + * The cipher types are stored over 2 registers. + * bssidx 0 and 1 keys are stored in SEC_CSR1 and + * bssidx 1 and 2 keys are stored in SEC_CSR5. + * Using the correct defines correctly will cause overhead, + * so just calculate the correct offset. + */ + if (key->hw_key_idx < 8) { + field.bit_offset = (3 * key->hw_key_idx); + field.bit_mask = 0x7 << field.bit_offset; + + rt2x00usb_register_read(rt2x00dev, SEC_CSR1, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt2x00usb_register_write(rt2x00dev, SEC_CSR1, reg); + } else { + field.bit_offset = (3 * (key->hw_key_idx - 8)); + field.bit_mask = 0x7 << field.bit_offset; + + rt2x00usb_register_read(rt2x00dev, SEC_CSR5, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt2x00usb_register_write(rt2x00dev, SEC_CSR5, reg); + } + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided separately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } + + /* + * SEC_CSR0 contains only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead we use + * a calculation to determine the correct bit directly. + */ + mask = 1 << key->hw_key_idx; + + rt2x00usb_register_read(rt2x00dev, SEC_CSR0, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00usb_register_write(rt2x00dev, SEC_CSR0, reg); + + return 0; +} + +static int rt73usb_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_pairwise_ta_entry addr_entry; + struct hw_key_entry key_entry; + u32 mask; + u32 reg; + + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for pairwise keys. We have 2 registers + * with key valid bits. The goal is simple, read + * the first register, if that is full move to + * the next register. + * When both registers are full, we drop the key, + * otherwise we use the first invalid entry. + */ + rt2x00usb_register_read(rt2x00dev, SEC_CSR2, ®); + if (reg && reg == ~0) { + key->hw_key_idx = 32; + rt2x00usb_register_read(rt2x00dev, SEC_CSR3, ®); + if (reg && reg == ~0) + return -ENOSPC; + } + + key->hw_key_idx += reg ? ffz(reg) : 0; + + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + reg = PAIRWISE_KEY_ENTRY(key->hw_key_idx); + rt2x00usb_register_multiwrite(rt2x00dev, reg, + &key_entry, sizeof(key_entry)); + + /* + * Send the address and cipher type to the hardware register. + */ + memset(&addr_entry, 0, sizeof(addr_entry)); + memcpy(&addr_entry, crypto->address, ETH_ALEN); + addr_entry.cipher = crypto->cipher; + + reg = PAIRWISE_TA_ENTRY(key->hw_key_idx); + rt2x00usb_register_multiwrite(rt2x00dev, reg, + &addr_entry, sizeof(addr_entry)); + + /* + * Enable pairwise lookup table for given BSS idx, + * without this received frames will not be decrypted + * by the hardware. + */ + rt2x00usb_register_read(rt2x00dev, SEC_CSR4, ®); + reg |= (1 << crypto->bssidx); + rt2x00usb_register_write(rt2x00dev, SEC_CSR4, reg); + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided separately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } + + /* + * SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead we use + * a calculation to determine the correct bit directly. + */ + if (key->hw_key_idx < 32) { + mask = 1 << key->hw_key_idx; + + rt2x00usb_register_read(rt2x00dev, SEC_CSR2, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00usb_register_write(rt2x00dev, SEC_CSR2, reg); + } else { + mask = 1 << (key->hw_key_idx - 32); + + rt2x00usb_register_read(rt2x00dev, SEC_CSR3, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00usb_register_write(rt2x00dev, SEC_CSR3, reg); + } + + return 0; +} + +static void rt73usb_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) +{ + u32 reg; + + /* + * Start configuration steps. + * Note that the version error will always be dropped + * and broadcast frames will always be accepted since + * there is no filter for it at this time. + */ + rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, + !(filter_flags & (FIF_CONTROL | FIF_PSPOLL))); + rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, 1); + rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, + !rt2x00dev->intf_ap_count); + rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); + rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, + !(filter_flags & FIF_ALLMULTI)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_BROADCAST, 0); + rt2x00_set_field32(®, TXRX_CSR0_DROP_ACK_CTS, + !(filter_flags & FIF_CONTROL)); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); +} + +static void rt73usb_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags) +{ + u32 reg; + + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable synchronisation. + */ + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, conf->sync); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + } + + if (flags & CONFIG_UPDATE_MAC) { + reg = le32_to_cpu(conf->mac[1]); + rt2x00_set_field32(®, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); + conf->mac[1] = cpu_to_le32(reg); + + rt2x00usb_register_multiwrite(rt2x00dev, MAC_CSR2, + conf->mac, sizeof(conf->mac)); + } + + if (flags & CONFIG_UPDATE_BSSID) { + reg = le32_to_cpu(conf->bssid[1]); + rt2x00_set_field32(®, MAC_CSR5_BSS_ID_MASK, 3); + conf->bssid[1] = cpu_to_le32(reg); + + rt2x00usb_register_multiwrite(rt2x00dev, MAC_CSR4, + conf->bssid, sizeof(conf->bssid)); + } +} + +static void rt73usb_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed) +{ + u32 reg; + + rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, 0x32); + rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, + !!erp->short_preamble); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg); + } + + if (changed & BSS_CHANGED_BASIC_RATES) + rt2x00usb_register_write(rt2x00dev, TXRX_CSR5, + erp->basic_rates); + + if (changed & BSS_CHANGED_BEACON_INT) { + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, + erp->beacon_int * 16); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2x00usb_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, erp->slot_time); + rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg); + + rt2x00usb_register_read(rt2x00dev, MAC_CSR8, ®); + rt2x00_set_field32(®, MAC_CSR8_SIFS, erp->sifs); + rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); + rt2x00_set_field32(®, MAC_CSR8_EIFS, erp->eifs); + rt2x00usb_register_write(rt2x00dev, MAC_CSR8, reg); + } +} + +static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) +{ + u8 r3; + u8 r4; + u8 r77; + u8 temp; + + rt73usb_bbp_read(rt2x00dev, 3, &r3); + rt73usb_bbp_read(rt2x00dev, 4, &r4); + rt73usb_bbp_read(rt2x00dev, 77, &r77); + + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0); + + /* + * Configure the RX antenna. + */ + switch (ant->rx) { + case ANTENNA_HW_DIVERSITY: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); + temp = !rt2x00_has_cap_frame_type(rt2x00dev) && + (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ); + rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, temp); + break; + case ANTENNA_A: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); + rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); + else + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); + rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); + else + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); + break; + } + + rt73usb_bbp_write(rt2x00dev, 77, r77); + rt73usb_bbp_write(rt2x00dev, 3, r3); + rt73usb_bbp_write(rt2x00dev, 4, r4); +} + +static void rt73usb_config_antenna_2x(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) +{ + u8 r3; + u8 r4; + u8 r77; + + rt73usb_bbp_read(rt2x00dev, 3, &r3); + rt73usb_bbp_read(rt2x00dev, 4, &r4); + rt73usb_bbp_read(rt2x00dev, 77, &r77); + + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0); + rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, + !rt2x00_has_cap_frame_type(rt2x00dev)); + + /* + * Configure the RX antenna. + */ + switch (ant->rx) { + case ANTENNA_HW_DIVERSITY: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); + break; + case ANTENNA_A: + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); + break; + } + + rt73usb_bbp_write(rt2x00dev, 77, r77); + rt73usb_bbp_write(rt2x00dev, 3, r3); + rt73usb_bbp_write(rt2x00dev, 4, r4); +} + +struct antenna_sel { + u8 word; + /* + * value[0] -> non-LNA + * value[1] -> LNA + */ + u8 value[2]; +}; + +static const struct antenna_sel antenna_sel_a[] = { + { 96, { 0x58, 0x78 } }, + { 104, { 0x38, 0x48 } }, + { 75, { 0xfe, 0x80 } }, + { 86, { 0xfe, 0x80 } }, + { 88, { 0xfe, 0x80 } }, + { 35, { 0x60, 0x60 } }, + { 97, { 0x58, 0x58 } }, + { 98, { 0x58, 0x58 } }, +}; + +static const struct antenna_sel antenna_sel_bg[] = { + { 96, { 0x48, 0x68 } }, + { 104, { 0x2c, 0x3c } }, + { 75, { 0xfe, 0x80 } }, + { 86, { 0xfe, 0x80 } }, + { 88, { 0xfe, 0x80 } }, + { 35, { 0x50, 0x50 } }, + { 97, { 0x48, 0x48 } }, + { 98, { 0x48, 0x48 } }, +}; + +static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) +{ + const struct antenna_sel *sel; + unsigned int lna; + unsigned int i; + u32 reg; + + /* + * We should never come here because rt2x00lib is supposed + * to catch this and send us the correct antenna explicitely. + */ + BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || + ant->tx == ANTENNA_SW_DIVERSITY); + + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { + sel = antenna_sel_a; + lna = rt2x00_has_cap_external_lna_a(rt2x00dev); + } else { + sel = antenna_sel_bg; + lna = rt2x00_has_cap_external_lna_bg(rt2x00dev); + } + + for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) + rt73usb_bbp_write(rt2x00dev, sel[i].word, sel[i].value[lna]); + + rt2x00usb_register_read(rt2x00dev, PHY_CSR0, ®); + + rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, + (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ)); + rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, + (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ)); + + rt2x00usb_register_write(rt2x00dev, PHY_CSR0, reg); + + if (rt2x00_rf(rt2x00dev, RF5226) || rt2x00_rf(rt2x00dev, RF5225)) + rt73usb_config_antenna_5x(rt2x00dev, ant); + else if (rt2x00_rf(rt2x00dev, RF2528) || rt2x00_rf(rt2x00dev, RF2527)) + rt73usb_config_antenna_2x(rt2x00dev, ant); +} + +static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u16 eeprom; + short lna_gain = 0; + + if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) + lna_gain += 14; + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); + } else { + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); + } + + rt2x00dev->lna_gain = lna_gain; +} + +static void rt73usb_config_channel(struct rt2x00_dev *rt2x00dev, + struct rf_channel *rf, const int txpower) +{ + u8 r3; + u8 r94; + u8 smart; + + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); + rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); + + smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)); + + rt73usb_bbp_read(rt2x00dev, 3, &r3); + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); + rt73usb_bbp_write(rt2x00dev, 3, r3); + + r94 = 6; + if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94)) + r94 += txpower - MAX_TXPOWER; + else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94)) + r94 += txpower; + rt73usb_bbp_write(rt2x00dev, 94, r94); + + rt73usb_rf_write(rt2x00dev, 1, rf->rf1); + rt73usb_rf_write(rt2x00dev, 2, rf->rf2); + rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt73usb_rf_write(rt2x00dev, 4, rf->rf4); + + rt73usb_rf_write(rt2x00dev, 1, rf->rf1); + rt73usb_rf_write(rt2x00dev, 2, rf->rf2); + rt73usb_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); + rt73usb_rf_write(rt2x00dev, 4, rf->rf4); + + rt73usb_rf_write(rt2x00dev, 1, rf->rf1); + rt73usb_rf_write(rt2x00dev, 2, rf->rf2); + rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt73usb_rf_write(rt2x00dev, 4, rf->rf4); + + udelay(10); +} + +static void rt73usb_config_txpower(struct rt2x00_dev *rt2x00dev, + const int txpower) +{ + struct rf_channel rf; + + rt2x00_rf_read(rt2x00dev, 1, &rf.rf1); + rt2x00_rf_read(rt2x00dev, 2, &rf.rf2); + rt2x00_rf_read(rt2x00dev, 3, &rf.rf3); + rt2x00_rf_read(rt2x00dev, 4, &rf.rf4); + + rt73usb_config_channel(rt2x00dev, &rf, txpower); +} + +static void rt73usb_config_retry_limit(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u32 reg; + + rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_DOWN, 1); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_STEP, 0); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_FALLBACK_CCK, 0); + rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, + libconf->conf->long_frame_max_tx_count); + rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, + libconf->conf->short_frame_max_tx_count); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg); +} + +static void rt73usb_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; + u32 reg; + + if (state == STATE_SLEEP) { + rt2x00usb_register_read(rt2x00dev, MAC_CSR11, ®); + rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, + rt2x00dev->beacon_int - 10); + rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, + libconf->conf->listen_interval - 1); + rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 5); + + /* We must first disable autowake before it can be enabled */ + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); + rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); + + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 1); + rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); + + rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, + USB_MODE_SLEEP, REGISTER_TIMEOUT); + } else { + rt2x00usb_register_read(rt2x00dev, MAC_CSR11, ®); + rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, 0); + rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, 0); + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); + rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 0); + rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); + + rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, + USB_MODE_WAKEUP, REGISTER_TIMEOUT); + } +} + +static void rt73usb_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags) +{ + /* Always recalculate LNA gain before changing configuration */ + rt73usb_config_lna_gain(rt2x00dev, libconf); + + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) + rt73usb_config_channel(rt2x00dev, &libconf->rf, + libconf->conf->power_level); + if ((flags & IEEE80211_CONF_CHANGE_POWER) && + !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) + rt73usb_config_txpower(rt2x00dev, libconf->conf->power_level); + if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + rt73usb_config_retry_limit(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt73usb_config_ps(rt2x00dev, libconf); +} + +/* + * Link tuning + */ +static void rt73usb_link_stats(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) +{ + u32 reg; + + /* + * Update FCS error count from register. + */ + rt2x00usb_register_read(rt2x00dev, STA_CSR0, ®); + qual->rx_failed = rt2x00_get_field32(reg, STA_CSR0_FCS_ERROR); + + /* + * Update False CCA count from register. + */ + rt2x00usb_register_read(rt2x00dev, STA_CSR1, ®); + qual->false_cca = rt2x00_get_field32(reg, STA_CSR1_FALSE_CCA_ERROR); +} + +static inline void rt73usb_set_vgc(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, u8 vgc_level) +{ + if (qual->vgc_level != vgc_level) { + rt73usb_bbp_write(rt2x00dev, 17, vgc_level); + qual->vgc_level = vgc_level; + qual->vgc_level_reg = vgc_level; + } +} + +static void rt73usb_reset_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) +{ + rt73usb_set_vgc(rt2x00dev, qual, 0x20); +} + +static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, const u32 count) +{ + u8 up_bound; + u8 low_bound; + + /* + * Determine r17 bounds. + */ + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { + low_bound = 0x28; + up_bound = 0x48; + + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { + low_bound += 0x10; + up_bound += 0x10; + } + } else { + if (qual->rssi > -82) { + low_bound = 0x1c; + up_bound = 0x40; + } else if (qual->rssi > -84) { + low_bound = 0x1c; + up_bound = 0x20; + } else { + low_bound = 0x1c; + up_bound = 0x1c; + } + + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { + low_bound += 0x14; + up_bound += 0x10; + } + } + + /* + * If we are not associated, we should go straight to the + * dynamic CCA tuning. + */ + if (!rt2x00dev->intf_associated) + goto dynamic_cca_tune; + + /* + * Special big-R17 for very short distance + */ + if (qual->rssi > -35) { + rt73usb_set_vgc(rt2x00dev, qual, 0x60); + return; + } + + /* + * Special big-R17 for short distance + */ + if (qual->rssi >= -58) { + rt73usb_set_vgc(rt2x00dev, qual, up_bound); + return; + } + + /* + * Special big-R17 for middle-short distance + */ + if (qual->rssi >= -66) { + rt73usb_set_vgc(rt2x00dev, qual, low_bound + 0x10); + return; + } + + /* + * Special mid-R17 for middle distance + */ + if (qual->rssi >= -74) { + rt73usb_set_vgc(rt2x00dev, qual, low_bound + 0x08); + return; + } + + /* + * Special case: Change up_bound based on the rssi. + * Lower up_bound when rssi is weaker then -74 dBm. + */ + up_bound -= 2 * (-74 - qual->rssi); + if (low_bound > up_bound) + up_bound = low_bound; + + if (qual->vgc_level > up_bound) { + rt73usb_set_vgc(rt2x00dev, qual, up_bound); + return; + } + +dynamic_cca_tune: + + /* + * r17 does not yet exceed upper limit, continue and base + * the r17 tuning on the false CCA count. + */ + if ((qual->false_cca > 512) && (qual->vgc_level < up_bound)) + rt73usb_set_vgc(rt2x00dev, qual, + min_t(u8, qual->vgc_level + 4, up_bound)); + else if ((qual->false_cca < 100) && (qual->vgc_level > low_bound)) + rt73usb_set_vgc(rt2x00dev, qual, + max_t(u8, qual->vgc_level - 4, low_bound)); +} + +/* + * Queue handlers. + */ +static void rt73usb_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); + break; + case QID_BEACON: + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + break; + default: + break; + } +} + +static void rt73usb_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 1); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); + break; + case QID_BEACON: + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + break; + default: + break; + } +} + +/* + * Firmware functions + */ +static char *rt73usb_get_firmware_name(struct rt2x00_dev *rt2x00dev) +{ + return FIRMWARE_RT2571; +} + +static int rt73usb_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + u16 fw_crc; + u16 crc; + + /* + * Only support 2kb firmware files. + */ + if (len != 2048) + return FW_BAD_LENGTH; + + /* + * The last 2 bytes in the firmware array are the crc checksum itself, + * this means that we should never pass those 2 bytes to the crc + * algorithm. + */ + fw_crc = (data[len - 2] << 8 | data[len - 1]); + + /* + * Use the crc itu-t algorithm. + */ + crc = crc_itu_t(0, data, len - 2); + crc = crc_itu_t_byte(crc, 0); + crc = crc_itu_t_byte(crc, 0); + + return (fw_crc == crc) ? FW_OK : FW_BAD_CRC; +} + +static int rt73usb_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + unsigned int i; + int status; + u32 reg; + + /* + * Wait for stable hardware. + */ + for (i = 0; i < 100; i++) { + rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); + if (reg) + break; + msleep(1); + } + + if (!reg) { + rt2x00_err(rt2x00dev, "Unstable hardware\n"); + return -EBUSY; + } + + /* + * Write firmware to device. + */ + rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, data, len); + + /* + * Send firmware request to device to load firmware, + * we need to specify a long timeout time. + */ + status = rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, + 0, USB_MODE_FIRMWARE, + REGISTER_TIMEOUT_FIRMWARE); + if (status < 0) { + rt2x00_err(rt2x00dev, "Failed to write Firmware to device\n"); + return status; + } + + return 0; +} + +/* + * Initialization functions. + */ +static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_AUTO_TX_SEQ, 1); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); + rt2x00_set_field32(®, TXRX_CSR0_TX_WITHOUT_WAITING, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); + + rt2x00usb_register_read(rt2x00dev, TXRX_CSR1, ®); + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0, 47); /* CCK Signal */ + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1, 30); /* Rssi */ + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2, 42); /* OFDM Rate */ + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3, 30); /* Rssi */ + rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3_VALID, 1); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR1, reg); + + /* + * CCK TXD BBP registers + */ + rt2x00usb_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0, 13); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1, 12); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2, 11); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3, 10); + rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3_VALID, 1); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR2, reg); + + /* + * OFDM TXD BBP registers + */ + rt2x00usb_register_read(rt2x00dev, TXRX_CSR3, ®); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0, 7); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1, 6); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1_VALID, 1); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2, 5); + rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2_VALID, 1); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR3, reg); + + rt2x00usb_register_read(rt2x00dev, TXRX_CSR7, ®); + rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_6MBS, 59); + rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_9MBS, 53); + rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_12MBS, 49); + rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_18MBS, 46); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR7, reg); + + rt2x00usb_register_read(rt2x00dev, TXRX_CSR8, ®); + rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_24MBS, 44); + rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_36MBS, 42); + rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_48MBS, 42); + rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_54MBS, 42); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR8, reg); + + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, 0); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 0); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00_set_field32(®, TXRX_CSR9_TIMESTAMP_COMPENSATE, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + + rt2x00usb_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); + + rt2x00usb_register_read(rt2x00dev, MAC_CSR6, ®); + rt2x00_set_field32(®, MAC_CSR6_MAX_FRAME_UNIT, 0xfff); + rt2x00usb_register_write(rt2x00dev, MAC_CSR6, reg); + + rt2x00usb_register_write(rt2x00dev, MAC_CSR10, 0x00000718); + + if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) + return -EBUSY; + + rt2x00usb_register_write(rt2x00dev, MAC_CSR13, 0x00007f00); + + /* + * Invalidate all Shared Keys (SEC_CSR0), + * and clear the Shared key Cipher algorithms (SEC_CSR1 & SEC_CSR5) + */ + rt2x00usb_register_write(rt2x00dev, SEC_CSR0, 0x00000000); + rt2x00usb_register_write(rt2x00dev, SEC_CSR1, 0x00000000); + rt2x00usb_register_write(rt2x00dev, SEC_CSR5, 0x00000000); + + reg = 0x000023b0; + if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)) + rt2x00_set_field32(®, PHY_CSR1_RF_RPI, 1); + rt2x00usb_register_write(rt2x00dev, PHY_CSR1, reg); + + rt2x00usb_register_write(rt2x00dev, PHY_CSR5, 0x00040a06); + rt2x00usb_register_write(rt2x00dev, PHY_CSR6, 0x00080606); + rt2x00usb_register_write(rt2x00dev, PHY_CSR7, 0x00000408); + + rt2x00usb_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00_set_field32(®, MAC_CSR9_CW_SELECT, 0); + rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg); + + /* + * Clear all beacons + * For the Beacon base registers we only need to clear + * the first byte since that byte contains the VALID and OWNER + * bits which (when set to 0) will invalidate the entire beacon. + */ + rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE0, 0); + rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE1, 0); + rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE2, 0); + rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE3, 0); + + /* + * We must clear the error counters. + * These registers are cleared on read, + * so we may pass a useless variable to store the value. + */ + rt2x00usb_register_read(rt2x00dev, STA_CSR0, ®); + rt2x00usb_register_read(rt2x00dev, STA_CSR1, ®); + rt2x00usb_register_read(rt2x00dev, STA_CSR2, ®); + + /* + * Reset MAC and BBP registers. + */ + rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); + rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); + rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); + rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); + + return 0; +} + +static int rt73usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u8 value; + + for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) { + rt73usb_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + return 0; + udelay(REGISTER_BUSY_DELAY); + } + + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); + return -EACCES; +} + +static int rt73usb_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + if (unlikely(rt73usb_wait_bbp_ready(rt2x00dev))) + return -EACCES; + + rt73usb_bbp_write(rt2x00dev, 3, 0x80); + rt73usb_bbp_write(rt2x00dev, 15, 0x30); + rt73usb_bbp_write(rt2x00dev, 21, 0xc8); + rt73usb_bbp_write(rt2x00dev, 22, 0x38); + rt73usb_bbp_write(rt2x00dev, 23, 0x06); + rt73usb_bbp_write(rt2x00dev, 24, 0xfe); + rt73usb_bbp_write(rt2x00dev, 25, 0x0a); + rt73usb_bbp_write(rt2x00dev, 26, 0x0d); + rt73usb_bbp_write(rt2x00dev, 32, 0x0b); + rt73usb_bbp_write(rt2x00dev, 34, 0x12); + rt73usb_bbp_write(rt2x00dev, 37, 0x07); + rt73usb_bbp_write(rt2x00dev, 39, 0xf8); + rt73usb_bbp_write(rt2x00dev, 41, 0x60); + rt73usb_bbp_write(rt2x00dev, 53, 0x10); + rt73usb_bbp_write(rt2x00dev, 54, 0x18); + rt73usb_bbp_write(rt2x00dev, 60, 0x10); + rt73usb_bbp_write(rt2x00dev, 61, 0x04); + rt73usb_bbp_write(rt2x00dev, 62, 0x04); + rt73usb_bbp_write(rt2x00dev, 75, 0xfe); + rt73usb_bbp_write(rt2x00dev, 86, 0xfe); + rt73usb_bbp_write(rt2x00dev, 88, 0xfe); + rt73usb_bbp_write(rt2x00dev, 90, 0x0f); + rt73usb_bbp_write(rt2x00dev, 99, 0x00); + rt73usb_bbp_write(rt2x00dev, 102, 0x16); + rt73usb_bbp_write(rt2x00dev, 107, 0x04); + + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + rt73usb_bbp_write(rt2x00dev, reg_id, value); + } + } + + return 0; +} + +/* + * Device state switch handlers. + */ +static int rt73usb_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + /* + * Initialize all registers. + */ + if (unlikely(rt73usb_init_registers(rt2x00dev) || + rt73usb_init_bbp(rt2x00dev))) + return -EIO; + + return 0; +} + +static void rt73usb_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + rt2x00usb_register_write(rt2x00dev, MAC_CSR10, 0x00001818); + + /* + * Disable synchronisation. + */ + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, 0); + + rt2x00usb_disable_radio(rt2x00dev); +} + +static int rt73usb_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) +{ + u32 reg, reg2; + unsigned int i; + char put_to_sleep; + + put_to_sleep = (state != STATE_AWAKE); + + rt2x00usb_register_read(rt2x00dev, MAC_CSR12, ®); + rt2x00_set_field32(®, MAC_CSR12_FORCE_WAKEUP, !put_to_sleep); + rt2x00_set_field32(®, MAC_CSR12_PUT_TO_SLEEP, put_to_sleep); + rt2x00usb_register_write(rt2x00dev, MAC_CSR12, reg); + + /* + * Device is not guaranteed to be in the requested state yet. + * We must wait until the register indicates that the + * device has entered the correct state. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00usb_register_read(rt2x00dev, MAC_CSR12, ®2); + state = rt2x00_get_field32(reg2, MAC_CSR12_BBP_CURRENT_STATE); + if (state == !put_to_sleep) + return 0; + rt2x00usb_register_write(rt2x00dev, MAC_CSR12, reg); + msleep(10); + } + + return -EBUSY; +} + +static int rt73usb_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + retval = rt73usb_enable_radio(rt2x00dev); + break; + case STATE_RADIO_OFF: + rt73usb_disable_radio(rt2x00dev); + break; + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + /* No support, but no error either */ + break; + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + retval = rt73usb_set_state(rt2x00dev, state); + break; + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +/* + * TX descriptor initialization + */ +static void rt73usb_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + __le32 *txd = (__le32 *) entry->skb->data; + u32 word; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_BURST, + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_VALID, 1); + rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_ACK, + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_OFDM, + (txdesc->rate_mode == RATE_MODE_OFDM)); + rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); + rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, + test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, + test_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_TABLE, + test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); + rt2x00_set_field32(&word, TXD_W0_BURST2, + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher); + rt2x00_desc_write(txd, 0, word); + + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, entry->queue->qid); + rt2x00_set_field32(&word, TXD_W1_AIFSN, entry->queue->aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); + rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); + rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, + test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, + txdesc->u.plcp.length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, + txdesc->u.plcp.length_high); + rt2x00_desc_write(txd, 2, word); + + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { + _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); + _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); + } + + rt2x00_desc_read(txd, 5, &word); + rt2x00_set_field32(&word, TXD_W5_TX_POWER, + TXPOWER_TO_DEV(entry->queue->rt2x00dev->tx_power)); + rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1); + rt2x00_desc_write(txd, 5, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->flags |= SKBDESC_DESC_IN_SKB; + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; +} + +/* + * TX data initialization + */ +static void rt73usb_write_beacon(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + unsigned int beacon_base; + unsigned int padding_len; + u32 orig_reg, reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + orig_reg = reg; + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Add space for the descriptor in front of the skb. + */ + skb_push(entry->skb, TXD_DESC_SIZE); + memset(entry->skb->data, 0, TXD_DESC_SIZE); + + /* + * Write the TX descriptor for the beacon. + */ + rt73usb_write_tx_desc(entry, txdesc); + + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); + + /* + * Write entire beacon with descriptor and padding to register. + */ + padding_len = roundup(entry->skb->len, 4) - entry->skb->len; + if (padding_len && skb_pad(entry->skb, padding_len)) { + rt2x00_err(rt2x00dev, "Failure padding beacon, aborting\n"); + /* skb freed by skb_pad() on failure */ + entry->skb = NULL; + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, orig_reg); + return; + } + + beacon_base = HW_BEACON_OFFSET(entry->entry_idx); + rt2x00usb_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data, + entry->skb->len + padding_len); + + /* + * Enable beaconing again. + * + * For Wi-Fi faily generated beacons between participating stations. + * Set TBTT phase adaptive adjustment step to 8us (default 16us) + */ + rt2x00usb_register_write(rt2x00dev, TXRX_CSR10, 0x00001008); + + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Clean up the beacon skb. + */ + dev_kfree_skb(entry->skb); + entry->skb = NULL; +} + +static void rt73usb_clear_beacon(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + unsigned int beacon_base; + u32 orig_reg, reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &orig_reg); + reg = orig_reg; + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Clear beacon. + */ + beacon_base = HW_BEACON_OFFSET(entry->entry_idx); + rt2x00usb_register_write(rt2x00dev, beacon_base, 0); + + /* + * Restore beaconing state. + */ + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, orig_reg); +} + +static int rt73usb_get_tx_data_len(struct queue_entry *entry) +{ + int length; + + /* + * The length _must_ be a multiple of 4, + * but it must _not_ be a multiple of the USB packet size. + */ + length = roundup(entry->skb->len, 4); + length += (4 * !(length % entry->queue->usb_maxpacket)); + + return length; +} + +/* + * RX control handlers + */ +static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) +{ + u8 offset = rt2x00dev->lna_gain; + u8 lna; + + lna = rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_LNA); + switch (lna) { + case 3: + offset += 90; + break; + case 2: + offset += 74; + break; + case 1: + offset += 64; + break; + default: + return 0; + } + + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { + if (lna == 3 || lna == 2) + offset += 10; + } else { + if (lna == 3) + offset += 6; + else if (lna == 2) + offset += 8; + } + } + + return rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_AGC) * 2 - offset; +} + +static void rt73usb_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + __le32 *rxd = (__le32 *)entry->skb->data; + u32 word0; + u32 word1; + + /* + * Copy descriptor to the skbdesc->desc buffer, making it safe from moving of + * frame data in rt2x00usb. + */ + memcpy(skbdesc->desc, rxd, skbdesc->desc_len); + rxd = (__le32 *)skbdesc->desc; + + /* + * It is now safe to read the descriptor on all architectures. + */ + rt2x00_desc_read(rxd, 0, &word0); + rt2x00_desc_read(rxd, 1, &word1); + + if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + + rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG); + rxdesc->cipher_status = rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR); + + if (rxdesc->cipher != CIPHER_NONE) { + _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]); + _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]); + rxdesc->dev_flags |= RXDONE_CRYPTO_IV; + + _rt2x00_desc_read(rxd, 4, &rxdesc->icv); + rxdesc->dev_flags |= RXDONE_CRYPTO_ICV; + + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. It has provided the data separately but rt2x00lib + * should decide if it should be reinserted. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * The hardware has already checked the Michael Mic and has + * stripped it from the frame. Signal this to mac80211. + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + + /* + * Obtain the status about this packet. + * When frame was received with an OFDM bitrate, + * the signal is the PLCP value. If it was received with + * a CCK bitrate the signal is the rate in 100kbit/s. + */ + rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); + rxdesc->rssi = rt73usb_agc_to_rssi(rt2x00dev, word1); + rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + if (rt2x00_get_field32(word0, RXD_W0_OFDM)) + rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; + else + rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; + if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; + + /* + * Set skb pointers, and update frame information. + */ + skb_pull(entry->skb, entry->queue->desc_size); + skb_trim(entry->skb, rxdesc->size); +} + +/* + * Device probe functions. + */ +static int rt73usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u16 word; + u8 *mac; + s8 value; + + rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE); + + /* + * Start validation of the data that has been read. + */ + mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + if (!is_valid_ether_addr(mac)) { + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); + rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, + ANTENNA_B); + rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, + ANTENNA_B); + rt2x00_set_field16(&word, EEPROM_ANTENNA_FRAME_TYPE, 0); + rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); + rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); + rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF5226); + rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); + rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); + rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_LED_POLARITY_RDY_G, 0); + rt2x00_set_field16(&word, EEPROM_LED_POLARITY_RDY_A, 0); + rt2x00_set_field16(&word, EEPROM_LED_POLARITY_ACT, 0); + rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_0, 0); + rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_1, 0); + rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_2, 0); + rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_3, 0); + rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_4, 0); + rt2x00_set_field16(&word, EEPROM_LED_LED_MODE, + LED_MODE_DEFAULT); + rt2x00_eeprom_write(rt2x00dev, EEPROM_LED, word); + rt2x00_eeprom_dbg(rt2x00dev, "Led: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); + rt2x00_set_field16(&word, EEPROM_FREQ_SEQ, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); + rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); + rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); + } else { + value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_1); + if (value < -10 || value > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); + value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_2); + if (value < -10 || value > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); + } + + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); + rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET A: 0x%04x\n", word); + } else { + value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_1); + if (value < -10 || value > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); + value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_2); + if (value < -10 || value > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); + rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); + } + + return 0; +} + +static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 value; + u16 eeprom; + + /* + * Read EEPROM word for configuration. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Identify RF chipset. + */ + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); + rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); + rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), + value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); + + if (!rt2x00_rt(rt2x00dev, RT2573) || (rt2x00_rev(rt2x00dev) == 0)) { + rt2x00_err(rt2x00dev, "Invalid RT chipset detected\n"); + return -ENODEV; + } + + if (!rt2x00_rf(rt2x00dev, RF5226) && + !rt2x00_rf(rt2x00dev, RF2528) && + !rt2x00_rf(rt2x00dev, RF5225) && + !rt2x00_rf(rt2x00dev, RF2527)) { + rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); + return -ENODEV; + } + + /* + * Identify default antenna configuration. + */ + rt2x00dev->default_ant.tx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); + rt2x00dev->default_ant.rx = + rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); + + /* + * Read the Frame type. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_FRAME_TYPE)) + __set_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags); + + /* + * Detect if this device has an hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); + + /* + * Read frequency offset. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); + rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); + + /* + * Read external LNA informations. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); + + if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA)) { + __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); + __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); + } + + /* + * Store led settings, for correct led behaviour. + */ +#ifdef CONFIG_RT2X00_LIB_LEDS + rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); + + rt73usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + rt73usb_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); + if (value == LED_MODE_SIGNAL_STRENGTH) + rt73usb_init_led(rt2x00dev, &rt2x00dev->led_qual, + LED_TYPE_QUALITY); + + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_LED_MODE, value); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_0, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_GPIO_0)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_1, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_GPIO_1)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_2, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_GPIO_2)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_3, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_GPIO_3)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_4, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_GPIO_4)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_ACT, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_ACT)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_BG, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_RDY_G)); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A, + rt2x00_get_field16(eeprom, + EEPROM_LED_POLARITY_RDY_A)); +#endif /* CONFIG_RT2X00_LIB_LEDS */ + + return 0; +} + +/* + * RF value list for RF2528 + * Supports: 2.4 GHz + */ +static const struct rf_channel rf_vals_bg_2528[] = { + { 1, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea0b }, + { 2, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea1f }, + { 3, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea0b }, + { 4, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea1f }, + { 5, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea0b }, + { 6, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea1f }, + { 7, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea0b }, + { 8, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea1f }, + { 9, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea0b }, + { 10, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea1f }, + { 11, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea0b }, + { 12, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea1f }, + { 13, 0x00002c0c, 0x0000079e, 0x00068255, 0x000fea0b }, + { 14, 0x00002c0c, 0x000007a2, 0x00068255, 0x000fea13 }, +}; + +/* + * RF value list for RF5226 + * Supports: 2.4 GHz & 5.2 GHz + */ +static const struct rf_channel rf_vals_5226[] = { + { 1, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea0b }, + { 2, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea1f }, + { 3, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea0b }, + { 4, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea1f }, + { 5, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea0b }, + { 6, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea1f }, + { 7, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea0b }, + { 8, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea1f }, + { 9, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea0b }, + { 10, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea1f }, + { 11, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea0b }, + { 12, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea1f }, + { 13, 0x00002c0c, 0x0000079e, 0x00068255, 0x000fea0b }, + { 14, 0x00002c0c, 0x000007a2, 0x00068255, 0x000fea13 }, + + /* 802.11 UNI / HyperLan 2 */ + { 36, 0x00002c0c, 0x0000099a, 0x00098255, 0x000fea23 }, + { 40, 0x00002c0c, 0x000009a2, 0x00098255, 0x000fea03 }, + { 44, 0x00002c0c, 0x000009a6, 0x00098255, 0x000fea0b }, + { 48, 0x00002c0c, 0x000009aa, 0x00098255, 0x000fea13 }, + { 52, 0x00002c0c, 0x000009ae, 0x00098255, 0x000fea1b }, + { 56, 0x00002c0c, 0x000009b2, 0x00098255, 0x000fea23 }, + { 60, 0x00002c0c, 0x000009ba, 0x00098255, 0x000fea03 }, + { 64, 0x00002c0c, 0x000009be, 0x00098255, 0x000fea0b }, + + /* 802.11 HyperLan 2 */ + { 100, 0x00002c0c, 0x00000a2a, 0x000b8255, 0x000fea03 }, + { 104, 0x00002c0c, 0x00000a2e, 0x000b8255, 0x000fea0b }, + { 108, 0x00002c0c, 0x00000a32, 0x000b8255, 0x000fea13 }, + { 112, 0x00002c0c, 0x00000a36, 0x000b8255, 0x000fea1b }, + { 116, 0x00002c0c, 0x00000a3a, 0x000b8255, 0x000fea23 }, + { 120, 0x00002c0c, 0x00000a82, 0x000b8255, 0x000fea03 }, + { 124, 0x00002c0c, 0x00000a86, 0x000b8255, 0x000fea0b }, + { 128, 0x00002c0c, 0x00000a8a, 0x000b8255, 0x000fea13 }, + { 132, 0x00002c0c, 0x00000a8e, 0x000b8255, 0x000fea1b }, + { 136, 0x00002c0c, 0x00000a92, 0x000b8255, 0x000fea23 }, + + /* 802.11 UNII */ + { 140, 0x00002c0c, 0x00000a9a, 0x000b8255, 0x000fea03 }, + { 149, 0x00002c0c, 0x00000aa2, 0x000b8255, 0x000fea1f }, + { 153, 0x00002c0c, 0x00000aa6, 0x000b8255, 0x000fea27 }, + { 157, 0x00002c0c, 0x00000aae, 0x000b8255, 0x000fea07 }, + { 161, 0x00002c0c, 0x00000ab2, 0x000b8255, 0x000fea0f }, + { 165, 0x00002c0c, 0x00000ab6, 0x000b8255, 0x000fea17 }, + + /* MMAC(Japan)J52 ch 34,38,42,46 */ + { 34, 0x00002c0c, 0x0008099a, 0x000da255, 0x000d3a0b }, + { 38, 0x00002c0c, 0x0008099e, 0x000da255, 0x000d3a13 }, + { 42, 0x00002c0c, 0x000809a2, 0x000da255, 0x000d3a1b }, + { 46, 0x00002c0c, 0x000809a6, 0x000da255, 0x000d3a23 }, +}; + +/* + * RF value list for RF5225 & RF2527 + * Supports: 2.4 GHz & 5.2 GHz + */ +static const struct rf_channel rf_vals_5225_2527[] = { + { 1, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa0b }, + { 2, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa1f }, + { 3, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa0b }, + { 4, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa1f }, + { 5, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa0b }, + { 6, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa1f }, + { 7, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa0b }, + { 8, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa1f }, + { 9, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa0b }, + { 10, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa1f }, + { 11, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa0b }, + { 12, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa1f }, + { 13, 0x00002ccc, 0x0000479e, 0x00068455, 0x000ffa0b }, + { 14, 0x00002ccc, 0x000047a2, 0x00068455, 0x000ffa13 }, + + /* 802.11 UNI / HyperLan 2 */ + { 36, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa23 }, + { 40, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa03 }, + { 44, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa0b }, + { 48, 0x00002ccc, 0x000049aa, 0x0009be55, 0x000ffa13 }, + { 52, 0x00002ccc, 0x000049ae, 0x0009ae55, 0x000ffa1b }, + { 56, 0x00002ccc, 0x000049b2, 0x0009ae55, 0x000ffa23 }, + { 60, 0x00002ccc, 0x000049ba, 0x0009ae55, 0x000ffa03 }, + { 64, 0x00002ccc, 0x000049be, 0x0009ae55, 0x000ffa0b }, + + /* 802.11 HyperLan 2 */ + { 100, 0x00002ccc, 0x00004a2a, 0x000bae55, 0x000ffa03 }, + { 104, 0x00002ccc, 0x00004a2e, 0x000bae55, 0x000ffa0b }, + { 108, 0x00002ccc, 0x00004a32, 0x000bae55, 0x000ffa13 }, + { 112, 0x00002ccc, 0x00004a36, 0x000bae55, 0x000ffa1b }, + { 116, 0x00002ccc, 0x00004a3a, 0x000bbe55, 0x000ffa23 }, + { 120, 0x00002ccc, 0x00004a82, 0x000bbe55, 0x000ffa03 }, + { 124, 0x00002ccc, 0x00004a86, 0x000bbe55, 0x000ffa0b }, + { 128, 0x00002ccc, 0x00004a8a, 0x000bbe55, 0x000ffa13 }, + { 132, 0x00002ccc, 0x00004a8e, 0x000bbe55, 0x000ffa1b }, + { 136, 0x00002ccc, 0x00004a92, 0x000bbe55, 0x000ffa23 }, + + /* 802.11 UNII */ + { 140, 0x00002ccc, 0x00004a9a, 0x000bbe55, 0x000ffa03 }, + { 149, 0x00002ccc, 0x00004aa2, 0x000bbe55, 0x000ffa1f }, + { 153, 0x00002ccc, 0x00004aa6, 0x000bbe55, 0x000ffa27 }, + { 157, 0x00002ccc, 0x00004aae, 0x000bbe55, 0x000ffa07 }, + { 161, 0x00002ccc, 0x00004ab2, 0x000bbe55, 0x000ffa0f }, + { 165, 0x00002ccc, 0x00004ab6, 0x000bbe55, 0x000ffa17 }, + + /* MMAC(Japan)J52 ch 34,38,42,46 */ + { 34, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa0b }, + { 38, 0x00002ccc, 0x0000499e, 0x0009be55, 0x000ffa13 }, + { 42, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa1b }, + { 46, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa23 }, +}; + + +static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + struct channel_info *info; + char *tx_power; + unsigned int i; + + /* + * Initialize all hw fields. + * + * Don't set IEEE80211_HOST_BROADCAST_PS_BUFFERING unless we are + * capable of sending the buffered frames out after the DTIM + * transmission using rt2x00lib_beacondone. This will send out + * multicast and broadcast traffic immediately instead of buffering it + * infinitly and thus dropping it after some time. + */ + ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); + ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); + ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); + + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, + rt2x00_eeprom_addr(rt2x00dev, + EEPROM_MAC_ADDR_0)); + + /* + * Initialize hw_mode information. + */ + spec->supported_bands = SUPPORT_BAND_2GHZ; + spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; + + if (rt2x00_rf(rt2x00dev, RF2528)) { + spec->num_channels = ARRAY_SIZE(rf_vals_bg_2528); + spec->channels = rf_vals_bg_2528; + } else if (rt2x00_rf(rt2x00dev, RF5226)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; + spec->num_channels = ARRAY_SIZE(rf_vals_5226); + spec->channels = rf_vals_5226; + } else if (rt2x00_rf(rt2x00dev, RF2527)) { + spec->num_channels = 14; + spec->channels = rf_vals_5225_2527; + } else if (rt2x00_rf(rt2x00dev, RF5225)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; + spec->num_channels = ARRAY_SIZE(rf_vals_5225_2527); + spec->channels = rf_vals_5225_2527; + } + + /* + * Create channel information array + */ + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); + for (i = 0; i < 14; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); + } + + if (spec->num_channels > 14) { + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); + for (i = 14; i < spec->num_channels; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = + TXPOWER_FROM_DEV(tx_power[i - 14]); + } + } + + return 0; +} + +static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + u32 reg; + + /* + * Allocate eeprom data. + */ + retval = rt73usb_validate_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt73usb_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. + */ + rt2x00usb_register_read(rt2x00dev, MAC_CSR13, ®); + rt2x00_set_field32(®, MAC_CSR13_DIR7, 0); + rt2x00usb_register_write(rt2x00dev, MAC_CSR13, reg); + + /* + * Initialize hw specifications. + */ + retval = rt73usb_probe_hw_mode(rt2x00dev); + if (retval) + return retval; + + /* + * This device has multiple filters for control frames, + * but has no a separate filter for PS Poll frames. + */ + __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); + + /* + * This device requires firmware. + */ + __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); + if (!modparam_nohwcrypt) + __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); + __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); + + /* + * Set the rssi offset. + */ + rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; + + return 0; +} + +/* + * IEEE80211 stack callback functions. + */ +static int rt73usb_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, + const struct ieee80211_tx_queue_params *params) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + struct rt2x00_field32 field; + int retval; + u32 reg; + u32 offset; + + /* + * First pass the configuration through rt2x00lib, that will + * update the queue settings and validate the input. After that + * we are free to update the registers based on the value + * in the queue parameter. + */ + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); + if (retval) + return retval; + + /* + * We only need to perform additional register initialization + * for WMM queues/ + */ + if (queue_idx >= 4) + return 0; + + queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); + + /* Update WMM TXOP register */ + offset = AC_TXOP_CSR0 + (sizeof(u32) * (!!(queue_idx & 2))); + field.bit_offset = (queue_idx & 1) * 16; + field.bit_mask = 0xffff << field.bit_offset; + + rt2x00usb_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, field, queue->txop); + rt2x00usb_register_write(rt2x00dev, offset, reg); + + /* Update WMM registers */ + field.bit_offset = queue_idx * 4; + field.bit_mask = 0xf << field.bit_offset; + + rt2x00usb_register_read(rt2x00dev, AIFSN_CSR, ®); + rt2x00_set_field32(®, field, queue->aifs); + rt2x00usb_register_write(rt2x00dev, AIFSN_CSR, reg); + + rt2x00usb_register_read(rt2x00dev, CWMIN_CSR, ®); + rt2x00_set_field32(®, field, queue->cw_min); + rt2x00usb_register_write(rt2x00dev, CWMIN_CSR, reg); + + rt2x00usb_register_read(rt2x00dev, CWMAX_CSR, ®); + rt2x00_set_field32(®, field, queue->cw_max); + rt2x00usb_register_write(rt2x00dev, CWMAX_CSR, reg); + + return 0; +} + +static u64 rt73usb_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u64 tsf; + u32 reg; + + rt2x00usb_register_read(rt2x00dev, TXRX_CSR13, ®); + tsf = (u64) rt2x00_get_field32(reg, TXRX_CSR13_HIGH_TSFTIMER) << 32; + rt2x00usb_register_read(rt2x00dev, TXRX_CSR12, ®); + tsf |= rt2x00_get_field32(reg, TXRX_CSR12_LOW_TSFTIMER); + + return tsf; +} + +static const struct ieee80211_ops rt73usb_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt73usb_conf_tx, + .get_tsf = rt73usb_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, + .flush = rt2x00mac_flush, + .set_antenna = rt2x00mac_set_antenna, + .get_antenna = rt2x00mac_get_antenna, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { + .probe_hw = rt73usb_probe_hw, + .get_firmware_name = rt73usb_get_firmware_name, + .check_firmware = rt73usb_check_firmware, + .load_firmware = rt73usb_load_firmware, + .initialize = rt2x00usb_initialize, + .uninitialize = rt2x00usb_uninitialize, + .clear_entry = rt2x00usb_clear_entry, + .set_device_state = rt73usb_set_device_state, + .rfkill_poll = rt73usb_rfkill_poll, + .link_stats = rt73usb_link_stats, + .reset_tuner = rt73usb_reset_tuner, + .link_tuner = rt73usb_link_tuner, + .watchdog = rt2x00usb_watchdog, + .start_queue = rt73usb_start_queue, + .kick_queue = rt2x00usb_kick_queue, + .stop_queue = rt73usb_stop_queue, + .flush_queue = rt2x00usb_flush_queue, + .write_tx_desc = rt73usb_write_tx_desc, + .write_beacon = rt73usb_write_beacon, + .clear_beacon = rt73usb_clear_beacon, + .get_tx_data_len = rt73usb_get_tx_data_len, + .fill_rxdone = rt73usb_fill_rxdone, + .config_shared_key = rt73usb_config_shared_key, + .config_pairwise_key = rt73usb_config_pairwise_key, + .config_filter = rt73usb_config_filter, + .config_intf = rt73usb_config_intf, + .config_erp = rt73usb_config_erp, + .config_ant = rt73usb_config_ant, + .config = rt73usb_config, +}; + +static void rt73usb_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_BEACON: + queue->limit = 4; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXINFO_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} + +static const struct rt2x00_ops rt73usb_ops = { + .name = KBUILD_MODNAME, + .max_ap_intf = 4, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt73usb_queue_init, + .lib = &rt73usb_rt2x00_ops, + .hw = &rt73usb_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt73usb_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +/* + * rt73usb module information. + */ +static struct usb_device_id rt73usb_device_table[] = { + /* AboCom */ + { USB_DEVICE(0x07b8, 0xb21b) }, + { USB_DEVICE(0x07b8, 0xb21c) }, + { USB_DEVICE(0x07b8, 0xb21d) }, + { USB_DEVICE(0x07b8, 0xb21e) }, + { USB_DEVICE(0x07b8, 0xb21f) }, + /* AL */ + { USB_DEVICE(0x14b2, 0x3c10) }, + /* Amigo */ + { USB_DEVICE(0x148f, 0x9021) }, + { USB_DEVICE(0x0eb0, 0x9021) }, + /* AMIT */ + { USB_DEVICE(0x18c5, 0x0002) }, + /* Askey */ + { USB_DEVICE(0x1690, 0x0722) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x1723) }, + { USB_DEVICE(0x0b05, 0x1724) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x7050) }, /* FCC ID: K7SF5D7050B ver. 3.x */ + { USB_DEVICE(0x050d, 0x705a) }, + { USB_DEVICE(0x050d, 0x905b) }, + { USB_DEVICE(0x050d, 0x905c) }, + /* Billionton */ + { USB_DEVICE(0x1631, 0xc019) }, + { USB_DEVICE(0x08dd, 0x0120) }, + /* Buffalo */ + { USB_DEVICE(0x0411, 0x00d8) }, + { USB_DEVICE(0x0411, 0x00d9) }, + { USB_DEVICE(0x0411, 0x00e6) }, + { USB_DEVICE(0x0411, 0x00f4) }, + { USB_DEVICE(0x0411, 0x0116) }, + { USB_DEVICE(0x0411, 0x0119) }, + { USB_DEVICE(0x0411, 0x0137) }, + /* CEIVA */ + { USB_DEVICE(0x178d, 0x02be) }, + /* CNet */ + { USB_DEVICE(0x1371, 0x9022) }, + { USB_DEVICE(0x1371, 0x9032) }, + /* Conceptronic */ + { USB_DEVICE(0x14b2, 0x3c22) }, + /* Corega */ + { USB_DEVICE(0x07aa, 0x002e) }, + /* D-Link */ + { USB_DEVICE(0x07d1, 0x3c03) }, + { USB_DEVICE(0x07d1, 0x3c04) }, + { USB_DEVICE(0x07d1, 0x3c06) }, + { USB_DEVICE(0x07d1, 0x3c07) }, + /* Edimax */ + { USB_DEVICE(0x7392, 0x7318) }, + { USB_DEVICE(0x7392, 0x7618) }, + /* EnGenius */ + { USB_DEVICE(0x1740, 0x3701) }, + /* Gemtek */ + { USB_DEVICE(0x15a9, 0x0004) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x8008) }, + { USB_DEVICE(0x1044, 0x800a) }, + /* Huawei-3Com */ + { USB_DEVICE(0x1472, 0x0009) }, + /* Hercules */ + { USB_DEVICE(0x06f8, 0xe002) }, + { USB_DEVICE(0x06f8, 0xe010) }, + { USB_DEVICE(0x06f8, 0xe020) }, + /* Linksys */ + { USB_DEVICE(0x13b1, 0x0020) }, + { USB_DEVICE(0x13b1, 0x0023) }, + { USB_DEVICE(0x13b1, 0x0028) }, + /* MSI */ + { USB_DEVICE(0x0db0, 0x4600) }, + { USB_DEVICE(0x0db0, 0x6877) }, + { USB_DEVICE(0x0db0, 0x6874) }, + { USB_DEVICE(0x0db0, 0xa861) }, + { USB_DEVICE(0x0db0, 0xa874) }, + /* Ovislink */ + { USB_DEVICE(0x1b75, 0x7318) }, + /* Ralink */ + { USB_DEVICE(0x04bb, 0x093d) }, + { USB_DEVICE(0x148f, 0x2573) }, + { USB_DEVICE(0x148f, 0x2671) }, + { USB_DEVICE(0x0812, 0x3101) }, + /* Qcom */ + { USB_DEVICE(0x18e8, 0x6196) }, + { USB_DEVICE(0x18e8, 0x6229) }, + { USB_DEVICE(0x18e8, 0x6238) }, + /* Samsung */ + { USB_DEVICE(0x04e8, 0x4471) }, + /* Senao */ + { USB_DEVICE(0x1740, 0x7100) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0024) }, + { USB_DEVICE(0x0df6, 0x0027) }, + { USB_DEVICE(0x0df6, 0x002f) }, + { USB_DEVICE(0x0df6, 0x90ac) }, + { USB_DEVICE(0x0df6, 0x9712) }, + /* Surecom */ + { USB_DEVICE(0x0769, 0x31f3) }, + /* Tilgin */ + { USB_DEVICE(0x6933, 0x5001) }, + /* Philips */ + { USB_DEVICE(0x0471, 0x200a) }, + /* Planex */ + { USB_DEVICE(0x2019, 0xab01) }, + { USB_DEVICE(0x2019, 0xab50) }, + /* WideTell */ + { USB_DEVICE(0x7167, 0x3840) }, + /* Zcom */ + { USB_DEVICE(0x0cde, 0x001c) }, + /* ZyXEL */ + { USB_DEVICE(0x0586, 0x3415) }, + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT73 USB Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2571W & RT2671 USB chipset based cards"); +MODULE_DEVICE_TABLE(usb, rt73usb_device_table); +MODULE_FIRMWARE(FIRMWARE_RT2571); +MODULE_LICENSE("GPL"); + +static int rt73usb_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + return rt2x00usb_probe(usb_intf, &rt73usb_ops); +} + +static struct usb_driver rt73usb_driver = { + .name = KBUILD_MODNAME, + .id_table = rt73usb_device_table, + .probe = rt73usb_probe, + .disconnect = rt2x00usb_disconnect, + .suspend = rt2x00usb_suspend, + .resume = rt2x00usb_resume, + .reset_resume = rt2x00usb_resume, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(rt73usb_driver); diff --git a/drivers/net/wireless/ralink/rt2x00/rt73usb.h b/drivers/net/wireless/ralink/rt2x00/rt73usb.h new file mode 100644 index 000000000000..4a4f235466d1 --- /dev/null +++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.h @@ -0,0 +1,1079 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + */ + +/* + Module: rt73usb + Abstract: Data structures and registers for the rt73usb module. + Supported chipsets: rt2571W & rt2671. + */ + +#ifndef RT73USB_H +#define RT73USB_H + +/* + * RF chip defines. + */ +#define RF5226 0x0001 +#define RF2528 0x0002 +#define RF5225 0x0003 +#define RF2527 0x0004 + +/* + * Signal information. + * Default offset is required for RSSI <-> dBm conversion. + */ +#define DEFAULT_RSSI_OFFSET 120 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x3000 +#define CSR_REG_SIZE 0x04b0 +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x0100 +#define BBP_BASE 0x0000 +#define BBP_SIZE 0x0080 +#define RF_BASE 0x0004 +#define RF_SIZE 0x0010 + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 4 + +/* + * USB registers. + */ + +/* + * MCU_LEDCS: LED control for MCU Mailbox. + */ +#define MCU_LEDCS_LED_MODE FIELD16(0x001f) +#define MCU_LEDCS_RADIO_STATUS FIELD16(0x0020) +#define MCU_LEDCS_LINK_BG_STATUS FIELD16(0x0040) +#define MCU_LEDCS_LINK_A_STATUS FIELD16(0x0080) +#define MCU_LEDCS_POLARITY_GPIO_0 FIELD16(0x0100) +#define MCU_LEDCS_POLARITY_GPIO_1 FIELD16(0x0200) +#define MCU_LEDCS_POLARITY_GPIO_2 FIELD16(0x0400) +#define MCU_LEDCS_POLARITY_GPIO_3 FIELD16(0x0800) +#define MCU_LEDCS_POLARITY_GPIO_4 FIELD16(0x1000) +#define MCU_LEDCS_POLARITY_ACT FIELD16(0x2000) +#define MCU_LEDCS_POLARITY_READY_BG FIELD16(0x4000) +#define MCU_LEDCS_POLARITY_READY_A FIELD16(0x8000) + +/* + * 8051 firmware image. + */ +#define FIRMWARE_RT2571 "rt73.bin" +#define FIRMWARE_IMAGE_BASE 0x0800 + +/* + * Security key table memory. + * 16 entries 32-byte for shared key table + * 64 entries 32-byte for pairwise key table + * 64 entries 8-byte for pairwise ta key table + */ +#define SHARED_KEY_TABLE_BASE 0x1000 +#define PAIRWISE_KEY_TABLE_BASE 0x1200 +#define PAIRWISE_TA_TABLE_BASE 0x1a00 + +#define SHARED_KEY_ENTRY(__idx) \ + ( SHARED_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_KEY_ENTRY(__idx) \ + ( PAIRWISE_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_TA_ENTRY(__idx) \ + ( PAIRWISE_TA_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_pairwise_ta_entry)) ) + +struct hw_key_entry { + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; +} __packed; + +struct hw_pairwise_ta_entry { + u8 address[6]; + u8 cipher; + u8 reserved; +} __packed; + +/* + * Since NULL frame won't be that long (256 byte), + * We steal 16 tail bytes to save debugging settings. + */ +#define HW_DEBUG_SETTING_BASE 0x2bf0 + +/* + * On-chip BEACON frame space. + */ +#define HW_BEACON_BASE0 0x2400 +#define HW_BEACON_BASE1 0x2500 +#define HW_BEACON_BASE2 0x2600 +#define HW_BEACON_BASE3 0x2700 + +#define HW_BEACON_OFFSET(__index) \ + ( HW_BEACON_BASE0 + (__index * 0x0100) ) + +/* + * MAC Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * MAC_CSR0: ASIC revision number. + */ +#define MAC_CSR0 0x3000 +#define MAC_CSR0_REVISION FIELD32(0x0000000f) +#define MAC_CSR0_CHIPSET FIELD32(0x000ffff0) + +/* + * MAC_CSR1: System control register. + * SOFT_RESET: Software reset bit, 1: reset, 0: normal. + * BBP_RESET: Hardware reset BBP. + * HOST_READY: Host is ready after initialization, 1: ready. + */ +#define MAC_CSR1 0x3004 +#define MAC_CSR1_SOFT_RESET FIELD32(0x00000001) +#define MAC_CSR1_BBP_RESET FIELD32(0x00000002) +#define MAC_CSR1_HOST_READY FIELD32(0x00000004) + +/* + * MAC_CSR2: STA MAC register 0. + */ +#define MAC_CSR2 0x3008 +#define MAC_CSR2_BYTE0 FIELD32(0x000000ff) +#define MAC_CSR2_BYTE1 FIELD32(0x0000ff00) +#define MAC_CSR2_BYTE2 FIELD32(0x00ff0000) +#define MAC_CSR2_BYTE3 FIELD32(0xff000000) + +/* + * MAC_CSR3: STA MAC register 1. + * UNICAST_TO_ME_MASK: + * Used to mask off bits from byte 5 of the MAC address + * to determine the UNICAST_TO_ME bit for RX frames. + * The full mask is complemented by BSS_ID_MASK: + * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK + */ +#define MAC_CSR3 0x300c +#define MAC_CSR3_BYTE4 FIELD32(0x000000ff) +#define MAC_CSR3_BYTE5 FIELD32(0x0000ff00) +#define MAC_CSR3_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) + +/* + * MAC_CSR4: BSSID register 0. + */ +#define MAC_CSR4 0x3010 +#define MAC_CSR4_BYTE0 FIELD32(0x000000ff) +#define MAC_CSR4_BYTE1 FIELD32(0x0000ff00) +#define MAC_CSR4_BYTE2 FIELD32(0x00ff0000) +#define MAC_CSR4_BYTE3 FIELD32(0xff000000) + +/* + * MAC_CSR5: BSSID register 1. + * BSS_ID_MASK: + * This mask is used to mask off bits 0 and 1 of byte 5 of the + * BSSID. This will make sure that those bits will be ignored + * when determining the MY_BSS of RX frames. + * 0: 1-BSSID mode (BSS index = 0) + * 1: 2-BSSID mode (BSS index: Byte5, bit 0) + * 2: 2-BSSID mode (BSS index: byte5, bit 1) + * 3: 4-BSSID mode (BSS index: byte5, bit 0 - 1) + */ +#define MAC_CSR5 0x3014 +#define MAC_CSR5_BYTE4 FIELD32(0x000000ff) +#define MAC_CSR5_BYTE5 FIELD32(0x0000ff00) +#define MAC_CSR5_BSS_ID_MASK FIELD32(0x00ff0000) + +/* + * MAC_CSR6: Maximum frame length register. + */ +#define MAC_CSR6 0x3018 +#define MAC_CSR6_MAX_FRAME_UNIT FIELD32(0x00000fff) + +/* + * MAC_CSR7: Reserved + */ +#define MAC_CSR7 0x301c + +/* + * MAC_CSR8: SIFS/EIFS register. + * All units are in US. + */ +#define MAC_CSR8 0x3020 +#define MAC_CSR8_SIFS FIELD32(0x000000ff) +#define MAC_CSR8_SIFS_AFTER_RX_OFDM FIELD32(0x0000ff00) +#define MAC_CSR8_EIFS FIELD32(0xffff0000) + +/* + * MAC_CSR9: Back-Off control register. + * SLOT_TIME: Slot time, default is 20us for 802.11BG. + * CWMIN: Bit for Cwmin. default Cwmin is 31 (2^5 - 1). + * CWMAX: Bit for Cwmax, default Cwmax is 1023 (2^10 - 1). + * CW_SELECT: 1: CWmin/Cwmax select from register, 0:select from TxD. + */ +#define MAC_CSR9 0x3024 +#define MAC_CSR9_SLOT_TIME FIELD32(0x000000ff) +#define MAC_CSR9_CWMIN FIELD32(0x00000f00) +#define MAC_CSR9_CWMAX FIELD32(0x0000f000) +#define MAC_CSR9_CW_SELECT FIELD32(0x00010000) + +/* + * MAC_CSR10: Power state configuration. + */ +#define MAC_CSR10 0x3028 + +/* + * MAC_CSR11: Power saving transition time register. + * DELAY_AFTER_TBCN: Delay after Tbcn expired in units of TU. + * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. + * WAKEUP_LATENCY: In unit of TU. + */ +#define MAC_CSR11 0x302c +#define MAC_CSR11_DELAY_AFTER_TBCN FIELD32(0x000000ff) +#define MAC_CSR11_TBCN_BEFORE_WAKEUP FIELD32(0x00007f00) +#define MAC_CSR11_AUTOWAKE FIELD32(0x00008000) +#define MAC_CSR11_WAKEUP_LATENCY FIELD32(0x000f0000) + +/* + * MAC_CSR12: Manual power control / status register (merge CSR20 & PWRCSR1). + * CURRENT_STATE: 0:sleep, 1:awake. + * FORCE_WAKEUP: This has higher priority than PUT_TO_SLEEP. + * BBP_CURRENT_STATE: 0: BBP sleep, 1: BBP awake. + */ +#define MAC_CSR12 0x3030 +#define MAC_CSR12_CURRENT_STATE FIELD32(0x00000001) +#define MAC_CSR12_PUT_TO_SLEEP FIELD32(0x00000002) +#define MAC_CSR12_FORCE_WAKEUP FIELD32(0x00000004) +#define MAC_CSR12_BBP_CURRENT_STATE FIELD32(0x00000008) + +/* + * MAC_CSR13: GPIO. + * MAC_CSR13_VALx: GPIO value + * MAC_CSR13_DIRx: GPIO direction: 0 = input; 1 = output + */ +#define MAC_CSR13 0x3034 +#define MAC_CSR13_VAL0 FIELD32(0x00000001) +#define MAC_CSR13_VAL1 FIELD32(0x00000002) +#define MAC_CSR13_VAL2 FIELD32(0x00000004) +#define MAC_CSR13_VAL3 FIELD32(0x00000008) +#define MAC_CSR13_VAL4 FIELD32(0x00000010) +#define MAC_CSR13_VAL5 FIELD32(0x00000020) +#define MAC_CSR13_VAL6 FIELD32(0x00000040) +#define MAC_CSR13_VAL7 FIELD32(0x00000080) +#define MAC_CSR13_DIR0 FIELD32(0x00000100) +#define MAC_CSR13_DIR1 FIELD32(0x00000200) +#define MAC_CSR13_DIR2 FIELD32(0x00000400) +#define MAC_CSR13_DIR3 FIELD32(0x00000800) +#define MAC_CSR13_DIR4 FIELD32(0x00001000) +#define MAC_CSR13_DIR5 FIELD32(0x00002000) +#define MAC_CSR13_DIR6 FIELD32(0x00004000) +#define MAC_CSR13_DIR7 FIELD32(0x00008000) + +/* + * MAC_CSR14: LED control register. + * ON_PERIOD: On period, default 70ms. + * OFF_PERIOD: Off period, default 30ms. + * HW_LED: HW TX activity, 1: normal OFF, 0: normal ON. + * SW_LED: s/w LED, 1: ON, 0: OFF. + * HW_LED_POLARITY: 0: active low, 1: active high. + */ +#define MAC_CSR14 0x3038 +#define MAC_CSR14_ON_PERIOD FIELD32(0x000000ff) +#define MAC_CSR14_OFF_PERIOD FIELD32(0x0000ff00) +#define MAC_CSR14_HW_LED FIELD32(0x00010000) +#define MAC_CSR14_SW_LED FIELD32(0x00020000) +#define MAC_CSR14_HW_LED_POLARITY FIELD32(0x00040000) +#define MAC_CSR14_SW_LED2 FIELD32(0x00080000) + +/* + * MAC_CSR15: NAV control. + */ +#define MAC_CSR15 0x303c + +/* + * TXRX control registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * TXRX_CSR0: TX/RX configuration register. + * TSF_OFFSET: Default is 24. + * AUTO_TX_SEQ: 1: ASIC auto replace sequence nr in outgoing frame. + * DISABLE_RX: Disable Rx engine. + * DROP_CRC: Drop CRC error. + * DROP_PHYSICAL: Drop physical error. + * DROP_CONTROL: Drop control frame. + * DROP_NOT_TO_ME: Drop not to me unicast frame. + * DROP_TO_DS: Drop fram ToDs bit is true. + * DROP_VERSION_ERROR: Drop version error frame. + * DROP_MULTICAST: Drop multicast frames. + * DROP_BORADCAST: Drop broadcast frames. + * DROP_ACK_CTS: Drop received ACK and CTS. + */ +#define TXRX_CSR0 0x3040 +#define TXRX_CSR0_RX_ACK_TIMEOUT FIELD32(0x000001ff) +#define TXRX_CSR0_TSF_OFFSET FIELD32(0x00007e00) +#define TXRX_CSR0_AUTO_TX_SEQ FIELD32(0x00008000) +#define TXRX_CSR0_DISABLE_RX FIELD32(0x00010000) +#define TXRX_CSR0_DROP_CRC FIELD32(0x00020000) +#define TXRX_CSR0_DROP_PHYSICAL FIELD32(0x00040000) +#define TXRX_CSR0_DROP_CONTROL FIELD32(0x00080000) +#define TXRX_CSR0_DROP_NOT_TO_ME FIELD32(0x00100000) +#define TXRX_CSR0_DROP_TO_DS FIELD32(0x00200000) +#define TXRX_CSR0_DROP_VERSION_ERROR FIELD32(0x00400000) +#define TXRX_CSR0_DROP_MULTICAST FIELD32(0x00800000) +#define TXRX_CSR0_DROP_BROADCAST FIELD32(0x01000000) +#define TXRX_CSR0_DROP_ACK_CTS FIELD32(0x02000000) +#define TXRX_CSR0_TX_WITHOUT_WAITING FIELD32(0x04000000) + +/* + * TXRX_CSR1 + */ +#define TXRX_CSR1 0x3044 +#define TXRX_CSR1_BBP_ID0 FIELD32(0x0000007f) +#define TXRX_CSR1_BBP_ID0_VALID FIELD32(0x00000080) +#define TXRX_CSR1_BBP_ID1 FIELD32(0x00007f00) +#define TXRX_CSR1_BBP_ID1_VALID FIELD32(0x00008000) +#define TXRX_CSR1_BBP_ID2 FIELD32(0x007f0000) +#define TXRX_CSR1_BBP_ID2_VALID FIELD32(0x00800000) +#define TXRX_CSR1_BBP_ID3 FIELD32(0x7f000000) +#define TXRX_CSR1_BBP_ID3_VALID FIELD32(0x80000000) + +/* + * TXRX_CSR2 + */ +#define TXRX_CSR2 0x3048 +#define TXRX_CSR2_BBP_ID0 FIELD32(0x0000007f) +#define TXRX_CSR2_BBP_ID0_VALID FIELD32(0x00000080) +#define TXRX_CSR2_BBP_ID1 FIELD32(0x00007f00) +#define TXRX_CSR2_BBP_ID1_VALID FIELD32(0x00008000) +#define TXRX_CSR2_BBP_ID2 FIELD32(0x007f0000) +#define TXRX_CSR2_BBP_ID2_VALID FIELD32(0x00800000) +#define TXRX_CSR2_BBP_ID3 FIELD32(0x7f000000) +#define TXRX_CSR2_BBP_ID3_VALID FIELD32(0x80000000) + +/* + * TXRX_CSR3 + */ +#define TXRX_CSR3 0x304c +#define TXRX_CSR3_BBP_ID0 FIELD32(0x0000007f) +#define TXRX_CSR3_BBP_ID0_VALID FIELD32(0x00000080) +#define TXRX_CSR3_BBP_ID1 FIELD32(0x00007f00) +#define TXRX_CSR3_BBP_ID1_VALID FIELD32(0x00008000) +#define TXRX_CSR3_BBP_ID2 FIELD32(0x007f0000) +#define TXRX_CSR3_BBP_ID2_VALID FIELD32(0x00800000) +#define TXRX_CSR3_BBP_ID3 FIELD32(0x7f000000) +#define TXRX_CSR3_BBP_ID3_VALID FIELD32(0x80000000) + +/* + * TXRX_CSR4: Auto-Responder/Tx-retry register. + * AUTORESPOND_PREAMBLE: 0:long, 1:short preamble. + * OFDM_TX_RATE_DOWN: 1:enable. + * OFDM_TX_RATE_STEP: 0:1-step, 1: 2-step, 2:3-step, 3:4-step. + * OFDM_TX_FALLBACK_CCK: 0: Fallback to OFDM 6M only, 1: Fallback to CCK 1M,2M. + */ +#define TXRX_CSR4 0x3050 +#define TXRX_CSR4_TX_ACK_TIMEOUT FIELD32(0x000000ff) +#define TXRX_CSR4_CNTL_ACK_POLICY FIELD32(0x00000700) +#define TXRX_CSR4_ACK_CTS_PSM FIELD32(0x00010000) +#define TXRX_CSR4_AUTORESPOND_ENABLE FIELD32(0x00020000) +#define TXRX_CSR4_AUTORESPOND_PREAMBLE FIELD32(0x00040000) +#define TXRX_CSR4_OFDM_TX_RATE_DOWN FIELD32(0x00080000) +#define TXRX_CSR4_OFDM_TX_RATE_STEP FIELD32(0x00300000) +#define TXRX_CSR4_OFDM_TX_FALLBACK_CCK FIELD32(0x00400000) +#define TXRX_CSR4_LONG_RETRY_LIMIT FIELD32(0x0f000000) +#define TXRX_CSR4_SHORT_RETRY_LIMIT FIELD32(0xf0000000) + +/* + * TXRX_CSR5 + */ +#define TXRX_CSR5 0x3054 + +/* + * TXRX_CSR6: ACK/CTS payload consumed time + */ +#define TXRX_CSR6 0x3058 + +/* + * TXRX_CSR7: OFDM ACK/CTS payload consumed time for 6/9/12/18 mbps. + */ +#define TXRX_CSR7 0x305c +#define TXRX_CSR7_ACK_CTS_6MBS FIELD32(0x000000ff) +#define TXRX_CSR7_ACK_CTS_9MBS FIELD32(0x0000ff00) +#define TXRX_CSR7_ACK_CTS_12MBS FIELD32(0x00ff0000) +#define TXRX_CSR7_ACK_CTS_18MBS FIELD32(0xff000000) + +/* + * TXRX_CSR8: OFDM ACK/CTS payload consumed time for 24/36/48/54 mbps. + */ +#define TXRX_CSR8 0x3060 +#define TXRX_CSR8_ACK_CTS_24MBS FIELD32(0x000000ff) +#define TXRX_CSR8_ACK_CTS_36MBS FIELD32(0x0000ff00) +#define TXRX_CSR8_ACK_CTS_48MBS FIELD32(0x00ff0000) +#define TXRX_CSR8_ACK_CTS_54MBS FIELD32(0xff000000) + +/* + * TXRX_CSR9: Synchronization control register. + * BEACON_INTERVAL: In unit of 1/16 TU. + * TSF_TICKING: Enable TSF auto counting. + * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. + * BEACON_GEN: Enable beacon generator. + */ +#define TXRX_CSR9 0x3064 +#define TXRX_CSR9_BEACON_INTERVAL FIELD32(0x0000ffff) +#define TXRX_CSR9_TSF_TICKING FIELD32(0x00010000) +#define TXRX_CSR9_TSF_SYNC FIELD32(0x00060000) +#define TXRX_CSR9_TBTT_ENABLE FIELD32(0x00080000) +#define TXRX_CSR9_BEACON_GEN FIELD32(0x00100000) +#define TXRX_CSR9_TIMESTAMP_COMPENSATE FIELD32(0xff000000) + +/* + * TXRX_CSR10: BEACON alignment. + */ +#define TXRX_CSR10 0x3068 + +/* + * TXRX_CSR11: AES mask. + */ +#define TXRX_CSR11 0x306c + +/* + * TXRX_CSR12: TSF low 32. + */ +#define TXRX_CSR12 0x3070 +#define TXRX_CSR12_LOW_TSFTIMER FIELD32(0xffffffff) + +/* + * TXRX_CSR13: TSF high 32. + */ +#define TXRX_CSR13 0x3074 +#define TXRX_CSR13_HIGH_TSFTIMER FIELD32(0xffffffff) + +/* + * TXRX_CSR14: TBTT timer. + */ +#define TXRX_CSR14 0x3078 + +/* + * TXRX_CSR15: TKIP MIC priority byte "AND" mask. + */ +#define TXRX_CSR15 0x307c + +/* + * PHY control registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * PHY_CSR0: RF/PS control. + */ +#define PHY_CSR0 0x3080 +#define PHY_CSR0_PA_PE_BG FIELD32(0x00010000) +#define PHY_CSR0_PA_PE_A FIELD32(0x00020000) + +/* + * PHY_CSR1 + */ +#define PHY_CSR1 0x3084 +#define PHY_CSR1_RF_RPI FIELD32(0x00010000) + +/* + * PHY_CSR2: Pre-TX BBP control. + */ +#define PHY_CSR2 0x3088 + +/* + * PHY_CSR3: BBP serial control register. + * VALUE: Register value to program into BBP. + * REG_NUM: Selected BBP register. + * READ_CONTROL: 0: Write BBP, 1: Read BBP. + * BUSY: 1: ASIC is busy execute BBP programming. + */ +#define PHY_CSR3 0x308c +#define PHY_CSR3_VALUE FIELD32(0x000000ff) +#define PHY_CSR3_REGNUM FIELD32(0x00007f00) +#define PHY_CSR3_READ_CONTROL FIELD32(0x00008000) +#define PHY_CSR3_BUSY FIELD32(0x00010000) + +/* + * PHY_CSR4: RF serial control register + * VALUE: Register value (include register id) serial out to RF/IF chip. + * NUMBER_OF_BITS: Number of bits used in RFRegValue (I:20, RFMD:22). + * IF_SELECT: 1: select IF to program, 0: select RF to program. + * PLL_LD: RF PLL_LD status. + * BUSY: 1: ASIC is busy execute RF programming. + */ +#define PHY_CSR4 0x3090 +#define PHY_CSR4_VALUE FIELD32(0x00ffffff) +#define PHY_CSR4_NUMBER_OF_BITS FIELD32(0x1f000000) +#define PHY_CSR4_IF_SELECT FIELD32(0x20000000) +#define PHY_CSR4_PLL_LD FIELD32(0x40000000) +#define PHY_CSR4_BUSY FIELD32(0x80000000) + +/* + * PHY_CSR5: RX to TX signal switch timing control. + */ +#define PHY_CSR5 0x3094 +#define PHY_CSR5_IQ_FLIP FIELD32(0x00000004) + +/* + * PHY_CSR6: TX to RX signal timing control. + */ +#define PHY_CSR6 0x3098 +#define PHY_CSR6_IQ_FLIP FIELD32(0x00000004) + +/* + * PHY_CSR7: TX DAC switching timing control. + */ +#define PHY_CSR7 0x309c + +/* + * Security control register. + */ + +/* + * SEC_CSR0: Shared key table control. + */ +#define SEC_CSR0 0x30a0 +#define SEC_CSR0_BSS0_KEY0_VALID FIELD32(0x00000001) +#define SEC_CSR0_BSS0_KEY1_VALID FIELD32(0x00000002) +#define SEC_CSR0_BSS0_KEY2_VALID FIELD32(0x00000004) +#define SEC_CSR0_BSS0_KEY3_VALID FIELD32(0x00000008) +#define SEC_CSR0_BSS1_KEY0_VALID FIELD32(0x00000010) +#define SEC_CSR0_BSS1_KEY1_VALID FIELD32(0x00000020) +#define SEC_CSR0_BSS1_KEY2_VALID FIELD32(0x00000040) +#define SEC_CSR0_BSS1_KEY3_VALID FIELD32(0x00000080) +#define SEC_CSR0_BSS2_KEY0_VALID FIELD32(0x00000100) +#define SEC_CSR0_BSS2_KEY1_VALID FIELD32(0x00000200) +#define SEC_CSR0_BSS2_KEY2_VALID FIELD32(0x00000400) +#define SEC_CSR0_BSS2_KEY3_VALID FIELD32(0x00000800) +#define SEC_CSR0_BSS3_KEY0_VALID FIELD32(0x00001000) +#define SEC_CSR0_BSS3_KEY1_VALID FIELD32(0x00002000) +#define SEC_CSR0_BSS3_KEY2_VALID FIELD32(0x00004000) +#define SEC_CSR0_BSS3_KEY3_VALID FIELD32(0x00008000) + +/* + * SEC_CSR1: Shared key table security mode register. + */ +#define SEC_CSR1 0x30a4 +#define SEC_CSR1_BSS0_KEY0_CIPHER_ALG FIELD32(0x00000007) +#define SEC_CSR1_BSS0_KEY1_CIPHER_ALG FIELD32(0x00000070) +#define SEC_CSR1_BSS0_KEY2_CIPHER_ALG FIELD32(0x00000700) +#define SEC_CSR1_BSS0_KEY3_CIPHER_ALG FIELD32(0x00007000) +#define SEC_CSR1_BSS1_KEY0_CIPHER_ALG FIELD32(0x00070000) +#define SEC_CSR1_BSS1_KEY1_CIPHER_ALG FIELD32(0x00700000) +#define SEC_CSR1_BSS1_KEY2_CIPHER_ALG FIELD32(0x07000000) +#define SEC_CSR1_BSS1_KEY3_CIPHER_ALG FIELD32(0x70000000) + +/* + * Pairwise key table valid bitmap registers. + * SEC_CSR2: pairwise key table valid bitmap 0. + * SEC_CSR3: pairwise key table valid bitmap 1. + */ +#define SEC_CSR2 0x30a8 +#define SEC_CSR3 0x30ac + +/* + * SEC_CSR4: Pairwise key table lookup control. + */ +#define SEC_CSR4 0x30b0 +#define SEC_CSR4_ENABLE_BSS0 FIELD32(0x00000001) +#define SEC_CSR4_ENABLE_BSS1 FIELD32(0x00000002) +#define SEC_CSR4_ENABLE_BSS2 FIELD32(0x00000004) +#define SEC_CSR4_ENABLE_BSS3 FIELD32(0x00000008) + +/* + * SEC_CSR5: shared key table security mode register. + */ +#define SEC_CSR5 0x30b4 +#define SEC_CSR5_BSS2_KEY0_CIPHER_ALG FIELD32(0x00000007) +#define SEC_CSR5_BSS2_KEY1_CIPHER_ALG FIELD32(0x00000070) +#define SEC_CSR5_BSS2_KEY2_CIPHER_ALG FIELD32(0x00000700) +#define SEC_CSR5_BSS2_KEY3_CIPHER_ALG FIELD32(0x00007000) +#define SEC_CSR5_BSS3_KEY0_CIPHER_ALG FIELD32(0x00070000) +#define SEC_CSR5_BSS3_KEY1_CIPHER_ALG FIELD32(0x00700000) +#define SEC_CSR5_BSS3_KEY2_CIPHER_ALG FIELD32(0x07000000) +#define SEC_CSR5_BSS3_KEY3_CIPHER_ALG FIELD32(0x70000000) + +/* + * STA control registers. + */ + +/* + * STA_CSR0: RX PLCP error count & RX FCS error count. + */ +#define STA_CSR0 0x30c0 +#define STA_CSR0_FCS_ERROR FIELD32(0x0000ffff) +#define STA_CSR0_PLCP_ERROR FIELD32(0xffff0000) + +/* + * STA_CSR1: RX False CCA count & RX LONG frame count. + */ +#define STA_CSR1 0x30c4 +#define STA_CSR1_PHYSICAL_ERROR FIELD32(0x0000ffff) +#define STA_CSR1_FALSE_CCA_ERROR FIELD32(0xffff0000) + +/* + * STA_CSR2: TX Beacon count and RX FIFO overflow count. + */ +#define STA_CSR2 0x30c8 +#define STA_CSR2_RX_FIFO_OVERFLOW_COUNT FIELD32(0x0000ffff) +#define STA_CSR2_RX_OVERFLOW_COUNT FIELD32(0xffff0000) + +/* + * STA_CSR3: TX Beacon count. + */ +#define STA_CSR3 0x30cc +#define STA_CSR3_TX_BEACON_COUNT FIELD32(0x0000ffff) + +/* + * STA_CSR4: TX Retry count. + */ +#define STA_CSR4 0x30d0 +#define STA_CSR4_TX_NO_RETRY_COUNT FIELD32(0x0000ffff) +#define STA_CSR4_TX_ONE_RETRY_COUNT FIELD32(0xffff0000) + +/* + * STA_CSR5: TX Retry count. + */ +#define STA_CSR5 0x30d4 +#define STA_CSR4_TX_MULTI_RETRY_COUNT FIELD32(0x0000ffff) +#define STA_CSR4_TX_RETRY_FAIL_COUNT FIELD32(0xffff0000) + +/* + * QOS control registers. + */ + +/* + * QOS_CSR1: TXOP holder MAC address register. + */ +#define QOS_CSR1 0x30e4 +#define QOS_CSR1_BYTE4 FIELD32(0x000000ff) +#define QOS_CSR1_BYTE5 FIELD32(0x0000ff00) + +/* + * QOS_CSR2: TXOP holder timeout register. + */ +#define QOS_CSR2 0x30e8 + +/* + * RX QOS-CFPOLL MAC address register. + * QOS_CSR3: RX QOS-CFPOLL MAC address 0. + * QOS_CSR4: RX QOS-CFPOLL MAC address 1. + */ +#define QOS_CSR3 0x30ec +#define QOS_CSR4 0x30f0 + +/* + * QOS_CSR5: "QosControl" field of the RX QOS-CFPOLL. + */ +#define QOS_CSR5 0x30f4 + +/* + * WMM Scheduler Register + */ + +/* + * AIFSN_CSR: AIFSN for each EDCA AC. + * AIFSN0: For AC_VO. + * AIFSN1: For AC_VI. + * AIFSN2: For AC_BE. + * AIFSN3: For AC_BK. + */ +#define AIFSN_CSR 0x0400 +#define AIFSN_CSR_AIFSN0 FIELD32(0x0000000f) +#define AIFSN_CSR_AIFSN1 FIELD32(0x000000f0) +#define AIFSN_CSR_AIFSN2 FIELD32(0x00000f00) +#define AIFSN_CSR_AIFSN3 FIELD32(0x0000f000) + +/* + * CWMIN_CSR: CWmin for each EDCA AC. + * CWMIN0: For AC_VO. + * CWMIN1: For AC_VI. + * CWMIN2: For AC_BE. + * CWMIN3: For AC_BK. + */ +#define CWMIN_CSR 0x0404 +#define CWMIN_CSR_CWMIN0 FIELD32(0x0000000f) +#define CWMIN_CSR_CWMIN1 FIELD32(0x000000f0) +#define CWMIN_CSR_CWMIN2 FIELD32(0x00000f00) +#define CWMIN_CSR_CWMIN3 FIELD32(0x0000f000) + +/* + * CWMAX_CSR: CWmax for each EDCA AC. + * CWMAX0: For AC_VO. + * CWMAX1: For AC_VI. + * CWMAX2: For AC_BE. + * CWMAX3: For AC_BK. + */ +#define CWMAX_CSR 0x0408 +#define CWMAX_CSR_CWMAX0 FIELD32(0x0000000f) +#define CWMAX_CSR_CWMAX1 FIELD32(0x000000f0) +#define CWMAX_CSR_CWMAX2 FIELD32(0x00000f00) +#define CWMAX_CSR_CWMAX3 FIELD32(0x0000f000) + +/* + * AC_TXOP_CSR0: AC_VO/AC_VI TXOP register. + * AC0_TX_OP: For AC_VO, in unit of 32us. + * AC1_TX_OP: For AC_VI, in unit of 32us. + */ +#define AC_TXOP_CSR0 0x040c +#define AC_TXOP_CSR0_AC0_TX_OP FIELD32(0x0000ffff) +#define AC_TXOP_CSR0_AC1_TX_OP FIELD32(0xffff0000) + +/* + * AC_TXOP_CSR1: AC_BE/AC_BK TXOP register. + * AC2_TX_OP: For AC_BE, in unit of 32us. + * AC3_TX_OP: For AC_BK, in unit of 32us. + */ +#define AC_TXOP_CSR1 0x0410 +#define AC_TXOP_CSR1_AC2_TX_OP FIELD32(0x0000ffff) +#define AC_TXOP_CSR1_AC3_TX_OP FIELD32(0xffff0000) + +/* + * BBP registers. + * The wordsize of the BBP is 8 bits. + */ + +/* + * R2 + */ +#define BBP_R2_BG_MODE FIELD8(0x20) + +/* + * R3 + */ +#define BBP_R3_SMART_MODE FIELD8(0x01) + +/* + * R4: RX antenna control + * FRAME_END: 1 - DPDT, 0 - SPDT (Only valid for 802.11G, RF2527 & RF2529) + */ + +/* + * ANTENNA_CONTROL semantics (guessed): + * 0x1: Software controlled antenna switching (fixed or SW diversity) + * 0x2: Hardware diversity. + */ +#define BBP_R4_RX_ANTENNA_CONTROL FIELD8(0x03) +#define BBP_R4_RX_FRAME_END FIELD8(0x20) + +/* + * R77 + */ +#define BBP_R77_RX_ANTENNA FIELD8(0x03) + +/* + * RF registers + */ + +/* + * RF 3 + */ +#define RF3_TXPOWER FIELD32(0x00003e00) + +/* + * RF 4 + */ +#define RF4_FREQ_OFFSET FIELD32(0x0003f000) + +/* + * EEPROM content. + * The wordsize of the EEPROM is 16 bits. + */ + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_0 0x0002 +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR1 0x0003 +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_2 0x0004 +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM antenna. + * ANTENNA_NUM: Number of antennas. + * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * FRAME_TYPE: 0: DPDT , 1: SPDT , noted this bit is valid for g only. + * DYN_TXAGC: Dynamic TX AGC control. + * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. + * RF_TYPE: Rf_type of this adapter. + */ +#define EEPROM_ANTENNA 0x0010 +#define EEPROM_ANTENNA_NUM FIELD16(0x0003) +#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) +#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) +#define EEPROM_ANTENNA_FRAME_TYPE FIELD16(0x0040) +#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) +#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) +#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) + +/* + * EEPROM NIC config. + * EXTERNAL_LNA: External LNA. + */ +#define EEPROM_NIC 0x0011 +#define EEPROM_NIC_EXTERNAL_LNA FIELD16(0x0010) + +/* + * EEPROM geography. + * GEO_A: Default geographical setting for 5GHz band + * GEO: Default geographical setting. + */ +#define EEPROM_GEOGRAPHY 0x0012 +#define EEPROM_GEOGRAPHY_GEO_A FIELD16(0x00ff) +#define EEPROM_GEOGRAPHY_GEO FIELD16(0xff00) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_START 0x0013 +#define EEPROM_BBP_SIZE 16 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* + * EEPROM TXPOWER 802.11G + */ +#define EEPROM_TXPOWER_G_START 0x0023 +#define EEPROM_TXPOWER_G_SIZE 7 +#define EEPROM_TXPOWER_G_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_G_2 FIELD16(0xff00) + +/* + * EEPROM Frequency + */ +#define EEPROM_FREQ 0x002f +#define EEPROM_FREQ_OFFSET FIELD16(0x00ff) +#define EEPROM_FREQ_SEQ_MASK FIELD16(0xff00) +#define EEPROM_FREQ_SEQ FIELD16(0x0300) + +/* + * EEPROM LED. + * POLARITY_RDY_G: Polarity RDY_G setting. + * POLARITY_RDY_A: Polarity RDY_A setting. + * POLARITY_ACT: Polarity ACT setting. + * POLARITY_GPIO_0: Polarity GPIO0 setting. + * POLARITY_GPIO_1: Polarity GPIO1 setting. + * POLARITY_GPIO_2: Polarity GPIO2 setting. + * POLARITY_GPIO_3: Polarity GPIO3 setting. + * POLARITY_GPIO_4: Polarity GPIO4 setting. + * LED_MODE: Led mode. + */ +#define EEPROM_LED 0x0030 +#define EEPROM_LED_POLARITY_RDY_G FIELD16(0x0001) +#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) +#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) +#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) +#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) +#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) +#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) +#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) +#define EEPROM_LED_LED_MODE FIELD16(0x1f00) + +/* + * EEPROM TXPOWER 802.11A + */ +#define EEPROM_TXPOWER_A_START 0x0031 +#define EEPROM_TXPOWER_A_SIZE 12 +#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) + +/* + * EEPROM RSSI offset 802.11BG + */ +#define EEPROM_RSSI_OFFSET_BG 0x004d +#define EEPROM_RSSI_OFFSET_BG_1 FIELD16(0x00ff) +#define EEPROM_RSSI_OFFSET_BG_2 FIELD16(0xff00) + +/* + * EEPROM RSSI offset 802.11A + */ +#define EEPROM_RSSI_OFFSET_A 0x004e +#define EEPROM_RSSI_OFFSET_A_1 FIELD16(0x00ff) +#define EEPROM_RSSI_OFFSET_A_2 FIELD16(0xff00) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE ( 6 * sizeof(__le32) ) +#define TXINFO_SIZE ( 6 * sizeof(__le32) ) +#define RXD_DESC_SIZE ( 6 * sizeof(__le32) ) + +/* + * TX descriptor format for TX, PRIO and Beacon Ring. + */ + +/* + * Word0 + * BURST: Next frame belongs to same "burst" event. + * TKIP_MIC: ASIC appends TKIP MIC if TKIP is used. + * KEY_TABLE: Use per-client pairwise KEY table. + * KEY_INDEX: + * Key index (0~31) to the pairwise KEY table. + * 0~3 to shared KEY table 0 (BSS0). + * 4~7 to shared KEY table 1 (BSS1). + * 8~11 to shared KEY table 2 (BSS2). + * 12~15 to shared KEY table 3 (BSS3). + * BURST2: For backward compatibility, set to same value as BURST. + */ +#define TXD_W0_BURST FIELD32(0x00000001) +#define TXD_W0_VALID FIELD32(0x00000002) +#define TXD_W0_MORE_FRAG FIELD32(0x00000004) +#define TXD_W0_ACK FIELD32(0x00000008) +#define TXD_W0_TIMESTAMP FIELD32(0x00000010) +#define TXD_W0_OFDM FIELD32(0x00000020) +#define TXD_W0_IFS FIELD32(0x00000040) +#define TXD_W0_RETRY_MODE FIELD32(0x00000080) +#define TXD_W0_TKIP_MIC FIELD32(0x00000100) +#define TXD_W0_KEY_TABLE FIELD32(0x00000200) +#define TXD_W0_KEY_INDEX FIELD32(0x0000fc00) +#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define TXD_W0_BURST2 FIELD32(0x10000000) +#define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * Word1 + * HOST_Q_ID: EDCA/HCCA queue ID. + * HW_SEQUENCE: MAC overwrites the frame sequence number. + * BUFFER_COUNT: Number of buffers in this TXD. + */ +#define TXD_W1_HOST_Q_ID FIELD32(0x0000000f) +#define TXD_W1_AIFSN FIELD32(0x000000f0) +#define TXD_W1_CWMIN FIELD32(0x00000f00) +#define TXD_W1_CWMAX FIELD32(0x0000f000) +#define TXD_W1_IV_OFFSET FIELD32(0x003f0000) +#define TXD_W1_HW_SEQUENCE FIELD32(0x10000000) +#define TXD_W1_BUFFER_COUNT FIELD32(0xe0000000) + +/* + * Word2: PLCP information + */ +#define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) +#define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) +#define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) +#define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) + +/* + * Word3 + */ +#define TXD_W3_IV FIELD32(0xffffffff) + +/* + * Word4 + */ +#define TXD_W4_EIV FIELD32(0xffffffff) + +/* + * Word5 + * FRAME_OFFSET: Frame start offset inside ASIC TXFIFO (after TXINFO field). + * PACKET_ID: Driver assigned packet ID to categorize TXResult in interrupt. + * WAITING_DMA_DONE_INT: TXD been filled with data + * and waiting for TxDoneISR housekeeping. + */ +#define TXD_W5_FRAME_OFFSET FIELD32(0x000000ff) +#define TXD_W5_PACKET_ID FIELD32(0x0000ff00) +#define TXD_W5_TX_POWER FIELD32(0x00ff0000) +#define TXD_W5_WAITING_DMA_DONE_INT FIELD32(0x01000000) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + * CIPHER_ERROR: 1:ICV error, 2:MIC error, 3:invalid key. + * KEY_INDEX: Decryption key actually used. + */ +#define RXD_W0_OWNER_NIC FIELD32(0x00000001) +#define RXD_W0_DROP FIELD32(0x00000002) +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000004) +#define RXD_W0_MULTICAST FIELD32(0x00000008) +#define RXD_W0_BROADCAST FIELD32(0x00000010) +#define RXD_W0_MY_BSS FIELD32(0x00000020) +#define RXD_W0_CRC_ERROR FIELD32(0x00000040) +#define RXD_W0_OFDM FIELD32(0x00000080) +#define RXD_W0_CIPHER_ERROR FIELD32(0x00000300) +#define RXD_W0_KEY_INDEX FIELD32(0x0000fc00) +#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * WORD1 + * SIGNAL: RX raw data rate reported by BBP. + * RSSI: RSSI reported by BBP. + */ +#define RXD_W1_SIGNAL FIELD32(0x000000ff) +#define RXD_W1_RSSI_AGC FIELD32(0x00001f00) +#define RXD_W1_RSSI_LNA FIELD32(0x00006000) +#define RXD_W1_FRAME_OFFSET FIELD32(0x7f000000) + +/* + * Word2 + * IV: Received IV of originally encrypted. + */ +#define RXD_W2_IV FIELD32(0xffffffff) + +/* + * Word3 + * EIV: Received EIV of originally encrypted. + */ +#define RXD_W3_EIV FIELD32(0xffffffff) + +/* + * Word4 + * ICV: Received ICV of originally encrypted. + * NOTE: This is a guess, the official definition is "reserved" + */ +#define RXD_W4_ICV FIELD32(0xffffffff) + +/* + * the above 20-byte is called RXINFO and will be DMAed to MAC RX block + * and passed to the HOST driver. + * The following fields are for DMA block and HOST usage only. + * Can't be touched by ASIC MAC block. + */ + +/* + * Word5 + */ +#define RXD_W5_RESERVED FIELD32(0xffffffff) + +/* + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. + */ +#define MIN_TXPOWER 0 +#define MAX_TXPOWER 31 +#define DEFAULT_TXPOWER 24 + +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) + +#endif /* RT73USB_H */ diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig deleted file mode 100644 index de62f5dcb62f..000000000000 --- a/drivers/net/wireless/rt2x00/Kconfig +++ /dev/null @@ -1,269 +0,0 @@ -menuconfig RT2X00 - tristate "Ralink driver support" - depends on MAC80211 && HAS_DMA - ---help--- - This will enable the support for the Ralink drivers, - developed in the rt2x00 project . - - These drivers make use of the mac80211 stack. - - When building one of the individual drivers, the rt2x00 library - will also be created. That library (when the driver is built as - a module) will be called rt2x00lib. - - Additionally PCI and USB libraries will also be build depending - on the types of drivers being selected, these libraries will be - called rt2x00pci and rt2x00usb. - -if RT2X00 - -config RT2400PCI - tristate "Ralink rt2400 (PCI/PCMCIA) support" - depends on PCI - select RT2X00_LIB_MMIO - select RT2X00_LIB_PCI - select EEPROM_93CX6 - ---help--- - This adds support for rt2400 wireless chipset family. - Supported chips: RT2460. - - When compiled as a module, this driver will be called rt2400pci. - -config RT2500PCI - tristate "Ralink rt2500 (PCI/PCMCIA) support" - depends on PCI - select RT2X00_LIB_MMIO - select RT2X00_LIB_PCI - select EEPROM_93CX6 - ---help--- - This adds support for rt2500 wireless chipset family. - Supported chips: RT2560. - - When compiled as a module, this driver will be called rt2500pci. - -config RT61PCI - tristate "Ralink rt2501/rt61 (PCI/PCMCIA) support" - depends on PCI - select RT2X00_LIB_PCI - select RT2X00_LIB_MMIO - select RT2X00_LIB_FIRMWARE - select RT2X00_LIB_CRYPTO - select CRC_ITU_T - select EEPROM_93CX6 - ---help--- - This adds support for rt2501 wireless chipset family. - Supported chips: RT2561, RT2561S & RT2661. - - When compiled as a module, this driver will be called rt61pci. - -config RT2800PCI - tristate "Ralink rt27xx/rt28xx/rt30xx (PCI/PCIe/PCMCIA) support" - depends on PCI - select RT2800_LIB - select RT2800_LIB_MMIO - select RT2X00_LIB_MMIO - select RT2X00_LIB_PCI - select RT2X00_LIB_FIRMWARE - select RT2X00_LIB_CRYPTO - select CRC_CCITT - select EEPROM_93CX6 - ---help--- - This adds support for rt27xx/rt28xx/rt30xx wireless chipset family. - Supported chips: RT2760, RT2790, RT2860, RT2880, RT2890, RT3052, - RT3090, RT3091 & RT3092 - - When compiled as a module, this driver will be called "rt2800pci.ko". - -if RT2800PCI - -config RT2800PCI_RT33XX - bool "rt2800pci - Include support for rt33xx devices" - default y - ---help--- - This adds support for rt33xx wireless chipset family to the - rt2800pci driver. - Supported chips: RT3390 - -config RT2800PCI_RT35XX - bool "rt2800pci - Include support for rt35xx devices (EXPERIMENTAL)" - default y - ---help--- - This adds support for rt35xx wireless chipset family to the - rt2800pci driver. - Supported chips: RT3060, RT3062, RT3562, RT3592 - - -config RT2800PCI_RT53XX - bool "rt2800pci - Include support for rt53xx devices (EXPERIMENTAL)" - default y - ---help--- - This adds support for rt53xx wireless chipset family to the - rt2800pci driver. - Supported chips: RT5390 - -config RT2800PCI_RT3290 - bool "rt2800pci - Include support for rt3290 devices (EXPERIMENTAL)" - default y - ---help--- - This adds support for rt3290 wireless chipset family to the - rt2800pci driver. - Supported chips: RT3290 -endif - -config RT2500USB - tristate "Ralink rt2500 (USB) support" - depends on USB - select RT2X00_LIB_USB - select RT2X00_LIB_CRYPTO - ---help--- - This adds support for rt2500 wireless chipset family. - Supported chips: RT2571 & RT2572. - - When compiled as a module, this driver will be called rt2500usb. - -config RT73USB - tristate "Ralink rt2501/rt73 (USB) support" - depends on USB - select RT2X00_LIB_USB - select RT2X00_LIB_FIRMWARE - select RT2X00_LIB_CRYPTO - select CRC_ITU_T - ---help--- - This adds support for rt2501 wireless chipset family. - Supported chips: RT2571W, RT2573 & RT2671. - - When compiled as a module, this driver will be called rt73usb. - -config RT2800USB - tristate "Ralink rt27xx/rt28xx/rt30xx (USB) support" - depends on USB - select RT2800_LIB - select RT2X00_LIB_USB - select RT2X00_LIB_FIRMWARE - select RT2X00_LIB_CRYPTO - select CRC_CCITT - ---help--- - This adds support for rt27xx/rt28xx/rt30xx wireless chipset family. - Supported chips: RT2770, RT2870 & RT3070, RT3071 & RT3072 - - When compiled as a module, this driver will be called "rt2800usb.ko". - -if RT2800USB - -config RT2800USB_RT33XX - bool "rt2800usb - Include support for rt33xx devices" - default y - ---help--- - This adds support for rt33xx wireless chipset family to the - rt2800usb driver. - Supported chips: RT3370 - -config RT2800USB_RT35XX - bool "rt2800usb - Include support for rt35xx devices (EXPERIMENTAL)" - default y - ---help--- - This adds support for rt35xx wireless chipset family to the - rt2800usb driver. - Supported chips: RT3572 - -config RT2800USB_RT3573 - bool "rt2800usb - Include support for rt3573 devices (EXPERIMENTAL)" - ---help--- - This enables support for RT3573 chipset based wireless USB devices - in the rt2800usb driver. - -config RT2800USB_RT53XX - bool "rt2800usb - Include support for rt53xx devices (EXPERIMENTAL)" - ---help--- - This adds support for rt53xx wireless chipset family to the - rt2800usb driver. - Supported chips: RT5370 - -config RT2800USB_RT55XX - bool "rt2800usb - Include support for rt55xx devices (EXPERIMENTAL)" - ---help--- - This adds support for rt55xx wireless chipset family to the - rt2800usb driver. - Supported chips: RT5572 - -config RT2800USB_UNKNOWN - bool "rt2800usb - Include support for unknown (USB) devices" - default n - ---help--- - This adds support for rt2800usb devices that are known to - have a rt28xx family compatible chipset, but for which the exact - chipset is unknown. - - Support status for these devices is unknown, and enabling these - devices may or may not work. - -endif - -config RT2800SOC - tristate "Ralink WiSoC support" - depends on SOC_RT288X || SOC_RT305X - select RT2X00_LIB_SOC - select RT2X00_LIB_MMIO - select RT2X00_LIB_CRYPTO - select RT2X00_LIB_FIRMWARE - select RT2800_LIB - select RT2800_LIB_MMIO - ---help--- - This adds support for Ralink WiSoC devices. - Supported chips: RT2880, RT3050, RT3052, RT3350, RT3352. - - When compiled as a module, this driver will be called rt2800soc. - - -config RT2800_LIB - tristate - -config RT2800_LIB_MMIO - tristate - select RT2X00_LIB_MMIO - select RT2800_LIB - -config RT2X00_LIB_MMIO - tristate - -config RT2X00_LIB_PCI - tristate - select RT2X00_LIB - -config RT2X00_LIB_SOC - tristate - select RT2X00_LIB - -config RT2X00_LIB_USB - tristate - select RT2X00_LIB - -config RT2X00_LIB - tristate - -config RT2X00_LIB_FIRMWARE - bool - select FW_LOADER - -config RT2X00_LIB_CRYPTO - bool - -config RT2X00_LIB_LEDS - bool - default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n) - -config RT2X00_LIB_DEBUGFS - bool "Ralink debugfs support" - depends on RT2X00_LIB && MAC80211_DEBUGFS - ---help--- - Enable creation of debugfs files for the rt2x00 drivers. - These debugfs files support both reading and writing of the - most important register types of the rt2x00 hardware. - -config RT2X00_DEBUG - bool "Ralink debug output" - depends on RT2X00_LIB - ---help--- - Enable debugging output for all rt2x00 modules - -endif diff --git a/drivers/net/wireless/rt2x00/Makefile b/drivers/net/wireless/rt2x00/Makefile deleted file mode 100644 index 24a66015a495..000000000000 --- a/drivers/net/wireless/rt2x00/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -rt2x00lib-y += rt2x00dev.o -rt2x00lib-y += rt2x00mac.o -rt2x00lib-y += rt2x00config.o -rt2x00lib-y += rt2x00queue.o -rt2x00lib-y += rt2x00link.o -rt2x00lib-$(CONFIG_RT2X00_LIB_DEBUGFS) += rt2x00debug.o -rt2x00lib-$(CONFIG_RT2X00_LIB_CRYPTO) += rt2x00crypto.o -rt2x00lib-$(CONFIG_RT2X00_LIB_FIRMWARE) += rt2x00firmware.o -rt2x00lib-$(CONFIG_RT2X00_LIB_LEDS) += rt2x00leds.o - -obj-$(CONFIG_RT2X00_LIB) += rt2x00lib.o -obj-$(CONFIG_RT2X00_LIB_MMIO) += rt2x00mmio.o -obj-$(CONFIG_RT2X00_LIB_PCI) += rt2x00pci.o -obj-$(CONFIG_RT2X00_LIB_SOC) += rt2x00soc.o -obj-$(CONFIG_RT2X00_LIB_USB) += rt2x00usb.o -obj-$(CONFIG_RT2800_LIB) += rt2800lib.o -obj-$(CONFIG_RT2800_LIB_MMIO) += rt2800mmio.o -obj-$(CONFIG_RT2400PCI) += rt2400pci.o -obj-$(CONFIG_RT2500PCI) += rt2500pci.o -obj-$(CONFIG_RT61PCI) += rt61pci.o -obj-$(CONFIG_RT2800PCI) += rt2800pci.o -obj-$(CONFIG_RT2500USB) += rt2500usb.o -obj-$(CONFIG_RT73USB) += rt73usb.o -obj-$(CONFIG_RT2800USB) += rt2800usb.o -obj-$(CONFIG_RT2800SOC) += rt2800soc.o diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c deleted file mode 100644 index 9a3966cd6fbe..000000000000 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ /dev/null @@ -1,1850 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2400pci - Abstract: rt2400pci device specific routines. - Supported chipsets: RT2460. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00mmio.h" -#include "rt2x00pci.h" -#include "rt2400pci.h" - -/* - * Register access. - * All access to the CSR registers will go through the methods - * rt2x00mmio_register_read and rt2x00mmio_register_write. - * BBP and RF register require indirect register access, - * and use the CSR registers BBPCSR and RFCSR to achieve this. - * These indirect registers work with busy bits, - * and we will try maximal REGISTER_BUSY_COUNT times to access - * the register while taking a REGISTER_BUSY_DELAY us delay - * between each attempt. When the busy bit is still set at that time, - * the access attempt is considered to have failed, - * and we will print an error. - */ -#define WAIT_FOR_BBP(__dev, __reg) \ - rt2x00mmio_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg)) -#define WAIT_FOR_RF(__dev, __reg) \ - rt2x00mmio_regbusy_read((__dev), RFCSR, RFCSR_BUSY, (__reg)) - -static void rt2400pci_bbp_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u8 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, BBPCSR_VALUE, value); - rt2x00_set_field32(®, BBPCSR_REGNUM, word); - rt2x00_set_field32(®, BBPCSR_BUSY, 1); - rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); - - rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2400pci_bbp_read(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u8 *value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the read request into the register. - * After the data has been written, we wait until hardware - * returns the correct value, if at any time the register - * doesn't become available in time, reg will be 0xffffffff - * which means we return 0xff to the caller. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, BBPCSR_REGNUM, word); - rt2x00_set_field32(®, BBPCSR_BUSY, 1); - rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); - - rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); - - WAIT_FOR_BBP(rt2x00dev, ®); - } - - *value = rt2x00_get_field32(reg, BBPCSR_VALUE); - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2400pci_rf_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u32 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the RF becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_RF(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, RFCSR_VALUE, value); - rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); - rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); - rt2x00_set_field32(®, RFCSR_BUSY, 1); - - rt2x00mmio_register_write(rt2x00dev, RFCSR, reg); - rt2x00_rf_write(rt2x00dev, word, value); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2400pci_eepromregister_read(struct eeprom_93cx6 *eeprom) -{ - struct rt2x00_dev *rt2x00dev = eeprom->data; - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, CSR21, ®); - - eeprom->reg_data_in = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_IN); - eeprom->reg_data_out = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_OUT); - eeprom->reg_data_clock = - !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_CLOCK); - eeprom->reg_chip_select = - !!rt2x00_get_field32(reg, CSR21_EEPROM_CHIP_SELECT); -} - -static void rt2400pci_eepromregister_write(struct eeprom_93cx6 *eeprom) -{ - struct rt2x00_dev *rt2x00dev = eeprom->data; - u32 reg = 0; - - rt2x00_set_field32(®, CSR21_EEPROM_DATA_IN, !!eeprom->reg_data_in); - rt2x00_set_field32(®, CSR21_EEPROM_DATA_OUT, !!eeprom->reg_data_out); - rt2x00_set_field32(®, CSR21_EEPROM_DATA_CLOCK, - !!eeprom->reg_data_clock); - rt2x00_set_field32(®, CSR21_EEPROM_CHIP_SELECT, - !!eeprom->reg_chip_select); - - rt2x00mmio_register_write(rt2x00dev, CSR21, reg); -} - -#ifdef CONFIG_RT2X00_LIB_DEBUGFS -static const struct rt2x00debug rt2400pci_rt2x00debug = { - .owner = THIS_MODULE, - .csr = { - .read = rt2x00mmio_register_read, - .write = rt2x00mmio_register_write, - .flags = RT2X00DEBUGFS_OFFSET, - .word_base = CSR_REG_BASE, - .word_size = sizeof(u32), - .word_count = CSR_REG_SIZE / sizeof(u32), - }, - .eeprom = { - .read = rt2x00_eeprom_read, - .write = rt2x00_eeprom_write, - .word_base = EEPROM_BASE, - .word_size = sizeof(u16), - .word_count = EEPROM_SIZE / sizeof(u16), - }, - .bbp = { - .read = rt2400pci_bbp_read, - .write = rt2400pci_bbp_write, - .word_base = BBP_BASE, - .word_size = sizeof(u8), - .word_count = BBP_SIZE / sizeof(u8), - }, - .rf = { - .read = rt2x00_rf_read, - .write = rt2400pci_rf_write, - .word_base = RF_BASE, - .word_size = sizeof(u32), - .word_count = RF_SIZE / sizeof(u32), - }, -}; -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ - -static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); - return rt2x00_get_field32(reg, GPIOCSR_VAL0); -} - -#ifdef CONFIG_RT2X00_LIB_LEDS -static void rt2400pci_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - unsigned int enabled = brightness != LED_OFF; - u32 reg; - - rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); - - if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) - rt2x00_set_field32(®, LEDCSR_LINK, enabled); - else if (led->type == LED_TYPE_ACTIVITY) - rt2x00_set_field32(®, LEDCSR_ACTIVITY, enabled); - - rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); -} - -static int rt2400pci_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - u32 reg; - - rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); - rt2x00_set_field32(®, LEDCSR_ON_PERIOD, *delay_on); - rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, *delay_off); - rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); - - return 0; -} - -static void rt2400pci_init_led(struct rt2x00_dev *rt2x00dev, - struct rt2x00_led *led, - enum led_type type) -{ - led->rt2x00dev = rt2x00dev; - led->type = type; - led->led_dev.brightness_set = rt2400pci_brightness_set; - led->led_dev.blink_set = rt2400pci_blink_set; - led->flags = LED_INITIALIZED; -} -#endif /* CONFIG_RT2X00_LIB_LEDS */ - -/* - * Configuration handlers. - */ -static void rt2400pci_config_filter(struct rt2x00_dev *rt2x00dev, - const unsigned int filter_flags) -{ - u32 reg; - - /* - * Start configuration steps. - * Note that the version error will always be dropped - * since there is no filter for it at this time. - */ - rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); - rt2x00_set_field32(®, RXCSR0_DROP_CRC, - !(filter_flags & FIF_FCSFAIL)); - rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, - !(filter_flags & FIF_PLCPFAIL)); - rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, 1); - rt2x00_set_field32(®, RXCSR0_DROP_TODS, - !rt2x00dev->intf_ap_count); - rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); - rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); -} - -static void rt2400pci_config_intf(struct rt2x00_dev *rt2x00dev, - struct rt2x00_intf *intf, - struct rt2x00intf_conf *conf, - const unsigned int flags) -{ - unsigned int bcn_preload; - u32 reg; - - if (flags & CONFIG_UPDATE_TYPE) { - /* - * Enable beacon config - */ - bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); - rt2x00mmio_register_read(rt2x00dev, BCNCSR1, ®); - rt2x00_set_field32(®, BCNCSR1_PRELOAD, bcn_preload); - rt2x00mmio_register_write(rt2x00dev, BCNCSR1, reg); - - /* - * Enable synchronisation. - */ - rt2x00mmio_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_SYNC, conf->sync); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - } - - if (flags & CONFIG_UPDATE_MAC) - rt2x00mmio_register_multiwrite(rt2x00dev, CSR3, - conf->mac, sizeof(conf->mac)); - - if (flags & CONFIG_UPDATE_BSSID) - rt2x00mmio_register_multiwrite(rt2x00dev, CSR5, - conf->bssid, - sizeof(conf->bssid)); -} - -static void rt2400pci_config_erp(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_erp *erp, - u32 changed) -{ - int preamble_mask; - u32 reg; - - /* - * When short preamble is enabled, we should set bit 0x08 - */ - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - preamble_mask = erp->short_preamble << 3; - - rt2x00mmio_register_read(rt2x00dev, TXCSR1, ®); - rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, 0x1ff); - rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, 0x13a); - rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); - rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); - rt2x00mmio_register_write(rt2x00dev, TXCSR1, reg); - - rt2x00mmio_register_read(rt2x00dev, ARCSR2, ®); - rt2x00_set_field32(®, ARCSR2_SIGNAL, 0x00); - rt2x00_set_field32(®, ARCSR2_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, - GET_DURATION(ACK_SIZE, 10)); - rt2x00mmio_register_write(rt2x00dev, ARCSR2, reg); - - rt2x00mmio_register_read(rt2x00dev, ARCSR3, ®); - rt2x00_set_field32(®, ARCSR3_SIGNAL, 0x01 | preamble_mask); - rt2x00_set_field32(®, ARCSR3_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, - GET_DURATION(ACK_SIZE, 20)); - rt2x00mmio_register_write(rt2x00dev, ARCSR3, reg); - - rt2x00mmio_register_read(rt2x00dev, ARCSR4, ®); - rt2x00_set_field32(®, ARCSR4_SIGNAL, 0x02 | preamble_mask); - rt2x00_set_field32(®, ARCSR4_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, - GET_DURATION(ACK_SIZE, 55)); - rt2x00mmio_register_write(rt2x00dev, ARCSR4, reg); - - rt2x00mmio_register_read(rt2x00dev, ARCSR5, ®); - rt2x00_set_field32(®, ARCSR5_SIGNAL, 0x03 | preamble_mask); - rt2x00_set_field32(®, ARCSR5_SERVICE, 0x84); - rt2x00_set_field32(®, ARCSR2_LENGTH, - GET_DURATION(ACK_SIZE, 110)); - rt2x00mmio_register_write(rt2x00dev, ARCSR5, reg); - } - - if (changed & BSS_CHANGED_BASIC_RATES) - rt2x00mmio_register_write(rt2x00dev, ARCSR1, erp->basic_rates); - - if (changed & BSS_CHANGED_ERP_SLOT) { - rt2x00mmio_register_read(rt2x00dev, CSR11, ®); - rt2x00_set_field32(®, CSR11_SLOT_TIME, erp->slot_time); - rt2x00mmio_register_write(rt2x00dev, CSR11, reg); - - rt2x00mmio_register_read(rt2x00dev, CSR18, ®); - rt2x00_set_field32(®, CSR18_SIFS, erp->sifs); - rt2x00_set_field32(®, CSR18_PIFS, erp->pifs); - rt2x00mmio_register_write(rt2x00dev, CSR18, reg); - - rt2x00mmio_register_read(rt2x00dev, CSR19, ®); - rt2x00_set_field32(®, CSR19_DIFS, erp->difs); - rt2x00_set_field32(®, CSR19_EIFS, erp->eifs); - rt2x00mmio_register_write(rt2x00dev, CSR19, reg); - } - - if (changed & BSS_CHANGED_BEACON_INT) { - rt2x00mmio_register_read(rt2x00dev, CSR12, ®); - rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, - erp->beacon_int * 16); - rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, - erp->beacon_int * 16); - rt2x00mmio_register_write(rt2x00dev, CSR12, reg); - } -} - -static void rt2400pci_config_ant(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) -{ - u8 r1; - u8 r4; - - /* - * We should never come here because rt2x00lib is supposed - * to catch this and send us the correct antenna explicitely. - */ - BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || - ant->tx == ANTENNA_SW_DIVERSITY); - - rt2400pci_bbp_read(rt2x00dev, 4, &r4); - rt2400pci_bbp_read(rt2x00dev, 1, &r1); - - /* - * Configure the TX antenna. - */ - switch (ant->tx) { - case ANTENNA_HW_DIVERSITY: - rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 1); - break; - case ANTENNA_A: - rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 0); - break; - case ANTENNA_B: - default: - rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 2); - break; - } - - /* - * Configure the RX antenna. - */ - switch (ant->rx) { - case ANTENNA_HW_DIVERSITY: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 1); - break; - case ANTENNA_A: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 0); - break; - case ANTENNA_B: - default: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 2); - break; - } - - rt2400pci_bbp_write(rt2x00dev, 4, r4); - rt2400pci_bbp_write(rt2x00dev, 1, r1); -} - -static void rt2400pci_config_channel(struct rt2x00_dev *rt2x00dev, - struct rf_channel *rf) -{ - /* - * Switch on tuning bits. - */ - rt2x00_set_field32(&rf->rf1, RF1_TUNER, 1); - rt2x00_set_field32(&rf->rf3, RF3_TUNER, 1); - - rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); - rt2400pci_rf_write(rt2x00dev, 2, rf->rf2); - rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); - - /* - * RF2420 chipset don't need any additional actions. - */ - if (rt2x00_rf(rt2x00dev, RF2420)) - return; - - /* - * For the RT2421 chipsets we need to write an invalid - * reference clock rate to activate auto_tune. - * After that we set the value back to the correct channel. - */ - rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); - rt2400pci_rf_write(rt2x00dev, 2, 0x000c2a32); - rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); - - msleep(1); - - rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); - rt2400pci_rf_write(rt2x00dev, 2, rf->rf2); - rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); - - msleep(1); - - /* - * Switch off tuning bits. - */ - rt2x00_set_field32(&rf->rf1, RF1_TUNER, 0); - rt2x00_set_field32(&rf->rf3, RF3_TUNER, 0); - - rt2400pci_rf_write(rt2x00dev, 1, rf->rf1); - rt2400pci_rf_write(rt2x00dev, 3, rf->rf3); - - /* - * Clear false CRC during channel switch. - */ - rt2x00mmio_register_read(rt2x00dev, CNT0, &rf->rf1); -} - -static void rt2400pci_config_txpower(struct rt2x00_dev *rt2x00dev, int txpower) -{ - rt2400pci_bbp_write(rt2x00dev, 3, TXPOWER_TO_DEV(txpower)); -} - -static void rt2400pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, CSR11, ®); - rt2x00_set_field32(®, CSR11_LONG_RETRY, - libconf->conf->long_frame_max_tx_count); - rt2x00_set_field32(®, CSR11_SHORT_RETRY, - libconf->conf->short_frame_max_tx_count); - rt2x00mmio_register_write(rt2x00dev, CSR11, reg); -} - -static void rt2400pci_config_ps(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - enum dev_state state = - (libconf->conf->flags & IEEE80211_CONF_PS) ? - STATE_SLEEP : STATE_AWAKE; - u32 reg; - - if (state == STATE_SLEEP) { - rt2x00mmio_register_read(rt2x00dev, CSR20, ®); - rt2x00_set_field32(®, CSR20_DELAY_AFTER_TBCN, - (rt2x00dev->beacon_int - 20) * 16); - rt2x00_set_field32(®, CSR20_TBCN_BEFORE_WAKEUP, - libconf->conf->listen_interval - 1); - - /* We must first disable autowake before it can be enabled */ - rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); - rt2x00mmio_register_write(rt2x00dev, CSR20, reg); - - rt2x00_set_field32(®, CSR20_AUTOWAKE, 1); - rt2x00mmio_register_write(rt2x00dev, CSR20, reg); - } else { - rt2x00mmio_register_read(rt2x00dev, CSR20, ®); - rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); - rt2x00mmio_register_write(rt2x00dev, CSR20, reg); - } - - rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); -} - -static void rt2400pci_config(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf, - const unsigned int flags) -{ - if (flags & IEEE80211_CONF_CHANGE_CHANNEL) - rt2400pci_config_channel(rt2x00dev, &libconf->rf); - if (flags & IEEE80211_CONF_CHANGE_POWER) - rt2400pci_config_txpower(rt2x00dev, - libconf->conf->power_level); - if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) - rt2400pci_config_retry_limit(rt2x00dev, libconf); - if (flags & IEEE80211_CONF_CHANGE_PS) - rt2400pci_config_ps(rt2x00dev, libconf); -} - -static void rt2400pci_config_cw(struct rt2x00_dev *rt2x00dev, - const int cw_min, const int cw_max) -{ - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, CSR11, ®); - rt2x00_set_field32(®, CSR11_CWMIN, cw_min); - rt2x00_set_field32(®, CSR11_CWMAX, cw_max); - rt2x00mmio_register_write(rt2x00dev, CSR11, reg); -} - -/* - * Link tuning - */ -static void rt2400pci_link_stats(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - u32 reg; - u8 bbp; - - /* - * Update FCS error count from register. - */ - rt2x00mmio_register_read(rt2x00dev, CNT0, ®); - qual->rx_failed = rt2x00_get_field32(reg, CNT0_FCS_ERROR); - - /* - * Update False CCA count from register. - */ - rt2400pci_bbp_read(rt2x00dev, 39, &bbp); - qual->false_cca = bbp; -} - -static inline void rt2400pci_set_vgc(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, u8 vgc_level) -{ - if (qual->vgc_level_reg != vgc_level) { - rt2400pci_bbp_write(rt2x00dev, 13, vgc_level); - qual->vgc_level = vgc_level; - qual->vgc_level_reg = vgc_level; - } -} - -static void rt2400pci_reset_tuner(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - rt2400pci_set_vgc(rt2x00dev, qual, 0x08); -} - -static void rt2400pci_link_tuner(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, const u32 count) -{ - /* - * The link tuner should not run longer then 60 seconds, - * and should run once every 2 seconds. - */ - if (count > 60 || !(count & 1)) - return; - - /* - * Base r13 link tuning on the false cca count. - */ - if ((qual->false_cca > 512) && (qual->vgc_level < 0x20)) - rt2400pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level); - else if ((qual->false_cca < 100) && (qual->vgc_level > 0x08)) - rt2400pci_set_vgc(rt2x00dev, qual, --qual->vgc_level); -} - -/* - * Queue handlers. - */ -static void rt2400pci_start_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); - rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 0); - rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); - rt2x00_set_field32(®, CSR14_TBCN, 1); - rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - break; - default: - break; - } -} - -static void rt2400pci_kick_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_AC_VO: - rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_KICK_PRIO, 1); - rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); - break; - case QID_AC_VI: - rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_KICK_TX, 1); - rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); - break; - case QID_ATIM: - rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_KICK_ATIM, 1); - rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); - break; - default: - break; - } -} - -static void rt2400pci_stop_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_AC_VO: - case QID_AC_VI: - case QID_ATIM: - rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_ABORT, 1); - rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); - break; - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); - rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 1); - rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); - rt2x00_set_field32(®, CSR14_TBCN, 0); - rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - - /* - * Wait for possibly running tbtt tasklets. - */ - tasklet_kill(&rt2x00dev->tbtt_tasklet); - break; - default: - break; - } -} - -/* - * Initialization functions. - */ -static bool rt2400pci_get_entry_state(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 0, &word); - - return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); - } else { - rt2x00_desc_read(entry_priv->desc, 0, &word); - - return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || - rt2x00_get_field32(word, TXD_W0_VALID)); - } -} - -static void rt2400pci_clear_entry(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 2, &word); - rt2x00_set_field32(&word, RXD_W2_BUFFER_LENGTH, entry->skb->len); - rt2x00_desc_write(entry_priv->desc, 2, word); - - rt2x00_desc_read(entry_priv->desc, 1, &word); - rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); - rt2x00_desc_write(entry_priv->desc, 1, word); - - rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); - rt2x00_desc_write(entry_priv->desc, 0, word); - } else { - rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_set_field32(&word, TXD_W0_VALID, 0); - rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); - rt2x00_desc_write(entry_priv->desc, 0, word); - } -} - -static int rt2400pci_init_queues(struct rt2x00_dev *rt2x00dev) -{ - struct queue_entry_priv_mmio *entry_priv; - u32 reg; - - /* - * Initialize registers. - */ - rt2x00mmio_register_read(rt2x00dev, TXCSR2, ®); - rt2x00_set_field32(®, TXCSR2_TXD_SIZE, rt2x00dev->tx[0].desc_size); - rt2x00_set_field32(®, TXCSR2_NUM_TXD, rt2x00dev->tx[1].limit); - rt2x00_set_field32(®, TXCSR2_NUM_ATIM, rt2x00dev->atim->limit); - rt2x00_set_field32(®, TXCSR2_NUM_PRIO, rt2x00dev->tx[0].limit); - rt2x00mmio_register_write(rt2x00dev, TXCSR2, reg); - - entry_priv = rt2x00dev->tx[1].entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, TXCSR3, ®); - rt2x00_set_field32(®, TXCSR3_TX_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TXCSR3, reg); - - entry_priv = rt2x00dev->tx[0].entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, TXCSR5, ®); - rt2x00_set_field32(®, TXCSR5_PRIO_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TXCSR5, reg); - - entry_priv = rt2x00dev->atim->entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, TXCSR4, ®); - rt2x00_set_field32(®, TXCSR4_ATIM_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TXCSR4, reg); - - entry_priv = rt2x00dev->bcn->entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, TXCSR6, ®); - rt2x00_set_field32(®, TXCSR6_BEACON_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TXCSR6, reg); - - rt2x00mmio_register_read(rt2x00dev, RXCSR1, ®); - rt2x00_set_field32(®, RXCSR1_RXD_SIZE, rt2x00dev->rx->desc_size); - rt2x00_set_field32(®, RXCSR1_NUM_RXD, rt2x00dev->rx->limit); - rt2x00mmio_register_write(rt2x00dev, RXCSR1, reg); - - entry_priv = rt2x00dev->rx->entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, RXCSR2, ®); - rt2x00_set_field32(®, RXCSR2_RX_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, RXCSR2, reg); - - return 0; -} - -static int rt2400pci_init_registers(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00mmio_register_write(rt2x00dev, PSCSR0, 0x00020002); - rt2x00mmio_register_write(rt2x00dev, PSCSR1, 0x00000002); - rt2x00mmio_register_write(rt2x00dev, PSCSR2, 0x00023f20); - rt2x00mmio_register_write(rt2x00dev, PSCSR3, 0x00000002); - - rt2x00mmio_register_read(rt2x00dev, TIMECSR, ®); - rt2x00_set_field32(®, TIMECSR_US_COUNT, 33); - rt2x00_set_field32(®, TIMECSR_US_64_COUNT, 63); - rt2x00_set_field32(®, TIMECSR_BEACON_EXPECT, 0); - rt2x00mmio_register_write(rt2x00dev, TIMECSR, reg); - - rt2x00mmio_register_read(rt2x00dev, CSR9, ®); - rt2x00_set_field32(®, CSR9_MAX_FRAME_UNIT, - (rt2x00dev->rx->data_size / 128)); - rt2x00mmio_register_write(rt2x00dev, CSR9, reg); - - rt2x00mmio_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); - rt2x00_set_field32(®, CSR14_TSF_SYNC, 0); - rt2x00_set_field32(®, CSR14_TBCN, 0); - rt2x00_set_field32(®, CSR14_TCFP, 0); - rt2x00_set_field32(®, CSR14_TATIMW, 0); - rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); - rt2x00_set_field32(®, CSR14_CFP_COUNT_PRELOAD, 0); - rt2x00_set_field32(®, CSR14_TBCM_PRELOAD, 0); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - - rt2x00mmio_register_write(rt2x00dev, CNT3, 0x3f080000); - - rt2x00mmio_register_read(rt2x00dev, ARCSR0, ®); - rt2x00_set_field32(®, ARCSR0_AR_BBP_DATA0, 133); - rt2x00_set_field32(®, ARCSR0_AR_BBP_ID0, 134); - rt2x00_set_field32(®, ARCSR0_AR_BBP_DATA1, 136); - rt2x00_set_field32(®, ARCSR0_AR_BBP_ID1, 135); - rt2x00mmio_register_write(rt2x00dev, ARCSR0, reg); - - rt2x00mmio_register_read(rt2x00dev, RXCSR3, ®); - rt2x00_set_field32(®, RXCSR3_BBP_ID0, 3); /* Tx power.*/ - rt2x00_set_field32(®, RXCSR3_BBP_ID0_VALID, 1); - rt2x00_set_field32(®, RXCSR3_BBP_ID1, 32); /* Signal */ - rt2x00_set_field32(®, RXCSR3_BBP_ID1_VALID, 1); - rt2x00_set_field32(®, RXCSR3_BBP_ID2, 36); /* Rssi */ - rt2x00_set_field32(®, RXCSR3_BBP_ID2_VALID, 1); - rt2x00mmio_register_write(rt2x00dev, RXCSR3, reg); - - rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); - - if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) - return -EBUSY; - - rt2x00mmio_register_write(rt2x00dev, MACCSR0, 0x00217223); - rt2x00mmio_register_write(rt2x00dev, MACCSR1, 0x00235518); - - rt2x00mmio_register_read(rt2x00dev, MACCSR2, ®); - rt2x00_set_field32(®, MACCSR2_DELAY, 64); - rt2x00mmio_register_write(rt2x00dev, MACCSR2, reg); - - rt2x00mmio_register_read(rt2x00dev, RALINKCSR, ®); - rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA0, 17); - rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID0, 154); - rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA1, 0); - rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID1, 154); - rt2x00mmio_register_write(rt2x00dev, RALINKCSR, reg); - - rt2x00mmio_register_read(rt2x00dev, CSR1, ®); - rt2x00_set_field32(®, CSR1_SOFT_RESET, 1); - rt2x00_set_field32(®, CSR1_BBP_RESET, 0); - rt2x00_set_field32(®, CSR1_HOST_READY, 0); - rt2x00mmio_register_write(rt2x00dev, CSR1, reg); - - rt2x00mmio_register_read(rt2x00dev, CSR1, ®); - rt2x00_set_field32(®, CSR1_SOFT_RESET, 0); - rt2x00_set_field32(®, CSR1_HOST_READY, 1); - rt2x00mmio_register_write(rt2x00dev, CSR1, reg); - - /* - * We must clear the FCS and FIFO error count. - * These registers are cleared on read, - * so we may pass a useless variable to store the value. - */ - rt2x00mmio_register_read(rt2x00dev, CNT0, ®); - rt2x00mmio_register_read(rt2x00dev, CNT4, ®); - - return 0; -} - -static int rt2400pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u8 value; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2400pci_bbp_read(rt2x00dev, 0, &value); - if ((value != 0xff) && (value != 0x00)) - return 0; - udelay(REGISTER_BUSY_DELAY); - } - - rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); - return -EACCES; -} - -static int rt2400pci_init_bbp(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u16 eeprom; - u8 reg_id; - u8 value; - - if (unlikely(rt2400pci_wait_bbp_ready(rt2x00dev))) - return -EACCES; - - rt2400pci_bbp_write(rt2x00dev, 1, 0x00); - rt2400pci_bbp_write(rt2x00dev, 3, 0x27); - rt2400pci_bbp_write(rt2x00dev, 4, 0x08); - rt2400pci_bbp_write(rt2x00dev, 10, 0x0f); - rt2400pci_bbp_write(rt2x00dev, 15, 0x72); - rt2400pci_bbp_write(rt2x00dev, 16, 0x74); - rt2400pci_bbp_write(rt2x00dev, 17, 0x20); - rt2400pci_bbp_write(rt2x00dev, 18, 0x72); - rt2400pci_bbp_write(rt2x00dev, 19, 0x0b); - rt2400pci_bbp_write(rt2x00dev, 20, 0x00); - rt2400pci_bbp_write(rt2x00dev, 28, 0x11); - rt2400pci_bbp_write(rt2x00dev, 29, 0x04); - rt2400pci_bbp_write(rt2x00dev, 30, 0x21); - rt2400pci_bbp_write(rt2x00dev, 31, 0x00); - - for (i = 0; i < EEPROM_BBP_SIZE; i++) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); - - if (eeprom != 0xffff && eeprom != 0x0000) { - reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); - value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - rt2400pci_bbp_write(rt2x00dev, reg_id, value); - } - } - - return 0; -} - -/* - * Device state switch handlers. - */ -static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - int mask = (state == STATE_RADIO_IRQ_OFF); - u32 reg; - unsigned long flags; - - /* - * When interrupts are being enabled, the interrupt registers - * should clear the register to assure a clean state. - */ - if (state == STATE_RADIO_IRQ_ON) { - rt2x00mmio_register_read(rt2x00dev, CSR7, ®); - rt2x00mmio_register_write(rt2x00dev, CSR7, reg); - } - - /* - * Only toggle the interrupts bits we are going to use. - * Non-checked interrupt bits are disabled by default. - */ - spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); - - rt2x00mmio_register_read(rt2x00dev, CSR8, ®); - rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); - rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); - rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, mask); - rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); - rt2x00_set_field32(®, CSR8_RXDONE, mask); - rt2x00mmio_register_write(rt2x00dev, CSR8, reg); - - spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); - - if (state == STATE_RADIO_IRQ_OFF) { - /* - * Ensure that all tasklets are finished before - * disabling the interrupts. - */ - tasklet_kill(&rt2x00dev->txstatus_tasklet); - tasklet_kill(&rt2x00dev->rxdone_tasklet); - tasklet_kill(&rt2x00dev->tbtt_tasklet); - } -} - -static int rt2400pci_enable_radio(struct rt2x00_dev *rt2x00dev) -{ - /* - * Initialize all registers. - */ - if (unlikely(rt2400pci_init_queues(rt2x00dev) || - rt2400pci_init_registers(rt2x00dev) || - rt2400pci_init_bbp(rt2x00dev))) - return -EIO; - - return 0; -} - -static void rt2400pci_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - /* - * Disable power - */ - rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0); -} - -static int rt2400pci_set_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - u32 reg, reg2; - unsigned int i; - char put_to_sleep; - char bbp_state; - char rf_state; - - put_to_sleep = (state != STATE_AWAKE); - - rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®); - rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1); - rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state); - rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state); - rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep); - rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); - - /* - * Device is not guaranteed to be in the requested state yet. - * We must wait until the register indicates that the - * device has entered the correct state. - */ - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®2); - bbp_state = rt2x00_get_field32(reg2, PWRCSR1_BBP_CURR_STATE); - rf_state = rt2x00_get_field32(reg2, PWRCSR1_RF_CURR_STATE); - if (bbp_state == state && rf_state == state) - return 0; - rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); - msleep(10); - } - - return -EBUSY; -} - -static int rt2400pci_set_device_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - int retval = 0; - - switch (state) { - case STATE_RADIO_ON: - retval = rt2400pci_enable_radio(rt2x00dev); - break; - case STATE_RADIO_OFF: - rt2400pci_disable_radio(rt2x00dev); - break; - case STATE_RADIO_IRQ_ON: - case STATE_RADIO_IRQ_OFF: - rt2400pci_toggle_irq(rt2x00dev, state); - break; - case STATE_DEEP_SLEEP: - case STATE_SLEEP: - case STATE_STANDBY: - case STATE_AWAKE: - retval = rt2400pci_set_state(rt2x00dev, state); - break; - default: - retval = -ENOTSUPP; - break; - } - - if (unlikely(retval)) - rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", - state, retval); - - return retval; -} - -/* - * TX descriptor initialization - */ -static void rt2400pci_write_tx_desc(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - __le32 *txd = entry_priv->desc; - u32 word; - - /* - * Start writing the descriptor words. - */ - rt2x00_desc_read(txd, 1, &word); - rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); - rt2x00_desc_write(txd, 1, word); - - rt2x00_desc_read(txd, 2, &word); - rt2x00_set_field32(&word, TXD_W2_BUFFER_LENGTH, txdesc->length); - rt2x00_set_field32(&word, TXD_W2_DATABYTE_COUNT, txdesc->length); - rt2x00_desc_write(txd, 2, word); - - rt2x00_desc_read(txd, 3, &word); - rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, txdesc->u.plcp.signal); - rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL_REGNUM, 5); - rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL_BUSY, 1); - rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, txdesc->u.plcp.service); - rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE_REGNUM, 6); - rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE_BUSY, 1); - rt2x00_desc_write(txd, 3, word); - - rt2x00_desc_read(txd, 4, &word); - rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_LOW, - txdesc->u.plcp.length_low); - rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW_REGNUM, 8); - rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW_BUSY, 1); - rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_HIGH, - txdesc->u.plcp.length_high); - rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH_REGNUM, 7); - rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH_BUSY, 1); - rt2x00_desc_write(txd, 4, word); - - /* - * Writing TXD word 0 must the last to prevent a race condition with - * the device, whereby the device may take hold of the TXD before we - * finished updating it. - */ - rt2x00_desc_read(txd, 0, &word); - rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); - rt2x00_set_field32(&word, TXD_W0_VALID, 1); - rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, - test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_ACK, - test_bit(ENTRY_TXD_ACK, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, - test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_RTS, - test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); - rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, - test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); - rt2x00_desc_write(txd, 0, word); - - /* - * Register descriptor details in skb frame descriptor. - */ - skbdesc->desc = txd; - skbdesc->desc_len = TXD_DESC_SIZE; -} - -/* - * TX data initialization - */ -static void rt2400pci_write_beacon(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - u32 reg; - - /* - * Disable beaconing while we are reloading the beacon data, - * otherwise we might be sending out invalid data. - */ - rt2x00mmio_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - - if (rt2x00queue_map_txskb(entry)) { - rt2x00_err(rt2x00dev, "Fail to map beacon, aborting\n"); - goto out; - } - /* - * Enable beaconing again. - */ - rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); - /* - * Write the TX descriptor for the beacon. - */ - rt2400pci_write_tx_desc(entry, txdesc); - - /* - * Dump beacon to userspace through debugfs. - */ - rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); -out: - /* - * Enable beaconing again. - */ - rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); -} - -/* - * RX control handlers - */ -static void rt2400pci_fill_rxdone(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - u32 word0; - u32 word2; - u32 word3; - u32 word4; - u64 tsf; - u32 rx_low; - u32 rx_high; - - rt2x00_desc_read(entry_priv->desc, 0, &word0); - rt2x00_desc_read(entry_priv->desc, 2, &word2); - rt2x00_desc_read(entry_priv->desc, 3, &word3); - rt2x00_desc_read(entry_priv->desc, 4, &word4); - - if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; - if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; - - /* - * We only get the lower 32bits from the timestamp, - * to get the full 64bits we must complement it with - * the timestamp from get_tsf(). - * Note that when a wraparound of the lower 32bits - * has occurred between the frame arrival and the get_tsf() - * call, we must decrease the higher 32bits with 1 to get - * to correct value. - */ - tsf = rt2x00dev->ops->hw->get_tsf(rt2x00dev->hw, NULL); - rx_low = rt2x00_get_field32(word4, RXD_W4_RX_END_TIME); - rx_high = upper_32_bits(tsf); - - if ((u32)tsf <= rx_low) - rx_high--; - - /* - * Obtain the status about this packet. - * The signal is the PLCP value, and needs to be stripped - * of the preamble bit (0x08). - */ - rxdesc->timestamp = ((u64)rx_high << 32) | rx_low; - rxdesc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL) & ~0x08; - rxdesc->rssi = rt2x00_get_field32(word3, RXD_W3_RSSI) - - entry->queue->rt2x00dev->rssi_offset; - rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); - - rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; - if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) - rxdesc->dev_flags |= RXDONE_MY_BSS; -} - -/* - * Interrupt functions. - */ -static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev, - const enum data_queue_qid queue_idx) -{ - struct data_queue *queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); - struct queue_entry_priv_mmio *entry_priv; - struct queue_entry *entry; - struct txdone_entry_desc txdesc; - u32 word; - - while (!rt2x00queue_empty(queue)) { - entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - entry_priv = entry->priv_data; - rt2x00_desc_read(entry_priv->desc, 0, &word); - - if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || - !rt2x00_get_field32(word, TXD_W0_VALID)) - break; - - /* - * Obtain the status about this packet. - */ - txdesc.flags = 0; - switch (rt2x00_get_field32(word, TXD_W0_RESULT)) { - case 0: /* Success */ - case 1: /* Success with retry */ - __set_bit(TXDONE_SUCCESS, &txdesc.flags); - break; - case 2: /* Failure, excessive retries */ - __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); - /* Don't break, this is a failed frame! */ - default: /* Failure */ - __set_bit(TXDONE_FAILURE, &txdesc.flags); - } - txdesc.retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); - - rt2x00lib_txdone(entry, &txdesc); - } -} - -static inline void rt2400pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, - struct rt2x00_field32 irq_field) -{ - u32 reg; - - /* - * Enable a single interrupt. The interrupt mask register - * access needs locking. - */ - spin_lock_irq(&rt2x00dev->irqmask_lock); - - rt2x00mmio_register_read(rt2x00dev, CSR8, ®); - rt2x00_set_field32(®, irq_field, 0); - rt2x00mmio_register_write(rt2x00dev, CSR8, reg); - - spin_unlock_irq(&rt2x00dev->irqmask_lock); -} - -static void rt2400pci_txstatus_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - u32 reg; - - /* - * Handle all tx queues. - */ - rt2400pci_txdone(rt2x00dev, QID_ATIM); - rt2400pci_txdone(rt2x00dev, QID_AC_VO); - rt2400pci_txdone(rt2x00dev, QID_AC_VI); - - /* - * Enable all TXDONE interrupts again. - */ - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) { - spin_lock_irq(&rt2x00dev->irqmask_lock); - - rt2x00mmio_register_read(rt2x00dev, CSR8, ®); - rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); - rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); - rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); - rt2x00mmio_register_write(rt2x00dev, CSR8, reg); - - spin_unlock_irq(&rt2x00dev->irqmask_lock); - } -} - -static void rt2400pci_tbtt_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - rt2x00lib_beacondone(rt2x00dev); - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2400pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); -} - -static void rt2400pci_rxdone_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - if (rt2x00mmio_rxdone(rt2x00dev)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2400pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); -} - -static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance) -{ - struct rt2x00_dev *rt2x00dev = dev_instance; - u32 reg, mask; - - /* - * Get the interrupt sources & saved to local variable. - * Write register value back to clear pending interrupts. - */ - rt2x00mmio_register_read(rt2x00dev, CSR7, ®); - rt2x00mmio_register_write(rt2x00dev, CSR7, reg); - - if (!reg) - return IRQ_NONE; - - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return IRQ_HANDLED; - - mask = reg; - - /* - * Schedule tasklets for interrupt handling. - */ - if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) - tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); - - if (rt2x00_get_field32(reg, CSR7_RXDONE)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - - if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) || - rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) || - rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) { - tasklet_schedule(&rt2x00dev->txstatus_tasklet); - /* - * Mask out all txdone interrupts. - */ - rt2x00_set_field32(&mask, CSR8_TXDONE_TXRING, 1); - rt2x00_set_field32(&mask, CSR8_TXDONE_ATIMRING, 1); - rt2x00_set_field32(&mask, CSR8_TXDONE_PRIORING, 1); - } - - /* - * Disable all interrupts for which a tasklet was scheduled right now, - * the tasklet will reenable the appropriate interrupts. - */ - spin_lock(&rt2x00dev->irqmask_lock); - - rt2x00mmio_register_read(rt2x00dev, CSR8, ®); - reg |= mask; - rt2x00mmio_register_write(rt2x00dev, CSR8, reg); - - spin_unlock(&rt2x00dev->irqmask_lock); - - - - return IRQ_HANDLED; -} - -/* - * Device probe functions. - */ -static int rt2400pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) -{ - struct eeprom_93cx6 eeprom; - u32 reg; - u16 word; - u8 *mac; - - rt2x00mmio_register_read(rt2x00dev, CSR21, ®); - - eeprom.data = rt2x00dev; - eeprom.register_read = rt2400pci_eepromregister_read; - eeprom.register_write = rt2400pci_eepromregister_write; - eeprom.width = rt2x00_get_field32(reg, CSR21_TYPE_93C46) ? - PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; - eeprom.reg_data_in = 0; - eeprom.reg_data_out = 0; - eeprom.reg_data_clock = 0; - eeprom.reg_chip_select = 0; - - eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, - EEPROM_SIZE / sizeof(u16)); - - /* - * Start validation of the data that has been read. - */ - mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); - if (word == 0xffff) { - rt2x00_err(rt2x00dev, "Invalid EEPROM data detected\n"); - return -EINVAL; - } - - return 0; -} - -static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - u16 value; - u16 eeprom; - - /* - * Read EEPROM word for configuration. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); - - /* - * Identify RF chipset. - */ - value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); - rt2x00mmio_register_read(rt2x00dev, CSR0, ®); - rt2x00_set_chip(rt2x00dev, RT2460, value, - rt2x00_get_field32(reg, CSR0_REVISION)); - - if (!rt2x00_rf(rt2x00dev, RF2420) && !rt2x00_rf(rt2x00dev, RF2421)) { - rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); - return -ENODEV; - } - - /* - * Identify default antenna configuration. - */ - rt2x00dev->default_ant.tx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); - rt2x00dev->default_ant.rx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); - - /* - * When the eeprom indicates SW_DIVERSITY use HW_DIVERSITY instead. - * I am not 100% sure about this, but the legacy drivers do not - * indicate antenna swapping in software is required when - * diversity is enabled. - */ - if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY) - rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY; - if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY) - rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY; - - /* - * Store led mode, for correct led behaviour. - */ -#ifdef CONFIG_RT2X00_LIB_LEDS - value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); - - rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); - if (value == LED_MODE_TXRX_ACTIVITY || - value == LED_MODE_DEFAULT || - value == LED_MODE_ASUS) - rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_qual, - LED_TYPE_ACTIVITY); -#endif /* CONFIG_RT2X00_LIB_LEDS */ - - /* - * Detect if this device has an hardware controlled radio. - */ - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) - __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); - - /* - * Check if the BBP tuning should be enabled. - */ - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_AGCVGC_TUNING)) - __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); - - return 0; -} - -/* - * RF value list for RF2420 & RF2421 - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_b[] = { - { 1, 0x00022058, 0x000c1fda, 0x00000101, 0 }, - { 2, 0x00022058, 0x000c1fee, 0x00000101, 0 }, - { 3, 0x00022058, 0x000c2002, 0x00000101, 0 }, - { 4, 0x00022058, 0x000c2016, 0x00000101, 0 }, - { 5, 0x00022058, 0x000c202a, 0x00000101, 0 }, - { 6, 0x00022058, 0x000c203e, 0x00000101, 0 }, - { 7, 0x00022058, 0x000c2052, 0x00000101, 0 }, - { 8, 0x00022058, 0x000c2066, 0x00000101, 0 }, - { 9, 0x00022058, 0x000c207a, 0x00000101, 0 }, - { 10, 0x00022058, 0x000c208e, 0x00000101, 0 }, - { 11, 0x00022058, 0x000c20a2, 0x00000101, 0 }, - { 12, 0x00022058, 0x000c20b6, 0x00000101, 0 }, - { 13, 0x00022058, 0x000c20ca, 0x00000101, 0 }, - { 14, 0x00022058, 0x000c20fa, 0x00000101, 0 }, -}; - -static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) -{ - struct hw_mode_spec *spec = &rt2x00dev->spec; - struct channel_info *info; - char *tx_power; - unsigned int i; - - /* - * Initialize all hw fields. - */ - ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); - ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); - ieee80211_hw_set(rt2x00dev->hw, HOST_BROADCAST_PS_BUFFERING); - ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); - - SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); - SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, - rt2x00_eeprom_addr(rt2x00dev, - EEPROM_MAC_ADDR_0)); - - /* - * Initialize hw_mode information. - */ - spec->supported_bands = SUPPORT_BAND_2GHZ; - spec->supported_rates = SUPPORT_RATE_CCK; - - spec->num_channels = ARRAY_SIZE(rf_vals_b); - spec->channels = rf_vals_b; - - /* - * Create channel information array - */ - info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - spec->channels_info = info; - - tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); - for (i = 0; i < 14; i++) { - info[i].max_power = TXPOWER_FROM_DEV(MAX_TXPOWER); - info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); - } - - return 0; -} - -static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev) -{ - int retval; - u32 reg; - - /* - * Allocate eeprom data. - */ - retval = rt2400pci_validate_eeprom(rt2x00dev); - if (retval) - return retval; - - retval = rt2400pci_init_eeprom(rt2x00dev); - if (retval) - return retval; - - /* - * Enable rfkill polling by setting GPIO direction of the - * rfkill switch GPIO pin correctly. - */ - rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); - rt2x00_set_field32(®, GPIOCSR_DIR0, 1); - rt2x00mmio_register_write(rt2x00dev, GPIOCSR, reg); - - /* - * Initialize hw specifications. - */ - retval = rt2400pci_probe_hw_mode(rt2x00dev); - if (retval) - return retval; - - /* - * This device requires the atim queue and DMA-mapped skbs. - */ - __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); - - /* - * Set the rssi offset. - */ - rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; - - return 0; -} - -/* - * IEEE80211 stack callback functions. - */ -static int rt2400pci_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - - /* - * We don't support variating cw_min and cw_max variables - * per queue. So by default we only configure the TX queue, - * and ignore all other configurations. - */ - if (queue != 0) - return -EINVAL; - - if (rt2x00mac_conf_tx(hw, vif, queue, params)) - return -EINVAL; - - /* - * Write configuration to register. - */ - rt2400pci_config_cw(rt2x00dev, - rt2x00dev->tx->cw_min, rt2x00dev->tx->cw_max); - - return 0; -} - -static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u64 tsf; - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, CSR17, ®); - tsf = (u64) rt2x00_get_field32(reg, CSR17_HIGH_TSFTIMER) << 32; - rt2x00mmio_register_read(rt2x00dev, CSR16, ®); - tsf |= rt2x00_get_field32(reg, CSR16_LOW_TSFTIMER); - - return tsf; -} - -static int rt2400pci_tx_last_beacon(struct ieee80211_hw *hw) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, CSR15, ®); - return rt2x00_get_field32(reg, CSR15_BEACON_SENT); -} - -static const struct ieee80211_ops rt2400pci_mac80211_ops = { - .tx = rt2x00mac_tx, - .start = rt2x00mac_start, - .stop = rt2x00mac_stop, - .add_interface = rt2x00mac_add_interface, - .remove_interface = rt2x00mac_remove_interface, - .config = rt2x00mac_config, - .configure_filter = rt2x00mac_configure_filter, - .sw_scan_start = rt2x00mac_sw_scan_start, - .sw_scan_complete = rt2x00mac_sw_scan_complete, - .get_stats = rt2x00mac_get_stats, - .bss_info_changed = rt2x00mac_bss_info_changed, - .conf_tx = rt2400pci_conf_tx, - .get_tsf = rt2400pci_get_tsf, - .tx_last_beacon = rt2400pci_tx_last_beacon, - .rfkill_poll = rt2x00mac_rfkill_poll, - .flush = rt2x00mac_flush, - .set_antenna = rt2x00mac_set_antenna, - .get_antenna = rt2x00mac_get_antenna, - .get_ringparam = rt2x00mac_get_ringparam, - .tx_frames_pending = rt2x00mac_tx_frames_pending, -}; - -static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { - .irq_handler = rt2400pci_interrupt, - .txstatus_tasklet = rt2400pci_txstatus_tasklet, - .tbtt_tasklet = rt2400pci_tbtt_tasklet, - .rxdone_tasklet = rt2400pci_rxdone_tasklet, - .probe_hw = rt2400pci_probe_hw, - .initialize = rt2x00mmio_initialize, - .uninitialize = rt2x00mmio_uninitialize, - .get_entry_state = rt2400pci_get_entry_state, - .clear_entry = rt2400pci_clear_entry, - .set_device_state = rt2400pci_set_device_state, - .rfkill_poll = rt2400pci_rfkill_poll, - .link_stats = rt2400pci_link_stats, - .reset_tuner = rt2400pci_reset_tuner, - .link_tuner = rt2400pci_link_tuner, - .start_queue = rt2400pci_start_queue, - .kick_queue = rt2400pci_kick_queue, - .stop_queue = rt2400pci_stop_queue, - .flush_queue = rt2x00mmio_flush_queue, - .write_tx_desc = rt2400pci_write_tx_desc, - .write_beacon = rt2400pci_write_beacon, - .fill_rxdone = rt2400pci_fill_rxdone, - .config_filter = rt2400pci_config_filter, - .config_intf = rt2400pci_config_intf, - .config_erp = rt2400pci_config_erp, - .config_ant = rt2400pci_config_ant, - .config = rt2400pci_config, -}; - -static void rt2400pci_queue_init(struct data_queue *queue) -{ - switch (queue->qid) { - case QID_RX: - queue->limit = 24; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = RXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - queue->limit = 24; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_BEACON: - queue->limit = 1; - queue->data_size = MGMT_FRAME_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_ATIM: - queue->limit = 8; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - default: - BUG(); - break; - } -} - -static const struct rt2x00_ops rt2400pci_ops = { - .name = KBUILD_MODNAME, - .max_ap_intf = 1, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .queue_init = rt2400pci_queue_init, - .lib = &rt2400pci_rt2x00_ops, - .hw = &rt2400pci_mac80211_ops, -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2400pci_rt2x00debug, -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -}; - -/* - * RT2400pci module information. - */ -static const struct pci_device_id rt2400pci_device_table[] = { - { PCI_DEVICE(0x1814, 0x0101) }, - { 0, } -}; - - -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("Ralink RT2400 PCI & PCMCIA Wireless LAN driver."); -MODULE_SUPPORTED_DEVICE("Ralink RT2460 PCI & PCMCIA chipset based cards"); -MODULE_DEVICE_TABLE(pci, rt2400pci_device_table); -MODULE_LICENSE("GPL"); - -static int rt2400pci_probe(struct pci_dev *pci_dev, - const struct pci_device_id *id) -{ - return rt2x00pci_probe(pci_dev, &rt2400pci_ops); -} - -static struct pci_driver rt2400pci_driver = { - .name = KBUILD_MODNAME, - .id_table = rt2400pci_device_table, - .probe = rt2400pci_probe, - .remove = rt2x00pci_remove, - .suspend = rt2x00pci_suspend, - .resume = rt2x00pci_resume, -}; - -module_pci_driver(rt2400pci_driver); diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/rt2x00/rt2400pci.h deleted file mode 100644 index 0fd3a9d01a60..000000000000 --- a/drivers/net/wireless/rt2x00/rt2400pci.h +++ /dev/null @@ -1,961 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2400pci - Abstract: Data structures and registers for the rt2400pci module. - Supported chipsets: RT2460. - */ - -#ifndef RT2400PCI_H -#define RT2400PCI_H - -/* - * RF chip defines. - */ -#define RF2420 0x0000 -#define RF2421 0x0001 - -/* - * Signal information. - * Default offset is required for RSSI <-> dBm conversion. - */ -#define DEFAULT_RSSI_OFFSET 100 - -/* - * Register layout information. - */ -#define CSR_REG_BASE 0x0000 -#define CSR_REG_SIZE 0x014c -#define EEPROM_BASE 0x0000 -#define EEPROM_SIZE 0x0100 -#define BBP_BASE 0x0000 -#define BBP_SIZE 0x0020 -#define RF_BASE 0x0004 -#define RF_SIZE 0x000c - -/* - * Number of TX queues. - */ -#define NUM_TX_QUEUES 2 - -/* - * Control/Status Registers(CSR). - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * CSR0: ASIC revision number. - */ -#define CSR0 0x0000 -#define CSR0_REVISION FIELD32(0x0000ffff) - -/* - * CSR1: System control register. - * SOFT_RESET: Software reset, 1: reset, 0: normal. - * BBP_RESET: Hardware reset, 1: reset, 0, release. - * HOST_READY: Host ready after initialization. - */ -#define CSR1 0x0004 -#define CSR1_SOFT_RESET FIELD32(0x00000001) -#define CSR1_BBP_RESET FIELD32(0x00000002) -#define CSR1_HOST_READY FIELD32(0x00000004) - -/* - * CSR2: System admin status register (invalid). - */ -#define CSR2 0x0008 - -/* - * CSR3: STA MAC address register 0. - */ -#define CSR3 0x000c -#define CSR3_BYTE0 FIELD32(0x000000ff) -#define CSR3_BYTE1 FIELD32(0x0000ff00) -#define CSR3_BYTE2 FIELD32(0x00ff0000) -#define CSR3_BYTE3 FIELD32(0xff000000) - -/* - * CSR4: STA MAC address register 1. - */ -#define CSR4 0x0010 -#define CSR4_BYTE4 FIELD32(0x000000ff) -#define CSR4_BYTE5 FIELD32(0x0000ff00) - -/* - * CSR5: BSSID register 0. - */ -#define CSR5 0x0014 -#define CSR5_BYTE0 FIELD32(0x000000ff) -#define CSR5_BYTE1 FIELD32(0x0000ff00) -#define CSR5_BYTE2 FIELD32(0x00ff0000) -#define CSR5_BYTE3 FIELD32(0xff000000) - -/* - * CSR6: BSSID register 1. - */ -#define CSR6 0x0018 -#define CSR6_BYTE4 FIELD32(0x000000ff) -#define CSR6_BYTE5 FIELD32(0x0000ff00) - -/* - * CSR7: Interrupt source register. - * Write 1 to clear interrupt. - * TBCN_EXPIRE: Beacon timer expired interrupt. - * TWAKE_EXPIRE: Wakeup timer expired interrupt. - * TATIMW_EXPIRE: Timer of atim window expired interrupt. - * TXDONE_TXRING: Tx ring transmit done interrupt. - * TXDONE_ATIMRING: Atim ring transmit done interrupt. - * TXDONE_PRIORING: Priority ring transmit done interrupt. - * RXDONE: Receive done interrupt. - */ -#define CSR7 0x001c -#define CSR7_TBCN_EXPIRE FIELD32(0x00000001) -#define CSR7_TWAKE_EXPIRE FIELD32(0x00000002) -#define CSR7_TATIMW_EXPIRE FIELD32(0x00000004) -#define CSR7_TXDONE_TXRING FIELD32(0x00000008) -#define CSR7_TXDONE_ATIMRING FIELD32(0x00000010) -#define CSR7_TXDONE_PRIORING FIELD32(0x00000020) -#define CSR7_RXDONE FIELD32(0x00000040) - -/* - * CSR8: Interrupt mask register. - * Write 1 to mask interrupt. - * TBCN_EXPIRE: Beacon timer expired interrupt. - * TWAKE_EXPIRE: Wakeup timer expired interrupt. - * TATIMW_EXPIRE: Timer of atim window expired interrupt. - * TXDONE_TXRING: Tx ring transmit done interrupt. - * TXDONE_ATIMRING: Atim ring transmit done interrupt. - * TXDONE_PRIORING: Priority ring transmit done interrupt. - * RXDONE: Receive done interrupt. - */ -#define CSR8 0x0020 -#define CSR8_TBCN_EXPIRE FIELD32(0x00000001) -#define CSR8_TWAKE_EXPIRE FIELD32(0x00000002) -#define CSR8_TATIMW_EXPIRE FIELD32(0x00000004) -#define CSR8_TXDONE_TXRING FIELD32(0x00000008) -#define CSR8_TXDONE_ATIMRING FIELD32(0x00000010) -#define CSR8_TXDONE_PRIORING FIELD32(0x00000020) -#define CSR8_RXDONE FIELD32(0x00000040) - -/* - * CSR9: Maximum frame length register. - * MAX_FRAME_UNIT: Maximum frame length in 128b unit, default: 12. - */ -#define CSR9 0x0024 -#define CSR9_MAX_FRAME_UNIT FIELD32(0x00000f80) - -/* - * CSR11: Back-off control register. - * CWMIN: CWmin. Default cwmin is 31 (2^5 - 1). - * CWMAX: CWmax. Default cwmax is 1023 (2^10 - 1). - * SLOT_TIME: Slot time, default is 20us for 802.11b. - * LONG_RETRY: Long retry count. - * SHORT_RETRY: Short retry count. - */ -#define CSR11 0x002c -#define CSR11_CWMIN FIELD32(0x0000000f) -#define CSR11_CWMAX FIELD32(0x000000f0) -#define CSR11_SLOT_TIME FIELD32(0x00001f00) -#define CSR11_LONG_RETRY FIELD32(0x00ff0000) -#define CSR11_SHORT_RETRY FIELD32(0xff000000) - -/* - * CSR12: Synchronization configuration register 0. - * All units in 1/16 TU. - * BEACON_INTERVAL: Beacon interval, default is 100 TU. - * CFPMAX_DURATION: Cfp maximum duration, default is 100 TU. - */ -#define CSR12 0x0030 -#define CSR12_BEACON_INTERVAL FIELD32(0x0000ffff) -#define CSR12_CFP_MAX_DURATION FIELD32(0xffff0000) - -/* - * CSR13: Synchronization configuration register 1. - * All units in 1/16 TU. - * ATIMW_DURATION: Atim window duration. - * CFP_PERIOD: Cfp period, default is 0 TU. - */ -#define CSR13 0x0034 -#define CSR13_ATIMW_DURATION FIELD32(0x0000ffff) -#define CSR13_CFP_PERIOD FIELD32(0x00ff0000) - -/* - * CSR14: Synchronization control register. - * TSF_COUNT: Enable tsf auto counting. - * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. - * TBCN: Enable tbcn with reload value. - * TCFP: Enable tcfp & cfp / cp switching. - * TATIMW: Enable tatimw & atim window switching. - * BEACON_GEN: Enable beacon generator. - * CFP_COUNT_PRELOAD: Cfp count preload value. - * TBCM_PRELOAD: Tbcn preload value in units of 64us. - */ -#define CSR14 0x0038 -#define CSR14_TSF_COUNT FIELD32(0x00000001) -#define CSR14_TSF_SYNC FIELD32(0x00000006) -#define CSR14_TBCN FIELD32(0x00000008) -#define CSR14_TCFP FIELD32(0x00000010) -#define CSR14_TATIMW FIELD32(0x00000020) -#define CSR14_BEACON_GEN FIELD32(0x00000040) -#define CSR14_CFP_COUNT_PRELOAD FIELD32(0x0000ff00) -#define CSR14_TBCM_PRELOAD FIELD32(0xffff0000) - -/* - * CSR15: Synchronization status register. - * CFP: ASIC is in contention-free period. - * ATIMW: ASIC is in ATIM window. - * BEACON_SENT: Beacon is send. - */ -#define CSR15 0x003c -#define CSR15_CFP FIELD32(0x00000001) -#define CSR15_ATIMW FIELD32(0x00000002) -#define CSR15_BEACON_SENT FIELD32(0x00000004) - -/* - * CSR16: TSF timer register 0. - */ -#define CSR16 0x0040 -#define CSR16_LOW_TSFTIMER FIELD32(0xffffffff) - -/* - * CSR17: TSF timer register 1. - */ -#define CSR17 0x0044 -#define CSR17_HIGH_TSFTIMER FIELD32(0xffffffff) - -/* - * CSR18: IFS timer register 0. - * SIFS: Sifs, default is 10 us. - * PIFS: Pifs, default is 30 us. - */ -#define CSR18 0x0048 -#define CSR18_SIFS FIELD32(0x0000ffff) -#define CSR18_PIFS FIELD32(0xffff0000) - -/* - * CSR19: IFS timer register 1. - * DIFS: Difs, default is 50 us. - * EIFS: Eifs, default is 364 us. - */ -#define CSR19 0x004c -#define CSR19_DIFS FIELD32(0x0000ffff) -#define CSR19_EIFS FIELD32(0xffff0000) - -/* - * CSR20: Wakeup timer register. - * DELAY_AFTER_TBCN: Delay after tbcn expired in units of 1/16 TU. - * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. - * AUTOWAKE: Enable auto wakeup / sleep mechanism. - */ -#define CSR20 0x0050 -#define CSR20_DELAY_AFTER_TBCN FIELD32(0x0000ffff) -#define CSR20_TBCN_BEFORE_WAKEUP FIELD32(0x00ff0000) -#define CSR20_AUTOWAKE FIELD32(0x01000000) - -/* - * CSR21: EEPROM control register. - * RELOAD: Write 1 to reload eeprom content. - * TYPE_93C46: 1: 93c46, 0:93c66. - */ -#define CSR21 0x0054 -#define CSR21_RELOAD FIELD32(0x00000001) -#define CSR21_EEPROM_DATA_CLOCK FIELD32(0x00000002) -#define CSR21_EEPROM_CHIP_SELECT FIELD32(0x00000004) -#define CSR21_EEPROM_DATA_IN FIELD32(0x00000008) -#define CSR21_EEPROM_DATA_OUT FIELD32(0x00000010) -#define CSR21_TYPE_93C46 FIELD32(0x00000020) - -/* - * CSR22: CFP control register. - * CFP_DURATION_REMAIN: Cfp duration remain, in units of TU. - * RELOAD_CFP_DURATION: Write 1 to reload cfp duration remain. - */ -#define CSR22 0x0058 -#define CSR22_CFP_DURATION_REMAIN FIELD32(0x0000ffff) -#define CSR22_RELOAD_CFP_DURATION FIELD32(0x00010000) - -/* - * Transmit related CSRs. - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * TXCSR0: TX Control Register. - * KICK_TX: Kick tx ring. - * KICK_ATIM: Kick atim ring. - * KICK_PRIO: Kick priority ring. - * ABORT: Abort all transmit related ring operation. - */ -#define TXCSR0 0x0060 -#define TXCSR0_KICK_TX FIELD32(0x00000001) -#define TXCSR0_KICK_ATIM FIELD32(0x00000002) -#define TXCSR0_KICK_PRIO FIELD32(0x00000004) -#define TXCSR0_ABORT FIELD32(0x00000008) - -/* - * TXCSR1: TX Configuration Register. - * ACK_TIMEOUT: Ack timeout, default = sifs + 2*slottime + acktime @ 1mbps. - * ACK_CONSUME_TIME: Ack consume time, default = sifs + acktime @ 1mbps. - * TSF_OFFSET: Insert tsf offset. - * AUTORESPONDER: Enable auto responder which include ack & cts. - */ -#define TXCSR1 0x0064 -#define TXCSR1_ACK_TIMEOUT FIELD32(0x000001ff) -#define TXCSR1_ACK_CONSUME_TIME FIELD32(0x0003fe00) -#define TXCSR1_TSF_OFFSET FIELD32(0x00fc0000) -#define TXCSR1_AUTORESPONDER FIELD32(0x01000000) - -/* - * TXCSR2: Tx descriptor configuration register. - * TXD_SIZE: Tx descriptor size, default is 48. - * NUM_TXD: Number of tx entries in ring. - * NUM_ATIM: Number of atim entries in ring. - * NUM_PRIO: Number of priority entries in ring. - */ -#define TXCSR2 0x0068 -#define TXCSR2_TXD_SIZE FIELD32(0x000000ff) -#define TXCSR2_NUM_TXD FIELD32(0x0000ff00) -#define TXCSR2_NUM_ATIM FIELD32(0x00ff0000) -#define TXCSR2_NUM_PRIO FIELD32(0xff000000) - -/* - * TXCSR3: TX Ring Base address register. - */ -#define TXCSR3 0x006c -#define TXCSR3_TX_RING_REGISTER FIELD32(0xffffffff) - -/* - * TXCSR4: TX Atim Ring Base address register. - */ -#define TXCSR4 0x0070 -#define TXCSR4_ATIM_RING_REGISTER FIELD32(0xffffffff) - -/* - * TXCSR5: TX Prio Ring Base address register. - */ -#define TXCSR5 0x0074 -#define TXCSR5_PRIO_RING_REGISTER FIELD32(0xffffffff) - -/* - * TXCSR6: Beacon Base address register. - */ -#define TXCSR6 0x0078 -#define TXCSR6_BEACON_RING_REGISTER FIELD32(0xffffffff) - -/* - * TXCSR7: Auto responder control register. - * AR_POWERMANAGEMENT: Auto responder power management bit. - */ -#define TXCSR7 0x007c -#define TXCSR7_AR_POWERMANAGEMENT FIELD32(0x00000001) - -/* - * Receive related CSRs. - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * RXCSR0: RX Control Register. - * DISABLE_RX: Disable rx engine. - * DROP_CRC: Drop crc error. - * DROP_PHYSICAL: Drop physical error. - * DROP_CONTROL: Drop control frame. - * DROP_NOT_TO_ME: Drop not to me unicast frame. - * DROP_TODS: Drop frame tods bit is true. - * DROP_VERSION_ERROR: Drop version error frame. - * PASS_CRC: Pass all packets with crc attached. - */ -#define RXCSR0 0x0080 -#define RXCSR0_DISABLE_RX FIELD32(0x00000001) -#define RXCSR0_DROP_CRC FIELD32(0x00000002) -#define RXCSR0_DROP_PHYSICAL FIELD32(0x00000004) -#define RXCSR0_DROP_CONTROL FIELD32(0x00000008) -#define RXCSR0_DROP_NOT_TO_ME FIELD32(0x00000010) -#define RXCSR0_DROP_TODS FIELD32(0x00000020) -#define RXCSR0_DROP_VERSION_ERROR FIELD32(0x00000040) -#define RXCSR0_PASS_CRC FIELD32(0x00000080) - -/* - * RXCSR1: RX descriptor configuration register. - * RXD_SIZE: Rx descriptor size, default is 32b. - * NUM_RXD: Number of rx entries in ring. - */ -#define RXCSR1 0x0084 -#define RXCSR1_RXD_SIZE FIELD32(0x000000ff) -#define RXCSR1_NUM_RXD FIELD32(0x0000ff00) - -/* - * RXCSR2: RX Ring base address register. - */ -#define RXCSR2 0x0088 -#define RXCSR2_RX_RING_REGISTER FIELD32(0xffffffff) - -/* - * RXCSR3: BBP ID register for Rx operation. - * BBP_ID#: BBP register # id. - * BBP_ID#_VALID: BBP register # id is valid or not. - */ -#define RXCSR3 0x0090 -#define RXCSR3_BBP_ID0 FIELD32(0x0000007f) -#define RXCSR3_BBP_ID0_VALID FIELD32(0x00000080) -#define RXCSR3_BBP_ID1 FIELD32(0x00007f00) -#define RXCSR3_BBP_ID1_VALID FIELD32(0x00008000) -#define RXCSR3_BBP_ID2 FIELD32(0x007f0000) -#define RXCSR3_BBP_ID2_VALID FIELD32(0x00800000) -#define RXCSR3_BBP_ID3 FIELD32(0x7f000000) -#define RXCSR3_BBP_ID3_VALID FIELD32(0x80000000) - -/* - * RXCSR4: BBP ID register for Rx operation. - * BBP_ID#: BBP register # id. - * BBP_ID#_VALID: BBP register # id is valid or not. - */ -#define RXCSR4 0x0094 -#define RXCSR4_BBP_ID4 FIELD32(0x0000007f) -#define RXCSR4_BBP_ID4_VALID FIELD32(0x00000080) -#define RXCSR4_BBP_ID5 FIELD32(0x00007f00) -#define RXCSR4_BBP_ID5_VALID FIELD32(0x00008000) - -/* - * ARCSR0: Auto Responder PLCP config register 0. - * ARCSR0_AR_BBP_DATA#: Auto responder BBP register # data. - * ARCSR0_AR_BBP_ID#: Auto responder BBP register # Id. - */ -#define ARCSR0 0x0098 -#define ARCSR0_AR_BBP_DATA0 FIELD32(0x000000ff) -#define ARCSR0_AR_BBP_ID0 FIELD32(0x0000ff00) -#define ARCSR0_AR_BBP_DATA1 FIELD32(0x00ff0000) -#define ARCSR0_AR_BBP_ID1 FIELD32(0xff000000) - -/* - * ARCSR1: Auto Responder PLCP config register 1. - * ARCSR0_AR_BBP_DATA#: Auto responder BBP register # data. - * ARCSR0_AR_BBP_ID#: Auto responder BBP register # Id. - */ -#define ARCSR1 0x009c -#define ARCSR1_AR_BBP_DATA2 FIELD32(0x000000ff) -#define ARCSR1_AR_BBP_ID2 FIELD32(0x0000ff00) -#define ARCSR1_AR_BBP_DATA3 FIELD32(0x00ff0000) -#define ARCSR1_AR_BBP_ID3 FIELD32(0xff000000) - -/* - * Miscellaneous Registers. - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * PCICSR: PCI control register. - * BIG_ENDIAN: 1: big endian, 0: little endian. - * RX_TRESHOLD: Rx threshold in dw to start pci access - * 0: 16dw (default), 1: 8dw, 2: 4dw, 3: 32dw. - * TX_TRESHOLD: Tx threshold in dw to start pci access - * 0: 0dw (default), 1: 1dw, 2: 4dw, 3: forward. - * BURST_LENTH: Pci burst length 0: 4dw (default, 1: 8dw, 2: 16dw, 3:32dw. - * ENABLE_CLK: Enable clk_run, pci clock can't going down to non-operational. - */ -#define PCICSR 0x008c -#define PCICSR_BIG_ENDIAN FIELD32(0x00000001) -#define PCICSR_RX_TRESHOLD FIELD32(0x00000006) -#define PCICSR_TX_TRESHOLD FIELD32(0x00000018) -#define PCICSR_BURST_LENTH FIELD32(0x00000060) -#define PCICSR_ENABLE_CLK FIELD32(0x00000080) - -/* - * CNT0: FCS error count. - * FCS_ERROR: FCS error count, cleared when read. - */ -#define CNT0 0x00a0 -#define CNT0_FCS_ERROR FIELD32(0x0000ffff) - -/* - * Statistic Register. - * CNT1: PLCP error count. - * CNT2: Long error count. - * CNT3: CCA false alarm count. - * CNT4: Rx FIFO overflow count. - * CNT5: Tx FIFO underrun count. - */ -#define TIMECSR2 0x00a8 -#define CNT1 0x00ac -#define CNT2 0x00b0 -#define TIMECSR3 0x00b4 -#define CNT3 0x00b8 -#define CNT4 0x00bc -#define CNT5 0x00c0 - -/* - * Baseband Control Register. - */ - -/* - * PWRCSR0: Power mode configuration register. - */ -#define PWRCSR0 0x00c4 - -/* - * Power state transition time registers. - */ -#define PSCSR0 0x00c8 -#define PSCSR1 0x00cc -#define PSCSR2 0x00d0 -#define PSCSR3 0x00d4 - -/* - * PWRCSR1: Manual power control / status register. - * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. - * SET_STATE: Set state. Write 1 to trigger, self cleared. - * BBP_DESIRE_STATE: BBP desired state. - * RF_DESIRE_STATE: RF desired state. - * BBP_CURR_STATE: BBP current state. - * RF_CURR_STATE: RF current state. - * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. - */ -#define PWRCSR1 0x00d8 -#define PWRCSR1_SET_STATE FIELD32(0x00000001) -#define PWRCSR1_BBP_DESIRE_STATE FIELD32(0x00000006) -#define PWRCSR1_RF_DESIRE_STATE FIELD32(0x00000018) -#define PWRCSR1_BBP_CURR_STATE FIELD32(0x00000060) -#define PWRCSR1_RF_CURR_STATE FIELD32(0x00000180) -#define PWRCSR1_PUT_TO_SLEEP FIELD32(0x00000200) - -/* - * TIMECSR: Timer control register. - * US_COUNT: 1 us timer count in units of clock cycles. - * US_64_COUNT: 64 us timer count in units of 1 us timer. - * BEACON_EXPECT: Beacon expect window. - */ -#define TIMECSR 0x00dc -#define TIMECSR_US_COUNT FIELD32(0x000000ff) -#define TIMECSR_US_64_COUNT FIELD32(0x0000ff00) -#define TIMECSR_BEACON_EXPECT FIELD32(0x00070000) - -/* - * MACCSR0: MAC configuration register 0. - */ -#define MACCSR0 0x00e0 - -/* - * MACCSR1: MAC configuration register 1. - * KICK_RX: Kick one-shot rx in one-shot rx mode. - * ONESHOT_RXMODE: Enable one-shot rx mode for debugging. - * BBPRX_RESET_MODE: Ralink bbp rx reset mode. - * AUTO_TXBBP: Auto tx logic access bbp control register. - * AUTO_RXBBP: Auto rx logic access bbp control register. - * LOOPBACK: Loopback mode. 0: normal, 1: internal, 2: external, 3:rsvd. - * INTERSIL_IF: Intersil if calibration pin. - */ -#define MACCSR1 0x00e4 -#define MACCSR1_KICK_RX FIELD32(0x00000001) -#define MACCSR1_ONESHOT_RXMODE FIELD32(0x00000002) -#define MACCSR1_BBPRX_RESET_MODE FIELD32(0x00000004) -#define MACCSR1_AUTO_TXBBP FIELD32(0x00000008) -#define MACCSR1_AUTO_RXBBP FIELD32(0x00000010) -#define MACCSR1_LOOPBACK FIELD32(0x00000060) -#define MACCSR1_INTERSIL_IF FIELD32(0x00000080) - -/* - * RALINKCSR: Ralink Rx auto-reset BBCR. - * AR_BBP_DATA#: Auto reset BBP register # data. - * AR_BBP_ID#: Auto reset BBP register # id. - */ -#define RALINKCSR 0x00e8 -#define RALINKCSR_AR_BBP_DATA0 FIELD32(0x000000ff) -#define RALINKCSR_AR_BBP_ID0 FIELD32(0x0000ff00) -#define RALINKCSR_AR_BBP_DATA1 FIELD32(0x00ff0000) -#define RALINKCSR_AR_BBP_ID1 FIELD32(0xff000000) - -/* - * BCNCSR: Beacon interval control register. - * CHANGE: Write one to change beacon interval. - * DELTATIME: The delta time value. - * NUM_BEACON: Number of beacon according to mode. - * MODE: Please refer to asic specs. - * PLUS: Plus or minus delta time value. - */ -#define BCNCSR 0x00ec -#define BCNCSR_CHANGE FIELD32(0x00000001) -#define BCNCSR_DELTATIME FIELD32(0x0000001e) -#define BCNCSR_NUM_BEACON FIELD32(0x00001fe0) -#define BCNCSR_MODE FIELD32(0x00006000) -#define BCNCSR_PLUS FIELD32(0x00008000) - -/* - * BBP / RF / IF Control Register. - */ - -/* - * BBPCSR: BBP serial control register. - * VALUE: Register value to program into BBP. - * REGNUM: Selected BBP register. - * BUSY: 1: asic is busy execute BBP programming. - * WRITE_CONTROL: 1: write BBP, 0: read BBP. - */ -#define BBPCSR 0x00f0 -#define BBPCSR_VALUE FIELD32(0x000000ff) -#define BBPCSR_REGNUM FIELD32(0x00007f00) -#define BBPCSR_BUSY FIELD32(0x00008000) -#define BBPCSR_WRITE_CONTROL FIELD32(0x00010000) - -/* - * RFCSR: RF serial control register. - * VALUE: Register value + id to program into rf/if. - * NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). - * IF_SELECT: Chip to program: 0: rf, 1: if. - * PLL_LD: Rf pll_ld status. - * BUSY: 1: asic is busy execute rf programming. - */ -#define RFCSR 0x00f4 -#define RFCSR_VALUE FIELD32(0x00ffffff) -#define RFCSR_NUMBER_OF_BITS FIELD32(0x1f000000) -#define RFCSR_IF_SELECT FIELD32(0x20000000) -#define RFCSR_PLL_LD FIELD32(0x40000000) -#define RFCSR_BUSY FIELD32(0x80000000) - -/* - * LEDCSR: LED control register. - * ON_PERIOD: On period, default 70ms. - * OFF_PERIOD: Off period, default 30ms. - * LINK: 0: linkoff, 1: linkup. - * ACTIVITY: 0: idle, 1: active. - */ -#define LEDCSR 0x00f8 -#define LEDCSR_ON_PERIOD FIELD32(0x000000ff) -#define LEDCSR_OFF_PERIOD FIELD32(0x0000ff00) -#define LEDCSR_LINK FIELD32(0x00010000) -#define LEDCSR_ACTIVITY FIELD32(0x00020000) - -/* - * ASIC pointer information. - * RXPTR: Current RX ring address. - * TXPTR: Current Tx ring address. - * PRIPTR: Current Priority ring address. - * ATIMPTR: Current ATIM ring address. - */ -#define RXPTR 0x0100 -#define TXPTR 0x0104 -#define PRIPTR 0x0108 -#define ATIMPTR 0x010c - -/* - * GPIO and others. - */ - -/* - * GPIOCSR: GPIO control register. - * GPIOCSR_VALx: Actual GPIO pin x value - * GPIOCSR_DIRx: GPIO direction: 0 = output; 1 = input - */ -#define GPIOCSR 0x0120 -#define GPIOCSR_VAL0 FIELD32(0x00000001) -#define GPIOCSR_VAL1 FIELD32(0x00000002) -#define GPIOCSR_VAL2 FIELD32(0x00000004) -#define GPIOCSR_VAL3 FIELD32(0x00000008) -#define GPIOCSR_VAL4 FIELD32(0x00000010) -#define GPIOCSR_VAL5 FIELD32(0x00000020) -#define GPIOCSR_VAL6 FIELD32(0x00000040) -#define GPIOCSR_VAL7 FIELD32(0x00000080) -#define GPIOCSR_DIR0 FIELD32(0x00000100) -#define GPIOCSR_DIR1 FIELD32(0x00000200) -#define GPIOCSR_DIR2 FIELD32(0x00000400) -#define GPIOCSR_DIR3 FIELD32(0x00000800) -#define GPIOCSR_DIR4 FIELD32(0x00001000) -#define GPIOCSR_DIR5 FIELD32(0x00002000) -#define GPIOCSR_DIR6 FIELD32(0x00004000) -#define GPIOCSR_DIR7 FIELD32(0x00008000) - -/* - * BBPPCSR: BBP Pin control register. - */ -#define BBPPCSR 0x0124 - -/* - * BCNCSR1: Tx BEACON offset time control register. - * PRELOAD: Beacon timer offset in units of usec. - */ -#define BCNCSR1 0x0130 -#define BCNCSR1_PRELOAD FIELD32(0x0000ffff) - -/* - * MACCSR2: TX_PE to RX_PE turn-around time control register - * DELAY: RX_PE low width, in units of pci clock cycle. - */ -#define MACCSR2 0x0134 -#define MACCSR2_DELAY FIELD32(0x000000ff) - -/* - * ARCSR2: 1 Mbps ACK/CTS PLCP. - */ -#define ARCSR2 0x013c -#define ARCSR2_SIGNAL FIELD32(0x000000ff) -#define ARCSR2_SERVICE FIELD32(0x0000ff00) -#define ARCSR2_LENGTH_LOW FIELD32(0x00ff0000) -#define ARCSR2_LENGTH FIELD32(0xffff0000) - -/* - * ARCSR3: 2 Mbps ACK/CTS PLCP. - */ -#define ARCSR3 0x0140 -#define ARCSR3_SIGNAL FIELD32(0x000000ff) -#define ARCSR3_SERVICE FIELD32(0x0000ff00) -#define ARCSR3_LENGTH FIELD32(0xffff0000) - -/* - * ARCSR4: 5.5 Mbps ACK/CTS PLCP. - */ -#define ARCSR4 0x0144 -#define ARCSR4_SIGNAL FIELD32(0x000000ff) -#define ARCSR4_SERVICE FIELD32(0x0000ff00) -#define ARCSR4_LENGTH FIELD32(0xffff0000) - -/* - * ARCSR5: 11 Mbps ACK/CTS PLCP. - */ -#define ARCSR5 0x0148 -#define ARCSR5_SIGNAL FIELD32(0x000000ff) -#define ARCSR5_SERVICE FIELD32(0x0000ff00) -#define ARCSR5_LENGTH FIELD32(0xffff0000) - -/* - * BBP registers. - * The wordsize of the BBP is 8 bits. - */ - -/* - * R1: TX antenna control - */ -#define BBP_R1_TX_ANTENNA FIELD8(0x03) - -/* - * R4: RX antenna control - */ -#define BBP_R4_RX_ANTENNA FIELD8(0x06) - -/* - * RF registers - */ - -/* - * RF 1 - */ -#define RF1_TUNER FIELD32(0x00020000) - -/* - * RF 3 - */ -#define RF3_TUNER FIELD32(0x00000100) -#define RF3_TXPOWER FIELD32(0x00003e00) - -/* - * EEPROM content. - * The wordsize of the EEPROM is 16 bits. - */ - -/* - * HW MAC address. - */ -#define EEPROM_MAC_ADDR_0 0x0002 -#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) -#define EEPROM_MAC_ADDR1 0x0003 -#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_2 0x0004 -#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) - -/* - * EEPROM antenna. - * ANTENNA_NUM: Number of antenna's. - * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. - * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. - * RF_TYPE: Rf_type of this adapter. - * LED_MODE: 0: default, 1: TX/RX activity,2: Single (ignore link), 3: rsvd. - * RX_AGCVGC: 0: disable, 1:enable BBP R13 tuning. - * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. - */ -#define EEPROM_ANTENNA 0x0b -#define EEPROM_ANTENNA_NUM FIELD16(0x0003) -#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) -#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) -#define EEPROM_ANTENNA_RF_TYPE FIELD16(0x0040) -#define EEPROM_ANTENNA_LED_MODE FIELD16(0x0180) -#define EEPROM_ANTENNA_RX_AGCVGC_TUNING FIELD16(0x0200) -#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) - -/* - * EEPROM BBP. - */ -#define EEPROM_BBP_START 0x0c -#define EEPROM_BBP_SIZE 7 -#define EEPROM_BBP_VALUE FIELD16(0x00ff) -#define EEPROM_BBP_REG_ID FIELD16(0xff00) - -/* - * EEPROM TXPOWER - */ -#define EEPROM_TXPOWER_START 0x13 -#define EEPROM_TXPOWER_SIZE 7 -#define EEPROM_TXPOWER_1 FIELD16(0x00ff) -#define EEPROM_TXPOWER_2 FIELD16(0xff00) - -/* - * DMA descriptor defines. - */ -#define TXD_DESC_SIZE (8 * sizeof(__le32)) -#define RXD_DESC_SIZE (8 * sizeof(__le32)) - -/* - * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. - */ - -/* - * Word0 - */ -#define TXD_W0_OWNER_NIC FIELD32(0x00000001) -#define TXD_W0_VALID FIELD32(0x00000002) -#define TXD_W0_RESULT FIELD32(0x0000001c) -#define TXD_W0_RETRY_COUNT FIELD32(0x000000e0) -#define TXD_W0_MORE_FRAG FIELD32(0x00000100) -#define TXD_W0_ACK FIELD32(0x00000200) -#define TXD_W0_TIMESTAMP FIELD32(0x00000400) -#define TXD_W0_RTS FIELD32(0x00000800) -#define TXD_W0_IFS FIELD32(0x00006000) -#define TXD_W0_RETRY_MODE FIELD32(0x00008000) -#define TXD_W0_AGC FIELD32(0x00ff0000) -#define TXD_W0_R2 FIELD32(0xff000000) - -/* - * Word1 - */ -#define TXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) - -/* - * Word2 - */ -#define TXD_W2_BUFFER_LENGTH FIELD32(0x0000ffff) -#define TXD_W2_DATABYTE_COUNT FIELD32(0xffff0000) - -/* - * Word3 & 4: PLCP information - * The PLCP values should be treated as if they were BBP values. - */ -#define TXD_W3_PLCP_SIGNAL FIELD32(0x000000ff) -#define TXD_W3_PLCP_SIGNAL_REGNUM FIELD32(0x00007f00) -#define TXD_W3_PLCP_SIGNAL_BUSY FIELD32(0x00008000) -#define TXD_W3_PLCP_SERVICE FIELD32(0x00ff0000) -#define TXD_W3_PLCP_SERVICE_REGNUM FIELD32(0x7f000000) -#define TXD_W3_PLCP_SERVICE_BUSY FIELD32(0x80000000) - -#define TXD_W4_PLCP_LENGTH_LOW FIELD32(0x000000ff) -#define TXD_W3_PLCP_LENGTH_LOW_REGNUM FIELD32(0x00007f00) -#define TXD_W3_PLCP_LENGTH_LOW_BUSY FIELD32(0x00008000) -#define TXD_W4_PLCP_LENGTH_HIGH FIELD32(0x00ff0000) -#define TXD_W3_PLCP_LENGTH_HIGH_REGNUM FIELD32(0x7f000000) -#define TXD_W3_PLCP_LENGTH_HIGH_BUSY FIELD32(0x80000000) - -/* - * Word5 - */ -#define TXD_W5_BBCR4 FIELD32(0x0000ffff) -#define TXD_W5_AGC_REG FIELD32(0x007f0000) -#define TXD_W5_AGC_REG_VALID FIELD32(0x00800000) -#define TXD_W5_XXX_REG FIELD32(0x7f000000) -#define TXD_W5_XXX_REG_VALID FIELD32(0x80000000) - -/* - * Word6 - */ -#define TXD_W6_SK_BUFF FIELD32(0xffffffff) - -/* - * Word7 - */ -#define TXD_W7_RESERVED FIELD32(0xffffffff) - -/* - * RX descriptor format for RX Ring. - */ - -/* - * Word0 - */ -#define RXD_W0_OWNER_NIC FIELD32(0x00000001) -#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) -#define RXD_W0_MULTICAST FIELD32(0x00000004) -#define RXD_W0_BROADCAST FIELD32(0x00000008) -#define RXD_W0_MY_BSS FIELD32(0x00000010) -#define RXD_W0_CRC_ERROR FIELD32(0x00000020) -#define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) -#define RXD_W0_DATABYTE_COUNT FIELD32(0xffff0000) - -/* - * Word1 - */ -#define RXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) - -/* - * Word2 - */ -#define RXD_W2_BUFFER_LENGTH FIELD32(0x0000ffff) -#define RXD_W2_BBR0 FIELD32(0x00ff0000) -#define RXD_W2_SIGNAL FIELD32(0xff000000) - -/* - * Word3 - */ -#define RXD_W3_RSSI FIELD32(0x000000ff) -#define RXD_W3_BBR3 FIELD32(0x0000ff00) -#define RXD_W3_BBR4 FIELD32(0x00ff0000) -#define RXD_W3_BBR5 FIELD32(0xff000000) - -/* - * Word4 - */ -#define RXD_W4_RX_END_TIME FIELD32(0xffffffff) - -/* - * Word5 & 6 & 7: Reserved - */ -#define RXD_W5_RESERVED FIELD32(0xffffffff) -#define RXD_W6_RESERVED FIELD32(0xffffffff) -#define RXD_W7_RESERVED FIELD32(0xffffffff) - -/* - * Macros for converting txpower from EEPROM to mac80211 value - * and from mac80211 value to register value. - * NOTE: Logics in rt2400pci for txpower are reversed - * compared to the other rt2x00 drivers. A higher txpower - * value means that the txpower must be lowered. This is - * important when converting the value coming from the - * mac80211 stack to the rt2400 acceptable value. - */ -#define MIN_TXPOWER 31 -#define MAX_TXPOWER 62 -#define DEFAULT_TXPOWER 39 - -#define __CLAMP_TX(__txpower) \ - clamp_t(char, (__txpower), MIN_TXPOWER, MAX_TXPOWER) - -#define TXPOWER_FROM_DEV(__txpower) \ - ((__CLAMP_TX(__txpower) - MAX_TXPOWER) + MIN_TXPOWER) - -#define TXPOWER_TO_DEV(__txpower) \ - (MAX_TXPOWER - (__CLAMP_TX(__txpower) - MIN_TXPOWER)) - -#endif /* RT2400PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c deleted file mode 100644 index 1a6740b4d396..000000000000 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ /dev/null @@ -1,2148 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2500pci - Abstract: rt2500pci device specific routines. - Supported chipsets: RT2560. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00mmio.h" -#include "rt2x00pci.h" -#include "rt2500pci.h" - -/* - * Register access. - * All access to the CSR registers will go through the methods - * rt2x00mmio_register_read and rt2x00mmio_register_write. - * BBP and RF register require indirect register access, - * and use the CSR registers BBPCSR and RFCSR to achieve this. - * These indirect registers work with busy bits, - * and we will try maximal REGISTER_BUSY_COUNT times to access - * the register while taking a REGISTER_BUSY_DELAY us delay - * between each attampt. When the busy bit is still set at that time, - * the access attempt is considered to have failed, - * and we will print an error. - */ -#define WAIT_FOR_BBP(__dev, __reg) \ - rt2x00mmio_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg)) -#define WAIT_FOR_RF(__dev, __reg) \ - rt2x00mmio_regbusy_read((__dev), RFCSR, RFCSR_BUSY, (__reg)) - -static void rt2500pci_bbp_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u8 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, BBPCSR_VALUE, value); - rt2x00_set_field32(®, BBPCSR_REGNUM, word); - rt2x00_set_field32(®, BBPCSR_BUSY, 1); - rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); - - rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2500pci_bbp_read(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u8 *value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the read request into the register. - * After the data has been written, we wait until hardware - * returns the correct value, if at any time the register - * doesn't become available in time, reg will be 0xffffffff - * which means we return 0xff to the caller. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, BBPCSR_REGNUM, word); - rt2x00_set_field32(®, BBPCSR_BUSY, 1); - rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); - - rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); - - WAIT_FOR_BBP(rt2x00dev, ®); - } - - *value = rt2x00_get_field32(reg, BBPCSR_VALUE); - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2500pci_rf_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u32 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the RF becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_RF(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, RFCSR_VALUE, value); - rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); - rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); - rt2x00_set_field32(®, RFCSR_BUSY, 1); - - rt2x00mmio_register_write(rt2x00dev, RFCSR, reg); - rt2x00_rf_write(rt2x00dev, word, value); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2500pci_eepromregister_read(struct eeprom_93cx6 *eeprom) -{ - struct rt2x00_dev *rt2x00dev = eeprom->data; - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, CSR21, ®); - - eeprom->reg_data_in = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_IN); - eeprom->reg_data_out = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_OUT); - eeprom->reg_data_clock = - !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_CLOCK); - eeprom->reg_chip_select = - !!rt2x00_get_field32(reg, CSR21_EEPROM_CHIP_SELECT); -} - -static void rt2500pci_eepromregister_write(struct eeprom_93cx6 *eeprom) -{ - struct rt2x00_dev *rt2x00dev = eeprom->data; - u32 reg = 0; - - rt2x00_set_field32(®, CSR21_EEPROM_DATA_IN, !!eeprom->reg_data_in); - rt2x00_set_field32(®, CSR21_EEPROM_DATA_OUT, !!eeprom->reg_data_out); - rt2x00_set_field32(®, CSR21_EEPROM_DATA_CLOCK, - !!eeprom->reg_data_clock); - rt2x00_set_field32(®, CSR21_EEPROM_CHIP_SELECT, - !!eeprom->reg_chip_select); - - rt2x00mmio_register_write(rt2x00dev, CSR21, reg); -} - -#ifdef CONFIG_RT2X00_LIB_DEBUGFS -static const struct rt2x00debug rt2500pci_rt2x00debug = { - .owner = THIS_MODULE, - .csr = { - .read = rt2x00mmio_register_read, - .write = rt2x00mmio_register_write, - .flags = RT2X00DEBUGFS_OFFSET, - .word_base = CSR_REG_BASE, - .word_size = sizeof(u32), - .word_count = CSR_REG_SIZE / sizeof(u32), - }, - .eeprom = { - .read = rt2x00_eeprom_read, - .write = rt2x00_eeprom_write, - .word_base = EEPROM_BASE, - .word_size = sizeof(u16), - .word_count = EEPROM_SIZE / sizeof(u16), - }, - .bbp = { - .read = rt2500pci_bbp_read, - .write = rt2500pci_bbp_write, - .word_base = BBP_BASE, - .word_size = sizeof(u8), - .word_count = BBP_SIZE / sizeof(u8), - }, - .rf = { - .read = rt2x00_rf_read, - .write = rt2500pci_rf_write, - .word_base = RF_BASE, - .word_size = sizeof(u32), - .word_count = RF_SIZE / sizeof(u32), - }, -}; -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ - -static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); - return rt2x00_get_field32(reg, GPIOCSR_VAL0); -} - -#ifdef CONFIG_RT2X00_LIB_LEDS -static void rt2500pci_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - unsigned int enabled = brightness != LED_OFF; - u32 reg; - - rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); - - if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) - rt2x00_set_field32(®, LEDCSR_LINK, enabled); - else if (led->type == LED_TYPE_ACTIVITY) - rt2x00_set_field32(®, LEDCSR_ACTIVITY, enabled); - - rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); -} - -static int rt2500pci_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - u32 reg; - - rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); - rt2x00_set_field32(®, LEDCSR_ON_PERIOD, *delay_on); - rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, *delay_off); - rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); - - return 0; -} - -static void rt2500pci_init_led(struct rt2x00_dev *rt2x00dev, - struct rt2x00_led *led, - enum led_type type) -{ - led->rt2x00dev = rt2x00dev; - led->type = type; - led->led_dev.brightness_set = rt2500pci_brightness_set; - led->led_dev.blink_set = rt2500pci_blink_set; - led->flags = LED_INITIALIZED; -} -#endif /* CONFIG_RT2X00_LIB_LEDS */ - -/* - * Configuration handlers. - */ -static void rt2500pci_config_filter(struct rt2x00_dev *rt2x00dev, - const unsigned int filter_flags) -{ - u32 reg; - - /* - * Start configuration steps. - * Note that the version error will always be dropped - * and broadcast frames will always be accepted since - * there is no filter for it at this time. - */ - rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); - rt2x00_set_field32(®, RXCSR0_DROP_CRC, - !(filter_flags & FIF_FCSFAIL)); - rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, - !(filter_flags & FIF_PLCPFAIL)); - rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, 1); - rt2x00_set_field32(®, RXCSR0_DROP_TODS, - !rt2x00dev->intf_ap_count); - rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); - rt2x00_set_field32(®, RXCSR0_DROP_MCAST, - !(filter_flags & FIF_ALLMULTI)); - rt2x00_set_field32(®, RXCSR0_DROP_BCAST, 0); - rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); -} - -static void rt2500pci_config_intf(struct rt2x00_dev *rt2x00dev, - struct rt2x00_intf *intf, - struct rt2x00intf_conf *conf, - const unsigned int flags) -{ - struct data_queue *queue = rt2x00dev->bcn; - unsigned int bcn_preload; - u32 reg; - - if (flags & CONFIG_UPDATE_TYPE) { - /* - * Enable beacon config - */ - bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); - rt2x00mmio_register_read(rt2x00dev, BCNCSR1, ®); - rt2x00_set_field32(®, BCNCSR1_PRELOAD, bcn_preload); - rt2x00_set_field32(®, BCNCSR1_BEACON_CWMIN, queue->cw_min); - rt2x00mmio_register_write(rt2x00dev, BCNCSR1, reg); - - /* - * Enable synchronisation. - */ - rt2x00mmio_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_SYNC, conf->sync); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - } - - if (flags & CONFIG_UPDATE_MAC) - rt2x00mmio_register_multiwrite(rt2x00dev, CSR3, - conf->mac, sizeof(conf->mac)); - - if (flags & CONFIG_UPDATE_BSSID) - rt2x00mmio_register_multiwrite(rt2x00dev, CSR5, - conf->bssid, sizeof(conf->bssid)); -} - -static void rt2500pci_config_erp(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_erp *erp, - u32 changed) -{ - int preamble_mask; - u32 reg; - - /* - * When short preamble is enabled, we should set bit 0x08 - */ - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - preamble_mask = erp->short_preamble << 3; - - rt2x00mmio_register_read(rt2x00dev, TXCSR1, ®); - rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, 0x162); - rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, 0xa2); - rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); - rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); - rt2x00mmio_register_write(rt2x00dev, TXCSR1, reg); - - rt2x00mmio_register_read(rt2x00dev, ARCSR2, ®); - rt2x00_set_field32(®, ARCSR2_SIGNAL, 0x00); - rt2x00_set_field32(®, ARCSR2_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, - GET_DURATION(ACK_SIZE, 10)); - rt2x00mmio_register_write(rt2x00dev, ARCSR2, reg); - - rt2x00mmio_register_read(rt2x00dev, ARCSR3, ®); - rt2x00_set_field32(®, ARCSR3_SIGNAL, 0x01 | preamble_mask); - rt2x00_set_field32(®, ARCSR3_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, - GET_DURATION(ACK_SIZE, 20)); - rt2x00mmio_register_write(rt2x00dev, ARCSR3, reg); - - rt2x00mmio_register_read(rt2x00dev, ARCSR4, ®); - rt2x00_set_field32(®, ARCSR4_SIGNAL, 0x02 | preamble_mask); - rt2x00_set_field32(®, ARCSR4_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, - GET_DURATION(ACK_SIZE, 55)); - rt2x00mmio_register_write(rt2x00dev, ARCSR4, reg); - - rt2x00mmio_register_read(rt2x00dev, ARCSR5, ®); - rt2x00_set_field32(®, ARCSR5_SIGNAL, 0x03 | preamble_mask); - rt2x00_set_field32(®, ARCSR5_SERVICE, 0x84); - rt2x00_set_field32(®, ARCSR2_LENGTH, - GET_DURATION(ACK_SIZE, 110)); - rt2x00mmio_register_write(rt2x00dev, ARCSR5, reg); - } - - if (changed & BSS_CHANGED_BASIC_RATES) - rt2x00mmio_register_write(rt2x00dev, ARCSR1, erp->basic_rates); - - if (changed & BSS_CHANGED_ERP_SLOT) { - rt2x00mmio_register_read(rt2x00dev, CSR11, ®); - rt2x00_set_field32(®, CSR11_SLOT_TIME, erp->slot_time); - rt2x00mmio_register_write(rt2x00dev, CSR11, reg); - - rt2x00mmio_register_read(rt2x00dev, CSR18, ®); - rt2x00_set_field32(®, CSR18_SIFS, erp->sifs); - rt2x00_set_field32(®, CSR18_PIFS, erp->pifs); - rt2x00mmio_register_write(rt2x00dev, CSR18, reg); - - rt2x00mmio_register_read(rt2x00dev, CSR19, ®); - rt2x00_set_field32(®, CSR19_DIFS, erp->difs); - rt2x00_set_field32(®, CSR19_EIFS, erp->eifs); - rt2x00mmio_register_write(rt2x00dev, CSR19, reg); - } - - if (changed & BSS_CHANGED_BEACON_INT) { - rt2x00mmio_register_read(rt2x00dev, CSR12, ®); - rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, - erp->beacon_int * 16); - rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, - erp->beacon_int * 16); - rt2x00mmio_register_write(rt2x00dev, CSR12, reg); - } - -} - -static void rt2500pci_config_ant(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) -{ - u32 reg; - u8 r14; - u8 r2; - - /* - * We should never come here because rt2x00lib is supposed - * to catch this and send us the correct antenna explicitely. - */ - BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || - ant->tx == ANTENNA_SW_DIVERSITY); - - rt2x00mmio_register_read(rt2x00dev, BBPCSR1, ®); - rt2500pci_bbp_read(rt2x00dev, 14, &r14); - rt2500pci_bbp_read(rt2x00dev, 2, &r2); - - /* - * Configure the TX antenna. - */ - switch (ant->tx) { - case ANTENNA_A: - rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0); - rt2x00_set_field32(®, BBPCSR1_CCK, 0); - rt2x00_set_field32(®, BBPCSR1_OFDM, 0); - break; - case ANTENNA_B: - default: - rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2); - rt2x00_set_field32(®, BBPCSR1_CCK, 2); - rt2x00_set_field32(®, BBPCSR1_OFDM, 2); - break; - } - - /* - * Configure the RX antenna. - */ - switch (ant->rx) { - case ANTENNA_A: - rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0); - break; - case ANTENNA_B: - default: - rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2); - break; - } - - /* - * RT2525E and RT5222 need to flip TX I/Q - */ - if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) { - rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1); - rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 1); - rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 1); - - /* - * RT2525E does not need RX I/Q Flip. - */ - if (rt2x00_rf(rt2x00dev, RF2525E)) - rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0); - } else { - rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 0); - rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 0); - } - - rt2x00mmio_register_write(rt2x00dev, BBPCSR1, reg); - rt2500pci_bbp_write(rt2x00dev, 14, r14); - rt2500pci_bbp_write(rt2x00dev, 2, r2); -} - -static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev, - struct rf_channel *rf, const int txpower) -{ - u8 r70; - - /* - * Set TXpower. - */ - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); - - /* - * Switch on tuning bits. - * For RT2523 devices we do not need to update the R1 register. - */ - if (!rt2x00_rf(rt2x00dev, RF2523)) - rt2x00_set_field32(&rf->rf1, RF1_TUNER, 1); - rt2x00_set_field32(&rf->rf3, RF3_TUNER, 1); - - /* - * For RT2525 we should first set the channel to half band higher. - */ - if (rt2x00_rf(rt2x00dev, RF2525)) { - static const u32 vals[] = { - 0x00080cbe, 0x00080d02, 0x00080d06, 0x00080d0a, - 0x00080d0e, 0x00080d12, 0x00080d16, 0x00080d1a, - 0x00080d1e, 0x00080d22, 0x00080d26, 0x00080d2a, - 0x00080d2e, 0x00080d3a - }; - - rt2500pci_rf_write(rt2x00dev, 1, rf->rf1); - rt2500pci_rf_write(rt2x00dev, 2, vals[rf->channel - 1]); - rt2500pci_rf_write(rt2x00dev, 3, rf->rf3); - if (rf->rf4) - rt2500pci_rf_write(rt2x00dev, 4, rf->rf4); - } - - rt2500pci_rf_write(rt2x00dev, 1, rf->rf1); - rt2500pci_rf_write(rt2x00dev, 2, rf->rf2); - rt2500pci_rf_write(rt2x00dev, 3, rf->rf3); - if (rf->rf4) - rt2500pci_rf_write(rt2x00dev, 4, rf->rf4); - - /* - * Channel 14 requires the Japan filter bit to be set. - */ - r70 = 0x46; - rt2x00_set_field8(&r70, BBP_R70_JAPAN_FILTER, rf->channel == 14); - rt2500pci_bbp_write(rt2x00dev, 70, r70); - - msleep(1); - - /* - * Switch off tuning bits. - * For RT2523 devices we do not need to update the R1 register. - */ - if (!rt2x00_rf(rt2x00dev, RF2523)) { - rt2x00_set_field32(&rf->rf1, RF1_TUNER, 0); - rt2500pci_rf_write(rt2x00dev, 1, rf->rf1); - } - - rt2x00_set_field32(&rf->rf3, RF3_TUNER, 0); - rt2500pci_rf_write(rt2x00dev, 3, rf->rf3); - - /* - * Clear false CRC during channel switch. - */ - rt2x00mmio_register_read(rt2x00dev, CNT0, &rf->rf1); -} - -static void rt2500pci_config_txpower(struct rt2x00_dev *rt2x00dev, - const int txpower) -{ - u32 rf3; - - rt2x00_rf_read(rt2x00dev, 3, &rf3); - rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); - rt2500pci_rf_write(rt2x00dev, 3, rf3); -} - -static void rt2500pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, CSR11, ®); - rt2x00_set_field32(®, CSR11_LONG_RETRY, - libconf->conf->long_frame_max_tx_count); - rt2x00_set_field32(®, CSR11_SHORT_RETRY, - libconf->conf->short_frame_max_tx_count); - rt2x00mmio_register_write(rt2x00dev, CSR11, reg); -} - -static void rt2500pci_config_ps(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - enum dev_state state = - (libconf->conf->flags & IEEE80211_CONF_PS) ? - STATE_SLEEP : STATE_AWAKE; - u32 reg; - - if (state == STATE_SLEEP) { - rt2x00mmio_register_read(rt2x00dev, CSR20, ®); - rt2x00_set_field32(®, CSR20_DELAY_AFTER_TBCN, - (rt2x00dev->beacon_int - 20) * 16); - rt2x00_set_field32(®, CSR20_TBCN_BEFORE_WAKEUP, - libconf->conf->listen_interval - 1); - - /* We must first disable autowake before it can be enabled */ - rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); - rt2x00mmio_register_write(rt2x00dev, CSR20, reg); - - rt2x00_set_field32(®, CSR20_AUTOWAKE, 1); - rt2x00mmio_register_write(rt2x00dev, CSR20, reg); - } else { - rt2x00mmio_register_read(rt2x00dev, CSR20, ®); - rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); - rt2x00mmio_register_write(rt2x00dev, CSR20, reg); - } - - rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); -} - -static void rt2500pci_config(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf, - const unsigned int flags) -{ - if (flags & IEEE80211_CONF_CHANGE_CHANNEL) - rt2500pci_config_channel(rt2x00dev, &libconf->rf, - libconf->conf->power_level); - if ((flags & IEEE80211_CONF_CHANGE_POWER) && - !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) - rt2500pci_config_txpower(rt2x00dev, - libconf->conf->power_level); - if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) - rt2500pci_config_retry_limit(rt2x00dev, libconf); - if (flags & IEEE80211_CONF_CHANGE_PS) - rt2500pci_config_ps(rt2x00dev, libconf); -} - -/* - * Link tuning - */ -static void rt2500pci_link_stats(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - u32 reg; - - /* - * Update FCS error count from register. - */ - rt2x00mmio_register_read(rt2x00dev, CNT0, ®); - qual->rx_failed = rt2x00_get_field32(reg, CNT0_FCS_ERROR); - - /* - * Update False CCA count from register. - */ - rt2x00mmio_register_read(rt2x00dev, CNT3, ®); - qual->false_cca = rt2x00_get_field32(reg, CNT3_FALSE_CCA); -} - -static inline void rt2500pci_set_vgc(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, u8 vgc_level) -{ - if (qual->vgc_level_reg != vgc_level) { - rt2500pci_bbp_write(rt2x00dev, 17, vgc_level); - qual->vgc_level = vgc_level; - qual->vgc_level_reg = vgc_level; - } -} - -static void rt2500pci_reset_tuner(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - rt2500pci_set_vgc(rt2x00dev, qual, 0x48); -} - -static void rt2500pci_link_tuner(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, const u32 count) -{ - /* - * To prevent collisions with MAC ASIC on chipsets - * up to version C the link tuning should halt after 20 - * seconds while being associated. - */ - if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D && - rt2x00dev->intf_associated && count > 20) - return; - - /* - * Chipset versions C and lower should directly continue - * to the dynamic CCA tuning. Chipset version D and higher - * should go straight to dynamic CCA tuning when they - * are not associated. - */ - if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D || - !rt2x00dev->intf_associated) - goto dynamic_cca_tune; - - /* - * A too low RSSI will cause too much false CCA which will - * then corrupt the R17 tuning. To remidy this the tuning should - * be stopped (While making sure the R17 value will not exceed limits) - */ - if (qual->rssi < -80 && count > 20) { - if (qual->vgc_level_reg >= 0x41) - rt2500pci_set_vgc(rt2x00dev, qual, qual->vgc_level); - return; - } - - /* - * Special big-R17 for short distance - */ - if (qual->rssi >= -58) { - rt2500pci_set_vgc(rt2x00dev, qual, 0x50); - return; - } - - /* - * Special mid-R17 for middle distance - */ - if (qual->rssi >= -74) { - rt2500pci_set_vgc(rt2x00dev, qual, 0x41); - return; - } - - /* - * Leave short or middle distance condition, restore r17 - * to the dynamic tuning range. - */ - if (qual->vgc_level_reg >= 0x41) { - rt2500pci_set_vgc(rt2x00dev, qual, qual->vgc_level); - return; - } - -dynamic_cca_tune: - - /* - * R17 is inside the dynamic tuning range, - * start tuning the link based on the false cca counter. - */ - if (qual->false_cca > 512 && qual->vgc_level_reg < 0x40) - rt2500pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level_reg); - else if (qual->false_cca < 100 && qual->vgc_level_reg > 0x32) - rt2500pci_set_vgc(rt2x00dev, qual, --qual->vgc_level_reg); -} - -/* - * Queue handlers. - */ -static void rt2500pci_start_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); - rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 0); - rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); - rt2x00_set_field32(®, CSR14_TBCN, 1); - rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - break; - default: - break; - } -} - -static void rt2500pci_kick_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_AC_VO: - rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_KICK_PRIO, 1); - rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); - break; - case QID_AC_VI: - rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_KICK_TX, 1); - rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); - break; - case QID_ATIM: - rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_KICK_ATIM, 1); - rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); - break; - default: - break; - } -} - -static void rt2500pci_stop_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_AC_VO: - case QID_AC_VI: - case QID_ATIM: - rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_ABORT, 1); - rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); - break; - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); - rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 1); - rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); - rt2x00_set_field32(®, CSR14_TBCN, 0); - rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - - /* - * Wait for possibly running tbtt tasklets. - */ - tasklet_kill(&rt2x00dev->tbtt_tasklet); - break; - default: - break; - } -} - -/* - * Initialization functions. - */ -static bool rt2500pci_get_entry_state(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 0, &word); - - return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); - } else { - rt2x00_desc_read(entry_priv->desc, 0, &word); - - return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || - rt2x00_get_field32(word, TXD_W0_VALID)); - } -} - -static void rt2500pci_clear_entry(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 1, &word); - rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); - rt2x00_desc_write(entry_priv->desc, 1, word); - - rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); - rt2x00_desc_write(entry_priv->desc, 0, word); - } else { - rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_set_field32(&word, TXD_W0_VALID, 0); - rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); - rt2x00_desc_write(entry_priv->desc, 0, word); - } -} - -static int rt2500pci_init_queues(struct rt2x00_dev *rt2x00dev) -{ - struct queue_entry_priv_mmio *entry_priv; - u32 reg; - - /* - * Initialize registers. - */ - rt2x00mmio_register_read(rt2x00dev, TXCSR2, ®); - rt2x00_set_field32(®, TXCSR2_TXD_SIZE, rt2x00dev->tx[0].desc_size); - rt2x00_set_field32(®, TXCSR2_NUM_TXD, rt2x00dev->tx[1].limit); - rt2x00_set_field32(®, TXCSR2_NUM_ATIM, rt2x00dev->atim->limit); - rt2x00_set_field32(®, TXCSR2_NUM_PRIO, rt2x00dev->tx[0].limit); - rt2x00mmio_register_write(rt2x00dev, TXCSR2, reg); - - entry_priv = rt2x00dev->tx[1].entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, TXCSR3, ®); - rt2x00_set_field32(®, TXCSR3_TX_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TXCSR3, reg); - - entry_priv = rt2x00dev->tx[0].entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, TXCSR5, ®); - rt2x00_set_field32(®, TXCSR5_PRIO_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TXCSR5, reg); - - entry_priv = rt2x00dev->atim->entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, TXCSR4, ®); - rt2x00_set_field32(®, TXCSR4_ATIM_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TXCSR4, reg); - - entry_priv = rt2x00dev->bcn->entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, TXCSR6, ®); - rt2x00_set_field32(®, TXCSR6_BEACON_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TXCSR6, reg); - - rt2x00mmio_register_read(rt2x00dev, RXCSR1, ®); - rt2x00_set_field32(®, RXCSR1_RXD_SIZE, rt2x00dev->rx->desc_size); - rt2x00_set_field32(®, RXCSR1_NUM_RXD, rt2x00dev->rx->limit); - rt2x00mmio_register_write(rt2x00dev, RXCSR1, reg); - - entry_priv = rt2x00dev->rx->entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, RXCSR2, ®); - rt2x00_set_field32(®, RXCSR2_RX_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, RXCSR2, reg); - - return 0; -} - -static int rt2500pci_init_registers(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00mmio_register_write(rt2x00dev, PSCSR0, 0x00020002); - rt2x00mmio_register_write(rt2x00dev, PSCSR1, 0x00000002); - rt2x00mmio_register_write(rt2x00dev, PSCSR2, 0x00020002); - rt2x00mmio_register_write(rt2x00dev, PSCSR3, 0x00000002); - - rt2x00mmio_register_read(rt2x00dev, TIMECSR, ®); - rt2x00_set_field32(®, TIMECSR_US_COUNT, 33); - rt2x00_set_field32(®, TIMECSR_US_64_COUNT, 63); - rt2x00_set_field32(®, TIMECSR_BEACON_EXPECT, 0); - rt2x00mmio_register_write(rt2x00dev, TIMECSR, reg); - - rt2x00mmio_register_read(rt2x00dev, CSR9, ®); - rt2x00_set_field32(®, CSR9_MAX_FRAME_UNIT, - rt2x00dev->rx->data_size / 128); - rt2x00mmio_register_write(rt2x00dev, CSR9, reg); - - /* - * Always use CWmin and CWmax set in descriptor. - */ - rt2x00mmio_register_read(rt2x00dev, CSR11, ®); - rt2x00_set_field32(®, CSR11_CW_SELECT, 0); - rt2x00mmio_register_write(rt2x00dev, CSR11, reg); - - rt2x00mmio_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); - rt2x00_set_field32(®, CSR14_TSF_SYNC, 0); - rt2x00_set_field32(®, CSR14_TBCN, 0); - rt2x00_set_field32(®, CSR14_TCFP, 0); - rt2x00_set_field32(®, CSR14_TATIMW, 0); - rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); - rt2x00_set_field32(®, CSR14_CFP_COUNT_PRELOAD, 0); - rt2x00_set_field32(®, CSR14_TBCM_PRELOAD, 0); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - - rt2x00mmio_register_write(rt2x00dev, CNT3, 0); - - rt2x00mmio_register_read(rt2x00dev, TXCSR8, ®); - rt2x00_set_field32(®, TXCSR8_BBP_ID0, 10); - rt2x00_set_field32(®, TXCSR8_BBP_ID0_VALID, 1); - rt2x00_set_field32(®, TXCSR8_BBP_ID1, 11); - rt2x00_set_field32(®, TXCSR8_BBP_ID1_VALID, 1); - rt2x00_set_field32(®, TXCSR8_BBP_ID2, 13); - rt2x00_set_field32(®, TXCSR8_BBP_ID2_VALID, 1); - rt2x00_set_field32(®, TXCSR8_BBP_ID3, 12); - rt2x00_set_field32(®, TXCSR8_BBP_ID3_VALID, 1); - rt2x00mmio_register_write(rt2x00dev, TXCSR8, reg); - - rt2x00mmio_register_read(rt2x00dev, ARTCSR0, ®); - rt2x00_set_field32(®, ARTCSR0_ACK_CTS_1MBS, 112); - rt2x00_set_field32(®, ARTCSR0_ACK_CTS_2MBS, 56); - rt2x00_set_field32(®, ARTCSR0_ACK_CTS_5_5MBS, 20); - rt2x00_set_field32(®, ARTCSR0_ACK_CTS_11MBS, 10); - rt2x00mmio_register_write(rt2x00dev, ARTCSR0, reg); - - rt2x00mmio_register_read(rt2x00dev, ARTCSR1, ®); - rt2x00_set_field32(®, ARTCSR1_ACK_CTS_6MBS, 45); - rt2x00_set_field32(®, ARTCSR1_ACK_CTS_9MBS, 37); - rt2x00_set_field32(®, ARTCSR1_ACK_CTS_12MBS, 33); - rt2x00_set_field32(®, ARTCSR1_ACK_CTS_18MBS, 29); - rt2x00mmio_register_write(rt2x00dev, ARTCSR1, reg); - - rt2x00mmio_register_read(rt2x00dev, ARTCSR2, ®); - rt2x00_set_field32(®, ARTCSR2_ACK_CTS_24MBS, 29); - rt2x00_set_field32(®, ARTCSR2_ACK_CTS_36MBS, 25); - rt2x00_set_field32(®, ARTCSR2_ACK_CTS_48MBS, 25); - rt2x00_set_field32(®, ARTCSR2_ACK_CTS_54MBS, 25); - rt2x00mmio_register_write(rt2x00dev, ARTCSR2, reg); - - rt2x00mmio_register_read(rt2x00dev, RXCSR3, ®); - rt2x00_set_field32(®, RXCSR3_BBP_ID0, 47); /* CCK Signal */ - rt2x00_set_field32(®, RXCSR3_BBP_ID0_VALID, 1); - rt2x00_set_field32(®, RXCSR3_BBP_ID1, 51); /* Rssi */ - rt2x00_set_field32(®, RXCSR3_BBP_ID1_VALID, 1); - rt2x00_set_field32(®, RXCSR3_BBP_ID2, 42); /* OFDM Rate */ - rt2x00_set_field32(®, RXCSR3_BBP_ID2_VALID, 1); - rt2x00_set_field32(®, RXCSR3_BBP_ID3, 51); /* RSSI */ - rt2x00_set_field32(®, RXCSR3_BBP_ID3_VALID, 1); - rt2x00mmio_register_write(rt2x00dev, RXCSR3, reg); - - rt2x00mmio_register_read(rt2x00dev, PCICSR, ®); - rt2x00_set_field32(®, PCICSR_BIG_ENDIAN, 0); - rt2x00_set_field32(®, PCICSR_RX_TRESHOLD, 0); - rt2x00_set_field32(®, PCICSR_TX_TRESHOLD, 3); - rt2x00_set_field32(®, PCICSR_BURST_LENTH, 1); - rt2x00_set_field32(®, PCICSR_ENABLE_CLK, 1); - rt2x00_set_field32(®, PCICSR_READ_MULTIPLE, 1); - rt2x00_set_field32(®, PCICSR_WRITE_INVALID, 1); - rt2x00mmio_register_write(rt2x00dev, PCICSR, reg); - - rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); - - rt2x00mmio_register_write(rt2x00dev, GPIOCSR, 0x0000ff00); - rt2x00mmio_register_write(rt2x00dev, TESTCSR, 0x000000f0); - - if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) - return -EBUSY; - - rt2x00mmio_register_write(rt2x00dev, MACCSR0, 0x00213223); - rt2x00mmio_register_write(rt2x00dev, MACCSR1, 0x00235518); - - rt2x00mmio_register_read(rt2x00dev, MACCSR2, ®); - rt2x00_set_field32(®, MACCSR2_DELAY, 64); - rt2x00mmio_register_write(rt2x00dev, MACCSR2, reg); - - rt2x00mmio_register_read(rt2x00dev, RALINKCSR, ®); - rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA0, 17); - rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID0, 26); - rt2x00_set_field32(®, RALINKCSR_AR_BBP_VALID0, 1); - rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA1, 0); - rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID1, 26); - rt2x00_set_field32(®, RALINKCSR_AR_BBP_VALID1, 1); - rt2x00mmio_register_write(rt2x00dev, RALINKCSR, reg); - - rt2x00mmio_register_write(rt2x00dev, BBPCSR1, 0x82188200); - - rt2x00mmio_register_write(rt2x00dev, TXACKCSR0, 0x00000020); - - rt2x00mmio_register_read(rt2x00dev, CSR1, ®); - rt2x00_set_field32(®, CSR1_SOFT_RESET, 1); - rt2x00_set_field32(®, CSR1_BBP_RESET, 0); - rt2x00_set_field32(®, CSR1_HOST_READY, 0); - rt2x00mmio_register_write(rt2x00dev, CSR1, reg); - - rt2x00mmio_register_read(rt2x00dev, CSR1, ®); - rt2x00_set_field32(®, CSR1_SOFT_RESET, 0); - rt2x00_set_field32(®, CSR1_HOST_READY, 1); - rt2x00mmio_register_write(rt2x00dev, CSR1, reg); - - /* - * We must clear the FCS and FIFO error count. - * These registers are cleared on read, - * so we may pass a useless variable to store the value. - */ - rt2x00mmio_register_read(rt2x00dev, CNT0, ®); - rt2x00mmio_register_read(rt2x00dev, CNT4, ®); - - return 0; -} - -static int rt2500pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u8 value; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2500pci_bbp_read(rt2x00dev, 0, &value); - if ((value != 0xff) && (value != 0x00)) - return 0; - udelay(REGISTER_BUSY_DELAY); - } - - rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); - return -EACCES; -} - -static int rt2500pci_init_bbp(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u16 eeprom; - u8 reg_id; - u8 value; - - if (unlikely(rt2500pci_wait_bbp_ready(rt2x00dev))) - return -EACCES; - - rt2500pci_bbp_write(rt2x00dev, 3, 0x02); - rt2500pci_bbp_write(rt2x00dev, 4, 0x19); - rt2500pci_bbp_write(rt2x00dev, 14, 0x1c); - rt2500pci_bbp_write(rt2x00dev, 15, 0x30); - rt2500pci_bbp_write(rt2x00dev, 16, 0xac); - rt2500pci_bbp_write(rt2x00dev, 18, 0x18); - rt2500pci_bbp_write(rt2x00dev, 19, 0xff); - rt2500pci_bbp_write(rt2x00dev, 20, 0x1e); - rt2500pci_bbp_write(rt2x00dev, 21, 0x08); - rt2500pci_bbp_write(rt2x00dev, 22, 0x08); - rt2500pci_bbp_write(rt2x00dev, 23, 0x08); - rt2500pci_bbp_write(rt2x00dev, 24, 0x70); - rt2500pci_bbp_write(rt2x00dev, 25, 0x40); - rt2500pci_bbp_write(rt2x00dev, 26, 0x08); - rt2500pci_bbp_write(rt2x00dev, 27, 0x23); - rt2500pci_bbp_write(rt2x00dev, 30, 0x10); - rt2500pci_bbp_write(rt2x00dev, 31, 0x2b); - rt2500pci_bbp_write(rt2x00dev, 32, 0xb9); - rt2500pci_bbp_write(rt2x00dev, 34, 0x12); - rt2500pci_bbp_write(rt2x00dev, 35, 0x50); - rt2500pci_bbp_write(rt2x00dev, 39, 0xc4); - rt2500pci_bbp_write(rt2x00dev, 40, 0x02); - rt2500pci_bbp_write(rt2x00dev, 41, 0x60); - rt2500pci_bbp_write(rt2x00dev, 53, 0x10); - rt2500pci_bbp_write(rt2x00dev, 54, 0x18); - rt2500pci_bbp_write(rt2x00dev, 56, 0x08); - rt2500pci_bbp_write(rt2x00dev, 57, 0x10); - rt2500pci_bbp_write(rt2x00dev, 58, 0x08); - rt2500pci_bbp_write(rt2x00dev, 61, 0x6d); - rt2500pci_bbp_write(rt2x00dev, 62, 0x10); - - for (i = 0; i < EEPROM_BBP_SIZE; i++) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); - - if (eeprom != 0xffff && eeprom != 0x0000) { - reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); - value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - rt2500pci_bbp_write(rt2x00dev, reg_id, value); - } - } - - return 0; -} - -/* - * Device state switch handlers. - */ -static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - int mask = (state == STATE_RADIO_IRQ_OFF); - u32 reg; - unsigned long flags; - - /* - * When interrupts are being enabled, the interrupt registers - * should clear the register to assure a clean state. - */ - if (state == STATE_RADIO_IRQ_ON) { - rt2x00mmio_register_read(rt2x00dev, CSR7, ®); - rt2x00mmio_register_write(rt2x00dev, CSR7, reg); - } - - /* - * Only toggle the interrupts bits we are going to use. - * Non-checked interrupt bits are disabled by default. - */ - spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); - - rt2x00mmio_register_read(rt2x00dev, CSR8, ®); - rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); - rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); - rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, mask); - rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); - rt2x00_set_field32(®, CSR8_RXDONE, mask); - rt2x00mmio_register_write(rt2x00dev, CSR8, reg); - - spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); - - if (state == STATE_RADIO_IRQ_OFF) { - /* - * Ensure that all tasklets are finished. - */ - tasklet_kill(&rt2x00dev->txstatus_tasklet); - tasklet_kill(&rt2x00dev->rxdone_tasklet); - tasklet_kill(&rt2x00dev->tbtt_tasklet); - } -} - -static int rt2500pci_enable_radio(struct rt2x00_dev *rt2x00dev) -{ - /* - * Initialize all registers. - */ - if (unlikely(rt2500pci_init_queues(rt2x00dev) || - rt2500pci_init_registers(rt2x00dev) || - rt2500pci_init_bbp(rt2x00dev))) - return -EIO; - - return 0; -} - -static void rt2500pci_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - /* - * Disable power - */ - rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0); -} - -static int rt2500pci_set_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - u32 reg, reg2; - unsigned int i; - char put_to_sleep; - char bbp_state; - char rf_state; - - put_to_sleep = (state != STATE_AWAKE); - - rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®); - rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1); - rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state); - rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state); - rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep); - rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); - - /* - * Device is not guaranteed to be in the requested state yet. - * We must wait until the register indicates that the - * device has entered the correct state. - */ - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®2); - bbp_state = rt2x00_get_field32(reg2, PWRCSR1_BBP_CURR_STATE); - rf_state = rt2x00_get_field32(reg2, PWRCSR1_RF_CURR_STATE); - if (bbp_state == state && rf_state == state) - return 0; - rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); - msleep(10); - } - - return -EBUSY; -} - -static int rt2500pci_set_device_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - int retval = 0; - - switch (state) { - case STATE_RADIO_ON: - retval = rt2500pci_enable_radio(rt2x00dev); - break; - case STATE_RADIO_OFF: - rt2500pci_disable_radio(rt2x00dev); - break; - case STATE_RADIO_IRQ_ON: - case STATE_RADIO_IRQ_OFF: - rt2500pci_toggle_irq(rt2x00dev, state); - break; - case STATE_DEEP_SLEEP: - case STATE_SLEEP: - case STATE_STANDBY: - case STATE_AWAKE: - retval = rt2500pci_set_state(rt2x00dev, state); - break; - default: - retval = -ENOTSUPP; - break; - } - - if (unlikely(retval)) - rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", - state, retval); - - return retval; -} - -/* - * TX descriptor initialization - */ -static void rt2500pci_write_tx_desc(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - __le32 *txd = entry_priv->desc; - u32 word; - - /* - * Start writing the descriptor words. - */ - rt2x00_desc_read(txd, 1, &word); - rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); - rt2x00_desc_write(txd, 1, word); - - rt2x00_desc_read(txd, 2, &word); - rt2x00_set_field32(&word, TXD_W2_IV_OFFSET, IEEE80211_HEADER); - rt2x00_set_field32(&word, TXD_W2_AIFS, entry->queue->aifs); - rt2x00_set_field32(&word, TXD_W2_CWMIN, entry->queue->cw_min); - rt2x00_set_field32(&word, TXD_W2_CWMAX, entry->queue->cw_max); - rt2x00_desc_write(txd, 2, word); - - rt2x00_desc_read(txd, 3, &word); - rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, txdesc->u.plcp.signal); - rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, txdesc->u.plcp.service); - rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW, - txdesc->u.plcp.length_low); - rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH, - txdesc->u.plcp.length_high); - rt2x00_desc_write(txd, 3, word); - - rt2x00_desc_read(txd, 10, &word); - rt2x00_set_field32(&word, TXD_W10_RTS, - test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)); - rt2x00_desc_write(txd, 10, word); - - /* - * Writing TXD word 0 must the last to prevent a race condition with - * the device, whereby the device may take hold of the TXD before we - * finished updating it. - */ - rt2x00_desc_read(txd, 0, &word); - rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); - rt2x00_set_field32(&word, TXD_W0_VALID, 1); - rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, - test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_ACK, - test_bit(ENTRY_TXD_ACK, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, - test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_OFDM, - (txdesc->rate_mode == RATE_MODE_OFDM)); - rt2x00_set_field32(&word, TXD_W0_CIPHER_OWNER, 1); - rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); - rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, - test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); - rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); - rt2x00_desc_write(txd, 0, word); - - /* - * Register descriptor details in skb frame descriptor. - */ - skbdesc->desc = txd; - skbdesc->desc_len = TXD_DESC_SIZE; -} - -/* - * TX data initialization - */ -static void rt2500pci_write_beacon(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - u32 reg; - - /* - * Disable beaconing while we are reloading the beacon data, - * otherwise we might be sending out invalid data. - */ - rt2x00mmio_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - - if (rt2x00queue_map_txskb(entry)) { - rt2x00_err(rt2x00dev, "Fail to map beacon, aborting\n"); - goto out; - } - - /* - * Write the TX descriptor for the beacon. - */ - rt2500pci_write_tx_desc(entry, txdesc); - - /* - * Dump beacon to userspace through debugfs. - */ - rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); -out: - /* - * Enable beaconing again. - */ - rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); - rt2x00mmio_register_write(rt2x00dev, CSR14, reg); -} - -/* - * RX control handlers - */ -static void rt2500pci_fill_rxdone(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - u32 word0; - u32 word2; - - rt2x00_desc_read(entry_priv->desc, 0, &word0); - rt2x00_desc_read(entry_priv->desc, 2, &word2); - - if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; - if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; - - /* - * Obtain the status about this packet. - * When frame was received with an OFDM bitrate, - * the signal is the PLCP value. If it was received with - * a CCK bitrate the signal is the rate in 100kbit/s. - */ - rxdesc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL); - rxdesc->rssi = rt2x00_get_field32(word2, RXD_W2_RSSI) - - entry->queue->rt2x00dev->rssi_offset; - rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); - - if (rt2x00_get_field32(word0, RXD_W0_OFDM)) - rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; - else - rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; - if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) - rxdesc->dev_flags |= RXDONE_MY_BSS; -} - -/* - * Interrupt functions. - */ -static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev, - const enum data_queue_qid queue_idx) -{ - struct data_queue *queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); - struct queue_entry_priv_mmio *entry_priv; - struct queue_entry *entry; - struct txdone_entry_desc txdesc; - u32 word; - - while (!rt2x00queue_empty(queue)) { - entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - entry_priv = entry->priv_data; - rt2x00_desc_read(entry_priv->desc, 0, &word); - - if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || - !rt2x00_get_field32(word, TXD_W0_VALID)) - break; - - /* - * Obtain the status about this packet. - */ - txdesc.flags = 0; - switch (rt2x00_get_field32(word, TXD_W0_RESULT)) { - case 0: /* Success */ - case 1: /* Success with retry */ - __set_bit(TXDONE_SUCCESS, &txdesc.flags); - break; - case 2: /* Failure, excessive retries */ - __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); - /* Don't break, this is a failed frame! */ - default: /* Failure */ - __set_bit(TXDONE_FAILURE, &txdesc.flags); - } - txdesc.retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); - - rt2x00lib_txdone(entry, &txdesc); - } -} - -static inline void rt2500pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, - struct rt2x00_field32 irq_field) -{ - u32 reg; - - /* - * Enable a single interrupt. The interrupt mask register - * access needs locking. - */ - spin_lock_irq(&rt2x00dev->irqmask_lock); - - rt2x00mmio_register_read(rt2x00dev, CSR8, ®); - rt2x00_set_field32(®, irq_field, 0); - rt2x00mmio_register_write(rt2x00dev, CSR8, reg); - - spin_unlock_irq(&rt2x00dev->irqmask_lock); -} - -static void rt2500pci_txstatus_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - u32 reg; - - /* - * Handle all tx queues. - */ - rt2500pci_txdone(rt2x00dev, QID_ATIM); - rt2500pci_txdone(rt2x00dev, QID_AC_VO); - rt2500pci_txdone(rt2x00dev, QID_AC_VI); - - /* - * Enable all TXDONE interrupts again. - */ - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) { - spin_lock_irq(&rt2x00dev->irqmask_lock); - - rt2x00mmio_register_read(rt2x00dev, CSR8, ®); - rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); - rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); - rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); - rt2x00mmio_register_write(rt2x00dev, CSR8, reg); - - spin_unlock_irq(&rt2x00dev->irqmask_lock); - } -} - -static void rt2500pci_tbtt_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - rt2x00lib_beacondone(rt2x00dev); - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2500pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); -} - -static void rt2500pci_rxdone_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - if (rt2x00mmio_rxdone(rt2x00dev)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2500pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); -} - -static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance) -{ - struct rt2x00_dev *rt2x00dev = dev_instance; - u32 reg, mask; - - /* - * Get the interrupt sources & saved to local variable. - * Write register value back to clear pending interrupts. - */ - rt2x00mmio_register_read(rt2x00dev, CSR7, ®); - rt2x00mmio_register_write(rt2x00dev, CSR7, reg); - - if (!reg) - return IRQ_NONE; - - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return IRQ_HANDLED; - - mask = reg; - - /* - * Schedule tasklets for interrupt handling. - */ - if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) - tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); - - if (rt2x00_get_field32(reg, CSR7_RXDONE)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - - if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) || - rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) || - rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) { - tasklet_schedule(&rt2x00dev->txstatus_tasklet); - /* - * Mask out all txdone interrupts. - */ - rt2x00_set_field32(&mask, CSR8_TXDONE_TXRING, 1); - rt2x00_set_field32(&mask, CSR8_TXDONE_ATIMRING, 1); - rt2x00_set_field32(&mask, CSR8_TXDONE_PRIORING, 1); - } - - /* - * Disable all interrupts for which a tasklet was scheduled right now, - * the tasklet will reenable the appropriate interrupts. - */ - spin_lock(&rt2x00dev->irqmask_lock); - - rt2x00mmio_register_read(rt2x00dev, CSR8, ®); - reg |= mask; - rt2x00mmio_register_write(rt2x00dev, CSR8, reg); - - spin_unlock(&rt2x00dev->irqmask_lock); - - return IRQ_HANDLED; -} - -/* - * Device probe functions. - */ -static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) -{ - struct eeprom_93cx6 eeprom; - u32 reg; - u16 word; - u8 *mac; - - rt2x00mmio_register_read(rt2x00dev, CSR21, ®); - - eeprom.data = rt2x00dev; - eeprom.register_read = rt2500pci_eepromregister_read; - eeprom.register_write = rt2500pci_eepromregister_write; - eeprom.width = rt2x00_get_field32(reg, CSR21_TYPE_93C46) ? - PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; - eeprom.reg_data_in = 0; - eeprom.reg_data_out = 0; - eeprom.reg_data_clock = 0; - eeprom.reg_chip_select = 0; - - eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, - EEPROM_SIZE / sizeof(u16)); - - /* - * Start validation of the data that has been read. - */ - mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); - rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, - ANTENNA_SW_DIVERSITY); - rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, - ANTENNA_SW_DIVERSITY); - rt2x00_set_field16(&word, EEPROM_ANTENNA_LED_MODE, - LED_MODE_DEFAULT); - rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); - rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); - rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2522); - rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); - rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); - rt2x00_set_field16(&word, EEPROM_NIC_DYN_BBP_TUNE, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CCK_TX_POWER, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); - rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_CALIBRATE_OFFSET_RSSI, - DEFAULT_RSSI_OFFSET); - rt2x00_eeprom_write(rt2x00dev, EEPROM_CALIBRATE_OFFSET, word); - rt2x00_eeprom_dbg(rt2x00dev, "Calibrate offset: 0x%04x\n", - word); - } - - return 0; -} - -static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - u16 value; - u16 eeprom; - - /* - * Read EEPROM word for configuration. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); - - /* - * Identify RF chipset. - */ - value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); - rt2x00mmio_register_read(rt2x00dev, CSR0, ®); - rt2x00_set_chip(rt2x00dev, RT2560, value, - rt2x00_get_field32(reg, CSR0_REVISION)); - - if (!rt2x00_rf(rt2x00dev, RF2522) && - !rt2x00_rf(rt2x00dev, RF2523) && - !rt2x00_rf(rt2x00dev, RF2524) && - !rt2x00_rf(rt2x00dev, RF2525) && - !rt2x00_rf(rt2x00dev, RF2525E) && - !rt2x00_rf(rt2x00dev, RF5222)) { - rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); - return -ENODEV; - } - - /* - * Identify default antenna configuration. - */ - rt2x00dev->default_ant.tx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); - rt2x00dev->default_ant.rx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); - - /* - * Store led mode, for correct led behaviour. - */ -#ifdef CONFIG_RT2X00_LIB_LEDS - value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); - - rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); - if (value == LED_MODE_TXRX_ACTIVITY || - value == LED_MODE_DEFAULT || - value == LED_MODE_ASUS) - rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_qual, - LED_TYPE_ACTIVITY); -#endif /* CONFIG_RT2X00_LIB_LEDS */ - - /* - * Detect if this device has an hardware controlled radio. - */ - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) { - __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); - /* - * On this device RFKILL initialized during probe does not work. - */ - __set_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags); - } - - /* - * Check if the BBP tuning should be enabled. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); - if (!rt2x00_get_field16(eeprom, EEPROM_NIC_DYN_BBP_TUNE)) - __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); - - /* - * Read the RSSI <-> dBm offset information. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &eeprom); - rt2x00dev->rssi_offset = - rt2x00_get_field16(eeprom, EEPROM_CALIBRATE_OFFSET_RSSI); - - return 0; -} - -/* - * RF value list for RF2522 - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_bg_2522[] = { - { 1, 0x00002050, 0x000c1fda, 0x00000101, 0 }, - { 2, 0x00002050, 0x000c1fee, 0x00000101, 0 }, - { 3, 0x00002050, 0x000c2002, 0x00000101, 0 }, - { 4, 0x00002050, 0x000c2016, 0x00000101, 0 }, - { 5, 0x00002050, 0x000c202a, 0x00000101, 0 }, - { 6, 0x00002050, 0x000c203e, 0x00000101, 0 }, - { 7, 0x00002050, 0x000c2052, 0x00000101, 0 }, - { 8, 0x00002050, 0x000c2066, 0x00000101, 0 }, - { 9, 0x00002050, 0x000c207a, 0x00000101, 0 }, - { 10, 0x00002050, 0x000c208e, 0x00000101, 0 }, - { 11, 0x00002050, 0x000c20a2, 0x00000101, 0 }, - { 12, 0x00002050, 0x000c20b6, 0x00000101, 0 }, - { 13, 0x00002050, 0x000c20ca, 0x00000101, 0 }, - { 14, 0x00002050, 0x000c20fa, 0x00000101, 0 }, -}; - -/* - * RF value list for RF2523 - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_bg_2523[] = { - { 1, 0x00022010, 0x00000c9e, 0x000e0111, 0x00000a1b }, - { 2, 0x00022010, 0x00000ca2, 0x000e0111, 0x00000a1b }, - { 3, 0x00022010, 0x00000ca6, 0x000e0111, 0x00000a1b }, - { 4, 0x00022010, 0x00000caa, 0x000e0111, 0x00000a1b }, - { 5, 0x00022010, 0x00000cae, 0x000e0111, 0x00000a1b }, - { 6, 0x00022010, 0x00000cb2, 0x000e0111, 0x00000a1b }, - { 7, 0x00022010, 0x00000cb6, 0x000e0111, 0x00000a1b }, - { 8, 0x00022010, 0x00000cba, 0x000e0111, 0x00000a1b }, - { 9, 0x00022010, 0x00000cbe, 0x000e0111, 0x00000a1b }, - { 10, 0x00022010, 0x00000d02, 0x000e0111, 0x00000a1b }, - { 11, 0x00022010, 0x00000d06, 0x000e0111, 0x00000a1b }, - { 12, 0x00022010, 0x00000d0a, 0x000e0111, 0x00000a1b }, - { 13, 0x00022010, 0x00000d0e, 0x000e0111, 0x00000a1b }, - { 14, 0x00022010, 0x00000d1a, 0x000e0111, 0x00000a03 }, -}; - -/* - * RF value list for RF2524 - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_bg_2524[] = { - { 1, 0x00032020, 0x00000c9e, 0x00000101, 0x00000a1b }, - { 2, 0x00032020, 0x00000ca2, 0x00000101, 0x00000a1b }, - { 3, 0x00032020, 0x00000ca6, 0x00000101, 0x00000a1b }, - { 4, 0x00032020, 0x00000caa, 0x00000101, 0x00000a1b }, - { 5, 0x00032020, 0x00000cae, 0x00000101, 0x00000a1b }, - { 6, 0x00032020, 0x00000cb2, 0x00000101, 0x00000a1b }, - { 7, 0x00032020, 0x00000cb6, 0x00000101, 0x00000a1b }, - { 8, 0x00032020, 0x00000cba, 0x00000101, 0x00000a1b }, - { 9, 0x00032020, 0x00000cbe, 0x00000101, 0x00000a1b }, - { 10, 0x00032020, 0x00000d02, 0x00000101, 0x00000a1b }, - { 11, 0x00032020, 0x00000d06, 0x00000101, 0x00000a1b }, - { 12, 0x00032020, 0x00000d0a, 0x00000101, 0x00000a1b }, - { 13, 0x00032020, 0x00000d0e, 0x00000101, 0x00000a1b }, - { 14, 0x00032020, 0x00000d1a, 0x00000101, 0x00000a03 }, -}; - -/* - * RF value list for RF2525 - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_bg_2525[] = { - { 1, 0x00022020, 0x00080c9e, 0x00060111, 0x00000a1b }, - { 2, 0x00022020, 0x00080ca2, 0x00060111, 0x00000a1b }, - { 3, 0x00022020, 0x00080ca6, 0x00060111, 0x00000a1b }, - { 4, 0x00022020, 0x00080caa, 0x00060111, 0x00000a1b }, - { 5, 0x00022020, 0x00080cae, 0x00060111, 0x00000a1b }, - { 6, 0x00022020, 0x00080cb2, 0x00060111, 0x00000a1b }, - { 7, 0x00022020, 0x00080cb6, 0x00060111, 0x00000a1b }, - { 8, 0x00022020, 0x00080cba, 0x00060111, 0x00000a1b }, - { 9, 0x00022020, 0x00080cbe, 0x00060111, 0x00000a1b }, - { 10, 0x00022020, 0x00080d02, 0x00060111, 0x00000a1b }, - { 11, 0x00022020, 0x00080d06, 0x00060111, 0x00000a1b }, - { 12, 0x00022020, 0x00080d0a, 0x00060111, 0x00000a1b }, - { 13, 0x00022020, 0x00080d0e, 0x00060111, 0x00000a1b }, - { 14, 0x00022020, 0x00080d1a, 0x00060111, 0x00000a03 }, -}; - -/* - * RF value list for RF2525e - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_bg_2525e[] = { - { 1, 0x00022020, 0x00081136, 0x00060111, 0x00000a0b }, - { 2, 0x00022020, 0x0008113a, 0x00060111, 0x00000a0b }, - { 3, 0x00022020, 0x0008113e, 0x00060111, 0x00000a0b }, - { 4, 0x00022020, 0x00081182, 0x00060111, 0x00000a0b }, - { 5, 0x00022020, 0x00081186, 0x00060111, 0x00000a0b }, - { 6, 0x00022020, 0x0008118a, 0x00060111, 0x00000a0b }, - { 7, 0x00022020, 0x0008118e, 0x00060111, 0x00000a0b }, - { 8, 0x00022020, 0x00081192, 0x00060111, 0x00000a0b }, - { 9, 0x00022020, 0x00081196, 0x00060111, 0x00000a0b }, - { 10, 0x00022020, 0x0008119a, 0x00060111, 0x00000a0b }, - { 11, 0x00022020, 0x0008119e, 0x00060111, 0x00000a0b }, - { 12, 0x00022020, 0x000811a2, 0x00060111, 0x00000a0b }, - { 13, 0x00022020, 0x000811a6, 0x00060111, 0x00000a0b }, - { 14, 0x00022020, 0x000811ae, 0x00060111, 0x00000a1b }, -}; - -/* - * RF value list for RF5222 - * Supports: 2.4 GHz & 5.2 GHz - */ -static const struct rf_channel rf_vals_5222[] = { - { 1, 0x00022020, 0x00001136, 0x00000101, 0x00000a0b }, - { 2, 0x00022020, 0x0000113a, 0x00000101, 0x00000a0b }, - { 3, 0x00022020, 0x0000113e, 0x00000101, 0x00000a0b }, - { 4, 0x00022020, 0x00001182, 0x00000101, 0x00000a0b }, - { 5, 0x00022020, 0x00001186, 0x00000101, 0x00000a0b }, - { 6, 0x00022020, 0x0000118a, 0x00000101, 0x00000a0b }, - { 7, 0x00022020, 0x0000118e, 0x00000101, 0x00000a0b }, - { 8, 0x00022020, 0x00001192, 0x00000101, 0x00000a0b }, - { 9, 0x00022020, 0x00001196, 0x00000101, 0x00000a0b }, - { 10, 0x00022020, 0x0000119a, 0x00000101, 0x00000a0b }, - { 11, 0x00022020, 0x0000119e, 0x00000101, 0x00000a0b }, - { 12, 0x00022020, 0x000011a2, 0x00000101, 0x00000a0b }, - { 13, 0x00022020, 0x000011a6, 0x00000101, 0x00000a0b }, - { 14, 0x00022020, 0x000011ae, 0x00000101, 0x00000a1b }, - - /* 802.11 UNI / HyperLan 2 */ - { 36, 0x00022010, 0x00018896, 0x00000101, 0x00000a1f }, - { 40, 0x00022010, 0x0001889a, 0x00000101, 0x00000a1f }, - { 44, 0x00022010, 0x0001889e, 0x00000101, 0x00000a1f }, - { 48, 0x00022010, 0x000188a2, 0x00000101, 0x00000a1f }, - { 52, 0x00022010, 0x000188a6, 0x00000101, 0x00000a1f }, - { 66, 0x00022010, 0x000188aa, 0x00000101, 0x00000a1f }, - { 60, 0x00022010, 0x000188ae, 0x00000101, 0x00000a1f }, - { 64, 0x00022010, 0x000188b2, 0x00000101, 0x00000a1f }, - - /* 802.11 HyperLan 2 */ - { 100, 0x00022010, 0x00008802, 0x00000101, 0x00000a0f }, - { 104, 0x00022010, 0x00008806, 0x00000101, 0x00000a0f }, - { 108, 0x00022010, 0x0000880a, 0x00000101, 0x00000a0f }, - { 112, 0x00022010, 0x0000880e, 0x00000101, 0x00000a0f }, - { 116, 0x00022010, 0x00008812, 0x00000101, 0x00000a0f }, - { 120, 0x00022010, 0x00008816, 0x00000101, 0x00000a0f }, - { 124, 0x00022010, 0x0000881a, 0x00000101, 0x00000a0f }, - { 128, 0x00022010, 0x0000881e, 0x00000101, 0x00000a0f }, - { 132, 0x00022010, 0x00008822, 0x00000101, 0x00000a0f }, - { 136, 0x00022010, 0x00008826, 0x00000101, 0x00000a0f }, - - /* 802.11 UNII */ - { 140, 0x00022010, 0x0000882a, 0x00000101, 0x00000a0f }, - { 149, 0x00022020, 0x000090a6, 0x00000101, 0x00000a07 }, - { 153, 0x00022020, 0x000090ae, 0x00000101, 0x00000a07 }, - { 157, 0x00022020, 0x000090b6, 0x00000101, 0x00000a07 }, - { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 }, -}; - -static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) -{ - struct hw_mode_spec *spec = &rt2x00dev->spec; - struct channel_info *info; - char *tx_power; - unsigned int i; - - /* - * Initialize all hw fields. - */ - ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); - ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); - ieee80211_hw_set(rt2x00dev->hw, HOST_BROADCAST_PS_BUFFERING); - ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); - - SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); - SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, - rt2x00_eeprom_addr(rt2x00dev, - EEPROM_MAC_ADDR_0)); - - /* - * Disable powersaving as default. - */ - rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - /* - * Initialize hw_mode information. - */ - spec->supported_bands = SUPPORT_BAND_2GHZ; - spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - - if (rt2x00_rf(rt2x00dev, RF2522)) { - spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); - spec->channels = rf_vals_bg_2522; - } else if (rt2x00_rf(rt2x00dev, RF2523)) { - spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523); - spec->channels = rf_vals_bg_2523; - } else if (rt2x00_rf(rt2x00dev, RF2524)) { - spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524); - spec->channels = rf_vals_bg_2524; - } else if (rt2x00_rf(rt2x00dev, RF2525)) { - spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525); - spec->channels = rf_vals_bg_2525; - } else if (rt2x00_rf(rt2x00dev, RF2525E)) { - spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e); - spec->channels = rf_vals_bg_2525e; - } else if (rt2x00_rf(rt2x00dev, RF5222)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; - spec->num_channels = ARRAY_SIZE(rf_vals_5222); - spec->channels = rf_vals_5222; - } - - /* - * Create channel information array - */ - info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - spec->channels_info = info; - - tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); - for (i = 0; i < 14; i++) { - info[i].max_power = MAX_TXPOWER; - info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); - } - - if (spec->num_channels > 14) { - for (i = 14; i < spec->num_channels; i++) { - info[i].max_power = MAX_TXPOWER; - info[i].default_power1 = DEFAULT_TXPOWER; - } - } - - return 0; -} - -static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev) -{ - int retval; - u32 reg; - - /* - * Allocate eeprom data. - */ - retval = rt2500pci_validate_eeprom(rt2x00dev); - if (retval) - return retval; - - retval = rt2500pci_init_eeprom(rt2x00dev); - if (retval) - return retval; - - /* - * Enable rfkill polling by setting GPIO direction of the - * rfkill switch GPIO pin correctly. - */ - rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); - rt2x00_set_field32(®, GPIOCSR_DIR0, 1); - rt2x00mmio_register_write(rt2x00dev, GPIOCSR, reg); - - /* - * Initialize hw specifications. - */ - retval = rt2500pci_probe_hw_mode(rt2x00dev); - if (retval) - return retval; - - /* - * This device requires the atim queue and DMA-mapped skbs. - */ - __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); - - /* - * Set the rssi offset. - */ - rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; - - return 0; -} - -/* - * IEEE80211 stack callback functions. - */ -static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u64 tsf; - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, CSR17, ®); - tsf = (u64) rt2x00_get_field32(reg, CSR17_HIGH_TSFTIMER) << 32; - rt2x00mmio_register_read(rt2x00dev, CSR16, ®); - tsf |= rt2x00_get_field32(reg, CSR16_LOW_TSFTIMER); - - return tsf; -} - -static int rt2500pci_tx_last_beacon(struct ieee80211_hw *hw) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, CSR15, ®); - return rt2x00_get_field32(reg, CSR15_BEACON_SENT); -} - -static const struct ieee80211_ops rt2500pci_mac80211_ops = { - .tx = rt2x00mac_tx, - .start = rt2x00mac_start, - .stop = rt2x00mac_stop, - .add_interface = rt2x00mac_add_interface, - .remove_interface = rt2x00mac_remove_interface, - .config = rt2x00mac_config, - .configure_filter = rt2x00mac_configure_filter, - .sw_scan_start = rt2x00mac_sw_scan_start, - .sw_scan_complete = rt2x00mac_sw_scan_complete, - .get_stats = rt2x00mac_get_stats, - .bss_info_changed = rt2x00mac_bss_info_changed, - .conf_tx = rt2x00mac_conf_tx, - .get_tsf = rt2500pci_get_tsf, - .tx_last_beacon = rt2500pci_tx_last_beacon, - .rfkill_poll = rt2x00mac_rfkill_poll, - .flush = rt2x00mac_flush, - .set_antenna = rt2x00mac_set_antenna, - .get_antenna = rt2x00mac_get_antenna, - .get_ringparam = rt2x00mac_get_ringparam, - .tx_frames_pending = rt2x00mac_tx_frames_pending, -}; - -static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { - .irq_handler = rt2500pci_interrupt, - .txstatus_tasklet = rt2500pci_txstatus_tasklet, - .tbtt_tasklet = rt2500pci_tbtt_tasklet, - .rxdone_tasklet = rt2500pci_rxdone_tasklet, - .probe_hw = rt2500pci_probe_hw, - .initialize = rt2x00mmio_initialize, - .uninitialize = rt2x00mmio_uninitialize, - .get_entry_state = rt2500pci_get_entry_state, - .clear_entry = rt2500pci_clear_entry, - .set_device_state = rt2500pci_set_device_state, - .rfkill_poll = rt2500pci_rfkill_poll, - .link_stats = rt2500pci_link_stats, - .reset_tuner = rt2500pci_reset_tuner, - .link_tuner = rt2500pci_link_tuner, - .start_queue = rt2500pci_start_queue, - .kick_queue = rt2500pci_kick_queue, - .stop_queue = rt2500pci_stop_queue, - .flush_queue = rt2x00mmio_flush_queue, - .write_tx_desc = rt2500pci_write_tx_desc, - .write_beacon = rt2500pci_write_beacon, - .fill_rxdone = rt2500pci_fill_rxdone, - .config_filter = rt2500pci_config_filter, - .config_intf = rt2500pci_config_intf, - .config_erp = rt2500pci_config_erp, - .config_ant = rt2500pci_config_ant, - .config = rt2500pci_config, -}; - -static void rt2500pci_queue_init(struct data_queue *queue) -{ - switch (queue->qid) { - case QID_RX: - queue->limit = 32; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = RXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - queue->limit = 32; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_BEACON: - queue->limit = 1; - queue->data_size = MGMT_FRAME_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_ATIM: - queue->limit = 8; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - default: - BUG(); - break; - } -} - -static const struct rt2x00_ops rt2500pci_ops = { - .name = KBUILD_MODNAME, - .max_ap_intf = 1, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .queue_init = rt2500pci_queue_init, - .lib = &rt2500pci_rt2x00_ops, - .hw = &rt2500pci_mac80211_ops, -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2500pci_rt2x00debug, -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -}; - -/* - * RT2500pci module information. - */ -static const struct pci_device_id rt2500pci_device_table[] = { - { PCI_DEVICE(0x1814, 0x0201) }, - { 0, } -}; - -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("Ralink RT2500 PCI & PCMCIA Wireless LAN driver."); -MODULE_SUPPORTED_DEVICE("Ralink RT2560 PCI & PCMCIA chipset based cards"); -MODULE_DEVICE_TABLE(pci, rt2500pci_device_table); -MODULE_LICENSE("GPL"); - -static int rt2500pci_probe(struct pci_dev *pci_dev, - const struct pci_device_id *id) -{ - return rt2x00pci_probe(pci_dev, &rt2500pci_ops); -} - -static struct pci_driver rt2500pci_driver = { - .name = KBUILD_MODNAME, - .id_table = rt2500pci_device_table, - .probe = rt2500pci_probe, - .remove = rt2x00pci_remove, - .suspend = rt2x00pci_suspend, - .resume = rt2x00pci_resume, -}; - -module_pci_driver(rt2500pci_driver); diff --git a/drivers/net/wireless/rt2x00/rt2500pci.h b/drivers/net/wireless/rt2x00/rt2500pci.h deleted file mode 100644 index 573e87bcc553..000000000000 --- a/drivers/net/wireless/rt2x00/rt2500pci.h +++ /dev/null @@ -1,1235 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2500pci - Abstract: Data structures and registers for the rt2500pci module. - Supported chipsets: RT2560. - */ - -#ifndef RT2500PCI_H -#define RT2500PCI_H - -/* - * RF chip defines. - */ -#define RF2522 0x0000 -#define RF2523 0x0001 -#define RF2524 0x0002 -#define RF2525 0x0003 -#define RF2525E 0x0004 -#define RF5222 0x0010 - -/* - * RT2560 version - */ -#define RT2560_VERSION_B 2 -#define RT2560_VERSION_C 3 -#define RT2560_VERSION_D 4 - -/* - * Signal information. - * Default offset is required for RSSI <-> dBm conversion. - */ -#define DEFAULT_RSSI_OFFSET 121 - -/* - * Register layout information. - */ -#define CSR_REG_BASE 0x0000 -#define CSR_REG_SIZE 0x0174 -#define EEPROM_BASE 0x0000 -#define EEPROM_SIZE 0x0200 -#define BBP_BASE 0x0000 -#define BBP_SIZE 0x0040 -#define RF_BASE 0x0004 -#define RF_SIZE 0x0010 - -/* - * Number of TX queues. - */ -#define NUM_TX_QUEUES 2 - -/* - * Control/Status Registers(CSR). - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * CSR0: ASIC revision number. - */ -#define CSR0 0x0000 -#define CSR0_REVISION FIELD32(0x0000ffff) - -/* - * CSR1: System control register. - * SOFT_RESET: Software reset, 1: reset, 0: normal. - * BBP_RESET: Hardware reset, 1: reset, 0, release. - * HOST_READY: Host ready after initialization. - */ -#define CSR1 0x0004 -#define CSR1_SOFT_RESET FIELD32(0x00000001) -#define CSR1_BBP_RESET FIELD32(0x00000002) -#define CSR1_HOST_READY FIELD32(0x00000004) - -/* - * CSR2: System admin status register (invalid). - */ -#define CSR2 0x0008 - -/* - * CSR3: STA MAC address register 0. - */ -#define CSR3 0x000c -#define CSR3_BYTE0 FIELD32(0x000000ff) -#define CSR3_BYTE1 FIELD32(0x0000ff00) -#define CSR3_BYTE2 FIELD32(0x00ff0000) -#define CSR3_BYTE3 FIELD32(0xff000000) - -/* - * CSR4: STA MAC address register 1. - */ -#define CSR4 0x0010 -#define CSR4_BYTE4 FIELD32(0x000000ff) -#define CSR4_BYTE5 FIELD32(0x0000ff00) - -/* - * CSR5: BSSID register 0. - */ -#define CSR5 0x0014 -#define CSR5_BYTE0 FIELD32(0x000000ff) -#define CSR5_BYTE1 FIELD32(0x0000ff00) -#define CSR5_BYTE2 FIELD32(0x00ff0000) -#define CSR5_BYTE3 FIELD32(0xff000000) - -/* - * CSR6: BSSID register 1. - */ -#define CSR6 0x0018 -#define CSR6_BYTE4 FIELD32(0x000000ff) -#define CSR6_BYTE5 FIELD32(0x0000ff00) - -/* - * CSR7: Interrupt source register. - * Write 1 to clear. - * TBCN_EXPIRE: Beacon timer expired interrupt. - * TWAKE_EXPIRE: Wakeup timer expired interrupt. - * TATIMW_EXPIRE: Timer of atim window expired interrupt. - * TXDONE_TXRING: Tx ring transmit done interrupt. - * TXDONE_ATIMRING: Atim ring transmit done interrupt. - * TXDONE_PRIORING: Priority ring transmit done interrupt. - * RXDONE: Receive done interrupt. - * DECRYPTION_DONE: Decryption done interrupt. - * ENCRYPTION_DONE: Encryption done interrupt. - * UART1_TX_TRESHOLD: UART1 TX reaches threshold. - * UART1_RX_TRESHOLD: UART1 RX reaches threshold. - * UART1_IDLE_TRESHOLD: UART1 IDLE over threshold. - * UART1_TX_BUFF_ERROR: UART1 TX buffer error. - * UART1_RX_BUFF_ERROR: UART1 RX buffer error. - * UART2_TX_TRESHOLD: UART2 TX reaches threshold. - * UART2_RX_TRESHOLD: UART2 RX reaches threshold. - * UART2_IDLE_TRESHOLD: UART2 IDLE over threshold. - * UART2_TX_BUFF_ERROR: UART2 TX buffer error. - * UART2_RX_BUFF_ERROR: UART2 RX buffer error. - * TIMER_CSR3_EXPIRE: TIMECSR3 timer expired (802.1H quiet period). - - */ -#define CSR7 0x001c -#define CSR7_TBCN_EXPIRE FIELD32(0x00000001) -#define CSR7_TWAKE_EXPIRE FIELD32(0x00000002) -#define CSR7_TATIMW_EXPIRE FIELD32(0x00000004) -#define CSR7_TXDONE_TXRING FIELD32(0x00000008) -#define CSR7_TXDONE_ATIMRING FIELD32(0x00000010) -#define CSR7_TXDONE_PRIORING FIELD32(0x00000020) -#define CSR7_RXDONE FIELD32(0x00000040) -#define CSR7_DECRYPTION_DONE FIELD32(0x00000080) -#define CSR7_ENCRYPTION_DONE FIELD32(0x00000100) -#define CSR7_UART1_TX_TRESHOLD FIELD32(0x00000200) -#define CSR7_UART1_RX_TRESHOLD FIELD32(0x00000400) -#define CSR7_UART1_IDLE_TRESHOLD FIELD32(0x00000800) -#define CSR7_UART1_TX_BUFF_ERROR FIELD32(0x00001000) -#define CSR7_UART1_RX_BUFF_ERROR FIELD32(0x00002000) -#define CSR7_UART2_TX_TRESHOLD FIELD32(0x00004000) -#define CSR7_UART2_RX_TRESHOLD FIELD32(0x00008000) -#define CSR7_UART2_IDLE_TRESHOLD FIELD32(0x00010000) -#define CSR7_UART2_TX_BUFF_ERROR FIELD32(0x00020000) -#define CSR7_UART2_RX_BUFF_ERROR FIELD32(0x00040000) -#define CSR7_TIMER_CSR3_EXPIRE FIELD32(0x00080000) - -/* - * CSR8: Interrupt mask register. - * Write 1 to mask interrupt. - * TBCN_EXPIRE: Beacon timer expired interrupt. - * TWAKE_EXPIRE: Wakeup timer expired interrupt. - * TATIMW_EXPIRE: Timer of atim window expired interrupt. - * TXDONE_TXRING: Tx ring transmit done interrupt. - * TXDONE_ATIMRING: Atim ring transmit done interrupt. - * TXDONE_PRIORING: Priority ring transmit done interrupt. - * RXDONE: Receive done interrupt. - * DECRYPTION_DONE: Decryption done interrupt. - * ENCRYPTION_DONE: Encryption done interrupt. - * UART1_TX_TRESHOLD: UART1 TX reaches threshold. - * UART1_RX_TRESHOLD: UART1 RX reaches threshold. - * UART1_IDLE_TRESHOLD: UART1 IDLE over threshold. - * UART1_TX_BUFF_ERROR: UART1 TX buffer error. - * UART1_RX_BUFF_ERROR: UART1 RX buffer error. - * UART2_TX_TRESHOLD: UART2 TX reaches threshold. - * UART2_RX_TRESHOLD: UART2 RX reaches threshold. - * UART2_IDLE_TRESHOLD: UART2 IDLE over threshold. - * UART2_TX_BUFF_ERROR: UART2 TX buffer error. - * UART2_RX_BUFF_ERROR: UART2 RX buffer error. - * TIMER_CSR3_EXPIRE: TIMECSR3 timer expired (802.1H quiet period). - */ -#define CSR8 0x0020 -#define CSR8_TBCN_EXPIRE FIELD32(0x00000001) -#define CSR8_TWAKE_EXPIRE FIELD32(0x00000002) -#define CSR8_TATIMW_EXPIRE FIELD32(0x00000004) -#define CSR8_TXDONE_TXRING FIELD32(0x00000008) -#define CSR8_TXDONE_ATIMRING FIELD32(0x00000010) -#define CSR8_TXDONE_PRIORING FIELD32(0x00000020) -#define CSR8_RXDONE FIELD32(0x00000040) -#define CSR8_DECRYPTION_DONE FIELD32(0x00000080) -#define CSR8_ENCRYPTION_DONE FIELD32(0x00000100) -#define CSR8_UART1_TX_TRESHOLD FIELD32(0x00000200) -#define CSR8_UART1_RX_TRESHOLD FIELD32(0x00000400) -#define CSR8_UART1_IDLE_TRESHOLD FIELD32(0x00000800) -#define CSR8_UART1_TX_BUFF_ERROR FIELD32(0x00001000) -#define CSR8_UART1_RX_BUFF_ERROR FIELD32(0x00002000) -#define CSR8_UART2_TX_TRESHOLD FIELD32(0x00004000) -#define CSR8_UART2_RX_TRESHOLD FIELD32(0x00008000) -#define CSR8_UART2_IDLE_TRESHOLD FIELD32(0x00010000) -#define CSR8_UART2_TX_BUFF_ERROR FIELD32(0x00020000) -#define CSR8_UART2_RX_BUFF_ERROR FIELD32(0x00040000) -#define CSR8_TIMER_CSR3_EXPIRE FIELD32(0x00080000) - -/* - * CSR9: Maximum frame length register. - * MAX_FRAME_UNIT: Maximum frame length in 128b unit, default: 12. - */ -#define CSR9 0x0024 -#define CSR9_MAX_FRAME_UNIT FIELD32(0x00000f80) - -/* - * SECCSR0: WEP control register. - * KICK_DECRYPT: Kick decryption engine, self-clear. - * ONE_SHOT: 0: ring mode, 1: One shot only mode. - * DESC_ADDRESS: Descriptor physical address of frame. - */ -#define SECCSR0 0x0028 -#define SECCSR0_KICK_DECRYPT FIELD32(0x00000001) -#define SECCSR0_ONE_SHOT FIELD32(0x00000002) -#define SECCSR0_DESC_ADDRESS FIELD32(0xfffffffc) - -/* - * CSR11: Back-off control register. - * CWMIN: CWmin. Default cwmin is 31 (2^5 - 1). - * CWMAX: CWmax. Default cwmax is 1023 (2^10 - 1). - * SLOT_TIME: Slot time, default is 20us for 802.11b - * CW_SELECT: CWmin/CWmax selection, 1: Register, 0: TXD. - * LONG_RETRY: Long retry count. - * SHORT_RETRY: Short retry count. - */ -#define CSR11 0x002c -#define CSR11_CWMIN FIELD32(0x0000000f) -#define CSR11_CWMAX FIELD32(0x000000f0) -#define CSR11_SLOT_TIME FIELD32(0x00001f00) -#define CSR11_CW_SELECT FIELD32(0x00002000) -#define CSR11_LONG_RETRY FIELD32(0x00ff0000) -#define CSR11_SHORT_RETRY FIELD32(0xff000000) - -/* - * CSR12: Synchronization configuration register 0. - * All units in 1/16 TU. - * BEACON_INTERVAL: Beacon interval, default is 100 TU. - * CFP_MAX_DURATION: Cfp maximum duration, default is 100 TU. - */ -#define CSR12 0x0030 -#define CSR12_BEACON_INTERVAL FIELD32(0x0000ffff) -#define CSR12_CFP_MAX_DURATION FIELD32(0xffff0000) - -/* - * CSR13: Synchronization configuration register 1. - * All units in 1/16 TU. - * ATIMW_DURATION: Atim window duration. - * CFP_PERIOD: Cfp period, default is 0 TU. - */ -#define CSR13 0x0034 -#define CSR13_ATIMW_DURATION FIELD32(0x0000ffff) -#define CSR13_CFP_PERIOD FIELD32(0x00ff0000) - -/* - * CSR14: Synchronization control register. - * TSF_COUNT: Enable tsf auto counting. - * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. - * TBCN: Enable tbcn with reload value. - * TCFP: Enable tcfp & cfp / cp switching. - * TATIMW: Enable tatimw & atim window switching. - * BEACON_GEN: Enable beacon generator. - * CFP_COUNT_PRELOAD: Cfp count preload value. - * TBCM_PRELOAD: Tbcn preload value in units of 64us. - */ -#define CSR14 0x0038 -#define CSR14_TSF_COUNT FIELD32(0x00000001) -#define CSR14_TSF_SYNC FIELD32(0x00000006) -#define CSR14_TBCN FIELD32(0x00000008) -#define CSR14_TCFP FIELD32(0x00000010) -#define CSR14_TATIMW FIELD32(0x00000020) -#define CSR14_BEACON_GEN FIELD32(0x00000040) -#define CSR14_CFP_COUNT_PRELOAD FIELD32(0x0000ff00) -#define CSR14_TBCM_PRELOAD FIELD32(0xffff0000) - -/* - * CSR15: Synchronization status register. - * CFP: ASIC is in contention-free period. - * ATIMW: ASIC is in ATIM window. - * BEACON_SENT: Beacon is send. - */ -#define CSR15 0x003c -#define CSR15_CFP FIELD32(0x00000001) -#define CSR15_ATIMW FIELD32(0x00000002) -#define CSR15_BEACON_SENT FIELD32(0x00000004) - -/* - * CSR16: TSF timer register 0. - */ -#define CSR16 0x0040 -#define CSR16_LOW_TSFTIMER FIELD32(0xffffffff) - -/* - * CSR17: TSF timer register 1. - */ -#define CSR17 0x0044 -#define CSR17_HIGH_TSFTIMER FIELD32(0xffffffff) - -/* - * CSR18: IFS timer register 0. - * SIFS: Sifs, default is 10 us. - * PIFS: Pifs, default is 30 us. - */ -#define CSR18 0x0048 -#define CSR18_SIFS FIELD32(0x000001ff) -#define CSR18_PIFS FIELD32(0x001f0000) - -/* - * CSR19: IFS timer register 1. - * DIFS: Difs, default is 50 us. - * EIFS: Eifs, default is 364 us. - */ -#define CSR19 0x004c -#define CSR19_DIFS FIELD32(0x0000ffff) -#define CSR19_EIFS FIELD32(0xffff0000) - -/* - * CSR20: Wakeup timer register. - * DELAY_AFTER_TBCN: Delay after tbcn expired in units of 1/16 TU. - * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. - * AUTOWAKE: Enable auto wakeup / sleep mechanism. - */ -#define CSR20 0x0050 -#define CSR20_DELAY_AFTER_TBCN FIELD32(0x0000ffff) -#define CSR20_TBCN_BEFORE_WAKEUP FIELD32(0x00ff0000) -#define CSR20_AUTOWAKE FIELD32(0x01000000) - -/* - * CSR21: EEPROM control register. - * RELOAD: Write 1 to reload eeprom content. - * TYPE_93C46: 1: 93c46, 0:93c66. - */ -#define CSR21 0x0054 -#define CSR21_RELOAD FIELD32(0x00000001) -#define CSR21_EEPROM_DATA_CLOCK FIELD32(0x00000002) -#define CSR21_EEPROM_CHIP_SELECT FIELD32(0x00000004) -#define CSR21_EEPROM_DATA_IN FIELD32(0x00000008) -#define CSR21_EEPROM_DATA_OUT FIELD32(0x00000010) -#define CSR21_TYPE_93C46 FIELD32(0x00000020) - -/* - * CSR22: CFP control register. - * CFP_DURATION_REMAIN: Cfp duration remain, in units of TU. - * RELOAD_CFP_DURATION: Write 1 to reload cfp duration remain. - */ -#define CSR22 0x0058 -#define CSR22_CFP_DURATION_REMAIN FIELD32(0x0000ffff) -#define CSR22_RELOAD_CFP_DURATION FIELD32(0x00010000) - -/* - * Transmit related CSRs. - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * TXCSR0: TX Control Register. - * KICK_TX: Kick tx ring. - * KICK_ATIM: Kick atim ring. - * KICK_PRIO: Kick priority ring. - * ABORT: Abort all transmit related ring operation. - */ -#define TXCSR0 0x0060 -#define TXCSR0_KICK_TX FIELD32(0x00000001) -#define TXCSR0_KICK_ATIM FIELD32(0x00000002) -#define TXCSR0_KICK_PRIO FIELD32(0x00000004) -#define TXCSR0_ABORT FIELD32(0x00000008) - -/* - * TXCSR1: TX Configuration Register. - * ACK_TIMEOUT: Ack timeout, default = sifs + 2*slottime + acktime @ 1mbps. - * ACK_CONSUME_TIME: Ack consume time, default = sifs + acktime @ 1mbps. - * TSF_OFFSET: Insert tsf offset. - * AUTORESPONDER: Enable auto responder which include ack & cts. - */ -#define TXCSR1 0x0064 -#define TXCSR1_ACK_TIMEOUT FIELD32(0x000001ff) -#define TXCSR1_ACK_CONSUME_TIME FIELD32(0x0003fe00) -#define TXCSR1_TSF_OFFSET FIELD32(0x00fc0000) -#define TXCSR1_AUTORESPONDER FIELD32(0x01000000) - -/* - * TXCSR2: Tx descriptor configuration register. - * TXD_SIZE: Tx descriptor size, default is 48. - * NUM_TXD: Number of tx entries in ring. - * NUM_ATIM: Number of atim entries in ring. - * NUM_PRIO: Number of priority entries in ring. - */ -#define TXCSR2 0x0068 -#define TXCSR2_TXD_SIZE FIELD32(0x000000ff) -#define TXCSR2_NUM_TXD FIELD32(0x0000ff00) -#define TXCSR2_NUM_ATIM FIELD32(0x00ff0000) -#define TXCSR2_NUM_PRIO FIELD32(0xff000000) - -/* - * TXCSR3: TX Ring Base address register. - */ -#define TXCSR3 0x006c -#define TXCSR3_TX_RING_REGISTER FIELD32(0xffffffff) - -/* - * TXCSR4: TX Atim Ring Base address register. - */ -#define TXCSR4 0x0070 -#define TXCSR4_ATIM_RING_REGISTER FIELD32(0xffffffff) - -/* - * TXCSR5: TX Prio Ring Base address register. - */ -#define TXCSR5 0x0074 -#define TXCSR5_PRIO_RING_REGISTER FIELD32(0xffffffff) - -/* - * TXCSR6: Beacon Base address register. - */ -#define TXCSR6 0x0078 -#define TXCSR6_BEACON_RING_REGISTER FIELD32(0xffffffff) - -/* - * TXCSR7: Auto responder control register. - * AR_POWERMANAGEMENT: Auto responder power management bit. - */ -#define TXCSR7 0x007c -#define TXCSR7_AR_POWERMANAGEMENT FIELD32(0x00000001) - -/* - * TXCSR8: CCK Tx BBP register. - */ -#define TXCSR8 0x0098 -#define TXCSR8_BBP_ID0 FIELD32(0x0000007f) -#define TXCSR8_BBP_ID0_VALID FIELD32(0x00000080) -#define TXCSR8_BBP_ID1 FIELD32(0x00007f00) -#define TXCSR8_BBP_ID1_VALID FIELD32(0x00008000) -#define TXCSR8_BBP_ID2 FIELD32(0x007f0000) -#define TXCSR8_BBP_ID2_VALID FIELD32(0x00800000) -#define TXCSR8_BBP_ID3 FIELD32(0x7f000000) -#define TXCSR8_BBP_ID3_VALID FIELD32(0x80000000) - -/* - * TXCSR9: OFDM TX BBP registers - * OFDM_SIGNAL: BBP rate field address for OFDM. - * OFDM_SERVICE: BBP service field address for OFDM. - * OFDM_LENGTH_LOW: BBP length low byte address for OFDM. - * OFDM_LENGTH_HIGH: BBP length high byte address for OFDM. - */ -#define TXCSR9 0x0094 -#define TXCSR9_OFDM_RATE FIELD32(0x000000ff) -#define TXCSR9_OFDM_SERVICE FIELD32(0x0000ff00) -#define TXCSR9_OFDM_LENGTH_LOW FIELD32(0x00ff0000) -#define TXCSR9_OFDM_LENGTH_HIGH FIELD32(0xff000000) - -/* - * Receive related CSRs. - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * RXCSR0: RX Control Register. - * DISABLE_RX: Disable rx engine. - * DROP_CRC: Drop crc error. - * DROP_PHYSICAL: Drop physical error. - * DROP_CONTROL: Drop control frame. - * DROP_NOT_TO_ME: Drop not to me unicast frame. - * DROP_TODS: Drop frame tods bit is true. - * DROP_VERSION_ERROR: Drop version error frame. - * PASS_CRC: Pass all packets with crc attached. - * PASS_CRC: Pass all packets with crc attached. - * PASS_PLCP: Pass all packets with 4 bytes PLCP attached. - * DROP_MCAST: Drop multicast frames. - * DROP_BCAST: Drop broadcast frames. - * ENABLE_QOS: Accept QOS data frame and parse QOS field. - */ -#define RXCSR0 0x0080 -#define RXCSR0_DISABLE_RX FIELD32(0x00000001) -#define RXCSR0_DROP_CRC FIELD32(0x00000002) -#define RXCSR0_DROP_PHYSICAL FIELD32(0x00000004) -#define RXCSR0_DROP_CONTROL FIELD32(0x00000008) -#define RXCSR0_DROP_NOT_TO_ME FIELD32(0x00000010) -#define RXCSR0_DROP_TODS FIELD32(0x00000020) -#define RXCSR0_DROP_VERSION_ERROR FIELD32(0x00000040) -#define RXCSR0_PASS_CRC FIELD32(0x00000080) -#define RXCSR0_PASS_PLCP FIELD32(0x00000100) -#define RXCSR0_DROP_MCAST FIELD32(0x00000200) -#define RXCSR0_DROP_BCAST FIELD32(0x00000400) -#define RXCSR0_ENABLE_QOS FIELD32(0x00000800) - -/* - * RXCSR1: RX descriptor configuration register. - * RXD_SIZE: Rx descriptor size, default is 32b. - * NUM_RXD: Number of rx entries in ring. - */ -#define RXCSR1 0x0084 -#define RXCSR1_RXD_SIZE FIELD32(0x000000ff) -#define RXCSR1_NUM_RXD FIELD32(0x0000ff00) - -/* - * RXCSR2: RX Ring base address register. - */ -#define RXCSR2 0x0088 -#define RXCSR2_RX_RING_REGISTER FIELD32(0xffffffff) - -/* - * RXCSR3: BBP ID register for Rx operation. - * BBP_ID#: BBP register # id. - * BBP_ID#_VALID: BBP register # id is valid or not. - */ -#define RXCSR3 0x0090 -#define RXCSR3_BBP_ID0 FIELD32(0x0000007f) -#define RXCSR3_BBP_ID0_VALID FIELD32(0x00000080) -#define RXCSR3_BBP_ID1 FIELD32(0x00007f00) -#define RXCSR3_BBP_ID1_VALID FIELD32(0x00008000) -#define RXCSR3_BBP_ID2 FIELD32(0x007f0000) -#define RXCSR3_BBP_ID2_VALID FIELD32(0x00800000) -#define RXCSR3_BBP_ID3 FIELD32(0x7f000000) -#define RXCSR3_BBP_ID3_VALID FIELD32(0x80000000) - -/* - * ARCSR1: Auto Responder PLCP config register 1. - * AR_BBP_DATA#: Auto responder BBP register # data. - * AR_BBP_ID#: Auto responder BBP register # Id. - */ -#define ARCSR1 0x009c -#define ARCSR1_AR_BBP_DATA2 FIELD32(0x000000ff) -#define ARCSR1_AR_BBP_ID2 FIELD32(0x0000ff00) -#define ARCSR1_AR_BBP_DATA3 FIELD32(0x00ff0000) -#define ARCSR1_AR_BBP_ID3 FIELD32(0xff000000) - -/* - * Miscellaneous Registers. - * Some values are set in TU, whereas 1 TU == 1024 us. - - */ - -/* - * PCICSR: PCI control register. - * BIG_ENDIAN: 1: big endian, 0: little endian. - * RX_TRESHOLD: Rx threshold in dw to start pci access - * 0: 16dw (default), 1: 8dw, 2: 4dw, 3: 32dw. - * TX_TRESHOLD: Tx threshold in dw to start pci access - * 0: 0dw (default), 1: 1dw, 2: 4dw, 3: forward. - * BURST_LENTH: Pci burst length 0: 4dw (default, 1: 8dw, 2: 16dw, 3:32dw. - * ENABLE_CLK: Enable clk_run, pci clock can't going down to non-operational. - * READ_MULTIPLE: Enable memory read multiple. - * WRITE_INVALID: Enable memory write & invalid. - */ -#define PCICSR 0x008c -#define PCICSR_BIG_ENDIAN FIELD32(0x00000001) -#define PCICSR_RX_TRESHOLD FIELD32(0x00000006) -#define PCICSR_TX_TRESHOLD FIELD32(0x00000018) -#define PCICSR_BURST_LENTH FIELD32(0x00000060) -#define PCICSR_ENABLE_CLK FIELD32(0x00000080) -#define PCICSR_READ_MULTIPLE FIELD32(0x00000100) -#define PCICSR_WRITE_INVALID FIELD32(0x00000200) - -/* - * CNT0: FCS error count. - * FCS_ERROR: FCS error count, cleared when read. - */ -#define CNT0 0x00a0 -#define CNT0_FCS_ERROR FIELD32(0x0000ffff) - -/* - * Statistic Register. - * CNT1: PLCP error count. - * CNT2: Long error count. - */ -#define TIMECSR2 0x00a8 -#define CNT1 0x00ac -#define CNT2 0x00b0 -#define TIMECSR3 0x00b4 - -/* - * CNT3: CCA false alarm count. - */ -#define CNT3 0x00b8 -#define CNT3_FALSE_CCA FIELD32(0x0000ffff) - -/* - * Statistic Register. - * CNT4: Rx FIFO overflow count. - * CNT5: Tx FIFO underrun count. - */ -#define CNT4 0x00bc -#define CNT5 0x00c0 - -/* - * Baseband Control Register. - */ - -/* - * PWRCSR0: Power mode configuration register. - */ -#define PWRCSR0 0x00c4 - -/* - * Power state transition time registers. - */ -#define PSCSR0 0x00c8 -#define PSCSR1 0x00cc -#define PSCSR2 0x00d0 -#define PSCSR3 0x00d4 - -/* - * PWRCSR1: Manual power control / status register. - * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. - * SET_STATE: Set state. Write 1 to trigger, self cleared. - * BBP_DESIRE_STATE: BBP desired state. - * RF_DESIRE_STATE: RF desired state. - * BBP_CURR_STATE: BBP current state. - * RF_CURR_STATE: RF current state. - * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. - */ -#define PWRCSR1 0x00d8 -#define PWRCSR1_SET_STATE FIELD32(0x00000001) -#define PWRCSR1_BBP_DESIRE_STATE FIELD32(0x00000006) -#define PWRCSR1_RF_DESIRE_STATE FIELD32(0x00000018) -#define PWRCSR1_BBP_CURR_STATE FIELD32(0x00000060) -#define PWRCSR1_RF_CURR_STATE FIELD32(0x00000180) -#define PWRCSR1_PUT_TO_SLEEP FIELD32(0x00000200) - -/* - * TIMECSR: Timer control register. - * US_COUNT: 1 us timer count in units of clock cycles. - * US_64_COUNT: 64 us timer count in units of 1 us timer. - * BEACON_EXPECT: Beacon expect window. - */ -#define TIMECSR 0x00dc -#define TIMECSR_US_COUNT FIELD32(0x000000ff) -#define TIMECSR_US_64_COUNT FIELD32(0x0000ff00) -#define TIMECSR_BEACON_EXPECT FIELD32(0x00070000) - -/* - * MACCSR0: MAC configuration register 0. - */ -#define MACCSR0 0x00e0 - -/* - * MACCSR1: MAC configuration register 1. - * KICK_RX: Kick one-shot rx in one-shot rx mode. - * ONESHOT_RXMODE: Enable one-shot rx mode for debugging. - * BBPRX_RESET_MODE: Ralink bbp rx reset mode. - * AUTO_TXBBP: Auto tx logic access bbp control register. - * AUTO_RXBBP: Auto rx logic access bbp control register. - * LOOPBACK: Loopback mode. 0: normal, 1: internal, 2: external, 3:rsvd. - * INTERSIL_IF: Intersil if calibration pin. - */ -#define MACCSR1 0x00e4 -#define MACCSR1_KICK_RX FIELD32(0x00000001) -#define MACCSR1_ONESHOT_RXMODE FIELD32(0x00000002) -#define MACCSR1_BBPRX_RESET_MODE FIELD32(0x00000004) -#define MACCSR1_AUTO_TXBBP FIELD32(0x00000008) -#define MACCSR1_AUTO_RXBBP FIELD32(0x00000010) -#define MACCSR1_LOOPBACK FIELD32(0x00000060) -#define MACCSR1_INTERSIL_IF FIELD32(0x00000080) - -/* - * RALINKCSR: Ralink Rx auto-reset BBCR. - * AR_BBP_DATA#: Auto reset BBP register # data. - * AR_BBP_ID#: Auto reset BBP register # id. - */ -#define RALINKCSR 0x00e8 -#define RALINKCSR_AR_BBP_DATA0 FIELD32(0x000000ff) -#define RALINKCSR_AR_BBP_ID0 FIELD32(0x00007f00) -#define RALINKCSR_AR_BBP_VALID0 FIELD32(0x00008000) -#define RALINKCSR_AR_BBP_DATA1 FIELD32(0x00ff0000) -#define RALINKCSR_AR_BBP_ID1 FIELD32(0x7f000000) -#define RALINKCSR_AR_BBP_VALID1 FIELD32(0x80000000) - -/* - * BCNCSR: Beacon interval control register. - * CHANGE: Write one to change beacon interval. - * DELTATIME: The delta time value. - * NUM_BEACON: Number of beacon according to mode. - * MODE: Please refer to asic specs. - * PLUS: Plus or minus delta time value. - */ -#define BCNCSR 0x00ec -#define BCNCSR_CHANGE FIELD32(0x00000001) -#define BCNCSR_DELTATIME FIELD32(0x0000001e) -#define BCNCSR_NUM_BEACON FIELD32(0x00001fe0) -#define BCNCSR_MODE FIELD32(0x00006000) -#define BCNCSR_PLUS FIELD32(0x00008000) - -/* - * BBP / RF / IF Control Register. - */ - -/* - * BBPCSR: BBP serial control register. - * VALUE: Register value to program into BBP. - * REGNUM: Selected BBP register. - * BUSY: 1: asic is busy execute BBP programming. - * WRITE_CONTROL: 1: write BBP, 0: read BBP. - */ -#define BBPCSR 0x00f0 -#define BBPCSR_VALUE FIELD32(0x000000ff) -#define BBPCSR_REGNUM FIELD32(0x00007f00) -#define BBPCSR_BUSY FIELD32(0x00008000) -#define BBPCSR_WRITE_CONTROL FIELD32(0x00010000) - -/* - * RFCSR: RF serial control register. - * VALUE: Register value + id to program into rf/if. - * NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). - * IF_SELECT: Chip to program: 0: rf, 1: if. - * PLL_LD: Rf pll_ld status. - * BUSY: 1: asic is busy execute rf programming. - */ -#define RFCSR 0x00f4 -#define RFCSR_VALUE FIELD32(0x00ffffff) -#define RFCSR_NUMBER_OF_BITS FIELD32(0x1f000000) -#define RFCSR_IF_SELECT FIELD32(0x20000000) -#define RFCSR_PLL_LD FIELD32(0x40000000) -#define RFCSR_BUSY FIELD32(0x80000000) - -/* - * LEDCSR: LED control register. - * ON_PERIOD: On period, default 70ms. - * OFF_PERIOD: Off period, default 30ms. - * LINK: 0: linkoff, 1: linkup. - * ACTIVITY: 0: idle, 1: active. - * LINK_POLARITY: 0: active low, 1: active high. - * ACTIVITY_POLARITY: 0: active low, 1: active high. - * LED_DEFAULT: LED state for "enable" 0: ON, 1: OFF. - */ -#define LEDCSR 0x00f8 -#define LEDCSR_ON_PERIOD FIELD32(0x000000ff) -#define LEDCSR_OFF_PERIOD FIELD32(0x0000ff00) -#define LEDCSR_LINK FIELD32(0x00010000) -#define LEDCSR_ACTIVITY FIELD32(0x00020000) -#define LEDCSR_LINK_POLARITY FIELD32(0x00040000) -#define LEDCSR_ACTIVITY_POLARITY FIELD32(0x00080000) -#define LEDCSR_LED_DEFAULT FIELD32(0x00100000) - -/* - * SECCSR3: AES control register. - */ -#define SECCSR3 0x00fc - -/* - * ASIC pointer information. - * RXPTR: Current RX ring address. - * TXPTR: Current Tx ring address. - * PRIPTR: Current Priority ring address. - * ATIMPTR: Current ATIM ring address. - */ -#define RXPTR 0x0100 -#define TXPTR 0x0104 -#define PRIPTR 0x0108 -#define ATIMPTR 0x010c - -/* - * TXACKCSR0: TX ACK timeout. - */ -#define TXACKCSR0 0x0110 - -/* - * ACK timeout count registers. - * ACKCNT0: TX ACK timeout count. - * ACKCNT1: RX ACK timeout count. - */ -#define ACKCNT0 0x0114 -#define ACKCNT1 0x0118 - -/* - * GPIO and others. - */ - -/* - * GPIOCSR: GPIO control register. - * GPIOCSR_VALx: GPIO value - * GPIOCSR_DIRx: GPIO direction: 0 = output; 1 = input - */ -#define GPIOCSR 0x0120 -#define GPIOCSR_VAL0 FIELD32(0x00000001) -#define GPIOCSR_VAL1 FIELD32(0x00000002) -#define GPIOCSR_VAL2 FIELD32(0x00000004) -#define GPIOCSR_VAL3 FIELD32(0x00000008) -#define GPIOCSR_VAL4 FIELD32(0x00000010) -#define GPIOCSR_VAL5 FIELD32(0x00000020) -#define GPIOCSR_VAL6 FIELD32(0x00000040) -#define GPIOCSR_VAL7 FIELD32(0x00000080) -#define GPIOCSR_DIR0 FIELD32(0x00000100) -#define GPIOCSR_DIR1 FIELD32(0x00000200) -#define GPIOCSR_DIR2 FIELD32(0x00000400) -#define GPIOCSR_DIR3 FIELD32(0x00000800) -#define GPIOCSR_DIR4 FIELD32(0x00001000) -#define GPIOCSR_DIR5 FIELD32(0x00002000) -#define GPIOCSR_DIR6 FIELD32(0x00004000) -#define GPIOCSR_DIR7 FIELD32(0x00008000) - -/* - * FIFO pointer registers. - * FIFOCSR0: TX FIFO pointer. - * FIFOCSR1: RX FIFO pointer. - */ -#define FIFOCSR0 0x0128 -#define FIFOCSR1 0x012c - -/* - * BCNCSR1: Tx BEACON offset time control register. - * PRELOAD: Beacon timer offset in units of usec. - * BEACON_CWMIN: 2^CwMin. - */ -#define BCNCSR1 0x0130 -#define BCNCSR1_PRELOAD FIELD32(0x0000ffff) -#define BCNCSR1_BEACON_CWMIN FIELD32(0x000f0000) - -/* - * MACCSR2: TX_PE to RX_PE turn-around time control register - * DELAY: RX_PE low width, in units of pci clock cycle. - */ -#define MACCSR2 0x0134 -#define MACCSR2_DELAY FIELD32(0x000000ff) - -/* - * TESTCSR: TEST mode selection register. - */ -#define TESTCSR 0x0138 - -/* - * ARCSR2: 1 Mbps ACK/CTS PLCP. - */ -#define ARCSR2 0x013c -#define ARCSR2_SIGNAL FIELD32(0x000000ff) -#define ARCSR2_SERVICE FIELD32(0x0000ff00) -#define ARCSR2_LENGTH FIELD32(0xffff0000) - -/* - * ARCSR3: 2 Mbps ACK/CTS PLCP. - */ -#define ARCSR3 0x0140 -#define ARCSR3_SIGNAL FIELD32(0x000000ff) -#define ARCSR3_SERVICE FIELD32(0x0000ff00) -#define ARCSR3_LENGTH FIELD32(0xffff0000) - -/* - * ARCSR4: 5.5 Mbps ACK/CTS PLCP. - */ -#define ARCSR4 0x0144 -#define ARCSR4_SIGNAL FIELD32(0x000000ff) -#define ARCSR4_SERVICE FIELD32(0x0000ff00) -#define ARCSR4_LENGTH FIELD32(0xffff0000) - -/* - * ARCSR5: 11 Mbps ACK/CTS PLCP. - */ -#define ARCSR5 0x0148 -#define ARCSR5_SIGNAL FIELD32(0x000000ff) -#define ARCSR5_SERVICE FIELD32(0x0000ff00) -#define ARCSR5_LENGTH FIELD32(0xffff0000) - -/* - * ARTCSR0: CCK ACK/CTS payload consumed time for 1/2/5.5/11 mbps. - */ -#define ARTCSR0 0x014c -#define ARTCSR0_ACK_CTS_11MBS FIELD32(0x000000ff) -#define ARTCSR0_ACK_CTS_5_5MBS FIELD32(0x0000ff00) -#define ARTCSR0_ACK_CTS_2MBS FIELD32(0x00ff0000) -#define ARTCSR0_ACK_CTS_1MBS FIELD32(0xff000000) - - -/* - * ARTCSR1: OFDM ACK/CTS payload consumed time for 6/9/12/18 mbps. - */ -#define ARTCSR1 0x0150 -#define ARTCSR1_ACK_CTS_6MBS FIELD32(0x000000ff) -#define ARTCSR1_ACK_CTS_9MBS FIELD32(0x0000ff00) -#define ARTCSR1_ACK_CTS_12MBS FIELD32(0x00ff0000) -#define ARTCSR1_ACK_CTS_18MBS FIELD32(0xff000000) - -/* - * ARTCSR2: OFDM ACK/CTS payload consumed time for 24/36/48/54 mbps. - */ -#define ARTCSR2 0x0154 -#define ARTCSR2_ACK_CTS_24MBS FIELD32(0x000000ff) -#define ARTCSR2_ACK_CTS_36MBS FIELD32(0x0000ff00) -#define ARTCSR2_ACK_CTS_48MBS FIELD32(0x00ff0000) -#define ARTCSR2_ACK_CTS_54MBS FIELD32(0xff000000) - -/* - * SECCSR1: WEP control register. - * KICK_ENCRYPT: Kick encryption engine, self-clear. - * ONE_SHOT: 0: ring mode, 1: One shot only mode. - * DESC_ADDRESS: Descriptor physical address of frame. - */ -#define SECCSR1 0x0158 -#define SECCSR1_KICK_ENCRYPT FIELD32(0x00000001) -#define SECCSR1_ONE_SHOT FIELD32(0x00000002) -#define SECCSR1_DESC_ADDRESS FIELD32(0xfffffffc) - -/* - * BBPCSR1: BBP TX configuration. - */ -#define BBPCSR1 0x015c -#define BBPCSR1_CCK FIELD32(0x00000003) -#define BBPCSR1_CCK_FLIP FIELD32(0x00000004) -#define BBPCSR1_OFDM FIELD32(0x00030000) -#define BBPCSR1_OFDM_FLIP FIELD32(0x00040000) - -/* - * Dual band configuration registers. - * DBANDCSR0: Dual band configuration register 0. - * DBANDCSR1: Dual band configuration register 1. - */ -#define DBANDCSR0 0x0160 -#define DBANDCSR1 0x0164 - -/* - * BBPPCSR: BBP Pin control register. - */ -#define BBPPCSR 0x0168 - -/* - * MAC special debug mode selection registers. - * DBGSEL0: MAC special debug mode selection register 0. - * DBGSEL1: MAC special debug mode selection register 1. - */ -#define DBGSEL0 0x016c -#define DBGSEL1 0x0170 - -/* - * BISTCSR: BBP BIST register. - */ -#define BISTCSR 0x0174 - -/* - * Multicast filter registers. - * MCAST0: Multicast filter register 0. - * MCAST1: Multicast filter register 1. - */ -#define MCAST0 0x0178 -#define MCAST1 0x017c - -/* - * UART registers. - * UARTCSR0: UART1 TX register. - * UARTCSR1: UART1 RX register. - * UARTCSR3: UART1 frame control register. - * UARTCSR4: UART1 buffer control register. - * UART2CSR0: UART2 TX register. - * UART2CSR1: UART2 RX register. - * UART2CSR3: UART2 frame control register. - * UART2CSR4: UART2 buffer control register. - */ -#define UARTCSR0 0x0180 -#define UARTCSR1 0x0184 -#define UARTCSR3 0x0188 -#define UARTCSR4 0x018c -#define UART2CSR0 0x0190 -#define UART2CSR1 0x0194 -#define UART2CSR3 0x0198 -#define UART2CSR4 0x019c - -/* - * BBP registers. - * The wordsize of the BBP is 8 bits. - */ - -/* - * R2: TX antenna control - */ -#define BBP_R2_TX_ANTENNA FIELD8(0x03) -#define BBP_R2_TX_IQ_FLIP FIELD8(0x04) - -/* - * R14: RX antenna control - */ -#define BBP_R14_RX_ANTENNA FIELD8(0x03) -#define BBP_R14_RX_IQ_FLIP FIELD8(0x04) - -/* - * BBP_R70 - */ -#define BBP_R70_JAPAN_FILTER FIELD8(0x08) - -/* - * RF registers - */ - -/* - * RF 1 - */ -#define RF1_TUNER FIELD32(0x00020000) - -/* - * RF 3 - */ -#define RF3_TUNER FIELD32(0x00000100) -#define RF3_TXPOWER FIELD32(0x00003e00) - -/* - * EEPROM content. - * The wordsize of the EEPROM is 16 bits. - */ - -/* - * HW MAC address. - */ -#define EEPROM_MAC_ADDR_0 0x0002 -#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) -#define EEPROM_MAC_ADDR1 0x0003 -#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_2 0x0004 -#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) - -/* - * EEPROM antenna. - * ANTENNA_NUM: Number of antenna's. - * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. - * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. - * LED_MODE: 0: default, 1: TX/RX activity,2: Single (ignore link), 3: rsvd. - * DYN_TXAGC: Dynamic TX AGC control. - * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. - * RF_TYPE: Rf_type of this adapter. - */ -#define EEPROM_ANTENNA 0x10 -#define EEPROM_ANTENNA_NUM FIELD16(0x0003) -#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) -#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) -#define EEPROM_ANTENNA_LED_MODE FIELD16(0x01c0) -#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) -#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) -#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) - -/* - * EEPROM NIC config. - * CARDBUS_ACCEL: 0: enable, 1: disable. - * DYN_BBP_TUNE: 0: enable, 1: disable. - * CCK_TX_POWER: CCK TX power compensation. - */ -#define EEPROM_NIC 0x11 -#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0001) -#define EEPROM_NIC_DYN_BBP_TUNE FIELD16(0x0002) -#define EEPROM_NIC_CCK_TX_POWER FIELD16(0x000c) - -/* - * EEPROM geography. - * GEO: Default geography setting for device. - */ -#define EEPROM_GEOGRAPHY 0x12 -#define EEPROM_GEOGRAPHY_GEO FIELD16(0x0f00) - -/* - * EEPROM BBP. - */ -#define EEPROM_BBP_START 0x13 -#define EEPROM_BBP_SIZE 16 -#define EEPROM_BBP_VALUE FIELD16(0x00ff) -#define EEPROM_BBP_REG_ID FIELD16(0xff00) - -/* - * EEPROM TXPOWER - */ -#define EEPROM_TXPOWER_START 0x23 -#define EEPROM_TXPOWER_SIZE 7 -#define EEPROM_TXPOWER_1 FIELD16(0x00ff) -#define EEPROM_TXPOWER_2 FIELD16(0xff00) - -/* - * RSSI <-> dBm offset calibration - */ -#define EEPROM_CALIBRATE_OFFSET 0x3e -#define EEPROM_CALIBRATE_OFFSET_RSSI FIELD16(0x00ff) - -/* - * DMA descriptor defines. - */ -#define TXD_DESC_SIZE (11 * sizeof(__le32)) -#define RXD_DESC_SIZE (11 * sizeof(__le32)) - -/* - * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. - */ - -/* - * Word0 - */ -#define TXD_W0_OWNER_NIC FIELD32(0x00000001) -#define TXD_W0_VALID FIELD32(0x00000002) -#define TXD_W0_RESULT FIELD32(0x0000001c) -#define TXD_W0_RETRY_COUNT FIELD32(0x000000e0) -#define TXD_W0_MORE_FRAG FIELD32(0x00000100) -#define TXD_W0_ACK FIELD32(0x00000200) -#define TXD_W0_TIMESTAMP FIELD32(0x00000400) -#define TXD_W0_OFDM FIELD32(0x00000800) -#define TXD_W0_CIPHER_OWNER FIELD32(0x00001000) -#define TXD_W0_IFS FIELD32(0x00006000) -#define TXD_W0_RETRY_MODE FIELD32(0x00008000) -#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) -#define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) - -/* - * Word1 - */ -#define TXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) - -/* - * Word2 - */ -#define TXD_W2_IV_OFFSET FIELD32(0x0000003f) -#define TXD_W2_AIFS FIELD32(0x000000c0) -#define TXD_W2_CWMIN FIELD32(0x00000f00) -#define TXD_W2_CWMAX FIELD32(0x0000f000) - -/* - * Word3: PLCP information - */ -#define TXD_W3_PLCP_SIGNAL FIELD32(0x000000ff) -#define TXD_W3_PLCP_SERVICE FIELD32(0x0000ff00) -#define TXD_W3_PLCP_LENGTH_LOW FIELD32(0x00ff0000) -#define TXD_W3_PLCP_LENGTH_HIGH FIELD32(0xff000000) - -/* - * Word4 - */ -#define TXD_W4_IV FIELD32(0xffffffff) - -/* - * Word5 - */ -#define TXD_W5_EIV FIELD32(0xffffffff) - -/* - * Word6-9: Key - */ -#define TXD_W6_KEY FIELD32(0xffffffff) -#define TXD_W7_KEY FIELD32(0xffffffff) -#define TXD_W8_KEY FIELD32(0xffffffff) -#define TXD_W9_KEY FIELD32(0xffffffff) - -/* - * Word10 - */ -#define TXD_W10_RTS FIELD32(0x00000001) -#define TXD_W10_TX_RATE FIELD32(0x000000fe) - -/* - * RX descriptor format for RX Ring. - */ - -/* - * Word0 - */ -#define RXD_W0_OWNER_NIC FIELD32(0x00000001) -#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) -#define RXD_W0_MULTICAST FIELD32(0x00000004) -#define RXD_W0_BROADCAST FIELD32(0x00000008) -#define RXD_W0_MY_BSS FIELD32(0x00000010) -#define RXD_W0_CRC_ERROR FIELD32(0x00000020) -#define RXD_W0_OFDM FIELD32(0x00000040) -#define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) -#define RXD_W0_CIPHER_OWNER FIELD32(0x00000100) -#define RXD_W0_ICV_ERROR FIELD32(0x00000200) -#define RXD_W0_IV_OFFSET FIELD32(0x0000fc00) -#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) -#define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) - -/* - * Word1 - */ -#define RXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) - -/* - * Word2 - */ -#define RXD_W2_SIGNAL FIELD32(0x000000ff) -#define RXD_W2_RSSI FIELD32(0x0000ff00) -#define RXD_W2_TA FIELD32(0xffff0000) - -/* - * Word3 - */ -#define RXD_W3_TA FIELD32(0xffffffff) - -/* - * Word4 - */ -#define RXD_W4_IV FIELD32(0xffffffff) - -/* - * Word5 - */ -#define RXD_W5_EIV FIELD32(0xffffffff) - -/* - * Word6-9: Key - */ -#define RXD_W6_KEY FIELD32(0xffffffff) -#define RXD_W7_KEY FIELD32(0xffffffff) -#define RXD_W8_KEY FIELD32(0xffffffff) -#define RXD_W9_KEY FIELD32(0xffffffff) - -/* - * Word10 - */ -#define RXD_W10_DROP FIELD32(0x00000001) - -/* - * Macros for converting txpower from EEPROM to mac80211 value - * and from mac80211 value to register value. - */ -#define MIN_TXPOWER 0 -#define MAX_TXPOWER 31 -#define DEFAULT_TXPOWER 24 - -#define TXPOWER_FROM_DEV(__txpower) \ - (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) - -#define TXPOWER_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) - -#endif /* RT2500PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c deleted file mode 100644 index b50d873145d5..000000000000 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ /dev/null @@ -1,2001 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2500usb - Abstract: rt2500usb device specific routines. - Supported chipsets: RT2570. - */ - -#include -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00usb.h" -#include "rt2500usb.h" - -/* - * Allow hardware encryption to be disabled. - */ -static bool modparam_nohwcrypt; -module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); -MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); - -/* - * Register access. - * All access to the CSR registers will go through the methods - * rt2500usb_register_read and rt2500usb_register_write. - * BBP and RF register require indirect register access, - * and use the CSR registers BBPCSR and RFCSR to achieve this. - * These indirect registers work with busy bits, - * and we will try maximal REGISTER_USB_BUSY_COUNT times to access - * the register while taking a REGISTER_BUSY_DELAY us delay - * between each attampt. When the busy bit is still set at that time, - * the access attempt is considered to have failed, - * and we will print an error. - * If the csr_mutex is already held then the _lock variants must - * be used instead. - */ -static inline void rt2500usb_register_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u16 *value) -{ - __le16 reg; - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, - USB_VENDOR_REQUEST_IN, offset, - ®, sizeof(reg)); - *value = le16_to_cpu(reg); -} - -static inline void rt2500usb_register_read_lock(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u16 *value) -{ - __le16 reg; - rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ, - USB_VENDOR_REQUEST_IN, offset, - ®, sizeof(reg), REGISTER_TIMEOUT); - *value = le16_to_cpu(reg); -} - -static inline void rt2500usb_register_multiread(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - void *value, const u16 length) -{ - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, - USB_VENDOR_REQUEST_IN, offset, - value, length); -} - -static inline void rt2500usb_register_write(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u16 value) -{ - __le16 reg = cpu_to_le16(value); - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, offset, - ®, sizeof(reg)); -} - -static inline void rt2500usb_register_write_lock(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u16 value) -{ - __le16 reg = cpu_to_le16(value); - rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, offset, - ®, sizeof(reg), REGISTER_TIMEOUT); -} - -static inline void rt2500usb_register_multiwrite(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - void *value, const u16 length) -{ - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, offset, - value, length); -} - -static int rt2500usb_regbusy_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - struct rt2x00_field16 field, - u16 *reg) -{ - unsigned int i; - - for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) { - rt2500usb_register_read_lock(rt2x00dev, offset, reg); - if (!rt2x00_get_field16(*reg, field)) - return 1; - udelay(REGISTER_BUSY_DELAY); - } - - rt2x00_err(rt2x00dev, "Indirect register access failed: offset=0x%.08x, value=0x%.08x\n", - offset, *reg); - *reg = ~0; - - return 0; -} - -#define WAIT_FOR_BBP(__dev, __reg) \ - rt2500usb_regbusy_read((__dev), PHY_CSR8, PHY_CSR8_BUSY, (__reg)) -#define WAIT_FOR_RF(__dev, __reg) \ - rt2500usb_regbusy_read((__dev), PHY_CSR10, PHY_CSR10_RF_BUSY, (__reg)) - -static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u8 value) -{ - u16 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field16(®, PHY_CSR7_DATA, value); - rt2x00_set_field16(®, PHY_CSR7_REG_ID, word); - rt2x00_set_field16(®, PHY_CSR7_READ_CONTROL, 0); - - rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u8 *value) -{ - u16 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the read request into the register. - * After the data has been written, we wait until hardware - * returns the correct value, if at any time the register - * doesn't become available in time, reg will be 0xffffffff - * which means we return 0xff to the caller. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field16(®, PHY_CSR7_REG_ID, word); - rt2x00_set_field16(®, PHY_CSR7_READ_CONTROL, 1); - - rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); - - if (WAIT_FOR_BBP(rt2x00dev, ®)) - rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, ®); - } - - *value = rt2x00_get_field16(reg, PHY_CSR7_DATA); - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2500usb_rf_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u32 value) -{ - u16 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the RF becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_RF(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field16(®, PHY_CSR9_RF_VALUE, value); - rt2500usb_register_write_lock(rt2x00dev, PHY_CSR9, reg); - - reg = 0; - rt2x00_set_field16(®, PHY_CSR10_RF_VALUE, value >> 16); - rt2x00_set_field16(®, PHY_CSR10_RF_NUMBER_OF_BITS, 20); - rt2x00_set_field16(®, PHY_CSR10_RF_IF_SELECT, 0); - rt2x00_set_field16(®, PHY_CSR10_RF_BUSY, 1); - - rt2500usb_register_write_lock(rt2x00dev, PHY_CSR10, reg); - rt2x00_rf_write(rt2x00dev, word, value); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -#ifdef CONFIG_RT2X00_LIB_DEBUGFS -static void _rt2500usb_register_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 *value) -{ - rt2500usb_register_read(rt2x00dev, offset, (u16 *)value); -} - -static void _rt2500usb_register_write(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 value) -{ - rt2500usb_register_write(rt2x00dev, offset, value); -} - -static const struct rt2x00debug rt2500usb_rt2x00debug = { - .owner = THIS_MODULE, - .csr = { - .read = _rt2500usb_register_read, - .write = _rt2500usb_register_write, - .flags = RT2X00DEBUGFS_OFFSET, - .word_base = CSR_REG_BASE, - .word_size = sizeof(u16), - .word_count = CSR_REG_SIZE / sizeof(u16), - }, - .eeprom = { - .read = rt2x00_eeprom_read, - .write = rt2x00_eeprom_write, - .word_base = EEPROM_BASE, - .word_size = sizeof(u16), - .word_count = EEPROM_SIZE / sizeof(u16), - }, - .bbp = { - .read = rt2500usb_bbp_read, - .write = rt2500usb_bbp_write, - .word_base = BBP_BASE, - .word_size = sizeof(u8), - .word_count = BBP_SIZE / sizeof(u8), - }, - .rf = { - .read = rt2x00_rf_read, - .write = rt2500usb_rf_write, - .word_base = RF_BASE, - .word_size = sizeof(u32), - .word_count = RF_SIZE / sizeof(u32), - }, -}; -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ - -static int rt2500usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) -{ - u16 reg; - - rt2500usb_register_read(rt2x00dev, MAC_CSR19, ®); - return rt2x00_get_field16(reg, MAC_CSR19_VAL7); -} - -#ifdef CONFIG_RT2X00_LIB_LEDS -static void rt2500usb_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - unsigned int enabled = brightness != LED_OFF; - u16 reg; - - rt2500usb_register_read(led->rt2x00dev, MAC_CSR20, ®); - - if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) - rt2x00_set_field16(®, MAC_CSR20_LINK, enabled); - else if (led->type == LED_TYPE_ACTIVITY) - rt2x00_set_field16(®, MAC_CSR20_ACTIVITY, enabled); - - rt2500usb_register_write(led->rt2x00dev, MAC_CSR20, reg); -} - -static int rt2500usb_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - u16 reg; - - rt2500usb_register_read(led->rt2x00dev, MAC_CSR21, ®); - rt2x00_set_field16(®, MAC_CSR21_ON_PERIOD, *delay_on); - rt2x00_set_field16(®, MAC_CSR21_OFF_PERIOD, *delay_off); - rt2500usb_register_write(led->rt2x00dev, MAC_CSR21, reg); - - return 0; -} - -static void rt2500usb_init_led(struct rt2x00_dev *rt2x00dev, - struct rt2x00_led *led, - enum led_type type) -{ - led->rt2x00dev = rt2x00dev; - led->type = type; - led->led_dev.brightness_set = rt2500usb_brightness_set; - led->led_dev.blink_set = rt2500usb_blink_set; - led->flags = LED_INITIALIZED; -} -#endif /* CONFIG_RT2X00_LIB_LEDS */ - -/* - * Configuration handlers. - */ - -/* - * rt2500usb does not differentiate between shared and pairwise - * keys, so we should use the same function for both key types. - */ -static int rt2500usb_config_key(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key) -{ - u32 mask; - u16 reg; - enum cipher curr_cipher; - - if (crypto->cmd == SET_KEY) { - /* - * Disallow to set WEP key other than with index 0, - * it is known that not work at least on some hardware. - * SW crypto will be used in that case. - */ - if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || - key->cipher == WLAN_CIPHER_SUITE_WEP104) && - key->keyidx != 0) - return -EOPNOTSUPP; - - /* - * Pairwise key will always be entry 0, but this - * could collide with a shared key on the same - * position... - */ - mask = TXRX_CSR0_KEY_ID.bit_mask; - - rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); - curr_cipher = rt2x00_get_field16(reg, TXRX_CSR0_ALGORITHM); - reg &= mask; - - if (reg && reg == mask) - return -ENOSPC; - - reg = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID); - - key->hw_key_idx += reg ? ffz(reg) : 0; - /* - * Hardware requires that all keys use the same cipher - * (e.g. TKIP-only, AES-only, but not TKIP+AES). - * If this is not the first key, compare the cipher with the - * first one and fall back to SW crypto if not the same. - */ - if (key->hw_key_idx > 0 && crypto->cipher != curr_cipher) - return -EOPNOTSUPP; - - rt2500usb_register_multiwrite(rt2x00dev, KEY_ENTRY(key->hw_key_idx), - crypto->key, sizeof(crypto->key)); - - /* - * The driver does not support the IV/EIV generation - * in hardware. However it demands the data to be provided - * both separately as well as inside the frame. - * We already provided the CONFIG_CRYPTO_COPY_IV to rt2x00lib - * to ensure rt2x00lib will not strip the data from the - * frame after the copy, now we must tell mac80211 - * to generate the IV/EIV data. - */ - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - } - - /* - * TXRX_CSR0_KEY_ID contains only single-bit fields to indicate - * a particular key is valid. - */ - rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field16(®, TXRX_CSR0_ALGORITHM, crypto->cipher); - rt2x00_set_field16(®, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER); - - mask = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID); - if (crypto->cmd == SET_KEY) - mask |= 1 << key->hw_key_idx; - else if (crypto->cmd == DISABLE_KEY) - mask &= ~(1 << key->hw_key_idx); - rt2x00_set_field16(®, TXRX_CSR0_KEY_ID, mask); - rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg); - - return 0; -} - -static void rt2500usb_config_filter(struct rt2x00_dev *rt2x00dev, - const unsigned int filter_flags) -{ - u16 reg; - - /* - * Start configuration steps. - * Note that the version error will always be dropped - * and broadcast frames will always be accepted since - * there is no filter for it at this time. - */ - rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); - rt2x00_set_field16(®, TXRX_CSR2_DROP_CRC, - !(filter_flags & FIF_FCSFAIL)); - rt2x00_set_field16(®, TXRX_CSR2_DROP_PHYSICAL, - !(filter_flags & FIF_PLCPFAIL)); - rt2x00_set_field16(®, TXRX_CSR2_DROP_CONTROL, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field16(®, TXRX_CSR2_DROP_NOT_TO_ME, 1); - rt2x00_set_field16(®, TXRX_CSR2_DROP_TODS, - !rt2x00dev->intf_ap_count); - rt2x00_set_field16(®, TXRX_CSR2_DROP_VERSION_ERROR, 1); - rt2x00_set_field16(®, TXRX_CSR2_DROP_MULTICAST, - !(filter_flags & FIF_ALLMULTI)); - rt2x00_set_field16(®, TXRX_CSR2_DROP_BROADCAST, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); -} - -static void rt2500usb_config_intf(struct rt2x00_dev *rt2x00dev, - struct rt2x00_intf *intf, - struct rt2x00intf_conf *conf, - const unsigned int flags) -{ - unsigned int bcn_preload; - u16 reg; - - if (flags & CONFIG_UPDATE_TYPE) { - /* - * Enable beacon config - */ - bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); - rt2500usb_register_read(rt2x00dev, TXRX_CSR20, ®); - rt2x00_set_field16(®, TXRX_CSR20_OFFSET, bcn_preload >> 6); - rt2x00_set_field16(®, TXRX_CSR20_BCN_EXPECT_WINDOW, - 2 * (conf->type != NL80211_IFTYPE_STATION)); - rt2500usb_register_write(rt2x00dev, TXRX_CSR20, reg); - - /* - * Enable synchronisation. - */ - rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®); - rt2x00_set_field16(®, TXRX_CSR18_OFFSET, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg); - - rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); - rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, conf->sync); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - } - - if (flags & CONFIG_UPDATE_MAC) - rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR2, conf->mac, - (3 * sizeof(__le16))); - - if (flags & CONFIG_UPDATE_BSSID) - rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR5, conf->bssid, - (3 * sizeof(__le16))); -} - -static void rt2500usb_config_erp(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_erp *erp, - u32 changed) -{ - u16 reg; - - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - rt2500usb_register_read(rt2x00dev, TXRX_CSR10, ®); - rt2x00_set_field16(®, TXRX_CSR10_AUTORESPOND_PREAMBLE, - !!erp->short_preamble); - rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg); - } - - if (changed & BSS_CHANGED_BASIC_RATES) - rt2500usb_register_write(rt2x00dev, TXRX_CSR11, - erp->basic_rates); - - if (changed & BSS_CHANGED_BEACON_INT) { - rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®); - rt2x00_set_field16(®, TXRX_CSR18_INTERVAL, - erp->beacon_int * 4); - rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg); - } - - if (changed & BSS_CHANGED_ERP_SLOT) { - rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time); - rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs); - rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs); - } -} - -static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) -{ - u8 r2; - u8 r14; - u16 csr5; - u16 csr6; - - /* - * We should never come here because rt2x00lib is supposed - * to catch this and send us the correct antenna explicitely. - */ - BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || - ant->tx == ANTENNA_SW_DIVERSITY); - - rt2500usb_bbp_read(rt2x00dev, 2, &r2); - rt2500usb_bbp_read(rt2x00dev, 14, &r14); - rt2500usb_register_read(rt2x00dev, PHY_CSR5, &csr5); - rt2500usb_register_read(rt2x00dev, PHY_CSR6, &csr6); - - /* - * Configure the TX antenna. - */ - switch (ant->tx) { - case ANTENNA_HW_DIVERSITY: - rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 1); - rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 1); - rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 1); - break; - case ANTENNA_A: - rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0); - rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 0); - rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 0); - break; - case ANTENNA_B: - default: - rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2); - rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 2); - rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 2); - break; - } - - /* - * Configure the RX antenna. - */ - switch (ant->rx) { - case ANTENNA_HW_DIVERSITY: - rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 1); - break; - case ANTENNA_A: - rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0); - break; - case ANTENNA_B: - default: - rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2); - break; - } - - /* - * RT2525E and RT5222 need to flip TX I/Q - */ - if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) { - rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1); - rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 1); - rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 1); - - /* - * RT2525E does not need RX I/Q Flip. - */ - if (rt2x00_rf(rt2x00dev, RF2525E)) - rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0); - } else { - rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 0); - rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 0); - } - - rt2500usb_bbp_write(rt2x00dev, 2, r2); - rt2500usb_bbp_write(rt2x00dev, 14, r14); - rt2500usb_register_write(rt2x00dev, PHY_CSR5, csr5); - rt2500usb_register_write(rt2x00dev, PHY_CSR6, csr6); -} - -static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev, - struct rf_channel *rf, const int txpower) -{ - /* - * Set TXpower. - */ - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); - - /* - * For RT2525E we should first set the channel to half band higher. - */ - if (rt2x00_rf(rt2x00dev, RF2525E)) { - static const u32 vals[] = { - 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2, - 0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba, - 0x000008ba, 0x000008be, 0x000008b7, 0x00000902, - 0x00000902, 0x00000906 - }; - - rt2500usb_rf_write(rt2x00dev, 2, vals[rf->channel - 1]); - if (rf->rf4) - rt2500usb_rf_write(rt2x00dev, 4, rf->rf4); - } - - rt2500usb_rf_write(rt2x00dev, 1, rf->rf1); - rt2500usb_rf_write(rt2x00dev, 2, rf->rf2); - rt2500usb_rf_write(rt2x00dev, 3, rf->rf3); - if (rf->rf4) - rt2500usb_rf_write(rt2x00dev, 4, rf->rf4); -} - -static void rt2500usb_config_txpower(struct rt2x00_dev *rt2x00dev, - const int txpower) -{ - u32 rf3; - - rt2x00_rf_read(rt2x00dev, 3, &rf3); - rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); - rt2500usb_rf_write(rt2x00dev, 3, rf3); -} - -static void rt2500usb_config_ps(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - enum dev_state state = - (libconf->conf->flags & IEEE80211_CONF_PS) ? - STATE_SLEEP : STATE_AWAKE; - u16 reg; - - if (state == STATE_SLEEP) { - rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); - rt2x00_set_field16(®, MAC_CSR18_DELAY_AFTER_BEACON, - rt2x00dev->beacon_int - 20); - rt2x00_set_field16(®, MAC_CSR18_BEACONS_BEFORE_WAKEUP, - libconf->conf->listen_interval - 1); - - /* We must first disable autowake before it can be enabled */ - rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 0); - rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); - - rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 1); - rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); - } else { - rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); - rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 0); - rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); - } - - rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); -} - -static void rt2500usb_config(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf, - const unsigned int flags) -{ - if (flags & IEEE80211_CONF_CHANGE_CHANNEL) - rt2500usb_config_channel(rt2x00dev, &libconf->rf, - libconf->conf->power_level); - if ((flags & IEEE80211_CONF_CHANGE_POWER) && - !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) - rt2500usb_config_txpower(rt2x00dev, - libconf->conf->power_level); - if (flags & IEEE80211_CONF_CHANGE_PS) - rt2500usb_config_ps(rt2x00dev, libconf); -} - -/* - * Link tuning - */ -static void rt2500usb_link_stats(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - u16 reg; - - /* - * Update FCS error count from register. - */ - rt2500usb_register_read(rt2x00dev, STA_CSR0, ®); - qual->rx_failed = rt2x00_get_field16(reg, STA_CSR0_FCS_ERROR); - - /* - * Update False CCA count from register. - */ - rt2500usb_register_read(rt2x00dev, STA_CSR3, ®); - qual->false_cca = rt2x00_get_field16(reg, STA_CSR3_FALSE_CCA_ERROR); -} - -static void rt2500usb_reset_tuner(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - u16 eeprom; - u16 value; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &eeprom); - value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R24_LOW); - rt2500usb_bbp_write(rt2x00dev, 24, value); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &eeprom); - value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R25_LOW); - rt2500usb_bbp_write(rt2x00dev, 25, value); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &eeprom); - value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_R61_LOW); - rt2500usb_bbp_write(rt2x00dev, 61, value); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &eeprom); - value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_VGCUPPER); - rt2500usb_bbp_write(rt2x00dev, 17, value); - - qual->vgc_level = value; -} - -/* - * Queue handlers. - */ -static void rt2500usb_start_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u16 reg; - - switch (queue->qid) { - case QID_RX: - rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); - rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); - break; - case QID_BEACON: - rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); - rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 1); - rt2x00_set_field16(®, TXRX_CSR19_TBCN, 1); - rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 1); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - break; - default: - break; - } -} - -static void rt2500usb_stop_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u16 reg; - - switch (queue->qid) { - case QID_RX: - rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); - rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, 1); - rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); - break; - case QID_BEACON: - rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); - rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 0); - rt2x00_set_field16(®, TXRX_CSR19_TBCN, 0); - rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - break; - default: - break; - } -} - -/* - * Initialization functions. - */ -static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev) -{ - u16 reg; - - rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0x0001, - USB_MODE_TEST, REGISTER_TIMEOUT); - rt2x00usb_vendor_request_sw(rt2x00dev, USB_SINGLE_WRITE, 0x0308, - 0x00f0, REGISTER_TIMEOUT); - - rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); - rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, 1); - rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); - - rt2500usb_register_write(rt2x00dev, MAC_CSR13, 0x1111); - rt2500usb_register_write(rt2x00dev, MAC_CSR14, 0x1e11); - - rt2500usb_register_read(rt2x00dev, MAC_CSR1, ®); - rt2x00_set_field16(®, MAC_CSR1_SOFT_RESET, 1); - rt2x00_set_field16(®, MAC_CSR1_BBP_RESET, 1); - rt2x00_set_field16(®, MAC_CSR1_HOST_READY, 0); - rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg); - - rt2500usb_register_read(rt2x00dev, MAC_CSR1, ®); - rt2x00_set_field16(®, MAC_CSR1_SOFT_RESET, 0); - rt2x00_set_field16(®, MAC_CSR1_BBP_RESET, 0); - rt2x00_set_field16(®, MAC_CSR1_HOST_READY, 0); - rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg); - - rt2500usb_register_read(rt2x00dev, TXRX_CSR5, ®); - rt2x00_set_field16(®, TXRX_CSR5_BBP_ID0, 13); - rt2x00_set_field16(®, TXRX_CSR5_BBP_ID0_VALID, 1); - rt2x00_set_field16(®, TXRX_CSR5_BBP_ID1, 12); - rt2x00_set_field16(®, TXRX_CSR5_BBP_ID1_VALID, 1); - rt2500usb_register_write(rt2x00dev, TXRX_CSR5, reg); - - rt2500usb_register_read(rt2x00dev, TXRX_CSR6, ®); - rt2x00_set_field16(®, TXRX_CSR6_BBP_ID0, 10); - rt2x00_set_field16(®, TXRX_CSR6_BBP_ID0_VALID, 1); - rt2x00_set_field16(®, TXRX_CSR6_BBP_ID1, 11); - rt2x00_set_field16(®, TXRX_CSR6_BBP_ID1_VALID, 1); - rt2500usb_register_write(rt2x00dev, TXRX_CSR6, reg); - - rt2500usb_register_read(rt2x00dev, TXRX_CSR7, ®); - rt2x00_set_field16(®, TXRX_CSR7_BBP_ID0, 7); - rt2x00_set_field16(®, TXRX_CSR7_BBP_ID0_VALID, 1); - rt2x00_set_field16(®, TXRX_CSR7_BBP_ID1, 6); - rt2x00_set_field16(®, TXRX_CSR7_BBP_ID1_VALID, 1); - rt2500usb_register_write(rt2x00dev, TXRX_CSR7, reg); - - rt2500usb_register_read(rt2x00dev, TXRX_CSR8, ®); - rt2x00_set_field16(®, TXRX_CSR8_BBP_ID0, 5); - rt2x00_set_field16(®, TXRX_CSR8_BBP_ID0_VALID, 1); - rt2x00_set_field16(®, TXRX_CSR8_BBP_ID1, 0); - rt2x00_set_field16(®, TXRX_CSR8_BBP_ID1_VALID, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR8, reg); - - rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); - rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 0); - rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, 0); - rt2x00_set_field16(®, TXRX_CSR19_TBCN, 0); - rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - - rt2500usb_register_write(rt2x00dev, TXRX_CSR21, 0xe78f); - rt2500usb_register_write(rt2x00dev, MAC_CSR9, 0xff1d); - - if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) - return -EBUSY; - - rt2500usb_register_read(rt2x00dev, MAC_CSR1, ®); - rt2x00_set_field16(®, MAC_CSR1_SOFT_RESET, 0); - rt2x00_set_field16(®, MAC_CSR1_BBP_RESET, 0); - rt2x00_set_field16(®, MAC_CSR1_HOST_READY, 1); - rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg); - - if (rt2x00_rev(rt2x00dev) >= RT2570_VERSION_C) { - rt2500usb_register_read(rt2x00dev, PHY_CSR2, ®); - rt2x00_set_field16(®, PHY_CSR2_LNA, 0); - } else { - reg = 0; - rt2x00_set_field16(®, PHY_CSR2_LNA, 1); - rt2x00_set_field16(®, PHY_CSR2_LNA_MODE, 3); - } - rt2500usb_register_write(rt2x00dev, PHY_CSR2, reg); - - rt2500usb_register_write(rt2x00dev, MAC_CSR11, 0x0002); - rt2500usb_register_write(rt2x00dev, MAC_CSR22, 0x0053); - rt2500usb_register_write(rt2x00dev, MAC_CSR15, 0x01ee); - rt2500usb_register_write(rt2x00dev, MAC_CSR16, 0x0000); - - rt2500usb_register_read(rt2x00dev, MAC_CSR8, ®); - rt2x00_set_field16(®, MAC_CSR8_MAX_FRAME_UNIT, - rt2x00dev->rx->data_size); - rt2500usb_register_write(rt2x00dev, MAC_CSR8, reg); - - rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field16(®, TXRX_CSR0_ALGORITHM, CIPHER_NONE); - rt2x00_set_field16(®, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER); - rt2x00_set_field16(®, TXRX_CSR0_KEY_ID, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg); - - rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); - rt2x00_set_field16(®, MAC_CSR18_DELAY_AFTER_BEACON, 90); - rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); - - rt2500usb_register_read(rt2x00dev, PHY_CSR4, ®); - rt2x00_set_field16(®, PHY_CSR4_LOW_RF_LE, 1); - rt2500usb_register_write(rt2x00dev, PHY_CSR4, reg); - - rt2500usb_register_read(rt2x00dev, TXRX_CSR1, ®); - rt2x00_set_field16(®, TXRX_CSR1_AUTO_SEQUENCE, 1); - rt2500usb_register_write(rt2x00dev, TXRX_CSR1, reg); - - return 0; -} - -static int rt2500usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u8 value; - - for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) { - rt2500usb_bbp_read(rt2x00dev, 0, &value); - if ((value != 0xff) && (value != 0x00)) - return 0; - udelay(REGISTER_BUSY_DELAY); - } - - rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); - return -EACCES; -} - -static int rt2500usb_init_bbp(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u16 eeprom; - u8 value; - u8 reg_id; - - if (unlikely(rt2500usb_wait_bbp_ready(rt2x00dev))) - return -EACCES; - - rt2500usb_bbp_write(rt2x00dev, 3, 0x02); - rt2500usb_bbp_write(rt2x00dev, 4, 0x19); - rt2500usb_bbp_write(rt2x00dev, 14, 0x1c); - rt2500usb_bbp_write(rt2x00dev, 15, 0x30); - rt2500usb_bbp_write(rt2x00dev, 16, 0xac); - rt2500usb_bbp_write(rt2x00dev, 18, 0x18); - rt2500usb_bbp_write(rt2x00dev, 19, 0xff); - rt2500usb_bbp_write(rt2x00dev, 20, 0x1e); - rt2500usb_bbp_write(rt2x00dev, 21, 0x08); - rt2500usb_bbp_write(rt2x00dev, 22, 0x08); - rt2500usb_bbp_write(rt2x00dev, 23, 0x08); - rt2500usb_bbp_write(rt2x00dev, 24, 0x80); - rt2500usb_bbp_write(rt2x00dev, 25, 0x50); - rt2500usb_bbp_write(rt2x00dev, 26, 0x08); - rt2500usb_bbp_write(rt2x00dev, 27, 0x23); - rt2500usb_bbp_write(rt2x00dev, 30, 0x10); - rt2500usb_bbp_write(rt2x00dev, 31, 0x2b); - rt2500usb_bbp_write(rt2x00dev, 32, 0xb9); - rt2500usb_bbp_write(rt2x00dev, 34, 0x12); - rt2500usb_bbp_write(rt2x00dev, 35, 0x50); - rt2500usb_bbp_write(rt2x00dev, 39, 0xc4); - rt2500usb_bbp_write(rt2x00dev, 40, 0x02); - rt2500usb_bbp_write(rt2x00dev, 41, 0x60); - rt2500usb_bbp_write(rt2x00dev, 53, 0x10); - rt2500usb_bbp_write(rt2x00dev, 54, 0x18); - rt2500usb_bbp_write(rt2x00dev, 56, 0x08); - rt2500usb_bbp_write(rt2x00dev, 57, 0x10); - rt2500usb_bbp_write(rt2x00dev, 58, 0x08); - rt2500usb_bbp_write(rt2x00dev, 61, 0x60); - rt2500usb_bbp_write(rt2x00dev, 62, 0x10); - rt2500usb_bbp_write(rt2x00dev, 75, 0xff); - - for (i = 0; i < EEPROM_BBP_SIZE; i++) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); - - if (eeprom != 0xffff && eeprom != 0x0000) { - reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); - value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - rt2500usb_bbp_write(rt2x00dev, reg_id, value); - } - } - - return 0; -} - -/* - * Device state switch handlers. - */ -static int rt2500usb_enable_radio(struct rt2x00_dev *rt2x00dev) -{ - /* - * Initialize all registers. - */ - if (unlikely(rt2500usb_init_registers(rt2x00dev) || - rt2500usb_init_bbp(rt2x00dev))) - return -EIO; - - return 0; -} - -static void rt2500usb_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - rt2500usb_register_write(rt2x00dev, MAC_CSR13, 0x2121); - rt2500usb_register_write(rt2x00dev, MAC_CSR14, 0x2121); - - /* - * Disable synchronisation. - */ - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, 0); - - rt2x00usb_disable_radio(rt2x00dev); -} - -static int rt2500usb_set_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - u16 reg; - u16 reg2; - unsigned int i; - char put_to_sleep; - char bbp_state; - char rf_state; - - put_to_sleep = (state != STATE_AWAKE); - - reg = 0; - rt2x00_set_field16(®, MAC_CSR17_BBP_DESIRE_STATE, state); - rt2x00_set_field16(®, MAC_CSR17_RF_DESIRE_STATE, state); - rt2x00_set_field16(®, MAC_CSR17_PUT_TO_SLEEP, put_to_sleep); - rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg); - rt2x00_set_field16(®, MAC_CSR17_SET_STATE, 1); - rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg); - - /* - * Device is not guaranteed to be in the requested state yet. - * We must wait until the register indicates that the - * device has entered the correct state. - */ - for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) { - rt2500usb_register_read(rt2x00dev, MAC_CSR17, ®2); - bbp_state = rt2x00_get_field16(reg2, MAC_CSR17_BBP_CURR_STATE); - rf_state = rt2x00_get_field16(reg2, MAC_CSR17_RF_CURR_STATE); - if (bbp_state == state && rf_state == state) - return 0; - rt2500usb_register_write(rt2x00dev, MAC_CSR17, reg); - msleep(30); - } - - return -EBUSY; -} - -static int rt2500usb_set_device_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - int retval = 0; - - switch (state) { - case STATE_RADIO_ON: - retval = rt2500usb_enable_radio(rt2x00dev); - break; - case STATE_RADIO_OFF: - rt2500usb_disable_radio(rt2x00dev); - break; - case STATE_RADIO_IRQ_ON: - case STATE_RADIO_IRQ_OFF: - /* No support, but no error either */ - break; - case STATE_DEEP_SLEEP: - case STATE_SLEEP: - case STATE_STANDBY: - case STATE_AWAKE: - retval = rt2500usb_set_state(rt2x00dev, state); - break; - default: - retval = -ENOTSUPP; - break; - } - - if (unlikely(retval)) - rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", - state, retval); - - return retval; -} - -/* - * TX descriptor initialization - */ -static void rt2500usb_write_tx_desc(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - __le32 *txd = (__le32 *) entry->skb->data; - u32 word; - - /* - * Start writing the descriptor words. - */ - rt2x00_desc_read(txd, 0, &word); - rt2x00_set_field32(&word, TXD_W0_RETRY_LIMIT, txdesc->retry_limit); - rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, - test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_ACK, - test_bit(ENTRY_TXD_ACK, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, - test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_OFDM, - (txdesc->rate_mode == RATE_MODE_OFDM)); - rt2x00_set_field32(&word, TXD_W0_NEW_SEQ, - test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); - rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); - rt2x00_set_field32(&word, TXD_W0_CIPHER, !!txdesc->cipher); - rt2x00_set_field32(&word, TXD_W0_KEY_ID, txdesc->key_idx); - rt2x00_desc_write(txd, 0, word); - - rt2x00_desc_read(txd, 1, &word); - rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); - rt2x00_set_field32(&word, TXD_W1_AIFS, entry->queue->aifs); - rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); - rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); - rt2x00_desc_write(txd, 1, word); - - rt2x00_desc_read(txd, 2, &word); - rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); - rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, - txdesc->u.plcp.length_low); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, - txdesc->u.plcp.length_high); - rt2x00_desc_write(txd, 2, word); - - if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { - _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); - _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); - } - - /* - * Register descriptor details in skb frame descriptor. - */ - skbdesc->flags |= SKBDESC_DESC_IN_SKB; - skbdesc->desc = txd; - skbdesc->desc_len = TXD_DESC_SIZE; -} - -/* - * TX data initialization - */ -static void rt2500usb_beacondone(struct urb *urb); - -static void rt2500usb_write_beacon(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); - struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; - int pipe = usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint); - int length; - u16 reg, reg0; - - /* - * Disable beaconing while we are reloading the beacon data, - * otherwise we might be sending out invalid data. - */ - rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); - rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - - /* - * Add space for the descriptor in front of the skb. - */ - skb_push(entry->skb, TXD_DESC_SIZE); - memset(entry->skb->data, 0, TXD_DESC_SIZE); - - /* - * Write the TX descriptor for the beacon. - */ - rt2500usb_write_tx_desc(entry, txdesc); - - /* - * Dump beacon to userspace through debugfs. - */ - rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); - - /* - * USB devices cannot blindly pass the skb->len as the - * length of the data to usb_fill_bulk_urb. Pass the skb - * to the driver to determine what the length should be. - */ - length = rt2x00dev->ops->lib->get_tx_data_len(entry); - - usb_fill_bulk_urb(bcn_priv->urb, usb_dev, pipe, - entry->skb->data, length, rt2500usb_beacondone, - entry); - - /* - * Second we need to create the guardian byte. - * We only need a single byte, so lets recycle - * the 'flags' field we are not using for beacons. - */ - bcn_priv->guardian_data = 0; - usb_fill_bulk_urb(bcn_priv->guardian_urb, usb_dev, pipe, - &bcn_priv->guardian_data, 1, rt2500usb_beacondone, - entry); - - /* - * Send out the guardian byte. - */ - usb_submit_urb(bcn_priv->guardian_urb, GFP_ATOMIC); - - /* - * Enable beaconing again. - */ - rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 1); - rt2x00_set_field16(®, TXRX_CSR19_TBCN, 1); - reg0 = reg; - rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 1); - /* - * Beacon generation will fail initially. - * To prevent this we need to change the TXRX_CSR19 - * register several times (reg0 is the same as reg - * except for TXRX_CSR19_BEACON_GEN, which is 0 in reg0 - * and 1 in reg). - */ - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); -} - -static int rt2500usb_get_tx_data_len(struct queue_entry *entry) -{ - int length; - - /* - * The length _must_ be a multiple of 2, - * but it must _not_ be a multiple of the USB packet size. - */ - length = roundup(entry->skb->len, 2); - length += (2 * !(length % entry->queue->usb_maxpacket)); - - return length; -} - -/* - * RX control handlers - */ -static void rt2500usb_fill_rxdone(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct queue_entry_priv_usb *entry_priv = entry->priv_data; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - __le32 *rxd = - (__le32 *)(entry->skb->data + - (entry_priv->urb->actual_length - - entry->queue->desc_size)); - u32 word0; - u32 word1; - - /* - * Copy descriptor to the skbdesc->desc buffer, making it safe from moving of - * frame data in rt2x00usb. - */ - memcpy(skbdesc->desc, rxd, skbdesc->desc_len); - rxd = (__le32 *)skbdesc->desc; - - /* - * It is now safe to read the descriptor on all architectures. - */ - rt2x00_desc_read(rxd, 0, &word0); - rt2x00_desc_read(rxd, 1, &word1); - - if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; - if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; - - rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER); - if (rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR)) - rxdesc->cipher_status = RX_CRYPTO_FAIL_KEY; - - if (rxdesc->cipher != CIPHER_NONE) { - _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]); - _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]); - rxdesc->dev_flags |= RXDONE_CRYPTO_IV; - - /* ICV is located at the end of frame */ - - rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; - if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) - rxdesc->flags |= RX_FLAG_DECRYPTED; - else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) - rxdesc->flags |= RX_FLAG_MMIC_ERROR; - } - - /* - * Obtain the status about this packet. - * When frame was received with an OFDM bitrate, - * the signal is the PLCP value. If it was received with - * a CCK bitrate the signal is the rate in 100kbit/s. - */ - rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); - rxdesc->rssi = - rt2x00_get_field32(word1, RXD_W1_RSSI) - rt2x00dev->rssi_offset; - rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); - - if (rt2x00_get_field32(word0, RXD_W0_OFDM)) - rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; - else - rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; - if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) - rxdesc->dev_flags |= RXDONE_MY_BSS; - - /* - * Adjust the skb memory window to the frame boundaries. - */ - skb_trim(entry->skb, rxdesc->size); -} - -/* - * Interrupt functions. - */ -static void rt2500usb_beacondone(struct urb *urb) -{ - struct queue_entry *entry = (struct queue_entry *)urb->context; - struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; - - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &entry->queue->rt2x00dev->flags)) - return; - - /* - * Check if this was the guardian beacon, - * if that was the case we need to send the real beacon now. - * Otherwise we should free the sk_buffer, the device - * should be doing the rest of the work now. - */ - if (bcn_priv->guardian_urb == urb) { - usb_submit_urb(bcn_priv->urb, GFP_ATOMIC); - } else if (bcn_priv->urb == urb) { - dev_kfree_skb(entry->skb); - entry->skb = NULL; - } -} - -/* - * Device probe functions. - */ -static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) -{ - u16 word; - u8 *mac; - u8 bbp; - - rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE); - - /* - * Start validation of the data that has been read. - */ - mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); - rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, - ANTENNA_SW_DIVERSITY); - rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, - ANTENNA_SW_DIVERSITY); - rt2x00_set_field16(&word, EEPROM_ANTENNA_LED_MODE, - LED_MODE_DEFAULT); - rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); - rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); - rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2522); - rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); - rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); - rt2x00_set_field16(&word, EEPROM_NIC_DYN_BBP_TUNE, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CCK_TX_POWER, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); - rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_CALIBRATE_OFFSET_RSSI, - DEFAULT_RSSI_OFFSET); - rt2x00_eeprom_write(rt2x00dev, EEPROM_CALIBRATE_OFFSET, word); - rt2x00_eeprom_dbg(rt2x00dev, "Calibrate offset: 0x%04x\n", - word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_BBPTUNE_THRESHOLD, 45); - rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE, word); - rt2x00_eeprom_dbg(rt2x00dev, "BBPtune: 0x%04x\n", word); - } - - /* - * Switch lower vgc bound to current BBP R17 value, - * lower the value a bit for better quality. - */ - rt2500usb_bbp_read(rt2x00dev, 17, &bbp); - bbp -= 6; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCUPPER, 0x40); - rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp); - rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word); - rt2x00_eeprom_dbg(rt2x00dev, "BBPtune vgc: 0x%04x\n", word); - } else { - rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp); - rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R17, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_LOW, 0x48); - rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_HIGH, 0x41); - rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R17, word); - rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r17: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_LOW, 0x40); - rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_HIGH, 0x80); - rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R24, word); - rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r24: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_LOW, 0x40); - rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_HIGH, 0x50); - rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R25, word); - rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r25: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_LOW, 0x60); - rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_HIGH, 0x6d); - rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R61, word); - rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r61: 0x%04x\n", word); - } - - return 0; -} - -static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) -{ - u16 reg; - u16 value; - u16 eeprom; - - /* - * Read EEPROM word for configuration. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); - - /* - * Identify RF chipset. - */ - value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); - rt2500usb_register_read(rt2x00dev, MAC_CSR0, ®); - rt2x00_set_chip(rt2x00dev, RT2570, value, reg); - - if (((reg & 0xfff0) != 0) || ((reg & 0x0000000f) == 0)) { - rt2x00_err(rt2x00dev, "Invalid RT chipset detected\n"); - return -ENODEV; - } - - if (!rt2x00_rf(rt2x00dev, RF2522) && - !rt2x00_rf(rt2x00dev, RF2523) && - !rt2x00_rf(rt2x00dev, RF2524) && - !rt2x00_rf(rt2x00dev, RF2525) && - !rt2x00_rf(rt2x00dev, RF2525E) && - !rt2x00_rf(rt2x00dev, RF5222)) { - rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); - return -ENODEV; - } - - /* - * Identify default antenna configuration. - */ - rt2x00dev->default_ant.tx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); - rt2x00dev->default_ant.rx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); - - /* - * When the eeprom indicates SW_DIVERSITY use HW_DIVERSITY instead. - * I am not 100% sure about this, but the legacy drivers do not - * indicate antenna swapping in software is required when - * diversity is enabled. - */ - if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY) - rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY; - if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY) - rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY; - - /* - * Store led mode, for correct led behaviour. - */ -#ifdef CONFIG_RT2X00_LIB_LEDS - value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); - - rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); - if (value == LED_MODE_TXRX_ACTIVITY || - value == LED_MODE_DEFAULT || - value == LED_MODE_ASUS) - rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_qual, - LED_TYPE_ACTIVITY); -#endif /* CONFIG_RT2X00_LIB_LEDS */ - - /* - * Detect if this device has an hardware controlled radio. - */ - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) - __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); - - /* - * Read the RSSI <-> dBm offset information. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &eeprom); - rt2x00dev->rssi_offset = - rt2x00_get_field16(eeprom, EEPROM_CALIBRATE_OFFSET_RSSI); - - return 0; -} - -/* - * RF value list for RF2522 - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_bg_2522[] = { - { 1, 0x00002050, 0x000c1fda, 0x00000101, 0 }, - { 2, 0x00002050, 0x000c1fee, 0x00000101, 0 }, - { 3, 0x00002050, 0x000c2002, 0x00000101, 0 }, - { 4, 0x00002050, 0x000c2016, 0x00000101, 0 }, - { 5, 0x00002050, 0x000c202a, 0x00000101, 0 }, - { 6, 0x00002050, 0x000c203e, 0x00000101, 0 }, - { 7, 0x00002050, 0x000c2052, 0x00000101, 0 }, - { 8, 0x00002050, 0x000c2066, 0x00000101, 0 }, - { 9, 0x00002050, 0x000c207a, 0x00000101, 0 }, - { 10, 0x00002050, 0x000c208e, 0x00000101, 0 }, - { 11, 0x00002050, 0x000c20a2, 0x00000101, 0 }, - { 12, 0x00002050, 0x000c20b6, 0x00000101, 0 }, - { 13, 0x00002050, 0x000c20ca, 0x00000101, 0 }, - { 14, 0x00002050, 0x000c20fa, 0x00000101, 0 }, -}; - -/* - * RF value list for RF2523 - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_bg_2523[] = { - { 1, 0x00022010, 0x00000c9e, 0x000e0111, 0x00000a1b }, - { 2, 0x00022010, 0x00000ca2, 0x000e0111, 0x00000a1b }, - { 3, 0x00022010, 0x00000ca6, 0x000e0111, 0x00000a1b }, - { 4, 0x00022010, 0x00000caa, 0x000e0111, 0x00000a1b }, - { 5, 0x00022010, 0x00000cae, 0x000e0111, 0x00000a1b }, - { 6, 0x00022010, 0x00000cb2, 0x000e0111, 0x00000a1b }, - { 7, 0x00022010, 0x00000cb6, 0x000e0111, 0x00000a1b }, - { 8, 0x00022010, 0x00000cba, 0x000e0111, 0x00000a1b }, - { 9, 0x00022010, 0x00000cbe, 0x000e0111, 0x00000a1b }, - { 10, 0x00022010, 0x00000d02, 0x000e0111, 0x00000a1b }, - { 11, 0x00022010, 0x00000d06, 0x000e0111, 0x00000a1b }, - { 12, 0x00022010, 0x00000d0a, 0x000e0111, 0x00000a1b }, - { 13, 0x00022010, 0x00000d0e, 0x000e0111, 0x00000a1b }, - { 14, 0x00022010, 0x00000d1a, 0x000e0111, 0x00000a03 }, -}; - -/* - * RF value list for RF2524 - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_bg_2524[] = { - { 1, 0x00032020, 0x00000c9e, 0x00000101, 0x00000a1b }, - { 2, 0x00032020, 0x00000ca2, 0x00000101, 0x00000a1b }, - { 3, 0x00032020, 0x00000ca6, 0x00000101, 0x00000a1b }, - { 4, 0x00032020, 0x00000caa, 0x00000101, 0x00000a1b }, - { 5, 0x00032020, 0x00000cae, 0x00000101, 0x00000a1b }, - { 6, 0x00032020, 0x00000cb2, 0x00000101, 0x00000a1b }, - { 7, 0x00032020, 0x00000cb6, 0x00000101, 0x00000a1b }, - { 8, 0x00032020, 0x00000cba, 0x00000101, 0x00000a1b }, - { 9, 0x00032020, 0x00000cbe, 0x00000101, 0x00000a1b }, - { 10, 0x00032020, 0x00000d02, 0x00000101, 0x00000a1b }, - { 11, 0x00032020, 0x00000d06, 0x00000101, 0x00000a1b }, - { 12, 0x00032020, 0x00000d0a, 0x00000101, 0x00000a1b }, - { 13, 0x00032020, 0x00000d0e, 0x00000101, 0x00000a1b }, - { 14, 0x00032020, 0x00000d1a, 0x00000101, 0x00000a03 }, -}; - -/* - * RF value list for RF2525 - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_bg_2525[] = { - { 1, 0x00022020, 0x00080c9e, 0x00060111, 0x00000a1b }, - { 2, 0x00022020, 0x00080ca2, 0x00060111, 0x00000a1b }, - { 3, 0x00022020, 0x00080ca6, 0x00060111, 0x00000a1b }, - { 4, 0x00022020, 0x00080caa, 0x00060111, 0x00000a1b }, - { 5, 0x00022020, 0x00080cae, 0x00060111, 0x00000a1b }, - { 6, 0x00022020, 0x00080cb2, 0x00060111, 0x00000a1b }, - { 7, 0x00022020, 0x00080cb6, 0x00060111, 0x00000a1b }, - { 8, 0x00022020, 0x00080cba, 0x00060111, 0x00000a1b }, - { 9, 0x00022020, 0x00080cbe, 0x00060111, 0x00000a1b }, - { 10, 0x00022020, 0x00080d02, 0x00060111, 0x00000a1b }, - { 11, 0x00022020, 0x00080d06, 0x00060111, 0x00000a1b }, - { 12, 0x00022020, 0x00080d0a, 0x00060111, 0x00000a1b }, - { 13, 0x00022020, 0x00080d0e, 0x00060111, 0x00000a1b }, - { 14, 0x00022020, 0x00080d1a, 0x00060111, 0x00000a03 }, -}; - -/* - * RF value list for RF2525e - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_bg_2525e[] = { - { 1, 0x00022010, 0x0000089a, 0x00060111, 0x00000e1b }, - { 2, 0x00022010, 0x0000089e, 0x00060111, 0x00000e07 }, - { 3, 0x00022010, 0x0000089e, 0x00060111, 0x00000e1b }, - { 4, 0x00022010, 0x000008a2, 0x00060111, 0x00000e07 }, - { 5, 0x00022010, 0x000008a2, 0x00060111, 0x00000e1b }, - { 6, 0x00022010, 0x000008a6, 0x00060111, 0x00000e07 }, - { 7, 0x00022010, 0x000008a6, 0x00060111, 0x00000e1b }, - { 8, 0x00022010, 0x000008aa, 0x00060111, 0x00000e07 }, - { 9, 0x00022010, 0x000008aa, 0x00060111, 0x00000e1b }, - { 10, 0x00022010, 0x000008ae, 0x00060111, 0x00000e07 }, - { 11, 0x00022010, 0x000008ae, 0x00060111, 0x00000e1b }, - { 12, 0x00022010, 0x000008b2, 0x00060111, 0x00000e07 }, - { 13, 0x00022010, 0x000008b2, 0x00060111, 0x00000e1b }, - { 14, 0x00022010, 0x000008b6, 0x00060111, 0x00000e23 }, -}; - -/* - * RF value list for RF5222 - * Supports: 2.4 GHz & 5.2 GHz - */ -static const struct rf_channel rf_vals_5222[] = { - { 1, 0x00022020, 0x00001136, 0x00000101, 0x00000a0b }, - { 2, 0x00022020, 0x0000113a, 0x00000101, 0x00000a0b }, - { 3, 0x00022020, 0x0000113e, 0x00000101, 0x00000a0b }, - { 4, 0x00022020, 0x00001182, 0x00000101, 0x00000a0b }, - { 5, 0x00022020, 0x00001186, 0x00000101, 0x00000a0b }, - { 6, 0x00022020, 0x0000118a, 0x00000101, 0x00000a0b }, - { 7, 0x00022020, 0x0000118e, 0x00000101, 0x00000a0b }, - { 8, 0x00022020, 0x00001192, 0x00000101, 0x00000a0b }, - { 9, 0x00022020, 0x00001196, 0x00000101, 0x00000a0b }, - { 10, 0x00022020, 0x0000119a, 0x00000101, 0x00000a0b }, - { 11, 0x00022020, 0x0000119e, 0x00000101, 0x00000a0b }, - { 12, 0x00022020, 0x000011a2, 0x00000101, 0x00000a0b }, - { 13, 0x00022020, 0x000011a6, 0x00000101, 0x00000a0b }, - { 14, 0x00022020, 0x000011ae, 0x00000101, 0x00000a1b }, - - /* 802.11 UNI / HyperLan 2 */ - { 36, 0x00022010, 0x00018896, 0x00000101, 0x00000a1f }, - { 40, 0x00022010, 0x0001889a, 0x00000101, 0x00000a1f }, - { 44, 0x00022010, 0x0001889e, 0x00000101, 0x00000a1f }, - { 48, 0x00022010, 0x000188a2, 0x00000101, 0x00000a1f }, - { 52, 0x00022010, 0x000188a6, 0x00000101, 0x00000a1f }, - { 66, 0x00022010, 0x000188aa, 0x00000101, 0x00000a1f }, - { 60, 0x00022010, 0x000188ae, 0x00000101, 0x00000a1f }, - { 64, 0x00022010, 0x000188b2, 0x00000101, 0x00000a1f }, - - /* 802.11 HyperLan 2 */ - { 100, 0x00022010, 0x00008802, 0x00000101, 0x00000a0f }, - { 104, 0x00022010, 0x00008806, 0x00000101, 0x00000a0f }, - { 108, 0x00022010, 0x0000880a, 0x00000101, 0x00000a0f }, - { 112, 0x00022010, 0x0000880e, 0x00000101, 0x00000a0f }, - { 116, 0x00022010, 0x00008812, 0x00000101, 0x00000a0f }, - { 120, 0x00022010, 0x00008816, 0x00000101, 0x00000a0f }, - { 124, 0x00022010, 0x0000881a, 0x00000101, 0x00000a0f }, - { 128, 0x00022010, 0x0000881e, 0x00000101, 0x00000a0f }, - { 132, 0x00022010, 0x00008822, 0x00000101, 0x00000a0f }, - { 136, 0x00022010, 0x00008826, 0x00000101, 0x00000a0f }, - - /* 802.11 UNII */ - { 140, 0x00022010, 0x0000882a, 0x00000101, 0x00000a0f }, - { 149, 0x00022020, 0x000090a6, 0x00000101, 0x00000a07 }, - { 153, 0x00022020, 0x000090ae, 0x00000101, 0x00000a07 }, - { 157, 0x00022020, 0x000090b6, 0x00000101, 0x00000a07 }, - { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 }, -}; - -static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) -{ - struct hw_mode_spec *spec = &rt2x00dev->spec; - struct channel_info *info; - char *tx_power; - unsigned int i; - - /* - * Initialize all hw fields. - * - * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are - * capable of sending the buffered frames out after the DTIM - * transmission using rt2x00lib_beacondone. This will send out - * multicast and broadcast traffic immediately instead of buffering it - * infinitly and thus dropping it after some time. - */ - ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); - ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); - ieee80211_hw_set(rt2x00dev->hw, RX_INCLUDES_FCS); - ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); - - /* - * Disable powersaving as default. - */ - rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); - SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, - rt2x00_eeprom_addr(rt2x00dev, - EEPROM_MAC_ADDR_0)); - - /* - * Initialize hw_mode information. - */ - spec->supported_bands = SUPPORT_BAND_2GHZ; - spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - - if (rt2x00_rf(rt2x00dev, RF2522)) { - spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); - spec->channels = rf_vals_bg_2522; - } else if (rt2x00_rf(rt2x00dev, RF2523)) { - spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523); - spec->channels = rf_vals_bg_2523; - } else if (rt2x00_rf(rt2x00dev, RF2524)) { - spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524); - spec->channels = rf_vals_bg_2524; - } else if (rt2x00_rf(rt2x00dev, RF2525)) { - spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525); - spec->channels = rf_vals_bg_2525; - } else if (rt2x00_rf(rt2x00dev, RF2525E)) { - spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e); - spec->channels = rf_vals_bg_2525e; - } else if (rt2x00_rf(rt2x00dev, RF5222)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; - spec->num_channels = ARRAY_SIZE(rf_vals_5222); - spec->channels = rf_vals_5222; - } - - /* - * Create channel information array - */ - info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - spec->channels_info = info; - - tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); - for (i = 0; i < 14; i++) { - info[i].max_power = MAX_TXPOWER; - info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); - } - - if (spec->num_channels > 14) { - for (i = 14; i < spec->num_channels; i++) { - info[i].max_power = MAX_TXPOWER; - info[i].default_power1 = DEFAULT_TXPOWER; - } - } - - return 0; -} - -static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) -{ - int retval; - u16 reg; - - /* - * Allocate eeprom data. - */ - retval = rt2500usb_validate_eeprom(rt2x00dev); - if (retval) - return retval; - - retval = rt2500usb_init_eeprom(rt2x00dev); - if (retval) - return retval; - - /* - * Enable rfkill polling by setting GPIO direction of the - * rfkill switch GPIO pin correctly. - */ - rt2500usb_register_read(rt2x00dev, MAC_CSR19, ®); - rt2x00_set_field16(®, MAC_CSR19_DIR0, 0); - rt2500usb_register_write(rt2x00dev, MAC_CSR19, reg); - - /* - * Initialize hw specifications. - */ - retval = rt2500usb_probe_hw_mode(rt2x00dev); - if (retval) - return retval; - - /* - * This device requires the atim queue - */ - __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags); - if (!modparam_nohwcrypt) { - __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_COPY_IV, &rt2x00dev->cap_flags); - } - __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); - - /* - * Set the rssi offset. - */ - rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; - - return 0; -} - -static const struct ieee80211_ops rt2500usb_mac80211_ops = { - .tx = rt2x00mac_tx, - .start = rt2x00mac_start, - .stop = rt2x00mac_stop, - .add_interface = rt2x00mac_add_interface, - .remove_interface = rt2x00mac_remove_interface, - .config = rt2x00mac_config, - .configure_filter = rt2x00mac_configure_filter, - .set_tim = rt2x00mac_set_tim, - .set_key = rt2x00mac_set_key, - .sw_scan_start = rt2x00mac_sw_scan_start, - .sw_scan_complete = rt2x00mac_sw_scan_complete, - .get_stats = rt2x00mac_get_stats, - .bss_info_changed = rt2x00mac_bss_info_changed, - .conf_tx = rt2x00mac_conf_tx, - .rfkill_poll = rt2x00mac_rfkill_poll, - .flush = rt2x00mac_flush, - .set_antenna = rt2x00mac_set_antenna, - .get_antenna = rt2x00mac_get_antenna, - .get_ringparam = rt2x00mac_get_ringparam, - .tx_frames_pending = rt2x00mac_tx_frames_pending, -}; - -static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = { - .probe_hw = rt2500usb_probe_hw, - .initialize = rt2x00usb_initialize, - .uninitialize = rt2x00usb_uninitialize, - .clear_entry = rt2x00usb_clear_entry, - .set_device_state = rt2500usb_set_device_state, - .rfkill_poll = rt2500usb_rfkill_poll, - .link_stats = rt2500usb_link_stats, - .reset_tuner = rt2500usb_reset_tuner, - .watchdog = rt2x00usb_watchdog, - .start_queue = rt2500usb_start_queue, - .kick_queue = rt2x00usb_kick_queue, - .stop_queue = rt2500usb_stop_queue, - .flush_queue = rt2x00usb_flush_queue, - .write_tx_desc = rt2500usb_write_tx_desc, - .write_beacon = rt2500usb_write_beacon, - .get_tx_data_len = rt2500usb_get_tx_data_len, - .fill_rxdone = rt2500usb_fill_rxdone, - .config_shared_key = rt2500usb_config_key, - .config_pairwise_key = rt2500usb_config_key, - .config_filter = rt2500usb_config_filter, - .config_intf = rt2500usb_config_intf, - .config_erp = rt2500usb_config_erp, - .config_ant = rt2500usb_config_ant, - .config = rt2500usb_config, -}; - -static void rt2500usb_queue_init(struct data_queue *queue) -{ - switch (queue->qid) { - case QID_RX: - queue->limit = 32; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = RXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_usb); - break; - - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - queue->limit = 32; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_usb); - break; - - case QID_BEACON: - queue->limit = 1; - queue->data_size = MGMT_FRAME_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_usb_bcn); - break; - - case QID_ATIM: - queue->limit = 8; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_usb); - break; - - default: - BUG(); - break; - } -} - -static const struct rt2x00_ops rt2500usb_ops = { - .name = KBUILD_MODNAME, - .max_ap_intf = 1, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .queue_init = rt2500usb_queue_init, - .lib = &rt2500usb_rt2x00_ops, - .hw = &rt2500usb_mac80211_ops, -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2500usb_rt2x00debug, -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -}; - -/* - * rt2500usb module information. - */ -static struct usb_device_id rt2500usb_device_table[] = { - /* ASUS */ - { USB_DEVICE(0x0b05, 0x1706) }, - { USB_DEVICE(0x0b05, 0x1707) }, - /* Belkin */ - { USB_DEVICE(0x050d, 0x7050) }, /* FCC ID: K7SF5D7050A ver. 2.x */ - { USB_DEVICE(0x050d, 0x7051) }, - /* Cisco Systems */ - { USB_DEVICE(0x13b1, 0x000d) }, - { USB_DEVICE(0x13b1, 0x0011) }, - { USB_DEVICE(0x13b1, 0x001a) }, - /* Conceptronic */ - { USB_DEVICE(0x14b2, 0x3c02) }, - /* D-LINK */ - { USB_DEVICE(0x2001, 0x3c00) }, - /* Gigabyte */ - { USB_DEVICE(0x1044, 0x8001) }, - { USB_DEVICE(0x1044, 0x8007) }, - /* Hercules */ - { USB_DEVICE(0x06f8, 0xe000) }, - /* Melco */ - { USB_DEVICE(0x0411, 0x005e) }, - { USB_DEVICE(0x0411, 0x0066) }, - { USB_DEVICE(0x0411, 0x0067) }, - { USB_DEVICE(0x0411, 0x008b) }, - { USB_DEVICE(0x0411, 0x0097) }, - /* MSI */ - { USB_DEVICE(0x0db0, 0x6861) }, - { USB_DEVICE(0x0db0, 0x6865) }, - { USB_DEVICE(0x0db0, 0x6869) }, - /* Ralink */ - { USB_DEVICE(0x148f, 0x1706) }, - { USB_DEVICE(0x148f, 0x2570) }, - { USB_DEVICE(0x148f, 0x9020) }, - /* Sagem */ - { USB_DEVICE(0x079b, 0x004b) }, - /* Siemens */ - { USB_DEVICE(0x0681, 0x3c06) }, - /* SMC */ - { USB_DEVICE(0x0707, 0xee13) }, - /* Spairon */ - { USB_DEVICE(0x114b, 0x0110) }, - /* SURECOM */ - { USB_DEVICE(0x0769, 0x11f3) }, - /* Trust */ - { USB_DEVICE(0x0eb0, 0x9020) }, - /* VTech */ - { USB_DEVICE(0x0f88, 0x3012) }, - /* Zinwell */ - { USB_DEVICE(0x5a57, 0x0260) }, - { 0, } -}; - -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("Ralink RT2500 USB Wireless LAN driver."); -MODULE_SUPPORTED_DEVICE("Ralink RT2570 USB chipset based cards"); -MODULE_DEVICE_TABLE(usb, rt2500usb_device_table); -MODULE_LICENSE("GPL"); - -static int rt2500usb_probe(struct usb_interface *usb_intf, - const struct usb_device_id *id) -{ - return rt2x00usb_probe(usb_intf, &rt2500usb_ops); -} - -static struct usb_driver rt2500usb_driver = { - .name = KBUILD_MODNAME, - .id_table = rt2500usb_device_table, - .probe = rt2500usb_probe, - .disconnect = rt2x00usb_disconnect, - .suspend = rt2x00usb_suspend, - .resume = rt2x00usb_resume, - .reset_resume = rt2x00usb_resume, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(rt2500usb_driver); diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/rt2x00/rt2500usb.h deleted file mode 100644 index 78cc035b2d17..000000000000 --- a/drivers/net/wireless/rt2x00/rt2500usb.h +++ /dev/null @@ -1,855 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2500usb - Abstract: Data structures and registers for the rt2500usb module. - Supported chipsets: RT2570. - */ - -#ifndef RT2500USB_H -#define RT2500USB_H - -/* - * RF chip defines. - */ -#define RF2522 0x0000 -#define RF2523 0x0001 -#define RF2524 0x0002 -#define RF2525 0x0003 -#define RF2525E 0x0005 -#define RF5222 0x0010 - -/* - * RT2570 version - */ -#define RT2570_VERSION_B 2 -#define RT2570_VERSION_C 3 -#define RT2570_VERSION_D 4 - -/* - * Signal information. - * Default offset is required for RSSI <-> dBm conversion. - */ -#define DEFAULT_RSSI_OFFSET 120 - -/* - * Register layout information. - */ -#define CSR_REG_BASE 0x0400 -#define CSR_REG_SIZE 0x0100 -#define EEPROM_BASE 0x0000 -#define EEPROM_SIZE 0x006e -#define BBP_BASE 0x0000 -#define BBP_SIZE 0x0060 -#define RF_BASE 0x0004 -#define RF_SIZE 0x0010 - -/* - * Number of TX queues. - */ -#define NUM_TX_QUEUES 2 - -/* - * Control/Status Registers(CSR). - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * MAC_CSR0: ASIC revision number. - */ -#define MAC_CSR0 0x0400 - -/* - * MAC_CSR1: System control. - * SOFT_RESET: Software reset, 1: reset, 0: normal. - * BBP_RESET: Hardware reset, 1: reset, 0, release. - * HOST_READY: Host ready after initialization. - */ -#define MAC_CSR1 0x0402 -#define MAC_CSR1_SOFT_RESET FIELD16(0x00000001) -#define MAC_CSR1_BBP_RESET FIELD16(0x00000002) -#define MAC_CSR1_HOST_READY FIELD16(0x00000004) - -/* - * MAC_CSR2: STA MAC register 0. - */ -#define MAC_CSR2 0x0404 -#define MAC_CSR2_BYTE0 FIELD16(0x00ff) -#define MAC_CSR2_BYTE1 FIELD16(0xff00) - -/* - * MAC_CSR3: STA MAC register 1. - */ -#define MAC_CSR3 0x0406 -#define MAC_CSR3_BYTE2 FIELD16(0x00ff) -#define MAC_CSR3_BYTE3 FIELD16(0xff00) - -/* - * MAC_CSR4: STA MAC register 2. - */ -#define MAC_CSR4 0X0408 -#define MAC_CSR4_BYTE4 FIELD16(0x00ff) -#define MAC_CSR4_BYTE5 FIELD16(0xff00) - -/* - * MAC_CSR5: BSSID register 0. - */ -#define MAC_CSR5 0x040a -#define MAC_CSR5_BYTE0 FIELD16(0x00ff) -#define MAC_CSR5_BYTE1 FIELD16(0xff00) - -/* - * MAC_CSR6: BSSID register 1. - */ -#define MAC_CSR6 0x040c -#define MAC_CSR6_BYTE2 FIELD16(0x00ff) -#define MAC_CSR6_BYTE3 FIELD16(0xff00) - -/* - * MAC_CSR7: BSSID register 2. - */ -#define MAC_CSR7 0x040e -#define MAC_CSR7_BYTE4 FIELD16(0x00ff) -#define MAC_CSR7_BYTE5 FIELD16(0xff00) - -/* - * MAC_CSR8: Max frame length. - */ -#define MAC_CSR8 0x0410 -#define MAC_CSR8_MAX_FRAME_UNIT FIELD16(0x0fff) - -/* - * Misc MAC_CSR registers. - * MAC_CSR9: Timer control. - * MAC_CSR10: Slot time. - * MAC_CSR11: SIFS. - * MAC_CSR12: EIFS. - * MAC_CSR13: Power mode0. - * MAC_CSR14: Power mode1. - * MAC_CSR15: Power saving transition0 - * MAC_CSR16: Power saving transition1 - */ -#define MAC_CSR9 0x0412 -#define MAC_CSR10 0x0414 -#define MAC_CSR11 0x0416 -#define MAC_CSR12 0x0418 -#define MAC_CSR13 0x041a -#define MAC_CSR14 0x041c -#define MAC_CSR15 0x041e -#define MAC_CSR16 0x0420 - -/* - * MAC_CSR17: Manual power control / status register. - * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. - * SET_STATE: Set state. Write 1 to trigger, self cleared. - * BBP_DESIRE_STATE: BBP desired state. - * RF_DESIRE_STATE: RF desired state. - * BBP_CURRENT_STATE: BBP current state. - * RF_CURRENT_STATE: RF current state. - * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. - */ -#define MAC_CSR17 0x0422 -#define MAC_CSR17_SET_STATE FIELD16(0x0001) -#define MAC_CSR17_BBP_DESIRE_STATE FIELD16(0x0006) -#define MAC_CSR17_RF_DESIRE_STATE FIELD16(0x0018) -#define MAC_CSR17_BBP_CURR_STATE FIELD16(0x0060) -#define MAC_CSR17_RF_CURR_STATE FIELD16(0x0180) -#define MAC_CSR17_PUT_TO_SLEEP FIELD16(0x0200) - -/* - * MAC_CSR18: Wakeup timer register. - * DELAY_AFTER_BEACON: Delay after Tbcn expired in units of 1/16 TU. - * BEACONS_BEFORE_WAKEUP: Number of beacon before wakeup. - * AUTO_WAKE: Enable auto wakeup / sleep mechanism. - */ -#define MAC_CSR18 0x0424 -#define MAC_CSR18_DELAY_AFTER_BEACON FIELD16(0x00ff) -#define MAC_CSR18_BEACONS_BEFORE_WAKEUP FIELD16(0x7f00) -#define MAC_CSR18_AUTO_WAKE FIELD16(0x8000) - -/* - * MAC_CSR19: GPIO control register. - * MAC_CSR19_VALx: GPIO value - * MAC_CSR19_DIRx: GPIO direction: 0 = input; 1 = output - */ -#define MAC_CSR19 0x0426 -#define MAC_CSR19_VAL0 FIELD16(0x0001) -#define MAC_CSR19_VAL1 FIELD16(0x0002) -#define MAC_CSR19_VAL2 FIELD16(0x0004) -#define MAC_CSR19_VAL3 FIELD16(0x0008) -#define MAC_CSR19_VAL4 FIELD16(0x0010) -#define MAC_CSR19_VAL5 FIELD16(0x0020) -#define MAC_CSR19_VAL6 FIELD16(0x0040) -#define MAC_CSR19_VAL7 FIELD16(0x0080) -#define MAC_CSR19_DIR0 FIELD16(0x0100) -#define MAC_CSR19_DIR1 FIELD16(0x0200) -#define MAC_CSR19_DIR2 FIELD16(0x0400) -#define MAC_CSR19_DIR3 FIELD16(0x0800) -#define MAC_CSR19_DIR4 FIELD16(0x1000) -#define MAC_CSR19_DIR5 FIELD16(0x2000) -#define MAC_CSR19_DIR6 FIELD16(0x4000) -#define MAC_CSR19_DIR7 FIELD16(0x8000) - -/* - * MAC_CSR20: LED control register. - * ACTIVITY: 0: idle, 1: active. - * LINK: 0: linkoff, 1: linkup. - * ACTIVITY_POLARITY: 0: active low, 1: active high. - */ -#define MAC_CSR20 0x0428 -#define MAC_CSR20_ACTIVITY FIELD16(0x0001) -#define MAC_CSR20_LINK FIELD16(0x0002) -#define MAC_CSR20_ACTIVITY_POLARITY FIELD16(0x0004) - -/* - * MAC_CSR21: LED control register. - * ON_PERIOD: On period, default 70ms. - * OFF_PERIOD: Off period, default 30ms. - */ -#define MAC_CSR21 0x042a -#define MAC_CSR21_ON_PERIOD FIELD16(0x00ff) -#define MAC_CSR21_OFF_PERIOD FIELD16(0xff00) - -/* - * MAC_CSR22: Collision window control register. - */ -#define MAC_CSR22 0x042c - -/* - * Transmit related CSRs. - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * TXRX_CSR0: Security control register. - */ -#define TXRX_CSR0 0x0440 -#define TXRX_CSR0_ALGORITHM FIELD16(0x0007) -#define TXRX_CSR0_IV_OFFSET FIELD16(0x01f8) -#define TXRX_CSR0_KEY_ID FIELD16(0x1e00) - -/* - * TXRX_CSR1: TX configuration. - * ACK_TIMEOUT: ACK Timeout in unit of 1-us. - * TSF_OFFSET: TSF offset in MAC header. - * AUTO_SEQUENCE: Let ASIC control frame sequence number. - */ -#define TXRX_CSR1 0x0442 -#define TXRX_CSR1_ACK_TIMEOUT FIELD16(0x00ff) -#define TXRX_CSR1_TSF_OFFSET FIELD16(0x7f00) -#define TXRX_CSR1_AUTO_SEQUENCE FIELD16(0x8000) - -/* - * TXRX_CSR2: RX control. - * DISABLE_RX: Disable rx engine. - * DROP_CRC: Drop crc error. - * DROP_PHYSICAL: Drop physical error. - * DROP_CONTROL: Drop control frame. - * DROP_NOT_TO_ME: Drop not to me unicast frame. - * DROP_TODS: Drop frame tods bit is true. - * DROP_VERSION_ERROR: Drop version error frame. - * DROP_MCAST: Drop multicast frames. - * DROP_BCAST: Drop broadcast frames. - */ -#define TXRX_CSR2 0x0444 -#define TXRX_CSR2_DISABLE_RX FIELD16(0x0001) -#define TXRX_CSR2_DROP_CRC FIELD16(0x0002) -#define TXRX_CSR2_DROP_PHYSICAL FIELD16(0x0004) -#define TXRX_CSR2_DROP_CONTROL FIELD16(0x0008) -#define TXRX_CSR2_DROP_NOT_TO_ME FIELD16(0x0010) -#define TXRX_CSR2_DROP_TODS FIELD16(0x0020) -#define TXRX_CSR2_DROP_VERSION_ERROR FIELD16(0x0040) -#define TXRX_CSR2_DROP_MULTICAST FIELD16(0x0200) -#define TXRX_CSR2_DROP_BROADCAST FIELD16(0x0400) - -/* - * RX BBP ID registers - * TXRX_CSR3: CCK RX BBP ID. - * TXRX_CSR4: OFDM RX BBP ID. - */ -#define TXRX_CSR3 0x0446 -#define TXRX_CSR4 0x0448 - -/* - * TXRX_CSR5: CCK TX BBP ID0. - */ -#define TXRX_CSR5 0x044a -#define TXRX_CSR5_BBP_ID0 FIELD16(0x007f) -#define TXRX_CSR5_BBP_ID0_VALID FIELD16(0x0080) -#define TXRX_CSR5_BBP_ID1 FIELD16(0x7f00) -#define TXRX_CSR5_BBP_ID1_VALID FIELD16(0x8000) - -/* - * TXRX_CSR6: CCK TX BBP ID1. - */ -#define TXRX_CSR6 0x044c -#define TXRX_CSR6_BBP_ID0 FIELD16(0x007f) -#define TXRX_CSR6_BBP_ID0_VALID FIELD16(0x0080) -#define TXRX_CSR6_BBP_ID1 FIELD16(0x7f00) -#define TXRX_CSR6_BBP_ID1_VALID FIELD16(0x8000) - -/* - * TXRX_CSR7: OFDM TX BBP ID0. - */ -#define TXRX_CSR7 0x044e -#define TXRX_CSR7_BBP_ID0 FIELD16(0x007f) -#define TXRX_CSR7_BBP_ID0_VALID FIELD16(0x0080) -#define TXRX_CSR7_BBP_ID1 FIELD16(0x7f00) -#define TXRX_CSR7_BBP_ID1_VALID FIELD16(0x8000) - -/* - * TXRX_CSR8: OFDM TX BBP ID1. - */ -#define TXRX_CSR8 0x0450 -#define TXRX_CSR8_BBP_ID0 FIELD16(0x007f) -#define TXRX_CSR8_BBP_ID0_VALID FIELD16(0x0080) -#define TXRX_CSR8_BBP_ID1 FIELD16(0x7f00) -#define TXRX_CSR8_BBP_ID1_VALID FIELD16(0x8000) - -/* - * TXRX_CSR9: TX ACK time-out. - */ -#define TXRX_CSR9 0x0452 - -/* - * TXRX_CSR10: Auto responder control. - */ -#define TXRX_CSR10 0x0454 -#define TXRX_CSR10_AUTORESPOND_PREAMBLE FIELD16(0x0004) - -/* - * TXRX_CSR11: Auto responder basic rate. - */ -#define TXRX_CSR11 0x0456 - -/* - * ACK/CTS time registers. - */ -#define TXRX_CSR12 0x0458 -#define TXRX_CSR13 0x045a -#define TXRX_CSR14 0x045c -#define TXRX_CSR15 0x045e -#define TXRX_CSR16 0x0460 -#define TXRX_CSR17 0x0462 - -/* - * TXRX_CSR18: Synchronization control register. - */ -#define TXRX_CSR18 0x0464 -#define TXRX_CSR18_OFFSET FIELD16(0x000f) -#define TXRX_CSR18_INTERVAL FIELD16(0xfff0) - -/* - * TXRX_CSR19: Synchronization control register. - * TSF_COUNT: Enable TSF auto counting. - * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. - * TBCN: Enable Tbcn with reload value. - * BEACON_GEN: Enable beacon generator. - */ -#define TXRX_CSR19 0x0466 -#define TXRX_CSR19_TSF_COUNT FIELD16(0x0001) -#define TXRX_CSR19_TSF_SYNC FIELD16(0x0006) -#define TXRX_CSR19_TBCN FIELD16(0x0008) -#define TXRX_CSR19_BEACON_GEN FIELD16(0x0010) - -/* - * TXRX_CSR20: Tx BEACON offset time control register. - * OFFSET: In units of usec. - * BCN_EXPECT_WINDOW: Default: 2^CWmin - */ -#define TXRX_CSR20 0x0468 -#define TXRX_CSR20_OFFSET FIELD16(0x1fff) -#define TXRX_CSR20_BCN_EXPECT_WINDOW FIELD16(0xe000) - -/* - * TXRX_CSR21 - */ -#define TXRX_CSR21 0x046a - -/* - * Encryption related CSRs. - * - */ - -/* - * SEC_CSR0: Shared key 0, word 0 - * SEC_CSR1: Shared key 0, word 1 - * SEC_CSR2: Shared key 0, word 2 - * SEC_CSR3: Shared key 0, word 3 - * SEC_CSR4: Shared key 0, word 4 - * SEC_CSR5: Shared key 0, word 5 - * SEC_CSR6: Shared key 0, word 6 - * SEC_CSR7: Shared key 0, word 7 - */ -#define SEC_CSR0 0x0480 -#define SEC_CSR1 0x0482 -#define SEC_CSR2 0x0484 -#define SEC_CSR3 0x0486 -#define SEC_CSR4 0x0488 -#define SEC_CSR5 0x048a -#define SEC_CSR6 0x048c -#define SEC_CSR7 0x048e - -/* - * SEC_CSR8: Shared key 1, word 0 - * SEC_CSR9: Shared key 1, word 1 - * SEC_CSR10: Shared key 1, word 2 - * SEC_CSR11: Shared key 1, word 3 - * SEC_CSR12: Shared key 1, word 4 - * SEC_CSR13: Shared key 1, word 5 - * SEC_CSR14: Shared key 1, word 6 - * SEC_CSR15: Shared key 1, word 7 - */ -#define SEC_CSR8 0x0490 -#define SEC_CSR9 0x0492 -#define SEC_CSR10 0x0494 -#define SEC_CSR11 0x0496 -#define SEC_CSR12 0x0498 -#define SEC_CSR13 0x049a -#define SEC_CSR14 0x049c -#define SEC_CSR15 0x049e - -/* - * SEC_CSR16: Shared key 2, word 0 - * SEC_CSR17: Shared key 2, word 1 - * SEC_CSR18: Shared key 2, word 2 - * SEC_CSR19: Shared key 2, word 3 - * SEC_CSR20: Shared key 2, word 4 - * SEC_CSR21: Shared key 2, word 5 - * SEC_CSR22: Shared key 2, word 6 - * SEC_CSR23: Shared key 2, word 7 - */ -#define SEC_CSR16 0x04a0 -#define SEC_CSR17 0x04a2 -#define SEC_CSR18 0X04A4 -#define SEC_CSR19 0x04a6 -#define SEC_CSR20 0x04a8 -#define SEC_CSR21 0x04aa -#define SEC_CSR22 0x04ac -#define SEC_CSR23 0x04ae - -/* - * SEC_CSR24: Shared key 3, word 0 - * SEC_CSR25: Shared key 3, word 1 - * SEC_CSR26: Shared key 3, word 2 - * SEC_CSR27: Shared key 3, word 3 - * SEC_CSR28: Shared key 3, word 4 - * SEC_CSR29: Shared key 3, word 5 - * SEC_CSR30: Shared key 3, word 6 - * SEC_CSR31: Shared key 3, word 7 - */ -#define SEC_CSR24 0x04b0 -#define SEC_CSR25 0x04b2 -#define SEC_CSR26 0x04b4 -#define SEC_CSR27 0x04b6 -#define SEC_CSR28 0x04b8 -#define SEC_CSR29 0x04ba -#define SEC_CSR30 0x04bc -#define SEC_CSR31 0x04be - -#define KEY_ENTRY(__idx) \ - ( SEC_CSR0 + ((__idx) * 16) ) - -/* - * PHY control registers. - */ - -/* - * PHY_CSR0: RF switching timing control. - */ -#define PHY_CSR0 0x04c0 - -/* - * PHY_CSR1: TX PA configuration. - */ -#define PHY_CSR1 0x04c2 - -/* - * MAC configuration registers. - */ - -/* - * PHY_CSR2: TX MAC configuration. - * NOTE: Both register fields are complete dummy, - * documentation and legacy drivers are unclear un - * what this register means or what fields exists. - */ -#define PHY_CSR2 0x04c4 -#define PHY_CSR2_LNA FIELD16(0x0002) -#define PHY_CSR2_LNA_MODE FIELD16(0x3000) - -/* - * PHY_CSR3: RX MAC configuration. - */ -#define PHY_CSR3 0x04c6 - -/* - * PHY_CSR4: Interface configuration. - */ -#define PHY_CSR4 0x04c8 -#define PHY_CSR4_LOW_RF_LE FIELD16(0x0001) - -/* - * BBP pre-TX registers. - * PHY_CSR5: BBP pre-TX CCK. - */ -#define PHY_CSR5 0x04ca -#define PHY_CSR5_CCK FIELD16(0x0003) -#define PHY_CSR5_CCK_FLIP FIELD16(0x0004) - -/* - * BBP pre-TX registers. - * PHY_CSR6: BBP pre-TX OFDM. - */ -#define PHY_CSR6 0x04cc -#define PHY_CSR6_OFDM FIELD16(0x0003) -#define PHY_CSR6_OFDM_FLIP FIELD16(0x0004) - -/* - * PHY_CSR7: BBP access register 0. - * BBP_DATA: BBP data. - * BBP_REG_ID: BBP register ID. - * BBP_READ_CONTROL: 0: write, 1: read. - */ -#define PHY_CSR7 0x04ce -#define PHY_CSR7_DATA FIELD16(0x00ff) -#define PHY_CSR7_REG_ID FIELD16(0x7f00) -#define PHY_CSR7_READ_CONTROL FIELD16(0x8000) - -/* - * PHY_CSR8: BBP access register 1. - * BBP_BUSY: ASIC is busy execute BBP programming. - */ -#define PHY_CSR8 0x04d0 -#define PHY_CSR8_BUSY FIELD16(0x0001) - -/* - * PHY_CSR9: RF access register. - * RF_VALUE: Register value + id to program into rf/if. - */ -#define PHY_CSR9 0x04d2 -#define PHY_CSR9_RF_VALUE FIELD16(0xffff) - -/* - * PHY_CSR10: RF access register. - * RF_VALUE: Register value + id to program into rf/if. - * RF_NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). - * RF_IF_SELECT: Chip to program: 0: rf, 1: if. - * RF_PLL_LD: Rf pll_ld status. - * RF_BUSY: 1: asic is busy execute rf programming. - */ -#define PHY_CSR10 0x04d4 -#define PHY_CSR10_RF_VALUE FIELD16(0x00ff) -#define PHY_CSR10_RF_NUMBER_OF_BITS FIELD16(0x1f00) -#define PHY_CSR10_RF_IF_SELECT FIELD16(0x2000) -#define PHY_CSR10_RF_PLL_LD FIELD16(0x4000) -#define PHY_CSR10_RF_BUSY FIELD16(0x8000) - -/* - * STA_CSR0: FCS error count. - * FCS_ERROR: FCS error count, cleared when read. - */ -#define STA_CSR0 0x04e0 -#define STA_CSR0_FCS_ERROR FIELD16(0xffff) - -/* - * STA_CSR1: PLCP error count. - */ -#define STA_CSR1 0x04e2 - -/* - * STA_CSR2: LONG error count. - */ -#define STA_CSR2 0x04e4 - -/* - * STA_CSR3: CCA false alarm. - * FALSE_CCA_ERROR: False CCA error count, cleared when read. - */ -#define STA_CSR3 0x04e6 -#define STA_CSR3_FALSE_CCA_ERROR FIELD16(0xffff) - -/* - * STA_CSR4: RX FIFO overflow. - */ -#define STA_CSR4 0x04e8 - -/* - * STA_CSR5: Beacon sent counter. - */ -#define STA_CSR5 0x04ea - -/* - * Statistics registers - */ -#define STA_CSR6 0x04ec -#define STA_CSR7 0x04ee -#define STA_CSR8 0x04f0 -#define STA_CSR9 0x04f2 -#define STA_CSR10 0x04f4 - -/* - * BBP registers. - * The wordsize of the BBP is 8 bits. - */ - -/* - * R2: TX antenna control - */ -#define BBP_R2_TX_ANTENNA FIELD8(0x03) -#define BBP_R2_TX_IQ_FLIP FIELD8(0x04) - -/* - * R14: RX antenna control - */ -#define BBP_R14_RX_ANTENNA FIELD8(0x03) -#define BBP_R14_RX_IQ_FLIP FIELD8(0x04) - -/* - * RF registers. - */ - -/* - * RF 1 - */ -#define RF1_TUNER FIELD32(0x00020000) - -/* - * RF 3 - */ -#define RF3_TUNER FIELD32(0x00000100) -#define RF3_TXPOWER FIELD32(0x00003e00) - -/* - * EEPROM contents. - */ - -/* - * HW MAC address. - */ -#define EEPROM_MAC_ADDR_0 0x0002 -#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) -#define EEPROM_MAC_ADDR1 0x0003 -#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_2 0x0004 -#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) - -/* - * EEPROM antenna. - * ANTENNA_NUM: Number of antenna's. - * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. - * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. - * LED_MODE: 0: default, 1: TX/RX activity, 2: Single (ignore link), 3: rsvd. - * DYN_TXAGC: Dynamic TX AGC control. - * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. - * RF_TYPE: Rf_type of this adapter. - */ -#define EEPROM_ANTENNA 0x000b -#define EEPROM_ANTENNA_NUM FIELD16(0x0003) -#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) -#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) -#define EEPROM_ANTENNA_LED_MODE FIELD16(0x01c0) -#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) -#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) -#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) - -/* - * EEPROM NIC config. - * CARDBUS_ACCEL: 0: enable, 1: disable. - * DYN_BBP_TUNE: 0: enable, 1: disable. - * CCK_TX_POWER: CCK TX power compensation. - */ -#define EEPROM_NIC 0x000c -#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0001) -#define EEPROM_NIC_DYN_BBP_TUNE FIELD16(0x0002) -#define EEPROM_NIC_CCK_TX_POWER FIELD16(0x000c) - -/* - * EEPROM geography. - * GEO: Default geography setting for device. - */ -#define EEPROM_GEOGRAPHY 0x000d -#define EEPROM_GEOGRAPHY_GEO FIELD16(0x0f00) - -/* - * EEPROM BBP. - */ -#define EEPROM_BBP_START 0x000e -#define EEPROM_BBP_SIZE 16 -#define EEPROM_BBP_VALUE FIELD16(0x00ff) -#define EEPROM_BBP_REG_ID FIELD16(0xff00) - -/* - * EEPROM TXPOWER - */ -#define EEPROM_TXPOWER_START 0x001e -#define EEPROM_TXPOWER_SIZE 7 -#define EEPROM_TXPOWER_1 FIELD16(0x00ff) -#define EEPROM_TXPOWER_2 FIELD16(0xff00) - -/* - * EEPROM Tuning threshold - */ -#define EEPROM_BBPTUNE 0x0030 -#define EEPROM_BBPTUNE_THRESHOLD FIELD16(0x00ff) - -/* - * EEPROM BBP R24 Tuning. - */ -#define EEPROM_BBPTUNE_R24 0x0031 -#define EEPROM_BBPTUNE_R24_LOW FIELD16(0x00ff) -#define EEPROM_BBPTUNE_R24_HIGH FIELD16(0xff00) - -/* - * EEPROM BBP R25 Tuning. - */ -#define EEPROM_BBPTUNE_R25 0x0032 -#define EEPROM_BBPTUNE_R25_LOW FIELD16(0x00ff) -#define EEPROM_BBPTUNE_R25_HIGH FIELD16(0xff00) - -/* - * EEPROM BBP R24 Tuning. - */ -#define EEPROM_BBPTUNE_R61 0x0033 -#define EEPROM_BBPTUNE_R61_LOW FIELD16(0x00ff) -#define EEPROM_BBPTUNE_R61_HIGH FIELD16(0xff00) - -/* - * EEPROM BBP VGC Tuning. - */ -#define EEPROM_BBPTUNE_VGC 0x0034 -#define EEPROM_BBPTUNE_VGCUPPER FIELD16(0x00ff) -#define EEPROM_BBPTUNE_VGCLOWER FIELD16(0xff00) - -/* - * EEPROM BBP R17 Tuning. - */ -#define EEPROM_BBPTUNE_R17 0x0035 -#define EEPROM_BBPTUNE_R17_LOW FIELD16(0x00ff) -#define EEPROM_BBPTUNE_R17_HIGH FIELD16(0xff00) - -/* - * RSSI <-> dBm offset calibration - */ -#define EEPROM_CALIBRATE_OFFSET 0x0036 -#define EEPROM_CALIBRATE_OFFSET_RSSI FIELD16(0x00ff) - -/* - * DMA descriptor defines. - */ -#define TXD_DESC_SIZE ( 5 * sizeof(__le32) ) -#define RXD_DESC_SIZE ( 4 * sizeof(__le32) ) - -/* - * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. - */ - -/* - * Word0 - */ -#define TXD_W0_PACKET_ID FIELD32(0x0000000f) -#define TXD_W0_RETRY_LIMIT FIELD32(0x000000f0) -#define TXD_W0_MORE_FRAG FIELD32(0x00000100) -#define TXD_W0_ACK FIELD32(0x00000200) -#define TXD_W0_TIMESTAMP FIELD32(0x00000400) -#define TXD_W0_OFDM FIELD32(0x00000800) -#define TXD_W0_NEW_SEQ FIELD32(0x00001000) -#define TXD_W0_IFS FIELD32(0x00006000) -#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) -#define TXD_W0_CIPHER FIELD32(0x20000000) -#define TXD_W0_KEY_ID FIELD32(0xc0000000) - -/* - * Word1 - */ -#define TXD_W1_IV_OFFSET FIELD32(0x0000003f) -#define TXD_W1_AIFS FIELD32(0x000000c0) -#define TXD_W1_CWMIN FIELD32(0x00000f00) -#define TXD_W1_CWMAX FIELD32(0x0000f000) - -/* - * Word2: PLCP information - */ -#define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) -#define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) -#define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) -#define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) - -/* - * Word3 - */ -#define TXD_W3_IV FIELD32(0xffffffff) - -/* - * Word4 - */ -#define TXD_W4_EIV FIELD32(0xffffffff) - -/* - * RX descriptor format for RX Ring. - */ - -/* - * Word0 - */ -#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) -#define RXD_W0_MULTICAST FIELD32(0x00000004) -#define RXD_W0_BROADCAST FIELD32(0x00000008) -#define RXD_W0_MY_BSS FIELD32(0x00000010) -#define RXD_W0_CRC_ERROR FIELD32(0x00000020) -#define RXD_W0_OFDM FIELD32(0x00000040) -#define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) -#define RXD_W0_CIPHER FIELD32(0x00000100) -#define RXD_W0_CIPHER_ERROR FIELD32(0x00000200) -#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) - -/* - * Word1 - */ -#define RXD_W1_RSSI FIELD32(0x000000ff) -#define RXD_W1_SIGNAL FIELD32(0x0000ff00) - -/* - * Word2 - */ -#define RXD_W2_IV FIELD32(0xffffffff) - -/* - * Word3 - */ -#define RXD_W3_EIV FIELD32(0xffffffff) - -/* - * Macros for converting txpower from EEPROM to mac80211 value - * and from mac80211 value to register value. - */ -#define MIN_TXPOWER 0 -#define MAX_TXPOWER 31 -#define DEFAULT_TXPOWER 24 - -#define TXPOWER_FROM_DEV(__txpower) \ - (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) - -#define TXPOWER_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) - -#endif /* RT2500USB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h deleted file mode 100644 index 95c1d7c0a2f3..000000000000 --- a/drivers/net/wireless/rt2x00/rt2800.h +++ /dev/null @@ -1,2986 +0,0 @@ -/* - Copyright (C) 2004 - 2010 Ivo van Doorn - Copyright (C) 2010 Willow Garage - Copyright (C) 2009 Alban Browaeys - Copyright (C) 2009 Felix Fietkau - Copyright (C) 2009 Luis Correia - Copyright (C) 2009 Mattias Nissler - Copyright (C) 2009 Mark Asselstine - Copyright (C) 2009 Xose Vazquez Perez - Copyright (C) 2009 Bart Zolnierkiewicz - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2800 - Abstract: Data structures and registers for the rt2800 modules. - Supported chipsets: RT2800E, RT2800ED & RT2800U. - */ - -#ifndef RT2800_H -#define RT2800_H - -/* - * RF chip defines. - * - * RF2820 2.4G 2T3R - * RF2850 2.4G/5G 2T3R - * RF2720 2.4G 1T2R - * RF2750 2.4G/5G 1T2R - * RF3020 2.4G 1T1R - * RF2020 2.4G B/G - * RF3021 2.4G 1T2R - * RF3022 2.4G 2T2R - * RF3052 2.4G/5G 2T2R - * RF2853 2.4G/5G 3T3R - * RF3320 2.4G 1T1R(RT3350/RT3370/RT3390) - * RF3322 2.4G 2T2R(RT3352/RT3371/RT3372/RT3391/RT3392) - * RF3053 2.4G/5G 3T3R(RT3883/RT3563/RT3573/RT3593/RT3662) - * RF5592 2.4G/5G 2T2R - * RF3070 2.4G 1T1R - * RF5360 2.4G 1T1R - * RF5362 2.4G 1T1R - * RF5370 2.4G 1T1R - * RF5390 2.4G 1T1R - */ -#define RF2820 0x0001 -#define RF2850 0x0002 -#define RF2720 0x0003 -#define RF2750 0x0004 -#define RF3020 0x0005 -#define RF2020 0x0006 -#define RF3021 0x0007 -#define RF3022 0x0008 -#define RF3052 0x0009 -#define RF2853 0x000a -#define RF3320 0x000b -#define RF3322 0x000c -#define RF3053 0x000d -#define RF5592 0x000f -#define RF3070 0x3070 -#define RF3290 0x3290 -#define RF5360 0x5360 -#define RF5362 0x5362 -#define RF5370 0x5370 -#define RF5372 0x5372 -#define RF5390 0x5390 -#define RF5392 0x5392 - -/* - * Chipset revisions. - */ -#define REV_RT2860C 0x0100 -#define REV_RT2860D 0x0101 -#define REV_RT2872E 0x0200 -#define REV_RT3070E 0x0200 -#define REV_RT3070F 0x0201 -#define REV_RT3071E 0x0211 -#define REV_RT3090E 0x0211 -#define REV_RT3390E 0x0211 -#define REV_RT3593E 0x0211 -#define REV_RT5390F 0x0502 -#define REV_RT5390R 0x1502 -#define REV_RT5592C 0x0221 - -#define DEFAULT_RSSI_OFFSET 120 - -/* - * Register layout information. - */ -#define CSR_REG_BASE 0x1000 -#define CSR_REG_SIZE 0x0800 -#define EEPROM_BASE 0x0000 -#define EEPROM_SIZE 0x0200 -#define BBP_BASE 0x0000 -#define BBP_SIZE 0x00ff -#define RF_BASE 0x0004 -#define RF_SIZE 0x0010 -#define RFCSR_BASE 0x0000 -#define RFCSR_SIZE 0x0040 - -/* - * Number of TX queues. - */ -#define NUM_TX_QUEUES 4 - -/* - * Registers. - */ - - -/* - * MAC_CSR0_3290: MAC_CSR0 for RT3290 to identity MAC version number. - */ -#define MAC_CSR0_3290 0x0000 - -/* - * E2PROM_CSR: PCI EEPROM control register. - * RELOAD: Write 1 to reload eeprom content. - * TYPE: 0: 93c46, 1:93c66. - * LOAD_STATUS: 1:loading, 0:done. - */ -#define E2PROM_CSR 0x0004 -#define E2PROM_CSR_DATA_CLOCK FIELD32(0x00000001) -#define E2PROM_CSR_CHIP_SELECT FIELD32(0x00000002) -#define E2PROM_CSR_DATA_IN FIELD32(0x00000004) -#define E2PROM_CSR_DATA_OUT FIELD32(0x00000008) -#define E2PROM_CSR_TYPE FIELD32(0x00000030) -#define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040) -#define E2PROM_CSR_RELOAD FIELD32(0x00000080) - -/* - * CMB_CTRL_CFG - */ -#define CMB_CTRL 0x0020 -#define AUX_OPT_BIT0 FIELD32(0x00000001) -#define AUX_OPT_BIT1 FIELD32(0x00000002) -#define AUX_OPT_BIT2 FIELD32(0x00000004) -#define AUX_OPT_BIT3 FIELD32(0x00000008) -#define AUX_OPT_BIT4 FIELD32(0x00000010) -#define AUX_OPT_BIT5 FIELD32(0x00000020) -#define AUX_OPT_BIT6 FIELD32(0x00000040) -#define AUX_OPT_BIT7 FIELD32(0x00000080) -#define AUX_OPT_BIT8 FIELD32(0x00000100) -#define AUX_OPT_BIT9 FIELD32(0x00000200) -#define AUX_OPT_BIT10 FIELD32(0x00000400) -#define AUX_OPT_BIT11 FIELD32(0x00000800) -#define AUX_OPT_BIT12 FIELD32(0x00001000) -#define AUX_OPT_BIT13 FIELD32(0x00002000) -#define AUX_OPT_BIT14 FIELD32(0x00004000) -#define AUX_OPT_BIT15 FIELD32(0x00008000) -#define LDO25_LEVEL FIELD32(0x00030000) -#define LDO25_LARGEA FIELD32(0x00040000) -#define LDO25_FRC_ON FIELD32(0x00080000) -#define CMB_RSV FIELD32(0x00300000) -#define XTAL_RDY FIELD32(0x00400000) -#define PLL_LD FIELD32(0x00800000) -#define LDO_CORE_LEVEL FIELD32(0x0F000000) -#define LDO_BGSEL FIELD32(0x30000000) -#define LDO3_EN FIELD32(0x40000000) -#define LDO0_EN FIELD32(0x80000000) - -/* - * EFUSE_CSR_3290: RT3290 EEPROM - */ -#define EFUSE_CTRL_3290 0x0024 - -/* - * EFUSE_DATA3 of 3290 - */ -#define EFUSE_DATA3_3290 0x0028 - -/* - * EFUSE_DATA2 of 3290 - */ -#define EFUSE_DATA2_3290 0x002c - -/* - * EFUSE_DATA1 of 3290 - */ -#define EFUSE_DATA1_3290 0x0030 - -/* - * EFUSE_DATA0 of 3290 - */ -#define EFUSE_DATA0_3290 0x0034 - -/* - * OSC_CTRL_CFG - * Ring oscillator configuration - */ -#define OSC_CTRL 0x0038 -#define OSC_REF_CYCLE FIELD32(0x00001fff) -#define OSC_RSV FIELD32(0x0000e000) -#define OSC_CAL_CNT FIELD32(0x0fff0000) -#define OSC_CAL_ACK FIELD32(0x10000000) -#define OSC_CLK_32K_VLD FIELD32(0x20000000) -#define OSC_CAL_REQ FIELD32(0x40000000) -#define OSC_ROSC_EN FIELD32(0x80000000) - -/* - * COEX_CFG_0 - */ -#define COEX_CFG0 0x0040 -#define COEX_CFG_ANT FIELD32(0xff000000) -/* - * COEX_CFG_1 - */ -#define COEX_CFG1 0x0044 - -/* - * COEX_CFG_2 - */ -#define COEX_CFG2 0x0048 -#define BT_COEX_CFG1 FIELD32(0xff000000) -#define BT_COEX_CFG0 FIELD32(0x00ff0000) -#define WL_COEX_CFG1 FIELD32(0x0000ff00) -#define WL_COEX_CFG0 FIELD32(0x000000ff) -/* - * PLL_CTRL_CFG - * PLL configuration register - */ -#define PLL_CTRL 0x0050 -#define PLL_RESERVED_INPUT1 FIELD32(0x000000ff) -#define PLL_RESERVED_INPUT2 FIELD32(0x0000ff00) -#define PLL_CONTROL FIELD32(0x00070000) -#define PLL_LPF_R1 FIELD32(0x00080000) -#define PLL_LPF_C1_CTRL FIELD32(0x00300000) -#define PLL_LPF_C2_CTRL FIELD32(0x00c00000) -#define PLL_CP_CURRENT_CTRL FIELD32(0x03000000) -#define PLL_PFD_DELAY_CTRL FIELD32(0x0c000000) -#define PLL_LOCK_CTRL FIELD32(0x70000000) -#define PLL_VBGBK_EN FIELD32(0x80000000) - - -/* - * WLAN_CTRL_CFG - * RT3290 wlan configuration - */ -#define WLAN_FUN_CTRL 0x0080 -#define WLAN_EN FIELD32(0x00000001) -#define WLAN_CLK_EN FIELD32(0x00000002) -#define WLAN_RSV1 FIELD32(0x00000004) -#define WLAN_RESET FIELD32(0x00000008) -#define PCIE_APP0_CLK_REQ FIELD32(0x00000010) -#define FRC_WL_ANT_SET FIELD32(0x00000020) -#define INV_TR_SW0 FIELD32(0x00000040) -#define WLAN_GPIO_IN_BIT0 FIELD32(0x00000100) -#define WLAN_GPIO_IN_BIT1 FIELD32(0x00000200) -#define WLAN_GPIO_IN_BIT2 FIELD32(0x00000400) -#define WLAN_GPIO_IN_BIT3 FIELD32(0x00000800) -#define WLAN_GPIO_IN_BIT4 FIELD32(0x00001000) -#define WLAN_GPIO_IN_BIT5 FIELD32(0x00002000) -#define WLAN_GPIO_IN_BIT6 FIELD32(0x00004000) -#define WLAN_GPIO_IN_BIT7 FIELD32(0x00008000) -#define WLAN_GPIO_IN_BIT_ALL FIELD32(0x0000ff00) -#define WLAN_GPIO_OUT_BIT0 FIELD32(0x00010000) -#define WLAN_GPIO_OUT_BIT1 FIELD32(0x00020000) -#define WLAN_GPIO_OUT_BIT2 FIELD32(0x00040000) -#define WLAN_GPIO_OUT_BIT3 FIELD32(0x00050000) -#define WLAN_GPIO_OUT_BIT4 FIELD32(0x00100000) -#define WLAN_GPIO_OUT_BIT5 FIELD32(0x00200000) -#define WLAN_GPIO_OUT_BIT6 FIELD32(0x00400000) -#define WLAN_GPIO_OUT_BIT7 FIELD32(0x00800000) -#define WLAN_GPIO_OUT_BIT_ALL FIELD32(0x00ff0000) -#define WLAN_GPIO_OUT_OE_BIT0 FIELD32(0x01000000) -#define WLAN_GPIO_OUT_OE_BIT1 FIELD32(0x02000000) -#define WLAN_GPIO_OUT_OE_BIT2 FIELD32(0x04000000) -#define WLAN_GPIO_OUT_OE_BIT3 FIELD32(0x08000000) -#define WLAN_GPIO_OUT_OE_BIT4 FIELD32(0x10000000) -#define WLAN_GPIO_OUT_OE_BIT5 FIELD32(0x20000000) -#define WLAN_GPIO_OUT_OE_BIT6 FIELD32(0x40000000) -#define WLAN_GPIO_OUT_OE_BIT7 FIELD32(0x80000000) -#define WLAN_GPIO_OUT_OE_BIT_ALL FIELD32(0xff000000) - -/* - * AUX_CTRL: Aux/PCI-E related configuration - */ -#define AUX_CTRL 0x10c -#define AUX_CTRL_WAKE_PCIE_EN FIELD32(0x00000002) -#define AUX_CTRL_FORCE_PCIE_CLK FIELD32(0x00000400) - -/* - * OPT_14: Unknown register used by rt3xxx devices. - */ -#define OPT_14_CSR 0x0114 -#define OPT_14_CSR_BIT0 FIELD32(0x00000001) - -/* - * INT_SOURCE_CSR: Interrupt source register. - * Write one to clear corresponding bit. - * TX_FIFO_STATUS: FIFO Statistics is full, sw should read TX_STA_FIFO - */ -#define INT_SOURCE_CSR 0x0200 -#define INT_SOURCE_CSR_RXDELAYINT FIELD32(0x00000001) -#define INT_SOURCE_CSR_TXDELAYINT FIELD32(0x00000002) -#define INT_SOURCE_CSR_RX_DONE FIELD32(0x00000004) -#define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00000008) -#define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00000010) -#define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00000020) -#define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00000040) -#define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00000080) -#define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00000100) -#define INT_SOURCE_CSR_MCU_COMMAND FIELD32(0x00000200) -#define INT_SOURCE_CSR_RXTX_COHERENT FIELD32(0x00000400) -#define INT_SOURCE_CSR_TBTT FIELD32(0x00000800) -#define INT_SOURCE_CSR_PRE_TBTT FIELD32(0x00001000) -#define INT_SOURCE_CSR_TX_FIFO_STATUS FIELD32(0x00002000) -#define INT_SOURCE_CSR_AUTO_WAKEUP FIELD32(0x00004000) -#define INT_SOURCE_CSR_GPTIMER FIELD32(0x00008000) -#define INT_SOURCE_CSR_RX_COHERENT FIELD32(0x00010000) -#define INT_SOURCE_CSR_TX_COHERENT FIELD32(0x00020000) - -/* - * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF. - */ -#define INT_MASK_CSR 0x0204 -#define INT_MASK_CSR_RXDELAYINT FIELD32(0x00000001) -#define INT_MASK_CSR_TXDELAYINT FIELD32(0x00000002) -#define INT_MASK_CSR_RX_DONE FIELD32(0x00000004) -#define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00000008) -#define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00000010) -#define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00000020) -#define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00000040) -#define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00000080) -#define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00000100) -#define INT_MASK_CSR_MCU_COMMAND FIELD32(0x00000200) -#define INT_MASK_CSR_RXTX_COHERENT FIELD32(0x00000400) -#define INT_MASK_CSR_TBTT FIELD32(0x00000800) -#define INT_MASK_CSR_PRE_TBTT FIELD32(0x00001000) -#define INT_MASK_CSR_TX_FIFO_STATUS FIELD32(0x00002000) -#define INT_MASK_CSR_AUTO_WAKEUP FIELD32(0x00004000) -#define INT_MASK_CSR_GPTIMER FIELD32(0x00008000) -#define INT_MASK_CSR_RX_COHERENT FIELD32(0x00010000) -#define INT_MASK_CSR_TX_COHERENT FIELD32(0x00020000) - -/* - * WPDMA_GLO_CFG - */ -#define WPDMA_GLO_CFG 0x0208 -#define WPDMA_GLO_CFG_ENABLE_TX_DMA FIELD32(0x00000001) -#define WPDMA_GLO_CFG_TX_DMA_BUSY FIELD32(0x00000002) -#define WPDMA_GLO_CFG_ENABLE_RX_DMA FIELD32(0x00000004) -#define WPDMA_GLO_CFG_RX_DMA_BUSY FIELD32(0x00000008) -#define WPDMA_GLO_CFG_WP_DMA_BURST_SIZE FIELD32(0x00000030) -#define WPDMA_GLO_CFG_TX_WRITEBACK_DONE FIELD32(0x00000040) -#define WPDMA_GLO_CFG_BIG_ENDIAN FIELD32(0x00000080) -#define WPDMA_GLO_CFG_RX_HDR_SCATTER FIELD32(0x0000ff00) -#define WPDMA_GLO_CFG_HDR_SEG_LEN FIELD32(0xffff0000) - -/* - * WPDMA_RST_IDX - */ -#define WPDMA_RST_IDX 0x020c -#define WPDMA_RST_IDX_DTX_IDX0 FIELD32(0x00000001) -#define WPDMA_RST_IDX_DTX_IDX1 FIELD32(0x00000002) -#define WPDMA_RST_IDX_DTX_IDX2 FIELD32(0x00000004) -#define WPDMA_RST_IDX_DTX_IDX3 FIELD32(0x00000008) -#define WPDMA_RST_IDX_DTX_IDX4 FIELD32(0x00000010) -#define WPDMA_RST_IDX_DTX_IDX5 FIELD32(0x00000020) -#define WPDMA_RST_IDX_DRX_IDX0 FIELD32(0x00010000) - -/* - * DELAY_INT_CFG - */ -#define DELAY_INT_CFG 0x0210 -#define DELAY_INT_CFG_RXMAX_PTIME FIELD32(0x000000ff) -#define DELAY_INT_CFG_RXMAX_PINT FIELD32(0x00007f00) -#define DELAY_INT_CFG_RXDLY_INT_EN FIELD32(0x00008000) -#define DELAY_INT_CFG_TXMAX_PTIME FIELD32(0x00ff0000) -#define DELAY_INT_CFG_TXMAX_PINT FIELD32(0x7f000000) -#define DELAY_INT_CFG_TXDLY_INT_EN FIELD32(0x80000000) - -/* - * WMM_AIFSN_CFG: Aifsn for each EDCA AC - * AIFSN0: AC_VO - * AIFSN1: AC_VI - * AIFSN2: AC_BE - * AIFSN3: AC_BK - */ -#define WMM_AIFSN_CFG 0x0214 -#define WMM_AIFSN_CFG_AIFSN0 FIELD32(0x0000000f) -#define WMM_AIFSN_CFG_AIFSN1 FIELD32(0x000000f0) -#define WMM_AIFSN_CFG_AIFSN2 FIELD32(0x00000f00) -#define WMM_AIFSN_CFG_AIFSN3 FIELD32(0x0000f000) - -/* - * WMM_CWMIN_CSR: CWmin for each EDCA AC - * CWMIN0: AC_VO - * CWMIN1: AC_VI - * CWMIN2: AC_BE - * CWMIN3: AC_BK - */ -#define WMM_CWMIN_CFG 0x0218 -#define WMM_CWMIN_CFG_CWMIN0 FIELD32(0x0000000f) -#define WMM_CWMIN_CFG_CWMIN1 FIELD32(0x000000f0) -#define WMM_CWMIN_CFG_CWMIN2 FIELD32(0x00000f00) -#define WMM_CWMIN_CFG_CWMIN3 FIELD32(0x0000f000) - -/* - * WMM_CWMAX_CSR: CWmax for each EDCA AC - * CWMAX0: AC_VO - * CWMAX1: AC_VI - * CWMAX2: AC_BE - * CWMAX3: AC_BK - */ -#define WMM_CWMAX_CFG 0x021c -#define WMM_CWMAX_CFG_CWMAX0 FIELD32(0x0000000f) -#define WMM_CWMAX_CFG_CWMAX1 FIELD32(0x000000f0) -#define WMM_CWMAX_CFG_CWMAX2 FIELD32(0x00000f00) -#define WMM_CWMAX_CFG_CWMAX3 FIELD32(0x0000f000) - -/* - * AC_TXOP0: AC_VO/AC_VI TXOP register - * AC0TXOP: AC_VO in unit of 32us - * AC1TXOP: AC_VI in unit of 32us - */ -#define WMM_TXOP0_CFG 0x0220 -#define WMM_TXOP0_CFG_AC0TXOP FIELD32(0x0000ffff) -#define WMM_TXOP0_CFG_AC1TXOP FIELD32(0xffff0000) - -/* - * AC_TXOP1: AC_BE/AC_BK TXOP register - * AC2TXOP: AC_BE in unit of 32us - * AC3TXOP: AC_BK in unit of 32us - */ -#define WMM_TXOP1_CFG 0x0224 -#define WMM_TXOP1_CFG_AC2TXOP FIELD32(0x0000ffff) -#define WMM_TXOP1_CFG_AC3TXOP FIELD32(0xffff0000) - -/* - * GPIO_CTRL: - * GPIO_CTRL_VALx: GPIO value - * GPIO_CTRL_DIRx: GPIO direction: 0 = output; 1 = input - */ -#define GPIO_CTRL 0x0228 -#define GPIO_CTRL_VAL0 FIELD32(0x00000001) -#define GPIO_CTRL_VAL1 FIELD32(0x00000002) -#define GPIO_CTRL_VAL2 FIELD32(0x00000004) -#define GPIO_CTRL_VAL3 FIELD32(0x00000008) -#define GPIO_CTRL_VAL4 FIELD32(0x00000010) -#define GPIO_CTRL_VAL5 FIELD32(0x00000020) -#define GPIO_CTRL_VAL6 FIELD32(0x00000040) -#define GPIO_CTRL_VAL7 FIELD32(0x00000080) -#define GPIO_CTRL_DIR0 FIELD32(0x00000100) -#define GPIO_CTRL_DIR1 FIELD32(0x00000200) -#define GPIO_CTRL_DIR2 FIELD32(0x00000400) -#define GPIO_CTRL_DIR3 FIELD32(0x00000800) -#define GPIO_CTRL_DIR4 FIELD32(0x00001000) -#define GPIO_CTRL_DIR5 FIELD32(0x00002000) -#define GPIO_CTRL_DIR6 FIELD32(0x00004000) -#define GPIO_CTRL_DIR7 FIELD32(0x00008000) -#define GPIO_CTRL_VAL8 FIELD32(0x00010000) -#define GPIO_CTRL_VAL9 FIELD32(0x00020000) -#define GPIO_CTRL_VAL10 FIELD32(0x00040000) -#define GPIO_CTRL_DIR8 FIELD32(0x01000000) -#define GPIO_CTRL_DIR9 FIELD32(0x02000000) -#define GPIO_CTRL_DIR10 FIELD32(0x04000000) - -/* - * MCU_CMD_CFG - */ -#define MCU_CMD_CFG 0x022c - -/* - * AC_VO register offsets - */ -#define TX_BASE_PTR0 0x0230 -#define TX_MAX_CNT0 0x0234 -#define TX_CTX_IDX0 0x0238 -#define TX_DTX_IDX0 0x023c - -/* - * AC_VI register offsets - */ -#define TX_BASE_PTR1 0x0240 -#define TX_MAX_CNT1 0x0244 -#define TX_CTX_IDX1 0x0248 -#define TX_DTX_IDX1 0x024c - -/* - * AC_BE register offsets - */ -#define TX_BASE_PTR2 0x0250 -#define TX_MAX_CNT2 0x0254 -#define TX_CTX_IDX2 0x0258 -#define TX_DTX_IDX2 0x025c - -/* - * AC_BK register offsets - */ -#define TX_BASE_PTR3 0x0260 -#define TX_MAX_CNT3 0x0264 -#define TX_CTX_IDX3 0x0268 -#define TX_DTX_IDX3 0x026c - -/* - * HCCA register offsets - */ -#define TX_BASE_PTR4 0x0270 -#define TX_MAX_CNT4 0x0274 -#define TX_CTX_IDX4 0x0278 -#define TX_DTX_IDX4 0x027c - -/* - * MGMT register offsets - */ -#define TX_BASE_PTR5 0x0280 -#define TX_MAX_CNT5 0x0284 -#define TX_CTX_IDX5 0x0288 -#define TX_DTX_IDX5 0x028c - -/* - * RX register offsets - */ -#define RX_BASE_PTR 0x0290 -#define RX_MAX_CNT 0x0294 -#define RX_CRX_IDX 0x0298 -#define RX_DRX_IDX 0x029c - -/* - * USB_DMA_CFG - * RX_BULK_AGG_TIMEOUT: Rx Bulk Aggregation TimeOut in unit of 33ns. - * RX_BULK_AGG_LIMIT: Rx Bulk Aggregation Limit in unit of 256 bytes. - * PHY_CLEAR: phy watch dog enable. - * TX_CLEAR: Clear USB DMA TX path. - * TXOP_HALT: Halt TXOP count down when TX buffer is full. - * RX_BULK_AGG_EN: Enable Rx Bulk Aggregation. - * RX_BULK_EN: Enable USB DMA Rx. - * TX_BULK_EN: Enable USB DMA Tx. - * EP_OUT_VALID: OUT endpoint data valid. - * RX_BUSY: USB DMA RX FSM busy. - * TX_BUSY: USB DMA TX FSM busy. - */ -#define USB_DMA_CFG 0x02a0 -#define USB_DMA_CFG_RX_BULK_AGG_TIMEOUT FIELD32(0x000000ff) -#define USB_DMA_CFG_RX_BULK_AGG_LIMIT FIELD32(0x0000ff00) -#define USB_DMA_CFG_PHY_CLEAR FIELD32(0x00010000) -#define USB_DMA_CFG_TX_CLEAR FIELD32(0x00080000) -#define USB_DMA_CFG_TXOP_HALT FIELD32(0x00100000) -#define USB_DMA_CFG_RX_BULK_AGG_EN FIELD32(0x00200000) -#define USB_DMA_CFG_RX_BULK_EN FIELD32(0x00400000) -#define USB_DMA_CFG_TX_BULK_EN FIELD32(0x00800000) -#define USB_DMA_CFG_EP_OUT_VALID FIELD32(0x3f000000) -#define USB_DMA_CFG_RX_BUSY FIELD32(0x40000000) -#define USB_DMA_CFG_TX_BUSY FIELD32(0x80000000) - -/* - * US_CYC_CNT - * BT_MODE_EN: Bluetooth mode enable - * CLOCK CYCLE: Clock cycle count in 1us. - * PCI:0x21, PCIE:0x7d, USB:0x1e - */ -#define US_CYC_CNT 0x02a4 -#define US_CYC_CNT_BT_MODE_EN FIELD32(0x00000100) -#define US_CYC_CNT_CLOCK_CYCLE FIELD32(0x000000ff) - -/* - * PBF_SYS_CTRL - * HOST_RAM_WRITE: enable Host program ram write selection - */ -#define PBF_SYS_CTRL 0x0400 -#define PBF_SYS_CTRL_READY FIELD32(0x00000080) -#define PBF_SYS_CTRL_HOST_RAM_WRITE FIELD32(0x00010000) - -/* - * HOST-MCU shared memory - */ -#define HOST_CMD_CSR 0x0404 -#define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x000000ff) - -/* - * PBF registers - * Most are for debug. Driver doesn't touch PBF register. - */ -#define PBF_CFG 0x0408 -#define PBF_MAX_PCNT 0x040c -#define PBF_CTRL 0x0410 -#define PBF_INT_STA 0x0414 -#define PBF_INT_ENA 0x0418 - -/* - * BCN_OFFSET0: - */ -#define BCN_OFFSET0 0x042c -#define BCN_OFFSET0_BCN0 FIELD32(0x000000ff) -#define BCN_OFFSET0_BCN1 FIELD32(0x0000ff00) -#define BCN_OFFSET0_BCN2 FIELD32(0x00ff0000) -#define BCN_OFFSET0_BCN3 FIELD32(0xff000000) - -/* - * BCN_OFFSET1: - */ -#define BCN_OFFSET1 0x0430 -#define BCN_OFFSET1_BCN4 FIELD32(0x000000ff) -#define BCN_OFFSET1_BCN5 FIELD32(0x0000ff00) -#define BCN_OFFSET1_BCN6 FIELD32(0x00ff0000) -#define BCN_OFFSET1_BCN7 FIELD32(0xff000000) - -/* - * TXRXQ_PCNT: PBF register - * PCNT_TX0Q: Page count for TX hardware queue 0 - * PCNT_TX1Q: Page count for TX hardware queue 1 - * PCNT_TX2Q: Page count for TX hardware queue 2 - * PCNT_RX0Q: Page count for RX hardware queue - */ -#define TXRXQ_PCNT 0x0438 -#define TXRXQ_PCNT_TX0Q FIELD32(0x000000ff) -#define TXRXQ_PCNT_TX1Q FIELD32(0x0000ff00) -#define TXRXQ_PCNT_TX2Q FIELD32(0x00ff0000) -#define TXRXQ_PCNT_RX0Q FIELD32(0xff000000) - -/* - * PBF register - * Debug. Driver doesn't touch PBF register. - */ -#define PBF_DBG 0x043c - -/* - * RF registers - */ -#define RF_CSR_CFG 0x0500 -#define RF_CSR_CFG_DATA FIELD32(0x000000ff) -#define RF_CSR_CFG_REGNUM FIELD32(0x00003f00) -#define RF_CSR_CFG_WRITE FIELD32(0x00010000) -#define RF_CSR_CFG_BUSY FIELD32(0x00020000) - -/* - * EFUSE_CSR: RT30x0 EEPROM - */ -#define EFUSE_CTRL 0x0580 -#define EFUSE_CTRL_ADDRESS_IN FIELD32(0x03fe0000) -#define EFUSE_CTRL_MODE FIELD32(0x000000c0) -#define EFUSE_CTRL_KICK FIELD32(0x40000000) -#define EFUSE_CTRL_PRESENT FIELD32(0x80000000) - -/* - * EFUSE_DATA0 - */ -#define EFUSE_DATA0 0x0590 - -/* - * EFUSE_DATA1 - */ -#define EFUSE_DATA1 0x0594 - -/* - * EFUSE_DATA2 - */ -#define EFUSE_DATA2 0x0598 - -/* - * EFUSE_DATA3 - */ -#define EFUSE_DATA3 0x059c - -/* - * LDO_CFG0 - */ -#define LDO_CFG0 0x05d4 -#define LDO_CFG0_DELAY3 FIELD32(0x000000ff) -#define LDO_CFG0_DELAY2 FIELD32(0x0000ff00) -#define LDO_CFG0_DELAY1 FIELD32(0x00ff0000) -#define LDO_CFG0_BGSEL FIELD32(0x03000000) -#define LDO_CFG0_LDO_CORE_VLEVEL FIELD32(0x1c000000) -#define LD0_CFG0_LDO25_LEVEL FIELD32(0x60000000) -#define LDO_CFG0_LDO25_LARGEA FIELD32(0x80000000) - -/* - * GPIO_SWITCH - */ -#define GPIO_SWITCH 0x05dc -#define GPIO_SWITCH_0 FIELD32(0x00000001) -#define GPIO_SWITCH_1 FIELD32(0x00000002) -#define GPIO_SWITCH_2 FIELD32(0x00000004) -#define GPIO_SWITCH_3 FIELD32(0x00000008) -#define GPIO_SWITCH_4 FIELD32(0x00000010) -#define GPIO_SWITCH_5 FIELD32(0x00000020) -#define GPIO_SWITCH_6 FIELD32(0x00000040) -#define GPIO_SWITCH_7 FIELD32(0x00000080) - -/* - * FIXME: where the DEBUG_INDEX name come from? - */ -#define MAC_DEBUG_INDEX 0x05e8 -#define MAC_DEBUG_INDEX_XTAL FIELD32(0x80000000) - -/* - * MAC Control/Status Registers(CSR). - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * MAC_CSR0: ASIC revision number. - * ASIC_REV: 0 - * ASIC_VER: 2860 or 2870 - */ -#define MAC_CSR0 0x1000 -#define MAC_CSR0_REVISION FIELD32(0x0000ffff) -#define MAC_CSR0_CHIPSET FIELD32(0xffff0000) - -/* - * MAC_SYS_CTRL: - */ -#define MAC_SYS_CTRL 0x1004 -#define MAC_SYS_CTRL_RESET_CSR FIELD32(0x00000001) -#define MAC_SYS_CTRL_RESET_BBP FIELD32(0x00000002) -#define MAC_SYS_CTRL_ENABLE_TX FIELD32(0x00000004) -#define MAC_SYS_CTRL_ENABLE_RX FIELD32(0x00000008) -#define MAC_SYS_CTRL_CONTINUOUS_TX FIELD32(0x00000010) -#define MAC_SYS_CTRL_LOOPBACK FIELD32(0x00000020) -#define MAC_SYS_CTRL_WLAN_HALT FIELD32(0x00000040) -#define MAC_SYS_CTRL_RX_TIMESTAMP FIELD32(0x00000080) - -/* - * MAC_ADDR_DW0: STA MAC register 0 - */ -#define MAC_ADDR_DW0 0x1008 -#define MAC_ADDR_DW0_BYTE0 FIELD32(0x000000ff) -#define MAC_ADDR_DW0_BYTE1 FIELD32(0x0000ff00) -#define MAC_ADDR_DW0_BYTE2 FIELD32(0x00ff0000) -#define MAC_ADDR_DW0_BYTE3 FIELD32(0xff000000) - -/* - * MAC_ADDR_DW1: STA MAC register 1 - * UNICAST_TO_ME_MASK: - * Used to mask off bits from byte 5 of the MAC address - * to determine the UNICAST_TO_ME bit for RX frames. - * The full mask is complemented by BSS_ID_MASK: - * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK - */ -#define MAC_ADDR_DW1 0x100c -#define MAC_ADDR_DW1_BYTE4 FIELD32(0x000000ff) -#define MAC_ADDR_DW1_BYTE5 FIELD32(0x0000ff00) -#define MAC_ADDR_DW1_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) - -/* - * MAC_BSSID_DW0: BSSID register 0 - */ -#define MAC_BSSID_DW0 0x1010 -#define MAC_BSSID_DW0_BYTE0 FIELD32(0x000000ff) -#define MAC_BSSID_DW0_BYTE1 FIELD32(0x0000ff00) -#define MAC_BSSID_DW0_BYTE2 FIELD32(0x00ff0000) -#define MAC_BSSID_DW0_BYTE3 FIELD32(0xff000000) - -/* - * MAC_BSSID_DW1: BSSID register 1 - * BSS_ID_MASK: - * 0: 1-BSSID mode (BSS index = 0) - * 1: 2-BSSID mode (BSS index: Byte5, bit 0) - * 2: 4-BSSID mode (BSS index: byte5, bit 0 - 1) - * 3: 8-BSSID mode (BSS index: byte5, bit 0 - 2) - * This mask is used to mask off bits 0, 1 and 2 of byte 5 of the - * BSSID. This will make sure that those bits will be ignored - * when determining the MY_BSS of RX frames. - */ -#define MAC_BSSID_DW1 0x1014 -#define MAC_BSSID_DW1_BYTE4 FIELD32(0x000000ff) -#define MAC_BSSID_DW1_BYTE5 FIELD32(0x0000ff00) -#define MAC_BSSID_DW1_BSS_ID_MASK FIELD32(0x00030000) -#define MAC_BSSID_DW1_BSS_BCN_NUM FIELD32(0x001c0000) - -/* - * MAX_LEN_CFG: Maximum frame length register. - * MAX_MPDU: rt2860b max 16k bytes - * MAX_PSDU: Maximum PSDU length - * (power factor) 0:2^13, 1:2^14, 2:2^15, 3:2^16 - */ -#define MAX_LEN_CFG 0x1018 -#define MAX_LEN_CFG_MAX_MPDU FIELD32(0x00000fff) -#define MAX_LEN_CFG_MAX_PSDU FIELD32(0x00003000) -#define MAX_LEN_CFG_MIN_PSDU FIELD32(0x0000c000) -#define MAX_LEN_CFG_MIN_MPDU FIELD32(0x000f0000) - -/* - * BBP_CSR_CFG: BBP serial control register - * VALUE: Register value to program into BBP - * REG_NUM: Selected BBP register - * READ_CONTROL: 0 write BBP, 1 read BBP - * BUSY: ASIC is busy executing BBP commands - * BBP_PAR_DUR: 0 4 MAC clocks, 1 8 MAC clocks - * BBP_RW_MODE: 0 serial, 1 parallel - */ -#define BBP_CSR_CFG 0x101c -#define BBP_CSR_CFG_VALUE FIELD32(0x000000ff) -#define BBP_CSR_CFG_REGNUM FIELD32(0x0000ff00) -#define BBP_CSR_CFG_READ_CONTROL FIELD32(0x00010000) -#define BBP_CSR_CFG_BUSY FIELD32(0x00020000) -#define BBP_CSR_CFG_BBP_PAR_DUR FIELD32(0x00040000) -#define BBP_CSR_CFG_BBP_RW_MODE FIELD32(0x00080000) - -/* - * RF_CSR_CFG0: RF control register - * REGID_AND_VALUE: Register value to program into RF - * BITWIDTH: Selected RF register - * STANDBYMODE: 0 high when standby, 1 low when standby - * SEL: 0 RF_LE0 activate, 1 RF_LE1 activate - * BUSY: ASIC is busy executing RF commands - */ -#define RF_CSR_CFG0 0x1020 -#define RF_CSR_CFG0_REGID_AND_VALUE FIELD32(0x00ffffff) -#define RF_CSR_CFG0_BITWIDTH FIELD32(0x1f000000) -#define RF_CSR_CFG0_REG_VALUE_BW FIELD32(0x1fffffff) -#define RF_CSR_CFG0_STANDBYMODE FIELD32(0x20000000) -#define RF_CSR_CFG0_SEL FIELD32(0x40000000) -#define RF_CSR_CFG0_BUSY FIELD32(0x80000000) - -/* - * RF_CSR_CFG1: RF control register - * REGID_AND_VALUE: Register value to program into RF - * RFGAP: Gap between BB_CONTROL_RF and RF_LE - * 0: 3 system clock cycle (37.5usec) - * 1: 5 system clock cycle (62.5usec) - */ -#define RF_CSR_CFG1 0x1024 -#define RF_CSR_CFG1_REGID_AND_VALUE FIELD32(0x00ffffff) -#define RF_CSR_CFG1_RFGAP FIELD32(0x1f000000) - -/* - * RF_CSR_CFG2: RF control register - * VALUE: Register value to program into RF - */ -#define RF_CSR_CFG2 0x1028 -#define RF_CSR_CFG2_VALUE FIELD32(0x00ffffff) - -/* - * LED_CFG: LED control - * ON_PERIOD: LED active time (ms) during TX (only used for LED mode 1) - * OFF_PERIOD: LED inactive time (ms) during TX (only used for LED mode 1) - * SLOW_BLINK_PERIOD: LED blink interval in seconds (only used for LED mode 2) - * color LED's: - * 0: off - * 1: blinking upon TX2 - * 2: periodic slow blinking - * 3: always on - * LED polarity: - * 0: active low - * 1: active high - */ -#define LED_CFG 0x102c -#define LED_CFG_ON_PERIOD FIELD32(0x000000ff) -#define LED_CFG_OFF_PERIOD FIELD32(0x0000ff00) -#define LED_CFG_SLOW_BLINK_PERIOD FIELD32(0x003f0000) -#define LED_CFG_R_LED_MODE FIELD32(0x03000000) -#define LED_CFG_G_LED_MODE FIELD32(0x0c000000) -#define LED_CFG_Y_LED_MODE FIELD32(0x30000000) -#define LED_CFG_LED_POLAR FIELD32(0x40000000) - -/* - * AMPDU_BA_WINSIZE: Force BlockAck window size - * FORCE_WINSIZE_ENABLE: - * 0: Disable forcing of BlockAck window size - * 1: Enable forcing of BlockAck window size, overwrites values BlockAck - * window size values in the TXWI - * FORCE_WINSIZE: BlockAck window size - */ -#define AMPDU_BA_WINSIZE 0x1040 -#define AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE FIELD32(0x00000020) -#define AMPDU_BA_WINSIZE_FORCE_WINSIZE FIELD32(0x0000001f) - -/* - * XIFS_TIME_CFG: MAC timing - * CCKM_SIFS_TIME: unit 1us. Applied after CCK RX/TX - * OFDM_SIFS_TIME: unit 1us. Applied after OFDM RX/TX - * OFDM_XIFS_TIME: unit 1us. Applied after OFDM RX - * when MAC doesn't reference BBP signal BBRXEND - * EIFS: unit 1us - * BB_RXEND_ENABLE: reference RXEND signal to begin XIFS defer - * - */ -#define XIFS_TIME_CFG 0x1100 -#define XIFS_TIME_CFG_CCKM_SIFS_TIME FIELD32(0x000000ff) -#define XIFS_TIME_CFG_OFDM_SIFS_TIME FIELD32(0x0000ff00) -#define XIFS_TIME_CFG_OFDM_XIFS_TIME FIELD32(0x000f0000) -#define XIFS_TIME_CFG_EIFS FIELD32(0x1ff00000) -#define XIFS_TIME_CFG_BB_RXEND_ENABLE FIELD32(0x20000000) - -/* - * BKOFF_SLOT_CFG: - */ -#define BKOFF_SLOT_CFG 0x1104 -#define BKOFF_SLOT_CFG_SLOT_TIME FIELD32(0x000000ff) -#define BKOFF_SLOT_CFG_CC_DELAY_TIME FIELD32(0x0000ff00) - -/* - * NAV_TIME_CFG: - */ -#define NAV_TIME_CFG 0x1108 -#define NAV_TIME_CFG_SIFS FIELD32(0x000000ff) -#define NAV_TIME_CFG_SLOT_TIME FIELD32(0x0000ff00) -#define NAV_TIME_CFG_EIFS FIELD32(0x01ff0000) -#define NAV_TIME_ZERO_SIFS FIELD32(0x02000000) - -/* - * CH_TIME_CFG: count as channel busy - * EIFS_BUSY: Count EIFS as channel busy - * NAV_BUSY: Count NAS as channel busy - * RX_BUSY: Count RX as channel busy - * TX_BUSY: Count TX as channel busy - * TMR_EN: Enable channel statistics timer - */ -#define CH_TIME_CFG 0x110c -#define CH_TIME_CFG_EIFS_BUSY FIELD32(0x00000010) -#define CH_TIME_CFG_NAV_BUSY FIELD32(0x00000008) -#define CH_TIME_CFG_RX_BUSY FIELD32(0x00000004) -#define CH_TIME_CFG_TX_BUSY FIELD32(0x00000002) -#define CH_TIME_CFG_TMR_EN FIELD32(0x00000001) - -/* - * PBF_LIFE_TIMER: TX/RX MPDU timestamp timer (free run) Unit: 1us - */ -#define PBF_LIFE_TIMER 0x1110 - -/* - * BCN_TIME_CFG: - * BEACON_INTERVAL: in unit of 1/16 TU - * TSF_TICKING: Enable TSF auto counting - * TSF_SYNC: Enable TSF sync, 00: disable, 01: infra mode, 10: ad-hoc mode - * BEACON_GEN: Enable beacon generator - */ -#define BCN_TIME_CFG 0x1114 -#define BCN_TIME_CFG_BEACON_INTERVAL FIELD32(0x0000ffff) -#define BCN_TIME_CFG_TSF_TICKING FIELD32(0x00010000) -#define BCN_TIME_CFG_TSF_SYNC FIELD32(0x00060000) -#define BCN_TIME_CFG_TBTT_ENABLE FIELD32(0x00080000) -#define BCN_TIME_CFG_BEACON_GEN FIELD32(0x00100000) -#define BCN_TIME_CFG_TX_TIME_COMPENSATE FIELD32(0xf0000000) - -/* - * TBTT_SYNC_CFG: - * BCN_AIFSN: Beacon AIFSN after TBTT interrupt in slots - * BCN_CWMIN: Beacon CWMin after TBTT interrupt in slots - */ -#define TBTT_SYNC_CFG 0x1118 -#define TBTT_SYNC_CFG_TBTT_ADJUST FIELD32(0x000000ff) -#define TBTT_SYNC_CFG_BCN_EXP_WIN FIELD32(0x0000ff00) -#define TBTT_SYNC_CFG_BCN_AIFSN FIELD32(0x000f0000) -#define TBTT_SYNC_CFG_BCN_CWMIN FIELD32(0x00f00000) - -/* - * TSF_TIMER_DW0: Local lsb TSF timer, read-only - */ -#define TSF_TIMER_DW0 0x111c -#define TSF_TIMER_DW0_LOW_WORD FIELD32(0xffffffff) - -/* - * TSF_TIMER_DW1: Local msb TSF timer, read-only - */ -#define TSF_TIMER_DW1 0x1120 -#define TSF_TIMER_DW1_HIGH_WORD FIELD32(0xffffffff) - -/* - * TBTT_TIMER: TImer remains till next TBTT, read-only - */ -#define TBTT_TIMER 0x1124 - -/* - * INT_TIMER_CFG: timer configuration - * PRE_TBTT_TIMER: leadtime to tbtt for pretbtt interrupt in units of 1/16 TU - * GP_TIMER: period of general purpose timer in units of 1/16 TU - */ -#define INT_TIMER_CFG 0x1128 -#define INT_TIMER_CFG_PRE_TBTT_TIMER FIELD32(0x0000ffff) -#define INT_TIMER_CFG_GP_TIMER FIELD32(0xffff0000) - -/* - * INT_TIMER_EN: GP-timer and pre-tbtt Int enable - */ -#define INT_TIMER_EN 0x112c -#define INT_TIMER_EN_PRE_TBTT_TIMER FIELD32(0x00000001) -#define INT_TIMER_EN_GP_TIMER FIELD32(0x00000002) - -/* - * CH_IDLE_STA: channel idle time (in us) - */ -#define CH_IDLE_STA 0x1130 - -/* - * CH_BUSY_STA: channel busy time on primary channel (in us) - */ -#define CH_BUSY_STA 0x1134 - -/* - * CH_BUSY_STA_SEC: channel busy time on secondary channel in HT40 mode (in us) - */ -#define CH_BUSY_STA_SEC 0x1138 - -/* - * MAC_STATUS_CFG: - * BBP_RF_BUSY: When set to 0, BBP and RF are stable. - * if 1 or higher one of the 2 registers is busy. - */ -#define MAC_STATUS_CFG 0x1200 -#define MAC_STATUS_CFG_BBP_RF_BUSY FIELD32(0x00000003) - -/* - * PWR_PIN_CFG: - */ -#define PWR_PIN_CFG 0x1204 - -/* - * AUTOWAKEUP_CFG: Manual power control / status register - * TBCN_BEFORE_WAKE: ForceWake has high privilege than PutToSleep when both set - * AUTOWAKE: 0:sleep, 1:awake - */ -#define AUTOWAKEUP_CFG 0x1208 -#define AUTOWAKEUP_CFG_AUTO_LEAD_TIME FIELD32(0x000000ff) -#define AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE FIELD32(0x00007f00) -#define AUTOWAKEUP_CFG_AUTOWAKE FIELD32(0x00008000) - -/* - * EDCA_AC0_CFG: - */ -#define EDCA_AC0_CFG 0x1300 -#define EDCA_AC0_CFG_TX_OP FIELD32(0x000000ff) -#define EDCA_AC0_CFG_AIFSN FIELD32(0x00000f00) -#define EDCA_AC0_CFG_CWMIN FIELD32(0x0000f000) -#define EDCA_AC0_CFG_CWMAX FIELD32(0x000f0000) - -/* - * EDCA_AC1_CFG: - */ -#define EDCA_AC1_CFG 0x1304 -#define EDCA_AC1_CFG_TX_OP FIELD32(0x000000ff) -#define EDCA_AC1_CFG_AIFSN FIELD32(0x00000f00) -#define EDCA_AC1_CFG_CWMIN FIELD32(0x0000f000) -#define EDCA_AC1_CFG_CWMAX FIELD32(0x000f0000) - -/* - * EDCA_AC2_CFG: - */ -#define EDCA_AC2_CFG 0x1308 -#define EDCA_AC2_CFG_TX_OP FIELD32(0x000000ff) -#define EDCA_AC2_CFG_AIFSN FIELD32(0x00000f00) -#define EDCA_AC2_CFG_CWMIN FIELD32(0x0000f000) -#define EDCA_AC2_CFG_CWMAX FIELD32(0x000f0000) - -/* - * EDCA_AC3_CFG: - */ -#define EDCA_AC3_CFG 0x130c -#define EDCA_AC3_CFG_TX_OP FIELD32(0x000000ff) -#define EDCA_AC3_CFG_AIFSN FIELD32(0x00000f00) -#define EDCA_AC3_CFG_CWMIN FIELD32(0x0000f000) -#define EDCA_AC3_CFG_CWMAX FIELD32(0x000f0000) - -/* - * EDCA_TID_AC_MAP: - */ -#define EDCA_TID_AC_MAP 0x1310 - -/* - * TX_PWR_CFG: - */ -#define TX_PWR_CFG_RATE0 FIELD32(0x0000000f) -#define TX_PWR_CFG_RATE1 FIELD32(0x000000f0) -#define TX_PWR_CFG_RATE2 FIELD32(0x00000f00) -#define TX_PWR_CFG_RATE3 FIELD32(0x0000f000) -#define TX_PWR_CFG_RATE4 FIELD32(0x000f0000) -#define TX_PWR_CFG_RATE5 FIELD32(0x00f00000) -#define TX_PWR_CFG_RATE6 FIELD32(0x0f000000) -#define TX_PWR_CFG_RATE7 FIELD32(0xf0000000) - -/* - * TX_PWR_CFG_0: - */ -#define TX_PWR_CFG_0 0x1314 -#define TX_PWR_CFG_0_1MBS FIELD32(0x0000000f) -#define TX_PWR_CFG_0_2MBS FIELD32(0x000000f0) -#define TX_PWR_CFG_0_55MBS FIELD32(0x00000f00) -#define TX_PWR_CFG_0_11MBS FIELD32(0x0000f000) -#define TX_PWR_CFG_0_6MBS FIELD32(0x000f0000) -#define TX_PWR_CFG_0_9MBS FIELD32(0x00f00000) -#define TX_PWR_CFG_0_12MBS FIELD32(0x0f000000) -#define TX_PWR_CFG_0_18MBS FIELD32(0xf0000000) -/* bits for 3T devices */ -#define TX_PWR_CFG_0_CCK1_CH0 FIELD32(0x0000000f) -#define TX_PWR_CFG_0_CCK1_CH1 FIELD32(0x000000f0) -#define TX_PWR_CFG_0_CCK5_CH0 FIELD32(0x00000f00) -#define TX_PWR_CFG_0_CCK5_CH1 FIELD32(0x0000f000) -#define TX_PWR_CFG_0_OFDM6_CH0 FIELD32(0x000f0000) -#define TX_PWR_CFG_0_OFDM6_CH1 FIELD32(0x00f00000) -#define TX_PWR_CFG_0_OFDM12_CH0 FIELD32(0x0f000000) -#define TX_PWR_CFG_0_OFDM12_CH1 FIELD32(0xf0000000) - -/* - * TX_PWR_CFG_1: - */ -#define TX_PWR_CFG_1 0x1318 -#define TX_PWR_CFG_1_24MBS FIELD32(0x0000000f) -#define TX_PWR_CFG_1_36MBS FIELD32(0x000000f0) -#define TX_PWR_CFG_1_48MBS FIELD32(0x00000f00) -#define TX_PWR_CFG_1_54MBS FIELD32(0x0000f000) -#define TX_PWR_CFG_1_MCS0 FIELD32(0x000f0000) -#define TX_PWR_CFG_1_MCS1 FIELD32(0x00f00000) -#define TX_PWR_CFG_1_MCS2 FIELD32(0x0f000000) -#define TX_PWR_CFG_1_MCS3 FIELD32(0xf0000000) -/* bits for 3T devices */ -#define TX_PWR_CFG_1_OFDM24_CH0 FIELD32(0x0000000f) -#define TX_PWR_CFG_1_OFDM24_CH1 FIELD32(0x000000f0) -#define TX_PWR_CFG_1_OFDM48_CH0 FIELD32(0x00000f00) -#define TX_PWR_CFG_1_OFDM48_CH1 FIELD32(0x0000f000) -#define TX_PWR_CFG_1_MCS0_CH0 FIELD32(0x000f0000) -#define TX_PWR_CFG_1_MCS0_CH1 FIELD32(0x00f00000) -#define TX_PWR_CFG_1_MCS2_CH0 FIELD32(0x0f000000) -#define TX_PWR_CFG_1_MCS2_CH1 FIELD32(0xf0000000) - -/* - * TX_PWR_CFG_2: - */ -#define TX_PWR_CFG_2 0x131c -#define TX_PWR_CFG_2_MCS4 FIELD32(0x0000000f) -#define TX_PWR_CFG_2_MCS5 FIELD32(0x000000f0) -#define TX_PWR_CFG_2_MCS6 FIELD32(0x00000f00) -#define TX_PWR_CFG_2_MCS7 FIELD32(0x0000f000) -#define TX_PWR_CFG_2_MCS8 FIELD32(0x000f0000) -#define TX_PWR_CFG_2_MCS9 FIELD32(0x00f00000) -#define TX_PWR_CFG_2_MCS10 FIELD32(0x0f000000) -#define TX_PWR_CFG_2_MCS11 FIELD32(0xf0000000) -/* bits for 3T devices */ -#define TX_PWR_CFG_2_MCS4_CH0 FIELD32(0x0000000f) -#define TX_PWR_CFG_2_MCS4_CH1 FIELD32(0x000000f0) -#define TX_PWR_CFG_2_MCS6_CH0 FIELD32(0x00000f00) -#define TX_PWR_CFG_2_MCS6_CH1 FIELD32(0x0000f000) -#define TX_PWR_CFG_2_MCS8_CH0 FIELD32(0x000f0000) -#define TX_PWR_CFG_2_MCS8_CH1 FIELD32(0x00f00000) -#define TX_PWR_CFG_2_MCS10_CH0 FIELD32(0x0f000000) -#define TX_PWR_CFG_2_MCS10_CH1 FIELD32(0xf0000000) - -/* - * TX_PWR_CFG_3: - */ -#define TX_PWR_CFG_3 0x1320 -#define TX_PWR_CFG_3_MCS12 FIELD32(0x0000000f) -#define TX_PWR_CFG_3_MCS13 FIELD32(0x000000f0) -#define TX_PWR_CFG_3_MCS14 FIELD32(0x00000f00) -#define TX_PWR_CFG_3_MCS15 FIELD32(0x0000f000) -#define TX_PWR_CFG_3_UKNOWN1 FIELD32(0x000f0000) -#define TX_PWR_CFG_3_UKNOWN2 FIELD32(0x00f00000) -#define TX_PWR_CFG_3_UKNOWN3 FIELD32(0x0f000000) -#define TX_PWR_CFG_3_UKNOWN4 FIELD32(0xf0000000) -/* bits for 3T devices */ -#define TX_PWR_CFG_3_MCS12_CH0 FIELD32(0x0000000f) -#define TX_PWR_CFG_3_MCS12_CH1 FIELD32(0x000000f0) -#define TX_PWR_CFG_3_MCS14_CH0 FIELD32(0x00000f00) -#define TX_PWR_CFG_3_MCS14_CH1 FIELD32(0x0000f000) -#define TX_PWR_CFG_3_STBC0_CH0 FIELD32(0x000f0000) -#define TX_PWR_CFG_3_STBC0_CH1 FIELD32(0x00f00000) -#define TX_PWR_CFG_3_STBC2_CH0 FIELD32(0x0f000000) -#define TX_PWR_CFG_3_STBC2_CH1 FIELD32(0xf0000000) - -/* - * TX_PWR_CFG_4: - */ -#define TX_PWR_CFG_4 0x1324 -#define TX_PWR_CFG_4_UKNOWN5 FIELD32(0x0000000f) -#define TX_PWR_CFG_4_UKNOWN6 FIELD32(0x000000f0) -#define TX_PWR_CFG_4_UKNOWN7 FIELD32(0x00000f00) -#define TX_PWR_CFG_4_UKNOWN8 FIELD32(0x0000f000) -/* bits for 3T devices */ -#define TX_PWR_CFG_3_STBC4_CH0 FIELD32(0x0000000f) -#define TX_PWR_CFG_3_STBC4_CH1 FIELD32(0x000000f0) -#define TX_PWR_CFG_3_STBC6_CH0 FIELD32(0x00000f00) -#define TX_PWR_CFG_3_STBC6_CH1 FIELD32(0x0000f000) - -/* - * TX_PIN_CFG: - */ -#define TX_PIN_CFG 0x1328 -#define TX_PIN_CFG_PA_PE_DISABLE 0xfcfffff0 -#define TX_PIN_CFG_PA_PE_A0_EN FIELD32(0x00000001) -#define TX_PIN_CFG_PA_PE_G0_EN FIELD32(0x00000002) -#define TX_PIN_CFG_PA_PE_A1_EN FIELD32(0x00000004) -#define TX_PIN_CFG_PA_PE_G1_EN FIELD32(0x00000008) -#define TX_PIN_CFG_PA_PE_A0_POL FIELD32(0x00000010) -#define TX_PIN_CFG_PA_PE_G0_POL FIELD32(0x00000020) -#define TX_PIN_CFG_PA_PE_A1_POL FIELD32(0x00000040) -#define TX_PIN_CFG_PA_PE_G1_POL FIELD32(0x00000080) -#define TX_PIN_CFG_LNA_PE_A0_EN FIELD32(0x00000100) -#define TX_PIN_CFG_LNA_PE_G0_EN FIELD32(0x00000200) -#define TX_PIN_CFG_LNA_PE_A1_EN FIELD32(0x00000400) -#define TX_PIN_CFG_LNA_PE_G1_EN FIELD32(0x00000800) -#define TX_PIN_CFG_LNA_PE_A0_POL FIELD32(0x00001000) -#define TX_PIN_CFG_LNA_PE_G0_POL FIELD32(0x00002000) -#define TX_PIN_CFG_LNA_PE_A1_POL FIELD32(0x00004000) -#define TX_PIN_CFG_LNA_PE_G1_POL FIELD32(0x00008000) -#define TX_PIN_CFG_RFTR_EN FIELD32(0x00010000) -#define TX_PIN_CFG_RFTR_POL FIELD32(0x00020000) -#define TX_PIN_CFG_TRSW_EN FIELD32(0x00040000) -#define TX_PIN_CFG_TRSW_POL FIELD32(0x00080000) -#define TX_PIN_CFG_PA_PE_A2_EN FIELD32(0x01000000) -#define TX_PIN_CFG_PA_PE_G2_EN FIELD32(0x02000000) -#define TX_PIN_CFG_PA_PE_A2_POL FIELD32(0x04000000) -#define TX_PIN_CFG_PA_PE_G2_POL FIELD32(0x08000000) -#define TX_PIN_CFG_LNA_PE_A2_EN FIELD32(0x10000000) -#define TX_PIN_CFG_LNA_PE_G2_EN FIELD32(0x20000000) -#define TX_PIN_CFG_LNA_PE_A2_POL FIELD32(0x40000000) -#define TX_PIN_CFG_LNA_PE_G2_POL FIELD32(0x80000000) - -/* - * TX_BAND_CFG: 0x1 use upper 20MHz, 0x0 use lower 20MHz - */ -#define TX_BAND_CFG 0x132c -#define TX_BAND_CFG_HT40_MINUS FIELD32(0x00000001) -#define TX_BAND_CFG_A FIELD32(0x00000002) -#define TX_BAND_CFG_BG FIELD32(0x00000004) - -/* - * TX_SW_CFG0: - */ -#define TX_SW_CFG0 0x1330 - -/* - * TX_SW_CFG1: - */ -#define TX_SW_CFG1 0x1334 - -/* - * TX_SW_CFG2: - */ -#define TX_SW_CFG2 0x1338 - -/* - * TXOP_THRES_CFG: - */ -#define TXOP_THRES_CFG 0x133c - -/* - * TXOP_CTRL_CFG: - * TIMEOUT_TRUN_EN: Enable/Disable TXOP timeout truncation - * AC_TRUN_EN: Enable/Disable truncation for AC change - * TXRATEGRP_TRUN_EN: Enable/Disable truncation for TX rate group change - * USER_MODE_TRUN_EN: Enable/Disable truncation for user TXOP mode - * MIMO_PS_TRUN_EN: Enable/Disable truncation for MIMO PS RTS/CTS - * RESERVED_TRUN_EN: Reserved - * LSIG_TXOP_EN: Enable/Disable L-SIG TXOP protection - * EXT_CCA_EN: Enable/Disable extension channel CCA reference (Defer 40Mhz - * transmissions if extension CCA is clear). - * EXT_CCA_DLY: Extension CCA signal delay time (unit: us) - * EXT_CWMIN: CwMin for extension channel backoff - * 0: Disabled - * - */ -#define TXOP_CTRL_CFG 0x1340 -#define TXOP_CTRL_CFG_TIMEOUT_TRUN_EN FIELD32(0x00000001) -#define TXOP_CTRL_CFG_AC_TRUN_EN FIELD32(0x00000002) -#define TXOP_CTRL_CFG_TXRATEGRP_TRUN_EN FIELD32(0x00000004) -#define TXOP_CTRL_CFG_USER_MODE_TRUN_EN FIELD32(0x00000008) -#define TXOP_CTRL_CFG_MIMO_PS_TRUN_EN FIELD32(0x00000010) -#define TXOP_CTRL_CFG_RESERVED_TRUN_EN FIELD32(0x00000020) -#define TXOP_CTRL_CFG_LSIG_TXOP_EN FIELD32(0x00000040) -#define TXOP_CTRL_CFG_EXT_CCA_EN FIELD32(0x00000080) -#define TXOP_CTRL_CFG_EXT_CCA_DLY FIELD32(0x0000ff00) -#define TXOP_CTRL_CFG_EXT_CWMIN FIELD32(0x000f0000) - -/* - * TX_RTS_CFG: - * RTS_THRES: unit:byte - * RTS_FBK_EN: enable rts rate fallback - */ -#define TX_RTS_CFG 0x1344 -#define TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT FIELD32(0x000000ff) -#define TX_RTS_CFG_RTS_THRES FIELD32(0x00ffff00) -#define TX_RTS_CFG_RTS_FBK_EN FIELD32(0x01000000) - -/* - * TX_TIMEOUT_CFG: - * MPDU_LIFETIME: expiration time = 2^(9+MPDU LIFE TIME) us - * RX_ACK_TIMEOUT: unit:slot. Used for TX procedure - * TX_OP_TIMEOUT: TXOP timeout value for TXOP truncation. - * it is recommended that: - * (SLOT_TIME) > (TX_OP_TIMEOUT) > (RX_ACK_TIMEOUT) - */ -#define TX_TIMEOUT_CFG 0x1348 -#define TX_TIMEOUT_CFG_MPDU_LIFETIME FIELD32(0x000000f0) -#define TX_TIMEOUT_CFG_RX_ACK_TIMEOUT FIELD32(0x0000ff00) -#define TX_TIMEOUT_CFG_TX_OP_TIMEOUT FIELD32(0x00ff0000) - -/* - * TX_RTY_CFG: - * SHORT_RTY_LIMIT: short retry limit - * LONG_RTY_LIMIT: long retry limit - * LONG_RTY_THRE: Long retry threshoold - * NON_AGG_RTY_MODE: Non-Aggregate MPDU retry mode - * 0:expired by retry limit, 1: expired by mpdu life timer - * AGG_RTY_MODE: Aggregate MPDU retry mode - * 0:expired by retry limit, 1: expired by mpdu life timer - * TX_AUTO_FB_ENABLE: Tx retry PHY rate auto fallback enable - */ -#define TX_RTY_CFG 0x134c -#define TX_RTY_CFG_SHORT_RTY_LIMIT FIELD32(0x000000ff) -#define TX_RTY_CFG_LONG_RTY_LIMIT FIELD32(0x0000ff00) -#define TX_RTY_CFG_LONG_RTY_THRE FIELD32(0x0fff0000) -#define TX_RTY_CFG_NON_AGG_RTY_MODE FIELD32(0x10000000) -#define TX_RTY_CFG_AGG_RTY_MODE FIELD32(0x20000000) -#define TX_RTY_CFG_TX_AUTO_FB_ENABLE FIELD32(0x40000000) - -/* - * TX_LINK_CFG: - * REMOTE_MFB_LIFETIME: remote MFB life time. unit: 32us - * MFB_ENABLE: TX apply remote MFB 1:enable - * REMOTE_UMFS_ENABLE: remote unsolicit MFB enable - * 0: not apply remote remote unsolicit (MFS=7) - * TX_MRQ_EN: MCS request TX enable - * TX_RDG_EN: RDG TX enable - * TX_CF_ACK_EN: Piggyback CF-ACK enable - * REMOTE_MFB: remote MCS feedback - * REMOTE_MFS: remote MCS feedback sequence number - */ -#define TX_LINK_CFG 0x1350 -#define TX_LINK_CFG_REMOTE_MFB_LIFETIME FIELD32(0x000000ff) -#define TX_LINK_CFG_MFB_ENABLE FIELD32(0x00000100) -#define TX_LINK_CFG_REMOTE_UMFS_ENABLE FIELD32(0x00000200) -#define TX_LINK_CFG_TX_MRQ_EN FIELD32(0x00000400) -#define TX_LINK_CFG_TX_RDG_EN FIELD32(0x00000800) -#define TX_LINK_CFG_TX_CF_ACK_EN FIELD32(0x00001000) -#define TX_LINK_CFG_REMOTE_MFB FIELD32(0x00ff0000) -#define TX_LINK_CFG_REMOTE_MFS FIELD32(0xff000000) - -/* - * HT_FBK_CFG0: - */ -#define HT_FBK_CFG0 0x1354 -#define HT_FBK_CFG0_HTMCS0FBK FIELD32(0x0000000f) -#define HT_FBK_CFG0_HTMCS1FBK FIELD32(0x000000f0) -#define HT_FBK_CFG0_HTMCS2FBK FIELD32(0x00000f00) -#define HT_FBK_CFG0_HTMCS3FBK FIELD32(0x0000f000) -#define HT_FBK_CFG0_HTMCS4FBK FIELD32(0x000f0000) -#define HT_FBK_CFG0_HTMCS5FBK FIELD32(0x00f00000) -#define HT_FBK_CFG0_HTMCS6FBK FIELD32(0x0f000000) -#define HT_FBK_CFG0_HTMCS7FBK FIELD32(0xf0000000) - -/* - * HT_FBK_CFG1: - */ -#define HT_FBK_CFG1 0x1358 -#define HT_FBK_CFG1_HTMCS8FBK FIELD32(0x0000000f) -#define HT_FBK_CFG1_HTMCS9FBK FIELD32(0x000000f0) -#define HT_FBK_CFG1_HTMCS10FBK FIELD32(0x00000f00) -#define HT_FBK_CFG1_HTMCS11FBK FIELD32(0x0000f000) -#define HT_FBK_CFG1_HTMCS12FBK FIELD32(0x000f0000) -#define HT_FBK_CFG1_HTMCS13FBK FIELD32(0x00f00000) -#define HT_FBK_CFG1_HTMCS14FBK FIELD32(0x0f000000) -#define HT_FBK_CFG1_HTMCS15FBK FIELD32(0xf0000000) - -/* - * LG_FBK_CFG0: - */ -#define LG_FBK_CFG0 0x135c -#define LG_FBK_CFG0_OFDMMCS0FBK FIELD32(0x0000000f) -#define LG_FBK_CFG0_OFDMMCS1FBK FIELD32(0x000000f0) -#define LG_FBK_CFG0_OFDMMCS2FBK FIELD32(0x00000f00) -#define LG_FBK_CFG0_OFDMMCS3FBK FIELD32(0x0000f000) -#define LG_FBK_CFG0_OFDMMCS4FBK FIELD32(0x000f0000) -#define LG_FBK_CFG0_OFDMMCS5FBK FIELD32(0x00f00000) -#define LG_FBK_CFG0_OFDMMCS6FBK FIELD32(0x0f000000) -#define LG_FBK_CFG0_OFDMMCS7FBK FIELD32(0xf0000000) - -/* - * LG_FBK_CFG1: - */ -#define LG_FBK_CFG1 0x1360 -#define LG_FBK_CFG0_CCKMCS0FBK FIELD32(0x0000000f) -#define LG_FBK_CFG0_CCKMCS1FBK FIELD32(0x000000f0) -#define LG_FBK_CFG0_CCKMCS2FBK FIELD32(0x00000f00) -#define LG_FBK_CFG0_CCKMCS3FBK FIELD32(0x0000f000) - -/* - * CCK_PROT_CFG: CCK Protection - * PROTECT_RATE: Protection control frame rate for CCK TX(RTS/CTS/CFEnd) - * PROTECT_CTRL: Protection control frame type for CCK TX - * 0:none, 1:RTS/CTS, 2:CTS-to-self - * PROTECT_NAV_SHORT: TXOP protection type for CCK TX with short NAV - * PROTECT_NAV_LONG: TXOP protection type for CCK TX with long NAV - * TX_OP_ALLOW_CCK: CCK TXOP allowance, 0:disallow - * TX_OP_ALLOW_OFDM: CCK TXOP allowance, 0:disallow - * TX_OP_ALLOW_MM20: CCK TXOP allowance, 0:disallow - * TX_OP_ALLOW_MM40: CCK TXOP allowance, 0:disallow - * TX_OP_ALLOW_GF20: CCK TXOP allowance, 0:disallow - * TX_OP_ALLOW_GF40: CCK TXOP allowance, 0:disallow - * RTS_TH_EN: RTS threshold enable on CCK TX - */ -#define CCK_PROT_CFG 0x1364 -#define CCK_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define CCK_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define CCK_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) -#define CCK_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) -#define CCK_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define CCK_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define CCK_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define CCK_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define CCK_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define CCK_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define CCK_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * OFDM_PROT_CFG: OFDM Protection - */ -#define OFDM_PROT_CFG 0x1368 -#define OFDM_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define OFDM_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define OFDM_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) -#define OFDM_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define OFDM_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define OFDM_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * MM20_PROT_CFG: MM20 Protection - */ -#define MM20_PROT_CFG 0x136c -#define MM20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define MM20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define MM20_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) -#define MM20_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) -#define MM20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define MM20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define MM20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define MM20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define MM20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define MM20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define MM20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * MM40_PROT_CFG: MM40 Protection - */ -#define MM40_PROT_CFG 0x1370 -#define MM40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define MM40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define MM40_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) -#define MM40_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) -#define MM40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define MM40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define MM40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define MM40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define MM40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define MM40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define MM40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * GF20_PROT_CFG: GF20 Protection - */ -#define GF20_PROT_CFG 0x1374 -#define GF20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define GF20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define GF20_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) -#define GF20_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) -#define GF20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define GF20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define GF20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define GF20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define GF20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define GF20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define GF20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * GF40_PROT_CFG: GF40 Protection - */ -#define GF40_PROT_CFG 0x1378 -#define GF40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) -#define GF40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) -#define GF40_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) -#define GF40_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) -#define GF40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) -#define GF40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) -#define GF40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) -#define GF40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) -#define GF40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) -#define GF40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) -#define GF40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) - -/* - * EXP_CTS_TIME: - */ -#define EXP_CTS_TIME 0x137c - -/* - * EXP_ACK_TIME: - */ -#define EXP_ACK_TIME 0x1380 - -/* TX_PWR_CFG_5 */ -#define TX_PWR_CFG_5 0x1384 -#define TX_PWR_CFG_5_MCS16_CH0 FIELD32(0x0000000f) -#define TX_PWR_CFG_5_MCS16_CH1 FIELD32(0x000000f0) -#define TX_PWR_CFG_5_MCS16_CH2 FIELD32(0x00000f00) -#define TX_PWR_CFG_5_MCS18_CH0 FIELD32(0x000f0000) -#define TX_PWR_CFG_5_MCS18_CH1 FIELD32(0x00f00000) -#define TX_PWR_CFG_5_MCS18_CH2 FIELD32(0x0f000000) - -/* TX_PWR_CFG_6 */ -#define TX_PWR_CFG_6 0x1388 -#define TX_PWR_CFG_6_MCS20_CH0 FIELD32(0x0000000f) -#define TX_PWR_CFG_6_MCS20_CH1 FIELD32(0x000000f0) -#define TX_PWR_CFG_6_MCS20_CH2 FIELD32(0x00000f00) -#define TX_PWR_CFG_6_MCS22_CH0 FIELD32(0x000f0000) -#define TX_PWR_CFG_6_MCS22_CH1 FIELD32(0x00f00000) -#define TX_PWR_CFG_6_MCS22_CH2 FIELD32(0x0f000000) - -/* TX_PWR_CFG_0_EXT */ -#define TX_PWR_CFG_0_EXT 0x1390 -#define TX_PWR_CFG_0_EXT_CCK1_CH2 FIELD32(0x0000000f) -#define TX_PWR_CFG_0_EXT_CCK5_CH2 FIELD32(0x00000f00) -#define TX_PWR_CFG_0_EXT_OFDM6_CH2 FIELD32(0x000f0000) -#define TX_PWR_CFG_0_EXT_OFDM12_CH2 FIELD32(0x0f000000) - -/* TX_PWR_CFG_1_EXT */ -#define TX_PWR_CFG_1_EXT 0x1394 -#define TX_PWR_CFG_1_EXT_OFDM24_CH2 FIELD32(0x0000000f) -#define TX_PWR_CFG_1_EXT_OFDM48_CH2 FIELD32(0x00000f00) -#define TX_PWR_CFG_1_EXT_MCS0_CH2 FIELD32(0x000f0000) -#define TX_PWR_CFG_1_EXT_MCS2_CH2 FIELD32(0x0f000000) - -/* TX_PWR_CFG_2_EXT */ -#define TX_PWR_CFG_2_EXT 0x1398 -#define TX_PWR_CFG_2_EXT_MCS4_CH2 FIELD32(0x0000000f) -#define TX_PWR_CFG_2_EXT_MCS6_CH2 FIELD32(0x00000f00) -#define TX_PWR_CFG_2_EXT_MCS8_CH2 FIELD32(0x000f0000) -#define TX_PWR_CFG_2_EXT_MCS10_CH2 FIELD32(0x0f000000) - -/* TX_PWR_CFG_3_EXT */ -#define TX_PWR_CFG_3_EXT 0x139c -#define TX_PWR_CFG_3_EXT_MCS12_CH2 FIELD32(0x0000000f) -#define TX_PWR_CFG_3_EXT_MCS14_CH2 FIELD32(0x00000f00) -#define TX_PWR_CFG_3_EXT_STBC0_CH2 FIELD32(0x000f0000) -#define TX_PWR_CFG_3_EXT_STBC2_CH2 FIELD32(0x0f000000) - -/* TX_PWR_CFG_4_EXT */ -#define TX_PWR_CFG_4_EXT 0x13a0 -#define TX_PWR_CFG_4_EXT_STBC4_CH2 FIELD32(0x0000000f) -#define TX_PWR_CFG_4_EXT_STBC6_CH2 FIELD32(0x00000f00) - -/* TX_PWR_CFG_7 */ -#define TX_PWR_CFG_7 0x13d4 -#define TX_PWR_CFG_7_OFDM54_CH0 FIELD32(0x0000000f) -#define TX_PWR_CFG_7_OFDM54_CH1 FIELD32(0x000000f0) -#define TX_PWR_CFG_7_OFDM54_CH2 FIELD32(0x00000f00) -#define TX_PWR_CFG_7_MCS7_CH0 FIELD32(0x000f0000) -#define TX_PWR_CFG_7_MCS7_CH1 FIELD32(0x00f00000) -#define TX_PWR_CFG_7_MCS7_CH2 FIELD32(0x0f000000) - -/* TX_PWR_CFG_8 */ -#define TX_PWR_CFG_8 0x13d8 -#define TX_PWR_CFG_8_MCS15_CH0 FIELD32(0x0000000f) -#define TX_PWR_CFG_8_MCS15_CH1 FIELD32(0x000000f0) -#define TX_PWR_CFG_8_MCS15_CH2 FIELD32(0x00000f00) -#define TX_PWR_CFG_8_MCS23_CH0 FIELD32(0x000f0000) -#define TX_PWR_CFG_8_MCS23_CH1 FIELD32(0x00f00000) -#define TX_PWR_CFG_8_MCS23_CH2 FIELD32(0x0f000000) - -/* TX_PWR_CFG_9 */ -#define TX_PWR_CFG_9 0x13dc -#define TX_PWR_CFG_9_STBC7_CH0 FIELD32(0x0000000f) -#define TX_PWR_CFG_9_STBC7_CH1 FIELD32(0x000000f0) -#define TX_PWR_CFG_9_STBC7_CH2 FIELD32(0x00000f00) - -/* - * RX_FILTER_CFG: RX configuration register. - */ -#define RX_FILTER_CFG 0x1400 -#define RX_FILTER_CFG_DROP_CRC_ERROR FIELD32(0x00000001) -#define RX_FILTER_CFG_DROP_PHY_ERROR FIELD32(0x00000002) -#define RX_FILTER_CFG_DROP_NOT_TO_ME FIELD32(0x00000004) -#define RX_FILTER_CFG_DROP_NOT_MY_BSSD FIELD32(0x00000008) -#define RX_FILTER_CFG_DROP_VER_ERROR FIELD32(0x00000010) -#define RX_FILTER_CFG_DROP_MULTICAST FIELD32(0x00000020) -#define RX_FILTER_CFG_DROP_BROADCAST FIELD32(0x00000040) -#define RX_FILTER_CFG_DROP_DUPLICATE FIELD32(0x00000080) -#define RX_FILTER_CFG_DROP_CF_END_ACK FIELD32(0x00000100) -#define RX_FILTER_CFG_DROP_CF_END FIELD32(0x00000200) -#define RX_FILTER_CFG_DROP_ACK FIELD32(0x00000400) -#define RX_FILTER_CFG_DROP_CTS FIELD32(0x00000800) -#define RX_FILTER_CFG_DROP_RTS FIELD32(0x00001000) -#define RX_FILTER_CFG_DROP_PSPOLL FIELD32(0x00002000) -#define RX_FILTER_CFG_DROP_BA FIELD32(0x00004000) -#define RX_FILTER_CFG_DROP_BAR FIELD32(0x00008000) -#define RX_FILTER_CFG_DROP_CNTL FIELD32(0x00010000) - -/* - * AUTO_RSP_CFG: - * AUTORESPONDER: 0: disable, 1: enable - * BAC_ACK_POLICY: 0:long, 1:short preamble - * CTS_40_MMODE: Response CTS 40MHz duplicate mode - * CTS_40_MREF: Response CTS 40MHz duplicate mode - * AR_PREAMBLE: Auto responder preamble 0:long, 1:short preamble - * DUAL_CTS_EN: Power bit value in control frame - * ACK_CTS_PSM_BIT:Power bit value in control frame - */ -#define AUTO_RSP_CFG 0x1404 -#define AUTO_RSP_CFG_AUTORESPONDER FIELD32(0x00000001) -#define AUTO_RSP_CFG_BAC_ACK_POLICY FIELD32(0x00000002) -#define AUTO_RSP_CFG_CTS_40_MMODE FIELD32(0x00000004) -#define AUTO_RSP_CFG_CTS_40_MREF FIELD32(0x00000008) -#define AUTO_RSP_CFG_AR_PREAMBLE FIELD32(0x00000010) -#define AUTO_RSP_CFG_DUAL_CTS_EN FIELD32(0x00000040) -#define AUTO_RSP_CFG_ACK_CTS_PSM_BIT FIELD32(0x00000080) - -/* - * LEGACY_BASIC_RATE: - */ -#define LEGACY_BASIC_RATE 0x1408 - -/* - * HT_BASIC_RATE: - */ -#define HT_BASIC_RATE 0x140c - -/* - * HT_CTRL_CFG: - */ -#define HT_CTRL_CFG 0x1410 - -/* - * SIFS_COST_CFG: - */ -#define SIFS_COST_CFG 0x1414 - -/* - * RX_PARSER_CFG: - * Set NAV for all received frames - */ -#define RX_PARSER_CFG 0x1418 - -/* - * TX_SEC_CNT0: - */ -#define TX_SEC_CNT0 0x1500 - -/* - * RX_SEC_CNT0: - */ -#define RX_SEC_CNT0 0x1504 - -/* - * CCMP_FC_MUTE: - */ -#define CCMP_FC_MUTE 0x1508 - -/* - * TXOP_HLDR_ADDR0: - */ -#define TXOP_HLDR_ADDR0 0x1600 - -/* - * TXOP_HLDR_ADDR1: - */ -#define TXOP_HLDR_ADDR1 0x1604 - -/* - * TXOP_HLDR_ET: - */ -#define TXOP_HLDR_ET 0x1608 - -/* - * QOS_CFPOLL_RA_DW0: - */ -#define QOS_CFPOLL_RA_DW0 0x160c - -/* - * QOS_CFPOLL_RA_DW1: - */ -#define QOS_CFPOLL_RA_DW1 0x1610 - -/* - * QOS_CFPOLL_QC: - */ -#define QOS_CFPOLL_QC 0x1614 - -/* - * RX_STA_CNT0: RX PLCP error count & RX CRC error count - */ -#define RX_STA_CNT0 0x1700 -#define RX_STA_CNT0_CRC_ERR FIELD32(0x0000ffff) -#define RX_STA_CNT0_PHY_ERR FIELD32(0xffff0000) - -/* - * RX_STA_CNT1: RX False CCA count & RX LONG frame count - */ -#define RX_STA_CNT1 0x1704 -#define RX_STA_CNT1_FALSE_CCA FIELD32(0x0000ffff) -#define RX_STA_CNT1_PLCP_ERR FIELD32(0xffff0000) - -/* - * RX_STA_CNT2: - */ -#define RX_STA_CNT2 0x1708 -#define RX_STA_CNT2_RX_DUPLI_COUNT FIELD32(0x0000ffff) -#define RX_STA_CNT2_RX_FIFO_OVERFLOW FIELD32(0xffff0000) - -/* - * TX_STA_CNT0: TX Beacon count - */ -#define TX_STA_CNT0 0x170c -#define TX_STA_CNT0_TX_FAIL_COUNT FIELD32(0x0000ffff) -#define TX_STA_CNT0_TX_BEACON_COUNT FIELD32(0xffff0000) - -/* - * TX_STA_CNT1: TX tx count - */ -#define TX_STA_CNT1 0x1710 -#define TX_STA_CNT1_TX_SUCCESS FIELD32(0x0000ffff) -#define TX_STA_CNT1_TX_RETRANSMIT FIELD32(0xffff0000) - -/* - * TX_STA_CNT2: TX tx count - */ -#define TX_STA_CNT2 0x1714 -#define TX_STA_CNT2_TX_ZERO_LEN_COUNT FIELD32(0x0000ffff) -#define TX_STA_CNT2_TX_UNDER_FLOW_COUNT FIELD32(0xffff0000) - -/* - * TX_STA_FIFO: TX Result for specific PID status fifo register. - * - * This register is implemented as FIFO with 16 entries in the HW. Each - * register read fetches the next tx result. If the FIFO is full because - * it wasn't read fast enough after the according interrupt (TX_FIFO_STATUS) - * triggered, the hw seems to simply drop further tx results. - * - * VALID: 1: this tx result is valid - * 0: no valid tx result -> driver should stop reading - * PID_TYPE: The PID latched from the PID field in the TXWI, can be used - * to match a frame with its tx result (even though the PID is - * only 4 bits wide). - * PID_QUEUE: Part of PID_TYPE, this is the queue index number (0-3) - * PID_ENTRY: Part of PID_TYPE, this is the queue entry index number (1-3) - * This identification number is calculated by ((idx % 3) + 1). - * TX_SUCCESS: Indicates tx success (1) or failure (0) - * TX_AGGRE: Indicates if the frame was part of an aggregate (1) or not (0) - * TX_ACK_REQUIRED: Indicates if the frame needed to get ack'ed (1) or not (0) - * WCID: The wireless client ID. - * MCS: The tx rate used during the last transmission of this frame, be it - * successful or not. - * PHYMODE: The phymode used for the transmission. - */ -#define TX_STA_FIFO 0x1718 -#define TX_STA_FIFO_VALID FIELD32(0x00000001) -#define TX_STA_FIFO_PID_TYPE FIELD32(0x0000001e) -#define TX_STA_FIFO_PID_QUEUE FIELD32(0x00000006) -#define TX_STA_FIFO_PID_ENTRY FIELD32(0x00000018) -#define TX_STA_FIFO_TX_SUCCESS FIELD32(0x00000020) -#define TX_STA_FIFO_TX_AGGRE FIELD32(0x00000040) -#define TX_STA_FIFO_TX_ACK_REQUIRED FIELD32(0x00000080) -#define TX_STA_FIFO_WCID FIELD32(0x0000ff00) -#define TX_STA_FIFO_SUCCESS_RATE FIELD32(0xffff0000) -#define TX_STA_FIFO_MCS FIELD32(0x007f0000) -#define TX_STA_FIFO_PHYMODE FIELD32(0xc0000000) - -/* - * TX_AGG_CNT: Debug counter - */ -#define TX_AGG_CNT 0x171c -#define TX_AGG_CNT_NON_AGG_TX_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT_AGG_TX_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT0: - */ -#define TX_AGG_CNT0 0x1720 -#define TX_AGG_CNT0_AGG_SIZE_1_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT0_AGG_SIZE_2_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT1: - */ -#define TX_AGG_CNT1 0x1724 -#define TX_AGG_CNT1_AGG_SIZE_3_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT1_AGG_SIZE_4_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT2: - */ -#define TX_AGG_CNT2 0x1728 -#define TX_AGG_CNT2_AGG_SIZE_5_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT2_AGG_SIZE_6_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT3: - */ -#define TX_AGG_CNT3 0x172c -#define TX_AGG_CNT3_AGG_SIZE_7_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT3_AGG_SIZE_8_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT4: - */ -#define TX_AGG_CNT4 0x1730 -#define TX_AGG_CNT4_AGG_SIZE_9_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT4_AGG_SIZE_10_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT5: - */ -#define TX_AGG_CNT5 0x1734 -#define TX_AGG_CNT5_AGG_SIZE_11_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT5_AGG_SIZE_12_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT6: - */ -#define TX_AGG_CNT6 0x1738 -#define TX_AGG_CNT6_AGG_SIZE_13_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT6_AGG_SIZE_14_COUNT FIELD32(0xffff0000) - -/* - * TX_AGG_CNT7: - */ -#define TX_AGG_CNT7 0x173c -#define TX_AGG_CNT7_AGG_SIZE_15_COUNT FIELD32(0x0000ffff) -#define TX_AGG_CNT7_AGG_SIZE_16_COUNT FIELD32(0xffff0000) - -/* - * MPDU_DENSITY_CNT: - * TX_ZERO_DEL: TX zero length delimiter count - * RX_ZERO_DEL: RX zero length delimiter count - */ -#define MPDU_DENSITY_CNT 0x1740 -#define MPDU_DENSITY_CNT_TX_ZERO_DEL FIELD32(0x0000ffff) -#define MPDU_DENSITY_CNT_RX_ZERO_DEL FIELD32(0xffff0000) - -/* - * Security key table memory. - * - * The pairwise key table shares some memory with the beacon frame - * buffers 6 and 7. That basically means that when beacon 6 & 7 - * are used we should only use the reduced pairwise key table which - * has a maximum of 222 entries. - * - * --------------------------------------------- - * |0x4000 | Pairwise Key | Reduced Pairwise | - * | | Table | Key Table | - * | | Size: 256 * 32 | Size: 222 * 32 | - * |0x5BC0 | |------------------- - * | | | Beacon 6 | - * |0x5DC0 | |------------------- - * | | | Beacon 7 | - * |0x5FC0 | |------------------- - * |0x5FFF | | - * -------------------------- - * - * MAC_WCID_BASE: 8-bytes (use only 6 bytes) * 256 entry - * PAIRWISE_KEY_TABLE_BASE: 32-byte * 256 entry - * MAC_IVEIV_TABLE_BASE: 8-byte * 256-entry - * MAC_WCID_ATTRIBUTE_BASE: 4-byte * 256-entry - * SHARED_KEY_TABLE_BASE: 32-byte * 16-entry - * SHARED_KEY_MODE_BASE: 4-byte * 16-entry - */ -#define MAC_WCID_BASE 0x1800 -#define PAIRWISE_KEY_TABLE_BASE 0x4000 -#define MAC_IVEIV_TABLE_BASE 0x6000 -#define MAC_WCID_ATTRIBUTE_BASE 0x6800 -#define SHARED_KEY_TABLE_BASE 0x6c00 -#define SHARED_KEY_MODE_BASE 0x7000 - -#define MAC_WCID_ENTRY(__idx) \ - (MAC_WCID_BASE + ((__idx) * sizeof(struct mac_wcid_entry))) -#define PAIRWISE_KEY_ENTRY(__idx) \ - (PAIRWISE_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry))) -#define MAC_IVEIV_ENTRY(__idx) \ - (MAC_IVEIV_TABLE_BASE + ((__idx) * sizeof(struct mac_iveiv_entry))) -#define MAC_WCID_ATTR_ENTRY(__idx) \ - (MAC_WCID_ATTRIBUTE_BASE + ((__idx) * sizeof(u32))) -#define SHARED_KEY_ENTRY(__idx) \ - (SHARED_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry))) -#define SHARED_KEY_MODE_ENTRY(__idx) \ - (SHARED_KEY_MODE_BASE + ((__idx) * sizeof(u32))) - -struct mac_wcid_entry { - u8 mac[6]; - u8 reserved[2]; -} __packed; - -struct hw_key_entry { - u8 key[16]; - u8 tx_mic[8]; - u8 rx_mic[8]; -} __packed; - -struct mac_iveiv_entry { - u8 iv[8]; -} __packed; - -/* - * MAC_WCID_ATTRIBUTE: - */ -#define MAC_WCID_ATTRIBUTE_KEYTAB FIELD32(0x00000001) -#define MAC_WCID_ATTRIBUTE_CIPHER FIELD32(0x0000000e) -#define MAC_WCID_ATTRIBUTE_BSS_IDX FIELD32(0x00000070) -#define MAC_WCID_ATTRIBUTE_RX_WIUDF FIELD32(0x00000380) -#define MAC_WCID_ATTRIBUTE_CIPHER_EXT FIELD32(0x00000400) -#define MAC_WCID_ATTRIBUTE_BSS_IDX_EXT FIELD32(0x00000800) -#define MAC_WCID_ATTRIBUTE_WAPI_MCBC FIELD32(0x00008000) -#define MAC_WCID_ATTRIBUTE_WAPI_KEY_IDX FIELD32(0xff000000) - -/* - * SHARED_KEY_MODE: - */ -#define SHARED_KEY_MODE_BSS0_KEY0 FIELD32(0x00000007) -#define SHARED_KEY_MODE_BSS0_KEY1 FIELD32(0x00000070) -#define SHARED_KEY_MODE_BSS0_KEY2 FIELD32(0x00000700) -#define SHARED_KEY_MODE_BSS0_KEY3 FIELD32(0x00007000) -#define SHARED_KEY_MODE_BSS1_KEY0 FIELD32(0x00070000) -#define SHARED_KEY_MODE_BSS1_KEY1 FIELD32(0x00700000) -#define SHARED_KEY_MODE_BSS1_KEY2 FIELD32(0x07000000) -#define SHARED_KEY_MODE_BSS1_KEY3 FIELD32(0x70000000) - -/* - * HOST-MCU communication - */ - -/* - * H2M_MAILBOX_CSR: Host-to-MCU Mailbox. - * CMD_TOKEN: Command id, 0xff disable status reporting. - */ -#define H2M_MAILBOX_CSR 0x7010 -#define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff) -#define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00) -#define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000) -#define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000) - -/* - * H2M_MAILBOX_CID: - * Free slots contain 0xff. MCU will store command's token to lowest free slot. - * If all slots are occupied status will be dropped. - */ -#define H2M_MAILBOX_CID 0x7014 -#define H2M_MAILBOX_CID_CMD0 FIELD32(0x000000ff) -#define H2M_MAILBOX_CID_CMD1 FIELD32(0x0000ff00) -#define H2M_MAILBOX_CID_CMD2 FIELD32(0x00ff0000) -#define H2M_MAILBOX_CID_CMD3 FIELD32(0xff000000) - -/* - * H2M_MAILBOX_STATUS: - * Command status will be saved to same slot as command id. - */ -#define H2M_MAILBOX_STATUS 0x701c - -/* - * H2M_INT_SRC: - */ -#define H2M_INT_SRC 0x7024 - -/* - * H2M_BBP_AGENT: - */ -#define H2M_BBP_AGENT 0x7028 - -/* - * MCU_LEDCS: LED control for MCU Mailbox. - */ -#define MCU_LEDCS_LED_MODE FIELD8(0x1f) -#define MCU_LEDCS_POLARITY FIELD8(0x01) - -/* - * HW_CS_CTS_BASE: - * Carrier-sense CTS frame base address. - * It's where mac stores carrier-sense frame for carrier-sense function. - */ -#define HW_CS_CTS_BASE 0x7700 - -/* - * HW_DFS_CTS_BASE: - * DFS CTS frame base address. It's where mac stores CTS frame for DFS. - */ -#define HW_DFS_CTS_BASE 0x7780 - -/* - * TXRX control registers - base address 0x3000 - */ - -/* - * TXRX_CSR1: - * rt2860b UNKNOWN reg use R/O Reg Addr 0x77d0 first.. - */ -#define TXRX_CSR1 0x77d0 - -/* - * HW_DEBUG_SETTING_BASE: - * since NULL frame won't be that long (256 byte) - * We steal 16 tail bytes to save debugging settings - */ -#define HW_DEBUG_SETTING_BASE 0x77f0 -#define HW_DEBUG_SETTING_BASE2 0x7770 - -/* - * HW_BEACON_BASE - * In order to support maximum 8 MBSS and its maximum length - * is 512 bytes for each beacon - * Three section discontinue memory segments will be used. - * 1. The original region for BCN 0~3 - * 2. Extract memory from FCE table for BCN 4~5 - * 3. Extract memory from Pair-wise key table for BCN 6~7 - * It occupied those memory of wcid 238~253 for BCN 6 - * and wcid 222~237 for BCN 7 (see Security key table memory - * for more info). - * - * IMPORTANT NOTE: Not sure why legacy driver does this, - * but HW_BEACON_BASE7 is 0x0200 bytes below HW_BEACON_BASE6. - */ -#define HW_BEACON_BASE0 0x7800 -#define HW_BEACON_BASE1 0x7a00 -#define HW_BEACON_BASE2 0x7c00 -#define HW_BEACON_BASE3 0x7e00 -#define HW_BEACON_BASE4 0x7200 -#define HW_BEACON_BASE5 0x7400 -#define HW_BEACON_BASE6 0x5dc0 -#define HW_BEACON_BASE7 0x5bc0 - -#define HW_BEACON_BASE(__index) \ - (((__index) < 4) ? (HW_BEACON_BASE0 + (__index * 0x0200)) : \ - (((__index) < 6) ? (HW_BEACON_BASE4 + ((__index - 4) * 0x0200)) : \ - (HW_BEACON_BASE6 - ((__index - 6) * 0x0200)))) - -#define BEACON_BASE_TO_OFFSET(_base) (((_base) - 0x4000) / 64) - -/* - * BBP registers. - * The wordsize of the BBP is 8 bits. - */ - -/* - * BBP 1: TX Antenna & Power Control - * POWER_CTRL: - * 0 - normal, - * 1 - drop tx power by 6dBm, - * 2 - drop tx power by 12dBm, - * 3 - increase tx power by 6dBm - */ -#define BBP1_TX_POWER_CTRL FIELD8(0x03) -#define BBP1_TX_ANTENNA FIELD8(0x18) - -/* - * BBP 3: RX Antenna - */ -#define BBP3_RX_ADC FIELD8(0x03) -#define BBP3_RX_ANTENNA FIELD8(0x18) -#define BBP3_HT40_MINUS FIELD8(0x20) -#define BBP3_ADC_MODE_SWITCH FIELD8(0x40) -#define BBP3_ADC_INIT_MODE FIELD8(0x80) - -/* - * BBP 4: Bandwidth - */ -#define BBP4_TX_BF FIELD8(0x01) -#define BBP4_BANDWIDTH FIELD8(0x18) -#define BBP4_MAC_IF_CTRL FIELD8(0x40) - -/* BBP27 */ -#define BBP27_RX_CHAIN_SEL FIELD8(0x60) - -/* - * BBP 47: Bandwidth - */ -#define BBP47_TSSI_REPORT_SEL FIELD8(0x03) -#define BBP47_TSSI_UPDATE_REQ FIELD8(0x04) -#define BBP47_TSSI_TSSI_MODE FIELD8(0x18) -#define BBP47_TSSI_ADC6 FIELD8(0x80) - -/* - * BBP 49 - */ -#define BBP49_UPDATE_FLAG FIELD8(0x01) - -/* - * BBP 105: - * - bit0: detect SIG on primary channel only (on 40MHz bandwidth) - * - bit1: FEQ (Feed Forward Compensation) for independend streams - * - bit2: MLD (Maximum Likehood Detection) for 2 streams (reserved on single - * stream) - * - bit4: channel estimation updates based on remodulation of - * L-SIG and HT-SIG symbols - */ -#define BBP105_DETECT_SIG_ON_PRIMARY FIELD8(0x01) -#define BBP105_FEQ FIELD8(0x02) -#define BBP105_MLD FIELD8(0x04) -#define BBP105_SIG_REMODULATION FIELD8(0x08) - -/* - * BBP 109 - */ -#define BBP109_TX0_POWER FIELD8(0x0f) -#define BBP109_TX1_POWER FIELD8(0xf0) - -/* BBP 110 */ -#define BBP110_TX2_POWER FIELD8(0x0f) - - -/* - * BBP 138: Unknown - */ -#define BBP138_RX_ADC1 FIELD8(0x02) -#define BBP138_RX_ADC2 FIELD8(0x04) -#define BBP138_TX_DAC1 FIELD8(0x20) -#define BBP138_TX_DAC2 FIELD8(0x40) - -/* - * BBP 152: Rx Ant - */ -#define BBP152_RX_DEFAULT_ANT FIELD8(0x80) - -/* - * BBP 254: unknown - */ -#define BBP254_BIT7 FIELD8(0x80) - -/* - * RFCSR registers - * The wordsize of the RFCSR is 8 bits. - */ - -/* - * RFCSR 1: - */ -#define RFCSR1_RF_BLOCK_EN FIELD8(0x01) -#define RFCSR1_PLL_PD FIELD8(0x02) -#define RFCSR1_RX0_PD FIELD8(0x04) -#define RFCSR1_TX0_PD FIELD8(0x08) -#define RFCSR1_RX1_PD FIELD8(0x10) -#define RFCSR1_TX1_PD FIELD8(0x20) -#define RFCSR1_RX2_PD FIELD8(0x40) -#define RFCSR1_TX2_PD FIELD8(0x80) - -/* - * RFCSR 2: - */ -#define RFCSR2_RESCAL_EN FIELD8(0x80) - -/* - * RFCSR 3: - */ -#define RFCSR3_K FIELD8(0x0f) -/* Bits [7-4] for RF3320 (RT3370/RT3390), on other chipsets reserved */ -#define RFCSR3_PA1_BIAS_CCK FIELD8(0x70) -#define RFCSR3_PA2_CASCODE_BIAS_CCKK FIELD8(0x80) -/* Bits for RF3290/RF5360/RF5362/RF5370/RF5372/RF5390/RF5392 */ -#define RFCSR3_VCOCAL_EN FIELD8(0x80) -/* Bits for RF3050 */ -#define RFCSR3_BIT1 FIELD8(0x02) -#define RFCSR3_BIT2 FIELD8(0x04) -#define RFCSR3_BIT3 FIELD8(0x08) -#define RFCSR3_BIT4 FIELD8(0x10) -#define RFCSR3_BIT5 FIELD8(0x20) - -/* - * FRCSR 5: - */ -#define RFCSR5_R1 FIELD8(0x0c) - -/* - * RFCSR 6: - */ -#define RFCSR6_R1 FIELD8(0x03) -#define RFCSR6_R2 FIELD8(0x40) -#define RFCSR6_TXDIV FIELD8(0x0c) -/* bits for RF3053 */ -#define RFCSR6_VCO_IC FIELD8(0xc0) - -/* - * RFCSR 7: - */ -#define RFCSR7_RF_TUNING FIELD8(0x01) -#define RFCSR7_BIT1 FIELD8(0x02) -#define RFCSR7_BIT2 FIELD8(0x04) -#define RFCSR7_BIT3 FIELD8(0x08) -#define RFCSR7_BIT4 FIELD8(0x10) -#define RFCSR7_BIT5 FIELD8(0x20) -#define RFCSR7_BITS67 FIELD8(0xc0) - -/* - * RFCSR 9: - */ -#define RFCSR9_K FIELD8(0x0f) -#define RFCSR9_N FIELD8(0x10) -#define RFCSR9_UNKNOWN FIELD8(0x60) -#define RFCSR9_MOD FIELD8(0x80) - -/* - * RFCSR 11: - */ -#define RFCSR11_R FIELD8(0x03) -#define RFCSR11_PLL_MOD FIELD8(0x0c) -#define RFCSR11_MOD FIELD8(0xc0) -/* bits for RF3053 */ -/* TODO: verify RFCSR11_MOD usage on other chips */ -#define RFCSR11_PLL_IDOH FIELD8(0x40) - - -/* - * RFCSR 12: - */ -#define RFCSR12_TX_POWER FIELD8(0x1f) -#define RFCSR12_DR0 FIELD8(0xe0) - -/* - * RFCSR 13: - */ -#define RFCSR13_TX_POWER FIELD8(0x1f) -#define RFCSR13_DR0 FIELD8(0xe0) - -/* - * RFCSR 15: - */ -#define RFCSR15_TX_LO2_EN FIELD8(0x08) - -/* - * RFCSR 16: - */ -#define RFCSR16_TXMIXER_GAIN FIELD8(0x07) - -/* - * RFCSR 17: - */ -#define RFCSR17_TXMIXER_GAIN FIELD8(0x07) -#define RFCSR17_TX_LO1_EN FIELD8(0x08) -#define RFCSR17_R FIELD8(0x20) -#define RFCSR17_CODE FIELD8(0x7f) - -/* RFCSR 18 */ -#define RFCSR18_XO_TUNE_BYPASS FIELD8(0x40) - - -/* - * RFCSR 20: - */ -#define RFCSR20_RX_LO1_EN FIELD8(0x08) - -/* - * RFCSR 21: - */ -#define RFCSR21_RX_LO2_EN FIELD8(0x08) - -/* - * RFCSR 22: - */ -#define RFCSR22_BASEBAND_LOOPBACK FIELD8(0x01) - -/* - * RFCSR 23: - */ -#define RFCSR23_FREQ_OFFSET FIELD8(0x7f) - -/* - * RFCSR 24: - */ -#define RFCSR24_TX_AGC_FC FIELD8(0x1f) -#define RFCSR24_TX_H20M FIELD8(0x20) -#define RFCSR24_TX_CALIB FIELD8(0x7f) - -/* - * RFCSR 27: - */ -#define RFCSR27_R1 FIELD8(0x03) -#define RFCSR27_R2 FIELD8(0x04) -#define RFCSR27_R3 FIELD8(0x30) -#define RFCSR27_R4 FIELD8(0x40) - -/* - * RFCSR 29: - */ -#define RFCSR29_ADC6_TEST FIELD8(0x01) -#define RFCSR29_ADC6_INT_TEST FIELD8(0x02) -#define RFCSR29_RSSI_RESET FIELD8(0x04) -#define RFCSR29_RSSI_ON FIELD8(0x08) -#define RFCSR29_RSSI_RIP_CTRL FIELD8(0x30) -#define RFCSR29_RSSI_GAIN FIELD8(0xc0) - -/* - * RFCSR 30: - */ -#define RFCSR30_TX_H20M FIELD8(0x02) -#define RFCSR30_RX_H20M FIELD8(0x04) -#define RFCSR30_RX_VCM FIELD8(0x18) -#define RFCSR30_RF_CALIBRATION FIELD8(0x80) - -/* - * RFCSR 31: - */ -#define RFCSR31_RX_AGC_FC FIELD8(0x1f) -#define RFCSR31_RX_H20M FIELD8(0x20) -#define RFCSR31_RX_CALIB FIELD8(0x7f) - -/* RFCSR 32 bits for RF3053 */ -#define RFCSR32_TX_AGC_FC FIELD8(0xf8) - -/* RFCSR 36 bits for RF3053 */ -#define RFCSR36_RF_BS FIELD8(0x80) - -/* - * RFCSR 38: - */ -#define RFCSR38_RX_LO1_EN FIELD8(0x20) - -/* - * RFCSR 39: - */ -#define RFCSR39_RX_DIV FIELD8(0x40) -#define RFCSR39_RX_LO2_EN FIELD8(0x80) - -/* - * RFCSR 49: - */ -#define RFCSR49_TX FIELD8(0x3f) -#define RFCSR49_EP FIELD8(0xc0) -/* bits for RT3593 */ -#define RFCSR49_TX_LO1_IC FIELD8(0x1c) -#define RFCSR49_TX_DIV FIELD8(0x20) - -/* - * RFCSR 50: - */ -#define RFCSR50_TX FIELD8(0x3f) -#define RFCSR50_EP FIELD8(0xc0) -/* bits for RT3593 */ -#define RFCSR50_TX_LO1_EN FIELD8(0x20) -#define RFCSR50_TX_LO2_EN FIELD8(0x10) - -/* RFCSR 51 */ -/* bits for RT3593 */ -#define RFCSR51_BITS01 FIELD8(0x03) -#define RFCSR51_BITS24 FIELD8(0x1c) -#define RFCSR51_BITS57 FIELD8(0xe0) - -#define RFCSR53_TX_POWER FIELD8(0x3f) -#define RFCSR53_UNKNOWN FIELD8(0xc0) - -#define RFCSR54_TX_POWER FIELD8(0x3f) -#define RFCSR54_UNKNOWN FIELD8(0xc0) - -#define RFCSR55_TX_POWER FIELD8(0x3f) -#define RFCSR55_UNKNOWN FIELD8(0xc0) - -#define RFCSR57_DRV_CC FIELD8(0xfc) - - -/* - * RF registers - */ - -/* - * RF 2 - */ -#define RF2_ANTENNA_RX2 FIELD32(0x00000040) -#define RF2_ANTENNA_TX1 FIELD32(0x00004000) -#define RF2_ANTENNA_RX1 FIELD32(0x00020000) - -/* - * RF 3 - */ -#define RF3_TXPOWER_G FIELD32(0x00003e00) -#define RF3_TXPOWER_A_7DBM_BOOST FIELD32(0x00000200) -#define RF3_TXPOWER_A FIELD32(0x00003c00) - -/* - * RF 4 - */ -#define RF4_TXPOWER_G FIELD32(0x000007c0) -#define RF4_TXPOWER_A_7DBM_BOOST FIELD32(0x00000040) -#define RF4_TXPOWER_A FIELD32(0x00000780) -#define RF4_FREQ_OFFSET FIELD32(0x001f8000) -#define RF4_HT40 FIELD32(0x00200000) - -/* - * EEPROM content. - * The wordsize of the EEPROM is 16 bits. - */ - -enum rt2800_eeprom_word { - EEPROM_CHIP_ID = 0, - EEPROM_VERSION, - EEPROM_MAC_ADDR_0, - EEPROM_MAC_ADDR_1, - EEPROM_MAC_ADDR_2, - EEPROM_NIC_CONF0, - EEPROM_NIC_CONF1, - EEPROM_FREQ, - EEPROM_LED_AG_CONF, - EEPROM_LED_ACT_CONF, - EEPROM_LED_POLARITY, - EEPROM_NIC_CONF2, - EEPROM_LNA, - EEPROM_RSSI_BG, - EEPROM_RSSI_BG2, - EEPROM_TXMIXER_GAIN_BG, - EEPROM_RSSI_A, - EEPROM_RSSI_A2, - EEPROM_TXMIXER_GAIN_A, - EEPROM_EIRP_MAX_TX_POWER, - EEPROM_TXPOWER_DELTA, - EEPROM_TXPOWER_BG1, - EEPROM_TXPOWER_BG2, - EEPROM_TSSI_BOUND_BG1, - EEPROM_TSSI_BOUND_BG2, - EEPROM_TSSI_BOUND_BG3, - EEPROM_TSSI_BOUND_BG4, - EEPROM_TSSI_BOUND_BG5, - EEPROM_TXPOWER_A1, - EEPROM_TXPOWER_A2, - EEPROM_TSSI_BOUND_A1, - EEPROM_TSSI_BOUND_A2, - EEPROM_TSSI_BOUND_A3, - EEPROM_TSSI_BOUND_A4, - EEPROM_TSSI_BOUND_A5, - EEPROM_TXPOWER_BYRATE, - EEPROM_BBP_START, - - /* IDs for extended EEPROM format used by three-chain devices */ - EEPROM_EXT_LNA2, - EEPROM_EXT_TXPOWER_BG3, - EEPROM_EXT_TXPOWER_A3, - - /* New values must be added before this */ - EEPROM_WORD_COUNT -}; - -/* - * EEPROM Version - */ -#define EEPROM_VERSION_FAE FIELD16(0x00ff) -#define EEPROM_VERSION_VERSION FIELD16(0xff00) - -/* - * HW MAC address. - */ -#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) - -/* - * EEPROM NIC Configuration 0 - * RXPATH: 1: 1R, 2: 2R, 3: 3R - * TXPATH: 1: 1T, 2: 2T, 3: 3T - * RF_TYPE: RFIC type - */ -#define EEPROM_NIC_CONF0_RXPATH FIELD16(0x000f) -#define EEPROM_NIC_CONF0_TXPATH FIELD16(0x00f0) -#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00) - -/* - * EEPROM NIC Configuration 1 - * HW_RADIO: 0: disable, 1: enable - * EXTERNAL_TX_ALC: 0: disable, 1: enable - * EXTERNAL_LNA_2G: 0: disable, 1: enable - * EXTERNAL_LNA_5G: 0: disable, 1: enable - * CARDBUS_ACCEL: 0: enable, 1: disable - * BW40M_SB_2G: 0: disable, 1: enable - * BW40M_SB_5G: 0: disable, 1: enable - * WPS_PBC: 0: disable, 1: enable - * BW40M_2G: 0: enable, 1: disable - * BW40M_5G: 0: enable, 1: disable - * BROADBAND_EXT_LNA: 0: disable, 1: enable - * ANT_DIVERSITY: 00: Disable, 01: Diversity, - * 10: Main antenna, 11: Aux antenna - * INTERNAL_TX_ALC: 0: disable, 1: enable - * BT_COEXIST: 0: disable, 1: enable - * DAC_TEST: 0: disable, 1: enable - */ -#define EEPROM_NIC_CONF1_HW_RADIO FIELD16(0x0001) -#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002) -#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004) -#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008) -#define EEPROM_NIC_CONF1_CARDBUS_ACCEL FIELD16(0x0010) -#define EEPROM_NIC_CONF1_BW40M_SB_2G FIELD16(0x0020) -#define EEPROM_NIC_CONF1_BW40M_SB_5G FIELD16(0x0040) -#define EEPROM_NIC_CONF1_WPS_PBC FIELD16(0x0080) -#define EEPROM_NIC_CONF1_BW40M_2G FIELD16(0x0100) -#define EEPROM_NIC_CONF1_BW40M_5G FIELD16(0x0200) -#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400) -#define EEPROM_NIC_CONF1_ANT_DIVERSITY FIELD16(0x1800) -#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000) -#define EEPROM_NIC_CONF1_BT_COEXIST FIELD16(0x4000) -#define EEPROM_NIC_CONF1_DAC_TEST FIELD16(0x8000) - -/* - * EEPROM frequency - */ -#define EEPROM_FREQ_OFFSET FIELD16(0x00ff) -#define EEPROM_FREQ_LED_MODE FIELD16(0x7f00) -#define EEPROM_FREQ_LED_POLARITY FIELD16(0x1000) - -/* - * EEPROM LED - * POLARITY_RDY_G: Polarity RDY_G setting. - * POLARITY_RDY_A: Polarity RDY_A setting. - * POLARITY_ACT: Polarity ACT setting. - * POLARITY_GPIO_0: Polarity GPIO0 setting. - * POLARITY_GPIO_1: Polarity GPIO1 setting. - * POLARITY_GPIO_2: Polarity GPIO2 setting. - * POLARITY_GPIO_3: Polarity GPIO3 setting. - * POLARITY_GPIO_4: Polarity GPIO4 setting. - * LED_MODE: Led mode. - */ -#define EEPROM_LED_POLARITY_RDY_BG FIELD16(0x0001) -#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) -#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) -#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) -#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) -#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) -#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) -#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) -#define EEPROM_LED_LED_MODE FIELD16(0x1f00) - -/* - * EEPROM NIC Configuration 2 - * RX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream - * TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream - * CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved - */ -#define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f) -#define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0) -#define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600) - -/* - * EEPROM LNA - */ -#define EEPROM_LNA_BG FIELD16(0x00ff) -#define EEPROM_LNA_A0 FIELD16(0xff00) - -/* - * EEPROM RSSI BG offset - */ -#define EEPROM_RSSI_BG_OFFSET0 FIELD16(0x00ff) -#define EEPROM_RSSI_BG_OFFSET1 FIELD16(0xff00) - -/* - * EEPROM RSSI BG2 offset - */ -#define EEPROM_RSSI_BG2_OFFSET2 FIELD16(0x00ff) -#define EEPROM_RSSI_BG2_LNA_A1 FIELD16(0xff00) - -/* - * EEPROM TXMIXER GAIN BG offset (note overlaps with EEPROM RSSI BG2). - */ -#define EEPROM_TXMIXER_GAIN_BG_VAL FIELD16(0x0007) - -/* - * EEPROM RSSI A offset - */ -#define EEPROM_RSSI_A_OFFSET0 FIELD16(0x00ff) -#define EEPROM_RSSI_A_OFFSET1 FIELD16(0xff00) - -/* - * EEPROM RSSI A2 offset - */ -#define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff) -#define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00) - -/* - * EEPROM TXMIXER GAIN A offset (note overlaps with EEPROM RSSI A2). - */ -#define EEPROM_TXMIXER_GAIN_A_VAL FIELD16(0x0007) - -/* - * EEPROM EIRP Maximum TX power values(unit: dbm) - */ -#define EEPROM_EIRP_MAX_TX_POWER_2GHZ FIELD16(0x00ff) -#define EEPROM_EIRP_MAX_TX_POWER_5GHZ FIELD16(0xff00) - -/* - * EEPROM TXpower delta: 20MHZ AND 40 MHZ use different power. - * This is delta in 40MHZ. - * VALUE: Tx Power dalta value, MAX=4(unit: dbm) - * TYPE: 1: Plus the delta value, 0: minus the delta value - * ENABLE: enable tx power compensation for 40BW - */ -#define EEPROM_TXPOWER_DELTA_VALUE_2G FIELD16(0x003f) -#define EEPROM_TXPOWER_DELTA_TYPE_2G FIELD16(0x0040) -#define EEPROM_TXPOWER_DELTA_ENABLE_2G FIELD16(0x0080) -#define EEPROM_TXPOWER_DELTA_VALUE_5G FIELD16(0x3f00) -#define EEPROM_TXPOWER_DELTA_TYPE_5G FIELD16(0x4000) -#define EEPROM_TXPOWER_DELTA_ENABLE_5G FIELD16(0x8000) - -/* - * EEPROM TXPOWER 802.11BG - */ -#define EEPROM_TXPOWER_BG_SIZE 7 -#define EEPROM_TXPOWER_BG_1 FIELD16(0x00ff) -#define EEPROM_TXPOWER_BG_2 FIELD16(0xff00) - -/* - * EEPROM temperature compensation boundaries 802.11BG - * MINUS4: If the actual TSSI is below this boundary, tx power needs to be - * reduced by (agc_step * -4) - * MINUS3: If the actual TSSI is below this boundary, tx power needs to be - * reduced by (agc_step * -3) - */ -#define EEPROM_TSSI_BOUND_BG1_MINUS4 FIELD16(0x00ff) -#define EEPROM_TSSI_BOUND_BG1_MINUS3 FIELD16(0xff00) - -/* - * EEPROM temperature compensation boundaries 802.11BG - * MINUS2: If the actual TSSI is below this boundary, tx power needs to be - * reduced by (agc_step * -2) - * MINUS1: If the actual TSSI is below this boundary, tx power needs to be - * reduced by (agc_step * -1) - */ -#define EEPROM_TSSI_BOUND_BG2_MINUS2 FIELD16(0x00ff) -#define EEPROM_TSSI_BOUND_BG2_MINUS1 FIELD16(0xff00) - -/* - * EEPROM temperature compensation boundaries 802.11BG - * REF: Reference TSSI value, no tx power changes needed - * PLUS1: If the actual TSSI is above this boundary, tx power needs to be - * increased by (agc_step * 1) - */ -#define EEPROM_TSSI_BOUND_BG3_REF FIELD16(0x00ff) -#define EEPROM_TSSI_BOUND_BG3_PLUS1 FIELD16(0xff00) - -/* - * EEPROM temperature compensation boundaries 802.11BG - * PLUS2: If the actual TSSI is above this boundary, tx power needs to be - * increased by (agc_step * 2) - * PLUS3: If the actual TSSI is above this boundary, tx power needs to be - * increased by (agc_step * 3) - */ -#define EEPROM_TSSI_BOUND_BG4_PLUS2 FIELD16(0x00ff) -#define EEPROM_TSSI_BOUND_BG4_PLUS3 FIELD16(0xff00) - -/* - * EEPROM temperature compensation boundaries 802.11BG - * PLUS4: If the actual TSSI is above this boundary, tx power needs to be - * increased by (agc_step * 4) - * AGC_STEP: Temperature compensation step. - */ -#define EEPROM_TSSI_BOUND_BG5_PLUS4 FIELD16(0x00ff) -#define EEPROM_TSSI_BOUND_BG5_AGC_STEP FIELD16(0xff00) - -/* - * EEPROM TXPOWER 802.11A - */ -#define EEPROM_TXPOWER_A_SIZE 6 -#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) -#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) - -/* EEPROM_TXPOWER_{A,G} fields for RT3593 */ -#define EEPROM_TXPOWER_ALC FIELD8(0x1f) -#define EEPROM_TXPOWER_FINE_CTRL FIELD8(0xe0) - -/* - * EEPROM temperature compensation boundaries 802.11A - * MINUS4: If the actual TSSI is below this boundary, tx power needs to be - * reduced by (agc_step * -4) - * MINUS3: If the actual TSSI is below this boundary, tx power needs to be - * reduced by (agc_step * -3) - */ -#define EEPROM_TSSI_BOUND_A1_MINUS4 FIELD16(0x00ff) -#define EEPROM_TSSI_BOUND_A1_MINUS3 FIELD16(0xff00) - -/* - * EEPROM temperature compensation boundaries 802.11A - * MINUS2: If the actual TSSI is below this boundary, tx power needs to be - * reduced by (agc_step * -2) - * MINUS1: If the actual TSSI is below this boundary, tx power needs to be - * reduced by (agc_step * -1) - */ -#define EEPROM_TSSI_BOUND_A2_MINUS2 FIELD16(0x00ff) -#define EEPROM_TSSI_BOUND_A2_MINUS1 FIELD16(0xff00) - -/* - * EEPROM temperature compensation boundaries 802.11A - * REF: Reference TSSI value, no tx power changes needed - * PLUS1: If the actual TSSI is above this boundary, tx power needs to be - * increased by (agc_step * 1) - */ -#define EEPROM_TSSI_BOUND_A3_REF FIELD16(0x00ff) -#define EEPROM_TSSI_BOUND_A3_PLUS1 FIELD16(0xff00) - -/* - * EEPROM temperature compensation boundaries 802.11A - * PLUS2: If the actual TSSI is above this boundary, tx power needs to be - * increased by (agc_step * 2) - * PLUS3: If the actual TSSI is above this boundary, tx power needs to be - * increased by (agc_step * 3) - */ -#define EEPROM_TSSI_BOUND_A4_PLUS2 FIELD16(0x00ff) -#define EEPROM_TSSI_BOUND_A4_PLUS3 FIELD16(0xff00) - -/* - * EEPROM temperature compensation boundaries 802.11A - * PLUS4: If the actual TSSI is above this boundary, tx power needs to be - * increased by (agc_step * 4) - * AGC_STEP: Temperature compensation step. - */ -#define EEPROM_TSSI_BOUND_A5_PLUS4 FIELD16(0x00ff) -#define EEPROM_TSSI_BOUND_A5_AGC_STEP FIELD16(0xff00) - -/* - * EEPROM TXPOWER by rate: tx power per tx rate for HT20 mode - */ -#define EEPROM_TXPOWER_BYRATE_SIZE 9 - -#define EEPROM_TXPOWER_BYRATE_RATE0 FIELD16(0x000f) -#define EEPROM_TXPOWER_BYRATE_RATE1 FIELD16(0x00f0) -#define EEPROM_TXPOWER_BYRATE_RATE2 FIELD16(0x0f00) -#define EEPROM_TXPOWER_BYRATE_RATE3 FIELD16(0xf000) - -/* - * EEPROM BBP. - */ -#define EEPROM_BBP_SIZE 16 -#define EEPROM_BBP_VALUE FIELD16(0x00ff) -#define EEPROM_BBP_REG_ID FIELD16(0xff00) - -/* EEPROM_EXT_LNA2 */ -#define EEPROM_EXT_LNA2_A1 FIELD16(0x00ff) -#define EEPROM_EXT_LNA2_A2 FIELD16(0xff00) - -/* - * EEPROM IQ Calibration, unlike other entries those are byte addresses. - */ - -#define EEPROM_IQ_GAIN_CAL_TX0_2G 0x130 -#define EEPROM_IQ_PHASE_CAL_TX0_2G 0x131 -#define EEPROM_IQ_GROUPDELAY_CAL_TX0_2G 0x132 -#define EEPROM_IQ_GAIN_CAL_TX1_2G 0x133 -#define EEPROM_IQ_PHASE_CAL_TX1_2G 0x134 -#define EEPROM_IQ_GROUPDELAY_CAL_TX1_2G 0x135 -#define EEPROM_IQ_GAIN_CAL_RX0_2G 0x136 -#define EEPROM_IQ_PHASE_CAL_RX0_2G 0x137 -#define EEPROM_IQ_GROUPDELAY_CAL_RX0_2G 0x138 -#define EEPROM_IQ_GAIN_CAL_RX1_2G 0x139 -#define EEPROM_IQ_PHASE_CAL_RX1_2G 0x13A -#define EEPROM_IQ_GROUPDELAY_CAL_RX1_2G 0x13B -#define EEPROM_RF_IQ_COMPENSATION_CONTROL 0x13C -#define EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CONTROL 0x13D -#define EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5G 0x144 -#define EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5G 0x145 -#define EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5G 0X146 -#define EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5G 0x147 -#define EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5G 0x148 -#define EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5G 0x149 -#define EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5G 0x14A -#define EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5G 0x14B -#define EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5G 0X14C -#define EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5G 0x14D -#define EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5G 0x14E -#define EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5G 0x14F -#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH36_TO_CH64_5G 0x150 -#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH36_TO_CH64_5G 0x151 -#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH100_TO_CH138_5G 0x152 -#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH100_TO_CH138_5G 0x153 -#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH140_TO_CH165_5G 0x154 -#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH140_TO_CH165_5G 0x155 -#define EEPROM_IQ_GAIN_CAL_RX0_CH36_TO_CH64_5G 0x156 -#define EEPROM_IQ_PHASE_CAL_RX0_CH36_TO_CH64_5G 0x157 -#define EEPROM_IQ_GAIN_CAL_RX0_CH100_TO_CH138_5G 0X158 -#define EEPROM_IQ_PHASE_CAL_RX0_CH100_TO_CH138_5G 0x159 -#define EEPROM_IQ_GAIN_CAL_RX0_CH140_TO_CH165_5G 0x15A -#define EEPROM_IQ_PHASE_CAL_RX0_CH140_TO_CH165_5G 0x15B -#define EEPROM_IQ_GAIN_CAL_RX1_CH36_TO_CH64_5G 0x15C -#define EEPROM_IQ_PHASE_CAL_RX1_CH36_TO_CH64_5G 0x15D -#define EEPROM_IQ_GAIN_CAL_RX1_CH100_TO_CH138_5G 0X15E -#define EEPROM_IQ_PHASE_CAL_RX1_CH100_TO_CH138_5G 0x15F -#define EEPROM_IQ_GAIN_CAL_RX1_CH140_TO_CH165_5G 0x160 -#define EEPROM_IQ_PHASE_CAL_RX1_CH140_TO_CH165_5G 0x161 -#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH36_TO_CH64_5G 0x162 -#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH36_TO_CH64_5G 0x163 -#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH100_TO_CH138_5G 0x164 -#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH100_TO_CH138_5G 0x165 -#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH140_TO_CH165_5G 0x166 -#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH140_TO_CH165_5G 0x167 - -/* - * MCU mailbox commands. - * MCU_SLEEP - go to power-save mode. - * arg1: 1: save as much power as possible, 0: save less power. - * status: 1: success, 2: already asleep, - * 3: maybe MAC is busy so can't finish this task. - * MCU_RADIO_OFF - * arg0: 0: do power-saving, NOT turn off radio. - */ -#define MCU_SLEEP 0x30 -#define MCU_WAKEUP 0x31 -#define MCU_RADIO_OFF 0x35 -#define MCU_CURRENT 0x36 -#define MCU_LED 0x50 -#define MCU_LED_STRENGTH 0x51 -#define MCU_LED_AG_CONF 0x52 -#define MCU_LED_ACT_CONF 0x53 -#define MCU_LED_LED_POLARITY 0x54 -#define MCU_RADAR 0x60 -#define MCU_BOOT_SIGNAL 0x72 -#define MCU_ANT_SELECT 0X73 -#define MCU_FREQ_OFFSET 0x74 -#define MCU_BBP_SIGNAL 0x80 -#define MCU_POWER_SAVE 0x83 -#define MCU_BAND_SELECT 0x91 - -/* - * MCU mailbox tokens - */ -#define TOKEN_SLEEP 1 -#define TOKEN_RADIO_OFF 2 -#define TOKEN_WAKEUP 3 - - -/* - * DMA descriptor defines. - */ - -#define TXWI_DESC_SIZE_4WORDS (4 * sizeof(__le32)) -#define TXWI_DESC_SIZE_5WORDS (5 * sizeof(__le32)) - -#define RXWI_DESC_SIZE_4WORDS (4 * sizeof(__le32)) -#define RXWI_DESC_SIZE_5WORDS (5 * sizeof(__le32)) -#define RXWI_DESC_SIZE_6WORDS (6 * sizeof(__le32)) - -/* - * TX WI structure - */ - -/* - * Word0 - * FRAG: 1 To inform TKIP engine this is a fragment. - * MIMO_PS: The remote peer is in dynamic MIMO-PS mode - * TX_OP: 0:HT TXOP rule , 1:PIFS TX ,2:Backoff, 3:sifs - * BW: Channel bandwidth 0:20MHz, 1:40 MHz (for legacy rates this will - * duplicate the frame to both channels). - * STBC: 1: STBC support MCS =0-7, 2,3 : RESERVED - * AMPDU: 1: this frame is eligible for AMPDU aggregation, the hw will - * aggregate consecutive frames with the same RA and QoS TID. If - * a frame A with the same RA and QoS TID but AMPDU=0 is queued - * directly after a frame B with AMPDU=1, frame A might still - * get aggregated into the AMPDU started by frame B. So, setting - * AMPDU to 0 does _not_ necessarily mean the frame is sent as - * MPDU, it can still end up in an AMPDU if the previous frame - * was tagged as AMPDU. - */ -#define TXWI_W0_FRAG FIELD32(0x00000001) -#define TXWI_W0_MIMO_PS FIELD32(0x00000002) -#define TXWI_W0_CF_ACK FIELD32(0x00000004) -#define TXWI_W0_TS FIELD32(0x00000008) -#define TXWI_W0_AMPDU FIELD32(0x00000010) -#define TXWI_W0_MPDU_DENSITY FIELD32(0x000000e0) -#define TXWI_W0_TX_OP FIELD32(0x00000300) -#define TXWI_W0_MCS FIELD32(0x007f0000) -#define TXWI_W0_BW FIELD32(0x00800000) -#define TXWI_W0_SHORT_GI FIELD32(0x01000000) -#define TXWI_W0_STBC FIELD32(0x06000000) -#define TXWI_W0_IFS FIELD32(0x08000000) -#define TXWI_W0_PHYMODE FIELD32(0xc0000000) - -/* - * Word1 - * ACK: 0: No Ack needed, 1: Ack needed - * NSEQ: 0: Don't assign hw sequence number, 1: Assign hw sequence number - * BW_WIN_SIZE: BA windows size of the recipient - * WIRELESS_CLI_ID: Client ID for WCID table access - * MPDU_TOTAL_BYTE_COUNT: Length of 802.11 frame - * PACKETID: Will be latched into the TX_STA_FIFO register once the according - * frame was processed. If multiple frames are aggregated together - * (AMPDU==1) the reported tx status will always contain the packet - * id of the first frame. 0: Don't report tx status for this frame. - * PACKETID_QUEUE: Part of PACKETID, This is the queue index (0-3) - * PACKETID_ENTRY: Part of PACKETID, THis is the queue entry index (1-3) - * This identification number is calculated by ((idx % 3) + 1). - * The (+1) is required to prevent PACKETID to become 0. - */ -#define TXWI_W1_ACK FIELD32(0x00000001) -#define TXWI_W1_NSEQ FIELD32(0x00000002) -#define TXWI_W1_BW_WIN_SIZE FIELD32(0x000000fc) -#define TXWI_W1_WIRELESS_CLI_ID FIELD32(0x0000ff00) -#define TXWI_W1_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) -#define TXWI_W1_PACKETID FIELD32(0xf0000000) -#define TXWI_W1_PACKETID_QUEUE FIELD32(0x30000000) -#define TXWI_W1_PACKETID_ENTRY FIELD32(0xc0000000) - -/* - * Word2 - */ -#define TXWI_W2_IV FIELD32(0xffffffff) - -/* - * Word3 - */ -#define TXWI_W3_EIV FIELD32(0xffffffff) - -/* - * RX WI structure - */ - -/* - * Word0 - */ -#define RXWI_W0_WIRELESS_CLI_ID FIELD32(0x000000ff) -#define RXWI_W0_KEY_INDEX FIELD32(0x00000300) -#define RXWI_W0_BSSID FIELD32(0x00001c00) -#define RXWI_W0_UDF FIELD32(0x0000e000) -#define RXWI_W0_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) -#define RXWI_W0_TID FIELD32(0xf0000000) - -/* - * Word1 - */ -#define RXWI_W1_FRAG FIELD32(0x0000000f) -#define RXWI_W1_SEQUENCE FIELD32(0x0000fff0) -#define RXWI_W1_MCS FIELD32(0x007f0000) -#define RXWI_W1_BW FIELD32(0x00800000) -#define RXWI_W1_SHORT_GI FIELD32(0x01000000) -#define RXWI_W1_STBC FIELD32(0x06000000) -#define RXWI_W1_PHYMODE FIELD32(0xc0000000) - -/* - * Word2 - */ -#define RXWI_W2_RSSI0 FIELD32(0x000000ff) -#define RXWI_W2_RSSI1 FIELD32(0x0000ff00) -#define RXWI_W2_RSSI2 FIELD32(0x00ff0000) - -/* - * Word3 - */ -#define RXWI_W3_SNR0 FIELD32(0x000000ff) -#define RXWI_W3_SNR1 FIELD32(0x0000ff00) - -/* - * Macros for converting txpower from EEPROM to mac80211 value - * and from mac80211 value to register value. - */ -#define MIN_G_TXPOWER 0 -#define MIN_A_TXPOWER -7 -#define MAX_G_TXPOWER 31 -#define MAX_A_TXPOWER 15 -#define DEFAULT_TXPOWER 5 - -#define MIN_A_TXPOWER_3593 0 -#define MAX_A_TXPOWER_3593 31 - -#define TXPOWER_G_FROM_DEV(__txpower) \ - ((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) - -#define TXPOWER_A_FROM_DEV(__txpower) \ - ((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) - -/* - * Board's maximun TX power limitation - */ -#define EIRP_MAX_TX_POWER_LIMIT 0x50 - -/* - * Number of TBTT intervals after which we have to adjust - * the hw beacon timer. - */ -#define BCN_TBTT_OFFSET 64 - -/* - * Hardware has 255 WCID table entries. First 32 entries are reserved for - * shared keys. Since parts of the pairwise key table might be shared with - * the beacon frame buffers 6 & 7 we could only use the first 222 entries. - */ -#define WCID_START 33 -#define WCID_END 222 -#define STA_IDS_SIZE (WCID_END - WCID_START + 2) - -/* - * RT2800 driver data structure - */ -struct rt2800_drv_data { - u8 calibration_bw20; - u8 calibration_bw40; - u8 bbp25; - u8 bbp26; - u8 txmixer_gain_24g; - u8 txmixer_gain_5g; - unsigned int tbtt_tick; - DECLARE_BITMAP(sta_ids, STA_IDS_SIZE); -}; - -#endif /* RT2800_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c deleted file mode 100644 index 9733b31a780d..000000000000 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ /dev/null @@ -1,8021 +0,0 @@ -/* - Copyright (C) 2010 Willow Garage - Copyright (C) 2010 Ivo van Doorn - Copyright (C) 2009 Bartlomiej Zolnierkiewicz - Copyright (C) 2009 Gertjan van Wingerde - - Based on the original rt2800pci.c and rt2800usb.c. - Copyright (C) 2009 Alban Browaeys - Copyright (C) 2009 Felix Fietkau - Copyright (C) 2009 Luis Correia - Copyright (C) 2009 Mattias Nissler - Copyright (C) 2009 Mark Asselstine - Copyright (C) 2009 Xose Vazquez Perez - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2800lib - Abstract: rt2800 generic device routines. - */ - -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2800lib.h" -#include "rt2800.h" - -/* - * Register access. - * All access to the CSR registers will go through the methods - * rt2800_register_read and rt2800_register_write. - * BBP and RF register require indirect register access, - * and use the CSR registers BBPCSR and RFCSR to achieve this. - * These indirect registers work with busy bits, - * and we will try maximal REGISTER_BUSY_COUNT times to access - * the register while taking a REGISTER_BUSY_DELAY us delay - * between each attampt. When the busy bit is still set at that time, - * the access attempt is considered to have failed, - * and we will print an error. - * The _lock versions must be used if you already hold the csr_mutex - */ -#define WAIT_FOR_BBP(__dev, __reg) \ - rt2800_regbusy_read((__dev), BBP_CSR_CFG, BBP_CSR_CFG_BUSY, (__reg)) -#define WAIT_FOR_RFCSR(__dev, __reg) \ - rt2800_regbusy_read((__dev), RF_CSR_CFG, RF_CSR_CFG_BUSY, (__reg)) -#define WAIT_FOR_RF(__dev, __reg) \ - rt2800_regbusy_read((__dev), RF_CSR_CFG0, RF_CSR_CFG0_BUSY, (__reg)) -#define WAIT_FOR_MCU(__dev, __reg) \ - rt2800_regbusy_read((__dev), H2M_MAILBOX_CSR, \ - H2M_MAILBOX_CSR_OWNER, (__reg)) - -static inline bool rt2800_is_305x_soc(struct rt2x00_dev *rt2x00dev) -{ - /* check for rt2872 on SoC */ - if (!rt2x00_is_soc(rt2x00dev) || - !rt2x00_rt(rt2x00dev, RT2872)) - return false; - - /* we know for sure that these rf chipsets are used on rt305x boards */ - if (rt2x00_rf(rt2x00dev, RF3020) || - rt2x00_rf(rt2x00dev, RF3021) || - rt2x00_rf(rt2x00dev, RF3022)) - return true; - - rt2x00_warn(rt2x00dev, "Unknown RF chipset on rt305x\n"); - return false; -} - -static void rt2800_bbp_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u8 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, BBP_CSR_CFG_VALUE, value); - rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); - rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); - rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 0); - rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); - - rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2800_bbp_read(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u8 *value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the read request into the register. - * After the data has been written, we wait until hardware - * returns the correct value, if at any time the register - * doesn't become available in time, reg will be 0xffffffff - * which means we return 0xff to the caller. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); - rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); - rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 1); - rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); - - rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); - - WAIT_FOR_BBP(rt2x00dev, ®); - } - - *value = rt2x00_get_field32(reg, BBP_CSR_CFG_VALUE); - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2800_rfcsr_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u8 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the RFCSR becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, RF_CSR_CFG_DATA, value); - rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); - rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 1); - rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); - - rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2800_rfcsr_read(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u8 *value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the RFCSR becomes available, afterwards we - * can safely write the read request into the register. - * After the data has been written, we wait until hardware - * returns the correct value, if at any time the register - * doesn't become available in time, reg will be 0xffffffff - * which means we return 0xff to the caller. - */ - if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); - rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 0); - rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); - - rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); - - WAIT_FOR_RFCSR(rt2x00dev, ®); - } - - *value = rt2x00_get_field32(reg, RF_CSR_CFG_DATA); - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u32 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the RF becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_RF(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, RF_CSR_CFG0_REG_VALUE_BW, value); - rt2x00_set_field32(®, RF_CSR_CFG0_STANDBYMODE, 0); - rt2x00_set_field32(®, RF_CSR_CFG0_SEL, 0); - rt2x00_set_field32(®, RF_CSR_CFG0_BUSY, 1); - - rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG0, reg); - rt2x00_rf_write(rt2x00dev, word, value); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = { - [EEPROM_CHIP_ID] = 0x0000, - [EEPROM_VERSION] = 0x0001, - [EEPROM_MAC_ADDR_0] = 0x0002, - [EEPROM_MAC_ADDR_1] = 0x0003, - [EEPROM_MAC_ADDR_2] = 0x0004, - [EEPROM_NIC_CONF0] = 0x001a, - [EEPROM_NIC_CONF1] = 0x001b, - [EEPROM_FREQ] = 0x001d, - [EEPROM_LED_AG_CONF] = 0x001e, - [EEPROM_LED_ACT_CONF] = 0x001f, - [EEPROM_LED_POLARITY] = 0x0020, - [EEPROM_NIC_CONF2] = 0x0021, - [EEPROM_LNA] = 0x0022, - [EEPROM_RSSI_BG] = 0x0023, - [EEPROM_RSSI_BG2] = 0x0024, - [EEPROM_TXMIXER_GAIN_BG] = 0x0024, /* overlaps with RSSI_BG2 */ - [EEPROM_RSSI_A] = 0x0025, - [EEPROM_RSSI_A2] = 0x0026, - [EEPROM_TXMIXER_GAIN_A] = 0x0026, /* overlaps with RSSI_A2 */ - [EEPROM_EIRP_MAX_TX_POWER] = 0x0027, - [EEPROM_TXPOWER_DELTA] = 0x0028, - [EEPROM_TXPOWER_BG1] = 0x0029, - [EEPROM_TXPOWER_BG2] = 0x0030, - [EEPROM_TSSI_BOUND_BG1] = 0x0037, - [EEPROM_TSSI_BOUND_BG2] = 0x0038, - [EEPROM_TSSI_BOUND_BG3] = 0x0039, - [EEPROM_TSSI_BOUND_BG4] = 0x003a, - [EEPROM_TSSI_BOUND_BG5] = 0x003b, - [EEPROM_TXPOWER_A1] = 0x003c, - [EEPROM_TXPOWER_A2] = 0x0053, - [EEPROM_TSSI_BOUND_A1] = 0x006a, - [EEPROM_TSSI_BOUND_A2] = 0x006b, - [EEPROM_TSSI_BOUND_A3] = 0x006c, - [EEPROM_TSSI_BOUND_A4] = 0x006d, - [EEPROM_TSSI_BOUND_A5] = 0x006e, - [EEPROM_TXPOWER_BYRATE] = 0x006f, - [EEPROM_BBP_START] = 0x0078, -}; - -static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = { - [EEPROM_CHIP_ID] = 0x0000, - [EEPROM_VERSION] = 0x0001, - [EEPROM_MAC_ADDR_0] = 0x0002, - [EEPROM_MAC_ADDR_1] = 0x0003, - [EEPROM_MAC_ADDR_2] = 0x0004, - [EEPROM_NIC_CONF0] = 0x001a, - [EEPROM_NIC_CONF1] = 0x001b, - [EEPROM_NIC_CONF2] = 0x001c, - [EEPROM_EIRP_MAX_TX_POWER] = 0x0020, - [EEPROM_FREQ] = 0x0022, - [EEPROM_LED_AG_CONF] = 0x0023, - [EEPROM_LED_ACT_CONF] = 0x0024, - [EEPROM_LED_POLARITY] = 0x0025, - [EEPROM_LNA] = 0x0026, - [EEPROM_EXT_LNA2] = 0x0027, - [EEPROM_RSSI_BG] = 0x0028, - [EEPROM_RSSI_BG2] = 0x0029, - [EEPROM_RSSI_A] = 0x002a, - [EEPROM_RSSI_A2] = 0x002b, - [EEPROM_TXPOWER_BG1] = 0x0030, - [EEPROM_TXPOWER_BG2] = 0x0037, - [EEPROM_EXT_TXPOWER_BG3] = 0x003e, - [EEPROM_TSSI_BOUND_BG1] = 0x0045, - [EEPROM_TSSI_BOUND_BG2] = 0x0046, - [EEPROM_TSSI_BOUND_BG3] = 0x0047, - [EEPROM_TSSI_BOUND_BG4] = 0x0048, - [EEPROM_TSSI_BOUND_BG5] = 0x0049, - [EEPROM_TXPOWER_A1] = 0x004b, - [EEPROM_TXPOWER_A2] = 0x0065, - [EEPROM_EXT_TXPOWER_A3] = 0x007f, - [EEPROM_TSSI_BOUND_A1] = 0x009a, - [EEPROM_TSSI_BOUND_A2] = 0x009b, - [EEPROM_TSSI_BOUND_A3] = 0x009c, - [EEPROM_TSSI_BOUND_A4] = 0x009d, - [EEPROM_TSSI_BOUND_A5] = 0x009e, - [EEPROM_TXPOWER_BYRATE] = 0x00a0, -}; - -static unsigned int rt2800_eeprom_word_index(struct rt2x00_dev *rt2x00dev, - const enum rt2800_eeprom_word word) -{ - const unsigned int *map; - unsigned int index; - - if (WARN_ONCE(word >= EEPROM_WORD_COUNT, - "%s: invalid EEPROM word %d\n", - wiphy_name(rt2x00dev->hw->wiphy), word)) - return 0; - - if (rt2x00_rt(rt2x00dev, RT3593)) - map = rt2800_eeprom_map_ext; - else - map = rt2800_eeprom_map; - - index = map[word]; - - /* Index 0 is valid only for EEPROM_CHIP_ID. - * Otherwise it means that the offset of the - * given word is not initialized in the map, - * or that the field is not usable on the - * actual chipset. - */ - WARN_ONCE(word != EEPROM_CHIP_ID && index == 0, - "%s: invalid access of EEPROM word %d\n", - wiphy_name(rt2x00dev->hw->wiphy), word); - - return index; -} - -static void *rt2800_eeprom_addr(struct rt2x00_dev *rt2x00dev, - const enum rt2800_eeprom_word word) -{ - unsigned int index; - - index = rt2800_eeprom_word_index(rt2x00dev, word); - return rt2x00_eeprom_addr(rt2x00dev, index); -} - -static void rt2800_eeprom_read(struct rt2x00_dev *rt2x00dev, - const enum rt2800_eeprom_word word, u16 *data) -{ - unsigned int index; - - index = rt2800_eeprom_word_index(rt2x00dev, word); - rt2x00_eeprom_read(rt2x00dev, index, data); -} - -static void rt2800_eeprom_write(struct rt2x00_dev *rt2x00dev, - const enum rt2800_eeprom_word word, u16 data) -{ - unsigned int index; - - index = rt2800_eeprom_word_index(rt2x00dev, word); - rt2x00_eeprom_write(rt2x00dev, index, data); -} - -static void rt2800_eeprom_read_from_array(struct rt2x00_dev *rt2x00dev, - const enum rt2800_eeprom_word array, - unsigned int offset, - u16 *data) -{ - unsigned int index; - - index = rt2800_eeprom_word_index(rt2x00dev, array); - rt2x00_eeprom_read(rt2x00dev, index + offset, data); -} - -static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - int i, count; - - rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); - if (rt2x00_get_field32(reg, WLAN_EN)) - return 0; - - rt2x00_set_field32(®, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff); - rt2x00_set_field32(®, FRC_WL_ANT_SET, 1); - rt2x00_set_field32(®, WLAN_CLK_EN, 0); - rt2x00_set_field32(®, WLAN_EN, 1); - rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); - - udelay(REGISTER_BUSY_DELAY); - - count = 0; - do { - /* - * Check PLL_LD & XTAL_RDY. - */ - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2800_register_read(rt2x00dev, CMB_CTRL, ®); - if (rt2x00_get_field32(reg, PLL_LD) && - rt2x00_get_field32(reg, XTAL_RDY)) - break; - udelay(REGISTER_BUSY_DELAY); - } - - if (i >= REGISTER_BUSY_COUNT) { - - if (count >= 10) - return -EIO; - - rt2800_register_write(rt2x00dev, 0x58, 0x018); - udelay(REGISTER_BUSY_DELAY); - rt2800_register_write(rt2x00dev, 0x58, 0x418); - udelay(REGISTER_BUSY_DELAY); - rt2800_register_write(rt2x00dev, 0x58, 0x618); - udelay(REGISTER_BUSY_DELAY); - count++; - } else { - count = 0; - } - - rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); - rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 0); - rt2x00_set_field32(®, WLAN_CLK_EN, 1); - rt2x00_set_field32(®, WLAN_RESET, 1); - rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); - udelay(10); - rt2x00_set_field32(®, WLAN_RESET, 0); - rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); - udelay(10); - rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff); - } while (count != 0); - - return 0; -} - -void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, - const u8 command, const u8 token, - const u8 arg0, const u8 arg1) -{ - u32 reg; - - /* - * SOC devices don't support MCU requests. - */ - if (rt2x00_is_soc(rt2x00dev)) - return; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the MCU becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_MCU(rt2x00dev, ®)) { - rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); - rt2800_register_write_lock(rt2x00dev, H2M_MAILBOX_CSR, reg); - - reg = 0; - rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); - rt2800_register_write_lock(rt2x00dev, HOST_CMD_CSR, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} -EXPORT_SYMBOL_GPL(rt2800_mcu_request); - -int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i = 0; - u32 reg; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2800_register_read(rt2x00dev, MAC_CSR0, ®); - if (reg && reg != ~0) - return 0; - msleep(1); - } - - rt2x00_err(rt2x00dev, "Unstable hardware\n"); - return -EBUSY; -} -EXPORT_SYMBOL_GPL(rt2800_wait_csr_ready); - -int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u32 reg; - - /* - * Some devices are really slow to respond here. Wait a whole second - * before timing out. - */ - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); - if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && - !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) - return 0; - - msleep(10); - } - - rt2x00_err(rt2x00dev, "WPDMA TX/RX busy [0x%08x]\n", reg); - return -EACCES; -} -EXPORT_SYMBOL_GPL(rt2800_wait_wpdma_ready); - -void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); - rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); - rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); -} -EXPORT_SYMBOL_GPL(rt2800_disable_wpdma); - -void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev, - unsigned short *txwi_size, - unsigned short *rxwi_size) -{ - switch (rt2x00dev->chip.rt) { - case RT3593: - *txwi_size = TXWI_DESC_SIZE_4WORDS; - *rxwi_size = RXWI_DESC_SIZE_5WORDS; - break; - - case RT5592: - *txwi_size = TXWI_DESC_SIZE_5WORDS; - *rxwi_size = RXWI_DESC_SIZE_6WORDS; - break; - - default: - *txwi_size = TXWI_DESC_SIZE_4WORDS; - *rxwi_size = RXWI_DESC_SIZE_4WORDS; - break; - } -} -EXPORT_SYMBOL_GPL(rt2800_get_txwi_rxwi_size); - -static bool rt2800_check_firmware_crc(const u8 *data, const size_t len) -{ - u16 fw_crc; - u16 crc; - - /* - * The last 2 bytes in the firmware array are the crc checksum itself, - * this means that we should never pass those 2 bytes to the crc - * algorithm. - */ - fw_crc = (data[len - 2] << 8 | data[len - 1]); - - /* - * Use the crc ccitt algorithm. - * This will return the same value as the legacy driver which - * used bit ordering reversion on the both the firmware bytes - * before input input as well as on the final output. - * Obviously using crc ccitt directly is much more efficient. - */ - crc = crc_ccitt(~0, data, len - 2); - - /* - * There is a small difference between the crc-itu-t + bitrev and - * the crc-ccitt crc calculation. In the latter method the 2 bytes - * will be swapped, use swab16 to convert the crc to the correct - * value. - */ - crc = swab16(crc); - - return fw_crc == crc; -} - -int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - size_t offset = 0; - size_t fw_len; - bool multiple; - - /* - * PCI(e) & SOC devices require firmware with a length - * of 8kb. USB devices require firmware files with a length - * of 4kb. Certain USB chipsets however require different firmware, - * which Ralink only provides attached to the original firmware - * file. Thus for USB devices, firmware files have a length - * which is a multiple of 4kb. The firmware for rt3290 chip also - * have a length which is a multiple of 4kb. - */ - if (rt2x00_is_usb(rt2x00dev) || rt2x00_rt(rt2x00dev, RT3290)) - fw_len = 4096; - else - fw_len = 8192; - - multiple = true; - /* - * Validate the firmware length - */ - if (len != fw_len && (!multiple || (len % fw_len) != 0)) - return FW_BAD_LENGTH; - - /* - * Check if the chipset requires one of the upper parts - * of the firmware. - */ - if (rt2x00_is_usb(rt2x00dev) && - !rt2x00_rt(rt2x00dev, RT2860) && - !rt2x00_rt(rt2x00dev, RT2872) && - !rt2x00_rt(rt2x00dev, RT3070) && - ((len / fw_len) == 1)) - return FW_BAD_VERSION; - - /* - * 8kb firmware files must be checked as if it were - * 2 separate firmware files. - */ - while (offset < len) { - if (!rt2800_check_firmware_crc(data + offset, fw_len)) - return FW_BAD_CRC; - - offset += fw_len; - } - - return FW_OK; -} -EXPORT_SYMBOL_GPL(rt2800_check_firmware); - -int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - unsigned int i; - u32 reg; - int retval; - - if (rt2x00_rt(rt2x00dev, RT3290)) { - retval = rt2800_enable_wlan_rt3290(rt2x00dev); - if (retval) - return -EBUSY; - } - - /* - * If driver doesn't wake up firmware here, - * rt2800_load_firmware will hang forever when interface is up again. - */ - rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000); - - /* - * Wait for stable hardware. - */ - if (rt2800_wait_csr_ready(rt2x00dev)) - return -EBUSY; - - if (rt2x00_is_pci(rt2x00dev)) { - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_register_read(rt2x00dev, AUX_CTRL, ®); - rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); - rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); - rt2800_register_write(rt2x00dev, AUX_CTRL, reg); - } - rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002); - } - - rt2800_disable_wpdma(rt2x00dev); - - /* - * Write firmware to the device. - */ - rt2800_drv_write_firmware(rt2x00dev, data, len); - - /* - * Wait for device to stabilize. - */ - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, ®); - if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY)) - break; - msleep(1); - } - - if (i == REGISTER_BUSY_COUNT) { - rt2x00_err(rt2x00dev, "PBF system register not ready\n"); - return -EBUSY; - } - - /* - * Disable DMA, will be reenabled later when enabling - * the radio. - */ - rt2800_disable_wpdma(rt2x00dev); - - /* - * Initialize firmware. - */ - rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); - rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); - if (rt2x00_is_usb(rt2x00dev)) { - rt2800_register_write(rt2x00dev, H2M_INT_SRC, 0); - rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0); - } - msleep(1); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2800_load_firmware); - -void rt2800_write_tx_data(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - __le32 *txwi = rt2800_drv_get_txwi(entry); - u32 word; - int i; - - /* - * Initialize TX Info descriptor - */ - rt2x00_desc_read(txwi, 0, &word); - rt2x00_set_field32(&word, TXWI_W0_FRAG, - test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); - rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, - test_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags)); - rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0); - rt2x00_set_field32(&word, TXWI_W0_TS, - test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); - rt2x00_set_field32(&word, TXWI_W0_AMPDU, - test_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags)); - rt2x00_set_field32(&word, TXWI_W0_MPDU_DENSITY, - txdesc->u.ht.mpdu_density); - rt2x00_set_field32(&word, TXWI_W0_TX_OP, txdesc->u.ht.txop); - rt2x00_set_field32(&word, TXWI_W0_MCS, txdesc->u.ht.mcs); - rt2x00_set_field32(&word, TXWI_W0_BW, - test_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags)); - rt2x00_set_field32(&word, TXWI_W0_SHORT_GI, - test_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags)); - rt2x00_set_field32(&word, TXWI_W0_STBC, txdesc->u.ht.stbc); - rt2x00_set_field32(&word, TXWI_W0_PHYMODE, txdesc->rate_mode); - rt2x00_desc_write(txwi, 0, word); - - rt2x00_desc_read(txwi, 1, &word); - rt2x00_set_field32(&word, TXWI_W1_ACK, - test_bit(ENTRY_TXD_ACK, &txdesc->flags)); - rt2x00_set_field32(&word, TXWI_W1_NSEQ, - test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); - rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->u.ht.ba_size); - rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID, - test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ? - txdesc->key_idx : txdesc->u.ht.wcid); - rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT, - txdesc->length); - rt2x00_set_field32(&word, TXWI_W1_PACKETID_QUEUE, entry->queue->qid); - rt2x00_set_field32(&word, TXWI_W1_PACKETID_ENTRY, (entry->entry_idx % 3) + 1); - rt2x00_desc_write(txwi, 1, word); - - /* - * Always write 0 to IV/EIV fields (word 2 and 3), hardware will insert - * the IV from the IVEIV register when TXD_W3_WIV is set to 0. - * When TXD_W3_WIV is set to 1 it will use the IV data - * from the descriptor. The TXWI_W1_WIRELESS_CLI_ID indicates which - * crypto entry in the registers should be used to encrypt the frame. - * - * Nulify all remaining words as well, we don't know how to program them. - */ - for (i = 2; i < entry->queue->winfo_size / sizeof(__le32); i++) - _rt2x00_desc_write(txwi, i, 0); -} -EXPORT_SYMBOL_GPL(rt2800_write_tx_data); - -static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2) -{ - s8 rssi0 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI0); - s8 rssi1 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI1); - s8 rssi2 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI2); - u16 eeprom; - u8 offset0; - u8 offset1; - u8 offset2; - - if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { - rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom); - offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0); - offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1); - rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); - offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_OFFSET2); - } else { - rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom); - offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET0); - offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET1); - rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); - offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_OFFSET2); - } - - /* - * Convert the value from the descriptor into the RSSI value - * If the value in the descriptor is 0, it is considered invalid - * and the default (extremely low) rssi value is assumed - */ - rssi0 = (rssi0) ? (-12 - offset0 - rt2x00dev->lna_gain - rssi0) : -128; - rssi1 = (rssi1) ? (-12 - offset1 - rt2x00dev->lna_gain - rssi1) : -128; - rssi2 = (rssi2) ? (-12 - offset2 - rt2x00dev->lna_gain - rssi2) : -128; - - /* - * mac80211 only accepts a single RSSI value. Calculating the - * average doesn't deliver a fair answer either since -60:-60 would - * be considered equally good as -50:-70 while the second is the one - * which gives less energy... - */ - rssi0 = max(rssi0, rssi1); - return (int)max(rssi0, rssi2); -} - -void rt2800_process_rxwi(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc) -{ - __le32 *rxwi = (__le32 *) entry->skb->data; - u32 word; - - rt2x00_desc_read(rxwi, 0, &word); - - rxdesc->cipher = rt2x00_get_field32(word, RXWI_W0_UDF); - rxdesc->size = rt2x00_get_field32(word, RXWI_W0_MPDU_TOTAL_BYTE_COUNT); - - rt2x00_desc_read(rxwi, 1, &word); - - if (rt2x00_get_field32(word, RXWI_W1_SHORT_GI)) - rxdesc->flags |= RX_FLAG_SHORT_GI; - - if (rt2x00_get_field32(word, RXWI_W1_BW)) - rxdesc->flags |= RX_FLAG_40MHZ; - - /* - * Detect RX rate, always use MCS as signal type. - */ - rxdesc->dev_flags |= RXDONE_SIGNAL_MCS; - rxdesc->signal = rt2x00_get_field32(word, RXWI_W1_MCS); - rxdesc->rate_mode = rt2x00_get_field32(word, RXWI_W1_PHYMODE); - - /* - * Mask of 0x8 bit to remove the short preamble flag. - */ - if (rxdesc->rate_mode == RATE_MODE_CCK) - rxdesc->signal &= ~0x8; - - rt2x00_desc_read(rxwi, 2, &word); - - /* - * Convert descriptor AGC value to RSSI value. - */ - rxdesc->rssi = rt2800_agc_to_rssi(entry->queue->rt2x00dev, word); - /* - * Remove RXWI descriptor from start of the buffer. - */ - skb_pull(entry->skb, entry->queue->winfo_size); -} -EXPORT_SYMBOL_GPL(rt2800_process_rxwi); - -void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - struct txdone_entry_desc txdesc; - u32 word; - u16 mcs, real_mcs; - int aggr, ampdu; - - /* - * Obtain the status about this packet. - */ - txdesc.flags = 0; - rt2x00_desc_read(txwi, 0, &word); - - mcs = rt2x00_get_field32(word, TXWI_W0_MCS); - ampdu = rt2x00_get_field32(word, TXWI_W0_AMPDU); - - real_mcs = rt2x00_get_field32(status, TX_STA_FIFO_MCS); - aggr = rt2x00_get_field32(status, TX_STA_FIFO_TX_AGGRE); - - /* - * If a frame was meant to be sent as a single non-aggregated MPDU - * but ended up in an aggregate the used tx rate doesn't correlate - * with the one specified in the TXWI as the whole aggregate is sent - * with the same rate. - * - * For example: two frames are sent to rt2x00, the first one sets - * AMPDU=1 and requests MCS7 whereas the second frame sets AMDPU=0 - * and requests MCS15. If the hw aggregates both frames into one - * AMDPU the tx status for both frames will contain MCS7 although - * the frame was sent successfully. - * - * Hence, replace the requested rate with the real tx rate to not - * confuse the rate control algortihm by providing clearly wrong - * data. - */ - if (unlikely(aggr == 1 && ampdu == 0 && real_mcs != mcs)) { - skbdesc->tx_rate_idx = real_mcs; - mcs = real_mcs; - } - - if (aggr == 1 || ampdu == 1) - __set_bit(TXDONE_AMPDU, &txdesc.flags); - - /* - * Ralink has a retry mechanism using a global fallback - * table. We setup this fallback table to try the immediate - * lower rate for all rates. In the TX_STA_FIFO, the MCS field - * always contains the MCS used for the last transmission, be - * it successful or not. - */ - if (rt2x00_get_field32(status, TX_STA_FIFO_TX_SUCCESS)) { - /* - * Transmission succeeded. The number of retries is - * mcs - real_mcs - */ - __set_bit(TXDONE_SUCCESS, &txdesc.flags); - txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0); - } else { - /* - * Transmission failed. The number of retries is - * always 7 in this case (for a total number of 8 - * frames sent). - */ - __set_bit(TXDONE_FAILURE, &txdesc.flags); - txdesc.retry = rt2x00dev->long_retry; - } - - /* - * the frame was retried at least once - * -> hw used fallback rates - */ - if (txdesc.retry) - __set_bit(TXDONE_FALLBACK, &txdesc.flags); - - rt2x00lib_txdone(entry, &txdesc); -} -EXPORT_SYMBOL_GPL(rt2800_txdone_entry); - -static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev, - unsigned int index) -{ - return HW_BEACON_BASE(index); -} - -static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev, - unsigned int index) -{ - return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index)); -} - -static void rt2800_update_beacons_setup(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue = rt2x00dev->bcn; - struct queue_entry *entry; - int i, bcn_num = 0; - u64 off, reg = 0; - u32 bssid_dw1; - - /* - * Setup offsets of all active beacons in BCN_OFFSET{0,1} registers. - */ - for (i = 0; i < queue->limit; i++) { - entry = &queue->entries[i]; - if (!test_bit(ENTRY_BCN_ENABLED, &entry->flags)) - continue; - off = rt2800_get_beacon_offset(rt2x00dev, entry->entry_idx); - reg |= off << (8 * bcn_num); - bcn_num++; - } - - WARN_ON_ONCE(bcn_num != rt2x00dev->intf_beaconing); - - rt2800_register_write(rt2x00dev, BCN_OFFSET0, (u32) reg); - rt2800_register_write(rt2x00dev, BCN_OFFSET1, (u32) (reg >> 32)); - - /* - * H/W sends up to MAC_BSSID_DW1_BSS_BCN_NUM + 1 consecutive beacons. - */ - rt2800_register_read(rt2x00dev, MAC_BSSID_DW1, &bssid_dw1); - rt2x00_set_field32(&bssid_dw1, MAC_BSSID_DW1_BSS_BCN_NUM, - bcn_num > 0 ? bcn_num - 1 : 0); - rt2800_register_write(rt2x00dev, MAC_BSSID_DW1, bssid_dw1); -} - -void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - unsigned int beacon_base; - unsigned int padding_len; - u32 orig_reg, reg; - const int txwi_desc_size = entry->queue->winfo_size; - - /* - * Disable beaconing while we are reloading the beacon data, - * otherwise we might be sending out invalid data. - */ - rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); - orig_reg = reg; - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); - rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); - - /* - * Add space for the TXWI in front of the skb. - */ - memset(skb_push(entry->skb, txwi_desc_size), 0, txwi_desc_size); - - /* - * Register descriptor details in skb frame descriptor. - */ - skbdesc->flags |= SKBDESC_DESC_IN_SKB; - skbdesc->desc = entry->skb->data; - skbdesc->desc_len = txwi_desc_size; - - /* - * Add the TXWI for the beacon to the skb. - */ - rt2800_write_tx_data(entry, txdesc); - - /* - * Dump beacon to userspace through debugfs. - */ - rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); - - /* - * Write entire beacon with TXWI and padding to register. - */ - padding_len = roundup(entry->skb->len, 4) - entry->skb->len; - if (padding_len && skb_pad(entry->skb, padding_len)) { - rt2x00_err(rt2x00dev, "Failure padding beacon, aborting\n"); - /* skb freed by skb_pad() on failure */ - entry->skb = NULL; - rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); - return; - } - - beacon_base = rt2800_hw_beacon_base(rt2x00dev, entry->entry_idx); - - rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data, - entry->skb->len + padding_len); - __set_bit(ENTRY_BCN_ENABLED, &entry->flags); - - /* - * Change global beacons settings. - */ - rt2800_update_beacons_setup(rt2x00dev); - - /* - * Restore beaconing state. - */ - rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); - - /* - * Clean up beacon skb. - */ - dev_kfree_skb_any(entry->skb); - entry->skb = NULL; -} -EXPORT_SYMBOL_GPL(rt2800_write_beacon); - -static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev, - unsigned int index) -{ - int i; - const int txwi_desc_size = rt2x00dev->bcn->winfo_size; - unsigned int beacon_base; - - beacon_base = rt2800_hw_beacon_base(rt2x00dev, index); - - /* - * For the Beacon base registers we only need to clear - * the whole TXWI which (when set to 0) will invalidate - * the entire beacon. - */ - for (i = 0; i < txwi_desc_size; i += sizeof(__le32)) - rt2800_register_write(rt2x00dev, beacon_base + i, 0); -} - -void rt2800_clear_beacon(struct queue_entry *entry) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - u32 orig_reg, reg; - - /* - * Disable beaconing while we are reloading the beacon data, - * otherwise we might be sending out invalid data. - */ - rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &orig_reg); - reg = orig_reg; - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); - rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); - - /* - * Clear beacon. - */ - rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx); - __clear_bit(ENTRY_BCN_ENABLED, &entry->flags); - - /* - * Change global beacons settings. - */ - rt2800_update_beacons_setup(rt2x00dev); - /* - * Restore beaconing state. - */ - rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); -} -EXPORT_SYMBOL_GPL(rt2800_clear_beacon); - -#ifdef CONFIG_RT2X00_LIB_DEBUGFS -const struct rt2x00debug rt2800_rt2x00debug = { - .owner = THIS_MODULE, - .csr = { - .read = rt2800_register_read, - .write = rt2800_register_write, - .flags = RT2X00DEBUGFS_OFFSET, - .word_base = CSR_REG_BASE, - .word_size = sizeof(u32), - .word_count = CSR_REG_SIZE / sizeof(u32), - }, - .eeprom = { - /* NOTE: The local EEPROM access functions can't - * be used here, use the generic versions instead. - */ - .read = rt2x00_eeprom_read, - .write = rt2x00_eeprom_write, - .word_base = EEPROM_BASE, - .word_size = sizeof(u16), - .word_count = EEPROM_SIZE / sizeof(u16), - }, - .bbp = { - .read = rt2800_bbp_read, - .write = rt2800_bbp_write, - .word_base = BBP_BASE, - .word_size = sizeof(u8), - .word_count = BBP_SIZE / sizeof(u8), - }, - .rf = { - .read = rt2x00_rf_read, - .write = rt2800_rf_write, - .word_base = RF_BASE, - .word_size = sizeof(u32), - .word_count = RF_SIZE / sizeof(u32), - }, - .rfcsr = { - .read = rt2800_rfcsr_read, - .write = rt2800_rfcsr_write, - .word_base = RFCSR_BASE, - .word_size = sizeof(u8), - .word_count = RFCSR_SIZE / sizeof(u8), - }, -}; -EXPORT_SYMBOL_GPL(rt2800_rt2x00debug); -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ - -int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - if (rt2x00_rt(rt2x00dev, RT3290)) { - rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); - return rt2x00_get_field32(reg, WLAN_GPIO_IN_BIT0); - } else { - rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); - return rt2x00_get_field32(reg, GPIO_CTRL_VAL2); - } -} -EXPORT_SYMBOL_GPL(rt2800_rfkill_poll); - -#ifdef CONFIG_RT2X00_LIB_LEDS -static void rt2800_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - unsigned int enabled = brightness != LED_OFF; - unsigned int bg_mode = - (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); - unsigned int polarity = - rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, - EEPROM_FREQ_LED_POLARITY); - unsigned int ledmode = - rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, - EEPROM_FREQ_LED_MODE); - u32 reg; - - /* Check for SoC (SOC devices don't support MCU requests) */ - if (rt2x00_is_soc(led->rt2x00dev)) { - rt2800_register_read(led->rt2x00dev, LED_CFG, ®); - - /* Set LED Polarity */ - rt2x00_set_field32(®, LED_CFG_LED_POLAR, polarity); - - /* Set LED Mode */ - if (led->type == LED_TYPE_RADIO) { - rt2x00_set_field32(®, LED_CFG_G_LED_MODE, - enabled ? 3 : 0); - } else if (led->type == LED_TYPE_ASSOC) { - rt2x00_set_field32(®, LED_CFG_Y_LED_MODE, - enabled ? 3 : 0); - } else if (led->type == LED_TYPE_QUALITY) { - rt2x00_set_field32(®, LED_CFG_R_LED_MODE, - enabled ? 3 : 0); - } - - rt2800_register_write(led->rt2x00dev, LED_CFG, reg); - - } else { - if (led->type == LED_TYPE_RADIO) { - rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, - enabled ? 0x20 : 0); - } else if (led->type == LED_TYPE_ASSOC) { - rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, - enabled ? (bg_mode ? 0x60 : 0xa0) : 0x20); - } else if (led->type == LED_TYPE_QUALITY) { - /* - * The brightness is divided into 6 levels (0 - 5), - * The specs tell us the following levels: - * 0, 1 ,3, 7, 15, 31 - * to determine the level in a simple way we can simply - * work with bitshifting: - * (1 << level) - 1 - */ - rt2800_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff, - (1 << brightness / (LED_FULL / 6)) - 1, - polarity); - } - } -} - -static void rt2800_init_led(struct rt2x00_dev *rt2x00dev, - struct rt2x00_led *led, enum led_type type) -{ - led->rt2x00dev = rt2x00dev; - led->type = type; - led->led_dev.brightness_set = rt2800_brightness_set; - led->flags = LED_INITIALIZED; -} -#endif /* CONFIG_RT2X00_LIB_LEDS */ - -/* - * Configuration handlers. - */ -static void rt2800_config_wcid(struct rt2x00_dev *rt2x00dev, - const u8 *address, - int wcid) -{ - struct mac_wcid_entry wcid_entry; - u32 offset; - - offset = MAC_WCID_ENTRY(wcid); - - memset(&wcid_entry, 0xff, sizeof(wcid_entry)); - if (address) - memcpy(wcid_entry.mac, address, ETH_ALEN); - - rt2800_register_multiwrite(rt2x00dev, offset, - &wcid_entry, sizeof(wcid_entry)); -} - -static void rt2800_delete_wcid_attr(struct rt2x00_dev *rt2x00dev, int wcid) -{ - u32 offset; - offset = MAC_WCID_ATTR_ENTRY(wcid); - rt2800_register_write(rt2x00dev, offset, 0); -} - -static void rt2800_config_wcid_attr_bssidx(struct rt2x00_dev *rt2x00dev, - int wcid, u32 bssidx) -{ - u32 offset = MAC_WCID_ATTR_ENTRY(wcid); - u32 reg; - - /* - * The BSS Idx numbers is split in a main value of 3 bits, - * and a extended field for adding one additional bit to the value. - */ - rt2800_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_BSS_IDX, (bssidx & 0x7)); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_BSS_IDX_EXT, - (bssidx & 0x8) >> 3); - rt2800_register_write(rt2x00dev, offset, reg); -} - -static void rt2800_config_wcid_attr_cipher(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key) -{ - struct mac_iveiv_entry iveiv_entry; - u32 offset; - u32 reg; - - offset = MAC_WCID_ATTR_ENTRY(key->hw_key_idx); - - if (crypto->cmd == SET_KEY) { - rt2800_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_KEYTAB, - !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)); - /* - * Both the cipher as the BSS Idx numbers are split in a main - * value of 3 bits, and a extended field for adding one additional - * bit to the value. - */ - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER, - (crypto->cipher & 0x7)); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER_EXT, - (crypto->cipher & 0x8) >> 3); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_RX_WIUDF, crypto->cipher); - rt2800_register_write(rt2x00dev, offset, reg); - } else { - /* Delete the cipher without touching the bssidx */ - rt2800_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_KEYTAB, 0); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER, 0); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER_EXT, 0); - rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_RX_WIUDF, 0); - rt2800_register_write(rt2x00dev, offset, reg); - } - - offset = MAC_IVEIV_ENTRY(key->hw_key_idx); - - memset(&iveiv_entry, 0, sizeof(iveiv_entry)); - if ((crypto->cipher == CIPHER_TKIP) || - (crypto->cipher == CIPHER_TKIP_NO_MIC) || - (crypto->cipher == CIPHER_AES)) - iveiv_entry.iv[3] |= 0x20; - iveiv_entry.iv[3] |= key->keyidx << 6; - rt2800_register_multiwrite(rt2x00dev, offset, - &iveiv_entry, sizeof(iveiv_entry)); -} - -int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key) -{ - struct hw_key_entry key_entry; - struct rt2x00_field32 field; - u32 offset; - u32 reg; - - if (crypto->cmd == SET_KEY) { - key->hw_key_idx = (4 * crypto->bssidx) + key->keyidx; - - memcpy(key_entry.key, crypto->key, - sizeof(key_entry.key)); - memcpy(key_entry.tx_mic, crypto->tx_mic, - sizeof(key_entry.tx_mic)); - memcpy(key_entry.rx_mic, crypto->rx_mic, - sizeof(key_entry.rx_mic)); - - offset = SHARED_KEY_ENTRY(key->hw_key_idx); - rt2800_register_multiwrite(rt2x00dev, offset, - &key_entry, sizeof(key_entry)); - } - - /* - * The cipher types are stored over multiple registers - * starting with SHARED_KEY_MODE_BASE each word will have - * 32 bits and contains the cipher types for 2 bssidx each. - * Using the correct defines correctly will cause overhead, - * so just calculate the correct offset. - */ - field.bit_offset = 4 * (key->hw_key_idx % 8); - field.bit_mask = 0x7 << field.bit_offset; - - offset = SHARED_KEY_MODE_ENTRY(key->hw_key_idx / 8); - - rt2800_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, field, - (crypto->cmd == SET_KEY) * crypto->cipher); - rt2800_register_write(rt2x00dev, offset, reg); - - /* - * Update WCID information - */ - rt2800_config_wcid(rt2x00dev, crypto->address, key->hw_key_idx); - rt2800_config_wcid_attr_bssidx(rt2x00dev, key->hw_key_idx, - crypto->bssidx); - rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2800_config_shared_key); - -int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key) -{ - struct hw_key_entry key_entry; - u32 offset; - - if (crypto->cmd == SET_KEY) { - /* - * Allow key configuration only for STAs that are - * known by the hw. - */ - if (crypto->wcid > WCID_END) - return -ENOSPC; - key->hw_key_idx = crypto->wcid; - - memcpy(key_entry.key, crypto->key, - sizeof(key_entry.key)); - memcpy(key_entry.tx_mic, crypto->tx_mic, - sizeof(key_entry.tx_mic)); - memcpy(key_entry.rx_mic, crypto->rx_mic, - sizeof(key_entry.rx_mic)); - - offset = PAIRWISE_KEY_ENTRY(key->hw_key_idx); - rt2800_register_multiwrite(rt2x00dev, offset, - &key_entry, sizeof(key_entry)); - } - - /* - * Update WCID information - */ - rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2800_config_pairwise_key); - -int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - int wcid; - struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta); - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - - /* - * Search for the first free WCID entry and return the corresponding - * index. - */ - wcid = find_first_zero_bit(drv_data->sta_ids, STA_IDS_SIZE) + WCID_START; - - /* - * Store selected wcid even if it is invalid so that we can - * later decide if the STA is uploaded into the hw. - */ - sta_priv->wcid = wcid; - - /* - * No space left in the device, however, we can still communicate - * with the STA -> No error. - */ - if (wcid > WCID_END) - return 0; - - __set_bit(wcid - WCID_START, drv_data->sta_ids); - - /* - * Clean up WCID attributes and write STA address to the device. - */ - rt2800_delete_wcid_attr(rt2x00dev, wcid); - rt2800_config_wcid(rt2x00dev, sta->addr, wcid); - rt2800_config_wcid_attr_bssidx(rt2x00dev, wcid, - rt2x00lib_get_bssidx(rt2x00dev, vif)); - return 0; -} -EXPORT_SYMBOL_GPL(rt2800_sta_add); - -int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, int wcid) -{ - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - - if (wcid > WCID_END) - return 0; - /* - * Remove WCID entry, no need to clean the attributes as they will - * get renewed when the WCID is reused. - */ - rt2800_config_wcid(rt2x00dev, NULL, wcid); - __clear_bit(wcid - WCID_START, drv_data->sta_ids); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2800_sta_remove); - -void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, - const unsigned int filter_flags) -{ - u32 reg; - - /* - * Start configuration steps. - * Note that the version error will always be dropped - * and broadcast frames will always be accepted since - * there is no filter for it at this time. - */ - rt2800_register_read(rt2x00dev, RX_FILTER_CFG, ®); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CRC_ERROR, - !(filter_flags & FIF_FCSFAIL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PHY_ERROR, - !(filter_flags & FIF_PLCPFAIL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_TO_ME, 1); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_MY_BSSD, 0); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_VER_ERROR, 1); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_MULTICAST, - !(filter_flags & FIF_ALLMULTI)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BROADCAST, 0); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_DUPLICATE, 1); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END_ACK, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_ACK, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CTS, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_RTS, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PSPOLL, - !(filter_flags & FIF_PSPOLL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BA, 0); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BAR, - !(filter_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CNTL, - !(filter_flags & FIF_CONTROL)); - rt2800_register_write(rt2x00dev, RX_FILTER_CFG, reg); -} -EXPORT_SYMBOL_GPL(rt2800_config_filter); - -void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, - struct rt2x00intf_conf *conf, const unsigned int flags) -{ - u32 reg; - bool update_bssid = false; - - if (flags & CONFIG_UPDATE_TYPE) { - /* - * Enable synchronisation. - */ - rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, conf->sync); - rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); - - if (conf->sync == TSF_SYNC_AP_NONE) { - /* - * Tune beacon queue transmit parameters for AP mode - */ - rt2800_register_read(rt2x00dev, TBTT_SYNC_CFG, ®); - rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_CWMIN, 0); - rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_AIFSN, 1); - rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_EXP_WIN, 32); - rt2x00_set_field32(®, TBTT_SYNC_CFG_TBTT_ADJUST, 0); - rt2800_register_write(rt2x00dev, TBTT_SYNC_CFG, reg); - } else { - rt2800_register_read(rt2x00dev, TBTT_SYNC_CFG, ®); - rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_CWMIN, 4); - rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_AIFSN, 2); - rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_EXP_WIN, 32); - rt2x00_set_field32(®, TBTT_SYNC_CFG_TBTT_ADJUST, 16); - rt2800_register_write(rt2x00dev, TBTT_SYNC_CFG, reg); - } - } - - if (flags & CONFIG_UPDATE_MAC) { - if (flags & CONFIG_UPDATE_TYPE && - conf->sync == TSF_SYNC_AP_NONE) { - /* - * The BSSID register has to be set to our own mac - * address in AP mode. - */ - memcpy(conf->bssid, conf->mac, sizeof(conf->mac)); - update_bssid = true; - } - - if (!is_zero_ether_addr((const u8 *)conf->mac)) { - reg = le32_to_cpu(conf->mac[1]); - rt2x00_set_field32(®, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff); - conf->mac[1] = cpu_to_le32(reg); - } - - rt2800_register_multiwrite(rt2x00dev, MAC_ADDR_DW0, - conf->mac, sizeof(conf->mac)); - } - - if ((flags & CONFIG_UPDATE_BSSID) || update_bssid) { - if (!is_zero_ether_addr((const u8 *)conf->bssid)) { - reg = le32_to_cpu(conf->bssid[1]); - rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_ID_MASK, 3); - rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_BCN_NUM, 0); - conf->bssid[1] = cpu_to_le32(reg); - } - - rt2800_register_multiwrite(rt2x00dev, MAC_BSSID_DW0, - conf->bssid, sizeof(conf->bssid)); - } -} -EXPORT_SYMBOL_GPL(rt2800_config_intf); - -static void rt2800_config_ht_opmode(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_erp *erp) -{ - bool any_sta_nongf = !!(erp->ht_opmode & - IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - u8 protection = erp->ht_opmode & IEEE80211_HT_OP_MODE_PROTECTION; - u8 mm20_mode, mm40_mode, gf20_mode, gf40_mode; - u16 mm20_rate, mm40_rate, gf20_rate, gf40_rate; - u32 reg; - - /* default protection rate for HT20: OFDM 24M */ - mm20_rate = gf20_rate = 0x4004; - - /* default protection rate for HT40: duplicate OFDM 24M */ - mm40_rate = gf40_rate = 0x4084; - - switch (protection) { - case IEEE80211_HT_OP_MODE_PROTECTION_NONE: - /* - * All STAs in this BSS are HT20/40 but there might be - * STAs not supporting greenfield mode. - * => Disable protection for HT transmissions. - */ - mm20_mode = mm40_mode = gf20_mode = gf40_mode = 0; - - break; - case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: - /* - * All STAs in this BSS are HT20 or HT20/40 but there - * might be STAs not supporting greenfield mode. - * => Protect all HT40 transmissions. - */ - mm20_mode = gf20_mode = 0; - mm40_mode = gf40_mode = 2; - - break; - case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: - /* - * Nonmember protection: - * According to 802.11n we _should_ protect all - * HT transmissions (but we don't have to). - * - * But if cts_protection is enabled we _shall_ protect - * all HT transmissions using a CCK rate. - * - * And if any station is non GF we _shall_ protect - * GF transmissions. - * - * We decide to protect everything - * -> fall through to mixed mode. - */ - case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: - /* - * Legacy STAs are present - * => Protect all HT transmissions. - */ - mm20_mode = mm40_mode = gf20_mode = gf40_mode = 2; - - /* - * If erp protection is needed we have to protect HT - * transmissions with CCK 11M long preamble. - */ - if (erp->cts_protection) { - /* don't duplicate RTS/CTS in CCK mode */ - mm20_rate = mm40_rate = 0x0003; - gf20_rate = gf40_rate = 0x0003; - } - break; - } - - /* check for STAs not supporting greenfield mode */ - if (any_sta_nongf) - gf20_mode = gf40_mode = 2; - - /* Update HT protection config */ - rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); - rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_RATE, mm20_rate); - rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, mm20_mode); - rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); - rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_RATE, mm40_rate); - rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, mm40_mode); - rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); - rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_RATE, gf20_rate); - rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, gf20_mode); - rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); - rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_RATE, gf40_rate); - rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, gf40_mode); - rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); -} - -void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, - u32 changed) -{ - u32 reg; - - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); - rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, - !!erp->short_preamble); - rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, - !!erp->short_preamble); - rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); - } - - if (changed & BSS_CHANGED_ERP_CTS_PROT) { - rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); - rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, - erp->cts_protection ? 2 : 0); - rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); - } - - if (changed & BSS_CHANGED_BASIC_RATES) { - rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, - erp->basic_rates); - rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); - } - - if (changed & BSS_CHANGED_ERP_SLOT) { - rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, ®); - rt2x00_set_field32(®, BKOFF_SLOT_CFG_SLOT_TIME, - erp->slot_time); - rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg); - - rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, ®); - rt2x00_set_field32(®, XIFS_TIME_CFG_EIFS, erp->eifs); - rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg); - } - - if (changed & BSS_CHANGED_BEACON_INT) { - rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, - erp->beacon_int * 16); - rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); - } - - if (changed & BSS_CHANGED_HT) - rt2800_config_ht_opmode(rt2x00dev, erp); -} -EXPORT_SYMBOL_GPL(rt2800_config_erp); - -static void rt2800_config_3572bt_ant(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - u16 eeprom; - u8 led_ctrl, led_g_mode, led_r_mode; - - rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); - if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { - rt2x00_set_field32(®, GPIO_SWITCH_0, 1); - rt2x00_set_field32(®, GPIO_SWITCH_1, 1); - } else { - rt2x00_set_field32(®, GPIO_SWITCH_0, 0); - rt2x00_set_field32(®, GPIO_SWITCH_1, 0); - } - rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); - - rt2800_register_read(rt2x00dev, LED_CFG, ®); - led_g_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 3 : 0; - led_r_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 0 : 3; - if (led_g_mode != rt2x00_get_field32(reg, LED_CFG_G_LED_MODE) || - led_r_mode != rt2x00_get_field32(reg, LED_CFG_R_LED_MODE)) { - rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); - led_ctrl = rt2x00_get_field16(eeprom, EEPROM_FREQ_LED_MODE); - if (led_ctrl == 0 || led_ctrl > 0x40) { - rt2x00_set_field32(®, LED_CFG_G_LED_MODE, led_g_mode); - rt2x00_set_field32(®, LED_CFG_R_LED_MODE, led_r_mode); - rt2800_register_write(rt2x00dev, LED_CFG, reg); - } else { - rt2800_mcu_request(rt2x00dev, MCU_BAND_SELECT, 0xff, - (led_g_mode << 2) | led_r_mode, 1); - } - } -} - -static void rt2800_set_ant_diversity(struct rt2x00_dev *rt2x00dev, - enum antenna ant) -{ - u32 reg; - u8 eesk_pin = (ant == ANTENNA_A) ? 1 : 0; - u8 gpio_bit3 = (ant == ANTENNA_A) ? 0 : 1; - - if (rt2x00_is_pci(rt2x00dev)) { - rt2800_register_read(rt2x00dev, E2PROM_CSR, ®); - rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, eesk_pin); - rt2800_register_write(rt2x00dev, E2PROM_CSR, reg); - } else if (rt2x00_is_usb(rt2x00dev)) - rt2800_mcu_request(rt2x00dev, MCU_ANT_SELECT, 0xff, - eesk_pin, 0); - - rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); - rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); - rt2x00_set_field32(®, GPIO_CTRL_VAL3, gpio_bit3); - rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); -} - -void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) -{ - u8 r1; - u8 r3; - u16 eeprom; - - rt2800_bbp_read(rt2x00dev, 1, &r1); - rt2800_bbp_read(rt2x00dev, 3, &r3); - - if (rt2x00_rt(rt2x00dev, RT3572) && - rt2x00_has_cap_bt_coexist(rt2x00dev)) - rt2800_config_3572bt_ant(rt2x00dev); - - /* - * Configure the TX antenna. - */ - switch (ant->tx_chain_num) { - case 1: - rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0); - break; - case 2: - if (rt2x00_rt(rt2x00dev, RT3572) && - rt2x00_has_cap_bt_coexist(rt2x00dev)) - rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1); - else - rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); - break; - case 3: - rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); - break; - } - - /* - * Configure the RX antenna. - */ - switch (ant->rx_chain_num) { - case 1: - if (rt2x00_rt(rt2x00dev, RT3070) || - rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT3390)) { - rt2800_eeprom_read(rt2x00dev, - EEPROM_NIC_CONF1, &eeprom); - if (rt2x00_get_field16(eeprom, - EEPROM_NIC_CONF1_ANT_DIVERSITY)) - rt2800_set_ant_diversity(rt2x00dev, - rt2x00dev->default_ant.rx); - } - rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0); - break; - case 2: - if (rt2x00_rt(rt2x00dev, RT3572) && - rt2x00_has_cap_bt_coexist(rt2x00dev)) { - rt2x00_set_field8(&r3, BBP3_RX_ADC, 1); - rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, - rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); - rt2800_set_ant_diversity(rt2x00dev, ANTENNA_B); - } else { - rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 1); - } - break; - case 3: - rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 2); - break; - } - - rt2800_bbp_write(rt2x00dev, 3, r3); - rt2800_bbp_write(rt2x00dev, 1, r1); - - if (rt2x00_rt(rt2x00dev, RT3593)) { - if (ant->rx_chain_num == 1) - rt2800_bbp_write(rt2x00dev, 86, 0x00); - else - rt2800_bbp_write(rt2x00dev, 86, 0x46); - } -} -EXPORT_SYMBOL_GPL(rt2800_config_ant); - -static void rt2800_config_lna_gain(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - u16 eeprom; - short lna_gain; - - if (libconf->rf.channel <= 14) { - rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG); - } else if (libconf->rf.channel <= 64) { - rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0); - } else if (libconf->rf.channel <= 128) { - if (rt2x00_rt(rt2x00dev, RT3593)) { - rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, - EEPROM_EXT_LNA2_A1); - } else { - rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, - EEPROM_RSSI_BG2_LNA_A1); - } - } else { - if (rt2x00_rt(rt2x00dev, RT3593)) { - rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, - EEPROM_EXT_LNA2_A2); - } else { - rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); - lna_gain = rt2x00_get_field16(eeprom, - EEPROM_RSSI_A2_LNA_A2); - } - } - - rt2x00dev->lna_gain = lna_gain; -} - -#define FREQ_OFFSET_BOUND 0x5f - -static void rt2800_adjust_freq_offset(struct rt2x00_dev *rt2x00dev) -{ - u8 freq_offset, prev_freq_offset; - u8 rfcsr, prev_rfcsr; - - freq_offset = rt2x00_get_field8(rt2x00dev->freq_offset, RFCSR17_CODE); - freq_offset = min_t(u8, freq_offset, FREQ_OFFSET_BOUND); - - rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); - prev_rfcsr = rfcsr; - - rt2x00_set_field8(&rfcsr, RFCSR17_CODE, freq_offset); - if (rfcsr == prev_rfcsr) - return; - - if (rt2x00_is_usb(rt2x00dev)) { - rt2800_mcu_request(rt2x00dev, MCU_FREQ_OFFSET, 0xff, - freq_offset, prev_rfcsr); - return; - } - - prev_freq_offset = rt2x00_get_field8(prev_rfcsr, RFCSR17_CODE); - while (prev_freq_offset != freq_offset) { - if (prev_freq_offset < freq_offset) - prev_freq_offset++; - else - prev_freq_offset--; - - rt2x00_set_field8(&rfcsr, RFCSR17_CODE, prev_freq_offset); - rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); - - usleep_range(1000, 1500); - } -} - -static void rt2800_config_channel_rf2xxx(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); - - if (rt2x00dev->default_ant.tx_chain_num == 1) - rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_TX1, 1); - - if (rt2x00dev->default_ant.rx_chain_num == 1) { - rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX1, 1); - rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); - } else if (rt2x00dev->default_ant.rx_chain_num == 2) - rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); - - if (rf->channel > 14) { - /* - * When TX power is below 0, we should increase it by 7 to - * make it a positive value (Minimum value is -7). - * However this means that values between 0 and 7 have - * double meaning, and we should set a 7DBm boost flag. - */ - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST, - (info->default_power1 >= 0)); - - if (info->default_power1 < 0) - info->default_power1 += 7; - - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A, info->default_power1); - - rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST, - (info->default_power2 >= 0)); - - if (info->default_power2 < 0) - info->default_power2 += 7; - - rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A, info->default_power2); - } else { - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G, info->default_power1); - rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G, info->default_power2); - } - - rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf)); - - rt2800_rf_write(rt2x00dev, 1, rf->rf1); - rt2800_rf_write(rt2x00dev, 2, rf->rf2); - rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt2800_rf_write(rt2x00dev, 4, rf->rf4); - - udelay(200); - - rt2800_rf_write(rt2x00dev, 1, rf->rf1); - rt2800_rf_write(rt2x00dev, 2, rf->rf2); - rt2800_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); - rt2800_rf_write(rt2x00dev, 4, rf->rf4); - - udelay(200); - - rt2800_rf_write(rt2x00dev, 1, rf->rf1); - rt2800_rf_write(rt2x00dev, 2, rf->rf2); - rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt2800_rf_write(rt2x00dev, 4, rf->rf4); -} - -static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - u8 rfcsr, calib_tx, calib_rx; - - rt2800_rfcsr_write(rt2x00dev, 2, rf->rf1); - - rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR3_K, rf->rf3); - rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR6_R1, rf->rf2); - rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, info->default_power1); - rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, info->default_power2); - rt2800_rfcsr_write(rt2x00dev, 13, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, - rt2x00dev->default_ant.rx_chain_num <= 1); - rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, - rt2x00dev->default_ant.rx_chain_num <= 2); - rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, - rt2x00dev->default_ant.tx_chain_num <= 1); - rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, - rt2x00dev->default_ant.tx_chain_num <= 2); - rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); - rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); - - if (rt2x00_rt(rt2x00dev, RT3390)) { - calib_tx = conf_is_ht40(conf) ? 0x68 : 0x4f; - calib_rx = conf_is_ht40(conf) ? 0x6f : 0x4f; - } else { - if (conf_is_ht40(conf)) { - calib_tx = drv_data->calibration_bw40; - calib_rx = drv_data->calibration_bw40; - } else { - calib_tx = drv_data->calibration_bw20; - calib_rx = drv_data->calibration_bw20; - } - } - - rt2800_rfcsr_read(rt2x00dev, 24, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR24_TX_CALIB, calib_tx); - rt2800_rfcsr_write(rt2x00dev, 24, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 31, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR31_RX_CALIB, calib_rx); - rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); - rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); - rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); - msleep(1); - rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0); - rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); -} - -static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - u8 rfcsr; - u32 reg; - - if (rf->channel <= 14) { - rt2800_bbp_write(rt2x00dev, 25, drv_data->bbp25); - rt2800_bbp_write(rt2x00dev, 26, drv_data->bbp26); - } else { - rt2800_bbp_write(rt2x00dev, 25, 0x09); - rt2800_bbp_write(rt2x00dev, 26, 0xff); - } - - rt2800_rfcsr_write(rt2x00dev, 2, rf->rf1); - rt2800_rfcsr_write(rt2x00dev, 3, rf->rf3); - - rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR6_R1, rf->rf2); - if (rf->channel <= 14) - rt2x00_set_field8(&rfcsr, RFCSR6_TXDIV, 2); - else - rt2x00_set_field8(&rfcsr, RFCSR6_TXDIV, 1); - rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 5, &rfcsr); - if (rf->channel <= 14) - rt2x00_set_field8(&rfcsr, RFCSR5_R1, 1); - else - rt2x00_set_field8(&rfcsr, RFCSR5_R1, 2); - rt2800_rfcsr_write(rt2x00dev, 5, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); - if (rf->channel <= 14) { - rt2x00_set_field8(&rfcsr, RFCSR12_DR0, 3); - rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, - info->default_power1); - } else { - rt2x00_set_field8(&rfcsr, RFCSR12_DR0, 7); - rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, - (info->default_power1 & 0x3) | - ((info->default_power1 & 0xC) << 1)); - } - rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr); - if (rf->channel <= 14) { - rt2x00_set_field8(&rfcsr, RFCSR13_DR0, 3); - rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, - info->default_power2); - } else { - rt2x00_set_field8(&rfcsr, RFCSR13_DR0, 7); - rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, - (info->default_power2 & 0x3) | - ((info->default_power2 & 0xC) << 1)); - } - rt2800_rfcsr_write(rt2x00dev, 13, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); - if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { - if (rf->channel <= 14) { - rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); - } - rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); - } else { - switch (rt2x00dev->default_ant.tx_chain_num) { - case 1: - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); - case 2: - rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); - break; - } - - switch (rt2x00dev->default_ant.rx_chain_num) { - case 1: - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); - case 2: - rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); - break; - } - } - rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); - rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); - - if (conf_is_ht40(conf)) { - rt2800_rfcsr_write(rt2x00dev, 24, drv_data->calibration_bw40); - rt2800_rfcsr_write(rt2x00dev, 31, drv_data->calibration_bw40); - } else { - rt2800_rfcsr_write(rt2x00dev, 24, drv_data->calibration_bw20); - rt2800_rfcsr_write(rt2x00dev, 31, drv_data->calibration_bw20); - } - - if (rf->channel <= 14) { - rt2800_rfcsr_write(rt2x00dev, 7, 0xd8); - rt2800_rfcsr_write(rt2x00dev, 9, 0xc3); - rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); - rt2800_rfcsr_write(rt2x00dev, 11, 0xb9); - rt2800_rfcsr_write(rt2x00dev, 15, 0x53); - rfcsr = 0x4c; - rt2x00_set_field8(&rfcsr, RFCSR16_TXMIXER_GAIN, - drv_data->txmixer_gain_24g); - rt2800_rfcsr_write(rt2x00dev, 16, rfcsr); - rt2800_rfcsr_write(rt2x00dev, 17, 0x23); - rt2800_rfcsr_write(rt2x00dev, 19, 0x93); - rt2800_rfcsr_write(rt2x00dev, 20, 0xb3); - rt2800_rfcsr_write(rt2x00dev, 25, 0x15); - rt2800_rfcsr_write(rt2x00dev, 26, 0x85); - rt2800_rfcsr_write(rt2x00dev, 27, 0x00); - rt2800_rfcsr_write(rt2x00dev, 29, 0x9b); - } else { - rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR7_BIT2, 1); - rt2x00_set_field8(&rfcsr, RFCSR7_BIT3, 0); - rt2x00_set_field8(&rfcsr, RFCSR7_BIT4, 1); - rt2x00_set_field8(&rfcsr, RFCSR7_BITS67, 0); - rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); - rt2800_rfcsr_write(rt2x00dev, 9, 0xc0); - rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); - rt2800_rfcsr_write(rt2x00dev, 11, 0x00); - rt2800_rfcsr_write(rt2x00dev, 15, 0x43); - rfcsr = 0x7a; - rt2x00_set_field8(&rfcsr, RFCSR16_TXMIXER_GAIN, - drv_data->txmixer_gain_5g); - rt2800_rfcsr_write(rt2x00dev, 16, rfcsr); - rt2800_rfcsr_write(rt2x00dev, 17, 0x23); - if (rf->channel <= 64) { - rt2800_rfcsr_write(rt2x00dev, 19, 0xb7); - rt2800_rfcsr_write(rt2x00dev, 20, 0xf6); - rt2800_rfcsr_write(rt2x00dev, 25, 0x3d); - } else if (rf->channel <= 128) { - rt2800_rfcsr_write(rt2x00dev, 19, 0x74); - rt2800_rfcsr_write(rt2x00dev, 20, 0xf4); - rt2800_rfcsr_write(rt2x00dev, 25, 0x01); - } else { - rt2800_rfcsr_write(rt2x00dev, 19, 0x72); - rt2800_rfcsr_write(rt2x00dev, 20, 0xf3); - rt2800_rfcsr_write(rt2x00dev, 25, 0x01); - } - rt2800_rfcsr_write(rt2x00dev, 26, 0x87); - rt2800_rfcsr_write(rt2x00dev, 27, 0x01); - rt2800_rfcsr_write(rt2x00dev, 29, 0x9f); - } - - rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); - rt2x00_set_field32(®, GPIO_CTRL_DIR7, 0); - if (rf->channel <= 14) - rt2x00_set_field32(®, GPIO_CTRL_VAL7, 1); - else - rt2x00_set_field32(®, GPIO_CTRL_VAL7, 0); - rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); - - rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); - rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); -} - -static void rt2800_config_channel_rf3053(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - u8 txrx_agc_fc; - u8 txrx_h20m; - u8 rfcsr; - u8 bbp; - const bool txbf_enabled = false; /* TODO */ - - /* TODO: use TX{0,1,2}FinePowerControl values from EEPROM */ - rt2800_bbp_read(rt2x00dev, 109, &bbp); - rt2x00_set_field8(&bbp, BBP109_TX0_POWER, 0); - rt2x00_set_field8(&bbp, BBP109_TX1_POWER, 0); - rt2800_bbp_write(rt2x00dev, 109, bbp); - - rt2800_bbp_read(rt2x00dev, 110, &bbp); - rt2x00_set_field8(&bbp, BBP110_TX2_POWER, 0); - rt2800_bbp_write(rt2x00dev, 110, bbp); - - if (rf->channel <= 14) { - /* Restore BBP 25 & 26 for 2.4 GHz */ - rt2800_bbp_write(rt2x00dev, 25, drv_data->bbp25); - rt2800_bbp_write(rt2x00dev, 26, drv_data->bbp26); - } else { - /* Hard code BBP 25 & 26 for 5GHz */ - - /* Enable IQ Phase correction */ - rt2800_bbp_write(rt2x00dev, 25, 0x09); - /* Setup IQ Phase correction value */ - rt2800_bbp_write(rt2x00dev, 26, 0xff); - } - - rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); - rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3 & 0xf); - - rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR11_R, (rf->rf2 & 0x3)); - rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR11_PLL_IDOH, 1); - if (rf->channel <= 14) - rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 1); - else - rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 2); - rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 53, &rfcsr); - if (rf->channel <= 14) { - rfcsr = 0; - rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER, - info->default_power1 & 0x1f); - } else { - if (rt2x00_is_usb(rt2x00dev)) - rfcsr = 0x40; - - rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER, - ((info->default_power1 & 0x18) << 1) | - (info->default_power1 & 7)); - } - rt2800_rfcsr_write(rt2x00dev, 53, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 55, &rfcsr); - if (rf->channel <= 14) { - rfcsr = 0; - rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER, - info->default_power2 & 0x1f); - } else { - if (rt2x00_is_usb(rt2x00dev)) - rfcsr = 0x40; - - rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER, - ((info->default_power2 & 0x18) << 1) | - (info->default_power2 & 7)); - } - rt2800_rfcsr_write(rt2x00dev, 55, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 54, &rfcsr); - if (rf->channel <= 14) { - rfcsr = 0; - rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER, - info->default_power3 & 0x1f); - } else { - if (rt2x00_is_usb(rt2x00dev)) - rfcsr = 0x40; - - rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER, - ((info->default_power3 & 0x18) << 1) | - (info->default_power3 & 7)); - } - rt2800_rfcsr_write(rt2x00dev, 54, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); - - switch (rt2x00dev->default_ant.tx_chain_num) { - case 3: - rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); - /* fallthrough */ - case 2: - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); - /* fallthrough */ - case 1: - rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); - break; - } - - switch (rt2x00dev->default_ant.rx_chain_num) { - case 3: - rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); - /* fallthrough */ - case 2: - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); - /* fallthrough */ - case 1: - rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); - break; - } - rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); - - rt2800_adjust_freq_offset(rt2x00dev); - - if (conf_is_ht40(conf)) { - txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw40, - RFCSR24_TX_AGC_FC); - txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw40, - RFCSR24_TX_H20M); - } else { - txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw20, - RFCSR24_TX_AGC_FC); - txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw20, - RFCSR24_TX_H20M); - } - - /* NOTE: the reference driver does not writes the new value - * back to RFCSR 32 - */ - rt2800_rfcsr_read(rt2x00dev, 32, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR32_TX_AGC_FC, txrx_agc_fc); - - if (rf->channel <= 14) - rfcsr = 0xa0; - else - rfcsr = 0x80; - rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, txrx_h20m); - rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, txrx_h20m); - rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); - - /* Band selection */ - rt2800_rfcsr_read(rt2x00dev, 36, &rfcsr); - if (rf->channel <= 14) - rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 1); - else - rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 0); - rt2800_rfcsr_write(rt2x00dev, 36, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 34, &rfcsr); - if (rf->channel <= 14) - rfcsr = 0x3c; - else - rfcsr = 0x20; - rt2800_rfcsr_write(rt2x00dev, 34, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); - if (rf->channel <= 14) - rfcsr = 0x1a; - else - rfcsr = 0x12; - rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); - if (rf->channel >= 1 && rf->channel <= 14) - rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1); - else if (rf->channel >= 36 && rf->channel <= 64) - rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2); - else if (rf->channel >= 100 && rf->channel <= 128) - rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2); - else - rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1); - rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2); - rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); - - rt2800_rfcsr_write(rt2x00dev, 46, 0x60); - - if (rf->channel <= 14) { - rt2800_rfcsr_write(rt2x00dev, 10, 0xd3); - rt2800_rfcsr_write(rt2x00dev, 13, 0x12); - } else { - rt2800_rfcsr_write(rt2x00dev, 10, 0xd8); - rt2800_rfcsr_write(rt2x00dev, 13, 0x23); - } - - rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR51_BITS01, 1); - rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); - if (rf->channel <= 14) { - rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 5); - rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 3); - } else { - rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 4); - rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 2); - } - rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); - if (rf->channel <= 14) - rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 3); - else - rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 2); - - if (txbf_enabled) - rt2x00_set_field8(&rfcsr, RFCSR49_TX_DIV, 1); - - rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO1_EN, 0); - rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 57, &rfcsr); - if (rf->channel <= 14) - rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x1b); - else - rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x0f); - rt2800_rfcsr_write(rt2x00dev, 57, rfcsr); - - if (rf->channel <= 14) { - rt2800_rfcsr_write(rt2x00dev, 44, 0x93); - rt2800_rfcsr_write(rt2x00dev, 52, 0x45); - } else { - rt2800_rfcsr_write(rt2x00dev, 44, 0x9b); - rt2800_rfcsr_write(rt2x00dev, 52, 0x05); - } - - /* Initiate VCO calibration */ - rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); - if (rf->channel <= 14) { - rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); - } else { - rt2x00_set_field8(&rfcsr, RFCSR3_BIT1, 1); - rt2x00_set_field8(&rfcsr, RFCSR3_BIT2, 1); - rt2x00_set_field8(&rfcsr, RFCSR3_BIT3, 1); - rt2x00_set_field8(&rfcsr, RFCSR3_BIT4, 1); - rt2x00_set_field8(&rfcsr, RFCSR3_BIT5, 1); - rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); - } - rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); - - if (rf->channel >= 1 && rf->channel <= 14) { - rfcsr = 0x23; - if (txbf_enabled) - rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); - rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); - - rt2800_rfcsr_write(rt2x00dev, 45, 0xbb); - } else if (rf->channel >= 36 && rf->channel <= 64) { - rfcsr = 0x36; - if (txbf_enabled) - rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); - rt2800_rfcsr_write(rt2x00dev, 39, 0x36); - - rt2800_rfcsr_write(rt2x00dev, 45, 0xeb); - } else if (rf->channel >= 100 && rf->channel <= 128) { - rfcsr = 0x32; - if (txbf_enabled) - rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); - rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); - - rt2800_rfcsr_write(rt2x00dev, 45, 0xb3); - } else { - rfcsr = 0x30; - if (txbf_enabled) - rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); - rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); - - rt2800_rfcsr_write(rt2x00dev, 45, 0x9b); - } -} - -#define POWER_BOUND 0x27 -#define POWER_BOUND_5G 0x2b - -static void rt2800_config_channel_rf3290(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - u8 rfcsr; - - rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); - rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); - rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf2); - rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); - if (info->default_power1 > POWER_BOUND) - rt2x00_set_field8(&rfcsr, RFCSR49_TX, POWER_BOUND); - else - rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); - rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); - - rt2800_adjust_freq_offset(rt2x00dev); - - if (rf->channel <= 14) { - if (rf->channel == 6) - rt2800_bbp_write(rt2x00dev, 68, 0x0c); - else - rt2800_bbp_write(rt2x00dev, 68, 0x0b); - - if (rf->channel >= 1 && rf->channel <= 6) - rt2800_bbp_write(rt2x00dev, 59, 0x0f); - else if (rf->channel >= 7 && rf->channel <= 11) - rt2800_bbp_write(rt2x00dev, 59, 0x0e); - else if (rf->channel >= 12 && rf->channel <= 14) - rt2800_bbp_write(rt2x00dev, 59, 0x0d); - } -} - -static void rt2800_config_channel_rf3322(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - u8 rfcsr; - - rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); - rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); - - rt2800_rfcsr_write(rt2x00dev, 11, 0x42); - rt2800_rfcsr_write(rt2x00dev, 12, 0x1c); - rt2800_rfcsr_write(rt2x00dev, 13, 0x00); - - if (info->default_power1 > POWER_BOUND) - rt2800_rfcsr_write(rt2x00dev, 47, POWER_BOUND); - else - rt2800_rfcsr_write(rt2x00dev, 47, info->default_power1); - - if (info->default_power2 > POWER_BOUND) - rt2800_rfcsr_write(rt2x00dev, 48, POWER_BOUND); - else - rt2800_rfcsr_write(rt2x00dev, 48, info->default_power2); - - rt2800_adjust_freq_offset(rt2x00dev); - - rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); - - if ( rt2x00dev->default_ant.tx_chain_num == 2 ) - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); - else - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); - - if ( rt2x00dev->default_ant.rx_chain_num == 2 ) - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); - else - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); - - rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); - - rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); - - rt2800_rfcsr_write(rt2x00dev, 31, 80); -} - -static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - u8 rfcsr; - - rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); - rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); - rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf2); - rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); - if (info->default_power1 > POWER_BOUND) - rt2x00_set_field8(&rfcsr, RFCSR49_TX, POWER_BOUND); - else - rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); - rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); - - if (rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); - if (info->default_power2 > POWER_BOUND) - rt2x00_set_field8(&rfcsr, RFCSR50_TX, POWER_BOUND); - else - rt2x00_set_field8(&rfcsr, RFCSR50_TX, - info->default_power2); - rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); - } - - rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); - if (rt2x00_rt(rt2x00dev, RT5392)) { - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); - } - rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); - rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); - - rt2800_adjust_freq_offset(rt2x00dev); - - if (rf->channel <= 14) { - int idx = rf->channel-1; - - if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { - /* r55/r59 value array of channel 1~14 */ - static const char r55_bt_rev[] = {0x83, 0x83, - 0x83, 0x73, 0x73, 0x63, 0x53, 0x53, - 0x53, 0x43, 0x43, 0x43, 0x43, 0x43}; - static const char r59_bt_rev[] = {0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0b, 0x0a, 0x09, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07}; - - rt2800_rfcsr_write(rt2x00dev, 55, - r55_bt_rev[idx]); - rt2800_rfcsr_write(rt2x00dev, 59, - r59_bt_rev[idx]); - } else { - static const char r59_bt[] = {0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x89, - 0x88, 0x88, 0x86, 0x85, 0x84}; - - rt2800_rfcsr_write(rt2x00dev, 59, r59_bt[idx]); - } - } else { - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { - static const char r55_nonbt_rev[] = {0x23, 0x23, - 0x23, 0x23, 0x13, 0x13, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}; - static const char r59_nonbt_rev[] = {0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x06, 0x05, 0x04, 0x04}; - - rt2800_rfcsr_write(rt2x00dev, 55, - r55_nonbt_rev[idx]); - rt2800_rfcsr_write(rt2x00dev, 59, - r59_nonbt_rev[idx]); - } else if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { - static const char r59_non_bt[] = {0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8d, - 0x8a, 0x88, 0x88, 0x87, 0x87, 0x86}; - - rt2800_rfcsr_write(rt2x00dev, 59, - r59_non_bt[idx]); - } - } - } -} - -static void rt2800_config_channel_rf55xx(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - u8 rfcsr, ep_reg; - u32 reg; - int power_bound; - - /* TODO */ - const bool is_11b = false; - const bool is_type_ep = false; - - rt2800_register_read(rt2x00dev, LDO_CFG0, ®); - rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, - (rf->channel > 14 || conf_is_ht40(conf)) ? 5 : 0); - rt2800_register_write(rt2x00dev, LDO_CFG0, reg); - - /* Order of values on rf_channel entry: N, K, mod, R */ - rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1 & 0xff); - - rt2800_rfcsr_read(rt2x00dev, 9, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR9_K, rf->rf2 & 0xf); - rt2x00_set_field8(&rfcsr, RFCSR9_N, (rf->rf1 & 0x100) >> 8); - rt2x00_set_field8(&rfcsr, RFCSR9_MOD, ((rf->rf3 - 8) & 0x4) >> 2); - rt2800_rfcsr_write(rt2x00dev, 9, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf4 - 1); - rt2x00_set_field8(&rfcsr, RFCSR11_MOD, (rf->rf3 - 8) & 0x3); - rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); - - if (rf->channel <= 14) { - rt2800_rfcsr_write(rt2x00dev, 10, 0x90); - /* FIXME: RF11 owerwrite ? */ - rt2800_rfcsr_write(rt2x00dev, 11, 0x4A); - rt2800_rfcsr_write(rt2x00dev, 12, 0x52); - rt2800_rfcsr_write(rt2x00dev, 13, 0x42); - rt2800_rfcsr_write(rt2x00dev, 22, 0x40); - rt2800_rfcsr_write(rt2x00dev, 24, 0x4A); - rt2800_rfcsr_write(rt2x00dev, 25, 0x80); - rt2800_rfcsr_write(rt2x00dev, 27, 0x42); - rt2800_rfcsr_write(rt2x00dev, 36, 0x80); - rt2800_rfcsr_write(rt2x00dev, 37, 0x08); - rt2800_rfcsr_write(rt2x00dev, 38, 0x89); - rt2800_rfcsr_write(rt2x00dev, 39, 0x1B); - rt2800_rfcsr_write(rt2x00dev, 40, 0x0D); - rt2800_rfcsr_write(rt2x00dev, 41, 0x9B); - rt2800_rfcsr_write(rt2x00dev, 42, 0xD5); - rt2800_rfcsr_write(rt2x00dev, 43, 0x72); - rt2800_rfcsr_write(rt2x00dev, 44, 0x0E); - rt2800_rfcsr_write(rt2x00dev, 45, 0xA2); - rt2800_rfcsr_write(rt2x00dev, 46, 0x6B); - rt2800_rfcsr_write(rt2x00dev, 48, 0x10); - rt2800_rfcsr_write(rt2x00dev, 51, 0x3E); - rt2800_rfcsr_write(rt2x00dev, 52, 0x48); - rt2800_rfcsr_write(rt2x00dev, 54, 0x38); - rt2800_rfcsr_write(rt2x00dev, 56, 0xA1); - rt2800_rfcsr_write(rt2x00dev, 57, 0x00); - rt2800_rfcsr_write(rt2x00dev, 58, 0x39); - rt2800_rfcsr_write(rt2x00dev, 60, 0x45); - rt2800_rfcsr_write(rt2x00dev, 61, 0x91); - rt2800_rfcsr_write(rt2x00dev, 62, 0x39); - - /* TODO RF27 <- tssi */ - - rfcsr = rf->channel <= 10 ? 0x07 : 0x06; - rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); - rt2800_rfcsr_write(rt2x00dev, 59, rfcsr); - - if (is_11b) { - /* CCK */ - rt2800_rfcsr_write(rt2x00dev, 31, 0xF8); - rt2800_rfcsr_write(rt2x00dev, 32, 0xC0); - if (is_type_ep) - rt2800_rfcsr_write(rt2x00dev, 55, 0x06); - else - rt2800_rfcsr_write(rt2x00dev, 55, 0x47); - } else { - /* OFDM */ - if (is_type_ep) - rt2800_rfcsr_write(rt2x00dev, 55, 0x03); - else - rt2800_rfcsr_write(rt2x00dev, 55, 0x43); - } - - power_bound = POWER_BOUND; - ep_reg = 0x2; - } else { - rt2800_rfcsr_write(rt2x00dev, 10, 0x97); - /* FIMXE: RF11 overwrite */ - rt2800_rfcsr_write(rt2x00dev, 11, 0x40); - rt2800_rfcsr_write(rt2x00dev, 25, 0xBF); - rt2800_rfcsr_write(rt2x00dev, 27, 0x42); - rt2800_rfcsr_write(rt2x00dev, 36, 0x00); - rt2800_rfcsr_write(rt2x00dev, 37, 0x04); - rt2800_rfcsr_write(rt2x00dev, 38, 0x85); - rt2800_rfcsr_write(rt2x00dev, 40, 0x42); - rt2800_rfcsr_write(rt2x00dev, 41, 0xBB); - rt2800_rfcsr_write(rt2x00dev, 42, 0xD7); - rt2800_rfcsr_write(rt2x00dev, 45, 0x41); - rt2800_rfcsr_write(rt2x00dev, 48, 0x00); - rt2800_rfcsr_write(rt2x00dev, 57, 0x77); - rt2800_rfcsr_write(rt2x00dev, 60, 0x05); - rt2800_rfcsr_write(rt2x00dev, 61, 0x01); - - /* TODO RF27 <- tssi */ - - if (rf->channel >= 36 && rf->channel <= 64) { - - rt2800_rfcsr_write(rt2x00dev, 12, 0x2E); - rt2800_rfcsr_write(rt2x00dev, 13, 0x22); - rt2800_rfcsr_write(rt2x00dev, 22, 0x60); - rt2800_rfcsr_write(rt2x00dev, 23, 0x7F); - if (rf->channel <= 50) - rt2800_rfcsr_write(rt2x00dev, 24, 0x09); - else if (rf->channel >= 52) - rt2800_rfcsr_write(rt2x00dev, 24, 0x07); - rt2800_rfcsr_write(rt2x00dev, 39, 0x1C); - rt2800_rfcsr_write(rt2x00dev, 43, 0x5B); - rt2800_rfcsr_write(rt2x00dev, 44, 0X40); - rt2800_rfcsr_write(rt2x00dev, 46, 0X00); - rt2800_rfcsr_write(rt2x00dev, 51, 0xFE); - rt2800_rfcsr_write(rt2x00dev, 52, 0x0C); - rt2800_rfcsr_write(rt2x00dev, 54, 0xF8); - if (rf->channel <= 50) { - rt2800_rfcsr_write(rt2x00dev, 55, 0x06), - rt2800_rfcsr_write(rt2x00dev, 56, 0xD3); - } else if (rf->channel >= 52) { - rt2800_rfcsr_write(rt2x00dev, 55, 0x04); - rt2800_rfcsr_write(rt2x00dev, 56, 0xBB); - } - - rt2800_rfcsr_write(rt2x00dev, 58, 0x15); - rt2800_rfcsr_write(rt2x00dev, 59, 0x7F); - rt2800_rfcsr_write(rt2x00dev, 62, 0x15); - - } else if (rf->channel >= 100 && rf->channel <= 165) { - - rt2800_rfcsr_write(rt2x00dev, 12, 0x0E); - rt2800_rfcsr_write(rt2x00dev, 13, 0x42); - rt2800_rfcsr_write(rt2x00dev, 22, 0x40); - if (rf->channel <= 153) { - rt2800_rfcsr_write(rt2x00dev, 23, 0x3C); - rt2800_rfcsr_write(rt2x00dev, 24, 0x06); - } else if (rf->channel >= 155) { - rt2800_rfcsr_write(rt2x00dev, 23, 0x38); - rt2800_rfcsr_write(rt2x00dev, 24, 0x05); - } - if (rf->channel <= 138) { - rt2800_rfcsr_write(rt2x00dev, 39, 0x1A); - rt2800_rfcsr_write(rt2x00dev, 43, 0x3B); - rt2800_rfcsr_write(rt2x00dev, 44, 0x20); - rt2800_rfcsr_write(rt2x00dev, 46, 0x18); - } else if (rf->channel >= 140) { - rt2800_rfcsr_write(rt2x00dev, 39, 0x18); - rt2800_rfcsr_write(rt2x00dev, 43, 0x1B); - rt2800_rfcsr_write(rt2x00dev, 44, 0x10); - rt2800_rfcsr_write(rt2x00dev, 46, 0X08); - } - if (rf->channel <= 124) - rt2800_rfcsr_write(rt2x00dev, 51, 0xFC); - else if (rf->channel >= 126) - rt2800_rfcsr_write(rt2x00dev, 51, 0xEC); - if (rf->channel <= 138) - rt2800_rfcsr_write(rt2x00dev, 52, 0x06); - else if (rf->channel >= 140) - rt2800_rfcsr_write(rt2x00dev, 52, 0x06); - rt2800_rfcsr_write(rt2x00dev, 54, 0xEB); - if (rf->channel <= 138) - rt2800_rfcsr_write(rt2x00dev, 55, 0x01); - else if (rf->channel >= 140) - rt2800_rfcsr_write(rt2x00dev, 55, 0x00); - if (rf->channel <= 128) - rt2800_rfcsr_write(rt2x00dev, 56, 0xBB); - else if (rf->channel >= 130) - rt2800_rfcsr_write(rt2x00dev, 56, 0xAB); - if (rf->channel <= 116) - rt2800_rfcsr_write(rt2x00dev, 58, 0x1D); - else if (rf->channel >= 118) - rt2800_rfcsr_write(rt2x00dev, 58, 0x15); - if (rf->channel <= 138) - rt2800_rfcsr_write(rt2x00dev, 59, 0x3F); - else if (rf->channel >= 140) - rt2800_rfcsr_write(rt2x00dev, 59, 0x7C); - if (rf->channel <= 116) - rt2800_rfcsr_write(rt2x00dev, 62, 0x1D); - else if (rf->channel >= 118) - rt2800_rfcsr_write(rt2x00dev, 62, 0x15); - } - - power_bound = POWER_BOUND_5G; - ep_reg = 0x3; - } - - rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); - if (info->default_power1 > power_bound) - rt2x00_set_field8(&rfcsr, RFCSR49_TX, power_bound); - else - rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); - if (is_type_ep) - rt2x00_set_field8(&rfcsr, RFCSR49_EP, ep_reg); - rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); - if (info->default_power2 > power_bound) - rt2x00_set_field8(&rfcsr, RFCSR50_TX, power_bound); - else - rt2x00_set_field8(&rfcsr, RFCSR50_TX, info->default_power2); - if (is_type_ep) - rt2x00_set_field8(&rfcsr, RFCSR50_EP, ep_reg); - rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); - - rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, - rt2x00dev->default_ant.tx_chain_num >= 1); - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, - rt2x00dev->default_ant.tx_chain_num == 2); - rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); - - rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, - rt2x00dev->default_ant.rx_chain_num >= 1); - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, - rt2x00dev->default_ant.rx_chain_num == 2); - rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); - - rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); - rt2800_rfcsr_write(rt2x00dev, 6, 0xe4); - - if (conf_is_ht40(conf)) - rt2800_rfcsr_write(rt2x00dev, 30, 0x16); - else - rt2800_rfcsr_write(rt2x00dev, 30, 0x10); - - if (!is_11b) { - rt2800_rfcsr_write(rt2x00dev, 31, 0x80); - rt2800_rfcsr_write(rt2x00dev, 32, 0x80); - } - - /* TODO proper frequency adjustment */ - rt2800_adjust_freq_offset(rt2x00dev); - - /* TODO merge with others */ - rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); - rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); - - /* BBP settings */ - rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); - rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); - rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); - - rt2800_bbp_write(rt2x00dev, 79, (rf->channel <= 14) ? 0x1C : 0x18); - rt2800_bbp_write(rt2x00dev, 80, (rf->channel <= 14) ? 0x0E : 0x08); - rt2800_bbp_write(rt2x00dev, 81, (rf->channel <= 14) ? 0x3A : 0x38); - rt2800_bbp_write(rt2x00dev, 82, (rf->channel <= 14) ? 0x62 : 0x92); - - /* GLRT band configuration */ - rt2800_bbp_write(rt2x00dev, 195, 128); - rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0xE0 : 0xF0); - rt2800_bbp_write(rt2x00dev, 195, 129); - rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x1F : 0x1E); - rt2800_bbp_write(rt2x00dev, 195, 130); - rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x38 : 0x28); - rt2800_bbp_write(rt2x00dev, 195, 131); - rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x32 : 0x20); - rt2800_bbp_write(rt2x00dev, 195, 133); - rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x28 : 0x7F); - rt2800_bbp_write(rt2x00dev, 195, 124); - rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x19 : 0x7F); -} - -static void rt2800_bbp_write_with_rx_chain(struct rt2x00_dev *rt2x00dev, - const unsigned int word, - const u8 value) -{ - u8 chain, reg; - - for (chain = 0; chain < rt2x00dev->default_ant.rx_chain_num; chain++) { - rt2800_bbp_read(rt2x00dev, 27, ®); - rt2x00_set_field8(®, BBP27_RX_CHAIN_SEL, chain); - rt2800_bbp_write(rt2x00dev, 27, reg); - - rt2800_bbp_write(rt2x00dev, word, value); - } -} - -static void rt2800_iq_calibrate(struct rt2x00_dev *rt2x00dev, int channel) -{ - u8 cal; - - /* TX0 IQ Gain */ - rt2800_bbp_write(rt2x00dev, 158, 0x2c); - if (channel <= 14) - cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_GAIN_CAL_TX0_2G); - else if (channel >= 36 && channel <= 64) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5G); - else if (channel >= 100 && channel <= 138) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5G); - else if (channel >= 140 && channel <= 165) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5G); - else - cal = 0; - rt2800_bbp_write(rt2x00dev, 159, cal); - - /* TX0 IQ Phase */ - rt2800_bbp_write(rt2x00dev, 158, 0x2d); - if (channel <= 14) - cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_PHASE_CAL_TX0_2G); - else if (channel >= 36 && channel <= 64) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5G); - else if (channel >= 100 && channel <= 138) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5G); - else if (channel >= 140 && channel <= 165) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5G); - else - cal = 0; - rt2800_bbp_write(rt2x00dev, 159, cal); - - /* TX1 IQ Gain */ - rt2800_bbp_write(rt2x00dev, 158, 0x4a); - if (channel <= 14) - cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_GAIN_CAL_TX1_2G); - else if (channel >= 36 && channel <= 64) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5G); - else if (channel >= 100 && channel <= 138) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5G); - else if (channel >= 140 && channel <= 165) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5G); - else - cal = 0; - rt2800_bbp_write(rt2x00dev, 159, cal); - - /* TX1 IQ Phase */ - rt2800_bbp_write(rt2x00dev, 158, 0x4b); - if (channel <= 14) - cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_PHASE_CAL_TX1_2G); - else if (channel >= 36 && channel <= 64) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5G); - else if (channel >= 100 && channel <= 138) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5G); - else if (channel >= 140 && channel <= 165) - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5G); - else - cal = 0; - rt2800_bbp_write(rt2x00dev, 159, cal); - - /* FIXME: possible RX0, RX1 callibration ? */ - - /* RF IQ compensation control */ - rt2800_bbp_write(rt2x00dev, 158, 0x04); - cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_RF_IQ_COMPENSATION_CONTROL); - rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0); - - /* RF IQ imbalance compensation control */ - rt2800_bbp_write(rt2x00dev, 158, 0x03); - cal = rt2x00_eeprom_byte(rt2x00dev, - EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CONTROL); - rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0); -} - -static char rt2800_txpower_to_dev(struct rt2x00_dev *rt2x00dev, - unsigned int channel, - char txpower) -{ - if (rt2x00_rt(rt2x00dev, RT3593)) - txpower = rt2x00_get_field8(txpower, EEPROM_TXPOWER_ALC); - - if (channel <= 14) - return clamp_t(char, txpower, MIN_G_TXPOWER, MAX_G_TXPOWER); - - if (rt2x00_rt(rt2x00dev, RT3593)) - return clamp_t(char, txpower, MIN_A_TXPOWER_3593, - MAX_A_TXPOWER_3593); - else - return clamp_t(char, txpower, MIN_A_TXPOWER, MAX_A_TXPOWER); -} - -static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - struct rf_channel *rf, - struct channel_info *info) -{ - u32 reg; - unsigned int tx_pin; - u8 bbp, rfcsr; - - info->default_power1 = rt2800_txpower_to_dev(rt2x00dev, rf->channel, - info->default_power1); - info->default_power2 = rt2800_txpower_to_dev(rt2x00dev, rf->channel, - info->default_power2); - if (rt2x00dev->default_ant.tx_chain_num > 2) - info->default_power3 = - rt2800_txpower_to_dev(rt2x00dev, rf->channel, - info->default_power3); - - switch (rt2x00dev->chip.rf) { - case RF2020: - case RF3020: - case RF3021: - case RF3022: - case RF3320: - rt2800_config_channel_rf3xxx(rt2x00dev, conf, rf, info); - break; - case RF3052: - rt2800_config_channel_rf3052(rt2x00dev, conf, rf, info); - break; - case RF3053: - rt2800_config_channel_rf3053(rt2x00dev, conf, rf, info); - break; - case RF3290: - rt2800_config_channel_rf3290(rt2x00dev, conf, rf, info); - break; - case RF3322: - rt2800_config_channel_rf3322(rt2x00dev, conf, rf, info); - break; - case RF3070: - case RF5360: - case RF5362: - case RF5370: - case RF5372: - case RF5390: - case RF5392: - rt2800_config_channel_rf53xx(rt2x00dev, conf, rf, info); - break; - case RF5592: - rt2800_config_channel_rf55xx(rt2x00dev, conf, rf, info); - break; - default: - rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info); - } - - if (rt2x00_rf(rt2x00dev, RF3070) || - rt2x00_rf(rt2x00dev, RF3290) || - rt2x00_rf(rt2x00dev, RF3322) || - rt2x00_rf(rt2x00dev, RF5360) || - rt2x00_rf(rt2x00dev, RF5362) || - rt2x00_rf(rt2x00dev, RF5370) || - rt2x00_rf(rt2x00dev, RF5372) || - rt2x00_rf(rt2x00dev, RF5390) || - rt2x00_rf(rt2x00dev, RF5392)) { - rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, 0); - rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, 0); - rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); - rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); - } - - /* - * Change BBP settings - */ - if (rt2x00_rt(rt2x00dev, RT3352)) { - rt2800_bbp_write(rt2x00dev, 27, 0x0); - rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain); - rt2800_bbp_write(rt2x00dev, 27, 0x20); - rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain); - } else if (rt2x00_rt(rt2x00dev, RT3593)) { - if (rf->channel > 14) { - /* Disable CCK Packet detection on 5GHz */ - rt2800_bbp_write(rt2x00dev, 70, 0x00); - } else { - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - } - - if (conf_is_ht40(conf)) - rt2800_bbp_write(rt2x00dev, 105, 0x04); - else - rt2800_bbp_write(rt2x00dev, 105, 0x34); - - rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); - rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); - rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); - rt2800_bbp_write(rt2x00dev, 77, 0x98); - } else { - rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); - rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); - rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); - rt2800_bbp_write(rt2x00dev, 86, 0); - } - - if (rf->channel <= 14) { - if (!rt2x00_rt(rt2x00dev, RT5390) && - !rt2x00_rt(rt2x00dev, RT5392)) { - if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { - rt2800_bbp_write(rt2x00dev, 82, 0x62); - rt2800_bbp_write(rt2x00dev, 75, 0x46); - } else { - if (rt2x00_rt(rt2x00dev, RT3593)) - rt2800_bbp_write(rt2x00dev, 82, 0x62); - else - rt2800_bbp_write(rt2x00dev, 82, 0x84); - rt2800_bbp_write(rt2x00dev, 75, 0x50); - } - if (rt2x00_rt(rt2x00dev, RT3593)) - rt2800_bbp_write(rt2x00dev, 83, 0x8a); - } - - } else { - if (rt2x00_rt(rt2x00dev, RT3572)) - rt2800_bbp_write(rt2x00dev, 82, 0x94); - else if (rt2x00_rt(rt2x00dev, RT3593)) - rt2800_bbp_write(rt2x00dev, 82, 0x82); - else - rt2800_bbp_write(rt2x00dev, 82, 0xf2); - - if (rt2x00_rt(rt2x00dev, RT3593)) - rt2800_bbp_write(rt2x00dev, 83, 0x9a); - - if (rt2x00_has_cap_external_lna_a(rt2x00dev)) - rt2800_bbp_write(rt2x00dev, 75, 0x46); - else - rt2800_bbp_write(rt2x00dev, 75, 0x50); - } - - rt2800_register_read(rt2x00dev, TX_BAND_CFG, ®); - rt2x00_set_field32(®, TX_BAND_CFG_HT40_MINUS, conf_is_ht40_minus(conf)); - rt2x00_set_field32(®, TX_BAND_CFG_A, rf->channel > 14); - rt2x00_set_field32(®, TX_BAND_CFG_BG, rf->channel <= 14); - rt2800_register_write(rt2x00dev, TX_BAND_CFG, reg); - - if (rt2x00_rt(rt2x00dev, RT3572)) - rt2800_rfcsr_write(rt2x00dev, 8, 0); - - tx_pin = 0; - - switch (rt2x00dev->default_ant.tx_chain_num) { - case 3: - /* Turn on tertiary PAs */ - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A2_EN, - rf->channel > 14); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G2_EN, - rf->channel <= 14); - /* fall-through */ - case 2: - /* Turn on secondary PAs */ - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, - rf->channel > 14); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, - rf->channel <= 14); - /* fall-through */ - case 1: - /* Turn on primary PAs */ - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, - rf->channel > 14); - if (rt2x00_has_cap_bt_coexist(rt2x00dev)) - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); - else - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, - rf->channel <= 14); - break; - } - - switch (rt2x00dev->default_ant.rx_chain_num) { - case 3: - /* Turn on tertiary LNAs */ - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A2_EN, 1); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G2_EN, 1); - /* fall-through */ - case 2: - /* Turn on secondary LNAs */ - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, 1); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, 1); - /* fall-through */ - case 1: - /* Turn on primary LNAs */ - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1); - break; - } - - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1); - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1); - - rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); - - if (rt2x00_rt(rt2x00dev, RT3572)) { - rt2800_rfcsr_write(rt2x00dev, 8, 0x80); - - /* AGC init */ - if (rf->channel <= 14) - reg = 0x1c + (2 * rt2x00dev->lna_gain); - else - reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3); - - rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); - } - - if (rt2x00_rt(rt2x00dev, RT3593)) { - rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); - - /* Band selection */ - if (rt2x00_is_usb(rt2x00dev) || - rt2x00_is_pcie(rt2x00dev)) { - /* GPIO #8 controls all paths */ - rt2x00_set_field32(®, GPIO_CTRL_DIR8, 0); - if (rf->channel <= 14) - rt2x00_set_field32(®, GPIO_CTRL_VAL8, 1); - else - rt2x00_set_field32(®, GPIO_CTRL_VAL8, 0); - } - - /* LNA PE control. */ - if (rt2x00_is_usb(rt2x00dev)) { - /* GPIO #4 controls PE0 and PE1, - * GPIO #7 controls PE2 - */ - rt2x00_set_field32(®, GPIO_CTRL_DIR4, 0); - rt2x00_set_field32(®, GPIO_CTRL_DIR7, 0); - - rt2x00_set_field32(®, GPIO_CTRL_VAL4, 1); - rt2x00_set_field32(®, GPIO_CTRL_VAL7, 1); - } else if (rt2x00_is_pcie(rt2x00dev)) { - /* GPIO #4 controls PE0, PE1 and PE2 */ - rt2x00_set_field32(®, GPIO_CTRL_DIR4, 0); - rt2x00_set_field32(®, GPIO_CTRL_VAL4, 1); - } - - rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); - - /* AGC init */ - if (rf->channel <= 14) - reg = 0x1c + 2 * rt2x00dev->lna_gain; - else - reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3); - - rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); - - usleep_range(1000, 1500); - } - - if (rt2x00_rt(rt2x00dev, RT5592)) { - rt2800_bbp_write(rt2x00dev, 195, 141); - rt2800_bbp_write(rt2x00dev, 196, conf_is_ht40(conf) ? 0x10 : 0x1a); - - /* AGC init */ - reg = (rf->channel <= 14 ? 0x1c : 0x24) + 2 * rt2x00dev->lna_gain; - rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); - - rt2800_iq_calibrate(rt2x00dev, rf->channel); - } - - rt2800_bbp_read(rt2x00dev, 4, &bbp); - rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf)); - rt2800_bbp_write(rt2x00dev, 4, bbp); - - rt2800_bbp_read(rt2x00dev, 3, &bbp); - rt2x00_set_field8(&bbp, BBP3_HT40_MINUS, conf_is_ht40_minus(conf)); - rt2800_bbp_write(rt2x00dev, 3, bbp); - - if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { - if (conf_is_ht40(conf)) { - rt2800_bbp_write(rt2x00dev, 69, 0x1a); - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - rt2800_bbp_write(rt2x00dev, 73, 0x16); - } else { - rt2800_bbp_write(rt2x00dev, 69, 0x16); - rt2800_bbp_write(rt2x00dev, 70, 0x08); - rt2800_bbp_write(rt2x00dev, 73, 0x11); - } - } - - msleep(1); - - /* - * Clear channel statistic counters - */ - rt2800_register_read(rt2x00dev, CH_IDLE_STA, ®); - rt2800_register_read(rt2x00dev, CH_BUSY_STA, ®); - rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, ®); - - /* - * Clear update flag - */ - if (rt2x00_rt(rt2x00dev, RT3352)) { - rt2800_bbp_read(rt2x00dev, 49, &bbp); - rt2x00_set_field8(&bbp, BBP49_UPDATE_FLAG, 0); - rt2800_bbp_write(rt2x00dev, 49, bbp); - } -} - -static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev) -{ - u8 tssi_bounds[9]; - u8 current_tssi; - u16 eeprom; - u8 step; - int i; - - /* - * First check if temperature compensation is supported. - */ - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); - if (!rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC)) - return 0; - - /* - * Read TSSI boundaries for temperature compensation from - * the EEPROM. - * - * Array idx 0 1 2 3 4 5 6 7 8 - * Matching Delta value -4 -3 -2 -1 0 +1 +2 +3 +4 - * Example TSSI bounds 0xF0 0xD0 0xB5 0xA0 0x88 0x45 0x25 0x15 0x00 - */ - if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { - rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom); - tssi_bounds[0] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_BG1_MINUS4); - tssi_bounds[1] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_BG1_MINUS3); - - rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom); - tssi_bounds[2] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_BG2_MINUS2); - tssi_bounds[3] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_BG2_MINUS1); - - rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom); - tssi_bounds[4] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_BG3_REF); - tssi_bounds[5] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_BG3_PLUS1); - - rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom); - tssi_bounds[6] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_BG4_PLUS2); - tssi_bounds[7] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_BG4_PLUS3); - - rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom); - tssi_bounds[8] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_BG5_PLUS4); - - step = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_BG5_AGC_STEP); - } else { - rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom); - tssi_bounds[0] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_A1_MINUS4); - tssi_bounds[1] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_A1_MINUS3); - - rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom); - tssi_bounds[2] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_A2_MINUS2); - tssi_bounds[3] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_A2_MINUS1); - - rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom); - tssi_bounds[4] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_A3_REF); - tssi_bounds[5] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_A3_PLUS1); - - rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom); - tssi_bounds[6] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_A4_PLUS2); - tssi_bounds[7] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_A4_PLUS3); - - rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom); - tssi_bounds[8] = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_A5_PLUS4); - - step = rt2x00_get_field16(eeprom, - EEPROM_TSSI_BOUND_A5_AGC_STEP); - } - - /* - * Check if temperature compensation is supported. - */ - if (tssi_bounds[4] == 0xff || step == 0xff) - return 0; - - /* - * Read current TSSI (BBP 49). - */ - rt2800_bbp_read(rt2x00dev, 49, ¤t_tssi); - - /* - * Compare TSSI value (BBP49) with the compensation boundaries - * from the EEPROM and increase or decrease tx power. - */ - for (i = 0; i <= 3; i++) { - if (current_tssi > tssi_bounds[i]) - break; - } - - if (i == 4) { - for (i = 8; i >= 5; i--) { - if (current_tssi < tssi_bounds[i]) - break; - } - } - - return (i - 4) * step; -} - -static int rt2800_get_txpower_bw_comp(struct rt2x00_dev *rt2x00dev, - enum ieee80211_band band) -{ - u16 eeprom; - u8 comp_en; - u8 comp_type; - int comp_value = 0; - - rt2800_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom); - - /* - * HT40 compensation not required. - */ - if (eeprom == 0xffff || - !test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) - return 0; - - if (band == IEEE80211_BAND_2GHZ) { - comp_en = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_DELTA_ENABLE_2G); - if (comp_en) { - comp_type = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_DELTA_TYPE_2G); - comp_value = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_DELTA_VALUE_2G); - if (!comp_type) - comp_value = -comp_value; - } - } else { - comp_en = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_DELTA_ENABLE_5G); - if (comp_en) { - comp_type = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_DELTA_TYPE_5G); - comp_value = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_DELTA_VALUE_5G); - if (!comp_type) - comp_value = -comp_value; - } - } - - return comp_value; -} - -static int rt2800_get_txpower_reg_delta(struct rt2x00_dev *rt2x00dev, - int power_level, int max_power) -{ - int delta; - - if (rt2x00_has_cap_power_limit(rt2x00dev)) - return 0; - - /* - * XXX: We don't know the maximum transmit power of our hardware since - * the EEPROM doesn't expose it. We only know that we are calibrated - * to 100% tx power. - * - * Hence, we assume the regulatory limit that cfg80211 calulated for - * the current channel is our maximum and if we are requested to lower - * the value we just reduce our tx power accordingly. - */ - delta = power_level - max_power; - return min(delta, 0); -} - -static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b, - enum ieee80211_band band, int power_level, - u8 txpower, int delta) -{ - u16 eeprom; - u8 criterion; - u8 eirp_txpower; - u8 eirp_txpower_criterion; - u8 reg_limit; - - if (rt2x00_rt(rt2x00dev, RT3593)) - return min_t(u8, txpower, 0xc); - - if (rt2x00_has_cap_power_limit(rt2x00dev)) { - /* - * Check if eirp txpower exceed txpower_limit. - * We use OFDM 6M as criterion and its eirp txpower - * is stored at EEPROM_EIRP_MAX_TX_POWER. - * .11b data rate need add additional 4dbm - * when calculating eirp txpower. - */ - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, - 1, &eeprom); - criterion = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_BYRATE_RATE0); - - rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, - &eeprom); - - if (band == IEEE80211_BAND_2GHZ) - eirp_txpower_criterion = rt2x00_get_field16(eeprom, - EEPROM_EIRP_MAX_TX_POWER_2GHZ); - else - eirp_txpower_criterion = rt2x00_get_field16(eeprom, - EEPROM_EIRP_MAX_TX_POWER_5GHZ); - - eirp_txpower = eirp_txpower_criterion + (txpower - criterion) + - (is_rate_b ? 4 : 0) + delta; - - reg_limit = (eirp_txpower > power_level) ? - (eirp_txpower - power_level) : 0; - } else - reg_limit = 0; - - txpower = max(0, txpower + delta - reg_limit); - return min_t(u8, txpower, 0xc); -} - - -enum { - TX_PWR_CFG_0_IDX, - TX_PWR_CFG_1_IDX, - TX_PWR_CFG_2_IDX, - TX_PWR_CFG_3_IDX, - TX_PWR_CFG_4_IDX, - TX_PWR_CFG_5_IDX, - TX_PWR_CFG_6_IDX, - TX_PWR_CFG_7_IDX, - TX_PWR_CFG_8_IDX, - TX_PWR_CFG_9_IDX, - TX_PWR_CFG_0_EXT_IDX, - TX_PWR_CFG_1_EXT_IDX, - TX_PWR_CFG_2_EXT_IDX, - TX_PWR_CFG_3_EXT_IDX, - TX_PWR_CFG_4_EXT_IDX, - TX_PWR_CFG_IDX_COUNT, -}; - -static void rt2800_config_txpower_rt3593(struct rt2x00_dev *rt2x00dev, - struct ieee80211_channel *chan, - int power_level) -{ - u8 txpower; - u16 eeprom; - u32 regs[TX_PWR_CFG_IDX_COUNT]; - unsigned int offset; - enum ieee80211_band band = chan->band; - int delta; - int i; - - memset(regs, '\0', sizeof(regs)); - - /* TODO: adapt TX power reduction from the rt28xx code */ - - /* calculate temperature compensation delta */ - delta = rt2800_get_gain_calibration_delta(rt2x00dev); - - if (band == IEEE80211_BAND_5GHZ) - offset = 16; - else - offset = 0; - - if (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) - offset += 8; - - /* read the next four txpower values */ - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, - offset, &eeprom); - - /* CCK 1MBS,2MBS */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); - txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], - TX_PWR_CFG_0_CCK1_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], - TX_PWR_CFG_0_CCK1_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], - TX_PWR_CFG_0_EXT_CCK1_CH2, txpower); - - /* CCK 5.5MBS,11MBS */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); - txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], - TX_PWR_CFG_0_CCK5_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], - TX_PWR_CFG_0_CCK5_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], - TX_PWR_CFG_0_EXT_CCK5_CH2, txpower); - - /* OFDM 6MBS,9MBS */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], - TX_PWR_CFG_0_OFDM6_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], - TX_PWR_CFG_0_OFDM6_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], - TX_PWR_CFG_0_EXT_OFDM6_CH2, txpower); - - /* OFDM 12MBS,18MBS */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], - TX_PWR_CFG_0_OFDM12_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], - TX_PWR_CFG_0_OFDM12_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], - TX_PWR_CFG_0_EXT_OFDM12_CH2, txpower); - - /* read the next four txpower values */ - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, - offset + 1, &eeprom); - - /* OFDM 24MBS,36MBS */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], - TX_PWR_CFG_1_OFDM24_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], - TX_PWR_CFG_1_OFDM24_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], - TX_PWR_CFG_1_EXT_OFDM24_CH2, txpower); - - /* OFDM 48MBS */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], - TX_PWR_CFG_1_OFDM48_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], - TX_PWR_CFG_1_OFDM48_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], - TX_PWR_CFG_1_EXT_OFDM48_CH2, txpower); - - /* OFDM 54MBS */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], - TX_PWR_CFG_7_OFDM54_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], - TX_PWR_CFG_7_OFDM54_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], - TX_PWR_CFG_7_OFDM54_CH2, txpower); - - /* read the next four txpower values */ - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, - offset + 2, &eeprom); - - /* MCS 0,1 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], - TX_PWR_CFG_1_MCS0_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], - TX_PWR_CFG_1_MCS0_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], - TX_PWR_CFG_1_EXT_MCS0_CH2, txpower); - - /* MCS 2,3 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], - TX_PWR_CFG_1_MCS2_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], - TX_PWR_CFG_1_MCS2_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], - TX_PWR_CFG_1_EXT_MCS2_CH2, txpower); - - /* MCS 4,5 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], - TX_PWR_CFG_2_MCS4_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], - TX_PWR_CFG_2_MCS4_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], - TX_PWR_CFG_2_EXT_MCS4_CH2, txpower); - - /* MCS 6 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], - TX_PWR_CFG_2_MCS6_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], - TX_PWR_CFG_2_MCS6_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], - TX_PWR_CFG_2_EXT_MCS6_CH2, txpower); - - /* read the next four txpower values */ - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, - offset + 3, &eeprom); - - /* MCS 7 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], - TX_PWR_CFG_7_MCS7_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], - TX_PWR_CFG_7_MCS7_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], - TX_PWR_CFG_7_MCS7_CH2, txpower); - - /* MCS 8,9 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], - TX_PWR_CFG_2_MCS8_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], - TX_PWR_CFG_2_MCS8_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], - TX_PWR_CFG_2_EXT_MCS8_CH2, txpower); - - /* MCS 10,11 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], - TX_PWR_CFG_2_MCS10_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], - TX_PWR_CFG_2_MCS10_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], - TX_PWR_CFG_2_EXT_MCS10_CH2, txpower); - - /* MCS 12,13 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], - TX_PWR_CFG_3_MCS12_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], - TX_PWR_CFG_3_MCS12_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], - TX_PWR_CFG_3_EXT_MCS12_CH2, txpower); - - /* read the next four txpower values */ - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, - offset + 4, &eeprom); - - /* MCS 14 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], - TX_PWR_CFG_3_MCS14_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], - TX_PWR_CFG_3_MCS14_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], - TX_PWR_CFG_3_EXT_MCS14_CH2, txpower); - - /* MCS 15 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], - TX_PWR_CFG_8_MCS15_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], - TX_PWR_CFG_8_MCS15_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], - TX_PWR_CFG_8_MCS15_CH2, txpower); - - /* MCS 16,17 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], - TX_PWR_CFG_5_MCS16_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], - TX_PWR_CFG_5_MCS16_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], - TX_PWR_CFG_5_MCS16_CH2, txpower); - - /* MCS 18,19 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], - TX_PWR_CFG_5_MCS18_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], - TX_PWR_CFG_5_MCS18_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], - TX_PWR_CFG_5_MCS18_CH2, txpower); - - /* read the next four txpower values */ - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, - offset + 5, &eeprom); - - /* MCS 20,21 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], - TX_PWR_CFG_6_MCS20_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], - TX_PWR_CFG_6_MCS20_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], - TX_PWR_CFG_6_MCS20_CH2, txpower); - - /* MCS 22 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], - TX_PWR_CFG_6_MCS22_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], - TX_PWR_CFG_6_MCS22_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], - TX_PWR_CFG_6_MCS22_CH2, txpower); - - /* MCS 23 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], - TX_PWR_CFG_8_MCS23_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], - TX_PWR_CFG_8_MCS23_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], - TX_PWR_CFG_8_MCS23_CH2, txpower); - - /* read the next four txpower values */ - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, - offset + 6, &eeprom); - - /* STBC, MCS 0,1 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], - TX_PWR_CFG_3_STBC0_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], - TX_PWR_CFG_3_STBC0_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], - TX_PWR_CFG_3_EXT_STBC0_CH2, txpower); - - /* STBC, MCS 2,3 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], - TX_PWR_CFG_3_STBC2_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], - TX_PWR_CFG_3_STBC2_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], - TX_PWR_CFG_3_EXT_STBC2_CH2, txpower); - - /* STBC, MCS 4,5 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE0, - txpower); - - /* STBC, MCS 6 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE2, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE3, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE2, - txpower); - - /* read the next four txpower values */ - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, - offset + 7, &eeprom); - - /* STBC, MCS 7 */ - txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); - txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, - txpower, delta); - rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], - TX_PWR_CFG_9_STBC7_CH0, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], - TX_PWR_CFG_9_STBC7_CH1, txpower); - rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], - TX_PWR_CFG_9_STBC7_CH2, txpower); - - rt2800_register_write(rt2x00dev, TX_PWR_CFG_0, regs[TX_PWR_CFG_0_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_1, regs[TX_PWR_CFG_1_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_2, regs[TX_PWR_CFG_2_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_3, regs[TX_PWR_CFG_3_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_4, regs[TX_PWR_CFG_4_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_5, regs[TX_PWR_CFG_5_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_6, regs[TX_PWR_CFG_6_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_7, regs[TX_PWR_CFG_7_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_8, regs[TX_PWR_CFG_8_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_9, regs[TX_PWR_CFG_9_IDX]); - - rt2800_register_write(rt2x00dev, TX_PWR_CFG_0_EXT, - regs[TX_PWR_CFG_0_EXT_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_1_EXT, - regs[TX_PWR_CFG_1_EXT_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_2_EXT, - regs[TX_PWR_CFG_2_EXT_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_3_EXT, - regs[TX_PWR_CFG_3_EXT_IDX]); - rt2800_register_write(rt2x00dev, TX_PWR_CFG_4_EXT, - regs[TX_PWR_CFG_4_EXT_IDX]); - - for (i = 0; i < TX_PWR_CFG_IDX_COUNT; i++) - rt2x00_dbg(rt2x00dev, - "band:%cGHz, BW:%c0MHz, TX_PWR_CFG_%d%s = %08lx\n", - (band == IEEE80211_BAND_5GHZ) ? '5' : '2', - (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) ? - '4' : '2', - (i > TX_PWR_CFG_9_IDX) ? - (i - TX_PWR_CFG_9_IDX - 1) : i, - (i > TX_PWR_CFG_9_IDX) ? "_EXT" : "", - (unsigned long) regs[i]); -} - -/* - * We configure transmit power using MAC TX_PWR_CFG_{0,...,N} registers and - * BBP R1 register. TX_PWR_CFG_X allow to configure per rate TX power values, - * 4 bits for each rate (tune from 0 to 15 dBm). BBP_R1 controls transmit power - * for all rates, but allow to set only 4 discrete values: -12, -6, 0 and 6 dBm. - * Reference per rate transmit power values are located in the EEPROM at - * EEPROM_TXPOWER_BYRATE offset. We adjust them and BBP R1 settings according to - * current conditions (i.e. band, bandwidth, temperature, user settings). - */ -static void rt2800_config_txpower_rt28xx(struct rt2x00_dev *rt2x00dev, - struct ieee80211_channel *chan, - int power_level) -{ - u8 txpower, r1; - u16 eeprom; - u32 reg, offset; - int i, is_rate_b, delta, power_ctrl; - enum ieee80211_band band = chan->band; - - /* - * Calculate HT40 compensation. For 40MHz we need to add or subtract - * value read from EEPROM (different for 2GHz and for 5GHz). - */ - delta = rt2800_get_txpower_bw_comp(rt2x00dev, band); - - /* - * Calculate temperature compensation. Depends on measurement of current - * TSSI (Transmitter Signal Strength Indication) we know TX power (due - * to temperature or maybe other factors) is smaller or bigger than - * expected. We adjust it, based on TSSI reference and boundaries values - * provided in EEPROM. - */ - switch (rt2x00dev->chip.rt) { - case RT2860: - case RT2872: - case RT2883: - case RT3070: - case RT3071: - case RT3090: - case RT3572: - delta += rt2800_get_gain_calibration_delta(rt2x00dev); - break; - default: - /* TODO: temperature compensation code for other chips. */ - break; - } - - /* - * Decrease power according to user settings, on devices with unknown - * maximum tx power. For other devices we take user power_level into - * consideration on rt2800_compensate_txpower(). - */ - delta += rt2800_get_txpower_reg_delta(rt2x00dev, power_level, - chan->max_power); - - /* - * BBP_R1 controls TX power for all rates, it allow to set the following - * gains -12, -6, 0, +6 dBm by setting values 2, 1, 0, 3 respectively. - * - * TODO: we do not use +6 dBm option to do not increase power beyond - * regulatory limit, however this could be utilized for devices with - * CAPABILITY_POWER_LIMIT. - */ - if (delta <= -12) { - power_ctrl = 2; - delta += 12; - } else if (delta <= -6) { - power_ctrl = 1; - delta += 6; - } else { - power_ctrl = 0; - } - rt2800_bbp_read(rt2x00dev, 1, &r1); - rt2x00_set_field8(&r1, BBP1_TX_POWER_CTRL, power_ctrl); - rt2800_bbp_write(rt2x00dev, 1, r1); - - offset = TX_PWR_CFG_0; - - for (i = 0; i < EEPROM_TXPOWER_BYRATE_SIZE; i += 2) { - /* just to be safe */ - if (offset > TX_PWR_CFG_4) - break; - - rt2800_register_read(rt2x00dev, offset, ®); - - /* read the next four txpower values */ - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, - i, &eeprom); - - is_rate_b = i ? 0 : 1; - /* - * TX_PWR_CFG_0: 1MBS, TX_PWR_CFG_1: 24MBS, - * TX_PWR_CFG_2: MCS4, TX_PWR_CFG_3: MCS12, - * TX_PWR_CFG_4: unknown - */ - txpower = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_BYRATE_RATE0); - txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, - power_level, txpower, delta); - rt2x00_set_field32(®, TX_PWR_CFG_RATE0, txpower); - - /* - * TX_PWR_CFG_0: 2MBS, TX_PWR_CFG_1: 36MBS, - * TX_PWR_CFG_2: MCS5, TX_PWR_CFG_3: MCS13, - * TX_PWR_CFG_4: unknown - */ - txpower = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_BYRATE_RATE1); - txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, - power_level, txpower, delta); - rt2x00_set_field32(®, TX_PWR_CFG_RATE1, txpower); - - /* - * TX_PWR_CFG_0: 5.5MBS, TX_PWR_CFG_1: 48MBS, - * TX_PWR_CFG_2: MCS6, TX_PWR_CFG_3: MCS14, - * TX_PWR_CFG_4: unknown - */ - txpower = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_BYRATE_RATE2); - txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, - power_level, txpower, delta); - rt2x00_set_field32(®, TX_PWR_CFG_RATE2, txpower); - - /* - * TX_PWR_CFG_0: 11MBS, TX_PWR_CFG_1: 54MBS, - * TX_PWR_CFG_2: MCS7, TX_PWR_CFG_3: MCS15, - * TX_PWR_CFG_4: unknown - */ - txpower = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_BYRATE_RATE3); - txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, - power_level, txpower, delta); - rt2x00_set_field32(®, TX_PWR_CFG_RATE3, txpower); - - /* read the next four txpower values */ - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, - i + 1, &eeprom); - - is_rate_b = 0; - /* - * TX_PWR_CFG_0: 6MBS, TX_PWR_CFG_1: MCS0, - * TX_PWR_CFG_2: MCS8, TX_PWR_CFG_3: unknown, - * TX_PWR_CFG_4: unknown - */ - txpower = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_BYRATE_RATE0); - txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, - power_level, txpower, delta); - rt2x00_set_field32(®, TX_PWR_CFG_RATE4, txpower); - - /* - * TX_PWR_CFG_0: 9MBS, TX_PWR_CFG_1: MCS1, - * TX_PWR_CFG_2: MCS9, TX_PWR_CFG_3: unknown, - * TX_PWR_CFG_4: unknown - */ - txpower = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_BYRATE_RATE1); - txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, - power_level, txpower, delta); - rt2x00_set_field32(®, TX_PWR_CFG_RATE5, txpower); - - /* - * TX_PWR_CFG_0: 12MBS, TX_PWR_CFG_1: MCS2, - * TX_PWR_CFG_2: MCS10, TX_PWR_CFG_3: unknown, - * TX_PWR_CFG_4: unknown - */ - txpower = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_BYRATE_RATE2); - txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, - power_level, txpower, delta); - rt2x00_set_field32(®, TX_PWR_CFG_RATE6, txpower); - - /* - * TX_PWR_CFG_0: 18MBS, TX_PWR_CFG_1: MCS3, - * TX_PWR_CFG_2: MCS11, TX_PWR_CFG_3: unknown, - * TX_PWR_CFG_4: unknown - */ - txpower = rt2x00_get_field16(eeprom, - EEPROM_TXPOWER_BYRATE_RATE3); - txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, - power_level, txpower, delta); - rt2x00_set_field32(®, TX_PWR_CFG_RATE7, txpower); - - rt2800_register_write(rt2x00dev, offset, reg); - - /* next TX_PWR_CFG register */ - offset += 4; - } -} - -static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, - struct ieee80211_channel *chan, - int power_level) -{ - if (rt2x00_rt(rt2x00dev, RT3593)) - rt2800_config_txpower_rt3593(rt2x00dev, chan, power_level); - else - rt2800_config_txpower_rt28xx(rt2x00dev, chan, power_level); -} - -void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev) -{ - rt2800_config_txpower(rt2x00dev, rt2x00dev->hw->conf.chandef.chan, - rt2x00dev->tx_power); -} -EXPORT_SYMBOL_GPL(rt2800_gain_calibration); - -void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) -{ - u32 tx_pin; - u8 rfcsr; - - /* - * A voltage-controlled oscillator(VCO) is an electronic oscillator - * designed to be controlled in oscillation frequency by a voltage - * input. Maybe the temperature will affect the frequency of - * oscillation to be shifted. The VCO calibration will be called - * periodically to adjust the frequency to be precision. - */ - - rt2800_register_read(rt2x00dev, TX_PIN_CFG, &tx_pin); - tx_pin &= TX_PIN_CFG_PA_PE_DISABLE; - rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); - - switch (rt2x00dev->chip.rf) { - case RF2020: - case RF3020: - case RF3021: - case RF3022: - case RF3320: - case RF3052: - rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); - rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); - break; - case RF3053: - case RF3070: - case RF3290: - case RF5360: - case RF5362: - case RF5370: - case RF5372: - case RF5390: - case RF5392: - rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); - rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); - break; - default: - return; - } - - mdelay(1); - - rt2800_register_read(rt2x00dev, TX_PIN_CFG, &tx_pin); - if (rt2x00dev->rf_channel <= 14) { - switch (rt2x00dev->default_ant.tx_chain_num) { - case 3: - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G2_EN, 1); - /* fall through */ - case 2: - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, 1); - /* fall through */ - case 1: - default: - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); - break; - } - } else { - switch (rt2x00dev->default_ant.tx_chain_num) { - case 3: - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A2_EN, 1); - /* fall through */ - case 2: - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, 1); - /* fall through */ - case 1: - default: - rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, 1); - break; - } - } - rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); - -} -EXPORT_SYMBOL_GPL(rt2800_vco_calibration); - -static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - u32 reg; - - rt2800_register_read(rt2x00dev, TX_RTY_CFG, ®); - rt2x00_set_field32(®, TX_RTY_CFG_SHORT_RTY_LIMIT, - libconf->conf->short_frame_max_tx_count); - rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_LIMIT, - libconf->conf->long_frame_max_tx_count); - rt2800_register_write(rt2x00dev, TX_RTY_CFG, reg); -} - -static void rt2800_config_ps(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - enum dev_state state = - (libconf->conf->flags & IEEE80211_CONF_PS) ? - STATE_SLEEP : STATE_AWAKE; - u32 reg; - - if (state == STATE_SLEEP) { - rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0); - - rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 5); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, - libconf->conf->listen_interval - 1); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 1); - rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); - - rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); - } else { - rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 0); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, 0); - rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 0); - rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); - - rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); - } -} - -void rt2800_config(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf, - const unsigned int flags) -{ - /* Always recalculate LNA gain before changing configuration */ - rt2800_config_lna_gain(rt2x00dev, libconf); - - if (flags & IEEE80211_CONF_CHANGE_CHANNEL) { - rt2800_config_channel(rt2x00dev, libconf->conf, - &libconf->rf, &libconf->channel); - rt2800_config_txpower(rt2x00dev, libconf->conf->chandef.chan, - libconf->conf->power_level); - } - if (flags & IEEE80211_CONF_CHANGE_POWER) - rt2800_config_txpower(rt2x00dev, libconf->conf->chandef.chan, - libconf->conf->power_level); - if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) - rt2800_config_retry_limit(rt2x00dev, libconf); - if (flags & IEEE80211_CONF_CHANGE_PS) - rt2800_config_ps(rt2x00dev, libconf); -} -EXPORT_SYMBOL_GPL(rt2800_config); - -/* - * Link tuning - */ -void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) -{ - u32 reg; - - /* - * Update FCS error count from register. - */ - rt2800_register_read(rt2x00dev, RX_STA_CNT0, ®); - qual->rx_failed = rt2x00_get_field32(reg, RX_STA_CNT0_CRC_ERR); -} -EXPORT_SYMBOL_GPL(rt2800_link_stats); - -static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) -{ - u8 vgc; - - if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { - if (rt2x00_rt(rt2x00dev, RT3070) || - rt2x00_rt(rt2x00dev, RT3071) || - rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3390) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT3593) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392) || - rt2x00_rt(rt2x00dev, RT5592)) - vgc = 0x1c + (2 * rt2x00dev->lna_gain); - else - vgc = 0x2e + rt2x00dev->lna_gain; - } else { /* 5GHZ band */ - if (rt2x00_rt(rt2x00dev, RT3593)) - vgc = 0x20 + (rt2x00dev->lna_gain * 5) / 3; - else if (rt2x00_rt(rt2x00dev, RT5592)) - vgc = 0x24 + (2 * rt2x00dev->lna_gain); - else { - if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) - vgc = 0x32 + (rt2x00dev->lna_gain * 5) / 3; - else - vgc = 0x3a + (rt2x00dev->lna_gain * 5) / 3; - } - } - - return vgc; -} - -static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, u8 vgc_level) -{ - if (qual->vgc_level != vgc_level) { - if (rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT3593)) { - rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, - vgc_level); - } else if (rt2x00_rt(rt2x00dev, RT5592)) { - rt2800_bbp_write(rt2x00dev, 83, qual->rssi > -65 ? 0x4a : 0x7a); - rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, vgc_level); - } else { - rt2800_bbp_write(rt2x00dev, 66, vgc_level); - } - - qual->vgc_level = vgc_level; - qual->vgc_level_reg = vgc_level; - } -} - -void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) -{ - rt2800_set_vgc(rt2x00dev, qual, rt2800_get_default_vgc(rt2x00dev)); -} -EXPORT_SYMBOL_GPL(rt2800_reset_tuner); - -void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, - const u32 count) -{ - u8 vgc; - - if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) - return; - - /* When RSSI is better than a certain threshold, increase VGC - * with a chip specific value in order to improve the balance - * between sensibility and noise isolation. - */ - - vgc = rt2800_get_default_vgc(rt2x00dev); - - switch (rt2x00dev->chip.rt) { - case RT3572: - case RT3593: - if (qual->rssi > -65) { - if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) - vgc += 0x20; - else - vgc += 0x10; - } - break; - - case RT5592: - if (qual->rssi > -65) - vgc += 0x20; - break; - - default: - if (qual->rssi > -80) - vgc += 0x10; - break; - } - - rt2800_set_vgc(rt2x00dev, qual, vgc); -} -EXPORT_SYMBOL_GPL(rt2800_link_tuner); - -/* - * Initialization functions. - */ -static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - u16 eeprom; - unsigned int i; - int ret; - - rt2800_disable_wpdma(rt2x00dev); - - ret = rt2800_drv_init_registers(rt2x00dev); - if (ret) - return ret; - - rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f); - rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); - - rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); - - rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, 1600); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TX_TIME_COMPENSATE, 0); - rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); - - rt2800_config_filter(rt2x00dev, FIF_ALLMULTI); - - rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, ®); - rt2x00_set_field32(®, BKOFF_SLOT_CFG_SLOT_TIME, 9); - rt2x00_set_field32(®, BKOFF_SLOT_CFG_CC_DELAY_TIME, 2); - rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg); - - if (rt2x00_rt(rt2x00dev, RT3290)) { - rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); - if (rt2x00_get_field32(reg, WLAN_EN) == 1) { - rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 1); - rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); - } - - rt2800_register_read(rt2x00dev, CMB_CTRL, ®); - if (!(rt2x00_get_field32(reg, LDO0_EN) == 1)) { - rt2x00_set_field32(®, LDO0_EN, 1); - rt2x00_set_field32(®, LDO_BGSEL, 3); - rt2800_register_write(rt2x00dev, CMB_CTRL, reg); - } - - rt2800_register_read(rt2x00dev, OSC_CTRL, ®); - rt2x00_set_field32(®, OSC_ROSC_EN, 1); - rt2x00_set_field32(®, OSC_CAL_REQ, 1); - rt2x00_set_field32(®, OSC_REF_CYCLE, 0x27); - rt2800_register_write(rt2x00dev, OSC_CTRL, reg); - - rt2800_register_read(rt2x00dev, COEX_CFG0, ®); - rt2x00_set_field32(®, COEX_CFG_ANT, 0x5e); - rt2800_register_write(rt2x00dev, COEX_CFG0, reg); - - rt2800_register_read(rt2x00dev, COEX_CFG2, ®); - rt2x00_set_field32(®, BT_COEX_CFG1, 0x00); - rt2x00_set_field32(®, BT_COEX_CFG0, 0x17); - rt2x00_set_field32(®, WL_COEX_CFG1, 0x93); - rt2x00_set_field32(®, WL_COEX_CFG0, 0x7f); - rt2800_register_write(rt2x00dev, COEX_CFG2, reg); - - rt2800_register_read(rt2x00dev, PLL_CTRL, ®); - rt2x00_set_field32(®, PLL_CONTROL, 1); - rt2800_register_write(rt2x00dev, PLL_CTRL, reg); - } - - if (rt2x00_rt(rt2x00dev, RT3071) || - rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3390)) { - - if (rt2x00_rt(rt2x00dev, RT3290)) - rt2800_register_write(rt2x00dev, TX_SW_CFG0, - 0x00000404); - else - rt2800_register_write(rt2x00dev, TX_SW_CFG0, - 0x00000400); - - rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); - if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || - rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || - rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, - &eeprom); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST)) - rt2800_register_write(rt2x00dev, TX_SW_CFG2, - 0x0000002c); - else - rt2800_register_write(rt2x00dev, TX_SW_CFG2, - 0x0000000f); - } else { - rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); - } - } else if (rt2x00_rt(rt2x00dev, RT3070)) { - rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); - - if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) { - rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); - rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x0000002c); - } else { - rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); - rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); - } - } else if (rt2800_is_305x_soc(rt2x00dev)) { - rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); - rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); - rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000030); - } else if (rt2x00_rt(rt2x00dev, RT3352)) { - rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000402); - rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); - rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); - } else if (rt2x00_rt(rt2x00dev, RT3572)) { - rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); - rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); - } else if (rt2x00_rt(rt2x00dev, RT3593)) { - rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000402); - rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); - if (rt2x00_rt_rev_lt(rt2x00dev, RT3593, REV_RT3593E)) { - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, - &eeprom); - if (rt2x00_get_field16(eeprom, - EEPROM_NIC_CONF1_DAC_TEST)) - rt2800_register_write(rt2x00dev, TX_SW_CFG2, - 0x0000001f); - else - rt2800_register_write(rt2x00dev, TX_SW_CFG2, - 0x0000000f); - } else { - rt2800_register_write(rt2x00dev, TX_SW_CFG2, - 0x00000000); - } - } else if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392) || - rt2x00_rt(rt2x00dev, RT5592)) { - rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); - rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); - rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); - } else { - rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000000); - rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); - } - - rt2800_register_read(rt2x00dev, TX_LINK_CFG, ®); - rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB_LIFETIME, 32); - rt2x00_set_field32(®, TX_LINK_CFG_MFB_ENABLE, 0); - rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_UMFS_ENABLE, 0); - rt2x00_set_field32(®, TX_LINK_CFG_TX_MRQ_EN, 0); - rt2x00_set_field32(®, TX_LINK_CFG_TX_RDG_EN, 0); - rt2x00_set_field32(®, TX_LINK_CFG_TX_CF_ACK_EN, 1); - rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB, 0); - rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFS, 0); - rt2800_register_write(rt2x00dev, TX_LINK_CFG, reg); - - rt2800_register_read(rt2x00dev, TX_TIMEOUT_CFG, ®); - rt2x00_set_field32(®, TX_TIMEOUT_CFG_MPDU_LIFETIME, 9); - rt2x00_set_field32(®, TX_TIMEOUT_CFG_RX_ACK_TIMEOUT, 32); - rt2x00_set_field32(®, TX_TIMEOUT_CFG_TX_OP_TIMEOUT, 10); - rt2800_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg); - - rt2800_register_read(rt2x00dev, MAX_LEN_CFG, ®); - rt2x00_set_field32(®, MAX_LEN_CFG_MAX_MPDU, AGGREGATION_SIZE); - if (rt2x00_rt_rev_gte(rt2x00dev, RT2872, REV_RT2872E) || - rt2x00_rt(rt2x00dev, RT2883) || - rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070E)) - rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 2); - else - rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 1); - rt2x00_set_field32(®, MAX_LEN_CFG_MIN_PSDU, 0); - rt2x00_set_field32(®, MAX_LEN_CFG_MIN_MPDU, 0); - rt2800_register_write(rt2x00dev, MAX_LEN_CFG, reg); - - rt2800_register_read(rt2x00dev, LED_CFG, ®); - rt2x00_set_field32(®, LED_CFG_ON_PERIOD, 70); - rt2x00_set_field32(®, LED_CFG_OFF_PERIOD, 30); - rt2x00_set_field32(®, LED_CFG_SLOW_BLINK_PERIOD, 3); - rt2x00_set_field32(®, LED_CFG_R_LED_MODE, 3); - rt2x00_set_field32(®, LED_CFG_G_LED_MODE, 3); - rt2x00_set_field32(®, LED_CFG_Y_LED_MODE, 3); - rt2x00_set_field32(®, LED_CFG_LED_POLAR, 1); - rt2800_register_write(rt2x00dev, LED_CFG, reg); - - rt2800_register_write(rt2x00dev, PBF_MAX_PCNT, 0x1f3fbf9f); - - rt2800_register_read(rt2x00dev, TX_RTY_CFG, ®); - rt2x00_set_field32(®, TX_RTY_CFG_SHORT_RTY_LIMIT, 15); - rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_LIMIT, 31); - rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_THRE, 2000); - rt2x00_set_field32(®, TX_RTY_CFG_NON_AGG_RTY_MODE, 0); - rt2x00_set_field32(®, TX_RTY_CFG_AGG_RTY_MODE, 0); - rt2x00_set_field32(®, TX_RTY_CFG_TX_AUTO_FB_ENABLE, 1); - rt2800_register_write(rt2x00dev, TX_RTY_CFG, reg); - - rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); - rt2x00_set_field32(®, AUTO_RSP_CFG_AUTORESPONDER, 1); - rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, 1); - rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MMODE, 0); - rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MREF, 0); - rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, 1); - rt2x00_set_field32(®, AUTO_RSP_CFG_DUAL_CTS_EN, 0); - rt2x00_set_field32(®, AUTO_RSP_CFG_ACK_CTS_PSM_BIT, 0); - rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); - - rt2800_register_read(rt2x00dev, CCK_PROT_CFG, ®); - rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_RATE, 3); - rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_NAV_SHORT, 1); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM40, 0); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF40, 0); - rt2x00_set_field32(®, CCK_PROT_CFG_RTS_TH_EN, 1); - rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); - rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_RATE, 3); - rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_NAV_SHORT, 1); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM40, 0); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF40, 0); - rt2x00_set_field32(®, OFDM_PROT_CFG_RTS_TH_EN, 1); - rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); - rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_RATE, 0x4004); - rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_NAV_SHORT, 1); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM40, 0); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF40, 0); - rt2x00_set_field32(®, MM20_PROT_CFG_RTS_TH_EN, 0); - rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); - rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_RATE, 0x4084); - rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_NAV_SHORT, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM40, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF40, 1); - rt2x00_set_field32(®, MM40_PROT_CFG_RTS_TH_EN, 0); - rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); - rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_RATE, 0x4004); - rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_NAV_SHORT, 1); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM40, 0); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF40, 0); - rt2x00_set_field32(®, GF20_PROT_CFG_RTS_TH_EN, 0); - rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); - rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_RATE, 0x4084); - rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, 0); - rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_NAV_SHORT, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_CCK, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM20, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM40, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF20, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF40, 1); - rt2x00_set_field32(®, GF40_PROT_CFG_RTS_TH_EN, 0); - rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); - - if (rt2x00_is_usb(rt2x00dev)) { - rt2800_register_write(rt2x00dev, PBF_CFG, 0xf40006); - - rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); - rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 3); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_BIG_ENDIAN, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_HDR_SCATTER, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_HDR_SEG_LEN, 0); - rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); - } - - /* - * The legacy driver also sets TXOP_CTRL_CFG_RESERVED_TRUN_EN to 1 - * although it is reserved. - */ - rt2800_register_read(rt2x00dev, TXOP_CTRL_CFG, ®); - rt2x00_set_field32(®, TXOP_CTRL_CFG_TIMEOUT_TRUN_EN, 1); - rt2x00_set_field32(®, TXOP_CTRL_CFG_AC_TRUN_EN, 1); - rt2x00_set_field32(®, TXOP_CTRL_CFG_TXRATEGRP_TRUN_EN, 1); - rt2x00_set_field32(®, TXOP_CTRL_CFG_USER_MODE_TRUN_EN, 1); - rt2x00_set_field32(®, TXOP_CTRL_CFG_MIMO_PS_TRUN_EN, 1); - rt2x00_set_field32(®, TXOP_CTRL_CFG_RESERVED_TRUN_EN, 1); - rt2x00_set_field32(®, TXOP_CTRL_CFG_LSIG_TXOP_EN, 0); - rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CCA_EN, 0); - rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CCA_DLY, 88); - rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CWMIN, 0); - rt2800_register_write(rt2x00dev, TXOP_CTRL_CFG, reg); - - reg = rt2x00_rt(rt2x00dev, RT5592) ? 0x00000082 : 0x00000002; - rt2800_register_write(rt2x00dev, TXOP_HLDR_ET, reg); - - rt2800_register_read(rt2x00dev, TX_RTS_CFG, ®); - rt2x00_set_field32(®, TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT, 32); - rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, - IEEE80211_MAX_RTS_THRESHOLD); - rt2x00_set_field32(®, TX_RTS_CFG_RTS_FBK_EN, 0); - rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg); - - rt2800_register_write(rt2x00dev, EXP_ACK_TIME, 0x002400ca); - - /* - * Usually the CCK SIFS time should be set to 10 and the OFDM SIFS - * time should be set to 16. However, the original Ralink driver uses - * 16 for both and indeed using a value of 10 for CCK SIFS results in - * connection problems with 11g + CTS protection. Hence, use the same - * defaults as the Ralink driver: 16 for both, CCK and OFDM SIFS. - */ - rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, ®); - rt2x00_set_field32(®, XIFS_TIME_CFG_CCKM_SIFS_TIME, 16); - rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_SIFS_TIME, 16); - rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_XIFS_TIME, 4); - rt2x00_set_field32(®, XIFS_TIME_CFG_EIFS, 314); - rt2x00_set_field32(®, XIFS_TIME_CFG_BB_RXEND_ENABLE, 1); - rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg); - - rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); - - /* - * ASIC will keep garbage value after boot, clear encryption keys. - */ - for (i = 0; i < 4; i++) - rt2800_register_write(rt2x00dev, - SHARED_KEY_MODE_ENTRY(i), 0); - - for (i = 0; i < 256; i++) { - rt2800_config_wcid(rt2x00dev, NULL, i); - rt2800_delete_wcid_attr(rt2x00dev, i); - rt2800_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0); - } - - /* - * Clear all beacons - */ - for (i = 0; i < 8; i++) - rt2800_clear_beacon_register(rt2x00dev, i); - - if (rt2x00_is_usb(rt2x00dev)) { - rt2800_register_read(rt2x00dev, US_CYC_CNT, ®); - rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, 30); - rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); - } else if (rt2x00_is_pcie(rt2x00dev)) { - rt2800_register_read(rt2x00dev, US_CYC_CNT, ®); - rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, 125); - rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); - } - - rt2800_register_read(rt2x00dev, HT_FBK_CFG0, ®); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS0FBK, 0); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS1FBK, 0); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS2FBK, 1); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS3FBK, 2); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS4FBK, 3); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS5FBK, 4); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS6FBK, 5); - rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS7FBK, 6); - rt2800_register_write(rt2x00dev, HT_FBK_CFG0, reg); - - rt2800_register_read(rt2x00dev, HT_FBK_CFG1, ®); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS8FBK, 8); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS9FBK, 8); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS10FBK, 9); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS11FBK, 10); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS12FBK, 11); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS13FBK, 12); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS14FBK, 13); - rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS15FBK, 14); - rt2800_register_write(rt2x00dev, HT_FBK_CFG1, reg); - - rt2800_register_read(rt2x00dev, LG_FBK_CFG0, ®); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS0FBK, 8); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS1FBK, 8); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS2FBK, 9); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS3FBK, 10); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS4FBK, 11); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS5FBK, 12); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS6FBK, 13); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS7FBK, 14); - rt2800_register_write(rt2x00dev, LG_FBK_CFG0, reg); - - rt2800_register_read(rt2x00dev, LG_FBK_CFG1, ®); - rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS0FBK, 0); - rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS1FBK, 0); - rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS2FBK, 1); - rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS3FBK, 2); - rt2800_register_write(rt2x00dev, LG_FBK_CFG1, reg); - - /* - * Do not force the BA window size, we use the TXWI to set it - */ - rt2800_register_read(rt2x00dev, AMPDU_BA_WINSIZE, ®); - rt2x00_set_field32(®, AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE, 0); - rt2x00_set_field32(®, AMPDU_BA_WINSIZE_FORCE_WINSIZE, 0); - rt2800_register_write(rt2x00dev, AMPDU_BA_WINSIZE, reg); - - /* - * We must clear the error counters. - * These registers are cleared on read, - * so we may pass a useless variable to store the value. - */ - rt2800_register_read(rt2x00dev, RX_STA_CNT0, ®); - rt2800_register_read(rt2x00dev, RX_STA_CNT1, ®); - rt2800_register_read(rt2x00dev, RX_STA_CNT2, ®); - rt2800_register_read(rt2x00dev, TX_STA_CNT0, ®); - rt2800_register_read(rt2x00dev, TX_STA_CNT1, ®); - rt2800_register_read(rt2x00dev, TX_STA_CNT2, ®); - - /* - * Setup leadtime for pre tbtt interrupt to 6ms - */ - rt2800_register_read(rt2x00dev, INT_TIMER_CFG, ®); - rt2x00_set_field32(®, INT_TIMER_CFG_PRE_TBTT_TIMER, 6 << 4); - rt2800_register_write(rt2x00dev, INT_TIMER_CFG, reg); - - /* - * Set up channel statistics timer - */ - rt2800_register_read(rt2x00dev, CH_TIME_CFG, ®); - rt2x00_set_field32(®, CH_TIME_CFG_EIFS_BUSY, 1); - rt2x00_set_field32(®, CH_TIME_CFG_NAV_BUSY, 1); - rt2x00_set_field32(®, CH_TIME_CFG_RX_BUSY, 1); - rt2x00_set_field32(®, CH_TIME_CFG_TX_BUSY, 1); - rt2x00_set_field32(®, CH_TIME_CFG_TMR_EN, 1); - rt2800_register_write(rt2x00dev, CH_TIME_CFG, reg); - - return 0; -} - -static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u32 reg; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2800_register_read(rt2x00dev, MAC_STATUS_CFG, ®); - if (!rt2x00_get_field32(reg, MAC_STATUS_CFG_BBP_RF_BUSY)) - return 0; - - udelay(REGISTER_BUSY_DELAY); - } - - rt2x00_err(rt2x00dev, "BBP/RF register access failed, aborting\n"); - return -EACCES; -} - -static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u8 value; - - /* - * BBP was enabled after firmware was loaded, - * but we need to reactivate it now. - */ - rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); - rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); - msleep(1); - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2800_bbp_read(rt2x00dev, 0, &value); - if ((value != 0xff) && (value != 0x00)) - return 0; - udelay(REGISTER_BUSY_DELAY); - } - - rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); - return -EACCES; -} - -static void rt2800_bbp4_mac_if_ctrl(struct rt2x00_dev *rt2x00dev) -{ - u8 value; - - rt2800_bbp_read(rt2x00dev, 4, &value); - rt2x00_set_field8(&value, BBP4_MAC_IF_CTRL, 1); - rt2800_bbp_write(rt2x00dev, 4, value); -} - -static void rt2800_init_freq_calibration(struct rt2x00_dev *rt2x00dev) -{ - rt2800_bbp_write(rt2x00dev, 142, 1); - rt2800_bbp_write(rt2x00dev, 143, 57); -} - -static void rt2800_init_bbp_5592_glrt(struct rt2x00_dev *rt2x00dev) -{ - const u8 glrt_table[] = { - 0xE0, 0x1F, 0X38, 0x32, 0x08, 0x28, 0x19, 0x0A, 0xFF, 0x00, /* 128 ~ 137 */ - 0x16, 0x10, 0x10, 0x0B, 0x36, 0x2C, 0x26, 0x24, 0x42, 0x36, /* 138 ~ 147 */ - 0x30, 0x2D, 0x4C, 0x46, 0x3D, 0x40, 0x3E, 0x42, 0x3D, 0x40, /* 148 ~ 157 */ - 0X3C, 0x34, 0x2C, 0x2F, 0x3C, 0x35, 0x2E, 0x2A, 0x49, 0x41, /* 158 ~ 167 */ - 0x36, 0x31, 0x30, 0x30, 0x0E, 0x0D, 0x28, 0x21, 0x1C, 0x16, /* 168 ~ 177 */ - 0x50, 0x4A, 0x43, 0x40, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, /* 178 ~ 187 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 188 ~ 197 */ - 0x00, 0x00, 0x7D, 0x14, 0x32, 0x2C, 0x36, 0x4C, 0x43, 0x2C, /* 198 ~ 207 */ - 0x2E, 0x36, 0x30, 0x6E, /* 208 ~ 211 */ - }; - int i; - - for (i = 0; i < ARRAY_SIZE(glrt_table); i++) { - rt2800_bbp_write(rt2x00dev, 195, 128 + i); - rt2800_bbp_write(rt2x00dev, 196, glrt_table[i]); - } -}; - -static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev) -{ - rt2800_bbp_write(rt2x00dev, 65, 0x2C); - rt2800_bbp_write(rt2x00dev, 66, 0x38); - rt2800_bbp_write(rt2x00dev, 68, 0x0B); - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - rt2800_bbp_write(rt2x00dev, 73, 0x10); - rt2800_bbp_write(rt2x00dev, 81, 0x37); - rt2800_bbp_write(rt2x00dev, 82, 0x62); - rt2800_bbp_write(rt2x00dev, 83, 0x6A); - rt2800_bbp_write(rt2x00dev, 84, 0x99); - rt2800_bbp_write(rt2x00dev, 86, 0x00); - rt2800_bbp_write(rt2x00dev, 91, 0x04); - rt2800_bbp_write(rt2x00dev, 92, 0x00); - rt2800_bbp_write(rt2x00dev, 103, 0x00); - rt2800_bbp_write(rt2x00dev, 105, 0x05); - rt2800_bbp_write(rt2x00dev, 106, 0x35); -} - -static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev) -{ - u16 eeprom; - u8 value; - - rt2800_bbp_read(rt2x00dev, 138, &value); - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) - value |= 0x20; - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) - value &= ~0x02; - rt2800_bbp_write(rt2x00dev, 138, value); -} - -static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) -{ - rt2800_bbp_write(rt2x00dev, 31, 0x08); - - rt2800_bbp_write(rt2x00dev, 65, 0x2c); - rt2800_bbp_write(rt2x00dev, 66, 0x38); - - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x10); - - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - - rt2800_bbp_write(rt2x00dev, 78, 0x0e); - rt2800_bbp_write(rt2x00dev, 80, 0x08); - - rt2800_bbp_write(rt2x00dev, 82, 0x62); - - rt2800_bbp_write(rt2x00dev, 83, 0x6a); - - rt2800_bbp_write(rt2x00dev, 84, 0x99); - - rt2800_bbp_write(rt2x00dev, 86, 0x00); - - rt2800_bbp_write(rt2x00dev, 91, 0x04); - - rt2800_bbp_write(rt2x00dev, 92, 0x00); - - rt2800_bbp_write(rt2x00dev, 103, 0xc0); - - rt2800_bbp_write(rt2x00dev, 105, 0x01); - - rt2800_bbp_write(rt2x00dev, 106, 0x35); -} - -static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) -{ - rt2800_bbp_write(rt2x00dev, 65, 0x2c); - rt2800_bbp_write(rt2x00dev, 66, 0x38); - - if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { - rt2800_bbp_write(rt2x00dev, 69, 0x16); - rt2800_bbp_write(rt2x00dev, 73, 0x12); - } else { - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x10); - } - - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - - rt2800_bbp_write(rt2x00dev, 81, 0x37); - - rt2800_bbp_write(rt2x00dev, 82, 0x62); - - rt2800_bbp_write(rt2x00dev, 83, 0x6a); - - if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D)) - rt2800_bbp_write(rt2x00dev, 84, 0x19); - else - rt2800_bbp_write(rt2x00dev, 84, 0x99); - - rt2800_bbp_write(rt2x00dev, 86, 0x00); - - rt2800_bbp_write(rt2x00dev, 91, 0x04); - - rt2800_bbp_write(rt2x00dev, 92, 0x00); - - rt2800_bbp_write(rt2x00dev, 103, 0x00); - - rt2800_bbp_write(rt2x00dev, 105, 0x05); - - rt2800_bbp_write(rt2x00dev, 106, 0x35); -} - -static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) -{ - rt2800_bbp_write(rt2x00dev, 65, 0x2c); - rt2800_bbp_write(rt2x00dev, 66, 0x38); - - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x10); - - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - - rt2800_bbp_write(rt2x00dev, 79, 0x13); - rt2800_bbp_write(rt2x00dev, 80, 0x05); - rt2800_bbp_write(rt2x00dev, 81, 0x33); - - rt2800_bbp_write(rt2x00dev, 82, 0x62); - - rt2800_bbp_write(rt2x00dev, 83, 0x6a); - - rt2800_bbp_write(rt2x00dev, 84, 0x99); - - rt2800_bbp_write(rt2x00dev, 86, 0x00); - - rt2800_bbp_write(rt2x00dev, 91, 0x04); - - rt2800_bbp_write(rt2x00dev, 92, 0x00); - - if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) || - rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) || - rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E)) - rt2800_bbp_write(rt2x00dev, 103, 0xc0); - else - rt2800_bbp_write(rt2x00dev, 103, 0x00); - - rt2800_bbp_write(rt2x00dev, 105, 0x05); - - rt2800_bbp_write(rt2x00dev, 106, 0x35); - - if (rt2x00_rt(rt2x00dev, RT3071) || - rt2x00_rt(rt2x00dev, RT3090)) - rt2800_disable_unused_dac_adc(rt2x00dev); -} - -static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) -{ - u8 value; - - rt2800_bbp4_mac_if_ctrl(rt2x00dev); - - rt2800_bbp_write(rt2x00dev, 31, 0x08); - - rt2800_bbp_write(rt2x00dev, 65, 0x2c); - rt2800_bbp_write(rt2x00dev, 66, 0x38); - - rt2800_bbp_write(rt2x00dev, 68, 0x0b); - - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x13); - rt2800_bbp_write(rt2x00dev, 75, 0x46); - rt2800_bbp_write(rt2x00dev, 76, 0x28); - - rt2800_bbp_write(rt2x00dev, 77, 0x58); - - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - - rt2800_bbp_write(rt2x00dev, 74, 0x0b); - rt2800_bbp_write(rt2x00dev, 79, 0x18); - rt2800_bbp_write(rt2x00dev, 80, 0x09); - rt2800_bbp_write(rt2x00dev, 81, 0x33); - - rt2800_bbp_write(rt2x00dev, 82, 0x62); - - rt2800_bbp_write(rt2x00dev, 83, 0x7a); - - rt2800_bbp_write(rt2x00dev, 84, 0x9a); - - rt2800_bbp_write(rt2x00dev, 86, 0x38); - - rt2800_bbp_write(rt2x00dev, 91, 0x04); - - rt2800_bbp_write(rt2x00dev, 92, 0x02); - - rt2800_bbp_write(rt2x00dev, 103, 0xc0); - - rt2800_bbp_write(rt2x00dev, 104, 0x92); - - rt2800_bbp_write(rt2x00dev, 105, 0x1c); - - rt2800_bbp_write(rt2x00dev, 106, 0x03); - - rt2800_bbp_write(rt2x00dev, 128, 0x12); - - rt2800_bbp_write(rt2x00dev, 67, 0x24); - rt2800_bbp_write(rt2x00dev, 143, 0x04); - rt2800_bbp_write(rt2x00dev, 142, 0x99); - rt2800_bbp_write(rt2x00dev, 150, 0x30); - rt2800_bbp_write(rt2x00dev, 151, 0x2e); - rt2800_bbp_write(rt2x00dev, 152, 0x20); - rt2800_bbp_write(rt2x00dev, 153, 0x34); - rt2800_bbp_write(rt2x00dev, 154, 0x40); - rt2800_bbp_write(rt2x00dev, 155, 0x3b); - rt2800_bbp_write(rt2x00dev, 253, 0x04); - - rt2800_bbp_read(rt2x00dev, 47, &value); - rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1); - rt2800_bbp_write(rt2x00dev, 47, value); - - /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */ - rt2800_bbp_read(rt2x00dev, 3, &value); - rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1); - rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1); - rt2800_bbp_write(rt2x00dev, 3, value); -} - -static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) -{ - rt2800_bbp_write(rt2x00dev, 3, 0x00); - rt2800_bbp_write(rt2x00dev, 4, 0x50); - - rt2800_bbp_write(rt2x00dev, 31, 0x08); - - rt2800_bbp_write(rt2x00dev, 47, 0x48); - - rt2800_bbp_write(rt2x00dev, 65, 0x2c); - rt2800_bbp_write(rt2x00dev, 66, 0x38); - - rt2800_bbp_write(rt2x00dev, 68, 0x0b); - - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x13); - rt2800_bbp_write(rt2x00dev, 75, 0x46); - rt2800_bbp_write(rt2x00dev, 76, 0x28); - - rt2800_bbp_write(rt2x00dev, 77, 0x59); - - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - - rt2800_bbp_write(rt2x00dev, 78, 0x0e); - rt2800_bbp_write(rt2x00dev, 80, 0x08); - rt2800_bbp_write(rt2x00dev, 81, 0x37); - - rt2800_bbp_write(rt2x00dev, 82, 0x62); - - rt2800_bbp_write(rt2x00dev, 83, 0x6a); - - rt2800_bbp_write(rt2x00dev, 84, 0x99); - - rt2800_bbp_write(rt2x00dev, 86, 0x38); - - rt2800_bbp_write(rt2x00dev, 88, 0x90); - - rt2800_bbp_write(rt2x00dev, 91, 0x04); - - rt2800_bbp_write(rt2x00dev, 92, 0x02); - - rt2800_bbp_write(rt2x00dev, 103, 0xc0); - - rt2800_bbp_write(rt2x00dev, 104, 0x92); - - rt2800_bbp_write(rt2x00dev, 105, 0x34); - - rt2800_bbp_write(rt2x00dev, 106, 0x05); - - rt2800_bbp_write(rt2x00dev, 120, 0x50); - - rt2800_bbp_write(rt2x00dev, 137, 0x0f); - - rt2800_bbp_write(rt2x00dev, 163, 0xbd); - /* Set ITxBF timeout to 0x9c40=1000msec */ - rt2800_bbp_write(rt2x00dev, 179, 0x02); - rt2800_bbp_write(rt2x00dev, 180, 0x00); - rt2800_bbp_write(rt2x00dev, 182, 0x40); - rt2800_bbp_write(rt2x00dev, 180, 0x01); - rt2800_bbp_write(rt2x00dev, 182, 0x9c); - rt2800_bbp_write(rt2x00dev, 179, 0x00); - /* Reprogram the inband interface to put right values in RXWI */ - rt2800_bbp_write(rt2x00dev, 142, 0x04); - rt2800_bbp_write(rt2x00dev, 143, 0x3b); - rt2800_bbp_write(rt2x00dev, 142, 0x06); - rt2800_bbp_write(rt2x00dev, 143, 0xa0); - rt2800_bbp_write(rt2x00dev, 142, 0x07); - rt2800_bbp_write(rt2x00dev, 143, 0xa1); - rt2800_bbp_write(rt2x00dev, 142, 0x08); - rt2800_bbp_write(rt2x00dev, 143, 0xa2); - - rt2800_bbp_write(rt2x00dev, 148, 0xc8); -} - -static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) -{ - rt2800_bbp_write(rt2x00dev, 65, 0x2c); - rt2800_bbp_write(rt2x00dev, 66, 0x38); - - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x10); - - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - - rt2800_bbp_write(rt2x00dev, 79, 0x13); - rt2800_bbp_write(rt2x00dev, 80, 0x05); - rt2800_bbp_write(rt2x00dev, 81, 0x33); - - rt2800_bbp_write(rt2x00dev, 82, 0x62); - - rt2800_bbp_write(rt2x00dev, 83, 0x6a); - - rt2800_bbp_write(rt2x00dev, 84, 0x99); - - rt2800_bbp_write(rt2x00dev, 86, 0x00); - - rt2800_bbp_write(rt2x00dev, 91, 0x04); - - rt2800_bbp_write(rt2x00dev, 92, 0x00); - - if (rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E)) - rt2800_bbp_write(rt2x00dev, 103, 0xc0); - else - rt2800_bbp_write(rt2x00dev, 103, 0x00); - - rt2800_bbp_write(rt2x00dev, 105, 0x05); - - rt2800_bbp_write(rt2x00dev, 106, 0x35); - - rt2800_disable_unused_dac_adc(rt2x00dev); -} - -static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) -{ - rt2800_bbp_write(rt2x00dev, 31, 0x08); - - rt2800_bbp_write(rt2x00dev, 65, 0x2c); - rt2800_bbp_write(rt2x00dev, 66, 0x38); - - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x10); - - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - - rt2800_bbp_write(rt2x00dev, 79, 0x13); - rt2800_bbp_write(rt2x00dev, 80, 0x05); - rt2800_bbp_write(rt2x00dev, 81, 0x33); - - rt2800_bbp_write(rt2x00dev, 82, 0x62); - - rt2800_bbp_write(rt2x00dev, 83, 0x6a); - - rt2800_bbp_write(rt2x00dev, 84, 0x99); - - rt2800_bbp_write(rt2x00dev, 86, 0x00); - - rt2800_bbp_write(rt2x00dev, 91, 0x04); - - rt2800_bbp_write(rt2x00dev, 92, 0x00); - - rt2800_bbp_write(rt2x00dev, 103, 0xc0); - - rt2800_bbp_write(rt2x00dev, 105, 0x05); - - rt2800_bbp_write(rt2x00dev, 106, 0x35); - - rt2800_disable_unused_dac_adc(rt2x00dev); -} - -static void rt2800_init_bbp_3593(struct rt2x00_dev *rt2x00dev) -{ - rt2800_init_bbp_early(rt2x00dev); - - rt2800_bbp_write(rt2x00dev, 79, 0x13); - rt2800_bbp_write(rt2x00dev, 80, 0x05); - rt2800_bbp_write(rt2x00dev, 81, 0x33); - rt2800_bbp_write(rt2x00dev, 137, 0x0f); - - rt2800_bbp_write(rt2x00dev, 84, 0x19); - - /* Enable DC filter */ - if (rt2x00_rt_rev_gte(rt2x00dev, RT3593, REV_RT3593E)) - rt2800_bbp_write(rt2x00dev, 103, 0xc0); -} - -static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) -{ - int ant, div_mode; - u16 eeprom; - u8 value; - - rt2800_bbp4_mac_if_ctrl(rt2x00dev); - - rt2800_bbp_write(rt2x00dev, 31, 0x08); - - rt2800_bbp_write(rt2x00dev, 65, 0x2c); - rt2800_bbp_write(rt2x00dev, 66, 0x38); - - rt2800_bbp_write(rt2x00dev, 68, 0x0b); - - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x13); - rt2800_bbp_write(rt2x00dev, 75, 0x46); - rt2800_bbp_write(rt2x00dev, 76, 0x28); - - rt2800_bbp_write(rt2x00dev, 77, 0x59); - - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - - rt2800_bbp_write(rt2x00dev, 79, 0x13); - rt2800_bbp_write(rt2x00dev, 80, 0x05); - rt2800_bbp_write(rt2x00dev, 81, 0x33); - - rt2800_bbp_write(rt2x00dev, 82, 0x62); - - rt2800_bbp_write(rt2x00dev, 83, 0x7a); - - rt2800_bbp_write(rt2x00dev, 84, 0x9a); - - rt2800_bbp_write(rt2x00dev, 86, 0x38); - - if (rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 88, 0x90); - - rt2800_bbp_write(rt2x00dev, 91, 0x04); - - rt2800_bbp_write(rt2x00dev, 92, 0x02); - - if (rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_bbp_write(rt2x00dev, 95, 0x9a); - rt2800_bbp_write(rt2x00dev, 98, 0x12); - } - - rt2800_bbp_write(rt2x00dev, 103, 0xc0); - - rt2800_bbp_write(rt2x00dev, 104, 0x92); - - rt2800_bbp_write(rt2x00dev, 105, 0x3c); - - if (rt2x00_rt(rt2x00dev, RT5390)) - rt2800_bbp_write(rt2x00dev, 106, 0x03); - else if (rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 106, 0x12); - else - WARN_ON(1); - - rt2800_bbp_write(rt2x00dev, 128, 0x12); - - if (rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_bbp_write(rt2x00dev, 134, 0xd0); - rt2800_bbp_write(rt2x00dev, 135, 0xf6); - } - - rt2800_disable_unused_dac_adc(rt2x00dev); - - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); - div_mode = rt2x00_get_field16(eeprom, - EEPROM_NIC_CONF1_ANT_DIVERSITY); - ant = (div_mode == 3) ? 1 : 0; - - /* check if this is a Bluetooth combo card */ - if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { - u32 reg; - - rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); - rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); - rt2x00_set_field32(®, GPIO_CTRL_DIR6, 0); - rt2x00_set_field32(®, GPIO_CTRL_VAL3, 0); - rt2x00_set_field32(®, GPIO_CTRL_VAL6, 0); - if (ant == 0) - rt2x00_set_field32(®, GPIO_CTRL_VAL3, 1); - else if (ant == 1) - rt2x00_set_field32(®, GPIO_CTRL_VAL6, 1); - rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); - } - - /* This chip has hardware antenna diversity*/ - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { - rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */ - rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */ - rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */ - } - - rt2800_bbp_read(rt2x00dev, 152, &value); - if (ant == 0) - rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); - else - rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); - rt2800_bbp_write(rt2x00dev, 152, value); - - rt2800_init_freq_calibration(rt2x00dev); -} - -static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) -{ - int ant, div_mode; - u16 eeprom; - u8 value; - - rt2800_init_bbp_early(rt2x00dev); - - rt2800_bbp_read(rt2x00dev, 105, &value); - rt2x00_set_field8(&value, BBP105_MLD, - rt2x00dev->default_ant.rx_chain_num == 2); - rt2800_bbp_write(rt2x00dev, 105, value); - - rt2800_bbp4_mac_if_ctrl(rt2x00dev); - - rt2800_bbp_write(rt2x00dev, 20, 0x06); - rt2800_bbp_write(rt2x00dev, 31, 0x08); - rt2800_bbp_write(rt2x00dev, 65, 0x2C); - rt2800_bbp_write(rt2x00dev, 68, 0xDD); - rt2800_bbp_write(rt2x00dev, 69, 0x1A); - rt2800_bbp_write(rt2x00dev, 70, 0x05); - rt2800_bbp_write(rt2x00dev, 73, 0x13); - rt2800_bbp_write(rt2x00dev, 74, 0x0F); - rt2800_bbp_write(rt2x00dev, 75, 0x4F); - rt2800_bbp_write(rt2x00dev, 76, 0x28); - rt2800_bbp_write(rt2x00dev, 77, 0x59); - rt2800_bbp_write(rt2x00dev, 84, 0x9A); - rt2800_bbp_write(rt2x00dev, 86, 0x38); - rt2800_bbp_write(rt2x00dev, 88, 0x90); - rt2800_bbp_write(rt2x00dev, 91, 0x04); - rt2800_bbp_write(rt2x00dev, 92, 0x02); - rt2800_bbp_write(rt2x00dev, 95, 0x9a); - rt2800_bbp_write(rt2x00dev, 98, 0x12); - rt2800_bbp_write(rt2x00dev, 103, 0xC0); - rt2800_bbp_write(rt2x00dev, 104, 0x92); - /* FIXME BBP105 owerwrite */ - rt2800_bbp_write(rt2x00dev, 105, 0x3C); - rt2800_bbp_write(rt2x00dev, 106, 0x35); - rt2800_bbp_write(rt2x00dev, 128, 0x12); - rt2800_bbp_write(rt2x00dev, 134, 0xD0); - rt2800_bbp_write(rt2x00dev, 135, 0xF6); - rt2800_bbp_write(rt2x00dev, 137, 0x0F); - - /* Initialize GLRT (Generalized Likehood Radio Test) */ - rt2800_init_bbp_5592_glrt(rt2x00dev); - - rt2800_bbp4_mac_if_ctrl(rt2x00dev); - - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); - div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY); - ant = (div_mode == 3) ? 1 : 0; - rt2800_bbp_read(rt2x00dev, 152, &value); - if (ant == 0) { - /* Main antenna */ - rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); - } else { - /* Auxiliary antenna */ - rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); - } - rt2800_bbp_write(rt2x00dev, 152, value); - - if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) { - rt2800_bbp_read(rt2x00dev, 254, &value); - rt2x00_set_field8(&value, BBP254_BIT7, 1); - rt2800_bbp_write(rt2x00dev, 254, value); - } - - rt2800_init_freq_calibration(rt2x00dev); - - rt2800_bbp_write(rt2x00dev, 84, 0x19); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) - rt2800_bbp_write(rt2x00dev, 103, 0xc0); -} - -static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u16 eeprom; - u8 reg_id; - u8 value; - - if (rt2800_is_305x_soc(rt2x00dev)) - rt2800_init_bbp_305x_soc(rt2x00dev); - - switch (rt2x00dev->chip.rt) { - case RT2860: - case RT2872: - case RT2883: - rt2800_init_bbp_28xx(rt2x00dev); - break; - case RT3070: - case RT3071: - case RT3090: - rt2800_init_bbp_30xx(rt2x00dev); - break; - case RT3290: - rt2800_init_bbp_3290(rt2x00dev); - break; - case RT3352: - rt2800_init_bbp_3352(rt2x00dev); - break; - case RT3390: - rt2800_init_bbp_3390(rt2x00dev); - break; - case RT3572: - rt2800_init_bbp_3572(rt2x00dev); - break; - case RT3593: - rt2800_init_bbp_3593(rt2x00dev); - return; - case RT5390: - case RT5392: - rt2800_init_bbp_53xx(rt2x00dev); - break; - case RT5592: - rt2800_init_bbp_5592(rt2x00dev); - return; - } - - for (i = 0; i < EEPROM_BBP_SIZE; i++) { - rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_BBP_START, i, - &eeprom); - - if (eeprom != 0xffff && eeprom != 0x0000) { - reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); - value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - rt2800_bbp_write(rt2x00dev, reg_id, value); - } - } -} - -static void rt2800_led_open_drain_enable(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2800_register_read(rt2x00dev, OPT_14_CSR, ®); - rt2x00_set_field32(®, OPT_14_CSR_BIT0, 1); - rt2800_register_write(rt2x00dev, OPT_14_CSR, reg); -} - -static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev, bool bw40, - u8 filter_target) -{ - unsigned int i; - u8 bbp; - u8 rfcsr; - u8 passband; - u8 stopband; - u8 overtuned = 0; - u8 rfcsr24 = (bw40) ? 0x27 : 0x07; - - rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); - - rt2800_bbp_read(rt2x00dev, 4, &bbp); - rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * bw40); - rt2800_bbp_write(rt2x00dev, 4, bbp); - - rt2800_rfcsr_read(rt2x00dev, 31, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR31_RX_H20M, bw40); - rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 1); - rt2800_rfcsr_write(rt2x00dev, 22, rfcsr); - - /* - * Set power & frequency of passband test tone - */ - rt2800_bbp_write(rt2x00dev, 24, 0); - - for (i = 0; i < 100; i++) { - rt2800_bbp_write(rt2x00dev, 25, 0x90); - msleep(1); - - rt2800_bbp_read(rt2x00dev, 55, &passband); - if (passband) - break; - } - - /* - * Set power & frequency of stopband test tone - */ - rt2800_bbp_write(rt2x00dev, 24, 0x06); - - for (i = 0; i < 100; i++) { - rt2800_bbp_write(rt2x00dev, 25, 0x90); - msleep(1); - - rt2800_bbp_read(rt2x00dev, 55, &stopband); - - if ((passband - stopband) <= filter_target) { - rfcsr24++; - overtuned += ((passband - stopband) == filter_target); - } else - break; - - rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); - } - - rfcsr24 -= !!overtuned; - - rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); - return rfcsr24; -} - -static void rt2800_rf_init_calibration(struct rt2x00_dev *rt2x00dev, - const unsigned int rf_reg) -{ - u8 rfcsr; - - rt2800_rfcsr_read(rt2x00dev, rf_reg, &rfcsr); - rt2x00_set_field8(&rfcsr, FIELD8(0x80), 1); - rt2800_rfcsr_write(rt2x00dev, rf_reg, rfcsr); - msleep(1); - rt2x00_set_field8(&rfcsr, FIELD8(0x80), 0); - rt2800_rfcsr_write(rt2x00dev, rf_reg, rfcsr); -} - -static void rt2800_rx_filter_calibration(struct rt2x00_dev *rt2x00dev) -{ - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - u8 filter_tgt_bw20; - u8 filter_tgt_bw40; - u8 rfcsr, bbp; - - /* - * TODO: sync filter_tgt values with vendor driver - */ - if (rt2x00_rt(rt2x00dev, RT3070)) { - filter_tgt_bw20 = 0x16; - filter_tgt_bw40 = 0x19; - } else { - filter_tgt_bw20 = 0x13; - filter_tgt_bw40 = 0x15; - } - - drv_data->calibration_bw20 = - rt2800_init_rx_filter(rt2x00dev, false, filter_tgt_bw20); - drv_data->calibration_bw40 = - rt2800_init_rx_filter(rt2x00dev, true, filter_tgt_bw40); - - /* - * Save BBP 25 & 26 values for later use in channel switching (for 3052) - */ - rt2800_bbp_read(rt2x00dev, 25, &drv_data->bbp25); - rt2800_bbp_read(rt2x00dev, 26, &drv_data->bbp26); - - /* - * Set back to initial state - */ - rt2800_bbp_write(rt2x00dev, 24, 0); - - rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 0); - rt2800_rfcsr_write(rt2x00dev, 22, rfcsr); - - /* - * Set BBP back to BW20 - */ - rt2800_bbp_read(rt2x00dev, 4, &bbp); - rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 0); - rt2800_bbp_write(rt2x00dev, 4, bbp); -} - -static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev) -{ - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - u8 min_gain, rfcsr, bbp; - u16 eeprom; - - rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); - - rt2x00_set_field8(&rfcsr, RFCSR17_TX_LO1_EN, 0); - if (rt2x00_rt(rt2x00dev, RT3070) || - rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || - rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || - rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { - if (!rt2x00_has_cap_external_lna_bg(rt2x00dev)) - rt2x00_set_field8(&rfcsr, RFCSR17_R, 1); - } - - min_gain = rt2x00_rt(rt2x00dev, RT3070) ? 1 : 2; - if (drv_data->txmixer_gain_24g >= min_gain) { - rt2x00_set_field8(&rfcsr, RFCSR17_TXMIXER_GAIN, - drv_data->txmixer_gain_24g); - } - - rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); - - if (rt2x00_rt(rt2x00dev, RT3090)) { - /* Turn off unused DAC1 and ADC1 to reduce power consumption */ - rt2800_bbp_read(rt2x00dev, 138, &bbp); - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) - rt2x00_set_field8(&bbp, BBP138_RX_ADC1, 0); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) - rt2x00_set_field8(&bbp, BBP138_TX_DAC1, 1); - rt2800_bbp_write(rt2x00dev, 138, bbp); - } - - if (rt2x00_rt(rt2x00dev, RT3070)) { - rt2800_rfcsr_read(rt2x00dev, 27, &rfcsr); - if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) - rt2x00_set_field8(&rfcsr, RFCSR27_R1, 3); - else - rt2x00_set_field8(&rfcsr, RFCSR27_R1, 0); - rt2x00_set_field8(&rfcsr, RFCSR27_R2, 0); - rt2x00_set_field8(&rfcsr, RFCSR27_R3, 0); - rt2x00_set_field8(&rfcsr, RFCSR27_R4, 0); - rt2800_rfcsr_write(rt2x00dev, 27, rfcsr); - } else if (rt2x00_rt(rt2x00dev, RT3071) || - rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3390)) { - rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); - rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 15, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR15_TX_LO2_EN, 0); - rt2800_rfcsr_write(rt2x00dev, 15, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 20, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR20_RX_LO1_EN, 0); - rt2800_rfcsr_write(rt2x00dev, 20, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 21, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR21_RX_LO2_EN, 0); - rt2800_rfcsr_write(rt2x00dev, 21, rfcsr); - } -} - -static void rt2800_normal_mode_setup_3593(struct rt2x00_dev *rt2x00dev) -{ - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - u8 rfcsr; - u8 tx_gain; - - rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO2_EN, 0); - rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); - tx_gain = rt2x00_get_field8(drv_data->txmixer_gain_24g, - RFCSR17_TXMIXER_GAIN); - rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, tx_gain); - rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 38, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR38_RX_LO1_EN, 0); - rt2800_rfcsr_write(rt2x00dev, 38, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 39, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR39_RX_LO2_EN, 0); - rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); - rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); - rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2); - rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); - - /* TODO: enable stream mode */ -} - -static void rt2800_normal_mode_setup_5xxx(struct rt2x00_dev *rt2x00dev) -{ - u8 reg; - u16 eeprom; - - /* Turn off unused DAC1 and ADC1 to reduce power consumption */ - rt2800_bbp_read(rt2x00dev, 138, ®); - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) - rt2x00_set_field8(®, BBP138_RX_ADC1, 0); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) - rt2x00_set_field8(®, BBP138_TX_DAC1, 1); - rt2800_bbp_write(rt2x00dev, 138, reg); - - rt2800_rfcsr_read(rt2x00dev, 38, ®); - rt2x00_set_field8(®, RFCSR38_RX_LO1_EN, 0); - rt2800_rfcsr_write(rt2x00dev, 38, reg); - - rt2800_rfcsr_read(rt2x00dev, 39, ®); - rt2x00_set_field8(®, RFCSR39_RX_LO2_EN, 0); - rt2800_rfcsr_write(rt2x00dev, 39, reg); - - rt2800_bbp4_mac_if_ctrl(rt2x00dev); - - rt2800_rfcsr_read(rt2x00dev, 30, ®); - rt2x00_set_field8(®, RFCSR30_RX_VCM, 2); - rt2800_rfcsr_write(rt2x00dev, 30, reg); -} - -static void rt2800_init_rfcsr_305x_soc(struct rt2x00_dev *rt2x00dev) -{ - rt2800_rf_init_calibration(rt2x00dev, 30); - - rt2800_rfcsr_write(rt2x00dev, 0, 0x50); - rt2800_rfcsr_write(rt2x00dev, 1, 0x01); - rt2800_rfcsr_write(rt2x00dev, 2, 0xf7); - rt2800_rfcsr_write(rt2x00dev, 3, 0x75); - rt2800_rfcsr_write(rt2x00dev, 4, 0x40); - rt2800_rfcsr_write(rt2x00dev, 5, 0x03); - rt2800_rfcsr_write(rt2x00dev, 6, 0x02); - rt2800_rfcsr_write(rt2x00dev, 7, 0x50); - rt2800_rfcsr_write(rt2x00dev, 8, 0x39); - rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); - rt2800_rfcsr_write(rt2x00dev, 10, 0x60); - rt2800_rfcsr_write(rt2x00dev, 11, 0x21); - rt2800_rfcsr_write(rt2x00dev, 12, 0x75); - rt2800_rfcsr_write(rt2x00dev, 13, 0x75); - rt2800_rfcsr_write(rt2x00dev, 14, 0x90); - rt2800_rfcsr_write(rt2x00dev, 15, 0x58); - rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); - rt2800_rfcsr_write(rt2x00dev, 17, 0x92); - rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); - rt2800_rfcsr_write(rt2x00dev, 19, 0x02); - rt2800_rfcsr_write(rt2x00dev, 20, 0xba); - rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); - rt2800_rfcsr_write(rt2x00dev, 22, 0x00); - rt2800_rfcsr_write(rt2x00dev, 23, 0x31); - rt2800_rfcsr_write(rt2x00dev, 24, 0x08); - rt2800_rfcsr_write(rt2x00dev, 25, 0x01); - rt2800_rfcsr_write(rt2x00dev, 26, 0x25); - rt2800_rfcsr_write(rt2x00dev, 27, 0x23); - rt2800_rfcsr_write(rt2x00dev, 28, 0x13); - rt2800_rfcsr_write(rt2x00dev, 29, 0x83); - rt2800_rfcsr_write(rt2x00dev, 30, 0x00); - rt2800_rfcsr_write(rt2x00dev, 31, 0x00); -} - -static void rt2800_init_rfcsr_30xx(struct rt2x00_dev *rt2x00dev) -{ - u8 rfcsr; - u16 eeprom; - u32 reg; - - /* XXX vendor driver do this only for 3070 */ - rt2800_rf_init_calibration(rt2x00dev, 30); - - rt2800_rfcsr_write(rt2x00dev, 4, 0x40); - rt2800_rfcsr_write(rt2x00dev, 5, 0x03); - rt2800_rfcsr_write(rt2x00dev, 6, 0x02); - rt2800_rfcsr_write(rt2x00dev, 7, 0x60); - rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); - rt2800_rfcsr_write(rt2x00dev, 10, 0x41); - rt2800_rfcsr_write(rt2x00dev, 11, 0x21); - rt2800_rfcsr_write(rt2x00dev, 12, 0x7b); - rt2800_rfcsr_write(rt2x00dev, 14, 0x90); - rt2800_rfcsr_write(rt2x00dev, 15, 0x58); - rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); - rt2800_rfcsr_write(rt2x00dev, 17, 0x92); - rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); - rt2800_rfcsr_write(rt2x00dev, 19, 0x02); - rt2800_rfcsr_write(rt2x00dev, 20, 0xba); - rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); - rt2800_rfcsr_write(rt2x00dev, 24, 0x16); - rt2800_rfcsr_write(rt2x00dev, 25, 0x03); - rt2800_rfcsr_write(rt2x00dev, 29, 0x1f); - - if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) { - rt2800_register_read(rt2x00dev, LDO_CFG0, ®); - rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); - rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); - rt2800_register_write(rt2x00dev, LDO_CFG0, reg); - } else if (rt2x00_rt(rt2x00dev, RT3071) || - rt2x00_rt(rt2x00dev, RT3090)) { - rt2800_rfcsr_write(rt2x00dev, 31, 0x14); - - rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR6_R2, 1); - rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); - - rt2800_register_read(rt2x00dev, LDO_CFG0, ®); - rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); - if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || - rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E)) { - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, - &eeprom); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST)) - rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); - else - rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); - } - rt2800_register_write(rt2x00dev, LDO_CFG0, reg); - - rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); - rt2x00_set_field32(®, GPIO_SWITCH_5, 0); - rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); - } - - rt2800_rx_filter_calibration(rt2x00dev); - - if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F) || - rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || - rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E)) - rt2800_rfcsr_write(rt2x00dev, 27, 0x03); - - rt2800_led_open_drain_enable(rt2x00dev); - rt2800_normal_mode_setup_3xxx(rt2x00dev); -} - -static void rt2800_init_rfcsr_3290(struct rt2x00_dev *rt2x00dev) -{ - u8 rfcsr; - - rt2800_rf_init_calibration(rt2x00dev, 2); - - rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); - rt2800_rfcsr_write(rt2x00dev, 2, 0x80); - rt2800_rfcsr_write(rt2x00dev, 3, 0x08); - rt2800_rfcsr_write(rt2x00dev, 4, 0x00); - rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); - rt2800_rfcsr_write(rt2x00dev, 8, 0xf3); - rt2800_rfcsr_write(rt2x00dev, 9, 0x02); - rt2800_rfcsr_write(rt2x00dev, 10, 0x53); - rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); - rt2800_rfcsr_write(rt2x00dev, 12, 0x46); - rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); - rt2800_rfcsr_write(rt2x00dev, 18, 0x02); - rt2800_rfcsr_write(rt2x00dev, 22, 0x20); - rt2800_rfcsr_write(rt2x00dev, 25, 0x83); - rt2800_rfcsr_write(rt2x00dev, 26, 0x82); - rt2800_rfcsr_write(rt2x00dev, 27, 0x09); - rt2800_rfcsr_write(rt2x00dev, 29, 0x10); - rt2800_rfcsr_write(rt2x00dev, 30, 0x10); - rt2800_rfcsr_write(rt2x00dev, 31, 0x80); - rt2800_rfcsr_write(rt2x00dev, 32, 0x80); - rt2800_rfcsr_write(rt2x00dev, 33, 0x00); - rt2800_rfcsr_write(rt2x00dev, 34, 0x05); - rt2800_rfcsr_write(rt2x00dev, 35, 0x12); - rt2800_rfcsr_write(rt2x00dev, 36, 0x00); - rt2800_rfcsr_write(rt2x00dev, 38, 0x85); - rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); - rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); - rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); - rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); - rt2800_rfcsr_write(rt2x00dev, 43, 0x7b); - rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); - rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); - rt2800_rfcsr_write(rt2x00dev, 46, 0x73); - rt2800_rfcsr_write(rt2x00dev, 47, 0x00); - rt2800_rfcsr_write(rt2x00dev, 48, 0x10); - rt2800_rfcsr_write(rt2x00dev, 49, 0x98); - rt2800_rfcsr_write(rt2x00dev, 52, 0x38); - rt2800_rfcsr_write(rt2x00dev, 53, 0x00); - rt2800_rfcsr_write(rt2x00dev, 54, 0x78); - rt2800_rfcsr_write(rt2x00dev, 55, 0x43); - rt2800_rfcsr_write(rt2x00dev, 56, 0x02); - rt2800_rfcsr_write(rt2x00dev, 57, 0x80); - rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); - rt2800_rfcsr_write(rt2x00dev, 59, 0x09); - rt2800_rfcsr_write(rt2x00dev, 60, 0x45); - rt2800_rfcsr_write(rt2x00dev, 61, 0xc1); - - rt2800_rfcsr_read(rt2x00dev, 29, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR29_RSSI_GAIN, 3); - rt2800_rfcsr_write(rt2x00dev, 29, rfcsr); - - rt2800_led_open_drain_enable(rt2x00dev); - rt2800_normal_mode_setup_3xxx(rt2x00dev); -} - -static void rt2800_init_rfcsr_3352(struct rt2x00_dev *rt2x00dev) -{ - rt2800_rf_init_calibration(rt2x00dev, 30); - - rt2800_rfcsr_write(rt2x00dev, 0, 0xf0); - rt2800_rfcsr_write(rt2x00dev, 1, 0x23); - rt2800_rfcsr_write(rt2x00dev, 2, 0x50); - rt2800_rfcsr_write(rt2x00dev, 3, 0x18); - rt2800_rfcsr_write(rt2x00dev, 4, 0x00); - rt2800_rfcsr_write(rt2x00dev, 5, 0x00); - rt2800_rfcsr_write(rt2x00dev, 6, 0x33); - rt2800_rfcsr_write(rt2x00dev, 7, 0x00); - rt2800_rfcsr_write(rt2x00dev, 8, 0xf1); - rt2800_rfcsr_write(rt2x00dev, 9, 0x02); - rt2800_rfcsr_write(rt2x00dev, 10, 0xd2); - rt2800_rfcsr_write(rt2x00dev, 11, 0x42); - rt2800_rfcsr_write(rt2x00dev, 12, 0x1c); - rt2800_rfcsr_write(rt2x00dev, 13, 0x00); - rt2800_rfcsr_write(rt2x00dev, 14, 0x5a); - rt2800_rfcsr_write(rt2x00dev, 15, 0x00); - rt2800_rfcsr_write(rt2x00dev, 16, 0x01); - rt2800_rfcsr_write(rt2x00dev, 18, 0x45); - rt2800_rfcsr_write(rt2x00dev, 19, 0x02); - rt2800_rfcsr_write(rt2x00dev, 20, 0x00); - rt2800_rfcsr_write(rt2x00dev, 21, 0x00); - rt2800_rfcsr_write(rt2x00dev, 22, 0x00); - rt2800_rfcsr_write(rt2x00dev, 23, 0x00); - rt2800_rfcsr_write(rt2x00dev, 24, 0x00); - rt2800_rfcsr_write(rt2x00dev, 25, 0x80); - rt2800_rfcsr_write(rt2x00dev, 26, 0x00); - rt2800_rfcsr_write(rt2x00dev, 27, 0x03); - rt2800_rfcsr_write(rt2x00dev, 28, 0x03); - rt2800_rfcsr_write(rt2x00dev, 29, 0x00); - rt2800_rfcsr_write(rt2x00dev, 30, 0x10); - rt2800_rfcsr_write(rt2x00dev, 31, 0x80); - rt2800_rfcsr_write(rt2x00dev, 32, 0x80); - rt2800_rfcsr_write(rt2x00dev, 33, 0x00); - rt2800_rfcsr_write(rt2x00dev, 34, 0x01); - rt2800_rfcsr_write(rt2x00dev, 35, 0x03); - rt2800_rfcsr_write(rt2x00dev, 36, 0xbd); - rt2800_rfcsr_write(rt2x00dev, 37, 0x3c); - rt2800_rfcsr_write(rt2x00dev, 38, 0x5f); - rt2800_rfcsr_write(rt2x00dev, 39, 0xc5); - rt2800_rfcsr_write(rt2x00dev, 40, 0x33); - rt2800_rfcsr_write(rt2x00dev, 41, 0x5b); - rt2800_rfcsr_write(rt2x00dev, 42, 0x5b); - rt2800_rfcsr_write(rt2x00dev, 43, 0xdb); - rt2800_rfcsr_write(rt2x00dev, 44, 0xdb); - rt2800_rfcsr_write(rt2x00dev, 45, 0xdb); - rt2800_rfcsr_write(rt2x00dev, 46, 0xdd); - rt2800_rfcsr_write(rt2x00dev, 47, 0x0d); - rt2800_rfcsr_write(rt2x00dev, 48, 0x14); - rt2800_rfcsr_write(rt2x00dev, 49, 0x00); - rt2800_rfcsr_write(rt2x00dev, 50, 0x2d); - rt2800_rfcsr_write(rt2x00dev, 51, 0x7f); - rt2800_rfcsr_write(rt2x00dev, 52, 0x00); - rt2800_rfcsr_write(rt2x00dev, 53, 0x52); - rt2800_rfcsr_write(rt2x00dev, 54, 0x1b); - rt2800_rfcsr_write(rt2x00dev, 55, 0x7f); - rt2800_rfcsr_write(rt2x00dev, 56, 0x00); - rt2800_rfcsr_write(rt2x00dev, 57, 0x52); - rt2800_rfcsr_write(rt2x00dev, 58, 0x1b); - rt2800_rfcsr_write(rt2x00dev, 59, 0x00); - rt2800_rfcsr_write(rt2x00dev, 60, 0x00); - rt2800_rfcsr_write(rt2x00dev, 61, 0x00); - rt2800_rfcsr_write(rt2x00dev, 62, 0x00); - rt2800_rfcsr_write(rt2x00dev, 63, 0x00); - - rt2800_rx_filter_calibration(rt2x00dev); - rt2800_led_open_drain_enable(rt2x00dev); - rt2800_normal_mode_setup_3xxx(rt2x00dev); -} - -static void rt2800_init_rfcsr_3390(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2800_rf_init_calibration(rt2x00dev, 30); - - rt2800_rfcsr_write(rt2x00dev, 0, 0xa0); - rt2800_rfcsr_write(rt2x00dev, 1, 0xe1); - rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); - rt2800_rfcsr_write(rt2x00dev, 3, 0x62); - rt2800_rfcsr_write(rt2x00dev, 4, 0x40); - rt2800_rfcsr_write(rt2x00dev, 5, 0x8b); - rt2800_rfcsr_write(rt2x00dev, 6, 0x42); - rt2800_rfcsr_write(rt2x00dev, 7, 0x34); - rt2800_rfcsr_write(rt2x00dev, 8, 0x00); - rt2800_rfcsr_write(rt2x00dev, 9, 0xc0); - rt2800_rfcsr_write(rt2x00dev, 10, 0x61); - rt2800_rfcsr_write(rt2x00dev, 11, 0x21); - rt2800_rfcsr_write(rt2x00dev, 12, 0x3b); - rt2800_rfcsr_write(rt2x00dev, 13, 0xe0); - rt2800_rfcsr_write(rt2x00dev, 14, 0x90); - rt2800_rfcsr_write(rt2x00dev, 15, 0x53); - rt2800_rfcsr_write(rt2x00dev, 16, 0xe0); - rt2800_rfcsr_write(rt2x00dev, 17, 0x94); - rt2800_rfcsr_write(rt2x00dev, 18, 0x5c); - rt2800_rfcsr_write(rt2x00dev, 19, 0x4a); - rt2800_rfcsr_write(rt2x00dev, 20, 0xb2); - rt2800_rfcsr_write(rt2x00dev, 21, 0xf6); - rt2800_rfcsr_write(rt2x00dev, 22, 0x00); - rt2800_rfcsr_write(rt2x00dev, 23, 0x14); - rt2800_rfcsr_write(rt2x00dev, 24, 0x08); - rt2800_rfcsr_write(rt2x00dev, 25, 0x3d); - rt2800_rfcsr_write(rt2x00dev, 26, 0x85); - rt2800_rfcsr_write(rt2x00dev, 27, 0x00); - rt2800_rfcsr_write(rt2x00dev, 28, 0x41); - rt2800_rfcsr_write(rt2x00dev, 29, 0x8f); - rt2800_rfcsr_write(rt2x00dev, 30, 0x20); - rt2800_rfcsr_write(rt2x00dev, 31, 0x0f); - - rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); - rt2x00_set_field32(®, GPIO_SWITCH_5, 0); - rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); - - rt2800_rx_filter_calibration(rt2x00dev); - - if (rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) - rt2800_rfcsr_write(rt2x00dev, 27, 0x03); - - rt2800_led_open_drain_enable(rt2x00dev); - rt2800_normal_mode_setup_3xxx(rt2x00dev); -} - -static void rt2800_init_rfcsr_3572(struct rt2x00_dev *rt2x00dev) -{ - u8 rfcsr; - u32 reg; - - rt2800_rf_init_calibration(rt2x00dev, 30); - - rt2800_rfcsr_write(rt2x00dev, 0, 0x70); - rt2800_rfcsr_write(rt2x00dev, 1, 0x81); - rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); - rt2800_rfcsr_write(rt2x00dev, 3, 0x02); - rt2800_rfcsr_write(rt2x00dev, 4, 0x4c); - rt2800_rfcsr_write(rt2x00dev, 5, 0x05); - rt2800_rfcsr_write(rt2x00dev, 6, 0x4a); - rt2800_rfcsr_write(rt2x00dev, 7, 0xd8); - rt2800_rfcsr_write(rt2x00dev, 9, 0xc3); - rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); - rt2800_rfcsr_write(rt2x00dev, 11, 0xb9); - rt2800_rfcsr_write(rt2x00dev, 12, 0x70); - rt2800_rfcsr_write(rt2x00dev, 13, 0x65); - rt2800_rfcsr_write(rt2x00dev, 14, 0xa0); - rt2800_rfcsr_write(rt2x00dev, 15, 0x53); - rt2800_rfcsr_write(rt2x00dev, 16, 0x4c); - rt2800_rfcsr_write(rt2x00dev, 17, 0x23); - rt2800_rfcsr_write(rt2x00dev, 18, 0xac); - rt2800_rfcsr_write(rt2x00dev, 19, 0x93); - rt2800_rfcsr_write(rt2x00dev, 20, 0xb3); - rt2800_rfcsr_write(rt2x00dev, 21, 0xd0); - rt2800_rfcsr_write(rt2x00dev, 22, 0x00); - rt2800_rfcsr_write(rt2x00dev, 23, 0x3c); - rt2800_rfcsr_write(rt2x00dev, 24, 0x16); - rt2800_rfcsr_write(rt2x00dev, 25, 0x15); - rt2800_rfcsr_write(rt2x00dev, 26, 0x85); - rt2800_rfcsr_write(rt2x00dev, 27, 0x00); - rt2800_rfcsr_write(rt2x00dev, 28, 0x00); - rt2800_rfcsr_write(rt2x00dev, 29, 0x9b); - rt2800_rfcsr_write(rt2x00dev, 30, 0x09); - rt2800_rfcsr_write(rt2x00dev, 31, 0x10); - - rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR6_R2, 1); - rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); - - rt2800_register_read(rt2x00dev, LDO_CFG0, ®); - rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); - rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); - rt2800_register_write(rt2x00dev, LDO_CFG0, reg); - msleep(1); - rt2800_register_read(rt2x00dev, LDO_CFG0, ®); - rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); - rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); - rt2800_register_write(rt2x00dev, LDO_CFG0, reg); - - rt2800_rx_filter_calibration(rt2x00dev); - rt2800_led_open_drain_enable(rt2x00dev); - rt2800_normal_mode_setup_3xxx(rt2x00dev); -} - -static void rt3593_post_bbp_init(struct rt2x00_dev *rt2x00dev) -{ - u8 bbp; - bool txbf_enabled = false; /* FIXME */ - - rt2800_bbp_read(rt2x00dev, 105, &bbp); - if (rt2x00dev->default_ant.rx_chain_num == 1) - rt2x00_set_field8(&bbp, BBP105_MLD, 0); - else - rt2x00_set_field8(&bbp, BBP105_MLD, 1); - rt2800_bbp_write(rt2x00dev, 105, bbp); - - rt2800_bbp4_mac_if_ctrl(rt2x00dev); - - rt2800_bbp_write(rt2x00dev, 92, 0x02); - rt2800_bbp_write(rt2x00dev, 82, 0x82); - rt2800_bbp_write(rt2x00dev, 106, 0x05); - rt2800_bbp_write(rt2x00dev, 104, 0x92); - rt2800_bbp_write(rt2x00dev, 88, 0x90); - rt2800_bbp_write(rt2x00dev, 148, 0xc8); - rt2800_bbp_write(rt2x00dev, 47, 0x48); - rt2800_bbp_write(rt2x00dev, 120, 0x50); - - if (txbf_enabled) - rt2800_bbp_write(rt2x00dev, 163, 0xbd); - else - rt2800_bbp_write(rt2x00dev, 163, 0x9d); - - /* SNR mapping */ - rt2800_bbp_write(rt2x00dev, 142, 6); - rt2800_bbp_write(rt2x00dev, 143, 160); - rt2800_bbp_write(rt2x00dev, 142, 7); - rt2800_bbp_write(rt2x00dev, 143, 161); - rt2800_bbp_write(rt2x00dev, 142, 8); - rt2800_bbp_write(rt2x00dev, 143, 162); - - /* ADC/DAC control */ - rt2800_bbp_write(rt2x00dev, 31, 0x08); - - /* RX AGC energy lower bound in log2 */ - rt2800_bbp_write(rt2x00dev, 68, 0x0b); - - /* FIXME: BBP 105 owerwrite? */ - rt2800_bbp_write(rt2x00dev, 105, 0x04); - -} - -static void rt2800_init_rfcsr_3593(struct rt2x00_dev *rt2x00dev) -{ - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - u32 reg; - u8 rfcsr; - - /* Disable GPIO #4 and #7 function for LAN PE control */ - rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); - rt2x00_set_field32(®, GPIO_SWITCH_4, 0); - rt2x00_set_field32(®, GPIO_SWITCH_7, 0); - rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); - - /* Initialize default register values */ - rt2800_rfcsr_write(rt2x00dev, 1, 0x03); - rt2800_rfcsr_write(rt2x00dev, 3, 0x80); - rt2800_rfcsr_write(rt2x00dev, 5, 0x00); - rt2800_rfcsr_write(rt2x00dev, 6, 0x40); - rt2800_rfcsr_write(rt2x00dev, 8, 0xf1); - rt2800_rfcsr_write(rt2x00dev, 9, 0x02); - rt2800_rfcsr_write(rt2x00dev, 10, 0xd3); - rt2800_rfcsr_write(rt2x00dev, 11, 0x40); - rt2800_rfcsr_write(rt2x00dev, 12, 0x4e); - rt2800_rfcsr_write(rt2x00dev, 13, 0x12); - rt2800_rfcsr_write(rt2x00dev, 18, 0x40); - rt2800_rfcsr_write(rt2x00dev, 22, 0x20); - rt2800_rfcsr_write(rt2x00dev, 30, 0x10); - rt2800_rfcsr_write(rt2x00dev, 31, 0x80); - rt2800_rfcsr_write(rt2x00dev, 32, 0x78); - rt2800_rfcsr_write(rt2x00dev, 33, 0x3b); - rt2800_rfcsr_write(rt2x00dev, 34, 0x3c); - rt2800_rfcsr_write(rt2x00dev, 35, 0xe0); - rt2800_rfcsr_write(rt2x00dev, 38, 0x86); - rt2800_rfcsr_write(rt2x00dev, 39, 0x23); - rt2800_rfcsr_write(rt2x00dev, 44, 0xd3); - rt2800_rfcsr_write(rt2x00dev, 45, 0xbb); - rt2800_rfcsr_write(rt2x00dev, 46, 0x60); - rt2800_rfcsr_write(rt2x00dev, 49, 0x8e); - rt2800_rfcsr_write(rt2x00dev, 50, 0x86); - rt2800_rfcsr_write(rt2x00dev, 51, 0x75); - rt2800_rfcsr_write(rt2x00dev, 52, 0x45); - rt2800_rfcsr_write(rt2x00dev, 53, 0x18); - rt2800_rfcsr_write(rt2x00dev, 54, 0x18); - rt2800_rfcsr_write(rt2x00dev, 55, 0x18); - rt2800_rfcsr_write(rt2x00dev, 56, 0xdb); - rt2800_rfcsr_write(rt2x00dev, 57, 0x6e); - - /* Initiate calibration */ - /* TODO: use rt2800_rf_init_calibration ? */ - rt2800_rfcsr_read(rt2x00dev, 2, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR2_RESCAL_EN, 1); - rt2800_rfcsr_write(rt2x00dev, 2, rfcsr); - - rt2800_adjust_freq_offset(rt2x00dev); - - rt2800_rfcsr_read(rt2x00dev, 18, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR18_XO_TUNE_BYPASS, 1); - rt2800_rfcsr_write(rt2x00dev, 18, rfcsr); - - rt2800_register_read(rt2x00dev, LDO_CFG0, ®); - rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); - rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); - rt2800_register_write(rt2x00dev, LDO_CFG0, reg); - usleep_range(1000, 1500); - rt2800_register_read(rt2x00dev, LDO_CFG0, ®); - rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); - rt2800_register_write(rt2x00dev, LDO_CFG0, reg); - - /* Set initial values for RX filter calibration */ - drv_data->calibration_bw20 = 0x1f; - drv_data->calibration_bw40 = 0x2f; - - /* Save BBP 25 & 26 values for later use in channel switching */ - rt2800_bbp_read(rt2x00dev, 25, &drv_data->bbp25); - rt2800_bbp_read(rt2x00dev, 26, &drv_data->bbp26); - - rt2800_led_open_drain_enable(rt2x00dev); - rt2800_normal_mode_setup_3593(rt2x00dev); - - rt3593_post_bbp_init(rt2x00dev); - - /* TODO: enable stream mode support */ -} - -static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) -{ - rt2800_rf_init_calibration(rt2x00dev, 2); - - rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); - rt2800_rfcsr_write(rt2x00dev, 2, 0x80); - rt2800_rfcsr_write(rt2x00dev, 3, 0x88); - rt2800_rfcsr_write(rt2x00dev, 5, 0x10); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); - else - rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); - rt2800_rfcsr_write(rt2x00dev, 7, 0x00); - rt2800_rfcsr_write(rt2x00dev, 10, 0x53); - rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); - rt2800_rfcsr_write(rt2x00dev, 12, 0x46); - rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); - rt2800_rfcsr_write(rt2x00dev, 14, 0x00); - rt2800_rfcsr_write(rt2x00dev, 15, 0x00); - rt2800_rfcsr_write(rt2x00dev, 16, 0x00); - rt2800_rfcsr_write(rt2x00dev, 18, 0x03); - rt2800_rfcsr_write(rt2x00dev, 19, 0x00); - - rt2800_rfcsr_write(rt2x00dev, 20, 0x00); - rt2800_rfcsr_write(rt2x00dev, 21, 0x00); - rt2800_rfcsr_write(rt2x00dev, 22, 0x20); - rt2800_rfcsr_write(rt2x00dev, 23, 0x00); - rt2800_rfcsr_write(rt2x00dev, 24, 0x00); - if (rt2x00_is_usb(rt2x00dev) && - rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 25, 0x80); - else - rt2800_rfcsr_write(rt2x00dev, 25, 0xc0); - rt2800_rfcsr_write(rt2x00dev, 26, 0x00); - rt2800_rfcsr_write(rt2x00dev, 27, 0x09); - rt2800_rfcsr_write(rt2x00dev, 28, 0x00); - rt2800_rfcsr_write(rt2x00dev, 29, 0x10); - - rt2800_rfcsr_write(rt2x00dev, 30, 0x10); - rt2800_rfcsr_write(rt2x00dev, 31, 0x80); - rt2800_rfcsr_write(rt2x00dev, 32, 0x80); - rt2800_rfcsr_write(rt2x00dev, 33, 0x00); - rt2800_rfcsr_write(rt2x00dev, 34, 0x07); - rt2800_rfcsr_write(rt2x00dev, 35, 0x12); - rt2800_rfcsr_write(rt2x00dev, 36, 0x00); - rt2800_rfcsr_write(rt2x00dev, 37, 0x08); - rt2800_rfcsr_write(rt2x00dev, 38, 0x85); - rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); - - rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); - rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); - rt2800_rfcsr_write(rt2x00dev, 42, 0xd2); - rt2800_rfcsr_write(rt2x00dev, 43, 0x9a); - rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); - rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 46, 0x73); - else - rt2800_rfcsr_write(rt2x00dev, 46, 0x7b); - rt2800_rfcsr_write(rt2x00dev, 47, 0x00); - rt2800_rfcsr_write(rt2x00dev, 48, 0x10); - rt2800_rfcsr_write(rt2x00dev, 49, 0x94); - - rt2800_rfcsr_write(rt2x00dev, 52, 0x38); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 53, 0x00); - else - rt2800_rfcsr_write(rt2x00dev, 53, 0x84); - rt2800_rfcsr_write(rt2x00dev, 54, 0x78); - rt2800_rfcsr_write(rt2x00dev, 55, 0x44); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 56, 0x42); - else - rt2800_rfcsr_write(rt2x00dev, 56, 0x22); - rt2800_rfcsr_write(rt2x00dev, 57, 0x80); - rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); - rt2800_rfcsr_write(rt2x00dev, 59, 0x8f); - - rt2800_rfcsr_write(rt2x00dev, 60, 0x45); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { - if (rt2x00_is_usb(rt2x00dev)) - rt2800_rfcsr_write(rt2x00dev, 61, 0xd1); - else - rt2800_rfcsr_write(rt2x00dev, 61, 0xd5); - } else { - if (rt2x00_is_usb(rt2x00dev)) - rt2800_rfcsr_write(rt2x00dev, 61, 0xdd); - else - rt2800_rfcsr_write(rt2x00dev, 61, 0xb5); - } - rt2800_rfcsr_write(rt2x00dev, 62, 0x00); - rt2800_rfcsr_write(rt2x00dev, 63, 0x00); - - rt2800_normal_mode_setup_5xxx(rt2x00dev); - - rt2800_led_open_drain_enable(rt2x00dev); -} - -static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev) -{ - rt2800_rf_init_calibration(rt2x00dev, 2); - - rt2800_rfcsr_write(rt2x00dev, 1, 0x17); - rt2800_rfcsr_write(rt2x00dev, 3, 0x88); - rt2800_rfcsr_write(rt2x00dev, 5, 0x10); - rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); - rt2800_rfcsr_write(rt2x00dev, 7, 0x00); - rt2800_rfcsr_write(rt2x00dev, 10, 0x53); - rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); - rt2800_rfcsr_write(rt2x00dev, 12, 0x46); - rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); - rt2800_rfcsr_write(rt2x00dev, 14, 0x00); - rt2800_rfcsr_write(rt2x00dev, 15, 0x00); - rt2800_rfcsr_write(rt2x00dev, 16, 0x00); - rt2800_rfcsr_write(rt2x00dev, 18, 0x03); - rt2800_rfcsr_write(rt2x00dev, 19, 0x4d); - rt2800_rfcsr_write(rt2x00dev, 20, 0x00); - rt2800_rfcsr_write(rt2x00dev, 21, 0x8d); - rt2800_rfcsr_write(rt2x00dev, 22, 0x20); - rt2800_rfcsr_write(rt2x00dev, 23, 0x0b); - rt2800_rfcsr_write(rt2x00dev, 24, 0x44); - rt2800_rfcsr_write(rt2x00dev, 25, 0x80); - rt2800_rfcsr_write(rt2x00dev, 26, 0x82); - rt2800_rfcsr_write(rt2x00dev, 27, 0x09); - rt2800_rfcsr_write(rt2x00dev, 28, 0x00); - rt2800_rfcsr_write(rt2x00dev, 29, 0x10); - rt2800_rfcsr_write(rt2x00dev, 30, 0x10); - rt2800_rfcsr_write(rt2x00dev, 31, 0x80); - rt2800_rfcsr_write(rt2x00dev, 32, 0x20); - rt2800_rfcsr_write(rt2x00dev, 33, 0xC0); - rt2800_rfcsr_write(rt2x00dev, 34, 0x07); - rt2800_rfcsr_write(rt2x00dev, 35, 0x12); - rt2800_rfcsr_write(rt2x00dev, 36, 0x00); - rt2800_rfcsr_write(rt2x00dev, 37, 0x08); - rt2800_rfcsr_write(rt2x00dev, 38, 0x89); - rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); - rt2800_rfcsr_write(rt2x00dev, 40, 0x0f); - rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); - rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); - rt2800_rfcsr_write(rt2x00dev, 43, 0x9b); - rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); - rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); - rt2800_rfcsr_write(rt2x00dev, 46, 0x73); - rt2800_rfcsr_write(rt2x00dev, 47, 0x0c); - rt2800_rfcsr_write(rt2x00dev, 48, 0x10); - rt2800_rfcsr_write(rt2x00dev, 49, 0x94); - rt2800_rfcsr_write(rt2x00dev, 50, 0x94); - rt2800_rfcsr_write(rt2x00dev, 51, 0x3a); - rt2800_rfcsr_write(rt2x00dev, 52, 0x48); - rt2800_rfcsr_write(rt2x00dev, 53, 0x44); - rt2800_rfcsr_write(rt2x00dev, 54, 0x38); - rt2800_rfcsr_write(rt2x00dev, 55, 0x43); - rt2800_rfcsr_write(rt2x00dev, 56, 0xa1); - rt2800_rfcsr_write(rt2x00dev, 57, 0x00); - rt2800_rfcsr_write(rt2x00dev, 58, 0x39); - rt2800_rfcsr_write(rt2x00dev, 59, 0x07); - rt2800_rfcsr_write(rt2x00dev, 60, 0x45); - rt2800_rfcsr_write(rt2x00dev, 61, 0x91); - rt2800_rfcsr_write(rt2x00dev, 62, 0x39); - rt2800_rfcsr_write(rt2x00dev, 63, 0x07); - - rt2800_normal_mode_setup_5xxx(rt2x00dev); - - rt2800_led_open_drain_enable(rt2x00dev); -} - -static void rt2800_init_rfcsr_5592(struct rt2x00_dev *rt2x00dev) -{ - rt2800_rf_init_calibration(rt2x00dev, 30); - - rt2800_rfcsr_write(rt2x00dev, 1, 0x3F); - rt2800_rfcsr_write(rt2x00dev, 3, 0x08); - rt2800_rfcsr_write(rt2x00dev, 5, 0x10); - rt2800_rfcsr_write(rt2x00dev, 6, 0xE4); - rt2800_rfcsr_write(rt2x00dev, 7, 0x00); - rt2800_rfcsr_write(rt2x00dev, 14, 0x00); - rt2800_rfcsr_write(rt2x00dev, 15, 0x00); - rt2800_rfcsr_write(rt2x00dev, 16, 0x00); - rt2800_rfcsr_write(rt2x00dev, 18, 0x03); - rt2800_rfcsr_write(rt2x00dev, 19, 0x4D); - rt2800_rfcsr_write(rt2x00dev, 20, 0x10); - rt2800_rfcsr_write(rt2x00dev, 21, 0x8D); - rt2800_rfcsr_write(rt2x00dev, 26, 0x82); - rt2800_rfcsr_write(rt2x00dev, 28, 0x00); - rt2800_rfcsr_write(rt2x00dev, 29, 0x10); - rt2800_rfcsr_write(rt2x00dev, 33, 0xC0); - rt2800_rfcsr_write(rt2x00dev, 34, 0x07); - rt2800_rfcsr_write(rt2x00dev, 35, 0x12); - rt2800_rfcsr_write(rt2x00dev, 47, 0x0C); - rt2800_rfcsr_write(rt2x00dev, 53, 0x22); - rt2800_rfcsr_write(rt2x00dev, 63, 0x07); - - rt2800_rfcsr_write(rt2x00dev, 2, 0x80); - msleep(1); - - rt2800_adjust_freq_offset(rt2x00dev); - - /* Enable DC filter */ - if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) - rt2800_bbp_write(rt2x00dev, 103, 0xc0); - - rt2800_normal_mode_setup_5xxx(rt2x00dev); - - if (rt2x00_rt_rev_lt(rt2x00dev, RT5592, REV_RT5592C)) - rt2800_rfcsr_write(rt2x00dev, 27, 0x03); - - rt2800_led_open_drain_enable(rt2x00dev); -} - -static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) -{ - if (rt2800_is_305x_soc(rt2x00dev)) { - rt2800_init_rfcsr_305x_soc(rt2x00dev); - return; - } - - switch (rt2x00dev->chip.rt) { - case RT3070: - case RT3071: - case RT3090: - rt2800_init_rfcsr_30xx(rt2x00dev); - break; - case RT3290: - rt2800_init_rfcsr_3290(rt2x00dev); - break; - case RT3352: - rt2800_init_rfcsr_3352(rt2x00dev); - break; - case RT3390: - rt2800_init_rfcsr_3390(rt2x00dev); - break; - case RT3572: - rt2800_init_rfcsr_3572(rt2x00dev); - break; - case RT3593: - rt2800_init_rfcsr_3593(rt2x00dev); - break; - case RT5390: - rt2800_init_rfcsr_5390(rt2x00dev); - break; - case RT5392: - rt2800_init_rfcsr_5392(rt2x00dev); - break; - case RT5592: - rt2800_init_rfcsr_5592(rt2x00dev); - break; - } -} - -int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - u16 word; - - /* - * Initialize MAC registers. - */ - if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) || - rt2800_init_registers(rt2x00dev))) - return -EIO; - - /* - * Wait BBP/RF to wake up. - */ - if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev))) - return -EIO; - - /* - * Send signal during boot time to initialize firmware. - */ - rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); - rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); - if (rt2x00_is_usb(rt2x00dev)) - rt2800_register_write(rt2x00dev, H2M_INT_SRC, 0); - rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0); - msleep(1); - - /* - * Make sure BBP is up and running. - */ - if (unlikely(rt2800_wait_bbp_ready(rt2x00dev))) - return -EIO; - - /* - * Initialize BBP/RF registers. - */ - rt2800_init_bbp(rt2x00dev); - rt2800_init_rfcsr(rt2x00dev); - - if (rt2x00_is_usb(rt2x00dev) && - (rt2x00_rt(rt2x00dev, RT3070) || - rt2x00_rt(rt2x00dev, RT3071) || - rt2x00_rt(rt2x00dev, RT3572))) { - udelay(200); - rt2800_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0); - udelay(10); - } - - /* - * Enable RX. - */ - rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); - rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - - udelay(50); - - rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); - rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1); - rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1); - rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 2); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); - rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); - - rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); - rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - - /* - * Initialize LED control - */ - rt2800_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word); - rt2800_mcu_request(rt2x00dev, MCU_LED_AG_CONF, 0xff, - word & 0xff, (word >> 8) & 0xff); - - rt2800_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word); - rt2800_mcu_request(rt2x00dev, MCU_LED_ACT_CONF, 0xff, - word & 0xff, (word >> 8) & 0xff); - - rt2800_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word); - rt2800_mcu_request(rt2x00dev, MCU_LED_LED_POLARITY, 0xff, - word & 0xff, (word >> 8) & 0xff); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2800_enable_radio); - -void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2800_disable_wpdma(rt2x00dev); - - /* Wait for DMA, ignore error */ - rt2800_wait_wpdma_ready(rt2x00dev); - - rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 0); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); - rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); -} -EXPORT_SYMBOL_GPL(rt2800_disable_radio); - -int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - u16 efuse_ctrl_reg; - - if (rt2x00_rt(rt2x00dev, RT3290)) - efuse_ctrl_reg = EFUSE_CTRL_3290; - else - efuse_ctrl_reg = EFUSE_CTRL; - - rt2800_register_read(rt2x00dev, efuse_ctrl_reg, ®); - return rt2x00_get_field32(reg, EFUSE_CTRL_PRESENT); -} -EXPORT_SYMBOL_GPL(rt2800_efuse_detect); - -static void rt2800_efuse_read(struct rt2x00_dev *rt2x00dev, unsigned int i) -{ - u32 reg; - u16 efuse_ctrl_reg; - u16 efuse_data0_reg; - u16 efuse_data1_reg; - u16 efuse_data2_reg; - u16 efuse_data3_reg; - - if (rt2x00_rt(rt2x00dev, RT3290)) { - efuse_ctrl_reg = EFUSE_CTRL_3290; - efuse_data0_reg = EFUSE_DATA0_3290; - efuse_data1_reg = EFUSE_DATA1_3290; - efuse_data2_reg = EFUSE_DATA2_3290; - efuse_data3_reg = EFUSE_DATA3_3290; - } else { - efuse_ctrl_reg = EFUSE_CTRL; - efuse_data0_reg = EFUSE_DATA0; - efuse_data1_reg = EFUSE_DATA1; - efuse_data2_reg = EFUSE_DATA2; - efuse_data3_reg = EFUSE_DATA3; - } - mutex_lock(&rt2x00dev->csr_mutex); - - rt2800_register_read_lock(rt2x00dev, efuse_ctrl_reg, ®); - rt2x00_set_field32(®, EFUSE_CTRL_ADDRESS_IN, i); - rt2x00_set_field32(®, EFUSE_CTRL_MODE, 0); - rt2x00_set_field32(®, EFUSE_CTRL_KICK, 1); - rt2800_register_write_lock(rt2x00dev, efuse_ctrl_reg, reg); - - /* Wait until the EEPROM has been loaded */ - rt2800_regbusy_read(rt2x00dev, efuse_ctrl_reg, EFUSE_CTRL_KICK, ®); - /* Apparently the data is read from end to start */ - rt2800_register_read_lock(rt2x00dev, efuse_data3_reg, ®); - /* The returned value is in CPU order, but eeprom is le */ - *(u32 *)&rt2x00dev->eeprom[i] = cpu_to_le32(reg); - rt2800_register_read_lock(rt2x00dev, efuse_data2_reg, ®); - *(u32 *)&rt2x00dev->eeprom[i + 2] = cpu_to_le32(reg); - rt2800_register_read_lock(rt2x00dev, efuse_data1_reg, ®); - *(u32 *)&rt2x00dev->eeprom[i + 4] = cpu_to_le32(reg); - rt2800_register_read_lock(rt2x00dev, efuse_data0_reg, ®); - *(u32 *)&rt2x00dev->eeprom[i + 6] = cpu_to_le32(reg); - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - - for (i = 0; i < EEPROM_SIZE / sizeof(u16); i += 8) - rt2800_efuse_read(rt2x00dev, i); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse); - -static u8 rt2800_get_txmixer_gain_24g(struct rt2x00_dev *rt2x00dev) -{ - u16 word; - - if (rt2x00_rt(rt2x00dev, RT3593)) - return 0; - - rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word); - if ((word & 0x00ff) != 0x00ff) - return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL); - - return 0; -} - -static u8 rt2800_get_txmixer_gain_5g(struct rt2x00_dev *rt2x00dev) -{ - u16 word; - - if (rt2x00_rt(rt2x00dev, RT3593)) - return 0; - - rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word); - if ((word & 0x00ff) != 0x00ff) - return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL); - - return 0; -} - -static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) -{ - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - u16 word; - u8 *mac; - u8 default_lna_gain; - int retval; - - /* - * Read the EEPROM. - */ - retval = rt2800_read_eeprom(rt2x00dev); - if (retval) - return retval; - - /* - * Start validation of the data that has been read. - */ - mac = rt2800_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } - - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2); - rt2x00_set_field16(&word, EEPROM_NIC_CONF0_TXPATH, 1); - rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RF_TYPE, RF2820); - rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); - rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); - } else if (rt2x00_rt(rt2x00dev, RT2860) || - rt2x00_rt(rt2x00dev, RT2872)) { - /* - * There is a max of 2 RX streams for RT28x0 series - */ - if (rt2x00_get_field16(word, EEPROM_NIC_CONF0_RXPATH) > 2) - rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2); - rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); - } - - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_HW_RADIO, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_LNA_2G, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_LNA_5G, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_CARDBUS_ACCEL, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_SB_2G, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_SB_5G, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_WPS_PBC, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_2G, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_5G, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BROADBAND_EXT_LNA, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_ANT_DIVERSITY, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_INTERNAL_TX_ALC, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BT_COEXIST, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CONF1_DAC_TEST, 0); - rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word); - rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); - } - - rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); - if ((word & 0x00ff) == 0x00ff) { - rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); - rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word); - rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word); - } - if ((word & 0xff00) == 0xff00) { - rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE, - LED_MODE_TXRX_ACTIVITY); - rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0); - rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word); - rt2800_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555); - rt2800_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221); - rt2800_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8); - rt2x00_eeprom_dbg(rt2x00dev, "Led Mode: 0x%04x\n", word); - } - - /* - * During the LNA validation we are going to use - * lna0 as correct value. Note that EEPROM_LNA - * is never validated. - */ - rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &word); - default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0); - - rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0); - rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word); - - drv_data->txmixer_gain_24g = rt2800_get_txmixer_gain_24g(rt2x00dev); - - rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0); - if (!rt2x00_rt(rt2x00dev, RT3593)) { - if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 || - rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff) - rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1, - default_lna_gain); - } - rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word); - - drv_data->txmixer_gain_5g = rt2800_get_txmixer_gain_5g(rt2x00dev); - - rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0); - rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word); - - rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word); - if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0); - if (!rt2x00_rt(rt2x00dev, RT3593)) { - if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 || - rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff) - rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2, - default_lna_gain); - } - rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word); - - if (rt2x00_rt(rt2x00dev, RT3593)) { - rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &word); - if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0x00 || - rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0xff) - rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1, - default_lna_gain); - if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0x00 || - rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0xff) - rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1, - default_lna_gain); - rt2800_eeprom_write(rt2x00dev, EEPROM_EXT_LNA2, word); - } - - return 0; -} - -static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) -{ - u16 value; - u16 eeprom; - u16 rf; - - /* - * Read EEPROM word for configuration. - */ - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); - - /* - * Identify RF chipset by EEPROM value - * RT28xx/RT30xx: defined in "EEPROM_NIC_CONF0_RF_TYPE" field - * RT53xx: defined in "EEPROM_CHIP_ID" field - */ - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf); - else - rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE); - - switch (rf) { - case RF2820: - case RF2850: - case RF2720: - case RF2750: - case RF3020: - case RF2020: - case RF3021: - case RF3022: - case RF3052: - case RF3053: - case RF3070: - case RF3290: - case RF3320: - case RF3322: - case RF5360: - case RF5362: - case RF5370: - case RF5372: - case RF5390: - case RF5392: - case RF5592: - break; - default: - rt2x00_err(rt2x00dev, "Invalid RF chipset 0x%04x detected\n", - rf); - return -ENODEV; - } - - rt2x00_set_rf(rt2x00dev, rf); - - /* - * Identify default antenna configuration. - */ - rt2x00dev->default_ant.tx_chain_num = - rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH); - rt2x00dev->default_ant.rx_chain_num = - rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH); - - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); - - if (rt2x00_rt(rt2x00dev, RT3070) || - rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT3390)) { - value = rt2x00_get_field16(eeprom, - EEPROM_NIC_CONF1_ANT_DIVERSITY); - switch (value) { - case 0: - case 1: - case 2: - rt2x00dev->default_ant.tx = ANTENNA_A; - rt2x00dev->default_ant.rx = ANTENNA_A; - break; - case 3: - rt2x00dev->default_ant.tx = ANTENNA_A; - rt2x00dev->default_ant.rx = ANTENNA_B; - break; - } - } else { - rt2x00dev->default_ant.tx = ANTENNA_A; - rt2x00dev->default_ant.rx = ANTENNA_A; - } - - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { - rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY; /* Unused */ - rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY; /* Unused */ - } - - /* - * Determine external LNA informations. - */ - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_LNA_5G)) - __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_LNA_2G)) - __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); - - /* - * Detect if this device has an hardware controlled radio. - */ - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_HW_RADIO)) - __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); - - /* - * Detect if this device has Bluetooth co-existence. - */ - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_BT_COEXIST)) - __set_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags); - - /* - * Read frequency offset and RF programming sequence. - */ - rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); - rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); - - /* - * Store led settings, for correct led behaviour. - */ -#ifdef CONFIG_RT2X00_LIB_LEDS - rt2800_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); - rt2800_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); - rt2800_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY); - - rt2x00dev->led_mcu_reg = eeprom; -#endif /* CONFIG_RT2X00_LIB_LEDS */ - - /* - * Check if support EIRP tx power limit feature. - */ - rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom); - - if (rt2x00_get_field16(eeprom, EEPROM_EIRP_MAX_TX_POWER_2GHZ) < - EIRP_MAX_TX_POWER_LIMIT) - __set_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags); - - return 0; -} - -/* - * RF value list for rt28xx - * Supports: 2.4 GHz (all) & 5.2 GHz (RF2850 & RF2750) - */ -static const struct rf_channel rf_vals[] = { - { 1, 0x18402ecc, 0x184c0786, 0x1816b455, 0x1800510b }, - { 2, 0x18402ecc, 0x184c0786, 0x18168a55, 0x1800519f }, - { 3, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800518b }, - { 4, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800519f }, - { 5, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800518b }, - { 6, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800519f }, - { 7, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800518b }, - { 8, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800519f }, - { 9, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800518b }, - { 10, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800519f }, - { 11, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800518b }, - { 12, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800519f }, - { 13, 0x18402ecc, 0x184c079e, 0x18168a55, 0x1800518b }, - { 14, 0x18402ecc, 0x184c07a2, 0x18168a55, 0x18005193 }, - - /* 802.11 UNI / HyperLan 2 */ - { 36, 0x18402ecc, 0x184c099a, 0x18158a55, 0x180ed1a3 }, - { 38, 0x18402ecc, 0x184c099e, 0x18158a55, 0x180ed193 }, - { 40, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed183 }, - { 44, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed1a3 }, - { 46, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed18b }, - { 48, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed19b }, - { 52, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed193 }, - { 54, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed1a3 }, - { 56, 0x18402ec8, 0x184c068e, 0x18158a55, 0x180ed18b }, - { 60, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed183 }, - { 62, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed193 }, - { 64, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed1a3 }, - - /* 802.11 HyperLan 2 */ - { 100, 0x18402ec8, 0x184c06b2, 0x18178a55, 0x180ed783 }, - { 102, 0x18402ec8, 0x184c06b2, 0x18578a55, 0x180ed793 }, - { 104, 0x18402ec8, 0x185c06b2, 0x18578a55, 0x180ed1a3 }, - { 108, 0x18402ecc, 0x185c0a32, 0x18578a55, 0x180ed193 }, - { 110, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed183 }, - { 112, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed19b }, - { 116, 0x18402ecc, 0x184c0a3a, 0x18178a55, 0x180ed1a3 }, - { 118, 0x18402ecc, 0x184c0a3e, 0x18178a55, 0x180ed193 }, - { 120, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed183 }, - { 124, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed193 }, - { 126, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed15b }, - { 128, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed1a3 }, - { 132, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed18b }, - { 134, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed193 }, - { 136, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed19b }, - { 140, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed183 }, - - /* 802.11 UNII */ - { 149, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed1a7 }, - { 151, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed187 }, - { 153, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed18f }, - { 157, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed19f }, - { 159, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed1a7 }, - { 161, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed187 }, - { 165, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed197 }, - { 167, 0x18402ec4, 0x184c03d2, 0x18179855, 0x1815531f }, - { 169, 0x18402ec4, 0x184c03d2, 0x18179855, 0x18155327 }, - { 171, 0x18402ec4, 0x184c03d6, 0x18179855, 0x18155307 }, - { 173, 0x18402ec4, 0x184c03d6, 0x18179855, 0x1815530f }, - - /* 802.11 Japan */ - { 184, 0x15002ccc, 0x1500491e, 0x1509be55, 0x150c0a0b }, - { 188, 0x15002ccc, 0x15004922, 0x1509be55, 0x150c0a13 }, - { 192, 0x15002ccc, 0x15004926, 0x1509be55, 0x150c0a1b }, - { 196, 0x15002ccc, 0x1500492a, 0x1509be55, 0x150c0a23 }, - { 208, 0x15002ccc, 0x1500493a, 0x1509be55, 0x150c0a13 }, - { 212, 0x15002ccc, 0x1500493e, 0x1509be55, 0x150c0a1b }, - { 216, 0x15002ccc, 0x15004982, 0x1509be55, 0x150c0a23 }, -}; - -/* - * RF value list for rt3xxx - * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052 & RF3053) - */ -static const struct rf_channel rf_vals_3x[] = { - {1, 241, 2, 2 }, - {2, 241, 2, 7 }, - {3, 242, 2, 2 }, - {4, 242, 2, 7 }, - {5, 243, 2, 2 }, - {6, 243, 2, 7 }, - {7, 244, 2, 2 }, - {8, 244, 2, 7 }, - {9, 245, 2, 2 }, - {10, 245, 2, 7 }, - {11, 246, 2, 2 }, - {12, 246, 2, 7 }, - {13, 247, 2, 2 }, - {14, 248, 2, 4 }, - - /* 802.11 UNI / HyperLan 2 */ - {36, 0x56, 0, 4}, - {38, 0x56, 0, 6}, - {40, 0x56, 0, 8}, - {44, 0x57, 0, 0}, - {46, 0x57, 0, 2}, - {48, 0x57, 0, 4}, - {52, 0x57, 0, 8}, - {54, 0x57, 0, 10}, - {56, 0x58, 0, 0}, - {60, 0x58, 0, 4}, - {62, 0x58, 0, 6}, - {64, 0x58, 0, 8}, - - /* 802.11 HyperLan 2 */ - {100, 0x5b, 0, 8}, - {102, 0x5b, 0, 10}, - {104, 0x5c, 0, 0}, - {108, 0x5c, 0, 4}, - {110, 0x5c, 0, 6}, - {112, 0x5c, 0, 8}, - {116, 0x5d, 0, 0}, - {118, 0x5d, 0, 2}, - {120, 0x5d, 0, 4}, - {124, 0x5d, 0, 8}, - {126, 0x5d, 0, 10}, - {128, 0x5e, 0, 0}, - {132, 0x5e, 0, 4}, - {134, 0x5e, 0, 6}, - {136, 0x5e, 0, 8}, - {140, 0x5f, 0, 0}, - - /* 802.11 UNII */ - {149, 0x5f, 0, 9}, - {151, 0x5f, 0, 11}, - {153, 0x60, 0, 1}, - {157, 0x60, 0, 5}, - {159, 0x60, 0, 7}, - {161, 0x60, 0, 9}, - {165, 0x61, 0, 1}, - {167, 0x61, 0, 3}, - {169, 0x61, 0, 5}, - {171, 0x61, 0, 7}, - {173, 0x61, 0, 9}, -}; - -static const struct rf_channel rf_vals_5592_xtal20[] = { - /* Channel, N, K, mod, R */ - {1, 482, 4, 10, 3}, - {2, 483, 4, 10, 3}, - {3, 484, 4, 10, 3}, - {4, 485, 4, 10, 3}, - {5, 486, 4, 10, 3}, - {6, 487, 4, 10, 3}, - {7, 488, 4, 10, 3}, - {8, 489, 4, 10, 3}, - {9, 490, 4, 10, 3}, - {10, 491, 4, 10, 3}, - {11, 492, 4, 10, 3}, - {12, 493, 4, 10, 3}, - {13, 494, 4, 10, 3}, - {14, 496, 8, 10, 3}, - {36, 172, 8, 12, 1}, - {38, 173, 0, 12, 1}, - {40, 173, 4, 12, 1}, - {42, 173, 8, 12, 1}, - {44, 174, 0, 12, 1}, - {46, 174, 4, 12, 1}, - {48, 174, 8, 12, 1}, - {50, 175, 0, 12, 1}, - {52, 175, 4, 12, 1}, - {54, 175, 8, 12, 1}, - {56, 176, 0, 12, 1}, - {58, 176, 4, 12, 1}, - {60, 176, 8, 12, 1}, - {62, 177, 0, 12, 1}, - {64, 177, 4, 12, 1}, - {100, 183, 4, 12, 1}, - {102, 183, 8, 12, 1}, - {104, 184, 0, 12, 1}, - {106, 184, 4, 12, 1}, - {108, 184, 8, 12, 1}, - {110, 185, 0, 12, 1}, - {112, 185, 4, 12, 1}, - {114, 185, 8, 12, 1}, - {116, 186, 0, 12, 1}, - {118, 186, 4, 12, 1}, - {120, 186, 8, 12, 1}, - {122, 187, 0, 12, 1}, - {124, 187, 4, 12, 1}, - {126, 187, 8, 12, 1}, - {128, 188, 0, 12, 1}, - {130, 188, 4, 12, 1}, - {132, 188, 8, 12, 1}, - {134, 189, 0, 12, 1}, - {136, 189, 4, 12, 1}, - {138, 189, 8, 12, 1}, - {140, 190, 0, 12, 1}, - {149, 191, 6, 12, 1}, - {151, 191, 10, 12, 1}, - {153, 192, 2, 12, 1}, - {155, 192, 6, 12, 1}, - {157, 192, 10, 12, 1}, - {159, 193, 2, 12, 1}, - {161, 193, 6, 12, 1}, - {165, 194, 2, 12, 1}, - {184, 164, 0, 12, 1}, - {188, 164, 4, 12, 1}, - {192, 165, 8, 12, 1}, - {196, 166, 0, 12, 1}, -}; - -static const struct rf_channel rf_vals_5592_xtal40[] = { - /* Channel, N, K, mod, R */ - {1, 241, 2, 10, 3}, - {2, 241, 7, 10, 3}, - {3, 242, 2, 10, 3}, - {4, 242, 7, 10, 3}, - {5, 243, 2, 10, 3}, - {6, 243, 7, 10, 3}, - {7, 244, 2, 10, 3}, - {8, 244, 7, 10, 3}, - {9, 245, 2, 10, 3}, - {10, 245, 7, 10, 3}, - {11, 246, 2, 10, 3}, - {12, 246, 7, 10, 3}, - {13, 247, 2, 10, 3}, - {14, 248, 4, 10, 3}, - {36, 86, 4, 12, 1}, - {38, 86, 6, 12, 1}, - {40, 86, 8, 12, 1}, - {42, 86, 10, 12, 1}, - {44, 87, 0, 12, 1}, - {46, 87, 2, 12, 1}, - {48, 87, 4, 12, 1}, - {50, 87, 6, 12, 1}, - {52, 87, 8, 12, 1}, - {54, 87, 10, 12, 1}, - {56, 88, 0, 12, 1}, - {58, 88, 2, 12, 1}, - {60, 88, 4, 12, 1}, - {62, 88, 6, 12, 1}, - {64, 88, 8, 12, 1}, - {100, 91, 8, 12, 1}, - {102, 91, 10, 12, 1}, - {104, 92, 0, 12, 1}, - {106, 92, 2, 12, 1}, - {108, 92, 4, 12, 1}, - {110, 92, 6, 12, 1}, - {112, 92, 8, 12, 1}, - {114, 92, 10, 12, 1}, - {116, 93, 0, 12, 1}, - {118, 93, 2, 12, 1}, - {120, 93, 4, 12, 1}, - {122, 93, 6, 12, 1}, - {124, 93, 8, 12, 1}, - {126, 93, 10, 12, 1}, - {128, 94, 0, 12, 1}, - {130, 94, 2, 12, 1}, - {132, 94, 4, 12, 1}, - {134, 94, 6, 12, 1}, - {136, 94, 8, 12, 1}, - {138, 94, 10, 12, 1}, - {140, 95, 0, 12, 1}, - {149, 95, 9, 12, 1}, - {151, 95, 11, 12, 1}, - {153, 96, 1, 12, 1}, - {155, 96, 3, 12, 1}, - {157, 96, 5, 12, 1}, - {159, 96, 7, 12, 1}, - {161, 96, 9, 12, 1}, - {165, 97, 1, 12, 1}, - {184, 82, 0, 12, 1}, - {188, 82, 4, 12, 1}, - {192, 82, 8, 12, 1}, - {196, 83, 0, 12, 1}, -}; - -static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) -{ - struct hw_mode_spec *spec = &rt2x00dev->spec; - struct channel_info *info; - char *default_power1; - char *default_power2; - char *default_power3; - unsigned int i; - u32 reg; - - /* - * Disable powersaving as default. - */ - rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - /* - * Initialize all hw fields. - */ - ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_HT_CCK_RATES); - ieee80211_hw_set(rt2x00dev->hw, REPORTS_TX_ACK_STATUS); - ieee80211_hw_set(rt2x00dev->hw, AMPDU_AGGREGATION); - ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); - ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); - ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); - - /* - * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices - * unless we are capable of sending the buffered frames out after the - * DTIM transmission using rt2x00lib_beacondone. This will send out - * multicast and broadcast traffic immediately instead of buffering it - * infinitly and thus dropping it after some time. - */ - if (!rt2x00_is_usb(rt2x00dev)) - ieee80211_hw_set(rt2x00dev->hw, HOST_BROADCAST_PS_BUFFERING); - - SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); - SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, - rt2800_eeprom_addr(rt2x00dev, - EEPROM_MAC_ADDR_0)); - - /* - * As rt2800 has a global fallback table we cannot specify - * more then one tx rate per frame but since the hw will - * try several rates (based on the fallback table) we should - * initialize max_report_rates to the maximum number of rates - * we are going to try. Otherwise mac80211 will truncate our - * reported tx rates and the rc algortihm will end up with - * incorrect data. - */ - rt2x00dev->hw->max_rates = 1; - rt2x00dev->hw->max_report_rates = 7; - rt2x00dev->hw->max_rate_tries = 1; - - /* - * Initialize hw_mode information. - */ - spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - - switch (rt2x00dev->chip.rf) { - case RF2720: - case RF2820: - spec->num_channels = 14; - spec->channels = rf_vals; - break; - - case RF2750: - case RF2850: - spec->num_channels = ARRAY_SIZE(rf_vals); - spec->channels = rf_vals; - break; - - case RF2020: - case RF3020: - case RF3021: - case RF3022: - case RF3070: - case RF3290: - case RF3320: - case RF3322: - case RF5360: - case RF5362: - case RF5370: - case RF5372: - case RF5390: - case RF5392: - spec->num_channels = 14; - spec->channels = rf_vals_3x; - break; - - case RF3052: - case RF3053: - spec->num_channels = ARRAY_SIZE(rf_vals_3x); - spec->channels = rf_vals_3x; - break; - - case RF5592: - rt2800_register_read(rt2x00dev, MAC_DEBUG_INDEX, ®); - if (rt2x00_get_field32(reg, MAC_DEBUG_INDEX_XTAL)) { - spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal40); - spec->channels = rf_vals_5592_xtal40; - } else { - spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal20); - spec->channels = rf_vals_5592_xtal20; - } - break; - } - - if (WARN_ON_ONCE(!spec->channels)) - return -ENODEV; - - spec->supported_bands = SUPPORT_BAND_2GHZ; - if (spec->num_channels > 14) - spec->supported_bands |= SUPPORT_BAND_5GHZ; - - /* - * Initialize HT information. - */ - if (!rt2x00_rf(rt2x00dev, RF2020)) - spec->ht.ht_supported = true; - else - spec->ht.ht_supported = false; - - spec->ht.cap = - IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_GRN_FLD | - IEEE80211_HT_CAP_SGI_20 | - IEEE80211_HT_CAP_SGI_40; - - if (rt2x00dev->default_ant.tx_chain_num >= 2) - spec->ht.cap |= IEEE80211_HT_CAP_TX_STBC; - - spec->ht.cap |= rt2x00dev->default_ant.rx_chain_num << - IEEE80211_HT_CAP_RX_STBC_SHIFT; - - spec->ht.ampdu_factor = 3; - spec->ht.ampdu_density = 4; - spec->ht.mcs.tx_params = - IEEE80211_HT_MCS_TX_DEFINED | - IEEE80211_HT_MCS_TX_RX_DIFF | - ((rt2x00dev->default_ant.tx_chain_num - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - - switch (rt2x00dev->default_ant.rx_chain_num) { - case 3: - spec->ht.mcs.rx_mask[2] = 0xff; - case 2: - spec->ht.mcs.rx_mask[1] = 0xff; - case 1: - spec->ht.mcs.rx_mask[0] = 0xff; - spec->ht.mcs.rx_mask[4] = 0x1; /* MCS32 */ - break; - } - - /* - * Create channel information array - */ - info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - spec->channels_info = info; - - default_power1 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1); - default_power2 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2); - - if (rt2x00dev->default_ant.tx_chain_num > 2) - default_power3 = rt2800_eeprom_addr(rt2x00dev, - EEPROM_EXT_TXPOWER_BG3); - else - default_power3 = NULL; - - for (i = 0; i < 14; i++) { - info[i].default_power1 = default_power1[i]; - info[i].default_power2 = default_power2[i]; - if (default_power3) - info[i].default_power3 = default_power3[i]; - } - - if (spec->num_channels > 14) { - default_power1 = rt2800_eeprom_addr(rt2x00dev, - EEPROM_TXPOWER_A1); - default_power2 = rt2800_eeprom_addr(rt2x00dev, - EEPROM_TXPOWER_A2); - - if (rt2x00dev->default_ant.tx_chain_num > 2) - default_power3 = - rt2800_eeprom_addr(rt2x00dev, - EEPROM_EXT_TXPOWER_A3); - else - default_power3 = NULL; - - for (i = 14; i < spec->num_channels; i++) { - info[i].default_power1 = default_power1[i - 14]; - info[i].default_power2 = default_power2[i - 14]; - if (default_power3) - info[i].default_power3 = default_power3[i - 14]; - } - } - - switch (rt2x00dev->chip.rf) { - case RF2020: - case RF3020: - case RF3021: - case RF3022: - case RF3320: - case RF3052: - case RF3053: - case RF3070: - case RF3290: - case RF5360: - case RF5362: - case RF5370: - case RF5372: - case RF5390: - case RF5392: - __set_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags); - break; - } - - return 0; -} - -static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - u32 rt; - u32 rev; - - if (rt2x00_rt(rt2x00dev, RT3290)) - rt2800_register_read(rt2x00dev, MAC_CSR0_3290, ®); - else - rt2800_register_read(rt2x00dev, MAC_CSR0, ®); - - rt = rt2x00_get_field32(reg, MAC_CSR0_CHIPSET); - rev = rt2x00_get_field32(reg, MAC_CSR0_REVISION); - - switch (rt) { - case RT2860: - case RT2872: - case RT2883: - case RT3070: - case RT3071: - case RT3090: - case RT3290: - case RT3352: - case RT3390: - case RT3572: - case RT3593: - case RT5390: - case RT5392: - case RT5592: - break; - default: - rt2x00_err(rt2x00dev, "Invalid RT chipset 0x%04x, rev %04x detected\n", - rt, rev); - return -ENODEV; - } - - rt2x00_set_rt(rt2x00dev, rt, rev); - - return 0; -} - -int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev) -{ - int retval; - u32 reg; - - retval = rt2800_probe_rt(rt2x00dev); - if (retval) - return retval; - - /* - * Allocate eeprom data. - */ - retval = rt2800_validate_eeprom(rt2x00dev); - if (retval) - return retval; - - retval = rt2800_init_eeprom(rt2x00dev); - if (retval) - return retval; - - /* - * Enable rfkill polling by setting GPIO direction of the - * rfkill switch GPIO pin correctly. - */ - rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); - rt2x00_set_field32(®, GPIO_CTRL_DIR2, 1); - rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); - - /* - * Initialize hw specifications. - */ - retval = rt2800_probe_hw_mode(rt2x00dev); - if (retval) - return retval; - - /* - * Set device capabilities. - */ - __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); - __set_bit(CAPABILITY_CONTROL_FILTER_PSPOLL, &rt2x00dev->cap_flags); - if (!rt2x00_is_usb(rt2x00dev)) - __set_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags); - - /* - * Set device requirements. - */ - if (!rt2x00_is_soc(rt2x00dev)) - __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags); - if (!rt2800_hwcrypt_disabled(rt2x00dev)) - __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); - __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags); - if (rt2x00_is_usb(rt2x00dev)) - __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); - else { - __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags); - } - - /* - * Set the rssi offset. - */ - rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; - - return 0; -} -EXPORT_SYMBOL_GPL(rt2800_probe_hw); - -/* - * IEEE80211 stack callback functions. - */ -void rt2800_get_key_seq(struct ieee80211_hw *hw, - struct ieee80211_key_conf *key, - struct ieee80211_key_seq *seq) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct mac_iveiv_entry iveiv_entry; - u32 offset; - - if (key->cipher != WLAN_CIPHER_SUITE_TKIP) - return; - - offset = MAC_IVEIV_ENTRY(key->hw_key_idx); - rt2800_register_multiread(rt2x00dev, offset, - &iveiv_entry, sizeof(iveiv_entry)); - - memcpy(&seq->tkip.iv16, &iveiv_entry.iv[0], 2); - memcpy(&seq->tkip.iv32, &iveiv_entry.iv[4], 4); -} -EXPORT_SYMBOL_GPL(rt2800_get_key_seq); - -int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u32 reg; - bool enabled = (value < IEEE80211_MAX_RTS_THRESHOLD); - - rt2800_register_read(rt2x00dev, TX_RTS_CFG, ®); - rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, value); - rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg); - - rt2800_register_read(rt2x00dev, CCK_PROT_CFG, ®); - rt2x00_set_field32(®, CCK_PROT_CFG_RTS_TH_EN, enabled); - rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); - rt2x00_set_field32(®, OFDM_PROT_CFG_RTS_TH_EN, enabled); - rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); - rt2x00_set_field32(®, MM20_PROT_CFG_RTS_TH_EN, enabled); - rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); - rt2x00_set_field32(®, MM40_PROT_CFG_RTS_TH_EN, enabled); - rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); - rt2x00_set_field32(®, GF20_PROT_CFG_RTS_TH_EN, enabled); - rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); - - rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); - rt2x00_set_field32(®, GF40_PROT_CFG_RTS_TH_EN, enabled); - rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2800_set_rts_threshold); - -int rt2800_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue_idx, - const struct ieee80211_tx_queue_params *params) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct data_queue *queue; - struct rt2x00_field32 field; - int retval; - u32 reg; - u32 offset; - - /* - * First pass the configuration through rt2x00lib, that will - * update the queue settings and validate the input. After that - * we are free to update the registers based on the value - * in the queue parameter. - */ - retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); - if (retval) - return retval; - - /* - * We only need to perform additional register initialization - * for WMM queues/ - */ - if (queue_idx >= 4) - return 0; - - queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); - - /* Update WMM TXOP register */ - offset = WMM_TXOP0_CFG + (sizeof(u32) * (!!(queue_idx & 2))); - field.bit_offset = (queue_idx & 1) * 16; - field.bit_mask = 0xffff << field.bit_offset; - - rt2800_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, field, queue->txop); - rt2800_register_write(rt2x00dev, offset, reg); - - /* Update WMM registers */ - field.bit_offset = queue_idx * 4; - field.bit_mask = 0xf << field.bit_offset; - - rt2800_register_read(rt2x00dev, WMM_AIFSN_CFG, ®); - rt2x00_set_field32(®, field, queue->aifs); - rt2800_register_write(rt2x00dev, WMM_AIFSN_CFG, reg); - - rt2800_register_read(rt2x00dev, WMM_CWMIN_CFG, ®); - rt2x00_set_field32(®, field, queue->cw_min); - rt2800_register_write(rt2x00dev, WMM_CWMIN_CFG, reg); - - rt2800_register_read(rt2x00dev, WMM_CWMAX_CFG, ®); - rt2x00_set_field32(®, field, queue->cw_max); - rt2800_register_write(rt2x00dev, WMM_CWMAX_CFG, reg); - - /* Update EDCA registers */ - offset = EDCA_AC0_CFG + (sizeof(u32) * queue_idx); - - rt2800_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, EDCA_AC0_CFG_TX_OP, queue->txop); - rt2x00_set_field32(®, EDCA_AC0_CFG_AIFSN, queue->aifs); - rt2x00_set_field32(®, EDCA_AC0_CFG_CWMIN, queue->cw_min); - rt2x00_set_field32(®, EDCA_AC0_CFG_CWMAX, queue->cw_max); - rt2800_register_write(rt2x00dev, offset, reg); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2800_conf_tx); - -u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u64 tsf; - u32 reg; - - rt2800_register_read(rt2x00dev, TSF_TIMER_DW1, ®); - tsf = (u64) rt2x00_get_field32(reg, TSF_TIMER_DW1_HIGH_WORD) << 32; - rt2800_register_read(rt2x00dev, TSF_TIMER_DW0, ®); - tsf |= rt2x00_get_field32(reg, TSF_TIMER_DW0_LOW_WORD); - - return tsf; -} -EXPORT_SYMBOL_GPL(rt2800_get_tsf); - -int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) -{ - struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv; - int ret = 0; - - /* - * Don't allow aggregation for stations the hardware isn't aware - * of because tx status reports for frames to an unknown station - * always contain wcid=WCID_END+1 and thus we can't distinguish - * between multiple stations which leads to unwanted situations - * when the hw reorders frames due to aggregation. - */ - if (sta_priv->wcid > WCID_END) - return 1; - - switch (action) { - case IEEE80211_AMPDU_RX_START: - case IEEE80211_AMPDU_RX_STOP: - /* - * The hw itself takes care of setting up BlockAck mechanisms. - * So, we only have to allow mac80211 to nagotiate a BlockAck - * agreement. Once that is done, the hw will BlockAck incoming - * AMPDUs without further setup. - */ - break; - case IEEE80211_AMPDU_TX_START: - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - case IEEE80211_AMPDU_TX_STOP_CONT: - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - break; - default: - rt2x00_warn((struct rt2x00_dev *)hw->priv, - "Unknown AMPDU action\n"); - } - - return ret; -} -EXPORT_SYMBOL_GPL(rt2800_ampdu_action); - -int rt2800_get_survey(struct ieee80211_hw *hw, int idx, - struct survey_info *survey) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct ieee80211_conf *conf = &hw->conf; - u32 idle, busy, busy_ext; - - if (idx != 0) - return -ENOENT; - - survey->channel = conf->chandef.chan; - - rt2800_register_read(rt2x00dev, CH_IDLE_STA, &idle); - rt2800_register_read(rt2x00dev, CH_BUSY_STA, &busy); - rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, &busy_ext); - - if (idle || busy) { - survey->filled = SURVEY_INFO_TIME | - SURVEY_INFO_TIME_BUSY | - SURVEY_INFO_TIME_EXT_BUSY; - - survey->time = (idle + busy) / 1000; - survey->time_busy = busy / 1000; - survey->time_ext_busy = busy_ext / 1000; - } - - if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) - survey->filled |= SURVEY_INFO_IN_USE; - - return 0; - -} -EXPORT_SYMBOL_GPL(rt2800_get_survey); - -MODULE_AUTHOR(DRV_PROJECT ", Bartlomiej Zolnierkiewicz"); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("Ralink RT2800 library"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h deleted file mode 100644 index 440790b92b19..000000000000 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - Copyright (C) 2010 Willow Garage - Copyright (C) 2010 Ivo van Doorn - Copyright (C) 2009 Bartlomiej Zolnierkiewicz - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -#ifndef RT2800LIB_H -#define RT2800LIB_H - -struct rt2800_ops { - void (*register_read)(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, u32 *value); - void (*register_read_lock)(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, u32 *value); - void (*register_write)(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, u32 value); - void (*register_write_lock)(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, u32 value); - - void (*register_multiread)(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - void *value, const u32 length); - void (*register_multiwrite)(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - const void *value, const u32 length); - - int (*regbusy_read)(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - const struct rt2x00_field32 field, u32 *reg); - - int (*read_eeprom)(struct rt2x00_dev *rt2x00dev); - bool (*hwcrypt_disabled)(struct rt2x00_dev *rt2x00dev); - - int (*drv_write_firmware)(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len); - int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev); - __le32 *(*drv_get_txwi)(struct queue_entry *entry); -}; - -static inline void rt2800_register_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 *value) -{ - const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - - rt2800ops->register_read(rt2x00dev, offset, value); -} - -static inline void rt2800_register_read_lock(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 *value) -{ - const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - - rt2800ops->register_read_lock(rt2x00dev, offset, value); -} - -static inline void rt2800_register_write(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 value) -{ - const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - - rt2800ops->register_write(rt2x00dev, offset, value); -} - -static inline void rt2800_register_write_lock(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 value) -{ - const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - - rt2800ops->register_write_lock(rt2x00dev, offset, value); -} - -static inline void rt2800_register_multiread(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - void *value, const u32 length) -{ - const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - - rt2800ops->register_multiread(rt2x00dev, offset, value, length); -} - -static inline void rt2800_register_multiwrite(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - const void *value, - const u32 length) -{ - const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - - rt2800ops->register_multiwrite(rt2x00dev, offset, value, length); -} - -static inline int rt2800_regbusy_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - const struct rt2x00_field32 field, - u32 *reg) -{ - const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - - return rt2800ops->regbusy_read(rt2x00dev, offset, field, reg); -} - -static inline int rt2800_read_eeprom(struct rt2x00_dev *rt2x00dev) -{ - const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - - return rt2800ops->read_eeprom(rt2x00dev); -} - -static inline bool rt2800_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) -{ - const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - - return rt2800ops->hwcrypt_disabled(rt2x00dev); -} - -static inline int rt2800_drv_write_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - - return rt2800ops->drv_write_firmware(rt2x00dev, data, len); -} - -static inline int rt2800_drv_init_registers(struct rt2x00_dev *rt2x00dev) -{ - const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; - - return rt2800ops->drv_init_registers(rt2x00dev); -} - -static inline __le32 *rt2800_drv_get_txwi(struct queue_entry *entry) -{ - const struct rt2800_ops *rt2800ops = entry->queue->rt2x00dev->ops->drv; - - return rt2800ops->drv_get_txwi(entry); -} - -void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, - const u8 command, const u8 token, - const u8 arg0, const u8 arg1); - -int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev); -int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev); - -int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len); -int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len); - -void rt2800_write_tx_data(struct queue_entry *entry, - struct txentry_desc *txdesc); -void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc); - -void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32* txwi); - -void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc); -void rt2800_clear_beacon(struct queue_entry *entry); - -extern const struct rt2x00debug rt2800_rt2x00debug; - -int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev); -int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key); -int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key); -int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, int wcid); -void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, - const unsigned int filter_flags); -void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, - struct rt2x00intf_conf *conf, const unsigned int flags); -void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, - u32 changed); -void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant); -void rt2800_config(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf, - const unsigned int flags); -void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual); -void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual); -void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, - const u32 count); -void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev); -void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev); - -int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev); -void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev); - -int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev); -int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev); - -int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev); - -void rt2800_get_key_seq(struct ieee80211_hw *hw, - struct ieee80211_key_conf *key, - struct ieee80211_key_seq *seq); -int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value); -int rt2800_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue_idx, - const struct ieee80211_tx_queue_params *params); -u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); -int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu); -int rt2800_get_survey(struct ieee80211_hw *hw, int idx, - struct survey_info *survey); -void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev); - -void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev, - unsigned short *txwi_size, - unsigned short *rxwi_size); - -#endif /* RT2800LIB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.c b/drivers/net/wireless/rt2x00/rt2800mmio.c deleted file mode 100644 index de4790b41be7..000000000000 --- a/drivers/net/wireless/rt2x00/rt2800mmio.c +++ /dev/null @@ -1,871 +0,0 @@ -/* Copyright (C) 2009 - 2010 Ivo van Doorn - * Copyright (C) 2009 Alban Browaeys - * Copyright (C) 2009 Felix Fietkau - * Copyright (C) 2009 Luis Correia - * Copyright (C) 2009 Mattias Nissler - * Copyright (C) 2009 Mark Asselstine - * Copyright (C) 2009 Xose Vazquez Perez - * Copyright (C) 2009 Bart Zolnierkiewicz - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -/* Module: rt2800mmio - * Abstract: rt2800 MMIO device routines. - */ - -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00mmio.h" -#include "rt2800.h" -#include "rt2800lib.h" -#include "rt2800mmio.h" - -/* - * TX descriptor initialization - */ -__le32 *rt2800mmio_get_txwi(struct queue_entry *entry) -{ - return (__le32 *) entry->skb->data; -} -EXPORT_SYMBOL_GPL(rt2800mmio_get_txwi); - -void rt2800mmio_write_tx_desc(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - __le32 *txd = entry_priv->desc; - u32 word; - const unsigned int txwi_size = entry->queue->winfo_size; - - /* - * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 - * must contains a TXWI structure + 802.11 header + padding + 802.11 - * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and - * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11 - * data. It means that LAST_SEC0 is always 0. - */ - - /* - * Initialize TX descriptor - */ - word = 0; - rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma); - rt2x00_desc_write(txd, 0, word); - - word = 0; - rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len); - rt2x00_set_field32(&word, TXD_W1_LAST_SEC1, - !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W1_BURST, - test_bit(ENTRY_TXD_BURST, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size); - rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); - rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); - rt2x00_desc_write(txd, 1, word); - - word = 0; - rt2x00_set_field32(&word, TXD_W2_SD_PTR1, - skbdesc->skb_dma + txwi_size); - rt2x00_desc_write(txd, 2, word); - - word = 0; - rt2x00_set_field32(&word, TXD_W3_WIV, - !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W3_QSEL, 2); - rt2x00_desc_write(txd, 3, word); - - /* - * Register descriptor details in skb frame descriptor. - */ - skbdesc->desc = txd; - skbdesc->desc_len = TXD_DESC_SIZE; -} -EXPORT_SYMBOL_GPL(rt2800mmio_write_tx_desc); - -/* - * RX control handlers - */ -void rt2800mmio_fill_rxdone(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - __le32 *rxd = entry_priv->desc; - u32 word; - - rt2x00_desc_read(rxd, 3, &word); - - if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; - - /* - * Unfortunately we don't know the cipher type used during - * decryption. This prevents us from correct providing - * correct statistics through debugfs. - */ - rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR); - - if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) { - /* - * Hardware has stripped IV/EIV data from 802.11 frame during - * decryption. Unfortunately the descriptor doesn't contain - * any fields with the EIV/IV data either, so they can't - * be restored by rt2x00lib. - */ - rxdesc->flags |= RX_FLAG_IV_STRIPPED; - - /* - * The hardware has already checked the Michael Mic and has - * stripped it from the frame. Signal this to mac80211. - */ - rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; - - if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) - rxdesc->flags |= RX_FLAG_DECRYPTED; - else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) - rxdesc->flags |= RX_FLAG_MMIC_ERROR; - } - - if (rt2x00_get_field32(word, RXD_W3_MY_BSS)) - rxdesc->dev_flags |= RXDONE_MY_BSS; - - if (rt2x00_get_field32(word, RXD_W3_L2PAD)) - rxdesc->dev_flags |= RXDONE_L2PAD; - - /* - * Process the RXWI structure that is at the start of the buffer. - */ - rt2800_process_rxwi(entry, rxdesc); -} -EXPORT_SYMBOL_GPL(rt2800mmio_fill_rxdone); - -/* - * Interrupt functions. - */ -static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev) -{ - struct ieee80211_conf conf = { .flags = 0 }; - struct rt2x00lib_conf libconf = { .conf = &conf }; - - rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); -} - -static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status) -{ - __le32 *txwi; - u32 word; - int wcid, tx_wcid; - - wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); - - txwi = rt2800_drv_get_txwi(entry); - rt2x00_desc_read(txwi, 1, &word); - tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); - - return (tx_wcid == wcid); -} - -static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data) -{ - u32 status = *(u32 *)data; - - /* - * rt2800pci hardware might reorder frames when exchanging traffic - * with multiple BA enabled STAs. - * - * For example, a tx queue - * [ STA1 | STA2 | STA1 | STA2 ] - * can result in tx status reports - * [ STA1 | STA1 | STA2 | STA2 ] - * when the hw decides to aggregate the frames for STA1 into one AMPDU. - * - * To mitigate this effect, associate the tx status to the first frame - * in the tx queue with a matching wcid. - */ - if (rt2800mmio_txdone_entry_check(entry, status) && - !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - /* - * Got a matching frame, associate the tx status with - * the frame - */ - entry->status = status; - set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); - return true; - } - - /* Check the next frame */ - return false; -} - -static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data) -{ - u32 status = *(u32 *)data; - - /* - * Find the first frame without tx status and assign this status to it - * regardless if it matches or not. - */ - if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - /* - * Got a matching frame, associate the tx status with - * the frame - */ - entry->status = status; - set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); - return true; - } - - /* Check the next frame */ - return false; -} -static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry, - void *data) -{ - if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - rt2800_txdone_entry(entry, entry->status, - rt2800mmio_get_txwi(entry)); - return false; - } - - /* No more frames to release */ - return true; -} - -static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - u32 status; - u8 qid; - int max_tx_done = 16; - - while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { - qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); - if (unlikely(qid >= QID_RX)) { - /* - * Unknown queue, this shouldn't happen. Just drop - * this tx status. - */ - rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n", - qid); - break; - } - - queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); - if (unlikely(queue == NULL)) { - /* - * The queue is NULL, this shouldn't happen. Stop - * processing here and drop the tx status - */ - rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n", - qid); - break; - } - - if (unlikely(rt2x00queue_empty(queue))) { - /* - * The queue is empty. Stop processing here - * and drop the tx status. - */ - rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", - qid); - break; - } - - /* - * Let's associate this tx status with the first - * matching frame. - */ - if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, &status, - rt2800mmio_txdone_find_entry)) { - /* - * We cannot match the tx status to any frame, so just - * use the first one. - */ - if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, &status, - rt2800mmio_txdone_match_first)) { - rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n", - qid); - break; - } - } - - /* - * Release all frames with a valid tx status. - */ - rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, NULL, - rt2800mmio_txdone_release_entries); - - if (--max_tx_done == 0) - break; - } - - return !max_tx_done; -} - -static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, - struct rt2x00_field32 irq_field) -{ - u32 reg; - - /* - * Enable a single interrupt. The interrupt mask register - * access needs locking. - */ - spin_lock_irq(&rt2x00dev->irqmask_lock); - rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); - rt2x00_set_field32(®, irq_field, 1); - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - spin_unlock_irq(&rt2x00dev->irqmask_lock); -} - -void rt2800mmio_txstatus_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - if (rt2800mmio_txdone(rt2x00dev)) - tasklet_schedule(&rt2x00dev->txstatus_tasklet); - - /* - * No need to enable the tx status interrupt here as we always - * leave it enabled to minimize the possibility of a tx status - * register overflow. See comment in interrupt handler. - */ -} -EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet); - -void rt2800mmio_pretbtt_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - rt2x00lib_pretbtt(rt2x00dev); - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); -} -EXPORT_SYMBOL_GPL(rt2800mmio_pretbtt_tasklet); - -void rt2800mmio_tbtt_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - u32 reg; - - rt2x00lib_beacondone(rt2x00dev); - - if (rt2x00dev->intf_ap_count) { - /* - * The rt2800pci hardware tbtt timer is off by 1us per tbtt - * causing beacon skew and as a result causing problems with - * some powersaving clients over time. Shorten the beacon - * interval every 64 beacons by 64us to mitigate this effect. - */ - if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, - (rt2x00dev->beacon_int * 16) - 1); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, - (rt2x00dev->beacon_int * 16)); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - } - drv_data->tbtt_tick++; - drv_data->tbtt_tick %= BCN_TBTT_OFFSET; - } - - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); -} -EXPORT_SYMBOL_GPL(rt2800mmio_tbtt_tasklet); - -void rt2800mmio_rxdone_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - if (rt2x00mmio_rxdone(rt2x00dev)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); -} -EXPORT_SYMBOL_GPL(rt2800mmio_rxdone_tasklet); - -void rt2800mmio_autowake_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - rt2800mmio_wakeup(rt2x00dev); - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800mmio_enable_interrupt(rt2x00dev, - INT_MASK_CSR_AUTO_WAKEUP); -} -EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet); - -static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) -{ - u32 status; - int i; - - /* - * The TX_FIFO_STATUS interrupt needs special care. We should - * read TX_STA_FIFO but we should do it immediately as otherwise - * the register can overflow and we would lose status reports. - * - * Hence, read the TX_STA_FIFO register and copy all tx status - * reports into a kernel FIFO which is handled in the txstatus - * tasklet. We use a tasklet to process the tx status reports - * because we can schedule the tasklet multiple times (when the - * interrupt fires again during tx status processing). - * - * Furthermore we don't disable the TX_FIFO_STATUS - * interrupt here but leave it enabled so that the TX_STA_FIFO - * can also be read while the tx status tasklet gets executed. - * - * Since we have only one producer and one consumer we don't - * need to lock the kfifo. - */ - for (i = 0; i < rt2x00dev->tx->limit; i++) { - rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); - - if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) - break; - - if (!kfifo_put(&rt2x00dev->txstatus_fifo, status)) { - rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n"); - break; - } - } - - /* Schedule the tasklet for processing the tx status. */ - tasklet_schedule(&rt2x00dev->txstatus_tasklet); -} - -irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) -{ - struct rt2x00_dev *rt2x00dev = dev_instance; - u32 reg, mask; - - /* Read status and ACK all interrupts */ - rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); - rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); - - if (!reg) - return IRQ_NONE; - - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return IRQ_HANDLED; - - /* - * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits - * for interrupts and interrupt masks we can just use the value of - * INT_SOURCE_CSR to create the interrupt mask. - */ - mask = ~reg; - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { - rt2800mmio_txstatus_interrupt(rt2x00dev); - /* - * Never disable the TX_FIFO_STATUS interrupt. - */ - rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); - } - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) - tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) - tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) - tasklet_schedule(&rt2x00dev->autowake_tasklet); - - /* - * Disable all interrupts for which a tasklet was scheduled right now, - * the tasklet will reenable the appropriate interrupts. - */ - spin_lock(&rt2x00dev->irqmask_lock); - rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); - reg &= mask; - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - spin_unlock(&rt2x00dev->irqmask_lock); - - return IRQ_HANDLED; -} -EXPORT_SYMBOL_GPL(rt2800mmio_interrupt); - -void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - u32 reg; - unsigned long flags; - - /* - * When interrupts are being enabled, the interrupt registers - * should clear the register to assure a clean state. - */ - if (state == STATE_RADIO_IRQ_ON) { - rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); - rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); - } - - spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); - reg = 0; - if (state == STATE_RADIO_IRQ_ON) { - rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); - rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); - rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); - rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); - rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); - } - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); - - if (state == STATE_RADIO_IRQ_OFF) { - /* - * Wait for possibly running tasklets to finish. - */ - tasklet_kill(&rt2x00dev->txstatus_tasklet); - tasklet_kill(&rt2x00dev->rxdone_tasklet); - tasklet_kill(&rt2x00dev->autowake_tasklet); - tasklet_kill(&rt2x00dev->tbtt_tasklet); - tasklet_kill(&rt2x00dev->pretbtt_tasklet); - } -} -EXPORT_SYMBOL_GPL(rt2800mmio_toggle_irq); - -/* - * Queue handlers. - */ -void rt2800mmio_start_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - - rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); - rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 1); - rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); - break; - default: - break; - } -} -EXPORT_SYMBOL_GPL(rt2800mmio_start_queue); - -void rt2800mmio_kick_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - struct queue_entry *entry; - - switch (queue->qid) { - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - entry = rt2x00queue_get_entry(queue, Q_INDEX); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid), - entry->entry_idx); - break; - case QID_MGMT: - entry = rt2x00queue_get_entry(queue, Q_INDEX); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5), - entry->entry_idx); - break; - default: - break; - } -} -EXPORT_SYMBOL_GPL(rt2800mmio_kick_queue); - -void rt2800mmio_stop_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - - rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); - rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 0); - rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); - - /* - * Wait for current invocation to finish. The tasklet - * won't be scheduled anymore afterwards since we disabled - * the TBTT and PRE TBTT timer. - */ - tasklet_kill(&rt2x00dev->tbtt_tasklet); - tasklet_kill(&rt2x00dev->pretbtt_tasklet); - - break; - default: - break; - } -} -EXPORT_SYMBOL_GPL(rt2800mmio_stop_queue); - -void rt2800mmio_queue_init(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - unsigned short txwi_size, rxwi_size; - - rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); - - switch (queue->qid) { - case QID_RX: - queue->limit = 128; - queue->data_size = AGGREGATION_SIZE; - queue->desc_size = RXD_DESC_SIZE; - queue->winfo_size = rxwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - queue->limit = 64; - queue->data_size = AGGREGATION_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->winfo_size = txwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_BEACON: - queue->limit = 8; - queue->data_size = 0; /* No DMA required for beacons */ - queue->desc_size = TXD_DESC_SIZE; - queue->winfo_size = txwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_ATIM: - /* fallthrough */ - default: - BUG(); - break; - } -} -EXPORT_SYMBOL_GPL(rt2800mmio_queue_init); - -/* - * Initialization functions. - */ -bool rt2800mmio_get_entry_state(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 1, &word); - - return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE)); - } else { - rt2x00_desc_read(entry_priv->desc, 1, &word); - - return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE)); - } -} -EXPORT_SYMBOL_GPL(rt2800mmio_get_entry_state); - -void rt2800mmio_clear_entry(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma); - rt2x00_desc_write(entry_priv->desc, 0, word); - - rt2x00_desc_read(entry_priv->desc, 1, &word); - rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0); - rt2x00_desc_write(entry_priv->desc, 1, word); - - /* - * Set RX IDX in register to inform hardware that we have - * handled this entry and it is available for reuse again. - */ - rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, - entry->entry_idx); - } else { - rt2x00_desc_read(entry_priv->desc, 1, &word); - rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1); - rt2x00_desc_write(entry_priv->desc, 1, word); - } -} -EXPORT_SYMBOL_GPL(rt2800mmio_clear_entry); - -int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev) -{ - struct queue_entry_priv_mmio *entry_priv; - - /* - * Initialize registers. - */ - entry_priv = rt2x00dev->tx[0].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0, - rt2x00dev->tx[0].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0); - - entry_priv = rt2x00dev->tx[1].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1, - rt2x00dev->tx[1].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0); - - entry_priv = rt2x00dev->tx[2].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2, - rt2x00dev->tx[2].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0); - - entry_priv = rt2x00dev->tx[3].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3, - rt2x00dev->tx[3].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0); - - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0); - - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0); - - entry_priv = rt2x00dev->rx->entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT, - rt2x00dev->rx[0].limit); - rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, - rt2x00dev->rx[0].limit - 1); - rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0); - - rt2800_disable_wpdma(rt2x00dev); - - rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2800mmio_init_queues); - -int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - /* - * Reset DMA indexes - */ - rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX, ®); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); - rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg); - - rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); - rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); - - if (rt2x00_is_pcie(rt2x00dev) && - (rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3390) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT3593) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392) || - rt2x00_rt(rt2x00dev, RT5592))) { - rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, ®); - rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); - rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); - rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg); - } - - rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); - - reg = 0; - rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); - rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2800mmio_init_registers); - -/* - * Device state switch handlers. - */ -int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev) -{ - /* Wait for DMA, ignore error until we initialize queues. */ - rt2800_wait_wpdma_ready(rt2x00dev); - - if (unlikely(rt2800mmio_init_queues(rt2x00dev))) - return -EIO; - - return rt2800_enable_radio(rt2x00dev); -} -EXPORT_SYMBOL_GPL(rt2800mmio_enable_radio); - -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("rt2800 MMIO library"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.h b/drivers/net/wireless/rt2x00/rt2800mmio.h deleted file mode 100644 index b63312ce3f27..000000000000 --- a/drivers/net/wireless/rt2x00/rt2800mmio.h +++ /dev/null @@ -1,163 +0,0 @@ -/* Copyright (C) 2009 - 2010 Ivo van Doorn - * Copyright (C) 2009 Alban Browaeys - * Copyright (C) 2009 Felix Fietkau - * Copyright (C) 2009 Luis Correia - * Copyright (C) 2009 Mattias Nissler - * Copyright (C) 2009 Mark Asselstine - * Copyright (C) 2009 Xose Vazquez Perez - * Copyright (C) 2009 Bart Zolnierkiewicz - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -/* Module: rt2800mmio - * Abstract: forward declarations for the rt2800mmio module. - */ - -#ifndef RT2800MMIO_H -#define RT2800MMIO_H - -/* - * Queue register offset macros - */ -#define TX_QUEUE_REG_OFFSET 0x10 -#define TX_BASE_PTR(__x) (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET)) -#define TX_MAX_CNT(__x) (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET)) -#define TX_CTX_IDX(__x) (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) -#define TX_DTX_IDX(__x) (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) - -/* - * DMA descriptor defines. - */ -#define TXD_DESC_SIZE (4 * sizeof(__le32)) -#define RXD_DESC_SIZE (4 * sizeof(__le32)) - -/* - * TX descriptor format for TX, PRIO and Beacon Ring. - */ - -/* - * Word0 - */ -#define TXD_W0_SD_PTR0 FIELD32(0xffffffff) - -/* - * Word1 - */ -#define TXD_W1_SD_LEN1 FIELD32(0x00003fff) -#define TXD_W1_LAST_SEC1 FIELD32(0x00004000) -#define TXD_W1_BURST FIELD32(0x00008000) -#define TXD_W1_SD_LEN0 FIELD32(0x3fff0000) -#define TXD_W1_LAST_SEC0 FIELD32(0x40000000) -#define TXD_W1_DMA_DONE FIELD32(0x80000000) - -/* - * Word2 - */ -#define TXD_W2_SD_PTR1 FIELD32(0xffffffff) - -/* - * Word3 - * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI - * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. - * 0:MGMT, 1:HCCA 2:EDCA - */ -#define TXD_W3_WIV FIELD32(0x01000000) -#define TXD_W3_QSEL FIELD32(0x06000000) -#define TXD_W3_TCO FIELD32(0x20000000) -#define TXD_W3_UCO FIELD32(0x40000000) -#define TXD_W3_ICO FIELD32(0x80000000) - -/* - * RX descriptor format for RX Ring. - */ - -/* - * Word0 - */ -#define RXD_W0_SDP0 FIELD32(0xffffffff) - -/* - * Word1 - */ -#define RXD_W1_SDL1 FIELD32(0x00003fff) -#define RXD_W1_SDL0 FIELD32(0x3fff0000) -#define RXD_W1_LS0 FIELD32(0x40000000) -#define RXD_W1_DMA_DONE FIELD32(0x80000000) - -/* - * Word2 - */ -#define RXD_W2_SDP1 FIELD32(0xffffffff) - -/* - * Word3 - * AMSDU: RX with 802.3 header, not 802.11 header. - * DECRYPTED: This frame is being decrypted. - */ -#define RXD_W3_BA FIELD32(0x00000001) -#define RXD_W3_DATA FIELD32(0x00000002) -#define RXD_W3_NULLDATA FIELD32(0x00000004) -#define RXD_W3_FRAG FIELD32(0x00000008) -#define RXD_W3_UNICAST_TO_ME FIELD32(0x00000010) -#define RXD_W3_MULTICAST FIELD32(0x00000020) -#define RXD_W3_BROADCAST FIELD32(0x00000040) -#define RXD_W3_MY_BSS FIELD32(0x00000080) -#define RXD_W3_CRC_ERROR FIELD32(0x00000100) -#define RXD_W3_CIPHER_ERROR FIELD32(0x00000600) -#define RXD_W3_AMSDU FIELD32(0x00000800) -#define RXD_W3_HTC FIELD32(0x00001000) -#define RXD_W3_RSSI FIELD32(0x00002000) -#define RXD_W3_L2PAD FIELD32(0x00004000) -#define RXD_W3_AMPDU FIELD32(0x00008000) -#define RXD_W3_DECRYPTED FIELD32(0x00010000) -#define RXD_W3_PLCP_SIGNAL FIELD32(0x00020000) -#define RXD_W3_PLCP_RSSI FIELD32(0x00040000) - -/* TX descriptor initialization */ -__le32 *rt2800mmio_get_txwi(struct queue_entry *entry); -void rt2800mmio_write_tx_desc(struct queue_entry *entry, - struct txentry_desc *txdesc); - -/* RX control handlers */ -void rt2800mmio_fill_rxdone(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc); - -/* Interrupt functions */ -void rt2800mmio_txstatus_tasklet(unsigned long data); -void rt2800mmio_pretbtt_tasklet(unsigned long data); -void rt2800mmio_tbtt_tasklet(unsigned long data); -void rt2800mmio_rxdone_tasklet(unsigned long data); -void rt2800mmio_autowake_tasklet(unsigned long data); -irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance); -void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, - enum dev_state state); - -/* Queue handlers */ -void rt2800mmio_start_queue(struct data_queue *queue); -void rt2800mmio_kick_queue(struct data_queue *queue); -void rt2800mmio_stop_queue(struct data_queue *queue); -void rt2800mmio_queue_init(struct data_queue *queue); - -/* Initialization functions */ -bool rt2800mmio_get_entry_state(struct queue_entry *entry); -void rt2800mmio_clear_entry(struct queue_entry *entry); -int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev); -int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev); - -/* Device state switch handlers. */ -int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev); - -#endif /* RT2800MMIO_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c deleted file mode 100644 index 0af22573a2eb..000000000000 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ /dev/null @@ -1,471 +0,0 @@ -/* - Copyright (C) 2009 - 2010 Ivo van Doorn - Copyright (C) 2009 Alban Browaeys - Copyright (C) 2009 Felix Fietkau - Copyright (C) 2009 Luis Correia - Copyright (C) 2009 Mattias Nissler - Copyright (C) 2009 Mark Asselstine - Copyright (C) 2009 Xose Vazquez Perez - Copyright (C) 2009 Bart Zolnierkiewicz - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2800pci - Abstract: rt2800pci device specific routines. - Supported chipsets: RT2800E & RT2800ED. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00mmio.h" -#include "rt2x00pci.h" -#include "rt2800lib.h" -#include "rt2800mmio.h" -#include "rt2800.h" -#include "rt2800pci.h" - -/* - * Allow hardware encryption to be disabled. - */ -static bool modparam_nohwcrypt = false; -module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); -MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); - -static bool rt2800pci_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) -{ - return modparam_nohwcrypt; -} - -static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token) -{ - unsigned int i; - u32 reg; - - /* - * SOC devices don't support MCU requests. - */ - if (rt2x00_is_soc(rt2x00dev)) - return; - - for (i = 0; i < 200; i++) { - rt2x00mmio_register_read(rt2x00dev, H2M_MAILBOX_CID, ®); - - if ((rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD0) == token) || - (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD1) == token) || - (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD2) == token) || - (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD3) == token)) - break; - - udelay(REGISTER_BUSY_DELAY); - } - - if (i == 200) - rt2x00_err(rt2x00dev, "MCU request failed, no response from hardware\n"); - - rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); - rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); -} - -static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom) -{ - struct rt2x00_dev *rt2x00dev = eeprom->data; - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); - - eeprom->reg_data_in = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_IN); - eeprom->reg_data_out = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_OUT); - eeprom->reg_data_clock = - !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_CLOCK); - eeprom->reg_chip_select = - !!rt2x00_get_field32(reg, E2PROM_CSR_CHIP_SELECT); -} - -static void rt2800pci_eepromregister_write(struct eeprom_93cx6 *eeprom) -{ - struct rt2x00_dev *rt2x00dev = eeprom->data; - u32 reg = 0; - - rt2x00_set_field32(®, E2PROM_CSR_DATA_IN, !!eeprom->reg_data_in); - rt2x00_set_field32(®, E2PROM_CSR_DATA_OUT, !!eeprom->reg_data_out); - rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, - !!eeprom->reg_data_clock); - rt2x00_set_field32(®, E2PROM_CSR_CHIP_SELECT, - !!eeprom->reg_chip_select); - - rt2x00mmio_register_write(rt2x00dev, E2PROM_CSR, reg); -} - -static int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) -{ - struct eeprom_93cx6 eeprom; - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); - - eeprom.data = rt2x00dev; - eeprom.register_read = rt2800pci_eepromregister_read; - eeprom.register_write = rt2800pci_eepromregister_write; - switch (rt2x00_get_field32(reg, E2PROM_CSR_TYPE)) - { - case 0: - eeprom.width = PCI_EEPROM_WIDTH_93C46; - break; - case 1: - eeprom.width = PCI_EEPROM_WIDTH_93C66; - break; - default: - eeprom.width = PCI_EEPROM_WIDTH_93C86; - break; - } - eeprom.reg_data_in = 0; - eeprom.reg_data_out = 0; - eeprom.reg_data_clock = 0; - eeprom.reg_chip_select = 0; - - eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, - EEPROM_SIZE / sizeof(u16)); - - return 0; -} - -static int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) -{ - return rt2800_efuse_detect(rt2x00dev); -} - -static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) -{ - return rt2800_read_eeprom_efuse(rt2x00dev); -} - -/* - * Firmware functions - */ -static char *rt2800pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) -{ - /* - * Chip rt3290 use specific 4KB firmware named rt3290.bin. - */ - if (rt2x00_rt(rt2x00dev, RT3290)) - return FIRMWARE_RT3290; - else - return FIRMWARE_RT2860; -} - -static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - u32 reg; - - /* - * enable Host program ram write selection - */ - reg = 0; - rt2x00_set_field32(®, PBF_SYS_CTRL_HOST_RAM_WRITE, 1); - rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, reg); - - /* - * Write firmware to device. - */ - rt2x00mmio_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, - data, len); - - rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000); - rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001); - - rt2x00mmio_register_write(rt2x00dev, H2M_BBP_AGENT, 0); - rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); - - return 0; -} - -/* - * Device state switch handlers. - */ -static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) -{ - int retval; - - retval = rt2800mmio_enable_radio(rt2x00dev); - if (retval) - return retval; - - /* After resume MCU_BOOT_SIGNAL will trash these. */ - rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); - rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); - - rt2800_mcu_request(rt2x00dev, MCU_SLEEP, TOKEN_RADIO_OFF, 0xff, 0x02); - rt2800pci_mcu_status(rt2x00dev, TOKEN_RADIO_OFF); - - rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKEUP, 0, 0); - rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKEUP); - - return retval; -} - -static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - if (state == STATE_AWAKE) { - rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKEUP, - 0, 0x02); - rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKEUP); - } else if (state == STATE_SLEEP) { - rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_STATUS, - 0xffffffff); - rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, - 0xffffffff); - rt2800_mcu_request(rt2x00dev, MCU_SLEEP, TOKEN_SLEEP, - 0xff, 0x01); - } - - return 0; -} - -static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - int retval = 0; - - switch (state) { - case STATE_RADIO_ON: - retval = rt2800pci_enable_radio(rt2x00dev); - break; - case STATE_RADIO_OFF: - /* - * After the radio has been disabled, the device should - * be put to sleep for powersaving. - */ - rt2800pci_set_state(rt2x00dev, STATE_SLEEP); - break; - case STATE_RADIO_IRQ_ON: - case STATE_RADIO_IRQ_OFF: - rt2800mmio_toggle_irq(rt2x00dev, state); - break; - case STATE_DEEP_SLEEP: - case STATE_SLEEP: - case STATE_STANDBY: - case STATE_AWAKE: - retval = rt2800pci_set_state(rt2x00dev, state); - break; - default: - retval = -ENOTSUPP; - break; - } - - if (unlikely(retval)) - rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", - state, retval); - - return retval; -} - -/* - * Device probe functions. - */ -static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) -{ - int retval; - - if (rt2800pci_efuse_detect(rt2x00dev)) - retval = rt2800pci_read_eeprom_efuse(rt2x00dev); - else - retval = rt2800pci_read_eeprom_pci(rt2x00dev); - - return retval; -} - -static const struct ieee80211_ops rt2800pci_mac80211_ops = { - .tx = rt2x00mac_tx, - .start = rt2x00mac_start, - .stop = rt2x00mac_stop, - .add_interface = rt2x00mac_add_interface, - .remove_interface = rt2x00mac_remove_interface, - .config = rt2x00mac_config, - .configure_filter = rt2x00mac_configure_filter, - .set_key = rt2x00mac_set_key, - .sw_scan_start = rt2x00mac_sw_scan_start, - .sw_scan_complete = rt2x00mac_sw_scan_complete, - .get_stats = rt2x00mac_get_stats, - .get_key_seq = rt2800_get_key_seq, - .set_rts_threshold = rt2800_set_rts_threshold, - .sta_add = rt2x00mac_sta_add, - .sta_remove = rt2x00mac_sta_remove, - .bss_info_changed = rt2x00mac_bss_info_changed, - .conf_tx = rt2800_conf_tx, - .get_tsf = rt2800_get_tsf, - .rfkill_poll = rt2x00mac_rfkill_poll, - .ampdu_action = rt2800_ampdu_action, - .flush = rt2x00mac_flush, - .get_survey = rt2800_get_survey, - .get_ringparam = rt2x00mac_get_ringparam, - .tx_frames_pending = rt2x00mac_tx_frames_pending, -}; - -static const struct rt2800_ops rt2800pci_rt2800_ops = { - .register_read = rt2x00mmio_register_read, - .register_read_lock = rt2x00mmio_register_read, /* same for PCI */ - .register_write = rt2x00mmio_register_write, - .register_write_lock = rt2x00mmio_register_write, /* same for PCI */ - .register_multiread = rt2x00mmio_register_multiread, - .register_multiwrite = rt2x00mmio_register_multiwrite, - .regbusy_read = rt2x00mmio_regbusy_read, - .read_eeprom = rt2800pci_read_eeprom, - .hwcrypt_disabled = rt2800pci_hwcrypt_disabled, - .drv_write_firmware = rt2800pci_write_firmware, - .drv_init_registers = rt2800mmio_init_registers, - .drv_get_txwi = rt2800mmio_get_txwi, -}; - -static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { - .irq_handler = rt2800mmio_interrupt, - .txstatus_tasklet = rt2800mmio_txstatus_tasklet, - .pretbtt_tasklet = rt2800mmio_pretbtt_tasklet, - .tbtt_tasklet = rt2800mmio_tbtt_tasklet, - .rxdone_tasklet = rt2800mmio_rxdone_tasklet, - .autowake_tasklet = rt2800mmio_autowake_tasklet, - .probe_hw = rt2800_probe_hw, - .get_firmware_name = rt2800pci_get_firmware_name, - .check_firmware = rt2800_check_firmware, - .load_firmware = rt2800_load_firmware, - .initialize = rt2x00mmio_initialize, - .uninitialize = rt2x00mmio_uninitialize, - .get_entry_state = rt2800mmio_get_entry_state, - .clear_entry = rt2800mmio_clear_entry, - .set_device_state = rt2800pci_set_device_state, - .rfkill_poll = rt2800_rfkill_poll, - .link_stats = rt2800_link_stats, - .reset_tuner = rt2800_reset_tuner, - .link_tuner = rt2800_link_tuner, - .gain_calibration = rt2800_gain_calibration, - .vco_calibration = rt2800_vco_calibration, - .start_queue = rt2800mmio_start_queue, - .kick_queue = rt2800mmio_kick_queue, - .stop_queue = rt2800mmio_stop_queue, - .flush_queue = rt2x00mmio_flush_queue, - .write_tx_desc = rt2800mmio_write_tx_desc, - .write_tx_data = rt2800_write_tx_data, - .write_beacon = rt2800_write_beacon, - .clear_beacon = rt2800_clear_beacon, - .fill_rxdone = rt2800mmio_fill_rxdone, - .config_shared_key = rt2800_config_shared_key, - .config_pairwise_key = rt2800_config_pairwise_key, - .config_filter = rt2800_config_filter, - .config_intf = rt2800_config_intf, - .config_erp = rt2800_config_erp, - .config_ant = rt2800_config_ant, - .config = rt2800_config, - .sta_add = rt2800_sta_add, - .sta_remove = rt2800_sta_remove, -}; - -static const struct rt2x00_ops rt2800pci_ops = { - .name = KBUILD_MODNAME, - .drv_data_size = sizeof(struct rt2800_drv_data), - .max_ap_intf = 8, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .queue_init = rt2800mmio_queue_init, - .lib = &rt2800pci_rt2x00_ops, - .drv = &rt2800pci_rt2800_ops, - .hw = &rt2800pci_mac80211_ops, -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2800_rt2x00debug, -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -}; - -/* - * RT2800pci module information. - */ -static const struct pci_device_id rt2800pci_device_table[] = { - { PCI_DEVICE(0x1814, 0x0601) }, - { PCI_DEVICE(0x1814, 0x0681) }, - { PCI_DEVICE(0x1814, 0x0701) }, - { PCI_DEVICE(0x1814, 0x0781) }, - { PCI_DEVICE(0x1814, 0x3090) }, - { PCI_DEVICE(0x1814, 0x3091) }, - { PCI_DEVICE(0x1814, 0x3092) }, - { PCI_DEVICE(0x1432, 0x7708) }, - { PCI_DEVICE(0x1432, 0x7727) }, - { PCI_DEVICE(0x1432, 0x7728) }, - { PCI_DEVICE(0x1432, 0x7738) }, - { PCI_DEVICE(0x1432, 0x7748) }, - { PCI_DEVICE(0x1432, 0x7758) }, - { PCI_DEVICE(0x1432, 0x7768) }, - { PCI_DEVICE(0x1462, 0x891a) }, - { PCI_DEVICE(0x1a3b, 0x1059) }, -#ifdef CONFIG_RT2800PCI_RT3290 - { PCI_DEVICE(0x1814, 0x3290) }, -#endif -#ifdef CONFIG_RT2800PCI_RT33XX - { PCI_DEVICE(0x1814, 0x3390) }, -#endif -#ifdef CONFIG_RT2800PCI_RT35XX - { PCI_DEVICE(0x1432, 0x7711) }, - { PCI_DEVICE(0x1432, 0x7722) }, - { PCI_DEVICE(0x1814, 0x3060) }, - { PCI_DEVICE(0x1814, 0x3062) }, - { PCI_DEVICE(0x1814, 0x3562) }, - { PCI_DEVICE(0x1814, 0x3592) }, - { PCI_DEVICE(0x1814, 0x3593) }, - { PCI_DEVICE(0x1814, 0x359f) }, -#endif -#ifdef CONFIG_RT2800PCI_RT53XX - { PCI_DEVICE(0x1814, 0x5360) }, - { PCI_DEVICE(0x1814, 0x5362) }, - { PCI_DEVICE(0x1814, 0x5390) }, - { PCI_DEVICE(0x1814, 0x5392) }, - { PCI_DEVICE(0x1814, 0x539a) }, - { PCI_DEVICE(0x1814, 0x539b) }, - { PCI_DEVICE(0x1814, 0x539f) }, -#endif - { 0, } -}; - -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("Ralink RT2800 PCI & PCMCIA Wireless LAN driver."); -MODULE_SUPPORTED_DEVICE("Ralink RT2860 PCI & PCMCIA chipset based cards"); -MODULE_FIRMWARE(FIRMWARE_RT2860); -MODULE_DEVICE_TABLE(pci, rt2800pci_device_table); -MODULE_LICENSE("GPL"); - -static int rt2800pci_probe(struct pci_dev *pci_dev, - const struct pci_device_id *id) -{ - return rt2x00pci_probe(pci_dev, &rt2800pci_ops); -} - -static struct pci_driver rt2800pci_driver = { - .name = KBUILD_MODNAME, - .id_table = rt2800pci_device_table, - .probe = rt2800pci_probe, - .remove = rt2x00pci_remove, - .suspend = rt2x00pci_suspend, - .resume = rt2x00pci_resume, -}; - -module_pci_driver(rt2800pci_driver); diff --git a/drivers/net/wireless/rt2x00/rt2800pci.h b/drivers/net/wireless/rt2x00/rt2800pci.h deleted file mode 100644 index 9dfef4607d6b..000000000000 --- a/drivers/net/wireless/rt2x00/rt2800pci.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2009 Ivo van Doorn - Copyright (C) 2009 Alban Browaeys - Copyright (C) 2009 Felix Fietkau - Copyright (C) 2009 Luis Correia - Copyright (C) 2009 Mattias Nissler - Copyright (C) 2009 Mark Asselstine - Copyright (C) 2009 Xose Vazquez Perez - Copyright (C) 2009 Bart Zolnierkiewicz - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2800pci - Abstract: Data structures and registers for the rt2800pci module. - Supported chipsets: RT2800E & RT2800ED. - */ - -#ifndef RT2800PCI_H -#define RT2800PCI_H - -/* - * 8051 firmware image. - */ -#define FIRMWARE_RT2860 "rt2860.bin" -#define FIRMWARE_RT3290 "rt3290.bin" -#define FIRMWARE_IMAGE_BASE 0x2000 - -#endif /* RT2800PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800soc.c b/drivers/net/wireless/rt2x00/rt2800soc.c deleted file mode 100644 index a985a5a7945e..000000000000 --- a/drivers/net/wireless/rt2x00/rt2800soc.c +++ /dev/null @@ -1,260 +0,0 @@ -/* Copyright (C) 2009 - 2010 Ivo van Doorn - * Copyright (C) 2009 Alban Browaeys - * Copyright (C) 2009 Felix Fietkau - * Copyright (C) 2009 Luis Correia - * Copyright (C) 2009 Mattias Nissler - * Copyright (C) 2009 Mark Asselstine - * Copyright (C) 2009 Xose Vazquez Perez - * Copyright (C) 2009 Bart Zolnierkiewicz - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -/* Module: rt2800soc - * Abstract: rt2800 WiSoC specific routines. - */ - -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00mmio.h" -#include "rt2x00soc.h" -#include "rt2800.h" -#include "rt2800lib.h" -#include "rt2800mmio.h" - -/* Allow hardware encryption to be disabled. */ -static bool modparam_nohwcrypt; -module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); -MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); - -static bool rt2800soc_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) -{ - return modparam_nohwcrypt; -} - -static void rt2800soc_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - rt2800_disable_radio(rt2x00dev); - rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0); - rt2x00mmio_register_write(rt2x00dev, TX_PIN_CFG, 0); -} - -static int rt2800soc_set_device_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - int retval = 0; - - switch (state) { - case STATE_RADIO_ON: - retval = rt2800mmio_enable_radio(rt2x00dev); - break; - - case STATE_RADIO_OFF: - rt2800soc_disable_radio(rt2x00dev); - break; - - case STATE_RADIO_IRQ_ON: - case STATE_RADIO_IRQ_OFF: - rt2800mmio_toggle_irq(rt2x00dev, state); - break; - - case STATE_DEEP_SLEEP: - case STATE_SLEEP: - case STATE_STANDBY: - case STATE_AWAKE: - /* These states are not supported, but don't report an error */ - retval = 0; - break; - - default: - retval = -ENOTSUPP; - break; - } - - if (unlikely(retval)) - rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", - state, retval); - - return retval; -} - -static int rt2800soc_read_eeprom(struct rt2x00_dev *rt2x00dev) -{ - void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE); - - if (!base_addr) - return -ENOMEM; - - memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE); - - iounmap(base_addr); - return 0; -} - -/* Firmware functions */ -static char *rt2800soc_get_firmware_name(struct rt2x00_dev *rt2x00dev) -{ - WARN_ON_ONCE(1); - return NULL; -} - -static int rt2800soc_load_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - WARN_ON_ONCE(1); - return 0; -} - -static int rt2800soc_check_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - WARN_ON_ONCE(1); - return 0; -} - -static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - WARN_ON_ONCE(1); - return 0; -} - -static const struct ieee80211_ops rt2800soc_mac80211_ops = { - .tx = rt2x00mac_tx, - .start = rt2x00mac_start, - .stop = rt2x00mac_stop, - .add_interface = rt2x00mac_add_interface, - .remove_interface = rt2x00mac_remove_interface, - .config = rt2x00mac_config, - .configure_filter = rt2x00mac_configure_filter, - .set_key = rt2x00mac_set_key, - .sw_scan_start = rt2x00mac_sw_scan_start, - .sw_scan_complete = rt2x00mac_sw_scan_complete, - .get_stats = rt2x00mac_get_stats, - .get_key_seq = rt2800_get_key_seq, - .set_rts_threshold = rt2800_set_rts_threshold, - .sta_add = rt2x00mac_sta_add, - .sta_remove = rt2x00mac_sta_remove, - .bss_info_changed = rt2x00mac_bss_info_changed, - .conf_tx = rt2800_conf_tx, - .get_tsf = rt2800_get_tsf, - .rfkill_poll = rt2x00mac_rfkill_poll, - .ampdu_action = rt2800_ampdu_action, - .flush = rt2x00mac_flush, - .get_survey = rt2800_get_survey, - .get_ringparam = rt2x00mac_get_ringparam, - .tx_frames_pending = rt2x00mac_tx_frames_pending, -}; - -static const struct rt2800_ops rt2800soc_rt2800_ops = { - .register_read = rt2x00mmio_register_read, - .register_read_lock = rt2x00mmio_register_read, /* same for SoCs */ - .register_write = rt2x00mmio_register_write, - .register_write_lock = rt2x00mmio_register_write, /* same for SoCs */ - .register_multiread = rt2x00mmio_register_multiread, - .register_multiwrite = rt2x00mmio_register_multiwrite, - .regbusy_read = rt2x00mmio_regbusy_read, - .read_eeprom = rt2800soc_read_eeprom, - .hwcrypt_disabled = rt2800soc_hwcrypt_disabled, - .drv_write_firmware = rt2800soc_write_firmware, - .drv_init_registers = rt2800mmio_init_registers, - .drv_get_txwi = rt2800mmio_get_txwi, -}; - -static const struct rt2x00lib_ops rt2800soc_rt2x00_ops = { - .irq_handler = rt2800mmio_interrupt, - .txstatus_tasklet = rt2800mmio_txstatus_tasklet, - .pretbtt_tasklet = rt2800mmio_pretbtt_tasklet, - .tbtt_tasklet = rt2800mmio_tbtt_tasklet, - .rxdone_tasklet = rt2800mmio_rxdone_tasklet, - .autowake_tasklet = rt2800mmio_autowake_tasklet, - .probe_hw = rt2800_probe_hw, - .get_firmware_name = rt2800soc_get_firmware_name, - .check_firmware = rt2800soc_check_firmware, - .load_firmware = rt2800soc_load_firmware, - .initialize = rt2x00mmio_initialize, - .uninitialize = rt2x00mmio_uninitialize, - .get_entry_state = rt2800mmio_get_entry_state, - .clear_entry = rt2800mmio_clear_entry, - .set_device_state = rt2800soc_set_device_state, - .rfkill_poll = rt2800_rfkill_poll, - .link_stats = rt2800_link_stats, - .reset_tuner = rt2800_reset_tuner, - .link_tuner = rt2800_link_tuner, - .gain_calibration = rt2800_gain_calibration, - .vco_calibration = rt2800_vco_calibration, - .start_queue = rt2800mmio_start_queue, - .kick_queue = rt2800mmio_kick_queue, - .stop_queue = rt2800mmio_stop_queue, - .flush_queue = rt2x00mmio_flush_queue, - .write_tx_desc = rt2800mmio_write_tx_desc, - .write_tx_data = rt2800_write_tx_data, - .write_beacon = rt2800_write_beacon, - .clear_beacon = rt2800_clear_beacon, - .fill_rxdone = rt2800mmio_fill_rxdone, - .config_shared_key = rt2800_config_shared_key, - .config_pairwise_key = rt2800_config_pairwise_key, - .config_filter = rt2800_config_filter, - .config_intf = rt2800_config_intf, - .config_erp = rt2800_config_erp, - .config_ant = rt2800_config_ant, - .config = rt2800_config, - .sta_add = rt2800_sta_add, - .sta_remove = rt2800_sta_remove, -}; - -static const struct rt2x00_ops rt2800soc_ops = { - .name = KBUILD_MODNAME, - .drv_data_size = sizeof(struct rt2800_drv_data), - .max_ap_intf = 8, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .queue_init = rt2800mmio_queue_init, - .lib = &rt2800soc_rt2x00_ops, - .drv = &rt2800soc_rt2800_ops, - .hw = &rt2800soc_mac80211_ops, -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2800_rt2x00debug, -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -}; - -static int rt2800soc_probe(struct platform_device *pdev) -{ - return rt2x00soc_probe(pdev, &rt2800soc_ops); -} - -static struct platform_driver rt2800soc_driver = { - .driver = { - .name = "rt2800_wmac", - .mod_name = KBUILD_MODNAME, - }, - .probe = rt2800soc_probe, - .remove = rt2x00soc_remove, - .suspend = rt2x00soc_suspend, - .resume = rt2x00soc_resume, -}; - -module_platform_driver(rt2800soc_driver); - -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("Ralink WiSoC Wireless LAN driver."); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c deleted file mode 100644 index bf9afbf46c1b..000000000000 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ /dev/null @@ -1,1462 +0,0 @@ -/* - Copyright (C) 2010 Willow Garage - Copyright (C) 2009 - 2010 Ivo van Doorn - Copyright (C) 2009 Mattias Nissler - Copyright (C) 2009 Felix Fietkau - Copyright (C) 2009 Xose Vazquez Perez - Copyright (C) 2009 Axel Kollhofer - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2800usb - Abstract: rt2800usb device specific routines. - Supported chipsets: RT2800U. - */ - -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00usb.h" -#include "rt2800lib.h" -#include "rt2800.h" -#include "rt2800usb.h" - -/* - * Allow hardware encryption to be disabled. - */ -static bool modparam_nohwcrypt; -module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); -MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); - -static bool rt2800usb_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) -{ - return modparam_nohwcrypt; -} - -/* - * Queue handlers. - */ -static void rt2800usb_start_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); - rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - break; - case QID_BEACON: - rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); - rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); - break; - default: - break; - } -} - -static void rt2800usb_stop_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); - rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - break; - case QID_BEACON: - rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); - rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); - break; - default: - break; - } -} - -/* - * test if there is an entry in any TX queue for which DMA is done - * but the TX status has not been returned yet - */ -static bool rt2800usb_txstatus_pending(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - - tx_queue_for_each(rt2x00dev, queue) { - if (rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE) != - rt2x00queue_get_entry(queue, Q_INDEX_DONE)) - return true; - } - return false; -} - -static inline bool rt2800usb_entry_txstatus_timeout(struct queue_entry *entry) -{ - bool tout; - - if (!test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) - return false; - - tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(100)); - if (unlikely(tout)) - rt2x00_dbg(entry->queue->rt2x00dev, - "TX status timeout for entry %d in queue %d\n", - entry->entry_idx, entry->queue->qid); - return tout; - -} - -static bool rt2800usb_txstatus_timeout(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - struct queue_entry *entry; - - tx_queue_for_each(rt2x00dev, queue) { - entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - if (rt2800usb_entry_txstatus_timeout(entry)) - return true; - } - return false; -} - -#define TXSTATUS_READ_INTERVAL 1000000 - -static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, - int urb_status, u32 tx_status) -{ - bool valid; - - if (urb_status) { - rt2x00_warn(rt2x00dev, "TX status read failed %d\n", - urb_status); - - goto stop_reading; - } - - valid = rt2x00_get_field32(tx_status, TX_STA_FIFO_VALID); - if (valid) { - if (!kfifo_put(&rt2x00dev->txstatus_fifo, tx_status)) - rt2x00_warn(rt2x00dev, "TX status FIFO overrun\n"); - - queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); - - /* Reschedule urb to read TX status again instantly */ - return true; - } - - /* Check if there is any entry that timedout waiting on TX status */ - if (rt2800usb_txstatus_timeout(rt2x00dev)) - queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); - - if (rt2800usb_txstatus_pending(rt2x00dev)) { - /* Read register after 1 ms */ - hrtimer_start(&rt2x00dev->txstatus_timer, - ktime_set(0, TXSTATUS_READ_INTERVAL), - HRTIMER_MODE_REL); - return false; - } - -stop_reading: - clear_bit(TX_STATUS_READING, &rt2x00dev->flags); - /* - * There is small race window above, between txstatus pending check and - * clear_bit someone could do rt2x00usb_interrupt_txdone, so recheck - * here again if status reading is needed. - */ - if (rt2800usb_txstatus_pending(rt2x00dev) && - !test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) - return true; - else - return false; -} - -static void rt2800usb_async_read_tx_status(struct rt2x00_dev *rt2x00dev) -{ - - if (test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) - return; - - /* Read TX_STA_FIFO register after 2 ms */ - hrtimer_start(&rt2x00dev->txstatus_timer, - ktime_set(0, 2*TXSTATUS_READ_INTERVAL), - HRTIMER_MODE_REL); -} - -static void rt2800usb_tx_dma_done(struct queue_entry *entry) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - - rt2800usb_async_read_tx_status(rt2x00dev); -} - -static enum hrtimer_restart rt2800usb_tx_sta_fifo_timeout(struct hrtimer *timer) -{ - struct rt2x00_dev *rt2x00dev = - container_of(timer, struct rt2x00_dev, txstatus_timer); - - rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO, - rt2800usb_tx_sta_fifo_read_completed); - - return HRTIMER_NORESTART; -} - -/* - * Firmware functions - */ -static int rt2800usb_autorun_detect(struct rt2x00_dev *rt2x00dev) -{ - __le32 *reg; - u32 fw_mode; - int ret; - - reg = kmalloc(sizeof(*reg), GFP_KERNEL); - if (reg == NULL) - return -ENOMEM; - /* cannot use rt2x00usb_register_read here as it uses different - * mode (MULTI_READ vs. DEVICE_MODE) and does not pass the - * magic value USB_MODE_AUTORUN (0x11) to the device, thus the - * returned value would be invalid. - */ - ret = rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE, - USB_VENDOR_REQUEST_IN, 0, - USB_MODE_AUTORUN, reg, sizeof(*reg), - REGISTER_TIMEOUT_FIRMWARE); - fw_mode = le32_to_cpu(*reg); - kfree(reg); - if (ret < 0) - return ret; - - if ((fw_mode & 0x00000003) == 2) - return 1; - - return 0; -} - -static char *rt2800usb_get_firmware_name(struct rt2x00_dev *rt2x00dev) -{ - return FIRMWARE_RT2870; -} - -static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - int status; - u32 offset; - u32 length; - int retval; - - /* - * Check which section of the firmware we need. - */ - if (rt2x00_rt(rt2x00dev, RT2860) || - rt2x00_rt(rt2x00dev, RT2872) || - rt2x00_rt(rt2x00dev, RT3070)) { - offset = 0; - length = 4096; - } else { - offset = 4096; - length = 4096; - } - - /* - * Write firmware to device. - */ - retval = rt2800usb_autorun_detect(rt2x00dev); - if (retval < 0) - return retval; - if (retval) { - rt2x00_info(rt2x00dev, - "Firmware loading not required - NIC in AutoRun mode\n"); - __clear_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); - } else { - rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, - data + offset, length); - } - - rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); - rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); - - /* - * Send firmware request to device to load firmware, - * we need to specify a long timeout time. - */ - status = rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, - 0, USB_MODE_FIRMWARE, - REGISTER_TIMEOUT_FIRMWARE); - if (status < 0) { - rt2x00_err(rt2x00dev, "Failed to write Firmware to device\n"); - return status; - } - - msleep(10); - rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); - - return 0; -} - -/* - * Device state switch handlers. - */ -static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - /* - * Wait until BBP and RF are ready. - */ - if (rt2800_wait_csr_ready(rt2x00dev)) - return -EBUSY; - - rt2x00usb_register_read(rt2x00dev, PBF_SYS_CTRL, ®); - rt2x00usb_register_write(rt2x00dev, PBF_SYS_CTRL, reg & ~0x00002000); - - reg = 0; - rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); - rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); - rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - - rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, 0x00000000); - - rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, - USB_MODE_RESET, REGISTER_TIMEOUT); - - rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); - - return 0; -} - -static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev))) - return -EIO; - - rt2x00usb_register_read(rt2x00dev, USB_DMA_CFG, ®); - rt2x00_set_field32(®, USB_DMA_CFG_PHY_CLEAR, 0); - rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN, 0); - rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_TIMEOUT, 128); - /* - * Total room for RX frames in kilobytes, PBF might still exceed - * this limit so reduce the number to prevent errors. - */ - rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_LIMIT, - ((rt2x00dev->rx->limit * DATA_FRAME_SIZE) - / 1024) - 3); - rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_EN, 1); - rt2x00_set_field32(®, USB_DMA_CFG_TX_BULK_EN, 1); - rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, reg); - - return rt2800_enable_radio(rt2x00dev); -} - -static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - rt2800_disable_radio(rt2x00dev); -} - -static int rt2800usb_set_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - if (state == STATE_AWAKE) - rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 2); - else - rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0xff, 2); - - return 0; -} - -static int rt2800usb_set_device_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - int retval = 0; - - switch (state) { - case STATE_RADIO_ON: - /* - * Before the radio can be enabled, the device first has - * to be woken up. After that it needs a bit of time - * to be fully awake and then the radio can be enabled. - */ - rt2800usb_set_state(rt2x00dev, STATE_AWAKE); - msleep(1); - retval = rt2800usb_enable_radio(rt2x00dev); - break; - case STATE_RADIO_OFF: - /* - * After the radio has been disabled, the device should - * be put to sleep for powersaving. - */ - rt2800usb_disable_radio(rt2x00dev); - rt2800usb_set_state(rt2x00dev, STATE_SLEEP); - break; - case STATE_RADIO_IRQ_ON: - case STATE_RADIO_IRQ_OFF: - /* No support, but no error either */ - break; - case STATE_DEEP_SLEEP: - case STATE_SLEEP: - case STATE_STANDBY: - case STATE_AWAKE: - retval = rt2800usb_set_state(rt2x00dev, state); - break; - default: - retval = -ENOTSUPP; - break; - } - - if (unlikely(retval)) - rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", - state, retval); - - return retval; -} - -/* - * Watchdog handlers - */ -static void rt2800usb_watchdog(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u32 reg; - - rt2x00usb_register_read(rt2x00dev, TXRXQ_PCNT, ®); - if (rt2x00_get_field32(reg, TXRXQ_PCNT_TX0Q)) { - rt2x00_warn(rt2x00dev, "TX HW queue 0 timed out, invoke forced kick\n"); - - rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40012); - - for (i = 0; i < 10; i++) { - udelay(10); - if (!rt2x00_get_field32(reg, TXRXQ_PCNT_TX0Q)) - break; - } - - rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40006); - } - - rt2x00usb_register_read(rt2x00dev, TXRXQ_PCNT, ®); - if (rt2x00_get_field32(reg, TXRXQ_PCNT_TX1Q)) { - rt2x00_warn(rt2x00dev, "TX HW queue 1 timed out, invoke forced kick\n"); - - rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf4000a); - - for (i = 0; i < 10; i++) { - udelay(10); - if (!rt2x00_get_field32(reg, TXRXQ_PCNT_TX1Q)) - break; - } - - rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40006); - } - - rt2x00usb_watchdog(rt2x00dev); -} - -/* - * TX descriptor initialization - */ -static __le32 *rt2800usb_get_txwi(struct queue_entry *entry) -{ - if (entry->queue->qid == QID_BEACON) - return (__le32 *) (entry->skb->data); - else - return (__le32 *) (entry->skb->data + TXINFO_DESC_SIZE); -} - -static void rt2800usb_write_tx_desc(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - __le32 *txi = (__le32 *) entry->skb->data; - u32 word; - - /* - * Initialize TXINFO descriptor - */ - rt2x00_desc_read(txi, 0, &word); - - /* - * The size of TXINFO_W0_USB_DMA_TX_PKT_LEN is - * TXWI + 802.11 header + L2 pad + payload + pad, - * so need to decrease size of TXINFO. - */ - rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_TX_PKT_LEN, - roundup(entry->skb->len, 4) - TXINFO_DESC_SIZE); - rt2x00_set_field32(&word, TXINFO_W0_WIV, - !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); - rt2x00_set_field32(&word, TXINFO_W0_QSEL, 2); - rt2x00_set_field32(&word, TXINFO_W0_SW_USE_LAST_ROUND, 0); - rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_NEXT_VALID, 0); - rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_TX_BURST, - test_bit(ENTRY_TXD_BURST, &txdesc->flags)); - rt2x00_desc_write(txi, 0, word); - - /* - * Register descriptor details in skb frame descriptor. - */ - skbdesc->flags |= SKBDESC_DESC_IN_SKB; - skbdesc->desc = txi; - skbdesc->desc_len = TXINFO_DESC_SIZE + entry->queue->winfo_size; -} - -/* - * TX data initialization - */ -static int rt2800usb_get_tx_data_len(struct queue_entry *entry) -{ - /* - * pad(1~3 bytes) is needed after each 802.11 payload. - * USB end pad(4 bytes) is needed at each USB bulk out packet end. - * TX frame format is : - * | TXINFO | TXWI | 802.11 header | L2 pad | payload | pad | USB end pad | - * |<------------- tx_pkt_len ------------->| - */ - - return roundup(entry->skb->len, 4) + 4; -} - -/* - * TX control handlers - */ -static enum txdone_entry_desc_flags -rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg) -{ - __le32 *txwi; - u32 word; - int wcid, ack, pid; - int tx_wcid, tx_ack, tx_pid, is_agg; - - /* - * This frames has returned with an IO error, - * so the status report is not intended for this - * frame. - */ - if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) - return TXDONE_FAILURE; - - wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID); - ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED); - pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE); - is_agg = rt2x00_get_field32(reg, TX_STA_FIFO_TX_AGGRE); - - /* - * Validate if this TX status report is intended for - * this entry by comparing the WCID/ACK/PID fields. - */ - txwi = rt2800usb_get_txwi(entry); - - rt2x00_desc_read(txwi, 1, &word); - tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); - tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK); - tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID); - - if (wcid != tx_wcid || ack != tx_ack || (!is_agg && pid != tx_pid)) { - rt2x00_dbg(entry->queue->rt2x00dev, - "TX status report missed for queue %d entry %d\n", - entry->queue->qid, entry->entry_idx); - return TXDONE_UNKNOWN; - } - - return TXDONE_SUCCESS; -} - -static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - struct queue_entry *entry; - u32 reg; - u8 qid; - enum txdone_entry_desc_flags done_status; - - while (kfifo_get(&rt2x00dev->txstatus_fifo, ®)) { - /* - * TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus qid is - * guaranteed to be one of the TX QIDs . - */ - qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE); - queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); - - if (unlikely(rt2x00queue_empty(queue))) { - rt2x00_dbg(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", - qid); - break; - } - - entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - - if (unlikely(test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || - !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))) { - rt2x00_warn(rt2x00dev, "Data pending for entry %u in queue %u\n", - entry->entry_idx, qid); - break; - } - - done_status = rt2800usb_txdone_entry_check(entry, reg); - if (likely(done_status == TXDONE_SUCCESS)) - rt2800_txdone_entry(entry, reg, rt2800usb_get_txwi(entry)); - else - rt2x00lib_txdone_noinfo(entry, done_status); - } -} - -static void rt2800usb_txdone_nostatus(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - struct queue_entry *entry; - - /* - * Process any trailing TX status reports for IO failures, - * we loop until we find the first non-IO error entry. This - * can either be a frame which is free, is being uploaded, - * or has completed the upload but didn't have an entry - * in the TX_STAT_FIFO register yet. - */ - tx_queue_for_each(rt2x00dev, queue) { - while (!rt2x00queue_empty(queue)) { - entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - - if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || - !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) - break; - - if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) - rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE); - else if (rt2800usb_entry_txstatus_timeout(entry)) - rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN); - else - break; - } - } -} - -static void rt2800usb_work_txdone(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, txdone_work); - - while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo) || - rt2800usb_txstatus_timeout(rt2x00dev)) { - - rt2800usb_txdone(rt2x00dev); - - rt2800usb_txdone_nostatus(rt2x00dev); - - /* - * The hw may delay sending the packet after DMA complete - * if the medium is busy, thus the TX_STA_FIFO entry is - * also delayed -> use a timer to retrieve it. - */ - if (rt2800usb_txstatus_pending(rt2x00dev)) - rt2800usb_async_read_tx_status(rt2x00dev); - } -} - -/* - * RX control handlers - */ -static void rt2800usb_fill_rxdone(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - __le32 *rxi = (__le32 *)entry->skb->data; - __le32 *rxd; - u32 word; - int rx_pkt_len; - - /* - * Copy descriptor to the skbdesc->desc buffer, making it safe from - * moving of frame data in rt2x00usb. - */ - memcpy(skbdesc->desc, rxi, skbdesc->desc_len); - - /* - * RX frame format is : - * | RXINFO | RXWI | header | L2 pad | payload | pad | RXD | USB pad | - * |<------------ rx_pkt_len -------------->| - */ - rt2x00_desc_read(rxi, 0, &word); - rx_pkt_len = rt2x00_get_field32(word, RXINFO_W0_USB_DMA_RX_PKT_LEN); - - /* - * Remove the RXINFO structure from the sbk. - */ - skb_pull(entry->skb, RXINFO_DESC_SIZE); - - /* - * Check for rx_pkt_len validity. Return if invalid, leaving - * rxdesc->size zeroed out by the upper level. - */ - if (unlikely(rx_pkt_len == 0 || - rx_pkt_len > entry->queue->data_size)) { - rt2x00_err(entry->queue->rt2x00dev, - "Bad frame size %d, forcing to 0\n", rx_pkt_len); - return; - } - - rxd = (__le32 *)(entry->skb->data + rx_pkt_len); - - /* - * It is now safe to read the descriptor on all architectures. - */ - rt2x00_desc_read(rxd, 0, &word); - - if (rt2x00_get_field32(word, RXD_W0_CRC_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; - - rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W0_CIPHER_ERROR); - - if (rt2x00_get_field32(word, RXD_W0_DECRYPTED)) { - /* - * Hardware has stripped IV/EIV data from 802.11 frame during - * decryption. Unfortunately the descriptor doesn't contain - * any fields with the EIV/IV data either, so they can't - * be restored by rt2x00lib. - */ - rxdesc->flags |= RX_FLAG_IV_STRIPPED; - - /* - * The hardware has already checked the Michael Mic and has - * stripped it from the frame. Signal this to mac80211. - */ - rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; - - if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) - rxdesc->flags |= RX_FLAG_DECRYPTED; - else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) - rxdesc->flags |= RX_FLAG_MMIC_ERROR; - } - - if (rt2x00_get_field32(word, RXD_W0_MY_BSS)) - rxdesc->dev_flags |= RXDONE_MY_BSS; - - if (rt2x00_get_field32(word, RXD_W0_L2PAD)) - rxdesc->dev_flags |= RXDONE_L2PAD; - - /* - * Remove RXD descriptor from end of buffer. - */ - skb_trim(entry->skb, rx_pkt_len); - - /* - * Process the RXWI structure. - */ - rt2800_process_rxwi(entry, rxdesc); -} - -/* - * Device probe functions. - */ -static int rt2800usb_efuse_detect(struct rt2x00_dev *rt2x00dev) -{ - int retval; - - retval = rt2800usb_autorun_detect(rt2x00dev); - if (retval < 0) - return retval; - if (retval) - return 1; - return rt2800_efuse_detect(rt2x00dev); -} - -static int rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev) -{ - int retval; - - retval = rt2800usb_efuse_detect(rt2x00dev); - if (retval < 0) - return retval; - if (retval) - retval = rt2800_read_eeprom_efuse(rt2x00dev); - else - retval = rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, - EEPROM_SIZE); - - return retval; -} - -static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) -{ - int retval; - - retval = rt2800_probe_hw(rt2x00dev); - if (retval) - return retval; - - /* - * Set txstatus timer function. - */ - rt2x00dev->txstatus_timer.function = rt2800usb_tx_sta_fifo_timeout; - - /* - * Overwrite TX done handler - */ - INIT_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone); - - return 0; -} - -static const struct ieee80211_ops rt2800usb_mac80211_ops = { - .tx = rt2x00mac_tx, - .start = rt2x00mac_start, - .stop = rt2x00mac_stop, - .add_interface = rt2x00mac_add_interface, - .remove_interface = rt2x00mac_remove_interface, - .config = rt2x00mac_config, - .configure_filter = rt2x00mac_configure_filter, - .set_tim = rt2x00mac_set_tim, - .set_key = rt2x00mac_set_key, - .sw_scan_start = rt2x00mac_sw_scan_start, - .sw_scan_complete = rt2x00mac_sw_scan_complete, - .get_stats = rt2x00mac_get_stats, - .get_key_seq = rt2800_get_key_seq, - .set_rts_threshold = rt2800_set_rts_threshold, - .sta_add = rt2x00mac_sta_add, - .sta_remove = rt2x00mac_sta_remove, - .bss_info_changed = rt2x00mac_bss_info_changed, - .conf_tx = rt2800_conf_tx, - .get_tsf = rt2800_get_tsf, - .rfkill_poll = rt2x00mac_rfkill_poll, - .ampdu_action = rt2800_ampdu_action, - .flush = rt2x00mac_flush, - .get_survey = rt2800_get_survey, - .get_ringparam = rt2x00mac_get_ringparam, - .tx_frames_pending = rt2x00mac_tx_frames_pending, -}; - -static const struct rt2800_ops rt2800usb_rt2800_ops = { - .register_read = rt2x00usb_register_read, - .register_read_lock = rt2x00usb_register_read_lock, - .register_write = rt2x00usb_register_write, - .register_write_lock = rt2x00usb_register_write_lock, - .register_multiread = rt2x00usb_register_multiread, - .register_multiwrite = rt2x00usb_register_multiwrite, - .regbusy_read = rt2x00usb_regbusy_read, - .read_eeprom = rt2800usb_read_eeprom, - .hwcrypt_disabled = rt2800usb_hwcrypt_disabled, - .drv_write_firmware = rt2800usb_write_firmware, - .drv_init_registers = rt2800usb_init_registers, - .drv_get_txwi = rt2800usb_get_txwi, -}; - -static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { - .probe_hw = rt2800usb_probe_hw, - .get_firmware_name = rt2800usb_get_firmware_name, - .check_firmware = rt2800_check_firmware, - .load_firmware = rt2800_load_firmware, - .initialize = rt2x00usb_initialize, - .uninitialize = rt2x00usb_uninitialize, - .clear_entry = rt2x00usb_clear_entry, - .set_device_state = rt2800usb_set_device_state, - .rfkill_poll = rt2800_rfkill_poll, - .link_stats = rt2800_link_stats, - .reset_tuner = rt2800_reset_tuner, - .link_tuner = rt2800_link_tuner, - .gain_calibration = rt2800_gain_calibration, - .vco_calibration = rt2800_vco_calibration, - .watchdog = rt2800usb_watchdog, - .start_queue = rt2800usb_start_queue, - .kick_queue = rt2x00usb_kick_queue, - .stop_queue = rt2800usb_stop_queue, - .flush_queue = rt2x00usb_flush_queue, - .tx_dma_done = rt2800usb_tx_dma_done, - .write_tx_desc = rt2800usb_write_tx_desc, - .write_tx_data = rt2800_write_tx_data, - .write_beacon = rt2800_write_beacon, - .clear_beacon = rt2800_clear_beacon, - .get_tx_data_len = rt2800usb_get_tx_data_len, - .fill_rxdone = rt2800usb_fill_rxdone, - .config_shared_key = rt2800_config_shared_key, - .config_pairwise_key = rt2800_config_pairwise_key, - .config_filter = rt2800_config_filter, - .config_intf = rt2800_config_intf, - .config_erp = rt2800_config_erp, - .config_ant = rt2800_config_ant, - .config = rt2800_config, - .sta_add = rt2800_sta_add, - .sta_remove = rt2800_sta_remove, -}; - -static void rt2800usb_queue_init(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - unsigned short txwi_size, rxwi_size; - - rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); - - switch (queue->qid) { - case QID_RX: - queue->limit = 128; - queue->data_size = AGGREGATION_SIZE; - queue->desc_size = RXINFO_DESC_SIZE; - queue->winfo_size = rxwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_usb); - break; - - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - queue->limit = 16; - queue->data_size = AGGREGATION_SIZE; - queue->desc_size = TXINFO_DESC_SIZE; - queue->winfo_size = txwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_usb); - break; - - case QID_BEACON: - queue->limit = 8; - queue->data_size = MGMT_FRAME_SIZE; - queue->desc_size = TXINFO_DESC_SIZE; - queue->winfo_size = txwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_usb); - break; - - case QID_ATIM: - /* fallthrough */ - default: - BUG(); - break; - } -} - -static const struct rt2x00_ops rt2800usb_ops = { - .name = KBUILD_MODNAME, - .drv_data_size = sizeof(struct rt2800_drv_data), - .max_ap_intf = 8, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .queue_init = rt2800usb_queue_init, - .lib = &rt2800usb_rt2x00_ops, - .drv = &rt2800usb_rt2800_ops, - .hw = &rt2800usb_mac80211_ops, -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2800_rt2x00debug, -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -}; - -/* - * rt2800usb module information. - */ -static struct usb_device_id rt2800usb_device_table[] = { - /* Abocom */ - { USB_DEVICE(0x07b8, 0x2870) }, - { USB_DEVICE(0x07b8, 0x2770) }, - { USB_DEVICE(0x07b8, 0x3070) }, - { USB_DEVICE(0x07b8, 0x3071) }, - { USB_DEVICE(0x07b8, 0x3072) }, - { USB_DEVICE(0x1482, 0x3c09) }, - /* AirTies */ - { USB_DEVICE(0x1eda, 0x2012) }, - { USB_DEVICE(0x1eda, 0x2210) }, - { USB_DEVICE(0x1eda, 0x2310) }, - /* Allwin */ - { USB_DEVICE(0x8516, 0x2070) }, - { USB_DEVICE(0x8516, 0x2770) }, - { USB_DEVICE(0x8516, 0x2870) }, - { USB_DEVICE(0x8516, 0x3070) }, - { USB_DEVICE(0x8516, 0x3071) }, - { USB_DEVICE(0x8516, 0x3072) }, - /* Alpha Networks */ - { USB_DEVICE(0x14b2, 0x3c06) }, - { USB_DEVICE(0x14b2, 0x3c07) }, - { USB_DEVICE(0x14b2, 0x3c09) }, - { USB_DEVICE(0x14b2, 0x3c12) }, - { USB_DEVICE(0x14b2, 0x3c23) }, - { USB_DEVICE(0x14b2, 0x3c25) }, - { USB_DEVICE(0x14b2, 0x3c27) }, - { USB_DEVICE(0x14b2, 0x3c28) }, - { USB_DEVICE(0x14b2, 0x3c2c) }, - /* Amit */ - { USB_DEVICE(0x15c5, 0x0008) }, - /* Askey */ - { USB_DEVICE(0x1690, 0x0740) }, - /* ASUS */ - { USB_DEVICE(0x0b05, 0x1731) }, - { USB_DEVICE(0x0b05, 0x1732) }, - { USB_DEVICE(0x0b05, 0x1742) }, - { USB_DEVICE(0x0b05, 0x1784) }, - { USB_DEVICE(0x1761, 0x0b05) }, - /* AzureWave */ - { USB_DEVICE(0x13d3, 0x3247) }, - { USB_DEVICE(0x13d3, 0x3273) }, - { USB_DEVICE(0x13d3, 0x3305) }, - { USB_DEVICE(0x13d3, 0x3307) }, - { USB_DEVICE(0x13d3, 0x3321) }, - /* Belkin */ - { USB_DEVICE(0x050d, 0x8053) }, - { USB_DEVICE(0x050d, 0x805c) }, - { USB_DEVICE(0x050d, 0x815c) }, - { USB_DEVICE(0x050d, 0x825a) }, - { USB_DEVICE(0x050d, 0x825b) }, - { USB_DEVICE(0x050d, 0x935a) }, - { USB_DEVICE(0x050d, 0x935b) }, - /* Buffalo */ - { USB_DEVICE(0x0411, 0x00e8) }, - { USB_DEVICE(0x0411, 0x0158) }, - { USB_DEVICE(0x0411, 0x015d) }, - { USB_DEVICE(0x0411, 0x016f) }, - { USB_DEVICE(0x0411, 0x01a2) }, - { USB_DEVICE(0x0411, 0x01ee) }, - { USB_DEVICE(0x0411, 0x01a8) }, - /* Corega */ - { USB_DEVICE(0x07aa, 0x002f) }, - { USB_DEVICE(0x07aa, 0x003c) }, - { USB_DEVICE(0x07aa, 0x003f) }, - { USB_DEVICE(0x18c5, 0x0012) }, - /* D-Link */ - { USB_DEVICE(0x07d1, 0x3c09) }, - { USB_DEVICE(0x07d1, 0x3c0a) }, - { USB_DEVICE(0x07d1, 0x3c0d) }, - { USB_DEVICE(0x07d1, 0x3c0e) }, - { USB_DEVICE(0x07d1, 0x3c0f) }, - { USB_DEVICE(0x07d1, 0x3c11) }, - { USB_DEVICE(0x07d1, 0x3c13) }, - { USB_DEVICE(0x07d1, 0x3c15) }, - { USB_DEVICE(0x07d1, 0x3c16) }, - { USB_DEVICE(0x07d1, 0x3c17) }, - { USB_DEVICE(0x2001, 0x3317) }, - { USB_DEVICE(0x2001, 0x3c1b) }, - { USB_DEVICE(0x2001, 0x3c25) }, - /* Draytek */ - { USB_DEVICE(0x07fa, 0x7712) }, - /* DVICO */ - { USB_DEVICE(0x0fe9, 0xb307) }, - /* Edimax */ - { USB_DEVICE(0x7392, 0x4085) }, - { USB_DEVICE(0x7392, 0x7711) }, - { USB_DEVICE(0x7392, 0x7717) }, - { USB_DEVICE(0x7392, 0x7718) }, - { USB_DEVICE(0x7392, 0x7722) }, - /* Encore */ - { USB_DEVICE(0x203d, 0x1480) }, - { USB_DEVICE(0x203d, 0x14a9) }, - /* EnGenius */ - { USB_DEVICE(0x1740, 0x9701) }, - { USB_DEVICE(0x1740, 0x9702) }, - { USB_DEVICE(0x1740, 0x9703) }, - { USB_DEVICE(0x1740, 0x9705) }, - { USB_DEVICE(0x1740, 0x9706) }, - { USB_DEVICE(0x1740, 0x9707) }, - { USB_DEVICE(0x1740, 0x9708) }, - { USB_DEVICE(0x1740, 0x9709) }, - /* Gemtek */ - { USB_DEVICE(0x15a9, 0x0012) }, - /* Gigabyte */ - { USB_DEVICE(0x1044, 0x800b) }, - { USB_DEVICE(0x1044, 0x800d) }, - /* Hawking */ - { USB_DEVICE(0x0e66, 0x0001) }, - { USB_DEVICE(0x0e66, 0x0003) }, - { USB_DEVICE(0x0e66, 0x0009) }, - { USB_DEVICE(0x0e66, 0x000b) }, - { USB_DEVICE(0x0e66, 0x0013) }, - { USB_DEVICE(0x0e66, 0x0017) }, - { USB_DEVICE(0x0e66, 0x0018) }, - /* I-O DATA */ - { USB_DEVICE(0x04bb, 0x0945) }, - { USB_DEVICE(0x04bb, 0x0947) }, - { USB_DEVICE(0x04bb, 0x0948) }, - /* Linksys */ - { USB_DEVICE(0x13b1, 0x0031) }, - { USB_DEVICE(0x1737, 0x0070) }, - { USB_DEVICE(0x1737, 0x0071) }, - { USB_DEVICE(0x1737, 0x0077) }, - { USB_DEVICE(0x1737, 0x0078) }, - /* Logitec */ - { USB_DEVICE(0x0789, 0x0162) }, - { USB_DEVICE(0x0789, 0x0163) }, - { USB_DEVICE(0x0789, 0x0164) }, - { USB_DEVICE(0x0789, 0x0166) }, - /* Motorola */ - { USB_DEVICE(0x100d, 0x9031) }, - /* MSI */ - { USB_DEVICE(0x0db0, 0x3820) }, - { USB_DEVICE(0x0db0, 0x3821) }, - { USB_DEVICE(0x0db0, 0x3822) }, - { USB_DEVICE(0x0db0, 0x3870) }, - { USB_DEVICE(0x0db0, 0x3871) }, - { USB_DEVICE(0x0db0, 0x6899) }, - { USB_DEVICE(0x0db0, 0x821a) }, - { USB_DEVICE(0x0db0, 0x822a) }, - { USB_DEVICE(0x0db0, 0x822b) }, - { USB_DEVICE(0x0db0, 0x822c) }, - { USB_DEVICE(0x0db0, 0x870a) }, - { USB_DEVICE(0x0db0, 0x871a) }, - { USB_DEVICE(0x0db0, 0x871b) }, - { USB_DEVICE(0x0db0, 0x871c) }, - { USB_DEVICE(0x0db0, 0x899a) }, - /* Ovislink */ - { USB_DEVICE(0x1b75, 0x3070) }, - { USB_DEVICE(0x1b75, 0x3071) }, - { USB_DEVICE(0x1b75, 0x3072) }, - { USB_DEVICE(0x1b75, 0xa200) }, - /* Para */ - { USB_DEVICE(0x20b8, 0x8888) }, - /* Pegatron */ - { USB_DEVICE(0x1d4d, 0x0002) }, - { USB_DEVICE(0x1d4d, 0x000c) }, - { USB_DEVICE(0x1d4d, 0x000e) }, - { USB_DEVICE(0x1d4d, 0x0011) }, - /* Philips */ - { USB_DEVICE(0x0471, 0x200f) }, - /* Planex */ - { USB_DEVICE(0x2019, 0x5201) }, - { USB_DEVICE(0x2019, 0xab25) }, - { USB_DEVICE(0x2019, 0xed06) }, - /* Quanta */ - { USB_DEVICE(0x1a32, 0x0304) }, - /* Ralink */ - { USB_DEVICE(0x148f, 0x2070) }, - { USB_DEVICE(0x148f, 0x2770) }, - { USB_DEVICE(0x148f, 0x2870) }, - { USB_DEVICE(0x148f, 0x3070) }, - { USB_DEVICE(0x148f, 0x3071) }, - { USB_DEVICE(0x148f, 0x3072) }, - /* Samsung */ - { USB_DEVICE(0x04e8, 0x2018) }, - /* Siemens */ - { USB_DEVICE(0x129b, 0x1828) }, - /* Sitecom */ - { USB_DEVICE(0x0df6, 0x0017) }, - { USB_DEVICE(0x0df6, 0x002b) }, - { USB_DEVICE(0x0df6, 0x002c) }, - { USB_DEVICE(0x0df6, 0x002d) }, - { USB_DEVICE(0x0df6, 0x0039) }, - { USB_DEVICE(0x0df6, 0x003b) }, - { USB_DEVICE(0x0df6, 0x003d) }, - { USB_DEVICE(0x0df6, 0x003e) }, - { USB_DEVICE(0x0df6, 0x003f) }, - { USB_DEVICE(0x0df6, 0x0040) }, - { USB_DEVICE(0x0df6, 0x0042) }, - { USB_DEVICE(0x0df6, 0x0047) }, - { USB_DEVICE(0x0df6, 0x0048) }, - { USB_DEVICE(0x0df6, 0x0051) }, - { USB_DEVICE(0x0df6, 0x005f) }, - { USB_DEVICE(0x0df6, 0x0060) }, - /* SMC */ - { USB_DEVICE(0x083a, 0x6618) }, - { USB_DEVICE(0x083a, 0x7511) }, - { USB_DEVICE(0x083a, 0x7512) }, - { USB_DEVICE(0x083a, 0x7522) }, - { USB_DEVICE(0x083a, 0x8522) }, - { USB_DEVICE(0x083a, 0xa618) }, - { USB_DEVICE(0x083a, 0xa701) }, - { USB_DEVICE(0x083a, 0xa702) }, - { USB_DEVICE(0x083a, 0xa703) }, - { USB_DEVICE(0x083a, 0xb522) }, - /* Sparklan */ - { USB_DEVICE(0x15a9, 0x0006) }, - /* Sweex */ - { USB_DEVICE(0x177f, 0x0153) }, - { USB_DEVICE(0x177f, 0x0164) }, - { USB_DEVICE(0x177f, 0x0302) }, - { USB_DEVICE(0x177f, 0x0313) }, - { USB_DEVICE(0x177f, 0x0323) }, - { USB_DEVICE(0x177f, 0x0324) }, - /* U-Media */ - { USB_DEVICE(0x157e, 0x300e) }, - { USB_DEVICE(0x157e, 0x3013) }, - /* ZCOM */ - { USB_DEVICE(0x0cde, 0x0022) }, - { USB_DEVICE(0x0cde, 0x0025) }, - /* Zinwell */ - { USB_DEVICE(0x5a57, 0x0280) }, - { USB_DEVICE(0x5a57, 0x0282) }, - { USB_DEVICE(0x5a57, 0x0283) }, - { USB_DEVICE(0x5a57, 0x5257) }, - /* Zyxel */ - { USB_DEVICE(0x0586, 0x3416) }, - { USB_DEVICE(0x0586, 0x3418) }, - { USB_DEVICE(0x0586, 0x341a) }, - { USB_DEVICE(0x0586, 0x341e) }, - { USB_DEVICE(0x0586, 0x343e) }, -#ifdef CONFIG_RT2800USB_RT33XX - /* Belkin */ - { USB_DEVICE(0x050d, 0x945b) }, - /* D-Link */ - { USB_DEVICE(0x2001, 0x3c17) }, - /* Panasonic */ - { USB_DEVICE(0x083a, 0xb511) }, - /* Philips */ - { USB_DEVICE(0x0471, 0x20dd) }, - /* Ralink */ - { USB_DEVICE(0x148f, 0x3370) }, - { USB_DEVICE(0x148f, 0x8070) }, - /* Sitecom */ - { USB_DEVICE(0x0df6, 0x0050) }, - /* Sweex */ - { USB_DEVICE(0x177f, 0x0163) }, - { USB_DEVICE(0x177f, 0x0165) }, -#endif -#ifdef CONFIG_RT2800USB_RT35XX - /* Allwin */ - { USB_DEVICE(0x8516, 0x3572) }, - /* Askey */ - { USB_DEVICE(0x1690, 0x0744) }, - { USB_DEVICE(0x1690, 0x0761) }, - { USB_DEVICE(0x1690, 0x0764) }, - /* ASUS */ - { USB_DEVICE(0x0b05, 0x179d) }, - /* Cisco */ - { USB_DEVICE(0x167b, 0x4001) }, - /* EnGenius */ - { USB_DEVICE(0x1740, 0x9801) }, - /* I-O DATA */ - { USB_DEVICE(0x04bb, 0x0944) }, - /* Linksys */ - { USB_DEVICE(0x13b1, 0x002f) }, - { USB_DEVICE(0x1737, 0x0079) }, - /* Logitec */ - { USB_DEVICE(0x0789, 0x0170) }, - /* Ralink */ - { USB_DEVICE(0x148f, 0x3572) }, - /* Sitecom */ - { USB_DEVICE(0x0df6, 0x0041) }, - { USB_DEVICE(0x0df6, 0x0062) }, - { USB_DEVICE(0x0df6, 0x0065) }, - { USB_DEVICE(0x0df6, 0x0066) }, - { USB_DEVICE(0x0df6, 0x0068) }, - /* Toshiba */ - { USB_DEVICE(0x0930, 0x0a07) }, - /* Zinwell */ - { USB_DEVICE(0x5a57, 0x0284) }, -#endif -#ifdef CONFIG_RT2800USB_RT3573 - /* AirLive */ - { USB_DEVICE(0x1b75, 0x7733) }, - /* ASUS */ - { USB_DEVICE(0x0b05, 0x17bc) }, - { USB_DEVICE(0x0b05, 0x17ad) }, - /* Belkin */ - { USB_DEVICE(0x050d, 0x1103) }, - /* Cameo */ - { USB_DEVICE(0x148f, 0xf301) }, - /* D-Link */ - { USB_DEVICE(0x2001, 0x3c1f) }, - /* Edimax */ - { USB_DEVICE(0x7392, 0x7733) }, - /* Hawking */ - { USB_DEVICE(0x0e66, 0x0020) }, - { USB_DEVICE(0x0e66, 0x0021) }, - /* I-O DATA */ - { USB_DEVICE(0x04bb, 0x094e) }, - /* Linksys */ - { USB_DEVICE(0x13b1, 0x003b) }, - /* Logitec */ - { USB_DEVICE(0x0789, 0x016b) }, - /* NETGEAR */ - { USB_DEVICE(0x0846, 0x9012) }, - { USB_DEVICE(0x0846, 0x9013) }, - { USB_DEVICE(0x0846, 0x9019) }, - /* Planex */ - { USB_DEVICE(0x2019, 0xed19) }, - /* Ralink */ - { USB_DEVICE(0x148f, 0x3573) }, - /* Sitecom */ - { USB_DEVICE(0x0df6, 0x0067) }, - { USB_DEVICE(0x0df6, 0x006a) }, - { USB_DEVICE(0x0df6, 0x006e) }, - /* ZyXEL */ - { USB_DEVICE(0x0586, 0x3421) }, -#endif -#ifdef CONFIG_RT2800USB_RT53XX - /* Arcadyan */ - { USB_DEVICE(0x043e, 0x7a12) }, - { USB_DEVICE(0x043e, 0x7a32) }, - /* ASUS */ - { USB_DEVICE(0x0b05, 0x17e8) }, - /* Azurewave */ - { USB_DEVICE(0x13d3, 0x3329) }, - { USB_DEVICE(0x13d3, 0x3365) }, - /* D-Link */ - { USB_DEVICE(0x2001, 0x3c15) }, - { USB_DEVICE(0x2001, 0x3c19) }, - { USB_DEVICE(0x2001, 0x3c1c) }, - { USB_DEVICE(0x2001, 0x3c1d) }, - { USB_DEVICE(0x2001, 0x3c1e) }, - { USB_DEVICE(0x2001, 0x3c20) }, - { USB_DEVICE(0x2001, 0x3c22) }, - { USB_DEVICE(0x2001, 0x3c23) }, - /* LG innotek */ - { USB_DEVICE(0x043e, 0x7a22) }, - { USB_DEVICE(0x043e, 0x7a42) }, - /* Panasonic */ - { USB_DEVICE(0x04da, 0x1801) }, - { USB_DEVICE(0x04da, 0x1800) }, - { USB_DEVICE(0x04da, 0x23f6) }, - /* Philips */ - { USB_DEVICE(0x0471, 0x2104) }, - { USB_DEVICE(0x0471, 0x2126) }, - { USB_DEVICE(0x0471, 0x2180) }, - { USB_DEVICE(0x0471, 0x2181) }, - { USB_DEVICE(0x0471, 0x2182) }, - /* Ralink */ - { USB_DEVICE(0x148f, 0x5370) }, - { USB_DEVICE(0x148f, 0x5372) }, -#endif -#ifdef CONFIG_RT2800USB_RT55XX - /* Arcadyan */ - { USB_DEVICE(0x043e, 0x7a32) }, - /* AVM GmbH */ - { USB_DEVICE(0x057c, 0x8501) }, - /* Buffalo */ - { USB_DEVICE(0x0411, 0x0241) }, - { USB_DEVICE(0x0411, 0x0253) }, - /* D-Link */ - { USB_DEVICE(0x2001, 0x3c1a) }, - { USB_DEVICE(0x2001, 0x3c21) }, - /* Proware */ - { USB_DEVICE(0x043e, 0x7a13) }, - /* Ralink */ - { USB_DEVICE(0x148f, 0x5572) }, - /* TRENDnet */ - { USB_DEVICE(0x20f4, 0x724a) }, -#endif -#ifdef CONFIG_RT2800USB_UNKNOWN - /* - * Unclear what kind of devices these are (they aren't supported by the - * vendor linux driver). - */ - /* Abocom */ - { USB_DEVICE(0x07b8, 0x3073) }, - { USB_DEVICE(0x07b8, 0x3074) }, - /* Alpha Networks */ - { USB_DEVICE(0x14b2, 0x3c08) }, - { USB_DEVICE(0x14b2, 0x3c11) }, - /* Amigo */ - { USB_DEVICE(0x0e0b, 0x9031) }, - { USB_DEVICE(0x0e0b, 0x9041) }, - /* ASUS */ - { USB_DEVICE(0x0b05, 0x166a) }, - { USB_DEVICE(0x0b05, 0x1760) }, - { USB_DEVICE(0x0b05, 0x1761) }, - { USB_DEVICE(0x0b05, 0x1790) }, - { USB_DEVICE(0x0b05, 0x17a7) }, - /* AzureWave */ - { USB_DEVICE(0x13d3, 0x3262) }, - { USB_DEVICE(0x13d3, 0x3284) }, - { USB_DEVICE(0x13d3, 0x3322) }, - { USB_DEVICE(0x13d3, 0x3340) }, - { USB_DEVICE(0x13d3, 0x3399) }, - { USB_DEVICE(0x13d3, 0x3400) }, - { USB_DEVICE(0x13d3, 0x3401) }, - /* Belkin */ - { USB_DEVICE(0x050d, 0x1003) }, - /* Buffalo */ - { USB_DEVICE(0x0411, 0x012e) }, - { USB_DEVICE(0x0411, 0x0148) }, - { USB_DEVICE(0x0411, 0x0150) }, - /* Corega */ - { USB_DEVICE(0x07aa, 0x0041) }, - { USB_DEVICE(0x07aa, 0x0042) }, - { USB_DEVICE(0x18c5, 0x0008) }, - /* D-Link */ - { USB_DEVICE(0x07d1, 0x3c0b) }, - /* Encore */ - { USB_DEVICE(0x203d, 0x14a1) }, - /* EnGenius */ - { USB_DEVICE(0x1740, 0x0600) }, - { USB_DEVICE(0x1740, 0x0602) }, - /* Gemtek */ - { USB_DEVICE(0x15a9, 0x0010) }, - /* Gigabyte */ - { USB_DEVICE(0x1044, 0x800c) }, - /* Hercules */ - { USB_DEVICE(0x06f8, 0xe036) }, - /* Huawei */ - { USB_DEVICE(0x148f, 0xf101) }, - /* I-O DATA */ - { USB_DEVICE(0x04bb, 0x094b) }, - /* LevelOne */ - { USB_DEVICE(0x1740, 0x0605) }, - { USB_DEVICE(0x1740, 0x0615) }, - /* Logitec */ - { USB_DEVICE(0x0789, 0x0168) }, - { USB_DEVICE(0x0789, 0x0169) }, - /* Motorola */ - { USB_DEVICE(0x100d, 0x9032) }, - /* Pegatron */ - { USB_DEVICE(0x05a6, 0x0101) }, - { USB_DEVICE(0x1d4d, 0x0010) }, - /* Planex */ - { USB_DEVICE(0x2019, 0xab24) }, - { USB_DEVICE(0x2019, 0xab29) }, - /* Qcom */ - { USB_DEVICE(0x18e8, 0x6259) }, - /* RadioShack */ - { USB_DEVICE(0x08b9, 0x1197) }, - /* Sitecom */ - { USB_DEVICE(0x0df6, 0x003c) }, - { USB_DEVICE(0x0df6, 0x004a) }, - { USB_DEVICE(0x0df6, 0x004d) }, - { USB_DEVICE(0x0df6, 0x0053) }, - { USB_DEVICE(0x0df6, 0x0069) }, - { USB_DEVICE(0x0df6, 0x006f) }, - { USB_DEVICE(0x0df6, 0x0078) }, - /* SMC */ - { USB_DEVICE(0x083a, 0xa512) }, - { USB_DEVICE(0x083a, 0xc522) }, - { USB_DEVICE(0x083a, 0xd522) }, - { USB_DEVICE(0x083a, 0xf511) }, - /* Sweex */ - { USB_DEVICE(0x177f, 0x0254) }, - /* TP-LINK */ - { USB_DEVICE(0xf201, 0x5370) }, -#endif - { 0, } -}; - -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("Ralink RT2800 USB Wireless LAN driver."); -MODULE_SUPPORTED_DEVICE("Ralink RT2870 USB chipset based cards"); -MODULE_DEVICE_TABLE(usb, rt2800usb_device_table); -MODULE_FIRMWARE(FIRMWARE_RT2870); -MODULE_LICENSE("GPL"); - -static int rt2800usb_probe(struct usb_interface *usb_intf, - const struct usb_device_id *id) -{ - return rt2x00usb_probe(usb_intf, &rt2800usb_ops); -} - -static struct usb_driver rt2800usb_driver = { - .name = KBUILD_MODNAME, - .id_table = rt2800usb_device_table, - .probe = rt2800usb_probe, - .disconnect = rt2x00usb_disconnect, - .suspend = rt2x00usb_suspend, - .resume = rt2x00usb_resume, - .reset_resume = rt2x00usb_resume, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(rt2800usb_driver); diff --git a/drivers/net/wireless/rt2x00/rt2800usb.h b/drivers/net/wireless/rt2x00/rt2800usb.h deleted file mode 100644 index ea7cac095997..000000000000 --- a/drivers/net/wireless/rt2x00/rt2800usb.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - Copyright (C) 2009 Ivo van Doorn - Copyright (C) 2009 Mattias Nissler - Copyright (C) 2009 Felix Fietkau - Copyright (C) 2009 Xose Vazquez Perez - Copyright (C) 2009 Axel Kollhofer - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2800usb - Abstract: Data structures and registers for the rt2800usb module. - Supported chipsets: RT2800U. - */ - -#ifndef RT2800USB_H -#define RT2800USB_H - -/* - * 8051 firmware image. - */ -#define FIRMWARE_RT2870 "rt2870.bin" -#define FIRMWARE_IMAGE_BASE 0x3000 - -/* - * DMA descriptor defines. - */ -#define TXINFO_DESC_SIZE (1 * sizeof(__le32)) -#define RXINFO_DESC_SIZE (1 * sizeof(__le32)) - -/* - * TX Info structure - */ - -/* - * Word0 - * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI - * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. - * 0:MGMT, 1:HCCA 2:EDCA - * USB_DMA_NEXT_VALID: Used ONLY in USB bulk Aggregation, NextValid - * DMA_TX_BURST: used ONLY in USB bulk Aggregation. - * Force USB DMA transmit frame from current selected endpoint - */ -#define TXINFO_W0_USB_DMA_TX_PKT_LEN FIELD32(0x0000ffff) -#define TXINFO_W0_WIV FIELD32(0x01000000) -#define TXINFO_W0_QSEL FIELD32(0x06000000) -#define TXINFO_W0_SW_USE_LAST_ROUND FIELD32(0x08000000) -#define TXINFO_W0_USB_DMA_NEXT_VALID FIELD32(0x40000000) -#define TXINFO_W0_USB_DMA_TX_BURST FIELD32(0x80000000) - -/* - * RX Info structure - */ - -/* - * Word 0 - */ - -#define RXINFO_W0_USB_DMA_RX_PKT_LEN FIELD32(0x0000ffff) - -/* - * RX descriptor format for RX Ring. - */ - -/* - * Word0 - * UNICAST_TO_ME: This RX frame is unicast to me. - * MULTICAST: This is a multicast frame. - * BROADCAST: This is a broadcast frame. - * MY_BSS: this frame belongs to the same BSSID. - * CRC_ERROR: CRC error. - * CIPHER_ERROR: 0: decryption okay, 1:ICV error, 2:MIC error, 3:KEY not valid. - * AMSDU: rx with 802.3 header, not 802.11 header. - */ - -#define RXD_W0_BA FIELD32(0x00000001) -#define RXD_W0_DATA FIELD32(0x00000002) -#define RXD_W0_NULLDATA FIELD32(0x00000004) -#define RXD_W0_FRAG FIELD32(0x00000008) -#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000010) -#define RXD_W0_MULTICAST FIELD32(0x00000020) -#define RXD_W0_BROADCAST FIELD32(0x00000040) -#define RXD_W0_MY_BSS FIELD32(0x00000080) -#define RXD_W0_CRC_ERROR FIELD32(0x00000100) -#define RXD_W0_CIPHER_ERROR FIELD32(0x00000600) -#define RXD_W0_AMSDU FIELD32(0x00000800) -#define RXD_W0_HTC FIELD32(0x00001000) -#define RXD_W0_RSSI FIELD32(0x00002000) -#define RXD_W0_L2PAD FIELD32(0x00004000) -#define RXD_W0_AMPDU FIELD32(0x00008000) -#define RXD_W0_DECRYPTED FIELD32(0x00010000) -#define RXD_W0_PLCP_RSSI FIELD32(0x00020000) -#define RXD_W0_CIPHER_ALG FIELD32(0x00040000) -#define RXD_W0_LAST_AMSDU FIELD32(0x00080000) -#define RXD_W0_PLCP_SIGNAL FIELD32(0xfff00000) - -#endif /* RT2800USB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h deleted file mode 100644 index 3282ddb766f4..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ /dev/null @@ -1,1478 +0,0 @@ -/* - Copyright (C) 2010 Willow Garage - Copyright (C) 2004 - 2010 Ivo van Doorn - Copyright (C) 2004 - 2009 Gertjan van Wingerde - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00 - Abstract: rt2x00 global information. - */ - -#ifndef RT2X00_H -#define RT2X00_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "rt2x00debug.h" -#include "rt2x00dump.h" -#include "rt2x00leds.h" -#include "rt2x00reg.h" -#include "rt2x00queue.h" - -/* - * Module information. - */ -#define DRV_VERSION "2.3.0" -#define DRV_PROJECT "http://rt2x00.serialmonkey.com" - -/* Debug definitions. - * Debug output has to be enabled during compile time. - */ -#ifdef CONFIG_RT2X00_DEBUG -#define DEBUG -#endif /* CONFIG_RT2X00_DEBUG */ - -/* Utility printing macros - * rt2x00_probe_err is for messages when rt2x00_dev is uninitialized - */ -#define rt2x00_probe_err(fmt, ...) \ - printk(KERN_ERR KBUILD_MODNAME ": %s: Error - " fmt, \ - __func__, ##__VA_ARGS__) -#define rt2x00_err(dev, fmt, ...) \ - wiphy_err((dev)->hw->wiphy, "%s: Error - " fmt, \ - __func__, ##__VA_ARGS__) -#define rt2x00_warn(dev, fmt, ...) \ - wiphy_warn((dev)->hw->wiphy, "%s: Warning - " fmt, \ - __func__, ##__VA_ARGS__) -#define rt2x00_info(dev, fmt, ...) \ - wiphy_info((dev)->hw->wiphy, "%s: Info - " fmt, \ - __func__, ##__VA_ARGS__) - -/* Various debug levels */ -#define rt2x00_dbg(dev, fmt, ...) \ - wiphy_dbg((dev)->hw->wiphy, "%s: Debug - " fmt, \ - __func__, ##__VA_ARGS__) -#define rt2x00_eeprom_dbg(dev, fmt, ...) \ - wiphy_dbg((dev)->hw->wiphy, "%s: EEPROM recovery - " fmt, \ - __func__, ##__VA_ARGS__) - -/* - * Duration calculations - * The rate variable passed is: 100kbs. - * To convert from bytes to bits we multiply size with 8, - * then the size is multiplied with 10 to make the - * real rate -> rate argument correction. - */ -#define GET_DURATION(__size, __rate) (((__size) * 8 * 10) / (__rate)) -#define GET_DURATION_RES(__size, __rate)(((__size) * 8 * 10) % (__rate)) - -/* - * Determine the number of L2 padding bytes required between the header and - * the payload. - */ -#define L2PAD_SIZE(__hdrlen) (-(__hdrlen) & 3) - -/* - * Determine the alignment requirement, - * to make sure the 802.11 payload is padded to a 4-byte boundrary - * we must determine the address of the payload and calculate the - * amount of bytes needed to move the data. - */ -#define ALIGN_SIZE(__skb, __header) \ - ( ((unsigned long)((__skb)->data + (__header))) & 3 ) - -/* - * Constants for extra TX headroom for alignment purposes. - */ -#define RT2X00_ALIGN_SIZE 4 /* Only whole frame needs alignment */ -#define RT2X00_L2PAD_SIZE 8 /* Both header & payload need alignment */ - -/* - * Standard timing and size defines. - * These values should follow the ieee80211 specifications. - */ -#define ACK_SIZE 14 -#define IEEE80211_HEADER 24 -#define PLCP 48 -#define BEACON 100 -#define PREAMBLE 144 -#define SHORT_PREAMBLE 72 -#define SLOT_TIME 20 -#define SHORT_SLOT_TIME 9 -#define SIFS 10 -#define PIFS ( SIFS + SLOT_TIME ) -#define SHORT_PIFS ( SIFS + SHORT_SLOT_TIME ) -#define DIFS ( PIFS + SLOT_TIME ) -#define SHORT_DIFS ( SHORT_PIFS + SHORT_SLOT_TIME ) -#define EIFS ( SIFS + DIFS + \ - GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) -#define SHORT_EIFS ( SIFS + SHORT_DIFS + \ - GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) - -enum rt2x00_chip_intf { - RT2X00_CHIP_INTF_PCI, - RT2X00_CHIP_INTF_PCIE, - RT2X00_CHIP_INTF_USB, - RT2X00_CHIP_INTF_SOC, -}; - -/* - * Chipset identification - * The chipset on the device is composed of a RT and RF chip. - * The chipset combination is important for determining device capabilities. - */ -struct rt2x00_chip { - u16 rt; -#define RT2460 0x2460 -#define RT2560 0x2560 -#define RT2570 0x2570 -#define RT2661 0x2661 -#define RT2573 0x2573 -#define RT2860 0x2860 /* 2.4GHz */ -#define RT2872 0x2872 /* WSOC */ -#define RT2883 0x2883 /* WSOC */ -#define RT3070 0x3070 -#define RT3071 0x3071 -#define RT3090 0x3090 /* 2.4GHz PCIe */ -#define RT3290 0x3290 -#define RT3352 0x3352 /* WSOC */ -#define RT3390 0x3390 -#define RT3572 0x3572 -#define RT3593 0x3593 -#define RT3883 0x3883 /* WSOC */ -#define RT5390 0x5390 /* 2.4GHz */ -#define RT5392 0x5392 /* 2.4GHz */ -#define RT5592 0x5592 - - u16 rf; - u16 rev; - - enum rt2x00_chip_intf intf; -}; - -/* - * RF register values that belong to a particular channel. - */ -struct rf_channel { - int channel; - u32 rf1; - u32 rf2; - u32 rf3; - u32 rf4; -}; - -/* - * Channel information structure - */ -struct channel_info { - unsigned int flags; -#define GEOGRAPHY_ALLOWED 0x00000001 - - short max_power; - short default_power1; - short default_power2; - short default_power3; -}; - -/* - * Antenna setup values. - */ -struct antenna_setup { - enum antenna rx; - enum antenna tx; - u8 rx_chain_num; - u8 tx_chain_num; -}; - -/* - * Quality statistics about the currently active link. - */ -struct link_qual { - /* - * Statistics required for Link tuning by driver - * The rssi value is provided by rt2x00lib during the - * link_tuner() callback function. - * The false_cca field is filled during the link_stats() - * callback function and could be used during the - * link_tuner() callback function. - */ - int rssi; - int false_cca; - - /* - * VGC levels - * Hardware driver will tune the VGC level during each call - * to the link_tuner() callback function. This vgc_level is - * is determined based on the link quality statistics like - * average RSSI and the false CCA count. - * - * In some cases the drivers need to differentiate between - * the currently "desired" VGC level and the level configured - * in the hardware. The latter is important to reduce the - * number of BBP register reads to reduce register access - * overhead. For this reason we store both values here. - */ - u8 vgc_level; - u8 vgc_level_reg; - - /* - * Statistics required for Signal quality calculation. - * These fields might be changed during the link_stats() - * callback function. - */ - int rx_success; - int rx_failed; - int tx_success; - int tx_failed; -}; - -DECLARE_EWMA(rssi, 1024, 8) - -/* - * Antenna settings about the currently active link. - */ -struct link_ant { - /* - * Antenna flags - */ - unsigned int flags; -#define ANTENNA_RX_DIVERSITY 0x00000001 -#define ANTENNA_TX_DIVERSITY 0x00000002 -#define ANTENNA_MODE_SAMPLE 0x00000004 - - /* - * Currently active TX/RX antenna setup. - * When software diversity is used, this will indicate - * which antenna is actually used at this time. - */ - struct antenna_setup active; - - /* - * RSSI history information for the antenna. - * Used to determine when to switch antenna - * when using software diversity. - */ - int rssi_history; - - /* - * Current RSSI average of the currently active antenna. - * Similar to the avg_rssi in the link_qual structure - * this value is updated by using the walking average. - */ - struct ewma_rssi rssi_ant; -}; - -/* - * To optimize the quality of the link we need to store - * the quality of received frames and periodically - * optimize the link. - */ -struct link { - /* - * Link tuner counter - * The number of times the link has been tuned - * since the radio has been switched on. - */ - u32 count; - - /* - * Quality measurement values. - */ - struct link_qual qual; - - /* - * TX/RX antenna setup. - */ - struct link_ant ant; - - /* - * Currently active average RSSI value - */ - struct ewma_rssi avg_rssi; - - /* - * Work structure for scheduling periodic link tuning. - */ - struct delayed_work work; - - /* - * Work structure for scheduling periodic watchdog monitoring. - * This work must be scheduled on the kernel workqueue, while - * all other work structures must be queued on the mac80211 - * workqueue. This guarantees that the watchdog can schedule - * other work structures and wait for their completion in order - * to bring the device/driver back into the desired state. - */ - struct delayed_work watchdog_work; - - /* - * Work structure for scheduling periodic AGC adjustments. - */ - struct delayed_work agc_work; - - /* - * Work structure for scheduling periodic VCO calibration. - */ - struct delayed_work vco_work; -}; - -enum rt2x00_delayed_flags { - DELAYED_UPDATE_BEACON, -}; - -/* - * Interface structure - * Per interface configuration details, this structure - * is allocated as the private data for ieee80211_vif. - */ -struct rt2x00_intf { - /* - * beacon->skb must be protected with the mutex. - */ - struct mutex beacon_skb_mutex; - - /* - * Entry in the beacon queue which belongs to - * this interface. Each interface has its own - * dedicated beacon entry. - */ - struct queue_entry *beacon; - bool enable_beacon; - - /* - * Actions that needed rescheduling. - */ - unsigned long delayed_flags; - - /* - * Software sequence counter, this is only required - * for hardware which doesn't support hardware - * sequence counting. - */ - atomic_t seqno; -}; - -static inline struct rt2x00_intf* vif_to_intf(struct ieee80211_vif *vif) -{ - return (struct rt2x00_intf *)vif->drv_priv; -} - -/** - * struct hw_mode_spec: Hardware specifications structure - * - * Details about the supported modes, rates and channels - * of a particular chipset. This is used by rt2x00lib - * to build the ieee80211_hw_mode array for mac80211. - * - * @supported_bands: Bitmask contained the supported bands (2.4GHz, 5.2GHz). - * @supported_rates: Rate types which are supported (CCK, OFDM). - * @num_channels: Number of supported channels. This is used as array size - * for @tx_power_a, @tx_power_bg and @channels. - * @channels: Device/chipset specific channel values (See &struct rf_channel). - * @channels_info: Additional information for channels (See &struct channel_info). - * @ht: Driver HT Capabilities (See &ieee80211_sta_ht_cap). - */ -struct hw_mode_spec { - unsigned int supported_bands; -#define SUPPORT_BAND_2GHZ 0x00000001 -#define SUPPORT_BAND_5GHZ 0x00000002 - - unsigned int supported_rates; -#define SUPPORT_RATE_CCK 0x00000001 -#define SUPPORT_RATE_OFDM 0x00000002 - - unsigned int num_channels; - const struct rf_channel *channels; - const struct channel_info *channels_info; - - struct ieee80211_sta_ht_cap ht; -}; - -/* - * Configuration structure wrapper around the - * mac80211 configuration structure. - * When mac80211 configures the driver, rt2x00lib - * can precalculate values which are equal for all - * rt2x00 drivers. Those values can be stored in here. - */ -struct rt2x00lib_conf { - struct ieee80211_conf *conf; - - struct rf_channel rf; - struct channel_info channel; -}; - -/* - * Configuration structure for erp settings. - */ -struct rt2x00lib_erp { - int short_preamble; - int cts_protection; - - u32 basic_rates; - - int slot_time; - - short sifs; - short pifs; - short difs; - short eifs; - - u16 beacon_int; - u16 ht_opmode; -}; - -/* - * Configuration structure for hardware encryption. - */ -struct rt2x00lib_crypto { - enum cipher cipher; - - enum set_key_cmd cmd; - const u8 *address; - - u32 bssidx; - - u8 key[16]; - u8 tx_mic[8]; - u8 rx_mic[8]; - - int wcid; -}; - -/* - * Configuration structure wrapper around the - * rt2x00 interface configuration handler. - */ -struct rt2x00intf_conf { - /* - * Interface type - */ - enum nl80211_iftype type; - - /* - * TSF sync value, this is dependent on the operation type. - */ - enum tsf_sync sync; - - /* - * The MAC and BSSID addresses are simple array of bytes, - * these arrays are little endian, so when sending the addresses - * to the drivers, copy the it into a endian-signed variable. - * - * Note that all devices (except rt2500usb) have 32 bits - * register word sizes. This means that whatever variable we - * pass _must_ be a multiple of 32 bits. Otherwise the device - * might not accept what we are sending to it. - * This will also make it easier for the driver to write - * the data to the device. - */ - __le32 mac[2]; - __le32 bssid[2]; -}; - -/* - * Private structure for storing STA details - * wcid: Wireless Client ID - */ -struct rt2x00_sta { - int wcid; -}; - -static inline struct rt2x00_sta* sta_to_rt2x00_sta(struct ieee80211_sta *sta) -{ - return (struct rt2x00_sta *)sta->drv_priv; -} - -/* - * rt2x00lib callback functions. - */ -struct rt2x00lib_ops { - /* - * Interrupt handlers. - */ - irq_handler_t irq_handler; - - /* - * TX status tasklet handler. - */ - void (*txstatus_tasklet) (unsigned long data); - void (*pretbtt_tasklet) (unsigned long data); - void (*tbtt_tasklet) (unsigned long data); - void (*rxdone_tasklet) (unsigned long data); - void (*autowake_tasklet) (unsigned long data); - - /* - * Device init handlers. - */ - int (*probe_hw) (struct rt2x00_dev *rt2x00dev); - char *(*get_firmware_name) (struct rt2x00_dev *rt2x00dev); - int (*check_firmware) (struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len); - int (*load_firmware) (struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len); - - /* - * Device initialization/deinitialization handlers. - */ - int (*initialize) (struct rt2x00_dev *rt2x00dev); - void (*uninitialize) (struct rt2x00_dev *rt2x00dev); - - /* - * queue initialization handlers - */ - bool (*get_entry_state) (struct queue_entry *entry); - void (*clear_entry) (struct queue_entry *entry); - - /* - * Radio control handlers. - */ - int (*set_device_state) (struct rt2x00_dev *rt2x00dev, - enum dev_state state); - int (*rfkill_poll) (struct rt2x00_dev *rt2x00dev); - void (*link_stats) (struct rt2x00_dev *rt2x00dev, - struct link_qual *qual); - void (*reset_tuner) (struct rt2x00_dev *rt2x00dev, - struct link_qual *qual); - void (*link_tuner) (struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, const u32 count); - void (*gain_calibration) (struct rt2x00_dev *rt2x00dev); - void (*vco_calibration) (struct rt2x00_dev *rt2x00dev); - - /* - * Data queue handlers. - */ - void (*watchdog) (struct rt2x00_dev *rt2x00dev); - void (*start_queue) (struct data_queue *queue); - void (*kick_queue) (struct data_queue *queue); - void (*stop_queue) (struct data_queue *queue); - void (*flush_queue) (struct data_queue *queue, bool drop); - void (*tx_dma_done) (struct queue_entry *entry); - - /* - * TX control handlers - */ - void (*write_tx_desc) (struct queue_entry *entry, - struct txentry_desc *txdesc); - void (*write_tx_data) (struct queue_entry *entry, - struct txentry_desc *txdesc); - void (*write_beacon) (struct queue_entry *entry, - struct txentry_desc *txdesc); - void (*clear_beacon) (struct queue_entry *entry); - int (*get_tx_data_len) (struct queue_entry *entry); - - /* - * RX control handlers - */ - void (*fill_rxdone) (struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc); - - /* - * Configuration handlers. - */ - int (*config_shared_key) (struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key); - int (*config_pairwise_key) (struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key); - void (*config_filter) (struct rt2x00_dev *rt2x00dev, - const unsigned int filter_flags); - void (*config_intf) (struct rt2x00_dev *rt2x00dev, - struct rt2x00_intf *intf, - struct rt2x00intf_conf *conf, - const unsigned int flags); -#define CONFIG_UPDATE_TYPE ( 1 << 1 ) -#define CONFIG_UPDATE_MAC ( 1 << 2 ) -#define CONFIG_UPDATE_BSSID ( 1 << 3 ) - - void (*config_erp) (struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_erp *erp, - u32 changed); - void (*config_ant) (struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant); - void (*config) (struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf, - const unsigned int changed_flags); - int (*sta_add) (struct rt2x00_dev *rt2x00dev, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta); - int (*sta_remove) (struct rt2x00_dev *rt2x00dev, - int wcid); -}; - -/* - * rt2x00 driver callback operation structure. - */ -struct rt2x00_ops { - const char *name; - const unsigned int drv_data_size; - const unsigned int max_ap_intf; - const unsigned int eeprom_size; - const unsigned int rf_size; - const unsigned int tx_queues; - void (*queue_init)(struct data_queue *queue); - const struct rt2x00lib_ops *lib; - const void *drv; - const struct ieee80211_ops *hw; -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - const struct rt2x00debug *debugfs; -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -}; - -/* - * rt2x00 state flags - */ -enum rt2x00_state_flags { - /* - * Device flags - */ - DEVICE_STATE_PRESENT, - DEVICE_STATE_REGISTERED_HW, - DEVICE_STATE_INITIALIZED, - DEVICE_STATE_STARTED, - DEVICE_STATE_ENABLED_RADIO, - DEVICE_STATE_SCANNING, - - /* - * Driver configuration - */ - CONFIG_CHANNEL_HT40, - CONFIG_POWERSAVING, - CONFIG_HT_DISABLED, - CONFIG_QOS_DISABLED, - - /* - * Mark we currently are sequentially reading TX_STA_FIFO register - * FIXME: this is for only rt2800usb, should go to private data - */ - TX_STATUS_READING, -}; - -/* - * rt2x00 capability flags - */ -enum rt2x00_capability_flags { - /* - * Requirements - */ - REQUIRE_FIRMWARE, - REQUIRE_BEACON_GUARD, - REQUIRE_ATIM_QUEUE, - REQUIRE_DMA, - REQUIRE_COPY_IV, - REQUIRE_L2PAD, - REQUIRE_TXSTATUS_FIFO, - REQUIRE_TASKLET_CONTEXT, - REQUIRE_SW_SEQNO, - REQUIRE_HT_TX_DESC, - REQUIRE_PS_AUTOWAKE, - REQUIRE_DELAYED_RFKILL, - - /* - * Capabilities - */ - CAPABILITY_HW_BUTTON, - CAPABILITY_HW_CRYPTO, - CAPABILITY_POWER_LIMIT, - CAPABILITY_CONTROL_FILTERS, - CAPABILITY_CONTROL_FILTER_PSPOLL, - CAPABILITY_PRE_TBTT_INTERRUPT, - CAPABILITY_LINK_TUNING, - CAPABILITY_FRAME_TYPE, - CAPABILITY_RF_SEQUENCE, - CAPABILITY_EXTERNAL_LNA_A, - CAPABILITY_EXTERNAL_LNA_BG, - CAPABILITY_DOUBLE_ANTENNA, - CAPABILITY_BT_COEXIST, - CAPABILITY_VCO_RECALIBRATION, -}; - -/* - * Interface combinations - */ -enum { - IF_COMB_AP = 0, - NUM_IF_COMB, -}; - -/* - * rt2x00 device structure. - */ -struct rt2x00_dev { - /* - * Device structure. - * The structure stored in here depends on the - * system bus (PCI or USB). - * When accessing this variable, the rt2x00dev_{pci,usb} - * macros should be used for correct typecasting. - */ - struct device *dev; - - /* - * Callback functions. - */ - const struct rt2x00_ops *ops; - - /* - * Driver data. - */ - void *drv_data; - - /* - * IEEE80211 control structure. - */ - struct ieee80211_hw *hw; - struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; - enum ieee80211_band curr_band; - int curr_freq; - - /* - * If enabled, the debugfs interface structures - * required for deregistration of debugfs. - */ -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - struct rt2x00debug_intf *debugfs_intf; -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ - - /* - * LED structure for changing the LED status - * by mac8011 or the kernel. - */ -#ifdef CONFIG_RT2X00_LIB_LEDS - struct rt2x00_led led_radio; - struct rt2x00_led led_assoc; - struct rt2x00_led led_qual; - u16 led_mcu_reg; -#endif /* CONFIG_RT2X00_LIB_LEDS */ - - /* - * Device state flags. - * In these flags the current status is stored. - * Access to these flags should occur atomically. - */ - unsigned long flags; - - /* - * Device capabiltiy flags. - * In these flags the device/driver capabilities are stored. - * Access to these flags should occur non-atomically. - */ - unsigned long cap_flags; - - /* - * Device information, Bus IRQ and name (PCI, SoC) - */ - int irq; - const char *name; - - /* - * Chipset identification. - */ - struct rt2x00_chip chip; - - /* - * hw capability specifications. - */ - struct hw_mode_spec spec; - - /* - * This is the default TX/RX antenna setup as indicated - * by the device's EEPROM. - */ - struct antenna_setup default_ant; - - /* - * Register pointers - * csr.base: CSR base register address. (PCI) - * csr.cache: CSR cache for usb_control_msg. (USB) - */ - union csr { - void __iomem *base; - void *cache; - } csr; - - /* - * Mutex to protect register accesses. - * For PCI and USB devices it protects against concurrent indirect - * register access (BBP, RF, MCU) since accessing those - * registers require multiple calls to the CSR registers. - * For USB devices it also protects the csr_cache since that - * field is used for normal CSR access and it cannot support - * multiple callers simultaneously. - */ - struct mutex csr_mutex; - - /* - * Current packet filter configuration for the device. - * This contains all currently active FIF_* flags send - * to us by mac80211 during configure_filter(). - */ - unsigned int packet_filter; - - /* - * Interface details: - * - Open ap interface count. - * - Open sta interface count. - * - Association count. - * - Beaconing enabled count. - */ - unsigned int intf_ap_count; - unsigned int intf_sta_count; - unsigned int intf_associated; - unsigned int intf_beaconing; - - /* - * Interface combinations - */ - struct ieee80211_iface_limit if_limits_ap; - struct ieee80211_iface_combination if_combinations[NUM_IF_COMB]; - - /* - * Link quality - */ - struct link link; - - /* - * EEPROM data. - */ - __le16 *eeprom; - - /* - * Active RF register values. - * These are stored here so we don't need - * to read the rf registers and can directly - * use this value instead. - * This field should be accessed by using - * rt2x00_rf_read() and rt2x00_rf_write(). - */ - u32 *rf; - - /* - * LNA gain - */ - short lna_gain; - - /* - * Current TX power value. - */ - u16 tx_power; - - /* - * Current retry values. - */ - u8 short_retry; - u8 long_retry; - - /* - * Rssi <-> Dbm offset - */ - u8 rssi_offset; - - /* - * Frequency offset. - */ - u8 freq_offset; - - /* - * Association id. - */ - u16 aid; - - /* - * Beacon interval. - */ - u16 beacon_int; - - /** - * Timestamp of last received beacon - */ - unsigned long last_beacon; - - /* - * Low level statistics which will have - * to be kept up to date while device is running. - */ - struct ieee80211_low_level_stats low_level_stats; - - /** - * Work queue for all work which should not be placed - * on the mac80211 workqueue (because of dependencies - * between various work structures). - */ - struct workqueue_struct *workqueue; - - /* - * Scheduled work. - * NOTE: intf_work will use ieee80211_iterate_active_interfaces() - * which means it cannot be placed on the hw->workqueue - * due to RTNL locking requirements. - */ - struct work_struct intf_work; - - /** - * Scheduled work for TX/RX done handling (USB devices) - */ - struct work_struct rxdone_work; - struct work_struct txdone_work; - - /* - * Powersaving work - */ - struct delayed_work autowakeup_work; - struct work_struct sleep_work; - - /* - * Data queue arrays for RX, TX, Beacon and ATIM. - */ - unsigned int data_queues; - struct data_queue *rx; - struct data_queue *tx; - struct data_queue *bcn; - struct data_queue *atim; - - /* - * Firmware image. - */ - const struct firmware *fw; - - /* - * FIFO for storing tx status reports between isr and tasklet. - */ - DECLARE_KFIFO_PTR(txstatus_fifo, u32); - - /* - * Timer to ensure tx status reports are read (rt2800usb). - */ - struct hrtimer txstatus_timer; - - /* - * Tasklet for processing tx status reports (rt2800pci). - */ - struct tasklet_struct txstatus_tasklet; - struct tasklet_struct pretbtt_tasklet; - struct tasklet_struct tbtt_tasklet; - struct tasklet_struct rxdone_tasklet; - struct tasklet_struct autowake_tasklet; - - /* - * Used for VCO periodic calibration. - */ - int rf_channel; - - /* - * Protect the interrupt mask register. - */ - spinlock_t irqmask_lock; - - /* - * List of BlockAckReq TX entries that need driver BlockAck processing. - */ - struct list_head bar_list; - spinlock_t bar_list_lock; - - /* Extra TX headroom required for alignment purposes. */ - unsigned int extra_tx_headroom; -}; - -struct rt2x00_bar_list_entry { - struct list_head list; - struct rcu_head head; - - struct queue_entry *entry; - int block_acked; - - /* Relevant parts of the IEEE80211 BAR header */ - __u8 ra[6]; - __u8 ta[6]; - __le16 control; - __le16 start_seq_num; -}; - -/* - * Register defines. - * Some registers require multiple attempts before success, - * in those cases REGISTER_BUSY_COUNT attempts should be - * taken with a REGISTER_BUSY_DELAY interval. Due to USB - * bus delays, we do not have to loop so many times to wait - * for valid register value on that bus. - */ -#define REGISTER_BUSY_COUNT 100 -#define REGISTER_USB_BUSY_COUNT 20 -#define REGISTER_BUSY_DELAY 100 - -/* - * Generic RF access. - * The RF is being accessed by word index. - */ -static inline void rt2x00_rf_read(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 *data) -{ - BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32)); - *data = rt2x00dev->rf[word - 1]; -} - -static inline void rt2x00_rf_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 data) -{ - BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32)); - rt2x00dev->rf[word - 1] = data; -} - -/* - * Generic EEPROM access. The EEPROM is being accessed by word or byte index. - */ -static inline void *rt2x00_eeprom_addr(struct rt2x00_dev *rt2x00dev, - const unsigned int word) -{ - return (void *)&rt2x00dev->eeprom[word]; -} - -static inline void rt2x00_eeprom_read(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u16 *data) -{ - *data = le16_to_cpu(rt2x00dev->eeprom[word]); -} - -static inline void rt2x00_eeprom_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u16 data) -{ - rt2x00dev->eeprom[word] = cpu_to_le16(data); -} - -static inline u8 rt2x00_eeprom_byte(struct rt2x00_dev *rt2x00dev, - const unsigned int byte) -{ - return *(((u8 *)rt2x00dev->eeprom) + byte); -} - -/* - * Chipset handlers - */ -static inline void rt2x00_set_chip(struct rt2x00_dev *rt2x00dev, - const u16 rt, const u16 rf, const u16 rev) -{ - rt2x00dev->chip.rt = rt; - rt2x00dev->chip.rf = rf; - rt2x00dev->chip.rev = rev; - - rt2x00_info(rt2x00dev, "Chipset detected - rt: %04x, rf: %04x, rev: %04x\n", - rt2x00dev->chip.rt, rt2x00dev->chip.rf, - rt2x00dev->chip.rev); -} - -static inline void rt2x00_set_rt(struct rt2x00_dev *rt2x00dev, - const u16 rt, const u16 rev) -{ - rt2x00dev->chip.rt = rt; - rt2x00dev->chip.rev = rev; - - rt2x00_info(rt2x00dev, "RT chipset %04x, rev %04x detected\n", - rt2x00dev->chip.rt, rt2x00dev->chip.rev); -} - -static inline void rt2x00_set_rf(struct rt2x00_dev *rt2x00dev, const u16 rf) -{ - rt2x00dev->chip.rf = rf; - - rt2x00_info(rt2x00dev, "RF chipset %04x detected\n", - rt2x00dev->chip.rf); -} - -static inline bool rt2x00_rt(struct rt2x00_dev *rt2x00dev, const u16 rt) -{ - return (rt2x00dev->chip.rt == rt); -} - -static inline bool rt2x00_rf(struct rt2x00_dev *rt2x00dev, const u16 rf) -{ - return (rt2x00dev->chip.rf == rf); -} - -static inline u16 rt2x00_rev(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00dev->chip.rev; -} - -static inline bool rt2x00_rt_rev(struct rt2x00_dev *rt2x00dev, - const u16 rt, const u16 rev) -{ - return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) == rev); -} - -static inline bool rt2x00_rt_rev_lt(struct rt2x00_dev *rt2x00dev, - const u16 rt, const u16 rev) -{ - return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) < rev); -} - -static inline bool rt2x00_rt_rev_gte(struct rt2x00_dev *rt2x00dev, - const u16 rt, const u16 rev) -{ - return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) >= rev); -} - -static inline void rt2x00_set_chip_intf(struct rt2x00_dev *rt2x00dev, - enum rt2x00_chip_intf intf) -{ - rt2x00dev->chip.intf = intf; -} - -static inline bool rt2x00_intf(struct rt2x00_dev *rt2x00dev, - enum rt2x00_chip_intf intf) -{ - return (rt2x00dev->chip.intf == intf); -} - -static inline bool rt2x00_is_pci(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI) || - rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); -} - -static inline bool rt2x00_is_pcie(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); -} - -static inline bool rt2x00_is_usb(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); -} - -static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); -} - -/* Helpers for capability flags */ - -static inline bool -rt2x00_has_cap_flag(struct rt2x00_dev *rt2x00dev, - enum rt2x00_capability_flags cap_flag) -{ - return test_bit(cap_flag, &rt2x00dev->cap_flags); -} - -static inline bool -rt2x00_has_cap_hw_crypto(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_HW_CRYPTO); -} - -static inline bool -rt2x00_has_cap_power_limit(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_POWER_LIMIT); -} - -static inline bool -rt2x00_has_cap_control_filters(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTERS); -} - -static inline bool -rt2x00_has_cap_control_filter_pspoll(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTER_PSPOLL); -} - -static inline bool -rt2x00_has_cap_pre_tbtt_interrupt(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_PRE_TBTT_INTERRUPT); -} - -static inline bool -rt2x00_has_cap_link_tuning(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_LINK_TUNING); -} - -static inline bool -rt2x00_has_cap_frame_type(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_FRAME_TYPE); -} - -static inline bool -rt2x00_has_cap_rf_sequence(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_RF_SEQUENCE); -} - -static inline bool -rt2x00_has_cap_external_lna_a(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_A); -} - -static inline bool -rt2x00_has_cap_external_lna_bg(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_BG); -} - -static inline bool -rt2x00_has_cap_double_antenna(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_DOUBLE_ANTENNA); -} - -static inline bool -rt2x00_has_cap_bt_coexist(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_BT_COEXIST); -} - -static inline bool -rt2x00_has_cap_vco_recalibration(struct rt2x00_dev *rt2x00dev) -{ - return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_VCO_RECALIBRATION); -} - -/** - * rt2x00queue_map_txskb - Map a skb into DMA for TX purposes. - * @entry: Pointer to &struct queue_entry - * - * Returns -ENOMEM if mapping fail, 0 otherwise. - */ -int rt2x00queue_map_txskb(struct queue_entry *entry); - -/** - * rt2x00queue_unmap_skb - Unmap a skb from DMA. - * @entry: Pointer to &struct queue_entry - */ -void rt2x00queue_unmap_skb(struct queue_entry *entry); - -/** - * rt2x00queue_get_tx_queue - Convert tx queue index to queue pointer - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * @queue: rt2x00 queue index (see &enum data_queue_qid). - * - * Returns NULL for non tx queues. - */ -static inline struct data_queue * -rt2x00queue_get_tx_queue(struct rt2x00_dev *rt2x00dev, - const enum data_queue_qid queue) -{ - if (queue < rt2x00dev->ops->tx_queues && rt2x00dev->tx) - return &rt2x00dev->tx[queue]; - - if (queue == QID_ATIM) - return rt2x00dev->atim; - - return NULL; -} - -/** - * rt2x00queue_get_entry - Get queue entry where the given index points to. - * @queue: Pointer to &struct data_queue from where we obtain the entry. - * @index: Index identifier for obtaining the correct index. - */ -struct queue_entry *rt2x00queue_get_entry(struct data_queue *queue, - enum queue_index index); - -/** - * rt2x00queue_pause_queue - Pause a data queue - * @queue: Pointer to &struct data_queue. - * - * This function will pause the data queue locally, preventing - * new frames to be added to the queue (while the hardware is - * still allowed to run). - */ -void rt2x00queue_pause_queue(struct data_queue *queue); - -/** - * rt2x00queue_unpause_queue - unpause a data queue - * @queue: Pointer to &struct data_queue. - * - * This function will unpause the data queue locally, allowing - * new frames to be added to the queue again. - */ -void rt2x00queue_unpause_queue(struct data_queue *queue); - -/** - * rt2x00queue_start_queue - Start a data queue - * @queue: Pointer to &struct data_queue. - * - * This function will start handling all pending frames in the queue. - */ -void rt2x00queue_start_queue(struct data_queue *queue); - -/** - * rt2x00queue_stop_queue - Halt a data queue - * @queue: Pointer to &struct data_queue. - * - * This function will stop all pending frames in the queue. - */ -void rt2x00queue_stop_queue(struct data_queue *queue); - -/** - * rt2x00queue_flush_queue - Flush a data queue - * @queue: Pointer to &struct data_queue. - * @drop: True to drop all pending frames. - * - * This function will flush the queue. After this call - * the queue is guaranteed to be empty. - */ -void rt2x00queue_flush_queue(struct data_queue *queue, bool drop); - -/** - * rt2x00queue_start_queues - Start all data queues - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * - * This function will loop through all available queues to start them - */ -void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00queue_stop_queues - Halt all data queues - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * - * This function will loop through all available queues to stop - * any pending frames. - */ -void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00queue_flush_queues - Flush all data queues - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * @drop: True to drop all pending frames. - * - * This function will loop through all available queues to flush - * any pending frames. - */ -void rt2x00queue_flush_queues(struct rt2x00_dev *rt2x00dev, bool drop); - -/* - * Debugfs handlers. - */ -/** - * rt2x00debug_dump_frame - Dump a frame to userspace through debugfs. - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * @type: The type of frame that is being dumped. - * @skb: The skb containing the frame to be dumped. - */ -#ifdef CONFIG_RT2X00_LIB_DEBUGFS -void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, - enum rt2x00_dump_type type, struct sk_buff *skb); -#else -static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, - enum rt2x00_dump_type type, - struct sk_buff *skb) -{ -} -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ - -/* - * Utility functions. - */ -u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev, - struct ieee80211_vif *vif); - -/* - * Interrupt context handlers. - */ -void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev); -void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev); -void rt2x00lib_dmastart(struct queue_entry *entry); -void rt2x00lib_dmadone(struct queue_entry *entry); -void rt2x00lib_txdone(struct queue_entry *entry, - struct txdone_entry_desc *txdesc); -void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status); -void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp); - -/* - * mac80211 handlers. - */ -void rt2x00mac_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb); -int rt2x00mac_start(struct ieee80211_hw *hw); -void rt2x00mac_stop(struct ieee80211_hw *hw); -int rt2x00mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); -void rt2x00mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); -int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed); -void rt2x00mac_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast); -int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, - bool set); -#ifdef CONFIG_RT2X00_LIB_CRYPTO -int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key); -#else -#define rt2x00mac_set_key NULL -#endif /* CONFIG_RT2X00_LIB_CRYPTO */ -int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const u8 *mac_addr); -void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); -int rt2x00mac_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats); -void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes); -int rt2x00mac_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params); -void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw); -void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 queues, bool drop); -int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); -int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); -void rt2x00mac_get_ringparam(struct ieee80211_hw *hw, - u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); -bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw); - -/* - * Driver allocation handlers. - */ -int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev); -void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev); -#ifdef CONFIG_PM -int rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev, pm_message_t state); -int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev); -#endif /* CONFIG_PM */ - -#endif /* RT2X00_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c deleted file mode 100644 index 7e8bb1198ae9..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00lib - Abstract: rt2x00 generic configuration routines. - */ - -#include -#include - -#include "rt2x00.h" -#include "rt2x00lib.h" - -void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev, - struct rt2x00_intf *intf, - enum nl80211_iftype type, - const u8 *mac, const u8 *bssid) -{ - struct rt2x00intf_conf conf; - unsigned int flags = 0; - - conf.type = type; - - switch (type) { - case NL80211_IFTYPE_ADHOC: - conf.sync = TSF_SYNC_ADHOC; - break; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_WDS: - conf.sync = TSF_SYNC_AP_NONE; - break; - case NL80211_IFTYPE_STATION: - conf.sync = TSF_SYNC_INFRA; - break; - default: - conf.sync = TSF_SYNC_NONE; - break; - } - - /* - * Note that when NULL is passed as address we will send - * 00:00:00:00:00 to the device to clear the address. - * This will prevent the device being confused when it wants - * to ACK frames or considers itself associated. - */ - memset(conf.mac, 0, sizeof(conf.mac)); - if (mac) - memcpy(conf.mac, mac, ETH_ALEN); - - memset(conf.bssid, 0, sizeof(conf.bssid)); - if (bssid) - memcpy(conf.bssid, bssid, ETH_ALEN); - - flags |= CONFIG_UPDATE_TYPE; - if (mac || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)) - flags |= CONFIG_UPDATE_MAC; - if (bssid || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)) - flags |= CONFIG_UPDATE_BSSID; - - rt2x00dev->ops->lib->config_intf(rt2x00dev, intf, &conf, flags); -} - -void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, - struct rt2x00_intf *intf, - struct ieee80211_bss_conf *bss_conf, - u32 changed) -{ - struct rt2x00lib_erp erp; - - memset(&erp, 0, sizeof(erp)); - - erp.short_preamble = bss_conf->use_short_preamble; - erp.cts_protection = bss_conf->use_cts_prot; - - erp.slot_time = bss_conf->use_short_slot ? SHORT_SLOT_TIME : SLOT_TIME; - erp.sifs = SIFS; - erp.pifs = bss_conf->use_short_slot ? SHORT_PIFS : PIFS; - erp.difs = bss_conf->use_short_slot ? SHORT_DIFS : DIFS; - erp.eifs = bss_conf->use_short_slot ? SHORT_EIFS : EIFS; - - erp.basic_rates = bss_conf->basic_rates; - erp.beacon_int = bss_conf->beacon_int; - - /* Update the AID, this is needed for dynamic PS support */ - rt2x00dev->aid = bss_conf->assoc ? bss_conf->aid : 0; - rt2x00dev->last_beacon = bss_conf->sync_tsf; - - /* Update global beacon interval time, this is needed for PS support */ - rt2x00dev->beacon_int = bss_conf->beacon_int; - - if (changed & BSS_CHANGED_HT) - erp.ht_opmode = bss_conf->ht_operation_mode; - - rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed); -} - -void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, - struct antenna_setup config) -{ - struct link_ant *ant = &rt2x00dev->link.ant; - struct antenna_setup *def = &rt2x00dev->default_ant; - struct antenna_setup *active = &rt2x00dev->link.ant.active; - - /* - * When the caller tries to send the SW diversity, - * we must update the ANTENNA_RX_DIVERSITY flag to - * enable the antenna diversity in the link tuner. - * - * Secondly, we must guarentee we never send the - * software antenna diversity command to the driver. - */ - if (!(ant->flags & ANTENNA_RX_DIVERSITY)) { - if (config.rx == ANTENNA_SW_DIVERSITY) { - ant->flags |= ANTENNA_RX_DIVERSITY; - - if (def->rx == ANTENNA_SW_DIVERSITY) - config.rx = ANTENNA_B; - else - config.rx = def->rx; - } - } else if (config.rx == ANTENNA_SW_DIVERSITY) - config.rx = active->rx; - - if (!(ant->flags & ANTENNA_TX_DIVERSITY)) { - if (config.tx == ANTENNA_SW_DIVERSITY) { - ant->flags |= ANTENNA_TX_DIVERSITY; - - if (def->tx == ANTENNA_SW_DIVERSITY) - config.tx = ANTENNA_B; - else - config.tx = def->tx; - } - } else if (config.tx == ANTENNA_SW_DIVERSITY) - config.tx = active->tx; - - /* - * Antenna setup changes require the RX to be disabled, - * else the changes will be ignored by the device. - */ - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2x00queue_stop_queue(rt2x00dev->rx); - - /* - * Write new antenna setup to device and reset the link tuner. - * The latter is required since we need to recalibrate the - * noise-sensitivity ratio for the new setup. - */ - rt2x00dev->ops->lib->config_ant(rt2x00dev, &config); - - rt2x00link_reset_tuner(rt2x00dev, true); - - memcpy(active, &config, sizeof(config)); - - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2x00queue_start_queue(rt2x00dev->rx); -} - -static u16 rt2x00ht_center_channel(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf) -{ - struct hw_mode_spec *spec = &rt2x00dev->spec; - int center_channel; - u16 i; - - /* - * Initialize center channel to current channel. - */ - center_channel = spec->channels[conf->chandef.chan->hw_value].channel; - - /* - * Adjust center channel to HT40+ and HT40- operation. - */ - if (conf_is_ht40_plus(conf)) - center_channel += 2; - else if (conf_is_ht40_minus(conf)) - center_channel -= (center_channel == 14) ? 1 : 2; - - for (i = 0; i < spec->num_channels; i++) - if (spec->channels[i].channel == center_channel) - return i; - - WARN_ON(1); - return conf->chandef.chan->hw_value; -} - -void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - unsigned int ieee80211_flags) -{ - struct rt2x00lib_conf libconf; - u16 hw_value; - u16 autowake_timeout; - u16 beacon_int; - u16 beacon_diff; - - memset(&libconf, 0, sizeof(libconf)); - - libconf.conf = conf; - - if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) { - if (!conf_is_ht(conf)) - set_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags); - else - clear_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags); - - if (conf_is_ht40(conf)) { - set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); - hw_value = rt2x00ht_center_channel(rt2x00dev, conf); - } else { - clear_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); - hw_value = conf->chandef.chan->hw_value; - } - - memcpy(&libconf.rf, - &rt2x00dev->spec.channels[hw_value], - sizeof(libconf.rf)); - - memcpy(&libconf.channel, - &rt2x00dev->spec.channels_info[hw_value], - sizeof(libconf.channel)); - - /* Used for VCO periodic calibration */ - rt2x00dev->rf_channel = libconf.rf.channel; - } - - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) && - (ieee80211_flags & IEEE80211_CONF_CHANGE_PS)) - cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); - - /* - * Start configuration. - */ - rt2x00dev->ops->lib->config(rt2x00dev, &libconf, ieee80211_flags); - - /* - * Some configuration changes affect the link quality - * which means we need to reset the link tuner. - */ - if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) - rt2x00link_reset_tuner(rt2x00dev, false); - - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && - rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) && - (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) && - (conf->flags & IEEE80211_CONF_PS)) { - beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon; - beacon_int = msecs_to_jiffies(rt2x00dev->beacon_int); - - if (beacon_diff > beacon_int) - beacon_diff = 0; - - autowake_timeout = (conf->ps_dtim_period * beacon_int) - beacon_diff; - queue_delayed_work(rt2x00dev->workqueue, - &rt2x00dev->autowakeup_work, - autowake_timeout - 15); - } - - if (conf->flags & IEEE80211_CONF_PS) - set_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); - else - clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); - - rt2x00dev->curr_band = conf->chandef.chan->band; - rt2x00dev->curr_freq = conf->chandef.chan->center_freq; - rt2x00dev->tx_power = conf->power_level; - rt2x00dev->short_retry = conf->short_frame_max_tx_count; - rt2x00dev->long_retry = conf->long_frame_max_tx_count; -} diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c deleted file mode 100644 index a2fd05ba25ca..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00crypto.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00lib - Abstract: rt2x00 crypto specific routines. - */ - -#include -#include - -#include "rt2x00.h" -#include "rt2x00lib.h" - -enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) -{ - switch (key->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - return CIPHER_WEP64; - case WLAN_CIPHER_SUITE_WEP104: - return CIPHER_WEP128; - case WLAN_CIPHER_SUITE_TKIP: - return CIPHER_TKIP; - case WLAN_CIPHER_SUITE_CCMP: - return CIPHER_AES; - default: - return CIPHER_NONE; - } -} - -void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txentry_desc *txdesc) -{ - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; - - if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !hw_key) - return; - - __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); - - txdesc->cipher = rt2x00crypto_key_to_cipher(hw_key); - - if (hw_key->flags & IEEE80211_KEY_FLAG_PAIRWISE) - __set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags); - - txdesc->key_idx = hw_key->hw_key_idx; - txdesc->iv_offset = txdesc->header_length; - txdesc->iv_len = hw_key->iv_len; - - if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) - __set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags); - - if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) - __set_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags); -} - -unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb) -{ - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_key_conf *key = tx_info->control.hw_key; - unsigned int overhead = 0; - - if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key) - return overhead; - - /* - * Extend frame length to include IV/EIV/ICV/MMIC, - * note that these lengths should only be added when - * mac80211 does not generate it. - */ - overhead += key->icv_len; - - if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) - overhead += key->iv_len; - - if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { - if (key->cipher == WLAN_CIPHER_SUITE_TKIP) - overhead += 8; - } - - return overhead; -} - -void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, struct txentry_desc *txdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); - - if (unlikely(!txdesc->iv_len)) - return; - - /* Copy IV/EIV data */ - memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); -} - -void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, struct txentry_desc *txdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); - - if (unlikely(!txdesc->iv_len)) - return; - - /* Copy IV/EIV data */ - memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); - - /* Move ieee80211 header */ - memmove(skb->data + txdesc->iv_len, skb->data, txdesc->iv_offset); - - /* Pull buffer to correct size */ - skb_pull(skb, txdesc->iv_len); - txdesc->length -= txdesc->iv_len; - - /* IV/EIV data has officially been stripped */ - skbdesc->flags |= SKBDESC_IV_STRIPPED; -} - -void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); - const unsigned int iv_len = - ((!!(skbdesc->iv[0])) * 4) + ((!!(skbdesc->iv[1])) * 4); - - if (!(skbdesc->flags & SKBDESC_IV_STRIPPED)) - return; - - skb_push(skb, iv_len); - - /* Move ieee80211 header */ - memmove(skb->data, skb->data + iv_len, header_length); - - /* Copy IV/EIV data */ - memcpy(skb->data + header_length, skbdesc->iv, iv_len); - - /* IV/EIV data has returned into the frame */ - skbdesc->flags &= ~SKBDESC_IV_STRIPPED; -} - -void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, - unsigned int header_length, - struct rxdone_entry_desc *rxdesc) -{ - unsigned int payload_len = rxdesc->size - header_length; - unsigned int align = ALIGN_SIZE(skb, header_length); - unsigned int iv_len; - unsigned int icv_len; - unsigned int transfer = 0; - - /* - * WEP64/WEP128: Provides IV & ICV - * TKIP: Provides IV/EIV & ICV - * AES: Provies IV/EIV & ICV - */ - switch (rxdesc->cipher) { - case CIPHER_WEP64: - case CIPHER_WEP128: - iv_len = 4; - icv_len = 4; - break; - case CIPHER_TKIP: - iv_len = 8; - icv_len = 4; - break; - case CIPHER_AES: - iv_len = 8; - icv_len = 8; - break; - default: - /* Unsupport type */ - return; - } - - /* - * Make room for new data. There are 2 possibilities - * either the alignment is already present between - * the 802.11 header and payload. In that case we - * we have to move the header less then the iv_len - * since we can use the already available l2pad bytes - * for the iv data. - * When the alignment must be added manually we must - * move the header more then iv_len since we must - * make room for the payload move as well. - */ - if (rxdesc->dev_flags & RXDONE_L2PAD) { - skb_push(skb, iv_len - align); - skb_put(skb, icv_len); - - /* Move ieee80211 header */ - memmove(skb->data + transfer, - skb->data + transfer + (iv_len - align), - header_length); - transfer += header_length; - } else { - skb_push(skb, iv_len + align); - if (align < icv_len) - skb_put(skb, icv_len - align); - else if (align > icv_len) - skb_trim(skb, rxdesc->size + iv_len + icv_len); - - /* Move ieee80211 header */ - memmove(skb->data + transfer, - skb->data + transfer + iv_len + align, - header_length); - transfer += header_length; - } - - /* Copy IV/EIV data */ - memcpy(skb->data + transfer, rxdesc->iv, iv_len); - transfer += iv_len; - - /* - * Move payload for alignment purposes. Note that - * this is only needed when no l2 padding is present. - */ - if (!(rxdesc->dev_flags & RXDONE_L2PAD)) { - memmove(skb->data + transfer, - skb->data + transfer + align, - payload_len); - } - - /* - * NOTE: Always count the payload as transferred, - * even when alignment was set to zero. This is required - * for determining the correct offset for the ICV data. - */ - transfer += payload_len; - - /* - * Copy ICV data - * AES appends 8 bytes, we can't fill the upper - * 4 bytes, but mac80211 doesn't care about what - * we provide here anyway and strips it immediately. - */ - memcpy(skb->data + transfer, &rxdesc->icv, 4); - transfer += icv_len; - - /* IV/EIV/ICV has been inserted into frame */ - rxdesc->size = transfer; - rxdesc->flags &= ~RX_FLAG_IV_STRIPPED; -} diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c deleted file mode 100644 index 90fdb02b55e7..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ /dev/null @@ -1,800 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00lib - Abstract: rt2x00 debugfs specific routines. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00lib.h" -#include "rt2x00dump.h" - -#define MAX_LINE_LENGTH 64 - -struct rt2x00debug_crypto { - unsigned long success; - unsigned long icv_error; - unsigned long mic_error; - unsigned long key_error; -}; - -struct rt2x00debug_intf { - /* - * Pointer to driver structure where - * this debugfs entry belongs to. - */ - struct rt2x00_dev *rt2x00dev; - - /* - * Reference to the rt2x00debug structure - * which can be used to communicate with - * the registers. - */ - const struct rt2x00debug *debug; - - /* - * Debugfs entries for: - * - driver folder - * - driver file - * - chipset file - * - device state flags file - * - device capability flags file - * - register folder - * - csr offset/value files - * - eeprom offset/value files - * - bbp offset/value files - * - rf offset/value files - * - rfcsr offset/value files - * - queue folder - * - frame dump file - * - queue stats file - * - crypto stats file - */ - struct dentry *driver_folder; - struct dentry *driver_entry; - struct dentry *chipset_entry; - struct dentry *dev_flags; - struct dentry *cap_flags; - struct dentry *register_folder; - struct dentry *csr_off_entry; - struct dentry *csr_val_entry; - struct dentry *eeprom_off_entry; - struct dentry *eeprom_val_entry; - struct dentry *bbp_off_entry; - struct dentry *bbp_val_entry; - struct dentry *rf_off_entry; - struct dentry *rf_val_entry; - struct dentry *rfcsr_off_entry; - struct dentry *rfcsr_val_entry; - struct dentry *queue_folder; - struct dentry *queue_frame_dump_entry; - struct dentry *queue_stats_entry; - struct dentry *crypto_stats_entry; - - /* - * The frame dump file only allows a single reader, - * so we need to store the current state here. - */ - unsigned long frame_dump_flags; -#define FRAME_DUMP_FILE_OPEN 1 - - /* - * We queue each frame before dumping it to the user, - * per read command we will pass a single skb structure - * so we should be prepared to queue multiple sk buffers - * before sending it to userspace. - */ - struct sk_buff_head frame_dump_skbqueue; - wait_queue_head_t frame_dump_waitqueue; - - /* - * HW crypto statistics. - * All statistics are stored separately per cipher type. - */ - struct rt2x00debug_crypto crypto_stats[CIPHER_MAX]; - - /* - * Driver and chipset files will use a data buffer - * that has been created in advance. This will simplify - * the code since we can use the debugfs functions. - */ - struct debugfs_blob_wrapper driver_blob; - struct debugfs_blob_wrapper chipset_blob; - - /* - * Requested offset for each register type. - */ - unsigned int offset_csr; - unsigned int offset_eeprom; - unsigned int offset_bbp; - unsigned int offset_rf; - unsigned int offset_rfcsr; -}; - -void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, - struct rxdone_entry_desc *rxdesc) -{ - struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; - enum cipher cipher = rxdesc->cipher; - enum rx_crypto status = rxdesc->cipher_status; - - if (cipher == CIPHER_TKIP_NO_MIC) - cipher = CIPHER_TKIP; - if (cipher == CIPHER_NONE || cipher >= CIPHER_MAX) - return; - - /* Remove CIPHER_NONE index */ - cipher--; - - intf->crypto_stats[cipher].success += (status == RX_CRYPTO_SUCCESS); - intf->crypto_stats[cipher].icv_error += (status == RX_CRYPTO_FAIL_ICV); - intf->crypto_stats[cipher].mic_error += (status == RX_CRYPTO_FAIL_MIC); - intf->crypto_stats[cipher].key_error += (status == RX_CRYPTO_FAIL_KEY); -} - -void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, - enum rt2x00_dump_type type, struct sk_buff *skb) -{ - struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); - struct sk_buff *skbcopy; - struct rt2x00dump_hdr *dump_hdr; - struct timeval timestamp; - u32 data_len; - - if (likely(!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags))) - return; - - do_gettimeofday(×tamp); - - if (skb_queue_len(&intf->frame_dump_skbqueue) > 20) { - rt2x00_dbg(rt2x00dev, "txrx dump queue length exceeded\n"); - return; - } - - data_len = skb->len; - if (skbdesc->flags & SKBDESC_DESC_IN_SKB) - data_len -= skbdesc->desc_len; - - skbcopy = alloc_skb(sizeof(*dump_hdr) + skbdesc->desc_len + data_len, - GFP_ATOMIC); - if (!skbcopy) { - rt2x00_dbg(rt2x00dev, "Failed to copy skb for dump\n"); - return; - } - - dump_hdr = (struct rt2x00dump_hdr *)skb_put(skbcopy, sizeof(*dump_hdr)); - dump_hdr->version = cpu_to_le32(DUMP_HEADER_VERSION); - dump_hdr->header_length = cpu_to_le32(sizeof(*dump_hdr)); - dump_hdr->desc_length = cpu_to_le32(skbdesc->desc_len); - dump_hdr->data_length = cpu_to_le32(data_len); - dump_hdr->chip_rt = cpu_to_le16(rt2x00dev->chip.rt); - dump_hdr->chip_rf = cpu_to_le16(rt2x00dev->chip.rf); - dump_hdr->chip_rev = cpu_to_le16(rt2x00dev->chip.rev); - dump_hdr->type = cpu_to_le16(type); - dump_hdr->queue_index = skbdesc->entry->queue->qid; - dump_hdr->entry_index = skbdesc->entry->entry_idx; - dump_hdr->timestamp_sec = cpu_to_le32(timestamp.tv_sec); - dump_hdr->timestamp_usec = cpu_to_le32(timestamp.tv_usec); - - if (!(skbdesc->flags & SKBDESC_DESC_IN_SKB)) - memcpy(skb_put(skbcopy, skbdesc->desc_len), skbdesc->desc, - skbdesc->desc_len); - memcpy(skb_put(skbcopy, skb->len), skb->data, skb->len); - - skb_queue_tail(&intf->frame_dump_skbqueue, skbcopy); - wake_up_interruptible(&intf->frame_dump_waitqueue); - - /* - * Verify that the file has not been closed while we were working. - */ - if (!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) - skb_queue_purge(&intf->frame_dump_skbqueue); -} -EXPORT_SYMBOL_GPL(rt2x00debug_dump_frame); - -static int rt2x00debug_file_open(struct inode *inode, struct file *file) -{ - struct rt2x00debug_intf *intf = inode->i_private; - - file->private_data = inode->i_private; - - if (!try_module_get(intf->debug->owner)) - return -EBUSY; - - return 0; -} - -static int rt2x00debug_file_release(struct inode *inode, struct file *file) -{ - struct rt2x00debug_intf *intf = file->private_data; - - module_put(intf->debug->owner); - - return 0; -} - -static int rt2x00debug_open_queue_dump(struct inode *inode, struct file *file) -{ - struct rt2x00debug_intf *intf = inode->i_private; - int retval; - - retval = rt2x00debug_file_open(inode, file); - if (retval) - return retval; - - if (test_and_set_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) { - rt2x00debug_file_release(inode, file); - return -EBUSY; - } - - return 0; -} - -static int rt2x00debug_release_queue_dump(struct inode *inode, struct file *file) -{ - struct rt2x00debug_intf *intf = inode->i_private; - - skb_queue_purge(&intf->frame_dump_skbqueue); - - clear_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags); - - return rt2x00debug_file_release(inode, file); -} - -static ssize_t rt2x00debug_read_queue_dump(struct file *file, - char __user *buf, - size_t length, - loff_t *offset) -{ - struct rt2x00debug_intf *intf = file->private_data; - struct sk_buff *skb; - size_t status; - int retval; - - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - - retval = - wait_event_interruptible(intf->frame_dump_waitqueue, - (skb = - skb_dequeue(&intf->frame_dump_skbqueue))); - if (retval) - return retval; - - status = min_t(size_t, skb->len, length); - if (copy_to_user(buf, skb->data, status)) { - status = -EFAULT; - goto exit; - } - - *offset += status; - -exit: - kfree_skb(skb); - - return status; -} - -static unsigned int rt2x00debug_poll_queue_dump(struct file *file, - poll_table *wait) -{ - struct rt2x00debug_intf *intf = file->private_data; - - poll_wait(file, &intf->frame_dump_waitqueue, wait); - - if (!skb_queue_empty(&intf->frame_dump_skbqueue)) - return POLLOUT | POLLWRNORM; - - return 0; -} - -static const struct file_operations rt2x00debug_fop_queue_dump = { - .owner = THIS_MODULE, - .read = rt2x00debug_read_queue_dump, - .poll = rt2x00debug_poll_queue_dump, - .open = rt2x00debug_open_queue_dump, - .release = rt2x00debug_release_queue_dump, - .llseek = default_llseek, -}; - -static ssize_t rt2x00debug_read_queue_stats(struct file *file, - char __user *buf, - size_t length, - loff_t *offset) -{ - struct rt2x00debug_intf *intf = file->private_data; - struct data_queue *queue; - unsigned long irqflags; - unsigned int lines = 1 + intf->rt2x00dev->data_queues; - size_t size; - char *data; - char *temp; - - if (*offset) - return 0; - - data = kcalloc(lines, MAX_LINE_LENGTH, GFP_KERNEL); - if (!data) - return -ENOMEM; - - temp = data + - sprintf(data, "qid\tflags\t\tcount\tlimit\tlength\tindex\tdma done\tdone\n"); - - queue_for_each(intf->rt2x00dev, queue) { - spin_lock_irqsave(&queue->index_lock, irqflags); - - temp += sprintf(temp, "%d\t0x%.8x\t%d\t%d\t%d\t%d\t%d\t\t%d\n", - queue->qid, (unsigned int)queue->flags, - queue->count, queue->limit, queue->length, - queue->index[Q_INDEX], - queue->index[Q_INDEX_DMA_DONE], - queue->index[Q_INDEX_DONE]); - - spin_unlock_irqrestore(&queue->index_lock, irqflags); - } - - size = strlen(data); - size = min(size, length); - - if (copy_to_user(buf, data, size)) { - kfree(data); - return -EFAULT; - } - - kfree(data); - - *offset += size; - return size; -} - -static const struct file_operations rt2x00debug_fop_queue_stats = { - .owner = THIS_MODULE, - .read = rt2x00debug_read_queue_stats, - .open = rt2x00debug_file_open, - .release = rt2x00debug_file_release, - .llseek = default_llseek, -}; - -#ifdef CONFIG_RT2X00_LIB_CRYPTO -static ssize_t rt2x00debug_read_crypto_stats(struct file *file, - char __user *buf, - size_t length, - loff_t *offset) -{ - struct rt2x00debug_intf *intf = file->private_data; - static const char * const name[] = { "WEP64", "WEP128", "TKIP", "AES" }; - char *data; - char *temp; - size_t size; - unsigned int i; - - if (*offset) - return 0; - - data = kzalloc((1 + CIPHER_MAX) * MAX_LINE_LENGTH, GFP_KERNEL); - if (!data) - return -ENOMEM; - - temp = data; - temp += sprintf(data, "cipher\tsuccess\ticv err\tmic err\tkey err\n"); - - for (i = 0; i < CIPHER_MAX; i++) { - temp += sprintf(temp, "%s\t%lu\t%lu\t%lu\t%lu\n", name[i], - intf->crypto_stats[i].success, - intf->crypto_stats[i].icv_error, - intf->crypto_stats[i].mic_error, - intf->crypto_stats[i].key_error); - } - - size = strlen(data); - size = min(size, length); - - if (copy_to_user(buf, data, size)) { - kfree(data); - return -EFAULT; - } - - kfree(data); - - *offset += size; - return size; -} - -static const struct file_operations rt2x00debug_fop_crypto_stats = { - .owner = THIS_MODULE, - .read = rt2x00debug_read_crypto_stats, - .open = rt2x00debug_file_open, - .release = rt2x00debug_file_release, - .llseek = default_llseek, -}; -#endif - -#define RT2X00DEBUGFS_OPS_READ(__name, __format, __type) \ -static ssize_t rt2x00debug_read_##__name(struct file *file, \ - char __user *buf, \ - size_t length, \ - loff_t *offset) \ -{ \ - struct rt2x00debug_intf *intf = file->private_data; \ - const struct rt2x00debug *debug = intf->debug; \ - char line[16]; \ - size_t size; \ - unsigned int index = intf->offset_##__name; \ - __type value; \ - \ - if (*offset) \ - return 0; \ - \ - if (index >= debug->__name.word_count) \ - return -EINVAL; \ - \ - index += (debug->__name.word_base / \ - debug->__name.word_size); \ - \ - if (debug->__name.flags & RT2X00DEBUGFS_OFFSET) \ - index *= debug->__name.word_size; \ - \ - debug->__name.read(intf->rt2x00dev, index, &value); \ - \ - size = sprintf(line, __format, value); \ - \ - if (copy_to_user(buf, line, size)) \ - return -EFAULT; \ - \ - *offset += size; \ - return size; \ -} - -#define RT2X00DEBUGFS_OPS_WRITE(__name, __type) \ -static ssize_t rt2x00debug_write_##__name(struct file *file, \ - const char __user *buf,\ - size_t length, \ - loff_t *offset) \ -{ \ - struct rt2x00debug_intf *intf = file->private_data; \ - const struct rt2x00debug *debug = intf->debug; \ - char line[16]; \ - size_t size; \ - unsigned int index = intf->offset_##__name; \ - __type value; \ - \ - if (*offset) \ - return 0; \ - \ - if (index >= debug->__name.word_count) \ - return -EINVAL; \ - \ - if (length > sizeof(line)) \ - return -EINVAL; \ - \ - if (copy_from_user(line, buf, length)) \ - return -EFAULT; \ - \ - size = strlen(line); \ - value = simple_strtoul(line, NULL, 0); \ - \ - index += (debug->__name.word_base / \ - debug->__name.word_size); \ - \ - if (debug->__name.flags & RT2X00DEBUGFS_OFFSET) \ - index *= debug->__name.word_size; \ - \ - debug->__name.write(intf->rt2x00dev, index, value); \ - \ - *offset += size; \ - return size; \ -} - -#define RT2X00DEBUGFS_OPS(__name, __format, __type) \ -RT2X00DEBUGFS_OPS_READ(__name, __format, __type); \ -RT2X00DEBUGFS_OPS_WRITE(__name, __type); \ - \ -static const struct file_operations rt2x00debug_fop_##__name = {\ - .owner = THIS_MODULE, \ - .read = rt2x00debug_read_##__name, \ - .write = rt2x00debug_write_##__name, \ - .open = rt2x00debug_file_open, \ - .release = rt2x00debug_file_release, \ - .llseek = generic_file_llseek, \ -}; - -RT2X00DEBUGFS_OPS(csr, "0x%.8x\n", u32); -RT2X00DEBUGFS_OPS(eeprom, "0x%.4x\n", u16); -RT2X00DEBUGFS_OPS(bbp, "0x%.2x\n", u8); -RT2X00DEBUGFS_OPS(rf, "0x%.8x\n", u32); -RT2X00DEBUGFS_OPS(rfcsr, "0x%.2x\n", u8); - -static ssize_t rt2x00debug_read_dev_flags(struct file *file, - char __user *buf, - size_t length, - loff_t *offset) -{ - struct rt2x00debug_intf *intf = file->private_data; - char line[16]; - size_t size; - - if (*offset) - return 0; - - size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->flags); - - if (copy_to_user(buf, line, size)) - return -EFAULT; - - *offset += size; - return size; -} - -static const struct file_operations rt2x00debug_fop_dev_flags = { - .owner = THIS_MODULE, - .read = rt2x00debug_read_dev_flags, - .open = rt2x00debug_file_open, - .release = rt2x00debug_file_release, - .llseek = default_llseek, -}; - -static ssize_t rt2x00debug_read_cap_flags(struct file *file, - char __user *buf, - size_t length, - loff_t *offset) -{ - struct rt2x00debug_intf *intf = file->private_data; - char line[16]; - size_t size; - - if (*offset) - return 0; - - size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->cap_flags); - - if (copy_to_user(buf, line, size)) - return -EFAULT; - - *offset += size; - return size; -} - -static const struct file_operations rt2x00debug_fop_cap_flags = { - .owner = THIS_MODULE, - .read = rt2x00debug_read_cap_flags, - .open = rt2x00debug_file_open, - .release = rt2x00debug_file_release, - .llseek = default_llseek, -}; - -static struct dentry *rt2x00debug_create_file_driver(const char *name, - struct rt2x00debug_intf - *intf, - struct debugfs_blob_wrapper - *blob) -{ - char *data; - - data = kzalloc(3 * MAX_LINE_LENGTH, GFP_KERNEL); - if (!data) - return NULL; - - blob->data = data; - data += sprintf(data, "driver:\t%s\n", intf->rt2x00dev->ops->name); - data += sprintf(data, "version:\t%s\n", DRV_VERSION); - blob->size = strlen(blob->data); - - return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob); -} - -static struct dentry *rt2x00debug_create_file_chipset(const char *name, - struct rt2x00debug_intf - *intf, - struct - debugfs_blob_wrapper - *blob) -{ - const struct rt2x00debug *debug = intf->debug; - char *data; - - data = kzalloc(9 * MAX_LINE_LENGTH, GFP_KERNEL); - if (!data) - return NULL; - - blob->data = data; - data += sprintf(data, "rt chip:\t%04x\n", intf->rt2x00dev->chip.rt); - data += sprintf(data, "rf chip:\t%04x\n", intf->rt2x00dev->chip.rf); - data += sprintf(data, "revision:\t%04x\n", intf->rt2x00dev->chip.rev); - data += sprintf(data, "\n"); - data += sprintf(data, "register\tbase\twords\twordsize\n"); -#define RT2X00DEBUGFS_SPRINTF_REGISTER(__name) \ -{ \ - if(debug->__name.read) \ - data += sprintf(data, __stringify(__name) \ - "\t%d\t%d\t%d\n", \ - debug->__name.word_base, \ - debug->__name.word_count, \ - debug->__name.word_size); \ -} - RT2X00DEBUGFS_SPRINTF_REGISTER(csr); - RT2X00DEBUGFS_SPRINTF_REGISTER(eeprom); - RT2X00DEBUGFS_SPRINTF_REGISTER(bbp); - RT2X00DEBUGFS_SPRINTF_REGISTER(rf); - RT2X00DEBUGFS_SPRINTF_REGISTER(rfcsr); -#undef RT2X00DEBUGFS_SPRINTF_REGISTER - - blob->size = strlen(blob->data); - - return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob); -} - -void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) -{ - const struct rt2x00debug *debug = rt2x00dev->ops->debugfs; - struct rt2x00debug_intf *intf; - - intf = kzalloc(sizeof(struct rt2x00debug_intf), GFP_KERNEL); - if (!intf) { - rt2x00_err(rt2x00dev, "Failed to allocate debug handler\n"); - return; - } - - intf->debug = debug; - intf->rt2x00dev = rt2x00dev; - rt2x00dev->debugfs_intf = intf; - - intf->driver_folder = - debugfs_create_dir(intf->rt2x00dev->ops->name, - rt2x00dev->hw->wiphy->debugfsdir); - if (IS_ERR(intf->driver_folder) || !intf->driver_folder) - goto exit; - - intf->driver_entry = - rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob); - if (IS_ERR(intf->driver_entry) || !intf->driver_entry) - goto exit; - - intf->chipset_entry = - rt2x00debug_create_file_chipset("chipset", - intf, &intf->chipset_blob); - if (IS_ERR(intf->chipset_entry) || !intf->chipset_entry) - goto exit; - - intf->dev_flags = debugfs_create_file("dev_flags", S_IRUSR, - intf->driver_folder, intf, - &rt2x00debug_fop_dev_flags); - if (IS_ERR(intf->dev_flags) || !intf->dev_flags) - goto exit; - - intf->cap_flags = debugfs_create_file("cap_flags", S_IRUSR, - intf->driver_folder, intf, - &rt2x00debug_fop_cap_flags); - if (IS_ERR(intf->cap_flags) || !intf->cap_flags) - goto exit; - - intf->register_folder = - debugfs_create_dir("register", intf->driver_folder); - if (IS_ERR(intf->register_folder) || !intf->register_folder) - goto exit; - -#define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \ -({ \ - if(debug->__name.read) { \ - (__intf)->__name##_off_entry = \ - debugfs_create_u32(__stringify(__name) "_offset", \ - S_IRUSR | S_IWUSR, \ - (__intf)->register_folder, \ - &(__intf)->offset_##__name); \ - if (IS_ERR((__intf)->__name##_off_entry) \ - || !(__intf)->__name##_off_entry) \ - goto exit; \ - \ - (__intf)->__name##_val_entry = \ - debugfs_create_file(__stringify(__name) "_value", \ - S_IRUSR | S_IWUSR, \ - (__intf)->register_folder, \ - (__intf), &rt2x00debug_fop_##__name); \ - if (IS_ERR((__intf)->__name##_val_entry) \ - || !(__intf)->__name##_val_entry) \ - goto exit; \ - } \ -}) - - RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, csr); - RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, eeprom); - RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, bbp); - RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rf); - RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rfcsr); - -#undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY - - intf->queue_folder = - debugfs_create_dir("queue", intf->driver_folder); - if (IS_ERR(intf->queue_folder) || !intf->queue_folder) - goto exit; - - intf->queue_frame_dump_entry = - debugfs_create_file("dump", S_IRUSR, intf->queue_folder, - intf, &rt2x00debug_fop_queue_dump); - if (IS_ERR(intf->queue_frame_dump_entry) - || !intf->queue_frame_dump_entry) - goto exit; - - skb_queue_head_init(&intf->frame_dump_skbqueue); - init_waitqueue_head(&intf->frame_dump_waitqueue); - - intf->queue_stats_entry = - debugfs_create_file("queue", S_IRUSR, intf->queue_folder, - intf, &rt2x00debug_fop_queue_stats); - -#ifdef CONFIG_RT2X00_LIB_CRYPTO - if (rt2x00_has_cap_hw_crypto(rt2x00dev)) - intf->crypto_stats_entry = - debugfs_create_file("crypto", S_IRUGO, intf->queue_folder, - intf, &rt2x00debug_fop_crypto_stats); -#endif - - return; - -exit: - rt2x00debug_deregister(rt2x00dev); - rt2x00_err(rt2x00dev, "Failed to register debug handler\n"); -} - -void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) -{ - struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; - - if (unlikely(!intf)) - return; - - skb_queue_purge(&intf->frame_dump_skbqueue); - -#ifdef CONFIG_RT2X00_LIB_CRYPTO - debugfs_remove(intf->crypto_stats_entry); -#endif - debugfs_remove(intf->queue_stats_entry); - debugfs_remove(intf->queue_frame_dump_entry); - debugfs_remove(intf->queue_folder); - debugfs_remove(intf->rfcsr_val_entry); - debugfs_remove(intf->rfcsr_off_entry); - debugfs_remove(intf->rf_val_entry); - debugfs_remove(intf->rf_off_entry); - debugfs_remove(intf->bbp_val_entry); - debugfs_remove(intf->bbp_off_entry); - debugfs_remove(intf->eeprom_val_entry); - debugfs_remove(intf->eeprom_off_entry); - debugfs_remove(intf->csr_val_entry); - debugfs_remove(intf->csr_off_entry); - debugfs_remove(intf->register_folder); - debugfs_remove(intf->dev_flags); - debugfs_remove(intf->cap_flags); - debugfs_remove(intf->chipset_entry); - debugfs_remove(intf->driver_entry); - debugfs_remove(intf->driver_folder); - kfree(intf->chipset_blob.data); - kfree(intf->driver_blob.data); - kfree(intf); - - rt2x00dev->debugfs_intf = NULL; -} diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.h b/drivers/net/wireless/rt2x00/rt2x00debug.h deleted file mode 100644 index e65712c235bd..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00debug.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00debug - Abstract: Data structures for the rt2x00debug. - */ - -#ifndef RT2X00DEBUG_H -#define RT2X00DEBUG_H - -struct rt2x00_dev; - -/** - * enum rt2x00debugfs_entry_flags: Flags for debugfs registry entry - * - * @RT2X00DEBUGFS_OFFSET: rt2x00lib should pass the register offset - * as argument when using the callback function read()/write() - */ -enum rt2x00debugfs_entry_flags { - RT2X00DEBUGFS_OFFSET = (1 << 0), -}; - -#define RT2X00DEBUGFS_REGISTER_ENTRY(__name, __type) \ -struct reg##__name { \ - void (*read)(struct rt2x00_dev *rt2x00dev, \ - const unsigned int word, __type *data); \ - void (*write)(struct rt2x00_dev *rt2x00dev, \ - const unsigned int word, __type data); \ - \ - unsigned int flags; \ - \ - unsigned int word_base; \ - unsigned int word_size; \ - unsigned int word_count; \ -} __name - -struct rt2x00debug { - /* - * Reference to the modules structure. - */ - struct module *owner; - - /* - * Register access entries. - */ - RT2X00DEBUGFS_REGISTER_ENTRY(csr, u32); - RT2X00DEBUGFS_REGISTER_ENTRY(eeprom, u16); - RT2X00DEBUGFS_REGISTER_ENTRY(bbp, u8); - RT2X00DEBUGFS_REGISTER_ENTRY(rf, u32); - RT2X00DEBUGFS_REGISTER_ENTRY(rfcsr, u8); -}; - -#endif /* RT2X00DEBUG_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c deleted file mode 100644 index 5639ed816813..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ /dev/null @@ -1,1549 +0,0 @@ -/* - Copyright (C) 2010 Willow Garage - Copyright (C) 2004 - 2010 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00lib - Abstract: rt2x00 generic device routines. - */ - -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00lib.h" - -/* - * Utility functions. - */ -u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev, - struct ieee80211_vif *vif) -{ - /* - * When in STA mode, bssidx is always 0 otherwise local_address[5] - * contains the bss number, see BSS_ID_MASK comments for details. - */ - if (rt2x00dev->intf_sta_count) - return 0; - return vif->addr[5] & (rt2x00dev->ops->max_ap_intf - 1); -} -EXPORT_SYMBOL_GPL(rt2x00lib_get_bssidx); - -/* - * Radio control handlers. - */ -int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev) -{ - int status; - - /* - * Don't enable the radio twice. - * And check if the hardware button has been disabled. - */ - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return 0; - - /* - * Initialize all data queues. - */ - rt2x00queue_init_queues(rt2x00dev); - - /* - * Enable radio. - */ - status = - rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_ON); - if (status) - return status; - - rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_ON); - - rt2x00leds_led_radio(rt2x00dev, true); - rt2x00led_led_activity(rt2x00dev, true); - - set_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags); - - /* - * Enable queues. - */ - rt2x00queue_start_queues(rt2x00dev); - rt2x00link_start_tuner(rt2x00dev); - rt2x00link_start_agc(rt2x00dev); - if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) - rt2x00link_start_vcocal(rt2x00dev); - - /* - * Start watchdog monitoring. - */ - rt2x00link_start_watchdog(rt2x00dev); - - return 0; -} - -void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - if (!test_and_clear_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return; - - /* - * Stop watchdog monitoring. - */ - rt2x00link_stop_watchdog(rt2x00dev); - - /* - * Stop all queues - */ - rt2x00link_stop_agc(rt2x00dev); - if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) - rt2x00link_stop_vcocal(rt2x00dev); - rt2x00link_stop_tuner(rt2x00dev); - rt2x00queue_stop_queues(rt2x00dev); - rt2x00queue_flush_queues(rt2x00dev, true); - - /* - * Disable radio. - */ - rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_OFF); - rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_OFF); - rt2x00led_led_activity(rt2x00dev, false); - rt2x00leds_led_radio(rt2x00dev, false); -} - -static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct rt2x00_dev *rt2x00dev = data; - struct rt2x00_intf *intf = vif_to_intf(vif); - - /* - * It is possible the radio was disabled while the work had been - * scheduled. If that happens we should return here immediately, - * note that in the spinlock protected area above the delayed_flags - * have been cleared correctly. - */ - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return; - - if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags)) { - mutex_lock(&intf->beacon_skb_mutex); - rt2x00queue_update_beacon(rt2x00dev, vif); - mutex_unlock(&intf->beacon_skb_mutex); - } -} - -static void rt2x00lib_intf_scheduled(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, intf_work); - - /* - * Iterate over each interface and perform the - * requested configurations. - */ - ieee80211_iterate_active_interfaces(rt2x00dev->hw, - IEEE80211_IFACE_ITER_RESUME_ALL, - rt2x00lib_intf_scheduled_iter, - rt2x00dev); -} - -static void rt2x00lib_autowakeup(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, autowakeup_work.work); - - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return; - - if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) - rt2x00_err(rt2x00dev, "Device failed to wakeup\n"); - clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); -} - -/* - * Interrupt context handlers. - */ -static void rt2x00lib_bc_buffer_iter(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct ieee80211_tx_control control = {}; - struct rt2x00_dev *rt2x00dev = data; - struct sk_buff *skb; - - /* - * Only AP mode interfaces do broad- and multicast buffering - */ - if (vif->type != NL80211_IFTYPE_AP) - return; - - /* - * Send out buffered broad- and multicast frames - */ - skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); - while (skb) { - rt2x00mac_tx(rt2x00dev->hw, &control, skb); - skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); - } -} - -static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct rt2x00_dev *rt2x00dev = data; - - if (vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_ADHOC && - vif->type != NL80211_IFTYPE_MESH_POINT && - vif->type != NL80211_IFTYPE_WDS) - return; - - /* - * Update the beacon without locking. This is safe on PCI devices - * as they only update the beacon periodically here. This should - * never be called for USB devices. - */ - WARN_ON(rt2x00_is_usb(rt2x00dev)); - rt2x00queue_update_beacon(rt2x00dev, vif); -} - -void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) -{ - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return; - - /* send buffered bc/mc frames out for every bssid */ - ieee80211_iterate_active_interfaces_atomic( - rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - rt2x00lib_bc_buffer_iter, rt2x00dev); - /* - * Devices with pre tbtt interrupt don't need to update the beacon - * here as they will fetch the next beacon directly prior to - * transmission. - */ - if (rt2x00_has_cap_pre_tbtt_interrupt(rt2x00dev)) - return; - - /* fetch next beacon */ - ieee80211_iterate_active_interfaces_atomic( - rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - rt2x00lib_beaconupdate_iter, rt2x00dev); -} -EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); - -void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev) -{ - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return; - - /* fetch next beacon */ - ieee80211_iterate_active_interfaces_atomic( - rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - rt2x00lib_beaconupdate_iter, rt2x00dev); -} -EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt); - -void rt2x00lib_dmastart(struct queue_entry *entry) -{ - set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); - rt2x00queue_index_inc(entry, Q_INDEX); -} -EXPORT_SYMBOL_GPL(rt2x00lib_dmastart); - -void rt2x00lib_dmadone(struct queue_entry *entry) -{ - set_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags); - clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); - rt2x00queue_index_inc(entry, Q_INDEX_DMA_DONE); -} -EXPORT_SYMBOL_GPL(rt2x00lib_dmadone); - -static inline int rt2x00lib_txdone_bar_status(struct queue_entry *entry) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct ieee80211_bar *bar = (void *) entry->skb->data; - struct rt2x00_bar_list_entry *bar_entry; - int ret; - - if (likely(!ieee80211_is_back_req(bar->frame_control))) - return 0; - - /* - * Unlike all other frames, the status report for BARs does - * not directly come from the hardware as it is incapable of - * matching a BA to a previously send BAR. The hardware will - * report all BARs as if they weren't acked at all. - * - * Instead the RX-path will scan for incoming BAs and set the - * block_acked flag if it sees one that was likely caused by - * a BAR from us. - * - * Remove remaining BARs here and return their status for - * TX done processing. - */ - ret = 0; - rcu_read_lock(); - list_for_each_entry_rcu(bar_entry, &rt2x00dev->bar_list, list) { - if (bar_entry->entry != entry) - continue; - - spin_lock_bh(&rt2x00dev->bar_list_lock); - /* Return whether this BAR was blockacked or not */ - ret = bar_entry->block_acked; - /* Remove the BAR from our checklist */ - list_del_rcu(&bar_entry->list); - spin_unlock_bh(&rt2x00dev->bar_list_lock); - kfree_rcu(bar_entry, head); - - break; - } - rcu_read_unlock(); - - return ret; -} - -void rt2x00lib_txdone(struct queue_entry *entry, - struct txdone_entry_desc *txdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - unsigned int header_length, i; - u8 rate_idx, rate_flags, retry_rates; - u8 skbdesc_flags = skbdesc->flags; - bool success; - - /* - * Unmap the skb. - */ - rt2x00queue_unmap_skb(entry); - - /* - * Remove the extra tx headroom from the skb. - */ - skb_pull(entry->skb, rt2x00dev->extra_tx_headroom); - - /* - * Signal that the TX descriptor is no longer in the skb. - */ - skbdesc->flags &= ~SKBDESC_DESC_IN_SKB; - - /* - * Determine the length of 802.11 header. - */ - header_length = ieee80211_get_hdrlen_from_skb(entry->skb); - - /* - * Remove L2 padding which was added during - */ - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) - rt2x00queue_remove_l2pad(entry->skb, header_length); - - /* - * If the IV/EIV data was stripped from the frame before it was - * passed to the hardware, we should now reinsert it again because - * mac80211 will expect the same data to be present it the - * frame as it was passed to us. - */ - if (rt2x00_has_cap_hw_crypto(rt2x00dev)) - rt2x00crypto_tx_insert_iv(entry->skb, header_length); - - /* - * Send frame to debugfs immediately, after this call is completed - * we are going to overwrite the skb->cb array. - */ - rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry->skb); - - /* - * Determine if the frame has been successfully transmitted and - * remove BARs from our check list while checking for their - * TX status. - */ - success = - rt2x00lib_txdone_bar_status(entry) || - test_bit(TXDONE_SUCCESS, &txdesc->flags) || - test_bit(TXDONE_UNKNOWN, &txdesc->flags); - - /* - * Update TX statistics. - */ - rt2x00dev->link.qual.tx_success += success; - rt2x00dev->link.qual.tx_failed += !success; - - rate_idx = skbdesc->tx_rate_idx; - rate_flags = skbdesc->tx_rate_flags; - retry_rates = test_bit(TXDONE_FALLBACK, &txdesc->flags) ? - (txdesc->retry + 1) : 1; - - /* - * Initialize TX status - */ - memset(&tx_info->status, 0, sizeof(tx_info->status)); - tx_info->status.ack_signal = 0; - - /* - * Frame was send with retries, hardware tried - * different rates to send out the frame, at each - * retry it lowered the rate 1 step except when the - * lowest rate was used. - */ - for (i = 0; i < retry_rates && i < IEEE80211_TX_MAX_RATES; i++) { - tx_info->status.rates[i].idx = rate_idx - i; - tx_info->status.rates[i].flags = rate_flags; - - if (rate_idx - i == 0) { - /* - * The lowest rate (index 0) was used until the - * number of max retries was reached. - */ - tx_info->status.rates[i].count = retry_rates - i; - i++; - break; - } - tx_info->status.rates[i].count = 1; - } - if (i < (IEEE80211_TX_MAX_RATES - 1)) - tx_info->status.rates[i].idx = -1; /* terminate */ - - if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) { - if (success) - tx_info->flags |= IEEE80211_TX_STAT_ACK; - else - rt2x00dev->low_level_stats.dot11ACKFailureCount++; - } - - /* - * Every single frame has it's own tx status, hence report - * every frame as ampdu of size 1. - * - * TODO: if we can find out how many frames were aggregated - * by the hw we could provide the real ampdu_len to mac80211 - * which would allow the rc algorithm to better decide on - * which rates are suitable. - */ - if (test_bit(TXDONE_AMPDU, &txdesc->flags) || - tx_info->flags & IEEE80211_TX_CTL_AMPDU) { - tx_info->flags |= IEEE80211_TX_STAT_AMPDU; - tx_info->status.ampdu_len = 1; - tx_info->status.ampdu_ack_len = success ? 1 : 0; - - if (!success) - tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; - } - - if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) { - if (success) - rt2x00dev->low_level_stats.dot11RTSSuccessCount++; - else - rt2x00dev->low_level_stats.dot11RTSFailureCount++; - } - - /* - * Only send the status report to mac80211 when it's a frame - * that originated in mac80211. If this was a extra frame coming - * through a mac80211 library call (RTS/CTS) then we should not - * send the status report back. - */ - if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) { - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TASKLET_CONTEXT)) - ieee80211_tx_status(rt2x00dev->hw, entry->skb); - else - ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb); - } else - dev_kfree_skb_any(entry->skb); - - /* - * Make this entry available for reuse. - */ - entry->skb = NULL; - entry->flags = 0; - - rt2x00dev->ops->lib->clear_entry(entry); - - rt2x00queue_index_inc(entry, Q_INDEX_DONE); - - /* - * If the data queue was below the threshold before the txdone - * handler we must make sure the packet queue in the mac80211 stack - * is reenabled when the txdone handler has finished. This has to be - * serialized with rt2x00mac_tx(), otherwise we can wake up queue - * before it was stopped. - */ - spin_lock_bh(&entry->queue->tx_lock); - if (!rt2x00queue_threshold(entry->queue)) - rt2x00queue_unpause_queue(entry->queue); - spin_unlock_bh(&entry->queue->tx_lock); -} -EXPORT_SYMBOL_GPL(rt2x00lib_txdone); - -void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status) -{ - struct txdone_entry_desc txdesc; - - txdesc.flags = 0; - __set_bit(status, &txdesc.flags); - txdesc.retry = 0; - - rt2x00lib_txdone(entry, &txdesc); -} -EXPORT_SYMBOL_GPL(rt2x00lib_txdone_noinfo); - -static u8 *rt2x00lib_find_ie(u8 *data, unsigned int len, u8 ie) -{ - struct ieee80211_mgmt *mgmt = (void *)data; - u8 *pos, *end; - - pos = (u8 *)mgmt->u.beacon.variable; - end = data + len; - while (pos < end) { - if (pos + 2 + pos[1] > end) - return NULL; - - if (pos[0] == ie) - return pos; - - pos += 2 + pos[1]; - } - - return NULL; -} - -static void rt2x00lib_sleep(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, sleep_work); - - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return; - - /* - * Check again is powersaving is enabled, to prevent races from delayed - * work execution. - */ - if (!test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) - rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf, - IEEE80211_CONF_CHANGE_PS); -} - -static void rt2x00lib_rxdone_check_ba(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct rxdone_entry_desc *rxdesc) -{ - struct rt2x00_bar_list_entry *entry; - struct ieee80211_bar *ba = (void *)skb->data; - - if (likely(!ieee80211_is_back(ba->frame_control))) - return; - - if (rxdesc->size < sizeof(*ba) + FCS_LEN) - return; - - rcu_read_lock(); - list_for_each_entry_rcu(entry, &rt2x00dev->bar_list, list) { - - if (ba->start_seq_num != entry->start_seq_num) - continue; - -#define TID_CHECK(a, b) ( \ - ((a) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK)) == \ - ((b) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK))) \ - - if (!TID_CHECK(ba->control, entry->control)) - continue; - -#undef TID_CHECK - - if (!ether_addr_equal_64bits(ba->ra, entry->ta)) - continue; - - if (!ether_addr_equal_64bits(ba->ta, entry->ra)) - continue; - - /* Mark BAR since we received the according BA */ - spin_lock_bh(&rt2x00dev->bar_list_lock); - entry->block_acked = 1; - spin_unlock_bh(&rt2x00dev->bar_list_lock); - break; - } - rcu_read_unlock(); - -} - -static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct rxdone_entry_desc *rxdesc) -{ - struct ieee80211_hdr *hdr = (void *) skb->data; - struct ieee80211_tim_ie *tim_ie; - u8 *tim; - u8 tim_len; - bool cam; - - /* If this is not a beacon, or if mac80211 has no powersaving - * configured, or if the device is already in powersaving mode - * we can exit now. */ - if (likely(!ieee80211_is_beacon(hdr->frame_control) || - !(rt2x00dev->hw->conf.flags & IEEE80211_CONF_PS))) - return; - - /* min. beacon length + FCS_LEN */ - if (skb->len <= 40 + FCS_LEN) - return; - - /* and only beacons from the associated BSSID, please */ - if (!(rxdesc->dev_flags & RXDONE_MY_BSS) || - !rt2x00dev->aid) - return; - - rt2x00dev->last_beacon = jiffies; - - tim = rt2x00lib_find_ie(skb->data, skb->len - FCS_LEN, WLAN_EID_TIM); - if (!tim) - return; - - if (tim[1] < sizeof(*tim_ie)) - return; - - tim_len = tim[1]; - tim_ie = (struct ieee80211_tim_ie *) &tim[2]; - - /* Check whenever the PHY can be turned off again. */ - - /* 1. What about buffered unicast traffic for our AID? */ - cam = ieee80211_check_tim(tim_ie, tim_len, rt2x00dev->aid); - - /* 2. Maybe the AP wants to send multicast/broadcast data? */ - cam |= (tim_ie->bitmap_ctrl & 0x01); - - if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) - queue_work(rt2x00dev->workqueue, &rt2x00dev->sleep_work); -} - -static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev, - struct rxdone_entry_desc *rxdesc) -{ - struct ieee80211_supported_band *sband; - const struct rt2x00_rate *rate; - unsigned int i; - int signal = rxdesc->signal; - int type = (rxdesc->dev_flags & RXDONE_SIGNAL_MASK); - - switch (rxdesc->rate_mode) { - case RATE_MODE_CCK: - case RATE_MODE_OFDM: - /* - * For non-HT rates the MCS value needs to contain the - * actually used rate modulation (CCK or OFDM). - */ - if (rxdesc->dev_flags & RXDONE_SIGNAL_MCS) - signal = RATE_MCS(rxdesc->rate_mode, signal); - - sband = &rt2x00dev->bands[rt2x00dev->curr_band]; - for (i = 0; i < sband->n_bitrates; i++) { - rate = rt2x00_get_rate(sband->bitrates[i].hw_value); - if (((type == RXDONE_SIGNAL_PLCP) && - (rate->plcp == signal)) || - ((type == RXDONE_SIGNAL_BITRATE) && - (rate->bitrate == signal)) || - ((type == RXDONE_SIGNAL_MCS) && - (rate->mcs == signal))) { - return i; - } - } - break; - case RATE_MODE_HT_MIX: - case RATE_MODE_HT_GREENFIELD: - if (signal >= 0 && signal <= 76) - return signal; - break; - default: - break; - } - - rt2x00_warn(rt2x00dev, "Frame received with unrecognized signal, mode=0x%.4x, signal=0x%.4x, type=%d\n", - rxdesc->rate_mode, signal, type); - return 0; -} - -void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct rxdone_entry_desc rxdesc; - struct sk_buff *skb; - struct ieee80211_rx_status *rx_status; - unsigned int header_length; - int rate_idx; - - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || - !test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - goto submit_entry; - - if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) - goto submit_entry; - - /* - * Allocate a new sk_buffer. If no new buffer available, drop the - * received frame and reuse the existing buffer. - */ - skb = rt2x00queue_alloc_rxskb(entry, gfp); - if (!skb) - goto submit_entry; - - /* - * Unmap the skb. - */ - rt2x00queue_unmap_skb(entry); - - /* - * Extract the RXD details. - */ - memset(&rxdesc, 0, sizeof(rxdesc)); - rt2x00dev->ops->lib->fill_rxdone(entry, &rxdesc); - - /* - * Check for valid size in case we get corrupted descriptor from - * hardware. - */ - if (unlikely(rxdesc.size == 0 || - rxdesc.size > entry->queue->data_size)) { - rt2x00_err(rt2x00dev, "Wrong frame size %d max %d\n", - rxdesc.size, entry->queue->data_size); - dev_kfree_skb(entry->skb); - goto renew_skb; - } - - /* - * The data behind the ieee80211 header must be - * aligned on a 4 byte boundary. - */ - header_length = ieee80211_get_hdrlen_from_skb(entry->skb); - - /* - * Hardware might have stripped the IV/EIV/ICV data, - * in that case it is possible that the data was - * provided separately (through hardware descriptor) - * in which case we should reinsert the data into the frame. - */ - if ((rxdesc.dev_flags & RXDONE_CRYPTO_IV) && - (rxdesc.flags & RX_FLAG_IV_STRIPPED)) - rt2x00crypto_rx_insert_iv(entry->skb, header_length, - &rxdesc); - else if (header_length && - (rxdesc.size > header_length) && - (rxdesc.dev_flags & RXDONE_L2PAD)) - rt2x00queue_remove_l2pad(entry->skb, header_length); - - /* Trim buffer to correct size */ - skb_trim(entry->skb, rxdesc.size); - - /* - * Translate the signal to the correct bitrate index. - */ - rate_idx = rt2x00lib_rxdone_read_signal(rt2x00dev, &rxdesc); - if (rxdesc.rate_mode == RATE_MODE_HT_MIX || - rxdesc.rate_mode == RATE_MODE_HT_GREENFIELD) - rxdesc.flags |= RX_FLAG_HT; - - /* - * Check if this is a beacon, and more frames have been - * buffered while we were in powersaving mode. - */ - rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc); - - /* - * Check for incoming BlockAcks to match to the BlockAckReqs - * we've send out. - */ - rt2x00lib_rxdone_check_ba(rt2x00dev, entry->skb, &rxdesc); - - /* - * Update extra components - */ - rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc); - rt2x00debug_update_crypto(rt2x00dev, &rxdesc); - rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb); - - /* - * Initialize RX status information, and send frame - * to mac80211. - */ - rx_status = IEEE80211_SKB_RXCB(entry->skb); - - /* Ensure that all fields of rx_status are initialized - * properly. The skb->cb array was used for driver - * specific informations, so rx_status might contain - * garbage. - */ - memset(rx_status, 0, sizeof(*rx_status)); - - rx_status->mactime = rxdesc.timestamp; - rx_status->band = rt2x00dev->curr_band; - rx_status->freq = rt2x00dev->curr_freq; - rx_status->rate_idx = rate_idx; - rx_status->signal = rxdesc.rssi; - rx_status->flag = rxdesc.flags; - rx_status->antenna = rt2x00dev->link.ant.active.rx; - - ieee80211_rx_ni(rt2x00dev->hw, entry->skb); - -renew_skb: - /* - * Replace the skb with the freshly allocated one. - */ - entry->skb = skb; - -submit_entry: - entry->flags = 0; - rt2x00queue_index_inc(entry, Q_INDEX_DONE); - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && - test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2x00dev->ops->lib->clear_entry(entry); -} -EXPORT_SYMBOL_GPL(rt2x00lib_rxdone); - -/* - * Driver initialization handlers. - */ -const struct rt2x00_rate rt2x00_supported_rates[12] = { - { - .flags = DEV_RATE_CCK, - .bitrate = 10, - .ratemask = BIT(0), - .plcp = 0x00, - .mcs = RATE_MCS(RATE_MODE_CCK, 0), - }, - { - .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, - .bitrate = 20, - .ratemask = BIT(1), - .plcp = 0x01, - .mcs = RATE_MCS(RATE_MODE_CCK, 1), - }, - { - .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, - .bitrate = 55, - .ratemask = BIT(2), - .plcp = 0x02, - .mcs = RATE_MCS(RATE_MODE_CCK, 2), - }, - { - .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, - .bitrate = 110, - .ratemask = BIT(3), - .plcp = 0x03, - .mcs = RATE_MCS(RATE_MODE_CCK, 3), - }, - { - .flags = DEV_RATE_OFDM, - .bitrate = 60, - .ratemask = BIT(4), - .plcp = 0x0b, - .mcs = RATE_MCS(RATE_MODE_OFDM, 0), - }, - { - .flags = DEV_RATE_OFDM, - .bitrate = 90, - .ratemask = BIT(5), - .plcp = 0x0f, - .mcs = RATE_MCS(RATE_MODE_OFDM, 1), - }, - { - .flags = DEV_RATE_OFDM, - .bitrate = 120, - .ratemask = BIT(6), - .plcp = 0x0a, - .mcs = RATE_MCS(RATE_MODE_OFDM, 2), - }, - { - .flags = DEV_RATE_OFDM, - .bitrate = 180, - .ratemask = BIT(7), - .plcp = 0x0e, - .mcs = RATE_MCS(RATE_MODE_OFDM, 3), - }, - { - .flags = DEV_RATE_OFDM, - .bitrate = 240, - .ratemask = BIT(8), - .plcp = 0x09, - .mcs = RATE_MCS(RATE_MODE_OFDM, 4), - }, - { - .flags = DEV_RATE_OFDM, - .bitrate = 360, - .ratemask = BIT(9), - .plcp = 0x0d, - .mcs = RATE_MCS(RATE_MODE_OFDM, 5), - }, - { - .flags = DEV_RATE_OFDM, - .bitrate = 480, - .ratemask = BIT(10), - .plcp = 0x08, - .mcs = RATE_MCS(RATE_MODE_OFDM, 6), - }, - { - .flags = DEV_RATE_OFDM, - .bitrate = 540, - .ratemask = BIT(11), - .plcp = 0x0c, - .mcs = RATE_MCS(RATE_MODE_OFDM, 7), - }, -}; - -static void rt2x00lib_channel(struct ieee80211_channel *entry, - const int channel, const int tx_power, - const int value) -{ - /* XXX: this assumption about the band is wrong for 802.11j */ - entry->band = channel <= 14 ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - entry->center_freq = ieee80211_channel_to_frequency(channel, - entry->band); - entry->hw_value = value; - entry->max_power = tx_power; - entry->max_antenna_gain = 0xff; -} - -static void rt2x00lib_rate(struct ieee80211_rate *entry, - const u16 index, const struct rt2x00_rate *rate) -{ - entry->flags = 0; - entry->bitrate = rate->bitrate; - entry->hw_value = index; - entry->hw_value_short = index; - - if (rate->flags & DEV_RATE_SHORT_PREAMBLE) - entry->flags |= IEEE80211_RATE_SHORT_PREAMBLE; -} - -static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, - struct hw_mode_spec *spec) -{ - struct ieee80211_hw *hw = rt2x00dev->hw; - struct ieee80211_channel *channels; - struct ieee80211_rate *rates; - unsigned int num_rates; - unsigned int i; - - num_rates = 0; - if (spec->supported_rates & SUPPORT_RATE_CCK) - num_rates += 4; - if (spec->supported_rates & SUPPORT_RATE_OFDM) - num_rates += 8; - - channels = kcalloc(spec->num_channels, sizeof(*channels), GFP_KERNEL); - if (!channels) - return -ENOMEM; - - rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL); - if (!rates) - goto exit_free_channels; - - /* - * Initialize Rate list. - */ - for (i = 0; i < num_rates; i++) - rt2x00lib_rate(&rates[i], i, rt2x00_get_rate(i)); - - /* - * Initialize Channel list. - */ - for (i = 0; i < spec->num_channels; i++) { - rt2x00lib_channel(&channels[i], - spec->channels[i].channel, - spec->channels_info[i].max_power, i); - } - - /* - * Intitialize 802.11b, 802.11g - * Rates: CCK, OFDM. - * Channels: 2.4 GHz - */ - if (spec->supported_bands & SUPPORT_BAND_2GHZ) { - rt2x00dev->bands[IEEE80211_BAND_2GHZ].n_channels = 14; - rt2x00dev->bands[IEEE80211_BAND_2GHZ].n_bitrates = num_rates; - rt2x00dev->bands[IEEE80211_BAND_2GHZ].channels = channels; - rt2x00dev->bands[IEEE80211_BAND_2GHZ].bitrates = rates; - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &rt2x00dev->bands[IEEE80211_BAND_2GHZ]; - memcpy(&rt2x00dev->bands[IEEE80211_BAND_2GHZ].ht_cap, - &spec->ht, sizeof(spec->ht)); - } - - /* - * Intitialize 802.11a - * Rates: OFDM. - * Channels: OFDM, UNII, HiperLAN2. - */ - if (spec->supported_bands & SUPPORT_BAND_5GHZ) { - rt2x00dev->bands[IEEE80211_BAND_5GHZ].n_channels = - spec->num_channels - 14; - rt2x00dev->bands[IEEE80211_BAND_5GHZ].n_bitrates = - num_rates - 4; - rt2x00dev->bands[IEEE80211_BAND_5GHZ].channels = &channels[14]; - rt2x00dev->bands[IEEE80211_BAND_5GHZ].bitrates = &rates[4]; - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &rt2x00dev->bands[IEEE80211_BAND_5GHZ]; - memcpy(&rt2x00dev->bands[IEEE80211_BAND_5GHZ].ht_cap, - &spec->ht, sizeof(spec->ht)); - } - - return 0; - - exit_free_channels: - kfree(channels); - rt2x00_err(rt2x00dev, "Allocation ieee80211 modes failed\n"); - return -ENOMEM; -} - -static void rt2x00lib_remove_hw(struct rt2x00_dev *rt2x00dev) -{ - if (test_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags)) - ieee80211_unregister_hw(rt2x00dev->hw); - - if (likely(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ])) { - kfree(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels); - kfree(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->bitrates); - rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; - rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; - } - - kfree(rt2x00dev->spec.channels_info); -} - -static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) -{ - struct hw_mode_spec *spec = &rt2x00dev->spec; - int status; - - if (test_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags)) - return 0; - - /* - * Initialize HW modes. - */ - status = rt2x00lib_probe_hw_modes(rt2x00dev, spec); - if (status) - return status; - - /* - * Initialize HW fields. - */ - rt2x00dev->hw->queues = rt2x00dev->ops->tx_queues; - - /* - * Initialize extra TX headroom required. - */ - rt2x00dev->hw->extra_tx_headroom = - max_t(unsigned int, IEEE80211_TX_STATUS_HEADROOM, - rt2x00dev->extra_tx_headroom); - - /* - * Take TX headroom required for alignment into account. - */ - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) - rt2x00dev->hw->extra_tx_headroom += RT2X00_L2PAD_SIZE; - else if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA)) - rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE; - - /* - * Tell mac80211 about the size of our private STA structure. - */ - rt2x00dev->hw->sta_data_size = sizeof(struct rt2x00_sta); - - /* - * Allocate tx status FIFO for driver use. - */ - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TXSTATUS_FIFO)) { - /* - * Allocate the txstatus fifo. In the worst case the tx - * status fifo has to hold the tx status of all entries - * in all tx queues. Hence, calculate the kfifo size as - * tx_queues * entry_num and round up to the nearest - * power of 2. - */ - int kfifo_size = - roundup_pow_of_two(rt2x00dev->ops->tx_queues * - rt2x00dev->tx->limit * - sizeof(u32)); - - status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size, - GFP_KERNEL); - if (status) - return status; - } - - /* - * Initialize tasklets if used by the driver. Tasklets are - * disabled until the interrupts are turned on. The driver - * has to handle that. - */ -#define RT2X00_TASKLET_INIT(taskletname) \ - if (rt2x00dev->ops->lib->taskletname) { \ - tasklet_init(&rt2x00dev->taskletname, \ - rt2x00dev->ops->lib->taskletname, \ - (unsigned long)rt2x00dev); \ - } - - RT2X00_TASKLET_INIT(txstatus_tasklet); - RT2X00_TASKLET_INIT(pretbtt_tasklet); - RT2X00_TASKLET_INIT(tbtt_tasklet); - RT2X00_TASKLET_INIT(rxdone_tasklet); - RT2X00_TASKLET_INIT(autowake_tasklet); - -#undef RT2X00_TASKLET_INIT - - /* - * Register HW. - */ - status = ieee80211_register_hw(rt2x00dev->hw); - if (status) - return status; - - set_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags); - - return 0; -} - -/* - * Initialization/uninitialization handlers. - */ -static void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev) -{ - if (!test_and_clear_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags)) - return; - - /* - * Stop rfkill polling. - */ - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) - rt2x00rfkill_unregister(rt2x00dev); - - /* - * Allow the HW to uninitialize. - */ - rt2x00dev->ops->lib->uninitialize(rt2x00dev); - - /* - * Free allocated queue entries. - */ - rt2x00queue_uninitialize(rt2x00dev); -} - -static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) -{ - int status; - - if (test_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags)) - return 0; - - /* - * Allocate all queue entries. - */ - status = rt2x00queue_initialize(rt2x00dev); - if (status) - return status; - - /* - * Initialize the device. - */ - status = rt2x00dev->ops->lib->initialize(rt2x00dev); - if (status) { - rt2x00queue_uninitialize(rt2x00dev); - return status; - } - - set_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags); - - /* - * Start rfkill polling. - */ - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) - rt2x00rfkill_register(rt2x00dev); - - return 0; -} - -int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) -{ - int retval; - - if (test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) - return 0; - - /* - * If this is the first interface which is added, - * we should load the firmware now. - */ - retval = rt2x00lib_load_firmware(rt2x00dev); - if (retval) - return retval; - - /* - * Initialize the device. - */ - retval = rt2x00lib_initialize(rt2x00dev); - if (retval) - return retval; - - rt2x00dev->intf_ap_count = 0; - rt2x00dev->intf_sta_count = 0; - rt2x00dev->intf_associated = 0; - - /* Enable the radio */ - retval = rt2x00lib_enable_radio(rt2x00dev); - if (retval) - return retval; - - set_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags); - - return 0; -} - -void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev) -{ - if (!test_and_clear_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) - return; - - /* - * Perhaps we can add something smarter here, - * but for now just disabling the radio should do. - */ - rt2x00lib_disable_radio(rt2x00dev); - - rt2x00dev->intf_ap_count = 0; - rt2x00dev->intf_sta_count = 0; - rt2x00dev->intf_associated = 0; -} - -static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev) -{ - struct ieee80211_iface_limit *if_limit; - struct ieee80211_iface_combination *if_combination; - - if (rt2x00dev->ops->max_ap_intf < 2) - return; - - /* - * Build up AP interface limits structure. - */ - if_limit = &rt2x00dev->if_limits_ap; - if_limit->max = rt2x00dev->ops->max_ap_intf; - if_limit->types = BIT(NL80211_IFTYPE_AP); -#ifdef CONFIG_MAC80211_MESH - if_limit->types |= BIT(NL80211_IFTYPE_MESH_POINT); -#endif - - /* - * Build up AP interface combinations structure. - */ - if_combination = &rt2x00dev->if_combinations[IF_COMB_AP]; - if_combination->limits = if_limit; - if_combination->n_limits = 1; - if_combination->max_interfaces = if_limit->max; - if_combination->num_different_channels = 1; - - /* - * Finally, specify the possible combinations to mac80211. - */ - rt2x00dev->hw->wiphy->iface_combinations = rt2x00dev->if_combinations; - rt2x00dev->hw->wiphy->n_iface_combinations = 1; -} - -static unsigned int rt2x00dev_extra_tx_headroom(struct rt2x00_dev *rt2x00dev) -{ - if (WARN_ON(!rt2x00dev->tx)) - return 0; - - if (rt2x00_is_usb(rt2x00dev)) - return rt2x00dev->tx[0].winfo_size + rt2x00dev->tx[0].desc_size; - - return rt2x00dev->tx[0].winfo_size; -} - -/* - * driver allocation handlers. - */ -int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) -{ - int retval = -ENOMEM; - - /* - * Set possible interface combinations. - */ - rt2x00lib_set_if_combinations(rt2x00dev); - - /* - * Allocate the driver data memory, if necessary. - */ - if (rt2x00dev->ops->drv_data_size > 0) { - rt2x00dev->drv_data = kzalloc(rt2x00dev->ops->drv_data_size, - GFP_KERNEL); - if (!rt2x00dev->drv_data) { - retval = -ENOMEM; - goto exit; - } - } - - spin_lock_init(&rt2x00dev->irqmask_lock); - mutex_init(&rt2x00dev->csr_mutex); - INIT_LIST_HEAD(&rt2x00dev->bar_list); - spin_lock_init(&rt2x00dev->bar_list_lock); - - set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); - - /* - * Make room for rt2x00_intf inside the per-interface - * structure ieee80211_vif. - */ - rt2x00dev->hw->vif_data_size = sizeof(struct rt2x00_intf); - - /* - * rt2x00 devices can only use the last n bits of the MAC address - * for virtual interfaces. - */ - rt2x00dev->hw->wiphy->addr_mask[ETH_ALEN - 1] = - (rt2x00dev->ops->max_ap_intf - 1); - - /* - * Initialize work. - */ - rt2x00dev->workqueue = - alloc_ordered_workqueue("%s", 0, wiphy_name(rt2x00dev->hw->wiphy)); - if (!rt2x00dev->workqueue) { - retval = -ENOMEM; - goto exit; - } - - INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); - INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup); - INIT_WORK(&rt2x00dev->sleep_work, rt2x00lib_sleep); - - /* - * Let the driver probe the device to detect the capabilities. - */ - retval = rt2x00dev->ops->lib->probe_hw(rt2x00dev); - if (retval) { - rt2x00_err(rt2x00dev, "Failed to allocate device\n"); - goto exit; - } - - /* - * Allocate queue array. - */ - retval = rt2x00queue_allocate(rt2x00dev); - if (retval) - goto exit; - - /* Cache TX headroom value */ - rt2x00dev->extra_tx_headroom = rt2x00dev_extra_tx_headroom(rt2x00dev); - - /* - * Determine which operating modes are supported, all modes - * which require beaconing, depend on the availability of - * beacon entries. - */ - rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - if (rt2x00dev->bcn->limit > 0) - rt2x00dev->hw->wiphy->interface_modes |= - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | -#ifdef CONFIG_MAC80211_MESH - BIT(NL80211_IFTYPE_MESH_POINT) | -#endif - BIT(NL80211_IFTYPE_WDS); - - rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - - /* - * Initialize ieee80211 structure. - */ - retval = rt2x00lib_probe_hw(rt2x00dev); - if (retval) { - rt2x00_err(rt2x00dev, "Failed to initialize hw\n"); - goto exit; - } - - /* - * Register extra components. - */ - rt2x00link_register(rt2x00dev); - rt2x00leds_register(rt2x00dev); - rt2x00debug_register(rt2x00dev); - - /* - * Start rfkill polling. - */ - if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) - rt2x00rfkill_register(rt2x00dev); - - return 0; - -exit: - rt2x00lib_remove_dev(rt2x00dev); - - return retval; -} -EXPORT_SYMBOL_GPL(rt2x00lib_probe_dev); - -void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) -{ - clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); - - /* - * Stop rfkill polling. - */ - if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) - rt2x00rfkill_unregister(rt2x00dev); - - /* - * Disable radio. - */ - rt2x00lib_disable_radio(rt2x00dev); - - /* - * Stop all work. - */ - cancel_work_sync(&rt2x00dev->intf_work); - cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); - cancel_work_sync(&rt2x00dev->sleep_work); - if (rt2x00_is_usb(rt2x00dev)) { - hrtimer_cancel(&rt2x00dev->txstatus_timer); - cancel_work_sync(&rt2x00dev->rxdone_work); - cancel_work_sync(&rt2x00dev->txdone_work); - } - if (rt2x00dev->workqueue) - destroy_workqueue(rt2x00dev->workqueue); - - /* - * Free the tx status fifo. - */ - kfifo_free(&rt2x00dev->txstatus_fifo); - - /* - * Kill the tx status tasklet. - */ - tasklet_kill(&rt2x00dev->txstatus_tasklet); - tasklet_kill(&rt2x00dev->pretbtt_tasklet); - tasklet_kill(&rt2x00dev->tbtt_tasklet); - tasklet_kill(&rt2x00dev->rxdone_tasklet); - tasklet_kill(&rt2x00dev->autowake_tasklet); - - /* - * Uninitialize device. - */ - rt2x00lib_uninitialize(rt2x00dev); - - /* - * Free extra components - */ - rt2x00debug_deregister(rt2x00dev); - rt2x00leds_unregister(rt2x00dev); - - /* - * Free ieee80211_hw memory. - */ - rt2x00lib_remove_hw(rt2x00dev); - - /* - * Free firmware image. - */ - rt2x00lib_free_firmware(rt2x00dev); - - /* - * Free queue structures. - */ - rt2x00queue_free(rt2x00dev); - - /* - * Free the driver data. - */ - kfree(rt2x00dev->drv_data); -} -EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev); - -/* - * Device state handlers - */ -#ifdef CONFIG_PM -int rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev, pm_message_t state) -{ - rt2x00_dbg(rt2x00dev, "Going to sleep\n"); - - /* - * Prevent mac80211 from accessing driver while suspended. - */ - if (!test_and_clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return 0; - - /* - * Cleanup as much as possible. - */ - rt2x00lib_uninitialize(rt2x00dev); - - /* - * Suspend/disable extra components. - */ - rt2x00leds_suspend(rt2x00dev); - rt2x00debug_deregister(rt2x00dev); - - /* - * Set device mode to sleep for power management, - * on some hardware this call seems to consistently fail. - * From the specifications it is hard to tell why it fails, - * and if this is a "bad thing". - * Overall it is safe to just ignore the failure and - * continue suspending. The only downside is that the - * device will not be in optimal power save mode, but with - * the radio and the other components already disabled the - * device is as good as disabled. - */ - if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_SLEEP)) - rt2x00_warn(rt2x00dev, "Device failed to enter sleep state, continue suspending\n"); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00lib_suspend); - -int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev) -{ - rt2x00_dbg(rt2x00dev, "Waking up\n"); - - /* - * Restore/enable extra components. - */ - rt2x00debug_register(rt2x00dev); - rt2x00leds_resume(rt2x00dev); - - /* - * We are ready again to receive requests from mac80211. - */ - set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00lib_resume); -#endif /* CONFIG_PM */ - -/* - * rt2x00lib module information. - */ -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("rt2x00 library"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2x00dump.h b/drivers/net/wireless/rt2x00/rt2x00dump.h deleted file mode 100644 index 4c0e01b5d515..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00dump.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00dump - Abstract: - Data structures for the rt2x00debug & userspace. - - The declarations in this file can be used by both rt2x00 - and userspace and therefore should be kept together in - this file. - */ - -#ifndef RT2X00DUMP_H -#define RT2X00DUMP_H - -/** - * DOC: Introduction - * - * This header is intended to be exported to userspace, - * to make the structures and enumerations available to userspace - * applications. This means that all data types should be exportable. - * - * When rt2x00 is compiled with debugfs support enabled, - * it is possible to capture all data coming in and out of the device - * by reading the frame dump file. This file can have only a single reader. - * The following frames will be reported: - * - All incoming frames (rx) - * - All outgoing frames (tx, including beacon and atim) - * - All completed frames (txdone including atim) - * - * The data is send to the file using the following format: - * - * [rt2x00dump header][hardware descriptor][ieee802.11 frame] - * - * rt2x00dump header: The description of the dumped frame, as well as - * additional information useful for debugging. See &rt2x00dump_hdr. - * hardware descriptor: Descriptor that was used to receive or transmit - * the frame. - * ieee802.11 frame: The actual frame that was received or transmitted. - */ - -/** - * enum rt2x00_dump_type - Frame type - * - * These values are used for the @type member of &rt2x00dump_hdr. - * @DUMP_FRAME_RXDONE: This frame has been received by the hardware. - * @DUMP_FRAME_TX: This frame is queued for transmission to the hardware. - * @DUMP_FRAME_TXDONE: This frame indicates the device has handled - * the tx event which has either succeeded or failed. A frame - * with this type should also have been reported with as a - * %DUMP_FRAME_TX frame. - * @DUMP_FRAME_BEACON: This beacon frame is queued for transmission to the - * hardware. - */ -enum rt2x00_dump_type { - DUMP_FRAME_RXDONE = 1, - DUMP_FRAME_TX = 2, - DUMP_FRAME_TXDONE = 3, - DUMP_FRAME_BEACON = 4, -}; - -/** - * struct rt2x00dump_hdr - Dump frame header - * - * Each frame dumped to the debugfs file starts with this header - * attached. This header contains the description of the actual - * frame which was dumped. - * - * New fields inside the structure must be appended to the end of - * the structure. This way userspace tools compiled for earlier - * header versions can still correctly handle the frame dump - * (although they will not handle all data passed to them in the dump). - * - * @version: Header version should always be set to %DUMP_HEADER_VERSION. - * This field must be checked by userspace to determine if it can - * handle this frame. - * @header_length: The length of the &rt2x00dump_hdr structure. This is - * used for compatibility reasons so userspace can easily determine - * the location of the next field in the dump. - * @desc_length: The length of the device descriptor. - * @data_length: The length of the frame data (including the ieee802.11 header. - * @chip_rt: RT chipset - * @chip_rf: RF chipset - * @chip_rev: Chipset revision - * @type: The frame type (&rt2x00_dump_type) - * @queue_index: The index number of the data queue. - * @entry_index: The index number of the entry inside the data queue. - * @timestamp_sec: Timestamp - seconds - * @timestamp_usec: Timestamp - microseconds - */ -struct rt2x00dump_hdr { - __le32 version; -#define DUMP_HEADER_VERSION 2 - - __le32 header_length; - __le32 desc_length; - __le32 data_length; - - __le16 chip_rt; - __le16 chip_rf; - __le16 chip_rev; - - __le16 type; - __u8 queue_index; - __u8 entry_index; - - __le32 timestamp_sec; - __le32 timestamp_usec; -}; - -#endif /* RT2X00DUMP_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00firmware.c b/drivers/net/wireless/rt2x00/rt2x00firmware.c deleted file mode 100644 index 5813300f68a2..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00firmware.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - Copyright (C) 2004 - 2009 Gertjan van Wingerde - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00lib - Abstract: rt2x00 firmware loading routines. - */ - -#include -#include - -#include "rt2x00.h" -#include "rt2x00lib.h" - -static int rt2x00lib_request_firmware(struct rt2x00_dev *rt2x00dev) -{ - struct device *device = wiphy_dev(rt2x00dev->hw->wiphy); - const struct firmware *fw; - char *fw_name; - int retval; - - /* - * Read correct firmware from harddisk. - */ - fw_name = rt2x00dev->ops->lib->get_firmware_name(rt2x00dev); - if (!fw_name) { - rt2x00_err(rt2x00dev, - "Invalid firmware filename\n" - "Please file bug report to %s\n", DRV_PROJECT); - return -EINVAL; - } - - rt2x00_info(rt2x00dev, "Loading firmware file '%s'\n", fw_name); - - retval = request_firmware(&fw, fw_name, device); - if (retval) { - rt2x00_err(rt2x00dev, "Failed to request Firmware\n"); - return retval; - } - - if (!fw || !fw->size || !fw->data) { - rt2x00_err(rt2x00dev, "Failed to read Firmware\n"); - release_firmware(fw); - return -ENOENT; - } - - rt2x00_info(rt2x00dev, "Firmware detected - version: %d.%d\n", - fw->data[fw->size - 4], fw->data[fw->size - 3]); - snprintf(rt2x00dev->hw->wiphy->fw_version, - sizeof(rt2x00dev->hw->wiphy->fw_version), "%d.%d", - fw->data[fw->size - 4], fw->data[fw->size - 3]); - - retval = rt2x00dev->ops->lib->check_firmware(rt2x00dev, fw->data, fw->size); - switch (retval) { - case FW_OK: - break; - case FW_BAD_CRC: - rt2x00_err(rt2x00dev, "Firmware checksum error\n"); - goto exit; - case FW_BAD_LENGTH: - rt2x00_err(rt2x00dev, "Invalid firmware file length (len=%zu)\n", - fw->size); - goto exit; - case FW_BAD_VERSION: - rt2x00_err(rt2x00dev, "Current firmware does not support detected chipset\n"); - goto exit; - } - - rt2x00dev->fw = fw; - - return 0; - -exit: - release_firmware(fw); - - return -ENOENT; -} - -int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) -{ - int retval; - - if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_FIRMWARE)) - return 0; - - if (!rt2x00dev->fw) { - retval = rt2x00lib_request_firmware(rt2x00dev); - if (retval) - return retval; - } - - /* - * Send firmware to the device. - */ - retval = rt2x00dev->ops->lib->load_firmware(rt2x00dev, - rt2x00dev->fw->data, - rt2x00dev->fw->size); - - /* - * When the firmware is uploaded to the hardware the LED - * association status might have been triggered, for correct - * LED handling it should now be reset. - */ - rt2x00leds_led_assoc(rt2x00dev, false); - - return retval; -} - -void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev) -{ - release_firmware(rt2x00dev->fw); - rt2x00dev->fw = NULL; -} diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.c b/drivers/net/wireless/rt2x00/rt2x00leds.c deleted file mode 100644 index c681d04b506c..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00leds.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00lib - Abstract: rt2x00 led specific routines. - */ - -#include -#include - -#include "rt2x00.h" -#include "rt2x00lib.h" - -void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi) -{ - struct rt2x00_led *led = &rt2x00dev->led_qual; - unsigned int brightness; - - if ((led->type != LED_TYPE_QUALITY) || !(led->flags & LED_REGISTERED)) - return; - - /* - * Led handling requires a positive value for the rssi, - * to do that correctly we need to add the correction. - */ - rssi += rt2x00dev->rssi_offset; - - /* - * Get the rssi level, this is used to convert the rssi - * to a LED value inside the range LED_OFF - LED_FULL. - */ - if (rssi <= 30) - rssi = 0; - else if (rssi <= 39) - rssi = 1; - else if (rssi <= 49) - rssi = 2; - else if (rssi <= 53) - rssi = 3; - else if (rssi <= 63) - rssi = 4; - else - rssi = 5; - - /* - * Note that we must _not_ send LED_OFF since the driver - * is going to calculate the value and might use it in a - * division. - */ - brightness = ((LED_FULL / 6) * rssi) + 1; - if (brightness != led->led_dev.brightness) { - led->led_dev.brightness_set(&led->led_dev, brightness); - led->led_dev.brightness = brightness; - } -} - -static void rt2x00led_led_simple(struct rt2x00_led *led, bool enabled) -{ - unsigned int brightness = enabled ? LED_FULL : LED_OFF; - - if (!(led->flags & LED_REGISTERED)) - return; - - led->led_dev.brightness_set(&led->led_dev, brightness); - led->led_dev.brightness = brightness; -} - -void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled) -{ - if (rt2x00dev->led_qual.type == LED_TYPE_ACTIVITY) - rt2x00led_led_simple(&rt2x00dev->led_qual, enabled); -} - -void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled) -{ - if (rt2x00dev->led_assoc.type == LED_TYPE_ASSOC) - rt2x00led_led_simple(&rt2x00dev->led_assoc, enabled); -} - -void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled) -{ - if (rt2x00dev->led_radio.type == LED_TYPE_RADIO) - rt2x00led_led_simple(&rt2x00dev->led_radio, enabled); -} - -static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev, - struct rt2x00_led *led, - const char *name) -{ - struct device *device = wiphy_dev(rt2x00dev->hw->wiphy); - int retval; - - led->led_dev.name = name; - led->led_dev.brightness = LED_OFF; - - retval = led_classdev_register(device, &led->led_dev); - if (retval) { - rt2x00_err(rt2x00dev, "Failed to register led handler\n"); - return retval; - } - - led->flags |= LED_REGISTERED; - - return 0; -} - -void rt2x00leds_register(struct rt2x00_dev *rt2x00dev) -{ - char name[36]; - int retval; - unsigned long on_period; - unsigned long off_period; - const char *phy_name = wiphy_name(rt2x00dev->hw->wiphy); - - if (rt2x00dev->led_radio.flags & LED_INITIALIZED) { - snprintf(name, sizeof(name), "%s-%s::radio", - rt2x00dev->ops->name, phy_name); - - retval = rt2x00leds_register_led(rt2x00dev, - &rt2x00dev->led_radio, - name); - if (retval) - goto exit_fail; - } - - if (rt2x00dev->led_assoc.flags & LED_INITIALIZED) { - snprintf(name, sizeof(name), "%s-%s::assoc", - rt2x00dev->ops->name, phy_name); - - retval = rt2x00leds_register_led(rt2x00dev, - &rt2x00dev->led_assoc, - name); - if (retval) - goto exit_fail; - } - - if (rt2x00dev->led_qual.flags & LED_INITIALIZED) { - snprintf(name, sizeof(name), "%s-%s::quality", - rt2x00dev->ops->name, phy_name); - - retval = rt2x00leds_register_led(rt2x00dev, - &rt2x00dev->led_qual, - name); - if (retval) - goto exit_fail; - } - - /* - * Initialize blink time to default value: - * On period: 70ms - * Off period: 30ms - */ - if (rt2x00dev->led_radio.led_dev.blink_set) { - on_period = 70; - off_period = 30; - rt2x00dev->led_radio.led_dev.blink_set( - &rt2x00dev->led_radio.led_dev, &on_period, &off_period); - } - - return; - -exit_fail: - rt2x00leds_unregister(rt2x00dev); -} - -static void rt2x00leds_unregister_led(struct rt2x00_led *led) -{ - led_classdev_unregister(&led->led_dev); - - /* - * This might look weird, but when we are unregistering while - * suspended the led is already off, and since we haven't - * fully resumed yet, access to the device might not be - * possible yet. - */ - if (!(led->led_dev.flags & LED_SUSPENDED)) - led->led_dev.brightness_set(&led->led_dev, LED_OFF); - - led->flags &= ~LED_REGISTERED; -} - -void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev) -{ - if (rt2x00dev->led_qual.flags & LED_REGISTERED) - rt2x00leds_unregister_led(&rt2x00dev->led_qual); - if (rt2x00dev->led_assoc.flags & LED_REGISTERED) - rt2x00leds_unregister_led(&rt2x00dev->led_assoc); - if (rt2x00dev->led_radio.flags & LED_REGISTERED) - rt2x00leds_unregister_led(&rt2x00dev->led_radio); -} - -static inline void rt2x00leds_suspend_led(struct rt2x00_led *led) -{ - led_classdev_suspend(&led->led_dev); - - /* This shouldn't be needed, but just to be safe */ - led->led_dev.brightness_set(&led->led_dev, LED_OFF); - led->led_dev.brightness = LED_OFF; -} - -void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev) -{ - if (rt2x00dev->led_qual.flags & LED_REGISTERED) - rt2x00leds_suspend_led(&rt2x00dev->led_qual); - if (rt2x00dev->led_assoc.flags & LED_REGISTERED) - rt2x00leds_suspend_led(&rt2x00dev->led_assoc); - if (rt2x00dev->led_radio.flags & LED_REGISTERED) - rt2x00leds_suspend_led(&rt2x00dev->led_radio); -} - -static inline void rt2x00leds_resume_led(struct rt2x00_led *led) -{ - led_classdev_resume(&led->led_dev); - - /* Device might have enabled the LEDS during resume */ - led->led_dev.brightness_set(&led->led_dev, LED_OFF); - led->led_dev.brightness = LED_OFF; -} - -void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev) -{ - if (rt2x00dev->led_radio.flags & LED_REGISTERED) - rt2x00leds_resume_led(&rt2x00dev->led_radio); - if (rt2x00dev->led_assoc.flags & LED_REGISTERED) - rt2x00leds_resume_led(&rt2x00dev->led_assoc); - if (rt2x00dev->led_qual.flags & LED_REGISTERED) - rt2x00leds_resume_led(&rt2x00dev->led_qual); -} diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.h b/drivers/net/wireless/rt2x00/rt2x00leds.h deleted file mode 100644 index b2c5269570da..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00leds.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00lib - Abstract: rt2x00 led datastructures and routines - */ - -#ifndef RT2X00LEDS_H -#define RT2X00LEDS_H - -enum led_type { - LED_TYPE_RADIO, - LED_TYPE_ASSOC, - LED_TYPE_ACTIVITY, - LED_TYPE_QUALITY, -}; - -struct rt2x00_led { - struct rt2x00_dev *rt2x00dev; - struct led_classdev led_dev; - - enum led_type type; - unsigned int flags; -#define LED_INITIALIZED ( 1 << 0 ) -#define LED_REGISTERED ( 1 << 1 ) -}; - -#endif /* RT2X00LEDS_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h deleted file mode 100644 index fb7c349ccc9c..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00lib.h +++ /dev/null @@ -1,468 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - Copyright (C) 2004 - 2009 Gertjan van Wingerde - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00lib - Abstract: Data structures and definitions for the rt2x00lib module. - */ - -#ifndef RT2X00LIB_H -#define RT2X00LIB_H - -/* - * Interval defines - */ -#define WATCHDOG_INTERVAL round_jiffies_relative(HZ) -#define LINK_TUNE_INTERVAL round_jiffies_relative(HZ) -#define AGC_INTERVAL round_jiffies_relative(4 * HZ) -#define VCO_INTERVAL round_jiffies_relative(10 * HZ) /* 10 sec */ - -/* - * rt2x00_rate: Per rate device information - */ -struct rt2x00_rate { - unsigned short flags; -#define DEV_RATE_CCK 0x0001 -#define DEV_RATE_OFDM 0x0002 -#define DEV_RATE_SHORT_PREAMBLE 0x0004 - - unsigned short bitrate; /* In 100kbit/s */ - unsigned short ratemask; - - unsigned short plcp; - unsigned short mcs; -}; - -extern const struct rt2x00_rate rt2x00_supported_rates[12]; - -static inline const struct rt2x00_rate *rt2x00_get_rate(const u16 hw_value) -{ - return &rt2x00_supported_rates[hw_value & 0xff]; -} - -#define RATE_MCS(__mode, __mcs) \ - ((((__mode) & 0x00ff) << 8) | ((__mcs) & 0x00ff)) - -static inline int rt2x00_get_rate_mcs(const u16 mcs_value) -{ - return (mcs_value & 0x00ff); -} - -/* - * Radio control handlers. - */ -int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev); -void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev); - -/* - * Initialization handlers. - */ -int rt2x00lib_start(struct rt2x00_dev *rt2x00dev); -void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev); - -/* - * Configuration handlers. - */ -void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev, - struct rt2x00_intf *intf, - enum nl80211_iftype type, - const u8 *mac, const u8 *bssid); -void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, - struct rt2x00_intf *intf, - struct ieee80211_bss_conf *conf, - u32 changed); -void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, - struct antenna_setup ant); -void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, - const unsigned int changed_flags); - -/** - * DOC: Queue handlers - */ - -/** - * rt2x00queue_alloc_rxskb - allocate a skb for RX purposes. - * @entry: The entry for which the skb will be applicable. - */ -struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp); - -/** - * rt2x00queue_free_skb - free a skb - * @entry: The entry for which the skb will be applicable. - */ -void rt2x00queue_free_skb(struct queue_entry *entry); - -/** - * rt2x00queue_align_frame - Align 802.11 frame to 4-byte boundary - * @skb: The skb to align - * - * Align the start of the 802.11 frame to a 4-byte boundary, this could - * mean the payload is not aligned properly though. - */ -void rt2x00queue_align_frame(struct sk_buff *skb); - -/** - * rt2x00queue_insert_l2pad - Align 802.11 header & payload to 4-byte boundary - * @skb: The skb to align - * @header_length: Length of 802.11 header - * - * Apply L2 padding to align both header and payload to 4-byte boundary - */ -void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length); - -/** - * rt2x00queue_insert_l2pad - Remove L2 padding from 802.11 frame - * @skb: The skb to align - * @header_length: Length of 802.11 header - * - * Remove L2 padding used to align both header and payload to 4-byte boundary, - * by removing the L2 padding the header will no longer be 4-byte aligned. - */ -void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length); - -/** - * rt2x00queue_write_tx_frame - Write TX frame to hardware - * @queue: Queue over which the frame should be send - * @skb: The skb to send - * @local: frame is not from mac80211 - */ -int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, - struct ieee80211_sta *sta, bool local); - -/** - * rt2x00queue_update_beacon - Send new beacon from mac80211 - * to hardware. Handles locking by itself (mutex). - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * @vif: Interface for which the beacon should be updated. - */ -int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, - struct ieee80211_vif *vif); - -/** - * rt2x00queue_update_beacon_locked - Send new beacon from mac80211 - * to hardware. Caller needs to ensure locking. - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * @vif: Interface for which the beacon should be updated. - */ -int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev, - struct ieee80211_vif *vif); - -/** - * rt2x00queue_clear_beacon - Clear beacon in hardware - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * @vif: Interface for which the beacon should be updated. - */ -int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev, - struct ieee80211_vif *vif); - -/** - * rt2x00queue_index_inc - Index incrementation function - * @entry: Queue entry (&struct queue_entry) to perform the action on. - * @index: Index type (&enum queue_index) to perform the action on. - * - * This function will increase the requested index on the entry's queue, - * it will grab the appropriate locks and handle queue overflow events by - * resetting the index to the start of the queue. - */ -void rt2x00queue_index_inc(struct queue_entry *entry, enum queue_index index); - -/** - * rt2x00queue_init_queues - Initialize all data queues - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * - * This function will loop through all available queues to clear all - * index numbers and set the queue entry to the correct initialization - * state. - */ -void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev); - -int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev); -void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev); -int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev); -void rt2x00queue_free(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00link_update_stats - Update link statistics from RX frame - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * @skb: Received frame - * @rxdesc: Received frame descriptor - * - * Update link statistics based on the information from the - * received frame descriptor. - */ -void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct rxdone_entry_desc *rxdesc); - -/** - * rt2x00link_start_tuner - Start periodic link tuner work - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * - * This start the link tuner periodic work, this work will - * be executed periodically until &rt2x00link_stop_tuner has - * been called. - */ -void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00link_stop_tuner - Stop periodic link tuner work - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * - * After this function completed the link tuner will not - * be running until &rt2x00link_start_tuner is called. - */ -void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00link_reset_tuner - Reset periodic link tuner work - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * @antenna: Should the antenna tuning also be reset - * - * The VGC limit configured in the hardware will be reset to 0 - * which forces the driver to rediscover the correct value for - * the current association. This is needed when configuration - * options have changed which could drastically change the - * SNR level or link quality (i.e. changing the antenna setting). - * - * Resetting the link tuner will also cause the periodic work counter - * to be reset. Any driver which has a fixed limit on the number - * of rounds the link tuner is supposed to work will accept the - * tuner actions again if this limit was previously reached. - * - * If @antenna is set to true a the software antenna diversity - * tuning will also be reset. - */ -void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna); - -/** - * rt2x00link_start_watchdog - Start periodic watchdog monitoring - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * - * This start the watchdog periodic work, this work will - *be executed periodically until &rt2x00link_stop_watchdog has - * been called. - */ -void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00link_stop_watchdog - Stop periodic watchdog monitoring - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * - * After this function completed the watchdog monitoring will not - * be running until &rt2x00link_start_watchdog is called. - */ -void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00link_start_agc - Start periodic gain calibration - * @rt2x00dev: Pointer to &struct rt2x00_dev. - */ -void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00link_start_vcocal - Start periodic VCO calibration - * @rt2x00dev: Pointer to &struct rt2x00_dev. - */ -void rt2x00link_start_vcocal(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00link_stop_agc - Stop periodic gain calibration - * @rt2x00dev: Pointer to &struct rt2x00_dev. - */ -void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00link_stop_vcocal - Stop periodic VCO calibration - * @rt2x00dev: Pointer to &struct rt2x00_dev. - */ -void rt2x00link_stop_vcocal(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00link_register - Initialize link tuning & watchdog functionality - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * - * Initialize work structure and all link tuning and watchdog related - * parameters. This will not start the periodic work itself. - */ -void rt2x00link_register(struct rt2x00_dev *rt2x00dev); - -/* - * Firmware handlers. - */ -#ifdef CONFIG_RT2X00_LIB_FIRMWARE -int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev); -void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev); -#else -static inline int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) -{ - return 0; -} -static inline void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev) -{ -} -#endif /* CONFIG_RT2X00_LIB_FIRMWARE */ - -/* - * Debugfs handlers. - */ -#ifdef CONFIG_RT2X00_LIB_DEBUGFS -void rt2x00debug_register(struct rt2x00_dev *rt2x00dev); -void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev); -void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, - struct rxdone_entry_desc *rxdesc); -#else -static inline void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) -{ -} - -static inline void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) -{ -} - -static inline void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, - struct rxdone_entry_desc *rxdesc) -{ -} -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ - -/* - * Crypto handlers. - */ -#ifdef CONFIG_RT2X00_LIB_CRYPTO -enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key); -void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txentry_desc *txdesc); -unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb); -void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, - struct txentry_desc *txdesc); -void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, - struct txentry_desc *txdesc); -void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length); -void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, - unsigned int header_length, - struct rxdone_entry_desc *rxdesc); -#else -static inline enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) -{ - return CIPHER_NONE; -} - -static inline void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txentry_desc *txdesc) -{ -} - -static inline unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb) -{ - return 0; -} - -static inline void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, - struct txentry_desc *txdesc) -{ -} - -static inline void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, - struct txentry_desc *txdesc) -{ -} - -static inline void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, - unsigned int header_length) -{ -} - -static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, - unsigned int header_length, - struct rxdone_entry_desc *rxdesc) -{ -} -#endif /* CONFIG_RT2X00_LIB_CRYPTO */ - -/* - * RFkill handlers. - */ -static inline void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev) -{ - if (test_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags)) - wiphy_rfkill_start_polling(rt2x00dev->hw->wiphy); -} - -static inline void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev) -{ - if (test_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags)) - wiphy_rfkill_stop_polling(rt2x00dev->hw->wiphy); -} - -/* - * LED handlers - */ -#ifdef CONFIG_RT2X00_LIB_LEDS -void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi); -void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled); -void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled); -void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled); -void rt2x00leds_register(struct rt2x00_dev *rt2x00dev); -void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev); -void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev); -void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev); -#else -static inline void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, - int rssi) -{ -} - -static inline void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, - bool enabled) -{ -} - -static inline void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, - bool enabled) -{ -} - -static inline void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, - bool enabled) -{ -} - -static inline void rt2x00leds_register(struct rt2x00_dev *rt2x00dev) -{ -} - -static inline void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev) -{ -} - -static inline void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev) -{ -} - -static inline void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev) -{ -} -#endif /* CONFIG_RT2X00_LIB_LEDS */ - -#endif /* RT2X00LIB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c deleted file mode 100644 index 017188e5a736..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00link.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00lib - Abstract: rt2x00 generic link tuning routines. - */ - -#include -#include - -#include "rt2x00.h" -#include "rt2x00lib.h" - -/* - * When we lack RSSI information return something less then -80 to - * tell the driver to tune the device to maximum sensitivity. - */ -#define DEFAULT_RSSI -128 - -static inline int rt2x00link_get_avg_rssi(struct ewma_rssi *ewma) -{ - unsigned long avg; - - avg = ewma_rssi_read(ewma); - if (avg) - return -avg; - - return DEFAULT_RSSI; -} - -static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev) -{ - struct link_ant *ant = &rt2x00dev->link.ant; - - if (rt2x00dev->link.qual.rx_success) - return rt2x00link_get_avg_rssi(&ant->rssi_ant); - - return DEFAULT_RSSI; -} - -static int rt2x00link_antenna_get_rssi_history(struct rt2x00_dev *rt2x00dev) -{ - struct link_ant *ant = &rt2x00dev->link.ant; - - if (ant->rssi_history) - return ant->rssi_history; - return DEFAULT_RSSI; -} - -static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev, - int rssi) -{ - struct link_ant *ant = &rt2x00dev->link.ant; - ant->rssi_history = rssi; -} - -static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev) -{ - ewma_rssi_init(&rt2x00dev->link.ant.rssi_ant); -} - -static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev) -{ - struct link_ant *ant = &rt2x00dev->link.ant; - struct antenna_setup new_ant; - int other_antenna; - - int sample_current = rt2x00link_antenna_get_link_rssi(rt2x00dev); - int sample_other = rt2x00link_antenna_get_rssi_history(rt2x00dev); - - memcpy(&new_ant, &ant->active, sizeof(new_ant)); - - /* - * We are done sampling. Now we should evaluate the results. - */ - ant->flags &= ~ANTENNA_MODE_SAMPLE; - - /* - * During the last period we have sampled the RSSI - * from both antennas. It now is time to determine - * which antenna demonstrated the best performance. - * When we are already on the antenna with the best - * performance, just create a good starting point - * for the history and we are done. - */ - if (sample_current >= sample_other) { - rt2x00link_antenna_update_rssi_history(rt2x00dev, - sample_current); - return; - } - - other_antenna = (ant->active.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; - - if (ant->flags & ANTENNA_RX_DIVERSITY) - new_ant.rx = other_antenna; - - if (ant->flags & ANTENNA_TX_DIVERSITY) - new_ant.tx = other_antenna; - - rt2x00lib_config_antenna(rt2x00dev, new_ant); -} - -static void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev) -{ - struct link_ant *ant = &rt2x00dev->link.ant; - struct antenna_setup new_ant; - int rssi_curr; - int rssi_old; - - memcpy(&new_ant, &ant->active, sizeof(new_ant)); - - /* - * Get current RSSI value along with the historical value, - * after that update the history with the current value. - */ - rssi_curr = rt2x00link_antenna_get_link_rssi(rt2x00dev); - rssi_old = rt2x00link_antenna_get_rssi_history(rt2x00dev); - rt2x00link_antenna_update_rssi_history(rt2x00dev, rssi_curr); - - /* - * Legacy driver indicates that we should swap antenna's - * when the difference in RSSI is greater that 5. This - * also should be done when the RSSI was actually better - * then the previous sample. - * When the difference exceeds the threshold we should - * sample the rssi from the other antenna to make a valid - * comparison between the 2 antennas. - */ - if (abs(rssi_curr - rssi_old) < 5) - return; - - ant->flags |= ANTENNA_MODE_SAMPLE; - - if (ant->flags & ANTENNA_RX_DIVERSITY) - new_ant.rx = (new_ant.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; - - if (ant->flags & ANTENNA_TX_DIVERSITY) - new_ant.tx = (new_ant.tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; - - rt2x00lib_config_antenna(rt2x00dev, new_ant); -} - -static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev) -{ - struct link_ant *ant = &rt2x00dev->link.ant; - - /* - * Determine if software diversity is enabled for - * either the TX or RX antenna (or both). - */ - if (!(ant->flags & ANTENNA_RX_DIVERSITY) && - !(ant->flags & ANTENNA_TX_DIVERSITY)) { - ant->flags = 0; - return true; - } - - /* - * If we have only sampled the data over the last period - * we should now harvest the data. Otherwise just evaluate - * the data. The latter should only be performed once - * every 2 seconds. - */ - if (ant->flags & ANTENNA_MODE_SAMPLE) { - rt2x00lib_antenna_diversity_sample(rt2x00dev); - return true; - } else if (rt2x00dev->link.count & 1) { - rt2x00lib_antenna_diversity_eval(rt2x00dev); - return true; - } - - return false; -} - -void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct rxdone_entry_desc *rxdesc) -{ - struct link *link = &rt2x00dev->link; - struct link_qual *qual = &rt2x00dev->link.qual; - struct link_ant *ant = &rt2x00dev->link.ant; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - - /* - * No need to update the stats for !=STA interfaces - */ - if (!rt2x00dev->intf_sta_count) - return; - - /* - * Frame was received successfully since non-succesfull - * frames would have been dropped by the hardware. - */ - qual->rx_success++; - - /* - * We are only interested in quality statistics from - * beacons which came from the BSS which we are - * associated with. - */ - if (!ieee80211_is_beacon(hdr->frame_control) || - !(rxdesc->dev_flags & RXDONE_MY_BSS)) - return; - - /* - * Update global RSSI - */ - ewma_rssi_add(&link->avg_rssi, -rxdesc->rssi); - - /* - * Update antenna RSSI - */ - ewma_rssi_add(&ant->rssi_ant, -rxdesc->rssi); -} - -void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) -{ - struct link *link = &rt2x00dev->link; - - /* - * Link tuning should only be performed when - * an active sta interface exists. AP interfaces - * don't need link tuning and monitor mode interfaces - * should never have to work with link tuners. - */ - if (!rt2x00dev->intf_sta_count) - return; - - /** - * While scanning, link tuning is disabled. By default - * the most sensitive settings will be used to make sure - * that all beacons and probe responses will be received - * during the scan. - */ - if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) - return; - - rt2x00link_reset_tuner(rt2x00dev, false); - - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - ieee80211_queue_delayed_work(rt2x00dev->hw, - &link->work, LINK_TUNE_INTERVAL); -} - -void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev) -{ - cancel_delayed_work_sync(&rt2x00dev->link.work); -} - -void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna) -{ - struct link_qual *qual = &rt2x00dev->link.qual; - u8 vgc_level = qual->vgc_level_reg; - - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return; - - /* - * Reset link information. - * Both the currently active vgc level as well as - * the link tuner counter should be reset. Resetting - * the counter is important for devices where the - * device should only perform link tuning during the - * first minute after being enabled. - */ - rt2x00dev->link.count = 0; - memset(qual, 0, sizeof(*qual)); - ewma_rssi_init(&rt2x00dev->link.avg_rssi); - - /* - * Restore the VGC level as stored in the registers, - * the driver can use this to determine if the register - * must be updated during reset or not. - */ - qual->vgc_level_reg = vgc_level; - - /* - * Reset the link tuner. - */ - rt2x00dev->ops->lib->reset_tuner(rt2x00dev, qual); - - if (antenna) - rt2x00link_antenna_reset(rt2x00dev); -} - -static void rt2x00link_reset_qual(struct rt2x00_dev *rt2x00dev) -{ - struct link_qual *qual = &rt2x00dev->link.qual; - - qual->rx_success = 0; - qual->rx_failed = 0; - qual->tx_success = 0; - qual->tx_failed = 0; -} - -static void rt2x00link_tuner(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, link.work.work); - struct link *link = &rt2x00dev->link; - struct link_qual *qual = &rt2x00dev->link.qual; - - /* - * When the radio is shutting down we should - * immediately cease all link tuning. - */ - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) || - test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) - return; - - /* - * Update statistics. - */ - rt2x00dev->ops->lib->link_stats(rt2x00dev, qual); - rt2x00dev->low_level_stats.dot11FCSErrorCount += qual->rx_failed; - - /* - * Update quality RSSI for link tuning, - * when we have received some frames and we managed to - * collect the RSSI data we could use this. Otherwise we - * must fallback to the default RSSI value. - */ - if (!qual->rx_success) - qual->rssi = DEFAULT_RSSI; - else - qual->rssi = rt2x00link_get_avg_rssi(&link->avg_rssi); - - /* - * Check if link tuning is supported by the hardware, some hardware - * do not support link tuning at all, while other devices can disable - * the feature from the EEPROM. - */ - if (rt2x00_has_cap_link_tuning(rt2x00dev)) - rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count); - - /* - * Send a signal to the led to update the led signal strength. - */ - rt2x00leds_led_quality(rt2x00dev, qual->rssi); - - /* - * Evaluate antenna setup, make this the last step when - * rt2x00lib_antenna_diversity made changes the quality - * statistics will be reset. - */ - if (rt2x00lib_antenna_diversity(rt2x00dev)) - rt2x00link_reset_qual(rt2x00dev); - - /* - * Increase tuner counter, and reschedule the next link tuner run. - */ - link->count++; - - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - ieee80211_queue_delayed_work(rt2x00dev->hw, - &link->work, LINK_TUNE_INTERVAL); -} - -void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev) -{ - struct link *link = &rt2x00dev->link; - - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && - rt2x00dev->ops->lib->watchdog) - ieee80211_queue_delayed_work(rt2x00dev->hw, - &link->watchdog_work, - WATCHDOG_INTERVAL); -} - -void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev) -{ - cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work); -} - -static void rt2x00link_watchdog(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, link.watchdog_work.work); - struct link *link = &rt2x00dev->link; - - /* - * When the radio is shutting down we should - * immediately cease the watchdog monitoring. - */ - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return; - - rt2x00dev->ops->lib->watchdog(rt2x00dev); - - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - ieee80211_queue_delayed_work(rt2x00dev->hw, - &link->watchdog_work, - WATCHDOG_INTERVAL); -} - -void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev) -{ - struct link *link = &rt2x00dev->link; - - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && - rt2x00dev->ops->lib->gain_calibration) - ieee80211_queue_delayed_work(rt2x00dev->hw, - &link->agc_work, - AGC_INTERVAL); -} - -void rt2x00link_start_vcocal(struct rt2x00_dev *rt2x00dev) -{ - struct link *link = &rt2x00dev->link; - - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && - rt2x00dev->ops->lib->vco_calibration) - ieee80211_queue_delayed_work(rt2x00dev->hw, - &link->vco_work, - VCO_INTERVAL); -} - -void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev) -{ - cancel_delayed_work_sync(&rt2x00dev->link.agc_work); -} - -void rt2x00link_stop_vcocal(struct rt2x00_dev *rt2x00dev) -{ - cancel_delayed_work_sync(&rt2x00dev->link.vco_work); -} - -static void rt2x00link_agc(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, link.agc_work.work); - struct link *link = &rt2x00dev->link; - - /* - * When the radio is shutting down we should - * immediately cease the watchdog monitoring. - */ - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return; - - rt2x00dev->ops->lib->gain_calibration(rt2x00dev); - - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - ieee80211_queue_delayed_work(rt2x00dev->hw, - &link->agc_work, - AGC_INTERVAL); -} - -static void rt2x00link_vcocal(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, link.vco_work.work); - struct link *link = &rt2x00dev->link; - - /* - * When the radio is shutting down we should - * immediately cease the VCO calibration. - */ - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return; - - rt2x00dev->ops->lib->vco_calibration(rt2x00dev); - - if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - ieee80211_queue_delayed_work(rt2x00dev->hw, - &link->vco_work, - VCO_INTERVAL); -} - -void rt2x00link_register(struct rt2x00_dev *rt2x00dev) -{ - INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc); - if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) - INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal); - INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog); - INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner); -} diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c deleted file mode 100644 index 3c26ee65a415..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ /dev/null @@ -1,846 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00mac - Abstract: rt2x00 generic mac80211 routines. - */ - -#include -#include - -#include "rt2x00.h" -#include "rt2x00lib.h" - -static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, - struct data_queue *queue, - struct sk_buff *frag_skb) -{ - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(frag_skb); - struct ieee80211_tx_info *rts_info; - struct sk_buff *skb; - unsigned int data_length; - int retval = 0; - - if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) - data_length = sizeof(struct ieee80211_cts); - else - data_length = sizeof(struct ieee80211_rts); - - skb = dev_alloc_skb(data_length + rt2x00dev->hw->extra_tx_headroom); - if (unlikely(!skb)) { - rt2x00_warn(rt2x00dev, "Failed to create RTS/CTS frame\n"); - return -ENOMEM; - } - - skb_reserve(skb, rt2x00dev->hw->extra_tx_headroom); - skb_put(skb, data_length); - - /* - * Copy TX information over from original frame to - * RTS/CTS frame. Note that we set the no encryption flag - * since we don't want this frame to be encrypted. - * RTS frames should be acked, while CTS-to-self frames - * should not. The ready for TX flag is cleared to prevent - * it being automatically send when the descriptor is - * written to the hardware. - */ - memcpy(skb->cb, frag_skb->cb, sizeof(skb->cb)); - rts_info = IEEE80211_SKB_CB(skb); - rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_RTS_CTS; - rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_CTS_PROTECT; - - if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) - rts_info->flags |= IEEE80211_TX_CTL_NO_ACK; - else - rts_info->flags &= ~IEEE80211_TX_CTL_NO_ACK; - - /* Disable hardware encryption */ - rts_info->control.hw_key = NULL; - - /* - * RTS/CTS frame should use the length of the frame plus any - * encryption overhead that will be added by the hardware. - */ - data_length += rt2x00crypto_tx_overhead(rt2x00dev, skb); - - if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) - ieee80211_ctstoself_get(rt2x00dev->hw, tx_info->control.vif, - frag_skb->data, data_length, tx_info, - (struct ieee80211_cts *)(skb->data)); - else - ieee80211_rts_get(rt2x00dev->hw, tx_info->control.vif, - frag_skb->data, data_length, tx_info, - (struct ieee80211_rts *)(skb->data)); - - retval = rt2x00queue_write_tx_frame(queue, skb, NULL, true); - if (retval) { - dev_kfree_skb_any(skb); - rt2x00_warn(rt2x00dev, "Failed to send RTS/CTS frame\n"); - } - - return retval; -} - -void rt2x00mac_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - enum data_queue_qid qid = skb_get_queue_mapping(skb); - struct data_queue *queue = NULL; - - /* - * Mac80211 might be calling this function while we are trying - * to remove the device or perhaps suspending it. - * Note that we can only stop the TX queues inside the TX path - * due to possible race conditions in mac80211. - */ - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - goto exit_free_skb; - - /* - * Use the ATIM queue if appropriate and present. - */ - if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && - rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE)) - qid = QID_ATIM; - - queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); - if (unlikely(!queue)) { - rt2x00_err(rt2x00dev, - "Attempt to send packet over invalid queue %d\n" - "Please file bug report to %s\n", qid, DRV_PROJECT); - goto exit_free_skb; - } - - /* - * If CTS/RTS is required. create and queue that frame first. - * Make sure we have at least enough entries available to send - * this CTS/RTS frame as well as the data frame. - * Note that when the driver has set the set_rts_threshold() - * callback function it doesn't need software generation of - * either RTS or CTS-to-self frame and handles everything - * inside the hardware. - */ - if (!rt2x00dev->ops->hw->set_rts_threshold && - (tx_info->control.rates[0].flags & (IEEE80211_TX_RC_USE_RTS_CTS | - IEEE80211_TX_RC_USE_CTS_PROTECT))) { - if (rt2x00queue_available(queue) <= 1) - goto exit_fail; - - if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb)) - goto exit_fail; - } - - if (unlikely(rt2x00queue_write_tx_frame(queue, skb, control->sta, false))) - goto exit_fail; - - /* - * Pausing queue has to be serialized with rt2x00lib_txdone(). Note - * we should not use spin_lock_bh variant as bottom halve was already - * disabled before ieee80211_xmit() call. - */ - spin_lock(&queue->tx_lock); - if (rt2x00queue_threshold(queue)) - rt2x00queue_pause_queue(queue); - spin_unlock(&queue->tx_lock); - - return; - - exit_fail: - spin_lock(&queue->tx_lock); - rt2x00queue_pause_queue(queue); - spin_unlock(&queue->tx_lock); - exit_free_skb: - ieee80211_free_txskb(hw, skb); -} -EXPORT_SYMBOL_GPL(rt2x00mac_tx); - -int rt2x00mac_start(struct ieee80211_hw *hw) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return 0; - - return rt2x00lib_start(rt2x00dev); -} -EXPORT_SYMBOL_GPL(rt2x00mac_start); - -void rt2x00mac_stop(struct ieee80211_hw *hw) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return; - - rt2x00lib_stop(rt2x00dev); -} -EXPORT_SYMBOL_GPL(rt2x00mac_stop); - -int rt2x00mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct rt2x00_intf *intf = vif_to_intf(vif); - struct data_queue *queue = rt2x00dev->bcn; - struct queue_entry *entry = NULL; - unsigned int i; - - /* - * Don't allow interfaces to be added - * the device has disappeared. - */ - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || - !test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) - return -ENODEV; - - /* - * Loop through all beacon queues to find a free - * entry. Since there are as much beacon entries - * as the maximum interfaces, this search shouldn't - * fail. - */ - for (i = 0; i < queue->limit; i++) { - entry = &queue->entries[i]; - if (!test_and_set_bit(ENTRY_BCN_ASSIGNED, &entry->flags)) - break; - } - - if (unlikely(i == queue->limit)) - return -ENOBUFS; - - /* - * We are now absolutely sure the interface can be created, - * increase interface count and start initialization. - */ - - if (vif->type == NL80211_IFTYPE_AP) - rt2x00dev->intf_ap_count++; - else - rt2x00dev->intf_sta_count++; - - mutex_init(&intf->beacon_skb_mutex); - intf->beacon = entry; - - /* - * The MAC address must be configured after the device - * has been initialized. Otherwise the device can reset - * the MAC registers. - * The BSSID address must only be configured in AP mode, - * however we should not send an empty BSSID address for - * STA interfaces at this time, since this can cause - * invalid behavior in the device. - */ - rt2x00lib_config_intf(rt2x00dev, intf, vif->type, - vif->addr, NULL); - - /* - * Some filters depend on the current working mode. We can force - * an update during the next configure_filter() run by mac80211 by - * resetting the current packet_filter state. - */ - rt2x00dev->packet_filter = 0; - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00mac_add_interface); - -void rt2x00mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct rt2x00_intf *intf = vif_to_intf(vif); - - /* - * Don't allow interfaces to be remove while - * either the device has disappeared or when - * no interface is present. - */ - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || - (vif->type == NL80211_IFTYPE_AP && !rt2x00dev->intf_ap_count) || - (vif->type != NL80211_IFTYPE_AP && !rt2x00dev->intf_sta_count)) - return; - - if (vif->type == NL80211_IFTYPE_AP) - rt2x00dev->intf_ap_count--; - else - rt2x00dev->intf_sta_count--; - - /* - * Release beacon entry so it is available for - * new interfaces again. - */ - clear_bit(ENTRY_BCN_ASSIGNED, &intf->beacon->flags); - - /* - * Make sure the bssid and mac address registers - * are cleared to prevent false ACKing of frames. - */ - rt2x00lib_config_intf(rt2x00dev, intf, - NL80211_IFTYPE_UNSPECIFIED, NULL, NULL); -} -EXPORT_SYMBOL_GPL(rt2x00mac_remove_interface); - -int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct ieee80211_conf *conf = &hw->conf; - - /* - * mac80211 might be calling this function while we are trying - * to remove the device or perhaps suspending it. - */ - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return 0; - - /* - * Some configuration parameters (e.g. channel and antenna values) can - * only be set when the radio is enabled, but do require the RX to - * be off. During this period we should keep link tuning enabled, - * if for any reason the link tuner must be reset, this will be - * handled by rt2x00lib_config(). - */ - rt2x00queue_stop_queue(rt2x00dev->rx); - - /* - * When we've just turned on the radio, we want to reprogram - * everything to ensure a consistent state - */ - rt2x00lib_config(rt2x00dev, conf, changed); - - /* - * After the radio has been enabled we need to configure - * the antenna to the default settings. rt2x00lib_config_antenna() - * should determine if any action should be taken based on - * checking if diversity has been enabled or no antenna changes - * have been made since the last configuration change. - */ - rt2x00lib_config_antenna(rt2x00dev, rt2x00dev->default_ant); - - /* Turn RX back on */ - rt2x00queue_start_queue(rt2x00dev->rx); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00mac_config); - -void rt2x00mac_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - - /* - * Mask off any flags we are going to ignore - * from the total_flags field. - */ - *total_flags &= - FIF_ALLMULTI | - FIF_FCSFAIL | - FIF_PLCPFAIL | - FIF_CONTROL | - FIF_PSPOLL | - FIF_OTHER_BSS; - - /* - * Apply some rules to the filters: - * - Some filters imply different filters to be set. - * - Some things we can't filter out at all. - * - Multicast filter seems to kill broadcast traffic so never use it. - */ - *total_flags |= FIF_ALLMULTI; - - /* - * If the device has a single filter for all control frames, - * FIF_CONTROL and FIF_PSPOLL flags imply each other. - * And if the device has more than one filter for control frames - * of different types, but has no a separate filter for PS Poll frames, - * FIF_CONTROL flag implies FIF_PSPOLL. - */ - if (!rt2x00_has_cap_control_filters(rt2x00dev)) { - if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL) - *total_flags |= FIF_CONTROL | FIF_PSPOLL; - } - if (!rt2x00_has_cap_control_filter_pspoll(rt2x00dev)) { - if (*total_flags & FIF_CONTROL) - *total_flags |= FIF_PSPOLL; - } - - /* - * Check if there is any work left for us. - */ - if (rt2x00dev->packet_filter == *total_flags) - return; - rt2x00dev->packet_filter = *total_flags; - - rt2x00dev->ops->lib->config_filter(rt2x00dev, *total_flags); -} -EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter); - -static void rt2x00mac_set_tim_iter(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct rt2x00_intf *intf = vif_to_intf(vif); - - if (vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_ADHOC && - vif->type != NL80211_IFTYPE_MESH_POINT && - vif->type != NL80211_IFTYPE_WDS) - return; - - set_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags); -} - -int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, - bool set) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return 0; - - ieee80211_iterate_active_interfaces_atomic( - rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, - rt2x00mac_set_tim_iter, rt2x00dev); - - /* queue work to upodate the beacon template */ - ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00mac_set_tim); - -#ifdef CONFIG_RT2X00_LIB_CRYPTO -static void memcpy_tkip(struct rt2x00lib_crypto *crypto, u8 *key, u8 key_len) -{ - if (key_len > NL80211_TKIP_DATA_OFFSET_ENCR_KEY) - memcpy(crypto->key, - &key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY], - sizeof(crypto->key)); - - if (key_len > NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY) - memcpy(crypto->tx_mic, - &key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], - sizeof(crypto->tx_mic)); - - if (key_len > NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY) - memcpy(crypto->rx_mic, - &key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], - sizeof(crypto->rx_mic)); -} - -int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - int (*set_key) (struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key); - struct rt2x00lib_crypto crypto; - static const u8 bcast_addr[ETH_ALEN] = - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; - struct rt2x00_sta *sta_priv = NULL; - - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return 0; - - if (!rt2x00_has_cap_hw_crypto(rt2x00dev)) - return -EOPNOTSUPP; - - /* - * To support IBSS RSN, don't program group keys in IBSS, the - * hardware will then not attempt to decrypt the frames. - */ - if (vif->type == NL80211_IFTYPE_ADHOC && - !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) - return -EOPNOTSUPP; - - if (key->keylen > 32) - return -ENOSPC; - - memset(&crypto, 0, sizeof(crypto)); - - crypto.bssidx = rt2x00lib_get_bssidx(rt2x00dev, vif); - crypto.cipher = rt2x00crypto_key_to_cipher(key); - if (crypto.cipher == CIPHER_NONE) - return -EOPNOTSUPP; - if (crypto.cipher == CIPHER_TKIP && rt2x00_is_usb(rt2x00dev)) - return -EOPNOTSUPP; - - crypto.cmd = cmd; - - if (sta) { - crypto.address = sta->addr; - sta_priv = sta_to_rt2x00_sta(sta); - crypto.wcid = sta_priv->wcid; - } else - crypto.address = bcast_addr; - - if (crypto.cipher == CIPHER_TKIP) - memcpy_tkip(&crypto, &key->key[0], key->keylen); - else - memcpy(crypto.key, &key->key[0], key->keylen); - /* - * Each BSS has a maximum of 4 shared keys. - * Shared key index values: - * 0) BSS0 key0 - * 1) BSS0 key1 - * ... - * 4) BSS1 key0 - * ... - * 8) BSS2 key0 - * ... - * Both pairwise as shared key indeces are determined by - * driver. This is required because the hardware requires - * keys to be assigned in correct order (When key 1 is - * provided but key 0 is not, then the key is not found - * by the hardware during RX). - */ - if (cmd == SET_KEY) - key->hw_key_idx = 0; - - if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) - set_key = rt2x00dev->ops->lib->config_pairwise_key; - else - set_key = rt2x00dev->ops->lib->config_shared_key; - - if (!set_key) - return -EOPNOTSUPP; - - return set_key(rt2x00dev, &crypto, key); -} -EXPORT_SYMBOL_GPL(rt2x00mac_set_key); -#endif /* CONFIG_RT2X00_LIB_CRYPTO */ - -int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - - return rt2x00dev->ops->lib->sta_add(rt2x00dev, vif, sta); -} -EXPORT_SYMBOL_GPL(rt2x00mac_sta_add); - -int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta); - - return rt2x00dev->ops->lib->sta_remove(rt2x00dev, sta_priv->wcid); -} -EXPORT_SYMBOL_GPL(rt2x00mac_sta_remove); - -void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const u8 *mac_addr) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - set_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags); - rt2x00link_stop_tuner(rt2x00dev); -} -EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_start); - -void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - clear_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags); - rt2x00link_start_tuner(rt2x00dev); -} -EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_complete); - -int rt2x00mac_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - - /* - * The dot11ACKFailureCount, dot11RTSFailureCount and - * dot11RTSSuccessCount are updated in interrupt time. - * dot11FCSErrorCount is updated in the link tuner. - */ - memcpy(stats, &rt2x00dev->low_level_stats, sizeof(*stats)); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00mac_get_stats); - -void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct rt2x00_intf *intf = vif_to_intf(vif); - - /* - * mac80211 might be calling this function while we are trying - * to remove the device or perhaps suspending it. - */ - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return; - - /* - * Update the BSSID. - */ - if (changes & BSS_CHANGED_BSSID) - rt2x00lib_config_intf(rt2x00dev, intf, vif->type, NULL, - bss_conf->bssid); - - /* - * Start/stop beaconing. - */ - if (changes & BSS_CHANGED_BEACON_ENABLED) { - mutex_lock(&intf->beacon_skb_mutex); - if (!bss_conf->enable_beacon && intf->enable_beacon) { - rt2x00dev->intf_beaconing--; - intf->enable_beacon = false; - - if (rt2x00dev->intf_beaconing == 0) { - /* - * Last beaconing interface disabled - * -> stop beacon queue. - */ - rt2x00queue_stop_queue(rt2x00dev->bcn); - } - /* - * Clear beacon in the H/W for this vif. This is needed - * to disable beaconing on this particular interface - * and keep it running on other interfaces. - */ - rt2x00queue_clear_beacon(rt2x00dev, vif); - } else if (bss_conf->enable_beacon && !intf->enable_beacon) { - rt2x00dev->intf_beaconing++; - intf->enable_beacon = true; - /* - * Upload beacon to the H/W. This is only required on - * USB devices. PCI devices fetch beacons periodically. - */ - if (rt2x00_is_usb(rt2x00dev)) - rt2x00queue_update_beacon(rt2x00dev, vif); - - if (rt2x00dev->intf_beaconing == 1) { - /* - * First beaconing interface enabled - * -> start beacon queue. - */ - rt2x00queue_start_queue(rt2x00dev->bcn); - } - } - mutex_unlock(&intf->beacon_skb_mutex); - } - - /* - * When the association status has changed we must reset the link - * tuner counter. This is because some drivers determine if they - * should perform link tuning based on the number of seconds - * while associated or not associated. - */ - if (changes & BSS_CHANGED_ASSOC) { - rt2x00dev->link.count = 0; - - if (bss_conf->assoc) - rt2x00dev->intf_associated++; - else - rt2x00dev->intf_associated--; - - rt2x00leds_led_assoc(rt2x00dev, !!rt2x00dev->intf_associated); - - clear_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags); - } - - /* - * Check for access point which do not support 802.11e . We have to - * generate data frames sequence number in S/W for such AP, because - * of H/W bug. - */ - if (changes & BSS_CHANGED_QOS && !bss_conf->qos) - set_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags); - - /* - * When the erp information has changed, we should perform - * additional configuration steps. For all other changes we are done. - */ - if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE | - BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BASIC_RATES | - BSS_CHANGED_BEACON_INT | BSS_CHANGED_HT)) - rt2x00lib_config_erp(rt2x00dev, intf, bss_conf, changes); -} -EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed); - -int rt2x00mac_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue_idx, - const struct ieee80211_tx_queue_params *params) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct data_queue *queue; - - queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); - if (unlikely(!queue)) - return -EINVAL; - - /* - * The passed variables are stored as real value ((2^n)-1). - * Ralink registers require to know the bit number 'n'. - */ - if (params->cw_min > 0) - queue->cw_min = fls(params->cw_min); - else - queue->cw_min = 5; /* cw_min: 2^5 = 32. */ - - if (params->cw_max > 0) - queue->cw_max = fls(params->cw_max); - else - queue->cw_max = 10; /* cw_min: 2^10 = 1024. */ - - queue->aifs = params->aifs; - queue->txop = params->txop; - - rt2x00_dbg(rt2x00dev, - "Configured TX queue %d - CWmin: %d, CWmax: %d, Aifs: %d, TXop: %d\n", - queue_idx, queue->cw_min, queue->cw_max, queue->aifs, - queue->txop); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00mac_conf_tx); - -void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - bool active = !!rt2x00dev->ops->lib->rfkill_poll(rt2x00dev); - - wiphy_rfkill_set_hw_state(hw->wiphy, !active); -} -EXPORT_SYMBOL_GPL(rt2x00mac_rfkill_poll); - -void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 queues, bool drop) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct data_queue *queue; - - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return; - - tx_queue_for_each(rt2x00dev, queue) - rt2x00queue_flush_queue(queue, drop); -} -EXPORT_SYMBOL_GPL(rt2x00mac_flush); - -int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct link_ant *ant = &rt2x00dev->link.ant; - struct antenna_setup *def = &rt2x00dev->default_ant; - struct antenna_setup setup; - - // The antenna value is not supposed to be 0, - // or exceed the maximum number of antenna's. - if (!tx_ant || (tx_ant & ~3) || !rx_ant || (rx_ant & ~3)) - return -EINVAL; - - // When the client tried to configure the antenna to or from - // diversity mode, we must reset the default antenna as well - // as that controls the diversity switch. - if (ant->flags & ANTENNA_TX_DIVERSITY && tx_ant != 3) - ant->flags &= ~ANTENNA_TX_DIVERSITY; - if (ant->flags & ANTENNA_RX_DIVERSITY && rx_ant != 3) - ant->flags &= ~ANTENNA_RX_DIVERSITY; - - // If diversity is being enabled, check if we need hardware - // or software diversity. In the latter case, reset the value, - // and make sure we update the antenna flags to have the - // link tuner pick up the diversity tuning. - if (tx_ant == 3 && def->tx == ANTENNA_SW_DIVERSITY) { - tx_ant = ANTENNA_SW_DIVERSITY; - ant->flags |= ANTENNA_TX_DIVERSITY; - } - - if (rx_ant == 3 && def->rx == ANTENNA_SW_DIVERSITY) { - rx_ant = ANTENNA_SW_DIVERSITY; - ant->flags |= ANTENNA_RX_DIVERSITY; - } - - setup.tx = tx_ant; - setup.rx = rx_ant; - setup.rx_chain_num = 0; - setup.tx_chain_num = 0; - - rt2x00lib_config_antenna(rt2x00dev, setup); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00mac_set_antenna); - -int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct link_ant *ant = &rt2x00dev->link.ant; - struct antenna_setup *active = &rt2x00dev->link.ant.active; - - // When software diversity is active, we must report this to the - // client and not the current active antenna state. - if (ant->flags & ANTENNA_TX_DIVERSITY) - *tx_ant = ANTENNA_HW_DIVERSITY; - else - *tx_ant = active->tx; - - if (ant->flags & ANTENNA_RX_DIVERSITY) - *rx_ant = ANTENNA_HW_DIVERSITY; - else - *rx_ant = active->rx; - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00mac_get_antenna); - -void rt2x00mac_get_ringparam(struct ieee80211_hw *hw, - u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct data_queue *queue; - - tx_queue_for_each(rt2x00dev, queue) { - *tx += queue->length; - *tx_max += queue->limit; - } - - *rx = rt2x00dev->rx->length; - *rx_max = rt2x00dev->rx->limit; -} -EXPORT_SYMBOL_GPL(rt2x00mac_get_ringparam); - -bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct data_queue *queue; - - tx_queue_for_each(rt2x00dev, queue) { - if (!rt2x00queue_empty(queue)) - return true; - } - - return false; -} -EXPORT_SYMBOL_GPL(rt2x00mac_tx_frames_pending); diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.c b/drivers/net/wireless/rt2x00/rt2x00mmio.c deleted file mode 100644 index f0178fd4fe5f..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00mmio.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00mmio - Abstract: rt2x00 generic mmio device routines. - */ - -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00mmio.h" - -/* - * Register access. - */ -int rt2x00mmio_regbusy_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - const struct rt2x00_field32 field, - u32 *reg) -{ - unsigned int i; - - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return 0; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00mmio_register_read(rt2x00dev, offset, reg); - if (!rt2x00_get_field32(*reg, field)) - return 1; - udelay(REGISTER_BUSY_DELAY); - } - - printk_once(KERN_ERR "%s() Indirect register access failed: " - "offset=0x%.08x, value=0x%.08x\n", __func__, offset, *reg); - *reg = ~0; - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00mmio_regbusy_read); - -bool rt2x00mmio_rxdone(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue = rt2x00dev->rx; - struct queue_entry *entry; - struct queue_entry_priv_mmio *entry_priv; - struct skb_frame_desc *skbdesc; - int max_rx = 16; - - while (--max_rx) { - entry = rt2x00queue_get_entry(queue, Q_INDEX); - entry_priv = entry->priv_data; - - if (rt2x00dev->ops->lib->get_entry_state(entry)) - break; - - /* - * Fill in desc fields of the skb descriptor - */ - skbdesc = get_skb_frame_desc(entry->skb); - skbdesc->desc = entry_priv->desc; - skbdesc->desc_len = entry->queue->desc_size; - - /* - * DMA is already done, notify rt2x00lib that - * it finished successfully. - */ - rt2x00lib_dmastart(entry); - rt2x00lib_dmadone(entry); - - /* - * Send the frame to rt2x00lib for further processing. - */ - rt2x00lib_rxdone(entry, GFP_ATOMIC); - } - - return !max_rx; -} -EXPORT_SYMBOL_GPL(rt2x00mmio_rxdone); - -void rt2x00mmio_flush_queue(struct data_queue *queue, bool drop) -{ - unsigned int i; - - for (i = 0; !rt2x00queue_empty(queue) && i < 10; i++) - msleep(10); -} -EXPORT_SYMBOL_GPL(rt2x00mmio_flush_queue); - -/* - * Device initialization handlers. - */ -static int rt2x00mmio_alloc_queue_dma(struct rt2x00_dev *rt2x00dev, - struct data_queue *queue) -{ - struct queue_entry_priv_mmio *entry_priv; - void *addr; - dma_addr_t dma; - unsigned int i; - - /* - * Allocate DMA memory for descriptor and buffer. - */ - addr = dma_zalloc_coherent(rt2x00dev->dev, - queue->limit * queue->desc_size, &dma, - GFP_KERNEL); - if (!addr) - return -ENOMEM; - - /* - * Initialize all queue entries to contain valid addresses. - */ - for (i = 0; i < queue->limit; i++) { - entry_priv = queue->entries[i].priv_data; - entry_priv->desc = addr + i * queue->desc_size; - entry_priv->desc_dma = dma + i * queue->desc_size; - } - - return 0; -} - -static void rt2x00mmio_free_queue_dma(struct rt2x00_dev *rt2x00dev, - struct data_queue *queue) -{ - struct queue_entry_priv_mmio *entry_priv = - queue->entries[0].priv_data; - - if (entry_priv->desc) - dma_free_coherent(rt2x00dev->dev, - queue->limit * queue->desc_size, - entry_priv->desc, entry_priv->desc_dma); - entry_priv->desc = NULL; -} - -int rt2x00mmio_initialize(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - int status; - - /* - * Allocate DMA - */ - queue_for_each(rt2x00dev, queue) { - status = rt2x00mmio_alloc_queue_dma(rt2x00dev, queue); - if (status) - goto exit; - } - - /* - * Register interrupt handler. - */ - status = request_irq(rt2x00dev->irq, - rt2x00dev->ops->lib->irq_handler, - IRQF_SHARED, rt2x00dev->name, rt2x00dev); - if (status) { - rt2x00_err(rt2x00dev, "IRQ %d allocation failed (error %d)\n", - rt2x00dev->irq, status); - goto exit; - } - - return 0; - -exit: - queue_for_each(rt2x00dev, queue) - rt2x00mmio_free_queue_dma(rt2x00dev, queue); - - return status; -} -EXPORT_SYMBOL_GPL(rt2x00mmio_initialize); - -void rt2x00mmio_uninitialize(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - - /* - * Free irq line. - */ - free_irq(rt2x00dev->irq, rt2x00dev); - - /* - * Free DMA - */ - queue_for_each(rt2x00dev, queue) - rt2x00mmio_free_queue_dma(rt2x00dev, queue); -} -EXPORT_SYMBOL_GPL(rt2x00mmio_uninitialize); - -/* - * rt2x00mmio module information. - */ -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("rt2x00 mmio library"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.h b/drivers/net/wireless/rt2x00/rt2x00mmio.h deleted file mode 100644 index 701c3127efb9..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00mmio.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00mmio - Abstract: Data structures for the rt2x00mmio module. - */ - -#ifndef RT2X00MMIO_H -#define RT2X00MMIO_H - -#include - -/* - * Register access. - */ -static inline void rt2x00mmio_register_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 *value) -{ - *value = readl(rt2x00dev->csr.base + offset); -} - -static inline void rt2x00mmio_register_multiread(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - void *value, const u32 length) -{ - memcpy_fromio(value, rt2x00dev->csr.base + offset, length); -} - -static inline void rt2x00mmio_register_write(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 value) -{ - writel(value, rt2x00dev->csr.base + offset); -} - -static inline void rt2x00mmio_register_multiwrite(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - const void *value, - const u32 length) -{ - __iowrite32_copy(rt2x00dev->csr.base + offset, value, length >> 2); -} - -/** - * rt2x00mmio_regbusy_read - Read from register with busy check - * @rt2x00dev: Device pointer, see &struct rt2x00_dev. - * @offset: Register offset - * @field: Field to check if register is busy - * @reg: Pointer to where register contents should be stored - * - * This function will read the given register, and checks if the - * register is busy. If it is, it will sleep for a couple of - * microseconds before reading the register again. If the register - * is not read after a certain timeout, this function will return - * FALSE. - */ -int rt2x00mmio_regbusy_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - const struct rt2x00_field32 field, - u32 *reg); - -/** - * struct queue_entry_priv_mmio: Per entry PCI specific information - * - * @desc: Pointer to device descriptor - * @desc_dma: DMA pointer to &desc. - * @data: Pointer to device's entry memory. - * @data_dma: DMA pointer to &data. - */ -struct queue_entry_priv_mmio { - __le32 *desc; - dma_addr_t desc_dma; -}; - -/** - * rt2x00mmio_rxdone - Handle RX done events - * @rt2x00dev: Device pointer, see &struct rt2x00_dev. - * - * Returns true if there are still rx frames pending and false if all - * pending rx frames were processed. - */ -bool rt2x00mmio_rxdone(struct rt2x00_dev *rt2x00dev); - -/** - * rt2x00mmio_flush_queue - Flush data queue - * @queue: Data queue to stop - * @drop: True to drop all pending frames. - * - * This will wait for a maximum of 100ms, waiting for the queues - * to become empty. - */ -void rt2x00mmio_flush_queue(struct data_queue *queue, bool drop); - -/* - * Device initialization handlers. - */ -int rt2x00mmio_initialize(struct rt2x00_dev *rt2x00dev); -void rt2x00mmio_uninitialize(struct rt2x00_dev *rt2x00dev); - -#endif /* RT2X00MMIO_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c deleted file mode 100644 index d93db4b0371b..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00pci.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00pci - Abstract: rt2x00 generic pci device routines. - */ - -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00pci.h" - -/* - * PCI driver handlers. - */ -static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev) -{ - kfree(rt2x00dev->rf); - rt2x00dev->rf = NULL; - - kfree(rt2x00dev->eeprom); - rt2x00dev->eeprom = NULL; - - if (rt2x00dev->csr.base) { - iounmap(rt2x00dev->csr.base); - rt2x00dev->csr.base = NULL; - } -} - -static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev) -{ - struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev); - - rt2x00dev->csr.base = pci_ioremap_bar(pci_dev, 0); - if (!rt2x00dev->csr.base) - goto exit; - - rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); - if (!rt2x00dev->eeprom) - goto exit; - - rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); - if (!rt2x00dev->rf) - goto exit; - - return 0; - -exit: - rt2x00_probe_err("Failed to allocate registers\n"); - - rt2x00pci_free_reg(rt2x00dev); - - return -ENOMEM; -} - -int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) -{ - struct ieee80211_hw *hw; - struct rt2x00_dev *rt2x00dev; - int retval; - u16 chip; - - retval = pci_enable_device(pci_dev); - if (retval) { - rt2x00_probe_err("Enable device failed\n"); - return retval; - } - - retval = pci_request_regions(pci_dev, pci_name(pci_dev)); - if (retval) { - rt2x00_probe_err("PCI request regions failed\n"); - goto exit_disable_device; - } - - pci_set_master(pci_dev); - - if (pci_set_mwi(pci_dev)) - rt2x00_probe_err("MWI not available\n"); - - if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) { - rt2x00_probe_err("PCI DMA not supported\n"); - retval = -EIO; - goto exit_release_regions; - } - - hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); - if (!hw) { - rt2x00_probe_err("Failed to allocate hardware\n"); - retval = -ENOMEM; - goto exit_release_regions; - } - - pci_set_drvdata(pci_dev, hw); - - rt2x00dev = hw->priv; - rt2x00dev->dev = &pci_dev->dev; - rt2x00dev->ops = ops; - rt2x00dev->hw = hw; - rt2x00dev->irq = pci_dev->irq; - rt2x00dev->name = ops->name; - - if (pci_is_pcie(pci_dev)) - rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); - else - rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); - - retval = rt2x00pci_alloc_reg(rt2x00dev); - if (retval) - goto exit_free_device; - - /* - * Because rt3290 chip use different efuse offset to read efuse data. - * So before read efuse it need to indicate it is the - * rt3290 or not. - */ - pci_read_config_word(pci_dev, PCI_DEVICE_ID, &chip); - rt2x00dev->chip.rt = chip; - - retval = rt2x00lib_probe_dev(rt2x00dev); - if (retval) - goto exit_free_reg; - - return 0; - -exit_free_reg: - rt2x00pci_free_reg(rt2x00dev); - -exit_free_device: - ieee80211_free_hw(hw); - -exit_release_regions: - pci_release_regions(pci_dev); - -exit_disable_device: - pci_disable_device(pci_dev); - - return retval; -} -EXPORT_SYMBOL_GPL(rt2x00pci_probe); - -void rt2x00pci_remove(struct pci_dev *pci_dev) -{ - struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); - struct rt2x00_dev *rt2x00dev = hw->priv; - - /* - * Free all allocated data. - */ - rt2x00lib_remove_dev(rt2x00dev); - rt2x00pci_free_reg(rt2x00dev); - ieee80211_free_hw(hw); - - /* - * Free the PCI device data. - */ - pci_disable_device(pci_dev); - pci_release_regions(pci_dev); -} -EXPORT_SYMBOL_GPL(rt2x00pci_remove); - -#ifdef CONFIG_PM -int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state) -{ - struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); - struct rt2x00_dev *rt2x00dev = hw->priv; - int retval; - - retval = rt2x00lib_suspend(rt2x00dev, state); - if (retval) - return retval; - - pci_save_state(pci_dev); - pci_disable_device(pci_dev); - return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); -} -EXPORT_SYMBOL_GPL(rt2x00pci_suspend); - -int rt2x00pci_resume(struct pci_dev *pci_dev) -{ - struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); - struct rt2x00_dev *rt2x00dev = hw->priv; - - if (pci_set_power_state(pci_dev, PCI_D0) || - pci_enable_device(pci_dev)) { - rt2x00_err(rt2x00dev, "Failed to resume device\n"); - return -EIO; - } - - pci_restore_state(pci_dev); - return rt2x00lib_resume(rt2x00dev); -} -EXPORT_SYMBOL_GPL(rt2x00pci_resume); -#endif /* CONFIG_PM */ - -/* - * rt2x00pci module information. - */ -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("rt2x00 pci library"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.h b/drivers/net/wireless/rt2x00/rt2x00pci.h deleted file mode 100644 index bc0ca5f58f38..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00pci.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00pci - Abstract: Data structures for the rt2x00pci module. - */ - -#ifndef RT2X00PCI_H -#define RT2X00PCI_H - -#include -#include - -/* - * This variable should be used with the - * pci_driver structure initialization. - */ -#define PCI_DEVICE_DATA(__ops) .driver_data = (kernel_ulong_t)(__ops) - -/* - * PCI driver handlers. - */ -int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops); -void rt2x00pci_remove(struct pci_dev *pci_dev); -#ifdef CONFIG_PM -int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state); -int rt2x00pci_resume(struct pci_dev *pci_dev); -#else -#define rt2x00pci_suspend NULL -#define rt2x00pci_resume NULL -#endif /* CONFIG_PM */ - -#endif /* RT2X00PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c deleted file mode 100644 index 68b620b2462f..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ /dev/null @@ -1,1290 +0,0 @@ -/* - Copyright (C) 2010 Willow Garage - Copyright (C) 2004 - 2010 Ivo van Doorn - Copyright (C) 2004 - 2009 Gertjan van Wingerde - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00lib - Abstract: rt2x00 queue specific routines. - */ - -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00lib.h" - -struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp) -{ - struct data_queue *queue = entry->queue; - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - struct sk_buff *skb; - struct skb_frame_desc *skbdesc; - unsigned int frame_size; - unsigned int head_size = 0; - unsigned int tail_size = 0; - - /* - * The frame size includes descriptor size, because the - * hardware directly receive the frame into the skbuffer. - */ - frame_size = queue->data_size + queue->desc_size + queue->winfo_size; - - /* - * The payload should be aligned to a 4-byte boundary, - * this means we need at least 3 bytes for moving the frame - * into the correct offset. - */ - head_size = 4; - - /* - * For IV/EIV/ICV assembly we must make sure there is - * at least 8 bytes bytes available in headroom for IV/EIV - * and 8 bytes for ICV data as tailroon. - */ - if (rt2x00_has_cap_hw_crypto(rt2x00dev)) { - head_size += 8; - tail_size += 8; - } - - /* - * Allocate skbuffer. - */ - skb = __dev_alloc_skb(frame_size + head_size + tail_size, gfp); - if (!skb) - return NULL; - - /* - * Make sure we not have a frame with the requested bytes - * available in the head and tail. - */ - skb_reserve(skb, head_size); - skb_put(skb, frame_size); - - /* - * Populate skbdesc. - */ - skbdesc = get_skb_frame_desc(skb); - memset(skbdesc, 0, sizeof(*skbdesc)); - skbdesc->entry = entry; - - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA)) { - dma_addr_t skb_dma; - - skb_dma = dma_map_single(rt2x00dev->dev, skb->data, skb->len, - DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(rt2x00dev->dev, skb_dma))) { - dev_kfree_skb_any(skb); - return NULL; - } - - skbdesc->skb_dma = skb_dma; - skbdesc->flags |= SKBDESC_DMA_MAPPED_RX; - } - - return skb; -} - -int rt2x00queue_map_txskb(struct queue_entry *entry) -{ - struct device *dev = entry->queue->rt2x00dev->dev; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - - skbdesc->skb_dma = - dma_map_single(dev, entry->skb->data, entry->skb->len, DMA_TO_DEVICE); - - if (unlikely(dma_mapping_error(dev, skbdesc->skb_dma))) - return -ENOMEM; - - skbdesc->flags |= SKBDESC_DMA_MAPPED_TX; - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00queue_map_txskb); - -void rt2x00queue_unmap_skb(struct queue_entry *entry) -{ - struct device *dev = entry->queue->rt2x00dev->dev; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - - if (skbdesc->flags & SKBDESC_DMA_MAPPED_RX) { - dma_unmap_single(dev, skbdesc->skb_dma, entry->skb->len, - DMA_FROM_DEVICE); - skbdesc->flags &= ~SKBDESC_DMA_MAPPED_RX; - } else if (skbdesc->flags & SKBDESC_DMA_MAPPED_TX) { - dma_unmap_single(dev, skbdesc->skb_dma, entry->skb->len, - DMA_TO_DEVICE); - skbdesc->flags &= ~SKBDESC_DMA_MAPPED_TX; - } -} -EXPORT_SYMBOL_GPL(rt2x00queue_unmap_skb); - -void rt2x00queue_free_skb(struct queue_entry *entry) -{ - if (!entry->skb) - return; - - rt2x00queue_unmap_skb(entry); - dev_kfree_skb_any(entry->skb); - entry->skb = NULL; -} - -void rt2x00queue_align_frame(struct sk_buff *skb) -{ - unsigned int frame_length = skb->len; - unsigned int align = ALIGN_SIZE(skb, 0); - - if (!align) - return; - - skb_push(skb, align); - memmove(skb->data, skb->data + align, frame_length); - skb_trim(skb, frame_length); -} - -/* - * H/W needs L2 padding between the header and the paylod if header size - * is not 4 bytes aligned. - */ -void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int hdr_len) -{ - unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0; - - if (!l2pad) - return; - - skb_push(skb, l2pad); - memmove(skb->data, skb->data + l2pad, hdr_len); -} - -void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int hdr_len) -{ - unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0; - - if (!l2pad) - return; - - memmove(skb->data + l2pad, skb->data, hdr_len); - skb_pull(skb, l2pad); -} - -static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txentry_desc *txdesc) -{ - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif); - u16 seqno; - - if (!(tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) - return; - - __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); - - if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_SW_SEQNO)) { - /* - * rt2800 has a H/W (or F/W) bug, device incorrectly increase - * seqno on retransmited data (non-QOS) frames. To workaround - * the problem let's generate seqno in software if QOS is - * disabled. - */ - if (test_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags)) - __clear_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); - else - /* H/W will generate sequence number */ - return; - } - - /* - * The hardware is not able to insert a sequence number. Assign a - * software generated one here. - * - * This is wrong because beacons are not getting sequence - * numbers assigned properly. - * - * A secondary problem exists for drivers that cannot toggle - * sequence counting per-frame, since those will override the - * sequence counter given by mac80211. - */ - if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) - seqno = atomic_add_return(0x10, &intf->seqno); - else - seqno = atomic_read(&intf->seqno); - - hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(seqno); -} - -static void rt2x00queue_create_tx_descriptor_plcp(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txentry_desc *txdesc, - const struct rt2x00_rate *hwrate) -{ - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; - unsigned int data_length; - unsigned int duration; - unsigned int residual; - - /* - * Determine with what IFS priority this frame should be send. - * Set ifs to IFS_SIFS when the this is not the first fragment, - * or this fragment came after RTS/CTS. - */ - if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) - txdesc->u.plcp.ifs = IFS_BACKOFF; - else - txdesc->u.plcp.ifs = IFS_SIFS; - - /* Data length + CRC + Crypto overhead (IV/EIV/ICV/MIC) */ - data_length = skb->len + 4; - data_length += rt2x00crypto_tx_overhead(rt2x00dev, skb); - - /* - * PLCP setup - * Length calculation depends on OFDM/CCK rate. - */ - txdesc->u.plcp.signal = hwrate->plcp; - txdesc->u.plcp.service = 0x04; - - if (hwrate->flags & DEV_RATE_OFDM) { - txdesc->u.plcp.length_high = (data_length >> 6) & 0x3f; - txdesc->u.plcp.length_low = data_length & 0x3f; - } else { - /* - * Convert length to microseconds. - */ - residual = GET_DURATION_RES(data_length, hwrate->bitrate); - duration = GET_DURATION(data_length, hwrate->bitrate); - - if (residual != 0) { - duration++; - - /* - * Check if we need to set the Length Extension - */ - if (hwrate->bitrate == 110 && residual <= 30) - txdesc->u.plcp.service |= 0x80; - } - - txdesc->u.plcp.length_high = (duration >> 8) & 0xff; - txdesc->u.plcp.length_low = duration & 0xff; - - /* - * When preamble is enabled we should set the - * preamble bit for the signal. - */ - if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) - txdesc->u.plcp.signal |= 0x08; - } -} - -static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txentry_desc *txdesc, - struct ieee80211_sta *sta, - const struct rt2x00_rate *hwrate) -{ - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct rt2x00_sta *sta_priv = NULL; - - if (sta) { - txdesc->u.ht.mpdu_density = - sta->ht_cap.ampdu_density; - - sta_priv = sta_to_rt2x00_sta(sta); - txdesc->u.ht.wcid = sta_priv->wcid; - } - - /* - * If IEEE80211_TX_RC_MCS is set txrate->idx just contains the - * mcs rate to be used - */ - if (txrate->flags & IEEE80211_TX_RC_MCS) { - txdesc->u.ht.mcs = txrate->idx; - - /* - * MIMO PS should be set to 1 for STA's using dynamic SM PS - * when using more then one tx stream (>MCS7). - */ - if (sta && txdesc->u.ht.mcs > 7 && - sta->smps_mode == IEEE80211_SMPS_DYNAMIC) - __set_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags); - } else { - txdesc->u.ht.mcs = rt2x00_get_rate_mcs(hwrate->mcs); - if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) - txdesc->u.ht.mcs |= 0x08; - } - - if (test_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags)) { - if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)) - txdesc->u.ht.txop = TXOP_SIFS; - else - txdesc->u.ht.txop = TXOP_BACKOFF; - - /* Left zero on all other settings. */ - return; - } - - txdesc->u.ht.ba_size = 7; /* FIXME: What value is needed? */ - - /* - * Only one STBC stream is supported for now. - */ - if (tx_info->flags & IEEE80211_TX_CTL_STBC) - txdesc->u.ht.stbc = 1; - - /* - * This frame is eligible for an AMPDU, however, don't aggregate - * frames that are intended to probe a specific tx rate. - */ - if (tx_info->flags & IEEE80211_TX_CTL_AMPDU && - !(tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) - __set_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags); - - /* - * Set 40Mhz mode if necessary (for legacy rates this will - * duplicate the frame to both channels). - */ - if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH || - txrate->flags & IEEE80211_TX_RC_DUP_DATA) - __set_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags); - if (txrate->flags & IEEE80211_TX_RC_SHORT_GI) - __set_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags); - - /* - * Determine IFS values - * - Use TXOP_BACKOFF for management frames except beacons - * - Use TXOP_SIFS for fragment bursts - * - Use TXOP_HTTXOP for everything else - * - * Note: rt2800 devices won't use CTS protection (if used) - * for frames not transmitted with TXOP_HTTXOP - */ - if (ieee80211_is_mgmt(hdr->frame_control) && - !ieee80211_is_beacon(hdr->frame_control)) - txdesc->u.ht.txop = TXOP_BACKOFF; - else if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)) - txdesc->u.ht.txop = TXOP_SIFS; - else - txdesc->u.ht.txop = TXOP_HTTXOP; -} - -static void rt2x00queue_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txentry_desc *txdesc, - struct ieee80211_sta *sta) -{ - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; - struct ieee80211_rate *rate; - const struct rt2x00_rate *hwrate = NULL; - - memset(txdesc, 0, sizeof(*txdesc)); - - /* - * Header and frame information. - */ - txdesc->length = skb->len; - txdesc->header_length = ieee80211_get_hdrlen_from_skb(skb); - - /* - * Check whether this frame is to be acked. - */ - if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) - __set_bit(ENTRY_TXD_ACK, &txdesc->flags); - - /* - * Check if this is a RTS/CTS frame - */ - if (ieee80211_is_rts(hdr->frame_control) || - ieee80211_is_cts(hdr->frame_control)) { - __set_bit(ENTRY_TXD_BURST, &txdesc->flags); - if (ieee80211_is_rts(hdr->frame_control)) - __set_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags); - else - __set_bit(ENTRY_TXD_CTS_FRAME, &txdesc->flags); - if (tx_info->control.rts_cts_rate_idx >= 0) - rate = - ieee80211_get_rts_cts_rate(rt2x00dev->hw, tx_info); - } - - /* - * Determine retry information. - */ - txdesc->retry_limit = tx_info->control.rates[0].count - 1; - if (txdesc->retry_limit >= rt2x00dev->long_retry) - __set_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags); - - /* - * Check if more fragments are pending - */ - if (ieee80211_has_morefrags(hdr->frame_control)) { - __set_bit(ENTRY_TXD_BURST, &txdesc->flags); - __set_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags); - } - - /* - * Check if more frames (!= fragments) are pending - */ - if (tx_info->flags & IEEE80211_TX_CTL_MORE_FRAMES) - __set_bit(ENTRY_TXD_BURST, &txdesc->flags); - - /* - * Beacons and probe responses require the tsf timestamp - * to be inserted into the frame. - */ - if (ieee80211_is_beacon(hdr->frame_control) || - ieee80211_is_probe_resp(hdr->frame_control)) - __set_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags); - - if ((tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) && - !test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)) - __set_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags); - - /* - * Determine rate modulation. - */ - if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD) - txdesc->rate_mode = RATE_MODE_HT_GREENFIELD; - else if (txrate->flags & IEEE80211_TX_RC_MCS) - txdesc->rate_mode = RATE_MODE_HT_MIX; - else { - rate = ieee80211_get_tx_rate(rt2x00dev->hw, tx_info); - hwrate = rt2x00_get_rate(rate->hw_value); - if (hwrate->flags & DEV_RATE_OFDM) - txdesc->rate_mode = RATE_MODE_OFDM; - else - txdesc->rate_mode = RATE_MODE_CCK; - } - - /* - * Apply TX descriptor handling by components - */ - rt2x00crypto_create_tx_descriptor(rt2x00dev, skb, txdesc); - rt2x00queue_create_tx_descriptor_seq(rt2x00dev, skb, txdesc); - - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_HT_TX_DESC)) - rt2x00queue_create_tx_descriptor_ht(rt2x00dev, skb, txdesc, - sta, hwrate); - else - rt2x00queue_create_tx_descriptor_plcp(rt2x00dev, skb, txdesc, - hwrate); -} - -static int rt2x00queue_write_tx_data(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - - /* - * This should not happen, we already checked the entry - * was ours. When the hardware disagrees there has been - * a queue corruption! - */ - if (unlikely(rt2x00dev->ops->lib->get_entry_state && - rt2x00dev->ops->lib->get_entry_state(entry))) { - rt2x00_err(rt2x00dev, - "Corrupt queue %d, accessing entry which is not ours\n" - "Please file bug report to %s\n", - entry->queue->qid, DRV_PROJECT); - return -EINVAL; - } - - /* - * Add the requested extra tx headroom in front of the skb. - */ - skb_push(entry->skb, rt2x00dev->extra_tx_headroom); - memset(entry->skb->data, 0, rt2x00dev->extra_tx_headroom); - - /* - * Call the driver's write_tx_data function, if it exists. - */ - if (rt2x00dev->ops->lib->write_tx_data) - rt2x00dev->ops->lib->write_tx_data(entry, txdesc); - - /* - * Map the skb to DMA. - */ - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA) && - rt2x00queue_map_txskb(entry)) - return -ENOMEM; - - return 0; -} - -static void rt2x00queue_write_tx_descriptor(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct data_queue *queue = entry->queue; - - queue->rt2x00dev->ops->lib->write_tx_desc(entry, txdesc); - - /* - * All processing on the frame has been completed, this means - * it is now ready to be dumped to userspace through debugfs. - */ - rt2x00debug_dump_frame(queue->rt2x00dev, DUMP_FRAME_TX, entry->skb); -} - -static void rt2x00queue_kick_tx_queue(struct data_queue *queue, - struct txentry_desc *txdesc) -{ - /* - * Check if we need to kick the queue, there are however a few rules - * 1) Don't kick unless this is the last in frame in a burst. - * When the burst flag is set, this frame is always followed - * by another frame which in some way are related to eachother. - * This is true for fragments, RTS or CTS-to-self frames. - * 2) Rule 1 can be broken when the available entries - * in the queue are less then a certain threshold. - */ - if (rt2x00queue_threshold(queue) || - !test_bit(ENTRY_TXD_BURST, &txdesc->flags)) - queue->rt2x00dev->ops->lib->kick_queue(queue); -} - -static void rt2x00queue_bar_check(struct queue_entry *entry) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct ieee80211_bar *bar = (void *) (entry->skb->data + - rt2x00dev->extra_tx_headroom); - struct rt2x00_bar_list_entry *bar_entry; - - if (likely(!ieee80211_is_back_req(bar->frame_control))) - return; - - bar_entry = kmalloc(sizeof(*bar_entry), GFP_ATOMIC); - - /* - * If the alloc fails we still send the BAR out but just don't track - * it in our bar list. And as a result we will report it to mac80211 - * back as failed. - */ - if (!bar_entry) - return; - - bar_entry->entry = entry; - bar_entry->block_acked = 0; - - /* - * Copy the relevant parts of the 802.11 BAR into out check list - * such that we can use RCU for less-overhead in the RX path since - * sending BARs and processing the according BlockAck should be - * the exception. - */ - memcpy(bar_entry->ra, bar->ra, sizeof(bar->ra)); - memcpy(bar_entry->ta, bar->ta, sizeof(bar->ta)); - bar_entry->control = bar->control; - bar_entry->start_seq_num = bar->start_seq_num; - - /* - * Insert BAR into our BAR check list. - */ - spin_lock_bh(&rt2x00dev->bar_list_lock); - list_add_tail_rcu(&bar_entry->list, &rt2x00dev->bar_list); - spin_unlock_bh(&rt2x00dev->bar_list_lock); -} - -int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, - struct ieee80211_sta *sta, bool local) -{ - struct ieee80211_tx_info *tx_info; - struct queue_entry *entry; - struct txentry_desc txdesc; - struct skb_frame_desc *skbdesc; - u8 rate_idx, rate_flags; - int ret = 0; - - /* - * Copy all TX descriptor information into txdesc, - * after that we are free to use the skb->cb array - * for our information. - */ - rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc, sta); - - /* - * All information is retrieved from the skb->cb array, - * now we should claim ownership of the driver part of that - * array, preserving the bitrate index and flags. - */ - tx_info = IEEE80211_SKB_CB(skb); - rate_idx = tx_info->control.rates[0].idx; - rate_flags = tx_info->control.rates[0].flags; - skbdesc = get_skb_frame_desc(skb); - memset(skbdesc, 0, sizeof(*skbdesc)); - skbdesc->tx_rate_idx = rate_idx; - skbdesc->tx_rate_flags = rate_flags; - - if (local) - skbdesc->flags |= SKBDESC_NOT_MAC80211; - - /* - * When hardware encryption is supported, and this frame - * is to be encrypted, we should strip the IV/EIV data from - * the frame so we can provide it to the driver separately. - */ - if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) && - !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) { - if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_COPY_IV)) - rt2x00crypto_tx_copy_iv(skb, &txdesc); - else - rt2x00crypto_tx_remove_iv(skb, &txdesc); - } - - /* - * When DMA allocation is required we should guarantee to the - * driver that the DMA is aligned to a 4-byte boundary. - * However some drivers require L2 padding to pad the payload - * rather then the header. This could be a requirement for - * PCI and USB devices, while header alignment only is valid - * for PCI devices. - */ - if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_L2PAD)) - rt2x00queue_insert_l2pad(skb, txdesc.header_length); - else if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_DMA)) - rt2x00queue_align_frame(skb); - - /* - * That function must be called with bh disabled. - */ - spin_lock(&queue->tx_lock); - - if (unlikely(rt2x00queue_full(queue))) { - rt2x00_err(queue->rt2x00dev, "Dropping frame due to full tx queue %d\n", - queue->qid); - ret = -ENOBUFS; - goto out; - } - - entry = rt2x00queue_get_entry(queue, Q_INDEX); - - if (unlikely(test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, - &entry->flags))) { - rt2x00_err(queue->rt2x00dev, - "Arrived at non-free entry in the non-full queue %d\n" - "Please file bug report to %s\n", - queue->qid, DRV_PROJECT); - ret = -EINVAL; - goto out; - } - - skbdesc->entry = entry; - entry->skb = skb; - - /* - * It could be possible that the queue was corrupted and this - * call failed. Since we always return NETDEV_TX_OK to mac80211, - * this frame will simply be dropped. - */ - if (unlikely(rt2x00queue_write_tx_data(entry, &txdesc))) { - clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); - entry->skb = NULL; - ret = -EIO; - goto out; - } - - /* - * Put BlockAckReqs into our check list for driver BA processing. - */ - rt2x00queue_bar_check(entry); - - set_bit(ENTRY_DATA_PENDING, &entry->flags); - - rt2x00queue_index_inc(entry, Q_INDEX); - rt2x00queue_write_tx_descriptor(entry, &txdesc); - rt2x00queue_kick_tx_queue(queue, &txdesc); - -out: - spin_unlock(&queue->tx_lock); - return ret; -} - -int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev, - struct ieee80211_vif *vif) -{ - struct rt2x00_intf *intf = vif_to_intf(vif); - - if (unlikely(!intf->beacon)) - return -ENOBUFS; - - /* - * Clean up the beacon skb. - */ - rt2x00queue_free_skb(intf->beacon); - - /* - * Clear beacon (single bssid devices don't need to clear the beacon - * since the beacon queue will get stopped anyway). - */ - if (rt2x00dev->ops->lib->clear_beacon) - rt2x00dev->ops->lib->clear_beacon(intf->beacon); - - return 0; -} - -int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, - struct ieee80211_vif *vif) -{ - struct rt2x00_intf *intf = vif_to_intf(vif); - struct skb_frame_desc *skbdesc; - struct txentry_desc txdesc; - - if (unlikely(!intf->beacon)) - return -ENOBUFS; - - /* - * Clean up the beacon skb. - */ - rt2x00queue_free_skb(intf->beacon); - - intf->beacon->skb = ieee80211_beacon_get(rt2x00dev->hw, vif); - if (!intf->beacon->skb) - return -ENOMEM; - - /* - * Copy all TX descriptor information into txdesc, - * after that we are free to use the skb->cb array - * for our information. - */ - rt2x00queue_create_tx_descriptor(rt2x00dev, intf->beacon->skb, &txdesc, NULL); - - /* - * Fill in skb descriptor - */ - skbdesc = get_skb_frame_desc(intf->beacon->skb); - memset(skbdesc, 0, sizeof(*skbdesc)); - skbdesc->entry = intf->beacon; - - /* - * Send beacon to hardware. - */ - rt2x00dev->ops->lib->write_beacon(intf->beacon, &txdesc); - - return 0; - -} - -bool rt2x00queue_for_each_entry(struct data_queue *queue, - enum queue_index start, - enum queue_index end, - void *data, - bool (*fn)(struct queue_entry *entry, - void *data)) -{ - unsigned long irqflags; - unsigned int index_start; - unsigned int index_end; - unsigned int i; - - if (unlikely(start >= Q_INDEX_MAX || end >= Q_INDEX_MAX)) { - rt2x00_err(queue->rt2x00dev, - "Entry requested from invalid index range (%d - %d)\n", - start, end); - return true; - } - - /* - * Only protect the range we are going to loop over, - * if during our loop a extra entry is set to pending - * it should not be kicked during this run, since it - * is part of another TX operation. - */ - spin_lock_irqsave(&queue->index_lock, irqflags); - index_start = queue->index[start]; - index_end = queue->index[end]; - spin_unlock_irqrestore(&queue->index_lock, irqflags); - - /* - * Start from the TX done pointer, this guarantees that we will - * send out all frames in the correct order. - */ - if (index_start < index_end) { - for (i = index_start; i < index_end; i++) { - if (fn(&queue->entries[i], data)) - return true; - } - } else { - for (i = index_start; i < queue->limit; i++) { - if (fn(&queue->entries[i], data)) - return true; - } - - for (i = 0; i < index_end; i++) { - if (fn(&queue->entries[i], data)) - return true; - } - } - - return false; -} -EXPORT_SYMBOL_GPL(rt2x00queue_for_each_entry); - -struct queue_entry *rt2x00queue_get_entry(struct data_queue *queue, - enum queue_index index) -{ - struct queue_entry *entry; - unsigned long irqflags; - - if (unlikely(index >= Q_INDEX_MAX)) { - rt2x00_err(queue->rt2x00dev, "Entry requested from invalid index type (%d)\n", - index); - return NULL; - } - - spin_lock_irqsave(&queue->index_lock, irqflags); - - entry = &queue->entries[queue->index[index]]; - - spin_unlock_irqrestore(&queue->index_lock, irqflags); - - return entry; -} -EXPORT_SYMBOL_GPL(rt2x00queue_get_entry); - -void rt2x00queue_index_inc(struct queue_entry *entry, enum queue_index index) -{ - struct data_queue *queue = entry->queue; - unsigned long irqflags; - - if (unlikely(index >= Q_INDEX_MAX)) { - rt2x00_err(queue->rt2x00dev, - "Index change on invalid index type (%d)\n", index); - return; - } - - spin_lock_irqsave(&queue->index_lock, irqflags); - - queue->index[index]++; - if (queue->index[index] >= queue->limit) - queue->index[index] = 0; - - entry->last_action = jiffies; - - if (index == Q_INDEX) { - queue->length++; - } else if (index == Q_INDEX_DONE) { - queue->length--; - queue->count++; - } - - spin_unlock_irqrestore(&queue->index_lock, irqflags); -} - -static void rt2x00queue_pause_queue_nocheck(struct data_queue *queue) -{ - switch (queue->qid) { - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - /* - * For TX queues, we have to disable the queue - * inside mac80211. - */ - ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid); - break; - default: - break; - } -} -void rt2x00queue_pause_queue(struct data_queue *queue) -{ - if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || - !test_bit(QUEUE_STARTED, &queue->flags) || - test_and_set_bit(QUEUE_PAUSED, &queue->flags)) - return; - - rt2x00queue_pause_queue_nocheck(queue); -} -EXPORT_SYMBOL_GPL(rt2x00queue_pause_queue); - -void rt2x00queue_unpause_queue(struct data_queue *queue) -{ - if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || - !test_bit(QUEUE_STARTED, &queue->flags) || - !test_and_clear_bit(QUEUE_PAUSED, &queue->flags)) - return; - - switch (queue->qid) { - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - /* - * For TX queues, we have to enable the queue - * inside mac80211. - */ - ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid); - break; - case QID_RX: - /* - * For RX we need to kick the queue now in order to - * receive frames. - */ - queue->rt2x00dev->ops->lib->kick_queue(queue); - default: - break; - } -} -EXPORT_SYMBOL_GPL(rt2x00queue_unpause_queue); - -void rt2x00queue_start_queue(struct data_queue *queue) -{ - mutex_lock(&queue->status_lock); - - if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || - test_and_set_bit(QUEUE_STARTED, &queue->flags)) { - mutex_unlock(&queue->status_lock); - return; - } - - set_bit(QUEUE_PAUSED, &queue->flags); - - queue->rt2x00dev->ops->lib->start_queue(queue); - - rt2x00queue_unpause_queue(queue); - - mutex_unlock(&queue->status_lock); -} -EXPORT_SYMBOL_GPL(rt2x00queue_start_queue); - -void rt2x00queue_stop_queue(struct data_queue *queue) -{ - mutex_lock(&queue->status_lock); - - if (!test_and_clear_bit(QUEUE_STARTED, &queue->flags)) { - mutex_unlock(&queue->status_lock); - return; - } - - rt2x00queue_pause_queue_nocheck(queue); - - queue->rt2x00dev->ops->lib->stop_queue(queue); - - mutex_unlock(&queue->status_lock); -} -EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue); - -void rt2x00queue_flush_queue(struct data_queue *queue, bool drop) -{ - bool tx_queue = - (queue->qid == QID_AC_VO) || - (queue->qid == QID_AC_VI) || - (queue->qid == QID_AC_BE) || - (queue->qid == QID_AC_BK); - - - /* - * If we are not supposed to drop any pending - * frames, this means we must force a start (=kick) - * to the queue to make sure the hardware will - * start transmitting. - */ - if (!drop && tx_queue) - queue->rt2x00dev->ops->lib->kick_queue(queue); - - /* - * Check if driver supports flushing, if that is the case we can - * defer the flushing to the driver. Otherwise we must use the - * alternative which just waits for the queue to become empty. - */ - if (likely(queue->rt2x00dev->ops->lib->flush_queue)) - queue->rt2x00dev->ops->lib->flush_queue(queue, drop); - - /* - * The queue flush has failed... - */ - if (unlikely(!rt2x00queue_empty(queue))) - rt2x00_warn(queue->rt2x00dev, "Queue %d failed to flush\n", - queue->qid); -} -EXPORT_SYMBOL_GPL(rt2x00queue_flush_queue); - -void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - - /* - * rt2x00queue_start_queue will call ieee80211_wake_queue - * for each queue after is has been properly initialized. - */ - tx_queue_for_each(rt2x00dev, queue) - rt2x00queue_start_queue(queue); - - rt2x00queue_start_queue(rt2x00dev->rx); -} -EXPORT_SYMBOL_GPL(rt2x00queue_start_queues); - -void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - - /* - * rt2x00queue_stop_queue will call ieee80211_stop_queue - * as well, but we are completely shutting doing everything - * now, so it is much safer to stop all TX queues at once, - * and use rt2x00queue_stop_queue for cleaning up. - */ - ieee80211_stop_queues(rt2x00dev->hw); - - tx_queue_for_each(rt2x00dev, queue) - rt2x00queue_stop_queue(queue); - - rt2x00queue_stop_queue(rt2x00dev->rx); -} -EXPORT_SYMBOL_GPL(rt2x00queue_stop_queues); - -void rt2x00queue_flush_queues(struct rt2x00_dev *rt2x00dev, bool drop) -{ - struct data_queue *queue; - - tx_queue_for_each(rt2x00dev, queue) - rt2x00queue_flush_queue(queue, drop); - - rt2x00queue_flush_queue(rt2x00dev->rx, drop); -} -EXPORT_SYMBOL_GPL(rt2x00queue_flush_queues); - -static void rt2x00queue_reset(struct data_queue *queue) -{ - unsigned long irqflags; - unsigned int i; - - spin_lock_irqsave(&queue->index_lock, irqflags); - - queue->count = 0; - queue->length = 0; - - for (i = 0; i < Q_INDEX_MAX; i++) - queue->index[i] = 0; - - spin_unlock_irqrestore(&queue->index_lock, irqflags); -} - -void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - unsigned int i; - - queue_for_each(rt2x00dev, queue) { - rt2x00queue_reset(queue); - - for (i = 0; i < queue->limit; i++) - rt2x00dev->ops->lib->clear_entry(&queue->entries[i]); - } -} - -static int rt2x00queue_alloc_entries(struct data_queue *queue) -{ - struct queue_entry *entries; - unsigned int entry_size; - unsigned int i; - - rt2x00queue_reset(queue); - - /* - * Allocate all queue entries. - */ - entry_size = sizeof(*entries) + queue->priv_size; - entries = kcalloc(queue->limit, entry_size, GFP_KERNEL); - if (!entries) - return -ENOMEM; - -#define QUEUE_ENTRY_PRIV_OFFSET(__base, __index, __limit, __esize, __psize) \ - (((char *)(__base)) + ((__limit) * (__esize)) + \ - ((__index) * (__psize))) - - for (i = 0; i < queue->limit; i++) { - entries[i].flags = 0; - entries[i].queue = queue; - entries[i].skb = NULL; - entries[i].entry_idx = i; - entries[i].priv_data = - QUEUE_ENTRY_PRIV_OFFSET(entries, i, queue->limit, - sizeof(*entries), queue->priv_size); - } - -#undef QUEUE_ENTRY_PRIV_OFFSET - - queue->entries = entries; - - return 0; -} - -static void rt2x00queue_free_skbs(struct data_queue *queue) -{ - unsigned int i; - - if (!queue->entries) - return; - - for (i = 0; i < queue->limit; i++) { - rt2x00queue_free_skb(&queue->entries[i]); - } -} - -static int rt2x00queue_alloc_rxskbs(struct data_queue *queue) -{ - unsigned int i; - struct sk_buff *skb; - - for (i = 0; i < queue->limit; i++) { - skb = rt2x00queue_alloc_rxskb(&queue->entries[i], GFP_KERNEL); - if (!skb) - return -ENOMEM; - queue->entries[i].skb = skb; - } - - return 0; -} - -int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - int status; - - status = rt2x00queue_alloc_entries(rt2x00dev->rx); - if (status) - goto exit; - - tx_queue_for_each(rt2x00dev, queue) { - status = rt2x00queue_alloc_entries(queue); - if (status) - goto exit; - } - - status = rt2x00queue_alloc_entries(rt2x00dev->bcn); - if (status) - goto exit; - - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE)) { - status = rt2x00queue_alloc_entries(rt2x00dev->atim); - if (status) - goto exit; - } - - status = rt2x00queue_alloc_rxskbs(rt2x00dev->rx); - if (status) - goto exit; - - return 0; - -exit: - rt2x00_err(rt2x00dev, "Queue entries allocation failed\n"); - - rt2x00queue_uninitialize(rt2x00dev); - - return status; -} - -void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - - rt2x00queue_free_skbs(rt2x00dev->rx); - - queue_for_each(rt2x00dev, queue) { - kfree(queue->entries); - queue->entries = NULL; - } -} - -static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, - struct data_queue *queue, enum data_queue_qid qid) -{ - mutex_init(&queue->status_lock); - spin_lock_init(&queue->tx_lock); - spin_lock_init(&queue->index_lock); - - queue->rt2x00dev = rt2x00dev; - queue->qid = qid; - queue->txop = 0; - queue->aifs = 2; - queue->cw_min = 5; - queue->cw_max = 10; - - rt2x00dev->ops->queue_init(queue); - - queue->threshold = DIV_ROUND_UP(queue->limit, 10); -} - -int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - enum data_queue_qid qid; - unsigned int req_atim = - rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE); - - /* - * We need the following queues: - * RX: 1 - * TX: ops->tx_queues - * Beacon: 1 - * Atim: 1 (if required) - */ - rt2x00dev->data_queues = 2 + rt2x00dev->ops->tx_queues + req_atim; - - queue = kcalloc(rt2x00dev->data_queues, sizeof(*queue), GFP_KERNEL); - if (!queue) { - rt2x00_err(rt2x00dev, "Queue allocation failed\n"); - return -ENOMEM; - } - - /* - * Initialize pointers - */ - rt2x00dev->rx = queue; - rt2x00dev->tx = &queue[1]; - rt2x00dev->bcn = &queue[1 + rt2x00dev->ops->tx_queues]; - rt2x00dev->atim = req_atim ? &queue[2 + rt2x00dev->ops->tx_queues] : NULL; - - /* - * Initialize queue parameters. - * RX: qid = QID_RX - * TX: qid = QID_AC_VO + index - * TX: cw_min: 2^5 = 32. - * TX: cw_max: 2^10 = 1024. - * BCN: qid = QID_BEACON - * ATIM: qid = QID_ATIM - */ - rt2x00queue_init(rt2x00dev, rt2x00dev->rx, QID_RX); - - qid = QID_AC_VO; - tx_queue_for_each(rt2x00dev, queue) - rt2x00queue_init(rt2x00dev, queue, qid++); - - rt2x00queue_init(rt2x00dev, rt2x00dev->bcn, QID_BEACON); - if (req_atim) - rt2x00queue_init(rt2x00dev, rt2x00dev->atim, QID_ATIM); - - return 0; -} - -void rt2x00queue_free(struct rt2x00_dev *rt2x00dev) -{ - kfree(rt2x00dev->rx); - rt2x00dev->rx = NULL; - rt2x00dev->tx = NULL; - rt2x00dev->bcn = NULL; -} diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h deleted file mode 100644 index 2233b911a1d7..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ /dev/null @@ -1,686 +0,0 @@ -/* - Copyright (C) 2004 - 2010 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00 - Abstract: rt2x00 queue datastructures and routines - */ - -#ifndef RT2X00QUEUE_H -#define RT2X00QUEUE_H - -#include - -/** - * DOC: Entry frame size - * - * Ralink PCI devices demand the Frame size to be a multiple of 128 bytes, - * for USB devices this restriction does not apply, but the value of - * 2432 makes sense since it is big enough to contain the maximum fragment - * size according to the ieee802.11 specs. - * The aggregation size depends on support from the driver, but should - * be something around 3840 bytes. - */ -#define DATA_FRAME_SIZE 2432 -#define MGMT_FRAME_SIZE 256 -#define AGGREGATION_SIZE 3840 - -/** - * enum data_queue_qid: Queue identification - * - * @QID_AC_VO: AC VO queue - * @QID_AC_VI: AC VI queue - * @QID_AC_BE: AC BE queue - * @QID_AC_BK: AC BK queue - * @QID_HCCA: HCCA queue - * @QID_MGMT: MGMT queue (prio queue) - * @QID_RX: RX queue - * @QID_OTHER: None of the above (don't use, only present for completeness) - * @QID_BEACON: Beacon queue (value unspecified, don't send it to device) - * @QID_ATIM: Atim queue (value unspecified, don't send it to device) - */ -enum data_queue_qid { - QID_AC_VO = 0, - QID_AC_VI = 1, - QID_AC_BE = 2, - QID_AC_BK = 3, - QID_HCCA = 4, - QID_MGMT = 13, - QID_RX = 14, - QID_OTHER = 15, - QID_BEACON, - QID_ATIM, -}; - -/** - * enum skb_frame_desc_flags: Flags for &struct skb_frame_desc - * - * @SKBDESC_DMA_MAPPED_RX: &skb_dma field has been mapped for RX - * @SKBDESC_DMA_MAPPED_TX: &skb_dma field has been mapped for TX - * @SKBDESC_IV_STRIPPED: Frame contained a IV/EIV provided by - * mac80211 but was stripped for processing by the driver. - * @SKBDESC_NOT_MAC80211: Frame didn't originate from mac80211, - * don't try to pass it back. - * @SKBDESC_DESC_IN_SKB: The descriptor is at the start of the - * skb, instead of in the desc field. - */ -enum skb_frame_desc_flags { - SKBDESC_DMA_MAPPED_RX = 1 << 0, - SKBDESC_DMA_MAPPED_TX = 1 << 1, - SKBDESC_IV_STRIPPED = 1 << 2, - SKBDESC_NOT_MAC80211 = 1 << 3, - SKBDESC_DESC_IN_SKB = 1 << 4, -}; - -/** - * struct skb_frame_desc: Descriptor information for the skb buffer - * - * This structure is placed over the driver_data array, this means that - * this structure should not exceed the size of that array (40 bytes). - * - * @flags: Frame flags, see &enum skb_frame_desc_flags. - * @desc_len: Length of the frame descriptor. - * @tx_rate_idx: the index of the TX rate, used for TX status reporting - * @tx_rate_flags: the TX rate flags, used for TX status reporting - * @desc: Pointer to descriptor part of the frame. - * Note that this pointer could point to something outside - * of the scope of the skb->data pointer. - * @iv: IV/EIV data used during encryption/decryption. - * @skb_dma: (PCI-only) the DMA address associated with the sk buffer. - * @entry: The entry to which this sk buffer belongs. - */ -struct skb_frame_desc { - u8 flags; - - u8 desc_len; - u8 tx_rate_idx; - u8 tx_rate_flags; - - void *desc; - - __le32 iv[2]; - - dma_addr_t skb_dma; - - struct queue_entry *entry; -}; - -/** - * get_skb_frame_desc - Obtain the rt2x00 frame descriptor from a sk_buff. - * @skb: &struct sk_buff from where we obtain the &struct skb_frame_desc - */ -static inline struct skb_frame_desc* get_skb_frame_desc(struct sk_buff *skb) -{ - BUILD_BUG_ON(sizeof(struct skb_frame_desc) > - IEEE80211_TX_INFO_DRIVER_DATA_SIZE); - return (struct skb_frame_desc *)&IEEE80211_SKB_CB(skb)->driver_data; -} - -/** - * enum rxdone_entry_desc_flags: Flags for &struct rxdone_entry_desc - * - * @RXDONE_SIGNAL_PLCP: Signal field contains the plcp value. - * @RXDONE_SIGNAL_BITRATE: Signal field contains the bitrate value. - * @RXDONE_SIGNAL_MCS: Signal field contains the mcs value. - * @RXDONE_MY_BSS: Does this frame originate from device's BSS. - * @RXDONE_CRYPTO_IV: Driver provided IV/EIV data. - * @RXDONE_CRYPTO_ICV: Driver provided ICV data. - * @RXDONE_L2PAD: 802.11 payload has been padded to 4-byte boundary. - */ -enum rxdone_entry_desc_flags { - RXDONE_SIGNAL_PLCP = BIT(0), - RXDONE_SIGNAL_BITRATE = BIT(1), - RXDONE_SIGNAL_MCS = BIT(2), - RXDONE_MY_BSS = BIT(3), - RXDONE_CRYPTO_IV = BIT(4), - RXDONE_CRYPTO_ICV = BIT(5), - RXDONE_L2PAD = BIT(6), -}; - -/** - * RXDONE_SIGNAL_MASK - Define to mask off all &rxdone_entry_desc_flags flags - * except for the RXDONE_SIGNAL_* flags. This is useful to convert the dev_flags - * from &rxdone_entry_desc to a signal value type. - */ -#define RXDONE_SIGNAL_MASK \ - ( RXDONE_SIGNAL_PLCP | RXDONE_SIGNAL_BITRATE | RXDONE_SIGNAL_MCS ) - -/** - * struct rxdone_entry_desc: RX Entry descriptor - * - * Summary of information that has been read from the RX frame descriptor. - * - * @timestamp: RX Timestamp - * @signal: Signal of the received frame. - * @rssi: RSSI of the received frame. - * @size: Data size of the received frame. - * @flags: MAC80211 receive flags (See &enum mac80211_rx_flags). - * @dev_flags: Ralink receive flags (See &enum rxdone_entry_desc_flags). - * @rate_mode: Rate mode (See @enum rate_modulation). - * @cipher: Cipher type used during decryption. - * @cipher_status: Decryption status. - * @iv: IV/EIV data used during decryption. - * @icv: ICV data used during decryption. - */ -struct rxdone_entry_desc { - u64 timestamp; - int signal; - int rssi; - int size; - int flags; - int dev_flags; - u16 rate_mode; - u8 cipher; - u8 cipher_status; - - __le32 iv[2]; - __le32 icv; -}; - -/** - * enum txdone_entry_desc_flags: Flags for &struct txdone_entry_desc - * - * Every txdone report has to contain the basic result of the - * transmission, either &TXDONE_UNKNOWN, &TXDONE_SUCCESS or - * &TXDONE_FAILURE. The flag &TXDONE_FALLBACK can be used in - * conjunction with all of these flags but should only be set - * if retires > 0. The flag &TXDONE_EXCESSIVE_RETRY can only be used - * in conjunction with &TXDONE_FAILURE. - * - * @TXDONE_UNKNOWN: Hardware could not determine success of transmission. - * @TXDONE_SUCCESS: Frame was successfully send - * @TXDONE_FALLBACK: Hardware used fallback rates for retries - * @TXDONE_FAILURE: Frame was not successfully send - * @TXDONE_EXCESSIVE_RETRY: In addition to &TXDONE_FAILURE, the - * frame transmission failed due to excessive retries. - */ -enum txdone_entry_desc_flags { - TXDONE_UNKNOWN, - TXDONE_SUCCESS, - TXDONE_FALLBACK, - TXDONE_FAILURE, - TXDONE_EXCESSIVE_RETRY, - TXDONE_AMPDU, -}; - -/** - * struct txdone_entry_desc: TX done entry descriptor - * - * Summary of information that has been read from the TX frame descriptor - * after the device is done with transmission. - * - * @flags: TX done flags (See &enum txdone_entry_desc_flags). - * @retry: Retry count. - */ -struct txdone_entry_desc { - unsigned long flags; - int retry; -}; - -/** - * enum txentry_desc_flags: Status flags for TX entry descriptor - * - * @ENTRY_TXD_RTS_FRAME: This frame is a RTS frame. - * @ENTRY_TXD_CTS_FRAME: This frame is a CTS-to-self frame. - * @ENTRY_TXD_GENERATE_SEQ: This frame requires sequence counter. - * @ENTRY_TXD_FIRST_FRAGMENT: This is the first frame. - * @ENTRY_TXD_MORE_FRAG: This frame is followed by another fragment. - * @ENTRY_TXD_REQ_TIMESTAMP: Require timestamp to be inserted. - * @ENTRY_TXD_BURST: This frame belongs to the same burst event. - * @ENTRY_TXD_ACK: An ACK is required for this frame. - * @ENTRY_TXD_RETRY_MODE: When set, the long retry count is used. - * @ENTRY_TXD_ENCRYPT: This frame should be encrypted. - * @ENTRY_TXD_ENCRYPT_PAIRWISE: Use pairwise key table (instead of shared). - * @ENTRY_TXD_ENCRYPT_IV: Generate IV/EIV in hardware. - * @ENTRY_TXD_ENCRYPT_MMIC: Generate MIC in hardware. - * @ENTRY_TXD_HT_AMPDU: This frame is part of an AMPDU. - * @ENTRY_TXD_HT_BW_40: Use 40MHz Bandwidth. - * @ENTRY_TXD_HT_SHORT_GI: Use short GI. - * @ENTRY_TXD_HT_MIMO_PS: The receiving STA is in dynamic SM PS mode. - */ -enum txentry_desc_flags { - ENTRY_TXD_RTS_FRAME, - ENTRY_TXD_CTS_FRAME, - ENTRY_TXD_GENERATE_SEQ, - ENTRY_TXD_FIRST_FRAGMENT, - ENTRY_TXD_MORE_FRAG, - ENTRY_TXD_REQ_TIMESTAMP, - ENTRY_TXD_BURST, - ENTRY_TXD_ACK, - ENTRY_TXD_RETRY_MODE, - ENTRY_TXD_ENCRYPT, - ENTRY_TXD_ENCRYPT_PAIRWISE, - ENTRY_TXD_ENCRYPT_IV, - ENTRY_TXD_ENCRYPT_MMIC, - ENTRY_TXD_HT_AMPDU, - ENTRY_TXD_HT_BW_40, - ENTRY_TXD_HT_SHORT_GI, - ENTRY_TXD_HT_MIMO_PS, -}; - -/** - * struct txentry_desc: TX Entry descriptor - * - * Summary of information for the frame descriptor before sending a TX frame. - * - * @flags: Descriptor flags (See &enum queue_entry_flags). - * @length: Length of the entire frame. - * @header_length: Length of 802.11 header. - * @length_high: PLCP length high word. - * @length_low: PLCP length low word. - * @signal: PLCP signal. - * @service: PLCP service. - * @msc: MCS. - * @stbc: Use Space Time Block Coding (only available for MCS rates < 8). - * @ba_size: Size of the recepients RX reorder buffer - 1. - * @rate_mode: Rate mode (See @enum rate_modulation). - * @mpdu_density: MDPU density. - * @retry_limit: Max number of retries. - * @ifs: IFS value. - * @txop: IFS value for 11n capable chips. - * @cipher: Cipher type used for encryption. - * @key_idx: Key index used for encryption. - * @iv_offset: Position where IV should be inserted by hardware. - * @iv_len: Length of IV data. - */ -struct txentry_desc { - unsigned long flags; - - u16 length; - u16 header_length; - - union { - struct { - u16 length_high; - u16 length_low; - u16 signal; - u16 service; - enum ifs ifs; - } plcp; - - struct { - u16 mcs; - u8 stbc; - u8 ba_size; - u8 mpdu_density; - enum txop txop; - int wcid; - } ht; - } u; - - enum rate_modulation rate_mode; - - short retry_limit; - - enum cipher cipher; - u16 key_idx; - u16 iv_offset; - u16 iv_len; -}; - -/** - * enum queue_entry_flags: Status flags for queue entry - * - * @ENTRY_BCN_ASSIGNED: This entry has been assigned to an interface. - * As long as this bit is set, this entry may only be touched - * through the interface structure. - * @ENTRY_OWNER_DEVICE_DATA: This entry is owned by the device for data - * transfer (either TX or RX depending on the queue). The entry should - * only be touched after the device has signaled it is done with it. - * @ENTRY_DATA_PENDING: This entry contains a valid frame and is waiting - * for the signal to start sending. - * @ENTRY_DATA_IO_FAILED: Hardware indicated that an IO error occurred - * while transferring the data to the hardware. No TX status report will - * be expected from the hardware. - * @ENTRY_DATA_STATUS_PENDING: The entry has been send to the device and - * returned. It is now waiting for the status reporting before the - * entry can be reused again. - */ -enum queue_entry_flags { - ENTRY_BCN_ASSIGNED, - ENTRY_BCN_ENABLED, - ENTRY_OWNER_DEVICE_DATA, - ENTRY_DATA_PENDING, - ENTRY_DATA_IO_FAILED, - ENTRY_DATA_STATUS_PENDING, - ENTRY_DATA_STATUS_SET, -}; - -/** - * struct queue_entry: Entry inside the &struct data_queue - * - * @flags: Entry flags, see &enum queue_entry_flags. - * @last_action: Timestamp of last change. - * @queue: The data queue (&struct data_queue) to which this entry belongs. - * @skb: The buffer which is currently being transmitted (for TX queue), - * or used to directly receive data in (for RX queue). - * @entry_idx: The entry index number. - * @priv_data: Private data belonging to this queue entry. The pointer - * points to data specific to a particular driver and queue type. - * @status: Device specific status - */ -struct queue_entry { - unsigned long flags; - unsigned long last_action; - - struct data_queue *queue; - - struct sk_buff *skb; - - unsigned int entry_idx; - - u32 status; - - void *priv_data; -}; - -/** - * enum queue_index: Queue index type - * - * @Q_INDEX: Index pointer to the current entry in the queue, if this entry is - * owned by the hardware then the queue is considered to be full. - * @Q_INDEX_DMA_DONE: Index pointer for the next entry which will have been - * transferred to the hardware. - * @Q_INDEX_DONE: Index pointer to the next entry which will be completed by - * the hardware and for which we need to run the txdone handler. If this - * entry is not owned by the hardware the queue is considered to be empty. - * @Q_INDEX_MAX: Keep last, used in &struct data_queue to determine the size - * of the index array. - */ -enum queue_index { - Q_INDEX, - Q_INDEX_DMA_DONE, - Q_INDEX_DONE, - Q_INDEX_MAX, -}; - -/** - * enum data_queue_flags: Status flags for data queues - * - * @QUEUE_STARTED: The queue has been started. Fox RX queues this means the - * device might be DMA'ing skbuffers. TX queues will accept skbuffers to - * be transmitted and beacon queues will start beaconing the configured - * beacons. - * @QUEUE_PAUSED: The queue has been started but is currently paused. - * When this bit is set, the queue has been stopped in mac80211, - * preventing new frames to be enqueued. However, a few frames - * might still appear shortly after the pausing... - */ -enum data_queue_flags { - QUEUE_STARTED, - QUEUE_PAUSED, -}; - -/** - * struct data_queue: Data queue - * - * @rt2x00dev: Pointer to main &struct rt2x00dev where this queue belongs to. - * @entries: Base address of the &struct queue_entry which are - * part of this queue. - * @qid: The queue identification, see &enum data_queue_qid. - * @flags: Entry flags, see &enum queue_entry_flags. - * @status_lock: The mutex for protecting the start/stop/flush - * handling on this queue. - * @tx_lock: Spinlock to serialize tx operations on this queue. - * @index_lock: Spinlock to protect index handling. Whenever @index, @index_done or - * @index_crypt needs to be changed this lock should be grabbed to prevent - * index corruption due to concurrency. - * @count: Number of frames handled in the queue. - * @limit: Maximum number of entries in the queue. - * @threshold: Minimum number of free entries before queue is kicked by force. - * @length: Number of frames in queue. - * @index: Index pointers to entry positions in the queue, - * use &enum queue_index to get a specific index field. - * @txop: maximum burst time. - * @aifs: The aifs value for outgoing frames (field ignored in RX queue). - * @cw_min: The cw min value for outgoing frames (field ignored in RX queue). - * @cw_max: The cw max value for outgoing frames (field ignored in RX queue). - * @data_size: Maximum data size for the frames in this queue. - * @desc_size: Hardware descriptor size for the data in this queue. - * @priv_size: Size of per-queue_entry private data. - * @usb_endpoint: Device endpoint used for communication (USB only) - * @usb_maxpacket: Max packet size for given endpoint (USB only) - */ -struct data_queue { - struct rt2x00_dev *rt2x00dev; - struct queue_entry *entries; - - enum data_queue_qid qid; - unsigned long flags; - - struct mutex status_lock; - spinlock_t tx_lock; - spinlock_t index_lock; - - unsigned int count; - unsigned short limit; - unsigned short threshold; - unsigned short length; - unsigned short index[Q_INDEX_MAX]; - - unsigned short txop; - unsigned short aifs; - unsigned short cw_min; - unsigned short cw_max; - - unsigned short data_size; - unsigned char desc_size; - unsigned char winfo_size; - unsigned short priv_size; - - unsigned short usb_endpoint; - unsigned short usb_maxpacket; -}; - -/** - * queue_end - Return pointer to the last queue (HELPER MACRO). - * @__dev: Pointer to &struct rt2x00_dev - * - * Using the base rx pointer and the maximum number of available queues, - * this macro will return the address of 1 position beyond the end of the - * queues array. - */ -#define queue_end(__dev) \ - &(__dev)->rx[(__dev)->data_queues] - -/** - * tx_queue_end - Return pointer to the last TX queue (HELPER MACRO). - * @__dev: Pointer to &struct rt2x00_dev - * - * Using the base tx pointer and the maximum number of available TX - * queues, this macro will return the address of 1 position beyond - * the end of the TX queue array. - */ -#define tx_queue_end(__dev) \ - &(__dev)->tx[(__dev)->ops->tx_queues] - -/** - * queue_next - Return pointer to next queue in list (HELPER MACRO). - * @__queue: Current queue for which we need the next queue - * - * Using the current queue address we take the address directly - * after the queue to take the next queue. Note that this macro - * should be used carefully since it does not protect against - * moving past the end of the list. (See macros &queue_end and - * &tx_queue_end for determining the end of the queue). - */ -#define queue_next(__queue) \ - &(__queue)[1] - -/** - * queue_loop - Loop through the queues within a specific range (HELPER MACRO). - * @__entry: Pointer where the current queue entry will be stored in. - * @__start: Start queue pointer. - * @__end: End queue pointer. - * - * This macro will loop through all queues between &__start and &__end. - */ -#define queue_loop(__entry, __start, __end) \ - for ((__entry) = (__start); \ - prefetch(queue_next(__entry)), (__entry) != (__end);\ - (__entry) = queue_next(__entry)) - -/** - * queue_for_each - Loop through all queues - * @__dev: Pointer to &struct rt2x00_dev - * @__entry: Pointer where the current queue entry will be stored in. - * - * This macro will loop through all available queues. - */ -#define queue_for_each(__dev, __entry) \ - queue_loop(__entry, (__dev)->rx, queue_end(__dev)) - -/** - * tx_queue_for_each - Loop through the TX queues - * @__dev: Pointer to &struct rt2x00_dev - * @__entry: Pointer where the current queue entry will be stored in. - * - * This macro will loop through all TX related queues excluding - * the Beacon and Atim queues. - */ -#define tx_queue_for_each(__dev, __entry) \ - queue_loop(__entry, (__dev)->tx, tx_queue_end(__dev)) - -/** - * txall_queue_for_each - Loop through all TX related queues - * @__dev: Pointer to &struct rt2x00_dev - * @__entry: Pointer where the current queue entry will be stored in. - * - * This macro will loop through all TX related queues including - * the Beacon and Atim queues. - */ -#define txall_queue_for_each(__dev, __entry) \ - queue_loop(__entry, (__dev)->tx, queue_end(__dev)) - -/** - * rt2x00queue_for_each_entry - Loop through all entries in the queue - * @queue: Pointer to @data_queue - * @start: &enum queue_index Pointer to start index - * @end: &enum queue_index Pointer to end index - * @data: Data to pass to the callback function - * @fn: The function to call for each &struct queue_entry - * - * This will walk through all entries in the queue, in chronological - * order. This means it will start at the current @start pointer - * and will walk through the queue until it reaches the @end pointer. - * - * If fn returns true for an entry rt2x00queue_for_each_entry will stop - * processing and return true as well. - */ -bool rt2x00queue_for_each_entry(struct data_queue *queue, - enum queue_index start, - enum queue_index end, - void *data, - bool (*fn)(struct queue_entry *entry, - void *data)); - -/** - * rt2x00queue_empty - Check if the queue is empty. - * @queue: Queue to check if empty. - */ -static inline int rt2x00queue_empty(struct data_queue *queue) -{ - return queue->length == 0; -} - -/** - * rt2x00queue_full - Check if the queue is full. - * @queue: Queue to check if full. - */ -static inline int rt2x00queue_full(struct data_queue *queue) -{ - return queue->length == queue->limit; -} - -/** - * rt2x00queue_free - Check the number of available entries in queue. - * @queue: Queue to check. - */ -static inline int rt2x00queue_available(struct data_queue *queue) -{ - return queue->limit - queue->length; -} - -/** - * rt2x00queue_threshold - Check if the queue is below threshold - * @queue: Queue to check. - */ -static inline int rt2x00queue_threshold(struct data_queue *queue) -{ - return rt2x00queue_available(queue) < queue->threshold; -} -/** - * rt2x00queue_dma_timeout - Check if a timeout occurred for DMA transfers - * @entry: Queue entry to check. - */ -static inline int rt2x00queue_dma_timeout(struct queue_entry *entry) -{ - if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) - return false; - return time_after(jiffies, entry->last_action + msecs_to_jiffies(100)); -} - -/** - * _rt2x00_desc_read - Read a word from the hardware descriptor. - * @desc: Base descriptor address - * @word: Word index from where the descriptor should be read. - * @value: Address where the descriptor value should be written into. - */ -static inline void _rt2x00_desc_read(__le32 *desc, const u8 word, __le32 *value) -{ - *value = desc[word]; -} - -/** - * rt2x00_desc_read - Read a word from the hardware descriptor, this - * function will take care of the byte ordering. - * @desc: Base descriptor address - * @word: Word index from where the descriptor should be read. - * @value: Address where the descriptor value should be written into. - */ -static inline void rt2x00_desc_read(__le32 *desc, const u8 word, u32 *value) -{ - __le32 tmp; - _rt2x00_desc_read(desc, word, &tmp); - *value = le32_to_cpu(tmp); -} - -/** - * rt2x00_desc_write - write a word to the hardware descriptor, this - * function will take care of the byte ordering. - * @desc: Base descriptor address - * @word: Word index from where the descriptor should be written. - * @value: Value that should be written into the descriptor. - */ -static inline void _rt2x00_desc_write(__le32 *desc, const u8 word, __le32 value) -{ - desc[word] = value; -} - -/** - * rt2x00_desc_write - write a word to the hardware descriptor. - * @desc: Base descriptor address - * @word: Word index from where the descriptor should be written. - * @value: Value that should be written into the descriptor. - */ -static inline void rt2x00_desc_write(__le32 *desc, const u8 word, u32 value) -{ - _rt2x00_desc_write(desc, word, cpu_to_le32(value)); -} - -#endif /* RT2X00QUEUE_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00reg.h b/drivers/net/wireless/rt2x00/rt2x00reg.h deleted file mode 100644 index 3cc541d13d67..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00reg.h +++ /dev/null @@ -1,277 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00 - Abstract: rt2x00 generic register information. - */ - -#ifndef RT2X00REG_H -#define RT2X00REG_H - -/* - * RX crypto status - */ -enum rx_crypto { - RX_CRYPTO_SUCCESS = 0, - RX_CRYPTO_FAIL_ICV = 1, - RX_CRYPTO_FAIL_MIC = 2, - RX_CRYPTO_FAIL_KEY = 3, -}; - -/* - * Antenna values - */ -enum antenna { - ANTENNA_SW_DIVERSITY = 0, - ANTENNA_A = 1, - ANTENNA_B = 2, - ANTENNA_HW_DIVERSITY = 3, -}; - -/* - * Led mode values. - */ -enum led_mode { - LED_MODE_DEFAULT = 0, - LED_MODE_TXRX_ACTIVITY = 1, - LED_MODE_SIGNAL_STRENGTH = 2, - LED_MODE_ASUS = 3, - LED_MODE_ALPHA = 4, -}; - -/* - * TSF sync values - */ -enum tsf_sync { - TSF_SYNC_NONE = 0, - TSF_SYNC_INFRA = 1, - TSF_SYNC_ADHOC = 2, - TSF_SYNC_AP_NONE = 3, -}; - -/* - * Device states - */ -enum dev_state { - STATE_DEEP_SLEEP = 0, - STATE_SLEEP = 1, - STATE_STANDBY = 2, - STATE_AWAKE = 3, - -/* - * Additional device states, these values are - * not strict since they are not directly passed - * into the device. - */ - STATE_RADIO_ON, - STATE_RADIO_OFF, - STATE_RADIO_IRQ_ON, - STATE_RADIO_IRQ_OFF, -}; - -/* - * IFS backoff values - */ -enum ifs { - IFS_BACKOFF = 0, - IFS_SIFS = 1, - IFS_NEW_BACKOFF = 2, - IFS_NONE = 3, -}; - -/* - * IFS backoff values for HT devices - */ -enum txop { - TXOP_HTTXOP = 0, - TXOP_PIFS = 1, - TXOP_SIFS = 2, - TXOP_BACKOFF = 3, -}; - -/* - * Cipher types for hardware encryption - */ -enum cipher { - CIPHER_NONE = 0, - CIPHER_WEP64 = 1, - CIPHER_WEP128 = 2, - CIPHER_TKIP = 3, - CIPHER_AES = 4, -/* - * The following fields were added by rt61pci and rt73usb. - */ - CIPHER_CKIP64 = 5, - CIPHER_CKIP128 = 6, - CIPHER_TKIP_NO_MIC = 7, /* Don't send to device */ - -/* - * Max cipher type. - * Note that CIPHER_NONE isn't counted, and CKIP64 and CKIP128 - * are excluded due to limitations in mac80211. - */ - CIPHER_MAX = 4, -}; - -/* - * Rate modulations - */ -enum rate_modulation { - RATE_MODE_CCK = 0, - RATE_MODE_OFDM = 1, - RATE_MODE_HT_MIX = 2, - RATE_MODE_HT_GREENFIELD = 3, -}; - -/* - * Firmware validation error codes - */ -enum firmware_errors { - FW_OK, - FW_BAD_CRC, - FW_BAD_LENGTH, - FW_BAD_VERSION, -}; - -/* - * Register handlers. - * We store the position of a register field inside a field structure, - * This will simplify the process of setting and reading a certain field - * inside the register while making sure the process remains byte order safe. - */ -struct rt2x00_field8 { - u8 bit_offset; - u8 bit_mask; -}; - -struct rt2x00_field16 { - u16 bit_offset; - u16 bit_mask; -}; - -struct rt2x00_field32 { - u32 bit_offset; - u32 bit_mask; -}; - -/* - * Power of two check, this will check - * if the mask that has been given contains and contiguous set of bits. - * Note that we cannot use the is_power_of_2() function since this - * check must be done at compile-time. - */ -#define is_power_of_two(x) ( !((x) & ((x)-1)) ) -#define low_bit_mask(x) ( ((x)-1) & ~(x) ) -#define is_valid_mask(x) is_power_of_two(1LU + (x) + low_bit_mask(x)) - -/* - * Macros to find first set bit in a variable. - * These macros behave the same as the __ffs() functions but - * the most important difference that this is done during - * compile-time rather then run-time. - */ -#define compile_ffs2(__x) \ - __builtin_choose_expr(((__x) & 0x1), 0, 1) - -#define compile_ffs4(__x) \ - __builtin_choose_expr(((__x) & 0x3), \ - (compile_ffs2((__x))), \ - (compile_ffs2((__x) >> 2) + 2)) - -#define compile_ffs8(__x) \ - __builtin_choose_expr(((__x) & 0xf), \ - (compile_ffs4((__x))), \ - (compile_ffs4((__x) >> 4) + 4)) - -#define compile_ffs16(__x) \ - __builtin_choose_expr(((__x) & 0xff), \ - (compile_ffs8((__x))), \ - (compile_ffs8((__x) >> 8) + 8)) - -#define compile_ffs32(__x) \ - __builtin_choose_expr(((__x) & 0xffff), \ - (compile_ffs16((__x))), \ - (compile_ffs16((__x) >> 16) + 16)) - -/* - * This macro will check the requirements for the FIELD{8,16,32} macros - * The mask should be a constant non-zero contiguous set of bits which - * does not exceed the given typelimit. - */ -#define FIELD_CHECK(__mask, __type) \ - BUILD_BUG_ON(!(__mask) || \ - !is_valid_mask(__mask) || \ - (__mask) != (__type)(__mask)) \ - -#define FIELD8(__mask) \ -({ \ - FIELD_CHECK(__mask, u8); \ - (struct rt2x00_field8) { \ - compile_ffs8(__mask), (__mask) \ - }; \ -}) - -#define FIELD16(__mask) \ -({ \ - FIELD_CHECK(__mask, u16); \ - (struct rt2x00_field16) { \ - compile_ffs16(__mask), (__mask) \ - }; \ -}) - -#define FIELD32(__mask) \ -({ \ - FIELD_CHECK(__mask, u32); \ - (struct rt2x00_field32) { \ - compile_ffs32(__mask), (__mask) \ - }; \ -}) - -#define SET_FIELD(__reg, __type, __field, __value)\ -({ \ - typecheck(__type, __field); \ - *(__reg) &= ~((__field).bit_mask); \ - *(__reg) |= ((__value) << \ - ((__field).bit_offset)) & \ - ((__field).bit_mask); \ -}) - -#define GET_FIELD(__reg, __type, __field) \ -({ \ - typecheck(__type, __field); \ - ((__reg) & ((__field).bit_mask)) >> \ - ((__field).bit_offset); \ -}) - -#define rt2x00_set_field32(__reg, __field, __value) \ - SET_FIELD(__reg, struct rt2x00_field32, __field, __value) -#define rt2x00_get_field32(__reg, __field) \ - GET_FIELD(__reg, struct rt2x00_field32, __field) - -#define rt2x00_set_field16(__reg, __field, __value) \ - SET_FIELD(__reg, struct rt2x00_field16, __field, __value) -#define rt2x00_get_field16(__reg, __field) \ - GET_FIELD(__reg, struct rt2x00_field16, __field) - -#define rt2x00_set_field8(__reg, __field, __value) \ - SET_FIELD(__reg, struct rt2x00_field8, __field, __value) -#define rt2x00_get_field8(__reg, __field) \ - GET_FIELD(__reg, struct rt2x00_field8, __field) - -#endif /* RT2X00REG_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.c b/drivers/net/wireless/rt2x00/rt2x00soc.c deleted file mode 100644 index 69a0cdadb07f..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00soc.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - Copyright (C) 2004 - 2009 Felix Fietkau - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00soc - Abstract: rt2x00 generic soc device routines. - */ - -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00soc.h" - -static void rt2x00soc_free_reg(struct rt2x00_dev *rt2x00dev) -{ - kfree(rt2x00dev->rf); - rt2x00dev->rf = NULL; - - kfree(rt2x00dev->eeprom); - rt2x00dev->eeprom = NULL; - - iounmap(rt2x00dev->csr.base); -} - -static int rt2x00soc_alloc_reg(struct rt2x00_dev *rt2x00dev) -{ - struct platform_device *pdev = to_platform_device(rt2x00dev->dev); - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - rt2x00dev->csr.base = ioremap(res->start, resource_size(res)); - if (!rt2x00dev->csr.base) - return -ENOMEM; - - rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); - if (!rt2x00dev->eeprom) - goto exit; - - rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); - if (!rt2x00dev->rf) - goto exit; - - return 0; - -exit: - rt2x00_probe_err("Failed to allocate registers\n"); - rt2x00soc_free_reg(rt2x00dev); - - return -ENOMEM; -} - -int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops) -{ - struct ieee80211_hw *hw; - struct rt2x00_dev *rt2x00dev; - int retval; - - hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); - if (!hw) { - rt2x00_probe_err("Failed to allocate hardware\n"); - return -ENOMEM; - } - - platform_set_drvdata(pdev, hw); - - rt2x00dev = hw->priv; - rt2x00dev->dev = &pdev->dev; - rt2x00dev->ops = ops; - rt2x00dev->hw = hw; - rt2x00dev->irq = platform_get_irq(pdev, 0); - rt2x00dev->name = pdev->dev.driver->name; - - rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); - - retval = rt2x00soc_alloc_reg(rt2x00dev); - if (retval) - goto exit_free_device; - - retval = rt2x00lib_probe_dev(rt2x00dev); - if (retval) - goto exit_free_reg; - - return 0; - -exit_free_reg: - rt2x00soc_free_reg(rt2x00dev); - -exit_free_device: - ieee80211_free_hw(hw); - - return retval; -} -EXPORT_SYMBOL_GPL(rt2x00soc_probe); - -int rt2x00soc_remove(struct platform_device *pdev) -{ - struct ieee80211_hw *hw = platform_get_drvdata(pdev); - struct rt2x00_dev *rt2x00dev = hw->priv; - - /* - * Free all allocated data. - */ - rt2x00lib_remove_dev(rt2x00dev); - rt2x00soc_free_reg(rt2x00dev); - ieee80211_free_hw(hw); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00soc_remove); - -#ifdef CONFIG_PM -int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct ieee80211_hw *hw = platform_get_drvdata(pdev); - struct rt2x00_dev *rt2x00dev = hw->priv; - - return rt2x00lib_suspend(rt2x00dev, state); -} -EXPORT_SYMBOL_GPL(rt2x00soc_suspend); - -int rt2x00soc_resume(struct platform_device *pdev) -{ - struct ieee80211_hw *hw = platform_get_drvdata(pdev); - struct rt2x00_dev *rt2x00dev = hw->priv; - - return rt2x00lib_resume(rt2x00dev); -} -EXPORT_SYMBOL_GPL(rt2x00soc_resume); -#endif /* CONFIG_PM */ - -/* - * rt2x00soc module information. - */ -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("rt2x00 soc library"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.h b/drivers/net/wireless/rt2x00/rt2x00soc.h deleted file mode 100644 index 9948d355e9a4..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00soc.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00soc - Abstract: Data structures for the rt2x00soc module. - */ - -#ifndef RT2X00SOC_H -#define RT2X00SOC_H - -/* - * SoC driver handlers. - */ -int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops); -int rt2x00soc_remove(struct platform_device *pdev); -#ifdef CONFIG_PM -int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state); -int rt2x00soc_resume(struct platform_device *pdev); -#else -#define rt2x00soc_suspend NULL -#define rt2x00soc_resume NULL -#endif /* CONFIG_PM */ - -#endif /* RT2X00SOC_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c deleted file mode 100644 index 7627af6098eb..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ /dev/null @@ -1,884 +0,0 @@ -/* - Copyright (C) 2010 Willow Garage - Copyright (C) 2004 - 2010 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00usb - Abstract: rt2x00 generic usb device routines. - */ - -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00usb.h" - -/* - * Interfacing with the HW. - */ -int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, - const u8 request, const u8 requesttype, - const u16 offset, const u16 value, - void *buffer, const u16 buffer_length, - const int timeout) -{ - struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); - int status; - unsigned int pipe = - (requesttype == USB_VENDOR_REQUEST_IN) ? - usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0); - unsigned long expire = jiffies + msecs_to_jiffies(timeout); - - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return -ENODEV; - - do { - status = usb_control_msg(usb_dev, pipe, request, requesttype, - value, offset, buffer, buffer_length, - timeout / 2); - if (status >= 0) - return 0; - - if (status == -ENODEV) { - /* Device has disappeared. */ - clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); - break; - } - } while (time_before(jiffies, expire)); - - rt2x00_err(rt2x00dev, - "Vendor Request 0x%02x failed for offset 0x%04x with error %d\n", - request, offset, status); - - return status; -} -EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request); - -int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev, - const u8 request, const u8 requesttype, - const u16 offset, void *buffer, - const u16 buffer_length, const int timeout) -{ - int status; - - BUG_ON(!mutex_is_locked(&rt2x00dev->csr_mutex)); - - /* - * Check for Cache availability. - */ - if (unlikely(!rt2x00dev->csr.cache || buffer_length > CSR_CACHE_SIZE)) { - rt2x00_err(rt2x00dev, "CSR cache not available\n"); - return -ENOMEM; - } - - if (requesttype == USB_VENDOR_REQUEST_OUT) - memcpy(rt2x00dev->csr.cache, buffer, buffer_length); - - status = rt2x00usb_vendor_request(rt2x00dev, request, requesttype, - offset, 0, rt2x00dev->csr.cache, - buffer_length, timeout); - - if (!status && requesttype == USB_VENDOR_REQUEST_IN) - memcpy(buffer, rt2x00dev->csr.cache, buffer_length); - - return status; -} -EXPORT_SYMBOL_GPL(rt2x00usb_vendor_req_buff_lock); - -int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev, - const u8 request, const u8 requesttype, - const u16 offset, void *buffer, - const u16 buffer_length) -{ - int status = 0; - unsigned char *tb; - u16 off, len, bsize; - - mutex_lock(&rt2x00dev->csr_mutex); - - tb = (char *)buffer; - off = offset; - len = buffer_length; - while (len && !status) { - bsize = min_t(u16, CSR_CACHE_SIZE, len); - status = rt2x00usb_vendor_req_buff_lock(rt2x00dev, request, - requesttype, off, tb, - bsize, REGISTER_TIMEOUT); - - tb += bsize; - len -= bsize; - off += bsize; - } - - mutex_unlock(&rt2x00dev->csr_mutex); - - return status; -} -EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_buff); - -int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - const struct rt2x00_field32 field, - u32 *reg) -{ - unsigned int i; - - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return -ENODEV; - - for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) { - rt2x00usb_register_read_lock(rt2x00dev, offset, reg); - if (!rt2x00_get_field32(*reg, field)) - return 1; - udelay(REGISTER_BUSY_DELAY); - } - - rt2x00_err(rt2x00dev, "Indirect register access failed: offset=0x%.08x, value=0x%.08x\n", - offset, *reg); - *reg = ~0; - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00usb_regbusy_read); - - -struct rt2x00_async_read_data { - __le32 reg; - struct usb_ctrlrequest cr; - struct rt2x00_dev *rt2x00dev; - bool (*callback)(struct rt2x00_dev *, int, u32); -}; - -static void rt2x00usb_register_read_async_cb(struct urb *urb) -{ - struct rt2x00_async_read_data *rd = urb->context; - if (rd->callback(rd->rt2x00dev, urb->status, le32_to_cpu(rd->reg))) { - if (usb_submit_urb(urb, GFP_ATOMIC) < 0) - kfree(rd); - } else - kfree(rd); -} - -void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - bool (*callback)(struct rt2x00_dev*, int, u32)) -{ - struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); - struct urb *urb; - struct rt2x00_async_read_data *rd; - - rd = kmalloc(sizeof(*rd), GFP_ATOMIC); - if (!rd) - return; - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - kfree(rd); - return; - } - - rd->rt2x00dev = rt2x00dev; - rd->callback = callback; - rd->cr.bRequestType = USB_VENDOR_REQUEST_IN; - rd->cr.bRequest = USB_MULTI_READ; - rd->cr.wValue = 0; - rd->cr.wIndex = cpu_to_le16(offset); - rd->cr.wLength = cpu_to_le16(sizeof(u32)); - - usb_fill_control_urb(urb, usb_dev, usb_rcvctrlpipe(usb_dev, 0), - (unsigned char *)(&rd->cr), &rd->reg, sizeof(rd->reg), - rt2x00usb_register_read_async_cb, rd); - if (usb_submit_urb(urb, GFP_ATOMIC) < 0) - kfree(rd); - usb_free_urb(urb); -} -EXPORT_SYMBOL_GPL(rt2x00usb_register_read_async); - -/* - * TX data handlers. - */ -static void rt2x00usb_work_txdone_entry(struct queue_entry *entry) -{ - /* - * If the transfer to hardware succeeded, it does not mean the - * frame was send out correctly. It only means the frame - * was successfully pushed to the hardware, we have no - * way to determine the transmission status right now. - * (Only indirectly by looking at the failed TX counters - * in the register). - */ - if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) - rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE); - else - rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN); -} - -static void rt2x00usb_work_txdone(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, txdone_work); - struct data_queue *queue; - struct queue_entry *entry; - - tx_queue_for_each(rt2x00dev, queue) { - while (!rt2x00queue_empty(queue)) { - entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - - if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || - !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) - break; - - rt2x00usb_work_txdone_entry(entry); - } - } -} - -static void rt2x00usb_interrupt_txdone(struct urb *urb) -{ - struct queue_entry *entry = (struct queue_entry *)urb->context; - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - - if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) - return; - /* - * Check if the frame was correctly uploaded - */ - if (urb->status) - set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); - /* - * Report the frame as DMA done - */ - rt2x00lib_dmadone(entry); - - if (rt2x00dev->ops->lib->tx_dma_done) - rt2x00dev->ops->lib->tx_dma_done(entry); - /* - * Schedule the delayed work for reading the TX status - * from the device. - */ - if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TXSTATUS_FIFO) || - !kfifo_is_empty(&rt2x00dev->txstatus_fifo)) - queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); -} - -static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void *data) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); - struct queue_entry_priv_usb *entry_priv = entry->priv_data; - u32 length; - int status; - - if (!test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags) || - test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) - return false; - - /* - * USB devices require certain padding at the end of each frame - * and urb. Those paddings are not included in skbs. Pass entry - * to the driver to determine what the overall length should be. - */ - length = rt2x00dev->ops->lib->get_tx_data_len(entry); - - status = skb_padto(entry->skb, length); - if (unlikely(status)) { - /* TODO: report something more appropriate than IO_FAILED. */ - rt2x00_warn(rt2x00dev, "TX SKB padding error, out of memory\n"); - set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); - rt2x00lib_dmadone(entry); - - return false; - } - - usb_fill_bulk_urb(entry_priv->urb, usb_dev, - usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint), - entry->skb->data, length, - rt2x00usb_interrupt_txdone, entry); - - status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); - if (status) { - if (status == -ENODEV) - clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); - set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); - rt2x00lib_dmadone(entry); - } - - return false; -} - -/* - * RX data handlers. - */ -static void rt2x00usb_work_rxdone(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, rxdone_work); - struct queue_entry *entry; - struct skb_frame_desc *skbdesc; - u8 rxd[32]; - - while (!rt2x00queue_empty(rt2x00dev->rx)) { - entry = rt2x00queue_get_entry(rt2x00dev->rx, Q_INDEX_DONE); - - if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || - !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) - break; - - /* - * Fill in desc fields of the skb descriptor - */ - skbdesc = get_skb_frame_desc(entry->skb); - skbdesc->desc = rxd; - skbdesc->desc_len = entry->queue->desc_size; - - /* - * Send the frame to rt2x00lib for further processing. - */ - rt2x00lib_rxdone(entry, GFP_KERNEL); - } -} - -static void rt2x00usb_interrupt_rxdone(struct urb *urb) -{ - struct queue_entry *entry = (struct queue_entry *)urb->context; - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - - if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) - return; - - /* - * Report the frame as DMA done - */ - rt2x00lib_dmadone(entry); - - /* - * Check if the received data is simply too small - * to be actually valid, or if the urb is signaling - * a problem. - */ - if (urb->actual_length < entry->queue->desc_size || urb->status) - set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); - - /* - * Schedule the delayed work for reading the RX status - * from the device. - */ - queue_work(rt2x00dev->workqueue, &rt2x00dev->rxdone_work); -} - -static bool rt2x00usb_kick_rx_entry(struct queue_entry *entry, void *data) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); - struct queue_entry_priv_usb *entry_priv = entry->priv_data; - int status; - - if (test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || - test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) - return false; - - rt2x00lib_dmastart(entry); - - usb_fill_bulk_urb(entry_priv->urb, usb_dev, - usb_rcvbulkpipe(usb_dev, entry->queue->usb_endpoint), - entry->skb->data, entry->skb->len, - rt2x00usb_interrupt_rxdone, entry); - - status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); - if (status) { - if (status == -ENODEV) - clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); - set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); - rt2x00lib_dmadone(entry); - } - - return false; -} - -void rt2x00usb_kick_queue(struct data_queue *queue) -{ - switch (queue->qid) { - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - if (!rt2x00queue_empty(queue)) - rt2x00queue_for_each_entry(queue, - Q_INDEX_DONE, - Q_INDEX, - NULL, - rt2x00usb_kick_tx_entry); - break; - case QID_RX: - if (!rt2x00queue_full(queue)) - rt2x00queue_for_each_entry(queue, - Q_INDEX, - Q_INDEX_DONE, - NULL, - rt2x00usb_kick_rx_entry); - break; - default: - break; - } -} -EXPORT_SYMBOL_GPL(rt2x00usb_kick_queue); - -static bool rt2x00usb_flush_entry(struct queue_entry *entry, void *data) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct queue_entry_priv_usb *entry_priv = entry->priv_data; - struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; - - if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) - return false; - - usb_kill_urb(entry_priv->urb); - - /* - * Kill guardian urb (if required by driver). - */ - if ((entry->queue->qid == QID_BEACON) && - (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD))) - usb_kill_urb(bcn_priv->guardian_urb); - - return false; -} - -void rt2x00usb_flush_queue(struct data_queue *queue, bool drop) -{ - struct work_struct *completion; - unsigned int i; - - if (drop) - rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, NULL, - rt2x00usb_flush_entry); - - /* - * Obtain the queue completion handler - */ - switch (queue->qid) { - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - completion = &queue->rt2x00dev->txdone_work; - break; - case QID_RX: - completion = &queue->rt2x00dev->rxdone_work; - break; - default: - return; - } - - for (i = 0; i < 10; i++) { - /* - * Check if the driver is already done, otherwise we - * have to sleep a little while to give the driver/hw - * the oppurtunity to complete interrupt process itself. - */ - if (rt2x00queue_empty(queue)) - break; - - /* - * Schedule the completion handler manually, when this - * worker function runs, it should cleanup the queue. - */ - queue_work(queue->rt2x00dev->workqueue, completion); - - /* - * Wait for a little while to give the driver - * the oppurtunity to recover itself. - */ - msleep(10); - } -} -EXPORT_SYMBOL_GPL(rt2x00usb_flush_queue); - -static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) -{ - rt2x00_warn(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced forced reset\n", - queue->qid); - - rt2x00queue_stop_queue(queue); - rt2x00queue_flush_queue(queue, true); - rt2x00queue_start_queue(queue); -} - -static int rt2x00usb_dma_timeout(struct data_queue *queue) -{ - struct queue_entry *entry; - - entry = rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE); - return rt2x00queue_dma_timeout(entry); -} - -void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - - tx_queue_for_each(rt2x00dev, queue) { - if (!rt2x00queue_empty(queue)) { - if (rt2x00usb_dma_timeout(queue)) - rt2x00usb_watchdog_tx_dma(queue); - } - } -} -EXPORT_SYMBOL_GPL(rt2x00usb_watchdog); - -/* - * Radio handlers - */ -void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - rt2x00usb_vendor_request_sw(rt2x00dev, USB_RX_CONTROL, 0, 0, - REGISTER_TIMEOUT); -} -EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio); - -/* - * Device initialization handlers. - */ -void rt2x00usb_clear_entry(struct queue_entry *entry) -{ - entry->flags = 0; - - if (entry->queue->qid == QID_RX) - rt2x00usb_kick_rx_entry(entry, NULL); -} -EXPORT_SYMBOL_GPL(rt2x00usb_clear_entry); - -static void rt2x00usb_assign_endpoint(struct data_queue *queue, - struct usb_endpoint_descriptor *ep_desc) -{ - struct usb_device *usb_dev = to_usb_device_intf(queue->rt2x00dev->dev); - int pipe; - - queue->usb_endpoint = usb_endpoint_num(ep_desc); - - if (queue->qid == QID_RX) { - pipe = usb_rcvbulkpipe(usb_dev, queue->usb_endpoint); - queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 0); - } else { - pipe = usb_sndbulkpipe(usb_dev, queue->usb_endpoint); - queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 1); - } - - if (!queue->usb_maxpacket) - queue->usb_maxpacket = 1; -} - -static int rt2x00usb_find_endpoints(struct rt2x00_dev *rt2x00dev) -{ - struct usb_interface *intf = to_usb_interface(rt2x00dev->dev); - struct usb_host_interface *intf_desc = intf->cur_altsetting; - struct usb_endpoint_descriptor *ep_desc; - struct data_queue *queue = rt2x00dev->tx; - struct usb_endpoint_descriptor *tx_ep_desc = NULL; - unsigned int i; - - /* - * Walk through all available endpoints to search for "bulk in" - * and "bulk out" endpoints. When we find such endpoints collect - * the information we need from the descriptor and assign it - * to the queue. - */ - for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { - ep_desc = &intf_desc->endpoint[i].desc; - - if (usb_endpoint_is_bulk_in(ep_desc)) { - rt2x00usb_assign_endpoint(rt2x00dev->rx, ep_desc); - } else if (usb_endpoint_is_bulk_out(ep_desc) && - (queue != queue_end(rt2x00dev))) { - rt2x00usb_assign_endpoint(queue, ep_desc); - queue = queue_next(queue); - - tx_ep_desc = ep_desc; - } - } - - /* - * At least 1 endpoint for RX and 1 endpoint for TX must be available. - */ - if (!rt2x00dev->rx->usb_endpoint || !rt2x00dev->tx->usb_endpoint) { - rt2x00_err(rt2x00dev, "Bulk-in/Bulk-out endpoints not found\n"); - return -EPIPE; - } - - /* - * It might be possible not all queues have a dedicated endpoint. - * Loop through all TX queues and copy the endpoint information - * which we have gathered from already assigned endpoints. - */ - txall_queue_for_each(rt2x00dev, queue) { - if (!queue->usb_endpoint) - rt2x00usb_assign_endpoint(queue, tx_ep_desc); - } - - return 0; -} - -static int rt2x00usb_alloc_entries(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - struct queue_entry_priv_usb *entry_priv; - struct queue_entry_priv_usb_bcn *bcn_priv; - unsigned int i; - - for (i = 0; i < queue->limit; i++) { - entry_priv = queue->entries[i].priv_data; - entry_priv->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!entry_priv->urb) - return -ENOMEM; - } - - /* - * If this is not the beacon queue or - * no guardian byte was required for the beacon, - * then we are done. - */ - if (queue->qid != QID_BEACON || - !rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD)) - return 0; - - for (i = 0; i < queue->limit; i++) { - bcn_priv = queue->entries[i].priv_data; - bcn_priv->guardian_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!bcn_priv->guardian_urb) - return -ENOMEM; - } - - return 0; -} - -static void rt2x00usb_free_entries(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - struct queue_entry_priv_usb *entry_priv; - struct queue_entry_priv_usb_bcn *bcn_priv; - unsigned int i; - - if (!queue->entries) - return; - - for (i = 0; i < queue->limit; i++) { - entry_priv = queue->entries[i].priv_data; - usb_kill_urb(entry_priv->urb); - usb_free_urb(entry_priv->urb); - } - - /* - * If this is not the beacon queue or - * no guardian byte was required for the beacon, - * then we are done. - */ - if (queue->qid != QID_BEACON || - !rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD)) - return; - - for (i = 0; i < queue->limit; i++) { - bcn_priv = queue->entries[i].priv_data; - usb_kill_urb(bcn_priv->guardian_urb); - usb_free_urb(bcn_priv->guardian_urb); - } -} - -int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - int status; - - /* - * Find endpoints for each queue - */ - status = rt2x00usb_find_endpoints(rt2x00dev); - if (status) - goto exit; - - /* - * Allocate DMA - */ - queue_for_each(rt2x00dev, queue) { - status = rt2x00usb_alloc_entries(queue); - if (status) - goto exit; - } - - return 0; - -exit: - rt2x00usb_uninitialize(rt2x00dev); - - return status; -} -EXPORT_SYMBOL_GPL(rt2x00usb_initialize); - -void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - - queue_for_each(rt2x00dev, queue) - rt2x00usb_free_entries(queue); -} -EXPORT_SYMBOL_GPL(rt2x00usb_uninitialize); - -/* - * USB driver handlers. - */ -static void rt2x00usb_free_reg(struct rt2x00_dev *rt2x00dev) -{ - kfree(rt2x00dev->rf); - rt2x00dev->rf = NULL; - - kfree(rt2x00dev->eeprom); - rt2x00dev->eeprom = NULL; - - kfree(rt2x00dev->csr.cache); - rt2x00dev->csr.cache = NULL; -} - -static int rt2x00usb_alloc_reg(struct rt2x00_dev *rt2x00dev) -{ - rt2x00dev->csr.cache = kzalloc(CSR_CACHE_SIZE, GFP_KERNEL); - if (!rt2x00dev->csr.cache) - goto exit; - - rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); - if (!rt2x00dev->eeprom) - goto exit; - - rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); - if (!rt2x00dev->rf) - goto exit; - - return 0; - -exit: - rt2x00_probe_err("Failed to allocate registers\n"); - - rt2x00usb_free_reg(rt2x00dev); - - return -ENOMEM; -} - -int rt2x00usb_probe(struct usb_interface *usb_intf, - const struct rt2x00_ops *ops) -{ - struct usb_device *usb_dev = interface_to_usbdev(usb_intf); - struct ieee80211_hw *hw; - struct rt2x00_dev *rt2x00dev; - int retval; - - usb_dev = usb_get_dev(usb_dev); - usb_reset_device(usb_dev); - - hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); - if (!hw) { - rt2x00_probe_err("Failed to allocate hardware\n"); - retval = -ENOMEM; - goto exit_put_device; - } - - usb_set_intfdata(usb_intf, hw); - - rt2x00dev = hw->priv; - rt2x00dev->dev = &usb_intf->dev; - rt2x00dev->ops = ops; - rt2x00dev->hw = hw; - - rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); - - INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone); - INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone); - hrtimer_init(&rt2x00dev->txstatus_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - - retval = rt2x00usb_alloc_reg(rt2x00dev); - if (retval) - goto exit_free_device; - - retval = rt2x00lib_probe_dev(rt2x00dev); - if (retval) - goto exit_free_reg; - - return 0; - -exit_free_reg: - rt2x00usb_free_reg(rt2x00dev); - -exit_free_device: - ieee80211_free_hw(hw); - -exit_put_device: - usb_put_dev(usb_dev); - - usb_set_intfdata(usb_intf, NULL); - - return retval; -} -EXPORT_SYMBOL_GPL(rt2x00usb_probe); - -void rt2x00usb_disconnect(struct usb_interface *usb_intf) -{ - struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); - struct rt2x00_dev *rt2x00dev = hw->priv; - - /* - * Free all allocated data. - */ - rt2x00lib_remove_dev(rt2x00dev); - rt2x00usb_free_reg(rt2x00dev); - ieee80211_free_hw(hw); - - /* - * Free the USB device data. - */ - usb_set_intfdata(usb_intf, NULL); - usb_put_dev(interface_to_usbdev(usb_intf)); -} -EXPORT_SYMBOL_GPL(rt2x00usb_disconnect); - -#ifdef CONFIG_PM -int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state) -{ - struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); - struct rt2x00_dev *rt2x00dev = hw->priv; - - return rt2x00lib_suspend(rt2x00dev, state); -} -EXPORT_SYMBOL_GPL(rt2x00usb_suspend); - -int rt2x00usb_resume(struct usb_interface *usb_intf) -{ - struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); - struct rt2x00_dev *rt2x00dev = hw->priv; - - return rt2x00lib_resume(rt2x00dev); -} -EXPORT_SYMBOL_GPL(rt2x00usb_resume); -#endif /* CONFIG_PM */ - -/* - * rt2x00usb module information. - */ -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("rt2x00 usb library"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h deleted file mode 100644 index 569363da00a2..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00usb.h +++ /dev/null @@ -1,424 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt2x00usb - Abstract: Data structures for the rt2x00usb module. - */ - -#ifndef RT2X00USB_H -#define RT2X00USB_H - -#include - -#define to_usb_device_intf(d) \ -({ \ - struct usb_interface *intf = to_usb_interface(d); \ - interface_to_usbdev(intf); \ -}) - -/* - * For USB vendor requests we need to pass a timeout time in ms, for this we - * use the REGISTER_TIMEOUT, however when loading firmware or read EEPROM - * a higher value is required. In that case we use the REGISTER_TIMEOUT_FIRMWARE - * and EEPROM_TIMEOUT. - */ -#define REGISTER_TIMEOUT 100 -#define REGISTER_TIMEOUT_FIRMWARE 1000 -#define EEPROM_TIMEOUT 2000 - -/* - * Cache size - */ -#define CSR_CACHE_SIZE 64 - -/* - * USB request types. - */ -#define USB_VENDOR_REQUEST ( USB_TYPE_VENDOR | USB_RECIP_DEVICE ) -#define USB_VENDOR_REQUEST_IN ( USB_DIR_IN | USB_VENDOR_REQUEST ) -#define USB_VENDOR_REQUEST_OUT ( USB_DIR_OUT | USB_VENDOR_REQUEST ) - -/** - * enum rt2x00usb_vendor_request: USB vendor commands. - */ -enum rt2x00usb_vendor_request { - USB_DEVICE_MODE = 1, - USB_SINGLE_WRITE = 2, - USB_SINGLE_READ = 3, - USB_MULTI_WRITE = 6, - USB_MULTI_READ = 7, - USB_EEPROM_WRITE = 8, - USB_EEPROM_READ = 9, - USB_LED_CONTROL = 10, /* RT73USB */ - USB_RX_CONTROL = 12, -}; - -/** - * enum rt2x00usb_mode_offset: Device modes offset. - */ -enum rt2x00usb_mode_offset { - USB_MODE_RESET = 1, - USB_MODE_UNPLUG = 2, - USB_MODE_FUNCTION = 3, - USB_MODE_TEST = 4, - USB_MODE_SLEEP = 7, /* RT73USB */ - USB_MODE_FIRMWARE = 8, /* RT73USB */ - USB_MODE_WAKEUP = 9, /* RT73USB */ - USB_MODE_AUTORUN = 17, /* RT2800USB */ -}; - -/** - * rt2x00usb_vendor_request - Send register command to device - * @rt2x00dev: Pointer to &struct rt2x00_dev - * @request: USB vendor command (See &enum rt2x00usb_vendor_request) - * @requesttype: Request type &USB_VENDOR_REQUEST_* - * @offset: Register offset to perform action on - * @value: Value to write to device - * @buffer: Buffer where information will be read/written to by device - * @buffer_length: Size of &buffer - * @timeout: Operation timeout - * - * This is the main function to communicate with the device, - * the &buffer argument _must_ either be NULL or point to - * a buffer allocated by kmalloc. Failure to do so can lead - * to unexpected behavior depending on the architecture. - */ -int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, - const u8 request, const u8 requesttype, - const u16 offset, const u16 value, - void *buffer, const u16 buffer_length, - const int timeout); - -/** - * rt2x00usb_vendor_request_buff - Send register command to device (buffered) - * @rt2x00dev: Pointer to &struct rt2x00_dev - * @request: USB vendor command (See &enum rt2x00usb_vendor_request) - * @requesttype: Request type &USB_VENDOR_REQUEST_* - * @offset: Register offset to perform action on - * @buffer: Buffer where information will be read/written to by device - * @buffer_length: Size of &buffer - * - * This function will use a previously with kmalloc allocated cache - * to communicate with the device. The contents of the buffer pointer - * will be copied to this cache when writing, or read from the cache - * when reading. - * Buffers send to &rt2x00usb_vendor_request _must_ be allocated with - * kmalloc. Hence the reason for using a previously allocated cache - * which has been allocated properly. - */ -int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev, - const u8 request, const u8 requesttype, - const u16 offset, void *buffer, - const u16 buffer_length); - -/** - * rt2x00usb_vendor_request_buff - Send register command to device (buffered) - * @rt2x00dev: Pointer to &struct rt2x00_dev - * @request: USB vendor command (See &enum rt2x00usb_vendor_request) - * @requesttype: Request type &USB_VENDOR_REQUEST_* - * @offset: Register offset to perform action on - * @buffer: Buffer where information will be read/written to by device - * @buffer_length: Size of &buffer - * @timeout: Operation timeout - * - * A version of &rt2x00usb_vendor_request_buff which must be called - * if the usb_cache_mutex is already held. - */ -int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev, - const u8 request, const u8 requesttype, - const u16 offset, void *buffer, - const u16 buffer_length, const int timeout); - -/** - * rt2x00usb_vendor_request_sw - Send single register command to device - * @rt2x00dev: Pointer to &struct rt2x00_dev - * @request: USB vendor command (See &enum rt2x00usb_vendor_request) - * @offset: Register offset to perform action on - * @value: Value to write to device - * @timeout: Operation timeout - * - * Simple wrapper around rt2x00usb_vendor_request to write a single - * command to the device. Since we don't use the buffer argument we - * don't have to worry about kmalloc here. - */ -static inline int rt2x00usb_vendor_request_sw(struct rt2x00_dev *rt2x00dev, - const u8 request, - const u16 offset, - const u16 value, - const int timeout) -{ - return rt2x00usb_vendor_request(rt2x00dev, request, - USB_VENDOR_REQUEST_OUT, offset, - value, NULL, 0, timeout); -} - -/** - * rt2x00usb_eeprom_read - Read eeprom from device - * @rt2x00dev: Pointer to &struct rt2x00_dev - * @eeprom: Pointer to eeprom array to store the information in - * @length: Number of bytes to read from the eeprom - * - * Simple wrapper around rt2x00usb_vendor_request to read the eeprom - * from the device. Note that the eeprom argument _must_ be allocated using - * kmalloc for correct handling inside the kernel USB layer. - */ -static inline int rt2x00usb_eeprom_read(struct rt2x00_dev *rt2x00dev, - __le16 *eeprom, const u16 length) -{ - return rt2x00usb_vendor_request(rt2x00dev, USB_EEPROM_READ, - USB_VENDOR_REQUEST_IN, 0, 0, - eeprom, length, EEPROM_TIMEOUT); -} - -/** - * rt2x00usb_register_read - Read 32bit register word - * @rt2x00dev: Device pointer, see &struct rt2x00_dev. - * @offset: Register offset - * @value: Pointer to where register contents should be stored - * - * This function is a simple wrapper for 32bit register access - * through rt2x00usb_vendor_request_buff(). - */ -static inline void rt2x00usb_register_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 *value) -{ - __le32 reg = 0; - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, - USB_VENDOR_REQUEST_IN, offset, - ®, sizeof(reg)); - *value = le32_to_cpu(reg); -} - -/** - * rt2x00usb_register_read_lock - Read 32bit register word - * @rt2x00dev: Device pointer, see &struct rt2x00_dev. - * @offset: Register offset - * @value: Pointer to where register contents should be stored - * - * This function is a simple wrapper for 32bit register access - * through rt2x00usb_vendor_req_buff_lock(). - */ -static inline void rt2x00usb_register_read_lock(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 *value) -{ - __le32 reg = 0; - rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ, - USB_VENDOR_REQUEST_IN, offset, - ®, sizeof(reg), REGISTER_TIMEOUT); - *value = le32_to_cpu(reg); -} - -/** - * rt2x00usb_register_multiread - Read 32bit register words - * @rt2x00dev: Device pointer, see &struct rt2x00_dev. - * @offset: Register offset - * @value: Pointer to where register contents should be stored - * @length: Length of the data - * - * This function is a simple wrapper for 32bit register access - * through rt2x00usb_vendor_request_buff(). - */ -static inline void rt2x00usb_register_multiread(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - void *value, const u32 length) -{ - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, - USB_VENDOR_REQUEST_IN, offset, - value, length); -} - -/** - * rt2x00usb_register_write - Write 32bit register word - * @rt2x00dev: Device pointer, see &struct rt2x00_dev. - * @offset: Register offset - * @value: Data which should be written - * - * This function is a simple wrapper for 32bit register access - * through rt2x00usb_vendor_request_buff(). - */ -static inline void rt2x00usb_register_write(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 value) -{ - __le32 reg = cpu_to_le32(value); - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, offset, - ®, sizeof(reg)); -} - -/** - * rt2x00usb_register_write_lock - Write 32bit register word - * @rt2x00dev: Device pointer, see &struct rt2x00_dev. - * @offset: Register offset - * @value: Data which should be written - * - * This function is a simple wrapper for 32bit register access - * through rt2x00usb_vendor_req_buff_lock(). - */ -static inline void rt2x00usb_register_write_lock(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - u32 value) -{ - __le32 reg = cpu_to_le32(value); - rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, offset, - ®, sizeof(reg), REGISTER_TIMEOUT); -} - -/** - * rt2x00usb_register_multiwrite - Write 32bit register words - * @rt2x00dev: Device pointer, see &struct rt2x00_dev. - * @offset: Register offset - * @value: Data which should be written - * @length: Length of the data - * - * This function is a simple wrapper for 32bit register access - * through rt2x00usb_vendor_request_buff(). - */ -static inline void rt2x00usb_register_multiwrite(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - const void *value, - const u32 length) -{ - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, offset, - (void *)value, length); -} - -/** - * rt2x00usb_regbusy_read - Read from register with busy check - * @rt2x00dev: Device pointer, see &struct rt2x00_dev. - * @offset: Register offset - * @field: Field to check if register is busy - * @reg: Pointer to where register contents should be stored - * - * This function will read the given register, and checks if the - * register is busy. If it is, it will sleep for a couple of - * microseconds before reading the register again. If the register - * is not read after a certain timeout, this function will return - * FALSE. - */ -int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - const struct rt2x00_field32 field, - u32 *reg); - -/** - * rt2x00usb_register_read_async - Asynchronously read 32bit register word - * @rt2x00dev: Device pointer, see &struct rt2x00_dev. - * @offset: Register offset - * @callback: Functon to call when read completes. - * - * Submit a control URB to read a 32bit register. This safe to - * be called from atomic context. The callback will be called - * when the URB completes. Otherwise the function is similar - * to rt2x00usb_register_read(). - * When the callback function returns false, the memory will be cleaned up, - * when it returns true, the urb will be fired again. - */ -void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - bool (*callback)(struct rt2x00_dev*, int, u32)); - -/* - * Radio handlers - */ -void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev); - -/** - * struct queue_entry_priv_usb: Per entry USB specific information - * - * @urb: Urb structure used for device communication. - */ -struct queue_entry_priv_usb { - struct urb *urb; -}; - -/** - * struct queue_entry_priv_usb_bcn: Per TX entry USB specific information - * - * The first section should match &struct queue_entry_priv_usb exactly. - * rt2500usb can use this structure to send a guardian byte when working - * with beacons. - * - * @urb: Urb structure used for device communication. - * @guardian_data: Set to 0, used for sending the guardian data. - * @guardian_urb: Urb structure used to send the guardian data. - */ -struct queue_entry_priv_usb_bcn { - struct urb *urb; - - unsigned int guardian_data; - struct urb *guardian_urb; -}; - -/** - * rt2x00usb_kick_queue - Kick data queue - * @queue: Data queue to kick - * - * This will walk through all entries of the queue and push all pending - * frames to the hardware as a single burst. - */ -void rt2x00usb_kick_queue(struct data_queue *queue); - -/** - * rt2x00usb_flush_queue - Flush data queue - * @queue: Data queue to stop - * @drop: True to drop all pending frames. - * - * This will walk through all entries of the queue and will optionally - * kill all URB's which were send to the device, or at least wait until - * they have been returned from the device.. - */ -void rt2x00usb_flush_queue(struct data_queue *queue, bool drop); - -/** - * rt2x00usb_watchdog - Watchdog for USB communication - * @rt2x00dev: Pointer to &struct rt2x00_dev - * - * Check the health of the USB communication and determine - * if timeouts have occurred. If this is the case, this function - * will reset all communication to restore functionality again. - */ -void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev); - -/* - * Device initialization handlers. - */ -void rt2x00usb_clear_entry(struct queue_entry *entry); -int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev); -void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev); - -/* - * USB driver handlers. - */ -int rt2x00usb_probe(struct usb_interface *usb_intf, - const struct rt2x00_ops *ops); -void rt2x00usb_disconnect(struct usb_interface *usb_intf); -#ifdef CONFIG_PM -int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state); -int rt2x00usb_resume(struct usb_interface *usb_intf); -#else -#define rt2x00usb_suspend NULL -#define rt2x00usb_resume NULL -#endif /* CONFIG_PM */ - -#endif /* RT2X00USB_H */ diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c deleted file mode 100644 index c0e730ea1b69..000000000000 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ /dev/null @@ -1,3111 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt61pci - Abstract: rt61pci device specific routines. - Supported chipsets: RT2561, RT2561s, RT2661. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00mmio.h" -#include "rt2x00pci.h" -#include "rt61pci.h" - -/* - * Allow hardware encryption to be disabled. - */ -static bool modparam_nohwcrypt = false; -module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); -MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); - -/* - * Register access. - * BBP and RF register require indirect register access, - * and use the CSR registers PHY_CSR3 and PHY_CSR4 to achieve this. - * These indirect registers work with busy bits, - * and we will try maximal REGISTER_BUSY_COUNT times to access - * the register while taking a REGISTER_BUSY_DELAY us delay - * between each attempt. When the busy bit is still set at that time, - * the access attempt is considered to have failed, - * and we will print an error. - */ -#define WAIT_FOR_BBP(__dev, __reg) \ - rt2x00mmio_regbusy_read((__dev), PHY_CSR3, PHY_CSR3_BUSY, (__reg)) -#define WAIT_FOR_RF(__dev, __reg) \ - rt2x00mmio_regbusy_read((__dev), PHY_CSR4, PHY_CSR4_BUSY, (__reg)) -#define WAIT_FOR_MCU(__dev, __reg) \ - rt2x00mmio_regbusy_read((__dev), H2M_MAILBOX_CSR, \ - H2M_MAILBOX_CSR_OWNER, (__reg)) - -static void rt61pci_bbp_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u8 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, PHY_CSR3_VALUE, value); - rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); - rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); - rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); - - rt2x00mmio_register_write(rt2x00dev, PHY_CSR3, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt61pci_bbp_read(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u8 *value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the read request into the register. - * After the data has been written, we wait until hardware - * returns the correct value, if at any time the register - * doesn't become available in time, reg will be 0xffffffff - * which means we return 0xff to the caller. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); - rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); - rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); - - rt2x00mmio_register_write(rt2x00dev, PHY_CSR3, reg); - - WAIT_FOR_BBP(rt2x00dev, ®); - } - - *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt61pci_rf_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u32 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the RF becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_RF(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, PHY_CSR4_VALUE, value); - rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, 21); - rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); - rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); - - rt2x00mmio_register_write(rt2x00dev, PHY_CSR4, reg); - rt2x00_rf_write(rt2x00dev, word, value); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt61pci_mcu_request(struct rt2x00_dev *rt2x00dev, - const u8 command, const u8 token, - const u8 arg0, const u8 arg1) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the MCU becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_MCU(rt2x00dev, ®)) { - rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); - rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CSR, reg); - - rt2x00mmio_register_read(rt2x00dev, HOST_CMD_CSR, ®); - rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); - rt2x00_set_field32(®, HOST_CMD_CSR_INTERRUPT_MCU, 1); - rt2x00mmio_register_write(rt2x00dev, HOST_CMD_CSR, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); - -} - -static void rt61pci_eepromregister_read(struct eeprom_93cx6 *eeprom) -{ - struct rt2x00_dev *rt2x00dev = eeprom->data; - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); - - eeprom->reg_data_in = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_IN); - eeprom->reg_data_out = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_OUT); - eeprom->reg_data_clock = - !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_CLOCK); - eeprom->reg_chip_select = - !!rt2x00_get_field32(reg, E2PROM_CSR_CHIP_SELECT); -} - -static void rt61pci_eepromregister_write(struct eeprom_93cx6 *eeprom) -{ - struct rt2x00_dev *rt2x00dev = eeprom->data; - u32 reg = 0; - - rt2x00_set_field32(®, E2PROM_CSR_DATA_IN, !!eeprom->reg_data_in); - rt2x00_set_field32(®, E2PROM_CSR_DATA_OUT, !!eeprom->reg_data_out); - rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, - !!eeprom->reg_data_clock); - rt2x00_set_field32(®, E2PROM_CSR_CHIP_SELECT, - !!eeprom->reg_chip_select); - - rt2x00mmio_register_write(rt2x00dev, E2PROM_CSR, reg); -} - -#ifdef CONFIG_RT2X00_LIB_DEBUGFS -static const struct rt2x00debug rt61pci_rt2x00debug = { - .owner = THIS_MODULE, - .csr = { - .read = rt2x00mmio_register_read, - .write = rt2x00mmio_register_write, - .flags = RT2X00DEBUGFS_OFFSET, - .word_base = CSR_REG_BASE, - .word_size = sizeof(u32), - .word_count = CSR_REG_SIZE / sizeof(u32), - }, - .eeprom = { - .read = rt2x00_eeprom_read, - .write = rt2x00_eeprom_write, - .word_base = EEPROM_BASE, - .word_size = sizeof(u16), - .word_count = EEPROM_SIZE / sizeof(u16), - }, - .bbp = { - .read = rt61pci_bbp_read, - .write = rt61pci_bbp_write, - .word_base = BBP_BASE, - .word_size = sizeof(u8), - .word_count = BBP_SIZE / sizeof(u8), - }, - .rf = { - .read = rt2x00_rf_read, - .write = rt61pci_rf_write, - .word_base = RF_BASE, - .word_size = sizeof(u32), - .word_count = RF_SIZE / sizeof(u32), - }, -}; -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ - -static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, MAC_CSR13, ®); - return rt2x00_get_field32(reg, MAC_CSR13_VAL5); -} - -#ifdef CONFIG_RT2X00_LIB_LEDS -static void rt61pci_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - unsigned int enabled = brightness != LED_OFF; - unsigned int a_mode = - (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); - unsigned int bg_mode = - (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); - - if (led->type == LED_TYPE_RADIO) { - rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, - MCU_LEDCS_RADIO_STATUS, enabled); - - rt61pci_mcu_request(led->rt2x00dev, MCU_LED, 0xff, - (led->rt2x00dev->led_mcu_reg & 0xff), - ((led->rt2x00dev->led_mcu_reg >> 8))); - } else if (led->type == LED_TYPE_ASSOC) { - rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, - MCU_LEDCS_LINK_BG_STATUS, bg_mode); - rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, - MCU_LEDCS_LINK_A_STATUS, a_mode); - - rt61pci_mcu_request(led->rt2x00dev, MCU_LED, 0xff, - (led->rt2x00dev->led_mcu_reg & 0xff), - ((led->rt2x00dev->led_mcu_reg >> 8))); - } else if (led->type == LED_TYPE_QUALITY) { - /* - * The brightness is divided into 6 levels (0 - 5), - * this means we need to convert the brightness - * argument into the matching level within that range. - */ - rt61pci_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff, - brightness / (LED_FULL / 6), 0); - } -} - -static int rt61pci_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - u32 reg; - - rt2x00mmio_register_read(led->rt2x00dev, MAC_CSR14, ®); - rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, *delay_on); - rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, *delay_off); - rt2x00mmio_register_write(led->rt2x00dev, MAC_CSR14, reg); - - return 0; -} - -static void rt61pci_init_led(struct rt2x00_dev *rt2x00dev, - struct rt2x00_led *led, - enum led_type type) -{ - led->rt2x00dev = rt2x00dev; - led->type = type; - led->led_dev.brightness_set = rt61pci_brightness_set; - led->led_dev.blink_set = rt61pci_blink_set; - led->flags = LED_INITIALIZED; -} -#endif /* CONFIG_RT2X00_LIB_LEDS */ - -/* - * Configuration handlers. - */ -static int rt61pci_config_shared_key(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key) -{ - struct hw_key_entry key_entry; - struct rt2x00_field32 field; - u32 mask; - u32 reg; - - if (crypto->cmd == SET_KEY) { - /* - * rt2x00lib can't determine the correct free - * key_idx for shared keys. We have 1 register - * with key valid bits. The goal is simple, read - * the register, if that is full we have no slots - * left. - * Note that each BSS is allowed to have up to 4 - * shared keys, so put a mask over the allowed - * entries. - */ - mask = (0xf << crypto->bssidx); - - rt2x00mmio_register_read(rt2x00dev, SEC_CSR0, ®); - reg &= mask; - - if (reg && reg == mask) - return -ENOSPC; - - key->hw_key_idx += reg ? ffz(reg) : 0; - - /* - * Upload key to hardware - */ - memcpy(key_entry.key, crypto->key, - sizeof(key_entry.key)); - memcpy(key_entry.tx_mic, crypto->tx_mic, - sizeof(key_entry.tx_mic)); - memcpy(key_entry.rx_mic, crypto->rx_mic, - sizeof(key_entry.rx_mic)); - - reg = SHARED_KEY_ENTRY(key->hw_key_idx); - rt2x00mmio_register_multiwrite(rt2x00dev, reg, - &key_entry, sizeof(key_entry)); - - /* - * The cipher types are stored over 2 registers. - * bssidx 0 and 1 keys are stored in SEC_CSR1 and - * bssidx 1 and 2 keys are stored in SEC_CSR5. - * Using the correct defines correctly will cause overhead, - * so just calculate the correct offset. - */ - if (key->hw_key_idx < 8) { - field.bit_offset = (3 * key->hw_key_idx); - field.bit_mask = 0x7 << field.bit_offset; - - rt2x00mmio_register_read(rt2x00dev, SEC_CSR1, ®); - rt2x00_set_field32(®, field, crypto->cipher); - rt2x00mmio_register_write(rt2x00dev, SEC_CSR1, reg); - } else { - field.bit_offset = (3 * (key->hw_key_idx - 8)); - field.bit_mask = 0x7 << field.bit_offset; - - rt2x00mmio_register_read(rt2x00dev, SEC_CSR5, ®); - rt2x00_set_field32(®, field, crypto->cipher); - rt2x00mmio_register_write(rt2x00dev, SEC_CSR5, reg); - } - - /* - * The driver does not support the IV/EIV generation - * in hardware. However it doesn't support the IV/EIV - * inside the ieee80211 frame either, but requires it - * to be provided separately for the descriptor. - * rt2x00lib will cut the IV/EIV data out of all frames - * given to us by mac80211, but we must tell mac80211 - * to generate the IV/EIV data. - */ - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - } - - /* - * SEC_CSR0 contains only single-bit fields to indicate - * a particular key is valid. Because using the FIELD32() - * defines directly will cause a lot of overhead, we use - * a calculation to determine the correct bit directly. - */ - mask = 1 << key->hw_key_idx; - - rt2x00mmio_register_read(rt2x00dev, SEC_CSR0, ®); - if (crypto->cmd == SET_KEY) - reg |= mask; - else if (crypto->cmd == DISABLE_KEY) - reg &= ~mask; - rt2x00mmio_register_write(rt2x00dev, SEC_CSR0, reg); - - return 0; -} - -static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key) -{ - struct hw_pairwise_ta_entry addr_entry; - struct hw_key_entry key_entry; - u32 mask; - u32 reg; - - if (crypto->cmd == SET_KEY) { - /* - * rt2x00lib can't determine the correct free - * key_idx for pairwise keys. We have 2 registers - * with key valid bits. The goal is simple: read - * the first register. If that is full, move to - * the next register. - * When both registers are full, we drop the key. - * Otherwise, we use the first invalid entry. - */ - rt2x00mmio_register_read(rt2x00dev, SEC_CSR2, ®); - if (reg && reg == ~0) { - key->hw_key_idx = 32; - rt2x00mmio_register_read(rt2x00dev, SEC_CSR3, ®); - if (reg && reg == ~0) - return -ENOSPC; - } - - key->hw_key_idx += reg ? ffz(reg) : 0; - - /* - * Upload key to hardware - */ - memcpy(key_entry.key, crypto->key, - sizeof(key_entry.key)); - memcpy(key_entry.tx_mic, crypto->tx_mic, - sizeof(key_entry.tx_mic)); - memcpy(key_entry.rx_mic, crypto->rx_mic, - sizeof(key_entry.rx_mic)); - - memset(&addr_entry, 0, sizeof(addr_entry)); - memcpy(&addr_entry, crypto->address, ETH_ALEN); - addr_entry.cipher = crypto->cipher; - - reg = PAIRWISE_KEY_ENTRY(key->hw_key_idx); - rt2x00mmio_register_multiwrite(rt2x00dev, reg, - &key_entry, sizeof(key_entry)); - - reg = PAIRWISE_TA_ENTRY(key->hw_key_idx); - rt2x00mmio_register_multiwrite(rt2x00dev, reg, - &addr_entry, sizeof(addr_entry)); - - /* - * Enable pairwise lookup table for given BSS idx. - * Without this, received frames will not be decrypted - * by the hardware. - */ - rt2x00mmio_register_read(rt2x00dev, SEC_CSR4, ®); - reg |= (1 << crypto->bssidx); - rt2x00mmio_register_write(rt2x00dev, SEC_CSR4, reg); - - /* - * The driver does not support the IV/EIV generation - * in hardware. However it doesn't support the IV/EIV - * inside the ieee80211 frame either, but requires it - * to be provided separately for the descriptor. - * rt2x00lib will cut the IV/EIV data out of all frames - * given to us by mac80211, but we must tell mac80211 - * to generate the IV/EIV data. - */ - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - } - - /* - * SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate - * a particular key is valid. Because using the FIELD32() - * defines directly will cause a lot of overhead, we use - * a calculation to determine the correct bit directly. - */ - if (key->hw_key_idx < 32) { - mask = 1 << key->hw_key_idx; - - rt2x00mmio_register_read(rt2x00dev, SEC_CSR2, ®); - if (crypto->cmd == SET_KEY) - reg |= mask; - else if (crypto->cmd == DISABLE_KEY) - reg &= ~mask; - rt2x00mmio_register_write(rt2x00dev, SEC_CSR2, reg); - } else { - mask = 1 << (key->hw_key_idx - 32); - - rt2x00mmio_register_read(rt2x00dev, SEC_CSR3, ®); - if (crypto->cmd == SET_KEY) - reg |= mask; - else if (crypto->cmd == DISABLE_KEY) - reg &= ~mask; - rt2x00mmio_register_write(rt2x00dev, SEC_CSR3, reg); - } - - return 0; -} - -static void rt61pci_config_filter(struct rt2x00_dev *rt2x00dev, - const unsigned int filter_flags) -{ - u32 reg; - - /* - * Start configuration steps. - * Note that the version error will always be dropped - * and broadcast frames will always be accepted since - * there is no filter for it at this time. - */ - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, - !(filter_flags & FIF_FCSFAIL)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, - !(filter_flags & FIF_PLCPFAIL)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, - !(filter_flags & (FIF_CONTROL | FIF_PSPOLL))); - rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, 1); - rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, - !rt2x00dev->intf_ap_count); - rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); - rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, - !(filter_flags & FIF_ALLMULTI)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_BROADCAST, 0); - rt2x00_set_field32(®, TXRX_CSR0_DROP_ACK_CTS, - !(filter_flags & FIF_CONTROL)); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); -} - -static void rt61pci_config_intf(struct rt2x00_dev *rt2x00dev, - struct rt2x00_intf *intf, - struct rt2x00intf_conf *conf, - const unsigned int flags) -{ - u32 reg; - - if (flags & CONFIG_UPDATE_TYPE) { - /* - * Enable synchronisation. - */ - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, conf->sync); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); - } - - if (flags & CONFIG_UPDATE_MAC) { - reg = le32_to_cpu(conf->mac[1]); - rt2x00_set_field32(®, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); - conf->mac[1] = cpu_to_le32(reg); - - rt2x00mmio_register_multiwrite(rt2x00dev, MAC_CSR2, - conf->mac, sizeof(conf->mac)); - } - - if (flags & CONFIG_UPDATE_BSSID) { - reg = le32_to_cpu(conf->bssid[1]); - rt2x00_set_field32(®, MAC_CSR5_BSS_ID_MASK, 3); - conf->bssid[1] = cpu_to_le32(reg); - - rt2x00mmio_register_multiwrite(rt2x00dev, MAC_CSR4, - conf->bssid, - sizeof(conf->bssid)); - } -} - -static void rt61pci_config_erp(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_erp *erp, - u32 changed) -{ - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, 0x32); - rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); - - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR4, ®); - rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); - rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, - !!erp->short_preamble); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR4, reg); - } - - if (changed & BSS_CHANGED_BASIC_RATES) - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR5, - erp->basic_rates); - - if (changed & BSS_CHANGED_BEACON_INT) { - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, - erp->beacon_int * 16); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); - } - - if (changed & BSS_CHANGED_ERP_SLOT) { - rt2x00mmio_register_read(rt2x00dev, MAC_CSR9, ®); - rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, erp->slot_time); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR9, reg); - - rt2x00mmio_register_read(rt2x00dev, MAC_CSR8, ®); - rt2x00_set_field32(®, MAC_CSR8_SIFS, erp->sifs); - rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); - rt2x00_set_field32(®, MAC_CSR8_EIFS, erp->eifs); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR8, reg); - } -} - -static void rt61pci_config_antenna_5x(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) -{ - u8 r3; - u8 r4; - u8 r77; - - rt61pci_bbp_read(rt2x00dev, 3, &r3); - rt61pci_bbp_read(rt2x00dev, 4, &r4); - rt61pci_bbp_read(rt2x00dev, 77, &r77); - - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF5325)); - - /* - * Configure the RX antenna. - */ - switch (ant->rx) { - case ANTENNA_HW_DIVERSITY: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); - rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, - (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ)); - break; - case ANTENNA_A: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); - rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); - if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); - else - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); - break; - case ANTENNA_B: - default: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); - rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); - if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); - else - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); - break; - } - - rt61pci_bbp_write(rt2x00dev, 77, r77); - rt61pci_bbp_write(rt2x00dev, 3, r3); - rt61pci_bbp_write(rt2x00dev, 4, r4); -} - -static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) -{ - u8 r3; - u8 r4; - u8 r77; - - rt61pci_bbp_read(rt2x00dev, 3, &r3); - rt61pci_bbp_read(rt2x00dev, 4, &r4); - rt61pci_bbp_read(rt2x00dev, 77, &r77); - - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529)); - rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, - !rt2x00_has_cap_frame_type(rt2x00dev)); - - /* - * Configure the RX antenna. - */ - switch (ant->rx) { - case ANTENNA_HW_DIVERSITY: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); - break; - case ANTENNA_A: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); - break; - case ANTENNA_B: - default: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); - break; - } - - rt61pci_bbp_write(rt2x00dev, 77, r77); - rt61pci_bbp_write(rt2x00dev, 3, r3); - rt61pci_bbp_write(rt2x00dev, 4, r4); -} - -static void rt61pci_config_antenna_2529_rx(struct rt2x00_dev *rt2x00dev, - const int p1, const int p2) -{ - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, MAC_CSR13, ®); - - rt2x00_set_field32(®, MAC_CSR13_DIR4, 0); - rt2x00_set_field32(®, MAC_CSR13_VAL4, p1); - - rt2x00_set_field32(®, MAC_CSR13_DIR3, 0); - rt2x00_set_field32(®, MAC_CSR13_VAL3, !p2); - - rt2x00mmio_register_write(rt2x00dev, MAC_CSR13, reg); -} - -static void rt61pci_config_antenna_2529(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) -{ - u8 r3; - u8 r4; - u8 r77; - - rt61pci_bbp_read(rt2x00dev, 3, &r3); - rt61pci_bbp_read(rt2x00dev, 4, &r4); - rt61pci_bbp_read(rt2x00dev, 77, &r77); - - /* - * Configure the RX antenna. - */ - switch (ant->rx) { - case ANTENNA_A: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); - rt61pci_config_antenna_2529_rx(rt2x00dev, 0, 0); - break; - case ANTENNA_HW_DIVERSITY: - /* - * FIXME: Antenna selection for the rf 2529 is very confusing - * in the legacy driver. Just default to antenna B until the - * legacy code can be properly translated into rt2x00 code. - */ - case ANTENNA_B: - default: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); - rt61pci_config_antenna_2529_rx(rt2x00dev, 1, 1); - break; - } - - rt61pci_bbp_write(rt2x00dev, 77, r77); - rt61pci_bbp_write(rt2x00dev, 3, r3); - rt61pci_bbp_write(rt2x00dev, 4, r4); -} - -struct antenna_sel { - u8 word; - /* - * value[0] -> non-LNA - * value[1] -> LNA - */ - u8 value[2]; -}; - -static const struct antenna_sel antenna_sel_a[] = { - { 96, { 0x58, 0x78 } }, - { 104, { 0x38, 0x48 } }, - { 75, { 0xfe, 0x80 } }, - { 86, { 0xfe, 0x80 } }, - { 88, { 0xfe, 0x80 } }, - { 35, { 0x60, 0x60 } }, - { 97, { 0x58, 0x58 } }, - { 98, { 0x58, 0x58 } }, -}; - -static const struct antenna_sel antenna_sel_bg[] = { - { 96, { 0x48, 0x68 } }, - { 104, { 0x2c, 0x3c } }, - { 75, { 0xfe, 0x80 } }, - { 86, { 0xfe, 0x80 } }, - { 88, { 0xfe, 0x80 } }, - { 35, { 0x50, 0x50 } }, - { 97, { 0x48, 0x48 } }, - { 98, { 0x48, 0x48 } }, -}; - -static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) -{ - const struct antenna_sel *sel; - unsigned int lna; - unsigned int i; - u32 reg; - - /* - * We should never come here because rt2x00lib is supposed - * to catch this and send us the correct antenna explicitely. - */ - BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || - ant->tx == ANTENNA_SW_DIVERSITY); - - if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { - sel = antenna_sel_a; - lna = rt2x00_has_cap_external_lna_a(rt2x00dev); - } else { - sel = antenna_sel_bg; - lna = rt2x00_has_cap_external_lna_bg(rt2x00dev); - } - - for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) - rt61pci_bbp_write(rt2x00dev, sel[i].word, sel[i].value[lna]); - - rt2x00mmio_register_read(rt2x00dev, PHY_CSR0, ®); - - rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, - rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); - rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, - rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); - - rt2x00mmio_register_write(rt2x00dev, PHY_CSR0, reg); - - if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325)) - rt61pci_config_antenna_5x(rt2x00dev, ant); - else if (rt2x00_rf(rt2x00dev, RF2527)) - rt61pci_config_antenna_2x(rt2x00dev, ant); - else if (rt2x00_rf(rt2x00dev, RF2529)) { - if (rt2x00_has_cap_double_antenna(rt2x00dev)) - rt61pci_config_antenna_2x(rt2x00dev, ant); - else - rt61pci_config_antenna_2529(rt2x00dev, ant); - } -} - -static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - u16 eeprom; - short lna_gain = 0; - - if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { - if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) - lna_gain += 14; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); - lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); - } else { - if (rt2x00_has_cap_external_lna_a(rt2x00dev)) - lna_gain += 14; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); - lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); - } - - rt2x00dev->lna_gain = lna_gain; -} - -static void rt61pci_config_channel(struct rt2x00_dev *rt2x00dev, - struct rf_channel *rf, const int txpower) -{ - u8 r3; - u8 r94; - u8 smart; - - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); - rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); - - smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)); - - rt61pci_bbp_read(rt2x00dev, 3, &r3); - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); - rt61pci_bbp_write(rt2x00dev, 3, r3); - - r94 = 6; - if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94)) - r94 += txpower - MAX_TXPOWER; - else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94)) - r94 += txpower; - rt61pci_bbp_write(rt2x00dev, 94, r94); - - rt61pci_rf_write(rt2x00dev, 1, rf->rf1); - rt61pci_rf_write(rt2x00dev, 2, rf->rf2); - rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt61pci_rf_write(rt2x00dev, 4, rf->rf4); - - udelay(200); - - rt61pci_rf_write(rt2x00dev, 1, rf->rf1); - rt61pci_rf_write(rt2x00dev, 2, rf->rf2); - rt61pci_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); - rt61pci_rf_write(rt2x00dev, 4, rf->rf4); - - udelay(200); - - rt61pci_rf_write(rt2x00dev, 1, rf->rf1); - rt61pci_rf_write(rt2x00dev, 2, rf->rf2); - rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt61pci_rf_write(rt2x00dev, 4, rf->rf4); - - msleep(1); -} - -static void rt61pci_config_txpower(struct rt2x00_dev *rt2x00dev, - const int txpower) -{ - struct rf_channel rf; - - rt2x00_rf_read(rt2x00dev, 1, &rf.rf1); - rt2x00_rf_read(rt2x00dev, 2, &rf.rf2); - rt2x00_rf_read(rt2x00dev, 3, &rf.rf3); - rt2x00_rf_read(rt2x00dev, 4, &rf.rf4); - - rt61pci_config_channel(rt2x00dev, &rf, txpower); -} - -static void rt61pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR4, ®); - rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_DOWN, 1); - rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_STEP, 0); - rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_FALLBACK_CCK, 0); - rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, - libconf->conf->long_frame_max_tx_count); - rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, - libconf->conf->short_frame_max_tx_count); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR4, reg); -} - -static void rt61pci_config_ps(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - enum dev_state state = - (libconf->conf->flags & IEEE80211_CONF_PS) ? - STATE_SLEEP : STATE_AWAKE; - u32 reg; - - if (state == STATE_SLEEP) { - rt2x00mmio_register_read(rt2x00dev, MAC_CSR11, ®); - rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, - rt2x00dev->beacon_int - 10); - rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, - libconf->conf->listen_interval - 1); - rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 5); - - /* We must first disable autowake before it can be enabled */ - rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR11, reg); - - rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR11, reg); - - rt2x00mmio_register_write(rt2x00dev, SOFT_RESET_CSR, - 0x00000005); - rt2x00mmio_register_write(rt2x00dev, IO_CNTL_CSR, 0x0000001c); - rt2x00mmio_register_write(rt2x00dev, PCI_USEC_CSR, 0x00000060); - - rt61pci_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0, 0); - } else { - rt2x00mmio_register_read(rt2x00dev, MAC_CSR11, ®); - rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, 0); - rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, 0); - rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); - rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 0); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR11, reg); - - rt2x00mmio_register_write(rt2x00dev, SOFT_RESET_CSR, - 0x00000007); - rt2x00mmio_register_write(rt2x00dev, IO_CNTL_CSR, 0x00000018); - rt2x00mmio_register_write(rt2x00dev, PCI_USEC_CSR, 0x00000020); - - rt61pci_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 0); - } -} - -static void rt61pci_config(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf, - const unsigned int flags) -{ - /* Always recalculate LNA gain before changing configuration */ - rt61pci_config_lna_gain(rt2x00dev, libconf); - - if (flags & IEEE80211_CONF_CHANGE_CHANNEL) - rt61pci_config_channel(rt2x00dev, &libconf->rf, - libconf->conf->power_level); - if ((flags & IEEE80211_CONF_CHANGE_POWER) && - !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) - rt61pci_config_txpower(rt2x00dev, libconf->conf->power_level); - if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) - rt61pci_config_retry_limit(rt2x00dev, libconf); - if (flags & IEEE80211_CONF_CHANGE_PS) - rt61pci_config_ps(rt2x00dev, libconf); -} - -/* - * Link tuning - */ -static void rt61pci_link_stats(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - u32 reg; - - /* - * Update FCS error count from register. - */ - rt2x00mmio_register_read(rt2x00dev, STA_CSR0, ®); - qual->rx_failed = rt2x00_get_field32(reg, STA_CSR0_FCS_ERROR); - - /* - * Update False CCA count from register. - */ - rt2x00mmio_register_read(rt2x00dev, STA_CSR1, ®); - qual->false_cca = rt2x00_get_field32(reg, STA_CSR1_FALSE_CCA_ERROR); -} - -static inline void rt61pci_set_vgc(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, u8 vgc_level) -{ - if (qual->vgc_level != vgc_level) { - rt61pci_bbp_write(rt2x00dev, 17, vgc_level); - qual->vgc_level = vgc_level; - qual->vgc_level_reg = vgc_level; - } -} - -static void rt61pci_reset_tuner(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - rt61pci_set_vgc(rt2x00dev, qual, 0x20); -} - -static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, const u32 count) -{ - u8 up_bound; - u8 low_bound; - - /* - * Determine r17 bounds. - */ - if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { - low_bound = 0x28; - up_bound = 0x48; - if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { - low_bound += 0x10; - up_bound += 0x10; - } - } else { - low_bound = 0x20; - up_bound = 0x40; - if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { - low_bound += 0x10; - up_bound += 0x10; - } - } - - /* - * If we are not associated, we should go straight to the - * dynamic CCA tuning. - */ - if (!rt2x00dev->intf_associated) - goto dynamic_cca_tune; - - /* - * Special big-R17 for very short distance - */ - if (qual->rssi >= -35) { - rt61pci_set_vgc(rt2x00dev, qual, 0x60); - return; - } - - /* - * Special big-R17 for short distance - */ - if (qual->rssi >= -58) { - rt61pci_set_vgc(rt2x00dev, qual, up_bound); - return; - } - - /* - * Special big-R17 for middle-short distance - */ - if (qual->rssi >= -66) { - rt61pci_set_vgc(rt2x00dev, qual, low_bound + 0x10); - return; - } - - /* - * Special mid-R17 for middle distance - */ - if (qual->rssi >= -74) { - rt61pci_set_vgc(rt2x00dev, qual, low_bound + 0x08); - return; - } - - /* - * Special case: Change up_bound based on the rssi. - * Lower up_bound when rssi is weaker then -74 dBm. - */ - up_bound -= 2 * (-74 - qual->rssi); - if (low_bound > up_bound) - up_bound = low_bound; - - if (qual->vgc_level > up_bound) { - rt61pci_set_vgc(rt2x00dev, qual, up_bound); - return; - } - -dynamic_cca_tune: - - /* - * r17 does not yet exceed upper limit, continue and base - * the r17 tuning on the false CCA count. - */ - if ((qual->false_cca > 512) && (qual->vgc_level < up_bound)) - rt61pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level); - else if ((qual->false_cca < 100) && (qual->vgc_level > low_bound)) - rt61pci_set_vgc(rt2x00dev, qual, --qual->vgc_level); -} - -/* - * Queue handlers. - */ -static void rt61pci_start_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); - rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); - break; - default: - break; - } -} - -static void rt61pci_kick_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_AC_VO: - rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); - rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC0, 1); - rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); - break; - case QID_AC_VI: - rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); - rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC1, 1); - rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); - break; - case QID_AC_BE: - rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); - rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC2, 1); - rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); - break; - case QID_AC_BK: - rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); - rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC3, 1); - rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); - break; - default: - break; - } -} - -static void rt61pci_stop_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_AC_VO: - rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); - rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC0, 1); - rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); - break; - case QID_AC_VI: - rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); - rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC1, 1); - rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); - break; - case QID_AC_BE: - rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); - rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC2, 1); - rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); - break; - case QID_AC_BK: - rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); - rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC3, 1); - rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); - break; - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 1); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); - rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); - - /* - * Wait for possibly running tbtt tasklets. - */ - tasklet_kill(&rt2x00dev->tbtt_tasklet); - break; - default: - break; - } -} - -/* - * Firmware functions - */ -static char *rt61pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) -{ - u16 chip; - char *fw_name; - - pci_read_config_word(to_pci_dev(rt2x00dev->dev), PCI_DEVICE_ID, &chip); - switch (chip) { - case RT2561_PCI_ID: - fw_name = FIRMWARE_RT2561; - break; - case RT2561s_PCI_ID: - fw_name = FIRMWARE_RT2561s; - break; - case RT2661_PCI_ID: - fw_name = FIRMWARE_RT2661; - break; - default: - fw_name = NULL; - break; - } - - return fw_name; -} - -static int rt61pci_check_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - u16 fw_crc; - u16 crc; - - /* - * Only support 8kb firmware files. - */ - if (len != 8192) - return FW_BAD_LENGTH; - - /* - * The last 2 bytes in the firmware array are the crc checksum itself. - * This means that we should never pass those 2 bytes to the crc - * algorithm. - */ - fw_crc = (data[len - 2] << 8 | data[len - 1]); - - /* - * Use the crc itu-t algorithm. - */ - crc = crc_itu_t(0, data, len - 2); - crc = crc_itu_t_byte(crc, 0); - crc = crc_itu_t_byte(crc, 0); - - return (fw_crc == crc) ? FW_OK : FW_BAD_CRC; -} - -static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - int i; - u32 reg; - - /* - * Wait for stable hardware. - */ - for (i = 0; i < 100; i++) { - rt2x00mmio_register_read(rt2x00dev, MAC_CSR0, ®); - if (reg) - break; - msleep(1); - } - - if (!reg) { - rt2x00_err(rt2x00dev, "Unstable hardware\n"); - return -EBUSY; - } - - /* - * Prepare MCU and mailbox for firmware loading. - */ - reg = 0; - rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 1); - rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); - rt2x00mmio_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); - rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); - rt2x00mmio_register_write(rt2x00dev, HOST_CMD_CSR, 0); - - /* - * Write firmware to device. - */ - reg = 0; - rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 1); - rt2x00_set_field32(®, MCU_CNTL_CSR_SELECT_BANK, 1); - rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); - - rt2x00mmio_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, - data, len); - - rt2x00_set_field32(®, MCU_CNTL_CSR_SELECT_BANK, 0); - rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); - - rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 0); - rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); - - for (i = 0; i < 100; i++) { - rt2x00mmio_register_read(rt2x00dev, MCU_CNTL_CSR, ®); - if (rt2x00_get_field32(reg, MCU_CNTL_CSR_READY)) - break; - msleep(1); - } - - if (i == 100) { - rt2x00_err(rt2x00dev, "MCU Control register not ready\n"); - return -EBUSY; - } - - /* - * Hardware needs another millisecond before it is ready. - */ - msleep(1); - - /* - * Reset MAC and BBP registers. - */ - reg = 0; - rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); - rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); - - rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); - rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); - rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); - - rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); - rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); - - return 0; -} - -/* - * Initialization functions. - */ -static bool rt61pci_get_entry_state(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 0, &word); - - return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); - } else { - rt2x00_desc_read(entry_priv->desc, 0, &word); - - return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || - rt2x00_get_field32(word, TXD_W0_VALID)); - } -} - -static void rt61pci_clear_entry(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 5, &word); - rt2x00_set_field32(&word, RXD_W5_BUFFER_PHYSICAL_ADDRESS, - skbdesc->skb_dma); - rt2x00_desc_write(entry_priv->desc, 5, word); - - rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); - rt2x00_desc_write(entry_priv->desc, 0, word); - } else { - rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_set_field32(&word, TXD_W0_VALID, 0); - rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); - rt2x00_desc_write(entry_priv->desc, 0, word); - } -} - -static int rt61pci_init_queues(struct rt2x00_dev *rt2x00dev) -{ - struct queue_entry_priv_mmio *entry_priv; - u32 reg; - - /* - * Initialize registers. - */ - rt2x00mmio_register_read(rt2x00dev, TX_RING_CSR0, ®); - rt2x00_set_field32(®, TX_RING_CSR0_AC0_RING_SIZE, - rt2x00dev->tx[0].limit); - rt2x00_set_field32(®, TX_RING_CSR0_AC1_RING_SIZE, - rt2x00dev->tx[1].limit); - rt2x00_set_field32(®, TX_RING_CSR0_AC2_RING_SIZE, - rt2x00dev->tx[2].limit); - rt2x00_set_field32(®, TX_RING_CSR0_AC3_RING_SIZE, - rt2x00dev->tx[3].limit); - rt2x00mmio_register_write(rt2x00dev, TX_RING_CSR0, reg); - - rt2x00mmio_register_read(rt2x00dev, TX_RING_CSR1, ®); - rt2x00_set_field32(®, TX_RING_CSR1_TXD_SIZE, - rt2x00dev->tx[0].desc_size / 4); - rt2x00mmio_register_write(rt2x00dev, TX_RING_CSR1, reg); - - entry_priv = rt2x00dev->tx[0].entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, AC0_BASE_CSR, ®); - rt2x00_set_field32(®, AC0_BASE_CSR_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, AC0_BASE_CSR, reg); - - entry_priv = rt2x00dev->tx[1].entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, AC1_BASE_CSR, ®); - rt2x00_set_field32(®, AC1_BASE_CSR_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, AC1_BASE_CSR, reg); - - entry_priv = rt2x00dev->tx[2].entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, AC2_BASE_CSR, ®); - rt2x00_set_field32(®, AC2_BASE_CSR_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, AC2_BASE_CSR, reg); - - entry_priv = rt2x00dev->tx[3].entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, AC3_BASE_CSR, ®); - rt2x00_set_field32(®, AC3_BASE_CSR_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, AC3_BASE_CSR, reg); - - rt2x00mmio_register_read(rt2x00dev, RX_RING_CSR, ®); - rt2x00_set_field32(®, RX_RING_CSR_RING_SIZE, rt2x00dev->rx->limit); - rt2x00_set_field32(®, RX_RING_CSR_RXD_SIZE, - rt2x00dev->rx->desc_size / 4); - rt2x00_set_field32(®, RX_RING_CSR_RXD_WRITEBACK_SIZE, 4); - rt2x00mmio_register_write(rt2x00dev, RX_RING_CSR, reg); - - entry_priv = rt2x00dev->rx->entries[0].priv_data; - rt2x00mmio_register_read(rt2x00dev, RX_BASE_CSR, ®); - rt2x00_set_field32(®, RX_BASE_CSR_RING_REGISTER, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, RX_BASE_CSR, reg); - - rt2x00mmio_register_read(rt2x00dev, TX_DMA_DST_CSR, ®); - rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC0, 2); - rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC1, 2); - rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC2, 2); - rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC3, 2); - rt2x00mmio_register_write(rt2x00dev, TX_DMA_DST_CSR, reg); - - rt2x00mmio_register_read(rt2x00dev, LOAD_TX_RING_CSR, ®); - rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC0, 1); - rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC1, 1); - rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC2, 1); - rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC3, 1); - rt2x00mmio_register_write(rt2x00dev, LOAD_TX_RING_CSR, reg); - - rt2x00mmio_register_read(rt2x00dev, RX_CNTL_CSR, ®); - rt2x00_set_field32(®, RX_CNTL_CSR_LOAD_RXD, 1); - rt2x00mmio_register_write(rt2x00dev, RX_CNTL_CSR, reg); - - return 0; -} - -static int rt61pci_init_registers(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_AUTO_TX_SEQ, 1); - rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); - rt2x00_set_field32(®, TXRX_CSR0_TX_WITHOUT_WAITING, 0); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); - - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR1, ®); - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0, 47); /* CCK Signal */ - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1, 30); /* Rssi */ - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2, 42); /* OFDM Rate */ - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3, 30); /* Rssi */ - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3_VALID, 1); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR1, reg); - - /* - * CCK TXD BBP registers - */ - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR2, ®); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0, 13); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1, 12); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2, 11); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3, 10); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3_VALID, 1); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR2, reg); - - /* - * OFDM TXD BBP registers - */ - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR3, ®); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0, 7); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1, 6); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2, 5); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2_VALID, 1); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR3, reg); - - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR7, ®); - rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_6MBS, 59); - rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_9MBS, 53); - rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_12MBS, 49); - rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_18MBS, 46); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR7, reg); - - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR8, ®); - rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_24MBS, 44); - rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_36MBS, 42); - rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_48MBS, 42); - rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_54MBS, 42); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR8, reg); - - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, 0); - rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); - rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 0); - rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); - rt2x00_set_field32(®, TXRX_CSR9_TIMESTAMP_COMPENSATE, 0); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); - - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); - - rt2x00mmio_register_write(rt2x00dev, MAC_CSR6, 0x00000fff); - - rt2x00mmio_register_read(rt2x00dev, MAC_CSR9, ®); - rt2x00_set_field32(®, MAC_CSR9_CW_SELECT, 0); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR9, reg); - - rt2x00mmio_register_write(rt2x00dev, MAC_CSR10, 0x0000071c); - - if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) - return -EBUSY; - - rt2x00mmio_register_write(rt2x00dev, MAC_CSR13, 0x0000e000); - - /* - * Invalidate all Shared Keys (SEC_CSR0), - * and clear the Shared key Cipher algorithms (SEC_CSR1 & SEC_CSR5) - */ - rt2x00mmio_register_write(rt2x00dev, SEC_CSR0, 0x00000000); - rt2x00mmio_register_write(rt2x00dev, SEC_CSR1, 0x00000000); - rt2x00mmio_register_write(rt2x00dev, SEC_CSR5, 0x00000000); - - rt2x00mmio_register_write(rt2x00dev, PHY_CSR1, 0x000023b0); - rt2x00mmio_register_write(rt2x00dev, PHY_CSR5, 0x060a100c); - rt2x00mmio_register_write(rt2x00dev, PHY_CSR6, 0x00080606); - rt2x00mmio_register_write(rt2x00dev, PHY_CSR7, 0x00000a08); - - rt2x00mmio_register_write(rt2x00dev, PCI_CFG_CSR, 0x28ca4404); - - rt2x00mmio_register_write(rt2x00dev, TEST_MODE_CSR, 0x00000200); - - rt2x00mmio_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); - - /* - * Clear all beacons - * For the Beacon base registers we only need to clear - * the first byte since that byte contains the VALID and OWNER - * bits which (when set to 0) will invalidate the entire beacon. - */ - rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE0, 0); - rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE1, 0); - rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE2, 0); - rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE3, 0); - - /* - * We must clear the error counters. - * These registers are cleared on read, - * so we may pass a useless variable to store the value. - */ - rt2x00mmio_register_read(rt2x00dev, STA_CSR0, ®); - rt2x00mmio_register_read(rt2x00dev, STA_CSR1, ®); - rt2x00mmio_register_read(rt2x00dev, STA_CSR2, ®); - - /* - * Reset MAC and BBP registers. - */ - rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); - rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); - rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); - - rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); - rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); - rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); - - rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); - rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); - - return 0; -} - -static int rt61pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u8 value; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt61pci_bbp_read(rt2x00dev, 0, &value); - if ((value != 0xff) && (value != 0x00)) - return 0; - udelay(REGISTER_BUSY_DELAY); - } - - rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); - return -EACCES; -} - -static int rt61pci_init_bbp(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u16 eeprom; - u8 reg_id; - u8 value; - - if (unlikely(rt61pci_wait_bbp_ready(rt2x00dev))) - return -EACCES; - - rt61pci_bbp_write(rt2x00dev, 3, 0x00); - rt61pci_bbp_write(rt2x00dev, 15, 0x30); - rt61pci_bbp_write(rt2x00dev, 21, 0xc8); - rt61pci_bbp_write(rt2x00dev, 22, 0x38); - rt61pci_bbp_write(rt2x00dev, 23, 0x06); - rt61pci_bbp_write(rt2x00dev, 24, 0xfe); - rt61pci_bbp_write(rt2x00dev, 25, 0x0a); - rt61pci_bbp_write(rt2x00dev, 26, 0x0d); - rt61pci_bbp_write(rt2x00dev, 34, 0x12); - rt61pci_bbp_write(rt2x00dev, 37, 0x07); - rt61pci_bbp_write(rt2x00dev, 39, 0xf8); - rt61pci_bbp_write(rt2x00dev, 41, 0x60); - rt61pci_bbp_write(rt2x00dev, 53, 0x10); - rt61pci_bbp_write(rt2x00dev, 54, 0x18); - rt61pci_bbp_write(rt2x00dev, 60, 0x10); - rt61pci_bbp_write(rt2x00dev, 61, 0x04); - rt61pci_bbp_write(rt2x00dev, 62, 0x04); - rt61pci_bbp_write(rt2x00dev, 75, 0xfe); - rt61pci_bbp_write(rt2x00dev, 86, 0xfe); - rt61pci_bbp_write(rt2x00dev, 88, 0xfe); - rt61pci_bbp_write(rt2x00dev, 90, 0x0f); - rt61pci_bbp_write(rt2x00dev, 99, 0x00); - rt61pci_bbp_write(rt2x00dev, 102, 0x16); - rt61pci_bbp_write(rt2x00dev, 107, 0x04); - - for (i = 0; i < EEPROM_BBP_SIZE; i++) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); - - if (eeprom != 0xffff && eeprom != 0x0000) { - reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); - value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - rt61pci_bbp_write(rt2x00dev, reg_id, value); - } - } - - return 0; -} - -/* - * Device state switch handlers. - */ -static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - int mask = (state == STATE_RADIO_IRQ_OFF); - u32 reg; - unsigned long flags; - - /* - * When interrupts are being enabled, the interrupt registers - * should clear the register to assure a clean state. - */ - if (state == STATE_RADIO_IRQ_ON) { - rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); - rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); - - rt2x00mmio_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®); - rt2x00mmio_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg); - } - - /* - * Only toggle the interrupts bits we are going to use. - * Non-checked interrupt bits are disabled by default. - */ - spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); - - rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); - rt2x00_set_field32(®, INT_MASK_CSR_TXDONE, mask); - rt2x00_set_field32(®, INT_MASK_CSR_RXDONE, mask); - rt2x00_set_field32(®, INT_MASK_CSR_BEACON_DONE, mask); - rt2x00_set_field32(®, INT_MASK_CSR_ENABLE_MITIGATION, mask); - rt2x00_set_field32(®, INT_MASK_CSR_MITIGATION_PERIOD, 0xff); - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - - rt2x00mmio_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); - rt2x00_set_field32(®, MCU_INT_MASK_CSR_0, mask); - rt2x00_set_field32(®, MCU_INT_MASK_CSR_1, mask); - rt2x00_set_field32(®, MCU_INT_MASK_CSR_2, mask); - rt2x00_set_field32(®, MCU_INT_MASK_CSR_3, mask); - rt2x00_set_field32(®, MCU_INT_MASK_CSR_4, mask); - rt2x00_set_field32(®, MCU_INT_MASK_CSR_5, mask); - rt2x00_set_field32(®, MCU_INT_MASK_CSR_6, mask); - rt2x00_set_field32(®, MCU_INT_MASK_CSR_7, mask); - rt2x00_set_field32(®, MCU_INT_MASK_CSR_TWAKEUP, mask); - rt2x00mmio_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); - - spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); - - if (state == STATE_RADIO_IRQ_OFF) { - /* - * Ensure that all tasklets are finished. - */ - tasklet_kill(&rt2x00dev->txstatus_tasklet); - tasklet_kill(&rt2x00dev->rxdone_tasklet); - tasklet_kill(&rt2x00dev->autowake_tasklet); - tasklet_kill(&rt2x00dev->tbtt_tasklet); - } -} - -static int rt61pci_enable_radio(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - /* - * Initialize all registers. - */ - if (unlikely(rt61pci_init_queues(rt2x00dev) || - rt61pci_init_registers(rt2x00dev) || - rt61pci_init_bbp(rt2x00dev))) - return -EIO; - - /* - * Enable RX. - */ - rt2x00mmio_register_read(rt2x00dev, RX_CNTL_CSR, ®); - rt2x00_set_field32(®, RX_CNTL_CSR_ENABLE_RX_DMA, 1); - rt2x00mmio_register_write(rt2x00dev, RX_CNTL_CSR, reg); - - return 0; -} - -static void rt61pci_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - /* - * Disable power - */ - rt2x00mmio_register_write(rt2x00dev, MAC_CSR10, 0x00001818); -} - -static int rt61pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) -{ - u32 reg, reg2; - unsigned int i; - char put_to_sleep; - - put_to_sleep = (state != STATE_AWAKE); - - rt2x00mmio_register_read(rt2x00dev, MAC_CSR12, ®); - rt2x00_set_field32(®, MAC_CSR12_FORCE_WAKEUP, !put_to_sleep); - rt2x00_set_field32(®, MAC_CSR12_PUT_TO_SLEEP, put_to_sleep); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR12, reg); - - /* - * Device is not guaranteed to be in the requested state yet. - * We must wait until the register indicates that the - * device has entered the correct state. - */ - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00mmio_register_read(rt2x00dev, MAC_CSR12, ®2); - state = rt2x00_get_field32(reg2, MAC_CSR12_BBP_CURRENT_STATE); - if (state == !put_to_sleep) - return 0; - rt2x00mmio_register_write(rt2x00dev, MAC_CSR12, reg); - msleep(10); - } - - return -EBUSY; -} - -static int rt61pci_set_device_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - int retval = 0; - - switch (state) { - case STATE_RADIO_ON: - retval = rt61pci_enable_radio(rt2x00dev); - break; - case STATE_RADIO_OFF: - rt61pci_disable_radio(rt2x00dev); - break; - case STATE_RADIO_IRQ_ON: - case STATE_RADIO_IRQ_OFF: - rt61pci_toggle_irq(rt2x00dev, state); - break; - case STATE_DEEP_SLEEP: - case STATE_SLEEP: - case STATE_STANDBY: - case STATE_AWAKE: - retval = rt61pci_set_state(rt2x00dev, state); - break; - default: - retval = -ENOTSUPP; - break; - } - - if (unlikely(retval)) - rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", - state, retval); - - return retval; -} - -/* - * TX descriptor initialization - */ -static void rt61pci_write_tx_desc(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - __le32 *txd = entry_priv->desc; - u32 word; - - /* - * Start writing the descriptor words. - */ - rt2x00_desc_read(txd, 1, &word); - rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, entry->queue->qid); - rt2x00_set_field32(&word, TXD_W1_AIFSN, entry->queue->aifs); - rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); - rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); - rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); - rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, - test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W1_BUFFER_COUNT, 1); - rt2x00_desc_write(txd, 1, word); - - rt2x00_desc_read(txd, 2, &word); - rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); - rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, - txdesc->u.plcp.length_low); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, - txdesc->u.plcp.length_high); - rt2x00_desc_write(txd, 2, word); - - if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { - _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); - _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); - } - - rt2x00_desc_read(txd, 5, &word); - rt2x00_set_field32(&word, TXD_W5_PID_TYPE, entry->queue->qid); - rt2x00_set_field32(&word, TXD_W5_PID_SUBTYPE, - skbdesc->entry->entry_idx); - rt2x00_set_field32(&word, TXD_W5_TX_POWER, - TXPOWER_TO_DEV(entry->queue->rt2x00dev->tx_power)); - rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1); - rt2x00_desc_write(txd, 5, word); - - if (entry->queue->qid != QID_BEACON) { - rt2x00_desc_read(txd, 6, &word); - rt2x00_set_field32(&word, TXD_W6_BUFFER_PHYSICAL_ADDRESS, - skbdesc->skb_dma); - rt2x00_desc_write(txd, 6, word); - - rt2x00_desc_read(txd, 11, &word); - rt2x00_set_field32(&word, TXD_W11_BUFFER_LENGTH0, - txdesc->length); - rt2x00_desc_write(txd, 11, word); - } - - /* - * Writing TXD word 0 must the last to prevent a race condition with - * the device, whereby the device may take hold of the TXD before we - * finished updating it. - */ - rt2x00_desc_read(txd, 0, &word); - rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); - rt2x00_set_field32(&word, TXD_W0_VALID, 1); - rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, - test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_ACK, - test_bit(ENTRY_TXD_ACK, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, - test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_OFDM, - (txdesc->rate_mode == RATE_MODE_OFDM)); - rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); - rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, - test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, - test_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_KEY_TABLE, - test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx); - rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); - rt2x00_set_field32(&word, TXD_W0_BURST, - test_bit(ENTRY_TXD_BURST, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher); - rt2x00_desc_write(txd, 0, word); - - /* - * Register descriptor details in skb frame descriptor. - */ - skbdesc->desc = txd; - skbdesc->desc_len = (entry->queue->qid == QID_BEACON) ? TXINFO_SIZE : - TXD_DESC_SIZE; -} - -/* - * TX data initialization - */ -static void rt61pci_write_beacon(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - unsigned int beacon_base; - unsigned int padding_len; - u32 orig_reg, reg; - - /* - * Disable beaconing while we are reloading the beacon data, - * otherwise we might be sending out invalid data. - */ - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); - orig_reg = reg; - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); - - /* - * Write the TX descriptor for the beacon. - */ - rt61pci_write_tx_desc(entry, txdesc); - - /* - * Dump beacon to userspace through debugfs. - */ - rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); - - /* - * Write entire beacon with descriptor and padding to register. - */ - padding_len = roundup(entry->skb->len, 4) - entry->skb->len; - if (padding_len && skb_pad(entry->skb, padding_len)) { - rt2x00_err(rt2x00dev, "Failure padding beacon, aborting\n"); - /* skb freed by skb_pad() on failure */ - entry->skb = NULL; - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, orig_reg); - return; - } - - beacon_base = HW_BEACON_OFFSET(entry->entry_idx); - rt2x00mmio_register_multiwrite(rt2x00dev, beacon_base, - entry_priv->desc, TXINFO_SIZE); - rt2x00mmio_register_multiwrite(rt2x00dev, beacon_base + TXINFO_SIZE, - entry->skb->data, - entry->skb->len + padding_len); - - /* - * Enable beaconing again. - * - * For Wi-Fi faily generated beacons between participating - * stations. Set TBTT phase adaptive adjustment step to 8us. - */ - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR10, 0x00001008); - - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); - - /* - * Clean up beacon skb. - */ - dev_kfree_skb_any(entry->skb); - entry->skb = NULL; -} - -static void rt61pci_clear_beacon(struct queue_entry *entry) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - u32 orig_reg, reg; - - /* - * Disable beaconing while we are reloading the beacon data, - * otherwise we might be sending out invalid data. - */ - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, &orig_reg); - reg = orig_reg; - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); - - /* - * Clear beacon. - */ - rt2x00mmio_register_write(rt2x00dev, - HW_BEACON_OFFSET(entry->entry_idx), 0); - - /* - * Restore global beaconing state. - */ - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, orig_reg); -} - -/* - * RX control handlers - */ -static int rt61pci_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) -{ - u8 offset = rt2x00dev->lna_gain; - u8 lna; - - lna = rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_LNA); - switch (lna) { - case 3: - offset += 90; - break; - case 2: - offset += 74; - break; - case 1: - offset += 64; - break; - default: - return 0; - } - - if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { - if (lna == 3 || lna == 2) - offset += 10; - } - - return rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_AGC) * 2 - offset; -} - -static void rt61pci_fill_rxdone(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - u32 word0; - u32 word1; - - rt2x00_desc_read(entry_priv->desc, 0, &word0); - rt2x00_desc_read(entry_priv->desc, 1, &word1); - - if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; - - rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG); - rxdesc->cipher_status = rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR); - - if (rxdesc->cipher != CIPHER_NONE) { - _rt2x00_desc_read(entry_priv->desc, 2, &rxdesc->iv[0]); - _rt2x00_desc_read(entry_priv->desc, 3, &rxdesc->iv[1]); - rxdesc->dev_flags |= RXDONE_CRYPTO_IV; - - _rt2x00_desc_read(entry_priv->desc, 4, &rxdesc->icv); - rxdesc->dev_flags |= RXDONE_CRYPTO_ICV; - - /* - * Hardware has stripped IV/EIV data from 802.11 frame during - * decryption. It has provided the data separately but rt2x00lib - * should decide if it should be reinserted. - */ - rxdesc->flags |= RX_FLAG_IV_STRIPPED; - - /* - * The hardware has already checked the Michael Mic and has - * stripped it from the frame. Signal this to mac80211. - */ - rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; - - if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) - rxdesc->flags |= RX_FLAG_DECRYPTED; - else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) - rxdesc->flags |= RX_FLAG_MMIC_ERROR; - } - - /* - * Obtain the status about this packet. - * When frame was received with an OFDM bitrate, - * the signal is the PLCP value. If it was received with - * a CCK bitrate the signal is the rate in 100kbit/s. - */ - rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); - rxdesc->rssi = rt61pci_agc_to_rssi(rt2x00dev, word1); - rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); - - if (rt2x00_get_field32(word0, RXD_W0_OFDM)) - rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; - else - rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; - if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) - rxdesc->dev_flags |= RXDONE_MY_BSS; -} - -/* - * Interrupt functions. - */ -static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - struct queue_entry *entry; - struct queue_entry *entry_done; - struct queue_entry_priv_mmio *entry_priv; - struct txdone_entry_desc txdesc; - u32 word; - u32 reg; - int type; - int index; - int i; - - /* - * TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO - * at most X times and also stop processing once the TX_STA_FIFO_VALID - * flag is not set anymore. - * - * The legacy drivers use X=TX_RING_SIZE but state in a comment - * that the TX_STA_FIFO stack has a size of 16. We stick to our - * tx ring size for now. - */ - for (i = 0; i < rt2x00dev->tx->limit; i++) { - rt2x00mmio_register_read(rt2x00dev, STA_CSR4, ®); - if (!rt2x00_get_field32(reg, STA_CSR4_VALID)) - break; - - /* - * Skip this entry when it contains an invalid - * queue identication number. - */ - type = rt2x00_get_field32(reg, STA_CSR4_PID_TYPE); - queue = rt2x00queue_get_tx_queue(rt2x00dev, type); - if (unlikely(!queue)) - continue; - - /* - * Skip this entry when it contains an invalid - * index number. - */ - index = rt2x00_get_field32(reg, STA_CSR4_PID_SUBTYPE); - if (unlikely(index >= queue->limit)) - continue; - - entry = &queue->entries[index]; - entry_priv = entry->priv_data; - rt2x00_desc_read(entry_priv->desc, 0, &word); - - if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || - !rt2x00_get_field32(word, TXD_W0_VALID)) - return; - - entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - while (entry != entry_done) { - /* Catch up. - * Just report any entries we missed as failed. - */ - rt2x00_warn(rt2x00dev, "TX status report missed for entry %d\n", - entry_done->entry_idx); - - rt2x00lib_txdone_noinfo(entry_done, TXDONE_UNKNOWN); - entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - } - - /* - * Obtain the status about this packet. - */ - txdesc.flags = 0; - switch (rt2x00_get_field32(reg, STA_CSR4_TX_RESULT)) { - case 0: /* Success, maybe with retry */ - __set_bit(TXDONE_SUCCESS, &txdesc.flags); - break; - case 6: /* Failure, excessive retries */ - __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); - /* Don't break, this is a failed frame! */ - default: /* Failure */ - __set_bit(TXDONE_FAILURE, &txdesc.flags); - } - txdesc.retry = rt2x00_get_field32(reg, STA_CSR4_RETRY_COUNT); - - /* - * the frame was retried at least once - * -> hw used fallback rates - */ - if (txdesc.retry) - __set_bit(TXDONE_FALLBACK, &txdesc.flags); - - rt2x00lib_txdone(entry, &txdesc); - } -} - -static void rt61pci_wakeup(struct rt2x00_dev *rt2x00dev) -{ - struct rt2x00lib_conf libconf = { .conf = &rt2x00dev->hw->conf }; - - rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); -} - -static inline void rt61pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, - struct rt2x00_field32 irq_field) -{ - u32 reg; - - /* - * Enable a single interrupt. The interrupt mask register - * access needs locking. - */ - spin_lock_irq(&rt2x00dev->irqmask_lock); - - rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); - rt2x00_set_field32(®, irq_field, 0); - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - - spin_unlock_irq(&rt2x00dev->irqmask_lock); -} - -static void rt61pci_enable_mcu_interrupt(struct rt2x00_dev *rt2x00dev, - struct rt2x00_field32 irq_field) -{ - u32 reg; - - /* - * Enable a single MCU interrupt. The interrupt mask register - * access needs locking. - */ - spin_lock_irq(&rt2x00dev->irqmask_lock); - - rt2x00mmio_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); - rt2x00_set_field32(®, irq_field, 0); - rt2x00mmio_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); - - spin_unlock_irq(&rt2x00dev->irqmask_lock); -} - -static void rt61pci_txstatus_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - rt61pci_txdone(rt2x00dev); - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TXDONE); -} - -static void rt61pci_tbtt_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - rt2x00lib_beacondone(rt2x00dev); - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_BEACON_DONE); -} - -static void rt61pci_rxdone_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - if (rt2x00mmio_rxdone(rt2x00dev)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RXDONE); -} - -static void rt61pci_autowake_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - rt61pci_wakeup(rt2x00dev); - rt2x00mmio_register_write(rt2x00dev, - M2H_CMD_DONE_CSR, 0xffffffff); - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt61pci_enable_mcu_interrupt(rt2x00dev, MCU_INT_MASK_CSR_TWAKEUP); -} - -static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) -{ - struct rt2x00_dev *rt2x00dev = dev_instance; - u32 reg_mcu, mask_mcu; - u32 reg, mask; - - /* - * Get the interrupt sources & saved to local variable. - * Write register value back to clear pending interrupts. - */ - rt2x00mmio_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®_mcu); - rt2x00mmio_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg_mcu); - - rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); - rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); - - if (!reg && !reg_mcu) - return IRQ_NONE; - - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return IRQ_HANDLED; - - /* - * Schedule tasklets for interrupt handling. - */ - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RXDONE)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE)) - tasklet_schedule(&rt2x00dev->txstatus_tasklet); - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_BEACON_DONE)) - tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); - - if (rt2x00_get_field32(reg_mcu, MCU_INT_SOURCE_CSR_TWAKEUP)) - tasklet_schedule(&rt2x00dev->autowake_tasklet); - - /* - * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits - * for interrupts and interrupt masks we can just use the value of - * INT_SOURCE_CSR to create the interrupt mask. - */ - mask = reg; - mask_mcu = reg_mcu; - - /* - * Disable all interrupts for which a tasklet was scheduled right now, - * the tasklet will reenable the appropriate interrupts. - */ - spin_lock(&rt2x00dev->irqmask_lock); - - rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); - reg |= mask; - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - - rt2x00mmio_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); - reg |= mask_mcu; - rt2x00mmio_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); - - spin_unlock(&rt2x00dev->irqmask_lock); - - return IRQ_HANDLED; -} - -/* - * Device probe functions. - */ -static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) -{ - struct eeprom_93cx6 eeprom; - u32 reg; - u16 word; - u8 *mac; - s8 value; - - rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); - - eeprom.data = rt2x00dev; - eeprom.register_read = rt61pci_eepromregister_read; - eeprom.register_write = rt61pci_eepromregister_write; - eeprom.width = rt2x00_get_field32(reg, E2PROM_CSR_TYPE_93C46) ? - PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; - eeprom.reg_data_in = 0; - eeprom.reg_data_out = 0; - eeprom.reg_data_clock = 0; - eeprom.reg_chip_select = 0; - - eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, - EEPROM_SIZE / sizeof(u16)); - - /* - * Start validation of the data that has been read. - */ - mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); - rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, - ANTENNA_B); - rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, - ANTENNA_B); - rt2x00_set_field16(&word, EEPROM_ANTENNA_FRAME_TYPE, 0); - rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); - rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); - rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF5225); - rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); - rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_NIC_ENABLE_DIVERSITY, 0); - rt2x00_set_field16(&word, EEPROM_NIC_TX_DIVERSITY, 0); - rt2x00_set_field16(&word, EEPROM_NIC_RX_FIXED, 0); - rt2x00_set_field16(&word, EEPROM_NIC_TX_FIXED, 0); - rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_BG, 0); - rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); - rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_A, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); - rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_LED_LED_MODE, - LED_MODE_DEFAULT); - rt2x00_eeprom_write(rt2x00dev, EEPROM_LED, word); - rt2x00_eeprom_dbg(rt2x00dev, "Led: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); - rt2x00_set_field16(&word, EEPROM_FREQ_SEQ, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); - rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); - rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); - } else { - value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_1); - if (value < -10 || value > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); - value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_2); - if (value < -10 || value > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); - rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET A: 0x%04x\n", word); - } else { - value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_1); - if (value < -10 || value > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); - value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_2); - if (value < -10 || value > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); - } - - return 0; -} - -static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - u16 value; - u16 eeprom; - - /* - * Read EEPROM word for configuration. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); - - /* - * Identify RF chipset. - */ - value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); - rt2x00mmio_register_read(rt2x00dev, MAC_CSR0, ®); - rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), - value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); - - if (!rt2x00_rf(rt2x00dev, RF5225) && - !rt2x00_rf(rt2x00dev, RF5325) && - !rt2x00_rf(rt2x00dev, RF2527) && - !rt2x00_rf(rt2x00dev, RF2529)) { - rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); - return -ENODEV; - } - - /* - * Determine number of antennas. - */ - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_NUM) == 2) - __set_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags); - - /* - * Identify default antenna configuration. - */ - rt2x00dev->default_ant.tx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); - rt2x00dev->default_ant.rx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); - - /* - * Read the Frame type. - */ - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_FRAME_TYPE)) - __set_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags); - - /* - * Detect if this device has a hardware controlled radio. - */ - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) - __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); - - /* - * Read frequency offset and RF programming sequence. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); - if (rt2x00_get_field16(eeprom, EEPROM_FREQ_SEQ)) - __set_bit(CAPABILITY_RF_SEQUENCE, &rt2x00dev->cap_flags); - - rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); - - /* - * Read external LNA informations. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); - - if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_A)) - __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_BG)) - __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); - - /* - * When working with a RF2529 chip without double antenna, - * the antenna settings should be gathered from the NIC - * eeprom word. - */ - if (rt2x00_rf(rt2x00dev, RF2529) && - !rt2x00_has_cap_double_antenna(rt2x00dev)) { - rt2x00dev->default_ant.rx = - ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED); - rt2x00dev->default_ant.tx = - ANTENNA_B - rt2x00_get_field16(eeprom, EEPROM_NIC_TX_FIXED); - - if (rt2x00_get_field16(eeprom, EEPROM_NIC_TX_DIVERSITY)) - rt2x00dev->default_ant.tx = ANTENNA_SW_DIVERSITY; - if (rt2x00_get_field16(eeprom, EEPROM_NIC_ENABLE_DIVERSITY)) - rt2x00dev->default_ant.rx = ANTENNA_SW_DIVERSITY; - } - - /* - * Store led settings, for correct led behaviour. - * If the eeprom value is invalid, - * switch to default led mode. - */ -#ifdef CONFIG_RT2X00_LIB_LEDS - rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); - value = rt2x00_get_field16(eeprom, EEPROM_LED_LED_MODE); - - rt61pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); - rt61pci_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); - if (value == LED_MODE_SIGNAL_STRENGTH) - rt61pci_init_led(rt2x00dev, &rt2x00dev->led_qual, - LED_TYPE_QUALITY); - - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_LED_MODE, value); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_0, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_GPIO_0)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_1, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_GPIO_1)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_2, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_GPIO_2)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_3, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_GPIO_3)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_4, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_GPIO_4)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_ACT, - rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_ACT)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_BG, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_RDY_G)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_RDY_A)); -#endif /* CONFIG_RT2X00_LIB_LEDS */ - - return 0; -} - -/* - * RF value list for RF5225 & RF5325 - * Supports: 2.4 GHz & 5.2 GHz, rf_sequence disabled - */ -static const struct rf_channel rf_vals_noseq[] = { - { 1, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa0b }, - { 2, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa1f }, - { 3, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa0b }, - { 4, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa1f }, - { 5, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa0b }, - { 6, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa1f }, - { 7, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa0b }, - { 8, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa1f }, - { 9, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa0b }, - { 10, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa1f }, - { 11, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa0b }, - { 12, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa1f }, - { 13, 0x00002ccc, 0x0000479e, 0x00068455, 0x000ffa0b }, - { 14, 0x00002ccc, 0x000047a2, 0x00068455, 0x000ffa13 }, - - /* 802.11 UNI / HyperLan 2 */ - { 36, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa23 }, - { 40, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa03 }, - { 44, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa0b }, - { 48, 0x00002ccc, 0x000049aa, 0x0009be55, 0x000ffa13 }, - { 52, 0x00002ccc, 0x000049ae, 0x0009ae55, 0x000ffa1b }, - { 56, 0x00002ccc, 0x000049b2, 0x0009ae55, 0x000ffa23 }, - { 60, 0x00002ccc, 0x000049ba, 0x0009ae55, 0x000ffa03 }, - { 64, 0x00002ccc, 0x000049be, 0x0009ae55, 0x000ffa0b }, - - /* 802.11 HyperLan 2 */ - { 100, 0x00002ccc, 0x00004a2a, 0x000bae55, 0x000ffa03 }, - { 104, 0x00002ccc, 0x00004a2e, 0x000bae55, 0x000ffa0b }, - { 108, 0x00002ccc, 0x00004a32, 0x000bae55, 0x000ffa13 }, - { 112, 0x00002ccc, 0x00004a36, 0x000bae55, 0x000ffa1b }, - { 116, 0x00002ccc, 0x00004a3a, 0x000bbe55, 0x000ffa23 }, - { 120, 0x00002ccc, 0x00004a82, 0x000bbe55, 0x000ffa03 }, - { 124, 0x00002ccc, 0x00004a86, 0x000bbe55, 0x000ffa0b }, - { 128, 0x00002ccc, 0x00004a8a, 0x000bbe55, 0x000ffa13 }, - { 132, 0x00002ccc, 0x00004a8e, 0x000bbe55, 0x000ffa1b }, - { 136, 0x00002ccc, 0x00004a92, 0x000bbe55, 0x000ffa23 }, - - /* 802.11 UNII */ - { 140, 0x00002ccc, 0x00004a9a, 0x000bbe55, 0x000ffa03 }, - { 149, 0x00002ccc, 0x00004aa2, 0x000bbe55, 0x000ffa1f }, - { 153, 0x00002ccc, 0x00004aa6, 0x000bbe55, 0x000ffa27 }, - { 157, 0x00002ccc, 0x00004aae, 0x000bbe55, 0x000ffa07 }, - { 161, 0x00002ccc, 0x00004ab2, 0x000bbe55, 0x000ffa0f }, - { 165, 0x00002ccc, 0x00004ab6, 0x000bbe55, 0x000ffa17 }, - - /* MMAC(Japan)J52 ch 34,38,42,46 */ - { 34, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa0b }, - { 38, 0x00002ccc, 0x0000499e, 0x0009be55, 0x000ffa13 }, - { 42, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa1b }, - { 46, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa23 }, -}; - -/* - * RF value list for RF5225 & RF5325 - * Supports: 2.4 GHz & 5.2 GHz, rf_sequence enabled - */ -static const struct rf_channel rf_vals_seq[] = { - { 1, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa0b }, - { 2, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa1f }, - { 3, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa0b }, - { 4, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa1f }, - { 5, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa0b }, - { 6, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa1f }, - { 7, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa0b }, - { 8, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa1f }, - { 9, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa0b }, - { 10, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa1f }, - { 11, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa0b }, - { 12, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa1f }, - { 13, 0x00002ccc, 0x0000479e, 0x00068455, 0x000ffa0b }, - { 14, 0x00002ccc, 0x000047a2, 0x00068455, 0x000ffa13 }, - - /* 802.11 UNI / HyperLan 2 */ - { 36, 0x00002cd4, 0x0004481a, 0x00098455, 0x000c0a03 }, - { 40, 0x00002cd0, 0x00044682, 0x00098455, 0x000c0a03 }, - { 44, 0x00002cd0, 0x00044686, 0x00098455, 0x000c0a1b }, - { 48, 0x00002cd0, 0x0004468e, 0x00098655, 0x000c0a0b }, - { 52, 0x00002cd0, 0x00044692, 0x00098855, 0x000c0a23 }, - { 56, 0x00002cd0, 0x0004469a, 0x00098c55, 0x000c0a13 }, - { 60, 0x00002cd0, 0x000446a2, 0x00098e55, 0x000c0a03 }, - { 64, 0x00002cd0, 0x000446a6, 0x00099255, 0x000c0a1b }, - - /* 802.11 HyperLan 2 */ - { 100, 0x00002cd4, 0x0004489a, 0x000b9855, 0x000c0a03 }, - { 104, 0x00002cd4, 0x000448a2, 0x000b9855, 0x000c0a03 }, - { 108, 0x00002cd4, 0x000448aa, 0x000b9855, 0x000c0a03 }, - { 112, 0x00002cd4, 0x000448b2, 0x000b9a55, 0x000c0a03 }, - { 116, 0x00002cd4, 0x000448ba, 0x000b9a55, 0x000c0a03 }, - { 120, 0x00002cd0, 0x00044702, 0x000b9a55, 0x000c0a03 }, - { 124, 0x00002cd0, 0x00044706, 0x000b9a55, 0x000c0a1b }, - { 128, 0x00002cd0, 0x0004470e, 0x000b9c55, 0x000c0a0b }, - { 132, 0x00002cd0, 0x00044712, 0x000b9c55, 0x000c0a23 }, - { 136, 0x00002cd0, 0x0004471a, 0x000b9e55, 0x000c0a13 }, - - /* 802.11 UNII */ - { 140, 0x00002cd0, 0x00044722, 0x000b9e55, 0x000c0a03 }, - { 149, 0x00002cd0, 0x0004472e, 0x000ba255, 0x000c0a1b }, - { 153, 0x00002cd0, 0x00044736, 0x000ba255, 0x000c0a0b }, - { 157, 0x00002cd4, 0x0004490a, 0x000ba255, 0x000c0a17 }, - { 161, 0x00002cd4, 0x00044912, 0x000ba255, 0x000c0a17 }, - { 165, 0x00002cd4, 0x0004491a, 0x000ba255, 0x000c0a17 }, - - /* MMAC(Japan)J52 ch 34,38,42,46 */ - { 34, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000c0a0b }, - { 38, 0x00002ccc, 0x0000499e, 0x0009be55, 0x000c0a13 }, - { 42, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000c0a1b }, - { 46, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000c0a23 }, -}; - -static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) -{ - struct hw_mode_spec *spec = &rt2x00dev->spec; - struct channel_info *info; - char *tx_power; - unsigned int i; - - /* - * Disable powersaving as default. - */ - rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - /* - * Initialize all hw fields. - */ - ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); - ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); - ieee80211_hw_set(rt2x00dev->hw, HOST_BROADCAST_PS_BUFFERING); - ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); - - SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); - SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, - rt2x00_eeprom_addr(rt2x00dev, - EEPROM_MAC_ADDR_0)); - - /* - * As rt61 has a global fallback table we cannot specify - * more then one tx rate per frame but since the hw will - * try several rates (based on the fallback table) we should - * initialize max_report_rates to the maximum number of rates - * we are going to try. Otherwise mac80211 will truncate our - * reported tx rates and the rc algortihm will end up with - * incorrect data. - */ - rt2x00dev->hw->max_rates = 1; - rt2x00dev->hw->max_report_rates = 7; - rt2x00dev->hw->max_rate_tries = 1; - - /* - * Initialize hw_mode information. - */ - spec->supported_bands = SUPPORT_BAND_2GHZ; - spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - - if (!rt2x00_has_cap_rf_sequence(rt2x00dev)) { - spec->num_channels = 14; - spec->channels = rf_vals_noseq; - } else { - spec->num_channels = 14; - spec->channels = rf_vals_seq; - } - - if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; - spec->num_channels = ARRAY_SIZE(rf_vals_seq); - } - - /* - * Create channel information array - */ - info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - spec->channels_info = info; - - tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); - for (i = 0; i < 14; i++) { - info[i].max_power = MAX_TXPOWER; - info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); - } - - if (spec->num_channels > 14) { - tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); - for (i = 14; i < spec->num_channels; i++) { - info[i].max_power = MAX_TXPOWER; - info[i].default_power1 = - TXPOWER_FROM_DEV(tx_power[i - 14]); - } - } - - return 0; -} - -static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) -{ - int retval; - u32 reg; - - /* - * Disable power saving. - */ - rt2x00mmio_register_write(rt2x00dev, SOFT_RESET_CSR, 0x00000007); - - /* - * Allocate eeprom data. - */ - retval = rt61pci_validate_eeprom(rt2x00dev); - if (retval) - return retval; - - retval = rt61pci_init_eeprom(rt2x00dev); - if (retval) - return retval; - - /* - * Enable rfkill polling by setting GPIO direction of the - * rfkill switch GPIO pin correctly. - */ - rt2x00mmio_register_read(rt2x00dev, MAC_CSR13, ®); - rt2x00_set_field32(®, MAC_CSR13_DIR5, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_CSR13, reg); - - /* - * Initialize hw specifications. - */ - retval = rt61pci_probe_hw_mode(rt2x00dev); - if (retval) - return retval; - - /* - * This device has multiple filters for control frames, - * but has no a separate filter for PS Poll frames. - */ - __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); - - /* - * This device requires firmware and DMA mapped skbs. - */ - __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); - if (!modparam_nohwcrypt) - __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); - __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); - - /* - * Set the rssi offset. - */ - rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; - - return 0; -} - -/* - * IEEE80211 stack callback functions. - */ -static int rt61pci_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue_idx, - const struct ieee80211_tx_queue_params *params) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct data_queue *queue; - struct rt2x00_field32 field; - int retval; - u32 reg; - u32 offset; - - /* - * First pass the configuration through rt2x00lib, that will - * update the queue settings and validate the input. After that - * we are free to update the registers based on the value - * in the queue parameter. - */ - retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); - if (retval) - return retval; - - /* - * We only need to perform additional register initialization - * for WMM queues. - */ - if (queue_idx >= 4) - return 0; - - queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); - - /* Update WMM TXOP register */ - offset = AC_TXOP_CSR0 + (sizeof(u32) * (!!(queue_idx & 2))); - field.bit_offset = (queue_idx & 1) * 16; - field.bit_mask = 0xffff << field.bit_offset; - - rt2x00mmio_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, field, queue->txop); - rt2x00mmio_register_write(rt2x00dev, offset, reg); - - /* Update WMM registers */ - field.bit_offset = queue_idx * 4; - field.bit_mask = 0xf << field.bit_offset; - - rt2x00mmio_register_read(rt2x00dev, AIFSN_CSR, ®); - rt2x00_set_field32(®, field, queue->aifs); - rt2x00mmio_register_write(rt2x00dev, AIFSN_CSR, reg); - - rt2x00mmio_register_read(rt2x00dev, CWMIN_CSR, ®); - rt2x00_set_field32(®, field, queue->cw_min); - rt2x00mmio_register_write(rt2x00dev, CWMIN_CSR, reg); - - rt2x00mmio_register_read(rt2x00dev, CWMAX_CSR, ®); - rt2x00_set_field32(®, field, queue->cw_max); - rt2x00mmio_register_write(rt2x00dev, CWMAX_CSR, reg); - - return 0; -} - -static u64 rt61pci_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u64 tsf; - u32 reg; - - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR13, ®); - tsf = (u64) rt2x00_get_field32(reg, TXRX_CSR13_HIGH_TSFTIMER) << 32; - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR12, ®); - tsf |= rt2x00_get_field32(reg, TXRX_CSR12_LOW_TSFTIMER); - - return tsf; -} - -static const struct ieee80211_ops rt61pci_mac80211_ops = { - .tx = rt2x00mac_tx, - .start = rt2x00mac_start, - .stop = rt2x00mac_stop, - .add_interface = rt2x00mac_add_interface, - .remove_interface = rt2x00mac_remove_interface, - .config = rt2x00mac_config, - .configure_filter = rt2x00mac_configure_filter, - .set_key = rt2x00mac_set_key, - .sw_scan_start = rt2x00mac_sw_scan_start, - .sw_scan_complete = rt2x00mac_sw_scan_complete, - .get_stats = rt2x00mac_get_stats, - .bss_info_changed = rt2x00mac_bss_info_changed, - .conf_tx = rt61pci_conf_tx, - .get_tsf = rt61pci_get_tsf, - .rfkill_poll = rt2x00mac_rfkill_poll, - .flush = rt2x00mac_flush, - .set_antenna = rt2x00mac_set_antenna, - .get_antenna = rt2x00mac_get_antenna, - .get_ringparam = rt2x00mac_get_ringparam, - .tx_frames_pending = rt2x00mac_tx_frames_pending, -}; - -static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { - .irq_handler = rt61pci_interrupt, - .txstatus_tasklet = rt61pci_txstatus_tasklet, - .tbtt_tasklet = rt61pci_tbtt_tasklet, - .rxdone_tasklet = rt61pci_rxdone_tasklet, - .autowake_tasklet = rt61pci_autowake_tasklet, - .probe_hw = rt61pci_probe_hw, - .get_firmware_name = rt61pci_get_firmware_name, - .check_firmware = rt61pci_check_firmware, - .load_firmware = rt61pci_load_firmware, - .initialize = rt2x00mmio_initialize, - .uninitialize = rt2x00mmio_uninitialize, - .get_entry_state = rt61pci_get_entry_state, - .clear_entry = rt61pci_clear_entry, - .set_device_state = rt61pci_set_device_state, - .rfkill_poll = rt61pci_rfkill_poll, - .link_stats = rt61pci_link_stats, - .reset_tuner = rt61pci_reset_tuner, - .link_tuner = rt61pci_link_tuner, - .start_queue = rt61pci_start_queue, - .kick_queue = rt61pci_kick_queue, - .stop_queue = rt61pci_stop_queue, - .flush_queue = rt2x00mmio_flush_queue, - .write_tx_desc = rt61pci_write_tx_desc, - .write_beacon = rt61pci_write_beacon, - .clear_beacon = rt61pci_clear_beacon, - .fill_rxdone = rt61pci_fill_rxdone, - .config_shared_key = rt61pci_config_shared_key, - .config_pairwise_key = rt61pci_config_pairwise_key, - .config_filter = rt61pci_config_filter, - .config_intf = rt61pci_config_intf, - .config_erp = rt61pci_config_erp, - .config_ant = rt61pci_config_ant, - .config = rt61pci_config, -}; - -static void rt61pci_queue_init(struct data_queue *queue) -{ - switch (queue->qid) { - case QID_RX: - queue->limit = 32; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = RXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - queue->limit = 32; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_BEACON: - queue->limit = 4; - queue->data_size = 0; /* No DMA required for beacons */ - queue->desc_size = TXINFO_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_ATIM: - /* fallthrough */ - default: - BUG(); - break; - } -} - -static const struct rt2x00_ops rt61pci_ops = { - .name = KBUILD_MODNAME, - .max_ap_intf = 4, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .queue_init = rt61pci_queue_init, - .lib = &rt61pci_rt2x00_ops, - .hw = &rt61pci_mac80211_ops, -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt61pci_rt2x00debug, -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -}; - -/* - * RT61pci module information. - */ -static const struct pci_device_id rt61pci_device_table[] = { - /* RT2561s */ - { PCI_DEVICE(0x1814, 0x0301) }, - /* RT2561 v2 */ - { PCI_DEVICE(0x1814, 0x0302) }, - /* RT2661 */ - { PCI_DEVICE(0x1814, 0x0401) }, - { 0, } -}; - -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("Ralink RT61 PCI & PCMCIA Wireless LAN driver."); -MODULE_SUPPORTED_DEVICE("Ralink RT2561, RT2561s & RT2661 " - "PCI & PCMCIA chipset based cards"); -MODULE_DEVICE_TABLE(pci, rt61pci_device_table); -MODULE_FIRMWARE(FIRMWARE_RT2561); -MODULE_FIRMWARE(FIRMWARE_RT2561s); -MODULE_FIRMWARE(FIRMWARE_RT2661); -MODULE_LICENSE("GPL"); - -static int rt61pci_probe(struct pci_dev *pci_dev, - const struct pci_device_id *id) -{ - return rt2x00pci_probe(pci_dev, &rt61pci_ops); -} - -static struct pci_driver rt61pci_driver = { - .name = KBUILD_MODNAME, - .id_table = rt61pci_device_table, - .probe = rt61pci_probe, - .remove = rt2x00pci_remove, - .suspend = rt2x00pci_suspend, - .resume = rt2x00pci_resume, -}; - -module_pci_driver(rt61pci_driver); diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h deleted file mode 100644 index 1442075a8382..000000000000 --- a/drivers/net/wireless/rt2x00/rt61pci.h +++ /dev/null @@ -1,1500 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt61pci - Abstract: Data structures and registers for the rt61pci module. - Supported chipsets: RT2561, RT2561s, RT2661. - */ - -#ifndef RT61PCI_H -#define RT61PCI_H - -/* - * RT chip PCI IDs. - */ -#define RT2561s_PCI_ID 0x0301 -#define RT2561_PCI_ID 0x0302 -#define RT2661_PCI_ID 0x0401 - -/* - * RF chip defines. - */ -#define RF5225 0x0001 -#define RF5325 0x0002 -#define RF2527 0x0003 -#define RF2529 0x0004 - -/* - * Signal information. - * Default offset is required for RSSI <-> dBm conversion. - */ -#define DEFAULT_RSSI_OFFSET 120 - -/* - * Register layout information. - */ -#define CSR_REG_BASE 0x3000 -#define CSR_REG_SIZE 0x04b0 -#define EEPROM_BASE 0x0000 -#define EEPROM_SIZE 0x0100 -#define BBP_BASE 0x0000 -#define BBP_SIZE 0x0080 -#define RF_BASE 0x0004 -#define RF_SIZE 0x0010 - -/* - * Number of TX queues. - */ -#define NUM_TX_QUEUES 4 - -/* - * PCI registers. - */ - -/* - * HOST_CMD_CSR: For HOST to interrupt embedded processor - */ -#define HOST_CMD_CSR 0x0008 -#define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x0000007f) -#define HOST_CMD_CSR_INTERRUPT_MCU FIELD32(0x00000080) - -/* - * MCU_CNTL_CSR - * SELECT_BANK: Select 8051 program bank. - * RESET: Enable 8051 reset state. - * READY: Ready state for 8051. - */ -#define MCU_CNTL_CSR 0x000c -#define MCU_CNTL_CSR_SELECT_BANK FIELD32(0x00000001) -#define MCU_CNTL_CSR_RESET FIELD32(0x00000002) -#define MCU_CNTL_CSR_READY FIELD32(0x00000004) - -/* - * SOFT_RESET_CSR - * FORCE_CLOCK_ON: Host force MAC clock ON - */ -#define SOFT_RESET_CSR 0x0010 -#define SOFT_RESET_CSR_FORCE_CLOCK_ON FIELD32(0x00000002) - -/* - * MCU_INT_SOURCE_CSR: MCU interrupt source/mask register. - */ -#define MCU_INT_SOURCE_CSR 0x0014 -#define MCU_INT_SOURCE_CSR_0 FIELD32(0x00000001) -#define MCU_INT_SOURCE_CSR_1 FIELD32(0x00000002) -#define MCU_INT_SOURCE_CSR_2 FIELD32(0x00000004) -#define MCU_INT_SOURCE_CSR_3 FIELD32(0x00000008) -#define MCU_INT_SOURCE_CSR_4 FIELD32(0x00000010) -#define MCU_INT_SOURCE_CSR_5 FIELD32(0x00000020) -#define MCU_INT_SOURCE_CSR_6 FIELD32(0x00000040) -#define MCU_INT_SOURCE_CSR_7 FIELD32(0x00000080) -#define MCU_INT_SOURCE_CSR_TWAKEUP FIELD32(0x00000100) -#define MCU_INT_SOURCE_CSR_TBTT_EXPIRE FIELD32(0x00000200) - -/* - * MCU_INT_MASK_CSR: MCU interrupt source/mask register. - */ -#define MCU_INT_MASK_CSR 0x0018 -#define MCU_INT_MASK_CSR_0 FIELD32(0x00000001) -#define MCU_INT_MASK_CSR_1 FIELD32(0x00000002) -#define MCU_INT_MASK_CSR_2 FIELD32(0x00000004) -#define MCU_INT_MASK_CSR_3 FIELD32(0x00000008) -#define MCU_INT_MASK_CSR_4 FIELD32(0x00000010) -#define MCU_INT_MASK_CSR_5 FIELD32(0x00000020) -#define MCU_INT_MASK_CSR_6 FIELD32(0x00000040) -#define MCU_INT_MASK_CSR_7 FIELD32(0x00000080) -#define MCU_INT_MASK_CSR_TWAKEUP FIELD32(0x00000100) -#define MCU_INT_MASK_CSR_TBTT_EXPIRE FIELD32(0x00000200) - -/* - * PCI_USEC_CSR - */ -#define PCI_USEC_CSR 0x001c - -/* - * Security key table memory. - * 16 entries 32-byte for shared key table - * 64 entries 32-byte for pairwise key table - * 64 entries 8-byte for pairwise ta key table - */ -#define SHARED_KEY_TABLE_BASE 0x1000 -#define PAIRWISE_KEY_TABLE_BASE 0x1200 -#define PAIRWISE_TA_TABLE_BASE 0x1a00 - -#define SHARED_KEY_ENTRY(__idx) \ - ( SHARED_KEY_TABLE_BASE + \ - ((__idx) * sizeof(struct hw_key_entry)) ) -#define PAIRWISE_KEY_ENTRY(__idx) \ - ( PAIRWISE_KEY_TABLE_BASE + \ - ((__idx) * sizeof(struct hw_key_entry)) ) -#define PAIRWISE_TA_ENTRY(__idx) \ - ( PAIRWISE_TA_TABLE_BASE + \ - ((__idx) * sizeof(struct hw_pairwise_ta_entry)) ) - -struct hw_key_entry { - u8 key[16]; - u8 tx_mic[8]; - u8 rx_mic[8]; -} __packed; - -struct hw_pairwise_ta_entry { - u8 address[6]; - u8 cipher; - u8 reserved; -} __packed; - -/* - * Other on-chip shared memory space. - */ -#define HW_CIS_BASE 0x2000 -#define HW_NULL_BASE 0x2b00 - -/* - * Since NULL frame won't be that long (256 byte), - * We steal 16 tail bytes to save debugging settings. - */ -#define HW_DEBUG_SETTING_BASE 0x2bf0 - -/* - * On-chip BEACON frame space. - */ -#define HW_BEACON_BASE0 0x2c00 -#define HW_BEACON_BASE1 0x2d00 -#define HW_BEACON_BASE2 0x2e00 -#define HW_BEACON_BASE3 0x2f00 - -#define HW_BEACON_OFFSET(__index) \ - ( HW_BEACON_BASE0 + (__index * 0x0100) ) - -/* - * HOST-MCU shared memory. - */ - -/* - * H2M_MAILBOX_CSR: Host-to-MCU Mailbox. - */ -#define H2M_MAILBOX_CSR 0x2100 -#define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff) -#define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00) -#define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000) -#define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000) - -/* - * MCU_LEDCS: LED control for MCU Mailbox. - */ -#define MCU_LEDCS_LED_MODE FIELD16(0x001f) -#define MCU_LEDCS_RADIO_STATUS FIELD16(0x0020) -#define MCU_LEDCS_LINK_BG_STATUS FIELD16(0x0040) -#define MCU_LEDCS_LINK_A_STATUS FIELD16(0x0080) -#define MCU_LEDCS_POLARITY_GPIO_0 FIELD16(0x0100) -#define MCU_LEDCS_POLARITY_GPIO_1 FIELD16(0x0200) -#define MCU_LEDCS_POLARITY_GPIO_2 FIELD16(0x0400) -#define MCU_LEDCS_POLARITY_GPIO_3 FIELD16(0x0800) -#define MCU_LEDCS_POLARITY_GPIO_4 FIELD16(0x1000) -#define MCU_LEDCS_POLARITY_ACT FIELD16(0x2000) -#define MCU_LEDCS_POLARITY_READY_BG FIELD16(0x4000) -#define MCU_LEDCS_POLARITY_READY_A FIELD16(0x8000) - -/* - * M2H_CMD_DONE_CSR. - */ -#define M2H_CMD_DONE_CSR 0x2104 - -/* - * MCU_TXOP_ARRAY_BASE. - */ -#define MCU_TXOP_ARRAY_BASE 0x2110 - -/* - * MAC Control/Status Registers(CSR). - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * MAC_CSR0: ASIC revision number. - */ -#define MAC_CSR0 0x3000 -#define MAC_CSR0_REVISION FIELD32(0x0000000f) -#define MAC_CSR0_CHIPSET FIELD32(0x000ffff0) - -/* - * MAC_CSR1: System control register. - * SOFT_RESET: Software reset bit, 1: reset, 0: normal. - * BBP_RESET: Hardware reset BBP. - * HOST_READY: Host is ready after initialization, 1: ready. - */ -#define MAC_CSR1 0x3004 -#define MAC_CSR1_SOFT_RESET FIELD32(0x00000001) -#define MAC_CSR1_BBP_RESET FIELD32(0x00000002) -#define MAC_CSR1_HOST_READY FIELD32(0x00000004) - -/* - * MAC_CSR2: STA MAC register 0. - */ -#define MAC_CSR2 0x3008 -#define MAC_CSR2_BYTE0 FIELD32(0x000000ff) -#define MAC_CSR2_BYTE1 FIELD32(0x0000ff00) -#define MAC_CSR2_BYTE2 FIELD32(0x00ff0000) -#define MAC_CSR2_BYTE3 FIELD32(0xff000000) - -/* - * MAC_CSR3: STA MAC register 1. - * UNICAST_TO_ME_MASK: - * Used to mask off bits from byte 5 of the MAC address - * to determine the UNICAST_TO_ME bit for RX frames. - * The full mask is complemented by BSS_ID_MASK: - * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK - */ -#define MAC_CSR3 0x300c -#define MAC_CSR3_BYTE4 FIELD32(0x000000ff) -#define MAC_CSR3_BYTE5 FIELD32(0x0000ff00) -#define MAC_CSR3_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) - -/* - * MAC_CSR4: BSSID register 0. - */ -#define MAC_CSR4 0x3010 -#define MAC_CSR4_BYTE0 FIELD32(0x000000ff) -#define MAC_CSR4_BYTE1 FIELD32(0x0000ff00) -#define MAC_CSR4_BYTE2 FIELD32(0x00ff0000) -#define MAC_CSR4_BYTE3 FIELD32(0xff000000) - -/* - * MAC_CSR5: BSSID register 1. - * BSS_ID_MASK: - * This mask is used to mask off bits 0 and 1 of byte 5 of the - * BSSID. This will make sure that those bits will be ignored - * when determining the MY_BSS of RX frames. - * 0: 1-BSSID mode (BSS index = 0) - * 1: 2-BSSID mode (BSS index: Byte5, bit 0) - * 2: 2-BSSID mode (BSS index: byte5, bit 1) - * 3: 4-BSSID mode (BSS index: byte5, bit 0 - 1) - */ -#define MAC_CSR5 0x3014 -#define MAC_CSR5_BYTE4 FIELD32(0x000000ff) -#define MAC_CSR5_BYTE5 FIELD32(0x0000ff00) -#define MAC_CSR5_BSS_ID_MASK FIELD32(0x00ff0000) - -/* - * MAC_CSR6: Maximum frame length register. - */ -#define MAC_CSR6 0x3018 -#define MAC_CSR6_MAX_FRAME_UNIT FIELD32(0x00000fff) - -/* - * MAC_CSR7: Reserved - */ -#define MAC_CSR7 0x301c - -/* - * MAC_CSR8: SIFS/EIFS register. - * All units are in US. - */ -#define MAC_CSR8 0x3020 -#define MAC_CSR8_SIFS FIELD32(0x000000ff) -#define MAC_CSR8_SIFS_AFTER_RX_OFDM FIELD32(0x0000ff00) -#define MAC_CSR8_EIFS FIELD32(0xffff0000) - -/* - * MAC_CSR9: Back-Off control register. - * SLOT_TIME: Slot time, default is 20us for 802.11BG. - * CWMIN: Bit for Cwmin. default Cwmin is 31 (2^5 - 1). - * CWMAX: Bit for Cwmax, default Cwmax is 1023 (2^10 - 1). - * CW_SELECT: 1: CWmin/Cwmax select from register, 0:select from TxD. - */ -#define MAC_CSR9 0x3024 -#define MAC_CSR9_SLOT_TIME FIELD32(0x000000ff) -#define MAC_CSR9_CWMIN FIELD32(0x00000f00) -#define MAC_CSR9_CWMAX FIELD32(0x0000f000) -#define MAC_CSR9_CW_SELECT FIELD32(0x00010000) - -/* - * MAC_CSR10: Power state configuration. - */ -#define MAC_CSR10 0x3028 - -/* - * MAC_CSR11: Power saving transition time register. - * DELAY_AFTER_TBCN: Delay after Tbcn expired in units of TU. - * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. - * WAKEUP_LATENCY: In unit of TU. - */ -#define MAC_CSR11 0x302c -#define MAC_CSR11_DELAY_AFTER_TBCN FIELD32(0x000000ff) -#define MAC_CSR11_TBCN_BEFORE_WAKEUP FIELD32(0x00007f00) -#define MAC_CSR11_AUTOWAKE FIELD32(0x00008000) -#define MAC_CSR11_WAKEUP_LATENCY FIELD32(0x000f0000) - -/* - * MAC_CSR12: Manual power control / status register (merge CSR20 & PWRCSR1). - * CURRENT_STATE: 0:sleep, 1:awake. - * FORCE_WAKEUP: This has higher priority than PUT_TO_SLEEP. - * BBP_CURRENT_STATE: 0: BBP sleep, 1: BBP awake. - */ -#define MAC_CSR12 0x3030 -#define MAC_CSR12_CURRENT_STATE FIELD32(0x00000001) -#define MAC_CSR12_PUT_TO_SLEEP FIELD32(0x00000002) -#define MAC_CSR12_FORCE_WAKEUP FIELD32(0x00000004) -#define MAC_CSR12_BBP_CURRENT_STATE FIELD32(0x00000008) - -/* - * MAC_CSR13: GPIO. - * MAC_CSR13_VALx: GPIO value - * MAC_CSR13_DIRx: GPIO direction: 0 = output; 1 = input - */ -#define MAC_CSR13 0x3034 -#define MAC_CSR13_VAL0 FIELD32(0x00000001) -#define MAC_CSR13_VAL1 FIELD32(0x00000002) -#define MAC_CSR13_VAL2 FIELD32(0x00000004) -#define MAC_CSR13_VAL3 FIELD32(0x00000008) -#define MAC_CSR13_VAL4 FIELD32(0x00000010) -#define MAC_CSR13_VAL5 FIELD32(0x00000020) -#define MAC_CSR13_DIR0 FIELD32(0x00000100) -#define MAC_CSR13_DIR1 FIELD32(0x00000200) -#define MAC_CSR13_DIR2 FIELD32(0x00000400) -#define MAC_CSR13_DIR3 FIELD32(0x00000800) -#define MAC_CSR13_DIR4 FIELD32(0x00001000) -#define MAC_CSR13_DIR5 FIELD32(0x00002000) - -/* - * MAC_CSR14: LED control register. - * ON_PERIOD: On period, default 70ms. - * OFF_PERIOD: Off period, default 30ms. - * HW_LED: HW TX activity, 1: normal OFF, 0: normal ON. - * SW_LED: s/w LED, 1: ON, 0: OFF. - * HW_LED_POLARITY: 0: active low, 1: active high. - */ -#define MAC_CSR14 0x3038 -#define MAC_CSR14_ON_PERIOD FIELD32(0x000000ff) -#define MAC_CSR14_OFF_PERIOD FIELD32(0x0000ff00) -#define MAC_CSR14_HW_LED FIELD32(0x00010000) -#define MAC_CSR14_SW_LED FIELD32(0x00020000) -#define MAC_CSR14_HW_LED_POLARITY FIELD32(0x00040000) -#define MAC_CSR14_SW_LED2 FIELD32(0x00080000) - -/* - * MAC_CSR15: NAV control. - */ -#define MAC_CSR15 0x303c - -/* - * TXRX control registers. - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * TXRX_CSR0: TX/RX configuration register. - * TSF_OFFSET: Default is 24. - * AUTO_TX_SEQ: 1: ASIC auto replace sequence nr in outgoing frame. - * DISABLE_RX: Disable Rx engine. - * DROP_CRC: Drop CRC error. - * DROP_PHYSICAL: Drop physical error. - * DROP_CONTROL: Drop control frame. - * DROP_NOT_TO_ME: Drop not to me unicast frame. - * DROP_TO_DS: Drop fram ToDs bit is true. - * DROP_VERSION_ERROR: Drop version error frame. - * DROP_MULTICAST: Drop multicast frames. - * DROP_BORADCAST: Drop broadcast frames. - * DROP_ACK_CTS: Drop received ACK and CTS. - */ -#define TXRX_CSR0 0x3040 -#define TXRX_CSR0_RX_ACK_TIMEOUT FIELD32(0x000001ff) -#define TXRX_CSR0_TSF_OFFSET FIELD32(0x00007e00) -#define TXRX_CSR0_AUTO_TX_SEQ FIELD32(0x00008000) -#define TXRX_CSR0_DISABLE_RX FIELD32(0x00010000) -#define TXRX_CSR0_DROP_CRC FIELD32(0x00020000) -#define TXRX_CSR0_DROP_PHYSICAL FIELD32(0x00040000) -#define TXRX_CSR0_DROP_CONTROL FIELD32(0x00080000) -#define TXRX_CSR0_DROP_NOT_TO_ME FIELD32(0x00100000) -#define TXRX_CSR0_DROP_TO_DS FIELD32(0x00200000) -#define TXRX_CSR0_DROP_VERSION_ERROR FIELD32(0x00400000) -#define TXRX_CSR0_DROP_MULTICAST FIELD32(0x00800000) -#define TXRX_CSR0_DROP_BROADCAST FIELD32(0x01000000) -#define TXRX_CSR0_DROP_ACK_CTS FIELD32(0x02000000) -#define TXRX_CSR0_TX_WITHOUT_WAITING FIELD32(0x04000000) - -/* - * TXRX_CSR1 - */ -#define TXRX_CSR1 0x3044 -#define TXRX_CSR1_BBP_ID0 FIELD32(0x0000007f) -#define TXRX_CSR1_BBP_ID0_VALID FIELD32(0x00000080) -#define TXRX_CSR1_BBP_ID1 FIELD32(0x00007f00) -#define TXRX_CSR1_BBP_ID1_VALID FIELD32(0x00008000) -#define TXRX_CSR1_BBP_ID2 FIELD32(0x007f0000) -#define TXRX_CSR1_BBP_ID2_VALID FIELD32(0x00800000) -#define TXRX_CSR1_BBP_ID3 FIELD32(0x7f000000) -#define TXRX_CSR1_BBP_ID3_VALID FIELD32(0x80000000) - -/* - * TXRX_CSR2 - */ -#define TXRX_CSR2 0x3048 -#define TXRX_CSR2_BBP_ID0 FIELD32(0x0000007f) -#define TXRX_CSR2_BBP_ID0_VALID FIELD32(0x00000080) -#define TXRX_CSR2_BBP_ID1 FIELD32(0x00007f00) -#define TXRX_CSR2_BBP_ID1_VALID FIELD32(0x00008000) -#define TXRX_CSR2_BBP_ID2 FIELD32(0x007f0000) -#define TXRX_CSR2_BBP_ID2_VALID FIELD32(0x00800000) -#define TXRX_CSR2_BBP_ID3 FIELD32(0x7f000000) -#define TXRX_CSR2_BBP_ID3_VALID FIELD32(0x80000000) - -/* - * TXRX_CSR3 - */ -#define TXRX_CSR3 0x304c -#define TXRX_CSR3_BBP_ID0 FIELD32(0x0000007f) -#define TXRX_CSR3_BBP_ID0_VALID FIELD32(0x00000080) -#define TXRX_CSR3_BBP_ID1 FIELD32(0x00007f00) -#define TXRX_CSR3_BBP_ID1_VALID FIELD32(0x00008000) -#define TXRX_CSR3_BBP_ID2 FIELD32(0x007f0000) -#define TXRX_CSR3_BBP_ID2_VALID FIELD32(0x00800000) -#define TXRX_CSR3_BBP_ID3 FIELD32(0x7f000000) -#define TXRX_CSR3_BBP_ID3_VALID FIELD32(0x80000000) - -/* - * TXRX_CSR4: Auto-Responder/Tx-retry register. - * AUTORESPOND_PREAMBLE: 0:long, 1:short preamble. - * OFDM_TX_RATE_DOWN: 1:enable. - * OFDM_TX_RATE_STEP: 0:1-step, 1: 2-step, 2:3-step, 3:4-step. - * OFDM_TX_FALLBACK_CCK: 0: Fallback to OFDM 6M only, 1: Fallback to CCK 1M,2M. - */ -#define TXRX_CSR4 0x3050 -#define TXRX_CSR4_TX_ACK_TIMEOUT FIELD32(0x000000ff) -#define TXRX_CSR4_CNTL_ACK_POLICY FIELD32(0x00000700) -#define TXRX_CSR4_ACK_CTS_PSM FIELD32(0x00010000) -#define TXRX_CSR4_AUTORESPOND_ENABLE FIELD32(0x00020000) -#define TXRX_CSR4_AUTORESPOND_PREAMBLE FIELD32(0x00040000) -#define TXRX_CSR4_OFDM_TX_RATE_DOWN FIELD32(0x00080000) -#define TXRX_CSR4_OFDM_TX_RATE_STEP FIELD32(0x00300000) -#define TXRX_CSR4_OFDM_TX_FALLBACK_CCK FIELD32(0x00400000) -#define TXRX_CSR4_LONG_RETRY_LIMIT FIELD32(0x0f000000) -#define TXRX_CSR4_SHORT_RETRY_LIMIT FIELD32(0xf0000000) - -/* - * TXRX_CSR5 - */ -#define TXRX_CSR5 0x3054 - -/* - * TXRX_CSR6: ACK/CTS payload consumed time - */ -#define TXRX_CSR6 0x3058 - -/* - * TXRX_CSR7: OFDM ACK/CTS payload consumed time for 6/9/12/18 mbps. - */ -#define TXRX_CSR7 0x305c -#define TXRX_CSR7_ACK_CTS_6MBS FIELD32(0x000000ff) -#define TXRX_CSR7_ACK_CTS_9MBS FIELD32(0x0000ff00) -#define TXRX_CSR7_ACK_CTS_12MBS FIELD32(0x00ff0000) -#define TXRX_CSR7_ACK_CTS_18MBS FIELD32(0xff000000) - -/* - * TXRX_CSR8: OFDM ACK/CTS payload consumed time for 24/36/48/54 mbps. - */ -#define TXRX_CSR8 0x3060 -#define TXRX_CSR8_ACK_CTS_24MBS FIELD32(0x000000ff) -#define TXRX_CSR8_ACK_CTS_36MBS FIELD32(0x0000ff00) -#define TXRX_CSR8_ACK_CTS_48MBS FIELD32(0x00ff0000) -#define TXRX_CSR8_ACK_CTS_54MBS FIELD32(0xff000000) - -/* - * TXRX_CSR9: Synchronization control register. - * BEACON_INTERVAL: In unit of 1/16 TU. - * TSF_TICKING: Enable TSF auto counting. - * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. - * BEACON_GEN: Enable beacon generator. - */ -#define TXRX_CSR9 0x3064 -#define TXRX_CSR9_BEACON_INTERVAL FIELD32(0x0000ffff) -#define TXRX_CSR9_TSF_TICKING FIELD32(0x00010000) -#define TXRX_CSR9_TSF_SYNC FIELD32(0x00060000) -#define TXRX_CSR9_TBTT_ENABLE FIELD32(0x00080000) -#define TXRX_CSR9_BEACON_GEN FIELD32(0x00100000) -#define TXRX_CSR9_TIMESTAMP_COMPENSATE FIELD32(0xff000000) - -/* - * TXRX_CSR10: BEACON alignment. - */ -#define TXRX_CSR10 0x3068 - -/* - * TXRX_CSR11: AES mask. - */ -#define TXRX_CSR11 0x306c - -/* - * TXRX_CSR12: TSF low 32. - */ -#define TXRX_CSR12 0x3070 -#define TXRX_CSR12_LOW_TSFTIMER FIELD32(0xffffffff) - -/* - * TXRX_CSR13: TSF high 32. - */ -#define TXRX_CSR13 0x3074 -#define TXRX_CSR13_HIGH_TSFTIMER FIELD32(0xffffffff) - -/* - * TXRX_CSR14: TBTT timer. - */ -#define TXRX_CSR14 0x3078 - -/* - * TXRX_CSR15: TKIP MIC priority byte "AND" mask. - */ -#define TXRX_CSR15 0x307c - -/* - * PHY control registers. - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * PHY_CSR0: RF/PS control. - */ -#define PHY_CSR0 0x3080 -#define PHY_CSR0_PA_PE_BG FIELD32(0x00010000) -#define PHY_CSR0_PA_PE_A FIELD32(0x00020000) - -/* - * PHY_CSR1 - */ -#define PHY_CSR1 0x3084 - -/* - * PHY_CSR2: Pre-TX BBP control. - */ -#define PHY_CSR2 0x3088 - -/* - * PHY_CSR3: BBP serial control register. - * VALUE: Register value to program into BBP. - * REG_NUM: Selected BBP register. - * READ_CONTROL: 0: Write BBP, 1: Read BBP. - * BUSY: 1: ASIC is busy execute BBP programming. - */ -#define PHY_CSR3 0x308c -#define PHY_CSR3_VALUE FIELD32(0x000000ff) -#define PHY_CSR3_REGNUM FIELD32(0x00007f00) -#define PHY_CSR3_READ_CONTROL FIELD32(0x00008000) -#define PHY_CSR3_BUSY FIELD32(0x00010000) - -/* - * PHY_CSR4: RF serial control register - * VALUE: Register value (include register id) serial out to RF/IF chip. - * NUMBER_OF_BITS: Number of bits used in RFRegValue (I:20, RFMD:22). - * IF_SELECT: 1: select IF to program, 0: select RF to program. - * PLL_LD: RF PLL_LD status. - * BUSY: 1: ASIC is busy execute RF programming. - */ -#define PHY_CSR4 0x3090 -#define PHY_CSR4_VALUE FIELD32(0x00ffffff) -#define PHY_CSR4_NUMBER_OF_BITS FIELD32(0x1f000000) -#define PHY_CSR4_IF_SELECT FIELD32(0x20000000) -#define PHY_CSR4_PLL_LD FIELD32(0x40000000) -#define PHY_CSR4_BUSY FIELD32(0x80000000) - -/* - * PHY_CSR5: RX to TX signal switch timing control. - */ -#define PHY_CSR5 0x3094 -#define PHY_CSR5_IQ_FLIP FIELD32(0x00000004) - -/* - * PHY_CSR6: TX to RX signal timing control. - */ -#define PHY_CSR6 0x3098 -#define PHY_CSR6_IQ_FLIP FIELD32(0x00000004) - -/* - * PHY_CSR7: TX DAC switching timing control. - */ -#define PHY_CSR7 0x309c - -/* - * Security control register. - */ - -/* - * SEC_CSR0: Shared key table control. - */ -#define SEC_CSR0 0x30a0 -#define SEC_CSR0_BSS0_KEY0_VALID FIELD32(0x00000001) -#define SEC_CSR0_BSS0_KEY1_VALID FIELD32(0x00000002) -#define SEC_CSR0_BSS0_KEY2_VALID FIELD32(0x00000004) -#define SEC_CSR0_BSS0_KEY3_VALID FIELD32(0x00000008) -#define SEC_CSR0_BSS1_KEY0_VALID FIELD32(0x00000010) -#define SEC_CSR0_BSS1_KEY1_VALID FIELD32(0x00000020) -#define SEC_CSR0_BSS1_KEY2_VALID FIELD32(0x00000040) -#define SEC_CSR0_BSS1_KEY3_VALID FIELD32(0x00000080) -#define SEC_CSR0_BSS2_KEY0_VALID FIELD32(0x00000100) -#define SEC_CSR0_BSS2_KEY1_VALID FIELD32(0x00000200) -#define SEC_CSR0_BSS2_KEY2_VALID FIELD32(0x00000400) -#define SEC_CSR0_BSS2_KEY3_VALID FIELD32(0x00000800) -#define SEC_CSR0_BSS3_KEY0_VALID FIELD32(0x00001000) -#define SEC_CSR0_BSS3_KEY1_VALID FIELD32(0x00002000) -#define SEC_CSR0_BSS3_KEY2_VALID FIELD32(0x00004000) -#define SEC_CSR0_BSS3_KEY3_VALID FIELD32(0x00008000) - -/* - * SEC_CSR1: Shared key table security mode register. - */ -#define SEC_CSR1 0x30a4 -#define SEC_CSR1_BSS0_KEY0_CIPHER_ALG FIELD32(0x00000007) -#define SEC_CSR1_BSS0_KEY1_CIPHER_ALG FIELD32(0x00000070) -#define SEC_CSR1_BSS0_KEY2_CIPHER_ALG FIELD32(0x00000700) -#define SEC_CSR1_BSS0_KEY3_CIPHER_ALG FIELD32(0x00007000) -#define SEC_CSR1_BSS1_KEY0_CIPHER_ALG FIELD32(0x00070000) -#define SEC_CSR1_BSS1_KEY1_CIPHER_ALG FIELD32(0x00700000) -#define SEC_CSR1_BSS1_KEY2_CIPHER_ALG FIELD32(0x07000000) -#define SEC_CSR1_BSS1_KEY3_CIPHER_ALG FIELD32(0x70000000) - -/* - * Pairwise key table valid bitmap registers. - * SEC_CSR2: pairwise key table valid bitmap 0. - * SEC_CSR3: pairwise key table valid bitmap 1. - */ -#define SEC_CSR2 0x30a8 -#define SEC_CSR3 0x30ac - -/* - * SEC_CSR4: Pairwise key table lookup control. - */ -#define SEC_CSR4 0x30b0 -#define SEC_CSR4_ENABLE_BSS0 FIELD32(0x00000001) -#define SEC_CSR4_ENABLE_BSS1 FIELD32(0x00000002) -#define SEC_CSR4_ENABLE_BSS2 FIELD32(0x00000004) -#define SEC_CSR4_ENABLE_BSS3 FIELD32(0x00000008) - -/* - * SEC_CSR5: shared key table security mode register. - */ -#define SEC_CSR5 0x30b4 -#define SEC_CSR5_BSS2_KEY0_CIPHER_ALG FIELD32(0x00000007) -#define SEC_CSR5_BSS2_KEY1_CIPHER_ALG FIELD32(0x00000070) -#define SEC_CSR5_BSS2_KEY2_CIPHER_ALG FIELD32(0x00000700) -#define SEC_CSR5_BSS2_KEY3_CIPHER_ALG FIELD32(0x00007000) -#define SEC_CSR5_BSS3_KEY0_CIPHER_ALG FIELD32(0x00070000) -#define SEC_CSR5_BSS3_KEY1_CIPHER_ALG FIELD32(0x00700000) -#define SEC_CSR5_BSS3_KEY2_CIPHER_ALG FIELD32(0x07000000) -#define SEC_CSR5_BSS3_KEY3_CIPHER_ALG FIELD32(0x70000000) - -/* - * STA control registers. - */ - -/* - * STA_CSR0: RX PLCP error count & RX FCS error count. - */ -#define STA_CSR0 0x30c0 -#define STA_CSR0_FCS_ERROR FIELD32(0x0000ffff) -#define STA_CSR0_PLCP_ERROR FIELD32(0xffff0000) - -/* - * STA_CSR1: RX False CCA count & RX LONG frame count. - */ -#define STA_CSR1 0x30c4 -#define STA_CSR1_PHYSICAL_ERROR FIELD32(0x0000ffff) -#define STA_CSR1_FALSE_CCA_ERROR FIELD32(0xffff0000) - -/* - * STA_CSR2: TX Beacon count and RX FIFO overflow count. - */ -#define STA_CSR2 0x30c8 -#define STA_CSR2_RX_FIFO_OVERFLOW_COUNT FIELD32(0x0000ffff) -#define STA_CSR2_RX_OVERFLOW_COUNT FIELD32(0xffff0000) - -/* - * STA_CSR3: TX Beacon count. - */ -#define STA_CSR3 0x30cc -#define STA_CSR3_TX_BEACON_COUNT FIELD32(0x0000ffff) - -/* - * STA_CSR4: TX Result status register. - * VALID: 1:This register contains a valid TX result. - */ -#define STA_CSR4 0x30d0 -#define STA_CSR4_VALID FIELD32(0x00000001) -#define STA_CSR4_TX_RESULT FIELD32(0x0000000e) -#define STA_CSR4_RETRY_COUNT FIELD32(0x000000f0) -#define STA_CSR4_PID_SUBTYPE FIELD32(0x00001f00) -#define STA_CSR4_PID_TYPE FIELD32(0x0000e000) -#define STA_CSR4_TXRATE FIELD32(0x000f0000) - -/* - * QOS control registers. - */ - -/* - * QOS_CSR0: TXOP holder MAC address register. - */ -#define QOS_CSR0 0x30e0 -#define QOS_CSR0_BYTE0 FIELD32(0x000000ff) -#define QOS_CSR0_BYTE1 FIELD32(0x0000ff00) -#define QOS_CSR0_BYTE2 FIELD32(0x00ff0000) -#define QOS_CSR0_BYTE3 FIELD32(0xff000000) - -/* - * QOS_CSR1: TXOP holder MAC address register. - */ -#define QOS_CSR1 0x30e4 -#define QOS_CSR1_BYTE4 FIELD32(0x000000ff) -#define QOS_CSR1_BYTE5 FIELD32(0x0000ff00) - -/* - * QOS_CSR2: TXOP holder timeout register. - */ -#define QOS_CSR2 0x30e8 - -/* - * RX QOS-CFPOLL MAC address register. - * QOS_CSR3: RX QOS-CFPOLL MAC address 0. - * QOS_CSR4: RX QOS-CFPOLL MAC address 1. - */ -#define QOS_CSR3 0x30ec -#define QOS_CSR4 0x30f0 - -/* - * QOS_CSR5: "QosControl" field of the RX QOS-CFPOLL. - */ -#define QOS_CSR5 0x30f4 - -/* - * Host DMA registers. - */ - -/* - * AC0_BASE_CSR: AC_VO base address. - */ -#define AC0_BASE_CSR 0x3400 -#define AC0_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) - -/* - * AC1_BASE_CSR: AC_VI base address. - */ -#define AC1_BASE_CSR 0x3404 -#define AC1_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) - -/* - * AC2_BASE_CSR: AC_BE base address. - */ -#define AC2_BASE_CSR 0x3408 -#define AC2_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) - -/* - * AC3_BASE_CSR: AC_BK base address. - */ -#define AC3_BASE_CSR 0x340c -#define AC3_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) - -/* - * MGMT_BASE_CSR: MGMT ring base address. - */ -#define MGMT_BASE_CSR 0x3410 -#define MGMT_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) - -/* - * TX_RING_CSR0: TX Ring size for AC_VO, AC_VI, AC_BE, AC_BK. - */ -#define TX_RING_CSR0 0x3418 -#define TX_RING_CSR0_AC0_RING_SIZE FIELD32(0x000000ff) -#define TX_RING_CSR0_AC1_RING_SIZE FIELD32(0x0000ff00) -#define TX_RING_CSR0_AC2_RING_SIZE FIELD32(0x00ff0000) -#define TX_RING_CSR0_AC3_RING_SIZE FIELD32(0xff000000) - -/* - * TX_RING_CSR1: TX Ring size for MGMT Ring, HCCA Ring - * TXD_SIZE: In unit of 32-bit. - */ -#define TX_RING_CSR1 0x341c -#define TX_RING_CSR1_MGMT_RING_SIZE FIELD32(0x000000ff) -#define TX_RING_CSR1_HCCA_RING_SIZE FIELD32(0x0000ff00) -#define TX_RING_CSR1_TXD_SIZE FIELD32(0x003f0000) - -/* - * AIFSN_CSR: AIFSN for each EDCA AC. - * AIFSN0: For AC_VO. - * AIFSN1: For AC_VI. - * AIFSN2: For AC_BE. - * AIFSN3: For AC_BK. - */ -#define AIFSN_CSR 0x3420 -#define AIFSN_CSR_AIFSN0 FIELD32(0x0000000f) -#define AIFSN_CSR_AIFSN1 FIELD32(0x000000f0) -#define AIFSN_CSR_AIFSN2 FIELD32(0x00000f00) -#define AIFSN_CSR_AIFSN3 FIELD32(0x0000f000) - -/* - * CWMIN_CSR: CWmin for each EDCA AC. - * CWMIN0: For AC_VO. - * CWMIN1: For AC_VI. - * CWMIN2: For AC_BE. - * CWMIN3: For AC_BK. - */ -#define CWMIN_CSR 0x3424 -#define CWMIN_CSR_CWMIN0 FIELD32(0x0000000f) -#define CWMIN_CSR_CWMIN1 FIELD32(0x000000f0) -#define CWMIN_CSR_CWMIN2 FIELD32(0x00000f00) -#define CWMIN_CSR_CWMIN3 FIELD32(0x0000f000) - -/* - * CWMAX_CSR: CWmax for each EDCA AC. - * CWMAX0: For AC_VO. - * CWMAX1: For AC_VI. - * CWMAX2: For AC_BE. - * CWMAX3: For AC_BK. - */ -#define CWMAX_CSR 0x3428 -#define CWMAX_CSR_CWMAX0 FIELD32(0x0000000f) -#define CWMAX_CSR_CWMAX1 FIELD32(0x000000f0) -#define CWMAX_CSR_CWMAX2 FIELD32(0x00000f00) -#define CWMAX_CSR_CWMAX3 FIELD32(0x0000f000) - -/* - * TX_DMA_DST_CSR: TX DMA destination - * 0: TX ring0, 1: TX ring1, 2: TX ring2 3: invalid - */ -#define TX_DMA_DST_CSR 0x342c -#define TX_DMA_DST_CSR_DEST_AC0 FIELD32(0x00000003) -#define TX_DMA_DST_CSR_DEST_AC1 FIELD32(0x0000000c) -#define TX_DMA_DST_CSR_DEST_AC2 FIELD32(0x00000030) -#define TX_DMA_DST_CSR_DEST_AC3 FIELD32(0x000000c0) -#define TX_DMA_DST_CSR_DEST_MGMT FIELD32(0x00000300) - -/* - * TX_CNTL_CSR: KICK/Abort TX. - * KICK_TX_AC0: For AC_VO. - * KICK_TX_AC1: For AC_VI. - * KICK_TX_AC2: For AC_BE. - * KICK_TX_AC3: For AC_BK. - * ABORT_TX_AC0: For AC_VO. - * ABORT_TX_AC1: For AC_VI. - * ABORT_TX_AC2: For AC_BE. - * ABORT_TX_AC3: For AC_BK. - */ -#define TX_CNTL_CSR 0x3430 -#define TX_CNTL_CSR_KICK_TX_AC0 FIELD32(0x00000001) -#define TX_CNTL_CSR_KICK_TX_AC1 FIELD32(0x00000002) -#define TX_CNTL_CSR_KICK_TX_AC2 FIELD32(0x00000004) -#define TX_CNTL_CSR_KICK_TX_AC3 FIELD32(0x00000008) -#define TX_CNTL_CSR_KICK_TX_MGMT FIELD32(0x00000010) -#define TX_CNTL_CSR_ABORT_TX_AC0 FIELD32(0x00010000) -#define TX_CNTL_CSR_ABORT_TX_AC1 FIELD32(0x00020000) -#define TX_CNTL_CSR_ABORT_TX_AC2 FIELD32(0x00040000) -#define TX_CNTL_CSR_ABORT_TX_AC3 FIELD32(0x00080000) -#define TX_CNTL_CSR_ABORT_TX_MGMT FIELD32(0x00100000) - -/* - * LOAD_TX_RING_CSR: Load RX desriptor - */ -#define LOAD_TX_RING_CSR 0x3434 -#define LOAD_TX_RING_CSR_LOAD_TXD_AC0 FIELD32(0x00000001) -#define LOAD_TX_RING_CSR_LOAD_TXD_AC1 FIELD32(0x00000002) -#define LOAD_TX_RING_CSR_LOAD_TXD_AC2 FIELD32(0x00000004) -#define LOAD_TX_RING_CSR_LOAD_TXD_AC3 FIELD32(0x00000008) -#define LOAD_TX_RING_CSR_LOAD_TXD_MGMT FIELD32(0x00000010) - -/* - * Several read-only registers, for debugging. - */ -#define AC0_TXPTR_CSR 0x3438 -#define AC1_TXPTR_CSR 0x343c -#define AC2_TXPTR_CSR 0x3440 -#define AC3_TXPTR_CSR 0x3444 -#define MGMT_TXPTR_CSR 0x3448 - -/* - * RX_BASE_CSR - */ -#define RX_BASE_CSR 0x3450 -#define RX_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) - -/* - * RX_RING_CSR. - * RXD_SIZE: In unit of 32-bit. - */ -#define RX_RING_CSR 0x3454 -#define RX_RING_CSR_RING_SIZE FIELD32(0x000000ff) -#define RX_RING_CSR_RXD_SIZE FIELD32(0x00003f00) -#define RX_RING_CSR_RXD_WRITEBACK_SIZE FIELD32(0x00070000) - -/* - * RX_CNTL_CSR - */ -#define RX_CNTL_CSR 0x3458 -#define RX_CNTL_CSR_ENABLE_RX_DMA FIELD32(0x00000001) -#define RX_CNTL_CSR_LOAD_RXD FIELD32(0x00000002) - -/* - * RXPTR_CSR: Read-only, for debugging. - */ -#define RXPTR_CSR 0x345c - -/* - * PCI_CFG_CSR - */ -#define PCI_CFG_CSR 0x3460 - -/* - * BUF_FORMAT_CSR - */ -#define BUF_FORMAT_CSR 0x3464 - -/* - * INT_SOURCE_CSR: Interrupt source register. - * Write one to clear corresponding bit. - */ -#define INT_SOURCE_CSR 0x3468 -#define INT_SOURCE_CSR_TXDONE FIELD32(0x00000001) -#define INT_SOURCE_CSR_RXDONE FIELD32(0x00000002) -#define INT_SOURCE_CSR_BEACON_DONE FIELD32(0x00000004) -#define INT_SOURCE_CSR_TX_ABORT_DONE FIELD32(0x00000010) -#define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00010000) -#define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00020000) -#define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00040000) -#define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00080000) -#define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00100000) -#define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00200000) - -/* - * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF. - * MITIGATION_PERIOD: Interrupt mitigation in unit of 32 PCI clock. - */ -#define INT_MASK_CSR 0x346c -#define INT_MASK_CSR_TXDONE FIELD32(0x00000001) -#define INT_MASK_CSR_RXDONE FIELD32(0x00000002) -#define INT_MASK_CSR_BEACON_DONE FIELD32(0x00000004) -#define INT_MASK_CSR_TX_ABORT_DONE FIELD32(0x00000010) -#define INT_MASK_CSR_ENABLE_MITIGATION FIELD32(0x00000080) -#define INT_MASK_CSR_MITIGATION_PERIOD FIELD32(0x0000ff00) -#define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00010000) -#define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00020000) -#define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00040000) -#define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00080000) -#define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00100000) -#define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00200000) - -/* - * E2PROM_CSR: EEPROM control register. - * RELOAD: Write 1 to reload eeprom content. - * TYPE_93C46: 1: 93c46, 0:93c66. - * LOAD_STATUS: 1:loading, 0:done. - */ -#define E2PROM_CSR 0x3470 -#define E2PROM_CSR_RELOAD FIELD32(0x00000001) -#define E2PROM_CSR_DATA_CLOCK FIELD32(0x00000002) -#define E2PROM_CSR_CHIP_SELECT FIELD32(0x00000004) -#define E2PROM_CSR_DATA_IN FIELD32(0x00000008) -#define E2PROM_CSR_DATA_OUT FIELD32(0x00000010) -#define E2PROM_CSR_TYPE_93C46 FIELD32(0x00000020) -#define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040) - -/* - * AC_TXOP_CSR0: AC_VO/AC_VI TXOP register. - * AC0_TX_OP: For AC_VO, in unit of 32us. - * AC1_TX_OP: For AC_VI, in unit of 32us. - */ -#define AC_TXOP_CSR0 0x3474 -#define AC_TXOP_CSR0_AC0_TX_OP FIELD32(0x0000ffff) -#define AC_TXOP_CSR0_AC1_TX_OP FIELD32(0xffff0000) - -/* - * AC_TXOP_CSR1: AC_BE/AC_BK TXOP register. - * AC2_TX_OP: For AC_BE, in unit of 32us. - * AC3_TX_OP: For AC_BK, in unit of 32us. - */ -#define AC_TXOP_CSR1 0x3478 -#define AC_TXOP_CSR1_AC2_TX_OP FIELD32(0x0000ffff) -#define AC_TXOP_CSR1_AC3_TX_OP FIELD32(0xffff0000) - -/* - * DMA_STATUS_CSR - */ -#define DMA_STATUS_CSR 0x3480 - -/* - * TEST_MODE_CSR - */ -#define TEST_MODE_CSR 0x3484 - -/* - * UART0_TX_CSR - */ -#define UART0_TX_CSR 0x3488 - -/* - * UART0_RX_CSR - */ -#define UART0_RX_CSR 0x348c - -/* - * UART0_FRAME_CSR - */ -#define UART0_FRAME_CSR 0x3490 - -/* - * UART0_BUFFER_CSR - */ -#define UART0_BUFFER_CSR 0x3494 - -/* - * IO_CNTL_CSR - * RF_PS: Set RF interface value to power save - */ -#define IO_CNTL_CSR 0x3498 -#define IO_CNTL_CSR_RF_PS FIELD32(0x00000004) - -/* - * UART_INT_SOURCE_CSR - */ -#define UART_INT_SOURCE_CSR 0x34a8 - -/* - * UART_INT_MASK_CSR - */ -#define UART_INT_MASK_CSR 0x34ac - -/* - * PBF_QUEUE_CSR - */ -#define PBF_QUEUE_CSR 0x34b0 - -/* - * Firmware DMA registers. - * Firmware DMA registers are dedicated for MCU usage - * and should not be touched by host driver. - * Therefore we skip the definition of these registers. - */ -#define FW_TX_BASE_CSR 0x34c0 -#define FW_TX_START_CSR 0x34c4 -#define FW_TX_LAST_CSR 0x34c8 -#define FW_MODE_CNTL_CSR 0x34cc -#define FW_TXPTR_CSR 0x34d0 - -/* - * 8051 firmware image. - */ -#define FIRMWARE_RT2561 "rt2561.bin" -#define FIRMWARE_RT2561s "rt2561s.bin" -#define FIRMWARE_RT2661 "rt2661.bin" -#define FIRMWARE_IMAGE_BASE 0x4000 - -/* - * BBP registers. - * The wordsize of the BBP is 8 bits. - */ - -/* - * R2 - */ -#define BBP_R2_BG_MODE FIELD8(0x20) - -/* - * R3 - */ -#define BBP_R3_SMART_MODE FIELD8(0x01) - -/* - * R4: RX antenna control - * FRAME_END: 1 - DPDT, 0 - SPDT (Only valid for 802.11G, RF2527 & RF2529) - */ - -/* - * ANTENNA_CONTROL semantics (guessed): - * 0x1: Software controlled antenna switching (fixed or SW diversity) - * 0x2: Hardware diversity. - */ -#define BBP_R4_RX_ANTENNA_CONTROL FIELD8(0x03) -#define BBP_R4_RX_FRAME_END FIELD8(0x20) - -/* - * R77 - */ -#define BBP_R77_RX_ANTENNA FIELD8(0x03) - -/* - * RF registers - */ - -/* - * RF 3 - */ -#define RF3_TXPOWER FIELD32(0x00003e00) - -/* - * RF 4 - */ -#define RF4_FREQ_OFFSET FIELD32(0x0003f000) - -/* - * EEPROM content. - * The wordsize of the EEPROM is 16 bits. - */ - -/* - * HW MAC address. - */ -#define EEPROM_MAC_ADDR_0 0x0002 -#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) -#define EEPROM_MAC_ADDR1 0x0003 -#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_2 0x0004 -#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) - -/* - * EEPROM antenna. - * ANTENNA_NUM: Number of antenna's. - * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. - * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. - * FRAME_TYPE: 0: DPDT , 1: SPDT , noted this bit is valid for g only. - * DYN_TXAGC: Dynamic TX AGC control. - * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. - * RF_TYPE: Rf_type of this adapter. - */ -#define EEPROM_ANTENNA 0x0010 -#define EEPROM_ANTENNA_NUM FIELD16(0x0003) -#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) -#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) -#define EEPROM_ANTENNA_FRAME_TYPE FIELD16(0x0040) -#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) -#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) -#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) - -/* - * EEPROM NIC config. - * ENABLE_DIVERSITY: 1:enable, 0:disable. - * EXTERNAL_LNA_BG: External LNA enable for 2.4G. - * CARDBUS_ACCEL: 0:enable, 1:disable. - * EXTERNAL_LNA_A: External LNA enable for 5G. - */ -#define EEPROM_NIC 0x0011 -#define EEPROM_NIC_ENABLE_DIVERSITY FIELD16(0x0001) -#define EEPROM_NIC_TX_DIVERSITY FIELD16(0x0002) -#define EEPROM_NIC_RX_FIXED FIELD16(0x0004) -#define EEPROM_NIC_TX_FIXED FIELD16(0x0008) -#define EEPROM_NIC_EXTERNAL_LNA_BG FIELD16(0x0010) -#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0020) -#define EEPROM_NIC_EXTERNAL_LNA_A FIELD16(0x0040) - -/* - * EEPROM geography. - * GEO_A: Default geographical setting for 5GHz band - * GEO: Default geographical setting. - */ -#define EEPROM_GEOGRAPHY 0x0012 -#define EEPROM_GEOGRAPHY_GEO_A FIELD16(0x00ff) -#define EEPROM_GEOGRAPHY_GEO FIELD16(0xff00) - -/* - * EEPROM BBP. - */ -#define EEPROM_BBP_START 0x0013 -#define EEPROM_BBP_SIZE 16 -#define EEPROM_BBP_VALUE FIELD16(0x00ff) -#define EEPROM_BBP_REG_ID FIELD16(0xff00) - -/* - * EEPROM TXPOWER 802.11G - */ -#define EEPROM_TXPOWER_G_START 0x0023 -#define EEPROM_TXPOWER_G_SIZE 7 -#define EEPROM_TXPOWER_G_1 FIELD16(0x00ff) -#define EEPROM_TXPOWER_G_2 FIELD16(0xff00) - -/* - * EEPROM Frequency - */ -#define EEPROM_FREQ 0x002f -#define EEPROM_FREQ_OFFSET FIELD16(0x00ff) -#define EEPROM_FREQ_SEQ_MASK FIELD16(0xff00) -#define EEPROM_FREQ_SEQ FIELD16(0x0300) - -/* - * EEPROM LED. - * POLARITY_RDY_G: Polarity RDY_G setting. - * POLARITY_RDY_A: Polarity RDY_A setting. - * POLARITY_ACT: Polarity ACT setting. - * POLARITY_GPIO_0: Polarity GPIO0 setting. - * POLARITY_GPIO_1: Polarity GPIO1 setting. - * POLARITY_GPIO_2: Polarity GPIO2 setting. - * POLARITY_GPIO_3: Polarity GPIO3 setting. - * POLARITY_GPIO_4: Polarity GPIO4 setting. - * LED_MODE: Led mode. - */ -#define EEPROM_LED 0x0030 -#define EEPROM_LED_POLARITY_RDY_G FIELD16(0x0001) -#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) -#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) -#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) -#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) -#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) -#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) -#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) -#define EEPROM_LED_LED_MODE FIELD16(0x1f00) - -/* - * EEPROM TXPOWER 802.11A - */ -#define EEPROM_TXPOWER_A_START 0x0031 -#define EEPROM_TXPOWER_A_SIZE 12 -#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) -#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) - -/* - * EEPROM RSSI offset 802.11BG - */ -#define EEPROM_RSSI_OFFSET_BG 0x004d -#define EEPROM_RSSI_OFFSET_BG_1 FIELD16(0x00ff) -#define EEPROM_RSSI_OFFSET_BG_2 FIELD16(0xff00) - -/* - * EEPROM RSSI offset 802.11A - */ -#define EEPROM_RSSI_OFFSET_A 0x004e -#define EEPROM_RSSI_OFFSET_A_1 FIELD16(0x00ff) -#define EEPROM_RSSI_OFFSET_A_2 FIELD16(0xff00) - -/* - * MCU mailbox commands. - */ -#define MCU_SLEEP 0x30 -#define MCU_WAKEUP 0x31 -#define MCU_LED 0x50 -#define MCU_LED_STRENGTH 0x52 - -/* - * DMA descriptor defines. - */ -#define TXD_DESC_SIZE ( 16 * sizeof(__le32) ) -#define TXINFO_SIZE ( 6 * sizeof(__le32) ) -#define RXD_DESC_SIZE ( 16 * sizeof(__le32) ) - -/* - * TX descriptor format for TX, PRIO and Beacon Ring. - */ - -/* - * Word0 - * TKIP_MIC: ASIC appends TKIP MIC if TKIP is used. - * KEY_TABLE: Use per-client pairwise KEY table. - * KEY_INDEX: - * Key index (0~31) to the pairwise KEY table. - * 0~3 to shared KEY table 0 (BSS0). - * 4~7 to shared KEY table 1 (BSS1). - * 8~11 to shared KEY table 2 (BSS2). - * 12~15 to shared KEY table 3 (BSS3). - * BURST: Next frame belongs to same "burst" event. - */ -#define TXD_W0_OWNER_NIC FIELD32(0x00000001) -#define TXD_W0_VALID FIELD32(0x00000002) -#define TXD_W0_MORE_FRAG FIELD32(0x00000004) -#define TXD_W0_ACK FIELD32(0x00000008) -#define TXD_W0_TIMESTAMP FIELD32(0x00000010) -#define TXD_W0_OFDM FIELD32(0x00000020) -#define TXD_W0_IFS FIELD32(0x00000040) -#define TXD_W0_RETRY_MODE FIELD32(0x00000080) -#define TXD_W0_TKIP_MIC FIELD32(0x00000100) -#define TXD_W0_KEY_TABLE FIELD32(0x00000200) -#define TXD_W0_KEY_INDEX FIELD32(0x0000fc00) -#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) -#define TXD_W0_BURST FIELD32(0x10000000) -#define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) - -/* - * Word1 - * HOST_Q_ID: EDCA/HCCA queue ID. - * HW_SEQUENCE: MAC overwrites the frame sequence number. - * BUFFER_COUNT: Number of buffers in this TXD. - */ -#define TXD_W1_HOST_Q_ID FIELD32(0x0000000f) -#define TXD_W1_AIFSN FIELD32(0x000000f0) -#define TXD_W1_CWMIN FIELD32(0x00000f00) -#define TXD_W1_CWMAX FIELD32(0x0000f000) -#define TXD_W1_IV_OFFSET FIELD32(0x003f0000) -#define TXD_W1_PIGGY_BACK FIELD32(0x01000000) -#define TXD_W1_HW_SEQUENCE FIELD32(0x10000000) -#define TXD_W1_BUFFER_COUNT FIELD32(0xe0000000) - -/* - * Word2: PLCP information - */ -#define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) -#define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) -#define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) -#define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) - -/* - * Word3 - */ -#define TXD_W3_IV FIELD32(0xffffffff) - -/* - * Word4 - */ -#define TXD_W4_EIV FIELD32(0xffffffff) - -/* - * Word5 - * FRAME_OFFSET: Frame start offset inside ASIC TXFIFO (after TXINFO field). - * TXD_W5_PID_SUBTYPE: Driver assigned packet ID index for txdone handler. - * TXD_W5_PID_TYPE: Driver assigned packet ID type for txdone handler. - * WAITING_DMA_DONE_INT: TXD been filled with data - * and waiting for TxDoneISR housekeeping. - */ -#define TXD_W5_FRAME_OFFSET FIELD32(0x000000ff) -#define TXD_W5_PID_SUBTYPE FIELD32(0x00001f00) -#define TXD_W5_PID_TYPE FIELD32(0x0000e000) -#define TXD_W5_TX_POWER FIELD32(0x00ff0000) -#define TXD_W5_WAITING_DMA_DONE_INT FIELD32(0x01000000) - -/* - * the above 24-byte is called TXINFO and will be DMAed to MAC block - * through TXFIFO. MAC block use this TXINFO to control the transmission - * behavior of this frame. - * The following fields are not used by MAC block. - * They are used by DMA block and HOST driver only. - * Once a frame has been DMA to ASIC, all the following fields are useless - * to ASIC. - */ - -/* - * Word6-10: Buffer physical address - */ -#define TXD_W6_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) -#define TXD_W7_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) -#define TXD_W8_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) -#define TXD_W9_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) -#define TXD_W10_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) - -/* - * Word11-13: Buffer length - */ -#define TXD_W11_BUFFER_LENGTH0 FIELD32(0x00000fff) -#define TXD_W11_BUFFER_LENGTH1 FIELD32(0x0fff0000) -#define TXD_W12_BUFFER_LENGTH2 FIELD32(0x00000fff) -#define TXD_W12_BUFFER_LENGTH3 FIELD32(0x0fff0000) -#define TXD_W13_BUFFER_LENGTH4 FIELD32(0x00000fff) - -/* - * Word14 - */ -#define TXD_W14_SK_BUFFER FIELD32(0xffffffff) - -/* - * Word15 - */ -#define TXD_W15_NEXT_SK_BUFFER FIELD32(0xffffffff) - -/* - * RX descriptor format for RX Ring. - */ - -/* - * Word0 - * CIPHER_ERROR: 1:ICV error, 2:MIC error, 3:invalid key. - * KEY_INDEX: Decryption key actually used. - */ -#define RXD_W0_OWNER_NIC FIELD32(0x00000001) -#define RXD_W0_DROP FIELD32(0x00000002) -#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000004) -#define RXD_W0_MULTICAST FIELD32(0x00000008) -#define RXD_W0_BROADCAST FIELD32(0x00000010) -#define RXD_W0_MY_BSS FIELD32(0x00000020) -#define RXD_W0_CRC_ERROR FIELD32(0x00000040) -#define RXD_W0_OFDM FIELD32(0x00000080) -#define RXD_W0_CIPHER_ERROR FIELD32(0x00000300) -#define RXD_W0_KEY_INDEX FIELD32(0x0000fc00) -#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) -#define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) - -/* - * Word1 - * SIGNAL: RX raw data rate reported by BBP. - */ -#define RXD_W1_SIGNAL FIELD32(0x000000ff) -#define RXD_W1_RSSI_AGC FIELD32(0x00001f00) -#define RXD_W1_RSSI_LNA FIELD32(0x00006000) -#define RXD_W1_FRAME_OFFSET FIELD32(0x7f000000) - -/* - * Word2 - * IV: Received IV of originally encrypted. - */ -#define RXD_W2_IV FIELD32(0xffffffff) - -/* - * Word3 - * EIV: Received EIV of originally encrypted. - */ -#define RXD_W3_EIV FIELD32(0xffffffff) - -/* - * Word4 - * ICV: Received ICV of originally encrypted. - * NOTE: This is a guess, the official definition is "reserved" - */ -#define RXD_W4_ICV FIELD32(0xffffffff) - -/* - * the above 20-byte is called RXINFO and will be DMAed to MAC RX block - * and passed to the HOST driver. - * The following fields are for DMA block and HOST usage only. - * Can't be touched by ASIC MAC block. - */ - -/* - * Word5 - */ -#define RXD_W5_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) - -/* - * Word6-15: Reserved - */ -#define RXD_W6_RESERVED FIELD32(0xffffffff) -#define RXD_W7_RESERVED FIELD32(0xffffffff) -#define RXD_W8_RESERVED FIELD32(0xffffffff) -#define RXD_W9_RESERVED FIELD32(0xffffffff) -#define RXD_W10_RESERVED FIELD32(0xffffffff) -#define RXD_W11_RESERVED FIELD32(0xffffffff) -#define RXD_W12_RESERVED FIELD32(0xffffffff) -#define RXD_W13_RESERVED FIELD32(0xffffffff) -#define RXD_W14_RESERVED FIELD32(0xffffffff) -#define RXD_W15_RESERVED FIELD32(0xffffffff) - -/* - * Macros for converting txpower from EEPROM to mac80211 value - * and from mac80211 value to register value. - */ -#define MIN_TXPOWER 0 -#define MAX_TXPOWER 31 -#define DEFAULT_TXPOWER 24 - -#define TXPOWER_FROM_DEV(__txpower) \ - (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) - -#define TXPOWER_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) - -#endif /* RT61PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c deleted file mode 100644 index 7081e13b4fd6..000000000000 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ /dev/null @@ -1,2548 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt73usb - Abstract: rt73usb device specific routines. - Supported chipsets: rt2571W & rt2671. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "rt2x00.h" -#include "rt2x00usb.h" -#include "rt73usb.h" - -/* - * Allow hardware encryption to be disabled. - */ -static bool modparam_nohwcrypt; -module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); -MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); - -/* - * Register access. - * All access to the CSR registers will go through the methods - * rt2x00usb_register_read and rt2x00usb_register_write. - * BBP and RF register require indirect register access, - * and use the CSR registers BBPCSR and RFCSR to achieve this. - * These indirect registers work with busy bits, - * and we will try maximal REGISTER_BUSY_COUNT times to access - * the register while taking a REGISTER_BUSY_DELAY us delay - * between each attampt. When the busy bit is still set at that time, - * the access attempt is considered to have failed, - * and we will print an error. - * The _lock versions must be used if you already hold the csr_mutex - */ -#define WAIT_FOR_BBP(__dev, __reg) \ - rt2x00usb_regbusy_read((__dev), PHY_CSR3, PHY_CSR3_BUSY, (__reg)) -#define WAIT_FOR_RF(__dev, __reg) \ - rt2x00usb_regbusy_read((__dev), PHY_CSR4, PHY_CSR4_BUSY, (__reg)) - -static void rt73usb_bbp_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u8 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, PHY_CSR3_VALUE, value); - rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); - rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); - rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); - - rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u8 *value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the BBP becomes available, afterwards we - * can safely write the read request into the register. - * After the data has been written, we wait until hardware - * returns the correct value, if at any time the register - * doesn't become available in time, reg will be 0xffffffff - * which means we return 0xff to the caller. - */ - if (WAIT_FOR_BBP(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); - rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); - rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); - - rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); - - WAIT_FOR_BBP(rt2x00dev, ®); - } - - *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -static void rt73usb_rf_write(struct rt2x00_dev *rt2x00dev, - const unsigned int word, const u32 value) -{ - u32 reg; - - mutex_lock(&rt2x00dev->csr_mutex); - - /* - * Wait until the RF becomes available, afterwards we - * can safely write the new data into the register. - */ - if (WAIT_FOR_RF(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, PHY_CSR4_VALUE, value); - /* - * RF5225 and RF2527 contain 21 bits per RF register value, - * all others contain 20 bits. - */ - rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, - 20 + (rt2x00_rf(rt2x00dev, RF5225) || - rt2x00_rf(rt2x00dev, RF2527))); - rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); - rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); - - rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR4, reg); - rt2x00_rf_write(rt2x00dev, word, value); - } - - mutex_unlock(&rt2x00dev->csr_mutex); -} - -#ifdef CONFIG_RT2X00_LIB_DEBUGFS -static const struct rt2x00debug rt73usb_rt2x00debug = { - .owner = THIS_MODULE, - .csr = { - .read = rt2x00usb_register_read, - .write = rt2x00usb_register_write, - .flags = RT2X00DEBUGFS_OFFSET, - .word_base = CSR_REG_BASE, - .word_size = sizeof(u32), - .word_count = CSR_REG_SIZE / sizeof(u32), - }, - .eeprom = { - .read = rt2x00_eeprom_read, - .write = rt2x00_eeprom_write, - .word_base = EEPROM_BASE, - .word_size = sizeof(u16), - .word_count = EEPROM_SIZE / sizeof(u16), - }, - .bbp = { - .read = rt73usb_bbp_read, - .write = rt73usb_bbp_write, - .word_base = BBP_BASE, - .word_size = sizeof(u8), - .word_count = BBP_SIZE / sizeof(u8), - }, - .rf = { - .read = rt2x00_rf_read, - .write = rt73usb_rf_write, - .word_base = RF_BASE, - .word_size = sizeof(u32), - .word_count = RF_SIZE / sizeof(u32), - }, -}; -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ - -static int rt73usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00usb_register_read(rt2x00dev, MAC_CSR13, ®); - return rt2x00_get_field32(reg, MAC_CSR13_VAL7); -} - -#ifdef CONFIG_RT2X00_LIB_LEDS -static void rt73usb_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - unsigned int enabled = brightness != LED_OFF; - unsigned int a_mode = - (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); - unsigned int bg_mode = - (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); - - if (led->type == LED_TYPE_RADIO) { - rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, - MCU_LEDCS_RADIO_STATUS, enabled); - - rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, - 0, led->rt2x00dev->led_mcu_reg, - REGISTER_TIMEOUT); - } else if (led->type == LED_TYPE_ASSOC) { - rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, - MCU_LEDCS_LINK_BG_STATUS, bg_mode); - rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, - MCU_LEDCS_LINK_A_STATUS, a_mode); - - rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, - 0, led->rt2x00dev->led_mcu_reg, - REGISTER_TIMEOUT); - } else if (led->type == LED_TYPE_QUALITY) { - /* - * The brightness is divided into 6 levels (0 - 5), - * this means we need to convert the brightness - * argument into the matching level within that range. - */ - rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, - brightness / (LED_FULL / 6), - led->rt2x00dev->led_mcu_reg, - REGISTER_TIMEOUT); - } -} - -static int rt73usb_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) -{ - struct rt2x00_led *led = - container_of(led_cdev, struct rt2x00_led, led_dev); - u32 reg; - - rt2x00usb_register_read(led->rt2x00dev, MAC_CSR14, ®); - rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, *delay_on); - rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, *delay_off); - rt2x00usb_register_write(led->rt2x00dev, MAC_CSR14, reg); - - return 0; -} - -static void rt73usb_init_led(struct rt2x00_dev *rt2x00dev, - struct rt2x00_led *led, - enum led_type type) -{ - led->rt2x00dev = rt2x00dev; - led->type = type; - led->led_dev.brightness_set = rt73usb_brightness_set; - led->led_dev.blink_set = rt73usb_blink_set; - led->flags = LED_INITIALIZED; -} -#endif /* CONFIG_RT2X00_LIB_LEDS */ - -/* - * Configuration handlers. - */ -static int rt73usb_config_shared_key(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key) -{ - struct hw_key_entry key_entry; - struct rt2x00_field32 field; - u32 mask; - u32 reg; - - if (crypto->cmd == SET_KEY) { - /* - * rt2x00lib can't determine the correct free - * key_idx for shared keys. We have 1 register - * with key valid bits. The goal is simple, read - * the register, if that is full we have no slots - * left. - * Note that each BSS is allowed to have up to 4 - * shared keys, so put a mask over the allowed - * entries. - */ - mask = (0xf << crypto->bssidx); - - rt2x00usb_register_read(rt2x00dev, SEC_CSR0, ®); - reg &= mask; - - if (reg && reg == mask) - return -ENOSPC; - - key->hw_key_idx += reg ? ffz(reg) : 0; - - /* - * Upload key to hardware - */ - memcpy(key_entry.key, crypto->key, - sizeof(key_entry.key)); - memcpy(key_entry.tx_mic, crypto->tx_mic, - sizeof(key_entry.tx_mic)); - memcpy(key_entry.rx_mic, crypto->rx_mic, - sizeof(key_entry.rx_mic)); - - reg = SHARED_KEY_ENTRY(key->hw_key_idx); - rt2x00usb_register_multiwrite(rt2x00dev, reg, - &key_entry, sizeof(key_entry)); - - /* - * The cipher types are stored over 2 registers. - * bssidx 0 and 1 keys are stored in SEC_CSR1 and - * bssidx 1 and 2 keys are stored in SEC_CSR5. - * Using the correct defines correctly will cause overhead, - * so just calculate the correct offset. - */ - if (key->hw_key_idx < 8) { - field.bit_offset = (3 * key->hw_key_idx); - field.bit_mask = 0x7 << field.bit_offset; - - rt2x00usb_register_read(rt2x00dev, SEC_CSR1, ®); - rt2x00_set_field32(®, field, crypto->cipher); - rt2x00usb_register_write(rt2x00dev, SEC_CSR1, reg); - } else { - field.bit_offset = (3 * (key->hw_key_idx - 8)); - field.bit_mask = 0x7 << field.bit_offset; - - rt2x00usb_register_read(rt2x00dev, SEC_CSR5, ®); - rt2x00_set_field32(®, field, crypto->cipher); - rt2x00usb_register_write(rt2x00dev, SEC_CSR5, reg); - } - - /* - * The driver does not support the IV/EIV generation - * in hardware. However it doesn't support the IV/EIV - * inside the ieee80211 frame either, but requires it - * to be provided separately for the descriptor. - * rt2x00lib will cut the IV/EIV data out of all frames - * given to us by mac80211, but we must tell mac80211 - * to generate the IV/EIV data. - */ - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - } - - /* - * SEC_CSR0 contains only single-bit fields to indicate - * a particular key is valid. Because using the FIELD32() - * defines directly will cause a lot of overhead we use - * a calculation to determine the correct bit directly. - */ - mask = 1 << key->hw_key_idx; - - rt2x00usb_register_read(rt2x00dev, SEC_CSR0, ®); - if (crypto->cmd == SET_KEY) - reg |= mask; - else if (crypto->cmd == DISABLE_KEY) - reg &= ~mask; - rt2x00usb_register_write(rt2x00dev, SEC_CSR0, reg); - - return 0; -} - -static int rt73usb_config_pairwise_key(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_crypto *crypto, - struct ieee80211_key_conf *key) -{ - struct hw_pairwise_ta_entry addr_entry; - struct hw_key_entry key_entry; - u32 mask; - u32 reg; - - if (crypto->cmd == SET_KEY) { - /* - * rt2x00lib can't determine the correct free - * key_idx for pairwise keys. We have 2 registers - * with key valid bits. The goal is simple, read - * the first register, if that is full move to - * the next register. - * When both registers are full, we drop the key, - * otherwise we use the first invalid entry. - */ - rt2x00usb_register_read(rt2x00dev, SEC_CSR2, ®); - if (reg && reg == ~0) { - key->hw_key_idx = 32; - rt2x00usb_register_read(rt2x00dev, SEC_CSR3, ®); - if (reg && reg == ~0) - return -ENOSPC; - } - - key->hw_key_idx += reg ? ffz(reg) : 0; - - /* - * Upload key to hardware - */ - memcpy(key_entry.key, crypto->key, - sizeof(key_entry.key)); - memcpy(key_entry.tx_mic, crypto->tx_mic, - sizeof(key_entry.tx_mic)); - memcpy(key_entry.rx_mic, crypto->rx_mic, - sizeof(key_entry.rx_mic)); - - reg = PAIRWISE_KEY_ENTRY(key->hw_key_idx); - rt2x00usb_register_multiwrite(rt2x00dev, reg, - &key_entry, sizeof(key_entry)); - - /* - * Send the address and cipher type to the hardware register. - */ - memset(&addr_entry, 0, sizeof(addr_entry)); - memcpy(&addr_entry, crypto->address, ETH_ALEN); - addr_entry.cipher = crypto->cipher; - - reg = PAIRWISE_TA_ENTRY(key->hw_key_idx); - rt2x00usb_register_multiwrite(rt2x00dev, reg, - &addr_entry, sizeof(addr_entry)); - - /* - * Enable pairwise lookup table for given BSS idx, - * without this received frames will not be decrypted - * by the hardware. - */ - rt2x00usb_register_read(rt2x00dev, SEC_CSR4, ®); - reg |= (1 << crypto->bssidx); - rt2x00usb_register_write(rt2x00dev, SEC_CSR4, reg); - - /* - * The driver does not support the IV/EIV generation - * in hardware. However it doesn't support the IV/EIV - * inside the ieee80211 frame either, but requires it - * to be provided separately for the descriptor. - * rt2x00lib will cut the IV/EIV data out of all frames - * given to us by mac80211, but we must tell mac80211 - * to generate the IV/EIV data. - */ - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - } - - /* - * SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate - * a particular key is valid. Because using the FIELD32() - * defines directly will cause a lot of overhead we use - * a calculation to determine the correct bit directly. - */ - if (key->hw_key_idx < 32) { - mask = 1 << key->hw_key_idx; - - rt2x00usb_register_read(rt2x00dev, SEC_CSR2, ®); - if (crypto->cmd == SET_KEY) - reg |= mask; - else if (crypto->cmd == DISABLE_KEY) - reg &= ~mask; - rt2x00usb_register_write(rt2x00dev, SEC_CSR2, reg); - } else { - mask = 1 << (key->hw_key_idx - 32); - - rt2x00usb_register_read(rt2x00dev, SEC_CSR3, ®); - if (crypto->cmd == SET_KEY) - reg |= mask; - else if (crypto->cmd == DISABLE_KEY) - reg &= ~mask; - rt2x00usb_register_write(rt2x00dev, SEC_CSR3, reg); - } - - return 0; -} - -static void rt73usb_config_filter(struct rt2x00_dev *rt2x00dev, - const unsigned int filter_flags) -{ - u32 reg; - - /* - * Start configuration steps. - * Note that the version error will always be dropped - * and broadcast frames will always be accepted since - * there is no filter for it at this time. - */ - rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, - !(filter_flags & FIF_FCSFAIL)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, - !(filter_flags & FIF_PLCPFAIL)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, - !(filter_flags & (FIF_CONTROL | FIF_PSPOLL))); - rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, 1); - rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, - !rt2x00dev->intf_ap_count); - rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); - rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, - !(filter_flags & FIF_ALLMULTI)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_BROADCAST, 0); - rt2x00_set_field32(®, TXRX_CSR0_DROP_ACK_CTS, - !(filter_flags & FIF_CONTROL)); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); -} - -static void rt73usb_config_intf(struct rt2x00_dev *rt2x00dev, - struct rt2x00_intf *intf, - struct rt2x00intf_conf *conf, - const unsigned int flags) -{ - u32 reg; - - if (flags & CONFIG_UPDATE_TYPE) { - /* - * Enable synchronisation. - */ - rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, conf->sync); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); - } - - if (flags & CONFIG_UPDATE_MAC) { - reg = le32_to_cpu(conf->mac[1]); - rt2x00_set_field32(®, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); - conf->mac[1] = cpu_to_le32(reg); - - rt2x00usb_register_multiwrite(rt2x00dev, MAC_CSR2, - conf->mac, sizeof(conf->mac)); - } - - if (flags & CONFIG_UPDATE_BSSID) { - reg = le32_to_cpu(conf->bssid[1]); - rt2x00_set_field32(®, MAC_CSR5_BSS_ID_MASK, 3); - conf->bssid[1] = cpu_to_le32(reg); - - rt2x00usb_register_multiwrite(rt2x00dev, MAC_CSR4, - conf->bssid, sizeof(conf->bssid)); - } -} - -static void rt73usb_config_erp(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_erp *erp, - u32 changed) -{ - u32 reg; - - rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, 0x32); - rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); - - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, ®); - rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); - rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, - !!erp->short_preamble); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg); - } - - if (changed & BSS_CHANGED_BASIC_RATES) - rt2x00usb_register_write(rt2x00dev, TXRX_CSR5, - erp->basic_rates); - - if (changed & BSS_CHANGED_BEACON_INT) { - rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, - erp->beacon_int * 16); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); - } - - if (changed & BSS_CHANGED_ERP_SLOT) { - rt2x00usb_register_read(rt2x00dev, MAC_CSR9, ®); - rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, erp->slot_time); - rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg); - - rt2x00usb_register_read(rt2x00dev, MAC_CSR8, ®); - rt2x00_set_field32(®, MAC_CSR8_SIFS, erp->sifs); - rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); - rt2x00_set_field32(®, MAC_CSR8_EIFS, erp->eifs); - rt2x00usb_register_write(rt2x00dev, MAC_CSR8, reg); - } -} - -static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) -{ - u8 r3; - u8 r4; - u8 r77; - u8 temp; - - rt73usb_bbp_read(rt2x00dev, 3, &r3); - rt73usb_bbp_read(rt2x00dev, 4, &r4); - rt73usb_bbp_read(rt2x00dev, 77, &r77); - - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0); - - /* - * Configure the RX antenna. - */ - switch (ant->rx) { - case ANTENNA_HW_DIVERSITY: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); - temp = !rt2x00_has_cap_frame_type(rt2x00dev) && - (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ); - rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, temp); - break; - case ANTENNA_A: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); - rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); - if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); - else - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); - break; - case ANTENNA_B: - default: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); - rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); - if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); - else - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); - break; - } - - rt73usb_bbp_write(rt2x00dev, 77, r77); - rt73usb_bbp_write(rt2x00dev, 3, r3); - rt73usb_bbp_write(rt2x00dev, 4, r4); -} - -static void rt73usb_config_antenna_2x(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) -{ - u8 r3; - u8 r4; - u8 r77; - - rt73usb_bbp_read(rt2x00dev, 3, &r3); - rt73usb_bbp_read(rt2x00dev, 4, &r4); - rt73usb_bbp_read(rt2x00dev, 77, &r77); - - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0); - rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, - !rt2x00_has_cap_frame_type(rt2x00dev)); - - /* - * Configure the RX antenna. - */ - switch (ant->rx) { - case ANTENNA_HW_DIVERSITY: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); - break; - case ANTENNA_A: - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); - break; - case ANTENNA_B: - default: - rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); - break; - } - - rt73usb_bbp_write(rt2x00dev, 77, r77); - rt73usb_bbp_write(rt2x00dev, 3, r3); - rt73usb_bbp_write(rt2x00dev, 4, r4); -} - -struct antenna_sel { - u8 word; - /* - * value[0] -> non-LNA - * value[1] -> LNA - */ - u8 value[2]; -}; - -static const struct antenna_sel antenna_sel_a[] = { - { 96, { 0x58, 0x78 } }, - { 104, { 0x38, 0x48 } }, - { 75, { 0xfe, 0x80 } }, - { 86, { 0xfe, 0x80 } }, - { 88, { 0xfe, 0x80 } }, - { 35, { 0x60, 0x60 } }, - { 97, { 0x58, 0x58 } }, - { 98, { 0x58, 0x58 } }, -}; - -static const struct antenna_sel antenna_sel_bg[] = { - { 96, { 0x48, 0x68 } }, - { 104, { 0x2c, 0x3c } }, - { 75, { 0xfe, 0x80 } }, - { 86, { 0xfe, 0x80 } }, - { 88, { 0xfe, 0x80 } }, - { 35, { 0x50, 0x50 } }, - { 97, { 0x48, 0x48 } }, - { 98, { 0x48, 0x48 } }, -}; - -static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) -{ - const struct antenna_sel *sel; - unsigned int lna; - unsigned int i; - u32 reg; - - /* - * We should never come here because rt2x00lib is supposed - * to catch this and send us the correct antenna explicitely. - */ - BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || - ant->tx == ANTENNA_SW_DIVERSITY); - - if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { - sel = antenna_sel_a; - lna = rt2x00_has_cap_external_lna_a(rt2x00dev); - } else { - sel = antenna_sel_bg; - lna = rt2x00_has_cap_external_lna_bg(rt2x00dev); - } - - for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) - rt73usb_bbp_write(rt2x00dev, sel[i].word, sel[i].value[lna]); - - rt2x00usb_register_read(rt2x00dev, PHY_CSR0, ®); - - rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, - (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ)); - rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, - (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ)); - - rt2x00usb_register_write(rt2x00dev, PHY_CSR0, reg); - - if (rt2x00_rf(rt2x00dev, RF5226) || rt2x00_rf(rt2x00dev, RF5225)) - rt73usb_config_antenna_5x(rt2x00dev, ant); - else if (rt2x00_rf(rt2x00dev, RF2528) || rt2x00_rf(rt2x00dev, RF2527)) - rt73usb_config_antenna_2x(rt2x00dev, ant); -} - -static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - u16 eeprom; - short lna_gain = 0; - - if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { - if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) - lna_gain += 14; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); - lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); - } else { - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); - lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); - } - - rt2x00dev->lna_gain = lna_gain; -} - -static void rt73usb_config_channel(struct rt2x00_dev *rt2x00dev, - struct rf_channel *rf, const int txpower) -{ - u8 r3; - u8 r94; - u8 smart; - - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); - rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); - - smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)); - - rt73usb_bbp_read(rt2x00dev, 3, &r3); - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); - rt73usb_bbp_write(rt2x00dev, 3, r3); - - r94 = 6; - if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94)) - r94 += txpower - MAX_TXPOWER; - else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94)) - r94 += txpower; - rt73usb_bbp_write(rt2x00dev, 94, r94); - - rt73usb_rf_write(rt2x00dev, 1, rf->rf1); - rt73usb_rf_write(rt2x00dev, 2, rf->rf2); - rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt73usb_rf_write(rt2x00dev, 4, rf->rf4); - - rt73usb_rf_write(rt2x00dev, 1, rf->rf1); - rt73usb_rf_write(rt2x00dev, 2, rf->rf2); - rt73usb_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); - rt73usb_rf_write(rt2x00dev, 4, rf->rf4); - - rt73usb_rf_write(rt2x00dev, 1, rf->rf1); - rt73usb_rf_write(rt2x00dev, 2, rf->rf2); - rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt73usb_rf_write(rt2x00dev, 4, rf->rf4); - - udelay(10); -} - -static void rt73usb_config_txpower(struct rt2x00_dev *rt2x00dev, - const int txpower) -{ - struct rf_channel rf; - - rt2x00_rf_read(rt2x00dev, 1, &rf.rf1); - rt2x00_rf_read(rt2x00dev, 2, &rf.rf2); - rt2x00_rf_read(rt2x00dev, 3, &rf.rf3); - rt2x00_rf_read(rt2x00dev, 4, &rf.rf4); - - rt73usb_config_channel(rt2x00dev, &rf, txpower); -} - -static void rt73usb_config_retry_limit(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - u32 reg; - - rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, ®); - rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_DOWN, 1); - rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_STEP, 0); - rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_FALLBACK_CCK, 0); - rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, - libconf->conf->long_frame_max_tx_count); - rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, - libconf->conf->short_frame_max_tx_count); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg); -} - -static void rt73usb_config_ps(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - enum dev_state state = - (libconf->conf->flags & IEEE80211_CONF_PS) ? - STATE_SLEEP : STATE_AWAKE; - u32 reg; - - if (state == STATE_SLEEP) { - rt2x00usb_register_read(rt2x00dev, MAC_CSR11, ®); - rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, - rt2x00dev->beacon_int - 10); - rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, - libconf->conf->listen_interval - 1); - rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 5); - - /* We must first disable autowake before it can be enabled */ - rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); - rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); - - rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 1); - rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); - - rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, - USB_MODE_SLEEP, REGISTER_TIMEOUT); - } else { - rt2x00usb_register_read(rt2x00dev, MAC_CSR11, ®); - rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, 0); - rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, 0); - rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); - rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 0); - rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); - - rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, - USB_MODE_WAKEUP, REGISTER_TIMEOUT); - } -} - -static void rt73usb_config(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf, - const unsigned int flags) -{ - /* Always recalculate LNA gain before changing configuration */ - rt73usb_config_lna_gain(rt2x00dev, libconf); - - if (flags & IEEE80211_CONF_CHANGE_CHANNEL) - rt73usb_config_channel(rt2x00dev, &libconf->rf, - libconf->conf->power_level); - if ((flags & IEEE80211_CONF_CHANGE_POWER) && - !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) - rt73usb_config_txpower(rt2x00dev, libconf->conf->power_level); - if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) - rt73usb_config_retry_limit(rt2x00dev, libconf); - if (flags & IEEE80211_CONF_CHANGE_PS) - rt73usb_config_ps(rt2x00dev, libconf); -} - -/* - * Link tuning - */ -static void rt73usb_link_stats(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - u32 reg; - - /* - * Update FCS error count from register. - */ - rt2x00usb_register_read(rt2x00dev, STA_CSR0, ®); - qual->rx_failed = rt2x00_get_field32(reg, STA_CSR0_FCS_ERROR); - - /* - * Update False CCA count from register. - */ - rt2x00usb_register_read(rt2x00dev, STA_CSR1, ®); - qual->false_cca = rt2x00_get_field32(reg, STA_CSR1_FALSE_CCA_ERROR); -} - -static inline void rt73usb_set_vgc(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, u8 vgc_level) -{ - if (qual->vgc_level != vgc_level) { - rt73usb_bbp_write(rt2x00dev, 17, vgc_level); - qual->vgc_level = vgc_level; - qual->vgc_level_reg = vgc_level; - } -} - -static void rt73usb_reset_tuner(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual) -{ - rt73usb_set_vgc(rt2x00dev, qual, 0x20); -} - -static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev, - struct link_qual *qual, const u32 count) -{ - u8 up_bound; - u8 low_bound; - - /* - * Determine r17 bounds. - */ - if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { - low_bound = 0x28; - up_bound = 0x48; - - if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { - low_bound += 0x10; - up_bound += 0x10; - } - } else { - if (qual->rssi > -82) { - low_bound = 0x1c; - up_bound = 0x40; - } else if (qual->rssi > -84) { - low_bound = 0x1c; - up_bound = 0x20; - } else { - low_bound = 0x1c; - up_bound = 0x1c; - } - - if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { - low_bound += 0x14; - up_bound += 0x10; - } - } - - /* - * If we are not associated, we should go straight to the - * dynamic CCA tuning. - */ - if (!rt2x00dev->intf_associated) - goto dynamic_cca_tune; - - /* - * Special big-R17 for very short distance - */ - if (qual->rssi > -35) { - rt73usb_set_vgc(rt2x00dev, qual, 0x60); - return; - } - - /* - * Special big-R17 for short distance - */ - if (qual->rssi >= -58) { - rt73usb_set_vgc(rt2x00dev, qual, up_bound); - return; - } - - /* - * Special big-R17 for middle-short distance - */ - if (qual->rssi >= -66) { - rt73usb_set_vgc(rt2x00dev, qual, low_bound + 0x10); - return; - } - - /* - * Special mid-R17 for middle distance - */ - if (qual->rssi >= -74) { - rt73usb_set_vgc(rt2x00dev, qual, low_bound + 0x08); - return; - } - - /* - * Special case: Change up_bound based on the rssi. - * Lower up_bound when rssi is weaker then -74 dBm. - */ - up_bound -= 2 * (-74 - qual->rssi); - if (low_bound > up_bound) - up_bound = low_bound; - - if (qual->vgc_level > up_bound) { - rt73usb_set_vgc(rt2x00dev, qual, up_bound); - return; - } - -dynamic_cca_tune: - - /* - * r17 does not yet exceed upper limit, continue and base - * the r17 tuning on the false CCA count. - */ - if ((qual->false_cca > 512) && (qual->vgc_level < up_bound)) - rt73usb_set_vgc(rt2x00dev, qual, - min_t(u8, qual->vgc_level + 4, up_bound)); - else if ((qual->false_cca < 100) && (qual->vgc_level > low_bound)) - rt73usb_set_vgc(rt2x00dev, qual, - max_t(u8, qual->vgc_level - 4, low_bound)); -} - -/* - * Queue handlers. - */ -static void rt73usb_start_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); - break; - case QID_BEACON: - rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); - rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); - break; - default: - break; - } -} - -static void rt73usb_stop_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 1); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); - break; - case QID_BEACON: - rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); - rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); - break; - default: - break; - } -} - -/* - * Firmware functions - */ -static char *rt73usb_get_firmware_name(struct rt2x00_dev *rt2x00dev) -{ - return FIRMWARE_RT2571; -} - -static int rt73usb_check_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - u16 fw_crc; - u16 crc; - - /* - * Only support 2kb firmware files. - */ - if (len != 2048) - return FW_BAD_LENGTH; - - /* - * The last 2 bytes in the firmware array are the crc checksum itself, - * this means that we should never pass those 2 bytes to the crc - * algorithm. - */ - fw_crc = (data[len - 2] << 8 | data[len - 1]); - - /* - * Use the crc itu-t algorithm. - */ - crc = crc_itu_t(0, data, len - 2); - crc = crc_itu_t_byte(crc, 0); - crc = crc_itu_t_byte(crc, 0); - - return (fw_crc == crc) ? FW_OK : FW_BAD_CRC; -} - -static int rt73usb_load_firmware(struct rt2x00_dev *rt2x00dev, - const u8 *data, const size_t len) -{ - unsigned int i; - int status; - u32 reg; - - /* - * Wait for stable hardware. - */ - for (i = 0; i < 100; i++) { - rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); - if (reg) - break; - msleep(1); - } - - if (!reg) { - rt2x00_err(rt2x00dev, "Unstable hardware\n"); - return -EBUSY; - } - - /* - * Write firmware to device. - */ - rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, data, len); - - /* - * Send firmware request to device to load firmware, - * we need to specify a long timeout time. - */ - status = rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, - 0, USB_MODE_FIRMWARE, - REGISTER_TIMEOUT_FIRMWARE); - if (status < 0) { - rt2x00_err(rt2x00dev, "Failed to write Firmware to device\n"); - return status; - } - - return 0; -} - -/* - * Initialization functions. - */ -static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_AUTO_TX_SEQ, 1); - rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); - rt2x00_set_field32(®, TXRX_CSR0_TX_WITHOUT_WAITING, 0); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); - - rt2x00usb_register_read(rt2x00dev, TXRX_CSR1, ®); - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0, 47); /* CCK Signal */ - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1, 30); /* Rssi */ - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2, 42); /* OFDM Rate */ - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3, 30); /* Rssi */ - rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3_VALID, 1); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR1, reg); - - /* - * CCK TXD BBP registers - */ - rt2x00usb_register_read(rt2x00dev, TXRX_CSR2, ®); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0, 13); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1, 12); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2, 11); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3, 10); - rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3_VALID, 1); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR2, reg); - - /* - * OFDM TXD BBP registers - */ - rt2x00usb_register_read(rt2x00dev, TXRX_CSR3, ®); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0, 7); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1, 6); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1_VALID, 1); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2, 5); - rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2_VALID, 1); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR3, reg); - - rt2x00usb_register_read(rt2x00dev, TXRX_CSR7, ®); - rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_6MBS, 59); - rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_9MBS, 53); - rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_12MBS, 49); - rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_18MBS, 46); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR7, reg); - - rt2x00usb_register_read(rt2x00dev, TXRX_CSR8, ®); - rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_24MBS, 44); - rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_36MBS, 42); - rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_48MBS, 42); - rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_54MBS, 42); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR8, reg); - - rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, 0); - rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); - rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 0); - rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); - rt2x00_set_field32(®, TXRX_CSR9_TIMESTAMP_COMPENSATE, 0); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); - - rt2x00usb_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); - - rt2x00usb_register_read(rt2x00dev, MAC_CSR6, ®); - rt2x00_set_field32(®, MAC_CSR6_MAX_FRAME_UNIT, 0xfff); - rt2x00usb_register_write(rt2x00dev, MAC_CSR6, reg); - - rt2x00usb_register_write(rt2x00dev, MAC_CSR10, 0x00000718); - - if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) - return -EBUSY; - - rt2x00usb_register_write(rt2x00dev, MAC_CSR13, 0x00007f00); - - /* - * Invalidate all Shared Keys (SEC_CSR0), - * and clear the Shared key Cipher algorithms (SEC_CSR1 & SEC_CSR5) - */ - rt2x00usb_register_write(rt2x00dev, SEC_CSR0, 0x00000000); - rt2x00usb_register_write(rt2x00dev, SEC_CSR1, 0x00000000); - rt2x00usb_register_write(rt2x00dev, SEC_CSR5, 0x00000000); - - reg = 0x000023b0; - if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)) - rt2x00_set_field32(®, PHY_CSR1_RF_RPI, 1); - rt2x00usb_register_write(rt2x00dev, PHY_CSR1, reg); - - rt2x00usb_register_write(rt2x00dev, PHY_CSR5, 0x00040a06); - rt2x00usb_register_write(rt2x00dev, PHY_CSR6, 0x00080606); - rt2x00usb_register_write(rt2x00dev, PHY_CSR7, 0x00000408); - - rt2x00usb_register_read(rt2x00dev, MAC_CSR9, ®); - rt2x00_set_field32(®, MAC_CSR9_CW_SELECT, 0); - rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg); - - /* - * Clear all beacons - * For the Beacon base registers we only need to clear - * the first byte since that byte contains the VALID and OWNER - * bits which (when set to 0) will invalidate the entire beacon. - */ - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE0, 0); - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE1, 0); - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE2, 0); - rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE3, 0); - - /* - * We must clear the error counters. - * These registers are cleared on read, - * so we may pass a useless variable to store the value. - */ - rt2x00usb_register_read(rt2x00dev, STA_CSR0, ®); - rt2x00usb_register_read(rt2x00dev, STA_CSR1, ®); - rt2x00usb_register_read(rt2x00dev, STA_CSR2, ®); - - /* - * Reset MAC and BBP registers. - */ - rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); - rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); - rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); - rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); - - rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); - rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); - rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); - rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); - - rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); - rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); - rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); - - return 0; -} - -static int rt73usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u8 value; - - for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) { - rt73usb_bbp_read(rt2x00dev, 0, &value); - if ((value != 0xff) && (value != 0x00)) - return 0; - udelay(REGISTER_BUSY_DELAY); - } - - rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); - return -EACCES; -} - -static int rt73usb_init_bbp(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u16 eeprom; - u8 reg_id; - u8 value; - - if (unlikely(rt73usb_wait_bbp_ready(rt2x00dev))) - return -EACCES; - - rt73usb_bbp_write(rt2x00dev, 3, 0x80); - rt73usb_bbp_write(rt2x00dev, 15, 0x30); - rt73usb_bbp_write(rt2x00dev, 21, 0xc8); - rt73usb_bbp_write(rt2x00dev, 22, 0x38); - rt73usb_bbp_write(rt2x00dev, 23, 0x06); - rt73usb_bbp_write(rt2x00dev, 24, 0xfe); - rt73usb_bbp_write(rt2x00dev, 25, 0x0a); - rt73usb_bbp_write(rt2x00dev, 26, 0x0d); - rt73usb_bbp_write(rt2x00dev, 32, 0x0b); - rt73usb_bbp_write(rt2x00dev, 34, 0x12); - rt73usb_bbp_write(rt2x00dev, 37, 0x07); - rt73usb_bbp_write(rt2x00dev, 39, 0xf8); - rt73usb_bbp_write(rt2x00dev, 41, 0x60); - rt73usb_bbp_write(rt2x00dev, 53, 0x10); - rt73usb_bbp_write(rt2x00dev, 54, 0x18); - rt73usb_bbp_write(rt2x00dev, 60, 0x10); - rt73usb_bbp_write(rt2x00dev, 61, 0x04); - rt73usb_bbp_write(rt2x00dev, 62, 0x04); - rt73usb_bbp_write(rt2x00dev, 75, 0xfe); - rt73usb_bbp_write(rt2x00dev, 86, 0xfe); - rt73usb_bbp_write(rt2x00dev, 88, 0xfe); - rt73usb_bbp_write(rt2x00dev, 90, 0x0f); - rt73usb_bbp_write(rt2x00dev, 99, 0x00); - rt73usb_bbp_write(rt2x00dev, 102, 0x16); - rt73usb_bbp_write(rt2x00dev, 107, 0x04); - - for (i = 0; i < EEPROM_BBP_SIZE; i++) { - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); - - if (eeprom != 0xffff && eeprom != 0x0000) { - reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); - value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - rt73usb_bbp_write(rt2x00dev, reg_id, value); - } - } - - return 0; -} - -/* - * Device state switch handlers. - */ -static int rt73usb_enable_radio(struct rt2x00_dev *rt2x00dev) -{ - /* - * Initialize all registers. - */ - if (unlikely(rt73usb_init_registers(rt2x00dev) || - rt73usb_init_bbp(rt2x00dev))) - return -EIO; - - return 0; -} - -static void rt73usb_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - rt2x00usb_register_write(rt2x00dev, MAC_CSR10, 0x00001818); - - /* - * Disable synchronisation. - */ - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, 0); - - rt2x00usb_disable_radio(rt2x00dev); -} - -static int rt73usb_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) -{ - u32 reg, reg2; - unsigned int i; - char put_to_sleep; - - put_to_sleep = (state != STATE_AWAKE); - - rt2x00usb_register_read(rt2x00dev, MAC_CSR12, ®); - rt2x00_set_field32(®, MAC_CSR12_FORCE_WAKEUP, !put_to_sleep); - rt2x00_set_field32(®, MAC_CSR12_PUT_TO_SLEEP, put_to_sleep); - rt2x00usb_register_write(rt2x00dev, MAC_CSR12, reg); - - /* - * Device is not guaranteed to be in the requested state yet. - * We must wait until the register indicates that the - * device has entered the correct state. - */ - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00usb_register_read(rt2x00dev, MAC_CSR12, ®2); - state = rt2x00_get_field32(reg2, MAC_CSR12_BBP_CURRENT_STATE); - if (state == !put_to_sleep) - return 0; - rt2x00usb_register_write(rt2x00dev, MAC_CSR12, reg); - msleep(10); - } - - return -EBUSY; -} - -static int rt73usb_set_device_state(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - int retval = 0; - - switch (state) { - case STATE_RADIO_ON: - retval = rt73usb_enable_radio(rt2x00dev); - break; - case STATE_RADIO_OFF: - rt73usb_disable_radio(rt2x00dev); - break; - case STATE_RADIO_IRQ_ON: - case STATE_RADIO_IRQ_OFF: - /* No support, but no error either */ - break; - case STATE_DEEP_SLEEP: - case STATE_SLEEP: - case STATE_STANDBY: - case STATE_AWAKE: - retval = rt73usb_set_state(rt2x00dev, state); - break; - default: - retval = -ENOTSUPP; - break; - } - - if (unlikely(retval)) - rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", - state, retval); - - return retval; -} - -/* - * TX descriptor initialization - */ -static void rt73usb_write_tx_desc(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - __le32 *txd = (__le32 *) entry->skb->data; - u32 word; - - /* - * Start writing the descriptor words. - */ - rt2x00_desc_read(txd, 0, &word); - rt2x00_set_field32(&word, TXD_W0_BURST, - test_bit(ENTRY_TXD_BURST, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_VALID, 1); - rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, - test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_ACK, - test_bit(ENTRY_TXD_ACK, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, - test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_OFDM, - (txdesc->rate_mode == RATE_MODE_OFDM)); - rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); - rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, - test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, - test_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_KEY_TABLE, - test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx); - rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); - rt2x00_set_field32(&word, TXD_W0_BURST2, - test_bit(ENTRY_TXD_BURST, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher); - rt2x00_desc_write(txd, 0, word); - - rt2x00_desc_read(txd, 1, &word); - rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, entry->queue->qid); - rt2x00_set_field32(&word, TXD_W1_AIFSN, entry->queue->aifs); - rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); - rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); - rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); - rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, - test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); - rt2x00_desc_write(txd, 1, word); - - rt2x00_desc_read(txd, 2, &word); - rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); - rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, - txdesc->u.plcp.length_low); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, - txdesc->u.plcp.length_high); - rt2x00_desc_write(txd, 2, word); - - if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { - _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); - _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); - } - - rt2x00_desc_read(txd, 5, &word); - rt2x00_set_field32(&word, TXD_W5_TX_POWER, - TXPOWER_TO_DEV(entry->queue->rt2x00dev->tx_power)); - rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1); - rt2x00_desc_write(txd, 5, word); - - /* - * Register descriptor details in skb frame descriptor. - */ - skbdesc->flags |= SKBDESC_DESC_IN_SKB; - skbdesc->desc = txd; - skbdesc->desc_len = TXD_DESC_SIZE; -} - -/* - * TX data initialization - */ -static void rt73usb_write_beacon(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - unsigned int beacon_base; - unsigned int padding_len; - u32 orig_reg, reg; - - /* - * Disable beaconing while we are reloading the beacon data, - * otherwise we might be sending out invalid data. - */ - rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); - orig_reg = reg; - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); - - /* - * Add space for the descriptor in front of the skb. - */ - skb_push(entry->skb, TXD_DESC_SIZE); - memset(entry->skb->data, 0, TXD_DESC_SIZE); - - /* - * Write the TX descriptor for the beacon. - */ - rt73usb_write_tx_desc(entry, txdesc); - - /* - * Dump beacon to userspace through debugfs. - */ - rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); - - /* - * Write entire beacon with descriptor and padding to register. - */ - padding_len = roundup(entry->skb->len, 4) - entry->skb->len; - if (padding_len && skb_pad(entry->skb, padding_len)) { - rt2x00_err(rt2x00dev, "Failure padding beacon, aborting\n"); - /* skb freed by skb_pad() on failure */ - entry->skb = NULL; - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, orig_reg); - return; - } - - beacon_base = HW_BEACON_OFFSET(entry->entry_idx); - rt2x00usb_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data, - entry->skb->len + padding_len); - - /* - * Enable beaconing again. - * - * For Wi-Fi faily generated beacons between participating stations. - * Set TBTT phase adaptive adjustment step to 8us (default 16us) - */ - rt2x00usb_register_write(rt2x00dev, TXRX_CSR10, 0x00001008); - - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); - - /* - * Clean up the beacon skb. - */ - dev_kfree_skb(entry->skb); - entry->skb = NULL; -} - -static void rt73usb_clear_beacon(struct queue_entry *entry) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - unsigned int beacon_base; - u32 orig_reg, reg; - - /* - * Disable beaconing while we are reloading the beacon data, - * otherwise we might be sending out invalid data. - */ - rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &orig_reg); - reg = orig_reg; - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); - - /* - * Clear beacon. - */ - beacon_base = HW_BEACON_OFFSET(entry->entry_idx); - rt2x00usb_register_write(rt2x00dev, beacon_base, 0); - - /* - * Restore beaconing state. - */ - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, orig_reg); -} - -static int rt73usb_get_tx_data_len(struct queue_entry *entry) -{ - int length; - - /* - * The length _must_ be a multiple of 4, - * but it must _not_ be a multiple of the USB packet size. - */ - length = roundup(entry->skb->len, 4); - length += (4 * !(length % entry->queue->usb_maxpacket)); - - return length; -} - -/* - * RX control handlers - */ -static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) -{ - u8 offset = rt2x00dev->lna_gain; - u8 lna; - - lna = rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_LNA); - switch (lna) { - case 3: - offset += 90; - break; - case 2: - offset += 74; - break; - case 1: - offset += 64; - break; - default: - return 0; - } - - if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { - if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { - if (lna == 3 || lna == 2) - offset += 10; - } else { - if (lna == 3) - offset += 6; - else if (lna == 2) - offset += 8; - } - } - - return rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_AGC) * 2 - offset; -} - -static void rt73usb_fill_rxdone(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc) -{ - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - __le32 *rxd = (__le32 *)entry->skb->data; - u32 word0; - u32 word1; - - /* - * Copy descriptor to the skbdesc->desc buffer, making it safe from moving of - * frame data in rt2x00usb. - */ - memcpy(skbdesc->desc, rxd, skbdesc->desc_len); - rxd = (__le32 *)skbdesc->desc; - - /* - * It is now safe to read the descriptor on all architectures. - */ - rt2x00_desc_read(rxd, 0, &word0); - rt2x00_desc_read(rxd, 1, &word1); - - if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; - - rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG); - rxdesc->cipher_status = rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR); - - if (rxdesc->cipher != CIPHER_NONE) { - _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]); - _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]); - rxdesc->dev_flags |= RXDONE_CRYPTO_IV; - - _rt2x00_desc_read(rxd, 4, &rxdesc->icv); - rxdesc->dev_flags |= RXDONE_CRYPTO_ICV; - - /* - * Hardware has stripped IV/EIV data from 802.11 frame during - * decryption. It has provided the data separately but rt2x00lib - * should decide if it should be reinserted. - */ - rxdesc->flags |= RX_FLAG_IV_STRIPPED; - - /* - * The hardware has already checked the Michael Mic and has - * stripped it from the frame. Signal this to mac80211. - */ - rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; - - if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) - rxdesc->flags |= RX_FLAG_DECRYPTED; - else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) - rxdesc->flags |= RX_FLAG_MMIC_ERROR; - } - - /* - * Obtain the status about this packet. - * When frame was received with an OFDM bitrate, - * the signal is the PLCP value. If it was received with - * a CCK bitrate the signal is the rate in 100kbit/s. - */ - rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); - rxdesc->rssi = rt73usb_agc_to_rssi(rt2x00dev, word1); - rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); - - if (rt2x00_get_field32(word0, RXD_W0_OFDM)) - rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; - else - rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; - if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) - rxdesc->dev_flags |= RXDONE_MY_BSS; - - /* - * Set skb pointers, and update frame information. - */ - skb_pull(entry->skb, entry->queue->desc_size); - skb_trim(entry->skb, rxdesc->size); -} - -/* - * Device probe functions. - */ -static int rt73usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) -{ - u16 word; - u8 *mac; - s8 value; - - rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE); - - /* - * Start validation of the data that has been read. - */ - mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); - if (!is_valid_ether_addr(mac)) { - eth_random_addr(mac); - rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_ANTENNA_NUM, 2); - rt2x00_set_field16(&word, EEPROM_ANTENNA_TX_DEFAULT, - ANTENNA_B); - rt2x00_set_field16(&word, EEPROM_ANTENNA_RX_DEFAULT, - ANTENNA_B); - rt2x00_set_field16(&word, EEPROM_ANTENNA_FRAME_TYPE, 0); - rt2x00_set_field16(&word, EEPROM_ANTENNA_DYN_TXAGC, 0); - rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); - rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF5226); - rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); - rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); - rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_LED_POLARITY_RDY_G, 0); - rt2x00_set_field16(&word, EEPROM_LED_POLARITY_RDY_A, 0); - rt2x00_set_field16(&word, EEPROM_LED_POLARITY_ACT, 0); - rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_0, 0); - rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_1, 0); - rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_2, 0); - rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_3, 0); - rt2x00_set_field16(&word, EEPROM_LED_POLARITY_GPIO_4, 0); - rt2x00_set_field16(&word, EEPROM_LED_LED_MODE, - LED_MODE_DEFAULT); - rt2x00_eeprom_write(rt2x00dev, EEPROM_LED, word); - rt2x00_eeprom_dbg(rt2x00dev, "Led: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); - rt2x00_set_field16(&word, EEPROM_FREQ_SEQ, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); - rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); - rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); - } else { - value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_1); - if (value < -10 || value > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); - value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_2); - if (value < -10 || value > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); - } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &word); - if (word == 0xffff) { - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); - rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET A: 0x%04x\n", word); - } else { - value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_1); - if (value < -10 || value > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); - value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_2); - if (value < -10 || value > 10) - rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); - rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); - } - - return 0; -} - -static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - u16 value; - u16 eeprom; - - /* - * Read EEPROM word for configuration. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); - - /* - * Identify RF chipset. - */ - value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); - rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); - rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), - value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); - - if (!rt2x00_rt(rt2x00dev, RT2573) || (rt2x00_rev(rt2x00dev) == 0)) { - rt2x00_err(rt2x00dev, "Invalid RT chipset detected\n"); - return -ENODEV; - } - - if (!rt2x00_rf(rt2x00dev, RF5226) && - !rt2x00_rf(rt2x00dev, RF2528) && - !rt2x00_rf(rt2x00dev, RF5225) && - !rt2x00_rf(rt2x00dev, RF2527)) { - rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); - return -ENODEV; - } - - /* - * Identify default antenna configuration. - */ - rt2x00dev->default_ant.tx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_TX_DEFAULT); - rt2x00dev->default_ant.rx = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_DEFAULT); - - /* - * Read the Frame type. - */ - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_FRAME_TYPE)) - __set_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags); - - /* - * Detect if this device has an hardware controlled radio. - */ - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) - __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); - - /* - * Read frequency offset. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); - rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); - - /* - * Read external LNA informations. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); - - if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA)) { - __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); - __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); - } - - /* - * Store led settings, for correct led behaviour. - */ -#ifdef CONFIG_RT2X00_LIB_LEDS - rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); - - rt73usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); - rt73usb_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); - if (value == LED_MODE_SIGNAL_STRENGTH) - rt73usb_init_led(rt2x00dev, &rt2x00dev->led_qual, - LED_TYPE_QUALITY); - - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_LED_MODE, value); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_0, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_GPIO_0)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_1, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_GPIO_1)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_2, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_GPIO_2)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_3, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_GPIO_3)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_4, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_GPIO_4)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_ACT, - rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_ACT)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_BG, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_RDY_G)); - rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A, - rt2x00_get_field16(eeprom, - EEPROM_LED_POLARITY_RDY_A)); -#endif /* CONFIG_RT2X00_LIB_LEDS */ - - return 0; -} - -/* - * RF value list for RF2528 - * Supports: 2.4 GHz - */ -static const struct rf_channel rf_vals_bg_2528[] = { - { 1, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea0b }, - { 2, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea1f }, - { 3, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea0b }, - { 4, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea1f }, - { 5, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea0b }, - { 6, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea1f }, - { 7, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea0b }, - { 8, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea1f }, - { 9, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea0b }, - { 10, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea1f }, - { 11, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea0b }, - { 12, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea1f }, - { 13, 0x00002c0c, 0x0000079e, 0x00068255, 0x000fea0b }, - { 14, 0x00002c0c, 0x000007a2, 0x00068255, 0x000fea13 }, -}; - -/* - * RF value list for RF5226 - * Supports: 2.4 GHz & 5.2 GHz - */ -static const struct rf_channel rf_vals_5226[] = { - { 1, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea0b }, - { 2, 0x00002c0c, 0x00000786, 0x00068255, 0x000fea1f }, - { 3, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea0b }, - { 4, 0x00002c0c, 0x0000078a, 0x00068255, 0x000fea1f }, - { 5, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea0b }, - { 6, 0x00002c0c, 0x0000078e, 0x00068255, 0x000fea1f }, - { 7, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea0b }, - { 8, 0x00002c0c, 0x00000792, 0x00068255, 0x000fea1f }, - { 9, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea0b }, - { 10, 0x00002c0c, 0x00000796, 0x00068255, 0x000fea1f }, - { 11, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea0b }, - { 12, 0x00002c0c, 0x0000079a, 0x00068255, 0x000fea1f }, - { 13, 0x00002c0c, 0x0000079e, 0x00068255, 0x000fea0b }, - { 14, 0x00002c0c, 0x000007a2, 0x00068255, 0x000fea13 }, - - /* 802.11 UNI / HyperLan 2 */ - { 36, 0x00002c0c, 0x0000099a, 0x00098255, 0x000fea23 }, - { 40, 0x00002c0c, 0x000009a2, 0x00098255, 0x000fea03 }, - { 44, 0x00002c0c, 0x000009a6, 0x00098255, 0x000fea0b }, - { 48, 0x00002c0c, 0x000009aa, 0x00098255, 0x000fea13 }, - { 52, 0x00002c0c, 0x000009ae, 0x00098255, 0x000fea1b }, - { 56, 0x00002c0c, 0x000009b2, 0x00098255, 0x000fea23 }, - { 60, 0x00002c0c, 0x000009ba, 0x00098255, 0x000fea03 }, - { 64, 0x00002c0c, 0x000009be, 0x00098255, 0x000fea0b }, - - /* 802.11 HyperLan 2 */ - { 100, 0x00002c0c, 0x00000a2a, 0x000b8255, 0x000fea03 }, - { 104, 0x00002c0c, 0x00000a2e, 0x000b8255, 0x000fea0b }, - { 108, 0x00002c0c, 0x00000a32, 0x000b8255, 0x000fea13 }, - { 112, 0x00002c0c, 0x00000a36, 0x000b8255, 0x000fea1b }, - { 116, 0x00002c0c, 0x00000a3a, 0x000b8255, 0x000fea23 }, - { 120, 0x00002c0c, 0x00000a82, 0x000b8255, 0x000fea03 }, - { 124, 0x00002c0c, 0x00000a86, 0x000b8255, 0x000fea0b }, - { 128, 0x00002c0c, 0x00000a8a, 0x000b8255, 0x000fea13 }, - { 132, 0x00002c0c, 0x00000a8e, 0x000b8255, 0x000fea1b }, - { 136, 0x00002c0c, 0x00000a92, 0x000b8255, 0x000fea23 }, - - /* 802.11 UNII */ - { 140, 0x00002c0c, 0x00000a9a, 0x000b8255, 0x000fea03 }, - { 149, 0x00002c0c, 0x00000aa2, 0x000b8255, 0x000fea1f }, - { 153, 0x00002c0c, 0x00000aa6, 0x000b8255, 0x000fea27 }, - { 157, 0x00002c0c, 0x00000aae, 0x000b8255, 0x000fea07 }, - { 161, 0x00002c0c, 0x00000ab2, 0x000b8255, 0x000fea0f }, - { 165, 0x00002c0c, 0x00000ab6, 0x000b8255, 0x000fea17 }, - - /* MMAC(Japan)J52 ch 34,38,42,46 */ - { 34, 0x00002c0c, 0x0008099a, 0x000da255, 0x000d3a0b }, - { 38, 0x00002c0c, 0x0008099e, 0x000da255, 0x000d3a13 }, - { 42, 0x00002c0c, 0x000809a2, 0x000da255, 0x000d3a1b }, - { 46, 0x00002c0c, 0x000809a6, 0x000da255, 0x000d3a23 }, -}; - -/* - * RF value list for RF5225 & RF2527 - * Supports: 2.4 GHz & 5.2 GHz - */ -static const struct rf_channel rf_vals_5225_2527[] = { - { 1, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa0b }, - { 2, 0x00002ccc, 0x00004786, 0x00068455, 0x000ffa1f }, - { 3, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa0b }, - { 4, 0x00002ccc, 0x0000478a, 0x00068455, 0x000ffa1f }, - { 5, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa0b }, - { 6, 0x00002ccc, 0x0000478e, 0x00068455, 0x000ffa1f }, - { 7, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa0b }, - { 8, 0x00002ccc, 0x00004792, 0x00068455, 0x000ffa1f }, - { 9, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa0b }, - { 10, 0x00002ccc, 0x00004796, 0x00068455, 0x000ffa1f }, - { 11, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa0b }, - { 12, 0x00002ccc, 0x0000479a, 0x00068455, 0x000ffa1f }, - { 13, 0x00002ccc, 0x0000479e, 0x00068455, 0x000ffa0b }, - { 14, 0x00002ccc, 0x000047a2, 0x00068455, 0x000ffa13 }, - - /* 802.11 UNI / HyperLan 2 */ - { 36, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa23 }, - { 40, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa03 }, - { 44, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa0b }, - { 48, 0x00002ccc, 0x000049aa, 0x0009be55, 0x000ffa13 }, - { 52, 0x00002ccc, 0x000049ae, 0x0009ae55, 0x000ffa1b }, - { 56, 0x00002ccc, 0x000049b2, 0x0009ae55, 0x000ffa23 }, - { 60, 0x00002ccc, 0x000049ba, 0x0009ae55, 0x000ffa03 }, - { 64, 0x00002ccc, 0x000049be, 0x0009ae55, 0x000ffa0b }, - - /* 802.11 HyperLan 2 */ - { 100, 0x00002ccc, 0x00004a2a, 0x000bae55, 0x000ffa03 }, - { 104, 0x00002ccc, 0x00004a2e, 0x000bae55, 0x000ffa0b }, - { 108, 0x00002ccc, 0x00004a32, 0x000bae55, 0x000ffa13 }, - { 112, 0x00002ccc, 0x00004a36, 0x000bae55, 0x000ffa1b }, - { 116, 0x00002ccc, 0x00004a3a, 0x000bbe55, 0x000ffa23 }, - { 120, 0x00002ccc, 0x00004a82, 0x000bbe55, 0x000ffa03 }, - { 124, 0x00002ccc, 0x00004a86, 0x000bbe55, 0x000ffa0b }, - { 128, 0x00002ccc, 0x00004a8a, 0x000bbe55, 0x000ffa13 }, - { 132, 0x00002ccc, 0x00004a8e, 0x000bbe55, 0x000ffa1b }, - { 136, 0x00002ccc, 0x00004a92, 0x000bbe55, 0x000ffa23 }, - - /* 802.11 UNII */ - { 140, 0x00002ccc, 0x00004a9a, 0x000bbe55, 0x000ffa03 }, - { 149, 0x00002ccc, 0x00004aa2, 0x000bbe55, 0x000ffa1f }, - { 153, 0x00002ccc, 0x00004aa6, 0x000bbe55, 0x000ffa27 }, - { 157, 0x00002ccc, 0x00004aae, 0x000bbe55, 0x000ffa07 }, - { 161, 0x00002ccc, 0x00004ab2, 0x000bbe55, 0x000ffa0f }, - { 165, 0x00002ccc, 0x00004ab6, 0x000bbe55, 0x000ffa17 }, - - /* MMAC(Japan)J52 ch 34,38,42,46 */ - { 34, 0x00002ccc, 0x0000499a, 0x0009be55, 0x000ffa0b }, - { 38, 0x00002ccc, 0x0000499e, 0x0009be55, 0x000ffa13 }, - { 42, 0x00002ccc, 0x000049a2, 0x0009be55, 0x000ffa1b }, - { 46, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000ffa23 }, -}; - - -static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) -{ - struct hw_mode_spec *spec = &rt2x00dev->spec; - struct channel_info *info; - char *tx_power; - unsigned int i; - - /* - * Initialize all hw fields. - * - * Don't set IEEE80211_HOST_BROADCAST_PS_BUFFERING unless we are - * capable of sending the buffered frames out after the DTIM - * transmission using rt2x00lib_beacondone. This will send out - * multicast and broadcast traffic immediately instead of buffering it - * infinitly and thus dropping it after some time. - */ - ieee80211_hw_set(rt2x00dev->hw, PS_NULLFUNC_STACK); - ieee80211_hw_set(rt2x00dev->hw, SIGNAL_DBM); - ieee80211_hw_set(rt2x00dev->hw, SUPPORTS_PS); - - SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); - SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, - rt2x00_eeprom_addr(rt2x00dev, - EEPROM_MAC_ADDR_0)); - - /* - * Initialize hw_mode information. - */ - spec->supported_bands = SUPPORT_BAND_2GHZ; - spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - - if (rt2x00_rf(rt2x00dev, RF2528)) { - spec->num_channels = ARRAY_SIZE(rf_vals_bg_2528); - spec->channels = rf_vals_bg_2528; - } else if (rt2x00_rf(rt2x00dev, RF5226)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; - spec->num_channels = ARRAY_SIZE(rf_vals_5226); - spec->channels = rf_vals_5226; - } else if (rt2x00_rf(rt2x00dev, RF2527)) { - spec->num_channels = 14; - spec->channels = rf_vals_5225_2527; - } else if (rt2x00_rf(rt2x00dev, RF5225)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; - spec->num_channels = ARRAY_SIZE(rf_vals_5225_2527); - spec->channels = rf_vals_5225_2527; - } - - /* - * Create channel information array - */ - info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - spec->channels_info = info; - - tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); - for (i = 0; i < 14; i++) { - info[i].max_power = MAX_TXPOWER; - info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); - } - - if (spec->num_channels > 14) { - tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); - for (i = 14; i < spec->num_channels; i++) { - info[i].max_power = MAX_TXPOWER; - info[i].default_power1 = - TXPOWER_FROM_DEV(tx_power[i - 14]); - } - } - - return 0; -} - -static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) -{ - int retval; - u32 reg; - - /* - * Allocate eeprom data. - */ - retval = rt73usb_validate_eeprom(rt2x00dev); - if (retval) - return retval; - - retval = rt73usb_init_eeprom(rt2x00dev); - if (retval) - return retval; - - /* - * Enable rfkill polling by setting GPIO direction of the - * rfkill switch GPIO pin correctly. - */ - rt2x00usb_register_read(rt2x00dev, MAC_CSR13, ®); - rt2x00_set_field32(®, MAC_CSR13_DIR7, 0); - rt2x00usb_register_write(rt2x00dev, MAC_CSR13, reg); - - /* - * Initialize hw specifications. - */ - retval = rt73usb_probe_hw_mode(rt2x00dev); - if (retval) - return retval; - - /* - * This device has multiple filters for control frames, - * but has no a separate filter for PS Poll frames. - */ - __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); - - /* - * This device requires firmware. - */ - __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); - if (!modparam_nohwcrypt) - __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); - __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); - __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); - - /* - * Set the rssi offset. - */ - rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; - - return 0; -} - -/* - * IEEE80211 stack callback functions. - */ -static int rt73usb_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue_idx, - const struct ieee80211_tx_queue_params *params) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct data_queue *queue; - struct rt2x00_field32 field; - int retval; - u32 reg; - u32 offset; - - /* - * First pass the configuration through rt2x00lib, that will - * update the queue settings and validate the input. After that - * we are free to update the registers based on the value - * in the queue parameter. - */ - retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); - if (retval) - return retval; - - /* - * We only need to perform additional register initialization - * for WMM queues/ - */ - if (queue_idx >= 4) - return 0; - - queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); - - /* Update WMM TXOP register */ - offset = AC_TXOP_CSR0 + (sizeof(u32) * (!!(queue_idx & 2))); - field.bit_offset = (queue_idx & 1) * 16; - field.bit_mask = 0xffff << field.bit_offset; - - rt2x00usb_register_read(rt2x00dev, offset, ®); - rt2x00_set_field32(®, field, queue->txop); - rt2x00usb_register_write(rt2x00dev, offset, reg); - - /* Update WMM registers */ - field.bit_offset = queue_idx * 4; - field.bit_mask = 0xf << field.bit_offset; - - rt2x00usb_register_read(rt2x00dev, AIFSN_CSR, ®); - rt2x00_set_field32(®, field, queue->aifs); - rt2x00usb_register_write(rt2x00dev, AIFSN_CSR, reg); - - rt2x00usb_register_read(rt2x00dev, CWMIN_CSR, ®); - rt2x00_set_field32(®, field, queue->cw_min); - rt2x00usb_register_write(rt2x00dev, CWMIN_CSR, reg); - - rt2x00usb_register_read(rt2x00dev, CWMAX_CSR, ®); - rt2x00_set_field32(®, field, queue->cw_max); - rt2x00usb_register_write(rt2x00dev, CWMAX_CSR, reg); - - return 0; -} - -static u64 rt73usb_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u64 tsf; - u32 reg; - - rt2x00usb_register_read(rt2x00dev, TXRX_CSR13, ®); - tsf = (u64) rt2x00_get_field32(reg, TXRX_CSR13_HIGH_TSFTIMER) << 32; - rt2x00usb_register_read(rt2x00dev, TXRX_CSR12, ®); - tsf |= rt2x00_get_field32(reg, TXRX_CSR12_LOW_TSFTIMER); - - return tsf; -} - -static const struct ieee80211_ops rt73usb_mac80211_ops = { - .tx = rt2x00mac_tx, - .start = rt2x00mac_start, - .stop = rt2x00mac_stop, - .add_interface = rt2x00mac_add_interface, - .remove_interface = rt2x00mac_remove_interface, - .config = rt2x00mac_config, - .configure_filter = rt2x00mac_configure_filter, - .set_tim = rt2x00mac_set_tim, - .set_key = rt2x00mac_set_key, - .sw_scan_start = rt2x00mac_sw_scan_start, - .sw_scan_complete = rt2x00mac_sw_scan_complete, - .get_stats = rt2x00mac_get_stats, - .bss_info_changed = rt2x00mac_bss_info_changed, - .conf_tx = rt73usb_conf_tx, - .get_tsf = rt73usb_get_tsf, - .rfkill_poll = rt2x00mac_rfkill_poll, - .flush = rt2x00mac_flush, - .set_antenna = rt2x00mac_set_antenna, - .get_antenna = rt2x00mac_get_antenna, - .get_ringparam = rt2x00mac_get_ringparam, - .tx_frames_pending = rt2x00mac_tx_frames_pending, -}; - -static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { - .probe_hw = rt73usb_probe_hw, - .get_firmware_name = rt73usb_get_firmware_name, - .check_firmware = rt73usb_check_firmware, - .load_firmware = rt73usb_load_firmware, - .initialize = rt2x00usb_initialize, - .uninitialize = rt2x00usb_uninitialize, - .clear_entry = rt2x00usb_clear_entry, - .set_device_state = rt73usb_set_device_state, - .rfkill_poll = rt73usb_rfkill_poll, - .link_stats = rt73usb_link_stats, - .reset_tuner = rt73usb_reset_tuner, - .link_tuner = rt73usb_link_tuner, - .watchdog = rt2x00usb_watchdog, - .start_queue = rt73usb_start_queue, - .kick_queue = rt2x00usb_kick_queue, - .stop_queue = rt73usb_stop_queue, - .flush_queue = rt2x00usb_flush_queue, - .write_tx_desc = rt73usb_write_tx_desc, - .write_beacon = rt73usb_write_beacon, - .clear_beacon = rt73usb_clear_beacon, - .get_tx_data_len = rt73usb_get_tx_data_len, - .fill_rxdone = rt73usb_fill_rxdone, - .config_shared_key = rt73usb_config_shared_key, - .config_pairwise_key = rt73usb_config_pairwise_key, - .config_filter = rt73usb_config_filter, - .config_intf = rt73usb_config_intf, - .config_erp = rt73usb_config_erp, - .config_ant = rt73usb_config_ant, - .config = rt73usb_config, -}; - -static void rt73usb_queue_init(struct data_queue *queue) -{ - switch (queue->qid) { - case QID_RX: - queue->limit = 32; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = RXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_usb); - break; - - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - queue->limit = 32; - queue->data_size = DATA_FRAME_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_usb); - break; - - case QID_BEACON: - queue->limit = 4; - queue->data_size = MGMT_FRAME_SIZE; - queue->desc_size = TXINFO_SIZE; - queue->priv_size = sizeof(struct queue_entry_priv_usb); - break; - - case QID_ATIM: - /* fallthrough */ - default: - BUG(); - break; - } -} - -static const struct rt2x00_ops rt73usb_ops = { - .name = KBUILD_MODNAME, - .max_ap_intf = 4, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .queue_init = rt73usb_queue_init, - .lib = &rt73usb_rt2x00_ops, - .hw = &rt73usb_mac80211_ops, -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt73usb_rt2x00debug, -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -}; - -/* - * rt73usb module information. - */ -static struct usb_device_id rt73usb_device_table[] = { - /* AboCom */ - { USB_DEVICE(0x07b8, 0xb21b) }, - { USB_DEVICE(0x07b8, 0xb21c) }, - { USB_DEVICE(0x07b8, 0xb21d) }, - { USB_DEVICE(0x07b8, 0xb21e) }, - { USB_DEVICE(0x07b8, 0xb21f) }, - /* AL */ - { USB_DEVICE(0x14b2, 0x3c10) }, - /* Amigo */ - { USB_DEVICE(0x148f, 0x9021) }, - { USB_DEVICE(0x0eb0, 0x9021) }, - /* AMIT */ - { USB_DEVICE(0x18c5, 0x0002) }, - /* Askey */ - { USB_DEVICE(0x1690, 0x0722) }, - /* ASUS */ - { USB_DEVICE(0x0b05, 0x1723) }, - { USB_DEVICE(0x0b05, 0x1724) }, - /* Belkin */ - { USB_DEVICE(0x050d, 0x7050) }, /* FCC ID: K7SF5D7050B ver. 3.x */ - { USB_DEVICE(0x050d, 0x705a) }, - { USB_DEVICE(0x050d, 0x905b) }, - { USB_DEVICE(0x050d, 0x905c) }, - /* Billionton */ - { USB_DEVICE(0x1631, 0xc019) }, - { USB_DEVICE(0x08dd, 0x0120) }, - /* Buffalo */ - { USB_DEVICE(0x0411, 0x00d8) }, - { USB_DEVICE(0x0411, 0x00d9) }, - { USB_DEVICE(0x0411, 0x00e6) }, - { USB_DEVICE(0x0411, 0x00f4) }, - { USB_DEVICE(0x0411, 0x0116) }, - { USB_DEVICE(0x0411, 0x0119) }, - { USB_DEVICE(0x0411, 0x0137) }, - /* CEIVA */ - { USB_DEVICE(0x178d, 0x02be) }, - /* CNet */ - { USB_DEVICE(0x1371, 0x9022) }, - { USB_DEVICE(0x1371, 0x9032) }, - /* Conceptronic */ - { USB_DEVICE(0x14b2, 0x3c22) }, - /* Corega */ - { USB_DEVICE(0x07aa, 0x002e) }, - /* D-Link */ - { USB_DEVICE(0x07d1, 0x3c03) }, - { USB_DEVICE(0x07d1, 0x3c04) }, - { USB_DEVICE(0x07d1, 0x3c06) }, - { USB_DEVICE(0x07d1, 0x3c07) }, - /* Edimax */ - { USB_DEVICE(0x7392, 0x7318) }, - { USB_DEVICE(0x7392, 0x7618) }, - /* EnGenius */ - { USB_DEVICE(0x1740, 0x3701) }, - /* Gemtek */ - { USB_DEVICE(0x15a9, 0x0004) }, - /* Gigabyte */ - { USB_DEVICE(0x1044, 0x8008) }, - { USB_DEVICE(0x1044, 0x800a) }, - /* Huawei-3Com */ - { USB_DEVICE(0x1472, 0x0009) }, - /* Hercules */ - { USB_DEVICE(0x06f8, 0xe002) }, - { USB_DEVICE(0x06f8, 0xe010) }, - { USB_DEVICE(0x06f8, 0xe020) }, - /* Linksys */ - { USB_DEVICE(0x13b1, 0x0020) }, - { USB_DEVICE(0x13b1, 0x0023) }, - { USB_DEVICE(0x13b1, 0x0028) }, - /* MSI */ - { USB_DEVICE(0x0db0, 0x4600) }, - { USB_DEVICE(0x0db0, 0x6877) }, - { USB_DEVICE(0x0db0, 0x6874) }, - { USB_DEVICE(0x0db0, 0xa861) }, - { USB_DEVICE(0x0db0, 0xa874) }, - /* Ovislink */ - { USB_DEVICE(0x1b75, 0x7318) }, - /* Ralink */ - { USB_DEVICE(0x04bb, 0x093d) }, - { USB_DEVICE(0x148f, 0x2573) }, - { USB_DEVICE(0x148f, 0x2671) }, - { USB_DEVICE(0x0812, 0x3101) }, - /* Qcom */ - { USB_DEVICE(0x18e8, 0x6196) }, - { USB_DEVICE(0x18e8, 0x6229) }, - { USB_DEVICE(0x18e8, 0x6238) }, - /* Samsung */ - { USB_DEVICE(0x04e8, 0x4471) }, - /* Senao */ - { USB_DEVICE(0x1740, 0x7100) }, - /* Sitecom */ - { USB_DEVICE(0x0df6, 0x0024) }, - { USB_DEVICE(0x0df6, 0x0027) }, - { USB_DEVICE(0x0df6, 0x002f) }, - { USB_DEVICE(0x0df6, 0x90ac) }, - { USB_DEVICE(0x0df6, 0x9712) }, - /* Surecom */ - { USB_DEVICE(0x0769, 0x31f3) }, - /* Tilgin */ - { USB_DEVICE(0x6933, 0x5001) }, - /* Philips */ - { USB_DEVICE(0x0471, 0x200a) }, - /* Planex */ - { USB_DEVICE(0x2019, 0xab01) }, - { USB_DEVICE(0x2019, 0xab50) }, - /* WideTell */ - { USB_DEVICE(0x7167, 0x3840) }, - /* Zcom */ - { USB_DEVICE(0x0cde, 0x001c) }, - /* ZyXEL */ - { USB_DEVICE(0x0586, 0x3415) }, - { 0, } -}; - -MODULE_AUTHOR(DRV_PROJECT); -MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("Ralink RT73 USB Wireless LAN driver."); -MODULE_SUPPORTED_DEVICE("Ralink RT2571W & RT2671 USB chipset based cards"); -MODULE_DEVICE_TABLE(usb, rt73usb_device_table); -MODULE_FIRMWARE(FIRMWARE_RT2571); -MODULE_LICENSE("GPL"); - -static int rt73usb_probe(struct usb_interface *usb_intf, - const struct usb_device_id *id) -{ - return rt2x00usb_probe(usb_intf, &rt73usb_ops); -} - -static struct usb_driver rt73usb_driver = { - .name = KBUILD_MODNAME, - .id_table = rt73usb_device_table, - .probe = rt73usb_probe, - .disconnect = rt2x00usb_disconnect, - .suspend = rt2x00usb_suspend, - .resume = rt2x00usb_resume, - .reset_resume = rt2x00usb_resume, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(rt73usb_driver); diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h deleted file mode 100644 index 4a4f235466d1..000000000000 --- a/drivers/net/wireless/rt2x00/rt73usb.h +++ /dev/null @@ -1,1079 +0,0 @@ -/* - Copyright (C) 2004 - 2009 Ivo van Doorn - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . - */ - -/* - Module: rt73usb - Abstract: Data structures and registers for the rt73usb module. - Supported chipsets: rt2571W & rt2671. - */ - -#ifndef RT73USB_H -#define RT73USB_H - -/* - * RF chip defines. - */ -#define RF5226 0x0001 -#define RF2528 0x0002 -#define RF5225 0x0003 -#define RF2527 0x0004 - -/* - * Signal information. - * Default offset is required for RSSI <-> dBm conversion. - */ -#define DEFAULT_RSSI_OFFSET 120 - -/* - * Register layout information. - */ -#define CSR_REG_BASE 0x3000 -#define CSR_REG_SIZE 0x04b0 -#define EEPROM_BASE 0x0000 -#define EEPROM_SIZE 0x0100 -#define BBP_BASE 0x0000 -#define BBP_SIZE 0x0080 -#define RF_BASE 0x0004 -#define RF_SIZE 0x0010 - -/* - * Number of TX queues. - */ -#define NUM_TX_QUEUES 4 - -/* - * USB registers. - */ - -/* - * MCU_LEDCS: LED control for MCU Mailbox. - */ -#define MCU_LEDCS_LED_MODE FIELD16(0x001f) -#define MCU_LEDCS_RADIO_STATUS FIELD16(0x0020) -#define MCU_LEDCS_LINK_BG_STATUS FIELD16(0x0040) -#define MCU_LEDCS_LINK_A_STATUS FIELD16(0x0080) -#define MCU_LEDCS_POLARITY_GPIO_0 FIELD16(0x0100) -#define MCU_LEDCS_POLARITY_GPIO_1 FIELD16(0x0200) -#define MCU_LEDCS_POLARITY_GPIO_2 FIELD16(0x0400) -#define MCU_LEDCS_POLARITY_GPIO_3 FIELD16(0x0800) -#define MCU_LEDCS_POLARITY_GPIO_4 FIELD16(0x1000) -#define MCU_LEDCS_POLARITY_ACT FIELD16(0x2000) -#define MCU_LEDCS_POLARITY_READY_BG FIELD16(0x4000) -#define MCU_LEDCS_POLARITY_READY_A FIELD16(0x8000) - -/* - * 8051 firmware image. - */ -#define FIRMWARE_RT2571 "rt73.bin" -#define FIRMWARE_IMAGE_BASE 0x0800 - -/* - * Security key table memory. - * 16 entries 32-byte for shared key table - * 64 entries 32-byte for pairwise key table - * 64 entries 8-byte for pairwise ta key table - */ -#define SHARED_KEY_TABLE_BASE 0x1000 -#define PAIRWISE_KEY_TABLE_BASE 0x1200 -#define PAIRWISE_TA_TABLE_BASE 0x1a00 - -#define SHARED_KEY_ENTRY(__idx) \ - ( SHARED_KEY_TABLE_BASE + \ - ((__idx) * sizeof(struct hw_key_entry)) ) -#define PAIRWISE_KEY_ENTRY(__idx) \ - ( PAIRWISE_KEY_TABLE_BASE + \ - ((__idx) * sizeof(struct hw_key_entry)) ) -#define PAIRWISE_TA_ENTRY(__idx) \ - ( PAIRWISE_TA_TABLE_BASE + \ - ((__idx) * sizeof(struct hw_pairwise_ta_entry)) ) - -struct hw_key_entry { - u8 key[16]; - u8 tx_mic[8]; - u8 rx_mic[8]; -} __packed; - -struct hw_pairwise_ta_entry { - u8 address[6]; - u8 cipher; - u8 reserved; -} __packed; - -/* - * Since NULL frame won't be that long (256 byte), - * We steal 16 tail bytes to save debugging settings. - */ -#define HW_DEBUG_SETTING_BASE 0x2bf0 - -/* - * On-chip BEACON frame space. - */ -#define HW_BEACON_BASE0 0x2400 -#define HW_BEACON_BASE1 0x2500 -#define HW_BEACON_BASE2 0x2600 -#define HW_BEACON_BASE3 0x2700 - -#define HW_BEACON_OFFSET(__index) \ - ( HW_BEACON_BASE0 + (__index * 0x0100) ) - -/* - * MAC Control/Status Registers(CSR). - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * MAC_CSR0: ASIC revision number. - */ -#define MAC_CSR0 0x3000 -#define MAC_CSR0_REVISION FIELD32(0x0000000f) -#define MAC_CSR0_CHIPSET FIELD32(0x000ffff0) - -/* - * MAC_CSR1: System control register. - * SOFT_RESET: Software reset bit, 1: reset, 0: normal. - * BBP_RESET: Hardware reset BBP. - * HOST_READY: Host is ready after initialization, 1: ready. - */ -#define MAC_CSR1 0x3004 -#define MAC_CSR1_SOFT_RESET FIELD32(0x00000001) -#define MAC_CSR1_BBP_RESET FIELD32(0x00000002) -#define MAC_CSR1_HOST_READY FIELD32(0x00000004) - -/* - * MAC_CSR2: STA MAC register 0. - */ -#define MAC_CSR2 0x3008 -#define MAC_CSR2_BYTE0 FIELD32(0x000000ff) -#define MAC_CSR2_BYTE1 FIELD32(0x0000ff00) -#define MAC_CSR2_BYTE2 FIELD32(0x00ff0000) -#define MAC_CSR2_BYTE3 FIELD32(0xff000000) - -/* - * MAC_CSR3: STA MAC register 1. - * UNICAST_TO_ME_MASK: - * Used to mask off bits from byte 5 of the MAC address - * to determine the UNICAST_TO_ME bit for RX frames. - * The full mask is complemented by BSS_ID_MASK: - * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK - */ -#define MAC_CSR3 0x300c -#define MAC_CSR3_BYTE4 FIELD32(0x000000ff) -#define MAC_CSR3_BYTE5 FIELD32(0x0000ff00) -#define MAC_CSR3_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) - -/* - * MAC_CSR4: BSSID register 0. - */ -#define MAC_CSR4 0x3010 -#define MAC_CSR4_BYTE0 FIELD32(0x000000ff) -#define MAC_CSR4_BYTE1 FIELD32(0x0000ff00) -#define MAC_CSR4_BYTE2 FIELD32(0x00ff0000) -#define MAC_CSR4_BYTE3 FIELD32(0xff000000) - -/* - * MAC_CSR5: BSSID register 1. - * BSS_ID_MASK: - * This mask is used to mask off bits 0 and 1 of byte 5 of the - * BSSID. This will make sure that those bits will be ignored - * when determining the MY_BSS of RX frames. - * 0: 1-BSSID mode (BSS index = 0) - * 1: 2-BSSID mode (BSS index: Byte5, bit 0) - * 2: 2-BSSID mode (BSS index: byte5, bit 1) - * 3: 4-BSSID mode (BSS index: byte5, bit 0 - 1) - */ -#define MAC_CSR5 0x3014 -#define MAC_CSR5_BYTE4 FIELD32(0x000000ff) -#define MAC_CSR5_BYTE5 FIELD32(0x0000ff00) -#define MAC_CSR5_BSS_ID_MASK FIELD32(0x00ff0000) - -/* - * MAC_CSR6: Maximum frame length register. - */ -#define MAC_CSR6 0x3018 -#define MAC_CSR6_MAX_FRAME_UNIT FIELD32(0x00000fff) - -/* - * MAC_CSR7: Reserved - */ -#define MAC_CSR7 0x301c - -/* - * MAC_CSR8: SIFS/EIFS register. - * All units are in US. - */ -#define MAC_CSR8 0x3020 -#define MAC_CSR8_SIFS FIELD32(0x000000ff) -#define MAC_CSR8_SIFS_AFTER_RX_OFDM FIELD32(0x0000ff00) -#define MAC_CSR8_EIFS FIELD32(0xffff0000) - -/* - * MAC_CSR9: Back-Off control register. - * SLOT_TIME: Slot time, default is 20us for 802.11BG. - * CWMIN: Bit for Cwmin. default Cwmin is 31 (2^5 - 1). - * CWMAX: Bit for Cwmax, default Cwmax is 1023 (2^10 - 1). - * CW_SELECT: 1: CWmin/Cwmax select from register, 0:select from TxD. - */ -#define MAC_CSR9 0x3024 -#define MAC_CSR9_SLOT_TIME FIELD32(0x000000ff) -#define MAC_CSR9_CWMIN FIELD32(0x00000f00) -#define MAC_CSR9_CWMAX FIELD32(0x0000f000) -#define MAC_CSR9_CW_SELECT FIELD32(0x00010000) - -/* - * MAC_CSR10: Power state configuration. - */ -#define MAC_CSR10 0x3028 - -/* - * MAC_CSR11: Power saving transition time register. - * DELAY_AFTER_TBCN: Delay after Tbcn expired in units of TU. - * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. - * WAKEUP_LATENCY: In unit of TU. - */ -#define MAC_CSR11 0x302c -#define MAC_CSR11_DELAY_AFTER_TBCN FIELD32(0x000000ff) -#define MAC_CSR11_TBCN_BEFORE_WAKEUP FIELD32(0x00007f00) -#define MAC_CSR11_AUTOWAKE FIELD32(0x00008000) -#define MAC_CSR11_WAKEUP_LATENCY FIELD32(0x000f0000) - -/* - * MAC_CSR12: Manual power control / status register (merge CSR20 & PWRCSR1). - * CURRENT_STATE: 0:sleep, 1:awake. - * FORCE_WAKEUP: This has higher priority than PUT_TO_SLEEP. - * BBP_CURRENT_STATE: 0: BBP sleep, 1: BBP awake. - */ -#define MAC_CSR12 0x3030 -#define MAC_CSR12_CURRENT_STATE FIELD32(0x00000001) -#define MAC_CSR12_PUT_TO_SLEEP FIELD32(0x00000002) -#define MAC_CSR12_FORCE_WAKEUP FIELD32(0x00000004) -#define MAC_CSR12_BBP_CURRENT_STATE FIELD32(0x00000008) - -/* - * MAC_CSR13: GPIO. - * MAC_CSR13_VALx: GPIO value - * MAC_CSR13_DIRx: GPIO direction: 0 = input; 1 = output - */ -#define MAC_CSR13 0x3034 -#define MAC_CSR13_VAL0 FIELD32(0x00000001) -#define MAC_CSR13_VAL1 FIELD32(0x00000002) -#define MAC_CSR13_VAL2 FIELD32(0x00000004) -#define MAC_CSR13_VAL3 FIELD32(0x00000008) -#define MAC_CSR13_VAL4 FIELD32(0x00000010) -#define MAC_CSR13_VAL5 FIELD32(0x00000020) -#define MAC_CSR13_VAL6 FIELD32(0x00000040) -#define MAC_CSR13_VAL7 FIELD32(0x00000080) -#define MAC_CSR13_DIR0 FIELD32(0x00000100) -#define MAC_CSR13_DIR1 FIELD32(0x00000200) -#define MAC_CSR13_DIR2 FIELD32(0x00000400) -#define MAC_CSR13_DIR3 FIELD32(0x00000800) -#define MAC_CSR13_DIR4 FIELD32(0x00001000) -#define MAC_CSR13_DIR5 FIELD32(0x00002000) -#define MAC_CSR13_DIR6 FIELD32(0x00004000) -#define MAC_CSR13_DIR7 FIELD32(0x00008000) - -/* - * MAC_CSR14: LED control register. - * ON_PERIOD: On period, default 70ms. - * OFF_PERIOD: Off period, default 30ms. - * HW_LED: HW TX activity, 1: normal OFF, 0: normal ON. - * SW_LED: s/w LED, 1: ON, 0: OFF. - * HW_LED_POLARITY: 0: active low, 1: active high. - */ -#define MAC_CSR14 0x3038 -#define MAC_CSR14_ON_PERIOD FIELD32(0x000000ff) -#define MAC_CSR14_OFF_PERIOD FIELD32(0x0000ff00) -#define MAC_CSR14_HW_LED FIELD32(0x00010000) -#define MAC_CSR14_SW_LED FIELD32(0x00020000) -#define MAC_CSR14_HW_LED_POLARITY FIELD32(0x00040000) -#define MAC_CSR14_SW_LED2 FIELD32(0x00080000) - -/* - * MAC_CSR15: NAV control. - */ -#define MAC_CSR15 0x303c - -/* - * TXRX control registers. - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * TXRX_CSR0: TX/RX configuration register. - * TSF_OFFSET: Default is 24. - * AUTO_TX_SEQ: 1: ASIC auto replace sequence nr in outgoing frame. - * DISABLE_RX: Disable Rx engine. - * DROP_CRC: Drop CRC error. - * DROP_PHYSICAL: Drop physical error. - * DROP_CONTROL: Drop control frame. - * DROP_NOT_TO_ME: Drop not to me unicast frame. - * DROP_TO_DS: Drop fram ToDs bit is true. - * DROP_VERSION_ERROR: Drop version error frame. - * DROP_MULTICAST: Drop multicast frames. - * DROP_BORADCAST: Drop broadcast frames. - * DROP_ACK_CTS: Drop received ACK and CTS. - */ -#define TXRX_CSR0 0x3040 -#define TXRX_CSR0_RX_ACK_TIMEOUT FIELD32(0x000001ff) -#define TXRX_CSR0_TSF_OFFSET FIELD32(0x00007e00) -#define TXRX_CSR0_AUTO_TX_SEQ FIELD32(0x00008000) -#define TXRX_CSR0_DISABLE_RX FIELD32(0x00010000) -#define TXRX_CSR0_DROP_CRC FIELD32(0x00020000) -#define TXRX_CSR0_DROP_PHYSICAL FIELD32(0x00040000) -#define TXRX_CSR0_DROP_CONTROL FIELD32(0x00080000) -#define TXRX_CSR0_DROP_NOT_TO_ME FIELD32(0x00100000) -#define TXRX_CSR0_DROP_TO_DS FIELD32(0x00200000) -#define TXRX_CSR0_DROP_VERSION_ERROR FIELD32(0x00400000) -#define TXRX_CSR0_DROP_MULTICAST FIELD32(0x00800000) -#define TXRX_CSR0_DROP_BROADCAST FIELD32(0x01000000) -#define TXRX_CSR0_DROP_ACK_CTS FIELD32(0x02000000) -#define TXRX_CSR0_TX_WITHOUT_WAITING FIELD32(0x04000000) - -/* - * TXRX_CSR1 - */ -#define TXRX_CSR1 0x3044 -#define TXRX_CSR1_BBP_ID0 FIELD32(0x0000007f) -#define TXRX_CSR1_BBP_ID0_VALID FIELD32(0x00000080) -#define TXRX_CSR1_BBP_ID1 FIELD32(0x00007f00) -#define TXRX_CSR1_BBP_ID1_VALID FIELD32(0x00008000) -#define TXRX_CSR1_BBP_ID2 FIELD32(0x007f0000) -#define TXRX_CSR1_BBP_ID2_VALID FIELD32(0x00800000) -#define TXRX_CSR1_BBP_ID3 FIELD32(0x7f000000) -#define TXRX_CSR1_BBP_ID3_VALID FIELD32(0x80000000) - -/* - * TXRX_CSR2 - */ -#define TXRX_CSR2 0x3048 -#define TXRX_CSR2_BBP_ID0 FIELD32(0x0000007f) -#define TXRX_CSR2_BBP_ID0_VALID FIELD32(0x00000080) -#define TXRX_CSR2_BBP_ID1 FIELD32(0x00007f00) -#define TXRX_CSR2_BBP_ID1_VALID FIELD32(0x00008000) -#define TXRX_CSR2_BBP_ID2 FIELD32(0x007f0000) -#define TXRX_CSR2_BBP_ID2_VALID FIELD32(0x00800000) -#define TXRX_CSR2_BBP_ID3 FIELD32(0x7f000000) -#define TXRX_CSR2_BBP_ID3_VALID FIELD32(0x80000000) - -/* - * TXRX_CSR3 - */ -#define TXRX_CSR3 0x304c -#define TXRX_CSR3_BBP_ID0 FIELD32(0x0000007f) -#define TXRX_CSR3_BBP_ID0_VALID FIELD32(0x00000080) -#define TXRX_CSR3_BBP_ID1 FIELD32(0x00007f00) -#define TXRX_CSR3_BBP_ID1_VALID FIELD32(0x00008000) -#define TXRX_CSR3_BBP_ID2 FIELD32(0x007f0000) -#define TXRX_CSR3_BBP_ID2_VALID FIELD32(0x00800000) -#define TXRX_CSR3_BBP_ID3 FIELD32(0x7f000000) -#define TXRX_CSR3_BBP_ID3_VALID FIELD32(0x80000000) - -/* - * TXRX_CSR4: Auto-Responder/Tx-retry register. - * AUTORESPOND_PREAMBLE: 0:long, 1:short preamble. - * OFDM_TX_RATE_DOWN: 1:enable. - * OFDM_TX_RATE_STEP: 0:1-step, 1: 2-step, 2:3-step, 3:4-step. - * OFDM_TX_FALLBACK_CCK: 0: Fallback to OFDM 6M only, 1: Fallback to CCK 1M,2M. - */ -#define TXRX_CSR4 0x3050 -#define TXRX_CSR4_TX_ACK_TIMEOUT FIELD32(0x000000ff) -#define TXRX_CSR4_CNTL_ACK_POLICY FIELD32(0x00000700) -#define TXRX_CSR4_ACK_CTS_PSM FIELD32(0x00010000) -#define TXRX_CSR4_AUTORESPOND_ENABLE FIELD32(0x00020000) -#define TXRX_CSR4_AUTORESPOND_PREAMBLE FIELD32(0x00040000) -#define TXRX_CSR4_OFDM_TX_RATE_DOWN FIELD32(0x00080000) -#define TXRX_CSR4_OFDM_TX_RATE_STEP FIELD32(0x00300000) -#define TXRX_CSR4_OFDM_TX_FALLBACK_CCK FIELD32(0x00400000) -#define TXRX_CSR4_LONG_RETRY_LIMIT FIELD32(0x0f000000) -#define TXRX_CSR4_SHORT_RETRY_LIMIT FIELD32(0xf0000000) - -/* - * TXRX_CSR5 - */ -#define TXRX_CSR5 0x3054 - -/* - * TXRX_CSR6: ACK/CTS payload consumed time - */ -#define TXRX_CSR6 0x3058 - -/* - * TXRX_CSR7: OFDM ACK/CTS payload consumed time for 6/9/12/18 mbps. - */ -#define TXRX_CSR7 0x305c -#define TXRX_CSR7_ACK_CTS_6MBS FIELD32(0x000000ff) -#define TXRX_CSR7_ACK_CTS_9MBS FIELD32(0x0000ff00) -#define TXRX_CSR7_ACK_CTS_12MBS FIELD32(0x00ff0000) -#define TXRX_CSR7_ACK_CTS_18MBS FIELD32(0xff000000) - -/* - * TXRX_CSR8: OFDM ACK/CTS payload consumed time for 24/36/48/54 mbps. - */ -#define TXRX_CSR8 0x3060 -#define TXRX_CSR8_ACK_CTS_24MBS FIELD32(0x000000ff) -#define TXRX_CSR8_ACK_CTS_36MBS FIELD32(0x0000ff00) -#define TXRX_CSR8_ACK_CTS_48MBS FIELD32(0x00ff0000) -#define TXRX_CSR8_ACK_CTS_54MBS FIELD32(0xff000000) - -/* - * TXRX_CSR9: Synchronization control register. - * BEACON_INTERVAL: In unit of 1/16 TU. - * TSF_TICKING: Enable TSF auto counting. - * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. - * BEACON_GEN: Enable beacon generator. - */ -#define TXRX_CSR9 0x3064 -#define TXRX_CSR9_BEACON_INTERVAL FIELD32(0x0000ffff) -#define TXRX_CSR9_TSF_TICKING FIELD32(0x00010000) -#define TXRX_CSR9_TSF_SYNC FIELD32(0x00060000) -#define TXRX_CSR9_TBTT_ENABLE FIELD32(0x00080000) -#define TXRX_CSR9_BEACON_GEN FIELD32(0x00100000) -#define TXRX_CSR9_TIMESTAMP_COMPENSATE FIELD32(0xff000000) - -/* - * TXRX_CSR10: BEACON alignment. - */ -#define TXRX_CSR10 0x3068 - -/* - * TXRX_CSR11: AES mask. - */ -#define TXRX_CSR11 0x306c - -/* - * TXRX_CSR12: TSF low 32. - */ -#define TXRX_CSR12 0x3070 -#define TXRX_CSR12_LOW_TSFTIMER FIELD32(0xffffffff) - -/* - * TXRX_CSR13: TSF high 32. - */ -#define TXRX_CSR13 0x3074 -#define TXRX_CSR13_HIGH_TSFTIMER FIELD32(0xffffffff) - -/* - * TXRX_CSR14: TBTT timer. - */ -#define TXRX_CSR14 0x3078 - -/* - * TXRX_CSR15: TKIP MIC priority byte "AND" mask. - */ -#define TXRX_CSR15 0x307c - -/* - * PHY control registers. - * Some values are set in TU, whereas 1 TU == 1024 us. - */ - -/* - * PHY_CSR0: RF/PS control. - */ -#define PHY_CSR0 0x3080 -#define PHY_CSR0_PA_PE_BG FIELD32(0x00010000) -#define PHY_CSR0_PA_PE_A FIELD32(0x00020000) - -/* - * PHY_CSR1 - */ -#define PHY_CSR1 0x3084 -#define PHY_CSR1_RF_RPI FIELD32(0x00010000) - -/* - * PHY_CSR2: Pre-TX BBP control. - */ -#define PHY_CSR2 0x3088 - -/* - * PHY_CSR3: BBP serial control register. - * VALUE: Register value to program into BBP. - * REG_NUM: Selected BBP register. - * READ_CONTROL: 0: Write BBP, 1: Read BBP. - * BUSY: 1: ASIC is busy execute BBP programming. - */ -#define PHY_CSR3 0x308c -#define PHY_CSR3_VALUE FIELD32(0x000000ff) -#define PHY_CSR3_REGNUM FIELD32(0x00007f00) -#define PHY_CSR3_READ_CONTROL FIELD32(0x00008000) -#define PHY_CSR3_BUSY FIELD32(0x00010000) - -/* - * PHY_CSR4: RF serial control register - * VALUE: Register value (include register id) serial out to RF/IF chip. - * NUMBER_OF_BITS: Number of bits used in RFRegValue (I:20, RFMD:22). - * IF_SELECT: 1: select IF to program, 0: select RF to program. - * PLL_LD: RF PLL_LD status. - * BUSY: 1: ASIC is busy execute RF programming. - */ -#define PHY_CSR4 0x3090 -#define PHY_CSR4_VALUE FIELD32(0x00ffffff) -#define PHY_CSR4_NUMBER_OF_BITS FIELD32(0x1f000000) -#define PHY_CSR4_IF_SELECT FIELD32(0x20000000) -#define PHY_CSR4_PLL_LD FIELD32(0x40000000) -#define PHY_CSR4_BUSY FIELD32(0x80000000) - -/* - * PHY_CSR5: RX to TX signal switch timing control. - */ -#define PHY_CSR5 0x3094 -#define PHY_CSR5_IQ_FLIP FIELD32(0x00000004) - -/* - * PHY_CSR6: TX to RX signal timing control. - */ -#define PHY_CSR6 0x3098 -#define PHY_CSR6_IQ_FLIP FIELD32(0x00000004) - -/* - * PHY_CSR7: TX DAC switching timing control. - */ -#define PHY_CSR7 0x309c - -/* - * Security control register. - */ - -/* - * SEC_CSR0: Shared key table control. - */ -#define SEC_CSR0 0x30a0 -#define SEC_CSR0_BSS0_KEY0_VALID FIELD32(0x00000001) -#define SEC_CSR0_BSS0_KEY1_VALID FIELD32(0x00000002) -#define SEC_CSR0_BSS0_KEY2_VALID FIELD32(0x00000004) -#define SEC_CSR0_BSS0_KEY3_VALID FIELD32(0x00000008) -#define SEC_CSR0_BSS1_KEY0_VALID FIELD32(0x00000010) -#define SEC_CSR0_BSS1_KEY1_VALID FIELD32(0x00000020) -#define SEC_CSR0_BSS1_KEY2_VALID FIELD32(0x00000040) -#define SEC_CSR0_BSS1_KEY3_VALID FIELD32(0x00000080) -#define SEC_CSR0_BSS2_KEY0_VALID FIELD32(0x00000100) -#define SEC_CSR0_BSS2_KEY1_VALID FIELD32(0x00000200) -#define SEC_CSR0_BSS2_KEY2_VALID FIELD32(0x00000400) -#define SEC_CSR0_BSS2_KEY3_VALID FIELD32(0x00000800) -#define SEC_CSR0_BSS3_KEY0_VALID FIELD32(0x00001000) -#define SEC_CSR0_BSS3_KEY1_VALID FIELD32(0x00002000) -#define SEC_CSR0_BSS3_KEY2_VALID FIELD32(0x00004000) -#define SEC_CSR0_BSS3_KEY3_VALID FIELD32(0x00008000) - -/* - * SEC_CSR1: Shared key table security mode register. - */ -#define SEC_CSR1 0x30a4 -#define SEC_CSR1_BSS0_KEY0_CIPHER_ALG FIELD32(0x00000007) -#define SEC_CSR1_BSS0_KEY1_CIPHER_ALG FIELD32(0x00000070) -#define SEC_CSR1_BSS0_KEY2_CIPHER_ALG FIELD32(0x00000700) -#define SEC_CSR1_BSS0_KEY3_CIPHER_ALG FIELD32(0x00007000) -#define SEC_CSR1_BSS1_KEY0_CIPHER_ALG FIELD32(0x00070000) -#define SEC_CSR1_BSS1_KEY1_CIPHER_ALG FIELD32(0x00700000) -#define SEC_CSR1_BSS1_KEY2_CIPHER_ALG FIELD32(0x07000000) -#define SEC_CSR1_BSS1_KEY3_CIPHER_ALG FIELD32(0x70000000) - -/* - * Pairwise key table valid bitmap registers. - * SEC_CSR2: pairwise key table valid bitmap 0. - * SEC_CSR3: pairwise key table valid bitmap 1. - */ -#define SEC_CSR2 0x30a8 -#define SEC_CSR3 0x30ac - -/* - * SEC_CSR4: Pairwise key table lookup control. - */ -#define SEC_CSR4 0x30b0 -#define SEC_CSR4_ENABLE_BSS0 FIELD32(0x00000001) -#define SEC_CSR4_ENABLE_BSS1 FIELD32(0x00000002) -#define SEC_CSR4_ENABLE_BSS2 FIELD32(0x00000004) -#define SEC_CSR4_ENABLE_BSS3 FIELD32(0x00000008) - -/* - * SEC_CSR5: shared key table security mode register. - */ -#define SEC_CSR5 0x30b4 -#define SEC_CSR5_BSS2_KEY0_CIPHER_ALG FIELD32(0x00000007) -#define SEC_CSR5_BSS2_KEY1_CIPHER_ALG FIELD32(0x00000070) -#define SEC_CSR5_BSS2_KEY2_CIPHER_ALG FIELD32(0x00000700) -#define SEC_CSR5_BSS2_KEY3_CIPHER_ALG FIELD32(0x00007000) -#define SEC_CSR5_BSS3_KEY0_CIPHER_ALG FIELD32(0x00070000) -#define SEC_CSR5_BSS3_KEY1_CIPHER_ALG FIELD32(0x00700000) -#define SEC_CSR5_BSS3_KEY2_CIPHER_ALG FIELD32(0x07000000) -#define SEC_CSR5_BSS3_KEY3_CIPHER_ALG FIELD32(0x70000000) - -/* - * STA control registers. - */ - -/* - * STA_CSR0: RX PLCP error count & RX FCS error count. - */ -#define STA_CSR0 0x30c0 -#define STA_CSR0_FCS_ERROR FIELD32(0x0000ffff) -#define STA_CSR0_PLCP_ERROR FIELD32(0xffff0000) - -/* - * STA_CSR1: RX False CCA count & RX LONG frame count. - */ -#define STA_CSR1 0x30c4 -#define STA_CSR1_PHYSICAL_ERROR FIELD32(0x0000ffff) -#define STA_CSR1_FALSE_CCA_ERROR FIELD32(0xffff0000) - -/* - * STA_CSR2: TX Beacon count and RX FIFO overflow count. - */ -#define STA_CSR2 0x30c8 -#define STA_CSR2_RX_FIFO_OVERFLOW_COUNT FIELD32(0x0000ffff) -#define STA_CSR2_RX_OVERFLOW_COUNT FIELD32(0xffff0000) - -/* - * STA_CSR3: TX Beacon count. - */ -#define STA_CSR3 0x30cc -#define STA_CSR3_TX_BEACON_COUNT FIELD32(0x0000ffff) - -/* - * STA_CSR4: TX Retry count. - */ -#define STA_CSR4 0x30d0 -#define STA_CSR4_TX_NO_RETRY_COUNT FIELD32(0x0000ffff) -#define STA_CSR4_TX_ONE_RETRY_COUNT FIELD32(0xffff0000) - -/* - * STA_CSR5: TX Retry count. - */ -#define STA_CSR5 0x30d4 -#define STA_CSR4_TX_MULTI_RETRY_COUNT FIELD32(0x0000ffff) -#define STA_CSR4_TX_RETRY_FAIL_COUNT FIELD32(0xffff0000) - -/* - * QOS control registers. - */ - -/* - * QOS_CSR1: TXOP holder MAC address register. - */ -#define QOS_CSR1 0x30e4 -#define QOS_CSR1_BYTE4 FIELD32(0x000000ff) -#define QOS_CSR1_BYTE5 FIELD32(0x0000ff00) - -/* - * QOS_CSR2: TXOP holder timeout register. - */ -#define QOS_CSR2 0x30e8 - -/* - * RX QOS-CFPOLL MAC address register. - * QOS_CSR3: RX QOS-CFPOLL MAC address 0. - * QOS_CSR4: RX QOS-CFPOLL MAC address 1. - */ -#define QOS_CSR3 0x30ec -#define QOS_CSR4 0x30f0 - -/* - * QOS_CSR5: "QosControl" field of the RX QOS-CFPOLL. - */ -#define QOS_CSR5 0x30f4 - -/* - * WMM Scheduler Register - */ - -/* - * AIFSN_CSR: AIFSN for each EDCA AC. - * AIFSN0: For AC_VO. - * AIFSN1: For AC_VI. - * AIFSN2: For AC_BE. - * AIFSN3: For AC_BK. - */ -#define AIFSN_CSR 0x0400 -#define AIFSN_CSR_AIFSN0 FIELD32(0x0000000f) -#define AIFSN_CSR_AIFSN1 FIELD32(0x000000f0) -#define AIFSN_CSR_AIFSN2 FIELD32(0x00000f00) -#define AIFSN_CSR_AIFSN3 FIELD32(0x0000f000) - -/* - * CWMIN_CSR: CWmin for each EDCA AC. - * CWMIN0: For AC_VO. - * CWMIN1: For AC_VI. - * CWMIN2: For AC_BE. - * CWMIN3: For AC_BK. - */ -#define CWMIN_CSR 0x0404 -#define CWMIN_CSR_CWMIN0 FIELD32(0x0000000f) -#define CWMIN_CSR_CWMIN1 FIELD32(0x000000f0) -#define CWMIN_CSR_CWMIN2 FIELD32(0x00000f00) -#define CWMIN_CSR_CWMIN3 FIELD32(0x0000f000) - -/* - * CWMAX_CSR: CWmax for each EDCA AC. - * CWMAX0: For AC_VO. - * CWMAX1: For AC_VI. - * CWMAX2: For AC_BE. - * CWMAX3: For AC_BK. - */ -#define CWMAX_CSR 0x0408 -#define CWMAX_CSR_CWMAX0 FIELD32(0x0000000f) -#define CWMAX_CSR_CWMAX1 FIELD32(0x000000f0) -#define CWMAX_CSR_CWMAX2 FIELD32(0x00000f00) -#define CWMAX_CSR_CWMAX3 FIELD32(0x0000f000) - -/* - * AC_TXOP_CSR0: AC_VO/AC_VI TXOP register. - * AC0_TX_OP: For AC_VO, in unit of 32us. - * AC1_TX_OP: For AC_VI, in unit of 32us. - */ -#define AC_TXOP_CSR0 0x040c -#define AC_TXOP_CSR0_AC0_TX_OP FIELD32(0x0000ffff) -#define AC_TXOP_CSR0_AC1_TX_OP FIELD32(0xffff0000) - -/* - * AC_TXOP_CSR1: AC_BE/AC_BK TXOP register. - * AC2_TX_OP: For AC_BE, in unit of 32us. - * AC3_TX_OP: For AC_BK, in unit of 32us. - */ -#define AC_TXOP_CSR1 0x0410 -#define AC_TXOP_CSR1_AC2_TX_OP FIELD32(0x0000ffff) -#define AC_TXOP_CSR1_AC3_TX_OP FIELD32(0xffff0000) - -/* - * BBP registers. - * The wordsize of the BBP is 8 bits. - */ - -/* - * R2 - */ -#define BBP_R2_BG_MODE FIELD8(0x20) - -/* - * R3 - */ -#define BBP_R3_SMART_MODE FIELD8(0x01) - -/* - * R4: RX antenna control - * FRAME_END: 1 - DPDT, 0 - SPDT (Only valid for 802.11G, RF2527 & RF2529) - */ - -/* - * ANTENNA_CONTROL semantics (guessed): - * 0x1: Software controlled antenna switching (fixed or SW diversity) - * 0x2: Hardware diversity. - */ -#define BBP_R4_RX_ANTENNA_CONTROL FIELD8(0x03) -#define BBP_R4_RX_FRAME_END FIELD8(0x20) - -/* - * R77 - */ -#define BBP_R77_RX_ANTENNA FIELD8(0x03) - -/* - * RF registers - */ - -/* - * RF 3 - */ -#define RF3_TXPOWER FIELD32(0x00003e00) - -/* - * RF 4 - */ -#define RF4_FREQ_OFFSET FIELD32(0x0003f000) - -/* - * EEPROM content. - * The wordsize of the EEPROM is 16 bits. - */ - -/* - * HW MAC address. - */ -#define EEPROM_MAC_ADDR_0 0x0002 -#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) -#define EEPROM_MAC_ADDR1 0x0003 -#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_2 0x0004 -#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) -#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) - -/* - * EEPROM antenna. - * ANTENNA_NUM: Number of antennas. - * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. - * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. - * FRAME_TYPE: 0: DPDT , 1: SPDT , noted this bit is valid for g only. - * DYN_TXAGC: Dynamic TX AGC control. - * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. - * RF_TYPE: Rf_type of this adapter. - */ -#define EEPROM_ANTENNA 0x0010 -#define EEPROM_ANTENNA_NUM FIELD16(0x0003) -#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) -#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) -#define EEPROM_ANTENNA_FRAME_TYPE FIELD16(0x0040) -#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) -#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) -#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) - -/* - * EEPROM NIC config. - * EXTERNAL_LNA: External LNA. - */ -#define EEPROM_NIC 0x0011 -#define EEPROM_NIC_EXTERNAL_LNA FIELD16(0x0010) - -/* - * EEPROM geography. - * GEO_A: Default geographical setting for 5GHz band - * GEO: Default geographical setting. - */ -#define EEPROM_GEOGRAPHY 0x0012 -#define EEPROM_GEOGRAPHY_GEO_A FIELD16(0x00ff) -#define EEPROM_GEOGRAPHY_GEO FIELD16(0xff00) - -/* - * EEPROM BBP. - */ -#define EEPROM_BBP_START 0x0013 -#define EEPROM_BBP_SIZE 16 -#define EEPROM_BBP_VALUE FIELD16(0x00ff) -#define EEPROM_BBP_REG_ID FIELD16(0xff00) - -/* - * EEPROM TXPOWER 802.11G - */ -#define EEPROM_TXPOWER_G_START 0x0023 -#define EEPROM_TXPOWER_G_SIZE 7 -#define EEPROM_TXPOWER_G_1 FIELD16(0x00ff) -#define EEPROM_TXPOWER_G_2 FIELD16(0xff00) - -/* - * EEPROM Frequency - */ -#define EEPROM_FREQ 0x002f -#define EEPROM_FREQ_OFFSET FIELD16(0x00ff) -#define EEPROM_FREQ_SEQ_MASK FIELD16(0xff00) -#define EEPROM_FREQ_SEQ FIELD16(0x0300) - -/* - * EEPROM LED. - * POLARITY_RDY_G: Polarity RDY_G setting. - * POLARITY_RDY_A: Polarity RDY_A setting. - * POLARITY_ACT: Polarity ACT setting. - * POLARITY_GPIO_0: Polarity GPIO0 setting. - * POLARITY_GPIO_1: Polarity GPIO1 setting. - * POLARITY_GPIO_2: Polarity GPIO2 setting. - * POLARITY_GPIO_3: Polarity GPIO3 setting. - * POLARITY_GPIO_4: Polarity GPIO4 setting. - * LED_MODE: Led mode. - */ -#define EEPROM_LED 0x0030 -#define EEPROM_LED_POLARITY_RDY_G FIELD16(0x0001) -#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) -#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) -#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) -#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) -#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) -#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) -#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) -#define EEPROM_LED_LED_MODE FIELD16(0x1f00) - -/* - * EEPROM TXPOWER 802.11A - */ -#define EEPROM_TXPOWER_A_START 0x0031 -#define EEPROM_TXPOWER_A_SIZE 12 -#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) -#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) - -/* - * EEPROM RSSI offset 802.11BG - */ -#define EEPROM_RSSI_OFFSET_BG 0x004d -#define EEPROM_RSSI_OFFSET_BG_1 FIELD16(0x00ff) -#define EEPROM_RSSI_OFFSET_BG_2 FIELD16(0xff00) - -/* - * EEPROM RSSI offset 802.11A - */ -#define EEPROM_RSSI_OFFSET_A 0x004e -#define EEPROM_RSSI_OFFSET_A_1 FIELD16(0x00ff) -#define EEPROM_RSSI_OFFSET_A_2 FIELD16(0xff00) - -/* - * DMA descriptor defines. - */ -#define TXD_DESC_SIZE ( 6 * sizeof(__le32) ) -#define TXINFO_SIZE ( 6 * sizeof(__le32) ) -#define RXD_DESC_SIZE ( 6 * sizeof(__le32) ) - -/* - * TX descriptor format for TX, PRIO and Beacon Ring. - */ - -/* - * Word0 - * BURST: Next frame belongs to same "burst" event. - * TKIP_MIC: ASIC appends TKIP MIC if TKIP is used. - * KEY_TABLE: Use per-client pairwise KEY table. - * KEY_INDEX: - * Key index (0~31) to the pairwise KEY table. - * 0~3 to shared KEY table 0 (BSS0). - * 4~7 to shared KEY table 1 (BSS1). - * 8~11 to shared KEY table 2 (BSS2). - * 12~15 to shared KEY table 3 (BSS3). - * BURST2: For backward compatibility, set to same value as BURST. - */ -#define TXD_W0_BURST FIELD32(0x00000001) -#define TXD_W0_VALID FIELD32(0x00000002) -#define TXD_W0_MORE_FRAG FIELD32(0x00000004) -#define TXD_W0_ACK FIELD32(0x00000008) -#define TXD_W0_TIMESTAMP FIELD32(0x00000010) -#define TXD_W0_OFDM FIELD32(0x00000020) -#define TXD_W0_IFS FIELD32(0x00000040) -#define TXD_W0_RETRY_MODE FIELD32(0x00000080) -#define TXD_W0_TKIP_MIC FIELD32(0x00000100) -#define TXD_W0_KEY_TABLE FIELD32(0x00000200) -#define TXD_W0_KEY_INDEX FIELD32(0x0000fc00) -#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) -#define TXD_W0_BURST2 FIELD32(0x10000000) -#define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) - -/* - * Word1 - * HOST_Q_ID: EDCA/HCCA queue ID. - * HW_SEQUENCE: MAC overwrites the frame sequence number. - * BUFFER_COUNT: Number of buffers in this TXD. - */ -#define TXD_W1_HOST_Q_ID FIELD32(0x0000000f) -#define TXD_W1_AIFSN FIELD32(0x000000f0) -#define TXD_W1_CWMIN FIELD32(0x00000f00) -#define TXD_W1_CWMAX FIELD32(0x0000f000) -#define TXD_W1_IV_OFFSET FIELD32(0x003f0000) -#define TXD_W1_HW_SEQUENCE FIELD32(0x10000000) -#define TXD_W1_BUFFER_COUNT FIELD32(0xe0000000) - -/* - * Word2: PLCP information - */ -#define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) -#define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) -#define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) -#define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) - -/* - * Word3 - */ -#define TXD_W3_IV FIELD32(0xffffffff) - -/* - * Word4 - */ -#define TXD_W4_EIV FIELD32(0xffffffff) - -/* - * Word5 - * FRAME_OFFSET: Frame start offset inside ASIC TXFIFO (after TXINFO field). - * PACKET_ID: Driver assigned packet ID to categorize TXResult in interrupt. - * WAITING_DMA_DONE_INT: TXD been filled with data - * and waiting for TxDoneISR housekeeping. - */ -#define TXD_W5_FRAME_OFFSET FIELD32(0x000000ff) -#define TXD_W5_PACKET_ID FIELD32(0x0000ff00) -#define TXD_W5_TX_POWER FIELD32(0x00ff0000) -#define TXD_W5_WAITING_DMA_DONE_INT FIELD32(0x01000000) - -/* - * RX descriptor format for RX Ring. - */ - -/* - * Word0 - * CIPHER_ERROR: 1:ICV error, 2:MIC error, 3:invalid key. - * KEY_INDEX: Decryption key actually used. - */ -#define RXD_W0_OWNER_NIC FIELD32(0x00000001) -#define RXD_W0_DROP FIELD32(0x00000002) -#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000004) -#define RXD_W0_MULTICAST FIELD32(0x00000008) -#define RXD_W0_BROADCAST FIELD32(0x00000010) -#define RXD_W0_MY_BSS FIELD32(0x00000020) -#define RXD_W0_CRC_ERROR FIELD32(0x00000040) -#define RXD_W0_OFDM FIELD32(0x00000080) -#define RXD_W0_CIPHER_ERROR FIELD32(0x00000300) -#define RXD_W0_KEY_INDEX FIELD32(0x0000fc00) -#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) -#define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) - -/* - * WORD1 - * SIGNAL: RX raw data rate reported by BBP. - * RSSI: RSSI reported by BBP. - */ -#define RXD_W1_SIGNAL FIELD32(0x000000ff) -#define RXD_W1_RSSI_AGC FIELD32(0x00001f00) -#define RXD_W1_RSSI_LNA FIELD32(0x00006000) -#define RXD_W1_FRAME_OFFSET FIELD32(0x7f000000) - -/* - * Word2 - * IV: Received IV of originally encrypted. - */ -#define RXD_W2_IV FIELD32(0xffffffff) - -/* - * Word3 - * EIV: Received EIV of originally encrypted. - */ -#define RXD_W3_EIV FIELD32(0xffffffff) - -/* - * Word4 - * ICV: Received ICV of originally encrypted. - * NOTE: This is a guess, the official definition is "reserved" - */ -#define RXD_W4_ICV FIELD32(0xffffffff) - -/* - * the above 20-byte is called RXINFO and will be DMAed to MAC RX block - * and passed to the HOST driver. - * The following fields are for DMA block and HOST usage only. - * Can't be touched by ASIC MAC block. - */ - -/* - * Word5 - */ -#define RXD_W5_RESERVED FIELD32(0xffffffff) - -/* - * Macros for converting txpower from EEPROM to mac80211 value - * and from mac80211 value to register value. - */ -#define MIN_TXPOWER 0 -#define MAX_TXPOWER 31 -#define DEFAULT_TXPOWER 24 - -#define TXPOWER_FROM_DEV(__txpower) \ - (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) - -#define TXPOWER_TO_DEV(__txpower) \ - clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) - -#endif /* RT73USB_H */ -- cgit v1.2.3-59-g8ed1b From 7b09ed5a09532d566c34f941bbcb2a813ef97afb Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 10:23:52 +0200 Subject: mediatek: unify Kconfig with other vendors Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- drivers/net/wireless/Kconfig | 2 +- drivers/net/wireless/Makefile | 3 +-- drivers/net/wireless/mediatek/Kconfig | 16 ++++++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index c58e244b5079..2225e06611fa 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -24,6 +24,7 @@ source "drivers/net/wireless/cisco/Kconfig" source "drivers/net/wireless/intel/Kconfig" source "drivers/net/wireless/intersil/Kconfig" source "drivers/net/wireless/marvell/Kconfig" +source "drivers/net/wireless/mediatek/Kconfig" source "drivers/net/wireless/ralink/Kconfig" source "drivers/net/wireless/realtek/Kconfig" source "drivers/net/wireless/rsi/Kconfig" @@ -98,7 +99,6 @@ config MAC80211_HWSIM called mac80211_hwsim. If unsure, say N. source "drivers/net/wireless/ath/Kconfig" -source "drivers/net/wireless/mediatek/Kconfig" source "drivers/net/wireless/ti/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 8ac72fd44d17..0fa909072846 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/ obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ +obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/ obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/ obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/ obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/ @@ -21,8 +22,6 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o -obj-$(CONFIG_WL_MEDIATEK) += mediatek/ - obj-$(CONFIG_ATH_CARDS) += ath/ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o diff --git a/drivers/net/wireless/mediatek/Kconfig b/drivers/net/wireless/mediatek/Kconfig index cba300c6b5da..28843fed750a 100644 --- a/drivers/net/wireless/mediatek/Kconfig +++ b/drivers/net/wireless/mediatek/Kconfig @@ -1,10 +1,14 @@ -menuconfig WL_MEDIATEK - bool "Mediatek Wireless LAN support" +config WLAN_VENDOR_MEDIATEK + bool "MediaTek devices" + default y ---help--- - Enable community drivers for MediaTek WiFi devices. - Those drivers make use of the Linux mac80211 stack. + If you have a wireless card belonging to this class, say Y. + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. -if WL_MEDIATEK +if WLAN_VENDOR_MEDIATEK source "drivers/net/wireless/mediatek/mt7601u/Kconfig" -endif # WL_MEDIATEK +endif # WLAN_VENDOR_MEDIATEK -- cgit v1.2.3-59-g8ed1b From bd842171a8b7af696eece7157a988b7726ed641c Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 10:27:43 +0200 Subject: ti: unify Kconfig with other vendors Rename WL_TI to WLAN_VENDOR_TI to match with other vendor configs and make sure that it's enabled by default in new configs. Convert menuconfigs to regular configs to unify the wireless drivers menuconfig. Part of reorganising wireless drivers directory and Kconfig. Also remove WLCORE dependency to WL_TI. It should not be needed as WLCORE is already under if WLAN_VENDOR_TI. Signed-off-by: Kalle Valo --- drivers/net/wireless/Kconfig | 2 +- drivers/net/wireless/Makefile | 3 +-- drivers/net/wireless/ti/Kconfig | 18 +++++++++++------- drivers/net/wireless/ti/wl1251/Kconfig | 2 +- drivers/net/wireless/ti/wlcore/Kconfig | 2 +- 5 files changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 2225e06611fa..cb115db309ce 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -29,6 +29,7 @@ source "drivers/net/wireless/ralink/Kconfig" source "drivers/net/wireless/realtek/Kconfig" source "drivers/net/wireless/rsi/Kconfig" source "drivers/net/wireless/st/Kconfig" +source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/zydas/Kconfig" config PCMCIA_RAYCS @@ -99,6 +100,5 @@ config MAC80211_HWSIM called mac80211_hwsim. If unsure, say N. source "drivers/net/wireless/ath/Kconfig" -source "drivers/net/wireless/ti/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 0fa909072846..8c99b9faab09 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/ obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/ obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ +obj-$(CONFIG_WLAN_VENDOR_TI) += ti/ obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ # 16-bit wireless PCMCIA client drivers @@ -25,5 +26,3 @@ obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o obj-$(CONFIG_ATH_CARDS) += ath/ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o - -obj-$(CONFIG_WL_TI) += ti/ diff --git a/drivers/net/wireless/ti/Kconfig b/drivers/net/wireless/ti/Kconfig index cbe1e7fef61b..92fbd6597e34 100644 --- a/drivers/net/wireless/ti/Kconfig +++ b/drivers/net/wireless/ti/Kconfig @@ -1,11 +1,15 @@ -menuconfig WL_TI - bool "TI Wireless LAN support" +config WLAN_VENDOR_TI + bool "Texas Instrument devices" + default y ---help--- - This section contains support for all the wireless drivers - for Texas Instruments WLAN chips, such as wl1251 and the wl12xx - family. + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. -if WL_TI +if WLAN_VENDOR_TI source "drivers/net/wireless/ti/wl1251/Kconfig" source "drivers/net/wireless/ti/wl12xx/Kconfig" source "drivers/net/wireless/ti/wl18xx/Kconfig" @@ -21,4 +25,4 @@ config WILINK_PLATFORM_DATA Small platform data bit needed to pass data to the sdio modules. -endif # WL_TI +endif # WLAN_VENDOR_TI diff --git a/drivers/net/wireless/ti/wl1251/Kconfig b/drivers/net/wireless/ti/wl1251/Kconfig index 477a206c098e..7142ccf3a425 100644 --- a/drivers/net/wireless/ti/wl1251/Kconfig +++ b/drivers/net/wireless/ti/wl1251/Kconfig @@ -1,4 +1,4 @@ -menuconfig WL1251 +config WL1251 tristate "TI wl1251 driver support" depends on MAC80211 select FW_LOADER diff --git a/drivers/net/wireless/ti/wlcore/Kconfig b/drivers/net/wireless/ti/wlcore/Kconfig index 7c099542b214..969c9d79bfc8 100644 --- a/drivers/net/wireless/ti/wlcore/Kconfig +++ b/drivers/net/wireless/ti/wlcore/Kconfig @@ -1,6 +1,6 @@ config WLCORE tristate "TI wlcore support" - depends on WL_TI && MAC80211 + depends on MAC80211 select FW_LOADER ---help--- This module contains the main code for TI WLAN chips. It abstracts -- cgit v1.2.3-59-g8ed1b From b5c9b4f91a6f91cdbf777e6f365d56debe500421 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 10:38:32 +0200 Subject: ath: unify Kconfig with other vendors Change menuconfig to config to keep the Kconfig entries unified. Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- drivers/net/wireless/Kconfig | 3 +-- drivers/net/wireless/Makefile | 3 +-- drivers/net/wireless/ath/Kconfig | 17 ++++++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index cb115db309ce..3af6d0b9ca21 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -18,6 +18,7 @@ menuconfig WLAN if WLAN source "drivers/net/wireless/admtek/Kconfig" +source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/atmel/Kconfig" source "drivers/net/wireless/broadcom/Kconfig" source "drivers/net/wireless/cisco/Kconfig" @@ -99,6 +100,4 @@ config MAC80211_HWSIM To compile this driver as a module, choose M here: the module will be called mac80211_hwsim. If unsure, say N. -source "drivers/net/wireless/ath/Kconfig" - endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 8c99b9faab09..f00d42953fb8 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/ +obj-$(CONFIG_WLAN_VENDOR_ATH) += ath/ obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/ obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ @@ -23,6 +24,4 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o -obj-$(CONFIG_ATH_CARDS) += ath/ - obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index ce7826009eeb..44b2470af81d 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -1,13 +1,16 @@ config ATH_COMMON tristate -menuconfig ATH_CARDS - tristate "Atheros Wireless Cards" - depends on CFG80211 && (!UML || BROKEN) +config WLAN_VENDOR_ATH + bool "Atheros/Qualcomm devices" + default y ---help--- - This will enable the support for the Atheros wireless drivers. - ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option - enables the common ath.ko module which shares common helpers. + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. For more information and documentation on this module you can visit: @@ -17,7 +20,7 @@ menuconfig ATH_CARDS http://wireless.kernel.org/en/users/Drivers/Atheros -if ATH_CARDS +if WLAN_VENDOR_ATH config ATH_DEBUG bool "Atheros wireless debugging" -- cgit v1.2.3-59-g8ed1b From 6d808eba602b00f77f26191f45328774ff057cc0 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 18 Nov 2015 10:45:54 +0200 Subject: mac80211_hwsim: move Kconfig entry for sorting alphabetically mac80211_hwsim was not placed alphabetically correctly in menuconfig, fix that. Signed-off-by: Kalle Valo --- drivers/net/wireless/Kconfig | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 3af6d0b9ca21..8c8edaf1bba6 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -58,6 +58,19 @@ config PCMCIA_WL3501 It has basic support for Linux wireless extensions and initial micro support for ethtool. +config MAC80211_HWSIM + tristate "Simulated radio testing tool for mac80211" + depends on MAC80211 + ---help--- + This driver is a developer testing tool that can be used to test + IEEE 802.11 networking stack (mac80211) functionality. This is not + needed for normal wireless LAN usage and is only for testing. See + Documentation/networking/mac80211_hwsim for more information on how + to use this tool. + + To compile this driver as a module, choose M here: the module will be + called mac80211_hwsim. If unsure, say N. + config USB_NET_RNDIS_WLAN tristate "Wireless RNDIS USB support" depends on USB @@ -87,17 +100,4 @@ config USB_NET_RNDIS_WLAN If you choose to build a module, it'll be called rndis_wlan. -config MAC80211_HWSIM - tristate "Simulated radio testing tool for mac80211" - depends on MAC80211 - ---help--- - This driver is a developer testing tool that can be used to test - IEEE 802.11 networking stack (mac80211) functionality. This is not - needed for normal wireless LAN usage and is only for testing. See - Documentation/networking/mac80211_hwsim for more information on how - to use this tool. - - To compile this driver as a module, choose M here: the module will be - called mac80211_hwsim. If unsure, say N. - endif # WLAN -- cgit v1.2.3-59-g8ed1b From d37b4c0a3647db23f41c5ee85701eec3544446d1 Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Wed, 18 Nov 2015 14:06:34 +0100 Subject: be2net: remove local variable 'status' The lancer_cmd_get_file_len() uses lancer_cmd_read_object() to get the current size of registers for ethtool registers dump. Returned status value is stored but not checked. The check itself is not necessary as the data_read output variable is initialized to 0 and status variable can be removed. Signed-off-by: Ivan Vecera Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_ethtool.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 734f655c99c1..d2a5baf019ab 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -241,13 +241,11 @@ static u32 lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name) u32 data_read = 0, eof; u8 addn_status; struct be_dma_mem data_len_cmd; - int status; memset(&data_len_cmd, 0, sizeof(data_len_cmd)); /* data_offset and data_size should be 0 to get reg len */ - status = lancer_cmd_read_object(adapter, &data_len_cmd, 0, 0, - file_name, &data_read, &eof, - &addn_status); + lancer_cmd_read_object(adapter, &data_len_cmd, 0, 0, file_name, + &data_read, &eof, &addn_status); return data_read; } -- cgit v1.2.3-59-g8ed1b From 5865316c9db31e863d4baef04dc86957d7fdae7f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Nov 2015 06:30:51 -0800 Subject: mlx4: mlx4_en_low_latency_recv() called with BH disabled mlx4_en_low_latency_recv() is called with BH disabled, as other ndo_busy_poll() methods. No need for spin_lock_bh()/spin_unlock_bh() Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index c41f15102ae0..965c8f016ac4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -660,11 +660,12 @@ static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq) return rc; } -/* called from mlx4_en_low_latency_poll() */ +/* called from mlx4_en_low_latency_recv(), BH are disabled */ static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq) { int rc = true; - spin_lock_bh(&cq->poll_lock); + + spin_lock(&cq->poll_lock); if ((cq->state & MLX4_CQ_LOCKED)) { struct net_device *dev = cq->dev; struct mlx4_en_priv *priv = netdev_priv(dev); @@ -676,7 +677,7 @@ static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq) } else /* preserve yield marks */ cq->state |= MLX4_EN_CQ_STATE_POLL; - spin_unlock_bh(&cq->poll_lock); + spin_unlock(&cq->poll_lock); return rc; } @@ -684,13 +685,14 @@ static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq) static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq) { int rc = false; - spin_lock_bh(&cq->poll_lock); + + spin_lock(&cq->poll_lock); WARN_ON(cq->state & (MLX4_EN_CQ_STATE_NAPI)); if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD) rc = true; cq->state = MLX4_EN_CQ_STATE_IDLE; - spin_unlock_bh(&cq->poll_lock); + spin_unlock(&cq->poll_lock); return rc; } -- cgit v1.2.3-59-g8ed1b From 7ae92ae588c9f78006c106bb3398d50274c5d7de Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Nov 2015 06:30:55 -0800 Subject: mlx5: add busy polling support It is now easy to add busy polling support to a NAPI driver, with very little impact on normal input path. This patch serves as a reference implementation. Note: A followup patch will add proper napi_complete_done() in mlx5, so that LINUX_MIB_BUSYPOLLRXPACKETS snmp counter is properly handled. Tested: Normal TCP_RR results without busy polling : lpk51:~# echo 0 >/proc/sys/net/core/busy_read lpk52:~# echo 0 >/proc/sys/net/core/busy_read lpk51:~# ./netperf -H 192.168.4.52 -t TCP_RR -l 10 MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.4.52 () port 0 AF_INET : first burst 0 Local /Remote Socket Size Request Resp. Elapsed Trans. Send Recv Size Size Time Rate bytes Bytes bytes bytes secs. per sec 16384 87380 1 1 10.00 53509.49 16384 87380 Now enable busy polling : lpk51:~# echo 70 >/proc/sys/net/core/busy_read lpk52:~# echo 70 >/proc/sys/net/core/busy_read lpk51:~# ./netperf -H 192.168.4.52 -t TCP_RR -l 10 MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.4.52 () port 0 AF_INET : first burst 0 Local /Remote Socket Size Request Resp. Elapsed Trans. Send Recv Size Size Time Rate bytes Bytes bytes bytes secs. per sec 16384 87380 1 1 10.00 97530.92 16384 87380 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 6 ++++++ drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 2 ++ 2 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 1e52db32c73d..ffb1f9c1b973 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -982,6 +982,7 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, mlx5e_build_channeltc_to_txq_map(priv, ix); netif_napi_add(netdev, &c->napi, mlx5e_napi_poll, 64); + napi_hash_add(&c->napi); err = mlx5e_open_tx_cqs(c, cparam); if (err) @@ -1020,6 +1021,7 @@ err_close_tx_cqs: err_napi_del: netif_napi_del(&c->napi); + napi_hash_del(&c->napi); kfree(c); return err; @@ -1033,6 +1035,10 @@ static void mlx5e_close_channel(struct mlx5e_channel *c) mlx5e_close_cq(&c->rq.cq); mlx5e_close_tx_cqs(c); netif_napi_del(&c->napi); + + napi_hash_del(&c->napi); + synchronize_rcu(); + kfree(c); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index cf0098596e85..54800c61a563 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "en.h" static inline int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, @@ -242,6 +243,7 @@ bool mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) wqe = mlx5_wq_ll_get_wqe(&rq->wq, wqe_counter); skb = rq->skb[wqe_counter]; prefetch(skb->data); + skb_mark_napi_id(skb, cq->napi); rq->skb[wqe_counter] = NULL; dma_unmap_single(rq->pdev, -- cgit v1.2.3-59-g8ed1b From 44fb6fbbac23ee304d113ceb5eed45666d8a32f9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Nov 2015 06:30:56 -0800 Subject: mlx5: support napi_complete_done() A NAPI poll handler should return number of RX packets processed, instead of 0 / budget. This allows proper busy poll accounting through LINUX_MIB_BUSYPOLLRXPACKETS SNMP counter. napi_complete_done() allows /sys/class/net/ethX/gro_flush_timeout to be used for finer GRO aggregation control. Tested: Enabled busy polling, and checked TcpExtBusyPollRxPackets counter is increasing. echo 70 >/proc/sys/net/core/busy_read nstat >/dev/null netperf -H target -t TCP_RR >/dev/null nstat | grep TcpExtBusyPollRxPackets TcpExtBusyPollRxPackets 490958 0.0 Signed-off-by: Eric Dumazet Cc: Eli Cohen Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 14 ++++++-------- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 11 ++++++----- 3 files changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 22e72bf1ae48..69f1c1a412b4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -564,7 +564,7 @@ void mlx5e_completion_event(struct mlx5_core_cq *mcq); void mlx5e_cq_error_event(struct mlx5_core_cq *mcq, enum mlx5_event event); int mlx5e_napi_poll(struct napi_struct *napi, int budget); bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq); -bool mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget); +int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget); bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq); struct mlx5_cqe64 *mlx5e_get_cqe(struct mlx5e_cq *cq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 54800c61a563..fe752f8e24b9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -216,16 +216,16 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe, be16_to_cpu(cqe->vlan_info)); } -bool mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) +int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) { struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq); - int i; + int work_done; /* avoid accessing cq (dma coherent memory) if not needed */ if (!test_and_clear_bit(MLX5E_CQ_HAS_CQES, &cq->flags)) - return false; + return 0; - for (i = 0; i < budget; i++) { + for (work_done = 0; work_done < budget; work_done++) { struct mlx5e_rx_wqe *wqe; struct mlx5_cqe64 *cqe; struct sk_buff *skb; @@ -271,10 +271,8 @@ wq_ll_pop: /* ensure cq space is freed before enabling more cqes */ wmb(); - if (i == budget) { + if (work_done == budget) set_bit(MLX5E_CQ_HAS_CQES, &cq->flags); - return true; - } - return false; + return work_done; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 2c7cb6755d1d..4ac8d716dbdd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -54,6 +54,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) struct mlx5e_channel *c = container_of(napi, struct mlx5e_channel, napi); bool busy = false; + int work_done; int i; clear_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags); @@ -61,26 +62,26 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) for (i = 0; i < c->num_tc; i++) busy |= mlx5e_poll_tx_cq(&c->sq[i].cq); - busy |= mlx5e_poll_rx_cq(&c->rq.cq, budget); - + work_done = mlx5e_poll_rx_cq(&c->rq.cq, budget); + busy |= work_done == budget; busy |= mlx5e_post_rx_wqes(&c->rq); if (busy) return budget; - napi_complete(napi); + napi_complete_done(napi, work_done); /* avoid losing completion event during/after polling cqs */ if (test_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags)) { napi_schedule(napi); - return 0; + return work_done; } for (i = 0; i < c->num_tc; i++) mlx5e_cq_arm(&c->sq[i].cq); mlx5e_cq_arm(&c->rq.cq); - return 0; + return work_done; } void mlx5e_completion_event(struct mlx5_core_cq *mcq) -- cgit v1.2.3-59-g8ed1b From b59768c6b4fc1c95b7679f2cfa9c13d8cc70ab46 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Nov 2015 06:30:57 -0800 Subject: bnx2x: remove bnx2x_low_latency_recv() support Switch to native NAPI polling, as this reduces overhead and complexity. Normal path is faster, since one cmpxchg() is not anymore requested, and busy polling with the NAPI polling has same performance. Tested: lpk50:~# cat /proc/sys/net/core/busy_read 70 lpk50:~# nstat >/dev/null;./netperf -H lpk55 -t TCP_RR;nstat MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to lpk55.prod.google.com () port 0 AF_INET : first burst 0 Local /Remote Socket Size Request Resp. Elapsed Trans. Send Recv Size Size Time Rate bytes Bytes bytes bytes secs. per sec 16384 87380 1 1 10.00 40095.07 16384 87380 IpInReceives 401062 0.0 IpInDelivers 401062 0.0 IpOutRequests 401079 0.0 TcpActiveOpens 7 0.0 TcpPassiveOpens 3 0.0 TcpAttemptFails 3 0.0 TcpEstabResets 5 0.0 TcpInSegs 401036 0.0 TcpOutSegs 401052 0.0 TcpOutRsts 38 0.0 UdpInDatagrams 26 0.0 UdpOutDatagrams 27 0.0 Ip6OutNoRoutes 1 0.0 TcpExtDelayedACKs 1 0.0 TcpExtTCPPrequeued 98 0.0 TcpExtTCPDirectCopyFromPrequeue 98 0.0 TcpExtTCPHPHits 4 0.0 TcpExtTCPHPHitsToUser 98 0.0 TcpExtTCPPureAcks 5 0.0 TcpExtTCPHPAcks 101 0.0 TcpExtTCPAbortOnData 6 0.0 TcpExtBusyPollRxPackets 400832 0.0 TcpExtTCPOrigDataSent 400983 0.0 IpExtInOctets 21273867 0.0 IpExtOutOctets 21261254 0.0 IpExtInNoECTPkts 401064 0.0 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 113 ----------------------- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 46 +-------- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 7 -- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 3 - 4 files changed, 2 insertions(+), 167 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index b5e64b02200c..0b214b5d944a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -540,10 +540,6 @@ struct bnx2x_fastpath { struct napi_struct napi; -#ifdef CONFIG_NET_RX_BUSY_POLL - unsigned long busy_poll_state; -#endif - union host_hc_status_block status_blk; /* chip independent shortcuts into sb structure */ __le16 *sb_index_values; @@ -617,115 +613,6 @@ struct bnx2x_fastpath { #define bnx2x_fp_stats(bp, fp) (&((bp)->fp_stats[(fp)->index])) #define bnx2x_fp_qstats(bp, fp) (&((bp)->fp_stats[(fp)->index].eth_q_stats)) -#ifdef CONFIG_NET_RX_BUSY_POLL - -enum bnx2x_fp_state { - BNX2X_STATE_FP_NAPI = BIT(0), /* NAPI handler owns the queue */ - - BNX2X_STATE_FP_NAPI_REQ_BIT = 1, /* NAPI would like to own the queue */ - BNX2X_STATE_FP_NAPI_REQ = BIT(1), - - BNX2X_STATE_FP_POLL_BIT = 2, - BNX2X_STATE_FP_POLL = BIT(2), /* busy_poll owns the queue */ - - BNX2X_STATE_FP_DISABLE_BIT = 3, /* queue is dismantled */ -}; - -static inline void bnx2x_fp_busy_poll_init(struct bnx2x_fastpath *fp) -{ - WRITE_ONCE(fp->busy_poll_state, 0); -} - -/* called from the device poll routine to get ownership of a FP */ -static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp) -{ - unsigned long prev, old = READ_ONCE(fp->busy_poll_state); - - while (1) { - switch (old) { - case BNX2X_STATE_FP_POLL: - /* make sure bnx2x_fp_lock_poll() wont starve us */ - set_bit(BNX2X_STATE_FP_NAPI_REQ_BIT, - &fp->busy_poll_state); - /* fallthrough */ - case BNX2X_STATE_FP_POLL | BNX2X_STATE_FP_NAPI_REQ: - return false; - default: - break; - } - prev = cmpxchg(&fp->busy_poll_state, old, BNX2X_STATE_FP_NAPI); - if (unlikely(prev != old)) { - old = prev; - continue; - } - return true; - } -} - -static inline void bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp) -{ - smp_wmb(); - fp->busy_poll_state = 0; -} - -/* called from bnx2x_low_latency_poll() */ -static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp) -{ - return cmpxchg(&fp->busy_poll_state, 0, BNX2X_STATE_FP_POLL) == 0; -} - -static inline void bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp) -{ - smp_mb__before_atomic(); - clear_bit(BNX2X_STATE_FP_POLL_BIT, &fp->busy_poll_state); -} - -/* true if a socket is polling */ -static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp) -{ - return READ_ONCE(fp->busy_poll_state) & BNX2X_STATE_FP_POLL; -} - -/* false if fp is currently owned */ -static inline bool bnx2x_fp_ll_disable(struct bnx2x_fastpath *fp) -{ - set_bit(BNX2X_STATE_FP_DISABLE_BIT, &fp->busy_poll_state); - return !bnx2x_fp_ll_polling(fp); - -} -#else -static inline void bnx2x_fp_busy_poll_init(struct bnx2x_fastpath *fp) -{ -} - -static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp) -{ - return true; -} - -static inline void bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp) -{ -} - -static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp) -{ - return false; -} - -static inline void bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp) -{ -} - -static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp) -{ - return false; -} -static inline bool bnx2x_fp_ll_disable(struct bnx2x_fastpath *fp) -{ - return true; -} -#endif /* CONFIG_NET_RX_BUSY_POLL */ - /* Use 2500 as a mini-jumbo MTU for FCoE */ #define BNX2X_FCOE_MINI_JUMBO_MTU 2500 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index f8d7a2f06950..ca208a7eecd5 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -1096,10 +1096,7 @@ reuse_rx: skb_mark_napi_id(skb, &fp->napi); - if (bnx2x_fp_ll_polling(fp)) - netif_receive_skb(skb); - else - napi_gro_receive(&fp->napi, skb); + napi_gro_receive(&fp->napi, skb); next_rx: rx_buf->data = NULL; @@ -1869,7 +1866,6 @@ static void bnx2x_napi_enable_cnic(struct bnx2x *bp) int i; for_each_rx_queue_cnic(bp, i) { - bnx2x_fp_busy_poll_init(&bp->fp[i]); napi_enable(&bnx2x_fp(bp, i, napi)); } } @@ -1879,7 +1875,6 @@ static void bnx2x_napi_enable(struct bnx2x *bp) int i; for_each_eth_queue(bp, i) { - bnx2x_fp_busy_poll_init(&bp->fp[i]); napi_enable(&bnx2x_fp(bp, i, napi)); } } @@ -1890,8 +1885,6 @@ static void bnx2x_napi_disable_cnic(struct bnx2x *bp) for_each_rx_queue_cnic(bp, i) { napi_disable(&bnx2x_fp(bp, i, napi)); - while (!bnx2x_fp_ll_disable(&bp->fp[i])) - usleep_range(1000, 2000); } } @@ -1901,8 +1894,6 @@ static void bnx2x_napi_disable(struct bnx2x *bp) for_each_eth_queue(bp, i) { napi_disable(&bnx2x_fp(bp, i, napi)); - while (!bnx2x_fp_ll_disable(&bp->fp[i])) - usleep_range(1000, 2000); } } @@ -3232,9 +3223,6 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) return 0; } #endif - if (!bnx2x_fp_lock_napi(fp)) - return budget; - for_each_cos_in_tx_queue(fp, cos) if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos])) bnx2x_tx_int(bp, fp->txdata_ptr[cos]); @@ -3243,14 +3231,10 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) work_done += bnx2x_rx_int(fp, budget - work_done); /* must not complete if we consumed full budget */ - if (work_done >= budget) { - bnx2x_fp_unlock_napi(fp); + if (work_done >= budget) break; - } } - bnx2x_fp_unlock_napi(fp); - /* Fall out from the NAPI loop if needed */ if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) { @@ -3294,32 +3278,6 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) return work_done; } -#ifdef CONFIG_NET_RX_BUSY_POLL -/* must be called with local_bh_disable()d */ -int bnx2x_low_latency_recv(struct napi_struct *napi) -{ - struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath, - napi); - struct bnx2x *bp = fp->bp; - int found = 0; - - if ((bp->state == BNX2X_STATE_CLOSED) || - (bp->state == BNX2X_STATE_ERROR) || - (bp->dev->features & (NETIF_F_LRO | NETIF_F_GRO))) - return LL_FLUSH_FAILED; - - if (!bnx2x_fp_lock_poll(fp)) - return LL_FLUSH_BUSY; - - if (bnx2x_has_rx_work(fp)) - found = bnx2x_rx_int(fp, 4); - - bnx2x_fp_unlock_poll(fp); - - return found; -} -#endif - /* we split the first BD into headers and data BDs * to ease the pain of our fellow microcode engineers * we use one mapping for both BDs diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index b7d32e8412f1..4cbb03f87b5a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -569,13 +569,6 @@ int bnx2x_enable_msix(struct bnx2x *bp); */ int bnx2x_enable_msi(struct bnx2x *bp); -/** - * bnx2x_low_latency_recv - LL callback - * - * @napi: napi structure - */ -int bnx2x_low_latency_recv(struct napi_struct *napi); - /** * bnx2x_alloc_mem_bp - allocate memories outsize main driver structure * diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index c9b036789184..2273576404b4 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -13004,9 +13004,6 @@ static const struct net_device_ops bnx2x_netdev_ops = { .ndo_fcoe_get_wwn = bnx2x_fcoe_get_wwn, #endif -#ifdef CONFIG_NET_RX_BUSY_POLL - .ndo_busy_poll = bnx2x_low_latency_recv, -#endif .ndo_get_phys_port_id = bnx2x_get_phys_port_id, .ndo_set_vf_link_state = bnx2x_set_vf_link_state, .ndo_features_check = bnx2x_features_check, -- cgit v1.2.3-59-g8ed1b From 868fdb06063d92f4373be4974d1b651a32bb9442 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Nov 2015 06:30:58 -0800 Subject: mlx4: remove mlx4_en_low_latency_recv() Busy polling can now be handled in generic NAPI poll infrastructure. This removes complexity and fast path overhead : mlx4 used two spin_lock()/spin_unlock() pair per napi->poll() call in mlx4_en_cq_lock_napi()/mlx4_en_cq_unlock_napi() Tested: Without busy polling : lpaa23:~# echo 0 >/proc/sys/net/core/busy_read lpaa24:~# echo 0 >/proc/sys/net/core/busy_read lpaa23:~# ./netperf -H lpaa24 -t TCP_RR MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to lpaa24.prod.google.com () port 0 AF_INET : first burst 0 Local /Remote Socket Size Request Resp. Elapsed Trans. Send Recv Size Size Time Rate bytes Bytes bytes bytes secs. per sec 16384 87380 1 1 10.00 47330.78 With busy polling : lpaa23:~# echo 70 >/proc/sys/net/core/busy_read lpaa24:~# echo 70 >/proc/sys/net/core/busy_read lpaa23:~# ./netperf -H lpaa24 -t TCP_RR MIGRATED TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to lpaa24.prod.google.com () port 0 AF_INET : first burst 0 Local /Remote Socket Size Request Resp. Elapsed Trans. Send Recv Size Size Time Rate bytes Bytes bytes bytes secs. per sec 16384 87380 1 1 10.00 97643.55 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 17 ---- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 40 -------- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 15 +-- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 128 ------------------------ 4 files changed, 2 insertions(+), 198 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index ddb5541882f5..dd84cabb2a51 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -337,11 +337,7 @@ static int mlx4_en_get_sset_count(struct net_device *dev, int sset) case ETH_SS_STATS: return bitmap_iterator_count(&it) + (priv->tx_ring_num * 2) + -#ifdef CONFIG_NET_RX_BUSY_POLL - (priv->rx_ring_num * 5); -#else (priv->rx_ring_num * 2); -#endif case ETH_SS_TEST: return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2; @@ -408,11 +404,6 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev, for (i = 0; i < priv->rx_ring_num; i++) { data[index++] = priv->rx_ring[i]->packets; data[index++] = priv->rx_ring[i]->bytes; -#ifdef CONFIG_NET_RX_BUSY_POLL - data[index++] = priv->rx_ring[i]->yields; - data[index++] = priv->rx_ring[i]->misses; - data[index++] = priv->rx_ring[i]->cleaned; -#endif } spin_unlock_bh(&priv->stats_lock); @@ -486,14 +477,6 @@ static void mlx4_en_get_strings(struct net_device *dev, "rx%d_packets", i); sprintf(data + (index++) * ETH_GSTRING_LEN, "rx%d_bytes", i); -#ifdef CONFIG_NET_RX_BUSY_POLL - sprintf(data + (index++) * ETH_GSTRING_LEN, - "rx%d_napi_yield", i); - sprintf(data + (index++) * ETH_GSTRING_LEN, - "rx%d_misses", i); - sprintf(data + (index++) * ETH_GSTRING_LEN, - "rx%d_cleaned", i); -#endif } break; case ETH_SS_PRIV_FLAGS: diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 886e1bc86374..659209ff7af6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -69,34 +69,6 @@ int mlx4_en_setup_tc(struct net_device *dev, u8 up) return 0; } -#ifdef CONFIG_NET_RX_BUSY_POLL -/* must be called with local_bh_disable()d */ -static int mlx4_en_low_latency_recv(struct napi_struct *napi) -{ - struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi); - struct net_device *dev = cq->dev; - struct mlx4_en_priv *priv = netdev_priv(dev); - struct mlx4_en_rx_ring *rx_ring = priv->rx_ring[cq->ring]; - int done; - - if (!priv->port_up) - return LL_FLUSH_FAILED; - - if (!mlx4_en_cq_lock_poll(cq)) - return LL_FLUSH_BUSY; - - done = mlx4_en_process_rx_cq(dev, cq, 4); - if (likely(done)) - rx_ring->cleaned += done; - else - rx_ring->misses++; - - mlx4_en_cq_unlock_poll(cq); - - return done; -} -#endif /* CONFIG_NET_RX_BUSY_POLL */ - #ifdef CONFIG_RFS_ACCEL struct mlx4_en_filter { @@ -1561,8 +1533,6 @@ int mlx4_en_start_port(struct net_device *dev) for (i = 0; i < priv->rx_ring_num; i++) { cq = priv->rx_cq[i]; - mlx4_en_cq_init_lock(cq); - err = mlx4_en_init_affinity_hint(priv, i); if (err) { en_err(priv, "Failed preparing IRQ affinity hint\n"); @@ -1859,13 +1829,6 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) for (i = 0; i < priv->rx_ring_num; i++) { struct mlx4_en_cq *cq = priv->rx_cq[i]; - local_bh_disable(); - while (!mlx4_en_cq_lock_napi(cq)) { - pr_info("CQ %d locked\n", i); - mdelay(1); - } - local_bh_enable(); - napi_synchronize(&cq->napi); mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]); mlx4_en_deactivate_cq(priv, cq); @@ -2503,9 +2466,6 @@ static const struct net_device_ops mlx4_netdev_ops = { .ndo_setup_tc = mlx4_en_setup_tc, #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = mlx4_en_filter_rfs, -#endif -#ifdef CONFIG_NET_RX_BUSY_POLL - .ndo_busy_poll = mlx4_en_low_latency_recv, #endif .ndo_get_phys_port_id = mlx4_en_get_phys_port_id, #ifdef CONFIG_MLX4_EN_VXLAN diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index e7a5000aa12c..1feead34093b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -873,10 +873,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud * - TCP/IP (v4) * - without IP options * - not an IP fragment - * - no LLS polling in progress */ - if (!mlx4_en_cq_busy_polling(cq) && - (dev->features & NETIF_F_GRO)) { + if (dev->features & NETIF_F_GRO) { struct sk_buff *gro_skb = napi_get_frags(&cq->napi); if (!gro_skb) goto next; @@ -992,11 +990,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud skb_mark_napi_id(skb, &cq->napi); - if (!mlx4_en_cq_busy_polling(cq)) - napi_gro_receive(&cq->napi, skb); - else - netif_receive_skb(skb); - + napi_gro_receive(&cq->napi, skb); next: for (nr = 0; nr < priv->num_frags; nr++) mlx4_en_free_frag(priv, frags, nr); @@ -1038,13 +1032,8 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) struct mlx4_en_priv *priv = netdev_priv(dev); int done; - if (!mlx4_en_cq_lock_napi(cq)) - return budget; - done = mlx4_en_process_rx_cq(dev, cq, budget); - mlx4_en_cq_unlock_napi(cq); - /* If we used up all the quota - we're probably not done yet... */ if (done == budget) { const struct cpumask *aff; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 965c8f016ac4..35de7d2e6b34 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -320,11 +320,6 @@ struct mlx4_en_rx_ring { void *rx_info; unsigned long bytes; unsigned long packets; -#ifdef CONFIG_NET_RX_BUSY_POLL - unsigned long yields; - unsigned long misses; - unsigned long cleaned; -#endif unsigned long csum_ok; unsigned long csum_none; unsigned long csum_complete; @@ -347,18 +342,6 @@ struct mlx4_en_cq { struct mlx4_cqe *buf; #define MLX4_EN_OPCODE_ERROR 0x1e -#ifdef CONFIG_NET_RX_BUSY_POLL - unsigned int state; -#define MLX4_EN_CQ_STATE_IDLE 0 -#define MLX4_EN_CQ_STATE_NAPI 1 /* NAPI owns this CQ */ -#define MLX4_EN_CQ_STATE_POLL 2 /* poll owns this CQ */ -#define MLX4_CQ_LOCKED (MLX4_EN_CQ_STATE_NAPI | MLX4_EN_CQ_STATE_POLL) -#define MLX4_EN_CQ_STATE_NAPI_YIELD 4 /* NAPI yielded this CQ */ -#define MLX4_EN_CQ_STATE_POLL_YIELD 8 /* poll yielded this CQ */ -#define CQ_YIELD (MLX4_EN_CQ_STATE_NAPI_YIELD | MLX4_EN_CQ_STATE_POLL_YIELD) -#define CQ_USER_PEND (MLX4_EN_CQ_STATE_POLL | MLX4_EN_CQ_STATE_POLL_YIELD) - spinlock_t poll_lock; /* protects from LLS/napi conflicts */ -#endif /* CONFIG_NET_RX_BUSY_POLL */ struct irq_desc *irq_desc; }; @@ -622,117 +605,6 @@ static inline struct mlx4_cqe *mlx4_en_get_cqe(void *buf, int idx, int cqe_sz) return buf + idx * cqe_sz; } -#ifdef CONFIG_NET_RX_BUSY_POLL -static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq) -{ - spin_lock_init(&cq->poll_lock); - cq->state = MLX4_EN_CQ_STATE_IDLE; -} - -/* called from the device poll rutine to get ownership of a cq */ -static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq) -{ - int rc = true; - spin_lock(&cq->poll_lock); - if (cq->state & MLX4_CQ_LOCKED) { - WARN_ON(cq->state & MLX4_EN_CQ_STATE_NAPI); - cq->state |= MLX4_EN_CQ_STATE_NAPI_YIELD; - rc = false; - } else - /* we don't care if someone yielded */ - cq->state = MLX4_EN_CQ_STATE_NAPI; - spin_unlock(&cq->poll_lock); - return rc; -} - -/* returns true is someone tried to get the cq while napi had it */ -static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq) -{ - int rc = false; - spin_lock(&cq->poll_lock); - WARN_ON(cq->state & (MLX4_EN_CQ_STATE_POLL | - MLX4_EN_CQ_STATE_NAPI_YIELD)); - - if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD) - rc = true; - cq->state = MLX4_EN_CQ_STATE_IDLE; - spin_unlock(&cq->poll_lock); - return rc; -} - -/* called from mlx4_en_low_latency_recv(), BH are disabled */ -static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq) -{ - int rc = true; - - spin_lock(&cq->poll_lock); - if ((cq->state & MLX4_CQ_LOCKED)) { - struct net_device *dev = cq->dev; - struct mlx4_en_priv *priv = netdev_priv(dev); - struct mlx4_en_rx_ring *rx_ring = priv->rx_ring[cq->ring]; - - cq->state |= MLX4_EN_CQ_STATE_POLL_YIELD; - rc = false; - rx_ring->yields++; - } else - /* preserve yield marks */ - cq->state |= MLX4_EN_CQ_STATE_POLL; - spin_unlock(&cq->poll_lock); - return rc; -} - -/* returns true if someone tried to get the cq while it was locked */ -static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq) -{ - int rc = false; - - spin_lock(&cq->poll_lock); - WARN_ON(cq->state & (MLX4_EN_CQ_STATE_NAPI)); - - if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD) - rc = true; - cq->state = MLX4_EN_CQ_STATE_IDLE; - spin_unlock(&cq->poll_lock); - return rc; -} - -/* true if a socket is polling, even if it did not get the lock */ -static inline bool mlx4_en_cq_busy_polling(struct mlx4_en_cq *cq) -{ - WARN_ON(!(cq->state & MLX4_CQ_LOCKED)); - return cq->state & CQ_USER_PEND; -} -#else -static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq) -{ -} - -static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq) -{ - return true; -} - -static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq) -{ - return false; -} - -static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq) -{ - return false; -} - -static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq) -{ - return false; -} - -static inline bool mlx4_en_cq_busy_polling(struct mlx4_en_cq *cq) -{ - return false; -} -#endif /* CONFIG_NET_RX_BUSY_POLL */ - #define MLX4_EN_WOL_DO_MODIFY (1ULL << 63) void mlx4_en_update_loopback_state(struct net_device *dev, -- cgit v1.2.3-59-g8ed1b From 93f93a4404159ecf7e9148f5ad0718ec702ac4cb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Nov 2015 06:30:59 -0800 Subject: net: move skb_mark_napi_id() into core networking stack We would like to automatically provide busy polling support to all NAPI drivers, without them having to implement anything. skb_mark_napi_id() can be called from napi_gro_receive() and napi_get_frags(). Few drivers are still calling skb_mark_napi_id() because they use netif_receive_skb(). They should eventually call napi_gro_receive() instead. I will leave this to drivers maintainers. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 1 - drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 2 -- drivers/net/ethernet/chelsio/cxgb4/sge.c | 1 - drivers/net/ethernet/emulex/benet/be_main.c | 1 - drivers/net/ethernet/intel/i40e/i40e_txrx.c | 1 - drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 1 - drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 3 --- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 1 - drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 2 +- drivers/net/ethernet/sfc/rx.c | 1 - drivers/net/virtio_net.c | 2 -- net/core/dev.c | 2 ++ 13 files changed, 4 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 53ce1222b11d..8a9b493566c9 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -2024,7 +2024,6 @@ read_again: skb->dev = netdev; skb->protocol = eth_type_trans(skb, netdev); skb_record_rx_queue(skb, channel->queue_index); - skb_mark_napi_id(skb, napi); napi_gro_receive(napi, skb); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index ca208a7eecd5..ab9222924bd9 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -1094,8 +1094,6 @@ reuse_rx: __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), le16_to_cpu(cqe_fp->vlan_tag)); - skb_mark_napi_id(skb, &fp->napi); - napi_gro_receive(&fp->napi, skb); next_rx: rx_buf->data = NULL; diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index b7b93e7a643d..f650f295f264 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -1864,7 +1864,6 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl, skb->truesize += skb->data_len; skb->ip_summed = CHECKSUM_UNNECESSARY; skb_record_rx_queue(skb, rxq->rspq.idx); - skb_mark_napi_id(skb, &rxq->rspq.napi); pi = netdev_priv(skb->dev); if (pi->rxtstamp) cxgb4_sgetim_to_hwtstamp(adapter, skb_hwtstamps(skb), diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index b6ad02909d6b..c29d62496ad9 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2184,7 +2184,6 @@ static void be_rx_compl_process_gro(struct be_rx_obj *rxo, skb_set_hash(skb, rxcp->rss_hash, PKT_HASH_TYPE_L3); skb->csum_level = rxcp->tunneled; - skb_mark_napi_id(skb, napi); if (rxcp->vlanf) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 635b3ac17877..6649ce4ba2de 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1632,7 +1632,6 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) continue; } #endif - skb_mark_napi_id(skb, &rx_ring->q_vector->napi); i40e_receive_skb(rx_ring, skb, vlan_tag); rx_desc->wb.qword1.status_error_len = 0; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 47e9a90d6b10..77968b184b1f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1090,7 +1090,6 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) continue; } #endif - skb_mark_napi_id(skb, &rx_ring->q_vector->napi); i40e_receive_skb(rx_ring, skb, vlan_tag); rx_desc->wb.qword1.status_error_len = 0; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 47395ff5d908..4089d776d01a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1659,6 +1659,7 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring, static void ixgbe_rx_skb(struct ixgbe_q_vector *q_vector, struct sk_buff *skb) { + skb_mark_napi_id(skb, &q_vector->napi); if (ixgbe_qv_busy_polling(q_vector)) netif_receive_skb(skb); else @@ -2123,7 +2124,6 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, } #endif /* IXGBE_FCOE */ - skb_mark_napi_id(skb, &q_vector->napi); ixgbe_rx_skb(q_vector, skb); /* update budget accounting */ diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 1feead34093b..41440b2b20a3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -925,7 +925,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud PKT_HASH_TYPE_L3); skb_record_rx_queue(gro_skb, cq->ring); - skb_mark_napi_id(gro_skb, &cq->napi); if (ring->hwtstamp_rx_filter == HWTSTAMP_FILTER_ALL) { timestamp = mlx4_en_get_cqe_ts(cqe); @@ -988,8 +987,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud timestamp); } - skb_mark_napi_id(skb, &cq->napi); - napi_gro_receive(&cq->napi, skb); next: for (nr = 0; nr < priv->num_frags; nr++) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index fe752f8e24b9..7c8c4088d1be 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -243,7 +243,6 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) wqe = mlx5_wq_ll_get_wqe(&rq->wq, wqe_counter); skb = rq->skb[wqe_counter]; prefetch(skb->data); - skb_mark_napi_id(skb, cq->napi); rq->skb[wqe_counter] = NULL; dma_unmap_single(rq->pdev, diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 83651ac8ddb9..acf866147d65 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -1488,7 +1488,6 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum) } myri10ge_vlan_rx(mgp->dev, va, skb); skb_record_rx_queue(skb, ss - &mgp->ss[0]); - skb_mark_napi_id(skb, &ss->napi); if (polling) { int hlen; @@ -1506,6 +1505,7 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum) skb->data_len -= hlen; skb->tail += hlen; skb->protocol = eth_type_trans(skb, dev); + skb_mark_napi_id(skb, &ss->napi); netif_receive_skb(skb); } else diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 809ea4610a77..8956995b2fe7 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -463,7 +463,6 @@ efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf, skb_record_rx_queue(skb, channel->rx_queue.core_index); - skb_mark_napi_id(skb, &channel->napi_str); gro_result = napi_gro_frags(napi); if (gro_result != GRO_DROP) channel->irq_mod_score += 2; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index d8838dedb7a4..d1d14cecf450 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -516,8 +516,6 @@ static void receive_buf(struct virtnet_info *vi, struct receive_queue *rq, skb_shinfo(skb)->gso_segs = 0; } - skb_mark_napi_id(skb, &rq->napi); - napi_gro_receive(&rq->napi, skb); return; diff --git a/net/core/dev.c b/net/core/dev.c index 93009610aee8..83b48747928c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4356,6 +4356,7 @@ static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb) gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) { + skb_mark_napi_id(skb, napi); trace_napi_gro_receive_entry(skb); skb_gro_reset_offset(skb); @@ -4390,6 +4391,7 @@ struct sk_buff *napi_get_frags(struct napi_struct *napi) if (!skb) { skb = napi_alloc_skb(napi, GRO_MAX_HEAD); napi->skb = skb; + skb_mark_napi_id(skb, napi); } return skb; } -- cgit v1.2.3-59-g8ed1b From d64b5e85bfe2fe4c790abcbd16d9ae32391ddd7e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Nov 2015 06:31:00 -0800 Subject: net: add netif_tx_napi_add() netif_tx_napi_add() is a variant of netif_napi_add() It should be used by drivers that use a napi structure to exclusively poll TX. We do not want to add this kind of napi in napi_hash[] in following patches, adding generic busy polling to all NAPI drivers. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcmsysport.c | 2 +- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 4 ++-- .../net/ethernet/freescale/fs_enet/fs_enet-main.c | 2 +- drivers/net/ethernet/freescale/gianfar.c | 4 ++-- drivers/net/ethernet/mellanox/mlx4/en_cq.c | 4 ++-- drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c | 4 ++-- drivers/net/ethernet/rocker/rocker.c | 2 +- drivers/net/ethernet/ti/cpsw.c | 2 +- drivers/net/ethernet/ti/netcp_core.c | 2 +- drivers/net/wireless/ath/wil6210/netdev.c | 2 +- include/linux/netdevice.h | 23 +++++++++++++++++++++- net/core/dev.c | 3 ++- 12 files changed, 38 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 858106352ce9..993c780bdfab 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -1216,7 +1216,7 @@ static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv, /* Initialize SW view of the ring */ spin_lock_init(&ring->lock); ring->priv = priv; - netif_napi_add(priv->netdev, &ring->napi, bcm_sysport_tx_poll, 64); + netif_tx_napi_add(priv->netdev, &ring->napi, bcm_sysport_tx_poll, 64); ring->index = index; ring->size = size; ring->alloc_size = ring->size; diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 17f017ab4dac..b15a60d787c7 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -2041,11 +2041,11 @@ static void bcmgenet_init_tx_napi(struct bcmgenet_priv *priv) for (i = 0; i < priv->hw_params->tx_queues; ++i) { ring = &priv->tx_rings[i]; - netif_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64); + netif_tx_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64); } ring = &priv->tx_rings[DESC_INDEX]; - netif_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64); + netif_tx_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64); } static void bcmgenet_enable_tx_napi(struct bcmgenet_priv *priv) diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index cf8e54652df9..48a9c176e0d1 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -1050,7 +1050,7 @@ static int fs_enet_probe(struct platform_device *ofdev) ndev->netdev_ops = &fs_enet_netdev_ops; ndev->watchdog_timeo = 2 * HZ; netif_napi_add(ndev, &fep->napi, fs_enet_rx_napi, fpi->napi_weight); - netif_napi_add(ndev, &fep->napi_tx, fs_enet_tx_napi, 2); + netif_tx_napi_add(ndev, &fep->napi_tx, fs_enet_tx_napi, 2); ndev->ethtool_ops = &fs_ethtool_ops; diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 3e6b9b437497..c8bc43e99a35 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -1347,12 +1347,12 @@ static int gfar_probe(struct platform_device *ofdev) if (priv->poll_mode == GFAR_SQ_POLLING) { netif_napi_add(dev, &priv->gfargrp[i].napi_rx, gfar_poll_rx_sq, GFAR_DEV_WEIGHT); - netif_napi_add(dev, &priv->gfargrp[i].napi_tx, + netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx, gfar_poll_tx_sq, 2); } else { netif_napi_add(dev, &priv->gfargrp[i].napi_rx, gfar_poll_rx, GFAR_DEV_WEIGHT); - netif_napi_add(dev, &priv->gfargrp[i].napi_tx, + netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx, gfar_poll_tx, 2); } } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index eb8a4988de63..3a6176fea78d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -156,8 +156,8 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, cq->mcq.event = mlx4_en_cq_event; if (cq->is_tx) { - netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_tx_cq, - NAPI_POLL_WEIGHT); + netif_tx_napi_add(cq->dev, &cq->napi, mlx4_en_poll_tx_cq, + NAPI_POLL_WEIGHT); } else { netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq, 64); napi_hash_add(&cq->napi); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index d4b5085a21fa..7bd6f25b4625 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -1604,7 +1604,7 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter, if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) { for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; - netif_napi_add(netdev, &tx_ring->napi, qlcnic_tx_poll, + netif_tx_napi_add(netdev, &tx_ring->napi, qlcnic_tx_poll, NAPI_POLL_WEIGHT); } } @@ -2135,7 +2135,7 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter, !(adapter->flags & QLCNIC_TX_INTR_SHARED)) { for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; - netif_napi_add(netdev, &tx_ring->napi, + netif_tx_napi_add(netdev, &tx_ring->napi, qlcnic_83xx_msix_tx_poll, NAPI_POLL_WEIGHT); } diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index e9f2349e98bc..a4ab71d43e4e 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -4998,7 +4998,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) dev->netdev_ops = &rocker_port_netdev_ops; dev->ethtool_ops = &rocker_port_ethtool_ops; dev->switchdev_ops = &rocker_port_switchdev_ops; - netif_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx, + netif_tx_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx, NAPI_POLL_WEIGHT); netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx, NAPI_POLL_WEIGHT); diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 48b92c9de12a..15322c08de80 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2469,7 +2469,7 @@ static int cpsw_probe(struct platform_device *pdev) ndev->netdev_ops = &cpsw_netdev_ops; ndev->ethtool_ops = &cpsw_ethtool_ops; netif_napi_add(ndev, &priv->napi_rx, cpsw_rx_poll, CPSW_POLL_WEIGHT); - netif_napi_add(ndev, &priv->napi_tx, cpsw_tx_poll, CPSW_POLL_WEIGHT); + netif_tx_napi_add(ndev, &priv->napi_tx, cpsw_tx_poll, CPSW_POLL_WEIGHT); /* register the network device */ SET_NETDEV_DEV(ndev, &pdev->dev); diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 37b9b39192ec..e5e20e734f21 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1990,7 +1990,7 @@ static int netcp_create_interface(struct netcp_device *netcp_device, /* NAPI register */ netif_napi_add(ndev, &netcp->rx_napi, netcp_rx_poll, NETCP_NAPI_WEIGHT); - netif_napi_add(ndev, &netcp->tx_napi, netcp_tx_poll, NETCP_NAPI_WEIGHT); + netif_tx_napi_add(ndev, &netcp->tx_napi, netcp_tx_poll, NETCP_NAPI_WEIGHT); /* Register the network device */ ndev->dev_id = 0; diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index e3b3c8fb4605..56aaa2d4fb0e 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -183,7 +183,7 @@ void *wil_if_alloc(struct device *dev) netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx, WIL6210_NAPI_BUDGET); - netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx, + netif_tx_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx, WIL6210_NAPI_BUDGET); netif_tx_stop_all_queues(ndev); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 2020a89df12b..838935d1cdbb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -326,7 +326,8 @@ enum { NAPI_STATE_SCHED, /* Poll is scheduled */ NAPI_STATE_DISABLE, /* Disable pending */ NAPI_STATE_NPSVC, /* Netpoll - don't dequeue from poll_list */ - NAPI_STATE_HASHED, /* In NAPI hash */ + NAPI_STATE_HASHED, /* In NAPI hash (busy polling possible) */ + NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */ }; enum gro_result { @@ -1938,6 +1939,26 @@ static inline void *netdev_priv(const struct net_device *dev) void netif_napi_add(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *, int), int weight); +/** + * netif_tx_napi_add - initialize a napi context + * @dev: network device + * @napi: napi context + * @poll: polling function + * @weight: default weight + * + * This variant of netif_napi_add() should be used from drivers using NAPI + * to exclusively poll a TX queue. + * This will avoid we add it into napi_hash[], thus polluting this hash table. + */ +static inline void netif_tx_napi_add(struct net_device *dev, + struct napi_struct *napi, + int (*poll)(struct napi_struct *, int), + int weight) +{ + set_bit(NAPI_STATE_NO_BUSY_POLL, &napi->state); + netif_napi_add(dev, napi, poll, weight); +} + /** * netif_napi_del - remove a napi context * @napi: napi context diff --git a/net/core/dev.c b/net/core/dev.c index 83b48747928c..ff58a8bc5e3c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4737,7 +4737,8 @@ EXPORT_SYMBOL(sk_busy_loop); void napi_hash_add(struct napi_struct *napi) { - if (test_and_set_bit(NAPI_STATE_HASHED, &napi->state)) + if (test_bit(NAPI_STATE_NO_BUSY_POLL, &napi->state) || + test_and_set_bit(NAPI_STATE_HASHED, &napi->state)) return; spin_lock(&napi_hash_lock); -- cgit v1.2.3-59-g8ed1b From 93d05d4a320cb16712bb3d57a9658f395d8cecb9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 Nov 2015 06:31:03 -0800 Subject: net: provide generic busy polling to all NAPI drivers NAPI drivers no longer need to observe a particular protocol to benefit from busy polling (CONFIG_NET_RX_BUSY_POLL=y) napi_hash_add() and napi_hash_del() are automatically called from core networking stack, respectively from netif_napi_add() and netif_napi_del() This patch depends on free_netdev() and netif_napi_del() being called from process context, which seems to be the norm. Drivers might still prefer to call napi_hash_del() on their own, since they might combine all the rcu grace periods into a single one, knowing their NAPI structures lifetime, while core networking stack has no idea of a possible combining. Once this patch proves to not bring serious regressions, we will cleanup drivers to either remove napi_hash_del() or provide appropriate rcu grace periods combining. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 2 -- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 -- drivers/net/ethernet/chelsio/cxgb4/sge.c | 1 - drivers/net/ethernet/cisco/enic/enic_main.c | 2 -- drivers/net/ethernet/emulex/benet/be_main.c | 1 - drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c | 1 - drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 3 --- drivers/net/ethernet/mellanox/mlx4/en_cq.c | 6 ++---- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 1 - drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 1 - drivers/net/ethernet/sfc/efx.c | 1 - drivers/net/virtio_net.c | 1 - include/linux/netdevice.h | 7 +++++++ net/core/dev.c | 7 +++++++ 14 files changed, 16 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index ab9222924bd9..d9add7c02e42 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -46,7 +46,6 @@ static void bnx2x_add_all_napi_cnic(struct bnx2x *bp) for_each_rx_queue_cnic(bp, i) { netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), bnx2x_poll, NAPI_POLL_WEIGHT); - napi_hash_add(&bnx2x_fp(bp, i, napi)); } } @@ -58,7 +57,6 @@ static void bnx2x_add_all_napi(struct bnx2x *bp) for_each_eth_queue(bp, i) { netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), bnx2x_poll, NAPI_POLL_WEIGHT); - napi_hash_add(&bnx2x_fp(bp, i, napi)); } } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index db15c5ee09c5..f2d0dc9b1c41 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4227,12 +4227,10 @@ static void bnxt_init_napi(struct bnxt *bp) bnapi = bp->bnapi[i]; netif_napi_add(bp->dev, &bnapi->napi, bnxt_poll, 64); - napi_hash_add(&bnapi->napi); } } else { bnapi = bp->bnapi[0]; netif_napi_add(bp->dev, &bnapi->napi, bnxt_poll, 64); - napi_hash_add(&bnapi->napi); } } diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index f650f295f264..48d8fbb1c220 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -2527,7 +2527,6 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, goto err; netif_napi_add(dev, &iq->napi, napi_rx_handler, 64); - napi_hash_add(&iq->napi); iq->cur_desc = iq->desc; iq->cidx = 0; iq->gen = 1; diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index b36643ef0593..b2182d3ba3cc 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -2458,13 +2458,11 @@ static int enic_dev_init(struct enic *enic) switch (vnic_dev_get_intr_mode(enic->vdev)) { default: netif_napi_add(netdev, &enic->napi[0], enic_poll, 64); - napi_hash_add(&enic->napi[0]); break; case VNIC_DEV_INTR_MODE_MSIX: for (i = 0; i < enic->rq_count; i++) { netif_napi_add(netdev, &enic->napi[i], enic_poll_msix_rq, NAPI_POLL_WEIGHT); - napi_hash_add(&enic->napi[i]); } for (i = 0; i < enic->wq_count; i++) netif_napi_add(netdev, &enic->napi[enic_cq_wq(enic, i)], diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index c29d62496ad9..4cab8879f5ae 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2630,7 +2630,6 @@ static int be_evt_queues_create(struct be_adapter *adapter) eqo->affinity_mask); netif_napi_add(adapter->netdev, &eqo->napi, be_poll, BE_NAPI_WEIGHT); - napi_hash_add(&eqo->napi); } return 0; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index f3168bcc7d87..e771e764daa3 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -844,7 +844,6 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, /* initialize NAPI */ netif_napi_add(adapter->netdev, &q_vector->napi, ixgbe_poll, 64); - napi_hash_add(&q_vector->napi); #ifdef CONFIG_NET_RX_BUSY_POLL /* initialize busy poll */ diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 592ff237d692..2955186cd4f6 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -2483,9 +2483,6 @@ static int ixgbevf_alloc_q_vectors(struct ixgbevf_adapter *adapter) q_vector->v_idx = q_idx; netif_napi_add(adapter->netdev, &q_vector->napi, ixgbevf_poll, 64); -#ifdef CONFIG_NET_RX_BUSY_POLL - napi_hash_add(&q_vector->napi); -#endif adapter->q_vector[q_idx] = q_vector; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index 3a6176fea78d..af975a2b74c6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -155,13 +155,11 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, cq->mcq.comp = cq->is_tx ? mlx4_en_tx_irq : mlx4_en_rx_irq; cq->mcq.event = mlx4_en_cq_event; - if (cq->is_tx) { + if (cq->is_tx) netif_tx_napi_add(cq->dev, &cq->napi, mlx4_en_poll_tx_cq, NAPI_POLL_WEIGHT); - } else { + else netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq, 64); - napi_hash_add(&cq->napi); - } napi_enable(&cq->napi); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index ffb1f9c1b973..f6a8cc787603 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -982,7 +982,6 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, mlx5e_build_channeltc_to_txq_map(priv, ix); netif_napi_add(netdev, &c->napi, mlx5e_napi_poll, 64); - napi_hash_add(&c->napi); err = mlx5e_open_tx_cqs(c, cparam); if (err) diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index acf866147d65..270c9eeb7ab6 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -3814,7 +3814,6 @@ static int myri10ge_alloc_slices(struct myri10ge_priv *mgp) ss->dev = mgp->dev; netif_napi_add(ss->dev, &ss->napi, myri10ge_poll, myri10ge_napi_weight); - napi_hash_add(&ss->napi); } return 0; abort: diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index a3c42a376741..4e82bcfbe3e0 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -2059,7 +2059,6 @@ static void efx_init_napi_channel(struct efx_channel *channel) channel->napi_dev = efx->net_dev; netif_napi_add(channel->napi_dev, &channel->napi_str, efx_poll, napi_weight); - napi_hash_add(&channel->napi_str); efx_channel_busy_poll_init(channel); } diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index d1d14cecf450..b1ae4cbf2453 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1610,7 +1610,6 @@ static int virtnet_alloc_queues(struct virtnet_info *vi) vi->rq[i].pages = NULL; netif_napi_add(vi->dev, &vi->rq[i].napi, virtnet_poll, napi_weight); - napi_hash_add(&vi->rq[i].napi); sg_init_table(vi->rq[i].sg, ARRAY_SIZE(vi->rq[i].sg)); ewma_pkt_len_init(&vi->rq[i].mrg_avg_pkt_len); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e5c33b29471b..7d2d1d7aaec7 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -466,6 +466,9 @@ static inline void napi_complete(struct napi_struct *n) * @napi: napi context * * generate a new napi_id and store a @napi under it in napi_hash + * Used for busy polling (CONFIG_NET_RX_BUSY_POLL) + * Note: This is normally automatically done from netif_napi_add(), + * so might disappear in a future linux version. */ void napi_hash_add(struct napi_struct *napi); @@ -476,6 +479,10 @@ void napi_hash_add(struct napi_struct *napi); * Warning: caller must observe rcu grace period * before freeing memory containing @napi, if * this function returns true. + * Note: core networking stack automatically calls it + * from netif_napi_del() + * Drivers might want to call this helper to combine all + * the needed rcu grace periods into a single one. */ bool napi_hash_del(struct napi_struct *napi); diff --git a/net/core/dev.c b/net/core/dev.c index 59dddac1c2e7..41cef3e3f558 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4807,6 +4807,7 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi, napi->poll_owner = -1; #endif set_bit(NAPI_STATE_SCHED, &napi->state); + napi_hash_add(napi); } EXPORT_SYMBOL(netif_napi_add); @@ -4826,8 +4827,12 @@ void napi_disable(struct napi_struct *n) } EXPORT_SYMBOL(napi_disable); +/* Must be called in process context */ void netif_napi_del(struct napi_struct *napi) { + might_sleep(); + if (napi_hash_del(napi)) + synchronize_net(); list_del_init(&napi->dev_list); napi_free_frags(napi); @@ -7227,11 +7232,13 @@ EXPORT_SYMBOL(alloc_netdev_mqs); * This function does the last stage of destroying an allocated device * interface. The reference to the device object is released. * If this is the last reference then it will be freed. + * Must be called in process context. */ void free_netdev(struct net_device *dev) { struct napi_struct *p, *n; + might_sleep(); netif_free_tx_queues(dev); #ifdef CONFIG_SYSFS kvfree(dev->_rx); -- cgit v1.2.3-59-g8ed1b From 618e8bc228cda7b8c517caac40a45ee909b8672d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 5 Nov 2015 07:33:56 +0100 Subject: Bluetooth: Use new hci_skb_pkt_* wrappers for drivers The new hci_skb_pkt_* wrappers are mainly intented for drivers to require less knowledge about bt_cb(sbk) handling. So after converting the core packet handling, convert all drivers. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/bfusb.c | 9 ++++---- drivers/bluetooth/bluecard_cs.c | 25 ++++++++++----------- drivers/bluetooth/bpa10x.c | 4 ++-- drivers/bluetooth/bt3c_cs.c | 11 +++++----- drivers/bluetooth/btmrvl_main.c | 8 +++---- drivers/bluetooth/btmrvl_sdio.c | 4 ++-- drivers/bluetooth/btsdio.c | 6 +++--- drivers/bluetooth/btuart_cs.c | 11 +++++----- drivers/bluetooth/btusb.c | 48 ++++++++++++++++++++--------------------- drivers/bluetooth/btwilink.c | 8 +++---- drivers/bluetooth/dtl1_cs.c | 11 +++++----- drivers/bluetooth/hci_ath.c | 6 +++--- drivers/bluetooth/hci_bcm.c | 2 +- drivers/bluetooth/hci_bcsp.c | 25 ++++++++++++--------- drivers/bluetooth/hci_h4.c | 16 +++++++------- drivers/bluetooth/hci_h5.c | 14 ++++++------ drivers/bluetooth/hci_intel.c | 14 ++++++------ drivers/bluetooth/hci_ldisc.c | 5 +++-- drivers/bluetooth/hci_ll.c | 4 ++-- drivers/bluetooth/hci_qca.c | 4 ++-- drivers/bluetooth/hci_vhci.c | 8 +++---- 21 files changed, 127 insertions(+), 116 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 616ec2ac1b22..72d8bfabef09 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -324,7 +324,7 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch return -ENOMEM; } - bt_cb(skb)->pkt_type = pkt_type; + hci_skb_pkt_type(skb) = pkt_type; data->reassembly = skb; } else { @@ -469,9 +469,10 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) unsigned char buf[3]; int sent = 0, size, count; - BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len); + BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, + hci_skb_pkt_type(skb), skb->len); - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; @@ -484,7 +485,7 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) } /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); count = skb->len; diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 36fa1c958c74..c0b3b5576992 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -261,7 +261,7 @@ static void bluecard_write_wakeup(struct bluecard_info *info) if (!skb) break; - if (bt_cb(skb)->pkt_type & 0x80) { + if (hci_skb_pkt_type(skb) & 0x80) { /* Disable RTS */ info->ctrl_reg |= REG_CONTROL_RTS; outb(info->ctrl_reg, iobase + REG_CONTROL); @@ -279,13 +279,13 @@ static void bluecard_write_wakeup(struct bluecard_info *info) /* Mark the buffer as dirty */ clear_bit(ready_bit, &(info->tx_state)); - if (bt_cb(skb)->pkt_type & 0x80) { + if (hci_skb_pkt_type(skb) & 0x80) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); DEFINE_WAIT(wait); unsigned char baud_reg; - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case PKT_BAUD_RATE_460800: baud_reg = REG_CONTROL_BAUD_RATE_460800; break; @@ -402,9 +402,9 @@ static void bluecard_receive(struct bluecard_info *info, if (info->rx_state == RECV_WAIT_PACKET_TYPE) { - bt_cb(info->rx_skb)->pkt_type = buf[i]; + hci_skb_pkt_type(info->rx_skb) = buf[i]; - switch (bt_cb(info->rx_skb)->pkt_type) { + switch (hci_skb_pkt_type(info->rx_skb)) { case 0x00: /* init packet */ @@ -436,7 +436,8 @@ static void bluecard_receive(struct bluecard_info *info, default: /* unknown packet */ - BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); + BT_ERR("Unknown HCI packet with type 0x%02x received", + hci_skb_pkt_type(info->rx_skb)); info->hdev->stat.err_rx++; kfree_skb(info->rx_skb); @@ -578,21 +579,21 @@ static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud) switch (baud) { case 460800: cmd[4] = 0x00; - bt_cb(skb)->pkt_type = PKT_BAUD_RATE_460800; + hci_skb_pkt_type(skb) = PKT_BAUD_RATE_460800; break; case 230400: cmd[4] = 0x01; - bt_cb(skb)->pkt_type = PKT_BAUD_RATE_230400; + hci_skb_pkt_type(skb) = PKT_BAUD_RATE_230400; break; case 115200: cmd[4] = 0x02; - bt_cb(skb)->pkt_type = PKT_BAUD_RATE_115200; + hci_skb_pkt_type(skb) = PKT_BAUD_RATE_115200; break; case 57600: /* Fall through... */ default: cmd[4] = 0x03; - bt_cb(skb)->pkt_type = PKT_BAUD_RATE_57600; + hci_skb_pkt_type(skb) = PKT_BAUD_RATE_57600; break; } @@ -660,7 +661,7 @@ static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct bluecard_info *info = hci_get_drvdata(hdev); - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; @@ -673,7 +674,7 @@ static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) } /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); skb_queue_tail(&(info->txq), skb); bluecard_write_wakeup(info); diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 49c397e21b39..fd6b53e9bbf2 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -295,9 +295,9 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb) return -ENOMEM; /* Prepend skb with frame type */ - *skb_push(skb, 1) = bt_cb(skb)->pkt_type; + *skb_push(skb, 1) = hci_skb_pkt_type(skb); - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: dr = kmalloc(sizeof(*dr), GFP_ATOMIC); if (!dr) { diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 5803aaed958f..8165ef2fe877 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -246,10 +246,10 @@ static void bt3c_receive(struct bt3c_info *info) if (info->rx_state == RECV_WAIT_PACKET_TYPE) { - bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L); + hci_skb_pkt_type(info->rx_skb) = inb(iobase + DATA_L); inb(iobase + DATA_H); - switch (bt_cb(info->rx_skb)->pkt_type) { + switch (hci_skb_pkt_type(info->rx_skb)) { case HCI_EVENT_PKT: info->rx_state = RECV_WAIT_EVENT_HEADER; @@ -268,7 +268,8 @@ static void bt3c_receive(struct bt3c_info *info) default: /* Unknown packet */ - BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); + BT_ERR("Unknown HCI packet with type 0x%02x received", + hci_skb_pkt_type(info->rx_skb)); info->hdev->stat.err_rx++; kfree_skb(info->rx_skb); @@ -411,7 +412,7 @@ static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) struct bt3c_info *info = hci_get_drvdata(hdev); unsigned long flags; - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; @@ -424,7 +425,7 @@ static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) } /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); skb_queue_tail(&(info->txq), skb); spin_lock_irqsave(&(info->lock), flags); diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 6af917331962..f2b38c880b11 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -196,7 +196,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode, if (len) memcpy(skb_put(skb, len), param, len); - bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + hci_skb_pkt_type(skb) = MRVL_VENDOR_PKT; skb_queue_head(&priv->adapter->tx_queue, skb); @@ -387,7 +387,7 @@ static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) skb->data[0] = (skb->len & 0x0000ff); skb->data[1] = (skb->len & 0x00ff00) >> 8; skb->data[2] = (skb->len & 0xff0000) >> 16; - skb->data[3] = bt_cb(skb)->pkt_type; + skb->data[3] = hci_skb_pkt_type(skb); if (priv->hw_host_to_card) ret = priv->hw_host_to_card(priv, skb->data, skb->len); @@ -434,9 +434,9 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct btmrvl_private *priv = hci_get_drvdata(hdev); - BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len); + BT_DBG("type=%d, len=%d", hci_skb_pkt_type(skb), skb->len); - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 71ea2a3af293..d3a4acdf98c9 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -698,7 +698,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) case HCI_ACLDATA_PKT: case HCI_SCODATA_PKT: case HCI_EVENT_PKT: - bt_cb(skb)->pkt_type = type; + hci_skb_pkt_type(skb) = type; skb_put(skb, buf_len); skb_pull(skb, SDIO_HEADER_LEN); @@ -713,7 +713,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) break; case MRVL_VENDOR_PKT: - bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; + hci_skb_pkt_type(skb) = HCI_VENDOR_PKT; skb_put(skb, buf_len); skb_pull(skb, SDIO_HEADER_LEN); diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index 7b624423a7e8..2b05661e3818 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -86,7 +86,7 @@ static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb) skb->data[0] = (skb->len & 0x0000ff); skb->data[1] = (skb->len & 0x00ff00) >> 8; skb->data[2] = (skb->len & 0xff0000) >> 16; - skb->data[3] = bt_cb(skb)->pkt_type; + skb->data[3] = hci_skb_pkt_type(skb); err = sdio_writesb(data->func, REG_TDAT, skb->data, skb->len); if (err < 0) { @@ -158,7 +158,7 @@ static int btsdio_rx_packet(struct btsdio_data *data) data->hdev->stat.byte_rx += len; - bt_cb(skb)->pkt_type = hdr[3]; + hci_skb_pkt_type(skb) = hdr[3]; err = hci_recv_frame(data->hdev, skb); if (err < 0) @@ -252,7 +252,7 @@ static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index bb8e4025fb9e..9624b29f8349 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -200,9 +200,9 @@ static void btuart_receive(struct btuart_info *info) if (info->rx_state == RECV_WAIT_PACKET_TYPE) { - bt_cb(info->rx_skb)->pkt_type = inb(iobase + UART_RX); + hci_skb_pkt_type(info->rx_skb) = inb(iobase + UART_RX); - switch (bt_cb(info->rx_skb)->pkt_type) { + switch (hci_skb_pkt_type(info->rx_skb)) { case HCI_EVENT_PKT: info->rx_state = RECV_WAIT_EVENT_HEADER; @@ -221,7 +221,8 @@ static void btuart_receive(struct btuart_info *info) default: /* Unknown packet */ - BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); + BT_ERR("Unknown HCI packet with type 0x%02x received", + hci_skb_pkt_type(info->rx_skb)); info->hdev->stat.err_rx++; kfree_skb(info->rx_skb); @@ -424,7 +425,7 @@ static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct btuart_info *info = hci_get_drvdata(hdev); - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; @@ -437,7 +438,7 @@ static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) } /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); skb_queue_tail(&(info->txq), skb); btuart_write_wakeup(info); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 92f0ee388f9e..806353410eb7 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -437,22 +437,22 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count) break; } - bt_cb(skb)->pkt_type = HCI_EVENT_PKT; - bt_cb(skb)->expect = HCI_EVENT_HDR_SIZE; + hci_skb_pkt_type(skb) = HCI_EVENT_PKT; + hci_skb_expect(skb) = HCI_EVENT_HDR_SIZE; } - len = min_t(uint, bt_cb(skb)->expect, count); + len = min_t(uint, hci_skb_expect(skb), count); memcpy(skb_put(skb, len), buffer, len); count -= len; buffer += len; - bt_cb(skb)->expect -= len; + hci_skb_expect(skb) -= len; if (skb->len == HCI_EVENT_HDR_SIZE) { /* Complete event header */ - bt_cb(skb)->expect = hci_event_hdr(skb)->plen; + hci_skb_expect(skb) = hci_event_hdr(skb)->plen; - if (skb_tailroom(skb) < bt_cb(skb)->expect) { + if (skb_tailroom(skb) < hci_skb_expect(skb)) { kfree_skb(skb); skb = NULL; @@ -461,7 +461,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count) } } - if (bt_cb(skb)->expect == 0) { + if (!hci_skb_expect(skb)) { /* Complete frame */ data->recv_event(data->hdev, skb); skb = NULL; @@ -492,24 +492,24 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count) break; } - bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - bt_cb(skb)->expect = HCI_ACL_HDR_SIZE; + hci_skb_pkt_type(skb) = HCI_ACLDATA_PKT; + hci_skb_expect(skb) = HCI_ACL_HDR_SIZE; } - len = min_t(uint, bt_cb(skb)->expect, count); + len = min_t(uint, hci_skb_expect(skb), count); memcpy(skb_put(skb, len), buffer, len); count -= len; buffer += len; - bt_cb(skb)->expect -= len; + hci_skb_expect(skb) -= len; if (skb->len == HCI_ACL_HDR_SIZE) { __le16 dlen = hci_acl_hdr(skb)->dlen; /* Complete ACL header */ - bt_cb(skb)->expect = __le16_to_cpu(dlen); + hci_skb_expect(skb) = __le16_to_cpu(dlen); - if (skb_tailroom(skb) < bt_cb(skb)->expect) { + if (skb_tailroom(skb) < hci_skb_expect(skb)) { kfree_skb(skb); skb = NULL; @@ -518,7 +518,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count) } } - if (bt_cb(skb)->expect == 0) { + if (!hci_skb_expect(skb)) { /* Complete frame */ hci_recv_frame(data->hdev, skb); skb = NULL; @@ -549,22 +549,22 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count) break; } - bt_cb(skb)->pkt_type = HCI_SCODATA_PKT; - bt_cb(skb)->expect = HCI_SCO_HDR_SIZE; + hci_skb_pkt_type(skb) = HCI_SCODATA_PKT; + hci_skb_expect(skb) = HCI_SCO_HDR_SIZE; } - len = min_t(uint, bt_cb(skb)->expect, count); + len = min_t(uint, hci_skb_expect(skb), count); memcpy(skb_put(skb, len), buffer, len); count -= len; buffer += len; - bt_cb(skb)->expect -= len; + hci_skb_expect(skb) -= len; if (skb->len == HCI_SCO_HDR_SIZE) { /* Complete SCO header */ - bt_cb(skb)->expect = hci_sco_hdr(skb)->dlen; + hci_skb_expect(skb) = hci_sco_hdr(skb)->dlen; - if (skb_tailroom(skb) < bt_cb(skb)->expect) { + if (skb_tailroom(skb) < hci_skb_expect(skb)) { kfree_skb(skb); skb = NULL; @@ -573,7 +573,7 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count) } } - if (bt_cb(skb)->expect == 0) { + if (!hci_skb_expect(skb)) { /* Complete frame */ hci_recv_frame(data->hdev, skb); skb = NULL; @@ -1257,7 +1257,7 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: urb = alloc_ctrl_urb(hdev, skb); if (IS_ERR(urb)) @@ -1853,7 +1853,7 @@ static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode) *skb_put(skb, 1) = 0x00; - bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + hci_skb_pkt_type(skb) = HCI_EVENT_PKT; return hci_recv_frame(hdev, skb); } @@ -1945,7 +1945,7 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s", hdev->name); - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: if (test_bit(BTUSB_BOOTLOADER, &data->flags)) { struct hci_command_hdr *cmd = (void *)skb->data; diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c index 57eb935aedc7..24a652f9252b 100644 --- a/drivers/bluetooth/btwilink.c +++ b/drivers/bluetooth/btwilink.c @@ -249,10 +249,10 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb) hst = hci_get_drvdata(hdev); /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); - BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, - skb->len); + BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb), + skb->len); /* Insert skb to shared transport layer's transmit queue. * Freeing skb memory is taken care in shared transport layer, @@ -268,7 +268,7 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb) /* ST accepted our skb. So, Go ahead and do rest */ hdev->stat.byte_tx += len; - ti_st_tx_complete(hst, bt_cb(skb)->pkt_type); + ti_st_tx_complete(hst, hci_skb_pkt_type(skb)); return 0; } diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 5026f66fac88..6317c6f323bf 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -239,7 +239,7 @@ static void dtl1_receive(struct dtl1_info *info) info->rx_count = nsh->len + (nsh->len & 0x0001); break; case RECV_WAIT_DATA: - bt_cb(info->rx_skb)->pkt_type = nsh->type; + hci_skb_pkt_type(info->rx_skb) = nsh->type; /* remove PAD byte if it exists */ if (nsh->len & 0x0001) { @@ -250,7 +250,7 @@ static void dtl1_receive(struct dtl1_info *info) /* remove NSH */ skb_pull(info->rx_skb, NSHL); - switch (bt_cb(info->rx_skb)->pkt_type) { + switch (hci_skb_pkt_type(info->rx_skb)) { case 0x80: /* control data for the Nokia Card */ dtl1_control(info, info->rx_skb); @@ -259,12 +259,13 @@ static void dtl1_receive(struct dtl1_info *info) case 0x83: case 0x84: /* send frame to the HCI layer */ - bt_cb(info->rx_skb)->pkt_type &= 0x0f; + hci_skb_pkt_type(info->rx_skb) &= 0x0f; hci_recv_frame(info->hdev, info->rx_skb); break; default: /* unknown packet */ - BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type); + BT_ERR("Unknown HCI packet with type 0x%02x received", + hci_skb_pkt_type(info->rx_skb)); kfree_skb(info->rx_skb); break; } @@ -386,7 +387,7 @@ static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) struct sk_buff *s; struct nsh nsh; - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; nsh.type = 0x81; diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c index d776dfd51478..0ccf6bf01ed4 100644 --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -205,7 +205,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct ath_struct *ath = hu->priv; - if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { + if (hci_skb_pkt_type(skb) == HCI_SCODATA_PKT) { kfree_skb(skb); return 0; } @@ -213,7 +213,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) /* Update power management enable flag with parameters of * HCI sleep enable vendor specific HCI command. */ - if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { + if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT) { struct hci_command_hdr *hdr = (void *)skb->data; if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP) @@ -223,7 +223,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) BT_DBG("hu %p skb %p", hu, skb); /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); skb_queue_tail(&ath->txq, skb); set_bit(HCI_UART_SENDING, &hu->tx_state); diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index cb852cc750b7..3eed35e0207c 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -472,7 +472,7 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb) bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb); /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); skb_queue_tail(&bcm->txq, skb); return 0; diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index d0b615a932d1..064f2fefad62 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -155,7 +155,7 @@ static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb) return 0; } - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_ACLDATA_PKT: case HCI_COMMAND_PKT: skb_queue_tail(&bcsp->rel, skb); @@ -231,7 +231,7 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, if (!nskb) return NULL; - bt_cb(nskb)->pkt_type = pkt_type; + hci_skb_pkt_type(nskb) = pkt_type; bcsp_slip_msgdelim(nskb); @@ -291,7 +291,10 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu) skb = skb_dequeue(&bcsp->unrel); if (skb != NULL) { - struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type); + struct sk_buff *nskb; + + nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, + hci_skb_pkt_type(skb)); if (nskb) { kfree_skb(skb); return nskb; @@ -310,8 +313,10 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu) if (bcsp->unack.qlen < BCSP_TXWINSIZE) { skb = skb_dequeue(&bcsp->rel); if (skb != NULL) { - struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, - bt_cb(skb)->pkt_type); + struct sk_buff *nskb; + + nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, + hci_skb_pkt_type(skb)); if (nskb) { __skb_queue_tail(&bcsp->unack, skb); mod_timer(&bcsp->tbcsp, jiffies + HZ / 4); @@ -412,7 +417,7 @@ static void bcsp_handle_le_pkt(struct hci_uart *hu) if (!nskb) return; memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4); - bt_cb(nskb)->pkt_type = BCSP_LE_PKT; + hci_skb_pkt_type(nskb) = BCSP_LE_PKT; skb_queue_head(&bcsp->unrel, nskb); hci_uart_tx_wakeup(hu); @@ -494,14 +499,14 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu) bcsp_pkt_cull(bcsp); if ((bcsp->rx_skb->data[1] & 0x0f) == 6 && bcsp->rx_skb->data[0] & 0x80) { - bt_cb(bcsp->rx_skb)->pkt_type = HCI_ACLDATA_PKT; + hci_skb_pkt_type(bcsp->rx_skb) = HCI_ACLDATA_PKT; pass_up = 1; } else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 && bcsp->rx_skb->data[0] & 0x80) { - bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT; + hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT; pass_up = 1; } else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) { - bt_cb(bcsp->rx_skb)->pkt_type = HCI_SCODATA_PKT; + hci_skb_pkt_type(bcsp->rx_skb) = HCI_SCODATA_PKT; pass_up = 1; } else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 && !(bcsp->rx_skb->data[0] & 0x80)) { @@ -523,7 +528,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu) hdr.evt = 0xff; hdr.plen = bcsp->rx_skb->len; memcpy(skb_push(bcsp->rx_skb, HCI_EVENT_HDR_SIZE), &hdr, HCI_EVENT_HDR_SIZE); - bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT; + hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT; hci_recv_frame(hu->hdev, bcsp->rx_skb); } else { diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index a6fce48da0fb..635597b6e168 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -108,7 +108,7 @@ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb) BT_DBG("hu %p skb %p", hu, skb); /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); skb_queue_tail(&h4->txq, skb); return 0; @@ -184,8 +184,8 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, if (!skb) return ERR_PTR(-ENOMEM); - bt_cb(skb)->pkt_type = (&pkts[i])->type; - bt_cb(skb)->expect = (&pkts[i])->hlen; + hci_skb_pkt_type(skb) = (&pkts[i])->type; + hci_skb_expect(skb) = (&pkts[i])->hlen; break; } @@ -197,18 +197,18 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, buffer += 1; } - len = min_t(uint, bt_cb(skb)->expect - skb->len, count); + len = min_t(uint, hci_skb_expect(skb) - skb->len, count); memcpy(skb_put(skb, len), buffer, len); count -= len; buffer += len; /* Check for partial packet */ - if (skb->len < bt_cb(skb)->expect) + if (skb->len < hci_skb_expect(skb)) continue; for (i = 0; i < pkts_count; i++) { - if (bt_cb(skb)->pkt_type == (&pkts[i])->type) + if (hci_skb_pkt_type(skb) == (&pkts[i])->type) break; } @@ -228,7 +228,7 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, case 1: /* Single octet variable length */ dlen = skb->data[(&pkts[i])->loff]; - bt_cb(skb)->expect += dlen; + hci_skb_expect(skb) += dlen; if (skb_tailroom(skb) < dlen) { kfree_skb(skb); @@ -239,7 +239,7 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, /* Double octet variable length */ dlen = get_unaligned_le16(skb->data + (&pkts[i])->loff); - bt_cb(skb)->expect += dlen; + hci_skb_expect(skb) += dlen; if (skb_tailroom(skb) < dlen) { kfree_skb(skb); diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index abee2216fdeb..ebefe5eb6b71 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -107,7 +107,7 @@ static void h5_link_control(struct hci_uart *hu, const void *data, size_t len) if (!nskb) return; - bt_cb(nskb)->pkt_type = HCI_3WIRE_LINK_PKT; + hci_skb_pkt_type(nskb) = HCI_3WIRE_LINK_PKT; memcpy(skb_put(nskb, len), data, len); @@ -360,7 +360,7 @@ static void h5_complete_rx_pkt(struct hci_uart *hu) case HCI_EVENT_PKT: case HCI_ACLDATA_PKT: case HCI_SCODATA_PKT: - bt_cb(h5->rx_skb)->pkt_type = H5_HDR_PKT_TYPE(hdr); + hci_skb_pkt_type(h5->rx_skb) = H5_HDR_PKT_TYPE(hdr); /* Remove Three-wire header */ skb_pull(h5->rx_skb, 4); @@ -562,7 +562,7 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb) return 0; } - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_ACLDATA_PKT: case HCI_COMMAND_PKT: skb_queue_tail(&h5->rel, skb); @@ -573,7 +573,7 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb) break; default: - BT_ERR("Unknown packet type %u", bt_cb(skb)->pkt_type); + BT_ERR("Unknown packet type %u", hci_skb_pkt_type(skb)); kfree_skb(skb); break; } @@ -642,7 +642,7 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type, if (!nskb) return NULL; - bt_cb(nskb)->pkt_type = pkt_type; + hci_skb_pkt_type(nskb) = pkt_type; h5_slip_delim(nskb); @@ -697,7 +697,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu) skb = skb_dequeue(&h5->unrel); if (skb) { - nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type, + nskb = h5_prepare_pkt(hu, hci_skb_pkt_type(skb), skb->data, skb->len); if (nskb) { kfree_skb(skb); @@ -715,7 +715,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu) skb = skb_dequeue(&h5->rel); if (skb) { - nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type, + nskb = h5_prepare_pkt(hu, hci_skb_pkt_type(skb), skb->data, skb->len); if (nskb) { __skb_queue_tail(&h5->unack, skb); diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index 4a414a5a3165..69760e2850d9 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -186,7 +186,7 @@ static int intel_lpm_suspend(struct hci_uart *hu) } memcpy(skb_put(skb, sizeof(suspend)), suspend, sizeof(suspend)); - bt_cb(skb)->pkt_type = HCI_LPM_PKT; + hci_skb_pkt_type(skb) = HCI_LPM_PKT; set_bit(STATE_LPM_TRANSACTION, &intel->flags); @@ -230,7 +230,7 @@ static int intel_lpm_resume(struct hci_uart *hu) return -ENOMEM; } - bt_cb(skb)->pkt_type = HCI_LPM_WAKE_PKT; + hci_skb_pkt_type(skb) = HCI_LPM_WAKE_PKT; set_bit(STATE_LPM_TRANSACTION, &intel->flags); @@ -272,7 +272,7 @@ static int intel_lpm_host_wake(struct hci_uart *hu) memcpy(skb_put(skb, sizeof(lpm_resume_ack)), lpm_resume_ack, sizeof(lpm_resume_ack)); - bt_cb(skb)->pkt_type = HCI_LPM_PKT; + hci_skb_pkt_type(skb) = HCI_LPM_PKT; /* LPM flow is a priority, enqueue packet at list head */ skb_queue_head(&intel->txq, skb); @@ -467,7 +467,7 @@ static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode) *skb_put(skb, 1) = 0x00; - bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + hci_skb_pkt_type(skb) = HCI_EVENT_PKT; return hci_recv_frame(hdev, skb); } @@ -517,7 +517,7 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed) } memcpy(skb_put(skb, sizeof(speed_cmd)), speed_cmd, sizeof(speed_cmd)); - bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; hci_uart_set_flow_control(hu, true); @@ -1126,7 +1126,7 @@ static struct sk_buff *intel_dequeue(struct hci_uart *hu) return skb; if (test_bit(STATE_BOOTLOADER, &intel->flags) && - (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT)) { + (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT)) { struct hci_command_hdr *cmd = (void *)skb->data; __u16 opcode = le16_to_cpu(cmd->opcode); @@ -1140,7 +1140,7 @@ static struct sk_buff *intel_dequeue(struct hci_uart *hu) } /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); return skb; } diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 96bcec5598c2..03146d707a95 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -162,7 +162,7 @@ restart: break; } - hci_uart_tx_complete(hu, bt_cb(skb)->pkt_type); + hci_uart_tx_complete(hu, hci_skb_pkt_type(skb)); kfree_skb(skb); } @@ -248,7 +248,8 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_uart *hu = hci_get_drvdata(hdev); - BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); + BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb), + skb->len); hu->proto->enqueue(hu, skb); diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index 9ee24b075f79..02692fe30279 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -307,7 +307,7 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb) BT_DBG("hu %p skb %p", hu, skb); /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); /* lock hcill state */ spin_lock_irqsave(&ll->hcill_lock, flags); @@ -493,7 +493,7 @@ static int ll_recv(struct hci_uart *hu, const void *data, int count) return -ENOMEM; } - bt_cb(ll->rx_skb)->pkt_type = type; + hci_skb_pkt_type(ll->rx_skb) = type; } return count; diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 71325e443e46..683c2b642057 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -678,7 +678,7 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb) qca->tx_ibs_state); /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); /* Don't go to sleep in middle of patch download or * Out-Of-Band(GPIOs control) sleep is selected. @@ -873,7 +873,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) /* Assign commands to change baudrate and packet type. */ memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd)); - bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; skb_queue_tail(&qca->txq, skb); hci_uart_tx_wakeup(hu); diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index ed888e302bc3..80783dcb7f57 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -80,7 +80,7 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct vhci_data *data = hci_get_drvdata(hdev); - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); skb_queue_tail(&data->readq, skb); wake_up_interruptible(&data->read_wait); @@ -140,7 +140,7 @@ static int vhci_create_device(struct vhci_data *data, __u8 opcode) return -EBUSY; } - bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; + hci_skb_pkt_type(skb) = HCI_VENDOR_PKT; *skb_put(skb, 1) = 0xff; *skb_put(skb, 1) = opcode; @@ -183,7 +183,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data, return -ENODEV; } - bt_cb(skb)->pkt_type = pkt_type; + hci_skb_pkt_type(skb) = pkt_type; ret = hci_recv_frame(data->hdev, skb); break; @@ -234,7 +234,7 @@ static inline ssize_t vhci_put_user(struct vhci_data *data, data->hdev->stat.byte_tx += len; - switch (bt_cb(skb)->pkt_type) { + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: data->hdev->stat.cmd_tx++; break; -- cgit v1.2.3-59-g8ed1b From 4223f368b96893c843653e15a3f57c830ed44169 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 19 Nov 2015 11:29:11 +0200 Subject: Bluetooth: Fix mask for H5 header len Fixes mask when calculating three-wire (h5) length. Signed-off-by: Andrei Emeltchenko Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index ebefe5eb6b71..012248efaf6f 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -51,7 +51,7 @@ #define H5_HDR_CRC(hdr) (((hdr)[0] >> 6) & 0x01) #define H5_HDR_RELIABLE(hdr) (((hdr)[0] >> 7) & 0x01) #define H5_HDR_PKT_TYPE(hdr) ((hdr)[1] & 0x0f) -#define H5_HDR_LEN(hdr) ((((hdr)[1] >> 4) & 0xff) + ((hdr)[2] << 4)) +#define H5_HDR_LEN(hdr) ((((hdr)[1] >> 4) & 0x0f) + ((hdr)[2] << 4)) #define SLIP_DELIMITER 0xc0 #define SLIP_ESC 0xdb -- cgit v1.2.3-59-g8ed1b From e9d63767258753f28920d954712806a061b69547 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 19 Nov 2015 16:47:38 +0200 Subject: Bluetooth: Use hex notation for mask This makes it easier to read and makes code consistent. Signed-off-by: Andrei Emeltchenko Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 012248efaf6f..db039f2ce655 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -119,7 +119,7 @@ static u8 h5_cfg_field(struct h5 *h5) u8 field = 0; /* Sliding window size (first 3 bits) */ - field |= (h5->tx_win & 7); + field |= (h5->tx_win & 0x07); return field; } -- cgit v1.2.3-59-g8ed1b From 7b5dc0dd59a8e5cf837c941ab10a565dcca76603 Mon Sep 17 00:00:00 2001 From: Jon Ringle Date: Wed, 18 Nov 2015 16:22:21 -0500 Subject: net: encx24j600: move rev announcement to probe function When encx24j600 is open and closed many times due to userspace polling the interface, the log gets noise with this log message. Moving this to encx24j600_spi_probe function where it belongs. Signed-off-by: Jon Ringle Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/encx24j600.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c index 2056b719c262..7df318346b05 100644 --- a/drivers/net/ethernet/microchip/encx24j600.c +++ b/drivers/net/ethernet/microchip/encx24j600.c @@ -600,22 +600,11 @@ static void encx24j600_set_rxfilter_mode(struct encx24j600_priv *priv) static int encx24j600_hw_init(struct encx24j600_priv *priv) { - struct net_device *dev = priv->ndev; int ret = 0; - u16 eidled; u16 macon2; priv->hw_enabled = false; - eidled = encx24j600_read_reg(priv, EIDLED); - if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) { - ret = -EINVAL; - goto err_out; - } - - netif_info(priv, drv, dev, "Silicon rev ID: 0x%02x\n", - (eidled & REVID_MASK) >> REVID_SHIFT); - /* PHY Leds: link status, * LEDA: Link State + collision events * LEDB: Link State + transmit/receive events @@ -655,7 +644,6 @@ static int encx24j600_hw_init(struct encx24j600_priv *priv) if (netif_msg_hw(priv)) encx24j600_dump_config(priv, "Hw is initialized"); -err_out: return ret; } @@ -1004,6 +992,7 @@ static int encx24j600_spi_probe(struct spi_device *spi) struct net_device *ndev; struct encx24j600_priv *priv; + u16 eidled; ndev = alloc_etherdev(sizeof(struct encx24j600_priv)); @@ -1072,10 +1061,21 @@ static int encx24j600_spi_probe(struct spi_device *spi) goto out_free; } + eidled = encx24j600_read_reg(priv, EIDLED); + if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) { + ret = -EINVAL; + goto out_unregister; + } + + netif_info(priv, probe, ndev, "Silicon rev ID: 0x%02x\n", + (eidled & REVID_MASK) >> REVID_SHIFT); + netif_info(priv, drv, priv->ndev, "MAC address %pM\n", ndev->dev_addr); return ret; +out_unregister: + unregister_netdev(priv->ndev); out_free: free_netdev(ndev); -- cgit v1.2.3-59-g8ed1b From 06c071f68d8123bef0f152e5d2c7272920597a7e Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 19 Nov 2015 12:27:38 +0100 Subject: mlxsw: spectrum: Use correct PVID value when removing VLANs When removing a range of VLANs in which PVID is a member we should use the correct PVID value instead of some VLAN in the range. Also, change two print statements to use 'dev' instead of 'mlxsw_sp_port->dev', as it's already used in other print statements in the function. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 617fb22b5d81..be6339859451 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -532,7 +532,7 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin, u16 vid_end, bool init) { struct net_device *dev = mlxsw_sp_port->dev; - u16 vid, vid_e; + u16 vid, vid_e, pvid; int err; /* In case this is invoked with BRIDGE_FLAGS_SELF and port is @@ -549,23 +549,21 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, false, false); if (err) { - netdev_err(mlxsw_sp_port->dev, "Unable to del VIDs %d-%d\n", - vid, vid_e); + netdev_err(dev, "Unable to del VIDs %d-%d\n", vid, + vid_e); return err; } } - if ((mlxsw_sp_port->pvid >= vid_begin) && - (mlxsw_sp_port->pvid <= vid_end)) { + pvid = mlxsw_sp_port->pvid; + if (pvid >= vid_begin && pvid <= vid_end && pvid != 1) { /* Default VLAN is always 1 */ - mlxsw_sp_port->pvid = 1; - err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, - mlxsw_sp_port->pvid); + err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1); if (err) { - netdev_err(mlxsw_sp_port->dev, "Unable to del PVID %d\n", - vid); + netdev_err(dev, "Unable to del PVID %d\n", pvid); return err; } + mlxsw_sp_port->pvid = 1; } if (init) -- cgit v1.2.3-59-g8ed1b From 3b7ad5ece49fe72498b50d28253fd39daead1e14 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 19 Nov 2015 12:27:39 +0100 Subject: mlxsw: spectrum: Unify setting of HW VLAN filters When adding or deleting VLANs from a bridged port, HW VLAN filters must be set accordingly. Instead of having the same code in both add and delete functions, just wrap it in a function and call it with the appropriate parameters. Signed-off-by: Ido Schimmel Signed-off-by: Elad Raz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 58 +++++++++++++--------- 1 file changed, 34 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index be6339859451..fe881655a864 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -342,6 +342,27 @@ err_port_add_vid: return err; } +static int __mlxsw_sp_port_vlans_set(struct mlxsw_sp_port *mlxsw_sp_port, + u16 vid_begin, u16 vid_end, bool is_member, + bool untagged) +{ + u16 vid, vid_e; + int err; + + for (vid = vid_begin; vid <= vid_end; + vid += MLXSW_REG_SPVM_REC_MAX_COUNT) { + vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1), + vid_end); + + err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, + is_member, untagged); + if (err) + return err; + } + + return 0; +} + static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin, u16 vid_end, bool flag_untagged, bool flag_pvid) @@ -396,18 +417,12 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, return err; } - for (vid = vid_begin; vid <= vid_end; - vid += MLXSW_REG_SPVM_REC_MAX_COUNT) { - vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1), - vid_end); - - err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, true, - flag_untagged); - if (err) { - netdev_err(mlxsw_sp_port->dev, "Unable to add VIDs %d-%d\n", - vid, vid_e); - return err; - } + err = __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, + true, flag_untagged); + if (err) { + netdev_err(dev, "Unable to add VIDs %d-%d\n", vid_begin, + vid_end); + return err; } vid = vid_begin; @@ -532,7 +547,7 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin, u16 vid_end, bool init) { struct net_device *dev = mlxsw_sp_port->dev; - u16 vid, vid_e, pvid; + u16 vid, pvid; int err; /* In case this is invoked with BRIDGE_FLAGS_SELF and port is @@ -542,17 +557,12 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, if (!init && !mlxsw_sp_port->bridged) return mlxsw_sp_port_kill_vids(dev, vid_begin, vid_end); - for (vid = vid_begin; vid <= vid_end; - vid += MLXSW_REG_SPVM_REC_MAX_COUNT) { - vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1), - vid_end); - err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, false, - false); - if (err) { - netdev_err(dev, "Unable to del VIDs %d-%d\n", vid, - vid_e); - return err; - } + err = __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, + false, false); + if (err) { + netdev_err(dev, "Unable to del VIDs %d-%d\n", vid_begin, + vid_end); + return err; } pvid = mlxsw_sp_port->pvid; -- cgit v1.2.3-59-g8ed1b From b07a966c700761f86306925fe8aedf7d1060fa6e Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 19 Nov 2015 12:27:40 +0100 Subject: mlxsw: spectrum: Add error paths to __mlxsw_sp_port_vlans_add The operation of adding VLANs on a port via switchdev ops can fail and we need to be prepared for it. If we do not rollback hardware operations following a failure, hardware and software will remain in an inconsistent state. Solve that by adding suitable error paths to __mlxsw_sp_port_vlans_add. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 61 ++++++++++++++++------ 1 file changed, 46 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index fe881655a864..f21e23983a1a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -369,8 +369,8 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct net_device *dev = mlxsw_sp_port->dev; + u16 vid, last_visited_vid, old_pvid; enum mlxsw_reg_svfa_mt mt; - u16 vid, vid_e; int err; /* In case this is invoked with BRIDGE_FLAGS_SELF and port is @@ -398,15 +398,18 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, if (err) { netdev_err(dev, "Failed to create FID=VID=%d mapping\n", vid); - return err; + goto err_port_vid_to_fid_set; } } + } - /* Set FID mapping according to port's mode */ + /* Set FID mapping according to port's mode */ + for (vid = vid_begin; vid <= vid_end; vid++) { err = mlxsw_sp_port_fid_map(mlxsw_sp_port, vid); if (err) { netdev_err(dev, "Failed to map FID=%d", vid); - return err; + last_visited_vid = --vid; + goto err_port_fid_map; } } @@ -414,7 +417,7 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, true, false); if (err) { netdev_err(dev, "Failed to configure flooding\n"); - return err; + goto err_port_flood_set; } err = __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, @@ -422,26 +425,54 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, if (err) { netdev_err(dev, "Unable to add VIDs %d-%d\n", vid_begin, vid_end); - return err; + goto err_port_vlans_set; } - vid = vid_begin; - if (flag_pvid && mlxsw_sp_port->pvid != vid) { - err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid); + old_pvid = mlxsw_sp_port->pvid; + if (flag_pvid && old_pvid != vid_begin) { + err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid_begin); if (err) { - netdev_err(mlxsw_sp_port->dev, "Unable to add PVID %d\n", - vid); - return err; + netdev_err(dev, "Unable to add PVID %d\n", vid_begin); + goto err_port_pvid_set; } - mlxsw_sp_port->pvid = vid; + mlxsw_sp_port->pvid = vid_begin; } /* Changing activity bits only if HW operation succeded */ for (vid = vid_begin; vid <= vid_end; vid++) set_bit(vid, mlxsw_sp_port->active_vlans); - return mlxsw_sp_port_stp_state_set(mlxsw_sp_port, - mlxsw_sp_port->stp_state); + /* STP state change must be done after we set active VLANs */ + err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, + mlxsw_sp_port->stp_state); + if (err) { + netdev_err(dev, "Failed to set STP state\n"); + goto err_port_stp_state_set; + } + + return 0; + +err_port_vid_to_fid_set: + mlxsw_sp_fid_destroy(mlxsw_sp, vid); + return err; + +err_port_stp_state_set: + for (vid = vid_begin; vid <= vid_end; vid++) + clear_bit(vid, mlxsw_sp_port->active_vlans); + if (old_pvid != mlxsw_sp_port->pvid) + mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid); +err_port_pvid_set: + __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, false, + false); +err_port_vlans_set: + __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid_begin, vid_end, false, + false); +err_port_flood_set: + last_visited_vid = vid_end; +err_port_fid_map: + for (vid = last_visited_vid; vid >= vid_begin; vid--) + mlxsw_sp_port_fid_unmap(mlxsw_sp_port, vid); + return err; } static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, -- cgit v1.2.3-59-g8ed1b From 8734e485fed5fc5ce8b03aebfc2681e2de662838 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Thu, 19 Nov 2015 12:52:56 +0100 Subject: ppp: don't set sk_state to PPPOX_ZOMBIE in pppoe_disc_rcv() Since 287f3a943fef ("pppoe: Use workqueue to die properly when a PADT is received"), pppoe_disc_rcv() disconnects the socket by scheduling pppoe_unbind_sock_work(). This is enough to stop socket transmission and makes the PPPOX_ZOMBIE state uncessary. Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- drivers/net/ppp/pppoe.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 5e0b43283bce..1dedfbf1d423 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -500,27 +500,9 @@ static int pppoe_disc_rcv(struct sk_buff *skb, struct net_device *dev, pn = pppoe_pernet(dev_net(dev)); po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex); - if (po) { - struct sock *sk = sk_pppox(po); - - bh_lock_sock(sk); - - /* If the user has locked the socket, just ignore - * the packet. With the way two rcv protocols hook into - * one socket family type, we cannot (easily) distinguish - * what kind of SKB it is during backlog rcv. - */ - if (sock_owned_by_user(sk) == 0) { - /* We're no longer connect at the PPPOE layer, - * and must wait for ppp channel to disconnect us. - */ - sk->sk_state = PPPOX_ZOMBIE; - } - - bh_unlock_sock(sk); + if (po) if (!schedule_work(&po->proto.pppoe.padt_work)) - sock_put(sk); - } + sock_put(sk_pppox(po)); abort: kfree_skb(skb); -- cgit v1.2.3-59-g8ed1b From a8acce6aa584aa731a2bed240bcd8dc955f01414 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Thu, 19 Nov 2015 12:53:21 +0100 Subject: ppp: remove PPPOX_ZOMBIE socket state PPPOX_ZOMBIE is never set anymore. Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- drivers/net/ppp/pppoe.c | 4 ++-- drivers/net/ppp/pppox.c | 2 +- include/linux/if_pppox.h | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 1dedfbf1d423..277e6827d7cd 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -311,7 +311,7 @@ static void pppoe_flush_dev(struct net_device *dev) lock_sock(sk); if (po->pppoe_dev == dev && - sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) { + sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { pppox_unbind_sock(sk); sk->sk_state_change(sk); po->pppoe_dev = NULL; @@ -775,7 +775,7 @@ static int pppoe_ioctl(struct socket *sock, unsigned int cmd, struct pppox_sock *relay_po; err = -EBUSY; - if (sk->sk_state & (PPPOX_BOUND | PPPOX_ZOMBIE | PPPOX_DEAD)) + if (sk->sk_state & (PPPOX_BOUND | PPPOX_DEAD)) break; err = -ENOTCONN; diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c index 0e1b30622477..0200de74eebc 100644 --- a/drivers/net/ppp/pppox.c +++ b/drivers/net/ppp/pppox.c @@ -58,7 +58,7 @@ void pppox_unbind_sock(struct sock *sk) { /* Clear connection to ppp device, if attached. */ - if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) { + if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED)) { ppp_unregister_channel(&pppox_sk(sk)->chan); sk->sk_state = PPPOX_DEAD; } diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index b49cf923becc..ba7a9b0c7c57 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -91,7 +91,6 @@ enum { PPPOX_CONNECTED = 1, /* connection established ==TCP_ESTABLISHED */ PPPOX_BOUND = 2, /* bound to ppp device */ PPPOX_RELAY = 4, /* forwarding is enabled */ - PPPOX_ZOMBIE = 8, /* dead, but still bound to ppp device */ PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/ }; -- cgit v1.2.3-59-g8ed1b From 6a5311982e423ddacc2e39e1e32f4371756752d6 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Thu, 19 Nov 2015 17:04:35 +0200 Subject: bnx2x: Add new SW stat 'tx_exhaustion_events' Driver already has an internal counter for number of times a given queue had to be stopped due to Tx ring exhaustion. This add the counter to the statistics presented by driver, e.g., by using `ethtool -S'. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index d84efcd34fac..1dc77ac7cb21 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -52,7 +52,7 @@ static const struct { { Q_STATS_OFFSET32(rx_skb_alloc_failed), 4, "[%s]: rx_skb_alloc_discard" }, { Q_STATS_OFFSET32(hw_csum_err), 4, "[%s]: rx_csum_offload_errors" }, - + { Q_STATS_OFFSET32(driver_xoff), 4, "[%s]: tx_exhaustion_events" }, { Q_STATS_OFFSET32(total_bytes_transmitted_hi), 8, "[%s]: tx_bytes" }, /* 10 */{ Q_STATS_OFFSET32(total_unicast_packets_transmitted_hi), 8, "[%s]: tx_ucast_packets" }, @@ -128,7 +128,8 @@ static const struct { 4, STATS_FLAGS_BOTH, "rx_skb_alloc_discard" }, { STATS_OFFSET32(hw_csum_err), 4, STATS_FLAGS_BOTH, "rx_csum_offload_errors" }, - + { STATS_OFFSET32(driver_xoff), + 4, STATS_FLAGS_BOTH, "tx_exhaustion_events" }, { STATS_OFFSET32(total_bytes_transmitted_hi), 8, STATS_FLAGS_BOTH, "tx_bytes" }, { STATS_OFFSET32(tx_stat_ifhcoutbadoctets_hi), -- cgit v1.2.3-59-g8ed1b From 3fb2d4926c9363791a61404ac4d3b66f17244f00 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Thu, 19 Nov 2015 17:04:36 +0200 Subject: bnx2x: Show port statistics in Multi-function Today, port statistics are being presented when using `ethool -S' only for single-function devices, but there are some port statistics which are crucial for analyzing bottle-necks. E.g., HW Rx discards due to lack of buffer space [when device isn't handling ingress traffic fast enough]. Judging the pros and cons, it was decided that in-order to better support automatic dump-gathering tools, bnx2x should no longer hide those stats. This leaves only VFs lacking the port statistics. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 1dc77ac7cb21..a3ce9f2a2335 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -3069,9 +3069,7 @@ static void bnx2x_self_test(struct net_device *dev, #define IS_PORT_STAT(i) \ ((bnx2x_stats_arr[i].flags & STATS_FLAGS_BOTH) == STATS_FLAGS_PORT) #define IS_FUNC_STAT(i) (bnx2x_stats_arr[i].flags & STATS_FLAGS_FUNC) -#define HIDE_PORT_STAT(bp) \ - ((IS_MF(bp) && !(bp->msg_enable & BNX2X_MSG_STATS)) || \ - IS_VF(bp)) +#define HIDE_PORT_STAT(bp) IS_VF(bp) /* ethtool statistics are displayed for all regular ethernet queues and the * fcoe L2 queue if not disabled -- cgit v1.2.3-59-g8ed1b From 39536ff81e81fef6202612a3ba98aa21fa00c254 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 19 Nov 2015 20:13:04 +0100 Subject: dl2k: Handle memory allocation errors in alloc_list If memory allocation fails in alloc_list(), free the already allocated memory and return -ENOMEM. In rio_open(), call alloc_list() first and abort if it fails. Move HW access (set RFDListPtr) out ot alloc_list(). Signed-off-by: Ondrej Zary Signed-off-by: David S. Miller --- drivers/net/ethernet/dlink/dl2k.c | 182 ++++++++++++++++++++------------------ 1 file changed, 97 insertions(+), 85 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index ccca4799c27b..ec0eb7ff1146 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -70,7 +70,6 @@ static const int multicast_filter_limit = 0x40; static int rio_open (struct net_device *dev); static void rio_timer (unsigned long data); static void rio_tx_timeout (struct net_device *dev); -static void alloc_list (struct net_device *dev); static netdev_tx_t start_xmit (struct sk_buff *skb, struct net_device *dev); static irqreturn_t rio_interrupt (int irq, void *dev_instance); static void rio_free_tx (struct net_device *dev, int irq); @@ -446,6 +445,92 @@ static void rio_set_led_mode(struct net_device *dev) dw32(ASICCtrl, mode); } +static inline dma_addr_t desc_to_dma(struct netdev_desc *desc) +{ + return le64_to_cpu(desc->fraginfo) & DMA_BIT_MASK(48); +} + +static void free_list(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + struct sk_buff *skb; + int i; + + /* Free all the skbuffs in the queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + skb = np->rx_skbuff[i]; + if (skb) { + pci_unmap_single(np->pdev, desc_to_dma(&np->rx_ring[i]), + skb->len, PCI_DMA_FROMDEVICE); + dev_kfree_skb(skb); + np->rx_skbuff[i] = NULL; + } + np->rx_ring[i].status = 0; + np->rx_ring[i].fraginfo = 0; + } + for (i = 0; i < TX_RING_SIZE; i++) { + skb = np->tx_skbuff[i]; + if (skb) { + pci_unmap_single(np->pdev, desc_to_dma(&np->tx_ring[i]), + skb->len, PCI_DMA_TODEVICE); + dev_kfree_skb(skb); + np->tx_skbuff[i] = NULL; + } + } +} + + /* allocate and initialize Tx and Rx descriptors */ +static int alloc_list(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + int i; + + np->cur_rx = np->cur_tx = 0; + np->old_rx = np->old_tx = 0; + np->rx_buf_sz = (dev->mtu <= 1500 ? PACKET_SIZE : dev->mtu + 32); + + /* Initialize Tx descriptors, TFDListPtr leaves in start_xmit(). */ + for (i = 0; i < TX_RING_SIZE; i++) { + np->tx_skbuff[i] = NULL; + np->tx_ring[i].status = cpu_to_le64(TFDDone); + np->tx_ring[i].next_desc = cpu_to_le64(np->tx_ring_dma + + ((i + 1) % TX_RING_SIZE) * + sizeof(struct netdev_desc)); + } + + /* Initialize Rx descriptors */ + for (i = 0; i < RX_RING_SIZE; i++) { + np->rx_ring[i].next_desc = cpu_to_le64(np->rx_ring_dma + + ((i + 1) % RX_RING_SIZE) * + sizeof(struct netdev_desc)); + np->rx_ring[i].status = 0; + np->rx_ring[i].fraginfo = 0; + np->rx_skbuff[i] = NULL; + } + + /* Allocate the rx buffers */ + for (i = 0; i < RX_RING_SIZE; i++) { + /* Allocated fixed size of skbuff */ + struct sk_buff *skb; + + skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz); + np->rx_skbuff[i] = skb; + if (!skb) { + free_list(dev); + return -ENOMEM; + } + + /* Rubicon now supports 40 bits of addressing space. */ + np->rx_ring[i].fraginfo = + cpu_to_le64(pci_map_single( + np->pdev, skb->data, np->rx_buf_sz, + PCI_DMA_FROMDEVICE)); + np->rx_ring[i].fraginfo |= cpu_to_le64((u64)np->rx_buf_sz << 48); + } + + return 0; +} + static int rio_open (struct net_device *dev) { @@ -455,10 +540,16 @@ rio_open (struct net_device *dev) int i; u16 macctrl; - i = request_irq(irq, rio_interrupt, IRQF_SHARED, dev->name, dev); + i = alloc_list(dev); if (i) return i; + i = request_irq(irq, rio_interrupt, IRQF_SHARED, dev->name, dev); + if (i) { + free_list(dev); + return i; + } + /* Reset all logic functions */ dw16(ASICCtrl + 2, GlobalReset | DMAReset | FIFOReset | NetworkReset | HostReset); @@ -473,7 +564,9 @@ rio_open (struct net_device *dev) if (np->jumbo != 0) dw16(MaxFrameSize, MAX_JUMBO+14); - alloc_list (dev); + /* Set RFDListPtr */ + dw32(RFDListPtr0, np->rx_ring_dma); + dw32(RFDListPtr1, 0); /* Set station address */ /* 16 or 32-bit access is required by TC9020 datasheet but 8-bit works @@ -586,60 +679,6 @@ rio_tx_timeout (struct net_device *dev) dev->trans_start = jiffies; /* prevent tx timeout */ } - /* allocate and initialize Tx and Rx descriptors */ -static void -alloc_list (struct net_device *dev) -{ - struct netdev_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->ioaddr; - int i; - - np->cur_rx = np->cur_tx = 0; - np->old_rx = np->old_tx = 0; - np->rx_buf_sz = (dev->mtu <= 1500 ? PACKET_SIZE : dev->mtu + 32); - - /* Initialize Tx descriptors, TFDListPtr leaves in start_xmit(). */ - for (i = 0; i < TX_RING_SIZE; i++) { - np->tx_skbuff[i] = NULL; - np->tx_ring[i].status = cpu_to_le64 (TFDDone); - np->tx_ring[i].next_desc = cpu_to_le64 (np->tx_ring_dma + - ((i+1)%TX_RING_SIZE) * - sizeof (struct netdev_desc)); - } - - /* Initialize Rx descriptors */ - for (i = 0; i < RX_RING_SIZE; i++) { - np->rx_ring[i].next_desc = cpu_to_le64 (np->rx_ring_dma + - ((i + 1) % RX_RING_SIZE) * - sizeof (struct netdev_desc)); - np->rx_ring[i].status = 0; - np->rx_ring[i].fraginfo = 0; - np->rx_skbuff[i] = NULL; - } - - /* Allocate the rx buffers */ - for (i = 0; i < RX_RING_SIZE; i++) { - /* Allocated fixed size of skbuff */ - struct sk_buff *skb; - - skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz); - np->rx_skbuff[i] = skb; - if (skb == NULL) - break; - - /* Rubicon now supports 40 bits of addressing space. */ - np->rx_ring[i].fraginfo = - cpu_to_le64 ( pci_map_single ( - np->pdev, skb->data, np->rx_buf_sz, - PCI_DMA_FROMDEVICE)); - np->rx_ring[i].fraginfo |= cpu_to_le64((u64)np->rx_buf_sz << 48); - } - - /* Set RFDListPtr */ - dw32(RFDListPtr0, np->rx_ring_dma); - dw32(RFDListPtr1, 0); -} - static netdev_tx_t start_xmit (struct sk_buff *skb, struct net_device *dev) { @@ -748,11 +787,6 @@ rio_interrupt (int irq, void *dev_instance) return IRQ_RETVAL(handled); } -static inline dma_addr_t desc_to_dma(struct netdev_desc *desc) -{ - return le64_to_cpu(desc->fraginfo) & DMA_BIT_MASK(48); -} - static void rio_free_tx (struct net_device *dev, int irq) { @@ -1733,8 +1767,6 @@ rio_close (struct net_device *dev) void __iomem *ioaddr = np->ioaddr; struct pci_dev *pdev = np->pdev; - struct sk_buff *skb; - int i; netif_stop_queue (dev); @@ -1747,27 +1779,7 @@ rio_close (struct net_device *dev) free_irq(pdev->irq, dev); del_timer_sync (&np->timer); - /* Free all the skbuffs in the queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - skb = np->rx_skbuff[i]; - if (skb) { - pci_unmap_single(pdev, desc_to_dma(&np->rx_ring[i]), - skb->len, PCI_DMA_FROMDEVICE); - dev_kfree_skb (skb); - np->rx_skbuff[i] = NULL; - } - np->rx_ring[i].status = 0; - np->rx_ring[i].fraginfo = 0; - } - for (i = 0; i < TX_RING_SIZE; i++) { - skb = np->tx_skbuff[i]; - if (skb) { - pci_unmap_single(pdev, desc_to_dma(&np->tx_ring[i]), - skb->len, PCI_DMA_TODEVICE); - dev_kfree_skb (skb); - np->tx_skbuff[i] = NULL; - } - } + free_list(dev); return 0; } -- cgit v1.2.3-59-g8ed1b From 966e07f4bf11f9fcce57c706903defe86516344c Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 19 Nov 2015 20:13:05 +0100 Subject: dl2k: Reorder and cleanup initialization Move HW init and stop into separate functions. Request IRQ only after the HW has been reset (so interrupts are disabled and no stale interrupts are pending). Signed-off-by: Ondrej Zary Signed-off-by: David S. Miller --- drivers/net/ethernet/dlink/dl2k.c | 95 +++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index ec0eb7ff1146..9e9baa0b87e5 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -252,19 +252,6 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto err_out_unmap_rx; - if (np->chip_id == CHIP_IP1000A && - (np->pdev->revision == 0x40 || np->pdev->revision == 0x41)) { - /* PHY magic taken from ipg driver, undocumented registers */ - mii_write(dev, np->phy_addr, 31, 0x0001); - mii_write(dev, np->phy_addr, 27, 0x01e0); - mii_write(dev, np->phy_addr, 31, 0x0002); - mii_write(dev, np->phy_addr, 27, 0xeb8e); - mii_write(dev, np->phy_addr, 31, 0x0000); - mii_write(dev, np->phy_addr, 30, 0x005e); - /* advertise 1000BASE-T half & full duplex, prefer MASTER */ - mii_write(dev, np->phy_addr, MII_CTRL1000, 0x0700); - } - /* Fiber device? */ np->phy_media = (dr16(ASICCtrl) & PhyMedia) ? 1 : 0; np->link_status = 0; @@ -274,13 +261,11 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) if (np->an_enable == 2) { np->an_enable = 1; } - mii_set_media_pcs (dev); } else { /* Auto-Negotiation is mandatory for 1000BASE-T, IEEE 802.3ab Annex 28D page 14 */ if (np->speed == 1000) np->an_enable = 1; - mii_set_media (dev); } err = register_netdev (dev); @@ -531,25 +516,13 @@ static int alloc_list(struct net_device *dev) return 0; } -static int -rio_open (struct net_device *dev) +static void rio_hw_init(struct net_device *dev) { struct netdev_private *np = netdev_priv(dev); void __iomem *ioaddr = np->ioaddr; - const int irq = np->pdev->irq; int i; u16 macctrl; - i = alloc_list(dev); - if (i) - return i; - - i = request_irq(irq, rio_interrupt, IRQF_SHARED, dev->name, dev); - if (i) { - free_list(dev); - return i; - } - /* Reset all logic functions */ dw16(ASICCtrl + 2, GlobalReset | DMAReset | FIFOReset | NetworkReset | HostReset); @@ -560,6 +533,24 @@ rio_open (struct net_device *dev) /* DebugCtrl bit 4, 5, 9 must set */ dw32(DebugCtrl, dr32(DebugCtrl) | 0x0230); + if (np->chip_id == CHIP_IP1000A && + (np->pdev->revision == 0x40 || np->pdev->revision == 0x41)) { + /* PHY magic taken from ipg driver, undocumented registers */ + mii_write(dev, np->phy_addr, 31, 0x0001); + mii_write(dev, np->phy_addr, 27, 0x01e0); + mii_write(dev, np->phy_addr, 31, 0x0002); + mii_write(dev, np->phy_addr, 27, 0xeb8e); + mii_write(dev, np->phy_addr, 31, 0x0000); + mii_write(dev, np->phy_addr, 30, 0x005e); + /* advertise 1000BASE-T half & full duplex, prefer MASTER */ + mii_write(dev, np->phy_addr, MII_CTRL1000, 0x0700); + } + + if (np->phy_media) + mii_set_media_pcs(dev); + else + mii_set_media(dev); + /* Jumbo frame */ if (np->jumbo != 0) dw16(MaxFrameSize, MAX_JUMBO+14); @@ -602,10 +593,6 @@ rio_open (struct net_device *dev) dw32(MACCtrl, dr32(MACCtrl) | AutoVLANuntagging); } - setup_timer(&np->timer, rio_timer, (unsigned long)dev); - np->timer.expires = jiffies + 1*HZ; - add_timer (&np->timer); - /* Start Tx/Rx */ dw32(MACCtrl, dr32(MACCtrl) | StatsEnable | RxEnable | TxEnable); @@ -615,6 +602,42 @@ rio_open (struct net_device *dev) macctrl |= (np->tx_flow) ? TxFlowControlEnable : 0; macctrl |= (np->rx_flow) ? RxFlowControlEnable : 0; dw16(MACCtrl, macctrl); +} + +static void rio_hw_stop(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + void __iomem *ioaddr = np->ioaddr; + + /* Disable interrupts */ + dw16(IntEnable, 0); + + /* Stop Tx and Rx logics */ + dw32(MACCtrl, TxDisable | RxDisable | StatsDisable); +} + +static int rio_open(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + const int irq = np->pdev->irq; + int i; + + i = alloc_list(dev); + if (i) + return i; + + rio_hw_init(dev); + + i = request_irq(irq, rio_interrupt, IRQF_SHARED, dev->name, dev); + if (i) { + rio_hw_stop(dev); + free_list(dev); + return i; + } + + setup_timer(&np->timer, rio_timer, (unsigned long)dev); + np->timer.expires = jiffies + 1 * HZ; + add_timer(&np->timer); netif_start_queue (dev); @@ -1764,17 +1787,11 @@ static int rio_close (struct net_device *dev) { struct netdev_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->ioaddr; - struct pci_dev *pdev = np->pdev; netif_stop_queue (dev); - /* Disable interrupts */ - dw16(IntEnable, 0); - - /* Stop Tx and Rx logics */ - dw32(MACCtrl, TxDisable | RxDisable | StatsDisable); + rio_hw_stop(dev); free_irq(pdev->irq, dev); del_timer_sync (&np->timer); -- cgit v1.2.3-59-g8ed1b From 1777ddb84a543444d44594d437b0f3d3f8734f32 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Thu, 19 Nov 2015 20:13:06 +0100 Subject: dl2k: Implement suspend Add suspend/resume support to dl2k driver. This requires RX/TX rings to be reset so split out the required functionality from alloc_list() into new rio_reset_ring(). Tested on Asus NX1101 (IP1000A) and D-Link DGE-550T (DL-2000). Signed-off-by: Ondrej Zary Signed-off-by: David S. Miller --- drivers/net/ethernet/dlink/dl2k.c | 79 ++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index 9e9baa0b87e5..f92b6d948398 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -464,36 +464,40 @@ static void free_list(struct net_device *dev) } } +static void rio_reset_ring(struct netdev_private *np) +{ + int i; + + np->cur_rx = 0; + np->cur_tx = 0; + np->old_rx = 0; + np->old_tx = 0; + + for (i = 0; i < TX_RING_SIZE; i++) + np->tx_ring[i].status = cpu_to_le64(TFDDone); + + for (i = 0; i < RX_RING_SIZE; i++) + np->rx_ring[i].status = 0; +} + /* allocate and initialize Tx and Rx descriptors */ static int alloc_list(struct net_device *dev) { struct netdev_private *np = netdev_priv(dev); int i; - np->cur_rx = np->cur_tx = 0; - np->old_rx = np->old_tx = 0; + rio_reset_ring(np); np->rx_buf_sz = (dev->mtu <= 1500 ? PACKET_SIZE : dev->mtu + 32); /* Initialize Tx descriptors, TFDListPtr leaves in start_xmit(). */ for (i = 0; i < TX_RING_SIZE; i++) { np->tx_skbuff[i] = NULL; - np->tx_ring[i].status = cpu_to_le64(TFDDone); np->tx_ring[i].next_desc = cpu_to_le64(np->tx_ring_dma + ((i + 1) % TX_RING_SIZE) * sizeof(struct netdev_desc)); } - /* Initialize Rx descriptors */ - for (i = 0; i < RX_RING_SIZE; i++) { - np->rx_ring[i].next_desc = cpu_to_le64(np->rx_ring_dma + - ((i + 1) % RX_RING_SIZE) * - sizeof(struct netdev_desc)); - np->rx_ring[i].status = 0; - np->rx_ring[i].fraginfo = 0; - np->rx_skbuff[i] = NULL; - } - - /* Allocate the rx buffers */ + /* Initialize Rx descriptors & allocate buffers */ for (i = 0; i < RX_RING_SIZE; i++) { /* Allocated fixed size of skbuff */ struct sk_buff *skb; @@ -505,6 +509,9 @@ static int alloc_list(struct net_device *dev) return -ENOMEM; } + np->rx_ring[i].next_desc = cpu_to_le64(np->rx_ring_dma + + ((i + 1) % RX_RING_SIZE) * + sizeof(struct netdev_desc)); /* Rubicon now supports 40 bits of addressing space. */ np->rx_ring[i].fraginfo = cpu_to_le64(pci_map_single( @@ -1824,11 +1831,55 @@ rio_remove1 (struct pci_dev *pdev) } } +#ifdef CONFIG_PM_SLEEP +static int rio_suspend(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + struct netdev_private *np = netdev_priv(dev); + + if (!netif_running(dev)) + return 0; + + netif_device_detach(dev); + del_timer_sync(&np->timer); + rio_hw_stop(dev); + + return 0; +} + +static int rio_resume(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + struct netdev_private *np = netdev_priv(dev); + + if (!netif_running(dev)) + return 0; + + rio_reset_ring(np); + rio_hw_init(dev); + np->timer.expires = jiffies + 1 * HZ; + add_timer(&np->timer); + netif_device_attach(dev); + dl2k_enable_int(np); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(rio_pm_ops, rio_suspend, rio_resume); +#define RIO_PM_OPS (&rio_pm_ops) + +#else + +#define RIO_PM_OPS NULL + +#endif /* CONFIG_PM_SLEEP */ + static struct pci_driver rio_driver = { .name = "dl2k", .id_table = rio_pci_tbl, .probe = rio_probe1, .remove = rio_remove1, + .driver.pm = RIO_PM_OPS, }; module_pci_driver(rio_driver); -- cgit v1.2.3-59-g8ed1b From b3d39a8805c5109dde960204806cf540e3be12fa Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 20 Nov 2015 11:29:39 -0800 Subject: ravb: use clock rate as basis for GTI.TIV The GTI.TIV may be set to 2GHz^2 / rate, where rate is that of the clock of the device. Rather than assuming a rate of 130MHz use the actual rate of the clock. The motivation for this is to use the correct rate on the r8a7795/Salvator-X which is advertised as 133MHz but may differ depending on the extal present on the Salvator-X. Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/ravb.h | 3 +++ drivers/net/ethernet/renesas/ravb_main.c | 38 +++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h index 0623fff932e4..f9dee7436e81 100644 --- a/drivers/net/ethernet/renesas/ravb.h +++ b/drivers/net/ethernet/renesas/ravb.h @@ -576,6 +576,9 @@ enum GTI_BIT { GTI_TIV = 0x0FFFFFFF, }; +#define GTI_TIV_MAX GTI_TIV +#define GTI_TIV_MIN 0x20 + /* GIC */ enum GIC_BIT { GIC_PTCE = 0x00000001, /* Undocumented? */ diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index ee8d1ec61fab..990dc55cdada 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -32,6 +32,8 @@ #include #include +#include + #include "ravb.h" #define RAVB_DEF_MSG_ENABLE \ @@ -1659,6 +1661,38 @@ static const struct of_device_id ravb_match_table[] = { }; MODULE_DEVICE_TABLE(of, ravb_match_table); +static int ravb_set_gti(struct net_device *ndev) +{ + + struct device *dev = ndev->dev.parent; + struct device_node *np = dev->of_node; + unsigned long rate; + struct clk *clk; + uint64_t inc; + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + dev_err(dev, "could not get clock\n"); + return PTR_ERR(clk); + } + + rate = clk_get_rate(clk); + clk_put(clk); + + inc = 1000000000ULL << 20; + do_div(inc, rate); + + if (inc < GTI_TIV_MIN || inc > GTI_TIV_MAX) { + dev_err(dev, "gti.tiv increment 0x%llx is outside the range 0x%x - 0x%x\n", + inc, GTI_TIV_MIN, GTI_TIV_MAX); + return -EINVAL; + } + + ravb_write(ndev, inc, GTI); + + return 0; +} + static int ravb_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1755,7 +1789,9 @@ static int ravb_probe(struct platform_device *pdev) CCC); /* Set GTI value */ - ravb_write(ndev, ((1000 << 20) / 130) & GTI_TIV, GTI); + error = ravb_set_gti(ndev); + if (error) + goto out_release; /* Request GTI loading */ ravb_write(ndev, ravb_read(ndev, GCCR) | GCCR_LTI, GCCR); -- cgit v1.2.3-59-g8ed1b From 5e091e7ad0baa84a401b4dc3f859890990f0854f Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 22 Nov 2015 15:01:29 +0200 Subject: bnx2x: Utilize FW 7.13.1.0. Commit 46e8a249423ff "bnx2x: Add FW 7.13.1.0" added said .bin FW to linux-firmware; This patch incorporates the FW in the bnx2x driver. This introduces 2 fixes/enhancements: - In some management protocols there are outer-vlan configurations that can be dynamically changed while device is running. This fixes some corner cases where such a change did not take effect. - Prevent VFs from sending MAC control frames; FW would treat a VF sending such a packet as malicious and block any further communication done by the VF. Signed-off-by: Yuval Mintz Signed-off-by: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h | 43 +++++++++++++------------ 1 file changed, 23 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index cafd5de675cf..27aa0802d87d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -3013,8 +3013,8 @@ struct afex_stats { }; #define BCM_5710_FW_MAJOR_VERSION 7 -#define BCM_5710_FW_MINOR_VERSION 12 -#define BCM_5710_FW_REVISION_VERSION 30 +#define BCM_5710_FW_MINOR_VERSION 13 +#define BCM_5710_FW_REVISION_VERSION 1 #define BCM_5710_FW_ENGINEERING_VERSION 0 #define BCM_5710_FW_COMPILE_FLAGS 1 @@ -3583,7 +3583,7 @@ enum classify_rule { CLASSIFY_RULE_OPCODE_MAC, CLASSIFY_RULE_OPCODE_VLAN, CLASSIFY_RULE_OPCODE_PAIR, - CLASSIFY_RULE_OPCODE_VXLAN, + CLASSIFY_RULE_OPCODE_IMAC_VNI, MAX_CLASSIFY_RULE }; @@ -3826,6 +3826,17 @@ struct eth_classify_header { __le32 echo; }; +/* + * Command for adding/removing a Inner-MAC/VNI classification rule + */ +struct eth_classify_imac_vni_cmd { + struct eth_classify_cmd_header header; + __le32 vni; + __le16 imac_lsb; + __le16 imac_mid; + __le16 imac_msb; + __le16 reserved1; +}; /* * Command for adding/removing a MAC classification rule @@ -3869,14 +3880,6 @@ struct eth_classify_vlan_cmd { /* * Command for adding/removing a VXLAN classification rule */ -struct eth_classify_vxlan_cmd { - struct eth_classify_cmd_header header; - __le32 vni; - __le16 inner_mac_lsb; - __le16 inner_mac_mid; - __le16 inner_mac_msb; - __le16 reserved1; -}; /* * union for eth classification rule @@ -3885,7 +3888,7 @@ union eth_classify_rule_cmd { struct eth_classify_mac_cmd mac; struct eth_classify_vlan_cmd vlan; struct eth_classify_pair_cmd pair; - struct eth_classify_vxlan_cmd vxlan; + struct eth_classify_imac_vni_cmd imac_vni; }; /* @@ -5623,6 +5626,14 @@ enum igu_mode { MAX_IGU_MODE }; +/* + * Inner Headers Classification Type + */ +enum inner_clss_type { + INNER_CLSS_DISABLED, + INNER_CLSS_USE_VLAN, + INNER_CLSS_USE_VNI, + MAX_INNER_CLSS_TYPE}; /* * IP versions @@ -5953,14 +5964,6 @@ enum ts_offset_cmd { MAX_TS_OFFSET_CMD }; -/* Tunnel Mode */ -enum tunnel_mode { - TUNN_MODE_NONE, - TUNN_MODE_VXLAN, - TUNN_MODE_GRE, - MAX_TUNNEL_MODE -}; - /* zone A per-queue data */ struct ustorm_queue_zone_data { struct ustorm_eth_rx_producers eth_rx_producers; -- cgit v1.2.3-59-g8ed1b From 3f8c0f7efb4fcac11f31afa97584d06118c614bb Mon Sep 17 00:00:00 2001 From: Saurabh Sengar Date: Fri, 20 Nov 2015 23:23:58 +0530 Subject: gianfar: use of_property_read_bool() use of_property_read_bool() for testing bool property Signed-off-by: Saurabh Sengar Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index c8bc43e99a35..67b1850c034e 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -738,7 +738,6 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) struct gfar_private *priv = NULL; struct device_node *np = ofdev->dev.of_node; struct device_node *child = NULL; - struct property *stash; u32 stash_len = 0; u32 stash_idx = 0; unsigned int num_tx_qs, num_rx_qs; @@ -854,9 +853,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) goto err_grp_init; } - stash = of_find_property(np, "bd-stash", NULL); - - if (stash) { + if (of_property_read_bool(np, "bd-stash")) { priv->device_flags |= FSL_GIANFAR_DEV_HAS_BD_STASHING; priv->bd_stash_en = 1; } -- cgit v1.2.3-59-g8ed1b From 4716620d1b6291ce45522d1346c086f76b995d1c Mon Sep 17 00:00:00 2001 From: Kedareswara rao Appana Date: Mon, 26 Oct 2015 11:41:54 +0530 Subject: can: xilinx: Convert to runtime_pm Instead of enabling/disabling clocks at several locations in the driver, Use the runtime_pm framework. This consolidates the actions for runtime PM In the appropriate callbacks and makes the driver more readable and mantainable. Signed-off-by: Kedareswara rao Appana Signed-off-by: Marc Kleine-Budde --- drivers/net/can/xilinx_can.c | 176 +++++++++++++++++++++++++------------------ 1 file changed, 101 insertions(+), 75 deletions(-) (limited to 'drivers') diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c index fc55e8e0351d..ad3806542d33 100644 --- a/drivers/net/can/xilinx_can.c +++ b/drivers/net/can/xilinx_can.c @@ -32,6 +32,7 @@ #include #include #include +#include #define DRIVER_NAME "xilinx_can" @@ -138,7 +139,7 @@ struct xcan_priv { u32 (*read_reg)(const struct xcan_priv *priv, enum xcan_reg reg); void (*write_reg)(const struct xcan_priv *priv, enum xcan_reg reg, u32 val); - struct net_device *dev; + struct device *dev; void __iomem *reg_base; unsigned long irq_flags; struct clk *bus_clk; @@ -843,6 +844,13 @@ static int xcan_open(struct net_device *ndev) struct xcan_priv *priv = netdev_priv(ndev); int ret; + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, ret); + return ret; + } + ret = request_irq(ndev->irq, xcan_interrupt, priv->irq_flags, ndev->name, ndev); if (ret < 0) { @@ -850,29 +858,17 @@ static int xcan_open(struct net_device *ndev) goto err; } - ret = clk_prepare_enable(priv->can_clk); - if (ret) { - netdev_err(ndev, "unable to enable device clock\n"); - goto err_irq; - } - - ret = clk_prepare_enable(priv->bus_clk); - if (ret) { - netdev_err(ndev, "unable to enable bus clock\n"); - goto err_can_clk; - } - /* Set chip into reset mode */ ret = set_reset_mode(ndev); if (ret < 0) { netdev_err(ndev, "mode resetting failed!\n"); - goto err_bus_clk; + goto err_irq; } /* Common open */ ret = open_candev(ndev); if (ret) - goto err_bus_clk; + goto err_irq; ret = xcan_chip_start(ndev); if (ret < 0) { @@ -888,13 +884,11 @@ static int xcan_open(struct net_device *ndev) err_candev: close_candev(ndev); -err_bus_clk: - clk_disable_unprepare(priv->bus_clk); -err_can_clk: - clk_disable_unprepare(priv->can_clk); err_irq: free_irq(ndev->irq, ndev); err: + pm_runtime_put(priv->dev); + return ret; } @@ -911,12 +905,11 @@ static int xcan_close(struct net_device *ndev) netif_stop_queue(ndev); napi_disable(&priv->napi); xcan_chip_stop(ndev); - clk_disable_unprepare(priv->bus_clk); - clk_disable_unprepare(priv->can_clk); free_irq(ndev->irq, ndev); close_candev(ndev); can_led_event(ndev, CAN_LED_EVENT_STOP); + pm_runtime_put(priv->dev); return 0; } @@ -935,27 +928,20 @@ static int xcan_get_berr_counter(const struct net_device *ndev, struct xcan_priv *priv = netdev_priv(ndev); int ret; - ret = clk_prepare_enable(priv->can_clk); - if (ret) - goto err; - - ret = clk_prepare_enable(priv->bus_clk); - if (ret) - goto err_clk; + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, ret); + return ret; + } bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK; bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT); - clk_disable_unprepare(priv->bus_clk); - clk_disable_unprepare(priv->can_clk); + pm_runtime_put(priv->dev); return 0; - -err_clk: - clk_disable_unprepare(priv->can_clk); -err: - return ret; } @@ -968,15 +954,45 @@ static const struct net_device_ops xcan_netdev_ops = { /** * xcan_suspend - Suspend method for the driver - * @dev: Address of the platform_device structure + * @dev: Address of the device structure * * Put the driver into low power mode. - * Return: 0 always + * Return: 0 on success and failure value on error */ static int __maybe_unused xcan_suspend(struct device *dev) { - struct platform_device *pdev = dev_get_drvdata(dev); - struct net_device *ndev = platform_get_drvdata(pdev); + if (!device_may_wakeup(dev)) + return pm_runtime_force_suspend(dev); + + return 0; +} + +/** + * xcan_resume - Resume from suspend + * @dev: Address of the device structure + * + * Resume operation after suspend. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused xcan_resume(struct device *dev) +{ + if (!device_may_wakeup(dev)) + return pm_runtime_force_resume(dev); + + return 0; + +} + +/** + * xcan_runtime_suspend - Runtime suspend method for the driver + * @dev: Address of the device structure + * + * Put the driver into low power mode. + * Return: 0 always + */ +static int __maybe_unused xcan_runtime_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); struct xcan_priv *priv = netdev_priv(ndev); if (netif_running(ndev)) { @@ -987,43 +1003,55 @@ static int __maybe_unused xcan_suspend(struct device *dev) priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK); priv->can.state = CAN_STATE_SLEEPING; - clk_disable(priv->bus_clk); - clk_disable(priv->can_clk); + clk_disable_unprepare(priv->bus_clk); + clk_disable_unprepare(priv->can_clk); return 0; } /** - * xcan_resume - Resume from suspend - * @dev: Address of the platformdevice structure + * xcan_runtime_resume - Runtime resume from suspend + * @dev: Address of the device structure * * Resume operation after suspend. * Return: 0 on success and failure value on error */ -static int __maybe_unused xcan_resume(struct device *dev) +static int __maybe_unused xcan_runtime_resume(struct device *dev) { - struct platform_device *pdev = dev_get_drvdata(dev); - struct net_device *ndev = platform_get_drvdata(pdev); + struct net_device *ndev = dev_get_drvdata(dev); struct xcan_priv *priv = netdev_priv(ndev); int ret; + u32 isr, status; - ret = clk_enable(priv->bus_clk); + ret = clk_prepare_enable(priv->bus_clk); if (ret) { dev_err(dev, "Cannot enable clock.\n"); return ret; } - ret = clk_enable(priv->can_clk); + ret = clk_prepare_enable(priv->can_clk); if (ret) { dev_err(dev, "Cannot enable clock.\n"); clk_disable_unprepare(priv->bus_clk); return ret; } - priv->write_reg(priv, XCAN_MSR_OFFSET, 0); - priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK); - priv->can.state = CAN_STATE_ERROR_ACTIVE; + priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK); + isr = priv->read_reg(priv, XCAN_ISR_OFFSET); + status = priv->read_reg(priv, XCAN_SR_OFFSET); if (netif_running(ndev)) { + if (isr & XCAN_IXR_BSOFF_MASK) { + priv->can.state = CAN_STATE_BUS_OFF; + priv->write_reg(priv, XCAN_SRR_OFFSET, + XCAN_SRR_RESET_MASK); + } else if ((status & XCAN_SR_ESTAT_MASK) == + XCAN_SR_ESTAT_MASK) { + priv->can.state = CAN_STATE_ERROR_PASSIVE; + } else if (status & XCAN_SR_ERRWRN_MASK) { + priv->can.state = CAN_STATE_ERROR_WARNING; + } else { + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } netif_device_attach(ndev); netif_start_queue(ndev); } @@ -1031,7 +1059,10 @@ static int __maybe_unused xcan_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume); +static const struct dev_pm_ops xcan_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(xcan_suspend, xcan_resume) + SET_RUNTIME_PM_OPS(xcan_runtime_suspend, xcan_runtime_resume, NULL) +}; /** * xcan_probe - Platform registration call @@ -1072,7 +1103,7 @@ static int xcan_probe(struct platform_device *pdev) return -ENOMEM; priv = netdev_priv(ndev); - priv->dev = ndev; + priv->dev = &pdev->dev; priv->can.bittiming_const = &xcan_bittiming_const; priv->can.do_set_mode = xcan_do_set_mode; priv->can.do_get_berr_counter = xcan_get_berr_counter; @@ -1114,21 +1145,17 @@ static int xcan_probe(struct platform_device *pdev) } } - ret = clk_prepare_enable(priv->can_clk); - if (ret) { - dev_err(&pdev->dev, "unable to enable device clock\n"); - goto err_free; - } - - ret = clk_prepare_enable(priv->bus_clk); - if (ret) { - dev_err(&pdev->dev, "unable to enable bus clock\n"); - goto err_unprepare_disable_dev; - } - priv->write_reg = xcan_write_reg_le; priv->read_reg = xcan_read_reg_le; + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, ret); + goto err_pmdisable; + } + if (priv->read_reg(priv, XCAN_SR_OFFSET) != XCAN_SR_CONFIG_MASK) { priv->write_reg = xcan_write_reg_be; priv->read_reg = xcan_read_reg_be; @@ -1141,22 +1168,23 @@ static int xcan_probe(struct platform_device *pdev) ret = register_candev(ndev); if (ret) { dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret); - goto err_unprepare_disable_busclk; + goto err_disableclks; } devm_can_led_init(ndev); - clk_disable_unprepare(priv->bus_clk); - clk_disable_unprepare(priv->can_clk); + + pm_runtime_put(&pdev->dev); + netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n", priv->reg_base, ndev->irq, priv->can.clock.freq, priv->tx_max); return 0; -err_unprepare_disable_busclk: - clk_disable_unprepare(priv->bus_clk); -err_unprepare_disable_dev: - clk_disable_unprepare(priv->can_clk); +err_disableclks: + pm_runtime_put(priv->dev); +err_pmdisable: + pm_runtime_disable(&pdev->dev); err_free: free_candev(ndev); err: @@ -1175,10 +1203,8 @@ static int xcan_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct xcan_priv *priv = netdev_priv(ndev); - if (set_reset_mode(ndev) < 0) - netdev_err(ndev, "mode resetting failed!\n"); - unregister_candev(ndev); + pm_runtime_disable(&pdev->dev); netif_napi_del(&priv->napi); free_candev(ndev); -- cgit v1.2.3-59-g8ed1b From b8d55fca9e8853d4065a50061ca2aade123e628d Mon Sep 17 00:00:00 2001 From: Yanbo Li Date: Mon, 16 Nov 2015 22:22:02 +0200 Subject: ath10k: adjust the RX packet pad offset at QCA99X0 4addr mode The QCA99X0 4 addresses RX packets pad 2 bytes at the beginning of MSDU instead the end of ieee80211 header to keep alignment. The currently RX data path can't parse the header correctly in this case. This patch fixes it for QCA99X0. Signed-off-by: Yanbo Li [kvalo@qca.qualcomm.com: checkpatch fixes and naming changes] Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 5 +++++ drivers/net/wireless/ath/ath10k/core.h | 3 +++ drivers/net/wireless/ath/ath10k/htt_rx.c | 15 ++++++++++++++- drivers/net/wireless/ath/ath10k/hw.h | 5 +++++ 4 files changed, 27 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index dc4fc4e8d5ef..f128adbae454 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -58,6 +58,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, + .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER, .fw = { .dir = QCA988X_HW_2_0_FW_DIR, .fw = QCA988X_HW_2_0_FW_FILE, @@ -75,6 +76,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, + .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER, .fw = { .dir = QCA6174_HW_2_1_FW_DIR, .fw = QCA6174_HW_2_1_FW_FILE, @@ -92,6 +94,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, + .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER, .fw = { .dir = QCA6174_HW_3_0_FW_DIR, .fw = QCA6174_HW_3_0_FW_FILE, @@ -109,6 +112,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .otp_exe_param = 0, .channel_counters_freq_hz = 88000, .max_probe_resp_desc_thres = 0, + .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER, .fw = { /* uses same binaries as hw3.0 */ .dir = QCA6174_HW_3_0_FW_DIR, @@ -128,6 +132,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .continuous_frag_desc = true, .channel_counters_freq_hz = 150000, .max_probe_resp_desc_thres = 24, + .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_BEFORE, .fw = { .dir = QCA99X0_HW_2_0_FW_DIR, .fw = QCA99X0_HW_2_0_FW_FILE, diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index c16f3484dc8a..91a39878c873 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -668,6 +668,9 @@ struct ath10k { */ u32 max_probe_resp_desc_thres; + /* The padding bytes's location is different on various chips */ + enum ath10k_hw_4addr_pad hw_4addr_pad; + struct ath10k_hw_params_fw { const char *dir; const char *fw; diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 396645b508e2..b84727bcf2da 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1114,7 +1114,20 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar, */ /* pull decapped header and copy SA & DA */ - hdr = (struct ieee80211_hdr *)msdu->data; + if ((ar->hw_params.hw_4addr_pad == ATH10K_HW_4ADDR_PAD_BEFORE) && + ieee80211_has_a4(((struct ieee80211_hdr *)first_hdr)->frame_control)) { + /* The QCA99X0 4 address mode pad 2 bytes at the + * beginning of MSDU + */ + hdr = (struct ieee80211_hdr *)(msdu->data + 2); + /* The skb length need be extended 2 as the 2 bytes at the tail + * be excluded due to the padding + */ + skb_put(msdu, 2); + } else { + hdr = (struct ieee80211_hdr *)(msdu->data); + } + hdr_len = ath10k_htt_rx_nwifi_hdrlen(ar, hdr); ether_addr_copy(da, ieee80211_get_DA(hdr)); ether_addr_copy(sa, ieee80211_get_SA(hdr)); diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 557d8d2d06aa..b99b691ccccc 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -304,6 +304,11 @@ enum ath10k_hw_rate_cck { ATH10K_HW_RATE_CCK_SP_2M, }; +enum ath10k_hw_4addr_pad { + ATH10K_HW_4ADDR_PAD_AFTER, + ATH10K_HW_4ADDR_PAD_BEFORE, +}; + /* Target specific defines for MAIN firmware */ #define TARGET_NUM_VDEVS 8 #define TARGET_NUM_PEER_AST 2 -- cgit v1.2.3-59-g8ed1b From fd12cb32467e7aff5648fd0e968a66e22c0696b6 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 18 Nov 2015 06:59:15 +0100 Subject: ath10k: merge is_protected with nohwcrypt It was wasteful to have two flags describing the same thing. While at it fix code style of ath10k_tx_h_use_hwcrypto(). Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 - drivers/net/wireless/ath/ath10k/htt_tx.c | 3 --- drivers/net/wireless/ath/ath10k/mac.c | 14 ++++++++++---- 3 files changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 91a39878c873..73737ea06ccd 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -86,7 +86,6 @@ struct ath10k_skb_cb { u8 eid; u8 vdev_id; enum ath10k_hw_txrx_mode txmode; - bool is_protected; struct { u8 tid; diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 8f76b9d96486..b57335d47c78 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -663,9 +663,6 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) if (skb_cb->htt.nohwcrypt) flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT; - if (!skb_cb->is_protected) - flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT; - flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID); flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID); if (msdu->ip_summed == CHECKSUM_PARTIAL && diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 66378544f3c9..50d6e599ad70 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3200,14 +3200,22 @@ ath10k_tx_h_get_txmode(struct ath10k *ar, struct ieee80211_vif *vif, } static bool ath10k_tx_h_use_hwcrypto(struct ieee80211_vif *vif, - struct sk_buff *skb) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct sk_buff *skb) +{ + const struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + const struct ieee80211_hdr *hdr = (void *)skb->data; const u32 mask = IEEE80211_TX_INTFL_DONT_ENCRYPT | IEEE80211_TX_CTL_INJECTED; + + if (!ieee80211_has_protected(hdr->frame_control)) + return false; + if ((info->flags & mask) == mask) return false; + if (vif) return !ath10k_vif_to_arvif(vif)->nohwcrypt; + return true; } @@ -3646,7 +3654,6 @@ static void ath10k_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif = info->control.vif; struct ieee80211_sta *sta = control->sta; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - __le16 fc = hdr->frame_control; /* We should disable CCK RATE due to P2P */ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) @@ -3658,7 +3665,6 @@ static void ath10k_tx(struct ieee80211_hw *hw, ATH10K_SKB_CB(skb)->htt.nohwcrypt = !ath10k_tx_h_use_hwcrypto(vif, skb); ATH10K_SKB_CB(skb)->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif); ATH10K_SKB_CB(skb)->txmode = ath10k_tx_h_get_txmode(ar, vif, sta, skb); - ATH10K_SKB_CB(skb)->is_protected = ieee80211_has_protected(fc); switch (ATH10K_SKB_CB(skb)->txmode) { case ATH10K_HW_TXRX_MGMT: -- cgit v1.2.3-59-g8ed1b From 6a2636d811a05687b24d784986468716ff1220ca Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 18 Nov 2015 06:59:16 +0100 Subject: ath10k: rename function to adhere to naming convention All functions should have ath10k_{filename}_ prefixes. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 50d6e599ad70..e905722f17c1 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3149,8 +3149,10 @@ static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, struct ieee80211_vif *vif) } static enum ath10k_hw_txrx_mode -ath10k_tx_h_get_txmode(struct ath10k *ar, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, struct sk_buff *skb) +ath10k_mac_tx_h_get_txmode(struct ath10k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct sk_buff *skb) { const struct ieee80211_hdr *hdr = (void *)skb->data; __le16 fc = hdr->frame_control; @@ -3664,7 +3666,8 @@ static void ath10k_tx(struct ieee80211_hw *hw, ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr); ATH10K_SKB_CB(skb)->htt.nohwcrypt = !ath10k_tx_h_use_hwcrypto(vif, skb); ATH10K_SKB_CB(skb)->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif); - ATH10K_SKB_CB(skb)->txmode = ath10k_tx_h_get_txmode(ar, vif, sta, skb); + ATH10K_SKB_CB(skb)->txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, + skb); switch (ATH10K_SKB_CB(skb)->txmode) { case ATH10K_HW_TXRX_MGMT: -- cgit v1.2.3-59-g8ed1b From 8a933964e8a4da3f28b46ac43d2e57eecfd833f6 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 18 Nov 2015 06:59:17 +0100 Subject: ath10k: remove txmode from skb_cb It was wasteful to keep it in the struct because it can be passed as function argument down the tx path. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 - drivers/net/wireless/ath/ath10k/htt.h | 4 +++- drivers/net/wireless/ath/ath10k/htt_tx.c | 9 +++---- drivers/net/wireless/ath/ath10k/mac.c | 40 ++++++++++++++++++++++++-------- 4 files changed, 38 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 73737ea06ccd..063c34ac76d2 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -85,7 +85,6 @@ struct ath10k_skb_cb { dma_addr_t paddr; u8 eid; u8 vdev_id; - enum ath10k_hw_txrx_mode txmode; struct { u8 tid; diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 2b87ed6458db..47ca048feaf0 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -1602,7 +1602,9 @@ void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc); int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb); void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id); int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *); -int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *); +int ath10k_htt_tx(struct ath10k_htt *htt, + enum ath10k_hw_txrx_mode txmode, + struct sk_buff *msdu); void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar, struct sk_buff *skb); diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index b57335d47c78..f6fb4f131542 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -532,7 +532,8 @@ err: return res; } -int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) +int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, + struct sk_buff *msdu) { struct ath10k *ar = htt->ar; struct device *dev = ar->dev; @@ -584,7 +585,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) ieee80211_has_protected(hdr->frame_control)) { skb_put(msdu, IEEE80211_CCMP_MIC_LEN); } else if (!skb_cb->htt.nohwcrypt && - skb_cb->txmode == ATH10K_HW_TXRX_RAW && + txmode == ATH10K_HW_TXRX_RAW && ieee80211_has_protected(hdr->frame_control)) { skb_put(msdu, IEEE80211_CCMP_MIC_LEN); } @@ -597,7 +598,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) goto err_free_msdu_id; } - switch (skb_cb->txmode) { + switch (txmode) { case ATH10K_HW_TXRX_RAW: case ATH10K_HW_TXRX_NATIVE_WIFI: flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT; @@ -626,7 +627,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) frags_paddr = skb_cb->htt.txbuf_paddr; } - flags0 |= SM(skb_cb->txmode, HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); + flags0 |= SM(txmode, HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); break; case ATH10K_HW_TXRX_MGMT: flags0 |= SM(ATH10K_HW_TXRX_MGMT, diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index e905722f17c1..fd4c13507790 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3335,24 +3335,24 @@ unlock: return ret; } -static void ath10k_mac_tx(struct ath10k *ar, struct sk_buff *skb) +static void ath10k_mac_tx(struct ath10k *ar, enum ath10k_hw_txrx_mode txmode, + struct sk_buff *skb) { - struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb); struct ath10k_htt *htt = &ar->htt; int ret = 0; - switch (cb->txmode) { + switch (txmode) { case ATH10K_HW_TXRX_RAW: case ATH10K_HW_TXRX_NATIVE_WIFI: case ATH10K_HW_TXRX_ETHERNET: - ret = ath10k_htt_tx(htt, skb); + ret = ath10k_htt_tx(htt, txmode, skb); break; case ATH10K_HW_TXRX_MGMT: if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX, ar->fw_features)) ret = ath10k_mac_tx_wmi_mgmt(ar, skb); else if (ar->htt.target_version_major >= 3) - ret = ath10k_htt_tx(htt, skb); + ret = ath10k_htt_tx(htt, txmode, skb); else ret = ath10k_htt_mgmt_tx(htt, skb); break; @@ -3382,9 +3382,13 @@ void ath10k_offchan_tx_work(struct work_struct *work) { struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work); struct ath10k_peer *peer; + struct ath10k_vif *arvif; struct ieee80211_hdr *hdr; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; struct sk_buff *skb; const u8 *peer_addr; + enum ath10k_hw_txrx_mode txmode; int vdev_id; int ret; unsigned long time_left; @@ -3434,7 +3438,22 @@ void ath10k_offchan_tx_work(struct work_struct *work) ar->offchan_tx_skb = skb; spin_unlock_bh(&ar->data_lock); - ath10k_mac_tx(ar, skb); + /* It's safe to access vif and sta - conf_mutex guarantees that + * sta_state() and remove_interface() are locked exclusively + * out wrt to this offchannel worker. + */ + arvif = ath10k_get_arvif(ar, vdev_id); + if (arvif) { + vif = arvif->vif; + sta = ieee80211_find_sta(vif, peer_addr); + } else { + vif = NULL; + sta = NULL; + } + + txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb); + + ath10k_mac_tx(ar, txmode, skb); time_left = wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ); @@ -3656,20 +3675,21 @@ static void ath10k_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif = info->control.vif; struct ieee80211_sta *sta = control->sta; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + enum ath10k_hw_txrx_mode txmode; /* We should disable CCK RATE due to P2P */ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n"); + txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb); + ATH10K_SKB_CB(skb)->htt.is_offchan = false; ATH10K_SKB_CB(skb)->htt.freq = 0; ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr); ATH10K_SKB_CB(skb)->htt.nohwcrypt = !ath10k_tx_h_use_hwcrypto(vif, skb); ATH10K_SKB_CB(skb)->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif); - ATH10K_SKB_CB(skb)->txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, - skb); - switch (ATH10K_SKB_CB(skb)->txmode) { + switch (txmode) { case ATH10K_HW_TXRX_MGMT: case ATH10K_HW_TXRX_NATIVE_WIFI: ath10k_tx_h_nwifi(hw, skb); @@ -3706,7 +3726,7 @@ static void ath10k_tx(struct ieee80211_hw *hw, } } - ath10k_mac_tx(ar, skb); + ath10k_mac_tx(ar, txmode, skb); } /* Must not be called with conf_mutex held as workers can use that also. */ -- cgit v1.2.3-59-g8ed1b From e0813d34b553d8335c39b37475f32f1e49271c40 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 18 Nov 2015 06:59:18 +0100 Subject: ath10k: remove is_offchan It was wasteful to keep it in the struct. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 - drivers/net/wireless/ath/ath10k/mac.c | 2 -- drivers/net/wireless/ath/ath10k/txrx.c | 7 ++++++- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 063c34ac76d2..891e63d67220 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -89,7 +89,6 @@ struct ath10k_skb_cb { struct { u8 tid; u16 freq; - bool is_offchan; bool nohwcrypt; struct ath10k_htt_txbuf *txbuf; u32 txbuf_paddr; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index fd4c13507790..a14d095fbea5 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3683,7 +3683,6 @@ static void ath10k_tx(struct ieee80211_hw *hw, txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb); - ATH10K_SKB_CB(skb)->htt.is_offchan = false; ATH10K_SKB_CB(skb)->htt.freq = 0; ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr); ATH10K_SKB_CB(skb)->htt.nohwcrypt = !ath10k_tx_h_use_hwcrypto(vif, skb); @@ -3715,7 +3714,6 @@ static void ath10k_tx(struct ieee80211_hw *hw, if (!ath10k_mac_tx_frm_has_freq(ar)) { ATH10K_SKB_CB(skb)->htt.freq = 0; - ATH10K_SKB_CB(skb)->htt.is_offchan = true; ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb); diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 6d1105ab4592..9e14c04ac89f 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -23,7 +23,12 @@ static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb) { - if (!ATH10K_SKB_CB(skb)->htt.is_offchan) + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (likely(!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN))) + return; + + if (ath10k_mac_tx_frm_has_freq(ar)) return; /* If the original wait_for_completion() timed out before -- cgit v1.2.3-59-g8ed1b From bd87744028475207172ee0fb75f4bdb888d516d7 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 18 Nov 2015 06:59:19 +0100 Subject: ath10k: remove freq from skb_cb It was wasteful to keep it in the struct. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 - drivers/net/wireless/ath/ath10k/htt_tx.c | 9 +++++++-- drivers/net/wireless/ath/ath10k/mac.c | 5 +---- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 891e63d67220..4cc410d2ac27 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -88,7 +88,6 @@ struct ath10k_skb_cb { struct { u8 tid; - u16 freq; bool nohwcrypt; struct ath10k_htt_txbuf *txbuf; u32 txbuf_paddr; diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index f6fb4f131542..a7d8798b2f37 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -538,6 +538,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, struct ath10k *ar = htt->ar; struct device *dev = ar->dev; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu); struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); struct ath10k_hif_sg_item sg_items[2]; struct htt_data_tx_desc_frag *frags; @@ -547,6 +548,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, int res; u8 flags0 = 0; u16 msdu_id, flags1 = 0; + u16 freq = 0; u32 frags_paddr = 0; struct htt_msdu_ext_desc *ext_desc = NULL; bool limit_mgmt_desc = false; @@ -598,6 +600,9 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, goto err_free_msdu_id; } + if (unlikely(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) + freq = ar->scan.roc_freq; + switch (txmode) { case ATH10K_HW_TXRX_RAW: case ATH10K_HW_TXRX_NATIVE_WIFI: @@ -690,7 +695,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, skb_cb->htt.txbuf->cmd_tx.offchan_tx.peerid = __cpu_to_le16(HTT_INVALID_PEERID); skb_cb->htt.txbuf->cmd_tx.offchan_tx.freq = - __cpu_to_le16(skb_cb->htt.freq); + __cpu_to_le16(freq); } else { skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID); @@ -700,7 +705,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu freq %hu\n", flags0, flags1, msdu->len, msdu_id, frags_paddr, - (u32)skb_cb->paddr, vdev_id, tid, skb_cb->htt.freq); + (u32)skb_cb->paddr, vdev_id, tid, freq); ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ", msdu->data, msdu->len); trace_ath10k_tx_hdr(ar, msdu->data, msdu->len); diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index a14d095fbea5..ac70959a4db3 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3528,6 +3528,7 @@ void __ath10k_scan_finish(struct ath10k *ar) case ATH10K_SCAN_STARTING: ar->scan.state = ATH10K_SCAN_IDLE; ar->scan_channel = NULL; + ar->scan.roc_freq = 0; ath10k_offchan_tx_purge(ar); cancel_delayed_work(&ar->scan.timeout); complete_all(&ar->scan.completed); @@ -3683,7 +3684,6 @@ static void ath10k_tx(struct ieee80211_hw *hw, txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb); - ATH10K_SKB_CB(skb)->htt.freq = 0; ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr); ATH10K_SKB_CB(skb)->htt.nohwcrypt = !ath10k_tx_h_use_hwcrypto(vif, skb); ATH10K_SKB_CB(skb)->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif); @@ -3708,13 +3708,10 @@ static void ath10k_tx(struct ieee80211_hw *hw, if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { spin_lock_bh(&ar->data_lock); - ATH10K_SKB_CB(skb)->htt.freq = ar->scan.roc_freq; ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id; spin_unlock_bh(&ar->data_lock); if (!ath10k_mac_tx_frm_has_freq(ar)) { - ATH10K_SKB_CB(skb)->htt.freq = 0; - ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb); -- cgit v1.2.3-59-g8ed1b From 66b8a0108d73f9809b60d7e921189142207997c2 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 18 Nov 2015 06:59:20 +0100 Subject: ath10k: pack up flags in skb_cb It was wasteful to have all the flags as separate bools. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 13 +++++++------ drivers/net/wireless/ath/ath10k/htt_tx.c | 4 ++-- drivers/net/wireless/ath/ath10k/mac.c | 10 +++++++--- drivers/net/wireless/ath/ath10k/wmi.c | 12 ++++++++---- 4 files changed, 24 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 4cc410d2ac27..d0f2f2494ea9 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -81,22 +81,23 @@ static inline const char *ath10k_bus_str(enum ath10k_bus bus) return "unknown"; } +enum ath10k_skb_flags { + ATH10K_SKB_F_NO_HWCRYPT = BIT(0), + ATH10K_SKB_F_DTIM_ZERO = BIT(1), + ATH10K_SKB_F_DELIVER_CAB = BIT(2), +}; + struct ath10k_skb_cb { dma_addr_t paddr; + u8 flags; u8 eid; u8 vdev_id; struct { u8 tid; - bool nohwcrypt; struct ath10k_htt_txbuf *txbuf; u32 txbuf_paddr; } __packed htt; - - struct { - bool dtim_zero; - bool deliver_cab; - } bcn; } __packed; struct ath10k_skb_rxcb { diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index a7d8798b2f37..c955c6b6ea9d 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -586,7 +586,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, ieee80211_is_disassoc(hdr->frame_control)) && ieee80211_has_protected(hdr->frame_control)) { skb_put(msdu, IEEE80211_CCMP_MIC_LEN); - } else if (!skb_cb->htt.nohwcrypt && + } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) && txmode == ATH10K_HW_TXRX_RAW && ieee80211_has_protected(hdr->frame_control)) { skb_put(msdu, IEEE80211_CCMP_MIC_LEN); @@ -666,7 +666,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, prefetch_len); skb_cb->htt.txbuf->htc_hdr.flags = 0; - if (skb_cb->htt.nohwcrypt) + if (skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT; flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID); diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ac70959a4db3..eca0cfe756d0 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3672,6 +3672,7 @@ static void ath10k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath10k *ar = hw->priv; + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_vif *vif = info->control.vif; struct ieee80211_sta *sta = control->sta; @@ -3684,9 +3685,12 @@ static void ath10k_tx(struct ieee80211_hw *hw, txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb); - ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr); - ATH10K_SKB_CB(skb)->htt.nohwcrypt = !ath10k_tx_h_use_hwcrypto(vif, skb); - ATH10K_SKB_CB(skb)->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif); + skb_cb->flags = 0; + if (!ath10k_tx_h_use_hwcrypto(vif, skb)) + skb_cb->flags |= ATH10K_SKB_F_NO_HWCRYPT; + + skb_cb->htt.tid = ath10k_tx_h_get_tid(hdr); + skb_cb->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif); switch (txmode) { case ATH10K_HW_TXRX_MGMT: diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 902107934edd..6028a598c199 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1715,6 +1715,8 @@ static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif) struct ath10k *ar = arvif->ar; struct ath10k_skb_cb *cb; struct sk_buff *bcn; + bool dtim_zero; + bool deliver_cab; int ret; spin_lock_bh(&ar->data_lock); @@ -1734,12 +1736,14 @@ static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif) arvif->beacon_state = ATH10K_BEACON_SENDING; spin_unlock_bh(&ar->data_lock); + dtim_zero = !!(cb->flags & ATH10K_SKB_F_DTIM_ZERO); + deliver_cab = !!(cb->flags & ATH10K_SKB_F_DELIVER_CAB); ret = ath10k_wmi_beacon_send_ref_nowait(arvif->ar, arvif->vdev_id, bcn->data, bcn->len, cb->paddr, - cb->bcn.dtim_zero, - cb->bcn.deliver_cab); + dtim_zero, + deliver_cab); spin_lock_bh(&ar->data_lock); @@ -3157,10 +3161,10 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len); if (tim->dtim_count == 0) { - ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true; + ATH10K_SKB_CB(bcn)->flags |= ATH10K_SKB_F_DTIM_ZERO; if (__le32_to_cpu(tim_info->tim_mcast) == 1) - ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true; + ATH10K_SKB_CB(bcn)->flags |= ATH10K_SKB_F_DELIVER_CAB; } ath10k_dbg(ar, ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n", -- cgit v1.2.3-59-g8ed1b From d668dbaebe430af8843ca6e83c1e44fd1efb20aa Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 18 Nov 2015 06:59:21 +0100 Subject: ath10k: fix tx header parsing Frames are not guaranteed to be 802.11 frames in ath10k_htt_tx() and the tx completion handler. In some cases, like TDLS, they can be Ethernet. Hence checking, e.g. frame_control could yield bogus results and behavior. Fortunately this wasn't a real problem so far because there's no FW/HW combination to encounter this problem. However it is good to fix this in advance. Fixes: 75d85fd9993c ("ath10k: introduce basic tdls functionality") Fixes: eebc67fef3ee ("ath10k: fix pmf for wmi-tlv on qca6174") Fixes: 7b7da0a02192 ("ath10k: drop probe responses when too many are queued") Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 + drivers/net/wireless/ath/ath10k/mac.c | 3 +++ drivers/net/wireless/ath/ath10k/txrx.c | 8 ++------ 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index d0f2f2494ea9..933ffe3c2855 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -85,6 +85,7 @@ enum ath10k_skb_flags { ATH10K_SKB_F_NO_HWCRYPT = BIT(0), ATH10K_SKB_F_DTIM_ZERO = BIT(1), ATH10K_SKB_F_DELIVER_CAB = BIT(2), + ATH10K_SKB_F_MGMT = BIT(3), }; struct ath10k_skb_cb { diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index eca0cfe756d0..edf243358445 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3689,6 +3689,9 @@ static void ath10k_tx(struct ieee80211_hw *hw, if (!ath10k_tx_h_use_hwcrypto(vif, skb)) skb_cb->flags |= ATH10K_SKB_F_NO_HWCRYPT; + if (ieee80211_is_mgmt(hdr->frame_control)) + skb_cb->flags |= ATH10K_SKB_F_MGMT; + skb_cb->htt.tid = ath10k_tx_h_get_tid(hdr); skb_cb->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif); diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 9e14c04ac89f..fbfb608e48ab 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -57,8 +57,6 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct ieee80211_tx_info *info; struct ath10k_skb_cb *skb_cb; struct sk_buff *msdu; - struct ieee80211_hdr *hdr; - __le16 fc; bool limit_mgmt_desc = false; ath10k_dbg(ar, ATH10K_DBG_HTT, @@ -81,10 +79,9 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, return; } - hdr = (struct ieee80211_hdr *)msdu->data; - fc = hdr->frame_control; + skb_cb = ATH10K_SKB_CB(msdu); - if (unlikely(ieee80211_is_mgmt(fc)) && + if (unlikely(skb_cb->flags & ATH10K_SKB_F_MGMT) && ar->hw_params.max_probe_resp_desc_thres) limit_mgmt_desc = true; @@ -94,7 +91,6 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, wake_up(&htt->empty_tx_wq); spin_unlock_bh(&htt->tx_lock); - skb_cb = ATH10K_SKB_CB(msdu); dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); ath10k_report_offchan_tx(htt->ar, msdu); -- cgit v1.2.3-59-g8ed1b From 609db229b42fda3f7d3a21fe54084983bc28dfae Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 18 Nov 2015 06:59:22 +0100 Subject: ath10k: replace vdev_id and tid in skb cb This prepares the driver for future ieee80211_txq and wake_tx_queue() support. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 4 ++-- drivers/net/wireless/ath/ath10k/htt_tx.c | 36 +++++++++++++++++++++++++--- drivers/net/wireless/ath/ath10k/mac.c | 40 +++++--------------------------- drivers/net/wireless/ath/ath10k/wmi.c | 10 +++++++- 4 files changed, 50 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 933ffe3c2855..ccd02a7f847b 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -86,16 +86,16 @@ enum ath10k_skb_flags { ATH10K_SKB_F_DTIM_ZERO = BIT(1), ATH10K_SKB_F_DELIVER_CAB = BIT(2), ATH10K_SKB_F_MGMT = BIT(3), + ATH10K_SKB_F_QOS = BIT(4), }; struct ath10k_skb_cb { dma_addr_t paddr; u8 flags; u8 eid; - u8 vdev_id; + struct ieee80211_vif *vif; struct { - u8 tid; struct ath10k_htt_txbuf *txbuf; u32 txbuf_paddr; } __packed htt; diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index c955c6b6ea9d..23e047b0aca3 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -439,6 +439,35 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, return 0; } +static u8 ath10k_htt_tx_get_vdev_id(struct ath10k *ar, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb); + struct ath10k_vif *arvif = (void *)cb->vif->drv_priv; + + if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) + return ar->scan.vdev_id; + else if (cb->vif) + return arvif->vdev_id; + else if (ar->monitor_started) + return ar->monitor_vdev_id; + else + return 0; +} + +static u8 ath10k_htt_tx_get_tid(struct sk_buff *skb, bool is_eth) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb); + + if (!is_eth && ieee80211_is_mgmt(hdr->frame_control)) + return HTT_DATA_TX_EXT_TID_MGMT; + else if (cb->flags & ATH10K_SKB_F_QOS) + return skb->priority % IEEE80211_QOS_CTL_TID_MASK; + else + return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; +} + int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) { struct ath10k *ar = htt->ar; @@ -446,7 +475,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) struct sk_buff *txdesc = NULL; struct htt_cmd *cmd; struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); - u8 vdev_id = skb_cb->vdev_id; + u8 vdev_id = ath10k_htt_tx_get_vdev_id(ar, msdu); int len = 0; int msdu_id = -1; int res; @@ -542,8 +571,9 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); struct ath10k_hif_sg_item sg_items[2]; struct htt_data_tx_desc_frag *frags; - u8 vdev_id = skb_cb->vdev_id; - u8 tid = skb_cb->htt.tid; + bool is_eth = (txmode == ATH10K_HW_TXRX_ETHERNET); + u8 vdev_id = ath10k_htt_tx_get_vdev_id(ar, msdu); + u8 tid = ath10k_htt_tx_get_tid(msdu, is_eth); int prefetch_len; int res; u8 flags0 = 0; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index edf243358445..8a2c4a9ec73a 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3122,32 +3122,6 @@ void ath10k_mac_handle_tx_pause_vdev(struct ath10k *ar, u32 vdev_id, spin_unlock_bh(&ar->htt.tx_lock); } -static u8 ath10k_tx_h_get_tid(struct ieee80211_hdr *hdr) -{ - if (ieee80211_is_mgmt(hdr->frame_control)) - return HTT_DATA_TX_EXT_TID_MGMT; - - if (!ieee80211_is_data_qos(hdr->frame_control)) - return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; - - if (!is_unicast_ether_addr(ieee80211_get_DA(hdr))) - return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; - - return ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; -} - -static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, struct ieee80211_vif *vif) -{ - if (vif) - return ath10k_vif_to_arvif(vif)->vdev_id; - - if (ar->monitor_started) - return ar->monitor_vdev_id; - - ath10k_warn(ar, "failed to resolve vdev id\n"); - return 0; -} - static enum ath10k_hw_txrx_mode ath10k_mac_tx_h_get_txmode(struct ath10k *ar, struct ieee80211_vif *vif, @@ -3244,7 +3218,7 @@ static void ath10k_tx_h_nwifi(struct ieee80211_hw *hw, struct sk_buff *skb) */ hdr = (void *)skb->data; if (ieee80211_is_qos_nullfunc(hdr->frame_control)) - cb->htt.tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; + cb->flags &= ~ATH10K_SKB_F_QOS; hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA); } @@ -3413,9 +3387,9 @@ void ath10k_offchan_tx_work(struct work_struct *work) hdr = (struct ieee80211_hdr *)skb->data; peer_addr = ieee80211_get_DA(hdr); - vdev_id = ATH10K_SKB_CB(skb)->vdev_id; spin_lock_bh(&ar->data_lock); + vdev_id = ar->scan.vdev_id; peer = ath10k_peer_find(ar, vdev_id, peer_addr); spin_unlock_bh(&ar->data_lock); @@ -3692,8 +3666,10 @@ static void ath10k_tx(struct ieee80211_hw *hw, if (ieee80211_is_mgmt(hdr->frame_control)) skb_cb->flags |= ATH10K_SKB_F_MGMT; - skb_cb->htt.tid = ath10k_tx_h_get_tid(hdr); - skb_cb->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif); + if (ieee80211_is_data_qos(hdr->frame_control)) + skb_cb->flags |= ATH10K_SKB_F_QOS; + + skb_cb->vif = vif; switch (txmode) { case ATH10K_HW_TXRX_MGMT: @@ -3714,10 +3690,6 @@ static void ath10k_tx(struct ieee80211_hw *hw, } if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { - spin_lock_bh(&ar->data_lock); - ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id; - spin_unlock_bh(&ar->data_lock); - if (!ath10k_mac_tx_frm_has_freq(ar)) { ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 6028a598c199..2a44d63a03cd 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1814,16 +1814,24 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) static struct sk_buff * ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) { + struct ath10k_skb_cb *cb = ATH10K_SKB_CB(msdu); + struct ath10k_vif *arvif = (void *)cb->vif->drv_priv; struct wmi_mgmt_tx_cmd *cmd; struct ieee80211_hdr *hdr; struct sk_buff *skb; int len; + u32 vdev_id; u32 buf_len = msdu->len; u16 fc; hdr = (struct ieee80211_hdr *)msdu->data; fc = le16_to_cpu(hdr->frame_control); + if (cb->vif) + vdev_id = arvif->vdev_id; + else + vdev_id = 0; + if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control))) return ERR_PTR(-EINVAL); @@ -1845,7 +1853,7 @@ ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) cmd = (struct wmi_mgmt_tx_cmd *)skb->data; - cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(msdu)->vdev_id); + cmd->hdr.vdev_id = __cpu_to_le32(vdev_id); cmd->hdr.tx_rate = 0; cmd->hdr.tx_power = 0; cmd->hdr.buf_len = __cpu_to_le32(buf_len); -- cgit v1.2.3-59-g8ed1b From aca146afbc3b9191ca4265e80c60b86dcedeca13 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 18 Nov 2015 06:59:23 +0100 Subject: ath10k: store msdu_id instead of txbuf pointers Txbuf is no longer a DMA pool and can be easily tracked with a mere msdu_id. This saves 10 bytes on 64bit systems and 6 bytes on 32bit systems of precious sk_buff control buffer. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 6 +--- drivers/net/wireless/ath/ath10k/htt_tx.c | 55 ++++++++++++++++---------------- 2 files changed, 28 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index ccd02a7f847b..06309f0efa43 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -93,12 +93,8 @@ struct ath10k_skb_cb { dma_addr_t paddr; u8 flags; u8 eid; + u16 msdu_id; struct ieee80211_vif *vif; - - struct { - struct ath10k_htt_txbuf *txbuf; - u32 txbuf_paddr; - } __packed htt; } __packed; struct ath10k_skb_rxcb { diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 23e047b0aca3..5274f5bb0b45 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -539,8 +539,6 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) memcpy(cmd->mgmt_tx.hdr, msdu->data, min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN)); - skb_cb->htt.txbuf = NULL; - res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc); if (res) goto err_unmap_msdu; @@ -570,6 +568,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu); struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); struct ath10k_hif_sg_item sg_items[2]; + struct ath10k_htt_txbuf *txbuf; struct htt_data_tx_desc_frag *frags; bool is_eth = (txmode == ATH10K_HW_TXRX_ETHERNET); u8 vdev_id = ath10k_htt_tx_get_vdev_id(ar, msdu); @@ -580,6 +579,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, u16 msdu_id, flags1 = 0; u16 freq = 0; u32 frags_paddr = 0; + u32 txbuf_paddr; struct htt_msdu_ext_desc *ext_desc = NULL; bool limit_mgmt_desc = false; bool is_probe_resp = false; @@ -607,9 +607,9 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, prefetch_len = min(htt->prefetch_len, msdu->len); prefetch_len = roundup(prefetch_len, 4); - skb_cb->htt.txbuf = &htt->txbuf.vaddr[msdu_id]; - skb_cb->htt.txbuf_paddr = htt->txbuf.paddr + - (sizeof(struct ath10k_htt_txbuf) * msdu_id); + txbuf = &htt->txbuf.vaddr[msdu_id]; + txbuf_paddr = htt->txbuf.paddr + + (sizeof(struct ath10k_htt_txbuf) * msdu_id); if ((ieee80211_is_action(hdr->frame_control) || ieee80211_is_deauth(hdr->frame_control) || @@ -653,14 +653,14 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, frags_paddr = htt->frag_desc.paddr + (sizeof(struct htt_msdu_ext_desc) * msdu_id); } else { - frags = skb_cb->htt.txbuf->frags; + frags = txbuf->frags; frags[0].dword_addr.paddr = __cpu_to_le32(skb_cb->paddr); frags[0].dword_addr.len = __cpu_to_le32(msdu->len); frags[1].dword_addr.paddr = 0; frags[1].dword_addr.len = 0; - frags_paddr = skb_cb->htt.txbuf_paddr; + frags_paddr = txbuf_paddr; } flags0 |= SM(txmode, HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); break; @@ -689,12 +689,11 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, * avoid extra memory allocations, compress data structures and thus * improve performance. */ - skb_cb->htt.txbuf->htc_hdr.eid = htt->eid; - skb_cb->htt.txbuf->htc_hdr.len = __cpu_to_le16( - sizeof(skb_cb->htt.txbuf->cmd_hdr) + - sizeof(skb_cb->htt.txbuf->cmd_tx) + - prefetch_len); - skb_cb->htt.txbuf->htc_hdr.flags = 0; + txbuf->htc_hdr.eid = htt->eid; + txbuf->htc_hdr.len = __cpu_to_le16(sizeof(txbuf->cmd_hdr) + + sizeof(txbuf->cmd_tx) + + prefetch_len); + txbuf->htc_hdr.flags = 0; if (skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT; @@ -715,19 +714,19 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, */ flags1 |= HTT_DATA_TX_DESC_FLAGS1_POSTPONED; - skb_cb->htt.txbuf->cmd_hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM; - skb_cb->htt.txbuf->cmd_tx.flags0 = flags0; - skb_cb->htt.txbuf->cmd_tx.flags1 = __cpu_to_le16(flags1); - skb_cb->htt.txbuf->cmd_tx.len = __cpu_to_le16(msdu->len); - skb_cb->htt.txbuf->cmd_tx.id = __cpu_to_le16(msdu_id); - skb_cb->htt.txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr); + txbuf->cmd_hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM; + txbuf->cmd_tx.flags0 = flags0; + txbuf->cmd_tx.flags1 = __cpu_to_le16(flags1); + txbuf->cmd_tx.len = __cpu_to_le16(msdu->len); + txbuf->cmd_tx.id = __cpu_to_le16(msdu_id); + txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr); if (ath10k_mac_tx_frm_has_freq(ar)) { - skb_cb->htt.txbuf->cmd_tx.offchan_tx.peerid = + txbuf->cmd_tx.offchan_tx.peerid = __cpu_to_le16(HTT_INVALID_PEERID); - skb_cb->htt.txbuf->cmd_tx.offchan_tx.freq = + txbuf->cmd_tx.offchan_tx.freq = __cpu_to_le16(freq); } else { - skb_cb->htt.txbuf->cmd_tx.peerid = + txbuf->cmd_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID); } @@ -743,12 +742,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, sg_items[0].transfer_id = 0; sg_items[0].transfer_context = NULL; - sg_items[0].vaddr = &skb_cb->htt.txbuf->htc_hdr; - sg_items[0].paddr = skb_cb->htt.txbuf_paddr + - sizeof(skb_cb->htt.txbuf->frags); - sg_items[0].len = sizeof(skb_cb->htt.txbuf->htc_hdr) + - sizeof(skb_cb->htt.txbuf->cmd_hdr) + - sizeof(skb_cb->htt.txbuf->cmd_tx); + sg_items[0].vaddr = &txbuf->htc_hdr; + sg_items[0].paddr = txbuf_paddr + + sizeof(txbuf->frags); + sg_items[0].len = sizeof(txbuf->htc_hdr) + + sizeof(txbuf->cmd_hdr) + + sizeof(txbuf->cmd_tx); sg_items[1].transfer_id = 0; sg_items[1].transfer_context = NULL; -- cgit v1.2.3-59-g8ed1b From c8c1b39a86940edd35439f1e5c9ff39888daf0f0 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Fri, 20 Nov 2015 03:56:24 +0100 Subject: dsa: mv88e6xxx.c: Hardware reset the chip if available The device tree binding now allows a gpio to be specified which is attached to the switch chips reset line. If it is defined, perform a hardware reset on the switch during setup. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index b06dba05594a..75e245c4235e 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -2323,6 +2324,7 @@ int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); u16 is_reset = (ppu_active ? 0x8800 : 0xc800); + struct gpio_desc *gpiod = ds->pd->reset; unsigned long timeout; int ret; int i; @@ -2336,6 +2338,14 @@ int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active) /* Wait for transmit queues to drain. */ usleep_range(2000, 4000); + /* If there is a gpio connected to the reset pin, toggle it */ + if (gpiod) { + gpiod_set_value_cansleep(gpiod, 1); + usleep_range(10000, 20000); + gpiod_set_value_cansleep(gpiod, 0); + usleep_range(10000, 20000); + } + /* Reset the switch. Keep the PPU active if requested. The PPU * needs to be active to support indirect phy register access * through global registers 0x18 and 0x19. -- cgit v1.2.3-59-g8ed1b From 1340181fe435ccb8ca2f996b8680bd9566860619 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 24 Aug 2015 17:27:24 -0700 Subject: fm10k: do not assume VF always has 1 queue It is possible that the PF has not yet assigned resources to the VF. Although rare, this could result in the VF attempting to read queues it does not own and result in FUM or THI faults in the PF. To prevent this, check queue 0 before we continue in init_hw_vf. Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_type.h | 1 + drivers/net/ethernet/intel/fm10k/fm10k_vf.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h index 318a212f0a78..35afd711d144 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h @@ -77,6 +77,7 @@ struct fm10k_hw; #define FM10K_PCIE_SRIOV_CTRL_VFARI 0x10 #define FM10K_ERR_PARAM -2 +#define FM10K_ERR_NO_RESOURCES -3 #define FM10K_ERR_REQUESTS_PENDING -4 #define FM10K_ERR_RESET_REQUESTED -5 #define FM10K_ERR_DMA_PENDING -6 diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c index 36c8b0aa08fd..3a18ef1cc017 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c @@ -103,7 +103,12 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw) s32 err; u16 i; - /* assume we always have at least 1 queue */ + /* verify we have at least 1 queue */ + if (!~fm10k_read_reg(hw, FM10K_TXQCTL(0)) || + !~fm10k_read_reg(hw, FM10K_RXQCTL(0))) + return FM10K_ERR_NO_RESOURCES; + + /* determine how many queues we have */ for (i = 1; tqdloc0 && (i < FM10K_MAX_QUEUES_POOL); i++) { /* verify the Descriptor cache offsets are increasing */ tqdloc = ~fm10k_read_reg(hw, FM10K_TQDLOC(i)); -- cgit v1.2.3-59-g8ed1b From 8c7ee6d2cacc7794a91875ef5fd8284b4a900d8c Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 25 Aug 2015 13:49:11 -0700 Subject: fm10k: Correct MTU for jumbo frames Based on hardware testing, the host interface supports up to 15368 bytes as the maximum frame size. To determine the correct MTU, we subtract 8 for the internal switch tag, 14 for the L2 header, and 4 for the appended FCS header, resulting in 15342 bytes of payload for our maximum MTU on jumbo frames. Signed-off-by: Matthew Vick Signed-off-by: Jacob Keller Acked-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index 14440200499b..48809e5d3f79 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -33,7 +33,7 @@ #include "fm10k_pf.h" #include "fm10k_vf.h" -#define FM10K_MAX_JUMBO_FRAME_SIZE 15358 /* Maximum supported size 15K */ +#define FM10K_MAX_JUMBO_FRAME_SIZE 15342 /* Maximum supported size 15K */ #define MAX_QUEUES FM10K_MAX_QUEUES_PF -- cgit v1.2.3-59-g8ed1b From edab421a57fbdb7f7b83fb494a48c47bc719a7f0 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 13 Sep 2015 14:15:13 +0200 Subject: ixgbe: drop null test before destroy functions Remove unneeded NULL test. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression x; @@ -if (x != NULL) \(kmem_cache_destroy\|mempool_destroy\|dma_pool_destroy\)(x); // Signed-off-by: Julia Lawall Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c index 631c603fc966..5f988703e1b7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c @@ -620,8 +620,7 @@ static void ixgbe_fcoe_dma_pool_free(struct ixgbe_fcoe *fcoe, unsigned int cpu) struct ixgbe_fcoe_ddp_pool *ddp_pool; ddp_pool = per_cpu_ptr(fcoe->ddp_pool, cpu); - if (ddp_pool->pool) - dma_pool_destroy(ddp_pool->pool); + dma_pool_destroy(ddp_pool->pool); ddp_pool->pool = NULL; } -- cgit v1.2.3-59-g8ed1b From cc1f88ba16fa5cc4769cf25dca9fafeb1546be50 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Fri, 18 Sep 2015 10:08:00 -0700 Subject: ixgbe: Delete redundant include file Delete a redundant include of net/vxlan.h. Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 4089d776d01a..450db04e3020 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -65,9 +65,6 @@ #include "ixgbe_common.h" #include "ixgbe_dcb_82599.h" #include "ixgbe_sriov.h" -#ifdef CONFIG_IXGBE_VXLAN -#include -#endif char ixgbe_driver_name[] = "ixgbe"; static const char ixgbe_driver_string[] = -- cgit v1.2.3-59-g8ed1b From a897a2adb602fe3d9223aa59393be07341d3a124 Mon Sep 17 00:00:00 2001 From: Jean Sacren Date: Sat, 19 Sep 2015 05:08:44 -0600 Subject: ixgbe: fix multiple kernel-doc errors The commit dfaf891dd3e1 ("ixgbe: Refactor the RSS configuration code") introduced a few kernel-doc errors: 1) The function name is missing; 2) The format is wrong; 3) The short description is redundant. Fix all the above for the correct execution of the kernel doc. Signed-off-by: Jean Sacren Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 450db04e3020..c9b7e5ef62aa 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3312,8 +3312,7 @@ static void ixgbe_configure_srrctl(struct ixgbe_adapter *adapter, } /** - * Return a number of entries in the RSS indirection table - * + * ixgbe_rss_indir_tbl_entries - Return RSS indirection table entries * @adapter: device handle * * - 82598/82599/X540: 128 @@ -3331,8 +3330,7 @@ u32 ixgbe_rss_indir_tbl_entries(struct ixgbe_adapter *adapter) } /** - * Write the RETA table to HW - * + * ixgbe_store_reta - Write the RETA table to HW * @adapter: device handle * * Write the RSS redirection table stored in adapter.rss_indir_tbl[] to HW. @@ -3371,8 +3369,7 @@ void ixgbe_store_reta(struct ixgbe_adapter *adapter) } /** - * Write the RETA table to HW (for x550 devices in SRIOV mode) - * + * ixgbe_store_vfreta - Write the RETA table to HW (x550 devices in SRIOV mode) * @adapter: device handle * * Write the RSS redirection table stored in adapter.rss_indir_tbl[] to HW. -- cgit v1.2.3-59-g8ed1b From 9f872986479b6e0543eb5c615e5f9491bb04e5c1 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 22 Sep 2015 14:35:35 -0700 Subject: fm10k: Fix handling of NAPI budget when multiple queues are enabled per vector This patch corrects an issue in which the polling routine would increase the budget for Rx to at least 1 per queue if multiple queues were present. This would result in Rx packets being processed when the budget was 0 which is meant to indicate that no Rx can be handled. Signed-off-by: Alexander Duyck Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_main.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index e76a44cf330c..746a1986690b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -1428,6 +1428,10 @@ static int fm10k_poll(struct napi_struct *napi, int budget) fm10k_for_each_ring(ring, q_vector->tx) clean_complete &= fm10k_clean_tx_irq(q_vector, ring); + /* Handle case where we are called by netpoll with a budget of 0 */ + if (budget <= 0) + return budget; + /* attempt to distribute budget to each queue fairly, but don't * allow the budget to go below 1 because we'll exit polling */ -- cgit v1.2.3-59-g8ed1b From 5d6002b7b822c7423e75d4651e6790bfb5642b1b Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 22 Sep 2015 14:35:41 -0700 Subject: ixgbe: Fix handling of NAPI budget when multiple queues are enabled per vector This patch corrects an issue in which the polling routine would increase the budget for Rx to at least 1 per queue if multiple queues were present. This would result in Rx packets being processed when the budget was 0 which is meant to indicate that no Rx can be handled. Signed-off-by: Alexander Duyck Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index c9b7e5ef62aa..4fa94a3ca47c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2783,7 +2783,8 @@ int ixgbe_poll(struct napi_struct *napi, int budget) ixgbe_for_each_ring(ring, q_vector->tx) clean_complete &= !!ixgbe_clean_tx_irq(q_vector, ring); - if (!ixgbe_qv_lock_napi(q_vector)) + /* Exit if we are called by netpoll or busy polling is active */ + if ((budget <= 0) || !ixgbe_qv_lock_napi(q_vector)) return budget; /* attempt to distribute budget to each queue fairly, but don't allow -- cgit v1.2.3-59-g8ed1b From d91e3a7d624590220e31ccb80a6fb5247cbfa64a Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Mon, 28 Sep 2015 14:37:47 -0700 Subject: ixgbe: Add KR mode support for CS4227 chip KR auto-neg mode is what we will be using going forward. The SW interface for this mode is different that what was used for iXFI. Signed-off-by: Mark Rustad Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 85 +++++++++++++++++++-------- 1 file changed, 62 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index ebe0ac950b14..005f01b80f4a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -26,6 +26,8 @@ #include "ixgbe_common.h" #include "ixgbe_phy.h" +static s32 ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *, ixgbe_link_speed); + static s32 ixgbe_get_invariants_X550_x(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; @@ -1257,31 +1259,71 @@ ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw, if (status) return status; - /* Configure CS4227 LINE side to 10G SR. */ - slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12); - value = IXGBE_CS4227_SPEED_10G; - status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice, - value); - - /* Configure CS4227 for HOST connection rate then type. */ - slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12); - value = speed & IXGBE_LINK_SPEED_10GB_FULL ? - IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G; - status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice, - value); + if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) { + /* Configure CS4227 LINE side to 10G SR. */ + slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12); + value = IXGBE_CS4227_SPEED_10G; + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, + slice, value); + if (status) + goto i2c_err; - slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12); - if (setup_linear) - value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1; - else + slice = IXGBE_CS4227_LINE_SPARE24_LSB + (hw->bus.lan_id << 12); value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1; - status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice, - value); + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, + slice, value); + if (status) + goto i2c_err; + + /* Configure CS4227 for HOST connection rate then type. */ + slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12); + value = speed & IXGBE_LINK_SPEED_10GB_FULL ? + IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G; + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, + slice, value); + if (status) + goto i2c_err; - /* If internal link mode is XFI, then setup XFI internal link. */ - if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) + slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12); + if (setup_linear) + value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1; + else + value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1; + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, + slice, value); + if (status) + goto i2c_err; + + /* Setup XFI internal link. */ status = ixgbe_setup_ixfi_x550em(hw, &speed); + if (status) { + hw_dbg(hw, "setup_ixfi failed with %d\n", status); + return status; + } + } else { + /* Configure internal PHY for KR/KX. */ + status = ixgbe_setup_kr_speed_x550em(hw, speed); + if (status) { + hw_dbg(hw, "setup_kr_speed failed with %d\n", status); + return status; + } + /* Configure CS4227 LINE side to proper mode. */ + slice = IXGBE_CS4227_LINE_SPARE24_LSB + (hw->bus.lan_id << 12); + if (setup_linear) + value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1; + else + value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1; + status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, + slice, value); + if (status) + goto i2c_err; + } + + return 0; + +i2c_err: + hw_dbg(hw, "combined i2c access failed with %d\n", status); return status; } @@ -1982,12 +2024,9 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) * to determine internal PHY mode. */ phy->nw_mng_if_sel = IXGBE_READ_REG(hw, IXGBE_NW_MNG_IF_SEL); - - /* If internal PHY mode is KR, then initialize KR link */ if (phy->nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE) { speed = IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL; - ret_val = ixgbe_setup_kr_speed_x550em(hw, speed); } } -- cgit v1.2.3-59-g8ed1b From 8a9ca1104da0de6dd8551237e7d0e50eeeea4e80 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 29 Sep 2015 13:11:15 -0700 Subject: ixgbevf: Limit lowest interrupt rate for adaptive interrupt moderation to 12K This patch is the ixgbevf version of commit 8ac34f10a5ea4 "ixgbe: Limit lowest interrupt rate for adaptive interrupt moderation to 12K" The same logic applies here as well as the same results since a netperf test will starve for memory in the time from one Tx interrupt to the next. As a result the ixgbevf driver underperformed when compared to vhost_net. Signed-off-by: Alexander Duyck Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ethtool.c | 2 +- drivers/net/ethernet/intel/ixgbevf/ixgbevf.h | 3 +-- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 6 +++--- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index d3e5f5b37999..c48aef613b0a 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -774,7 +774,7 @@ static int ixgbevf_set_coalesce(struct net_device *netdev, adapter->tx_itr_setting = ec->tx_coalesce_usecs; if (adapter->tx_itr_setting == 1) - tx_itr_param = IXGBE_10K_ITR; + tx_itr_param = IXGBE_12K_ITR; else tx_itr_param = adapter->tx_itr_setting; diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index ec3147279621..68ec7daa04fd 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -326,8 +326,7 @@ static inline bool ixgbevf_qv_disable(struct ixgbevf_q_vector *q_vector) #define IXGBE_MIN_RSC_ITR 24 #define IXGBE_100K_ITR 40 #define IXGBE_20K_ITR 200 -#define IXGBE_10K_ITR 400 -#define IXGBE_8K_ITR 500 +#define IXGBE_12K_ITR 336 /* Helper macros to switch between ints/sec and what the register uses. * And yes, it's the same math going both ways. The lowest value diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 2955186cd4f6..e678178c7d35 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1138,7 +1138,7 @@ static void ixgbevf_configure_msix(struct ixgbevf_adapter *adapter) if (q_vector->tx.ring && !q_vector->rx.ring) { /* Tx only vector */ if (adapter->tx_itr_setting == 1) - q_vector->itr = IXGBE_10K_ITR; + q_vector->itr = IXGBE_12K_ITR; else q_vector->itr = adapter->tx_itr_setting; } else { @@ -1196,7 +1196,7 @@ static void ixgbevf_update_itr(struct ixgbevf_q_vector *q_vector, /* simple throttle rate management * 0-20MB/s lowest (100000 ints/s) * 20-100MB/s low (20000 ints/s) - * 100-1249MB/s bulk (8000 ints/s) + * 100-1249MB/s bulk (12000 ints/s) */ /* what was last interrupt timeslice? */ timepassed_us = q_vector->itr >> 2; @@ -1247,7 +1247,7 @@ static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector) break; case bulk_latency: default: - new_itr = IXGBE_8K_ITR; + new_itr = IXGBE_12K_ITR; break; } -- cgit v1.2.3-59-g8ed1b From ef2662b2a820aaca4c147b91659bf57c06688ede Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 29 Sep 2015 15:19:43 -0700 Subject: ixgbe/ixgbevf: use napi_schedule_irqoff() The ixgbe_intr and ixgbe/ixgbevf_msix_clean_rings functions run from hard interrupt context or with interrupts already disabled in netpoll. They can use napi_schedule_irqoff() instead of napi_schedule() Signed-off-by: Alexander Duyck Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 ++-- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 4fa94a3ca47c..c95042ee30de 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2754,7 +2754,7 @@ static irqreturn_t ixgbe_msix_clean_rings(int irq, void *data) /* EIAM disabled interrupts (on this vector) for us */ if (q_vector->rx.ring || q_vector->tx.ring) - napi_schedule(&q_vector->napi); + napi_schedule_irqoff(&q_vector->napi); return IRQ_HANDLED; } @@ -2948,7 +2948,7 @@ static irqreturn_t ixgbe_intr(int irq, void *data) ixgbe_ptp_check_pps_event(adapter, eicr); /* would disable interrupts here but EIAM disabled it */ - napi_schedule(&q_vector->napi); + napi_schedule_irqoff(&q_vector->napi); /* * re-enable link(maybe) and non-queue interrupts, no flush. diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index e678178c7d35..1b15f9578adf 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1288,7 +1288,7 @@ static irqreturn_t ixgbevf_msix_clean_rings(int irq, void *data) /* EIAM disabled interrupts (on this vector) for us */ if (q_vector->rx.ring || q_vector->tx.ring) - napi_schedule(&q_vector->napi); + napi_schedule_irqoff(&q_vector->napi); return IRQ_HANDLED; } -- cgit v1.2.3-59-g8ed1b From d206563ad8f6fb41943366cf22f1aabc19d2b1a7 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Fri, 2 Oct 2015 09:23:53 -0700 Subject: ixgbe: Remove CS4227 diagnostic code Testing has now shown that the diagnostic code used with the CS4227 is no longer needed, so remove it. Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 80 --------------------------- 1 file changed, 80 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 005f01b80f4a..bf2ae8daf717 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -86,79 +86,6 @@ static s32 ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value) value); } -/** - * ixgbe_check_cs4227_reg - Perform diag on a CS4227 register - * @hw: pointer to hardware structure - * @reg: the register to check - * - * Performs a diagnostic on a register in the CS4227 chip. Returns an error - * if it is not operating correctly. - * This function assumes that the caller has acquired the proper semaphore. - */ -static s32 ixgbe_check_cs4227_reg(struct ixgbe_hw *hw, u16 reg) -{ - s32 status; - u32 retry; - u16 reg_val; - - reg_val = (IXGBE_CS4227_EDC_MODE_DIAG << 1) | 1; - status = ixgbe_write_cs4227(hw, reg, reg_val); - if (status) - return status; - for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) { - msleep(IXGBE_CS4227_CHECK_DELAY); - reg_val = 0xFFFF; - ixgbe_read_cs4227(hw, reg, ®_val); - if (!reg_val) - break; - } - if (reg_val) { - hw_err(hw, "CS4227 reg 0x%04X failed diagnostic\n", reg); - return status; - } - - return 0; -} - -/** - * ixgbe_get_cs4227_status - Return CS4227 status - * @hw: pointer to hardware structure - * - * Performs a diagnostic on the CS4227 chip. Returns an error if it is - * not operating correctly. - * This function assumes that the caller has acquired the proper semaphore. - */ -static s32 ixgbe_get_cs4227_status(struct ixgbe_hw *hw) -{ - s32 status; - u16 value = 0; - - /* Exit if the diagnostic has already been performed. */ - status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value); - if (status) - return status; - if (value == IXGBE_CS4227_RESET_COMPLETE) - return 0; - - /* Check port 0. */ - status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB); - if (status) - return status; - - status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB); - if (status) - return status; - - /* Check port 1. */ - status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB + - (1 << 12)); - if (status) - return status; - - return ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB + - (1 << 12)); -} - /** * ixgbe_read_pe - Read register from port expander * @hw: pointer to hardware structure @@ -328,13 +255,6 @@ static void ixgbe_check_cs4227(struct ixgbe_hw *hw) return; } - /* Is the CS4227 working correctly? */ - status = ixgbe_get_cs4227_status(hw); - if (status) { - hw_err(hw, "CS4227 status failed: %d", status); - goto out; - } - /* Record completion for next time. */ status = ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH, IXGBE_CS4227_RESET_COMPLETE); -- cgit v1.2.3-59-g8ed1b From cfbe4dba0d5891faa11bd4e7c1fcdc512aff2f5f Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Sun, 4 Oct 2015 01:09:49 -0700 Subject: i40evf: fix compiler warning of unused variable Compiler complained of an unused variable, which the driver was just using to store the result of a rd32 which is used to clear a register unconditionally. Just drop the unused variable and re-use one. Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index d962164dfb0f..6ad62656c75e 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -307,10 +307,9 @@ static irqreturn_t i40evf_msix_aq(int irq, void *data) struct i40e_hw *hw = &adapter->hw; u32 val; - /* handle non-queue interrupts */ - rd32(hw, I40E_VFINT_ICR01); - rd32(hw, I40E_VFINT_ICR0_ENA1); - + /* handle non-queue interrupts, these reads clear the registers */ + val = rd32(hw, I40E_VFINT_ICR01); + val = rd32(hw, I40E_VFINT_ICR0_ENA1); val = rd32(hw, I40E_VFINT_DYN_CTL01) | I40E_VFINT_DYN_CTL01_CLEARPBA_MASK; -- cgit v1.2.3-59-g8ed1b From 91a76baadec1f30e8441c3d52c2559468a4da693 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Mon, 12 Oct 2015 10:55:51 -0700 Subject: ixgbevf: use ether_addr_copy instead of memcpy replace some instances of memcpy for setting up the mac address with ether_addr_copy() Signed-off-by: Emil Tantilov Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 1b15f9578adf..47c71e1fe1ce 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -2260,10 +2260,8 @@ void ixgbevf_reset(struct ixgbevf_adapter *adapter) } if (is_valid_ether_addr(adapter->hw.mac.addr)) { - memcpy(netdev->dev_addr, adapter->hw.mac.addr, - netdev->addr_len); - memcpy(netdev->perm_addr, adapter->hw.mac.addr, - netdev->addr_len); + ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr); + ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr); } adapter->last_reset = jiffies; @@ -2659,13 +2657,13 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter) else if (is_zero_ether_addr(adapter->hw.mac.addr)) dev_info(&pdev->dev, "MAC address not assigned by administrator.\n"); - memcpy(netdev->dev_addr, hw->mac.addr, netdev->addr_len); + ether_addr_copy(netdev->dev_addr, hw->mac.addr); } if (!is_valid_ether_addr(netdev->dev_addr)) { dev_info(&pdev->dev, "Assigning random MAC address\n"); eth_hw_addr_random(netdev); - memcpy(hw->mac.addr, netdev->dev_addr, netdev->addr_len); + ether_addr_copy(hw->mac.addr, netdev->dev_addr); } /* Enable dynamic interrupt throttling rates */ @@ -3695,8 +3693,8 @@ static int ixgbevf_set_mac(struct net_device *netdev, void *p) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); - memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len); + ether_addr_copy(netdev->dev_addr, addr->sa_data); + ether_addr_copy(hw->mac.addr, addr->sa_data); spin_lock_bh(&adapter->mbx_lock); -- cgit v1.2.3-59-g8ed1b From 465fc643c2dcbe08e0debac80c225f6750b40d3c Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Mon, 12 Oct 2015 10:56:00 -0700 Subject: ixgbevf: fix spoofed packets with random MAC If ixgbevf is loaded while the corresponding PF interface is down and the driver assigns a random MAC address, that address can be overwritten with the value of hw->mac.perm_addr, which would be 0 at that point. To avoid this case we init hw->mac.perm_addr to the randomly generated address and do not set it unless we receive ACK from ixgbe. Reported-by: John Greene Signed-off-by: Emil Tantilov Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 1 + drivers/net/ethernet/intel/ixgbevf/vf.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 47c71e1fe1ce..dbbd1be47462 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -2664,6 +2664,7 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter) dev_info(&pdev->dev, "Assigning random MAC address\n"); eth_hw_addr_random(netdev); ether_addr_copy(hw->mac.addr, netdev->dev_addr); + ether_addr_copy(hw->mac.perm_addr, netdev->dev_addr); } /* Enable dynamic interrupt throttling rates */ diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index 427f3605cbfc..61a98f4c5746 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -117,7 +117,9 @@ static s32 ixgbevf_reset_hw_vf(struct ixgbe_hw *hw) msgbuf[0] != (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_NACK)) return IXGBE_ERR_INVALID_MAC_ADDR; - ether_addr_copy(hw->mac.perm_addr, addr); + if (msgbuf[0] == (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_ACK)) + ether_addr_copy(hw->mac.perm_addr, addr); + hw->mac.mc_filter_type = msgbuf[IXGBE_VF_MC_TYPE_WORD]; return 0; -- cgit v1.2.3-59-g8ed1b From 0286c67e710cc82260b8c826e5a7b75143fedbd6 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Sat, 17 Oct 2015 22:58:19 +0200 Subject: intel: i40e: fix confused code This code is pretty confused. The variable name 'bytes_not_copied' clearly indicates that the programmer knew the semantics of copy_{to,from}_user, but then the return value is checked for being negative and used as a -Exxx return value. I'm not sure this is the proper fix, but at least we get rid of the dead code which pretended to check for access faults. Signed-off-by: Rasmus Villemoes Acked-by: Shannon Nelson Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index d4b7af9a2fc8..d1a91c8178d6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -103,8 +103,8 @@ static ssize_t i40e_dbg_dump_read(struct file *filp, char __user *buffer, len = min_t(int, count, (i40e_dbg_dump_data_len - *ppos)); bytes_not_copied = copy_to_user(buffer, &i40e_dbg_dump_buf[*ppos], len); - if (bytes_not_copied < 0) - return bytes_not_copied; + if (bytes_not_copied) + return -EFAULT; *ppos += len; return len; @@ -353,8 +353,8 @@ static ssize_t i40e_dbg_command_read(struct file *filp, char __user *buffer, bytes_not_copied = copy_to_user(buffer, buf, len); kfree(buf); - if (bytes_not_copied < 0) - return bytes_not_copied; + if (bytes_not_copied) + return -EFAULT; *ppos = len; return len; @@ -981,12 +981,10 @@ static ssize_t i40e_dbg_command_write(struct file *filp, if (!cmd_buf) return count; bytes_not_copied = copy_from_user(cmd_buf, buffer, count); - if (bytes_not_copied < 0) { + if (bytes_not_copied) { kfree(cmd_buf); - return bytes_not_copied; + return -EFAULT; } - if (bytes_not_copied > 0) - count -= bytes_not_copied; cmd_buf[count] = '\0'; cmd_buf_tmp = strchr(cmd_buf, '\n'); @@ -2034,8 +2032,8 @@ static ssize_t i40e_dbg_netdev_ops_read(struct file *filp, char __user *buffer, bytes_not_copied = copy_to_user(buffer, buf, len); kfree(buf); - if (bytes_not_copied < 0) - return bytes_not_copied; + if (bytes_not_copied) + return -EFAULT; *ppos = len; return len; @@ -2068,10 +2066,8 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, memset(i40e_dbg_netdev_ops_buf, 0, sizeof(i40e_dbg_netdev_ops_buf)); bytes_not_copied = copy_from_user(i40e_dbg_netdev_ops_buf, buffer, count); - if (bytes_not_copied < 0) - return bytes_not_copied; - else if (bytes_not_copied > 0) - count -= bytes_not_copied; + if (bytes_not_copied) + return -EFAULT; i40e_dbg_netdev_ops_buf[count] = '\0'; buf_tmp = strchr(i40e_dbg_netdev_ops_buf, '\n'); -- cgit v1.2.3-59-g8ed1b From 724fe6955c88db8b249681cd78a76c10163bb0ba Mon Sep 17 00:00:00 2001 From: Saurabh Sengar Date: Mon, 23 Nov 2015 19:02:15 +0530 Subject: drivers: net: xgene: optimizing the code this patch does the following: 1 . remove unnecessary if, else condition 2 . reduce one variable 3 . change the return type of 2 functions to void as there return values turn out to be 0 always after above changes Signed-off-by: Saurabh Sengar Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 25 +++++++++--------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 991412ce6f48..6096d0242841 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1084,7 +1084,7 @@ static const struct net_device_ops xgene_ndev_ops = { }; #ifdef CONFIG_ACPI -static int xgene_get_port_id_acpi(struct device *dev, +static void xgene_get_port_id_acpi(struct device *dev, struct xgene_enet_pdata *pdata) { acpi_status status; @@ -1097,24 +1097,19 @@ static int xgene_get_port_id_acpi(struct device *dev, pdata->port_id = temp; } - return 0; + return; } #endif -static int xgene_get_port_id_dt(struct device *dev, struct xgene_enet_pdata *pdata) +static void xgene_get_port_id_dt(struct device *dev, struct xgene_enet_pdata *pdata) { u32 id = 0; - int ret; - ret = of_property_read_u32(dev->of_node, "port-id", &id); - if (ret) { - pdata->port_id = 0; - ret = 0; - } else { - pdata->port_id = id & BIT(0); - } + of_property_read_u32(dev->of_node, "port-id", &id); - return ret; + pdata->port_id = id & BIT(0); + + return; } static int xgene_get_tx_delay(struct xgene_enet_pdata *pdata) @@ -1209,13 +1204,11 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) } if (dev->of_node) - ret = xgene_get_port_id_dt(dev, pdata); + xgene_get_port_id_dt(dev, pdata); #ifdef CONFIG_ACPI else - ret = xgene_get_port_id_acpi(dev, pdata); + xgene_get_port_id_acpi(dev, pdata); #endif - if (ret) - return ret; if (!device_get_mac_address(dev, ndev->dev_addr, ETH_ALEN)) eth_hw_addr_random(ndev); -- cgit v1.2.3-59-g8ed1b From 73b1c90d3650a81ff9997f6d1ddfda6efb9fac09 Mon Sep 17 00:00:00 2001 From: Saurabh Sengar Date: Mon, 23 Nov 2015 19:21:48 +0530 Subject: net: fec: no need to test for the return type of of_property_read_u32 in case of error no need to set num_tx and num_rx = 1, because in case of error these variables will remain unchanged by of_property_read_u32 ie 1 only Signed-off-by: Saurabh Sengar Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index b2a32209ffbf..d2328fc5da57 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3277,7 +3277,6 @@ static void fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) { struct device_node *np = pdev->dev.of_node; - int err; *num_tx = *num_rx = 1; @@ -3285,13 +3284,9 @@ fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx) return; /* parse the num of tx and rx queues */ - err = of_property_read_u32(np, "fsl,num-tx-queues", num_tx); - if (err) - *num_tx = 1; + of_property_read_u32(np, "fsl,num-tx-queues", num_tx); - err = of_property_read_u32(np, "fsl,num-rx-queues", num_rx); - if (err) - *num_rx = 1; + of_property_read_u32(np, "fsl,num-rx-queues", num_rx); if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) { dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n", -- cgit v1.2.3-59-g8ed1b From bad531623253d1e1fb8d140bdc3a077216fd0c5a Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Tue, 24 Nov 2015 14:29:16 +0100 Subject: vrf: remove slave queue and private slave struct The private slave queue and slave struct haven't been used for anything and aren't needed, this allows to reduce memory usage and simplify enslave/release. We can use netdev_for_each_lower_dev() to free the vrf ports when deleting a vrf device. Also if in the future a private struct is needed for each slave, it can be implemented via lower devices' private member (similar to how bonding does it). Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/vrf.c | 68 ++++--------------------------------------------------- 1 file changed, 5 insertions(+), 63 deletions(-) (limited to 'drivers') diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 92fa3e1ea65c..c2d54c4ed556 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -46,17 +46,7 @@ #define vrf_master_get_rcu(dev) \ ((struct net_device *)rcu_dereference(dev->rx_handler_data)) -struct slave { - struct list_head list; - struct net_device *dev; -}; - -struct slave_queue { - struct list_head all_slaves; -}; - struct net_vrf { - struct slave_queue queue; struct rtable *rth; struct rt6_info *rt6; u32 tb_id; @@ -621,42 +611,9 @@ static void cycle_netdev(struct net_device *dev) } } -static struct slave *__vrf_find_slave_dev(struct slave_queue *queue, - struct net_device *dev) -{ - struct list_head *head = &queue->all_slaves; - struct slave *slave; - - list_for_each_entry(slave, head, list) { - if (slave->dev == dev) - return slave; - } - - return NULL; -} - -/* inverse of __vrf_insert_slave */ -static void __vrf_remove_slave(struct slave_queue *queue, struct slave *slave) -{ - list_del(&slave->list); -} - -static void __vrf_insert_slave(struct slave_queue *queue, struct slave *slave) -{ - list_add(&slave->list, &queue->all_slaves); -} - static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) { - struct slave *slave = kzalloc(sizeof(*slave), GFP_KERNEL); - struct net_vrf *vrf = netdev_priv(dev); - struct slave_queue *queue = &vrf->queue; - int ret = -ENOMEM; - - if (!slave) - goto out_fail; - - slave->dev = port_dev; + int ret; /* register the packet handler for slave ports */ ret = netdev_rx_handler_register(port_dev, vrf_handle_frame, dev); @@ -672,7 +629,6 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) goto out_unregister; port_dev->priv_flags |= IFF_L3MDEV_SLAVE; - __vrf_insert_slave(queue, slave); cycle_netdev(port_dev); return 0; @@ -680,7 +636,6 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) out_unregister: netdev_rx_handler_unregister(port_dev); out_fail: - kfree(slave); return ret; } @@ -695,10 +650,6 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev) /* inverse of do_vrf_add_slave */ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev) { - struct net_vrf *vrf = netdev_priv(dev); - struct slave_queue *queue = &vrf->queue; - struct slave *slave; - netdev_upper_dev_unlink(port_dev, dev); port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE; @@ -706,12 +657,6 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev) cycle_netdev(port_dev); - slave = __vrf_find_slave_dev(queue, port_dev); - if (slave) - __vrf_remove_slave(queue, slave); - - kfree(slave); - return 0; } @@ -723,15 +668,14 @@ static int vrf_del_slave(struct net_device *dev, struct net_device *port_dev) static void vrf_dev_uninit(struct net_device *dev) { struct net_vrf *vrf = netdev_priv(dev); - struct slave_queue *queue = &vrf->queue; - struct list_head *head = &queue->all_slaves; - struct slave *slave, *next; + struct net_device *port_dev; + struct list_head *iter; vrf_rtable_destroy(vrf); vrf_rt6_destroy(vrf); - list_for_each_entry_safe(slave, next, head, list) - vrf_del_slave(dev, slave->dev); + netdev_for_each_lower_dev(dev, port_dev, iter) + vrf_del_slave(dev, port_dev); free_percpu(dev->dstats); dev->dstats = NULL; @@ -741,8 +685,6 @@ static int vrf_dev_init(struct net_device *dev) { struct net_vrf *vrf = netdev_priv(dev); - INIT_LIST_HEAD(&vrf->queue.all_slaves); - dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats); if (!dev->dstats) goto out_nomem; -- cgit v1.2.3-59-g8ed1b From 99f84be60ab1d0a89fd4215678dbc7f0392e558c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 24 Nov 2015 15:40:57 +0100 Subject: sh_eth: Remove obsolete r7s72100-ether platform_device_id entry Since commit 05104c266ae9a167 ("ARM: shmobile: r7s72100: genmai: Remove legacy board file"), r7s72100 is only supported in generic DT-only ARM multi-platform builds. The driver doesn't need to match platform devices by name anymore, hence remove the corresponding platform_device_id entry. Protect r7s72100_data by #ifdef CONFIG_OF as it's now referenced on DT platforms only. Move it to a more logical position, in front of the r8a777x support, so we can have a single #ifdef covering all r7s* and r8a* support soon. This requires moving a helper function, too. Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 83 ++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index e7bab7909ed9..fca688fbefc1 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -449,6 +449,48 @@ static void sh_eth_set_duplex(struct net_device *ndev) sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); } +static void sh_eth_chip_reset(struct net_device *ndev) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + + /* reset device */ + sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR); + mdelay(1); +} + +#ifdef CONFIG_OF +/* R7S72100 */ +static struct sh_eth_cpu_data r7s72100_data = { + .chip_reset = sh_eth_chip_reset, + .set_duplex = sh_eth_set_duplex, + + .register_type = SH_ETH_REG_FAST_RZ, + + .ecsr_value = ECSR_ICD, + .ecsipr_value = ECSIPR_ICDIP, + .eesipr_value = 0xff7f009f, + + .tx_check = EESR_TC1 | EESR_FTC, + .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT | + EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | + EESR_TDE | EESR_ECI, + .fdr_value = 0x0000070f, + + .no_psr = 1, + .apr = 1, + .mpr = 1, + .tpauser = 1, + .hw_swap = 1, + .rpadir = 1, + .rpadir_value = 2 << 16, + .no_trimd = 1, + .no_ade = 1, + .hw_crc = 1, + .tsu = 1, + .shift_rd0 = 1, +}; +#endif /* CONFIG_OF */ + /* There is CPU dependent code */ static void sh_eth_set_rate_r8a777x(struct net_device *ndev) { @@ -671,15 +713,6 @@ static struct sh_eth_cpu_data sh7757_data_giga = { .tsu = 1, }; -static void sh_eth_chip_reset(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - /* reset device */ - sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR); - mdelay(1); -} - static void sh_eth_set_rate_gether(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -799,37 +832,6 @@ static struct sh_eth_cpu_data r8a7740_data = { .shift_rd0 = 1, }; -/* R7S72100 */ -static struct sh_eth_cpu_data r7s72100_data = { - .chip_reset = sh_eth_chip_reset, - .set_duplex = sh_eth_set_duplex, - - .register_type = SH_ETH_REG_FAST_RZ, - - .ecsr_value = ECSR_ICD, - .ecsipr_value = ECSIPR_ICDIP, - .eesipr_value = 0xff7f009f, - - .tx_check = EESR_TC1 | EESR_FTC, - .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT | - EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | - EESR_TDE | EESR_ECI, - .fdr_value = 0x0000070f, - - .no_psr = 1, - .apr = 1, - .mpr = 1, - .tpauser = 1, - .hw_swap = 1, - .rpadir = 1, - .rpadir_value = 2 << 16, - .no_trimd = 1, - .no_ade = 1, - .hw_crc = 1, - .tsu = 1, - .shift_rd0 = 1, -}; - static struct sh_eth_cpu_data sh7619_data = { .register_type = SH_ETH_REG_FAST_SH3_SH2, @@ -3277,7 +3279,6 @@ static struct platform_device_id sh_eth_id_table[] = { { "sh7757-ether", (kernel_ulong_t)&sh7757_data }, { "sh7757-gether", (kernel_ulong_t)&sh7757_data_giga }, { "sh7763-gether", (kernel_ulong_t)&sh7763_data }, - { "r7s72100-ether", (kernel_ulong_t)&r7s72100_data }, { "r8a7740-gether", (kernel_ulong_t)&r8a7740_data }, { "r8a777x-ether", (kernel_ulong_t)&r8a777x_data }, { "r8a7790-ether", (kernel_ulong_t)&r8a779x_data }, -- cgit v1.2.3-59-g8ed1b From c74a2248f937d857a98e927f3e3c56d3dde61e14 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 24 Nov 2015 15:40:58 +0100 Subject: sh_eth: Remove obsolete r8a779x-ether platform_device_id entries Since commit a483dcbfa21f919c ("ARM: shmobile: lager: Remove legacy board support"), R-Car Gen2 SoCs are only supported in generic DT-only ARM multi-platform builds. The driver doesn't need to match platform devices by name anymore, hence remove the corresponding platform_device_id entry. Protect r8a779x_data by #ifdef CONFIG_OF as it's now referenced on DT platforms only. Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index fca688fbefc1..1af07f9de243 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -531,6 +531,7 @@ static struct sh_eth_cpu_data r8a777x_data = { .hw_swap = 1, }; +#ifdef CONFIG_OF /* R8A7790/1 */ static struct sh_eth_cpu_data r8a779x_data = { .set_duplex = sh_eth_set_duplex, @@ -556,6 +557,7 @@ static struct sh_eth_cpu_data r8a779x_data = { .hw_swap = 1, .rmiimode = 1, }; +#endif /* CONFIG_OF */ static void sh_eth_set_rate_sh7724(struct net_device *ndev) { @@ -3281,10 +3283,6 @@ static struct platform_device_id sh_eth_id_table[] = { { "sh7763-gether", (kernel_ulong_t)&sh7763_data }, { "r8a7740-gether", (kernel_ulong_t)&r8a7740_data }, { "r8a777x-ether", (kernel_ulong_t)&r8a777x_data }, - { "r8a7790-ether", (kernel_ulong_t)&r8a779x_data }, - { "r8a7791-ether", (kernel_ulong_t)&r8a779x_data }, - { "r8a7793-ether", (kernel_ulong_t)&r8a779x_data }, - { "r8a7794-ether", (kernel_ulong_t)&r8a779x_data }, { } }; MODULE_DEVICE_TABLE(platform, sh_eth_id_table); -- cgit v1.2.3-59-g8ed1b From a0f48be33c666b11a633a94414744adbb653ecd9 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 24 Nov 2015 15:40:59 +0100 Subject: sh_eth: Remove obsolete r8a7740-gether platform_device_id entry Since commit 1fa59bda21c7fa36 ("ARM: shmobile: Remove legacy board code for Armadillo-800 EVA"), r8a7740 is only supported in generic DT-only ARM multi-platform builds. The driver doesn't need to match platform devices by name anymore, hence remove the corresponding platform_device_id entry. Protect r8a7740_data by #ifdef CONFIG_OF as it's now referenced on DT platforms only. Move it to a more logical position, in front of the r8a777x support, so we can have a single #ifdef covering all r7s* and r8a* support soon. This requires moving a few helper functions, too. Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 125 +++++++++++++++++----------------- 1 file changed, 62 insertions(+), 63 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 1af07f9de243..99618bf513b2 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -458,6 +458,25 @@ static void sh_eth_chip_reset(struct net_device *ndev) mdelay(1); } +static void sh_eth_set_rate_gether(struct net_device *ndev) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + + switch (mdp->speed) { + case 10: /* 10BASE */ + sh_eth_write(ndev, GECMR_10, GECMR); + break; + case 100:/* 100BASE */ + sh_eth_write(ndev, GECMR_100, GECMR); + break; + case 1000: /* 1000BASE */ + sh_eth_write(ndev, GECMR_1000, GECMR); + break; + default: + break; + } +} + #ifdef CONFIG_OF /* R7S72100 */ static struct sh_eth_cpu_data r7s72100_data = { @@ -489,6 +508,49 @@ static struct sh_eth_cpu_data r7s72100_data = { .tsu = 1, .shift_rd0 = 1, }; + +static void sh_eth_chip_reset_r8a7740(struct net_device *ndev) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + + /* reset device */ + sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR); + mdelay(1); + + sh_eth_select_mii(ndev); +} + +/* R8A7740 */ +static struct sh_eth_cpu_data r8a7740_data = { + .chip_reset = sh_eth_chip_reset_r8a7740, + .set_duplex = sh_eth_set_duplex, + .set_rate = sh_eth_set_rate_gether, + + .register_type = SH_ETH_REG_GIGABIT, + + .ecsr_value = ECSR_ICD | ECSR_MPD, + .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP, + .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, + + .tx_check = EESR_TC1 | EESR_FTC, + .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT | + EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | + EESR_TDE | EESR_ECI, + .fdr_value = 0x0000070f, + + .apr = 1, + .mpr = 1, + .tpauser = 1, + .bculr = 1, + .hw_swap = 1, + .rpadir = 1, + .rpadir_value = 2 << 16, + .no_trimd = 1, + .no_ade = 1, + .tsu = 1, + .select_mii = 1, + .shift_rd0 = 1, +}; #endif /* CONFIG_OF */ /* There is CPU dependent code */ @@ -715,25 +777,6 @@ static struct sh_eth_cpu_data sh7757_data_giga = { .tsu = 1, }; -static void sh_eth_set_rate_gether(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - switch (mdp->speed) { - case 10: /* 10BASE */ - sh_eth_write(ndev, GECMR_10, GECMR); - break; - case 100:/* 100BASE */ - sh_eth_write(ndev, GECMR_100, GECMR); - break; - case 1000: /* 1000BASE */ - sh_eth_write(ndev, GECMR_1000, GECMR); - break; - default: - break; - } -} - /* SH7734 */ static struct sh_eth_cpu_data sh7734_data = { .chip_reset = sh_eth_chip_reset, @@ -791,49 +834,6 @@ static struct sh_eth_cpu_data sh7763_data = { .irq_flags = IRQF_SHARED, }; -static void sh_eth_chip_reset_r8a7740(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - /* reset device */ - sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR); - mdelay(1); - - sh_eth_select_mii(ndev); -} - -/* R8A7740 */ -static struct sh_eth_cpu_data r8a7740_data = { - .chip_reset = sh_eth_chip_reset_r8a7740, - .set_duplex = sh_eth_set_duplex, - .set_rate = sh_eth_set_rate_gether, - - .register_type = SH_ETH_REG_GIGABIT, - - .ecsr_value = ECSR_ICD | ECSR_MPD, - .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP, - .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, - - .tx_check = EESR_TC1 | EESR_FTC, - .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT | - EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | - EESR_TDE | EESR_ECI, - .fdr_value = 0x0000070f, - - .apr = 1, - .mpr = 1, - .tpauser = 1, - .bculr = 1, - .hw_swap = 1, - .rpadir = 1, - .rpadir_value = 2 << 16, - .no_trimd = 1, - .no_ade = 1, - .tsu = 1, - .select_mii = 1, - .shift_rd0 = 1, -}; - static struct sh_eth_cpu_data sh7619_data = { .register_type = SH_ETH_REG_FAST_SH3_SH2, @@ -3281,7 +3281,6 @@ static struct platform_device_id sh_eth_id_table[] = { { "sh7757-ether", (kernel_ulong_t)&sh7757_data }, { "sh7757-gether", (kernel_ulong_t)&sh7757_data_giga }, { "sh7763-gether", (kernel_ulong_t)&sh7763_data }, - { "r8a7740-gether", (kernel_ulong_t)&r8a7740_data }, { "r8a777x-ether", (kernel_ulong_t)&r8a777x_data }, { } }; -- cgit v1.2.3-59-g8ed1b From 1af2729c5eaab76874944a3557713a18b494cec0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 24 Nov 2015 15:41:00 +0100 Subject: sh_eth: Remove obsolete r8a777x-ether platform_device_id entry Since commit 3d7608e4c169af03 ("ARM: shmobile: bockw: remove legacy board file and config"), R-Car Gen1 SoCs are only supported in generic DT-only ARM multi-platform builds. The driver doesn't need to match platform devices by name anymore, hence remove the corresponding platform_device_id entry. Protect sh_eth_set_rate_r8a777x() and r8a777x_data by #ifdef CONFIG_OF, as they're now referenced on DT platforms only. Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 99618bf513b2..7f3c6109d45e 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -551,7 +551,6 @@ static struct sh_eth_cpu_data r8a7740_data = { .select_mii = 1, .shift_rd0 = 1, }; -#endif /* CONFIG_OF */ /* There is CPU dependent code */ static void sh_eth_set_rate_r8a777x(struct net_device *ndev) @@ -593,7 +592,6 @@ static struct sh_eth_cpu_data r8a777x_data = { .hw_swap = 1, }; -#ifdef CONFIG_OF /* R8A7790/1 */ static struct sh_eth_cpu_data r8a779x_data = { .set_duplex = sh_eth_set_duplex, @@ -3281,7 +3279,6 @@ static struct platform_device_id sh_eth_id_table[] = { { "sh7757-ether", (kernel_ulong_t)&sh7757_data }, { "sh7757-gether", (kernel_ulong_t)&sh7757_data_giga }, { "sh7763-gether", (kernel_ulong_t)&sh7763_data }, - { "r8a777x-ether", (kernel_ulong_t)&r8a777x_data }, { } }; MODULE_DEVICE_TABLE(platform, sh_eth_id_table); -- cgit v1.2.3-59-g8ed1b From 9458ceab49179b7fd2d5192fd9dcf316ca195dc0 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 24 Nov 2015 15:30:21 -0800 Subject: net: phy: bcm7xxx: Add entry for Broadcom BCM7435 Add a PHY entry for the Broadcom BCM7435 chips, this is a 40nm generation Ethernet PHY which is analogous to its 7425 and 7429 counter parts. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm7xxx.c | 14 ++++++++++++++ include/linux/brcmphy.h | 1 + 2 files changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 03d4809a9126..d4083c381cd1 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -360,6 +360,19 @@ static struct phy_driver bcm7xxx_driver[] = { .suspend = bcm7xxx_suspend, .resume = bcm7xxx_config_init, .driver = { .owner = THIS_MODULE }, +}, { + .phy_id = PHY_ID_BCM7435, + .phy_id_mask = 0xfffffff0, + .name = "Broadcom BCM7435", + .features = PHY_GBIT_FEATURES | + SUPPORTED_Pause | SUPPORTED_Asym_Pause, + .flags = PHY_IS_INTERNAL, + .config_init = bcm7xxx_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .suspend = bcm7xxx_suspend, + .resume = bcm7xxx_config_init, + .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_BCM_OUI_4, .phy_id_mask = 0xffff0000, @@ -395,6 +408,7 @@ static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { { PHY_ID_BCM7425, 0xfffffff0, }, { PHY_ID_BCM7429, 0xfffffff0, }, { PHY_ID_BCM7439, 0xfffffff0, }, + { PHY_ID_BCM7435, 0xfffffff0, }, { PHY_ID_BCM7445, 0xfffffff0, }, { PHY_BCM_OUI_4, 0xffff0000 }, { PHY_BCM_OUI_5, 0xffffff00 }, diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 59f4a7304419..f0ba9c2ec639 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -26,6 +26,7 @@ #define PHY_ID_BCM7366 0x600d8490 #define PHY_ID_BCM7425 0x600d86b0 #define PHY_ID_BCM7429 0x600d8730 +#define PHY_ID_BCM7435 0x600d8750 #define PHY_ID_BCM7439 0x600d8480 #define PHY_ID_BCM7439_2 0xae025080 #define PHY_ID_BCM7445 0x600d8510 -- cgit v1.2.3-59-g8ed1b From de125aaecf3f06984dd32335f1e6a41c80b71011 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 29 Sep 2015 15:19:56 -0700 Subject: fm10k: use napi_schedule_irqoff() The fm10k_msix_clean_rings function runs from hard interrupt context or with interrupts already disabled in netpoll. It can use napi_schedule_irqoff() instead of napi_schedule() Signed-off-by: Alexander Duyck Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 74be792f3f1b..5fbffbaefe32 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -846,7 +846,7 @@ static irqreturn_t fm10k_msix_clean_rings(int __always_unused irq, void *data) struct fm10k_q_vector *q_vector = data; if (q_vector->rx.count || q_vector->tx.count) - napi_schedule(&q_vector->napi); + napi_schedule_irqoff(&q_vector->napi); return IRQ_HANDLED; } -- cgit v1.2.3-59-g8ed1b From 9c883bd3ebd127785ef0538b04e2d4b4c64c4c2d Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 21 Oct 2015 19:47:02 -0400 Subject: i40e/i40evf: remove unused tunnel parameter Code was moved into a separate function some time ago. Change-ID: Icabbe71ce05cf5d716d3e1152cdd9cd41d11bcb5 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 11 ++++------- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 8 +++----- 2 files changed, 7 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 6649ce4ba2de..98680b66d16e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2186,14 +2186,12 @@ out: * @tx_ring: ptr to the ring to send * @skb: ptr to the skb we're sending * @hdr_len: ptr to the size of the packet header - * @cd_type_cmd_tso_mss: ptr to u64 object - * @cd_tunneling: ptr to context descriptor bits + * @cd_type_cmd_tso_mss: Quad Word 1 * * Returns 0 if no TSO can happen, 1 if tso is going, or error **/ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, - u8 *hdr_len, u64 *cd_type_cmd_tso_mss, - u32 *cd_tunneling) + u8 *hdr_len, u64 *cd_type_cmd_tso_mss) { u32 cd_cmd, cd_tso_len, cd_mss; struct ipv6hdr *ipv6h; @@ -2246,7 +2244,7 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, * @tx_ring: ptr to the ring to send * @skb: ptr to the skb we're sending * @tx_flags: the collected send information - * @cd_type_cmd_tso_mss: ptr to u64 object + * @cd_type_cmd_tso_mss: Quad Word 1 * * Returns 0 if no Tx timestamp can happen and 1 if the timestamp will happen **/ @@ -2825,8 +2823,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, else if (protocol == htons(ETH_P_IPV6)) tx_flags |= I40E_TX_FLAGS_IPV6; - tso = i40e_tso(tx_ring, skb, &hdr_len, - &cd_type_cmd_tso_mss, &cd_tunneling); + tso = i40e_tso(tx_ring, skb, &hdr_len, &cd_type_cmd_tso_mss); if (tso < 0) goto out_drop; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 77968b184b1f..a4eea0898186 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1436,13 +1436,12 @@ out: * @tx_ring: ptr to the ring to send * @skb: ptr to the skb we're sending * @hdr_len: ptr to the size of the packet header - * @cd_tunneling: ptr to context descriptor bits + * @cd_type_cmd_tso_mss: Quad Word 1 * * Returns 0 if no TSO can happen, 1 if tso is going, or error **/ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, - u8 *hdr_len, u64 *cd_type_cmd_tso_mss, - u32 *cd_tunneling) + u8 *hdr_len, u64 *cd_type_cmd_tso_mss) { u32 cd_cmd, cd_tso_len, cd_mss; struct ipv6hdr *ipv6h; @@ -1979,8 +1978,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, else if (protocol == htons(ETH_P_IPV6)) tx_flags |= I40E_TX_FLAGS_IPV6; - tso = i40e_tso(tx_ring, skb, &hdr_len, - &cd_type_cmd_tso_mss, &cd_tunneling); + tso = i40e_tso(tx_ring, skb, &hdr_len, &cd_type_cmd_tso_mss); if (tso < 0) goto out_drop; -- cgit v1.2.3-59-g8ed1b From b875f99b4cb454e0aa1e68d2d8792112a1e07050 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 21 Oct 2015 19:47:03 -0400 Subject: i40e: Change BUG_ON to WARN_ON in service event complete There's no need to kill the thread and eventually the kernel in this case. In fact, the remainder of the code won't hurt anything anyway, so just complain that we're here and move along. Prompted by a recent Linus diatribe. Change-ID: Iec020d8bcfedffc1cd2553cc6905fd915bb3e670 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index b825f978d441..7715c5486818 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5738,7 +5738,7 @@ static void i40e_handle_lan_overflow_event(struct i40e_pf *pf, **/ static void i40e_service_event_complete(struct i40e_pf *pf) { - BUG_ON(!test_bit(__I40E_SERVICE_SCHED, &pf->state)); + WARN_ON(!test_bit(__I40E_SERVICE_SCHED, &pf->state)); /* flush memory to make sure state is correct before next watchog */ smp_mb__before_atomic(); -- cgit v1.2.3-59-g8ed1b From 7fd89545f3374a061669a279ae0e084b0ddbb53d Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 21 Oct 2015 19:47:04 -0400 Subject: i40e: remove BUG_ON from feature string building There's really no reason to kill the kernel thread just because of a little info string. This reworks the code to use snprintf's limiting to assure that the string is never too long, and WARN_ON to still put out a warning that we might want to look at the feature list length. Prompted by a recent Linus diatribe. Change-ID: If52ba5ca1c2344d8bf454a31bbb805eb5d2c5802 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 34 +++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7715c5486818..7a4595a6b28e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -10111,10 +10111,12 @@ static int i40e_setup_pf_filter_control(struct i40e_pf *pf) } #define INFO_STRING_LEN 255 +#define REMAIN(__x) (INFO_STRING_LEN - (__x)) static void i40e_print_features(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; char *buf, *string; + int i = 0; string = kzalloc(INFO_STRING_LEN, GFP_KERNEL); if (!string) { @@ -10124,42 +10126,42 @@ static void i40e_print_features(struct i40e_pf *pf) buf = string; - buf += sprintf(string, "Features: PF-id[%d] ", hw->pf_id); + i += snprintf(&buf[i], REMAIN(i), "Features: PF-id[%d] ", hw->pf_id); #ifdef CONFIG_PCI_IOV - buf += sprintf(buf, "VFs: %d ", pf->num_req_vfs); + i += snprintf(&buf[i], REMAIN(i), "VFs: %d ", pf->num_req_vfs); #endif - buf += sprintf(buf, "VSIs: %d QP: %d RX: %s ", - pf->hw.func_caps.num_vsis, - pf->vsi[pf->lan_vsi]->num_queue_pairs, - pf->flags & I40E_FLAG_RX_PS_ENABLED ? "PS" : "1BUF"); + i += snprintf(&buf[i], REMAIN(i), "VSIs: %d QP: %d RX: %s ", + pf->hw.func_caps.num_vsis, + pf->vsi[pf->lan_vsi]->num_queue_pairs, + pf->flags & I40E_FLAG_RX_PS_ENABLED ? "PS" : "1BUF"); if (pf->flags & I40E_FLAG_RSS_ENABLED) - buf += sprintf(buf, "RSS "); + i += snprintf(&buf[i], REMAIN(i), "RSS "); if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) - buf += sprintf(buf, "FD_ATR "); + i += snprintf(&buf[i], REMAIN(i), "FD_ATR "); if (pf->flags & I40E_FLAG_FD_SB_ENABLED) { - buf += sprintf(buf, "FD_SB "); - buf += sprintf(buf, "NTUPLE "); + i += snprintf(&buf[i], REMAIN(i), "FD_SB "); + i += snprintf(&buf[i], REMAIN(i), "NTUPLE "); } if (pf->flags & I40E_FLAG_DCB_CAPABLE) - buf += sprintf(buf, "DCB "); + i += snprintf(&buf[i], REMAIN(i), "DCB "); #if IS_ENABLED(CONFIG_VXLAN) - buf += sprintf(buf, "VxLAN "); + i += snprintf(&buf[i], REMAIN(i), "VxLAN "); #endif if (pf->flags & I40E_FLAG_PTP) - buf += sprintf(buf, "PTP "); + i += snprintf(&buf[i], REMAIN(i), "PTP "); #ifdef I40E_FCOE if (pf->flags & I40E_FLAG_FCOE_ENABLED) - buf += sprintf(buf, "FCOE "); + i += snprintf(&buf[i], REMAIN(i), "FCOE "); #endif if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) - buf += sprintf(buf, "VEB "); + i += snprintf(&buf[i], REMAIN(i), "VEPA "); else buf += sprintf(buf, "VEPA "); - BUG_ON(buf > (string + INFO_STRING_LEN)); dev_info(&pf->pdev->dev, "%s\n", string); kfree(string); + WARN_ON(i > INFO_STRING_LEN); } /** -- cgit v1.2.3-59-g8ed1b From f9b26ebb6e46f2231adc22d69f4150dc53f19b74 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Wed, 21 Oct 2015 19:47:05 -0400 Subject: i40e: remove BUG_ON from FCoE setup There's no need to kill the kernel thread here. If this condition was true, the probe() would have died long before we got here. In any case, we'll get the same result when this code tries to use the VSI pointer being checked. Prompted by a recent Linus diatribe. Change-ID: I62f531cac34d4fc28ff9657d5b2d9523ae5e33a4 Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_fcoe.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c index fe5d9bf3ed6d..579a46ca82df 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c +++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c @@ -1544,8 +1544,6 @@ void i40e_fcoe_vsi_setup(struct i40e_pf *pf) if (!(pf->flags & I40E_FLAG_FCOE_ENABLED)) return; - BUG_ON(!pf->vsi[pf->lan_vsi]); - for (i = 0; i < pf->num_alloc_vsi; i++) { vsi = pf->vsi[i]; if (vsi && vsi->type == I40E_VSI_FCOE) { -- cgit v1.2.3-59-g8ed1b From 4f2f017c6101ab2ba202d6059c238c15577ad38b Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Wed, 21 Oct 2015 19:47:07 -0400 Subject: i40e: Workaround fix for mss < 256 issue HW/NVM sets a limit of no less than 256 bytes for MSS. Stack can send as low as 76 bytes MSS. This patch lowers the HW limit to 64 bytes to avoid MDDs from firing and causing a reset when the MSS is lower than 256. Change-ID: I36b500a6bb227d283c3e321a7718e0672b11fab0 Signed-off-by: Anjali Singhai Jain Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7a4595a6b28e..781a6f4880f2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6685,6 +6685,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) struct i40e_hw *hw = &pf->hw; u8 set_fc_aq_fail = 0; i40e_status ret; + u32 val; u32 v; /* Now we wait for GRST to settle out. @@ -6823,6 +6824,20 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) } } + /* Reconfigure hardware for allowing smaller MSS in the case + * of TSO, so that we avoid the MDD being fired and causing + * a reset in the case of small MSS+TSO. + */ +#define I40E_REG_MSS 0x000E64DC +#define I40E_REG_MSS_MIN_MASK 0x3FF0000 +#define I40E_64BYTE_MSS 0x400000 + val = rd32(hw, I40E_REG_MSS); + if ((val & I40E_REG_MSS_MIN_MASK) > I40E_64BYTE_MSS) { + val &= ~I40E_REG_MSS_MIN_MASK; + val |= I40E_64BYTE_MSS; + wr32(hw, I40E_REG_MSS, val); + } + if (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 33)) || (pf->hw.aq.fw_maj_ver < 4)) { msleep(75); @@ -10185,6 +10200,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) u16 link_status; int err; u32 len; + u32 val; u32 i; u8 set_fc_aq_fail; @@ -10489,6 +10505,17 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i40e_stat_str(&pf->hw, err), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + /* Reconfigure hardware for allowing smaller MSS in the case + * of TSO, so that we avoid the MDD being fired and causing + * a reset in the case of small MSS+TSO. + */ + val = rd32(hw, I40E_REG_MSS); + if ((val & I40E_REG_MSS_MIN_MASK) > I40E_64BYTE_MSS) { + val &= ~I40E_REG_MSS_MIN_MASK; + val |= I40E_64BYTE_MSS; + wr32(hw, I40E_REG_MSS, val); + } + if (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 33)) || (pf->hw.aq.fw_maj_ver < 4)) { msleep(75); -- cgit v1.2.3-59-g8ed1b From 164c9f54631beca4d174f306acdcaec2bdeef52e Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Wed, 21 Oct 2015 19:47:08 -0400 Subject: i40e/i40evf: Add a stat to track how many times we have to do a force WB When in NAPI with interrupts disabled, the HW needs to be forced to do a write back on TX if the number of descriptors pending are less than a cache line. This stat helps keep track of how many times we get into this situation. Change-ID: I76c1bcc7ebccd6bffcc5aa33bfe05f2fa1c9a984 Signed-off-by: Anjali Singhai Jain Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 1 + drivers/net/ethernet/intel/i40e/i40e_main.c | 5 ++++- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 4 +++- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 1 + drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 4 +++- drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 1 + 7 files changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 4dd3e26129b4..ca07a7b7642e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -487,6 +487,7 @@ struct i40e_vsi { u32 tx_restart; u32 tx_busy; u64 tx_linearize; + u64 tx_force_wb; u32 rx_buf_failed; u32 rx_page_failed; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 3f385ffe420f..eeb1af4885b2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -88,6 +88,7 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = { I40E_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast), I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol), I40E_VSI_STAT("tx_linearize", tx_linearize), + I40E_VSI_STAT("tx_force_wb", tx_force_wb), }; /* These PF_STATs might look like duplicates of some NETDEV_STATs, diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 781a6f4880f2..0e6abc2ec16d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -881,6 +881,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) u64 bytes, packets; unsigned int start; u64 tx_linearize; + u64 tx_force_wb; u64 rx_p, rx_b; u64 tx_p, tx_b; u16 q; @@ -899,7 +900,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) */ rx_b = rx_p = 0; tx_b = tx_p = 0; - tx_restart = tx_busy = tx_linearize = 0; + tx_restart = tx_busy = tx_linearize = tx_force_wb = 0; rx_page = 0; rx_buf = 0; rcu_read_lock(); @@ -917,6 +918,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) tx_restart += p->tx_stats.restart_queue; tx_busy += p->tx_stats.tx_busy; tx_linearize += p->tx_stats.tx_linearize; + tx_force_wb += p->tx_stats.tx_force_wb; /* Rx queue is part of the same block as Tx queue */ p = &p[1]; @@ -934,6 +936,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) vsi->tx_restart = tx_restart; vsi->tx_busy = tx_busy; vsi->tx_linearize = tx_linearize; + vsi->tx_force_wb = tx_force_wb; vsi->rx_page_failed = rx_page; vsi->rx_buf_failed = rx_buf; diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 98680b66d16e..dbd2bcae763d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1925,8 +1925,10 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) /* If work not completed, return budget and polling will return */ if (!clean_complete) { tx_only: - if (arm_wb) + if (arm_wb) { + q_vector->tx.ring[0].tx_stats.tx_force_wb++; i40e_force_wb(vsi, q_vector); + } return budget; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 6779fb771d6a..dccc1eb576f2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -202,6 +202,7 @@ struct i40e_tx_queue_stats { u64 tx_busy; u64 tx_done_old; u64 tx_linearize; + u64 tx_force_wb; }; struct i40e_rx_queue_stats { diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index a4eea0898186..8629a9f77e42 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1363,8 +1363,10 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) /* If work not completed, return budget and polling will return */ if (!clean_complete) { tx_only: - if (arm_wb) + if (arm_wb) { + q_vector->tx.ring[0].tx_stats.tx_force_wb++; i40evf_force_wb(vsi, q_vector); + } return budget; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index ebc1bf77f036..997b374e86af 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -201,6 +201,7 @@ struct i40e_tx_queue_stats { u64 tx_busy; u64 tx_done_old; u64 tx_linearize; + u64 tx_force_wb; }; struct i40e_rx_queue_stats { -- cgit v1.2.3-59-g8ed1b From 1f9610e4777c391e1e749ffc646b29dbff920834 Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Wed, 21 Oct 2015 19:47:09 -0400 Subject: i40e: Move the saving of old link info from handle_link_event to link_event The watchdog only calls link_event not handle_link_event which means that we need to save the old information in link_event. Previously when polling we were comparing current data to the old data saved the last time we actually received a link event. This means that the polling would only fix link status changes in one direction depending on what the last old data saved off was. Change-ID: Ie590f30fdbcb133d0ddad4e07e3eb1aad58255b3 Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 0e6abc2ec16d..9c0a381cf420 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6016,6 +6016,9 @@ static void i40e_link_event(struct i40e_pf *pf) i40e_status status; bool new_link, old_link; + /* save off old link status information */ + pf->hw.phy.link_info_old = pf->hw.phy.link_info; + /* set this to force the get_link_status call to refresh state */ pf->hw.phy.get_link_info = true; @@ -6150,13 +6153,9 @@ unlock: static void i40e_handle_link_event(struct i40e_pf *pf, struct i40e_arq_event_info *e) { - struct i40e_hw *hw = &pf->hw; struct i40e_aqc_get_link_status *status = (struct i40e_aqc_get_link_status *)&e->desc.params.raw; - /* save off old link status information */ - hw->phy.link_info_old = hw->phy.link_info; - /* Do a new status request to re-enable LSE reporting * and load new status information into the hw struct * This completely ignores any state information -- cgit v1.2.3-59-g8ed1b From 2f175f552ddb2498742c925332ce01b602fa029a Mon Sep 17 00:00:00 2001 From: Helin Zhang Date: Wed, 21 Oct 2015 19:47:10 -0400 Subject: i40e/i40evf: Add comment to #endif Add a comment to the #endif to more easily match it with its #if. Change-ID: I47eb0a60a17dc6d2f01a930e45006d2dc82e044f Signed-off-by: Helin Zhang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h | 2 +- drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 6584b6cd73fd..61a497935941 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -2403,4 +2403,4 @@ struct i40e_aqc_debug_modify_internals { I40E_CHECK_CMD_LENGTH(i40e_aqc_debug_modify_internals); -#endif +#endif /* _I40E_ADMINQ_CMD_H_ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h index fcb9ef34cc7a..1c76389bd888 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h @@ -2311,4 +2311,4 @@ struct i40e_aqc_debug_modify_internals { I40E_CHECK_CMD_LENGTH(i40e_aqc_debug_modify_internals); -#endif +#endif /* _I40E_ADMINQ_CMD_H_ */ -- cgit v1.2.3-59-g8ed1b From 8d8f2295d0752230e0a697af33e5af96561b64a0 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Wed, 21 Oct 2015 19:47:11 -0400 Subject: i40e/i40evf: clean up error messages Clean up and enhance error messages related to VF MAC/VLAN filters. Indicate which VF is having issues, and if possible indicate the MAC address or VLAN involved. Also, when an error is returned from the PF driver, print useful information about what went wrong, for the most likely cases. Change-ID: Ib3d15eef9e3369a78fd142948671e5fa26d921b8 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 21 +++++++++-------- .../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 26 +++++++++++++++++++--- 2 files changed, 35 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 44462b40f2d7..9c54ca2bd04d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1623,7 +1623,8 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) if (!f) { dev_err(&pf->pdev->dev, - "Unable to add VF MAC filter\n"); + "Unable to add MAC filter %pM for VF %d\n", + al->list[i].addr, vf->vf_id); ret = I40E_ERR_PARAM; spin_unlock_bh(&vsi->mac_filter_list_lock); goto error_param; @@ -1633,7 +1634,8 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) /* program the updated filter list */ if (i40e_sync_vsi_filters(vsi, false)) - dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n"); + dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters\n", + vf->vf_id); error_param: /* send the response to the VF */ @@ -1669,8 +1671,8 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) for (i = 0; i < al->num_elements; i++) { if (is_broadcast_ether_addr(al->list[i].addr) || is_zero_ether_addr(al->list[i].addr)) { - dev_err(&pf->pdev->dev, "invalid VF MAC addr %pM\n", - al->list[i].addr); + dev_err(&pf->pdev->dev, "Invalid MAC addr %pM for VF %d\n", + al->list[i].addr, vf->vf_id); ret = I40E_ERR_INVALID_MAC_ADDR; goto error_param; } @@ -1686,7 +1688,8 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) /* program the updated filter list */ if (i40e_sync_vsi_filters(vsi, false)) - dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n"); + dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters\n", + vf->vf_id); error_param: /* send the response to the VF */ @@ -1740,8 +1743,8 @@ static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) if (ret) dev_err(&pf->pdev->dev, - "Unable to add VF vlan filter %d, error %d\n", - vfl->vlan_id[i], ret); + "Unable to add VLAN filter %d for VF %d, error %d\n", + vfl->vlan_id[i], vf->vf_id, ret); } error_param: @@ -1792,8 +1795,8 @@ static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) if (ret) dev_err(&pf->pdev->dev, - "Unable to delete VF vlan filter %d, error %d\n", - vfl->vlan_id[i], ret); + "Unable to delete VLAN filter %d for VF %d, error %d\n", + vfl->vlan_id[i], vf->vf_id, ret); } error_param: diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 32e620e1eb5c..091ef6a8546a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -724,9 +724,29 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, return; } if (v_retval) { - dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n", - v_retval, i40evf_stat_str(&adapter->hw, v_retval), - v_opcode); + switch (v_opcode) { + case I40E_VIRTCHNL_OP_ADD_VLAN: + dev_err(&adapter->pdev->dev, "Failed to add VLAN filter, error %s\n", + i40evf_stat_str(&adapter->hw, v_retval)); + break; + case I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS: + dev_err(&adapter->pdev->dev, "Failed to add MAC filter, error %s\n", + i40evf_stat_str(&adapter->hw, v_retval)); + break; + case I40E_VIRTCHNL_OP_DEL_VLAN: + dev_err(&adapter->pdev->dev, "Failed to delete VLAN filter, error %s\n", + i40evf_stat_str(&adapter->hw, v_retval)); + break; + case I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS: + dev_err(&adapter->pdev->dev, "Failed to delete MAC filter, error %s\n", + i40evf_stat_str(&adapter->hw, v_retval)); + break; + default: + dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n", + v_retval, + i40evf_stat_str(&adapter->hw, v_retval), + v_opcode); + } } switch (v_opcode) { case I40E_VIRTCHNL_OP_GET_STATS: { -- cgit v1.2.3-59-g8ed1b From 1418c3458118c6969d08e23aa377da7e2a7be36c Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Wed, 21 Oct 2015 19:47:12 -0400 Subject: i40evf: handle many MAC filters correctly When a lot (many hundreds) of MAC or VLAN filters are added at one time, we can overflow the Admin Queue buffer size with all the requests. Unfortunately, the driver would then calculate the message size incorrectly, causing it to be rejected by the PF. Furthermore, there was no mechanism to trigger another request to allow for configuring the rest of the filters that didn't fit into the first request. To fix this, recalculate the correct buffer size when we detect the overflow condition instead of just assuming the max buffer size. Also, don't clear the request bit in adapter->aq_required when we have an overflow, so that the rest of the filters can be processed later. Change-ID: Idd7cbbc5af31315e0dcb1b10e6a02ad9817ce65c Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- .../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 32 ++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 091ef6a8546a..46b051684908 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -391,6 +391,7 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter) struct i40e_virtchnl_ether_addr_list *veal; int len, i = 0, count = 0; struct i40evf_mac_filter *f; + bool more = false; if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ @@ -415,7 +416,9 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter) count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_ether_addr_list)) / sizeof(struct i40e_virtchnl_ether_addr); - len = I40EVF_MAX_AQ_BUF_SIZE; + len = sizeof(struct i40e_virtchnl_ether_addr_list) + + (count * sizeof(struct i40e_virtchnl_ether_addr)); + more = true; } veal = kzalloc(len, GFP_ATOMIC); @@ -431,7 +434,8 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter) f->add = false; } } - adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_MAC_FILTER; + if (!more) + adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_MAC_FILTER; i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS, (u8 *)veal, len); kfree(veal); @@ -450,6 +454,7 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter) struct i40e_virtchnl_ether_addr_list *veal; struct i40evf_mac_filter *f, *ftmp; int len, i = 0, count = 0; + bool more = false; if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ @@ -474,7 +479,9 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter) count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_ether_addr_list)) / sizeof(struct i40e_virtchnl_ether_addr); - len = I40EVF_MAX_AQ_BUF_SIZE; + len = sizeof(struct i40e_virtchnl_ether_addr_list) + + (count * sizeof(struct i40e_virtchnl_ether_addr)); + more = true; } veal = kzalloc(len, GFP_ATOMIC); if (!veal) @@ -490,7 +497,8 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter) kfree(f); } } - adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_MAC_FILTER; + if (!more) + adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_MAC_FILTER; i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS, (u8 *)veal, len); kfree(veal); @@ -509,6 +517,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter) struct i40e_virtchnl_vlan_filter_list *vvfl; int len, i = 0, count = 0; struct i40evf_vlan_filter *f; + bool more = false; if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ @@ -534,7 +543,9 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter) count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_vlan_filter_list)) / sizeof(u16); - len = I40EVF_MAX_AQ_BUF_SIZE; + len = sizeof(struct i40e_virtchnl_vlan_filter_list) + + (count * sizeof(u16)); + more = true; } vvfl = kzalloc(len, GFP_ATOMIC); if (!vvfl) @@ -549,7 +560,8 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter) f->add = false; } } - adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_VLAN_FILTER; + if (!more) + adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_VLAN_FILTER; i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_ADD_VLAN, (u8 *)vvfl, len); kfree(vvfl); } @@ -567,6 +579,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter) struct i40e_virtchnl_vlan_filter_list *vvfl; struct i40evf_vlan_filter *f, *ftmp; int len, i = 0, count = 0; + bool more = false; if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ @@ -592,7 +605,9 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter) count = (I40EVF_MAX_AQ_BUF_SIZE - sizeof(struct i40e_virtchnl_vlan_filter_list)) / sizeof(u16); - len = I40EVF_MAX_AQ_BUF_SIZE; + len = sizeof(struct i40e_virtchnl_vlan_filter_list) + + (count * sizeof(u16)); + more = true; } vvfl = kzalloc(len, GFP_ATOMIC); if (!vvfl) @@ -608,7 +623,8 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter) kfree(f); } } - adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_VLAN_FILTER; + if (!more) + adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_VLAN_FILTER; i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_DEL_VLAN, (u8 *)vvfl, len); kfree(vvfl); } -- cgit v1.2.3-59-g8ed1b From 3e3aa21fe9265bdc45ff795dbcc90bcdd2f1017c Mon Sep 17 00:00:00 2001 From: Helin Zhang Date: Wed, 21 Oct 2015 19:47:13 -0400 Subject: i40e: return the number of enabled queues for ETHTOOL_GRXRINGS This patch fixes a problem where using ethtool rxnfc command could let RX flow hash be set on disabled queues. This patch fixes the problem by returning the number of enabled queues before setting rxnfc. Change-ID: Idbac86b0b47ddacc8deee7cd257e41de01cbe5c0 Signed-off-by: Helin Zhang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index eeb1af4885b2..a89da8a22092 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2111,7 +2111,7 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, switch (cmd->cmd) { case ETHTOOL_GRXRINGS: - cmd->data = vsi->alloc_queue_pairs; + cmd->data = vsi->num_queue_pairs; ret = 0; break; case ETHTOOL_GRXFH: -- cgit v1.2.3-59-g8ed1b From e69ff813af354ae445518d44e299eeb85d5037e2 Mon Sep 17 00:00:00 2001 From: Helin Zhang Date: Wed, 21 Oct 2015 19:56:22 -0400 Subject: i40e: rework the functions to configure RSS with similar parameters Adjust the RSS configure functions so that there is a generic way to hook to ethtool hooks. Change-ID: If446e34fcfaf1bc3320d9d319829a095b5976e67 Signed-off-by: Helin Zhang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 1 - drivers/net/ethernet/intel/i40e/i40e_main.c | 95 +++++++++++++++++++------- 3 files changed, 71 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index ca07a7b7642e..f6b747cf94d6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -187,6 +187,7 @@ struct i40e_lump_tracking { #define I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR (I40E_FDIR_BUFFER_HEAD_ROOM * 4) #define I40E_HKEY_ARRAY_SIZE ((I40E_PFQF_HKEY_MAX_INDEX + 1) * 4) +#define I40E_HLUT_ARRAY_SIZE ((I40E_PFQF_HLUT_MAX_INDEX + 1) * 4) enum i40e_fd_stat_idx { I40E_FD_STAT_ATR, diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index a89da8a22092..f26c0d16e43e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2584,7 +2584,6 @@ static int i40e_set_channels(struct net_device *dev, return -EINVAL; } -#define I40E_HLUT_ARRAY_SIZE ((I40E_PFQF_HLUT_MAX_INDEX + 1) * 4) /** * i40e_get_rxfh_key_size - get the RSS hash key size * @netdev: network interface device structure diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 9c0a381cf420..9fe68024d971 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -55,6 +55,8 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit); static int i40e_setup_misc_vector(struct i40e_pf *pf); static void i40e_determine_queue_usage(struct i40e_pf *pf); static int i40e_setup_pf_filter_control(struct i40e_pf *pf); +static void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut, + u16 rss_table_size, u16 rss_size); static void i40e_fdir_sb_setup(struct i40e_pf *pf); static int i40e_veb_get_bw_info(struct i40e_veb *veb); @@ -7797,7 +7799,8 @@ static int i40e_setup_misc_vector(struct i40e_pf *pf) * @vsi: vsi structure * @seed: RSS hash seed **/ -static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed) +static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed, + u8 *lut, u16 lut_size) { struct i40e_aqc_get_set_rss_key_data rss_key; struct i40e_pf *pf = vsi->back; @@ -7850,49 +7853,79 @@ static int i40e_vsi_config_rss(struct i40e_vsi *vsi) { u8 seed[I40E_HKEY_ARRAY_SIZE]; struct i40e_pf *pf = vsi->back; + u8 *lut; + int ret; + + if (!(pf->flags & I40E_FLAG_RSS_AQ_CAPABLE)) + return 0; + + lut = kzalloc(vsi->rss_table_size, GFP_KERNEL); + if (!lut) + return -ENOMEM; + i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size); netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); vsi->rss_size = min_t(int, pf->rss_size, vsi->num_queue_pairs); + ret = i40e_config_rss_aq(vsi, seed, lut, vsi->rss_table_size); + kfree(lut); - if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) - return i40e_config_rss_aq(vsi, seed); - - return 0; + return ret; } /** * i40e_config_rss_reg - Prepare for RSS if used - * @pf: board private structure + * @vsi: Pointer to vsi structure * @seed: RSS hash seed + * @lut: Lookup table + * @lut_size: Lookup table size + * + * Returns 0 on success, negative on failure **/ -static int i40e_config_rss_reg(struct i40e_pf *pf, const u8 *seed) +static int i40e_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed, + const u8 *lut, u16 lut_size) { - struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; + struct i40e_pf *pf = vsi->back; struct i40e_hw *hw = &pf->hw; - u32 *seed_dw = (u32 *)seed; - u32 current_queue = 0; - u32 lut = 0; - int i, j; + u8 i; /* Fill out hash function seed */ - for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) - wr32(hw, I40E_PFQF_HKEY(i), seed_dw[i]); + if (seed) { + u32 *seed_dw = (u32 *)seed; - for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) { - lut = 0; - for (j = 0; j < 4; j++) { - if (current_queue == vsi->rss_size) - current_queue = 0; - lut |= ((current_queue) << (8 * j)); - current_queue++; - } - wr32(&pf->hw, I40E_PFQF_HLUT(i), lut); + for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) + wr32(hw, I40E_PFQF_HKEY(i), seed_dw[i]); + } + + if (lut) { + u32 *lut_dw = (u32 *)lut; + + if (lut_size != I40E_HLUT_ARRAY_SIZE) + return -EINVAL; + + for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) + wr32(hw, I40E_PFQF_HLUT(i), lut_dw[i]); } i40e_flush(hw); return 0; } +/** + * i40e_fill_rss_lut - Fill the RSS lookup table with default values + * @pf: Pointer to board private structure + * @lut: Lookup table + * @rss_table_size: Lookup table size + * @rss_size: Range of queue number for hashing + */ +static void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut, + u16 rss_table_size, u16 rss_size) +{ + u16 i; + + for (i = 0; i < rss_table_size; i++) + lut[i] = i % rss_size; +} + /** * i40e_config_rss - Prepare for RSS if used * @pf: board private structure @@ -7901,9 +7934,11 @@ static int i40e_config_rss(struct i40e_pf *pf) { struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; u8 seed[I40E_HKEY_ARRAY_SIZE]; + u8 *lut; struct i40e_hw *hw = &pf->hw; u32 reg_val; u64 hena; + int ret; netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); @@ -7924,10 +7959,20 @@ static int i40e_config_rss(struct i40e_pf *pf) (reg_val & ~I40E_PFQF_CTL_0_HASHLUTSIZE_512); wr32(hw, I40E_PFQF_CTL_0, reg_val); + lut = kzalloc(vsi->rss_table_size, GFP_KERNEL); + if (!lut) + return -ENOMEM; + + i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size); + if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) - return i40e_config_rss_aq(pf->vsi[pf->lan_vsi], seed); + ret = i40e_config_rss_aq(vsi, seed, lut, vsi->rss_table_size); else - return i40e_config_rss_reg(pf, seed); + ret = i40e_config_rss_reg(vsi, seed, lut, vsi->rss_table_size); + + kfree(lut); + + return ret; } /** -- cgit v1.2.3-59-g8ed1b From 043dd650efde3dfc65a6461e1cdc51cc87cb76f7 Mon Sep 17 00:00:00 2001 From: Helin Zhang Date: Wed, 21 Oct 2015 19:56:23 -0400 Subject: i40e: create a generic configure rss function This patch renames the old pf-specific function in order to clarify its scope. This patch also creates a more generic configure RSS function with the old name. This patch also creates a new more generic function to get RSS configuration, using the appropriate method. Change-ID: Ieddca2707b708ef19f1ebccdfd03a0a0cd63d3af Signed-off-by: Helin Zhang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 + drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 72 ++++++++++------------ drivers/net/ethernet/intel/i40e/i40e_main.c | 85 ++++++++++++++++++++++---- 3 files changed, 107 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index f6b747cf94d6..89f5323362ed 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -670,6 +670,8 @@ extern const char i40e_driver_name[]; extern const char i40e_driver_version_str[]; void i40e_do_reset_safe(struct i40e_pf *pf, u32 reset_flags); void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags); +int i40e_config_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size); +int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size); struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id); void i40e_update_stats(struct i40e_vsi *vsi); void i40e_update_eth_stats(struct i40e_vsi *vsi); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index f26c0d16e43e..6cb2b340cdc2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2611,10 +2611,9 @@ static int i40e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; - struct i40e_pf *pf = vsi->back; - struct i40e_hw *hw = &pf->hw; - u32 reg_val; - int i, j; + u8 *lut, *seed = NULL; + int ret; + u16 i; if (hfunc) *hfunc = ETH_RSS_HASH_TOP; @@ -2622,24 +2621,20 @@ static int i40e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, if (!indir) return 0; - for (i = 0, j = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) { - reg_val = rd32(hw, I40E_PFQF_HLUT(i)); - indir[j++] = reg_val & 0xff; - indir[j++] = (reg_val >> 8) & 0xff; - indir[j++] = (reg_val >> 16) & 0xff; - indir[j++] = (reg_val >> 24) & 0xff; - } + seed = key; + lut = kzalloc(I40E_HLUT_ARRAY_SIZE, GFP_KERNEL); + if (!lut) + return -ENOMEM; + ret = i40e_get_rss(vsi, seed, lut, I40E_HLUT_ARRAY_SIZE); + if (ret) + goto out; + for (i = 0; i < I40E_HLUT_ARRAY_SIZE; i++) + indir[i] = (u32)(lut[i]); - if (key) { - for (i = 0, j = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) { - reg_val = rd32(hw, I40E_PFQF_HKEY(i)); - key[j++] = (u8)(reg_val & 0xff); - key[j++] = (u8)((reg_val >> 8) & 0xff); - key[j++] = (u8)((reg_val >> 16) & 0xff); - key[j++] = (u8)((reg_val >> 24) & 0xff); - } - } - return 0; +out: + kfree(lut); + + return ret; } /** @@ -2656,10 +2651,10 @@ static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir, { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; - struct i40e_pf *pf = vsi->back; - struct i40e_hw *hw = &pf->hw; - u32 reg_val; - int i, j; + u8 seed_def[I40E_HKEY_ARRAY_SIZE]; + u8 *lut, *seed = NULL; + u16 i; + int ret; if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; @@ -2667,24 +2662,19 @@ static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir, if (!indir) return 0; - for (i = 0, j = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) { - reg_val = indir[j++]; - reg_val |= indir[j++] << 8; - reg_val |= indir[j++] << 16; - reg_val |= indir[j++] << 24; - wr32(hw, I40E_PFQF_HLUT(i), reg_val); - } - if (key) { - for (i = 0, j = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) { - reg_val = key[j++]; - reg_val |= key[j++] << 8; - reg_val |= key[j++] << 16; - reg_val |= key[j++] << 24; - wr32(hw, I40E_PFQF_HKEY(i), reg_val); - } + memcpy(seed_def, key, I40E_HKEY_ARRAY_SIZE); + seed = seed_def; } - return 0; + lut = kzalloc(I40E_HLUT_ARRAY_SIZE, GFP_KERNEL); + if (!lut) + return -ENOMEM; + for (i = 0; i < I40E_HLUT_ARRAY_SIZE; i++) + lut[i] = (u8)(indir[i]); + ret = i40e_config_rss(vsi, seed, lut, I40E_HLUT_ARRAY_SIZE); + kfree(lut); + + return ret; } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 9fe68024d971..84b196238b92 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7873,7 +7873,7 @@ static int i40e_vsi_config_rss(struct i40e_vsi *vsi) } /** - * i40e_config_rss_reg - Prepare for RSS if used + * i40e_config_rss_reg - Configure RSS keys and lut by writing registers * @vsi: Pointer to vsi structure * @seed: RSS hash seed * @lut: Lookup table @@ -7910,6 +7910,73 @@ static int i40e_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed, return 0; } +/** + * i40e_get_rss_reg - Get the RSS keys and lut by reading registers + * @vsi: Pointer to VSI structure + * @seed: Buffer to store the keys + * @lut: Buffer to store the lookup table entries + * @lut_size: Size of buffer to store the lookup table entries + * + * Returns 0 on success, negative on failure + */ +static int i40e_get_rss_reg(struct i40e_vsi *vsi, u8 *seed, + u8 *lut, u16 lut_size) +{ + struct i40e_pf *pf = vsi->back; + struct i40e_hw *hw = &pf->hw; + u16 i; + + if (seed) { + u32 *seed_dw = (u32 *)seed; + + for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) + seed_dw[i] = rd32(hw, I40E_PFQF_HKEY(i)); + } + if (lut) { + u32 *lut_dw = (u32 *)lut; + + if (lut_size != I40E_HLUT_ARRAY_SIZE) + return -EINVAL; + for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) + lut_dw[i] = rd32(hw, I40E_PFQF_HLUT(i)); + } + + return 0; +} + +/** + * i40e_config_rss - Configure RSS keys and lut + * @vsi: Pointer to VSI structure + * @seed: RSS hash seed + * @lut: Lookup table + * @lut_size: Lookup table size + * + * Returns 0 on success, negative on failure + */ +int i40e_config_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size) +{ + struct i40e_pf *pf = vsi->back; + + if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) + return i40e_config_rss_aq(vsi, seed, lut, lut_size); + else + return i40e_config_rss_reg(vsi, seed, lut, lut_size); +} + +/** + * i40e_get_rss - Get RSS keys and lut + * @vsi: Pointer to VSI structure + * @seed: Buffer to store the keys + * @lut: Buffer to store the lookup table entries + * lut_size: Size of buffer to store the lookup table entries + * + * Returns 0 on success, negative on failure + */ +int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size) +{ + return i40e_get_rss_reg(vsi, seed, lut, lut_size); +} + /** * i40e_fill_rss_lut - Fill the RSS lookup table with default values * @pf: Pointer to board private structure @@ -7927,10 +7994,10 @@ static void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut, } /** - * i40e_config_rss - Prepare for RSS if used + * i40e_pf_config_rss - Prepare for RSS if used * @pf: board private structure **/ -static int i40e_config_rss(struct i40e_pf *pf) +static int i40e_pf_config_rss(struct i40e_pf *pf) { struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; u8 seed[I40E_HKEY_ARRAY_SIZE]; @@ -7940,8 +8007,6 @@ static int i40e_config_rss(struct i40e_pf *pf) u64 hena; int ret; - netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); - /* By default we enable TCP/UDP with IPv4/IPv6 ptypes */ hena = (u64)rd32(hw, I40E_PFQF_HENA(0)) | ((u64)rd32(hw, I40E_PFQF_HENA(1)) << 32); @@ -7965,10 +8030,8 @@ static int i40e_config_rss(struct i40e_pf *pf) i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size); - if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) - ret = i40e_config_rss_aq(vsi, seed, lut, vsi->rss_table_size); - else - ret = i40e_config_rss_reg(vsi, seed, lut, vsi->rss_table_size); + netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); + ret = i40e_config_rss(vsi, seed, lut, vsi->rss_table_size); kfree(lut); @@ -8000,7 +8063,7 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) pf->rss_size = new_rss_size; i40e_reset_and_rebuild(pf, true); - i40e_config_rss(pf); + i40e_pf_config_rss(pf); } dev_info(&pf->pdev->dev, "RSS count: %d\n", pf->rss_size); return pf->rss_size; @@ -10009,7 +10072,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) * the hash */ if ((pf->flags & I40E_FLAG_RSS_ENABLED)) - i40e_config_rss(pf); + i40e_pf_config_rss(pf); /* fill in link information and enable LSE reporting */ i40e_update_link_info(&pf->hw); -- cgit v1.2.3-59-g8ed1b From 1c2df9e5a763d6d88f9aea8ca76d0ca75753fbd5 Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Wed, 21 Oct 2015 19:56:24 -0400 Subject: i40e: Bump version to 1.4.2 Bump. Change-ID: I2d1ce93b2ce74e4eef2394c932aef52cba99713f Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 84b196238b92..4b7d87447fe1 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -38,8 +38,8 @@ static const char i40e_driver_string[] = #define DRV_KERN "-k" #define DRV_VERSION_MAJOR 1 -#define DRV_VERSION_MINOR 3 -#define DRV_VERSION_BUILD 46 +#define DRV_VERSION_MINOR 4 +#define DRV_VERSION_BUILD 2 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN -- cgit v1.2.3-59-g8ed1b From 2aff030355b5bbbabe04e16c71343d851dd39235 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 29 Oct 2015 20:33:11 +0100 Subject: brcmfmac: Add support for the BCM4359 11ac RSDB PCIE device. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 9 +++++++++ drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | 2 ++ 3 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index f04833db2fd0..82e4382eb177 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -681,6 +681,7 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) case BRCM_CC_43569_CHIP_ID: case BRCM_CC_43570_CHIP_ID: case BRCM_CC_4358_CHIP_ID: + case BRCM_CC_4359_CHIP_ID: case BRCM_CC_43602_CHIP_ID: case BRCM_CC_4371_CHIP_ID: return 0x180000; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 83d804221715..7982d4d5bbb2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -55,6 +55,8 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt" #define BRCMF_PCIE_4358_FW_NAME "brcm/brcmfmac4358-pcie.bin" #define BRCMF_PCIE_4358_NVRAM_NAME "brcm/brcmfmac4358-pcie.txt" +#define BRCMF_PCIE_4359_FW_NAME "brcm/brcmfmac4359-pcie.bin" +#define BRCMF_PCIE_4359_NVRAM_NAME "brcm/brcmfmac4359-pcie.txt" #define BRCMF_PCIE_4365_FW_NAME "brcm/brcmfmac4365b-pcie.bin" #define BRCMF_PCIE_4365_NVRAM_NAME "brcm/brcmfmac4365b-pcie.txt" #define BRCMF_PCIE_4366_FW_NAME "brcm/brcmfmac4366b-pcie.bin" @@ -210,6 +212,8 @@ MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4359_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4359_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4365_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4365_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4366_FW_NAME); @@ -1517,6 +1521,10 @@ static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) fw_name = BRCMF_PCIE_4358_FW_NAME; nvram_name = BRCMF_PCIE_4358_NVRAM_NAME; break; + case BRCM_CC_4359_CHIP_ID: + fw_name = BRCMF_PCIE_4359_FW_NAME; + nvram_name = BRCMF_PCIE_4359_NVRAM_NAME; + break; case BRCM_CC_4365_CHIP_ID: fw_name = BRCMF_PCIE_4365_FW_NAME; nvram_name = BRCMF_PCIE_4365_NVRAM_NAME; @@ -2058,6 +2066,7 @@ static struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4359_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID), diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h index aa06ea231db3..4092d2789979 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h @@ -47,6 +47,7 @@ #define BRCM_CC_43569_CHIP_ID 43569 #define BRCM_CC_43570_CHIP_ID 43570 #define BRCM_CC_4358_CHIP_ID 0x4358 +#define BRCM_CC_4359_CHIP_ID 0x4359 #define BRCM_CC_43602_CHIP_ID 43602 #define BRCM_CC_4365_CHIP_ID 0x4365 #define BRCM_CC_4366_CHIP_ID 0x4366 @@ -66,6 +67,7 @@ #define BRCM_PCIE_43567_DEVICE_ID 0x43d3 #define BRCM_PCIE_43570_DEVICE_ID 0x43d9 #define BRCM_PCIE_4358_DEVICE_ID 0x43e9 +#define BRCM_PCIE_4359_DEVICE_ID 0x43ef #define BRCM_PCIE_43602_DEVICE_ID 0x43ba #define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb #define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc -- cgit v1.2.3-59-g8ed1b From c9c0043894cf2666ae832fbad7afa68fa53178ac Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 29 Oct 2015 20:33:12 +0100 Subject: brcmfmac: Simplify and fix usage of brcmf_ifname. brcmf_ifname is a debug function to return a name related to an ifp, but is using a rather complex implementation. It was also used wrongly from bcdc as it did not use the bsscfgidx as it was supposed to, but bssidx. This patch fixes that bug and simplifies brcmf_ifname. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 10 ++++++---- .../net/wireless/broadcom/brcm80211/brcmfmac/core.c | 19 ++++++------------- .../net/wireless/broadcom/brcm80211/brcmfmac/core.h | 2 +- 3 files changed, 13 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index 288c84e7c56b..6af658e443e4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -187,7 +187,8 @@ retry: goto retry; if (id != bcdc->reqid) { brcmf_err("%s: unexpected request id %d (expected %d)\n", - brcmf_ifname(drvr, ifidx), id, bcdc->reqid); + brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id, + bcdc->reqid); ret = -EINVAL; goto done; } @@ -234,7 +235,8 @@ brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, if (id != bcdc->reqid) { brcmf_err("%s: unexpected request id %d (expected %d)\n", - brcmf_ifname(drvr, ifidx), id, bcdc->reqid); + brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id, + bcdc->reqid); ret = -EINVAL; goto done; } @@ -298,13 +300,13 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) != BCDC_PROTO_VER) { brcmf_err("%s: non-BCDC packet received, flags 0x%x\n", - brcmf_ifname(drvr, tmp_if->ifidx), h->flags); + brcmf_ifname(tmp_if), h->flags); return -EBADE; } if (h->flags & BCDC_FLAG_SUM_GOOD) { brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n", - brcmf_ifname(drvr, tmp_if->ifidx), h->flags); + brcmf_ifname(tmp_if), h->flags); pktbuf->ip_summed = CHECKSUM_UNNECESSARY; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index b5ab98ee1445..76b66bc15b59 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -66,20 +66,13 @@ static int brcmf_p2p_enable; module_param_named(p2pon, brcmf_p2p_enable, int, 0); MODULE_PARM_DESC(p2pon, "enable legacy p2p management functionality"); -char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) +char *brcmf_ifname(struct brcmf_if *ifp) { - if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { - brcmf_err("ifidx %d out of range\n", ifidx); - return ""; - } - - if (drvr->iflist[ifidx] == NULL) { - brcmf_err("null i/f %d\n", ifidx); + if (!ifp) return ""; - } - if (drvr->iflist[ifidx]->ndev) - return drvr->iflist[ifidx]->ndev->name; + if (ifp->ndev) + return ifp->ndev->name; return ""; } @@ -237,14 +230,14 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, struct sk_buff *skb2; brcmf_dbg(INFO, "%s: insufficient headroom\n", - brcmf_ifname(drvr, ifp->bssidx)); + brcmf_ifname(ifp)); drvr->bus_if->tx_realloc++; skb2 = skb_realloc_headroom(skb, drvr->hdrlen); dev_kfree_skb(skb); skb = skb2; if (skb == NULL) { brcmf_err("%s: skb_realloc_headroom failed\n", - brcmf_ifname(drvr, ifp->bssidx)); + brcmf_ifname(ifp)); ret = -ENOMEM; goto done; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 2f9101b2ad34..673d6972359f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -205,7 +205,7 @@ struct brcmf_skb_reorder_data { int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); /* Return pointer to interface name */ -char *brcmf_ifname(struct brcmf_pub *drvr, int idx); +char *brcmf_ifname(struct brcmf_if *ifp); struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx); int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, -- cgit v1.2.3-59-g8ed1b From 31fa86cb342bd0552c0c18dcda261b42afaa82ce Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 29 Oct 2015 20:33:13 +0100 Subject: brcmfmac: Remove unnecessary check from start_xmit. The brcmf_netdev_start_xmit checks if the ndev is still valid by checking if it still exists in database. This check is not needed and therefor removed. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 76b66bc15b59..b743a83fd234 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -217,14 +217,6 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, goto done; } - if (!drvr->iflist[ifp->bssidx]) { - brcmf_err("bad ifidx %d\n", ifp->bssidx); - netif_stop_queue(ndev); - dev_kfree_skb(skb); - ret = -ENODEV; - goto done; - } - /* Make sure there's enough room for any header */ if (skb_headroom(skb) < drvr->hdrlen) { struct sk_buff *skb2; -- cgit v1.2.3-59-g8ed1b From da402c56bc91fa69c595fcfebe5e5affa2933697 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 29 Oct 2015 20:33:14 +0100 Subject: brcmfmac: Remove unncessary variable irq_requested. The variable irq_requested is unneeded as the functionality it is providing, is also provided by the variable irq_allocated. Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 7982d4d5bbb2..0edaaf58bfbb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -257,7 +257,6 @@ struct brcmf_pcie_core_info { struct brcmf_pciedev_info { enum brcmf_pcie_state state; bool in_irq; - bool irq_requested; struct pci_dev *pdev; char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; @@ -889,7 +888,6 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo) brcmf_dbg(PCIE, "Enter\n"); /* is it a v1 or v2 implementation */ - devinfo->irq_requested = false; pci_enable_msi(pdev); if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { if (request_threaded_irq(pdev->irq, @@ -912,7 +910,6 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo) return -EIO; } } - devinfo->irq_requested = true; devinfo->irq_allocated = true; return 0; } @@ -930,9 +927,6 @@ static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo) pdev = devinfo->pdev; brcmf_pcie_intr_disable(devinfo); - if (!devinfo->irq_requested) - return; - devinfo->irq_requested = false; free_irq(pdev->irq, devinfo); pci_disable_msi(pdev); -- cgit v1.2.3-59-g8ed1b From dc1a272ec4405d70676b40cfde8f2ebdc77ee018 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 29 Oct 2015 20:33:15 +0100 Subject: brcmfmac: Disable runtime pm for USB. Currently runtime pm is enabled for USB, but it is not properly supported by driver. This patch disables the runtime PM support completely for USB, as it currently can result in problems on some systems. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 689e64d004bc..fe8b2bca4687 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1504,7 +1504,6 @@ static struct usb_driver brcmf_usbdrvr = { .suspend = brcmf_usb_suspend, .resume = brcmf_usb_resume, .reset_resume = brcmf_usb_reset_resume, - .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, }; -- cgit v1.2.3-59-g8ed1b From 8abffd8173a1c64eaa951f2527635c12b5a4ecc3 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 29 Oct 2015 20:33:16 +0100 Subject: brcmfmac: Add RSDB support. Broadcom devices with a single 802.11 core can work on two band concurrently using VSDB feature, ie. Virtual Simultaneous Dual-Band. For devices that are fitted with two 802.11 cores and RF paths the driver should support a firmware feature called RSDB, which stands for Real Simultaneous Dual-Band. RSDB works almost autonomously in firmware except for AP config. When the device supports RSDB then the interface should not be brought down when configuring it, otherwise the link (if configured) on the other interface will be lost. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel [kvalo@codeaurora.org: changed the commit log based on discussion] Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 4 +++- drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index deb5f78dcacc..8a3c9fa171ee 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -4183,7 +4183,9 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, } } - if (dev_role == NL80211_IFTYPE_AP) { + if ((dev_role == NL80211_IFTYPE_AP) && + ((ifp->ifidx == 0) || + !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) { err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); if (err < 0) { brcmf_err("BRCMF_C_DOWN error %d\n", err); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 44bb30636690..ba5249403036 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -137,6 +137,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode"); if (brcmf_feature_disable) { brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index 6b381f799f22..538175882c8a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -24,13 +24,16 @@ * PNO: preferred network offload. * WOWL: Wake-On-WLAN. * P2P: peer-to-peer + * RSDB: Real Simultaneous Dual Band */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ BRCMF_FEAT_DEF(MCHAN) \ BRCMF_FEAT_DEF(PNO) \ BRCMF_FEAT_DEF(WOWL) \ - BRCMF_FEAT_DEF(P2P) + BRCMF_FEAT_DEF(P2P) \ + BRCMF_FEAT_DEF(RSDB) + /* * Quirks: * -- cgit v1.2.3-59-g8ed1b From 37a869ec859308ea50ac6f8f320c86ad8d2f8767 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 29 Oct 2015 20:33:17 +0100 Subject: brcmfmac: Use consistent naming for bsscfgidx. The variable bsscfgidx is used in different places with different names, e.g. bsscfg, bssidx, bsscfg_idx. This patch cleans this up by using bsscfgidx everywhere. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 16 +++-- .../wireless/broadcom/brcm80211/brcmfmac/core.c | 78 +++++++++++----------- .../wireless/broadcom/brcm80211/brcmfmac/core.h | 6 +- .../wireless/broadcom/brcm80211/brcmfmac/debug.c | 2 +- .../wireless/broadcom/brcm80211/brcmfmac/fweh.c | 8 +-- .../wireless/broadcom/brcm80211/brcmfmac/fweh.h | 2 +- .../wireless/broadcom/brcm80211/brcmfmac/fwil.c | 28 ++++---- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 2 +- .../broadcom/brcm80211/brcmfmac/fwsignal.c | 4 +- .../net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 6 +- 10 files changed, 78 insertions(+), 74 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 8a3c9fa171ee..0c8cc7f3ad0b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -776,7 +776,8 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, s32 ap = 0; s32 err = 0; - brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type); + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx, + type); /* WAR: There are a number of p2p interface related problems which * need to be handled initially (before doing the validate). @@ -2921,7 +2922,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, status = e->status; if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx); + brcmf_err("scan not ready, bsscfgidx=%d\n", ifp->bsscfgidx); return -EPERM; } @@ -3877,7 +3878,8 @@ s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, ifp = vif->ifp; saved_ie = &vif->saved_ie; - brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag); + brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx, + pktflag); iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); if (!iovar_ie_buf) return -ENOMEM; @@ -4241,7 +4243,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("setting ssid failed %d\n", err); goto exit; } - bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); + bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx); bss_enable.enable = cpu_to_le32(1); err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, sizeof(bss_enable)); @@ -4308,7 +4310,7 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) if (err < 0) brcmf_err("BRCMF_C_UP error %d\n", err); } else { - bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); + bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx); bss_enable.enable = cpu_to_le32(0); err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, sizeof(bss_enable)); @@ -5094,9 +5096,9 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; struct brcmf_cfg80211_vif *vif; - brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n", + brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n", ifevent->action, ifevent->flags, ifevent->ifidx, - ifevent->bssidx); + ifevent->bsscfgidx); mutex_lock(&event->vif_event_lock); event->action = ifevent->action; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index b743a83fd234..8d16f50cea39 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -80,7 +80,7 @@ char *brcmf_ifname(struct brcmf_if *ifp) struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx) { struct brcmf_if *ifp; - s32 bssidx; + s32 bsscfgidx; if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { brcmf_err("ifidx %d out of range\n", ifidx); @@ -88,9 +88,9 @@ struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx) } ifp = NULL; - bssidx = drvr->if2bss[ifidx]; - if (bssidx >= 0) - ifp = drvr->iflist[bssidx]; + bsscfgidx = drvr->if2bss[ifidx]; + if (bsscfgidx >= 0) + ifp = drvr->iflist[bsscfgidx]; return ifp; } @@ -108,7 +108,7 @@ static void _brcmf_set_multicast_list(struct work_struct *work) ifp = container_of(work, struct brcmf_if, multicast_work); - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); ndev = ifp->ndev; @@ -168,7 +168,7 @@ _brcmf_set_mac_address(struct work_struct *work) ifp = container_of(work, struct brcmf_if, setmacaddr_work); - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr, ETH_ALEN); @@ -206,7 +206,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, struct brcmf_pub *drvr = ifp->drvr; struct ethhdr *eh = (struct ethhdr *)(skb->data); - brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); + brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); /* Can the device send data? */ if (drvr->bus_if->state != BRCMF_BUS_UP) { @@ -267,8 +267,8 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp, if (!ifp || !ifp->ndev) return; - brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n", - ifp->bssidx, ifp->netif_stop, reason, state); + brcmf_dbg(TRACE, "enter: bsscfgidx=%d stop=0x%X reason=%d state=%d\n", + ifp->bsscfgidx, ifp->netif_stop, reason, state); spin_lock_irqsave(&ifp->netif_stop_lock, flags); if (state) { @@ -587,7 +587,7 @@ static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); return &ifp->stats; } @@ -616,7 +616,7 @@ static int brcmf_netdev_stop(struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); brcmf_cfg80211_down(ndev); @@ -632,7 +632,7 @@ static int brcmf_netdev_open(struct net_device *ndev) struct brcmf_bus *bus_if = drvr->bus_if; u32 toe_ol; - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); /* If bus is not ready, can't continue */ if (bus_if->state != BRCMF_BUS_UP) { @@ -674,7 +674,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) struct net_device *ndev; s32 err; - brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx, + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx, ifp->mac_addr); ndev = ifp->ndev; @@ -706,7 +706,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) return 0; fail: - drvr->iflist[ifp->bssidx] = NULL; + drvr->iflist[ifp->bsscfgidx] = NULL; ndev->netdev_ops = NULL; free_netdev(ndev); return -EBADE; @@ -724,7 +724,8 @@ void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on) { struct net_device *ndev; - brcmf_dbg(TRACE, "Enter, idx=%d carrier=%d\n", ifp->bssidx, on); + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d carrier=%d\n", ifp->bsscfgidx, + on); ndev = ifp->ndev; brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_DISCONNECTED, !on); @@ -771,7 +772,7 @@ static int brcmf_net_p2p_attach(struct brcmf_if *ifp) { struct net_device *ndev; - brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx, + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx, ifp->mac_addr); ndev = ifp->ndev; @@ -790,21 +791,21 @@ static int brcmf_net_p2p_attach(struct brcmf_if *ifp) return 0; fail: - ifp->drvr->iflist[ifp->bssidx] = NULL; + ifp->drvr->iflist[ifp->bsscfgidx] = NULL; ndev->netdev_ops = NULL; free_netdev(ndev); return -EBADE; } -struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, +struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx, bool is_p2pdev, char *name, u8 *mac_addr) { struct brcmf_if *ifp; struct net_device *ndev; - brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifidx); + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx, ifidx); - ifp = drvr->iflist[bssidx]; + ifp = drvr->iflist[bsscfgidx]; /* * Delete the existing interface before overwriting it * in case we missed the BRCMF_E_IF_DEL event. @@ -815,7 +816,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, if (ifidx) { netif_stop_queue(ifp->ndev); brcmf_net_detach(ifp->ndev); - drvr->iflist[bssidx] = NULL; + drvr->iflist[bsscfgidx] = NULL; } else { brcmf_err("ignore IF event\n"); return ERR_PTR(-EINVAL); @@ -839,15 +840,15 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, ndev->destructor = brcmf_cfg80211_free_netdev; ifp = netdev_priv(ndev); ifp->ndev = ndev; - /* store mapping ifidx to bssidx */ + /* store mapping ifidx to bsscfgidx */ if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID) - drvr->if2bss[ifidx] = bssidx; + drvr->if2bss[ifidx] = bsscfgidx; } ifp->drvr = drvr; - drvr->iflist[bssidx] = ifp; + drvr->iflist[bsscfgidx] = ifp; ifp->ifidx = ifidx; - ifp->bssidx = bssidx; + ifp->bsscfgidx = bsscfgidx; init_waitqueue_head(&ifp->pend_8021x_wait); spin_lock_init(&ifp->netif_stop_lock); @@ -861,21 +862,22 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, return ifp; } -static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) +static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx) { struct brcmf_if *ifp; - ifp = drvr->iflist[bssidx]; - drvr->iflist[bssidx] = NULL; + ifp = drvr->iflist[bsscfgidx]; + drvr->iflist[bsscfgidx] = NULL; if (!ifp) { - brcmf_err("Null interface, idx=%d\n", bssidx); + brcmf_err("Null interface, bsscfgidx=%d\n", bsscfgidx); return; } - brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx); - if (drvr->if2bss[ifp->ifidx] == bssidx) + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx, + ifp->ifidx); + if (drvr->if2bss[ifp->ifidx] == bsscfgidx) drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID; if (ifp->ndev) { - if (bssidx == 0) { + if (bsscfgidx == 0) { if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { rtnl_lock(); brcmf_netdev_stop(ifp->ndev); @@ -905,12 +907,12 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) void brcmf_remove_interface(struct brcmf_if *ifp) { - if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp)) + if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bsscfgidx] != ifp)) return; - brcmf_dbg(TRACE, "Enter, bssidx=%d, ifidx=%d\n", ifp->bssidx, + brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", ifp->bsscfgidx, ifp->ifidx); brcmf_fws_del_interface(ifp); - brcmf_del_if(ifp->drvr, ifp->bssidx); + brcmf_del_if(ifp->drvr, ifp->bsscfgidx); } int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr) @@ -925,10 +927,10 @@ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr) highest = 2; for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) { if (drvr->iflist[ifidx]) { - if (drvr->iflist[ifidx]->bssidx == bsscfgidx) + if (drvr->iflist[ifidx]->bsscfgidx == bsscfgidx) bsscfgidx = highest + 1; - else if (drvr->iflist[ifidx]->bssidx > highest) - highest = drvr->iflist[ifidx]->bssidx; + else if (drvr->iflist[ifidx]->bsscfgidx > highest) + highest = drvr->iflist[ifidx]->bsscfgidx; } else { available = true; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 673d6972359f..77d8239d9536 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -174,7 +174,7 @@ enum brcmf_netif_stop_reason { * @multicast_work: worker object for multicast provisioning. * @fws_desc: interface specific firmware-signalling descriptor. * @ifidx: interface index in device firmware. - * @bssidx: index of bss associated with this interface. + * @bsscfgidx: index of bss associated with this interface. * @mac_addr: assigned mac address. * @netif_stop: bitmap indicates reason why netif queues are stopped. * @netif_stop_lock: spinlock for update netif_stop from multiple sources. @@ -190,7 +190,7 @@ struct brcmf_if { struct work_struct multicast_work; struct brcmf_fws_mac_descriptor *fws_desc; int ifidx; - s32 bssidx; + s32 bsscfgidx; u8 mac_addr[ETH_ALEN]; u8 netif_stop; spinlock_t netif_stop_lock; @@ -208,7 +208,7 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); char *brcmf_ifname(struct brcmf_if *ifp); struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx); int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); -struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, +struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx, bool is_p2pdev, char *name, u8 *mac_addr); void brcmf_remove_interface(struct brcmf_if *ifp); int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c index 1299dccc78b4..e64557c35553 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c @@ -49,7 +49,7 @@ static int brcmf_debug_psm_watchdog_notify(struct brcmf_if *ifp, const struct brcmf_event_msg *evtmsg, void *data) { - brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); + brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx); return brcmf_debug_create_memdump(ifp->drvr->bus_if, data, evtmsg->datalen); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c index 3878b6f6cfce..7b26fb1b437c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c @@ -182,8 +182,8 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, bool is_p2pdev; int err = 0; - brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n", - ifevent->action, ifevent->ifidx, ifevent->bssidx, + brcmf_dbg(EVENT, "action: %u ifidx: %u bsscfgidx: %u flags: %u role: %u\n", + ifevent->action, ifevent->ifidx, ifevent->bsscfgidx, ifevent->flags, ifevent->role); /* The P2P Device interface event must not be ignored contrary to what @@ -204,12 +204,12 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, return; } - ifp = drvr->iflist[ifevent->bssidx]; + ifp = drvr->iflist[ifevent->bsscfgidx]; if (ifevent->action == BRCMF_E_IF_ADD) { brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname, emsg->addr); - ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx, + ifp = brcmf_add_if(drvr, ifevent->bsscfgidx, ifevent->ifidx, is_p2pdev, emsg->ifname, emsg->addr); if (IS_ERR(ifp)) return; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h index d9a942842382..5e39e2a9e388 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h @@ -219,7 +219,7 @@ struct brcmf_if_event { u8 ifidx; u8 action; u8 flags; - u8 bssidx; + u8 bsscfgidx; u8 role; }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c index dcfa0bb149ce..bbf7abbf0901 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c @@ -293,22 +293,22 @@ brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data) } static u32 -brcmf_create_bsscfg(s32 bssidx, char *name, char *data, u32 datalen, char *buf, - u32 buflen) +brcmf_create_bsscfg(s32 bsscfgidx, char *name, char *data, u32 datalen, + char *buf, u32 buflen) { const s8 *prefix = "bsscfg:"; s8 *p; u32 prefixlen; u32 namelen; u32 iolen; - __le32 bssidx_le; + __le32 bsscfgidx_le; - if (bssidx == 0) + if (bsscfgidx == 0) return brcmf_create_iovar(name, data, datalen, buf, buflen); prefixlen = strlen(prefix); namelen = strlen(name) + 1; /* lengh of iovar name + null */ - iolen = prefixlen + namelen + sizeof(bssidx_le) + datalen; + iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen; if (buflen < iolen) { brcmf_err("buffer is too short\n"); @@ -326,9 +326,9 @@ brcmf_create_bsscfg(s32 bssidx, char *name, char *data, u32 datalen, char *buf, p += namelen; /* bss config index as first data */ - bssidx_le = cpu_to_le32(bssidx); - memcpy(p, &bssidx_le, sizeof(bssidx_le)); - p += sizeof(bssidx_le); + bsscfgidx_le = cpu_to_le32(bsscfgidx); + memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le)); + p += sizeof(bsscfgidx_le); /* parameter buffer follows */ if (datalen) @@ -347,12 +347,12 @@ brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, mutex_lock(&drvr->proto_block); - brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, - ifp->bssidx, name, len); + brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx, + ifp->bsscfgidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); - buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, + buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); if (buflen) { err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, @@ -376,7 +376,7 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, mutex_lock(&drvr->proto_block); - buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, + buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); if (buflen) { err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, @@ -387,8 +387,8 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, err = -EPERM; brcmf_err("Creating bsscfg failed\n"); } - brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, - ifp->bssidx, name, len); + brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx, + ifp->bsscfgidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index daa427b46712..50ff69dfa4b6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -170,7 +170,7 @@ struct brcmf_fil_af_params_le { }; struct brcmf_fil_bss_enable_le { - __le32 bsscfg_idx; + __le32 bsscfgidx; __le32 enable; }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index 086cac3f86d6..ffdc7f82df4b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -719,7 +719,7 @@ static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc, desc->state = BRCMF_FWS_STATE_OPEN; desc->requested_credit = 0; desc->requested_packet = 0; - /* depending on use may need ifp->bssidx instead */ + /* depending on use may need ifp->bsscfgidx instead */ desc->interface_id = ifidx; desc->ac_bitmap = 0xff; /* update this when handling APSD */ if (addr) @@ -1938,7 +1938,7 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp) { struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; - brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); + brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx); if (!entry) return; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index d224b3dd72ed..98cf1d0f447a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -2067,7 +2067,7 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, struct brcmf_if *p2p_ifp; struct brcmf_if *pri_ifp; int err; - u32 bssidx; + u32 bsscfgidx; if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif) return ERR_PTR(-ENOSPC); @@ -2113,13 +2113,13 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, memcpy(&p2p_vif->wdev.address, p2p->dev_addr, sizeof(p2p->dev_addr)); /* verify bsscfg index for P2P discovery */ - err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx); + err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bsscfgidx); if (err < 0) { brcmf_err("retrieving discover bsscfg index failed\n"); goto fail; } - WARN_ON(p2p_ifp->bssidx != bssidx); + WARN_ON(p2p_ifp->bsscfgidx != bsscfgidx); init_completion(&p2p->send_af_done); INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler); -- cgit v1.2.3-59-g8ed1b From c2a43a6ba5e56b4bbca46287628ef1c84c44fa8d Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 29 Oct 2015 20:33:18 +0100 Subject: brcmfmac: Use new methods for pcie Power Management. Currently the legacy methods suspend and resume are used for pcie devices. This is not the preferable method and is also causing issues with some setups when doing hibernate. Changing this to use the new PM methods. 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: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 89 +++++++++------------- 1 file changed, 37 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 0edaaf58bfbb..e08bc6441e62 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -1388,10 +1388,6 @@ static void brcmf_pcie_wowl_config(struct device *dev, bool enabled) brcmf_dbg(PCIE, "Configuring WOWL, enabled=%d\n", enabled); devinfo->wowl_enabled = enabled; - if (enabled) - device_set_wakeup_enable(&devinfo->pdev->dev, true); - else - device_set_wakeup_enable(&devinfo->pdev->dev, false); } @@ -1961,15 +1957,14 @@ brcmf_pcie_remove(struct pci_dev *pdev) #ifdef CONFIG_PM -static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state) +static int brcmf_pcie_pm_enter_D3(struct device *dev) { struct brcmf_pciedev_info *devinfo; struct brcmf_bus *bus; - int err; - brcmf_dbg(PCIE, "Enter, state=%d, pdev=%p\n", state.event, pdev); + brcmf_err("Enter\n"); - bus = dev_get_drvdata(&pdev->dev); + bus = dev_get_drvdata(dev); devinfo = bus->bus_priv.pcie->devinfo; brcmf_bus_change_state(bus, BRCMF_BUS_DOWN); @@ -1984,62 +1979,45 @@ static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state) brcmf_err("Timeout on response for entering D3 substate\n"); return -EIO; } - brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM_IN_USE); - err = pci_save_state(pdev); - if (err) - brcmf_err("pci_save_state failed, err=%d\n", err); - if ((err) || (!devinfo->wowl_enabled)) { - brcmf_chip_detach(devinfo->ci); - devinfo->ci = NULL; - brcmf_pcie_remove(pdev); - return 0; - } + devinfo->state = BRCMFMAC_PCIE_STATE_DOWN; - return pci_prepare_to_sleep(pdev); + return 0; } -static int brcmf_pcie_resume(struct pci_dev *pdev) + +static int brcmf_pcie_pm_leave_D3(struct device *dev) { struct brcmf_pciedev_info *devinfo; struct brcmf_bus *bus; + struct pci_dev *pdev; int err; - bus = dev_get_drvdata(&pdev->dev); - brcmf_dbg(PCIE, "Enter, pdev=%p, bus=%p\n", pdev, bus); + brcmf_err("Enter\n"); - err = pci_set_power_state(pdev, PCI_D0); - if (err) { - brcmf_err("pci_set_power_state failed, err=%d\n", err); - goto cleanup; - } - pci_restore_state(pdev); - pci_enable_wake(pdev, PCI_D3hot, false); - pci_enable_wake(pdev, PCI_D3cold, false); + bus = dev_get_drvdata(dev); + devinfo = bus->bus_priv.pcie->devinfo; + brcmf_dbg(PCIE, "Enter, dev=%p, bus=%p\n", dev, bus); /* Check if device is still up and running, if so we are ready */ - if (bus) { - devinfo = bus->bus_priv.pcie->devinfo; - if (brcmf_pcie_read_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_INTMASK) != 0) { - if (brcmf_pcie_send_mb_data(devinfo, - BRCMF_H2D_HOST_D0_INFORM)) - goto cleanup; - brcmf_dbg(PCIE, "Hot resume, continue....\n"); - brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - brcmf_bus_change_state(bus, BRCMF_BUS_UP); - brcmf_pcie_intr_enable(devinfo); - return 0; - } + if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) { + brcmf_dbg(PCIE, "Try to wakeup device....\n"); + if (brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM)) + goto cleanup; + brcmf_dbg(PCIE, "Hot resume, continue....\n"); + devinfo->state = BRCMFMAC_PCIE_STATE_UP; + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_bus_change_state(bus, BRCMF_BUS_UP); + brcmf_pcie_intr_enable(devinfo); + return 0; } cleanup: - if (bus) { - devinfo = bus->bus_priv.pcie->devinfo; - brcmf_chip_detach(devinfo->ci); - devinfo->ci = NULL; - brcmf_pcie_remove(pdev); - } + brcmf_chip_detach(devinfo->ci); + devinfo->ci = NULL; + pdev = devinfo->pdev; + brcmf_pcie_remove(pdev); + err = brcmf_pcie_probe(pdev, NULL); if (err) brcmf_err("probe after resume failed, err=%d\n", err); @@ -2048,6 +2026,14 @@ cleanup: } +static const struct dev_pm_ops brcmf_pciedrvr_pm = { + .suspend = brcmf_pcie_pm_enter_D3, + .resume = brcmf_pcie_pm_leave_D3, + .freeze = brcmf_pcie_pm_enter_D3, + .restore = brcmf_pcie_pm_leave_D3, +}; + + #endif /* CONFIG_PM */ @@ -2086,9 +2072,8 @@ static struct pci_driver brcmf_pciedrvr = { .probe = brcmf_pcie_probe, .remove = brcmf_pcie_remove, #ifdef CONFIG_PM - .suspend = brcmf_pcie_suspend, - .resume = brcmf_pcie_resume -#endif /* CONFIG_PM */ + .driver.pm = &brcmf_pciedrvr_pm, +#endif }; -- cgit v1.2.3-59-g8ed1b From aeb64225aa8e74804f81aa3e31b7a3860fd2b730 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 29 Oct 2015 20:33:19 +0100 Subject: brcmfmac: Add wowl wake indication report. On wakeup of the system (resume) a wowl wakeup indication report can be sent to cfg80211. This patch adds support for this. The report specifies if the device was responsible for the wakeup and if so, will specify the exact reason. 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: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 67 +++++++++++++++++++++- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 12 ++++ 2 files changed, 77 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 0c8cc7f3ad0b..998c52194b6c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3062,6 +3062,67 @@ static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], return ret; } +#ifdef CONFIG_PM + +static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) +{ + struct brcmf_wowl_wakeind_le wake_ind_le; + struct cfg80211_wowlan_wakeup wakeup_data; + struct cfg80211_wowlan_wakeup *wakeup; + u32 wakeind; + s32 err; + + err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le, + sizeof(wake_ind_le)); + if (!err) { + brcmf_err("Get wowl_wakeind failed, err = %d\n", err); + return; + } + + wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind); + if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | + BRCMF_WOWL_RETR | BRCMF_WOWL_NET)) { + wakeup = &wakeup_data; + memset(&wakeup_data, 0, sizeof(wakeup_data)); + wakeup_data.pattern_idx = -1; + + if (wakeind & BRCMF_WOWL_MAGIC) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n"); + wakeup_data.magic_pkt = true; + } + if (wakeind & BRCMF_WOWL_DIS) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n"); + wakeup_data.disconnect = true; + } + if (wakeind & BRCMF_WOWL_BCN) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n"); + wakeup_data.disconnect = true; + } + if (wakeind & BRCMF_WOWL_RETR) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n"); + wakeup_data.disconnect = true; + } + if (wakeind & BRCMF_WOWL_NET) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n"); + /* For now always map to pattern 0, no API to get + * correct information available at the moment. + */ + wakeup_data.pattern_idx = 0; + } + } else { + wakeup = NULL; + } + cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL); +} + +#else + +static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) +{ +} + +#endif /* CONFIG_PM */ + static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); @@ -3071,11 +3132,12 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) brcmf_dbg(TRACE, "Enter\n"); if (cfg->wowl_enabled) { + brcmf_report_wowl_wakeind(wiphy, ifp); + brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); + brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); brcmf_configure_arp_offload(ifp, true); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, cfg->pre_wowl_pmmode); - brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); - brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); cfg->wowl_enabled = false; } return 0; @@ -3109,6 +3171,7 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, wowl->patterns[i].pkt_offset); } } + brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear", strlen("clear")); brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); brcmf_bus_wowl_config(cfg->pub->bus_if, true); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 50ff69dfa4b6..92ee1ad6dd75 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -634,4 +634,16 @@ struct brcmf_assoclist_le { u8 mac[BRCMF_MAX_ASSOCLIST][ETH_ALEN]; }; +/** + * struct brcmf_wowl_wakeind_le - Wakeup indicators + * Note: note both fields contain same information. + * + * @pci_wakeind: Whether PCI PMECSR PMEStatus bit was set. + * @ucode_wakeind: What wakeup-event indication was set by ucode + */ +struct brcmf_wowl_wakeind_le { + __le32 pci_wakeind; + __le32 ucode_wakeind; +}; + #endif /* FWIL_TYPES_H_ */ -- cgit v1.2.3-59-g8ed1b From 21ba005464a95e2ac57e13d035d11d29f483738a Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 6 Nov 2015 08:48:23 +0100 Subject: brcm80211: Delete an unnecessary check before the function call "release_firmware" The release_firmware() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index 4248f3c80e78..33afb9aafa9b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -449,8 +449,7 @@ static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) if (raw_nvram) bcm47xx_nvram_release_contents(data); - if (fw) - release_firmware(fw); + release_firmware(fw); if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) goto fail; -- cgit v1.2.3-59-g8ed1b From 3a318426e09a9c9266fe6440842e11238f640a20 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 6 Nov 2015 13:01:20 +0300 Subject: ath9k_htc: check for underflow in ath9k_htc_rx_msg() We check for overflow here, but we don't check for underflow so it causes a static checker warning. Fixes: fb9987d0f748 ('ath9k_htc: Support for AR9271 chipset.') Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/htc_hst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index 2294709ee8b0..fd85f996c554 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -414,7 +414,7 @@ void ath9k_htc_rx_msg(struct htc_target *htc_handle, return; } - if (epid >= ENDPOINT_MAX) { + if (epid < 0 || epid >= ENDPOINT_MAX) { if (pipe_id != USB_REG_IN_PIPE) dev_kfree_skb_any(skb); else -- cgit v1.2.3-59-g8ed1b From 6866a64a0f9bba5145891ec0fe5cd52c73efd4e9 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 14 Nov 2015 17:22:07 +0100 Subject: brcmfmac: constify brcmf_bus_ops structures The brcmf_bus_ops structures are never modified, so declare them as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 230cad788ace..36093f93bfbe 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -137,7 +137,7 @@ struct brcmf_bus { bool always_use_fws_queue; bool wowl_supported; - struct brcmf_bus_ops *ops; + const struct brcmf_bus_ops *ops; struct brcmf_bus_msgbuf *msgbuf; }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index e08bc6441e62..bbf414494ca9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -1413,7 +1413,7 @@ static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len) } -static struct brcmf_bus_ops brcmf_pcie_bus_ops = { +static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { .txdata = brcmf_pcie_tx, .stop = brcmf_pcie_down, .txctl = brcmf_pcie_tx_ctlpkt, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 7e74ac3ad815..01f359efe27e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -4025,7 +4025,7 @@ brcmf_sdio_watchdog(unsigned long data) } } -static struct brcmf_bus_ops brcmf_sdio_bus_ops = { +static const struct brcmf_bus_ops brcmf_sdio_bus_ops = { .stop = brcmf_sdio_bus_stop, .preinit = brcmf_sdio_bus_preinit, .txdata = brcmf_sdio_bus_txdata, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index fe8b2bca4687..978d14c09532 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1163,7 +1163,7 @@ static void brcmf_usb_wowl_config(struct device *dev, bool enabled) device_set_wakeup_enable(devinfo->dev, false); } -static struct brcmf_bus_ops brcmf_usb_bus_ops = { +static const struct brcmf_bus_ops brcmf_usb_bus_ops = { .txdata = brcmf_usb_tx, .stop = brcmf_usb_down, .txctl = brcmf_usb_tx_ctlpkt, -- cgit v1.2.3-59-g8ed1b From 073d72f963c987bc79fb1efd61ce53b919e2de27 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 16 Nov 2015 13:12:25 +0100 Subject: rtlwifi: Delete unnecessary checks before the function call "kfree_skb" The kfree_skb() function tests whether its argument is NULL and then returns immediately. Thus the test around the calls is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/core.c | 3 +-- drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index c925a4dff599..4ae421ef30d9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -1833,8 +1833,7 @@ bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb) spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); pskb = __skb_dequeue(&ring->queue); - if (pskb) - kfree_skb(pskb); + kfree_skb(pskb); /*this is wrong, fill_tx_cmddesc needs update*/ pdesc = &ring->desc[0]; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c index a2f5e89bedfe..6e518625edbe 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c @@ -318,9 +318,7 @@ bool rtl8723_cmd_send_packet(struct ieee80211_hw *hw, ring = &rtlpci->tx_ring[BEACON_QUEUE]; pskb = __skb_dequeue(&ring->queue); - if (pskb) - kfree_skb(pskb); - + kfree_skb(pskb); spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); pdesc = &ring->desc[0]; -- cgit v1.2.3-59-g8ed1b From 68ec581011ae0cf84392e7b22b05dc63818e4e6d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 18 Nov 2015 16:09:18 +0200 Subject: rtlwifi: btcoexist: re-use %*ph specifier to hexdump Instead of printing each byte from the given buffer the code is converted to use %*ph specifier. Signed-off-by: Andy Shevchenko Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8192e2ant.c | 23 ++++++---------------- .../realtek/rtlwifi/btcoexist/halbtc8723b1ant.c | 21 ++++++-------------- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 22 ++++++--------------- .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 23 ++++++---------------- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 21 ++++++-------------- 5 files changed, 30 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c index 53261d6f8578..451456835f87 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c @@ -3356,9 +3356,8 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist) "Dot11 channel / HsMode(HsChnl)", wifi_dot11_chnl, bt_hson, wifi_hs_chnl); - RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %02x %02x %02x ", - "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info[0], - coex_dm->wifi_chnl_info[1], coex_dm->wifi_chnl_info[2]); + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %3ph ", + "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info); btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifirssi); btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi); @@ -3409,17 +3408,9 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist) for (i = 0; i < BT_INFO_SRC_8192E_2ANT_MAX; i++) { if (coex_sta->bt_info_c2h_cnt[i]) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x %02x ", + "\r\n %-35s = %7ph(%d)", GLBtInfoSrc8192e2Ant[i], - coex_sta->bt_info_c2h[i][0], - coex_sta->bt_info_c2h[i][1], - coex_sta->bt_info_c2h[i][2], - coex_sta->bt_info_c2h[i][3]); - RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "%02x %02x %02x(%d)", - coex_sta->bt_info_c2h[i][4], - coex_sta->bt_info_c2h[i][5], - coex_sta->bt_info_c2h[i][6], + coex_sta->bt_info_c2h[i], coex_sta->bt_info_c2h_cnt[i]); } } @@ -3453,10 +3444,8 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist) ps_tdma_case = coex_dm->cur_ps_tdma; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)", - "PS TDMA", coex_dm->ps_tdma_para[0], - coex_dm->ps_tdma_para[1], coex_dm->ps_tdma_para[2], - coex_dm->ps_tdma_para[3], coex_dm->ps_tdma_para[4], + "\r\n %-35s = %5ph case-%d (auto:%d)", + "PS TDMA", coex_dm->ps_tdma_para, ps_tdma_case, coex_dm->auto_tdma_adjust); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d/ %d ", diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c index c4acd403e5f6..7e239d3cea26 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c @@ -2457,10 +2457,9 @@ void ex_halbtc8723b1ant_display_coex_info(struct btc_coexist *btcoexist) "Dot11 channel / HsChnl(HsMode)", wifi_dot11_chnl, wifi_hs_chnl, bt_hs_on); - RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %02x %02x %02x ", + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %3ph ", "H2C Wifi inform bt chnl Info", - coex_dm->wifi_chnl_info[0], coex_dm->wifi_chnl_info[1], - coex_dm->wifi_chnl_info[2]); + coex_dm->wifi_chnl_info); btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi); btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi); @@ -2525,15 +2524,9 @@ void ex_halbtc8723b1ant_display_coex_info(struct btc_coexist *btcoexist) for (i = 0; i < BT_INFO_SRC_8723B_1ANT_MAX; i++) { if (coex_sta->bt_info_c2h_cnt[i]) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x %02x %02x %02x %02x(%d)", + "\r\n %-35s = %7ph(%d)", GLBtInfoSrc8723b1Ant[i], - coex_sta->bt_info_c2h[i][0], - coex_sta->bt_info_c2h[i][1], - coex_sta->bt_info_c2h[i][2], - coex_sta->bt_info_c2h[i][3], - coex_sta->bt_info_c2h[i][4], - coex_sta->bt_info_c2h[i][5], - coex_sta->bt_info_c2h[i][6], + coex_sta->bt_info_c2h[i], coex_sta->bt_info_c2h_cnt[i]); } } @@ -2569,10 +2562,8 @@ void ex_halbtc8723b1ant_display_coex_info(struct btc_coexist *btcoexist) pstdmacase = coex_dm->cur_ps_tdma; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)", - "PS TDMA", coex_dm->ps_tdma_para[0], - coex_dm->ps_tdma_para[1], coex_dm->ps_tdma_para[2], - coex_dm->ps_tdma_para[3], coex_dm->ps_tdma_para[4], + "\r\n %-35s = %5ph case-%d (auto:%d)", + "PS TDMA", coex_dm->ps_tdma_para, pstdmacase, coex_dm->auto_tdma_adjust); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d ", diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index f2b9d11adc9e..c43ab59a690a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -3215,9 +3215,8 @@ void ex_btc8723b2ant_display_coex_info(struct btc_coexist *btcoexist) "Dot11 channel / HsChnl(HsMode)", wifi_dot11_chnl, wifi_hs_chnl, bt_hs_on); - RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %02x %02x %02x ", - "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info[0], - coex_dm->wifi_chnl_info[1], coex_dm->wifi_chnl_info[2]); + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %3ph ", + "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info); btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi); btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi); @@ -3259,16 +3258,9 @@ void ex_btc8723b2ant_display_coex_info(struct btc_coexist *btcoexist) for (i = 0; i < BT_INFO_SRC_8723B_2ANT_MAX; i++) { if (coex_sta->bt_info_c2h_cnt[i]) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x " - "%02x %02x %02x %02x(%d)", + "\r\n %-35s = %7ph(%d)", glbt_info_src_8723b_2ant[i], - coex_sta->bt_info_c2h[i][0], - coex_sta->bt_info_c2h[i][1], - coex_sta->bt_info_c2h[i][2], - coex_sta->bt_info_c2h[i][3], - coex_sta->bt_info_c2h[i][4], - coex_sta->bt_info_c2h[i][5], - coex_sta->bt_info_c2h[i][6], + coex_sta->bt_info_c2h[i], coex_sta->bt_info_c2h_cnt[i]); } } @@ -3296,10 +3288,8 @@ void ex_btc8723b2ant_display_coex_info(struct btc_coexist *btcoexist) ps_tdma_case = coex_dm->cur_ps_tdma; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)", - "PS TDMA", coex_dm->ps_tdma_para[0], - coex_dm->ps_tdma_para[1], coex_dm->ps_tdma_para[2], - coex_dm->ps_tdma_para[3], coex_dm->ps_tdma_para[4], + "\r\n %-35s = %5ph case-%d (auto:%d)", + "PS TDMA", coex_dm->ps_tdma_para, ps_tdma_case, coex_dm->auto_tdma_adjust); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d/ %d ", diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index b72e5377bdbc..9cecf174a37d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -2302,10 +2302,9 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist) wifi_dot11_chnl, wifi_hs_chnl, bt_hs_on); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x ", + "\r\n %-35s = %3ph ", "H2C Wifi inform bt chnl Info", - coex_dm->wifi_chnl_info[0], coex_dm->wifi_chnl_info[1], - coex_dm->wifi_chnl_info[2]); + coex_dm->wifi_chnl_info); btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi); btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi); @@ -2366,15 +2365,9 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist) for (i = 0; i < BT_INFO_SRC_8821A_1ANT_MAX; i++) { if (coex_sta->bt_info_c2h_cnt[i]) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x %02x %02x %02x %02x(%d)", + "\r\n %-35s = %7ph(%d)", glbt_info_src_8821a_1ant[i], - coex_sta->bt_info_c2h[i][0], - coex_sta->bt_info_c2h[i][1], - coex_sta->bt_info_c2h[i][2], - coex_sta->bt_info_c2h[i][3], - coex_sta->bt_info_c2h[i][4], - coex_sta->bt_info_c2h[i][5], - coex_sta->bt_info_c2h[i][6], + coex_sta->bt_info_c2h[i], coex_sta->bt_info_c2h_cnt[i]); } } @@ -2412,13 +2405,9 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist) ps_tdma_case = coex_dm->cur_ps_tdma; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)", + "\r\n %-35s = %5ph case-%d (auto:%d)", "PS TDMA", - coex_dm->ps_tdma_para[0], - coex_dm->ps_tdma_para[1], - coex_dm->ps_tdma_para[2], - coex_dm->ps_tdma_para[3], - coex_dm->ps_tdma_para[4], + coex_dm->ps_tdma_para, ps_tdma_case, coex_dm->auto_tdma_adjust); diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index cf819f02ed23..044d914291c0 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -3393,10 +3393,9 @@ ex_halbtc8821a2ant_display_coex_info( wifi_dot_11_chnl, bt_hs_on, wifi_hs_chnl); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x ", + "\r\n %-35s = %3ph ", "H2C Wifi inform bt chnl Info", - coex_dm->wifi_chnl_info[0], coex_dm->wifi_chnl_info[1], - coex_dm->wifi_chnl_info[2]); + coex_dm->wifi_chnl_info); btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi); btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi); @@ -3454,15 +3453,9 @@ ex_halbtc8821a2ant_display_coex_info( for (i = 0; i < BT_INFO_SRC_8821A_2ANT_MAX; i++) { if (coex_sta->bt_info_c2h_cnt[i]) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x %02x %02x %02x %02x(%d)", + "\r\n %-35s = %7ph(%d)", glbt_info_src_8821a_2ant[i], - coex_sta->bt_info_c2h[i][0], - coex_sta->bt_info_c2h[i][1], - coex_sta->bt_info_c2h[i][2], - coex_sta->bt_info_c2h[i][3], - coex_sta->bt_info_c2h[i][4], - coex_sta->bt_info_c2h[i][5], - coex_sta->bt_info_c2h[i][6], + coex_sta->bt_info_c2h[i], coex_sta->bt_info_c2h_cnt[i]); } } @@ -3494,11 +3487,9 @@ ex_halbtc8821a2ant_display_coex_info( if (!btcoexist->manual_control) { ps_tdma_case = coex_dm->cur_ps_tdma; RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, - "\r\n %-35s = %02x %02x %02x %02x %02x case-%d", + "\r\n %-35s = %5ph case-%d", "PS TDMA", - coex_dm->ps_tdma_para[0], coex_dm->ps_tdma_para[1], - coex_dm->ps_tdma_para[2], coex_dm->ps_tdma_para[3], - coex_dm->ps_tdma_para[4], ps_tdma_case); + coex_dm->ps_tdma_para, ps_tdma_case); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d/ %d ", "DecBtPwr/ IgnWlanAct", -- cgit v1.2.3-59-g8ed1b From 0a38c8e1b592c16d959da456f425053e323a5153 Mon Sep 17 00:00:00 2001 From: sudip Date: Tue, 24 Nov 2015 13:51:38 +0530 Subject: libertas: check for NULL before use If kzalloc fails it will return NULL. Lets check for NULL first before using the pointer. Signed-off-by: Sudip Mukherjee Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/libertas/cfg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c index 8317afd065b4..86955c416b30 100644 --- a/drivers/net/wireless/marvell/libertas/cfg.c +++ b/drivers/net/wireless/marvell/libertas/cfg.c @@ -1108,7 +1108,7 @@ static int lbs_associate(struct lbs_private *priv, size_t len, resp_ie_len; int status; int ret; - u8 *pos = &(cmd->iebuf[0]); + u8 *pos; u8 *tmp; lbs_deb_enter(LBS_DEB_CFG80211); @@ -1117,6 +1117,7 @@ static int lbs_associate(struct lbs_private *priv, ret = -ENOMEM; goto done; } + pos = &cmd->iebuf[0]; /* * cmd 50 00 -- cgit v1.2.3-59-g8ed1b From 7d1620451d4994d767bdcb23907209b21603d185 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 17 Sep 2015 15:54:47 +0200 Subject: iwlwifi: nvm: fix up phy section when reading it This is a workaround to an OTP bug. In Series 8000 1x1, the OTP 0xA052 defines 2x2 antenna configuration. This workaround overrides the decision based on HW id and on MIMO disabled bit which is correct in the OTP and set to disabled. This fixes the previous workaround "force 1x1 antenna in Series 8000". Signed-off-by: Moshe Harel Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 15 +----------- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 27 +++++++++++++++++++--- 3 files changed, 26 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index d82984912e04..3b8e85e51002 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -580,15 +580,13 @@ static void iwl_set_hw_address_family_8000(struct device *dev, IWL_ERR_DEV(dev, "mac address is not found\n"); } -#define IWL_4165_DEVICE_ID 0x5501 - struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, const __le16 *nvm_calib, const __le16 *regulatory, const __le16 *mac_override, const __le16 *phy_sku, u8 tx_chains, u8 rx_chains, bool lar_fw_supported, - u32 mac_addr0, u32 mac_addr1, u32 hw_id) + u32 mac_addr0, u32 mac_addr1) { struct iwl_nvm_data *data; u32 sku; @@ -627,17 +625,6 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, (sku & NVM_SKU_CAP_11AC_ENABLE); data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE; - /* - * OTP 0x52 bug work around - * define antenna 1x1 according to MIMO disabled - */ - if (hw_id == IWL_4165_DEVICE_ID && data->sku_cap_mimo_disabled) { - data->valid_tx_ant = ANT_B; - data->valid_rx_ant = ANT_B; - tx_chains = ANT_B; - rx_chains = ANT_B; - } - data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw); if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 9f44d8188c5c..822ba52e0e5a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -79,7 +79,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_calib, const __le16 *regulatory, const __le16 *mac_override, const __le16 *phy_sku, u8 tx_chains, u8 rx_chains, bool lar_fw_supported, - u32 mac_addr0, u32 mac_addr1, u32 hw_id); + u32 mac_addr0, u32 mac_addr1); /** * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 2ee0f6fe56a1..1edcfcc459f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -210,6 +210,19 @@ static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section, return 0; } +static void iwl_mvm_nvm_fixups(struct iwl_mvm *mvm, unsigned int section, + u8 *data, unsigned int len) +{ +#define IWL_4165_DEVICE_ID 0x5501 +#define NVM_SKU_CAP_MIMO_DISABLE BIT(5) + + if (section == NVM_SECTION_TYPE_PHY_SKU && + mvm->trans->hw_id == IWL_4165_DEVICE_ID && data && len >= 5 && + (data[4] & NVM_SKU_CAP_MIMO_DISABLE)) + /* OTP 0x52 bug work around: it's a 1x1 device */ + data[3] = ANT_B | (ANT_B << 4); +} + /* * Reads an NVM section completely. * NICs prior to 7000 family doesn't have a real NVM, but just read @@ -250,6 +263,8 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, offset += ret; } + iwl_mvm_nvm_fixups(mvm, section, data, offset); + IWL_DEBUG_EEPROM(mvm->trans->dev, "NVM section %d read completed\n", section); return offset; @@ -316,8 +331,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, regulatory, mac_override, phy_sku, mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant, - lar_enabled, mac_addr0, mac_addr1, - mvm->trans->hw_id); + lar_enabled, mac_addr0, mac_addr1); } #define MAX_NVM_FILE_LEN 16384 @@ -353,7 +367,8 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) __le16 word2; u8 data[]; } *file_sec; - const u8 *eof, *temp; + const u8 *eof; + u8 *temp; int max_section_size; const __le32 *dword_buff; @@ -483,6 +498,9 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) ret = -ENOMEM; break; } + + iwl_mvm_nvm_fixups(mvm, section_id, temp, section_size); + kfree(mvm->nvm_sections[section_id].data); mvm->nvm_sections[section_id].data = temp; mvm->nvm_sections[section_id].length = section_size; @@ -548,6 +566,9 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) ret = -ENOMEM; break; } + + iwl_mvm_nvm_fixups(mvm, section, temp, ret); + mvm->nvm_sections[section].data = temp; mvm->nvm_sections[section].length = ret; -- cgit v1.2.3-59-g8ed1b From 6c4fbcbc1c954d61711e3761a05283e980a6106e Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 10 Nov 2015 11:57:41 +0200 Subject: iwlwifi: add support for 12K Receive Buffers 802.11ac allows A-MSDU that can be up to 12KB long. Since an entire A-MSDU needs to fit into one single Receive Buffer (RB), add support for big RBs. Since this adds lots of pressure to the memory manager and significantly increase the true_size of the RX buffers, don't enable this by default. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/main.c | 16 +++++++++++++++- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 4 ++-- .../net/wireless/intel/iwlwifi/iwl-eeprom-parse.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-modparams.h | 10 ++++++++-- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 13 ++++++++++++- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 20 ++++++++++++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 16 +++++++++++++++- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 16 +++++++++++++--- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 8 +++----- 10 files changed, 89 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index e7616f0ee6e8..41f3aa17475e 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -1227,7 +1227,21 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, trans_cfg.op_mode = op_mode; trans_cfg.no_reclaim_cmds = no_reclaim_cmds; trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); - trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; + + switch (iwlwifi_mod_params.amsdu_size) { + case IWL_AMSDU_4K: + trans_cfg.rx_buf_size = IWL_AMSDU_4K; + break; + case IWL_AMSDU_8K: + trans_cfg.rx_buf_size = IWL_AMSDU_8K; + break; + case IWL_AMSDU_12K: + default: + trans_cfg.rx_buf_size = IWL_AMSDU_4K; + pr_err("Unsupported amsdu_size: %d\n", + iwlwifi_mod_params.amsdu_size); + } + trans_cfg.cmd_q_wdg_timeout = IWL_WATCHDOG_DISABLED; trans_cfg.command_names = iwl_dvm_cmd_strings; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 463cadfbfccb..48f090af19db 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1637,9 +1637,9 @@ MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); module_param_named(11n_disable, iwlwifi_mod_params.disable_11n, uint, S_IRUGO); MODULE_PARM_DESC(11n_disable, "disable 11n functionality, bitmap: 1: full, 2: disable agg TX, 4: disable agg RX, 8 enable agg TX"); -module_param_named(amsdu_size_8K, iwlwifi_mod_params.amsdu_size_8K, +module_param_named(amsdu_size, iwlwifi_mod_params.amsdu_size, int, S_IRUGO); -MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0)"); +MODULE_PARM_DESC(amsdu_size, "amsdu size 0:4K 1:8K 2:12K (default 0)"); module_param_named(fw_restart, iwlwifi_mod_params.restart_fw, bool, S_IRUGO); MODULE_PARM_DESC(fw_restart, "restart firmware in case of error (default true)"); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c index acc3d186c5c1..a19c4582936f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c @@ -766,7 +766,7 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, if (cfg->ht_params->ldpc) ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; - if (iwlwifi_mod_params.amsdu_size_8K) + if (iwlwifi_mod_params.amsdu_size >= IWL_AMSDU_8K) ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; ht_info->ampdu_factor = cfg->max_ht_ampdu_exponent; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h index ac2b90df8413..3436c0066e84 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -86,6 +86,12 @@ enum iwl_disable_11n { IWL_ENABLE_HT_TXAGG = BIT(3), }; +enum iwl_amsdu_size { + IWL_AMSDU_4K = 0, + IWL_AMSDU_8K = 1, + IWL_AMSDU_12K = 2, +}; + /** * struct iwl_mod_params * @@ -94,7 +100,7 @@ enum iwl_disable_11n { * @sw_crypto: using hardware encryption, default = 0 * @disable_11n: disable 11n capabilities, default = 0, * use IWL_[DIS,EN]ABLE_HT_* constants - * @amsdu_size_8K: enable 8K amsdu size, default = 0 + * @amsdu_size: enable 8K amsdu size, default = 4K. enum iwl_amsdu_size. * @restart_fw: restart firmware, default = 1 * @bt_coex_active: enable bt coex, default = true * @led_mode: system default, default = 0 @@ -109,7 +115,7 @@ enum iwl_disable_11n { struct iwl_mod_params { int sw_crypto; unsigned int disable_11n; - int amsdu_size_8K; + int amsdu_size; bool restart_fw; bool bt_coex_active; int led_mode; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 3b8e85e51002..60b7fce88d32 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -379,8 +379,19 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, else vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; - if (iwlwifi_mod_params.amsdu_size_8K) + switch (iwlwifi_mod_params.amsdu_size) { + case IWL_AMSDU_4K: + vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; + break; + case IWL_AMSDU_8K: vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; + break; + case IWL_AMSDU_12K: + vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; + break; + default: + break; + } vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 6f76525088f0..607f4f7ea94c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -423,6 +423,22 @@ enum iwl_trans_status { STATUS_TRANS_DEAD, }; +static inline int +iwl_trans_get_rb_size_order(enum iwl_amsdu_size rb_size) +{ + switch (rb_size) { + case IWL_AMSDU_4K: + return get_order(4 * 1024); + case IWL_AMSDU_8K: + return get_order(8 * 1024); + case IWL_AMSDU_12K: + return get_order(12 * 1024); + default: + WARN_ON(1); + return -1; + } +} + /** * struct iwl_trans_config - transport configuration * @@ -436,7 +452,7 @@ enum iwl_trans_status { * list of such notifications to filter. Max length is * %MAX_NO_RECLAIM_CMDS. * @n_no_reclaim_cmds: # of commands in list - * @rx_buf_size_8k: 8 kB RX buffer size needed for A-MSDUs, + * @rx_buf_size: RX buffer size needed for A-MSDUs * if unset 4k will be the RX buffer size * @bc_table_dword: set to true if the BC table expects the byte count to be * in DWORD (as opposed to bytes) @@ -456,7 +472,7 @@ struct iwl_trans_config { const u8 *no_reclaim_cmds; unsigned int n_no_reclaim_cmds; - bool rx_buf_size_8k; + enum iwl_amsdu_size rx_buf_size; bool bc_table_dword; bool scd_set_active; bool wide_cmd_header; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 13c97f665ba8..1646cddadbd8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -485,7 +485,21 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.op_mode = op_mode; trans_cfg.no_reclaim_cmds = no_reclaim_cmds; trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); - trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; + switch (iwlwifi_mod_params.amsdu_size) { + case IWL_AMSDU_4K: + trans_cfg.rx_buf_size = IWL_AMSDU_4K; + break; + case IWL_AMSDU_8K: + trans_cfg.rx_buf_size = IWL_AMSDU_8K; + break; + case IWL_AMSDU_12K: + trans_cfg.rx_buf_size = IWL_AMSDU_12K; + break; + default: + pr_err("%s: Unsupported amsdu_size: %d\n", KBUILD_MODNAME, + iwlwifi_mod_params.amsdu_size); + trans_cfg.rx_buf_size = IWL_AMSDU_4K; + } trans_cfg.wide_cmd_header = fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index feb2f7e81134..bf8cb59097ea 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -302,7 +302,7 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) * @ucode_write_complete: indicates that the ucode has been copied. * @ucode_write_waitq: wait queue for uCode load * @cmd_queue - command queue number - * @rx_buf_size_8k: 8 kB RX buffer size + * @rx_buf_size: Rx buffer size * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) * @scd_set_active: should the transport configure the SCD for HCMD queue * @wide_cmd_header: true when ucode supports wide command header format @@ -356,7 +356,7 @@ struct iwl_trans_pcie { u8 n_no_reclaim_cmds; u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; - bool rx_buf_size_8k; + enum iwl_amsdu_size rx_buf_size; bool bc_table_dword; bool scd_set_active; bool wide_cmd_header; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index e06591f625c4..b837b34f9cdb 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -602,10 +602,20 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) u32 rb_size; const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ - if (trans_pcie->rx_buf_size_8k) + switch (trans_pcie->rx_buf_size) { + case IWL_AMSDU_4K: + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; + break; + case IWL_AMSDU_8K: rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; - else + break; + case IWL_AMSDU_12K: + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K; + break; + default: + WARN_ON(1); rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; + } /* Stop Rx DMA */ iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); @@ -629,7 +639,7 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in * the credit mechanism in 5000 HW RX FIFO * Direct rx interrupts to hosts - * Rx buffer size 4 or 8k + * Rx buffer size 4 or 8k or 12k * RB timeout 0x10 * 256 RBDs */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 90283453073c..889227c54120 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1435,11 +1435,9 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds, trans_pcie->n_no_reclaim_cmds * sizeof(u8)); - trans_pcie->rx_buf_size_8k = trans_cfg->rx_buf_size_8k; - if (trans_pcie->rx_buf_size_8k) - trans_pcie->rx_page_order = get_order(8 * 1024); - else - trans_pcie->rx_page_order = get_order(4 * 1024); + trans_pcie->rx_buf_size = trans_cfg->rx_buf_size; + trans_pcie->rx_page_order = + iwl_trans_get_rb_size_order(trans_pcie->rx_buf_size); trans_pcie->wide_cmd_header = trans_cfg->wide_cmd_header; trans_pcie->command_names = trans_cfg->command_names; -- cgit v1.2.3-59-g8ed1b From d29cb66463810614abf81dd80d75da59c1e4fd07 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Nov 2015 10:57:56 +0100 Subject: iwlwifi: dvm: remove Kconfig default The split of iwlwifi into DVM and MVM was a long time ago now, so we can remove the "default IWLWIFI" that we had to keep all existing .config files with working defaults during the split. This is no longer necessary, practically nobody should now be upgrading a .config that's older than the split. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig index 6e949df399d6..866067789330 100644 --- a/drivers/net/wireless/intel/iwlwifi/Kconfig +++ b/drivers/net/wireless/intel/iwlwifi/Kconfig @@ -53,7 +53,7 @@ config IWLWIFI_LEDS config IWLDVM tristate "Intel Wireless WiFi DVM Firmware support" - default IWLWIFI + depends on m help This is the driver that supports the DVM firmware. The list of the devices that use this firmware is available here: -- cgit v1.2.3-59-g8ed1b From 85e5a3876393484361db8ae8d43b1bc27b8ad9c2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Nov 2015 16:16:01 +0100 Subject: iwlwifi: trans: make various conversion macros inlines Make the various conversion functions typesafe, so we don't accidentally try to call them with the wrong pointers and cast them to something that will crash. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index bf8cb59097ea..bf41543996a8 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -378,8 +378,11 @@ struct iwl_trans_pcie { u32 fw_mon_size; }; -#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ - ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) +static inline struct iwl_trans_pcie * +IWL_TRANS_GET_PCIE_TRANS(struct iwl_trans *trans) +{ + return (void *)trans->trans_specific; +} static inline struct iwl_trans * iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie) -- cgit v1.2.3-59-g8ed1b From f9d716443f44238b43559cdd6ba2473560efa44c Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Tue, 10 Nov 2015 14:56:24 +0200 Subject: iwlwifi: mvm: Configure fragmented scan for scheduled scan Configure the FW to use fragmented scan when the traffic load is high or low latency traffic is on. This is useful for scans that are managed by the FW (e.g. scheduled scan). Signed-off-by: Avraham Stern Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 4bde2d027dcd..419afa47c7b4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -643,6 +643,7 @@ struct iwl_mvm { unsigned int scan_status; void *scan_cmd; struct iwl_mcast_filter_cmd *mcast_filter_cmd; + bool scan_fragmented; /* max number of simultaneous scans the FW supports */ unsigned int max_scans; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index d6e0c1b5c20c..6b9b1904551c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -920,6 +920,8 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) if (!scan_config) return -ENOMEM; + mvm->scan_fragmented = iwl_mvm_low_latency(mvm); + scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE | SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS | SCAN_CONFIG_FLAG_SET_TX_CHAINS | @@ -928,7 +930,10 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) SCAN_CONFIG_FLAG_SET_LEGACY_RATES | SCAN_CONFIG_FLAG_SET_MAC_ADDR | SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS| - SCAN_CONFIG_N_CHANNELS(num_channels)); + SCAN_CONFIG_N_CHANNELS(num_channels) | + (mvm->scan_fragmented ? + SCAN_CONFIG_FLAG_SET_FRAGMENTED : + SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED)); scan_config->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm)); scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm); -- cgit v1.2.3-59-g8ed1b From b9de521f2da2602a7649d58bc81ed5325f1291f7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 6 Nov 2015 12:00:36 +0100 Subject: iwlwifi: dvm: remove stray debug code This code was needed during initial PAN bringup, but now is just cruft - remove it. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index b3ad34e8bf5a..a9d8b51419b4 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -1411,13 +1411,7 @@ static void iwlagn_mac_remove_interface(struct ieee80211_hw *hw, mutex_lock(&priv->mutex); - if (WARN_ON(ctx->vif != vif)) { - struct iwl_rxon_context *tmp; - IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif); - for_each_context(priv, tmp) - IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n", - tmp->ctxid, tmp, tmp->vif); - } + WARN_ON(ctx->vif != vif); ctx->vif = NULL; iwl_teardown_interface(priv, vif, false); -- cgit v1.2.3-59-g8ed1b From e7c2e1fdcd2f3f6387a252320cf23a907eb4a2bb Mon Sep 17 00:00:00 2001 From: Avri Altman Date: Thu, 29 Oct 2015 16:50:57 +0200 Subject: iwlwifi: mvm: Enable MPLUT only on supported hw When there's a bt-wifi contention that requires arbitration, we use a priority-based mechanism to decide which comm wins. Over time, use cases become more and more complex, with multiple concurrent active links with different traffic types and different QoS requirements, on both WiFi and BT sides. This, in turn, requires us to elaborate our prioritization mechanism. However, our legacy products included hw that does not supports this, so selectively enable this on specific hw - as signaled by the firmware. Signed-off-by: Avri Altman Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 5 +---- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 7 +++++++ 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index 08303db0000f..6aeecebe9c76 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -308,6 +308,7 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT: supports gscan * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts + * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -334,6 +335,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)31, IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64, IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, + IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67, NUM_IWL_UCODE_TLV_CAPA #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index e290ac67d975..808f234c953e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -443,11 +443,8 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) if (iwl_mvm_bt_is_plcr_supported(mvm)) bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED); - if (IWL_MVM_BT_COEX_MPLUT) { + if (iwl_mvm_is_mplut_supported(mvm)) bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED); - bt_cmd.enabled_modules |= - cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED); - } bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 419afa47c7b4..aff71d300592 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -976,6 +976,13 @@ static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_CSUM_SUPPORT); } +static inline bool iwl_mvm_is_mplut_supported(struct iwl_mvm *mvm) +{ + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT) && + IWL_MVM_BT_COEX_MPLUT; +} + static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm) { /* firmware flag isn't defined yet */ -- cgit v1.2.3-59-g8ed1b From 72a3356885d13c4dd0b41034b931e3d5a0f807cc Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Fri, 6 Nov 2015 10:57:11 +0200 Subject: iwlwifi: mvm: ignore LMAC scan notifications when running UMAC scans If the firmware sends LMAC scan notifications while a UMAC scan is running, just WARN and ignore it, otherwise the scanning state gets messed up. Signed-off-by: Luca Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 6b9b1904551c..e8a79bff9c1b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -333,6 +333,13 @@ void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, struct iwl_periodic_scan_complete *scan_notif = (void *)pkt->data; bool aborted = (scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED); + /* If this happens, the firmware has mistakenly sent an LMAC + * notification during UMAC scans -- warn and ignore it. + */ + if (WARN_ON_ONCE(fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_UMAC_SCAN))) + return; + /* scan status must be locked for proper checking */ lockdep_assert_held(&mvm->mutex); -- cgit v1.2.3-59-g8ed1b From 9a57f650d0c95b827cb795e69d824accda045a9d Mon Sep 17 00:00:00 2001 From: Matti Gottlieb Date: Mon, 2 Nov 2015 13:01:54 +0200 Subject: iwlwifi: mvm: check FW's response for nvm access write cmd In case of using an external NVM file, the driver sends to the FW the different nvm sections. In the response of the cmd, the FW states the status of the writing of the chunk. Currently the value is not checked by the driver. Check FW's response for writing the nvm chunk in the NVM_ACCESS_CMD. Signed-off-by: Matti Gottlieb Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 1edcfcc459f4..a9fcb15e9d1a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -104,13 +104,35 @@ static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, struct iwl_host_cmd cmd = { .id = NVM_ACCESS_CMD, .len = { sizeof(struct iwl_nvm_access_cmd), length }, - .flags = CMD_SEND_IN_RFKILL, + .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, data }, /* data may come from vmalloc, so use _DUP */ .dataflags = { 0, IWL_HCMD_DFL_DUP }, }; + struct iwl_rx_packet *pkt; + struct iwl_nvm_access_resp *nvm_resp; + int ret; + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret) + return ret; - return iwl_mvm_send_cmd(mvm, &cmd); + pkt = cmd.resp_pkt; + if (!pkt) { + IWL_ERR(mvm, "Error in NVM_ACCESS response\n"); + return -EINVAL; + } + /* Extract & check NVM write response */ + nvm_resp = (void *)pkt->data; + if (le16_to_cpu(nvm_resp->status) != READ_NVM_CHUNK_SUCCEED) { + IWL_ERR(mvm, + "NVM access write command failed for section %u (status = 0x%x)\n", + section, le16_to_cpu(nvm_resp->status)); + ret = -EIO; + } + + iwl_free_resp(&cmd); + return ret; } static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, -- cgit v1.2.3-59-g8ed1b From 13fb01d8155207f34dcfbf36d51faf93506c72fe Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 6 Nov 2015 11:26:26 +0100 Subject: iwlwifi: pcie: remove ICT allocation message This message isn't very useful and presents a security risk due to the use of %p - remove it. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index b837b34f9cdb..2b5d3e1d0fee 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1491,10 +1491,6 @@ int iwl_pcie_alloc_ict(struct iwl_trans *trans) return -EINVAL; } - IWL_DEBUG_ISR(trans, "ict dma addr %Lx ict vir addr %p\n", - (unsigned long long)trans_pcie->ict_tbl_dma, - trans_pcie->ict_tbl); - return 0; } -- cgit v1.2.3-59-g8ed1b From b2c5d3a89fe4ec47bf5567220326d7b3b06fd514 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Fri, 18 Sep 2015 14:37:46 +0300 Subject: iwlwifi: generalize d0i3_entry_timeout module parameter The PCIe transport will also need a d0i3_entry_timeout_ms parameter, so move the existing one from the slave transports to iwlwifi, so it can be reused. While at it, rename the parameter to something shorter, namely d0i3_entry_delay. Signed-off-by: Luca Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 5 +++++ drivers/net/wireless/intel/iwlwifi/iwl-modparams.h | 3 +++ 2 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 48f090af19db..eedd0d874759 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1539,6 +1539,7 @@ struct iwl_mod_params iwlwifi_mod_params = { .bt_coex_active = true, .power_level = IWL_POWER_INDEX_1, .d0i3_disable = true, + .d0i3_entry_delay = 1000, #ifndef CONFIG_IWLWIFI_UAPSD .uapsd_disable = true, #endif /* CONFIG_IWLWIFI_UAPSD */ @@ -1704,3 +1705,7 @@ MODULE_PARM_DESC(power_level, module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO); MODULE_PARM_DESC(fw_monitor, "firmware monitor - to debug FW (default: false - needs lots of memory)"); + +module_param_named(d0i3_timeout, iwlwifi_mod_params.d0i3_entry_delay, + uint, S_IRUGO); +MODULE_PARM_DESC(d0i3_timeout, "Timeout to D0i3 entry when idle (ms)"); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h index 3436c0066e84..9baf9ef8bdfb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -109,6 +109,8 @@ enum iwl_amsdu_size { * @debug_level: levels are IWL_DL_* * @ant_coupling: antenna coupling in dB, default = 0 * @d0i3_disable: disable d0i3, default = 1, + * @d0i3_entry_delay: time to wait after no refs are taken before + * entering D0i3 (in msecs) * @lar_disable: disable LAR (regulatory), default = 0 * @fw_monitor: allow to use firmware monitor */ @@ -128,6 +130,7 @@ struct iwl_mod_params { char *nvm_file; bool uapsd_disable; bool d0i3_disable; + unsigned int d0i3_entry_delay; bool lar_disable; bool fw_monitor; }; -- cgit v1.2.3-59-g8ed1b From 59fd4bf64591fbd02c6115d9282e2e7cffefa6e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 1 Nov 2015 13:06:37 +0200 Subject: iwlwifi: mvm: change name of iwl_mvm_d3_update_gtk This function updates the pairwise keys as well. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 29ae58ebf223..76c20025a13d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1406,7 +1406,7 @@ struct iwl_mvm_d3_gtk_iter_data { int num_keys; }; -static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw, +static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key, @@ -1494,7 +1494,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, /* find last GTK that we used initially, if any */ gtkdata.find_phase = true; ieee80211_iter_keys(mvm->hw, vif, - iwl_mvm_d3_update_gtks, >kdata); + iwl_mvm_d3_update_keys, >kdata); /* not trying to keep connections with MFP/unhandled ciphers */ if (gtkdata.unhandled_cipher) return false; @@ -1509,7 +1509,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, */ gtkdata.find_phase = false; ieee80211_iter_keys(mvm->hw, vif, - iwl_mvm_d3_update_gtks, >kdata); + iwl_mvm_d3_update_keys, >kdata); if (status->num_of_gtk_rekeys) { struct ieee80211_key_conf *key; -- cgit v1.2.3-59-g8ed1b From 4707fde5cdefc73c408915af2579c4acaf49ade4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 22 Sep 2015 12:24:31 +0200 Subject: iwlwifi: mvm: use build-time assertion for fw trigger ID The firmware debug trigger ID is always a compile-time constant, so we can use a build-time assertion to validate that it is in fact a valid constant. To make that really guaranteed to work, convert this and the inline function iwl_fw_dbg_trigger_simple_stop() to macros. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-fw.h | 11 +++++++---- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 15 ++++++++------- 2 files changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h index 84ec0cefb62a..c6946f1644fc 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h @@ -311,12 +311,15 @@ iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id) }) static inline struct iwl_fw_dbg_trigger_tlv* -iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, u8 id) +_iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id) { - if (WARN_ON(id >= ARRAY_SIZE(fw->dbg_trigger_tlv))) - return NULL; - return fw->dbg_trigger_tlv[id]; } +#define iwl_fw_dbg_get_trigger(fw, id) ({ \ + BUILD_BUG_ON(!__builtin_constant_p(id)); \ + BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \ + _iwl_fw_dbg_get_trigger((fw), (id)); \ +}) + #endif /* __iwl_fw_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index aff71d300592..347d953f5604 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1524,20 +1524,21 @@ iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, } static inline void -iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - enum iwl_fw_dbg_trigger trig) +_iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_fw_dbg_trigger_tlv *trigger) { - struct iwl_fw_dbg_trigger_tlv *trigger; - - if (!iwl_fw_dbg_trigger_enabled(mvm->fw, trig)) + if (!trigger) return; - trigger = iwl_fw_dbg_get_trigger(mvm->fw, trig); if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) return; iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); } +#define iwl_fw_dbg_trigger_simple_stop(mvm, vif, trig) \ + _iwl_fw_dbg_trigger_simple_stop((mvm), (vif), \ + iwl_fw_dbg_get_trigger((mvm)->fw,\ + (trig))) #endif /* __IWL_MVM_H__ */ -- cgit v1.2.3-59-g8ed1b From 1e8f1329aa6767dfd29e319ab81ff3aa0a5bef49 Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Sun, 23 Aug 2015 17:57:33 +0300 Subject: iwlwifi: mvm: add trigger for firmware dump upon TDLS events This will allow to catch different TDLS events and get the firmware data when they occur. Add empty TX_LATENCY trigger on the way to avoid mismatch with trees in which this trigger is implemented. Signed-off-by: Golan Ben-Ami Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 2 ++ .../net/wireless/intel/iwlwifi/iwl-fw-error-dump.h | 5 +++ drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 13 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 42 ++++++++++++++++++++-- 4 files changed, 60 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index eedd0d874759..ce656650dd47 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1323,6 +1323,8 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) sizeof(struct iwl_fw_dbg_trigger_time_event); trigger_tlv_sz[FW_DBG_TRIGGER_BA] = sizeof(struct iwl_fw_dbg_trigger_ba); + trigger_tlv_sz[FW_DBG_TRIGGER_TDLS] = + sizeof(struct iwl_fw_dbg_trigger_tdls); for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) { if (pieces->dbg_trigger_tlv[i]) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h index 9dbe19cbb4dd..73bcf29d30cc 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h @@ -288,6 +288,9 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) * @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related * events. * @FW_DBG_TRIGGER_BA: trigger log collection upon BlockAck related events. + * @FW_DBG_TX_LATENCY: trigger log collection when the tx latency goes above a + * threshold. + * @FW_DBG_TDLS: trigger log collection upon TDLS related events. */ enum iwl_fw_dbg_trigger { FW_DBG_TRIGGER_INVALID = 0, @@ -302,6 +305,8 @@ enum iwl_fw_dbg_trigger { FW_DBG_TRIGGER_TXQ_TIMERS, FW_DBG_TRIGGER_TIME_EVENT, FW_DBG_TRIGGER_BA, + FW_DBG_TRIGGER_TX_LATENCY, + FW_DBG_TRIGGER_TDLS, /* must be last */ FW_DBG_TRIGGER_MAX, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index 6aeecebe9c76..e08319a3ed1b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -724,6 +724,19 @@ struct iwl_fw_dbg_trigger_ba { __le16 frame_timeout; } __packed; +/** + * struct iwl_fw_dbg_trigger_tdls - configures trigger for TDLS events. + * @action_bitmap: the TDLS action to trigger the collection upon + * @peer_mode: trigger on specific peer or all + * @peer: the TDLS peer to trigger the collection on + */ +struct iwl_fw_dbg_trigger_tdls { + u8 action_bitmap; + u8 peer_mode; + u8 peer[ETH_ALEN]; + u8 reserved[4]; +} __packed; + /** * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration. * @id: conf id diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index e88afac51c5d..f570c71aeb2a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2699,6 +2699,34 @@ static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; } +static void +iwl_mvm_tdls_check_trigger(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, u8 *peer_addr, + enum nl80211_tdls_operation action) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_tdls *tdls_trig; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TDLS)) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TDLS); + tdls_trig = (void *)trig->data; + if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + return; + + if (!(tdls_trig->action_bitmap & BIT(action))) + return; + + if (tdls_trig->peer_mode && + memcmp(tdls_trig->peer, peer_addr, ETH_ALEN) != 0) + return; + + iwl_mvm_fw_dbg_collect_trig(mvm, trig, + "TDLS event occurred, peer %pM, action %d", + peer_addr, action); +} + static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -2749,8 +2777,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, } ret = iwl_mvm_add_sta(mvm, vif, sta); - if (sta->tdls && ret == 0) + if (sta->tdls && ret == 0) { iwl_mvm_recalc_tdls_state(mvm, vif, true); + iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr, + NL80211_TDLS_SETUP); + } } else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_AUTH) { /* @@ -2774,6 +2805,10 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, if (iwl_mvm_phy_ctx_count(mvm) > 1) iwl_mvm_teardown_tdls_peers(mvm); + if (sta->tdls) + iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr, + NL80211_TDLS_ENABLE_LINK); + /* enable beacon filtering */ WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); ret = 0; @@ -2791,8 +2826,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, } else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST) { ret = iwl_mvm_rm_sta(mvm, vif, sta); - if (sta->tdls) + if (sta->tdls) { iwl_mvm_recalc_tdls_state(mvm, vif, false); + iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr, + NL80211_TDLS_DISABLE_LINK); + } } else { ret = -EIO; } -- cgit v1.2.3-59-g8ed1b From 5053e299aedaadcdc2aff7eaca847d1ee6fdeb54 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Thu, 17 Sep 2015 21:55:24 +0300 Subject: iwlwifi: mvm: remove redundant d0i3 flag from the config struct The d0i3 flag in the device configuration structure is redundant, because the same information can be determined by checking the firmware capability flag. Signed-off-by: Luca Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-8000.c | 1 - drivers/net/wireless/intel/iwlwifi/iwl-config.h | 2 -- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 9 ++++----- 3 files changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c index 9bcc0bf937d8..dd22b7822bc5 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c @@ -154,7 +154,6 @@ static const struct iwl_tt_params iwl8000_tt_params = { .base_params = &iwl8000_base_params, \ .led_mode = IWL_LED_RF_STATE, \ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000, \ - .d0i3 = true, \ .features = NETIF_F_RXCSUM, \ .non_shared_ant = ANT_A, \ .dccm_offset = IWL8260_DCCM_OFFSET, \ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 910970858f98..652cdfe9cc07 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -295,7 +295,6 @@ struct iwl_pwr_tx_backoff { * @high_temp: Is this NIC is designated to be in high temperature. * @host_interrupt_operation_mode: device needs host interrupt operation * mode set - * @d0i3: device uses d0i3 instead of d3 * @nvm_hw_section_num: the ID of the HW NVM section * @features: hw features, any combination of feature_whitelist * @pwr_tx_backoffs: translation table between power limits and backoffs @@ -342,7 +341,6 @@ struct iwl_cfg { const bool internal_wimax_coex; const bool host_interrupt_operation_mode; bool high_temp; - bool d0i3; u8 nvm_hw_section_num; bool lp_xtal_workaround; const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 347d953f5604..897d06f89896 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -916,11 +916,10 @@ iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id) static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) { - return mvm->trans->cfg->d0i3 && - mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF && - !iwlwifi_mod_params.d0i3_disable && - fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); + return mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF && + !iwlwifi_mod_params.d0i3_disable && + fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); } static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm) -- cgit v1.2.3-59-g8ed1b From c725a46bc76e694453230ebb83cd14ade1b16f6c Mon Sep 17 00:00:00 2001 From: Moshe Harel Date: Tue, 27 Oct 2015 14:04:12 +0200 Subject: iwlwifi: mvm: add bt settings to debugfs Add mplut and sync2sco and corunning to debugfs. Signed-off-by: Moshe Harel Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 05928fb4021d..2322c995de56 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -532,6 +532,13 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, mvm->last_ant_isol, mvm->last_corun_lut); } + pos += scnprintf(buf + pos, bufsz - pos, "sync_sco = %d\n", + IWL_MVM_BT_COEX_SYNC2SCO); + pos += scnprintf(buf + pos, bufsz - pos, "mplut = %d\n", + IWL_MVM_BT_COEX_MPLUT); + pos += scnprintf(buf + pos, bufsz - pos, "corunning = %d\n", + IWL_MVM_BT_COEX_CORUNNING); + mutex_unlock(&mvm->mutex); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); -- cgit v1.2.3-59-g8ed1b From 48f0a038d0da461bc6030eef2d6cad316d36aba3 Mon Sep 17 00:00:00 2001 From: Moshe Harel Date: Mon, 26 Oct 2015 11:33:49 +0200 Subject: iwlwifi: mvm: add bt rrc and ttc to debugfs As part of the bt_notif file add fields that are currently not represented Signed-off-by: Moshe Harel Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 2322c995de56..82a61f6c59d1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -512,6 +512,10 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, pos += scnprintf(buf+pos, bufsz-pos, "antenna isolation = %d CORUN LUT index = %d\n", mvm->last_ant_isol, mvm->last_corun_lut); + pos += scnprintf(buf + pos, bufsz - pos, "bt_rrc = %d\n", + notif->rrc_enabled); + pos += scnprintf(buf + pos, bufsz - pos, "bt_ttc = %d\n", + notif->ttc_enabled); } else { struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; @@ -530,6 +534,10 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, pos += scnprintf(buf+pos, bufsz-pos, "antenna isolation = %d CORUN LUT index = %d\n", mvm->last_ant_isol, mvm->last_corun_lut); + pos += scnprintf(buf + pos, bufsz - pos, "bt_rrc = %d\n", + (notif->ttc_rrc_status >> 4) & 0xF); + pos += scnprintf(buf + pos, bufsz - pos, "bt_ttc = %d\n", + notif->ttc_rrc_status & 0xF); } pos += scnprintf(buf + pos, bufsz - pos, "sync_sco = %d\n", -- cgit v1.2.3-59-g8ed1b From 566f165d28760580690d7244c1150357264f9593 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Sun, 25 Oct 2015 22:55:32 +0200 Subject: iwlwifi: mvm: remove stray nd_config element When the netdetect debugfs entry was removed, the nd_config element was accidentally left in the iwl_mvm structure. Remove it. Fixes: dbb04b0d29f8 ("iwlwifi: mvm: remove netdetect debugfs entry") Reported-by: Emmanuel Grumbach Signed-off-by: Luca Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 - drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 6 ------ 3 files changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 76c20025a13d..7bb549640581 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1057,13 +1057,13 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) { /* if we're not associated, this must be netdetect */ - if (!wowlan->nd_config && !mvm->nd_config) { + if (!wowlan->nd_config) { ret = 1; goto out_noreset; } ret = iwl_mvm_netdetect_config( - mvm, wowlan, wowlan->nd_config ?: mvm->nd_config, vif); + mvm, wowlan, wowlan->nd_config, vif); if (ret) goto out; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 897d06f89896..ff32f3604bc0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -732,7 +732,6 @@ struct iwl_mvm { int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; /* sched scan settings for net detect */ - struct cfg80211_sched_scan_request *nd_config; struct ieee80211_scan_ies nd_ies; struct cfg80211_match_set *nd_match_sets; int n_nd_match_sets; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 1646cddadbd8..926492832be7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -642,12 +642,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS) kfree(mvm->d3_resume_sram); - if (mvm->nd_config) { - kfree(mvm->nd_config->match_sets); - kfree(mvm->nd_config->scan_plans); - kfree(mvm->nd_config); - mvm->nd_config = NULL; - } #endif iwl_trans_op_mode_leave(mvm->trans); -- cgit v1.2.3-59-g8ed1b From 1412ee39afaba8806cf6a631ee7347319ca5ae8a Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 22 Oct 2015 13:10:36 +0300 Subject: iwlwifi: mvm: drop low_latency_agg_frame_cnt_limit This was an old workaround for solving latency issues with certain Miracast adapters like ActionTec. However this isn't needed anymore and furthermore it hurts throughput in other use cases. Signed-off-by: Eyal Shapira Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 4 ---- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 -- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 - drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 8 +------- 4 files changed, 1 insertion(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 82a61f6c59d1..48d5ee1b026c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -1491,10 +1491,6 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) goto err; #endif - if (!debugfs_create_u8("low_latency_agg_frame_limit", S_IRUSR | S_IWUSR, - mvm->debugfs_dir, - &mvm->low_latency_agg_frame_limit)) - goto err; if (!debugfs_create_u8("ps_disabled", S_IRUSR, mvm->debugfs_dir, &mvm->ps_disabled)) goto err; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index ff32f3604bc0..f28e0501c5e5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -813,8 +813,6 @@ struct iwl_mvm { bool lar_regdom_set; enum iwl_mcc_source mcc_src; - u8 low_latency_agg_frame_limit; - /* TDLS channel switch data */ struct { struct delayed_work dwork; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 926492832be7..e9b400c7ed79 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -452,7 +452,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->first_agg_queue = 12; } mvm->sf_state = SF_UNINIT; - mvm->low_latency_agg_frame_limit = 6; mvm->cur_ucode = IWL_UCODE_INIT; mutex_init(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index d1ad10391b47..37c2db1c1b1c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -3454,15 +3454,9 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm, * Tx Fifo so that it can start a transaction in the same TxOP. This * basically allows the firmware to send bursts. */ - if (iwl_mvm_vif_low_latency(mvmvif)) { + if (iwl_mvm_vif_low_latency(mvmvif)) lq_cmd->agg_frame_cnt_limit--; - if (mvm->low_latency_agg_frame_limit) - lq_cmd->agg_frame_cnt_limit = - min(lq_cmd->agg_frame_cnt_limit, - mvm->low_latency_agg_frame_limit); - } - if (mvmsta->vif->p2p) lq_cmd->flags |= LQ_FLAG_USE_RTS_MSK; -- cgit v1.2.3-59-g8ed1b From 14ef1b433e8f1336e14105f430a6e614ae86646f Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Wed, 21 Oct 2015 15:16:58 +0300 Subject: iwlwifi: export the _no_grab version of PRPH IO functions Expose _no_grab prph i/o functions that allow performing i/o outside the transport, without requiring grab and release NIC access for each operation. In addition, rename the functions so they reflect their non-grabbing behavior. This can be very useful for consecutive prph i/o operation that occur outside trans, such as fw dumps. Signed-off-by: Golan Ben-Ami Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 25 +++++++++++++++---------- drivers/net/wireless/intel/iwlwifi/iwl-io.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 13 +++++++------ 3 files changed, 24 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index 0bd9d4aad0c0..603c8945871b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project. * @@ -117,18 +118,20 @@ int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, } IWL_EXPORT_SYMBOL(iwl_poll_direct_bit); -u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs) +u32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs) { u32 val = iwl_trans_read_prph(trans, ofs); trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val); return val; } +IWL_EXPORT_SYMBOL(iwl_read_prph_no_grab); -void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) +void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val) { trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val); iwl_trans_write_prph(trans, ofs, val); } +IWL_EXPORT_SYMBOL(iwl_write_prph_no_grab); u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs) { @@ -136,7 +139,7 @@ u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs) u32 val = 0x5a5a5a5a; if (iwl_trans_grab_nic_access(trans, false, &flags)) { - val = __iwl_read_prph(trans, ofs); + val = iwl_read_prph_no_grab(trans, ofs); iwl_trans_release_nic_access(trans, &flags); } return val; @@ -148,7 +151,7 @@ void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) unsigned long flags; if (iwl_trans_grab_nic_access(trans, false, &flags)) { - __iwl_write_prph(trans, ofs, val); + iwl_write_prph_no_grab(trans, ofs, val); iwl_trans_release_nic_access(trans, &flags); } } @@ -174,8 +177,9 @@ void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) unsigned long flags; if (iwl_trans_grab_nic_access(trans, false, &flags)) { - __iwl_write_prph(trans, ofs, - __iwl_read_prph(trans, ofs) | mask); + iwl_write_prph_no_grab(trans, ofs, + iwl_read_prph_no_grab(trans, ofs) | + mask); iwl_trans_release_nic_access(trans, &flags); } } @@ -187,8 +191,9 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, unsigned long flags; if (iwl_trans_grab_nic_access(trans, false, &flags)) { - __iwl_write_prph(trans, ofs, - (__iwl_read_prph(trans, ofs) & mask) | bits); + iwl_write_prph_no_grab(trans, ofs, + (iwl_read_prph_no_grab(trans, ofs) & + mask) | bits); iwl_trans_release_nic_access(trans, &flags); } } @@ -200,8 +205,8 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) u32 val; if (iwl_trans_grab_nic_access(trans, false, &flags)) { - val = __iwl_read_prph(trans, ofs); - __iwl_write_prph(trans, ofs, (val & ~mask)); + val = iwl_read_prph_no_grab(trans, ofs); + iwl_write_prph_no_grab(trans, ofs, (val & ~mask)); iwl_trans_release_nic_access(trans, &flags); } } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h index 501d0560c061..2f4e12c44c6a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h @@ -55,9 +55,9 @@ u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg); void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value); -u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs); +u32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs); u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs); -void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); +void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val); void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, u32 bits, u32 mask, int timeout); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 889227c54120..3c9035da293d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2382,10 +2382,11 @@ iwl_trans_pci_dump_marbh_monitor(struct iwl_trans *trans, if (!iwl_trans_grab_nic_access(trans, false, &flags)) return 0; - __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x1); + iwl_write_prph_no_grab(trans, MON_DMARB_RD_CTL_ADDR, 0x1); for (i = 0; i < buf_size_in_dwords; i++) - buffer[i] = __iwl_read_prph(trans, MON_DMARB_RD_DATA_ADDR); - __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x0); + buffer[i] = iwl_read_prph_no_grab(trans, + MON_DMARB_RD_DATA_ADDR); + iwl_write_prph_no_grab(trans, MON_DMARB_RD_CTL_ADDR, 0x0); iwl_trans_release_nic_access(trans, &flags); @@ -2773,10 +2774,10 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, if (iwl_trans_grab_nic_access(trans, false, &flags)) { u32 hw_step; - hw_step = __iwl_read_prph(trans, WFPM_CTRL_REG); + hw_step = iwl_read_prph_no_grab(trans, WFPM_CTRL_REG); hw_step |= ENABLE_WFPM; - __iwl_write_prph(trans, WFPM_CTRL_REG, hw_step); - hw_step = __iwl_read_prph(trans, AUX_MISC_REG); + iwl_write_prph_no_grab(trans, WFPM_CTRL_REG, hw_step); + hw_step = iwl_read_prph_no_grab(trans, AUX_MISC_REG); hw_step = (hw_step >> HW_STEP_LOCATION_BITS) & 0xF; if (hw_step == 0x3) trans->hw_rev = (trans->hw_rev & 0xFFFFFFF3) | -- cgit v1.2.3-59-g8ed1b From 1a616dd2f17161b296aab46ee5a464ae3bf734ce Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Wed, 21 Oct 2015 16:15:38 +0300 Subject: iwlwifi: dump prph registers in a common place for all transports Currently the prph registers dump is in the transport layer, and each bus needs an additional dump implementation. Move the prph dump outside transport, and allow a common implementation for all of the buses. This is possible because prph base addresses are similar for all buses. Signed-off-by: Golan Ben-Ami Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 155 +++++++++++++++++++++- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 149 --------------------- 2 files changed, 154 insertions(+), 150 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index f570c71aeb2a..b83334f27b44 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1121,6 +1121,145 @@ void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) #define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ #define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ +static const struct { + u32 start, end; +} iwl_prph_dump_addr[] = { + { .start = 0x00a00000, .end = 0x00a00000 }, + { .start = 0x00a0000c, .end = 0x00a00024 }, + { .start = 0x00a0002c, .end = 0x00a0003c }, + { .start = 0x00a00410, .end = 0x00a00418 }, + { .start = 0x00a00420, .end = 0x00a00420 }, + { .start = 0x00a00428, .end = 0x00a00428 }, + { .start = 0x00a00430, .end = 0x00a0043c }, + { .start = 0x00a00444, .end = 0x00a00444 }, + { .start = 0x00a004c0, .end = 0x00a004cc }, + { .start = 0x00a004d8, .end = 0x00a004d8 }, + { .start = 0x00a004e0, .end = 0x00a004f0 }, + { .start = 0x00a00840, .end = 0x00a00840 }, + { .start = 0x00a00850, .end = 0x00a00858 }, + { .start = 0x00a01004, .end = 0x00a01008 }, + { .start = 0x00a01010, .end = 0x00a01010 }, + { .start = 0x00a01018, .end = 0x00a01018 }, + { .start = 0x00a01024, .end = 0x00a01024 }, + { .start = 0x00a0102c, .end = 0x00a01034 }, + { .start = 0x00a0103c, .end = 0x00a01040 }, + { .start = 0x00a01048, .end = 0x00a01094 }, + { .start = 0x00a01c00, .end = 0x00a01c20 }, + { .start = 0x00a01c58, .end = 0x00a01c58 }, + { .start = 0x00a01c7c, .end = 0x00a01c7c }, + { .start = 0x00a01c28, .end = 0x00a01c54 }, + { .start = 0x00a01c5c, .end = 0x00a01c5c }, + { .start = 0x00a01c60, .end = 0x00a01cdc }, + { .start = 0x00a01ce0, .end = 0x00a01d0c }, + { .start = 0x00a01d18, .end = 0x00a01d20 }, + { .start = 0x00a01d2c, .end = 0x00a01d30 }, + { .start = 0x00a01d40, .end = 0x00a01d5c }, + { .start = 0x00a01d80, .end = 0x00a01d80 }, + { .start = 0x00a01d98, .end = 0x00a01d9c }, + { .start = 0x00a01da8, .end = 0x00a01da8 }, + { .start = 0x00a01db8, .end = 0x00a01df4 }, + { .start = 0x00a01dc0, .end = 0x00a01dfc }, + { .start = 0x00a01e00, .end = 0x00a01e2c }, + { .start = 0x00a01e40, .end = 0x00a01e60 }, + { .start = 0x00a01e68, .end = 0x00a01e6c }, + { .start = 0x00a01e74, .end = 0x00a01e74 }, + { .start = 0x00a01e84, .end = 0x00a01e90 }, + { .start = 0x00a01e9c, .end = 0x00a01ec4 }, + { .start = 0x00a01ed0, .end = 0x00a01ee0 }, + { .start = 0x00a01f00, .end = 0x00a01f1c }, + { .start = 0x00a01f44, .end = 0x00a01ffc }, + { .start = 0x00a02000, .end = 0x00a02048 }, + { .start = 0x00a02068, .end = 0x00a020f0 }, + { .start = 0x00a02100, .end = 0x00a02118 }, + { .start = 0x00a02140, .end = 0x00a0214c }, + { .start = 0x00a02168, .end = 0x00a0218c }, + { .start = 0x00a021c0, .end = 0x00a021c0 }, + { .start = 0x00a02400, .end = 0x00a02410 }, + { .start = 0x00a02418, .end = 0x00a02420 }, + { .start = 0x00a02428, .end = 0x00a0242c }, + { .start = 0x00a02434, .end = 0x00a02434 }, + { .start = 0x00a02440, .end = 0x00a02460 }, + { .start = 0x00a02468, .end = 0x00a024b0 }, + { .start = 0x00a024c8, .end = 0x00a024cc }, + { .start = 0x00a02500, .end = 0x00a02504 }, + { .start = 0x00a0250c, .end = 0x00a02510 }, + { .start = 0x00a02540, .end = 0x00a02554 }, + { .start = 0x00a02580, .end = 0x00a025f4 }, + { .start = 0x00a02600, .end = 0x00a0260c }, + { .start = 0x00a02648, .end = 0x00a02650 }, + { .start = 0x00a02680, .end = 0x00a02680 }, + { .start = 0x00a026c0, .end = 0x00a026d0 }, + { .start = 0x00a02700, .end = 0x00a0270c }, + { .start = 0x00a02804, .end = 0x00a02804 }, + { .start = 0x00a02818, .end = 0x00a0281c }, + { .start = 0x00a02c00, .end = 0x00a02db4 }, + { .start = 0x00a02df4, .end = 0x00a02fb0 }, + { .start = 0x00a03000, .end = 0x00a03014 }, + { .start = 0x00a0301c, .end = 0x00a0302c }, + { .start = 0x00a03034, .end = 0x00a03038 }, + { .start = 0x00a03040, .end = 0x00a03048 }, + { .start = 0x00a03060, .end = 0x00a03068 }, + { .start = 0x00a03070, .end = 0x00a03074 }, + { .start = 0x00a0307c, .end = 0x00a0307c }, + { .start = 0x00a03080, .end = 0x00a03084 }, + { .start = 0x00a0308c, .end = 0x00a03090 }, + { .start = 0x00a03098, .end = 0x00a03098 }, + { .start = 0x00a030a0, .end = 0x00a030a0 }, + { .start = 0x00a030a8, .end = 0x00a030b4 }, + { .start = 0x00a030bc, .end = 0x00a030bc }, + { .start = 0x00a030c0, .end = 0x00a0312c }, + { .start = 0x00a03c00, .end = 0x00a03c5c }, + { .start = 0x00a04400, .end = 0x00a04454 }, + { .start = 0x00a04460, .end = 0x00a04474 }, + { .start = 0x00a044c0, .end = 0x00a044ec }, + { .start = 0x00a04500, .end = 0x00a04504 }, + { .start = 0x00a04510, .end = 0x00a04538 }, + { .start = 0x00a04540, .end = 0x00a04548 }, + { .start = 0x00a04560, .end = 0x00a0457c }, + { .start = 0x00a04590, .end = 0x00a04598 }, + { .start = 0x00a045c0, .end = 0x00a045f4 }, +}; + +static u32 iwl_dump_prph(struct iwl_trans *trans, + struct iwl_fw_error_dump_data **data) +{ + struct iwl_fw_error_dump_prph *prph; + unsigned long flags; + u32 prph_len = 0, i; + + if (!iwl_trans_grab_nic_access(trans, false, &flags)) + return 0; + + for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4; + int reg; + __le32 *val; + + prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk; + + (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); + (*data)->len = cpu_to_le32(sizeof(*prph) + + num_bytes_in_chunk); + prph = (void *)(*data)->data; + prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); + val = (void *)prph->data; + + for (reg = iwl_prph_dump_addr[i].start; + reg <= iwl_prph_dump_addr[i].end; + reg += 4) + *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans, + reg)); + + *data = iwl_fw_error_next_data(*data); + } + + iwl_trans_release_nic_access(trans, &flags); + + return prph_len; +} + void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) { struct iwl_fw_error_dump_file *dump_file; @@ -1134,6 +1273,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) u32 smem_len = mvm->cfg->smem_len; u32 sram2_len = mvm->cfg->dccm2_len; bool monitor_dump_only = false; + int i; lockdep_assert_held(&mvm->mutex); @@ -1166,7 +1306,6 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) /* reading RXF/TXF sizes */ if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg; - int i; fifo_data_len = 0; @@ -1219,6 +1358,17 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) sizeof(*dump_info); } + /* Make room for PRPH registers */ + for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4; + + file_len += sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_prph) + + num_bytes_in_chunk; + } + /* * In 8000 HW family B-step include the ICCM (which resides separately) */ @@ -1343,6 +1493,9 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) } } + dump_data = iwl_fw_error_next_data(dump_data); + iwl_dump_prph(mvm->trans, &dump_data); + dump_trans_data: fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans, mvm->fw_dump_trig); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 3c9035da293d..50614e4da279 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2144,144 +2144,6 @@ static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd) return cmdlen; } -static const struct { - u32 start, end; -} iwl_prph_dump_addr[] = { - { .start = 0x00a00000, .end = 0x00a00000 }, - { .start = 0x00a0000c, .end = 0x00a00024 }, - { .start = 0x00a0002c, .end = 0x00a0003c }, - { .start = 0x00a00410, .end = 0x00a00418 }, - { .start = 0x00a00420, .end = 0x00a00420 }, - { .start = 0x00a00428, .end = 0x00a00428 }, - { .start = 0x00a00430, .end = 0x00a0043c }, - { .start = 0x00a00444, .end = 0x00a00444 }, - { .start = 0x00a004c0, .end = 0x00a004cc }, - { .start = 0x00a004d8, .end = 0x00a004d8 }, - { .start = 0x00a004e0, .end = 0x00a004f0 }, - { .start = 0x00a00840, .end = 0x00a00840 }, - { .start = 0x00a00850, .end = 0x00a00858 }, - { .start = 0x00a01004, .end = 0x00a01008 }, - { .start = 0x00a01010, .end = 0x00a01010 }, - { .start = 0x00a01018, .end = 0x00a01018 }, - { .start = 0x00a01024, .end = 0x00a01024 }, - { .start = 0x00a0102c, .end = 0x00a01034 }, - { .start = 0x00a0103c, .end = 0x00a01040 }, - { .start = 0x00a01048, .end = 0x00a01094 }, - { .start = 0x00a01c00, .end = 0x00a01c20 }, - { .start = 0x00a01c58, .end = 0x00a01c58 }, - { .start = 0x00a01c7c, .end = 0x00a01c7c }, - { .start = 0x00a01c28, .end = 0x00a01c54 }, - { .start = 0x00a01c5c, .end = 0x00a01c5c }, - { .start = 0x00a01c60, .end = 0x00a01cdc }, - { .start = 0x00a01ce0, .end = 0x00a01d0c }, - { .start = 0x00a01d18, .end = 0x00a01d20 }, - { .start = 0x00a01d2c, .end = 0x00a01d30 }, - { .start = 0x00a01d40, .end = 0x00a01d5c }, - { .start = 0x00a01d80, .end = 0x00a01d80 }, - { .start = 0x00a01d98, .end = 0x00a01d9c }, - { .start = 0x00a01da8, .end = 0x00a01da8 }, - { .start = 0x00a01db8, .end = 0x00a01df4 }, - { .start = 0x00a01dc0, .end = 0x00a01dfc }, - { .start = 0x00a01e00, .end = 0x00a01e2c }, - { .start = 0x00a01e40, .end = 0x00a01e60 }, - { .start = 0x00a01e68, .end = 0x00a01e6c }, - { .start = 0x00a01e74, .end = 0x00a01e74 }, - { .start = 0x00a01e84, .end = 0x00a01e90 }, - { .start = 0x00a01e9c, .end = 0x00a01ec4 }, - { .start = 0x00a01ed0, .end = 0x00a01ee0 }, - { .start = 0x00a01f00, .end = 0x00a01f1c }, - { .start = 0x00a01f44, .end = 0x00a01ffc }, - { .start = 0x00a02000, .end = 0x00a02048 }, - { .start = 0x00a02068, .end = 0x00a020f0 }, - { .start = 0x00a02100, .end = 0x00a02118 }, - { .start = 0x00a02140, .end = 0x00a0214c }, - { .start = 0x00a02168, .end = 0x00a0218c }, - { .start = 0x00a021c0, .end = 0x00a021c0 }, - { .start = 0x00a02400, .end = 0x00a02410 }, - { .start = 0x00a02418, .end = 0x00a02420 }, - { .start = 0x00a02428, .end = 0x00a0242c }, - { .start = 0x00a02434, .end = 0x00a02434 }, - { .start = 0x00a02440, .end = 0x00a02460 }, - { .start = 0x00a02468, .end = 0x00a024b0 }, - { .start = 0x00a024c8, .end = 0x00a024cc }, - { .start = 0x00a02500, .end = 0x00a02504 }, - { .start = 0x00a0250c, .end = 0x00a02510 }, - { .start = 0x00a02540, .end = 0x00a02554 }, - { .start = 0x00a02580, .end = 0x00a025f4 }, - { .start = 0x00a02600, .end = 0x00a0260c }, - { .start = 0x00a02648, .end = 0x00a02650 }, - { .start = 0x00a02680, .end = 0x00a02680 }, - { .start = 0x00a026c0, .end = 0x00a026d0 }, - { .start = 0x00a02700, .end = 0x00a0270c }, - { .start = 0x00a02804, .end = 0x00a02804 }, - { .start = 0x00a02818, .end = 0x00a0281c }, - { .start = 0x00a02c00, .end = 0x00a02db4 }, - { .start = 0x00a02df4, .end = 0x00a02fb0 }, - { .start = 0x00a03000, .end = 0x00a03014 }, - { .start = 0x00a0301c, .end = 0x00a0302c }, - { .start = 0x00a03034, .end = 0x00a03038 }, - { .start = 0x00a03040, .end = 0x00a03048 }, - { .start = 0x00a03060, .end = 0x00a03068 }, - { .start = 0x00a03070, .end = 0x00a03074 }, - { .start = 0x00a0307c, .end = 0x00a0307c }, - { .start = 0x00a03080, .end = 0x00a03084 }, - { .start = 0x00a0308c, .end = 0x00a03090 }, - { .start = 0x00a03098, .end = 0x00a03098 }, - { .start = 0x00a030a0, .end = 0x00a030a0 }, - { .start = 0x00a030a8, .end = 0x00a030b4 }, - { .start = 0x00a030bc, .end = 0x00a030bc }, - { .start = 0x00a030c0, .end = 0x00a0312c }, - { .start = 0x00a03c00, .end = 0x00a03c5c }, - { .start = 0x00a04400, .end = 0x00a04454 }, - { .start = 0x00a04460, .end = 0x00a04474 }, - { .start = 0x00a044c0, .end = 0x00a044ec }, - { .start = 0x00a04500, .end = 0x00a04504 }, - { .start = 0x00a04510, .end = 0x00a04538 }, - { .start = 0x00a04540, .end = 0x00a04548 }, - { .start = 0x00a04560, .end = 0x00a0457c }, - { .start = 0x00a04590, .end = 0x00a04598 }, - { .start = 0x00a045c0, .end = 0x00a045f4 }, -}; - -static u32 iwl_trans_pcie_dump_prph(struct iwl_trans *trans, - struct iwl_fw_error_dump_data **data) -{ - struct iwl_fw_error_dump_prph *prph; - unsigned long flags; - u32 prph_len = 0, i; - - if (!iwl_trans_grab_nic_access(trans, false, &flags)) - return 0; - - for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4; - int reg; - __le32 *val; - - prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk; - - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); - (*data)->len = cpu_to_le32(sizeof(*prph) + - num_bytes_in_chunk); - prph = (void *)(*data)->data; - prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); - val = (void *)prph->data; - - for (reg = iwl_prph_dump_addr[i].start; - reg <= iwl_prph_dump_addr[i].end; - reg += 4) - *val++ = cpu_to_le32(iwl_trans_pcie_read_prph(trans, - reg)); - *data = iwl_fw_error_next_data(*data); - } - - iwl_trans_release_nic_access(trans, &flags); - - return prph_len; -} - static u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans, struct iwl_fw_error_dump_data **data, int allocated_rb_nums) @@ -2534,16 +2396,6 @@ static struct iwl_trans_dump_data /* CSR registers */ len += sizeof(*data) + IWL_CSR_TO_DUMP; - /* PRPH registers */ - for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4; - - len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_prph) + - num_bytes_in_chunk; - } - /* FH registers */ len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND); @@ -2591,7 +2443,6 @@ static struct iwl_trans_dump_data len += sizeof(*data); data = iwl_fw_error_next_data(data); - len += iwl_trans_pcie_dump_prph(trans, &data); len += iwl_trans_pcie_dump_csr(trans, &data); len += iwl_trans_pcie_fh_regs_dump(trans, &data); if (dump_rbs) -- cgit v1.2.3-59-g8ed1b From eb3908d3718455e9fcb0d52b391a2851900d7e27 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Fri, 2 Oct 2015 18:13:10 +0300 Subject: iwlwifi: mvm: flush all used TX queues before suspending There is a potential race condition when entering suspend with d0i3 in PCIe. If there is a frame queued just before we suspend, it won't complete and we will never clear the queue stuck timer. To solve this, call TX_PATH_FLUSH to flush all queues (except the command queue) as part of the d0i3 entry process. Add a new function that returns all the flushable queues. Signed-off-by: Luca Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 9 +++++++++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 5 +++++ 2 files changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index f28e0501c5e5..91c7480c6919 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1380,6 +1380,15 @@ void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, u8 tid, u8 flags); int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq); +/* Return a bitmask with all the hw supported queues, except for the + * command queue, which can't be flushed. + */ +static inline u32 iwl_mvm_flushable_queues(struct iwl_mvm *mvm) +{ + return ((BIT(mvm->cfg->base_params->num_of_queues) - 1) & + ~BIT(IWL_MVM_CMD_QUEUE)); +} + static inline void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, u8 fifo, u16 ssn, unsigned int wdg_timeout) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index e9b400c7ed79..f002e558fc13 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1203,6 +1203,11 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) /* make sure we have no running tx while configuring the seqno */ synchronize_net(); + /* Flush the hw queues, in case something got queued during entry */ + ret = iwl_mvm_flush_tx_path(mvm, iwl_mvm_flushable_queues(mvm), flags); + if (ret) + return ret; + /* configure wowlan configuration only if needed */ if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) { iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, -- cgit v1.2.3-59-g8ed1b From 6f7306622ff4e6b6d59efa77383e0cd9d85f46b0 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 26 Oct 2015 13:43:12 +0200 Subject: iwlwifi: mvm: remove unnecessary check in iwl_mvm_is_d0i3_supported() The d0i3_mode element is never set to IWL_D0I3_OFF, so it's not necessary to check it in iwl_mvm_is_d0i3_supported(). Signed-off-by: Luca Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 91c7480c6919..013b37920048 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -913,8 +913,7 @@ iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id) static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) { - return mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF && - !iwlwifi_mod_params.d0i3_disable && + return !iwlwifi_mod_params.d0i3_disable && fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); } -- cgit v1.2.3-59-g8ed1b From 9193adebc4d1e3138bbaf7690cfe254fb93730ec Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 10 Nov 2015 03:25:51 -0500 Subject: wcn36xx: introduce WCN36XX_HAL_AVOID_FREQ_RANGE_IND WCN3620 firmware introduces a new async indication, we need to add it as a known message type so we can accept it Signed-off-by: Andy Green Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/hal.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wcn36xx/hal.h b/drivers/net/wireless/ath/wcn36xx/hal.h index a1f1127d7808..b947de0fb2e5 100644 --- a/drivers/net/wireless/ath/wcn36xx/hal.h +++ b/drivers/net/wireless/ath/wcn36xx/hal.h @@ -345,6 +345,8 @@ enum wcn36xx_hal_host_msg_type { WCN36XX_HAL_DHCP_START_IND = 189, WCN36XX_HAL_DHCP_STOP_IND = 190, + WCN36XX_HAL_AVOID_FREQ_RANGE_IND = 233, + WCN36XX_HAL_MSG_MAX = WCN36XX_HAL_MSG_TYPE_MAX_ENUM_SIZE }; -- cgit v1.2.3-59-g8ed1b From df0d4364766fcc8f717d751e5b6cee2a56acd7b5 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 10 Nov 2015 03:25:52 -0500 Subject: wcn36xx: swallow two wcn3620 IND messages WCN3620 can asynchronously send two new kinds of indication message, since we can't handle them just accept them quietly. Signed-off-by: Andy Green Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/smd.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index c9263e1c75d4..be317f4fa716 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -2128,6 +2128,8 @@ static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len) complete(&wcn->hal_rsp_compl); break; + case WCN36XX_HAL_COEX_IND: + case WCN36XX_HAL_AVOID_FREQ_RANGE_IND: case WCN36XX_HAL_OTA_TX_COMPL_IND: case WCN36XX_HAL_MISSED_BEACON_IND: case WCN36XX_HAL_DELETE_STA_CONTEXT_IND: @@ -2174,6 +2176,9 @@ static void wcn36xx_ind_smd_work(struct work_struct *work) msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg; switch (msg_header->msg_type) { + case WCN36XX_HAL_COEX_IND: + case WCN36XX_HAL_AVOID_FREQ_RANGE_IND: + break; case WCN36XX_HAL_OTA_TX_COMPL_IND: wcn36xx_smd_tx_compl_ind(wcn, hal_ind_msg->msg, -- cgit v1.2.3-59-g8ed1b From 40ac77c8117b06501f3d190b614b008e66f68df2 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 10 Nov 2015 03:25:53 -0500 Subject: wcn36xx: handle new hal response format wcn3620 has a new message structure for the reply to some hal commands. This patch adds the struct and helper routine that uses it if the chip is wcn3620, or falls back to the old helper routine. We don't know what to do with the candidate list he sends back, but we can at least accept and ignore it nicely instead of dying. Signed-off-by: Andy Green Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/smd.c | 16 ++++++++++++++++ drivers/net/wireless/ath/wcn36xx/smd.h | 9 +++++++++ 2 files changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index be317f4fa716..b7e61a0efc1b 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -302,6 +302,22 @@ static int wcn36xx_smd_rsp_status_check(void *buf, size_t len) return 0; } +static int wcn36xx_smd_rsp_status_check_v2(struct wcn36xx *wcn, void *buf, + size_t len) +{ + struct wcn36xx_fw_msg_status_rsp_v2 *rsp; + + if (len < sizeof(struct wcn36xx_hal_msg_header) + sizeof(*rsp)) + return wcn36xx_smd_rsp_status_check(buf, len); + + rsp = buf + sizeof(struct wcn36xx_hal_msg_header); + + if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->status) + return rsp->status; + + return 0; +} + int wcn36xx_smd_load_nv(struct wcn36xx *wcn) { struct nv_data *nv_d; diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h index 008d03423dbf..8361f9e3995b 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.h +++ b/drivers/net/wireless/ath/wcn36xx/smd.h @@ -44,6 +44,15 @@ struct wcn36xx_fw_msg_status_rsp { u32 status; } __packed; +/* wcn3620 returns this for tigger_ba */ + +struct wcn36xx_fw_msg_status_rsp_v2 { + u8 bss_id[6]; + u32 status __packed; + u16 count_following_candidates __packed; + /* candidate list follows */ +}; + struct wcn36xx_hal_ind_msg { struct list_head list; u8 *msg; -- cgit v1.2.3-59-g8ed1b From 69f66b688e938acd0ff5acb77a9d8225e3051aab Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 10 Nov 2015 03:25:54 -0500 Subject: wcn36xx: use new response format for wcn3620 trigger_ba On wcn3620, firmware response to trigger_ba uses the new, larger "v2" format Signed-off-by: Andy Green Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/smd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index b7e61a0efc1b..dae5e1a51c73 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -1967,7 +1967,8 @@ int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index) wcn36xx_err("Sending hal_trigger_ba failed\n"); goto out; } - ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + ret = wcn36xx_smd_rsp_status_check_v2(wcn, wcn->hal_buf, + wcn->hal_rsp_len); if (ret) { wcn36xx_err("hal_trigger_ba response failed err=%d\n", ret); goto out; -- cgit v1.2.3-59-g8ed1b From 1d14c6f480f403cbc21c73326d84ec7ed67f7153 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Tue, 10 Nov 2015 03:25:55 -0500 Subject: wcn36xx: use new response format for wcn3620 remove_bsskey On wcn3620, firmware response to remove_bsskey uses the new, larger "v2" format Signed-off-by: Andy Green Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/smd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index dae5e1a51c73..74f56a81ad9a 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -1598,7 +1598,8 @@ int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn, wcn36xx_err("Sending hal_remove_bsskey failed\n"); goto out; } - ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + ret = wcn36xx_smd_rsp_status_check_v2(wcn, wcn->hal_buf, + wcn->hal_rsp_len); if (ret) { wcn36xx_err("hal_remove_bsskey response failed err=%d\n", ret); goto out; -- cgit v1.2.3-59-g8ed1b From e9a6ca825eb7cee946c0e7acde6fb40f4de43d3b Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 25 Nov 2015 11:32:37 +0100 Subject: brcmfmac: Cleanup ssid storage. SSIDs used for connect and p2p got stored, but never used. 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: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 41 ++++++++-------------- .../broadcom/brcm80211/brcmfmac/cfg80211.h | 2 -- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 7 +--- .../net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 20 +++++------ .../net/wireless/broadcom/brcm80211/brcmfmac/p2p.h | 2 -- 5 files changed, 24 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 998c52194b6c..49dfc00970c3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -960,7 +960,7 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, params_size += sizeof(u32) * ((request->n_channels + 1) / 2); /* Allocate space for populating ssids in struct */ - params_size += sizeof(struct brcmf_ssid) * request->n_ssids; + params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids; } params = kzalloc(params_size, GFP_KERNEL); @@ -1292,6 +1292,7 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, s32 wsec = 0; s32 bcnprd; u16 chanspec; + u32 ssid_len; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) @@ -1369,17 +1370,15 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, memset(&join_params, 0, sizeof(struct brcmf_join_params)); /* SSID */ - profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32); - memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len); - memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len); - join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); + ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN); + memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len); + join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len); join_params_size = sizeof(join_params.ssid_le); /* BSSID */ if (params->bssid) { memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN); - join_params_size = sizeof(join_params.ssid_le) + - BRCMF_ASSOC_PARAMS_FIXED_SIZE; + join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE; memcpy(profile->bssid, params->bssid, ETH_ALEN); } else { eth_broadcast_addr(join_params.params_le.bssid); @@ -1729,7 +1728,6 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; struct ieee80211_channel *chan = sme->channel; struct brcmf_join_params join_params; size_t join_params_size; @@ -1740,6 +1738,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_ext_join_params_le *ext_join_params; u16 chanspec; s32 err = 0; + u32 ssid_len; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) @@ -1825,15 +1824,6 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, goto done; } - profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID), - (u32)sme->ssid_len); - memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len); - if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { - profile->ssid.SSID[profile->ssid.SSID_len] = 0; - brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID, - profile->ssid.SSID_len); - } - /* Join with specific BSSID and cached SSID * If SSID is zero join based on BSSID only */ @@ -1846,9 +1836,12 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, err = -ENOMEM; goto done; } - ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); - memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, - profile->ssid.SSID_len); + ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN); + ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len); + memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len); + if (ssid_len < IEEE80211_MAX_SSID_LEN) + brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", + ext_join_params->ssid_le.SSID, ssid_len); /* Set up join scan parameters */ ext_join_params->scan_le.scan_type = -1; @@ -1896,8 +1889,8 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, memset(&join_params, 0, sizeof(join_params)); join_params_size = sizeof(join_params.ssid_le); - memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len); - join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); + memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len); + join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len); if (sme->bssid) memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN); @@ -2776,9 +2769,7 @@ CleanUp: static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp) { - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev); struct brcmf_bss_info_le *bi; - struct brcmf_ssid *ssid; const struct brcmf_tlv *tim; u16 beacon_interval; u8 dtim_period; @@ -2790,8 +2781,6 @@ static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, if (brcmf_is_ibssmode(ifp->vif)) return err; - ssid = &profile->ssid; - *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX); err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, cfg->extra_buf, WL_EXTRA_BUF_MAX); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 6a878c8f883f..98c8920be489 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -124,13 +124,11 @@ struct brcmf_cfg80211_security { /** * struct brcmf_cfg80211_profile - profile information. * - * @ssid: ssid of associated/associating ap. * @bssid: bssid of joined/joining ibss. * @sec: security information. * @key: key information */ struct brcmf_cfg80211_profile { - struct brcmf_ssid ssid; u8 bssid[ETH_ALEN]; struct brcmf_cfg80211_security sec; struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS]; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 92ee1ad6dd75..2bcd187adf46 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -282,14 +282,9 @@ struct brcm_rateset_le { u8 rates[BRCMF_MAXRATES_IN_SET]; }; -struct brcmf_ssid { - u32 SSID_len; - unsigned char SSID[32]; -}; - struct brcmf_ssid_le { __le32 SSID_len; - unsigned char SSID[32]; + unsigned char SSID[IEEE80211_MAX_SSID_LEN]; }; struct brcmf_scan_params_le { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index 98cf1d0f447a..a5902c383bac 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -642,7 +642,6 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans, struct brcmf_cfg80211_vif *vif; struct brcmf_p2p_scan_le *p2p_params; struct brcmf_scan_params_le *sparams; - struct brcmf_ssid ssid; memsize += num_chans * sizeof(__le16); memblk = kzalloc(memsize, GFP_KERNEL); @@ -655,16 +654,16 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans, ret = -EINVAL; goto exit; } + p2p_params = (struct brcmf_p2p_scan_le *)memblk; + sparams = &p2p_params->eparams.params_le; switch (search_state) { case WL_P2P_DISC_ST_SEARCH: /* * If we in SEARCH STATE, we don't need to set SSID explictly - * because dongle use P2P WILDCARD internally by default + * because dongle use P2P WILDCARD internally by default, use + * null ssid, which it is already due to kzalloc. */ - /* use null ssid */ - ssid.SSID_len = 0; - memset(ssid.SSID, 0, sizeof(ssid.SSID)); break; case WL_P2P_DISC_ST_SCAN: /* @@ -673,8 +672,10 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans, * P2P WILDCARD because we just do broadcast scan unless * setting SSID. */ - ssid.SSID_len = BRCMF_P2P_WILDCARD_SSID_LEN; - memcpy(ssid.SSID, BRCMF_P2P_WILDCARD_SSID, ssid.SSID_len); + sparams->ssid_le.SSID_len = + cpu_to_le32(BRCMF_P2P_WILDCARD_SSID_LEN); + memcpy(sparams->ssid_le.SSID, BRCMF_P2P_WILDCARD_SSID, + BRCMF_P2P_WILDCARD_SSID_LEN); break; default: brcmf_err(" invalid search state %d\n", search_state); @@ -687,11 +688,9 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans, /* * set p2p scan parameters. */ - p2p_params = (struct brcmf_p2p_scan_le *)memblk; p2p_params->type = 'E'; /* determine the scan engine parameters */ - sparams = &p2p_params->eparams.params_le; sparams->bss_type = DOT11_BSSTYPE_ANY; if (p2p->cfg->active_scan) sparams->scan_type = 0; @@ -699,9 +698,6 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans, sparams->scan_type = 1; eth_broadcast_addr(sparams->bssid); - if (ssid.SSID_len) - memcpy(sparams->ssid_le.SSID, ssid.SSID, ssid.SSID_len); - sparams->ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len); sparams->home_time = cpu_to_le32(P2PAPI_SCAN_HOME_TIME_MS); /* diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h index 5d49059021a9..a3bd18c2360b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h @@ -112,7 +112,6 @@ struct afx_hdl { * @int_addr: P2P interface address. * @bss_idx: informate for P2P bss types. * @listen_timer: timer for @WL_P2P_DISC_ST_LISTEN discover state. - * @ssid: ssid for P2P GO. * @listen_channel: channel for @WL_P2P_DISC_ST_LISTEN discover state. * @remain_on_channel: contains copy of struct used by cfg80211. * @remain_on_channel_cookie: cookie counter for remain on channel cmd @@ -133,7 +132,6 @@ struct brcmf_p2p_info { u8 int_addr[ETH_ALEN]; struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX]; struct timer_list listen_timer; - struct brcmf_ssid ssid; u8 listen_channel; struct ieee80211_channel remain_on_channel; u32 remain_on_channel_cookie; -- cgit v1.2.3-59-g8ed1b From 21000b3f3da45a5a33de3815f3c2b3584102960e Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 25 Nov 2015 11:32:38 +0100 Subject: brcmfmac: Return actual error by fwil. FWIL is always mapping back errors to EBADE. This is not very conventient when trying to understand problems by reading logs. Some callers print the error code, but that is quite useless when the exact error code is not returned. It also makes it impossible to differentiate based on error code. This patch changes the return of EBADE into the actual error code. 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: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c index bbf7abbf0901..f6a2df94dba7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c @@ -126,7 +126,8 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) brcmf_dbg(FIL, "Failed: %s (%d)\n", brcmf_fil_get_errstr((u32)(-err)), err); - return -EBADE; + + return err; } s32 -- cgit v1.2.3-59-g8ed1b From 6a23863eaa5153f1cd40f518eaf2bf6bf441fc42 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 25 Nov 2015 11:32:39 +0100 Subject: brcmfmac: Change error print on wlan0 existence. During initialization of the device, but also on some other moments the driver prints an error that the netdev already exists. This is a result of the way the driver is initializing the firmware and not really an error. The code is not treating it as an error either. This error print has resulted in many questions by users and is confusing and incorrect. This patch changes the error log into a debug info log. 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: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 8d16f50cea39..90389320de13 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -811,14 +811,15 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx, * in case we missed the BRCMF_E_IF_DEL event. */ if (ifp) { - brcmf_err("ERROR: netdev:%s already exists\n", - ifp->ndev->name); if (ifidx) { + brcmf_err("ERROR: netdev:%s already exists\n", + ifp->ndev->name); netif_stop_queue(ifp->ndev); brcmf_net_detach(ifp->ndev); drvr->iflist[bsscfgidx] = NULL; } else { - brcmf_err("ignore IF event\n"); + brcmf_dbg(INFO, "netdev:%s ignore IF event\n", + ifp->ndev->name); return ERR_PTR(-EINVAL); } } -- cgit v1.2.3-59-g8ed1b From 64d66c30c37e1f5de46da94d49bfae8ad2496d20 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Wed, 25 Nov 2015 11:32:40 +0100 Subject: brcmfmac: no retries on rxglom superframe errors Aborting the current read attempt on the superframe also removes the packet from the pipeline. Retries should not be attempted on the next packet since it would not be a superframe(either a superframe descriptor or other data packet) and should not be handled by brcmf_sdio_rxglom Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend Van Spriel Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/sdio.c | 30 ++++++---------------- 1 file changed, 8 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 01f359efe27e..bfadc97ab219 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -460,7 +460,6 @@ struct brcmf_sdio { struct sk_buff *glomd; /* Packet containing glomming descriptor */ struct sk_buff_head glom; /* Packet list for glommed superframe */ - uint glomerr; /* Glom packet read errors */ u8 *rxbuf; /* Buffer for receiving control packets */ uint rxblen; /* Allocated length of rxbuf */ @@ -1654,20 +1653,15 @@ static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) sdio_release_host(bus->sdiodev->func[1]); bus->sdcnt.f2rxdata++; - /* On failure, kill the superframe, allow a couple retries */ + /* On failure, kill the superframe */ if (errcode < 0) { brcmf_err("glom read of %d bytes failed: %d\n", dlen, errcode); sdio_claim_host(bus->sdiodev->func[1]); - if (bus->glomerr++ < 3) { - brcmf_sdio_rxfail(bus, true, true); - } else { - bus->glomerr = 0; - brcmf_sdio_rxfail(bus, true, false); - bus->sdcnt.rxglomfail++; - brcmf_sdio_free_glom(bus); - } + brcmf_sdio_rxfail(bus, true, false); + bus->sdcnt.rxglomfail++; + brcmf_sdio_free_glom(bus); sdio_release_host(bus->sdiodev->func[1]); return 0; } @@ -1708,19 +1702,11 @@ static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) } if (errcode) { - /* Terminate frame on error, request - a couple retries */ + /* Terminate frame on error */ sdio_claim_host(bus->sdiodev->func[1]); - if (bus->glomerr++ < 3) { - /* Restore superframe header space */ - skb_push(pfirst, sfdoff); - brcmf_sdio_rxfail(bus, true, true); - } else { - bus->glomerr = 0; - brcmf_sdio_rxfail(bus, true, false); - bus->sdcnt.rxglomfail++; - brcmf_sdio_free_glom(bus); - } + brcmf_sdio_rxfail(bus, true, false); + bus->sdcnt.rxglomfail++; + brcmf_sdio_free_glom(bus); sdio_release_host(bus->sdiodev->func[1]); bus->cur_read.len = 0; return 0; -- cgit v1.2.3-59-g8ed1b From c495810624ef5f503aa47a90a9b816d658cae90f Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 25 Nov 2015 11:32:41 +0100 Subject: brcmfmac: Remove redundant parameter action from scan. ESCAN is always performed using action start scan. No need to pass this parameter on to different functions. 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: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 6 +++--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 13 +++++-------- 3 files changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 49dfc00970c3..53831f506762 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -946,7 +946,7 @@ static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg, static s32 brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, - struct cfg80211_scan_request *request, u16 action) + struct cfg80211_scan_request *request) { s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + offsetof(struct brcmf_escan_params_le, params_le); @@ -971,7 +971,7 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN); brcmf_escan_prep(cfg, ¶ms->params_le, request); params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); - params->action = cpu_to_le16(action); + params->action = cpu_to_le16(WL_ESCAN_ACTION_START); params->sync_id = cpu_to_le16(0x1234); err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size); @@ -1013,7 +1013,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, results->count = 0; results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE; - err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START); + err = escan->run(cfg, ifp, request); if (err) brcmf_scan_config_mpc(ifp, 1); return err; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 98c8920be489..3b3096b47de9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -231,7 +231,7 @@ struct escan_info { struct wiphy *wiphy; struct brcmf_if *ifp; s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, - struct cfg80211_scan_request *request, u16 action); + struct cfg80211_scan_request *request); }; /** diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index a5902c383bac..b23dcbcd505e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -625,11 +625,10 @@ exit: * @num_chans: number of channels to scan. * @chanspecs: channel parameters for @num_chans channels. * @search_state: P2P discover state to use. - * @action: scan action to pass to firmware. * @bss_type: type of P2P bss. */ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans, - u16 chanspecs[], s32 search_state, u16 action, + u16 chanspecs[], s32 search_state, enum p2p_bss_type bss_type) { s32 ret = 0; @@ -738,7 +737,7 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans, /* set the escan specific parameters */ p2p_params->eparams.version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); - p2p_params->eparams.action = cpu_to_le16(action); + p2p_params->eparams.action = cpu_to_le16(WL_ESCAN_ACTION_START); p2p_params->eparams.sync_id = cpu_to_le16(0x1234); /* perform p2p scan on primary device */ ret = brcmf_fil_bsscfg_data_set(vif->ifp, "p2p_scan", memblk, memsize); @@ -762,8 +761,7 @@ exit: */ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, - struct cfg80211_scan_request *request, - u16 action) + struct cfg80211_scan_request *request) { struct brcmf_p2p_info *p2p = &cfg->p2p; s32 err = 0; @@ -823,7 +821,7 @@ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg, num_nodfs++; } err = brcmf_p2p_escan(p2p, num_nodfs, chanspecs, search_state, - action, P2PAPI_BSSCFG_DEVICE); + P2PAPI_BSSCFG_DEVICE); kfree(chanspecs); } exit: @@ -1092,8 +1090,7 @@ static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel) default_chan_list[2] = ch.chspec; } err = brcmf_p2p_escan(p2p, channel_cnt, default_chan_list, - WL_P2P_DISC_ST_SEARCH, WL_ESCAN_ACTION_START, - P2PAPI_BSSCFG_DEVICE); + WL_P2P_DISC_ST_SEARCH, P2PAPI_BSSCFG_DEVICE); kfree(default_chan_list); exit: return err; -- cgit v1.2.3-59-g8ed1b From 1119e23edf256f4b4a3655f50b31ec6406f16026 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 25 Nov 2015 11:32:42 +0100 Subject: brcmfmac: Cleanup roaming configuration. Put all roaming configuration related code in one place and configure timeout based upon roaming setting. 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: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 41 ++++++++++------------ .../broadcom/brcm80211/brcmfmac/cfg80211.h | 6 ++-- .../wireless/broadcom/brcm80211/brcmfmac/common.c | 21 ----------- 3 files changed, 22 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 53831f506762..ef0f88233d36 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -4756,7 +4756,6 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, vif->wdev.iftype = type; vif->pm_block = pm_block; - vif->roam_off = -1; brcmf_init_prof(&vif->profile); @@ -5306,35 +5305,33 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event) mutex_init(&event->vif_event_lock); } -static s32 -brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) +static s32 brcmf_dongle_roam(struct brcmf_if *ifp) { - s32 err = 0; + s32 err; + u32 bcn_timeout; __le32 roamtrigger[2]; __le32 roam_delta[2]; - /* - * Setup timeout if Beacons are lost and roam is - * off to report link down - */ - if (brcmf_roamoff) { - err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout); - if (err) { - brcmf_err("bcn_timeout error (%d)\n", err); - goto dongle_rom_out; - } + /* Configure beacon timeout value based upon roaming setting */ + if (brcmf_roamoff) + bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF; + else + bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON; + err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout); + if (err) { + brcmf_err("bcn_timeout error (%d)\n", err); + goto roam_setup_done; } - /* - * Enable/Disable built-in roaming to allow supplicant - * to take care of roaming + /* Enable/Disable built-in roaming to allow supplicant to take care of + * roaming. */ brcmf_dbg(INFO, "Internal Roaming = %s\n", brcmf_roamoff ? "Off" : "On"); err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff)); if (err) { brcmf_err("roam_off error (%d)\n", err); - goto dongle_rom_out; + goto roam_setup_done; } roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL); @@ -5343,7 +5340,7 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) (void *)roamtrigger, sizeof(roamtrigger)); if (err) { brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err); - goto dongle_rom_out; + goto roam_setup_done; } roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA); @@ -5352,10 +5349,10 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) (void *)roam_delta, sizeof(roam_delta)); if (err) { brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err); - goto dongle_rom_out; + goto roam_setup_done; } -dongle_rom_out: +roam_setup_done: return err; } @@ -6070,7 +6067,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) brcmf_dbg(INFO, "power save set to %s\n", (power_mode ? "enabled" : "disabled")); - err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT); + err = brcmf_dongle_roam(ifp); if (err) goto default_conf_out; err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 3b3096b47de9..d492163e4de9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -28,7 +28,6 @@ #define WL_EXTRA_BUF_MAX 2048 #define WL_ROAM_TRIGGER_LEVEL -75 #define WL_ROAM_DELTA 20 -#define WL_BEACON_TIMEOUT 3 #define WL_SCAN_CHANNEL_TIME 40 #define WL_SCAN_UNASSOC_TIME 40 @@ -77,6 +76,9 @@ #define BRCMF_MAX_DEFAULT_KEYS 4 +/* beacon loss timeout defaults */ +#define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON 2 +#define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF 4 /** * enum brcmf_scan_status - scan engine status @@ -178,7 +180,6 @@ struct vif_saved_ie { * @ifp: lower layer interface pointer * @wdev: wireless device. * @profile: profile information. - * @roam_off: roaming state. * @sme_state: SME state using enum brcmf_vif_status bits. * @pm_block: power-management blocked. * @list: linked list. @@ -189,7 +190,6 @@ struct brcmf_cfg80211_vif { struct brcmf_if *ifp; struct wireless_dev wdev; struct brcmf_cfg80211_profile profile; - s32 roam_off; unsigned long sme_state; bool pm_block; struct vif_saved_ie saved_ie; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index fe54844c75e0..59beed266dd3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -29,7 +29,6 @@ const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; -#define BRCMF_DEFAULT_BCN_TIMEOUT 3 #define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 @@ -107,26 +106,6 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) goto done; } - /* - * Setup timeout if Beacons are lost and roam is off to report - * link down - */ - err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", - BRCMF_DEFAULT_BCN_TIMEOUT); - if (err) { - brcmf_err("bcn_timeout error (%d)\n", err); - goto done; - } - - /* Enable/Disable build-in roaming to allowed ext supplicant to take - * of romaing - */ - err = brcmf_fil_iovar_int_set(ifp, "roam_off", 1); - if (err) { - brcmf_err("roam_off error (%d)\n", err); - goto done; - } - /* Setup join_pref to select target by RSSI(with boost on 5GHz) */ join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA; join_pref_params[0].len = 2; -- cgit v1.2.3-59-g8ed1b From 7bf65aa9ad3f3ebc1f7c889bb1bb005fdc8a07ec Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 25 Nov 2015 11:32:43 +0100 Subject: brcmfmac: Add beamforming support. Some devices support beamforming. This patch enables tx beamforming if supported and reports beamforming capabilities per channel if supported. 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: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 36 ++++++++++++++++++++-- .../wireless/broadcom/brcm80211/brcmfmac/common.c | 3 ++ .../broadcom/brcm80211/brcmfmac/fwil_types.h | 5 +++ 3 files changed, 42 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index ef0f88233d36..771c5819a6e3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -5672,7 +5672,8 @@ static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp) } static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, - u32 bw_cap[2], u32 nchain) + u32 bw_cap[2], u32 nchain, u32 txstreams, + u32 txbf_bfe_cap, u32 txbf_bfr_cap) { __le16 mcs_map; @@ -5691,6 +5692,25 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9); band->vht_cap.vht_mcs.rx_mcs_map = mcs_map; band->vht_cap.vht_mcs.tx_mcs_map = mcs_map; + + /* Beamforming support information */ + if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP) + band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; + if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP) + band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; + if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP) + band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; + if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP) + band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; + + if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) { + band->vht_cap.cap |= + (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT); + band->vht_cap.cap |= ((txstreams - 1) << + IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT); + band->vht_cap.cap |= + IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB; + } } static int brcmf_setup_wiphybands(struct wiphy *wiphy) @@ -5705,6 +5725,9 @@ static int brcmf_setup_wiphybands(struct wiphy *wiphy) int err; s32 i; struct ieee80211_supported_band *band; + u32 txstreams = 0; + u32 txbf_bfe_cap = 0; + u32 txbf_bfr_cap = 0; (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); @@ -5733,6 +5756,14 @@ static int brcmf_setup_wiphybands(struct wiphy *wiphy) return err; } + if (vhtmode) { + (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams); + (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap", + &txbf_bfe_cap); + (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap", + &txbf_bfr_cap); + } + wiphy = cfg_to_wiphy(cfg); for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) { band = wiphy->bands[i]; @@ -5742,7 +5773,8 @@ static int brcmf_setup_wiphybands(struct wiphy *wiphy) if (nmode) brcmf_update_ht_cap(band, bw_cap, nchain); if (vhtmode) - brcmf_update_vht_cap(band, bw_cap, nchain); + brcmf_update_vht_cap(band, bw_cap, nchain, txstreams, + txbf_bfe_cap, txbf_bfr_cap); } return 0; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 59beed266dd3..474de118d0b4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -153,6 +153,9 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) goto done; } + /* Enable tx beamforming, errors can be ignored (not supported) */ + (void)brcmf_fil_iovar_int_set(ifp, "txbf", 1); + /* do bus specific preinit here */ err = brcmf_bus_preinit(ifp->drvr->bus_if); done: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 2bcd187adf46..18483e782bfe 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -121,6 +121,11 @@ #define BRCMF_MAX_ASSOCLIST 128 +#define BRCMF_TXBF_SU_BFE_CAP BIT(0) +#define BRCMF_TXBF_MU_BFE_CAP BIT(1) +#define BRCMF_TXBF_SU_BFR_CAP BIT(0) +#define BRCMF_TXBF_MU_BFR_CAP BIT(1) + /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { BRCMF_JOIN_PREF_RSSI = 1, -- cgit v1.2.3-59-g8ed1b From b4fd63c608a2d0c74730d71c8f620604b4b86ef9 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 25 Nov 2015 11:32:45 +0100 Subject: brcmfmac: assure net_ratelimit() is declared before use Under some kernel configuration we get build issue with implicit declaration of net_ratelimit() function. Fix this by explicitly including the file providing the prototype. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h index d0d9676f7f9d..6687812770cc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h @@ -17,6 +17,8 @@ #ifndef BRCMFMAC_DEBUG_H #define BRCMFMAC_DEBUG_H +#include /* net_ratelimit() */ + /* message levels */ #define BRCMF_TRACE_VAL 0x00000002 #define BRCMF_INFO_VAL 0x00000004 -- cgit v1.2.3-59-g8ed1b From 46d703a775394e4724509ff55cdda41d228c028c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 25 Nov 2015 11:32:46 +0100 Subject: brcmfmac: Unify methods to define and map firmware files. All bus drivers (sdio, usb and pcie) require firmware files which needs to be downloaded to the device, The definitions and mapping of device id and revision to firmware and nvram file is done by each bus driver. This patch creates common functions and defines to simplify and unify the definition of these firmware and nvram files and mapping. 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: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/firmware.c | 44 +++++- .../broadcom/brcm80211/brcmfmac/firmware.h | 46 +++++- .../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 149 ++++-------------- .../wireless/broadcom/brcm80211/brcmfmac/sdio.c | 166 +++++---------------- .../wireless/broadcom/brcm80211/brcmfmac/sdio.h | 4 +- .../net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 86 ++++------- 6 files changed, 178 insertions(+), 317 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index 33afb9aafa9b..1e4d5f663036 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -28,9 +28,9 @@ #define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */ #define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */ -char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; +static char brcmf_firmware_path[BRCMF_FW_NAME_LEN]; module_param_string(alternative_fw_path, brcmf_firmware_path, - BRCMF_FW_PATH_LEN, 0440); + BRCMF_FW_NAME_LEN, 0440); enum nvram_parser_state { IDLE, @@ -539,3 +539,43 @@ int brcmf_fw_get_firmwares(struct device *dev, u16 flags, 0); } +int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev, + struct brcmf_firmware_mapping mapping_table[], + u32 table_size, char fw_name[BRCMF_FW_NAME_LEN], + char nvram_name[BRCMF_FW_NAME_LEN]) +{ + u32 i; + char end; + + for (i = 0; i < table_size; i++) { + if (mapping_table[i].chipid == chip && + mapping_table[i].revmask & BIT(chiprev)) + break; + } + + if (i == table_size) { + brcmf_err("Unknown chipid %d [%d]\n", chip, chiprev); + return -ENODEV; + } + + /* check if firmware path is provided by module parameter */ + if (brcmf_firmware_path[0] != '\0') { + strlcpy(fw_name, brcmf_firmware_path, BRCMF_FW_NAME_LEN); + if ((nvram_name) && (mapping_table[i].nvram)) + strlcpy(nvram_name, brcmf_firmware_path, + BRCMF_FW_NAME_LEN); + + end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; + if (end != '/') { + strlcat(fw_name, "/", BRCMF_FW_NAME_LEN); + if ((nvram_name) && (mapping_table[i].nvram)) + strlcat(nvram_name, "/", BRCMF_FW_NAME_LEN); + } + } + strlcat(fw_name, mapping_table[i].fw, BRCMF_FW_NAME_LEN); + if ((nvram_name) && (mapping_table[i].nvram)) + strlcat(nvram_name, mapping_table[i].nvram, BRCMF_FW_NAME_LEN); + + return 0; +} + diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h index 604dd48ab4e0..ef06f57a7a0e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h @@ -21,11 +21,51 @@ #define BRCMF_FW_REQ_FLAGS 0x00F0 #define BRCMF_FW_REQ_NV_OPTIONAL 0x0010 -#define BRCMF_FW_PATH_LEN 256 -#define BRCMF_FW_NAME_LEN 32 +#define BRCMF_FW_NAME_LEN 320 -extern char brcmf_firmware_path[]; +#define BRCMF_FW_DEFAULT_PATH "brcm/" +/** + * struct brcmf_firmware_mapping - Used to map chipid/revmask to firmware + * filename and nvram filename. Each bus type implementation should create + * a table of firmware mappings (using the macros defined below). + * + * @chipid: ID of chip. + * @revmask: bitmask of revisions, e.g. 0x10 means rev 4 only, 0xf means rev 0-3 + * @fw: name of the firmware file. + * @nvram: name of nvram file. + */ +struct brcmf_firmware_mapping { + u32 chipid; + u32 revmask; + const char *fw; + const char *nvram; +}; + +#define BRCMF_FW_NVRAM_DEF(fw_nvram_name, fw, nvram) \ +static const char BRCM_ ## fw_nvram_name ## _FIRMWARE_NAME[] = \ + BRCMF_FW_DEFAULT_PATH fw; \ +static const char BRCM_ ## fw_nvram_name ## _NVRAM_NAME[] = \ + BRCMF_FW_DEFAULT_PATH nvram; \ +MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw); \ +MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH nvram) + +#define BRCMF_FW_DEF(fw_name, fw) \ +static const char BRCM_ ## fw_name ## _FIRMWARE_NAME[] = \ + BRCMF_FW_DEFAULT_PATH fw; \ +MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw) \ + +#define BRCMF_FW_NVRAM_ENTRY(chipid, mask, name) \ + { chipid, mask, \ + BRCM_ ## name ## _FIRMWARE_NAME, BRCM_ ## name ## _NVRAM_NAME } + +#define BRCMF_FW_ENTRY(chipid, mask, name) \ + { chipid, mask, BRCM_ ## name ## _FIRMWARE_NAME, NULL } + +int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev, + struct brcmf_firmware_mapping mapping_table[], + u32 table_size, char fw_name[BRCMF_FW_NAME_LEN], + char nvram_name[BRCMF_FW_NAME_LEN]); void brcmf_fw_nvram_free(void *nvram); /* * Request firmware(s) asynchronously. When the asynchronous request diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index bbf414494ca9..ace0404817dc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -44,25 +44,29 @@ enum brcmf_pcie_state { BRCMFMAC_PCIE_STATE_UP }; - -#define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin" -#define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt" -#define BRCMF_PCIE_4350_FW_NAME "brcm/brcmfmac4350-pcie.bin" -#define BRCMF_PCIE_4350_NVRAM_NAME "brcm/brcmfmac4350-pcie.txt" -#define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin" -#define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt" -#define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin" -#define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt" -#define BRCMF_PCIE_4358_FW_NAME "brcm/brcmfmac4358-pcie.bin" -#define BRCMF_PCIE_4358_NVRAM_NAME "brcm/brcmfmac4358-pcie.txt" -#define BRCMF_PCIE_4359_FW_NAME "brcm/brcmfmac4359-pcie.bin" -#define BRCMF_PCIE_4359_NVRAM_NAME "brcm/brcmfmac4359-pcie.txt" -#define BRCMF_PCIE_4365_FW_NAME "brcm/brcmfmac4365b-pcie.bin" -#define BRCMF_PCIE_4365_NVRAM_NAME "brcm/brcmfmac4365b-pcie.txt" -#define BRCMF_PCIE_4366_FW_NAME "brcm/brcmfmac4366b-pcie.bin" -#define BRCMF_PCIE_4366_NVRAM_NAME "brcm/brcmfmac4366b-pcie.txt" -#define BRCMF_PCIE_4371_FW_NAME "brcm/brcmfmac4371-pcie.bin" -#define BRCMF_PCIE_4371_NVRAM_NAME "brcm/brcmfmac4371-pcie.txt" +BRCMF_FW_NVRAM_DEF(43602, "brcmfmac43602-pcie.bin", "brcmfmac43602-pcie.txt"); +BRCMF_FW_NVRAM_DEF(4350, "brcmfmac4350-pcie.bin", "brcmfmac4350-pcie.txt"); +BRCMF_FW_NVRAM_DEF(4356, "brcmfmac4356-pcie.bin", "brcmfmac4356-pcie.txt"); +BRCMF_FW_NVRAM_DEF(43570, "brcmfmac43570-pcie.bin", "brcmfmac43570-pcie.txt"); +BRCMF_FW_NVRAM_DEF(4358, "brcmfmac4358-pcie.bin", "brcmfmac4358-pcie.txt"); +BRCMF_FW_NVRAM_DEF(4359, "brcmfmac4359-pcie.bin", "brcmfmac4359-pcie.txt"); +BRCMF_FW_NVRAM_DEF(4365B, "brcmfmac4365b-pcie.bin", "brcmfmac4365b-pcie.txt"); +BRCMF_FW_NVRAM_DEF(4366B, "brcmfmac4366b-pcie.bin", "brcmfmac4366b-pcie.txt"); +BRCMF_FW_NVRAM_DEF(4371, "brcmfmac4371-pcie.bin", "brcmfmac4371-pcie.txt"); + +static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0xFFFFFFFF, 4350), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43567_CHIP_ID, 0xFFFFFFFF, 43570), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43570), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43570_CHIP_ID, 0xFFFFFFFF, 43570), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4358_CHIP_ID, 0xFFFFFFFF, 4358), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4365_CHIP_ID, 0xFFFFFFFF, 4365B), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFFF, 4366B), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371), +}; #define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ @@ -202,26 +206,6 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3 -MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4350_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4350_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4359_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4359_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4365_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4365_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4366_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4366_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4371_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4371_NVRAM_NAME); - - struct brcmf_pcie_console { u32 base_addr; u32 buf_addr; @@ -258,8 +242,8 @@ struct brcmf_pciedev_info { enum brcmf_pcie_state state; bool in_irq; struct pci_dev *pdev; - char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; - char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + char fw_name[BRCMF_FW_NAME_LEN]; + char nvram_name[BRCMF_FW_NAME_LEN]; void __iomem *regs; void __iomem *tcm; u32 tcm_size; @@ -1478,84 +1462,6 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, } -static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) -{ - char *fw_name; - char *nvram_name; - uint fw_len, nv_len; - char end; - - brcmf_dbg(PCIE, "Enter, chip 0x%04x chiprev %d\n", devinfo->ci->chip, - devinfo->ci->chiprev); - - switch (devinfo->ci->chip) { - case BRCM_CC_43602_CHIP_ID: - fw_name = BRCMF_PCIE_43602_FW_NAME; - nvram_name = BRCMF_PCIE_43602_NVRAM_NAME; - break; - case BRCM_CC_4350_CHIP_ID: - fw_name = BRCMF_PCIE_4350_FW_NAME; - nvram_name = BRCMF_PCIE_4350_NVRAM_NAME; - break; - case BRCM_CC_4356_CHIP_ID: - fw_name = BRCMF_PCIE_4356_FW_NAME; - nvram_name = BRCMF_PCIE_4356_NVRAM_NAME; - break; - case BRCM_CC_43567_CHIP_ID: - case BRCM_CC_43569_CHIP_ID: - case BRCM_CC_43570_CHIP_ID: - fw_name = BRCMF_PCIE_43570_FW_NAME; - nvram_name = BRCMF_PCIE_43570_NVRAM_NAME; - break; - case BRCM_CC_4358_CHIP_ID: - fw_name = BRCMF_PCIE_4358_FW_NAME; - nvram_name = BRCMF_PCIE_4358_NVRAM_NAME; - break; - case BRCM_CC_4359_CHIP_ID: - fw_name = BRCMF_PCIE_4359_FW_NAME; - nvram_name = BRCMF_PCIE_4359_NVRAM_NAME; - break; - case BRCM_CC_4365_CHIP_ID: - fw_name = BRCMF_PCIE_4365_FW_NAME; - nvram_name = BRCMF_PCIE_4365_NVRAM_NAME; - break; - case BRCM_CC_4366_CHIP_ID: - fw_name = BRCMF_PCIE_4366_FW_NAME; - nvram_name = BRCMF_PCIE_4366_NVRAM_NAME; - break; - case BRCM_CC_4371_CHIP_ID: - fw_name = BRCMF_PCIE_4371_FW_NAME; - nvram_name = BRCMF_PCIE_4371_NVRAM_NAME; - break; - default: - brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip); - return -ENODEV; - } - - fw_len = sizeof(devinfo->fw_name) - 1; - nv_len = sizeof(devinfo->nvram_name) - 1; - /* check if firmware path is provided by module parameter */ - if (brcmf_firmware_path[0] != '\0') { - strncpy(devinfo->fw_name, brcmf_firmware_path, fw_len); - strncpy(devinfo->nvram_name, brcmf_firmware_path, nv_len); - fw_len -= strlen(devinfo->fw_name); - nv_len -= strlen(devinfo->nvram_name); - - end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; - if (end != '/') { - strncat(devinfo->fw_name, "/", fw_len); - strncat(devinfo->nvram_name, "/", nv_len); - fw_len--; - nv_len--; - } - } - strncat(devinfo->fw_name, fw_name, fw_len); - strncat(devinfo->nvram_name, nvram_name, nv_len); - - return 0; -} - - static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, const struct firmware *fw, void *nvram, u32 nvram_len) @@ -1891,7 +1797,10 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot); dev_set_drvdata(&pdev->dev, bus); - ret = brcmf_pcie_get_fwnames(devinfo); + ret = brcmf_fw_map_chip_to_name(devinfo->ci->chip, devinfo->ci->chiprev, + brcmf_pcie_fwnames, + ARRAY_SIZE(brcmf_pcie_fwnames), + devinfo->fw_name, devinfo->nvram_name); if (ret) goto fail_bus; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index bfadc97ab219..9fa3a3a0f12c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -596,136 +596,41 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { {4, 0x1} }; -#define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin" -#define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt" -#define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin" -#define BCM43241B0_NVRAM_NAME "brcm/brcmfmac43241b0-sdio.txt" -#define BCM43241B4_FIRMWARE_NAME "brcm/brcmfmac43241b4-sdio.bin" -#define BCM43241B4_NVRAM_NAME "brcm/brcmfmac43241b4-sdio.txt" -#define BCM43241B5_FIRMWARE_NAME "brcm/brcmfmac43241b5-sdio.bin" -#define BCM43241B5_NVRAM_NAME "brcm/brcmfmac43241b5-sdio.txt" -#define BCM4329_FIRMWARE_NAME "brcm/brcmfmac4329-sdio.bin" -#define BCM4329_NVRAM_NAME "brcm/brcmfmac4329-sdio.txt" -#define BCM4330_FIRMWARE_NAME "brcm/brcmfmac4330-sdio.bin" -#define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt" -#define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin" -#define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt" -#define BCM43340_FIRMWARE_NAME "brcm/brcmfmac43340-sdio.bin" -#define BCM43340_NVRAM_NAME "brcm/brcmfmac43340-sdio.txt" -#define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin" -#define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt" -#define BCM43362_FIRMWARE_NAME "brcm/brcmfmac43362-sdio.bin" -#define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" -#define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" -#define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" -#define BCM43430_FIRMWARE_NAME "brcm/brcmfmac43430-sdio.bin" -#define BCM43430_NVRAM_NAME "brcm/brcmfmac43430-sdio.txt" -#define BCM43455_FIRMWARE_NAME "brcm/brcmfmac43455-sdio.bin" -#define BCM43455_NVRAM_NAME "brcm/brcmfmac43455-sdio.txt" -#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" -#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" - -MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43143_NVRAM_NAME); -MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); -MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); -MODULE_FIRMWARE(BCM43241B5_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43241B5_NVRAM_NAME); -MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4329_NVRAM_NAME); -MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4330_NVRAM_NAME); -MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4334_NVRAM_NAME); -MODULE_FIRMWARE(BCM43340_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43340_NVRAM_NAME); -MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4335_NVRAM_NAME); -MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43362_NVRAM_NAME); -MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4339_NVRAM_NAME); -MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43430_NVRAM_NAME); -MODULE_FIRMWARE(BCM43455_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43455_NVRAM_NAME); -MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4354_NVRAM_NAME); - -struct brcmf_firmware_names { - u32 chipid; - u32 revmsk; - const char *bin; - const char *nv; +BRCMF_FW_NVRAM_DEF(43143, "brcmfmac43143-sdio.bin", "brcmfmac43143-sdio.txt"); +BRCMF_FW_NVRAM_DEF(43241B0, "brcmfmac43241b0-sdio.bin", + "brcmfmac43241b0-sdio.txt"); +BRCMF_FW_NVRAM_DEF(43241B4, "brcmfmac43241b4-sdio.bin", + "brcmfmac43241b4-sdio.txt"); +BRCMF_FW_NVRAM_DEF(43241B5, "brcmfmac43241b5-sdio.bin", + "brcmfmac43241b5-sdio.txt"); +BRCMF_FW_NVRAM_DEF(4329, "brcmfmac4329-sdio.bin", "brcmfmac4329-sdio.txt"); +BRCMF_FW_NVRAM_DEF(4330, "brcmfmac4330-sdio.bin", "brcmfmac4330-sdio.txt"); +BRCMF_FW_NVRAM_DEF(4334, "brcmfmac4334-sdio.bin", "brcmfmac4334-sdio.txt"); +BRCMF_FW_NVRAM_DEF(43340, "brcmfmac43340-sdio.bin", "brcmfmac43340-sdio.txt"); +BRCMF_FW_NVRAM_DEF(4335, "brcmfmac4335-sdio.bin", "brcmfmac4335-sdio.txt"); +BRCMF_FW_NVRAM_DEF(43362, "brcmfmac43362-sdio.bin", "brcmfmac43362-sdio.txt"); +BRCMF_FW_NVRAM_DEF(4339, "brcmfmac4339-sdio.bin", "brcmfmac4339-sdio.txt"); +BRCMF_FW_NVRAM_DEF(43430, "brcmfmac43430-sdio.bin", "brcmfmac43430-sdio.txt"); +BRCMF_FW_NVRAM_DEF(43455, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt"); +BRCMF_FW_NVRAM_DEF(4354, "brcmfmac4354-sdio.bin", "brcmfmac4354-sdio.txt"); + +static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, 43241B5), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, 4329), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, 4330), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, 4334), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, 43340), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, 4335), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, 43430), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, 43455), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354) }; -enum brcmf_firmware_type { - BRCMF_FIRMWARE_BIN, - BRCMF_FIRMWARE_NVRAM -}; - -#define BRCMF_FIRMWARE_NVRAM(name) \ - name ## _FIRMWARE_NAME, name ## _NVRAM_NAME - -static const struct brcmf_firmware_names brcmf_fwname_data[] = { - { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, - { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, - { BRCM_CC_43241_CHIP_ID, 0x00000020, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, - { BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43241B5) }, - { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, - { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, - { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, - { BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43340) }, - { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, - { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, - { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, - { BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) }, - { BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43455) }, - { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } -}; - -static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, - struct brcmf_sdio_dev *sdiodev) -{ - int i; - char end; - - for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { - if (brcmf_fwname_data[i].chipid == ci->chip && - brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) - break; - } - - if (i == ARRAY_SIZE(brcmf_fwname_data)) { - brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev); - return -ENODEV; - } - - /* check if firmware path is provided by module parameter */ - if (brcmf_firmware_path[0] != '\0') { - strlcpy(sdiodev->fw_name, brcmf_firmware_path, - sizeof(sdiodev->fw_name)); - strlcpy(sdiodev->nvram_name, brcmf_firmware_path, - sizeof(sdiodev->nvram_name)); - - end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; - if (end != '/') { - strlcat(sdiodev->fw_name, "/", - sizeof(sdiodev->fw_name)); - strlcat(sdiodev->nvram_name, "/", - sizeof(sdiodev->nvram_name)); - } - } - strlcat(sdiodev->fw_name, brcmf_fwname_data[i].bin, - sizeof(sdiodev->fw_name)); - strlcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, - sizeof(sdiodev->nvram_name)); - - return 0; -} - static void pkt_align(struct sk_buff *p, int len, int align) { uint datalign; @@ -4252,7 +4157,10 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) brcmf_sdio_debugfs_create(bus); brcmf_dbg(INFO, "completed!!\n"); - ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev); + ret = brcmf_fw_map_chip_to_name(bus->ci->chip, bus->ci->chiprev, + brcmf_sdio_fwnames, + ARRAY_SIZE(brcmf_sdio_fwnames), + sdiodev->fw_name, sdiodev->nvram_name); if (ret) goto fail; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index 7328478b2d7b..d86ecf26f31a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -195,8 +195,8 @@ struct brcmf_sdio_dev { uint max_segment_size; uint txglomsz; struct sg_table sgtable; - char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; - char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + char fw_name[BRCMF_FW_NAME_LEN]; + char nvram_name[BRCMF_FW_NAME_LEN]; bool wowl_enabled; enum brcmf_sdiod_state state; struct brcmf_sdiod_freezer *freezer; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 978d14c09532..ccde5599c401 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -43,10 +43,20 @@ #define BRCMF_USB_CBCTL_READ 1 #define BRCMF_USB_MAX_PKT_SIZE 1600 -#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin" -#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" -#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" -#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin" +BRCMF_FW_DEF(43143, "brcmfmac43143.bin"); +BRCMF_FW_DEF(43236B, "brcmfmac43236b.bin"); +BRCMF_FW_DEF(43242A, "brcmfmac43242a.bin"); +BRCMF_FW_DEF(43569, "brcmfmac43569.bin"); + +static struct brcmf_firmware_mapping brcmf_usb_fwnames[] = { + BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), + BRCMF_FW_ENTRY(BRCM_CC_43235_CHIP_ID, 0x00000008, 43236B), + BRCMF_FW_ENTRY(BRCM_CC_43236_CHIP_ID, 0x00000008, 43236B), + BRCMF_FW_ENTRY(BRCM_CC_43238_CHIP_ID, 0x00000008, 43236B), + BRCMF_FW_ENTRY(BRCM_CC_43242_CHIP_ID, 0xFFFFFFFF, 43242A), + BRCMF_FW_ENTRY(BRCM_CC_43566_CHIP_ID, 0xFFFFFFFF, 43569), + BRCMF_FW_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43569) +}; #define TRX_MAGIC 0x30524448 /* "HDR0" */ #define TRX_MAX_OFFSET 3 /* Max number of file offsets */ @@ -139,6 +149,7 @@ struct brcmf_usbdev_info { struct brcmf_usbreq *tx_reqs; struct brcmf_usbreq *rx_reqs; + char fw_name[BRCMF_FW_NAME_LEN]; const u8 *image; /* buffer for combine fw and nvram */ int image_len; @@ -983,45 +994,15 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo) return 0; } -static bool brcmf_usb_chip_support(int chipid, int chiprev) -{ - switch(chipid) { - case BRCM_CC_43143_CHIP_ID: - return true; - case BRCM_CC_43235_CHIP_ID: - case BRCM_CC_43236_CHIP_ID: - case BRCM_CC_43238_CHIP_ID: - return (chiprev == 3); - case BRCM_CC_43242_CHIP_ID: - return true; - case BRCM_CC_43566_CHIP_ID: - case BRCM_CC_43569_CHIP_ID: - return true; - default: - break; - } - return false; -} - static int brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo) { - int devid, chiprev; int err; brcmf_dbg(USB, "Enter\n"); if (devinfo == NULL) return -ENODEV; - devid = devinfo->bus_pub.devid; - chiprev = devinfo->bus_pub.chiprev; - - if (!brcmf_usb_chip_support(devid, chiprev)) { - brcmf_err("unsupported chip %d rev %d\n", - devid, chiprev); - return -EINVAL; - } - if (!devinfo->image) { brcmf_err("No firmware!\n"); return -ENOENT; @@ -1071,25 +1052,6 @@ static int check_file(const u8 *headers) return -1; } -static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) -{ - switch (devinfo->bus_pub.devid) { - case BRCM_CC_43143_CHIP_ID: - return BRCMF_USB_43143_FW_NAME; - case BRCM_CC_43235_CHIP_ID: - case BRCM_CC_43236_CHIP_ID: - case BRCM_CC_43238_CHIP_ID: - return BRCMF_USB_43236_FW_NAME; - case BRCM_CC_43242_CHIP_ID: - return BRCMF_USB_43242_FW_NAME; - case BRCM_CC_43566_CHIP_ID: - case BRCM_CC_43569_CHIP_ID: - return BRCMF_USB_43569_FW_NAME; - default: - return NULL; - } -} - static struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, @@ -1274,9 +1236,16 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->chip = bus_pub->devid; bus->chiprev = bus_pub->chiprev; + ret = brcmf_fw_map_chip_to_name(bus_pub->devid, bus_pub->chiprev, + brcmf_usb_fwnames, + ARRAY_SIZE(brcmf_usb_fwnames), + devinfo->fw_name, NULL); + if (ret) + goto fail; + /* request firmware here */ - ret = brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), - NULL, brcmf_usb_probe_phase2); + ret = brcmf_fw_get_firmwares(dev, 0, devinfo->fw_name, NULL, + brcmf_usb_probe_phase2); if (ret) { brcmf_err("firmware request failed: %d\n", ret); goto fail; @@ -1472,8 +1441,7 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) brcmf_dbg(USB, "Enter\n"); - return brcmf_fw_get_firmwares(&usb->dev, 0, - brcmf_usb_get_fwname(devinfo), NULL, + return brcmf_fw_get_firmwares(&usb->dev, 0, devinfo->fw_name, NULL, brcmf_usb_probe_phase2); } @@ -1491,10 +1459,6 @@ static struct usb_device_id brcmf_usb_devid_table[] = { }; MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table); -MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME); -MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME); -MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME); -MODULE_FIRMWARE(BRCMF_USB_43569_FW_NAME); static struct usb_driver brcmf_usbdrvr = { .name = KBUILD_MODNAME, -- cgit v1.2.3-59-g8ed1b From 5536f20a1c43417901e5bb66d62c38853e070be5 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 25 Nov 2015 11:32:47 +0100 Subject: brcmfmac: Fix double free on exception at module load. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 90389320de13..3a39192e3f14 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -1083,6 +1083,8 @@ fail: brcmf_net_detach(ifp->ndev); if (p2p_ifp) brcmf_net_detach(p2p_ifp->ndev); + drvr->iflist[0] = NULL; + drvr->iflist[1] = NULL; return ret; } return 0; -- cgit v1.2.3-59-g8ed1b From 952348a5f88b92e412f3e490fb83acc17e68d85c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 26 Nov 2015 14:55:23 +0300 Subject: rt2x00: type bug in _rt2500usb_register_read() This code causes a static checker bug. drivers/net/wireless/ralink/rt2x00/rt2500usb.c:232 _rt2500usb_register_read() warn: passing casted pointer 'value' to 'rt2500usb_register_read()' 32 vs 16. If the low 16 bits were initialized to zero then this code would only be a problem on big endian systems. But in this case this is case the low 16 bits are never initialized. This is called from a function which is created using a macro: RT2X00DEBUGFS_OPS(csr, "0x%.8x\n", u32); We end up copying uninitialized data to the user which is bogus and an information leak. Signed-off-by: Dan Carpenter Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2500usb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c index b50d873145d5..d26018f30b7d 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c @@ -229,7 +229,10 @@ static void _rt2500usb_register_read(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value) { - rt2500usb_register_read(rt2x00dev, offset, (u16 *)value); + u16 tmp; + + rt2500usb_register_read(rt2x00dev, offset, &tmp); + *value = tmp; } static void _rt2500usb_register_write(struct rt2x00_dev *rt2x00dev, -- cgit v1.2.3-59-g8ed1b From 6d91ff7acc903b91488c17d781ebfecbce45285a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 26 Nov 2015 15:04:49 +0300 Subject: libertas: cleanup a variable name "&card->priv->driver_lock" and "&priv->driver_lock" are the same and it's nicer to use the shorter one consistently. Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/libertas/if_sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/libertas/if_sdio.c b/drivers/net/wireless/marvell/libertas/if_sdio.c index 33ceda296c9c..68fd3a9779bd 100644 --- a/drivers/net/wireless/marvell/libertas/if_sdio.c +++ b/drivers/net/wireless/marvell/libertas/if_sdio.c @@ -228,7 +228,7 @@ static int if_sdio_handle_cmd(struct if_sdio_card *card, memcpy(priv->resp_buf[i], buffer, size); lbs_notify_command_response(priv, i); - spin_unlock_irqrestore(&card->priv->driver_lock, flags); + spin_unlock_irqrestore(&priv->driver_lock, flags); ret = 0; -- cgit v1.2.3-59-g8ed1b From 7941c2129a4bc189e17e3188a7d315d946caa1d6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 27 Nov 2015 16:43:27 +0300 Subject: brcm80211: fix error code in brcmf_pcie_exit_download_state() The original code returns 1 on failure and 0 on success but the caller was expecting an error code on failure. Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index ace0404817dc..b5a4247604f3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -609,7 +609,9 @@ static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, brcmf_chip_resetcore(core, 0, 0, 0); } - return !brcmf_chip_set_active(devinfo->ci, resetintr); + if (!brcmf_chip_set_active(devinfo->ci, resetintr)) + return -EINVAL; + return 0; } -- cgit v1.2.3-59-g8ed1b From b31fa550241d1fdd62e298c843c8f08a55bd57ff Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 27 Nov 2015 18:29:31 +0200 Subject: wireless: airo: re-use mac_pton() mac_pton() converts 6-byte MAC / BSSID to binary format. Change an open coded variant by the generic one. Signed-off-by: Andy Shevchenko Signed-off-by: Kalle Valo --- drivers/net/wireless/cisco/airo.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c index 17c40f06f13e..d2353f6e5214 100644 --- a/drivers/net/wireless/cisco/airo.c +++ b/drivers/net/wireless/cisco/airo.c @@ -5137,21 +5137,9 @@ static void proc_APList_on_close( struct inode *inode, struct file *file ) { memset(APList_rid, 0, sizeof(*APList_rid)); APList_rid->len = cpu_to_le16(sizeof(*APList_rid)); - for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) { - int j; - for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) { - switch(j%3) { - case 0: - APList_rid->ap[i][j/3]= - hex_to_bin(data->wbuffer[j+i*6*3])<<4; - break; - case 1: - APList_rid->ap[i][j/3]|= - hex_to_bin(data->wbuffer[j+i*6*3]); - break; - } - } - } + for (i = 0; i < 4 && data->writelen >= (i + 1) * 6 * 3; i++) + mac_pton(data->wbuffer + i * 6 * 3, APList_rid->ap[i]); + disable_MAC(ai, 1); writeAPListRid(ai, APList_rid, 1); enable_MAC(ai, 1); -- cgit v1.2.3-59-g8ed1b From bd5632b0989b180db1199f7fc6969dd4ca45643d Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Fri, 20 Nov 2015 09:33:14 -0800 Subject: ath10k: fix board data fetch error message The error message order of board data fetch is board info, directory, and then file name, hence place print arguments in the order. Fixes: 0a51b343abfe ("ath10k: add board 2 API support") Signed-off-by: Peter Oh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index f128adbae454..7a59dbb1b1e2 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -849,7 +849,7 @@ out: if (!ar->board_data || !ar->board_len) { ath10k_err(ar, "failed to fetch board data for %s from %s/%s\n", - ar->hw_params.fw.dir, boardname, filename); + boardname, ar->hw_params.fw.dir, filename); ret = -ENODATA; goto err; } -- cgit v1.2.3-59-g8ed1b From 78f7aeb08f8f4be3868f4530552f56880fd08b8f Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Sat, 21 Nov 2015 15:24:41 +0530 Subject: ath10k: Enable AP + STA interface combination support for 10.x Enable AP + STA interface combination support for 10.x and this enables Repeater mode testing (with WDS Repeater combinations as well). Fix indentation as well. Based on the suggestions from Michal and Yanbo Li, thanks to them Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 8a2c4a9ec73a..de19967c64ce 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6921,35 +6921,39 @@ void ath10k_mac_destroy(struct ath10k *ar) static const struct ieee80211_iface_limit ath10k_if_limits[] = { { - .max = 8, - .types = BIT(NL80211_IFTYPE_STATION) - | BIT(NL80211_IFTYPE_P2P_CLIENT) + .max = 8, + .types = BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_P2P_CLIENT) }, { - .max = 3, - .types = BIT(NL80211_IFTYPE_P2P_GO) + .max = 3, + .types = BIT(NL80211_IFTYPE_P2P_GO) }, { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_DEVICE) + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, { - .max = 7, - .types = BIT(NL80211_IFTYPE_AP) + .max = 7, + .types = BIT(NL80211_IFTYPE_AP) #ifdef CONFIG_MAC80211_MESH - | BIT(NL80211_IFTYPE_MESH_POINT) + | BIT(NL80211_IFTYPE_MESH_POINT) #endif }, }; static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = { { - .max = 8, - .types = BIT(NL80211_IFTYPE_AP) + .max = 8, + .types = BIT(NL80211_IFTYPE_AP) #ifdef CONFIG_MAC80211_MESH - | BIT(NL80211_IFTYPE_MESH_POINT) + | BIT(NL80211_IFTYPE_MESH_POINT) #endif }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION) + }, }; static const struct ieee80211_iface_combination ath10k_if_comb[] = { -- cgit v1.2.3-59-g8ed1b From d6cb23b5147d2efb79a7d99ff019dfa6b4f8ed22 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 24 Nov 2015 11:36:52 +0100 Subject: ath10k: stop abusing GFP_DMA Allocations from the DMA zone were originally added for legacy ISA stuff, or PCI devices that have specific limitations in their DMA addressing capabilities. It has no place in ath10k, which can do full 32-bit DMA. Fixes memory allocation errors on some platforms. Signed-off-by: Felix Fietkau Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 4 ++-- drivers/net/wireless/ath/ath10k/htt_tx.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index b84727bcf2da..91afa3ae414c 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -536,7 +536,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt) size = htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring); - vaddr = dma_alloc_coherent(htt->ar->dev, size, &paddr, GFP_DMA); + vaddr = dma_alloc_coherent(htt->ar->dev, size, &paddr, GFP_KERNEL); if (!vaddr) goto err_dma_ring; @@ -545,7 +545,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt) vaddr = dma_alloc_coherent(htt->ar->dev, sizeof(*htt->rx_ring.alloc_idx.vaddr), - &paddr, GFP_DMA); + &paddr, GFP_KERNEL); if (!vaddr) goto err_dma_idx; diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 5274f5bb0b45..b3adadb5f824 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -111,7 +111,7 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) size = htt->max_num_pending_tx * sizeof(struct ath10k_htt_txbuf); htt->txbuf.vaddr = dma_alloc_coherent(ar->dev, size, &htt->txbuf.paddr, - GFP_DMA); + GFP_KERNEL); if (!htt->txbuf.vaddr) { ath10k_err(ar, "failed to alloc tx buffer\n"); ret = -ENOMEM; @@ -124,7 +124,7 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) size = htt->max_num_pending_tx * sizeof(struct htt_msdu_ext_desc); htt->frag_desc.vaddr = dma_alloc_coherent(ar->dev, size, &htt->frag_desc.paddr, - GFP_DMA); + GFP_KERNEL); if (!htt->frag_desc.vaddr) { ath10k_warn(ar, "failed to alloc fragment desc memory\n"); ret = -ENOMEM; -- cgit v1.2.3-59-g8ed1b From 8bf1ba1cd789381315579b4f570e37b6ff26ceaf Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Tue, 24 Nov 2015 22:26:37 +0530 Subject: ath10k: Fix write permission on few debugfs files Fix write permission for few of the debugfs entries which support write file operations as well. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 145c066b5087..b502b7fb3ca2 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -2185,8 +2185,8 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_wmi_services); - debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy, - ar, &fops_simulate_fw_crash); + debugfs_create_file("simulate_fw_crash", S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, &fops_simulate_fw_crash); debugfs_create_file("fw_crash_dump", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_fw_crash_dump); @@ -2203,15 +2203,15 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_chip_id); - debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy, - ar, &fops_htt_stats_mask); + debugfs_create_file("htt_stats_mask", S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, &fops_htt_stats_mask); debugfs_create_file("htt_max_amsdu_ampdu", S_IRUSR | S_IWUSR, ar->debug.debugfs_phy, ar, &fops_htt_max_amsdu_ampdu); - debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy, - ar, &fops_fw_dbglog); + debugfs_create_file("fw_dbglog", S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, &fops_fw_dbglog); debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_cal_data); -- cgit v1.2.3-59-g8ed1b From 20fa2f7f5f2263d6f76e1c1ce35693ca81d828ff Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Tue, 24 Nov 2015 09:37:33 -0800 Subject: ath10k: update WMI 10.x service map Update WMI 10.x service map to sync with firmware 10.2.4.70.12-2 released on 11/11/2015 which is the latest QCA988X firmware as of 11/18/2015. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 80d3f1c1b620..0747055faa28 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -208,6 +208,11 @@ enum wmi_10x_service { WMI_10X_SERVICE_SMART_ANTENNA_HW_SUPPORT, WMI_10X_SERVICE_ATF, WMI_10X_SERVICE_COEX_GPIO, + WMI_10X_SERVICE_AUX_SPECTRAL_INTF, + WMI_10X_SERVICE_AUX_CHAN_LOAD_INTF, + WMI_10X_SERVICE_BSS_CHANNEL_INFO_64, + WMI_10X_SERVICE_MESH, + WMI_10X_SERVICE_EXT_RES_CFG_SUPPORT, }; enum wmi_main_service { @@ -448,6 +453,16 @@ static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out, WMI_SERVICE_ATF, len); SVCMAP(WMI_10X_SERVICE_COEX_GPIO, WMI_SERVICE_COEX_GPIO, len); + SVCMAP(WMI_10X_SERVICE_AUX_SPECTRAL_INTF, + WMI_SERVICE_AUX_SPECTRAL_INTF, len); + SVCMAP(WMI_10X_SERVICE_AUX_CHAN_LOAD_INTF, + WMI_SERVICE_AUX_CHAN_LOAD_INTF, len); + SVCMAP(WMI_10X_SERVICE_BSS_CHANNEL_INFO_64, + WMI_SERVICE_BSS_CHANNEL_INFO_64, len); + SVCMAP(WMI_10X_SERVICE_MESH, + WMI_SERVICE_MESH, len); + SVCMAP(WMI_10X_SERVICE_EXT_RES_CFG_SUPPORT, + WMI_SERVICE_EXT_RES_CFG_SUPPORT, len); } static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out, -- cgit v1.2.3-59-g8ed1b From 42e08fea970576f4c0c6524fff150736801d8f2e Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Tue, 24 Nov 2015 09:37:34 -0800 Subject: ath10k: introduce new subtypes for proxy STA and Mesh QCA988X firmware starting from 10.2.4.70.12-2 supports new vdev subtypes for proxy STA and Mesh, hence add them to be used as needed. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 0747055faa28..d85ad7855d20 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -4274,6 +4274,8 @@ enum wmi_vdev_subtype { WMI_VDEV_SUBTYPE_P2P_DEVICE = 1, WMI_VDEV_SUBTYPE_P2P_CLIENT = 2, WMI_VDEV_SUBTYPE_P2P_GO = 3, + WMI_VDEV_SUBTYPE_PROXY_STA = 4, + WMI_VDEV_SUBTYPE_MESH = 5, }; /* values for vdev_subtype */ -- cgit v1.2.3-59-g8ed1b From bb58b89c5ea1006c93270fd2e667ab72312bf7ab Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Tue, 24 Nov 2015 09:37:35 -0800 Subject: ath10k: apply Mesh subtype when Mesh interface created. QCA988X firmware starting from 10.2.4.70.12-2 has capability to support Mesh Control Field Present bit in QoS field in native Wi-Fi mode. Hence apply Mesh subtype according to the WMI service map. Firmware will allows unicast, broadcast, multicast, and WDS frame (FromDS = 1 and ToDS = 1) to be received via the interface, once Mesh subtype is used. The firmware and this patch together make native Wi-Fi mode comply to IEEE802.11s Mesh frame in open mode, but the firmware doesn't yet support secured Mesh mode. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index de19967c64ce..8ae5216fd188 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4364,7 +4364,9 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, arvif->vdev_type = WMI_VDEV_TYPE_IBSS; break; case NL80211_IFTYPE_MESH_POINT: - if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { + if (test_bit(WMI_SERVICE_MESH, ar->wmi.svc_map)) { + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH; + } else if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { ret = -EINVAL; ath10k_warn(ar, "must load driver with rawmode=1 to add mesh interfaces\n"); goto err; -- cgit v1.2.3-59-g8ed1b From b091f36990ef85fa9959307f535a6b8431c2799f Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 25 Nov 2015 15:38:05 +0200 Subject: ath10k: fix otp board id error message We check board id from all board types, not just qca99x0, so the error message was misleading. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 7a59dbb1b1e2..574334268a3a 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1753,7 +1753,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ret = ath10k_core_get_board_id_from_otp(ar); if (ret && ret != -EOPNOTSUPP) { - ath10k_err(ar, "failed to get board id from otp for qca99x0: %d\n", + ath10k_err(ar, "failed to get board id from otp: %d\n", ret); return ret; } -- cgit v1.2.3-59-g8ed1b From f0de90bc1047affe4bb83a5724c612c6cce0e672 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 25 Nov 2015 15:38:12 +0200 Subject: ath10k: reorganise hardware and firmware info messages This is to make it easier to split them later and also make room for crc32 checksums. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index b502b7fb3ca2..c2b11bd50fa8 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -136,14 +136,28 @@ void ath10k_print_driver_info(struct ath10k *ar) scnprintf(boardinfo, sizeof(boardinfo), "sub %04x:%04x", ar->id.subsystem_vendor, ar->id.subsystem_device); - ath10k_info(ar, "%s (0x%08x, 0x%08x %s) fw %s fwapi %d bdapi %d htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d features %s\n", + ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x %s", ar->hw_params.name, ar->target_version, ar->chip_id, - boardinfo, + boardinfo); + + ath10k_info(ar, "firmware ver %s api %d features %s\n", ar->hw->wiphy->fw_version, ar->fw_api, - ar->bd_api, + fw_features); + + ath10k_info(ar, "board_file api %d", + ar->bd_api); + + ath10k_info(ar, "kconfig debug %d debugfs %d tracing %d dfs %d testmode %d\n", + config_enabled(CONFIG_ATH10K_DEBUG), + config_enabled(CONFIG_ATH10K_DEBUGFS), + config_enabled(CONFIG_ATH10K_TRACING), + config_enabled(CONFIG_ATH10K_DFS_CERTIFIED), + config_enabled(CONFIG_NL80211_TESTMODE)); + + ath10k_info(ar, "htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d\n", ar->htt.target_version_major, ar->htt.target_version_minor, ar->wmi.op_version, @@ -151,14 +165,7 @@ void ath10k_print_driver_info(struct ath10k *ar) ath10k_cal_mode_str(ar->cal_mode), ar->max_num_stations, test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags), - !test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags), - fw_features); - ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n", - config_enabled(CONFIG_ATH10K_DEBUG), - config_enabled(CONFIG_ATH10K_DEBUGFS), - config_enabled(CONFIG_ATH10K_TRACING), - config_enabled(CONFIG_ATH10K_DFS_CERTIFIED), - config_enabled(CONFIG_NL80211_TESTMODE)); + !test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags)); } EXPORT_SYMBOL(ath10k_print_driver_info); -- cgit v1.2.3-59-g8ed1b From 8605c02205bfcaaf7dcc59c5f9bd3f6acd63e086 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 25 Nov 2015 15:38:19 +0200 Subject: ath10k: always show bmi chip ids and subdevice ids That way we can split the messages to be printed in different stages of device initialisation. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index c2b11bd50fa8..2349cca31809 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -130,25 +130,25 @@ void ath10k_print_driver_info(struct ath10k *ar) ath10k_core_get_fw_features_str(ar, fw_features, sizeof(fw_features)); if (ar->id.bmi_ids_valid) - scnprintf(boardinfo, sizeof(boardinfo), "bmi %d:%d", + scnprintf(boardinfo, sizeof(boardinfo), "%d:%d", ar->id.bmi_chip_id, ar->id.bmi_board_id); else - scnprintf(boardinfo, sizeof(boardinfo), "sub %04x:%04x", - ar->id.subsystem_vendor, ar->id.subsystem_device); + scnprintf(boardinfo, sizeof(boardinfo), "N/A"); - ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x %s", + ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x sub %04x:%04x", ar->hw_params.name, ar->target_version, ar->chip_id, - boardinfo); + ar->id.subsystem_vendor, ar->id.subsystem_device); ath10k_info(ar, "firmware ver %s api %d features %s\n", ar->hw->wiphy->fw_version, ar->fw_api, fw_features); - ath10k_info(ar, "board_file api %d", - ar->bd_api); + ath10k_info(ar, "board_file api %d bmi_id %s", + ar->bd_api, + boardinfo); ath10k_info(ar, "kconfig debug %d debugfs %d tracing %d dfs %d testmode %d\n", config_enabled(CONFIG_ATH10K_DEBUG), -- cgit v1.2.3-59-g8ed1b From 23f591ea9b1c8ce7ddfbf14ffd972c8f33677b2d Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 25 Nov 2015 15:38:27 +0200 Subject: ath10k: split driver info messages during device initialisation Earlier we printed all the info messages after a successful device initialisation and firmware boot, but that's problematic if something goes wrong and there's no easy way to know what firmware version was used and so on. Split the info messages into smaller pieces and print them as soon as we have the info available. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 6 ++++- drivers/net/wireless/ath/ath10k/debug.c | 44 ++++++++++++++++++++++----------- drivers/net/wireless/ath/ath10k/debug.h | 4 +++ 3 files changed, 38 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 574334268a3a..fca702c991ed 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1751,6 +1751,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar) goto err_power_down; } + ath10k_debug_print_hwfw_info(ar); + ret = ath10k_core_get_board_id_from_otp(ar); if (ret && ret != -EOPNOTSUPP) { ath10k_err(ar, "failed to get board id from otp: %d\n", @@ -1764,6 +1766,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar) goto err_free_firmware_files; } + ath10k_debug_print_board_info(ar); + ret = ath10k_core_init_firmware_features(ar); if (ret) { ath10k_err(ar, "fatal problem with firmware features: %d\n", @@ -1786,7 +1790,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar) goto err_unlock; } - ath10k_print_driver_info(ar); + ath10k_debug_print_boot_info(ar); ath10k_core_stop(ar); mutex_unlock(&ar->conf_mutex); diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 2349cca31809..ea8b562aca8d 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -122,41 +122,48 @@ void ath10k_info(struct ath10k *ar, const char *fmt, ...) } EXPORT_SYMBOL(ath10k_info); -void ath10k_print_driver_info(struct ath10k *ar) +void ath10k_debug_print_hwfw_info(struct ath10k *ar) { char fw_features[128] = {}; - char boardinfo[100]; ath10k_core_get_fw_features_str(ar, fw_features, sizeof(fw_features)); - if (ar->id.bmi_ids_valid) - scnprintf(boardinfo, sizeof(boardinfo), "%d:%d", - ar->id.bmi_chip_id, ar->id.bmi_board_id); - else - scnprintf(boardinfo, sizeof(boardinfo), "N/A"); - ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x sub %04x:%04x", ar->hw_params.name, ar->target_version, ar->chip_id, ar->id.subsystem_vendor, ar->id.subsystem_device); + ath10k_info(ar, "kconfig debug %d debugfs %d tracing %d dfs %d testmode %d\n", + config_enabled(CONFIG_ATH10K_DEBUG), + config_enabled(CONFIG_ATH10K_DEBUGFS), + config_enabled(CONFIG_ATH10K_TRACING), + config_enabled(CONFIG_ATH10K_DFS_CERTIFIED), + config_enabled(CONFIG_NL80211_TESTMODE)); + ath10k_info(ar, "firmware ver %s api %d features %s\n", ar->hw->wiphy->fw_version, ar->fw_api, fw_features); +} + +void ath10k_debug_print_board_info(struct ath10k *ar) +{ + char boardinfo[100]; + + if (ar->id.bmi_ids_valid) + scnprintf(boardinfo, sizeof(boardinfo), "%d:%d", + ar->id.bmi_chip_id, ar->id.bmi_board_id); + else + scnprintf(boardinfo, sizeof(boardinfo), "N/A"); ath10k_info(ar, "board_file api %d bmi_id %s", ar->bd_api, boardinfo); +} - ath10k_info(ar, "kconfig debug %d debugfs %d tracing %d dfs %d testmode %d\n", - config_enabled(CONFIG_ATH10K_DEBUG), - config_enabled(CONFIG_ATH10K_DEBUGFS), - config_enabled(CONFIG_ATH10K_TRACING), - config_enabled(CONFIG_ATH10K_DFS_CERTIFIED), - config_enabled(CONFIG_NL80211_TESTMODE)); - +void ath10k_debug_print_boot_info(struct ath10k *ar) +{ ath10k_info(ar, "htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d\n", ar->htt.target_version_major, ar->htt.target_version_minor, @@ -167,6 +174,13 @@ void ath10k_print_driver_info(struct ath10k *ar) test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags), !test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags)); } + +void ath10k_print_driver_info(struct ath10k *ar) +{ + ath10k_debug_print_hwfw_info(ar); + ath10k_debug_print_board_info(ar); + ath10k_debug_print_boot_info(ar); +} EXPORT_SYMBOL(ath10k_print_driver_info); void ath10k_err(struct ath10k *ar, const char *fmt, ...) diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index 7de780c4ec8d..814719cf4f22 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -63,6 +63,10 @@ extern unsigned int ath10k_debug_mask; __printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...); __printf(2, 3) void ath10k_err(struct ath10k *ar, const char *fmt, ...); __printf(2, 3) void ath10k_warn(struct ath10k *ar, const char *fmt, ...); + +void ath10k_debug_print_hwfw_info(struct ath10k *ar); +void ath10k_debug_print_board_info(struct ath10k *ar); +void ath10k_debug_print_boot_info(struct ath10k *ar); void ath10k_print_driver_info(struct ath10k *ar); #ifdef CONFIG_ATH10K_DEBUGFS -- cgit v1.2.3-59-g8ed1b From 3e58044b61a92ad63e52b347782248659b6b6f12 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 25 Nov 2015 15:38:34 +0200 Subject: ath10k: print crc32 checksums for firmware and board files To detect cases if the firmare or board file is corrupted or otherwise modified print crc32 value of both. Now the output looks like: ath10k_pci 0000:02:00.0: pci irq msi interrupts 1 irq_mode 0 reset_mode 0 ath10k_pci 0000:02:00.0: qca99x0 hw2.0 target 0x01000000 chip_id 0x003801ff sub 168c:0002 ath10k_pci 0000:02:00.0: kconfig debug 1 debugfs 1 tracing 1 dfs 1 testmode 1 ath10k_pci 0000:02:00.0: firmware ver 10.4.1.00030-1 api 5 features no-p2p crc32 d2901e01 ath10k_pci 0000:02:00.0: board_file api 1 bmi_id 1:1 crc32 7e56fd07 ath10k_pci 0000:02:00.0: htt-ver 2.2 wmi-op 6 htt-op 4 cal otp max-sta 512 raw 0 hwcrypto 1 Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/Kconfig | 1 + drivers/net/wireless/ath/ath10k/debug.c | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index 72acb822bb11..03aa35f999a1 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -2,6 +2,7 @@ config ATH10K tristate "Atheros 802.11ac wireless cards support" depends on MAC80211 && HAS_DMA select ATH_COMMON + select CRC32 ---help--- This module adds support for wireless adapters based on Atheros IEEE 802.11ac family of chipsets. diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index ea8b562aca8d..4fc3d4fcfa8f 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "core.h" #include "debug.h" @@ -141,10 +143,11 @@ void ath10k_debug_print_hwfw_info(struct ath10k *ar) config_enabled(CONFIG_ATH10K_DFS_CERTIFIED), config_enabled(CONFIG_NL80211_TESTMODE)); - ath10k_info(ar, "firmware ver %s api %d features %s\n", + ath10k_info(ar, "firmware ver %s api %d features %s crc32 %08x\n", ar->hw->wiphy->fw_version, ar->fw_api, - fw_features); + fw_features, + crc32_le(0, ar->firmware->data, ar->firmware->size)); } void ath10k_debug_print_board_info(struct ath10k *ar) @@ -157,9 +160,10 @@ void ath10k_debug_print_board_info(struct ath10k *ar) else scnprintf(boardinfo, sizeof(boardinfo), "N/A"); - ath10k_info(ar, "board_file api %d bmi_id %s", + ath10k_info(ar, "board_file api %d bmi_id %s crc32 %08x", ar->bd_api, - boardinfo); + boardinfo, + crc32_le(0, ar->board->data, ar->board->size)); } void ath10k_debug_print_boot_info(struct ath10k *ar) -- cgit v1.2.3-59-g8ed1b From 9e100c4d013e6e27ea3318e53d200a912080387f Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 25 Nov 2015 15:38:41 +0200 Subject: ath10k: implement fw_checksums debugfs file When debugging firmware problems it's useful to check checksums of each component. Add a debugfs interface to retrieve crc32 checksums: # cat /sys/kernel/debug/ieee80211/phy0/ath10k/fw_checksums firmware-N.bin cc3fb466 athwlan c0089f21 otp f3efeb4f codeswap 00000000 board-N.bin bebc7c08 board bebc7c08 Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 4fc3d4fcfa8f..39fe4f3350aa 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -2161,6 +2161,59 @@ static const struct file_operations fops_btcoex = { .open = simple_open }; +static ssize_t ath10k_debug_fw_checksums_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned int len = 0, buf_len = 4096; + ssize_t ret_cnt; + char *buf; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + + if (len > buf_len) + len = buf_len; + + len += scnprintf(buf + len, buf_len - len, + "firmware-N.bin\t\t%08x\n", + crc32_le(0, ar->firmware->data, ar->firmware->size)); + len += scnprintf(buf + len, buf_len - len, + "athwlan\t\t\t%08x\n", + crc32_le(0, ar->firmware_data, ar->firmware_len)); + len += scnprintf(buf + len, buf_len - len, + "otp\t\t\t%08x\n", + crc32_le(0, ar->otp_data, ar->otp_len)); + len += scnprintf(buf + len, buf_len - len, + "codeswap\t\t%08x\n", + crc32_le(0, ar->swap.firmware_codeswap_data, + ar->swap.firmware_codeswap_len)); + len += scnprintf(buf + len, buf_len - len, + "board-N.bin\t\t%08x\n", + crc32_le(0, ar->board->data, ar->board->size)); + len += scnprintf(buf + len, buf_len - len, + "board\t\t\t%08x\n", + crc32_le(0, ar->board_data, ar->board_len)); + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + mutex_unlock(&ar->conf_mutex); + + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_fw_checksums = { + .read = ath10k_debug_fw_checksums_read, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_create(struct ath10k *ar) { ar->debug.fw_crash_data = vzalloc(sizeof(*ar->debug.fw_crash_data)); @@ -2274,6 +2327,9 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("btcoex", S_IRUGO | S_IWUSR, ar->debug.debugfs_phy, ar, &fops_btcoex); + debugfs_create_file("fw_checksums", S_IRUSR, + ar->debug.debugfs_phy, ar, &fops_fw_checksums); + return 0; } -- cgit v1.2.3-59-g8ed1b From 3161c1590049a10879557c7864eaf5ab7ac103ee Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 27 Nov 2015 13:45:54 +0100 Subject: mlxsw: reg: Add Management LED Control register definition Add the MLCR register, which controls physical port identification LEDs. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 236fb5d2ad69..fc6839d3c3e3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -2087,6 +2087,50 @@ static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id) mlxsw_reg_hpkt_ctrl_set(payload, MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT); } +/* MLCR - Management LED Control Register + * -------------------------------------- + * Controls the system LEDs. + */ +#define MLXSW_REG_MLCR_ID 0x902B +#define MLXSW_REG_MLCR_LEN 0x0C + +static const struct mlxsw_reg_info mlxsw_reg_mlcr = { + .id = MLXSW_REG_MLCR_ID, + .len = MLXSW_REG_MLCR_LEN, +}; + +/* reg_mlcr_local_port + * Local port number. + * Access: RW + */ +MLXSW_ITEM32(reg, mlcr, local_port, 0x00, 16, 8); + +#define MLXSW_REG_MLCR_DURATION_MAX 0xFFFF + +/* reg_mlcr_beacon_duration + * Duration of the beacon to be active, in seconds. + * 0x0 - Will turn off the beacon. + * 0xFFFF - Will turn on the beacon until explicitly turned off. + * Access: RW + */ +MLXSW_ITEM32(reg, mlcr, beacon_duration, 0x04, 0, 16); + +/* reg_mlcr_beacon_remain + * Remaining duration of the beacon, in seconds. + * 0xFFFF indicates an infinite amount of time. + * Access: RO + */ +MLXSW_ITEM32(reg, mlcr, beacon_remain, 0x08, 0, 16); + +static inline void mlxsw_reg_mlcr_pack(char *payload, u8 local_port, + bool active) +{ + MLXSW_REG_ZERO(mlcr, payload); + mlxsw_reg_mlcr_local_port_set(payload, local_port); + mlxsw_reg_mlcr_beacon_duration_set(payload, active ? + MLXSW_REG_MLCR_DURATION_MAX : 0); +} + /* SBPR - Shared Buffer Pools Register * ----------------------------------- * The SBPR configures and retrieves the shared buffer pools and configuration. @@ -2405,6 +2449,8 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "HTGT"; case MLXSW_REG_HPKT_ID: return "HPKT"; + case MLXSW_REG_MLCR_ID: + return "MLCR"; case MLXSW_REG_SBPR_ID: return "SBPR"; case MLXSW_REG_SBCM_ID: -- cgit v1.2.3-59-g8ed1b From 3a66ee38dcde2e7befcd1563151615644a560072 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 27 Nov 2015 13:45:55 +0100 Subject: mlxsw: spectrum: Add support for port identification Allow a user to flash the port's LED in order to identify it. This is achieved by setting the Management LED Control Register (MLCR). Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 3be4a2355ead..14a9a9ff89ed 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -859,6 +859,29 @@ static void mlxsw_sp_port_get_strings(struct net_device *dev, } } +static int mlxsw_sp_port_set_phys_id(struct net_device *dev, + enum ethtool_phys_id_state state) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char mlcr_pl[MLXSW_REG_MLCR_LEN]; + bool active; + + switch (state) { + case ETHTOOL_ID_ACTIVE: + active = true; + break; + case ETHTOOL_ID_INACTIVE: + active = false; + break; + default: + return -EOPNOTSUPP; + } + + mlxsw_reg_mlcr_pack(mlcr_pl, mlxsw_sp_port->local_port, active); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mlcr), mlcr_pl); +} + static void mlxsw_sp_port_get_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { @@ -1205,6 +1228,7 @@ static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = { .get_drvinfo = mlxsw_sp_port_get_drvinfo, .get_link = ethtool_op_get_link, .get_strings = mlxsw_sp_port_get_strings, + .set_phys_id = mlxsw_sp_port_set_phys_id, .get_ethtool_stats = mlxsw_sp_port_get_stats, .get_sset_count = mlxsw_sp_port_get_sset_count, .get_settings = mlxsw_sp_port_get_settings, -- cgit v1.2.3-59-g8ed1b From 85926f8770403d4c7a1828db873eb8c6bec14ce5 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 27 Nov 2015 13:45:56 +0100 Subject: mlxsw: reg: Add definition of temperature management registers Add definition of MTCAP and MTMP registers which provide access to temperature sensors. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 111 ++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index fc6839d3c3e3..459e04904512 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -2087,6 +2087,113 @@ static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id) mlxsw_reg_hpkt_ctrl_set(payload, MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT); } +/* MTCAP - Management Temperature Capabilities + * ------------------------------------------- + * This register exposes the capabilities of the device and + * system temperature sensing. + */ +#define MLXSW_REG_MTCAP_ID 0x9009 +#define MLXSW_REG_MTCAP_LEN 0x08 + +static const struct mlxsw_reg_info mlxsw_reg_mtcap = { + .id = MLXSW_REG_MTCAP_ID, + .len = MLXSW_REG_MTCAP_LEN, +}; + +/* reg_mtcap_sensor_count + * Number of sensors supported by the device. + * This includes the QSFP module sensors (if exists in the QSFP module). + * Access: RO + */ +MLXSW_ITEM32(reg, mtcap, sensor_count, 0x00, 0, 7); + +/* MTMP - Management Temperature + * ----------------------------- + * This register controls the settings of the temperature measurements + * and enables reading the temperature measurements. Note that temperature + * is in 0.125 degrees Celsius. + */ +#define MLXSW_REG_MTMP_ID 0x900A +#define MLXSW_REG_MTMP_LEN 0x20 + +static const struct mlxsw_reg_info mlxsw_reg_mtmp = { + .id = MLXSW_REG_MTMP_ID, + .len = MLXSW_REG_MTMP_LEN, +}; + +/* reg_mtmp_sensor_index + * Sensors index to access. + * 64-127 of sensor_index are mapped to the SFP+/QSFP modules sequentially + * (module 0 is mapped to sensor_index 64). + * Access: Index + */ +MLXSW_ITEM32(reg, mtmp, sensor_index, 0x00, 0, 7); + +/* Convert to milli degrees Celsius */ +#define MLXSW_REG_MTMP_TEMP_TO_MC(val) (val * 125) + +/* reg_mtmp_temperature + * Temperature reading from the sensor. Reading is in 0.125 Celsius + * degrees units. + * Access: RO + */ +MLXSW_ITEM32(reg, mtmp, temperature, 0x04, 0, 16); + +/* reg_mtmp_mte + * Max Temperature Enable - enables measuring the max temperature on a sensor. + * Access: RW + */ +MLXSW_ITEM32(reg, mtmp, mte, 0x08, 31, 1); + +/* reg_mtmp_mtr + * Max Temperature Reset - clears the value of the max temperature register. + * Access: WO + */ +MLXSW_ITEM32(reg, mtmp, mtr, 0x08, 30, 1); + +/* reg_mtmp_max_temperature + * The highest measured temperature from the sensor. + * When the bit mte is cleared, the field max_temperature is reserved. + * Access: RO + */ +MLXSW_ITEM32(reg, mtmp, max_temperature, 0x08, 0, 16); + +#define MLXSW_REG_MTMP_SENSOR_NAME_SIZE 8 + +/* reg_mtmp_sensor_name + * Sensor Name + * Access: RO + */ +MLXSW_ITEM_BUF(reg, mtmp, sensor_name, 0x18, MLXSW_REG_MTMP_SENSOR_NAME_SIZE); + +static inline void mlxsw_reg_mtmp_pack(char *payload, u8 sensor_index, + bool max_temp_enable, + bool max_temp_reset) +{ + MLXSW_REG_ZERO(mtmp, payload); + mlxsw_reg_mtmp_sensor_index_set(payload, sensor_index); + mlxsw_reg_mtmp_mte_set(payload, max_temp_enable); + mlxsw_reg_mtmp_mtr_set(payload, max_temp_reset); +} + +static inline void mlxsw_reg_mtmp_unpack(char *payload, unsigned int *p_temp, + unsigned int *p_max_temp, + char *sensor_name) +{ + u16 temp; + + if (p_temp) { + temp = mlxsw_reg_mtmp_temperature_get(payload); + *p_temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp); + } + if (p_max_temp) { + temp = mlxsw_reg_mtmp_temperature_get(payload); + *p_max_temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp); + } + if (sensor_name) + mlxsw_reg_mtmp_sensor_name_memcpy_from(payload, sensor_name); +} + /* MLCR - Management LED Control Register * -------------------------------------- * Controls the system LEDs. @@ -2449,6 +2556,10 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "HTGT"; case MLXSW_REG_HPKT_ID: return "HPKT"; + case MLXSW_REG_MTCAP_ID: + return "MTCAP"; + case MLXSW_REG_MTMP_ID: + return "MTMP"; case MLXSW_REG_MLCR_ID: return "MLCR"; case MLXSW_REG_SBPR_ID: -- cgit v1.2.3-59-g8ed1b From 89309da39f55922f6ca18ecd09a158c319c69d55 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 27 Nov 2015 13:45:57 +0100 Subject: mlxsw: core: Implement temperature hwmon interface ASIC provides access to temperature sensors. Implement their exposure to userspace using hwmon. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Kconfig | 8 + drivers/net/ethernet/mellanox/mlxsw/Makefile | 1 + drivers/net/ethernet/mellanox/mlxsw/core.c | 8 + drivers/net/ethernet/mellanox/mlxsw/core.h | 24 +++ drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c | 223 +++++++++++++++++++++++ 5 files changed, 264 insertions(+) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index e36e12219c9b..ec8caf8fedc6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -10,6 +10,14 @@ config MLXSW_CORE To compile this driver as a module, choose M here: the module will be called mlxsw_core. +config MLXSW_CORE_HWMON + bool "HWMON support for Mellanox Technologies Switch ASICs" + depends on MLXSW_CORE && HWMON + depends on !(MLXSW_CORE=y && HWMON=m) + default y + ---help--- + Say Y here if you want to expose HWMON interface on mlxsw devices. + config MLXSW_PCI tristate "PCI bus implementation for Mellanox Technologies Switch ASICs" depends on PCI && HAS_DMA && HAS_IOMEM && MLXSW_CORE diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index af015818fd19..584cac444852 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.o mlxsw_core-objs := core.o +mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o mlxsw_pci-objs := pci.o obj-$(CONFIG_MLXSW_SWITCHX2) += mlxsw_switchx2.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 97f0d93caf99..1ecb4aabbdc7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -105,6 +105,7 @@ struct mlxsw_core { struct debugfs_blob_wrapper vsd_blob; struct debugfs_blob_wrapper psid_blob; } dbg; + struct mlxsw_hwmon *hwmon; unsigned long driver_priv[0]; /* driver_priv has to be always the last item */ }; @@ -822,6 +823,10 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, if (err) goto err_emad_init; + err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon); + if (err) + goto err_hwmon_init; + err = mlxsw_driver->init(mlxsw_core->driver_priv, mlxsw_core, mlxsw_bus_info); if (err) @@ -836,6 +841,8 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, err_debugfs_init: mlxsw_core->driver->fini(mlxsw_core->driver_priv); err_driver_init: + mlxsw_hwmon_fini(mlxsw_core->hwmon); +err_hwmon_init: mlxsw_emad_fini(mlxsw_core); err_emad_init: mlxsw_bus->fini(bus_priv); @@ -855,6 +862,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core) mlxsw_core_debugfs_fini(mlxsw_core); mlxsw_core->driver->fini(mlxsw_core->driver_priv); + mlxsw_hwmon_fini(mlxsw_core->hwmon); mlxsw_emad_fini(mlxsw_core); mlxsw_core->bus->fini(mlxsw_core->bus_priv); free_percpu(mlxsw_core->pcpu_stats); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 807827350a89..5ac952956d55 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -209,4 +209,28 @@ struct mlxsw_bus_info { u8 psid[MLXSW_CMD_BOARDINFO_PSID_LEN]; }; +struct mlxsw_hwmon; + +#ifdef CONFIG_MLXSW_CORE_HWMON + +int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, + const struct mlxsw_bus_info *mlxsw_bus_info, + struct mlxsw_hwmon **p_hwmon); +void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon); + +#else + +static inline int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, + const struct mlxsw_bus_info *mlxsw_bus_info, + struct mlxsw_hwmon **p_hwmon) +{ + return 0; +} + +static inline void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon) +{ +} + +#endif + #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c new file mode 100644 index 000000000000..ec48c28ce0ca --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c @@ -0,0 +1,223 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c + * Copyright (c) 2015 Mellanox Technologies. All rights reserved. + * Copyright (c) 2015 Jiri Pirko + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "core.h" + +#define MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT 127 +#define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT * 4) + +struct mlxsw_hwmon_attr { + struct device_attribute dev_attr; + struct mlxsw_hwmon *hwmon; + unsigned int type_index; + char name[16]; +}; + +struct mlxsw_hwmon { + struct mlxsw_core *core; + const struct mlxsw_bus_info *bus_info; + struct device *hwmon_dev; + struct attribute_group group; + const struct attribute_group *groups[2]; + struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1]; + struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT]; + unsigned int attrs_count; +}; + +static ssize_t mlxsw_hwmon_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + container_of(attr, struct mlxsw_hwmon_attr, dev_attr); + struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + char mtmp_pl[MLXSW_REG_MTMP_LEN]; + unsigned int temp; + int err; + + mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index, + false, false); + err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl); + if (err) { + dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n"); + return err; + } + mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL); + return sprintf(buf, "%u\n", temp); +} + +static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + container_of(attr, struct mlxsw_hwmon_attr, dev_attr); + struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + char mtmp_pl[MLXSW_REG_MTMP_LEN]; + unsigned int temp_max; + int err; + + mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index, + false, false); + err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl); + if (err) { + dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n"); + return err; + } + mlxsw_reg_mtmp_unpack(mtmp_pl, NULL, &temp_max, NULL); + return sprintf(buf, "%u\n", temp_max); +} + +enum mlxsw_hwmon_attr_type { + MLXSW_HWMON_ATTR_TYPE_TEMP, + MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, +}; + +static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon, + enum mlxsw_hwmon_attr_type attr_type, + unsigned int type_index, unsigned int num) { + struct mlxsw_hwmon_attr *mlxsw_hwmon_attr; + unsigned int attr_index; + + attr_index = mlxsw_hwmon->attrs_count; + mlxsw_hwmon_attr = &mlxsw_hwmon->hwmon_attrs[attr_index]; + + switch (attr_type) { + case MLXSW_HWMON_ATTR_TYPE_TEMP: + mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_show; + mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO; + snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), + "temp%u_input", num + 1); + break; + case MLXSW_HWMON_ATTR_TYPE_TEMP_MAX: + mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_max_show; + mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO; + snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), + "temp%u_highest", num + 1); + break; + default: + BUG(); + } + + mlxsw_hwmon_attr->type_index = type_index; + mlxsw_hwmon_attr->hwmon = mlxsw_hwmon; + mlxsw_hwmon_attr->dev_attr.attr.name = mlxsw_hwmon_attr->name; + sysfs_attr_init(&mlxsw_hwmon_attr->dev_attr.attr); + + mlxsw_hwmon->attrs[attr_index] = &mlxsw_hwmon_attr->dev_attr.attr; + mlxsw_hwmon->attrs_count++; +} + +static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon) +{ + char mtcap_pl[MLXSW_REG_MTCAP_LEN]; + char mtmp_pl[MLXSW_REG_MTMP_LEN]; + u8 sensor_count; + int i; + int err; + + err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtcap), mtcap_pl); + if (err) { + dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n"); + return err; + } + sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl); + for (i = 0; i < sensor_count; i++) { + mlxsw_reg_mtmp_pack(mtmp_pl, 0, true, true); + err = mlxsw_reg_write(mlxsw_hwmon->core, + MLXSW_REG(mtmp), mtmp_pl); + if (err) { + dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n", + i); + return err; + } + mlxsw_hwmon_attr_add(mlxsw_hwmon, + MLXSW_HWMON_ATTR_TYPE_TEMP, i, i); + mlxsw_hwmon_attr_add(mlxsw_hwmon, + MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, i, i); + } + return 0; +} + +int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, + const struct mlxsw_bus_info *mlxsw_bus_info, + struct mlxsw_hwmon **p_hwmon) +{ + struct mlxsw_hwmon *mlxsw_hwmon; + struct device *hwmon_dev; + int err; + + mlxsw_hwmon = kzalloc(sizeof(*mlxsw_hwmon), GFP_KERNEL); + if (!mlxsw_hwmon) + return -ENOMEM; + mlxsw_hwmon->core = mlxsw_core; + mlxsw_hwmon->bus_info = mlxsw_bus_info; + + err = mlxsw_hwmon_temp_init(mlxsw_hwmon); + if (err) + goto err_temp_init; + + mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group; + mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs; + + hwmon_dev = devm_hwmon_device_register_with_groups(mlxsw_bus_info->dev, + "mlxsw", + mlxsw_hwmon, + mlxsw_hwmon->groups); + if (IS_ERR(hwmon_dev)) { + err = PTR_ERR(hwmon_dev); + goto err_hwmon_register; + } + + mlxsw_hwmon->hwmon_dev = hwmon_dev; + *p_hwmon = mlxsw_hwmon; + return 0; + +err_hwmon_register: +err_temp_init: + kfree(mlxsw_hwmon); + return err; +} + +void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon) +{ + kfree(mlxsw_hwmon); +} -- cgit v1.2.3-59-g8ed1b From 5246f2e29ab89b3fba422afc5bcc0f434e57af9c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 27 Nov 2015 13:45:58 +0100 Subject: mlxsw: reg: Add definition of fan management registers Add definition of MFCR, MFSC and MFSM which provide possibility to control and monitor fans. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 133 ++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 459e04904512..f89419393a7f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -2087,6 +2087,133 @@ static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id) mlxsw_reg_hpkt_ctrl_set(payload, MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT); } +/* MFCR - Management Fan Control Register + * -------------------------------------- + * This register controls the settings of the Fan Speed PWM mechanism. + */ +#define MLXSW_REG_MFCR_ID 0x9001 +#define MLXSW_REG_MFCR_LEN 0x08 + +static const struct mlxsw_reg_info mlxsw_reg_mfcr = { + .id = MLXSW_REG_MFCR_ID, + .len = MLXSW_REG_MFCR_LEN, +}; + +enum mlxsw_reg_mfcr_pwm_frequency { + MLXSW_REG_MFCR_PWM_FEQ_11HZ = 0x00, + MLXSW_REG_MFCR_PWM_FEQ_14_7HZ = 0x01, + MLXSW_REG_MFCR_PWM_FEQ_22_1HZ = 0x02, + MLXSW_REG_MFCR_PWM_FEQ_1_4KHZ = 0x40, + MLXSW_REG_MFCR_PWM_FEQ_5KHZ = 0x41, + MLXSW_REG_MFCR_PWM_FEQ_20KHZ = 0x42, + MLXSW_REG_MFCR_PWM_FEQ_22_5KHZ = 0x43, + MLXSW_REG_MFCR_PWM_FEQ_25KHZ = 0x44, +}; + +/* reg_mfcr_pwm_frequency + * Controls the frequency of the PWM signal. + * Access: RW + */ +MLXSW_ITEM32(reg, mfcr, pwm_frequency, 0x00, 0, 6); + +#define MLXSW_MFCR_TACHOS_MAX 10 + +/* reg_mfcr_tacho_active + * Indicates which of the tachometer is active (bit per tachometer). + * Access: RO + */ +MLXSW_ITEM32(reg, mfcr, tacho_active, 0x04, 16, MLXSW_MFCR_TACHOS_MAX); + +#define MLXSW_MFCR_PWMS_MAX 5 + +/* reg_mfcr_pwm_active + * Indicates which of the PWM control is active (bit per PWM). + * Access: RO + */ +MLXSW_ITEM32(reg, mfcr, pwm_active, 0x04, 0, MLXSW_MFCR_PWMS_MAX); + +static inline void +mlxsw_reg_mfcr_pack(char *payload, + enum mlxsw_reg_mfcr_pwm_frequency pwm_frequency) +{ + MLXSW_REG_ZERO(mfcr, payload); + mlxsw_reg_mfcr_pwm_frequency_set(payload, pwm_frequency); +} + +static inline void +mlxsw_reg_mfcr_unpack(char *payload, + enum mlxsw_reg_mfcr_pwm_frequency *p_pwm_frequency, + u16 *p_tacho_active, u8 *p_pwm_active) +{ + *p_pwm_frequency = mlxsw_reg_mfcr_pwm_frequency_get(payload); + *p_tacho_active = mlxsw_reg_mfcr_tacho_active_get(payload); + *p_pwm_active = mlxsw_reg_mfcr_pwm_active_get(payload); +} + +/* MFSC - Management Fan Speed Control Register + * -------------------------------------------- + * This register controls the settings of the Fan Speed PWM mechanism. + */ +#define MLXSW_REG_MFSC_ID 0x9002 +#define MLXSW_REG_MFSC_LEN 0x08 + +static const struct mlxsw_reg_info mlxsw_reg_mfsc = { + .id = MLXSW_REG_MFSC_ID, + .len = MLXSW_REG_MFSC_LEN, +}; + +/* reg_mfsc_pwm + * Fan pwm to control / monitor. + * Access: Index + */ +MLXSW_ITEM32(reg, mfsc, pwm, 0x00, 24, 3); + +/* reg_mfsc_pwm_duty_cycle + * Controls the duty cycle of the PWM. Value range from 0..255 to + * represent duty cycle of 0%...100%. + * Access: RW + */ +MLXSW_ITEM32(reg, mfsc, pwm_duty_cycle, 0x04, 0, 8); + +static inline void mlxsw_reg_mfsc_pack(char *payload, u8 pwm, + u8 pwm_duty_cycle) +{ + MLXSW_REG_ZERO(mfsc, payload); + mlxsw_reg_mfsc_pwm_set(payload, pwm); + mlxsw_reg_mfsc_pwm_duty_cycle_set(payload, pwm_duty_cycle); +} + +/* MFSM - Management Fan Speed Measurement + * --------------------------------------- + * This register controls the settings of the Tacho measurements and + * enables reading the Tachometer measurements. + */ +#define MLXSW_REG_MFSM_ID 0x9003 +#define MLXSW_REG_MFSM_LEN 0x08 + +static const struct mlxsw_reg_info mlxsw_reg_mfsm = { + .id = MLXSW_REG_MFSM_ID, + .len = MLXSW_REG_MFSM_LEN, +}; + +/* reg_mfsm_tacho + * Fan tachometer index. + * Access: Index + */ +MLXSW_ITEM32(reg, mfsm, tacho, 0x00, 24, 4); + +/* reg_mfsm_rpm + * Fan speed (round per minute). + * Access: RO + */ +MLXSW_ITEM32(reg, mfsm, rpm, 0x04, 0, 16); + +static inline void mlxsw_reg_mfsm_pack(char *payload, u8 tacho) +{ + MLXSW_REG_ZERO(mfsm, payload); + mlxsw_reg_mfsm_tacho_set(payload, tacho); +} + /* MTCAP - Management Temperature Capabilities * ------------------------------------------- * This register exposes the capabilities of the device and @@ -2556,6 +2683,12 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "HTGT"; case MLXSW_REG_HPKT_ID: return "HPKT"; + case MLXSW_REG_MFCR_ID: + return "MFCR"; + case MLXSW_REG_MFSC_ID: + return "MFSC"; + case MLXSW_REG_MFSM_ID: + return "MFSM"; case MLXSW_REG_MTCAP_ID: return "MTCAP"; case MLXSW_REG_MTMP_ID: -- cgit v1.2.3-59-g8ed1b From 52581961d83d3550959fdc2e33566c7c9bb1251d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 27 Nov 2015 13:45:59 +0100 Subject: mlxsw: core: Implement fan control using hwmon ASIC provides access to fans. Implement their exposure to userspace using hwmon. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c | 121 ++++++++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c index ec48c28ce0ca..ad8b27418a7f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c @@ -42,7 +42,8 @@ #include "core.h" #define MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT 127 -#define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT * 4) +#define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT * 4 + \ + MLXSW_MFCR_TACHOS_MAX + MLXSW_MFCR_PWMS_MAX) struct mlxsw_hwmon_attr { struct device_attribute dev_attr; @@ -106,9 +107,76 @@ static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev, return sprintf(buf, "%u\n", temp_max); } +static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + container_of(attr, struct mlxsw_hwmon_attr, dev_attr); + struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + char mfsm_pl[MLXSW_REG_MFSM_LEN]; + int err; + + mlxsw_reg_mfsm_pack(mfsm_pl, mlwsw_hwmon_attr->type_index); + err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsm), mfsm_pl); + if (err) { + dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n"); + return err; + } + return sprintf(buf, "%u\n", mlxsw_reg_mfsm_rpm_get(mfsm_pl)); +} + +static ssize_t mlxsw_hwmon_pwm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + container_of(attr, struct mlxsw_hwmon_attr, dev_attr); + struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + char mfsc_pl[MLXSW_REG_MFSC_LEN]; + int err; + + mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, 0); + err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl); + if (err) { + dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query PWM\n"); + return err; + } + return sprintf(buf, "%u\n", + mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl)); +} + +static ssize_t mlxsw_hwmon_pwm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + container_of(attr, struct mlxsw_hwmon_attr, dev_attr); + struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + char mfsc_pl[MLXSW_REG_MFSC_LEN]; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + if (val > 255) + return -EINVAL; + + mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, val); + err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl); + if (err) { + dev_err(mlxsw_hwmon->bus_info->dev, "Failed to write PWM\n"); + return err; + } + return err ? err : len; +} + enum mlxsw_hwmon_attr_type { MLXSW_HWMON_ATTR_TYPE_TEMP, MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, + MLXSW_HWMON_ATTR_TYPE_FAN_RPM, + MLXSW_HWMON_ATTR_TYPE_PWM, }; static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon, @@ -133,6 +201,19 @@ static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon, snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), "temp%u_highest", num + 1); break; + case MLXSW_HWMON_ATTR_TYPE_FAN_RPM: + mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_rpm_show; + mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO; + snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), + "fan%u_input", num + 1); + break; + case MLXSW_HWMON_ATTR_TYPE_PWM: + mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show; + mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store; + mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR | S_IRUGO; + snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), + "pwm%u", num + 1); + break; default: BUG(); } @@ -177,6 +258,39 @@ static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon) return 0; } +static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon) +{ + char mfcr_pl[MLXSW_REG_MFCR_LEN]; + enum mlxsw_reg_mfcr_pwm_frequency freq; + unsigned int type_index; + unsigned int num; + u16 tacho_active; + u8 pwm_active; + int err; + + err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfcr), mfcr_pl); + if (err) { + dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get to probe PWMs and Tachometers\n"); + return err; + } + mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active); + num = 0; + for (type_index = 0; type_index < MLXSW_MFCR_TACHOS_MAX; type_index++) { + if (tacho_active & BIT(type_index)) + mlxsw_hwmon_attr_add(mlxsw_hwmon, + MLXSW_HWMON_ATTR_TYPE_FAN_RPM, + type_index, num++); + } + num = 0; + for (type_index = 0; type_index < MLXSW_MFCR_PWMS_MAX; type_index++) { + if (pwm_active & BIT(type_index)) + mlxsw_hwmon_attr_add(mlxsw_hwmon, + MLXSW_HWMON_ATTR_TYPE_PWM, + type_index, num++); + } + return 0; +} + int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *mlxsw_bus_info, struct mlxsw_hwmon **p_hwmon) @@ -195,6 +309,10 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, if (err) goto err_temp_init; + err = mlxsw_hwmon_fans_init(mlxsw_hwmon); + if (err) + goto err_fans_init; + mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group; mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs; @@ -212,6 +330,7 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, return 0; err_hwmon_register: +err_fans_init: err_temp_init: kfree(mlxsw_hwmon); return err; -- cgit v1.2.3-59-g8ed1b From 2f89a5d7d37706a19c2e3d338a9654bfabc5b21b Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Tue, 27 Oct 2015 19:17:14 +0200 Subject: iwlwifi: mvm: move fw-dbg code to separate file The fw debug functionality is big enough to warrant a separate file. Move existing related functions to the new file. Signed-off-by: Golan Ben-Ami Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-fw.h | 17 - drivers/net/wireless/intel/iwlwifi/mvm/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 780 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h | 150 ++++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 132 +--- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 580 +-------------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 59 -- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 1 + .../net/wireless/intel/iwlwifi/mvm/time-event.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 2 +- 14 files changed, 941 insertions(+), 787 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h index c6946f1644fc..2e1909fcd3ba 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h @@ -305,21 +305,4 @@ iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id) return conf_tlv->usniffer; } -#define iwl_fw_dbg_trigger_enabled(fw, id) ({ \ - void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \ - unlikely(__dbg_trigger); \ -}) - -static inline struct iwl_fw_dbg_trigger_tlv* -_iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id) -{ - return fw->dbg_trigger_tlv[id]; -} - -#define iwl_fw_dbg_get_trigger(fw, id) ({ \ - BUILD_BUG_ON(!__builtin_constant_p(id)); \ - BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \ - _iwl_fw_dbg_get_trigger((fw), (id)); \ -}) - #endif /* __iwl_fw_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile index 8c2c3d13b092..2c0d20f2a918 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile @@ -6,7 +6,7 @@ iwlmvm-y += power.o coex.o coex_legacy.o iwlmvm-y += tt.o offloading.o tdls.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o -iwlmvm-y += tof.o +iwlmvm-y += tof.o fw-dbg.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 48d5ee1b026c..3f7682da99e7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -65,6 +65,7 @@ #include #include "mvm.h" +#include "fw-dbg.h" #include "sta.h" #include "iwl-io.h" #include "debugfs.h" diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c new file mode 100644 index 000000000000..b8e0591970ae --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -0,0 +1,780 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include + +#include "fw-dbg.h" +#include "iwl-io.h" +#include "mvm.h" +#include "iwl-prph.h" +#include "iwl-csr.h" + +static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, + const void *data, size_t datalen) +{ + const struct iwl_mvm_dump_ptrs *dump_ptrs = data; + ssize_t bytes_read; + ssize_t bytes_read_trans; + + if (offset < dump_ptrs->op_mode_len) { + bytes_read = min_t(ssize_t, count, + dump_ptrs->op_mode_len - offset); + memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset, + bytes_read); + offset += bytes_read; + count -= bytes_read; + + if (count == 0) + return bytes_read; + } else { + bytes_read = 0; + } + + if (!dump_ptrs->trans_ptr) + return bytes_read; + + offset -= dump_ptrs->op_mode_len; + bytes_read_trans = min_t(ssize_t, count, + dump_ptrs->trans_ptr->len - offset); + memcpy(buffer + bytes_read, + (u8 *)dump_ptrs->trans_ptr->data + offset, + bytes_read_trans); + + return bytes_read + bytes_read_trans; +} + +static void iwl_mvm_free_coredump(const void *data) +{ + const struct iwl_mvm_dump_ptrs *fw_error_dump = data; + + vfree(fw_error_dump->op_mode_ptr); + vfree(fw_error_dump->trans_ptr); + kfree(fw_error_dump); +} + +static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, + struct iwl_fw_error_dump_data **dump_data) +{ + struct iwl_fw_error_dump_fifo *fifo_hdr; + u32 *fifo_data; + u32 fifo_len; + unsigned long flags; + int i, j; + + if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) + return; + + /* Pull RXF data from all RXFs */ + for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) { + /* + * Keep aside the additional offset that might be needed for + * next RXF + */ + u32 offset_diff = RXF_DIFF_FROM_PREV * i; + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = mvm->shared_mem_cfg.rxfifo_size[i]; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + continue; + + /* Add a TLV for the RXF */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(i); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_D_SPACE + + offset_diff)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_WR_PTR + + offset_diff)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_RD_PTR + + offset_diff)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_FENCE_PTR + + offset_diff)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_SET_FENCE_MODE + + offset_diff)); + + /* Lock fence */ + iwl_trans_write_prph(mvm->trans, + RXF_SET_FENCE_MODE + offset_diff, 0x1); + /* Set fence pointer to the same place like WR pointer */ + iwl_trans_write_prph(mvm->trans, + RXF_LD_WR2FENCE + offset_diff, 0x1); + /* Set fence offset */ + iwl_trans_write_prph(mvm->trans, + RXF_LD_FENCE_OFFSET_ADDR + offset_diff, + 0x0); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (j = 0; j < fifo_len; j++) + fifo_data[j] = iwl_trans_read_prph(mvm->trans, + RXF_FIFO_RD_FENCE_INC + + offset_diff); + *dump_data = iwl_fw_error_next_data(*dump_data); + } + + /* Pull TXF data from all TXFs */ + for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) { + /* Mark the number of TXF we're pulling now */ + iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = mvm->shared_mem_cfg.txfifo_size[i]; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + continue; + + /* Add a TLV for the FIFO */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(i); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_FIFO_ITEM_CNT)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_WR_PTR)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_RD_PTR)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_FENCE_PTR)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_LOCK_FENCE)); + + /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ + iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR, + TXF_WR_PTR); + + /* Dummy-read to advance the read pointer to the head */ + iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (j = 0; j < fifo_len; j++) + fifo_data[j] = iwl_trans_read_prph(mvm->trans, + TXF_READ_MODIFY_DATA); + *dump_data = iwl_fw_error_next_data(*dump_data); + } + + iwl_trans_release_nic_access(mvm->trans, &flags); +} + +void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) +{ + if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert || + !mvm->fw_dump_desc) + return; + + kfree(mvm->fw_dump_desc); + mvm->fw_dump_desc = NULL; +} + +#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ +#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ + +static const struct { + u32 start, end; +} iwl_prph_dump_addr[] = { + { .start = 0x00a00000, .end = 0x00a00000 }, + { .start = 0x00a0000c, .end = 0x00a00024 }, + { .start = 0x00a0002c, .end = 0x00a0003c }, + { .start = 0x00a00410, .end = 0x00a00418 }, + { .start = 0x00a00420, .end = 0x00a00420 }, + { .start = 0x00a00428, .end = 0x00a00428 }, + { .start = 0x00a00430, .end = 0x00a0043c }, + { .start = 0x00a00444, .end = 0x00a00444 }, + { .start = 0x00a004c0, .end = 0x00a004cc }, + { .start = 0x00a004d8, .end = 0x00a004d8 }, + { .start = 0x00a004e0, .end = 0x00a004f0 }, + { .start = 0x00a00840, .end = 0x00a00840 }, + { .start = 0x00a00850, .end = 0x00a00858 }, + { .start = 0x00a01004, .end = 0x00a01008 }, + { .start = 0x00a01010, .end = 0x00a01010 }, + { .start = 0x00a01018, .end = 0x00a01018 }, + { .start = 0x00a01024, .end = 0x00a01024 }, + { .start = 0x00a0102c, .end = 0x00a01034 }, + { .start = 0x00a0103c, .end = 0x00a01040 }, + { .start = 0x00a01048, .end = 0x00a01094 }, + { .start = 0x00a01c00, .end = 0x00a01c20 }, + { .start = 0x00a01c58, .end = 0x00a01c58 }, + { .start = 0x00a01c7c, .end = 0x00a01c7c }, + { .start = 0x00a01c28, .end = 0x00a01c54 }, + { .start = 0x00a01c5c, .end = 0x00a01c5c }, + { .start = 0x00a01c60, .end = 0x00a01cdc }, + { .start = 0x00a01ce0, .end = 0x00a01d0c }, + { .start = 0x00a01d18, .end = 0x00a01d20 }, + { .start = 0x00a01d2c, .end = 0x00a01d30 }, + { .start = 0x00a01d40, .end = 0x00a01d5c }, + { .start = 0x00a01d80, .end = 0x00a01d80 }, + { .start = 0x00a01d98, .end = 0x00a01d9c }, + { .start = 0x00a01da8, .end = 0x00a01da8 }, + { .start = 0x00a01db8, .end = 0x00a01df4 }, + { .start = 0x00a01dc0, .end = 0x00a01dfc }, + { .start = 0x00a01e00, .end = 0x00a01e2c }, + { .start = 0x00a01e40, .end = 0x00a01e60 }, + { .start = 0x00a01e68, .end = 0x00a01e6c }, + { .start = 0x00a01e74, .end = 0x00a01e74 }, + { .start = 0x00a01e84, .end = 0x00a01e90 }, + { .start = 0x00a01e9c, .end = 0x00a01ec4 }, + { .start = 0x00a01ed0, .end = 0x00a01ee0 }, + { .start = 0x00a01f00, .end = 0x00a01f1c }, + { .start = 0x00a01f44, .end = 0x00a01ffc }, + { .start = 0x00a02000, .end = 0x00a02048 }, + { .start = 0x00a02068, .end = 0x00a020f0 }, + { .start = 0x00a02100, .end = 0x00a02118 }, + { .start = 0x00a02140, .end = 0x00a0214c }, + { .start = 0x00a02168, .end = 0x00a0218c }, + { .start = 0x00a021c0, .end = 0x00a021c0 }, + { .start = 0x00a02400, .end = 0x00a02410 }, + { .start = 0x00a02418, .end = 0x00a02420 }, + { .start = 0x00a02428, .end = 0x00a0242c }, + { .start = 0x00a02434, .end = 0x00a02434 }, + { .start = 0x00a02440, .end = 0x00a02460 }, + { .start = 0x00a02468, .end = 0x00a024b0 }, + { .start = 0x00a024c8, .end = 0x00a024cc }, + { .start = 0x00a02500, .end = 0x00a02504 }, + { .start = 0x00a0250c, .end = 0x00a02510 }, + { .start = 0x00a02540, .end = 0x00a02554 }, + { .start = 0x00a02580, .end = 0x00a025f4 }, + { .start = 0x00a02600, .end = 0x00a0260c }, + { .start = 0x00a02648, .end = 0x00a02650 }, + { .start = 0x00a02680, .end = 0x00a02680 }, + { .start = 0x00a026c0, .end = 0x00a026d0 }, + { .start = 0x00a02700, .end = 0x00a0270c }, + { .start = 0x00a02804, .end = 0x00a02804 }, + { .start = 0x00a02818, .end = 0x00a0281c }, + { .start = 0x00a02c00, .end = 0x00a02db4 }, + { .start = 0x00a02df4, .end = 0x00a02fb0 }, + { .start = 0x00a03000, .end = 0x00a03014 }, + { .start = 0x00a0301c, .end = 0x00a0302c }, + { .start = 0x00a03034, .end = 0x00a03038 }, + { .start = 0x00a03040, .end = 0x00a03048 }, + { .start = 0x00a03060, .end = 0x00a03068 }, + { .start = 0x00a03070, .end = 0x00a03074 }, + { .start = 0x00a0307c, .end = 0x00a0307c }, + { .start = 0x00a03080, .end = 0x00a03084 }, + { .start = 0x00a0308c, .end = 0x00a03090 }, + { .start = 0x00a03098, .end = 0x00a03098 }, + { .start = 0x00a030a0, .end = 0x00a030a0 }, + { .start = 0x00a030a8, .end = 0x00a030b4 }, + { .start = 0x00a030bc, .end = 0x00a030bc }, + { .start = 0x00a030c0, .end = 0x00a0312c }, + { .start = 0x00a03c00, .end = 0x00a03c5c }, + { .start = 0x00a04400, .end = 0x00a04454 }, + { .start = 0x00a04460, .end = 0x00a04474 }, + { .start = 0x00a044c0, .end = 0x00a044ec }, + { .start = 0x00a04500, .end = 0x00a04504 }, + { .start = 0x00a04510, .end = 0x00a04538 }, + { .start = 0x00a04540, .end = 0x00a04548 }, + { .start = 0x00a04560, .end = 0x00a0457c }, + { .start = 0x00a04590, .end = 0x00a04598 }, + { .start = 0x00a045c0, .end = 0x00a045f4 }, +}; + +static u32 iwl_dump_prph(struct iwl_trans *trans, + struct iwl_fw_error_dump_data **data) +{ + struct iwl_fw_error_dump_prph *prph; + unsigned long flags; + u32 prph_len = 0, i; + + if (!iwl_trans_grab_nic_access(trans, false, &flags)) + return 0; + + for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4; + int reg; + __le32 *val; + + prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk; + + (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); + (*data)->len = cpu_to_le32(sizeof(*prph) + + num_bytes_in_chunk); + prph = (void *)(*data)->data; + prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); + val = (void *)prph->data; + + for (reg = iwl_prph_dump_addr[i].start; + reg <= iwl_prph_dump_addr[i].end; + reg += 4) + *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans, + reg)); + + *data = iwl_fw_error_next_data(*data); + } + + iwl_trans_release_nic_access(trans, &flags); + + return prph_len; +} + +void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) +{ + struct iwl_fw_error_dump_file *dump_file; + struct iwl_fw_error_dump_data *dump_data; + struct iwl_fw_error_dump_info *dump_info; + struct iwl_fw_error_dump_mem *dump_mem; + struct iwl_fw_error_dump_trigger_desc *dump_trig; + struct iwl_mvm_dump_ptrs *fw_error_dump; + u32 sram_len, sram_ofs; + u32 file_len, fifo_data_len = 0; + u32 smem_len = mvm->cfg->smem_len; + u32 sram2_len = mvm->cfg->dccm2_len; + bool monitor_dump_only = false; + int i; + + lockdep_assert_held(&mvm->mutex); + + /* there's no point in fw dump if the bus is dead */ + if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { + IWL_ERR(mvm, "Skip fw error dump since bus is dead\n"); + return; + } + + if (mvm->fw_dump_trig && + mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY) + monitor_dump_only = true; + + fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); + if (!fw_error_dump) + return; + + /* SRAM - include stack CCM if driver knows the values for it */ + if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { + const struct fw_img *img; + + img = &mvm->fw->img[mvm->cur_ucode]; + sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + } else { + sram_ofs = mvm->cfg->dccm_offset; + sram_len = mvm->cfg->dccm_len; + } + + /* reading RXF/TXF sizes */ + if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { + struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg; + + fifo_data_len = 0; + + /* Count RXF size */ + for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) { + if (!mem_cfg->rxfifo_size[i]) + continue; + + /* Add header info */ + fifo_data_len += mem_cfg->rxfifo_size[i] + + sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + } + + for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) { + if (!mem_cfg->txfifo_size[i]) + continue; + + /* Add header info */ + fifo_data_len += mem_cfg->txfifo_size[i] + + sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + } + } + + file_len = sizeof(*dump_file) + + sizeof(*dump_data) * 2 + + sram_len + sizeof(*dump_mem) + + fifo_data_len + + sizeof(*dump_info); + + /* Make room for the SMEM, if it exists */ + if (smem_len) + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; + + /* Make room for the secondary SRAM, if it exists */ + if (sram2_len) + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; + + /* Make room for fw's virtual image pages, if it exists */ + if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) + file_len += mvm->num_of_paging_blk * + (sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_paging) + + PAGING_BLOCK_SIZE); + + /* If we only want a monitor dump, reset the file length */ + if (monitor_dump_only) { + file_len = sizeof(*dump_file) + sizeof(*dump_data) + + sizeof(*dump_info); + } + + /* Make room for PRPH registers */ + for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4; + + file_len += sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_prph) + + num_bytes_in_chunk; + } + + /* + * In 8000 HW family B-step include the ICCM (which resides separately) + */ + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && + CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + + IWL8260_ICCM_LEN; + + if (mvm->fw_dump_desc) + file_len += sizeof(*dump_data) + sizeof(*dump_trig) + + mvm->fw_dump_desc->len; + + dump_file = vzalloc(file_len); + if (!dump_file) { + kfree(fw_error_dump); + iwl_mvm_free_fw_dump_desc(mvm); + return; + } + + fw_error_dump->op_mode_ptr = dump_file; + + dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); + dump_data = (void *)dump_file->data; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_info)); + dump_info = (void *)dump_data->data; + dump_info->device_family = + mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); + dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev)); + memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, + sizeof(dump_info->fw_human_readable)); + strncpy(dump_info->dev_human_readable, mvm->cfg->name, + sizeof(dump_info->dev_human_readable)); + strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, + sizeof(dump_info->bus_human_readable)); + + dump_data = iwl_fw_error_next_data(dump_data); + /* We only dump the FIFOs if the FW is in error state */ + if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) + iwl_mvm_dump_fifos(mvm, &dump_data); + + if (mvm->fw_dump_desc) { + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_trig) + + mvm->fw_dump_desc->len); + dump_trig = (void *)dump_data->data; + memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc, + sizeof(*dump_trig) + mvm->fw_dump_desc->len); + + /* now we can free this copy */ + iwl_mvm_free_fw_dump_desc(mvm); + dump_data = iwl_fw_error_next_data(dump_data); + } + + /* In case we only want monitor dump, skip to dump trasport data */ + if (monitor_dump_only) + goto dump_trans_data; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(sram_ofs); + iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data, + sram_len); + + if (smem_len) { + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); + dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset); + iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, + dump_mem->data, smem_len); + } + + if (sram2_len) { + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset); + iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset, + dump_mem->data, sram2_len); + } + + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && + CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) { + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN + + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET); + iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET, + dump_mem->data, IWL8260_ICCM_LEN); + } + + /* Dump fw's virtual image */ + if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) { + u32 i; + + for (i = 1; i < mvm->num_of_paging_blk + 1; i++) { + struct iwl_fw_error_dump_paging *paging; + struct page *pages = + mvm->fw_paging_db[i].fw_paging_block; + + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); + dump_data->len = cpu_to_le32(sizeof(*paging) + + PAGING_BLOCK_SIZE); + paging = (void *)dump_data->data; + paging->index = cpu_to_le32(i); + memcpy(paging->data, page_address(pages), + PAGING_BLOCK_SIZE); + } + } + + dump_data = iwl_fw_error_next_data(dump_data); + iwl_dump_prph(mvm->trans, &dump_data); + +dump_trans_data: + fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans, + mvm->fw_dump_trig); + fw_error_dump->op_mode_len = file_len; + if (fw_error_dump->trans_ptr) + file_len += fw_error_dump->trans_ptr->len; + dump_file->file_len = cpu_to_le32(file_len); + + dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, + GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); + + mvm->fw_dump_trig = NULL; + clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); +} + +struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { + .trig_desc = { + .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), + }, +}; + +int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, + struct iwl_mvm_dump_desc *desc, + struct iwl_fw_dbg_trigger_tlv *trigger) +{ + unsigned int delay = 0; + + if (trigger) + delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); + + if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status)) + return -EBUSY; + + if (WARN_ON(mvm->fw_dump_desc)) + iwl_mvm_free_fw_dump_desc(mvm); + + IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", + le32_to_cpu(desc->trig_desc.type)); + + mvm->fw_dump_desc = desc; + mvm->fw_dump_trig = trigger; + + queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay); + + return 0; +} + +int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, + const char *str, size_t len, + struct iwl_fw_dbg_trigger_tlv *trigger) +{ + struct iwl_mvm_dump_desc *desc; + + desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); + if (!desc) + return -ENOMEM; + + desc->len = len; + desc->trig_desc.type = cpu_to_le32(trig); + memcpy(desc->trig_desc.data, str, len); + + return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger); +} + +int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, + struct iwl_fw_dbg_trigger_tlv *trigger, + const char *fmt, ...) +{ + u16 occurrences = le16_to_cpu(trigger->occurrences); + int ret, len = 0; + char buf[64]; + + if (!occurrences) + return 0; + + if (fmt) { + va_list ap; + + buf[sizeof(buf) - 1] = '\0'; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + /* check for truncation */ + if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) + buf[sizeof(buf) - 1] = '\0'; + + len = strlen(buf) + 1; + } + + ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len, + trigger); + + if (ret) + return ret; + + trigger->occurrences = cpu_to_le16(occurrences - 1); + return 0; +} + +static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm) +{ + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) + iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); + else + iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1); +} + +int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id) +{ + u8 *ptr; + int ret; + int i; + + if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv), + "Invalid configuration %d\n", conf_id)) + return -EINVAL; + + /* EARLY START - firmware's configuration is hard coded */ + if ((!mvm->fw->dbg_conf_tlv[conf_id] || + !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) && + conf_id == FW_DBG_START_FROM_ALIVE) { + iwl_mvm_restart_early_start(mvm); + return 0; + } + + if (!mvm->fw->dbg_conf_tlv[conf_id]) + return -EINVAL; + + if (mvm->fw_dbg_conf != FW_DBG_INVALID) + IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n", + mvm->fw_dbg_conf); + + /* Send all HCMDs for configuring the FW debug */ + ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd; + for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) { + struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr; + + ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0, + le16_to_cpu(cmd->len), cmd->data); + if (ret) + return ret; + + ptr += sizeof(*cmd); + ptr += le16_to_cpu(cmd->len); + } + + mvm->fw_dbg_conf = conf_id; + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h new file mode 100644 index 000000000000..32232d69ea44 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h @@ -0,0 +1,150 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __mvm_fw_dbg_h__ +#define __mvm_fw_dbg_h__ +#include "iwl-fw-file.h" +#include "iwl-fw-error-dump.h" +#include "mvm.h" + +void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); +void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm); +int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, + struct iwl_mvm_dump_desc *desc, + struct iwl_fw_dbg_trigger_tlv *trigger); +int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, + const char *str, size_t len, + struct iwl_fw_dbg_trigger_tlv *trigger); +int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, + struct iwl_fw_dbg_trigger_tlv *trigger, + const char *fmt, ...) __printf(3, 4); +int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id); + +#define iwl_fw_dbg_trigger_enabled(fw, id) ({ \ + void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \ + unlikely(__dbg_trigger); \ +}) + +static inline struct iwl_fw_dbg_trigger_tlv* +_iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id) +{ + return fw->dbg_trigger_tlv[id]; +} + +#define iwl_fw_dbg_get_trigger(fw, id) ({ \ + BUILD_BUG_ON(!__builtin_constant_p(id)); \ + BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \ + _iwl_fw_dbg_get_trigger((fw), (id)); \ +}) + +static inline bool +iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig, + struct ieee80211_vif *vif) +{ + u32 trig_vif = le32_to_cpu(trig->vif_type); + + return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif; +} + +static inline bool +iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm, + struct iwl_fw_dbg_trigger_tlv *trig) +{ + return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) && + (mvm->fw_dbg_conf == FW_DBG_INVALID || + (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids)))); +} + +static inline bool +iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_fw_dbg_trigger_tlv *trig) +{ + if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif)) + return false; + + return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig); +} + +static inline void +_iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_fw_dbg_trigger_tlv *trigger) +{ + if (!trigger) + return; + + if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) + return; + + iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); +} + +#define iwl_fw_dbg_trigger_simple_stop(mvm, vif, trig) \ + _iwl_fw_dbg_trigger_simple_stop((mvm), (vif), \ + iwl_fw_dbg_get_trigger((mvm)->fw,\ + (trig))) + +#endif /* __mvm_fw_dbg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index d906fa13ba97..05c933313f97 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -74,6 +74,7 @@ #include "iwl-eeprom-parse.h" #include "mvm.h" +#include "fw-dbg.h" #include "iwl-phy-db.h" #define MVM_UCODE_ALIVE_TIMEOUT HZ @@ -805,137 +806,6 @@ static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm) iwl_free_resp(&cmd); } -int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - struct iwl_mvm_dump_desc *desc, - struct iwl_fw_dbg_trigger_tlv *trigger) -{ - unsigned int delay = 0; - - if (trigger) - delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); - - if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status)) - return -EBUSY; - - if (WARN_ON(mvm->fw_dump_desc)) - iwl_mvm_free_fw_dump_desc(mvm); - - IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", - le32_to_cpu(desc->trig_desc.type)); - - mvm->fw_dump_desc = desc; - mvm->fw_dump_trig = trigger; - - queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay); - - return 0; -} - -int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, - const char *str, size_t len, - struct iwl_fw_dbg_trigger_tlv *trigger) -{ - struct iwl_mvm_dump_desc *desc; - - desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); - if (!desc) - return -ENOMEM; - - desc->len = len; - desc->trig_desc.type = cpu_to_le32(trig); - memcpy(desc->trig_desc.data, str, len); - - return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger); -} - -int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trigger, - const char *fmt, ...) -{ - u16 occurrences = le16_to_cpu(trigger->occurrences); - int ret, len = 0; - char buf[64]; - - if (!occurrences) - return 0; - - if (fmt) { - va_list ap; - - buf[sizeof(buf) - 1] = '\0'; - - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - /* check for truncation */ - if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) - buf[sizeof(buf) - 1] = '\0'; - - len = strlen(buf) + 1; - } - - ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len, - trigger); - - if (ret) - return ret; - - trigger->occurrences = cpu_to_le16(occurrences - 1); - return 0; -} - -static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm) -{ - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) - iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); - else - iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1); -} - -int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id) -{ - u8 *ptr; - int ret; - int i; - - if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv), - "Invalid configuration %d\n", conf_id)) - return -EINVAL; - - /* EARLY START - firmware's configuration is hard coded */ - if ((!mvm->fw->dbg_conf_tlv[conf_id] || - !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) && - conf_id == FW_DBG_START_FROM_ALIVE) { - iwl_mvm_restart_early_start(mvm); - return 0; - } - - if (!mvm->fw->dbg_conf_tlv[conf_id]) - return -EINVAL; - - if (mvm->fw_dbg_conf != FW_DBG_INVALID) - IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n", - mvm->fw_dbg_conf); - - /* Send all HCMDs for configuring the FW debug */ - ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd; - for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) { - struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr; - - ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0, - le16_to_cpu(cmd->len), cmd->data); - if (ret) - return ret; - - ptr += sizeof(*cmd); - ptr += le16_to_cpu(cmd->len); - } - - mvm->fw_dbg_conf = conf_id; - return ret; -} - static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) { struct iwl_ltr_config_cmd cmd = { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index ad7ad720d2e7..ee511aae727d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -72,6 +72,7 @@ #include "fw-api.h" #include "mvm.h" #include "time-event.h" +#include "fw-dbg.h" const u8 iwl_mvm_ac_to_tx_fifo[] = { IWL_MVM_TX_FIFO_VO, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index b83334f27b44..327703d5d4ba 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -70,6 +70,7 @@ #include #include #include +#include #include #include #include @@ -86,6 +87,7 @@ #include "iwl-prph.h" #include "iwl-csr.h" #include "iwl-nvm-parse.h" +#include "fw-dbg.h" static const struct ieee80211_iface_limit iwl_mvm_limits[] = { { @@ -939,584 +941,6 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data)); } -static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, - const void *data, size_t datalen) -{ - const struct iwl_mvm_dump_ptrs *dump_ptrs = data; - ssize_t bytes_read; - ssize_t bytes_read_trans; - - if (offset < dump_ptrs->op_mode_len) { - bytes_read = min_t(ssize_t, count, - dump_ptrs->op_mode_len - offset); - memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset, - bytes_read); - offset += bytes_read; - count -= bytes_read; - - if (count == 0) - return bytes_read; - } else { - bytes_read = 0; - } - - if (!dump_ptrs->trans_ptr) - return bytes_read; - - offset -= dump_ptrs->op_mode_len; - bytes_read_trans = min_t(ssize_t, count, - dump_ptrs->trans_ptr->len - offset); - memcpy(buffer + bytes_read, - (u8 *)dump_ptrs->trans_ptr->data + offset, - bytes_read_trans); - - return bytes_read + bytes_read_trans; -} - -static void iwl_mvm_free_coredump(const void *data) -{ - const struct iwl_mvm_dump_ptrs *fw_error_dump = data; - - vfree(fw_error_dump->op_mode_ptr); - vfree(fw_error_dump->trans_ptr); - kfree(fw_error_dump); -} - -static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, - struct iwl_fw_error_dump_data **dump_data) -{ - struct iwl_fw_error_dump_fifo *fifo_hdr; - u32 *fifo_data; - u32 fifo_len; - unsigned long flags; - int i, j; - - if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) - return; - - /* Pull RXF data from all RXFs */ - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) { - /* - * Keep aside the additional offset that might be needed for - * next RXF - */ - u32 offset_diff = RXF_DIFF_FROM_PREV * i; - - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = mvm->shared_mem_cfg.rxfifo_size[i]; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - continue; - - /* Add a TLV for the RXF */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(i); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_D_SPACE + - offset_diff)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_WR_PTR + - offset_diff)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_RD_PTR + - offset_diff)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_FENCE_PTR + - offset_diff)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_SET_FENCE_MODE + - offset_diff)); - - /* Lock fence */ - iwl_trans_write_prph(mvm->trans, - RXF_SET_FENCE_MODE + offset_diff, 0x1); - /* Set fence pointer to the same place like WR pointer */ - iwl_trans_write_prph(mvm->trans, - RXF_LD_WR2FENCE + offset_diff, 0x1); - /* Set fence offset */ - iwl_trans_write_prph(mvm->trans, - RXF_LD_FENCE_OFFSET_ADDR + offset_diff, - 0x0); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (j = 0; j < fifo_len; j++) - fifo_data[j] = iwl_trans_read_prph(mvm->trans, - RXF_FIFO_RD_FENCE_INC + - offset_diff); - *dump_data = iwl_fw_error_next_data(*dump_data); - } - - /* Pull TXF data from all TXFs */ - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) { - /* Mark the number of TXF we're pulling now */ - iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); - - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = mvm->shared_mem_cfg.txfifo_size[i]; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - continue; - - /* Add a TLV for the FIFO */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(i); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_FIFO_ITEM_CNT)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_WR_PTR)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_RD_PTR)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_FENCE_PTR)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_LOCK_FENCE)); - - /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ - iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR, - TXF_WR_PTR); - - /* Dummy-read to advance the read pointer to the head */ - iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (j = 0; j < fifo_len; j++) - fifo_data[j] = iwl_trans_read_prph(mvm->trans, - TXF_READ_MODIFY_DATA); - *dump_data = iwl_fw_error_next_data(*dump_data); - } - - iwl_trans_release_nic_access(mvm->trans, &flags); -} - -void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) -{ - if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert || - !mvm->fw_dump_desc) - return; - - kfree(mvm->fw_dump_desc); - mvm->fw_dump_desc = NULL; -} - -#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ -#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ - -static const struct { - u32 start, end; -} iwl_prph_dump_addr[] = { - { .start = 0x00a00000, .end = 0x00a00000 }, - { .start = 0x00a0000c, .end = 0x00a00024 }, - { .start = 0x00a0002c, .end = 0x00a0003c }, - { .start = 0x00a00410, .end = 0x00a00418 }, - { .start = 0x00a00420, .end = 0x00a00420 }, - { .start = 0x00a00428, .end = 0x00a00428 }, - { .start = 0x00a00430, .end = 0x00a0043c }, - { .start = 0x00a00444, .end = 0x00a00444 }, - { .start = 0x00a004c0, .end = 0x00a004cc }, - { .start = 0x00a004d8, .end = 0x00a004d8 }, - { .start = 0x00a004e0, .end = 0x00a004f0 }, - { .start = 0x00a00840, .end = 0x00a00840 }, - { .start = 0x00a00850, .end = 0x00a00858 }, - { .start = 0x00a01004, .end = 0x00a01008 }, - { .start = 0x00a01010, .end = 0x00a01010 }, - { .start = 0x00a01018, .end = 0x00a01018 }, - { .start = 0x00a01024, .end = 0x00a01024 }, - { .start = 0x00a0102c, .end = 0x00a01034 }, - { .start = 0x00a0103c, .end = 0x00a01040 }, - { .start = 0x00a01048, .end = 0x00a01094 }, - { .start = 0x00a01c00, .end = 0x00a01c20 }, - { .start = 0x00a01c58, .end = 0x00a01c58 }, - { .start = 0x00a01c7c, .end = 0x00a01c7c }, - { .start = 0x00a01c28, .end = 0x00a01c54 }, - { .start = 0x00a01c5c, .end = 0x00a01c5c }, - { .start = 0x00a01c60, .end = 0x00a01cdc }, - { .start = 0x00a01ce0, .end = 0x00a01d0c }, - { .start = 0x00a01d18, .end = 0x00a01d20 }, - { .start = 0x00a01d2c, .end = 0x00a01d30 }, - { .start = 0x00a01d40, .end = 0x00a01d5c }, - { .start = 0x00a01d80, .end = 0x00a01d80 }, - { .start = 0x00a01d98, .end = 0x00a01d9c }, - { .start = 0x00a01da8, .end = 0x00a01da8 }, - { .start = 0x00a01db8, .end = 0x00a01df4 }, - { .start = 0x00a01dc0, .end = 0x00a01dfc }, - { .start = 0x00a01e00, .end = 0x00a01e2c }, - { .start = 0x00a01e40, .end = 0x00a01e60 }, - { .start = 0x00a01e68, .end = 0x00a01e6c }, - { .start = 0x00a01e74, .end = 0x00a01e74 }, - { .start = 0x00a01e84, .end = 0x00a01e90 }, - { .start = 0x00a01e9c, .end = 0x00a01ec4 }, - { .start = 0x00a01ed0, .end = 0x00a01ee0 }, - { .start = 0x00a01f00, .end = 0x00a01f1c }, - { .start = 0x00a01f44, .end = 0x00a01ffc }, - { .start = 0x00a02000, .end = 0x00a02048 }, - { .start = 0x00a02068, .end = 0x00a020f0 }, - { .start = 0x00a02100, .end = 0x00a02118 }, - { .start = 0x00a02140, .end = 0x00a0214c }, - { .start = 0x00a02168, .end = 0x00a0218c }, - { .start = 0x00a021c0, .end = 0x00a021c0 }, - { .start = 0x00a02400, .end = 0x00a02410 }, - { .start = 0x00a02418, .end = 0x00a02420 }, - { .start = 0x00a02428, .end = 0x00a0242c }, - { .start = 0x00a02434, .end = 0x00a02434 }, - { .start = 0x00a02440, .end = 0x00a02460 }, - { .start = 0x00a02468, .end = 0x00a024b0 }, - { .start = 0x00a024c8, .end = 0x00a024cc }, - { .start = 0x00a02500, .end = 0x00a02504 }, - { .start = 0x00a0250c, .end = 0x00a02510 }, - { .start = 0x00a02540, .end = 0x00a02554 }, - { .start = 0x00a02580, .end = 0x00a025f4 }, - { .start = 0x00a02600, .end = 0x00a0260c }, - { .start = 0x00a02648, .end = 0x00a02650 }, - { .start = 0x00a02680, .end = 0x00a02680 }, - { .start = 0x00a026c0, .end = 0x00a026d0 }, - { .start = 0x00a02700, .end = 0x00a0270c }, - { .start = 0x00a02804, .end = 0x00a02804 }, - { .start = 0x00a02818, .end = 0x00a0281c }, - { .start = 0x00a02c00, .end = 0x00a02db4 }, - { .start = 0x00a02df4, .end = 0x00a02fb0 }, - { .start = 0x00a03000, .end = 0x00a03014 }, - { .start = 0x00a0301c, .end = 0x00a0302c }, - { .start = 0x00a03034, .end = 0x00a03038 }, - { .start = 0x00a03040, .end = 0x00a03048 }, - { .start = 0x00a03060, .end = 0x00a03068 }, - { .start = 0x00a03070, .end = 0x00a03074 }, - { .start = 0x00a0307c, .end = 0x00a0307c }, - { .start = 0x00a03080, .end = 0x00a03084 }, - { .start = 0x00a0308c, .end = 0x00a03090 }, - { .start = 0x00a03098, .end = 0x00a03098 }, - { .start = 0x00a030a0, .end = 0x00a030a0 }, - { .start = 0x00a030a8, .end = 0x00a030b4 }, - { .start = 0x00a030bc, .end = 0x00a030bc }, - { .start = 0x00a030c0, .end = 0x00a0312c }, - { .start = 0x00a03c00, .end = 0x00a03c5c }, - { .start = 0x00a04400, .end = 0x00a04454 }, - { .start = 0x00a04460, .end = 0x00a04474 }, - { .start = 0x00a044c0, .end = 0x00a044ec }, - { .start = 0x00a04500, .end = 0x00a04504 }, - { .start = 0x00a04510, .end = 0x00a04538 }, - { .start = 0x00a04540, .end = 0x00a04548 }, - { .start = 0x00a04560, .end = 0x00a0457c }, - { .start = 0x00a04590, .end = 0x00a04598 }, - { .start = 0x00a045c0, .end = 0x00a045f4 }, -}; - -static u32 iwl_dump_prph(struct iwl_trans *trans, - struct iwl_fw_error_dump_data **data) -{ - struct iwl_fw_error_dump_prph *prph; - unsigned long flags; - u32 prph_len = 0, i; - - if (!iwl_trans_grab_nic_access(trans, false, &flags)) - return 0; - - for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4; - int reg; - __le32 *val; - - prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk; - - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); - (*data)->len = cpu_to_le32(sizeof(*prph) + - num_bytes_in_chunk); - prph = (void *)(*data)->data; - prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); - val = (void *)prph->data; - - for (reg = iwl_prph_dump_addr[i].start; - reg <= iwl_prph_dump_addr[i].end; - reg += 4) - *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans, - reg)); - - *data = iwl_fw_error_next_data(*data); - } - - iwl_trans_release_nic_access(trans, &flags); - - return prph_len; -} - -void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) -{ - struct iwl_fw_error_dump_file *dump_file; - struct iwl_fw_error_dump_data *dump_data; - struct iwl_fw_error_dump_info *dump_info; - struct iwl_fw_error_dump_mem *dump_mem; - struct iwl_fw_error_dump_trigger_desc *dump_trig; - struct iwl_mvm_dump_ptrs *fw_error_dump; - u32 sram_len, sram_ofs; - u32 file_len, fifo_data_len = 0; - u32 smem_len = mvm->cfg->smem_len; - u32 sram2_len = mvm->cfg->dccm2_len; - bool monitor_dump_only = false; - int i; - - lockdep_assert_held(&mvm->mutex); - - /* there's no point in fw dump if the bus is dead */ - if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { - IWL_ERR(mvm, "Skip fw error dump since bus is dead\n"); - return; - } - - if (mvm->fw_dump_trig && - mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY) - monitor_dump_only = true; - - fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); - if (!fw_error_dump) - return; - - /* SRAM - include stack CCM if driver knows the values for it */ - if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { - const struct fw_img *img; - - img = &mvm->fw->img[mvm->cur_ucode]; - sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; - } else { - sram_ofs = mvm->cfg->dccm_offset; - sram_len = mvm->cfg->dccm_len; - } - - /* reading RXF/TXF sizes */ - if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { - struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg; - - fifo_data_len = 0; - - /* Count RXF size */ - for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) { - if (!mem_cfg->rxfifo_size[i]) - continue; - - /* Add header info */ - fifo_data_len += mem_cfg->rxfifo_size[i] + - sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - } - - for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) { - if (!mem_cfg->txfifo_size[i]) - continue; - - /* Add header info */ - fifo_data_len += mem_cfg->txfifo_size[i] + - sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_fifo); - } - } - - file_len = sizeof(*dump_file) + - sizeof(*dump_data) * 2 + - sram_len + sizeof(*dump_mem) + - fifo_data_len + - sizeof(*dump_info); - - /* Make room for the SMEM, if it exists */ - if (smem_len) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; - - /* Make room for the secondary SRAM, if it exists */ - if (sram2_len) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; - - /* Make room for fw's virtual image pages, if it exists */ - if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) - file_len += mvm->num_of_paging_blk * - (sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_paging) + - PAGING_BLOCK_SIZE); - - /* If we only want a monitor dump, reset the file length */ - if (monitor_dump_only) { - file_len = sizeof(*dump_file) + sizeof(*dump_data) + - sizeof(*dump_info); - } - - /* Make room for PRPH registers */ - for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4; - - file_len += sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_prph) + - num_bytes_in_chunk; - } - - /* - * In 8000 HW family B-step include the ICCM (which resides separately) - */ - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && - CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) - file_len += sizeof(*dump_data) + sizeof(*dump_mem) + - IWL8260_ICCM_LEN; - - if (mvm->fw_dump_desc) - file_len += sizeof(*dump_data) + sizeof(*dump_trig) + - mvm->fw_dump_desc->len; - - dump_file = vzalloc(file_len); - if (!dump_file) { - kfree(fw_error_dump); - iwl_mvm_free_fw_dump_desc(mvm); - return; - } - - fw_error_dump->op_mode_ptr = dump_file; - - dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); - dump_data = (void *)dump_file->data; - - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); - dump_data->len = cpu_to_le32(sizeof(*dump_info)); - dump_info = (void *) dump_data->data; - dump_info->device_family = - mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? - cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : - cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); - dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev)); - memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, - sizeof(dump_info->fw_human_readable)); - strncpy(dump_info->dev_human_readable, mvm->cfg->name, - sizeof(dump_info->dev_human_readable)); - strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, - sizeof(dump_info->bus_human_readable)); - - dump_data = iwl_fw_error_next_data(dump_data); - /* We only dump the FIFOs if the FW is in error state */ - if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) - iwl_mvm_dump_fifos(mvm, &dump_data); - - if (mvm->fw_dump_desc) { - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); - dump_data->len = cpu_to_le32(sizeof(*dump_trig) + - mvm->fw_dump_desc->len); - dump_trig = (void *)dump_data->data; - memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc, - sizeof(*dump_trig) + mvm->fw_dump_desc->len); - - /* now we can free this copy */ - iwl_mvm_free_fw_dump_desc(mvm); - dump_data = iwl_fw_error_next_data(dump_data); - } - - /* In case we only want monitor dump, skip to dump trasport data */ - if (monitor_dump_only) - goto dump_trans_data; - - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(sram_ofs); - iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data, - sram_len); - - if (smem_len) { - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); - dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset); - iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, - dump_mem->data, smem_len); - } - - if (sram2_len) { - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset); - iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset, - dump_mem->data, sram2_len); - } - - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && - CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) { - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN + - sizeof(*dump_mem)); - dump_mem = (void *)dump_data->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); - dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET); - iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET, - dump_mem->data, IWL8260_ICCM_LEN); - } - - /* Dump fw's virtual image */ - if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) { - u32 i; - - for (i = 1; i < mvm->num_of_paging_blk + 1; i++) { - struct iwl_fw_error_dump_paging *paging; - struct page *pages = - mvm->fw_paging_db[i].fw_paging_block; - - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); - dump_data->len = cpu_to_le32(sizeof(*paging) + - PAGING_BLOCK_SIZE); - paging = (void *)dump_data->data; - paging->index = cpu_to_le32(i); - memcpy(paging->data, page_address(pages), - PAGING_BLOCK_SIZE); - } - } - - dump_data = iwl_fw_error_next_data(dump_data); - iwl_dump_prph(mvm->trans, &dump_data); - -dump_trans_data: - fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans, - mvm->fw_dump_trig); - fw_error_dump->op_mode_len = file_len; - if (fw_error_dump->trans_ptr) - file_len += fw_error_dump->trans_ptr->len; - dump_file->file_len = cpu_to_le32(file_len); - - dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, - GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); - - mvm->fw_dump_trig = NULL; - clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); -} - -struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { - .trig_desc = { - .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), - }, -}; - static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) { /* clear the D3 reconfig, we only need it to avoid dumping a diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 013b37920048..5692856d3e42 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1480,69 +1480,10 @@ void iwl_mvm_tdls_ch_switch_work(struct work_struct *work); struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); -void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); - -int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id); -int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, - const char *str, size_t len, - struct iwl_fw_dbg_trigger_tlv *trigger); -int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - struct iwl_mvm_dump_desc *desc, - struct iwl_fw_dbg_trigger_tlv *trigger); -void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm); -int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trigger, - const char *fmt, ...) __printf(3, 4); unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool tdls, bool cmd_q); void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, const char *errmsg); -static inline bool -iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig, - struct ieee80211_vif *vif) -{ - u32 trig_vif = le32_to_cpu(trig->vif_type); - - return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif; -} - -static inline bool -iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm, - struct iwl_fw_dbg_trigger_tlv *trig) -{ - return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) && - (mvm->fw_dbg_conf == FW_DBG_INVALID || - (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids)))); -} - -static inline bool -iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_fw_dbg_trigger_tlv *trig) -{ - if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif)) - return false; - - return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig); -} - -static inline void -_iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_fw_dbg_trigger_tlv *trigger) -{ - if (!trigger) - return; - - if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) - return; - - iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); -} -#define iwl_fw_dbg_trigger_simple_stop(mvm, vif, trig) \ - _iwl_fw_dbg_trigger_simple_stop((mvm), (vif), \ - iwl_fw_dbg_get_trigger((mvm)->fw,\ - (trig))) #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index f002e558fc13..f96ed5577c5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -82,6 +82,7 @@ #include "rs.h" #include "fw-api-scan.h" #include "time-event.h" +#include "fw-dbg.h" #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" MODULE_DESCRIPTION(DRV_DESCRIPTION); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 5b58f5320e8d..d1d50ffb1459 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -65,6 +65,7 @@ #include "iwl-trans.h" #include "mvm.h" #include "fw-api.h" +#include "fw-dbg.h" /* * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 7530eb23035d..52692dfee47a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -73,6 +73,7 @@ #include "mvm.h" #include "iwl-io.h" #include "iwl-prph.h" +#include "fw-dbg.h" /* * For the high priority TE use a time event type that has similar priority to diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index c652a66be803..aaebb5d4462a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -69,6 +69,7 @@ #include "iwl-eeprom-parse.h" #include "mvm.h" #include "sta.h" +#include "fw-dbg.h" static void iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index ad0f16909e2e..bbb7f6b27f5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -68,7 +68,7 @@ #include "iwl-debug.h" #include "iwl-io.h" #include "iwl-prph.h" - +#include "fw-dbg.h" #include "mvm.h" #include "fw-api-rs.h" -- cgit v1.2.3-59-g8ed1b From 321c2104f2f1649665aecd3b9204fea9eecc5ce6 Mon Sep 17 00:00:00 2001 From: Golan Ben Ami Date: Mon, 27 Jul 2015 17:02:35 +0300 Subject: iwlwifi: mvm: Support setting continuous recording debug mode Add ability to set the continuous recording mode of the FW, while the FW debug data is configured to be stored on the NIC. This could be useful for storing large segments of FW usniffer debug data on the host, while having small store space on the NIC. The host receives the usniffer data through the regular RX path, and the data can get extracted using trace-cmd. Signed-off-by: Golan Ben-Ami Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 40 ++++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 21 +++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 3 files changed, 62 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 3f7682da99e7..7e2a8149411c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -961,6 +961,44 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file, return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } +/* + * Enable / Disable continuous recording. + * Cause the FW to start continuous recording, by sending the relevant hcmd. + * Enable: input of every integer larger than 0, ENABLE_CONT_RECORDING. + * Disable: for 0 as input, DISABLE_CONT_RECORDING. + */ +static ssize_t iwl_dbgfs_cont_recording_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + struct iwl_trans *trans = mvm->trans; + const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv; + struct iwl_continuous_record_cmd cont_rec = {}; + int ret, rec_mode; + + if (!dest) + return -EOPNOTSUPP; + + if (dest->monitor_mode != SMEM_MODE || + trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + return -EOPNOTSUPP; + + ret = kstrtouint(buf, 0, &rec_mode); + if (ret) + return ret; + + cont_rec.record_mode.enable_recording = rec_mode ? + cpu_to_le16(ENABLE_CONT_RECORDING) : + cpu_to_le16(DISABLE_CONT_RECORDING); + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_send_cmd_pdu(mvm, LDBG_CONFIG_CMD, 0, + sizeof(cont_rec), &cont_rec); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) @@ -1413,6 +1451,7 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8); MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(cont_recording, 8); #ifdef CONFIG_IWLWIFI_BCAST_FILTERING MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256); @@ -1456,6 +1495,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(cont_recording, mvm->debugfs_dir, S_IWUSR); if (!debugfs_create_bool("enable_scan_iteration_notif", S_IRUSR | S_IWUSR, mvm->debugfs_dir, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 181590fbd3b3..68dfa2848d3e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -239,6 +239,7 @@ enum { DTS_MEASUREMENT_NOTIFICATION = 0xdd, REPLY_DEBUG_CMD = 0xf0, + LDBG_CONFIG_CMD = 0xf6, DEBUG_LOG_MSG = 0xf7, BCAST_FILTER_CMD = 0xcf, @@ -426,6 +427,26 @@ struct iwl_fw_get_item_cmd { __le32 item_id; } __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */ +#define CONT_REC_COMMAND_SIZE 80 +#define ENABLE_CONT_RECORDING 0x15 +#define DISABLE_CONT_RECORDING 0x16 + +/* + * struct iwl_continuous_record_mode - recording mode + */ +struct iwl_continuous_record_mode { + __le16 enable_recording; +} __packed; + +/* + * struct iwl_continuous_record_cmd - enable/disable continuous recording + */ +struct iwl_continuous_record_cmd { + struct iwl_continuous_record_mode record_mode; + u8 pad[CONT_REC_COMMAND_SIZE - + sizeof(struct iwl_continuous_record_mode)]; +} __packed; + struct iwl_fw_get_item_resp { __le32 item_id; __le32 item_byte_cnt; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index f96ed5577c5e..fa849a284e1c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -363,6 +363,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX + 1] = { CMD(TDLS_CONFIG_CMD), CMD(MCC_UPDATE_CMD), CMD(SCAN_ITERATION_COMPLETE_UMAC), + CMD(LDBG_CONFIG_CMD), }; #undef CMD -- cgit v1.2.3-59-g8ed1b From f8a1edb76eca455c2d158c77045b9882bfa0857a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 11 Nov 2015 11:53:32 +0100 Subject: iwlwifi: clean up transport debugfs handling Transport code currently calls itself through the transport ops, which is quite pointless. Clean up all of this. While at it, remove the unnecessary dir argument and the redundant IDI code. In slave transports, call both the common slave debugfs and the transport's own. SDIO has no files, so remove it all there. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 9 --------- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 9 +++++++++ drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 18 ++++-------------- 4 files changed, 14 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 607f4f7ea94c..3d089ae171b7 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -542,8 +542,6 @@ struct iwl_trans_txq_scd_cfg { * @wait_tx_queue_empty: wait until tx queues are empty. May sleep. * @freeze_txq_timer: prevents the timer of the queue from firing until the * queue is set to awake. Must be atomic. - * @dbgfs_register: add the dbgfs files under this directory. Files will be - * automatically deleted. * @write8: write a u8 to a register at offset ofs from the BAR * @write32: write a u32 to a register at offset ofs from the BAR * @read32: read a u32 register at offset ofs from the BAR @@ -599,7 +597,6 @@ struct iwl_trans_ops { void (*txq_disable)(struct iwl_trans *trans, int queue, bool configure_scd); - int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm); void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs, bool freeze); @@ -1022,12 +1019,6 @@ static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans, return trans->ops->wait_tx_queue_empty(trans, txqs); } -static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) -{ - return trans->ops->dbgfs_register(trans, dir); -} - static inline void iwl_trans_write8(struct iwl_trans *trans, u32 ofs, u8 val) { trans->ops->write8(trans, ofs, val); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 639761fb2bfb..a52710fb89e7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -598,7 +598,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) set_dflt_pwr_limit(iwl_trans, pdev); /* register transport layer debugfs here */ - ret = iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir); + ret = iwl_trans_pcie_dbgfs_register(iwl_trans); if (ret) goto out_free_drv; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index bf41543996a8..44dc09d84d42 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -569,4 +569,13 @@ static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans, void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state); +#ifdef CONFIG_IWLWIFI_DEBUGFS +int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans); +#else +static inline int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) +{ + return 0; +} +#endif + #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 50614e4da279..efef4871478d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2107,13 +2107,11 @@ DEBUGFS_READ_FILE_OPS(rx_queue); DEBUGFS_READ_FILE_OPS(tx_queue); DEBUGFS_WRITE_FILE_OPS(csr); -/* - * Create the debugfs files and directories - * - */ -static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) +/* Create the debugfs files and directories */ +int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) { + struct dentry *dir = trans->dbgfs_dir; + DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR); DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR); DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR); @@ -2125,12 +2123,6 @@ err: IWL_ERR(trans, "failed to create the trans debugfs entry\n"); return -ENOMEM; } -#else -static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) -{ - return 0; -} #endif /*CONFIG_IWLWIFI_DEBUGFS */ static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd) @@ -2473,8 +2465,6 @@ static const struct iwl_trans_ops trans_ops_pcie = { .txq_disable = iwl_trans_pcie_txq_disable, .txq_enable = iwl_trans_pcie_txq_enable, - .dbgfs_register = iwl_trans_pcie_dbgfs_register, - .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, -- cgit v1.2.3-59-g8ed1b From f02d2ccd61638756b3b48db771ee419f6be7d011 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 6 Nov 2015 11:27:23 +0100 Subject: iwlwifi: pcie: remove pointer from debug message Since this pointer is not shown anywhere else, it's useless. Remove it, just keeping the indexes. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 2b5d3e1d0fee..3db82bf3a258 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -996,8 +996,7 @@ restart: rxb = rxq->queue[i]; rxq->queue[i] = NULL; - IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d (%p)\n", - r, i, rxb); + IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d\n", r, i); iwl_pcie_rx_handle_rb(trans, rxb, emergency); i = (i + 1) & RX_QUEUE_MASK; -- cgit v1.2.3-59-g8ed1b From 04b089922412ad3d0cee0046fd0cb495ef22b8e8 Mon Sep 17 00:00:00 2001 From: Nicholas Krause Date: Tue, 22 Sep 2015 20:24:19 -0400 Subject: iwlwifi: mvm: fix incorrect fallthrough in iwl_mvm_check_running_scans() In the iwl_mvm_check_running_scans() we were mistakenly ignoring the value returned by iwl_mvm_scan_stop() for scheduled scans and falling thorugh to the next case, which caused us to always return zero. Signed-off-by: Nicholas Krause Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index e8a79bff9c1b..7cbfb085f60b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -1162,7 +1162,7 @@ static int iwl_mvm_check_running_scans(struct iwl_mvm *mvm, int type) case IWL_MVM_SCAN_SCHED: if (mvm->scan_status & IWL_MVM_SCAN_SCHED_MASK) return -EBUSY; - iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); + return iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); case IWL_MVM_SCAN_NETDETECT: /* No need to stop anything for net-detect since the * firmware is restarted anyway. This way, any sched -- cgit v1.2.3-59-g8ed1b From a399f98069743a9a17f85a406b4dc36c059f75ca Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 24 Sep 2015 18:29:00 +0200 Subject: iwlwifi: mvm: use firmware station lookup, combine code In most cases, the firmware will already match the station that we received a given frame from and tell us the station ID in the RX status, so we can look up the station from that. This lets us skip the (more expensive) hash table lookup in mac80211. Also change the fallback case (no station info from the firmware) to not attempt to look up a multicast source address. While at it, also combine all the code using the station into a single if block. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h | 3 +- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 43 ++++++++++++++-------- 2 files changed, 29 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h index 9b7e49d4620f..d7903226313f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -229,7 +229,8 @@ enum iwl_mvm_rx_status { RX_MPDU_RES_STATUS_CSUM_DONE = BIT(16), RX_MPDU_RES_STATUS_CSUM_OK = BIT(17), RX_MPDU_RES_STATUS_HASH_INDEX_MSK = (0x3F0000), - RX_MPDU_RES_STATUS_STA_ID_MSK = (0x1f000000), + RX_MDPU_RES_STATUS_STA_ID_SHIFT = 24, + RX_MPDU_RES_STATUS_STA_ID_MSK = 0x1f << RX_MDPU_RES_STATUS_STA_ID_SHIFT, RX_MPDU_RES_STATUS_RRF_KILL = BIT(29), RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000), RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000), diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index d1d50ffb1459..a0e957a4018d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -61,6 +61,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +#include #include #include "iwl-trans.h" #include "mvm.h" @@ -262,7 +263,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rx_phy_info *phy_info; struct iwl_rx_mpdu_res_start *rx_res; - struct ieee80211_sta *sta; + struct ieee80211_sta *sta = NULL; struct sk_buff *skb; u32 len; u32 ampdu_status; @@ -333,22 +334,33 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, (unsigned long long)rx_status->mactime); rcu_read_lock(); - /* - * We have tx blocked stations (with CS bit). If we heard frames from - * a blocked station on a new channel we can TX to it again. - */ - if (unlikely(mvm->csa_tx_block_bcn_timeout)) { - sta = ieee80211_find_sta( - rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2); - if (sta) - iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); + if (rx_pkt_status & RX_MPDU_RES_STATUS_SRC_STA_FOUND) { + u32 id = rx_pkt_status & RX_MPDU_RES_STATUS_STA_ID_MSK; + + id >>= RX_MDPU_RES_STATUS_STA_ID_SHIFT; + + if (!WARN_ON_ONCE(id >= IWL_MVM_STATION_COUNT)) { + sta = rcu_dereference(mvm->fw_id_to_mac_id[id]); + if (IS_ERR(sta)) + sta = NULL; + } + } else if (!is_multicast_ether_addr(hdr->addr2)) { + /* This is fine since we prevent two stations with the same + * address from being added. + */ + sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); } - /* This is fine since we don't support multiple AP interfaces */ - sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); if (sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + /* We have tx blocked stations (with CS bit). If we heard + * frames from a blocked station on a new channel we can + * TX to it again. + */ + if (unlikely(mvm->csa_tx_block_bcn_timeout)) + iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); + rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) && @@ -369,11 +381,10 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, if (trig_check && rx_status->signal < rssi) iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); } - } - - if (sta && ieee80211_is_data(hdr->frame_control)) - iwl_mvm_rx_csum(sta, skb, rx_pkt_status); + if (ieee80211_is_data(hdr->frame_control)) + iwl_mvm_rx_csum(sta, skb, rx_pkt_status); + } rcu_read_unlock(); /* set the preamble flag if appropriate */ -- cgit v1.2.3-59-g8ed1b From ac8ef0ce38de082218ff5ef972bea7dbebd09f53 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 12 Nov 2015 10:49:38 +0200 Subject: iwlwifi: mvm: refactor d3 key update functions We need to reuse the key update logic for d0i3 as well. Add some parameters to deal with the constraints implied by the d0i3 flow (specifically, support non-SYNC commands, and don't take mutexes that might deadlock). Change some commands to be ASYNC, in order to simplify locking a bit. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 212 +++++++++++++++------------ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 + 2 files changed, 120 insertions(+), 96 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 7bb549640581..478cf82f6331 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -136,7 +136,7 @@ static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out) struct wowlan_key_data { struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc; struct iwl_wowlan_tkip_params_cmd *tkip; - bool error, use_rsc_tsc, use_tkip; + bool error, use_rsc_tsc, use_tkip, configure_keys; int wep_key_idx; }; @@ -158,8 +158,6 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, u16 p1k[IWL_P1K_SIZE]; int ret, i; - mutex_lock(&mvm->mutex); - switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: { /* hack it for now */ @@ -195,20 +193,25 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, wkc.wep_key.key_offset = data->wep_key_idx; } - ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc); - data->error = ret != 0; - - mvm->ptk_ivlen = key->iv_len; - mvm->ptk_icvlen = key->icv_len; - mvm->gtk_ivlen = key->iv_len; - mvm->gtk_icvlen = key->icv_len; + if (data->configure_keys) { + mutex_lock(&mvm->mutex); + ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, + sizeof(wkc), &wkc); + data->error = ret != 0; + + mvm->ptk_ivlen = key->iv_len; + mvm->ptk_icvlen = key->icv_len; + mvm->gtk_ivlen = key->iv_len; + mvm->gtk_icvlen = key->icv_len; + mutex_unlock(&mvm->mutex); + } /* don't upload key again */ - goto out_unlock; + return; } default: data->error = true; - goto out_unlock; + return; case WLAN_CIPHER_SUITE_AES_CMAC: /* * Ignore CMAC keys -- the WoWLAN firmware doesn't support them @@ -217,7 +220,7 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, * IGTK for anything. This means we could spuriously wake up or * be deauthenticated, but that was considered acceptable. */ - goto out_unlock; + return; case WLAN_CIPHER_SUITE_TKIP: if (sta) { tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; @@ -304,29 +307,30 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, break; } - /* - * The D3 firmware hardcodes the key offset 0 as the key it uses - * to transmit packets to the AP, i.e. the PTK. - */ - if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { - mvm->ptk_ivlen = key->iv_len; - mvm->ptk_icvlen = key->icv_len; - ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 0); - } else { + if (data->configure_keys) { + mutex_lock(&mvm->mutex); /* - * firmware only supports TSC/RSC for a single key, - * so if there are multiple keep overwriting them - * with new ones -- this relies on mac80211 doing - * list_add_tail(). + * The D3 firmware hardcodes the key offset 0 as the key it + * uses to transmit packets to the AP, i.e. the PTK. */ - mvm->gtk_ivlen = key->iv_len; - mvm->gtk_icvlen = key->icv_len; - ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 1); + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { + mvm->ptk_ivlen = key->iv_len; + mvm->ptk_icvlen = key->icv_len; + ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 0); + } else { + /* + * firmware only supports TSC/RSC for a single key, + * so if there are multiple keep overwriting them + * with new ones -- this relies on mac80211 doing + * list_add_tail(). + */ + mvm->gtk_ivlen = key->iv_len; + mvm->gtk_icvlen = key->icv_len; + ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 1); + } + mutex_unlock(&mvm->mutex); + data->error = ret != 0; } - - data->error = ret != 0; -out_unlock: - mutex_unlock(&mvm->mutex); } static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, @@ -842,20 +846,89 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, return 0; } -static int -iwl_mvm_wowlan_config(struct iwl_mvm *mvm, - struct cfg80211_wowlan *wowlan, - struct iwl_wowlan_config_cmd *wowlan_config_cmd, - struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, - struct ieee80211_sta *ap_sta) +int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool configure_keys, + u32 cmd_flags) { struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; struct wowlan_key_data key_data = { + .configure_keys = configure_keys, .use_rsc_tsc = false, .tkip = &tkip_cmd, .use_tkip = false, }; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); + if (!key_data.rsc_tsc) + return -ENOMEM; + + /* + * Note that currently we don't propagate cmd_flags + * to the iterator. In case of key_data.configure_keys, + * all the configured commands are SYNC, and + * iwl_mvm_wowlan_program_keys() will take care of + * locking/unlocking mvm->mutex. + */ + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_wowlan_program_keys, + &key_data); + + if (key_data.error) { + ret = -EIO; + goto out; + } + + if (key_data.use_rsc_tsc) { + ret = iwl_mvm_send_cmd_pdu(mvm, + WOWLAN_TSC_RSC_PARAM, cmd_flags, + sizeof(*key_data.rsc_tsc), + key_data.rsc_tsc); + if (ret) + goto out; + } + + if (key_data.use_tkip) { + ret = iwl_mvm_send_cmd_pdu(mvm, + WOWLAN_TKIP_PARAM, + cmd_flags, sizeof(tkip_cmd), + &tkip_cmd); + if (ret) + goto out; + } + + if (mvmvif->rekey_data.valid) { + memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); + memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck, + NL80211_KCK_LEN); + kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); + memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek, + NL80211_KEK_LEN); + kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); + kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr; + + ret = iwl_mvm_send_cmd_pdu(mvm, + WOWLAN_KEK_KCK_MATERIAL, cmd_flags, + sizeof(kek_kck_cmd), + &kek_kck_cmd); + if (ret) + goto out; + } +out: + kfree(key_data.rsc_tsc); + return ret; +} + +static int +iwl_mvm_wowlan_config(struct iwl_mvm *mvm, + struct cfg80211_wowlan *wowlan, + struct iwl_wowlan_config_cmd *wowlan_config_cmd, + struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif, + struct ieee80211_sta *ap_sta) +{ int ret; ret = iwl_mvm_switch_to_d3(mvm); @@ -866,10 +939,6 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, if (ret) return ret; - key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); - if (!key_data.rsc_tsc) - return -ENOMEM; - if (!iwlwifi_mod_params.sw_crypto) { /* * This needs to be unlocked due to lock ordering @@ -877,74 +946,25 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, * that isn't really a problem though. */ mutex_unlock(&mvm->mutex); - ieee80211_iter_keys(mvm->hw, vif, - iwl_mvm_wowlan_program_keys, - &key_data); + iwl_mvm_wowlan_config_key_params(mvm, vif, true, CMD_ASYNC); mutex_lock(&mvm->mutex); - if (key_data.error) { - ret = -EIO; - goto out; - } - - if (key_data.use_rsc_tsc) { - struct iwl_host_cmd rsc_tsc_cmd = { - .id = WOWLAN_TSC_RSC_PARAM, - .data[0] = key_data.rsc_tsc, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .len[0] = sizeof(*key_data.rsc_tsc), - }; - - ret = iwl_mvm_send_cmd(mvm, &rsc_tsc_cmd); - if (ret) - goto out; - } - - if (key_data.use_tkip) { - ret = iwl_mvm_send_cmd_pdu(mvm, - WOWLAN_TKIP_PARAM, - 0, sizeof(tkip_cmd), - &tkip_cmd); - if (ret) - goto out; - } - - if (mvmvif->rekey_data.valid) { - memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); - memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck, - NL80211_KCK_LEN); - kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); - memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek, - NL80211_KEK_LEN); - kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); - kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr; - - ret = iwl_mvm_send_cmd_pdu(mvm, - WOWLAN_KEK_KCK_MATERIAL, 0, - sizeof(kek_kck_cmd), - &kek_kck_cmd); - if (ret) - goto out; - } } ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, sizeof(*wowlan_config_cmd), wowlan_config_cmd); if (ret) - goto out; + return ret; ret = iwl_mvm_send_patterns(mvm, wowlan); if (ret) - goto out; + return ret; ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0); if (ret) - goto out; + return ret; ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp); - -out: - kfree(key_data.rsc_tsc); return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 5692856d3e42..7dc3af6e06f7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1249,6 +1249,10 @@ static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm) /* D3 (WoWLAN, NetDetect) */ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); int iwl_mvm_resume(struct ieee80211_hw *hw); +int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool configure_keys, + u32 cmd_flags); void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled); void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw, struct ieee80211_vif *vif, -- cgit v1.2.3-59-g8ed1b From 89374fe60bfb481a81b941adaa5e834ace2cc9df Mon Sep 17 00:00:00 2001 From: "Dreyfuss, Haim" Date: Mon, 1 Jun 2015 08:27:17 +0300 Subject: iwlwifi: Add new PCI IDs for 9260 and 5165 series Add 9000-family configuration to iwl_cfg struct Add a new struct to define the 5165 series. Rename the struct that defines the 9000 series to 9260. Add some new sub-system IDs for the 9260 and 5165 series. For 9260: 0x0A10, 0x0000, 0x0510, 0x0710, 0x0410, 0x0610. For 5165: 0x2A10, 0x2010, 0x0310, 0x0210. Signed-off-by: Haim Dreyfuss Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-9000.c | 163 ++++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-config.h | 3 + drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 13 ++ 4 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-9000.c (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index dbfc5b18bcb7..05828c61d1ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -8,7 +8,7 @@ iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o -iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o +iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o iwl-9000.o iwlwifi-objs += iwl-trans.o iwlwifi-objs += $(iwlwifi-m) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c new file mode 100644 index 000000000000..a784bb64ef63 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c @@ -0,0 +1,163 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * BSD LICENSE + * + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include +#include "iwl-config.h" +#include "iwl-agn-hw.h" + +/* Highest firmware API version supported */ +#define IWL9000_UCODE_API_MAX 16 + +/* Oldest version we won't warn about */ +#define IWL9000_UCODE_API_OK 13 + +/* Lowest firmware API version supported */ +#define IWL9000_UCODE_API_MIN 13 + +/* NVM versions */ +#define IWL9000_NVM_VERSION 0x0a1d +#define IWL9000_TX_POWER_VERSION 0xffff /* meaningless */ + +/* Memory offsets and lengths */ +#define IWL9000_DCCM_OFFSET 0x800000 +#define IWL9000_DCCM_LEN 0x18000 +#define IWL9000_DCCM2_OFFSET 0x880000 +#define IWL9000_DCCM2_LEN 0x8000 +#define IWL9000_SMEM_OFFSET 0x400000 +#define IWL9000_SMEM_LEN 0x68000 + +#define IWL9000_FW_PRE "iwlwifi-9000-" +#define IWL9000_MODULE_FIRMWARE(api) \ + IWL9000_FW_PRE "-" __stringify(api) ".ucode" + +#define NVM_HW_SECTION_NUM_FAMILY_9000 10 + +static const struct iwl_base_params iwl9000_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_9000, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .shadow_ram_support = true, + .led_compensation = 57, + .wd_timeout = IWL_LONG_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = true, + .pcie_l1_allowed = true, +}; + +static const struct iwl_ht_params iwl9000_ht_params = { + .stbc = true, + .ldpc = true, + .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), +}; + +static const struct iwl_tt_params iwl9000_tt_params = { + .ct_kill_entry = 115, + .ct_kill_exit = 93, + .ct_kill_duration = 5, + .dynamic_smps_entry = 111, + .dynamic_smps_exit = 107, + .tx_protection_entry = 112, + .tx_protection_exit = 105, + .tx_backoff = { + {.temperature = 110, .backoff = 200}, + {.temperature = 111, .backoff = 600}, + {.temperature = 112, .backoff = 1200}, + {.temperature = 113, .backoff = 2000}, + {.temperature = 114, .backoff = 4000}, + }, + .support_ct_kill = true, + .support_dynamic_smps = true, + .support_tx_protection = true, + .support_tx_backoff = true, +}; + +#define IWL_DEVICE_9000 \ + .ucode_api_max = IWL9000_UCODE_API_MAX, \ + .ucode_api_ok = IWL9000_UCODE_API_OK, \ + .ucode_api_min = IWL9000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_8000, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .base_params = &iwl9000_base_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_9000, \ + .non_shared_ant = ANT_A, \ + .dccm_offset = IWL9000_DCCM_OFFSET, \ + .dccm_len = IWL9000_DCCM_LEN, \ + .dccm2_offset = IWL9000_DCCM2_OFFSET, \ + .dccm2_len = IWL9000_DCCM2_LEN, \ + .smem_offset = IWL9000_SMEM_OFFSET, \ + .smem_len = IWL9000_SMEM_LEN, \ + .thermal_params = &iwl9000_tt_params, \ + .apmg_not_supported = true + +const struct iwl_cfg iwl9260_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 9260", + .fw_name_pre = IWL9000_FW_PRE, + IWL_DEVICE_9000, + .ht_params = &iwl9000_ht_params, + .nvm_ver = IWL9000_NVM_VERSION, + .nvm_calib_ver = IWL9000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, +}; + +const struct iwl_cfg iwl5165_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 5165", + .fw_name_pre = IWL9000_FW_PRE, + IWL_DEVICE_9000, + .ht_params = &iwl9000_ht_params, + .nvm_ver = IWL9000_NVM_VERSION, + .nvm_calib_ver = IWL9000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, +}; + +MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_OK)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 652cdfe9cc07..c9bf9acbb87c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -254,6 +254,7 @@ struct iwl_tt_params { #define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */ #define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */ #define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */ +#define OTP_LOW_IMAGE_SIZE_FAMILY_9000 OTP_LOW_IMAGE_SIZE_FAMILY_8000 struct iwl_eeprom_params { const u8 regulatory_bands[7]; @@ -430,6 +431,8 @@ extern const struct iwl_cfg iwl8260_2ac_cfg; extern const struct iwl_cfg iwl4165_2ac_cfg; extern const struct iwl_cfg iwl8260_2ac_sdio_cfg; extern const struct iwl_cfg iwl4165_2ac_sdio_cfg; +extern const struct iwl_cfg iwl9260_2ac_cfg; +extern const struct iwl_cfg iwl5165_2ac_cfg; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index a52710fb89e7..37cf81ba8e7d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -467,6 +467,19 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8260_2ac_cfg)}, + +/* 9000 Series */ + {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl5165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x2010, iwl5165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0A10, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0010, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0000, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0310, iwl5165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0510, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0710, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0210, iwl5165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0410, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0610, iwl9260_2ac_cfg)}, #endif /* CONFIG_IWLMVM */ {0} -- cgit v1.2.3-59-g8ed1b From c4836b056d8316faf40ae94ac01557e6ee12c643 Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Sun, 1 Nov 2015 15:07:48 +0200 Subject: iwlwifi: Add PCI IDs for the new 3168 series Add a new struct for the 3168 series and a few new PCI ID entries. Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-7000.c | 13 +++++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-config.h | 1 + drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 4 ++++ 3 files changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c index bf88ec3a65fa..51fec203a845 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -274,6 +276,17 @@ const struct iwl_cfg iwl3165_2ac_cfg = { .dccm_len = IWL7265_DCCM_LEN, }; +const struct iwl_cfg iwl3168_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 3168", + .fw_name_pre = IWL7265D_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3165_NVM_VERSION, + .nvm_calib_ver = IWL3165_TX_POWER_VERSION, + .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, +}; + const struct iwl_cfg iwl7265_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 7265", .fw_name_pre = IWL7265_FW_PRE, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index c9bf9acbb87c..a1452a144805 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -420,6 +420,7 @@ extern const struct iwl_cfg iwl3160_2ac_cfg; extern const struct iwl_cfg iwl3160_2n_cfg; extern const struct iwl_cfg iwl3160_n_cfg; extern const struct iwl_cfg iwl3165_2ac_cfg; +extern const struct iwl_cfg iwl3168_2ac_cfg; extern const struct iwl_cfg iwl7265_2ac_cfg; extern const struct iwl_cfg iwl7265_2n_cfg; extern const struct iwl_cfg iwl7265_n_cfg; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 37cf81ba8e7d..fe30c7862402 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -377,6 +377,10 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)}, {IWL_PCI_DEVICE(0x3165, 0x8110, iwl3165_2ac_cfg)}, +/* 3168 Series */ + {IWL_PCI_DEVICE(0x24FB, 0x2110, iwl3168_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FB, 0x0000, iwl3168_2ac_cfg)}, + /* 7265 Series */ {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)}, -- cgit v1.2.3-59-g8ed1b From abf10f868f70e8e7c62ac4bc61ff4ab94a09eda1 Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Sun, 1 Nov 2015 15:27:58 +0200 Subject: iwlwifi: Add PCI IDs for the new series 8165 Add a new struct for the 8165 series and a few new PCI ID entries. Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-8000.c | 10 ++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-config.h | 1 + drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 2 ++ 3 files changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c index dd22b7822bc5..89a25cffb060 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c @@ -186,6 +186,16 @@ const struct iwl_cfg iwl8260_2ac_cfg = { .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, }; +const struct iwl_cfg iwl8265_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 8265", + .fw_name_pre = IWL8000_FW_PRE, + IWL_DEVICE_8000, + .ht_params = &iwl8000_ht_params, + .nvm_ver = IWL8000_NVM_VERSION, + .nvm_calib_ver = IWL8000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, +}; + const struct iwl_cfg iwl4165_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 4165", .fw_name_pre = IWL8000_FW_PRE, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index a1452a144805..6a4c0c236d10 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -429,6 +429,7 @@ extern const struct iwl_cfg iwl7265d_2n_cfg; extern const struct iwl_cfg iwl7265d_n_cfg; extern const struct iwl_cfg iwl8260_2n_cfg; extern const struct iwl_cfg iwl8260_2ac_cfg; +extern const struct iwl_cfg iwl8265_2ac_cfg; extern const struct iwl_cfg iwl4165_2ac_cfg; extern const struct iwl_cfg iwl8260_2ac_sdio_cfg; extern const struct iwl_cfg iwl4165_2ac_sdio_cfg; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index fe30c7862402..9bdce443628b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -471,6 +471,8 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0000, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8265_2ac_cfg)}, /* 9000 Series */ {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl5165_2ac_cfg)}, -- cgit v1.2.3-59-g8ed1b From 6e365100c31eb4c2aa573af9f2d75ab324eebec8 Mon Sep 17 00:00:00 2001 From: Avri Altman Date: Mon, 2 Nov 2015 14:16:07 +0200 Subject: iwlwifi: mvm: Align bt-coex priority with requirements Fix the gaps between the system requirements and our code. Signed-off-by: Avri Altman Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 36 ++++++++++++++++----------- 1 file changed, 21 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 808f234c953e..c9ca029c69f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -901,6 +901,7 @@ u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, struct ieee80211_tx_info *info, u8 ac) { __le16 fc = hdr->frame_control; + bool mplut_enabled = iwl_mvm_is_mplut_supported(mvm); if (info->band != IEEE80211_BAND_2GHZ) return 0; @@ -908,22 +909,27 @@ u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, if (unlikely(mvm->bt_tx_prio)) return mvm->bt_tx_prio - 1; - /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */ - if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO || - is_multicast_ether_addr(hdr->addr1) || - ieee80211_is_ctl(fc) || ieee80211_is_mgmt(fc) || - ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) + if (likely(ieee80211_is_data(fc))) { + if (likely(ieee80211_is_data_qos(fc))) { + switch (ac) { + case IEEE80211_AC_BE: + return mplut_enabled ? 1 : 0; + case IEEE80211_AC_VI: + return mplut_enabled ? 2 : 3; + case IEEE80211_AC_VO: + return 3; + default: + return 0; + } + } else if (is_multicast_ether_addr(hdr->addr1)) { + return 3; + } else + return 0; + } else if (ieee80211_is_mgmt(fc)) { + return ieee80211_is_disassoc(fc) ? 0 : 3; + } else if (ieee80211_is_ctl(fc)) { + /* ignore cfend and cfendack frames as we never send those */ return 3; - - switch (ac) { - case IEEE80211_AC_BE: - return 1; - case IEEE80211_AC_VO: - return 3; - case IEEE80211_AC_VI: - return 2; - default: - break; } return 0; -- cgit v1.2.3-59-g8ed1b From d01c536672304541370ab01fe11f20f1d15b2a55 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 17 Nov 2015 15:39:56 +0200 Subject: iwlwifi: change the Intel Wireless email address ilw@linux.intel.com is not available anymore. linuxwifi@intel.com should be used instead. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/calib.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/calib.h | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/commands.h | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/devices.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/led.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/lib.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/power.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/power.h | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/ucode.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-1000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-2000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-5000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-6000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-fh.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-fw.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-io.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-scd.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/binding.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/led.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/offloading.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/sf.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/testmode.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/time-event.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/time-event.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 2 +- 52 files changed, 52 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/calib.c b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c index 20e6aa910700..9be636268848 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/calib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/calib.h b/drivers/net/wireless/intel/iwlwifi/dvm/calib.h index aeae4e80ea40..099e3ce80ffc 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/calib.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/calib.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h index 7a34e4d158d1..2ab2773655a8 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/devices.c b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c index 34b41e5f7cfc..cc13c04063a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.c b/drivers/net/wireless/intel/iwlwifi/dvm/led.c index ca4d6692cc4e..178806ff9c28 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.c @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c index e18629a16fb0..a612fbe66377 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c @@ -22,7 +22,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/power.c b/drivers/net/wireless/intel/iwlwifi/dvm/power.c index 1513dbc79c14..0ad557c89514 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.c @@ -22,7 +22,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/power.h b/drivers/net/wireless/intel/iwlwifi/dvm/power.h index 570d3a5e4670..2fd9b43adafd 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/power.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.h @@ -22,7 +22,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #ifndef __iwl_power_setting_h__ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c index 931a8e4269ef..b662cf35b033 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c @@ -23,7 +23,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-1000.c b/drivers/net/wireless/intel/iwlwifi/iwl-1000.c index 06f6cc08f451..a90dbab6bbbe 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-1000.c @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-2000.c b/drivers/net/wireless/intel/iwlwifi/iwl-2000.c index 890b95f497d6..a6da9594c4a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-2000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-2000.c @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-5000.c b/drivers/net/wireless/intel/iwlwifi/iwl-5000.c index 724194e23414..8b5afdef2d83 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-5000.c @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-6000.c b/drivers/net/wireless/intel/iwlwifi/iwl-6000.c index 21b2630763dc..0b4ba781b631 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-6000.c @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h index 04a483d38659..ee9347a54cdc 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h index 71a78cede9b0..bde023316b4a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h index f62c54485852..27914eedc146 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h index a3b3c2465f89..5dfc9295a7e0 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h index 10839fae9cd9..e9b8673dd245 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c index a19c4582936f..b395854a94d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h index 750c8c9ee70d..ad2b834668ff 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c index 219ca8acca62..f2cea1c7befc 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h index a6d3bdf82cdd..1ed78be06c23 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index d56064861a9c..5cc6be927eab 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h index 73bcf29d30cc..f08a1319fc04 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h index 2e1909fcd3ba..85d6d6d55e2f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h index 2f4e12c44c6a..a9bcc788cae1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h @@ -21,7 +21,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c index 6caf2affbbb5..8aa1f2b7fdfc 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h index dbe8234521de..0f9995ed71cd 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 60b7fce88d32..7b89bfc8c8ac 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 822ba52e0e5a..92466ee72806 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-scd.h b/drivers/net/wireless/intel/iwlwifi/iwl-scd.h index f2353ebf2666..99b43da32adf 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-scd.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-scd.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 71610968c365..ccd317b6408f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c index a1376539d2dc..7cb68f6ed1b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h index 8c4190e7e027..ede6ef8d390e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h index 20521bebb0b1..228684cfdb7e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h index f3f3ee0a766b..95ac59d088b1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h index c8f3e2536cbb..65a7c8a4cacf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h @@ -27,7 +27,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h index 0f1ea80a55ef..ad9cc03e16c4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h index 493a8bdfbc9e..6fca4fb1d306 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h index 0c321f63ee42..438665a54923 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h index eed6271d01a3..86aa51b2210e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h index 853698ab8b05..0036d18334af 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index b8e0591970ae..9fcabc5ce904 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h index 32232d69ea44..461acdf497dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c index e3b3cf4dbd77..1e51fbe95f7c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c index 68b0169c8892..f83e2bc6d0d5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c index b0f59fdd287c..c2def1232a8c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h b/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h index 79ab6beb6b26..cbbc16fd006a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 52692dfee47a..87a04c32cb92 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h index cbdf8e52a5f1..61d7cd791b6e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index cadfc0460597..473975cb34af 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 3db82bf3a258..9193f0c7f104 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -23,7 +23,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ -- cgit v1.2.3-59-g8ed1b From 20f4d39a2822905f250650cd9cac49492c07758f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 21 Nov 2015 21:57:52 +0100 Subject: iwlwifi: print index in api/capa flags parsing message If the API or capabilities index is bigger than the driver expects, an error message is printed. Make that message print the index and distinguish between API and capabilities. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index ce656650dd47..16756f0eaba8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -451,7 +451,9 @@ static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, int i; if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) { - IWL_ERR(drv, "api_index larger than supported by driver\n"); + IWL_ERR(drv, + "api flags index %d larger than supported by driver\n", + api_index); /* don't return an error so we can load FW that has more bits */ return 0; } @@ -473,7 +475,9 @@ static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data, int i; if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) { - IWL_ERR(drv, "api_index larger than supported by driver\n"); + IWL_ERR(drv, + "capa flags index %d larger than supported by driver\n", + api_index); /* don't return an error so we can load FW that has more bits */ return 0; } -- cgit v1.2.3-59-g8ed1b From 7281b16423cd9b8795ea319c65140f38fd8187a2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 21 Nov 2015 13:33:38 +0300 Subject: iwlwifi: mvm: rs: fix a warning message WARN_ON_ONCE() doesn't take a message, it only takes a condition. I have changed this to WARN(1, ...). Signed-off-by: Dan Carpenter Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 37c2db1c1b1c..54789bd3b352 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -1827,7 +1827,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; rate_mask = lq_sta->active_mimo2_rate; } else { - WARN_ON_ONCE("Bad column mode"); + WARN_ONCE(1, "Bad column mode"); } if (column->mode != RS_LEGACY) { -- cgit v1.2.3-59-g8ed1b From 06ae2ad413d20a1212bd7d32a49bd8b868a8629a Mon Sep 17 00:00:00 2001 From: Derek Basehore Date: Wed, 4 Nov 2015 17:37:52 -0800 Subject: iwlwifi: mvm: report wakeup for wowlan When the wifi wakes up the system, we need to report it via calling pm_wakeup_event for lucid sleep. This is so userspace knowns that the wifi woke up the system via the /sys/power/wakeup_type sysfs interface. Signed-off-by: Derek Basehore Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 478cf82f6331..9e51843764ca 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1236,6 +1236,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, goto report; } + pm_wakeup_event(mvm->dev, 0); + if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) wakeup.magic_pkt = true; -- cgit v1.2.3-59-g8ed1b From 13555e8ba2f43094d42e0159a8be40e7a5f690d6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 2 Sep 2015 16:16:49 +0200 Subject: iwlwifi: mvm: add 9000-series RX API Define the RX API that's used by the 9000 series hardware. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h | 113 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 3 files changed, 115 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h index d7903226313f..9a8a37f7fd19 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -68,6 +68,8 @@ #ifndef __fw_api_rx_h__ #define __fw_api_rx_h__ +/* API for pre-9000 hardware */ + #define IWL_RX_INFO_PHY_CNT 8 #define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1 #define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff @@ -236,4 +238,115 @@ enum iwl_mvm_rx_status { RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000), }; +/* 9000 series API */ +enum iwl_rx_mpdu_mac_flags1 { + IWL_RX_MDPU_MFLG1_ADDRTYPE_MASK = 0x03, + IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_MASK = 0xf0, + /* shift should be 4, but the length is measured in 2-byte + * words, so shifting only by 3 gives a byte result + */ + IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_SHIFT = 3, +}; + +enum iwl_rx_mpdu_mac_flags2 { + /* in 2-byte words */ + IWL_RX_MPDU_MFLG2_HDR_LEN_MASK = 0x1f, + IWL_RX_MPDU_MFLG2_PAD = 0x20, + IWL_RX_MPDU_MFLG2_AMSDU = 0x40, +}; + +enum iwl_rx_mpdu_amsdu_info { + IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK = 0x3f, + IWL_RX_MPDU_AMSDU_LAST_SUBFRAME = 0x40, + /* 0x80 bit reserved for now */ +}; + +enum iwl_rx_l3l4_flags { + IWL_RX_L3L4_IP_HDR_CSUM_OK = BIT(0), + IWL_RX_L3L4_TCP_UDP_CSUM_OK = BIT(1), + IWL_RX_L3L4_TCP_FIN_SYN_RST_PSH = BIT(2), + IWL_RX_L3L4_TCP_ACK = BIT(3), + IWL_RX_L3L4_L3_PROTO_MASK = 0xf << 4, + IWL_RX_L3L4_L4_PROTO_MASK = 0xf << 8, + IWL_RX_L3L4_RSS_HASH_MASK = 0xf << 12, +}; + +enum iwl_rx_mpdu_status { + IWL_RX_MPDU_STATUS_CRC_OK = BIT(0), + IWL_RX_MPDU_STATUS_OVERRUN_OK = BIT(1), + IWL_RX_MPDU_STATUS_SRC_STA_FOUND = BIT(2), + IWL_RX_MPDU_STATUS_KEY_VALID = BIT(3), + IWL_RX_MPDU_STATUS_KEY_ERROR = BIT(4), + IWL_RX_MPDU_STATUS_ICV_OK = BIT(5), + IWL_RX_MPDU_STATUS_MIC_OK = BIT(6), + IWL_RX_MPDU_STATUS_SEC_MASK = 0x7 << 8, + IWL_RX_MPDU_STATUS_SEC_NONE = 0x0 << 8, + IWL_RX_MPDU_STATUS_SEC_WEP = 0x1 << 8, + IWL_RX_MPDU_STATUS_SEC_CCM = 0x2 << 8, + IWL_RX_MPDU_STATUS_SEC_TKIP = 0x3 << 8, + IWL_RX_MPDU_STATUS_DECRYPTED = BIT(11), + IWL_RX_MPDU_STATUS_WEP_MATCH = BIT(12), + IWL_RX_MPDU_STATUS_EXT_IV_MATCH = BIT(13), + IWL_RX_MPDU_STATUS_KEY_ID_MATCH = BIT(14), + IWL_RX_MPDU_STATUS_KEY_COLOR = BIT(15), +}; + +enum iwl_rx_mpdu_hash_filter { + IWL_RX_MPDU_HF_A1_HASH_MASK = 0x3f, + IWL_RX_MPDU_HF_FILTER_STATUS_MASK = 0xc0, +}; + +enum iwl_rx_mpdu_sta_id_flags { + IWL_RX_MPDU_SIF_STA_ID_MASK = 0x1f, + IWL_RX_MPDU_SIF_RRF_ABORT = 0x20, + IWL_RX_MPDU_SIF_FILTER_STATUS_MASK = 0xc0, +}; + +enum iwl_rx_mpdu_reorder_data { + IWL_RX_MPDU_REORDER_NSSN_MASK = 0x00000fff, + IWL_RX_MPDU_REORDER_SN_MASK = 0x00fff000, + IWL_RX_MPDU_REORDER_SN_SHIFT = 12, + IWL_RX_MPDU_REORDER_BAID_MASK = 0x7f000000, + IWL_RX_MPDU_REORDER_BAID_SHIFT = 24, + IWL_RX_MPDU_REORDER_BA_OLD_SN = 0x80000000, +}; + +struct iwl_rx_mpdu_desc { + /* DW2 */ + __le16 mpdu_len; + u8 mac_flags1; + u8 mac_flags2; + /* DW3 */ + u8 amsdu_info; + __le16 reserved_for_software; + u8 mac_phy_idx; + /* DW4 */ + __le16 raw_csum; /* alledgedly unreliable */ + __le16 l3l4_flags; + /* DW5 */ + __le16 status; + u8 hash_filter; + u8 sta_id_flags; + /* DW6 */ + __le32 reorder_data; + /* DW7 */ + __le32 rss_hash; + /* DW8 */ + __le32 filter_match; + /* DW9 */ + __le32 gp2_on_air_rise; + /* DW10 */ + __le32 rate_n_flags; + /* DW11 */ + u8 energy_a, energy_b, energy_c, channel; + /* DW12 & DW13 */ + __le64 tsf_on_air_rise; +} __packed; + +struct iwl_frame_release { + u8 baid; + u8 reserved; + __le16 nssn; +}; + #endif /* __fw_api_rx_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 68dfa2848d3e..9436798977a0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -213,6 +213,7 @@ enum { REPLY_RX_PHY_CMD = 0xc0, REPLY_RX_MPDU_CMD = 0xc1, + FRAME_RELEASE = 0xc3, BA_NOTIF = 0xc5, /* Location Aware Regulatory */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index fa849a284e1c..3b0d597f5033 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -310,6 +310,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX + 1] = { CMD(WEP_KEY), CMD(REPLY_RX_PHY_CMD), CMD(REPLY_RX_MPDU_CMD), + CMD(FRAME_RELEASE), CMD(BEACON_NOTIFICATION), CMD(BEACON_TEMPLATE_CMD), CMD(STATISTICS_CMD), -- cgit v1.2.3-59-g8ed1b From dc28e12f212500191fbc3c3ede411e80e2f30a10 Mon Sep 17 00:00:00 2001 From: Matti Gottlieb Date: Sun, 22 Nov 2015 10:08:56 +0200 Subject: iwlwifi: mvm: ROC: Extend the ROC max delay duration & limit ROC duration When associated to an AP and a ROC event with a long duration is scheduled the FW may have a hard time scheduling a consecutive time event, since it has to remain on the connection channel to hear the AP's DTIM. In addition, when associated and a ROC is requested with a duration greater than the DTIM interval, the FW will not be able to schedule the ROC event, since it needs to wake up for the DTIM. Increasing the "max delay" duration to the DTIM period will allow the FW to wait until after the DTIM and then schedule the ROC time event. Limiting the ROC to be less than the DTIM interval will assure that the time event will be scheduled for at least part of the time (instead of automatically failing) Extend the ROC max delay duration to min(dtim_interval * 3, 600TU), and limit the duration to be less than the DTIM interval. Signed-off-by: Matti Gottlieb Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 42 ++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 327703d5d4ba..a90f1ee4e571 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2707,7 +2707,11 @@ static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait, return true; } -#define AUX_ROC_MAX_DELAY_ON_CHANNEL 200 +#define AUX_ROC_MIN_DURATION MSEC_TO_TU(100) +#define AUX_ROC_MIN_DELAY MSEC_TO_TU(200) +#define AUX_ROC_MAX_DELAY MSEC_TO_TU(600) +#define AUX_ROC_SAFETY_BUFFER MSEC_TO_TU(20) +#define AUX_ROC_MIN_SAFETY_BUFFER MSEC_TO_TU(10) static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, struct ieee80211_channel *channel, struct ieee80211_vif *vif, @@ -2718,6 +2722,9 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data; static const u16 time_event_response[] = { HOT_SPOT_CMD }; struct iwl_notification_wait wait_time_event; + u32 dtim_interval = vif->bss_conf.dtim_period * + vif->bss_conf.beacon_int; + u32 req_dur, delay; struct iwl_hs20_roc_req aux_roc_req = { .action = cpu_to_le32(FW_CTXT_ACTION_ADD), .id_and_color = @@ -2730,11 +2737,38 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, .channel_info.width = PHY_VHT_CHANNEL_MODE20, /* Set the time and duration */ .apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)), - .apply_time_max_delay = - cpu_to_le32(MSEC_TO_TU(AUX_ROC_MAX_DELAY_ON_CHANNEL)), - .duration = cpu_to_le32(MSEC_TO_TU(duration)), }; + delay = AUX_ROC_MIN_DELAY; + req_dur = MSEC_TO_TU(duration); + + /* + * If we are associated we want the delay time to be at least one + * dtim interval so that the FW can wait until after the DTIM and + * then start the time event, this will potentially allow us to + * remain off-channel for the max duration. + * Since we want to use almost a whole dtim interval we would also + * like the delay to be for 2-3 dtim intervals, in case there are + * other time events with higher priority. + */ + if (vif->bss_conf.assoc) { + delay = min_t(u32, dtim_interval * 3, AUX_ROC_MAX_DELAY); + /* We cannot remain off-channel longer than the DTIM interval */ + if (dtim_interval <= req_dur) { + req_dur = dtim_interval - AUX_ROC_SAFETY_BUFFER; + if (req_dur <= AUX_ROC_MIN_DURATION) + req_dur = dtim_interval - + AUX_ROC_MIN_SAFETY_BUFFER; + } + } + + aux_roc_req.duration = cpu_to_le32(req_dur); + aux_roc_req.apply_time_max_delay = cpu_to_le32(delay); + + IWL_DEBUG_TE(mvm, + "ROC: Requesting to remain on channel %u for %ums (requested = %ums, max_delay = %ums, dtim_interval = %ums)\n", + channel->hw_value, req_dur, duration, delay, + dtim_interval); /* Set the node address */ memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN); -- cgit v1.2.3-59-g8ed1b From 863558168d55e992ad6999736e5115ee0268a762 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 18 Nov 2015 16:10:57 +0200 Subject: iwlwifi: remove IWL_DL_LED no need to have a separate debug level for a single debug print (which is pretty much useless anyway). remove them both. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/led.c | 2 -- drivers/net/wireless/intel/iwlwifi/iwl-debug.h | 2 -- 2 files changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.c b/drivers/net/wireless/intel/iwlwifi/dvm/led.c index 178806ff9c28..1aabb5ec096f 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.c @@ -134,8 +134,6 @@ static int iwl_led_cmd(struct iwl_priv *priv, on = IWL_LED_SOLID; } - IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n", - priv->cfg->base_params->led_compensation); led_cmd.on = iwl_blink_compensation(priv, on, priv->cfg->base_params->led_compensation); led_cmd.off = iwl_blink_compensation(priv, off, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h index 9bb36d79c2bd..e8dbb24d993d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h @@ -163,7 +163,6 @@ do { \ #define IWL_DL_FW 0x00010000 #define IWL_DL_RF_KILL 0x00020000 #define IWL_DL_FW_ERRORS 0x00040000 -#define IWL_DL_LED 0x00080000 /* 0x00F00000 - 0x00100000 */ #define IWL_DL_RATE 0x00100000 #define IWL_DL_CALIB 0x00200000 @@ -189,7 +188,6 @@ do { \ #define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a) #define IWL_DEBUG_TX(p, f, a...) IWL_DEBUG(p, IWL_DL_TX, f, ## a) #define IWL_DEBUG_ISR(p, f, a...) IWL_DEBUG(p, IWL_DL_ISR, f, ## a) -#define IWL_DEBUG_LED(p, f, a...) IWL_DEBUG(p, IWL_DL_LED, f, ## a) #define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a) #define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a) #define IWL_DEBUG_QUOTA(p, f, a...) IWL_DEBUG(p, IWL_DL_QUOTA, f, ## a) -- cgit v1.2.3-59-g8ed1b From 27a70af3f4cf633a1b86c0ac7b426e2fe16ad2e5 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 27 Nov 2015 11:39:55 +0100 Subject: hv_netvsc: rework link status change handling There are several issues in hv_netvsc driver with regards to link status change handling: - RNDIS_STATUS_NETWORK_CHANGE results in calling userspace helper doing '/etc/init.d/network restart' and this is inappropriate and broken for many reasons. - link_watch infrastructure only sends one notification per second and in case of e.g. paired disconnect/connect events we get only one notification with last status. This makes it impossible to handle such situations in userspace. Redo link status changes handling in the following way: - Create a list of reconfig events in network device context. - On a reconfig event add it to the list of events and schedule netvsc_link_change(). - In netvsc_link_change() ensure 2-second delay between link status changes. - Handle RNDIS_STATUS_NETWORK_CHANGE as a paired disconnect/connect event. Signed-off-by: Vitaly Kuznetsov Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 14 +++- drivers/net/hyperv/netvsc_drv.c | 142 +++++++++++++++++++++++++++------------- 2 files changed, 108 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 5fa98f599b3d..7661a12b00f4 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -177,7 +177,6 @@ struct rndis_device { enum rndis_device_state state; bool link_state; - bool link_change; atomic_t new_req_id; spinlock_t request_lock; @@ -644,11 +643,24 @@ struct netvsc_stats { struct u64_stats_sync syncp; }; +struct netvsc_reconfig { + struct list_head list; + u32 event; +}; + /* The context of the netvsc device */ struct net_device_context { /* point back to our device context */ struct hv_device *device_ctx; + /* reconfigure work */ struct delayed_work dwork; + /* last reconfig time */ + unsigned long last_reconfig; + /* reconfig events */ + struct list_head reconfig_events; + /* list protection */ + spinlock_t lock; + struct work_struct work; u32 msg_enable; /* debug level */ diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 409b48e1e589..268a05821d46 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -42,6 +42,7 @@ #define RING_SIZE_MIN 64 +#define LINKCHANGE_INT (2 * HZ) static int ring_size = 128; module_param(ring_size, int, S_IRUGO); MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); @@ -647,37 +648,33 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, struct net_device *net; struct net_device_context *ndev_ctx; struct netvsc_device *net_device; - struct rndis_device *rdev; - - net_device = hv_get_drvdata(device_obj); - rdev = net_device->extension; + struct netvsc_reconfig *event; + unsigned long flags; - switch (indicate->status) { - case RNDIS_STATUS_MEDIA_CONNECT: - rdev->link_state = false; - break; - case RNDIS_STATUS_MEDIA_DISCONNECT: - rdev->link_state = true; - break; - case RNDIS_STATUS_NETWORK_CHANGE: - rdev->link_change = true; - break; - default: + /* Handle link change statuses only */ + if (indicate->status != RNDIS_STATUS_NETWORK_CHANGE && + indicate->status != RNDIS_STATUS_MEDIA_CONNECT && + indicate->status != RNDIS_STATUS_MEDIA_DISCONNECT) return; - } + net_device = hv_get_drvdata(device_obj); net = net_device->ndev; if (!net || net->reg_state != NETREG_REGISTERED) return; ndev_ctx = netdev_priv(net); - if (!rdev->link_state) { - schedule_delayed_work(&ndev_ctx->dwork, 0); - schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20)); - } else { - schedule_delayed_work(&ndev_ctx->dwork, 0); - } + + event = kzalloc(sizeof(*event), GFP_ATOMIC); + if (!event) + return; + event->event = indicate->status; + + spin_lock_irqsave(&ndev_ctx->lock, flags); + list_add_tail(&event->list, &ndev_ctx->reconfig_events); + spin_unlock_irqrestore(&ndev_ctx->lock, flags); + + schedule_delayed_work(&ndev_ctx->dwork, 0); } /* @@ -1009,12 +1006,9 @@ static const struct net_device_ops device_ops = { }; /* - * Send GARP packet to network peers after migrations. - * After Quick Migration, the network is not immediately operational in the - * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add - * another netif_notify_peers() into a delayed work, otherwise GARP packet - * will not be sent after quick migration, and cause network disconnection. - * Also, we update the carrier status here. + * Handle link status changes. For RNDIS_STATUS_NETWORK_CHANGE emulate link + * down/up sequence. In case of RNDIS_STATUS_MEDIA_CONNECT when carrier is + * present send GARP packet to network peers with netif_notify_peers(). */ static void netvsc_link_change(struct work_struct *w) { @@ -1022,36 +1016,89 @@ static void netvsc_link_change(struct work_struct *w) struct net_device *net; struct netvsc_device *net_device; struct rndis_device *rdev; - bool notify, refresh = false; - char *argv[] = { "/etc/init.d/network", "restart", NULL }; - char *envp[] = { "HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; - - rtnl_lock(); + struct netvsc_reconfig *event = NULL; + bool notify = false, reschedule = false; + unsigned long flags, next_reconfig, delay; ndev_ctx = container_of(w, struct net_device_context, dwork.work); net_device = hv_get_drvdata(ndev_ctx->device_ctx); rdev = net_device->extension; net = net_device->ndev; - if (rdev->link_state) { - netif_carrier_off(net); - notify = false; - } else { - netif_carrier_on(net); - notify = true; - if (rdev->link_change) { - rdev->link_change = false; - refresh = true; + next_reconfig = ndev_ctx->last_reconfig + LINKCHANGE_INT; + if (time_is_after_jiffies(next_reconfig)) { + /* link_watch only sends one notification with current state + * per second, avoid doing reconfig more frequently. Handle + * wrap around. + */ + delay = next_reconfig - jiffies; + delay = delay < LINKCHANGE_INT ? delay : LINKCHANGE_INT; + schedule_delayed_work(&ndev_ctx->dwork, delay); + return; + } + ndev_ctx->last_reconfig = jiffies; + + spin_lock_irqsave(&ndev_ctx->lock, flags); + if (!list_empty(&ndev_ctx->reconfig_events)) { + event = list_first_entry(&ndev_ctx->reconfig_events, + struct netvsc_reconfig, list); + list_del(&event->list); + reschedule = !list_empty(&ndev_ctx->reconfig_events); + } + spin_unlock_irqrestore(&ndev_ctx->lock, flags); + + if (!event) + return; + + rtnl_lock(); + + switch (event->event) { + /* Only the following events are possible due to the check in + * netvsc_linkstatus_callback() + */ + case RNDIS_STATUS_MEDIA_CONNECT: + if (rdev->link_state) { + rdev->link_state = false; + netif_carrier_on(net); + netif_tx_wake_all_queues(net); + } else { + notify = true; + } + kfree(event); + break; + case RNDIS_STATUS_MEDIA_DISCONNECT: + if (!rdev->link_state) { + rdev->link_state = true; + netif_carrier_off(net); + netif_tx_stop_all_queues(net); + } + kfree(event); + break; + case RNDIS_STATUS_NETWORK_CHANGE: + /* Only makes sense if carrier is present */ + if (!rdev->link_state) { + rdev->link_state = true; + netif_carrier_off(net); + netif_tx_stop_all_queues(net); + event->event = RNDIS_STATUS_MEDIA_CONNECT; + spin_lock_irqsave(&ndev_ctx->lock, flags); + list_add_tail(&event->list, &ndev_ctx->reconfig_events); + spin_unlock_irqrestore(&ndev_ctx->lock, flags); + reschedule = true; } + break; } rtnl_unlock(); - if (refresh) - call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); - if (notify) netdev_notify_peers(net); + + /* link_watch only sends one notification with current state per + * second, handle next reconfig event in 2 seconds. + */ + if (reschedule) + schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT); } static void netvsc_free_netdev(struct net_device *netdev) @@ -1106,6 +1153,9 @@ static int netvsc_probe(struct hv_device *dev, INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change); INIT_WORK(&net_device_ctx->work, do_set_multicast); + spin_lock_init(&net_device_ctx->lock); + INIT_LIST_HEAD(&net_device_ctx->reconfig_events); + net->netdev_ops = &device_ops; net->hw_features = NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_IP_CSUM | @@ -1145,8 +1195,6 @@ static int netvsc_probe(struct hv_device *dev, pr_err("Unable to register netdev.\n"); rndis_filter_device_remove(dev); netvsc_free_netdev(net); - } else { - schedule_delayed_work(&net_device_ctx->dwork, 0); } return ret; -- cgit v1.2.3-59-g8ed1b From 93171b14a5455442f973172ebdfae24c205c0f11 Mon Sep 17 00:00:00 2001 From: Bert Kenward Date: Mon, 30 Nov 2015 09:05:35 +0000 Subject: sfc: make TSO version a per-queue parameter The Solarflare 8000 series NIC will use a new TSO scheme. The current driver refuses to load if the current TSO scheme is not found. Remove that check and instead make the TSO version a per-queue parameter. Signed-off-by: Bert Kenward Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 13 ++++++------- drivers/net/ethernet/sfc/net_driver.h | 2 ++ drivers/net/ethernet/sfc/tx.c | 8 ++++++-- 3 files changed, 14 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index bc6d21b471be..425df3dbc77d 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -180,13 +180,6 @@ static int efx_ef10_init_datapath_caps(struct efx_nic *efx) nic_data->tx_dpcpu_fw_id = MCDI_WORD(outbuf, GET_CAPABILITIES_OUT_TX_DPCPU_FW_ID); - if (!(nic_data->datapath_caps & - (1 << MC_CMD_GET_CAPABILITIES_OUT_TX_TSO_LBN))) { - netif_err(efx, drv, efx->net_dev, - "current firmware does not support TSO\n"); - return -ENODEV; - } - if (!(nic_data->datapath_caps & (1 << MC_CMD_GET_CAPABILITIES_OUT_RX_PREFIX_LEN_14_LBN))) { netif_err(efx, probe, efx->net_dev, @@ -1797,6 +1790,12 @@ static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue) ESF_DZ_TX_OPTION_UDP_TCP_CSUM, csum_offload, ESF_DZ_TX_OPTION_IP_CSUM, csum_offload); tx_queue->write_count = 1; + + if (nic_data->datapath_caps & + (1 << MC_CMD_GET_CAPABILITIES_OUT_TX_TSO_LBN)) { + tx_queue->tso_version = 1; + } + wmb(); efx_ef10_push_tx_desc(tx_queue, txd); diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index a8ddd122f685..5c0d0baa185c 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -182,6 +182,7 @@ struct efx_tx_buffer { * * @efx: The associated Efx NIC * @queue: DMA queue number + * @tso_version: Version of TSO in use for this queue. * @channel: The associated channel * @core_txq: The networking core TX queue structure * @buffer: The software buffer ring @@ -228,6 +229,7 @@ struct efx_tx_queue { /* Members which don't change on the fast path */ struct efx_nic *efx ____cacheline_aligned_in_smp; unsigned queue; + unsigned int tso_version; struct efx_channel *channel; struct netdev_queue *core_txq; struct efx_tx_buffer *buffer; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 67f6afaa022f..f7a0ec1bca97 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -1010,13 +1010,17 @@ static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue, /* Parse the SKB header and initialise state. */ static int tso_start(struct tso_state *st, struct efx_nic *efx, + struct efx_tx_queue *tx_queue, const struct sk_buff *skb) { - bool use_opt_desc = efx_nic_rev(efx) >= EFX_REV_HUNT_A0; struct device *dma_dev = &efx->pci_dev->dev; unsigned int header_len, in_len; + bool use_opt_desc = false; dma_addr_t dma_addr; + if (tx_queue->tso_version == 1) + use_opt_desc = true; + st->ip_off = skb_network_header(skb) - skb->data; st->tcp_off = skb_transport_header(skb) - skb->data; header_len = st->tcp_off + (tcp_hdr(skb)->doff << 2u); @@ -1271,7 +1275,7 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, /* Find the packet protocol and sanity-check it */ state.protocol = efx_tso_check_protocol(skb); - rc = tso_start(&state, efx, skb); + rc = tso_start(&state, efx, tx_queue, skb); if (rc) goto mem_err; -- cgit v1.2.3-59-g8ed1b From dd248f1bc65b49cba622a7e925d90d790e572996 Mon Sep 17 00:00:00 2001 From: Bert Kenward Date: Mon, 30 Nov 2015 09:05:47 +0000 Subject: sfc: Add PCI ID for Solarflare 8000 series 10/40G NIC Also add support for 7000 series 40G NIC VF. Signed-off-by: Bert Kenward Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/efx.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 4e82bcfbe3e0..b405349a570c 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -2784,6 +2784,12 @@ static const struct pci_device_id efx_pci_table[] = { .driver_data = (unsigned long) &efx_hunt_a0_vf_nic_type}, {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x0923), /* SFC9140 PF */ .driver_data = (unsigned long) &efx_hunt_a0_nic_type}, + {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x1923), /* SFC9140 VF */ + .driver_data = (unsigned long) &efx_hunt_a0_vf_nic_type}, + {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x0a03), /* SFC9220 PF */ + .driver_data = (unsigned long) &efx_hunt_a0_nic_type}, + {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x1a03), /* SFC9220 VF */ + .driver_data = (unsigned long) &efx_hunt_a0_vf_nic_type}, {0} /* end of list */ }; -- cgit v1.2.3-59-g8ed1b From 8edf049d578e0877fb3e1a18a809a0b0a8e58e08 Mon Sep 17 00:00:00 2001 From: Sudarsana Kalluru Date: Mon, 30 Nov 2015 12:25:01 +0200 Subject: qede: Add support for {get, set}_channels Signed-off-by: Sudarsana Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 1 + drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 53 +++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qede/qede_main.c | 7 +++- 3 files changed, 59 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index ea00d5f3bab4..a65a9b22aaec 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -116,6 +116,7 @@ struct qede_dev { (edev)->dev_info.num_tc) struct qede_fastpath *fp_array; + u16 req_rss; u16 num_rss; u8 num_tc; #define QEDE_RSS_CNT(edev) ((edev)->num_rss) diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 3a362476a22c..ea2fda8e5b64 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -366,6 +366,57 @@ int qede_change_mtu(struct net_device *ndev, int new_mtu) return 0; } +static void qede_get_channels(struct net_device *dev, + struct ethtool_channels *channels) +{ + struct qede_dev *edev = netdev_priv(dev); + + channels->max_combined = QEDE_MAX_RSS_CNT(edev); + channels->combined_count = QEDE_RSS_CNT(edev); +} + +static int qede_set_channels(struct net_device *dev, + struct ethtool_channels *channels) +{ + struct qede_dev *edev = netdev_priv(dev); + + DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN), + "set-channels command parameters: rx = %d, tx = %d, other = %d, combined = %d\n", + channels->rx_count, channels->tx_count, + channels->other_count, channels->combined_count); + + /* We don't support separate rx / tx, nor `other' channels. */ + if (channels->rx_count || channels->tx_count || + channels->other_count || (channels->combined_count == 0) || + (channels->combined_count > QEDE_MAX_RSS_CNT(edev))) { + DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN), + "command parameters not supported\n"); + return -EINVAL; + } + + /* Check if there was a change in the active parameters */ + if (channels->combined_count == QEDE_RSS_CNT(edev)) { + DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN), + "No change in active parameters\n"); + return 0; + } + + /* We need the number of queues to be divisible between the hwfns */ + if (channels->combined_count % edev->dev_info.common.num_hwfns) { + DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN), + "Number of channels must be divisable by %04x\n", + edev->dev_info.common.num_hwfns); + return -EINVAL; + } + + /* Set number of queues and reload if necessary */ + edev->req_rss = channels->combined_count; + if (netif_running(dev)) + qede_reload(edev, NULL, NULL); + + return 0; +} + static const struct ethtool_ops qede_ethtool_ops = { .get_settings = qede_get_settings, .set_settings = qede_set_settings, @@ -377,6 +428,8 @@ static const struct ethtool_ops qede_ethtool_ops = { .get_ethtool_stats = qede_get_ethtool_stats, .get_sset_count = qede_get_sset_count, + .get_channels = qede_get_channels, + .set_channels = qede_set_channels, }; void qede_set_ethtool_ops(struct net_device *dev) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index f4657a2e730a..6237f10b5119 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1502,8 +1502,11 @@ static int qede_set_num_queues(struct qede_dev *edev) u16 rss_num; /* Setup queues according to possible resources*/ - rss_num = netif_get_num_default_rss_queues() * - edev->dev_info.common.num_hwfns; + if (edev->req_rss) + rss_num = edev->req_rss; + else + rss_num = netif_get_num_default_rss_queues() * + edev->dev_info.common.num_hwfns; rss_num = min_t(u16, QEDE_MAX_RSS_CNT(edev), rss_num); -- cgit v1.2.3-59-g8ed1b From 01ef7e05cc83b83f5bab247cf7b74f953c59e7f0 Mon Sep 17 00:00:00 2001 From: Sudarsana Kalluru Date: Mon, 30 Nov 2015 12:25:02 +0200 Subject: qede: Add support for {get, set}_ringparam Signed-off-by: Sudarsana Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 4 +-- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 44 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index a65a9b22aaec..7c6caf7f6612 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -270,13 +270,13 @@ int qede_change_mtu(struct net_device *dev, int new_mtu); void qede_fill_by_demand_stats(struct qede_dev *edev); #define RX_RING_SIZE_POW 13 -#define RX_RING_SIZE BIT(RX_RING_SIZE_POW) +#define RX_RING_SIZE ((u16)BIT(RX_RING_SIZE_POW)) #define NUM_RX_BDS_MAX (RX_RING_SIZE - 1) #define NUM_RX_BDS_MIN 128 #define NUM_RX_BDS_DEF NUM_RX_BDS_MAX #define TX_RING_SIZE_POW 13 -#define TX_RING_SIZE BIT(TX_RING_SIZE_POW) +#define TX_RING_SIZE ((u16)BIT(TX_RING_SIZE_POW)) #define NUM_TX_BDS_MAX (TX_RING_SIZE - 1) #define NUM_TX_BDS_MIN 128 #define NUM_TX_BDS_DEF NUM_TX_BDS_MAX diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index ea2fda8e5b64..10d80bad1fd6 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -333,6 +333,48 @@ static u32 qede_get_link(struct net_device *dev) return current_link.link_up; } +static void qede_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *ering) +{ + struct qede_dev *edev = netdev_priv(dev); + + ering->rx_max_pending = NUM_RX_BDS_MAX; + ering->rx_pending = edev->q_num_rx_buffers; + ering->tx_max_pending = NUM_TX_BDS_MAX; + ering->tx_pending = edev->q_num_tx_buffers; +} + +static int qede_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *ering) +{ + struct qede_dev *edev = netdev_priv(dev); + + DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN), + "Set ring params command parameters: rx_pending = %d, tx_pending = %d\n", + ering->rx_pending, ering->tx_pending); + + /* Validate legality of configuration */ + if (ering->rx_pending > NUM_RX_BDS_MAX || + ering->rx_pending < NUM_RX_BDS_MIN || + ering->tx_pending > NUM_TX_BDS_MAX || + ering->tx_pending < NUM_TX_BDS_MIN) { + DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN), + "Can only support Rx Buffer size [0%08x,...,0x%08x] and Tx Buffer size [0x%08x,...,0x%08x]\n", + NUM_RX_BDS_MIN, NUM_RX_BDS_MAX, + NUM_TX_BDS_MIN, NUM_TX_BDS_MAX); + return -EINVAL; + } + + /* Change ring size and re-load */ + edev->q_num_rx_buffers = ering->rx_pending; + edev->q_num_tx_buffers = ering->tx_pending; + + if (netif_running(edev->ndev)) + qede_reload(edev, NULL, NULL); + + return 0; +} + static void qede_update_mtu(struct qede_dev *edev, union qede_reload_args *args) { edev->ndev->mtu = args->mtu; @@ -424,6 +466,8 @@ static const struct ethtool_ops qede_ethtool_ops = { .get_msglevel = qede_get_msglevel, .set_msglevel = qede_set_msglevel, .get_link = qede_get_link, + .get_ringparam = qede_get_ringparam, + .set_ringparam = qede_set_ringparam, .get_strings = qede_get_strings, .get_ethtool_stats = qede_get_ethtool_stats, .get_sset_count = qede_get_sset_count, -- cgit v1.2.3-59-g8ed1b From 91420b83baa046ada1a899c97f3b2c52a9045705 Mon Sep 17 00:00:00 2001 From: Sudarsana Kalluru Date: Mon, 30 Nov 2015 12:25:03 +0200 Subject: qed: Add support for changing LED state Physical LEDs are being controlled by the management FW. This adds the qed functionality required to request management FW to change the LED configuration, as well as the necessary APIs for this functionality to later be used by the protocol drivers. Signed-off-by: Sudarsana Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 6 ++++++ drivers/net/ethernet/qlogic/qed/qed_main.c | 18 ++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_mcp.c | 27 +++++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_mcp.h | 13 +++++++++++++ include/linux/qed/qed_if.h | 17 +++++++++++++++++ 5 files changed, 81 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index b2f8e854dfd1..264e954675d1 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -3993,6 +3993,8 @@ struct public_drv_mb { #define DRV_MSG_CODE_PHY_CORE_WRITE 0x000e0000 #define DRV_MSG_CODE_SET_VERSION 0x000f0000 +#define DRV_MSG_CODE_SET_LED_MODE 0x00200000 + #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff u32 drv_mb_param; @@ -4044,6 +4046,10 @@ struct public_drv_mb { #define DRV_MB_PARAM_CFG_VF_MSIX_SB_NUM_SHIFT 8 #define DRV_MB_PARAM_CFG_VF_MSIX_SB_NUM_MASK 0x0000FF00 +#define DRV_MB_PARAM_SET_LED_MODE_OPER 0x0 +#define DRV_MB_PARAM_SET_LED_MODE_ON 0x1 +#define DRV_MB_PARAM_SET_LED_MODE_OFF 0x2 + u32 fw_mb_header; #define FW_MSG_CODE_MASK 0xffff0000 #define FW_MSG_CODE_DRV_LOAD_ENGINE 0x10100000 diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 947c7af72b25..6b02e1134360 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -1135,6 +1135,23 @@ static int qed_drain(struct qed_dev *cdev) return 0; } +static int qed_set_led(struct qed_dev *cdev, enum qed_led_mode mode) +{ + struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); + struct qed_ptt *ptt; + int status = 0; + + ptt = qed_ptt_acquire(hwfn); + if (!ptt) + return -EAGAIN; + + status = qed_mcp_set_led(hwfn, ptt, mode); + + qed_ptt_release(hwfn, ptt); + + return status; +} + const struct qed_common_ops qed_common_ops_pass = { .probe = &qed_probe, .remove = &qed_remove, @@ -1155,6 +1172,7 @@ const struct qed_common_ops qed_common_ops_pass = { .update_msglvl = &qed_init_dp, .chain_alloc = &qed_chain_alloc, .chain_free = &qed_chain_free, + .set_led = &qed_set_led, }; u32 qed_get_protocol_version(enum qed_protocol protocol) diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 20d048cdcb88..ba1b1f1ef789 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -858,3 +858,30 @@ qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn, return 0; } + +int qed_mcp_set_led(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, + enum qed_led_mode mode) +{ + u32 resp = 0, param = 0, drv_mb_param; + int rc; + + switch (mode) { + case QED_LED_MODE_ON: + drv_mb_param = DRV_MB_PARAM_SET_LED_MODE_ON; + break; + case QED_LED_MODE_OFF: + drv_mb_param = DRV_MB_PARAM_SET_LED_MODE_OFF; + break; + case QED_LED_MODE_RESTORE: + drv_mb_param = DRV_MB_PARAM_SET_LED_MODE_OPER; + break; + default: + DP_NOTICE(p_hwfn, "Invalid LED mode %d\n", mode); + return -EINVAL; + } + + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_SET_LED_MODE, + drv_mb_param, &resp, ¶m); + + return rc; +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index dbaae586b4a7..506197d5c3dd 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -224,6 +224,19 @@ qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct qed_mcp_drv_version *p_ver); +/** + * @brief Set LED status + * + * @param p_hwfn + * @param p_ptt + * @param mode - LED mode + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_set_led(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_led_mode mode); + /* Using hwfn number (and not pf_num) is required since in CMT mode, * same pf_num may be used by two different hwfn * TODO - this shouldn't really be in .h file, but until all fields diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index dc9a1353f971..d4a32e878180 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -25,6 +25,12 @@ #include #include +enum qed_led_mode { + QED_LED_MODE_OFF, + QED_LED_MODE_ON, + QED_LED_MODE_RESTORE +}; + #define DIRECT_REG_WR(reg_addr, val) writel((u32)val, \ (void __iomem *)(reg_addr)) @@ -252,6 +258,17 @@ struct qed_common_ops { void (*chain_free)(struct qed_dev *cdev, struct qed_chain *p_chain); + +/** + * @brief set_led - Configure LED mode + * + * @param cdev + * @param mode - LED mode + * + * @return 0 on success, error otherwise. + */ + int (*set_led)(struct qed_dev *cdev, + enum qed_led_mode mode); }; /** -- cgit v1.2.3-59-g8ed1b From 3d971cbd0be981a5c191558057734c2a19b6165d Mon Sep 17 00:00:00 2001 From: Sudarsana Kalluru Date: Mon, 30 Nov 2015 12:25:04 +0200 Subject: qede: Add support for set_phys_id Signed-off-by: Sudarsana Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 10d80bad1fd6..b90d88020a8a 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -459,6 +459,34 @@ static int qede_set_channels(struct net_device *dev, return 0; } +static int qede_set_phys_id(struct net_device *dev, + enum ethtool_phys_id_state state) +{ + struct qede_dev *edev = netdev_priv(dev); + u8 led_state = 0; + + switch (state) { + case ETHTOOL_ID_ACTIVE: + return 1; /* cycle on/off once per second */ + + case ETHTOOL_ID_ON: + led_state = QED_LED_MODE_ON; + break; + + case ETHTOOL_ID_OFF: + led_state = QED_LED_MODE_OFF; + break; + + case ETHTOOL_ID_INACTIVE: + led_state = QED_LED_MODE_RESTORE; + break; + } + + edev->ops->common->set_led(edev->cdev, led_state); + + return 0; +} + static const struct ethtool_ops qede_ethtool_ops = { .get_settings = qede_get_settings, .set_settings = qede_set_settings, @@ -469,6 +497,7 @@ static const struct ethtool_ops qede_ethtool_ops = { .get_ringparam = qede_get_ringparam, .set_ringparam = qede_set_ringparam, .get_strings = qede_get_strings, + .set_phys_id = qede_set_phys_id, .get_ethtool_stats = qede_get_ethtool_stats, .get_sset_count = qede_get_sset_count, -- cgit v1.2.3-59-g8ed1b From 32a7a57003cd438e1ce358e75874024a8ebcec2d Mon Sep 17 00:00:00 2001 From: Sudarsana Kalluru Date: Mon, 30 Nov 2015 12:25:05 +0200 Subject: qede: Add support for nway_reset Signed-off-by: Sudarsana Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index b90d88020a8a..9b0bf1261505 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -322,6 +322,30 @@ static void qede_set_msglevel(struct net_device *ndev, u32 level) dp_module, dp_level); } +static int qede_nway_reset(struct net_device *dev) +{ + struct qede_dev *edev = netdev_priv(dev); + struct qed_link_output current_link; + struct qed_link_params link_params; + + if (!netif_running(dev)) + return 0; + + memset(¤t_link, 0, sizeof(current_link)); + edev->ops->common->get_link(edev->cdev, ¤t_link); + if (!current_link.link_up) + return 0; + + /* Toggle the link */ + memset(&link_params, 0, sizeof(link_params)); + link_params.link_up = false; + edev->ops->common->set_link(edev->cdev, &link_params); + link_params.link_up = true; + edev->ops->common->set_link(edev->cdev, &link_params); + + return 0; +} + static u32 qede_get_link(struct net_device *dev) { struct qede_dev *edev = netdev_priv(dev); @@ -493,6 +517,7 @@ static const struct ethtool_ops qede_ethtool_ops = { .get_drvinfo = qede_get_drvinfo, .get_msglevel = qede_get_msglevel, .set_msglevel = qede_set_msglevel, + .nway_reset = qede_nway_reset, .get_link = qede_get_link, .get_ringparam = qede_get_ringparam, .set_ringparam = qede_set_ringparam, -- cgit v1.2.3-59-g8ed1b From 0f7db144c09889c552779996d78ac85539a9eb9e Mon Sep 17 00:00:00 2001 From: Sudarsana Kalluru Date: Mon, 30 Nov 2015 12:25:06 +0200 Subject: qede: Add support for {get, set}_pauseparam Signed-off-by: Sudarsana Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 60 +++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 9b0bf1261505..e442b85c9a5e 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -399,6 +399,64 @@ static int qede_set_ringparam(struct net_device *dev, return 0; } +static void qede_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *epause) +{ + struct qede_dev *edev = netdev_priv(dev); + struct qed_link_output current_link; + + memset(¤t_link, 0, sizeof(current_link)); + edev->ops->common->get_link(edev->cdev, ¤t_link); + + if (current_link.pause_config & QED_LINK_PAUSE_AUTONEG_ENABLE) + epause->autoneg = true; + if (current_link.pause_config & QED_LINK_PAUSE_RX_ENABLE) + epause->rx_pause = true; + if (current_link.pause_config & QED_LINK_PAUSE_TX_ENABLE) + epause->tx_pause = true; + + DP_VERBOSE(edev, QED_MSG_DEBUG, + "ethtool_pauseparam: cmd %d autoneg %d rx_pause %d tx_pause %d\n", + epause->cmd, epause->autoneg, epause->rx_pause, + epause->tx_pause); +} + +static int qede_set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *epause) +{ + struct qede_dev *edev = netdev_priv(dev); + struct qed_link_params params; + struct qed_link_output current_link; + + if (!edev->dev_info.common.is_mf) { + DP_INFO(edev, + "Pause parameters can not be updated in non-default mode\n"); + return -EOPNOTSUPP; + } + + memset(¤t_link, 0, sizeof(current_link)); + edev->ops->common->get_link(edev->cdev, ¤t_link); + + memset(¶ms, 0, sizeof(params)); + params.override_flags |= QED_LINK_OVERRIDE_PAUSE_CONFIG; + if (epause->autoneg) { + if (!(current_link.supported_caps & SUPPORTED_Autoneg)) { + DP_INFO(edev, "autoneg not supported\n"); + return -EINVAL; + } + params.pause_config |= QED_LINK_PAUSE_AUTONEG_ENABLE; + } + if (epause->rx_pause) + params.pause_config |= QED_LINK_PAUSE_RX_ENABLE; + if (epause->tx_pause) + params.pause_config |= QED_LINK_PAUSE_TX_ENABLE; + + params.link_up = true; + edev->ops->common->set_link(edev->cdev, ¶ms); + + return 0; +} + static void qede_update_mtu(struct qede_dev *edev, union qede_reload_args *args) { edev->ndev->mtu = args->mtu; @@ -521,6 +579,8 @@ static const struct ethtool_ops qede_ethtool_ops = { .get_link = qede_get_link, .get_ringparam = qede_get_ringparam, .set_ringparam = qede_set_ringparam, + .get_pauseparam = qede_get_pauseparam, + .set_pauseparam = qede_set_pauseparam, .get_strings = qede_get_strings, .set_phys_id = qede_set_phys_id, .get_ethtool_stats = qede_get_ethtool_stats, -- cgit v1.2.3-59-g8ed1b From 2f7a791c92a6226cf6d9e2213a89a49da72fa574 Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Mon, 30 Nov 2015 11:33:10 +0100 Subject: stmmac: support Reg_9 to get HW level information For GMAC newer than 3.40a there is a new register (Reg_9) that provides the status of all modules of the transmit and receive paths and FIFO status. These can be exposed via ethtool. Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 26 ++++++++ drivers/net/ethernet/stmicro/stmmac/dwmac1000.h | 42 ++++++++++++ .../net/ethernet/stmicro/stmmac/dwmac1000_core.c | 75 ++++++++++++++++++++++ .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 30 +++++++++ 4 files changed, 173 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 623c6ed8764a..f4518bc2cd28 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -137,6 +137,31 @@ struct stmmac_extra_stats { unsigned long pcs_link; unsigned long pcs_duplex; unsigned long pcs_speed; + /* debug register */ + unsigned long mtl_tx_status_fifo_full; + unsigned long mtl_tx_fifo_not_empty; + unsigned long mmtl_fifo_ctrl; + unsigned long mtl_tx_fifo_read_ctrl_write; + unsigned long mtl_tx_fifo_read_ctrl_wait; + unsigned long mtl_tx_fifo_read_ctrl_read; + unsigned long mtl_tx_fifo_read_ctrl_idle; + unsigned long mac_tx_in_pause; + unsigned long mac_tx_frame_ctrl_xfer; + unsigned long mac_tx_frame_ctrl_idle; + unsigned long mac_tx_frame_ctrl_wait; + unsigned long mac_tx_frame_ctrl_pause; + unsigned long mac_gmii_tx_proto_engine; + unsigned long mtl_rx_fifo_fill_level_full; + unsigned long mtl_rx_fifo_fill_above_thresh; + unsigned long mtl_rx_fifo_fill_below_thresh; + unsigned long mtl_rx_fifo_fill_level_empty; + unsigned long mtl_rx_fifo_read_ctrl_flush; + unsigned long mtl_rx_fifo_read_ctrl_read_data; + unsigned long mtl_rx_fifo_read_ctrl_status; + unsigned long mtl_rx_fifo_read_ctrl_idle; + unsigned long mtl_rx_fifo_ctrl_active; + unsigned long mac_rx_frame_ctrl_fifo; + unsigned long mac_gmii_rx_proto_engine; }; /* CSR Frequency Access Defines*/ @@ -408,6 +433,7 @@ struct stmmac_ops { void (*set_eee_pls)(struct mac_device_info *hw, int link); void (*ctrl_ane)(struct mac_device_info *hw, bool restart); void (*get_adv)(struct mac_device_info *hw, struct rgmii_adv *adv); + void (*debug)(void __iomem *ioaddr, struct stmmac_extra_stats *x); }; /* PTP and HW Timer helpers */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index b3fe0575ff6b..8831a053ac13 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -34,6 +34,7 @@ #define GMAC_FLOW_CTRL 0x00000018 /* Flow Control */ #define GMAC_VLAN_TAG 0x0000001c /* VLAN Tag */ #define GMAC_VERSION 0x00000020 /* GMAC CORE Version */ +#define GMAC_DEBUG 0x00000024 /* GMAC debug register */ #define GMAC_WAKEUP_FILTER 0x00000028 /* Wake-up Frame Filter */ #define GMAC_INT_STATUS 0x00000038 /* interrupt status register */ @@ -177,6 +178,47 @@ enum inter_frame_gap { #define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */ #define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */ +/* DEBUG Register defines */ +/* MTL TxStatus FIFO */ +#define GMAC_DEBUG_TXSTSFSTS BIT(25) /* MTL TxStatus FIFO Full Status */ +#define GMAC_DEBUG_TXFSTS BIT(24) /* MTL Tx FIFO Not Empty Status */ +#define GMAC_DEBUG_TWCSTS BIT(22) /* MTL Tx FIFO Write Controller */ +/* MTL Tx FIFO Read Controller Status */ +#define GMAC_DEBUG_TRCSTS_MASK GENMASK(21, 20) +#define GMAC_DEBUG_TRCSTS_SHIFT 20 +#define GMAC_DEBUG_TRCSTS_IDLE 0 +#define GMAC_DEBUG_TRCSTS_READ 1 +#define GMAC_DEBUG_TRCSTS_TXW 2 +#define GMAC_DEBUG_TRCSTS_WRITE 3 +#define GMAC_DEBUG_TXPAUSED BIT(19) /* MAC Transmitter in PAUSE */ +/* MAC Transmit Frame Controller Status */ +#define GMAC_DEBUG_TFCSTS_MASK GENMASK(18, 17) +#define GMAC_DEBUG_TFCSTS_SHIFT 17 +#define GMAC_DEBUG_TFCSTS_IDLE 0 +#define GMAC_DEBUG_TFCSTS_WAIT 1 +#define GMAC_DEBUG_TFCSTS_GEN_PAUSE 2 +#define GMAC_DEBUG_TFCSTS_XFER 3 +/* MAC GMII or MII Transmit Protocol Engine Status */ +#define GMAC_DEBUG_TPESTS BIT(16) +#define GMAC_DEBUG_RXFSTS_MASK GENMASK(9, 8) /* MTL Rx FIFO Fill-level */ +#define GMAC_DEBUG_RXFSTS_SHIFT 8 +#define GMAC_DEBUG_RXFSTS_EMPTY 0 +#define GMAC_DEBUG_RXFSTS_BT 1 +#define GMAC_DEBUG_RXFSTS_AT 2 +#define GMAC_DEBUG_RXFSTS_FULL 3 +#define GMAC_DEBUG_RRCSTS_MASK GENMASK(6, 5) /* MTL Rx FIFO Read Controller */ +#define GMAC_DEBUG_RRCSTS_SHIFT 5 +#define GMAC_DEBUG_RRCSTS_IDLE 0 +#define GMAC_DEBUG_RRCSTS_RDATA 1 +#define GMAC_DEBUG_RRCSTS_RSTAT 2 +#define GMAC_DEBUG_RRCSTS_FLUSH 3 +#define GMAC_DEBUG_RWCSTS BIT(4) /* MTL Rx FIFO Write Controller Active */ +/* MAC Receive Frame Controller FIFO Status */ +#define GMAC_DEBUG_RFCFCSTS_MASK GENMASK(2, 1) +#define GMAC_DEBUG_RFCFCSTS_SHIFT 1 +/* MAC GMII or MII Receive Protocol Engine Status */ +#define GMAC_DEBUG_RPESTS BIT(0) + /*--- DMA BLOCK defines ---*/ /* DMA Bus Mode register defines */ #define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index 371a669d69fd..c2941172f6d1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -397,6 +397,80 @@ static void dwmac1000_get_adv(struct mac_device_info *hw, struct rgmii_adv *adv) adv->lp_pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT; } +static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x) +{ + u32 value = readl(ioaddr + GMAC_DEBUG); + + if (value & GMAC_DEBUG_TXSTSFSTS) + x->mtl_tx_status_fifo_full++; + if (value & GMAC_DEBUG_TXFSTS) + x->mtl_tx_fifo_not_empty++; + if (value & GMAC_DEBUG_TWCSTS) + x->mmtl_fifo_ctrl++; + if (value & GMAC_DEBUG_TRCSTS_MASK) { + u32 trcsts = (value & GMAC_DEBUG_TRCSTS_MASK) + >> GMAC_DEBUG_TRCSTS_SHIFT; + if (trcsts == GMAC_DEBUG_TRCSTS_WRITE) + x->mtl_tx_fifo_read_ctrl_write++; + else if (trcsts == GMAC_DEBUG_TRCSTS_TXW) + x->mtl_tx_fifo_read_ctrl_wait++; + else if (trcsts == GMAC_DEBUG_TRCSTS_READ) + x->mtl_tx_fifo_read_ctrl_read++; + else + x->mtl_tx_fifo_read_ctrl_idle++; + } + if (value & GMAC_DEBUG_TXPAUSED) + x->mac_tx_in_pause++; + if (value & GMAC_DEBUG_TFCSTS_MASK) { + u32 tfcsts = (value & GMAC_DEBUG_TFCSTS_MASK) + >> GMAC_DEBUG_TFCSTS_SHIFT; + + if (tfcsts == GMAC_DEBUG_TFCSTS_XFER) + x->mac_tx_frame_ctrl_xfer++; + else if (tfcsts == GMAC_DEBUG_TFCSTS_GEN_PAUSE) + x->mac_tx_frame_ctrl_pause++; + else if (tfcsts == GMAC_DEBUG_TFCSTS_WAIT) + x->mac_tx_frame_ctrl_wait++; + else + x->mac_tx_frame_ctrl_idle++; + } + if (value & GMAC_DEBUG_TPESTS) + x->mac_gmii_tx_proto_engine++; + if (value & GMAC_DEBUG_RXFSTS_MASK) { + u32 rxfsts = (value & GMAC_DEBUG_RXFSTS_MASK) + >> GMAC_DEBUG_RRCSTS_SHIFT; + + if (rxfsts == GMAC_DEBUG_RXFSTS_FULL) + x->mtl_rx_fifo_fill_level_full++; + else if (rxfsts == GMAC_DEBUG_RXFSTS_AT) + x->mtl_rx_fifo_fill_above_thresh++; + else if (rxfsts == GMAC_DEBUG_RXFSTS_BT) + x->mtl_rx_fifo_fill_below_thresh++; + else + x->mtl_rx_fifo_fill_level_empty++; + } + if (value & GMAC_DEBUG_RRCSTS_MASK) { + u32 rrcsts = (value & GMAC_DEBUG_RRCSTS_MASK) >> + GMAC_DEBUG_RRCSTS_SHIFT; + + if (rrcsts == GMAC_DEBUG_RRCSTS_FLUSH) + x->mtl_rx_fifo_read_ctrl_flush++; + else if (rrcsts == GMAC_DEBUG_RRCSTS_RSTAT) + x->mtl_rx_fifo_read_ctrl_read_data++; + else if (rrcsts == GMAC_DEBUG_RRCSTS_RDATA) + x->mtl_rx_fifo_read_ctrl_status++; + else + x->mtl_rx_fifo_read_ctrl_idle++; + } + if (value & GMAC_DEBUG_RWCSTS) + x->mtl_rx_fifo_ctrl_active++; + if (value & GMAC_DEBUG_RFCFCSTS_MASK) + x->mac_rx_frame_ctrl_fifo = (value & GMAC_DEBUG_RFCFCSTS_MASK) + >> GMAC_DEBUG_RFCFCSTS_SHIFT; + if (value & GMAC_DEBUG_RPESTS) + x->mac_gmii_rx_proto_engine++; +} + static const struct stmmac_ops dwmac1000_ops = { .core_init = dwmac1000_core_init, .rx_ipc = dwmac1000_rx_ipc_enable, @@ -413,6 +487,7 @@ static const struct stmmac_ops dwmac1000_ops = { .set_eee_pls = dwmac1000_set_eee_pls, .ctrl_ane = dwmac1000_ctrl_ane, .get_adv = dwmac1000_get_adv, + .debug = dwmac1000_debug, }; struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 2e51b816a7e8..4c6486cc80fb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -136,6 +136,31 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { STMMAC_STAT(irq_pcs_ane_n), STMMAC_STAT(irq_pcs_link_n), STMMAC_STAT(irq_rgmii_n), + /* DEBUG */ + STMMAC_STAT(mtl_tx_status_fifo_full), + STMMAC_STAT(mtl_tx_fifo_not_empty), + STMMAC_STAT(mmtl_fifo_ctrl), + STMMAC_STAT(mtl_tx_fifo_read_ctrl_write), + STMMAC_STAT(mtl_tx_fifo_read_ctrl_wait), + STMMAC_STAT(mtl_tx_fifo_read_ctrl_read), + STMMAC_STAT(mtl_tx_fifo_read_ctrl_idle), + STMMAC_STAT(mac_tx_in_pause), + STMMAC_STAT(mac_tx_frame_ctrl_xfer), + STMMAC_STAT(mac_tx_frame_ctrl_idle), + STMMAC_STAT(mac_tx_frame_ctrl_wait), + STMMAC_STAT(mac_tx_frame_ctrl_pause), + STMMAC_STAT(mac_gmii_tx_proto_engine), + STMMAC_STAT(mtl_rx_fifo_fill_level_full), + STMMAC_STAT(mtl_rx_fifo_fill_above_thresh), + STMMAC_STAT(mtl_rx_fifo_fill_below_thresh), + STMMAC_STAT(mtl_rx_fifo_fill_level_empty), + STMMAC_STAT(mtl_rx_fifo_read_ctrl_flush), + STMMAC_STAT(mtl_rx_fifo_read_ctrl_read_data), + STMMAC_STAT(mtl_rx_fifo_read_ctrl_status), + STMMAC_STAT(mtl_rx_fifo_read_ctrl_idle), + STMMAC_STAT(mtl_rx_fifo_ctrl_active), + STMMAC_STAT(mac_rx_frame_ctrl_fifo), + STMMAC_STAT(mac_gmii_rx_proto_engine), }; #define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats) @@ -497,6 +522,11 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, if (val) priv->xstats.phy_eee_wakeup_error_n = val; } + + if ((priv->hw->mac->debug) && + (priv->synopsys_id >= DWMAC_CORE_3_50)) + priv->hw->mac->debug(priv->ioaddr, + (void *)&priv->xstats); } for (i = 0; i < STMMAC_STATS_LEN; i++) { char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset; -- cgit v1.2.3-59-g8ed1b From 6cc568340148424e657508d82692ef787d6c4b2c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 30 Nov 2015 11:34:09 +0100 Subject: isdn: remove spellcaster driver The 'sc' ISDN driver relies on using readl() to access ISA I/O memory. This has been deprecated and produced warnings since linux-2.3.23, disabled by default since 2.4.10 and finally removed in 2.6.5. I found this because the compiling the driver for ARM produces a warning: In file included from ../drivers/isdn/sc/includes.h:8:0, from ../drivers/isdn/sc/init.c:13: ../arch/arm/include/asm/io.h:115:21: note: expected 'const volatile void *' but argument is of type 'long unsigned int' It is pretty clear that this driver has not been used for a long time and there is no point fixing it now, so let's remove it. Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/isdn/Makefile | 1 - drivers/isdn/i4l/Kconfig | 2 - drivers/isdn/sc/Kconfig | 8 - drivers/isdn/sc/Makefile | 10 - drivers/isdn/sc/card.h | 131 ---------- drivers/isdn/sc/command.c | 363 --------------------------- drivers/isdn/sc/event.c | 68 ------ drivers/isdn/sc/hardware.h | 110 --------- drivers/isdn/sc/includes.h | 16 -- drivers/isdn/sc/init.c | 549 ----------------------------------------- drivers/isdn/sc/interrupt.c | 247 ------------------- drivers/isdn/sc/ioctl.c | 582 -------------------------------------------- drivers/isdn/sc/message.c | 230 ----------------- drivers/isdn/sc/message.h | 245 ------------------- drivers/isdn/sc/packet.c | 204 ---------------- drivers/isdn/sc/scioc.h | 110 --------- drivers/isdn/sc/shmem.c | 138 ----------- drivers/isdn/sc/timer.c | 122 ---------- 18 files changed, 3136 deletions(-) delete mode 100644 drivers/isdn/sc/Kconfig delete mode 100644 drivers/isdn/sc/Makefile delete mode 100644 drivers/isdn/sc/card.h delete mode 100644 drivers/isdn/sc/command.c delete mode 100644 drivers/isdn/sc/event.c delete mode 100644 drivers/isdn/sc/hardware.h delete mode 100644 drivers/isdn/sc/includes.h delete mode 100644 drivers/isdn/sc/init.c delete mode 100644 drivers/isdn/sc/interrupt.c delete mode 100644 drivers/isdn/sc/ioctl.c delete mode 100644 drivers/isdn/sc/message.c delete mode 100644 drivers/isdn/sc/message.h delete mode 100644 drivers/isdn/sc/packet.c delete mode 100644 drivers/isdn/sc/scioc.h delete mode 100644 drivers/isdn/sc/shmem.c delete mode 100644 drivers/isdn/sc/timer.c (limited to 'drivers') diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile index f1f777570e8e..91c81965e7ca 100644 --- a/drivers/isdn/Makefile +++ b/drivers/isdn/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_ISDN_DIVERSION) += divert/ obj-$(CONFIG_ISDN_DRV_HISAX) += hisax/ obj-$(CONFIG_ISDN_DRV_ICN) += icn/ obj-$(CONFIG_ISDN_DRV_PCBIT) += pcbit/ -obj-$(CONFIG_ISDN_DRV_SC) += sc/ obj-$(CONFIG_ISDN_DRV_LOOP) += isdnloop/ obj-$(CONFIG_ISDN_DRV_ACT2000) += act2000/ obj-$(CONFIG_HYSDN) += hysdn/ diff --git a/drivers/isdn/i4l/Kconfig b/drivers/isdn/i4l/Kconfig index 9c6650ea848e..f5b714cd7618 100644 --- a/drivers/isdn/i4l/Kconfig +++ b/drivers/isdn/i4l/Kconfig @@ -130,8 +130,6 @@ source "drivers/isdn/icn/Kconfig" source "drivers/isdn/pcbit/Kconfig" -source "drivers/isdn/sc/Kconfig" - source "drivers/isdn/act2000/Kconfig" endmenu diff --git a/drivers/isdn/sc/Kconfig b/drivers/isdn/sc/Kconfig deleted file mode 100644 index 7469863a7925..000000000000 --- a/drivers/isdn/sc/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -config ISDN_DRV_SC - tristate "Spellcaster support" - depends on ISA - help - This enables support for the Spellcaster BRI ISDN boards. This - driver currently builds only in a modularized version. - To build it, choose M here: the module will be called sc. - See for more information. diff --git a/drivers/isdn/sc/Makefile b/drivers/isdn/sc/Makefile deleted file mode 100644 index 0f2b7d602ac0..000000000000 --- a/drivers/isdn/sc/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# Makefile for the sc ISDN device driver - -# Each configuration option enables a list of files. - -obj-$(CONFIG_ISDN_DRV_SC) += sc.o - -# Multipart objects. - -sc-y := shmem.o init.o packet.o command.o event.o \ - ioctl.o interrupt.o message.o timer.o diff --git a/drivers/isdn/sc/card.h b/drivers/isdn/sc/card.h deleted file mode 100644 index 3da69ee43da7..000000000000 --- a/drivers/isdn/sc/card.h +++ /dev/null @@ -1,131 +0,0 @@ -/* $Id: card.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $ - * - * Driver parameters for SpellCaster ISA ISDN adapters - * - * Copyright (C) 1996 SpellCaster Telecommunications Inc. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * For more information, please contact gpl-info@spellcast.com or write: - * - * SpellCaster Telecommunications Inc. - * 5621 Finch Avenue East, Unit #3 - * Scarborough, Ontario Canada - * M1B 2T9 - * +1 (416) 297-8565 - * +1 (416) 297-6433 Facsimile - */ - -#ifndef CARD_H -#define CARD_H - -/* - * We need these if they're not already included - */ -#include -#include -#include -#include -#include "message.h" -#include "scioc.h" - -/* - * Amount of time to wait for a reset to complete - */ -#define CHECKRESET_TIME msecs_to_jiffies(4000) - -/* - * Amount of time between line status checks - */ -#define CHECKSTAT_TIME msecs_to_jiffies(8000) - -/* - * The maximum amount of time to wait for a message response - * to arrive. Use exclusively by send_and_receive - */ -#define SAR_TIMEOUT msecs_to_jiffies(10000) - -/* - * Macro to determine is a card id is valid - */ -#define IS_VALID_CARD(x) ((x >= 0) && (x <= cinst)) - -/* - * Per channel status and configuration - */ -typedef struct { - int l2_proto; - int l3_proto; - char dn[50]; - unsigned long first_sendbuf; /* Offset of first send buffer */ - unsigned int num_sendbufs; /* Number of send buffers */ - unsigned int free_sendbufs; /* Number of free sendbufs */ - unsigned int next_sendbuf; /* Next sequential buffer */ - char eazlist[50]; /* Set with SETEAZ */ - char sillist[50]; /* Set with SETSIL */ - int eazclear; /* Don't accept calls if TRUE */ -} bchan; - -/* - * Everything you want to know about the adapter ... - */ -typedef struct { - int model; - int driverId; /* LL Id */ - char devicename[20]; /* The device name */ - isdn_if *card; /* ISDN4Linux structure */ - bchan *channel; /* status of the B channels */ - char nChannels; /* Number of channels */ - unsigned int interrupt; /* Interrupt number */ - int iobase; /* I/O Base address */ - int ioport[MAX_IO_REGS]; /* Index to I/O ports */ - int shmem_pgport; /* port for the exp mem page reg. */ - int shmem_magic; /* adapter magic number */ - unsigned int rambase; /* Shared RAM base address */ - unsigned int ramsize; /* Size of shared memory */ - RspMessage async_msg; /* Async response message */ - int want_async_messages; /* Snoop the Q ? */ - unsigned char seq_no; /* Next send seq. number */ - struct timer_list reset_timer; /* Check reset timer */ - struct timer_list stat_timer; /* Check startproc timer */ - unsigned char nphystat; /* Latest PhyStat info */ - unsigned char phystat; /* Last PhyStat info */ - HWConfig_pl hwconfig; /* Hardware config info */ - char load_ver[11]; /* CommManage Version string */ - char proc_ver[11]; /* CommEngine Version */ - int StartOnReset; /* Indicates startproc after reset */ - int EngineUp; /* Indicates CommEngine Up */ - int trace_mode; /* Indicate if tracing is on */ - spinlock_t lock; /* local lock */ -} board; - - -extern board *sc_adapter[]; -extern int cinst; - -void memcpy_toshmem(int card, void *dest, const void *src, size_t n); -void memcpy_fromshmem(int card, void *dest, const void *src, size_t n); -int get_card_from_id(int driver); -int indicate_status(int card, int event, ulong Channel, char *Data); -irqreturn_t interrupt_handler(int interrupt, void *cardptr); -int sndpkt(int devId, int channel, int ack, struct sk_buff *data); -void rcvpkt(int card, RspMessage *rcvmsg); -int command(isdn_ctrl *cmd); -int reset(int card); -int startproc(int card); -int send_and_receive(int card, unsigned int procid, unsigned char type, - unsigned char class, unsigned char code, - unsigned char link, unsigned char data_len, - unsigned char *data, RspMessage *mesgdata, int timeout); -void flushreadfifo(int card); -int sendmessage(int card, unsigned int procid, unsigned int type, - unsigned int class, unsigned int code, unsigned int link, - unsigned int data_len, unsigned int *data); -int receivemessage(int card, RspMessage *rspmsg); -int sc_ioctl(int card, scs_ioctl *data); -int setup_buffers(int card, int c); -void sc_check_reset(unsigned long data); -void check_phystat(unsigned long data); - -#endif /* CARD_H */ diff --git a/drivers/isdn/sc/command.c b/drivers/isdn/sc/command.c deleted file mode 100644 index 4a4e66152ce7..000000000000 --- a/drivers/isdn/sc/command.c +++ /dev/null @@ -1,363 +0,0 @@ -/* $Id: command.c,v 1.4.10.1 2001/09/23 22:24:59 kai Exp $ - * - * Copyright (C) 1996 SpellCaster Telecommunications Inc. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * For more information, please contact gpl-info@spellcast.com or write: - * - * SpellCaster Telecommunications Inc. - * 5621 Finch Avenue East, Unit #3 - * Scarborough, Ontario Canada - * M1B 2T9 - * +1 (416) 297-8565 - * +1 (416) 297-6433 Facsimile - */ - -#include -#include "includes.h" /* This must be first */ -#include "hardware.h" -#include "message.h" -#include "card.h" -#include "scioc.h" - -static int dial(int card, unsigned long channel, setup_parm setup); -static int hangup(int card, unsigned long channel); -static int answer(int card, unsigned long channel); -static int clreaz(int card, unsigned long channel); -static int seteaz(int card, unsigned long channel, char *); -static int setl2(int card, unsigned long arg); -static int setl3(int card, unsigned long arg); -static int acceptb(int card, unsigned long channel); - -#ifdef DEBUG -/* - * Translate command codes to strings - */ -static char *commands[] = { "ISDN_CMD_IOCTL", - "ISDN_CMD_DIAL", - "ISDN_CMD_ACCEPTB", - "ISDN_CMD_ACCEPTB", - "ISDN_CMD_HANGUP", - "ISDN_CMD_CLREAZ", - "ISDN_CMD_SETEAZ", - NULL, - NULL, - NULL, - "ISDN_CMD_SETL2", - NULL, - "ISDN_CMD_SETL3", - NULL, - NULL, - NULL, - NULL, - NULL, }; - -/* - * Translates ISDN4Linux protocol codes to strings for debug messages - */ -static char *l3protos[] = { "ISDN_PROTO_L3_TRANS" }; -static char *l2protos[] = { "ISDN_PROTO_L2_X75I", - "ISDN_PROTO_L2_X75UI", - "ISDN_PROTO_L2_X75BUI", - "ISDN_PROTO_L2_HDLC", - "ISDN_PROTO_L2_TRANS" }; -#endif - -int get_card_from_id(int driver) -{ - int i; - - for (i = 0; i < cinst; i++) { - if (sc_adapter[i]->driverId == driver) - return i; - } - return -ENODEV; -} - -/* - * command - */ - -int command(isdn_ctrl *cmd) -{ - int card; - - card = get_card_from_id(cmd->driver); - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - /* - * Dispatch the command - */ - switch (cmd->command) { - case ISDN_CMD_IOCTL: - { - unsigned long cmdptr; - scs_ioctl ioc; - - memcpy(&cmdptr, cmd->parm.num, sizeof(unsigned long)); - if (copy_from_user(&ioc, (scs_ioctl __user *)cmdptr, - sizeof(scs_ioctl))) { - pr_debug("%s: Failed to verify user space 0x%lx\n", - sc_adapter[card]->devicename, cmdptr); - return -EFAULT; - } - return sc_ioctl(card, &ioc); - } - case ISDN_CMD_DIAL: - return dial(card, cmd->arg, cmd->parm.setup); - case ISDN_CMD_HANGUP: - return hangup(card, cmd->arg); - case ISDN_CMD_ACCEPTD: - return answer(card, cmd->arg); - case ISDN_CMD_ACCEPTB: - return acceptb(card, cmd->arg); - case ISDN_CMD_CLREAZ: - return clreaz(card, cmd->arg); - case ISDN_CMD_SETEAZ: - return seteaz(card, cmd->arg, cmd->parm.num); - case ISDN_CMD_SETL2: - return setl2(card, cmd->arg); - case ISDN_CMD_SETL3: - return setl3(card, cmd->arg); - default: - return -EINVAL; - } - return 0; -} - -/* - * start the onboard firmware - */ -int startproc(int card) -{ - int status; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - /* - * send start msg - */ - status = sendmessage(card, CMPID, cmReqType2, - cmReqClass0, - cmReqStartProc, - 0, 0, NULL); - pr_debug("%s: Sent startProc\n", sc_adapter[card]->devicename); - - return status; -} - - -/* - * Dials the number passed in - */ -static int dial(int card, unsigned long channel, setup_parm setup) -{ - int status; - char Phone[48]; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - /*extract ISDN number to dial from eaz/msn string*/ - strcpy(Phone, setup.phone); - - /*send the connection message*/ - status = sendmessage(card, CEPID, ceReqTypePhy, - ceReqClass1, - ceReqPhyConnect, - (unsigned char)channel + 1, - strlen(Phone), - (unsigned int *)Phone); - - pr_debug("%s: Dialing %s on channel %lu\n", - sc_adapter[card]->devicename, Phone, channel + 1); - - return status; -} - -/* - * Answer an incoming call - */ -static int answer(int card, unsigned long channel) -{ - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - if (setup_buffers(card, channel + 1)) { - hangup(card, channel + 1); - return -ENOBUFS; - } - - indicate_status(card, ISDN_STAT_BCONN, channel, NULL); - pr_debug("%s: Answered incoming call on channel %lu\n", - sc_adapter[card]->devicename, channel + 1); - return 0; -} - -/* - * Hangup up the call on specified channel - */ -static int hangup(int card, unsigned long channel) -{ - int status; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - status = sendmessage(card, CEPID, ceReqTypePhy, - ceReqClass1, - ceReqPhyDisconnect, - (unsigned char)channel + 1, - 0, - NULL); - pr_debug("%s: Sent HANGUP message to channel %lu\n", - sc_adapter[card]->devicename, channel + 1); - return status; -} - -/* - * Set the layer 2 protocol (X.25, HDLC, Raw) - */ -static int setl2(int card, unsigned long arg) -{ - int status = 0; - int protocol, channel; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - protocol = arg >> 8; - channel = arg & 0xff; - sc_adapter[card]->channel[channel].l2_proto = protocol; - - /* - * check that the adapter is also set to the correct protocol - */ - pr_debug("%s: Sending GetFrameFormat for channel %d\n", - sc_adapter[card]->devicename, channel + 1); - status = sendmessage(card, CEPID, ceReqTypeCall, - ceReqClass0, - ceReqCallGetFrameFormat, - (unsigned char)channel + 1, - 1, - (unsigned int *)protocol); - if (status) - return status; - return 0; -} - -/* - * Set the layer 3 protocol - */ -static int setl3(int card, unsigned long channel) -{ - int protocol = channel >> 8; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - sc_adapter[card]->channel[channel].l3_proto = protocol; - return 0; -} - -static int acceptb(int card, unsigned long channel) -{ - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - if (setup_buffers(card, channel + 1)) - { - hangup(card, channel + 1); - return -ENOBUFS; - } - - pr_debug("%s: B-Channel connection accepted on channel %lu\n", - sc_adapter[card]->devicename, channel + 1); - indicate_status(card, ISDN_STAT_BCONN, channel, NULL); - return 0; -} - -static int clreaz(int card, unsigned long arg) -{ - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - strcpy(sc_adapter[card]->channel[arg].eazlist, ""); - sc_adapter[card]->channel[arg].eazclear = 1; - pr_debug("%s: EAZ List cleared for channel %lu\n", - sc_adapter[card]->devicename, arg + 1); - return 0; -} - -static int seteaz(int card, unsigned long arg, char *num) -{ - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - strcpy(sc_adapter[card]->channel[arg].eazlist, num); - sc_adapter[card]->channel[arg].eazclear = 0; - pr_debug("%s: EAZ list for channel %lu set to: %s\n", - sc_adapter[card]->devicename, arg + 1, - sc_adapter[card]->channel[arg].eazlist); - return 0; -} - -int reset(int card) -{ - unsigned long flags; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - indicate_status(card, ISDN_STAT_STOP, 0, NULL); - - if (sc_adapter[card]->EngineUp) { - del_timer(&sc_adapter[card]->stat_timer); - } - - sc_adapter[card]->EngineUp = 0; - - spin_lock_irqsave(&sc_adapter[card]->lock, flags); - init_timer(&sc_adapter[card]->reset_timer); - sc_adapter[card]->reset_timer.function = sc_check_reset; - sc_adapter[card]->reset_timer.data = card; - sc_adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME; - add_timer(&sc_adapter[card]->reset_timer); - spin_unlock_irqrestore(&sc_adapter[card]->lock, flags); - - outb(0x1, sc_adapter[card]->ioport[SFT_RESET]); - - pr_debug("%s: Adapter Reset\n", sc_adapter[card]->devicename); - return 0; -} - -void flushreadfifo(int card) -{ - while (inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA) - inb(sc_adapter[card]->ioport[FIFO_READ]); -} diff --git a/drivers/isdn/sc/event.c b/drivers/isdn/sc/event.c deleted file mode 100644 index 833d96c2cf92..000000000000 --- a/drivers/isdn/sc/event.c +++ /dev/null @@ -1,68 +0,0 @@ -/* $Id: event.c,v 1.4.8.1 2001/09/23 22:24:59 kai Exp $ - * - * Copyright (C) 1996 SpellCaster Telecommunications Inc. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * For more information, please contact gpl-info@spellcast.com or write: - * - * SpellCaster Telecommunications Inc. - * 5621 Finch Avenue East, Unit #3 - * Scarborough, Ontario Canada - * M1B 2T9 - * +1 (416) 297-8565 - * +1 (416) 297-6433 Facsimile - */ - -#include "includes.h" -#include "hardware.h" -#include "message.h" -#include "card.h" - -#ifdef DEBUG -static char *events[] = { "ISDN_STAT_STAVAIL", - "ISDN_STAT_ICALL", - "ISDN_STAT_RUN", - "ISDN_STAT_STOP", - "ISDN_STAT_DCONN", - "ISDN_STAT_BCONN", - "ISDN_STAT_DHUP", - "ISDN_STAT_BHUP", - "ISDN_STAT_CINF", - "ISDN_STAT_LOAD", - "ISDN_STAT_UNLOAD", - "ISDN_STAT_BSENT", - "ISDN_STAT_NODCH", - "ISDN_STAT_ADDCH", - "ISDN_STAT_CAUSE" }; -#endif - -int indicate_status(int card, int event, ulong Channel, char *Data) -{ - isdn_ctrl cmd; - -#ifdef DEBUG - pr_debug("%s: Indicating event %s on Channel %d\n", - sc_adapter[card]->devicename, events[event - 256], Channel); -#endif - if (Data != NULL) { - pr_debug("%s: Event data: %s\n", sc_adapter[card]->devicename, - Data); - switch (event) { - case ISDN_STAT_BSENT: - memcpy(&cmd.parm.length, Data, sizeof(cmd.parm.length)); - break; - case ISDN_STAT_ICALL: - memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup)); - break; - default: - strlcpy(cmd.parm.num, Data, sizeof(cmd.parm.num)); - } - } - - cmd.command = event; - cmd.driver = sc_adapter[card]->driverId; - cmd.arg = Channel; - return sc_adapter[card]->card->statcallb(&cmd); -} diff --git a/drivers/isdn/sc/hardware.h b/drivers/isdn/sc/hardware.h deleted file mode 100644 index 81fbe78701f0..000000000000 --- a/drivers/isdn/sc/hardware.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Hardware specific macros, defines and structures - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#ifndef HARDWARE_H -#define HARDWARE_H - -#include /* For HZ */ - -/* - * General hardware parameters common to all ISA adapters - */ - -#define MAX_CARDS 4 /* The maximum number of cards to - control or probe for. */ - -#define SIGNATURE 0x87654321 /* Board reset signature */ -#define SIG_OFFSET 0x1004 /* Where to find signature in shared RAM */ -#define TRACE_OFFSET 0x1008 /* Trace enable word offset in shared RAM */ -#define BUFFER_OFFSET 0x1800 /* Beginning of buffers */ - -/* I/O Port parameters */ -#define IOBASE_MIN 0x180 /* Lowest I/O port address */ -#define IOBASE_MAX 0x3C0 /* Highest I/O port address */ -#define IOBASE_OFFSET 0x20 /* Inter-board I/O port gap used during - probing */ -#define FIFORD_OFFSET 0x0 -#define FIFOWR_OFFSET 0x400 -#define FIFOSTAT_OFFSET 0x1000 -#define RESET_OFFSET 0x2800 -#define PG0_OFFSET 0x3000 /* Offset from I/O Base for Page 0 register */ -#define PG1_OFFSET 0x3400 /* Offset from I/O Base for Page 1 register */ -#define PG2_OFFSET 0x3800 /* Offset from I/O Base for Page 2 register */ -#define PG3_OFFSET 0x3C00 /* Offset from I/O Base for Page 3 register */ - -#define FIFO_READ 0 /* FIFO Read register */ -#define FIFO_WRITE 1 /* FIFO Write rgister */ -#define LO_ADDR_PTR 2 /* Extended RAM Low Addr Pointer */ -#define HI_ADDR_PTR 3 /* Extended RAM High Addr Pointer */ -#define NOT_USED_1 4 -#define FIFO_STATUS 5 /* FIFO Status Register */ -#define NOT_USED_2 6 -#define MEM_OFFSET 7 -#define SFT_RESET 10 /* Reset Register */ -#define EXP_BASE 11 /* Shared RAM Base address */ -#define EXP_PAGE0 12 /* Shared RAM Page0 register */ -#define EXP_PAGE1 13 /* Shared RAM Page1 register */ -#define EXP_PAGE2 14 /* Shared RAM Page2 register */ -#define EXP_PAGE3 15 /* Shared RAM Page3 register */ -#define IRQ_SELECT 16 /* IRQ selection register */ -#define MAX_IO_REGS 17 /* Total number of I/O ports */ - -/* FIFO register values */ -#define RF_HAS_DATA 0x01 /* fifo has data */ -#define RF_QUART_FULL 0x02 /* fifo quarter full */ -#define RF_HALF_FULL 0x04 /* fifo half full */ -#define RF_NOT_FULL 0x08 /* fifo not full */ -#define WF_HAS_DATA 0x10 /* fifo has data */ -#define WF_QUART_FULL 0x20 /* fifo quarter full */ -#define WF_HALF_FULL 0x40 /* fifo half full */ -#define WF_NOT_FULL 0x80 /* fifo not full */ - -/* Shared RAM parameters */ -#define SRAM_MIN 0xC0000 /* Lowest host shared RAM address */ -#define SRAM_MAX 0xEFFFF /* Highest host shared RAM address */ -#define SRAM_PAGESIZE 0x4000 /* Size of one RAM page (16K) */ - -/* Shared RAM buffer parameters */ -#define BUFFER_SIZE 0x800 /* The size of a buffer in bytes */ -#define BUFFER_BASE BUFFER_OFFSET /* Offset from start of shared RAM - where buffer start */ -#define BUFFERS_MAX 16 /* Maximum number of send/receive - buffers per channel */ -#define HDLC_PROTO 0x01 /* Frame Format for Layer 2 */ - -#define BRI_BOARD 0 -#define POTS_BOARD 1 -#define PRI_BOARD 2 - -/* - * Specific hardware parameters for the DataCommute/BRI - */ -#define BRI_CHANNELS 2 /* Number of B channels */ -#define BRI_BASEPG_VAL 0x98 -#define BRI_MAGIC 0x60000 /* Magic Number */ -#define BRI_MEMSIZE 0x10000 /* Amount of RAM (64K) */ -#define BRI_PARTNO "72-029" -#define BRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS; -/* - * Specific hardware parameters for the DataCommute/PRI - */ -#define PRI_CHANNELS 23 /* Number of B channels */ -#define PRI_BASEPG_VAL 0x88 -#define PRI_MAGIC 0x20000 /* Magic Number */ -#define PRI_MEMSIZE 0x100000 /* Amount of RAM (1M) */ -#define PRI_PARTNO "72-030" -#define PRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS; - -/* - * Some handy macros - */ - -/* Determine if a channel number is valid for the adapter */ -#define IS_VALID_CHANNEL(y, x) ((x > 0) && (x <= sc_adapter[y]->channels)) - -#endif diff --git a/drivers/isdn/sc/includes.h b/drivers/isdn/sc/includes.h deleted file mode 100644 index 4766e5b77378..000000000000 --- a/drivers/isdn/sc/includes.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c deleted file mode 100644 index 3597ef47b28a..000000000000 --- a/drivers/isdn/sc/init.c +++ /dev/null @@ -1,549 +0,0 @@ -/* - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include -#include -#include -#include -#include -#include -#include "includes.h" -#include "hardware.h" -#include "card.h" - -MODULE_DESCRIPTION("ISDN4Linux: Driver for Spellcaster card"); -MODULE_AUTHOR("Spellcaster Telecommunications Inc."); -MODULE_LICENSE("GPL"); - -board *sc_adapter[MAX_CARDS]; -int cinst; - -static char devname[] = "scX"; -static const char version[] = "2.0b1"; - -static const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" }; - -/* insmod set parameters */ -static unsigned int io[] = {0, 0, 0, 0}; -static unsigned char irq[] = {0, 0, 0, 0}; -static unsigned long ram[] = {0, 0, 0, 0}; -static bool do_reset; - -module_param_array(io, int, NULL, 0); -module_param_array(irq, byte, NULL, 0); -module_param_array(ram, long, NULL, 0); -module_param(do_reset, bool, 0); - -static int identify_board(unsigned long, unsigned int); - -static int __init sc_init(void) -{ - int b = -1; - int i, j; - int status = -ENODEV; - - unsigned long memsize = 0; - unsigned long features = 0; - isdn_if *interface; - unsigned char channels; - unsigned char pgport; - unsigned long magic; - int model; - int last_base = IOBASE_MIN; - int probe_exhasted = 0; - -#ifdef MODULE - pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version); -#else - pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version); -#endif - pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n"); - - while (b++ < MAX_CARDS - 1) { - pr_debug("Probing for adapter #%d\n", b); - /* - * Initialize reusable variables - */ - model = -1; - magic = 0; - channels = 0; - pgport = 0; - - /* - * See if we should probe for IO base - */ - pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b], - io[b] == 0 ? "will" : "won't"); - if (io[b]) { - /* - * No, I/O Base has been provided - */ - for (i = 0; i < MAX_IO_REGS - 1; i++) { - if (!request_region(io[b] + i * 0x400, 1, "sc test")) { - pr_debug("request_region for 0x%x failed\n", io[b] + i * 0x400); - io[b] = 0; - break; - } else - release_region(io[b] + i * 0x400, 1); - } - - /* - * Confirm the I/O Address with a test - */ - if (io[b] == 0) { - pr_debug("I/O Address invalid.\n"); - continue; - } - - outb(0x18, io[b] + 0x400 * EXP_PAGE0); - if (inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) { - pr_debug("I/O Base 0x%x fails test\n", - io[b] + 0x400 * EXP_PAGE0); - continue; - } - } else { - /* - * Yes, probe for I/O Base - */ - if (probe_exhasted) { - pr_debug("All probe addresses exhausted, skipping\n"); - continue; - } - pr_debug("Probing for I/O...\n"); - for (i = last_base; i <= IOBASE_MAX; i += IOBASE_OFFSET) { - int found_io = 1; - if (i == IOBASE_MAX) { - probe_exhasted = 1; /* No more addresses to probe */ - pr_debug("End of Probes\n"); - } - last_base = i + IOBASE_OFFSET; - pr_debug(" checking 0x%x...", i); - for (j = 0; j < MAX_IO_REGS - 1; j++) { - if (!request_region(i + j * 0x400, 1, "sc test")) { - pr_debug("Failed\n"); - found_io = 0; - break; - } else - release_region(i + j * 0x400, 1); - } - - if (found_io) { - io[b] = i; - outb(0x18, io[b] + 0x400 * EXP_PAGE0); - if (inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) { - pr_debug("Failed by test\n"); - continue; - } - pr_debug("Passed\n"); - break; - } - } - if (probe_exhasted) { - continue; - } - } - - /* - * See if we should probe for shared RAM - */ - if (do_reset) { - pr_debug("Doing a SAFE probe reset\n"); - outb(0xFF, io[b] + RESET_OFFSET); - msleep_interruptible(10000); - } - pr_debug("RAM Base for board %d is 0x%lx, %s probe\n", b, - ram[b], ram[b] == 0 ? "will" : "won't"); - - if (ram[b]) { - /* - * No, the RAM base has been provided - * Just look for a signature and ID the - * board model - */ - if (request_region(ram[b], SRAM_PAGESIZE, "sc test")) { - pr_debug("request_region for RAM base 0x%lx succeeded\n", ram[b]); - model = identify_board(ram[b], io[b]); - release_region(ram[b], SRAM_PAGESIZE); - } - } else { - /* - * Yes, probe for free RAM and look for - * a signature and id the board model - */ - for (i = SRAM_MIN; i < SRAM_MAX; i += SRAM_PAGESIZE) { - pr_debug("Checking RAM address 0x%x...\n", i); - if (request_region(i, SRAM_PAGESIZE, "sc test")) { - pr_debug(" request_region succeeded\n"); - model = identify_board(i, io[b]); - release_region(i, SRAM_PAGESIZE); - if (model >= 0) { - pr_debug(" Identified a %s\n", - boardname[model]); - ram[b] = i; - break; - } - pr_debug(" Unidentified or inaccessible\n"); - continue; - } - pr_debug(" request failed\n"); - } - } - /* - * See if we found free RAM and the board model - */ - if (!ram[b] || model < 0) { - /* - * Nope, there was no place in RAM for the - * board, or it couldn't be identified - */ - pr_debug("Failed to find an adapter at 0x%lx\n", ram[b]); - continue; - } - - /* - * Set the board's magic number, memory size and page register - */ - switch (model) { - case PRI_BOARD: - channels = 23; - magic = 0x20000; - memsize = 0x100000; - features = PRI_FEATURES; - break; - - case BRI_BOARD: - case POTS_BOARD: - channels = 2; - magic = 0x60000; - memsize = 0x10000; - features = BRI_FEATURES; - break; - } - switch (ram[b] >> 12 & 0x0F) { - case 0x0: - pr_debug("RAM Page register set to EXP_PAGE0\n"); - pgport = EXP_PAGE0; - break; - - case 0x4: - pr_debug("RAM Page register set to EXP_PAGE1\n"); - pgport = EXP_PAGE1; - break; - - case 0x8: - pr_debug("RAM Page register set to EXP_PAGE2\n"); - pgport = EXP_PAGE2; - break; - - case 0xC: - pr_debug("RAM Page register set to EXP_PAGE3\n"); - pgport = EXP_PAGE3; - break; - - default: - pr_debug("RAM base address doesn't fall on 16K boundary\n"); - continue; - } - - pr_debug("current IRQ: %d b: %d\n", irq[b], b); - - /* - * Make sure we got an IRQ - */ - if (!irq[b]) { - /* - * No interrupt could be used - */ - pr_debug("Failed to acquire an IRQ line\n"); - continue; - } - - /* - * Horray! We found a board, Make sure we can register - * it with ISDN4Linux - */ - interface = kzalloc(sizeof(isdn_if), GFP_KERNEL); - if (interface == NULL) { - /* - * Oops, can't malloc isdn_if - */ - continue; - } - - interface->owner = THIS_MODULE; - interface->hl_hdrlen = 0; - interface->channels = channels; - interface->maxbufsize = BUFFER_SIZE; - interface->features = features; - interface->writebuf_skb = sndpkt; - interface->writecmd = NULL; - interface->command = command; - strcpy(interface->id, devname); - interface->id[2] = '0' + cinst; - - /* - * Allocate the board structure - */ - sc_adapter[cinst] = kzalloc(sizeof(board), GFP_KERNEL); - if (sc_adapter[cinst] == NULL) { - /* - * Oops, can't alloc memory for the board - */ - kfree(interface); - continue; - } - spin_lock_init(&sc_adapter[cinst]->lock); - - if (!register_isdn(interface)) { - /* - * Oops, couldn't register for some reason - */ - kfree(interface); - kfree(sc_adapter[cinst]); - continue; - } - - sc_adapter[cinst]->card = interface; - sc_adapter[cinst]->driverId = interface->channels; - strcpy(sc_adapter[cinst]->devicename, interface->id); - sc_adapter[cinst]->nChannels = channels; - sc_adapter[cinst]->ramsize = memsize; - sc_adapter[cinst]->shmem_magic = magic; - sc_adapter[cinst]->shmem_pgport = pgport; - sc_adapter[cinst]->StartOnReset = 1; - - /* - * Allocate channels status structures - */ - sc_adapter[cinst]->channel = kzalloc(sizeof(bchan) * channels, GFP_KERNEL); - if (sc_adapter[cinst]->channel == NULL) { - /* - * Oops, can't alloc memory for the channels - */ - indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */ - kfree(interface); - kfree(sc_adapter[cinst]); - continue; - } - - /* - * Lock down the hardware resources - */ - sc_adapter[cinst]->interrupt = irq[b]; - if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler, - 0, interface->id, - (void *)(unsigned long) cinst)) { - kfree(sc_adapter[cinst]->channel); - indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */ - kfree(interface); - kfree(sc_adapter[cinst]); - continue; - - } - sc_adapter[cinst]->iobase = io[b]; - for (i = 0; i < MAX_IO_REGS - 1; i++) { - sc_adapter[cinst]->ioport[i] = io[b] + i * 0x400; - request_region(sc_adapter[cinst]->ioport[i], 1, - interface->id); - pr_debug("Requesting I/O Port %#x\n", - sc_adapter[cinst]->ioport[i]); - } - sc_adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2; - request_region(sc_adapter[cinst]->ioport[IRQ_SELECT], 1, - interface->id); - pr_debug("Requesting I/O Port %#x\n", - sc_adapter[cinst]->ioport[IRQ_SELECT]); - sc_adapter[cinst]->rambase = ram[b]; - request_region(sc_adapter[cinst]->rambase, SRAM_PAGESIZE, - interface->id); - - pr_info(" %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n", - sc_adapter[cinst]->devicename, - sc_adapter[cinst]->driverId, - boardname[model], channels, irq[b], io[b], ram[b]); - - /* - * reset the adapter to put things in motion - */ - reset(cinst); - - cinst++; - status = 0; - } - if (status) - pr_info("Failed to find any adapters, driver unloaded\n"); - return status; -} - -static void __exit sc_exit(void) -{ - int i, j; - - for (i = 0; i < cinst; i++) { - pr_debug("Cleaning up after adapter %d\n", i); - /* - * kill the timers - */ - del_timer_sync(&(sc_adapter[i]->reset_timer)); - del_timer_sync(&(sc_adapter[i]->stat_timer)); - - /* - * Tell I4L we're toast - */ - indicate_status(i, ISDN_STAT_STOP, 0, NULL); - indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL); - - /* - * Release shared RAM - */ - release_region(sc_adapter[i]->rambase, SRAM_PAGESIZE); - - /* - * Release the IRQ - */ - free_irq(sc_adapter[i]->interrupt, NULL); - - /* - * Reset for a clean start - */ - outb(0xFF, sc_adapter[i]->ioport[SFT_RESET]); - - /* - * Release the I/O Port regions - */ - for (j = 0; j < MAX_IO_REGS - 1; j++) { - release_region(sc_adapter[i]->ioport[j], 1); - pr_debug("Releasing I/O Port %#x\n", - sc_adapter[i]->ioport[j]); - } - release_region(sc_adapter[i]->ioport[IRQ_SELECT], 1); - pr_debug("Releasing I/O Port %#x\n", - sc_adapter[i]->ioport[IRQ_SELECT]); - - /* - * Release any memory we alloced - */ - kfree(sc_adapter[i]->channel); - kfree(sc_adapter[i]->card); - kfree(sc_adapter[i]); - } - pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n"); -} - -static int identify_board(unsigned long rambase, unsigned int iobase) -{ - unsigned int pgport; - unsigned long sig; - DualPortMemory *dpm; - RspMessage rcvmsg; - ReqMessage sndmsg; - HWConfig_pl hwci; - int x; - - pr_debug("Attempting to identify adapter @ 0x%lx io 0x%x\n", - rambase, iobase); - - /* - * Enable the base pointer - */ - outb(rambase >> 12, iobase + 0x2c00); - - switch (rambase >> 12 & 0x0F) { - case 0x0: - pgport = iobase + PG0_OFFSET; - pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET); - break; - - case 0x4: - pgport = iobase + PG1_OFFSET; - pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET); - break; - - case 0x8: - pgport = iobase + PG2_OFFSET; - pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET); - break; - - case 0xC: - pgport = iobase + PG3_OFFSET; - pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET); - break; - default: - pr_debug("Invalid rambase 0x%lx\n", rambase); - return -1; - } - - /* - * Try to identify a PRI card - */ - outb(PRI_BASEPG_VAL, pgport); - msleep_interruptible(1000); - sig = readl(rambase + SIG_OFFSET); - pr_debug("Looking for a signature, got 0x%lx\n", sig); - if (sig == SIGNATURE) - return PRI_BOARD; - - /* - * Try to identify a PRI card - */ - outb(BRI_BASEPG_VAL, pgport); - msleep_interruptible(1000); - sig = readl(rambase + SIG_OFFSET); - pr_debug("Looking for a signature, got 0x%lx\n", sig); - if (sig == SIGNATURE) - return BRI_BOARD; - - return -1; - - /* - * Try to spot a card - */ - sig = readl(rambase + SIG_OFFSET); - pr_debug("Looking for a signature, got 0x%lx\n", sig); - if (sig != SIGNATURE) - return -1; - - dpm = (DualPortMemory *) rambase; - - memset(&sndmsg, 0, MSG_LEN); - sndmsg.msg_byte_cnt = 3; - sndmsg.type = cmReqType1; - sndmsg.class = cmReqClass0; - sndmsg.code = cmReqHWConfig; - memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN); - outb(0, iobase + 0x400); - pr_debug("Sent HWConfig message\n"); - /* - * Wait for the response - */ - x = 0; - while ((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) { - schedule_timeout_interruptible(1); - x++; - } - if (x == 100) { - pr_debug("Timeout waiting for response\n"); - return -1; - } - - memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN); - pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status); - memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl)); - pr_debug("Hardware Config: Interface: %s, RAM Size: %ld, Serial: %s\n" - " Part: %s, Rev: %s\n", - hwci.st_u_sense ? "S/T" : "U", hwci.ram_size, - hwci.serial_no, hwci.part_no, hwci.rev_no); - - if (!strncmp(PRI_PARTNO, hwci.part_no, 6)) - return PRI_BOARD; - if (!strncmp(BRI_PARTNO, hwci.part_no, 6)) - return BRI_BOARD; - - return -1; -} - -module_init(sc_init); -module_exit(sc_exit); diff --git a/drivers/isdn/sc/interrupt.c b/drivers/isdn/sc/interrupt.c deleted file mode 100644 index e80cc76bc314..000000000000 --- a/drivers/isdn/sc/interrupt.c +++ /dev/null @@ -1,247 +0,0 @@ -/* $Id: interrupt.c,v 1.4.8.3 2001/09/23 22:24:59 kai Exp $ - * - * Copyright (C) 1996 SpellCaster Telecommunications Inc. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * For more information, please contact gpl-info@spellcast.com or write: - * - * SpellCaster Telecommunications Inc. - * 5621 Finch Avenue East, Unit #3 - * Scarborough, Ontario Canada - * M1B 2T9 - * +1 (416) 297-8565 - * +1 (416) 297-6433 Facsimile - */ - -#include "includes.h" -#include "hardware.h" -#include "message.h" -#include "card.h" -#include - -/* - * - */ -irqreturn_t interrupt_handler(int dummy, void *card_inst) -{ - - RspMessage rcvmsg; - int channel; - int card = (int)(unsigned long) card_inst; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return IRQ_NONE; - } - - pr_debug("%s: Entered Interrupt handler\n", - sc_adapter[card]->devicename); - - /* - * Pull all of the waiting messages off the response queue - */ - while (!receivemessage(card, &rcvmsg)) { - /* - * Push the message to the adapter structure for - * send_and_receive to snoop - */ - if (sc_adapter[card]->want_async_messages) - memcpy(&(sc_adapter[card]->async_msg), - &rcvmsg, sizeof(RspMessage)); - - channel = (unsigned int) rcvmsg.phy_link_no; - - /* - * Trap Invalid request messages - */ - if (IS_CM_MESSAGE(rcvmsg, 0, 0, Invalid)) { - pr_debug("%s: Invalid request Message, rsp_status = %d\n", - sc_adapter[card]->devicename, - rcvmsg.rsp_status); - break; - } - - /* - * Check for a linkRead message - */ - if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Read)) - { - pr_debug("%s: Received packet 0x%x bytes long at 0x%lx\n", - sc_adapter[card]->devicename, - rcvmsg.msg_data.response.msg_len, - rcvmsg.msg_data.response.buff_offset); - rcvpkt(card, &rcvmsg); - continue; - - } - - /* - * Handle a write acknoledgement - */ - if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Write)) { - pr_debug("%s: Packet Send ACK on channel %d\n", - sc_adapter[card]->devicename, - rcvmsg.phy_link_no); - sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].free_sendbufs++; - continue; - } - - /* - * Handle a connection message - */ - if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Connect)) - { - unsigned int callid; - setup_parm setup; - pr_debug("%s: Connect message: line %d: status %d: cause 0x%x\n", - sc_adapter[card]->devicename, - rcvmsg.phy_link_no, - rcvmsg.rsp_status, - rcvmsg.msg_data.byte_array[2]); - - memcpy(&callid, rcvmsg.msg_data.byte_array, sizeof(int)); - if (callid >= 0x8000 && callid <= 0xFFFF) - { - pr_debug("%s: Got Dial-Out Rsp\n", - sc_adapter[card]->devicename); - indicate_status(card, ISDN_STAT_DCONN, - (unsigned long)rcvmsg.phy_link_no - 1, NULL); - - } - else if (callid >= 0x0000 && callid <= 0x7FFF) - { - int len; - - pr_debug("%s: Got Incoming Call\n", - sc_adapter[card]->devicename); - len = strlcpy(setup.phone, &(rcvmsg.msg_data.byte_array[4]), - sizeof(setup.phone)); - if (len >= sizeof(setup.phone)) - continue; - len = strlcpy(setup.eazmsn, - sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].dn, - sizeof(setup.eazmsn)); - if (len >= sizeof(setup.eazmsn)) - continue; - setup.si1 = 7; - setup.si2 = 0; - setup.plan = 0; - setup.screen = 0; - - indicate_status(card, ISDN_STAT_ICALL, (unsigned long)rcvmsg.phy_link_no - 1, (char *)&setup); - indicate_status(card, ISDN_STAT_DCONN, (unsigned long)rcvmsg.phy_link_no - 1, NULL); - } - continue; - } - - /* - * Handle a disconnection message - */ - if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Disconnect)) - { - pr_debug("%s: disconnect message: line %d: status %d: cause 0x%x\n", - sc_adapter[card]->devicename, - rcvmsg.phy_link_no, - rcvmsg.rsp_status, - rcvmsg.msg_data.byte_array[2]); - - indicate_status(card, ISDN_STAT_BHUP, (unsigned long)rcvmsg.phy_link_no - 1, NULL); - indicate_status(card, ISDN_STAT_DHUP, (unsigned long)rcvmsg.phy_link_no - 1, NULL); - continue; - - } - - /* - * Handle a startProc engine up message - */ - if (IS_CM_MESSAGE(rcvmsg, 5, 0, MiscEngineUp)) { - pr_debug("%s: Received EngineUp message\n", - sc_adapter[card]->devicename); - sc_adapter[card]->EngineUp = 1; - sendmessage(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber, 1, 0, NULL); - sendmessage(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber, 2, 0, NULL); - init_timer(&sc_adapter[card]->stat_timer); - sc_adapter[card]->stat_timer.function = check_phystat; - sc_adapter[card]->stat_timer.data = card; - sc_adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME; - add_timer(&sc_adapter[card]->stat_timer); - continue; - } - - /* - * Start proc response - */ - if (IS_CM_MESSAGE(rcvmsg, 2, 0, StartProc)) { - pr_debug("%s: StartProc Response Status %d\n", - sc_adapter[card]->devicename, - rcvmsg.rsp_status); - continue; - } - - /* - * Handle a GetMyNumber Rsp - */ - if (IS_CE_MESSAGE(rcvmsg, Call, 0, GetMyNumber)) { - strlcpy(sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].dn, - rcvmsg.msg_data.byte_array, - sizeof(rcvmsg.msg_data.byte_array)); - continue; - } - - /* - * PhyStatus response - */ - if (IS_CE_MESSAGE(rcvmsg, Phy, 2, Status)) { - unsigned int b1stat, b2stat; - - /* - * Covert the message data to the adapter->phystat code - */ - b1stat = (unsigned int) rcvmsg.msg_data.byte_array[0]; - b2stat = (unsigned int) rcvmsg.msg_data.byte_array[1]; - - sc_adapter[card]->nphystat = (b2stat >> 8) | b1stat; /* endian?? */ - pr_debug("%s: PhyStat is 0x%2x\n", - sc_adapter[card]->devicename, - sc_adapter[card]->nphystat); - continue; - } - - - /* - * Handle a GetFramFormat - */ - if (IS_CE_MESSAGE(rcvmsg, Call, 0, GetFrameFormat)) { - if (rcvmsg.msg_data.byte_array[0] != HDLC_PROTO) { - unsigned int proto = HDLC_PROTO; - /* - * Set board format to HDLC if it wasn't already - */ - pr_debug("%s: current frame format: 0x%x, will change to HDLC\n", - sc_adapter[card]->devicename, - rcvmsg.msg_data.byte_array[0]); - sendmessage(card, CEPID, ceReqTypeCall, - ceReqClass0, - ceReqCallSetFrameFormat, - (unsigned char)channel + 1, - 1, &proto); - } - continue; - } - - /* - * Hmm... - */ - pr_debug("%s: Received unhandled message (%d,%d,%d) link %d\n", - sc_adapter[card]->devicename, - rcvmsg.type, rcvmsg.class, rcvmsg.code, - rcvmsg.phy_link_no); - - } /* while */ - - pr_debug("%s: Exiting Interrupt Handler\n", - sc_adapter[card]->devicename); - return IRQ_HANDLED; -} diff --git a/drivers/isdn/sc/ioctl.c b/drivers/isdn/sc/ioctl.c deleted file mode 100644 index e63983aa1d27..000000000000 --- a/drivers/isdn/sc/ioctl.c +++ /dev/null @@ -1,582 +0,0 @@ -/* - * Copyright (C) 1996 SpellCaster Telecommunications Inc. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - */ - -#include "includes.h" -#include "hardware.h" -#include "message.h" -#include "card.h" -#include "scioc.h" - -static int GetStatus(int card, boardInfo *); - -/* - * Process private IOCTL messages (typically from scctrl) - */ -int sc_ioctl(int card, scs_ioctl *data) -{ - int status; - RspMessage *rcvmsg; - char *spid; - char *dn; - char switchtype; - char speed; - - rcvmsg = kmalloc(sizeof(RspMessage), GFP_KERNEL); - if (!rcvmsg) - return -ENOMEM; - - switch (data->command) { - case SCIOCRESET: /* Perform a hard reset of the adapter */ - { - pr_debug("%s: SCIOCRESET: ioctl received\n", - sc_adapter[card]->devicename); - sc_adapter[card]->StartOnReset = 0; - kfree(rcvmsg); - return reset(card); - } - - case SCIOCLOAD: - { - char *srec; - - srec = kmalloc(SCIOC_SRECSIZE, GFP_KERNEL); - if (!srec) { - kfree(rcvmsg); - return -ENOMEM; - } - pr_debug("%s: SCIOLOAD: ioctl received\n", - sc_adapter[card]->devicename); - if (sc_adapter[card]->EngineUp) { - pr_debug("%s: SCIOCLOAD: command failed, LoadProc while engine running.\n", - sc_adapter[card]->devicename); - kfree(rcvmsg); - kfree(srec); - return -1; - } - - /* - * Get the SRec from user space - */ - if (copy_from_user(srec, data->dataptr, SCIOC_SRECSIZE)) { - kfree(rcvmsg); - kfree(srec); - return -EFAULT; - } - - status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc, - 0, SCIOC_SRECSIZE, srec, rcvmsg, SAR_TIMEOUT); - kfree(rcvmsg); - kfree(srec); - - if (status) { - pr_debug("%s: SCIOCLOAD: command failed, status = %d\n", - sc_adapter[card]->devicename, status); - return -1; - } - else { - pr_debug("%s: SCIOCLOAD: command successful\n", - sc_adapter[card]->devicename); - return 0; - } - } - - case SCIOCSTART: - { - kfree(rcvmsg); - pr_debug("%s: SCIOSTART: ioctl received\n", - sc_adapter[card]->devicename); - if (sc_adapter[card]->EngineUp) { - pr_debug("%s: SCIOCSTART: command failed, engine already running.\n", - sc_adapter[card]->devicename); - return -1; - } - - sc_adapter[card]->StartOnReset = 1; - startproc(card); - return 0; - } - - case SCIOCSETSWITCH: - { - pr_debug("%s: SCIOSETSWITCH: ioctl received\n", - sc_adapter[card]->devicename); - - /* - * Get the switch type from user space - */ - if (copy_from_user(&switchtype, data->dataptr, sizeof(char))) { - kfree(rcvmsg); - return -EFAULT; - } - - pr_debug("%s: SCIOCSETSWITCH: setting switch type to %d\n", - sc_adapter[card]->devicename, - switchtype); - status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallSetSwitchType, - 0, sizeof(char), &switchtype, rcvmsg, SAR_TIMEOUT); - if (!status && !(rcvmsg->rsp_status)) { - pr_debug("%s: SCIOCSETSWITCH: command successful\n", - sc_adapter[card]->devicename); - kfree(rcvmsg); - return 0; - } - else { - pr_debug("%s: SCIOCSETSWITCH: command failed (status = %d)\n", - sc_adapter[card]->devicename, status); - kfree(rcvmsg); - return status; - } - } - - case SCIOCGETSWITCH: - { - pr_debug("%s: SCIOGETSWITCH: ioctl received\n", - sc_adapter[card]->devicename); - - /* - * Get the switch type from the board - */ - status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, - ceReqCallGetSwitchType, 0, 0, NULL, rcvmsg, SAR_TIMEOUT); - if (!status && !(rcvmsg->rsp_status)) { - pr_debug("%s: SCIOCGETSWITCH: command successful\n", - sc_adapter[card]->devicename); - } - else { - pr_debug("%s: SCIOCGETSWITCH: command failed (status = %d)\n", - sc_adapter[card]->devicename, status); - kfree(rcvmsg); - return status; - } - - switchtype = rcvmsg->msg_data.byte_array[0]; - - /* - * Package the switch type and send to user space - */ - if (copy_to_user(data->dataptr, &switchtype, - sizeof(char))) { - kfree(rcvmsg); - return -EFAULT; - } - - kfree(rcvmsg); - return 0; - } - - case SCIOCGETSPID: - { - pr_debug("%s: SCIOGETSPID: ioctl received\n", - sc_adapter[card]->devicename); - - spid = kzalloc(SCIOC_SPIDSIZE, GFP_KERNEL); - if (!spid) { - kfree(rcvmsg); - return -ENOMEM; - } - /* - * Get the spid from the board - */ - status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetSPID, - data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT); - if (!status) { - pr_debug("%s: SCIOCGETSPID: command successful\n", - sc_adapter[card]->devicename); - } else { - pr_debug("%s: SCIOCGETSPID: command failed (status = %d)\n", - sc_adapter[card]->devicename, status); - kfree(spid); - kfree(rcvmsg); - return status; - } - strlcpy(spid, rcvmsg->msg_data.byte_array, SCIOC_SPIDSIZE); - - /* - * Package the switch type and send to user space - */ - if (copy_to_user(data->dataptr, spid, SCIOC_SPIDSIZE)) { - kfree(spid); - kfree(rcvmsg); - return -EFAULT; - } - - kfree(spid); - kfree(rcvmsg); - return 0; - } - - case SCIOCSETSPID: - { - pr_debug("%s: DCBIOSETSPID: ioctl received\n", - sc_adapter[card]->devicename); - - /* - * Get the spid from user space - */ - spid = memdup_user(data->dataptr, SCIOC_SPIDSIZE); - if (IS_ERR(spid)) { - kfree(rcvmsg); - return PTR_ERR(spid); - } - - pr_debug("%s: SCIOCSETSPID: setting channel %d spid to %s\n", - sc_adapter[card]->devicename, data->channel, spid); - status = send_and_receive(card, CEPID, ceReqTypeCall, - ceReqClass0, ceReqCallSetSPID, data->channel, - strlen(spid), spid, rcvmsg, SAR_TIMEOUT); - if (!status && !(rcvmsg->rsp_status)) { - pr_debug("%s: SCIOCSETSPID: command successful\n", - sc_adapter[card]->devicename); - kfree(rcvmsg); - kfree(spid); - return 0; - } - else { - pr_debug("%s: SCIOCSETSPID: command failed (status = %d)\n", - sc_adapter[card]->devicename, status); - kfree(rcvmsg); - kfree(spid); - return status; - } - } - - case SCIOCGETDN: - { - pr_debug("%s: SCIOGETDN: ioctl received\n", - sc_adapter[card]->devicename); - - /* - * Get the dn from the board - */ - status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber, - data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT); - if (!status) { - pr_debug("%s: SCIOCGETDN: command successful\n", - sc_adapter[card]->devicename); - } - else { - pr_debug("%s: SCIOCGETDN: command failed (status = %d)\n", - sc_adapter[card]->devicename, status); - kfree(rcvmsg); - return status; - } - - dn = kzalloc(SCIOC_DNSIZE, GFP_KERNEL); - if (!dn) { - kfree(rcvmsg); - return -ENOMEM; - } - strlcpy(dn, rcvmsg->msg_data.byte_array, SCIOC_DNSIZE); - kfree(rcvmsg); - - /* - * Package the dn and send to user space - */ - if (copy_to_user(data->dataptr, dn, SCIOC_DNSIZE)) { - kfree(dn); - return -EFAULT; - } - kfree(dn); - return 0; - } - - case SCIOCSETDN: - { - pr_debug("%s: SCIOSETDN: ioctl received\n", - sc_adapter[card]->devicename); - - /* - * Get the spid from user space - */ - dn = memdup_user(data->dataptr, SCIOC_DNSIZE); - if (IS_ERR(dn)) { - kfree(rcvmsg); - return PTR_ERR(dn); - } - - pr_debug("%s: SCIOCSETDN: setting channel %d dn to %s\n", - sc_adapter[card]->devicename, data->channel, dn); - status = send_and_receive(card, CEPID, ceReqTypeCall, - ceReqClass0, ceReqCallSetMyNumber, data->channel, - strlen(dn), dn, rcvmsg, SAR_TIMEOUT); - if (!status && !(rcvmsg->rsp_status)) { - pr_debug("%s: SCIOCSETDN: command successful\n", - sc_adapter[card]->devicename); - kfree(rcvmsg); - kfree(dn); - return 0; - } - else { - pr_debug("%s: SCIOCSETDN: command failed (status = %d)\n", - sc_adapter[card]->devicename, status); - kfree(rcvmsg); - kfree(dn); - return status; - } - } - - case SCIOCTRACE: - - pr_debug("%s: SCIOTRACE: ioctl received\n", - sc_adapter[card]->devicename); -/* sc_adapter[card]->trace = !sc_adapter[card]->trace; - pr_debug("%s: SCIOCTRACE: tracing turned %s\n", - sc_adapter[card]->devicename, - sc_adapter[card]->trace ? "ON" : "OFF"); */ - break; - - case SCIOCSTAT: - { - boardInfo *bi; - - pr_debug("%s: SCIOSTAT: ioctl received\n", - sc_adapter[card]->devicename); - - bi = kzalloc(sizeof(boardInfo), GFP_KERNEL); - if (!bi) { - kfree(rcvmsg); - return -ENOMEM; - } - - kfree(rcvmsg); - GetStatus(card, bi); - - if (copy_to_user(data->dataptr, bi, sizeof(boardInfo))) { - kfree(bi); - return -EFAULT; - } - - kfree(bi); - return 0; - } - - case SCIOCGETSPEED: - { - pr_debug("%s: SCIOGETSPEED: ioctl received\n", - sc_adapter[card]->devicename); - - /* - * Get the speed from the board - */ - status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, - ceReqCallGetCallType, data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT); - if (!status && !(rcvmsg->rsp_status)) { - pr_debug("%s: SCIOCGETSPEED: command successful\n", - sc_adapter[card]->devicename); - } - else { - pr_debug("%s: SCIOCGETSPEED: command failed (status = %d)\n", - sc_adapter[card]->devicename, status); - kfree(rcvmsg); - return status; - } - - speed = rcvmsg->msg_data.byte_array[0]; - - kfree(rcvmsg); - - /* - * Package the switch type and send to user space - */ - - if (copy_to_user(data->dataptr, &speed, sizeof(char))) - return -EFAULT; - - return 0; - } - - case SCIOCSETSPEED: - pr_debug("%s: SCIOCSETSPEED: ioctl received\n", - sc_adapter[card]->devicename); - break; - - case SCIOCLOOPTST: - pr_debug("%s: SCIOCLOOPTST: ioctl received\n", - sc_adapter[card]->devicename); - break; - - default: - kfree(rcvmsg); - return -1; - } - - kfree(rcvmsg); - return 0; -} - -static int GetStatus(int card, boardInfo *bi) -{ - RspMessage rcvmsg; - int i, status; - - /* - * Fill in some of the basic info about the board - */ - bi->modelid = sc_adapter[card]->model; - strcpy(bi->serial_no, sc_adapter[card]->hwconfig.serial_no); - strcpy(bi->part_no, sc_adapter[card]->hwconfig.part_no); - bi->iobase = sc_adapter[card]->iobase; - bi->rambase = sc_adapter[card]->rambase; - bi->irq = sc_adapter[card]->interrupt; - bi->ramsize = sc_adapter[card]->hwconfig.ram_size; - bi->interface = sc_adapter[card]->hwconfig.st_u_sense; - strcpy(bi->load_ver, sc_adapter[card]->load_ver); - strcpy(bi->proc_ver, sc_adapter[card]->proc_ver); - - /* - * Get the current PhyStats and LnkStats - */ - status = send_and_receive(card, CEPID, ceReqTypePhy, ceReqClass2, - ceReqPhyStatus, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); - if (!status) { - if (sc_adapter[card]->model < PRI_BOARD) { - bi->l1_status = rcvmsg.msg_data.byte_array[2]; - for (i = 0; i < BRI_CHANNELS; i++) - bi->status.bristats[i].phy_stat = - rcvmsg.msg_data.byte_array[i]; - } - else { - bi->l1_status = rcvmsg.msg_data.byte_array[0]; - bi->l2_status = rcvmsg.msg_data.byte_array[1]; - for (i = 0; i < PRI_CHANNELS; i++) - bi->status.pristats[i].phy_stat = - rcvmsg.msg_data.byte_array[i + 2]; - } - } - - /* - * Get the call types for each channel - */ - for (i = 0; i < sc_adapter[card]->nChannels; i++) { - status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, - ceReqCallGetCallType, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); - if (!status) { - if (sc_adapter[card]->model == PRI_BOARD) { - bi->status.pristats[i].call_type = - rcvmsg.msg_data.byte_array[0]; - } - else { - bi->status.bristats[i].call_type = - rcvmsg.msg_data.byte_array[0]; - } - } - } - - /* - * If PRI, get the call states and service states for each channel - */ - if (sc_adapter[card]->model == PRI_BOARD) { - /* - * Get the call states - */ - status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2, - ceReqPhyChCallState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); - if (!status) { - for (i = 0; i < PRI_CHANNELS; i++) - bi->status.pristats[i].call_state = - rcvmsg.msg_data.byte_array[i]; - } - - /* - * Get the service states - */ - status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2, - ceReqPhyChServState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); - if (!status) { - for (i = 0; i < PRI_CHANNELS; i++) - bi->status.pristats[i].serv_state = - rcvmsg.msg_data.byte_array[i]; - } - - /* - * Get the link stats for the channels - */ - for (i = 1; i <= PRI_CHANNELS; i++) { - status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0, - ceReqLnkGetStats, i, 0, NULL, &rcvmsg, SAR_TIMEOUT); - if (!status) { - bi->status.pristats[i - 1].link_stats.tx_good = - (unsigned long)rcvmsg.msg_data.byte_array[0]; - bi->status.pristats[i - 1].link_stats.tx_bad = - (unsigned long)rcvmsg.msg_data.byte_array[4]; - bi->status.pristats[i - 1].link_stats.rx_good = - (unsigned long)rcvmsg.msg_data.byte_array[8]; - bi->status.pristats[i - 1].link_stats.rx_bad = - (unsigned long)rcvmsg.msg_data.byte_array[12]; - } - } - - /* - * Link stats for the D channel - */ - status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0, - ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); - if (!status) { - bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0]; - bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4]; - bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8]; - bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12]; - } - - return 0; - } - - /* - * If BRI or POTS, Get SPID, DN and call types for each channel - */ - - /* - * Get the link stats for the channels - */ - status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0, - ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT); - if (!status) { - bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0]; - bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4]; - bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8]; - bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12]; - bi->status.bristats[0].link_stats.tx_good = - (unsigned long)rcvmsg.msg_data.byte_array[16]; - bi->status.bristats[0].link_stats.tx_bad = - (unsigned long)rcvmsg.msg_data.byte_array[20]; - bi->status.bristats[0].link_stats.rx_good = - (unsigned long)rcvmsg.msg_data.byte_array[24]; - bi->status.bristats[0].link_stats.rx_bad = - (unsigned long)rcvmsg.msg_data.byte_array[28]; - bi->status.bristats[1].link_stats.tx_good = - (unsigned long)rcvmsg.msg_data.byte_array[32]; - bi->status.bristats[1].link_stats.tx_bad = - (unsigned long)rcvmsg.msg_data.byte_array[36]; - bi->status.bristats[1].link_stats.rx_good = - (unsigned long)rcvmsg.msg_data.byte_array[40]; - bi->status.bristats[1].link_stats.rx_bad = - (unsigned long)rcvmsg.msg_data.byte_array[44]; - } - - /* - * Get the SPIDs - */ - for (i = 0; i < BRI_CHANNELS; i++) { - status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, - ceReqCallGetSPID, i + 1, 0, NULL, &rcvmsg, SAR_TIMEOUT); - if (!status) - strcpy(bi->status.bristats[i].spid, rcvmsg.msg_data.byte_array); - } - - /* - * Get the DNs - */ - for (i = 0; i < BRI_CHANNELS; i++) { - status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, - ceReqCallGetMyNumber, i + 1, 0, NULL, &rcvmsg, SAR_TIMEOUT); - if (!status) - strcpy(bi->status.bristats[i].dn, rcvmsg.msg_data.byte_array); - } - - return 0; -} diff --git a/drivers/isdn/sc/message.c b/drivers/isdn/sc/message.c deleted file mode 100644 index 9679a1902b32..000000000000 --- a/drivers/isdn/sc/message.c +++ /dev/null @@ -1,230 +0,0 @@ -/* $Id: message.c,v 1.5.8.2 2001/09/23 22:24:59 kai Exp $ - * - * functions for sending and receiving control messages - * - * Copyright (C) 1996 SpellCaster Telecommunications Inc. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * For more information, please contact gpl-info@spellcast.com or write: - * - * SpellCaster Telecommunications Inc. - * 5621 Finch Avenue East, Unit #3 - * Scarborough, Ontario Canada - * M1B 2T9 - * +1 (416) 297-8565 - * +1 (416) 297-6433 Facsimile - */ -#include -#include "includes.h" -#include "hardware.h" -#include "message.h" -#include "card.h" - -/* - * receive a message from the board - */ -int receivemessage(int card, RspMessage *rspmsg) -{ - DualPortMemory *dpm; - unsigned long flags; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -EINVAL; - } - - pr_debug("%s: Entered receivemessage\n", - sc_adapter[card]->devicename); - - /* - * See if there are messages waiting - */ - if (inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA) { - /* - * Map in the DPM to the base page and copy the message - */ - spin_lock_irqsave(&sc_adapter[card]->lock, flags); - outb((sc_adapter[card]->shmem_magic >> 14) | 0x80, - sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]); - dpm = (DualPortMemory *) sc_adapter[card]->rambase; - memcpy_fromio(rspmsg, &(dpm->rsp_queue[dpm->rsp_tail]), - MSG_LEN); - dpm->rsp_tail = (dpm->rsp_tail + 1) % MAX_MESSAGES; - inb(sc_adapter[card]->ioport[FIFO_READ]); - spin_unlock_irqrestore(&sc_adapter[card]->lock, flags); - /* - * Tell the board that the message is received - */ - pr_debug("%s: Received Message seq:%d pid:%d time:%d cmd:%d " - "cnt:%d (type,class,code):(%d,%d,%d) " - "link:%d stat:0x%x\n", - sc_adapter[card]->devicename, - rspmsg->sequence_no, - rspmsg->process_id, - rspmsg->time_stamp, - rspmsg->cmd_sequence_no, - rspmsg->msg_byte_cnt, - rspmsg->type, - rspmsg->class, - rspmsg->code, - rspmsg->phy_link_no, - rspmsg->rsp_status); - - return 0; - } - return -ENOMSG; -} - -/* - * send a message to the board - */ -int sendmessage(int card, - unsigned int procid, - unsigned int type, - unsigned int class, - unsigned int code, - unsigned int link, - unsigned int data_len, - unsigned int *data) -{ - DualPortMemory *dpm; - ReqMessage sndmsg; - unsigned long flags; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -EINVAL; - } - - /* - * Make sure we only send CEPID messages when the engine is up - * and CMPID messages when it is down - */ - if (sc_adapter[card]->EngineUp && procid == CMPID) { - pr_debug("%s: Attempt to send CM message with engine up\n", - sc_adapter[card]->devicename); - return -ESRCH; - } - - if (!sc_adapter[card]->EngineUp && procid == CEPID) { - pr_debug("%s: Attempt to send CE message with engine down\n", - sc_adapter[card]->devicename); - return -ESRCH; - } - - memset(&sndmsg, 0, MSG_LEN); - sndmsg.msg_byte_cnt = 4; - sndmsg.type = type; - sndmsg.class = class; - sndmsg.code = code; - sndmsg.phy_link_no = link; - - if (data_len > 0) { - if (data_len > MSG_DATA_LEN) - data_len = MSG_DATA_LEN; - memcpy(&(sndmsg.msg_data), data, data_len); - sndmsg.msg_byte_cnt = data_len + 8; - } - - sndmsg.process_id = procid; - sndmsg.sequence_no = sc_adapter[card]->seq_no++ % 256; - - /* - * wait for an empty slot in the queue - */ - while (!(inb(sc_adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL)) - udelay(1); - - /* - * Disable interrupts and map in shared memory - */ - spin_lock_irqsave(&sc_adapter[card]->lock, flags); - outb((sc_adapter[card]->shmem_magic >> 14) | 0x80, - sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]); - dpm = (DualPortMemory *) sc_adapter[card]->rambase; /* Fix me */ - memcpy_toio(&(dpm->req_queue[dpm->req_head]), &sndmsg, MSG_LEN); - dpm->req_head = (dpm->req_head + 1) % MAX_MESSAGES; - outb(sndmsg.sequence_no, sc_adapter[card]->ioport[FIFO_WRITE]); - spin_unlock_irqrestore(&sc_adapter[card]->lock, flags); - - pr_debug("%s: Sent Message seq:%d pid:%d time:%d " - "cnt:%d (type,class,code):(%d,%d,%d) " - "link:%d\n ", - sc_adapter[card]->devicename, - sndmsg.sequence_no, - sndmsg.process_id, - sndmsg.time_stamp, - sndmsg.msg_byte_cnt, - sndmsg.type, - sndmsg.class, - sndmsg.code, - sndmsg.phy_link_no); - - return 0; -} - -int send_and_receive(int card, - unsigned int procid, - unsigned char type, - unsigned char class, - unsigned char code, - unsigned char link, - unsigned char data_len, - unsigned char *data, - RspMessage *mesgdata, - int timeout) -{ - int retval; - int tries; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return -EINVAL; - } - - sc_adapter[card]->want_async_messages = 1; - retval = sendmessage(card, procid, type, class, code, link, - data_len, (unsigned int *) data); - - if (retval) { - pr_debug("%s: SendMessage failed in SAR\n", - sc_adapter[card]->devicename); - sc_adapter[card]->want_async_messages = 0; - return -EIO; - } - - tries = 0; - /* wait for the response */ - while (tries < timeout) { - schedule_timeout_interruptible(1); - - pr_debug("SAR waiting..\n"); - - /* - * See if we got our message back - */ - if ((sc_adapter[card]->async_msg.type == type) && - (sc_adapter[card]->async_msg.class == class) && - (sc_adapter[card]->async_msg.code == code) && - (sc_adapter[card]->async_msg.phy_link_no == link)) { - - /* - * Got it! - */ - pr_debug("%s: Got ASYNC message\n", - sc_adapter[card]->devicename); - memcpy(mesgdata, &(sc_adapter[card]->async_msg), - sizeof(RspMessage)); - sc_adapter[card]->want_async_messages = 0; - return 0; - } - - tries++; - } - - pr_debug("%s: SAR message timeout\n", sc_adapter[card]->devicename); - sc_adapter[card]->want_async_messages = 0; - return -ETIME; -} diff --git a/drivers/isdn/sc/message.h b/drivers/isdn/sc/message.h deleted file mode 100644 index 5e6f4a5c15f8..000000000000 --- a/drivers/isdn/sc/message.h +++ /dev/null @@ -1,245 +0,0 @@ -/* $Id: message.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $ - * - * Copyright (C) 1996 SpellCaster Telecommunications Inc. - * - * structures, macros and defines useful for sending - * messages to the adapter - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * For more information, please contact gpl-info@spellcast.com or write: - * - * SpellCaster Telecommunications Inc. - * 5621 Finch Avenue East, Unit #3 - * Scarborough, Ontario Canada - * M1B 2T9 - * +1 (416) 297-8565 - * +1 (416) 297-6433 Facsimile - */ - -/* - * Board message macros, defines and structures - */ - -#ifndef MESSAGE_H -#define MESSAGE_H - -#define MAX_MESSAGES 32 /* Maximum messages that can be - queued */ -#define MSG_DATA_LEN 48 /* Maximum size of message payload */ -#define MSG_LEN 64 /* Size of a message */ -#define CMPID 0 /* Loader message process ID */ -#define CEPID 64 /* Firmware message process ID */ - -/* - * Macro to determine if a message is a loader message - */ -#define IS_CM_MESSAGE(mesg, tx, cx, dx) \ - ((mesg.type == cmRspType##tx) \ - && (mesg.class == cmRspClass##cx) \ - && (mesg.code == cmRsp##dx)) - -/* - * Macro to determine if a message is a firmware message - */ -#define IS_CE_MESSAGE(mesg, tx, cx, dx) \ - ((mesg.type == ceRspType##tx) \ - && (mesg.class == ceRspClass##cx) \ - && (mesg.code == ceRsp##tx##dx)) - -/* - * Loader Request and Response Messages - */ - -/* message types */ -#define cmReqType1 1 -#define cmReqType2 2 -#define cmRspType0 0 -#define cmRspType1 1 -#define cmRspType2 2 -#define cmRspType5 5 - -/* message classes */ -#define cmReqClass0 0 -#define cmRspClass0 0 - -/* message codes */ -#define cmReqHWConfig 1 /* 1,0,1 */ -#define cmReqMsgLpbk 2 /* 1,0,2 */ -#define cmReqVersion 3 /* 1,0,3 */ -#define cmReqLoadProc 1 /* 2,0,1 */ -#define cmReqStartProc 2 /* 2,0,2 */ -#define cmReqReadMem 6 /* 2,0,6 */ -#define cmRspHWConfig cmReqHWConfig -#define cmRspMsgLpbk cmReqMsgLpbk -#define cmRspVersion cmReqVersion -#define cmRspLoadProc cmReqLoadProc -#define cmRspStartProc cmReqStartProc -#define cmRspReadMem cmReqReadMem -#define cmRspMiscEngineUp 1 /* 5,0,1 */ -#define cmRspInvalid 0 /* 0,0,0 */ - - -/* - * Firmware Request and Response Messages - */ - -/* message types */ -#define ceReqTypePhy 1 -#define ceReqTypeLnk 2 -#define ceReqTypeCall 3 -#define ceReqTypeStat 1 -#define ceRspTypeErr 0 -#define ceRspTypePhy ceReqTypePhy -#define ceRspTypeLnk ceReqTypeLnk -#define ceRspTypeCall ceReqTypeCall -#define ceRspTypeStat ceReqTypeStat - -/* message classes */ -#define ceReqClass0 0 -#define ceReqClass1 1 -#define ceReqClass2 2 -#define ceReqClass3 3 -#define ceRspClass0 ceReqClass0 -#define ceRspClass1 ceReqClass1 -#define ceRspClass2 ceReqClass2 -#define ceRspClass3 ceReqClass3 - -/* message codes (B) = BRI only, (P) = PRI only, (V) = POTS only */ -#define ceReqPhyProcInfo 1 /* 1,0,1 */ -#define ceReqPhyConnect 1 /* 1,1,1 */ -#define ceReqPhyDisconnect 2 /* 1,1,2 */ -#define ceReqPhySetParams 3 /* 1,1,3 (P) */ -#define ceReqPhyGetParams 4 /* 1,1,4 (P) */ -#define ceReqPhyStatus 1 /* 1,2,1 */ -#define ceReqPhyAcfaStatus 2 /* 1,2,2 (P) */ -#define ceReqPhyChCallState 3 /* 1,2,3 (P) */ -#define ceReqPhyChServState 4 /* 1,2,4 (P) */ -#define ceReqPhyRLoopBack 1 /* 1,3,1 */ -#define ceRspPhyProcInfo ceReqPhyProcInfo -#define ceRspPhyConnect ceReqPhyConnect -#define ceRspPhyDisconnect ceReqPhyDisconnect -#define ceRspPhySetParams ceReqPhySetParams -#define ceRspPhyGetParams ceReqPhyGetParams -#define ceRspPhyStatus ceReqPhyStatus -#define ceRspPhyAcfaStatus ceReqPhyAcfaStatus -#define ceRspPhyChCallState ceReqPhyChCallState -#define ceRspPhyChServState ceReqPhyChServState -#define ceRspPhyRLoopBack ceReqphyRLoopBack -#define ceReqLnkSetParam 1 /* 2,0,1 */ -#define ceReqLnkGetParam 2 /* 2,0,2 */ -#define ceReqLnkGetStats 3 /* 2,0,3 */ -#define ceReqLnkWrite 1 /* 2,1,1 */ -#define ceReqLnkRead 2 /* 2,1,2 */ -#define ceReqLnkFlush 3 /* 2,1,3 */ -#define ceReqLnkWrBufTrc 4 /* 2,1,4 */ -#define ceReqLnkRdBufTrc 5 /* 2,1,5 */ -#define ceRspLnkSetParam ceReqLnkSetParam -#define ceRspLnkGetParam ceReqLnkGetParam -#define ceRspLnkGetStats ceReqLnkGetStats -#define ceRspLnkWrite ceReqLnkWrite -#define ceRspLnkRead ceReqLnkRead -#define ceRspLnkFlush ceReqLnkFlush -#define ceRspLnkWrBufTrc ceReqLnkWrBufTrc -#define ceRspLnkRdBufTrc ceReqLnkRdBufTrc -#define ceReqCallSetSwitchType 1 /* 3,0,1 */ -#define ceReqCallGetSwitchType 2 /* 3,0,2 */ -#define ceReqCallSetFrameFormat 3 /* 3,0,3 */ -#define ceReqCallGetFrameFormat 4 /* 3,0,4 */ -#define ceReqCallSetCallType 5 /* 3,0,5 */ -#define ceReqCallGetCallType 6 /* 3,0,6 */ -#define ceReqCallSetSPID 7 /* 3,0,7 (!P) */ -#define ceReqCallGetSPID 8 /* 3,0,8 (!P) */ -#define ceReqCallSetMyNumber 9 /* 3,0,9 (!P) */ -#define ceReqCallGetMyNumber 10 /* 3,0,10 (!P) */ -#define ceRspCallSetSwitchType ceReqCallSetSwitchType -#define ceRspCallGetSwitchType ceReqCallSetSwitchType -#define ceRspCallSetFrameFormat ceReqCallSetFrameFormat -#define ceRspCallGetFrameFormat ceReqCallGetFrameFormat -#define ceRspCallSetCallType ceReqCallSetCallType -#define ceRspCallGetCallType ceReqCallGetCallType -#define ceRspCallSetSPID ceReqCallSetSPID -#define ceRspCallGetSPID ceReqCallGetSPID -#define ceRspCallSetMyNumber ceReqCallSetMyNumber -#define ceRspCallGetMyNumber ceReqCallGetMyNumber -#define ceRspStatAcfaStatus 2 -#define ceRspStat -#define ceRspErrError 0 /* 0,0,0 */ - -/* - * Call Types - */ -#define CALLTYPE_64K 0 -#define CALLTYPE_56K 1 -#define CALLTYPE_SPEECH 2 -#define CALLTYPE_31KHZ 3 - -/* - * Link Level data contains a pointer to and the length of - * a buffer in shared RAM. Used by LnkRead and LnkWrite message - * types. Part of RspMsgStruct and ReqMsgStruct. - */ -typedef struct { - unsigned long buff_offset; - unsigned short msg_len; -} LLData; - - -/* - * Message payload template for an HWConfig message - */ -typedef struct { - char st_u_sense; - char powr_sense; - char sply_sense; - unsigned char asic_id; - long ram_size; - char serial_no[13]; - char part_no[13]; - char rev_no[2]; -} HWConfig_pl; - -/* - * A Message - */ -struct message { - unsigned char sequence_no; - unsigned char process_id; - unsigned char time_stamp; - unsigned char cmd_sequence_no; /* Rsp messages only */ - unsigned char reserved1[3]; - unsigned char msg_byte_cnt; - unsigned char type; - unsigned char class; - unsigned char code; - unsigned char phy_link_no; - unsigned char rsp_status; /* Rsp messages only */ - unsigned char reseved2[3]; - union { - unsigned char byte_array[MSG_DATA_LEN]; - LLData response; - HWConfig_pl HWCresponse; - } msg_data; -}; - -typedef struct message ReqMessage; /* Request message */ -typedef struct message RspMessage; /* Response message */ - -/* - * The first 5010 bytes of shared memory contain the message queues, - * indexes and other data. This structure is its template - */ -typedef struct { - volatile ReqMessage req_queue[MAX_MESSAGES]; - volatile RspMessage rsp_queue[MAX_MESSAGES]; - volatile unsigned char req_head; - volatile unsigned char req_tail; - volatile unsigned char rsp_head; - volatile unsigned char rsp_tail; - volatile unsigned long signature; - volatile unsigned long trace_enable; - volatile unsigned char reserved[4]; -} DualPortMemory; - -#endif diff --git a/drivers/isdn/sc/packet.c b/drivers/isdn/sc/packet.c deleted file mode 100644 index 2446957085e0..000000000000 --- a/drivers/isdn/sc/packet.c +++ /dev/null @@ -1,204 +0,0 @@ -/* $Id: packet.c,v 1.5.8.1 2001/09/23 22:24:59 kai Exp $ - * - * Copyright (C) 1996 SpellCaster Telecommunications Inc. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * For more information, please contact gpl-info@spellcast.com or write: - * - * SpellCaster Telecommunications Inc. - * 5621 Finch Avenue East, Unit #3 - * Scarborough, Ontario Canada - * M1B 2T9 - * +1 (416) 297-8565 - * +1 (416) 297-6433 Facsimile - */ - -#include "includes.h" -#include "hardware.h" -#include "message.h" -#include "card.h" - -int sndpkt(int devId, int channel, int ack, struct sk_buff *data) -{ - LLData ReqLnkWrite; - int status; - int card; - unsigned long len; - - card = get_card_from_id(devId); - - if (!IS_VALID_CARD(card)) { - pr_debug("invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - pr_debug("%s: sndpkt: frst = 0x%lx nxt = %d f = %d n = %d\n", - sc_adapter[card]->devicename, - sc_adapter[card]->channel[channel].first_sendbuf, - sc_adapter[card]->channel[channel].next_sendbuf, - sc_adapter[card]->channel[channel].free_sendbufs, - sc_adapter[card]->channel[channel].num_sendbufs); - - if (!sc_adapter[card]->channel[channel].free_sendbufs) { - pr_debug("%s: out of TX buffers\n", - sc_adapter[card]->devicename); - return -EINVAL; - } - - if (data->len > BUFFER_SIZE) { - pr_debug("%s: data overflows buffer size (data > buffer)\n", - sc_adapter[card]->devicename); - return -EINVAL; - } - - ReqLnkWrite.buff_offset = sc_adapter[card]->channel[channel].next_sendbuf * - BUFFER_SIZE + sc_adapter[card]->channel[channel].first_sendbuf; - ReqLnkWrite.msg_len = data->len; /* sk_buff size */ - pr_debug("%s: writing %d bytes to buffer offset 0x%lx\n", - sc_adapter[card]->devicename, - ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset); - memcpy_toshmem(card, (char *)ReqLnkWrite.buff_offset, data->data, ReqLnkWrite.msg_len); - - /* - * sendmessage - */ - pr_debug("%s: sndpkt size=%d, buf_offset=0x%lx buf_indx=%d\n", - sc_adapter[card]->devicename, - ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset, - sc_adapter[card]->channel[channel].next_sendbuf); - - status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite, - channel + 1, sizeof(LLData), (unsigned int *)&ReqLnkWrite); - len = data->len; - if (status) { - pr_debug("%s: failed to send packet, status = %d\n", - sc_adapter[card]->devicename, status); - return -1; - } - else { - sc_adapter[card]->channel[channel].free_sendbufs--; - sc_adapter[card]->channel[channel].next_sendbuf = - ++sc_adapter[card]->channel[channel].next_sendbuf == - sc_adapter[card]->channel[channel].num_sendbufs ? 0 : - sc_adapter[card]->channel[channel].next_sendbuf; - pr_debug("%s: packet sent successfully\n", sc_adapter[card]->devicename); - dev_kfree_skb(data); - indicate_status(card, ISDN_STAT_BSENT, channel, (char *)&len); - } - return len; -} - -void rcvpkt(int card, RspMessage *rcvmsg) -{ - LLData newll; - struct sk_buff *skb; - - if (!IS_VALID_CARD(card)) { - pr_debug("invalid param: %d is not a valid card id\n", card); - return; - } - - switch (rcvmsg->rsp_status) { - case 0x01: - case 0x02: - case 0x70: - pr_debug("%s: error status code: 0x%x\n", - sc_adapter[card]->devicename, rcvmsg->rsp_status); - return; - case 0x00: - if (!(skb = dev_alloc_skb(rcvmsg->msg_data.response.msg_len))) { - printk(KERN_WARNING "%s: rcvpkt out of memory, dropping packet\n", - sc_adapter[card]->devicename); - return; - } - skb_put(skb, rcvmsg->msg_data.response.msg_len); - pr_debug("%s: getting data from offset: 0x%lx\n", - sc_adapter[card]->devicename, - rcvmsg->msg_data.response.buff_offset); - memcpy_fromshmem(card, - skb_put(skb, rcvmsg->msg_data.response.msg_len), - (char *)rcvmsg->msg_data.response.buff_offset, - rcvmsg->msg_data.response.msg_len); - sc_adapter[card]->card->rcvcallb_skb(sc_adapter[card]->driverId, - rcvmsg->phy_link_no - 1, skb); - - case 0x03: - /* - * Recycle the buffer - */ - pr_debug("%s: buffer size : %d\n", - sc_adapter[card]->devicename, BUFFER_SIZE); -/* memset_shmem(card, rcvmsg->msg_data.response.buff_offset, 0, BUFFER_SIZE); */ - newll.buff_offset = rcvmsg->msg_data.response.buff_offset; - newll.msg_len = BUFFER_SIZE; - pr_debug("%s: recycled buffer at offset 0x%lx size %d\n", - sc_adapter[card]->devicename, - newll.buff_offset, newll.msg_len); - sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead, - rcvmsg->phy_link_no, sizeof(LLData), (unsigned int *)&newll); - } - -} - -int setup_buffers(int card, int c) -{ - unsigned int nBuffers, i, cBase; - unsigned int buffer_size; - LLData RcvBuffOffset; - - if (!IS_VALID_CARD(card)) { - pr_debug("invalid param: %d is not a valid card id\n", card); - return -ENODEV; - } - - /* - * Calculate the buffer offsets (send/recv/send/recv) - */ - pr_debug("%s: setting up channel buffer space in shared RAM\n", - sc_adapter[card]->devicename); - buffer_size = BUFFER_SIZE; - nBuffers = ((sc_adapter[card]->ramsize - BUFFER_BASE) / buffer_size) / 2; - nBuffers = nBuffers > BUFFERS_MAX ? BUFFERS_MAX : nBuffers; - pr_debug("%s: calculating buffer space: %d buffers, %d big\n", - sc_adapter[card]->devicename, - nBuffers, buffer_size); - if (nBuffers < 2) { - pr_debug("%s: not enough buffer space\n", - sc_adapter[card]->devicename); - return -1; - } - cBase = (nBuffers * buffer_size) * (c - 1); - pr_debug("%s: channel buffer offset from shared RAM: 0x%x\n", - sc_adapter[card]->devicename, cBase); - sc_adapter[card]->channel[c - 1].first_sendbuf = BUFFER_BASE + cBase; - sc_adapter[card]->channel[c - 1].num_sendbufs = nBuffers / 2; - sc_adapter[card]->channel[c - 1].free_sendbufs = nBuffers / 2; - sc_adapter[card]->channel[c - 1].next_sendbuf = 0; - pr_debug("%s: send buffer setup complete: first=0x%lx n=%d f=%d, nxt=%d\n", - sc_adapter[card]->devicename, - sc_adapter[card]->channel[c - 1].first_sendbuf, - sc_adapter[card]->channel[c - 1].num_sendbufs, - sc_adapter[card]->channel[c - 1].free_sendbufs, - sc_adapter[card]->channel[c - 1].next_sendbuf); - - /* - * Prep the receive buffers - */ - pr_debug("%s: adding %d RecvBuffers:\n", - sc_adapter[card]->devicename, nBuffers / 2); - for (i = 0; i < nBuffers / 2; i++) { - RcvBuffOffset.buff_offset = - ((sc_adapter[card]->channel[c - 1].first_sendbuf + - (nBuffers / 2) * buffer_size) + (buffer_size * i)); - RcvBuffOffset.msg_len = buffer_size; - pr_debug("%s: adding RcvBuffer #%d offset=0x%lx sz=%d bufsz:%d\n", - sc_adapter[card]->devicename, - i + 1, RcvBuffOffset.buff_offset, - RcvBuffOffset.msg_len, buffer_size); - sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead, - c, sizeof(LLData), (unsigned int *)&RcvBuffOffset); - } - return 0; -} diff --git a/drivers/isdn/sc/scioc.h b/drivers/isdn/sc/scioc.h deleted file mode 100644 index a50e143779e7..000000000000 --- a/drivers/isdn/sc/scioc.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef __ISDN_SC_SCIOC_H__ -#define __ISDN_SC_SCIOC_H__ - -/* - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - */ - -/* - * IOCTL Command Codes - */ -#define SCIOCLOAD 0x01 /* Load a firmware record */ -#define SCIOCRESET 0x02 /* Perform hard reset */ -#define SCIOCDEBUG 0x03 /* Set debug level */ -#define SCIOCREV 0x04 /* Get driver revision(s) */ -#define SCIOCSTART 0x05 /* Start the firmware */ -#define SCIOCGETSWITCH 0x06 /* Get switch type */ -#define SCIOCSETSWITCH 0x07 /* Set switch type */ -#define SCIOCGETSPID 0x08 /* Get channel SPID */ -#define SCIOCSETSPID 0x09 /* Set channel SPID */ -#define SCIOCGETDN 0x0A /* Get channel DN */ -#define SCIOCSETDN 0x0B /* Set channel DN */ -#define SCIOCTRACE 0x0C /* Toggle trace mode */ -#define SCIOCSTAT 0x0D /* Get line status */ -#define SCIOCGETSPEED 0x0E /* Set channel speed */ -#define SCIOCSETSPEED 0x0F /* Set channel speed */ -#define SCIOCLOOPTST 0x10 /* Perform loopback test */ - -typedef struct { - int device; - int channel; - unsigned long command; - void __user *dataptr; -} scs_ioctl; - -/* Size of strings */ -#define SCIOC_SPIDSIZE 49 -#define SCIOC_DNSIZE SCIOC_SPIDSIZE -#define SCIOC_REVSIZE SCIOC_SPIDSIZE -#define SCIOC_SRECSIZE 49 - -typedef struct { - unsigned long tx_good; - unsigned long tx_bad; - unsigned long rx_good; - unsigned long rx_bad; -} ChLinkStats; - -typedef struct { - char spid[49]; - char dn[49]; - char call_type; - char phy_stat; - ChLinkStats link_stats; -} BRIStat; - -typedef BRIStat POTStat; - -typedef struct { - char call_type; - char call_state; - char serv_state; - char phy_stat; - ChLinkStats link_stats; -} PRIStat; - -typedef char PRIInfo; -typedef char BRIInfo; -typedef char POTInfo; - - -typedef struct { - char acfa_nos; - char acfa_ais; - char acfa_los; - char acfa_rra; - char acfa_slpp; - char acfa_slpn; - char acfa_fsrf; -} ACFAStat; - -typedef struct { - unsigned char modelid; - char serial_no[13]; - char part_no[13]; - char load_ver[11]; - char proc_ver[11]; - int iobase; - long rambase; - char irq; - long ramsize; - char interface; - char switch_type; - char l1_status; - char l2_status; - ChLinkStats dch_stats; - ACFAStat AcfaStats; - union { - PRIStat pristats[23]; - BRIStat bristats[2]; - POTStat potsstats[2]; - } status; - union { - PRIInfo priinfo; - BRIInfo briinfo; - POTInfo potsinfo; - } info; -} boardInfo; - -#endif /* __ISDN_SC_SCIOC_H__ */ diff --git a/drivers/isdn/sc/shmem.c b/drivers/isdn/sc/shmem.c deleted file mode 100644 index d24506ceb6e8..000000000000 --- a/drivers/isdn/sc/shmem.c +++ /dev/null @@ -1,138 +0,0 @@ -/* $Id: shmem.c,v 1.2.10.1 2001/09/23 22:24:59 kai Exp $ - * - * Copyright (C) 1996 SpellCaster Telecommunications Inc. - * - * Card functions implementing ISDN4Linux functionality - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * For more information, please contact gpl-info@spellcast.com or write: - * - * SpellCaster Telecommunications Inc. - * 5621 Finch Avenue East, Unit #3 - * Scarborough, Ontario Canada - * M1B 2T9 - * +1 (416) 297-8565 - * +1 (416) 297-6433 Facsimile - */ - -#include "includes.h" /* This must be first */ -#include "hardware.h" -#include "card.h" - -/* - * - */ -void memcpy_toshmem(int card, void *dest, const void *src, size_t n) -{ - unsigned long flags; - unsigned char ch; - unsigned long dest_rem = ((unsigned long) dest) % 0x4000; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return; - } - - if (n > SRAM_PAGESIZE) - return; - - /* - * determine the page to load from the address - */ - ch = (unsigned long) dest / SRAM_PAGESIZE; - pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch); - /* - * Block interrupts and load the page - */ - spin_lock_irqsave(&sc_adapter[card]->lock, flags); - - outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80, - sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]); - memcpy_toio((void __iomem *)(sc_adapter[card]->rambase + dest_rem), src, n); - spin_unlock_irqrestore(&sc_adapter[card]->lock, flags); - pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename, - ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80); - pr_debug("%s: copying %zu bytes from %#lx to %#lx\n", - sc_adapter[card]->devicename, n, - (unsigned long) src, - sc_adapter[card]->rambase + ((unsigned long) dest % 0x4000)); -} - -/* - * Reverse of above - */ -void memcpy_fromshmem(int card, void *dest, const void *src, size_t n) -{ - unsigned long flags; - unsigned char ch; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return; - } - - if (n > SRAM_PAGESIZE) { - return; - } - - /* - * determine the page to load from the address - */ - ch = (unsigned long) src / SRAM_PAGESIZE; - pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch); - - - /* - * Block interrupts and load the page - */ - spin_lock_irqsave(&sc_adapter[card]->lock, flags); - - outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80, - sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]); - memcpy_fromio(dest, (void *)(sc_adapter[card]->rambase + - ((unsigned long) src % 0x4000)), n); - spin_unlock_irqrestore(&sc_adapter[card]->lock, flags); - pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename, - ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80); -/* pr_debug("%s: copying %d bytes from %#x to %#x\n", - sc_adapter[card]->devicename, n, - sc_adapter[card]->rambase + ((unsigned long) src %0x4000), (unsigned long) dest); */ -} - -#if 0 -void memset_shmem(int card, void *dest, int c, size_t n) -{ - unsigned long flags; - unsigned char ch; - - if (!IS_VALID_CARD(card)) { - pr_debug("Invalid param: %d is not a valid card id\n", card); - return; - } - - if (n > SRAM_PAGESIZE) { - return; - } - - /* - * determine the page to load from the address - */ - ch = (unsigned long) dest / SRAM_PAGESIZE; - pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch); - - /* - * Block interrupts and load the page - */ - spin_lock_irqsave(&sc_adapter[card]->lock, flags); - - outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80, - sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]); - memset_io(sc_adapter[card]->rambase + - ((unsigned long) dest % 0x4000), c, n); - pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename, - ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80); - spin_unlock_irqrestore(&sc_adapter[card]->lock, flags); -} -#endif /* 0 */ diff --git a/drivers/isdn/sc/timer.c b/drivers/isdn/sc/timer.c deleted file mode 100644 index 6fbac2230d7e..000000000000 --- a/drivers/isdn/sc/timer.c +++ /dev/null @@ -1,122 +0,0 @@ -/* $Id: timer.c,v 1.3.6.1 2001/09/23 22:24:59 kai Exp $ - * - * Copyright (C) 1996 SpellCaster Telecommunications Inc. - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * For more information, please contact gpl-info@spellcast.com or write: - * - * SpellCaster Telecommunications Inc. - * 5621 Finch Avenue East, Unit #3 - * Scarborough, Ontario Canada - * M1B 2T9 - * +1 (416) 297-8565 - * +1 (416) 297-6433 Facsimile - */ - -#include "includes.h" -#include "hardware.h" -#include "message.h" -#include "card.h" - - -/* - * Write the proper values into the I/O ports following a reset - */ -static void setup_ports(int card) -{ - - outb((sc_adapter[card]->rambase >> 12), sc_adapter[card]->ioport[EXP_BASE]); - - /* And the IRQ */ - outb((sc_adapter[card]->interrupt | 0x80), - sc_adapter[card]->ioport[IRQ_SELECT]); -} - -/* - * Timed function to check the status of a previous reset - * Must be very fast as this function runs in the context of - * an interrupt handler. - * - * Setup the ioports for the board that were cleared by the reset. - * Then, check to see if the signate has been set. Next, set the - * signature to a known value and issue a startproc if needed. - */ -void sc_check_reset(unsigned long data) -{ - unsigned long flags; - unsigned long sig; - int card = (unsigned int) data; - - pr_debug("%s: check_timer timer called\n", - sc_adapter[card]->devicename); - - /* Setup the io ports */ - setup_ports(card); - - spin_lock_irqsave(&sc_adapter[card]->lock, flags); - outb(sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport], - (sc_adapter[card]->shmem_magic >> 14) | 0x80); - sig = (unsigned long) *((unsigned long *)(sc_adapter[card]->rambase + SIG_OFFSET)); - - /* check the signature */ - if (sig == SIGNATURE) { - flushreadfifo(card); - spin_unlock_irqrestore(&sc_adapter[card]->lock, flags); - /* See if we need to do a startproc */ - if (sc_adapter[card]->StartOnReset) - startproc(card); - } else { - pr_debug("%s: No signature yet, waiting another %lu jiffies.\n", - sc_adapter[card]->devicename, CHECKRESET_TIME); - mod_timer(&sc_adapter[card]->reset_timer, jiffies + CHECKRESET_TIME); - spin_unlock_irqrestore(&sc_adapter[card]->lock, flags); - } -} - -/* - * Timed function to check the status of a previous reset - * Must be very fast as this function runs in the context of - * an interrupt handler. - * - * Send check sc_adapter->phystat to see if the channels are up - * If they are, tell ISDN4Linux that the board is up. If not, - * tell IADN4Linux that it is up. Always reset the timer to - * fire again (endless loop). - */ -void check_phystat(unsigned long data) -{ - unsigned long flags; - int card = (unsigned int) data; - - pr_debug("%s: Checking status...\n", sc_adapter[card]->devicename); - /* - * check the results of the last PhyStat and change only if - * has changed drastically - */ - if (sc_adapter[card]->nphystat && !sc_adapter[card]->phystat) { /* All is well */ - pr_debug("PhyStat transition to RUN\n"); - pr_info("%s: Switch contacted, transmitter enabled\n", - sc_adapter[card]->devicename); - indicate_status(card, ISDN_STAT_RUN, 0, NULL); - } - else if (!sc_adapter[card]->nphystat && sc_adapter[card]->phystat) { /* All is not well */ - pr_debug("PhyStat transition to STOP\n"); - pr_info("%s: Switch connection lost, transmitter disabled\n", - sc_adapter[card]->devicename); - - indicate_status(card, ISDN_STAT_STOP, 0, NULL); - } - - sc_adapter[card]->phystat = sc_adapter[card]->nphystat; - - /* Reinitialize the timer */ - spin_lock_irqsave(&sc_adapter[card]->lock, flags); - mod_timer(&sc_adapter[card]->stat_timer, jiffies + CHECKSTAT_TIME); - spin_unlock_irqrestore(&sc_adapter[card]->lock, flags); - - /* Send a new cePhyStatus message */ - sendmessage(card, CEPID, ceReqTypePhy, ceReqClass2, - ceReqPhyStatus, 0, 0, NULL); -} -- cgit v1.2.3-59-g8ed1b From 28c5869f2bc4e319027bc22a66a1aa9eefc69a16 Mon Sep 17 00:00:00 2001 From: Helin Zhang Date: Mon, 26 Oct 2015 19:44:27 -0400 Subject: i40e: add new fields to store user configuration This patch adds new fields to i40e_vsi to store user configured RSS config data and code to use it. Signed-off-by: Helin Zhang Change-ID: I73886469dca9e9f6b16d842182a87f3f4009f95d Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 + drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 31 +++++++++------ drivers/net/ethernet/intel/i40e/i40e_main.c | 52 +++++++++++++++++++++++--- 3 files changed, 68 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 89f5323362ed..6efab66a05ae 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -508,6 +508,8 @@ struct i40e_vsi { u16 rss_table_size; u16 rss_size; + u8 *rss_hkey_user; /* User configured hash keys */ + u8 *rss_lut_user; /* User configured lookup table entries */ u16 max_frame; u16 rx_hdr_len; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 6cb2b340cdc2..b52c50924274 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2651,10 +2651,8 @@ static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir, { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; - u8 seed_def[I40E_HKEY_ARRAY_SIZE]; - u8 *lut, *seed = NULL; + u8 *seed = NULL; u16 i; - int ret; if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; @@ -2663,18 +2661,27 @@ static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir, return 0; if (key) { - memcpy(seed_def, key, I40E_HKEY_ARRAY_SIZE); - seed = seed_def; + if (!vsi->rss_hkey_user) { + vsi->rss_hkey_user = kzalloc(I40E_HKEY_ARRAY_SIZE, + GFP_KERNEL); + if (!vsi->rss_hkey_user) + return -ENOMEM; + } + memcpy(vsi->rss_hkey_user, key, I40E_HKEY_ARRAY_SIZE); + seed = vsi->rss_hkey_user; } - lut = kzalloc(I40E_HLUT_ARRAY_SIZE, GFP_KERNEL); - if (!lut) - return -ENOMEM; + if (!vsi->rss_lut_user) { + vsi->rss_lut_user = kzalloc(I40E_HLUT_ARRAY_SIZE, GFP_KERNEL); + if (!vsi->rss_lut_user) + return -ENOMEM; + } + + /* Each 32 bits pointed by 'indir' is stored with a lut entry */ for (i = 0; i < I40E_HLUT_ARRAY_SIZE; i++) - lut[i] = (u8)(indir[i]); - ret = i40e_config_rss(vsi, seed, lut, I40E_HLUT_ARRAY_SIZE); - kfree(lut); + vsi->rss_lut_user[i] = (u8)(indir[i]); - return ret; + return i40e_config_rss(vsi, seed, vsi->rss_lut_user, + I40E_HLUT_ARRAY_SIZE); } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 4b7d87447fe1..c0a784fadeb0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7300,6 +7300,23 @@ static void i40e_vsi_free_arrays(struct i40e_vsi *vsi, bool free_qvectors) vsi->rx_rings = NULL; } +/** + * i40e_clear_rss_config_user - clear the user configured RSS hash keys + * and lookup table + * @vsi: Pointer to VSI structure + */ +static void i40e_clear_rss_config_user(struct i40e_vsi *vsi) +{ + if (!vsi) + return; + + kfree(vsi->rss_hkey_user); + vsi->rss_hkey_user = NULL; + + kfree(vsi->rss_lut_user); + vsi->rss_lut_user = NULL; +} + /** * i40e_vsi_clear - Deallocate the VSI provided * @vsi: the VSI being un-configured @@ -7337,6 +7354,7 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi) i40e_put_lump(pf->irq_pile, vsi->base_vector, vsi->idx); i40e_vsi_free_arrays(vsi, true); + i40e_clear_rss_config_user(vsi); pf->vsi[vsi->idx] = NULL; if (vsi->idx < pf->next_vsi) @@ -8015,8 +8033,6 @@ static int i40e_pf_config_rss(struct i40e_pf *pf) wr32(hw, I40E_PFQF_HENA(0), (u32)hena); wr32(hw, I40E_PFQF_HENA(1), (u32)(hena >> 32)); - vsi->rss_size = min_t(int, pf->rss_size, vsi->num_queue_pairs); - /* Determine the RSS table size based on the hardware capabilities */ reg_val = rd32(hw, I40E_PFQF_CTL_0); reg_val = (pf->rss_table_size == 512) ? @@ -8024,15 +8040,28 @@ static int i40e_pf_config_rss(struct i40e_pf *pf) (reg_val & ~I40E_PFQF_CTL_0_HASHLUTSIZE_512); wr32(hw, I40E_PFQF_CTL_0, reg_val); + /* Determine the RSS size of the VSI */ + if (!vsi->rss_size) + vsi->rss_size = min_t(int, pf->rss_size, vsi->num_queue_pairs); + lut = kzalloc(vsi->rss_table_size, GFP_KERNEL); if (!lut) return -ENOMEM; - i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size); + /* Use user configured lut if there is one, otherwise use default */ + if (vsi->rss_lut_user) + memcpy(lut, vsi->rss_lut_user, vsi->rss_table_size); + else + i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size); - netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); + /* Use user configured hash key if there is one, otherwise + * use default. + */ + if (vsi->rss_hkey_user) + memcpy(seed, vsi->rss_hkey_user, I40E_HKEY_ARRAY_SIZE); + else + netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); ret = i40e_config_rss(vsi, seed, lut, vsi->rss_table_size); - kfree(lut); return ret; @@ -8063,6 +8092,19 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) pf->rss_size = new_rss_size; i40e_reset_and_rebuild(pf, true); + + /* Discard the user configured hash keys and lut, if less + * queues are enabled. + */ + if (queue_count < vsi->rss_size) { + i40e_clear_rss_config_user(vsi); + dev_dbg(&pf->pdev->dev, + "discard user configured hash keys and lut\n"); + } + + /* Reset vsi->rss_size, as number of enabled queues changed */ + vsi->rss_size = min_t(int, pf->rss_size, vsi->num_queue_pairs); + i40e_pf_config_rss(pf); } dev_info(&pf->pdev->dev, "RSS count: %d\n", pf->rss_size); -- cgit v1.2.3-59-g8ed1b From acd65448f2e6b3407b8bb28b57d711ca417ea679 Mon Sep 17 00:00:00 2001 From: Helin Zhang Date: Mon, 26 Oct 2015 19:44:28 -0400 Subject: i40e: rename rss_size to alloc_rss_size in i40e_pf This patch renames rss_size to alloc_rss_size in i40e_pf, which is clearer and avoids confusion. It also adds comments to the other related structure members to help clarify usage. Change-ID: Ia90090609d006ab589cb639975bb8a0af795d16f Signed-off-by: Helin Zhang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 8 ++++---- drivers/net/ethernet/intel/i40e/i40e_main.c | 31 +++++++++++++++++------------ 2 files changed, 22 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 6efab66a05ae..d854a46bba1e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -266,7 +266,7 @@ struct i40e_pf { u16 num_lan_qps; /* num lan queues this PF has set up */ u16 num_lan_msix; /* num queue vectors for the base PF vsi */ int queues_left; /* queues left unclaimed */ - u16 rss_size; /* num queues in the RSS array */ + u16 alloc_rss_size; /* allocated RSS queues */ u16 rss_size_max; /* HW defined max RSS queues */ u16 fdir_pf_filter_count; /* num of guaranteed filters for this PF */ u16 num_alloc_vsi; /* num VSIs this driver supports */ @@ -413,7 +413,7 @@ struct i40e_pf { u32 rx_hwtstamp_cleared; bool ptp_tx; bool ptp_rx; - u16 rss_table_size; + u16 rss_table_size; /* HW RSS table size */ /* These are only valid in NPAR modes */ u32 npar_max_bw; u32 npar_min_bw; @@ -506,8 +506,8 @@ struct i40e_vsi { u16 tx_itr_setting; u16 int_rate_limit; /* value in usecs */ - u16 rss_table_size; - u16 rss_size; + u16 rss_table_size; /* HW RSS table size */ + u16 rss_size; /* Allocated RSS queues */ u8 *rss_hkey_user; /* User configured hash keys */ u8 *rss_lut_user; /* User configured lookup table entries */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c0a784fadeb0..491c82fe6467 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1630,7 +1630,8 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, switch (vsi->type) { case I40E_VSI_MAIN: - qcount = min_t(int, pf->rss_size, num_tc_qps); + qcount = min_t(int, pf->alloc_rss_size, + num_tc_qps); break; #ifdef I40E_FCOE case I40E_VSI_FCOE: @@ -7883,7 +7884,7 @@ static int i40e_vsi_config_rss(struct i40e_vsi *vsi) i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size); netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE); - vsi->rss_size = min_t(int, pf->rss_size, vsi->num_queue_pairs); + vsi->rss_size = min_t(int, pf->alloc_rss_size, vsi->num_queue_pairs); ret = i40e_config_rss_aq(vsi, seed, lut, vsi->rss_table_size); kfree(lut); @@ -8042,7 +8043,8 @@ static int i40e_pf_config_rss(struct i40e_pf *pf) /* Determine the RSS size of the VSI */ if (!vsi->rss_size) - vsi->rss_size = min_t(int, pf->rss_size, vsi->num_queue_pairs); + vsi->rss_size = min_t(int, pf->alloc_rss_size, + vsi->num_queue_pairs); lut = kzalloc(vsi->rss_table_size, GFP_KERNEL); if (!lut) @@ -8089,7 +8091,7 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) vsi->req_queue_pairs = queue_count; i40e_prep_for_reset(pf); - pf->rss_size = new_rss_size; + pf->alloc_rss_size = new_rss_size; i40e_reset_and_rebuild(pf, true); @@ -8103,12 +8105,13 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) } /* Reset vsi->rss_size, as number of enabled queues changed */ - vsi->rss_size = min_t(int, pf->rss_size, vsi->num_queue_pairs); + vsi->rss_size = min_t(int, pf->alloc_rss_size, + vsi->num_queue_pairs); i40e_pf_config_rss(pf); } - dev_info(&pf->pdev->dev, "RSS count: %d\n", pf->rss_size); - return pf->rss_size; + dev_info(&pf->pdev->dev, "RSS count: %d\n", pf->alloc_rss_size); + return pf->alloc_rss_size; } /** @@ -8279,13 +8282,14 @@ static int i40e_sw_init(struct i40e_pf *pf) * maximum might end up larger than the available queues */ pf->rss_size_max = BIT(pf->hw.func_caps.rss_table_entry_width); - pf->rss_size = 1; + pf->alloc_rss_size = 1; pf->rss_table_size = pf->hw.func_caps.rss_table_size; pf->rss_size_max = min_t(int, pf->rss_size_max, pf->hw.func_caps.num_tx_qp); if (pf->hw.func_caps.rss) { pf->flags |= I40E_FLAG_RSS_ENABLED; - pf->rss_size = min_t(int, pf->rss_size_max, num_online_cpus()); + pf->alloc_rss_size = min_t(int, pf->rss_size_max, + num_online_cpus()); } /* MFP mode enabled */ @@ -10152,7 +10156,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) !(pf->flags & I40E_FLAG_MSIX_ENABLED)) { /* one qp for PF, no queues for anything else */ queues_left = 0; - pf->rss_size = pf->num_lan_qps = 1; + pf->alloc_rss_size = pf->num_lan_qps = 1; /* make sure all the fancies are disabled */ pf->flags &= ~(I40E_FLAG_RSS_ENABLED | @@ -10169,7 +10173,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) I40E_FLAG_FD_ATR_ENABLED | I40E_FLAG_DCB_CAPABLE))) { /* one qp for PF */ - pf->rss_size = pf->num_lan_qps = 1; + pf->alloc_rss_size = pf->num_lan_qps = 1; queues_left -= pf->num_lan_qps; pf->flags &= ~(I40E_FLAG_RSS_ENABLED | @@ -10239,8 +10243,9 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) "qs_avail=%d FD SB=%d lan_qs=%d lan_tc0=%d vf=%d*%d vmdq=%d*%d, remaining=%d\n", pf->hw.func_caps.num_tx_qp, !!(pf->flags & I40E_FLAG_FD_SB_ENABLED), - pf->num_lan_qps, pf->rss_size, pf->num_req_vfs, pf->num_vf_qps, - pf->num_vmdq_vsis, pf->num_vmdq_qps, queues_left); + pf->num_lan_qps, pf->alloc_rss_size, pf->num_req_vfs, + pf->num_vf_qps, pf->num_vmdq_vsis, pf->num_vmdq_qps, + queues_left); #ifdef I40E_FCOE dev_dbg(&pf->pdev->dev, "fcoe queues = %d\n", pf->num_fcoe_qps); #endif -- cgit v1.2.3-59-g8ed1b From 6a7fded776a778f728b13d83a2c9fc893580c080 Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Mon, 26 Oct 2015 19:44:29 -0400 Subject: i40e/i40evf: Fix RS bit update in Tx path and disable force WB workaround This patch fixes the issue of forcing WB too often causing us to not benefit from NAPI. Without this patch we were forcing WB/arming interrupt too often taking away the benefits of NAPI and causing a performance impact. With this patch we disable force WB in the clean routine for X710 and XL710 adapters. X722 adapters do not enable interrupt to force a WB and benefit from WB_ON_ITR and hence force WB is left enabled for those adapters. For XL710 and X710 adapters if we have less than 4 packets pending a software Interrupt triggered from service task will force a WB. This patch also changes the conditions for setting RS bit as described in code comments. This optimizes when the HW does a tail bump amd when it does a WB. It also optimizes when we do a wmb. Change-ID: Id831e1ae7d3e2ec3f52cd0917b41ce1d22d75d9d Signed-off-by: Anjali Singhai Jain Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 118 ++++++++++++++++---------- drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 2 + 2 files changed, 77 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 8629a9f77e42..23fd11243583 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -245,16 +245,6 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) tx_ring->q_vector->tx.total_bytes += total_bytes; tx_ring->q_vector->tx.total_packets += total_packets; - /* check to see if there are any non-cache aligned descriptors - * waiting to be written back, and kick the hardware to force - * them to be written back in case of napi polling - */ - if (budget && - !((i & WB_STRIDE) == WB_STRIDE) && - !test_bit(__I40E_DOWN, &tx_ring->vsi->state) && - (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) - tx_ring->arm_wb = true; - netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev, tx_ring->queue_index), total_packets, total_bytes); @@ -1770,6 +1760,9 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, u32 td_tag = 0; dma_addr_t dma; u16 gso_segs; + u16 desc_count = 0; + bool tail_bump = true; + bool do_rs = false; if (tx_flags & I40E_TX_FLAGS_HW_VLAN) { td_cmd |= I40E_TX_DESC_CMD_IL2TAG1; @@ -1810,6 +1803,8 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_desc++; i++; + desc_count++; + if (i == tx_ring->count) { tx_desc = I40E_TX_DESC(tx_ring, 0); i = 0; @@ -1829,6 +1824,8 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_desc++; i++; + desc_count++; + if (i == tx_ring->count) { tx_desc = I40E_TX_DESC(tx_ring, 0); i = 0; @@ -1843,35 +1840,7 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_bi = &tx_ring->tx_bi[i]; } - /* Place RS bit on last descriptor of any packet that spans across the - * 4th descriptor (WB_STRIDE aka 0x3) in a 64B cacheline. - */ #define WB_STRIDE 0x3 - if (((i & WB_STRIDE) != WB_STRIDE) && - (first <= &tx_ring->tx_bi[i]) && - (first >= &tx_ring->tx_bi[i & ~WB_STRIDE])) { - tx_desc->cmd_type_offset_bsz = - build_ctob(td_cmd, td_offset, size, td_tag) | - cpu_to_le64((u64)I40E_TX_DESC_CMD_EOP << - I40E_TXD_QW1_CMD_SHIFT); - } else { - tx_desc->cmd_type_offset_bsz = - build_ctob(td_cmd, td_offset, size, td_tag) | - cpu_to_le64((u64)I40E_TXD_CMD << - I40E_TXD_QW1_CMD_SHIFT); - } - - netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev, - tx_ring->queue_index), - first->bytecount); - - /* Force memory writes to complete before letting h/w - * know there are new descriptors to fetch. (Only - * applicable for weak-ordered memory model archs, - * such as IA-64). - */ - wmb(); - /* set next_to_watch value indicating a packet is present */ first->next_to_watch = tx_desc; @@ -1881,15 +1850,78 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_ring->next_to_use = i; + netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index), + first->bytecount); i40evf_maybe_stop_tx(tx_ring, DESC_NEEDED); + + /* Algorithm to optimize tail and RS bit setting: + * if xmit_more is supported + * if xmit_more is true + * do not update tail and do not mark RS bit. + * if xmit_more is false and last xmit_more was false + * if every packet spanned less than 4 desc + * then set RS bit on 4th packet and update tail + * on every packet + * else + * update tail and set RS bit on every packet. + * if xmit_more is false and last_xmit_more was true + * update tail and set RS bit. + * else (kernel < 3.18) + * if every packet spanned less than 4 desc + * then set RS bit on 4th packet and update tail + * on every packet + * else + * set RS bit on EOP for every packet and update tail + * + * Optimization: wmb to be issued only in case of tail update. + * Also optimize the Descriptor WB path for RS bit with the same + * algorithm. + * + * Note: If there are less than 4 packets + * pending and interrupts were disabled the service task will + * trigger a force WB. + */ + if (skb->xmit_more && + !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index))) { + tx_ring->flags |= I40E_TXR_FLAGS_LAST_XMIT_MORE_SET; + tail_bump = false; + } else if (!skb->xmit_more && + !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index)) && + (!(tx_ring->flags & I40E_TXR_FLAGS_LAST_XMIT_MORE_SET)) && + (tx_ring->packet_stride < WB_STRIDE) && + (desc_count < WB_STRIDE)) { + tx_ring->packet_stride++; + } else { + tx_ring->packet_stride = 0; + tx_ring->flags &= ~I40E_TXR_FLAGS_LAST_XMIT_MORE_SET; + do_rs = true; + } + if (do_rs) + tx_ring->packet_stride = 0; + + tx_desc->cmd_type_offset_bsz = + build_ctob(td_cmd, td_offset, size, td_tag) | + cpu_to_le64((u64)(do_rs ? I40E_TXD_CMD : + I40E_TX_DESC_CMD_EOP) << + I40E_TXD_QW1_CMD_SHIFT); + /* notify HW of packet */ - if (!skb->xmit_more || - netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev, - tx_ring->queue_index))) - writel(i, tx_ring->tail); - else + if (!tail_bump) prefetchw(tx_desc + 1); + if (tail_bump) { + /* Force memory writes to complete before letting h/w + * know there are new descriptors to fetch. (Only + * applicable for weak-ordered memory model archs, + * such as IA-64). + */ + wmb(); + writel(i, tx_ring->tail); + } + return; dma_error: diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 997b374e86af..929ddd9f8215 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -268,6 +268,8 @@ struct i40e_ring { bool ring_active; /* is ring online or not */ bool arm_wb; /* do something to arm write back */ + u8 packet_stride; +#define I40E_TXR_FLAGS_LAST_XMIT_MORE_SET BIT(2) u16 flags; #define I40E_TXR_FLAGS_WB_ON_ITR BIT(0) -- cgit v1.2.3-59-g8ed1b From b74118f08356fd8ab6cb5d1a15705a2760c9afdd Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Mon, 26 Oct 2015 19:44:30 -0400 Subject: i40e/i40evf: prefetch skb data on transmit Issue a prefetch for data early in the transmit path. This should not be generally needed for Tx traffic, but it helps immensely for pktgen workloads and should help for forwarding workloads as well. Change-ID: Iefee870c20599e0c4240e1d8637e4f16b625f83a Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 3 +++ drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index dbd2bcae763d..7371df7b6fb0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2806,6 +2806,9 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, int tsyn; int tso; + /* prefetch the data, we'll need it later */ + prefetch(skb->data); + if (0 == i40e_xmit_descriptor_count(skb, tx_ring)) return NETDEV_TX_BUSY; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 23fd11243583..0f1a4ef5e3be 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1993,6 +1993,9 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, u8 hdr_len = 0; int tso; + /* prefetch the data, we'll need it later */ + prefetch(skb->data); + if (0 == i40evf_xmit_descriptor_count(skb, tx_ring)) return NETDEV_TX_BUSY; -- cgit v1.2.3-59-g8ed1b From 96a8198652e5b80a2cfc87397a3512bf8e45cd63 Mon Sep 17 00:00:00 2001 From: Helin Zhang Date: Mon, 26 Oct 2015 19:44:31 -0400 Subject: i40evf: rename VF adapter specific RSS function This patch renames old VF adapter specific RSS function to clarify its scope. Change-ID: Ie5253083a44c677ebb7709a8a3a18402ad2dc6a6 Signed-off-by: Helin Zhang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 6ad62656c75e..1b38c8883952 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1295,10 +1295,10 @@ static void i40evf_configure_rss_reg(struct i40evf_adapter *adapter, } /** - * i40evf_configure_rss - Prepare for RSS + * i40evf_init_rss - Prepare for RSS * @adapter: board private structure **/ -static void i40evf_configure_rss(struct i40evf_adapter *adapter) +static void i40evf_init_rss(struct i40evf_adapter *adapter) { struct i40e_hw *hw = &adapter->hw; u8 seed[I40EVF_HKEY_ARRAY_SIZE]; @@ -1564,7 +1564,7 @@ static void i40evf_watchdog_task(struct work_struct *work) * PF, so we don't have to set current_op as we will * not get a response through the ARQ. */ - i40evf_configure_rss(adapter); + i40evf_init_rss(adapter); adapter->aq_required &= ~I40EVF_FLAG_AQ_CONFIGURE_RSS; goto watchdog_done; } @@ -2312,7 +2312,7 @@ static void i40evf_init_task(struct work_struct *work) I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE; if (!RSS_AQ(adapter)) - i40evf_configure_rss(adapter); + i40evf_init_rss(adapter); err = i40evf_request_misc_irq(adapter); if (err) goto err_sw_init; @@ -2342,7 +2342,7 @@ static void i40evf_init_task(struct work_struct *work) adapter->aq_required |= I40EVF_FLAG_AQ_CONFIGURE_RSS; mod_timer_pending(&adapter->watchdog_timer, jiffies + 1); } else { - i40evf_configure_rss(adapter); + i40evf_init_rss(adapter); } return; restart: -- cgit v1.2.3-59-g8ed1b From 2c86ac3c70794f0ecc3684b0ae0fd75b0cf0c1f6 Mon Sep 17 00:00:00 2001 From: Helin Zhang Date: Tue, 27 Oct 2015 16:15:06 -0400 Subject: i40evf: create a generic config RSS function There are two ways to configure RSS, this patch adjusts those two functions with the same input parameters, and creates a more generic function for configuring RSS. Change-ID: Iace73bdeba4831909979bef221011060ab327f71 Signed-off-by: Helin Zhang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf.h | 3 + drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 29 ++-- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 168 +++++++++++++-------- 3 files changed, 130 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 22fc3d49c4b9..b5aa377fb8ae 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -99,6 +99,7 @@ struct i40e_vsi { #define MAX_TX_QUEUES MAX_RX_QUEUES #define I40EVF_HKEY_ARRAY_SIZE ((I40E_VFQF_HKEY_MAX_INDEX + 1) * 4) +#define I40EVF_HLUT_ARRAY_SIZE ((I40E_VFQF_HLUT_MAX_INDEX + 1) * 4) /* MAX_MSIX_Q_VECTORS of these are allocated, * but we only use one per queue-specific vector. @@ -313,4 +314,6 @@ void i40evf_request_reset(struct i40evf_adapter *adapter); void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, enum i40e_virtchnl_ops v_opcode, i40e_status v_retval, u8 *msg, u16 msglen); +int i40evf_config_rss(struct i40e_vsi *vsi, const u8 *seed, u8 *lut, + u16 lut_size); #endif /* _I40EVF_H_ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index 4790437a50ac..cf253df7f3b2 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -668,9 +668,11 @@ static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir, const u8 *key, const u8 hfunc) { struct i40evf_adapter *adapter = netdev_priv(netdev); - struct i40e_hw *hw = &adapter->hw; - u32 hlut_val; - int i, j; + struct i40e_vsi *vsi = &adapter->vsi; + u8 seed_def[I40EVF_HKEY_ARRAY_SIZE]; + u8 *seed = NULL, *lut; + int ret; + u16 i; /* We do not allow change in unsupported parameters */ if (key || @@ -679,15 +681,22 @@ static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir, if (!indir) return 0; - for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) { - hlut_val = indir[j++]; - hlut_val |= indir[j++] << 8; - hlut_val |= indir[j++] << 16; - hlut_val |= indir[j++] << 24; - wr32(hw, I40E_VFQF_HLUT(i), hlut_val); + if (key) { + memcpy(seed_def, key, I40EVF_HKEY_ARRAY_SIZE); + seed = seed_def; } + lut = kzalloc(I40EVF_HLUT_ARRAY_SIZE, GFP_KERNEL); + if (!lut) + return -ENOMEM; - return 0; + /* Each 32 bits pointed by 'indir' is stored with a lut entry */ + for (i = 0; i < I40EVF_HLUT_ARRAY_SIZE; i++) + lut[i] = (u8)(indir[i]); + + ret = i40evf_config_rss(vsi, seed, lut, I40EVF_HLUT_ARRAY_SIZE); + kfree(lut); + + return ret; } static const struct ethtool_ops i40evf_ethtool_ops = { diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 1b38c8883952..63d7e470d17c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1211,110 +1211,158 @@ out: } /** - * i40e_configure_rss_aq - Prepare for RSS using AQ commands + * i40e_config_rss_aq - Prepare for RSS using AQ commands * @vsi: vsi structure * @seed: RSS hash seed + * @lut: Lookup table + * @lut_size: Lookup table size + * + * Return 0 on success, negative on failure **/ -static void i40evf_configure_rss_aq(struct i40e_vsi *vsi, const u8 *seed) +static int i40evf_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed, + u8 *lut, u16 lut_size) { - struct i40e_aqc_get_set_rss_key_data rss_key; struct i40evf_adapter *adapter = vsi->back; struct i40e_hw *hw = &adapter->hw; - int ret = 0, i; - u8 *rss_lut; + int ret = 0; if (!vsi->id) - return; + return -EINVAL; if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ dev_err(&adapter->pdev->dev, "Cannot confiure RSS, command %d pending\n", adapter->current_op); - return; + return -EBUSY; } - memset(&rss_key, 0, sizeof(rss_key)); - memcpy(&rss_key, seed, sizeof(rss_key)); - - rss_lut = kzalloc(((I40E_VFQF_HLUT_MAX_INDEX + 1) * 4), GFP_KERNEL); - if (!rss_lut) - return; - - /* Populate the LUT with max no. PF queues in round robin fashion */ - for (i = 0; i <= (I40E_VFQF_HLUT_MAX_INDEX * 4); i++) - rss_lut[i] = i % adapter->num_active_queues; + if (seed) { + struct i40e_aqc_get_set_rss_key_data *rss_key = + (struct i40e_aqc_get_set_rss_key_data *)seed; + ret = i40evf_aq_set_rss_key(hw, vsi->id, rss_key); + if (ret) { + dev_err(&adapter->pdev->dev, "Cannot set RSS key, err %s aq_err %s\n", + i40evf_stat_str(hw, ret), + i40evf_aq_str(hw, hw->aq.asq_last_status)); + return ret; + } + } - ret = i40evf_aq_set_rss_key(hw, vsi->id, &rss_key); - if (ret) { - dev_err(&adapter->pdev->dev, - "Cannot set RSS key, err %s aq_err %s\n", - i40evf_stat_str(hw, ret), - i40evf_aq_str(hw, hw->aq.asq_last_status)); - return; + if (lut) { + ret = i40evf_aq_set_rss_lut(hw, vsi->id, false, lut, lut_size); + if (ret) { + dev_err(&adapter->pdev->dev, + "Cannot set RSS lut, err %s aq_err %s\n", + i40evf_stat_str(hw, ret), + i40evf_aq_str(hw, hw->aq.asq_last_status)); + return ret; + } } - ret = i40evf_aq_set_rss_lut(hw, vsi->id, false, rss_lut, - (I40E_VFQF_HLUT_MAX_INDEX + 1) * 4); - if (ret) - dev_err(&adapter->pdev->dev, - "Cannot set RSS lut, err %s aq_err %s\n", - i40evf_stat_str(hw, ret), - i40evf_aq_str(hw, hw->aq.asq_last_status)); + return ret; } /** - * i40e_configure_rss_reg - Prepare for RSS if used - * @adapter: board private structure + * i40evf_config_rss_reg - Configure RSS keys and lut by writing registers + * @vsi: Pointer to vsi structure * @seed: RSS hash seed + * @lut: Lookup table + * @lut_size: Lookup table size + * + * Returns 0 on success, negative on failure **/ -static void i40evf_configure_rss_reg(struct i40evf_adapter *adapter, - const u8 *seed) +static int i40evf_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed, + const u8 *lut, u16 lut_size) { + struct i40evf_adapter *adapter = vsi->back; struct i40e_hw *hw = &adapter->hw; - u32 *seed_dw = (u32 *)seed; - u32 cqueue = 0; - u32 lut = 0; - int i, j; + u16 i; - /* Fill out hash function seed */ - for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++) - wr32(hw, I40E_VFQF_HKEY(i), seed_dw[i]); - - /* Populate the LUT with max no. PF queues in round robin fashion */ - for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) { - lut = 0; - for (j = 0; j < 4; j++) { - if (cqueue == adapter->num_active_queues) - cqueue = 0; - lut |= ((cqueue) << (8 * j)); - cqueue++; - } - wr32(hw, I40E_VFQF_HLUT(i), lut); + if (seed) { + u32 *seed_dw = (u32 *)seed; + + for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++) + wr32(hw, I40E_VFQF_HKEY(i), seed_dw[i]); + } + + if (lut) { + u32 *lut_dw = (u32 *)lut; + + if (lut_size != I40EVF_HLUT_ARRAY_SIZE) + return -EINVAL; + + for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) + wr32(hw, I40E_VFQF_HLUT(i), lut_dw[i]); } i40e_flush(hw); + + return 0; +} + +/** + * i40evf_config_rss - Configure RSS keys and lut + * @vsi: Pointer to vsi structure + * @seed: RSS hash seed + * @lut: Lookup table + * @lut_size: Lookup table size + * + * Returns 0 on success, negative on failure + **/ +int i40evf_config_rss(struct i40e_vsi *vsi, const u8 *seed, + u8 *lut, u16 lut_size) +{ + struct i40evf_adapter *adapter = vsi->back; + + if (RSS_AQ(adapter)) + return i40evf_config_rss_aq(vsi, seed, lut, lut_size); + else + return i40evf_config_rss_reg(vsi, seed, lut, lut_size); +} + +/** + * i40evf_fill_rss_lut - Fill the lut with default values + * @lut: Lookup table to be filled with + * @rss_table_size: Lookup table size + * @rss_size: Range of queue number for hashing + **/ +static void i40evf_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size) +{ + u16 i; + + for (i = 0; i < rss_table_size; i++) + lut[i] = i % rss_size; } /** * i40evf_init_rss - Prepare for RSS * @adapter: board private structure + * + * Return 0 on success, negative on failure **/ -static void i40evf_init_rss(struct i40evf_adapter *adapter) +static int i40evf_init_rss(struct i40evf_adapter *adapter) { + struct i40e_vsi *vsi = &adapter->vsi; struct i40e_hw *hw = &adapter->hw; u8 seed[I40EVF_HKEY_ARRAY_SIZE]; u64 hena; - - netdev_rss_key_fill((void *)seed, I40EVF_HKEY_ARRAY_SIZE); + u8 *lut; + int ret; /* Enable PCTYPES for RSS, TCP/UDP with IPv4/IPv6 */ hena = I40E_DEFAULT_RSS_HENA; wr32(hw, I40E_VFQF_HENA(0), (u32)hena); wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32)); - if (RSS_AQ(adapter)) - i40evf_configure_rss_aq(&adapter->vsi, seed); - else - i40evf_configure_rss_reg(adapter, seed); + lut = kzalloc(I40EVF_HLUT_ARRAY_SIZE, GFP_KERNEL); + if (!lut) + return -ENOMEM; + i40evf_fill_rss_lut(lut, I40EVF_HLUT_ARRAY_SIZE, + adapter->num_active_queues); + netdev_rss_key_fill((void *)seed, I40EVF_HKEY_ARRAY_SIZE); + ret = i40evf_config_rss(vsi, seed, lut, I40EVF_HLUT_ARRAY_SIZE); + kfree(lut); + + return ret; } /** -- cgit v1.2.3-59-g8ed1b From 90b02b4392eb9778265ec6b4fef034010a937910 Mon Sep 17 00:00:00 2001 From: Helin Zhang Date: Mon, 26 Oct 2015 19:44:33 -0400 Subject: i40evf: create a generic get RSS function There are two ways to get RSS, this patch implements two functions with the same input parameters, and creates a more generic function for getting RSS configuration. Change-ID: I12d3b712c21455d47dd0a5aae58fc9b7c680db59 Signed-off-by: Helin Zhang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf.h | 2 + drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 35 +++++--- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 97 ++++++++++++++++++++++ 3 files changed, 121 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index b5aa377fb8ae..5f2e61dc20ec 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -316,4 +316,6 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, i40e_status v_retval, u8 *msg, u16 msglen); int i40evf_config_rss(struct i40e_vsi *vsi, const u8 *seed, u8 *lut, u16 lut_size); +int i40evf_get_rss(struct i40e_vsi *vsi, const u8 *seed, u8 *lut, + u16 lut_size); #endif /* _I40EVF_H_ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index cf253df7f3b2..a5209707bcf3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -634,25 +634,34 @@ static int i40evf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, u8 *hfunc) { struct i40evf_adapter *adapter = netdev_priv(netdev); - struct i40e_hw *hw = &adapter->hw; - u32 hlut_val; - int i, j; + struct i40e_vsi *vsi = &adapter->vsi; + u8 *seed = NULL, *lut; + int ret; + u16 i; if (hfunc) *hfunc = ETH_RSS_HASH_TOP; if (!indir) return 0; - if (indir) { - for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) { - hlut_val = rd32(hw, I40E_VFQF_HLUT(i)); - indir[j++] = hlut_val & 0xff; - indir[j++] = (hlut_val >> 8) & 0xff; - indir[j++] = (hlut_val >> 16) & 0xff; - indir[j++] = (hlut_val >> 24) & 0xff; - } - } - return 0; + seed = key; + + lut = kzalloc(I40EVF_HLUT_ARRAY_SIZE, GFP_KERNEL); + if (!lut) + return -ENOMEM; + + ret = i40evf_get_rss(vsi, seed, lut, I40EVF_HLUT_ARRAY_SIZE); + if (ret) + goto out; + + /* Each 32 bits pointed by 'indir' is stored with a lut entry */ + for (i = 0; i < I40EVF_HLUT_ARRAY_SIZE; i++) + indir[i] = (u32)lut[i]; + +out: + kfree(lut); + + return ret; } /** diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 63d7e470d17c..68251fa081ec 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1299,6 +1299,84 @@ static int i40evf_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed, return 0; } +/** + * * i40evf_get_rss_aq - Get RSS keys and lut by using AQ commands + * @vsi: Pointer to vsi structure + * @seed: RSS hash seed + * @lut: Lookup table + * @lut_size: Lookup table size + * + * Return 0 on success, negative on failure + **/ +static int i40evf_get_rss_aq(struct i40e_vsi *vsi, const u8 *seed, + u8 *lut, u16 lut_size) +{ + struct i40evf_adapter *adapter = vsi->back; + struct i40e_hw *hw = &adapter->hw; + int ret = 0; + + if (seed) { + ret = i40evf_aq_get_rss_key(hw, vsi->id, + (struct i40e_aqc_get_set_rss_key_data *)seed); + if (ret) { + dev_err(&adapter->pdev->dev, + "Cannot get RSS key, err %s aq_err %s\n", + i40evf_stat_str(hw, ret), + i40evf_aq_str(hw, hw->aq.asq_last_status)); + return ret; + } + } + + if (lut) { + ret = i40evf_aq_get_rss_lut(hw, vsi->id, seed, lut, lut_size); + if (ret) { + dev_err(&adapter->pdev->dev, + "Cannot get RSS lut, err %s aq_err %s\n", + i40evf_stat_str(hw, ret), + i40evf_aq_str(hw, hw->aq.asq_last_status)); + return ret; + } + } + + return ret; +} + +/** + * * i40evf_get_rss_reg - Get RSS keys and lut by reading registers + * @vsi: Pointer to vsi structure + * @seed: RSS hash seed + * @lut: Lookup table + * @lut_size: Lookup table size + * + * Returns 0 on success, negative on failure + **/ +static int i40evf_get_rss_reg(struct i40e_vsi *vsi, const u8 *seed, + const u8 *lut, u16 lut_size) +{ + struct i40evf_adapter *adapter = vsi->back; + struct i40e_hw *hw = &adapter->hw; + u16 i; + + if (seed) { + u32 *seed_dw = (u32 *)seed; + + for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++) + seed_dw[i] = rd32(hw, I40E_VFQF_HKEY(i)); + } + + if (lut) { + u32 *lut_dw = (u32 *)lut; + + if (lut_size != I40EVF_HLUT_ARRAY_SIZE) + return -EINVAL; + + for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) + lut_dw[i] = rd32(hw, I40E_VFQF_HLUT(i)); + } + + return 0; +} + /** * i40evf_config_rss - Configure RSS keys and lut * @vsi: Pointer to vsi structure @@ -1319,6 +1397,25 @@ int i40evf_config_rss(struct i40e_vsi *vsi, const u8 *seed, return i40evf_config_rss_reg(vsi, seed, lut, lut_size); } +/** + * i40evf_get_rss - Get RSS keys and lut + * @vsi: Pointer to vsi structure + * @seed: RSS hash seed + * @lut: Lookup table + * @lut_size: Lookup table size + * + * Returns 0 on success, negative on failure + **/ +int i40evf_get_rss(struct i40e_vsi *vsi, const u8 *seed, u8 *lut, u16 lut_size) +{ + struct i40evf_adapter *adapter = vsi->back; + + if (RSS_AQ(adapter)) + return i40evf_get_rss_aq(vsi, seed, lut, lut_size); + else + return i40evf_get_rss_reg(vsi, seed, lut, lut_size); +} + /** * i40evf_fill_rss_lut - Fill the lut with default values * @lut: Lookup table to be filled with -- cgit v1.2.3-59-g8ed1b From 66f9af855a4df146713866f6076207edea44a4f5 Mon Sep 17 00:00:00 2001 From: Helin Zhang Date: Mon, 26 Oct 2015 19:44:34 -0400 Subject: i40evf: add new fields to store user configuration of RSS This patch adds new fields to i40e_vsi to store user configured RSS config data and code to use it. Change-ID: Ic5d3db8d9df52182b560248f8cdca9c5c7546879 Signed-off-by: Helin Zhang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf.h | 2 ++ drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 31 ++++++++++-------- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 37 ++++++++++++++++++++-- 3 files changed, 54 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 5f2e61dc20ec..090c6048f0df 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -67,6 +67,8 @@ struct i40e_vsi { u16 rx_itr_setting; u16 tx_itr_setting; u16 qs_handle; + u8 *rss_hkey_user; /* User configured hash keys */ + u8 *rss_lut_user; /* User configured lookup table entries */ }; /* How many Rx Buffers do we bundle into one write to the hardware ? */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index a5209707bcf3..966863c93dc4 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -678,9 +678,7 @@ static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir, { struct i40evf_adapter *adapter = netdev_priv(netdev); struct i40e_vsi *vsi = &adapter->vsi; - u8 seed_def[I40EVF_HKEY_ARRAY_SIZE]; - u8 *seed = NULL, *lut; - int ret; + u8 *seed = NULL; u16 i; /* We do not allow change in unsupported parameters */ @@ -691,21 +689,28 @@ static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir, return 0; if (key) { - memcpy(seed_def, key, I40EVF_HKEY_ARRAY_SIZE); - seed = seed_def; + if (!vsi->rss_hkey_user) { + vsi->rss_hkey_user = kzalloc(I40EVF_HKEY_ARRAY_SIZE, + GFP_KERNEL); + if (!vsi->rss_hkey_user) + return -ENOMEM; + } + memcpy(vsi->rss_hkey_user, key, I40EVF_HKEY_ARRAY_SIZE); + seed = vsi->rss_hkey_user; + } + if (!vsi->rss_lut_user) { + vsi->rss_lut_user = kzalloc(I40EVF_HLUT_ARRAY_SIZE, + GFP_KERNEL); + if (!vsi->rss_lut_user) + return -ENOMEM; } - lut = kzalloc(I40EVF_HLUT_ARRAY_SIZE, GFP_KERNEL); - if (!lut) - return -ENOMEM; /* Each 32 bits pointed by 'indir' is stored with a lut entry */ for (i = 0; i < I40EVF_HLUT_ARRAY_SIZE; i++) - lut[i] = (u8)(indir[i]); + vsi->rss_lut_user[i] = (u8)(indir[i]); - ret = i40evf_config_rss(vsi, seed, lut, I40EVF_HLUT_ARRAY_SIZE); - kfree(lut); - - return ret; + return i40evf_config_rss(vsi, seed, vsi->rss_lut_user, + I40EVF_HLUT_ARRAY_SIZE); } static const struct ethtool_ops i40evf_ethtool_ops = { diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 68251fa081ec..1cd5d5d1b660 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1453,9 +1453,21 @@ static int i40evf_init_rss(struct i40evf_adapter *adapter) lut = kzalloc(I40EVF_HLUT_ARRAY_SIZE, GFP_KERNEL); if (!lut) return -ENOMEM; - i40evf_fill_rss_lut(lut, I40EVF_HLUT_ARRAY_SIZE, - adapter->num_active_queues); - netdev_rss_key_fill((void *)seed, I40EVF_HKEY_ARRAY_SIZE); + + /* Use user configured lut if there is one, otherwise use default */ + if (vsi->rss_lut_user) + memcpy(lut, vsi->rss_lut_user, I40EVF_HLUT_ARRAY_SIZE); + else + i40evf_fill_rss_lut(lut, I40EVF_HLUT_ARRAY_SIZE, + adapter->num_active_queues); + + /* Use user configured hash key if there is one, otherwise + * user default. + */ + if (vsi->rss_hkey_user) + memcpy(seed, vsi->rss_hkey_user, I40EVF_HKEY_ARRAY_SIZE); + else + netdev_rss_key_fill((void *)seed, I40EVF_HKEY_ARRAY_SIZE); ret = i40evf_config_rss(vsi, seed, lut, I40EVF_HLUT_ARRAY_SIZE); kfree(lut); @@ -1582,6 +1594,22 @@ err_set_interrupt: return err; } +/** + * i40evf_clear_rss_config_user - Clear user configurations of RSS + * @vsi: Pointer to VSI structure + **/ +static void i40evf_clear_rss_config_user(struct i40e_vsi *vsi) +{ + if (!vsi) + return; + + kfree(vsi->rss_hkey_user); + vsi->rss_hkey_user = NULL; + + kfree(vsi->rss_lut_user); + vsi->rss_lut_user = NULL; +} + /** * i40evf_watchdog_timer - Periodic call-back timer * @data: pointer to adapter disguised as unsigned long @@ -2770,6 +2798,9 @@ static void i40evf_remove(struct pci_dev *pdev) flush_scheduled_work(); + /* Clear user configurations for RSS */ + i40evf_clear_rss_config_user(&adapter->vsi); + if (hw->aq.asq.count) i40evf_shutdown_adminq(hw); -- cgit v1.2.3-59-g8ed1b From 2b2426a760764017cb53ffae128c87b7808dcf11 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Mon, 26 Oct 2015 19:44:35 -0400 Subject: i40e: Update error messaging This patch fixes an issue where adminq init failures always provided a message that NVM was newer than expected. This is not always the case for init_adminq failures. Without this patch, if adminq init fails for any reason, newer NVM message would be given. This problem is fixed by adding a check for that specific error condition and a different hopefully helpful message otherwise. Change-ID: Iaeaebee4e398989eae40bb70f943ab66a3a521a5 Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 491c82fe6467..508cf9a52522 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -10471,6 +10471,16 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pf->hw.fc.requested_mode = I40E_FC_NONE; err = i40e_init_adminq(hw); + if (err) { + if (err == I40E_ERR_FIRMWARE_API_VERSION) + dev_info(&pdev->dev, + "The driver for the device stopped because the NVM image is newer than expected. You must install the most recent version of the network driver.\n"); + else + dev_info(&pdev->dev, + "The driver for the device stopped because the device firmware failed to init. Try updating your NVM image.\n"); + + goto err_pf_reset; + } /* provide nvm, fw, api versions */ dev_info(&pdev->dev, "fw %d.%d.%05d api %d.%d nvm %s\n", @@ -10478,12 +10488,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw->aq.api_maj_ver, hw->aq.api_min_ver, i40e_nvm_version_str(hw)); - if (err) { - dev_info(&pdev->dev, - "The driver for the device stopped because the NVM image is newer than expected. You must install the most recent version of the network driver.\n"); - goto err_pf_reset; - } - if (hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR && hw->aq.api_min_ver > I40E_FW_API_VERSION_MINOR) dev_info(&pdev->dev, -- cgit v1.2.3-59-g8ed1b From e36b0b111bcc651ce030e88e042f83b015dd04b8 Mon Sep 17 00:00:00 2001 From: Helin Zhang Date: Mon, 26 Oct 2015 19:44:36 -0400 Subject: i40e: fix confusing message This patch fixes the confusing kernel message of enabled RSS size, by reporting it together with the hardware maximum RSS size. Change-ID: I64864dbfbc13beccc180a7871680def1f3d5a339 Signed-off-by: Helin Zhang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 508cf9a52522..4e9d6e564861 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8110,7 +8110,8 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) i40e_pf_config_rss(pf); } - dev_info(&pf->pdev->dev, "RSS count: %d\n", pf->alloc_rss_size); + dev_info(&pf->pdev->dev, "RSS count/HW max RSS count: %d/%d\n", + pf->alloc_rss_size, pf->rss_size_max); return pf->alloc_rss_size; } -- cgit v1.2.3-59-g8ed1b From e7ffb72d65c784ba445ac849bd46d2533365fae7 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 26 Oct 2015 19:44:37 -0400 Subject: i40e: make error message more useful If we get an invalid message from a VF, we should tell the user which VF is being naughty, rather than making them guess. Change-ID: I9252cef7baea3d8584043ed6ff12619a94e2f99c Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 9c54ca2bd04d..26f247d5cad3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1094,8 +1094,8 @@ static int i40e_vc_send_msg_to_vf(struct i40e_vf *vf, u32 v_opcode, /* single place to detect unsuccessful return values */ if (v_retval) { vf->num_invalid_msgs++; - dev_err(&pf->pdev->dev, "Failed opcode %d Error: %d\n", - v_opcode, v_retval); + dev_err(&pf->pdev->dev, "VF %d failed opcode %d, error: %d\n", + vf->vf_id, v_opcode, v_retval); if (vf->num_invalid_msgs > I40E_DEFAULT_NUM_INVALID_MSGS_ALLOWED) { dev_err(&pf->pdev->dev, -- cgit v1.2.3-59-g8ed1b From e743072fd16e6e772d5b5e4aef38873946f9a3e0 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 26 Oct 2015 19:44:38 -0400 Subject: i40evf: quoth the VF driver, Nevermore If, upon a midnight dreary, the PF returns ERR_PARAM when the VF is requesting resources, that's fatal. Either the firmware or NVM is badly, badly misconfigured, or this VF has been disabled due to a previous VF driver sending a bunch of bogus messages. Either way, there is no recovery from this. Don't ponder weak and weary, just quit. Change-ID: I09d9f16cc4ee7fec3b57646a289d33838c1c5bf5 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 1cd5d5d1b660..8476fc548f0f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2435,6 +2435,14 @@ static void i40evf_init_task(struct work_struct *work) if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) { err = i40evf_send_vf_config_msg(adapter); goto err; + } else if (err == I40E_ERR_PARAM) { + /* We only get ERR_PARAM if the device is in a very bad + * state or if we've been disabled for previous bad + * behavior. Either way, we're done now. + */ + i40evf_shutdown_adminq(hw); + dev_err(&pdev->dev, "Unable to get VF config due to PF error condition, not retrying\n"); + return; } if (err) { dev_err(&pdev->dev, "Unable to get VF config (%d)\n", -- cgit v1.2.3-59-g8ed1b From 7d96ba1a8b7c36d1f4a04dc40e031b6f8c677496 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 26 Oct 2015 19:44:39 -0400 Subject: i40evf: allocate queue vectors dynamically Change the queue_vector array from a statically-sized member of the adapter structure to a dynamically-allocated and -sized array. This reduces the size of the adapter structure, and allows us to support any number of queue vectors in the future without changing the code. Change-ID: I08dc622cb2f2ad01e832e51c1ad9b86524730693 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf.h | 5 +-- drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 38 ++++++++++------------ .../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 2 +- 4 files changed, 21 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 090c6048f0df..bd185add7948 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -145,9 +145,6 @@ struct i40e_q_vector { #define OTHER_VECTOR 1 #define NONQ_VECS (OTHER_VECTOR) -#define MAX_MSIX_Q_VECTORS 4 -#define MAX_MSIX_COUNT 5 - #define MIN_MSIX_Q_VECTORS 1 #define MIN_MSIX_COUNT (MIN_MSIX_Q_VECTORS + NONQ_VECS) @@ -193,7 +190,7 @@ struct i40evf_adapter { struct work_struct reset_task; struct work_struct adminq_task; struct delayed_work init_task; - struct i40e_q_vector *q_vector[MAX_MSIX_Q_VECTORS]; + struct i40e_q_vector *q_vectors; struct list_head vlan_filter_list; char misc_vector_name[IFNAMSIZ + 9]; int num_active_queues; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index 966863c93dc4..90c5110bb6de 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -351,7 +351,7 @@ static int i40evf_set_coalesce(struct net_device *netdev, vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC; for (i = 0; i < adapter->num_msix_vectors - NONQ_VECS; i++) { - q_vector = adapter->q_vector[i]; + q_vector = &adapter->q_vectors[i]; q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting); wr32(hw, I40E_VFINT_ITRN1(0, i), q_vector->rx.itr); q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 8476fc548f0f..129c274c3673 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -347,7 +347,7 @@ static irqreturn_t i40evf_msix_clean_rings(int irq, void *data) static void i40evf_map_vector_to_rxq(struct i40evf_adapter *adapter, int v_idx, int r_idx) { - struct i40e_q_vector *q_vector = adapter->q_vector[v_idx]; + struct i40e_q_vector *q_vector = &adapter->q_vectors[v_idx]; struct i40e_ring *rx_ring = adapter->rx_rings[r_idx]; rx_ring->q_vector = q_vector; @@ -368,7 +368,7 @@ i40evf_map_vector_to_rxq(struct i40evf_adapter *adapter, int v_idx, int r_idx) static void i40evf_map_vector_to_txq(struct i40evf_adapter *adapter, int v_idx, int t_idx) { - struct i40e_q_vector *q_vector = adapter->q_vector[v_idx]; + struct i40e_q_vector *q_vector = &adapter->q_vectors[v_idx]; struct i40e_ring *tx_ring = adapter->tx_rings[t_idx]; tx_ring->q_vector = q_vector; @@ -464,7 +464,7 @@ static void i40evf_netpoll(struct net_device *netdev) return; for (i = 0; i < q_vectors; i++) - i40evf_msix_clean_rings(0, adapter->q_vector[i]); + i40evf_msix_clean_rings(0, &adapter->q_vectors[i]); } #endif @@ -486,7 +486,7 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) q_vectors = adapter->num_msix_vectors - NONQ_VECS; for (vector = 0; vector < q_vectors; vector++) { - struct i40e_q_vector *q_vector = adapter->q_vector[vector]; + struct i40e_q_vector *q_vector = &adapter->q_vectors[vector]; if (q_vector->tx.ring && q_vector->rx.ring) { snprintf(q_vector->name, sizeof(q_vector->name) - 1, @@ -531,7 +531,7 @@ free_queue_irqs: adapter->msix_entries[vector + NONQ_VECS].vector, NULL); free_irq(adapter->msix_entries[vector + NONQ_VECS].vector, - adapter->q_vector[vector]); + &adapter->q_vectors[vector]); } return err; } @@ -581,7 +581,7 @@ static void i40evf_free_traffic_irqs(struct i40evf_adapter *adapter) irq_set_affinity_hint(adapter->msix_entries[i+1].vector, NULL); free_irq(adapter->msix_entries[i+1].vector, - adapter->q_vector[i]); + &adapter->q_vectors[i]); } } @@ -953,7 +953,7 @@ static void i40evf_napi_enable_all(struct i40evf_adapter *adapter) for (q_idx = 0; q_idx < q_vectors; q_idx++) { struct napi_struct *napi; - q_vector = adapter->q_vector[q_idx]; + q_vector = &adapter->q_vectors[q_idx]; napi = &q_vector->napi; napi_enable(napi); } @@ -970,7 +970,7 @@ static void i40evf_napi_disable_all(struct i40evf_adapter *adapter) int q_vectors = adapter->num_msix_vectors - NONQ_VECS; for (q_idx = 0; q_idx < q_vectors; q_idx++) { - q_vector = adapter->q_vector[q_idx]; + q_vector = &adapter->q_vectors[q_idx]; napi_disable(&q_vector->napi); } } @@ -1483,21 +1483,22 @@ static int i40evf_init_rss(struct i40evf_adapter *adapter) **/ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter) { - int q_idx, num_q_vectors; + int q_idx = 0, num_q_vectors; struct i40e_q_vector *q_vector; num_q_vectors = adapter->num_msix_vectors - NONQ_VECS; + adapter->q_vectors = kzalloc(sizeof(*q_vector) * num_q_vectors, + GFP_KERNEL); + if (!adapter->q_vectors) + goto err_out; for (q_idx = 0; q_idx < num_q_vectors; q_idx++) { - q_vector = kzalloc(sizeof(*q_vector), GFP_KERNEL); - if (!q_vector) - goto err_out; + q_vector = &adapter->q_vectors[q_idx]; q_vector->adapter = adapter; q_vector->vsi = &adapter->vsi; q_vector->v_idx = q_idx; netif_napi_add(adapter->netdev, &q_vector->napi, i40evf_napi_poll, NAPI_POLL_WEIGHT); - adapter->q_vector[q_idx] = q_vector; } return 0; @@ -1505,11 +1506,10 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter) err_out: while (q_idx) { q_idx--; - q_vector = adapter->q_vector[q_idx]; + q_vector = &adapter->q_vectors[q_idx]; netif_napi_del(&q_vector->napi); - kfree(q_vector); - adapter->q_vector[q_idx] = NULL; } + kfree(adapter->q_vectors); return -ENOMEM; } @@ -1530,13 +1530,11 @@ static void i40evf_free_q_vectors(struct i40evf_adapter *adapter) napi_vectors = adapter->num_active_queues; for (q_idx = 0; q_idx < num_q_vectors; q_idx++) { - struct i40e_q_vector *q_vector = adapter->q_vector[q_idx]; - - adapter->q_vector[q_idx] = NULL; + struct i40e_q_vector *q_vector = &adapter->q_vectors[q_idx]; if (q_idx < napi_vectors) netif_napi_del(&q_vector->napi); - kfree(q_vector); } + kfree(adapter->q_vectors); } /** diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 46b051684908..24b3af31b5a2 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -360,7 +360,7 @@ void i40evf_map_queues(struct i40evf_adapter *adapter) vimi->num_vectors = adapter->num_msix_vectors; /* Queue vectors first */ for (v_idx = 0; v_idx < q_vectors; v_idx++) { - q_vector = adapter->q_vector[v_idx]; + q_vector = adapter->q_vectors + v_idx; vimi->vecmap[v_idx].vsi_id = adapter->vsi_res->vsi_id; vimi->vecmap[v_idx].vector_id = v_idx + NONQ_VECS; vimi->vecmap[v_idx].txq_map = q_vector->ring_mask; -- cgit v1.2.3-59-g8ed1b From 0dd438d8ad5db85c1a805ce2244252e37d5586a0 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 26 Oct 2015 19:44:40 -0400 Subject: i40evf: allocate ring structs dynamically Instead of awkwardly keeping a fixed array of pointers in the adapter struct and then allocating ring structs individually, just keep a single pointer and allocate a single blob for the arrays. This simplifies code, shrinks the adapter structure, and future-proofs the driver by not limiting the number of rings we can handle. Change-ID: I31334ff911a6474954232cfe4bc98ccca3c769ff Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf.h | 4 +- drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 8 +-- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 57 +++++++++++----------- .../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 10 ++-- 5 files changed, 40 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 0f1a4ef5e3be..11561b648417 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -2063,7 +2063,7 @@ out_drop: netdev_tx_t i40evf_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct i40evf_adapter *adapter = netdev_priv(netdev); - struct i40e_ring *tx_ring = adapter->tx_rings[skb->queue_mapping]; + struct i40e_ring *tx_ring = &adapter->tx_rings[skb->queue_mapping]; /* hardware can't handle really short frames, hardware padding works * beyond this point diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index bd185add7948..a6318c421695 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -196,13 +196,13 @@ struct i40evf_adapter { int num_active_queues; /* TX */ - struct i40e_ring *tx_rings[I40E_MAX_VSI_QP]; + struct i40e_ring *tx_rings; u32 tx_timeout_count; struct list_head mac_filter_list; u32 tx_desc_count; /* RX */ - struct i40e_ring *rx_rings[I40E_MAX_VSI_QP]; + struct i40e_ring *rx_rings; u64 hw_csum_rx_error; u32 rx_desc_count; int num_msix_vectors; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index 90c5110bb6de..a4c9feb589e7 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -121,12 +121,12 @@ static void i40evf_get_ethtool_stats(struct net_device *netdev, data[i] = *(u64 *)p; } for (j = 0; j < adapter->num_active_queues; j++) { - data[i++] = adapter->tx_rings[j]->stats.packets; - data[i++] = adapter->tx_rings[j]->stats.bytes; + data[i++] = adapter->tx_rings[j].stats.packets; + data[i++] = adapter->tx_rings[j].stats.bytes; } for (j = 0; j < adapter->num_active_queues; j++) { - data[i++] = adapter->rx_rings[j]->stats.packets; - data[i++] = adapter->rx_rings[j]->stats.bytes; + data[i++] = adapter->rx_rings[j].stats.packets; + data[i++] = adapter->rx_rings[j].stats.bytes; } } diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 129c274c3673..af14160524c5 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -348,7 +348,7 @@ static void i40evf_map_vector_to_rxq(struct i40evf_adapter *adapter, int v_idx, int r_idx) { struct i40e_q_vector *q_vector = &adapter->q_vectors[v_idx]; - struct i40e_ring *rx_ring = adapter->rx_rings[r_idx]; + struct i40e_ring *rx_ring = &adapter->rx_rings[r_idx]; rx_ring->q_vector = q_vector; rx_ring->next = q_vector->rx.ring; @@ -369,7 +369,7 @@ static void i40evf_map_vector_to_txq(struct i40evf_adapter *adapter, int v_idx, int t_idx) { struct i40e_q_vector *q_vector = &adapter->q_vectors[v_idx]; - struct i40e_ring *tx_ring = adapter->tx_rings[t_idx]; + struct i40e_ring *tx_ring = &adapter->tx_rings[t_idx]; tx_ring->q_vector = q_vector; tx_ring->next = q_vector->tx.ring; @@ -610,7 +610,7 @@ static void i40evf_configure_tx(struct i40evf_adapter *adapter) int i; for (i = 0; i < adapter->num_active_queues; i++) - adapter->tx_rings[i]->tail = hw->hw_addr + I40E_QTX_TAIL1(i); + adapter->tx_rings[i].tail = hw->hw_addr + I40E_QTX_TAIL1(i); } /** @@ -655,8 +655,8 @@ static void i40evf_configure_rx(struct i40evf_adapter *adapter) } for (i = 0; i < adapter->num_active_queues; i++) { - adapter->rx_rings[i]->tail = hw->hw_addr + I40E_QRX_TAIL1(i); - adapter->rx_rings[i]->rx_buf_len = rx_buf_len; + adapter->rx_rings[i].tail = hw->hw_addr + I40E_QRX_TAIL1(i); + adapter->rx_rings[i].rx_buf_len = rx_buf_len; } } @@ -991,7 +991,7 @@ static void i40evf_configure(struct i40evf_adapter *adapter) adapter->aq_required |= I40EVF_FLAG_AQ_CONFIGURE_QUEUES; for (i = 0; i < adapter->num_active_queues; i++) { - struct i40e_ring *ring = adapter->rx_rings[i]; + struct i40e_ring *ring = &adapter->rx_rings[i]; i40evf_alloc_rx_buffers_1buf(ring, ring->count); ring->next_to_use = ring->count - 1; @@ -1111,16 +1111,10 @@ i40evf_acquire_msix_vectors(struct i40evf_adapter *adapter, int vectors) **/ static void i40evf_free_queues(struct i40evf_adapter *adapter) { - int i; - if (!adapter->vsi_res) return; - for (i = 0; i < adapter->num_active_queues; i++) { - if (adapter->tx_rings[i]) - kfree_rcu(adapter->tx_rings[i], rcu); - adapter->tx_rings[i] = NULL; - adapter->rx_rings[i] = NULL; - } + kfree(adapter->tx_rings); + kfree(adapter->rx_rings); } /** @@ -1135,13 +1129,20 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter) { int i; + adapter->tx_rings = kcalloc(adapter->num_active_queues, + sizeof(struct i40e_ring), GFP_KERNEL); + if (!adapter->tx_rings) + goto err_out; + adapter->rx_rings = kcalloc(adapter->num_active_queues, + sizeof(struct i40e_ring), GFP_KERNEL); + if (!adapter->rx_rings) + goto err_out; + for (i = 0; i < adapter->num_active_queues; i++) { struct i40e_ring *tx_ring; struct i40e_ring *rx_ring; - tx_ring = kzalloc(sizeof(*tx_ring) * 2, GFP_KERNEL); - if (!tx_ring) - goto err_out; + tx_ring = &adapter->tx_rings[i]; tx_ring->queue_index = i; tx_ring->netdev = adapter->netdev; @@ -1149,14 +1150,12 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter) tx_ring->count = adapter->tx_desc_count; if (adapter->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) tx_ring->flags |= I40E_TXR_FLAGS_WB_ON_ITR; - adapter->tx_rings[i] = tx_ring; - rx_ring = &tx_ring[1]; + rx_ring = &adapter->rx_rings[i]; rx_ring->queue_index = i; rx_ring->netdev = adapter->netdev; rx_ring->dev = &adapter->pdev->dev; rx_ring->count = adapter->rx_desc_count; - adapter->rx_rings[i] = rx_ring; } return 0; @@ -1487,7 +1486,7 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter) struct i40e_q_vector *q_vector; num_q_vectors = adapter->num_msix_vectors - NONQ_VECS; - adapter->q_vectors = kzalloc(sizeof(*q_vector) * num_q_vectors, + adapter->q_vectors = kcalloc(num_q_vectors, sizeof(*q_vector), GFP_KERNEL); if (!adapter->q_vectors) goto err_out; @@ -2035,8 +2034,8 @@ void i40evf_free_all_tx_resources(struct i40evf_adapter *adapter) int i; for (i = 0; i < adapter->num_active_queues; i++) - if (adapter->tx_rings[i]->desc) - i40evf_free_tx_resources(adapter->tx_rings[i]); + if (adapter->tx_rings[i].desc) + i40evf_free_tx_resources(&adapter->tx_rings[i]); } /** @@ -2054,8 +2053,8 @@ static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter) int i, err = 0; for (i = 0; i < adapter->num_active_queues; i++) { - adapter->tx_rings[i]->count = adapter->tx_desc_count; - err = i40evf_setup_tx_descriptors(adapter->tx_rings[i]); + adapter->tx_rings[i].count = adapter->tx_desc_count; + err = i40evf_setup_tx_descriptors(&adapter->tx_rings[i]); if (!err) continue; dev_err(&adapter->pdev->dev, @@ -2081,8 +2080,8 @@ static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter) int i, err = 0; for (i = 0; i < adapter->num_active_queues; i++) { - adapter->rx_rings[i]->count = adapter->rx_desc_count; - err = i40evf_setup_rx_descriptors(adapter->rx_rings[i]); + adapter->rx_rings[i].count = adapter->rx_desc_count; + err = i40evf_setup_rx_descriptors(&adapter->rx_rings[i]); if (!err) continue; dev_err(&adapter->pdev->dev, @@ -2103,8 +2102,8 @@ void i40evf_free_all_rx_resources(struct i40evf_adapter *adapter) int i; for (i = 0; i < adapter->num_active_queues; i++) - if (adapter->rx_rings[i]->desc) - i40evf_free_rx_resources(adapter->rx_rings[i]); + if (adapter->rx_rings[i].desc) + i40evf_free_rx_resources(&adapter->rx_rings[i]); } /** diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 24b3af31b5a2..9b5557622657 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -255,19 +255,19 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter) for (i = 0; i < pairs; i++) { vqpi->txq.vsi_id = vqci->vsi_id; vqpi->txq.queue_id = i; - vqpi->txq.ring_len = adapter->tx_rings[i]->count; - vqpi->txq.dma_ring_addr = adapter->tx_rings[i]->dma; + vqpi->txq.ring_len = adapter->tx_rings[i].count; + vqpi->txq.dma_ring_addr = adapter->tx_rings[i].dma; vqpi->txq.headwb_enabled = 1; vqpi->txq.dma_headwb_addr = vqpi->txq.dma_ring_addr + (vqpi->txq.ring_len * sizeof(struct i40e_tx_desc)); vqpi->rxq.vsi_id = vqci->vsi_id; vqpi->rxq.queue_id = i; - vqpi->rxq.ring_len = adapter->rx_rings[i]->count; - vqpi->rxq.dma_ring_addr = adapter->rx_rings[i]->dma; + vqpi->rxq.ring_len = adapter->rx_rings[i].count; + vqpi->rxq.dma_ring_addr = adapter->rx_rings[i].dma; vqpi->rxq.max_pkt_size = adapter->netdev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN; - vqpi->rxq.databuffer_size = adapter->rx_rings[i]->rx_buf_len; + vqpi->rxq.databuffer_size = adapter->rx_rings[i].rx_buf_len; vqpi++; } -- cgit v1.2.3-59-g8ed1b From e1c227919548fb9d8ee771e715d2764f1d41e8e0 Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Mon, 26 Oct 2015 19:44:41 -0400 Subject: i40e/i40evf: Bump i40e version to 1.4.4 and i40evf to 1.4.1 Bump. Change-ID: I00ebbb2e5e5572f947502b8f6db4d94f666d6b14 Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 4e9d6e564861..7759703a40bc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -39,7 +39,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 4 -#define DRV_VERSION_BUILD 2 +#define DRV_VERSION_BUILD 4 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index af14160524c5..7ef10424a67f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -34,7 +34,7 @@ char i40evf_driver_name[] = "i40evf"; static const char i40evf_driver_string[] = "Intel(R) XL710/X710 Virtual Function Network Driver"; -#define DRV_VERSION "1.3.33" +#define DRV_VERSION "1.4.1" const char i40evf_driver_version[] = DRV_VERSION; static const char i40evf_copyright[] = "Copyright (c) 2013 - 2015 Intel Corporation."; -- cgit v1.2.3-59-g8ed1b From 0e4425ed641f3eef67c892bc541949cd745a9ba9 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Thu, 5 Nov 2015 17:01:01 -0800 Subject: i40e: fix: do not sleep in netdev_ops The driver was being called by VLAN, bonding, teaming operations that expected to be able to hold locks like rcu_read_lock(). This causes the driver to be held to the requirement to not sleep, and was found by the kernel debug options for checking sleep inside critical section, and the locking validator. Change-ID: Ibc68c835f5ffa8ffe0638ffe910a66fc5649a7f7 Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 44 +++++++++++++---------------- 1 file changed, 20 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7759703a40bc..3119ee44b0bf 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1552,9 +1552,11 @@ static int i40e_set_mac(struct net_device *netdev, void *p) spin_unlock_bh(&vsi->mac_filter_list_lock); } - i40e_sync_vsi_filters(vsi, false); ether_addr_copy(netdev->dev_addr, addr->sa_data); - + /* schedule our worker thread which will take care of + * applying the new filter changes + */ + i40e_service_event_schedule(vsi->back); return 0; } @@ -2118,12 +2120,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl) */ if (pf->cur_promisc != cur_promisc) { pf->cur_promisc = cur_promisc; - if (grab_rtnl) - i40e_do_reset_safe(pf, - BIT(__I40E_PF_RESET_REQUESTED)); - else - i40e_do_reset(pf, - BIT(__I40E_PF_RESET_REQUESTED)); + set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); } } else { ret = i40e_aq_set_vsi_unicast_promiscuous( @@ -2383,16 +2380,13 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid) } } - /* Make sure to release before sync_vsi_filter because that - * function will lock/unlock as necessary - */ spin_unlock_bh(&vsi->mac_filter_list_lock); - if (test_bit(__I40E_DOWN, &vsi->back->state) || - test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) - return 0; - - return i40e_sync_vsi_filters(vsi, false); + /* schedule our worker thread which will take care of + * applying the new filter changes + */ + i40e_service_event_schedule(vsi->back); + return 0; } /** @@ -2465,16 +2459,13 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid) } } - /* Make sure to release before sync_vsi_filter because that - * function with lock/unlock as necessary - */ spin_unlock_bh(&vsi->mac_filter_list_lock); - if (test_bit(__I40E_DOWN, &vsi->back->state) || - test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) - return 0; - - return i40e_sync_vsi_filters(vsi, false); + /* schedule our worker thread which will take care of + * applying the new filter changes + */ + i40e_service_event_schedule(vsi->back); + return 0; } /** @@ -2717,6 +2708,11 @@ static void i40e_config_xps_tx_ring(struct i40e_ring *ring) netif_set_xps_queue(ring->netdev, mask, ring->queue_index); free_cpumask_var(mask); } + + /* schedule our worker thread which will take care of + * applying the new filter changes + */ + i40e_service_event_schedule(vsi->back); } /** -- cgit v1.2.3-59-g8ed1b From 17652c6336fdca0f48906f0a1d9b5ac77b29e5a7 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Thu, 5 Nov 2015 17:01:02 -0800 Subject: i40e: remove unused argument With the final edition of the patches to remove sleeps from the driver's entry points, the grab_rtnl argument is no longer needed, so partially revert the commit that added it. Change-ID: Ib9778476242586cc9e58b670f5f48d415cb59003 Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 +- drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 4 ++-- drivers/net/ethernet/intel/i40e/i40e_main.c | 16 +++++++++++----- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 8 ++++---- 4 files changed, 18 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index d854a46bba1e..0b9537b37412 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -697,7 +697,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, bool is_vf, bool is_netdev); void i40e_del_filter(struct i40e_vsi *vsi, u8 *macaddr, s16 vlan, bool is_vf, bool is_netdev); -int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl); +int i40e_sync_vsi_filters(struct i40e_vsi *vsi); struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, u16 uplink, u32 param1); int i40e_vsi_release(struct i40e_vsi *vsi); diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index d1a91c8178d6..10744a698d6f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -1138,7 +1138,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, spin_lock_bh(&vsi->mac_filter_list_lock); f = i40e_add_filter(vsi, ma, vlan, false, false); spin_unlock_bh(&vsi->mac_filter_list_lock); - ret = i40e_sync_vsi_filters(vsi, true); + ret = i40e_sync_vsi_filters(vsi); if (f && !ret) dev_info(&pf->pdev->dev, "add macaddr: %pM vlan=%d added to VSI %d\n", @@ -1177,7 +1177,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, spin_lock_bh(&vsi->mac_filter_list_lock); i40e_del_filter(vsi, ma, vlan, false, false); spin_unlock_bh(&vsi->mac_filter_list_lock); - ret = i40e_sync_vsi_filters(vsi, true); + ret = i40e_sync_vsi_filters(vsi); if (!ret) dev_info(&pf->pdev->dev, "del macaddr: %pM vlan=%d removed from VSI %d\n", diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 3119ee44b0bf..9e6268b4295a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1859,13 +1859,12 @@ static void i40e_cleanup_add_list(struct list_head *add_list) /** * i40e_sync_vsi_filters - Update the VSI filter list to the HW * @vsi: ptr to the VSI - * @grab_rtnl: whether RTNL needs to be grabbed * * Push any outstanding VSI filter changes through the AdminQ. * * Returns 0 or error value **/ -int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl) +int i40e_sync_vsi_filters(struct i40e_vsi *vsi) { struct list_head tmp_del_list, tmp_add_list; struct i40e_mac_filter *f, *ftmp, *fclone; @@ -2169,8 +2168,15 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf) for (v = 0; v < pf->num_alloc_vsi; v++) { if (pf->vsi[v] && - (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED)) - i40e_sync_vsi_filters(pf->vsi[v], true); + (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED)) { + int ret = i40e_sync_vsi_filters(pf->vsi[v]); + + if (ret) { + /* come back and try again later */ + pf->flags |= I40E_FLAG_FILTER_SYNC; + break; + } + } } } @@ -9219,7 +9225,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi) f->is_vf, f->is_netdev); spin_unlock_bh(&vsi->mac_filter_list_lock); - i40e_sync_vsi_filters(vsi, false); + i40e_sync_vsi_filters(vsi); i40e_vsi_delete(vsi); i40e_vsi_free_q_vectors(vsi); diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 26f247d5cad3..819803c8e461 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -565,7 +565,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) } /* program mac filter */ - ret = i40e_sync_vsi_filters(vsi, false); + ret = i40e_sync_vsi_filters(vsi); if (ret) dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); @@ -1633,7 +1633,7 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) spin_unlock_bh(&vsi->mac_filter_list_lock); /* program the updated filter list */ - if (i40e_sync_vsi_filters(vsi, false)) + if (i40e_sync_vsi_filters(vsi)) dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters\n", vf->vf_id); @@ -1687,7 +1687,7 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) spin_unlock_bh(&vsi->mac_filter_list_lock); /* program the updated filter list */ - if (i40e_sync_vsi_filters(vsi, false)) + if (i40e_sync_vsi_filters(vsi)) dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters\n", vf->vf_id); @@ -2102,7 +2102,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", mac, vf_id); /* program mac filter */ - if (i40e_sync_vsi_filters(vsi, false)) { + if (i40e_sync_vsi_filters(vsi)) { dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); ret = -EIO; goto error_param; -- cgit v1.2.3-59-g8ed1b From 22e9dd249bd4bcf338c282d90df08ccf5dbfbee2 Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:03 -0800 Subject: hv_netvsc: Resize some of the variables in hv_netvsc_packet As part of reducing the size of the hv_netvsc_packet, resize some of the variables based on their usage. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 7661a12b00f4..5f0a21fe0fb4 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -127,11 +127,11 @@ struct ndis_tcp_ip_checksum_info; */ struct hv_netvsc_packet { /* Bookkeeping stuff */ - u32 status; + u8 status; - bool is_data_pkt; - bool xmit_more; /* from skb */ - bool cp_partial; /* partial copy into send buffer */ + u8 is_data_pkt; + u8 xmit_more; /* from skb */ + u8 cp_partial; /* partial copy into send buffer */ u16 vlan_tci; @@ -147,13 +147,13 @@ struct hv_netvsc_packet { /* This points to the memory after page_buf */ struct rndis_message *rndis_msg; - u32 rmsg_size; /* RNDIS header and PPI size */ - u32 rmsg_pgcnt; /* page count of RNDIS header and PPI */ + u8 rmsg_size; /* RNDIS header and PPI size */ + u8 rmsg_pgcnt; /* page count of RNDIS header and PPI */ u32 total_data_buflen; /* Points to the send/receive buffer where the ethernet frame is */ void *data; - u32 page_buf_cnt; + u8 page_buf_cnt; struct hv_page_buffer *page_buf; }; -- cgit v1.2.3-59-g8ed1b From 934d202255ecd03d1f48b1031a2e03cb647e73d9 Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:04 -0800 Subject: hv_netvsc: Rearrange the hv_negtvsc_packet to be space efficient Rearrange the elements of struct hv_negtvsc_packet for optimal layout - eliminate unnecessary padding. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 5f0a21fe0fb4..3f66a23dd94f 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -128,32 +128,34 @@ struct ndis_tcp_ip_checksum_info; struct hv_netvsc_packet { /* Bookkeeping stuff */ u8 status; - u8 is_data_pkt; u8 xmit_more; /* from skb */ u8 cp_partial; /* partial copy into send buffer */ - u16 vlan_tci; + u8 rmsg_size; /* RNDIS header and PPI size */ + u8 rmsg_pgcnt; /* page count of RNDIS header and PPI */ + u8 page_buf_cnt; + u8 pad0; + u16 vlan_tci; u16 q_idx; + u32 send_buf_index; + + u32 total_data_buflen; + u32 pad1; + struct vmbus_channel *channel; u64 send_completion_tid; void *send_completion_ctx; void (*send_completion)(void *context); - u32 send_buf_index; /* This points to the memory after page_buf */ struct rndis_message *rndis_msg; - u8 rmsg_size; /* RNDIS header and PPI size */ - u8 rmsg_pgcnt; /* page count of RNDIS header and PPI */ - - u32 total_data_buflen; /* Points to the send/receive buffer where the ethernet frame is */ void *data; - u8 page_buf_cnt; struct hv_page_buffer *page_buf; }; -- cgit v1.2.3-59-g8ed1b From 25b85ee890530f70de850f15660ed41abbee1172 Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:05 -0800 Subject: hv_netvsc: Eliminate the channel field in hv_netvsc_packet structure Eliminate the channel field in hv_netvsc_packet structure. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 22 ++++++++++++++++++---- drivers/net/hyperv/netvsc.c | 19 ++++++++----------- drivers/net/hyperv/netvsc_drv.c | 5 +++-- drivers/net/hyperv/rndis_filter.c | 10 ++++++---- 4 files changed, 35 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 3f66a23dd94f..6bb1acd2a81c 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -144,7 +144,6 @@ struct hv_netvsc_packet { u32 total_data_buflen; u32 pad1; - struct vmbus_channel *channel; u64 send_completion_tid; void *send_completion_ctx; @@ -198,7 +197,8 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, void netvsc_xmit_completion(void *context); int netvsc_recv_callback(struct hv_device *device_obj, struct hv_netvsc_packet *packet, - struct ndis_tcp_ip_checksum_info *csum_info); + struct ndis_tcp_ip_checksum_info *csum_info, + struct vmbus_channel *channel); void netvsc_channel_cb(void *context); int rndis_filter_open(struct hv_device *dev); int rndis_filter_close(struct hv_device *dev); @@ -206,12 +206,12 @@ int rndis_filter_device_add(struct hv_device *dev, void *additional_info); void rndis_filter_device_remove(struct hv_device *dev); int rndis_filter_receive(struct hv_device *dev, - struct hv_netvsc_packet *pkt); + struct hv_netvsc_packet *pkt, + struct vmbus_channel *channel); int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac); - #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) #define NVSP_PROTOCOL_VERSION_1 2 @@ -1274,5 +1274,19 @@ struct rndis_message { #define TRANSPORT_INFO_IPV6_TCP ((INFO_IPV6 << 16) | INFO_TCP) #define TRANSPORT_INFO_IPV6_UDP ((INFO_IPV6 << 16) | INFO_UDP) +static inline struct vmbus_channel *get_channel(struct hv_netvsc_packet *packet, + struct netvsc_device *net_device) + +{ + struct vmbus_channel *out_channel; + + out_channel = net_device->chn_table[packet->q_idx]; + if (!out_channel) { + out_channel = net_device->dev->channel; + packet->q_idx = 0; + } + return out_channel; +} + #endif /* _HYPERV_NET_H */ diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 51e4c0fd0a74..52533edbd2b0 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -610,6 +610,7 @@ static inline void netvsc_free_send_slot(struct netvsc_device *net_device, } static void netvsc_send_completion(struct netvsc_device *net_device, + struct vmbus_channel *incoming_channel, struct hv_device *device, struct vmpacket_descriptor *packet) { @@ -651,7 +652,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device, if (send_index != NETVSC_INVALID_INDEX) netvsc_free_send_slot(net_device, send_index); q_idx = nvsc_packet->q_idx; - channel = nvsc_packet->channel; + channel = incoming_channel; nvsc_packet->send_completion(nvsc_packet-> send_completion_ctx); } @@ -748,7 +749,7 @@ static inline int netvsc_send_pkt( struct netvsc_device *net_device) { struct nvsp_message nvmsg; - struct vmbus_channel *out_channel = packet->channel; + struct vmbus_channel *out_channel = get_channel(packet, net_device); u16 q_idx = packet->q_idx; struct net_device *ndev = net_device->ndev; u64 req_id; @@ -857,13 +858,9 @@ int netvsc_send(struct hv_device *device, if (!net_device) return -ENODEV; - out_channel = net_device->chn_table[q_idx]; - if (!out_channel) { - out_channel = device->channel; - q_idx = 0; - packet->q_idx = 0; - } - packet->channel = out_channel; + out_channel = get_channel(packet, net_device); + q_idx = packet->q_idx; + packet->send_buf_index = NETVSC_INVALID_INDEX; packet->cp_partial = false; @@ -1043,7 +1040,6 @@ static void netvsc_receive(struct netvsc_device *net_device, } count = vmxferpage_packet->range_cnt; - netvsc_packet->channel = channel; /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ for (i = 0; i < count; i++) { @@ -1055,7 +1051,7 @@ static void netvsc_receive(struct netvsc_device *net_device, vmxferpage_packet->ranges[i].byte_count; /* Pass it to the upper layer */ - rndis_filter_receive(device, netvsc_packet); + rndis_filter_receive(device, netvsc_packet, channel); if (netvsc_packet->status != NVSP_STAT_SUCCESS) status = NVSP_STAT_FAIL; @@ -1150,6 +1146,7 @@ void netvsc_channel_cb(void *context) switch (desc->type) { case VM_PKT_COMP: netvsc_send_completion(net_device, + channel, device, desc); break; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 268a05821d46..c8f294ee347b 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -683,7 +683,8 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, */ int netvsc_recv_callback(struct hv_device *device_obj, struct hv_netvsc_packet *packet, - struct ndis_tcp_ip_checksum_info *csum_info) + struct ndis_tcp_ip_checksum_info *csum_info, + struct vmbus_channel *channel) { struct net_device *net; struct net_device_context *net_device_ctx; @@ -729,7 +730,7 @@ int netvsc_recv_callback(struct hv_device *device_obj, __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), packet->vlan_tci); - skb_record_rx_queue(skb, packet->channel-> + skb_record_rx_queue(skb, channel-> offermsg.offer.sub_channel_index); u64_stats_update_begin(&rx_stats->syncp); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 5931a799aa17..1b04d78bc7b9 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -350,7 +350,8 @@ static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type) static void rndis_filter_receive_data(struct rndis_device *dev, struct rndis_message *msg, - struct hv_netvsc_packet *pkt) + struct hv_netvsc_packet *pkt, + struct vmbus_channel *channel) { struct rndis_packet *rndis_pkt; u32 data_offset; @@ -393,11 +394,12 @@ static void rndis_filter_receive_data(struct rndis_device *dev, } csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); - netvsc_recv_callback(dev->net_dev->dev, pkt, csum_info); + netvsc_recv_callback(dev->net_dev->dev, pkt, csum_info, channel); } int rndis_filter_receive(struct hv_device *dev, - struct hv_netvsc_packet *pkt) + struct hv_netvsc_packet *pkt, + struct vmbus_channel *channel) { struct netvsc_device *net_dev = hv_get_drvdata(dev); struct rndis_device *rndis_dev; @@ -436,7 +438,7 @@ int rndis_filter_receive(struct hv_device *dev, switch (rndis_msg->ndis_msg_type) { case RNDIS_MSG_PACKET: /* data msg */ - rndis_filter_receive_data(rndis_dev, rndis_msg, pkt); + rndis_filter_receive_data(rndis_dev, rndis_msg, pkt, channel); break; case RNDIS_MSG_INIT_C: -- cgit v1.2.3-59-g8ed1b From 24476760ef0b45bab75ea6731d081aeb48113e6a Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:06 -0800 Subject: hv_netvsc: Eliminate rndis_msg pointer from hv_netvsc_packet structure Eliminate rndis_msg pointer from hv_netvsc_packet structure. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 8 +++----- drivers/net/hyperv/netvsc.c | 10 ++++++---- drivers/net/hyperv/netvsc_drv.c | 7 +++---- drivers/net/hyperv/rndis_filter.c | 2 +- 4 files changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 6bb1acd2a81c..14deedd1b411 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -149,10 +149,6 @@ struct hv_netvsc_packet { void *send_completion_ctx; void (*send_completion)(void *context); - - /* This points to the memory after page_buf */ - struct rndis_message *rndis_msg; - /* Points to the send/receive buffer where the ethernet frame is */ void *data; struct hv_page_buffer *page_buf; @@ -188,10 +184,12 @@ struct rndis_device { /* Interface */ +struct rndis_message; int netvsc_device_add(struct hv_device *device, void *additional_info); int netvsc_device_remove(struct hv_device *device); int netvsc_send(struct hv_device *device, - struct hv_netvsc_packet *packet); + struct hv_netvsc_packet *packet, + struct rndis_message *rndis_msg); void netvsc_linkstatus_callback(struct hv_device *device_obj, struct rndis_message *resp); void netvsc_xmit_completion(void *context); diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 52533edbd2b0..2de9e7fb4f68 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -706,7 +706,8 @@ static u32 netvsc_get_next_send_section(struct netvsc_device *net_device) static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, unsigned int section_index, u32 pend_size, - struct hv_netvsc_packet *packet) + struct hv_netvsc_packet *packet, + struct rndis_message *rndis_msg) { char *start = net_device->send_buf; char *dest = start + (section_index * net_device->send_section_size) @@ -722,7 +723,7 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, if (packet->is_data_pkt && packet->xmit_more && remain && !packet->cp_partial) { padding = net_device->pkt_align - remain; - packet->rndis_msg->msg_len += padding; + rndis_msg->msg_len += padding; packet->total_data_buflen += padding; } @@ -841,7 +842,8 @@ static inline int netvsc_send_pkt( } int netvsc_send(struct hv_device *device, - struct hv_netvsc_packet *packet) + struct hv_netvsc_packet *packet, + struct rndis_message *rndis_msg) { struct netvsc_device *net_device; int ret = 0, m_ret = 0; @@ -897,7 +899,7 @@ int netvsc_send(struct hv_device *device, if (section_index != NETVSC_INVALID_INDEX) { netvsc_copy_to_send_buf(net_device, section_index, msd_len, - packet); + packet, rndis_msg); packet->send_buf_index = section_index; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index c8f294ee347b..e5f81c797e26 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -483,10 +483,10 @@ check_size: packet->is_data_pkt = true; packet->total_data_buflen = skb->len; - packet->rndis_msg = (struct rndis_message *)((unsigned long)packet + + rndis_msg = (struct rndis_message *)((unsigned long)packet + sizeof(struct hv_netvsc_packet)); - memset(packet->rndis_msg, 0, RNDIS_AND_PPI_SIZE); + memset(rndis_msg, 0, RNDIS_AND_PPI_SIZE); /* Set the completion routine */ packet->send_completion = netvsc_xmit_completion; @@ -496,7 +496,6 @@ check_size: isvlan = packet->vlan_tci & VLAN_TAG_PRESENT; /* Add the rndis header */ - rndis_msg = packet->rndis_msg; rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET; rndis_msg->msg_len = packet->total_data_buflen; rndis_pkt = &rndis_msg->msg.pkt; @@ -620,7 +619,7 @@ do_send: packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size, skb, packet); - ret = netvsc_send(net_device_ctx->device_ctx, packet); + ret = netvsc_send(net_device_ctx->device_ctx, packet, rndis_msg); drop: if (ret == 0) { diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 1b04d78bc7b9..63584e7a49e3 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -240,7 +240,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, packet->send_completion = NULL; packet->xmit_more = false; - ret = netvsc_send(dev->net_dev->dev, packet); + ret = netvsc_send(dev->net_dev->dev, packet, NULL); return ret; } -- cgit v1.2.3-59-g8ed1b From c4b20c6370aa5cdbe11536125d86f31378d4b702 Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:07 -0800 Subject: hv_netvsc: Eliminatte the data field from struct hv_netvsc_packet Eliminatte the data field from struct hv_netvsc_packet. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 5 ++--- drivers/net/hyperv/netvsc.c | 5 +++-- drivers/net/hyperv/netvsc_drv.c | 3 ++- drivers/net/hyperv/rndis_filter.c | 11 +++++++---- 4 files changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 14deedd1b411..62542081a864 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -148,9 +148,6 @@ struct hv_netvsc_packet { u64 send_completion_tid; void *send_completion_ctx; void (*send_completion)(void *context); - - /* Points to the send/receive buffer where the ethernet frame is */ - void *data; struct hv_page_buffer *page_buf; }; @@ -195,6 +192,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, void netvsc_xmit_completion(void *context); int netvsc_recv_callback(struct hv_device *device_obj, struct hv_netvsc_packet *packet, + void **data, struct ndis_tcp_ip_checksum_info *csum_info, struct vmbus_channel *channel); void netvsc_channel_cb(void *context); @@ -205,6 +203,7 @@ int rndis_filter_device_add(struct hv_device *dev, void rndis_filter_device_remove(struct hv_device *dev); int rndis_filter_receive(struct hv_device *dev, struct hv_netvsc_packet *pkt, + void **data, struct vmbus_channel *channel); int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 2de9e7fb4f68..8fbf81626bc3 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -1008,6 +1008,7 @@ static void netvsc_receive(struct netvsc_device *net_device, int i; int count = 0; struct net_device *ndev; + void *data; ndev = net_device->ndev; @@ -1047,13 +1048,13 @@ static void netvsc_receive(struct netvsc_device *net_device, for (i = 0; i < count; i++) { /* Initialize the netvsc packet */ netvsc_packet->status = NVSP_STAT_SUCCESS; - netvsc_packet->data = (void *)((unsigned long)net_device-> + data = (void *)((unsigned long)net_device-> recv_buf + vmxferpage_packet->ranges[i].byte_offset); netvsc_packet->total_data_buflen = vmxferpage_packet->ranges[i].byte_count; /* Pass it to the upper layer */ - rndis_filter_receive(device, netvsc_packet, channel); + rndis_filter_receive(device, netvsc_packet, &data, channel); if (netvsc_packet->status != NVSP_STAT_SUCCESS) status = NVSP_STAT_FAIL; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index e5f81c797e26..622e62e32636 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -682,6 +682,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, */ int netvsc_recv_callback(struct hv_device *device_obj, struct hv_netvsc_packet *packet, + void **data, struct ndis_tcp_ip_checksum_info *csum_info, struct vmbus_channel *channel) { @@ -710,7 +711,7 @@ int netvsc_recv_callback(struct hv_device *device_obj, * Copy to skb. This copy is needed here since the memory pointed by * hv_netvsc_packet cannot be deallocated */ - memcpy(skb_put(skb, packet->total_data_buflen), packet->data, + memcpy(skb_put(skb, packet->total_data_buflen), *data, packet->total_data_buflen); skb->protocol = eth_type_trans(skb, net); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 63584e7a49e3..be0fa9c94f63 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -351,6 +351,7 @@ static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type) static void rndis_filter_receive_data(struct rndis_device *dev, struct rndis_message *msg, struct hv_netvsc_packet *pkt, + void **data, struct vmbus_channel *channel) { struct rndis_packet *rndis_pkt; @@ -383,7 +384,7 @@ static void rndis_filter_receive_data(struct rndis_device *dev, * the data packet to the stack, without the rndis trailer padding */ pkt->total_data_buflen = rndis_pkt->data_len; - pkt->data = (void *)((unsigned long)pkt->data + data_offset); + *data = (void *)((unsigned long)(*data) + data_offset); vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO); if (vlan) { @@ -394,11 +395,12 @@ static void rndis_filter_receive_data(struct rndis_device *dev, } csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); - netvsc_recv_callback(dev->net_dev->dev, pkt, csum_info, channel); + netvsc_recv_callback(dev->net_dev->dev, pkt, data, csum_info, channel); } int rndis_filter_receive(struct hv_device *dev, struct hv_netvsc_packet *pkt, + void **data, struct vmbus_channel *channel) { struct netvsc_device *net_dev = hv_get_drvdata(dev); @@ -430,7 +432,7 @@ int rndis_filter_receive(struct hv_device *dev, goto exit; } - rndis_msg = pkt->data; + rndis_msg = *data; if (netif_msg_rx_err(net_dev->nd_ctx)) dump_rndis_message(dev, rndis_msg); @@ -438,7 +440,8 @@ int rndis_filter_receive(struct hv_device *dev, switch (rndis_msg->ndis_msg_type) { case RNDIS_MSG_PACKET: /* data msg */ - rndis_filter_receive_data(rndis_dev, rndis_msg, pkt, channel); + rndis_filter_receive_data(rndis_dev, rndis_msg, pkt, + data, channel); break; case RNDIS_MSG_INIT_C: -- cgit v1.2.3-59-g8ed1b From 09215ef5dfd5a2c6c169733162091ca7e56a4890 Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:08 -0800 Subject: hv_netvsc: Eliminate send_completion from struct hv_netvsc_packet Eliminate send_completion from struct hv_netvsc_packet. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 3 +-- drivers/net/hyperv/netvsc.c | 6 +++--- drivers/net/hyperv/netvsc_drv.c | 2 +- drivers/net/hyperv/rndis_filter.c | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 62542081a864..f096f2fd713d 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -135,7 +135,7 @@ struct hv_netvsc_packet { u8 rmsg_size; /* RNDIS header and PPI size */ u8 rmsg_pgcnt; /* page count of RNDIS header and PPI */ u8 page_buf_cnt; - u8 pad0; + u8 completion_func; u16 vlan_tci; u16 q_idx; @@ -147,7 +147,6 @@ struct hv_netvsc_packet { u64 send_completion_tid; void *send_completion_ctx; - void (*send_completion)(void *context); struct hv_page_buffer *page_buf; }; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 8fbf81626bc3..34c16d19f05d 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -653,8 +653,8 @@ static void netvsc_send_completion(struct netvsc_device *net_device, netvsc_free_send_slot(net_device, send_index); q_idx = nvsc_packet->q_idx; channel = incoming_channel; - nvsc_packet->send_completion(nvsc_packet-> - send_completion_ctx); + netvsc_xmit_completion(nvsc_packet-> + send_completion_ctx); } num_outstanding_sends = @@ -775,7 +775,7 @@ static inline int netvsc_send_pkt( nvmsg.msg.v1_msg.send_rndis_pkt.send_buf_section_size = packet->total_data_buflen; - if (packet->send_completion) + if (packet->completion_func) req_id = (ulong)packet; else req_id = 0; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 622e62e32636..8f29d805bd77 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -489,7 +489,7 @@ check_size: memset(rndis_msg, 0, RNDIS_AND_PPI_SIZE); /* Set the completion routine */ - packet->send_completion = netvsc_xmit_completion; + packet->completion_func = 1; packet->send_completion_ctx = packet; packet->send_completion_tid = (unsigned long)skb; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index be0fa9c94f63..c8af172faee3 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -237,7 +237,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, packet->page_buf[0].len; } - packet->send_completion = NULL; + packet->completion_func = 0; packet->xmit_more = false; ret = netvsc_send(dev->net_dev->dev, packet, NULL); -- cgit v1.2.3-59-g8ed1b From 074c2fe5ef3d09e9a1008d307a22ab5086e22728 Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:09 -0800 Subject: hv_netvsc: Eliminate send_completion_ctx from struct hv_netvsc_packet Eliminate send_completion_ctx from struct hv_netvsc_packet. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 1 - drivers/net/hyperv/netvsc.c | 3 +-- drivers/net/hyperv/netvsc_drv.c | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index f096f2fd713d..fc6d0c6de741 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -146,7 +146,6 @@ struct hv_netvsc_packet { u64 send_completion_tid; - void *send_completion_ctx; struct hv_page_buffer *page_buf; }; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 34c16d19f05d..0e0b723df1ac 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -653,8 +653,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device, netvsc_free_send_slot(net_device, send_index); q_idx = nvsc_packet->q_idx; channel = incoming_channel; - netvsc_xmit_completion(nvsc_packet-> - send_completion_ctx); + netvsc_xmit_completion(nvsc_packet); } num_outstanding_sends = diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 8f29d805bd77..7e356a11c1d7 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -490,7 +490,6 @@ check_size: /* Set the completion routine */ packet->completion_func = 1; - packet->send_completion_ctx = packet; packet->send_completion_tid = (unsigned long)skb; isvlan = packet->vlan_tci & VLAN_TAG_PRESENT; -- cgit v1.2.3-59-g8ed1b From c0eb454034aab783dc602739237a63b30867f5bd Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:10 -0800 Subject: hv_netvsc: Don't ask for additional head room in the skb The rndis header is 116 bytes big and can be placed in the default head room that will be available in the skb. Since the netvsc packet is less than 48 bytes, we can use the skb control buffer for the netvsc packet. With these changes we don't need to ask for additional head room. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 3 +++ drivers/net/hyperv/netvsc_drv.c | 30 +++++++++++------------------- include/linux/netdevice.h | 4 +++- 3 files changed, 17 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index fc6d0c6de741..731054ef6da5 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -124,6 +124,9 @@ struct ndis_tcp_ip_checksum_info; /* * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame * within the RNDIS + * + * The size of this structure is less than 48 bytes and we can now + * place this structure in the skb->cb field. */ struct hv_netvsc_packet { /* Bookkeeping stuff */ diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 7e356a11c1d7..b820888409bc 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -433,7 +433,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) u32 net_trans_info; u32 hash; u32 skb_length; - u32 pkt_sz; struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT]; struct netvsc_stats *tx_stats = this_cpu_ptr(net_device_ctx->tx_stats); @@ -461,16 +460,21 @@ check_size: goto check_size; } - pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE; - - ret = skb_cow_head(skb, pkt_sz); + /* + * Place the rndis header in the skb head room and + * the skb->cb will be used for hv_netvsc_packet + * structure. + */ + ret = skb_cow_head(skb, RNDIS_AND_PPI_SIZE); if (ret) { netdev_err(net, "unable to alloc hv_netvsc_packet\n"); ret = -ENOMEM; goto drop; } - /* Use the headroom for building up the packet */ - packet = (struct hv_netvsc_packet *)skb->head; + /* Use the skb control buffer for building up the packet */ + BUILD_BUG_ON(sizeof(struct hv_netvsc_packet) > + FIELD_SIZEOF(struct sk_buff, cb)); + packet = (struct hv_netvsc_packet *)skb->cb; packet->status = 0; packet->xmit_more = skb->xmit_more; @@ -483,8 +487,7 @@ check_size: packet->is_data_pkt = true; packet->total_data_buflen = skb->len; - rndis_msg = (struct rndis_message *)((unsigned long)packet + - sizeof(struct hv_netvsc_packet)); + rndis_msg = (struct rndis_message *)skb->head; memset(rndis_msg, 0, RNDIS_AND_PPI_SIZE); @@ -1118,16 +1121,12 @@ static int netvsc_probe(struct hv_device *dev, struct netvsc_device_info device_info; struct netvsc_device *nvdev; int ret; - u32 max_needed_headroom; net = alloc_etherdev_mq(sizeof(struct net_device_context), num_online_cpus()); if (!net) return -ENOMEM; - max_needed_headroom = sizeof(struct hv_netvsc_packet) + - RNDIS_AND_PPI_SIZE; - netif_carrier_off(net); net_device_ctx = netdev_priv(net); @@ -1166,13 +1165,6 @@ static int netvsc_probe(struct hv_device *dev, net->ethtool_ops = ðtool_ops; SET_NETDEV_DEV(net, &dev->device); - /* - * Request additional head room in the skb. - * We will use this space to build the rndis - * heaser and other state we need to maintain. - */ - net->needed_headroom = max_needed_headroom; - /* Notify the netvsc driver of the new device */ memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7d2d1d7aaec7..fcbc5259c630 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -132,7 +132,9 @@ static inline bool dev_xmit_complete(int rc) * used. */ -#if defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) +#if defined(CONFIG_HYPERV_NET) +# define LL_MAX_HEADER 128 +#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25) # if defined(CONFIG_MAC80211_MESH) # define LL_MAX_HEADER 128 # else -- cgit v1.2.3-59-g8ed1b From 8b9fbe1ac390689f01153d6af8485caec5423ccc Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Tue, 1 Dec 2015 16:43:11 -0800 Subject: hv_netvsc: move subchannel existence check to netvsc_select_queue() Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 15 --------------- drivers/net/hyperv/netvsc.c | 5 ++--- drivers/net/hyperv/netvsc_drv.c | 3 +++ 3 files changed, 5 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 731054ef6da5..8d534a324ce1 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -1272,19 +1272,4 @@ struct rndis_message { #define TRANSPORT_INFO_IPV6_TCP ((INFO_IPV6 << 16) | INFO_TCP) #define TRANSPORT_INFO_IPV6_UDP ((INFO_IPV6 << 16) | INFO_UDP) -static inline struct vmbus_channel *get_channel(struct hv_netvsc_packet *packet, - struct netvsc_device *net_device) - -{ - struct vmbus_channel *out_channel; - - out_channel = net_device->chn_table[packet->q_idx]; - if (!out_channel) { - out_channel = net_device->dev->channel; - packet->q_idx = 0; - } - return out_channel; -} - - #endif /* _HYPERV_NET_H */ diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 0e0b723df1ac..419b05515b92 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -749,8 +749,8 @@ static inline int netvsc_send_pkt( struct netvsc_device *net_device) { struct nvsp_message nvmsg; - struct vmbus_channel *out_channel = get_channel(packet, net_device); u16 q_idx = packet->q_idx; + struct vmbus_channel *out_channel = net_device->chn_table[q_idx]; struct net_device *ndev = net_device->ndev; u64 req_id; int ret; @@ -859,8 +859,7 @@ int netvsc_send(struct hv_device *device, if (!net_device) return -ENODEV; - out_channel = get_channel(packet, net_device); - q_idx = packet->q_idx; + out_channel = net_device->chn_table[q_idx]; packet->send_buf_index = NETVSC_INVALID_INDEX; packet->cp_partial = false; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index b820888409bc..38b53faa8119 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -273,6 +273,9 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, skb_set_hash(skb, hash, PKT_HASH_TYPE_L3); } + if (!nvsc_dev->chn_table[q_idx]) + q_idx = 0; + return q_idx; } -- cgit v1.2.3-59-g8ed1b From 2a04ae8acb144996eba6e3d69ab2a7156c775416 Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Tue, 1 Dec 2015 16:43:12 -0800 Subject: hv_netvsc: remove locking in netvsc_send() Packet scheduler guarantees there won't be multiple senders for the same queue and as we use q_idx for multi_send_data the spinlock is redundant. Signed-off-by: Vitaly Kuznetsov Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 1 - drivers/net/hyperv/netvsc.c | 8 -------- 2 files changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 8d534a324ce1..ee0f55957ab8 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -632,7 +632,6 @@ struct nvsp_message { #define RNDIS_PKT_ALIGN_DEFAULT 8 struct multi_send_data { - spinlock_t lock; /* protect struct multi_send_data */ struct hv_netvsc_packet *pkt; /* netvsc pkt pending */ u32 count; /* counter of batched packets */ }; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 419b05515b92..081f14f75509 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -38,7 +38,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) { struct netvsc_device *net_device; struct net_device *ndev = hv_get_drvdata(device); - int i; net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL); if (!net_device) @@ -58,9 +57,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT; net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT; - for (i = 0; i < num_online_cpus(); i++) - spin_lock_init(&net_device->msd[i].lock); - hv_set_drvdata(device, net_device); return net_device; } @@ -850,7 +846,6 @@ int netvsc_send(struct hv_device *device, u16 q_idx = packet->q_idx; u32 pktlen = packet->total_data_buflen, msd_len = 0; unsigned int section_index = NETVSC_INVALID_INDEX; - unsigned long flag; struct multi_send_data *msdp; struct hv_netvsc_packet *msd_send = NULL, *cur_send = NULL; bool try_batch; @@ -867,7 +862,6 @@ int netvsc_send(struct hv_device *device, msdp = &net_device->msd[q_idx]; /* batch packets in send buffer if possible */ - spin_lock_irqsave(&msdp->lock, flag); if (msdp->pkt) msd_len = msdp->pkt->total_data_buflen; @@ -927,8 +921,6 @@ int netvsc_send(struct hv_device *device, cur_send = packet; } - spin_unlock_irqrestore(&msdp->lock, flag); - if (msd_send) { m_ret = netvsc_send_pkt(msd_send, net_device); -- cgit v1.2.3-59-g8ed1b From a9f2e2d6569d00ae8f9f832f1b56bc7702dfd2d3 Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:13 -0800 Subject: hv_netvsc: Eliminate page_buf from struct hv_netvsc_packet Eliminate page_buf from struct hv_netvsc_packet. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 4 ++-- drivers/net/hyperv/netvsc.c | 25 ++++++++++++++----------- drivers/net/hyperv/netvsc_drv.c | 11 ++++++----- drivers/net/hyperv/rndis_filter.c | 26 +++++++++++++------------- 4 files changed, 35 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index ee0f55957ab8..c7517b146b4f 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -149,7 +149,6 @@ struct hv_netvsc_packet { u64 send_completion_tid; - struct hv_page_buffer *page_buf; }; struct netvsc_device_info { @@ -187,7 +186,8 @@ int netvsc_device_add(struct hv_device *device, void *additional_info); int netvsc_device_remove(struct hv_device *device); int netvsc_send(struct hv_device *device, struct hv_netvsc_packet *packet, - struct rndis_message *rndis_msg); + struct rndis_message *rndis_msg, + struct hv_page_buffer **page_buffer); void netvsc_linkstatus_callback(struct hv_device *device_obj, struct rndis_message *resp); void netvsc_xmit_completion(void *context); diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 081f14f75509..18058a593b35 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -702,7 +702,8 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, unsigned int section_index, u32 pend_size, struct hv_netvsc_packet *packet, - struct rndis_message *rndis_msg) + struct rndis_message *rndis_msg, + struct hv_page_buffer **pb) { char *start = net_device->send_buf; char *dest = start + (section_index * net_device->send_section_size) @@ -723,9 +724,9 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, } for (i = 0; i < page_count; i++) { - char *src = phys_to_virt(packet->page_buf[i].pfn << PAGE_SHIFT); - u32 offset = packet->page_buf[i].offset; - u32 len = packet->page_buf[i].len; + char *src = phys_to_virt((*pb)[i].pfn << PAGE_SHIFT); + u32 offset = (*pb)[i].offset; + u32 len = (*pb)[i].len; memcpy(dest, (src + offset), len); msg_size += len; @@ -742,7 +743,8 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, static inline int netvsc_send_pkt( struct hv_netvsc_packet *packet, - struct netvsc_device *net_device) + struct netvsc_device *net_device, + struct hv_page_buffer **pb) { struct nvsp_message nvmsg; u16 q_idx = packet->q_idx; @@ -789,8 +791,8 @@ static inline int netvsc_send_pkt( packet->xmit_more = false; if (packet->page_buf_cnt) { - pgbuf = packet->cp_partial ? packet->page_buf + - packet->rmsg_pgcnt : packet->page_buf; + pgbuf = packet->cp_partial ? (*pb) + + packet->rmsg_pgcnt : (*pb); ret = vmbus_sendpacket_pagebuffer_ctl(out_channel, pgbuf, packet->page_buf_cnt, @@ -838,7 +840,8 @@ static inline int netvsc_send_pkt( int netvsc_send(struct hv_device *device, struct hv_netvsc_packet *packet, - struct rndis_message *rndis_msg) + struct rndis_message *rndis_msg, + struct hv_page_buffer **pb) { struct netvsc_device *net_device; int ret = 0, m_ret = 0; @@ -891,7 +894,7 @@ int netvsc_send(struct hv_device *device, if (section_index != NETVSC_INVALID_INDEX) { netvsc_copy_to_send_buf(net_device, section_index, msd_len, - packet, rndis_msg); + packet, rndis_msg, pb); packet->send_buf_index = section_index; @@ -922,7 +925,7 @@ int netvsc_send(struct hv_device *device, } if (msd_send) { - m_ret = netvsc_send_pkt(msd_send, net_device); + m_ret = netvsc_send_pkt(msd_send, net_device, pb); if (m_ret != 0) { netvsc_free_send_slot(net_device, @@ -932,7 +935,7 @@ int netvsc_send(struct hv_device *device, } if (cur_send) - ret = netvsc_send_pkt(cur_send, net_device); + ret = netvsc_send_pkt(cur_send, net_device, pb); if (ret != 0 && section_index != NETVSC_INVALID_INDEX) netvsc_free_send_slot(net_device, section_index); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 38b53faa8119..eca669257360 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -324,9 +324,10 @@ static u32 fill_pg_buf(struct page *page, u32 offset, u32 len, } static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb, - struct hv_netvsc_packet *packet) + struct hv_netvsc_packet *packet, + struct hv_page_buffer **page_buf) { - struct hv_page_buffer *pb = packet->page_buf; + struct hv_page_buffer *pb = *page_buf; u32 slots_used = 0; char *data = skb->data; int frags = skb_shinfo(skb)->nr_frags; @@ -437,6 +438,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) u32 hash; u32 skb_length; struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT]; + struct hv_page_buffer *pb = page_buf; struct netvsc_stats *tx_stats = this_cpu_ptr(net_device_ctx->tx_stats); /* We will atmost need two pages to describe the rndis @@ -483,7 +485,6 @@ check_size: packet->xmit_more = skb->xmit_more; packet->vlan_tci = skb->vlan_tci; - packet->page_buf = page_buf; packet->q_idx = skb_get_queue_mapping(skb); @@ -622,9 +623,9 @@ do_send: rndis_msg->msg_len += rndis_msg_size; packet->total_data_buflen = rndis_msg->msg_len; packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size, - skb, packet); + skb, packet, &pb); - ret = netvsc_send(net_device_ctx->device_ctx, packet, rndis_msg); + ret = netvsc_send(net_device_ctx->device_ctx, packet, rndis_msg, &pb); drop: if (ret == 0) { diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index c8af172faee3..6ff22530488f 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -210,6 +210,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, int ret; struct hv_netvsc_packet *packet; struct hv_page_buffer page_buf[2]; + struct hv_page_buffer *pb = page_buf; /* Setup the packet to send it */ packet = &req->pkt; @@ -217,30 +218,29 @@ static int rndis_filter_send_request(struct rndis_device *dev, packet->is_data_pkt = false; packet->total_data_buflen = req->request_msg.msg_len; packet->page_buf_cnt = 1; - packet->page_buf = page_buf; - packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >> + pb[0].pfn = virt_to_phys(&req->request_msg) >> PAGE_SHIFT; - packet->page_buf[0].len = req->request_msg.msg_len; - packet->page_buf[0].offset = + pb[0].len = req->request_msg.msg_len; + pb[0].offset = (unsigned long)&req->request_msg & (PAGE_SIZE - 1); /* Add one page_buf when request_msg crossing page boundary */ - if (packet->page_buf[0].offset + packet->page_buf[0].len > PAGE_SIZE) { + if (pb[0].offset + pb[0].len > PAGE_SIZE) { packet->page_buf_cnt++; - packet->page_buf[0].len = PAGE_SIZE - - packet->page_buf[0].offset; - packet->page_buf[1].pfn = virt_to_phys((void *)&req->request_msg - + packet->page_buf[0].len) >> PAGE_SHIFT; - packet->page_buf[1].offset = 0; - packet->page_buf[1].len = req->request_msg.msg_len - - packet->page_buf[0].len; + pb[0].len = PAGE_SIZE - + pb[0].offset; + pb[1].pfn = virt_to_phys((void *)&req->request_msg + + pb[0].len) >> PAGE_SHIFT; + pb[1].offset = 0; + pb[1].len = req->request_msg.msg_len - + pb[0].len; } packet->completion_func = 0; packet->xmit_more = false; - ret = netvsc_send(dev->net_dev->dev, packet, NULL); + ret = netvsc_send(dev->net_dev->dev, packet, NULL, &pb); return ret; } -- cgit v1.2.3-59-g8ed1b From 3a3d9a0a731add5afaafd9c714e7efe11820fe5b Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:14 -0800 Subject: hv_netvsc: Eliminate send_completion_tid from struct hv_netvsc_packet Eliminate send_completion_tid from struct hv_netvsc_packet. Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 8 ++------ drivers/net/hyperv/netvsc.c | 28 ++++++++++++++-------------- drivers/net/hyperv/netvsc_drv.c | 14 ++------------ drivers/net/hyperv/rndis_filter.c | 2 +- 4 files changed, 19 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index c7517b146b4f..eebfbe5f2f3d 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -145,10 +145,6 @@ struct hv_netvsc_packet { u32 send_buf_index; u32 total_data_buflen; - u32 pad1; - - - u64 send_completion_tid; }; struct netvsc_device_info { @@ -187,10 +183,10 @@ int netvsc_device_remove(struct hv_device *device); int netvsc_send(struct hv_device *device, struct hv_netvsc_packet *packet, struct rndis_message *rndis_msg, - struct hv_page_buffer **page_buffer); + struct hv_page_buffer **page_buffer, + struct sk_buff *skb); void netvsc_linkstatus_callback(struct hv_device *device_obj, struct rndis_message *resp); -void netvsc_xmit_completion(void *context); int netvsc_recv_callback(struct hv_device *device_obj, struct hv_netvsc_packet *packet, void **data, diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 18058a593b35..d18e10cceced 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -614,6 +614,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device, struct hv_netvsc_packet *nvsc_packet; struct net_device *ndev; u32 send_index; + struct sk_buff *skb; ndev = net_device->ndev; @@ -639,17 +640,17 @@ static void netvsc_send_completion(struct netvsc_device *net_device, int queue_sends; /* Get the send context */ - nvsc_packet = (struct hv_netvsc_packet *)(unsigned long) - packet->trans_id; + skb = (struct sk_buff *)(unsigned long)packet->trans_id; /* Notify the layer above us */ - if (nvsc_packet) { + if (skb) { + nvsc_packet = (struct hv_netvsc_packet *) skb->cb; send_index = nvsc_packet->send_buf_index; if (send_index != NETVSC_INVALID_INDEX) netvsc_free_send_slot(net_device, send_index); q_idx = nvsc_packet->q_idx; channel = incoming_channel; - netvsc_xmit_completion(nvsc_packet); + dev_kfree_skb_any(skb); } num_outstanding_sends = @@ -744,7 +745,8 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, static inline int netvsc_send_pkt( struct hv_netvsc_packet *packet, struct netvsc_device *net_device, - struct hv_page_buffer **pb) + struct hv_page_buffer **pb, + struct sk_buff *skb) { struct nvsp_message nvmsg; u16 q_idx = packet->q_idx; @@ -772,10 +774,7 @@ static inline int netvsc_send_pkt( nvmsg.msg.v1_msg.send_rndis_pkt.send_buf_section_size = packet->total_data_buflen; - if (packet->completion_func) - req_id = (ulong)packet; - else - req_id = 0; + req_id = (ulong)skb; if (out_channel->rescind) return -ENODEV; @@ -841,7 +840,8 @@ static inline int netvsc_send_pkt( int netvsc_send(struct hv_device *device, struct hv_netvsc_packet *packet, struct rndis_message *rndis_msg, - struct hv_page_buffer **pb) + struct hv_page_buffer **pb, + struct sk_buff *skb) { struct netvsc_device *net_device; int ret = 0, m_ret = 0; @@ -907,7 +907,7 @@ int netvsc_send(struct hv_device *device, } if (msdp->pkt) - netvsc_xmit_completion(msdp->pkt); + dev_kfree_skb_any(skb); if (packet->xmit_more && !packet->cp_partial) { msdp->pkt = packet; @@ -925,17 +925,17 @@ int netvsc_send(struct hv_device *device, } if (msd_send) { - m_ret = netvsc_send_pkt(msd_send, net_device, pb); + m_ret = netvsc_send_pkt(msd_send, net_device, pb, skb); if (m_ret != 0) { netvsc_free_send_slot(net_device, msd_send->send_buf_index); - netvsc_xmit_completion(msd_send); + dev_kfree_skb_any(skb); } } if (cur_send) - ret = netvsc_send_pkt(cur_send, net_device, pb); + ret = netvsc_send_pkt(cur_send, net_device, pb, skb); if (ret != 0 && section_index != NETVSC_INVALID_INDEX) netvsc_free_send_slot(net_device, section_index); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index eca669257360..c1078a62a231 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -279,16 +279,6 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, return q_idx; } -void netvsc_xmit_completion(void *context) -{ - struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; - struct sk_buff *skb = (struct sk_buff *) - (unsigned long)packet->send_completion_tid; - - if (skb) - dev_kfree_skb_any(skb); -} - static u32 fill_pg_buf(struct page *page, u32 offset, u32 len, struct hv_page_buffer *pb) { @@ -497,7 +487,6 @@ check_size: /* Set the completion routine */ packet->completion_func = 1; - packet->send_completion_tid = (unsigned long)skb; isvlan = packet->vlan_tci & VLAN_TAG_PRESENT; @@ -625,7 +614,8 @@ do_send: packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size, skb, packet, &pb); - ret = netvsc_send(net_device_ctx->device_ctx, packet, rndis_msg, &pb); + ret = netvsc_send(net_device_ctx->device_ctx, packet, + rndis_msg, &pb, skb); drop: if (ret == 0) { diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 6ff22530488f..53139f7efcb9 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -240,7 +240,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, packet->completion_func = 0; packet->xmit_more = false; - ret = netvsc_send(dev->net_dev->dev, packet, NULL, &pb); + ret = netvsc_send(dev->net_dev->dev, packet, NULL, &pb, NULL); return ret; } -- cgit v1.2.3-59-g8ed1b From 694a9fb0263dddfb07bc490a02e59d6962602fe8 Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:15 -0800 Subject: hv_netvsc: Eliminate is_data_pkt from struct hv_netvsc_packet Eliminate is_data_pkt from struct hv_netvsc_packet. Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 1 - drivers/net/hyperv/netvsc.c | 14 ++++++++------ drivers/net/hyperv/netvsc_drv.c | 1 - drivers/net/hyperv/rndis_filter.c | 1 - 4 files changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index eebfbe5f2f3d..2f69e31bc07d 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -131,7 +131,6 @@ struct ndis_tcp_ip_checksum_info; struct hv_netvsc_packet { /* Bookkeeping stuff */ u8 status; - u8 is_data_pkt; u8 xmit_more; /* from skb */ u8 cp_partial; /* partial copy into send buffer */ diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index d18e10cceced..11b009ebb33f 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -704,12 +704,14 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, u32 pend_size, struct hv_netvsc_packet *packet, struct rndis_message *rndis_msg, - struct hv_page_buffer **pb) + struct hv_page_buffer **pb, + struct sk_buff *skb) { char *start = net_device->send_buf; char *dest = start + (section_index * net_device->send_section_size) + pend_size; int i; + bool is_data_pkt = (skb != NULL) ? true : false; u32 msg_size = 0; u32 padding = 0; u32 remain = packet->total_data_buflen % net_device->pkt_align; @@ -717,7 +719,7 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, packet->page_buf_cnt; /* Add padding */ - if (packet->is_data_pkt && packet->xmit_more && remain && + if (is_data_pkt && packet->xmit_more && remain && !packet->cp_partial) { padding = net_device->pkt_align - remain; rndis_msg->msg_len += padding; @@ -758,7 +760,7 @@ static inline int netvsc_send_pkt( u32 ring_avail = hv_ringbuf_avail_percent(&out_channel->outbound); nvmsg.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT; - if (packet->is_data_pkt) { + if (skb != NULL) { /* 0 is RMC_DATA; */ nvmsg.msg.v1_msg.send_rndis_pkt.channel_type = 0; } else { @@ -868,7 +870,7 @@ int netvsc_send(struct hv_device *device, if (msdp->pkt) msd_len = msdp->pkt->total_data_buflen; - try_batch = packet->is_data_pkt && msd_len > 0 && msdp->count < + try_batch = (skb != NULL) && msd_len > 0 && msdp->count < net_device->max_pkt; if (try_batch && msd_len + pktlen + net_device->pkt_align < @@ -880,7 +882,7 @@ int netvsc_send(struct hv_device *device, section_index = msdp->pkt->send_buf_index; packet->cp_partial = true; - } else if (packet->is_data_pkt && pktlen + net_device->pkt_align < + } else if ((skb != NULL) && pktlen + net_device->pkt_align < net_device->send_section_size) { section_index = netvsc_get_next_send_section(net_device); if (section_index != NETVSC_INVALID_INDEX) { @@ -894,7 +896,7 @@ int netvsc_send(struct hv_device *device, if (section_index != NETVSC_INVALID_INDEX) { netvsc_copy_to_send_buf(net_device, section_index, msd_len, - packet, rndis_msg, pb); + packet, rndis_msg, pb, skb); packet->send_buf_index = section_index; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index c1078a62a231..f44e637720ba 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -478,7 +478,6 @@ check_size: packet->q_idx = skb_get_queue_mapping(skb); - packet->is_data_pkt = true; packet->total_data_buflen = skb->len; rndis_msg = (struct rndis_message *)skb->head; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 53139f7efcb9..0b986743abe6 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -215,7 +215,6 @@ static int rndis_filter_send_request(struct rndis_device *dev, /* Setup the packet to send it */ packet = &req->pkt; - packet->is_data_pkt = false; packet->total_data_buflen = req->request_msg.msg_len; packet->page_buf_cnt = 1; -- cgit v1.2.3-59-g8ed1b From a429bda374a640b05a8f949fed080af7512be49a Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:16 -0800 Subject: hv_netvsc: Eliminate completion_func from struct hv_netvsc_packet Eliminate completion_func from struct hv_netvsc_packet. Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 1 - drivers/net/hyperv/netvsc_drv.c | 3 --- drivers/net/hyperv/rndis_filter.c | 1 - 3 files changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 2f69e31bc07d..a613e723d8f7 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -137,7 +137,6 @@ struct hv_netvsc_packet { u8 rmsg_size; /* RNDIS header and PPI size */ u8 rmsg_pgcnt; /* page count of RNDIS header and PPI */ u8 page_buf_cnt; - u8 completion_func; u16 vlan_tci; u16 q_idx; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index f44e637720ba..7f4f6c318071 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -484,9 +484,6 @@ check_size: memset(rndis_msg, 0, RNDIS_AND_PPI_SIZE); - /* Set the completion routine */ - packet->completion_func = 1; - isvlan = packet->vlan_tci & VLAN_TAG_PRESENT; /* Add the rndis header */ diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 0b986743abe6..6ba5adfe93b6 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -236,7 +236,6 @@ static int rndis_filter_send_request(struct rndis_device *dev, pb[0].len; } - packet->completion_func = 0; packet->xmit_more = false; ret = netvsc_send(dev->net_dev->dev, packet, NULL, &pb, NULL); -- cgit v1.2.3-59-g8ed1b From bde79be529c43b5a5a877b3e0b93607d22a8b01e Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:17 -0800 Subject: hv_netvsc: Eliminate xmit_more from struct hv_netvsc_packet Eliminate xmit_more from struct hv_netvsc_packet. Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 1 - drivers/net/hyperv/netvsc.c | 13 ++++++++----- drivers/net/hyperv/netvsc_drv.c | 1 - drivers/net/hyperv/rndis_filter.c | 2 -- 4 files changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index a613e723d8f7..22ef86828bd0 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -131,7 +131,6 @@ struct ndis_tcp_ip_checksum_info; struct hv_netvsc_packet { /* Bookkeeping stuff */ u8 status; - u8 xmit_more; /* from skb */ u8 cp_partial; /* partial copy into send buffer */ u8 rmsg_size; /* RNDIS header and PPI size */ diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 11b009ebb33f..cd5b65e869ca 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -712,6 +712,7 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, + pend_size; int i; bool is_data_pkt = (skb != NULL) ? true : false; + bool xmit_more = (skb != NULL) ? skb->xmit_more : false; u32 msg_size = 0; u32 padding = 0; u32 remain = packet->total_data_buflen % net_device->pkt_align; @@ -719,7 +720,7 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, packet->page_buf_cnt; /* Add padding */ - if (is_data_pkt && packet->xmit_more && remain && + if (is_data_pkt && xmit_more && remain && !packet->cp_partial) { padding = net_device->pkt_align - remain; rndis_msg->msg_len += padding; @@ -758,6 +759,7 @@ static inline int netvsc_send_pkt( int ret; struct hv_page_buffer *pgbuf; u32 ring_avail = hv_ringbuf_avail_percent(&out_channel->outbound); + bool xmit_more = (skb != NULL) ? skb->xmit_more : false; nvmsg.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT; if (skb != NULL) { @@ -789,7 +791,7 @@ static inline int netvsc_send_pkt( * unnecessarily. */ if (ring_avail < (RING_AVAIL_PERCENT_LOWATER + 1)) - packet->xmit_more = false; + xmit_more = false; if (packet->page_buf_cnt) { pgbuf = packet->cp_partial ? (*pb) + @@ -801,14 +803,14 @@ static inline int netvsc_send_pkt( sizeof(struct nvsp_message), req_id, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED, - !packet->xmit_more); + !xmit_more); } else { ret = vmbus_sendpacket_ctl(out_channel, &nvmsg, sizeof(struct nvsp_message), req_id, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED, - !packet->xmit_more); + !xmit_more); } if (ret == 0) { @@ -854,6 +856,7 @@ int netvsc_send(struct hv_device *device, struct multi_send_data *msdp; struct hv_netvsc_packet *msd_send = NULL, *cur_send = NULL; bool try_batch; + bool xmit_more = (skb != NULL) ? skb->xmit_more : false; net_device = get_outbound_net_device(device); if (!net_device) @@ -911,7 +914,7 @@ int netvsc_send(struct hv_device *device, if (msdp->pkt) dev_kfree_skb_any(skb); - if (packet->xmit_more && !packet->cp_partial) { + if (xmit_more && !packet->cp_partial) { msdp->pkt = packet; msdp->count++; } else { diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 7f4f6c318071..d97eeb9e144b 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -472,7 +472,6 @@ check_size: packet = (struct hv_netvsc_packet *)skb->cb; packet->status = 0; - packet->xmit_more = skb->xmit_more; packet->vlan_tci = skb->vlan_tci; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 6ba5adfe93b6..3c06aa75ce11 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -236,8 +236,6 @@ static int rndis_filter_send_request(struct rndis_device *dev, pb[0].len; } - packet->xmit_more = false; - ret = netvsc_send(dev->net_dev->dev, packet, NULL, &pb, NULL); return ret; } -- cgit v1.2.3-59-g8ed1b From 10082f98878a9dff1563745f9f1dd9d1ff142700 Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:18 -0800 Subject: hv_netvsc: Eliminate status from struct hv_netvsc_packet Eliminate status from struct hv_netvsc_packet. Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 1 - drivers/net/hyperv/netvsc.c | 6 ++---- drivers/net/hyperv/netvsc_drv.c | 8 ++------ drivers/net/hyperv/rndis_filter.c | 20 +++++++++----------- 4 files changed, 13 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 22ef86828bd0..c4397f1ed43b 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -130,7 +130,6 @@ struct ndis_tcp_ip_checksum_info; */ struct hv_netvsc_packet { /* Bookkeeping stuff */ - u8 status; u8 cp_partial; /* partial copy into send buffer */ u8 rmsg_size; /* RNDIS header and PPI size */ diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index cd5b65e869ca..02bab9a7c9ff 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -1045,17 +1045,15 @@ static void netvsc_receive(struct netvsc_device *net_device, /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ for (i = 0; i < count; i++) { /* Initialize the netvsc packet */ - netvsc_packet->status = NVSP_STAT_SUCCESS; data = (void *)((unsigned long)net_device-> recv_buf + vmxferpage_packet->ranges[i].byte_offset); netvsc_packet->total_data_buflen = vmxferpage_packet->ranges[i].byte_count; /* Pass it to the upper layer */ - rndis_filter_receive(device, netvsc_packet, &data, channel); + status = rndis_filter_receive(device, netvsc_packet, &data, + channel); - if (netvsc_packet->status != NVSP_STAT_SUCCESS) - status = NVSP_STAT_FAIL; } netvsc_send_recv_completion(device, channel, net_device, diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index d97eeb9e144b..a34547497d18 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -471,8 +471,6 @@ check_size: FIELD_SIZEOF(struct sk_buff, cb)); packet = (struct hv_netvsc_packet *)skb->cb; - packet->status = 0; - packet->vlan_tci = skb->vlan_tci; packet->q_idx = skb_get_queue_mapping(skb); @@ -684,8 +682,7 @@ int netvsc_recv_callback(struct hv_device *device_obj, net = ((struct netvsc_device *)hv_get_drvdata(device_obj))->ndev; if (!net || net->reg_state != NETREG_REGISTERED) { - packet->status = NVSP_STAT_FAIL; - return 0; + return NVSP_STAT_FAIL; } net_device_ctx = netdev_priv(net); rx_stats = this_cpu_ptr(net_device_ctx->rx_stats); @@ -694,8 +691,7 @@ int netvsc_recv_callback(struct hv_device *device_obj, skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen); if (unlikely(!skb)) { ++net->stats.rx_dropped; - packet->status = NVSP_STAT_FAIL; - return 0; + return NVSP_STAT_FAIL; } /* diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 3c06aa75ce11..28adf6acf481 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -344,7 +344,7 @@ static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type) return NULL; } -static void rndis_filter_receive_data(struct rndis_device *dev, +static int rndis_filter_receive_data(struct rndis_device *dev, struct rndis_message *msg, struct hv_netvsc_packet *pkt, void **data, @@ -371,7 +371,7 @@ static void rndis_filter_receive_data(struct rndis_device *dev, "overflow detected (got %u, min %u)" "...dropping this message!\n", pkt->total_data_buflen, rndis_pkt->data_len); - return; + return NVSP_STAT_FAIL; } /* @@ -391,7 +391,8 @@ static void rndis_filter_receive_data(struct rndis_device *dev, } csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); - netvsc_recv_callback(dev->net_dev->dev, pkt, data, csum_info, channel); + return netvsc_recv_callback(dev->net_dev->dev, pkt, data, + csum_info, channel); } int rndis_filter_receive(struct hv_device *dev, @@ -406,7 +407,7 @@ int rndis_filter_receive(struct hv_device *dev, int ret = 0; if (!net_dev) { - ret = -EINVAL; + ret = NVSP_STAT_FAIL; goto exit; } @@ -416,7 +417,7 @@ int rndis_filter_receive(struct hv_device *dev, if (!net_dev->extension) { netdev_err(ndev, "got rndis message but no rndis device - " "dropping this message!\n"); - ret = -ENODEV; + ret = NVSP_STAT_FAIL; goto exit; } @@ -424,7 +425,7 @@ int rndis_filter_receive(struct hv_device *dev, if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) { netdev_err(ndev, "got rndis message but rndis device " "uninitialized...dropping this message!\n"); - ret = -ENODEV; + ret = NVSP_STAT_FAIL; goto exit; } @@ -436,8 +437,8 @@ int rndis_filter_receive(struct hv_device *dev, switch (rndis_msg->ndis_msg_type) { case RNDIS_MSG_PACKET: /* data msg */ - rndis_filter_receive_data(rndis_dev, rndis_msg, pkt, - data, channel); + ret = rndis_filter_receive_data(rndis_dev, rndis_msg, pkt, + data, channel); break; case RNDIS_MSG_INIT_C: @@ -460,9 +461,6 @@ int rndis_filter_receive(struct hv_device *dev, } exit: - if (ret != 0) - pkt->status = NVSP_STAT_FAIL; - return ret; } -- cgit v1.2.3-59-g8ed1b From 760d1e36cc16fa6444dd2000ac645455de1ecd51 Mon Sep 17 00:00:00 2001 From: KY Srinivasan Date: Tue, 1 Dec 2015 16:43:19 -0800 Subject: hv_netvsc: Eliminate vlan_tci from struct hv_netvsc_packet Eliminate vlan_tci from struct hv_netvsc_packet. Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 4 ++-- drivers/net/hyperv/netvsc_drv.c | 14 +++++++------- drivers/net/hyperv/rndis_filter.c | 7 +++---- 3 files changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index c4397f1ed43b..f4130af09244 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -136,7 +136,6 @@ struct hv_netvsc_packet { u8 rmsg_pgcnt; /* page count of RNDIS header and PPI */ u8 page_buf_cnt; - u16 vlan_tci; u16 q_idx; u32 send_buf_index; @@ -187,7 +186,8 @@ int netvsc_recv_callback(struct hv_device *device_obj, struct hv_netvsc_packet *packet, void **data, struct ndis_tcp_ip_checksum_info *csum_info, - struct vmbus_channel *channel); + struct vmbus_channel *channel, + u16 vlan_tci); void netvsc_channel_cb(void *context); int rndis_filter_open(struct hv_device *dev); int rndis_filter_close(struct hv_device *dev); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index a34547497d18..1c8db9afdcda 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -471,7 +471,6 @@ check_size: FIELD_SIZEOF(struct sk_buff, cb)); packet = (struct hv_netvsc_packet *)skb->cb; - packet->vlan_tci = skb->vlan_tci; packet->q_idx = skb_get_queue_mapping(skb); @@ -481,7 +480,7 @@ check_size: memset(rndis_msg, 0, RNDIS_AND_PPI_SIZE); - isvlan = packet->vlan_tci & VLAN_TAG_PRESENT; + isvlan = skb->vlan_tci & VLAN_TAG_PRESENT; /* Add the rndis header */ rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET; @@ -509,8 +508,8 @@ check_size: IEEE_8021Q_INFO); vlan = (struct ndis_pkt_8021q_info *)((void *)ppi + ppi->ppi_offset); - vlan->vlanid = packet->vlan_tci & VLAN_VID_MASK; - vlan->pri = (packet->vlan_tci & VLAN_PRIO_MASK) >> + vlan->vlanid = skb->vlan_tci & VLAN_VID_MASK; + vlan->pri = (skb->vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; } @@ -673,7 +672,8 @@ int netvsc_recv_callback(struct hv_device *device_obj, struct hv_netvsc_packet *packet, void **data, struct ndis_tcp_ip_checksum_info *csum_info, - struct vmbus_channel *channel) + struct vmbus_channel *channel, + u16 vlan_tci) { struct net_device *net; struct net_device_context *net_device_ctx; @@ -713,9 +713,9 @@ int netvsc_recv_callback(struct hv_device *device_obj, skb->ip_summed = CHECKSUM_NONE; } - if (packet->vlan_tci & VLAN_TAG_PRESENT) + if (vlan_tci & VLAN_TAG_PRESENT) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), - packet->vlan_tci); + vlan_tci); skb_record_rx_queue(skb, channel-> offermsg.offer.sub_channel_index); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 28adf6acf481..a37bbda37ffa 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -354,6 +354,7 @@ static int rndis_filter_receive_data(struct rndis_device *dev, u32 data_offset; struct ndis_pkt_8021q_info *vlan; struct ndis_tcp_ip_checksum_info *csum_info; + u16 vlan_tci = 0; rndis_pkt = &msg->msg.pkt; @@ -384,15 +385,13 @@ static int rndis_filter_receive_data(struct rndis_device *dev, vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO); if (vlan) { - pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid | + vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid | (vlan->pri << VLAN_PRIO_SHIFT); - } else { - pkt->vlan_tci = 0; } csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); return netvsc_recv_callback(dev->net_dev->dev, pkt, data, - csum_info, channel); + csum_info, channel, vlan_tci); } int rndis_filter_receive(struct hv_device *dev, -- cgit v1.2.3-59-g8ed1b From 6f24e5d599896b5091af72e4b3edfce6307627f7 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 30 Nov 2015 17:12:21 -0500 Subject: sfc: use ALIGN macro for aligning frame sizes Don't open-code it. CC: Solarflare linux maintainers CC: Shradha Shah CC: netdev@vger.kernel.org Signed-off-by: Jarod Wilson Acked-by: Bert Kenward Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/net_driver.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 5c0d0baa185c..38c422321cda 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -1504,8 +1504,9 @@ static inline struct efx_rx_buffer *efx_rx_buffer(struct efx_rx_queue *rx_queue, * same cycle, the XMAC can miss the IPG altogether. We work around * this by adding a further 16 bytes. */ +#define EFX_FRAME_PAD 16 #define EFX_MAX_FRAME_LEN(mtu) \ - ((((mtu) + ETH_HLEN + VLAN_HLEN + 4/* FCS */ + 7) & ~7) + 16) + (ALIGN(((mtu) + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN + EFX_FRAME_PAD), 8)) static inline bool efx_xmit_with_hwtstamp(struct sk_buff *skb) { -- cgit v1.2.3-59-g8ed1b From 1255b7a12ec4b6174a1c84f4aae4370b8ba1b1cf Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Fri, 6 Nov 2015 15:25:59 -0800 Subject: i40evf: increase max number of queues Future devices will allow for more queue pairs, so allocate a netdev that can handle them. While we're at it, get rid of the separate MAX_TX/MAX_RX defines. Since we always get matched queue pairs, having these makes no sense. Change-ID: I0e3556cd9a962506e509eb7c0afa36b329e8cb51 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf.h | 3 +-- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index a6318c421695..be1b72b93888 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -97,8 +97,7 @@ struct i40e_vsi { #define I40E_TX_DESC(R, i) (&(((struct i40e_tx_desc *)((R)->desc))[i])) #define I40E_TX_CTXTDESC(R, i) \ (&(((struct i40e_tx_context_desc *)((R)->desc))[i])) -#define MAX_RX_QUEUES 8 -#define MAX_TX_QUEUES MAX_RX_QUEUES +#define MAX_QUEUES 16 #define I40EVF_HKEY_ARRAY_SIZE ((I40E_VFQF_HKEY_MAX_INDEX + 1) * 4) #define I40EVF_HLUT_ARRAY_SIZE ((I40E_VFQF_HLUT_MAX_INDEX + 1) * 4) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 7ef10424a67f..38f882cede14 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2615,8 +2615,7 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); - netdev = alloc_etherdev_mq(sizeof(struct i40evf_adapter), - MAX_TX_QUEUES); + netdev = alloc_etherdev_mq(sizeof(struct i40evf_adapter), MAX_QUEUES); if (!netdev) { err = -ENOMEM; goto err_alloc_etherdev; -- cgit v1.2.3-59-g8ed1b From e6c4cf6fb9323c0f65b8fef1c0c8c9e15b51007e Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Fri, 6 Nov 2015 15:26:00 -0800 Subject: i40evf: set real num queues Use the helper function to set the real number of RX queues, and also set the real number of TX queues. Change-ID: I67982799de3f248fb4158ccdc9b1a74385f42ddd Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 38f882cede14..ba0360587fea 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1205,7 +1205,8 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter) err = i40evf_acquire_msix_vectors(adapter, v_budget); out: - adapter->netdev->real_num_tx_queues = pairs; + netif_set_real_num_rx_queues(adapter->netdev, pairs); + netif_set_real_num_tx_queues(adapter->netdev, pairs); return err; } -- cgit v1.2.3-59-g8ed1b From 05281eb8e1754688ecf07a63844c56c9f76b38ad Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Fri, 6 Nov 2015 15:26:01 -0800 Subject: i40evf: remove duplicate string We already print the driver info string in probe, so don't print it again in init. No need to repeat. No need to repeat. Change-ID: Ief597997f580a8c54d5950e3a84c29f2075be66b Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index ba0360587fea..82c9adf64d95 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2512,7 +2512,6 @@ static void i40evf_init_task(struct work_struct *work) if (netdev->features & NETIF_F_GRO) dev_info(&pdev->dev, "GRO is enabled\n"); - dev_info(&pdev->dev, "%s\n", i40evf_driver_string); adapter->state = __I40EVF_DOWN; set_bit(__I40E_DOWN, &adapter->vsi.state); i40evf_misc_irq_enable(adapter); -- cgit v1.2.3-59-g8ed1b From 9c6c12595b73e580f103b0812837f015034d460e Mon Sep 17 00:00:00 2001 From: Kiran Patil Date: Fri, 6 Nov 2015 15:26:02 -0800 Subject: i40e: Detection and recovery of TX queue hung logic moved to service_task from tx_timeout This patch contains following changes: - detection and recovery logic (issue SW interrupt) has been moved to service_task from timeout function. - added some more debug info from tx_timeout. Logic to detect and recover TX queue hung is now two step process: - service_task detects TX queue hung and sets a bit(hung_detected) if it was not set. - if bit was set (means this is back-back hung condition detected), issue SW interrupt and clear the bit. - napi_poll clears the bit unconditionally since it cleans TX/RX queues. Change-ID: Ieed03a48927c845a988b3ff375090bf37caeb903 Signed-off-by: Kiran Patil Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 3 +++ drivers/net/ethernet/intel/i40e/i40e_main.c | 36 ++++++++++++++++++++++----- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 2 ++ drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 21 ++++++++++------ drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 15 +++++++++++ 5 files changed, 64 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 0b9537b37412..dde7ae71bfb8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -579,6 +579,9 @@ struct i40e_q_vector { u8 num_ringpairs; /* total number of ring pairs in vector */ +#define I40E_Q_VECTOR_HUNG_DETECT 0 /* Bit Index for hung detection logic */ + unsigned long hung_detected; /* Set/Reset for hung_detection logic */ + cpumask_t affinity_mask; struct rcu_head rcu; /* to avoid race with update stats on free */ char name[I40E_INT_NAME_STR_LEN]; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 9e6268b4295a..e19a5790a3c7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4368,17 +4368,41 @@ static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi) else val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0); + /* Bail out if interrupts are disabled because napi_poll + * execution in-progress or will get scheduled soon. + * napi_poll cleans TX and RX queues and updates 'next_to_clean'. + */ + if (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK)) + return; + head = i40e_get_head(tx_ring); tx_pending = i40e_get_tx_pending(tx_ring); - /* Interrupts are disabled and TX pending is non-zero, - * trigger the SW interrupt (don't wait). Worst case - * there will be one extra interrupt which may result - * into not cleaning any queues because queues are cleaned. + /* HW is done executing descriptors, updated HEAD write back, + * but SW hasn't processed those descriptors. If interrupt is + * not generated from this point ON, it could result into + * dev_watchdog detecting timeout on those netdev_queue, + * hence proactively trigger SW interrupt. */ - if (tx_pending && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) - i40e_force_wb(vsi, tx_ring->q_vector); + if (tx_pending) { + /* NAPI Poll didn't run and clear since it was set */ + if (test_and_clear_bit(I40E_Q_VECTOR_HUNG_DETECT, + &tx_ring->q_vector->hung_detected)) { + netdev_info(vsi->netdev, "VSI_seid %d, Hung TX queue %d, tx_pending: %d, NTC:0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x\n", + vsi->seid, q_idx, tx_pending, + tx_ring->next_to_clean, head, + tx_ring->next_to_use, + readl(tx_ring->tail)); + netdev_info(vsi->netdev, "VSI_seid %d, Issuing force_wb for TX queue %d, Interrupt Reg: 0x%x\n", + vsi->seid, q_idx, val); + i40e_force_wb(vsi, tx_ring->q_vector); + } else { + /* First Chance - detected possible hung */ + set_bit(I40E_Q_VECTOR_HUNG_DETECT, + &tx_ring->q_vector->hung_detected); + } + } } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 7371df7b6fb0..1d7d01c723b9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1891,6 +1891,8 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) return 0; } + /* Clear hung_detected bit */ + clear_bit(I40E_Q_VECTOR_HUNG_DETECT, &q_vector->hung_detected); /* Since the actual Tx work is minimal, we can give the Tx a larger * budget and be more aggressive about cleaning up the Tx descriptors. */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 11561b648417..06b6636552dd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -127,17 +127,24 @@ void i40evf_free_tx_resources(struct i40e_ring *tx_ring) } /** - * i40e_get_head - Retrieve head from head writeback - * @tx_ring: tx ring to fetch head of + * i40evf_get_tx_pending - how many Tx descriptors not processed + * @tx_ring: the ring of descriptors * - * Returns value of Tx ring head based on value stored - * in head write-back location + * Since there is no access to the ring head register + * in XL710, we need to use our local copies **/ -static inline u32 i40e_get_head(struct i40e_ring *tx_ring) +u32 i40evf_get_tx_pending(struct i40e_ring *ring) { - void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count; + u32 head, tail; - return le32_to_cpu(*(volatile __le32 *)head); + head = i40e_get_head(ring); + tail = readl(ring->tail); + + if (head != tail) + return (head < tail) ? + tail - head : (tail + ring->count - head); + + return 0; } #define WB_STRIDE 0x3 diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 929ddd9f8215..e29bb3e86cfd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -324,4 +324,19 @@ int i40evf_setup_rx_descriptors(struct i40e_ring *rx_ring); void i40evf_free_tx_resources(struct i40e_ring *tx_ring); void i40evf_free_rx_resources(struct i40e_ring *rx_ring); int i40evf_napi_poll(struct napi_struct *napi, int budget); +u32 i40evf_get_tx_pending(struct i40e_ring *ring); + +/** + * i40e_get_head - Retrieve head from head writeback + * @tx_ring: Tx ring to fetch head of + * + * Returns value of Tx ring head based on value stored + * in head write-back location + **/ +static inline u32 i40e_get_head(struct i40e_ring *tx_ring) +{ + void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count; + + return le32_to_cpu(*(volatile __le32 *)head); +} #endif /* _I40E_TXRX_H_ */ -- cgit v1.2.3-59-g8ed1b From a42e7a369ea2b73a554a85dea7d6243af51cd4f0 Mon Sep 17 00:00:00 2001 From: Kiran Patil Date: Fri, 6 Nov 2015 15:26:03 -0800 Subject: i40e: Fix memory leaks, sideband filter programming This patch fixes the memory leak which would be seen otherwise when user programs flow-director filter using ethtool (sideband filter programming). When ethtool is used to program flow directory filter, 'raw_buf' gets allocated and it is supposed to be freed as part of queue cleanup. But check of 'tx_buffer->skb' was preventing it from being freed. Change-ID: Ief4f0a1a32a653180498bf6e987c1b4342ab8923 Signed-off-by: Kiran Patil Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 19 ++++++++++++++----- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 10 +++++----- 2 files changed, 19 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 1d7d01c723b9..18a493edbb54 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -235,6 +235,9 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, "Filter deleted for PCTYPE %d loc = %d\n", fd_data->pctype, fd_data->fd_id); } + if (err) + kfree(raw_packet); + return err ? -EOPNOTSUPP : 0; } @@ -312,6 +315,9 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, fd_data->pctype, fd_data->fd_id); } + if (err) + kfree(raw_packet); + return err ? -EOPNOTSUPP : 0; } @@ -387,6 +393,9 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi, } } + if (err) + kfree(raw_packet); + return err ? -EOPNOTSUPP : 0; } @@ -526,11 +535,7 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring, struct i40e_tx_buffer *tx_buffer) { if (tx_buffer->skb) { - if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB) - kfree(tx_buffer->raw_buf); - else - dev_kfree_skb_any(tx_buffer->skb); - + dev_kfree_skb_any(tx_buffer->skb); if (dma_unmap_len(tx_buffer, len)) dma_unmap_single(ring->dev, dma_unmap_addr(tx_buffer, dma), @@ -542,6 +547,10 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring, dma_unmap_len(tx_buffer, len), DMA_TO_DEVICE); } + + if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB) + kfree(tx_buffer->raw_buf); + tx_buffer->next_to_watch = NULL; tx_buffer->skb = NULL; dma_unmap_len_set(tx_buffer, len, 0); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 06b6636552dd..4f1ac86a64ba 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -51,11 +51,7 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring, struct i40e_tx_buffer *tx_buffer) { if (tx_buffer->skb) { - if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB) - kfree(tx_buffer->raw_buf); - else - dev_kfree_skb_any(tx_buffer->skb); - + dev_kfree_skb_any(tx_buffer->skb); if (dma_unmap_len(tx_buffer, len)) dma_unmap_single(ring->dev, dma_unmap_addr(tx_buffer, dma), @@ -67,6 +63,10 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring, dma_unmap_len(tx_buffer, len), DMA_TO_DEVICE); } + + if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB) + kfree(tx_buffer->raw_buf); + tx_buffer->next_to_watch = NULL; tx_buffer->skb = NULL; dma_unmap_len_set(tx_buffer, len, 0); -- cgit v1.2.3-59-g8ed1b From a85088d813a3ac44afa237113539585257127cfa Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Fri, 6 Nov 2015 15:26:04 -0800 Subject: i40evf: don't use atomic allocation These allocations don't need to be at atomic level. GFP_KERNEL is fine and they'll reduce stress on the allocator when the system is starved for memory. Change-ID: I3561d0399a681de0ad25291b6c848b224c1fde12 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 9b5557622657..3c9c008b168b 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -242,7 +242,7 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter) adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES; len = sizeof(struct i40e_virtchnl_vsi_queue_config_info) + (sizeof(struct i40e_virtchnl_queue_pair_info) * pairs); - vqci = kzalloc(len, GFP_ATOMIC); + vqci = kzalloc(len, GFP_KERNEL); if (!vqci) return; @@ -353,7 +353,7 @@ void i40evf_map_queues(struct i40evf_adapter *adapter) len = sizeof(struct i40e_virtchnl_irq_map_info) + (adapter->num_msix_vectors * sizeof(struct i40e_virtchnl_vector_map)); - vimi = kzalloc(len, GFP_ATOMIC); + vimi = kzalloc(len, GFP_KERNEL); if (!vimi) return; @@ -421,7 +421,7 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter) more = true; } - veal = kzalloc(len, GFP_ATOMIC); + veal = kzalloc(len, GFP_KERNEL); if (!veal) return; @@ -483,7 +483,7 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter) (count * sizeof(struct i40e_virtchnl_ether_addr)); more = true; } - veal = kzalloc(len, GFP_ATOMIC); + veal = kzalloc(len, GFP_KERNEL); if (!veal) return; @@ -547,7 +547,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter) (count * sizeof(u16)); more = true; } - vvfl = kzalloc(len, GFP_ATOMIC); + vvfl = kzalloc(len, GFP_KERNEL); if (!vvfl) return; @@ -609,7 +609,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter) (count * sizeof(u16)); more = true; } - vvfl = kzalloc(len, GFP_ATOMIC); + vvfl = kzalloc(len, GFP_KERNEL); if (!vvfl) return; -- cgit v1.2.3-59-g8ed1b From ea02e90b4b49782462d06a425e05c776909fbae4 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 9 Nov 2015 15:35:50 -0800 Subject: i40e: propagate properly i40e_sync_vsi_filters() is the surly teenager of this driver. It says it's going to report errors, but it doesn't actually do that most of the time. And when it does, it leaves a mess. Change this function to have a common exit point so it will properly release the busy lock on the VSI. Propagate errors to the callers. Finally, adjust a few callers to check for and deal with errors from this function. Change-ID: Ic6af4956491e72402ebb3c538a3c31a0ad7f8667 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 113 +++++++++++++-------- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 14 +-- 2 files changed, 76 insertions(+), 51 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index e19a5790a3c7..c5a24fe98dbf 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1553,11 +1553,8 @@ static int i40e_set_mac(struct net_device *netdev, void *p) } ether_addr_copy(netdev->dev_addr, addr->sa_data); - /* schedule our worker thread which will take care of - * applying the new filter changes - */ - i40e_service_event_schedule(vsi->back); - return 0; + + return i40e_sync_vsi_filters(vsi); } /** @@ -1872,8 +1869,9 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) bool add_happened = false; int filter_list_len = 0; u32 changed_flags = 0; + i40e_status aq_ret = 0; bool err_cond = false; - i40e_status ret = 0; + int retval = 0; struct i40e_pf *pf; int num_add = 0; int num_del = 0; @@ -1936,8 +1934,11 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) } spin_unlock_bh(&vsi->mac_filter_list_lock); - if (err_cond) + if (err_cond) { i40e_cleanup_add_list(&tmp_add_list); + retval = -ENOMEM; + goto out; + } } /* Now process 'del_list' outside the lock */ @@ -1955,7 +1956,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) i40e_undo_del_filter_entries(vsi, &tmp_del_list); i40e_undo_add_filter_entries(vsi); spin_unlock_bh(&vsi->mac_filter_list_lock); - return -ENOMEM; + retval = -ENOMEM; + goto out; } list_for_each_entry_safe(f, ftmp, &tmp_del_list, list) { @@ -1973,18 +1975,22 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* flush a full buffer */ if (num_del == filter_list_len) { - ret = i40e_aq_remove_macvlan(&pf->hw, - vsi->seid, del_list, num_del, - NULL); + aq_ret = i40e_aq_remove_macvlan(&pf->hw, + vsi->seid, + del_list, + num_del, + NULL); aq_err = pf->hw.aq.asq_last_status; num_del = 0; memset(del_list, 0, sizeof(*del_list)); - if (ret && aq_err != I40E_AQ_RC_ENOENT) + if (aq_ret && aq_err != I40E_AQ_RC_ENOENT) { + retval = -EIO; dev_err(&pf->pdev->dev, "ignoring delete macvlan error, err %s, aq_err %s while flushing a full buffer\n", - i40e_stat_str(&pf->hw, ret), + i40e_stat_str(&pf->hw, aq_ret), i40e_aq_str(&pf->hw, aq_err)); + } } /* Release memory for MAC filter entries which were * synced up with HW. @@ -1994,15 +2000,16 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) } if (num_del) { - ret = i40e_aq_remove_macvlan(&pf->hw, vsi->seid, - del_list, num_del, NULL); + aq_ret = i40e_aq_remove_macvlan(&pf->hw, vsi->seid, + del_list, num_del, + NULL); aq_err = pf->hw.aq.asq_last_status; num_del = 0; - if (ret && aq_err != I40E_AQ_RC_ENOENT) + if (aq_ret && aq_err != I40E_AQ_RC_ENOENT) dev_info(&pf->pdev->dev, "ignoring delete macvlan error, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), + i40e_stat_str(&pf->hw, aq_ret), i40e_aq_str(&pf->hw, aq_err)); } @@ -2026,7 +2033,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) spin_lock_bh(&vsi->mac_filter_list_lock); i40e_undo_add_filter_entries(vsi); spin_unlock_bh(&vsi->mac_filter_list_lock); - return -ENOMEM; + retval = -ENOMEM; + goto out; } list_for_each_entry_safe(f, ftmp, &tmp_add_list, list) { @@ -2047,13 +2055,13 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* flush a full buffer */ if (num_add == filter_list_len) { - ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid, - add_list, num_add, - NULL); + aq_ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid, + add_list, num_add, + NULL); aq_err = pf->hw.aq.asq_last_status; num_add = 0; - if (ret) + if (aq_ret) break; memset(add_list, 0, sizeof(*add_list)); } @@ -2065,18 +2073,19 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) } if (num_add) { - ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid, - add_list, num_add, NULL); + aq_ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid, + add_list, num_add, NULL); aq_err = pf->hw.aq.asq_last_status; num_add = 0; } kfree(add_list); add_list = NULL; - if (add_happened && ret && aq_err != I40E_AQ_RC_EINVAL) { + if (add_happened && aq_ret && aq_err != I40E_AQ_RC_EINVAL) { + retval = i40e_aq_rc_to_posix(aq_ret, aq_err); dev_info(&pf->pdev->dev, "add filter failed, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), + i40e_stat_str(&pf->hw, aq_ret), i40e_aq_str(&pf->hw, aq_err)); if ((pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOSPC) && !test_bit(__I40E_FILTER_OVERFLOW_PROMISC, @@ -2094,16 +2103,19 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) bool cur_multipromisc; cur_multipromisc = !!(vsi->current_netdev_flags & IFF_ALLMULTI); - ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw, - vsi->seid, - cur_multipromisc, - NULL); - if (ret) + aq_ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw, + vsi->seid, + cur_multipromisc, + NULL); + if (aq_ret) { + retval = i40e_aq_rc_to_posix(aq_ret, + pf->hw.aq.asq_last_status); dev_info(&pf->pdev->dev, "set multi promisc failed, err %s aq_err %s\n", - i40e_stat_str(&pf->hw, ret), + i40e_stat_str(&pf->hw, aq_ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + } } if ((changed_flags & IFF_PROMISC) || promisc_forced_on) { bool cur_promisc; @@ -2122,36 +2134,47 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); } } else { - ret = i40e_aq_set_vsi_unicast_promiscuous( + aq_ret = i40e_aq_set_vsi_unicast_promiscuous( &vsi->back->hw, vsi->seid, cur_promisc, NULL); - if (ret) + if (aq_ret) { + retval = + i40e_aq_rc_to_posix(aq_ret, + pf->hw.aq.asq_last_status); dev_info(&pf->pdev->dev, "set unicast promisc failed, err %d, aq_err %d\n", - ret, pf->hw.aq.asq_last_status); - ret = i40e_aq_set_vsi_multicast_promiscuous( + aq_ret, pf->hw.aq.asq_last_status); + } + aq_ret = i40e_aq_set_vsi_multicast_promiscuous( &vsi->back->hw, vsi->seid, cur_promisc, NULL); - if (ret) + if (aq_ret) { + retval = + i40e_aq_rc_to_posix(aq_ret, + pf->hw.aq.asq_last_status); dev_info(&pf->pdev->dev, "set multicast promisc failed, err %d, aq_err %d\n", - ret, pf->hw.aq.asq_last_status); + aq_ret, pf->hw.aq.asq_last_status); + } } - ret = i40e_aq_set_vsi_broadcast(&vsi->back->hw, - vsi->seid, - cur_promisc, NULL); - if (ret) + aq_ret = i40e_aq_set_vsi_broadcast(&vsi->back->hw, + vsi->seid, + cur_promisc, NULL); + if (aq_ret) { + retval = i40e_aq_rc_to_posix(aq_ret, + pf->hw.aq.asq_last_status); dev_info(&pf->pdev->dev, "set brdcast promisc failed, err %s, aq_err %s\n", - i40e_stat_str(&pf->hw, ret), + i40e_stat_str(&pf->hw, aq_ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); + } } - +out: clear_bit(__I40E_CONFIG_BUSY, &vsi->state); - return 0; + return retval; } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 819803c8e461..30a1d300c060 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1633,9 +1633,10 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) spin_unlock_bh(&vsi->mac_filter_list_lock); /* program the updated filter list */ - if (i40e_sync_vsi_filters(vsi)) - dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters\n", - vf->vf_id); + ret = i40e_sync_vsi_filters(vsi); + if (ret) + dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n", + vf->vf_id, ret); error_param: /* send the response to the VF */ @@ -1687,9 +1688,10 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) spin_unlock_bh(&vsi->mac_filter_list_lock); /* program the updated filter list */ - if (i40e_sync_vsi_filters(vsi)) - dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters\n", - vf->vf_id); + ret = i40e_sync_vsi_filters(vsi); + if (ret) + dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n", + vf->vf_id, ret); error_param: /* send the response to the VF */ -- cgit v1.2.3-59-g8ed1b From d82acb353246d3989377761ca26f5489264a4793 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Fri, 6 Nov 2015 15:26:06 -0800 Subject: i40evf: use correct types Don't use uint32_t type the kernel. Use u32 instead. No functional change. Change-ID: I77bbf3b6464edaef747c7104b43534032a4dba63 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 82c9adf64d95..2577757356d3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -259,7 +259,7 @@ static void i40evf_fire_sw_int(struct i40evf_adapter *adapter, u32 mask) { struct i40e_hw *hw = &adapter->hw; int i; - uint32_t dyn_ctl; + u32 dyn_ctl; if (mask & 1) { dyn_ctl = rd32(hw, I40E_VFINT_DYN_CTL01); -- cgit v1.2.3-59-g8ed1b From 827de39212298048e75309377c8262c4d7f2cb41 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Fri, 6 Nov 2015 15:26:07 -0800 Subject: i40e: use priv flags to control packet split Ethtool priv flags implementation to enable or disable packet split, which is a hardware feature that inspects headers and will put headers in a separate DMA buffer from the payload data. The driver was automatically choosing to enable packet split in some cases and this gives the user the ability to turn it off/on explicitly. to query state: ethtool --show-priv-flags ethx to enable: ethtool --set-priv-flags ethx packet-split on to disable: ethtool --set-priv-flags ethx packet-split off Why would anyone want this? Because some environments benefit from header/data split in the receive buffer, and the driver defaults to one or the other depending on environment/kernel parameters. Why didn't you implement a generic ethtool control for this feature? Because Intel hardware is the only hardware that supports header/data split. Change-ID: I803121e1eecc9ccb2884031fd85dd1110b3af66d Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 27 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index dde7ae71bfb8..4ca85d554b52 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -104,6 +104,7 @@ #define I40E_PRIV_FLAGS_LINKPOLL_FLAG BIT(1) #define I40E_PRIV_FLAGS_FD_ATR BIT(2) #define I40E_PRIV_FLAGS_VEB_STATS BIT(3) +#define I40E_PRIV_FLAGS_PS BIT(4) #define I40E_NVM_VERSION_LO_SHIFT 0 #define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index b52c50924274..29d5833e24a3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -231,6 +231,7 @@ static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = { "LinkPolling", "flow-director-atr", "veb-stats", + "packet-split", }; #define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings) @@ -2709,6 +2710,8 @@ static u32 i40e_get_priv_flags(struct net_device *dev) I40E_PRIV_FLAGS_FD_ATR : 0; ret_flags |= pf->flags & I40E_FLAG_VEB_STATS_ENABLED ? I40E_PRIV_FLAGS_VEB_STATS : 0; + ret_flags |= pf->flags & I40E_FLAG_RX_PS_ENABLED ? + I40E_PRIV_FLAGS_PS : 0; return ret_flags; } @@ -2723,6 +2726,26 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; + bool reset_required = false; + + /* NOTE: MFP is not settable */ + + /* allow the user to control the method of receive + * buffer DMA, whether the packet is split at header + * boundaries into two separate buffers. In some cases + * one routine or the other will perform better. + */ + if ((flags & I40E_PRIV_FLAGS_PS) && + !(pf->flags & I40E_FLAG_RX_PS_ENABLED)) { + pf->flags |= I40E_FLAG_RX_PS_ENABLED; + pf->flags &= ~I40E_FLAG_RX_1BUF_ENABLED; + reset_required = true; + } else if (!(flags & I40E_PRIV_FLAGS_PS) && + (pf->flags & I40E_FLAG_RX_PS_ENABLED)) { + pf->flags &= ~I40E_FLAG_RX_PS_ENABLED; + pf->flags |= I40E_FLAG_RX_1BUF_ENABLED; + reset_required = true; + } if (flags & I40E_PRIV_FLAGS_LINKPOLL_FLAG) pf->flags |= I40E_FLAG_LINK_POLLING_ENABLED; @@ -2745,6 +2768,10 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) else pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED; + /* if needed, issue reset to cause things to take effect */ + if (reset_required) + i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED)); + return 0; } -- cgit v1.2.3-59-g8ed1b From 8fe269991aece394a7ed274f525d96c73f94109a Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Fri, 6 Nov 2015 15:26:08 -0800 Subject: i40e: remove CONFIG_I40E_VXLAN Instead of having our own custom symbol, we can just rely on whether or not the kernel has the feature enabled. In this case use IS_ENABLED(CONFIG_VXLAN) in order to handle built-in or module in the current BKM way. Change-ID: I5890fbb518ff8ed6bb07c3362fb0a8a829f9b241 Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/Kconfig | 11 ----------- drivers/net/ethernet/intel/i40e/i40e.h | 4 +--- drivers/net/ethernet/intel/i40e/i40e_main.c | 12 ++++++------ 3 files changed, 7 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig index 4163b16489b3..061e4e04e561 100644 --- a/drivers/net/ethernet/intel/Kconfig +++ b/drivers/net/ethernet/intel/Kconfig @@ -269,17 +269,6 @@ config I40E To compile this driver as a module, choose M here. The module will be called i40e. -config I40E_VXLAN - bool "Virtual eXtensible Local Area Network Support" - default n - depends on I40E && VXLAN && !(I40E=y && VXLAN=m) - ---help--- - This allows one to create VXLAN virtual interfaces that provide - Layer 2 Networks over Layer 3 Networks. VXLAN is often used - to tunnel virtual network infrastructure in virtualized environments. - Say Y here if you want to use Virtual eXtensible Local Area Network - (VXLAN) in the driver. - config I40E_DCB bool "Data Center Bridging (DCB) Support" default n diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 4ca85d554b52..8ed759e88ab6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -282,7 +282,7 @@ struct i40e_pf { u32 fd_atr_cnt; u32 fd_tcp_rule; -#ifdef CONFIG_I40E_VXLAN +#if IS_ENABLED(CONFIG_VXLAN) __be16 vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS]; u16 pending_vxlan_bitmap; @@ -323,9 +323,7 @@ struct i40e_pf { #define I40E_FLAG_FD_ATR_ENABLED BIT_ULL(22) #define I40E_FLAG_PTP BIT_ULL(25) #define I40E_FLAG_MFP_ENABLED BIT_ULL(26) -#ifdef CONFIG_I40E_VXLAN #define I40E_FLAG_VXLAN_FILTER_SYNC BIT_ULL(27) -#endif #define I40E_FLAG_PORT_ID_VALID BIT_ULL(28) #define I40E_FLAG_DCB_CAPABLE BIT_ULL(29) #define I40E_FLAG_RSS_AQ_CAPABLE BIT_ULL(31) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c5a24fe98dbf..b731a824aba0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -27,7 +27,7 @@ /* Local includes */ #include "i40e.h" #include "i40e_diag.h" -#ifdef CONFIG_I40E_VXLAN +#if IS_ENABLED(CONFIG_VXLAN) #include #endif @@ -5357,7 +5357,7 @@ int i40e_open(struct net_device *netdev) TCP_FLAG_CWR) >> 16); wr32(&pf->hw, I40E_GLLAN_TSOMSK_L, be32_to_cpu(TCP_FLAG_CWR) >> 16); -#ifdef CONFIG_I40E_VXLAN +#if IS_ENABLED(CONFIG_VXLAN) vxlan_get_rx_port(netdev); #endif @@ -7053,7 +7053,7 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf) i40e_flush(hw); } -#ifdef CONFIG_I40E_VXLAN +#if IS_ENABLED(CONFIG_VXLAN) /** * i40e_sync_vxlan_filters_subtask - Sync the VSI filter list with HW * @pf: board private structure @@ -7120,7 +7120,7 @@ static void i40e_service_task(struct work_struct *work) i40e_watchdog_subtask(pf); i40e_fdir_reinit_subtask(pf); i40e_sync_filters_subtask(pf); -#ifdef CONFIG_I40E_VXLAN +#if IS_ENABLED(CONFIG_VXLAN) i40e_sync_vxlan_filters_subtask(pf); #endif i40e_clean_adminq_subtask(pf); @@ -8496,7 +8496,7 @@ static int i40e_set_features(struct net_device *netdev, return 0; } -#ifdef CONFIG_I40E_VXLAN +#if IS_ENABLED(CONFIG_VXLAN) /** * i40e_get_vxlan_port_idx - Lookup a possibly offloaded for Rx UDP port * @pf: board private structure @@ -8816,7 +8816,7 @@ static const struct net_device_ops i40e_netdev_ops = { .ndo_get_vf_config = i40e_ndo_get_vf_config, .ndo_set_vf_link_state = i40e_ndo_set_vf_link_state, .ndo_set_vf_spoofchk = i40e_ndo_set_vf_spoofchk, -#ifdef CONFIG_I40E_VXLAN +#if IS_ENABLED(CONFIG_VXLAN) .ndo_add_vxlan_port = i40e_add_vxlan_port, .ndo_del_vxlan_port = i40e_del_vxlan_port, #endif -- cgit v1.2.3-59-g8ed1b From 95db239f4fc664d9ce4ba9a6a3e87d2a3d856a23 Mon Sep 17 00:00:00 2001 From: Neerav Parikh Date: Fri, 6 Nov 2015 15:26:09 -0800 Subject: i40e: Remove separate functions gathering XOFF Rx stats The separate functions to gather Flow control Rx XOFF stats was to determine if the Tx for a queue was paused due to Link Flow Control(LFC) or Priority Flow Control(PFC). But, with recent change in the i40e driver the logic for checking th Tx hang has been removed and these functions don't do anything meaningful. Hence, there is no need to keep these separate functions to gather Rx XOFF stats for LFC or PFC. This patch removes these functions and moves the stat collection for XOFF Rx to the i40e_update_pf_stats() that collects all the PF stats. Change-ID: Iec1452dac3a6766f0d968e754cb407530d7c60cd Signed-off-by: Neerav Parikh Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 77 +++-------------------------- 1 file changed, 7 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index b731a824aba0..1c5c0fdb998d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -791,75 +791,6 @@ static void i40e_update_fcoe_stats(struct i40e_vsi *vsi) } #endif -/** - * i40e_update_link_xoff_rx - Update XOFF received in link flow control mode - * @pf: the corresponding PF - * - * Update the Rx XOFF counter (PAUSE frames) in link flow control mode - **/ -static void i40e_update_link_xoff_rx(struct i40e_pf *pf) -{ - struct i40e_hw_port_stats *osd = &pf->stats_offsets; - struct i40e_hw_port_stats *nsd = &pf->stats; - struct i40e_hw *hw = &pf->hw; - u64 xoff = 0; - - if ((hw->fc.current_mode != I40E_FC_FULL) && - (hw->fc.current_mode != I40E_FC_RX_PAUSE)) - return; - - xoff = nsd->link_xoff_rx; - i40e_stat_update32(hw, I40E_GLPRT_LXOFFRXC(hw->port), - pf->stat_offsets_loaded, - &osd->link_xoff_rx, &nsd->link_xoff_rx); - - /* No new LFC xoff rx */ - if (!(nsd->link_xoff_rx - xoff)) - return; - -} - -/** - * i40e_update_prio_xoff_rx - Update XOFF received in PFC mode - * @pf: the corresponding PF - * - * Update the Rx XOFF counter (PAUSE frames) in PFC mode - **/ -static void i40e_update_prio_xoff_rx(struct i40e_pf *pf) -{ - struct i40e_hw_port_stats *osd = &pf->stats_offsets; - struct i40e_hw_port_stats *nsd = &pf->stats; - bool xoff[I40E_MAX_TRAFFIC_CLASS] = {false}; - struct i40e_dcbx_config *dcb_cfg; - struct i40e_hw *hw = &pf->hw; - u16 i; - u8 tc; - - dcb_cfg = &hw->local_dcbx_config; - - /* Collect Link XOFF stats when PFC is disabled */ - if (!dcb_cfg->pfc.pfcenable) { - i40e_update_link_xoff_rx(pf); - return; - } - - for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { - u64 prio_xoff = nsd->priority_xoff_rx[i]; - - i40e_stat_update32(hw, I40E_GLPRT_PXOFFRXC(hw->port, i), - pf->stat_offsets_loaded, - &osd->priority_xoff_rx[i], - &nsd->priority_xoff_rx[i]); - - /* No new PFC xoff rx */ - if (!(nsd->priority_xoff_rx[i] - prio_xoff)) - continue; - /* Get the TC for given priority */ - tc = dcb_cfg->etscfg.prioritytable[i]; - xoff[tc] = true; - } -} - /** * i40e_update_vsi_stats - Update the vsi statistics counters. * @vsi: the VSI to be updated @@ -1054,12 +985,18 @@ static void i40e_update_pf_stats(struct i40e_pf *pf) i40e_stat_update32(hw, I40E_GLPRT_LXONTXC(hw->port), pf->stat_offsets_loaded, &osd->link_xon_tx, &nsd->link_xon_tx); - i40e_update_prio_xoff_rx(pf); /* handles I40E_GLPRT_LXOFFRXC */ + i40e_stat_update32(hw, I40E_GLPRT_LXOFFRXC(hw->port), + pf->stat_offsets_loaded, + &osd->link_xoff_rx, &nsd->link_xoff_rx); i40e_stat_update32(hw, I40E_GLPRT_LXOFFTXC(hw->port), pf->stat_offsets_loaded, &osd->link_xoff_tx, &nsd->link_xoff_tx); for (i = 0; i < 8; i++) { + i40e_stat_update32(hw, I40E_GLPRT_PXOFFRXC(hw->port, i), + pf->stat_offsets_loaded, + &osd->priority_xoff_rx[i], + &nsd->priority_xoff_rx[i]); i40e_stat_update32(hw, I40E_GLPRT_PXONRXC(hw->port, i), pf->stat_offsets_loaded, &osd->priority_xon_rx[i], -- cgit v1.2.3-59-g8ed1b From b82bc49ede1f012d37ba8777e8232cd7c01365cd Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Fri, 6 Nov 2015 15:26:10 -0800 Subject: i40e: fix whitespace Operators should have spaces around them. Change-ID: I64735e9aa8618b9a5059a87ace1c999d6d3bfcfb Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 30a1d300c060..b3bd81c3e1ce 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -290,8 +290,8 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_id, next_q = find_first_bit(&linklistmap, (I40E_MAX_VSI_QP * I40E_VIRTCHNL_SUPPORTED_QTYPES)); - vsi_queue_id = next_q/I40E_VIRTCHNL_SUPPORTED_QTYPES; - qtype = next_q%I40E_VIRTCHNL_SUPPORTED_QTYPES; + vsi_queue_id = next_q / I40E_VIRTCHNL_SUPPORTED_QTYPES; + qtype = next_q % I40E_VIRTCHNL_SUPPORTED_QTYPES; pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id); reg = ((qtype << I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_SHIFT) | pf_queue_id); -- cgit v1.2.3-59-g8ed1b From 44cdb791ae7bc40e0331b78a73992be44079a3ec Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Fri, 6 Nov 2015 15:26:11 -0800 Subject: i40e/i40evf: use logical operator We shouldn't be using a bitwise operator here; it's not a bitwise operation. Use a logical operator instead. Why doesn't c have a logical-or-and-assign operator? Change-ID: Id84f3ca884910bed7073c84b1e16a102e958d0de Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 2 +- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 18a493edbb54..be129d35ae14 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1907,7 +1907,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) */ i40e_for_each_ring(ring, q_vector->tx) { clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit); - arm_wb |= ring->arm_wb; + arm_wb = arm_wb || ring->arm_wb; ring->arm_wb = false; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 4f1ac86a64ba..b53333a4e10b 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1331,7 +1331,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) */ i40e_for_each_ring(ring, q_vector->tx) { clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit); - arm_wb |= ring->arm_wb; + arm_wb = arm_wb || ring->arm_wb; ring->arm_wb = false; } -- cgit v1.2.3-59-g8ed1b From 1e590660cb7ac675c735dd74d99dbca2103abf41 Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Fri, 6 Nov 2015 15:26:12 -0800 Subject: i40e/i40evf: Bump version to 1.4.7 for i40e and 1.4.3 for i40evf Bump. Change-ID: Id8c83c64c973349a722bab40d285ad8ded8c28f7 Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1c5c0fdb998d..b447af6973bb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -39,7 +39,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 4 -#define DRV_VERSION_BUILD 4 +#define DRV_VERSION_BUILD 7 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 2577757356d3..b4c632f417f6 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -34,7 +34,7 @@ char i40evf_driver_name[] = "i40evf"; static const char i40evf_driver_string[] = "Intel(R) XL710/X710 Virtual Function Network Driver"; -#define DRV_VERSION "1.4.1" +#define DRV_VERSION "1.4.3" const char i40evf_driver_version[] = DRV_VERSION; static const char i40evf_copyright[] = "Copyright (c) 2013 - 2015 Intel Corporation."; -- cgit v1.2.3-59-g8ed1b From 4eeb1fff27f4578386955b0e88f4cffc3814df24 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Wed, 18 Nov 2015 17:35:42 -0800 Subject: i40e: trivial fixes 1) remove duplicate include of tcp.h 2) put an ampersand at the end of a line instead of the beginning 3) remove a useless dev_info 4) match declaration of function to the implementation 5) repair incorrect comment 6) correct whitespace 7) remove unused define Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 7 +++---- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 6 +----- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 15 ++++----------- 3 files changed, 8 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 8ed759e88ab6..23b4580616b7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -608,8 +607,8 @@ static inline char *i40e_nvm_version_str(struct i40e_hw *hw) full_ver = hw->nvm.oem_ver; ver = (u8)(full_ver >> I40E_OEM_VER_SHIFT); - build = (u16)((full_ver >> I40E_OEM_VER_BUILD_SHIFT) - & I40E_OEM_VER_BUILD_MASK); + build = (u16)((full_ver >> I40E_OEM_VER_BUILD_SHIFT) & + I40E_OEM_VER_BUILD_MASK); patch = (u8)(full_ver & I40E_OEM_VER_PATCH_MASK); snprintf(buf, sizeof(buf), @@ -717,7 +716,7 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags, u16 uplink_seid, void i40e_veb_release(struct i40e_veb *veb); int i40e_veb_config_tc(struct i40e_veb *veb, u8 enabled_tc); -i40e_status i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid); +int i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid); void i40e_vsi_remove_pvid(struct i40e_vsi *vsi); void i40e_vsi_reset_stats(struct i40e_vsi *vsi); void i40e_pf_reset_stats(struct i40e_pf *pf); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index be129d35ae14..b0ae3e695783 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -328,7 +328,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, * @fd_data: the flow director data required for the FDir descriptor * @add: true adds a filter, false removes it * - * Always returns -EOPNOTSUPP + * Returns 0 if the filters were successfully added or removed **/ static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi, struct i40e_fdir_filter *fd_data, @@ -515,9 +515,6 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, pf->auto_disable_flags |= I40E_FLAG_FD_SB_ENABLED; } - } else { - dev_info(&pdev->dev, - "FD filter programming failed due to incorrect filter parameters\n"); } } else if (error == BIT(I40E_RX_PROG_STATUS_DESC_NO_FD_ENTRY_SHIFT)) { if (I40E_DEBUG_FD & pf->hw.debug_mask) @@ -1872,7 +1869,6 @@ enable_int: q_vector->itr_countdown--; else q_vector->itr_countdown = ITR_COUNTDOWN_START; - } /** diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index b53333a4e10b..4ca40651a228 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -411,7 +411,7 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) return false; } -/* +/** * i40evf_setup_tx_descriptors - Allocate the Tx descriptors * @tx_ring: the tx ring to set up * @@ -1259,10 +1259,12 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, rx = i40e_set_new_dynamic_itr(&q_vector->rx); rxval = i40e_buildreg_itr(I40E_RX_ITR, q_vector->rx.itr); } + if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) { tx = i40e_set_new_dynamic_itr(&q_vector->tx); txval = i40e_buildreg_itr(I40E_TX_ITR, q_vector->tx.itr); } + if (rx || tx) { /* get the higher of the two ITR adjustments and * use the same value for both ITR registers @@ -1298,7 +1300,6 @@ enable_int: q_vector->itr_countdown--; else q_vector->itr_countdown = ITR_COUNTDOWN_START; - } /** @@ -1552,7 +1553,6 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, *tx_flags |= I40E_TX_FLAGS_IPV6; } - if ((tx_ring->flags & I40E_TXR_FLAGS_OUTER_UDP_CSUM) && (l4_tunnel == I40E_TXD_CTX_UDP_TUNNELING) && (*cd_tunneling & I40E_TXD_CTX_QW0_EXT_IP_MASK)) { @@ -1651,7 +1651,7 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring, context_desc->type_cmd_tso_mss = cpu_to_le64(cd_type_cmd_tso_mss); } - /** +/** * i40e_chk_linearize - Check if there are more than 8 fragments per packet * @skb: send buffer * @tx_flags: collected send information @@ -1847,7 +1847,6 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_bi = &tx_ring->tx_bi[i]; } -#define WB_STRIDE 0x3 /* set next_to_watch value indicating a packet is present */ first->next_to_watch = tx_desc; @@ -1874,12 +1873,6 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, * update tail and set RS bit on every packet. * if xmit_more is false and last_xmit_more was true * update tail and set RS bit. - * else (kernel < 3.18) - * if every packet spanned less than 4 desc - * then set RS bit on 4th packet and update tail - * on every packet - * else - * set RS bit on EOP for every packet and update tail * * Optimization: wmb to be issued only in case of tail update. * Also optimize the Descriptor WB path for RS bit with the same -- cgit v1.2.3-59-g8ed1b From f164b84529e3bf9ae43882fd3ac84bef94d104cf Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Fri, 16 Oct 2015 13:27:49 -0700 Subject: ixgbe: Prevent KR PHY reset in ixgbe_init_phy_ops_x550em This patch removes KR PHY reset from ixgbe_init_phy_ops_x550em, since this function is meant to initialize function pointers for the detected PHY type. Internal PHY reset was moved to ixgbe_setup_internal_phy_t_x550em which will now detect which mode the internal PHY operates in and set it up as required. Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index bf2ae8daf717..a9c73b75a902 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1727,6 +1727,12 @@ static s32 ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw) if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper) return IXGBE_ERR_CONFIG; + if (hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE) { + speed = IXGBE_LINK_SPEED_10GB_FULL | + IXGBE_LINK_SPEED_1GB_FULL; + return ixgbe_setup_kr_speed_x550em(hw, speed); + } + /* If link is not up, then there is no setup necessary so return */ status = ixgbe_ext_phy_t_x550em_get_link(hw, &link_up); if (status) @@ -1931,7 +1937,6 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw) static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) { struct ixgbe_phy_info *phy = &hw->phy; - ixgbe_link_speed speed; s32 ret_val; hw->mac.ops.set_lan_id(hw); @@ -1944,10 +1949,6 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) * to determine internal PHY mode. */ phy->nw_mng_if_sel = IXGBE_READ_REG(hw, IXGBE_NW_MNG_IF_SEL); - if (phy->nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE) { - speed = IXGBE_LINK_SPEED_10GB_FULL | - IXGBE_LINK_SPEED_1GB_FULL; - } } /* Identify the PHY or SFP module */ @@ -1979,14 +1980,8 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) /* If internal link mode is XFI, then setup iXFI internal link, * else setup KR now. */ - if (!(phy->nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) { - phy->ops.setup_internal_link = - ixgbe_setup_internal_phy_t_x550em; - } else { - speed = IXGBE_LINK_SPEED_10GB_FULL | - IXGBE_LINK_SPEED_1GB_FULL; - ret_val = ixgbe_setup_kr_speed_x550em(hw, speed); - } + phy->ops.setup_internal_link = + ixgbe_setup_internal_phy_t_x550em; /* setup SW LPLU only for first revision */ if (!(IXGBE_FUSES0_REV1 & IXGBE_READ_REG(hw, -- cgit v1.2.3-59-g8ed1b From 83a9fb20ecc4bb8b36a610ab833962fed52db64c Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Mon, 19 Oct 2015 09:22:14 -0700 Subject: ixgbe: Add support for newer thermal alarm The newer copper PHY implementation used with newer X550EM_x devices uses a different thermal alarm type than the earlier one. Make changes to support both types. Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 4 ++++ drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 22 +++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 995f03107eac..927349da232a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -1345,7 +1345,10 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_MASK 0xFF01 /* int chip-wide mask */ #define IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_FLAG 0xFC01 /* int chip-wide mask */ #define IXGBE_MDIO_GLOBAL_ALARM_1 0xCC00 /* Global alarm 1 */ +#define IXGBE_MDIO_GLOBAL_ALM_1_DEV_FAULT 0x0010 /* device fault */ #define IXGBE_MDIO_GLOBAL_ALM_1_HI_TMP_FAIL 0x4000 /* high temp failure */ +#define IXGBE_MDIO_GLOBAL_FAULT_MSG 0xC850 /* global fault msg */ +#define IXGBE_MDIO_GLOBAL_FAULT_MSG_HI_TMP 0x8007 /* high temp failure */ #define IXGBE_MDIO_GLOBAL_INT_MASK 0xD400 /* Global int mask */ /* autoneg vendor alarm int enable */ #define IXGBE_MDIO_GLOBAL_AN_VEN_ALM_INT_EN 0x1000 @@ -1353,6 +1356,7 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_MDIO_GLOBAL_VEN_ALM_INT_EN 0x1 /* vendor alarm int enable */ #define IXGBE_MDIO_GLOBAL_STD_ALM2_INT 0x200 /* vendor alarm2 int mask */ #define IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN 0x4000 /* int high temp enable */ +#define IXGBE_MDIO_GLOBAL_INT_DEV_FAULT_EN 0x0010 /*int dev fault enable */ #define IXGBE_MDIO_PMA_PMD_SDA_SCL_ADDR 0xC30A /* PHY_XS SDA/SCL Addr Reg */ #define IXGBE_MDIO_PMA_PMD_SDA_SCL_DATA 0xC30B /* PHY_XS SDA/SCL Data Reg */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index a9c73b75a902..b8ad3f212e72 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1444,7 +1444,7 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc) IXGBE_MDIO_GLOBAL_ALARM_1_INT))) return status; - /* High temperature failure alarm triggered */ + /* Global alarm triggered */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_ALARM_1, IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, ®); @@ -1458,6 +1458,21 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc) ixgbe_set_copper_phy_power(hw, false); return IXGBE_ERR_OVERTEMP; } + if (reg & IXGBE_MDIO_GLOBAL_ALM_1_DEV_FAULT) { + /* device fault alarm triggered */ + status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_FAULT_MSG, + IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, + ®); + if (status) + return status; + + /* if device fault was due to high temp alarm handle and exit */ + if (reg == IXGBE_MDIO_GLOBAL_FAULT_MSG_HI_TMP) { + /* power down the PHY in case the PHY FW didn't */ + ixgbe_set_copper_phy_power(hw, false); + return IXGBE_ERR_OVERTEMP; + } + } /* Vendor alarm 2 triggered */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_CHIP_STD_INT_FLAG, @@ -1511,14 +1526,15 @@ static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw) if (status) return status; - /* Enables high temperature failure alarm */ + /* Enable high temperature failure and global fault alarms */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK, IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, ®); if (status) return status; - reg |= IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN; + reg |= (IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN | + IXGBE_MDIO_GLOBAL_INT_DEV_FAULT_EN); status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK, IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, -- cgit v1.2.3-59-g8ed1b From 780484d853d096b4253b966e1789c4f338dd7301 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Wed, 21 Oct 2015 17:21:10 -0700 Subject: ixgbe: Use private workqueue to avoid certain possible hangs Use a private workqueue to avoid hangs that were otherwise possible when performing stress tests, such as creating and destroying many VFS repeatedly. Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index c95042ee30de..0918e32012c2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -172,6 +172,8 @@ MODULE_DESCRIPTION("Intel(R) 10 Gigabit PCI Express Network Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); +static struct workqueue_struct *ixgbe_wq; + static bool ixgbe_check_cfg_remove(struct ixgbe_hw *hw, struct pci_dev *pdev); static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter, @@ -313,7 +315,7 @@ static void ixgbe_service_event_schedule(struct ixgbe_adapter *adapter) if (!test_bit(__IXGBE_DOWN, &adapter->state) && !test_bit(__IXGBE_REMOVING, &adapter->state) && !test_and_set_bit(__IXGBE_SERVICE_SCHED, &adapter->state)) - schedule_work(&adapter->service_task); + queue_work(ixgbe_wq, &adapter->service_task); } static void ixgbe_remove_adapter(struct ixgbe_hw *hw) @@ -9320,6 +9322,12 @@ static int __init ixgbe_init_module(void) pr_info("%s - version %s\n", ixgbe_driver_string, ixgbe_driver_version); pr_info("%s\n", ixgbe_copyright); + ixgbe_wq = create_singlethread_workqueue(ixgbe_driver_name); + if (!ixgbe_wq) { + pr_err("%s: Failed to create workqueue\n", ixgbe_driver_name); + return -ENOMEM; + } + ixgbe_dbg_init(); ret = pci_register_driver(&ixgbe_driver); @@ -9351,6 +9359,10 @@ static void __exit ixgbe_exit_module(void) pci_unregister_driver(&ixgbe_driver); ixgbe_dbg_exit(); + if (ixgbe_wq) { + destroy_workqueue(ixgbe_wq); + ixgbe_wq = NULL; + } } #ifdef CONFIG_IXGBE_DCA -- cgit v1.2.3-59-g8ed1b From 40a13e2493c9882cb4d09054d81a5063cd1589a2 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Wed, 21 Oct 2015 17:21:15 -0700 Subject: ixgbevf: Use a private workqueue to avoid certain possible hangs Use a private workqueue to avoid hangs that were otherwise possible when performing stress tests, such as creating and destroying many VFS repeatedly. Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index dbbd1be47462..18f1c2232cad 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -59,7 +59,7 @@ static const char ixgbevf_driver_string[] = #define DRV_VERSION "2.12.1-k" const char ixgbevf_driver_version[] = DRV_VERSION; static char ixgbevf_copyright[] = - "Copyright (c) 2009 - 2012 Intel Corporation."; + "Copyright (c) 2009 - 2015 Intel Corporation."; static const struct ixgbevf_info *ixgbevf_info_tbl[] = { [board_82599_vf] = &ixgbevf_82599_vf_info, @@ -96,12 +96,14 @@ static int debug = -1; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); +static struct workqueue_struct *ixgbevf_wq; + static void ixgbevf_service_event_schedule(struct ixgbevf_adapter *adapter) { if (!test_bit(__IXGBEVF_DOWN, &adapter->state) && !test_bit(__IXGBEVF_REMOVING, &adapter->state) && !test_and_set_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state)) - schedule_work(&adapter->service_task); + queue_work(ixgbevf_wq, &adapter->service_task); } static void ixgbevf_service_event_complete(struct ixgbevf_adapter *adapter) @@ -4250,6 +4252,11 @@ static int __init ixgbevf_init_module(void) ixgbevf_driver_version); pr_info("%s\n", ixgbevf_copyright); + ixgbevf_wq = create_singlethread_workqueue(ixgbevf_driver_name); + if (!ixgbevf_wq) { + pr_err("%s: Failed to create workqueue\n", ixgbevf_driver_name); + return -ENOMEM; + } ret = pci_register_driver(&ixgbevf_driver); return ret; @@ -4266,6 +4273,10 @@ module_init(ixgbevf_init_module); static void __exit ixgbevf_exit_module(void) { pci_unregister_driver(&ixgbevf_driver); + if (ixgbevf_wq) { + destroy_workqueue(ixgbevf_wq); + ixgbevf_wq = NULL; + } } #ifdef DEBUG -- cgit v1.2.3-59-g8ed1b From 50985b5f62cc74e9e222f0ddf890e1ba87be371a Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Wed, 21 Oct 2015 17:21:20 -0700 Subject: ixgbevf: Minor cleanups Make some minor cleanups, such as simplifying return paths, deleting unneeded initializations, return values more directly and so forth. Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 36 ++++++++--------------- 1 file changed, 12 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 18f1c2232cad..42b971c0cc87 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1334,7 +1334,6 @@ static int ixgbevf_map_rings_to_vectors(struct ixgbevf_adapter *adapter) int txr_remaining = adapter->num_tx_queues; int i, j; int rqpv, tqpv; - int err = 0; q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS; @@ -1347,7 +1346,7 @@ static int ixgbevf_map_rings_to_vectors(struct ixgbevf_adapter *adapter) for (; txr_idx < txr_remaining; v_start++, txr_idx++) map_vector_to_txq(adapter, v_start, txr_idx); - goto out; + return 0; } /* If we don't have enough vectors for a 1-to-1 @@ -1372,8 +1371,7 @@ static int ixgbevf_map_rings_to_vectors(struct ixgbevf_adapter *adapter) } } -out: - return err; + return 0; } /** @@ -1471,9 +1469,7 @@ static inline void ixgbevf_reset_q_vectors(struct ixgbevf_adapter *adapter) **/ static int ixgbevf_request_irq(struct ixgbevf_adapter *adapter) { - int err = 0; - - err = ixgbevf_request_msix_irqs(adapter); + int err = ixgbevf_request_msix_irqs(adapter); if (err) hw_dbg(&adapter->hw, "request_irq failed, Error %d\n", err); @@ -1832,7 +1828,7 @@ static int ixgbevf_vlan_rx_kill_vid(struct net_device *netdev, { struct ixgbevf_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; - int err = -EOPNOTSUPP; + int err; spin_lock_bh(&adapter->mbx_lock); @@ -2048,7 +2044,7 @@ static void ixgbevf_negotiate_api(struct ixgbevf_adapter *adapter) ixgbe_mbox_api_11, ixgbe_mbox_api_10, ixgbe_mbox_api_unknown }; - int err = 0, idx = 0; + int err, idx = 0; spin_lock_bh(&adapter->mbx_lock); @@ -2421,7 +2417,7 @@ err_allocation: static int ixgbevf_set_interrupt_capability(struct ixgbevf_adapter *adapter) { struct net_device *netdev = adapter->netdev; - int err = 0; + int err; int vector, v_budget; /* It's easy to be greedy for MSI-X vectors, but it really @@ -2439,26 +2435,21 @@ static int ixgbevf_set_interrupt_capability(struct ixgbevf_adapter *adapter) */ adapter->msix_entries = kcalloc(v_budget, sizeof(struct msix_entry), GFP_KERNEL); - if (!adapter->msix_entries) { - err = -ENOMEM; - goto out; - } + if (!adapter->msix_entries) + return -ENOMEM; for (vector = 0; vector < v_budget; vector++) adapter->msix_entries[vector].entry = vector; err = ixgbevf_acquire_msix_vectors(adapter, v_budget); if (err) - goto out; + return err; err = netif_set_real_num_tx_queues(netdev, adapter->num_tx_queues); if (err) - goto out; - - err = netif_set_real_num_rx_queues(netdev, adapter->num_rx_queues); + return err; -out: - return err; + return netif_set_real_num_rx_queues(netdev, adapter->num_rx_queues); } /** @@ -4246,8 +4237,6 @@ static struct pci_driver ixgbevf_driver = { **/ static int __init ixgbevf_init_module(void) { - int ret; - pr_info("%s - version %s\n", ixgbevf_driver_string, ixgbevf_driver_version); @@ -4258,8 +4247,7 @@ static int __init ixgbevf_init_module(void) return -ENOMEM; } - ret = pci_register_driver(&ixgbevf_driver); - return ret; + return pci_register_driver(&ixgbevf_driver); } module_init(ixgbevf_init_module); -- cgit v1.2.3-59-g8ed1b From c9f53e63c2089d8154900ed06da0aa7be9f74201 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 22 Oct 2015 16:26:30 -0700 Subject: ixgbe: Refactor MAC address configuration code In the process of tracking down a memory leak when adding/removing FDB entries I had to go through the MAC address configuration code for ixgbe. In the process of doing so I found a number of issues that impacted readability and performance. This change updates the code in general to clean it up so it becomes clear what each step is doing. From what I can tell there a couple of bugs cleaned up in this code. First is the fact that the MAC addresses were being double counted for the PF. As a result once entries up to 63 had been used you could no longer add additional filters. A simple test case for this: for i in `seq 0 96` do ip link add link ens8 name mv$i type macvlan ip link set dev mv$i up done Test script: ethregs -s 0:8.0 | grep -e "RAH" | grep 8000....$ When things are working correctly RAL/H registers 1 - 97 will be consumed. In the failing case it will stop at 63 and prevent any further filters from being added. Signed-off-by: Alexander Duyck Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 7 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 163 +++++++++++++++----------- 2 files changed, 100 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 1d2174526a4c..9214c9d71718 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -587,9 +587,10 @@ static inline u16 ixgbe_desc_unused(struct ixgbe_ring *ring) struct ixgbe_mac_addr { u8 addr[ETH_ALEN]; - u16 queue; + u16 pool; u16 state; /* bitmask */ }; + #define IXGBE_MAC_STATE_DEFAULT 0x1 #define IXGBE_MAC_STATE_MODIFIED 0x2 #define IXGBE_MAC_STATE_IN_USE 0x4 @@ -883,9 +884,9 @@ int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id, void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter); #endif int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter, - u8 *addr, u16 queue); + const u8 *addr, u16 queue); int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter, - u8 *addr, u16 queue); + const u8 *addr, u16 queue); void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter); netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *, struct ixgbe_adapter *, struct ixgbe_ring *); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 0918e32012c2..29f1a3622415 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -4031,124 +4031,156 @@ static int ixgbe_write_mc_addr_list(struct net_device *netdev) #ifdef CONFIG_PCI_IOV void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter) { + struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0]; struct ixgbe_hw *hw = &adapter->hw; int i; - for (i = 0; i < hw->mac.num_rar_entries; i++) { - if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE) - hw->mac.ops.set_rar(hw, i, adapter->mac_table[i].addr, - adapter->mac_table[i].queue, + + for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) { + mac_table->state &= ~IXGBE_MAC_STATE_MODIFIED; + + if (mac_table->state & IXGBE_MAC_STATE_IN_USE) + hw->mac.ops.set_rar(hw, i, + mac_table->addr, + mac_table->pool, IXGBE_RAH_AV); else hw->mac.ops.clear_rar(hw, i); - - adapter->mac_table[i].state &= ~(IXGBE_MAC_STATE_MODIFIED); } } -#endif +#endif static void ixgbe_sync_mac_table(struct ixgbe_adapter *adapter) { + struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0]; struct ixgbe_hw *hw = &adapter->hw; int i; - for (i = 0; i < hw->mac.num_rar_entries; i++) { - if (adapter->mac_table[i].state & IXGBE_MAC_STATE_MODIFIED) { - if (adapter->mac_table[i].state & - IXGBE_MAC_STATE_IN_USE) - hw->mac.ops.set_rar(hw, i, - adapter->mac_table[i].addr, - adapter->mac_table[i].queue, - IXGBE_RAH_AV); - else - hw->mac.ops.clear_rar(hw, i); - adapter->mac_table[i].state &= - ~(IXGBE_MAC_STATE_MODIFIED); - } + for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) { + if (!(mac_table->state & IXGBE_MAC_STATE_MODIFIED)) + continue; + + mac_table->state &= ~IXGBE_MAC_STATE_MODIFIED; + + if (mac_table->state & IXGBE_MAC_STATE_IN_USE) + hw->mac.ops.set_rar(hw, i, + mac_table->addr, + mac_table->pool, + IXGBE_RAH_AV); + else + hw->mac.ops.clear_rar(hw, i); } } static void ixgbe_flush_sw_mac_table(struct ixgbe_adapter *adapter) { - int i; + struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0]; struct ixgbe_hw *hw = &adapter->hw; + int i; - for (i = 0; i < hw->mac.num_rar_entries; i++) { - adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED; - adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE; - eth_zero_addr(adapter->mac_table[i].addr); - adapter->mac_table[i].queue = 0; + for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) { + mac_table->state |= IXGBE_MAC_STATE_MODIFIED; + mac_table->state &= ~IXGBE_MAC_STATE_IN_USE; } + ixgbe_sync_mac_table(adapter); } -static int ixgbe_available_rars(struct ixgbe_adapter *adapter) +static int ixgbe_available_rars(struct ixgbe_adapter *adapter, u16 pool) { + struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0]; struct ixgbe_hw *hw = &adapter->hw; int i, count = 0; - for (i = 0; i < hw->mac.num_rar_entries; i++) { - if (adapter->mac_table[i].state == 0) - count++; + for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) { + /* do not count default RAR as available */ + if (mac_table->state & IXGBE_MAC_STATE_DEFAULT) + continue; + + /* only count unused and addresses that belong to us */ + if (mac_table->state & IXGBE_MAC_STATE_IN_USE) { + if (mac_table->pool != pool) + continue; + } + + count++; } + return count; } /* this function destroys the first RAR entry */ -static void ixgbe_mac_set_default_filter(struct ixgbe_adapter *adapter, - u8 *addr) +static void ixgbe_mac_set_default_filter(struct ixgbe_adapter *adapter) { + struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0]; struct ixgbe_hw *hw = &adapter->hw; - memcpy(&adapter->mac_table[0].addr, addr, ETH_ALEN); - adapter->mac_table[0].queue = VMDQ_P(0); - adapter->mac_table[0].state = (IXGBE_MAC_STATE_DEFAULT | - IXGBE_MAC_STATE_IN_USE); - hw->mac.ops.set_rar(hw, 0, adapter->mac_table[0].addr, - adapter->mac_table[0].queue, + memcpy(&mac_table->addr, hw->mac.addr, ETH_ALEN); + mac_table->pool = VMDQ_P(0); + + mac_table->state = IXGBE_MAC_STATE_DEFAULT | IXGBE_MAC_STATE_IN_USE; + + hw->mac.ops.set_rar(hw, 0, mac_table->addr, mac_table->pool, IXGBE_RAH_AV); } -int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue) +int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter, + const u8 *addr, u16 pool) { + struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0]; struct ixgbe_hw *hw = &adapter->hw; int i; if (is_zero_ether_addr(addr)) return -EINVAL; - for (i = 0; i < hw->mac.num_rar_entries; i++) { - if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE) + for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) { + if (mac_table->state & IXGBE_MAC_STATE_IN_USE) continue; - adapter->mac_table[i].state |= (IXGBE_MAC_STATE_MODIFIED | - IXGBE_MAC_STATE_IN_USE); - ether_addr_copy(adapter->mac_table[i].addr, addr); - adapter->mac_table[i].queue = queue; + + ether_addr_copy(mac_table->addr, addr); + mac_table->pool = pool; + + mac_table->state |= IXGBE_MAC_STATE_MODIFIED | + IXGBE_MAC_STATE_IN_USE; + ixgbe_sync_mac_table(adapter); + return i; } + return -ENOMEM; } -int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue) +int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter, + const u8 *addr, u16 pool) { - /* search table for addr, if found, set to 0 and sync */ - int i; + struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0]; struct ixgbe_hw *hw = &adapter->hw; + int i; if (is_zero_ether_addr(addr)) return -EINVAL; - for (i = 0; i < hw->mac.num_rar_entries; i++) { - if (ether_addr_equal(addr, adapter->mac_table[i].addr) && - adapter->mac_table[i].queue == queue) { - adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED; - adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE; - eth_zero_addr(adapter->mac_table[i].addr); - adapter->mac_table[i].queue = 0; - ixgbe_sync_mac_table(adapter); - return 0; - } + /* search table for addr, if found clear IN_USE flag and sync */ + for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) { + /* we can only delete an entry if it is in use */ + if (!(mac_table->state & IXGBE_MAC_STATE_IN_USE)) + continue; + /* we only care about entries that belong to the given pool */ + if (mac_table->pool != pool) + continue; + /* we only care about a specific MAC address */ + if (!ether_addr_equal(addr, mac_table->addr)) + continue; + + mac_table->state |= IXGBE_MAC_STATE_MODIFIED; + mac_table->state &= ~IXGBE_MAC_STATE_IN_USE; + + ixgbe_sync_mac_table(adapter); + + return 0; } + return -ENOMEM; } /** @@ -4166,7 +4198,7 @@ static int ixgbe_write_uc_addr_list(struct net_device *netdev, int vfn) int count = 0; /* return ENOMEM indicating insufficient memory for addresses */ - if (netdev_uc_count(netdev) > ixgbe_available_rars(adapter)) + if (netdev_uc_count(netdev) > ixgbe_available_rars(adapter, vfn)) return -ENOMEM; if (!netdev_uc_empty(netdev)) { @@ -5039,7 +5071,6 @@ void ixgbe_reset(struct ixgbe_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; int err; - u8 old_addr[ETH_ALEN]; if (ixgbe_removed(hw->hw_addr)) return; @@ -5076,9 +5107,8 @@ void ixgbe_reset(struct ixgbe_adapter *adapter) clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state); /* do not flush user set addresses */ - memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len); ixgbe_flush_sw_mac_table(adapter); - ixgbe_mac_set_default_filter(adapter, old_addr); + ixgbe_mac_set_default_filter(adapter); /* update SAN MAC vmdq pool selection */ if (hw->mac.san_mac_rar_index) @@ -7656,17 +7686,16 @@ static int ixgbe_set_mac(struct net_device *netdev, void *p) struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; struct sockaddr *addr = p; - int ret; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - ixgbe_del_mac_filter(adapter, hw->mac.addr, VMDQ_P(0)); memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len); - ret = ixgbe_add_mac_filter(adapter, hw->mac.addr, VMDQ_P(0)); - return ret > 0 ? 0 : ret; + ixgbe_mac_set_default_filter(adapter); + + return 0; } static int @@ -8867,7 +8896,7 @@ skip_sriov: goto err_sw_init; } - ixgbe_mac_set_default_filter(adapter, hw->mac.perm_addr); + ixgbe_mac_set_default_filter(adapter); setup_timer(&adapter->service_timer, &ixgbe_service_timer, (unsigned long) adapter); -- cgit v1.2.3-59-g8ed1b From 0f079d22834ac0529413bdee5b5aa52485942162 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 22 Oct 2015 16:26:36 -0700 Subject: ixgbe: Use __dev_uc_sync and __dev_uc_unsync for unicast addresses This change replaces the ixgbe_write_uc_addr_list call in ixgbe_set_rx_mode with a call to __dev_uc_sync instead. This works much better with the MAC addr list code that was already in place and solves an issue in which you couldn't remove an FDB address without having to reset the port. Signed-off-by: Alexander Duyck Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 28 ++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 29f1a3622415..708e2109cdfa 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -4212,6 +4212,25 @@ static int ixgbe_write_uc_addr_list(struct net_device *netdev, int vfn) return count; } +static int ixgbe_uc_sync(struct net_device *netdev, const unsigned char *addr) +{ + struct ixgbe_adapter *adapter = netdev_priv(netdev); + int ret; + + ret = ixgbe_add_mac_filter(adapter, addr, VMDQ_P(0)); + + return min_t(int, ret, 0); +} + +static int ixgbe_uc_unsync(struct net_device *netdev, const unsigned char *addr) +{ + struct ixgbe_adapter *adapter = netdev_priv(netdev); + + ixgbe_del_mac_filter(adapter, addr, VMDQ_P(0)); + + return 0; +} + /** * ixgbe_set_rx_mode - Unicast, Multicast and Promiscuous mode set * @netdev: network interface device structure @@ -4267,8 +4286,7 @@ void ixgbe_set_rx_mode(struct net_device *netdev) * sufficient space to store all the addresses then enable * unicast promiscuous mode */ - count = ixgbe_write_uc_addr_list(netdev, VMDQ_P(0)); - if (count < 0) { + if (__dev_uc_sync(netdev, ixgbe_uc_sync, ixgbe_uc_unsync)) { fctrl |= IXGBE_FCTRL_UPE; vmolr |= IXGBE_VMOLR_ROPE; } @@ -5106,8 +5124,12 @@ void ixgbe_reset(struct ixgbe_adapter *adapter) } clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state); - /* do not flush user set addresses */ + + /* flush entries out of MAC table */ ixgbe_flush_sw_mac_table(adapter); + __dev_uc_unsync(netdev, NULL); + + /* do not flush user set addresses */ ixgbe_mac_set_default_filter(adapter); /* update SAN MAC vmdq pool selection */ -- cgit v1.2.3-59-g8ed1b From 2f9be1665585a3757a00a6d1b8201d0ede937a34 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 22 Oct 2015 16:26:42 -0700 Subject: ixgbe: Allow FDB entries access to more RAR filters This change makes it so that we allow the PF to make use of all free RAR entries for FDB use if needed. Previously the code limited us to 16 unicast entries, however this was shared between MACVLAN which wasn't limited and the FDB code which was. So instead of treating the FDB code as a second class citizen I have updated it so that it has access to just as many entries as the MACVLAN filters. Signed-off-by: Alexander Duyck Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 708e2109cdfa..378c44d0f017 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -8200,7 +8200,10 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], { /* guarantee we can provide a unique filter for the unicast address */ if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) { - if (IXGBE_MAX_PF_MACVLANS <= netdev_uc_count(dev)) + struct ixgbe_adapter *adapter = netdev_priv(dev); + u16 pool = VMDQ_P(0); + + if (netdev_uc_count(dev) >= ixgbe_available_rars(adapter, pool)) return -ENOMEM; } -- cgit v1.2.3-59-g8ed1b From a9763f3cb54c7f1c6a47962c814935654476d09f Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Tue, 27 Oct 2015 09:58:07 -0700 Subject: ixgbe: Update PTP to support X550EM_x devices The X550EM_x devices handle clocking differently, so update the PTP implementation to accommodate them. This involves significant changes to ixgbe's PTP code to accommodate the new range of behaviors including things like non-power-of-2 clock wrapping. Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 38 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 9 +- drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 720 +++++++++++++++++++------- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 8 + 4 files changed, 578 insertions(+), 197 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 9214c9d71718..518f339476f8 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -224,6 +224,8 @@ struct ixgbe_rx_queue_stats { u64 csum_err; }; +#define IXGBE_TS_HDR_LEN 8 + enum ixgbe_ring_state_t { __IXGBE_TX_FDIR_INIT_DONE, __IXGBE_TX_XPS_INIT_DONE, @@ -282,6 +284,8 @@ struct ixgbe_ring { u16 next_to_use; u16 next_to_clean; + unsigned long last_rx_timestamp; + union { u16 next_to_alloc; struct { @@ -640,6 +644,8 @@ struct ixgbe_adapter { #define IXGBE_FLAG_SRIOV_CAPABLE (u32)(1 << 22) #define IXGBE_FLAG_SRIOV_ENABLED (u32)(1 << 23) #define IXGBE_FLAG_VXLAN_OFFLOAD_CAPABLE BIT(24) +#define IXGBE_FLAG_RX_HWTSTAMP_ENABLED BIT(25) +#define IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER BIT(26) u32 flags2; #define IXGBE_FLAG2_RSC_CAPABLE (u32)(1 << 0) @@ -756,9 +762,12 @@ struct ixgbe_adapter { unsigned long last_rx_ptp_check; unsigned long last_rx_timestamp; spinlock_t tmreg_lock; - struct cyclecounter cc; - struct timecounter tc; + struct cyclecounter hw_cc; + struct timecounter hw_tc; u32 base_incval; + u32 tx_hwtstamp_timeouts; + u32 rx_hwtstamp_cleared; + void (*ptp_setup_sdp)(struct ixgbe_adapter *); /* SR-IOV */ DECLARE_BITMAP(active_vfs, IXGBE_MAX_VF_FUNCTIONS); @@ -969,12 +978,33 @@ void ixgbe_ptp_suspend(struct ixgbe_adapter *adapter); void ixgbe_ptp_stop(struct ixgbe_adapter *adapter); void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter); void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter); -void ixgbe_ptp_rx_hwtstamp(struct ixgbe_adapter *adapter, struct sk_buff *skb); +void ixgbe_ptp_rx_pktstamp(struct ixgbe_q_vector *, struct sk_buff *); +void ixgbe_ptp_rx_rgtstamp(struct ixgbe_q_vector *, struct sk_buff *skb); +static inline void ixgbe_ptp_rx_hwtstamp(struct ixgbe_ring *rx_ring, + union ixgbe_adv_rx_desc *rx_desc, + struct sk_buff *skb) +{ + if (unlikely(ixgbe_test_staterr(rx_desc, IXGBE_RXD_STAT_TSIP))) { + ixgbe_ptp_rx_pktstamp(rx_ring->q_vector, skb); + return; + } + + if (unlikely(!ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_TS))) + return; + + ixgbe_ptp_rx_rgtstamp(rx_ring->q_vector, skb); + + /* Update the last_rx_timestamp timer in order to enable watchdog check + * for error case of latched timestamp on a dropped packet. + */ + rx_ring->last_rx_timestamp = jiffies; +} + int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr); int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr); void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter); void ixgbe_ptp_reset(struct ixgbe_adapter *adapter); -void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr); +void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter); #ifdef CONFIG_PCI_IOV void ixgbe_sriov_reinit(struct ixgbe_adapter *adapter); #endif diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 378c44d0f017..61926283259a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1634,6 +1634,7 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring, struct sk_buff *skb) { struct net_device *dev = rx_ring->netdev; + u32 flags = rx_ring->q_vector->adapter->flags; ixgbe_update_rsc_stats(rx_ring, skb); @@ -1641,8 +1642,8 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring, ixgbe_rx_checksum(rx_ring, rx_desc, skb); - if (unlikely(ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_TS))) - ixgbe_ptp_rx_hwtstamp(rx_ring->q_vector->adapter, skb); + if (unlikely(flags & IXGBE_FLAG_RX_HWTSTAMP_ENABLED)) + ixgbe_ptp_rx_hwtstamp(rx_ring, rx_desc, skb); if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) && ixgbe_test_staterr(rx_desc, IXGBE_RXD_STAT_VP)) { @@ -2740,7 +2741,7 @@ static irqreturn_t ixgbe_msix_other(int irq, void *data) ixgbe_check_fan_failure(adapter, eicr); if (unlikely(eicr & IXGBE_EICR_TIMESYNC)) - ixgbe_ptp_check_pps_event(adapter, eicr); + ixgbe_ptp_check_pps_event(adapter); /* re-enable the original interrupt state, no lsc, no queues */ if (!test_bit(__IXGBE_DOWN, &adapter->state)) @@ -2947,7 +2948,7 @@ static irqreturn_t ixgbe_intr(int irq, void *data) ixgbe_check_fan_failure(adapter, eicr); if (unlikely(eicr & IXGBE_EICR_TIMESYNC)) - ixgbe_ptp_check_pps_event(adapter, eicr); + ixgbe_ptp_check_pps_event(adapter); /* would disable interrupts here but EIAM disabled it */ napi_schedule_irqoff(&q_vector->napi); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index e5ba04025e2b..ef1504d41890 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2013 Intel Corporation. + Copyright(c) 1999 - 2015 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -27,6 +27,7 @@ *******************************************************************************/ #include "ixgbe.h" #include +#include /* * The 82599 and the X540 do not have true 64bit nanosecond scale @@ -93,7 +94,6 @@ #define IXGBE_INCVAL_SHIFT_82599 7 #define IXGBE_INCPER_SHIFT_82599 24 -#define IXGBE_MAX_TIMEADJ_VALUE 0x7FFFFFFFFFFFFFFFULL #define IXGBE_OVERFLOW_PERIOD (HZ * 30) #define IXGBE_PTP_TX_TIMEOUT (HZ * 15) @@ -104,8 +104,68 @@ */ #define IXGBE_PTP_PPS_HALF_SECOND 500000000ULL +/* In contrast, the X550 controller has two registers, SYSTIMEH and SYSTIMEL + * which contain measurements of seconds and nanoseconds respectively. This + * matches the standard linux representation of time in the kernel. In addition, + * the X550 also has a SYSTIMER register which represents residue, or + * subnanosecond overflow adjustments. To control clock adjustment, the TIMINCA + * register is used, but it is unlike the X540 and 82599 devices. TIMINCA + * represents units of 2^-32 nanoseconds, and uses 31 bits for this, with the + * high bit representing whether the adjustent is positive or negative. Every + * clock cycle, the X550 will add 12.5 ns + TIMINCA which can result in a range + * of 12 to 13 nanoseconds adjustment. Unlike the 82599 and X540 devices, the + * X550's clock for purposes of SYSTIME generation is constant and not dependent + * on the link speed. + * + * SYSTIMEH SYSTIMEL SYSTIMER + * +--------------+ +--------------+ +-------------+ + * X550 | 32 | | 32 | | 32 | + * *--------------+ +--------------+ +-------------+ + * \____seconds___/ \_nanoseconds_/ \__2^-32 ns__/ + * + * This results in a full 96 bits to represent the clock, with 32 bits for + * seconds, 32 bits for nanoseconds (largest value is 0d999999999 or just under + * 1 second) and an additional 32 bits to measure sub nanosecond adjustments for + * underflow of adjustments. + * + * The 32 bits of seconds for the X550 overflows every + * 2^32 / ( 365.25 * 24 * 60 * 60 ) = ~136 years. + * + * In order to adjust the clock frequency for the X550, the TIMINCA register is + * provided. This register represents a + or minus nearly 0.5 ns adjustment to + * the base frequency. It is measured in 2^-32 ns units, with the high bit being + * the sign bit. This register enables software to calculate frequency + * adjustments and apply them directly to the clock rate. + * + * The math for converting ppb into TIMINCA values is fairly straightforward. + * TIMINCA value = ( Base_Frequency * ppb ) / 1000000000ULL + * + * This assumes that ppb is never high enough to create a value bigger than + * TIMINCA's 31 bits can store. This is ensured by the stack. Calculating this + * value is also simple. + * Max ppb = ( Max Adjustment / Base Frequency ) / 1000000000ULL + * + * For the X550, the Max adjustment is +/- 0.5 ns, and the base frequency is + * 12.5 nanoseconds. This means that the Max ppb is 39999999 + * Note: We subtract one in order to ensure no overflow, because the TIMINCA + * register can only hold slightly under 0.5 nanoseconds. + * + * Because TIMINCA is measured in 2^-32 ns units, we have to convert 12.5 ns + * into 2^-32 units, which is + * + * 12.5 * 2^32 = C80000000 + * + * Some revisions of hardware have a faster base frequency than the registers + * were defined for. To fix this, we use a timecounter structure with the + * proper mult and shift to convert the cycles into nanoseconds of time. + */ +#define IXGBE_X550_BASE_PERIOD 0xC80000000ULL +#define INCVALUE_MASK 0x7FFFFFFF +#define ISGN 0x80000000 +#define MAX_TIMADJ 0x7FFFFFFF + /** - * ixgbe_ptp_setup_sdp + * ixgbe_ptp_setup_sdp_x540 * @hw: the hardware private structure * * this function enables or disables the clock out feature on SDP0 for @@ -116,83 +176,116 @@ * aligns the start of the PPS signal to that value. The shift is * necessary because it can change based on the link speed. */ -static void ixgbe_ptp_setup_sdp(struct ixgbe_adapter *adapter) +static void ixgbe_ptp_setup_sdp_x540(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; - int shift = adapter->cc.shift; + int shift = adapter->hw_cc.shift; u32 esdp, tsauxc, clktiml, clktimh, trgttiml, trgttimh, rem; u64 ns = 0, clock_edge = 0; - if ((adapter->flags2 & IXGBE_FLAG2_PTP_PPS_ENABLED) && - (hw->mac.type == ixgbe_mac_X540)) { + /* disable the pin first */ + IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0); + IXGBE_WRITE_FLUSH(hw); - /* disable the pin first */ - IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0); - IXGBE_WRITE_FLUSH(hw); + if (!(adapter->flags2 & IXGBE_FLAG2_PTP_PPS_ENABLED)) + return; - esdp = IXGBE_READ_REG(hw, IXGBE_ESDP); + esdp = IXGBE_READ_REG(hw, IXGBE_ESDP); - /* - * enable the SDP0 pin as output, and connected to the - * native function for Timesync (ClockOut) - */ - esdp |= (IXGBE_ESDP_SDP0_DIR | - IXGBE_ESDP_SDP0_NATIVE); + /* enable the SDP0 pin as output, and connected to the + * native function for Timesync (ClockOut) + */ + esdp |= IXGBE_ESDP_SDP0_DIR | + IXGBE_ESDP_SDP0_NATIVE; - /* - * enable the Clock Out feature on SDP0, and allow - * interrupts to occur when the pin changes - */ - tsauxc = (IXGBE_TSAUXC_EN_CLK | - IXGBE_TSAUXC_SYNCLK | - IXGBE_TSAUXC_SDP0_INT); + /* enable the Clock Out feature on SDP0, and allow + * interrupts to occur when the pin changes + */ + tsauxc = IXGBE_TSAUXC_EN_CLK | + IXGBE_TSAUXC_SYNCLK | + IXGBE_TSAUXC_SDP0_INT; - /* clock period (or pulse length) */ - clktiml = (u32)(IXGBE_PTP_PPS_HALF_SECOND << shift); - clktimh = (u32)((IXGBE_PTP_PPS_HALF_SECOND << shift) >> 32); + /* clock period (or pulse length) */ + clktiml = (u32)(IXGBE_PTP_PPS_HALF_SECOND << shift); + clktimh = (u32)((IXGBE_PTP_PPS_HALF_SECOND << shift) >> 32); - /* - * Account for the cyclecounter wrap-around value by - * using the converted ns value of the current time to - * check for when the next aligned second would occur. - */ - clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIML); - clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIMH) << 32; - ns = timecounter_cyc2time(&adapter->tc, clock_edge); + /* Account for the cyclecounter wrap-around value by + * using the converted ns value of the current time to + * check for when the next aligned second would occur. + */ + clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIML); + clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIMH) << 32; + ns = timecounter_cyc2time(&adapter->hw_tc, clock_edge); - div_u64_rem(ns, IXGBE_PTP_PPS_HALF_SECOND, &rem); - clock_edge += ((IXGBE_PTP_PPS_HALF_SECOND - (u64)rem) << shift); + div_u64_rem(ns, IXGBE_PTP_PPS_HALF_SECOND, &rem); + clock_edge += ((IXGBE_PTP_PPS_HALF_SECOND - (u64)rem) << shift); - /* specify the initial clock start time */ - trgttiml = (u32)clock_edge; - trgttimh = (u32)(clock_edge >> 32); + /* specify the initial clock start time */ + trgttiml = (u32)clock_edge; + trgttimh = (u32)(clock_edge >> 32); - IXGBE_WRITE_REG(hw, IXGBE_CLKTIML, clktiml); - IXGBE_WRITE_REG(hw, IXGBE_CLKTIMH, clktimh); - IXGBE_WRITE_REG(hw, IXGBE_TRGTTIML0, trgttiml); - IXGBE_WRITE_REG(hw, IXGBE_TRGTTIMH0, trgttimh); + IXGBE_WRITE_REG(hw, IXGBE_CLKTIML, clktiml); + IXGBE_WRITE_REG(hw, IXGBE_CLKTIMH, clktimh); + IXGBE_WRITE_REG(hw, IXGBE_TRGTTIML0, trgttiml); + IXGBE_WRITE_REG(hw, IXGBE_TRGTTIMH0, trgttimh); - IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); - IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc); - } else { - IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0); - } + IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp); + IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc); IXGBE_WRITE_FLUSH(hw); } /** - * ixgbe_ptp_read - read raw cycle counter (to be used by time counter) + * ixgbe_ptp_read_X550 - read cycle counter value + * @hw_cc: cyclecounter structure + * + * This function reads SYSTIME registers. It is called by the cyclecounter + * structure to convert from internal representation into nanoseconds. We need + * this for X550 since some skews do not have expected clock frequency and + * result of SYSTIME is 32bits of "billions of cycles" and 32 bits of + * "cycles", rather than seconds and nanoseconds. + */ +static cycle_t ixgbe_ptp_read_X550(const struct cyclecounter *hw_cc) +{ + struct ixgbe_adapter *adapter = + container_of(hw_cc, struct ixgbe_adapter, hw_cc); + struct ixgbe_hw *hw = &adapter->hw; + struct timespec64 ts; + + /* storage is 32 bits of 'billions of cycles' and 32 bits of 'cycles'. + * Some revisions of hardware run at a higher frequency and so the + * cycles are not guaranteed to be nanoseconds. The timespec64 created + * here is used for its math/conversions but does not necessarily + * represent nominal time. + * + * It should be noted that this cyclecounter will overflow at a + * non-bitmask field since we have to convert our billions of cycles + * into an actual cycles count. This results in some possible weird + * situations at high cycle counter stamps. However given that 32 bits + * of "seconds" is ~138 years this isn't a problem. Even at the + * increased frequency of some revisions, this is still ~103 years. + * Since the SYSTIME values start at 0 and we never write them, it is + * highly unlikely for the cyclecounter to overflow in practice. + */ + IXGBE_READ_REG(hw, IXGBE_SYSTIMR); + ts.tv_nsec = IXGBE_READ_REG(hw, IXGBE_SYSTIML); + ts.tv_sec = IXGBE_READ_REG(hw, IXGBE_SYSTIMH); + + return (u64)timespec64_to_ns(&ts); +} + +/** + * ixgbe_ptp_read_82599 - read raw cycle counter (to be used by time counter) * @cc: the cyclecounter structure * * this function reads the cyclecounter registers and is called by the * cyclecounter structure used to construct a ns counter from the * arbitrary fixed point registers */ -static cycle_t ixgbe_ptp_read(const struct cyclecounter *cc) +static cycle_t ixgbe_ptp_read_82599(const struct cyclecounter *cc) { struct ixgbe_adapter *adapter = - container_of(cc, struct ixgbe_adapter, cc); + container_of(cc, struct ixgbe_adapter, hw_cc); struct ixgbe_hw *hw = &adapter->hw; u64 stamp = 0; @@ -203,20 +296,79 @@ static cycle_t ixgbe_ptp_read(const struct cyclecounter *cc) } /** - * ixgbe_ptp_adjfreq + * ixgbe_ptp_convert_to_hwtstamp - convert register value to hw timestamp + * @adapter: private adapter structure + * @hwtstamp: stack timestamp structure + * @systim: unsigned 64bit system time value + * + * We need to convert the adapter's RX/TXSTMP registers into a hwtstamp value + * which can be used by the stack's ptp functions. + * + * The lock is used to protect consistency of the cyclecounter and the SYSTIME + * registers. However, it does not need to protect against the Rx or Tx + * timestamp registers, as there can't be a new timestamp until the old one is + * unlatched by reading. + * + * In addition to the timestamp in hardware, some controllers need a software + * overflow cyclecounter, and this function takes this into account as well. + **/ +static void ixgbe_ptp_convert_to_hwtstamp(struct ixgbe_adapter *adapter, + struct skb_shared_hwtstamps *hwtstamp, + u64 timestamp) +{ + unsigned long flags; + struct timespec64 systime; + u64 ns; + + memset(hwtstamp, 0, sizeof(*hwtstamp)); + + switch (adapter->hw.mac.type) { + /* X550 and later hardware supposedly represent time using a seconds + * and nanoseconds counter, instead of raw 64bits nanoseconds. We need + * to convert the timestamp into cycles before it can be fed to the + * cyclecounter. We need an actual cyclecounter because some revisions + * of hardware run at a higher frequency and thus the counter does + * not represent seconds/nanoseconds. Instead it can be thought of as + * cycles and billions of cycles. + */ + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + /* Upper 32 bits represent billions of cycles, lower 32 bits + * represent cycles. However, we use timespec64_to_ns for the + * correct math even though the units haven't been corrected + * yet. + */ + systime.tv_sec = timestamp >> 32; + systime.tv_nsec = timestamp & 0xFFFFFFFF; + + timestamp = timespec64_to_ns(&systime); + break; + default: + break; + } + + spin_lock_irqsave(&adapter->tmreg_lock, flags); + ns = timecounter_cyc2time(&adapter->hw_tc, timestamp); + spin_unlock_irqrestore(&adapter->tmreg_lock, flags); + + hwtstamp->hwtstamp = ns_to_ktime(ns); +} + +/** + * ixgbe_ptp_adjfreq_82599 * @ptp: the ptp clock structure * @ppb: parts per billion adjustment from base * * adjust the frequency of the ptp cycle counter by the * indicated ppb from the base frequency. */ -static int ixgbe_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) +static int ixgbe_ptp_adjfreq_82599(struct ptp_clock_info *ptp, s32 ppb) { struct ixgbe_adapter *adapter = container_of(ptp, struct ixgbe_adapter, ptp_caps); struct ixgbe_hw *hw = &adapter->hw; - u64 freq; - u32 diff, incval; + u64 freq, incval; + u32 diff; int neg_adj = 0; if (ppb < 0) { @@ -235,12 +387,16 @@ static int ixgbe_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) switch (hw->mac.type) { case ixgbe_mac_X540: - IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval); + if (incval > 0xFFFFFFFFULL) + e_dev_warn("PTP ppb adjusted SYSTIME rate overflowed!\n"); + IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, (u32)incval); break; case ixgbe_mac_82599EB: + if (incval > 0x00FFFFFFULL) + e_dev_warn("PTP ppb adjusted SYSTIME rate overflowed!\n"); IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, (1 << IXGBE_INCPER_SHIFT_82599) | - incval); + ((u32)incval & 0x00FFFFFFUL)); break; default: break; @@ -249,6 +405,43 @@ static int ixgbe_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) return 0; } +/** + * ixgbe_ptp_adjfreq_X550 + * @ptp: the ptp clock structure + * @ppb: parts per billion adjustment from base + * + * adjust the frequency of the SYSTIME registers by the indicated ppb from base + * frequency + */ +static int ixgbe_ptp_adjfreq_X550(struct ptp_clock_info *ptp, s32 ppb) +{ + struct ixgbe_adapter *adapter = + container_of(ptp, struct ixgbe_adapter, ptp_caps); + struct ixgbe_hw *hw = &adapter->hw; + int neg_adj = 0; + u64 rate = IXGBE_X550_BASE_PERIOD; + u32 inca; + + if (ppb < 0) { + neg_adj = 1; + ppb = -ppb; + } + rate *= ppb; + rate = div_u64(rate, 1000000000ULL); + + /* warn if rate is too large */ + if (rate >= INCVALUE_MASK) + e_dev_warn("PTP ppb adjusted SYSTIME rate overflowed!\n"); + + inca = rate & INCVALUE_MASK; + if (neg_adj) + inca |= ISGN; + + IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, inca); + + return 0; +} + /** * ixgbe_ptp_adjtime * @ptp: the ptp clock structure @@ -263,10 +456,11 @@ static int ixgbe_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) unsigned long flags; spin_lock_irqsave(&adapter->tmreg_lock, flags); - timecounter_adjtime(&adapter->tc, delta); + timecounter_adjtime(&adapter->hw_tc, delta); spin_unlock_irqrestore(&adapter->tmreg_lock, flags); - ixgbe_ptp_setup_sdp(adapter); + if (adapter->ptp_setup_sdp) + adapter->ptp_setup_sdp(adapter); return 0; } @@ -283,11 +477,11 @@ static int ixgbe_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) { struct ixgbe_adapter *adapter = container_of(ptp, struct ixgbe_adapter, ptp_caps); - u64 ns; unsigned long flags; + u64 ns; spin_lock_irqsave(&adapter->tmreg_lock, flags); - ns = timecounter_read(&adapter->tc); + ns = timecounter_read(&adapter->hw_tc); spin_unlock_irqrestore(&adapter->tmreg_lock, flags); *ts = ns_to_timespec64(ns); @@ -308,17 +502,16 @@ static int ixgbe_ptp_settime(struct ptp_clock_info *ptp, { struct ixgbe_adapter *adapter = container_of(ptp, struct ixgbe_adapter, ptp_caps); - u64 ns; unsigned long flags; - - ns = timespec64_to_ns(ts); + u64 ns = timespec64_to_ns(ts); /* reset the timecounter */ spin_lock_irqsave(&adapter->tmreg_lock, flags); - timecounter_init(&adapter->tc, &adapter->cc, ns); + timecounter_init(&adapter->hw_tc, &adapter->hw_cc, ns); spin_unlock_irqrestore(&adapter->tmreg_lock, flags); - ixgbe_ptp_setup_sdp(adapter); + if (adapter->ptp_setup_sdp) + adapter->ptp_setup_sdp(adapter); return 0; } @@ -343,33 +536,26 @@ static int ixgbe_ptp_feature_enable(struct ptp_clock_info *ptp, * event when the clock SDP triggers. Clear mask when PPS is * disabled */ - if (rq->type == PTP_CLK_REQ_PPS) { - switch (adapter->hw.mac.type) { - case ixgbe_mac_X540: - if (on) - adapter->flags2 |= IXGBE_FLAG2_PTP_PPS_ENABLED; - else - adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED; - - ixgbe_ptp_setup_sdp(adapter); - return 0; - default: - break; - } - } + if (rq->type != PTP_CLK_REQ_PPS || !adapter->ptp_setup_sdp) + return -ENOTSUPP; + + if (on) + adapter->flags2 |= IXGBE_FLAG2_PTP_PPS_ENABLED; + else + adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED; - return -ENOTSUPP; + adapter->ptp_setup_sdp(adapter); + return 0; } /** * ixgbe_ptp_check_pps_event * @adapter: the private adapter structure - * @eicr: the interrupt cause register value * * This function is called by the interrupt routine when checking for * interrupts. It will check and handle a pps event. */ -void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr) +void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; struct ptp_clock_event event; @@ -425,7 +611,9 @@ void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; u32 tsyncrxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL); + struct ixgbe_ring *rx_ring; unsigned long rx_event; + int n; /* if we don't have a valid timestamp in the registers, just update the * timeout counter and exit @@ -437,18 +625,42 @@ void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter) /* determine the most recent watchdog or rx_timestamp event */ rx_event = adapter->last_rx_ptp_check; - if (time_after(adapter->last_rx_timestamp, rx_event)) - rx_event = adapter->last_rx_timestamp; + for (n = 0; n < adapter->num_rx_queues; n++) { + rx_ring = adapter->rx_ring[n]; + if (time_after(rx_ring->last_rx_timestamp, rx_event)) + rx_event = rx_ring->last_rx_timestamp; + } /* only need to read the high RXSTMP register to clear the lock */ - if (time_is_before_jiffies(rx_event + 5*HZ)) { + if (time_is_before_jiffies(rx_event + 5 * HZ)) { IXGBE_READ_REG(hw, IXGBE_RXSTMPH); adapter->last_rx_ptp_check = jiffies; + adapter->rx_hwtstamp_cleared++; e_warn(drv, "clearing RX Timestamp hang\n"); } } +/** + * ixgbe_ptp_clear_tx_timestamp - utility function to clear Tx timestamp state + * @adapter: the private adapter structure + * + * This function should be called whenever the state related to a Tx timestamp + * needs to be cleared. This helps ensure that all related bits are reset for + * the next Tx timestamp event. + */ +static void ixgbe_ptp_clear_tx_timestamp(struct ixgbe_adapter *adapter) +{ + struct ixgbe_hw *hw = &adapter->hw; + + IXGBE_READ_REG(hw, IXGBE_TXSTMPH); + if (adapter->ptp_tx_skb) { + dev_kfree_skb_any(adapter->ptp_tx_skb); + adapter->ptp_tx_skb = NULL; + } + clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state); +} + /** * ixgbe_ptp_tx_hwtstamp - utility function which checks for TX time stamp * @adapter: the private adapter struct @@ -461,23 +673,15 @@ static void ixgbe_ptp_tx_hwtstamp(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; struct skb_shared_hwtstamps shhwtstamps; - u64 regval = 0, ns; - unsigned long flags; + u64 regval = 0; regval |= (u64)IXGBE_READ_REG(hw, IXGBE_TXSTMPL); regval |= (u64)IXGBE_READ_REG(hw, IXGBE_TXSTMPH) << 32; - spin_lock_irqsave(&adapter->tmreg_lock, flags); - ns = timecounter_cyc2time(&adapter->tc, regval); - spin_unlock_irqrestore(&adapter->tmreg_lock, flags); - - memset(&shhwtstamps, 0, sizeof(shhwtstamps)); - shhwtstamps.hwtstamp = ns_to_ktime(ns); + ixgbe_ptp_convert_to_hwtstamp(adapter, &shhwtstamps, regval); skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamps); - dev_kfree_skb_any(adapter->ptp_tx_skb); - adapter->ptp_tx_skb = NULL; - clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state); + ixgbe_ptp_clear_tx_timestamp(adapter); } /** @@ -497,38 +701,85 @@ static void ixgbe_ptp_tx_hwtstamp_work(struct work_struct *work) IXGBE_PTP_TX_TIMEOUT); u32 tsynctxctl; - if (timeout) { - dev_kfree_skb_any(adapter->ptp_tx_skb); - adapter->ptp_tx_skb = NULL; - clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state); - e_warn(drv, "clearing Tx Timestamp hang\n"); + /* we have to have a valid skb to poll for a timestamp */ + if (!adapter->ptp_tx_skb) { + ixgbe_ptp_clear_tx_timestamp(adapter); return; } + /* stop polling once we have a valid timestamp */ tsynctxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL); - if (tsynctxctl & IXGBE_TSYNCTXCTL_VALID) + if (tsynctxctl & IXGBE_TSYNCTXCTL_VALID) { ixgbe_ptp_tx_hwtstamp(adapter); - else + return; + } + + if (timeout) { + ixgbe_ptp_clear_tx_timestamp(adapter); + adapter->tx_hwtstamp_timeouts++; + e_warn(drv, "clearing Tx Timestamp hang\n"); + } else { /* reschedule to keep checking if it's not available yet */ schedule_work(&adapter->ptp_tx_work); + } } /** - * ixgbe_ptp_rx_hwtstamp - utility function which checks for RX time stamp - * @adapter: pointer to adapter struct + * ixgbe_ptp_rx_pktstamp - utility function to get RX time stamp from buffer + * @q_vector: structure containing interrupt and ring information + * @skb: the packet + * + * This function will be called by the Rx routine of the timestamp for this + * packet is stored in the buffer. The value is stored in little endian format + * starting at the end of the packet data. + */ +void ixgbe_ptp_rx_pktstamp(struct ixgbe_q_vector *q_vector, + struct sk_buff *skb) +{ + __le64 regval; + + /* copy the bits out of the skb, and then trim the skb length */ + skb_copy_bits(skb, skb->len - IXGBE_TS_HDR_LEN, ®val, + IXGBE_TS_HDR_LEN); + __pskb_trim(skb, skb->len - IXGBE_TS_HDR_LEN); + + /* The timestamp is recorded in little endian format, and is stored at + * the end of the packet. + * + * DWORD: N N + 1 N + 2 + * Field: End of Packet SYSTIMH SYSTIML + */ + ixgbe_ptp_convert_to_hwtstamp(q_vector->adapter, skb_hwtstamps(skb), + le64_to_cpu(regval)); +} + +/** + * ixgbe_ptp_rx_rgtstamp - utility function which checks for RX time stamp + * @q_vector: structure containing interrupt and ring information * @skb: particular skb to send timestamp with * * if the timestamp is valid, we convert it into the timecounter ns * value, then store that result into the shhwtstamps structure which * is passed up the network stack */ -void ixgbe_ptp_rx_hwtstamp(struct ixgbe_adapter *adapter, struct sk_buff *skb) +void ixgbe_ptp_rx_rgtstamp(struct ixgbe_q_vector *q_vector, + struct sk_buff *skb) { - struct ixgbe_hw *hw = &adapter->hw; - struct skb_shared_hwtstamps *shhwtstamps; - u64 regval = 0, ns; + struct ixgbe_adapter *adapter; + struct ixgbe_hw *hw; + u64 regval = 0; u32 tsyncrxctl; - unsigned long flags; + + /* we cannot process timestamps on a ring without a q_vector */ + if (!q_vector || !q_vector->adapter) + return; + + adapter = q_vector->adapter; + hw = &adapter->hw; + + /* Read the tsyncrxctl register afterwards in order to prevent taking an + * I/O hit on every packet. + */ tsyncrxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL); if (!(tsyncrxctl & IXGBE_TSYNCRXCTL_VALID)) @@ -537,17 +788,7 @@ void ixgbe_ptp_rx_hwtstamp(struct ixgbe_adapter *adapter, struct sk_buff *skb) regval |= (u64)IXGBE_READ_REG(hw, IXGBE_RXSTMPL); regval |= (u64)IXGBE_READ_REG(hw, IXGBE_RXSTMPH) << 32; - spin_lock_irqsave(&adapter->tmreg_lock, flags); - ns = timecounter_cyc2time(&adapter->tc, regval); - spin_unlock_irqrestore(&adapter->tmreg_lock, flags); - - shhwtstamps = skb_hwtstamps(skb); - shhwtstamps->hwtstamp = ns_to_ktime(ns); - - /* Update the last_rx_timestamp timer in order to enable watchdog check - * for error case of latched timestamp on a dropped packet. - */ - adapter->last_rx_timestamp = jiffies; + ixgbe_ptp_convert_to_hwtstamp(adapter, skb_hwtstamps(skb), regval); } int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr) @@ -610,14 +851,20 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, case HWTSTAMP_FILTER_NONE: tsync_rx_ctl = 0; tsync_rx_mtrl = 0; + adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1; tsync_rx_mtrl |= IXGBE_RXMTRL_V1_SYNC_MSG; + adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1; tsync_rx_mtrl |= IXGBE_RXMTRL_V1_DELAY_REQ_MSG; + adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: @@ -631,9 +878,21 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_EVENT_V2; is_l2 = true; config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_ALL: + /* The X550 controller is capable of timestamping all packets, + * which allows it to accept any filter. + */ + if (hw->mac.type >= ixgbe_mac_X550) { + tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_ALL; + config->rx_filter = HWTSTAMP_FILTER_ALL; + adapter->flags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED; + break; + } + /* fall through */ default: /* * register RXMTRL must be set in order to do V1 packets, @@ -641,16 +900,46 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, * Delay_Req messages and hardware does not support * timestamping all packets => return error */ + adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); config->rx_filter = HWTSTAMP_FILTER_NONE; return -ERANGE; } if (hw->mac.type == ixgbe_mac_82598EB) { + adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); if (tsync_rx_ctl | tsync_tx_ctl) return -ERANGE; return 0; } + /* Per-packet timestamping only works if the filter is set to all + * packets. Since this is desired, always timestamp all packets as long + * as any Rx filter was configured. + */ + switch (hw->mac.type) { + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + /* enable timestamping all packets only if at least some + * packets were requested. Otherwise, play nice and disable + * timestamping + */ + if (config->rx_filter == HWTSTAMP_FILTER_NONE) + break; + + tsync_rx_ctl = IXGBE_TSYNCRXCTL_ENABLED | + IXGBE_TSYNCRXCTL_TYPE_ALL | + IXGBE_TSYNCRXCTL_TSIP_UT_EN; + config->rx_filter = HWTSTAMP_FILTER_ALL; + adapter->flags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED; + adapter->flags &= ~IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER; + is_l2 = true; + break; + default: + break; + } + /* define ethertype filter for timestamping L2 packets */ if (is_l2) IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588), @@ -678,8 +967,8 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, IXGBE_WRITE_FLUSH(hw); /* clear TX/RX time stamp registers, just to be sure */ - regval = IXGBE_READ_REG(hw, IXGBE_TXSTMPH); - regval = IXGBE_READ_REG(hw, IXGBE_RXSTMPH); + ixgbe_ptp_clear_tx_timestamp(adapter); + IXGBE_READ_REG(hw, IXGBE_RXSTMPH); return 0; } @@ -712,23 +1001,9 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr) -EFAULT : 0; } -/** - * ixgbe_ptp_start_cyclecounter - create the cycle counter from hw - * @adapter: pointer to the adapter structure - * - * This function should be called to set the proper values for the TIMINCA - * register and tell the cyclecounter structure what the tick rate of SYSTIME - * is. It does not directly modify SYSTIME registers or the timecounter - * structure. It should be called whenever a new TIMINCA value is necessary, - * such as during initialization or when the link speed changes. - */ -void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter) +static void ixgbe_ptp_link_speed_adjust(struct ixgbe_adapter *adapter, + u32 *shift, u32 *incval) { - struct ixgbe_hw *hw = &adapter->hw; - u32 incval = 0; - u32 shift = 0; - unsigned long flags; - /** * Scale the NIC cycle counter by a large factor so that * relatively small corrections to the frequency can be added @@ -745,36 +1020,98 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter) */ switch (adapter->link_speed) { case IXGBE_LINK_SPEED_100_FULL: - incval = IXGBE_INCVAL_100; - shift = IXGBE_INCVAL_SHIFT_100; + *shift = IXGBE_INCVAL_SHIFT_100; + *incval = IXGBE_INCVAL_100; break; case IXGBE_LINK_SPEED_1GB_FULL: - incval = IXGBE_INCVAL_1GB; - shift = IXGBE_INCVAL_SHIFT_1GB; + *shift = IXGBE_INCVAL_SHIFT_1GB; + *incval = IXGBE_INCVAL_1GB; break; case IXGBE_LINK_SPEED_10GB_FULL: default: - incval = IXGBE_INCVAL_10GB; - shift = IXGBE_INCVAL_SHIFT_10GB; + *shift = IXGBE_INCVAL_SHIFT_10GB; + *incval = IXGBE_INCVAL_10GB; break; } +} - /** - * Modify the calculated values to fit within the correct - * number of bits specified by the hardware. The 82599 doesn't - * have the same space as the X540, so bitshift the calculated - * values to fit. +/** + * ixgbe_ptp_start_cyclecounter - create the cycle counter from hw + * @adapter: pointer to the adapter structure + * + * This function should be called to set the proper values for the TIMINCA + * register and tell the cyclecounter structure what the tick rate of SYSTIME + * is. It does not directly modify SYSTIME registers or the timecounter + * structure. It should be called whenever a new TIMINCA value is necessary, + * such as during initialization or when the link speed changes. + */ +void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter) +{ + struct ixgbe_hw *hw = &adapter->hw; + struct cyclecounter cc; + unsigned long flags; + u32 incval = 0; + u32 tsauxc = 0; + u32 fuse0 = 0; + + /* For some of the boards below this mask is technically incorrect. + * The timestamp mask overflows at approximately 61bits. However the + * particular hardware does not overflow on an even bitmask value. + * Instead, it overflows due to conversion of upper 32bits billions of + * cycles. Timecounters are not really intended for this purpose so + * they do not properly function if the overflow point isn't 2^N-1. + * However, the actual SYSTIME values in question take ~138 years to + * overflow. In practice this means they won't actually overflow. A + * proper fix to this problem would require modification of the + * timecounter delta calculations. */ + cc.mask = CLOCKSOURCE_MASK(64); + cc.mult = 1; + cc.shift = 0; + switch (hw->mac.type) { + case ixgbe_mac_X550EM_x: + /* SYSTIME assumes X550EM_x board frequency is 300Mhz, and is + * designed to represent seconds and nanoseconds when this is + * the case. However, some revisions of hardware have a 400Mhz + * clock and we have to compensate for this frequency + * variation using corrected mult and shift values. + */ + fuse0 = IXGBE_READ_REG(hw, IXGBE_FUSES0_GROUP(0)); + if (!(fuse0 & IXGBE_FUSES0_300MHZ)) { + cc.mult = 3; + cc.shift = 2; + } + /* fallthrough */ + case ixgbe_mac_X550: + cc.read = ixgbe_ptp_read_X550; + + /* enable SYSTIME counter */ + IXGBE_WRITE_REG(hw, IXGBE_SYSTIMR, 0); + IXGBE_WRITE_REG(hw, IXGBE_SYSTIML, 0); + IXGBE_WRITE_REG(hw, IXGBE_SYSTIMH, 0); + tsauxc = IXGBE_READ_REG(hw, IXGBE_TSAUXC); + IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, + tsauxc & ~IXGBE_TSAUXC_DISABLE_SYSTIME); + IXGBE_WRITE_REG(hw, IXGBE_TSIM, IXGBE_TSIM_TXTS); + IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_TIMESYNC); + + IXGBE_WRITE_FLUSH(hw); + break; case ixgbe_mac_X540: + cc.read = ixgbe_ptp_read_82599; + + ixgbe_ptp_link_speed_adjust(adapter, &cc.shift, &incval); IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval); break; case ixgbe_mac_82599EB: + cc.read = ixgbe_ptp_read_82599; + + ixgbe_ptp_link_speed_adjust(adapter, &cc.shift, &incval); incval >>= IXGBE_INCVAL_SHIFT_82599; - shift -= IXGBE_INCVAL_SHIFT_82599; + cc.shift -= IXGBE_INCVAL_SHIFT_82599; IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, - (1 << IXGBE_INCPER_SHIFT_82599) | - incval); + (1 << IXGBE_INCPER_SHIFT_82599) | incval); break; default: /* other devices aren't supported */ @@ -787,13 +1124,7 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter) /* need lock to prevent incorrect read while modifying cyclecounter */ spin_lock_irqsave(&adapter->tmreg_lock, flags); - - memset(&adapter->cc, 0, sizeof(adapter->cc)); - adapter->cc.read = ixgbe_ptp_read; - adapter->cc.mask = CYCLECOUNTER_MASK(64); - adapter->cc.shift = shift; - adapter->cc.mult = 1; - + memcpy(&adapter->hw_cc, &cc, sizeof(adapter->hw_cc)); spin_unlock_irqrestore(&adapter->tmreg_lock, flags); } @@ -814,29 +1145,27 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; unsigned long flags; - /* set SYSTIME registers to 0 just in case */ - IXGBE_WRITE_REG(hw, IXGBE_SYSTIML, 0x00000000); - IXGBE_WRITE_REG(hw, IXGBE_SYSTIMH, 0x00000000); - IXGBE_WRITE_FLUSH(hw); - /* reset the hardware timestamping mode */ ixgbe_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config); + /* 82598 does not support PTP */ + if (hw->mac.type == ixgbe_mac_82598EB) + return; + ixgbe_ptp_start_cyclecounter(adapter); spin_lock_irqsave(&adapter->tmreg_lock, flags); - - /* reset the ns time counter */ - timecounter_init(&adapter->tc, &adapter->cc, + timecounter_init(&adapter->hw_tc, &adapter->hw_cc, ktime_to_ns(ktime_get_real())); - spin_unlock_irqrestore(&adapter->tmreg_lock, flags); - /* - * Now that the shift has been calculated and the systime + adapter->last_overflow_check = jiffies; + + /* Now that the shift has been calculated and the systime * registers reset, (re-)enable the Clock out feature */ - ixgbe_ptp_setup_sdp(adapter); + if (adapter->ptp_setup_sdp) + adapter->ptp_setup_sdp(adapter); } /** @@ -845,11 +1174,11 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter) * * This function performs setup of the user entry point function table and * initializes the PTP clock device, which is used to access the clock-like - * features of the PTP core. It will be called by ixgbe_ptp_init, only if - * there isn't already a clock device (such as after a suspend/resume cycle, - * where the clock device wasn't destroyed). + * features of the PTP core. It will be called by ixgbe_ptp_init, and may + * reuse a previously initialized clock (such as during a suspend/resume + * cycle). */ -static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter) +static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter) { struct net_device *netdev = adapter->netdev; long err; @@ -869,11 +1198,12 @@ static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter) adapter->ptp_caps.n_ext_ts = 0; adapter->ptp_caps.n_per_out = 0; adapter->ptp_caps.pps = 1; - adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq; + adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599; adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime; adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime; adapter->ptp_caps.settime64 = ixgbe_ptp_settime; adapter->ptp_caps.enable = ixgbe_ptp_feature_enable; + adapter->ptp_setup_sdp = ixgbe_ptp_setup_sdp_x540; break; case ixgbe_mac_82599EB: snprintf(adapter->ptp_caps.name, @@ -885,14 +1215,31 @@ static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter) adapter->ptp_caps.n_ext_ts = 0; adapter->ptp_caps.n_per_out = 0; adapter->ptp_caps.pps = 0; - adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq; + adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599; + adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime; + adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime; + adapter->ptp_caps.settime64 = ixgbe_ptp_settime; + adapter->ptp_caps.enable = ixgbe_ptp_feature_enable; + break; + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + snprintf(adapter->ptp_caps.name, 16, "%s", netdev->name); + adapter->ptp_caps.owner = THIS_MODULE; + adapter->ptp_caps.max_adj = 30000000; + adapter->ptp_caps.n_alarm = 0; + adapter->ptp_caps.n_ext_ts = 0; + adapter->ptp_caps.n_per_out = 0; + adapter->ptp_caps.pps = 0; + adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_X550; adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime; adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime; adapter->ptp_caps.settime64 = ixgbe_ptp_settime; adapter->ptp_caps.enable = ixgbe_ptp_feature_enable; + adapter->ptp_setup_sdp = NULL; break; default: adapter->ptp_clock = NULL; + adapter->ptp_setup_sdp = NULL; return -EOPNOTSUPP; } @@ -961,18 +1308,13 @@ void ixgbe_ptp_suspend(struct ixgbe_adapter *adapter) if (!test_and_clear_bit(__IXGBE_PTP_RUNNING, &adapter->state)) return; - /* since this might be called in suspend, we don't clear the state, - * but simply reset the auxiliary PPS signal control register - */ - IXGBE_WRITE_REG(&adapter->hw, IXGBE_TSAUXC, 0x0); + adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED; + if (adapter->ptp_setup_sdp) + adapter->ptp_setup_sdp(adapter); /* ensure that we cancel any pending PTP Tx work item in progress */ cancel_work_sync(&adapter->ptp_tx_work); - if (adapter->ptp_tx_skb) { - dev_kfree_skb_any(adapter->ptp_tx_skb); - adapter->ptp_tx_skb = NULL; - clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state); - } + ixgbe_ptp_clear_tx_timestamp(adapter); } /** diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 927349da232a..1329eddfc9ce 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -1020,6 +1020,7 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_TXSTMPH 0x08C08 /* Tx timestamp value High - RO */ #define IXGBE_SYSTIML 0x08C0C /* System time register Low - RO */ #define IXGBE_SYSTIMH 0x08C10 /* System time register High - RO */ +#define IXGBE_SYSTIMR 0x08C58 /* System time register Residue - RO */ #define IXGBE_TIMINCA 0x08C14 /* Increment attributes register - RW */ #define IXGBE_TIMADJL 0x08C18 /* Time Adjustment Offset register Low - RW */ #define IXGBE_TIMADJH 0x08C1C /* Time Adjustment Offset register High - RW */ @@ -1036,6 +1037,7 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_AUXSTMPH0 0x08C40 /* Auxiliary Time Stamp 0 register High - RO */ #define IXGBE_AUXSTMPL1 0x08C44 /* Auxiliary Time Stamp 1 register Low - RO */ #define IXGBE_AUXSTMPH1 0x08C48 /* Auxiliary Time Stamp 1 register High - RO */ +#define IXGBE_TSIM 0x08C68 /* TimeSync Interrupt Mask Register - RW */ /* Diagnostic Registers */ #define IXGBE_RDSTATCTL 0x02C20 @@ -2213,6 +2215,7 @@ enum { #define IXGBE_TSAUXC_EN_CLK 0x00000004 #define IXGBE_TSAUXC_SYNCLK 0x00000008 #define IXGBE_TSAUXC_SDP0_INT 0x00000040 +#define IXGBE_TSAUXC_DISABLE_SYSTIME 0x80000000 #define IXGBE_TSYNCTXCTL_VALID 0x00000001 /* Tx timestamp valid */ #define IXGBE_TSYNCTXCTL_ENABLED 0x00000010 /* Tx timestamping enabled */ @@ -2222,8 +2225,12 @@ enum { #define IXGBE_TSYNCRXCTL_TYPE_L2_V2 0x00 #define IXGBE_TSYNCRXCTL_TYPE_L4_V1 0x02 #define IXGBE_TSYNCRXCTL_TYPE_L2_L4_V2 0x04 +#define IXGBE_TSYNCRXCTL_TYPE_ALL 0x08 #define IXGBE_TSYNCRXCTL_TYPE_EVENT_V2 0x0A #define IXGBE_TSYNCRXCTL_ENABLED 0x00000010 /* Rx Timestamping enabled */ +#define IXGBE_TSYNCRXCTL_TSIP_UT_EN 0x00800000 /* Rx Timestamp in Packet */ + +#define IXGBE_TSIM_TXTS 0x00000002 #define IXGBE_RXMTRL_V1_CTRLT_MASK 0x000000FF #define IXGBE_RXMTRL_V1_SYNC_MSG 0x00 @@ -2336,6 +2343,7 @@ enum { #define IXGBE_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ #define IXGBE_RXD_STAT_DYNINT 0x800 /* Pkt caused INT via DYNINT */ #define IXGBE_RXD_STAT_LLINT 0x800 /* Pkt caused Low Latency Interrupt */ +#define IXGBE_RXD_STAT_TSIP 0x08000 /* Time Stamp in packet buffer */ #define IXGBE_RXD_STAT_TS 0x10000 /* Time Stamp */ #define IXGBE_RXD_STAT_SECP 0x20000 /* Security Processing */ #define IXGBE_RXD_STAT_LB 0x40000 /* Loopback Status */ -- cgit v1.2.3-59-g8ed1b From efff2e027758fd5cc739d500397f729591f32a94 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Tue, 27 Oct 2015 13:23:14 -0700 Subject: ixgbe: Correct spec violations by waiting after reset The ixgbe driver was violating the specification in the datasheet by not waiting 1ms before checking for the reset bit clearing. This is called out for devices supported by ixgbe, so implement the required delay. Reported-by: Dan Streetman Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c | 5 +++-- drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c | 3 ++- drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c | 3 ++- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c index 65db69b862fb..8f09d291a043 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2014 Intel Corporation. + Copyright(c) 1999 - 2015 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -765,13 +765,14 @@ mac_reset_top: ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL) | IXGBE_CTRL_RST; IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl); IXGBE_WRITE_FLUSH(hw); + usleep_range(1000, 1200); /* Poll for reset bit to self-clear indicating reset is complete */ for (i = 0; i < 10; i++) { - udelay(1); ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL); if (!(ctrl & IXGBE_CTRL_RST)) break; + udelay(1); } if (ctrl & IXGBE_CTRL_RST) { status = IXGBE_ERR_RESET_FAILED; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index a39afcf03e2c..b8bd72589f72 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -990,13 +990,14 @@ mac_reset_top: ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL); IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl); IXGBE_WRITE_FLUSH(hw); + usleep_range(1000, 1200); /* Poll for reset bit to self-clear indicating reset is complete */ for (i = 0; i < 10; i++) { - udelay(1); ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL); if (!(ctrl & IXGBE_CTRL_RST_MASK)) break; + udelay(1); } if (ctrl & IXGBE_CTRL_RST_MASK) { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index c1d4584f6469..b9e9b0c17398 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -110,13 +110,14 @@ mac_reset_top: ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL); IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl); IXGBE_WRITE_FLUSH(hw); + usleep_range(1000, 1200); /* Poll for reset bit to self-clear indicating reset is complete */ for (i = 0; i < 10; i++) { - udelay(1); ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL); if (!(ctrl & IXGBE_CTRL_RST_MASK)) break; + udelay(1); } if (ctrl & IXGBE_CTRL_RST_MASK) { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index b8ad3f212e72..f4ef0d1a5dbe 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -2146,13 +2146,14 @@ mac_reset_top: ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL); IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl); IXGBE_WRITE_FLUSH(hw); + usleep_range(1000, 1200); /* Poll for reset bit to self-clear meaning reset is complete */ for (i = 0; i < 10; i++) { - udelay(1); ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL); if (!(ctrl & IXGBE_CTRL_RST_MASK)) break; + udelay(1); } if (ctrl & IXGBE_CTRL_RST_MASK) { -- cgit v1.2.3-59-g8ed1b From 48b44612738793252c97c548f3d0bd56543d5273 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Tue, 27 Oct 2015 13:23:23 -0700 Subject: ixgbe: Wait for master disable to be set According to the datasheets, the driver should wait for the master disable bit to read as being set before checking the status register for master disable. Reported-by: Dan Streetman Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index ce61b36b94f1..daec6aef5dc8 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2014 Intel Corporation. + Copyright(c) 1999 - 2015 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -2454,6 +2454,17 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw) /* Always set this bit to ensure any future transactions are blocked */ IXGBE_WRITE_REG(hw, IXGBE_CTRL, IXGBE_CTRL_GIO_DIS); + /* Poll for bit to read as set */ + for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) { + if (IXGBE_READ_REG(hw, IXGBE_CTRL) & IXGBE_CTRL_GIO_DIS) + break; + usleep_range(100, 120); + } + if (i >= IXGBE_PCI_MASTER_DISABLE_TIMEOUT) { + hw_dbg(hw, "GIO disable did not set - requesting resets\n"); + goto gio_disable_fail; + } + /* Exit if master requests are blocked */ if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO) || ixgbe_removed(hw->hw_addr)) @@ -2475,6 +2486,7 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw) * again to clear out any effects they may have had on our device. */ hw_dbg(hw, "GIO Master Disable bit didn't clear - requesting resets\n"); +gio_disable_fail: hw->mac.flags |= IXGBE_FLAGS_DOUBLE_RESET_REQUIRED; if (hw->mac.type >= ixgbe_mac_X550) -- cgit v1.2.3-59-g8ed1b From 988d13073fe122f0b6a2b80b5f2aa1b0717f9edb Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Fri, 30 Oct 2015 15:29:34 -0700 Subject: ixgbe: Save VF info and take references Save VF device pointers and take references to speed accesses used to monitor the device behavior to avoid slot resets. The saved information avoids lock contention during the search used to access each of the VFs. Signed-off-by: Mark Rustad Tested-by: Darin Miller Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 31 ++++++---------- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 50 ++++++++++++++++++++++++-- 3 files changed, 59 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 518f339476f8..445b4c9169b6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -139,6 +139,7 @@ enum ixgbe_tx_flags { #define IXGBE_X540_VF_DEVICE_ID 0x1515 struct vf_data_storage { + struct pci_dev *vfdev; unsigned char vf_mac_addresses[ETH_ALEN]; u16 vf_mc_hashes[IXGBE_MAX_VF_MC_ENTRIES]; u16 num_vf_mc_hashes; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 61926283259a..a10a0facb4e8 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -6666,10 +6666,8 @@ static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; - struct pci_dev *vfdev; + unsigned int vf; u32 gpc; - int pos; - unsigned short vf_id; if (!(netif_carrier_ok(adapter->netdev))) return; @@ -6686,26 +6684,17 @@ static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter) if (!pdev) return; - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); - if (!pos) - return; - - /* get the device ID for the VF */ - pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &vf_id); - /* check status reg for all VFs owned by this PF */ - vfdev = pci_get_device(pdev->vendor, vf_id, NULL); - while (vfdev) { - if (vfdev->is_virtfn && (vfdev->physfn == pdev)) { - u16 status_reg; - - pci_read_config_word(vfdev, PCI_STATUS, &status_reg); - if (status_reg & PCI_STATUS_REC_MASTER_ABORT) - /* issue VFLR */ - ixgbe_issue_vf_flr(adapter, vfdev); - } + for (vf = 0; vf < adapter->num_vfs; ++vf) { + struct pci_dev *vfdev = adapter->vfinfo[vf].vfdev; + u16 status_reg; - vfdev = pci_get_device(pdev->vendor, vf_id, vfdev); + if (!vfdev) + continue; + pci_read_config_word(vfdev, PCI_STATUS, &status_reg); + if (status_reg != IXGBE_FAILED_READ_CFG_WORD && + status_reg & PCI_STATUS_REC_MASTER_ABORT) + ixgbe_issue_vf_flr(adapter, vfdev); } } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index fcd8b27a0ccb..31de6cf7adb0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2014 Intel Corporation. + Copyright(c) 1999 - 2015 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -130,6 +130,38 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter) return -ENOMEM; } +/** + * ixgbe_get_vfs - Find and take references to all vf devices + * @adapter: Pointer to adapter struct + */ +static void ixgbe_get_vfs(struct ixgbe_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + u16 vendor = pdev->vendor; + struct pci_dev *vfdev; + int vf = 0; + u16 vf_id; + int pos; + + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); + if (!pos) + return; + pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &vf_id); + + vfdev = pci_get_device(vendor, vf_id, NULL); + for (; vfdev; vfdev = pci_get_device(vendor, vf_id, vfdev)) { + if (!vfdev->is_virtfn) + continue; + if (vfdev->physfn != pdev) + continue; + if (vf >= adapter->num_vfs) + continue; + pci_dev_get(vfdev); + adapter->vfinfo[vf].vfdev = vfdev; + ++vf; + } +} + /* Note this function is called when the user wants to enable SR-IOV * VFs using the now deprecated module parameter */ @@ -170,8 +202,10 @@ void ixgbe_enable_sriov(struct ixgbe_adapter *adapter) } } - if (!__ixgbe_enable_sriov(adapter)) + if (!__ixgbe_enable_sriov(adapter)) { + ixgbe_get_vfs(adapter); return; + } /* If we have gotten to this point then there is no memory available * to manage the VF devices - print message and bail. @@ -184,6 +218,7 @@ void ixgbe_enable_sriov(struct ixgbe_adapter *adapter) #endif /* #ifdef CONFIG_PCI_IOV */ int ixgbe_disable_sriov(struct ixgbe_adapter *adapter) { + unsigned int num_vfs = adapter->num_vfs, vf; struct ixgbe_hw *hw = &adapter->hw; u32 gpie; u32 vmdctl; @@ -192,6 +227,16 @@ int ixgbe_disable_sriov(struct ixgbe_adapter *adapter) /* set num VFs to 0 to prevent access to vfinfo */ adapter->num_vfs = 0; + /* put the reference to all of the vf devices */ + for (vf = 0; vf < num_vfs; ++vf) { + struct pci_dev *vfdev = adapter->vfinfo[vf].vfdev; + + if (!vfdev) + continue; + adapter->vfinfo[vf].vfdev = NULL; + pci_dev_put(vfdev); + } + /* free VF control structures */ kfree(adapter->vfinfo); adapter->vfinfo = NULL; @@ -289,6 +334,7 @@ static int ixgbe_pci_sriov_enable(struct pci_dev *dev, int num_vfs) e_dev_warn("Failed to enable PCI sriov: %d\n", err); return err; } + ixgbe_get_vfs(adapter); ixgbe_sriov_reinit(adapter); return num_vfs; -- cgit v1.2.3-59-g8ed1b From 36a92d7190e68e9387347695fe4625eb2c9e7e1c Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Wed, 18 Nov 2015 09:21:28 -0800 Subject: ixgbe: Handle extended IPv6 headers in Tx path Check for and handle IPv6 extended headers so that Tx checksum offload can be done. Also use skb_checksum_help for unexpected cases. Thanks to Tom Herbert for noticing these problems. Thanks to Alexander Duyck for recognizing problems with the first version of this patch and recognizing how to coalesce error conditions into a single location. Reported-by: Tom Herbert Signed-off-by: Mark Rustad Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index a10a0facb4e8..ebd4522e7879 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -7063,6 +7063,7 @@ static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring, struct tcphdr *tcphdr; u8 *raw; } transport_hdr; + __be16 frag_off; if (skb->encapsulation) { network_hdr.raw = skb_inner_network_header(skb); @@ -7086,13 +7087,17 @@ static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring, case 6: vlan_macip_lens |= transport_hdr.raw - network_hdr.raw; l4_hdr = network_hdr.ipv6->nexthdr; + if (likely((transport_hdr.raw - network_hdr.raw) == + sizeof(struct ipv6hdr))) + break; + ipv6_skip_exthdr(skb, network_hdr.raw - skb->data + + sizeof(struct ipv6hdr), + &l4_hdr, &frag_off); + if (unlikely(frag_off)) + l4_hdr = NEXTHDR_FRAGMENT; break; default: - if (unlikely(net_ratelimit())) { - dev_warn(tx_ring->dev, - "partial checksum but version=%d\n", - network_hdr.ipv4->version); - } + break; } switch (l4_hdr) { @@ -7113,16 +7118,18 @@ static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring, default: if (unlikely(net_ratelimit())) { dev_warn(tx_ring->dev, - "partial checksum but l4 proto=%x!\n", - l4_hdr); + "partial checksum, version=%d, l4 proto=%x\n", + network_hdr.ipv4->version, l4_hdr); } - break; + skb_checksum_help(skb); + goto no_csum; } /* update TX checksum flag */ first->tx_flags |= IXGBE_TX_FLAGS_CSUM; } +no_csum: /* vlan_macip_lens: MACLEN, VLAN tag */ vlan_macip_lens |= first->tx_flags & IXGBE_TX_FLAGS_VLAN_MASK; -- cgit v1.2.3-59-g8ed1b From 3c2f2b77a917488b56b2676b99adb5d3c07d6e68 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Thu, 5 Nov 2015 11:02:14 -0800 Subject: ixgbe: Always turn PHY power on when requested Instead of inhibiting PHY power control when manageability is present, only inhibit turning PHY power off when manageability is present. Consequently, PHY power will always be turned on when requested. Without this patch, some systems with X540 or X550 devices in some conditions will never get link. Signed-off-by: Mark Rustad Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c | 3 +++ drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index fb8673d63806..db0731e05401 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -2393,6 +2393,9 @@ s32 ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on) if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper) return 0; + if (!on && ixgbe_mng_present(hw)) + return 0; + status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL, IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, ®); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index b9e9b0c17398..bf8225ceab8e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -57,8 +57,7 @@ s32 ixgbe_get_invariants_X540(struct ixgbe_hw *hw) struct ixgbe_phy_info *phy = &hw->phy; /* set_phy_power was set by default to NULL */ - if (!ixgbe_mng_present(hw)) - phy->ops.set_phy_power = ixgbe_set_copper_phy_power; + phy->ops.set_phy_power = ixgbe_set_copper_phy_power; mac->mcft_size = IXGBE_X540_MC_TBL_SIZE; mac->vft_size = IXGBE_X540_VFT_TBL_SIZE; -- cgit v1.2.3-59-g8ed1b From d34a614adfb16a560ddb6759d532eb32b6651eae Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Thu, 19 Nov 2015 13:56:30 -0800 Subject: ixgbevf: Handle extended IPv6 headers in Tx path Check for and handle IPv6 extended headers so that Tx checksum offload can be done. Also use skb_checksum_help for unexpected cases. Thanks to Tom Herbert for noticing these problems. Thanks to Alexander Duyck for seeing how to coalesce the error handling into one location. Reported-by: Tom Herbert Signed-off-by: Mark Rustad Tested-by: Darin Miller Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 42b971c0cc87..f098952d4fb4 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -3344,6 +3344,7 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring, if (skb->ip_summed == CHECKSUM_PARTIAL) { u8 l4_hdr = 0; + __be16 frag_off; switch (first->protocol) { case htons(ETH_P_IP): @@ -3354,13 +3355,16 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring, case htons(ETH_P_IPV6): vlan_macip_lens |= skb_network_header_len(skb); l4_hdr = ipv6_hdr(skb)->nexthdr; + if (likely(skb_network_header_len(skb) == + sizeof(struct ipv6hdr))) + break; + ipv6_skip_exthdr(skb, skb_network_offset(skb) + + sizeof(struct ipv6hdr), + &l4_hdr, &frag_off); + if (unlikely(frag_off)) + l4_hdr = NEXTHDR_FRAGMENT; break; default: - if (unlikely(net_ratelimit())) { - dev_warn(tx_ring->dev, - "partial checksum but proto=%x!\n", - first->protocol); - } break; } @@ -3382,16 +3386,18 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring, default: if (unlikely(net_ratelimit())) { dev_warn(tx_ring->dev, - "partial checksum but l4 proto=%x!\n", - l4_hdr); + "partial checksum, l3 proto=%x, l4 proto=%x\n", + first->protocol, l4_hdr); } - break; + skb_checksum_help(skb); + goto no_csum; } /* update TX checksum flag */ first->tx_flags |= IXGBE_TX_FLAGS_CSUM; } +no_csum: /* vlan_macip_lens: MACLEN, VLAN tag */ vlan_macip_lens |= skb_network_offset(skb) << IXGBE_ADVTXD_MACLEN_SHIFT; vlan_macip_lens |= first->tx_flags & IXGBE_TX_FLAGS_VLAN_MASK; -- cgit v1.2.3-59-g8ed1b From 3b195843f59b8aefdc15c689615745bf6758756c Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 3 Dec 2015 04:20:57 -0800 Subject: i40e: Fix i40e_print_features() VEB mode output Commit 7fd89545f337 ("i40e: remove BUG_ON from feature string building") added defective output when I40E_FLAG_VEB_MODE_ENABLED was set in function i40e_print_features. Fix it. Miscellanea: - Remove unnecessary string variable - Add space before not after fixed strings - Use kmalloc not kzalloc - Don't initialize i to 0, use result of first snprintf Reported-by: Sergei Shtylyov Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/i40e/i40e_main.c | 42 +++++++++++++---------------- 1 file changed, 19 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 9e6268b4295a..0ddec19a9087 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -10290,52 +10290,48 @@ static int i40e_setup_pf_filter_control(struct i40e_pf *pf) static void i40e_print_features(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; - char *buf, *string; - int i = 0; + char *buf; + int i; - string = kzalloc(INFO_STRING_LEN, GFP_KERNEL); - if (!string) { - dev_err(&pf->pdev->dev, "Features string allocation failed\n"); + buf = kmalloc(INFO_STRING_LEN, GFP_KERNEL); + if (!buf) return; - } - - buf = string; - i += snprintf(&buf[i], REMAIN(i), "Features: PF-id[%d] ", hw->pf_id); + i = snprintf(buf, INFO_STRING_LEN, "Features: PF-id[%d]", hw->pf_id); #ifdef CONFIG_PCI_IOV - i += snprintf(&buf[i], REMAIN(i), "VFs: %d ", pf->num_req_vfs); + i += snprintf(&buf[i], REMAIN(i), " VFs: %d", pf->num_req_vfs); #endif - i += snprintf(&buf[i], REMAIN(i), "VSIs: %d QP: %d RX: %s ", + i += snprintf(&buf[i], REMAIN(i), " VSIs: %d QP: %d RX: %s", pf->hw.func_caps.num_vsis, pf->vsi[pf->lan_vsi]->num_queue_pairs, pf->flags & I40E_FLAG_RX_PS_ENABLED ? "PS" : "1BUF"); if (pf->flags & I40E_FLAG_RSS_ENABLED) - i += snprintf(&buf[i], REMAIN(i), "RSS "); + i += snprintf(&buf[i], REMAIN(i), " RSS"); if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) - i += snprintf(&buf[i], REMAIN(i), "FD_ATR "); + i += snprintf(&buf[i], REMAIN(i), " FD_ATR"); if (pf->flags & I40E_FLAG_FD_SB_ENABLED) { - i += snprintf(&buf[i], REMAIN(i), "FD_SB "); - i += snprintf(&buf[i], REMAIN(i), "NTUPLE "); + i += snprintf(&buf[i], REMAIN(i), " FD_SB"); + i += snprintf(&buf[i], REMAIN(i), " NTUPLE"); } if (pf->flags & I40E_FLAG_DCB_CAPABLE) - i += snprintf(&buf[i], REMAIN(i), "DCB "); + i += snprintf(&buf[i], REMAIN(i), " DCB"); #if IS_ENABLED(CONFIG_VXLAN) - i += snprintf(&buf[i], REMAIN(i), "VxLAN "); + i += snprintf(&buf[i], REMAIN(i), " VxLAN"); #endif if (pf->flags & I40E_FLAG_PTP) - i += snprintf(&buf[i], REMAIN(i), "PTP "); + i += snprintf(&buf[i], REMAIN(i), " PTP"); #ifdef I40E_FCOE if (pf->flags & I40E_FLAG_FCOE_ENABLED) - i += snprintf(&buf[i], REMAIN(i), "FCOE "); + i += snprintf(&buf[i], REMAIN(i), " FCOE"); #endif if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) - i += snprintf(&buf[i], REMAIN(i), "VEPA "); + i += snprintf(&buf[i], REMAIN(i), " VEB"); else - buf += sprintf(buf, "VEPA "); + i += snprintf(&buf[i], REMAIN(i), " VEPA"); - dev_info(&pf->pdev->dev, "%s\n", string); - kfree(string); + dev_info(&pf->pdev->dev, "%s\n", buf); + kfree(buf); WARN_ON(i > INFO_STRING_LEN); } -- cgit v1.2.3-59-g8ed1b From 3952af4d50343728e54bf93880e0ecb9c42c47aa Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:05 +0100 Subject: bonding: add 802.3ad support for 100G speeds Similar to other speeds, add 100G to bonding 802.3ad code. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 940e2ebbdea8..4cbb8b27a891 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -93,7 +93,8 @@ enum ad_link_speed_type { AD_LINK_SPEED_10000MBPS, AD_LINK_SPEED_20000MBPS, AD_LINK_SPEED_40000MBPS, - AD_LINK_SPEED_56000MBPS + AD_LINK_SPEED_56000MBPS, + AD_LINK_SPEED_100000MBPS, }; /* compare MAC addresses */ @@ -258,6 +259,7 @@ static inline int __check_agg_selection_timer(struct port *port) * %AD_LINK_SPEED_20000MBPS * %AD_LINK_SPEED_40000MBPS * %AD_LINK_SPEED_56000MBPS + * %AD_LINK_SPEED_100000MBPS */ static u16 __get_link_speed(struct port *port) { @@ -305,6 +307,10 @@ static u16 __get_link_speed(struct port *port) speed = AD_LINK_SPEED_56000MBPS; break; + case SPEED_100000: + speed = AD_LINK_SPEED_100000MBPS; + break; + default: /* unknown speed value from ethtool. shouldn't happen */ speed = 0; @@ -681,6 +687,9 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator) case AD_LINK_SPEED_56000MBPS: bandwidth = aggregator->num_of_ports * 56000; break; + case AD_LINK_SPEED_100000MBPS: + bandwidth = aggregator->num_of_ports * 100000; + break; default: bandwidth = 0; /* to silence the compiler */ } -- cgit v1.2.3-59-g8ed1b From c981e4213e9d2d4ec79501bd607722ec712742a2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:06 +0100 Subject: net: add netif_is_team_master helper Similar to other helpers, caller can use this to find out if device is team master. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 1 + include/linux/netdevice.h | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 651d35ea22c5..d2f3ee832c47 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2054,6 +2054,7 @@ static void team_setup(struct net_device *dev) dev->flags |= IFF_MULTICAST; dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING); dev->priv_flags |= IFF_NO_QUEUE; + dev->priv_flags |= IFF_TEAM; /* * Indicate we support unicast address filtering. That way core won't diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fcbc5259c630..2b889be65d88 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1273,6 +1273,7 @@ struct net_device_ops { * @IFF_NO_QUEUE: device can run without qdisc attached * @IFF_OPENVSWITCH: device is a Open vSwitch master * @IFF_L3MDEV_SLAVE: device is enslaved to an L3 master device + * @IFF_TEAM: device is a team device */ enum netdev_priv_flags { IFF_802_1Q_VLAN = 1<<0, @@ -1299,6 +1300,7 @@ enum netdev_priv_flags { IFF_NO_QUEUE = 1<<21, IFF_OPENVSWITCH = 1<<22, IFF_L3MDEV_SLAVE = 1<<23, + IFF_TEAM = 1<<24, }; #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN @@ -1325,6 +1327,7 @@ enum netdev_priv_flags { #define IFF_NO_QUEUE IFF_NO_QUEUE #define IFF_OPENVSWITCH IFF_OPENVSWITCH #define IFF_L3MDEV_SLAVE IFF_L3MDEV_SLAVE +#define IFF_TEAM IFF_TEAM /** * struct net_device - The DEVICE structure. @@ -3889,6 +3892,11 @@ static inline bool netif_is_ovs_master(const struct net_device *dev) return dev->priv_flags & IFF_OPENVSWITCH; } +static inline bool netif_is_team_master(struct net_device *dev) +{ + return dev->priv_flags & IFF_TEAM; +} + /* This device needs to keep skb dst for qdisc enqueue or ndo_start_xmit() */ static inline void netif_keep_dst(struct net_device *dev) { -- cgit v1.2.3-59-g8ed1b From 6dffb0447c25476f499d205dfceb1972e8dae919 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:10 +0100 Subject: net: propagate upper priv via netdev_master_upper_dev_link Eliminate netdev_master_upper_dev_link_private and pass priv directly as a parameter of netdev_master_upper_dev_link. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 2 +- drivers/net/team/team.c | 2 +- drivers/net/vrf.c | 2 +- include/linux/netdevice.h | 6 ++---- net/batman-adv/hard-interface.c | 3 ++- net/bridge/br_if.c | 2 +- net/core/dev.c | 18 ++++++------------ net/openvswitch/vport-netdev.c | 2 +- 8 files changed, 15 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 9e0f8a7ef8b1..924015729b2d 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1204,7 +1204,7 @@ static int bond_master_upper_dev_link(struct net_device *bond_dev, { int err; - err = netdev_master_upper_dev_link_private(slave_dev, bond_dev, slave); + err = netdev_master_upper_dev_link(slave_dev, bond_dev, slave); if (err) return err; slave_dev->flags |= IFF_SLAVE; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index d2f3ee832c47..b37f8d14dca0 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1083,7 +1083,7 @@ static int team_upper_dev_link(struct net_device *dev, { int err; - err = netdev_master_upper_dev_link(port_dev, dev); + err = netdev_master_upper_dev_link(port_dev, dev, NULL); if (err) return err; port_dev->priv_flags |= IFF_TEAM_PORT; diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index c2d54c4ed556..59c5bddeaedd 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -624,7 +624,7 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) goto out_fail; } - ret = netdev_master_upper_dev_link(port_dev, dev); + ret = netdev_master_upper_dev_link(port_dev, dev, NULL); if (ret < 0) goto out_unregister; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1506be58c59a..939b8f3de810 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3619,10 +3619,8 @@ struct net_device *netdev_master_upper_dev_get(struct net_device *dev); struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev); int netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev); int netdev_master_upper_dev_link(struct net_device *dev, - struct net_device *upper_dev); -int netdev_master_upper_dev_link_private(struct net_device *dev, - struct net_device *upper_dev, - void *private); + struct net_device *upper_dev, + void *upper_priv); void netdev_upper_dev_unlink(struct net_device *dev, struct net_device *upper_dev); void netdev_adjacent_rename_links(struct net_device *dev, char *oldname); diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index f11345e163d7..a7f4f1085dbb 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -464,7 +464,8 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, hard_iface->soft_iface = soft_iface; bat_priv = netdev_priv(hard_iface->soft_iface); - ret = netdev_master_upper_dev_link(hard_iface->net_dev, soft_iface); + ret = netdev_master_upper_dev_link(hard_iface->net_dev, + soft_iface, NULL); if (ret) goto err_dev; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index ec02f5869a78..781abc34667a 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -493,7 +493,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) dev->priv_flags |= IFF_BRIDGE_PORT; - err = netdev_master_upper_dev_link(dev, br->dev); + err = netdev_master_upper_dev_link(dev, br->dev, NULL); if (err) goto err5; diff --git a/net/core/dev.c b/net/core/dev.c index 939cd1b1da15..27d052bb78bc 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5421,7 +5421,7 @@ static void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev, static int __netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, bool master, - void *private) + void *upper_priv) { struct netdev_notifier_changeupper_info changeupper_info; struct netdev_adjacent *i, *j, *to_i, *to_j; @@ -5452,7 +5452,7 @@ static int __netdev_upper_dev_link(struct net_device *dev, if (ret) return ret; - ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, private, + ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, upper_priv, master); if (ret) return ret; @@ -5557,6 +5557,7 @@ EXPORT_SYMBOL(netdev_upper_dev_link); * netdev_master_upper_dev_link - Add a master link to the upper device * @dev: device * @upper_dev: new upper device + * @upper_priv: upper device private * * Adds a link to device which is upper to this one. In this case, only * one master upper device can be linked, although other non-master devices @@ -5565,20 +5566,13 @@ EXPORT_SYMBOL(netdev_upper_dev_link); * counts are adjusted and the function returns zero. */ int netdev_master_upper_dev_link(struct net_device *dev, - struct net_device *upper_dev) + struct net_device *upper_dev, + void *upper_priv) { - return __netdev_upper_dev_link(dev, upper_dev, true, NULL); + return __netdev_upper_dev_link(dev, upper_dev, true, upper_priv); } EXPORT_SYMBOL(netdev_master_upper_dev_link); -int netdev_master_upper_dev_link_private(struct net_device *dev, - struct net_device *upper_dev, - void *private) -{ - return __netdev_upper_dev_link(dev, upper_dev, true, private); -} -EXPORT_SYMBOL(netdev_master_upper_dev_link_private); - /** * netdev_upper_dev_unlink - Removes a link to upper device * @dev: device diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index b327368a3848..3ee3df1edeae 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -105,7 +105,7 @@ struct vport *ovs_netdev_link(struct vport *vport, const char *name) rtnl_lock(); err = netdev_master_upper_dev_link(vport->dev, - get_dpdev(vport->dp)); + get_dpdev(vport->dp), NULL); if (err) goto error_unlock; -- cgit v1.2.3-59-g8ed1b From 29bf24afb29042f568fa67b1b0eee46796725ed2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:11 +0100 Subject: net: add possibility to pass information about upper device via notifier Sometimes the drivers and other code would find it handy to know some internal information about upper device being changed. So allow upper-code to pass information down to notifier listeners during linking. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 2 +- drivers/net/team/team.c | 2 +- drivers/net/vrf.c | 2 +- include/linux/netdevice.h | 3 ++- net/batman-adv/hard-interface.c | 2 +- net/bridge/br_if.c | 2 +- net/core/dev.c | 11 +++++++---- net/openvswitch/vport-netdev.c | 2 +- 8 files changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 924015729b2d..fa3ed1d8a12d 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1204,7 +1204,7 @@ static int bond_master_upper_dev_link(struct net_device *bond_dev, { int err; - err = netdev_master_upper_dev_link(slave_dev, bond_dev, slave); + err = netdev_master_upper_dev_link(slave_dev, bond_dev, slave, NULL); if (err) return err; slave_dev->flags |= IFF_SLAVE; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index b37f8d14dca0..f7b6ff7948b8 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1083,7 +1083,7 @@ static int team_upper_dev_link(struct net_device *dev, { int err; - err = netdev_master_upper_dev_link(port_dev, dev, NULL); + err = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL); if (err) return err; port_dev->priv_flags |= IFF_TEAM_PORT; diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 59c5bddeaedd..8944a49cda15 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -624,7 +624,7 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) goto out_fail; } - ret = netdev_master_upper_dev_link(port_dev, dev, NULL); + ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL); if (ret < 0) goto out_unregister; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 939b8f3de810..aea556c64f2c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2163,6 +2163,7 @@ struct netdev_notifier_changeupper_info { struct net_device *upper_dev; /* new upper dev */ bool master; /* is upper dev master */ bool linking; /* is the nofication for link or unlink */ + void *upper_info; /* upper dev info */ }; static inline void netdev_notifier_info_init(struct netdev_notifier_info *info, @@ -3620,7 +3621,7 @@ struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev); int netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev); int netdev_master_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, - void *upper_priv); + void *upper_priv, void *upper_info); void netdev_upper_dev_unlink(struct net_device *dev, struct net_device *upper_dev); void netdev_adjacent_rename_links(struct net_device *dev, char *oldname); diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index a7f4f1085dbb..aa8867e1d983 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -465,7 +465,7 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, bat_priv = netdev_priv(hard_iface->soft_iface); ret = netdev_master_upper_dev_link(hard_iface->net_dev, - soft_iface, NULL); + soft_iface, NULL, NULL); if (ret) goto err_dev; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 781abc34667a..8d1d4a22c50d 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -493,7 +493,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) dev->priv_flags |= IFF_BRIDGE_PORT; - err = netdev_master_upper_dev_link(dev, br->dev, NULL); + err = netdev_master_upper_dev_link(dev, br->dev, NULL, NULL); if (err) goto err5; diff --git a/net/core/dev.c b/net/core/dev.c index 27d052bb78bc..8ed886663c6d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5421,7 +5421,7 @@ static void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev, static int __netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, bool master, - void *upper_priv) + void *upper_priv, void *upper_info) { struct netdev_notifier_changeupper_info changeupper_info; struct netdev_adjacent *i, *j, *to_i, *to_j; @@ -5445,6 +5445,7 @@ static int __netdev_upper_dev_link(struct net_device *dev, changeupper_info.upper_dev = upper_dev; changeupper_info.master = master; changeupper_info.linking = true; + changeupper_info.upper_info = upper_info; ret = call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, dev, &changeupper_info.info); @@ -5549,7 +5550,7 @@ rollback_mesh: int netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev) { - return __netdev_upper_dev_link(dev, upper_dev, false, NULL); + return __netdev_upper_dev_link(dev, upper_dev, false, NULL, NULL); } EXPORT_SYMBOL(netdev_upper_dev_link); @@ -5558,6 +5559,7 @@ EXPORT_SYMBOL(netdev_upper_dev_link); * @dev: device * @upper_dev: new upper device * @upper_priv: upper device private + * @upper_info: upper info to be passed down via notifier * * Adds a link to device which is upper to this one. In this case, only * one master upper device can be linked, although other non-master devices @@ -5567,9 +5569,10 @@ EXPORT_SYMBOL(netdev_upper_dev_link); */ int netdev_master_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, - void *upper_priv) + void *upper_priv, void *upper_info) { - return __netdev_upper_dev_link(dev, upper_dev, true, upper_priv); + return __netdev_upper_dev_link(dev, upper_dev, true, + upper_priv, upper_info); } EXPORT_SYMBOL(netdev_master_upper_dev_link); diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 3ee3df1edeae..8f4dd4c39bfe 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -105,7 +105,7 @@ struct vport *ovs_netdev_link(struct vport *vport, const char *name) rtnl_lock(); err = netdev_master_upper_dev_link(vport->dev, - get_dpdev(vport->dp), NULL); + get_dpdev(vport->dp), NULL, NULL); if (err) goto error_unlock; -- cgit v1.2.3-59-g8ed1b From 8fd728566a354f7bc9cb6e781f185b8c39cf505b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:13 +0100 Subject: team: fill-up LAG changeupper info struct and pass it along Initialize netdev_lag_upper_info structure by TX type according to current team mode and pass it along via netdev_master_upper_dev_link. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 23 ++++++++++++----------- drivers/net/team/team_mode_activebackup.c | 1 + drivers/net/team/team_mode_broadcast.c | 1 + drivers/net/team/team_mode_loadbalance.c | 1 + drivers/net/team/team_mode_random.c | 1 + drivers/net/team/team_mode_roundrobin.c | 1 + include/linux/if_team.h | 1 + 7 files changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index f7b6ff7948b8..dd1504bbb4a7 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1078,23 +1078,24 @@ static void team_port_disable_netpoll(struct team_port *port) } #endif -static int team_upper_dev_link(struct net_device *dev, - struct net_device *port_dev) +static int team_upper_dev_link(struct team *team, struct team_port *port) { + struct netdev_lag_upper_info lag_upper_info; int err; - err = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL); + lag_upper_info.tx_type = team->mode->lag_tx_type; + err = netdev_master_upper_dev_link(port->dev, team->dev, NULL, + &lag_upper_info); if (err) return err; - port_dev->priv_flags |= IFF_TEAM_PORT; + port->dev->priv_flags |= IFF_TEAM_PORT; return 0; } -static void team_upper_dev_unlink(struct net_device *dev, - struct net_device *port_dev) +static void team_upper_dev_unlink(struct team *team, struct team_port *port) { - netdev_upper_dev_unlink(port_dev, dev); - port_dev->priv_flags &= ~IFF_TEAM_PORT; + netdev_upper_dev_unlink(port->dev, team->dev); + port->dev->priv_flags &= ~IFF_TEAM_PORT; } static void __team_port_change_port_added(struct team_port *port, bool linkup); @@ -1194,7 +1195,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev) goto err_handler_register; } - err = team_upper_dev_link(dev, port_dev); + err = team_upper_dev_link(team, port); if (err) { netdev_err(dev, "Device %s failed to set upper link\n", portname); @@ -1220,7 +1221,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev) return 0; err_option_port_add: - team_upper_dev_unlink(dev, port_dev); + team_upper_dev_unlink(team, port); err_set_upper_link: netdev_rx_handler_unregister(port_dev); @@ -1264,7 +1265,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) team_port_disable(team, port); list_del_rcu(&port->list); - team_upper_dev_unlink(dev, port_dev); + team_upper_dev_unlink(team, port); netdev_rx_handler_unregister(port_dev); team_port_disable_netpoll(port); vlan_vids_del_by_dev(port_dev, dev); diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index 40fd3381b693..3f189823ba3b 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c @@ -127,6 +127,7 @@ static const struct team_mode ab_mode = { .owner = THIS_MODULE, .priv_size = sizeof(struct ab_priv), .ops = &ab_mode_ops, + .lag_tx_type = NETDEV_LAG_TX_TYPE_ACTIVEBACKUP, }; static int __init ab_init_module(void) diff --git a/drivers/net/team/team_mode_broadcast.c b/drivers/net/team/team_mode_broadcast.c index c366cd299c06..302ff35b0cbc 100644 --- a/drivers/net/team/team_mode_broadcast.c +++ b/drivers/net/team/team_mode_broadcast.c @@ -56,6 +56,7 @@ static const struct team_mode bc_mode = { .kind = "broadcast", .owner = THIS_MODULE, .ops = &bc_mode_ops, + .lag_tx_type = NETDEV_LAG_TX_TYPE_BROADCAST, }; static int __init bc_init_module(void) diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index a1536d0d83a9..cdb19b385d42 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -661,6 +661,7 @@ static const struct team_mode lb_mode = { .priv_size = sizeof(struct lb_priv), .port_priv_size = sizeof(struct lb_port_priv), .ops = &lb_mode_ops, + .lag_tx_type = NETDEV_LAG_TX_TYPE_HASH, }; static int __init lb_init_module(void) diff --git a/drivers/net/team/team_mode_random.c b/drivers/net/team/team_mode_random.c index cd2f692b8074..215f845782db 100644 --- a/drivers/net/team/team_mode_random.c +++ b/drivers/net/team/team_mode_random.c @@ -46,6 +46,7 @@ static const struct team_mode rnd_mode = { .kind = "random", .owner = THIS_MODULE, .ops = &rnd_mode_ops, + .lag_tx_type = NETDEV_LAG_TX_TYPE_RANDOM, }; static int __init rnd_init_module(void) diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index 53665850b59e..0aa234118c03 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -58,6 +58,7 @@ static const struct team_mode rr_mode = { .owner = THIS_MODULE, .priv_size = sizeof(struct rr_priv), .ops = &rr_mode_ops, + .lag_tx_type = NETDEV_LAG_TX_TYPE_ROUNDROBIN, }; static int __init rr_init_module(void) diff --git a/include/linux/if_team.h b/include/linux/if_team.h index a6aa970758a2..b84e49c3a738 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -164,6 +164,7 @@ struct team_mode { size_t priv_size; size_t port_priv_size; const struct team_mode_ops *ops; + enum netdev_lag_tx_type lag_tx_type; }; #define TEAM_PORT_HASHBITS 4 -- cgit v1.2.3-59-g8ed1b From 41f0b0496466d6e0e8245f94a79889234cde5e3c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:14 +0100 Subject: bonding: fill-up LAG changeupper info struct and pass it along Initialize netdev_lag_upper_info structure by TX type according to current bonding mode and pass it along via netdev_master_upper_dev_link. Signed-off-by: Jiri Pirko Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 45 ++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index fa3ed1d8a12d..e94e79ad757c 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1198,26 +1198,43 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) return ret; } -static int bond_master_upper_dev_link(struct net_device *bond_dev, - struct net_device *slave_dev, - struct slave *slave) +static enum netdev_lag_tx_type bond_lag_tx_type(struct bonding *bond) { + switch (BOND_MODE(bond)) { + case BOND_MODE_ROUNDROBIN: + return NETDEV_LAG_TX_TYPE_ROUNDROBIN; + case BOND_MODE_ACTIVEBACKUP: + return NETDEV_LAG_TX_TYPE_ACTIVEBACKUP; + case BOND_MODE_BROADCAST: + return NETDEV_LAG_TX_TYPE_BROADCAST; + case BOND_MODE_XOR: + case BOND_MODE_8023AD: + return NETDEV_LAG_TX_TYPE_HASH; + default: + return NETDEV_LAG_TX_TYPE_UNKNOWN; + } +} + +static int bond_master_upper_dev_link(struct bonding *bond, struct slave *slave) +{ + struct netdev_lag_upper_info lag_upper_info; int err; - err = netdev_master_upper_dev_link(slave_dev, bond_dev, slave, NULL); + lag_upper_info.tx_type = bond_lag_tx_type(bond); + err = netdev_master_upper_dev_link(slave->dev, bond->dev, slave, + &lag_upper_info); if (err) return err; - slave_dev->flags |= IFF_SLAVE; - rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL); + slave->dev->flags |= IFF_SLAVE; + rtmsg_ifinfo(RTM_NEWLINK, slave->dev, IFF_SLAVE, GFP_KERNEL); return 0; } -static void bond_upper_dev_unlink(struct net_device *bond_dev, - struct net_device *slave_dev) +static void bond_upper_dev_unlink(struct bonding *bond, struct slave *slave) { - netdev_upper_dev_unlink(slave_dev, bond_dev); - slave_dev->flags &= ~IFF_SLAVE; - rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL); + netdev_upper_dev_unlink(slave->dev, bond->dev); + slave->dev->flags &= ~IFF_SLAVE; + rtmsg_ifinfo(RTM_NEWLINK, slave->dev, IFF_SLAVE, GFP_KERNEL); } static struct slave *bond_alloc_slave(struct bonding *bond) @@ -1662,7 +1679,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_detach; } - res = bond_master_upper_dev_link(bond_dev, slave_dev, new_slave); + res = bond_master_upper_dev_link(bond, new_slave); if (res) { netdev_dbg(bond_dev, "Error %d calling bond_master_upper_dev_link\n", res); goto err_unregister; @@ -1698,7 +1715,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) /* Undo stages on error */ err_upper_unlink: - bond_upper_dev_unlink(bond_dev, slave_dev); + bond_upper_dev_unlink(bond, new_slave); err_unregister: netdev_rx_handler_unregister(slave_dev); @@ -1804,7 +1821,7 @@ static int __bond_release_one(struct net_device *bond_dev, /* recompute stats just before removing the slave */ bond_get_stats(bond->dev, &bond->bond_stats); - bond_upper_dev_unlink(bond_dev, slave_dev); + bond_upper_dev_unlink(bond, slave); /* unregister rx_handler early so bond_handle_frame wouldn't be called * for this slave anymore. */ -- cgit v1.2.3-59-g8ed1b From 7d259505299cb6f4642eac4eebb191d6c2ebe558 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:17 +0100 Subject: team: rtnl_lock for options set During options set, there will be needed to hold rtnl_mutex in order to safely call netdev notifiers. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index dd1504bbb4a7..98141338d0ce 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2422,9 +2422,13 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) struct nlattr *nl_option; LIST_HEAD(opt_inst_list); + rtnl_lock(); + team = team_nl_team_get(info); - if (!team) - return -EINVAL; + if (!team) { + err = -EINVAL; + goto rtnl_unlock; + } err = -EINVAL; if (!info->attrs[TEAM_ATTR_LIST_OPTION]) { @@ -2551,7 +2555,8 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) team_put: team_nl_team_put(team); - +rtnl_unlock: + rtnl_unlock(); return err; } -- cgit v1.2.3-59-g8ed1b From 3a0d6c5af5e52622a58c68d7c276db9ec38ee2a4 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:18 +0100 Subject: team: implement lower state change propagation Let netdev notifier listeners know about link-up and port-enable state changes. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 98141338d0ce..059c0f60a2b2 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -91,10 +91,24 @@ void team_modeop_port_change_dev_addr(struct team *team, } EXPORT_SYMBOL(team_modeop_port_change_dev_addr); +static void team_lower_state_changed(struct team_port *port) +{ + struct netdev_lag_lower_state_info info; + + info.link_up = port->linkup; + info.tx_enabled = team_port_enabled(port); + netdev_lower_state_changed(port->dev, &info); +} + static void team_refresh_port_linkup(struct team_port *port) { - port->linkup = port->user.linkup_enabled ? port->user.linkup : - port->state.linkup; + bool new_linkup = port->user.linkup_enabled ? port->user.linkup : + port->state.linkup; + + if (port->linkup != new_linkup) { + port->linkup = new_linkup; + team_lower_state_changed(port); + } } @@ -932,6 +946,7 @@ static void team_port_enable(struct team *team, team->ops.port_enabled(team, port); team_notify_peers(team); team_mcast_rejoin(team); + team_lower_state_changed(port); } static void __reconstruct_port_hlist(struct team *team, int rm_index) @@ -963,6 +978,7 @@ static void team_port_disable(struct team *team, team_adjust_ops(team); team_notify_peers(team); team_mcast_rejoin(team); + team_lower_state_changed(port); } #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ -- cgit v1.2.3-59-g8ed1b From 5d397061ca2081d8a99e4bee5792122faa6aaf86 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:19 +0100 Subject: bonding: allow notifications for bond_set_slave_link_state Similar to state notifications. We allow caller to indicate if the notification should happen now or later, depending on if he holds rtnl mutex or not. Introduce bond_slave_link_notify function (similar to bond_slave_state_notify) which is later on called with rtnl mutex and goes over slaves and executes delayed notification. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 54 +++++++++++++++++++++++++++-------------- include/net/bonding.h | 32 +++++++++++++++++++++--- 2 files changed, 65 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e94e79ad757c..7695490061ec 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -830,7 +830,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) } new_active->delay = 0; - bond_set_slave_link_state(new_active, BOND_LINK_UP); + bond_set_slave_link_state(new_active, BOND_LINK_UP, + BOND_SLAVE_NOTIFY_NOW); if (BOND_MODE(bond) == BOND_MODE_8023AD) bond_3ad_handle_link_change(new_active, BOND_LINK_UP); @@ -1580,21 +1581,26 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) if (bond_check_dev_link(bond, slave_dev, 0) == BMSR_LSTATUS) { if (bond->params.updelay) { bond_set_slave_link_state(new_slave, - BOND_LINK_BACK); + BOND_LINK_BACK, + BOND_SLAVE_NOTIFY_NOW); new_slave->delay = bond->params.updelay; } else { bond_set_slave_link_state(new_slave, - BOND_LINK_UP); + BOND_LINK_UP, + BOND_SLAVE_NOTIFY_NOW); } } else { - bond_set_slave_link_state(new_slave, BOND_LINK_DOWN); + bond_set_slave_link_state(new_slave, BOND_LINK_DOWN, + BOND_SLAVE_NOTIFY_NOW); } } else if (bond->params.arp_interval) { bond_set_slave_link_state(new_slave, (netif_carrier_ok(slave_dev) ? - BOND_LINK_UP : BOND_LINK_DOWN)); + BOND_LINK_UP : BOND_LINK_DOWN), + BOND_SLAVE_NOTIFY_NOW); } else { - bond_set_slave_link_state(new_slave, BOND_LINK_UP); + bond_set_slave_link_state(new_slave, BOND_LINK_UP, + BOND_SLAVE_NOTIFY_NOW); } if (new_slave->link != BOND_LINK_DOWN) @@ -2013,7 +2019,8 @@ static int bond_miimon_inspect(struct bonding *bond) if (link_state) continue; - bond_set_slave_link_state(slave, BOND_LINK_FAIL); + bond_set_slave_link_state(slave, BOND_LINK_FAIL, + BOND_SLAVE_NOTIFY_LATER); slave->delay = bond->params.downdelay; if (slave->delay) { netdev_info(bond->dev, "link status down for %sinterface %s, disabling it in %d ms\n", @@ -2028,7 +2035,8 @@ static int bond_miimon_inspect(struct bonding *bond) case BOND_LINK_FAIL: if (link_state) { /* recovered before downdelay expired */ - bond_set_slave_link_state(slave, BOND_LINK_UP); + bond_set_slave_link_state(slave, BOND_LINK_UP, + BOND_SLAVE_NOTIFY_LATER); slave->last_link_up = jiffies; netdev_info(bond->dev, "link status up again after %d ms for interface %s\n", (bond->params.downdelay - slave->delay) * @@ -2050,7 +2058,8 @@ static int bond_miimon_inspect(struct bonding *bond) if (!link_state) continue; - bond_set_slave_link_state(slave, BOND_LINK_BACK); + bond_set_slave_link_state(slave, BOND_LINK_BACK, + BOND_SLAVE_NOTIFY_LATER); slave->delay = bond->params.updelay; if (slave->delay) { @@ -2064,7 +2073,8 @@ static int bond_miimon_inspect(struct bonding *bond) case BOND_LINK_BACK: if (!link_state) { bond_set_slave_link_state(slave, - BOND_LINK_DOWN); + BOND_LINK_DOWN, + BOND_SLAVE_NOTIFY_LATER); netdev_info(bond->dev, "link status down again after %d ms for interface %s\n", (bond->params.updelay - slave->delay) * bond->params.miimon, @@ -2102,7 +2112,8 @@ static void bond_miimon_commit(struct bonding *bond) continue; case BOND_LINK_UP: - bond_set_slave_link_state(slave, BOND_LINK_UP); + bond_set_slave_link_state(slave, BOND_LINK_UP, + BOND_SLAVE_NOTIFY_NOW); slave->last_link_up = jiffies; primary = rtnl_dereference(bond->primary_slave); @@ -2142,7 +2153,8 @@ static void bond_miimon_commit(struct bonding *bond) if (slave->link_failure_count < UINT_MAX) slave->link_failure_count++; - bond_set_slave_link_state(slave, BOND_LINK_DOWN); + bond_set_slave_link_state(slave, BOND_LINK_DOWN, + BOND_SLAVE_NOTIFY_NOW); if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP || BOND_MODE(bond) == BOND_MODE_8023AD) @@ -2725,7 +2737,8 @@ static void bond_ab_arp_commit(struct bonding *bond) struct slave *current_arp_slave; current_arp_slave = rtnl_dereference(bond->current_arp_slave); - bond_set_slave_link_state(slave, BOND_LINK_UP); + bond_set_slave_link_state(slave, BOND_LINK_UP, + BOND_SLAVE_NOTIFY_NOW); if (current_arp_slave) { bond_set_slave_inactive_flags( current_arp_slave, @@ -2748,7 +2761,8 @@ static void bond_ab_arp_commit(struct bonding *bond) if (slave->link_failure_count < UINT_MAX) slave->link_failure_count++; - bond_set_slave_link_state(slave, BOND_LINK_DOWN); + bond_set_slave_link_state(slave, BOND_LINK_DOWN, + BOND_SLAVE_NOTIFY_NOW); bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW); @@ -2827,7 +2841,8 @@ static bool bond_ab_arp_probe(struct bonding *bond) * up when it is actually down */ if (!bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) { - bond_set_slave_link_state(slave, BOND_LINK_DOWN); + bond_set_slave_link_state(slave, BOND_LINK_DOWN, + BOND_SLAVE_NOTIFY_LATER); if (slave->link_failure_count < UINT_MAX) slave->link_failure_count++; @@ -2847,7 +2862,8 @@ static bool bond_ab_arp_probe(struct bonding *bond) if (!new_slave) goto check_state; - bond_set_slave_link_state(new_slave, BOND_LINK_BACK); + bond_set_slave_link_state(new_slave, BOND_LINK_BACK, + BOND_SLAVE_NOTIFY_LATER); bond_set_slave_active_flags(new_slave, BOND_SLAVE_NOTIFY_LATER); bond_arp_send_all(bond, new_slave); new_slave->last_link_up = jiffies; @@ -2855,7 +2871,7 @@ static bool bond_ab_arp_probe(struct bonding *bond) check_state: bond_for_each_slave_rcu(bond, slave, iter) { - if (slave->should_notify) { + if (slave->should_notify || slave->should_notify_link) { should_notify_rtnl = BOND_SLAVE_NOTIFY_NOW; break; } @@ -2910,8 +2926,10 @@ re_arm: if (should_notify_peers) call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, bond->dev); - if (should_notify_rtnl) + if (should_notify_rtnl) { bond_slave_state_notify(bond); + bond_slave_link_notify(bond); + } rtnl_unlock(); } diff --git a/include/net/bonding.h b/include/net/bonding.h index c1740a2794a3..1df437715e2f 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -165,7 +165,8 @@ struct slave { u8 backup:1, /* indicates backup slave. Value corresponds with BOND_STATE_ACTIVE and BOND_STATE_BACKUP */ inactive:1, /* indicates inactive slave */ - should_notify:1; /* indicateds whether the state changed */ + should_notify:1, /* indicates whether the state changed */ + should_notify_link:1; /* indicates whether the link changed */ u8 duplex; u32 original_mtu; u32 link_failure_count; @@ -504,10 +505,35 @@ static inline bool bond_is_slave_inactive(struct slave *slave) return slave->inactive; } -static inline void bond_set_slave_link_state(struct slave *slave, int state) +static inline void bond_set_slave_link_state(struct slave *slave, int state, + bool notify) { + if (slave->link == state) + return; + slave->link = state; - bond_queue_slave_event(slave); + if (notify) { + bond_queue_slave_event(slave); + slave->should_notify_link = 0; + } else { + if (slave->should_notify_link) + slave->should_notify_link = 0; + else + slave->should_notify_link = 1; + } +} + +static inline void bond_slave_link_notify(struct bonding *bond) +{ + struct list_head *iter; + struct slave *tmp; + + bond_for_each_slave(bond, tmp, iter) { + if (tmp->should_notify_link) { + bond_queue_slave_event(tmp); + tmp->should_notify_link = 0; + } + } } static inline __be32 bond_confirm_addr(struct net_device *dev, __be32 dst, __be32 local) -- cgit v1.2.3-59-g8ed1b From f7c7eb7f7af7f87e0fc150994785fd139576e43a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:20 +0100 Subject: bonding: implement lower state change propagation Let netdev notifier listeners know about link and slave state change. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 10 ++++++++++ include/net/bonding.h | 7 +++++++ 2 files changed, 17 insertions(+) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 7695490061ec..2f1145063c60 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1317,6 +1317,16 @@ void bond_queue_slave_event(struct slave *slave) queue_delayed_work(slave->bond->wq, &nnw->work, 0); } +void bond_lower_state_changed(struct slave *slave) +{ + struct netdev_lag_lower_state_info info; + + info.link_up = slave->link == BOND_LINK_UP || + slave->link == BOND_LINK_FAIL; + info.tx_enabled = bond_is_active_slave(slave); + netdev_lower_state_changed(slave->dev, &info); +} + /* enslave device to bond device */ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) { diff --git a/include/net/bonding.h b/include/net/bonding.h index 1df437715e2f..ee6c52053aa3 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -247,6 +247,7 @@ struct bonding { ((struct slave *) rtnl_dereference(dev->rx_handler_data)) void bond_queue_slave_event(struct slave *slave); +void bond_lower_state_changed(struct slave *slave); struct bond_vlan_tag { __be16 vlan_proto; @@ -328,6 +329,7 @@ static inline void bond_set_active_slave(struct slave *slave) if (slave->backup) { slave->backup = 0; bond_queue_slave_event(slave); + bond_lower_state_changed(slave); rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC); } } @@ -337,6 +339,7 @@ static inline void bond_set_backup_slave(struct slave *slave) if (!slave->backup) { slave->backup = 1; bond_queue_slave_event(slave); + bond_lower_state_changed(slave); rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC); } } @@ -349,6 +352,7 @@ static inline void bond_set_slave_state(struct slave *slave, slave->backup = slave_state; if (notify) { + bond_lower_state_changed(slave); rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC); bond_queue_slave_event(slave); slave->should_notify = 0; @@ -380,6 +384,7 @@ static inline void bond_slave_state_notify(struct bonding *bond) bond_for_each_slave(bond, tmp, iter) { if (tmp->should_notify) { + bond_lower_state_changed(tmp); rtmsg_ifinfo(RTM_NEWLINK, tmp->dev, 0, GFP_ATOMIC); tmp->should_notify = 0; } @@ -514,6 +519,7 @@ static inline void bond_set_slave_link_state(struct slave *slave, int state, slave->link = state; if (notify) { bond_queue_slave_event(slave); + bond_lower_state_changed(slave); slave->should_notify_link = 0; } else { if (slave->should_notify_link) @@ -531,6 +537,7 @@ static inline void bond_slave_link_notify(struct bonding *bond) bond_for_each_slave(bond, tmp, iter) { if (tmp->should_notify_link) { bond_queue_slave_event(tmp); + bond_lower_state_changed(tmp); tmp->should_notify_link = 0; } } -- cgit v1.2.3-59-g8ed1b From 57beaca8ecd77c85087270ef15ff319767994f6d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:21 +0100 Subject: bonding: set inactive flags on release Be correct and symmetric to enslave and set inactive flags during release. That gives LAG offload drivers - lower state change listeners - possibility to do proper cleanup. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 2f1145063c60..5a7de43a09f8 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1832,6 +1832,8 @@ static int __bond_release_one(struct net_device *bond_dev, return -EINVAL; } + bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW); + bond_sysfs_slave_del(slave); /* recompute stats just before removing the slave */ -- cgit v1.2.3-59-g8ed1b From c5b9b518adab8641d9e718e629c88698cc35e7fa Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:22 +0100 Subject: mlxsw: spectrum: Add set_rx_mode ndo stub Add just a stub for now. This allows to pass check in dev_ifsioc, SIOCADDMULTI and SIOCDELMULTI cases. Teamd is using these to add LACP slow MAC. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 14a9a9ff89ed..a397cc18693d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -417,6 +417,10 @@ static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } +static void mlxsw_sp_set_rx_mode(struct net_device *dev) +{ +} + static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); @@ -725,6 +729,7 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = { .ndo_open = mlxsw_sp_port_open, .ndo_stop = mlxsw_sp_port_stop, .ndo_start_xmit = mlxsw_sp_port_xmit, + .ndo_set_rx_mode = mlxsw_sp_set_rx_mode, .ndo_set_mac_address = mlxsw_sp_port_set_mac_address, .ndo_change_mtu = mlxsw_sp_port_change_mtu, .ndo_get_stats64 = mlxsw_sp_port_get_stats64, -- cgit v1.2.3-59-g8ed1b From 8060646a0fd1b1685a16694f2eb2f461e5eb46cc Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:23 +0100 Subject: mlxsw: core: Add support for packets received from LAG port Lower layer (pci) has information if the packet is received via LAG port. If that is the case, it fills up rx_info accordingly. However upper layer does not care about lag_id/port_index for received packets so convert it to local_port before passing it up. For that conversion, lag mapping array is introduced. Upper layer is responsible for setting up the mapping according to what is set in HW. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 79 ++++++++++++++++++++++++++++-- drivers/net/ethernet/mellanox/mlxsw/core.h | 14 +++++- drivers/net/ethernet/mellanox/mlxsw/pci.c | 4 +- 3 files changed, 92 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 1ecb4aabbdc7..af8a48b3b3ad 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -105,6 +105,9 @@ struct mlxsw_core { struct debugfs_blob_wrapper vsd_blob; struct debugfs_blob_wrapper psid_blob; } dbg; + struct { + u8 *mapping; /* lag_id+port_index to local_port mapping */ + } lag; struct mlxsw_hwmon *hwmon; unsigned long driver_priv[0]; /* driver_priv has to be always the last item */ @@ -815,6 +818,17 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, goto err_alloc_stats; } + if (mlxsw_driver->profile->used_max_lag && + mlxsw_driver->profile->used_max_port_per_lag) { + alloc_size = sizeof(u8) * mlxsw_driver->profile->max_lag * + mlxsw_driver->profile->max_port_per_lag; + mlxsw_core->lag.mapping = kzalloc(alloc_size, GFP_KERNEL); + if (!mlxsw_core->lag.mapping) { + err = -ENOMEM; + goto err_alloc_lag_mapping; + } + } + err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile); if (err) goto err_bus_init; @@ -847,6 +861,8 @@ err_hwmon_init: err_emad_init: mlxsw_bus->fini(bus_priv); err_bus_init: + kfree(mlxsw_core->lag.mapping); +err_alloc_lag_mapping: free_percpu(mlxsw_core->pcpu_stats); err_alloc_stats: kfree(mlxsw_core); @@ -865,6 +881,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core) mlxsw_hwmon_fini(mlxsw_core->hwmon); mlxsw_emad_fini(mlxsw_core); mlxsw_core->bus->fini(mlxsw_core->bus_priv); + kfree(mlxsw_core->lag.mapping); free_percpu(mlxsw_core->pcpu_stats); kfree(mlxsw_core); mlxsw_core_driver_put(device_kind); @@ -1196,11 +1213,25 @@ void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb, struct mlxsw_rx_listener_item *rxl_item; const struct mlxsw_rx_listener *rxl; struct mlxsw_core_pcpu_stats *pcpu_stats; - u8 local_port = rx_info->sys_port; + u8 local_port; bool found = false; - dev_dbg_ratelimited(mlxsw_core->bus_info->dev, "%s: sys_port = %d, trap_id = 0x%x\n", - __func__, rx_info->sys_port, rx_info->trap_id); + if (rx_info->is_lag) { + dev_dbg_ratelimited(mlxsw_core->bus_info->dev, "%s: lag_id = %d, lag_port_index = 0x%x\n", + __func__, rx_info->u.lag_id, + rx_info->trap_id); + /* Upper layer does not care if the skb came from LAG or not, + * so just get the local_port for the lag port and push it up. + */ + local_port = mlxsw_core_lag_mapping_get(mlxsw_core, + rx_info->u.lag_id, + rx_info->lag_port_index); + } else { + local_port = rx_info->u.sys_port; + } + + dev_dbg_ratelimited(mlxsw_core->bus_info->dev, "%s: local_port = %d, trap_id = 0x%x\n", + __func__, local_port, rx_info->trap_id); if ((rx_info->trap_id >= MLXSW_TRAP_ID_MAX) || (local_port >= MLXSW_PORT_MAX_PORTS)) @@ -1244,6 +1275,48 @@ drop: } EXPORT_SYMBOL(mlxsw_core_skb_receive); +static int mlxsw_core_lag_mapping_index(struct mlxsw_core *mlxsw_core, + u16 lag_id, u8 port_index) +{ + return mlxsw_core->driver->profile->max_port_per_lag * lag_id + + port_index; +} + +void mlxsw_core_lag_mapping_set(struct mlxsw_core *mlxsw_core, + u16 lag_id, u8 port_index, u8 local_port) +{ + int index = mlxsw_core_lag_mapping_index(mlxsw_core, + lag_id, port_index); + + mlxsw_core->lag.mapping[index] = local_port; +} +EXPORT_SYMBOL(mlxsw_core_lag_mapping_set); + +u8 mlxsw_core_lag_mapping_get(struct mlxsw_core *mlxsw_core, + u16 lag_id, u8 port_index) +{ + int index = mlxsw_core_lag_mapping_index(mlxsw_core, + lag_id, port_index); + + return mlxsw_core->lag.mapping[index]; +} +EXPORT_SYMBOL(mlxsw_core_lag_mapping_get); + +void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core, + u16 lag_id, u8 local_port) +{ + int i; + + for (i = 0; i < mlxsw_core->driver->profile->max_port_per_lag; i++) { + int index = mlxsw_core_lag_mapping_index(mlxsw_core, + lag_id, i); + + if (mlxsw_core->lag.mapping[index] == local_port) + mlxsw_core->lag.mapping[index] = 0; + } +} +EXPORT_SYMBOL(mlxsw_core_lag_mapping_clear); + int mlxsw_cmd_exec(struct mlxsw_core *mlxsw_core, u16 opcode, u8 opcode_mod, u32 in_mod, bool out_mbox_direct, char *in_mbox, size_t in_mbox_size, diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 5ac952956d55..4833fb33ce07 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -112,13 +112,25 @@ int mlxsw_reg_write(struct mlxsw_core *mlxsw_core, const struct mlxsw_reg_info *reg, char *payload); struct mlxsw_rx_info { - u16 sys_port; + bool is_lag; + union { + u16 sys_port; + u16 lag_id; + } u; + u8 lag_port_index; int trap_id; }; void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb, struct mlxsw_rx_info *rx_info); +void mlxsw_core_lag_mapping_set(struct mlxsw_core *mlxsw_core, + u16 lag_id, u8 port_index, u8 local_port); +u8 mlxsw_core_lag_mapping_get(struct mlxsw_core *mlxsw_core, + u16 lag_id, u8 port_index); +void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core, + u16 lag_id, u8 local_port); + #define MLXSW_CONFIG_PROFILE_SWID_COUNT 8 struct mlxsw_swid_config { diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index de69e719dc9d..8ca66a01b097 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -690,7 +690,9 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci, if (mlxsw_pci_cqe_lag_get(cqe)) goto drop; - rx_info.sys_port = mlxsw_pci_cqe_system_port_get(cqe); + rx_info.is_lag = false; + rx_info.u.sys_port = mlxsw_pci_cqe_system_port_get(cqe); + rx_info.trap_id = mlxsw_pci_cqe_trap_id_get(cqe); byte_count = mlxsw_pci_cqe_byte_count_get(cqe); -- cgit v1.2.3-59-g8ed1b From d2292e8761be2346732e3cc562ad2f38a19c325e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:24 +0100 Subject: mlxsw: pci: Implement LAG processing for received packets Completion queue element for receive queue provides information if the packet was received via LAG port. Extract this info and pass it along to core. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/pci.c | 19 ++++++++----------- drivers/net/ethernet/mellanox/mlxsw/pci.h | 4 +++- 2 files changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index 8ca66a01b097..d2102e572b1d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -686,12 +686,14 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci, if (q->consumer_counter++ != consumer_counter_limit) dev_dbg_ratelimited(&pdev->dev, "Consumer counter does not match limit in RDQ\n"); - /* We do not support lag now */ - if (mlxsw_pci_cqe_lag_get(cqe)) - goto drop; - - rx_info.is_lag = false; - rx_info.u.sys_port = mlxsw_pci_cqe_system_port_get(cqe); + if (mlxsw_pci_cqe_lag_get(cqe)) { + rx_info.is_lag = true; + rx_info.u.lag_id = mlxsw_pci_cqe_lag_id_get(cqe); + rx_info.lag_port_index = mlxsw_pci_cqe_lag_port_index_get(cqe); + } else { + rx_info.is_lag = false; + rx_info.u.sys_port = mlxsw_pci_cqe_system_port_get(cqe); + } rx_info.trap_id = mlxsw_pci_cqe_trap_id_get(cqe); @@ -701,7 +703,6 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci, skb_put(skb, byte_count); mlxsw_core_skb_receive(mlxsw_pci->core, skb, &rx_info); -put_new_skb: memset(wqe, 0, q->elem_size); err = mlxsw_pci_rdq_skb_alloc(mlxsw_pci, elem_info); if (err) @@ -710,10 +711,6 @@ put_new_skb: q->producer_counter++; mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q); return; - -drop: - dev_kfree_skb_any(skb); - goto put_new_skb; } static char *mlxsw_pci_cq_sw_cqe_get(struct mlxsw_pci_queue *q) diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h index 142f33d978c5..912106054ff2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.h +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h @@ -129,13 +129,15 @@ MLXSW_ITEM64_INDEXED(pci, wqe, address, 0x08, 0, 64, 0x8, 0x0, false); */ MLXSW_ITEM32(pci, cqe, lag, 0x00, 23, 1); -/* pci_cqe_system_port +/* pci_cqe_system_port/lag_id * When lag=0: System port on which the packet was received * When lag=1: * bits [15:4] LAG ID on which the packet was received * bits [3:0] sub_port on which the packet was received */ MLXSW_ITEM32(pci, cqe, system_port, 0x00, 0, 16); +MLXSW_ITEM32(pci, cqe, lag_id, 0x00, 4, 12); +MLXSW_ITEM32(pci, cqe, lag_port_index, 0x00, 0, 4); /* pci_cqe_wqe_counter * WQE count of the WQEs completed on the associated dqn -- cgit v1.2.3-59-g8ed1b From d1d40be084c60f2ba2ca369dedd27b12ae840aa1 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:25 +0100 Subject: mlxsw: reg: Add link aggregation configuration registers definitions Add definitions of SLDR, SLCR2, SLCOR registers that are used to configure LAG. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 293 ++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index f89419393a7f..049abbb8bcc2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -865,6 +865,293 @@ static inline void mlxsw_reg_sftr_pack(char *payload, mlxsw_reg_sftr_port_mask_set(payload, port, 1); } +/* SLDR - Switch LAG Descriptor Register + * ----------------------------------------- + * The switch LAG descriptor register is populated by LAG descriptors. + * Each LAG descriptor is indexed by lag_id. The LAG ID runs from 0 to + * max_lag-1. + */ +#define MLXSW_REG_SLDR_ID 0x2014 +#define MLXSW_REG_SLDR_LEN 0x0C /* counting in only one port in list */ + +static const struct mlxsw_reg_info mlxsw_reg_sldr = { + .id = MLXSW_REG_SLDR_ID, + .len = MLXSW_REG_SLDR_LEN, +}; + +enum mlxsw_reg_sldr_op { + /* Indicates a creation of a new LAG-ID, lag_id must be valid */ + MLXSW_REG_SLDR_OP_LAG_CREATE, + MLXSW_REG_SLDR_OP_LAG_DESTROY, + /* Ports that appear in the list have the Distributor enabled */ + MLXSW_REG_SLDR_OP_LAG_ADD_PORT_LIST, + /* Removes ports from the disributor list */ + MLXSW_REG_SLDR_OP_LAG_REMOVE_PORT_LIST, +}; + +/* reg_sldr_op + * Operation. + * Access: RW + */ +MLXSW_ITEM32(reg, sldr, op, 0x00, 29, 3); + +/* reg_sldr_lag_id + * LAG identifier. The lag_id is the index into the LAG descriptor table. + * Access: Index + */ +MLXSW_ITEM32(reg, sldr, lag_id, 0x00, 0, 10); + +static inline void mlxsw_reg_sldr_lag_create_pack(char *payload, u8 lag_id) +{ + MLXSW_REG_ZERO(sldr, payload); + mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_CREATE); + mlxsw_reg_sldr_lag_id_set(payload, lag_id); +} + +static inline void mlxsw_reg_sldr_lag_destroy_pack(char *payload, u8 lag_id) +{ + MLXSW_REG_ZERO(sldr, payload); + mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_DESTROY); + mlxsw_reg_sldr_lag_id_set(payload, lag_id); +} + +/* reg_sldr_num_ports + * The number of member ports of the LAG. + * Reserved for Create / Destroy operations + * For Add / Remove operations - indicates the number of ports in the list. + * Access: RW + */ +MLXSW_ITEM32(reg, sldr, num_ports, 0x04, 24, 8); + +/* reg_sldr_system_port + * System port. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sldr, system_port, 0x08, 0, 16, 4, 0, false); + +static inline void mlxsw_reg_sldr_lag_add_port_pack(char *payload, u8 lag_id, + u8 local_port) +{ + MLXSW_REG_ZERO(sldr, payload); + mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_ADD_PORT_LIST); + mlxsw_reg_sldr_lag_id_set(payload, lag_id); + mlxsw_reg_sldr_num_ports_set(payload, 1); + mlxsw_reg_sldr_system_port_set(payload, 0, local_port); +} + +static inline void mlxsw_reg_sldr_lag_remove_port_pack(char *payload, u8 lag_id, + u8 local_port) +{ + MLXSW_REG_ZERO(sldr, payload); + mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_REMOVE_PORT_LIST); + mlxsw_reg_sldr_lag_id_set(payload, lag_id); + mlxsw_reg_sldr_num_ports_set(payload, 1); + mlxsw_reg_sldr_system_port_set(payload, 0, local_port); +} + +/* SLCR - Switch LAG Configuration 2 Register + * ------------------------------------------- + * The Switch LAG Configuration register is used for configuring the + * LAG properties of the switch. + */ +#define MLXSW_REG_SLCR_ID 0x2015 +#define MLXSW_REG_SLCR_LEN 0x10 + +static const struct mlxsw_reg_info mlxsw_reg_slcr = { + .id = MLXSW_REG_SLCR_ID, + .len = MLXSW_REG_SLCR_LEN, +}; + +enum mlxsw_reg_slcr_pp { + /* Global Configuration (for all ports) */ + MLXSW_REG_SLCR_PP_GLOBAL, + /* Per port configuration, based on local_port field */ + MLXSW_REG_SLCR_PP_PER_PORT, +}; + +/* reg_slcr_pp + * Per Port Configuration + * Note: Reading at Global mode results in reading port 1 configuration. + * Access: Index + */ +MLXSW_ITEM32(reg, slcr, pp, 0x00, 24, 1); + +/* reg_slcr_local_port + * Local port number + * Supported from CPU port + * Not supported from router port + * Reserved when pp = Global Configuration + * Access: Index + */ +MLXSW_ITEM32(reg, slcr, local_port, 0x00, 16, 8); + +enum mlxsw_reg_slcr_type { + MLXSW_REG_SLCR_TYPE_CRC, /* default */ + MLXSW_REG_SLCR_TYPE_XOR, + MLXSW_REG_SLCR_TYPE_RANDOM, +}; + +/* reg_slcr_type + * Hash type + * Access: RW + */ +MLXSW_ITEM32(reg, slcr, type, 0x00, 0, 4); + +/* Ingress port */ +#define MLXSW_REG_SLCR_LAG_HASH_IN_PORT BIT(0) +/* SMAC - for IPv4 and IPv6 packets */ +#define MLXSW_REG_SLCR_LAG_HASH_SMAC_IP BIT(1) +/* SMAC - for non-IP packets */ +#define MLXSW_REG_SLCR_LAG_HASH_SMAC_NONIP BIT(2) +#define MLXSW_REG_SLCR_LAG_HASH_SMAC \ + (MLXSW_REG_SLCR_LAG_HASH_SMAC_IP | \ + MLXSW_REG_SLCR_LAG_HASH_SMAC_NONIP) +/* DMAC - for IPv4 and IPv6 packets */ +#define MLXSW_REG_SLCR_LAG_HASH_DMAC_IP BIT(3) +/* DMAC - for non-IP packets */ +#define MLXSW_REG_SLCR_LAG_HASH_DMAC_NONIP BIT(4) +#define MLXSW_REG_SLCR_LAG_HASH_DMAC \ + (MLXSW_REG_SLCR_LAG_HASH_DMAC_IP | \ + MLXSW_REG_SLCR_LAG_HASH_DMAC_NONIP) +/* Ethertype - for IPv4 and IPv6 packets */ +#define MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_IP BIT(5) +/* Ethertype - for non-IP packets */ +#define MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_NONIP BIT(6) +#define MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE \ + (MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_IP | \ + MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_NONIP) +/* VLAN ID - for IPv4 and IPv6 packets */ +#define MLXSW_REG_SLCR_LAG_HASH_VLANID_IP BIT(7) +/* VLAN ID - for non-IP packets */ +#define MLXSW_REG_SLCR_LAG_HASH_VLANID_NONIP BIT(8) +#define MLXSW_REG_SLCR_LAG_HASH_VLANID \ + (MLXSW_REG_SLCR_LAG_HASH_VLANID_IP | \ + MLXSW_REG_SLCR_LAG_HASH_VLANID_NONIP) +/* Source IP address (can be IPv4 or IPv6) */ +#define MLXSW_REG_SLCR_LAG_HASH_SIP BIT(9) +/* Destination IP address (can be IPv4 or IPv6) */ +#define MLXSW_REG_SLCR_LAG_HASH_DIP BIT(10) +/* TCP/UDP source port */ +#define MLXSW_REG_SLCR_LAG_HASH_SPORT BIT(11) +/* TCP/UDP destination port*/ +#define MLXSW_REG_SLCR_LAG_HASH_DPORT BIT(12) +/* IPv4 Protocol/IPv6 Next Header */ +#define MLXSW_REG_SLCR_LAG_HASH_IPPROTO BIT(13) +/* IPv6 Flow label */ +#define MLXSW_REG_SLCR_LAG_HASH_FLOWLABEL BIT(14) +/* SID - FCoE source ID */ +#define MLXSW_REG_SLCR_LAG_HASH_FCOE_SID BIT(15) +/* DID - FCoE destination ID */ +#define MLXSW_REG_SLCR_LAG_HASH_FCOE_DID BIT(16) +/* OXID - FCoE originator exchange ID */ +#define MLXSW_REG_SLCR_LAG_HASH_FCOE_OXID BIT(17) +/* Destination QP number - for RoCE packets */ +#define MLXSW_REG_SLCR_LAG_HASH_ROCE_DQP BIT(19) + +/* reg_slcr_lag_hash + * LAG hashing configuration. This is a bitmask, in which each set + * bit includes the corresponding item in the LAG hash calculation. + * The default lag_hash contains SMAC, DMAC, VLANID and + * Ethertype (for all packet types). + * Access: RW + */ +MLXSW_ITEM32(reg, slcr, lag_hash, 0x04, 0, 20); + +static inline void mlxsw_reg_slcr_pack(char *payload, u16 lag_hash) +{ + MLXSW_REG_ZERO(slcr, payload); + mlxsw_reg_slcr_pp_set(payload, MLXSW_REG_SLCR_PP_GLOBAL); + mlxsw_reg_slcr_type_set(payload, MLXSW_REG_SLCR_TYPE_XOR); + mlxsw_reg_slcr_lag_hash_set(payload, lag_hash); +} + +/* SLCOR - Switch LAG Collector Register + * ------------------------------------- + * The Switch LAG Collector register controls the Local Port membership + * in a LAG and enablement of the collector. + */ +#define MLXSW_REG_SLCOR_ID 0x2016 +#define MLXSW_REG_SLCOR_LEN 0x10 + +static const struct mlxsw_reg_info mlxsw_reg_slcor = { + .id = MLXSW_REG_SLCOR_ID, + .len = MLXSW_REG_SLCOR_LEN, +}; + +enum mlxsw_reg_slcor_col { + /* Port is added with collector disabled */ + MLXSW_REG_SLCOR_COL_LAG_ADD_PORT, + MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_ENABLED, + MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_DISABLED, + MLXSW_REG_SLCOR_COL_LAG_REMOVE_PORT, +}; + +/* reg_slcor_col + * Collector configuration + * Access: RW + */ +MLXSW_ITEM32(reg, slcor, col, 0x00, 30, 2); + +/* reg_slcor_local_port + * Local port number + * Not supported for CPU port + * Access: Index + */ +MLXSW_ITEM32(reg, slcor, local_port, 0x00, 16, 8); + +/* reg_slcor_lag_id + * LAG Identifier. Index into the LAG descriptor table. + * Access: Index + */ +MLXSW_ITEM32(reg, slcor, lag_id, 0x00, 0, 10); + +/* reg_slcor_port_index + * Port index in the LAG list. Only valid on Add Port to LAG col. + * Valid range is from 0 to cap_max_lag_members-1 + * Access: RW + */ +MLXSW_ITEM32(reg, slcor, port_index, 0x04, 0, 10); + +static inline void mlxsw_reg_slcor_pack(char *payload, + u8 local_port, u16 lag_id, + enum mlxsw_reg_slcor_col col) +{ + MLXSW_REG_ZERO(slcor, payload); + mlxsw_reg_slcor_col_set(payload, col); + mlxsw_reg_slcor_local_port_set(payload, local_port); + mlxsw_reg_slcor_lag_id_set(payload, lag_id); +} + +static inline void mlxsw_reg_slcor_port_add_pack(char *payload, + u8 local_port, u16 lag_id, + u8 port_index) +{ + mlxsw_reg_slcor_pack(payload, local_port, lag_id, + MLXSW_REG_SLCOR_COL_LAG_ADD_PORT); + mlxsw_reg_slcor_port_index_set(payload, port_index); +} + +static inline void mlxsw_reg_slcor_port_remove_pack(char *payload, + u8 local_port, u16 lag_id) +{ + mlxsw_reg_slcor_pack(payload, local_port, lag_id, + MLXSW_REG_SLCOR_COL_LAG_REMOVE_PORT); +} + +static inline void mlxsw_reg_slcor_col_enable_pack(char *payload, + u8 local_port, u16 lag_id) +{ + mlxsw_reg_slcor_pack(payload, local_port, lag_id, + MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_ENABLED); +} + +static inline void mlxsw_reg_slcor_col_disable_pack(char *payload, + u8 local_port, u16 lag_id) +{ + mlxsw_reg_slcor_pack(payload, local_port, lag_id, + MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_ENABLED); +} + /* SPMLR - Switch Port MAC Learning Register * ----------------------------------------- * Controls the Switch MAC learning policy per port. @@ -2653,6 +2940,12 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "SFGC"; case MLXSW_REG_SFTR_ID: return "SFTR"; + case MLXSW_REG_SLDR_ID: + return "SLDR"; + case MLXSW_REG_SLCR_ID: + return "SLCR"; + case MLXSW_REG_SLCOR_ID: + return "SLCOR"; case MLXSW_REG_SPMLR_ID: return "SPMLR"; case MLXSW_REG_SVFA_ID: -- cgit v1.2.3-59-g8ed1b From e4bfbae29a1978a855f7c35bbcab1fe7ed0602d2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:26 +0100 Subject: mlxsw: reg: Add definition of LAG unicast record for SFD register LAG-related records have specific format in SFD register. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 79 +++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 049abbb8bcc2..431b28e2b420 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -286,6 +286,7 @@ MLXSW_ITEM32_INDEXED(reg, sfd, rec_swid, MLXSW_REG_SFD_BASE_LEN, 24, 8, enum mlxsw_reg_sfd_rec_type { MLXSW_REG_SFD_REC_TYPE_UNICAST = 0x0, + MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG = 0x1, }; /* reg_sfd_rec_type @@ -376,24 +377,34 @@ MLXSW_ITEM32_INDEXED(reg, sfd, uc_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16, MLXSW_ITEM32_INDEXED(reg, sfd, uc_system_port, MLXSW_REG_SFD_BASE_LEN, 0, 16, MLXSW_REG_SFD_REC_LEN, 0x0C, false); -static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index, - enum mlxsw_reg_sfd_rec_policy policy, - const char *mac, u16 vid, - enum mlxsw_reg_sfd_rec_action action, - u8 local_port) +static inline void mlxsw_reg_sfd_rec_pack(char *payload, int rec_index, + enum mlxsw_reg_sfd_rec_type rec_type, + enum mlxsw_reg_sfd_rec_policy policy, + const char *mac, + enum mlxsw_reg_sfd_rec_action action) { u8 num_rec = mlxsw_reg_sfd_num_rec_get(payload); if (rec_index >= num_rec) mlxsw_reg_sfd_num_rec_set(payload, rec_index + 1); mlxsw_reg_sfd_rec_swid_set(payload, rec_index, 0); - mlxsw_reg_sfd_rec_type_set(payload, rec_index, - MLXSW_REG_SFD_REC_TYPE_UNICAST); + mlxsw_reg_sfd_rec_type_set(payload, rec_index, rec_type); mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy); mlxsw_reg_sfd_rec_mac_memcpy_to(payload, rec_index, mac); + mlxsw_reg_sfd_rec_action_set(payload, rec_index, action); +} + +static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index, + enum mlxsw_reg_sfd_rec_policy policy, + const char *mac, u16 vid, + enum mlxsw_reg_sfd_rec_action action, + u8 local_port) +{ + mlxsw_reg_sfd_rec_pack(payload, rec_index, + MLXSW_REG_SFD_REC_TYPE_UNICAST, + policy, mac, action); mlxsw_reg_sfd_uc_sub_port_set(payload, rec_index, 0); mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, vid); - mlxsw_reg_sfd_rec_action_set(payload, rec_index, action); mlxsw_reg_sfd_uc_system_port_set(payload, rec_index, local_port); } @@ -406,6 +417,58 @@ static inline void mlxsw_reg_sfd_uc_unpack(char *payload, int rec_index, *p_local_port = mlxsw_reg_sfd_uc_system_port_get(payload, rec_index); } +/* reg_sfd_uc_lag_sub_port + * LAG sub port. + * Must be 0 if multichannel VEPA is not enabled. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_sub_port, MLXSW_REG_SFD_BASE_LEN, 16, 8, + MLXSW_REG_SFD_REC_LEN, 0x08, false); + +/* reg_sfd_uc_lag_fid_vid + * Filtering ID or VLAN ID + * For SwitchX and SwitchX-2: + * - Dynamic entries (policy 2,3) use FID + * - Static entries (policy 0) use VID + * - When independent learning is configured, VID=FID + * For Spectrum: use FID for both Dynamic and Static entries. + * VID should not be used. + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16, + MLXSW_REG_SFD_REC_LEN, 0x08, false); + +/* reg_sfd_uc_lag_lag_id + * LAG Identifier - pointer into the LAG descriptor table. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_lag_id, MLXSW_REG_SFD_BASE_LEN, 0, 10, + MLXSW_REG_SFD_REC_LEN, 0x0C, false); + +static inline void +mlxsw_reg_sfd_uc_lag_pack(char *payload, int rec_index, + enum mlxsw_reg_sfd_rec_policy policy, + const char *mac, u16 vid, + enum mlxsw_reg_sfd_rec_action action, + u16 lag_id) +{ + mlxsw_reg_sfd_rec_pack(payload, rec_index, + MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG, + policy, mac, action); + mlxsw_reg_sfd_uc_lag_sub_port_set(payload, rec_index, 0); + mlxsw_reg_sfd_uc_lag_fid_vid_set(payload, rec_index, vid); + mlxsw_reg_sfd_uc_lag_lag_id_set(payload, rec_index, lag_id); +} + +static inline void mlxsw_reg_sfd_uc_lag_unpack(char *payload, int rec_index, + char *mac, u16 *p_vid, + u16 *p_lag_id) +{ + mlxsw_reg_sfd_rec_mac_memcpy_from(payload, rec_index, mac); + *p_vid = mlxsw_reg_sfd_uc_lag_fid_vid_get(payload, rec_index); + *p_lag_id = mlxsw_reg_sfd_uc_lag_lag_id_get(payload, rec_index); +} + /* SFN - Switch FDB Notification Register * ------------------------------------------- * The switch provides notifications on newly learned FDB entries and -- cgit v1.2.3-59-g8ed1b From 3b71571c0123ec52c5b73233b4f012fa59aed61a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:27 +0100 Subject: mlxsw: reg: Add definition of LAG unicast record for SFN register LAG-related records have specific format in SFN register. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 431b28e2b420..4e4e4dcf054f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -519,8 +519,12 @@ MLXSW_ITEM32_INDEXED(reg, sfn, rec_swid, MLXSW_REG_SFN_BASE_LEN, 24, 8, enum mlxsw_reg_sfn_rec_type { /* MAC addresses learned on a regular port. */ MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC = 0x5, - /* Aged-out MAC address on a regular port */ + /* MAC addresses learned on a LAG port. */ + MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG = 0x6, + /* Aged-out MAC address on a regular port. */ MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC = 0x7, + /* Aged-out MAC address on a LAG port. */ + MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG = 0x8, }; /* reg_sfn_rec_type @@ -568,6 +572,22 @@ static inline void mlxsw_reg_sfn_mac_unpack(char *payload, int rec_index, *p_local_port = mlxsw_reg_sfn_mac_system_port_get(payload, rec_index); } +/* reg_sfn_mac_lag_lag_id + * LAG ID (pointer into the LAG descriptor table). + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, sfn, mac_lag_lag_id, MLXSW_REG_SFN_BASE_LEN, 0, 10, + MLXSW_REG_SFN_REC_LEN, 0x0C, false); + +static inline void mlxsw_reg_sfn_mac_lag_unpack(char *payload, int rec_index, + char *mac, u16 *p_vid, + u16 *p_lag_id) +{ + mlxsw_reg_sfn_rec_mac_memcpy_from(payload, rec_index, mac); + *p_vid = mlxsw_reg_sfn_mac_fid_get(payload, rec_index); + *p_lag_id = mlxsw_reg_sfn_mac_lag_lag_id_get(payload, rec_index); +} + /* SPMS - Switch Port MSTP/RSTP State Register * ------------------------------------------- * Configures the spanning tree state of a physical port. -- cgit v1.2.3-59-g8ed1b From 0d65fc13042fce6a2d6de58ff0dc9531e8523c07 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:28 +0100 Subject: mlxsw: spectrum: Implement LAG port join/leave Implement basic procedures for joining/leaving port to/from LAG. That includes HW setup of collector, core LAG mapping setup. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 288 +++++++++++++++++++++++-- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 35 ++- 2 files changed, 306 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index a397cc18693d..7d1f0a82a090 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1712,6 +1712,22 @@ static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp) return 0; } +static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp) +{ + char slcr_pl[MLXSW_REG_SLCR_LEN]; + + mlxsw_reg_slcr_pack(slcr_pl, MLXSW_REG_SLCR_LAG_HASH_SMAC | + MLXSW_REG_SLCR_LAG_HASH_DMAC | + MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE | + MLXSW_REG_SLCR_LAG_HASH_VLANID | + MLXSW_REG_SLCR_LAG_HASH_SIP | + MLXSW_REG_SLCR_LAG_HASH_DIP | + MLXSW_REG_SLCR_LAG_HASH_SPORT | + MLXSW_REG_SLCR_LAG_HASH_DPORT | + MLXSW_REG_SLCR_LAG_HASH_IPPROTO); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcr), slcr_pl); +} + static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *mlxsw_bus_info) { @@ -1757,6 +1773,12 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, goto err_buffers_init; } + err = mlxsw_sp_lag_init(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize LAG\n"); + goto err_lag_init; + } + err = mlxsw_sp_switchdev_init(mlxsw_sp); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize switchdev\n"); @@ -1766,6 +1788,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, return 0; err_switchdev_init: +err_lag_init: err_buffers_init: err_flood_init: mlxsw_sp_traps_fini(mlxsw_sp); @@ -1793,9 +1816,9 @@ static struct mlxsw_config_profile mlxsw_sp_config_profile = { .used_max_vepa_channels = 1, .max_vepa_channels = 0, .used_max_lag = 1, - .max_lag = 64, + .max_lag = MLXSW_SP_LAG_MAX, .used_max_port_per_lag = 1, - .max_port_per_lag = 16, + .max_port_per_lag = MLXSW_SP_PORT_PER_LAG_MAX, .used_max_mid = 1, .max_mid = 7000, .used_max_pgt = 1, @@ -1894,19 +1917,206 @@ static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp, mlxsw_sp->master_bridge.dev = NULL; } -static int mlxsw_sp_netdevice_event(struct notifier_block *unused, - unsigned long event, void *ptr) +static int mlxsw_sp_lag_create(struct mlxsw_sp *mlxsw_sp, u16 lag_id) +{ + char sldr_pl[MLXSW_REG_SLDR_LEN]; + + mlxsw_reg_sldr_lag_create_pack(sldr_pl, lag_id); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl); +} + +static int mlxsw_sp_lag_destroy(struct mlxsw_sp *mlxsw_sp, u16 lag_id) +{ + char sldr_pl[MLXSW_REG_SLDR_LEN]; + + mlxsw_reg_sldr_lag_destroy_pack(sldr_pl, lag_id); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl); +} + +static int mlxsw_sp_lag_col_port_add(struct mlxsw_sp_port *mlxsw_sp_port, + u16 lag_id, u8 port_index) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char slcor_pl[MLXSW_REG_SLCOR_LEN]; + + mlxsw_reg_slcor_port_add_pack(slcor_pl, mlxsw_sp_port->local_port, + lag_id, port_index); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl); +} + +static int mlxsw_sp_lag_col_port_remove(struct mlxsw_sp_port *mlxsw_sp_port, + u16 lag_id) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char slcor_pl[MLXSW_REG_SLCOR_LEN]; + + mlxsw_reg_slcor_port_remove_pack(slcor_pl, mlxsw_sp_port->local_port, + lag_id); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl); +} + +static int mlxsw_sp_lag_col_port_enable(struct mlxsw_sp_port *mlxsw_sp_port, + u16 lag_id) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char slcor_pl[MLXSW_REG_SLCOR_LEN]; + + mlxsw_reg_slcor_col_enable_pack(slcor_pl, mlxsw_sp_port->local_port, + lag_id); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl); +} + +static int mlxsw_sp_lag_col_port_disable(struct mlxsw_sp_port *mlxsw_sp_port, + u16 lag_id) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char slcor_pl[MLXSW_REG_SLCOR_LEN]; + + mlxsw_reg_slcor_col_disable_pack(slcor_pl, mlxsw_sp_port->local_port, + lag_id); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl); +} + +static int mlxsw_sp_lag_index_get(struct mlxsw_sp *mlxsw_sp, + struct net_device *lag_dev, + u16 *p_lag_id) +{ + struct mlxsw_sp_upper *lag; + int free_lag_id = -1; + int i; + + for (i = 0; i < MLXSW_SP_LAG_MAX; i++) { + lag = mlxsw_sp_lag_get(mlxsw_sp, i); + if (lag->ref_count) { + if (lag->dev == lag_dev) { + *p_lag_id = i; + return 0; + } + } else if (free_lag_id < 0) { + free_lag_id = i; + } + } + if (free_lag_id < 0) + return -EBUSY; + *p_lag_id = free_lag_id; + return 0; +} + +static bool +mlxsw_sp_master_lag_check(struct mlxsw_sp *mlxsw_sp, + struct net_device *lag_dev, + struct netdev_lag_upper_info *lag_upper_info) +{ + u16 lag_id; + + if (mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id) != 0) + return false; + if (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) + return false; + return true; +} + +static int mlxsw_sp_port_lag_index_get(struct mlxsw_sp *mlxsw_sp, + u16 lag_id, u8 *p_port_index) +{ + int i; + + for (i = 0; i < MLXSW_SP_PORT_PER_LAG_MAX; i++) { + if (!mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i)) { + *p_port_index = i; + return 0; + } + } + return -EBUSY; +} + +static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, + struct net_device *lag_dev) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + struct mlxsw_sp_upper *lag; + u16 lag_id; + u8 port_index; + int err; + + err = mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id); + if (err) + return err; + lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id); + if (!lag->ref_count) { + err = mlxsw_sp_lag_create(mlxsw_sp, lag_id); + if (err) + return err; + lag->dev = lag_dev; + } + + err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index); + if (err) + return err; + err = mlxsw_sp_lag_col_port_add(mlxsw_sp_port, lag_id, port_index); + if (err) + goto err_col_port_add; + err = mlxsw_sp_lag_col_port_enable(mlxsw_sp_port, lag_id); + if (err) + goto err_col_port_enable; + + mlxsw_core_lag_mapping_set(mlxsw_sp->core, lag_id, port_index, + mlxsw_sp_port->local_port); + mlxsw_sp_port->lag_id = lag_id; + mlxsw_sp_port->lagged = 1; + lag->ref_count++; + return 0; + +err_col_port_add: + if (!lag->ref_count) + mlxsw_sp_lag_destroy(mlxsw_sp, lag_id); +err_col_port_enable: + mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id); + return err; +} + +static int mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port, + struct net_device *lag_dev) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + struct mlxsw_sp_upper *lag; + u16 lag_id = mlxsw_sp_port->lag_id; + int err; + + if (!mlxsw_sp_port->lagged) + return 0; + lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id); + WARN_ON(lag->ref_count == 0); + + err = mlxsw_sp_lag_col_port_disable(mlxsw_sp_port, lag_id); + if (err) + return err; + mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id); + if (err) + return err; + + if (lag->ref_count == 1) { + err = mlxsw_sp_lag_destroy(mlxsw_sp, lag_id); + if (err) + return err; + } + + mlxsw_core_lag_mapping_clear(mlxsw_sp->core, lag_id, + mlxsw_sp_port->local_port); + mlxsw_sp_port->lagged = 0; + lag->ref_count--; + return 0; +} + +static int mlxsw_sp_netdevice_port_event(struct net_device *dev, + unsigned long event, void *ptr) { - struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct netdev_notifier_changeupper_info *info; struct mlxsw_sp_port *mlxsw_sp_port; struct net_device *upper_dev; struct mlxsw_sp *mlxsw_sp; int err; - if (!mlxsw_sp_port_dev_check(dev)) - return NOTIFY_DONE; - mlxsw_sp_port = netdev_priv(dev); mlxsw_sp = mlxsw_sp_port->mlxsw_sp; info = ptr; @@ -1914,16 +2124,22 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused, switch (event) { case NETDEV_PRECHANGEUPPER: upper_dev = info->upper_dev; + if (!info->master || !info->linking) + break; /* HW limitation forbids to put ports to multiple bridges. */ - if (info->master && info->linking && - netif_is_bridge_master(upper_dev) && + if (netif_is_bridge_master(upper_dev) && !mlxsw_sp_master_bridge_check(mlxsw_sp, upper_dev)) return NOTIFY_BAD; + if (netif_is_lag_master(upper_dev) && + !mlxsw_sp_master_lag_check(mlxsw_sp, upper_dev, + info->upper_info)) + return NOTIFY_BAD; break; case NETDEV_CHANGEUPPER: upper_dev = info->upper_dev; - if (info->master && - netif_is_bridge_master(upper_dev)) { + if (!info->master) + break; + if (netif_is_bridge_master(upper_dev)) { if (info->linking) { err = mlxsw_sp_port_bridge_join(mlxsw_sp_port); if (err) @@ -1937,6 +2153,22 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused, mlxsw_sp_port->bridged = 0; mlxsw_sp_master_bridge_dec(mlxsw_sp, upper_dev); } + } else if (netif_is_lag_master(upper_dev)) { + if (info->linking) { + err = mlxsw_sp_port_lag_join(mlxsw_sp_port, + upper_dev); + if (err) { + netdev_err(dev, "Failed to join link aggregation\n"); + return NOTIFY_BAD; + } + } else { + err = mlxsw_sp_port_lag_leave(mlxsw_sp_port, + upper_dev); + if (err) { + netdev_err(dev, "Failed to leave link aggregation\n"); + return NOTIFY_BAD; + } + } } break; } @@ -1944,6 +2176,38 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused, return NOTIFY_DONE; } +static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev, + unsigned long event, void *ptr) +{ + struct net_device *dev; + struct list_head *iter; + int ret; + + netdev_for_each_lower_dev(lag_dev, dev, iter) { + if (mlxsw_sp_port_dev_check(dev)) { + ret = mlxsw_sp_netdevice_port_event(dev, event, ptr); + if (ret == NOTIFY_BAD) + return ret; + } + } + + return NOTIFY_DONE; +} + +static int mlxsw_sp_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + + if (mlxsw_sp_port_dev_check(dev)) + return mlxsw_sp_netdevice_port_event(dev, event, ptr); + + if (netif_is_lag_master(dev)) + return mlxsw_sp_netdevice_lag_event(dev, event, ptr); + + return NOTIFY_DONE; +} + static struct notifier_block mlxsw_sp_netdevice_nb __read_mostly = { .notifier_call = mlxsw_sp_netdevice_event, }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 4365c8bccc6d..48be5a63b9b5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -46,9 +46,16 @@ #include "core.h" #define MLXSW_SP_VFID_BASE VLAN_N_VID +#define MLXSW_SP_LAG_MAX 64 +#define MLXSW_SP_PORT_PER_LAG_MAX 16 struct mlxsw_sp_port; +struct mlxsw_sp_upper { + struct net_device *dev; + unsigned int ref_count; +}; + struct mlxsw_sp { unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)]; unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)]; @@ -63,12 +70,16 @@ struct mlxsw_sp { } fdb_notify; #define MLXSW_SP_DEFAULT_AGEING_TIME 300 u32 ageing_time; - struct { - struct net_device *dev; - unsigned int ref_count; - } master_bridge; + struct mlxsw_sp_upper master_bridge; + struct mlxsw_sp_upper lags[MLXSW_SP_LAG_MAX]; }; +static inline struct mlxsw_sp_upper * +mlxsw_sp_lag_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id) +{ + return &mlxsw_sp->lags[lag_id]; +} + struct mlxsw_sp_port_pcpu_stats { u64 rx_packets; u64 rx_bytes; @@ -87,8 +98,10 @@ struct mlxsw_sp_port { u8 learning:1, learning_sync:1, uc_flood:1, - bridged:1; + bridged:1, + lagged:1; u16 pvid; + u16 lag_id; /* 802.1Q bridge VLANs */ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; /* VLAN interfaces */ @@ -96,6 +109,18 @@ struct mlxsw_sp_port { u16 nr_vfids; }; +static inline struct mlxsw_sp_port * +mlxsw_sp_port_lagged_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id, u8 port_index) +{ + struct mlxsw_sp_port *mlxsw_sp_port; + u8 local_port; + + local_port = mlxsw_core_lag_mapping_get(mlxsw_sp->core, + lag_id, port_index); + mlxsw_sp_port = mlxsw_sp->ports[local_port]; + return mlxsw_sp_port && mlxsw_sp_port->lagged ? mlxsw_sp_port : NULL; +} + enum mlxsw_sp_flood_table { MLXSW_SP_FLOOD_TABLE_UC, MLXSW_SP_FLOOD_TABLE_BM, -- cgit v1.2.3-59-g8ed1b From 8a1ab5d766396aad0e60cc8796646a1171b419c8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:29 +0100 Subject: mlxsw: spectrum: Implement FDB add/remove/dump for LAG Implement FDB offloading for lagged ports, including learning LAG FDB entries, adding/removing static FDB entries and dumping existing LAG FDB entries. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 182 +++++++++++++++++---- 1 file changed, 150 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index f21e23983a1a..406dab2f6b17 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -490,32 +490,56 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, untagged_flag, pvid_flag); } -static int mlxsw_sp_port_fdb_op(struct mlxsw_sp_port *mlxsw_sp_port, - const char *mac, u16 vid, bool adding, - bool dynamic) +static enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic) { - enum mlxsw_reg_sfd_rec_policy policy; - enum mlxsw_reg_sfd_op op; + return dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS : + MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY; +} + +static enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding) +{ + return adding ? MLXSW_REG_SFD_OP_WRITE_EDIT : + MLXSW_REG_SFD_OP_WRITE_REMOVE; +} + +static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port, + const char *mac, u16 vid, bool adding, + bool dynamic) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; char *sfd_pl; int err; - if (!vid) - vid = mlxsw_sp_port->pvid; - sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); if (!sfd_pl) return -ENOMEM; - policy = dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS : - MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY; - op = adding ? MLXSW_REG_SFD_OP_WRITE_EDIT : - MLXSW_REG_SFD_OP_WRITE_REMOVE; - mlxsw_reg_sfd_pack(sfd_pl, op, 0); - mlxsw_reg_sfd_uc_pack(sfd_pl, 0, policy, + mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); + mlxsw_reg_sfd_uc_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic), mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP, mlxsw_sp_port->local_port); - err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(sfd), - sfd_pl); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); + kfree(sfd_pl); + + return err; +} + +static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id, + const char *mac, u16 vid, bool adding, + bool dynamic) +{ + char *sfd_pl; + int err; + + sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); + if (!sfd_pl) + return -ENOMEM; + + mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); + mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic), + mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP, + lag_id); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); kfree(sfd_pl); return err; @@ -526,11 +550,21 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_fdb *fdb, struct switchdev_trans *trans) { + u16 vid = fdb->vid; + if (switchdev_trans_ph_prepare(trans)) return 0; - return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid, - true, false); + if (!vid) + vid = mlxsw_sp_port->pvid; + + if (!mlxsw_sp_port->lagged) + return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, + fdb->addr, vid, true, false); + else + return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp, + mlxsw_sp_port->lag_id, + fdb->addr, vid, true, false); } static int mlxsw_sp_port_obj_add(struct net_device *dev, @@ -645,8 +679,15 @@ static int mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_fdb *fdb) { - return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid, - false, false); + if (!mlxsw_sp_port->lagged) + return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, + fdb->addr, fdb->vid, + false, false); + else + return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp, + mlxsw_sp_port->lag_id, + fdb->addr, fdb->vid, + false, false); } static int mlxsw_sp_port_obj_del(struct net_device *dev, @@ -672,14 +713,30 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev, return err; } +static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp, + u16 lag_id) +{ + struct mlxsw_sp_port *mlxsw_sp_port; + int i; + + for (i = 0; i < MLXSW_SP_PORT_PER_LAG_MAX; i++) { + mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i); + if (mlxsw_sp_port) + return mlxsw_sp_port; + } + return NULL; +} + static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, struct switchdev_obj_port_fdb *fdb, switchdev_obj_dump_cb_t *cb) { + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; char *sfd_pl; char mac[ETH_ALEN]; u16 vid; u8 local_port; + u16 lag_id; u8 num_rec; int stored_err = 0; int i; @@ -692,8 +749,7 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0); do { mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT); - err = mlxsw_reg_query(mlxsw_sp_port->mlxsw_sp->core, - MLXSW_REG(sfd), sfd_pl); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); if (err) goto out; @@ -718,6 +774,20 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, if (err) stored_err = err; } + break; + case MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG: + mlxsw_reg_sfd_uc_lag_unpack(sfd_pl, i, + mac, &vid, &lag_id); + if (mlxsw_sp_port == + mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id)) { + ether_addr_copy(fdb->addr, mac); + fdb->ndm_state = NUD_REACHABLE; + fdb->vid = vid; + err = cb(&fdb->obj); + if (err) + stored_err = err; + } + break; } } } while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT); @@ -779,6 +849,21 @@ static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = { .switchdev_port_obj_dump = mlxsw_sp_port_obj_dump, }; +static void mlxsw_sp_fdb_call_notifiers(bool learning, bool learning_sync, + bool adding, char *mac, u16 vid, + struct net_device *dev) +{ + struct switchdev_notifier_fdb_info info; + unsigned long notifier_type; + + if (learning && learning_sync) { + info.addr = mac; + info.vid = vid; + notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL; + call_switchdev_notifiers(notifier_type, dev, &info.info); + } +} + static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, char *sfn_pl, int rec_index, bool adding) @@ -796,24 +881,49 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, return; } - err = mlxsw_sp_port_fdb_op(mlxsw_sp_port, mac, vid, - adding && mlxsw_sp_port->learning, true); + err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, mac, vid, + adding && mlxsw_sp_port->learning, true); if (err) { if (net_ratelimit()) netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n"); return; } - if (mlxsw_sp_port->learning && mlxsw_sp_port->learning_sync) { - struct switchdev_notifier_fdb_info info; - unsigned long notifier_type; + mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning, + mlxsw_sp_port->learning_sync, + adding, mac, vid, mlxsw_sp_port->dev); +} - info.addr = mac; - info.vid = vid; - notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL; - call_switchdev_notifiers(notifier_type, mlxsw_sp_port->dev, - &info.info); +static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, + char *sfn_pl, int rec_index, + bool adding) +{ + struct mlxsw_sp_port *mlxsw_sp_port; + char mac[ETH_ALEN]; + u16 lag_id; + u16 vid; + int err; + + mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &vid, &lag_id); + mlxsw_sp_port = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id); + if (!mlxsw_sp_port) { + dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Cannot find port representor for LAG\n"); + return; } + + err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, vid, + adding && mlxsw_sp_port->learning, + true); + if (err) { + if (net_ratelimit()) + netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n"); + return; + } + + mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning, + mlxsw_sp_port->learning_sync, + adding, mac, vid, + mlxsw_sp_lag_get(mlxsw_sp, lag_id)->dev); } static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp, @@ -828,6 +938,14 @@ static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl, rec_index, false); break; + case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG: + mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl, + rec_index, true); + break; + case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG: + mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl, + rec_index, false); + break; } } -- cgit v1.2.3-59-g8ed1b From 745812065cf302ad11bf63bcbdbd0ff41af0e36a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 12:12:30 +0100 Subject: mlxsw: spectrum: Implement LAG tx enabled lower state change Enabling/disabling TX on a LAG port means enabling/disabling distribution in our HW. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 81 +++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 7d1f0a82a090..3ec07b9a458d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2108,8 +2108,47 @@ static int mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port, return 0; } -static int mlxsw_sp_netdevice_port_event(struct net_device *dev, - unsigned long event, void *ptr) +static int mlxsw_sp_lag_dist_port_add(struct mlxsw_sp_port *mlxsw_sp_port, + u16 lag_id) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char sldr_pl[MLXSW_REG_SLDR_LEN]; + + mlxsw_reg_sldr_lag_add_port_pack(sldr_pl, lag_id, + mlxsw_sp_port->local_port); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl); +} + +static int mlxsw_sp_lag_dist_port_remove(struct mlxsw_sp_port *mlxsw_sp_port, + u16 lag_id) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char sldr_pl[MLXSW_REG_SLDR_LEN]; + + mlxsw_reg_sldr_lag_remove_port_pack(sldr_pl, lag_id, + mlxsw_sp_port->local_port); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl); +} + +static int mlxsw_sp_port_lag_tx_en_set(struct mlxsw_sp_port *mlxsw_sp_port, + bool lag_tx_enabled) +{ + if (lag_tx_enabled) + return mlxsw_sp_lag_dist_port_add(mlxsw_sp_port, + mlxsw_sp_port->lag_id); + else + return mlxsw_sp_lag_dist_port_remove(mlxsw_sp_port, + mlxsw_sp_port->lag_id); +} + +static int mlxsw_sp_port_lag_changed(struct mlxsw_sp_port *mlxsw_sp_port, + struct netdev_lag_lower_state_info *info) +{ + return mlxsw_sp_port_lag_tx_en_set(mlxsw_sp_port, info->tx_enabled); +} + +static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, + unsigned long event, void *ptr) { struct netdev_notifier_changeupper_info *info; struct mlxsw_sp_port *mlxsw_sp_port; @@ -2176,6 +2215,44 @@ static int mlxsw_sp_netdevice_port_event(struct net_device *dev, return NOTIFY_DONE; } +static int mlxsw_sp_netdevice_port_lower_event(struct net_device *dev, + unsigned long event, void *ptr) +{ + struct netdev_notifier_changelowerstate_info *info; + struct mlxsw_sp_port *mlxsw_sp_port; + int err; + + mlxsw_sp_port = netdev_priv(dev); + info = ptr; + + switch (event) { + case NETDEV_CHANGELOWERSTATE: + if (netif_is_lag_port(dev) && mlxsw_sp_port->lagged) { + err = mlxsw_sp_port_lag_changed(mlxsw_sp_port, + info->lower_state_info); + if (err) + netdev_err(dev, "Failed to reflect link aggregation lower state change\n"); + } + break; + } + + return NOTIFY_DONE; +} + +static int mlxsw_sp_netdevice_port_event(struct net_device *dev, + unsigned long event, void *ptr) +{ + switch (event) { + case NETDEV_PRECHANGEUPPER: + case NETDEV_CHANGEUPPER: + return mlxsw_sp_netdevice_port_upper_event(dev, event, ptr); + case NETDEV_CHANGELOWERSTATE: + return mlxsw_sp_netdevice_port_lower_event(dev, event, ptr); + } + + return NOTIFY_DONE; +} + static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev, unsigned long event, void *ptr) { -- cgit v1.2.3-59-g8ed1b From 24e2416e5ad2142bd1ab71ed8344c3fb9001c0ca Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 30 Nov 2015 17:33:36 -0500 Subject: alx: remove pointless assignment Reasonably sure this doesn't serve any purpose. CC: Jay Cliburn CC: Chris Snook CC: netdev@vger.kernel.org Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/main.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index c8af3ce3ea38..0a81808f1968 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -577,7 +577,6 @@ static int alx_alloc_rings(struct alx_priv *alx) alx->int_mask &= ~ALX_ISR_ALL_QUEUES; alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0; - alx->tx_ringsz = alx->tx_ringsz; netif_napi_add(alx->dev, &alx->napi, alx_poll, 64); -- cgit v1.2.3-59-g8ed1b From 0b10710603b27e86ddd89fb87742997594892e50 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Tue, 1 Dec 2015 18:03:08 +0200 Subject: net/mlx5_core: Modify enable/disable hca functions Modify these functions to have func_id argument to state which device we are referring to. This is done as a preparation for SRIOV support where a PF driver needs to control its virtual functions. Signed-off-by: Eli Cohen Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 45 ++++++++++------------ .../net/ethernet/mellanox/mlx5/core/mlx5_core.h | 2 + 2 files changed, 23 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 4ac8d4cc4973..f2e64dc1a443 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -462,42 +462,39 @@ static int set_hca_ctrl(struct mlx5_core_dev *dev) return err; } -static int mlx5_core_enable_hca(struct mlx5_core_dev *dev) +int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id) { + u32 out[MLX5_ST_SZ_DW(enable_hca_out)]; + u32 in[MLX5_ST_SZ_DW(enable_hca_in)]; int err; - struct mlx5_enable_hca_mbox_in in; - struct mlx5_enable_hca_mbox_out out; - memset(&in, 0, sizeof(in)); - memset(&out, 0, sizeof(out)); - in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ENABLE_HCA); + memset(in, 0, sizeof(in)); + MLX5_SET(enable_hca_in, in, opcode, MLX5_CMD_OP_ENABLE_HCA); + MLX5_SET(enable_hca_in, in, function_id, func_id); + memset(out, 0, sizeof(out)); + err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); if (err) return err; - if (out.hdr.status) - return mlx5_cmd_status_to_err(&out.hdr); - - return 0; + return mlx5_cmd_status_to_err_v2(out); } -static int mlx5_core_disable_hca(struct mlx5_core_dev *dev) +int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id) { + u32 out[MLX5_ST_SZ_DW(disable_hca_out)]; + u32 in[MLX5_ST_SZ_DW(disable_hca_in)]; int err; - struct mlx5_disable_hca_mbox_in in; - struct mlx5_disable_hca_mbox_out out; - memset(&in, 0, sizeof(in)); - memset(&out, 0, sizeof(out)); - in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DISABLE_HCA); - err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out)); + memset(in, 0, sizeof(in)); + MLX5_SET(disable_hca_in, in, opcode, MLX5_CMD_OP_DISABLE_HCA); + MLX5_SET(disable_hca_in, in, function_id, func_id); + memset(out, 0, sizeof(out)); + err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); if (err) return err; - if (out.hdr.status) - return mlx5_cmd_status_to_err(&out.hdr); - - return 0; + return mlx5_cmd_status_to_err_v2(out); } static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i) @@ -942,7 +939,7 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) mlx5_pagealloc_init(dev); - err = mlx5_core_enable_hca(dev); + err = mlx5_core_enable_hca(dev, 0); if (err) { dev_err(&pdev->dev, "enable hca failed\n"); goto err_pagealloc_cleanup; @@ -1106,7 +1103,7 @@ reclaim_boot_pages: mlx5_reclaim_startup_pages(dev); err_disable_hca: - mlx5_core_disable_hca(dev); + mlx5_core_disable_hca(dev, 0); err_pagealloc_cleanup: mlx5_pagealloc_cleanup(dev); @@ -1149,7 +1146,7 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) } mlx5_pagealloc_stop(dev); mlx5_reclaim_startup_pages(dev); - mlx5_core_disable_hca(dev); + mlx5_core_disable_hca(dev, 0); mlx5_pagealloc_cleanup(dev); mlx5_cmd_cleanup(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index cee5b7a839bc..1ed2239a6a6d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -90,6 +90,8 @@ void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, unsigned long param); void mlx5_enter_error_state(struct mlx5_core_dev *dev); void mlx5_disable_device(struct mlx5_core_dev *dev); +int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id); +int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id); void mlx5e_init(void); void mlx5e_cleanup(void); -- cgit v1.2.3-59-g8ed1b From fc50db98ff872372f266695858f87a12eb1b4f05 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Tue, 1 Dec 2015 18:03:09 +0200 Subject: net/mlx5_core: Add base sriov support This patch adds SRIOV base support for mlx5 supported devices. The same driver is used for both PFs and VFs; VFs are identified by the driver through the flag MLX5_PCI_DEV_IS_VF added to the pci table entries. Virtual functions are created as usual through writing a value to the sriov_numvs sysfs file of the PF device. Upon instantiating VFs, they will all be probed by the driver on the hypervisor. One can gracefully unbind them through /sys/bus/pci/drivers/mlx5_core/unbind. mlx5_wait_for_vf_pages() was added to ensure that when a VF dies without executing proper teardown, the hypervisor driver waits till all of the pages that were allocated at the hypervisor to maintain its operation are returned. In order for the VF to be operational, the PF needs to call enable_hca for it. This can be done before the VFs are created through a call to pci_enable_sriov. If the there are VFs assigned to a VMs when the driver of the PF is unloaded, all the VF will experience system error and PF driver unloads cleanly; in this case pci_disable_sriov is not called and the devices will show when running lspci. Once the PF driver is reloaded, it will sync its data structures which maintain state on its VFs. Signed-off-by: Eli Cohen Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 +- drivers/net/ethernet/mellanox/mlx5/core/main.c | 36 +++- .../net/ethernet/mellanox/mlx5/core/mlx5_core.h | 2 + .../net/ethernet/mellanox/mlx5/core/pagealloc.c | 38 ++++ drivers/net/ethernet/mellanox/mlx5/core/sriov.c | 221 +++++++++++++++++++++ include/linux/mlx5/driver.h | 24 +++ include/linux/mlx5/mlx5_ifc.h | 4 +- 7 files changed, 318 insertions(+), 9 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/sriov.c (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 26a68b8af2c5..4d5103911527 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_MLX5_CORE) += mlx5_core.o mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \ - mad.o transobj.o vport.o + mad.o transobj.o vport.o sriov.o mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o flow_table.o \ en_main.o en_flow_table.o en_ethtool.o en_tx.o en_rx.o \ en_txrx.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index f2e64dc1a443..66e2b37cfbbf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -454,6 +454,9 @@ static int set_hca_ctrl(struct mlx5_core_dev *dev) struct mlx5_reg_host_endianess he_out; int err; + if (!mlx5_core_is_pf(dev)) + return 0; + memset(&he_in, 0, sizeof(he_in)); he_in.he = MLX5_SET_HOST_ENDIANNESS; err = mlx5_core_access_reg(dev, &he_in, sizeof(he_in), @@ -1049,6 +1052,12 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) mlx5_init_srq_table(dev); mlx5_init_mr_table(dev); + err = mlx5_sriov_init(dev); + if (err) { + dev_err(&pdev->dev, "sriov init failed %d\n", err); + goto err_sriov; + } + err = mlx5_register_device(dev); if (err) { dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err); @@ -1065,6 +1074,10 @@ out: return 0; +err_sriov: + if (mlx5_sriov_cleanup(dev)) + dev_err(&dev->pdev->dev, "sriov cleanup failed\n"); + err_reg_dev: mlx5_cleanup_mr_table(dev); mlx5_cleanup_srq_table(dev); @@ -1120,6 +1133,13 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) { int err = 0; + err = mlx5_sriov_cleanup(dev); + if (err) { + dev_warn(&dev->pdev->dev, "%s: sriov cleanup failed - abort\n", + __func__); + return err; + } + mutex_lock(&dev->intf_state_mutex); if (dev->interface_state == MLX5_INTERFACE_STATE_DOWN) { dev_warn(&dev->pdev->dev, "%s: interface is down, NOP\n", @@ -1192,6 +1212,7 @@ static int init_one(struct pci_dev *pdev, return -ENOMEM; } priv = &dev->priv; + priv->pci_dev_data = id->driver_data; pci_set_drvdata(pdev, dev); @@ -1362,12 +1383,12 @@ static const struct pci_error_handlers mlx5_err_handler = { }; static const struct pci_device_id mlx5_core_pci_table[] = { - { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */ - { PCI_VDEVICE(MELLANOX, 0x1012) }, /* Connect-IB VF */ - { PCI_VDEVICE(MELLANOX, 0x1013) }, /* ConnectX-4 */ - { PCI_VDEVICE(MELLANOX, 0x1014) }, /* ConnectX-4 VF */ - { PCI_VDEVICE(MELLANOX, 0x1015) }, /* ConnectX-4LX */ - { PCI_VDEVICE(MELLANOX, 0x1016) }, /* ConnectX-4LX VF */ + { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */ + { PCI_VDEVICE(MELLANOX, 0x1012), MLX5_PCI_DEV_IS_VF}, /* Connect-IB VF */ + { PCI_VDEVICE(MELLANOX, 0x1013) }, /* ConnectX-4 */ + { PCI_VDEVICE(MELLANOX, 0x1014), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4 VF */ + { PCI_VDEVICE(MELLANOX, 0x1015) }, /* ConnectX-4LX */ + { PCI_VDEVICE(MELLANOX, 0x1016), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4LX VF */ { 0, } }; @@ -1378,7 +1399,8 @@ static struct pci_driver mlx5_core_driver = { .id_table = mlx5_core_pci_table, .probe = init_one, .remove = remove_one, - .err_handler = &mlx5_err_handler + .err_handler = &mlx5_err_handler, + .sriov_configure = mlx5_core_sriov_configure, }; static int __init init(void) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 1ed2239a6a6d..1649d5cf9e29 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -90,8 +90,10 @@ void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, unsigned long param); void mlx5_enter_error_state(struct mlx5_core_dev *dev); void mlx5_disable_device(struct mlx5_core_dev *dev); +int mlx5_core_sriov_configure(struct pci_dev *dev, int num_vfs); int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id); int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id); +int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev); void mlx5e_init(void); void mlx5e_cleanup(void); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index 4d3377b12657..9eeee0545f1c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include "mlx5_core.h" @@ -95,6 +96,7 @@ struct mlx5_manage_pages_outbox { enum { MAX_RECLAIM_TIME_MSECS = 5000, + MAX_RECLAIM_VFS_PAGES_TIME_MSECS = 2 * 1000 * 60, }; enum { @@ -352,6 +354,10 @@ retry: goto out_4k; } + dev->priv.fw_pages += npages; + if (func_id) + dev->priv.vfs_pages += npages; + mlx5_core_dbg(dev, "err %d\n", err); kvfree(in); @@ -405,6 +411,12 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages, } num_claimed = be32_to_cpu(out->num_entries); + if (num_claimed > npages) { + mlx5_core_warn(dev, "fw returned %d, driver asked %d => corruption\n", + num_claimed, npages); + err = -EINVAL; + goto out_free; + } if (nclaimed) *nclaimed = num_claimed; @@ -412,6 +424,9 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages, addr = be64_to_cpu(out->pas[i]); free_4k(dev, addr); } + dev->priv.fw_pages -= num_claimed; + if (func_id) + dev->priv.vfs_pages -= num_claimed; out_free: kvfree(out); @@ -548,3 +563,26 @@ void mlx5_pagealloc_stop(struct mlx5_core_dev *dev) { destroy_workqueue(dev->priv.pg_wq); } + +int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev) +{ + unsigned long end = jiffies + msecs_to_jiffies(MAX_RECLAIM_VFS_PAGES_TIME_MSECS); + int prev_vfs_pages = dev->priv.vfs_pages; + + mlx5_core_dbg(dev, "Waiting for %d pages from %s\n", prev_vfs_pages, + dev->priv.name); + while (dev->priv.vfs_pages) { + if (time_after(jiffies, end)) { + mlx5_core_warn(dev, "aborting while there are %d pending pages\n", dev->priv.vfs_pages); + return -ETIMEDOUT; + } + if (dev->priv.vfs_pages < prev_vfs_pages) { + end = jiffies + msecs_to_jiffies(MAX_RECLAIM_VFS_PAGES_TIME_MSECS); + prev_vfs_pages = dev->priv.vfs_pages; + } + msleep(50); + } + + mlx5_core_dbg(dev, "All pages received from %s\n", dev->priv.name); + return 0; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c new file mode 100644 index 000000000000..19a43240e359 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2014, Mellanox Technologies inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "mlx5_core.h" + +static void enable_vfs(struct mlx5_core_dev *dev, int num_vfs) +{ + struct mlx5_core_sriov *sriov = &dev->priv.sriov; + int err; + int vf; + + for (vf = 1; vf <= num_vfs; vf++) { + err = mlx5_core_enable_hca(dev, vf); + if (err) { + mlx5_core_warn(dev, "failed to enable VF %d\n", vf - 1); + } else { + sriov->vfs_ctx[vf - 1].enabled = 1; + mlx5_core_dbg(dev, "successfully enabled VF %d\n", vf - 1); + } + } +} + +static void disable_vfs(struct mlx5_core_dev *dev, int num_vfs) +{ + struct mlx5_core_sriov *sriov = &dev->priv.sriov; + int vf; + + for (vf = 1; vf <= num_vfs; vf++) { + if (sriov->vfs_ctx[vf - 1].enabled) { + if (mlx5_core_disable_hca(dev, vf)) + mlx5_core_warn(dev, "failed to disable VF %d\n", vf - 1); + else + sriov->vfs_ctx[vf - 1].enabled = 0; + } + } +} + +static int mlx5_core_create_vfs(struct pci_dev *pdev, int num_vfs) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + int err; + + if (pci_num_vf(pdev)) + pci_disable_sriov(pdev); + + enable_vfs(dev, num_vfs); + + err = pci_enable_sriov(pdev, num_vfs); + if (err) { + dev_warn(&pdev->dev, "enable sriov failed %d\n", err); + goto ex; + } + + return 0; + +ex: + disable_vfs(dev, num_vfs); + return err; +} + +static int mlx5_core_sriov_enable(struct pci_dev *pdev, int num_vfs) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_core_sriov *sriov = &dev->priv.sriov; + int err; + + kfree(sriov->vfs_ctx); + sriov->vfs_ctx = kcalloc(num_vfs, sizeof(*sriov->vfs_ctx), GFP_ATOMIC); + if (!sriov->vfs_ctx) + return -ENOMEM; + + sriov->enabled_vfs = num_vfs; + err = mlx5_core_create_vfs(pdev, num_vfs); + if (err) { + kfree(sriov->vfs_ctx); + sriov->vfs_ctx = NULL; + return err; + } + + return 0; +} + +static void mlx5_core_init_vfs(struct mlx5_core_dev *dev, int num_vfs) +{ + struct mlx5_core_sriov *sriov = &dev->priv.sriov; + + sriov->num_vfs = num_vfs; +} + +static void mlx5_core_cleanup_vfs(struct mlx5_core_dev *dev) +{ + struct mlx5_core_sriov *sriov; + + sriov = &dev->priv.sriov; + disable_vfs(dev, sriov->num_vfs); + + if (mlx5_wait_for_vf_pages(dev)) + mlx5_core_warn(dev, "timeout claiming VFs pages\n"); + + sriov->num_vfs = 0; +} + +int mlx5_core_sriov_configure(struct pci_dev *pdev, int num_vfs) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_core_sriov *sriov = &dev->priv.sriov; + int err; + + mlx5_core_dbg(dev, "requsted num_vfs %d\n", num_vfs); + if (!mlx5_core_is_pf(dev)) + return -EPERM; + + mlx5_core_cleanup_vfs(dev); + + if (!num_vfs) { + kfree(sriov->vfs_ctx); + sriov->vfs_ctx = NULL; + if (!pci_vfs_assigned(pdev)) + pci_disable_sriov(pdev); + else + pr_info("unloading PF driver while leaving orphan VFs\n"); + + return 0; + } + + err = mlx5_core_sriov_enable(pdev, num_vfs); + if (err) { + dev_warn(&pdev->dev, "mlx5_core_sriov_enable failed %d\n", err); + return err; + } + + mlx5_core_init_vfs(dev, num_vfs); + + return num_vfs; +} + +static int sync_required(struct pci_dev *pdev) +{ + struct mlx5_core_dev *dev = pci_get_drvdata(pdev); + struct mlx5_core_sriov *sriov = &dev->priv.sriov; + int cur_vfs = pci_num_vf(pdev); + + if (cur_vfs != sriov->num_vfs) { + pr_info("current VFs %d, registered %d - sync needed\n", cur_vfs, sriov->num_vfs); + return 1; + } + + return 0; +} + +int mlx5_sriov_init(struct mlx5_core_dev *dev) +{ + struct mlx5_core_sriov *sriov = &dev->priv.sriov; + struct pci_dev *pdev = dev->pdev; + int cur_vfs; + + if (!mlx5_core_is_pf(dev)) + return 0; + + if (!sync_required(dev->pdev)) + return 0; + + cur_vfs = pci_num_vf(pdev); + sriov->vfs_ctx = kcalloc(cur_vfs, sizeof(*sriov->vfs_ctx), GFP_KERNEL); + if (!sriov->vfs_ctx) + return -ENOMEM; + + sriov->enabled_vfs = cur_vfs; + + mlx5_core_init_vfs(dev, cur_vfs); + + enable_vfs(dev, cur_vfs); + + return 0; +} + +int mlx5_sriov_cleanup(struct mlx5_core_dev *dev) +{ + struct pci_dev *pdev = dev->pdev; + int err; + + if (!mlx5_core_is_pf(dev)) + return 0; + + err = mlx5_core_sriov_configure(pdev, 0); + if (err) + return err; + + return 0; +} diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 5c857f2a20d7..efebb87163c8 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -426,6 +426,16 @@ struct mlx5_mr_table { struct radix_tree_root tree; }; +struct mlx5_vf_context { + int enabled; +}; + +struct mlx5_core_sriov { + struct mlx5_vf_context *vfs_ctx; + int num_vfs; + int enabled_vfs; +}; + struct mlx5_irq_info { cpumask_var_t mask; char name[MLX5_MAX_IRQ_NAME]; @@ -447,6 +457,7 @@ struct mlx5_priv { int fw_pages; atomic_t reg_pages; struct list_head free_list; + int vfs_pages; struct mlx5_core_health health; @@ -485,6 +496,8 @@ struct mlx5_priv { struct list_head dev_list; struct list_head ctx_list; spinlock_t ctx_lock; + struct mlx5_core_sriov sriov; + unsigned long pci_dev_data; }; enum mlx5_device_state { @@ -739,6 +752,8 @@ void mlx5_pagealloc_init(struct mlx5_core_dev *dev); void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev); int mlx5_pagealloc_start(struct mlx5_core_dev *dev); void mlx5_pagealloc_stop(struct mlx5_core_dev *dev); +int mlx5_sriov_init(struct mlx5_core_dev *dev); +int mlx5_sriov_cleanup(struct mlx5_core_dev *dev); void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id, s32 npages); int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot); @@ -884,6 +899,15 @@ struct mlx5_profile { } mr_cache[MAX_MR_CACHE_ENTRIES]; }; +enum { + MLX5_PCI_DEV_IS_VF = 1 << 0, +}; + +static inline int mlx5_core_is_pf(struct mlx5_core_dev *dev) +{ + return !(dev->priv.pci_dev_data & MLX5_PCI_DEV_IS_VF); +} + static inline int mlx5_get_gid_table_len(u16 param) { if (param > 4) { diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 1565324eb620..9b76fddd696b 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -665,7 +665,9 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 reserved_17[0x1]; u8 ets[0x1]; u8 nic_flow_table[0x1]; - u8 reserved_18[0x4]; + u8 reserved_18_0; + u8 early_vf_enable; + u8 reserved_18[0x2]; u8 local_ca_ack_delay[0x5]; u8 reserved_19[0x6]; u8 port_type[0x2]; -- cgit v1.2.3-59-g8ed1b From e1d7d349c69d12721c420f1fe673ce9aa462aadd Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:11 +0200 Subject: net/mlx5: Update access functions to Query/Modify vport MAC address In preparation for SR-IOV we add here an API to enable each e-switch client (PF/VF) to configure its L2 MAC addresses and for the e-switch manager (usually the PF) to access them in order to be able to configure them into the e-switch. Therefore we now pass vport num parameter to mlx5_query_nic_vport_context, so PF can access other vports contexts. preperation for ethernet sriov and l2 table management. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/vport.c | 87 ++++++++++++++++++++--- include/linux/mlx5/vport.h | 5 +- 3 files changed, 81 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index f6a8cc787603..2ef717f98b0d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2028,7 +2028,7 @@ static void mlx5e_set_netdev_dev_addr(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); - mlx5_query_nic_vport_mac_address(priv->mdev, netdev->dev_addr); + mlx5_query_nic_vport_mac_address(priv->mdev, 0, netdev->dev_addr); } static void mlx5e_build_netdev(struct net_device *netdev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index b94177ebcf3a..442916e98724 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -57,33 +57,98 @@ u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod) } EXPORT_SYMBOL(mlx5_query_vport_state); -void mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, u8 *addr) +static int mlx5_query_nic_vport_context(struct mlx5_core_dev *mdev, u16 vport, + u32 *out, int outlen) +{ + u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)]; + + memset(in, 0, sizeof(in)); + + MLX5_SET(query_nic_vport_context_in, in, opcode, + MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT); + + MLX5_SET(query_nic_vport_context_in, in, vport_number, vport); + if (vport) + MLX5_SET(query_nic_vport_context_in, in, other_vport, 1); + + return mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen); +} + +static int mlx5_modify_nic_vport_context(struct mlx5_core_dev *mdev, void *in, + int inlen) +{ + u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)]; + + MLX5_SET(modify_nic_vport_context_in, in, opcode, + MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT); + + memset(out, 0, sizeof(out)); + return mlx5_cmd_exec_check_status(mdev, in, inlen, out, sizeof(out)); +} + +int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, + u16 vport, u8 *addr) { - u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)]; u32 *out; int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out); u8 *out_addr; + int err; out = mlx5_vzalloc(outlen); if (!out) - return; + return -ENOMEM; out_addr = MLX5_ADDR_OF(query_nic_vport_context_out, out, nic_vport_context.permanent_address); - memset(in, 0, sizeof(in)); - - MLX5_SET(query_nic_vport_context_in, in, opcode, - MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT); - - memset(out, 0, outlen); - mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen); + err = mlx5_query_nic_vport_context(mdev, vport, out, outlen); + if (err) + goto out; ether_addr_copy(addr, &out_addr[2]); +out: kvfree(out); + return err; +} +EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_mac_address); + +int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *mdev, + u16 vport, u8 *addr) +{ + void *in; + int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in); + int err; + void *nic_vport_ctx; + u8 *perm_mac; + + in = mlx5_vzalloc(inlen); + if (!in) { + mlx5_core_warn(mdev, "failed to allocate inbox\n"); + return -ENOMEM; + } + + MLX5_SET(modify_nic_vport_context_in, in, + field_select.permanent_address, 1); + MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport); + + if (vport) + MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1); + + nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in, + in, nic_vport_context); + perm_mac = MLX5_ADDR_OF(nic_vport_context, nic_vport_ctx, + permanent_address); + + ether_addr_copy(&perm_mac[2], addr); + + err = mlx5_modify_nic_vport_context(mdev, in, inlen); + + kvfree(in); + + return err; } -EXPORT_SYMBOL(mlx5_query_nic_vport_mac_address); +EXPORT_SYMBOL(mlx5_modify_nic_vport_mac_address); int mlx5_query_hca_vport_gid(struct mlx5_core_dev *dev, u8 other_vport, u8 port_num, u16 vf_num, u16 gid_index, diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index 967e0fd06e89..43e82d9f5463 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -36,7 +36,10 @@ #include u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod); -void mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, u8 *addr); +int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, + u16 vport, u8 *addr); +int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *dev, + u16 vport, u8 *addr); int mlx5_query_hca_vport_gid(struct mlx5_core_dev *dev, u8 other_vport, u8 port_num, u16 vf_num, u16 gid_index, union ib_gid *gid); -- cgit v1.2.3-59-g8ed1b From e16aea2744abea612c27ee0eef606c6a6a8204de Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:12 +0200 Subject: net/mlx5: Introduce access functions to modify/query vport mac lists Those functions are needed to notify the upcoming L2 table and SR-IOV E-Switch(FDB) manager(PF), of the NIC vport (vf) UC/MC mac lists changes. preperation for ethernet sriov and l2 table management. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/vport.c | 119 ++++++++++++++++++++++++ include/linux/mlx5/device.h | 6 ++ include/linux/mlx5/vport.h | 10 ++ 3 files changed, 135 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index 442916e98724..986d0d364df7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -150,6 +150,125 @@ int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *mdev, } EXPORT_SYMBOL(mlx5_modify_nic_vport_mac_address); +int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, + u32 vport, + enum mlx5_list_type list_type, + u8 addr_list[][ETH_ALEN], + int *list_size) +{ + u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)]; + void *nic_vport_ctx; + int max_list_size; + int req_list_size; + int out_sz; + void *out; + int err; + int i; + + req_list_size = *list_size; + + max_list_size = list_type == MLX5_NVPRT_LIST_TYPE_UC ? + 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) : + 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list); + + if (req_list_size > max_list_size) { + mlx5_core_warn(dev, "Requested list size (%d) > (%d) max_list_size\n", + req_list_size, max_list_size); + req_list_size = max_list_size; + } + + out_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) + + req_list_size * MLX5_ST_SZ_BYTES(mac_address_layout); + + memset(in, 0, sizeof(in)); + out = kzalloc(out_sz, GFP_KERNEL); + if (!out) + return -ENOMEM; + + MLX5_SET(query_nic_vport_context_in, in, opcode, + MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT); + MLX5_SET(query_nic_vport_context_in, in, allowed_list_type, list_type); + MLX5_SET(query_nic_vport_context_in, in, vport_number, vport); + + if (vport) + MLX5_SET(query_nic_vport_context_in, in, other_vport, 1); + + err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, out_sz); + if (err) + goto out; + + nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out, + nic_vport_context); + req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx, + allowed_list_size); + + *list_size = req_list_size; + for (i = 0; i < req_list_size; i++) { + u8 *mac_addr = MLX5_ADDR_OF(nic_vport_context, + nic_vport_ctx, + current_uc_mac_address[i]) + 2; + ether_addr_copy(addr_list[i], mac_addr); + } +out: + kfree(out); + return err; +} +EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_mac_list); + +int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev, + enum mlx5_list_type list_type, + u8 addr_list[][ETH_ALEN], + int list_size) +{ + u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)]; + void *nic_vport_ctx; + int max_list_size; + int in_sz; + void *in; + int err; + int i; + + max_list_size = list_type == MLX5_NVPRT_LIST_TYPE_UC ? + 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) : + 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list); + + if (list_size > max_list_size) + return -ENOSPC; + + in_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) + + list_size * MLX5_ST_SZ_BYTES(mac_address_layout); + + memset(out, 0, sizeof(out)); + in = kzalloc(in_sz, GFP_KERNEL); + if (!in) + return -ENOMEM; + + MLX5_SET(modify_nic_vport_context_in, in, opcode, + MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT); + MLX5_SET(modify_nic_vport_context_in, in, + field_select.addresses_list, 1); + + nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in, in, + nic_vport_context); + + MLX5_SET(nic_vport_context, nic_vport_ctx, + allowed_list_type, list_type); + MLX5_SET(nic_vport_context, nic_vport_ctx, + allowed_list_size, list_size); + + for (i = 0; i < list_size; i++) { + u8 *curr_mac = MLX5_ADDR_OF(nic_vport_context, + nic_vport_ctx, + current_uc_mac_address[i]) + 2; + ether_addr_copy(curr_mac, addr_list[i]); + } + + err = mlx5_cmd_exec_check_status(dev, in, in_sz, out, sizeof(out)); + kfree(in); + return err; +} +EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mac_list); + int mlx5_query_hca_vport_gid(struct mlx5_core_dev *dev, u8 other_vport, u8 port_num, u16 vf_num, u16 gid_index, union ib_gid *gid) diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 0b473cbfa7ef..0d2f0435a9f0 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -1102,6 +1102,12 @@ enum { MLX5_FLOW_CONTEXT_DEST_TYPE_TIR = 2, }; +enum mlx5_list_type { + MLX5_NVPRT_LIST_TYPE_UC = 0x0, + MLX5_NVPRT_LIST_TYPE_MC = 0x1, + MLX5_NVPRT_LIST_TYPE_VLAN = 0x2, +}; + enum { MLX5_RQC_RQ_TYPE_MEMORY_RQ_INLINE = 0x0, MLX5_RQC_RQ_TYPE_MEMORY_RQ_RPM = 0x1, diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index 43e82d9f5463..00bbec8d9527 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -34,6 +34,7 @@ #define __MLX5_VPORT_H__ #include +#include u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod); int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, @@ -54,5 +55,14 @@ int mlx5_query_hca_vport_system_image_guid(struct mlx5_core_dev *dev, u64 *sys_image_guid); int mlx5_query_hca_vport_node_guid(struct mlx5_core_dev *dev, u64 *node_guid); +int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev, + u32 vport, + enum mlx5_list_type list_type, + u8 addr_list[][ETH_ALEN], + int *list_size); +int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev, + enum mlx5_list_type list_type, + u8 addr_list[][ETH_ALEN], + int list_size); #endif /* __MLX5_VPORT_H__ */ -- cgit v1.2.3-59-g8ed1b From e75465148b7df7f2796c75bf98bf33f171edeb2b Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:13 +0200 Subject: net/mlx5: Introduce access functions to modify/query vport state In preparation for SR-IOV we add here an API to enable each e-switch manager (PF) to configure its VFs link states in e-switch preparation for ethernet sriov. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/vport.c | 61 ++++++++++++++++++++--- include/linux/mlx5/mlx5_ifc.h | 1 + include/linux/mlx5/vport.h | 6 ++- 4 files changed, 62 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 2ef717f98b0d..007e464b3e58 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -63,7 +63,7 @@ static void mlx5e_update_carrier(struct mlx5e_priv *priv) u8 port_state; port_state = mlx5_query_vport_state(mdev, - MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT); + MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT, 0); if (port_state == VPORT_STATE_UP) netif_carrier_on(priv->netdev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index 986d0d364df7..b017a7e68b28 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -36,26 +36,75 @@ #include #include "mlx5_core.h" -u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod) +static int _mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, + u16 vport, u32 *out, int outlen) { - u32 in[MLX5_ST_SZ_DW(query_vport_state_in)]; - u32 out[MLX5_ST_SZ_DW(query_vport_state_out)]; int err; + u32 in[MLX5_ST_SZ_DW(query_vport_state_in)]; memset(in, 0, sizeof(in)); MLX5_SET(query_vport_state_in, in, opcode, MLX5_CMD_OP_QUERY_VPORT_STATE); MLX5_SET(query_vport_state_in, in, op_mod, opmod); + MLX5_SET(query_vport_state_in, in, vport_number, vport); + if (vport) + MLX5_SET(query_vport_state_in, in, other_vport, 1); - err = mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, - sizeof(out)); + err = mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen); if (err) mlx5_core_warn(mdev, "MLX5_CMD_OP_QUERY_VPORT_STATE failed\n"); + return err; +} + +u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport) +{ + u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {0}; + + _mlx5_query_vport_state(mdev, opmod, vport, out, sizeof(out)); + return MLX5_GET(query_vport_state_out, out, state); } -EXPORT_SYMBOL(mlx5_query_vport_state); +EXPORT_SYMBOL_GPL(mlx5_query_vport_state); + +u8 mlx5_query_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport) +{ + u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {0}; + + _mlx5_query_vport_state(mdev, opmod, vport, out, sizeof(out)); + + return MLX5_GET(query_vport_state_out, out, admin_state); +} +EXPORT_SYMBOL(mlx5_query_vport_admin_state); + +int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod, + u16 vport, u8 state) +{ + u32 in[MLX5_ST_SZ_DW(modify_vport_state_in)]; + u32 out[MLX5_ST_SZ_DW(modify_vport_state_out)]; + int err; + + memset(in, 0, sizeof(in)); + + MLX5_SET(modify_vport_state_in, in, opcode, + MLX5_CMD_OP_MODIFY_VPORT_STATE); + MLX5_SET(modify_vport_state_in, in, op_mod, opmod); + MLX5_SET(modify_vport_state_in, in, vport_number, vport); + + if (vport) + MLX5_SET(modify_vport_state_in, in, other_vport, 1); + + MLX5_SET(modify_vport_state_in, in, admin_state, state); + + err = mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, + sizeof(out)); + if (err) + mlx5_core_warn(mdev, "MLX5_CMD_OP_MODIFY_VPORT_STATE failed\n"); + + return err; +} +EXPORT_SYMBOL(mlx5_modify_vport_admin_state); static int mlx5_query_nic_vport_context(struct mlx5_core_dev *mdev, u16 vport, u32 *out, int outlen) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 836cf0e43174..655184702ea2 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -2946,6 +2946,7 @@ struct mlx5_ifc_query_vport_state_out_bits { enum { MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT = 0x0, + MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT = 0x1, }; struct mlx5_ifc_query_vport_state_in_bits { diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index 00bbec8d9527..c1bba5948851 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -36,7 +36,11 @@ #include #include -u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod); +u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport); +u8 mlx5_query_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod, + u16 vport); +int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod, + u16 vport, u8 state); int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, u16 vport, u8 *addr); int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *dev, -- cgit v1.2.3-59-g8ed1b From d82b73186dab70d6d332dd2afdb48608be2e5230 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:14 +0200 Subject: net/mlx5: Introduce access functions to modify/query vport promisc mode Those functions are needed to notify the upcoming SR-IOV E-Switch(FDB) manager(PF), of the NIC vport (vf) promisc mode changes. Preperation for ethernet sriov and l2 table management. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/vport.c | 62 +++++++++++++++++++++++++ include/linux/mlx5/mlx5_ifc.h | 28 +++++++++-- include/linux/mlx5/vport.h | 9 ++++ 3 files changed, 94 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index b017a7e68b28..68aa51df29c1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -576,3 +576,65 @@ int mlx5_query_hca_vport_node_guid(struct mlx5_core_dev *dev, return err; } EXPORT_SYMBOL_GPL(mlx5_query_hca_vport_node_guid); + +int mlx5_query_nic_vport_promisc(struct mlx5_core_dev *mdev, + u32 vport, + int *promisc_uc, + int *promisc_mc, + int *promisc_all) +{ + u32 *out; + int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out); + int err; + + out = kzalloc(outlen, GFP_KERNEL); + if (!out) + return -ENOMEM; + + err = mlx5_query_nic_vport_context(mdev, vport, out, outlen); + if (err) + goto out; + + *promisc_uc = MLX5_GET(query_nic_vport_context_out, out, + nic_vport_context.promisc_uc); + *promisc_mc = MLX5_GET(query_nic_vport_context_out, out, + nic_vport_context.promisc_mc); + *promisc_all = MLX5_GET(query_nic_vport_context_out, out, + nic_vport_context.promisc_all); + +out: + kfree(out); + return err; +} +EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_promisc); + +int mlx5_modify_nic_vport_promisc(struct mlx5_core_dev *mdev, + int promisc_uc, + int promisc_mc, + int promisc_all) +{ + void *in; + int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in); + int err; + + in = mlx5_vzalloc(inlen); + if (!in) { + mlx5_core_err(mdev, "failed to allocate inbox\n"); + return -ENOMEM; + } + + MLX5_SET(modify_nic_vport_context_in, in, field_select.promisc, 1); + MLX5_SET(modify_nic_vport_context_in, in, + nic_vport_context.promisc_uc, promisc_uc); + MLX5_SET(modify_nic_vport_context_in, in, + nic_vport_context.promisc_mc, promisc_mc); + MLX5_SET(modify_nic_vport_context_in, in, + nic_vport_context.promisc_all, promisc_all); + + err = mlx5_modify_nic_vport_context(mdev, in, inlen); + + kvfree(in); + + return err; +} +EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_promisc); diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 655184702ea2..2728b5f6c017 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -2147,16 +2147,31 @@ struct mlx5_ifc_nic_vport_context_bits { u8 reserved_0[0x1f]; u8 roce_en[0x1]; - u8 reserved_1[0x760]; + u8 arm_change_event[0x1]; + u8 reserved_1[0x1a]; + u8 event_on_mtu[0x1]; + u8 event_on_promisc_change[0x1]; + u8 event_on_vlan_change[0x1]; + u8 event_on_mc_address_change[0x1]; + u8 event_on_uc_address_change[0x1]; - u8 reserved_2[0x5]; + u8 reserved_2[0xf0]; + + u8 mtu[0x10]; + + u8 reserved_3[0x640]; + + u8 promisc_uc[0x1]; + u8 promisc_mc[0x1]; + u8 promisc_all[0x1]; + u8 reserved_4[0x2]; u8 allowed_list_type[0x3]; - u8 reserved_3[0xc]; + u8 reserved_5[0xc]; u8 allowed_list_size[0xc]; struct mlx5_ifc_mac_address_layout_bits permanent_address; - u8 reserved_4[0x20]; + u8 reserved_6[0x20]; u8 current_uc_mac_address[0][0x40]; }; @@ -4235,7 +4250,10 @@ struct mlx5_ifc_modify_nic_vport_context_out_bits { }; struct mlx5_ifc_modify_nic_vport_field_select_bits { - u8 reserved_0[0x1c]; + u8 reserved_0[0x19]; + u8 mtu[0x1]; + u8 change_event[0x1]; + u8 promisc[0x1]; u8 permanent_address[0x1]; u8 addresses_list[0x1]; u8 roce_en[0x1]; diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index c1bba5948851..dbbaed9f975a 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -68,5 +68,14 @@ int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev, enum mlx5_list_type list_type, u8 addr_list[][ETH_ALEN], int list_size); +int mlx5_query_nic_vport_promisc(struct mlx5_core_dev *mdev, + u32 vport, + int *promisc_uc, + int *promisc_mc, + int *promisc_all); +int mlx5_modify_nic_vport_promisc(struct mlx5_core_dev *mdev, + int promisc_uc, + int promisc_mc, + int promisc_all); #endif /* __MLX5_VPORT_H__ */ -- cgit v1.2.3-59-g8ed1b From c0046cf7b81ac55b8bf056c71918ec04edd99379 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:15 +0200 Subject: net/mlx5: Introduce access functions to modify/query vport vlans Those functions are needed to notify the upcoming L2 table and SR-IOV E-Switch(FDB) manager(PF), of the NIC vport (vf) vlan table changes. preperation for ethernet sriov and l2 table management. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/vport.c | 112 ++++++++++++++++++++++++ include/linux/mlx5/mlx5_ifc.h | 7 ++ include/linux/mlx5/vport.h | 7 ++ 3 files changed, 126 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c index 68aa51df29c1..076197efea9b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c @@ -318,6 +318,118 @@ int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev, } EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mac_list); +int mlx5_query_nic_vport_vlans(struct mlx5_core_dev *dev, + u32 vport, + u16 vlans[], + int *size) +{ + u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)]; + void *nic_vport_ctx; + int req_list_size; + int max_list_size; + int out_sz; + void *out; + int err; + int i; + + req_list_size = *size; + max_list_size = 1 << MLX5_CAP_GEN(dev, log_max_vlan_list); + if (req_list_size > max_list_size) { + mlx5_core_warn(dev, "Requested list size (%d) > (%d) max list size\n", + req_list_size, max_list_size); + req_list_size = max_list_size; + } + + out_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) + + req_list_size * MLX5_ST_SZ_BYTES(vlan_layout); + + memset(in, 0, sizeof(in)); + out = kzalloc(out_sz, GFP_KERNEL); + if (!out) + return -ENOMEM; + + MLX5_SET(query_nic_vport_context_in, in, opcode, + MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT); + MLX5_SET(query_nic_vport_context_in, in, allowed_list_type, + MLX5_NVPRT_LIST_TYPE_VLAN); + MLX5_SET(query_nic_vport_context_in, in, vport_number, vport); + + if (vport) + MLX5_SET(query_nic_vport_context_in, in, other_vport, 1); + + err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, out_sz); + if (err) + goto out; + + nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out, + nic_vport_context); + req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx, + allowed_list_size); + + *size = req_list_size; + for (i = 0; i < req_list_size; i++) { + void *vlan_addr = MLX5_ADDR_OF(nic_vport_context, + nic_vport_ctx, + current_uc_mac_address[i]); + vlans[i] = MLX5_GET(vlan_layout, vlan_addr, vlan); + } +out: + kfree(out); + return err; +} +EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_vlans); + +int mlx5_modify_nic_vport_vlans(struct mlx5_core_dev *dev, + u16 vlans[], + int list_size) +{ + u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)]; + void *nic_vport_ctx; + int max_list_size; + int in_sz; + void *in; + int err; + int i; + + max_list_size = 1 << MLX5_CAP_GEN(dev, log_max_vlan_list); + + if (list_size > max_list_size) + return -ENOSPC; + + in_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) + + list_size * MLX5_ST_SZ_BYTES(vlan_layout); + + memset(out, 0, sizeof(out)); + in = kzalloc(in_sz, GFP_KERNEL); + if (!in) + return -ENOMEM; + + MLX5_SET(modify_nic_vport_context_in, in, opcode, + MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT); + MLX5_SET(modify_nic_vport_context_in, in, + field_select.addresses_list, 1); + + nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in, in, + nic_vport_context); + + MLX5_SET(nic_vport_context, nic_vport_ctx, + allowed_list_type, MLX5_NVPRT_LIST_TYPE_VLAN); + MLX5_SET(nic_vport_context, nic_vport_ctx, + allowed_list_size, list_size); + + for (i = 0; i < list_size; i++) { + void *vlan_addr = MLX5_ADDR_OF(nic_vport_context, + nic_vport_ctx, + current_uc_mac_address[i]); + MLX5_SET(vlan_layout, vlan_addr, vlan, vlans[i]); + } + + err = mlx5_cmd_exec_check_status(dev, in, in_sz, out, sizeof(out)); + kfree(in); + return err; +} +EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_vlans); + int mlx5_query_hca_vport_gid(struct mlx5_core_dev *dev, u8 other_vport, u8 port_num, u16 vf_num, u16 gid_index, union ib_gid *gid) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 2728b5f6c017..39487d0c305d 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -910,6 +910,13 @@ struct mlx5_ifc_mac_address_layout_bits { u8 mac_addr_31_0[0x20]; }; +struct mlx5_ifc_vlan_layout_bits { + u8 reserved_0[0x14]; + u8 vlan[0x0c]; + + u8 reserved_1[0x20]; +}; + struct mlx5_ifc_cong_control_r_roce_ecn_np_bits { u8 reserved_0[0xa0]; diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h index dbbaed9f975a..638f2ca7a527 100644 --- a/include/linux/mlx5/vport.h +++ b/include/linux/mlx5/vport.h @@ -77,5 +77,12 @@ int mlx5_modify_nic_vport_promisc(struct mlx5_core_dev *mdev, int promisc_uc, int promisc_mc, int promisc_all); +int mlx5_query_nic_vport_vlans(struct mlx5_core_dev *dev, + u32 vport, + u16 vlans[], + int *size); +int mlx5_modify_nic_vport_vlans(struct mlx5_core_dev *dev, + u16 vlans[], + int list_size); #endif /* __MLX5_VPORT_H__ */ -- cgit v1.2.3-59-g8ed1b From 5e55da1d5a5f5ffd2038def3bfdcb2f8f3a0418a Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:16 +0200 Subject: net/mlx5e: Write UC/MC list and promisc mode into vport context Each Vport/vNIC must notify underlying e-Switch layer for UC/MC list and promisc mode updates, in-order to update l2 tables and SR-IOV FDB tables. We do that at set_rx_mode ndo. preperation for ethernet-SRIOV and l2 table management. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlx5/core/en_flow_table.c | 87 ++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c b/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c index 22d603f78273..fb02f6c0891f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c @@ -671,6 +671,91 @@ static void mlx5e_sync_netdev_addr(struct mlx5e_priv *priv) netif_addr_unlock_bh(netdev); } +static void mlx5e_fill_addr_array(struct mlx5e_priv *priv, int list_type, + u8 addr_array[][ETH_ALEN], int size) +{ + bool is_uc = (list_type == MLX5_NVPRT_LIST_TYPE_UC); + struct net_device *ndev = priv->netdev; + struct mlx5e_eth_addr_hash_node *hn; + struct hlist_head *addr_list; + struct hlist_node *tmp; + int i = 0; + int hi; + + addr_list = is_uc ? priv->eth_addr.netdev_uc : priv->eth_addr.netdev_mc; + + if (is_uc) /* Make sure our own address is pushed first */ + ether_addr_copy(addr_array[i++], ndev->dev_addr); + else if (priv->eth_addr.broadcast_enabled) + ether_addr_copy(addr_array[i++], ndev->broadcast); + + mlx5e_for_each_hash_node(hn, tmp, addr_list, hi) { + if (ether_addr_equal(ndev->dev_addr, hn->ai.addr)) + continue; + if (i >= size) + break; + ether_addr_copy(addr_array[i++], hn->ai.addr); + } +} + +static void mlx5e_vport_context_update_addr_list(struct mlx5e_priv *priv, + int list_type) +{ + bool is_uc = (list_type == MLX5_NVPRT_LIST_TYPE_UC); + struct mlx5e_eth_addr_hash_node *hn; + u8 (*addr_array)[ETH_ALEN] = NULL; + struct hlist_head *addr_list; + struct hlist_node *tmp; + int max_size; + int size; + int err; + int hi; + + size = is_uc ? 0 : (priv->eth_addr.broadcast_enabled ? 1 : 0); + max_size = is_uc ? + 1 << MLX5_CAP_GEN(priv->mdev, log_max_current_uc_list) : + 1 << MLX5_CAP_GEN(priv->mdev, log_max_current_mc_list); + + addr_list = is_uc ? priv->eth_addr.netdev_uc : priv->eth_addr.netdev_mc; + mlx5e_for_each_hash_node(hn, tmp, addr_list, hi) + size++; + + if (size > max_size) { + netdev_warn(priv->netdev, + "netdev %s list size (%d) > (%d) max vport list size, some addresses will be dropped\n", + is_uc ? "UC" : "MC", size, max_size); + size = max_size; + } + + if (size) { + addr_array = kcalloc(size, ETH_ALEN, GFP_KERNEL); + if (!addr_array) { + err = -ENOMEM; + goto out; + } + mlx5e_fill_addr_array(priv, list_type, addr_array, size); + } + + err = mlx5_modify_nic_vport_mac_list(priv->mdev, list_type, addr_array, size); +out: + if (err) + netdev_err(priv->netdev, + "Failed to modify vport %s list err(%d)\n", + is_uc ? "UC" : "MC", err); + kfree(addr_array); +} + +static void mlx5e_vport_context_update(struct mlx5e_priv *priv) +{ + struct mlx5e_eth_addr_db *ea = &priv->eth_addr; + + mlx5e_vport_context_update_addr_list(priv, MLX5_NVPRT_LIST_TYPE_UC); + mlx5e_vport_context_update_addr_list(priv, MLX5_NVPRT_LIST_TYPE_MC); + mlx5_modify_nic_vport_promisc(priv->mdev, 0, + ea->allmulti_enabled, + ea->promisc_enabled); +} + static void mlx5e_apply_netdev_addr(struct mlx5e_priv *priv) { struct mlx5e_eth_addr_hash_node *hn; @@ -748,6 +833,8 @@ void mlx5e_set_rx_mode_work(struct work_struct *work) ea->promisc_enabled = promisc_enabled; ea->allmulti_enabled = allmulti_enabled; ea->broadcast_enabled = broadcast_enabled; + + mlx5e_vport_context_update(priv); } void mlx5e_init_eth_addr(struct mlx5e_priv *priv) -- cgit v1.2.3-59-g8ed1b From aad9e6e41e5e595528e316871bbc5be78a8a1eb4 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:17 +0200 Subject: net/mlx5e: Write vlan list into vport context Each Vport/vNIC must notify underlying e-Switch layer for vlan table changes in-order to update SR-IOV FDB tables. We do that at vlan_rx_add_vid and vlan_rx_kill_vid ndos. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 1 + .../ethernet/mellanox/mlx5/core/en_flow_table.c | 52 ++++++++++++++++++++++ 2 files changed, 53 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 69f1c1a412b4..89313d46952d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -465,6 +465,7 @@ enum { }; struct mlx5e_vlan_db { + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; u32 active_vlans_ft_ix[VLAN_N_VID]; u32 untagged_rule_ft_ix; u32 any_vlan_rule_ft_ix; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c b/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c index fb02f6c0891f..5b93c9c6e341 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c @@ -502,6 +502,49 @@ add_eth_addr_rule_out: return err; } +static int mlx5e_vport_context_update_vlans(struct mlx5e_priv *priv) +{ + struct net_device *ndev = priv->netdev; + int max_list_size; + int list_size; + u16 *vlans; + int vlan; + int err; + int i; + + list_size = 0; + for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID) + list_size++; + + max_list_size = 1 << MLX5_CAP_GEN(priv->mdev, log_max_vlan_list); + + if (list_size > max_list_size) { + netdev_warn(ndev, + "netdev vlans list size (%d) > (%d) max vport list size, some vlans will be dropped\n", + list_size, max_list_size); + list_size = max_list_size; + } + + vlans = kcalloc(list_size, sizeof(*vlans), GFP_KERNEL); + if (!vlans) + return -ENOMEM; + + i = 0; + for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID) { + if (i >= list_size) + break; + vlans[i++] = vlan; + } + + err = mlx5_modify_nic_vport_vlans(priv->mdev, vlans, list_size); + if (err) + netdev_err(ndev, "Failed to modify vport vlans list err(%d)\n", + err); + + kfree(vlans); + return err; +} + enum mlx5e_vlan_rule_type { MLX5E_VLAN_RULE_TYPE_UNTAGGED, MLX5E_VLAN_RULE_TYPE_ANY_VID, @@ -552,6 +595,10 @@ static int mlx5e_add_vlan_rule(struct mlx5e_priv *priv, 1); break; default: /* MLX5E_VLAN_RULE_TYPE_MATCH_VID */ + err = mlx5e_vport_context_update_vlans(priv); + if (err) + goto add_vlan_rule_out; + ft_ix = &priv->vlan.active_vlans_ft_ix[vid]; MLX5_SET(fte_match_param, match_value, outer_headers.vlan_tag, 1); @@ -588,6 +635,7 @@ static void mlx5e_del_vlan_rule(struct mlx5e_priv *priv, case MLX5E_VLAN_RULE_TYPE_MATCH_VID: mlx5_del_flow_table_entry(priv->ft.vlan, priv->vlan.active_vlans_ft_ix[vid]); + mlx5e_vport_context_update_vlans(priv); break; } } @@ -619,6 +667,8 @@ int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto, { struct mlx5e_priv *priv = netdev_priv(dev); + set_bit(vid, priv->vlan.active_vlans); + return mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid); } @@ -627,6 +677,8 @@ int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto, { struct mlx5e_priv *priv = netdev_priv(dev); + clear_bit(vid, priv->vlan.active_vlans); + mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid); return 0; -- cgit v1.2.3-59-g8ed1b From 073bb189a41d7bbad509b576a690611c46c4858f Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:18 +0200 Subject: net/mlx5: Introducing E-Switch and l2 table E-Switch is the software entity that represents and manages ConnectX4 inter-HCA ethernet l2 switching. E-Switch has its own Virtual Ports, each Vport/vNIC/VF can be connected to the device through a vport of an e-switch. Each e-switch is managed by one vNIC identified by HCA_CAP.vport_group_manager (usually it is the PF/vport[0]), and its main responsibility is to forward each packet to the right vport. e-Switch needs to manage its own l2-table and FDB tables. L2 table is a flow table that is managed by FW, it is needed for Multi-host (Multi PF) configuration for inter HCA switching between PFs. FDB table is a flow table that is totally managed by e-Switch driver, its main responsibility is to switch packets between e-Swtich internal vports and uplink vport that belong to the same. This patch introduces only e-Swtich l2 table management, FDB managemnt will come later when ethernet SRIOV/VFs will be enabled. preperation for ethernet sriov and l2 table management. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 +- drivers/net/ethernet/mellanox/mlx5/core/eq.c | 13 + drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 500 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 122 ++++++ drivers/net/ethernet/mellanox/mlx5/core/main.c | 18 + include/linux/mlx5/device.h | 8 + include/linux/mlx5/driver.h | 4 + 7 files changed, 666 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/eswitch.c create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/eswitch.h (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 4d5103911527..a0755919ccaf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_MLX5_CORE) += mlx5_core.o mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \ mad.o transobj.o vport.o sriov.o -mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o flow_table.o \ +mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o flow_table.o eswitch.o \ en_main.o en_flow_table.o en_ethtool.o en_tx.o en_rx.o \ en_txrx.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index 713ead583347..23c244a7e5d7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -35,6 +35,9 @@ #include #include #include "mlx5_core.h" +#ifdef CONFIG_MLX5_CORE_EN +#include "eswitch.h" +#endif enum { MLX5_EQE_SIZE = sizeof(struct mlx5_eqe), @@ -287,6 +290,11 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq) break; #endif +#ifdef CONFIG_MLX5_CORE_EN + case MLX5_EVENT_TYPE_NIC_VPORT_CHANGE: + mlx5_eswitch_vport_event(dev->priv.eswitch, eqe); + break; +#endif default: mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n", eqe->type, eq->eqn); @@ -459,6 +467,11 @@ int mlx5_start_eqs(struct mlx5_core_dev *dev) if (MLX5_CAP_GEN(dev, pg)) async_event_mask |= (1ull << MLX5_EVENT_TYPE_PAGE_FAULT); + if (MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_ETH && + MLX5_CAP_GEN(dev, vport_group_manager) && + mlx5_core_is_pf(dev)) + async_event_mask |= (1ull << MLX5_EVENT_TYPE_NIC_VPORT_CHANGE); + err = mlx5_create_map_eq(dev, &table->cmd_eq, MLX5_EQ_VEC_CMD, MLX5_NUM_CMD_EQE, 1ull << MLX5_EVENT_TYPE_CMD, "mlx5_cmd_eq", &dev->priv.uuari.uars[0]); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c new file mode 100644 index 000000000000..1f2f804bde3e --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2015, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include "mlx5_core.h" +#include "eswitch.h" + +#define MLX5_DEBUG_ESWITCH_MASK BIT(3) + +#define esw_info(dev, format, ...) \ + pr_info("(%s): E-Switch: " format, (dev)->priv.name, ##__VA_ARGS__) + +#define esw_warn(dev, format, ...) \ + pr_warn("(%s): E-Switch: " format, (dev)->priv.name, ##__VA_ARGS__) + +#define esw_debug(dev, format, ...) \ + mlx5_core_dbg_mask(dev, MLX5_DEBUG_ESWITCH_MASK, format, ##__VA_ARGS__) + +enum { + MLX5_ACTION_NONE = 0, + MLX5_ACTION_ADD = 1, + MLX5_ACTION_DEL = 2, +}; + +/* HW UC L2 table hash node */ +struct mlx5_uc_l2addr { + struct l2addr_node node; + u8 action; + u32 table_index; + u32 vport; +}; + +/* Vport UC L2 table hash node */ +struct mlx5_vport_addr { + struct l2addr_node node; + u8 action; +}; + +enum { + UC_ADDR_CHANGE = BIT(0), + MC_ADDR_CHANGE = BIT(1), +}; + +static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, int vport, + u32 events_mask) +{ + int in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)]; + int out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)]; + void *nic_vport_ctx; + int err; + + memset(out, 0, sizeof(out)); + memset(in, 0, sizeof(in)); + + MLX5_SET(modify_nic_vport_context_in, in, + opcode, MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT); + MLX5_SET(modify_nic_vport_context_in, in, field_select.change_event, 1); + MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport); + if (vport) + MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1); + nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in, + in, nic_vport_context); + + MLX5_SET(nic_vport_context, nic_vport_ctx, arm_change_event, 1); + + if (events_mask & UC_ADDR_CHANGE) + MLX5_SET(nic_vport_context, nic_vport_ctx, + event_on_uc_address_change, 1); + if (events_mask & MC_ADDR_CHANGE) + MLX5_SET(nic_vport_context, nic_vport_ctx, + event_on_mc_address_change, 1); + + err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); + if (err) + goto ex; + err = mlx5_cmd_status_to_err_v2(out); + if (err) + goto ex; + return 0; +ex: + return err; +} + +/* HW L2 Table (MPFS) management */ +static int set_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index, + u8 *mac, u8 vlan_valid, u16 vlan) +{ + u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)]; + u32 out[MLX5_ST_SZ_DW(set_l2_table_entry_out)]; + u8 *in_mac_addr; + + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + + MLX5_SET(set_l2_table_entry_in, in, opcode, + MLX5_CMD_OP_SET_L2_TABLE_ENTRY); + MLX5_SET(set_l2_table_entry_in, in, table_index, index); + MLX5_SET(set_l2_table_entry_in, in, vlan_valid, vlan_valid); + MLX5_SET(set_l2_table_entry_in, in, vlan, vlan); + + in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address); + ether_addr_copy(&in_mac_addr[2], mac); + + return mlx5_cmd_exec_check_status(dev, in, sizeof(in), + out, sizeof(out)); +} + +static int del_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index) +{ + u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)]; + u32 out[MLX5_ST_SZ_DW(delete_l2_table_entry_out)]; + + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + + MLX5_SET(delete_l2_table_entry_in, in, opcode, + MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY); + MLX5_SET(delete_l2_table_entry_in, in, table_index, index); + return mlx5_cmd_exec_check_status(dev, in, sizeof(in), + out, sizeof(out)); +} + +static int alloc_l2_table_index(struct mlx5_l2_table *l2_table, u32 *ix) +{ + int err = 0; + + *ix = find_first_zero_bit(l2_table->bitmap, l2_table->size); + if (*ix >= l2_table->size) + err = -ENOSPC; + else + __set_bit(*ix, l2_table->bitmap); + + return err; +} + +static void free_l2_table_index(struct mlx5_l2_table *l2_table, u32 ix) +{ + __clear_bit(ix, l2_table->bitmap); +} + +static int set_l2_table_entry(struct mlx5_core_dev *dev, u8 *mac, + u8 vlan_valid, u16 vlan, + u32 *index) +{ + struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table; + int err; + + err = alloc_l2_table_index(l2_table, index); + if (err) + return err; + + err = set_l2_table_entry_cmd(dev, *index, mac, vlan_valid, vlan); + if (err) + free_l2_table_index(l2_table, *index); + + return err; +} + +static void del_l2_table_entry(struct mlx5_core_dev *dev, u32 index) +{ + struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table; + + del_l2_table_entry_cmd(dev, index); + free_l2_table_index(l2_table, index); +} + +/* SW E-Switch L2 Table management */ +static int l2_table_addr_add(struct mlx5_eswitch *esw, + u8 mac[ETH_ALEN], u32 vport) +{ + struct hlist_head *hash; + struct mlx5_uc_l2addr *addr; + int err; + + hash = esw->l2_table.l2_hash; + addr = l2addr_hash_find(hash, mac, struct mlx5_uc_l2addr); + if (addr) { + esw_warn(esw->dev, + "Failed to set L2 mac(%pM) for vport(%d), mac is already in use by vport(%d)\n", + mac, vport, addr->vport); + return -EEXIST; + } + + addr = l2addr_hash_add(hash, mac, struct mlx5_uc_l2addr, + GFP_KERNEL); + if (!addr) + return -ENOMEM; + + addr->vport = vport; + addr->action = MLX5_ACTION_NONE; + err = set_l2_table_entry(esw->dev, mac, 0, 0, &addr->table_index); + if (err) + l2addr_hash_del(addr); + else + esw_debug(esw->dev, "\tADDED L2 MAC: vport[%d] %pM index:%d\n", + vport, addr->node.addr, addr->table_index); + return err; +} + +static int l2_table_addr_del(struct mlx5_eswitch *esw, + u8 mac[ETH_ALEN], u32 vport) +{ + struct hlist_head *hash; + struct mlx5_uc_l2addr *addr; + + hash = esw->l2_table.l2_hash; + addr = l2addr_hash_find(hash, mac, struct mlx5_uc_l2addr); + if (!addr || addr->vport != vport) { + esw_warn(esw->dev, "MAC(%pM) doesn't belong to vport (%d)\n", + mac, vport); + return -EINVAL; + } + + esw_debug(esw->dev, "\tDELETE L2 MAC: vport[%d] %pM index:%d\n", + vport, addr->node.addr, addr->table_index); + del_l2_table_entry(esw->dev, addr->table_index); + l2addr_hash_del(addr); + return 0; +} + +/* E-Switch vport uc list management */ + +/* Apply vport uc list to HW l2 table */ +static void esw_apply_vport_uc_list(struct mlx5_eswitch *esw, + u32 vport_num) +{ + struct mlx5_vport *vport = &esw->vports[vport_num]; + struct mlx5_vport_addr *addr; + struct l2addr_node *node; + struct hlist_head *hash; + struct hlist_node *tmp; + int hi; + + hash = vport->uc_list; + for_each_l2hash_node(node, tmp, hash, hi) { + addr = container_of(node, struct mlx5_vport_addr, node); + switch (addr->action) { + case MLX5_ACTION_ADD: + l2_table_addr_add(esw, addr->node.addr, vport_num); + addr->action = MLX5_ACTION_NONE; + break; + case MLX5_ACTION_DEL: + l2_table_addr_del(esw, addr->node.addr, vport_num); + l2addr_hash_del(addr); + break; + } + } +} + +/* Sync vport uc list from vport context */ +static void esw_update_vport_uc_list(struct mlx5_eswitch *esw, + u32 vport_num) +{ + struct mlx5_vport *vport = &esw->vports[vport_num]; + struct mlx5_vport_addr *addr; + struct l2addr_node *node; + u8 (*mac_list)[ETH_ALEN]; + struct hlist_head *hash; + struct hlist_node *tmp; + int size; + int err; + int hi; + int i; + + size = MLX5_MAX_UC_PER_VPORT(esw->dev); + + mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL); + if (!mac_list) + return; + + hash = vport->uc_list; + + for_each_l2hash_node(node, tmp, hash, hi) { + addr = container_of(node, struct mlx5_vport_addr, node); + addr->action = MLX5_ACTION_DEL; + } + + err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, + MLX5_NVPRT_LIST_TYPE_UC, + mac_list, &size); + if (err) + return; + esw_debug(esw->dev, "vport[%d] context update UC list size (%d)\n", + vport_num, size); + + for (i = 0; i < size; i++) { + if (!is_valid_ether_addr(mac_list[i])) + continue; + + addr = l2addr_hash_find(hash, mac_list[i], + struct mlx5_vport_addr); + if (addr) { + addr->action = MLX5_ACTION_NONE; + continue; + } + + addr = l2addr_hash_add(hash, mac_list[i], + struct mlx5_vport_addr, + GFP_KERNEL); + if (!addr) { + esw_warn(esw->dev, + "Failed to add MAC(%pM) to vport[%d] DB\n", + mac_list[i], vport_num); + continue; + } + addr->action = MLX5_ACTION_ADD; + } + kfree(mac_list); +} + +static void esw_vport_change_handler(struct work_struct *work) +{ + struct mlx5_vport *vport = + container_of(work, struct mlx5_vport, vport_change_handler); + struct mlx5_core_dev *dev = vport->dev; + u8 mac[ETH_ALEN]; + + mlx5_query_nic_vport_mac_address(dev, vport->vport, mac); + + if (!is_valid_ether_addr(mac)) + goto out; + + esw_update_vport_uc_list(dev->priv.eswitch, vport->vport); + esw_apply_vport_uc_list(dev->priv.eswitch, vport->vport); + +out: + if (vport->enabled) + arm_vport_context_events_cmd(dev, vport->vport, + UC_ADDR_CHANGE); +} + +static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num) +{ + struct mlx5_vport *vport = &esw->vports[vport_num]; + unsigned long flags; + + spin_lock_irqsave(&vport->lock, flags); + vport->enabled = true; + spin_unlock_irqrestore(&vport->lock, flags); + + arm_vport_context_events_cmd(esw->dev, vport_num, UC_ADDR_CHANGE); +} + +static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num) +{ + struct mlx5_vport *vport = &esw->vports[vport_num]; + unsigned long flags; + + if (!vport->enabled) + return; + + /* Mark this vport as disabled to discard new events */ + spin_lock_irqsave(&vport->lock, flags); + vport->enabled = false; + spin_unlock_irqrestore(&vport->lock, flags); + + /* Wait for current already scheduled events to complete */ + flush_workqueue(esw->work_queue); + + /* Disable events from this vport */ + arm_vport_context_events_cmd(esw->dev, vport->vport, 0); +} + +/* Public E-Switch API */ +int mlx5_eswitch_init(struct mlx5_core_dev *dev) +{ + int l2_table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table); + int total_vports = 1 + pci_sriov_get_totalvfs(dev->pdev); + struct mlx5_eswitch *esw; + int vport_num; + int err; + + if (!MLX5_CAP_GEN(dev, vport_group_manager) || + MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + return 0; + + esw_info(dev, + "Total vports %d, l2 table size(%d), per vport: max uc(%d) max mc(%d)\n", + total_vports, l2_table_size, + MLX5_MAX_UC_PER_VPORT(dev), + MLX5_MAX_MC_PER_VPORT(dev)); + + esw = kzalloc(sizeof(*esw), GFP_KERNEL); + if (!esw) + return -ENOMEM; + + esw->dev = dev; + + esw->l2_table.bitmap = kcalloc(BITS_TO_LONGS(l2_table_size), + sizeof(uintptr_t), GFP_KERNEL); + if (!esw->l2_table.bitmap) { + err = -ENOMEM; + goto abort; + } + esw->l2_table.size = l2_table_size; + + esw->work_queue = create_singlethread_workqueue("mlx5_esw_wq"); + if (!esw->work_queue) { + err = -ENOMEM; + goto abort; + } + + esw->vports = kcalloc(total_vports, sizeof(struct mlx5_vport), + GFP_KERNEL); + if (!esw->vports) { + err = -ENOMEM; + goto abort; + } + + esw->total_vports = total_vports; + for (vport_num = 0; vport_num < total_vports; vport_num++) { + struct mlx5_vport *vport = &esw->vports[vport_num]; + + vport->vport = vport_num; + vport->dev = dev; + INIT_WORK(&vport->vport_change_handler, + esw_vport_change_handler); + spin_lock_init(&vport->lock); + } + + dev->priv.eswitch = esw; + + esw_enable_vport(esw, 0); + /* VF Vports will be enabled when SRIOV is enabled */ + return 0; +abort: + if (esw->work_queue) + destroy_workqueue(esw->work_queue); + kfree(esw->l2_table.bitmap); + kfree(esw->vports); + kfree(esw); + return err; +} + +void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) +{ + if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || + MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + return; + + esw_info(esw->dev, "cleanup\n"); + esw_disable_vport(esw, 0); + + esw->dev->priv.eswitch = NULL; + destroy_workqueue(esw->work_queue); + kfree(esw->l2_table.bitmap); + kfree(esw->vports); + kfree(esw); +} + +void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe) +{ + struct mlx5_eqe_vport_change *vc_eqe = &eqe->data.vport_change; + u16 vport_num = be16_to_cpu(vc_eqe->vport_num); + struct mlx5_vport *vport; + + if (!esw) { + pr_warn("MLX5 E-Switch: vport %d got an event while eswitch is not initialized\n", + vport_num); + return; + } + + vport = &esw->vports[vport_num]; + spin_lock(&vport->lock); + if (vport->enabled) + queue_work(esw->work_queue, &vport->vport_change_handler); + spin_unlock(&vport->lock); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h new file mode 100644 index 000000000000..0c41f2657824 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015, Mellanox Technologies, Ltd. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __MLX5_ESWITCH_H__ +#define __MLX5_ESWITCH_H__ + +#include + +#define MLX5_MAX_UC_PER_VPORT(dev) \ + (1 << MLX5_CAP_GEN(dev, log_max_current_uc_list)) + +#define MLX5_MAX_MC_PER_VPORT(dev) \ + (1 << MLX5_CAP_GEN(dev, log_max_current_mc_list)) + +#define MLX5_L2_ADDR_HASH_SIZE (BIT(BITS_PER_BYTE)) +#define MLX5_L2_ADDR_HASH(addr) (addr[5]) + +/* L2 -mac address based- hash helpers */ +struct l2addr_node { + struct hlist_node hlist; + u8 addr[ETH_ALEN]; +}; + +#define for_each_l2hash_node(hn, tmp, hash, i) \ + for (i = 0; i < MLX5_L2_ADDR_HASH_SIZE; i++) \ + hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist) + +#define l2addr_hash_find(hash, mac, type) ({ \ + int ix = MLX5_L2_ADDR_HASH(mac); \ + bool found = false; \ + type *ptr = NULL; \ + \ + hlist_for_each_entry(ptr, &hash[ix], node.hlist) \ + if (ether_addr_equal(ptr->node.addr, mac)) {\ + found = true; \ + break; \ + } \ + if (!found) \ + ptr = NULL; \ + ptr; \ +}) + +#define l2addr_hash_add(hash, mac, type, gfp) ({ \ + int ix = MLX5_L2_ADDR_HASH(mac); \ + type *ptr = NULL; \ + \ + ptr = kzalloc(sizeof(type), gfp); \ + if (ptr) { \ + ether_addr_copy(ptr->node.addr, mac); \ + hlist_add_head(&ptr->node.hlist, &hash[ix]);\ + } \ + ptr; \ +}) + +#define l2addr_hash_del(ptr) ({ \ + hlist_del(&ptr->node.hlist); \ + kfree(ptr); \ +}) + +struct mlx5_vport { + struct mlx5_core_dev *dev; + int vport; + struct hlist_head uc_list[MLX5_L2_ADDR_HASH_SIZE]; + struct work_struct vport_change_handler; + + /* This spinlock protects access to vport data, between + * "esw_vport_disable" and ongoing interrupt "mlx5_eswitch_vport_event" + * once vport marked as disabled new interrupts are discarded. + */ + spinlock_t lock; /* vport events sync */ + bool enabled; +}; + +struct mlx5_l2_table { + struct hlist_head l2_hash[MLX5_L2_ADDR_HASH_SIZE]; + u32 size; + unsigned long *bitmap; +}; + +struct mlx5_eswitch { + struct mlx5_core_dev *dev; + struct mlx5_l2_table l2_table; + struct workqueue_struct *work_queue; + struct mlx5_vport *vports; + int total_vports; +}; + +/* E-Switch API */ +int mlx5_eswitch_init(struct mlx5_core_dev *dev); +void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw); +void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe); + +#endif /* __MLX5_ESWITCH_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 66e2b37cfbbf..c6de3240f76f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -49,6 +49,9 @@ #include #include #include "mlx5_core.h" +#ifdef CONFIG_MLX5_CORE_EN +#include "eswitch.h" +#endif MODULE_AUTHOR("Eli Cohen "); MODULE_DESCRIPTION("Mellanox Connect-IB, ConnectX-4 core driver"); @@ -1052,6 +1055,14 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) mlx5_init_srq_table(dev); mlx5_init_mr_table(dev); +#ifdef CONFIG_MLX5_CORE_EN + err = mlx5_eswitch_init(dev); + if (err) { + dev_err(&pdev->dev, "eswitch init failed %d\n", err); + goto err_reg_dev; + } +#endif + err = mlx5_sriov_init(dev); if (err) { dev_err(&pdev->dev, "sriov init failed %d\n", err); @@ -1078,6 +1089,9 @@ err_sriov: if (mlx5_sriov_cleanup(dev)) dev_err(&dev->pdev->dev, "sriov cleanup failed\n"); +#ifdef CONFIG_MLX5_CORE_EN + mlx5_eswitch_cleanup(dev->priv.eswitch); +#endif err_reg_dev: mlx5_cleanup_mr_table(dev); mlx5_cleanup_srq_table(dev); @@ -1147,6 +1161,10 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) goto out; } mlx5_unregister_device(dev); +#ifdef CONFIG_MLX5_CORE_EN + mlx5_eswitch_cleanup(dev->priv.eswitch); +#endif + mlx5_cleanup_mr_table(dev); mlx5_cleanup_srq_table(dev); mlx5_cleanup_qp_table(dev); diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 0d2f0435a9f0..90a4cb6dc4cb 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -251,6 +251,7 @@ enum mlx5_event { MLX5_EVENT_TYPE_PAGE_REQUEST = 0xb, MLX5_EVENT_TYPE_PAGE_FAULT = 0xc, + MLX5_EVENT_TYPE_NIC_VPORT_CHANGE = 0xd, }; enum { @@ -520,6 +521,12 @@ struct mlx5_eqe_page_fault { __be32 flags_qpn; } __packed; +struct mlx5_eqe_vport_change { + u8 rsvd0[2]; + __be16 vport_num; + __be32 rsvd1[6]; +} __packed; + union ev_data { __be32 raw[7]; struct mlx5_eqe_cmd cmd; @@ -532,6 +539,7 @@ union ev_data { struct mlx5_eqe_stall_vl stall_vl; struct mlx5_eqe_page_req req_pages; struct mlx5_eqe_page_fault page_fault; + struct mlx5_eqe_vport_change vport_change; } __packed; struct mlx5_eqe { diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index efebb87163c8..ac098b6b97bf 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -441,6 +441,8 @@ struct mlx5_irq_info { char name[MLX5_MAX_IRQ_NAME]; }; +struct mlx5_eswitch; + struct mlx5_priv { char name[MLX5_MAX_NAME_LEN]; struct mlx5_eq_table eq_table; @@ -496,6 +498,8 @@ struct mlx5_priv { struct list_head dev_list; struct list_head ctx_list; spinlock_t ctx_lock; + + struct mlx5_eswitch *eswitch; struct mlx5_core_sriov sriov; unsigned long pci_dev_data; }; -- cgit v1.2.3-59-g8ed1b From 495716b191f607b2cb2175f7499966daef79f663 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:19 +0200 Subject: net/mlx5: E-Switch, Introduce FDB hardware capabilities Define needed hardware structures and capabilities needed for E-Switch FDB flow tables and read them on driver load. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fw.c | 13 +++++++++++++ include/linux/mlx5/device.h | 15 +++++++++++++++ include/linux/mlx5/mlx5_ifc.h | 13 +++++++++++++ 3 files changed, 41 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index 9335e5ae18cc..bf6e3dfcef51 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -160,6 +160,19 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev) if (err) return err; } + + if (MLX5_CAP_GEN(dev, vport_group_manager) && + MLX5_CAP_GEN(dev, eswitch_flow_table)) { + err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH_FLOW_TABLE, + HCA_CAP_OPMOD_GET_CUR); + if (err) + return err; + err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH_FLOW_TABLE, + HCA_CAP_OPMOD_GET_MAX); + if (err) + return err; + } + return 0; } diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 90a4cb6dc4cb..bce9caed1eed 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -1138,6 +1138,7 @@ enum mlx5_cap_type { MLX5_CAP_IPOIB_OFFLOADS, MLX5_CAP_EOIB_OFFLOADS, MLX5_CAP_FLOW_TABLE, + MLX5_CAP_ESWITCH_FLOW_TABLE, /* NUM OF CAP Types */ MLX5_CAP_NUM }; @@ -1175,6 +1176,20 @@ enum mlx5_cap_type { #define MLX5_CAP_FLOWTABLE_MAX(mdev, cap) \ MLX5_GET(flow_table_nic_cap, mdev->hca_caps_max[MLX5_CAP_FLOW_TABLE], cap) +#define MLX5_CAP_ESW_FLOWTABLE(mdev, cap) \ + MLX5_GET(flow_table_eswitch_cap, \ + mdev->hca_caps_cur[MLX5_CAP_ESWITCH_FLOW_TABLE], cap) + +#define MLX5_CAP_ESW_FLOWTABLE_MAX(mdev, cap) \ + MLX5_GET(flow_table_eswitch_cap, \ + mdev->hca_caps_max[MLX5_CAP_ESWITCH_FLOW_TABLE], cap) + +#define MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, cap) \ + MLX5_CAP_ESW_FLOWTABLE(mdev, flow_table_properties_nic_esw_fdb.cap) + +#define MLX5_CAP_ESW_FLOWTABLE_FDB_MAX(mdev, cap) \ + MLX5_CAP_ESW_FLOWTABLE_MAX(mdev, flow_table_properties_nic_esw_fdb.cap) + #define MLX5_CAP_ODP(mdev, cap)\ MLX5_GET(odp_cap, mdev->hca_caps_cur[MLX5_CAP_ODP], cap) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 39487d0c305d..ae7c08adba4a 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -447,6 +447,18 @@ struct mlx5_ifc_flow_table_nic_cap_bits { u8 reserved_3[0x7200]; }; +struct mlx5_ifc_flow_table_eswitch_cap_bits { + u8 reserved_0[0x200]; + + struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_nic_esw_fdb; + + struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_esw_acl_ingress; + + struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_esw_acl_egress; + + u8 reserved_1[0x7800]; +}; + struct mlx5_ifc_per_protocol_networking_offload_caps_bits { u8 csum_cap[0x1]; u8 vlan_cap[0x1]; @@ -1846,6 +1858,7 @@ union mlx5_ifc_hca_cap_union_bits { struct mlx5_ifc_roce_cap_bits roce_cap; struct mlx5_ifc_per_protocol_networking_offload_caps_bits per_protocol_networking_offload_caps; struct mlx5_ifc_flow_table_nic_cap_bits flow_table_nic_cap; + struct mlx5_ifc_flow_table_eswitch_cap_bits flow_table_eswitch_cap; u8 reserved_0[0x8000]; }; -- cgit v1.2.3-59-g8ed1b From 81848731ff4070a3e4136efa6a99d507177a53fe Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:20 +0200 Subject: net/mlx5: E-Switch, Add SR-IOV (FDB) support Enabling E-Switch SRIOV for nvfs+1 vports. Create E-Switch FDB for L2 UC/MC mac steering between VFs/PF and external vport (Uplink). FDB contains forwarding rules such as: UC MAC0 -> vport0(PF). UC MAC1 -> vport1. UC MAC2 -> vport2. MC MACX -> vport0, vport2, Uplink. MC MACY -> vport1, Uplink. For unmatched traffic FDB has the following default rules: Unmached Traffic (src vport != Uplink) -> Uplink. Unmached Traffic (src vport == Uplink) -> vport0(PF). FDB rules population: Each NIC vport (VF) will notify E-Switch manager of its UC/MC vport context changes via modify vport context command, which will be translated to an event that will be handled by E-Switch manager (PF) which will update FDB table accordingly. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 682 ++++++++++++++++++--- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 25 + .../net/ethernet/mellanox/mlx5/core/mlx5_core.h | 1 + drivers/net/ethernet/mellanox/mlx5/core/sriov.c | 14 +- include/linux/mlx5/device.h | 6 + include/linux/mlx5/flow_table.h | 9 + include/linux/mlx5/mlx5_ifc.h | 7 +- 7 files changed, 661 insertions(+), 83 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 1f2f804bde3e..6fa6f013d2c5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -34,9 +34,12 @@ #include #include #include +#include #include "mlx5_core.h" #include "eswitch.h" +#define UPLINK_VPORT 0xFFFF + #define MLX5_DEBUG_ESWITCH_MASK BIT(3) #define esw_info(dev, format, ...) \ @@ -54,18 +57,26 @@ enum { MLX5_ACTION_DEL = 2, }; -/* HW UC L2 table hash node */ -struct mlx5_uc_l2addr { +/* E-Switch UC L2 table hash node */ +struct esw_uc_addr { struct l2addr_node node; - u8 action; u32 table_index; u32 vport; }; -/* Vport UC L2 table hash node */ -struct mlx5_vport_addr { - struct l2addr_node node; - u8 action; +/* E-Switch MC FDB table hash node */ +struct esw_mc_addr { /* SRIOV only */ + struct l2addr_node node; + struct mlx5_flow_rule *uplink_rule; /* Forward to uplink rule */ + u32 refcnt; +}; + +/* Vport UC/MC hash node */ +struct vport_addr { + struct l2addr_node node; + u8 action; + u32 vport; + struct mlx5_flow_rule *flow_rule; /* SRIOV only */ }; enum { @@ -73,7 +84,11 @@ enum { MC_ADDR_CHANGE = BIT(1), }; -static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, int vport, +/* Vport context events */ +#define SRIOV_VPORT_EVENTS (UC_ADDR_CHANGE | \ + MC_ADDR_CHANGE) + +static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport, u32 events_mask) { int in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)]; @@ -196,97 +211,492 @@ static void del_l2_table_entry(struct mlx5_core_dev *dev, u32 index) free_l2_table_index(l2_table, index); } -/* SW E-Switch L2 Table management */ -static int l2_table_addr_add(struct mlx5_eswitch *esw, - u8 mac[ETH_ALEN], u32 vport) +/* E-Switch FDB flow steering */ +struct dest_node { + struct list_head list; + struct mlx5_flow_destination dest; +}; + +static int _mlx5_flow_rule_apply(struct mlx5_flow_rule *fr) { - struct hlist_head *hash; - struct mlx5_uc_l2addr *addr; + bool was_valid = fr->valid; + struct dest_node *dest_n; + u32 dest_list_size = 0; + void *in_match_value; + u32 *flow_context; + u32 flow_index; int err; + int i; + + if (list_empty(&fr->dest_list)) { + if (fr->valid) + mlx5_del_flow_table_entry(fr->ft, fr->fi); + fr->valid = false; + return 0; + } + + list_for_each_entry(dest_n, &fr->dest_list, list) + dest_list_size++; - hash = esw->l2_table.l2_hash; - addr = l2addr_hash_find(hash, mac, struct mlx5_uc_l2addr); - if (addr) { + flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context) + + MLX5_ST_SZ_BYTES(dest_format_struct) * + dest_list_size); + if (!flow_context) + return -ENOMEM; + + MLX5_SET(flow_context, flow_context, flow_tag, fr->flow_tag); + MLX5_SET(flow_context, flow_context, action, fr->action); + MLX5_SET(flow_context, flow_context, destination_list_size, + dest_list_size); + + i = 0; + list_for_each_entry(dest_n, &fr->dest_list, list) { + void *dest_addr = MLX5_ADDR_OF(flow_context, flow_context, + destination[i++]); + + MLX5_SET(dest_format_struct, dest_addr, destination_type, + dest_n->dest.type); + MLX5_SET(dest_format_struct, dest_addr, destination_id, + dest_n->dest.vport_num); + } + + in_match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value); + memcpy(in_match_value, fr->match_value, MLX5_ST_SZ_BYTES(fte_match_param)); + + err = mlx5_add_flow_table_entry(fr->ft, fr->match_criteria_enable, + fr->match_criteria, flow_context, + &flow_index); + if (!err) { + if (was_valid) + mlx5_del_flow_table_entry(fr->ft, fr->fi); + fr->fi = flow_index; + fr->valid = true; + } + kfree(flow_context); + return err; +} + +static int mlx5_flow_rule_add_dest(struct mlx5_flow_rule *fr, + struct mlx5_flow_destination *new_dest) +{ + struct dest_node *dest_n; + int err; + + dest_n = kzalloc(sizeof(*dest_n), GFP_KERNEL); + if (!dest_n) + return -ENOMEM; + + memcpy(&dest_n->dest, new_dest, sizeof(dest_n->dest)); + mutex_lock(&fr->mutex); + list_add(&dest_n->list, &fr->dest_list); + err = _mlx5_flow_rule_apply(fr); + if (err) { + list_del(&dest_n->list); + kfree(dest_n); + } + mutex_unlock(&fr->mutex); + return err; +} + +static int mlx5_flow_rule_del_dest(struct mlx5_flow_rule *fr, + struct mlx5_flow_destination *dest) +{ + struct dest_node *dest_n; + struct dest_node *n; + int err; + + mutex_lock(&fr->mutex); + list_for_each_entry_safe(dest_n, n, &fr->dest_list, list) { + if (dest->vport_num == dest_n->dest.vport_num) + goto found; + } + mutex_unlock(&fr->mutex); + return -ENOENT; + +found: + list_del(&dest_n->list); + err = _mlx5_flow_rule_apply(fr); + mutex_unlock(&fr->mutex); + kfree(dest_n); + + return err; +} + +static struct mlx5_flow_rule *find_fr(struct mlx5_eswitch *esw, + u8 match_criteria_enable, + u32 *match_value) +{ + struct hlist_head *hash = esw->mc_table; + struct esw_mc_addr *esw_mc; + u8 *dmac_v; + + dmac_v = MLX5_ADDR_OF(fte_match_param, match_value, + outer_headers.dmac_47_16); + + /* UNICAST FULL MATCH */ + if (!is_multicast_ether_addr(dmac_v)) + return NULL; + + /* MULTICAST FULL MATCH */ + esw_mc = l2addr_hash_find(hash, dmac_v, struct esw_mc_addr); + + return esw_mc ? esw_mc->uplink_rule : NULL; +} + +static struct mlx5_flow_rule *alloc_fr(void *ft, + u8 match_criteria_enable, + u32 *match_criteria, + u32 *match_value, + u32 action, + u32 flow_tag) +{ + struct mlx5_flow_rule *fr = kzalloc(sizeof(*fr), GFP_KERNEL); + + if (!fr) + return NULL; + + fr->match_criteria = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); + fr->match_value = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); + if (!fr->match_criteria || !fr->match_value) { + kfree(fr->match_criteria); + kfree(fr->match_value); + kfree(fr); + return NULL; + } + + memcpy(fr->match_criteria, match_criteria, MLX5_ST_SZ_BYTES(fte_match_param)); + memcpy(fr->match_value, match_value, MLX5_ST_SZ_BYTES(fte_match_param)); + fr->match_criteria_enable = match_criteria_enable; + fr->flow_tag = flow_tag; + fr->action = action; + + mutex_init(&fr->mutex); + INIT_LIST_HEAD(&fr->dest_list); + atomic_set(&fr->refcount, 0); + fr->ft = ft; + return fr; +} + +static void deref_fr(struct mlx5_flow_rule *fr) +{ + if (!atomic_dec_and_test(&fr->refcount)) + return; + + kfree(fr->match_criteria); + kfree(fr->match_value); + kfree(fr); +} + +static struct mlx5_flow_rule * +mlx5_add_flow_rule(struct mlx5_eswitch *esw, + u8 match_criteria_enable, + u32 *match_criteria, + u32 *match_value, + u32 action, + u32 flow_tag, + struct mlx5_flow_destination *dest) +{ + struct mlx5_flow_rule *fr; + int err; + + fr = find_fr(esw, match_criteria_enable, match_value); + fr = fr ? fr : alloc_fr(esw->fdb_table.fdb, match_criteria_enable, match_criteria, + match_value, action, flow_tag); + if (!fr) + return NULL; + + atomic_inc(&fr->refcount); + + err = mlx5_flow_rule_add_dest(fr, dest); + if (err) { + deref_fr(fr); + return NULL; + } + + return fr; +} + +static void mlx5_del_flow_rule(struct mlx5_flow_rule *fr, u32 vport) +{ + struct mlx5_flow_destination dest; + + dest.vport_num = vport; + mlx5_flow_rule_del_dest(fr, &dest); + deref_fr(fr); +} + +/* E-Switch FDB */ +static struct mlx5_flow_rule * +esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport) +{ + int match_header = MLX5_MATCH_OUTER_HEADERS; + struct mlx5_flow_destination dest; + struct mlx5_flow_rule *flow_rule = NULL; + u32 *match_v; + u32 *match_c; + u8 *dmac_v; + u8 *dmac_c; + + match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); + match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); + if (!match_v || !match_c) { + pr_warn("FDB: Failed to alloc match parameters\n"); + goto out; + } + dmac_v = MLX5_ADDR_OF(fte_match_param, match_v, + outer_headers.dmac_47_16); + dmac_c = MLX5_ADDR_OF(fte_match_param, match_c, + outer_headers.dmac_47_16); + + ether_addr_copy(dmac_v, mac); + /* Match criteria mask */ + memset(dmac_c, 0xff, 6); + + dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; + dest.vport_num = vport; + + esw_debug(esw->dev, + "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n", + dmac_v, dmac_c, vport); + flow_rule = + mlx5_add_flow_rule(esw, + match_header, + match_c, + match_v, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + 0, &dest); + if (IS_ERR_OR_NULL(flow_rule)) { + pr_warn( + "FDB: Failed to add flow rule: dmac_v(%pM) dmac_c(%pM) -> vport(%d), err(%ld)\n", + dmac_v, dmac_c, vport, PTR_ERR(flow_rule)); + flow_rule = NULL; + } +out: + kfree(match_v); + kfree(match_c); + return flow_rule; +} + +static int esw_create_fdb_table(struct mlx5_eswitch *esw, int nvports) +{ + struct mlx5_core_dev *dev = esw->dev; + struct mlx5_flow_table_group g; + struct mlx5_flow_table *fdb; + u8 *dmac; + + esw_debug(dev, "Create FDB log_max_size(%d)\n", + MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)); + + memset(&g, 0, sizeof(g)); + /* UC MC Full match rules*/ + g.log_sz = MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size); + g.match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + dmac = MLX5_ADDR_OF(fte_match_param, g.match_criteria, + outer_headers.dmac_47_16); + /* Match criteria mask */ + memset(dmac, 0xff, 6); + + fdb = mlx5_create_flow_table(dev, 0, + MLX5_FLOW_TABLE_TYPE_ESWITCH, + 1, &g); + if (fdb) + esw_debug(dev, "ESW: FDB Table created fdb->id %d\n", mlx5_get_flow_table_id(fdb)); + else + esw_warn(dev, "ESW: Failed to create FDB Table\n"); + + esw->fdb_table.fdb = fdb; + return fdb ? 0 : -ENOMEM; +} + +static void esw_destroy_fdb_table(struct mlx5_eswitch *esw) +{ + if (!esw->fdb_table.fdb) + return; + + esw_debug(esw->dev, "Destroy FDB Table fdb(%d)\n", + mlx5_get_flow_table_id(esw->fdb_table.fdb)); + mlx5_destroy_flow_table(esw->fdb_table.fdb); + esw->fdb_table.fdb = NULL; +} + +/* E-Switch vport UC/MC lists management */ +typedef int (*vport_addr_action)(struct mlx5_eswitch *esw, + struct vport_addr *vaddr); + +static int esw_add_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) +{ + struct hlist_head *hash = esw->l2_table.l2_hash; + struct esw_uc_addr *esw_uc; + u8 *mac = vaddr->node.addr; + u32 vport = vaddr->vport; + int err; + + esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr); + if (esw_uc) { esw_warn(esw->dev, "Failed to set L2 mac(%pM) for vport(%d), mac is already in use by vport(%d)\n", - mac, vport, addr->vport); + mac, vport, esw_uc->vport); return -EEXIST; } - addr = l2addr_hash_add(hash, mac, struct mlx5_uc_l2addr, - GFP_KERNEL); - if (!addr) + esw_uc = l2addr_hash_add(hash, mac, struct esw_uc_addr, GFP_KERNEL); + if (!esw_uc) return -ENOMEM; + esw_uc->vport = vport; - addr->vport = vport; - addr->action = MLX5_ACTION_NONE; - err = set_l2_table_entry(esw->dev, mac, 0, 0, &addr->table_index); + err = set_l2_table_entry(esw->dev, mac, 0, 0, &esw_uc->table_index); if (err) - l2addr_hash_del(addr); - else - esw_debug(esw->dev, "\tADDED L2 MAC: vport[%d] %pM index:%d\n", - vport, addr->node.addr, addr->table_index); + goto abort; + + if (esw->fdb_table.fdb) /* SRIOV is enabled: Forward UC MAC to vport */ + vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport); + + esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM index:%d fr(%p)\n", + vport, mac, esw_uc->table_index, vaddr->flow_rule); + return err; +abort: + l2addr_hash_del(esw_uc); return err; } -static int l2_table_addr_del(struct mlx5_eswitch *esw, - u8 mac[ETH_ALEN], u32 vport) +static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) { - struct hlist_head *hash; - struct mlx5_uc_l2addr *addr; + struct hlist_head *hash = esw->l2_table.l2_hash; + struct esw_uc_addr *esw_uc; + u8 *mac = vaddr->node.addr; + u32 vport = vaddr->vport; + + esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr); + if (!esw_uc || esw_uc->vport != vport) { + esw_debug(esw->dev, + "MAC(%pM) doesn't belong to vport (%d)\n", + mac, vport); + return -EINVAL; + } + esw_debug(esw->dev, "\tDELETE UC MAC: vport[%d] %pM index:%d fr(%p)\n", + vport, mac, esw_uc->table_index, vaddr->flow_rule); + + del_l2_table_entry(esw->dev, esw_uc->table_index); + + if (vaddr->flow_rule) + mlx5_del_flow_rule(vaddr->flow_rule, vport); + vaddr->flow_rule = NULL; + + l2addr_hash_del(esw_uc); + return 0; +} + +static int esw_add_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) +{ + struct hlist_head *hash = esw->mc_table; + struct esw_mc_addr *esw_mc; + u8 *mac = vaddr->node.addr; + u32 vport = vaddr->vport; + + if (!esw->fdb_table.fdb) + return 0; + + esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr); + if (esw_mc) + goto add; + + esw_mc = l2addr_hash_add(hash, mac, struct esw_mc_addr, GFP_KERNEL); + if (!esw_mc) + return -ENOMEM; + + esw_mc->uplink_rule = /* Forward MC MAC to Uplink */ + esw_fdb_set_vport_rule(esw, mac, UPLINK_VPORT); +add: + esw_mc->refcnt++; + /* Forward MC MAC to vport */ + vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport); + esw_debug(esw->dev, + "\tADDED MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n", + vport, mac, vaddr->flow_rule, + esw_mc->refcnt, esw_mc->uplink_rule); + return 0; +} + +static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) +{ + struct hlist_head *hash = esw->mc_table; + struct esw_mc_addr *esw_mc; + u8 *mac = vaddr->node.addr; + u32 vport = vaddr->vport; - hash = esw->l2_table.l2_hash; - addr = l2addr_hash_find(hash, mac, struct mlx5_uc_l2addr); - if (!addr || addr->vport != vport) { - esw_warn(esw->dev, "MAC(%pM) doesn't belong to vport (%d)\n", + if (!esw->fdb_table.fdb) + return 0; + + esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr); + if (!esw_mc) { + esw_warn(esw->dev, + "Failed to find eswitch MC addr for MAC(%pM) vport(%d)", mac, vport); return -EINVAL; } + esw_debug(esw->dev, + "\tDELETE MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n", + vport, mac, vaddr->flow_rule, esw_mc->refcnt, + esw_mc->uplink_rule); - esw_debug(esw->dev, "\tDELETE L2 MAC: vport[%d] %pM index:%d\n", - vport, addr->node.addr, addr->table_index); - del_l2_table_entry(esw->dev, addr->table_index); - l2addr_hash_del(addr); + if (vaddr->flow_rule) + mlx5_del_flow_rule(vaddr->flow_rule, vport); + vaddr->flow_rule = NULL; + + if (--esw_mc->refcnt) + return 0; + + if (esw_mc->uplink_rule) + mlx5_del_flow_rule(esw_mc->uplink_rule, UPLINK_VPORT); + + l2addr_hash_del(esw_mc); return 0; } -/* E-Switch vport uc list management */ - -/* Apply vport uc list to HW l2 table */ -static void esw_apply_vport_uc_list(struct mlx5_eswitch *esw, - u32 vport_num) +/* Apply vport UC/MC list to HW l2 table and FDB table */ +static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw, + u32 vport_num, int list_type) { struct mlx5_vport *vport = &esw->vports[vport_num]; - struct mlx5_vport_addr *addr; + bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC; + vport_addr_action vport_addr_add; + vport_addr_action vport_addr_del; + struct vport_addr *addr; struct l2addr_node *node; struct hlist_head *hash; struct hlist_node *tmp; int hi; - hash = vport->uc_list; + vport_addr_add = is_uc ? esw_add_uc_addr : + esw_add_mc_addr; + vport_addr_del = is_uc ? esw_del_uc_addr : + esw_del_mc_addr; + + hash = is_uc ? vport->uc_list : vport->mc_list; for_each_l2hash_node(node, tmp, hash, hi) { - addr = container_of(node, struct mlx5_vport_addr, node); + addr = container_of(node, struct vport_addr, node); switch (addr->action) { case MLX5_ACTION_ADD: - l2_table_addr_add(esw, addr->node.addr, vport_num); + vport_addr_add(esw, addr); addr->action = MLX5_ACTION_NONE; break; case MLX5_ACTION_DEL: - l2_table_addr_del(esw, addr->node.addr, vport_num); + vport_addr_del(esw, addr); l2addr_hash_del(addr); break; } } } -/* Sync vport uc list from vport context */ -static void esw_update_vport_uc_list(struct mlx5_eswitch *esw, - u32 vport_num) +/* Sync vport UC/MC list from vport context */ +static void esw_update_vport_addr_list(struct mlx5_eswitch *esw, + u32 vport_num, int list_type) { struct mlx5_vport *vport = &esw->vports[vport_num]; - struct mlx5_vport_addr *addr; - struct l2addr_node *node; + bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC; u8 (*mac_list)[ETH_ALEN]; + struct l2addr_node *node; + struct vport_addr *addr; struct hlist_head *hash; struct hlist_node *tmp; int size; @@ -294,40 +704,41 @@ static void esw_update_vport_uc_list(struct mlx5_eswitch *esw, int hi; int i; - size = MLX5_MAX_UC_PER_VPORT(esw->dev); + size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) : + MLX5_MAX_MC_PER_VPORT(esw->dev); mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL); if (!mac_list) return; - hash = vport->uc_list; + hash = is_uc ? vport->uc_list : vport->mc_list; for_each_l2hash_node(node, tmp, hash, hi) { - addr = container_of(node, struct mlx5_vport_addr, node); + addr = container_of(node, struct vport_addr, node); addr->action = MLX5_ACTION_DEL; } - err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, - MLX5_NVPRT_LIST_TYPE_UC, + err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, list_type, mac_list, &size); if (err) return; - esw_debug(esw->dev, "vport[%d] context update UC list size (%d)\n", - vport_num, size); + esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n", + vport_num, is_uc ? "UC" : "MC", size); for (i = 0; i < size; i++) { - if (!is_valid_ether_addr(mac_list[i])) + if (is_uc && !is_valid_ether_addr(mac_list[i])) + continue; + + if (!is_uc && !is_multicast_ether_addr(mac_list[i])) continue; - addr = l2addr_hash_find(hash, mac_list[i], - struct mlx5_vport_addr); + addr = l2addr_hash_find(hash, mac_list[i], struct vport_addr); if (addr) { addr->action = MLX5_ACTION_NONE; continue; } - addr = l2addr_hash_add(hash, mac_list[i], - struct mlx5_vport_addr, + addr = l2addr_hash_add(hash, mac_list[i], struct vport_addr, GFP_KERNEL); if (!addr) { esw_warn(esw->dev, @@ -335,6 +746,7 @@ static void esw_update_vport_uc_list(struct mlx5_eswitch *esw, mac_list[i], vport_num); continue; } + addr->vport = vport_num; addr->action = MLX5_ACTION_ADD; } kfree(mac_list); @@ -345,32 +757,80 @@ static void esw_vport_change_handler(struct work_struct *work) struct mlx5_vport *vport = container_of(work, struct mlx5_vport, vport_change_handler); struct mlx5_core_dev *dev = vport->dev; + struct mlx5_eswitch *esw = dev->priv.eswitch; u8 mac[ETH_ALEN]; mlx5_query_nic_vport_mac_address(dev, vport->vport, mac); + esw_debug(dev, "vport[%d] Context Changed: perm mac: %pM\n", + vport->vport, mac); + + if (vport->enabled_events & UC_ADDR_CHANGE) { + esw_update_vport_addr_list(esw, vport->vport, + MLX5_NVPRT_LIST_TYPE_UC); + esw_apply_vport_addr_list(esw, vport->vport, + MLX5_NVPRT_LIST_TYPE_UC); + } - if (!is_valid_ether_addr(mac)) - goto out; - - esw_update_vport_uc_list(dev->priv.eswitch, vport->vport); - esw_apply_vport_uc_list(dev->priv.eswitch, vport->vport); + if (vport->enabled_events & MC_ADDR_CHANGE) { + esw_update_vport_addr_list(esw, vport->vport, + MLX5_NVPRT_LIST_TYPE_MC); + esw_apply_vport_addr_list(esw, vport->vport, + MLX5_NVPRT_LIST_TYPE_MC); + } -out: + esw_debug(esw->dev, "vport[%d] Context Changed: Done\n", vport->vport); if (vport->enabled) arm_vport_context_events_cmd(dev, vport->vport, - UC_ADDR_CHANGE); + vport->enabled_events); } -static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num) +static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num, + int enable_events) { struct mlx5_vport *vport = &esw->vports[vport_num]; unsigned long flags; + WARN_ON(vport->enabled); + + esw_debug(esw->dev, "Enabling VPORT(%d)\n", vport_num); + mlx5_modify_vport_admin_state(esw->dev, + MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT, + vport_num, + MLX5_ESW_VPORT_ADMIN_STATE_AUTO); + + /* Sync with current vport context */ + vport->enabled_events = enable_events; + esw_vport_change_handler(&vport->vport_change_handler); + spin_lock_irqsave(&vport->lock, flags); vport->enabled = true; spin_unlock_irqrestore(&vport->lock, flags); - arm_vport_context_events_cmd(esw->dev, vport_num, UC_ADDR_CHANGE); + arm_vport_context_events_cmd(esw->dev, vport_num, enable_events); + + esw->enabled_vports++; + esw_debug(esw->dev, "Enabled VPORT(%d)\n", vport_num); +} + +static void esw_cleanup_vport(struct mlx5_eswitch *esw, u16 vport_num) +{ + struct mlx5_vport *vport = &esw->vports[vport_num]; + struct l2addr_node *node; + struct vport_addr *addr; + struct hlist_node *tmp; + int hi; + + for_each_l2hash_node(node, tmp, vport->uc_list, hi) { + addr = container_of(node, struct vport_addr, node); + addr->action = MLX5_ACTION_DEL; + } + esw_apply_vport_addr_list(esw, vport_num, MLX5_NVPRT_LIST_TYPE_UC); + + for_each_l2hash_node(node, tmp, vport->mc_list, hi) { + addr = container_of(node, struct vport_addr, node); + addr->action = MLX5_ACTION_DEL; + } + esw_apply_vport_addr_list(esw, vport_num, MLX5_NVPRT_LIST_TYPE_MC); } static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num) @@ -381,19 +841,82 @@ static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num) if (!vport->enabled) return; + esw_debug(esw->dev, "Disabling vport(%d)\n", vport_num); /* Mark this vport as disabled to discard new events */ spin_lock_irqsave(&vport->lock, flags); vport->enabled = false; + vport->enabled_events = 0; spin_unlock_irqrestore(&vport->lock, flags); + mlx5_modify_vport_admin_state(esw->dev, + MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT, + vport_num, + MLX5_ESW_VPORT_ADMIN_STATE_DOWN); /* Wait for current already scheduled events to complete */ flush_workqueue(esw->work_queue); - /* Disable events from this vport */ arm_vport_context_events_cmd(esw->dev, vport->vport, 0); + /* We don't assume VFs will cleanup after themselves */ + esw_cleanup_vport(esw, vport_num); + esw->enabled_vports--; } /* Public E-Switch API */ +int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs) +{ + int err; + int i; + + if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || + MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + return 0; + + if (!MLX5_CAP_GEN(esw->dev, eswitch_flow_table) || + !MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, ft_support)) { + esw_warn(esw->dev, "E-Switch FDB is not supported, aborting ...\n"); + return -ENOTSUPP; + } + + esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d)\n", nvfs); + + esw_disable_vport(esw, 0); + + err = esw_create_fdb_table(esw, nvfs + 1); + if (err) + goto abort; + + for (i = 0; i <= nvfs; i++) + esw_enable_vport(esw, i, SRIOV_VPORT_EVENTS); + + esw_info(esw->dev, "SRIOV enabled: active vports(%d)\n", + esw->enabled_vports); + return 0; + +abort: + esw_enable_vport(esw, 0, UC_ADDR_CHANGE); + return err; +} + +void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) +{ + int i; + + if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || + MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + return; + + esw_info(esw->dev, "disable SRIOV: active vports(%d)\n", + esw->enabled_vports); + + for (i = 0; i < esw->total_vports; i++) + esw_disable_vport(esw, i); + + esw_destroy_fdb_table(esw); + + /* VPORT 0 (PF) must be enabled back with non-sriov configuration */ + esw_enable_vport(esw, 0, UC_ADDR_CHANGE); +} + int mlx5_eswitch_init(struct mlx5_core_dev *dev) { int l2_table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table); @@ -439,7 +962,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) goto abort; } - esw->total_vports = total_vports; for (vport_num = 0; vport_num < total_vports; vport_num++) { struct mlx5_vport *vport = &esw->vports[vport_num]; @@ -450,9 +972,11 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) spin_lock_init(&vport->lock); } - dev->priv.eswitch = esw; + esw->total_vports = total_vports; + esw->enabled_vports = 0; - esw_enable_vport(esw, 0); + dev->priv.eswitch = esw; + esw_enable_vport(esw, 0, UC_ADDR_CHANGE); /* VF Vports will be enabled when SRIOV is enabled */ return 0; abort: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 0c41f2657824..f222e336f34f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -86,10 +86,25 @@ struct l2addr_node { kfree(ptr); \ }) +struct mlx5_flow_rule { + void *ft; + u32 fi; + u8 match_criteria_enable; + u32 *match_criteria; + u32 *match_value; + u32 action; + u32 flow_tag; + bool valid; + atomic_t refcount; + struct mutex mutex; /* protect flow rule updates */ + struct list_head dest_list; +}; + struct mlx5_vport { struct mlx5_core_dev *dev; int vport; struct hlist_head uc_list[MLX5_L2_ADDR_HASH_SIZE]; + struct hlist_head mc_list[MLX5_L2_ADDR_HASH_SIZE]; struct work_struct vport_change_handler; /* This spinlock protects access to vport data, between @@ -98,6 +113,7 @@ struct mlx5_vport { */ spinlock_t lock; /* vport events sync */ bool enabled; + u16 enabled_events; }; struct mlx5_l2_table { @@ -106,17 +122,26 @@ struct mlx5_l2_table { unsigned long *bitmap; }; +struct mlx5_eswitch_fdb { + void *fdb; +}; + struct mlx5_eswitch { struct mlx5_core_dev *dev; struct mlx5_l2_table l2_table; + struct mlx5_eswitch_fdb fdb_table; + struct hlist_head mc_table[MLX5_L2_ADDR_HASH_SIZE]; struct workqueue_struct *work_queue; struct mlx5_vport *vports; int total_vports; + int enabled_vports; }; /* E-Switch API */ int mlx5_eswitch_init(struct mlx5_core_dev *dev); void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw); void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe); +int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs); +void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw); #endif /* __MLX5_ESWITCH_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 1649d5cf9e29..bee7da822dfe 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -36,6 +36,7 @@ #include #include #include +#include #define DRIVER_NAME "mlx5_core" #define DRIVER_VERSION "3.0-1" diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c index 19a43240e359..7b24386794f9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c @@ -33,6 +33,9 @@ #include #include #include "mlx5_core.h" +#ifdef CONFIG_MLX5_CORE_EN +#include "eswitch.h" +#endif static void enable_vfs(struct mlx5_core_dev *dev, int num_vfs) { @@ -144,13 +147,15 @@ int mlx5_core_sriov_configure(struct pci_dev *pdev, int num_vfs) mlx5_core_cleanup_vfs(dev); if (!num_vfs) { +#ifdef CONFIG_MLX5_CORE_EN + mlx5_eswitch_disable_sriov(dev->priv.eswitch); +#endif kfree(sriov->vfs_ctx); sriov->vfs_ctx = NULL; if (!pci_vfs_assigned(pdev)) pci_disable_sriov(pdev); else pr_info("unloading PF driver while leaving orphan VFs\n"); - return 0; } @@ -161,6 +166,9 @@ int mlx5_core_sriov_configure(struct pci_dev *pdev, int num_vfs) } mlx5_core_init_vfs(dev, num_vfs); +#ifdef CONFIG_MLX5_CORE_EN + mlx5_eswitch_enable_sriov(dev->priv.eswitch, num_vfs); +#endif return num_vfs; } @@ -199,6 +207,10 @@ int mlx5_sriov_init(struct mlx5_core_dev *dev) sriov->enabled_vfs = cur_vfs; mlx5_core_init_vfs(dev, cur_vfs); +#ifdef CONFIG_MLX5_CORE_EN + if (cur_vfs) + mlx5_eswitch_enable_sriov(dev->priv.eswitch, cur_vfs); +#endif enable_vfs(dev, cur_vfs); diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index bce9caed1eed..88eb4490a8b3 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -1074,6 +1074,12 @@ enum { VPORT_STATE_UP = 0x1, }; +enum { + MLX5_ESW_VPORT_ADMIN_STATE_DOWN = 0x0, + MLX5_ESW_VPORT_ADMIN_STATE_UP = 0x1, + MLX5_ESW_VPORT_ADMIN_STATE_AUTO = 0x2, +}; + enum { MLX5_L3_PROT_TYPE_IPV4 = 0, MLX5_L3_PROT_TYPE_IPV6 = 1, diff --git a/include/linux/mlx5/flow_table.h b/include/linux/mlx5/flow_table.h index 5f922c6d4fc2..0f2a15cf3317 100644 --- a/include/linux/mlx5/flow_table.h +++ b/include/linux/mlx5/flow_table.h @@ -41,6 +41,15 @@ struct mlx5_flow_table_group { u32 match_criteria[MLX5_ST_SZ_DW(fte_match_param)]; }; +struct mlx5_flow_destination { + enum mlx5_flow_destination_type type; + union { + u32 tir_num; + void *ft; + u32 vport_num; + }; +}; + void *mlx5_create_flow_table(struct mlx5_core_dev *dev, u8 level, u8 table_type, u16 num_groups, struct mlx5_flow_table_group *group); diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index ae7c08adba4a..a81b008738fd 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -827,9 +827,10 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 reserved_69[0x220]; }; -enum { - MLX5_DEST_FORMAT_STRUCT_DESTINATION_TYPE_FLOW_TABLE_ = 0x1, - MLX5_DEST_FORMAT_STRUCT_DESTINATION_TYPE_TIR = 0x2, +enum mlx5_flow_destination_type { + MLX5_FLOW_DESTINATION_TYPE_VPORT = 0x0, + MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE = 0x1, + MLX5_FLOW_DESTINATION_TYPE_TIR = 0x2, }; struct mlx5_ifc_dest_format_struct_bits { -- cgit v1.2.3-59-g8ed1b From 77256579c6b43bb88da96b43637bafc0df20f8e8 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:21 +0200 Subject: net/mlx5: E-Switch, Introduce Vport administration functions Implement set VF mac/link state and query VF config to be used later in nedev VF ndos or any other management API. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 61 +++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 8 +++ 2 files changed, 69 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 6fa6f013d2c5..d7d6eba672a7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1022,3 +1022,64 @@ void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe) queue_work(esw->work_queue, &vport->vport_change_handler); spin_unlock(&vport->lock); } + +/* Vport Administration */ +#define ESW_ALLOWED(esw) \ + (esw && MLX5_CAP_GEN(esw->dev, vport_group_manager) && mlx5_core_is_pf(esw->dev)) +#define LEGAL_VPORT(esw, vport) (vport >= 0 && vport < esw->total_vports) + +int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, + int vport, u8 mac[ETH_ALEN]) +{ + int err = 0; + + if (!ESW_ALLOWED(esw)) + return -EPERM; + if (!LEGAL_VPORT(esw, vport)) + return -EINVAL; + + err = mlx5_modify_nic_vport_mac_address(esw->dev, vport, mac); + if (err) { + mlx5_core_warn(esw->dev, + "Failed to mlx5_modify_nic_vport_mac vport(%d) err=(%d)\n", + vport, err); + return err; + } + + return err; +} + +int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw, + int vport, int link_state) +{ + if (!ESW_ALLOWED(esw)) + return -EPERM; + if (!LEGAL_VPORT(esw, vport)) + return -EINVAL; + + return mlx5_modify_vport_admin_state(esw->dev, + MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT, + vport, link_state); +} + +int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, + int vport, struct ifla_vf_info *ivi) +{ + if (!ESW_ALLOWED(esw)) + return -EPERM; + if (!LEGAL_VPORT(esw, vport)) + return -EINVAL; + + memset(ivi, 0, sizeof(*ivi)); + ivi->vf = vport - 1; + + mlx5_query_nic_vport_mac_address(esw->dev, vport, ivi->mac); + ivi->linkstate = mlx5_query_vport_admin_state(esw->dev, + MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT, + vport); + ivi->vlan = 0; + ivi->qos = 0; + ivi->spoofchk = 0; + + return 0; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index f222e336f34f..43a6730d76a3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -33,6 +33,8 @@ #ifndef __MLX5_ESWITCH_H__ #define __MLX5_ESWITCH_H__ +#include +#include #include #define MLX5_MAX_UC_PER_VPORT(dev) \ @@ -143,5 +145,11 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw); void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe); int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs); void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw); +int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, + int vport, u8 mac[ETH_ALEN]); +int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw, + int vport, int link_state); +int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, + int vport, struct ifla_vf_info *ivi); #endif /* __MLX5_ESWITCH_H__ */ -- cgit v1.2.3-59-g8ed1b From d6666753c6e85834f1669c7b831cc2b7fc9e4390 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:22 +0200 Subject: net/mlx5: E-Switch, Introduce HCA cap and E-Switch vport context E-Switch vport context is unlike NIC vport context, managed by the E-Switch manager or vport_group_manager and not by the NIC(VF) driver. The E-Switch manager can access (read/modify) any of its vports E-Switch context. Currently E-Switch vport context includes only clietnt and server vlan insertion and striping data (for later support of VST mode). Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fw.c | 11 ++++ include/linux/mlx5/device.h | 9 +++ include/linux/mlx5/mlx5_ifc.h | 90 ++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index bf6e3dfcef51..1c9f9a54a873 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -173,6 +173,17 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev) return err; } + if (MLX5_CAP_GEN(dev, vport_group_manager)) { + err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH, + HCA_CAP_OPMOD_GET_CUR); + if (err) + return err; + err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH, + HCA_CAP_OPMOD_GET_MAX); + if (err) + return err; + } + return 0; } diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 88eb4490a8b3..7d3a85faefb7 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -1145,6 +1145,7 @@ enum mlx5_cap_type { MLX5_CAP_EOIB_OFFLOADS, MLX5_CAP_FLOW_TABLE, MLX5_CAP_ESWITCH_FLOW_TABLE, + MLX5_CAP_ESWITCH, /* NUM OF CAP Types */ MLX5_CAP_NUM }; @@ -1196,6 +1197,14 @@ enum mlx5_cap_type { #define MLX5_CAP_ESW_FLOWTABLE_FDB_MAX(mdev, cap) \ MLX5_CAP_ESW_FLOWTABLE_MAX(mdev, flow_table_properties_nic_esw_fdb.cap) +#define MLX5_CAP_ESW(mdev, cap) \ + MLX5_GET(e_switch_cap, \ + mdev->hca_caps_cur[MLX5_CAP_ESWITCH], cap) + +#define MLX5_CAP_ESW_MAX(mdev, cap) \ + MLX5_GET(e_switch_cap, \ + mdev->hca_caps_max[MLX5_CAP_ESWITCH], cap) + #define MLX5_CAP_ODP(mdev, cap)\ MLX5_GET(odp_cap, mdev->hca_caps_cur[MLX5_CAP_ODP], cap) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index a81b008738fd..f5d94495758a 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -459,6 +459,17 @@ struct mlx5_ifc_flow_table_eswitch_cap_bits { u8 reserved_1[0x7800]; }; +struct mlx5_ifc_e_switch_cap_bits { + u8 vport_svlan_strip[0x1]; + u8 vport_cvlan_strip[0x1]; + u8 vport_svlan_insert[0x1]; + u8 vport_cvlan_insert_if_not_exist[0x1]; + u8 vport_cvlan_insert_overwrite[0x1]; + u8 reserved_0[0x1b]; + + u8 reserved_1[0x7e0]; +}; + struct mlx5_ifc_per_protocol_networking_offload_caps_bits { u8 csum_cap[0x1]; u8 vlan_cap[0x1]; @@ -1860,6 +1871,7 @@ union mlx5_ifc_hca_cap_union_bits { struct mlx5_ifc_per_protocol_networking_offload_caps_bits per_protocol_networking_offload_caps; struct mlx5_ifc_flow_table_nic_cap_bits flow_table_nic_cap; struct mlx5_ifc_flow_table_eswitch_cap_bits flow_table_eswitch_cap; + struct mlx5_ifc_e_switch_cap_bits e_switch_cap; u8 reserved_0[0x8000]; }; @@ -2305,6 +2317,26 @@ struct mlx5_ifc_hca_vport_context_bits { u8 reserved_6[0xca0]; }; +struct mlx5_ifc_esw_vport_context_bits { + u8 reserved_0[0x3]; + u8 vport_svlan_strip[0x1]; + u8 vport_cvlan_strip[0x1]; + u8 vport_svlan_insert[0x1]; + u8 vport_cvlan_insert[0x2]; + u8 reserved_1[0x18]; + + u8 reserved_2[0x20]; + + u8 svlan_cfi[0x1]; + u8 svlan_pcp[0x3]; + u8 svlan_id[0xc]; + u8 cvlan_cfi[0x1]; + u8 cvlan_pcp[0x3]; + u8 cvlan_id[0xc]; + + u8 reserved_3[0x7a0]; +}; + enum { MLX5_EQC_STATUS_OK = 0x0, MLX5_EQC_STATUS_EQ_WRITE_FAILURE = 0xa, @@ -3743,6 +3775,64 @@ struct mlx5_ifc_query_flow_group_in_bits { u8 reserved_5[0x120]; }; +struct mlx5_ifc_query_esw_vport_context_out_bits { + u8 status[0x8]; + u8 reserved_0[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_1[0x40]; + + struct mlx5_ifc_esw_vport_context_bits esw_vport_context; +}; + +struct mlx5_ifc_query_esw_vport_context_in_bits { + u8 opcode[0x10]; + u8 reserved_0[0x10]; + + u8 reserved_1[0x10]; + u8 op_mod[0x10]; + + u8 other_vport[0x1]; + u8 reserved_2[0xf]; + u8 vport_number[0x10]; + + u8 reserved_3[0x20]; +}; + +struct mlx5_ifc_modify_esw_vport_context_out_bits { + u8 status[0x8]; + u8 reserved_0[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_1[0x40]; +}; + +struct mlx5_ifc_esw_vport_context_fields_select_bits { + u8 reserved[0x1c]; + u8 vport_cvlan_insert[0x1]; + u8 vport_svlan_insert[0x1]; + u8 vport_cvlan_strip[0x1]; + u8 vport_svlan_strip[0x1]; +}; + +struct mlx5_ifc_modify_esw_vport_context_in_bits { + u8 opcode[0x10]; + u8 reserved_0[0x10]; + + u8 reserved_1[0x10]; + u8 op_mod[0x10]; + + u8 other_vport[0x1]; + u8 reserved_2[0xf]; + u8 vport_number[0x10]; + + struct mlx5_ifc_esw_vport_context_fields_select_bits field_select; + + struct mlx5_ifc_esw_vport_context_bits esw_vport_context; +}; + struct mlx5_ifc_query_eq_out_bits { u8 status[0x8]; u8 reserved_0[0x18]; -- cgit v1.2.3-59-g8ed1b From 9e7ea3524a3f819911ffa7f471b78223757e824e Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:23 +0200 Subject: net/mlx5: E-Switch, Introduce set vport vlan (VST mode) Add query and modify functions to control client vlan and qos striping or insertion, in E-Switch vports contexts. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 134 +++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 2 + 2 files changed, 134 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index d7d6eba672a7..8fb66c075014 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -128,6 +128,116 @@ ex: return err; } +/* E-Switch vport context HW commands */ +static int query_esw_vport_context_cmd(struct mlx5_core_dev *mdev, u32 vport, + u32 *out, int outlen) +{ + u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)]; + + memset(in, 0, sizeof(in)); + + MLX5_SET(query_nic_vport_context_in, in, opcode, + MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT); + + MLX5_SET(query_esw_vport_context_in, in, vport_number, vport); + if (vport) + MLX5_SET(query_esw_vport_context_in, in, other_vport, 1); + + return mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen); +} + +static int query_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport, + u16 *vlan, u8 *qos) +{ + u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)]; + int err; + bool cvlan_strip; + bool cvlan_insert; + + memset(out, 0, sizeof(out)); + + *vlan = 0; + *qos = 0; + + if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) || + !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist)) + return -ENOTSUPP; + + err = query_esw_vport_context_cmd(dev, vport, out, sizeof(out)); + if (err) + goto out; + + cvlan_strip = MLX5_GET(query_esw_vport_context_out, out, + esw_vport_context.vport_cvlan_strip); + + cvlan_insert = MLX5_GET(query_esw_vport_context_out, out, + esw_vport_context.vport_cvlan_insert); + + if (cvlan_strip || cvlan_insert) { + *vlan = MLX5_GET(query_esw_vport_context_out, out, + esw_vport_context.cvlan_id); + *qos = MLX5_GET(query_esw_vport_context_out, out, + esw_vport_context.cvlan_pcp); + } + + esw_debug(dev, "Query Vport[%d] cvlan: VLAN %d qos=%d\n", + vport, *vlan, *qos); +out: + return err; +} + +static int modify_esw_vport_context_cmd(struct mlx5_core_dev *dev, u16 vport, + void *in, int inlen) +{ + u32 out[MLX5_ST_SZ_DW(modify_esw_vport_context_out)]; + + memset(out, 0, sizeof(out)); + + MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport); + if (vport) + MLX5_SET(modify_esw_vport_context_in, in, other_vport, 1); + + MLX5_SET(modify_esw_vport_context_in, in, opcode, + MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT); + + return mlx5_cmd_exec_check_status(dev, in, inlen, + out, sizeof(out)); +} + +static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport, + u16 vlan, u8 qos, bool set) +{ + u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)]; + + memset(in, 0, sizeof(in)); + + if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) || + !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist)) + return -ENOTSUPP; + + esw_debug(dev, "Set Vport[%d] VLAN %d qos %d set=%d\n", + vport, vlan, qos, set); + + if (set) { + MLX5_SET(modify_esw_vport_context_in, in, + esw_vport_context.vport_cvlan_strip, 1); + /* insert only if no vlan in packet */ + MLX5_SET(modify_esw_vport_context_in, in, + esw_vport_context.vport_cvlan_insert, 1); + MLX5_SET(modify_esw_vport_context_in, in, + esw_vport_context.cvlan_pcp, qos); + MLX5_SET(modify_esw_vport_context_in, in, + esw_vport_context.cvlan_id, vlan); + } + + MLX5_SET(modify_esw_vport_context_in, in, + field_select.vport_cvlan_strip, 1); + MLX5_SET(modify_esw_vport_context_in, in, + field_select.vport_cvlan_insert, 1); + + return modify_esw_vport_context_cmd(dev, vport, in, sizeof(in)); +} + /* HW L2 Table (MPFS) management */ static int set_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index, u8 *mac, u8 vlan_valid, u16 vlan) @@ -1065,6 +1175,9 @@ int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw, int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, int vport, struct ifla_vf_info *ivi) { + u16 vlan; + u8 qos; + if (!ESW_ALLOWED(esw)) return -EPERM; if (!LEGAL_VPORT(esw, vport)) @@ -1077,9 +1190,26 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, ivi->linkstate = mlx5_query_vport_admin_state(esw->dev, MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT, vport); - ivi->vlan = 0; - ivi->qos = 0; + query_esw_vport_cvlan(esw->dev, vport, &vlan, &qos); + ivi->vlan = vlan; + ivi->qos = qos; ivi->spoofchk = 0; return 0; } + +int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw, + int vport, u16 vlan, u8 qos) +{ + int set = 0; + + if (!ESW_ALLOWED(esw)) + return -EPERM; + if (!LEGAL_VPORT(esw, vport) || (vlan > 4095) || (qos > 7)) + return -EINVAL; + + if (vlan || qos) + set = 1; + + return modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 43a6730d76a3..f85d5b86ef41 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -149,6 +149,8 @@ int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, int vport, u8 mac[ETH_ALEN]); int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw, int vport, int link_state); +int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw, + int vport, u16 vlan, u8 qos); int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, int vport, struct ifla_vf_info *ivi); -- cgit v1.2.3-59-g8ed1b From 3b751a2a418aa58c5bd3b23bf97d169cc4c63819 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:24 +0200 Subject: net/mlx5: E-Switch, Introduce get vf statistics Add support to get VF statistics using query vport counter command. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 67 +++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 3 + 2 files changed, 70 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 8fb66c075014..d8939e597c54 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1213,3 +1213,70 @@ int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw, return modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set); } + +int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw, + int vport, + struct ifla_vf_stats *vf_stats) +{ + int outlen = MLX5_ST_SZ_BYTES(query_vport_counter_out); + u32 in[MLX5_ST_SZ_DW(query_vport_counter_in)]; + int err = 0; + u32 *out; + + if (!ESW_ALLOWED(esw)) + return -EPERM; + if (!LEGAL_VPORT(esw, vport)) + return -EINVAL; + + out = mlx5_vzalloc(outlen); + if (!out) + return -ENOMEM; + + memset(in, 0, sizeof(in)); + + MLX5_SET(query_vport_counter_in, in, opcode, + MLX5_CMD_OP_QUERY_VPORT_COUNTER); + MLX5_SET(query_vport_counter_in, in, op_mod, 0); + MLX5_SET(query_vport_counter_in, in, vport_number, vport); + if (vport) + MLX5_SET(query_vport_counter_in, in, other_vport, 1); + + memset(out, 0, outlen); + err = mlx5_cmd_exec(esw->dev, in, sizeof(in), out, outlen); + if (err) + goto free_out; + + #define MLX5_GET_CTR(p, x) \ + MLX5_GET64(query_vport_counter_out, p, x) + + memset(vf_stats, 0, sizeof(*vf_stats)); + vf_stats->rx_packets = + MLX5_GET_CTR(out, received_eth_unicast.packets) + + MLX5_GET_CTR(out, received_eth_multicast.packets) + + MLX5_GET_CTR(out, received_eth_broadcast.packets); + + vf_stats->rx_bytes = + MLX5_GET_CTR(out, received_eth_unicast.octets) + + MLX5_GET_CTR(out, received_eth_multicast.octets) + + MLX5_GET_CTR(out, received_eth_broadcast.octets); + + vf_stats->tx_packets = + MLX5_GET_CTR(out, transmitted_eth_unicast.packets) + + MLX5_GET_CTR(out, transmitted_eth_multicast.packets) + + MLX5_GET_CTR(out, transmitted_eth_broadcast.packets); + + vf_stats->tx_bytes = + MLX5_GET_CTR(out, transmitted_eth_unicast.octets) + + MLX5_GET_CTR(out, transmitted_eth_multicast.octets) + + MLX5_GET_CTR(out, transmitted_eth_broadcast.octets); + + vf_stats->multicast = + MLX5_GET_CTR(out, received_eth_multicast.packets); + + vf_stats->broadcast = + MLX5_GET_CTR(out, received_eth_broadcast.packets); + +free_out: + kvfree(out); + return err; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index f85d5b86ef41..02ff3eade026 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -153,5 +153,8 @@ int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw, int vport, u16 vlan, u8 qos); int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, int vport, struct ifla_vf_info *ivi); +int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw, + int vport, + struct ifla_vf_stats *vf_stats); #endif /* __MLX5_ESWITCH_H__ */ -- cgit v1.2.3-59-g8ed1b From 66e49dedada6e5badbb01e19e7ae511172c5ac7d Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 1 Dec 2015 18:03:25 +0200 Subject: net/mlx5e: Add support for SR-IOV ndos Implement and enable SR-IOV ndos to manage SR-IOV configuration via netdev netlink API. Signed-off-by: Saeed Mahameed Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 84 ++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 007e464b3e58..d67058afe87e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -32,6 +32,7 @@ #include #include "en.h" +#include "eswitch.h" struct mlx5e_rq_param { u32 rqc[MLX5_ST_SZ_DW(rqc)]; @@ -1931,6 +1932,79 @@ static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu) return err; } +static int mlx5e_set_vf_mac(struct net_device *dev, int vf, u8 *mac) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5_core_dev *mdev = priv->mdev; + + return mlx5_eswitch_set_vport_mac(mdev->priv.eswitch, vf + 1, mac); +} + +static int mlx5e_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5_core_dev *mdev = priv->mdev; + + return mlx5_eswitch_set_vport_vlan(mdev->priv.eswitch, vf + 1, + vlan, qos); +} + +static int mlx5_vport_link2ifla(u8 esw_link) +{ + switch (esw_link) { + case MLX5_ESW_VPORT_ADMIN_STATE_DOWN: + return IFLA_VF_LINK_STATE_DISABLE; + case MLX5_ESW_VPORT_ADMIN_STATE_UP: + return IFLA_VF_LINK_STATE_ENABLE; + } + return IFLA_VF_LINK_STATE_AUTO; +} + +static int mlx5_ifla_link2vport(u8 ifla_link) +{ + switch (ifla_link) { + case IFLA_VF_LINK_STATE_DISABLE: + return MLX5_ESW_VPORT_ADMIN_STATE_DOWN; + case IFLA_VF_LINK_STATE_ENABLE: + return MLX5_ESW_VPORT_ADMIN_STATE_UP; + } + return MLX5_ESW_VPORT_ADMIN_STATE_AUTO; +} + +static int mlx5e_set_vf_link_state(struct net_device *dev, int vf, + int link_state) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5_core_dev *mdev = priv->mdev; + + return mlx5_eswitch_set_vport_state(mdev->priv.eswitch, vf + 1, + mlx5_ifla_link2vport(link_state)); +} + +static int mlx5e_get_vf_config(struct net_device *dev, + int vf, struct ifla_vf_info *ivi) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5_core_dev *mdev = priv->mdev; + int err; + + err = mlx5_eswitch_get_vport_config(mdev->priv.eswitch, vf + 1, ivi); + if (err) + return err; + ivi->linkstate = mlx5_vport_link2ifla(ivi->linkstate); + return 0; +} + +static int mlx5e_get_vf_stats(struct net_device *dev, + int vf, struct ifla_vf_stats *vf_stats) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5_core_dev *mdev = priv->mdev; + + return mlx5_eswitch_get_vport_stats(mdev->priv.eswitch, vf + 1, + vf_stats); +} + static struct net_device_ops mlx5e_netdev_ops = { .ndo_open = mlx5e_open, .ndo_stop = mlx5e_close, @@ -1941,7 +2015,7 @@ static struct net_device_ops mlx5e_netdev_ops = { .ndo_vlan_rx_add_vid = mlx5e_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = mlx5e_vlan_rx_kill_vid, .ndo_set_features = mlx5e_set_features, - .ndo_change_mtu = mlx5e_change_mtu, + .ndo_change_mtu = mlx5e_change_mtu }; static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev) @@ -2041,6 +2115,14 @@ static void mlx5e_build_netdev(struct net_device *netdev) if (priv->params.num_tc > 1) mlx5e_netdev_ops.ndo_select_queue = mlx5e_select_queue; + if (MLX5_CAP_GEN(mdev, vport_group_manager)) { + mlx5e_netdev_ops.ndo_set_vf_mac = mlx5e_set_vf_mac; + mlx5e_netdev_ops.ndo_set_vf_vlan = mlx5e_set_vf_vlan; + mlx5e_netdev_ops.ndo_get_vf_config = mlx5e_get_vf_config; + mlx5e_netdev_ops.ndo_set_vf_link_state = mlx5e_set_vf_link_state; + mlx5e_netdev_ops.ndo_get_vf_stats = mlx5e_get_vf_stats; + } + netdev->netdev_ops = &mlx5e_netdev_ops; netdev->watchdog_timeo = 15 * HZ; -- cgit v1.2.3-59-g8ed1b From 4c3523623dc0b980158e34b64360603035239a71 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 1 Dec 2015 14:55:22 +0000 Subject: net: add driver for Netronome NFP4000/NFP6000 NIC VFs Add driver for Virtual Functions for the Netronome's NFP-4000 and NFP-6000 based NICs. Signed-off-by: Jakub Kicinski Signed-off-by: Rolf Neugebauer Signed-off-by: David S. Miller --- MAINTAINERS | 7 + drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/netronome/Kconfig | 36 + drivers/net/ethernet/netronome/Makefile | 5 + drivers/net/ethernet/netronome/nfp/Makefile | 8 + drivers/net/ethernet/netronome/nfp/nfp_net.h | 748 ++++++ .../net/ethernet/netronome/nfp/nfp_net_common.c | 2432 ++++++++++++++++++++ drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h | 323 +++ .../net/ethernet/netronome/nfp/nfp_net_debugfs.c | 235 ++ .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 640 ++++++ .../net/ethernet/netronome/nfp/nfp_netvf_main.c | 385 ++++ 12 files changed, 4821 insertions(+) create mode 100644 drivers/net/ethernet/netronome/Kconfig create mode 100644 drivers/net/ethernet/netronome/Makefile create mode 100644 drivers/net/ethernet/netronome/nfp/Makefile create mode 100644 drivers/net/ethernet/netronome/nfp/nfp_net.h create mode 100644 drivers/net/ethernet/netronome/nfp/nfp_net_common.c create mode 100644 drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h create mode 100644 drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c create mode 100644 drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c create mode 100644 drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 9981db3f03ed..79029b4bfc80 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7364,6 +7364,13 @@ F: include/net/netrom.h F: include/uapi/linux/netrom.h F: net/netrom/ +NETRONOME ETHERNET DRIVERS +M: Jakub Kicinski +M: Rolf Neugebauer +L: oss-drivers@netronome.com +S: Maintained +F: drivers/net/ethernet/netronome/ + NETWORK BLOCK DEVICE (NBD) M: Markus Pargmann S: Maintained diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 955d06b9cdba..e57304397d33 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -121,6 +121,7 @@ config FEALNX cards. source "drivers/net/ethernet/natsemi/Kconfig" +source "drivers/net/ethernet/netronome/Kconfig" source "drivers/net/ethernet/8390/Kconfig" config NET_NETX diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 4a2ee98738f0..99ede51adf10 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/ obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/ obj-$(CONFIG_FEALNX) += fealnx.o obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/ +obj-$(CONFIG_NET_VENDOR_NETRONOME) += netronome/ obj-$(CONFIG_NET_NETX) += netx-eth.o obj-$(CONFIG_NET_VENDOR_NUVOTON) += nuvoton/ obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/ diff --git a/drivers/net/ethernet/netronome/Kconfig b/drivers/net/ethernet/netronome/Kconfig new file mode 100644 index 000000000000..9508ad782c30 --- /dev/null +++ b/drivers/net/ethernet/netronome/Kconfig @@ -0,0 +1,36 @@ +# +# Netronome device configuration +# + +config NET_VENDOR_NETRONOME + bool "Netronome(R) devices" + default y + ---help--- + If you have a Netronome(R) network (Ethernet) card or device, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Netronome(R) cards. If you say Y, you will be + asked for your specific card in the following questions. + +if NET_VENDOR_NETRONOME + +config NFP_NETVF + tristate "Netronome(R) NFP4000/NFP6000 VF NIC driver" + depends on PCI && PCI_MSI + depends on VXLAN || VXLAN=n + ---help--- + This driver supports SR-IOV virtual functions of + the Netronome(R) NFP4000/NFP6000 cards working as + a advanced Ethernet NIC. + +config NFP_NET_DEBUG + bool "Debug support for Netronome(R) NFP3200/NFP6000 NIC drivers" + depends on NFP_NET || NFP_NETVF + ---help--- + Enable extra sanity checks and debugfs support in + Netronome(R) NFP3200/NFP6000 NIC PF and VF drivers. + Note: selecting this option may adversely impact + performance. + +endif diff --git a/drivers/net/ethernet/netronome/Makefile b/drivers/net/ethernet/netronome/Makefile new file mode 100644 index 000000000000..dcb7b383f634 --- /dev/null +++ b/drivers/net/ethernet/netronome/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Netronome network device drivers +# + +obj-$(CONFIG_NFP_NETVF) += nfp/ diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile new file mode 100644 index 000000000000..68178819ff12 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_NFP_NETVF) += nfp_netvf.o + +nfp_netvf-objs := \ + nfp_net_common.o \ + nfp_net_ethtool.o \ + nfp_netvf_main.o + +nfp_netvf-$(CONFIG_NFP_NET_DEBUG) += nfp_net_debugfs.o diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h new file mode 100644 index 000000000000..ab264e1bccd0 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -0,0 +1,748 @@ +/* + * Copyright (C) 2015 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * nfp_net.h + * Declarations for Netronome network device driver. + * Authors: Jakub Kicinski + * Jason McMullan + * Rolf Neugebauer + */ + +#ifndef _NFP_NET_H_ +#define _NFP_NET_H_ + +#include +#include +#include +#include + +#include "nfp_net_ctrl.h" + +#define nn_err(nn, fmt, args...) netdev_err((nn)->netdev, fmt, ## args) +#define nn_warn(nn, fmt, args...) netdev_warn((nn)->netdev, fmt, ## args) +#define nn_info(nn, fmt, args...) netdev_info((nn)->netdev, fmt, ## args) +#define nn_dbg(nn, fmt, args...) netdev_dbg((nn)->netdev, fmt, ## args) +#define nn_warn_ratelimit(nn, fmt, args...) \ + do { \ + if (unlikely(net_ratelimit())) \ + netdev_warn((nn)->netdev, fmt, ## args); \ + } while (0) + +/* Max time to wait for NFP to respond on updates (in ms) */ +#define NFP_NET_POLL_TIMEOUT 5000 + +/* Bar allocation */ +#define NFP_NET_CRTL_BAR 0 +#define NFP_NET_Q0_BAR 2 +#define NFP_NET_Q1_BAR 4 /* OBSOLETE */ + +/* Max bits in DMA address */ +#define NFP_NET_MAX_DMA_BITS 40 + +/* Default size for MTU and freelist buffer sizes */ +#define NFP_NET_DEFAULT_MTU 1500 +#define NFP_NET_DEFAULT_RX_BUFSZ 2048 + +/* Maximum number of bytes prepended to a packet */ +#define NFP_NET_MAX_PREPEND 64 + +/* Interrupt definitions */ +#define NFP_NET_NON_Q_VECTORS 2 +#define NFP_NET_IRQ_LSC_IDX 0 +#define NFP_NET_IRQ_EXN_IDX 1 + +/* Queue/Ring definitions */ +#define NFP_NET_MAX_TX_RINGS 64 /* Max. # of Tx rings per device */ +#define NFP_NET_MAX_RX_RINGS 64 /* Max. # of Rx rings per device */ + +#define NFP_NET_MIN_TX_DESCS 256 /* Min. # of Tx descs per ring */ +#define NFP_NET_MIN_RX_DESCS 256 /* Min. # of Rx descs per ring */ +#define NFP_NET_MAX_TX_DESCS (256 * 1024) /* Max. # of Tx descs per ring */ +#define NFP_NET_MAX_RX_DESCS (256 * 1024) /* Max. # of Rx descs per ring */ + +#define NFP_NET_TX_DESCS_DEFAULT 4096 /* Default # of Tx descs per ring */ +#define NFP_NET_RX_DESCS_DEFAULT 4096 /* Default # of Rx descs per ring */ + +#define NFP_NET_FL_BATCH 16 /* Add freelist in this Batch size */ + +/* Offload definitions */ +#define NFP_NET_N_VXLAN_PORTS (NFP_NET_CFG_VXLAN_SZ / sizeof(__be16)) + +/* Forward declarations */ +struct nfp_net; +struct nfp_net_r_vector; + +/* Convenience macro for writing dma address into RX/TX descriptors */ +#define nfp_desc_set_dma_addr(desc, dma_addr) \ + do { \ + __typeof(desc) __d = (desc); \ + dma_addr_t __addr = (dma_addr); \ + \ + __d->dma_addr_lo = cpu_to_le32(lower_32_bits(__addr)); \ + __d->dma_addr_hi = upper_32_bits(__addr) & 0xff; \ + } while (0) + +/* TX descriptor format */ + +#define PCIE_DESC_TX_EOP BIT(7) +#define PCIE_DESC_TX_OFFSET_MASK GENMASK(6, 0) +#define PCIE_DESC_TX_MSS_MASK GENMASK(13, 0) + +/* Flags in the host TX descriptor */ +#define PCIE_DESC_TX_CSUM BIT(7) +#define PCIE_DESC_TX_IP4_CSUM BIT(6) +#define PCIE_DESC_TX_TCP_CSUM BIT(5) +#define PCIE_DESC_TX_UDP_CSUM BIT(4) +#define PCIE_DESC_TX_VLAN BIT(3) +#define PCIE_DESC_TX_LSO BIT(2) +#define PCIE_DESC_TX_ENCAP BIT(1) +#define PCIE_DESC_TX_O_IP4_CSUM BIT(0) + +struct nfp_net_tx_desc { + union { + struct { + u8 dma_addr_hi; /* High bits of host buf address */ + __le16 dma_len; /* Length to DMA for this desc */ + u8 offset_eop; /* Offset in buf where pkt starts + + * highest bit is eop flag. + */ + __le32 dma_addr_lo; /* Low 32bit of host buf addr */ + + __le16 mss; /* MSS to be used for LSO */ + u8 l4_offset; /* LSO, where the L4 data starts */ + u8 flags; /* TX Flags, see @PCIE_DESC_TX_* */ + + __le16 vlan; /* VLAN tag to add if indicated */ + __le16 data_len; /* Length of frame + meta data */ + } __packed; + __le32 vals[4]; + }; +}; + +/** + * struct nfp_net_tx_buf - software TX buffer descriptor + * @skb: sk_buff associated with this buffer + * @dma_addr: DMA mapping address of the buffer + * @fidx: Fragment index (-1 for the head and [0..nr_frags-1] for frags) + * @pkt_cnt: Number of packets to be produced out of the skb associated + * with this buffer (valid only on the head's buffer). + * Will be 1 for all non-TSO packets. + * @real_len: Number of bytes which to be produced out of the skb (valid only + * on the head's buffer). Equal to skb->len for non-TSO packets. + */ +struct nfp_net_tx_buf { + struct sk_buff *skb; + dma_addr_t dma_addr; + short int fidx; + u16 pkt_cnt; + u32 real_len; +}; + +/** + * struct nfp_net_tx_ring - TX ring structure + * @r_vec: Back pointer to ring vector structure + * @idx: Ring index from Linux's perspective + * @qcidx: Queue Controller Peripheral (QCP) queue index for the TX queue + * @qcp_q: Pointer to base of the QCP TX queue + * @cnt: Size of the queue in number of descriptors + * @wr_p: TX ring write pointer (free running) + * @rd_p: TX ring read pointer (free running) + * @qcp_rd_p: Local copy of QCP TX queue read pointer + * @wr_ptr_add: Accumulated number of buffers to add to QCP write pointer + * (used for .xmit_more delayed kick) + * @txbufs: Array of transmitted TX buffers, to free on transmit + * @txds: Virtual address of TX ring in host memory + * @dma: DMA address of the TX ring + * @size: Size, in bytes, of the TX ring (needed to free) + */ +struct nfp_net_tx_ring { + struct nfp_net_r_vector *r_vec; + + u32 idx; + int qcidx; + u8 __iomem *qcp_q; + + u32 cnt; + u32 wr_p; + u32 rd_p; + u32 qcp_rd_p; + + u32 wr_ptr_add; + + struct nfp_net_tx_buf *txbufs; + struct nfp_net_tx_desc *txds; + + dma_addr_t dma; + unsigned int size; +} ____cacheline_aligned; + +/* RX and freelist descriptor format */ + +#define PCIE_DESC_RX_DD BIT(7) +#define PCIE_DESC_RX_META_LEN_MASK GENMASK(6, 0) + +/* Flags in the RX descriptor */ +#define PCIE_DESC_RX_RSS cpu_to_le16(BIT(15)) +#define PCIE_DESC_RX_I_IP4_CSUM cpu_to_le16(BIT(14)) +#define PCIE_DESC_RX_I_IP4_CSUM_OK cpu_to_le16(BIT(13)) +#define PCIE_DESC_RX_I_TCP_CSUM cpu_to_le16(BIT(12)) +#define PCIE_DESC_RX_I_TCP_CSUM_OK cpu_to_le16(BIT(11)) +#define PCIE_DESC_RX_I_UDP_CSUM cpu_to_le16(BIT(10)) +#define PCIE_DESC_RX_I_UDP_CSUM_OK cpu_to_le16(BIT(9)) +#define PCIE_DESC_RX_SPARE cpu_to_le16(BIT(8)) +#define PCIE_DESC_RX_EOP cpu_to_le16(BIT(7)) +#define PCIE_DESC_RX_IP4_CSUM cpu_to_le16(BIT(6)) +#define PCIE_DESC_RX_IP4_CSUM_OK cpu_to_le16(BIT(5)) +#define PCIE_DESC_RX_TCP_CSUM cpu_to_le16(BIT(4)) +#define PCIE_DESC_RX_TCP_CSUM_OK cpu_to_le16(BIT(3)) +#define PCIE_DESC_RX_UDP_CSUM cpu_to_le16(BIT(2)) +#define PCIE_DESC_RX_UDP_CSUM_OK cpu_to_le16(BIT(1)) +#define PCIE_DESC_RX_VLAN cpu_to_le16(BIT(0)) + +#define PCIE_DESC_RX_CSUM_ALL (PCIE_DESC_RX_IP4_CSUM | \ + PCIE_DESC_RX_TCP_CSUM | \ + PCIE_DESC_RX_UDP_CSUM | \ + PCIE_DESC_RX_I_IP4_CSUM | \ + PCIE_DESC_RX_I_TCP_CSUM | \ + PCIE_DESC_RX_I_UDP_CSUM) +#define PCIE_DESC_RX_CSUM_OK_SHIFT 1 +#define __PCIE_DESC_RX_CSUM_ALL le16_to_cpu(PCIE_DESC_RX_CSUM_ALL) +#define __PCIE_DESC_RX_CSUM_ALL_OK (__PCIE_DESC_RX_CSUM_ALL >> \ + PCIE_DESC_RX_CSUM_OK_SHIFT) + +struct nfp_net_rx_desc { + union { + struct { + u8 dma_addr_hi; /* High bits of the buf address */ + __le16 reserved; /* Must be zero */ + u8 meta_len_dd; /* Must be zero */ + + __le32 dma_addr_lo; /* Low bits of the buffer address */ + } __packed fld; + + struct { + __le16 data_len; /* Length of the frame + meta data */ + u8 reserved; + u8 meta_len_dd; /* Length of meta data prepended + + * descriptor done flag. + */ + + __le16 flags; /* RX flags. See @PCIE_DESC_RX_* */ + __le16 vlan; /* VLAN if stripped */ + } __packed rxd; + + __le32 vals[2]; + }; +}; + +struct nfp_net_rx_hash { + __be32 hash_type; + __be32 hash; +}; + +/** + * struct nfp_net_rx_buf - software RX buffer descriptor + * @skb: sk_buff associated with this buffer + * @dma_addr: DMA mapping address of the buffer + */ +struct nfp_net_rx_buf { + struct sk_buff *skb; + dma_addr_t dma_addr; +}; + +/** + * struct nfp_net_rx_ring - RX ring structure + * @r_vec: Back pointer to ring vector structure + * @cnt: Size of the queue in number of descriptors + * @wr_p: FL/RX ring write pointer (free running) + * @rd_p: FL/RX ring read pointer (free running) + * @idx: Ring index from Linux's perspective + * @fl_qcidx: Queue Controller Peripheral (QCP) queue index for the freelist + * @rx_qcidx: Queue Controller Peripheral (QCP) queue index for the RX queue + * @qcp_fl: Pointer to base of the QCP freelist queue + * @qcp_rx: Pointer to base of the QCP RX queue + * @wr_ptr_add: Accumulated number of buffers to add to QCP write pointer + * (used for free list batching) + * @rxbufs: Array of transmitted FL/RX buffers + * @rxds: Virtual address of FL/RX ring in host memory + * @dma: DMA address of the FL/RX ring + * @size: Size, in bytes, of the FL/RX ring (needed to free) + */ +struct nfp_net_rx_ring { + struct nfp_net_r_vector *r_vec; + + u32 cnt; + u32 wr_p; + u32 rd_p; + + u16 idx; + u16 wr_ptr_add; + + int fl_qcidx; + int rx_qcidx; + u8 __iomem *qcp_fl; + u8 __iomem *qcp_rx; + + struct nfp_net_rx_buf *rxbufs; + struct nfp_net_rx_desc *rxds; + + dma_addr_t dma; + unsigned int size; +} ____cacheline_aligned; + +/** + * struct nfp_net_r_vector - Per ring interrupt vector configuration + * @nfp_net: Backpointer to nfp_net structure + * @napi: NAPI structure for this ring vec + * @tx_ring: Pointer to TX ring + * @rx_ring: Pointer to RX ring + * @irq_idx: Index into MSI-X table + * @rx_sync: Seqlock for atomic updates of RX stats + * @rx_pkts: Number of received packets + * @rx_bytes: Number of received bytes + * @rx_drops: Number of packets dropped on RX due to lack of resources + * @hw_csum_rx_ok: Counter of packets where the HW checksum was OK + * @hw_csum_rx_inner_ok: Counter of packets where the inner HW checksum was OK + * @hw_csum_rx_error: Counter of packets with bad checksums + * @tx_sync: Seqlock for atomic updates of TX stats + * @tx_pkts: Number of Transmitted packets + * @tx_bytes: Number of Transmitted bytes + * @hw_csum_tx: Counter of packets with TX checksum offload requested + * @hw_csum_tx_inner: Counter of inner TX checksum offload requests + * @tx_gather: Counter of packets with Gather DMA + * @tx_lso: Counter of LSO packets sent + * @tx_errors: How many TX errors were encountered + * @tx_busy: How often was TX busy (no space)? + * @handler: Interrupt handler for this ring vector + * @name: Name of the interrupt vector + * @affinity_mask: SMP affinity mask for this vector + * + * This structure ties RX and TX rings to interrupt vectors and a NAPI + * context. This currently only supports one RX and TX ring per + * interrupt vector but might be extended in the future to allow + * association of multiple rings per vector. + */ +struct nfp_net_r_vector { + struct nfp_net *nfp_net; + struct napi_struct napi; + + struct nfp_net_tx_ring *tx_ring; + struct nfp_net_rx_ring *rx_ring; + + int irq_idx; + + struct u64_stats_sync rx_sync; + u64 rx_pkts; + u64 rx_bytes; + u64 rx_drops; + u64 hw_csum_rx_ok; + u64 hw_csum_rx_inner_ok; + u64 hw_csum_rx_error; + + struct u64_stats_sync tx_sync; + u64 tx_pkts; + u64 tx_bytes; + u64 hw_csum_tx; + u64 hw_csum_tx_inner; + u64 tx_gather; + u64 tx_lso; + u64 tx_errors; + u64 tx_busy; + + irq_handler_t handler; + char name[IFNAMSIZ + 8]; + cpumask_t affinity_mask; +} ____cacheline_aligned; + +/* Firmware version as it is written in the 32bit value in the BAR */ +struct nfp_net_fw_version { + u8 minor; + u8 major; + u8 class; + u8 resv; +} __packed; + +static inline bool nfp_net_fw_ver_eq(struct nfp_net_fw_version *fw_ver, + u8 resv, u8 class, u8 major, u8 minor) +{ + return fw_ver->resv == resv && + fw_ver->class == class && + fw_ver->major == major && + fw_ver->minor == minor; +} + +/** + * struct nfp_net - NFP network device structure + * @pdev: Backpointer to PCI device + * @netdev: Backpointer to net_device structure + * @nfp_fallback: Is the driver used in fallback mode? + * @is_vf: Is the driver attached to a VF? + * @is_nfp3200: Is the driver for a NFP-3200 card? + * @fw_loaded: Is the firmware loaded? + * @ctrl: Local copy of the control register/word. + * @fl_bufsz: Currently configured size of the freelist buffers + * @rx_offset: Offset in the RX buffers where packet data starts + * @cpp: Pointer to the CPP handle + * @nfp_dev_cpp: Pointer to the NFP Device handle + * @ctrl_area: Pointer to the CPP area for the control BAR + * @tx_area: Pointer to the CPP area for the TX queues + * @rx_area: Pointer to the CPP area for the FL/RX queues + * @fw_ver: Firmware version + * @cap: Capabilities advertised by the Firmware + * @max_mtu: Maximum support MTU advertised by the Firmware + * @rss_cfg: RSS configuration + * @rss_key: RSS secret key + * @rss_itbl: RSS indirection table + * @max_tx_rings: Maximum number of TX rings supported by the Firmware + * @max_rx_rings: Maximum number of RX rings supported by the Firmware + * @num_tx_rings: Currently configured number of TX rings + * @num_rx_rings: Currently configured number of RX rings + * @txd_cnt: Size of the TX ring in number of descriptors + * @rxd_cnt: Size of the RX ring in number of descriptors + * @tx_rings: Array of pre-allocated TX ring structures + * @rx_rings: Array of pre-allocated RX ring structures + * @num_irqs: Number of allocated interrupt vectors + * @num_r_vecs: Number of used ring vectors + * @r_vecs: Pre-allocated array of ring vectors + * @irq_entries: Pre-allocated array of MSI-X entries + * @lsc_handler: Handler for Link State Change interrupt + * @lsc_name: Name for Link State Change interrupt + * @exn_handler: Handler for Exception interrupt + * @exn_name: Name for Exception interrupt + * @shared_handler: Handler for shared interrupts + * @shared_name: Name for shared interrupt + * @me_freq_mhz: ME clock_freq (MHz) + * @reconfig_lock: Protects HW reconfiguration request regs/machinery + * @link_up: Is the link up? + * @link_status_lock: Protects @link_up and ensures atomicity with BAR reading + * @rx_coalesce_usecs: RX interrupt moderation usecs delay parameter + * @rx_coalesce_max_frames: RX interrupt moderation frame count parameter + * @tx_coalesce_usecs: TX interrupt moderation usecs delay parameter + * @tx_coalesce_max_frames: TX interrupt moderation frame count parameter + * @vxlan_ports: VXLAN ports for RX inner csum offload communicated to HW + * @vxlan_usecnt: IPv4/IPv6 VXLAN port use counts + * @qcp_cfg: Pointer to QCP queue used for configuration notification + * @ctrl_bar: Pointer to mapped control BAR + * @tx_bar: Pointer to mapped TX queues + * @rx_bar: Pointer to mapped FL/RX queues + * @debugfs_dir: Device directory in debugfs + */ +struct nfp_net { + struct pci_dev *pdev; + struct net_device *netdev; + + unsigned nfp_fallback:1; + unsigned is_vf:1; + unsigned is_nfp3200:1; + unsigned fw_loaded:1; + + u32 ctrl; + u32 fl_bufsz; + + u32 rx_offset; + +#ifdef CONFIG_PCI_IOV + unsigned int num_vfs; + struct vf_data_storage *vfinfo; + int vf_rate_link_speed; +#endif + + struct nfp_cpp *cpp; + struct platform_device *nfp_dev_cpp; + struct nfp_cpp_area *ctrl_area; + struct nfp_cpp_area *tx_area; + struct nfp_cpp_area *rx_area; + + struct nfp_net_fw_version fw_ver; + u32 cap; + u32 max_mtu; + + u32 rss_cfg; + u8 rss_key[NFP_NET_CFG_RSS_KEY_SZ]; + u8 rss_itbl[NFP_NET_CFG_RSS_ITBL_SZ]; + + int max_tx_rings; + int max_rx_rings; + + int num_tx_rings; + int num_rx_rings; + + int stride_tx; + int stride_rx; + + int txd_cnt; + int rxd_cnt; + + struct nfp_net_tx_ring tx_rings[NFP_NET_MAX_TX_RINGS]; + struct nfp_net_rx_ring rx_rings[NFP_NET_MAX_RX_RINGS]; + + u8 num_irqs; + u8 num_r_vecs; + struct nfp_net_r_vector r_vecs[NFP_NET_MAX_TX_RINGS]; + struct msix_entry irq_entries[NFP_NET_NON_Q_VECTORS + + NFP_NET_MAX_TX_RINGS]; + + irq_handler_t lsc_handler; + char lsc_name[IFNAMSIZ + 8]; + + irq_handler_t exn_handler; + char exn_name[IFNAMSIZ + 8]; + + irq_handler_t shared_handler; + char shared_name[IFNAMSIZ + 8]; + + u32 me_freq_mhz; + + bool link_up; + spinlock_t link_status_lock; + + spinlock_t reconfig_lock; + + u32 rx_coalesce_usecs; + u32 rx_coalesce_max_frames; + u32 tx_coalesce_usecs; + u32 tx_coalesce_max_frames; + + __be16 vxlan_ports[NFP_NET_N_VXLAN_PORTS]; + u8 vxlan_usecnt[NFP_NET_N_VXLAN_PORTS]; + + u8 __iomem *qcp_cfg; + + u8 __iomem *ctrl_bar; + u8 __iomem *q_bar; + u8 __iomem *tx_bar; + u8 __iomem *rx_bar; + + struct dentry *debugfs_dir; +}; + +/* Functions to read/write from/to a BAR + * Performs any endian conversion necessary. + */ +static inline void nn_writeb(struct nfp_net *nn, int off, u8 val) +{ + writeb(val, nn->ctrl_bar + off); +} + +/* NFP-3200 can't handle 16-bit accesses too well - hence no readw/writew */ + +static inline u32 nn_readl(struct nfp_net *nn, int off) +{ + return readl(nn->ctrl_bar + off); +} + +static inline void nn_writel(struct nfp_net *nn, int off, u32 val) +{ + writel(val, nn->ctrl_bar + off); +} + +static inline u64 nn_readq(struct nfp_net *nn, int off) +{ + return readq(nn->ctrl_bar + off); +} + +static inline void nn_writeq(struct nfp_net *nn, int off, u64 val) +{ + writeq(val, nn->ctrl_bar + off); +} + +/* Flush posted PCI writes by reading something without side effects */ +static inline void nn_pci_flush(struct nfp_net *nn) +{ + nn_readl(nn, NFP_NET_CFG_VERSION); +} + +/* Queue Controller Peripheral access functions and definitions. + * + * Some of the BARs of the NFP are mapped to portions of the Queue + * Controller Peripheral (QCP) address space on the NFP. A QCP queue + * has a read and a write pointer (as well as a size and flags, + * indicating overflow etc). The QCP offers a number of different + * operation on queue pointers, but here we only offer function to + * either add to a pointer or to read the pointer value. + */ +#define NFP_QCP_QUEUE_ADDR_SZ 0x800 +#define NFP_QCP_QUEUE_OFF(_x) ((_x) * NFP_QCP_QUEUE_ADDR_SZ) +#define NFP_QCP_QUEUE_ADD_RPTR 0x0000 +#define NFP_QCP_QUEUE_ADD_WPTR 0x0004 +#define NFP_QCP_QUEUE_STS_LO 0x0008 +#define NFP_QCP_QUEUE_STS_LO_READPTR_mask 0x3ffff +#define NFP_QCP_QUEUE_STS_HI 0x000c +#define NFP_QCP_QUEUE_STS_HI_WRITEPTR_mask 0x3ffff + +/* The offset of a QCP queues in the PCIe Target (same on NFP3200 and NFP6000 */ +#define NFP_PCIE_QUEUE(_q) (0x80000 + (NFP_QCP_QUEUE_ADDR_SZ * ((_q) & 0xff))) + +/* nfp_qcp_ptr - Read or Write Pointer of a queue */ +enum nfp_qcp_ptr { + NFP_QCP_READ_PTR = 0, + NFP_QCP_WRITE_PTR +}; + +/* There appear to be an *undocumented* upper limit on the value which + * one can add to a queue and that value is either 0x3f or 0x7f. We + * go with 0x3f as a conservative measure. + */ +#define NFP_QCP_MAX_ADD 0x3f + +static inline void _nfp_qcp_ptr_add(u8 __iomem *q, + enum nfp_qcp_ptr ptr, u32 val) +{ + u32 off; + + if (ptr == NFP_QCP_READ_PTR) + off = NFP_QCP_QUEUE_ADD_RPTR; + else + off = NFP_QCP_QUEUE_ADD_WPTR; + + while (val > NFP_QCP_MAX_ADD) { + writel(NFP_QCP_MAX_ADD, q + off); + val -= NFP_QCP_MAX_ADD; + } + + writel(val, q + off); +} + +/** + * nfp_qcp_rd_ptr_add() - Add the value to the read pointer of a queue + * + * @q: Base address for queue structure + * @val: Value to add to the queue pointer + * + * If @val is greater than @NFP_QCP_MAX_ADD multiple writes are performed. + */ +static inline void nfp_qcp_rd_ptr_add(u8 __iomem *q, u32 val) +{ + _nfp_qcp_ptr_add(q, NFP_QCP_READ_PTR, val); +} + +/** + * nfp_qcp_wr_ptr_add() - Add the value to the write pointer of a queue + * + * @q: Base address for queue structure + * @val: Value to add to the queue pointer + * + * If @val is greater than @NFP_QCP_MAX_ADD multiple writes are performed. + */ +static inline void nfp_qcp_wr_ptr_add(u8 __iomem *q, u32 val) +{ + _nfp_qcp_ptr_add(q, NFP_QCP_WRITE_PTR, val); +} + +static inline u32 _nfp_qcp_read(u8 __iomem *q, enum nfp_qcp_ptr ptr) +{ + u32 off; + u32 val; + + if (ptr == NFP_QCP_READ_PTR) + off = NFP_QCP_QUEUE_STS_LO; + else + off = NFP_QCP_QUEUE_STS_HI; + + val = readl(q + off); + + if (ptr == NFP_QCP_READ_PTR) + return val & NFP_QCP_QUEUE_STS_LO_READPTR_mask; + else + return val & NFP_QCP_QUEUE_STS_HI_WRITEPTR_mask; +} + +/** + * nfp_qcp_rd_ptr_read() - Read the current read pointer value for a queue + * @q: Base address for queue structure + * + * Return: Value read. + */ +static inline u32 nfp_qcp_rd_ptr_read(u8 __iomem *q) +{ + return _nfp_qcp_read(q, NFP_QCP_READ_PTR); +} + +/** + * nfp_qcp_wr_ptr_read() - Read the current write pointer value for a queue + * @q: Base address for queue structure + * + * Return: Value read. + */ +static inline u32 nfp_qcp_wr_ptr_read(u8 __iomem *q) +{ + return _nfp_qcp_read(q, NFP_QCP_WRITE_PTR); +} + +/* Globals */ +extern const char nfp_net_driver_name[]; +extern const char nfp_net_driver_version[]; + +/* Prototypes */ +void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver, + void __iomem *ctrl_bar); + +struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev, + int max_tx_rings, int max_rx_rings); +void nfp_net_netdev_free(struct nfp_net *nn); +int nfp_net_netdev_init(struct net_device *netdev); +void nfp_net_netdev_clean(struct net_device *netdev); +void nfp_net_set_ethtool_ops(struct net_device *netdev); +void nfp_net_info(struct nfp_net *nn); +int nfp_net_reconfig(struct nfp_net *nn, u32 update); +void nfp_net_rss_write_itbl(struct nfp_net *nn); +void nfp_net_rss_write_key(struct nfp_net *nn); +void nfp_net_coalesce_write_cfg(struct nfp_net *nn); +int nfp_net_irqs_alloc(struct nfp_net *nn); +void nfp_net_irqs_disable(struct nfp_net *nn); + +#ifdef CONFIG_NFP_NET_DEBUG +void nfp_net_debugfs_create(void); +void nfp_net_debugfs_destroy(void); +void nfp_net_debugfs_adapter_add(struct nfp_net *nn); +void nfp_net_debugfs_adapter_del(struct nfp_net *nn); +#else +static inline void nfp_net_debugfs_create(void) +{ +} + +static inline void nfp_net_debugfs_destroy(void) +{ +} + +static inline void nfp_net_debugfs_adapter_add(struct nfp_net *nn) +{ +} + +static inline void nfp_net_debugfs_adapter_del(struct nfp_net *nn) +{ +} +#endif /* CONFIG_NFP_NET_DEBUG */ + +#endif /* _NFP_NET_H_ */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c new file mode 100644 index 000000000000..038ac6b14a60 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -0,0 +1,2432 @@ +/* + * Copyright (C) 2015 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * nfp_net_common.c + * Netronome network device driver: Common functions between PF and VF + * Authors: Jakub Kicinski + * Jason McMullan + * Rolf Neugebauer + * Brad Petrus + * Chris Telfer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "nfp_net_ctrl.h" +#include "nfp_net.h" + +/** + * nfp_net_get_fw_version() - Read and parse the FW version + * @fw_ver: Output fw_version structure to read to + * @ctrl_bar: Mapped address of the control BAR + */ +void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver, + void __iomem *ctrl_bar) +{ + u32 reg; + + reg = readl(ctrl_bar + NFP_NET_CFG_VERSION); + put_unaligned_le32(reg, fw_ver); +} + +/** + * nfp_net_reconfig() - Reconfigure the firmware + * @nn: NFP Net device to reconfigure + * @update: The value for the update field in the BAR config + * + * Write the update word to the BAR and ping the reconfig queue. The + * poll until the firmware has acknowledged the update by zeroing the + * update word. + * + * Return: Negative errno on error, 0 on success + */ +int nfp_net_reconfig(struct nfp_net *nn, u32 update) +{ + int cnt, ret = 0; + u32 new; + + spin_lock_bh(&nn->reconfig_lock); + + nn_writel(nn, NFP_NET_CFG_UPDATE, update); + /* ensure update is written before pinging HW */ + nn_pci_flush(nn); + nfp_qcp_wr_ptr_add(nn->qcp_cfg, 1); + + /* Poll update field, waiting for NFP to ack the config */ + for (cnt = 0; ; cnt++) { + new = nn_readl(nn, NFP_NET_CFG_UPDATE); + if (new == 0) + break; + if (new & NFP_NET_CFG_UPDATE_ERR) { + nn_err(nn, "Reconfig error: 0x%08x\n", new); + ret = -EIO; + break; + } else if (cnt >= NFP_NET_POLL_TIMEOUT) { + nn_err(nn, "Reconfig timeout for 0x%08x after %dms\n", + update, cnt); + ret = -EIO; + break; + } + mdelay(1); + } + + spin_unlock_bh(&nn->reconfig_lock); + return ret; +} + +/* Interrupt configuration and handling + */ + +/** + * nfp_net_irq_unmask_msix() - Unmask MSI-X after automasking + * @nn: NFP Network structure + * @entry_nr: MSI-X table entry + * + * Clear the MSI-X table mask bit for the given entry bypassing Linux irq + * handling subsystem. Use *only* to reenable automasked vectors. + */ +static void nfp_net_irq_unmask_msix(struct nfp_net *nn, unsigned int entry_nr) +{ + struct list_head *msi_head = &nn->pdev->dev.msi_list; + struct msi_desc *entry; + u32 off; + + /* All MSI-Xs have the same mask_base */ + entry = list_first_entry(msi_head, struct msi_desc, list); + + off = (PCI_MSIX_ENTRY_SIZE * entry_nr) + + PCI_MSIX_ENTRY_VECTOR_CTRL; + writel(0, entry->mask_base + off); + readl(entry->mask_base); +} + +/** + * nfp_net_irq_unmask() - Unmask automasked interrupt + * @nn: NFP Network structure + * @entry_nr: MSI-X table entry + * + * If MSI-X auto-masking is enabled clear the mask bit, otherwise + * clear the ICR for the entry. + */ +static void nfp_net_irq_unmask(struct nfp_net *nn, unsigned int entry_nr) +{ + if (nn->ctrl & NFP_NET_CFG_CTRL_MSIXAUTO) { + nfp_net_irq_unmask_msix(nn, entry_nr); + return; + } + + nn_writeb(nn, NFP_NET_CFG_ICR(entry_nr), NFP_NET_CFG_ICR_UNMASKED); + nn_pci_flush(nn); +} + +/** + * nfp_net_msix_alloc() - Try to allocate MSI-X irqs + * @nn: NFP Network structure + * @nr_vecs: Number of MSI-X vectors to allocate + * + * For MSI-X we want at least NFP_NET_NON_Q_VECTORS + 1 vectors. + * + * Return: Number of MSI-X vectors obtained or 0 on error. + */ +static int nfp_net_msix_alloc(struct nfp_net *nn, int nr_vecs) +{ + struct pci_dev *pdev = nn->pdev; + int nvecs; + int i; + + for (i = 0; i < nr_vecs; i++) + nn->irq_entries[i].entry = i; + + nvecs = pci_enable_msix_range(pdev, nn->irq_entries, + NFP_NET_NON_Q_VECTORS + 1, nr_vecs); + if (nvecs < 0) { + nn_warn(nn, "Failed to enable MSI-X. Wanted %d-%d (err=%d)\n", + NFP_NET_NON_Q_VECTORS + 1, nr_vecs, nvecs); + return 0; + } + + return nvecs; +} + +/** + * nfp_net_irqs_wanted() - Work out how many interrupt vectors we want + * @nn: NFP Network structure + * + * We want a vector per CPU (or ring), whatever is smaller plus + * NFP_NET_NON_Q_VECTORS for LSC etc. + * + * Return: Number of interrupts wanted + */ +static int nfp_net_irqs_wanted(struct nfp_net *nn) +{ + int ncpus; + int vecs; + + ncpus = num_online_cpus(); + + vecs = max_t(int, nn->num_tx_rings, nn->num_rx_rings); + vecs = min_t(int, vecs, ncpus); + + return vecs + NFP_NET_NON_Q_VECTORS; +} + +/** + * nfp_net_irqs_alloc() - allocates MSI-X irqs + * @nn: NFP Network structure + * + * Return: Number of irqs obtained or 0 on error. + */ +int nfp_net_irqs_alloc(struct nfp_net *nn) +{ + int wanted_irqs; + + wanted_irqs = nfp_net_irqs_wanted(nn); + + nn->num_irqs = nfp_net_msix_alloc(nn, wanted_irqs); + if (nn->num_irqs == 0) { + nn_err(nn, "Failed to allocate MSI-X IRQs\n"); + return 0; + } + + nn->num_r_vecs = nn->num_irqs - NFP_NET_NON_Q_VECTORS; + + if (nn->num_irqs < wanted_irqs) + nn_warn(nn, "Unable to allocate %d vectors. Got %d instead\n", + wanted_irqs, nn->num_irqs); + + return nn->num_irqs; +} + +/** + * nfp_net_irqs_disable() - Disable interrupts + * @nn: NFP Network structure + * + * Undoes what @nfp_net_irqs_alloc() does. + */ +void nfp_net_irqs_disable(struct nfp_net *nn) +{ + pci_disable_msix(nn->pdev); +} + +/** + * nfp_net_irq_rxtx() - Interrupt service routine for RX/TX rings. + * @irq: Interrupt + * @data: Opaque data structure + * + * Return: Indicate if the interrupt has been handled. + */ +static irqreturn_t nfp_net_irq_rxtx(int irq, void *data) +{ + struct nfp_net_r_vector *r_vec = data; + + napi_schedule_irqoff(&r_vec->napi); + + /* The FW auto-masks any interrupt, either via the MASK bit in + * the MSI-X table or via the per entry ICR field. So there + * is no need to disable interrupts here. + */ + return IRQ_HANDLED; +} + +/** + * nfp_net_read_link_status() - Reread link status from control BAR + * @nn: NFP Network structure + */ +static void nfp_net_read_link_status(struct nfp_net *nn) +{ + unsigned long flags; + bool link_up; + u32 sts; + + spin_lock_irqsave(&nn->link_status_lock, flags); + + sts = nn_readl(nn, NFP_NET_CFG_STS); + link_up = !!(sts & NFP_NET_CFG_STS_LINK); + + if (nn->link_up == link_up) + goto out; + + nn->link_up = link_up; + + if (nn->link_up) { + netif_carrier_on(nn->netdev); + netdev_info(nn->netdev, "NIC Link is Up\n"); + } else { + netif_carrier_off(nn->netdev); + netdev_info(nn->netdev, "NIC Link is Down\n"); + } +out: + spin_unlock_irqrestore(&nn->link_status_lock, flags); +} + +/** + * nfp_net_irq_lsc() - Interrupt service routine for link state changes + * @irq: Interrupt + * @data: Opaque data structure + * + * Return: Indicate if the interrupt has been handled. + */ +static irqreturn_t nfp_net_irq_lsc(int irq, void *data) +{ + struct nfp_net *nn = data; + + nfp_net_read_link_status(nn); + + nfp_net_irq_unmask(nn, NFP_NET_IRQ_LSC_IDX); + + return IRQ_HANDLED; +} + +/** + * nfp_net_irq_exn() - Interrupt service routine for exceptions + * @irq: Interrupt + * @data: Opaque data structure + * + * Return: Indicate if the interrupt has been handled. + */ +static irqreturn_t nfp_net_irq_exn(int irq, void *data) +{ + struct nfp_net *nn = data; + + nn_err(nn, "%s: UNIMPLEMENTED.\n", __func__); + /* XXX TO BE IMPLEMENTED */ + return IRQ_HANDLED; +} + +/** + * nfp_net_tx_ring_init() - Fill in the boilerplate for a TX ring + * @tx_ring: TX ring structure + */ +static void nfp_net_tx_ring_init(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net *nn = r_vec->nfp_net; + + tx_ring->qcidx = tx_ring->idx * nn->stride_tx; + tx_ring->qcp_q = nn->tx_bar + NFP_QCP_QUEUE_OFF(tx_ring->qcidx); +} + +/** + * nfp_net_rx_ring_init() - Fill in the boilerplate for a RX ring + * @rx_ring: RX ring structure + */ +static void nfp_net_rx_ring_init(struct nfp_net_rx_ring *rx_ring) +{ + struct nfp_net_r_vector *r_vec = rx_ring->r_vec; + struct nfp_net *nn = r_vec->nfp_net; + + rx_ring->fl_qcidx = rx_ring->idx * nn->stride_rx; + rx_ring->rx_qcidx = rx_ring->fl_qcidx + (nn->stride_rx - 1); + + rx_ring->qcp_fl = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->fl_qcidx); + rx_ring->qcp_rx = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->rx_qcidx); +} + +/** + * nfp_net_irqs_assign() - Assign IRQs and setup rvecs. + * @netdev: netdev structure + */ +static void nfp_net_irqs_assign(struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + struct nfp_net_r_vector *r_vec; + int r; + + /* Assumes nn->num_tx_rings == nn->num_rx_rings */ + if (nn->num_tx_rings > nn->num_r_vecs) { + nn_warn(nn, "More rings (%d) than vectors (%d).\n", + nn->num_tx_rings, nn->num_r_vecs); + nn->num_tx_rings = nn->num_r_vecs; + nn->num_rx_rings = nn->num_r_vecs; + } + + nn->lsc_handler = nfp_net_irq_lsc; + nn->exn_handler = nfp_net_irq_exn; + + for (r = 0; r < nn->num_r_vecs; r++) { + r_vec = &nn->r_vecs[r]; + r_vec->nfp_net = nn; + r_vec->handler = nfp_net_irq_rxtx; + r_vec->irq_idx = NFP_NET_NON_Q_VECTORS + r; + + cpumask_set_cpu(r, &r_vec->affinity_mask); + + r_vec->tx_ring = &nn->tx_rings[r]; + nn->tx_rings[r].idx = r; + nn->tx_rings[r].r_vec = r_vec; + nfp_net_tx_ring_init(r_vec->tx_ring); + + r_vec->rx_ring = &nn->rx_rings[r]; + nn->rx_rings[r].idx = r; + nn->rx_rings[r].r_vec = r_vec; + nfp_net_rx_ring_init(r_vec->rx_ring); + } +} + +/** + * nfp_net_aux_irq_request() - Request an auxiliary interrupt (LSC or EXN) + * @nn: NFP Network structure + * @ctrl_offset: Control BAR offset where IRQ configuration should be written + * @format: printf-style format to construct the interrupt name + * @name: Pointer to allocated space for interrupt name + * @name_sz: Size of space for interrupt name + * @vector_idx: Index of MSI-X vector used for this interrupt + * @handler: IRQ handler to register for this interrupt + */ +static int +nfp_net_aux_irq_request(struct nfp_net *nn, u32 ctrl_offset, + const char *format, char *name, size_t name_sz, + unsigned int vector_idx, irq_handler_t handler) +{ + struct msix_entry *entry; + int err; + + entry = &nn->irq_entries[vector_idx]; + + snprintf(name, name_sz, format, netdev_name(nn->netdev)); + err = request_irq(entry->vector, handler, 0, name, nn); + if (err) { + nn_err(nn, "Failed to request IRQ %d (err=%d).\n", + entry->vector, err); + return err; + } + nn_writeb(nn, ctrl_offset, vector_idx); + + return 0; +} + +/** + * nfp_net_aux_irq_free() - Free an auxiliary interrupt (LSC or EXN) + * @nn: NFP Network structure + * @ctrl_offset: Control BAR offset where IRQ configuration should be written + * @vector_idx: Index of MSI-X vector used for this interrupt + */ +static void nfp_net_aux_irq_free(struct nfp_net *nn, u32 ctrl_offset, + unsigned int vector_idx) +{ + nn_writeb(nn, ctrl_offset, 0xff); + free_irq(nn->irq_entries[vector_idx].vector, nn); +} + +/* Transmit + * + * One queue controller peripheral queue is used for transmit. The + * driver en-queues packets for transmit by advancing the write + * pointer. The device indicates that packets have transmitted by + * advancing the read pointer. The driver maintains a local copy of + * the read and write pointer in @struct nfp_net_tx_ring. The driver + * keeps @wr_p in sync with the queue controller write pointer and can + * determine how many packets have been transmitted by comparing its + * copy of the read pointer @rd_p with the read pointer maintained by + * the queue controller peripheral. + */ + +/** + * nfp_net_tx_full() - Check if the TX ring is full + * @tx_ring: TX ring to check + * @dcnt: Number of descriptors that need to be enqueued (must be >= 1) + * + * This function checks, based on the *host copy* of read/write + * pointer if a given TX ring is full. The real TX queue may have + * some newly made available slots. + * + * Return: True if the ring is full. + */ +static inline int nfp_net_tx_full(struct nfp_net_tx_ring *tx_ring, int dcnt) +{ + return (tx_ring->wr_p - tx_ring->rd_p) >= (tx_ring->cnt - dcnt); +} + +/* Wrappers for deciding when to stop and restart TX queues */ +static int nfp_net_tx_ring_should_wake(struct nfp_net_tx_ring *tx_ring) +{ + return !nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS * 4); +} + +static int nfp_net_tx_ring_should_stop(struct nfp_net_tx_ring *tx_ring) +{ + return nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS + 1); +} + +/** + * nfp_net_tx_ring_stop() - stop tx ring + * @nd_q: netdev queue + * @tx_ring: driver tx queue structure + * + * Safely stop TX ring. Remember that while we are running .start_xmit() + * someone else may be cleaning the TX ring completions so we need to be + * extra careful here. + */ +static void nfp_net_tx_ring_stop(struct netdev_queue *nd_q, + struct nfp_net_tx_ring *tx_ring) +{ + netif_tx_stop_queue(nd_q); + + /* We can race with the TX completion out of NAPI so recheck */ + smp_mb(); + if (unlikely(nfp_net_tx_ring_should_wake(tx_ring))) + netif_tx_start_queue(nd_q); +} + +/** + * nfp_net_tx_tso() - Set up Tx descriptor for LSO + * @nn: NFP Net device + * @r_vec: per-ring structure + * @txbuf: Pointer to driver soft TX descriptor + * @txd: Pointer to HW TX descriptor + * @skb: Pointer to SKB + * + * Set up Tx descriptor for LSO, do nothing for non-LSO skbs. + * Return error on packet header greater than maximum supported LSO header size. + */ +static void nfp_net_tx_tso(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, + struct nfp_net_tx_buf *txbuf, + struct nfp_net_tx_desc *txd, struct sk_buff *skb) +{ + u32 hdrlen; + u16 mss; + + if (!skb_is_gso(skb)) + return; + + if (!skb->encapsulation) + hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb); + else + hdrlen = skb_inner_transport_header(skb) - skb->data + + inner_tcp_hdrlen(skb); + + txbuf->pkt_cnt = skb_shinfo(skb)->gso_segs; + txbuf->real_len += hdrlen * (txbuf->pkt_cnt - 1); + + mss = skb_shinfo(skb)->gso_size & PCIE_DESC_TX_MSS_MASK; + txd->l4_offset = hdrlen; + txd->mss = cpu_to_le16(mss); + txd->flags |= PCIE_DESC_TX_LSO; + + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_lso++; + u64_stats_update_end(&r_vec->tx_sync); +} + +/** + * nfp_net_tx_csum() - Set TX CSUM offload flags in TX descriptor + * @nn: NFP Net device + * @r_vec: per-ring structure + * @txbuf: Pointer to driver soft TX descriptor + * @txd: Pointer to TX descriptor + * @skb: Pointer to SKB + * + * This function sets the TX checksum flags in the TX descriptor based + * on the configuration and the protocol of the packet to be transmitted. + */ +static void nfp_net_tx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, + struct nfp_net_tx_buf *txbuf, + struct nfp_net_tx_desc *txd, struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h; + struct iphdr *iph; + u8 l4_hdr; + + if (!(nn->ctrl & NFP_NET_CFG_CTRL_TXCSUM)) + return; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return; + + txd->flags |= PCIE_DESC_TX_CSUM; + if (skb->encapsulation) + txd->flags |= PCIE_DESC_TX_ENCAP; + + iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); + ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb); + + if (iph->version == 4) { + txd->flags |= PCIE_DESC_TX_IP4_CSUM; + l4_hdr = iph->protocol; + } else if (ipv6h->version == 6) { + l4_hdr = ipv6h->nexthdr; + } else { + nn_warn_ratelimit(nn, "partial checksum but ipv=%x!\n", + iph->version); + return; + } + + switch (l4_hdr) { + case IPPROTO_TCP: + txd->flags |= PCIE_DESC_TX_TCP_CSUM; + break; + case IPPROTO_UDP: + txd->flags |= PCIE_DESC_TX_UDP_CSUM; + break; + default: + nn_warn_ratelimit(nn, "partial checksum but l4 proto=%x!\n", + l4_hdr); + return; + } + + u64_stats_update_begin(&r_vec->tx_sync); + if (skb->encapsulation) + r_vec->hw_csum_tx_inner += txbuf->pkt_cnt; + else + r_vec->hw_csum_tx += txbuf->pkt_cnt; + u64_stats_update_end(&r_vec->tx_sync); +} + +/** + * nfp_net_tx() - Main transmit entry point + * @skb: SKB to transmit + * @netdev: netdev structure + * + * Return: NETDEV_TX_OK on success. + */ +static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + const struct skb_frag_struct *frag; + struct nfp_net_r_vector *r_vec; + struct nfp_net_tx_desc *txd, txdg; + struct nfp_net_tx_buf *txbuf; + struct nfp_net_tx_ring *tx_ring; + struct netdev_queue *nd_q; + dma_addr_t dma_addr; + unsigned int fsize; + int f, nr_frags; + int wr_idx; + u16 qidx; + + qidx = skb_get_queue_mapping(skb); + tx_ring = &nn->tx_rings[qidx]; + r_vec = tx_ring->r_vec; + nd_q = netdev_get_tx_queue(nn->netdev, qidx); + + nr_frags = skb_shinfo(skb)->nr_frags; + + if (unlikely(nfp_net_tx_full(tx_ring, nr_frags + 1))) { + nn_warn_ratelimit(nn, "TX ring %d busy. wrp=%u rdp=%u\n", + qidx, tx_ring->wr_p, tx_ring->rd_p); + netif_tx_stop_queue(nd_q); + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_busy++; + u64_stats_update_end(&r_vec->tx_sync); + return NETDEV_TX_BUSY; + } + + /* Start with the head skbuf */ + dma_addr = dma_map_single(&nn->pdev->dev, skb->data, skb_headlen(skb), + DMA_TO_DEVICE); + if (dma_mapping_error(&nn->pdev->dev, dma_addr)) + goto err_free; + + wr_idx = tx_ring->wr_p % tx_ring->cnt; + + /* Stash the soft descriptor of the head then initialize it */ + txbuf = &tx_ring->txbufs[wr_idx]; + txbuf->skb = skb; + txbuf->dma_addr = dma_addr; + txbuf->fidx = -1; + txbuf->pkt_cnt = 1; + txbuf->real_len = skb->len; + + /* Build TX descriptor */ + txd = &tx_ring->txds[wr_idx]; + txd->offset_eop = (nr_frags == 0) ? PCIE_DESC_TX_EOP : 0; + txd->dma_len = cpu_to_le16(skb_headlen(skb)); + nfp_desc_set_dma_addr(txd, dma_addr); + txd->data_len = cpu_to_le16(skb->len); + + txd->flags = 0; + txd->mss = 0; + txd->l4_offset = 0; + + nfp_net_tx_tso(nn, r_vec, txbuf, txd, skb); + + nfp_net_tx_csum(nn, r_vec, txbuf, txd, skb); + + if (skb_vlan_tag_present(skb) && nn->ctrl & NFP_NET_CFG_CTRL_TXVLAN) { + txd->flags |= PCIE_DESC_TX_VLAN; + txd->vlan = cpu_to_le16(skb_vlan_tag_get(skb)); + } + + /* Gather DMA */ + if (nr_frags > 0) { + /* all descs must match except for in addr, length and eop */ + txdg = *txd; + + for (f = 0; f < nr_frags; f++) { + frag = &skb_shinfo(skb)->frags[f]; + fsize = skb_frag_size(frag); + + dma_addr = skb_frag_dma_map(&nn->pdev->dev, frag, 0, + fsize, DMA_TO_DEVICE); + if (dma_mapping_error(&nn->pdev->dev, dma_addr)) + goto err_unmap; + + wr_idx = (wr_idx + 1) % tx_ring->cnt; + tx_ring->txbufs[wr_idx].skb = skb; + tx_ring->txbufs[wr_idx].dma_addr = dma_addr; + tx_ring->txbufs[wr_idx].fidx = f; + + txd = &tx_ring->txds[wr_idx]; + *txd = txdg; + txd->dma_len = cpu_to_le16(fsize); + nfp_desc_set_dma_addr(txd, dma_addr); + txd->offset_eop = + (f == nr_frags - 1) ? PCIE_DESC_TX_EOP : 0; + } + + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_gather++; + u64_stats_update_end(&r_vec->tx_sync); + } + + netdev_tx_sent_queue(nd_q, txbuf->real_len); + + tx_ring->wr_p += nr_frags + 1; + if (nfp_net_tx_ring_should_stop(tx_ring)) + nfp_net_tx_ring_stop(nd_q, tx_ring); + + tx_ring->wr_ptr_add += nr_frags + 1; + if (!skb->xmit_more || netif_xmit_stopped(nd_q)) { + /* force memory write before we let HW know */ + wmb(); + nfp_qcp_wr_ptr_add(tx_ring->qcp_q, tx_ring->wr_ptr_add); + tx_ring->wr_ptr_add = 0; + } + + skb_tx_timestamp(skb); + + return NETDEV_TX_OK; + +err_unmap: + --f; + while (f >= 0) { + frag = &skb_shinfo(skb)->frags[f]; + dma_unmap_page(&nn->pdev->dev, + tx_ring->txbufs[wr_idx].dma_addr, + skb_frag_size(frag), DMA_TO_DEVICE); + tx_ring->txbufs[wr_idx].skb = NULL; + tx_ring->txbufs[wr_idx].dma_addr = 0; + tx_ring->txbufs[wr_idx].fidx = -2; + wr_idx = wr_idx - 1; + if (wr_idx < 0) + wr_idx += tx_ring->cnt; + } + dma_unmap_single(&nn->pdev->dev, tx_ring->txbufs[wr_idx].dma_addr, + skb_headlen(skb), DMA_TO_DEVICE); + tx_ring->txbufs[wr_idx].skb = NULL; + tx_ring->txbufs[wr_idx].dma_addr = 0; + tx_ring->txbufs[wr_idx].fidx = -2; +err_free: + nn_warn_ratelimit(nn, "Failed to map DMA TX buffer\n"); + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_errors++; + u64_stats_update_end(&r_vec->tx_sync); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +/** + * nfp_net_tx_complete() - Handled completed TX packets + * @tx_ring: TX ring structure + * + * Return: Number of completed TX descriptors + */ +static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net *nn = r_vec->nfp_net; + const struct skb_frag_struct *frag; + struct netdev_queue *nd_q; + u32 done_pkts = 0, done_bytes = 0; + struct sk_buff *skb; + int todo, nr_frags; + u32 qcp_rd_p; + int fidx; + int idx; + + /* Work out how many descriptors have been transmitted */ + qcp_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q); + + if (qcp_rd_p == tx_ring->qcp_rd_p) + return; + + if (qcp_rd_p > tx_ring->qcp_rd_p) + todo = qcp_rd_p - tx_ring->qcp_rd_p; + else + todo = qcp_rd_p + tx_ring->cnt - tx_ring->qcp_rd_p; + + while (todo--) { + idx = tx_ring->rd_p % tx_ring->cnt; + tx_ring->rd_p++; + + skb = tx_ring->txbufs[idx].skb; + if (!skb) + continue; + + nr_frags = skb_shinfo(skb)->nr_frags; + fidx = tx_ring->txbufs[idx].fidx; + + if (fidx == -1) { + /* unmap head */ + dma_unmap_single(&nn->pdev->dev, + tx_ring->txbufs[idx].dma_addr, + skb_headlen(skb), DMA_TO_DEVICE); + + done_pkts += tx_ring->txbufs[idx].pkt_cnt; + done_bytes += tx_ring->txbufs[idx].real_len; + } else { + /* unmap fragment */ + frag = &skb_shinfo(skb)->frags[fidx]; + dma_unmap_page(&nn->pdev->dev, + tx_ring->txbufs[idx].dma_addr, + skb_frag_size(frag), DMA_TO_DEVICE); + } + + /* check for last gather fragment */ + if (fidx == nr_frags - 1) + dev_kfree_skb_any(skb); + + tx_ring->txbufs[idx].dma_addr = 0; + tx_ring->txbufs[idx].skb = NULL; + tx_ring->txbufs[idx].fidx = -2; + } + + tx_ring->qcp_rd_p = qcp_rd_p; + + u64_stats_update_begin(&r_vec->tx_sync); + r_vec->tx_bytes += done_bytes; + r_vec->tx_pkts += done_pkts; + u64_stats_update_end(&r_vec->tx_sync); + + nd_q = netdev_get_tx_queue(nn->netdev, tx_ring->idx); + netdev_tx_completed_queue(nd_q, done_pkts, done_bytes); + if (nfp_net_tx_ring_should_wake(tx_ring)) { + /* Make sure TX thread will see updated tx_ring->rd_p */ + smp_mb(); + + if (unlikely(netif_tx_queue_stopped(nd_q))) + netif_tx_wake_queue(nd_q); + } + + WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt, + "TX ring corruption rd_p=%u wr_p=%u cnt=%u\n", + tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt); +} + +/** + * nfp_net_tx_flush() - Free any untransmitted buffers currently on the TX ring + * @tx_ring: TX ring structure + * + * Assumes that the device is stopped + */ +static void nfp_net_tx_flush(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net *nn = r_vec->nfp_net; + struct pci_dev *pdev = nn->pdev; + const struct skb_frag_struct *frag; + struct netdev_queue *nd_q; + struct sk_buff *skb; + int nr_frags; + int fidx; + int idx; + + while (tx_ring->rd_p != tx_ring->wr_p) { + idx = tx_ring->rd_p % tx_ring->cnt; + + skb = tx_ring->txbufs[idx].skb; + if (skb) { + nr_frags = skb_shinfo(skb)->nr_frags; + fidx = tx_ring->txbufs[idx].fidx; + + if (fidx == -1) { + /* unmap head */ + dma_unmap_single(&pdev->dev, + tx_ring->txbufs[idx].dma_addr, + skb_headlen(skb), + DMA_TO_DEVICE); + } else { + /* unmap fragment */ + frag = &skb_shinfo(skb)->frags[fidx]; + dma_unmap_page(&pdev->dev, + tx_ring->txbufs[idx].dma_addr, + skb_frag_size(frag), + DMA_TO_DEVICE); + } + + /* check for last gather fragment */ + if (fidx == nr_frags - 1) + dev_kfree_skb_any(skb); + + tx_ring->txbufs[idx].dma_addr = 0; + tx_ring->txbufs[idx].skb = NULL; + tx_ring->txbufs[idx].fidx = -2; + } + + memset(&tx_ring->txds[idx], 0, sizeof(tx_ring->txds[idx])); + + tx_ring->qcp_rd_p++; + tx_ring->rd_p++; + } + + nd_q = netdev_get_tx_queue(nn->netdev, tx_ring->idx); + netdev_tx_reset_queue(nd_q); +} + +static void nfp_net_tx_timeout(struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + int i; + + for (i = 0; i < nn->num_tx_rings; i++) { + if (!netif_tx_queue_stopped(netdev_get_tx_queue(netdev, i))) + continue; + nn_warn(nn, "TX timeout on ring: %d\n", i); + } + nn_warn(nn, "TX watchdog timeout\n"); +} + +/* Receive processing + */ + +/** + * nfp_net_rx_space() - return the number of free slots on the RX ring + * @rx_ring: RX ring structure + * + * Make sure we leave at least one slot free. + * + * Return: True if there is space on the RX ring + */ +static inline int nfp_net_rx_space(struct nfp_net_rx_ring *rx_ring) +{ + return (rx_ring->cnt - 1) - (rx_ring->wr_p - rx_ring->rd_p); +} + +/** + * nfp_net_rx_alloc_one() - Allocate and map skb for RX + * @rx_ring: RX ring structure of the skb + * @dma_addr: Pointer to storage for DMA address (output param) + * + * This function will allcate a new skb, map it for DMA. + * + * Return: allocated skb or NULL on failure. + */ +static struct sk_buff * +nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr) +{ + struct nfp_net *nn = rx_ring->r_vec->nfp_net; + struct sk_buff *skb; + + skb = netdev_alloc_skb(nn->netdev, nn->fl_bufsz); + if (!skb) { + nn_warn_ratelimit(nn, "Failed to alloc receive SKB\n"); + return NULL; + } + + *dma_addr = dma_map_single(&nn->pdev->dev, skb->data, + nn->fl_bufsz, DMA_FROM_DEVICE); + if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) { + dev_kfree_skb_any(skb); + nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n"); + return NULL; + } + + return skb; +} + +/** + * nfp_net_rx_give_one() - Put mapped skb on the software and hardware rings + * @rx_ring: RX ring structure + * @skb: Skb to put on rings + * @dma_addr: DMA address of skb mapping + */ +static void nfp_net_rx_give_one(struct nfp_net_rx_ring *rx_ring, + struct sk_buff *skb, dma_addr_t dma_addr) +{ + unsigned int wr_idx; + + wr_idx = rx_ring->wr_p % rx_ring->cnt; + + /* Stash SKB and DMA address away */ + rx_ring->rxbufs[wr_idx].skb = skb; + rx_ring->rxbufs[wr_idx].dma_addr = dma_addr; + + /* Fill freelist descriptor */ + rx_ring->rxds[wr_idx].fld.reserved = 0; + rx_ring->rxds[wr_idx].fld.meta_len_dd = 0; + nfp_desc_set_dma_addr(&rx_ring->rxds[wr_idx].fld, dma_addr); + + rx_ring->wr_p++; + rx_ring->wr_ptr_add++; + if (rx_ring->wr_ptr_add >= NFP_NET_FL_BATCH) { + /* Update write pointer of the freelist queue. Make + * sure all writes are flushed before telling the hardware. + */ + wmb(); + nfp_qcp_wr_ptr_add(rx_ring->qcp_fl, rx_ring->wr_ptr_add); + rx_ring->wr_ptr_add = 0; + } +} + +/** + * nfp_net_rx_flush() - Free any buffers currently on the RX ring + * @rx_ring: RX ring to remove buffers from + * + * Assumes that the device is stopped + */ +static void nfp_net_rx_flush(struct nfp_net_rx_ring *rx_ring) +{ + struct nfp_net *nn = rx_ring->r_vec->nfp_net; + struct pci_dev *pdev = nn->pdev; + int idx; + + while (rx_ring->rd_p != rx_ring->wr_p) { + idx = rx_ring->rd_p % rx_ring->cnt; + + if (rx_ring->rxbufs[idx].skb) { + dma_unmap_single(&pdev->dev, + rx_ring->rxbufs[idx].dma_addr, + nn->fl_bufsz, DMA_FROM_DEVICE); + dev_kfree_skb_any(rx_ring->rxbufs[idx].skb); + rx_ring->rxbufs[idx].dma_addr = 0; + rx_ring->rxbufs[idx].skb = NULL; + } + + memset(&rx_ring->rxds[idx], 0, sizeof(rx_ring->rxds[idx])); + + rx_ring->rd_p++; + } +} + +/** + * nfp_net_rx_fill_freelist() - Attempt filling freelist with RX buffers + * @rx_ring: RX ring to fill + * + * Try to fill as many buffers as possible into freelist. Return + * number of buffers added. + * + * Return: Number of freelist buffers added. + */ +static int nfp_net_rx_fill_freelist(struct nfp_net_rx_ring *rx_ring) +{ + struct sk_buff *skb; + dma_addr_t dma_addr; + + while (nfp_net_rx_space(rx_ring)) { + skb = nfp_net_rx_alloc_one(rx_ring, &dma_addr); + if (!skb) { + nfp_net_rx_flush(rx_ring); + return -ENOMEM; + } + nfp_net_rx_give_one(rx_ring, skb, dma_addr); + } + + return 0; +} + +/** + * nfp_net_rx_csum_has_errors() - group check if rxd has any csum errors + * @flags: RX descriptor flags field in CPU byte order + */ +static int nfp_net_rx_csum_has_errors(u16 flags) +{ + u16 csum_all_checked, csum_all_ok; + + csum_all_checked = flags & __PCIE_DESC_RX_CSUM_ALL; + csum_all_ok = flags & __PCIE_DESC_RX_CSUM_ALL_OK; + + return csum_all_checked != (csum_all_ok << PCIE_DESC_RX_CSUM_OK_SHIFT); +} + +/** + * nfp_net_rx_csum() - set SKB checksum field based on RX descriptor flags + * @nn: NFP Net device + * @r_vec: per-ring structure + * @rxd: Pointer to RX descriptor + * @skb: Pointer to SKB + */ +static void nfp_net_rx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, + struct nfp_net_rx_desc *rxd, struct sk_buff *skb) +{ + skb_checksum_none_assert(skb); + + if (!(nn->netdev->features & NETIF_F_RXCSUM)) + return; + + if (nfp_net_rx_csum_has_errors(le16_to_cpu(rxd->rxd.flags))) { + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_csum_rx_error++; + u64_stats_update_end(&r_vec->rx_sync); + return; + } + + /* Assume that the firmware will never report inner CSUM_OK unless outer + * L4 headers were successfully parsed. FW will always report zero UDP + * checksum as CSUM_OK. + */ + if (rxd->rxd.flags & PCIE_DESC_RX_TCP_CSUM_OK || + rxd->rxd.flags & PCIE_DESC_RX_UDP_CSUM_OK) { + __skb_incr_checksum_unnecessary(skb); + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_csum_rx_ok++; + u64_stats_update_end(&r_vec->rx_sync); + } + + if (rxd->rxd.flags & PCIE_DESC_RX_I_TCP_CSUM_OK || + rxd->rxd.flags & PCIE_DESC_RX_I_UDP_CSUM_OK) { + __skb_incr_checksum_unnecessary(skb); + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->hw_csum_rx_inner_ok++; + u64_stats_update_end(&r_vec->rx_sync); + } +} + +/** + * nfp_net_set_hash() - Set SKB hash data + * @netdev: adapter's net_device structure + * @skb: SKB to set the hash data on + * @rxd: RX descriptor + * + * The RSS hash and hash-type are pre-pended to the packet data. + * Extract and decode it and set the skb fields. + */ +static void nfp_net_set_hash(struct net_device *netdev, struct sk_buff *skb, + struct nfp_net_rx_desc *rxd) +{ + struct nfp_net_rx_hash *rx_hash; + + if (!(rxd->rxd.flags & PCIE_DESC_RX_RSS) || + !(netdev->features & NETIF_F_RXHASH)) + return; + + rx_hash = (struct nfp_net_rx_hash *)(skb->data - sizeof(*rx_hash)); + + switch (be32_to_cpu(rx_hash->hash_type)) { + case NFP_NET_RSS_IPV4: + case NFP_NET_RSS_IPV6: + case NFP_NET_RSS_IPV6_EX: + skb_set_hash(skb, be32_to_cpu(rx_hash->hash), PKT_HASH_TYPE_L3); + break; + default: + skb_set_hash(skb, be32_to_cpu(rx_hash->hash), PKT_HASH_TYPE_L4); + break; + } +} + +/** + * nfp_net_rx() - receive up to @budget packets on @rx_ring + * @rx_ring: RX ring to receive from + * @budget: NAPI budget + * + * Note, this function is separated out from the napi poll function to + * more cleanly separate packet receive code from other bookkeeping + * functions performed in the napi poll function. + * + * There are differences between the NFP-3200 firmware and the + * NFP-6000 firmware. The NFP-3200 firmware uses a dedicated RX queue + * to indicate that new packets have arrived. The NFP-6000 does not + * have this queue and uses the DD bit in the RX descriptor. This + * method cannot be used on the NFP-3200 as it causes a race + * condition: The RX ring write pointer on the NFP-3200 is updated + * after packets (and descriptors) have been DMAed. If the DD bit is + * used and subsequently the read pointer is updated this may lead to + * the RX queue to underflow (if the firmware has not yet update the + * write pointer). Therefore we use slightly ugly conditional code + * below to handle the differences. We may, in the future update the + * NFP-3200 firmware to behave the same as the firmware on the + * NFP-6000. + * + * Return: Number of packets received. + */ +static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) +{ + struct nfp_net_r_vector *r_vec = rx_ring->r_vec; + struct nfp_net *nn = r_vec->nfp_net; + unsigned int data_len, meta_len; + int avail = 0, pkts_polled = 0; + struct sk_buff *skb, *new_skb; + struct nfp_net_rx_desc *rxd; + dma_addr_t new_dma_addr; + u32 qcp_wr_p; + int idx; + + if (nn->is_nfp3200) { + /* Work out how many packets arrived */ + qcp_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_rx); + idx = rx_ring->rd_p % rx_ring->cnt; + + if (qcp_wr_p == idx) + /* No new packets */ + return 0; + + if (qcp_wr_p > idx) + avail = qcp_wr_p - idx; + else + avail = qcp_wr_p + rx_ring->cnt - idx; + } else { + avail = budget + 1; + } + + while (avail > 0 && pkts_polled < budget) { + idx = rx_ring->rd_p % rx_ring->cnt; + + rxd = &rx_ring->rxds[idx]; + if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) { + if (nn->is_nfp3200) + nn_dbg(nn, "RX descriptor not valid (DD)%d:%u rxd[0]=%#x rxd[1]=%#x\n", + rx_ring->idx, idx, + rxd->vals[0], rxd->vals[1]); + break; + } + /* Memory barrier to ensure that we won't do other reads + * before the DD bit. + */ + dma_rmb(); + + rx_ring->rd_p++; + pkts_polled++; + avail--; + + skb = rx_ring->rxbufs[idx].skb; + + new_skb = nfp_net_rx_alloc_one(rx_ring, &new_dma_addr); + if (!new_skb) { + nfp_net_rx_give_one(rx_ring, rx_ring->rxbufs[idx].skb, + rx_ring->rxbufs[idx].dma_addr); + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->rx_drops++; + u64_stats_update_end(&r_vec->rx_sync); + continue; + } + + dma_unmap_single(&nn->pdev->dev, + rx_ring->rxbufs[idx].dma_addr, + nn->fl_bufsz, DMA_FROM_DEVICE); + + nfp_net_rx_give_one(rx_ring, new_skb, new_dma_addr); + + meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK; + data_len = le16_to_cpu(rxd->rxd.data_len); + + if (WARN_ON_ONCE(data_len > nn->fl_bufsz)) { + dev_kfree_skb_any(skb); + continue; + } + + if (nn->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) { + /* The packet data starts after the metadata */ + skb_reserve(skb, meta_len); + } else { + /* The packet data starts at a fixed offset */ + skb_reserve(skb, nn->rx_offset); + } + + /* Adjust the SKB for the dynamic meta data pre-pended */ + skb_put(skb, data_len - meta_len); + + nfp_net_set_hash(nn->netdev, skb, rxd); + + /* Pad small frames to minimum */ + if (skb_put_padto(skb, 60)) + break; + + /* Stats update */ + u64_stats_update_begin(&r_vec->rx_sync); + r_vec->rx_pkts++; + r_vec->rx_bytes += skb->len; + u64_stats_update_end(&r_vec->rx_sync); + + skb_record_rx_queue(skb, rx_ring->idx); + skb->protocol = eth_type_trans(skb, nn->netdev); + + nfp_net_rx_csum(nn, r_vec, rxd, skb); + + if (rxd->rxd.flags & PCIE_DESC_RX_VLAN) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + le16_to_cpu(rxd->rxd.vlan)); + + napi_gro_receive(&rx_ring->r_vec->napi, skb); + } + + if (nn->is_nfp3200) + nfp_qcp_rd_ptr_add(rx_ring->qcp_rx, pkts_polled); + + return pkts_polled; +} + +/** + * nfp_net_poll() - napi poll function + * @napi: NAPI structure + * @budget: NAPI budget + * + * Return: number of packets polled. + */ +static int nfp_net_poll(struct napi_struct *napi, int budget) +{ + struct nfp_net_r_vector *r_vec = + container_of(napi, struct nfp_net_r_vector, napi); + struct nfp_net_rx_ring *rx_ring = r_vec->rx_ring; + struct nfp_net_tx_ring *tx_ring = r_vec->tx_ring; + struct nfp_net *nn = r_vec->nfp_net; + struct netdev_queue *txq; + unsigned int pkts_polled; + + tx_ring = &nn->tx_rings[rx_ring->idx]; + txq = netdev_get_tx_queue(nn->netdev, tx_ring->idx); + nfp_net_tx_complete(tx_ring); + + pkts_polled = nfp_net_rx(rx_ring, budget); + + if (pkts_polled < budget) { + napi_complete_done(napi, pkts_polled); + nfp_net_irq_unmask(nn, r_vec->irq_idx); + } + + return pkts_polled; +} + +/* Setup and Configuration + */ + +/** + * nfp_net_tx_ring_free() - Free resources allocated to a TX ring + * @tx_ring: TX ring to free + */ +static void nfp_net_tx_ring_free(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net *nn = r_vec->nfp_net; + struct pci_dev *pdev = nn->pdev; + + nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(tx_ring->idx), 0); + nn_writeb(nn, NFP_NET_CFG_TXR_SZ(tx_ring->idx), 0); + nn_writeb(nn, NFP_NET_CFG_TXR_VEC(tx_ring->idx), 0); + + kfree(tx_ring->txbufs); + + if (tx_ring->txds) + dma_free_coherent(&pdev->dev, tx_ring->size, + tx_ring->txds, tx_ring->dma); + + tx_ring->cnt = 0; + tx_ring->wr_p = 0; + tx_ring->rd_p = 0; + tx_ring->qcp_rd_p = 0; + + tx_ring->txbufs = NULL; + tx_ring->txds = NULL; + tx_ring->dma = 0; + tx_ring->size = 0; +} + +/** + * nfp_net_tx_ring_alloc() - Allocate resource for a TX ring + * @tx_ring: TX Ring structure to allocate + * + * Return: 0 on success, negative errno otherwise. + */ +static int nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_r_vector *r_vec = tx_ring->r_vec; + struct nfp_net *nn = r_vec->nfp_net; + struct pci_dev *pdev = nn->pdev; + int sz; + + tx_ring->cnt = nn->txd_cnt; + + tx_ring->size = sizeof(*tx_ring->txds) * tx_ring->cnt; + tx_ring->txds = dma_zalloc_coherent(&pdev->dev, tx_ring->size, + &tx_ring->dma, GFP_KERNEL); + if (!tx_ring->txds) + goto err_alloc; + + sz = sizeof(*tx_ring->txbufs) * tx_ring->cnt; + tx_ring->txbufs = kzalloc(sz, GFP_KERNEL); + if (!tx_ring->txbufs) + goto err_alloc; + + /* Write the DMA address, size and MSI-X info to the device */ + nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(tx_ring->idx), tx_ring->dma); + nn_writeb(nn, NFP_NET_CFG_TXR_SZ(tx_ring->idx), ilog2(tx_ring->cnt)); + nn_writeb(nn, NFP_NET_CFG_TXR_VEC(tx_ring->idx), r_vec->irq_idx); + + netif_set_xps_queue(nn->netdev, &r_vec->affinity_mask, tx_ring->idx); + + nn_dbg(nn, "TxQ%02d: QCidx=%02d cnt=%d dma=%#llx host=%p\n", + tx_ring->idx, tx_ring->qcidx, + tx_ring->cnt, (unsigned long long)tx_ring->dma, tx_ring->txds); + + return 0; + +err_alloc: + nfp_net_tx_ring_free(tx_ring); + return -ENOMEM; +} + +/** + * nfp_net_rx_ring_free() - Free resources allocated to a RX ring + * @rx_ring: RX ring to free + */ +static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring) +{ + struct nfp_net_r_vector *r_vec = rx_ring->r_vec; + struct nfp_net *nn = r_vec->nfp_net; + struct pci_dev *pdev = nn->pdev; + + nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(rx_ring->idx), 0); + nn_writeb(nn, NFP_NET_CFG_RXR_SZ(rx_ring->idx), 0); + nn_writeb(nn, NFP_NET_CFG_RXR_VEC(rx_ring->idx), 0); + + kfree(rx_ring->rxbufs); + + if (rx_ring->rxds) + dma_free_coherent(&pdev->dev, rx_ring->size, + rx_ring->rxds, rx_ring->dma); + + rx_ring->cnt = 0; + rx_ring->wr_p = 0; + rx_ring->rd_p = 0; + + rx_ring->rxbufs = NULL; + rx_ring->rxds = NULL; + rx_ring->dma = 0; + rx_ring->size = 0; +} + +/** + * nfp_net_rx_ring_alloc() - Allocate resource for a RX ring + * @rx_ring: RX ring to allocate + * + * Return: 0 on success, negative errno otherwise. + */ +static int nfp_net_rx_ring_alloc(struct nfp_net_rx_ring *rx_ring) +{ + struct nfp_net_r_vector *r_vec = rx_ring->r_vec; + struct nfp_net *nn = r_vec->nfp_net; + struct pci_dev *pdev = nn->pdev; + int sz; + + rx_ring->cnt = nn->rxd_cnt; + + rx_ring->size = sizeof(*rx_ring->rxds) * rx_ring->cnt; + rx_ring->rxds = dma_zalloc_coherent(&pdev->dev, rx_ring->size, + &rx_ring->dma, GFP_KERNEL); + if (!rx_ring->rxds) + goto err_alloc; + + sz = sizeof(*rx_ring->rxbufs) * rx_ring->cnt; + rx_ring->rxbufs = kzalloc(sz, GFP_KERNEL); + if (!rx_ring->rxbufs) + goto err_alloc; + + /* Write the DMA address, size and MSI-X info to the device */ + nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(rx_ring->idx), rx_ring->dma); + nn_writeb(nn, NFP_NET_CFG_RXR_SZ(rx_ring->idx), ilog2(rx_ring->cnt)); + nn_writeb(nn, NFP_NET_CFG_RXR_VEC(rx_ring->idx), r_vec->irq_idx); + + nn_dbg(nn, "RxQ%02d: FlQCidx=%02d RxQCidx=%02d cnt=%d dma=%#llx host=%p\n", + rx_ring->idx, rx_ring->fl_qcidx, rx_ring->rx_qcidx, + rx_ring->cnt, (unsigned long long)rx_ring->dma, rx_ring->rxds); + + return 0; + +err_alloc: + nfp_net_rx_ring_free(rx_ring); + return -ENOMEM; +} + +static void __nfp_net_free_rings(struct nfp_net *nn, unsigned int n_free) +{ + struct nfp_net_r_vector *r_vec; + struct msix_entry *entry; + + while (n_free--) { + r_vec = &nn->r_vecs[n_free]; + entry = &nn->irq_entries[r_vec->irq_idx]; + + nfp_net_rx_ring_free(r_vec->rx_ring); + nfp_net_tx_ring_free(r_vec->tx_ring); + + irq_set_affinity_hint(entry->vector, NULL); + free_irq(entry->vector, r_vec); + + netif_napi_del(&r_vec->napi); + } +} + +/** + * nfp_net_free_rings() - Free all ring resources + * @nn: NFP Net device to reconfigure + */ +static void nfp_net_free_rings(struct nfp_net *nn) +{ + __nfp_net_free_rings(nn, nn->num_r_vecs); +} + +/** + * nfp_net_alloc_rings() - Allocate resources for RX and TX rings + * @nn: NFP Net device to reconfigure + * + * Return: 0 on success or negative errno on error. + */ +static int nfp_net_alloc_rings(struct nfp_net *nn) +{ + struct nfp_net_r_vector *r_vec; + struct msix_entry *entry; + int err; + int r; + + for (r = 0; r < nn->num_r_vecs; r++) { + r_vec = &nn->r_vecs[r]; + entry = &nn->irq_entries[r_vec->irq_idx]; + + /* Setup NAPI */ + netif_napi_add(nn->netdev, &r_vec->napi, + nfp_net_poll, NAPI_POLL_WEIGHT); + + snprintf(r_vec->name, sizeof(r_vec->name), + "%s-rxtx-%d", nn->netdev->name, r); + err = request_irq(entry->vector, r_vec->handler, 0, + r_vec->name, r_vec); + if (err) { + nn_dbg(nn, "Error requesting IRQ %d\n", entry->vector); + goto err_napi_del; + } + + irq_set_affinity_hint(entry->vector, &r_vec->affinity_mask); + + nn_dbg(nn, "RV%02d: irq=%03d/%03d\n", + r, entry->vector, entry->entry); + + /* Allocate TX ring resources */ + err = nfp_net_tx_ring_alloc(r_vec->tx_ring); + if (err) + goto err_free_irq; + + /* Allocate RX ring resources */ + err = nfp_net_rx_ring_alloc(r_vec->rx_ring); + if (err) + goto err_free_tx; + } + + return 0; + +err_free_tx: + nfp_net_tx_ring_free(r_vec->tx_ring); +err_free_irq: + irq_set_affinity_hint(entry->vector, NULL); + free_irq(entry->vector, r_vec); +err_napi_del: + netif_napi_del(&r_vec->napi); + __nfp_net_free_rings(nn, r); + return err; +} + +/** + * nfp_net_rss_write_itbl() - Write RSS indirection table to device + * @nn: NFP Net device to reconfigure + */ +void nfp_net_rss_write_itbl(struct nfp_net *nn) +{ + int i; + + for (i = 0; i < NFP_NET_CFG_RSS_ITBL_SZ; i += 4) + nn_writel(nn, NFP_NET_CFG_RSS_ITBL + i, + get_unaligned_le32(nn->rss_itbl + i)); +} + +/** + * nfp_net_rss_write_key() - Write RSS hash key to device + * @nn: NFP Net device to reconfigure + */ +void nfp_net_rss_write_key(struct nfp_net *nn) +{ + int i; + + for (i = 0; i < NFP_NET_CFG_RSS_KEY_SZ; i += 4) + nn_writel(nn, NFP_NET_CFG_RSS_KEY + i, + get_unaligned_le32(nn->rss_key + i)); +} + +/** + * nfp_net_coalesce_write_cfg() - Write irq coalescence configuration to HW + * @nn: NFP Net device to reconfigure + */ +void nfp_net_coalesce_write_cfg(struct nfp_net *nn) +{ + u8 i; + u32 factor; + u32 value; + + /* Compute factor used to convert coalesce '_usecs' parameters to + * ME timestamp ticks. There are 16 ME clock cycles for each timestamp + * count. + */ + factor = nn->me_freq_mhz / 16; + + /* copy RX interrupt coalesce parameters */ + value = (nn->rx_coalesce_max_frames << 16) | + (factor * nn->rx_coalesce_usecs); + for (i = 0; i < nn->num_r_vecs; i++) + nn_writel(nn, NFP_NET_CFG_RXR_IRQ_MOD(i), value); + + /* copy TX interrupt coalesce parameters */ + value = (nn->tx_coalesce_max_frames << 16) | + (factor * nn->tx_coalesce_usecs); + for (i = 0; i < nn->num_r_vecs; i++) + nn_writel(nn, NFP_NET_CFG_TXR_IRQ_MOD(i), value); +} + +/** + * nfp_net_write_mac_addr() - Write mac address to device registers + * @nn: NFP Net device to reconfigure + * @mac: Six-byte MAC address to be written + * + * We do a bit of byte swapping dance because firmware is LE. + */ +static void nfp_net_write_mac_addr(struct nfp_net *nn, const u8 *mac) +{ + nn_writel(nn, NFP_NET_CFG_MACADDR + 0, + get_unaligned_be32(nn->netdev->dev_addr)); + /* We can't do writew for NFP-3200 compatibility */ + nn_writel(nn, NFP_NET_CFG_MACADDR + 4, + get_unaligned_be16(nn->netdev->dev_addr + 4) << 16); +} + +/** + * nfp_net_clear_config_and_disable() - Clear control BAR and disable NFP + * @nn: NFP Net device to reconfigure + */ +static void nfp_net_clear_config_and_disable(struct nfp_net *nn) +{ + u32 new_ctrl, update; + int err; + + new_ctrl = nn->ctrl; + new_ctrl &= ~NFP_NET_CFG_CTRL_ENABLE; + update = NFP_NET_CFG_UPDATE_GEN; + update |= NFP_NET_CFG_UPDATE_MSIX; + update |= NFP_NET_CFG_UPDATE_RING; + + if (nn->cap & NFP_NET_CFG_CTRL_RINGCFG) + new_ctrl &= ~NFP_NET_CFG_CTRL_RINGCFG; + + nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, 0); + nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, 0); + + nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl); + err = nfp_net_reconfig(nn, update); + if (err) { + nn_err(nn, "Could not disable device: %d\n", err); + return; + } + + nn->ctrl = new_ctrl; +} + +/** + * nfp_net_start_vec() - Start ring vector + * @nn: NFP Net device structure + * @r_vec: Ring vector to be started + */ +static int nfp_net_start_vec(struct nfp_net *nn, struct nfp_net_r_vector *r_vec) +{ + unsigned int irq_vec; + int err = 0; + + irq_vec = nn->irq_entries[r_vec->irq_idx].vector; + + disable_irq(irq_vec); + + err = nfp_net_rx_fill_freelist(r_vec->rx_ring); + if (err) { + nn_err(nn, "RV%02d: couldn't allocate enough buffers\n", + r_vec->irq_idx); + goto out; + } + + napi_enable(&r_vec->napi); +out: + enable_irq(irq_vec); + + return err; +} + +static int nfp_net_netdev_open(struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + int err, r; + u32 update = 0; + u32 new_ctrl; + + if (nn->ctrl & NFP_NET_CFG_CTRL_ENABLE) { + nn_err(nn, "Dev is already enabled: 0x%08x\n", nn->ctrl); + return -EBUSY; + } + + new_ctrl = nn->ctrl; + + /* Step 1: Allocate resources for rings and the like + * - Request interrupts + * - Allocate RX and TX ring resources + * - Setup initial RSS table + */ + err = nfp_net_aux_irq_request(nn, NFP_NET_CFG_EXN, "%s-exn", + nn->exn_name, sizeof(nn->exn_name), + NFP_NET_IRQ_EXN_IDX, nn->exn_handler); + if (err) + return err; + + err = nfp_net_alloc_rings(nn); + if (err) + goto err_free_exn; + + err = netif_set_real_num_tx_queues(netdev, nn->num_tx_rings); + if (err) + goto err_free_rings; + + err = netif_set_real_num_rx_queues(netdev, nn->num_rx_rings); + if (err) + goto err_free_rings; + + if (nn->cap & NFP_NET_CFG_CTRL_RSS) { + nfp_net_rss_write_key(nn); + nfp_net_rss_write_itbl(nn); + nn_writel(nn, NFP_NET_CFG_RSS_CTRL, nn->rss_cfg); + update |= NFP_NET_CFG_UPDATE_RSS; + } + + if (nn->cap & NFP_NET_CFG_CTRL_IRQMOD) { + nfp_net_coalesce_write_cfg(nn); + + new_ctrl |= NFP_NET_CFG_CTRL_IRQMOD; + update |= NFP_NET_CFG_UPDATE_IRQMOD; + } + + /* Step 2: Configure the NFP + * - Enable rings from 0 to tx_rings/rx_rings - 1. + * - Write MAC address (in case it changed) + * - Set the MTU + * - Set the Freelist buffer size + * - Enable the FW + */ + nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, nn->num_tx_rings == 64 ? + 0xffffffffffffffffULL : ((u64)1 << nn->num_tx_rings) - 1); + + nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, nn->num_rx_rings == 64 ? + 0xffffffffffffffffULL : ((u64)1 << nn->num_rx_rings) - 1); + + nfp_net_write_mac_addr(nn, netdev->dev_addr); + + nn_writel(nn, NFP_NET_CFG_MTU, netdev->mtu); + nn_writel(nn, NFP_NET_CFG_FLBUFSZ, nn->fl_bufsz); + + /* Enable device */ + new_ctrl |= NFP_NET_CFG_CTRL_ENABLE; + update |= NFP_NET_CFG_UPDATE_GEN; + update |= NFP_NET_CFG_UPDATE_MSIX; + update |= NFP_NET_CFG_UPDATE_RING; + if (nn->cap & NFP_NET_CFG_CTRL_RINGCFG) + new_ctrl |= NFP_NET_CFG_CTRL_RINGCFG; + + nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl); + err = nfp_net_reconfig(nn, update); + if (err) + goto err_clear_config; + + nn->ctrl = new_ctrl; + + /* Since reconfiguration requests while NFP is down are ignored we + * have to wipe the entire VXLAN configuration and reinitialize it. + */ + if (nn->ctrl & NFP_NET_CFG_CTRL_VXLAN) { + memset(&nn->vxlan_ports, 0, sizeof(nn->vxlan_ports)); + memset(&nn->vxlan_usecnt, 0, sizeof(nn->vxlan_usecnt)); + vxlan_get_rx_port(netdev); + } + + /* Step 3: Enable for kernel + * - put some freelist descriptors on each RX ring + * - enable NAPI on each ring + * - enable all TX queues + * - set link state + */ + for (r = 0; r < nn->num_r_vecs; r++) { + err = nfp_net_start_vec(nn, &nn->r_vecs[r]); + if (err) + goto err_disable_napi; + } + + netif_tx_wake_all_queues(netdev); + + err = nfp_net_aux_irq_request(nn, NFP_NET_CFG_LSC, "%s-lsc", + nn->lsc_name, sizeof(nn->lsc_name), + NFP_NET_IRQ_LSC_IDX, nn->lsc_handler); + if (err) + goto err_stop_tx; + nfp_net_read_link_status(nn); + + return 0; + +err_stop_tx: + netif_tx_disable(netdev); + for (r = 0; r < nn->num_r_vecs; r++) + nfp_net_tx_flush(nn->r_vecs[r].tx_ring); +err_disable_napi: + while (r--) { + napi_disable(&nn->r_vecs[r].napi); + nfp_net_rx_flush(nn->r_vecs[r].rx_ring); + } +err_clear_config: + nfp_net_clear_config_and_disable(nn); +err_free_rings: + nfp_net_free_rings(nn); +err_free_exn: + nfp_net_aux_irq_free(nn, NFP_NET_CFG_EXN, NFP_NET_IRQ_EXN_IDX); + return err; +} + +/** + * nfp_net_netdev_close() - Called when the device is downed + * @netdev: netdev structure + */ +static int nfp_net_netdev_close(struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + int r; + + if (!(nn->ctrl & NFP_NET_CFG_CTRL_ENABLE)) { + nn_err(nn, "Dev is not up: 0x%08x\n", nn->ctrl); + return 0; + } + + /* Step 1: Disable RX and TX rings from the Linux kernel perspective + */ + nfp_net_aux_irq_free(nn, NFP_NET_CFG_LSC, NFP_NET_IRQ_LSC_IDX); + netif_carrier_off(netdev); + nn->link_up = false; + + for (r = 0; r < nn->num_r_vecs; r++) + napi_disable(&nn->r_vecs[r].napi); + + netif_tx_disable(netdev); + + /* Step 2: Tell NFP + */ + nfp_net_clear_config_and_disable(nn); + + /* Step 3: Free resources + */ + for (r = 0; r < nn->num_r_vecs; r++) { + nfp_net_rx_flush(nn->r_vecs[r].rx_ring); + nfp_net_tx_flush(nn->r_vecs[r].tx_ring); + } + + nfp_net_free_rings(nn); + nfp_net_aux_irq_free(nn, NFP_NET_CFG_EXN, NFP_NET_IRQ_EXN_IDX); + + nn_dbg(nn, "%s down", netdev->name); + return 0; +} + +static void nfp_net_set_rx_mode(struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + u32 new_ctrl; + + new_ctrl = nn->ctrl; + + if (netdev->flags & IFF_PROMISC) { + if (nn->cap & NFP_NET_CFG_CTRL_PROMISC) + new_ctrl |= NFP_NET_CFG_CTRL_PROMISC; + else + nn_warn(nn, "FW does not support promiscuous mode\n"); + } else { + new_ctrl &= ~NFP_NET_CFG_CTRL_PROMISC; + } + + if (new_ctrl == nn->ctrl) + return; + + nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl); + if (nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN)) + return; + + nn->ctrl = new_ctrl; +} + +static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct nfp_net *nn = netdev_priv(netdev); + u32 tmp; + + nn_dbg(nn, "New MTU = %d\n", new_mtu); + + if (new_mtu < 68 || new_mtu > nn->max_mtu) { + nn_err(nn, "New MTU (%d) is not valid\n", new_mtu); + return -EINVAL; + } + + netdev->mtu = new_mtu; + + /* Freelist buffer size rounded up to the nearest 1K */ + tmp = new_mtu + ETH_HLEN + VLAN_HLEN + NFP_NET_MAX_PREPEND; + nn->fl_bufsz = roundup(tmp, 1024); + + /* restart if running */ + if (netif_running(netdev)) { + nfp_net_netdev_close(netdev); + nfp_net_netdev_open(netdev); + } + + return 0; +} + +static struct rtnl_link_stats64 *nfp_net_stat64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct nfp_net *nn = netdev_priv(netdev); + int r; + + for (r = 0; r < nn->num_r_vecs; r++) { + struct nfp_net_r_vector *r_vec = &nn->r_vecs[r]; + u64 data[3]; + unsigned int start; + + do { + start = u64_stats_fetch_begin(&r_vec->rx_sync); + data[0] = r_vec->rx_pkts; + data[1] = r_vec->rx_bytes; + data[2] = r_vec->rx_drops; + } while (u64_stats_fetch_retry(&r_vec->rx_sync, start)); + stats->rx_packets += data[0]; + stats->rx_bytes += data[1]; + stats->rx_dropped += data[2]; + + do { + start = u64_stats_fetch_begin(&r_vec->tx_sync); + data[0] = r_vec->tx_pkts; + data[1] = r_vec->tx_bytes; + data[2] = r_vec->tx_errors; + } while (u64_stats_fetch_retry(&r_vec->tx_sync, start)); + stats->tx_packets += data[0]; + stats->tx_bytes += data[1]; + stats->tx_errors += data[2]; + } + + return stats; +} + +static int nfp_net_set_features(struct net_device *netdev, + netdev_features_t features) +{ + netdev_features_t changed = netdev->features ^ features; + struct nfp_net *nn = netdev_priv(netdev); + u32 new_ctrl; + int err; + + /* Assume this is not called with features we have not advertised */ + + new_ctrl = nn->ctrl; + + if (changed & NETIF_F_RXCSUM) { + if (features & NETIF_F_RXCSUM) + new_ctrl |= NFP_NET_CFG_CTRL_RXCSUM; + else + new_ctrl &= ~NFP_NET_CFG_CTRL_RXCSUM; + } + + if (changed & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) { + if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) + new_ctrl |= NFP_NET_CFG_CTRL_TXCSUM; + else + new_ctrl &= ~NFP_NET_CFG_CTRL_TXCSUM; + } + + if (changed & (NETIF_F_TSO | NETIF_F_TSO6)) { + if (features & (NETIF_F_TSO | NETIF_F_TSO6)) + new_ctrl |= NFP_NET_CFG_CTRL_LSO; + else + new_ctrl &= ~NFP_NET_CFG_CTRL_LSO; + } + + if (changed & NETIF_F_HW_VLAN_CTAG_RX) { + if (features & NETIF_F_HW_VLAN_CTAG_RX) + new_ctrl |= NFP_NET_CFG_CTRL_RXVLAN; + else + new_ctrl &= ~NFP_NET_CFG_CTRL_RXVLAN; + } + + if (changed & NETIF_F_HW_VLAN_CTAG_TX) { + if (features & NETIF_F_HW_VLAN_CTAG_TX) + new_ctrl |= NFP_NET_CFG_CTRL_TXVLAN; + else + new_ctrl &= ~NFP_NET_CFG_CTRL_TXVLAN; + } + + if (changed & NETIF_F_SG) { + if (features & NETIF_F_SG) + new_ctrl |= NFP_NET_CFG_CTRL_GATHER; + else + new_ctrl &= ~NFP_NET_CFG_CTRL_GATHER; + } + + nn_dbg(nn, "Feature change 0x%llx -> 0x%llx (changed=0x%llx)\n", + netdev->features, features, changed); + + if (new_ctrl == nn->ctrl) + return 0; + + nn_dbg(nn, "NIC ctrl: 0x%x -> 0x%x\n", nn->ctrl, new_ctrl); + nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl); + err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN); + if (err) + return err; + + nn->ctrl = new_ctrl; + + return 0; +} + +static netdev_features_t +nfp_net_features_check(struct sk_buff *skb, struct net_device *dev, + netdev_features_t features) +{ + u8 l4_hdr; + + /* We can't do TSO over double tagged packets (802.1AD) */ + features &= vlan_features_check(skb, features); + + if (!skb->encapsulation) + return features; + + /* Ensure that inner L4 header offset fits into TX descriptor field */ + if (skb_is_gso(skb)) { + u32 hdrlen; + + hdrlen = skb_inner_transport_header(skb) - skb->data + + inner_tcp_hdrlen(skb); + + if (unlikely(hdrlen > NFP_NET_LSO_MAX_HDR_SZ)) + features &= ~NETIF_F_GSO_MASK; + } + + /* VXLAN/GRE check */ + switch (vlan_get_protocol(skb)) { + case htons(ETH_P_IP): + l4_hdr = ip_hdr(skb)->protocol; + break; + case htons(ETH_P_IPV6): + l4_hdr = ipv6_hdr(skb)->nexthdr; + break; + default: + return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); + } + + if (skb->inner_protocol_type != ENCAP_TYPE_ETHER || + skb->inner_protocol != htons(ETH_P_TEB) || + (l4_hdr != IPPROTO_UDP && l4_hdr != IPPROTO_GRE) || + (l4_hdr == IPPROTO_UDP && + (skb_inner_mac_header(skb) - skb_transport_header(skb) != + sizeof(struct udphdr) + sizeof(struct vxlanhdr)))) + return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); + + return features; +} + +/** + * nfp_net_set_vxlan_port() - set vxlan port in SW and reconfigure HW + * @nn: NFP Net device to reconfigure + * @idx: Index into the port table where new port should be written + * @port: UDP port to configure (pass zero to remove VXLAN port) + */ +static void nfp_net_set_vxlan_port(struct nfp_net *nn, int idx, __be16 port) +{ + int i; + + nn->vxlan_ports[idx] = port; + + if (!(nn->ctrl & NFP_NET_CFG_CTRL_VXLAN)) + return; + + BUILD_BUG_ON(NFP_NET_N_VXLAN_PORTS & 1); + for (i = 0; i < NFP_NET_N_VXLAN_PORTS; i += 2) + nn_writel(nn, NFP_NET_CFG_VXLAN_PORT + i * sizeof(port), + be16_to_cpu(nn->vxlan_ports[i + 1]) << 16 | + be16_to_cpu(nn->vxlan_ports[i])); + + nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_VXLAN); +} + +/** + * nfp_net_find_vxlan_idx() - find table entry of the port or a free one + * @nn: NFP Network structure + * @port: UDP port to look for + * + * Return: if the port is already in the table -- it's position; + * if the port is not in the table -- free position to use; + * if the table is full -- -ENOSPC. + */ +static int nfp_net_find_vxlan_idx(struct nfp_net *nn, __be16 port) +{ + int i, free_idx = -ENOSPC; + + for (i = 0; i < NFP_NET_N_VXLAN_PORTS; i++) { + if (nn->vxlan_ports[i] == port) + return i; + if (!nn->vxlan_usecnt[i]) + free_idx = i; + } + + return free_idx; +} + +static void nfp_net_add_vxlan_port(struct net_device *netdev, + sa_family_t sa_family, __be16 port) +{ + struct nfp_net *nn = netdev_priv(netdev); + int idx; + + idx = nfp_net_find_vxlan_idx(nn, port); + if (idx == -ENOSPC) + return; + + if (!nn->vxlan_usecnt[idx]++) + nfp_net_set_vxlan_port(nn, idx, port); +} + +static void nfp_net_del_vxlan_port(struct net_device *netdev, + sa_family_t sa_family, __be16 port) +{ + struct nfp_net *nn = netdev_priv(netdev); + int idx; + + idx = nfp_net_find_vxlan_idx(nn, port); + if (!nn->vxlan_usecnt[idx] || idx == -ENOSPC) + return; + + if (!--nn->vxlan_usecnt[idx]) + nfp_net_set_vxlan_port(nn, idx, 0); +} + +static const struct net_device_ops nfp_net_netdev_ops = { + .ndo_open = nfp_net_netdev_open, + .ndo_stop = nfp_net_netdev_close, + .ndo_start_xmit = nfp_net_tx, + .ndo_get_stats64 = nfp_net_stat64, + .ndo_tx_timeout = nfp_net_tx_timeout, + .ndo_set_rx_mode = nfp_net_set_rx_mode, + .ndo_change_mtu = nfp_net_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_set_features = nfp_net_set_features, + .ndo_features_check = nfp_net_features_check, + .ndo_add_vxlan_port = nfp_net_add_vxlan_port, + .ndo_del_vxlan_port = nfp_net_del_vxlan_port, +}; + +/** + * nfp_net_info() - Print general info about the NIC + * @nn: NFP Net device to reconfigure + */ +void nfp_net_info(struct nfp_net *nn) +{ + nn_info(nn, "Netronome %s %sNetdev: TxQs=%d/%d RxQs=%d/%d\n", + nn->is_nfp3200 ? "NFP-32xx" : "NFP-6xxx", + nn->is_vf ? "VF " : "", + nn->num_tx_rings, nn->max_tx_rings, + nn->num_rx_rings, nn->max_rx_rings); + nn_info(nn, "VER: %d.%d.%d.%d, Maximum supported MTU: %d\n", + nn->fw_ver.resv, nn->fw_ver.class, + nn->fw_ver.major, nn->fw_ver.minor, + nn->max_mtu); + nn_info(nn, "CAP: %#x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + nn->cap, + nn->cap & NFP_NET_CFG_CTRL_PROMISC ? "PROMISC " : "", + nn->cap & NFP_NET_CFG_CTRL_L2BC ? "L2BCFILT " : "", + nn->cap & NFP_NET_CFG_CTRL_L2MC ? "L2MCFILT " : "", + nn->cap & NFP_NET_CFG_CTRL_RXCSUM ? "RXCSUM " : "", + nn->cap & NFP_NET_CFG_CTRL_TXCSUM ? "TXCSUM " : "", + nn->cap & NFP_NET_CFG_CTRL_RXVLAN ? "RXVLAN " : "", + nn->cap & NFP_NET_CFG_CTRL_TXVLAN ? "TXVLAN " : "", + nn->cap & NFP_NET_CFG_CTRL_SCATTER ? "SCATTER " : "", + nn->cap & NFP_NET_CFG_CTRL_GATHER ? "GATHER " : "", + nn->cap & NFP_NET_CFG_CTRL_LSO ? "TSO " : "", + nn->cap & NFP_NET_CFG_CTRL_RSS ? "RSS " : "", + nn->cap & NFP_NET_CFG_CTRL_L2SWITCH ? "L2SWITCH " : "", + nn->cap & NFP_NET_CFG_CTRL_MSIXAUTO ? "AUTOMASK " : "", + nn->cap & NFP_NET_CFG_CTRL_IRQMOD ? "IRQMOD " : "", + nn->cap & NFP_NET_CFG_CTRL_VXLAN ? "VXLAN " : "", + nn->cap & NFP_NET_CFG_CTRL_NVGRE ? "NVGRE " : ""); +} + +/** + * nfp_net_netdev_alloc() - Allocate netdev and related structure + * @pdev: PCI device + * @max_tx_rings: Maximum number of TX rings supported by device + * @max_rx_rings: Maximum number of RX rings supported by device + * + * This function allocates a netdev device and fills in the initial + * part of the @struct nfp_net structure. + * + * Return: NFP Net device structure, or ERR_PTR on error. + */ +struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev, + int max_tx_rings, int max_rx_rings) +{ + struct net_device *netdev; + struct nfp_net *nn; + int nqs; + + netdev = alloc_etherdev_mqs(sizeof(struct nfp_net), + max_tx_rings, max_rx_rings); + if (!netdev) + return ERR_PTR(-ENOMEM); + + SET_NETDEV_DEV(netdev, &pdev->dev); + nn = netdev_priv(netdev); + + nn->netdev = netdev; + nn->pdev = pdev; + + nn->max_tx_rings = max_tx_rings; + nn->max_rx_rings = max_rx_rings; + + nqs = netif_get_num_default_rss_queues(); + nn->num_tx_rings = min_t(int, nqs, max_tx_rings); + nn->num_rx_rings = min_t(int, nqs, max_rx_rings); + + nn->txd_cnt = NFP_NET_TX_DESCS_DEFAULT; + nn->rxd_cnt = NFP_NET_RX_DESCS_DEFAULT; + + spin_lock_init(&nn->reconfig_lock); + spin_lock_init(&nn->link_status_lock); + + return nn; +} + +/** + * nfp_net_netdev_free() - Undo what @nfp_net_netdev_alloc() did + * @nn: NFP Net device to reconfigure + */ +void nfp_net_netdev_free(struct nfp_net *nn) +{ + free_netdev(nn->netdev); +} + +/** + * nfp_net_rss_init() - Set the initial RSS parameters + * @nn: NFP Net device to reconfigure + */ +static void nfp_net_rss_init(struct nfp_net *nn) +{ + int i; + + netdev_rss_key_fill(nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ); + + for (i = 0; i < sizeof(nn->rss_itbl); i++) + nn->rss_itbl[i] = + ethtool_rxfh_indir_default(i, nn->num_rx_rings); + + /* Enable IPv4/IPv6 TCP by default */ + nn->rss_cfg = NFP_NET_CFG_RSS_IPV4_TCP | + NFP_NET_CFG_RSS_IPV6_TCP | + NFP_NET_CFG_RSS_TOEPLITZ | + NFP_NET_CFG_RSS_MASK; +} + +/** + * nfp_net_irqmod_init() - Set the initial IRQ moderation parameters + * @nn: NFP Net device to reconfigure + */ +static void nfp_net_irqmod_init(struct nfp_net *nn) +{ + nn->rx_coalesce_usecs = 50; + nn->rx_coalesce_max_frames = 64; + nn->tx_coalesce_usecs = 50; + nn->tx_coalesce_max_frames = 64; +} + +/** + * nfp_net_netdev_init() - Initialise/finalise the netdev structure + * @netdev: netdev structure + * + * Return: 0 on success or negative errno on error. + */ +int nfp_net_netdev_init(struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + int err; + + /* Get some of the read-only fields from the BAR */ + nn->cap = nn_readl(nn, NFP_NET_CFG_CAP); + nn->max_mtu = nn_readl(nn, NFP_NET_CFG_MAX_MTU); + + nfp_net_write_mac_addr(nn, nn->netdev->dev_addr); + + /* Set default MTU and Freelist buffer size */ + if (nn->max_mtu < NFP_NET_DEFAULT_MTU) + netdev->mtu = nn->max_mtu; + else + netdev->mtu = NFP_NET_DEFAULT_MTU; + nn->fl_bufsz = NFP_NET_DEFAULT_RX_BUFSZ; + + /* Advertise/enable offloads based on capabilities + * + * Note: netdev->features show the currently enabled features + * and netdev->hw_features advertises which features are + * supported. By default we enable most features. + */ + netdev->hw_features = NETIF_F_HIGHDMA; + if (nn->cap & NFP_NET_CFG_CTRL_RXCSUM) { + netdev->hw_features |= NETIF_F_RXCSUM; + nn->ctrl |= NFP_NET_CFG_CTRL_RXCSUM; + } + if (nn->cap & NFP_NET_CFG_CTRL_TXCSUM) { + netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + nn->ctrl |= NFP_NET_CFG_CTRL_TXCSUM; + } + if (nn->cap & NFP_NET_CFG_CTRL_GATHER) { + netdev->hw_features |= NETIF_F_SG; + nn->ctrl |= NFP_NET_CFG_CTRL_GATHER; + } + if ((nn->cap & NFP_NET_CFG_CTRL_LSO) && nn->fw_ver.major > 2) { + netdev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6; + nn->ctrl |= NFP_NET_CFG_CTRL_LSO; + } + if (nn->cap & NFP_NET_CFG_CTRL_RSS) { + netdev->hw_features |= NETIF_F_RXHASH; + nfp_net_rss_init(nn); + nn->ctrl |= NFP_NET_CFG_CTRL_RSS; + } + if (nn->cap & NFP_NET_CFG_CTRL_VXLAN && + nn->cap & NFP_NET_CFG_CTRL_NVGRE) { + if (nn->cap & NFP_NET_CFG_CTRL_LSO) + netdev->hw_features |= NETIF_F_GSO_GRE | + NETIF_F_GSO_UDP_TUNNEL; + nn->ctrl |= NFP_NET_CFG_CTRL_VXLAN | NFP_NET_CFG_CTRL_NVGRE; + + netdev->hw_enc_features = netdev->hw_features; + } + + netdev->vlan_features = netdev->hw_features; + + if (nn->cap & NFP_NET_CFG_CTRL_RXVLAN) { + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; + nn->ctrl |= NFP_NET_CFG_CTRL_RXVLAN; + } + if (nn->cap & NFP_NET_CFG_CTRL_TXVLAN) { + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX; + nn->ctrl |= NFP_NET_CFG_CTRL_TXVLAN; + } + + netdev->features = netdev->hw_features; + + /* Advertise but disable TSO by default. */ + netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6); + + /* Allow L2 Broadcast and Multicast through by default, if supported */ + if (nn->cap & NFP_NET_CFG_CTRL_L2BC) + nn->ctrl |= NFP_NET_CFG_CTRL_L2BC; + if (nn->cap & NFP_NET_CFG_CTRL_L2MC) + nn->ctrl |= NFP_NET_CFG_CTRL_L2MC; + + /* Allow IRQ moderation, if supported */ + if (nn->cap & NFP_NET_CFG_CTRL_IRQMOD) { + nfp_net_irqmod_init(nn); + nn->ctrl |= NFP_NET_CFG_CTRL_IRQMOD; + } + + /* On NFP-3200 enable MSI-X auto-masking, if supported and the + * interrupts are not shared. + */ + if (nn->is_nfp3200 && nn->cap & NFP_NET_CFG_CTRL_MSIXAUTO) + nn->ctrl |= NFP_NET_CFG_CTRL_MSIXAUTO; + + /* On NFP4000/NFP6000, determine RX packet/metadata boundary offset */ + if (nn->fw_ver.major >= 2) + nn->rx_offset = nn_readl(nn, NFP_NET_CFG_RX_OFFSET); + else + nn->rx_offset = NFP_NET_RX_OFFSET; + + /* Stash the re-configuration queue away. First odd queue in TX Bar */ + nn->qcp_cfg = nn->tx_bar + NFP_QCP_QUEUE_ADDR_SZ; + + /* Make sure the FW knows the netdev is supposed to be disabled here */ + nn_writel(nn, NFP_NET_CFG_CTRL, 0); + nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, 0); + nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, 0); + err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RING | + NFP_NET_CFG_UPDATE_GEN); + if (err) + return err; + + /* Finalise the netdev setup */ + ether_setup(netdev); + netdev->netdev_ops = &nfp_net_netdev_ops; + netdev->watchdog_timeo = msecs_to_jiffies(5 * 1000); + + nfp_net_set_ethtool_ops(netdev); + nfp_net_irqs_assign(netdev); + + return register_netdev(netdev); +} + +/** + * nfp_net_netdev_clean() - Undo what nfp_net_netdev_init() did. + * @netdev: netdev structure + */ +void nfp_net_netdev_clean(struct net_device *netdev) +{ + unregister_netdev(netdev); +} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h new file mode 100644 index 000000000000..8692003aeed8 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2015 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * nfp_net_ctrl.h + * Netronome network device driver: Control BAR layout + * Authors: Jakub Kicinski + * Jason McMullan + * Rolf Neugebauer + * Brad Petrus + */ + +#ifndef _NFP_NET_CTRL_H_ +#define _NFP_NET_CTRL_H_ + +/* IMPORTANT: This header file is shared with the FW, + * no OS specific constructs, please! + */ + +/** + * Configuration BAR size. + * + * The configuration BAR is 8K in size, but on the NFP6000, due to + * THB-350, 32k needs to be reserved. + */ +#define NFP_NET_CFG_BAR_SZ (32 * 1024) + +/** + * Offset in Freelist buffer where packet starts on RX + */ +#define NFP_NET_RX_OFFSET 32 + +/** + * Maximum header size supported for LSO frames + */ +#define NFP_NET_LSO_MAX_HDR_SZ 255 + +/** + * Hash type pre-pended when a RSS hash was computed + */ +#define NFP_NET_RSS_NONE 0 +#define NFP_NET_RSS_IPV4 1 +#define NFP_NET_RSS_IPV6 2 +#define NFP_NET_RSS_IPV6_EX 3 +#define NFP_NET_RSS_IPV4_TCP 4 +#define NFP_NET_RSS_IPV6_TCP 5 +#define NFP_NET_RSS_IPV6_EX_TCP 6 +#define NFP_NET_RSS_IPV4_UDP 7 +#define NFP_NET_RSS_IPV6_UDP 8 +#define NFP_NET_RSS_IPV6_EX_UDP 9 + +/** + * @NFP_NET_TXR_MAX: Maximum number of TX rings + * @NFP_NET_TXR_MASK: Mask for TX rings + * @NFP_NET_RXR_MAX: Maximum number of RX rings + * @NFP_NET_RXR_MASK: Mask for RX rings + */ +#define NFP_NET_TXR_MAX 64 +#define NFP_NET_TXR_MASK (NFP_NET_TXR_MAX - 1) +#define NFP_NET_RXR_MAX 64 +#define NFP_NET_RXR_MASK (NFP_NET_RXR_MAX - 1) + +/** + * Read/Write config words (0x0000 - 0x002c) + * @NFP_NET_CFG_CTRL: Global control + * @NFP_NET_CFG_UPDATE: Indicate which fields are updated + * @NFP_NET_CFG_TXRS_ENABLE: Bitmask of enabled TX rings + * @NFP_NET_CFG_RXRS_ENABLE: Bitmask of enabled RX rings + * @NFP_NET_CFG_MTU: Set MTU size + * @NFP_NET_CFG_FLBUFSZ: Set freelist buffer size (must be larger than MTU) + * @NFP_NET_CFG_EXN: MSI-X table entry for exceptions + * @NFP_NET_CFG_LSC: MSI-X table entry for link state changes + * @NFP_NET_CFG_MACADDR: MAC address + * + * TODO: + * - define Error details in UPDATE + */ +#define NFP_NET_CFG_CTRL 0x0000 +#define NFP_NET_CFG_CTRL_ENABLE (0x1 << 0) /* Global enable */ +#define NFP_NET_CFG_CTRL_PROMISC (0x1 << 1) /* Enable Promisc mode */ +#define NFP_NET_CFG_CTRL_L2BC (0x1 << 2) /* Allow L2 Broadcast */ +#define NFP_NET_CFG_CTRL_L2MC (0x1 << 3) /* Allow L2 Multicast */ +#define NFP_NET_CFG_CTRL_RXCSUM (0x1 << 4) /* Enable RX Checksum */ +#define NFP_NET_CFG_CTRL_TXCSUM (0x1 << 5) /* Enable TX Checksum */ +#define NFP_NET_CFG_CTRL_RXVLAN (0x1 << 6) /* Enable VLAN strip */ +#define NFP_NET_CFG_CTRL_TXVLAN (0x1 << 7) /* Enable VLAN insert */ +#define NFP_NET_CFG_CTRL_SCATTER (0x1 << 8) /* Scatter DMA */ +#define NFP_NET_CFG_CTRL_GATHER (0x1 << 9) /* Gather DMA */ +#define NFP_NET_CFG_CTRL_LSO (0x1 << 10) /* LSO/TSO */ +#define NFP_NET_CFG_CTRL_RINGCFG (0x1 << 16) /* Ring runtime changes */ +#define NFP_NET_CFG_CTRL_RSS (0x1 << 17) /* RSS */ +#define NFP_NET_CFG_CTRL_IRQMOD (0x1 << 18) /* Interrupt moderation */ +#define NFP_NET_CFG_CTRL_RINGPRIO (0x1 << 19) /* Ring priorities */ +#define NFP_NET_CFG_CTRL_MSIXAUTO (0x1 << 20) /* MSI-X auto-masking */ +#define NFP_NET_CFG_CTRL_TXRWB (0x1 << 21) /* Write-back of TX ring*/ +#define NFP_NET_CFG_CTRL_L2SWITCH (0x1 << 22) /* L2 Switch */ +#define NFP_NET_CFG_CTRL_L2SWITCH_LOCAL (0x1 << 23) /* Switch to local */ +#define NFP_NET_CFG_CTRL_VXLAN (0x1 << 24) /* VXLAN tunnel support */ +#define NFP_NET_CFG_CTRL_NVGRE (0x1 << 25) /* NVGRE tunnel support */ +#define NFP_NET_CFG_UPDATE 0x0004 +#define NFP_NET_CFG_UPDATE_GEN (0x1 << 0) /* General update */ +#define NFP_NET_CFG_UPDATE_RING (0x1 << 1) /* Ring config change */ +#define NFP_NET_CFG_UPDATE_RSS (0x1 << 2) /* RSS config change */ +#define NFP_NET_CFG_UPDATE_TXRPRIO (0x1 << 3) /* TX Ring prio change */ +#define NFP_NET_CFG_UPDATE_RXRPRIO (0x1 << 4) /* RX Ring prio change */ +#define NFP_NET_CFG_UPDATE_MSIX (0x1 << 5) /* MSI-X change */ +#define NFP_NET_CFG_UPDATE_L2SWITCH (0x1 << 6) /* Switch changes */ +#define NFP_NET_CFG_UPDATE_RESET (0x1 << 7) /* Update due to FLR */ +#define NFP_NET_CFG_UPDATE_IRQMOD (0x1 << 8) /* IRQ mod change */ +#define NFP_NET_CFG_UPDATE_VXLAN (0x1 << 9) /* VXLAN port change */ +#define NFP_NET_CFG_UPDATE_ERR (0x1 << 31) /* A error occurred */ +#define NFP_NET_CFG_TXRS_ENABLE 0x0008 +#define NFP_NET_CFG_RXRS_ENABLE 0x0010 +#define NFP_NET_CFG_MTU 0x0018 +#define NFP_NET_CFG_FLBUFSZ 0x001c +#define NFP_NET_CFG_EXN 0x001f +#define NFP_NET_CFG_LSC 0x0020 +#define NFP_NET_CFG_MACADDR 0x0024 + +/** + * Read-only words (0x0030 - 0x0050): + * @NFP_NET_CFG_VERSION: Firmware version number + * @NFP_NET_CFG_STS: Status + * @NFP_NET_CFG_CAP: Capabilities (same bits as @NFP_NET_CFG_CTRL) + * @NFP_NET_MAX_TXRINGS: Maximum number of TX rings + * @NFP_NET_MAX_RXRINGS: Maximum number of RX rings + * @NFP_NET_MAX_MTU: Maximum support MTU + * @NFP_NET_CFG_START_TXQ: Start Queue Control Queue to use for TX (PF only) + * @NFP_NET_CFG_START_RXQ: Start Queue Control Queue to use for RX (PF only) + * + * TODO: + * - define more STS bits + */ +#define NFP_NET_CFG_VERSION 0x0030 +#define NFP_NET_CFG_VERSION_RESERVED_MASK (0xff << 24) +#define NFP_NET_CFG_VERSION_CLASS_MASK (0xff << 16) +#define NFP_NET_CFG_VERSION_CLASS(x) (((x) & 0xff) << 16) +#define NFP_NET_CFG_VERSION_CLASS_GENERIC 0 +#define NFP_NET_CFG_VERSION_MAJOR_MASK (0xff << 8) +#define NFP_NET_CFG_VERSION_MAJOR(x) (((x) & 0xff) << 8) +#define NFP_NET_CFG_VERSION_MINOR_MASK (0xff << 0) +#define NFP_NET_CFG_VERSION_MINOR(x) (((x) & 0xff) << 0) +#define NFP_NET_CFG_STS 0x0034 +#define NFP_NET_CFG_STS_LINK (0x1 << 0) /* Link up or down */ +#define NFP_NET_CFG_CAP 0x0038 +#define NFP_NET_CFG_MAX_TXRINGS 0x003c +#define NFP_NET_CFG_MAX_RXRINGS 0x0040 +#define NFP_NET_CFG_MAX_MTU 0x0044 +/* Next two words are being used by VFs for solving THB350 issue */ +#define NFP_NET_CFG_START_TXQ 0x0048 +#define NFP_NET_CFG_START_RXQ 0x004c + +/** + * NFP-3200 workaround (0x0050 - 0x0058) + * @NFP_NET_CFG_SPARE_ADDR: DMA address for ME code to use (e.g. YDS-155 fix) + */ +#define NFP_NET_CFG_SPARE_ADDR 0x0050 +/** + * NFP6000/NFP4000 - Prepend configuration + */ +#define NFP_NET_CFG_RX_OFFSET 0x0050 +#define NFP_NET_CFG_RX_OFFSET_DYNAMIC 0 /* Prepend mode */ + +/** + * NFP6000/NFP4000 - VXLAN/UDP encap configuration + * @NFP_NET_CFG_VXLAN_PORT: Base address of table of tunnels' UDP dst ports + * @NFP_NET_CFG_VXLAN_SZ: Size of the UDP port table in bytes + */ +#define NFP_NET_CFG_VXLAN_PORT 0x0060 +#define NFP_NET_CFG_VXLAN_SZ 0x0008 + +/** + * 64B reserved for future use (0x0080 - 0x00c0) + */ +#define NFP_NET_CFG_RESERVED 0x0080 +#define NFP_NET_CFG_RESERVED_SZ 0x0040 + +/** + * RSS configuration (0x0100 - 0x01ac): + * Used only when NFP_NET_CFG_CTRL_RSS is enabled + * @NFP_NET_CFG_RSS_CFG: RSS configuration word + * @NFP_NET_CFG_RSS_KEY: RSS "secret" key + * @NFP_NET_CFG_RSS_ITBL: RSS indirection table + */ +#define NFP_NET_CFG_RSS_BASE 0x0100 +#define NFP_NET_CFG_RSS_CTRL NFP_NET_CFG_RSS_BASE +#define NFP_NET_CFG_RSS_MASK (0x7f) +#define NFP_NET_CFG_RSS_MASK_of(_x) ((_x) & 0x7f) +#define NFP_NET_CFG_RSS_IPV4 (1 << 8) /* RSS for IPv4 */ +#define NFP_NET_CFG_RSS_IPV6 (1 << 9) /* RSS for IPv6 */ +#define NFP_NET_CFG_RSS_IPV4_TCP (1 << 10) /* RSS for IPv4/TCP */ +#define NFP_NET_CFG_RSS_IPV4_UDP (1 << 11) /* RSS for IPv4/UDP */ +#define NFP_NET_CFG_RSS_IPV6_TCP (1 << 12) /* RSS for IPv6/TCP */ +#define NFP_NET_CFG_RSS_IPV6_UDP (1 << 13) /* RSS for IPv6/UDP */ +#define NFP_NET_CFG_RSS_TOEPLITZ (1 << 24) /* Use Toeplitz hash */ +#define NFP_NET_CFG_RSS_KEY (NFP_NET_CFG_RSS_BASE + 0x4) +#define NFP_NET_CFG_RSS_KEY_SZ 0x28 +#define NFP_NET_CFG_RSS_ITBL (NFP_NET_CFG_RSS_BASE + 0x4 + \ + NFP_NET_CFG_RSS_KEY_SZ) +#define NFP_NET_CFG_RSS_ITBL_SZ 0x80 + +/** + * TX ring configuration (0x200 - 0x800) + * @NFP_NET_CFG_TXR_BASE: Base offset for TX ring configuration + * @NFP_NET_CFG_TXR_ADDR: Per TX ring DMA address (8B entries) + * @NFP_NET_CFG_TXR_WB_ADDR: Per TX ring write back DMA address (8B entries) + * @NFP_NET_CFG_TXR_SZ: Per TX ring ring size (1B entries) + * @NFP_NET_CFG_TXR_VEC: Per TX ring MSI-X table entry (1B entries) + * @NFP_NET_CFG_TXR_PRIO: Per TX ring priority (1B entries) + * @NFP_NET_CFG_TXR_IRQ_MOD: Per TX ring interrupt moderation packet + */ +#define NFP_NET_CFG_TXR_BASE 0x0200 +#define NFP_NET_CFG_TXR_ADDR(_x) (NFP_NET_CFG_TXR_BASE + ((_x) * 0x8)) +#define NFP_NET_CFG_TXR_WB_ADDR(_x) (NFP_NET_CFG_TXR_BASE + 0x200 + \ + ((_x) * 0x8)) +#define NFP_NET_CFG_TXR_SZ(_x) (NFP_NET_CFG_TXR_BASE + 0x400 + (_x)) +#define NFP_NET_CFG_TXR_VEC(_x) (NFP_NET_CFG_TXR_BASE + 0x440 + (_x)) +#define NFP_NET_CFG_TXR_PRIO(_x) (NFP_NET_CFG_TXR_BASE + 0x480 + (_x)) +#define NFP_NET_CFG_TXR_IRQ_MOD(_x) (NFP_NET_CFG_TXR_BASE + 0x500 + \ + ((_x) * 0x4)) + +/** + * RX ring configuration (0x0800 - 0x0c00) + * @NFP_NET_CFG_RXR_BASE: Base offset for RX ring configuration + * @NFP_NET_CFG_RXR_ADDR: Per RX ring DMA address (8B entries) + * @NFP_NET_CFG_RXR_SZ: Per RX ring ring size (1B entries) + * @NFP_NET_CFG_RXR_VEC: Per RX ring MSI-X table entry (1B entries) + * @NFP_NET_CFG_RXR_PRIO: Per RX ring priority (1B entries) + * @NFP_NET_CFG_RXR_IRQ_MOD: Per RX ring interrupt moderation (4B entries) + */ +#define NFP_NET_CFG_RXR_BASE 0x0800 +#define NFP_NET_CFG_RXR_ADDR(_x) (NFP_NET_CFG_RXR_BASE + ((_x) * 0x8)) +#define NFP_NET_CFG_RXR_SZ(_x) (NFP_NET_CFG_RXR_BASE + 0x200 + (_x)) +#define NFP_NET_CFG_RXR_VEC(_x) (NFP_NET_CFG_RXR_BASE + 0x240 + (_x)) +#define NFP_NET_CFG_RXR_PRIO(_x) (NFP_NET_CFG_RXR_BASE + 0x280 + (_x)) +#define NFP_NET_CFG_RXR_IRQ_MOD(_x) (NFP_NET_CFG_RXR_BASE + 0x300 + \ + ((_x) * 0x4)) + +/** + * Interrupt Control/Cause registers (0x0c00 - 0x0d00) + * These registers are only used when MSI-X auto-masking is not + * enabled (@NFP_NET_CFG_CTRL_MSIXAUTO not set). The array is index + * by MSI-X entry and are 1B in size. If an entry is zero, the + * corresponding entry is enabled. If the FW generates an interrupt, + * it writes a cause into the corresponding field. This also masks + * the MSI-X entry and the host driver must clear the register to + * re-enable the interrupt. + */ +#define NFP_NET_CFG_ICR_BASE 0x0c00 +#define NFP_NET_CFG_ICR(_x) (NFP_NET_CFG_ICR_BASE + (_x)) +#define NFP_NET_CFG_ICR_UNMASKED 0x0 +#define NFP_NET_CFG_ICR_RXTX 0x1 +#define NFP_NET_CFG_ICR_LSC 0x2 + +/** + * General device stats (0x0d00 - 0x0d90) + * all counters are 64bit. + */ +#define NFP_NET_CFG_STATS_BASE 0x0d00 +#define NFP_NET_CFG_STATS_RX_DISCARDS (NFP_NET_CFG_STATS_BASE + 0x00) +#define NFP_NET_CFG_STATS_RX_ERRORS (NFP_NET_CFG_STATS_BASE + 0x08) +#define NFP_NET_CFG_STATS_RX_OCTETS (NFP_NET_CFG_STATS_BASE + 0x10) +#define NFP_NET_CFG_STATS_RX_UC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x18) +#define NFP_NET_CFG_STATS_RX_MC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x20) +#define NFP_NET_CFG_STATS_RX_BC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x28) +#define NFP_NET_CFG_STATS_RX_FRAMES (NFP_NET_CFG_STATS_BASE + 0x30) +#define NFP_NET_CFG_STATS_RX_MC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x38) +#define NFP_NET_CFG_STATS_RX_BC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x40) + +#define NFP_NET_CFG_STATS_TX_DISCARDS (NFP_NET_CFG_STATS_BASE + 0x48) +#define NFP_NET_CFG_STATS_TX_ERRORS (NFP_NET_CFG_STATS_BASE + 0x50) +#define NFP_NET_CFG_STATS_TX_OCTETS (NFP_NET_CFG_STATS_BASE + 0x58) +#define NFP_NET_CFG_STATS_TX_UC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x60) +#define NFP_NET_CFG_STATS_TX_MC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x68) +#define NFP_NET_CFG_STATS_TX_BC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x70) +#define NFP_NET_CFG_STATS_TX_FRAMES (NFP_NET_CFG_STATS_BASE + 0x78) +#define NFP_NET_CFG_STATS_TX_MC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x80) +#define NFP_NET_CFG_STATS_TX_BC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x88) + +/** + * Per ring stats (0x1000 - 0x1800) + * options, 64bit per entry + * @NFP_NET_CFG_TXR_STATS: TX ring statistics (Packet and Byte count) + * @NFP_NET_CFG_RXR_STATS: RX ring statistics (Packet and Byte count) + */ +#define NFP_NET_CFG_TXR_STATS_BASE 0x1000 +#define NFP_NET_CFG_TXR_STATS(_x) (NFP_NET_CFG_TXR_STATS_BASE + \ + ((_x) * 0x10)) +#define NFP_NET_CFG_RXR_STATS_BASE 0x1400 +#define NFP_NET_CFG_RXR_STATS(_x) (NFP_NET_CFG_RXR_STATS_BASE + \ + ((_x) * 0x10)) + +#endif /* _NFP_NET_CTRL_H_ */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c new file mode 100644 index 000000000000..4c97c713121c --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2015 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include + +#include "nfp_net.h" + +static struct dentry *nfp_dir; + +static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data) +{ + struct nfp_net_rx_ring *rx_ring = file->private; + int fl_rd_p, fl_wr_p, rx_rd_p, rx_wr_p, rxd_cnt; + struct nfp_net_rx_desc *rxd; + struct sk_buff *skb; + struct nfp_net *nn; + int i; + + rtnl_lock(); + + if (!rx_ring->r_vec || !rx_ring->r_vec->nfp_net) + goto out; + nn = rx_ring->r_vec->nfp_net; + if (!netif_running(nn->netdev)) + goto out; + + rxd_cnt = rx_ring->cnt; + + fl_rd_p = nfp_qcp_rd_ptr_read(rx_ring->qcp_fl); + fl_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_fl); + rx_rd_p = nfp_qcp_rd_ptr_read(rx_ring->qcp_rx); + rx_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_rx); + + seq_printf(file, "RX[%02d]: H_RD=%d H_WR=%d FL_RD=%d FL_WR=%d RX_RD=%d RX_WR=%d\n", + rx_ring->idx, rx_ring->rd_p, rx_ring->wr_p, + fl_rd_p, fl_wr_p, rx_rd_p, rx_wr_p); + + for (i = 0; i < rxd_cnt; i++) { + rxd = &rx_ring->rxds[i]; + seq_printf(file, "%04d: 0x%08x 0x%08x", i, + rxd->vals[0], rxd->vals[1]); + + skb = READ_ONCE(rx_ring->rxbufs[i].skb); + if (skb) + seq_printf(file, " skb->head=%p skb->data=%p", + skb->head, skb->data); + + if (rx_ring->rxbufs[i].dma_addr) + seq_printf(file, " dma_addr=%pad", + &rx_ring->rxbufs[i].dma_addr); + + if (i == rx_ring->rd_p % rxd_cnt) + seq_puts(file, " H_RD "); + if (i == rx_ring->wr_p % rxd_cnt) + seq_puts(file, " H_WR "); + if (i == fl_rd_p % rxd_cnt) + seq_puts(file, " FL_RD"); + if (i == fl_wr_p % rxd_cnt) + seq_puts(file, " FL_WR"); + if (i == rx_rd_p % rxd_cnt) + seq_puts(file, " RX_RD"); + if (i == rx_wr_p % rxd_cnt) + seq_puts(file, " RX_WR"); + + seq_putc(file, '\n'); + } +out: + rtnl_unlock(); + return 0; +} + +static int nfp_net_debugfs_rx_q_open(struct inode *inode, struct file *f) +{ + return single_open(f, nfp_net_debugfs_rx_q_read, inode->i_private); +} + +static const struct file_operations nfp_rx_q_fops = { + .owner = THIS_MODULE, + .open = nfp_net_debugfs_rx_q_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek +}; + +static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data) +{ + struct nfp_net_tx_ring *tx_ring = file->private; + struct nfp_net_tx_desc *txd; + int d_rd_p, d_wr_p, txd_cnt; + struct sk_buff *skb; + struct nfp_net *nn; + int i; + + rtnl_lock(); + + if (!tx_ring->r_vec || !tx_ring->r_vec->nfp_net) + goto out; + nn = tx_ring->r_vec->nfp_net; + if (!netif_running(nn->netdev)) + goto out; + + txd_cnt = tx_ring->cnt; + + d_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q); + d_wr_p = nfp_qcp_wr_ptr_read(tx_ring->qcp_q); + + seq_printf(file, "TX[%02d]: H_RD=%d H_WR=%d D_RD=%d D_WR=%d\n", + tx_ring->idx, tx_ring->rd_p, tx_ring->wr_p, d_rd_p, d_wr_p); + + for (i = 0; i < txd_cnt; i++) { + txd = &tx_ring->txds[i]; + seq_printf(file, "%04d: 0x%08x 0x%08x 0x%08x 0x%08x", i, + txd->vals[0], txd->vals[1], + txd->vals[2], txd->vals[3]); + + skb = READ_ONCE(tx_ring->txbufs[i].skb); + if (skb) + seq_printf(file, " skb->head=%p skb->data=%p", + skb->head, skb->data); + if (tx_ring->txbufs[i].dma_addr) + seq_printf(file, " dma_addr=%pad", + &tx_ring->txbufs[i].dma_addr); + + if (i == tx_ring->rd_p % txd_cnt) + seq_puts(file, " H_RD"); + if (i == tx_ring->wr_p % txd_cnt) + seq_puts(file, " H_WR"); + if (i == d_rd_p % txd_cnt) + seq_puts(file, " D_RD"); + if (i == d_wr_p % txd_cnt) + seq_puts(file, " D_WR"); + + seq_putc(file, '\n'); + } +out: + rtnl_unlock(); + return 0; +} + +static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f) +{ + return single_open(f, nfp_net_debugfs_tx_q_read, inode->i_private); +} + +static const struct file_operations nfp_tx_q_fops = { + .owner = THIS_MODULE, + .open = nfp_net_debugfs_tx_q_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek +}; + +void nfp_net_debugfs_adapter_add(struct nfp_net *nn) +{ + static struct dentry *queues, *tx, *rx; + char int_name[16]; + int i; + + if (IS_ERR_OR_NULL(nfp_dir)) + return; + + nn->debugfs_dir = debugfs_create_dir(pci_name(nn->pdev), nfp_dir); + if (IS_ERR_OR_NULL(nn->debugfs_dir)) + return; + + /* Create queue debugging sub-tree */ + queues = debugfs_create_dir("queue", nn->debugfs_dir); + if (IS_ERR_OR_NULL(nn->debugfs_dir)) + return; + + rx = debugfs_create_dir("rx", queues); + tx = debugfs_create_dir("tx", queues); + if (IS_ERR_OR_NULL(rx) || IS_ERR_OR_NULL(tx)) + return; + + for (i = 0; i < nn->num_rx_rings; i++) { + sprintf(int_name, "%d", i); + debugfs_create_file(int_name, S_IRUSR, rx, + &nn->rx_rings[i], &nfp_rx_q_fops); + } + + for (i = 0; i < nn->num_tx_rings; i++) { + sprintf(int_name, "%d", i); + debugfs_create_file(int_name, S_IRUSR, tx, + &nn->tx_rings[i], &nfp_tx_q_fops); + } +} + +void nfp_net_debugfs_adapter_del(struct nfp_net *nn) +{ + debugfs_remove_recursive(nn->debugfs_dir); + nn->debugfs_dir = NULL; +} + +void nfp_net_debugfs_create(void) +{ + nfp_dir = debugfs_create_dir("nfp_net", NULL); +} + +void nfp_net_debugfs_destroy(void) +{ + debugfs_remove_recursive(nfp_dir); + nfp_dir = NULL; +} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c new file mode 100644 index 000000000000..9a4084a68db5 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -0,0 +1,640 @@ +/* + * Copyright (C) 2015 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * nfp_net_ethtool.c + * Netronome network device driver: ethtool support + * Authors: Jakub Kicinski + * Jason McMullan + * Rolf Neugebauer + * Brad Petrus + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "nfp_net_ctrl.h" +#include "nfp_net.h" + +/* Support for stats. Returns netdev, driver, and device stats */ +enum { NETDEV_ET_STATS, NFP_NET_DRV_ET_STATS, NFP_NET_DEV_ET_STATS }; +struct _nfp_net_et_stats { + char name[ETH_GSTRING_LEN]; + int type; + int sz; + int off; +}; + +#define NN_ET_NETDEV_STAT(m) NETDEV_ET_STATS, \ + FIELD_SIZEOF(struct net_device_stats, m), \ + offsetof(struct net_device_stats, m) +/* For stats in the control BAR (other than Q stats) */ +#define NN_ET_DEV_STAT(m) NFP_NET_DEV_ET_STATS, \ + sizeof(u64), \ + (m) +static const struct _nfp_net_et_stats nfp_net_et_stats[] = { + /* netdev stats */ + {"rx_packets", NN_ET_NETDEV_STAT(rx_packets)}, + {"tx_packets", NN_ET_NETDEV_STAT(tx_packets)}, + {"rx_bytes", NN_ET_NETDEV_STAT(rx_bytes)}, + {"tx_bytes", NN_ET_NETDEV_STAT(tx_bytes)}, + {"rx_errors", NN_ET_NETDEV_STAT(rx_errors)}, + {"tx_errors", NN_ET_NETDEV_STAT(tx_errors)}, + {"rx_dropped", NN_ET_NETDEV_STAT(rx_dropped)}, + {"tx_dropped", NN_ET_NETDEV_STAT(tx_dropped)}, + {"multicast", NN_ET_NETDEV_STAT(multicast)}, + {"collisions", NN_ET_NETDEV_STAT(collisions)}, + {"rx_over_errors", NN_ET_NETDEV_STAT(rx_over_errors)}, + {"rx_crc_errors", NN_ET_NETDEV_STAT(rx_crc_errors)}, + {"rx_frame_errors", NN_ET_NETDEV_STAT(rx_frame_errors)}, + {"rx_fifo_errors", NN_ET_NETDEV_STAT(rx_fifo_errors)}, + {"rx_missed_errors", NN_ET_NETDEV_STAT(rx_missed_errors)}, + {"tx_aborted_errors", NN_ET_NETDEV_STAT(tx_aborted_errors)}, + {"tx_carrier_errors", NN_ET_NETDEV_STAT(tx_carrier_errors)}, + {"tx_fifo_errors", NN_ET_NETDEV_STAT(tx_fifo_errors)}, + /* Stats from the device */ + {"dev_rx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_DISCARDS)}, + {"dev_rx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_ERRORS)}, + {"dev_rx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_OCTETS)}, + {"dev_rx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_UC_OCTETS)}, + {"dev_rx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_OCTETS)}, + {"dev_rx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_OCTETS)}, + {"dev_rx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_FRAMES)}, + {"dev_rx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_FRAMES)}, + {"dev_rx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_FRAMES)}, + + {"dev_tx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_DISCARDS)}, + {"dev_tx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_ERRORS)}, + {"dev_tx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_OCTETS)}, + {"dev_tx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_UC_OCTETS)}, + {"dev_tx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_OCTETS)}, + {"dev_tx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_OCTETS)}, + {"dev_tx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_FRAMES)}, + {"dev_tx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_FRAMES)}, + {"dev_tx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_FRAMES)}, +}; + +#define NN_ET_GLOBAL_STATS_LEN ARRAY_SIZE(nfp_net_et_stats) +#define NN_ET_RVEC_STATS_LEN (nn->num_r_vecs * 3) +#define NN_ET_RVEC_GATHER_STATS 7 +#define NN_ET_QUEUE_STATS_LEN ((nn->num_tx_rings + nn->num_rx_rings) * 2) +#define NN_ET_STATS_LEN (NN_ET_GLOBAL_STATS_LEN + NN_ET_RVEC_GATHER_STATS + \ + NN_ET_RVEC_STATS_LEN + NN_ET_QUEUE_STATS_LEN) + +static void nfp_net_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct nfp_net *nn = netdev_priv(netdev); + + strlcpy(drvinfo->driver, nfp_net_driver_name, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, nfp_net_driver_version, + sizeof(drvinfo->version)); + + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%d.%d", + nn->fw_ver.resv, nn->fw_ver.class, + nn->fw_ver.major, nn->fw_ver.minor); + strlcpy(drvinfo->bus_info, pci_name(nn->pdev), + sizeof(drvinfo->bus_info)); + + drvinfo->n_stats = NN_ET_STATS_LEN; + drvinfo->regdump_len = NFP_NET_CFG_BAR_SZ; +} + +static void nfp_net_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct nfp_net *nn = netdev_priv(netdev); + + ring->rx_max_pending = NFP_NET_MAX_RX_DESCS; + ring->tx_max_pending = NFP_NET_MAX_TX_DESCS; + ring->rx_pending = nn->rxd_cnt; + ring->tx_pending = nn->txd_cnt; +} + +static int nfp_net_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct nfp_net *nn = netdev_priv(netdev); + u32 rxd_cnt, txd_cnt; + + if (netif_running(netdev)) { + /* Some NIC drivers allow reconfiguration on the fly, + * some down the interface, change and then up it + * again. For now we don't allow changes when the + * device is up. + */ + nn_warn(nn, "Can't change rings while device is up\n"); + return -EBUSY; + } + + /* We don't have separate queues/rings for small/large frames. */ + if (ring->rx_mini_pending || ring->rx_jumbo_pending) + return -EINVAL; + + /* Round up to supported values */ + rxd_cnt = roundup_pow_of_two(ring->rx_pending); + rxd_cnt = max_t(u32, rxd_cnt, NFP_NET_MIN_RX_DESCS); + rxd_cnt = min_t(u32, rxd_cnt, NFP_NET_MAX_RX_DESCS); + + txd_cnt = roundup_pow_of_two(ring->tx_pending); + txd_cnt = max_t(u32, txd_cnt, NFP_NET_MIN_TX_DESCS); + txd_cnt = min_t(u32, txd_cnt, NFP_NET_MAX_TX_DESCS); + + if (nn->rxd_cnt != rxd_cnt || nn->txd_cnt != txd_cnt) + nn_dbg(nn, "Change ring size: RxQ %u->%u, TxQ %u->%u\n", + nn->rxd_cnt, rxd_cnt, nn->txd_cnt, txd_cnt); + + nn->rxd_cnt = rxd_cnt; + nn->txd_cnt = txd_cnt; + + return 0; +} + +static void nfp_net_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + struct nfp_net *nn = netdev_priv(netdev); + u8 *p = data; + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) { + memcpy(p, nfp_net_et_stats[i].name, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + for (i = 0; i < nn->num_r_vecs; i++) { + sprintf(p, "rvec_%u_rx_pkts", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rvec_%u_tx_pkts", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rvec_%u_tx_busy", i); + p += ETH_GSTRING_LEN; + } + strncpy(p, "hw_rx_csum_ok", ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + strncpy(p, "hw_rx_csum_inner_ok", ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + strncpy(p, "hw_rx_csum_err", ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + strncpy(p, "hw_tx_csum", ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + strncpy(p, "hw_tx_inner_csum", ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + strncpy(p, "tx_gather", ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + strncpy(p, "tx_lso", ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + for (i = 0; i < nn->num_tx_rings; i++) { + sprintf(p, "txq_%u_pkts", i); + p += ETH_GSTRING_LEN; + sprintf(p, "txq_%u_bytes", i); + p += ETH_GSTRING_LEN; + } + for (i = 0; i < nn->num_rx_rings; i++) { + sprintf(p, "rxq_%u_pkts", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rxq_%u_bytes", i); + p += ETH_GSTRING_LEN; + } + break; + } +} + +static void nfp_net_get_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + u64 gathered_stats[NN_ET_RVEC_GATHER_STATS] = {}; + struct nfp_net *nn = netdev_priv(netdev); + struct rtnl_link_stats64 *netdev_stats; + struct rtnl_link_stats64 temp = {}; + u64 tmp[NN_ET_RVEC_GATHER_STATS]; + u8 __iomem *io_p; + int i, j, k; + u8 *p; + + netdev_stats = dev_get_stats(netdev, &temp); + + for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) { + switch (nfp_net_et_stats[i].type) { + case NETDEV_ET_STATS: + p = (char *)netdev_stats + nfp_net_et_stats[i].off; + data[i] = nfp_net_et_stats[i].sz == sizeof(u64) ? + *(u64 *)p : *(u32 *)p; + break; + + case NFP_NET_DEV_ET_STATS: + io_p = nn->ctrl_bar + nfp_net_et_stats[i].off; + data[i] = readq(io_p); + break; + } + } + for (j = 0; j < nn->num_r_vecs; j++) { + unsigned int start; + + do { + start = u64_stats_fetch_begin(&nn->r_vecs[j].rx_sync); + data[i++] = nn->r_vecs[j].rx_pkts; + tmp[0] = nn->r_vecs[j].hw_csum_rx_ok; + tmp[1] = nn->r_vecs[j].hw_csum_rx_inner_ok; + tmp[2] = nn->r_vecs[j].hw_csum_rx_error; + } while (u64_stats_fetch_retry(&nn->r_vecs[j].rx_sync, start)); + + do { + start = u64_stats_fetch_begin(&nn->r_vecs[j].tx_sync); + data[i++] = nn->r_vecs[j].tx_pkts; + data[i++] = nn->r_vecs[j].tx_busy; + tmp[3] = nn->r_vecs[j].hw_csum_tx; + tmp[4] = nn->r_vecs[j].hw_csum_tx_inner; + tmp[5] = nn->r_vecs[j].tx_gather; + tmp[6] = nn->r_vecs[j].tx_lso; + } while (u64_stats_fetch_retry(&nn->r_vecs[j].tx_sync, start)); + + for (k = 0; k < NN_ET_RVEC_GATHER_STATS; k++) + gathered_stats[k] += tmp[k]; + } + for (j = 0; j < NN_ET_RVEC_GATHER_STATS; j++) + data[i++] = gathered_stats[j]; + for (j = 0; j < nn->num_tx_rings; j++) { + io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j); + data[i++] = readq(io_p); + io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j) + 8; + data[i++] = readq(io_p); + } + for (j = 0; j < nn->num_rx_rings; j++) { + io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j); + data[i++] = readq(io_p); + io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j) + 8; + data[i++] = readq(io_p); + } +} + +static int nfp_net_get_sset_count(struct net_device *netdev, int sset) +{ + struct nfp_net *nn = netdev_priv(netdev); + + switch (sset) { + case ETH_SS_STATS: + return NN_ET_STATS_LEN; + default: + return -EOPNOTSUPP; + } +} + +/* RX network flow classification (RSS, filters, etc) + */ +static u32 ethtool_flow_to_nfp_flag(u32 flow_type) +{ + static const u32 xlate_ethtool_to_nfp[IPV6_FLOW + 1] = { + [TCP_V4_FLOW] = NFP_NET_CFG_RSS_IPV4_TCP, + [TCP_V6_FLOW] = NFP_NET_CFG_RSS_IPV6_TCP, + [UDP_V4_FLOW] = NFP_NET_CFG_RSS_IPV4_UDP, + [UDP_V6_FLOW] = NFP_NET_CFG_RSS_IPV6_UDP, + [IPV4_FLOW] = NFP_NET_CFG_RSS_IPV4, + [IPV6_FLOW] = NFP_NET_CFG_RSS_IPV6, + }; + + if (flow_type >= ARRAY_SIZE(xlate_ethtool_to_nfp)) + return 0; + + return xlate_ethtool_to_nfp[flow_type]; +} + +static int nfp_net_get_rss_hash_opts(struct nfp_net *nn, + struct ethtool_rxnfc *cmd) +{ + u32 nfp_rss_flag; + + cmd->data = 0; + + if (!(nn->cap & NFP_NET_CFG_CTRL_RSS)) + return -EOPNOTSUPP; + + nfp_rss_flag = ethtool_flow_to_nfp_flag(cmd->flow_type); + if (!nfp_rss_flag) + return -EINVAL; + + cmd->data |= RXH_IP_SRC | RXH_IP_DST; + if (nn->rss_cfg & nfp_rss_flag) + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + + return 0; +} + +static int nfp_net_get_rxnfc(struct net_device *netdev, + struct ethtool_rxnfc *cmd, u32 *rule_locs) +{ + struct nfp_net *nn = netdev_priv(netdev); + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = nn->num_rx_rings; + return 0; + case ETHTOOL_GRXFH: + return nfp_net_get_rss_hash_opts(nn, cmd); + default: + return -EOPNOTSUPP; + } +} + +static int nfp_net_set_rss_hash_opt(struct nfp_net *nn, + struct ethtool_rxnfc *nfc) +{ + u32 new_rss_cfg = nn->rss_cfg; + u32 nfp_rss_flag; + int err; + + if (!(nn->cap & NFP_NET_CFG_CTRL_RSS)) + return -EOPNOTSUPP; + + /* RSS only supports IP SA/DA and L4 src/dst ports */ + if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3)) + return -EINVAL; + + /* We need at least the IP SA/DA fields for hashing */ + if (!(nfc->data & RXH_IP_SRC) || + !(nfc->data & RXH_IP_DST)) + return -EINVAL; + + nfp_rss_flag = ethtool_flow_to_nfp_flag(nfc->flow_type); + if (!nfp_rss_flag) + return -EINVAL; + + switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { + case 0: + new_rss_cfg &= ~nfp_rss_flag; + break; + case (RXH_L4_B_0_1 | RXH_L4_B_2_3): + new_rss_cfg |= nfp_rss_flag; + break; + default: + return -EINVAL; + } + + new_rss_cfg |= NFP_NET_CFG_RSS_TOEPLITZ; + new_rss_cfg |= NFP_NET_CFG_RSS_MASK; + + if (new_rss_cfg == nn->rss_cfg) + return 0; + + writel(new_rss_cfg, nn->ctrl_bar + NFP_NET_CFG_RSS_CTRL); + err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RSS); + if (err) + return err; + + nn->rss_cfg = new_rss_cfg; + + nn_dbg(nn, "Changed RSS config to 0x%x\n", nn->rss_cfg); + return 0; +} + +static int nfp_net_set_rxnfc(struct net_device *netdev, + struct ethtool_rxnfc *cmd) +{ + struct nfp_net *nn = netdev_priv(netdev); + + switch (cmd->cmd) { + case ETHTOOL_SRXFH: + return nfp_net_set_rss_hash_opt(nn, cmd); + default: + return -EOPNOTSUPP; + } +} + +static u32 nfp_net_get_rxfh_indir_size(struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + + if (!(nn->cap & NFP_NET_CFG_CTRL_RSS)) + return 0; + + return ARRAY_SIZE(nn->rss_itbl); +} + +static u32 nfp_net_get_rxfh_key_size(struct net_device *netdev) +{ + return NFP_NET_CFG_RSS_KEY_SZ; +} + +static int nfp_net_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, + u8 *hfunc) +{ + struct nfp_net *nn = netdev_priv(netdev); + int i; + + if (!(nn->cap & NFP_NET_CFG_CTRL_RSS)) + return -EOPNOTSUPP; + + if (indir) + for (i = 0; i < ARRAY_SIZE(nn->rss_itbl); i++) + indir[i] = nn->rss_itbl[i]; + if (key) + memcpy(key, nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ); + if (hfunc) + *hfunc = ETH_RSS_HASH_TOP; + + return 0; +} + +static int nfp_net_set_rxfh(struct net_device *netdev, + const u32 *indir, const u8 *key, + const u8 hfunc) +{ + struct nfp_net *nn = netdev_priv(netdev); + int i; + + if (!(nn->cap & NFP_NET_CFG_CTRL_RSS) || + !(hfunc == ETH_RSS_HASH_NO_CHANGE || hfunc == ETH_RSS_HASH_TOP)) + return -EOPNOTSUPP; + + if (!key && !indir) + return 0; + + if (key) { + memcpy(nn->rss_key, key, NFP_NET_CFG_RSS_KEY_SZ); + nfp_net_rss_write_key(nn); + } + if (indir) { + for (i = 0; i < ARRAY_SIZE(nn->rss_itbl); i++) + nn->rss_itbl[i] = indir[i]; + + nfp_net_rss_write_itbl(nn); + } + + return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RSS); +} + +/* Dump BAR registers + */ +static int nfp_net_get_regs_len(struct net_device *netdev) +{ + return NFP_NET_CFG_BAR_SZ; +} + +static void nfp_net_get_regs(struct net_device *netdev, + struct ethtool_regs *regs, void *p) +{ + struct nfp_net *nn = netdev_priv(netdev); + u32 *regs_buf = p; + int i; + + regs->version = nn_readl(nn, NFP_NET_CFG_VERSION); + + for (i = 0; i < NFP_NET_CFG_BAR_SZ / sizeof(u32); i++) + regs_buf[i] = readl(nn->ctrl_bar + (i * sizeof(u32))); +} + +static int nfp_net_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct nfp_net *nn = netdev_priv(netdev); + + if (!(nn->cap & NFP_NET_CFG_CTRL_IRQMOD)) + return -EINVAL; + + ec->rx_coalesce_usecs = nn->rx_coalesce_usecs; + ec->rx_max_coalesced_frames = nn->rx_coalesce_max_frames; + ec->tx_coalesce_usecs = nn->tx_coalesce_usecs; + ec->tx_max_coalesced_frames = nn->tx_coalesce_max_frames; + + return 0; +} + +static int nfp_net_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct nfp_net *nn = netdev_priv(netdev); + unsigned int factor; + + if (ec->rx_coalesce_usecs_irq || + ec->rx_max_coalesced_frames_irq || + ec->tx_coalesce_usecs_irq || + ec->tx_max_coalesced_frames_irq || + ec->stats_block_coalesce_usecs || + ec->use_adaptive_rx_coalesce || + ec->use_adaptive_tx_coalesce || + ec->pkt_rate_low || + ec->rx_coalesce_usecs_low || + ec->rx_max_coalesced_frames_low || + ec->tx_coalesce_usecs_low || + ec->tx_max_coalesced_frames_low || + ec->pkt_rate_high || + ec->rx_coalesce_usecs_high || + ec->rx_max_coalesced_frames_high || + ec->tx_coalesce_usecs_high || + ec->tx_max_coalesced_frames_high || + ec->rate_sample_interval) + return -ENOTSUPP; + + /* Compute factor used to convert coalesce '_usecs' parameters to + * ME timestamp ticks. There are 16 ME clock cycles for each timestamp + * count. + */ + factor = nn->me_freq_mhz / 16; + + /* Each pair of (usecs, max_frames) fields specifies that interrupts + * should be coalesced until + * (usecs > 0 && time_since_first_completion >= usecs) || + * (max_frames > 0 && completed_frames >= max_frames) + * + * It is illegal to set both usecs and max_frames to zero as this would + * cause interrupts to never be generated. To disable coalescing, set + * usecs = 0 and max_frames = 1. + * + * Some implementations ignore the value of max_frames and use the + * condition time_since_first_completion >= usecs + */ + + if (!(nn->cap & NFP_NET_CFG_CTRL_IRQMOD)) + return -EINVAL; + + /* ensure valid configuration */ + if (!ec->rx_coalesce_usecs && !ec->rx_max_coalesced_frames) + return -EINVAL; + + if (!ec->tx_coalesce_usecs && !ec->tx_max_coalesced_frames) + return -EINVAL; + + if (ec->rx_coalesce_usecs * factor >= ((1 << 16) - 1)) + return -EINVAL; + + if (ec->tx_coalesce_usecs * factor >= ((1 << 16) - 1)) + return -EINVAL; + + if (ec->rx_max_coalesced_frames >= ((1 << 16) - 1)) + return -EINVAL; + + if (ec->tx_max_coalesced_frames >= ((1 << 16) - 1)) + return -EINVAL; + + /* configuration is valid */ + nn->rx_coalesce_usecs = ec->rx_coalesce_usecs; + nn->rx_coalesce_max_frames = ec->rx_max_coalesced_frames; + nn->tx_coalesce_usecs = ec->tx_coalesce_usecs; + nn->tx_coalesce_max_frames = ec->tx_max_coalesced_frames; + + /* write configuration to device */ + nfp_net_coalesce_write_cfg(nn); + return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_IRQMOD); +} + +static const struct ethtool_ops nfp_net_ethtool_ops = { + .get_drvinfo = nfp_net_get_drvinfo, + .get_ringparam = nfp_net_get_ringparam, + .set_ringparam = nfp_net_set_ringparam, + .get_strings = nfp_net_get_strings, + .get_ethtool_stats = nfp_net_get_stats, + .get_sset_count = nfp_net_get_sset_count, + .get_rxnfc = nfp_net_get_rxnfc, + .set_rxnfc = nfp_net_set_rxnfc, + .get_rxfh_indir_size = nfp_net_get_rxfh_indir_size, + .get_rxfh_key_size = nfp_net_get_rxfh_key_size, + .get_rxfh = nfp_net_get_rxfh, + .set_rxfh = nfp_net_set_rxfh, + .get_regs_len = nfp_net_get_regs_len, + .get_regs = nfp_net_get_regs, + .get_coalesce = nfp_net_get_coalesce, + .set_coalesce = nfp_net_set_coalesce, +}; + +void nfp_net_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &nfp_net_ethtool_ops; +} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c new file mode 100644 index 000000000000..e2b22b8a20f1 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2015 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * nfp_netvf_main.c + * Netronome virtual function network device driver: Main entry point + * Author: Jason McMullan + * Rolf Neugebauer + */ + +#include +#include +#include +#include +#include + +#include "nfp_net_ctrl.h" +#include "nfp_net.h" + +const char nfp_net_driver_name[] = "nfp_netvf"; +const char nfp_net_driver_version[] = "0.1"; +#define PCI_DEVICE_NFP6000VF 0x6003 +static const struct pci_device_id nfp_netvf_pci_device_ids[] = { + { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_NFP6000VF, + PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID, + PCI_ANY_ID, 0, + }, + { 0, } /* Required last entry. */ +}; +MODULE_DEVICE_TABLE(pci, nfp_netvf_pci_device_ids); + +static void nfp_netvf_get_mac_addr(struct nfp_net *nn) +{ + u8 mac_addr[ETH_ALEN]; + + put_unaligned_be32(nn_readl(nn, NFP_NET_CFG_MACADDR + 0), &mac_addr[0]); + /* We can't do readw for NFP-3200 compatibility */ + put_unaligned_be16(nn_readl(nn, NFP_NET_CFG_MACADDR + 4) >> 16, + &mac_addr[4]); + + if (!is_valid_ether_addr(mac_addr)) { + eth_hw_addr_random(nn->netdev); + return; + } + + ether_addr_copy(nn->netdev->dev_addr, mac_addr); + ether_addr_copy(nn->netdev->perm_addr, mac_addr); +} + +static int nfp_netvf_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_id) +{ + struct nfp_net_fw_version fw_ver; + int max_tx_rings, max_rx_rings; + u32 tx_bar_off, rx_bar_off; + u32 tx_bar_sz, rx_bar_sz; + int tx_bar_no, rx_bar_no; + u8 __iomem *ctrl_bar; + struct nfp_net *nn; + int is_nfp3200; + u32 startq; + int stride; + int err; + + err = pci_enable_device_mem(pdev); + if (err) + return err; + + err = pci_request_regions(pdev, nfp_net_driver_name); + if (err) { + dev_err(&pdev->dev, "Unable to allocate device memory.\n"); + goto err_pci_disable; + } + + switch (pdev->device) { + case PCI_DEVICE_NFP6000VF: + is_nfp3200 = 0; + break; + default: + err = -ENODEV; + goto err_pci_regions; + } + + pci_set_master(pdev); + + err = dma_set_mask_and_coherent(&pdev->dev, + DMA_BIT_MASK(NFP_NET_MAX_DMA_BITS)); + if (err) + goto err_pci_regions; + + /* Map the Control BAR. + * + * Irrespective of the advertised BAR size we only map the + * first NFP_NET_CFG_BAR_SZ of the BAR. This keeps the code + * the identical for PF and VF drivers. + */ + ctrl_bar = ioremap_nocache(pci_resource_start(pdev, NFP_NET_CRTL_BAR), + NFP_NET_CFG_BAR_SZ); + if (!ctrl_bar) { + dev_err(&pdev->dev, + "Failed to map resource %d\n", NFP_NET_CRTL_BAR); + err = -EIO; + goto err_pci_regions; + } + + nfp_net_get_fw_version(&fw_ver, ctrl_bar); + if (fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { + dev_err(&pdev->dev, "Unknown Firmware ABI %d.%d.%d.%d\n", + fw_ver.resv, fw_ver.class, fw_ver.major, fw_ver.minor); + err = -EINVAL; + goto err_ctrl_unmap; + } + + /* Determine stride */ + if (nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0, 0) || + nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0, 1) || + nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0x12, 0x48)) { + stride = 2; + tx_bar_no = NFP_NET_Q0_BAR; + rx_bar_no = NFP_NET_Q1_BAR; + dev_warn(&pdev->dev, "OBSOLETE Firmware detected - VF isolation not available\n"); + } else { + switch (fw_ver.major) { + case 1 ... 3: + if (is_nfp3200) { + stride = 2; + tx_bar_no = NFP_NET_Q0_BAR; + rx_bar_no = NFP_NET_Q1_BAR; + } else { + stride = 4; + tx_bar_no = NFP_NET_Q0_BAR; + rx_bar_no = tx_bar_no; + } + break; + default: + dev_err(&pdev->dev, "Unsupported Firmware ABI %d.%d.%d.%d\n", + fw_ver.resv, fw_ver.class, + fw_ver.major, fw_ver.minor); + err = -EINVAL; + goto err_ctrl_unmap; + } + } + + /* Find out how many rings are supported */ + max_tx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_TXRINGS); + max_rx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_RXRINGS); + + tx_bar_sz = NFP_QCP_QUEUE_ADDR_SZ * max_tx_rings * stride; + rx_bar_sz = NFP_QCP_QUEUE_ADDR_SZ * max_rx_rings * stride; + + /* Sanity checks */ + if (tx_bar_sz > pci_resource_len(pdev, tx_bar_no)) { + dev_err(&pdev->dev, + "TX BAR too small for number of TX rings. Adjusting\n"); + tx_bar_sz = pci_resource_len(pdev, tx_bar_no); + max_tx_rings = (tx_bar_sz / NFP_QCP_QUEUE_ADDR_SZ) / 2; + } + if (rx_bar_sz > pci_resource_len(pdev, rx_bar_no)) { + dev_err(&pdev->dev, + "RX BAR too small for number of RX rings. Adjusting\n"); + rx_bar_sz = pci_resource_len(pdev, rx_bar_no); + max_rx_rings = (rx_bar_sz / NFP_QCP_QUEUE_ADDR_SZ) / 2; + } + + /* XXX Implement a workaround for THB-350 here. Ideally, we + * have a different PCI ID for A rev VFs. + */ + switch (pdev->device) { + case PCI_DEVICE_NFP6000VF: + startq = readl(ctrl_bar + NFP_NET_CFG_START_TXQ); + tx_bar_off = NFP_PCIE_QUEUE(startq); + startq = readl(ctrl_bar + NFP_NET_CFG_START_RXQ); + rx_bar_off = NFP_PCIE_QUEUE(startq); + break; + default: + err = -ENODEV; + goto err_ctrl_unmap; + } + + /* Allocate and initialise the netdev */ + nn = nfp_net_netdev_alloc(pdev, max_tx_rings, max_rx_rings); + if (IS_ERR(nn)) { + err = PTR_ERR(nn); + goto err_ctrl_unmap; + } + + nn->fw_ver = fw_ver; + nn->ctrl_bar = ctrl_bar; + nn->is_vf = 1; + nn->is_nfp3200 = is_nfp3200; + nn->stride_tx = stride; + nn->stride_rx = stride; + + if (rx_bar_no == tx_bar_no) { + u32 bar_off, bar_sz; + resource_size_t map_addr; + + /* Make a single overlapping BAR mapping */ + if (tx_bar_off < rx_bar_off) + bar_off = tx_bar_off; + else + bar_off = rx_bar_off; + + if ((tx_bar_off + tx_bar_sz) > (rx_bar_off + rx_bar_sz)) + bar_sz = (tx_bar_off + tx_bar_sz) - bar_off; + else + bar_sz = (rx_bar_off + rx_bar_sz) - bar_off; + + map_addr = pci_resource_start(pdev, tx_bar_no) + bar_off; + nn->q_bar = ioremap_nocache(map_addr, bar_sz); + if (!nn->q_bar) { + nn_err(nn, "Failed to map resource %d\n", tx_bar_no); + err = -EIO; + goto err_netdev_free; + } + + /* TX queues */ + nn->tx_bar = nn->q_bar + (tx_bar_off - bar_off); + /* RX queues */ + nn->rx_bar = nn->q_bar + (rx_bar_off - bar_off); + } else { + resource_size_t map_addr; + + /* TX queues */ + map_addr = pci_resource_start(pdev, tx_bar_no) + tx_bar_off; + nn->tx_bar = ioremap_nocache(map_addr, tx_bar_sz); + if (!nn->tx_bar) { + nn_err(nn, "Failed to map resource %d\n", tx_bar_no); + err = -EIO; + goto err_netdev_free; + } + + /* RX queues */ + map_addr = pci_resource_start(pdev, rx_bar_no) + rx_bar_off; + nn->rx_bar = ioremap_nocache(map_addr, rx_bar_sz); + if (!nn->rx_bar) { + nn_err(nn, "Failed to map resource %d\n", rx_bar_no); + err = -EIO; + goto err_unmap_tx; + } + } + + nfp_netvf_get_mac_addr(nn); + + err = nfp_net_irqs_alloc(nn); + if (!err) { + nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting\n"); + err = -EIO; + goto err_unmap_rx; + } + + /* Get ME clock frequency from ctrl BAR + * XXX for now frequency is hardcoded until we figure out how + * to get the value from nfp-hwinfo into ctrl bar + */ + nn->me_freq_mhz = 1200; + + err = nfp_net_netdev_init(nn->netdev); + if (err) + goto err_irqs_disable; + + pci_set_drvdata(pdev, nn); + + nfp_net_info(nn); + nfp_net_debugfs_adapter_add(nn); + + return 0; + +err_irqs_disable: + nfp_net_irqs_disable(nn); +err_unmap_rx: + if (!nn->q_bar) + iounmap(nn->rx_bar); +err_unmap_tx: + if (!nn->q_bar) + iounmap(nn->tx_bar); + else + iounmap(nn->q_bar); +err_netdev_free: + pci_set_drvdata(pdev, NULL); + nfp_net_netdev_free(nn); +err_ctrl_unmap: + iounmap(ctrl_bar); +err_pci_regions: + pci_release_regions(pdev); +err_pci_disable: + pci_disable_device(pdev); + return err; +} + +static void nfp_netvf_pci_remove(struct pci_dev *pdev) +{ + struct nfp_net *nn = pci_get_drvdata(pdev); + + /* Note, the order is slightly different from above as we need + * to keep the nn pointer around till we have freed everything. + */ + nfp_net_debugfs_adapter_del(nn); + + nfp_net_netdev_clean(nn->netdev); + + nfp_net_irqs_disable(nn); + + if (!nn->q_bar) { + iounmap(nn->rx_bar); + iounmap(nn->tx_bar); + } else { + iounmap(nn->q_bar); + } + iounmap(nn->ctrl_bar); + + pci_set_drvdata(pdev, NULL); + + nfp_net_netdev_free(nn); + + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static struct pci_driver nfp_netvf_pci_driver = { + .name = nfp_net_driver_name, + .id_table = nfp_netvf_pci_device_ids, + .probe = nfp_netvf_pci_probe, + .remove = nfp_netvf_pci_remove, +}; + +static int __init nfp_netvf_init(void) +{ + int err; + + pr_info("%s: NFP VF Network driver, Copyright (C) 2014-2015 Netronome Systems\n", + nfp_net_driver_name); + + nfp_net_debugfs_create(); + err = pci_register_driver(&nfp_netvf_pci_driver); + if (err) { + nfp_net_debugfs_destroy(); + return err; + } + + return 0; +} + +static void __exit nfp_netvf_exit(void) +{ + pci_unregister_driver(&nfp_netvf_pci_driver); + nfp_net_debugfs_destroy(); +} + +module_init(nfp_netvf_init); +module_exit(nfp_netvf_exit); + +MODULE_AUTHOR("Netronome Systems "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("NFP VF network device driver"); -- cgit v1.2.3-59-g8ed1b From f5d7837f96e53a8c9b6c49e1bc95cf0ae88b99e8 Mon Sep 17 00:00:00 2001 From: Kazuya Mizuguchi Date: Wed, 2 Dec 2015 02:04:39 +0900 Subject: ravb: ptp: Add CONFIG mode support This patch makes PTP support active in CONFIG mode on R-Car Gen3. Signed-off-by: Kazuya Mizuguchi Signed-off-by: Yoshihiro Kaneko Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/ravb.h | 1 + drivers/net/ethernet/renesas/ravb_main.c | 33 +++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h index f9dee7436e81..9fbe92ac225b 100644 --- a/drivers/net/ethernet/renesas/ravb.h +++ b/drivers/net/ethernet/renesas/ravb.h @@ -206,6 +206,7 @@ enum CCC_BIT { CCC_OPC_RESET = 0x00000000, CCC_OPC_CONFIG = 0x00000001, CCC_OPC_OPERATION = 0x00000002, + CCC_GAC = 0x00000080, CCC_DTSR = 0x00000100, CCC_CSEL = 0x00030000, CCC_CSEL_HPB = 0x00010000, diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 990dc55cdada..293046d30b3b 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1231,7 +1231,8 @@ static int ravb_open(struct net_device *ndev) ravb_emac_init(ndev); /* Initialise PTP Clock driver */ - ravb_ptp_init(ndev, priv->pdev); + if (priv->chip_id == RCAR_GEN2) + ravb_ptp_init(ndev, priv->pdev); netif_tx_start_all_queues(ndev); @@ -1244,7 +1245,8 @@ static int ravb_open(struct net_device *ndev) out_ptp_stop: /* Stop PTP Clock driver */ - ravb_ptp_stop(ndev); + if (priv->chip_id == RCAR_GEN2) + ravb_ptp_stop(ndev); out_free_irq: free_irq(ndev->irq, ndev); free_irq(priv->emac_irq, ndev); @@ -1476,7 +1478,8 @@ static int ravb_close(struct net_device *ndev) ravb_write(ndev, 0, TIC); /* Stop PTP Clock driver */ - ravb_ptp_stop(ndev); + if (priv->chip_id == RCAR_GEN2) + ravb_ptp_stop(ndev); /* Set the config mode to stop the AVB-DMAC's processes */ if (ravb_stop_dma(ndev) < 0) @@ -1781,8 +1784,16 @@ static int ravb_probe(struct platform_device *pdev) ndev->ethtool_ops = &ravb_ethtool_ops; /* Set AVB config mode */ - ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_OPC) | CCC_OPC_CONFIG, - CCC); + if (chip_id == RCAR_GEN2) { + ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_OPC) | + CCC_OPC_CONFIG, CCC); + /* Set CSEL value */ + ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_CSEL) | + CCC_CSEL_HPB, CCC); + } else { + ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_OPC) | + CCC_OPC_CONFIG | CCC_GAC | CCC_CSEL_HPB, CCC); + } /* Set CSEL value */ ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_CSEL) | CCC_CSEL_HPB, @@ -1814,6 +1825,10 @@ static int ravb_probe(struct platform_device *pdev) /* Initialise HW timestamp list */ INIT_LIST_HEAD(&priv->ts_skb_list); + /* Initialise PTP Clock driver */ + if (chip_id != RCAR_GEN2) + ravb_ptp_init(ndev, pdev); + /* Debug message level */ priv->msg_enable = RAVB_DEF_MSG_ENABLE; @@ -1855,6 +1870,10 @@ out_napi_del: out_dma_free: dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat, priv->desc_bat_dma); + + /* Stop PTP Clock driver */ + if (chip_id != RCAR_GEN2) + ravb_ptp_stop(ndev); out_release: if (ndev) free_netdev(ndev); @@ -1869,6 +1888,10 @@ static int ravb_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct ravb_private *priv = netdev_priv(ndev); + /* Stop PTP Clock driver */ + if (priv->chip_id != RCAR_GEN2) + ravb_ptp_stop(ndev); + dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat, priv->desc_bat_dma); /* Set reset mode */ -- cgit v1.2.3-59-g8ed1b From 0e8743611a76ced8b0971b6894f982ce89624cc4 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 2 Dec 2015 14:58:32 +0900 Subject: ravb: add fallback compatibility strings Add fallback compatibility strings for R-Car Gen 2 & 3 SoC Families. This is in keeping with the fallback scheme being adopted wherever appropriate for drivers for Renesas SoCs. Signed-off-by: Simon Horman Acked-by: Rob Herring Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/renesas,ravb.txt | 9 ++++++++- drivers/net/ethernet/renesas/ravb_main.c | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/net/renesas,ravb.txt b/Documentation/devicetree/bindings/net/renesas,ravb.txt index b486f3f5f6a3..7555116b5db2 100644 --- a/Documentation/devicetree/bindings/net/renesas,ravb.txt +++ b/Documentation/devicetree/bindings/net/renesas,ravb.txt @@ -7,6 +7,13 @@ Required properties: - compatible: "renesas,etheravb-r8a7790" if the device is a part of R8A7790 SoC. "renesas,etheravb-r8a7794" if the device is a part of R8A7794 SoC. "renesas,etheravb-r8a7795" if the device is a part of R8A7795 SoC. + "renesas,etheravb-rcar-gen2" for generic R-Car Gen 2 compatible interface. + "renesas,etheravb-rcar-gen3" for generic R-Car Gen 3 compatible interface. + + When compatible with the generic version, nodes must list the + SoC-specific version corresponding to the platform first + followed by the generic version. + - reg: offset and length of (1) the register block and (2) the stream buffer. - interrupts: A list of interrupt-specifiers, one for each entry in interrupt-names. @@ -37,7 +44,7 @@ Optional properties: Example: ethernet@e6800000 { - compatible = "renesas,etheravb-r8a7795"; + compatible = "renesas,etheravb-r8a7795", "renesas,etheravb-rcar-gen3"; reg = <0 0xe6800000 0 0x800>, <0 0xe6a00000 0 0x10000>; interrupt-parent = <&gic>; interrupts = , diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 293046d30b3b..2c613d367873 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1659,7 +1659,9 @@ static int ravb_mdio_release(struct ravb_private *priv) static const struct of_device_id ravb_match_table[] = { { .compatible = "renesas,etheravb-r8a7790", .data = (void *)RCAR_GEN2 }, { .compatible = "renesas,etheravb-r8a7794", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,etheravb-rcar-gen2", .data = (void *)RCAR_GEN2 }, { .compatible = "renesas,etheravb-r8a7795", .data = (void *)RCAR_GEN3 }, + { .compatible = "renesas,etheravb-rcar-gen3", .data = (void *)RCAR_GEN3 }, { } }; MODULE_DEVICE_TABLE(of, ravb_match_table); -- cgit v1.2.3-59-g8ed1b From 98bb892821c1ad3781b8c7daec2fc8a8de3390c9 Mon Sep 17 00:00:00 2001 From: Asias He Date: Wed, 2 Dec 2015 14:44:02 +0800 Subject: VSOCK: Introduce vhost-vsock.ko VM sockets vhost transport implementation. This module runs in host kernel. Signed-off-by: Asias He Signed-off-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- drivers/vhost/vsock.c | 631 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/vhost/vsock.h | 4 + 2 files changed, 635 insertions(+) create mode 100644 drivers/vhost/vsock.c create mode 100644 drivers/vhost/vsock.h (limited to 'drivers') diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c new file mode 100644 index 000000000000..65b1cf8a06cb --- /dev/null +++ b/drivers/vhost/vsock.c @@ -0,0 +1,631 @@ +/* + * vhost transport for vsock + * + * Copyright (C) 2013-2015 Red Hat, Inc. + * Author: Asias He + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU GPL, version 2. + */ +#include +#include +#include +#include +#include +#include + +#include +#include "vhost.h" +#include "vsock.h" + +#define VHOST_VSOCK_DEFAULT_HOST_CID 2 + +static int vhost_transport_socket_init(struct vsock_sock *vsk, + struct vsock_sock *psk); + +enum { + VHOST_VSOCK_FEATURES = VHOST_FEATURES, +}; + +/* Used to track all the vhost_vsock instances on the system. */ +static LIST_HEAD(vhost_vsock_list); +static DEFINE_MUTEX(vhost_vsock_mutex); + +struct vhost_vsock_virtqueue { + struct vhost_virtqueue vq; +}; + +struct vhost_vsock { + /* Vhost device */ + struct vhost_dev dev; + /* Vhost vsock virtqueue*/ + struct vhost_vsock_virtqueue vqs[VSOCK_VQ_MAX]; + /* Link to global vhost_vsock_list*/ + struct list_head list; + /* Head for pkt from host to guest */ + struct list_head send_pkt_list; + /* Work item to send pkt */ + struct vhost_work send_pkt_work; + /* Wait queue for send pkt */ + wait_queue_head_t queue_wait; + /* Used for global tx buf limitation */ + u32 total_tx_buf; + /* Guest contex id this vhost_vsock instance handles */ + u32 guest_cid; +}; + +static u32 vhost_transport_get_local_cid(void) +{ + u32 cid = VHOST_VSOCK_DEFAULT_HOST_CID; + return cid; +} + +static struct vhost_vsock *vhost_vsock_get(u32 guest_cid) +{ + struct vhost_vsock *vsock; + + mutex_lock(&vhost_vsock_mutex); + list_for_each_entry(vsock, &vhost_vsock_list, list) { + if (vsock->guest_cid == guest_cid) { + mutex_unlock(&vhost_vsock_mutex); + return vsock; + } + } + mutex_unlock(&vhost_vsock_mutex); + + return NULL; +} + +static void +vhost_transport_do_send_pkt(struct vhost_vsock *vsock, + struct vhost_virtqueue *vq) +{ + bool added = false; + + mutex_lock(&vq->mutex); + vhost_disable_notify(&vsock->dev, vq); + for (;;) { + struct virtio_vsock_pkt *pkt; + struct iov_iter iov_iter; + unsigned out, in; + struct sock *sk; + size_t nbytes; + size_t len; + int head; + + if (list_empty(&vsock->send_pkt_list)) { + vhost_enable_notify(&vsock->dev, vq); + break; + } + + head = vhost_get_vq_desc(vq, vq->iov, ARRAY_SIZE(vq->iov), + &out, &in, NULL, NULL); + pr_debug("%s: head = %d\n", __func__, head); + if (head < 0) + break; + + if (head == vq->num) { + if (unlikely(vhost_enable_notify(&vsock->dev, vq))) { + vhost_disable_notify(&vsock->dev, vq); + continue; + } + break; + } + + pkt = list_first_entry(&vsock->send_pkt_list, + struct virtio_vsock_pkt, list); + list_del_init(&pkt->list); + + if (out) { + virtio_transport_free_pkt(pkt); + vq_err(vq, "Expected 0 output buffers, got %u\n", out); + break; + } + + len = iov_length(&vq->iov[out], in); + iov_iter_init(&iov_iter, READ, &vq->iov[out], in, len); + + nbytes = copy_to_iter(&pkt->hdr, sizeof(pkt->hdr), &iov_iter); + if (nbytes != sizeof(pkt->hdr)) { + virtio_transport_free_pkt(pkt); + vq_err(vq, "Faulted on copying pkt hdr\n"); + break; + } + + nbytes = copy_to_iter(pkt->buf, pkt->len, &iov_iter); + if (nbytes != pkt->len) { + virtio_transport_free_pkt(pkt); + vq_err(vq, "Faulted on copying pkt buf\n"); + break; + } + + vhost_add_used(vq, head, pkt->len); /* TODO should this be sizeof(pkt->hdr) + pkt->len? */ + added = true; + + virtio_transport_dec_tx_pkt(pkt); + vsock->total_tx_buf -= pkt->len; + + sk = sk_vsock(pkt->trans->vsk); + /* Release refcnt taken in vhost_transport_send_pkt */ + sock_put(sk); + + virtio_transport_free_pkt(pkt); + } + if (added) + vhost_signal(&vsock->dev, vq); + mutex_unlock(&vq->mutex); + + if (added) + wake_up(&vsock->queue_wait); +} + +static void vhost_transport_send_pkt_work(struct vhost_work *work) +{ + struct vhost_virtqueue *vq; + struct vhost_vsock *vsock; + + vsock = container_of(work, struct vhost_vsock, send_pkt_work); + vq = &vsock->vqs[VSOCK_VQ_RX].vq; + + vhost_transport_do_send_pkt(vsock, vq); +} + +static int +vhost_transport_send_pkt(struct vsock_sock *vsk, + struct virtio_vsock_pkt_info *info) +{ + u32 src_cid, src_port, dst_cid, dst_port; + struct virtio_transport *trans; + struct virtio_vsock_pkt *pkt; + struct vhost_virtqueue *vq; + struct vhost_vsock *vsock; + u32 pkt_len = info->pkt_len; + DEFINE_WAIT(wait); + + src_cid = vhost_transport_get_local_cid(); + src_port = vsk->local_addr.svm_port; + if (!info->remote_cid) { + dst_cid = vsk->remote_addr.svm_cid; + dst_port = vsk->remote_addr.svm_port; + } else { + dst_cid = info->remote_cid; + dst_port = info->remote_port; + } + + /* Find the vhost_vsock according to guest context id */ + vsock = vhost_vsock_get(dst_cid); + if (!vsock) + return -ENODEV; + + trans = vsk->trans; + vq = &vsock->vqs[VSOCK_VQ_RX].vq; + + /* we can send less than pkt_len bytes */ + if (pkt_len > VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE) + pkt_len = VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE; + + /* virtio_transport_get_credit might return less than pkt_len credit */ + pkt_len = virtio_transport_get_credit(trans, pkt_len); + + /* Do not send zero length OP_RW pkt*/ + if (pkt_len == 0 && info->op == VIRTIO_VSOCK_OP_RW) + return pkt_len; + + /* Respect global tx buf limitation */ + mutex_lock(&vq->mutex); + while (pkt_len + vsock->total_tx_buf > VIRTIO_VSOCK_MAX_TX_BUF_SIZE) { + prepare_to_wait_exclusive(&vsock->queue_wait, &wait, + TASK_UNINTERRUPTIBLE); + mutex_unlock(&vq->mutex); + schedule(); + mutex_lock(&vq->mutex); + finish_wait(&vsock->queue_wait, &wait); + } + vsock->total_tx_buf += pkt_len; + mutex_unlock(&vq->mutex); + + pkt = virtio_transport_alloc_pkt(vsk, info, pkt_len, + src_cid, src_port, + dst_cid, dst_port); + if (!pkt) { + mutex_lock(&vq->mutex); + vsock->total_tx_buf -= pkt_len; + mutex_unlock(&vq->mutex); + virtio_transport_put_credit(trans, pkt_len); + return -ENOMEM; + } + + pr_debug("%s:info->pkt_len= %d\n", __func__, pkt_len); + /* Released in vhost_transport_do_send_pkt */ + sock_hold(&trans->vsk->sk); + virtio_transport_inc_tx_pkt(pkt); + + /* Queue it up in vhost work */ + mutex_lock(&vq->mutex); + list_add_tail(&pkt->list, &vsock->send_pkt_list); + vhost_work_queue(&vsock->dev, &vsock->send_pkt_work); + mutex_unlock(&vq->mutex); + + return pkt_len; +} + +static struct virtio_transport_pkt_ops vhost_ops = { + .send_pkt = vhost_transport_send_pkt, +}; + +static struct virtio_vsock_pkt * +vhost_vsock_alloc_pkt(struct vhost_virtqueue *vq, + unsigned int out, unsigned int in) +{ + struct virtio_vsock_pkt *pkt; + struct iov_iter iov_iter; + size_t nbytes; + size_t len; + + if (in != 0) { + vq_err(vq, "Expected 0 input buffers, got %u\n", in); + return NULL; + } + + pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); + if (!pkt) + return NULL; + + len = iov_length(vq->iov, out); + iov_iter_init(&iov_iter, WRITE, vq->iov, out, len); + + nbytes = copy_from_iter(&pkt->hdr, sizeof(pkt->hdr), &iov_iter); + if (nbytes != sizeof(pkt->hdr)) { + vq_err(vq, "Expected %zu bytes for pkt->hdr, got %zu bytes\n", + sizeof(pkt->hdr), nbytes); + kfree(pkt); + return NULL; + } + + if (le16_to_cpu(pkt->hdr.type) == VIRTIO_VSOCK_TYPE_DGRAM) + pkt->len = le32_to_cpu(pkt->hdr.len) & 0XFFFF; + else if (le16_to_cpu(pkt->hdr.type) == VIRTIO_VSOCK_TYPE_STREAM) + pkt->len = le32_to_cpu(pkt->hdr.len); + + /* No payload */ + if (!pkt->len) + return pkt; + + /* The pkt is too big */ + if (pkt->len > VIRTIO_VSOCK_MAX_PKT_BUF_SIZE) { + kfree(pkt); + return NULL; + } + + pkt->buf = kmalloc(pkt->len, GFP_KERNEL); + if (!pkt->buf) { + kfree(pkt); + return NULL; + } + + nbytes = copy_from_iter(pkt->buf, pkt->len, &iov_iter); + if (nbytes != pkt->len) { + vq_err(vq, "Expected %u byte payload, got %zu bytes\n", + pkt->len, nbytes); + virtio_transport_free_pkt(pkt); + return NULL; + } + + return pkt; +} + +static void vhost_vsock_handle_ctl_kick(struct vhost_work *work) +{ + struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, + poll.work); + struct vhost_vsock *vsock = container_of(vq->dev, struct vhost_vsock, + dev); + + pr_debug("%s vq=%p, vsock=%p\n", __func__, vq, vsock); +} + +static void vhost_vsock_handle_tx_kick(struct vhost_work *work) +{ + struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, + poll.work); + struct vhost_vsock *vsock = container_of(vq->dev, struct vhost_vsock, + dev); + struct virtio_vsock_pkt *pkt; + int head; + unsigned int out, in; + bool added = false; + u32 len; + + mutex_lock(&vq->mutex); + vhost_disable_notify(&vsock->dev, vq); + for (;;) { + head = vhost_get_vq_desc(vq, vq->iov, ARRAY_SIZE(vq->iov), + &out, &in, NULL, NULL); + if (head < 0) + break; + + if (head == vq->num) { + if (unlikely(vhost_enable_notify(&vsock->dev, vq))) { + vhost_disable_notify(&vsock->dev, vq); + continue; + } + break; + } + + pkt = vhost_vsock_alloc_pkt(vq, out, in); + if (!pkt) { + vq_err(vq, "Faulted on pkt\n"); + continue; + } + + len = pkt->len; + + /* Only accept correctly addressed packets */ + if (le32_to_cpu(pkt->hdr.src_cid) == vsock->guest_cid && + le32_to_cpu(pkt->hdr.dst_cid) == vhost_transport_get_local_cid()) + virtio_transport_recv_pkt(pkt); + else + virtio_transport_free_pkt(pkt); + + vhost_add_used(vq, head, len); + added = true; + } + if (added) + vhost_signal(&vsock->dev, vq); + mutex_unlock(&vq->mutex); +} + +static void vhost_vsock_handle_rx_kick(struct vhost_work *work) +{ + struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, + poll.work); + struct vhost_vsock *vsock = container_of(vq->dev, struct vhost_vsock, + dev); + + vhost_transport_do_send_pkt(vsock, vq); +} + +static int vhost_vsock_dev_open(struct inode *inode, struct file *file) +{ + struct vhost_virtqueue **vqs; + struct vhost_vsock *vsock; + int ret; + + vsock = kzalloc(sizeof(*vsock), GFP_KERNEL); + if (!vsock) + return -ENOMEM; + + pr_debug("%s:vsock=%p\n", __func__, vsock); + + vqs = kmalloc(VSOCK_VQ_MAX * sizeof(*vqs), GFP_KERNEL); + if (!vqs) { + ret = -ENOMEM; + goto out; + } + + vqs[VSOCK_VQ_CTRL] = &vsock->vqs[VSOCK_VQ_CTRL].vq; + vqs[VSOCK_VQ_TX] = &vsock->vqs[VSOCK_VQ_TX].vq; + vqs[VSOCK_VQ_RX] = &vsock->vqs[VSOCK_VQ_RX].vq; + vsock->vqs[VSOCK_VQ_CTRL].vq.handle_kick = vhost_vsock_handle_ctl_kick; + vsock->vqs[VSOCK_VQ_TX].vq.handle_kick = vhost_vsock_handle_tx_kick; + vsock->vqs[VSOCK_VQ_RX].vq.handle_kick = vhost_vsock_handle_rx_kick; + + vhost_dev_init(&vsock->dev, vqs, VSOCK_VQ_MAX); + + file->private_data = vsock; + init_waitqueue_head(&vsock->queue_wait); + INIT_LIST_HEAD(&vsock->send_pkt_list); + vhost_work_init(&vsock->send_pkt_work, vhost_transport_send_pkt_work); + + mutex_lock(&vhost_vsock_mutex); + list_add_tail(&vsock->list, &vhost_vsock_list); + mutex_unlock(&vhost_vsock_mutex); + return 0; + +out: + kfree(vsock); + return ret; +} + +static void vhost_vsock_flush(struct vhost_vsock *vsock) +{ + int i; + + for (i = 0; i < VSOCK_VQ_MAX; i++) + vhost_poll_flush(&vsock->vqs[i].vq.poll); + vhost_work_flush(&vsock->dev, &vsock->send_pkt_work); +} + +static int vhost_vsock_dev_release(struct inode *inode, struct file *file) +{ + struct vhost_vsock *vsock = file->private_data; + + mutex_lock(&vhost_vsock_mutex); + list_del(&vsock->list); + mutex_unlock(&vhost_vsock_mutex); + + vhost_dev_stop(&vsock->dev); + vhost_vsock_flush(vsock); + vhost_dev_cleanup(&vsock->dev, false); + kfree(vsock->dev.vqs); + kfree(vsock); + return 0; +} + +static int vhost_vsock_set_cid(struct vhost_vsock *vsock, u32 guest_cid) +{ + struct vhost_vsock *other; + + /* Refuse reserved CIDs */ + if (guest_cid <= VMADDR_CID_HOST) { + return -EINVAL; + } + + /* Refuse if CID is already in use */ + other = vhost_vsock_get(guest_cid); + if (other && other != vsock) { + return -EADDRINUSE; + } + + mutex_lock(&vhost_vsock_mutex); + vsock->guest_cid = guest_cid; + pr_debug("%s:guest_cid=%d\n", __func__, guest_cid); + mutex_unlock(&vhost_vsock_mutex); + + return 0; +} + +static int vhost_vsock_set_features(struct vhost_vsock *vsock, u64 features) +{ + struct vhost_virtqueue *vq; + int i; + + if (features & ~VHOST_VSOCK_FEATURES) + return -EOPNOTSUPP; + + mutex_lock(&vsock->dev.mutex); + if ((features & (1 << VHOST_F_LOG_ALL)) && + !vhost_log_access_ok(&vsock->dev)) { + mutex_unlock(&vsock->dev.mutex); + return -EFAULT; + } + + for (i = 0; i < VSOCK_VQ_MAX; i++) { + vq = &vsock->vqs[i].vq; + mutex_lock(&vq->mutex); + vq->acked_features = features; + mutex_unlock(&vq->mutex); + } + mutex_unlock(&vsock->dev.mutex); + return 0; +} + +static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl, + unsigned long arg) +{ + struct vhost_vsock *vsock = f->private_data; + void __user *argp = (void __user *)arg; + u64 __user *featurep = argp; + u32 __user *cidp = argp; + u32 guest_cid; + u64 features; + int r; + + switch (ioctl) { + case VHOST_VSOCK_SET_GUEST_CID: + if (get_user(guest_cid, cidp)) + return -EFAULT; + return vhost_vsock_set_cid(vsock, guest_cid); + case VHOST_GET_FEATURES: + features = VHOST_VSOCK_FEATURES; + if (copy_to_user(featurep, &features, sizeof(features))) + return -EFAULT; + return 0; + case VHOST_SET_FEATURES: + if (copy_from_user(&features, featurep, sizeof(features))) + return -EFAULT; + return vhost_vsock_set_features(vsock, features); + default: + mutex_lock(&vsock->dev.mutex); + r = vhost_dev_ioctl(&vsock->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + r = vhost_vring_ioctl(&vsock->dev, ioctl, argp); + else + vhost_vsock_flush(vsock); + mutex_unlock(&vsock->dev.mutex); + return r; + } +} + +static const struct file_operations vhost_vsock_fops = { + .owner = THIS_MODULE, + .open = vhost_vsock_dev_open, + .release = vhost_vsock_dev_release, + .llseek = noop_llseek, + .unlocked_ioctl = vhost_vsock_dev_ioctl, +}; + +static struct miscdevice vhost_vsock_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vhost-vsock", + .fops = &vhost_vsock_fops, +}; + +static int +vhost_transport_socket_init(struct vsock_sock *vsk, struct vsock_sock *psk) +{ + struct virtio_transport *trans; + int ret; + + ret = virtio_transport_do_socket_init(vsk, psk); + if (ret) + return ret; + + trans = vsk->trans; + trans->ops = &vhost_ops; + + return ret; +} + +static struct vsock_transport vhost_transport = { + .get_local_cid = vhost_transport_get_local_cid, + + .init = vhost_transport_socket_init, + .destruct = virtio_transport_destruct, + .release = virtio_transport_release, + .connect = virtio_transport_connect, + .shutdown = virtio_transport_shutdown, + + .dgram_enqueue = virtio_transport_dgram_enqueue, + .dgram_dequeue = virtio_transport_dgram_dequeue, + .dgram_bind = virtio_transport_dgram_bind, + .dgram_allow = virtio_transport_dgram_allow, + + .stream_enqueue = virtio_transport_stream_enqueue, + .stream_dequeue = virtio_transport_stream_dequeue, + .stream_has_data = virtio_transport_stream_has_data, + .stream_has_space = virtio_transport_stream_has_space, + .stream_rcvhiwat = virtio_transport_stream_rcvhiwat, + .stream_is_active = virtio_transport_stream_is_active, + .stream_allow = virtio_transport_stream_allow, + + .notify_poll_in = virtio_transport_notify_poll_in, + .notify_poll_out = virtio_transport_notify_poll_out, + .notify_recv_init = virtio_transport_notify_recv_init, + .notify_recv_pre_block = virtio_transport_notify_recv_pre_block, + .notify_recv_pre_dequeue = virtio_transport_notify_recv_pre_dequeue, + .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue, + .notify_send_init = virtio_transport_notify_send_init, + .notify_send_pre_block = virtio_transport_notify_send_pre_block, + .notify_send_pre_enqueue = virtio_transport_notify_send_pre_enqueue, + .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue, + + .set_buffer_size = virtio_transport_set_buffer_size, + .set_min_buffer_size = virtio_transport_set_min_buffer_size, + .set_max_buffer_size = virtio_transport_set_max_buffer_size, + .get_buffer_size = virtio_transport_get_buffer_size, + .get_min_buffer_size = virtio_transport_get_min_buffer_size, + .get_max_buffer_size = virtio_transport_get_max_buffer_size, +}; + +static int __init vhost_vsock_init(void) +{ + int ret; + + ret = vsock_core_init(&vhost_transport); + if (ret < 0) + return ret; + return misc_register(&vhost_vsock_misc); +}; + +static void __exit vhost_vsock_exit(void) +{ + misc_deregister(&vhost_vsock_misc); + vsock_core_exit(); +}; + +module_init(vhost_vsock_init); +module_exit(vhost_vsock_exit); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Asias He"); +MODULE_DESCRIPTION("vhost transport for vsock "); diff --git a/drivers/vhost/vsock.h b/drivers/vhost/vsock.h new file mode 100644 index 000000000000..0ddb107b86ca --- /dev/null +++ b/drivers/vhost/vsock.h @@ -0,0 +1,4 @@ +#ifndef VHOST_VSOCK_H +#define VHOST_VSOCK_H +#define VHOST_VSOCK_SET_GUEST_CID _IOW(VHOST_VIRTIO, 0x60, __u32) +#endif -- cgit v1.2.3-59-g8ed1b From 8a2a2029893b4c35d1aba2932111a1a164b9c948 Mon Sep 17 00:00:00 2001 From: Asias He Date: Wed, 2 Dec 2015 14:44:03 +0800 Subject: VSOCK: Add Makefile and Kconfig Enable virtio-vsock and vhost-vsock. Signed-off-by: Asias He Signed-off-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- drivers/vhost/Kconfig | 4 ++++ drivers/vhost/Kconfig.vsock | 7 +++++++ drivers/vhost/Makefile | 4 ++++ net/vmw_vsock/Kconfig | 18 ++++++++++++++++++ net/vmw_vsock/Makefile | 2 ++ 5 files changed, 35 insertions(+) create mode 100644 drivers/vhost/Kconfig.vsock (limited to 'drivers') diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig index 533eaf04f12f..81449bfc8d3b 100644 --- a/drivers/vhost/Kconfig +++ b/drivers/vhost/Kconfig @@ -47,3 +47,7 @@ config VHOST_CROSS_ENDIAN_LEGACY adds some overhead, it is disabled by default. If unsure, say "N". + +if STAGING +source "drivers/vhost/Kconfig.vsock" +endif diff --git a/drivers/vhost/Kconfig.vsock b/drivers/vhost/Kconfig.vsock new file mode 100644 index 000000000000..3491865d3eb9 --- /dev/null +++ b/drivers/vhost/Kconfig.vsock @@ -0,0 +1,7 @@ +config VHOST_VSOCK + tristate "vhost virtio-vsock driver" + depends on VSOCKETS && EVENTFD + select VIRTIO_VSOCKETS_COMMON + default n + ---help--- + Say M here to enable the vhost-vsock for virtio-vsock guests diff --git a/drivers/vhost/Makefile b/drivers/vhost/Makefile index e0441c34db1c..6b012b986b57 100644 --- a/drivers/vhost/Makefile +++ b/drivers/vhost/Makefile @@ -4,5 +4,9 @@ vhost_net-y := net.o obj-$(CONFIG_VHOST_SCSI) += vhost_scsi.o vhost_scsi-y := scsi.o +obj-$(CONFIG_VHOST_VSOCK) += vhost_vsock.o +vhost_vsock-y := vsock.o + obj-$(CONFIG_VHOST_RING) += vringh.o + obj-$(CONFIG_VHOST) += vhost.o diff --git a/net/vmw_vsock/Kconfig b/net/vmw_vsock/Kconfig index 14810abedc2e..74e0bc887a33 100644 --- a/net/vmw_vsock/Kconfig +++ b/net/vmw_vsock/Kconfig @@ -26,3 +26,21 @@ config VMWARE_VMCI_VSOCKETS To compile this driver as a module, choose M here: the module will be called vmw_vsock_vmci_transport. If unsure, say N. + +config VIRTIO_VSOCKETS + tristate "virtio transport for Virtual Sockets" + depends on VSOCKETS && VIRTIO + select VIRTIO_VSOCKETS_COMMON + help + This module implements a virtio transport for Virtual Sockets. + + Enable this transport if your Virtual Machine runs on Qemu/KVM. + + To compile this driver as a module, choose M here: the module + will be called virtio_vsock_transport. If unsure, say N. + +config VIRTIO_VSOCKETS_COMMON + tristate + ---help--- + This option is selected by any driver which needs to access + the virtio_vsock. diff --git a/net/vmw_vsock/Makefile b/net/vmw_vsock/Makefile index 2ce52d70f224..cf4c29439081 100644 --- a/net/vmw_vsock/Makefile +++ b/net/vmw_vsock/Makefile @@ -1,5 +1,7 @@ obj-$(CONFIG_VSOCKETS) += vsock.o obj-$(CONFIG_VMWARE_VMCI_VSOCKETS) += vmw_vsock_vmci_transport.o +obj-$(CONFIG_VIRTIO_VSOCKETS) += virtio_transport.o +obj-$(CONFIG_VIRTIO_VSOCKETS_COMMON) += virtio_transport_common.o vsock-y += af_vsock.o vsock_addr.o -- cgit v1.2.3-59-g8ed1b From 681b4d88ad8e5b67c34f4d0a40448efb94e2b227 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Wed, 2 Dec 2015 16:27:39 +0100 Subject: pppox: use standard module auto-loading feature * Register PF_PPPOX with pppox module rather than with pppoe, so that pppoe doesn't get loaded for any PF_PPPOX socket. * Register PX_PROTO_* with standard MODULE_ALIAS_NET_PF_PROTO() instead of using pppox's own naming scheme. * While there, add auto-loading feature for pptp. Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- drivers/net/ppp/pppoe.c | 2 +- drivers/net/ppp/pppox.c | 3 ++- drivers/net/ppp/pptp.c | 1 + net/l2tp/l2tp_ppp.c | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index 277e6827d7cd..b8da2eabac3e 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -1202,4 +1202,4 @@ module_exit(pppoe_exit); MODULE_AUTHOR("Michal Ostrowski "); MODULE_DESCRIPTION("PPP over Ethernet driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_PPPOX); +MODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_OE); diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c index 0200de74eebc..b9c8be6283d3 100644 --- a/drivers/net/ppp/pppox.c +++ b/drivers/net/ppp/pppox.c @@ -113,7 +113,7 @@ static int pppox_create(struct net *net, struct socket *sock, int protocol, rc = -EPROTONOSUPPORT; if (!pppox_protos[protocol]) - request_module("pppox-proto-%d", protocol); + request_module("net-pf-%d-proto-%d", PF_PPPOX, protocol); if (!pppox_protos[protocol] || !try_module_get(pppox_protos[protocol]->owner)) goto out; @@ -147,3 +147,4 @@ module_exit(pppox_exit); MODULE_AUTHOR("Michal Ostrowski "); MODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)"); MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(PF_PPPOX); diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index fc69e41d0950..e18e0980bc61 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -718,3 +718,4 @@ module_exit(pptp_exit_module); MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol"); MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)"); MODULE_LICENSE("GPL"); +MODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_PPTP); diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 1ad18c55064c..d93f113cb522 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1862,5 +1862,5 @@ MODULE_AUTHOR("James Chapman "); MODULE_DESCRIPTION("PPP over L2TP over UDP"); MODULE_LICENSE("GPL"); MODULE_VERSION(PPPOL2TP_DRV_VERSION); -MODULE_ALIAS("pppox-proto-" __stringify(PX_PROTO_OL2TP)); +MODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_OL2TP); MODULE_ALIAS_L2TP_PWTYPE(11); -- cgit v1.2.3-59-g8ed1b From 36b9ddd53587537ee9584f43a2713474000346cd Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 2 Dec 2015 17:30:26 +0100 Subject: net: bfin_mac: Use platform_register/unregister_drivers() These new helpers simplify implementing multi-driver modules and properly handle failure to register one driver by unregistering all previously registered drivers. Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/adi/bfin_mac.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index 096531a73124..e0e95a15cab0 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -1912,21 +1912,21 @@ static struct platform_driver bfin_mac_driver = { }, }; +static struct platform_driver * const drivers[] = { + &bfin_mii_bus_driver, + &bfin_mac_driver, +}; + static int __init bfin_mac_init(void) { - int ret; - ret = platform_driver_register(&bfin_mii_bus_driver); - if (!ret) - return platform_driver_register(&bfin_mac_driver); - return -ENODEV; + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); } module_init(bfin_mac_init); static void __exit bfin_mac_cleanup(void) { - platform_driver_unregister(&bfin_mac_driver); - platform_driver_unregister(&bfin_mii_bus_driver); + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); } module_exit(bfin_mac_cleanup); -- cgit v1.2.3-59-g8ed1b From 0d1c744cbdcbf7b57044319f4ecad74ba230b138 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 2 Dec 2015 17:30:27 +0100 Subject: net: bcm63xx: Use platform_register/unregister_drivers() These new helpers simplify implementing multi-driver modules and properly handle failure to register one driver by unregistering all previously registered drivers. Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 8b1929e9f698..a54bafad3538 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -2884,33 +2884,21 @@ struct platform_driver bcm63xx_enet_shared_driver = { }, }; +static struct platform_driver * const drivers[] = { + &bcm63xx_enet_shared_driver, + &bcm63xx_enet_driver, + &bcm63xx_enetsw_driver, +}; + /* entry point */ static int __init bcm_enet_init(void) { - int ret; - - ret = platform_driver_register(&bcm63xx_enet_shared_driver); - if (ret) - return ret; - - ret = platform_driver_register(&bcm63xx_enet_driver); - if (ret) - platform_driver_unregister(&bcm63xx_enet_shared_driver); - - ret = platform_driver_register(&bcm63xx_enetsw_driver); - if (ret) { - platform_driver_unregister(&bcm63xx_enet_driver); - platform_driver_unregister(&bcm63xx_enet_shared_driver); - } - - return ret; + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); } static void __exit bcm_enet_exit(void) { - platform_driver_unregister(&bcm63xx_enet_driver); - platform_driver_unregister(&bcm63xx_enetsw_driver); - platform_driver_unregister(&bcm63xx_enet_shared_driver); + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); } -- cgit v1.2.3-59-g8ed1b From 8c7d3972fdbe9fb7644edea83e0ca5e771aa5563 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 2 Dec 2015 17:30:28 +0100 Subject: net: mpc52xx: Use platform_register/unregister_drivers() These new helpers simplify implementing multi-driver modules and properly handle failure to register one driver by unregistering all previously registered drivers. Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_mpc52xx.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c index afe7f39cdd7c..25553ee857b4 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c @@ -1084,27 +1084,23 @@ static struct platform_driver mpc52xx_fec_driver = { /* Module */ /* ======================================================================== */ +static struct platform_driver * const drivers[] = { +#ifdef CONFIG_FEC_MPC52xx_MDIO + &mpc52xx_fec_mdio_driver, +#endif + &mpc52xx_fec_driver, +}; + static int __init mpc52xx_fec_init(void) { -#ifdef CONFIG_FEC_MPC52xx_MDIO - int ret; - ret = platform_driver_register(&mpc52xx_fec_mdio_driver); - if (ret) { - pr_err("failed to register mdio driver\n"); - return ret; - } -#endif - return platform_driver_register(&mpc52xx_fec_driver); + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); } static void __exit mpc52xx_fec_exit(void) { - platform_driver_unregister(&mpc52xx_fec_driver); -#ifdef CONFIG_FEC_MPC52xx_MDIO - platform_driver_unregister(&mpc52xx_fec_mdio_driver); -#endif + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); } -- cgit v1.2.3-59-g8ed1b From 3b5dde70b18e2aa05138466cd6b83f6529d4df0d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 2 Dec 2015 17:30:29 +0100 Subject: net: mv643xx: Use platform_register/unregister_drivers() These new helpers simplify implementing multi-driver modules and properly handle failure to register one driver by unregistering all previously registered drivers. Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 4182290fdbcf..4eba2ed53052 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -3257,25 +3257,20 @@ static struct platform_driver mv643xx_eth_driver = { }, }; +static struct platform_driver * const drivers[] = { + &mv643xx_eth_shared_driver, + &mv643xx_eth_driver, +}; + static int __init mv643xx_eth_init_module(void) { - int rc; - - rc = platform_driver_register(&mv643xx_eth_shared_driver); - if (!rc) { - rc = platform_driver_register(&mv643xx_eth_driver); - if (rc) - platform_driver_unregister(&mv643xx_eth_shared_driver); - } - - return rc; + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); } module_init(mv643xx_eth_init_module); static void __exit mv643xx_eth_cleanup_module(void) { - platform_driver_unregister(&mv643xx_eth_driver); - platform_driver_unregister(&mv643xx_eth_shared_driver); + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); } module_exit(mv643xx_eth_cleanup_module); -- cgit v1.2.3-59-g8ed1b From aa5bc7a28d0c5422a0197f021fe5ddcc80f4bfd7 Mon Sep 17 00:00:00 2001 From: Stas Sergeev Date: Wed, 2 Dec 2015 20:33:56 +0300 Subject: mvneta: consolidate autoneg enabling This moves autoneg-related bit manipulations to the single place. CC: Thomas Petazzoni CC: netdev@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Stas Sergeev Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index e84c7f2634d3..8e0e6dea3476 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -1065,15 +1065,28 @@ static void mvneta_defaults_set(struct mvneta_port *pp) MVNETA_GMAC_AN_SPEED_EN | MVNETA_GMAC_AN_DUPLEX_EN; mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); val |= MVNETA_GMAC_1MS_CLOCK_ENABLE; mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val); + + val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); + val |= MVNETA_GMAC2_INBAND_AN_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); } else { val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_AN_SPEED_EN | MVNETA_GMAC_AN_DUPLEX_EN); mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + + val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); + val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val); + + val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); + val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); } mvneta_set_ucast_table(pp, -1); @@ -3223,9 +3236,6 @@ static int mvneta_port_power_up(struct mvneta_port *pp, int phy_mode) return -EINVAL; } - if (pp->use_inband_status) - ctrl |= MVNETA_GMAC2_INBAND_AN_ENABLE; - /* Cancel Port Reset */ ctrl &= ~MVNETA_GMAC2_PORT_RESET; mvreg_write(pp, MVNETA_GMAC_CTRL_2, ctrl); -- cgit v1.2.3-59-g8ed1b From 0c0744fc1dd5b39a7ae053433f3449b7a3de09de Mon Sep 17 00:00:00 2001 From: Stas Sergeev Date: Wed, 2 Dec 2015 20:35:11 +0300 Subject: mvneta: implement ethtool autonegotiation control This patch allows to do ethtool -s eth0 autoneg off ethtool -s eth0 autoneg on to disable or enable autonegotiation at run-time. Without that functionality, the only way to control the autonegotiation is to modify the device tree. This is needed if you plan to use the same kernel with different ethernet switches, the ones that support the in-band status and the ones that not. CC: Thomas Petazzoni CC: netdev@vger.kernel.org CC: linux-kernel@vger.kernel.org Signed-off-by: Stas Sergeev Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 109 +++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 8e0e6dea3476..d0e1ec51be50 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -369,7 +369,7 @@ struct mvneta_port { unsigned int duplex; unsigned int speed; unsigned int tx_csum_limit; - int use_inband_status:1; + unsigned int use_inband_status:1; u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)]; }; @@ -971,6 +971,44 @@ static void mvneta_set_other_mcast_table(struct mvneta_port *pp, int queue) mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val); } +static void mvneta_set_autoneg(struct mvneta_port *pp, int enable) +{ + u32 val; + + if (enable) { + val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~(MVNETA_GMAC_FORCE_LINK_PASS | + MVNETA_GMAC_FORCE_LINK_DOWN | + MVNETA_GMAC_AN_FLOW_CTRL_EN); + val |= MVNETA_GMAC_INBAND_AN_ENABLE | + MVNETA_GMAC_AN_SPEED_EN | + MVNETA_GMAC_AN_DUPLEX_EN; + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + + val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); + val |= MVNETA_GMAC_1MS_CLOCK_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val); + + val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); + val |= MVNETA_GMAC2_INBAND_AN_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); + } else { + val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE | + MVNETA_GMAC_AN_SPEED_EN | + MVNETA_GMAC_AN_DUPLEX_EN); + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + + val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); + val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val); + + val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); + val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); + } +} + /* This method sets defaults to the NETA port: * Clears interrupt Cause and Mask registers. * Clears all MAC tables. @@ -1056,39 +1094,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp) val &= ~MVNETA_PHY_POLLING_ENABLE; mvreg_write(pp, MVNETA_UNIT_CONTROL, val); - if (pp->use_inband_status) { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~(MVNETA_GMAC_FORCE_LINK_PASS | - MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_AN_FLOW_CTRL_EN); - val |= MVNETA_GMAC_INBAND_AN_ENABLE | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_DUPLEX_EN; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - - val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); - val |= MVNETA_GMAC_1MS_CLOCK_ENABLE; - mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val); - - val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); - val |= MVNETA_GMAC2_INBAND_AN_ENABLE; - mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); - } else { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_DUPLEX_EN); - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - - val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); - val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE; - mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val); - - val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); - val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE; - mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); - } - + mvneta_set_autoneg(pp, pp->use_inband_status); mvneta_set_ucast_table(pp, -1); mvneta_set_special_mcast_table(pp, -1); mvneta_set_other_mcast_table(pp, -1); @@ -2950,10 +2956,43 @@ int mvneta_ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) int mvneta_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct mvneta_port *pp = netdev_priv(dev); + struct phy_device *phydev = pp->phy_dev; - if (!pp->phy_dev) + if (!phydev) return -ENODEV; + if ((cmd->autoneg == AUTONEG_ENABLE) != pp->use_inband_status) { + u32 val; + + mvneta_set_autoneg(pp, cmd->autoneg == AUTONEG_ENABLE); + + if (cmd->autoneg == AUTONEG_DISABLE) { + val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED | + MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FULL_DUPLEX); + + if (phydev->duplex) + val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; + + if (phydev->speed == SPEED_1000) + val |= MVNETA_GMAC_CONFIG_GMII_SPEED; + else if (phydev->speed == SPEED_100) + val |= MVNETA_GMAC_CONFIG_MII_SPEED; + + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + } + + pp->use_inband_status = (cmd->autoneg == AUTONEG_ENABLE); + netdev_info(pp->dev, "autoneg status set to %i\n", + pp->use_inband_status); + + if (netif_running(dev)) { + mvneta_port_down(pp); + mvneta_port_up(pp); + } + } + return phy_ethtool_sset(pp->phy_dev, cmd); } -- cgit v1.2.3-59-g8ed1b From ead87637a9e47f6ce17e4bc9e4c99472f10d72b2 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 2 Dec 2015 23:55:15 -0800 Subject: stmmac: ipq806x: Return error values instead of pointers Typically we return error pointers when we want to use those pointers in the non-error case, but this function is just returning error pointers or NULL for success. Change the style to plain int to follow normal kernel coding styles. Cc: Joachim Eastwood Signed-off-by: Stephen Boyd Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/dwmac-ipq806x.c | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c index 82de68b1a452..36d3355f2fb0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c @@ -198,19 +198,19 @@ static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed) return 0; } -static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac) +static int ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac) { struct device *dev = &gmac->pdev->dev; gmac->phy_mode = of_get_phy_mode(dev->of_node); if (gmac->phy_mode < 0) { dev_err(dev, "missing phy mode property\n"); - return ERR_PTR(-EINVAL); + return -EINVAL; } if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) { dev_err(dev, "missing qcom id property\n"); - return ERR_PTR(-EINVAL); + return -EINVAL; } /* The GMACs are called 1 to 4 in the documentation, but to simplify the @@ -219,13 +219,13 @@ static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac) */ if (gmac->id < 0 || gmac->id > 3) { dev_err(dev, "invalid gmac id\n"); - return ERR_PTR(-EINVAL); + return -EINVAL; } gmac->core_clk = devm_clk_get(dev, "stmmaceth"); if (IS_ERR(gmac->core_clk)) { dev_err(dev, "missing stmmaceth clk property\n"); - return gmac->core_clk; + return PTR_ERR(gmac->core_clk); } clk_set_rate(gmac->core_clk, 266000000); @@ -234,18 +234,16 @@ static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac) "qcom,nss-common"); if (IS_ERR(gmac->nss_common)) { dev_err(dev, "missing nss-common node\n"); - return gmac->nss_common; + return PTR_ERR(gmac->nss_common); } /* Setup the register map for the qsgmii csr registers */ gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node, "qcom,qsgmii-csr"); - if (IS_ERR(gmac->qsgmii_csr)) { + if (IS_ERR(gmac->qsgmii_csr)) dev_err(dev, "missing qsgmii-csr node\n"); - return gmac->qsgmii_csr; - } - return NULL; + return PTR_ERR_OR_ZERO(gmac->qsgmii_csr); } static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed) @@ -262,7 +260,7 @@ static int ipq806x_gmac_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct ipq806x_gmac *gmac; int val; - void *err; + int err; val = stmmac_get_platform_resources(pdev, &stmmac_res); if (val) @@ -279,9 +277,9 @@ static int ipq806x_gmac_probe(struct platform_device *pdev) gmac->pdev = pdev; err = ipq806x_gmac_of_parse(gmac); - if (IS_ERR(err)) { + if (err) { dev_err(dev, "device tree parsing error\n"); - return PTR_ERR(err); + return err; } regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL, -- cgit v1.2.3-59-g8ed1b From 6b20da4d8f3f6a3be9f67e3207f435cfaa5f7f97 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 3 Dec 2015 10:12:03 +0100 Subject: mlxsw: core: Change BUG to WARN in hwmon code Better to just warn the user that something really odd is going on and continue to run. Suggested-by: Or Gerlitz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c index ad8b27418a7f..4dad146b41ae 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c @@ -215,7 +215,7 @@ static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon, "pwm%u", num + 1); break; default: - BUG(); + WARN_ON(1); } mlxsw_hwmon_attr->type_index = type_index; -- cgit v1.2.3-59-g8ed1b From ce3ea1c705761fc73ce4a08f301c93fcba39c58a Mon Sep 17 00:00:00 2001 From: yzhu1 Date: Thu, 3 Dec 2015 18:00:55 +0800 Subject: net: bonding: remove redudant brackets It is not necessary to use two brackets. As such, the redudant brackets are removed. CC: Jay Vosburgh CC: Veaceslav Falico CC: Andy Gospodarek Signed-off-by: Zhu Yanjun Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 5a7de43a09f8..fe0e7a6f4d72 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1379,7 +1379,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) * the current ifenslave will set the interface down prior to * enslaving it; the old ifenslave will not. */ - if ((slave_dev->flags & IFF_UP)) { + if (slave_dev->flags & IFF_UP) { netdev_err(bond_dev, "%s is up - this may be due to an out of date ifenslave\n", slave_dev->name); res = -EPERM; -- cgit v1.2.3-59-g8ed1b From a8e828deb393e8a5ca84cd3d0df56b3c0be04607 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 26 Nov 2015 17:59:27 +0100 Subject: mac80211_hwsim: do not actively scan DFS channels We had another change to fix this in mac80211, but the hwsim "hardware" scan should also be fixed. Obviously this one isn't important since it's not real hardware, but we'd better be consistent. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index c00a7daaa4bc..aaf1b3e860bf 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1871,7 +1871,8 @@ static void hw_scan_work(struct work_struct *work) req->channels[hwsim->scan_chan_idx]->center_freq); hwsim->tmp_chan = req->channels[hwsim->scan_chan_idx]; - if (hwsim->tmp_chan->flags & IEEE80211_CHAN_NO_IR || + if (hwsim->tmp_chan->flags & (IEEE80211_CHAN_NO_IR | + IEEE80211_CHAN_RADAR) || !req->n_ssids) { dwell = 120; } else { -- cgit v1.2.3-59-g8ed1b From 661ef475fdf5f24346ca33314964c71698ff3501 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 24 Nov 2015 18:29:45 +0100 Subject: mac80211_hwsim: delay hardware remain-on-channel start Typically drivers that implement hardware remain-on-channel will have to wait for scheduling constraints, so make hwsim also wait a little bit (only 20ms) before actually starting the operation. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index aaf1b3e860bf..e922a4df3db3 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -495,6 +495,9 @@ struct mac80211_hwsim_data { const struct ieee80211_regdomain *regd; struct ieee80211_channel *tmp_chan; + struct ieee80211_channel *roc_chan; + u32 roc_duration; + struct delayed_work roc_start; struct delayed_work roc_done; struct delayed_work hw_scan; struct cfg80211_scan_request *hw_scan_request; @@ -1988,6 +1991,23 @@ static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw, mutex_unlock(&hwsim->mutex); } +static void hw_roc_start(struct work_struct *work) +{ + struct mac80211_hwsim_data *hwsim = + container_of(work, struct mac80211_hwsim_data, roc_start.work); + + mutex_lock(&hwsim->mutex); + + wiphy_debug(hwsim->hw->wiphy, "hwsim ROC begins\n"); + hwsim->tmp_chan = hwsim->roc_chan; + ieee80211_ready_on_channel(hwsim->hw); + + ieee80211_queue_delayed_work(hwsim->hw, &hwsim->roc_done, + msecs_to_jiffies(hwsim->roc_duration)); + + mutex_unlock(&hwsim->mutex); +} + static void hw_roc_done(struct work_struct *work) { struct mac80211_hwsim_data *hwsim = @@ -2015,16 +2035,14 @@ static int mac80211_hwsim_roc(struct ieee80211_hw *hw, return -EBUSY; } - hwsim->tmp_chan = chan; + hwsim->roc_chan = chan; + hwsim->roc_duration = duration; mutex_unlock(&hwsim->mutex); wiphy_debug(hw->wiphy, "hwsim ROC (%d MHz, %d ms)\n", chan->center_freq, duration); + ieee80211_queue_delayed_work(hw, &hwsim->roc_start, HZ/50); - ieee80211_ready_on_channel(hw); - - ieee80211_queue_delayed_work(hw, &hwsim->roc_done, - msecs_to_jiffies(duration)); return 0; } @@ -2032,6 +2050,7 @@ static int mac80211_hwsim_croc(struct ieee80211_hw *hw) { struct mac80211_hwsim_data *hwsim = hw->priv; + cancel_delayed_work_sync(&hwsim->roc_start); cancel_delayed_work_sync(&hwsim->roc_done); mutex_lock(&hwsim->mutex); @@ -2376,6 +2395,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb); } + INIT_DELAYED_WORK(&data->roc_start, hw_roc_start); INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); -- cgit v1.2.3-59-g8ed1b From 732b5395efd05e79f546b59d6aa02653608bfe03 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 26 Nov 2015 20:50:12 +0200 Subject: mac80211_hwsim: Update timestamp in Probe Response frames Previously, this was done only for Beacon frames, but similar timestamp update is needed for Probe Response frames to make these more accurately match the real IEEE 802.11 behavior. Previously, all zeros timestamp was sent in Probe Response frames. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index e922a4df3db3..593314e94d9d 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1250,6 +1250,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, { struct mac80211_hwsim_data *data = hw->priv; struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *channel; bool ack; @@ -1295,6 +1296,22 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, ARRAY_SIZE(txi->control.rates)); txi->rate_driver_data[0] = channel; + + if (skb->len >= 24 + 8 && + ieee80211_is_probe_resp(hdr->frame_control)) { + /* fake header transmission time */ + struct ieee80211_mgmt *mgmt; + struct ieee80211_rate *txrate; + u64 ts; + + mgmt = (struct ieee80211_mgmt *)skb->data; + txrate = ieee80211_get_tx_rate(hw, txi); + ts = mac80211_hwsim_get_tsf_raw(); + mgmt->u.probe_resp.timestamp = + cpu_to_le64(ts + data->tsf_offset + + 24 * 8 * 10 / txrate->bitrate); + } + mac80211_hwsim_monitor_rx(hw, skb, channel); /* wmediumd mode check */ -- cgit v1.2.3-59-g8ed1b From 00eeccc4a9a84afe819062df13cdb47159919a7c Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 26 Nov 2015 20:51:04 +0200 Subject: mac80211_hwsim: Advertise support for VHT IBSS VHT can be used with IBSS without needing any additional changes in mac80211_hwsim, so start claiming support for this to increase test coverage. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 593314e94d9d..00d752523fc7 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2449,6 +2449,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, NL80211_FEATURE_STATIC_SMPS | NL80211_FEATURE_DYNAMIC_SMPS | NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS); /* ask mac80211 to reserve space for magic */ hw->vif_data_size = sizeof(struct hwsim_vif_priv); -- cgit v1.2.3-59-g8ed1b From 959eb2fd70df86a089e9e31be654462235952a24 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 6 Nov 2015 11:57:23 +0100 Subject: mac80211_hwsim: stop using pointers as cookies Instead of using pointers, use sequentially assigned cookies. This is easier to understand while debugging and also avoids problems when the pointer is reused for the next allocation. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 00d752523fc7..194264c1ee1f 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -517,6 +517,7 @@ struct mac80211_hwsim_data { bool ps_poll_pending; struct dentry *debugfs; + uintptr_t pending_cookie; struct sk_buff_head pending; /* packets pending */ /* * Only radios in the same group can communicate together (the @@ -963,6 +964,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, unsigned int hwsim_flags = 0; int i; struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES]; + uintptr_t cookie; if (data->ps != PS_DISABLED) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); @@ -1021,7 +1023,10 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, goto nla_put_failure; /* We create a cookie to identify this skb */ - if (nla_put_u64(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb)) + data->pending_cookie++; + cookie = data->pending_cookie; + info->rate_driver_data[0] = (void *)cookie; + if (nla_put_u64(skb, HWSIM_ATTR_COOKIE, cookie)) goto nla_put_failure; genlmsg_end(skb, msg_head); @@ -2749,7 +2754,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, struct mac80211_hwsim_data *data2; struct ieee80211_tx_info *txi; struct hwsim_tx_rate *tx_attempts; - unsigned long ret_skb_ptr; + u64 ret_skb_cookie; struct sk_buff *skb, *tmp; const u8 *src; unsigned int hwsim_flags; @@ -2767,7 +2772,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, 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]); + ret_skb_cookie = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]); data2 = get_hwsim_data_ref_from_addr(src); if (!data2) @@ -2775,7 +2780,12 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, /* look for the skb matching the cookie passed back from user */ skb_queue_walk_safe(&data2->pending, skb, tmp) { - if ((unsigned long)skb == ret_skb_ptr) { + u64 skb_cookie; + + txi = IEEE80211_SKB_CB(skb); + skb_cookie = (u64)(uintptr_t)txi->rate_driver_data[0]; + + if (skb_cookie == ret_skb_cookie) { skb_unlink(skb, &data2->pending); found = true; break; -- cgit v1.2.3-59-g8ed1b From 89f44d8c25bb21f167d7d124c6b83dd494f948af Mon Sep 17 00:00:00 2001 From: Amit Khatri Date: Mon, 30 Nov 2015 12:46:52 +0530 Subject: mac80211_hwsim: check txrate for NULL If the rate control algorithm messed up then the txrate pointer here could be NULL - WARN and drop the packet from monitoring. Signed-off-by: Amit Khatri Signed-off-by: Rahul Jain [rewrite commit message, add warning] Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 194264c1ee1f..297b192b9e13 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -814,6 +814,9 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_skb); struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info); + if (WARN_ON(!txrate)) + return; + if (!netif_running(hwsim_mon)) return; -- cgit v1.2.3-59-g8ed1b From 641cf2a5ba0345c01f4eb1ca2980372415c2a5e1 Mon Sep 17 00:00:00 2001 From: Adam Welle Date: Tue, 1 Dec 2015 17:13:52 -0500 Subject: mac80211_hwsim: check ATTR_FREQ for wmediumd (netlink) packets If a packet is received from netlink with the frequency value set it is checked against the current radio's frequency and discarded if different. The frequency is also checked against data2->tmp_chan to support the "hw" off-channel/scan case. Signed-off-by: Adam Welle [allow both simultaneously, add locking] Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 297b192b9e13..c32889a1e39c 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2879,10 +2879,25 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, /* A frame is received from user space */ memset(&rx_status, 0, sizeof(rx_status)); - /* TODO: Check ATTR_FREQ if it exists, and maybe throw away off-channel - * packets? - */ - rx_status.freq = data2->channel->center_freq; + if (info->attrs[HWSIM_ATTR_FREQ]) { + /* throw away off-channel packets, but allow both the temporary + * ("hw" scan/remain-on-channel) and regular channel, since the + * internal datapath also allows this + */ + mutex_lock(&data2->mutex); + rx_status.freq = nla_get_u32(info->attrs[HWSIM_ATTR_FREQ]); + + if (rx_status.freq != data2->channel->center_freq && + (!data2->tmp_chan || + rx_status.freq != data2->tmp_chan->center_freq)) { + mutex_unlock(&data2->mutex); + goto out; + } + mutex_unlock(&data2->mutex); + } else { + rx_status.freq = data2->channel->center_freq; + } + rx_status.band = data2->channel->band; rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]); rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]); -- cgit v1.2.3-59-g8ed1b From 13ac695e7ea16cb27b804fadf2ff569dbcab6af1 Mon Sep 17 00:00:00 2001 From: Salil Date: Thu, 3 Dec 2015 12:17:53 +0000 Subject: net:hns: Add support of Hip06 SoC to the Hislicon Network Subsystem This patchset adds support of Hisilicon Hip06 SoC to the existing HNS ethernet driver. The changes in the driver are mainly due to changes in the DMA descriptor provided by the Hip06 ethernet hardware. These changes need to co-exist with already present Hip05 DMA descriptor and its operating functions. The decision to choose the correct type of DMA descriptor is taken dynamically depending upon the version of the hardware (i.e. V1/hip05 or V2/hip06, see already existing hisilicon-hns-nic.txt binding file for detailed description). other changes includes in SBM, DSAF and PPE modules as well. Changes affecting the driver related to the newly added ethernet hardware features in Hip06 would be added as separate patch over this and subsequent patches. Signed-off-by: Salil Mehta Signed-off-by: yankejian Signed-off-by: huangdaode Signed-off-by: lipeng Signed-off-by: lisheng Signed-off-by: Fengguang Wu Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.h | 51 ++- drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 37 ++- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c | 14 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h | 4 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 213 +++++++++--- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h | 24 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c | 6 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c | 6 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c | 76 +++-- drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h | 8 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h | 72 ++++- drivers/net/ethernet/hisilicon/hns/hns_enet.c | 360 ++++++++++++++++----- drivers/net/ethernet/hisilicon/hns/hns_enet.h | 12 + drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 2 +- 14 files changed, 689 insertions(+), 196 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index cec95ac8687d..6c9a68c46444 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -35,7 +35,7 @@ #include #include -#define HNAE_DRIVER_VERSION "1.3.0" +#define HNAE_DRIVER_VERSION "2.0" #define HNAE_DRIVER_NAME "hns" #define HNAE_COPYRIGHT "Copyright(c) 2015 Huawei Corporation." #define HNAE_DRIVER_STRING "Hisilicon Network Subsystem Driver" @@ -63,6 +63,7 @@ do { \ #define AE_VERSION_1 ('6' << 16 | '6' << 8 | '0') #define AE_VERSION_2 ('1' << 24 | '6' << 16 | '1' << 8 | '0') +#define AE_IS_VER1(ver) ((ver) == AE_VERSION_1) #define AE_NAME_SIZE 16 /* some said the RX and TX RCB format should not be the same in the future. But @@ -144,23 +145,61 @@ enum hnae_led_state { #define HNS_RXD_ASID_S 24 #define HNS_RXD_ASID_M (0xff << HNS_RXD_ASID_S) +#define HNSV2_TXD_BUFNUM_S 0 +#define HNSV2_TXD_BUFNUM_M (0x7 << HNSV2_TXD_BUFNUM_S) +#define HNSV2_TXD_RI_B 1 +#define HNSV2_TXD_L4CS_B 2 +#define HNSV2_TXD_L3CS_B 3 +#define HNSV2_TXD_FE_B 4 +#define HNSV2_TXD_VLD_B 5 + +#define HNSV2_TXD_TSE_B 0 +#define HNSV2_TXD_VLAN_EN_B 1 +#define HNSV2_TXD_SNAP_B 2 +#define HNSV2_TXD_IPV6_B 3 +#define HNSV2_TXD_SCTP_B 4 + /* hardware spec ring buffer format */ struct __packed hnae_desc { __le64 addr; union { struct { - __le16 asid_bufnum_pid; + union { + __le16 asid_bufnum_pid; + __le16 asid; + }; __le16 send_size; - __le32 flag_ipoffset; - __le32 reserved_3[4]; + union { + __le32 flag_ipoffset; + struct { + __u8 bn_pid; + __u8 ra_ri_cs_fe_vld; + __u8 ip_offset; + __u8 tse_vlan_snap_v6_sctp_nth; + }; + }; + __le16 mss; + __u8 l4_len; + __u8 reserved1; + __le16 paylen; + __u8 vmid; + __u8 qid; + __le32 reserved2[2]; } tx; struct { __le32 ipoff_bnum_pid_flag; __le16 pkt_len; __le16 size; - __le32 vlan_pri_asid; - __le32 reserved_2[3]; + union { + __le32 vlan_pri_asid; + struct { + __le16 asid; + __le16 vlan_cfi_pri; + }; + }; + __le32 rss_hash; + __le32 reserved_1[2]; } rx; }; }; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index 1a16c0307b47..043b9e52084d 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -252,7 +252,7 @@ static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr) if (mac_cb->mac_type != HNAE_PORT_SERVICE) return 0; - ret = hns_mac_set_multi(mac_cb, mac_cb->mac_id, mac_addr, ENABLE); + ret = hns_mac_set_multi(mac_cb, mac_cb->mac_id, mac_addr, true); if (ret) { dev_err(handle->owner_dev, "mac add mul_mac:%pM port%d fail, ret = %#x!\n", @@ -261,7 +261,7 @@ static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr) } ret = hns_mac_set_multi(mac_cb, DSAF_BASE_INNER_PORT_NUM, - mac_addr, ENABLE); + mac_addr, true); if (ret) dev_err(handle->owner_dev, "mac add mul_mac:%pM port%d fail, ret = %#x!\n", @@ -282,7 +282,7 @@ static int hns_ae_start(struct hnae_handle *handle) int ret; struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); - ret = hns_mac_vm_config_bc_en(mac_cb, 0, ENABLE); + ret = hns_mac_vm_config_bc_en(mac_cb, 0, true); if (ret) return ret; @@ -309,7 +309,7 @@ void hns_ae_stop(struct hnae_handle *handle) hns_ae_ring_enable_all(handle, 0); - (void)hns_mac_vm_config_bc_en(mac_cb, 0, DISABLE); + (void)hns_mac_vm_config_bc_en(mac_cb, 0, false); } static void hns_ae_reset(struct hnae_handle *handle) @@ -338,8 +338,27 @@ void hns_ae_toggle_ring_irq(struct hnae_ring *ring, u32 mask) hns_rcb_int_ctrl_hw(ring->q, flag, mask); } +static void hns_aev2_toggle_ring_irq(struct hnae_ring *ring, u32 mask) +{ + u32 flag; + + if (is_tx_ring(ring)) + flag = RCB_INT_FLAG_TX; + else + flag = RCB_INT_FLAG_RX; + + hns_rcbv2_int_ctrl_hw(ring->q, flag, mask); +} + static void hns_ae_toggle_queue_status(struct hnae_queue *queue, u32 val) { + struct dsaf_device *dsaf_dev = hns_ae_get_dsaf_dev(queue->dev); + + if (AE_IS_VER1(dsaf_dev->dsaf_ver)) + hns_rcb_int_clr_hw(queue, RCB_INT_FLAG_TX | RCB_INT_FLAG_RX); + else + hns_rcbv2_int_clr_hw(queue, RCB_INT_FLAG_TX | RCB_INT_FLAG_RX); + hns_rcb_start(queue, val); } @@ -771,6 +790,16 @@ int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev) { struct hnae_ae_dev *ae_dev = &dsaf_dev->ae_dev; + switch (dsaf_dev->dsaf_ver) { + case AE_VERSION_1: + hns_dsaf_ops.toggle_ring_irq = hns_ae_toggle_ring_irq; + break; + case AE_VERSION_2: + hns_dsaf_ops.toggle_ring_irq = hns_aev2_toggle_ring_irq; + break; + default: + break; + } ae_dev->ops = &hns_dsaf_ops; ae_dev->dev = dsaf_dev->dev; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c index 026b38676cba..5ef0e96e918a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -283,7 +283,7 @@ int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, } int hns_mac_set_multi(struct hns_mac_cb *mac_cb, - u32 port_num, char *addr, u8 en) + u32 port_num, char *addr, bool enable) { int ret; struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; @@ -295,7 +295,7 @@ int hns_mac_set_multi(struct hns_mac_cb *mac_cb, mac_entry.in_port_num = mac_cb->mac_id; mac_entry.port_num = port_num; - if (en == DISABLE) + if (!enable) ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); else ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); @@ -368,7 +368,7 @@ static void hns_mac_param_get(struct mac_params *param, *retuen 0 - success , negative --fail */ static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb, - u32 port_num, u16 vlan_id, u8 en) + u32 port_num, u16 vlan_id, bool enable) { int ret; struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; @@ -386,7 +386,7 @@ static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb, mac_entry.in_port_num = mac_cb->mac_id; mac_entry.port_num = port_num; - if (en == DISABLE) + if (!enable) ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); else ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); @@ -403,7 +403,7 @@ static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb, *@en:enable *retuen 0 - success , negative --fail */ -int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, u8 en) +int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, bool enable) { int ret; struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; @@ -427,7 +427,7 @@ int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, u8 en) return ret; mac_entry.port_num = port_num; - if (en == DISABLE) + if (!enable) ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry); else ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry); @@ -648,7 +648,7 @@ static int hns_mac_init_ex(struct hns_mac_cb *mac_cb) hns_mac_adjust_link(mac_cb, mac_cb->speed, !mac_cb->half_duplex); - ret = hns_mac_port_config_bc_en(mac_cb, mac_cb->mac_id, 0, ENABLE); + ret = hns_mac_port_config_bc_en(mac_cb, mac_cb->mac_id, 0, true); if (ret) goto free_mac_drv; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h index 7da95a7581f9..0b052191d751 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -425,8 +425,8 @@ void mac_adjust_link(struct net_device *net_dev); void hns_mac_get_link_status(struct hns_mac_cb *mac_cb, u32 *link_status); int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, u32 vmid, char *addr); int hns_mac_set_multi(struct hns_mac_cb *mac_cb, - u32 port_num, char *addr, u8 en); -int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vm, u8 en); + u32 port_num, char *addr, bool enable); +int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vm, bool enable); void hns_mac_start(struct hns_mac_cb *mac_cb); void hns_mac_stop(struct hns_mac_cb *mac_cb); int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index 2a98eba660c0..636b205a2366 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -38,10 +38,10 @@ int hns_dsaf_get_cfg(struct dsaf_device *dsaf_dev) const char *name, *mode_str; struct device_node *np = dsaf_dev->dev->of_node; - if (of_device_is_compatible(np, "hisilicon,hns-dsaf-v2")) - dsaf_dev->dsaf_ver = AE_VERSION_2; - else + if (of_device_is_compatible(np, "hisilicon,hns-dsaf-v1")) dsaf_dev->dsaf_ver = AE_VERSION_1; + else + dsaf_dev->dsaf_ver = AE_VERSION_2; ret = of_property_read_string(np, "dsa_name", &name); if (ret) { @@ -274,6 +274,8 @@ static void hns_dsaf_stp_port_type_cfg(struct dsaf_device *dsaf_dev, } } +#define HNS_DSAF_SBM_NUM(dev) \ + (AE_IS_VER1((dev)->dsaf_ver) ? DSAF_SBM_NUM : DSAFV2_SBM_NUM) /** * hns_dsaf_sbm_cfg - config sbm * @dsaf_id: dsa fabric id @@ -283,7 +285,7 @@ static void hns_dsaf_sbm_cfg(struct dsaf_device *dsaf_dev) u32 o_sbm_cfg; u32 i; - for (i = 0; i < DSAF_SBM_NUM; i++) { + for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) { o_sbm_cfg = dsaf_read_dev(dsaf_dev, DSAF_SBM_CFG_REG_0_REG + 0x80 * i); dsaf_set_bit(o_sbm_cfg, DSAF_SBM_CFG_EN_S, 1); @@ -304,13 +306,19 @@ static int hns_dsaf_sbm_cfg_mib_en(struct dsaf_device *dsaf_dev) u32 reg; u32 read_cnt; - for (i = 0; i < DSAF_SBM_NUM; i++) { + /* validate configure by setting SBM_CFG_MIB_EN bit from 0 to 1. */ + for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) { + reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i; + dsaf_set_dev_bit(dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S, 0); + } + + for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) { reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i; dsaf_set_dev_bit(dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S, 1); } /* waitint for all sbm enable finished */ - for (i = 0; i < DSAF_SBM_NUM; i++) { + for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) { read_cnt = 0; reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i; do { @@ -338,83 +346,156 @@ static int hns_dsaf_sbm_cfg_mib_en(struct dsaf_device *dsaf_dev) */ static void hns_dsaf_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev) { - u32 o_sbm_bp_cfg0; - u32 o_sbm_bp_cfg1; - u32 o_sbm_bp_cfg2; - u32 o_sbm_bp_cfg3; + u32 o_sbm_bp_cfg; u32 reg; u32 i; /* XGE */ for (i = 0; i < DSAF_XGE_NUM; i++) { reg = DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + 0x80 * i; - o_sbm_bp_cfg0 = dsaf_read_dev(dsaf_dev, reg); - dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M, + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M, DSAF_SBM_CFG0_COM_MAX_BUF_NUM_S, 512); - dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M, + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M, DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_S, 0); - dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M, + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M, DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_S, 0); - dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg0); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); reg = DSAF_SBM_BP_CFG_1_REG_0_REG + 0x80 * i; - o_sbm_bp_cfg1 = dsaf_read_dev(dsaf_dev, reg); - dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M, + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M, DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_S, 0); - dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M, + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M, DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_S, 0); - dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg1); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); reg = DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + 0x80 * i; - o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); - dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_SET_BUF_NUM_M, DSAF_SBM_CFG2_SET_BUF_NUM_S, 104); - dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_RESET_BUF_NUM_M, DSAF_SBM_CFG2_RESET_BUF_NUM_S, 128); - dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); reg = DSAF_SBM_BP_CFG_3_REG_0_REG + 0x80 * i; - o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg); - dsaf_set_field(o_sbm_bp_cfg3, + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M, DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 110); - dsaf_set_field(o_sbm_bp_cfg3, + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M, DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 160); - dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); /* for no enable pfc mode */ reg = DSAF_SBM_BP_CFG_4_REG_0_REG + 0x80 * i; - o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg); - dsaf_set_field(o_sbm_bp_cfg3, + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M, DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 128); - dsaf_set_field(o_sbm_bp_cfg3, + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M, DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 192); - dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); } /* PPE */ for (i = 0; i < DSAF_COMM_CHN; i++) { reg = DSAF_SBM_BP_CFG_2_PPE_REG_0_REG + 0x80 * i; - o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); - dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_SET_BUF_NUM_M, DSAF_SBM_CFG2_SET_BUF_NUM_S, 10); - dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_RESET_BUF_NUM_M, DSAF_SBM_CFG2_RESET_BUF_NUM_S, 12); - dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); } /* RoCEE */ for (i = 0; i < DSAF_COMM_CHN; i++) { reg = DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG + 0x80 * i; - o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg); - dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M, + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_SET_BUF_NUM_M, DSAF_SBM_CFG2_SET_BUF_NUM_S, 2); - dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M, + dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_RESET_BUF_NUM_M, DSAF_SBM_CFG2_RESET_BUF_NUM_S, 4); - dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); + } +} + +static void hns_dsafv2_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev) +{ + u32 o_sbm_bp_cfg; + u32 reg; + u32 i; + + /* XGE */ + for (i = 0; i < DSAFV2_SBM_XGE_CHN; i++) { + reg = DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_M, + DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_S, 256); + dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_M, + DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_S, 0); + dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_M, + DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_S, 0); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); + + reg = DSAF_SBM_BP_CFG_1_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_M, + DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_S, 0); + dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_M, + DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_S, 0); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); + + reg = DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_SET_BUF_NUM_M, + DSAFV2_SBM_CFG2_SET_BUF_NUM_S, 104); + dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_RESET_BUF_NUM_M, + DSAFV2_SBM_CFG2_RESET_BUF_NUM_S, 128); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); + + reg = DSAF_SBM_BP_CFG_3_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, + DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_M, + DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 110); + dsaf_set_field(o_sbm_bp_cfg, + DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M, + DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 160); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); + + /* for no enable pfc mode */ + reg = DSAF_SBM_BP_CFG_4_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, + DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_M, + DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_S, 128); + dsaf_set_field(o_sbm_bp_cfg, + DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_M, + DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_S, 192); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); + } + + /* PPE */ + reg = DSAF_SBM_BP_CFG_2_PPE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_SET_BUF_NUM_M, + DSAFV2_SBM_CFG2_SET_BUF_NUM_S, 10); + dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_RESET_BUF_NUM_M, + DSAFV2_SBM_CFG2_RESET_BUF_NUM_S, 12); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); + /* RoCEE */ + for (i = 0; i < DASFV2_ROCEE_CRD_NUM; i++) { + reg = DSAFV2_SBM_BP_CFG_2_ROCEE_REG_0_REG + 0x80 * i; + o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); + dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_SET_BUF_NUM_M, + DSAFV2_SBM_CFG2_SET_BUF_NUM_S, 2); + dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_RESET_BUF_NUM_M, + DSAFV2_SBM_CFG2_RESET_BUF_NUM_S, 4); + dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); } } @@ -985,11 +1066,38 @@ static void hns_dsaf_inode_init(struct dsaf_device *dsaf_dev) else tc_cfg = HNS_DSAF_I8TC_CFG; + if (AE_IS_VER1(dsaf_dev->dsaf_ver)) { + for (i = 0; i < DSAF_INODE_NUM; i++) { + reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i; + dsaf_set_dev_field(dsaf_dev, reg, + DSAF_INODE_IN_PORT_NUM_M, + DSAF_INODE_IN_PORT_NUM_S, + i % DSAF_XGE_NUM); + } + } else { + for (i = 0; i < DSAF_PORT_TYPE_NUM; i++) { + reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i; + dsaf_set_dev_field(dsaf_dev, reg, + DSAF_INODE_IN_PORT_NUM_M, + DSAF_INODE_IN_PORT_NUM_S, 0); + dsaf_set_dev_field(dsaf_dev, reg, + DSAFV2_INODE_IN_PORT1_NUM_M, + DSAFV2_INODE_IN_PORT1_NUM_S, 1); + dsaf_set_dev_field(dsaf_dev, reg, + DSAFV2_INODE_IN_PORT2_NUM_M, + DSAFV2_INODE_IN_PORT2_NUM_S, 2); + dsaf_set_dev_field(dsaf_dev, reg, + DSAFV2_INODE_IN_PORT3_NUM_M, + DSAFV2_INODE_IN_PORT3_NUM_S, 3); + dsaf_set_dev_field(dsaf_dev, reg, + DSAFV2_INODE_IN_PORT4_NUM_M, + DSAFV2_INODE_IN_PORT4_NUM_S, 4); + dsaf_set_dev_field(dsaf_dev, reg, + DSAFV2_INODE_IN_PORT5_NUM_M, + DSAFV2_INODE_IN_PORT5_NUM_S, 5); + } + } for (i = 0; i < DSAF_INODE_NUM; i++) { - reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i; - dsaf_set_dev_field(dsaf_dev, reg, DSAF_INODE_IN_PORT_NUM_M, - DSAF_INODE_IN_PORT_NUM_S, i % DSAF_XGE_NUM); - reg = DSAF_INODE_PRI_TC_CFG_0_REG + 0x80 * i; dsaf_write_dev(dsaf_dev, reg, tc_cfg); } @@ -1002,10 +1110,17 @@ static void hns_dsaf_inode_init(struct dsaf_device *dsaf_dev) static int hns_dsaf_sbm_init(struct dsaf_device *dsaf_dev) { u32 flag; + u32 finish_msk; u32 cnt = 0; int ret; - hns_dsaf_sbm_bp_wl_cfg(dsaf_dev); + if (AE_IS_VER1(dsaf_dev->dsaf_ver)) { + hns_dsaf_sbm_bp_wl_cfg(dsaf_dev); + finish_msk = DSAF_SRAM_INIT_OVER_M; + } else { + hns_dsafv2_sbm_bp_wl_cfg(dsaf_dev); + finish_msk = DSAFV2_SRAM_INIT_OVER_M; + } /* enable sbm chanel, disable sbm chanel shcut function*/ hns_dsaf_sbm_cfg(dsaf_dev); @@ -1024,11 +1139,13 @@ static int hns_dsaf_sbm_init(struct dsaf_device *dsaf_dev) do { usleep_range(200, 210);/*udelay(200);*/ - flag = dsaf_read_dev(dsaf_dev, DSAF_SRAM_INIT_OVER_0_REG); + flag = dsaf_get_dev_field(dsaf_dev, DSAF_SRAM_INIT_OVER_0_REG, + finish_msk, DSAF_SRAM_INIT_OVER_S); cnt++; - } while (flag != DSAF_SRAM_INIT_FINISH_FLAG && cnt < DSAF_CFG_READ_CNT); + } while (flag != (finish_msk >> DSAF_SRAM_INIT_OVER_S) && + cnt < DSAF_CFG_READ_CNT); - if (flag != DSAF_SRAM_INIT_FINISH_FLAG) { + if (flag != (finish_msk >> DSAF_SRAM_INIT_OVER_S)) { dev_err(dsaf_dev->dev, "hns_dsaf_sbm_init fail %s, flag=%d, cnt=%d\n", dsaf_dev->ae_dev.name, flag, cnt); @@ -2032,7 +2149,7 @@ void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data) DSAF_INODE_VC1_IN_PKT_NUM_0_REG + port * 4); /* dsaf inode registers */ - for (i = 0; i < DSAF_SBM_NUM / DSAF_COMM_CHN; i++) { + for (i = 0; i < HNS_DSAF_SBM_NUM(ddev) / DSAF_COMM_CHN; i++) { j = i * DSAF_COMM_CHN + port; p[232 + i] = dsaf_read_dev(ddev, DSAF_SBM_CFG_REG_0_REG + j * 0x80); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h index b2b93484995c..31c312f9826e 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -19,24 +19,20 @@ struct hns_mac_cb; #define DSAF_DRV_NAME "hns_dsaf" #define DSAF_MOD_VERSION "v1.0" -#define ENABLE (0x1) -#define DISABLE (0x0) +#define HNS_DSAF_DEBUG_NW_REG_OFFSET 0x100000 -#define HNS_DSAF_DEBUG_NW_REG_OFFSET (0x100000) +#define DSAF_BASE_INNER_PORT_NUM 127/* mac tbl qid*/ -#define DSAF_BASE_INNER_PORT_NUM (127) /* mac tbl qid*/ +#define DSAF_MAX_CHIP_NUM 2 /*max 2 chips */ -#define DSAF_MAX_CHIP_NUM (2) /*max 2 chips */ +#define DSAF_DEFAUTL_QUEUE_NUM_PER_PPE 22 -#define DSAF_DEFAUTL_QUEUE_NUM_PER_PPE (22) +#define HNS_DSAF_MAX_DESC_CNT 1024 +#define HNS_DSAF_MIN_DESC_CNT 16 -#define HNS_DSAF_MAX_DESC_CNT (1024) -#define HNS_DSAF_MIN_DESC_CNT (16) +#define DSAF_INVALID_ENTRY_IDX 0xffff -#define DSAF_INVALID_ENTRY_IDX (0xffff) - -#define DSAF_CFG_READ_CNT (30) -#define DSAF_SRAM_INIT_FINISH_FLAG (0xff) +#define DSAF_CFG_READ_CNT 30 #define MAC_NUM_OCTETS_PER_ADDR 6 @@ -274,10 +270,6 @@ struct dsaf_device { struct device *dev; struct hnae_ae_dev ae_dev; - void *priv; - - int virq[DSAF_IRQ_NUM]; - u8 __iomem *sc_base; u8 __iomem *sds_base; u8 __iomem *ppe_base; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c index 523e9b83d304..607c3be42241 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c @@ -149,7 +149,11 @@ void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val) if (port < DSAF_SERVICE_NW_NUM) { reg_val_1 = 0x1 << port; - reg_val_2 = 0x1041041 << port; + /* there is difference between V1 and V2 in register.*/ + if (AE_IS_VER1(dsaf_dev->dsaf_ver)) + reg_val_2 = 0x1041041 << port; + else + reg_val_2 = 0x2082082 << port; if (val == 0) { dsaf_write_reg(dsaf_dev->sc_base, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c index 67f33f185a44..953199285375 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c @@ -341,13 +341,13 @@ void hns_ppe_reset_common(struct dsaf_device *dsaf_dev, u8 ppe_common_index) if (ret) return; + for (i = 0; i < ppe_common->ppe_num; i++) + hns_ppe_init_hw(&ppe_common->ppe_cb[i]); + ret = hns_rcb_common_init_hw(dsaf_dev->rcb_common[ppe_common_index]); if (ret) return; - for (i = 0; i < ppe_common->ppe_num; i++) - hns_ppe_init_hw(&ppe_common->ppe_cb[i]); - hns_rcb_common_init_commit_hw(dsaf_dev->rcb_common[ppe_common_index]); } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c index 4db32c62f062..8c30cec8850a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -136,19 +136,37 @@ void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask) void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag) { - u32 clr = 1; - if (flag & RCB_INT_FLAG_TX) { - dsaf_write_dev(q, RCB_RING_INTSTS_TX_RING_REG, clr); - dsaf_write_dev(q, RCB_RING_INTSTS_TX_OVERTIME_REG, clr); + dsaf_write_dev(q, RCB_RING_INTSTS_TX_RING_REG, 1); + dsaf_write_dev(q, RCB_RING_INTSTS_TX_OVERTIME_REG, 1); } if (flag & RCB_INT_FLAG_RX) { - dsaf_write_dev(q, RCB_RING_INTSTS_RX_RING_REG, clr); - dsaf_write_dev(q, RCB_RING_INTSTS_RX_OVERTIME_REG, clr); + dsaf_write_dev(q, RCB_RING_INTSTS_RX_RING_REG, 1); + dsaf_write_dev(q, RCB_RING_INTSTS_RX_OVERTIME_REG, 1); } } +void hns_rcbv2_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask) +{ + u32 int_mask_en = !!mask; + + if (flag & RCB_INT_FLAG_TX) + dsaf_write_dev(q, RCB_RING_INTMSK_TXWL_REG, int_mask_en); + + if (flag & RCB_INT_FLAG_RX) + dsaf_write_dev(q, RCB_RING_INTMSK_RXWL_REG, int_mask_en); +} + +void hns_rcbv2_int_clr_hw(struct hnae_queue *q, u32 flag) +{ + if (flag & RCB_INT_FLAG_TX) + dsaf_write_dev(q, RCBV2_TX_RING_INT_STS_REG, 1); + + if (flag & RCB_INT_FLAG_RX) + dsaf_write_dev(q, RCBV2_RX_RING_INT_STS_REG, 1); +} + /** *hns_rcb_ring_enable_hw - enable ring *@ring: rcb ring @@ -193,6 +211,7 @@ static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type) (u32)dma); dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_H_REG, (u32)((dma >> 31) >> 1)); + dsaf_write_dev(q, RCB_RING_RX_RING_BD_LEN_REG, bd_size_type); dsaf_write_dev(q, RCB_RING_RX_RING_BD_NUM_REG, @@ -204,6 +223,7 @@ static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type) (u32)dma); dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_H_REG, (u32)((dma >> 31) >> 1)); + dsaf_write_dev(q, RCB_RING_TX_RING_BD_LEN_REG, bd_size_type); dsaf_write_dev(q, RCB_RING_TX_RING_BD_NUM_REG, @@ -232,9 +252,6 @@ void hns_rcb_init_hw(struct ring_pair_cb *ring) static void hns_rcb_set_port_desc_cnt(struct rcb_common_cb *rcb_common, u32 port_idx, u32 desc_cnt) { - if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM) - port_idx = 0; - dsaf_write_dev(rcb_common, RCB_CFG_BD_NUM_REG + port_idx * 4, desc_cnt); } @@ -249,8 +266,6 @@ static int hns_rcb_set_port_coalesced_frames(struct rcb_common_cb *rcb_common, u32 port_idx, u32 coalesced_frames) { - if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM) - port_idx = 0; if (coalesced_frames >= rcb_common->desc_num || coalesced_frames > HNS_RCB_MAX_COALESCED_FRAMES) return -EINVAL; @@ -354,6 +369,9 @@ int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common) dsaf_write_dev(rcb_common, RCB_COM_CFG_ENDIAN_REG, HNS_RCB_COMMON_ENDIAN); + dsaf_write_dev(rcb_common, RCB_COM_CFG_FNA_REG, 0x0); + dsaf_write_dev(rcb_common, RCB_COM_CFG_FA_REG, 0x1); + return 0; } @@ -387,19 +405,23 @@ static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type) struct rcb_common_cb *rcb_common; struct ring_pair_cb *ring_pair_cb; u32 buf_size; - u16 desc_num; - int irq_idx; + u16 desc_num, mdnum_ppkt; + bool irq_idx, is_ver1; ring_pair_cb = container_of(q, struct ring_pair_cb, q); + is_ver1 = AE_IS_VER1(ring_pair_cb->rcb_common->dsaf_dev->dsaf_ver); if (ring_type == RX_RING) { ring = &q->rx_ring; ring->io_base = ring_pair_cb->q.io_base; irq_idx = HNS_RCB_IRQ_IDX_RX; + mdnum_ppkt = HNS_RCB_RING_MAX_BD_PER_PKT; } else { ring = &q->tx_ring; ring->io_base = (u8 __iomem *)ring_pair_cb->q.io_base + HNS_RCB_TX_REG_OFFSET; irq_idx = HNS_RCB_IRQ_IDX_TX; + mdnum_ppkt = is_ver1 ? HNS_RCB_RING_MAX_TXBD_PER_PKT : + HNS_RCBV2_RING_MAX_TXBD_PER_PKT; } rcb_common = ring_pair_cb->rcb_common; @@ -414,7 +436,7 @@ static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type) ring->buf_size = buf_size; ring->desc_num = desc_num; - ring->max_desc_num_per_pkt = HNS_RCB_RING_MAX_BD_PER_PKT; + ring->max_desc_num_per_pkt = mdnum_ppkt; ring->max_raw_data_sz_per_desc = HNS_RCB_MAX_PKT_SIZE; ring->max_pkt_size = HNS_RCB_MAX_PKT_SIZE; ring->next_to_use = 0; @@ -445,14 +467,22 @@ static int hns_rcb_get_port(struct rcb_common_cb *rcb_common, int ring_idx) return port; } +#define SERVICE_RING_IRQ_IDX(v1) \ + ((v1) ? HNS_SERVICE_RING_IRQ_IDX : HNSV2_SERVICE_RING_IRQ_IDX) +#define DEBUG_RING_IRQ_IDX(v1) \ + ((v1) ? HNS_DEBUG_RING_IRQ_IDX : HNSV2_DEBUG_RING_IRQ_IDX) +#define DEBUG_RING_IRQ_OFFSET(v1) \ + ((v1) ? HNS_DEBUG_RING_IRQ_OFFSET : HNSV2_DEBUG_RING_IRQ_OFFSET) static int hns_rcb_get_base_irq_idx(struct rcb_common_cb *rcb_common) { int comm_index = rcb_common->comm_index; + bool is_ver1 = AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver); if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX) - return HNS_SERVICE_RING_IRQ_IDX; + return SERVICE_RING_IRQ_IDX(is_ver1); else - return HNS_DEBUG_RING_IRQ_IDX + (comm_index - 1) * 2; + return DEBUG_RING_IRQ_IDX(is_ver1) + + (comm_index - 1) * DEBUG_RING_IRQ_OFFSET(is_ver1); } #define RCB_COMM_BASE_TO_RING_BASE(base, ringid)\ @@ -468,6 +498,10 @@ void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common) u32 ring_num = rcb_common->ring_num; int base_irq_idx = hns_rcb_get_base_irq_idx(rcb_common); struct device_node *np = rcb_common->dsaf_dev->dev->of_node; + struct platform_device *pdev = + container_of(rcb_common->dsaf_dev->dev, + struct platform_device, dev); + bool is_ver1 = AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver); for (i = 0; i < ring_num; i++) { ring_pair_cb = &rcb_common->ring_pair_cb[i]; @@ -477,10 +511,12 @@ void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common) ring_pair_cb->q.io_base = RCB_COMM_BASE_TO_RING_BASE(rcb_common->io_base, i); ring_pair_cb->port_id_in_dsa = hns_rcb_get_port(rcb_common, i); - ring_pair_cb->virq[HNS_RCB_IRQ_IDX_TX] - = irq_of_parse_and_map(np, base_irq_idx + i * 2); - ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX] - = irq_of_parse_and_map(np, base_irq_idx + i * 2 + 1); + ring_pair_cb->virq[HNS_RCB_IRQ_IDX_TX] = + is_ver1 ? irq_of_parse_and_map(np, base_irq_idx + i * 2) : + platform_get_irq(pdev, base_irq_idx + i * 3 + 1); + ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX] = + is_ver1 ? irq_of_parse_and_map(np, base_irq_idx + i * 2 + 1) : + platform_get_irq(pdev, base_irq_idx + i * 3); ring_pair_cb->q.phy_base = RCB_COMM_BASE_TO_RING_BASE(rcb_common->phy_base, i); hns_rcb_ring_pair_get_cfg(ring_pair_cb); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h index 3a2afe2dd8bb..29041b18741a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h @@ -26,6 +26,8 @@ struct rcb_common_cb; #define HNS_RCB_SERVICE_NW_ENGINE_NUM DSAF_COMM_CHN #define HNS_RCB_DEBUG_NW_ENGINE_NUM 1 #define HNS_RCB_RING_MAX_BD_PER_PKT 3 +#define HNS_RCB_RING_MAX_TXBD_PER_PKT 3 +#define HNS_RCBV2_RING_MAX_TXBD_PER_PKT 8 #define HNS_RCB_MAX_PKT_SIZE MAC_MAX_MTU #define HNS_RCB_RING_MAX_PENDING_BD 1024 @@ -106,13 +108,17 @@ void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index); int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common); void hns_rcb_start(struct hnae_queue *q, u32 val); void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common); -void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common); void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index, u16 *max_vfn, u16 *max_q_per_vf); +void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common); + void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val); void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag); void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 enable); +void hns_rcbv2_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask); +void hns_rcbv2_int_clr_hw(struct hnae_queue *q, u32 flag); + void hns_rcb_init_hw(struct ring_pair_cb *ring); void hns_rcb_reset_ring_hw(struct hnae_queue *q); void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index b475e1bf2e6f..cb0e9e1ecf2c 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -10,21 +10,12 @@ #ifndef _DSAF_REG_H_ #define _DSAF_REG_H_ -#define HNS_GE_FIFO_ERR_INTNUM 8 -#define HNS_XGE_ERR_INTNUM 6 -#define HNS_RCB_COMM_ERR_INTNUM 12 -#define HNS_PPE_TNL_ERR_INTNUM 8 -#define HNS_DSAF_EVENT_INTNUM 21 -#define HNS_DEBUG_RING_INTNUM 4 -#define HNS_SERVICE_RING_INTNUM 256 - -#define HNS_DEBUG_RING_IRQ_IDX (HNS_GE_FIFO_ERR_INTNUM + HNS_XGE_ERR_INTNUM +\ - HNS_RCB_COMM_ERR_INTNUM + HNS_PPE_TNL_ERR_INTNUM +\ - HNS_DSAF_EVENT_INTNUM) -#define HNS_SERVICE_RING_IRQ_IDX (HNS_DEBUG_RING_IRQ_IDX +\ - HNS_DEBUG_RING_INTNUM) - -#define DSAF_IRQ_NUM 18 +#define HNS_DEBUG_RING_IRQ_IDX 55 +#define HNS_SERVICE_RING_IRQ_IDX 59 +#define HNS_DEBUG_RING_IRQ_OFFSET 2 +#define HNSV2_DEBUG_RING_IRQ_IDX 409 +#define HNSV2_SERVICE_RING_IRQ_IDX 25 +#define HNSV2_DEBUG_RING_IRQ_OFFSET 9 #define DSAF_MAX_PORT_NUM_PER_CHIP 8 #define DSAF_SERVICE_PORT_NUM_PER_DSAF 6 @@ -39,9 +30,15 @@ #define DSAF_GE_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM)) #define DSAF_PORT_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM)) #define DSAF_XGE_NUM DSAF_SERVICE_NW_NUM +#define DSAF_PORT_TYPE_NUM 3 #define DSAF_NODE_NUM 18 #define DSAF_XOD_BIG_NUM DSAF_NODE_NUM #define DSAF_SBM_NUM DSAF_NODE_NUM +#define DSAFV2_SBM_NUM 8 +#define DSAFV2_SBM_XGE_CHN 6 +#define DSAFV2_SBM_PPE_CHN 1 +#define DASFV2_ROCEE_CRD_NUM 8 + #define DSAF_VOQ_NUM DSAF_NODE_NUM #define DSAF_INODE_NUM DSAF_NODE_NUM #define DSAF_XOD_NUM 8 @@ -178,6 +175,7 @@ #define DSAF_SBM_BP_CFG_2_XGE_REG_0_REG 0x200C #define DSAF_SBM_BP_CFG_2_PPE_REG_0_REG 0x230C #define DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG 0x260C +#define DSAFV2_SBM_BP_CFG_2_ROCEE_REG_0_REG 0x238C #define DSAF_SBM_FREE_CNT_0_0_REG 0x2010 #define DSAF_SBM_FREE_CNT_1_0_REG 0x2014 #define DSAF_SBM_BP_CNT_0_0_REG 0x2018 @@ -431,8 +429,10 @@ #define RCB_RING_INTMSK_RXWL_REG 0x000A0 #define RCB_RING_INTSTS_RX_RING_REG 0x000A4 +#define RCBV2_RX_RING_INT_STS_REG 0x000A8 #define RCB_RING_INTMSK_TXWL_REG 0x000AC #define RCB_RING_INTSTS_TX_RING_REG 0x000B0 +#define RCBV2_TX_RING_INT_STS_REG 0x000B4 #define RCB_RING_INTMSK_RX_OVERTIME_REG 0x000B8 #define RCB_RING_INTSTS_RX_OVERTIME_REG 0x000BC #define RCB_RING_INTMSK_TX_OVERTIME_REG 0x000C4 @@ -678,6 +678,10 @@ #define XGMAC_TRX_CORE_SRST_M 0x2080 +#define DSAF_SRAM_INIT_OVER_M 0xff +#define DSAFV2_SRAM_INIT_OVER_M 0x3ff +#define DSAF_SRAM_INIT_OVER_S 0 + #define DSAF_CFG_EN_S 0 #define DSAF_CFG_TC_MODE_S 1 #define DSAF_CFG_CRC_EN_S 2 @@ -685,6 +689,7 @@ #define DSAF_CFG_MIX_MODE_S 4 #define DSAF_CFG_STP_MODE_S 5 #define DSAF_CFG_LOCA_ADDR_EN_S 6 +#define DSAFV2_CFG_VLAN_TAG_MODE_S 17 #define DSAF_CNT_CLR_CE_S 0 #define DSAF_SNAP_EN_S 1 @@ -707,6 +712,16 @@ #define DSAF_INODE_IN_PORT_NUM_M 7 #define DSAF_INODE_IN_PORT_NUM_S 0 +#define DSAFV2_INODE_IN_PORT1_NUM_M (7ULL << 3) +#define DSAFV2_INODE_IN_PORT1_NUM_S 3 +#define DSAFV2_INODE_IN_PORT2_NUM_M (7ULL << 6) +#define DSAFV2_INODE_IN_PORT2_NUM_S 6 +#define DSAFV2_INODE_IN_PORT3_NUM_M (7ULL << 9) +#define DSAFV2_INODE_IN_PORT3_NUM_S 9 +#define DSAFV2_INODE_IN_PORT4_NUM_M (7ULL << 12) +#define DSAFV2_INODE_IN_PORT4_NUM_S 12 +#define DSAFV2_INODE_IN_PORT5_NUM_M (7ULL << 15) +#define DSAFV2_INODE_IN_PORT5_NUM_S 15 #define HNS_DSAF_I4TC_CFG 0x18688688 #define HNS_DSAF_I8TC_CFG 0x18FAC688 @@ -738,6 +753,33 @@ #define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S 10 #define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M (((1ULL << 10) - 1) << 10) +#define DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_S 0 +#define DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 0) +#define DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_S 9 +#define DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 9) +#define DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_S 18 +#define DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 18) + +#define DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_S 0 +#define DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 0) +#define DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_S 9 +#define DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 9) + +#define DSAFV2_SBM_CFG2_SET_BUF_NUM_S 0 +#define DSAFV2_SBM_CFG2_SET_BUF_NUM_M (((1ULL << 9) - 1) << 0) +#define DSAFV2_SBM_CFG2_RESET_BUF_NUM_S 9 +#define DSAFV2_SBM_CFG2_RESET_BUF_NUM_M (((1ULL << 9) - 1) << 9) + +#define DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_S 0 +#define DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 0) +#define DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S 9 +#define DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 9) + +#define DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_S 0 +#define DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 0) +#define DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_S 9 +#define DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 9) + #define DSAF_TBL_TCAM_ADDR_S 0 #define DSAF_TBL_TCAM_ADDR_M ((1ULL << 9) - 1) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 08cef0dfb5db..0ca7fa90a193 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -34,9 +34,103 @@ #define RCB_IRQ_NOT_INITED 0 #define RCB_IRQ_INITED 1 +#define BD_MAX_SEND_SIZE 8191 +#define SKB_TMP_LEN(SKB) \ + (((SKB)->transport_header - (SKB)->mac_header) + tcp_hdrlen(SKB)) + +static void fill_v2_desc(struct hnae_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + int buf_num, enum hns_desc_type type, int mtu) +{ + struct hnae_desc *desc = &ring->desc[ring->next_to_use]; + struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use]; + struct iphdr *iphdr; + struct ipv6hdr *ipv6hdr; + struct sk_buff *skb; + int skb_tmp_len; + __be16 protocol; + u8 bn_pid = 0; + u8 rrcfv = 0; + u8 ip_offset = 0; + u8 tvsvsn = 0; + u16 mss = 0; + u8 l4_len = 0; + u16 paylen = 0; + + desc_cb->priv = priv; + desc_cb->length = size; + desc_cb->dma = dma; + desc_cb->type = type; + + desc->addr = cpu_to_le64(dma); + desc->tx.send_size = cpu_to_le16((u16)size); + + /*config bd buffer end */ + hnae_set_bit(rrcfv, HNSV2_TXD_VLD_B, 1); + hnae_set_field(bn_pid, HNSV2_TXD_BUFNUM_M, 0, buf_num - 1); + + if (type == DESC_TYPE_SKB) { + skb = (struct sk_buff *)priv; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + skb_reset_mac_len(skb); + protocol = skb->protocol; + ip_offset = ETH_HLEN; + + if (protocol == htons(ETH_P_8021Q)) { + ip_offset += VLAN_HLEN; + protocol = vlan_get_protocol(skb); + skb->protocol = protocol; + } + + if (skb->protocol == htons(ETH_P_IP)) { + iphdr = ip_hdr(skb); + hnae_set_bit(rrcfv, HNSV2_TXD_L3CS_B, 1); + hnae_set_bit(rrcfv, HNSV2_TXD_L4CS_B, 1); + + /* check for tcp/udp header */ + if (iphdr->protocol == IPPROTO_TCP) { + hnae_set_bit(tvsvsn, + HNSV2_TXD_TSE_B, 1); + skb_tmp_len = SKB_TMP_LEN(skb); + l4_len = tcp_hdrlen(skb); + mss = mtu - skb_tmp_len - ETH_FCS_LEN; + paylen = skb->len - skb_tmp_len; + } + } else if (skb->protocol == htons(ETH_P_IPV6)) { + hnae_set_bit(tvsvsn, HNSV2_TXD_IPV6_B, 1); + ipv6hdr = ipv6_hdr(skb); + hnae_set_bit(rrcfv, HNSV2_TXD_L4CS_B, 1); + + /* check for tcp/udp header */ + if (ipv6hdr->nexthdr == IPPROTO_TCP) { + hnae_set_bit(tvsvsn, + HNSV2_TXD_TSE_B, 1); + skb_tmp_len = SKB_TMP_LEN(skb); + l4_len = tcp_hdrlen(skb); + mss = mtu - skb_tmp_len - ETH_FCS_LEN; + paylen = skb->len - skb_tmp_len; + } + } + desc->tx.ip_offset = ip_offset; + desc->tx.tse_vlan_snap_v6_sctp_nth = tvsvsn; + desc->tx.mss = cpu_to_le16(mss); + desc->tx.l4_len = l4_len; + desc->tx.paylen = cpu_to_le16(paylen); + } + } + + hnae_set_bit(rrcfv, HNSV2_TXD_FE_B, frag_end); + + desc->tx.bn_pid = bn_pid; + desc->tx.ra_ri_cs_fe_vld = rrcfv; + + ring_ptr_move_fw(ring, next_to_use); +} + static void fill_desc(struct hnae_ring *ring, void *priv, int size, dma_addr_t dma, int frag_end, - int buf_num, enum hns_desc_type type) + int buf_num, enum hns_desc_type type, int mtu) { struct hnae_desc *desc = &ring->desc[ring->next_to_use]; struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use]; @@ -100,47 +194,64 @@ static void unfill_desc(struct hnae_ring *ring) ring_ptr_move_bw(ring, next_to_use); } -int hns_nic_net_xmit_hw(struct net_device *ndev, - struct sk_buff *skb, - struct hns_nic_ring_data *ring_data) +static int hns_nic_maybe_stop_tx( + struct sk_buff **out_skb, int *bnum, struct hnae_ring *ring) { - struct hns_nic_priv *priv = netdev_priv(ndev); - struct device *dev = priv->dev; - struct hnae_ring *ring = ring_data->ring; - struct netdev_queue *dev_queue; - struct skb_frag_struct *frag; + struct sk_buff *skb = *out_skb; + struct sk_buff *new_skb = NULL; int buf_num; - dma_addr_t dma; - int size, next_to_use; - int i, j; - struct sk_buff *new_skb; - - assert(ring->max_desc_num_per_pkt <= ring->desc_num); /* no. of segments (plus a header) */ buf_num = skb_shinfo(skb)->nr_frags + 1; if (unlikely(buf_num > ring->max_desc_num_per_pkt)) { - if (ring_space(ring) < 1) { - ring->stats.tx_busy++; - goto out_net_tx_busy; - } + if (ring_space(ring) < 1) + return -EBUSY; new_skb = skb_copy(skb, GFP_ATOMIC); - if (!new_skb) { - ring->stats.sw_err_cnt++; - netdev_err(ndev, "no memory to xmit!\n"); - goto out_err_tx_ok; - } + if (!new_skb) + return -ENOMEM; dev_kfree_skb_any(skb); - skb = new_skb; + *out_skb = new_skb; buf_num = 1; - assert(skb_shinfo(skb)->nr_frags == 1); } else if (buf_num > ring_space(ring)) { + return -EBUSY; + } + + *bnum = buf_num; + return 0; +} + +int hns_nic_net_xmit_hw(struct net_device *ndev, + struct sk_buff *skb, + struct hns_nic_ring_data *ring_data) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct device *dev = priv->dev; + struct hnae_ring *ring = ring_data->ring; + struct netdev_queue *dev_queue; + struct skb_frag_struct *frag; + int buf_num; + int seg_num; + dma_addr_t dma; + int size, next_to_use; + int i; + + switch (priv->ops.maybe_stop_tx(&skb, &buf_num, ring)) { + case -EBUSY: ring->stats.tx_busy++; goto out_net_tx_busy; + case -ENOMEM: + ring->stats.sw_err_cnt++; + netdev_err(ndev, "no memory to xmit!\n"); + goto out_err_tx_ok; + default: + break; } + + /* no. of segments (plus a header) */ + seg_num = skb_shinfo(skb)->nr_frags + 1; next_to_use = ring->next_to_use; /* fill the first part */ @@ -151,11 +262,11 @@ int hns_nic_net_xmit_hw(struct net_device *ndev, ring->stats.sw_err_cnt++; goto out_err_tx_ok; } - fill_desc(ring, skb, size, dma, buf_num == 1 ? 1 : 0, buf_num, - DESC_TYPE_SKB); + priv->ops.fill_desc(ring, skb, size, dma, seg_num == 1 ? 1 : 0, + buf_num, DESC_TYPE_SKB, ndev->mtu); /* fill the fragments */ - for (i = 1; i < buf_num; i++) { + for (i = 1; i < seg_num; i++) { frag = &skb_shinfo(skb)->frags[i - 1]; size = skb_frag_size(frag); dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE); @@ -164,8 +275,9 @@ int hns_nic_net_xmit_hw(struct net_device *ndev, ring->stats.sw_err_cnt++; goto out_map_frag_fail; } - fill_desc(ring, skb_frag_page(frag), size, dma, - buf_num - 1 == i ? 1 : 0, buf_num, DESC_TYPE_PAGE); + priv->ops.fill_desc(ring, skb_frag_page(frag), size, dma, + seg_num - 1 == i ? 1 : 0, buf_num, + DESC_TYPE_PAGE, ndev->mtu); } /*complete translate all packets*/ @@ -182,19 +294,20 @@ int hns_nic_net_xmit_hw(struct net_device *ndev, out_map_frag_fail: - for (j = i - 1; j > 0; j--) { + while (ring->next_to_use != next_to_use) { unfill_desc(ring); - next_to_use = ring->next_to_use; - dma_unmap_page(dev, ring->desc_cb[next_to_use].dma, - ring->desc_cb[next_to_use].length, - DMA_TO_DEVICE); + if (ring->next_to_use != next_to_use) + dma_unmap_page(dev, + ring->desc_cb[ring->next_to_use].dma, + ring->desc_cb[ring->next_to_use].length, + DMA_TO_DEVICE); + else + dma_unmap_single(dev, + ring->desc_cb[next_to_use].dma, + ring->desc_cb[next_to_use].length, + DMA_TO_DEVICE); } - unfill_desc(ring); - next_to_use = ring->next_to_use; - dma_unmap_single(dev, ring->desc_cb[next_to_use].dma, - ring->desc_cb[next_to_use].length, DMA_TO_DEVICE); - out_err_tx_ok: dev_kfree_skb_any(skb); @@ -329,11 +442,24 @@ hns_nic_reuse_page(struct hnae_desc_cb *desc_cb, int tsize, int last_offset) } } +static void get_v2rx_desc_bnum(u32 bnum_flag, int *out_bnum) +{ + *out_bnum = hnae_get_field(bnum_flag, + HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S) + 1; +} + +static void get_rx_desc_bnum(u32 bnum_flag, int *out_bnum) +{ + *out_bnum = hnae_get_field(bnum_flag, + HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S); +} + static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, struct sk_buff **out_skb, int *out_bnum) { struct hnae_ring *ring = ring_data->ring; struct net_device *ndev = ring_data->napi.dev; + struct hns_nic_priv *priv = netdev_priv(ndev); struct sk_buff *skb; struct hnae_desc *desc; struct hnae_desc_cb *desc_cb; @@ -345,19 +471,36 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, last_offset = hnae_page_size(ring) - hnae_buf_size(ring); desc = &ring->desc[ring->next_to_clean]; desc_cb = &ring->desc_cb[ring->next_to_clean]; - length = le16_to_cpu(desc->rx.pkt_len); - bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag); - bnum = hnae_get_field(bnum_flag, HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S); - *out_bnum = bnum; + + prefetch(desc); + va = (unsigned char *)desc_cb->buf + desc_cb->page_offset; - skb = *out_skb = napi_alloc_skb(&ring_data->napi, HNS_RX_HEAD_SIZE); + /* prefetch first cache line of first page */ + prefetch(va); +#if L1_CACHE_BYTES < 128 + prefetch(va + L1_CACHE_BYTES); +#endif + + skb = *out_skb = napi_alloc_skb(&ring_data->napi, + HNS_RX_HEAD_SIZE); if (unlikely(!skb)) { netdev_err(ndev, "alloc rx skb fail\n"); ring->stats.sw_err_cnt++; return -ENOMEM; } + length = le16_to_cpu(desc->rx.pkt_len); + bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag); + priv->ops.get_rxd_bnum(bnum_flag, &bnum); + *out_bnum = bnum; + + /* we will be copying header into skb->data in + * pskb_may_pull so it is in our interest to prefetch + * it now to avoid a possible cache miss + */ + prefetchw(skb->data); + if (length <= HNS_RX_HEAD_SIZE) { memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long))); @@ -540,20 +683,19 @@ recv: } /* make all data has been write before submit */ - if (clean_count > 0) { - hns_nic_alloc_rx_buffers(ring_data, clean_count); - clean_count = 0; - } - if (recv_pkts < budget) { ex_num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); rmb(); /*complete read rx ring bd number*/ - if (ex_num > 0) { - num += ex_num; + if (ex_num > clean_count) { + num += ex_num - clean_count; goto recv; } } + /* make all data has been write before submit */ + if (clean_count > 0) + hns_nic_alloc_rx_buffers(ring_data, clean_count); + return recv_pkts; } @@ -650,6 +792,9 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data, dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); netdev_tx_completed_queue(dev_queue, pkts, bytes); + if (unlikely(priv->link && !netif_carrier_ok(ndev))) + netif_carrier_on(ndev); + if (unlikely(pkts && netif_carrier_ok(ndev) && (ring_space(ring) >= ring->max_desc_num_per_pkt * 2))) { /* Make sure that anybody stopping the queue after this @@ -848,15 +993,58 @@ static void hns_nic_ring_close(struct net_device *netdev, int idx) napi_disable(&priv->ring_data[idx].napi); } -static int hns_nic_init_irq(struct hns_nic_priv *priv) +static void hns_set_irq_affinity(struct hns_nic_priv *priv) { struct hnae_handle *h = priv->ae_handle; struct hns_nic_ring_data *rd; int i; - int ret; int cpu; cpumask_t mask; + /*diffrent irq banlance for 16core and 32core*/ + if (h->q_num == num_possible_cpus()) { + for (i = 0; i < h->q_num * 2; i++) { + rd = &priv->ring_data[i]; + if (cpu_online(rd->queue_index)) { + cpumask_clear(&mask); + cpu = rd->queue_index; + cpumask_set_cpu(cpu, &mask); + (void)irq_set_affinity_hint(rd->ring->irq, + &mask); + } + } + } else { + for (i = 0; i < h->q_num; i++) { + rd = &priv->ring_data[i]; + if (cpu_online(rd->queue_index * 2)) { + cpumask_clear(&mask); + cpu = rd->queue_index * 2; + cpumask_set_cpu(cpu, &mask); + (void)irq_set_affinity_hint(rd->ring->irq, + &mask); + } + } + + for (i = h->q_num; i < h->q_num * 2; i++) { + rd = &priv->ring_data[i]; + if (cpu_online(rd->queue_index * 2 + 1)) { + cpumask_clear(&mask); + cpu = rd->queue_index * 2 + 1; + cpumask_set_cpu(cpu, &mask); + (void)irq_set_affinity_hint(rd->ring->irq, + &mask); + } + } + } +} + +static int hns_nic_init_irq(struct hns_nic_priv *priv) +{ + struct hnae_handle *h = priv->ae_handle; + struct hns_nic_ring_data *rd; + int i; + int ret; + for (i = 0; i < h->q_num * 2; i++) { rd = &priv->ring_data[i]; @@ -878,16 +1066,11 @@ static int hns_nic_init_irq(struct hns_nic_priv *priv) } disable_irq(rd->ring->irq); rd->ring->irq_init_flag = RCB_IRQ_INITED; - - /*set cpu affinity*/ - if (cpu_online(rd->queue_index)) { - cpumask_clear(&mask); - cpu = rd->queue_index; - cpumask_set_cpu(cpu, &mask); - irq_set_affinity_hint(rd->ring->irq, &mask); - } } + /*set cpu affinity*/ + hns_set_irq_affinity(priv); + return 0; } @@ -1315,22 +1498,26 @@ static void hns_nic_reset_subtask(struct hns_nic_priv *priv) return; hns_nic_dump(priv); - netdev_info(priv->netdev, "Reset %s port\n", - (type == HNAE_PORT_DEBUG ? "debug" : "business")); + netdev_info(priv->netdev, "try to reset %s port!\n", + (type == HNAE_PORT_DEBUG ? "debug" : "service")); rtnl_lock(); /* put off any impending NetWatchDogTimeout */ priv->netdev->trans_start = jiffies; - if (type == HNAE_PORT_DEBUG) + if (type == HNAE_PORT_DEBUG) { hns_nic_net_reinit(priv->netdev); + } else { + netif_carrier_off(priv->netdev); + netif_tx_disable(priv->netdev); + } rtnl_unlock(); } /* for doing service complete*/ static void hns_nic_service_event_complete(struct hns_nic_priv *priv) { - assert(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state)); + WARN_ON(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state)); smp_mb__before_atomic(); clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state); @@ -1435,8 +1622,9 @@ static void hns_nic_uninit_ring_data(struct hns_nic_priv *priv) for (i = 0; i < h->q_num * 2; i++) { netif_napi_del(&priv->ring_data[i].napi); if (priv->ring_data[i].ring->irq_init_flag == RCB_IRQ_INITED) { - irq_set_affinity_hint(priv->ring_data[i].ring->irq, - NULL); + (void)irq_set_affinity_hint( + priv->ring_data[i].ring->irq, + NULL); free_irq(priv->ring_data[i].ring->irq, &priv->ring_data[i]); } @@ -1446,6 +1634,21 @@ static void hns_nic_uninit_ring_data(struct hns_nic_priv *priv) kfree(priv->ring_data); } +static void hns_nic_set_priv_ops(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + + if (AE_IS_VER1(priv->enet_ver)) { + priv->ops.fill_desc = fill_desc; + priv->ops.get_rxd_bnum = get_rx_desc_bnum; + priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx; + } else { + priv->ops.get_rxd_bnum = get_v2rx_desc_bnum; + priv->ops.fill_desc = fill_v2_desc; + priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx; + } +} + static int hns_nic_try_get_ae(struct net_device *ndev) { struct hns_nic_priv *priv = netdev_priv(ndev); @@ -1473,6 +1676,8 @@ static int hns_nic_try_get_ae(struct net_device *ndev) goto out_init_ring_data; } + hns_nic_set_priv_ops(ndev); + ret = register_netdev(ndev); if (ret) { dev_err(priv->dev, "probe register netdev fail!\n"); @@ -1524,10 +1729,10 @@ static int hns_nic_dev_probe(struct platform_device *pdev) priv->dev = dev; priv->netdev = ndev; - if (of_device_is_compatible(node, "hisilicon,hns-nic-v2")) - priv->enet_ver = AE_VERSION_2; - else + if (of_device_is_compatible(node, "hisilicon,hns-nic-v1")) priv->enet_ver = AE_VERSION_1; + else + priv->enet_ver = AE_VERSION_2; ret = of_property_read_string(node, "ae-name", &priv->ae_name); if (ret) @@ -1543,6 +1748,7 @@ static int hns_nic_dev_probe(struct platform_device *pdev) ndev->priv_flags |= IFF_UNICAST_FLT; ndev->netdev_ops = &hns_nic_netdev_ops; hns_ethtool_set_ops(ndev); + ndev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO; @@ -1550,6 +1756,16 @@ static int hns_nic_dev_probe(struct platform_device *pdev) NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM; ndev->vlan_features |= NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO; + switch (priv->enet_ver) { + case AE_VERSION_2: + ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | + NETIF_F_GRO; + break; + default: + break; + } + SET_NETDEV_DEV(ndev, dev); if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h index dae0ed19ac6d..4b75270f014e 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h @@ -40,6 +40,16 @@ struct hns_nic_ring_data { void (*fini_process)(struct hns_nic_ring_data *); }; +/* compatible the difference between two versions */ +struct hns_nic_ops { + void (*fill_desc)(struct hnae_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + int buf_num, enum hns_desc_type type, int mtu); + int (*maybe_stop_tx)(struct sk_buff **out_skb, + int *bnum, struct hnae_ring *ring); + void (*get_rxd_bnum)(u32 bnum_flag, int *out_bnum); +}; + struct hns_nic_priv { const char *ae_name; u32 enet_ver; @@ -51,6 +61,8 @@ struct hns_nic_priv { struct device *dev; struct hnae_handle *ae_handle; + struct hns_nic_ops ops; + /* the cb for nic to manage the ring buffer, the first half of the * array is for tx_ring and vice versa for the second half */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index a0332129970b..9df63ae13915 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -11,7 +11,6 @@ #include #include #include - #include "hns_enet.h" #define HNS_PHY_PAGE_MDIX 0 @@ -667,6 +666,7 @@ static void hns_nic_get_drvinfo(struct net_device *net_dev, drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0'; strncpy(drvinfo->fw_version, "N/A", ETHTOOL_FWVERS_LEN); + drvinfo->eedump_len = 0; } /** -- cgit v1.2.3-59-g8ed1b From 6bc0ce7d9adabf332afc102f7f97bf121b990ece Mon Sep 17 00:00:00 2001 From: Salil Date: Thu, 3 Dec 2015 12:17:54 +0000 Subject: net:hns: Add Hip06 "RSS(Receive Side Scaling)" support to HNS Driver This patch adds the support of "RSS (Receive Side Scaling)" feature provided by the Hip06 ethernet hardware to the HNS ethernet driver. This feature helps in distributing the different flows (mapped as hash by hardware using Toeplitz Hash) to different Queues asssociated with the processor cores. The mapping of flow-hash values to the different queues is stored in indirection table (which is per Packet- parse-Engine/PPE). This patch also provides the changes to re-program the (flow-hash<->Qid) mapping using the ethtool. Signed-off-by: Salil Mehta Reviewed-by: Kenneth Lee Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.h | 6 ++ drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 53 ++++++++++++- drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c | 54 ++++++++++++- drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h | 32 ++++++-- drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h | 14 ++++ drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 93 +++++++++++++++++++++++ 6 files changed, 242 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index 6c9a68c46444..76dd71563cc1 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -485,6 +485,12 @@ struct hnae_ae_ops { enum hnae_led_state status); void (*get_regs)(struct hnae_handle *handle, void *data); int (*get_regs_len)(struct hnae_handle *handle); + u32 (*get_rss_key_size)(struct hnae_handle *handle); + u32 (*get_rss_indir_size)(struct hnae_handle *handle); + int (*get_rss)(struct hnae_handle *handle, u32 *indir, u8 *key, + u8 *hfunc); + int (*set_rss)(struct hnae_handle *handle, const u32 *indir, + const u8 *key, const u8 hfunc); }; struct hnae_ae_dev { diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index 043b9e52084d..77e17351ade5 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -749,6 +749,53 @@ int hns_ae_get_regs_len(struct hnae_handle *handle) return total_num; } +static u32 hns_ae_get_rss_key_size(struct hnae_handle *handle) +{ + return HNS_PPEV2_RSS_KEY_SIZE; +} + +static u32 hns_ae_get_rss_indir_size(struct hnae_handle *handle) +{ + return HNS_PPEV2_RSS_IND_TBL_SIZE; +} + +static int hns_ae_get_rss(struct hnae_handle *handle, u32 *indir, u8 *key, + u8 *hfunc) +{ + struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle); + + /* currently we support only one type of hash function i.e. Toep hash */ + if (hfunc) + *hfunc = ETH_RSS_HASH_TOP; + + /* get the RSS Key required by the user */ + if (key) + memcpy(key, ppe_cb->rss_key, HNS_PPEV2_RSS_KEY_SIZE); + + /* update the current hash->queue mappings from the shadow RSS table */ + memcpy(indir, ppe_cb->rss_indir_table, HNS_PPEV2_RSS_IND_TBL_SIZE); + + return 0; +} + +static int hns_ae_set_rss(struct hnae_handle *handle, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle); + + /* set the RSS Hash Key if specififed by the user */ + if (key) + hns_ppe_set_rss_key(ppe_cb, (int *)key); + + /* update the shadow RSS table with user specified qids */ + memcpy(ppe_cb->rss_indir_table, indir, HNS_PPEV2_RSS_IND_TBL_SIZE); + + /* now update the hardware */ + hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table); + + return 0; +} + static struct hnae_ae_ops hns_dsaf_ops = { .get_handle = hns_ae_get_handle, .put_handle = hns_ae_put_handle, @@ -783,7 +830,11 @@ static struct hnae_ae_ops hns_dsaf_ops = { .update_led_status = hns_ae_update_led_status, .set_led_id = hns_ae_cpld_set_led_id, .get_regs = hns_ae_get_regs, - .get_regs_len = hns_ae_get_regs_len + .get_regs_len = hns_ae_get_regs_len, + .get_rss_key_size = hns_ae_get_rss_key_size, + .get_rss_indir_size = hns_ae_get_rss_indir_size, + .get_rss = hns_ae_get_rss, + .set_rss = hns_ae_set_rss }; int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c index 953199285375..7af0858f1fcb 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c @@ -19,6 +19,43 @@ #include "hns_dsaf_ppe.h" +void hns_ppe_set_rss_key(struct hns_ppe_cb *ppe_cb, + const u32 rss_key[HNS_PPEV2_RSS_KEY_NUM]) +{ + int key_item = 0; + + for (key_item = 0; key_item < HNS_PPEV2_RSS_KEY_NUM; key_item++) + dsaf_write_dev(ppe_cb, PPEV2_RSS_KEY_REG + key_item * 0x4, + rss_key[key_item]); +} + +void hns_ppe_set_indir_table(struct hns_ppe_cb *ppe_cb, + const u32 rss_tab[HNS_PPEV2_RSS_IND_TBL_SIZE]) +{ + int i; + int reg_value; + + for (i = 0; i < (HNS_PPEV2_RSS_IND_TBL_SIZE / 4); i++) { + reg_value = dsaf_read_dev(ppe_cb, + PPEV2_INDRECTION_TBL_REG + i * 0x4); + + dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N0_M, + PPEV2_CFG_RSS_TBL_4N0_S, + rss_tab[i * 4 + 0] & 0x1F); + dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N1_M, + PPEV2_CFG_RSS_TBL_4N1_S, + rss_tab[i * 4 + 1] & 0x1F); + dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N2_M, + PPEV2_CFG_RSS_TBL_4N2_S, + rss_tab[i * 4 + 2] & 0x1F); + dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N3_M, + PPEV2_CFG_RSS_TBL_4N3_S, + rss_tab[i * 4 + 3] & 0x1F); + dsaf_write_dev( + ppe_cb, PPEV2_INDRECTION_TBL_REG + i * 0x4, reg_value); + } +} + static void __iomem *hns_ppe_common_get_ioaddr( struct ppe_common_cb *ppe_common) { @@ -266,13 +303,17 @@ static void hns_ppe_exc_irq_en(struct hns_ppe_cb *ppe_cb, int en) /** * ppe_init_hw - init ppe - * @ppe_device: ppe device + * @ppe_cb: ppe device */ static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb) { struct ppe_common_cb *ppe_common_cb = ppe_cb->ppe_common_cb; u32 port = ppe_cb->port; struct dsaf_device *dsaf_dev = ppe_common_cb->dsaf_dev; + int i; + + /* get default RSS key */ + netdev_rss_key_fill(ppe_cb->rss_key, HNS_PPEV2_RSS_KEY_SIZE); hns_ppe_srst_by_port(dsaf_dev, port, 0); mdelay(10); @@ -285,8 +326,19 @@ static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb) hns_ppe_set_port_mode(ppe_cb, PPE_MODE_GE); else hns_ppe_set_port_mode(ppe_cb, PPE_MODE_XGE); + hns_ppe_checksum_hw(ppe_cb, 0xffffffff); hns_ppe_cnt_clr_ce(ppe_cb); + + if (!AE_IS_VER1(dsaf_dev->dsaf_ver)) { + /* set default RSS key in h/w */ + hns_ppe_set_rss_key(ppe_cb, ppe_cb->rss_key); + + /* Set default indrection table in h/w */ + for (i = 0; i < HNS_PPEV2_RSS_IND_TBL_SIZE; i++) + ppe_cb->rss_indir_table[i] = i; + hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table); + } } /** diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h index 4894f9a0d39f..dac853255eca 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h @@ -25,15 +25,24 @@ #define ETH_PPE_DUMP_NUM 576 #define ETH_PPE_STATIC_NUM 12 + +#define HNS_PPEV2_RSS_IND_TBL_SIZE 256 +#define HNS_PPEV2_RSS_KEY_SIZE 40 /* in bytes or 320 bits */ +#define HNS_PPEV2_RSS_KEY_NUM (HNS_PPEV2_RSS_KEY_SIZE / sizeof(u32)) + enum ppe_qid_mode { - PPE_QID_MODE0 = 0, /* fixed queue id mode */ - PPE_QID_MODE1, /* switch:128VM non switch:6Port/4VM/4TC */ - PPE_QID_MODE2, /* switch:32VM/4TC non switch:6Port/16VM */ - PPE_QID_MODE3, /* switch:4TC/8TAG non switch:2Port/64VM */ - PPE_QID_MODE4, /* switch:8VM/16TAG non switch:2Port/16VM/4TC */ - PPE_QID_MODE5, /* non switch:6Port/16TAG */ - PPE_QID_MODE6, /* non switch:6Port/2VM/8TC */ - PPE_QID_MODE7, /* non switch:2Port/8VM/8TC */ + PPE_QID_MODE0 = 0, /* fixed queue id mode */ + PPE_QID_MODE1, /* switch:128VM non switch:6Port/4VM/4TC */ + PPE_QID_MODE2, /* switch:32VM/4TC non switch:6Port/16VM */ + PPE_QID_MODE3, /* switch:4TC/8RSS non switch:2Port/64VM */ + PPE_QID_MODE4, /* switch:8VM/16RSS non switch:2Port/16VM/4TC */ + PPE_QID_MODE5, /* switch:16VM/8TC non switch:6Port/16RSS */ + PPE_QID_MODE6, /* switch:32VM/4RSS non switch:6Port/2VM/8TC */ + PPE_QID_MODE7, /* switch:32RSS non switch:2Port/8VM/8TC */ + PPE_QID_MODE8, /* switch:6VM/4TC/4RSS non switch:2Port/16VM/4RSS */ + PPE_QID_MODE9, /* non switch:2Port/32VM/2RSS */ + PPE_QID_MODE10, /* non switch:2Port/32RSS */ + PPE_QID_MODE11, /* non switch:2Port/4TC/16RSS */ }; enum ppe_port_mode { @@ -72,6 +81,8 @@ struct hns_ppe_cb { u8 port; /* port id in dsaf */ void __iomem *io_base; int virq; + u32 rss_indir_table[HNS_PPEV2_RSS_IND_TBL_SIZE]; /*shadow indir tab */ + u32 rss_key[HNS_PPEV2_RSS_KEY_NUM]; /* rss hash key */ }; struct ppe_common_cb { @@ -102,4 +113,9 @@ void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data); void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data); void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data); + +void hns_ppe_set_rss_key(struct hns_ppe_cb *ppe_cb, + const u32 rss_key[HNS_PPEV2_RSS_KEY_NUM]); +void hns_ppe_set_indir_table(struct hns_ppe_cb *ppe_cb, + const u32 rss_tab[HNS_PPEV2_RSS_IND_TBL_SIZE]); #endif /* _HNS_DSAF_PPE_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index cb0e9e1ecf2c..b070d57b204d 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -349,6 +349,8 @@ #define PPE_ECO0_REG 0x32C #define PPE_ECO1_REG 0x330 #define PPE_ECO2_REG 0x334 +#define PPEV2_INDRECTION_TBL_REG 0x800 +#define PPEV2_RSS_KEY_REG 0x900 #define RCB_COM_CFG_ENDIAN_REG 0x0 #define RCB_COM_CFG_SYS_FSH_REG 0xC @@ -839,6 +841,18 @@ #define PPE_CFG_QID_MODE_CF_QID_MODE_S 8 #define PPE_CFG_QID_MODE_CF_QID_MODE_M (0x7 << PPE_CFG_QID_MODE_CF_QID_MODE_S) +#define PPEV2_CFG_RSS_TBL_4N0_S 0 +#define PPEV2_CFG_RSS_TBL_4N0_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N0_S) + +#define PPEV2_CFG_RSS_TBL_4N1_S 8 +#define PPEV2_CFG_RSS_TBL_4N1_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N1_S) + +#define PPEV2_CFG_RSS_TBL_4N2_S 16 +#define PPEV2_CFG_RSS_TBL_4N2_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N2_S) + +#define PPEV2_CFG_RSS_TBL_4N3_S 24 +#define PPEV2_CFG_RSS_TBL_4N3_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N3_S) + #define PPE_CNT_CLR_CE_B 0 #define PPE_CNT_CLR_SNAP_EN_B 1 diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 9df63ae13915..3b234176dd36 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -1187,6 +1187,95 @@ static int hns_nic_nway_reset(struct net_device *netdev) return ret; } +static u32 +hns_get_rss_key_size(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_ae_ops *ops; + u32 ret; + + if (AE_IS_VER1(priv->enet_ver)) { + netdev_err(netdev, + "RSS feature is not supported on this hardware\n"); + return -EOPNOTSUPP; + } + + ops = priv->ae_handle->dev->ops; + ret = ops->get_rss_key_size(priv->ae_handle); + + return ret; +} + +static u32 +hns_get_rss_indir_size(struct net_device *netdev) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_ae_ops *ops; + u32 ret; + + if (AE_IS_VER1(priv->enet_ver)) { + netdev_err(netdev, + "RSS feature is not supported on this hardware\n"); + return -EOPNOTSUPP; + } + + ops = priv->ae_handle->dev->ops; + ret = ops->get_rss_indir_size(priv->ae_handle); + + return ret; +} + +static int +hns_get_rss(struct net_device *netdev, u32 *indir, u8 *key, u8 *hfunc) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_ae_ops *ops; + int ret; + + if (AE_IS_VER1(priv->enet_ver)) { + netdev_err(netdev, + "RSS feature is not supported on this hardware\n"); + return -EOPNOTSUPP; + } + + ops = priv->ae_handle->dev->ops; + + if (!indir) + return 0; + + ret = ops->get_rss(priv->ae_handle, indir, key, hfunc); + + return 0; +} + +static int +hns_set_rss(struct net_device *netdev, const u32 *indir, const u8 *key, + const u8 hfunc) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_ae_ops *ops; + int ret; + + if (AE_IS_VER1(priv->enet_ver)) { + netdev_err(netdev, + "RSS feature is not supported on this hardware\n"); + return -EOPNOTSUPP; + } + + ops = priv->ae_handle->dev->ops; + + /* currently hfunc can only be Toeplitz hash */ + if (key || + (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)) + return -EOPNOTSUPP; + if (!indir) + return 0; + + ret = ops->set_rss(priv->ae_handle, indir, key, hfunc); + + return 0; +} + static struct ethtool_ops hns_ethtool_ops = { .get_drvinfo = hns_nic_get_drvinfo, .get_link = hns_nic_get_link, @@ -1206,6 +1295,10 @@ static struct ethtool_ops hns_ethtool_ops = { .get_regs_len = hns_get_regs_len, .get_regs = hns_get_regs, .nway_reset = hns_nic_nway_reset, + .get_rxfh_key_size = hns_get_rss_key_size, + .get_rxfh_indir_size = hns_get_rss_indir_size, + .get_rxfh = hns_get_rss, + .set_rxfh = hns_set_rss, }; void hns_ethtool_set_ops(struct net_device *ndev) -- cgit v1.2.3-59-g8ed1b From 64353af63962f01a58af6c4f37736be80779ee3a Mon Sep 17 00:00:00 2001 From: Salil Date: Thu, 3 Dec 2015 12:17:55 +0000 Subject: net:hns: Add Hip06 "TSO(TCP Segment Offload)" support HNS Driver This patch adds the support of "TSO (TCP Segment Offload)" feature provided by the Hip06 ethernet hardware to the HNS ethernet driver. Enabling this feature would help offload the TCP Segmentation process to the Hip06 ethernet hardware. This eventually would help in saving precious cpu cycles. Signed-off-by: Salil Mehta Signed-off-by: lisheng Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.h | 1 + drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 8 +++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c | 5 ++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h | 2 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h | 1 + drivers/net/ethernet/hisilicon/hns/hns_enet.c | 82 ++++++++++++++++++++++- 6 files changed, 95 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index 76dd71563cc1..d1f33166e0c3 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -474,6 +474,7 @@ struct hnae_ae_ops { int (*set_mac_addr)(struct hnae_handle *handle, void *p); int (*set_mc_addr)(struct hnae_handle *handle, void *addr); int (*set_mtu)(struct hnae_handle *handle, int new_mtu); + void (*set_tso_stats)(struct hnae_handle *handle, int enable); void (*update_stats)(struct hnae_handle *handle, struct net_device_stats *net_stats); void (*get_stats)(struct hnae_handle *handle, u64 *data); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index 77e17351ade5..77c6edbe2666 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -277,6 +277,13 @@ static int hns_ae_set_mtu(struct hnae_handle *handle, int new_mtu) return hns_mac_set_mtu(mac_cb, new_mtu); } +static void hns_ae_set_tso_stats(struct hnae_handle *handle, int enable) +{ + struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle); + + hns_ppe_set_tso_enable(ppe_cb, enable); +} + static int hns_ae_start(struct hnae_handle *handle) { int ret; @@ -824,6 +831,7 @@ static struct hnae_ae_ops hns_dsaf_ops = { .set_mc_addr = hns_ae_set_multicast_one, .set_mtu = hns_ae_set_mtu, .update_stats = hns_ae_update_stats, + .set_tso_stats = hns_ae_set_tso_stats, .get_stats = hns_ae_get_stats, .get_strings = hns_ae_get_strings, .get_sset_count = hns_ae_get_sset_count, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c index 7af0858f1fcb..b5e4c44fc9fb 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c @@ -19,6 +19,11 @@ #include "hns_dsaf_ppe.h" +void hns_ppe_set_tso_enable(struct hns_ppe_cb *ppe_cb, u32 value) +{ + dsaf_set_dev_bit(ppe_cb, PPEV2_CFG_TSO_EN_REG, 0, !!value); +} + void hns_ppe_set_rss_key(struct hns_ppe_cb *ppe_cb, const u32 rss_key[HNS_PPEV2_RSS_KEY_NUM]) { diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h index dac853255eca..0f5cb6962acf 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h @@ -113,7 +113,7 @@ void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data); void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data); void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data); - +void hns_ppe_set_tso_enable(struct hns_ppe_cb *ppe_cb, u32 value); void hns_ppe_set_rss_key(struct hns_ppe_cb *ppe_cb, const u32 rss_key[HNS_PPEV2_RSS_KEY_NUM]); void hns_ppe_set_indir_table(struct hns_ppe_cb *ppe_cb, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index b070d57b204d..98c163ec1186 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -317,6 +317,7 @@ #define PPE_CFG_TAG_GEN_REG 0x90 #define PPE_CFG_PARSE_TAG_REG 0x94 #define PPE_CFG_PRO_CHECK_EN_REG 0x98 +#define PPEV2_CFG_TSO_EN_REG 0xA0 #define PPE_INTEN_REG 0x100 #define PPE_RINT_REG 0x104 #define PPE_INTSTS_REG 0x108 diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 0ca7fa90a193..c025a7195c17 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -223,6 +223,71 @@ static int hns_nic_maybe_stop_tx( return 0; } +static int hns_nic_maybe_stop_tso( + struct sk_buff **out_skb, int *bnum, struct hnae_ring *ring) +{ + int i; + int size; + int buf_num; + int frag_num; + struct sk_buff *skb = *out_skb; + struct sk_buff *new_skb = NULL; + struct skb_frag_struct *frag; + + size = skb_headlen(skb); + buf_num = (size + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE; + + frag_num = skb_shinfo(skb)->nr_frags; + for (i = 0; i < frag_num; i++) { + frag = &skb_shinfo(skb)->frags[i]; + size = skb_frag_size(frag); + buf_num += (size + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE; + } + + if (unlikely(buf_num > ring->max_desc_num_per_pkt)) { + buf_num = (skb->len + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE; + if (ring_space(ring) < buf_num) + return -EBUSY; + /* manual split the send packet */ + new_skb = skb_copy(skb, GFP_ATOMIC); + if (!new_skb) + return -ENOMEM; + dev_kfree_skb_any(skb); + *out_skb = new_skb; + + } else if (ring_space(ring) < buf_num) { + return -EBUSY; + } + + *bnum = buf_num; + return 0; +} + +static void fill_tso_desc(struct hnae_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + int buf_num, enum hns_desc_type type, int mtu) +{ + int frag_buf_num; + int sizeoflast; + int k; + + frag_buf_num = (size + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE; + sizeoflast = size % BD_MAX_SEND_SIZE; + sizeoflast = sizeoflast ? sizeoflast : BD_MAX_SEND_SIZE; + + /* when the frag size is bigger than hardware, split this frag */ + for (k = 0; k < frag_buf_num; k++) + fill_v2_desc(ring, priv, + (k == frag_buf_num - 1) ? + sizeoflast : BD_MAX_SEND_SIZE, + dma + BD_MAX_SEND_SIZE * k, + frag_end && (k == frag_buf_num - 1) ? 1 : 0, + buf_num, + (type == DESC_TYPE_SKB && !k) ? + DESC_TYPE_SKB : DESC_TYPE_PAGE, + mtu); +} + int hns_nic_net_xmit_hw(struct net_device *ndev, struct sk_buff *skb, struct hns_nic_ring_data *ring_data) @@ -1637,6 +1702,7 @@ static void hns_nic_uninit_ring_data(struct hns_nic_priv *priv) static void hns_nic_set_priv_ops(struct net_device *netdev) { struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; if (AE_IS_VER1(priv->enet_ver)) { priv->ops.fill_desc = fill_desc; @@ -1644,8 +1710,17 @@ static void hns_nic_set_priv_ops(struct net_device *netdev) priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx; } else { priv->ops.get_rxd_bnum = get_v2rx_desc_bnum; - priv->ops.fill_desc = fill_v2_desc; - priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx; + if ((netdev->features & NETIF_F_TSO) || + (netdev->features & NETIF_F_TSO6)) { + priv->ops.fill_desc = fill_tso_desc; + priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tso; + /* This chip only support 7*4096 */ + netif_set_gso_max_size(netdev, 7 * 4096); + h->dev->ops->set_tso_stats(h, 1); + } else { + priv->ops.fill_desc = fill_v2_desc; + priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx; + } } } @@ -1758,9 +1833,10 @@ static int hns_nic_dev_probe(struct platform_device *pdev) switch (priv->enet_ver) { case AE_VERSION_2: + ndev->features |= NETIF_F_TSO | NETIF_F_TSO6; ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | - NETIF_F_GRO; + NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6; break; default: break; -- cgit v1.2.3-59-g8ed1b From 38f616da1c28293798b3515a0f409531fff46c42 Mon Sep 17 00:00:00 2001 From: Salil Date: Thu, 3 Dec 2015 12:17:56 +0000 Subject: net:hns: Add support of ethtool TSO set option for Hip06 in HNS This patch adds the support of ethtool TSO option to support Hip06 SoC to HNS Signed-off-by: Salil Mehta Signed-off-by: lisheng Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_enet.c | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index c025a7195c17..cad266339200 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -1384,6 +1384,51 @@ static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu) return ret; } +static int hns_nic_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + struct hnae_handle *h = priv->ae_handle; + + switch (priv->enet_ver) { + case AE_VERSION_1: + if (features & (NETIF_F_TSO | NETIF_F_TSO6)) + netdev_info(netdev, "enet v1 do not support tso!\n"); + break; + default: + if (features & (NETIF_F_TSO | NETIF_F_TSO6)) { + priv->ops.fill_desc = fill_tso_desc; + priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tso; + /* The chip only support 7*4096 */ + netif_set_gso_max_size(netdev, 7 * 4096); + h->dev->ops->set_tso_stats(h, 1); + } else { + priv->ops.fill_desc = fill_v2_desc; + priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx; + h->dev->ops->set_tso_stats(h, 0); + } + break; + } + netdev->features = features; + return 0; +} + +static netdev_features_t hns_nic_fix_features( + struct net_device *netdev, netdev_features_t features) +{ + struct hns_nic_priv *priv = netdev_priv(netdev); + + switch (priv->enet_ver) { + case AE_VERSION_1: + features &= ~(NETIF_F_TSO | NETIF_F_TSO6 | + NETIF_F_HW_VLAN_CTAG_FILTER); + break; + default: + break; + } + return features; +} + /** * nic_set_multicast_list - set mutl mac address * @netdev: net device @@ -1479,6 +1524,8 @@ static const struct net_device_ops hns_nic_netdev_ops = { .ndo_set_mac_address = hns_nic_net_set_mac_address, .ndo_change_mtu = hns_nic_change_mtu, .ndo_do_ioctl = hns_nic_do_ioctl, + .ndo_set_features = hns_nic_set_features, + .ndo_fix_features = hns_nic_fix_features, .ndo_get_stats64 = hns_nic_get_stats64, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = hns_nic_poll_controller, -- cgit v1.2.3-59-g8ed1b From 8044f97ef5b1b7dfb037cfb78af9803ab76c7d92 Mon Sep 17 00:00:00 2001 From: Salil Date: Thu, 3 Dec 2015 12:17:57 +0000 Subject: net:hns: Add the init code to disable Hip06 "Hardware VLAN assist" This patch adds the initializzation code to disable the hardware vlan support for VLAN Tag stripping by default for now. Proper support of "hardware VLAN assitance" feature would soon come in the next coming patches. Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c | 7 ++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h | 103 +++++++++++----------- 2 files changed, 59 insertions(+), 51 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c index b5e4c44fc9fb..f302ef9073c6 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c @@ -176,6 +176,11 @@ static void hns_ppe_cnt_clr_ce(struct hns_ppe_cb *ppe_cb) PPE_CNT_CLR_CE_B, 1); } +static void hns_ppe_set_vlan_strip(struct hns_ppe_cb *ppe_cb, int en) +{ + dsaf_write_dev(ppe_cb, PPEV2_VLAN_STRIP_EN_REG, en); +} + /** * hns_ppe_checksum_hw - set ppe checksum caculate * @ppe_device: ppe device @@ -336,6 +341,8 @@ static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb) hns_ppe_cnt_clr_ce(ppe_cb); if (!AE_IS_VER1(dsaf_dev->dsaf_ver)) { + hns_ppe_set_vlan_strip(ppe_cb, 0); + /* set default RSS key in h/w */ hns_ppe_set_rss_key(ppe_cb, ppe_cb->rss_key); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index 98c163ec1186..c4d7c26952c4 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -49,56 +49,56 @@ #define DSAF_TCAM_SUM 512 #define DSAF_LINE_SUM (2048 * 14) -#define DSAF_SUB_SC_NT_SRAM_CLK_SEL_REG 0x100 -#define DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG 0x180 -#define DSAF_SUB_SC_HILINK3_CRG_CTRL1_REG 0x184 -#define DSAF_SUB_SC_HILINK3_CRG_CTRL2_REG 0x188 -#define DSAF_SUB_SC_HILINK3_CRG_CTRL3_REG 0x18C -#define DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG 0x190 -#define DSAF_SUB_SC_HILINK4_CRG_CTRL1_REG 0x194 -#define DSAF_SUB_SC_DSAF_CLK_EN_REG 0x300 -#define DSAF_SUB_SC_DSAF_CLK_DIS_REG 0x304 -#define DSAF_SUB_SC_NT_CLK_EN_REG 0x308 -#define DSAF_SUB_SC_NT_CLK_DIS_REG 0x30C -#define DSAF_SUB_SC_XGE_CLK_EN_REG 0x310 -#define DSAF_SUB_SC_XGE_CLK_DIS_REG 0x314 -#define DSAF_SUB_SC_GE_CLK_EN_REG 0x318 -#define DSAF_SUB_SC_GE_CLK_DIS_REG 0x31C -#define DSAF_SUB_SC_PPE_CLK_EN_REG 0x320 -#define DSAF_SUB_SC_PPE_CLK_DIS_REG 0x324 -#define DSAF_SUB_SC_RCB_PPE_COM_CLK_EN_REG 0x350 -#define DSAF_SUB_SC_RCB_PPE_COM_CLK_DIS_REG 0x354 -#define DSAF_SUB_SC_XBAR_RESET_REQ_REG 0xA00 -#define DSAF_SUB_SC_XBAR_RESET_DREQ_REG 0xA04 -#define DSAF_SUB_SC_NT_RESET_REQ_REG 0xA08 -#define DSAF_SUB_SC_NT_RESET_DREQ_REG 0xA0C -#define DSAF_SUB_SC_XGE_RESET_REQ_REG 0xA10 -#define DSAF_SUB_SC_XGE_RESET_DREQ_REG 0xA14 -#define DSAF_SUB_SC_GE_RESET_REQ0_REG 0xA18 -#define DSAF_SUB_SC_GE_RESET_DREQ0_REG 0xA1C -#define DSAF_SUB_SC_GE_RESET_REQ1_REG 0xA20 -#define DSAF_SUB_SC_GE_RESET_DREQ1_REG 0xA24 -#define DSAF_SUB_SC_PPE_RESET_REQ_REG 0xA48 -#define DSAF_SUB_SC_PPE_RESET_DREQ_REG 0xA4C -#define DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG 0xA88 -#define DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG 0xA8C -#define DSAF_SUB_SC_LIGHT_MODULE_DETECT_EN_REG 0x2060 -#define DSAF_SUB_SC_TCAM_MBIST_EN_REG 0x2300 -#define DSAF_SUB_SC_DSAF_CLK_ST_REG 0x5300 -#define DSAF_SUB_SC_NT_CLK_ST_REG 0x5304 -#define DSAF_SUB_SC_XGE_CLK_ST_REG 0x5308 -#define DSAF_SUB_SC_GE_CLK_ST_REG 0x530C -#define DSAF_SUB_SC_PPE_CLK_ST_REG 0x5310 -#define DSAF_SUB_SC_ROCEE_CLK_ST_REG 0x5314 -#define DSAF_SUB_SC_CPU_CLK_ST_REG 0x5318 -#define DSAF_SUB_SC_RCB_PPE_COM_CLK_ST_REG 0x5328 -#define DSAF_SUB_SC_XBAR_RESET_ST_REG 0x5A00 -#define DSAF_SUB_SC_NT_RESET_ST_REG 0x5A04 -#define DSAF_SUB_SC_XGE_RESET_ST_REG 0x5A08 -#define DSAF_SUB_SC_GE_RESET_ST0_REG 0x5A0C -#define DSAF_SUB_SC_GE_RESET_ST1_REG 0x5A10 -#define DSAF_SUB_SC_PPE_RESET_ST_REG 0x5A24 -#define DSAF_SUB_SC_RCB_PPE_COM_RESET_ST_REG 0x5A44 +#define DSAF_SUB_SC_NT_SRAM_CLK_SEL_REG 0x100 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG 0x180 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL1_REG 0x184 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL2_REG 0x188 +#define DSAF_SUB_SC_HILINK3_CRG_CTRL3_REG 0x18C +#define DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG 0x190 +#define DSAF_SUB_SC_HILINK4_CRG_CTRL1_REG 0x194 +#define DSAF_SUB_SC_DSAF_CLK_EN_REG 0x300 +#define DSAF_SUB_SC_DSAF_CLK_DIS_REG 0x304 +#define DSAF_SUB_SC_NT_CLK_EN_REG 0x308 +#define DSAF_SUB_SC_NT_CLK_DIS_REG 0x30C +#define DSAF_SUB_SC_XGE_CLK_EN_REG 0x310 +#define DSAF_SUB_SC_XGE_CLK_DIS_REG 0x314 +#define DSAF_SUB_SC_GE_CLK_EN_REG 0x318 +#define DSAF_SUB_SC_GE_CLK_DIS_REG 0x31C +#define DSAF_SUB_SC_PPE_CLK_EN_REG 0x320 +#define DSAF_SUB_SC_PPE_CLK_DIS_REG 0x324 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_EN_REG 0x350 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_DIS_REG 0x354 +#define DSAF_SUB_SC_XBAR_RESET_REQ_REG 0xA00 +#define DSAF_SUB_SC_XBAR_RESET_DREQ_REG 0xA04 +#define DSAF_SUB_SC_NT_RESET_REQ_REG 0xA08 +#define DSAF_SUB_SC_NT_RESET_DREQ_REG 0xA0C +#define DSAF_SUB_SC_XGE_RESET_REQ_REG 0xA10 +#define DSAF_SUB_SC_XGE_RESET_DREQ_REG 0xA14 +#define DSAF_SUB_SC_GE_RESET_REQ0_REG 0xA18 +#define DSAF_SUB_SC_GE_RESET_DREQ0_REG 0xA1C +#define DSAF_SUB_SC_GE_RESET_REQ1_REG 0xA20 +#define DSAF_SUB_SC_GE_RESET_DREQ1_REG 0xA24 +#define DSAF_SUB_SC_PPE_RESET_REQ_REG 0xA48 +#define DSAF_SUB_SC_PPE_RESET_DREQ_REG 0xA4C +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG 0xA88 +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG 0xA8C +#define DSAF_SUB_SC_LIGHT_MODULE_DETECT_EN_REG 0x2060 +#define DSAF_SUB_SC_TCAM_MBIST_EN_REG 0x2300 +#define DSAF_SUB_SC_DSAF_CLK_ST_REG 0x5300 +#define DSAF_SUB_SC_NT_CLK_ST_REG 0x5304 +#define DSAF_SUB_SC_XGE_CLK_ST_REG 0x5308 +#define DSAF_SUB_SC_GE_CLK_ST_REG 0x530C +#define DSAF_SUB_SC_PPE_CLK_ST_REG 0x5310 +#define DSAF_SUB_SC_ROCEE_CLK_ST_REG 0x5314 +#define DSAF_SUB_SC_CPU_CLK_ST_REG 0x5318 +#define DSAF_SUB_SC_RCB_PPE_COM_CLK_ST_REG 0x5328 +#define DSAF_SUB_SC_XBAR_RESET_ST_REG 0x5A00 +#define DSAF_SUB_SC_NT_RESET_ST_REG 0x5A04 +#define DSAF_SUB_SC_XGE_RESET_ST_REG 0x5A08 +#define DSAF_SUB_SC_GE_RESET_ST0_REG 0x5A0C +#define DSAF_SUB_SC_GE_RESET_ST1_REG 0x5A10 +#define DSAF_SUB_SC_PPE_RESET_ST_REG 0x5A24 +#define DSAF_SUB_SC_RCB_PPE_COM_RESET_ST_REG 0x5A44 /*serdes offset**/ #define HNS_MAC_HILINK3_REG DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG @@ -317,7 +317,8 @@ #define PPE_CFG_TAG_GEN_REG 0x90 #define PPE_CFG_PARSE_TAG_REG 0x94 #define PPE_CFG_PRO_CHECK_EN_REG 0x98 -#define PPEV2_CFG_TSO_EN_REG 0xA0 +#define PPEV2_CFG_TSO_EN_REG 0xA0 +#define PPEV2_VLAN_STRIP_EN_REG 0xAC #define PPE_INTEN_REG 0x100 #define PPE_RINT_REG 0x104 #define PPE_INTSTS_REG 0x108 -- cgit v1.2.3-59-g8ed1b From 93725149794d3d418cf1eddcae60c7b536c5faa1 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Thu, 3 Dec 2015 19:24:18 +0100 Subject: net: qmi_wwan: MDM9x30 specific power management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MDM9x30 based modems appear to go into a deeper sleep when suspended without "Remote Wakeup" enabled. The QMI interface will not respond unless a "set DTR" control request is sent on resume. The effect is similar to a QMI_CTL SYNC request, resetting (some of) the firmware state. We allow userspace sessions to span multiple character device open/close sequences. This means that userspace can depend on firmware state while both the netdev and the character device are closed. We have disabled "needs_remote_wakeup" at this point to allow devices without remote wakeup support to be auto-suspended. To make sure the MDM9x30 keeps firmware state, we need to keep "needs_remote_wakeup" always set. We also need to issue a "set DTR" request to enable the QMI interface. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 9a5be8b85186..fc9dd452a3b5 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -223,6 +223,20 @@ err: return rv; } +/* Send CDC SetControlLineState request, setting or clearing the DTR. + * "Required for Autoconnect and 9x30 to wake up" according to the + * GobiNet driver. The requirement has been verified on an MDM9230 + * based Sierra Wireless MC7455 + */ +static int qmi_wwan_change_dtr(struct usbnet *dev, bool on) +{ + u8 intf = dev->intf->cur_altsetting->desc.bInterfaceNumber; + + return usbnet_write_cmd(dev, USB_CDC_REQ_SET_CONTROL_LINE_STATE, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + on ? 0x01 : 0x00, intf, NULL, 0); +} + static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) { int status = -1; @@ -280,6 +294,24 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) usb_driver_release_interface(driver, info->data); } + /* disabling remote wakeup on MDM9x30 devices has the same + * effect as clearing DTR. The device will not respond to QMI + * requests until we set DTR again. This is similar to a + * QMI_CTL SYNC request, clearing a lot of firmware state + * including the client ID allocations. + * + * Our usage model allows a session to span multiple + * open/close events, so we must prevent the firmware from + * clearing out state the clients might need. + * + * MDM9x30 is the first QMI chipset with USB3 support. Abuse + * this fact to enable the quirk. + */ + if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) { + qmi_wwan_manage_power(dev, 1); + qmi_wwan_change_dtr(dev, true); + } + /* Never use the same address on both ends of the link, even if the * buggy firmware told us to. Or, if device is assigned the well-known * buggy firmware MAC address, replace it with a random address, @@ -307,6 +339,12 @@ static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf) if (info->subdriver && info->subdriver->disconnect) info->subdriver->disconnect(info->control); + /* disable MDM9x30 quirk */ + if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) { + qmi_wwan_change_dtr(dev, false); + qmi_wwan_manage_power(dev, 0); + } + /* allow user to unbind using either control or data */ if (intf == info->control) other = info->data; -- cgit v1.2.3-59-g8ed1b From 544c8f6507303e921979ac99f46f00a88b42b6e8 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Thu, 3 Dec 2015 19:24:19 +0100 Subject: net: qmi_wwan: remove 1199:9070 device id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This turned out to be a bootloader device ID. No need for that in this driver. It will only provide a single serial function. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index fc9dd452a3b5..e3727b66d850 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -753,8 +753,6 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1199, 0x9056, 8)}, /* Sierra Wireless Modem */ {QMI_FIXED_INTF(0x1199, 0x9057, 8)}, {QMI_FIXED_INTF(0x1199, 0x9061, 8)}, /* Sierra Wireless Modem */ - {QMI_FIXED_INTF(0x1199, 0x9070, 8)}, /* Sierra Wireless MC74xx/EM74xx */ - {QMI_FIXED_INTF(0x1199, 0x9070, 10)}, /* Sierra Wireless MC74xx/EM74xx */ {QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx/EM74xx */ {QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx/EM74xx */ {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ -- cgit v1.2.3-59-g8ed1b From 81e0ce79f2919dbd5f025894d29aa806af8695c7 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Thu, 3 Dec 2015 19:24:20 +0100 Subject: usbnet: allow mini-drivers to consume L2 headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assume the minidriver has taken care of all L2 header parsing if it sets skb->protocol. This allows the minidriver to support non-ethernet L2 headers, and even operate without any L2 header at all. Signed-off-by: Bjørn Mork Acked-by: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/usbnet.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 0744bf2ef2d6..0b0ba7ef14e4 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -324,7 +324,10 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) return; } - skb->protocol = eth_type_trans (skb, dev->net); + /* only update if unset to allow minidriver rx_fixup override */ + if (skb->protocol == 0) + skb->protocol = eth_type_trans (skb, dev->net); + dev->net->stats.rx_packets++; dev->net->stats.rx_bytes += skb->len; -- cgit v1.2.3-59-g8ed1b From 32f7adf633b9f99ad5089901bc7ebff57704aaa9 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Thu, 3 Dec 2015 19:24:21 +0100 Subject: net: qmi_wwan: support "raw IP" mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QMI wwan devices have traditionally emulated ethernet devices by default. But they have always had the capability of operating without any L2 header at all, transmitting and receiving "raw" IP packets over the USB link. This firmware feature used to be configurable through the QMI management protocol. Traditionally there was no way to verify the firmware mode without attempting to change it. And the firmware would often disallow changes anyway, i.e. due to a session already being established. In some cases, this could be a hidden firmware internal session, completely outside host control. For these reasons, sticking with the "well known" default mode was safest. But newer generations of QMI hardware and firmware have moved towards defaulting to "raw IP" mode instead, followed by an increasing number of bugs in the already buggy "802.3" firmware implementation. At the same time, the QMI management protocol gained the ability to detect the current mode. This has enabled the userspace QMI management application to verify the current firmware mode without trying to modify it. Following this development, the latest QMI hardware and firmware (the MDM9x30 generation) has dropped support for "802.3" mode entirely. Support for "raw IP" framing in the driver is therefore necessary for these devices, and to a certain degree to work around problems with the previous generation, This patch adds support for "raw IP" framing for QMI devices, changing the netdev from an ethernet device to an ARPHRD_NONE p-t-p device when "raw IP" framing is enabled. The firmware setup is fully delegated to the QMI userspace management application, through simple tunneling of the QMI protocol. The driver will therefore not know which mode has been "negotiated" between firmware and userspace. Allowing userspace to inform the driver of the result through a sysfs switch is considered a better alternative than to change the well established clean delegation of firmware management to userspace. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 98 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index e3727b66d850..98add3bf8821 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -48,11 +49,93 @@ struct qmi_wwan_state { struct usb_driver *subdriver; atomic_t pmcount; - unsigned long unused; + unsigned long flags; struct usb_interface *control; struct usb_interface *data; }; +enum qmi_wwan_flags { + QMI_WWAN_FLAG_RAWIP = 1 << 0, +}; + +static void qmi_wwan_netdev_setup(struct net_device *net) +{ + struct usbnet *dev = netdev_priv(net); + struct qmi_wwan_state *info = (void *)&dev->data; + + if (info->flags & QMI_WWAN_FLAG_RAWIP) { + net->header_ops = NULL; /* No header */ + net->type = ARPHRD_NONE; + net->hard_header_len = 0; + net->addr_len = 0; + net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + netdev_dbg(net, "mode: raw IP\n"); + } else if (!net->header_ops) { /* don't bother if already set */ + ether_setup(net); + netdev_dbg(net, "mode: Ethernet\n"); + } + + /* recalculate buffers after changing hard_header_len */ + usbnet_change_mtu(net, net->mtu); +} + +static ssize_t raw_ip_show(struct device *d, struct device_attribute *attr, char *buf) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct qmi_wwan_state *info = (void *)&dev->data; + + return sprintf(buf, "%c\n", info->flags & QMI_WWAN_FLAG_RAWIP ? 'Y' : 'N'); +} + +static ssize_t raw_ip_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct qmi_wwan_state *info = (void *)&dev->data; + bool enable; + int err; + + if (strtobool(buf, &enable)) + return -EINVAL; + + /* no change? */ + if (enable == (info->flags & QMI_WWAN_FLAG_RAWIP)) + return len; + + /* we don't want to modify a running netdev */ + if (netif_running(dev->net)) { + netdev_err(dev->net, "Cannot change a running device\n"); + return -EBUSY; + } + + /* let other drivers deny the change */ + err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev->net); + err = notifier_to_errno(err); + if (err) { + netdev_err(dev->net, "Type change was refused\n"); + return err; + } + + if (enable) + info->flags |= QMI_WWAN_FLAG_RAWIP; + else + info->flags &= ~QMI_WWAN_FLAG_RAWIP; + qmi_wwan_netdev_setup(dev->net); + call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev->net); + return len; +} + +static DEVICE_ATTR_RW(raw_ip); + +static struct attribute *qmi_wwan_sysfs_attrs[] = { + &dev_attr_raw_ip.attr, + NULL, +}; + +static struct attribute_group qmi_wwan_sysfs_attr_group = { + .name = "qmi", + .attrs = qmi_wwan_sysfs_attrs, +}; + /* default ethernet address used by the modem */ static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3}; @@ -80,6 +163,8 @@ static const u8 buggy_fw_addr[ETH_ALEN] = {0x00, 0xa0, 0xc6, 0x00, 0x00, 0x00}; */ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb) { + struct qmi_wwan_state *info = (void *)&dev->data; + bool rawip = info->flags & QMI_WWAN_FLAG_RAWIP; __be16 proto; /* This check is no longer done by usbnet */ @@ -94,15 +179,25 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb) proto = htons(ETH_P_IPV6); break; case 0x00: + if (rawip) + return 0; if (is_multicast_ether_addr(skb->data)) return 1; /* possibly bogus destination - rewrite just in case */ skb_reset_mac_header(skb); goto fix_dest; default: + if (rawip) + return 0; /* pass along other packets without modifications */ return 1; } + if (rawip) { + skb->dev = dev->net; /* normally set by eth_type_trans */ + skb->protocol = proto; + return 1; + } + if (skb_headroom(skb) < ETH_HLEN) return 0; skb_push(skb, ETH_HLEN); @@ -326,6 +421,7 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */ } dev->net->netdev_ops = &qmi_wwan_netdev_ops; + dev->net->sysfs_groups[0] = &qmi_wwan_sysfs_attr_group; err: return status; } -- cgit v1.2.3-59-g8ed1b From ff3516442768f0babe7ea2db62e34aee1d76e969 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 3 Dec 2015 21:12:30 +0100 Subject: WAN: HDLC: Detach protocol before unregistering device The current code first unregisters the device, and then detaches the protocol from it. This should be performed the other way around, since the detach may try to use state which has been freed by the unregister. Swap the order, so that we first detach and then remove the netdev. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/wan/hdlc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index 51f6cee8aab2..2a6595b4ae15 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -266,8 +266,8 @@ struct net_device *alloc_hdlcdev(void *priv) void unregister_hdlc_device(struct net_device *dev) { rtnl_lock(); - unregister_netdevice(dev); detach_hdlc_protocol(dev); + unregister_netdevice(dev); rtnl_unlock(); } -- cgit v1.2.3-59-g8ed1b From 2f8364a291e8adde25c93f97a76abbcaf4b1ed3f Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 3 Dec 2015 21:12:31 +0100 Subject: WAN: HDLC: Call notifiers before and after changing device type An HDLC device can change type when the protocol driver is changed. Calling the notifier change allows potential users of the interface know about this planned change, and even block it. After the change has occurred, send a second notification to users can evaluate the new device type etc. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/wan/hdlc.c | 19 +++++++++++++++++-- drivers/net/wan/hdlc_cisco.c | 1 + drivers/net/wan/hdlc_fr.c | 1 + drivers/net/wan/hdlc_ppp.c | 1 + drivers/net/wan/hdlc_raw.c | 1 + drivers/net/wan/hdlc_raw_eth.c | 1 + drivers/net/wan/hdlc_x25.c | 1 + include/linux/hdlc.h | 2 +- 8 files changed, 24 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index 2a6595b4ae15..9bd4aa8083ce 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -276,7 +276,11 @@ void unregister_hdlc_device(struct net_device *dev) int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto, size_t size) { - detach_hdlc_protocol(dev); + int err; + + err = detach_hdlc_protocol(dev); + if (err) + return err; if (!try_module_get(proto->module)) return -ENOSYS; @@ -289,15 +293,24 @@ int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto, } } dev_to_hdlc(dev)->proto = proto; + return 0; } -void detach_hdlc_protocol(struct net_device *dev) +int detach_hdlc_protocol(struct net_device *dev) { hdlc_device *hdlc = dev_to_hdlc(dev); + int err; if (hdlc->proto) { + err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev); + err = notifier_to_errno(err); + if (err) { + netdev_err(dev, "Refused to change device type\n"); + return err; + } + if (hdlc->proto->detach) hdlc->proto->detach(dev); module_put(hdlc->proto->module); @@ -306,6 +319,8 @@ void detach_hdlc_protocol(struct net_device *dev) kfree(hdlc->state); hdlc->state = NULL; hdlc_setup_dev(dev); + + return 0; } diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index 3f20808b5ff8..a408abc25512 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -378,6 +378,7 @@ static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr) spin_lock_init(&state(hdlc)->lock); dev->header_ops = &cisco_header_ops; dev->type = ARPHRD_CISCO; + call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev); netif_dormant_on(dev); return 0; } diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index 89541cc90e87..b6e0cfb095d3 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -1240,6 +1240,7 @@ static int fr_ioctl(struct net_device *dev, struct ifreq *ifr) } memcpy(&state(hdlc)->settings, &new_settings, size); dev->type = ARPHRD_FRAD; + call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev); return 0; case IF_PROTO_FR_ADD_PVC: diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c index 0d7645581f91..47fdb87d3567 100644 --- a/drivers/net/wan/hdlc_ppp.c +++ b/drivers/net/wan/hdlc_ppp.c @@ -687,6 +687,7 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr) dev->hard_header_len = sizeof(struct hdlc_header); dev->header_ops = &ppp_header_ops; dev->type = ARPHRD_PPP; + call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev); netif_dormant_on(dev); return 0; } diff --git a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c index 5dc153e8a29d..4feb45001aac 100644 --- a/drivers/net/wan/hdlc_raw.c +++ b/drivers/net/wan/hdlc_raw.c @@ -84,6 +84,7 @@ static int raw_ioctl(struct net_device *dev, struct ifreq *ifr) return result; memcpy(hdlc->state, &new_settings, size); dev->type = ARPHRD_RAWHDLC; + call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev); netif_dormant_off(dev); return 0; } diff --git a/drivers/net/wan/hdlc_raw_eth.c b/drivers/net/wan/hdlc_raw_eth.c index 3ab72b3082de..2f11836078ab 100644 --- a/drivers/net/wan/hdlc_raw_eth.c +++ b/drivers/net/wan/hdlc_raw_eth.c @@ -102,6 +102,7 @@ static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr) ether_setup(dev); dev->tx_queue_len = old_qlen; eth_hw_addr_random(dev); + call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev); netif_dormant_off(dev); return 0; } diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c index a49aec5efd20..e867638067a6 100644 --- a/drivers/net/wan/hdlc_x25.c +++ b/drivers/net/wan/hdlc_x25.c @@ -213,6 +213,7 @@ static int x25_ioctl(struct net_device *dev, struct ifreq *ifr) if ((result = attach_hdlc_protocol(dev, &proto, 0))) return result; dev->type = ARPHRD_X25; + call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev); netif_dormant_off(dev); return 0; } diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h index 1acb1445e05f..e31bcd4c7859 100644 --- a/include/linux/hdlc.h +++ b/include/linux/hdlc.h @@ -101,7 +101,7 @@ netdev_tx_t hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev); int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto, size_t size); /* May be used by hardware driver to gain control over HDLC device */ -void detach_hdlc_protocol(struct net_device *dev); +int detach_hdlc_protocol(struct net_device *dev); static __inline__ __be16 hdlc_type_trans(struct sk_buff *skb, struct net_device *dev) -- cgit v1.2.3-59-g8ed1b From e94d91a6eb155ff77110863d15ba51b3c6b5c548 Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Fri, 4 Dec 2015 08:43:19 +0100 Subject: atm: solos-pci: Replace simple_strtol by kstrtoint The simple_strtol function is obsolete. This patch replace it by kstrtoint. This will simplify code, since some error case not handled by simple_strtol are handled by kstrtoint. Signed-off-by: LABBE Corentin Signed-off-by: David S. Miller --- drivers/atm/solos-pci.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 3d7fb6516f74..0c2b4ba06813 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -347,8 +347,8 @@ static char *next_string(struct sk_buff *skb) */ static int process_status(struct solos_card *card, int port, struct sk_buff *skb) { - char *str, *end, *state_str, *snr, *attn; - int ver, rate_up, rate_down; + char *str, *state_str, *snr, *attn; + int ver, rate_up, rate_down, err; if (!card->atmdev[port]) return -ENODEV; @@ -357,7 +357,11 @@ static int process_status(struct solos_card *card, int port, struct sk_buff *skb if (!str) return -EIO; - ver = simple_strtol(str, NULL, 10); + err = kstrtoint(str, 10, &ver); + if (err) { + dev_warn(&card->dev->dev, "Unexpected status interrupt version\n"); + return err; + } if (ver < 1) { dev_warn(&card->dev->dev, "Unexpected status interrupt version %d\n", ver); @@ -373,16 +377,16 @@ static int process_status(struct solos_card *card, int port, struct sk_buff *skb return 0; } - rate_down = simple_strtol(str, &end, 10); - if (*end) - return -EIO; + err = kstrtoint(str, 10, &rate_down); + if (err) + return err; str = next_string(skb); if (!str) return -EIO; - rate_up = simple_strtol(str, &end, 10); - if (*end) - return -EIO; + err = kstrtoint(str, 10, &rate_up); + if (err) + return err; state_str = next_string(skb); if (!state_str) @@ -417,7 +421,7 @@ static int process_command(struct solos_card *card, int port, struct sk_buff *sk struct solos_param *prm; unsigned long flags; int cmdpid; - int found = 0; + int found = 0, err; if (skb->len < 7) return 0; @@ -428,7 +432,9 @@ static int process_command(struct solos_card *card, int port, struct sk_buff *sk skb->data[6] != '\n') return 0; - cmdpid = simple_strtol(&skb->data[1], NULL, 10); + err = kstrtoint(&skb->data[1], 10, &cmdpid); + if (err) + return err; spin_lock_irqsave(&card->param_queue_lock, flags); list_for_each_entry(prm, &card->param_queue, list) { -- cgit v1.2.3-59-g8ed1b From abd86a55f4079d00ba2a0bcca6fe33be45f3e2ff Mon Sep 17 00:00:00 2001 From: Daniel Pieczko Date: Fri, 4 Dec 2015 08:48:39 +0000 Subject: sfc: check warm_boot_count after other functions have been reset A change in MCFW behaviour means that the net driver must update its record of the warm_boot_count by reading it from the ER_DZ_BIU_MC_SFT_STATUS register. On v4.6.x MCFW the global boot count was incremented when some functions needed to be reset to enable multicast chaining, so all functions saw the same value. In that case, the driver needed to increment its warm_boot_count when other functions were reset, to avoid noticing it later and then trying to reset itself to recover unnecessarily. With v4.7+ MCFW, the boot count in firmware doesn't change as that is unnecessary since the PFs that have been reset will each receive an MC reboot notification. In that case, the driver re-reads the unchanged value. Signed-off-by: Bert Kenward Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 425df3dbc77d..c4a0e8a967dd 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -2374,8 +2374,19 @@ static int efx_ef10_ev_init(struct efx_channel *channel) 1 << MC_CMD_WORKAROUND_EXT_OUT_FLR_DONE_LBN) { netif_info(efx, drv, efx->net_dev, "other functions on NIC have been reset\n"); - /* MC's boot count has incremented */ - ++nic_data->warm_boot_count; + + /* With MCFW v4.6.x and earlier, the + * boot count will have incremented, + * so re-read the warm_boot_count + * value now to ensure this function + * doesn't think it has changed next + * time it checks. + */ + rc = efx_ef10_get_warm_boot_count(efx); + if (rc >= 0) { + nic_data->warm_boot_count = rc; + rc = 0; + } } nic_data->workaround_26807 = true; } else if (rc == -EPERM) { -- cgit v1.2.3-59-g8ed1b From 5c9ffde4a02144c4f7362152853d69138c437f17 Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Fri, 4 Dec 2015 17:22:34 +0100 Subject: bnx2x: drop redundant error message about allocation failure alloc_pages() already prints a warning when it fails. No need to emit another message. Certainly not at KERN_ERR level, because it is no big deal if this GFP_ATOMIC allocation fails occasionally. Signed-off-by: Michal Schmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index d9add7c02e42..5b6c17c53d37 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -558,10 +558,8 @@ static int bnx2x_alloc_rx_sge(struct bnx2x *bp, struct bnx2x_fastpath *fp, put_page(pool->page); pool->page = alloc_pages(gfp_mask, PAGES_PER_SGE_SHIFT); - if (unlikely(!pool->page)) { - BNX2X_ERR("Can't alloc sge\n"); + if (unlikely(!pool->page)) return -ENOMEM; - } pool->offset = 0; } -- cgit v1.2.3-59-g8ed1b From 9adab1b036b3b95edffdfe7835133b2a20eb7473 Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Fri, 4 Dec 2015 17:22:35 +0100 Subject: bnx2x: change FW GRO error message to WARN_ONCE It's supposed to be impossible for TPA to give us anything else than IPv4 or IPv6 here. But in case there is a way to reach this error by some strange received frames, we don't want to flood the kernel log. WARN_ONCE is better for this purpose. Signed-off-by: Michal Schmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 5b6c17c53d37..f6634a524153 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -743,7 +743,7 @@ static void bnx2x_gro_receive(struct bnx2x *bp, struct bnx2x_fastpath *fp, bnx2x_gro_csum(bp, skb, bnx2x_gro_ipv6_csum); break; default: - BNX2X_ERR("Error: FW GRO supports only IPv4/IPv6, not 0x%04x\n", + WARN_ONCE(1, "Error: FW GRO supports only IPv4/IPv6, not 0x%04x\n", be16_to_cpu(skb->protocol)); } } -- cgit v1.2.3-59-g8ed1b From 44c33c66310c76979649b7c0004af7744bd4ce7a Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Fri, 4 Dec 2015 17:22:36 +0100 Subject: bnx2x: simplify distinction between port and func stats The 'flags' field in bnx2x_stats_arr[] serves only one purpose - to tell us if the statistic is a per-port stat and thus should not be shown for virtual functions. It's strange that the field can have three different values. A boolean will do just fine. Also remove IS_FUNC_STAT(). It was used only once and it's in fact just a negation of IS_PORT_STAT(). Signed-off-by: Michal Schmidt Signed-off-by: David S. Miller --- .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 117 ++++++++++----------- 1 file changed, 56 insertions(+), 61 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index a3ce9f2a2335..820b7e04bb5f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -74,118 +74,115 @@ static const struct { static const struct { long offset; int size; - u32 flags; -#define STATS_FLAGS_PORT 1 -#define STATS_FLAGS_FUNC 2 -#define STATS_FLAGS_BOTH (STATS_FLAGS_FUNC | STATS_FLAGS_PORT) + bool is_port_stat; char string[ETH_GSTRING_LEN]; } bnx2x_stats_arr[] = { /* 1 */ { STATS_OFFSET32(total_bytes_received_hi), - 8, STATS_FLAGS_BOTH, "rx_bytes" }, + 8, false, "rx_bytes" }, { STATS_OFFSET32(error_bytes_received_hi), - 8, STATS_FLAGS_BOTH, "rx_error_bytes" }, + 8, false, "rx_error_bytes" }, { STATS_OFFSET32(total_unicast_packets_received_hi), - 8, STATS_FLAGS_BOTH, "rx_ucast_packets" }, + 8, false, "rx_ucast_packets" }, { STATS_OFFSET32(total_multicast_packets_received_hi), - 8, STATS_FLAGS_BOTH, "rx_mcast_packets" }, + 8, false, "rx_mcast_packets" }, { STATS_OFFSET32(total_broadcast_packets_received_hi), - 8, STATS_FLAGS_BOTH, "rx_bcast_packets" }, + 8, false, "rx_bcast_packets" }, { STATS_OFFSET32(rx_stat_dot3statsfcserrors_hi), - 8, STATS_FLAGS_PORT, "rx_crc_errors" }, + 8, true, "rx_crc_errors" }, { STATS_OFFSET32(rx_stat_dot3statsalignmenterrors_hi), - 8, STATS_FLAGS_PORT, "rx_align_errors" }, + 8, true, "rx_align_errors" }, { STATS_OFFSET32(rx_stat_etherstatsundersizepkts_hi), - 8, STATS_FLAGS_PORT, "rx_undersize_packets" }, + 8, true, "rx_undersize_packets" }, { STATS_OFFSET32(etherstatsoverrsizepkts_hi), - 8, STATS_FLAGS_PORT, "rx_oversize_packets" }, + 8, true, "rx_oversize_packets" }, /* 10 */{ STATS_OFFSET32(rx_stat_etherstatsfragments_hi), - 8, STATS_FLAGS_PORT, "rx_fragments" }, + 8, true, "rx_fragments" }, { STATS_OFFSET32(rx_stat_etherstatsjabbers_hi), - 8, STATS_FLAGS_PORT, "rx_jabbers" }, + 8, true, "rx_jabbers" }, { STATS_OFFSET32(no_buff_discard_hi), - 8, STATS_FLAGS_BOTH, "rx_discards" }, + 8, false, "rx_discards" }, { STATS_OFFSET32(mac_filter_discard), - 4, STATS_FLAGS_PORT, "rx_filtered_packets" }, + 4, true, "rx_filtered_packets" }, { STATS_OFFSET32(mf_tag_discard), - 4, STATS_FLAGS_PORT, "rx_mf_tag_discard" }, + 4, true, "rx_mf_tag_discard" }, { STATS_OFFSET32(pfc_frames_received_hi), - 8, STATS_FLAGS_PORT, "pfc_frames_received" }, + 8, true, "pfc_frames_received" }, { STATS_OFFSET32(pfc_frames_sent_hi), - 8, STATS_FLAGS_PORT, "pfc_frames_sent" }, + 8, true, "pfc_frames_sent" }, { STATS_OFFSET32(brb_drop_hi), - 8, STATS_FLAGS_PORT, "rx_brb_discard" }, + 8, true, "rx_brb_discard" }, { STATS_OFFSET32(brb_truncate_hi), - 8, STATS_FLAGS_PORT, "rx_brb_truncate" }, + 8, true, "rx_brb_truncate" }, { STATS_OFFSET32(pause_frames_received_hi), - 8, STATS_FLAGS_PORT, "rx_pause_frames" }, + 8, true, "rx_pause_frames" }, { STATS_OFFSET32(rx_stat_maccontrolframesreceived_hi), - 8, STATS_FLAGS_PORT, "rx_mac_ctrl_frames" }, + 8, true, "rx_mac_ctrl_frames" }, { STATS_OFFSET32(nig_timer_max), - 4, STATS_FLAGS_PORT, "rx_constant_pause_events" }, + 4, true, "rx_constant_pause_events" }, /* 20 */{ STATS_OFFSET32(rx_err_discard_pkt), - 4, STATS_FLAGS_BOTH, "rx_phy_ip_err_discards"}, + 4, false, "rx_phy_ip_err_discards"}, { STATS_OFFSET32(rx_skb_alloc_failed), - 4, STATS_FLAGS_BOTH, "rx_skb_alloc_discard" }, + 4, false, "rx_skb_alloc_discard" }, { STATS_OFFSET32(hw_csum_err), - 4, STATS_FLAGS_BOTH, "rx_csum_offload_errors" }, + 4, false, "rx_csum_offload_errors" }, { STATS_OFFSET32(driver_xoff), - 4, STATS_FLAGS_BOTH, "tx_exhaustion_events" }, + 4, false, "tx_exhaustion_events" }, { STATS_OFFSET32(total_bytes_transmitted_hi), - 8, STATS_FLAGS_BOTH, "tx_bytes" }, + 8, false, "tx_bytes" }, { STATS_OFFSET32(tx_stat_ifhcoutbadoctets_hi), - 8, STATS_FLAGS_PORT, "tx_error_bytes" }, + 8, true, "tx_error_bytes" }, { STATS_OFFSET32(total_unicast_packets_transmitted_hi), - 8, STATS_FLAGS_BOTH, "tx_ucast_packets" }, + 8, false, "tx_ucast_packets" }, { STATS_OFFSET32(total_multicast_packets_transmitted_hi), - 8, STATS_FLAGS_BOTH, "tx_mcast_packets" }, + 8, false, "tx_mcast_packets" }, { STATS_OFFSET32(total_broadcast_packets_transmitted_hi), - 8, STATS_FLAGS_BOTH, "tx_bcast_packets" }, + 8, false, "tx_bcast_packets" }, { STATS_OFFSET32(tx_stat_dot3statsinternalmactransmiterrors_hi), - 8, STATS_FLAGS_PORT, "tx_mac_errors" }, + 8, true, "tx_mac_errors" }, { STATS_OFFSET32(rx_stat_dot3statscarriersenseerrors_hi), - 8, STATS_FLAGS_PORT, "tx_carrier_errors" }, + 8, true, "tx_carrier_errors" }, /* 30 */{ STATS_OFFSET32(tx_stat_dot3statssinglecollisionframes_hi), - 8, STATS_FLAGS_PORT, "tx_single_collisions" }, + 8, true, "tx_single_collisions" }, { STATS_OFFSET32(tx_stat_dot3statsmultiplecollisionframes_hi), - 8, STATS_FLAGS_PORT, "tx_multi_collisions" }, + 8, true, "tx_multi_collisions" }, { STATS_OFFSET32(tx_stat_dot3statsdeferredtransmissions_hi), - 8, STATS_FLAGS_PORT, "tx_deferred" }, + 8, true, "tx_deferred" }, { STATS_OFFSET32(tx_stat_dot3statsexcessivecollisions_hi), - 8, STATS_FLAGS_PORT, "tx_excess_collisions" }, + 8, true, "tx_excess_collisions" }, { STATS_OFFSET32(tx_stat_dot3statslatecollisions_hi), - 8, STATS_FLAGS_PORT, "tx_late_collisions" }, + 8, true, "tx_late_collisions" }, { STATS_OFFSET32(tx_stat_etherstatscollisions_hi), - 8, STATS_FLAGS_PORT, "tx_total_collisions" }, + 8, true, "tx_total_collisions" }, { STATS_OFFSET32(tx_stat_etherstatspkts64octets_hi), - 8, STATS_FLAGS_PORT, "tx_64_byte_packets" }, + 8, true, "tx_64_byte_packets" }, { STATS_OFFSET32(tx_stat_etherstatspkts65octetsto127octets_hi), - 8, STATS_FLAGS_PORT, "tx_65_to_127_byte_packets" }, + 8, true, "tx_65_to_127_byte_packets" }, { STATS_OFFSET32(tx_stat_etherstatspkts128octetsto255octets_hi), - 8, STATS_FLAGS_PORT, "tx_128_to_255_byte_packets" }, + 8, true, "tx_128_to_255_byte_packets" }, { STATS_OFFSET32(tx_stat_etherstatspkts256octetsto511octets_hi), - 8, STATS_FLAGS_PORT, "tx_256_to_511_byte_packets" }, + 8, true, "tx_256_to_511_byte_packets" }, /* 40 */{ STATS_OFFSET32(tx_stat_etherstatspkts512octetsto1023octets_hi), - 8, STATS_FLAGS_PORT, "tx_512_to_1023_byte_packets" }, + 8, true, "tx_512_to_1023_byte_packets" }, { STATS_OFFSET32(etherstatspkts1024octetsto1522octets_hi), - 8, STATS_FLAGS_PORT, "tx_1024_to_1522_byte_packets" }, + 8, true, "tx_1024_to_1522_byte_packets" }, { STATS_OFFSET32(etherstatspktsover1522octets_hi), - 8, STATS_FLAGS_PORT, "tx_1523_to_9022_byte_packets" }, + 8, true, "tx_1523_to_9022_byte_packets" }, { STATS_OFFSET32(pause_frames_sent_hi), - 8, STATS_FLAGS_PORT, "tx_pause_frames" }, + 8, true, "tx_pause_frames" }, { STATS_OFFSET32(total_tpa_aggregations_hi), - 8, STATS_FLAGS_FUNC, "tpa_aggregations" }, + 8, false, "tpa_aggregations" }, { STATS_OFFSET32(total_tpa_aggregated_frames_hi), - 8, STATS_FLAGS_FUNC, "tpa_aggregated_frames"}, + 8, false, "tpa_aggregated_frames"}, { STATS_OFFSET32(total_tpa_bytes_hi), - 8, STATS_FLAGS_FUNC, "tpa_bytes"}, + 8, false, "tpa_bytes"}, { STATS_OFFSET32(recoverable_error), - 4, STATS_FLAGS_FUNC, "recoverable_errors" }, + 4, false, "recoverable_errors" }, { STATS_OFFSET32(unrecoverable_error), - 4, STATS_FLAGS_FUNC, "unrecoverable_errors" }, + 4, false, "unrecoverable_errors" }, { STATS_OFFSET32(driver_filtered_tx_pkt), - 4, STATS_FLAGS_FUNC, "driver_filtered_tx_pkt" }, + 4, false, "driver_filtered_tx_pkt" }, { STATS_OFFSET32(eee_tx_lpi), - 4, STATS_FLAGS_PORT, "Tx LPI entry count"} + 4, true, "Tx LPI entry count"} }; #define BNX2X_NUM_STATS ARRAY_SIZE(bnx2x_stats_arr) @@ -3066,9 +3063,7 @@ static void bnx2x_self_test(struct net_device *dev, } } -#define IS_PORT_STAT(i) \ - ((bnx2x_stats_arr[i].flags & STATS_FLAGS_BOTH) == STATS_FLAGS_PORT) -#define IS_FUNC_STAT(i) (bnx2x_stats_arr[i].flags & STATS_FLAGS_FUNC) +#define IS_PORT_STAT(i) (bnx2x_stats_arr[i].is_port_stat) #define HIDE_PORT_STAT(bp) IS_VF(bp) /* ethtool statistics are displayed for all regular ethernet queues and the @@ -3093,7 +3088,7 @@ static int bnx2x_get_sset_count(struct net_device *dev, int stringset) num_strings = 0; if (HIDE_PORT_STAT(bp)) { for (i = 0; i < BNX2X_NUM_STATS; i++) - if (IS_FUNC_STAT(i)) + if (!IS_PORT_STAT(i)) num_strings++; } else num_strings += BNX2X_NUM_STATS; -- cgit v1.2.3-59-g8ed1b From d96606389720fff450955e2b5c81a3d5974c465c Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 5 Dec 2015 00:58:07 +0300 Subject: ravb: read MAC address registers only once The code reading the MAHR/MALR registers in ravb_read_mac_address() is terribly ineffective -- it reads MAHR 4 times and MALR 2 times, while it's enough to read each register only once. Use the local variables to achieve that, somewhat beautifying the code while at it... Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/ravb_main.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index be019e769c83..1cf12264861c 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -115,12 +115,15 @@ static void ravb_read_mac_address(struct net_device *ndev, const u8 *mac) if (mac) { ether_addr_copy(ndev->dev_addr, mac); } else { - ndev->dev_addr[0] = (ravb_read(ndev, MAHR) >> 24); - ndev->dev_addr[1] = (ravb_read(ndev, MAHR) >> 16) & 0xFF; - ndev->dev_addr[2] = (ravb_read(ndev, MAHR) >> 8) & 0xFF; - ndev->dev_addr[3] = (ravb_read(ndev, MAHR) >> 0) & 0xFF; - ndev->dev_addr[4] = (ravb_read(ndev, MALR) >> 8) & 0xFF; - ndev->dev_addr[5] = (ravb_read(ndev, MALR) >> 0) & 0xFF; + u32 mahr = ravb_read(ndev, MAHR); + u32 malr = ravb_read(ndev, MALR); + + ndev->dev_addr[0] = (mahr >> 24) & 0xFF; + ndev->dev_addr[1] = (mahr >> 16) & 0xFF; + ndev->dev_addr[2] = (mahr >> 8) & 0xFF; + ndev->dev_addr[3] = (mahr >> 0) & 0xFF; + ndev->dev_addr[4] = (malr >> 8) & 0xFF; + ndev->dev_addr[5] = (malr >> 0) & 0xFF; } } -- cgit v1.2.3-59-g8ed1b From 37742f028b301205c2b658c16bafbee7552035d0 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 5 Dec 2015 00:58:57 +0300 Subject: sh_eth: read MAC address registers only once The code reading the MAHR/MALR registers in read_mac_address() is terribly ineffective -- it reads MAHR 4 times and MALR 2 times, while it's enough to read each register only once. Use the local variables to achieve that, somewhat beautifying the code while at it... Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 7f3c6109d45e..2406ad4f880e 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -989,12 +989,15 @@ static void read_mac_address(struct net_device *ndev, unsigned char *mac) if (mac[0] || mac[1] || mac[2] || mac[3] || mac[4] || mac[5]) { memcpy(ndev->dev_addr, mac, ETH_ALEN); } else { - ndev->dev_addr[0] = (sh_eth_read(ndev, MAHR) >> 24); - ndev->dev_addr[1] = (sh_eth_read(ndev, MAHR) >> 16) & 0xFF; - ndev->dev_addr[2] = (sh_eth_read(ndev, MAHR) >> 8) & 0xFF; - ndev->dev_addr[3] = (sh_eth_read(ndev, MAHR) & 0xFF); - ndev->dev_addr[4] = (sh_eth_read(ndev, MALR) >> 8) & 0xFF; - ndev->dev_addr[5] = (sh_eth_read(ndev, MALR) & 0xFF); + u32 mahr = sh_eth_read(ndev, MAHR); + u32 malr = sh_eth_read(ndev, MALR); + + ndev->dev_addr[0] = (mahr >> 24) & 0xFF; + ndev->dev_addr[1] = (mahr >> 16) & 0xFF; + ndev->dev_addr[2] = (mahr >> 8) & 0xFF; + ndev->dev_addr[3] = (mahr >> 0) & 0xFF; + ndev->dev_addr[4] = (malr >> 8) & 0xFF; + ndev->dev_addr[5] = (malr >> 0) & 0xFF; } } -- cgit v1.2.3-59-g8ed1b From e0244903d4a6a27f9f1e8b46b89afa4130aa9164 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:56:56 -0700 Subject: fm10k: set netdev features in one location Don't change netdev hw_features later in fm10k_probe, instead set all values inside fm10k_alloc_netdev. To do so, we need to know the MAC type (whether it is PF or VF) in order to determine what to do. This helps ensure that all logic regarding features is co-located. Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 2 +- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 31 +++++++++++++++---------- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 9 +------ 3 files changed, 21 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index 48809e5d3f79..b178905fa43e 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -484,7 +484,7 @@ void fm10k_netpoll(struct net_device *netdev); #endif /* Netdev */ -struct net_device *fm10k_alloc_netdev(void); +struct net_device *fm10k_alloc_netdev(const struct fm10k_info *info); int fm10k_setup_rx_resources(struct fm10k_ring *); int fm10k_setup_tx_resources(struct fm10k_ring *); void fm10k_free_rx_resources(struct fm10k_ring *); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 7781e80896a6..79f6b7dd2362 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1388,8 +1388,9 @@ static const struct net_device_ops fm10k_netdev_ops = { #define DEFAULT_DEBUG_LEVEL_SHIFT 3 -struct net_device *fm10k_alloc_netdev(void) +struct net_device *fm10k_alloc_netdev(const struct fm10k_info *info) { + netdev_features_t hw_features; struct fm10k_intfc *interface; struct net_device *dev; @@ -1412,27 +1413,31 @@ struct net_device *fm10k_alloc_netdev(void) NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN | - NETIF_F_GSO_UDP_TUNNEL | NETIF_F_RXHASH | NETIF_F_RXCSUM; + /* Only the PF can support VXLAN and NVGRE tunnel offloads */ + if (info->mac == fm10k_mac_pf) { + dev->hw_enc_features = NETIF_F_IP_CSUM | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_TSO_ECN | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_IPV6_CSUM | + NETIF_F_SG; + + dev->features |= NETIF_F_GSO_UDP_TUNNEL; + } + /* all features defined to this point should be changeable */ - dev->hw_features |= dev->features; + hw_features = dev->features; /* allow user to enable L2 forwarding acceleration */ - dev->hw_features |= NETIF_F_HW_L2FW_DOFFLOAD; + hw_features |= NETIF_F_HW_L2FW_DOFFLOAD; /* configure VLAN features */ dev->vlan_features |= dev->features; - /* configure tunnel offloads */ - dev->hw_enc_features |= NETIF_F_IP_CSUM | - NETIF_F_TSO | - NETIF_F_TSO6 | - NETIF_F_TSO_ECN | - NETIF_F_GSO_UDP_TUNNEL | - NETIF_F_IPV6_CSUM; - /* we want to leave these both on as we cannot disable VLAN tag * insertion or stripping on the hardware since it is contained * in the FTAG and not in the frame itself. @@ -1443,5 +1448,7 @@ struct net_device *fm10k_alloc_netdev(void) dev->priv_flags |= IFF_UNICAST_FLT; + dev->hw_features |= hw_features; + return dev; } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 5fbffbaefe32..1af4b222d003 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -1722,13 +1722,6 @@ static int fm10k_sw_init(struct fm10k_intfc *interface, pci_resource_len(pdev, 4)); hw->sw_addr = interface->sw_addr; - /* Only the PF can support VXLAN and NVGRE offloads */ - if (hw->mac.type != fm10k_mac_pf) { - netdev->hw_enc_features = 0; - netdev->features &= ~NETIF_F_GSO_UDP_TUNNEL; - netdev->hw_features &= ~NETIF_F_GSO_UDP_TUNNEL; - } - /* initialize DCBNL interface */ fm10k_dcbnl_set_ops(netdev); @@ -1894,7 +1887,7 @@ static int fm10k_probe(struct pci_dev *pdev, pci_set_master(pdev); pci_save_state(pdev); - netdev = fm10k_alloc_netdev(); + netdev = fm10k_alloc_netdev(fm10k_info_tbl[ent->driver_data]); if (!netdev) { err = -ENOMEM; goto err_alloc_netdev; -- cgit v1.2.3-59-g8ed1b From 0e8d5b5975401c83641efd5d4595e6cdbe9e9e2f Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:56:57 -0700 Subject: fm10k: reset max_queues on init_hw_vf failure VF drivers must detect how many queues are available. Previously, the driver assumed that each VF has at minimum 1 queue. This assumption is incorrect, since it is possible that the PF has not yet assigned the queues to the VF by the time the VF checks. To resolve this, we added a check first to ensure that the first queue is infact owned by the VF at init_hw_vf time. However, the code flow did not reset hw->mac.max_queues to 0. In some cases, such as during reinit flows, we call init_hw_vf without clearing the previous value of hw->mac.max_queues. Due to this, when init_hw_vf errors out, if its error code is not properly handled the VF driver may still believe it has queues which no longer belong to it. Fix this by clearing the hw->mac.max_queues on exit due to errors. Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_vf.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c index 3a18ef1cc017..d512575c33f3 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c @@ -105,8 +105,10 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw) /* verify we have at least 1 queue */ if (!~fm10k_read_reg(hw, FM10K_TXQCTL(0)) || - !~fm10k_read_reg(hw, FM10K_RXQCTL(0))) - return FM10K_ERR_NO_RESOURCES; + !~fm10k_read_reg(hw, FM10K_RXQCTL(0))) { + err = FM10K_ERR_NO_RESOURCES; + goto reset_max_queues; + } /* determine how many queues we have */ for (i = 1; tqdloc0 && (i < FM10K_MAX_QUEUES_POOL); i++) { @@ -124,7 +126,7 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw) /* shut down queues we own and reset DMA configuration */ err = fm10k_disable_queues_generic(hw, i); if (err) - return err; + goto reset_max_queues; /* record maximum queue count */ hw->mac.max_queues = i; @@ -134,6 +136,11 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw) FM10K_TXQCTL_VID_MASK) >> FM10K_TXQCTL_VID_SHIFT; return 0; + +reset_max_queues: + hw->mac.max_queues = 0; + + return err; } /* This structure defines the attibutes to be parsed below */ -- cgit v1.2.3-59-g8ed1b From 1343c65f70ee1b1f968a08b30e1836a4e37116cd Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:56:58 -0700 Subject: fm10k: always check init_hw for errors A recent change modified init_hw in some flows the function may fail on VF devices. For example, if a VF doesn't yet own its own queues. However, many callers of init_hw didn't bother to check the error code. Other callers checked but only displayed diagnostic messages without actually handling the consequences. Fix this by (a) always returning and preventing the netdevice from going up, and (b) printing the diagnostic in every flow for consistency. This should resolve an issue where VF drivers would attempt to come up before the PF has finished assigning queues. In addition, change the dmesg output to explicitly show the actual function that failed, instead of combining reset_hw and init_hw into a single check, to help for future debugging. Fixes: 1d568b0f6424 ("fm10k: do not assume VF always has 1 queue") Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 34 ++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 1af4b222d003..9c21d1e45543 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -163,9 +163,17 @@ static void fm10k_reinit(struct fm10k_intfc *interface) interface->last_reset = jiffies + (10 * HZ); /* reset and initialize the hardware so it is in a known state */ - err = hw->mac.ops.reset_hw(hw) ? : hw->mac.ops.init_hw(hw); - if (err) + err = hw->mac.ops.reset_hw(hw); + if (err) { + dev_err(&interface->pdev->dev, "reset_hw failed: %d\n", err); + goto reinit_err; + } + + err = hw->mac.ops.init_hw(hw); + if (err) { dev_err(&interface->pdev->dev, "init_hw failed: %d\n", err); + goto reinit_err; + } /* reassociate interrupts */ fm10k_mbx_request_irq(interface); @@ -193,6 +201,10 @@ static void fm10k_reinit(struct fm10k_intfc *interface) fm10k_iov_resume(interface->pdev); +reinit_err: + if (err) + netif_device_detach(netdev); + rtnl_unlock(); clear_bit(__FM10K_RESETTING, &interface->state); @@ -1684,7 +1696,13 @@ static int fm10k_sw_init(struct fm10k_intfc *interface, interface->last_reset = jiffies + (10 * HZ); /* reset and initialize the hardware so it is in a known state */ - err = hw->mac.ops.reset_hw(hw) ? : hw->mac.ops.init_hw(hw); + err = hw->mac.ops.reset_hw(hw); + if (err) { + dev_err(&pdev->dev, "reset_hw failed: %d\n", err); + return err; + } + + err = hw->mac.ops.init_hw(hw); if (err) { dev_err(&pdev->dev, "init_hw failed: %d\n", err); return err; @@ -2064,8 +2082,10 @@ static int fm10k_resume(struct pci_dev *pdev) /* reset hardware to known state */ err = hw->mac.ops.init_hw(&interface->hw); - if (err) + if (err) { + dev_err(&pdev->dev, "init_hw failed: %d\n", err); return err; + } /* reset statistics starting values */ hw->mac.ops.rebind_hw_stats(hw, &interface->stats); @@ -2241,7 +2261,11 @@ static void fm10k_io_resume(struct pci_dev *pdev) int err = 0; /* reset hardware to known state */ - hw->mac.ops.init_hw(&interface->hw); + err = hw->mac.ops.init_hw(&interface->hw); + if (err) { + dev_err(&pdev->dev, "init_hw failed: %d\n", err); + return; + } /* reset statistics starting values */ hw->mac.ops.rebind_hw_stats(hw, &interface->stats); -- cgit v1.2.3-59-g8ed1b From 875328e4bce696e85edcda3c4b0ec80fd525e3a3 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:56:59 -0700 Subject: fm10k: reinitialize queuing scheme after calling init_hw The init_hw function may fail, and in the case of VFs, it might change the number of maximum queues available. Thus, for every flow which checks init_hw, we need to ensure that we clear the queue scheme before, and initialize it after. The fm10k_io_slot_reset path will end up triggering a reset so fm10k_reinit needs this change. The fm10k_io_error_detected and fm10k_io_resume also need to properly clear and reinitialize the queue scheme. Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 9c21d1e45543..faf81492d009 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -159,6 +159,9 @@ static void fm10k_reinit(struct fm10k_intfc *interface) fm10k_mbx_free_irq(interface); + /* free interrupts */ + fm10k_clear_queueing_scheme(interface); + /* delay any future reset requests */ interface->last_reset = jiffies + (10 * HZ); @@ -175,6 +178,12 @@ static void fm10k_reinit(struct fm10k_intfc *interface) goto reinit_err; } + err = fm10k_init_queueing_scheme(interface); + if (err) { + dev_err(&interface->pdev->dev, "init_queueing_scheme failed: %d\n", err); + goto reinit_err; + } + /* reassociate interrupts */ fm10k_mbx_request_irq(interface); @@ -2198,6 +2207,9 @@ static pci_ers_result_t fm10k_io_error_detected(struct pci_dev *pdev, if (netif_running(netdev)) fm10k_close(netdev); + /* free interrupts */ + fm10k_clear_queueing_scheme(interface); + fm10k_mbx_free_irq(interface); pci_disable_device(pdev); @@ -2270,6 +2282,12 @@ static void fm10k_io_resume(struct pci_dev *pdev) /* reset statistics starting values */ hw->mac.ops.rebind_hw_stats(hw, &interface->stats); + err = fm10k_init_queueing_scheme(interface); + if (err) { + dev_err(&interface->pdev->dev, "init_queueing_scheme failed: %d\n", err); + return; + } + /* reassociate interrupts */ fm10k_mbx_request_irq(interface); -- cgit v1.2.3-59-g8ed1b From 9d4955b45888b1b7c9f2a954cf6aa1269904bb98 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:57:00 -0700 Subject: fm10k: Correct typecast in fm10k_update_xc_addr_pf Since the resultant data type of the mac_update.mac_upper field is u16, it does not make sense to typecast u8 variables to u32 first. Since we're modifying fm10k_pf.c, also update the copyright year. Reported-by: Matthew Vick Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 8c0bdc4e4edd..b1e09756a6ef 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -1,5 +1,5 @@ /* Intel Ethernet Switch Host Interface Driver - * Copyright(c) 2013 - 2014 Intel Corporation. + * Copyright(c) 2013 - 2015 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -334,8 +334,8 @@ static s32 fm10k_update_xc_addr_pf(struct fm10k_hw *hw, u16 glort, ((u32)mac[3] << 16) | ((u32)mac[4] << 8) | ((u32)mac[5])); - mac_update.mac_upper = cpu_to_le16(((u32)mac[0] << 8) | - ((u32)mac[1])); + mac_update.mac_upper = cpu_to_le16(((u16)mac[0] << 8) | + ((u16)mac[1])); mac_update.vlan = cpu_to_le16(vid); mac_update.glort = cpu_to_le16(glort); mac_update.action = add ? 0 : 1; -- cgit v1.2.3-59-g8ed1b From cdf32c94bd3569d4e46b8f993e0fd8e45d438d18 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:57:01 -0700 Subject: fm10k: explicitly typecast vlan values to u16 Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index b1e09756a6ef..00f7a29e734f 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -1212,7 +1212,7 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results, set = !(vid & FM10K_VLAN_CLEAR); vid &= ~FM10K_VLAN_CLEAR; - err = fm10k_iov_select_vid(vf_info, vid); + err = fm10k_iov_select_vid(vf_info, (u16)vid); if (err < 0) return err; else @@ -1242,7 +1242,7 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results, if (err < 0) return err; else - vlan = err; + vlan = (u16)err; /* notify switch of request for new unicast address */ err = hw->mac.ops.update_uc_addr(hw, vf_info->glort, @@ -1268,7 +1268,7 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results, if (err < 0) return err; else - vlan = err; + vlan = (u16)err; /* notify switch of request for new multicast address */ err = hw->mac.ops.update_mc_addr(hw, vf_info->glort, -- cgit v1.2.3-59-g8ed1b From 17d39fac0888bfd624f61f758c8cce60632a3394 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:57:02 -0700 Subject: fm10k: add statistics for actual DWORD count of mbmem mailbox A previous bug was uncovered by addition of a debug stat to indicate the actual number of DWORDS we pulled from the mbmem. It turned out this was not the same as the tx_dwords counter. While the previous bug fix should have corrected this in all cases, add some debug stats that count the number of DWORDs pushed or pulled from the mbmem. A future debugger may take advantage of this statistic for debugging purposes. Since we're modifying fm10k_mbx.h, update the copyright year as well. Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 2 ++ drivers/net/ethernet/intel/fm10k/fm10k_mbx.c | 4 ++++ drivers/net/ethernet/intel/fm10k/fm10k_mbx.h | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index 2ce0eba5e040..7c0156112b5a 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -114,9 +114,11 @@ static const struct fm10k_stats fm10k_gstrings_mbx_stats[] = { FM10K_MBX_STAT("mbx_tx_oversized", tx_dropped), FM10K_MBX_STAT("mbx_tx_messages", tx_messages), FM10K_MBX_STAT("mbx_tx_dwords", tx_dwords), + FM10K_MBX_STAT("mbx_tx_mbmem_pulled", tx_mbmem_pulled), FM10K_MBX_STAT("mbx_rx_messages", rx_messages), FM10K_MBX_STAT("mbx_rx_dwords", rx_dwords), FM10K_MBX_STAT("mbx_rx_parse_err", rx_parse_err), + FM10K_MBX_STAT("mbx_rx_mbmem_pushed", rx_mbmem_pushed), }; #define FM10K_GLOBAL_STATS_LEN ARRAY_SIZE(fm10k_gstrings_global_stats) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c index af09a1b272e6..2bce47490723 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c @@ -375,6 +375,8 @@ static void fm10k_mbx_write_copy(struct fm10k_hw *hw, if (!tail) tail++; + mbx->tx_mbmem_pulled++; + /* write message to hardware FIFO */ fm10k_write_reg(hw, mbmem + tail++, *(head++)); } while (--len && --end); @@ -459,6 +461,8 @@ static void fm10k_mbx_read_copy(struct fm10k_hw *hw, if (!head) head++; + mbx->rx_mbmem_pushed++; + /* read message from hardware FIFO */ *(tail++) = fm10k_read_reg(hw, mbmem + head++); } while (--len && --end); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h index 0419a7f0035e..c4f18a8f176c 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h @@ -1,5 +1,5 @@ /* Intel Ethernet Switch Host Interface Driver - * Copyright(c) 2013 - 2014 Intel Corporation. + * Copyright(c) 2013 - 2015 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -291,8 +291,10 @@ struct fm10k_mbx_info { u64 tx_dropped; u64 tx_messages; u64 tx_dwords; + u64 tx_mbmem_pulled; u64 rx_messages; u64 rx_dwords; + u64 rx_mbmem_pushed; u64 rx_parse_err; /* Buffer to store messages */ -- cgit v1.2.3-59-g8ed1b From 5680ea692407ce5113f3391c2e6b5dbe6eec84d8 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:57:03 -0700 Subject: fm10k: rename mbx_tx_oversized statistic to mbx_tx_dropped Originally this statistic was renamed because the method of dropping was called "drop_oversized_messages", but this logic has changed much, and this counter does actually represent messages which we failed to transmit for a number of reasons. Rename the counter back to tx_dropped since this is when it will increment, and it is less confusing. Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index 7c0156112b5a..fd29145a52e4 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -111,7 +111,7 @@ static const struct fm10k_stats fm10k_gstrings_pf_stats[] = { static const struct fm10k_stats fm10k_gstrings_mbx_stats[] = { FM10K_MBX_STAT("mbx_tx_busy", tx_busy), - FM10K_MBX_STAT("mbx_tx_oversized", tx_dropped), + FM10K_MBX_STAT("mbx_tx_dropped", tx_dropped), FM10K_MBX_STAT("mbx_tx_messages", tx_messages), FM10K_MBX_STAT("mbx_tx_dwords", tx_dwords), FM10K_MBX_STAT("mbx_tx_mbmem_pulled", tx_mbmem_pulled), -- cgit v1.2.3-59-g8ed1b From 20076fa18571e2e274f51a2012ff5ef5c82e93a5 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:57:05 -0700 Subject: fm10k: Add support for ITR scaling based on PCIe link speed The Intel Ethernet Switch FM10000 Host Interface interrupt throttle timers are based on the PCIe link speed. Because of this, the value being programmed into the ITR registers must be scaled accordingly. For the PF, this is as simple as reading the PCIe link speed and storing the result. However, in the case of SR-IOV, the VF's interrupt throttle timers are based on the link speed of the PF. However, the VF is unable to get the link speed information from its configuration space, so the PF must inform it of what scale to use. Rather than pass this scale via mailbox message, take advantage of unused bits in the TDLEN register to pass the scale. It is the responsibility of the PF to program this for the VF while setting up the VF queues and the responsibility of the VF to get the information accordingly. This is preferable because it allows the VF to set up the interrupts properly during initialization and matches how the MAC address is passed in the TDBAL/TDBAH registers. Since we're modifying fm10k_type.h, we may as well also update the copyright year. Reported-by: Matthew Vick Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 22 +++++++++++++++++++++- drivers/net/ethernet/intel/fm10k/fm10k_type.h | 17 ++++++++++++++++- drivers/net/ethernet/intel/fm10k/fm10k_vf.c | 20 ++++++++++++++++++-- 3 files changed, 55 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 00f7a29e734f..8b9b6ba5b92b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -150,19 +150,26 @@ static s32 fm10k_init_hw_pf(struct fm10k_hw *hw) FM10K_TPH_RXCTRL_HDR_WROEN); } - /* set max hold interval to align with 1.024 usec in all modes */ + /* set max hold interval to align with 1.024 usec in all modes and + * store ITR scale + */ switch (hw->bus.speed) { case fm10k_bus_speed_2500: dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN1; + hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN1; break; case fm10k_bus_speed_5000: dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN2; + hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN2; break; case fm10k_bus_speed_8000: dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN3; + hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN3; break; default: dma_ctrl = 0; + /* just in case, assume Gen3 ITR scale */ + hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN3; break; } @@ -903,6 +910,13 @@ static s32 fm10k_iov_assign_default_mac_vlan_pf(struct fm10k_hw *hw, fm10k_write_reg(hw, FM10K_TDBAL(vf_q_idx), tdbal); fm10k_write_reg(hw, FM10K_TDBAH(vf_q_idx), tdbah); + /* Provide the VF the ITR scale, using software-defined fields in TDLEN + * to pass the information during VF initialization. See definition of + * FM10K_TDLEN_ITR_SCALE_SHIFT for more details. + */ + fm10k_write_reg(hw, FM10K_TDLEN(vf_q_idx), hw->mac.itr_scale << + FM10K_TDLEN_ITR_SCALE_SHIFT); + err_out: /* configure Queue control register */ txqctl = ((u32)vf_vid << FM10K_TXQCTL_VID_SHIFT) & @@ -1035,6 +1049,12 @@ static s32 fm10k_iov_reset_resources_pf(struct fm10k_hw *hw, for (i = queues_per_pool; i--;) { fm10k_write_reg(hw, FM10K_TDBAL(vf_q_idx + i), tdbal); fm10k_write_reg(hw, FM10K_TDBAH(vf_q_idx + i), tdbah); + /* See definition of FM10K_TDLEN_ITR_SCALE_SHIFT for an + * explanation of how TDLEN is used. + */ + fm10k_write_reg(hw, FM10K_TDLEN(vf_q_idx + i), + hw->mac.itr_scale << + FM10K_TDLEN_ITR_SCALE_SHIFT); fm10k_write_reg(hw, FM10K_TQMAP(qmap_idx + i), vf_q_idx + i); fm10k_write_reg(hw, FM10K_RQMAP(qmap_idx + i), vf_q_idx + i); } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h index 35afd711d144..02727250ce1f 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h @@ -1,5 +1,5 @@ /* Intel Ethernet Switch Host Interface Driver - * Copyright(c) 2013 - 2014 Intel Corporation. + * Copyright(c) 2013 - 2015 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -272,6 +272,20 @@ struct fm10k_hw; #define FM10K_TDBAL(_n) ((0x40 * (_n)) + 0x8000) #define FM10K_TDBAH(_n) ((0x40 * (_n)) + 0x8001) #define FM10K_TDLEN(_n) ((0x40 * (_n)) + 0x8002) +/* When fist initialized, VFs need to know the Interrupt Throttle Rate (ITR) + * scale which is based on the PCIe speed but the speed information in the PCI + * configuration space may not be accurate. The PF already knows the ITR scale + * but there is no defined method to pass that information from the PF to the + * VF. This is accomplished during VF initialization by temporarily co-opting + * the yet-to-be-used TDLEN register to have the PF store the ITR shift for + * the VF to retrieve before the VF needs to use the TDLEN register for its + * intended purpose, i.e. before the Tx resources are allocated. + */ +#define FM10K_TDLEN_ITR_SCALE_SHIFT 9 +#define FM10K_TDLEN_ITR_SCALE_MASK 0x00000E00 +#define FM10K_TDLEN_ITR_SCALE_GEN1 2 +#define FM10K_TDLEN_ITR_SCALE_GEN2 1 +#define FM10K_TDLEN_ITR_SCALE_GEN3 0 #define FM10K_TPH_TXCTRL(_n) ((0x40 * (_n)) + 0x8003) #define FM10K_TPH_TXCTRL_DESC_TPHEN 0x00000020 #define FM10K_TPH_TXCTRL_DESC_RROEN 0x00000200 @@ -560,6 +574,7 @@ struct fm10k_mac_info { bool get_host_state; bool tx_ready; u32 dglort_map; + u8 itr_scale; }; struct fm10k_swapi_table_info { diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c index d512575c33f3..2af697df5abc 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c @@ -28,7 +28,7 @@ static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw) { u8 *perm_addr = hw->mac.perm_addr; - u32 bal = 0, bah = 0; + u32 bal = 0, bah = 0, tdlen; s32 err; u16 i; @@ -48,6 +48,9 @@ static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw) ((u32)perm_addr[2]); } + /* restore default itr_scale for next VF initialization */ + tdlen = hw->mac.itr_scale << FM10K_TDLEN_ITR_SCALE_SHIFT; + /* The queues have already been disabled so we just need to * update their base address registers */ @@ -56,6 +59,12 @@ static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw) fm10k_write_reg(hw, FM10K_TDBAH(i), bah); fm10k_write_reg(hw, FM10K_RDBAL(i), bal); fm10k_write_reg(hw, FM10K_RDBAH(i), bah); + /* Restore ITR scale in software-defined mechanism in TDLEN + * for next VF initialization. See definition of + * FM10K_TDLEN_ITR_SCALE_SHIFT for more details on the use of + * TDLEN here. + */ + fm10k_write_reg(hw, FM10K_TDLEN(i), tdlen); } return 0; @@ -131,9 +140,16 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw) /* record maximum queue count */ hw->mac.max_queues = i; - /* fetch default VLAN */ + /* fetch default VLAN and ITR scale */ hw->mac.default_vid = (fm10k_read_reg(hw, FM10K_TXQCTL(0)) & FM10K_TXQCTL_VID_MASK) >> FM10K_TXQCTL_VID_SHIFT; + /* Read the ITR scale from TDLEN. See the definition of + * FM10K_TDLEN_ITR_SCALE_SHIFT for more information about how TDLEN is + * used here. + */ + hw->mac.itr_scale = (fm10k_read_reg(hw, FM10K_TDLEN(0)) & + FM10K_TDLEN_ITR_SCALE_MASK) >> + FM10K_TDLEN_ITR_SCALE_SHIFT; return 0; -- cgit v1.2.3-59-g8ed1b From 584373f5b98aed81ff5a432d91b6e16d7554a5c9 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:57:06 -0700 Subject: fm10k: introduce ITR_IS_ADAPTIVE macro Define a macro for identifying when the itr value is dynamic or adaptive. The concept was taken from i40e. This helps make clear what the check is, and reduces the line length to something more reasonable in a few places. Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 2 ++ drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 6 ++---- drivers/net/ethernet/intel/fm10k/fm10k_main.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index b178905fa43e..cea0d94d8f5f 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -172,6 +172,8 @@ struct fm10k_ring_container { #define FM10K_ITR_20K 50 /* 50us */ #define FM10K_ITR_ADAPTIVE 0x8000 /* adaptive interrupt moderation flag */ +#define ITR_IS_ADAPTIVE(itr) (!!(itr & FM10K_ITR_ADAPTIVE)) + #define FM10K_ITR_ENABLE (FM10K_ITR_AUTOMASK | FM10K_ITR_MASK_CLEAR) static inline struct netdev_queue *txring_txq(const struct fm10k_ring *ring) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index fd29145a52e4..a505a502bb8e 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -701,12 +701,10 @@ static int fm10k_get_coalesce(struct net_device *dev, { struct fm10k_intfc *interface = netdev_priv(dev); - ec->use_adaptive_tx_coalesce = - !!(interface->tx_itr & FM10K_ITR_ADAPTIVE); + ec->use_adaptive_tx_coalesce = ITR_IS_ADAPTIVE(interface->tx_itr); ec->tx_coalesce_usecs = interface->tx_itr & ~FM10K_ITR_ADAPTIVE; - ec->use_adaptive_rx_coalesce = - !!(interface->rx_itr & FM10K_ITR_ADAPTIVE); + ec->use_adaptive_rx_coalesce = ITR_IS_ADAPTIVE(interface->rx_itr); ec->rx_coalesce_usecs = interface->rx_itr & ~FM10K_ITR_ADAPTIVE; return 0; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index 746a1986690b..21d49550935e 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -1366,7 +1366,7 @@ static void fm10k_update_itr(struct fm10k_ring_container *ring_container) unsigned int avg_wire_size, packets; /* Only update ITR if we are using adaptive setting */ - if (!(ring_container->itr & FM10K_ITR_ADAPTIVE)) + if (!ITR_IS_ADAPTIVE(ring_container->itr)) goto clear_counts; packets = ring_container->total_packets; -- cgit v1.2.3-59-g8ed1b From 242722dd3d0af32703d4ebb4af63c92a2c85b835 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:57:07 -0700 Subject: fm10k: Update adaptive ITR algorithm The existing adaptive ITR algorithm is overly restrictive. It throttles incorrectly for various traffic rates, and does not produce good performance. The algorithm now allows for more interrupts per second, and does some calculation to help improve for smaller packet loads. In addition, take into account the new itr_scale from the hardware which indicates how much to scale due to PCIe link speed. Reported-by: Matthew Vick Reported-by: Alex Duyck Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 1 + drivers/net/ethernet/intel/fm10k/fm10k_main.c | 52 ++++++++++++++++++++------- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 6 ++-- 3 files changed, 45 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index cea0d94d8f5f..bb799b368996 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -164,6 +164,7 @@ struct fm10k_ring_container { unsigned int total_packets; /* total packets processed this int */ u16 work_limit; /* total work allowed per interrupt */ u16 itr; /* interrupt throttle rate value */ + u8 itr_scale; /* ITR adjustment scaler based on PCI speed */ u8 count; /* total number of rings in vector */ }; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index 21d49550935e..d422cd187101 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -1363,7 +1363,7 @@ static bool fm10k_clean_tx_irq(struct fm10k_q_vector *q_vector, **/ static void fm10k_update_itr(struct fm10k_ring_container *ring_container) { - unsigned int avg_wire_size, packets; + unsigned int avg_wire_size, packets, itr_round; /* Only update ITR if we are using adaptive setting */ if (!ITR_IS_ADAPTIVE(ring_container->itr)) @@ -1375,18 +1375,44 @@ static void fm10k_update_itr(struct fm10k_ring_container *ring_container) avg_wire_size = ring_container->total_bytes / packets; - /* Add 24 bytes to size to account for CRC, preamble, and gap */ - avg_wire_size += 24; - - /* Don't starve jumbo frames */ - if (avg_wire_size > 3000) - avg_wire_size = 3000; + /* The following is a crude approximation of: + * wmem_default / (size + overhead) = desired_pkts_per_int + * rate / bits_per_byte / (size + ethernet overhead) = pkt_rate + * (desired_pkt_rate / pkt_rate) * usecs_per_sec = ITR value + * + * Assuming wmem_default is 212992 and overhead is 640 bytes per + * packet, (256 skb, 64 headroom, 320 shared info), we can reduce the + * formula down to + * + * (34 * (size + 24)) / (size + 640) = ITR + * + * We first do some math on the packet size and then finally bitshift + * by 8 after rounding up. We also have to account for PCIe link speed + * difference as ITR scales based on this. + */ + if (avg_wire_size <= 360) { + /* Start at 250K ints/sec and gradually drop to 77K ints/sec */ + avg_wire_size *= 8; + avg_wire_size += 376; + } else if (avg_wire_size <= 1152) { + /* 77K ints/sec to 45K ints/sec */ + avg_wire_size *= 3; + avg_wire_size += 2176; + } else if (avg_wire_size <= 1920) { + /* 45K ints/sec to 38K ints/sec */ + avg_wire_size += 4480; + } else { + /* plateau at a limit of 38K ints/sec */ + avg_wire_size = 6656; + } - /* Give a little boost to mid-size frames */ - if ((avg_wire_size > 300) && (avg_wire_size < 1200)) - avg_wire_size /= 3; - else - avg_wire_size /= 2; + /* Perform final bitshift for division after rounding up to ensure + * that the calculation will never get below a 1. The bit shift + * accounts for changes in the ITR due to PCIe link speed. + */ + itr_round = ACCESS_ONCE(ring_container->itr_scale) + 8; + avg_wire_size += (1 << itr_round) - 1; + avg_wire_size >>= itr_round; /* write back value and retain adaptive flag */ ring_container->itr = avg_wire_size | FM10K_ITR_ADAPTIVE; @@ -1604,6 +1630,7 @@ static int fm10k_alloc_q_vector(struct fm10k_intfc *interface, q_vector->tx.ring = ring; q_vector->tx.work_limit = FM10K_DEFAULT_TX_WORK; q_vector->tx.itr = interface->tx_itr; + q_vector->tx.itr_scale = interface->hw.mac.itr_scale; q_vector->tx.count = txr_count; while (txr_count) { @@ -1632,6 +1659,7 @@ static int fm10k_alloc_q_vector(struct fm10k_intfc *interface, /* save Rx ring container info */ q_vector->rx.ring = ring; q_vector->rx.itr = interface->rx_itr; + q_vector->rx.itr_scale = interface->hw.mac.itr_scale; q_vector->rx.count = rxr_count; while (rxr_count) { diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index faf81492d009..1f0d9bc3bf7b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -880,7 +880,8 @@ static irqreturn_t fm10k_msix_mbx_vf(int __always_unused irq, void *data) /* re-enable mailbox interrupt and indicate 20us delay */ fm10k_write_reg(hw, FM10K_VFITR(FM10K_MBX_VECTOR), - FM10K_ITR_ENABLE | FM10K_MBX_INT_DELAY); + FM10K_ITR_ENABLE | (FM10K_MBX_INT_DELAY >> + hw->mac.itr_scale)); /* service upstream mailbox */ if (fm10k_mbx_trylock(interface)) { @@ -1111,7 +1112,8 @@ static irqreturn_t fm10k_msix_mbx_pf(int __always_unused irq, void *data) /* re-enable mailbox interrupt and indicate 20us delay */ fm10k_write_reg(hw, FM10K_ITR(FM10K_MBX_VECTOR), - FM10K_ITR_ENABLE | FM10K_MBX_INT_DELAY); + FM10K_ITR_ENABLE | (FM10K_MBX_INT_DELAY >> + hw->mac.itr_scale)); return IRQ_HANDLED; } -- cgit v1.2.3-59-g8ed1b From 436ea956bffd9974bc41ae9bd99930e29d9c807d Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:57:08 -0700 Subject: fm10k: use macro for default Tx and Rx ITR values Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 2 ++ drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 4 ++-- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index bb799b368996..115033f6539e 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -175,6 +175,8 @@ struct fm10k_ring_container { #define ITR_IS_ADAPTIVE(itr) (!!(itr & FM10K_ITR_ADAPTIVE)) +#define FM10K_TX_ITR_DEFAULT FM10K_ITR_10K +#define FM10K_RX_ITR_DEFAULT FM10K_ITR_20K #define FM10K_ITR_ENABLE (FM10K_ITR_AUTOMASK | FM10K_ITR_MASK_CLEAR) static inline struct netdev_queue *txring_txq(const struct fm10k_ring *ring) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index a505a502bb8e..109e2111bdda 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -729,10 +729,10 @@ static int fm10k_set_coalesce(struct net_device *dev, /* set initial values for adaptive ITR */ if (ec->use_adaptive_tx_coalesce) - tx_itr = FM10K_ITR_ADAPTIVE | FM10K_ITR_10K; + tx_itr = FM10K_ITR_ADAPTIVE | FM10K_TX_ITR_DEFAULT; if (ec->use_adaptive_rx_coalesce) - rx_itr = FM10K_ITR_ADAPTIVE | FM10K_ITR_20K; + rx_itr = FM10K_ITR_ADAPTIVE | FM10K_RX_ITR_DEFAULT; /* update interface */ interface->tx_itr = tx_itr; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 1f0d9bc3bf7b..0cdb79e8b36e 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -1771,8 +1771,8 @@ static int fm10k_sw_init(struct fm10k_intfc *interface, interface->rx_ring_count = FM10K_DEFAULT_RXD; /* set default interrupt moderation */ - interface->tx_itr = FM10K_ITR_10K; - interface->rx_itr = FM10K_ITR_ADAPTIVE | FM10K_ITR_20K; + interface->tx_itr = FM10K_TX_ITR_DEFAULT; + interface->rx_itr = FM10K_ITR_ADAPTIVE | FM10K_RX_ITR_DEFAULT; /* initialize vxlan_port list */ INIT_LIST_HEAD(&interface->vxlan_port); -- cgit v1.2.3-59-g8ed1b From dbf4284886c59a17dc0081cee39039ac2a546078 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:57:09 -0700 Subject: fm10k: change default Tx ITR to 25usec The current default ITR for Tx is overly restrictive. Using a simple netperf TCP_STREAM test, we top out at about 10Gb/s for a single thread when running using 1500 byte frames. By reducing the ITR value to 25usec (up to 40K interrupts a second from 10K), we are able to achieve 36Gb/s for a single thread TCP stream test. Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index 115033f6539e..fa26e20445a5 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -171,11 +171,12 @@ struct fm10k_ring_container { #define FM10K_ITR_MAX 0x0FFF /* maximum value for ITR */ #define FM10K_ITR_10K 100 /* 100us */ #define FM10K_ITR_20K 50 /* 50us */ +#define FM10K_ITR_40K 25 /* 25us */ #define FM10K_ITR_ADAPTIVE 0x8000 /* adaptive interrupt moderation flag */ #define ITR_IS_ADAPTIVE(itr) (!!(itr & FM10K_ITR_ADAPTIVE)) -#define FM10K_TX_ITR_DEFAULT FM10K_ITR_10K +#define FM10K_TX_ITR_DEFAULT FM10K_ITR_40K #define FM10K_RX_ITR_DEFAULT FM10K_ITR_20K #define FM10K_ITR_ENABLE (FM10K_ITR_AUTOMASK | FM10K_ITR_MASK_CLEAR) -- cgit v1.2.3-59-g8ed1b From c7bc952349c39d8ec7b1a7f6ef403be2a08d5e86 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:57:10 -0700 Subject: fm10k: TRIVIAL fix typo of hardware Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 0cdb79e8b36e..15d8e10c2504 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -584,7 +584,7 @@ static void fm10k_configure_tx_ring(struct fm10k_intfc *interface, /* store tail pointer */ ring->tail = &interface->uc_addr[FM10K_TDT(reg_idx)]; - /* reset ntu and ntc to place SW in sync with hardwdare */ + /* reset ntu and ntc to place SW in sync with hardware */ ring->next_to_clean = 0; ring->next_to_use = 0; @@ -690,7 +690,7 @@ static void fm10k_configure_rx_ring(struct fm10k_intfc *interface, /* store tail pointer */ ring->tail = &interface->uc_addr[FM10K_RDT(reg_idx)]; - /* reset ntu and ntc to place SW in sync with hardwdare */ + /* reset ntu and ntc to place SW in sync with hardware */ ring->next_to_clean = 0; ring->next_to_use = 0; ring->next_to_alloc = 0; -- cgit v1.2.3-59-g8ed1b From 03d13a51fb4494f3bf47f65e2be00e56c36d2b63 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 16 Oct 2015 10:57:11 -0700 Subject: fm10k: TRIVIAL cleanup order at top of fm10k_xmit_frame Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index d422cd187101..1c17b6284daa 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -1094,11 +1094,11 @@ dma_error: netdev_tx_t fm10k_xmit_frame_ring(struct sk_buff *skb, struct fm10k_ring *tx_ring) { + u16 count = TXD_USE_COUNT(skb_headlen(skb)); struct fm10k_tx_buffer *first; - int tso; - u32 tx_flags = 0; unsigned short f; - u16 count = TXD_USE_COUNT(skb_headlen(skb)); + u32 tx_flags = 0; + int tso; /* need: 1 descriptor per page * PAGE_SIZE/FM10K_MAX_DATA_PER_TXD, * + 1 desc for skb_headlen/FM10K_MAX_DATA_PER_TXD, -- cgit v1.2.3-59-g8ed1b From 6633d3815c099a0aaf28e4853f7a8994331b8c05 Mon Sep 17 00:00:00 2001 From: "Singhai, Anjali" Date: Thu, 3 Dec 2015 23:49:31 -0800 Subject: Revert "i40e: remove CONFIG_I40E_VXLAN" This reverts commit 8fe269991aece394a7ed274f525d96c73f94109a. The case where VXLAN is a module and i40e driver is inbuilt will not be handled properly with this change since i40e will have an undefined symbol vxlan_get_rx_port in it. v2: Add a signed-off-by. Signed-off-by: Anjali Singhai Jain Acked-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/Kconfig | 11 +++++++++++ drivers/net/ethernet/intel/i40e/i40e.h | 4 +++- drivers/net/ethernet/intel/i40e/i40e_main.c | 12 ++++++------ 3 files changed, 20 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig index 061e4e04e561..4163b16489b3 100644 --- a/drivers/net/ethernet/intel/Kconfig +++ b/drivers/net/ethernet/intel/Kconfig @@ -269,6 +269,17 @@ config I40E To compile this driver as a module, choose M here. The module will be called i40e. +config I40E_VXLAN + bool "Virtual eXtensible Local Area Network Support" + default n + depends on I40E && VXLAN && !(I40E=y && VXLAN=m) + ---help--- + This allows one to create VXLAN virtual interfaces that provide + Layer 2 Networks over Layer 3 Networks. VXLAN is often used + to tunnel virtual network infrastructure in virtualized environments. + Say Y here if you want to use Virtual eXtensible Local Area Network + (VXLAN) in the driver. + config I40E_DCB bool "Data Center Bridging (DCB) Support" default n diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 23b4580616b7..bd6d9c002acc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -281,7 +281,7 @@ struct i40e_pf { u32 fd_atr_cnt; u32 fd_tcp_rule; -#if IS_ENABLED(CONFIG_VXLAN) +#ifdef CONFIG_I40E_VXLAN __be16 vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS]; u16 pending_vxlan_bitmap; @@ -322,7 +322,9 @@ struct i40e_pf { #define I40E_FLAG_FD_ATR_ENABLED BIT_ULL(22) #define I40E_FLAG_PTP BIT_ULL(25) #define I40E_FLAG_MFP_ENABLED BIT_ULL(26) +#ifdef CONFIG_I40E_VXLAN #define I40E_FLAG_VXLAN_FILTER_SYNC BIT_ULL(27) +#endif #define I40E_FLAG_PORT_ID_VALID BIT_ULL(28) #define I40E_FLAG_DCB_CAPABLE BIT_ULL(29) #define I40E_FLAG_RSS_AQ_CAPABLE BIT_ULL(31) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 8cd395d1cd09..2b1b655a3b52 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -27,7 +27,7 @@ /* Local includes */ #include "i40e.h" #include "i40e_diag.h" -#if IS_ENABLED(CONFIG_VXLAN) +#ifdef CONFIG_I40E_VXLAN #include #endif @@ -5294,7 +5294,7 @@ int i40e_open(struct net_device *netdev) TCP_FLAG_CWR) >> 16); wr32(&pf->hw, I40E_GLLAN_TSOMSK_L, be32_to_cpu(TCP_FLAG_CWR) >> 16); -#if IS_ENABLED(CONFIG_VXLAN) +#ifdef CONFIG_I40E_VXLAN vxlan_get_rx_port(netdev); #endif @@ -6990,7 +6990,7 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf) i40e_flush(hw); } -#if IS_ENABLED(CONFIG_VXLAN) +#ifdef CONFIG_I40E_VXLAN /** * i40e_sync_vxlan_filters_subtask - Sync the VSI filter list with HW * @pf: board private structure @@ -7057,7 +7057,7 @@ static void i40e_service_task(struct work_struct *work) i40e_watchdog_subtask(pf); i40e_fdir_reinit_subtask(pf); i40e_sync_filters_subtask(pf); -#if IS_ENABLED(CONFIG_VXLAN) +#ifdef CONFIG_I40E_VXLAN i40e_sync_vxlan_filters_subtask(pf); #endif i40e_clean_adminq_subtask(pf); @@ -8433,7 +8433,7 @@ static int i40e_set_features(struct net_device *netdev, return 0; } -#if IS_ENABLED(CONFIG_VXLAN) +#ifdef CONFIG_I40E_VXLAN /** * i40e_get_vxlan_port_idx - Lookup a possibly offloaded for Rx UDP port * @pf: board private structure @@ -8753,7 +8753,7 @@ static const struct net_device_ops i40e_netdev_ops = { .ndo_get_vf_config = i40e_ndo_get_vf_config, .ndo_set_vf_link_state = i40e_ndo_set_vf_link_state, .ndo_set_vf_spoofchk = i40e_ndo_set_vf_spoofchk, -#if IS_ENABLED(CONFIG_VXLAN) +#ifdef CONFIG_I40E_VXLAN .ndo_add_vxlan_port = i40e_add_vxlan_port, .ndo_del_vxlan_port = i40e_del_vxlan_port, #endif -- cgit v1.2.3-59-g8ed1b From 6c730080e663b1d629f8aa89348291fbcdc46cd9 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sun, 6 Dec 2015 21:25:50 +0100 Subject: net: qmi_wwan: should hold RTNL while changing netdev type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The notifier calls were thrown in as a last-minute fix for an imagined "this device could be part of a bridge" problem. That revealed a certain lack of locking. Not to mention testing... Avoid this splat: RTNL: assertion failed at net/core/dev.c (1639) CPU: 0 PID: 4293 Comm: bash Not tainted 4.4.0-rc3+ #358 Hardware name: LENOVO 2776LEG/2776LEG, BIOS 6EET55WW (3.15 ) 12/19/2011 0000000000000000 ffff8800ad253d60 ffffffff8122f7cf ffff8800ad253d98 ffff8800ad253d88 ffffffff813833ab 0000000000000002 ffff880230f48560 ffff880230a12900 ffff8800ad253da0 ffffffff813833da 0000000000000002 Call Trace: [] dump_stack+0x4b/0x63 [] call_netdevice_notifiers_info+0x3d/0x59 [] call_netdevice_notifiers+0x13/0x15 [] raw_ip_store+0x81/0x193 [qmi_wwan] [] dev_attr_store+0x20/0x22 [] sysfs_kf_write+0x49/0x50 [] kernfs_fop_write+0x10a/0x151 [] __vfs_write+0x26/0xa5 [] ? percpu_down_read+0x53/0x7f [] ? __sb_start_write+0x5f/0xb0 [] ? __sb_start_write+0x5f/0xb0 [] vfs_write+0xa3/0xe7 [] SyS_write+0x50/0x7e [] entry_SYSCALL_64_fastpath+0x12/0x6f Fixes: 32f7adf633b9 ("net: qmi_wwan: support "raw IP" mode") Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 98add3bf8821..babc84a3946c 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -92,7 +93,7 @@ static ssize_t raw_ip_store(struct device *d, struct device_attribute *attr, co struct usbnet *dev = netdev_priv(to_net_dev(d)); struct qmi_wwan_state *info = (void *)&dev->data; bool enable; - int err; + int ret; if (strtobool(buf, &enable)) return -EINVAL; @@ -101,18 +102,22 @@ static ssize_t raw_ip_store(struct device *d, struct device_attribute *attr, co if (enable == (info->flags & QMI_WWAN_FLAG_RAWIP)) return len; + if (!rtnl_trylock()) + return restart_syscall(); + /* we don't want to modify a running netdev */ if (netif_running(dev->net)) { netdev_err(dev->net, "Cannot change a running device\n"); - return -EBUSY; + ret = -EBUSY; + goto err; } /* let other drivers deny the change */ - err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev->net); - err = notifier_to_errno(err); - if (err) { + ret = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev->net); + ret = notifier_to_errno(ret); + if (ret) { netdev_err(dev->net, "Type change was refused\n"); - return err; + goto err; } if (enable) @@ -121,7 +126,10 @@ static ssize_t raw_ip_store(struct device *d, struct device_attribute *attr, co info->flags &= ~QMI_WWAN_FLAG_RAWIP; qmi_wwan_netdev_setup(dev->net); call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev->net); - return len; + ret = len; +err: + rtnl_unlock(); + return ret; } static DEVICE_ATTR_RW(raw_ip); -- cgit v1.2.3-59-g8ed1b From 0d76d6e8b2507983a2cae4c09880798079007421 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 6 Dec 2015 06:56:23 +0100 Subject: VSOCK: fix returnvar.cocci warnings Remove unneeded variable used to store return value. Generated by: scripts/coccinelle/misc/returnvar.cocci CC: Asias He Signed-off-by: Fengguang Wu Signed-off-by: Julia Lawall Reviewed-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- drivers/vhost/vsock.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c index 65b1cf8a06cb..64bcb10bb901 100644 --- a/drivers/vhost/vsock.c +++ b/drivers/vhost/vsock.c @@ -56,8 +56,7 @@ struct vhost_vsock { static u32 vhost_transport_get_local_cid(void) { - u32 cid = VHOST_VSOCK_DEFAULT_HOST_CID; - return cid; + return VHOST_VSOCK_DEFAULT_HOST_CID; } static struct vhost_vsock *vhost_vsock_get(u32 guest_cid) -- cgit v1.2.3-59-g8ed1b From e34305c85f86112838cb332696513bed8e04a211 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Sun, 6 Dec 2015 18:07:38 +0200 Subject: net/mlx4_core: Use both physical ports to set the VF link state In HA mode, the link state for VFs for which the policy is "auto" (i.e. follow the physical link state) should be ORed from both ports. Signed-off-by: Or Gerlitz Reviewed-by: Jack Morgenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/fw.c | 12 +++++++++++- drivers/net/ethernet/mellanox/mlx4/fw.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 90db94e83fde..2c2baab9d880 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -1104,6 +1104,7 @@ int mlx4_QUERY_PORT(struct mlx4_dev *dev, int port, struct mlx4_port_cap *port_c goto out; MLX4_GET(field, outbox, QUERY_PORT_SUPPORTED_TYPE_OFFSET); + port_cap->link_state = (field & 0x80) >> 7; port_cap->supported_port_types = field & 3; port_cap->suggested_type = (field >> 3) & 1; port_cap->default_sense = (field >> 4) & 1; @@ -1310,6 +1311,15 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave, port_type |= MLX4_PORT_LINK_UP_MASK; else if (IFLA_VF_LINK_STATE_DISABLE == admin_link_state) port_type &= ~MLX4_PORT_LINK_UP_MASK; + else if (IFLA_VF_LINK_STATE_AUTO == admin_link_state && mlx4_is_bonded(dev)) { + int other_port = (port == 1) ? 2 : 1; + struct mlx4_port_cap port_cap; + + err = mlx4_QUERY_PORT(dev, other_port, &port_cap); + if (err) + goto out; + port_type |= (port_cap.link_state << 7); + } MLX4_PUT(outbox->buf, port_type, QUERY_PORT_SUPPORTED_TYPE_OFFSET); @@ -1325,7 +1335,7 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave, MLX4_PUT(outbox->buf, short_field, QUERY_PORT_CUR_MAX_PKEY_OFFSET); } - +out: return err; } diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index 08de5555c2f4..7ea258af636a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -44,6 +44,7 @@ struct mlx4_mod_stat_cfg { }; struct mlx4_port_cap { + u8 link_state; u8 supported_port_types; u8 suggested_type; u8 default_sense; -- cgit v1.2.3-59-g8ed1b From 8d80d04a521d4acf949a449403040c38ec648f57 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Sun, 6 Dec 2015 18:07:39 +0200 Subject: net/mlx4_core: Use both physical ports to dispatch link state events to VF Under HA mode, the link down event should be sent to VFs only if both ports are down. Signed-off-by: Moni Shoua Reviewed-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/eq.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 603d1c3d3b2e..4696053165f8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -151,6 +151,17 @@ void mlx4_gen_slave_eqe(struct work_struct *work) eqe = next_slave_event_eqe(slave_eq)) { slave = eqe->slave_id; + if (eqe->type == MLX4_EVENT_TYPE_PORT_CHANGE && + eqe->subtype == MLX4_PORT_CHANGE_SUBTYPE_DOWN && + mlx4_is_bonded(dev)) { + struct mlx4_port_cap port_cap; + + if (!mlx4_QUERY_PORT(dev, 1, &port_cap) && port_cap.link_state) + goto consume; + + if (!mlx4_QUERY_PORT(dev, 2, &port_cap) && port_cap.link_state) + goto consume; + } /* All active slaves need to receive the event */ if (slave == ALL_SLAVES) { for (i = 0; i <= dev->persist->num_vfs; i++) { @@ -174,6 +185,7 @@ void mlx4_gen_slave_eqe(struct work_struct *work) mlx4_warn(dev, "Failed to generate event for slave %d\n", slave); } +consume: ++slave_eq->cons; } } @@ -594,7 +606,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) break; for (i = 0; i < dev->persist->num_vfs + 1; i++) { - if (!test_bit(i, slaves_port.slaves)) + int reported_port = mlx4_is_bonded(dev) ? 1 : mlx4_phys_to_slave_port(dev, i, port); + + if (!test_bit(i, slaves_port.slaves) && !mlx4_is_bonded(dev)) continue; if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) { if (i == mlx4_master_func_num(dev)) @@ -606,7 +620,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) eqe->event.port_change.port = cpu_to_be32( (be32_to_cpu(eqe->event.port_change.port) & 0xFFFFFFF) - | (mlx4_phys_to_slave_port(dev, i, port) << 28)); + | (reported_port << 28)); mlx4_slave_event(dev, i, eqe); } } else { /* IB port */ @@ -636,7 +650,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) for (i = 0; i < dev->persist->num_vfs + 1; i++) { - if (!test_bit(i, slaves_port.slaves)) + int reported_port = mlx4_is_bonded(dev) ? 1 : mlx4_phys_to_slave_port(dev, i, port); + + if (!test_bit(i, slaves_port.slaves) && !mlx4_is_bonded(dev)) continue; if (i == mlx4_master_func_num(dev)) continue; @@ -645,7 +661,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) eqe->event.port_change.port = cpu_to_be32( (be32_to_cpu(eqe->event.port_change.port) & 0xFFFFFFF) - | (mlx4_phys_to_slave_port(dev, i, port) << 28)); + | (reported_port << 28)); mlx4_slave_event(dev, i, eqe); } } -- cgit v1.2.3-59-g8ed1b From 78efed275117b189f06f8937798eab5cba53ed18 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Sun, 6 Dec 2015 18:07:40 +0200 Subject: net/mlx4_core: Support mirroring VF DMFS rules on both ports Under HA mode, steering rules set by VFs should be mirrored on both ports of the device so packets will be accepted no matter on which port they arrived. Since getting into HA mode is done dynamically when the user bonds mlx4 Ethernet netdevs, we keep hold of the VF DMFS rule mbox with the port value flipped (1->2,2->1) and execute the mirroring when getting into HA mode. Later, when going out of HA mode, we unset the mirrored rules. In that context note that mirrored rules cannot be removed explicitly. Signed-off-by: Moni Shoua Reviewed-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 2 + .../net/ethernet/mellanox/mlx4/resource_tracker.c | 193 ++++++++++++++++++++- 2 files changed, 186 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index e1cf9036af22..33c4c6f2c4bb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -1385,6 +1385,8 @@ int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave, int port); int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave); int mlx4_config_mad_demux(struct mlx4_dev *dev); int mlx4_do_bond(struct mlx4_dev *dev, bool enable); +int mlx4_bond_fs_rules(struct mlx4_dev *dev); +int mlx4_unbond_fs_rules(struct mlx4_dev *dev); enum mlx4_zone_flags { MLX4_ZONE_ALLOW_ALLOC_FROM_LOWER_PRIO = 1UL << 0, diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 6fec3e993d02..da7f578a3fe1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -222,6 +222,13 @@ enum res_fs_rule_states { struct res_fs_rule { struct res_common com; int qpn; + /* VF DMFS mbox with port flipped */ + void *mirr_mbox; + /* > 0 --> apply mirror when getting into HA mode */ + /* = 0 --> un-apply mirror when getting out of HA mode */ + u32 mirr_mbox_size; + struct list_head mirr_list; + u64 mirr_rule_id; }; static void *res_tracker_lookup(struct rb_root *root, u64 res_id) @@ -4284,6 +4291,22 @@ err_mac: return err; } +static u32 qp_attach_mbox_size(void *mbox) +{ + u32 size = sizeof(struct mlx4_net_trans_rule_hw_ctrl); + struct _rule_hw *rule_header; + + rule_header = (struct _rule_hw *)(mbox + size); + + while (rule_header->size) { + size += rule_header->size * sizeof(u32); + rule_header += 1; + } + return size; +} + +static int mlx4_do_mirror_rule(struct mlx4_dev *dev, struct res_fs_rule *fs_rule); + int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, @@ -4300,6 +4323,8 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_net_trans_rule_hw_ctrl *ctrl; struct _rule_hw *rule_header; int header_id; + struct res_fs_rule *rrule; + u32 mbox_size; if (dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) @@ -4328,7 +4353,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, case MLX4_NET_TRANS_RULE_ID_ETH: if (validate_eth_header_mac(slave, rule_header, rlist)) { err = -EINVAL; - goto err_put; + goto err_put_qp; } break; case MLX4_NET_TRANS_RULE_ID_IB: @@ -4339,7 +4364,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, pr_warn("Can't attach FS rule without L2 headers, adding L2 header\n"); if (add_eth_header(dev, slave, inbox, rlist, header_id)) { err = -EINVAL; - goto err_put; + goto err_put_qp; } vhcr->in_modifier += sizeof(struct mlx4_net_trans_rule_hw_eth) >> 2; @@ -4347,7 +4372,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, default: pr_err("Corrupted mailbox\n"); err = -EINVAL; - goto err_put; + goto err_put_qp; } execute: @@ -4356,23 +4381,69 @@ execute: MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); if (err) - goto err_put; + goto err_put_qp; + err = add_res_range(dev, slave, vhcr->out_param, 1, RES_FS_RULE, qpn); if (err) { mlx4_err(dev, "Fail to add flow steering resources\n"); - /* detach rule*/ + goto err_detach; + } + + err = get_res(dev, slave, vhcr->out_param, RES_FS_RULE, &rrule); + if (err) + goto err_detach; + + mbox_size = qp_attach_mbox_size(inbox->buf); + rrule->mirr_mbox = kmalloc(mbox_size, GFP_KERNEL); + if (!rrule->mirr_mbox) { + err = -ENOMEM; + goto err_put_rule; + } + rrule->mirr_mbox_size = mbox_size; + rrule->mirr_rule_id = 0; + memcpy(rrule->mirr_mbox, inbox->buf, mbox_size); + + /* set different port */ + ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)rrule->mirr_mbox; + if (ctrl->port == 1) + ctrl->port = 2; + else + ctrl->port = 1; + + if (mlx4_is_bonded(dev)) + mlx4_do_mirror_rule(dev, rrule); + + atomic_inc(&rqp->ref_count); + +err_put_rule: + put_res(dev, slave, vhcr->out_param, RES_FS_RULE); +err_detach: + /* detach rule on error */ + if (err) mlx4_cmd(dev, vhcr->out_param, 0, 0, MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); - goto err_put; - } - atomic_inc(&rqp->ref_count); -err_put: +err_put_qp: put_res(dev, slave, qpn, RES_QP); return err; } +static int mlx4_undo_mirror_rule(struct mlx4_dev *dev, struct res_fs_rule *fs_rule) +{ + int err; + + err = rem_res_range(dev, fs_rule->com.owner, fs_rule->com.res_id, 1, RES_FS_RULE, 0); + if (err) { + mlx4_err(dev, "Fail to remove flow steering resources\n"); + return err; + } + + mlx4_cmd(dev, fs_rule->com.res_id, 0, 0, MLX4_QP_FLOW_STEERING_DETACH, + MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); + return 0; +} + int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, @@ -4382,6 +4453,7 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave, int err; struct res_qp *rqp; struct res_fs_rule *rrule; + u64 mirr_reg_id; if (dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) @@ -4390,12 +4462,30 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave, err = get_res(dev, slave, vhcr->in_param, RES_FS_RULE, &rrule); if (err) return err; + + if (!rrule->mirr_mbox) { + mlx4_err(dev, "Mirror rules cannot be removed explicitly\n"); + put_res(dev, slave, vhcr->in_param, RES_FS_RULE); + return -EINVAL; + } + mirr_reg_id = rrule->mirr_rule_id; + kfree(rrule->mirr_mbox); + /* Release the rule form busy state before removal */ put_res(dev, slave, vhcr->in_param, RES_FS_RULE); err = get_res(dev, slave, rrule->qpn, RES_QP, &rqp); if (err) return err; + if (mirr_reg_id && mlx4_is_bonded(dev)) { + err = get_res(dev, slave, mirr_reg_id, RES_FS_RULE, &rrule); + if (err) { + mlx4_err(dev, "Fail to get resource of mirror rule\n"); + } else { + put_res(dev, slave, mirr_reg_id, RES_FS_RULE); + mlx4_undo_mirror_rule(dev, rrule); + } + } err = rem_res_range(dev, slave, vhcr->in_param, 1, RES_FS_RULE, 0); if (err) { mlx4_err(dev, "Fail to remove flow steering resources\n"); @@ -4833,6 +4923,91 @@ static void rem_slave_mtts(struct mlx4_dev *dev, int slave) spin_unlock_irq(mlx4_tlock(dev)); } +static int mlx4_do_mirror_rule(struct mlx4_dev *dev, struct res_fs_rule *fs_rule) +{ + struct mlx4_cmd_mailbox *mailbox; + int err; + struct res_fs_rule *mirr_rule; + u64 reg_id; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + if (!fs_rule->mirr_mbox) { + mlx4_err(dev, "rule mirroring mailbox is null\n"); + return -EINVAL; + } + memcpy(mailbox->buf, fs_rule->mirr_mbox, fs_rule->mirr_mbox_size); + err = mlx4_cmd_imm(dev, mailbox->dma, ®_id, fs_rule->mirr_mbox_size >> 2, 0, + MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + mlx4_free_cmd_mailbox(dev, mailbox); + + if (err) + goto err; + + err = add_res_range(dev, fs_rule->com.owner, reg_id, 1, RES_FS_RULE, fs_rule->qpn); + if (err) + goto err_detach; + + err = get_res(dev, fs_rule->com.owner, reg_id, RES_FS_RULE, &mirr_rule); + if (err) + goto err_rem; + + fs_rule->mirr_rule_id = reg_id; + mirr_rule->mirr_rule_id = 0; + mirr_rule->mirr_mbox_size = 0; + mirr_rule->mirr_mbox = NULL; + put_res(dev, fs_rule->com.owner, reg_id, RES_FS_RULE); + + return 0; +err_rem: + rem_res_range(dev, fs_rule->com.owner, reg_id, 1, RES_FS_RULE, 0); +err_detach: + mlx4_cmd(dev, reg_id, 0, 0, MLX4_QP_FLOW_STEERING_DETACH, + MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); +err: + return err; +} + +static int mlx4_mirror_fs_rules(struct mlx4_dev *dev, bool bond) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_resource_tracker *tracker = + &priv->mfunc.master.res_tracker; + struct rb_root *root = &tracker->res_tree[RES_FS_RULE]; + struct rb_node *p; + struct res_fs_rule *fs_rule; + int err = 0; + LIST_HEAD(mirr_list); + + for (p = rb_first(root); p; p = rb_next(p)) { + fs_rule = rb_entry(p, struct res_fs_rule, com.node); + if ((bond && fs_rule->mirr_mbox_size) || + (!bond && !fs_rule->mirr_mbox_size)) + list_add_tail(&fs_rule->mirr_list, &mirr_list); + } + + list_for_each_entry(fs_rule, &mirr_list, mirr_list) { + if (bond) + err += mlx4_do_mirror_rule(dev, fs_rule); + else + err += mlx4_undo_mirror_rule(dev, fs_rule); + } + return err; +} + +int mlx4_bond_fs_rules(struct mlx4_dev *dev) +{ + return mlx4_mirror_fs_rules(dev, true); +} + +int mlx4_unbond_fs_rules(struct mlx4_dev *dev) +{ + return mlx4_mirror_fs_rules(dev, false); +} + static void rem_slave_fs_rule(struct mlx4_dev *dev, int slave) { struct mlx4_priv *priv = mlx4_priv(dev); -- cgit v1.2.3-59-g8ed1b From 5f61385d2ebc2bd62bc389c7da0d8d2f263be1eb Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Sun, 6 Dec 2015 18:07:41 +0200 Subject: net/mlx4_core: Keep VLAN/MAC tables mirrored in multifunc HA mode Due to HW limitations, indexes to MAC and VLAN tables are always taken from the table of the actual port. So, if a resource holds an index to a table, it may refer to different values during the lifetime of the resource, unless the tables are mirrored. Also, even when driver is not in HA mode the policy of allocating an index to these tables is such to make sure, as much as possible, that when the time comes the mirroring will be successful. This means that in multifunction mode the allocation of a free index in a port's table tries to make sure that the same index in the other's port table is also free. Signed-off-by: Moni Shoua Reviewed-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 6 + drivers/net/ethernet/mellanox/mlx4/port.c | 598 ++++++++++++++++++++++++++++-- include/linux/mlx4/driver.h | 5 + 3 files changed, 586 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 33c4c6f2c4bb..2404c22ad2b2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -736,6 +736,7 @@ struct mlx4_catas_err { struct mlx4_mac_table { __be64 entries[MLX4_MAX_MAC_NUM]; int refs[MLX4_MAX_MAC_NUM]; + bool is_dup[MLX4_MAX_MAC_NUM]; struct mutex mutex; int total; int max; @@ -758,6 +759,7 @@ struct mlx4_roce_gid_table { struct mlx4_vlan_table { __be32 entries[MLX4_MAX_VLAN_NUM]; int refs[MLX4_MAX_VLAN_NUM]; + int is_dup[MLX4_MAX_VLAN_NUM]; struct mutex mutex; int total; int max; @@ -1225,6 +1227,10 @@ void mlx4_init_roce_gid_table(struct mlx4_dev *dev, struct mlx4_roce_gid_table *table); void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan); int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index); +int mlx4_bond_vlan_table(struct mlx4_dev *dev); +int mlx4_unbond_vlan_table(struct mlx4_dev *dev); +int mlx4_bond_mac_table(struct mlx4_dev *dev); +int mlx4_unbond_mac_table(struct mlx4_dev *dev); int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz); /* resource tracker functions*/ diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index c2b21313dba7..f2550425c251 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -61,6 +61,7 @@ void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table) for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { table->entries[i] = 0; table->refs[i] = 0; + table->is_dup[i] = false; } table->max = 1 << dev->caps.log_num_macs; table->total = 0; @@ -74,6 +75,7 @@ void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table) for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) { table->entries[i] = 0; table->refs[i] = 0; + table->is_dup[i] = false; } table->max = (1 << dev->caps.log_num_vlans) - MLX4_VLAN_REGULAR; table->total = 0; @@ -159,21 +161,94 @@ int mlx4_find_cached_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *idx) } EXPORT_SYMBOL_GPL(mlx4_find_cached_mac); +static bool mlx4_need_mf_bond(struct mlx4_dev *dev) +{ + int i, num_eth_ports = 0; + + if (!mlx4_is_mfunc(dev)) + return false; + mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) + ++num_eth_ports; + + return (num_eth_ports == 2) ? true : false; +} + int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac) { struct mlx4_port_info *info = &mlx4_priv(dev)->port[port]; struct mlx4_mac_table *table = &info->mac_table; int i, err = 0; int free = -1; + int free_for_dup = -1; + bool dup = mlx4_is_mf_bonded(dev); + u8 dup_port = (port == 1) ? 2 : 1; + struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table; + bool need_mf_bond = mlx4_need_mf_bond(dev); + bool can_mf_bond = true; + + mlx4_dbg(dev, "Registering MAC: 0x%llx for port %d %s duplicate\n", + (unsigned long long)mac, port, + dup ? "with" : "without"); + + if (need_mf_bond) { + if (port == 1) { + mutex_lock(&table->mutex); + mutex_lock(&dup_table->mutex); + } else { + mutex_lock(&dup_table->mutex); + mutex_lock(&table->mutex); + } + } else { + mutex_lock(&table->mutex); + } + + if (need_mf_bond) { + int index_at_port = -1; + int index_at_dup_port = -1; - mlx4_dbg(dev, "Registering MAC: 0x%llx for port %d\n", - (unsigned long long) mac, port); + for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { + if (((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(table->entries[i])))) + index_at_port = i; + if (((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(dup_table->entries[i])))) + index_at_dup_port = i; + } + + /* check that same mac is not in the tables at different indices */ + if ((index_at_port != index_at_dup_port) && + (index_at_port >= 0) && + (index_at_dup_port >= 0)) + can_mf_bond = false; + + /* If the mac is already in the primary table, the slot must be + * available in the duplicate table as well. + */ + if (index_at_port >= 0 && index_at_dup_port < 0 && + dup_table->refs[index_at_port]) { + can_mf_bond = false; + } + /* If the mac is already in the duplicate table, check that the + * corresponding index is not occupied in the primary table, or + * the primary table already contains the mac at the same index. + * Otherwise, you cannot bond (primary contains a different mac + * at that index). + */ + if (index_at_dup_port >= 0) { + if (!table->refs[index_at_dup_port] || + ((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(table->entries[index_at_dup_port])))) + free_for_dup = index_at_dup_port; + else + can_mf_bond = false; + } + } - mutex_lock(&table->mutex); for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { if (!table->refs[i]) { if (free < 0) free = i; + if (free_for_dup < 0 && need_mf_bond && can_mf_bond) { + if (!dup_table->refs[i]) + free_for_dup = i; + } continue; } @@ -182,10 +257,30 @@ int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac) /* MAC already registered, increment ref count */ err = i; ++table->refs[i]; + if (dup) { + u64 dup_mac = MLX4_MAC_MASK & be64_to_cpu(dup_table->entries[i]); + + if (dup_mac != mac || !dup_table->is_dup[i]) { + mlx4_warn(dev, "register mac: expect duplicate mac 0x%llx on port %d index %d\n", + mac, dup_port, i); + } + } goto out; } } + if (need_mf_bond && (free_for_dup < 0)) { + if (dup) { + mlx4_warn(dev, "Fail to allocate duplicate MAC table entry\n"); + mlx4_warn(dev, "High Availability for virtual functions may not work as expected\n"); + dup = false; + } + can_mf_bond = false; + } + + if (need_mf_bond && can_mf_bond) + free = free_for_dup; + mlx4_dbg(dev, "Free MAC index is %d\n", free); if (table->total == table->max) { @@ -205,10 +300,35 @@ int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac) goto out; } table->refs[free] = 1; - err = free; + table->is_dup[free] = false; ++table->total; + if (dup) { + dup_table->refs[free] = 0; + dup_table->is_dup[free] = true; + dup_table->entries[free] = cpu_to_be64(mac | MLX4_MAC_VALID); + + err = mlx4_set_port_mac_table(dev, dup_port, dup_table->entries); + if (unlikely(err)) { + mlx4_warn(dev, "Failed adding duplicate mac: 0x%llx\n", mac); + dup_table->is_dup[free] = false; + dup_table->entries[free] = 0; + goto out; + } + ++dup_table->total; + } + err = free; out: - mutex_unlock(&table->mutex); + if (need_mf_bond) { + if (port == 2) { + mutex_unlock(&table->mutex); + mutex_unlock(&dup_table->mutex); + } else { + mutex_unlock(&dup_table->mutex); + mutex_unlock(&table->mutex); + } + } else { + mutex_unlock(&table->mutex); + } return err; } EXPORT_SYMBOL_GPL(__mlx4_register_mac); @@ -255,6 +375,9 @@ void __mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac) struct mlx4_port_info *info; struct mlx4_mac_table *table; int index; + bool dup = mlx4_is_mf_bonded(dev); + u8 dup_port = (port == 1) ? 2 : 1; + struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table; if (port < 1 || port > dev->caps.num_ports) { mlx4_warn(dev, "invalid port number (%d), aborting...\n", port); @@ -262,22 +385,59 @@ void __mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac) } info = &mlx4_priv(dev)->port[port]; table = &info->mac_table; - mutex_lock(&table->mutex); + + if (dup) { + if (port == 1) { + mutex_lock(&table->mutex); + mutex_lock(&dup_table->mutex); + } else { + mutex_lock(&dup_table->mutex); + mutex_lock(&table->mutex); + } + } else { + mutex_lock(&table->mutex); + } + index = find_index(dev, table, mac); if (validate_index(dev, table, index)) goto out; - if (--table->refs[index]) { + + if (--table->refs[index] || table->is_dup[index]) { mlx4_dbg(dev, "Have more references for index %d, no need to modify mac table\n", index); + if (!table->refs[index]) + dup_table->is_dup[index] = false; goto out; } table->entries[index] = 0; - mlx4_set_port_mac_table(dev, port, table->entries); + if (mlx4_set_port_mac_table(dev, port, table->entries)) + mlx4_warn(dev, "Fail to set mac in port %d during unregister\n", port); --table->total; + + if (dup) { + dup_table->is_dup[index] = false; + if (dup_table->refs[index]) + goto out; + dup_table->entries[index] = 0; + if (mlx4_set_port_mac_table(dev, dup_port, dup_table->entries)) + mlx4_warn(dev, "Fail to set mac in duplicate port %d during unregister\n", dup_port); + + --table->total; + } out: - mutex_unlock(&table->mutex); + if (dup) { + if (port == 2) { + mutex_unlock(&table->mutex); + mutex_unlock(&dup_table->mutex); + } else { + mutex_unlock(&dup_table->mutex); + mutex_unlock(&table->mutex); + } + } else { + mutex_unlock(&table->mutex); + } } EXPORT_SYMBOL_GPL(__mlx4_unregister_mac); @@ -311,9 +471,22 @@ int __mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac) struct mlx4_mac_table *table = &info->mac_table; int index = qpn - info->base_qpn; int err = 0; + bool dup = mlx4_is_mf_bonded(dev); + u8 dup_port = (port == 1) ? 2 : 1; + struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table; /* CX1 doesn't support multi-functions */ - mutex_lock(&table->mutex); + if (dup) { + if (port == 1) { + mutex_lock(&table->mutex); + mutex_lock(&dup_table->mutex); + } else { + mutex_lock(&dup_table->mutex); + mutex_lock(&table->mutex); + } + } else { + mutex_lock(&table->mutex); + } err = validate_index(dev, table, index); if (err) @@ -326,9 +499,30 @@ int __mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac) mlx4_err(dev, "Failed adding MAC: 0x%llx\n", (unsigned long long) new_mac); table->entries[index] = 0; + } else { + if (dup) { + dup_table->entries[index] = cpu_to_be64(new_mac | MLX4_MAC_VALID); + + err = mlx4_set_port_mac_table(dev, dup_port, dup_table->entries); + if (unlikely(err)) { + mlx4_err(dev, "Failed adding duplicate MAC: 0x%llx\n", + (unsigned long long)new_mac); + dup_table->entries[index] = 0; + } + } } out: - mutex_unlock(&table->mutex); + if (dup) { + if (port == 2) { + mutex_unlock(&table->mutex); + mutex_unlock(&dup_table->mutex); + } else { + mutex_unlock(&dup_table->mutex); + mutex_unlock(&table->mutex); + } + } else { + mutex_unlock(&table->mutex); + } return err; } EXPORT_SYMBOL_GPL(__mlx4_replace_mac); @@ -380,8 +574,28 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table; int i, err = 0; int free = -1; - - mutex_lock(&table->mutex); + int free_for_dup = -1; + bool dup = mlx4_is_mf_bonded(dev); + u8 dup_port = (port == 1) ? 2 : 1; + struct mlx4_vlan_table *dup_table = &mlx4_priv(dev)->port[dup_port].vlan_table; + bool need_mf_bond = mlx4_need_mf_bond(dev); + bool can_mf_bond = true; + + mlx4_dbg(dev, "Registering VLAN: %d for port %d %s duplicate\n", + vlan, port, + dup ? "with" : "without"); + + if (need_mf_bond) { + if (port == 1) { + mutex_lock(&table->mutex); + mutex_lock(&dup_table->mutex); + } else { + mutex_lock(&dup_table->mutex); + mutex_lock(&table->mutex); + } + } else { + mutex_lock(&table->mutex); + } if (table->total == table->max) { /* No free vlan entries */ @@ -389,22 +603,85 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, goto out; } + if (need_mf_bond) { + int index_at_port = -1; + int index_at_dup_port = -1; + + for (i = MLX4_VLAN_REGULAR; i < MLX4_MAX_VLAN_NUM; i++) { + if ((vlan == (MLX4_VLAN_MASK & be32_to_cpu(table->entries[i])))) + index_at_port = i; + if ((vlan == (MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[i])))) + index_at_dup_port = i; + } + /* check that same vlan is not in the tables at different indices */ + if ((index_at_port != index_at_dup_port) && + (index_at_port >= 0) && + (index_at_dup_port >= 0)) + can_mf_bond = false; + + /* If the vlan is already in the primary table, the slot must be + * available in the duplicate table as well. + */ + if (index_at_port >= 0 && index_at_dup_port < 0 && + dup_table->refs[index_at_port]) { + can_mf_bond = false; + } + /* If the vlan is already in the duplicate table, check that the + * corresponding index is not occupied in the primary table, or + * the primary table already contains the vlan at the same index. + * Otherwise, you cannot bond (primary contains a different vlan + * at that index). + */ + if (index_at_dup_port >= 0) { + if (!table->refs[index_at_dup_port] || + (vlan == (MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[index_at_dup_port])))) + free_for_dup = index_at_dup_port; + else + can_mf_bond = false; + } + } + for (i = MLX4_VLAN_REGULAR; i < MLX4_MAX_VLAN_NUM; i++) { - if (free < 0 && (table->refs[i] == 0)) { - free = i; - continue; + if (!table->refs[i]) { + if (free < 0) + free = i; + if (free_for_dup < 0 && need_mf_bond && can_mf_bond) { + if (!dup_table->refs[i]) + free_for_dup = i; + } } - if (table->refs[i] && + if ((table->refs[i] || table->is_dup[i]) && (vlan == (MLX4_VLAN_MASK & be32_to_cpu(table->entries[i])))) { /* Vlan already registered, increase references count */ + mlx4_dbg(dev, "vlan %u is already registered.\n", vlan); *index = i; ++table->refs[i]; + if (dup) { + u16 dup_vlan = MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[i]); + + if (dup_vlan != vlan || !dup_table->is_dup[i]) { + mlx4_warn(dev, "register vlan: expected duplicate vlan %u on port %d index %d\n", + vlan, dup_port, i); + } + } goto out; } } + if (need_mf_bond && (free_for_dup < 0)) { + if (dup) { + mlx4_warn(dev, "Fail to allocate duplicate VLAN table entry\n"); + mlx4_warn(dev, "High Availability for virtual functions may not work as expected\n"); + dup = false; + } + can_mf_bond = false; + } + + if (need_mf_bond && can_mf_bond) + free = free_for_dup; + if (free < 0) { err = -ENOMEM; goto out; @@ -412,6 +689,7 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, /* Register new VLAN */ table->refs[free] = 1; + table->is_dup[free] = false; table->entries[free] = cpu_to_be32(vlan | MLX4_VLAN_VALID); err = mlx4_set_port_vlan_table(dev, port, table->entries); @@ -421,11 +699,35 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, table->entries[free] = 0; goto out; } + ++table->total; + if (dup) { + dup_table->refs[free] = 0; + dup_table->is_dup[free] = true; + dup_table->entries[free] = cpu_to_be32(vlan | MLX4_VLAN_VALID); + + err = mlx4_set_port_vlan_table(dev, dup_port, dup_table->entries); + if (unlikely(err)) { + mlx4_warn(dev, "Failed adding duplicate vlan: %u\n", vlan); + dup_table->is_dup[free] = false; + dup_table->entries[free] = 0; + goto out; + } + ++dup_table->total; + } *index = free; - ++table->total; out: - mutex_unlock(&table->mutex); + if (need_mf_bond) { + if (port == 2) { + mutex_unlock(&table->mutex); + mutex_unlock(&dup_table->mutex); + } else { + mutex_unlock(&dup_table->mutex); + mutex_unlock(&table->mutex); + } + } else { + mutex_unlock(&table->mutex); + } return err; } @@ -455,8 +757,22 @@ void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan) { struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table; int index; + bool dup = mlx4_is_mf_bonded(dev); + u8 dup_port = (port == 1) ? 2 : 1; + struct mlx4_vlan_table *dup_table = &mlx4_priv(dev)->port[dup_port].vlan_table; + + if (dup) { + if (port == 1) { + mutex_lock(&table->mutex); + mutex_lock(&dup_table->mutex); + } else { + mutex_lock(&dup_table->mutex); + mutex_lock(&table->mutex); + } + } else { + mutex_lock(&table->mutex); + } - mutex_lock(&table->mutex); if (mlx4_find_cached_vlan(dev, port, vlan, &index)) { mlx4_warn(dev, "vlan 0x%x is not in the vlan table\n", vlan); goto out; @@ -467,16 +783,38 @@ void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan) goto out; } - if (--table->refs[index]) { + if (--table->refs[index] || table->is_dup[index]) { mlx4_dbg(dev, "Have %d more references for index %d, no need to modify vlan table\n", table->refs[index], index); + if (!table->refs[index]) + dup_table->is_dup[index] = false; goto out; } table->entries[index] = 0; - mlx4_set_port_vlan_table(dev, port, table->entries); + if (mlx4_set_port_vlan_table(dev, port, table->entries)) + mlx4_warn(dev, "Fail to set vlan in port %d during unregister\n", port); --table->total; + if (dup) { + dup_table->is_dup[index] = false; + if (dup_table->refs[index]) + goto out; + dup_table->entries[index] = 0; + if (mlx4_set_port_vlan_table(dev, dup_port, dup_table->entries)) + mlx4_warn(dev, "Fail to set vlan in duplicate port %d during unregister\n", dup_port); + --dup_table->total; + } out: - mutex_unlock(&table->mutex); + if (dup) { + if (port == 2) { + mutex_unlock(&table->mutex); + mutex_unlock(&dup_table->mutex); + } else { + mutex_unlock(&dup_table->mutex); + mutex_unlock(&table->mutex); + } + } else { + mutex_unlock(&table->mutex); + } } void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan) @@ -495,6 +833,220 @@ void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan) } EXPORT_SYMBOL_GPL(mlx4_unregister_vlan); +int mlx4_bond_mac_table(struct mlx4_dev *dev) +{ + struct mlx4_mac_table *t1 = &mlx4_priv(dev)->port[1].mac_table; + struct mlx4_mac_table *t2 = &mlx4_priv(dev)->port[2].mac_table; + int ret = 0; + int i; + bool update1 = false; + bool update2 = false; + + mutex_lock(&t1->mutex); + mutex_lock(&t2->mutex); + for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { + if ((t1->entries[i] != t2->entries[i]) && + t1->entries[i] && t2->entries[i]) { + mlx4_warn(dev, "can't duplicate entry %d in mac table\n", i); + ret = -EINVAL; + goto unlock; + } + } + + for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { + if (t1->entries[i] && !t2->entries[i]) { + t2->entries[i] = t1->entries[i]; + t2->is_dup[i] = true; + update2 = true; + } else if (!t1->entries[i] && t2->entries[i]) { + t1->entries[i] = t2->entries[i]; + t1->is_dup[i] = true; + update1 = true; + } else if (t1->entries[i] && t2->entries[i]) { + t1->is_dup[i] = true; + t2->is_dup[i] = true; + } + } + + if (update1) { + ret = mlx4_set_port_mac_table(dev, 1, t1->entries); + if (ret) + mlx4_warn(dev, "failed to set MAC table for port 1 (%d)\n", ret); + } + if (!ret && update2) { + ret = mlx4_set_port_mac_table(dev, 2, t2->entries); + if (ret) + mlx4_warn(dev, "failed to set MAC table for port 2 (%d)\n", ret); + } + + if (ret) + mlx4_warn(dev, "failed to create mirror MAC tables\n"); +unlock: + mutex_unlock(&t2->mutex); + mutex_unlock(&t1->mutex); + return ret; +} + +int mlx4_unbond_mac_table(struct mlx4_dev *dev) +{ + struct mlx4_mac_table *t1 = &mlx4_priv(dev)->port[1].mac_table; + struct mlx4_mac_table *t2 = &mlx4_priv(dev)->port[2].mac_table; + int ret = 0; + int ret1; + int i; + bool update1 = false; + bool update2 = false; + + mutex_lock(&t1->mutex); + mutex_lock(&t2->mutex); + for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { + if (t1->entries[i] != t2->entries[i]) { + mlx4_warn(dev, "mac table is in an unexpected state when trying to unbond\n"); + ret = -EINVAL; + goto unlock; + } + } + + for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { + if (!t1->entries[i]) + continue; + t1->is_dup[i] = false; + if (!t1->refs[i]) { + t1->entries[i] = 0; + update1 = true; + } + t2->is_dup[i] = false; + if (!t2->refs[i]) { + t2->entries[i] = 0; + update2 = true; + } + } + + if (update1) { + ret = mlx4_set_port_mac_table(dev, 1, t1->entries); + if (ret) + mlx4_warn(dev, "failed to unmirror MAC tables for port 1(%d)\n", ret); + } + if (update2) { + ret1 = mlx4_set_port_mac_table(dev, 2, t2->entries); + if (ret1) { + mlx4_warn(dev, "failed to unmirror MAC tables for port 2(%d)\n", ret1); + ret = ret1; + } + } +unlock: + mutex_unlock(&t2->mutex); + mutex_unlock(&t1->mutex); + return ret; +} + +int mlx4_bond_vlan_table(struct mlx4_dev *dev) +{ + struct mlx4_vlan_table *t1 = &mlx4_priv(dev)->port[1].vlan_table; + struct mlx4_vlan_table *t2 = &mlx4_priv(dev)->port[2].vlan_table; + int ret = 0; + int i; + bool update1 = false; + bool update2 = false; + + mutex_lock(&t1->mutex); + mutex_lock(&t2->mutex); + for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) { + if ((t1->entries[i] != t2->entries[i]) && + t1->entries[i] && t2->entries[i]) { + mlx4_warn(dev, "can't duplicate entry %d in vlan table\n", i); + ret = -EINVAL; + goto unlock; + } + } + + for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) { + if (t1->entries[i] && !t2->entries[i]) { + t2->entries[i] = t1->entries[i]; + t2->is_dup[i] = true; + update2 = true; + } else if (!t1->entries[i] && t2->entries[i]) { + t1->entries[i] = t2->entries[i]; + t1->is_dup[i] = true; + update1 = true; + } else if (t1->entries[i] && t2->entries[i]) { + t1->is_dup[i] = true; + t2->is_dup[i] = true; + } + } + + if (update1) { + ret = mlx4_set_port_vlan_table(dev, 1, t1->entries); + if (ret) + mlx4_warn(dev, "failed to set VLAN table for port 1 (%d)\n", ret); + } + if (!ret && update2) { + ret = mlx4_set_port_vlan_table(dev, 2, t2->entries); + if (ret) + mlx4_warn(dev, "failed to set VLAN table for port 2 (%d)\n", ret); + } + + if (ret) + mlx4_warn(dev, "failed to create mirror VLAN tables\n"); +unlock: + mutex_unlock(&t2->mutex); + mutex_unlock(&t1->mutex); + return ret; +} + +int mlx4_unbond_vlan_table(struct mlx4_dev *dev) +{ + struct mlx4_vlan_table *t1 = &mlx4_priv(dev)->port[1].vlan_table; + struct mlx4_vlan_table *t2 = &mlx4_priv(dev)->port[2].vlan_table; + int ret = 0; + int ret1; + int i; + bool update1 = false; + bool update2 = false; + + mutex_lock(&t1->mutex); + mutex_lock(&t2->mutex); + for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) { + if (t1->entries[i] != t2->entries[i]) { + mlx4_warn(dev, "vlan table is in an unexpected state when trying to unbond\n"); + ret = -EINVAL; + goto unlock; + } + } + + for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) { + if (!t1->entries[i]) + continue; + t1->is_dup[i] = false; + if (!t1->refs[i]) { + t1->entries[i] = 0; + update1 = true; + } + t2->is_dup[i] = false; + if (!t2->refs[i]) { + t2->entries[i] = 0; + update2 = true; + } + } + + if (update1) { + ret = mlx4_set_port_vlan_table(dev, 1, t1->entries); + if (ret) + mlx4_warn(dev, "failed to unmirror VLAN tables for port 1(%d)\n", ret); + } + if (update2) { + ret1 = mlx4_set_port_vlan_table(dev, 2, t2->entries); + if (ret1) { + mlx4_warn(dev, "failed to unmirror VLAN tables for port 2(%d)\n", ret1); + ret = ret1; + } + } +unlock: + mutex_unlock(&t2->mutex); + mutex_unlock(&t1->mutex); + return ret; +} + int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps) { struct mlx4_cmd_mailbox *inmailbox, *outmailbox; diff --git a/include/linux/mlx4/driver.h b/include/linux/mlx4/driver.h index 5a06d969338e..2e8af001c5da 100644 --- a/include/linux/mlx4/driver.h +++ b/include/linux/mlx4/driver.h @@ -75,6 +75,11 @@ static inline int mlx4_is_bonded(struct mlx4_dev *dev) return !!(dev->flags & MLX4_FLAG_BONDED); } +static inline int mlx4_is_mf_bonded(struct mlx4_dev *dev) +{ + return (mlx4_is_bonded(dev) && mlx4_is_mfunc(dev)); +} + struct mlx4_port_map { u8 port1; u8 port2; -- cgit v1.2.3-59-g8ed1b From f1b4e12a9ab6cc08b1a047bf021d90c7e07b1517 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Sun, 6 Dec 2015 18:07:42 +0200 Subject: IB/mlx4: Use the VF base-port when demuxing mad from wire Under HA mode, it's possible that the VF registered its GID (and expects to get mads through the PV scheme) on a port which is different from the one this mad arrived on, due to HA fail over. Therefore, if the gid is not matched on the port that the packet arrived on, check for a match on the other port if HA mode is active -- and if a match is found on the other port, continue processing the mad using that other port. Signed-off-by: Or Gerlitz Reviewed-by: Jack Morgenstein Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx4/mad.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 870e56b6b25f..26833bfa639b 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -40,6 +40,7 @@ #include #include +#include #include "mlx4_ib.h" enum { @@ -606,8 +607,8 @@ static int mlx4_ib_demux_mad(struct ib_device *ibdev, u8 port, struct ib_mad *mad) { struct mlx4_ib_dev *dev = to_mdev(ibdev); - int err; - int slave; + int err, other_port; + int slave = -1; u8 *slave_id; int is_eth = 0; @@ -625,7 +626,17 @@ static int mlx4_ib_demux_mad(struct ib_device *ibdev, u8 port, mlx4_ib_warn(ibdev, "RoCE mgmt class is not CM\n"); return -EINVAL; } - if (mlx4_get_slave_from_roce_gid(dev->dev, port, grh->dgid.raw, &slave)) { + err = mlx4_get_slave_from_roce_gid(dev->dev, port, grh->dgid.raw, &slave); + if (err && mlx4_is_mf_bonded(dev->dev)) { + other_port = (port == 1) ? 2 : 1; + err = mlx4_get_slave_from_roce_gid(dev->dev, other_port, grh->dgid.raw, &slave); + if (!err) { + port = other_port; + pr_debug("resolved slave %d from gid %pI6 wire port %d other %d\n", + slave, grh->dgid.raw, port, other_port); + } + } + if (err) { mlx4_ib_warn(ibdev, "failed matching grh\n"); return -ENOENT; } -- cgit v1.2.3-59-g8ed1b From e57968a10bc1b3da6d2b8b0bdbbe4c5a43de2ad1 Mon Sep 17 00:00:00 2001 From: Moni Shoua Date: Sun, 6 Dec 2015 18:07:43 +0200 Subject: net/mlx4_core: Support the HA mode for SRIOV VFs too When the mlx4 driver runs in HA mode, and all VFs are single ported ones, we make their single port Highly-Available. This is done by taking advantage of the HA mode properties (following bonding changes with programming the port V2P map, etc) and adding the missing parts which are unique to SRIOV such as mirroring VF steering rules on both ports. Due to limits on the MAC and VLAN table this mode is enabled only when number of total VFs is under 64. Signed-off-by: Moni Shoua Reviewed-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/main.c | 107 +++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 31c491e02e69..f1b6d219e445 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -1221,6 +1221,76 @@ err_set_port: return err ? err : count; } +/* bond for multi-function device */ +#define MAX_MF_BOND_ALLOWED_SLAVES 63 +static int mlx4_mf_bond(struct mlx4_dev *dev) +{ + int err = 0; + struct mlx4_slaves_pport slaves_port1; + struct mlx4_slaves_pport slaves_port2; + DECLARE_BITMAP(slaves_port_1_2, MLX4_MFUNC_MAX); + + slaves_port1 = mlx4_phys_to_slaves_pport(dev, 1); + slaves_port2 = mlx4_phys_to_slaves_pport(dev, 2); + bitmap_and(slaves_port_1_2, + slaves_port1.slaves, slaves_port2.slaves, + dev->persist->num_vfs + 1); + + /* only single port vfs are allowed */ + if (bitmap_weight(slaves_port_1_2, dev->persist->num_vfs + 1) > 1) { + mlx4_warn(dev, "HA mode unsupported for dual ported VFs\n"); + return -EINVAL; + } + + /* limit on maximum allowed VFs */ + if ((bitmap_weight(slaves_port1.slaves, dev->persist->num_vfs + 1) + + bitmap_weight(slaves_port2.slaves, dev->persist->num_vfs + 1)) > + MAX_MF_BOND_ALLOWED_SLAVES) + return -EINVAL; + + if (dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) { + mlx4_warn(dev, "HA mode unsupported for NON DMFS steering\n"); + return -EINVAL; + } + + err = mlx4_bond_mac_table(dev); + if (err) + return err; + err = mlx4_bond_vlan_table(dev); + if (err) + goto err1; + err = mlx4_bond_fs_rules(dev); + if (err) + goto err2; + + return 0; +err2: + (void)mlx4_unbond_vlan_table(dev); +err1: + (void)mlx4_unbond_mac_table(dev); + return err; +} + +static int mlx4_mf_unbond(struct mlx4_dev *dev) +{ + int ret, ret1; + + ret = mlx4_unbond_fs_rules(dev); + if (ret) + mlx4_warn(dev, "multifunction unbond for flow rules failedi (%d)\n", ret); + ret1 = mlx4_unbond_mac_table(dev); + if (ret1) { + mlx4_warn(dev, "multifunction unbond for MAC table failed (%d)\n", ret1); + ret = ret1; + } + ret1 = mlx4_unbond_vlan_table(dev); + if (ret1) { + mlx4_warn(dev, "multifunction unbond for VLAN table failed (%d)\n", ret1); + ret = ret1; + } + return ret; +} + int mlx4_bond(struct mlx4_dev *dev) { int ret = 0; @@ -1228,16 +1298,23 @@ int mlx4_bond(struct mlx4_dev *dev) mutex_lock(&priv->bond_mutex); - if (!mlx4_is_bonded(dev)) + if (!mlx4_is_bonded(dev)) { ret = mlx4_do_bond(dev, true); - else - ret = 0; + if (ret) + mlx4_err(dev, "Failed to bond device: %d\n", ret); + if (!ret && mlx4_is_master(dev)) { + ret = mlx4_mf_bond(dev); + if (ret) { + mlx4_err(dev, "bond for multifunction failed\n"); + mlx4_do_bond(dev, false); + } + } + } mutex_unlock(&priv->bond_mutex); - if (ret) - mlx4_err(dev, "Failed to bond device: %d\n", ret); - else + if (!ret) mlx4_dbg(dev, "Device is bonded\n"); + return ret; } EXPORT_SYMBOL_GPL(mlx4_bond); @@ -1249,14 +1326,24 @@ int mlx4_unbond(struct mlx4_dev *dev) mutex_lock(&priv->bond_mutex); - if (mlx4_is_bonded(dev)) + if (mlx4_is_bonded(dev)) { + int ret2 = 0; + ret = mlx4_do_bond(dev, false); + if (ret) + mlx4_err(dev, "Failed to unbond device: %d\n", ret); + if (mlx4_is_master(dev)) + ret2 = mlx4_mf_unbond(dev); + if (ret2) { + mlx4_warn(dev, "Failed to unbond device for multifunction (%d)\n", ret2); + ret = ret2; + } + } mutex_unlock(&priv->bond_mutex); - if (ret) - mlx4_err(dev, "Failed to unbond device: %d\n", ret); - else + if (!ret) mlx4_dbg(dev, "Device is unbonded\n"); + return ret; } EXPORT_SYMBOL_GPL(mlx4_unbond); -- cgit v1.2.3-59-g8ed1b From 404814af69d4732276319b90886b81fb2884ae1b Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sun, 6 Dec 2015 22:47:15 +0100 Subject: net: cdc_ncm: add "ndp_to_end" sysfs attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding a writable sysfs attribute for the "NDP to end" quirk flag. This makes it easier for end users to test new devices for this firmware bug. We've been lucky so far, but we should not depend on reporters capable of rebuilding the driver. Cc: Enrico Mioso Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- Documentation/ABI/testing/sysfs-class-net-cdc_ncm | 19 ++++++++++ drivers/net/usb/cdc_ncm.c | 43 +++++++++++++++++++++++ 2 files changed, 62 insertions(+) (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-class-net-cdc_ncm b/Documentation/ABI/testing/sysfs-class-net-cdc_ncm index 5cedf72df358..f7be0e88b139 100644 --- a/Documentation/ABI/testing/sysfs-class-net-cdc_ncm +++ b/Documentation/ABI/testing/sysfs-class-net-cdc_ncm @@ -19,6 +19,25 @@ Description: Set to 0 to pad all frames. Set greater than tx_max to disable all padding. +What: /sys/class/net//cdc_ncm/ndp_to_end +Date: Dec 2015 +KernelVersion: 4.5 +Contact: Bjørn Mork +Description: + Boolean attribute showing the status of the "NDP to + end" quirk. Defaults to 'N', except for devices + already known to need it enabled. + + The "NDP to end" quirk makes the driver place the NDP + (the packet index table) after the payload. The NCM + specification does not mandate this, but some devices + are known to be more restrictive. Write 'Y' to this + attribute for temporary testing of a suspect device + failing to work with the default driver settings. + + A device entry should be added to the driver if this + quirk is found to be required. + What: /sys/class/net//cdc_ncm/rx_max Date: May 2014 KernelVersion: 3.16 diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 3b1ba8237768..b45e5cae2a6b 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -283,6 +283,48 @@ static DEVICE_ATTR(rx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_rx_max, cdc_ncm_store static DEVICE_ATTR(tx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max); static DEVICE_ATTR(tx_timer_usecs, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs); +static ssize_t ndp_to_end_show(struct device *d, struct device_attribute *attr, char *buf) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + + return sprintf(buf, "%c\n", ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END ? 'Y' : 'N'); +} + +static ssize_t ndp_to_end_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + bool enable; + + if (strtobool(buf, &enable)) + return -EINVAL; + + /* no change? */ + if (enable == (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)) + return len; + + if (enable && !ctx->delayed_ndp16) { + ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); + if (!ctx->delayed_ndp16) + return -ENOMEM; + } + + /* flush pending data before changing flag */ + netif_tx_lock_bh(dev->net); + usbnet_start_xmit(NULL, dev->net); + spin_lock_bh(&ctx->mtx); + if (enable) + ctx->drvflags |= CDC_NCM_FLAG_NDP_TO_END; + else + ctx->drvflags &= ~CDC_NCM_FLAG_NDP_TO_END; + spin_unlock_bh(&ctx->mtx); + netif_tx_unlock_bh(dev->net); + + return len; +} +static DEVICE_ATTR_RW(ndp_to_end); + #define NCM_PARM_ATTR(name, format, tocpu) \ static ssize_t cdc_ncm_show_##name(struct device *d, struct device_attribute *attr, char *buf) \ { \ @@ -305,6 +347,7 @@ NCM_PARM_ATTR(wNtbOutMaxDatagrams, "%u", le16_to_cpu); static struct attribute *cdc_ncm_sysfs_attrs[] = { &dev_attr_min_tx_pkt.attr, + &dev_attr_ndp_to_end.attr, &dev_attr_rx_max.attr, &dev_attr_tx_max.attr, &dev_attr_tx_timer_usecs.attr, -- cgit v1.2.3-59-g8ed1b From 7bf9ae016efc0cf08263fbee5ac708c23b90792e Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 7 Dec 2015 04:38:58 +0100 Subject: PHY: DP83867: Remove looking in parent device for OF properties Device tree properties for a phy device are expected to be in the phy node. The current code for the DP83867 also tries to look in the parent node. The devices binding documentation does not mention this, no current device tree file makes use of this, and it is not behaviour we want. So remove looking in the parent device. Signed-off-by: Andrew Lunn Acked-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/dp83867.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 32f10662f4ac..4ebf601073d9 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -107,10 +107,7 @@ static int dp83867_of_init(struct phy_device *phydev) struct device_node *of_node = dev->of_node; int ret; - if (!of_node && dev->parent->of_node) - of_node = dev->parent->of_node; - - if (!phydev->dev.of_node) + if (!of_node) return -ENODEV; ret = of_property_read_u32(of_node, "ti,rx-internal-delay", -- cgit v1.2.3-59-g8ed1b From a42b2af3cfcfa43307f4d56e6aeba154161c7f05 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 25 Nov 2015 22:15:32 +0200 Subject: iwlwifi: mvm: don't keep an mvm ref when the interface is down There is no reason to keep a reference when the interface is down, since we are not really doing anything. The reference is only needed when the mac80211 start op (or a hw restart) is running, to prevent going into runtime or system supend in the meantime. This will allow us to support runtime PM when the interface is down (in another patch). Signed-off-by: Luca Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 22 +++++++++++----------- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index a90f1ee4e571..518a1d518ed4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1004,10 +1004,18 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm) lockdep_assert_held(&mvm->mutex); - /* Clean up some internal and mac80211 state on restart */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + /* Clean up some internal and mac80211 state on restart */ iwl_mvm_restart_cleanup(mvm); - + } else { + /* Hold the reference to prevent runtime suspend while + * the start procedure runs. It's a bit confusing + * that the UCODE_DOWN reference is taken, but it just + * means "UCODE is not UP yet". ( TODO: rename this + * reference). + */ + iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); + } ret = iwl_mvm_up(mvm); if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { @@ -1110,14 +1118,6 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) */ memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats)); - /* - * Disallow low power states when the FW is down by taking - * the UCODE_DOWN ref. in case of ongoing hw restart the - * ref is already taken, so don't take it again. - */ - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); - /* async_handlers_wk is now blocked */ /* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 3b0d597f5033..b71c85b244a0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -607,8 +607,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); - /* rpm starts with a taken ref. only set the appropriate bit here. */ - mvm->refs[IWL_MVM_REF_UCODE_DOWN] = 1; + /* rpm starts with a taken reference, we can release it now */ + iwl_trans_unref(mvm->trans); iwl_mvm_tof_init(mvm); -- cgit v1.2.3-59-g8ed1b From b45ceb406e4fd3045180b8d70bff60b1d43c7ff4 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Mon, 7 Dec 2015 10:30:32 +0530 Subject: net: thunderx: nicvf_queues: nivc_*_intr: remove duplication The same switch-case repeates for nivc_*_intr functions. In this patch it is moved to a helper nicvf_int_type_to_mask(). By the way: - Unneeded write to NICVF register dropped if int_type is unknown. - netdev_dbg() is used instead of netdev_err(). Signed-off-by: Yury Norov Signed-off-by: Aleksey Makarov Acked-by: Vadim Lomovtsev Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 140 ++++++--------------- 1 file changed, 40 insertions(+), 100 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 206b6a71a545..99a29d0b5918 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -1234,153 +1234,93 @@ struct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic, struct cqe_rx_t *cqe_rx) return skb; } -/* Enable interrupt */ -void nicvf_enable_intr(struct nicvf *nic, int int_type, int q_idx) +static u64 nicvf_int_type_to_mask(int int_type, int q_idx) { u64 reg_val; - reg_val = nicvf_reg_read(nic, NIC_VF_ENA_W1S); - switch (int_type) { case NICVF_INTR_CQ: - reg_val |= ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT); + reg_val = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT); break; case NICVF_INTR_SQ: - reg_val |= ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT); + reg_val = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT); break; case NICVF_INTR_RBDR: - reg_val |= ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT); + reg_val = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT); break; case NICVF_INTR_PKT_DROP: - reg_val |= (1ULL << NICVF_INTR_PKT_DROP_SHIFT); + reg_val = (1ULL << NICVF_INTR_PKT_DROP_SHIFT); break; case NICVF_INTR_TCP_TIMER: - reg_val |= (1ULL << NICVF_INTR_TCP_TIMER_SHIFT); + reg_val = (1ULL << NICVF_INTR_TCP_TIMER_SHIFT); break; case NICVF_INTR_MBOX: - reg_val |= (1ULL << NICVF_INTR_MBOX_SHIFT); + reg_val = (1ULL << NICVF_INTR_MBOX_SHIFT); break; case NICVF_INTR_QS_ERR: - reg_val |= (1ULL << NICVF_INTR_QS_ERR_SHIFT); + reg_val = (1ULL << NICVF_INTR_QS_ERR_SHIFT); break; default: - netdev_err(nic->netdev, - "Failed to enable interrupt: unknown type\n"); - break; + reg_val = 0; } - nicvf_reg_write(nic, NIC_VF_ENA_W1S, reg_val); + return reg_val; +} + +/* Enable interrupt */ +void nicvf_enable_intr(struct nicvf *nic, int int_type, int q_idx) +{ + u64 mask = nicvf_int_type_to_mask(int_type, q_idx); + + if (!mask) { + netdev_dbg(nic->netdev, + "Failed to enable interrupt: unknown type\n"); + return; + } + nicvf_reg_write(nic, NIC_VF_ENA_W1S, + nicvf_reg_read(nic, NIC_VF_ENA_W1S) | mask); } /* Disable interrupt */ void nicvf_disable_intr(struct nicvf *nic, int int_type, int q_idx) { - u64 reg_val = 0; + u64 mask = nicvf_int_type_to_mask(int_type, q_idx); - switch (int_type) { - case NICVF_INTR_CQ: - reg_val |= ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT); - break; - case NICVF_INTR_SQ: - reg_val |= ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT); - break; - case NICVF_INTR_RBDR: - reg_val |= ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT); - break; - case NICVF_INTR_PKT_DROP: - reg_val |= (1ULL << NICVF_INTR_PKT_DROP_SHIFT); - break; - case NICVF_INTR_TCP_TIMER: - reg_val |= (1ULL << NICVF_INTR_TCP_TIMER_SHIFT); - break; - case NICVF_INTR_MBOX: - reg_val |= (1ULL << NICVF_INTR_MBOX_SHIFT); - break; - case NICVF_INTR_QS_ERR: - reg_val |= (1ULL << NICVF_INTR_QS_ERR_SHIFT); - break; - default: - netdev_err(nic->netdev, + if (!mask) { + netdev_dbg(nic->netdev, "Failed to disable interrupt: unknown type\n"); - break; + return; } - nicvf_reg_write(nic, NIC_VF_ENA_W1C, reg_val); + nicvf_reg_write(nic, NIC_VF_ENA_W1C, mask); } /* Clear interrupt */ void nicvf_clear_intr(struct nicvf *nic, int int_type, int q_idx) { - u64 reg_val = 0; + u64 mask = nicvf_int_type_to_mask(int_type, q_idx); - switch (int_type) { - case NICVF_INTR_CQ: - reg_val = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT); - break; - case NICVF_INTR_SQ: - reg_val = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT); - break; - case NICVF_INTR_RBDR: - reg_val = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT); - break; - case NICVF_INTR_PKT_DROP: - reg_val = (1ULL << NICVF_INTR_PKT_DROP_SHIFT); - break; - case NICVF_INTR_TCP_TIMER: - reg_val = (1ULL << NICVF_INTR_TCP_TIMER_SHIFT); - break; - case NICVF_INTR_MBOX: - reg_val = (1ULL << NICVF_INTR_MBOX_SHIFT); - break; - case NICVF_INTR_QS_ERR: - reg_val |= (1ULL << NICVF_INTR_QS_ERR_SHIFT); - break; - default: - netdev_err(nic->netdev, + if (!mask) { + netdev_dbg(nic->netdev, "Failed to clear interrupt: unknown type\n"); - break; + return; } - nicvf_reg_write(nic, NIC_VF_INT, reg_val); + nicvf_reg_write(nic, NIC_VF_INT, mask); } /* Check if interrupt is enabled */ int nicvf_is_intr_enabled(struct nicvf *nic, int int_type, int q_idx) { - u64 reg_val; - u64 mask = 0xff; - - reg_val = nicvf_reg_read(nic, NIC_VF_ENA_W1S); - - switch (int_type) { - case NICVF_INTR_CQ: - mask = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT); - break; - case NICVF_INTR_SQ: - mask = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT); - break; - case NICVF_INTR_RBDR: - mask = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT); - break; - case NICVF_INTR_PKT_DROP: - mask = NICVF_INTR_PKT_DROP_MASK; - break; - case NICVF_INTR_TCP_TIMER: - mask = NICVF_INTR_TCP_TIMER_MASK; - break; - case NICVF_INTR_MBOX: - mask = NICVF_INTR_MBOX_MASK; - break; - case NICVF_INTR_QS_ERR: - mask = NICVF_INTR_QS_ERR_MASK; - break; - default: - netdev_err(nic->netdev, + u64 mask = nicvf_int_type_to_mask(int_type, q_idx); + /* If interrupt type is unknown, we treat it disabled. */ + if (!mask) { + netdev_dbg(nic->netdev, "Failed to check interrupt enable: unknown type\n"); - break; + return 0; } - return (reg_val & mask); + return mask & nicvf_reg_read(nic, NIC_VF_ENA_W1S); } void nicvf_update_rq_stats(struct nicvf *nic, int rq_idx) -- cgit v1.2.3-59-g8ed1b From 668dda06d48fc16a5b40e6a32057bd18589e3f95 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 7 Dec 2015 10:30:33 +0530 Subject: net, thunderx: Remove unnecessary rcv buffer start address management Since we have moved on to using allocated pages to carve receive buffers instead of netdev_alloc_skb() there is no need to store any pointers for later retrieval. Earlier we had to store skb and skb->data pointers which later are used to handover received packet to network stack. This will avoid an unnecessary cache miss as well. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 49 ++++------------------ drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 10 +---- 2 files changed, 11 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 99a29d0b5918..1fbd9084333e 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -18,14 +18,6 @@ #include "q_struct.h" #include "nicvf_queues.h" -struct rbuf_info { - struct page *page; - void *data; - u64 offset; -}; - -#define GET_RBUF_INFO(x) ((struct rbuf_info *)(x - NICVF_RCV_BUF_ALIGN_BYTES)) - /* Poll a register for a specific value */ static int nicvf_poll_reg(struct nicvf *nic, int qidx, u64 reg, int bit_pos, int bits, int val) @@ -86,8 +78,6 @@ static void nicvf_free_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem) static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, gfp_t gfp, u32 buf_len, u64 **rbuf) { - u64 data; - struct rbuf_info *rinfo; int order = get_order(buf_len); /* Check if request can be accomodated in previous allocated page */ @@ -113,46 +103,28 @@ static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, gfp_t gfp, nic->rb_page_offset = 0; } - data = (u64)page_address(nic->rb_page) + nic->rb_page_offset; - - /* Align buffer addr to cache line i.e 128 bytes */ - rinfo = (struct rbuf_info *)(data + NICVF_RCV_BUF_ALIGN_LEN(data)); - /* Save page address for reference updation */ - rinfo->page = nic->rb_page; - /* Store start address for later retrieval */ - rinfo->data = (void *)data; - /* Store alignment offset */ - rinfo->offset = NICVF_RCV_BUF_ALIGN_LEN(data); + *rbuf = (u64 *)((u64)page_address(nic->rb_page) + nic->rb_page_offset); - data += rinfo->offset; - - /* Give next aligned address to hw for DMA */ - *rbuf = (u64 *)(data + NICVF_RCV_BUF_ALIGN_BYTES); return 0; } -/* Retrieve actual buffer start address and build skb for received packet */ +/* Build skb around receive buffer */ static struct sk_buff *nicvf_rb_ptr_to_skb(struct nicvf *nic, u64 rb_ptr, int len) { + void *data; struct sk_buff *skb; - struct rbuf_info *rinfo; - rb_ptr = (u64)phys_to_virt(rb_ptr); - /* Get buffer start address and alignment offset */ - rinfo = GET_RBUF_INFO(rb_ptr); + data = phys_to_virt(rb_ptr); /* Now build an skb to give to stack */ - skb = build_skb(rinfo->data, RCV_FRAG_LEN); + skb = build_skb(data, RCV_FRAG_LEN); if (!skb) { - put_page(rinfo->page); + put_page(virt_to_page(data)); return NULL; } - /* Set correct skb->data */ - skb_reserve(skb, rinfo->offset + NICVF_RCV_BUF_ALIGN_BYTES); - - prefetch((void *)rb_ptr); + prefetch(skb->data); return skb; } @@ -196,7 +168,6 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr) int head, tail; u64 buf_addr; struct rbdr_entry_t *desc; - struct rbuf_info *rinfo; if (!rbdr) return; @@ -212,16 +183,14 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr) while (head != tail) { desc = GET_RBDR_DESC(rbdr, head); buf_addr = desc->buf_addr << NICVF_RCV_BUF_ALIGN; - rinfo = GET_RBUF_INFO((u64)phys_to_virt(buf_addr)); - put_page(rinfo->page); + put_page(virt_to_page(phys_to_virt(buf_addr))); head++; head &= (rbdr->dmem.q_len - 1); } /* Free SKB of tail desc */ desc = GET_RBDR_DESC(rbdr, tail); buf_addr = desc->buf_addr << NICVF_RCV_BUF_ALIGN; - rinfo = GET_RBUF_INFO((u64)phys_to_virt(buf_addr)); - put_page(rinfo->page); + put_page(virt_to_page(phys_to_virt(buf_addr))); /* Free RBDR ring */ nicvf_free_q_desc_mem(nic, &rbdr->dmem); diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index 033e8306e91c..a4f6667fdbd4 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -83,10 +83,8 @@ #define MAX_RCV_BUF_COUNT (1ULL << (RBDR_SIZE6 + 13)) #define RBDR_THRESH (RCV_BUF_COUNT / 2) #define DMA_BUFFER_LEN 2048 /* In multiples of 128bytes */ -#define RCV_FRAG_LEN (SKB_DATA_ALIGN(DMA_BUFFER_LEN + NET_SKB_PAD) + \ - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + \ - (NICVF_RCV_BUF_ALIGN_BYTES * 2)) -#define RCV_DATA_OFFSET NICVF_RCV_BUF_ALIGN_BYTES +#define RCV_FRAG_LEN (SKB_DATA_ALIGN(DMA_BUFFER_LEN + NET_SKB_PAD) + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) #define MAX_CQES_FOR_TX ((SND_QUEUE_LEN / MIN_SQ_DESC_PER_PKT_XMIT) * \ MAX_CQE_PER_PKT_XMIT) @@ -108,10 +106,6 @@ #define NICVF_SQ_BASE_ALIGN_BYTES 128 /* 7 bits */ #define NICVF_ALIGNED_ADDR(ADDR, ALIGN_BYTES) ALIGN(ADDR, ALIGN_BYTES) -#define NICVF_ADDR_ALIGN_LEN(ADDR, BYTES)\ - (NICVF_ALIGNED_ADDR(ADDR, BYTES) - BYTES) -#define NICVF_RCV_BUF_ALIGN_LEN(X)\ - (NICVF_ALIGNED_ADDR(X, NICVF_RCV_BUF_ALIGN_BYTES) - X) /* Queue enable/disable */ #define NICVF_SQ_EN BIT_ULL(19) -- cgit v1.2.3-59-g8ed1b From e72c932d3f8a6b56ec9ebad6312504b2e675440a Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Mon, 7 Dec 2015 14:11:33 +0100 Subject: cxgb3: Convert simple_strtoul to kstrtox the simple_strtoul function is obsolete. This patch replace it by kstrtox. Signed-off-by: LABBE Corentin Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c | 14 +++++++------ drivers/net/ethernet/chelsio/cxgb3/t3_hw.c | 28 ++++++++++++++++++------- 2 files changed, 29 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 8f7aa53a4c4b..60908eab3b3a 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -701,15 +701,16 @@ static ssize_t attr_store(struct device *d, ssize_t(*set) (struct net_device *, unsigned int), unsigned int min_val, unsigned int max_val) { - char *endp; ssize_t ret; unsigned int val; if (!capable(CAP_NET_ADMIN)) return -EPERM; - val = simple_strtoul(buf, &endp, 0); - if (endp == buf || val < min_val || val > max_val) + ret = kstrtouint(buf, 0, &val); + if (ret) + return ret; + if (val < min_val || val > max_val) return -EINVAL; rtnl_lock(); @@ -829,14 +830,15 @@ static ssize_t tm_attr_store(struct device *d, struct port_info *pi = netdev_priv(to_net_dev(d)); struct adapter *adap = pi->adapter; unsigned int val; - char *endp; ssize_t ret; if (!capable(CAP_NET_ADMIN)) return -EPERM; - val = simple_strtoul(buf, &endp, 0); - if (endp == buf || val > 10000000) + ret = kstrtouint(buf, 0, &val); + if (ret) + return ret; + if (val > 10000000) return -EINVAL; rtnl_lock(); diff --git a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c index a22768c94200..ee04caa6c4d8 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c @@ -709,11 +709,21 @@ static int get_vpd_params(struct adapter *adapter, struct vpd_params *p) return ret; } - p->cclk = simple_strtoul(vpd.cclk_data, NULL, 10); - p->mclk = simple_strtoul(vpd.mclk_data, NULL, 10); - p->uclk = simple_strtoul(vpd.uclk_data, NULL, 10); - p->mdc = simple_strtoul(vpd.mdc_data, NULL, 10); - p->mem_timing = simple_strtoul(vpd.mt_data, NULL, 10); + ret = kstrtouint(vpd.cclk_data, 10, &p->cclk); + if (ret) + return ret; + ret = kstrtouint(vpd.mclk_data, 10, &p->mclk); + if (ret) + return ret; + ret = kstrtouint(vpd.uclk_data, 10, &p->uclk); + if (ret) + return ret; + ret = kstrtouint(vpd.mdc_data, 10, &p->mdc); + if (ret) + return ret; + ret = kstrtouint(vpd.mt_data, 10, &p->mem_timing); + if (ret) + return ret; memcpy(p->sn, vpd.sn_data, SERNUM_LEN); /* Old eeproms didn't have port information */ @@ -723,8 +733,12 @@ static int get_vpd_params(struct adapter *adapter, struct vpd_params *p) } else { p->port_type[0] = hex_to_bin(vpd.port0_data[0]); p->port_type[1] = hex_to_bin(vpd.port1_data[0]); - p->xauicfg[0] = simple_strtoul(vpd.xaui0cfg_data, NULL, 16); - p->xauicfg[1] = simple_strtoul(vpd.xaui1cfg_data, NULL, 16); + ret = kstrtou16(vpd.xaui0cfg_data, 16, &p->xauicfg[0]); + if (ret) + return ret; + ret = kstrtou16(vpd.xaui1cfg_data, 16, &p->xauicfg[1]); + if (ret) + return ret; } ret = hex2bin(p->eth_base, vpd.na_data, 6); -- cgit v1.2.3-59-g8ed1b From 81ec3c09ab1bb24e5edf2bd08707982a4e7fb8bd Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Mon, 30 Nov 2015 22:29:39 +0530 Subject: ath10k: remove unnecessary amsdu/ampdu assignment in debugfs The default values of max_num_amsdu / max_num_amdpu is assigned a default value as part of 'ath10k_core_init_firmware_features' Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 39fe4f3350aa..2bdf5408b0d9 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1139,7 +1139,7 @@ static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file, { struct ath10k *ar = file->private_data; char buf[64]; - u8 amsdu = 3, ampdu = 64; + u8 amsdu, ampdu; unsigned int len; mutex_lock(&ar->conf_mutex); -- cgit v1.2.3-59-g8ed1b From b057886524be060021e3cfad0ba8458c850330cd Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 30 Nov 2015 19:32:01 +0100 Subject: ath10k: do not use coherent memory for allocated device memory chunks Coherent memory is more expensive to allocate (and constrained on some architectures where it has to be pre-allocated). It is also completely unnecessary, since the host has no reason to even access these allocated memory spaces Signed-off-by: Felix Fietkau Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 61 ++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 2a44d63a03cd..a7c3d299639b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -4312,34 +4312,58 @@ void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb) ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); } -static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, - u32 num_units, u32 unit_len) +static int ath10k_wmi_alloc_chunk(struct ath10k *ar, u32 req_id, + u32 num_units, u32 unit_len) { dma_addr_t paddr; - u32 pool_size; + u32 pool_size = 0; int idx = ar->wmi.num_mem_chunks; + void *vaddr = NULL; - pool_size = num_units * round_up(unit_len, 4); + if (ar->wmi.num_mem_chunks == ARRAY_SIZE(ar->wmi.mem_chunks)) + return -ENOMEM; - if (!pool_size) - return -EINVAL; + while (!vaddr && num_units) { + pool_size = num_units * round_up(unit_len, 4); + if (!pool_size) + return -EINVAL; - ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev, - pool_size, - &paddr, - GFP_KERNEL); - if (!ar->wmi.mem_chunks[idx].vaddr) { - ath10k_warn(ar, "failed to allocate memory chunk\n"); - return -ENOMEM; + vaddr = kzalloc(pool_size, GFP_KERNEL | __GFP_NOWARN); + if (!vaddr) + num_units /= 2; } - memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size); + if (!num_units) + return -ENOMEM; + + paddr = dma_map_single(ar->dev, vaddr, pool_size, DMA_TO_DEVICE); + if (dma_mapping_error(ar->dev, paddr)) { + kfree(vaddr); + return -ENOMEM; + } + ar->wmi.mem_chunks[idx].vaddr = vaddr; ar->wmi.mem_chunks[idx].paddr = paddr; ar->wmi.mem_chunks[idx].len = pool_size; ar->wmi.mem_chunks[idx].req_id = req_id; ar->wmi.num_mem_chunks++; + return num_units; +} + +static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, + u32 num_units, u32 unit_len) +{ + int ret; + + while (num_units) { + ret = ath10k_wmi_alloc_chunk(ar, req_id, num_units, unit_len); + if (ret < 0) + return ret; + + num_units -= ret; + } + return 0; } @@ -7717,10 +7741,11 @@ void ath10k_wmi_free_host_mem(struct ath10k *ar) /* free the host memory chunks requested by firmware */ for (i = 0; i < ar->wmi.num_mem_chunks; i++) { - dma_free_coherent(ar->dev, - ar->wmi.mem_chunks[i].len, - ar->wmi.mem_chunks[i].vaddr, - ar->wmi.mem_chunks[i].paddr); + dma_unmap_single(ar->dev, + ar->wmi.mem_chunks[i].paddr, + ar->wmi.mem_chunks[i].len, + DMA_TO_DEVICE); + kfree(ar->wmi.mem_chunks[i].vaddr); } ar->wmi.num_mem_chunks = 0; -- cgit v1.2.3-59-g8ed1b From 6fa658fd5ab26a769ca7df7bfdd53c212a0ba852 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Sat, 31 Oct 2015 13:57:32 +0100 Subject: ath9k: Simplify and fix eeprom endianness swapping The three eeprom implementations had quite some duplicate code when it came to endianness swapping. Additionally there was a bug in eeprom_4k and eeprom_9287 which prevented the endianness swapping from working correctly, because the swapping code was guarded within an "if (!ath9k_hw_use_flash(ah))". In eeprom_def this check did not exist, so it seems that eeprom_def was the only implementation where endianness swapping worked. This patch takes the duplicate code and moves it from eeprom_* to eeprom.c. The new code is derived from eeprom_def, while taking into account the specifics from the other implementations. Signed-off-by: Martin Blumenstingl Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/eeprom.c | 74 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/eeprom.h | 3 ++ drivers/net/wireless/ath/ath9k/eeprom_4k.c | 76 ++++++---------------------- drivers/net/wireless/ath/ath9k/eeprom_9287.c | 68 ++++++------------------- drivers/net/wireless/ath/ath9k/eeprom_def.c | 61 ++++++---------------- 5 files changed, 123 insertions(+), 159 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index cc81482c934d..f8c5065e5f5f 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -138,6 +138,80 @@ bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) return ret; } +int ath9k_hw_nvram_swap_data(struct ath_hw *ah, bool *swap_needed, int size) +{ + u16 magic; + u16 *eepdata; + int i; + struct ath_common *common = ath9k_hw_common(ah); + + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { + ath_err(common, "Reading Magic # failed\n"); + return -EIO; + } + + if (magic == AR5416_EEPROM_MAGIC) { + *swap_needed = false; + } else if (swab16(magic) == AR5416_EEPROM_MAGIC) { + if (ah->ah_flags & AH_NO_EEP_SWAP) { + ath_info(common, + "Ignoring endianness difference in EEPROM magic bytes.\n"); + + *swap_needed = false; + } else { + *swap_needed = true; + } + } else { + ath_err(common, + "Invalid EEPROM Magic (0x%04x).\n", magic); + return -EINVAL; + } + + eepdata = (u16 *)(&ah->eeprom); + + if (*swap_needed) { + ath_dbg(common, EEPROM, + "EEPROM Endianness is not native.. Changing.\n"); + + for (i = 0; i < size; i++) + eepdata[i] = swab16(eepdata[i]); + } + + return 0; +} + +bool ath9k_hw_nvram_validate_checksum(struct ath_hw *ah, int size) +{ + u32 i, sum = 0; + u16 *eepdata = (u16 *)(&ah->eeprom); + struct ath_common *common = ath9k_hw_common(ah); + + for (i = 0; i < size; i++) + sum ^= eepdata[i]; + + if (sum != 0xffff) { + ath_err(common, "Bad EEPROM checksum 0x%x\n", sum); + return false; + } + + return true; +} + +bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev) +{ + struct ath_common *common = ath9k_hw_common(ah); + + if (ah->eep_ops->get_eeprom_ver(ah) != version || + ah->eep_ops->get_eeprom_rev(ah) < minrev) { + ath_err(common, "Bad EEPROM VER 0x%04x or REV 0x%04x\n", + ah->eep_ops->get_eeprom_ver(ah), + ah->eep_ops->get_eeprom_rev(ah)); + return -EINVAL; + } + + return true; +} + void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, u8 *pVpdList, u16 numIntercepts, u8 *pRetVpdList) diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index 40d4f62d0f16..4465c6566f20 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -664,6 +664,9 @@ int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, u16 *indexL, u16 *indexR); bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data); +int ath9k_hw_nvram_swap_data(struct ath_hw *ah, bool *swap_needed, int size); +bool ath9k_hw_nvram_validate_checksum(struct ath_hw *ah, int size); +bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev); void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, int eep_start_loc, int size); void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 4773da6dc6f2..5da0826bf1be 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -177,74 +177,30 @@ static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, } #endif - -#undef SIZE_EEPROM_4K - static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) { -#define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) - struct ath_common *common = ath9k_hw_common(ah); struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; - u16 *eepdata, temp, magic, magic2; - u32 sum = 0, el; - bool need_swap = false; - int i, addr; + u32 el; + bool need_swap; + int i, err; - - if (!ath9k_hw_use_flash(ah)) { - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, - &magic)) { - ath_err(common, "Reading Magic # failed\n"); - return false; - } - - ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic); - - if (magic != AR5416_EEPROM_MAGIC) { - magic2 = swab16(magic); - - if (magic2 == AR5416_EEPROM_MAGIC) { - need_swap = true; - eepdata = (u16 *) (&ah->eeprom); - - for (addr = 0; addr < EEPROM_4K_SIZE; addr++) { - temp = swab16(*eepdata); - *eepdata = temp; - eepdata++; - } - } else { - ath_err(common, - "Invalid EEPROM Magic. Endianness mismatch.\n"); - return -EINVAL; - } - } - } - - ath_dbg(common, EEPROM, "need_swap = %s\n", - need_swap ? "True" : "False"); + err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_4K); + if (err) + return err; if (need_swap) - el = swab16(ah->eeprom.map4k.baseEepHeader.length); - else - el = ah->eeprom.map4k.baseEepHeader.length; - - if (el > sizeof(struct ar5416_eeprom_4k)) - el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16); + el = swab16(eep->baseEepHeader.length); else - el = el / sizeof(u16); + el = eep->baseEepHeader.length; - eepdata = (u16 *)(&ah->eeprom); - - for (i = 0; i < el; i++) - sum ^= *eepdata++; + el = min(el / sizeof(u16), SIZE_EEPROM_4K); + if (!ath9k_hw_nvram_validate_checksum(ah, el)) + return -EINVAL; if (need_swap) { u32 integer; u16 word; - ath_dbg(common, EEPROM, - "EEPROM Endianness is not native.. Changing\n"); - word = swab16(eep->baseEepHeader.length); eep->baseEepHeader.length = word; @@ -283,17 +239,15 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) } } - if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || - ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", - sum, ah->eep_ops->get_eeprom_ver(ah)); + if (!ath9k_hw_nvram_check_version(ah, AR5416_EEP_VER, + AR5416_EEP_NO_BACK_VER)) return -EINVAL; - } return 0; -#undef EEPROM_4K_SIZE } +#undef SIZE_EEPROM_4K + static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah, enum eeprom_param param) { diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index 6ca33dfde1fd..1a019a39eda1 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -177,59 +177,24 @@ static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah) { - u32 sum = 0, el, integer; - u16 temp, word, magic, magic2, *eepdata; - int i, addr; - bool need_swap = false; + u32 el, integer; + u16 word; + int i, err; + bool need_swap; struct ar9287_eeprom *eep = &ah->eeprom.map9287; - struct ath_common *common = ath9k_hw_common(ah); - - if (!ath9k_hw_use_flash(ah)) { - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, - &magic)) { - ath_err(common, "Reading Magic # failed\n"); - return false; - } - - ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic); - - if (magic != AR5416_EEPROM_MAGIC) { - magic2 = swab16(magic); - - if (magic2 == AR5416_EEPROM_MAGIC) { - need_swap = true; - eepdata = (u16 *)(&ah->eeprom); - - for (addr = 0; addr < SIZE_EEPROM_AR9287; addr++) { - temp = swab16(*eepdata); - *eepdata = temp; - eepdata++; - } - } else { - ath_err(common, - "Invalid EEPROM Magic. Endianness mismatch.\n"); - return -EINVAL; - } - } - } - ath_dbg(common, EEPROM, "need_swap = %s\n", - need_swap ? "True" : "False"); + err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_AR9287); + if (err) + return err; if (need_swap) - el = swab16(ah->eeprom.map9287.baseEepHeader.length); - else - el = ah->eeprom.map9287.baseEepHeader.length; - - if (el > sizeof(struct ar9287_eeprom)) - el = sizeof(struct ar9287_eeprom) / sizeof(u16); + el = swab16(eep->baseEepHeader.length); else - el = el / sizeof(u16); - - eepdata = (u16 *)(&ah->eeprom); + el = eep->baseEepHeader.length; - for (i = 0; i < el; i++) - sum ^= *eepdata++; + el = min(el / sizeof(u16), SIZE_EEPROM_AR9287); + if (!ath9k_hw_nvram_validate_checksum(ah, el)) + return -EINVAL; if (need_swap) { word = swab16(eep->baseEepHeader.length); @@ -270,16 +235,15 @@ static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah) } } - if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR9287_EEP_VER - || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", - sum, ah->eep_ops->get_eeprom_ver(ah)); + if (!ath9k_hw_nvram_check_version(ah, AR9287_EEP_VER, + AR5416_EEP_NO_BACK_VER)) return -EINVAL; - } return 0; } +#undef SIZE_EEPROM_AR9287 + static u32 ath9k_hw_ar9287_get_eeprom(struct ath_hw *ah, enum eeprom_param param) { diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 056f516bf017..959682f7909c 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -126,8 +126,6 @@ static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah) return __ath9k_hw_def_fill_eeprom(ah); } -#undef SIZE_EEPROM_DEF - #if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS) static u32 ath9k_def_dump_modal_eeprom(char *buf, u32 len, u32 size, struct modal_eep_header *modal_hdr) @@ -257,59 +255,31 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, } #endif - static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) { struct ar5416_eeprom_def *eep = &ah->eeprom.def; struct ath_common *common = ath9k_hw_common(ah); - u16 *eepdata, temp, magic; - u32 sum = 0, el; - bool need_swap = false; - int i, addr, size; - - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { - ath_err(common, "Reading Magic # failed\n"); - return false; - } - - if (swab16(magic) == AR5416_EEPROM_MAGIC && - !(ah->ah_flags & AH_NO_EEP_SWAP)) { - size = sizeof(struct ar5416_eeprom_def); - need_swap = true; - eepdata = (u16 *) (&ah->eeprom); - - for (addr = 0; addr < size / sizeof(u16); addr++) { - temp = swab16(*eepdata); - *eepdata = temp; - eepdata++; - } - } + u32 el; + bool need_swap; + int i, err; - ath_dbg(common, EEPROM, "need_swap = %s\n", - need_swap ? "True" : "False"); + err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_DEF); + if (err) + return err; if (need_swap) - el = swab16(ah->eeprom.def.baseEepHeader.length); + el = swab16(eep->baseEepHeader.length); else - el = ah->eeprom.def.baseEepHeader.length; + el = eep->baseEepHeader.length; - if (el > sizeof(struct ar5416_eeprom_def)) - el = sizeof(struct ar5416_eeprom_def) / sizeof(u16); - else - el = el / sizeof(u16); - - eepdata = (u16 *)(&ah->eeprom); - - for (i = 0; i < el; i++) - sum ^= *eepdata++; + el = min(el / sizeof(u16), SIZE_EEPROM_DEF); + if (!ath9k_hw_nvram_validate_checksum(ah, el)) + return -EINVAL; if (need_swap) { u32 integer, j; u16 word; - ath_dbg(common, EEPROM, - "EEPROM Endianness is not native.. Changing.\n"); - word = swab16(eep->baseEepHeader.length); eep->baseEepHeader.length = word; @@ -356,12 +326,9 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) } } - if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || - ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", - sum, ah->eep_ops->get_eeprom_ver(ah)); + if (!ath9k_hw_nvram_check_version(ah, AR5416_EEP_VER, + AR5416_EEP_NO_BACK_VER)) return -EINVAL; - } /* Enable fixup for AR_AN_TOP2 if necessary */ if ((ah->hw_version.devid == AR9280_DEVID_PCI) && @@ -376,6 +343,8 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) return 0; } +#undef SIZE_EEPROM_DEF + static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, enum eeprom_param param) { -- cgit v1.2.3-59-g8ed1b From 79fd1709b64991c86e5ffef0689b4635874e6504 Mon Sep 17 00:00:00 2001 From: Steve deRosier Date: Wed, 18 Nov 2015 14:51:28 -0800 Subject: ath6kl: Don't print error message when recv is canceled An error message ath6kl_htc_rxmsg_pending_handler isn't appropate for when the error is ECANCELED. This could be the result of a perfectly appropriate RX cancel due to shutdown or suspend. This allows the right cleanup to continue, but without an alarming error message in this particular case. Signed-off-by: Steve deRosier Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc_mbox.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/htc_mbox.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c index fffb65b3e652..65c31da43c47 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_mbox.c +++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c @@ -2222,8 +2222,9 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, } if (status) { - ath6kl_err("failed to get pending recv messages: %d\n", - status); + if (status != -ECANCELED) + ath6kl_err("failed to get pending recv messages: %d\n", + status); /* cleanup any packets in sync completion queue */ list_for_each_entry_safe(packets, tmp_pkt, &comp_pktq, list) { -- cgit v1.2.3-59-g8ed1b From 410d13a6b057f69c58b8bf8ccca8e9cbf57ea710 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Thu, 19 Nov 2015 10:04:48 -0500 Subject: ath5k: fix RTS/CTS by using proper rate flags The rates in the tx control rateset do not have the protection flags applied, so RTS/CTS would never get enabled if requested. Fix by using the rate flags in the rates returned by ieee80211_get_tx_rates(). Signed-off-by: Bob Copeland Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath5k/base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 342563a3706f..3d946d8b2db2 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -767,7 +767,7 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, if (info->flags & IEEE80211_TX_CTL_NO_ACK) flags |= AR5K_TXDESC_NOACK; - rc_flags = info->control.rates[0].flags; + rc_flags = bf->rates[0].flags; hw_rate = ath5k_get_rate_hw_value(ah->hw, info, bf, 0); -- cgit v1.2.3-59-g8ed1b From 452133a717dd13b57a99defb791d25c568483f6b Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Tue, 24 Nov 2015 09:30:15 +0200 Subject: wil6210: prevent external wmi commands during suspend flow In __wmi_send we check if fw is ready at the beginning of the function. While we wait for the completion of the previous command, system suspend can be invoked and reset the HW, causing __wmi_send to read from HW registers while it is not ready. Taking the wmi_mutex in the reset flow when setting the FW ready bit to zero will prevent the above race condition. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 4 +++- drivers/net/wireless/ath/wil6210/wmi.c | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 48687f128dc6..09b4daebab9d 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -781,8 +781,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); wil_bcast_fini(wil); - /* prevent NAPI from being scheduled */ + /* prevent NAPI from being scheduled and prevent wmi commands */ + mutex_lock(&wil->wmi_mutex); bitmap_zero(wil->status, wil_status_last); + mutex_unlock(&wil->wmi_mutex); if (wil->scan_request) { wil_dbg_misc(wil, "Abort scan_request 0x%p\n", diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 6ed26baca0e5..e3ea74cdd4aa 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -228,6 +228,10 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); /* wait till FW finish with previous command */ for (retry = 5; retry > 0; retry--) { + if (!test_bit(wil_status_fwready, wil->status)) { + wil_err(wil, "WMI: cannot send command while FW not ready\n"); + return -EAGAIN; + } r->tail = wil_r(wil, RGF_MBOX + offsetof(struct wil6210_mbox_ctl, tx.tail)); if (next_head != r->tail) -- cgit v1.2.3-59-g8ed1b From 58bb9ca84c2013615069b94940846b274fc8e2c7 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:06 +0100 Subject: ath9k: add debug messages to aggr/chanctx funcs Add/extend debug messages when chanctx used. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 11 ++++++++--- drivers/net/wireless/ath/ath9k/hw.c | 8 ++++---- drivers/net/wireless/ath/ath9k/xmit.c | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 90f5773a1a61..35802c9d2d63 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -403,7 +403,7 @@ static void ath_chanctx_offchannel_noa(struct ath_softc *sc, avp->offchannel_duration = sc->sched.offchannel_duration; ath_dbg(common, CHAN_CTX, - "offchannel noa_duration: %d, noa_start: %d, noa_index: %d\n", + "offchannel noa_duration: %d, noa_start: %u, noa_index: %d\n", avp->offchannel_duration, avp->offchannel_start, avp->noa_index); @@ -443,7 +443,7 @@ static void ath_chanctx_set_periodic_noa(struct ath_softc *sc, avp->periodic_noa = true; ath_dbg(common, CHAN_CTX, - "noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n", + "noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n", avp->noa_duration, avp->noa_start, avp->noa_index, @@ -464,7 +464,7 @@ static void ath_chanctx_set_oneshot_noa(struct ath_softc *sc, avp->noa_duration = duration + sc->sched.channel_switch_time; ath_dbg(common, CHAN_CTX, - "oneshot noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n", + "oneshot noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n", avp->noa_duration, avp->noa_start, avp->noa_index, @@ -1401,6 +1401,7 @@ void ath9k_chanctx_wake_queues(struct ath_softc *sc, struct ath_chanctx *ctx) static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; s32 tsf, target_tsf; @@ -1418,6 +1419,10 @@ static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME) target_tsf = tsf + ATH_P2P_PS_STOP_TIME; + ath_dbg(common, CHAN_CTX, "%s absent %d tsf 0x%08X next_tsf 0x%08X (%dms)\n", + __func__, avp->noa.absent, tsf, target_tsf, + (target_tsf - tsf) / 1000); + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000); } diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 41382f89abe1..2e252af0b26a 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2299,10 +2299,10 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, else nextTbtt = bs->bs_nexttbtt; - ath_dbg(common, BEACON, "next DTIM %d\n", bs->bs_nextdtim); - ath_dbg(common, BEACON, "next beacon %d\n", nextTbtt); - ath_dbg(common, BEACON, "beacon period %d\n", beaconintval); - ath_dbg(common, BEACON, "DTIM period %d\n", dtimperiod); + ath_dbg(common, BEACON, "next DTIM %u\n", bs->bs_nextdtim); + ath_dbg(common, BEACON, "next beacon %u\n", nextTbtt); + ath_dbg(common, BEACON, "beacon period %u\n", beaconintval); + ath_dbg(common, BEACON, "DTIM period %u\n", dtimperiod); ENABLE_REGWRITE_BUFFER(ah); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 3e3dac3d7060..26698a6fbd1c 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1473,11 +1473,14 @@ static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_tid *txtid; struct ath_txq *txq; struct ath_node *an; u8 density; + ath_dbg(common, XMIT, "%s called\n", __func__); + an = (struct ath_node *)sta->drv_priv; txtid = ATH_AN_2_TID(an, tid); txq = txtid->txq; @@ -1512,10 +1515,13 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_node *an = (struct ath_node *)sta->drv_priv; struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid); struct ath_txq *txq = txtid->txq; + ath_dbg(common, XMIT, "%s called\n", __func__); + ath_txq_lock(sc, txq); txtid->active = false; ath_tx_flush_tid(sc, txtid); @@ -1526,11 +1532,14 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, struct ath_node *an) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_tid *tid; struct ath_txq *txq; bool buffered; int tidno; + ath_dbg(common, XMIT, "%s called\n", __func__); + for (tidno = 0, tid = &an->tid[tidno]; tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { @@ -1555,10 +1564,13 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_tid *tid; struct ath_txq *txq; int tidno; + ath_dbg(common, XMIT, "%s called\n", __func__); + for (tidno = 0, tid = &an->tid[tidno]; tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { @@ -1579,10 +1591,13 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tidno) { + struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_atx_tid *tid; struct ath_node *an; struct ath_txq *txq; + ath_dbg(common, XMIT, "%s called\n", __func__); + an = (struct ath_node *)sta->drv_priv; tid = ATH_AN_2_TID(an, tidno); txq = tid->txq; -- cgit v1.2.3-59-g8ed1b From 2f985539b98b01ddcb06c214f3c6b81f40e083e4 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:07 +0100 Subject: ath9k: print real timer value In case of low HZ before this patch we saw wrong values in debug message. Print real timeout value. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 35802c9d2d63..dddaaeac9433 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -356,14 +356,16 @@ static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; + unsigned long timeout; ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000); tsf_time -= ath9k_hw_gettsf32(ah); - tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1; - mod_timer(&sc->sched.timer, jiffies + tsf_time); + timeout = msecs_to_jiffies(tsf_time / 1000) + 1; + mod_timer(&sc->sched.timer, jiffies + timeout); ath_dbg(common, CHAN_CTX, - "Setup chanctx timer with timeout: %d ms\n", jiffies_to_msecs(tsf_time)); + "Setup chanctx timer with timeout: %d (%d) ms\n", + tsf_time / 1000, jiffies_to_msecs(timeout)); } static void ath_chanctx_handle_bmiss(struct ath_softc *sc, -- cgit v1.2.3-59-g8ed1b From 60337ed86306c1f75d2f6744c6ff53dfd39eacc0 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:08 +0100 Subject: ath9k: queue null frames in case of MCC While mac80211 using null frames when connection polling, we should queue this frames while NOA could be there, and AP, P2P_GO could be not present. Without this patch, with no traffic we often saw disconnections while we try to send nullfunc when AP/GO wasn't present. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/xmit.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 26698a6fbd1c..82fc76f5b2a9 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2331,6 +2331,12 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, queue = ieee80211_is_data_present(hdr->frame_control); + /* If chanctx, queue all null frames while NOA could be there */ + if (ath9k_is_chanctx_enabled() && + ieee80211_is_nullfunc(hdr->frame_control) && + !txctl->force_channel) + queue = true; + /* Force queueing of all frames that belong to a virtual interface on * a different channel context, to ensure that they are sent on the * correct channel. -- cgit v1.2.3-59-g8ed1b From b77b59ae8acd854d3b18ea3cbd85646922083b0b Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:09 +0100 Subject: ath9k: P2P_CLIENT, send frames after 1ms AP/GO will aprear AP/GO will aprear after NOA, wait 1ms to be sure AP could receive/answer this frames. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index dddaaeac9433..5640e88c6569 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1417,6 +1417,8 @@ static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) target_tsf = avp->noa.next_tsf; if (!avp->noa.absent) target_tsf -= ATH_P2P_PS_STOP_TIME; + else + target_tsf += ATH_P2P_PS_STOP_TIME; if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME) target_tsf = tsf + ATH_P2P_PS_STOP_TIME; @@ -1543,6 +1545,8 @@ void ath9k_p2p_ps_timer(void *priv) tsf = ath9k_hw_gettsf32(sc->sc_ah); if (!avp->noa.absent) tsf += ATH_P2P_PS_STOP_TIME; + else + tsf -= ATH_P2P_PS_STOP_TIME; if (!avp->noa.has_next_tsf || avp->noa.next_tsf - tsf > BIT(31)) -- cgit v1.2.3-59-g8ed1b From 631c45f41957032056568b57b185c214dcebbe84 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:10 +0100 Subject: ath9k: use u32 when calculate tsf Use u32 while ath9k_hw_gettsf32() and ath9k_hw_gen_timer_start() require u32. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 5640e88c6569..28bbbef7cabd 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1405,7 +1405,7 @@ static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; - s32 tsf, target_tsf; + u32 tsf, target_tsf; if (!avp || !avp->noa.has_next_tsf) return; @@ -1427,7 +1427,7 @@ static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) __func__, avp->noa.absent, tsf, target_tsf, (target_tsf - tsf) / 1000); - ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000); + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, target_tsf, 1000000); } static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) -- cgit v1.2.3-59-g8ed1b From c1b7bea038f00e710e738d8a8a76dc87ceb013f8 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:11 +0100 Subject: ath9k: setup correct skb priority for nullfunc After queue nullfunc for MCC case, we hit WARN_ON in xmit.c:2398 while skb priority wasn't set. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 28bbbef7cabd..2afb8dabf39a 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1101,6 +1101,7 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp, nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + skb->priority = 7; skb_set_queue_mapping(skb, IEEE80211_AC_VO); if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) { dev_kfree_skb_any(skb); -- cgit v1.2.3-59-g8ed1b From 3edbf0ba0494ad4c576763e733ad0da3bed2f621 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:12 +0100 Subject: ath9k: MCC enable Opportunistic Power Save When adding NOA attr enable Opportunistic Power Save. Before we calculate ctwindow but didn't enable oppps. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 2afb8dabf39a..31b4d0efcf6b 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1505,6 +1505,8 @@ void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp, noa->index = avp->noa_index; noa->oppps_ctwindow = ath9k_get_ctwin(sc, avp); + if (noa->oppps_ctwindow) + noa->oppps_ctwindow |= BIT(7); if (avp->noa_duration) { if (avp->periodic_noa) { -- cgit v1.2.3-59-g8ed1b From b10b7fb31a217084de0cf834d0e72c24d9710015 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:13 +0100 Subject: ath9k: P2P_CLIENT, get/set NOA correctly In case we get BSS_CHANGED_P2P_PS early, from mac80211, we didn't set NOA timer correctly, while p2p_ps_vif was NULL. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/channel.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 31b4d0efcf6b..5a1ede67b070 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1443,6 +1443,10 @@ static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) return; sc->p2p_ps_vif = avp; + + if (sc->ps_flags & PS_BEACON_SYNC) + return; + tsf = ath9k_hw_gettsf32(sc->sc_ah); ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf); ath9k_update_p2p_ps_timer(sc, avp); @@ -1585,8 +1589,7 @@ void ath9k_p2p_bss_info_changed(struct ath_softc *sc, spin_lock_bh(&sc->sc_pcu_lock); spin_lock_irqsave(&sc->sc_pm_lock, flags); - if (!(sc->ps_flags & PS_BEACON_SYNC)) - ath9k_update_p2p_ps(sc, vif); + ath9k_update_p2p_ps(sc, vif); spin_unlock_irqrestore(&sc->sc_pm_lock, flags); spin_unlock_bh(&sc->sc_pcu_lock); } -- cgit v1.2.3-59-g8ed1b From a64d876ef7dc33a7e3c52b06db2c2a55ff8ac785 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:15 +0100 Subject: ath9k: MCC, add NOA also in case of an AP In case of MCC and AP interface, add also NOA attr that will inform stations about absence of an AP. There is a chance that some stations will handle this NOA attr correctly and will know exactly when AP is present/absent. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/beacon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index f50a6bc5d06e..5cf0cd7cb2d1 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -148,7 +148,8 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, ath_assign_seq(common, skb); - if (vif->p2p) + /* Always assign NOA attr when MCC enabled */ + if (ath9k_is_chanctx_enabled()) ath9k_beacon_add_noa(sc, avp, skb); bf->bf_buf_addr = dma_map_single(sc->dev, skb->data, -- cgit v1.2.3-59-g8ed1b From 02edab5b5f82fcb35696b2606e840a3ec87d7954 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:17 +0100 Subject: ath9k: MCC, print time elapsed between events This is useful for MCC debugging and bug fixing. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/channel.c | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index b42f4a963ef4..acc21f60f36b 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -981,6 +981,7 @@ struct ath_softc { struct ath_offchannel offchannel; struct ath_chanctx *next_chan; struct completion go_beacon; + struct timespec last_event_time; #endif unsigned long driver_data; diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 5a1ede67b070..50e614b915f1 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -226,6 +226,20 @@ static const char *chanctx_state_string(enum ath_chanctx_state state) } } +static const u32 chanctx_event_delta(struct ath_softc *sc) +{ + u64 ms; + struct timespec ts, *old; + + getrawmonotonic(&ts); + old = &sc->last_event_time; + ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + ms -= old->tv_sec * 1000 + old->tv_nsec / 1000000; + sc->last_event_time = ts; + + return (u32)ms; +} + void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -489,10 +503,11 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, spin_lock_bh(&sc->chan_lock); - ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s\n", + ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s, delta: %u ms\n", sc->cur_chan->chandef.center_freq1, chanctx_event_string(ev), - chanctx_state_string(sc->sched.state)); + chanctx_state_string(sc->sched.state), + chanctx_event_delta(sc)); switch (ev) { case ATH_CHANCTX_EVENT_BEACON_PREPARE: -- cgit v1.2.3-59-g8ed1b From 05a85a6c42b59cd97d49314ad3d1939039eb9aee Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 27 Nov 2015 09:37:18 +0100 Subject: ath9k: remove ath9k_mod_tsf64_tu Remove ath9k_mod_tsf64_tu() function while we could use div_u64_rem() function. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/common-beacon.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/common-beacon.c b/drivers/net/wireless/ath/ath9k/common-beacon.c index 6ad44470d0f2..01d6d3205a65 100644 --- a/drivers/net/wireless/ath/ath9k/common-beacon.c +++ b/drivers/net/wireless/ath/ath9k/common-beacon.c @@ -18,30 +18,16 @@ #define FUDGE 2 -/* Calculate the modulo of a 64 bit TSF snapshot with a TU divisor */ -static u32 ath9k_mod_tsf64_tu(u64 tsf, u32 div_tu) -{ - u32 tsf_mod, tsf_hi, tsf_lo, mod_hi, mod_lo; - - tsf_mod = tsf & (BIT(10) - 1); - tsf_hi = tsf >> 32; - tsf_lo = ((u32) tsf) >> 10; - - mod_hi = tsf_hi % div_tu; - mod_lo = ((mod_hi << 22) + tsf_lo) % div_tu; - - return (mod_lo << 10) | tsf_mod; -} - static u32 ath9k_get_next_tbtt(struct ath_hw *ah, u64 tsf, unsigned int interval) { - unsigned int offset; + unsigned int offset, divisor; tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time); - offset = ath9k_mod_tsf64_tu(tsf, interval); + divisor = TU_TO_USEC(interval); + div_u64_rem(tsf, divisor, &offset); - return (u32) tsf + TU_TO_USEC(interval) - offset; + return (u32) tsf + divisor - offset; } /* -- cgit v1.2.3-59-g8ed1b From 3cdb73091767649ffd706a5caa3e75a411176f29 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 8 Dec 2015 21:18:25 +0100 Subject: drivers: net: xgene: constify xgene_mac_ops and xgene_port_ops structures The xgene_mac_ops and xgene_port_ops structures are never modified, so declare them as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 4 ++-- drivers/net/ethernet/apm/xgene/xgene_enet_hw.h | 4 ++-- drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 8 ++++---- drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 4 ++-- drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c | 4 ++-- drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h | 4 ++-- drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 4 ++-- drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index c31e691d11fc..db55c9f6e8e1 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -869,7 +869,7 @@ void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata) pdata->mdio_bus = NULL; } -struct xgene_mac_ops xgene_gmac_ops = { +const struct xgene_mac_ops xgene_gmac_ops = { .init = xgene_gmac_init, .reset = xgene_gmac_reset, .rx_enable = xgene_gmac_rx_enable, @@ -879,7 +879,7 @@ struct xgene_mac_ops xgene_gmac_ops = { .set_mac_addr = xgene_gmac_set_mac_addr, }; -struct xgene_port_ops xgene_gport_ops = { +const struct xgene_port_ops xgene_gport_ops = { .reset = xgene_enet_reset, .cle_bypass = xgene_enet_cle_bypass, .shutdown = xgene_gport_shutdown, diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h index c153a1dc5ff7..8a9091039ab4 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h @@ -340,8 +340,8 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata); void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata); bool xgene_ring_mgr_init(struct xgene_enet_pdata *p); -extern struct xgene_mac_ops xgene_gmac_ops; -extern struct xgene_port_ops xgene_gport_ops; +extern const struct xgene_mac_ops xgene_gmac_ops; +extern const struct xgene_port_ops xgene_gport_ops; extern struct xgene_ring_ops xgene_ring1_ops; #endif /* __XGENE_ENET_HW_H__ */ diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index d21ee8767c2d..2394191ad28e 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -682,7 +682,7 @@ static void xgene_enet_napi_disable(struct xgene_enet_pdata *pdata) static int xgene_enet_open(struct net_device *ndev) { struct xgene_enet_pdata *pdata = netdev_priv(ndev); - struct xgene_mac_ops *mac_ops = pdata->mac_ops; + const struct xgene_mac_ops *mac_ops = pdata->mac_ops; int ret; mac_ops->tx_enable(pdata); @@ -706,7 +706,7 @@ static int xgene_enet_open(struct net_device *ndev) static int xgene_enet_close(struct net_device *ndev) { struct xgene_enet_pdata *pdata = netdev_priv(ndev); - struct xgene_mac_ops *mac_ops = pdata->mac_ops; + const struct xgene_mac_ops *mac_ops = pdata->mac_ops; netif_stop_queue(ndev); @@ -1416,7 +1416,7 @@ static int xgene_enet_probe(struct platform_device *pdev) struct net_device *ndev; struct xgene_enet_pdata *pdata; struct device *dev = &pdev->dev; - struct xgene_mac_ops *mac_ops; + const struct xgene_mac_ops *mac_ops; const struct of_device_id *of_id; int ret; @@ -1503,7 +1503,7 @@ err: static int xgene_enet_remove(struct platform_device *pdev) { struct xgene_enet_pdata *pdata; - struct xgene_mac_ops *mac_ops; + const struct xgene_mac_ops *mac_ops; struct net_device *ndev; pdata = platform_get_drvdata(pdev); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index a6e56b88c0a0..054caf055f0a 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -174,8 +174,8 @@ struct xgene_enet_pdata { int phy_mode; enum xgene_enet_rm rm; struct rtnl_link_stats64 stats; - struct xgene_mac_ops *mac_ops; - struct xgene_port_ops *port_ops; + const struct xgene_mac_ops *mac_ops; + const struct xgene_port_ops *port_ops; struct xgene_ring_ops *ring_ops; struct delayed_work link_work; u32 port_id; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c index 05b817e56fde..78475512b683 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c @@ -405,7 +405,7 @@ static void xgene_enet_link_state(struct work_struct *work) schedule_delayed_work(&p->link_work, poll_interval); } -struct xgene_mac_ops xgene_sgmac_ops = { +const struct xgene_mac_ops xgene_sgmac_ops = { .init = xgene_sgmac_init, .reset = xgene_sgmac_reset, .rx_enable = xgene_sgmac_rx_enable, @@ -416,7 +416,7 @@ struct xgene_mac_ops xgene_sgmac_ops = { .link_state = xgene_enet_link_state }; -struct xgene_port_ops xgene_sgport_ops = { +const struct xgene_port_ops xgene_sgport_ops = { .reset = xgene_enet_reset, .cle_bypass = xgene_enet_cle_bypass, .shutdown = xgene_enet_shutdown diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h index de432465009c..29a71b4dcc44 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h @@ -35,7 +35,7 @@ #define MPA_IDLE_WITH_QMI_EMPTY BIT(12) #define SG_RX_DV_GATE_REG_0_ADDR 0x0dfc -extern struct xgene_mac_ops xgene_sgmac_ops; -extern struct xgene_port_ops xgene_sgport_ops; +extern const struct xgene_mac_ops xgene_sgmac_ops; +extern const struct xgene_port_ops xgene_sgport_ops; #endif /* __XGENE_ENET_SGMAC_H__ */ diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c index 7a28a48cb2c7..ba030dc1940b 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c @@ -326,7 +326,7 @@ static void xgene_enet_link_state(struct work_struct *work) schedule_delayed_work(&pdata->link_work, poll_interval); } -struct xgene_mac_ops xgene_xgmac_ops = { +const struct xgene_mac_ops xgene_xgmac_ops = { .init = xgene_xgmac_init, .reset = xgene_xgmac_reset, .rx_enable = xgene_xgmac_rx_enable, @@ -338,7 +338,7 @@ struct xgene_mac_ops xgene_xgmac_ops = { .link_state = xgene_enet_link_state }; -struct xgene_port_ops xgene_xgport_ops = { +const struct xgene_port_ops xgene_xgport_ops = { .reset = xgene_enet_reset, .cle_bypass = xgene_enet_xgcle_bypass, .shutdown = xgene_enet_shutdown, diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h index f8f908dbf51c..0a2dca8a1725 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h @@ -69,7 +69,7 @@ #define XG_ENET_SPARE_CFG_REG_1_ADDR 0x0410 #define XGENET_RX_DV_GATE_REG_0_ADDR 0x0804 -extern struct xgene_mac_ops xgene_xgmac_ops; -extern struct xgene_port_ops xgene_xgport_ops; +extern const struct xgene_mac_ops xgene_xgmac_ops; +extern const struct xgene_port_ops xgene_xgport_ops; #endif /* __XGENE_ENET_XGMAC_H__ */ -- cgit v1.2.3-59-g8ed1b From 3242e2b46d297e48846022694502d9023c22f6c1 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 8 Dec 2015 00:40:19 +0300 Subject: sh_eth: remove mask fields from 'struct bb_info' The MDIO control bits are always mapped to the same bits of the same register (PIR), so there's no need to store their masks in the 'struct bb_info'... Signed-off-by: Sergei Shtylyov Acked-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 2406ad4f880e..a2fb45aa776e 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1013,10 +1013,6 @@ struct bb_info { void (*set_gate)(void *addr); struct mdiobb_ctrl ctrl; void *addr; - u32 mmd_msk;/* MMD */ - u32 mdo_msk; - u32 mdi_msk; - u32 mdc_msk; }; /* PHY bit set */ @@ -1046,9 +1042,9 @@ static void sh_mmd_ctrl(struct mdiobb_ctrl *ctrl, int bit) bitbang->set_gate(bitbang->addr); if (bit) - bb_set(bitbang->addr, bitbang->mmd_msk); + bb_set(bitbang->addr, PIR_MMD); else - bb_clr(bitbang->addr, bitbang->mmd_msk); + bb_clr(bitbang->addr, PIR_MMD); } /* Set bit data*/ @@ -1060,9 +1056,9 @@ static void sh_set_mdio(struct mdiobb_ctrl *ctrl, int bit) bitbang->set_gate(bitbang->addr); if (bit) - bb_set(bitbang->addr, bitbang->mdo_msk); + bb_set(bitbang->addr, PIR_MDO); else - bb_clr(bitbang->addr, bitbang->mdo_msk); + bb_clr(bitbang->addr, PIR_MDO); } /* Get bit data*/ @@ -1073,7 +1069,7 @@ static int sh_get_mdio(struct mdiobb_ctrl *ctrl) if (bitbang->set_gate) bitbang->set_gate(bitbang->addr); - return bb_read(bitbang->addr, bitbang->mdi_msk); + return bb_read(bitbang->addr, PIR_MDI); } /* MDC pin control */ @@ -1085,9 +1081,9 @@ static void sh_mdc_ctrl(struct mdiobb_ctrl *ctrl, int bit) bitbang->set_gate(bitbang->addr); if (bit) - bb_set(bitbang->addr, bitbang->mdc_msk); + bb_set(bitbang->addr, PIR_MDC); else - bb_clr(bitbang->addr, bitbang->mdc_msk); + bb_clr(bitbang->addr, PIR_MDC); } /* mdio bus control struct */ @@ -2899,10 +2895,6 @@ static int sh_mdio_init(struct sh_eth_private *mdp, /* bitbang init */ bitbang->addr = mdp->addr + mdp->reg_offset[PIR]; bitbang->set_gate = pd->set_mdio_gate; - bitbang->mdi_msk = PIR_MDI; - bitbang->mdo_msk = PIR_MDO; - bitbang->mmd_msk = PIR_MMD; - bitbang->mdc_msk = PIR_MDC; bitbang->ctrl.ops = &bb_ops; /* MII controller setting */ -- cgit v1.2.3-59-g8ed1b From 39b4b06b8b20ae011373bae5f9552cce17c47f64 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 8 Dec 2015 00:40:57 +0300 Subject: sh_eth: factor out common code from MDIO bitbang methods sh_mm[cd]_ctrl() and sh_set_mdio() all look mostly the same -- factor out their common code and put it into sh_mdio_ctrl(). Signed-off-by: Sergei Shtylyov Acked-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index a2fb45aa776e..f0c7108f84d1 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1033,32 +1033,29 @@ static int bb_read(void *addr, u32 msk) return (ioread32(addr) & msk) != 0; } -/* Data I/O pin control */ -static void sh_mmd_ctrl(struct mdiobb_ctrl *ctrl, int bit) +static void sh_mdio_ctrl(struct mdiobb_ctrl *ctrl, u32 mask, int set) { struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); if (bitbang->set_gate) bitbang->set_gate(bitbang->addr); - if (bit) - bb_set(bitbang->addr, PIR_MMD); + if (set) + bb_set(bitbang->addr, mask); else - bb_clr(bitbang->addr, PIR_MMD); + bb_clr(bitbang->addr, mask); +} + +/* Data I/O pin control */ +static void sh_mmd_ctrl(struct mdiobb_ctrl *ctrl, int bit) +{ + sh_mdio_ctrl(ctrl, PIR_MMD, bit); } /* Set bit data*/ static void sh_set_mdio(struct mdiobb_ctrl *ctrl, int bit) { - struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); - - if (bitbang->set_gate) - bitbang->set_gate(bitbang->addr); - - if (bit) - bb_set(bitbang->addr, PIR_MDO); - else - bb_clr(bitbang->addr, PIR_MDO); + sh_mdio_ctrl(ctrl, PIR_MDO, bit); } /* Get bit data*/ @@ -1075,15 +1072,7 @@ static int sh_get_mdio(struct mdiobb_ctrl *ctrl) /* MDC pin control */ static void sh_mdc_ctrl(struct mdiobb_ctrl *ctrl, int bit) { - struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); - - if (bitbang->set_gate) - bitbang->set_gate(bitbang->addr); - - if (bit) - bb_set(bitbang->addr, PIR_MDC); - else - bb_clr(bitbang->addr, PIR_MDC); + sh_mdio_ctrl(ctrl, PIR_MDC, bit); } /* mdio bus control struct */ -- cgit v1.2.3-59-g8ed1b From 78fa3c5c52e63281415b73ee83a05ee4194498b1 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 8 Dec 2015 00:41:43 +0300 Subject: sh_eth: get rid of bb_{set|clr|read}() After the MDIO bitbang code consolidation, there's no need anymore for bb_{set|clr}() as well as bb_read() -- just expand them inline, thus saving more LoCs... Signed-off-by: Sergei Shtylyov Acked-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index f0c7108f84d1..67cd24312c11 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1015,35 +1015,20 @@ struct bb_info { void *addr; }; -/* PHY bit set */ -static void bb_set(void *addr, u32 msk) -{ - iowrite32(ioread32(addr) | msk, addr); -} - -/* PHY bit clear */ -static void bb_clr(void *addr, u32 msk) -{ - iowrite32((ioread32(addr) & ~msk), addr); -} - -/* PHY bit read */ -static int bb_read(void *addr, u32 msk) -{ - return (ioread32(addr) & msk) != 0; -} - static void sh_mdio_ctrl(struct mdiobb_ctrl *ctrl, u32 mask, int set) { struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); + u32 pir; if (bitbang->set_gate) bitbang->set_gate(bitbang->addr); + pir = ioread32(bitbang->addr); if (set) - bb_set(bitbang->addr, mask); + pir |= mask; else - bb_clr(bitbang->addr, mask); + pir &= ~mask; + iowrite32(pir, bitbang->addr); } /* Data I/O pin control */ @@ -1066,7 +1051,7 @@ static int sh_get_mdio(struct mdiobb_ctrl *ctrl) if (bitbang->set_gate) bitbang->set_gate(bitbang->addr); - return bb_read(bitbang->addr, PIR_MDI); + return (ioread32(bitbang->addr) & PIR_MDI) != 0; } /* MDC pin control */ -- cgit v1.2.3-59-g8ed1b From 8ac2837c89c8c0fcad557e4380aeef80580390f9 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 9 Dec 2015 10:51:12 +0800 Subject: Revert "Merge branch 'vsock-virtio'" This reverts commit 0d76d6e8b2507983a2cae4c09880798079007421 and merge commit c402293bd76fbc93e52ef8c0947ab81eea3ae019, reversing changes made to c89359a42e2a49656451569c382eed63e781153c. The virtio-vsock device specification is not finalized yet. Michael Tsirkin voiced concerned about merging this code when the hardware interface (and possibly the userspace interface) could still change. Signed-off-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- drivers/vhost/Kconfig | 4 - drivers/vhost/Kconfig.vsock | 7 - drivers/vhost/Makefile | 4 - drivers/vhost/vsock.c | 630 --------------- drivers/vhost/vsock.h | 4 - include/linux/virtio_vsock.h | 209 ----- include/net/af_vsock.h | 2 - include/uapi/linux/virtio_ids.h | 1 - include/uapi/linux/virtio_vsock.h | 89 --- net/vmw_vsock/Kconfig | 18 - net/vmw_vsock/Makefile | 2 - net/vmw_vsock/af_vsock.c | 70 -- net/vmw_vsock/virtio_transport.c | 466 ----------- net/vmw_vsock/virtio_transport_common.c | 1272 ------------------------------- 14 files changed, 2778 deletions(-) delete mode 100644 drivers/vhost/Kconfig.vsock delete mode 100644 drivers/vhost/vsock.c delete mode 100644 drivers/vhost/vsock.h delete mode 100644 include/linux/virtio_vsock.h delete mode 100644 include/uapi/linux/virtio_vsock.h delete mode 100644 net/vmw_vsock/virtio_transport.c delete mode 100644 net/vmw_vsock/virtio_transport_common.c (limited to 'drivers') diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig index 81449bfc8d3b..533eaf04f12f 100644 --- a/drivers/vhost/Kconfig +++ b/drivers/vhost/Kconfig @@ -47,7 +47,3 @@ config VHOST_CROSS_ENDIAN_LEGACY adds some overhead, it is disabled by default. If unsure, say "N". - -if STAGING -source "drivers/vhost/Kconfig.vsock" -endif diff --git a/drivers/vhost/Kconfig.vsock b/drivers/vhost/Kconfig.vsock deleted file mode 100644 index 3491865d3eb9..000000000000 --- a/drivers/vhost/Kconfig.vsock +++ /dev/null @@ -1,7 +0,0 @@ -config VHOST_VSOCK - tristate "vhost virtio-vsock driver" - depends on VSOCKETS && EVENTFD - select VIRTIO_VSOCKETS_COMMON - default n - ---help--- - Say M here to enable the vhost-vsock for virtio-vsock guests diff --git a/drivers/vhost/Makefile b/drivers/vhost/Makefile index 6b012b986b57..e0441c34db1c 100644 --- a/drivers/vhost/Makefile +++ b/drivers/vhost/Makefile @@ -4,9 +4,5 @@ vhost_net-y := net.o obj-$(CONFIG_VHOST_SCSI) += vhost_scsi.o vhost_scsi-y := scsi.o -obj-$(CONFIG_VHOST_VSOCK) += vhost_vsock.o -vhost_vsock-y := vsock.o - obj-$(CONFIG_VHOST_RING) += vringh.o - obj-$(CONFIG_VHOST) += vhost.o diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c deleted file mode 100644 index 64bcb10bb901..000000000000 --- a/drivers/vhost/vsock.c +++ /dev/null @@ -1,630 +0,0 @@ -/* - * vhost transport for vsock - * - * Copyright (C) 2013-2015 Red Hat, Inc. - * Author: Asias He - * Stefan Hajnoczi - * - * This work is licensed under the terms of the GNU GPL, version 2. - */ -#include -#include -#include -#include -#include -#include - -#include -#include "vhost.h" -#include "vsock.h" - -#define VHOST_VSOCK_DEFAULT_HOST_CID 2 - -static int vhost_transport_socket_init(struct vsock_sock *vsk, - struct vsock_sock *psk); - -enum { - VHOST_VSOCK_FEATURES = VHOST_FEATURES, -}; - -/* Used to track all the vhost_vsock instances on the system. */ -static LIST_HEAD(vhost_vsock_list); -static DEFINE_MUTEX(vhost_vsock_mutex); - -struct vhost_vsock_virtqueue { - struct vhost_virtqueue vq; -}; - -struct vhost_vsock { - /* Vhost device */ - struct vhost_dev dev; - /* Vhost vsock virtqueue*/ - struct vhost_vsock_virtqueue vqs[VSOCK_VQ_MAX]; - /* Link to global vhost_vsock_list*/ - struct list_head list; - /* Head for pkt from host to guest */ - struct list_head send_pkt_list; - /* Work item to send pkt */ - struct vhost_work send_pkt_work; - /* Wait queue for send pkt */ - wait_queue_head_t queue_wait; - /* Used for global tx buf limitation */ - u32 total_tx_buf; - /* Guest contex id this vhost_vsock instance handles */ - u32 guest_cid; -}; - -static u32 vhost_transport_get_local_cid(void) -{ - return VHOST_VSOCK_DEFAULT_HOST_CID; -} - -static struct vhost_vsock *vhost_vsock_get(u32 guest_cid) -{ - struct vhost_vsock *vsock; - - mutex_lock(&vhost_vsock_mutex); - list_for_each_entry(vsock, &vhost_vsock_list, list) { - if (vsock->guest_cid == guest_cid) { - mutex_unlock(&vhost_vsock_mutex); - return vsock; - } - } - mutex_unlock(&vhost_vsock_mutex); - - return NULL; -} - -static void -vhost_transport_do_send_pkt(struct vhost_vsock *vsock, - struct vhost_virtqueue *vq) -{ - bool added = false; - - mutex_lock(&vq->mutex); - vhost_disable_notify(&vsock->dev, vq); - for (;;) { - struct virtio_vsock_pkt *pkt; - struct iov_iter iov_iter; - unsigned out, in; - struct sock *sk; - size_t nbytes; - size_t len; - int head; - - if (list_empty(&vsock->send_pkt_list)) { - vhost_enable_notify(&vsock->dev, vq); - break; - } - - head = vhost_get_vq_desc(vq, vq->iov, ARRAY_SIZE(vq->iov), - &out, &in, NULL, NULL); - pr_debug("%s: head = %d\n", __func__, head); - if (head < 0) - break; - - if (head == vq->num) { - if (unlikely(vhost_enable_notify(&vsock->dev, vq))) { - vhost_disable_notify(&vsock->dev, vq); - continue; - } - break; - } - - pkt = list_first_entry(&vsock->send_pkt_list, - struct virtio_vsock_pkt, list); - list_del_init(&pkt->list); - - if (out) { - virtio_transport_free_pkt(pkt); - vq_err(vq, "Expected 0 output buffers, got %u\n", out); - break; - } - - len = iov_length(&vq->iov[out], in); - iov_iter_init(&iov_iter, READ, &vq->iov[out], in, len); - - nbytes = copy_to_iter(&pkt->hdr, sizeof(pkt->hdr), &iov_iter); - if (nbytes != sizeof(pkt->hdr)) { - virtio_transport_free_pkt(pkt); - vq_err(vq, "Faulted on copying pkt hdr\n"); - break; - } - - nbytes = copy_to_iter(pkt->buf, pkt->len, &iov_iter); - if (nbytes != pkt->len) { - virtio_transport_free_pkt(pkt); - vq_err(vq, "Faulted on copying pkt buf\n"); - break; - } - - vhost_add_used(vq, head, pkt->len); /* TODO should this be sizeof(pkt->hdr) + pkt->len? */ - added = true; - - virtio_transport_dec_tx_pkt(pkt); - vsock->total_tx_buf -= pkt->len; - - sk = sk_vsock(pkt->trans->vsk); - /* Release refcnt taken in vhost_transport_send_pkt */ - sock_put(sk); - - virtio_transport_free_pkt(pkt); - } - if (added) - vhost_signal(&vsock->dev, vq); - mutex_unlock(&vq->mutex); - - if (added) - wake_up(&vsock->queue_wait); -} - -static void vhost_transport_send_pkt_work(struct vhost_work *work) -{ - struct vhost_virtqueue *vq; - struct vhost_vsock *vsock; - - vsock = container_of(work, struct vhost_vsock, send_pkt_work); - vq = &vsock->vqs[VSOCK_VQ_RX].vq; - - vhost_transport_do_send_pkt(vsock, vq); -} - -static int -vhost_transport_send_pkt(struct vsock_sock *vsk, - struct virtio_vsock_pkt_info *info) -{ - u32 src_cid, src_port, dst_cid, dst_port; - struct virtio_transport *trans; - struct virtio_vsock_pkt *pkt; - struct vhost_virtqueue *vq; - struct vhost_vsock *vsock; - u32 pkt_len = info->pkt_len; - DEFINE_WAIT(wait); - - src_cid = vhost_transport_get_local_cid(); - src_port = vsk->local_addr.svm_port; - if (!info->remote_cid) { - dst_cid = vsk->remote_addr.svm_cid; - dst_port = vsk->remote_addr.svm_port; - } else { - dst_cid = info->remote_cid; - dst_port = info->remote_port; - } - - /* Find the vhost_vsock according to guest context id */ - vsock = vhost_vsock_get(dst_cid); - if (!vsock) - return -ENODEV; - - trans = vsk->trans; - vq = &vsock->vqs[VSOCK_VQ_RX].vq; - - /* we can send less than pkt_len bytes */ - if (pkt_len > VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE) - pkt_len = VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE; - - /* virtio_transport_get_credit might return less than pkt_len credit */ - pkt_len = virtio_transport_get_credit(trans, pkt_len); - - /* Do not send zero length OP_RW pkt*/ - if (pkt_len == 0 && info->op == VIRTIO_VSOCK_OP_RW) - return pkt_len; - - /* Respect global tx buf limitation */ - mutex_lock(&vq->mutex); - while (pkt_len + vsock->total_tx_buf > VIRTIO_VSOCK_MAX_TX_BUF_SIZE) { - prepare_to_wait_exclusive(&vsock->queue_wait, &wait, - TASK_UNINTERRUPTIBLE); - mutex_unlock(&vq->mutex); - schedule(); - mutex_lock(&vq->mutex); - finish_wait(&vsock->queue_wait, &wait); - } - vsock->total_tx_buf += pkt_len; - mutex_unlock(&vq->mutex); - - pkt = virtio_transport_alloc_pkt(vsk, info, pkt_len, - src_cid, src_port, - dst_cid, dst_port); - if (!pkt) { - mutex_lock(&vq->mutex); - vsock->total_tx_buf -= pkt_len; - mutex_unlock(&vq->mutex); - virtio_transport_put_credit(trans, pkt_len); - return -ENOMEM; - } - - pr_debug("%s:info->pkt_len= %d\n", __func__, pkt_len); - /* Released in vhost_transport_do_send_pkt */ - sock_hold(&trans->vsk->sk); - virtio_transport_inc_tx_pkt(pkt); - - /* Queue it up in vhost work */ - mutex_lock(&vq->mutex); - list_add_tail(&pkt->list, &vsock->send_pkt_list); - vhost_work_queue(&vsock->dev, &vsock->send_pkt_work); - mutex_unlock(&vq->mutex); - - return pkt_len; -} - -static struct virtio_transport_pkt_ops vhost_ops = { - .send_pkt = vhost_transport_send_pkt, -}; - -static struct virtio_vsock_pkt * -vhost_vsock_alloc_pkt(struct vhost_virtqueue *vq, - unsigned int out, unsigned int in) -{ - struct virtio_vsock_pkt *pkt; - struct iov_iter iov_iter; - size_t nbytes; - size_t len; - - if (in != 0) { - vq_err(vq, "Expected 0 input buffers, got %u\n", in); - return NULL; - } - - pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); - if (!pkt) - return NULL; - - len = iov_length(vq->iov, out); - iov_iter_init(&iov_iter, WRITE, vq->iov, out, len); - - nbytes = copy_from_iter(&pkt->hdr, sizeof(pkt->hdr), &iov_iter); - if (nbytes != sizeof(pkt->hdr)) { - vq_err(vq, "Expected %zu bytes for pkt->hdr, got %zu bytes\n", - sizeof(pkt->hdr), nbytes); - kfree(pkt); - return NULL; - } - - if (le16_to_cpu(pkt->hdr.type) == VIRTIO_VSOCK_TYPE_DGRAM) - pkt->len = le32_to_cpu(pkt->hdr.len) & 0XFFFF; - else if (le16_to_cpu(pkt->hdr.type) == VIRTIO_VSOCK_TYPE_STREAM) - pkt->len = le32_to_cpu(pkt->hdr.len); - - /* No payload */ - if (!pkt->len) - return pkt; - - /* The pkt is too big */ - if (pkt->len > VIRTIO_VSOCK_MAX_PKT_BUF_SIZE) { - kfree(pkt); - return NULL; - } - - pkt->buf = kmalloc(pkt->len, GFP_KERNEL); - if (!pkt->buf) { - kfree(pkt); - return NULL; - } - - nbytes = copy_from_iter(pkt->buf, pkt->len, &iov_iter); - if (nbytes != pkt->len) { - vq_err(vq, "Expected %u byte payload, got %zu bytes\n", - pkt->len, nbytes); - virtio_transport_free_pkt(pkt); - return NULL; - } - - return pkt; -} - -static void vhost_vsock_handle_ctl_kick(struct vhost_work *work) -{ - struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, - poll.work); - struct vhost_vsock *vsock = container_of(vq->dev, struct vhost_vsock, - dev); - - pr_debug("%s vq=%p, vsock=%p\n", __func__, vq, vsock); -} - -static void vhost_vsock_handle_tx_kick(struct vhost_work *work) -{ - struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, - poll.work); - struct vhost_vsock *vsock = container_of(vq->dev, struct vhost_vsock, - dev); - struct virtio_vsock_pkt *pkt; - int head; - unsigned int out, in; - bool added = false; - u32 len; - - mutex_lock(&vq->mutex); - vhost_disable_notify(&vsock->dev, vq); - for (;;) { - head = vhost_get_vq_desc(vq, vq->iov, ARRAY_SIZE(vq->iov), - &out, &in, NULL, NULL); - if (head < 0) - break; - - if (head == vq->num) { - if (unlikely(vhost_enable_notify(&vsock->dev, vq))) { - vhost_disable_notify(&vsock->dev, vq); - continue; - } - break; - } - - pkt = vhost_vsock_alloc_pkt(vq, out, in); - if (!pkt) { - vq_err(vq, "Faulted on pkt\n"); - continue; - } - - len = pkt->len; - - /* Only accept correctly addressed packets */ - if (le32_to_cpu(pkt->hdr.src_cid) == vsock->guest_cid && - le32_to_cpu(pkt->hdr.dst_cid) == vhost_transport_get_local_cid()) - virtio_transport_recv_pkt(pkt); - else - virtio_transport_free_pkt(pkt); - - vhost_add_used(vq, head, len); - added = true; - } - if (added) - vhost_signal(&vsock->dev, vq); - mutex_unlock(&vq->mutex); -} - -static void vhost_vsock_handle_rx_kick(struct vhost_work *work) -{ - struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, - poll.work); - struct vhost_vsock *vsock = container_of(vq->dev, struct vhost_vsock, - dev); - - vhost_transport_do_send_pkt(vsock, vq); -} - -static int vhost_vsock_dev_open(struct inode *inode, struct file *file) -{ - struct vhost_virtqueue **vqs; - struct vhost_vsock *vsock; - int ret; - - vsock = kzalloc(sizeof(*vsock), GFP_KERNEL); - if (!vsock) - return -ENOMEM; - - pr_debug("%s:vsock=%p\n", __func__, vsock); - - vqs = kmalloc(VSOCK_VQ_MAX * sizeof(*vqs), GFP_KERNEL); - if (!vqs) { - ret = -ENOMEM; - goto out; - } - - vqs[VSOCK_VQ_CTRL] = &vsock->vqs[VSOCK_VQ_CTRL].vq; - vqs[VSOCK_VQ_TX] = &vsock->vqs[VSOCK_VQ_TX].vq; - vqs[VSOCK_VQ_RX] = &vsock->vqs[VSOCK_VQ_RX].vq; - vsock->vqs[VSOCK_VQ_CTRL].vq.handle_kick = vhost_vsock_handle_ctl_kick; - vsock->vqs[VSOCK_VQ_TX].vq.handle_kick = vhost_vsock_handle_tx_kick; - vsock->vqs[VSOCK_VQ_RX].vq.handle_kick = vhost_vsock_handle_rx_kick; - - vhost_dev_init(&vsock->dev, vqs, VSOCK_VQ_MAX); - - file->private_data = vsock; - init_waitqueue_head(&vsock->queue_wait); - INIT_LIST_HEAD(&vsock->send_pkt_list); - vhost_work_init(&vsock->send_pkt_work, vhost_transport_send_pkt_work); - - mutex_lock(&vhost_vsock_mutex); - list_add_tail(&vsock->list, &vhost_vsock_list); - mutex_unlock(&vhost_vsock_mutex); - return 0; - -out: - kfree(vsock); - return ret; -} - -static void vhost_vsock_flush(struct vhost_vsock *vsock) -{ - int i; - - for (i = 0; i < VSOCK_VQ_MAX; i++) - vhost_poll_flush(&vsock->vqs[i].vq.poll); - vhost_work_flush(&vsock->dev, &vsock->send_pkt_work); -} - -static int vhost_vsock_dev_release(struct inode *inode, struct file *file) -{ - struct vhost_vsock *vsock = file->private_data; - - mutex_lock(&vhost_vsock_mutex); - list_del(&vsock->list); - mutex_unlock(&vhost_vsock_mutex); - - vhost_dev_stop(&vsock->dev); - vhost_vsock_flush(vsock); - vhost_dev_cleanup(&vsock->dev, false); - kfree(vsock->dev.vqs); - kfree(vsock); - return 0; -} - -static int vhost_vsock_set_cid(struct vhost_vsock *vsock, u32 guest_cid) -{ - struct vhost_vsock *other; - - /* Refuse reserved CIDs */ - if (guest_cid <= VMADDR_CID_HOST) { - return -EINVAL; - } - - /* Refuse if CID is already in use */ - other = vhost_vsock_get(guest_cid); - if (other && other != vsock) { - return -EADDRINUSE; - } - - mutex_lock(&vhost_vsock_mutex); - vsock->guest_cid = guest_cid; - pr_debug("%s:guest_cid=%d\n", __func__, guest_cid); - mutex_unlock(&vhost_vsock_mutex); - - return 0; -} - -static int vhost_vsock_set_features(struct vhost_vsock *vsock, u64 features) -{ - struct vhost_virtqueue *vq; - int i; - - if (features & ~VHOST_VSOCK_FEATURES) - return -EOPNOTSUPP; - - mutex_lock(&vsock->dev.mutex); - if ((features & (1 << VHOST_F_LOG_ALL)) && - !vhost_log_access_ok(&vsock->dev)) { - mutex_unlock(&vsock->dev.mutex); - return -EFAULT; - } - - for (i = 0; i < VSOCK_VQ_MAX; i++) { - vq = &vsock->vqs[i].vq; - mutex_lock(&vq->mutex); - vq->acked_features = features; - mutex_unlock(&vq->mutex); - } - mutex_unlock(&vsock->dev.mutex); - return 0; -} - -static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl, - unsigned long arg) -{ - struct vhost_vsock *vsock = f->private_data; - void __user *argp = (void __user *)arg; - u64 __user *featurep = argp; - u32 __user *cidp = argp; - u32 guest_cid; - u64 features; - int r; - - switch (ioctl) { - case VHOST_VSOCK_SET_GUEST_CID: - if (get_user(guest_cid, cidp)) - return -EFAULT; - return vhost_vsock_set_cid(vsock, guest_cid); - case VHOST_GET_FEATURES: - features = VHOST_VSOCK_FEATURES; - if (copy_to_user(featurep, &features, sizeof(features))) - return -EFAULT; - return 0; - case VHOST_SET_FEATURES: - if (copy_from_user(&features, featurep, sizeof(features))) - return -EFAULT; - return vhost_vsock_set_features(vsock, features); - default: - mutex_lock(&vsock->dev.mutex); - r = vhost_dev_ioctl(&vsock->dev, ioctl, argp); - if (r == -ENOIOCTLCMD) - r = vhost_vring_ioctl(&vsock->dev, ioctl, argp); - else - vhost_vsock_flush(vsock); - mutex_unlock(&vsock->dev.mutex); - return r; - } -} - -static const struct file_operations vhost_vsock_fops = { - .owner = THIS_MODULE, - .open = vhost_vsock_dev_open, - .release = vhost_vsock_dev_release, - .llseek = noop_llseek, - .unlocked_ioctl = vhost_vsock_dev_ioctl, -}; - -static struct miscdevice vhost_vsock_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = "vhost-vsock", - .fops = &vhost_vsock_fops, -}; - -static int -vhost_transport_socket_init(struct vsock_sock *vsk, struct vsock_sock *psk) -{ - struct virtio_transport *trans; - int ret; - - ret = virtio_transport_do_socket_init(vsk, psk); - if (ret) - return ret; - - trans = vsk->trans; - trans->ops = &vhost_ops; - - return ret; -} - -static struct vsock_transport vhost_transport = { - .get_local_cid = vhost_transport_get_local_cid, - - .init = vhost_transport_socket_init, - .destruct = virtio_transport_destruct, - .release = virtio_transport_release, - .connect = virtio_transport_connect, - .shutdown = virtio_transport_shutdown, - - .dgram_enqueue = virtio_transport_dgram_enqueue, - .dgram_dequeue = virtio_transport_dgram_dequeue, - .dgram_bind = virtio_transport_dgram_bind, - .dgram_allow = virtio_transport_dgram_allow, - - .stream_enqueue = virtio_transport_stream_enqueue, - .stream_dequeue = virtio_transport_stream_dequeue, - .stream_has_data = virtio_transport_stream_has_data, - .stream_has_space = virtio_transport_stream_has_space, - .stream_rcvhiwat = virtio_transport_stream_rcvhiwat, - .stream_is_active = virtio_transport_stream_is_active, - .stream_allow = virtio_transport_stream_allow, - - .notify_poll_in = virtio_transport_notify_poll_in, - .notify_poll_out = virtio_transport_notify_poll_out, - .notify_recv_init = virtio_transport_notify_recv_init, - .notify_recv_pre_block = virtio_transport_notify_recv_pre_block, - .notify_recv_pre_dequeue = virtio_transport_notify_recv_pre_dequeue, - .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue, - .notify_send_init = virtio_transport_notify_send_init, - .notify_send_pre_block = virtio_transport_notify_send_pre_block, - .notify_send_pre_enqueue = virtio_transport_notify_send_pre_enqueue, - .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue, - - .set_buffer_size = virtio_transport_set_buffer_size, - .set_min_buffer_size = virtio_transport_set_min_buffer_size, - .set_max_buffer_size = virtio_transport_set_max_buffer_size, - .get_buffer_size = virtio_transport_get_buffer_size, - .get_min_buffer_size = virtio_transport_get_min_buffer_size, - .get_max_buffer_size = virtio_transport_get_max_buffer_size, -}; - -static int __init vhost_vsock_init(void) -{ - int ret; - - ret = vsock_core_init(&vhost_transport); - if (ret < 0) - return ret; - return misc_register(&vhost_vsock_misc); -}; - -static void __exit vhost_vsock_exit(void) -{ - misc_deregister(&vhost_vsock_misc); - vsock_core_exit(); -}; - -module_init(vhost_vsock_init); -module_exit(vhost_vsock_exit); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Asias He"); -MODULE_DESCRIPTION("vhost transport for vsock "); diff --git a/drivers/vhost/vsock.h b/drivers/vhost/vsock.h deleted file mode 100644 index 0ddb107b86ca..000000000000 --- a/drivers/vhost/vsock.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef VHOST_VSOCK_H -#define VHOST_VSOCK_H -#define VHOST_VSOCK_SET_GUEST_CID _IOW(VHOST_VIRTIO, 0x60, __u32) -#endif diff --git a/include/linux/virtio_vsock.h b/include/linux/virtio_vsock.h deleted file mode 100644 index a5f3ecc038f7..000000000000 --- a/include/linux/virtio_vsock.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so - * anyone can use the definitions to implement compatible drivers/servers: - * - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of IBM nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Copyright (C) Red Hat, Inc., 2013-2015 - * Copyright (C) Asias He , 2013 - * Copyright (C) Stefan Hajnoczi , 2015 - */ - -#ifndef _LINUX_VIRTIO_VSOCK_H -#define _LINUX_VIRTIO_VSOCK_H - -#include -#include -#include - -#define VIRTIO_VSOCK_DEFAULT_MIN_BUF_SIZE 128 -#define VIRTIO_VSOCK_DEFAULT_BUF_SIZE (1024 * 256) -#define VIRTIO_VSOCK_DEFAULT_MAX_BUF_SIZE (1024 * 256) -#define VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE (1024 * 4) -#define VIRTIO_VSOCK_MAX_BUF_SIZE 0xFFFFFFFFUL -#define VIRTIO_VSOCK_MAX_PKT_BUF_SIZE (1024 * 64) -#define VIRTIO_VSOCK_MAX_TX_BUF_SIZE (1024 * 1024 * 16) -#define VIRTIO_VSOCK_MAX_DGRAM_SIZE (1024 * 64) - -struct vsock_transport_recv_notify_data; -struct vsock_transport_send_notify_data; -struct sockaddr_vm; -struct vsock_sock; - -enum { - VSOCK_VQ_CTRL = 0, - VSOCK_VQ_RX = 1, /* for host to guest data */ - VSOCK_VQ_TX = 2, /* for guest to host data */ - VSOCK_VQ_MAX = 3, -}; - -/* virtio transport socket state */ -struct virtio_transport { - struct virtio_transport_pkt_ops *ops; - struct vsock_sock *vsk; - - u32 buf_size; - u32 buf_size_min; - u32 buf_size_max; - - struct mutex tx_lock; - struct mutex rx_lock; - - struct list_head rx_queue; - u32 rx_bytes; - - /* Protected by trans->tx_lock */ - u32 tx_cnt; - u32 buf_alloc; - u32 peer_fwd_cnt; - u32 peer_buf_alloc; - /* Protected by trans->rx_lock */ - u32 fwd_cnt; - - /* Protected by sk_lock */ - u16 dgram_id; - struct list_head incomplete_dgrams; /* dgram fragments */ -}; - -struct virtio_vsock_pkt { - struct virtio_vsock_hdr hdr; - struct virtio_transport *trans; - struct work_struct work; - struct list_head list; - void *buf; - u32 len; - u32 off; -}; - -struct virtio_vsock_pkt_info { - u32 remote_cid, remote_port; - struct msghdr *msg; - u32 pkt_len; - u16 type; - u16 op; - u32 flags; - u16 dgram_id; - u16 dgram_len; -}; - -struct virtio_transport_pkt_ops { - int (*send_pkt)(struct vsock_sock *vsk, - struct virtio_vsock_pkt_info *info); -}; - -void virtio_vsock_dumppkt(const char *func, - const struct virtio_vsock_pkt *pkt); - -struct sock * -virtio_transport_get_pending(struct sock *listener, - struct virtio_vsock_pkt *pkt); -struct virtio_vsock_pkt * -virtio_transport_alloc_pkt(struct vsock_sock *vsk, - struct virtio_vsock_pkt_info *info, - size_t len, - u32 src_cid, - u32 src_port, - u32 dst_cid, - u32 dst_port); -ssize_t -virtio_transport_stream_dequeue(struct vsock_sock *vsk, - struct msghdr *msg, - size_t len, - int type); -int -virtio_transport_dgram_dequeue(struct vsock_sock *vsk, - struct msghdr *msg, - size_t len, int flags); - -s64 virtio_transport_stream_has_data(struct vsock_sock *vsk); -s64 virtio_transport_stream_has_space(struct vsock_sock *vsk); - -int virtio_transport_do_socket_init(struct vsock_sock *vsk, - struct vsock_sock *psk); -u64 virtio_transport_get_buffer_size(struct vsock_sock *vsk); -u64 virtio_transport_get_min_buffer_size(struct vsock_sock *vsk); -u64 virtio_transport_get_max_buffer_size(struct vsock_sock *vsk); -void virtio_transport_set_buffer_size(struct vsock_sock *vsk, u64 val); -void virtio_transport_set_min_buffer_size(struct vsock_sock *vsk, u64 val); -void virtio_transport_set_max_buffer_size(struct vsock_sock *vs, u64 val); -int -virtio_transport_notify_poll_in(struct vsock_sock *vsk, - size_t target, - bool *data_ready_now); -int -virtio_transport_notify_poll_out(struct vsock_sock *vsk, - size_t target, - bool *space_available_now); - -int virtio_transport_notify_recv_init(struct vsock_sock *vsk, - size_t target, struct vsock_transport_recv_notify_data *data); -int virtio_transport_notify_recv_pre_block(struct vsock_sock *vsk, - size_t target, struct vsock_transport_recv_notify_data *data); -int virtio_transport_notify_recv_pre_dequeue(struct vsock_sock *vsk, - size_t target, struct vsock_transport_recv_notify_data *data); -int virtio_transport_notify_recv_post_dequeue(struct vsock_sock *vsk, - size_t target, ssize_t copied, bool data_read, - struct vsock_transport_recv_notify_data *data); -int virtio_transport_notify_send_init(struct vsock_sock *vsk, - struct vsock_transport_send_notify_data *data); -int virtio_transport_notify_send_pre_block(struct vsock_sock *vsk, - struct vsock_transport_send_notify_data *data); -int virtio_transport_notify_send_pre_enqueue(struct vsock_sock *vsk, - struct vsock_transport_send_notify_data *data); -int virtio_transport_notify_send_post_enqueue(struct vsock_sock *vsk, - ssize_t written, struct vsock_transport_send_notify_data *data); - -u64 virtio_transport_stream_rcvhiwat(struct vsock_sock *vsk); -bool virtio_transport_stream_is_active(struct vsock_sock *vsk); -bool virtio_transport_stream_allow(u32 cid, u32 port); -int virtio_transport_dgram_bind(struct vsock_sock *vsk, - struct sockaddr_vm *addr); -bool virtio_transport_dgram_allow(u32 cid, u32 port); - -int virtio_transport_connect(struct vsock_sock *vsk); - -int virtio_transport_shutdown(struct vsock_sock *vsk, int mode); - -void virtio_transport_release(struct vsock_sock *vsk); - -ssize_t -virtio_transport_stream_enqueue(struct vsock_sock *vsk, - struct msghdr *msg, - size_t len); -int -virtio_transport_dgram_enqueue(struct vsock_sock *vsk, - struct sockaddr_vm *remote_addr, - struct msghdr *msg, - size_t len); - -void virtio_transport_destruct(struct vsock_sock *vsk); - -void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt); -void virtio_transport_free_pkt(struct virtio_vsock_pkt *pkt); -void virtio_transport_inc_tx_pkt(struct virtio_vsock_pkt *pkt); -void virtio_transport_dec_tx_pkt(struct virtio_vsock_pkt *pkt); -u32 virtio_transport_get_credit(struct virtio_transport *trans, u32 wanted); -void virtio_transport_put_credit(struct virtio_transport *trans, u32 credit); -#endif /* _LINUX_VIRTIO_VSOCK_H */ diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h index a0c8fa2ababf..e9eb2d6791b3 100644 --- a/include/net/af_vsock.h +++ b/include/net/af_vsock.h @@ -175,10 +175,8 @@ void vsock_insert_connected(struct vsock_sock *vsk); void vsock_remove_bound(struct vsock_sock *vsk); void vsock_remove_connected(struct vsock_sock *vsk); struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr); -struct sock *vsock_find_unbound_socket(struct sockaddr_vm *addr); struct sock *vsock_find_connected_socket(struct sockaddr_vm *src, struct sockaddr_vm *dst); void vsock_for_each_connected_socket(void (*fn)(struct sock *sk)); -int vsock_bind_dgram_generic(struct vsock_sock *vsk, struct sockaddr_vm *addr); #endif /* __AF_VSOCK_H__ */ diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index 16dcf5d06cd7..77925f587b15 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -39,7 +39,6 @@ #define VIRTIO_ID_9P 9 /* 9p virtio console */ #define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */ #define VIRTIO_ID_CAIF 12 /* Virtio caif */ -#define VIRTIO_ID_VSOCK 13 /* virtio vsock transport */ #define VIRTIO_ID_GPU 16 /* virtio GPU */ #define VIRTIO_ID_INPUT 18 /* virtio input */ diff --git a/include/uapi/linux/virtio_vsock.h b/include/uapi/linux/virtio_vsock.h deleted file mode 100644 index 8cf9b5682628..000000000000 --- a/include/uapi/linux/virtio_vsock.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so - * anyone can use the definitions to implement compatible drivers/servers: - * - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of IBM nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Copyright (C) Red Hat, Inc., 2013-2015 - * Copyright (C) Asias He , 2013 - * Copyright (C) Stefan Hajnoczi , 2015 - */ - -#ifndef _UAPI_LINUX_VIRTIO_VSOCK_H -#define _UAPI_LINUX_VIRTIO_VOSCK_H - -#include -#include -#include - -struct virtio_vsock_config { - __le32 guest_cid; - __le32 max_virtqueue_pairs; -}; - -struct virtio_vsock_hdr { - __le32 src_cid; - __le32 src_port; - __le32 dst_cid; - __le32 dst_port; - __le32 len; - __le16 type; /* enum virtio_vsock_type */ - __le16 op; /* enum virtio_vsock_op */ - __le32 flags; - __le32 buf_alloc; - __le32 fwd_cnt; -}; - -enum virtio_vsock_type { - VIRTIO_VSOCK_TYPE_STREAM = 1, - VIRTIO_VSOCK_TYPE_DGRAM = 2, -}; - -enum virtio_vsock_op { - VIRTIO_VSOCK_OP_INVALID = 0, - - /* Connect operations */ - VIRTIO_VSOCK_OP_REQUEST = 1, - VIRTIO_VSOCK_OP_RESPONSE = 2, - VIRTIO_VSOCK_OP_ACK = 3, - VIRTIO_VSOCK_OP_RST = 4, - VIRTIO_VSOCK_OP_SHUTDOWN = 5, - - /* To send payload */ - VIRTIO_VSOCK_OP_RW = 6, - - /* Tell the peer our credit info */ - VIRTIO_VSOCK_OP_CREDIT_UPDATE = 7, - /* Request the peer to send the credit info to us */ - VIRTIO_VSOCK_OP_CREDIT_REQUEST = 8, -}; - -/* VIRTIO_VSOCK_OP_SHUTDOWN flags values */ -enum virtio_vsock_shutdown { - VIRTIO_VSOCK_SHUTDOWN_RCV = 1, - VIRTIO_VSOCK_SHUTDOWN_SEND = 2, -}; - -#endif /* _UAPI_LINUX_VIRTIO_VSOCK_H */ diff --git a/net/vmw_vsock/Kconfig b/net/vmw_vsock/Kconfig index 74e0bc887a33..14810abedc2e 100644 --- a/net/vmw_vsock/Kconfig +++ b/net/vmw_vsock/Kconfig @@ -26,21 +26,3 @@ config VMWARE_VMCI_VSOCKETS To compile this driver as a module, choose M here: the module will be called vmw_vsock_vmci_transport. If unsure, say N. - -config VIRTIO_VSOCKETS - tristate "virtio transport for Virtual Sockets" - depends on VSOCKETS && VIRTIO - select VIRTIO_VSOCKETS_COMMON - help - This module implements a virtio transport for Virtual Sockets. - - Enable this transport if your Virtual Machine runs on Qemu/KVM. - - To compile this driver as a module, choose M here: the module - will be called virtio_vsock_transport. If unsure, say N. - -config VIRTIO_VSOCKETS_COMMON - tristate - ---help--- - This option is selected by any driver which needs to access - the virtio_vsock. diff --git a/net/vmw_vsock/Makefile b/net/vmw_vsock/Makefile index cf4c29439081..2ce52d70f224 100644 --- a/net/vmw_vsock/Makefile +++ b/net/vmw_vsock/Makefile @@ -1,7 +1,5 @@ obj-$(CONFIG_VSOCKETS) += vsock.o obj-$(CONFIG_VMWARE_VMCI_VSOCKETS) += vmw_vsock_vmci_transport.o -obj-$(CONFIG_VIRTIO_VSOCKETS) += virtio_transport.o -obj-$(CONFIG_VIRTIO_VSOCKETS_COMMON) += virtio_transport_common.o vsock-y += af_vsock.o vsock_addr.o diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 77247a2b670b..7fd1220fbfa0 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -223,17 +223,6 @@ static struct sock *__vsock_find_bound_socket(struct sockaddr_vm *addr) return NULL; } -static struct sock *__vsock_find_unbound_socket(struct sockaddr_vm *addr) -{ - struct vsock_sock *vsk; - - list_for_each_entry(vsk, vsock_unbound_sockets, bound_table) - if (addr->svm_port == vsk->local_addr.svm_port) - return sk_vsock(vsk); - - return NULL; -} - static struct sock *__vsock_find_connected_socket(struct sockaddr_vm *src, struct sockaddr_vm *dst) { @@ -309,21 +298,6 @@ struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr) } EXPORT_SYMBOL_GPL(vsock_find_bound_socket); -struct sock *vsock_find_unbound_socket(struct sockaddr_vm *addr) -{ - struct sock *sk; - - spin_lock_bh(&vsock_table_lock); - sk = __vsock_find_unbound_socket(addr); - if (sk) - sock_hold(sk); - - spin_unlock_bh(&vsock_table_lock); - - return sk; -} -EXPORT_SYMBOL_GPL(vsock_find_unbound_socket); - struct sock *vsock_find_connected_socket(struct sockaddr_vm *src, struct sockaddr_vm *dst) { @@ -558,50 +532,6 @@ static int __vsock_bind_stream(struct vsock_sock *vsk, return 0; } -int vsock_bind_dgram_generic(struct vsock_sock *vsk, struct sockaddr_vm *addr) -{ - static u32 port = LAST_RESERVED_PORT + 1; - struct sockaddr_vm new_addr; - - vsock_addr_init(&new_addr, addr->svm_cid, addr->svm_port); - - if (addr->svm_port == VMADDR_PORT_ANY) { - bool found = false; - unsigned int i; - - for (i = 0; i < MAX_PORT_RETRIES; i++) { - if (port <= LAST_RESERVED_PORT) - port = LAST_RESERVED_PORT + 1; - - new_addr.svm_port = port++; - - if (!__vsock_find_unbound_socket(&new_addr)) { - found = true; - break; - } - } - - if (!found) - return -EADDRNOTAVAIL; - } else { - /* If port is in reserved range, ensure caller - * has necessary privileges. - */ - if (addr->svm_port <= LAST_RESERVED_PORT && - !capable(CAP_NET_BIND_SERVICE)) { - return -EACCES; - } - - if (__vsock_find_unbound_socket(&new_addr)) - return -EADDRINUSE; - } - - vsock_addr_init(&vsk->local_addr, new_addr.svm_cid, new_addr.svm_port); - - return 0; -} -EXPORT_SYMBOL_GPL(vsock_bind_dgram_generic); - static int __vsock_bind_dgram(struct vsock_sock *vsk, struct sockaddr_vm *addr) { diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c deleted file mode 100644 index df65dca55fa1..000000000000 --- a/net/vmw_vsock/virtio_transport.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * virtio transport for vsock - * - * Copyright (C) 2013-2015 Red Hat, Inc. - * Author: Asias He - * Stefan Hajnoczi - * - * Some of the code is take from Gerd Hoffmann 's - * early virtio-vsock proof-of-concept bits. - * - * This work is licensed under the terms of the GNU GPL, version 2. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct workqueue_struct *virtio_vsock_workqueue; -static struct virtio_vsock *the_virtio_vsock; -static DEFINE_MUTEX(the_virtio_vsock_mutex); /* protects the_virtio_vsock */ -static void virtio_vsock_rx_fill(struct virtio_vsock *vsock); - -struct virtio_vsock { - /* Virtio device */ - struct virtio_device *vdev; - /* Virtio virtqueue */ - struct virtqueue *vqs[VSOCK_VQ_MAX]; - /* Wait queue for send pkt */ - wait_queue_head_t queue_wait; - /* Work item to send pkt */ - struct work_struct tx_work; - /* Work item to recv pkt */ - struct work_struct rx_work; - /* Mutex to protect send pkt*/ - struct mutex tx_lock; - /* Mutex to protect recv pkt*/ - struct mutex rx_lock; - /* Number of recv buffers */ - int rx_buf_nr; - /* Number of max recv buffers */ - int rx_buf_max_nr; - /* Used for global tx buf limitation */ - u32 total_tx_buf; - /* Guest context id, just like guest ip address */ - u32 guest_cid; -}; - -static struct virtio_vsock *virtio_vsock_get(void) -{ - return the_virtio_vsock; -} - -static u32 virtio_transport_get_local_cid(void) -{ - struct virtio_vsock *vsock = virtio_vsock_get(); - - return vsock->guest_cid; -} - -static int -virtio_transport_send_pkt(struct vsock_sock *vsk, - struct virtio_vsock_pkt_info *info) -{ - u32 src_cid, src_port, dst_cid, dst_port; - int ret, in_sg = 0, out_sg = 0; - struct virtio_transport *trans; - struct virtio_vsock_pkt *pkt; - struct virtio_vsock *vsock; - struct scatterlist hdr, buf, *sgs[2]; - struct virtqueue *vq; - u32 pkt_len = info->pkt_len; - DEFINE_WAIT(wait); - - vsock = virtio_vsock_get(); - if (!vsock) - return -ENODEV; - - src_cid = virtio_transport_get_local_cid(); - src_port = vsk->local_addr.svm_port; - if (!info->remote_cid) { - dst_cid = vsk->remote_addr.svm_cid; - dst_port = vsk->remote_addr.svm_port; - } else { - dst_cid = info->remote_cid; - dst_port = info->remote_port; - } - - trans = vsk->trans; - vq = vsock->vqs[VSOCK_VQ_TX]; - - if (pkt_len > VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE) - pkt_len = VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE; - pkt_len = virtio_transport_get_credit(trans, pkt_len); - /* Do not send zero length OP_RW pkt*/ - if (pkt_len == 0 && info->op == VIRTIO_VSOCK_OP_RW) - return pkt_len; - - /* Respect global tx buf limitation */ - mutex_lock(&vsock->tx_lock); - while (pkt_len + vsock->total_tx_buf > VIRTIO_VSOCK_MAX_TX_BUF_SIZE) { - prepare_to_wait_exclusive(&vsock->queue_wait, &wait, - TASK_UNINTERRUPTIBLE); - mutex_unlock(&vsock->tx_lock); - schedule(); - mutex_lock(&vsock->tx_lock); - finish_wait(&vsock->queue_wait, &wait); - } - vsock->total_tx_buf += pkt_len; - mutex_unlock(&vsock->tx_lock); - - pkt = virtio_transport_alloc_pkt(vsk, info, pkt_len, - src_cid, src_port, - dst_cid, dst_port); - if (!pkt) { - mutex_lock(&vsock->tx_lock); - vsock->total_tx_buf -= pkt_len; - mutex_unlock(&vsock->tx_lock); - virtio_transport_put_credit(trans, pkt_len); - return -ENOMEM; - } - - pr_debug("%s:info->pkt_len= %d\n", __func__, info->pkt_len); - - /* Will be released in virtio_transport_send_pkt_work */ - sock_hold(&trans->vsk->sk); - virtio_transport_inc_tx_pkt(pkt); - - /* Put pkt in the virtqueue */ - sg_init_one(&hdr, &pkt->hdr, sizeof(pkt->hdr)); - sgs[out_sg++] = &hdr; - if (info->msg && info->pkt_len > 0) { - sg_init_one(&buf, pkt->buf, pkt->len); - sgs[out_sg++] = &buf; - } - - mutex_lock(&vsock->tx_lock); - while ((ret = virtqueue_add_sgs(vq, sgs, out_sg, in_sg, pkt, - GFP_KERNEL)) < 0) { - prepare_to_wait_exclusive(&vsock->queue_wait, &wait, - TASK_UNINTERRUPTIBLE); - mutex_unlock(&vsock->tx_lock); - schedule(); - mutex_lock(&vsock->tx_lock); - finish_wait(&vsock->queue_wait, &wait); - } - virtqueue_kick(vq); - mutex_unlock(&vsock->tx_lock); - - return pkt_len; -} - -static struct virtio_transport_pkt_ops virtio_ops = { - .send_pkt = virtio_transport_send_pkt, -}; - -static void virtio_vsock_rx_fill(struct virtio_vsock *vsock) -{ - int buf_len = VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE; - struct virtio_vsock_pkt *pkt; - struct scatterlist hdr, buf, *sgs[2]; - struct virtqueue *vq; - int ret; - - vq = vsock->vqs[VSOCK_VQ_RX]; - - do { - pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); - if (!pkt) { - pr_debug("%s: fail to allocate pkt\n", __func__); - goto out; - } - - /* TODO: use mergeable rx buffer */ - pkt->buf = kmalloc(buf_len, GFP_KERNEL); - if (!pkt->buf) { - pr_debug("%s: fail to allocate pkt->buf\n", __func__); - goto err; - } - - sg_init_one(&hdr, &pkt->hdr, sizeof(pkt->hdr)); - sgs[0] = &hdr; - - sg_init_one(&buf, pkt->buf, buf_len); - sgs[1] = &buf; - ret = virtqueue_add_sgs(vq, sgs, 0, 2, pkt, GFP_KERNEL); - if (ret) - goto err; - vsock->rx_buf_nr++; - } while (vq->num_free); - if (vsock->rx_buf_nr > vsock->rx_buf_max_nr) - vsock->rx_buf_max_nr = vsock->rx_buf_nr; -out: - virtqueue_kick(vq); - return; -err: - virtqueue_kick(vq); - virtio_transport_free_pkt(pkt); - return; -} - -static void virtio_transport_send_pkt_work(struct work_struct *work) -{ - struct virtio_vsock *vsock = - container_of(work, struct virtio_vsock, tx_work); - struct virtio_vsock_pkt *pkt; - bool added = false; - struct virtqueue *vq; - unsigned int len; - struct sock *sk; - - vq = vsock->vqs[VSOCK_VQ_TX]; - mutex_lock(&vsock->tx_lock); - do { - virtqueue_disable_cb(vq); - while ((pkt = virtqueue_get_buf(vq, &len)) != NULL) { - sk = &pkt->trans->vsk->sk; - virtio_transport_dec_tx_pkt(pkt); - /* Release refcnt taken in virtio_transport_send_pkt */ - sock_put(sk); - vsock->total_tx_buf -= pkt->len; - virtio_transport_free_pkt(pkt); - added = true; - } - } while (!virtqueue_enable_cb(vq)); - mutex_unlock(&vsock->tx_lock); - - if (added) - wake_up(&vsock->queue_wait); -} - -static void virtio_transport_recv_pkt_work(struct work_struct *work) -{ - struct virtio_vsock *vsock = - container_of(work, struct virtio_vsock, rx_work); - struct virtio_vsock_pkt *pkt; - struct virtqueue *vq; - unsigned int len; - - vq = vsock->vqs[VSOCK_VQ_RX]; - mutex_lock(&vsock->rx_lock); - do { - virtqueue_disable_cb(vq); - while ((pkt = virtqueue_get_buf(vq, &len)) != NULL) { - pkt->len = len; - virtio_transport_recv_pkt(pkt); - vsock->rx_buf_nr--; - } - } while (!virtqueue_enable_cb(vq)); - - if (vsock->rx_buf_nr < vsock->rx_buf_max_nr / 2) - virtio_vsock_rx_fill(vsock); - mutex_unlock(&vsock->rx_lock); -} - -static void virtio_vsock_ctrl_done(struct virtqueue *vq) -{ -} - -static void virtio_vsock_tx_done(struct virtqueue *vq) -{ - struct virtio_vsock *vsock = vq->vdev->priv; - - if (!vsock) - return; - queue_work(virtio_vsock_workqueue, &vsock->tx_work); -} - -static void virtio_vsock_rx_done(struct virtqueue *vq) -{ - struct virtio_vsock *vsock = vq->vdev->priv; - - if (!vsock) - return; - queue_work(virtio_vsock_workqueue, &vsock->rx_work); -} - -static int -virtio_transport_socket_init(struct vsock_sock *vsk, struct vsock_sock *psk) -{ - struct virtio_transport *trans; - int ret; - - ret = virtio_transport_do_socket_init(vsk, psk); - if (ret) - return ret; - - trans = vsk->trans; - trans->ops = &virtio_ops; - return ret; -} - -static struct vsock_transport virtio_transport = { - .get_local_cid = virtio_transport_get_local_cid, - - .init = virtio_transport_socket_init, - .destruct = virtio_transport_destruct, - .release = virtio_transport_release, - .connect = virtio_transport_connect, - .shutdown = virtio_transport_shutdown, - - .dgram_bind = virtio_transport_dgram_bind, - .dgram_dequeue = virtio_transport_dgram_dequeue, - .dgram_enqueue = virtio_transport_dgram_enqueue, - .dgram_allow = virtio_transport_dgram_allow, - - .stream_dequeue = virtio_transport_stream_dequeue, - .stream_enqueue = virtio_transport_stream_enqueue, - .stream_has_data = virtio_transport_stream_has_data, - .stream_has_space = virtio_transport_stream_has_space, - .stream_rcvhiwat = virtio_transport_stream_rcvhiwat, - .stream_is_active = virtio_transport_stream_is_active, - .stream_allow = virtio_transport_stream_allow, - - .notify_poll_in = virtio_transport_notify_poll_in, - .notify_poll_out = virtio_transport_notify_poll_out, - .notify_recv_init = virtio_transport_notify_recv_init, - .notify_recv_pre_block = virtio_transport_notify_recv_pre_block, - .notify_recv_pre_dequeue = virtio_transport_notify_recv_pre_dequeue, - .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue, - .notify_send_init = virtio_transport_notify_send_init, - .notify_send_pre_block = virtio_transport_notify_send_pre_block, - .notify_send_pre_enqueue = virtio_transport_notify_send_pre_enqueue, - .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue, - - .set_buffer_size = virtio_transport_set_buffer_size, - .set_min_buffer_size = virtio_transport_set_min_buffer_size, - .set_max_buffer_size = virtio_transport_set_max_buffer_size, - .get_buffer_size = virtio_transport_get_buffer_size, - .get_min_buffer_size = virtio_transport_get_min_buffer_size, - .get_max_buffer_size = virtio_transport_get_max_buffer_size, -}; - -static int virtio_vsock_probe(struct virtio_device *vdev) -{ - vq_callback_t *callbacks[] = { - virtio_vsock_ctrl_done, - virtio_vsock_rx_done, - virtio_vsock_tx_done, - }; - const char *names[] = { - "ctrl", - "rx", - "tx", - }; - struct virtio_vsock *vsock = NULL; - u32 guest_cid; - int ret; - - ret = mutex_lock_interruptible(&the_virtio_vsock_mutex); - if (ret) - return ret; - - /* Only one virtio-vsock device per guest is supported */ - if (the_virtio_vsock) { - ret = -EBUSY; - goto out; - } - - vsock = kzalloc(sizeof(*vsock), GFP_KERNEL); - if (!vsock) { - ret = -ENOMEM; - goto out; - } - - vsock->vdev = vdev; - - ret = vsock->vdev->config->find_vqs(vsock->vdev, VSOCK_VQ_MAX, - vsock->vqs, callbacks, names); - if (ret < 0) - goto out; - - vdev->config->get(vdev, offsetof(struct virtio_vsock_config, guest_cid), - &guest_cid, sizeof(guest_cid)); - vsock->guest_cid = le32_to_cpu(guest_cid); - pr_debug("%s:guest_cid=%d\n", __func__, vsock->guest_cid); - - ret = vsock_core_init(&virtio_transport); - if (ret < 0) - goto out_vqs; - - vsock->rx_buf_nr = 0; - vsock->rx_buf_max_nr = 0; - - vdev->priv = the_virtio_vsock = vsock; - init_waitqueue_head(&vsock->queue_wait); - mutex_init(&vsock->tx_lock); - mutex_init(&vsock->rx_lock); - INIT_WORK(&vsock->rx_work, virtio_transport_recv_pkt_work); - INIT_WORK(&vsock->tx_work, virtio_transport_send_pkt_work); - - mutex_lock(&vsock->rx_lock); - virtio_vsock_rx_fill(vsock); - mutex_unlock(&vsock->rx_lock); - - mutex_unlock(&the_virtio_vsock_mutex); - return 0; - -out_vqs: - vsock->vdev->config->del_vqs(vsock->vdev); -out: - kfree(vsock); - mutex_unlock(&the_virtio_vsock_mutex); - return ret; -} - -static void virtio_vsock_remove(struct virtio_device *vdev) -{ - struct virtio_vsock *vsock = vdev->priv; - - mutex_lock(&the_virtio_vsock_mutex); - the_virtio_vsock = NULL; - vsock_core_exit(); - mutex_unlock(&the_virtio_vsock_mutex); - - kfree(vsock); -} - -static struct virtio_device_id id_table[] = { - { VIRTIO_ID_VSOCK, VIRTIO_DEV_ANY_ID }, - { 0 }, -}; - -static unsigned int features[] = { -}; - -static struct virtio_driver virtio_vsock_driver = { - .feature_table = features, - .feature_table_size = ARRAY_SIZE(features), - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .id_table = id_table, - .probe = virtio_vsock_probe, - .remove = virtio_vsock_remove, -}; - -static int __init virtio_vsock_init(void) -{ - int ret; - - virtio_vsock_workqueue = alloc_workqueue("virtio_vsock", 0, 0); - if (!virtio_vsock_workqueue) - return -ENOMEM; - ret = register_virtio_driver(&virtio_vsock_driver); - if (ret) - destroy_workqueue(virtio_vsock_workqueue); - return ret; -} - -static void __exit virtio_vsock_exit(void) -{ - unregister_virtio_driver(&virtio_vsock_driver); - destroy_workqueue(virtio_vsock_workqueue); -} - -module_init(virtio_vsock_init); -module_exit(virtio_vsock_exit); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Asias He"); -MODULE_DESCRIPTION("virtio transport for vsock"); -MODULE_DEVICE_TABLE(virtio, id_table); diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c deleted file mode 100644 index 28f790da6f15..000000000000 --- a/net/vmw_vsock/virtio_transport_common.c +++ /dev/null @@ -1,1272 +0,0 @@ -/* - * common code for virtio vsock - * - * Copyright (C) 2013-2015 Red Hat, Inc. - * Author: Asias He - * Stefan Hajnoczi - * - * This work is licensed under the terms of the GNU GPL, version 2. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define COOKIEBITS 24 -#define COOKIEMASK (((u32)1 << COOKIEBITS) - 1) -#define VSOCK_TIMEOUT_INIT 4 - -#define SHA_MESSAGE_WORDS 16 -#define SHA_VSOCK_WORDS 5 - -static u32 vsockcookie_secret[2][SHA_MESSAGE_WORDS - SHA_VSOCK_WORDS + - SHA_DIGEST_WORDS]; - -static DEFINE_PER_CPU(__u32[SHA_MESSAGE_WORDS + SHA_DIGEST_WORDS + - SHA_WORKSPACE_WORDS], vsock_cookie_scratch); - -static u32 cookie_hash(u32 saddr, u32 daddr, u16 sport, u16 dport, - u32 count, int c) -{ - __u32 *tmp = this_cpu_ptr(vsock_cookie_scratch); - - memcpy(tmp + SHA_VSOCK_WORDS, vsockcookie_secret[c], - sizeof(vsockcookie_secret[c])); - tmp[0] = saddr; - tmp[1] = daddr; - tmp[2] = sport; - tmp[3] = dport; - tmp[4] = count; - sha_transform(tmp + SHA_MESSAGE_WORDS, (__u8 *)tmp, - tmp + SHA_MESSAGE_WORDS + SHA_DIGEST_WORDS); - - return tmp[17]; -} - -static u32 -virtio_vsock_secure_cookie(u32 saddr, u32 daddr, u32 sport, u32 dport, - u32 count) -{ - u32 h1, h2; - - h1 = cookie_hash(saddr, daddr, sport, dport, 0, 0); - h2 = cookie_hash(saddr, daddr, sport, dport, count, 1); - - return h1 + (count << COOKIEBITS) + (h2 & COOKIEMASK); -} - -static u32 -virtio_vsock_check_cookie(u32 saddr, u32 daddr, u32 sport, u32 dport, - u32 count, u32 cookie, u32 maxdiff) -{ - u32 diff; - u32 ret; - - cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0); - - diff = (count - (cookie >> COOKIEBITS)) & ((u32)-1 >> COOKIEBITS); - pr_debug("%s: diff=%x\n", __func__, diff); - if (diff >= maxdiff) - return (u32)-1; - - ret = (cookie - - cookie_hash(saddr, daddr, sport, dport, count - diff, 1)) - & COOKIEMASK; - pr_debug("%s: ret=%x\n", __func__, diff); - - return ret; -} - -void virtio_vsock_dumppkt(const char *func, const struct virtio_vsock_pkt *pkt) -{ - pr_debug("%s: pkt=%p, op=%d, len=%d, %d:%d---%d:%d, len=%d\n", - func, pkt, - le16_to_cpu(pkt->hdr.op), - le32_to_cpu(pkt->hdr.len), - le32_to_cpu(pkt->hdr.src_cid), - le32_to_cpu(pkt->hdr.src_port), - le32_to_cpu(pkt->hdr.dst_cid), - le32_to_cpu(pkt->hdr.dst_port), - pkt->len); -} -EXPORT_SYMBOL_GPL(virtio_vsock_dumppkt); - -struct virtio_vsock_pkt * -virtio_transport_alloc_pkt(struct vsock_sock *vsk, - struct virtio_vsock_pkt_info *info, - size_t len, - u32 src_cid, - u32 src_port, - u32 dst_cid, - u32 dst_port) -{ - struct virtio_transport *trans = vsk->trans; - struct virtio_vsock_pkt *pkt; - int err; - - BUG_ON(!trans); - - pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); - if (!pkt) - return NULL; - - pkt->hdr.type = cpu_to_le16(info->type); - pkt->hdr.op = cpu_to_le16(info->op); - pkt->hdr.src_cid = cpu_to_le32(src_cid); - pkt->hdr.src_port = cpu_to_le32(src_port); - pkt->hdr.dst_cid = cpu_to_le32(dst_cid); - pkt->hdr.dst_port = cpu_to_le32(dst_port); - pkt->hdr.flags = cpu_to_le32(info->flags); - pkt->len = len; - pkt->trans = trans; - if (info->type == VIRTIO_VSOCK_TYPE_DGRAM) - pkt->hdr.len = cpu_to_le32(len + (info->dgram_len << 16)); - else if (info->type == VIRTIO_VSOCK_TYPE_STREAM) - pkt->hdr.len = cpu_to_le32(len); - - if (info->msg && len > 0) { - pkt->buf = kmalloc(len, GFP_KERNEL); - if (!pkt->buf) - goto out_pkt; - err = memcpy_from_msg(pkt->buf, info->msg, len); - if (err) - goto out; - } - - return pkt; - -out: - kfree(pkt->buf); -out_pkt: - kfree(pkt); - return NULL; -} -EXPORT_SYMBOL_GPL(virtio_transport_alloc_pkt); - -struct sock * -virtio_transport_get_pending(struct sock *listener, - struct virtio_vsock_pkt *pkt) -{ - struct vsock_sock *vlistener; - struct vsock_sock *vpending; - struct sockaddr_vm src; - struct sockaddr_vm dst; - struct sock *pending; - - vsock_addr_init(&src, le32_to_cpu(pkt->hdr.src_cid), le32_to_cpu(pkt->hdr.src_port)); - vsock_addr_init(&dst, le32_to_cpu(pkt->hdr.dst_cid), le32_to_cpu(pkt->hdr.dst_port)); - - vlistener = vsock_sk(listener); - list_for_each_entry(vpending, &vlistener->pending_links, - pending_links) { - if (vsock_addr_equals_addr(&src, &vpending->remote_addr) && - vsock_addr_equals_addr(&dst, &vpending->local_addr)) { - pending = sk_vsock(vpending); - sock_hold(pending); - return pending; - } - } - - return NULL; -} -EXPORT_SYMBOL_GPL(virtio_transport_get_pending); - -static void virtio_transport_inc_rx_pkt(struct virtio_vsock_pkt *pkt) -{ - pkt->trans->rx_bytes += pkt->len; -} - -static void virtio_transport_dec_rx_pkt(struct virtio_vsock_pkt *pkt) -{ - pkt->trans->rx_bytes -= pkt->len; - pkt->trans->fwd_cnt += pkt->len; -} - -void virtio_transport_inc_tx_pkt(struct virtio_vsock_pkt *pkt) -{ - mutex_lock(&pkt->trans->tx_lock); - pkt->hdr.fwd_cnt = cpu_to_le32(pkt->trans->fwd_cnt); - pkt->hdr.buf_alloc = cpu_to_le32(pkt->trans->buf_alloc); - mutex_unlock(&pkt->trans->tx_lock); -} -EXPORT_SYMBOL_GPL(virtio_transport_inc_tx_pkt); - -void virtio_transport_dec_tx_pkt(struct virtio_vsock_pkt *pkt) -{ -} -EXPORT_SYMBOL_GPL(virtio_transport_dec_tx_pkt); - -u32 virtio_transport_get_credit(struct virtio_transport *trans, u32 credit) -{ - u32 ret; - - mutex_lock(&trans->tx_lock); - ret = trans->peer_buf_alloc - (trans->tx_cnt - trans->peer_fwd_cnt); - if (ret > credit) - ret = credit; - trans->tx_cnt += ret; - mutex_unlock(&trans->tx_lock); - - pr_debug("%s: ret=%d, buf_alloc=%d, peer_buf_alloc=%d," - "tx_cnt=%d, fwd_cnt=%d, peer_fwd_cnt=%d\n", __func__, - ret, trans->buf_alloc, trans->peer_buf_alloc, - trans->tx_cnt, trans->fwd_cnt, trans->peer_fwd_cnt); - - return ret; -} -EXPORT_SYMBOL_GPL(virtio_transport_get_credit); - -void virtio_transport_put_credit(struct virtio_transport *trans, u32 credit) -{ - mutex_lock(&trans->tx_lock); - trans->tx_cnt -= credit; - mutex_unlock(&trans->tx_lock); -} -EXPORT_SYMBOL_GPL(virtio_transport_put_credit); - -static int virtio_transport_send_credit_update(struct vsock_sock *vsk, int type, struct virtio_vsock_hdr *hdr) -{ - struct virtio_transport *trans = vsk->trans; - struct virtio_vsock_pkt_info info = { - .op = VIRTIO_VSOCK_OP_CREDIT_UPDATE, - .type = type, - }; - - if (hdr && type == VIRTIO_VSOCK_TYPE_DGRAM) { - info.remote_cid = le32_to_cpu(hdr->src_cid); - info.remote_port = le32_to_cpu(hdr->src_port); - } - - pr_debug("%s: sk=%p send_credit_update\n", __func__, vsk); - return trans->ops->send_pkt(vsk, &info); -} - -static int virtio_transport_send_credit_request(struct vsock_sock *vsk, int type) -{ - struct virtio_transport *trans = vsk->trans; - struct virtio_vsock_pkt_info info = { - .op = VIRTIO_VSOCK_OP_CREDIT_REQUEST, - .type = type, - }; - - pr_debug("%s: sk=%p send_credit_request\n", __func__, vsk); - return trans->ops->send_pkt(vsk, &info); -} - -static ssize_t -virtio_transport_stream_do_dequeue(struct vsock_sock *vsk, - struct msghdr *msg, - size_t len) -{ - struct virtio_transport *trans = vsk->trans; - struct virtio_vsock_pkt *pkt; - size_t bytes, total = 0; - int err = -EFAULT; - - mutex_lock(&trans->rx_lock); - while (total < len && trans->rx_bytes > 0 && - !list_empty(&trans->rx_queue)) { - pkt = list_first_entry(&trans->rx_queue, - struct virtio_vsock_pkt, list); - - bytes = len - total; - if (bytes > pkt->len - pkt->off) - bytes = pkt->len - pkt->off; - - err = memcpy_to_msg(msg, pkt->buf + pkt->off, bytes); - if (err) - goto out; - total += bytes; - pkt->off += bytes; - if (pkt->off == pkt->len) { - virtio_transport_dec_rx_pkt(pkt); - list_del(&pkt->list); - virtio_transport_free_pkt(pkt); - } - } - mutex_unlock(&trans->rx_lock); - - /* Send a credit pkt to peer */ - virtio_transport_send_credit_update(vsk, VIRTIO_VSOCK_TYPE_STREAM, - NULL); - - return total; - -out: - mutex_unlock(&trans->rx_lock); - if (total) - err = total; - return err; -} - -ssize_t -virtio_transport_stream_dequeue(struct vsock_sock *vsk, - struct msghdr *msg, - size_t len, int flags) -{ - if (flags & MSG_PEEK) - return -EOPNOTSUPP; - - return virtio_transport_stream_do_dequeue(vsk, msg, len); -} -EXPORT_SYMBOL_GPL(virtio_transport_stream_dequeue); - -struct dgram_skb { - struct list_head list; - struct sk_buff *skb; - u16 id; -}; - -static struct dgram_skb *dgram_id_to_skb(struct virtio_transport *trans, - u16 id) -{ - struct dgram_skb *dgram_skb; - - list_for_each_entry(dgram_skb, &trans->incomplete_dgrams, list) { - if (dgram_skb->id == id) - return dgram_skb; - } - - return NULL; -} - -static void -virtio_transport_recv_dgram(struct sock *sk, - struct virtio_vsock_pkt *pkt) -{ - struct sk_buff *skb = NULL; - struct vsock_sock *vsk; - struct virtio_transport *trans; - size_t size; - u16 dgram_id, pkt_off, dgram_len, pkt_len; - u32 flags, len; - struct dgram_skb *dgram_skb; - - vsk = vsock_sk(sk); - trans = vsk->trans; - - /* len: dgram_len | pkt_len */ - len = le32_to_cpu(pkt->hdr.len); - dgram_len = len >> 16; - pkt_len = len & 0xFFFF; - - /* flags: dgram_id | pkt_off */ - flags = le32_to_cpu(pkt->hdr.flags); - dgram_id = flags >> 16; - pkt_off = flags & 0xFFFF; - - pr_debug("%s: dgram_len=%d, pkt_len=%d, id=%d, off=%d\n", __func__, - dgram_len, pkt_len, dgram_id, pkt_off); - - dgram_skb = dgram_id_to_skb(trans, dgram_id); - if (dgram_skb) { - /* This pkt is for a existing dgram */ - skb = dgram_skb->skb; - pr_debug("%s:found skb\n", __func__); - } - - /* Packet payload must be within datagram bounds */ - if (pkt_len > VIRTIO_VSOCK_DEFAULT_RX_BUF_SIZE) - goto drop; - if (pkt_len > dgram_len) - goto drop; - if (pkt_off > dgram_len) - goto drop; - if (dgram_len - pkt_off < pkt_len) - goto drop; - - if (!skb) { - /* This pkt is for a new dgram */ - pr_debug("%s:create skb\n", __func__); - - size = sizeof(pkt->hdr) + dgram_len; - /* Attach the packet to the socket's receive queue as an sk_buff. */ - dgram_skb = kzalloc(sizeof(struct dgram_skb), GFP_ATOMIC); - if (!dgram_skb) - goto drop; - - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) { - kfree(dgram_skb); - dgram_skb = NULL; - goto drop; - } - dgram_skb->id = dgram_id; - dgram_skb->skb = skb; - list_add_tail(&dgram_skb->list, &trans->incomplete_dgrams); - - /* sk_receive_skb() will do a sock_put(), so hold here. */ - sock_hold(sk); - skb_put(skb, size); - memcpy(skb->data, &pkt->hdr, sizeof(pkt->hdr)); - } - - memcpy(skb->data + sizeof(pkt->hdr) + pkt_off, pkt->buf, pkt_len); - - pr_debug("%s:C, off=%d, pkt_len=%d, dgram_len=%d\n", __func__, - pkt_off, pkt_len, dgram_len); - - /* We are done with this dgram */ - if (pkt_off + pkt_len == dgram_len) { - pr_debug("%s:dgram_id=%d is done\n", __func__, dgram_id); - list_del(&dgram_skb->list); - kfree(dgram_skb); - sk_receive_skb(sk, skb, 0); - } - virtio_transport_free_pkt(pkt); - return; - -drop: - if (dgram_skb) { - list_del(&dgram_skb->list); - kfree(dgram_skb); - kfree_skb(skb); - sock_put(sk); - } - virtio_transport_free_pkt(pkt); -} - -int -virtio_transport_dgram_dequeue(struct vsock_sock *vsk, - struct msghdr *msg, - size_t len, int flags) -{ - struct virtio_vsock_hdr *hdr; - struct sk_buff *skb; - int noblock; - int err; - int dgram_len; - - noblock = flags & MSG_DONTWAIT; - - if (flags & MSG_OOB || flags & MSG_ERRQUEUE) - return -EOPNOTSUPP; - - /* Retrieve the head sk_buff from the socket's receive queue. */ - err = 0; - skb = skb_recv_datagram(&vsk->sk, flags, noblock, &err); - if (err) - return err; - if (!skb) - return -EAGAIN; - - hdr = (struct virtio_vsock_hdr *)skb->data; - if (!hdr) - goto out; - - dgram_len = le32_to_cpu(hdr->len) >> 16; - /* Place the datagram payload in the user's iovec. */ - err = skb_copy_datagram_msg(skb, sizeof(*hdr), msg, dgram_len); - if (err) - goto out; - - if (msg->msg_name) { - /* Provide the address of the sender. */ - DECLARE_SOCKADDR(struct sockaddr_vm *, vm_addr, msg->msg_name); - vsock_addr_init(vm_addr, le32_to_cpu(hdr->src_cid), le32_to_cpu(hdr->src_port)); - msg->msg_namelen = sizeof(*vm_addr); - } - err = dgram_len; - - /* Send a credit pkt to peer */ - virtio_transport_send_credit_update(vsk, VIRTIO_VSOCK_TYPE_DGRAM, hdr); - - pr_debug("%s:done, recved =%d\n", __func__, dgram_len); -out: - skb_free_datagram(&vsk->sk, skb); - return err; -} -EXPORT_SYMBOL_GPL(virtio_transport_dgram_dequeue); - -s64 virtio_transport_stream_has_data(struct vsock_sock *vsk) -{ - struct virtio_transport *trans = vsk->trans; - s64 bytes; - - mutex_lock(&trans->rx_lock); - bytes = trans->rx_bytes; - mutex_unlock(&trans->rx_lock); - - return bytes; -} -EXPORT_SYMBOL_GPL(virtio_transport_stream_has_data); - -static s64 virtio_transport_has_space(struct vsock_sock *vsk) -{ - struct virtio_transport *trans = vsk->trans; - s64 bytes; - - bytes = trans->peer_buf_alloc - (trans->tx_cnt - trans->peer_fwd_cnt); - if (bytes < 0) - bytes = 0; - - return bytes; -} - -s64 virtio_transport_stream_has_space(struct vsock_sock *vsk) -{ - struct virtio_transport *trans = vsk->trans; - s64 bytes; - - mutex_lock(&trans->tx_lock); - bytes = virtio_transport_has_space(vsk); - mutex_unlock(&trans->tx_lock); - - pr_debug("%s: bytes=%lld\n", __func__, bytes); - - return bytes; -} -EXPORT_SYMBOL_GPL(virtio_transport_stream_has_space); - -int virtio_transport_do_socket_init(struct vsock_sock *vsk, - struct vsock_sock *psk) -{ - struct virtio_transport *trans; - - trans = kzalloc(sizeof(*trans), GFP_KERNEL); - if (!trans) - return -ENOMEM; - - vsk->trans = trans; - trans->vsk = vsk; - if (psk) { - struct virtio_transport *ptrans = psk->trans; - trans->buf_size = ptrans->buf_size; - trans->buf_size_min = ptrans->buf_size_min; - trans->buf_size_max = ptrans->buf_size_max; - trans->peer_buf_alloc = ptrans->peer_buf_alloc; - } else { - trans->buf_size = VIRTIO_VSOCK_DEFAULT_BUF_SIZE; - trans->buf_size_min = VIRTIO_VSOCK_DEFAULT_MIN_BUF_SIZE; - trans->buf_size_max = VIRTIO_VSOCK_DEFAULT_MAX_BUF_SIZE; - } - - trans->buf_alloc = trans->buf_size; - - pr_debug("%s: trans->buf_alloc=%d\n", __func__, trans->buf_alloc); - - mutex_init(&trans->rx_lock); - mutex_init(&trans->tx_lock); - INIT_LIST_HEAD(&trans->rx_queue); - INIT_LIST_HEAD(&trans->incomplete_dgrams); - - return 0; -} -EXPORT_SYMBOL_GPL(virtio_transport_do_socket_init); - -u64 virtio_transport_get_buffer_size(struct vsock_sock *vsk) -{ - struct virtio_transport *trans = vsk->trans; - - return trans->buf_size; -} -EXPORT_SYMBOL_GPL(virtio_transport_get_buffer_size); - -u64 virtio_transport_get_min_buffer_size(struct vsock_sock *vsk) -{ - struct virtio_transport *trans = vsk->trans; - - return trans->buf_size_min; -} -EXPORT_SYMBOL_GPL(virtio_transport_get_min_buffer_size); - -u64 virtio_transport_get_max_buffer_size(struct vsock_sock *vsk) -{ - struct virtio_transport *trans = vsk->trans; - - return trans->buf_size_max; -} -EXPORT_SYMBOL_GPL(virtio_transport_get_max_buffer_size); - -void virtio_transport_set_buffer_size(struct vsock_sock *vsk, u64 val) -{ - struct virtio_transport *trans = vsk->trans; - - if (val > VIRTIO_VSOCK_MAX_BUF_SIZE) - val = VIRTIO_VSOCK_MAX_BUF_SIZE; - if (val < trans->buf_size_min) - trans->buf_size_min = val; - if (val > trans->buf_size_max) - trans->buf_size_max = val; - trans->buf_size = val; - trans->buf_alloc = val; -} -EXPORT_SYMBOL_GPL(virtio_transport_set_buffer_size); - -void virtio_transport_set_min_buffer_size(struct vsock_sock *vsk, u64 val) -{ - struct virtio_transport *trans = vsk->trans; - - if (val > VIRTIO_VSOCK_MAX_BUF_SIZE) - val = VIRTIO_VSOCK_MAX_BUF_SIZE; - if (val > trans->buf_size) - trans->buf_size = val; - trans->buf_size_min = val; -} -EXPORT_SYMBOL_GPL(virtio_transport_set_min_buffer_size); - -void virtio_transport_set_max_buffer_size(struct vsock_sock *vsk, u64 val) -{ - struct virtio_transport *trans = vsk->trans; - - if (val > VIRTIO_VSOCK_MAX_BUF_SIZE) - val = VIRTIO_VSOCK_MAX_BUF_SIZE; - if (val < trans->buf_size) - trans->buf_size = val; - trans->buf_size_max = val; -} -EXPORT_SYMBOL_GPL(virtio_transport_set_max_buffer_size); - -int -virtio_transport_notify_poll_in(struct vsock_sock *vsk, - size_t target, - bool *data_ready_now) -{ - if (vsock_stream_has_data(vsk)) - *data_ready_now = true; - else - *data_ready_now = false; - - return 0; -} -EXPORT_SYMBOL_GPL(virtio_transport_notify_poll_in); - -int -virtio_transport_notify_poll_out(struct vsock_sock *vsk, - size_t target, - bool *space_avail_now) -{ - s64 free_space; - - free_space = vsock_stream_has_space(vsk); - if (free_space > 0) - *space_avail_now = true; - else if (free_space == 0) - *space_avail_now = false; - - return 0; -} -EXPORT_SYMBOL_GPL(virtio_transport_notify_poll_out); - -int virtio_transport_notify_recv_init(struct vsock_sock *vsk, - size_t target, struct vsock_transport_recv_notify_data *data) -{ - return 0; -} -EXPORT_SYMBOL_GPL(virtio_transport_notify_recv_init); - -int virtio_transport_notify_recv_pre_block(struct vsock_sock *vsk, - size_t target, struct vsock_transport_recv_notify_data *data) -{ - return 0; -} -EXPORT_SYMBOL_GPL(virtio_transport_notify_recv_pre_block); - -int virtio_transport_notify_recv_pre_dequeue(struct vsock_sock *vsk, - size_t target, struct vsock_transport_recv_notify_data *data) -{ - return 0; -} -EXPORT_SYMBOL_GPL(virtio_transport_notify_recv_pre_dequeue); - -int virtio_transport_notify_recv_post_dequeue(struct vsock_sock *vsk, - size_t target, ssize_t copied, bool data_read, - struct vsock_transport_recv_notify_data *data) -{ - return 0; -} -EXPORT_SYMBOL_GPL(virtio_transport_notify_recv_post_dequeue); - -int virtio_transport_notify_send_init(struct vsock_sock *vsk, - struct vsock_transport_send_notify_data *data) -{ - return 0; -} -EXPORT_SYMBOL_GPL(virtio_transport_notify_send_init); - -int virtio_transport_notify_send_pre_block(struct vsock_sock *vsk, - struct vsock_transport_send_notify_data *data) -{ - return 0; -} -EXPORT_SYMBOL_GPL(virtio_transport_notify_send_pre_block); - -int virtio_transport_notify_send_pre_enqueue(struct vsock_sock *vsk, - struct vsock_transport_send_notify_data *data) -{ - return 0; -} -EXPORT_SYMBOL_GPL(virtio_transport_notify_send_pre_enqueue); - -int virtio_transport_notify_send_post_enqueue(struct vsock_sock *vsk, - ssize_t written, struct vsock_transport_send_notify_data *data) -{ - return 0; -} -EXPORT_SYMBOL_GPL(virtio_transport_notify_send_post_enqueue); - -u64 virtio_transport_stream_rcvhiwat(struct vsock_sock *vsk) -{ - struct virtio_transport *trans = vsk->trans; - - return trans->buf_size; -} -EXPORT_SYMBOL_GPL(virtio_transport_stream_rcvhiwat); - -bool virtio_transport_stream_is_active(struct vsock_sock *vsk) -{ - return true; -} -EXPORT_SYMBOL_GPL(virtio_transport_stream_is_active); - -bool virtio_transport_stream_allow(u32 cid, u32 port) -{ - return true; -} -EXPORT_SYMBOL_GPL(virtio_transport_stream_allow); - -int virtio_transport_dgram_bind(struct vsock_sock *vsk, - struct sockaddr_vm *addr) -{ - return vsock_bind_dgram_generic(vsk, addr); -} -EXPORT_SYMBOL_GPL(virtio_transport_dgram_bind); - -bool virtio_transport_dgram_allow(u32 cid, u32 port) -{ - return true; -} -EXPORT_SYMBOL_GPL(virtio_transport_dgram_allow); - -int virtio_transport_connect(struct vsock_sock *vsk) -{ - struct virtio_transport *trans = vsk->trans; - struct virtio_vsock_pkt_info info = { - .op = VIRTIO_VSOCK_OP_REQUEST, - .type = VIRTIO_VSOCK_TYPE_STREAM, - }; - - pr_debug("%s: vsk=%p send_request\n", __func__, vsk); - return trans->ops->send_pkt(vsk, &info); -} -EXPORT_SYMBOL_GPL(virtio_transport_connect); - -int virtio_transport_shutdown(struct vsock_sock *vsk, int mode) -{ - struct virtio_transport *trans = vsk->trans; - struct virtio_vsock_pkt_info info = { - .op = VIRTIO_VSOCK_OP_SHUTDOWN, - .type = VIRTIO_VSOCK_TYPE_STREAM, - .flags = (mode & RCV_SHUTDOWN ? - VIRTIO_VSOCK_SHUTDOWN_RCV : 0) | - (mode & SEND_SHUTDOWN ? - VIRTIO_VSOCK_SHUTDOWN_SEND : 0), - }; - - pr_debug("%s: vsk=%p: send_shutdown\n", __func__, vsk); - return trans->ops->send_pkt(vsk, &info); -} -EXPORT_SYMBOL_GPL(virtio_transport_shutdown); - -void virtio_transport_release(struct vsock_sock *vsk) -{ - struct virtio_transport *trans = vsk->trans; - struct sock *sk = &vsk->sk; - struct dgram_skb *dgram_skb; - struct dgram_skb *dgram_skb_tmp; - - pr_debug("%s: vsk=%p\n", __func__, vsk); - - /* Tell other side to terminate connection */ - if (sk->sk_type == SOCK_STREAM && sk->sk_state == SS_CONNECTED) { - virtio_transport_shutdown(vsk, SHUTDOWN_MASK); - } - - /* Free incomplete dgrams */ - lock_sock(sk); - list_for_each_entry_safe(dgram_skb, dgram_skb_tmp, - &trans->incomplete_dgrams, list) { - list_del(&dgram_skb->list); - kfree_skb(dgram_skb->skb); - kfree(dgram_skb); - sock_put(sk); /* held in virtio_transport_recv_dgram() */ - } - release_sock(sk); -} -EXPORT_SYMBOL_GPL(virtio_transport_release); - -int -virtio_transport_dgram_enqueue(struct vsock_sock *vsk, - struct sockaddr_vm *remote_addr, - struct msghdr *msg, - size_t dgram_len) -{ - struct virtio_transport *trans = vsk->trans; - struct virtio_vsock_pkt_info info = { - .op = VIRTIO_VSOCK_OP_RW, - .type = VIRTIO_VSOCK_TYPE_DGRAM, - .msg = msg, - }; - size_t total_written = 0, pkt_off = 0, written; - u16 dgram_id; - - /* The max size of a single dgram we support is 64KB */ - if (dgram_len > VIRTIO_VSOCK_MAX_DGRAM_SIZE) - return -EMSGSIZE; - - info.dgram_len = dgram_len; - vsk->remote_addr = *remote_addr; - - dgram_id = trans->dgram_id++; - - /* TODO: To optimize, if we have enough credit to send the pkt already, - * do not ask the peer to send credit to use */ - virtio_transport_send_credit_request(vsk, VIRTIO_VSOCK_TYPE_DGRAM); - - while (total_written < dgram_len) { - info.pkt_len = dgram_len - total_written; - info.flags = dgram_id << 16 | pkt_off; - written = trans->ops->send_pkt(vsk, &info); - if (written < 0) - return -ENOMEM; - if (written == 0) { - /* TODO: if written = 0, we need a sleep & wakeup - * instead of sleep */ - pr_debug("%s: SHOULD WAIT written==0", __func__); - msleep(10); - } - total_written += written; - pkt_off += written; - pr_debug("%s:id=%d, dgram_len=%zu, off=%zu, total_written=%zu, written=%zu\n", - __func__, dgram_id, dgram_len, pkt_off, total_written, written); - } - - return dgram_len; -} -EXPORT_SYMBOL_GPL(virtio_transport_dgram_enqueue); - -ssize_t -virtio_transport_stream_enqueue(struct vsock_sock *vsk, - struct msghdr *msg, - size_t len) -{ - struct virtio_transport *trans = vsk->trans; - struct virtio_vsock_pkt_info info = { - .op = VIRTIO_VSOCK_OP_RW, - .type = VIRTIO_VSOCK_TYPE_STREAM, - .msg = msg, - .pkt_len = len, - }; - - return trans->ops->send_pkt(vsk, &info); -} -EXPORT_SYMBOL_GPL(virtio_transport_stream_enqueue); - -void virtio_transport_destruct(struct vsock_sock *vsk) -{ - struct virtio_transport *trans = vsk->trans; - - pr_debug("%s: vsk=%p\n", __func__, vsk); - kfree(trans); -} -EXPORT_SYMBOL_GPL(virtio_transport_destruct); - -static int virtio_transport_send_ack(struct vsock_sock *vsk, u32 cookie) -{ - struct virtio_transport *trans = vsk->trans; - struct virtio_vsock_pkt_info info = { - .op = VIRTIO_VSOCK_OP_ACK, - .type = VIRTIO_VSOCK_TYPE_STREAM, - .flags = cpu_to_le32(cookie), - }; - - pr_debug("%s: sk=%p send_offer\n", __func__, vsk); - return trans->ops->send_pkt(vsk, &info); -} - -static int virtio_transport_send_reset(struct vsock_sock *vsk, - struct virtio_vsock_pkt *pkt) -{ - struct virtio_transport *trans = vsk->trans; - struct virtio_vsock_pkt_info info = { - .op = VIRTIO_VSOCK_OP_RST, - .type = VIRTIO_VSOCK_TYPE_STREAM, - }; - - pr_debug("%s\n", __func__); - - /* Send RST only if the original pkt is not a RST pkt */ - if (le16_to_cpu(pkt->hdr.op) == VIRTIO_VSOCK_OP_RST) - return 0; - - return trans->ops->send_pkt(vsk, &info); -} - -static int -virtio_transport_recv_connecting(struct sock *sk, - struct virtio_vsock_pkt *pkt) -{ - struct vsock_sock *vsk = vsock_sk(sk); - int err; - int skerr; - u32 cookie; - - pr_debug("%s: vsk=%p\n", __func__, vsk); - switch (le16_to_cpu(pkt->hdr.op)) { - case VIRTIO_VSOCK_OP_RESPONSE: - cookie = le32_to_cpu(pkt->hdr.flags); - pr_debug("%s: got RESPONSE and send ACK, cookie=%x\n", __func__, cookie); - err = virtio_transport_send_ack(vsk, cookie); - if (err < 0) { - skerr = -err; - goto destroy; - } - sk->sk_state = SS_CONNECTED; - sk->sk_socket->state = SS_CONNECTED; - vsock_insert_connected(vsk); - sk->sk_state_change(sk); - break; - case VIRTIO_VSOCK_OP_INVALID: - pr_debug("%s: got invalid\n", __func__); - break; - case VIRTIO_VSOCK_OP_RST: - pr_debug("%s: got rst\n", __func__); - skerr = ECONNRESET; - err = 0; - goto destroy; - default: - pr_debug("%s: got def\n", __func__); - skerr = EPROTO; - err = -EINVAL; - goto destroy; - } - return 0; - -destroy: - virtio_transport_send_reset(vsk, pkt); - sk->sk_state = SS_UNCONNECTED; - sk->sk_err = skerr; - sk->sk_error_report(sk); - return err; -} - -static int -virtio_transport_recv_connected(struct sock *sk, - struct virtio_vsock_pkt *pkt) -{ - struct vsock_sock *vsk = vsock_sk(sk); - struct virtio_transport *trans = vsk->trans; - int err = 0; - - switch (le16_to_cpu(pkt->hdr.op)) { - case VIRTIO_VSOCK_OP_RW: - pkt->len = le32_to_cpu(pkt->hdr.len); - pkt->off = 0; - pkt->trans = trans; - - mutex_lock(&trans->rx_lock); - virtio_transport_inc_rx_pkt(pkt); - list_add_tail(&pkt->list, &trans->rx_queue); - mutex_unlock(&trans->rx_lock); - - sk->sk_data_ready(sk); - return err; - case VIRTIO_VSOCK_OP_CREDIT_UPDATE: - sk->sk_write_space(sk); - break; - case VIRTIO_VSOCK_OP_SHUTDOWN: - pr_debug("%s: got shutdown\n", __func__); - if (le32_to_cpu(pkt->hdr.flags) & VIRTIO_VSOCK_SHUTDOWN_RCV) - vsk->peer_shutdown |= RCV_SHUTDOWN; - if (le32_to_cpu(pkt->hdr.flags) & VIRTIO_VSOCK_SHUTDOWN_SEND) - vsk->peer_shutdown |= SEND_SHUTDOWN; - if (le32_to_cpu(pkt->hdr.flags)) - sk->sk_state_change(sk); - break; - case VIRTIO_VSOCK_OP_RST: - pr_debug("%s: got rst\n", __func__); - sock_set_flag(sk, SOCK_DONE); - vsk->peer_shutdown = SHUTDOWN_MASK; - if (vsock_stream_has_data(vsk) <= 0) - sk->sk_state = SS_DISCONNECTING; - sk->sk_state_change(sk); - break; - default: - err = -EINVAL; - break; - } - - virtio_transport_free_pkt(pkt); - return err; -} - -static int -virtio_transport_send_response(struct vsock_sock *vsk, - struct virtio_vsock_pkt *pkt) -{ - struct virtio_transport *trans = vsk->trans; - struct virtio_vsock_pkt_info info = { - .op = VIRTIO_VSOCK_OP_RESPONSE, - .type = VIRTIO_VSOCK_TYPE_STREAM, - .remote_cid = le32_to_cpu(pkt->hdr.src_cid), - .remote_port = le32_to_cpu(pkt->hdr.src_port), - }; - u32 cookie; - - cookie = virtio_vsock_secure_cookie(le32_to_cpu(pkt->hdr.src_cid), - le32_to_cpu(pkt->hdr.dst_cid), - le32_to_cpu(pkt->hdr.src_port), - le32_to_cpu(pkt->hdr.dst_port), - jiffies / (HZ * 60)); - info.flags = cpu_to_le32(cookie); - - pr_debug("%s: send_response, cookie=%x\n", __func__, le32_to_cpu(cookie)); - - return trans->ops->send_pkt(vsk, &info); -} - -/* Handle server socket */ -static int -virtio_transport_recv_listen(struct sock *sk, struct virtio_vsock_pkt *pkt) -{ - struct vsock_sock *vsk = vsock_sk(sk); - struct vsock_sock *vpending; - struct sock *pending; - int err; - u32 cookie; - - switch (le16_to_cpu(pkt->hdr.op)) { - case VIRTIO_VSOCK_OP_REQUEST: - err = virtio_transport_send_response(vsk, pkt); - if (err < 0) { - // FIXME vsk should be vpending - virtio_transport_send_reset(vsk, pkt); - return err; - } - break; - case VIRTIO_VSOCK_OP_ACK: - cookie = le32_to_cpu(pkt->hdr.flags); - err = virtio_vsock_check_cookie(le32_to_cpu(pkt->hdr.src_cid), - le32_to_cpu(pkt->hdr.dst_cid), - le32_to_cpu(pkt->hdr.src_port), - le32_to_cpu(pkt->hdr.dst_port), - jiffies / (HZ * 60), - le32_to_cpu(pkt->hdr.flags), - VSOCK_TIMEOUT_INIT); - pr_debug("%s: cookie=%x, err=%d\n", __func__, cookie, err); - if (err) - return err; - - /* So no pending socket are responsible for this pkt, create one */ - pr_debug("%s: create pending\n", __func__); - pending = __vsock_create(sock_net(sk), NULL, sk, GFP_KERNEL, - sk->sk_type, 0); - if (!pending) { - virtio_transport_send_reset(vsk, pkt); - return -ENOMEM; - } - sk->sk_ack_backlog++; - pending->sk_state = SS_CONNECTING; - - vpending = vsock_sk(pending); - vsock_addr_init(&vpending->local_addr, le32_to_cpu(pkt->hdr.dst_cid), - le32_to_cpu(pkt->hdr.dst_port)); - vsock_addr_init(&vpending->remote_addr, le32_to_cpu(pkt->hdr.src_cid), - le32_to_cpu(pkt->hdr.src_port)); - vsock_add_pending(sk, pending); - - pr_debug("%s: get pending\n", __func__); - pending = virtio_transport_get_pending(sk, pkt); - vpending = vsock_sk(pending); - lock_sock(pending); - switch (pending->sk_state) { - case SS_CONNECTING: - if (le16_to_cpu(pkt->hdr.op) != VIRTIO_VSOCK_OP_ACK) { - pr_debug("%s: op=%d != OP_ACK\n", __func__, - le16_to_cpu(pkt->hdr.op)); - virtio_transport_send_reset(vpending, pkt); - pending->sk_err = EPROTO; - pending->sk_state = SS_UNCONNECTED; - sock_put(pending); - } else { - pending->sk_state = SS_CONNECTED; - vsock_insert_connected(vpending); - - vsock_remove_pending(sk, pending); - vsock_enqueue_accept(sk, pending); - - sk->sk_data_ready(sk); - } - err = 0; - break; - default: - pr_debug("%s: sk->sk_ack_backlog=%d\n", __func__, - sk->sk_ack_backlog); - virtio_transport_send_reset(vpending, pkt); - err = -EINVAL; - break; - } - if (err < 0) - vsock_remove_pending(sk, pending); - release_sock(pending); - - /* Release refcnt obtained in virtio_transport_get_pending */ - sock_put(pending); - break; - default: - break; - } - - return 0; -} - -static void virtio_transport_space_update(struct sock *sk, - struct virtio_vsock_pkt *pkt) -{ - struct vsock_sock *vsk = vsock_sk(sk); - struct virtio_transport *trans = vsk->trans; - bool space_available; - - /* buf_alloc and fwd_cnt is always included in the hdr */ - mutex_lock(&trans->tx_lock); - trans->peer_buf_alloc = le32_to_cpu(pkt->hdr.buf_alloc); - trans->peer_fwd_cnt = le32_to_cpu(pkt->hdr.fwd_cnt); - space_available = virtio_transport_has_space(vsk); - mutex_unlock(&trans->tx_lock); - - if (space_available) - sk->sk_write_space(sk); -} - -/* We are under the virtio-vsock's vsock->rx_lock or - * vhost-vsock's vq->mutex lock */ -void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt) -{ - struct virtio_transport *trans; - struct sockaddr_vm src, dst; - struct vsock_sock *vsk; - struct sock *sk; - - vsock_addr_init(&src, le32_to_cpu(pkt->hdr.src_cid), le32_to_cpu(pkt->hdr.src_port)); - vsock_addr_init(&dst, le32_to_cpu(pkt->hdr.dst_cid), le32_to_cpu(pkt->hdr.dst_port)); - - virtio_vsock_dumppkt(__func__, pkt); - - if (le16_to_cpu(pkt->hdr.type) == VIRTIO_VSOCK_TYPE_DGRAM) { - sk = vsock_find_unbound_socket(&dst); - if (!sk) - goto free_pkt; - - vsk = vsock_sk(sk); - trans = vsk->trans; - BUG_ON(!trans); - - virtio_transport_space_update(sk, pkt); - - lock_sock(sk); - switch (le16_to_cpu(pkt->hdr.op)) { - case VIRTIO_VSOCK_OP_CREDIT_UPDATE: - virtio_transport_free_pkt(pkt); - break; - case VIRTIO_VSOCK_OP_CREDIT_REQUEST: - virtio_transport_send_credit_update(vsk, VIRTIO_VSOCK_TYPE_DGRAM, - &pkt->hdr); - virtio_transport_free_pkt(pkt); - break; - case VIRTIO_VSOCK_OP_RW: - virtio_transport_recv_dgram(sk, pkt); - break; - default: - virtio_transport_free_pkt(pkt); - break; - } - release_sock(sk); - - /* Release refcnt obtained when we fetched this socket out of - * the unbound list. - */ - sock_put(sk); - return; - } else if (le16_to_cpu(pkt->hdr.type) == VIRTIO_VSOCK_TYPE_STREAM) { - /* The socket must be in connected or bound table - * otherwise send reset back - */ - sk = vsock_find_connected_socket(&src, &dst); - if (!sk) { - sk = vsock_find_bound_socket(&dst); - if (!sk) { - pr_debug("%s: can not find bound_socket\n", __func__); - virtio_vsock_dumppkt(__func__, pkt); - /* Ignore this pkt instead of sending reset back */ - /* TODO send a RST unless this packet is a RST (to avoid infinite loops) */ - goto free_pkt; - } - } - - vsk = vsock_sk(sk); - trans = vsk->trans; - BUG_ON(!trans); - - virtio_transport_space_update(sk, pkt); - - lock_sock(sk); - switch (sk->sk_state) { - case VSOCK_SS_LISTEN: - virtio_transport_recv_listen(sk, pkt); - virtio_transport_free_pkt(pkt); - break; - case SS_CONNECTING: - virtio_transport_recv_connecting(sk, pkt); - virtio_transport_free_pkt(pkt); - break; - case SS_CONNECTED: - virtio_transport_recv_connected(sk, pkt); - break; - default: - virtio_transport_free_pkt(pkt); - break; - } - release_sock(sk); - - /* Release refcnt obtained when we fetched this socket out of the - * bound or connected list. - */ - sock_put(sk); - } - return; - -free_pkt: - virtio_transport_free_pkt(pkt); -} -EXPORT_SYMBOL_GPL(virtio_transport_recv_pkt); - -void virtio_transport_free_pkt(struct virtio_vsock_pkt *pkt) -{ - kfree(pkt->buf); - kfree(pkt); -} -EXPORT_SYMBOL_GPL(virtio_transport_free_pkt); - -static int __init virtio_vsock_common_init(void) -{ - get_random_bytes(vsockcookie_secret, sizeof(vsockcookie_secret)); - return 0; -} - -static void __exit virtio_vsock_common_exit(void) -{ -} - -module_init(virtio_vsock_common_init); -module_exit(virtio_vsock_common_exit); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Asias He"); -MODULE_DESCRIPTION("common code for virtio vsock"); -- cgit v1.2.3-59-g8ed1b From 9cbe9fd5214e18e6b6039136f83acf4a2695c608 Mon Sep 17 00:00:00 2001 From: yankejian Date: Tue, 8 Dec 2015 11:02:31 +0800 Subject: net: hns: optimize XGE capability by reducing cpu usage here is the patch raising the performance of XGE by: 1)changes the way page management method for enet momery, and 2)reduces the count of rmb, and 3)adds Memory prefetching Signed-off-by: Kejian Yan Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.h | 5 +- drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 1 - drivers/net/ethernet/hisilicon/hns/hns_enet.c | 79 +++++++++++++++-------- 3 files changed, 55 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index d1f33166e0c3..6ca94dc3dda3 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -341,7 +341,8 @@ struct hnae_queue { void __iomem *io_base; phys_addr_t phy_base; struct hnae_ae_dev *dev; /* the device who use this queue */ - struct hnae_ring rx_ring, tx_ring; + struct hnae_ring rx_ring ____cacheline_internodealigned_in_smp; + struct hnae_ring tx_ring ____cacheline_internodealigned_in_smp; struct hnae_handle *handle; }; @@ -597,11 +598,9 @@ static inline void hnae_replace_buffer(struct hnae_ring *ring, int i, struct hnae_desc_cb *res_cb) { struct hnae_buf_ops *bops = ring->q->handle->bops; - struct hnae_desc_cb tmp_cb = ring->desc_cb[i]; bops->unmap_buffer(ring, &ring->desc_cb[i]); ring->desc_cb[i] = *res_cb; - *res_cb = tmp_cb; ring->desc[i].addr = (__le64)ring->desc_cb[i].dma; ring->desc[i].rx.ipoff_bnum_pid_flag = 0; } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index 77c6edbe2666..522b264866b4 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -341,7 +341,6 @@ void hns_ae_toggle_ring_irq(struct hnae_ring *ring, u32 mask) else flag = RCB_INT_FLAG_RX; - hns_rcb_int_clr_hw(ring->q, flag); hns_rcb_int_ctrl_hw(ring->q, flag, mask); } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index cad266339200..5a81dafd725e 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -33,6 +33,7 @@ #define RCB_IRQ_NOT_INITED 0 #define RCB_IRQ_INITED 1 +#define HNS_BUFFER_SIZE_2048 2048 #define BD_MAX_SEND_SIZE 8191 #define SKB_TMP_LEN(SKB) \ @@ -491,13 +492,51 @@ static unsigned int hns_nic_get_headlen(unsigned char *data, u32 flag, return max_size; } -static void -hns_nic_reuse_page(struct hnae_desc_cb *desc_cb, int tsize, int last_offset) +static void hns_nic_reuse_page(struct sk_buff *skb, int i, + struct hnae_ring *ring, int pull_len, + struct hnae_desc_cb *desc_cb) { + struct hnae_desc *desc; + int truesize, size; + int last_offset; + + desc = &ring->desc[ring->next_to_clean]; + size = le16_to_cpu(desc->rx.size); + +#if (PAGE_SIZE < 8192) + if (hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048) { + truesize = hnae_buf_size(ring); + } else { + truesize = ALIGN(size, L1_CACHE_BYTES); + last_offset = hnae_page_size(ring) - hnae_buf_size(ring); + } + +#else + truesize = ALIGN(size, L1_CACHE_BYTES); + last_offset = hnae_page_size(ring) - hnae_buf_size(ring); +#endif + + skb_add_rx_frag(skb, i, desc_cb->priv, desc_cb->page_offset + pull_len, + size - pull_len, truesize - pull_len); + /* avoid re-using remote pages,flag default unreuse */ if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) { +#if (PAGE_SIZE < 8192) + if (hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048) { + /* if we are only owner of page we can reuse it */ + if (likely(page_count(desc_cb->priv) == 1)) { + /* flip page offset to other buffer */ + desc_cb->page_offset ^= truesize; + + desc_cb->reuse_flag = 1; + /* bump ref count on page before it is given*/ + get_page(desc_cb->priv); + } + return; + } +#endif /* move offset up to the next cache line */ - desc_cb->page_offset += tsize; + desc_cb->page_offset += truesize; if (desc_cb->page_offset <= last_offset) { desc_cb->reuse_flag = 1; @@ -529,11 +568,10 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, struct hnae_desc *desc; struct hnae_desc_cb *desc_cb; unsigned char *va; - int bnum, length, size, i, truesize, last_offset; + int bnum, length, i; int pull_len; u32 bnum_flag; - last_offset = hnae_page_size(ring) - hnae_buf_size(ring); desc = &ring->desc[ring->next_to_clean]; desc_cb = &ring->desc_cb[ring->next_to_clean]; @@ -555,17 +593,12 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, return -ENOMEM; } + prefetchw(skb->data); length = le16_to_cpu(desc->rx.pkt_len); bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag); priv->ops.get_rxd_bnum(bnum_flag, &bnum); *out_bnum = bnum; - /* we will be copying header into skb->data in - * pskb_may_pull so it is in our interest to prefetch - * it now to avoid a possible cache miss - */ - prefetchw(skb->data); - if (length <= HNS_RX_HEAD_SIZE) { memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long))); @@ -588,13 +621,7 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, memcpy(__skb_put(skb, pull_len), va, ALIGN(pull_len, sizeof(long))); - size = le16_to_cpu(desc->rx.size); - truesize = ALIGN(size, L1_CACHE_BYTES); - skb_add_rx_frag(skb, 0, desc_cb->priv, - desc_cb->page_offset + pull_len, - size - pull_len, truesize - pull_len); - - hns_nic_reuse_page(desc_cb, truesize, last_offset); + hns_nic_reuse_page(skb, 0, ring, pull_len, desc_cb); ring_ptr_move_fw(ring, next_to_clean); if (unlikely(bnum >= (int)MAX_SKB_FRAGS)) { /* check err*/ @@ -604,13 +631,8 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data, for (i = 1; i < bnum; i++) { desc = &ring->desc[ring->next_to_clean]; desc_cb = &ring->desc_cb[ring->next_to_clean]; - size = le16_to_cpu(desc->rx.size); - truesize = ALIGN(size, L1_CACHE_BYTES); - skb_add_rx_frag(skb, i, desc_cb->priv, - desc_cb->page_offset, - size, truesize); - hns_nic_reuse_page(desc_cb, truesize, last_offset); + hns_nic_reuse_page(skb, i, ring, 0, desc_cb); ring_ptr_move_fw(ring, next_to_clean); } } @@ -750,9 +772,10 @@ recv: /* make all data has been write before submit */ if (recv_pkts < budget) { ex_num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); - rmb(); /*complete read rx ring bd number*/ + if (ex_num > clean_count) { num += ex_num - clean_count; + rmb(); /*complete read rx ring bd number*/ goto recv; } } @@ -849,8 +872,11 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data, bytes = 0; pkts = 0; - while (head != ring->next_to_clean) + while (head != ring->next_to_clean) { hns_nic_reclaim_one_desc(ring, &bytes, &pkts); + /* issue prefetch for next Tx descriptor */ + prefetch(&ring->desc_cb[ring->next_to_clean]); + } NETIF_TX_UNLOCK(ndev); @@ -926,6 +952,7 @@ static int hns_nic_common_poll(struct napi_struct *napi, int budget) ring_data->ring, 0); ring_data->fini_process(ring_data); + return 0; } return clean_complete; -- cgit v1.2.3-59-g8ed1b From 0bf9a7d6174f4fc4a9355ec0804d44cf0ff53e24 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Tue, 8 Dec 2015 10:09:11 +0530 Subject: cxgb4: Align rest of the ethtool get stats Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c | 146 ++++++++++----------- 1 file changed, 73 insertions(+), 73 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index a077f9476daf..2a61a42ab033 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -35,79 +35,79 @@ static void set_msglevel(struct net_device *dev, u32 val) } static const char stats_strings[][ETH_GSTRING_LEN] = { - "tx_octets_ok ", - "tx_frames_ok ", - "tx_broadcast_frames ", - "tx_multicast_frames ", - "tx_unicast_frames ", - "tx_error_frames ", - - "tx_frames_64 ", - "tx_frames_65_to_127 ", - "tx_frames_128_to_255 ", - "tx_frames_256_to_511 ", - "tx_frames_512_to_1023 ", - "tx_frames_1024_to_1518 ", - "tx_frames_1519_to_max ", - - "tx_frames_dropped ", - "tx_pause_frames ", - "tx_ppp0_frames ", - "tx_ppp1_frames ", - "tx_ppp2_frames ", - "tx_ppp3_frames ", - "tx_ppp4_frames ", - "tx_ppp5_frames ", - "tx_ppp6_frames ", - "tx_ppp7_frames ", - - "rx_octets_ok ", - "rx_frames_ok ", - "rx_broadcast_frames ", - "rx_multicast_frames ", - "rx_unicast_frames ", - - "rx_frames_too_long ", - "rx_jabber_errors ", - "rx_fcs_errors ", - "rx_length_errors ", - "rx_symbol_errors ", - "rx_runt_frames ", - - "rx_frames_64 ", - "rx_frames_65_to_127 ", - "rx_frames_128_to_255 ", - "rx_frames_256_to_511 ", - "rx_frames_512_to_1023 ", - "rx_frames_1024_to_1518 ", - "rx_frames_1519_to_max ", - - "rx_pause_frames ", - "rx_ppp0_frames ", - "rx_ppp1_frames ", - "rx_ppp2_frames ", - "rx_ppp3_frames ", - "rx_ppp4_frames ", - "rx_ppp5_frames ", - "rx_ppp6_frames ", - "rx_ppp7_frames ", - - "rx_bg0_frames_dropped ", - "rx_bg1_frames_dropped ", - "rx_bg2_frames_dropped ", - "rx_bg3_frames_dropped ", - "rx_bg0_frames_trunc ", - "rx_bg1_frames_trunc ", - "rx_bg2_frames_trunc ", - "rx_bg3_frames_trunc ", - - "tso ", - "tx_csum_offload ", - "rx_csum_good ", - "vlan_extractions ", - "vlan_insertions ", - "gro_packets ", - "gro_merged ", + "tx_octets_ok ", + "tx_frames_ok ", + "tx_broadcast_frames ", + "tx_multicast_frames ", + "tx_unicast_frames ", + "tx_error_frames ", + + "tx_frames_64 ", + "tx_frames_65_to_127 ", + "tx_frames_128_to_255 ", + "tx_frames_256_to_511 ", + "tx_frames_512_to_1023 ", + "tx_frames_1024_to_1518 ", + "tx_frames_1519_to_max ", + + "tx_frames_dropped ", + "tx_pause_frames ", + "tx_ppp0_frames ", + "tx_ppp1_frames ", + "tx_ppp2_frames ", + "tx_ppp3_frames ", + "tx_ppp4_frames ", + "tx_ppp5_frames ", + "tx_ppp6_frames ", + "tx_ppp7_frames ", + + "rx_octets_ok ", + "rx_frames_ok ", + "rx_broadcast_frames ", + "rx_multicast_frames ", + "rx_unicast_frames ", + + "rx_frames_too_long ", + "rx_jabber_errors ", + "rx_fcs_errors ", + "rx_length_errors ", + "rx_symbol_errors ", + "rx_runt_frames ", + + "rx_frames_64 ", + "rx_frames_65_to_127 ", + "rx_frames_128_to_255 ", + "rx_frames_256_to_511 ", + "rx_frames_512_to_1023 ", + "rx_frames_1024_to_1518 ", + "rx_frames_1519_to_max ", + + "rx_pause_frames ", + "rx_ppp0_frames ", + "rx_ppp1_frames ", + "rx_ppp2_frames ", + "rx_ppp3_frames ", + "rx_ppp4_frames ", + "rx_ppp5_frames ", + "rx_ppp6_frames ", + "rx_ppp7_frames ", + + "rx_bg0_frames_dropped ", + "rx_bg1_frames_dropped ", + "rx_bg2_frames_dropped ", + "rx_bg3_frames_dropped ", + "rx_bg0_frames_trunc ", + "rx_bg1_frames_trunc ", + "rx_bg2_frames_trunc ", + "rx_bg3_frames_trunc ", + + "tso ", + "tx_csum_offload ", + "rx_csum_good ", + "vlan_extractions ", + "vlan_insertions ", + "gro_packets ", + "gro_merged ", }; static char adapter_stats_strings[][ETH_GSTRING_LEN] = { -- cgit v1.2.3-59-g8ed1b From b8759e917e9c78ef35c10a79ce47c5889306aaa8 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Tue, 8 Dec 2015 10:09:12 +0530 Subject: cxgb4/cxgb4vf: update Kconfig file to include T6 adapter Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/Kconfig | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/Kconfig b/drivers/net/ethernet/chelsio/Kconfig index a79813a17b6e..4d187f22c48b 100644 --- a/drivers/net/ethernet/chelsio/Kconfig +++ b/drivers/net/ethernet/chelsio/Kconfig @@ -65,13 +65,14 @@ config CHELSIO_T3 will be called cxgb3. config CHELSIO_T4 - tristate "Chelsio Communications T4/T5 Ethernet support" + tristate "Chelsio Communications T4/T5/T6 Ethernet support" depends on PCI && (IPV6 || IPV6=n) select FW_LOADER select MDIO ---help--- - This driver supports Chelsio T4 and T5 based gigabit, 10Gb Ethernet - adapter and T5 based 40Gb Ethernet adapter. + This driver supports Chelsio T4, T5 & T6 based gigabit, 10Gb Ethernet + adapter and T5/T6 based 40Gb and T6 based 25Gb, 50Gb and 100Gb + Ethernet adapters. For general information about Chelsio and our products, visit our website at . @@ -85,7 +86,7 @@ config CHELSIO_T4 will be called cxgb4. config CHELSIO_T4_DCB - bool "Data Center Bridging (DCB) Support for Chelsio T4/T5 cards" + bool "Data Center Bridging (DCB) Support for Chelsio T4/T5/T6 cards" default n depends on CHELSIO_T4 && DCB ---help--- @@ -107,12 +108,12 @@ config CHELSIO_T4_FCOE If unsure, say N. config CHELSIO_T4VF - tristate "Chelsio Communications T4/T5 Virtual Function Ethernet support" + tristate "Chelsio Communications T4/T5/T6 Virtual Function Ethernet support" depends on PCI ---help--- - This driver supports Chelsio T4 and T5 based gigabit, 10Gb Ethernet - adapters and T5 based 40Gb Ethernet adapters with PCI-E SR-IOV Virtual - Functions. + This driver supports Chelsio T4, T5 & T6 based gigabit, 10Gb Ethernet + adapters and T5/T6 based 40Gb and T6 based 25Gb, 50Gb and 100Gb + Ethernet adapters with PCI-E SR-IOV Virtual Functions. For general information about Chelsio and our products, visit our website at . -- cgit v1.2.3-59-g8ed1b From 632be1942d084df89066986e74b56ff0110dd621 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Tue, 8 Dec 2015 10:09:13 +0530 Subject: cxgb4: Use ACCES_ONCE macro to read queue's consumer index Use helper macro ACCESS_ONCE() to load from the SGE status page to prevent the compiler loading multiple times. Based on original work by Mike Werner Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/sge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 48d8fbb1c220..1076c35a8bfd 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -406,7 +406,7 @@ static void free_tx_desc(struct adapter *adap, struct sge_txq *q, */ static inline int reclaimable(const struct sge_txq *q) { - int hw_cidx = ntohs(q->stat->cidx); + int hw_cidx = ntohs(ACCESS_ONCE(q->stat->cidx)); hw_cidx -= q->cidx; return hw_cidx < 0 ? hw_cidx + q->size : hw_cidx; } @@ -1320,7 +1320,7 @@ out_free: dev_kfree_skb_any(skb); */ static inline void reclaim_completed_tx_imm(struct sge_txq *q) { - int hw_cidx = ntohs(q->stat->cidx); + int hw_cidx = ntohs(ACCESS_ONCE(q->stat->cidx)); int reclaim = hw_cidx - q->cidx; if (reclaim < 0) -- cgit v1.2.3-59-g8ed1b From 126fca643e3f6ee6e26533366f77acdd9e07d2ec Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Tue, 8 Dec 2015 10:09:14 +0530 Subject: cxgb4: prevent simultaneous execution of service_ofldq() Change mutual exclusion mechanism to prevent multiple threads of execution from running in service_ofldq() at the same time. The old mechanism used an implicit guard on the down-call path and none on the restart path and wasn't working. This checking makes the mechanism explicit and is much easier to understand as a result. Based on original work by Casey Leedom Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 1 + drivers/net/ethernet/chelsio/cxgb4/sge.c | 56 ++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 55a47de544ea..d4076e8e2e0b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -618,6 +618,7 @@ struct sge_ofld_txq { /* state for an SGE offload Tx queue */ struct adapter *adap; struct sk_buff_head sendq; /* list of backpressured packets */ struct tasklet_struct qresume_tsk; /* restarts the queue */ + bool service_ofldq_running; /* service_ofldq() is processing sendq */ u8 full; /* the Tx ring is full */ unsigned long mapping_err; /* # of I/O MMU packet mapping errors */ } ____cacheline_aligned_in_smp; diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 1076c35a8bfd..e1ff7d8d64eb 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -1542,11 +1542,22 @@ static void ofldtxq_stop(struct sge_ofld_txq *q, struct sk_buff *skb) } /** - * service_ofldq - restart a suspended offload queue + * service_ofldq - service/restart a suspended offload queue * @q: the offload queue * - * Services an offload Tx queue by moving packets from its packet queue - * to the HW Tx ring. The function starts and ends with the queue locked. + * Services an offload Tx queue by moving packets from its Pending Send + * Queue to the Hardware TX ring. The function starts and ends with the + * Send Queue locked, but drops the lock while putting the skb at the + * head of the Send Queue onto the Hardware TX Ring. Dropping the lock + * allows more skbs to be added to the Send Queue by other threads. + * The packet being processed at the head of the Pending Send Queue is + * left on the queue in case we experience DMA Mapping errors, etc. + * and need to give up and restart later. + * + * service_ofldq() can be thought of as a task which opportunistically + * uses other threads execution contexts. We use the Offload Queue + * boolean "service_ofldq_running" to make sure that only one instance + * is ever running at a time ... */ static void service_ofldq(struct sge_ofld_txq *q) { @@ -1556,10 +1567,23 @@ static void service_ofldq(struct sge_ofld_txq *q) unsigned int written = 0; unsigned int flits, ndesc; + /* If another thread is currently in service_ofldq() processing the + * Pending Send Queue then there's nothing to do. Otherwise, flag + * that we're doing the work and continue. Examining/modifying + * the Offload Queue boolean "service_ofldq_running" must be done + * while holding the Pending Send Queue Lock. + */ + if (q->service_ofldq_running) + return; + q->service_ofldq_running = true; + while ((skb = skb_peek(&q->sendq)) != NULL && !q->full) { - /* - * We drop the lock but leave skb on sendq, thus retaining - * exclusive access to the state of the queue. + /* We drop the lock while we're working with the skb at the + * head of the Pending Send Queue. This allows more skbs to + * be added to the Pending Send Queue while we're working on + * this one. We don't need to lock to guard the TX Ring + * updates because only one thread of execution is ever + * allowed into service_ofldq() at a time. */ spin_unlock(&q->sendq.lock); @@ -1604,6 +1628,11 @@ static void service_ofldq(struct sge_ofld_txq *q) written = 0; } + /* Reacquire the Pending Send Queue Lock so we can unlink the + * skb we've just successfully transferred to the TX Ring and + * loop for the next skb which may be at the head of the + * Pending Send Queue. + */ spin_lock(&q->sendq.lock); __skb_unlink(skb, &q->sendq); if (is_ofld_imm(skb)) @@ -1611,6 +1640,11 @@ static void service_ofldq(struct sge_ofld_txq *q) } if (likely(written)) ring_tx_db(q->adap, &q->q, written); + + /*Indicate that no thread is processing the Pending Send Queue + * currently. + */ + q->service_ofldq_running = false; } /** @@ -1624,9 +1658,19 @@ static int ofld_xmit(struct sge_ofld_txq *q, struct sk_buff *skb) { skb->priority = calc_tx_flits_ofld(skb); /* save for restart */ spin_lock(&q->sendq.lock); + + /* Queue the new skb onto the Offload Queue's Pending Send Queue. If + * that results in this new skb being the only one on the queue, start + * servicing it. If there are other skbs already on the list, then + * either the queue is currently being processed or it's been stopped + * for some reason and it'll be restarted at a later time. Restart + * paths are triggered by events like experiencing a DMA Mapping Error + * or filling the Hardware TX Ring. + */ __skb_queue_tail(&q->sendq, skb); if (q->sendq.qlen == 1) service_ofldq(q); + spin_unlock(&q->sendq.lock); return NET_XMIT_SUCCESS; } -- cgit v1.2.3-59-g8ed1b From 8d0557d27d59e830b2f90cfcd06a4e4e20e5f11d Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Tue, 8 Dec 2015 10:09:15 +0530 Subject: cxgb4: Deal with wrap-around of queue for Work request The WR headers may not fit within one descriptor. So we need to deal with wrap-around here. Based on original patch by Pranjal Joshi Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/sge.c | 57 +++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index e1ff7d8d64eb..c72fe1e86029 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -1029,6 +1029,30 @@ static void inline_tx_skb(const struct sk_buff *skb, const struct sge_txq *q, *p = 0; } +static void *inline_tx_skb_header(const struct sk_buff *skb, + const struct sge_txq *q, void *pos, + int length) +{ + u64 *p; + int left = (void *)q->stat - pos; + + if (likely(length <= left)) { + memcpy(pos, skb->data, length); + pos += length; + } else { + memcpy(pos, skb->data, left); + memcpy(q->desc, skb->data + left, length - left); + pos = (void *)q->desc + (length - left); + } + /* 0-pad to multiple of 16 */ + p = PTR_ALIGN(pos, 8); + if ((uintptr_t)p & 8) { + *p = 0; + return p + 1; + } + return p; +} + /* * Figure out what HW csum a packet wants and return the appropriate control * bits. @@ -1561,9 +1585,11 @@ static void ofldtxq_stop(struct sge_ofld_txq *q, struct sk_buff *skb) */ static void service_ofldq(struct sge_ofld_txq *q) { - u64 *pos; + u64 *pos, *before, *end; int credits; struct sk_buff *skb; + struct sge_txq *txq; + unsigned int left; unsigned int written = 0; unsigned int flits, ndesc; @@ -1607,9 +1633,32 @@ static void service_ofldq(struct sge_ofld_txq *q) } else { int last_desc, hdr_len = skb_transport_offset(skb); - memcpy(pos, skb->data, hdr_len); - write_sgl(skb, &q->q, (void *)pos + hdr_len, - pos + flits, hdr_len, + /* The WR headers may not fit within one descriptor. + * So we need to deal with wrap-around here. + */ + before = (u64 *)pos; + end = (u64 *)pos + flits; + txq = &q->q; + pos = (void *)inline_tx_skb_header(skb, &q->q, + (void *)pos, + hdr_len); + if (before > (u64 *)pos) { + left = (u8 *)end - (u8 *)txq->stat; + end = (void *)txq->desc + left; + } + + /* If current position is already at the end of the + * ofld queue, reset the current to point to + * start of the queue and update the end ptr as well. + */ + if (pos == (u64 *)txq->stat) { + left = (u8 *)end - (u8 *)txq->stat; + end = (void *)txq->desc + left; + pos = (void *)txq->desc; + } + + write_sgl(skb, &q->q, (void *)pos, + end, hdr_len, (dma_addr_t *)skb->head); #ifdef CONFIG_NEED_DMA_MAP_STATE skb->dev = q->adap->port[0]; -- cgit v1.2.3-59-g8ed1b From 70055dd0640666a8ffe7c98e91f0b36fd380fe2a Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Tue, 8 Dec 2015 10:09:16 +0530 Subject: cxgb4: Add FL DMA mapping error and low counter Add Free List DMA Mapping Errors to SGE Queue info for Free Lists. Add Free List "Low" counter to count the number of times we see the number of pointers that we _think_ the hardware sees in the Free List below the Egress Threshold. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 2 ++ drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 6 ++++++ drivers/net/ethernet/chelsio/cxgb4/sge.c | 3 +++ 3 files changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index d4076e8e2e0b..e01e7228f607 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -483,6 +483,8 @@ struct sge_fl { /* SGE free-buffer queue state */ unsigned int pidx; /* producer index */ unsigned long alloc_failed; /* # of times buffer allocation failed */ unsigned long large_alloc_failed; + unsigned long mapping_err; /* # of RX Buffer DMA Mapping failures */ + unsigned long low; /* # of times momentarily starving */ unsigned long starving; /* RO fields */ unsigned int cntxt_id; /* SGE context id for the free list */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 4269944c5db5..0d579b192350 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -2325,6 +2325,8 @@ do { \ TL("TxMapErr:", mapping_err); RL("FLAllocErr:", fl.alloc_failed); RL("FLLrgAlcErr:", fl.large_alloc_failed); + RL("FLMapErr:", fl.mapping_err); + RL("FLLow:", fl.low); RL("FLStarving:", fl.starving); } else if (iscsi_idx < iscsi_entries) { @@ -2359,6 +2361,8 @@ do { \ RL("RxNoMem:", stats.nomem); RL("FLAllocErr:", fl.alloc_failed); RL("FLLrgAlcErr:", fl.large_alloc_failed); + RL("FLMapErr:", fl.mapping_err); + RL("FLLow:", fl.low); RL("FLStarving:", fl.starving); } else if (rdma_idx < rdma_entries) { @@ -2388,6 +2392,8 @@ do { \ RL("RxNoMem:", stats.nomem); RL("FLAllocErr:", fl.alloc_failed); RL("FLLrgAlcErr:", fl.large_alloc_failed); + RL("FLMapErr:", fl.mapping_err); + RL("FLLow:", fl.low); RL("FLStarving:", fl.starving); } else if (ciq_idx < ciq_entries) { diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index c72fe1e86029..8d35ce317f67 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -613,6 +613,7 @@ static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n, PCI_DMA_FROMDEVICE); if (unlikely(dma_mapping_error(adap->pdev_dev, mapping))) { __free_pages(pg, s->fl_pg_order); + q->mapping_err++; goto out; /* do not try small pages for this error */ } mapping |= RX_LARGE_PG_BUF; @@ -642,6 +643,7 @@ alloc_small_pages: PCI_DMA_FROMDEVICE); if (unlikely(dma_mapping_error(adap->pdev_dev, mapping))) { put_page(pg); + q->mapping_err++; goto out; } *d++ = cpu_to_be64(mapping); @@ -663,6 +665,7 @@ out: cred = q->avail - cred; if (unlikely(fl_starving(adap, q))) { smp_wmb(); + q->low++; set_bit(q->cntxt_id - adap->sge.egr_start, adap->sge.starving_fl); } -- cgit v1.2.3-59-g8ed1b From 76928c904313ba1fa73b68bee505bfa7ed708dfc Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Tue, 8 Dec 2015 10:09:17 +0530 Subject: cxgb4: Adds PCI device id for new T5 adapters Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h index 03ed00c49823..a8dda635456d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h @@ -162,6 +162,9 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN CH_PCI_ID_TABLE_FENTRY(0x5095), /* Custom T540-CR-SO */ CH_PCI_ID_TABLE_FENTRY(0x5096), /* Custom T580-CR */ CH_PCI_ID_TABLE_FENTRY(0x5097), /* Custom T520-KR */ + CH_PCI_ID_TABLE_FENTRY(0x5098), /* Custom 2x40G QSFP */ + CH_PCI_ID_TABLE_FENTRY(0x5099), /* Custom 2x40G QSFP */ + CH_PCI_ID_TABLE_FENTRY(0x509a), /* Custom T520-CR */ /* T6 adapters: */ -- cgit v1.2.3-59-g8ed1b From 4d6acb62d2db5c1cc533c9a0b6e418ef9f63642e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 8 Dec 2015 05:54:40 -0800 Subject: bnx2x: avoid soft lockup in bnx2x_poll() Under heavy TX load, bnx2x_poll() can loop forever and trigger soft lockup bugs. A napi poll handler must yield after one TX completion round, risk of livelock is too high otherwise. Bug is very easy to trigger using a debug build, and udp flood, because of added cpu cycles in TX completion, and we do not receive enough packets to break the loop. Reported-by: Willem de Bruijn Signed-off-by: Eric Dumazet Cc: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 51 ++++++++++--------------- 1 file changed, 21 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index f6634a524153..d7cc2aa7c37a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -3204,42 +3204,32 @@ int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state) */ static int bnx2x_poll(struct napi_struct *napi, int budget) { - int work_done = 0; - u8 cos; struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath, napi); struct bnx2x *bp = fp->bp; + int rx_work_done; + u8 cos; - while (1) { #ifdef BNX2X_STOP_ON_ERROR - if (unlikely(bp->panic)) { - napi_complete(napi); - return 0; - } + if (unlikely(bp->panic)) { + napi_complete(napi); + return 0; + } #endif - for_each_cos_in_tx_queue(fp, cos) - if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos])) - bnx2x_tx_int(bp, fp->txdata_ptr[cos]); - - if (bnx2x_has_rx_work(fp)) { - work_done += bnx2x_rx_int(fp, budget - work_done); - - /* must not complete if we consumed full budget */ - if (work_done >= budget) - break; - } + for_each_cos_in_tx_queue(fp, cos) + if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos])) + bnx2x_tx_int(bp, fp->txdata_ptr[cos]); - /* Fall out from the NAPI loop if needed */ - if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) { + rx_work_done = (bnx2x_has_rx_work(fp)) ? bnx2x_rx_int(fp, budget) : 0; - /* No need to update SB for FCoE L2 ring as long as - * it's connected to the default SB and the SB - * has been updated when NAPI was scheduled. - */ - if (IS_FCOE_FP(fp)) { - napi_complete(napi); - break; - } + if (rx_work_done < budget) { + /* No need to update SB for FCoE L2 ring as long as + * it's connected to the default SB and the SB + * has been updated when NAPI was scheduled. + */ + if (IS_FCOE_FP(fp)) { + napi_complete(napi); + } else { bnx2x_update_fpsb_idx(fp); /* bnx2x_has_rx_work() reads the status block, * thus we need to ensure that status block indices @@ -3264,12 +3254,13 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) bnx2x_ack_sb(bp, fp->igu_sb_id, USTORM_ID, le16_to_cpu(fp->fp_hc_idx), IGU_INT_ENABLE, 1); - break; + } else { + rx_work_done = budget; } } } - return work_done; + return rx_work_done; } /* we split the first BD into headers and data BDs -- cgit v1.2.3-59-g8ed1b From 5abe255877d7a928077b0d84a428d03042c5ccc9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 8 Dec 2015 10:28:30 -0800 Subject: bnx2x: remove rx_pkt/rx_calls These fields are updated but never read. Remove the overhead. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 2 -- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 4 ---- 2 files changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 0b214b5d944a..cae0956186ce 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -590,8 +590,6 @@ struct bnx2x_fastpath { /* The last maximal completed SGE */ u16 last_max_sge; __le16 *rx_cons_sb; - unsigned long rx_pkt, - rx_calls; /* TPA related */ struct bnx2x_agg_info *tpa_info; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index d7cc2aa7c37a..b3552dd749c4 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -1122,9 +1122,6 @@ next_cqe: bnx2x_update_rx_prod(bp, fp, bd_prod_fw, sw_comp_prod, fp->rx_sge_prod); - fp->rx_pkt += rx_pkt; - fp->rx_calls++; - return rx_pkt; } @@ -4433,7 +4430,6 @@ static int bnx2x_alloc_rx_bds(struct bnx2x_fastpath *fp, /* Limit the CQE producer by the CQE ring size */ fp->rx_comp_prod = min_t(u16, NUM_RCQ_RINGS*RCQ_DESC_CNT, cqe_ring_prod); - fp->rx_pkt = fp->rx_calls = 0; bnx2x_fp_stats(bp, fp)->eth_q_stats.rx_skb_alloc_failed += failure_cnt; -- cgit v1.2.3-59-g8ed1b From 264a4aca531bca789d45b9d68158a423e9d54eaf Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 8 Dec 2015 21:42:09 +0100 Subject: chelsio: constify cmac_ops structures The cmac_ops structures are never modified, so declare them as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb/pm3393.c | 2 +- drivers/net/ethernet/chelsio/cxgb/vsc7326.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb/pm3393.c b/drivers/net/ethernet/chelsio/cxgb/pm3393.c index ec5e05052d99..eb462d7db427 100644 --- a/drivers/net/ethernet/chelsio/cxgb/pm3393.c +++ b/drivers/net/ethernet/chelsio/cxgb/pm3393.c @@ -570,7 +570,7 @@ static void pm3393_destroy(struct cmac *cmac) kfree(cmac); } -static struct cmac_ops pm3393_ops = { +static const struct cmac_ops pm3393_ops = { .destroy = pm3393_destroy, .reset = pm3393_reset, .interrupt_enable = pm3393_interrupt_enable, diff --git a/drivers/net/ethernet/chelsio/cxgb/vsc7326.c b/drivers/net/ethernet/chelsio/cxgb/vsc7326.c index b0cb388f5e12..6f30b6f78553 100644 --- a/drivers/net/ethernet/chelsio/cxgb/vsc7326.c +++ b/drivers/net/ethernet/chelsio/cxgb/vsc7326.c @@ -666,7 +666,7 @@ static void mac_destroy(struct cmac *mac) kfree(mac); } -static struct cmac_ops vsc7326_ops = { +static const struct cmac_ops vsc7326_ops = { .destroy = mac_destroy, .reset = mac_reset, .interrupt_handler = mac_intr_handler, -- cgit v1.2.3-59-g8ed1b From 1d3a1e69adde1f854d3fdeda64019b54189a390c Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 26 Nov 2015 16:49:33 +0200 Subject: Bluetooth: Use hexadecimal notation for mask Using hexadecimal notation for mask makes code easier to read Signed-off-by: Andrei Emeltchenko Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index db039f2ce655..fc1a499bca95 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -317,7 +317,7 @@ static void h5_handle_internal_rx(struct hci_uart *hu) h5_link_control(hu, conf_req, 3); } else if (memcmp(data, conf_rsp, 2) == 0) { if (H5_HDR_LEN(hdr) > 2) - h5->tx_win = (data[2] & 7); + h5->tx_win = (data[2] & 0x07); BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win); h5->state = H5_ACTIVE; hci_uart_init_ready(hu); -- cgit v1.2.3-59-g8ed1b From 742c59516822f4a4bc23b0961d88c569a7f1bf71 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 26 Nov 2015 16:49:34 +0200 Subject: Bluetooth: Simplify setting Configuration Field Only Sliding Window Size is used at the moment for H5 Bluetooth Configuration messages. Signed-off-by: Andrei Emeltchenko Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h5.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index fc1a499bca95..2d1c9f6a912c 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -116,12 +116,8 @@ static void h5_link_control(struct hci_uart *hu, const void *data, size_t len) static u8 h5_cfg_field(struct h5 *h5) { - u8 field = 0; - /* Sliding window size (first 3 bits) */ - field |= (h5->tx_win & 0x07); - - return field; + return h5->tx_win & 0x07; } static void h5_timed_event(unsigned long arg) -- cgit v1.2.3-59-g8ed1b From e6e981c74234a6f4e1414ba11ca9746850f261a9 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 27 Nov 2015 16:39:00 -0500 Subject: Bluetooth: hci_ldisc: Remove dead code The N_HCI ldisc does not define a flush_buffer() ldisc method, so the check when opening the ldisc is always false. Signed-off-by: Peter Hurley Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 03146d707a95..73202624133b 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -462,13 +462,7 @@ static int hci_uart_tty_open(struct tty_struct *tty) INIT_WORK(&hu->init_ready, hci_uart_init_work); INIT_WORK(&hu->write_work, hci_uart_write_work); - /* Flush any pending characters in the driver and line discipline. */ - - /* FIXME: why is this needed. Note don't use ldisc_ref here as the - open path is before the ldisc is referencable */ - - if (tty->ldisc->ops->flush_buffer) - tty->ldisc->ops->flush_buffer(tty); + /* Flush any pending characters in the driver */ tty_driver_flush_buffer(tty); return 0; -- cgit v1.2.3-59-g8ed1b From a0c38245153abe1fd844af9b166d1a5d5dafe7b1 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 27 Nov 2015 19:55:59 +0100 Subject: Bluetooth: hci_intel: Use shorter timeout for HCI commands Use the standard HCI_CMD_TIMEOUT(1s) for HCI command instead of HCI_INIT_TIMEOUT(10s) which is not justified in these cases. Signed-off-by: Loic Poulain Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_intel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index 69760e2850d9..3c28c4a6f380 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -502,7 +502,7 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed) /* Device will not accept speed change if Intel version has not been * previously requested. */ - skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT); + skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading Intel version information failed (%ld)", PTR_ERR(skb)); @@ -590,7 +590,7 @@ static int intel_setup(struct hci_uart *hu) * is in bootloader mode or if it already has operational firmware * loaded. */ - skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT); + skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading Intel version information failed (%ld)", PTR_ERR(skb)); @@ -671,7 +671,7 @@ static int intel_setup(struct hci_uart *hu) /* Read the secure boot parameters to identify the operating * details of the bootloader. */ - skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT); + skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_CMD_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading Intel boot parameters failed (%ld)", PTR_ERR(skb)); @@ -881,7 +881,7 @@ done: set_bit(STATE_BOOTING, &intel->flags); skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(reset_param), reset_param, - HCI_INIT_TIMEOUT); + HCI_CMD_TIMEOUT); if (IS_ERR(skb)) return PTR_ERR(skb); -- cgit v1.2.3-59-g8ed1b From 1623d0bf847d3b38d8cf24367b3689ba0e3fe2aa Mon Sep 17 00:00:00 2001 From: Dmitry Tunin Date: Sat, 5 Dec 2015 14:09:36 +0300 Subject: Bluetooth: Add support of Toshiba Broadcom based devices BugLink: https://bugs.launchpad.net/bugs/1522949 T: Bus=03 Lev=02 Prnt=02 Port=05 Cnt=02 Dev#= 4 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=ff(vend.) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0930 ProdID=0225 Rev=01.12 S: Manufacturer=Broadcom Corp S: Product=BCM43142A0 S: SerialNumber=4CBB58034671 C: #Ifs= 4 Cfg#= 1 Atr=e0 MxPwr=0mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=01 Prot=01 Driver=(none) I: If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=(none) I: If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none) I: If#= 3 Alt= 0 #EPs= 0 Cls=fe(app. ) Sub=01 Prot=01 Driver=(none) Signed-off-by: Dmitry Tunin Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- drivers/bluetooth/btusb.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 806353410eb7..25beb3a28eee 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -153,6 +153,10 @@ static const struct usb_device_id btusb_table[] = { { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01), .driver_info = BTUSB_BCM_PATCHRAM }, + /* Toshiba Corp - Broadcom based */ + { USB_VENDOR_AND_INTERFACE_INFO(0x0930, 0xff, 0x01, 0x01), + .driver_info = BTUSB_BCM_PATCHRAM }, + /* Intel Bluetooth USB Bootloader (RAM module) */ { USB_DEVICE(0x8087, 0x0a5a), .driver_info = BTUSB_INTEL_BOOT | BTUSB_BROKEN_ISOC }, -- cgit v1.2.3-59-g8ed1b From 28dc4b92e20e0fd18be2d8356abf959d58c7346a Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Thu, 3 Dec 2015 16:10:22 +0100 Subject: Bluetooth: btintel: Add manufacturing enter/exit helpers Older Intel controllers need to enter manufacturing mode to perform some vendor specific operations (patching, configuration...). Add enter/exit manufaturing methods and refactor existing manufacturing code. Exit can be configured to perform a reset. Reset can be performed either with patches activated or deactivated. Signed-off-by: Loic Poulain Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btintel.c | 114 +++++++++++++++++++++++--------------------- drivers/bluetooth/btintel.h | 12 +++++ drivers/bluetooth/btusb.c | 54 ++++++--------------- 3 files changed, 86 insertions(+), 94 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 1f13e617bf56..54410479f2f5 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -73,6 +73,48 @@ int btintel_check_bdaddr(struct hci_dev *hdev) } EXPORT_SYMBOL_GPL(btintel_check_bdaddr); +int btintel_enter_mfg(struct hci_dev *hdev) +{ + const u8 param[] = { 0x01, 0x00 }; + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Entering manufacturer mode failed (%ld)", + PTR_ERR(skb)); + return PTR_ERR(skb); + } + kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(btintel_enter_mfg); + +int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched) +{ + u8 param[] = { 0x00, 0x00 }; + struct sk_buff *skb; + + /* The 2nd command parameter specifies the manufacturing exit method: + * 0x00: Just disable the manufacturing mode (0x00). + * 0x01: Disable manufacturing mode and reset with patches deactivated. + * 0x02: Disable manufacturing mode and reset with patches activated. + */ + if (reset) + param[1] |= patched ? 0x02 : 0x01; + + skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Exiting manufacturer mode failed (%ld)", + PTR_ERR(skb)); + return PTR_ERR(skb); + } + kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(btintel_exit_mfg); + int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) { struct sk_buff *skb; @@ -126,37 +168,19 @@ EXPORT_SYMBOL_GPL(btintel_set_diag); int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable) { - struct sk_buff *skb; - u8 param[2]; - int err; - - param[0] = 0x01; - param[1] = 0x00; - - skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - BT_ERR("%s: Entering Intel manufacturer mode failed (%d)", - hdev->name, err); - return PTR_ERR(skb); - } - kfree_skb(skb); + int err, ret; - err = btintel_set_diag(hdev, enable); + err = btintel_enter_mfg(hdev); + if (err) + return err; - param[0] = 0x00; - param[1] = 0x00; + ret = btintel_set_diag(hdev, enable); - skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)", - hdev->name, err); - return PTR_ERR(skb); - } - kfree_skb(skb); + err = btintel_exit_mfg(hdev, false, false); + if (err) + return err; - return err; + return ret; } EXPORT_SYMBOL_GPL(btintel_set_diag_mfg); @@ -309,37 +333,19 @@ EXPORT_SYMBOL_GPL(btintel_set_event_mask); int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug) { - struct sk_buff *skb; - u8 param[2]; - int err; + int err, ret; - param[0] = 0x01; - param[1] = 0x00; - - skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - BT_ERR("%s: Entering Intel manufacturer mode failed (%d)", - hdev->name, err); - return PTR_ERR(skb); - } - kfree_skb(skb); - - err = btintel_set_event_mask(hdev, debug); + err = btintel_enter_mfg(hdev); + if (err) + return err; - param[0] = 0x00; - param[1] = 0x00; + ret = btintel_set_event_mask(hdev, debug); - skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)", - hdev->name, err); - return PTR_ERR(skb); - } - kfree_skb(skb); + err = btintel_exit_mfg(hdev, false, false); + if (err) + return err; - return err; + return ret; } EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg); diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 07e58e05a7fa..fa72eaec3461 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -72,6 +72,8 @@ struct intel_secure_send_result { #if IS_ENABLED(CONFIG_BT_INTEL) int btintel_check_bdaddr(struct hci_dev *hdev); +int btintel_enter_mfg(struct hci_dev *hdev); +int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched); int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr); int btintel_set_diag(struct hci_dev *hdev, bool enable); int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable); @@ -94,6 +96,16 @@ static inline int btintel_check_bdaddr(struct hci_dev *hdev) return -EOPNOTSUPP; } +static inline int btintel_enter_mfg(struct hci_dev *hdev) +{ + return -EOPNOTSUPP; +} + +static inline int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched) +{ + return -EOPNOTSUPP; +} + static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) { return -EOPNOTSUPP; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 25beb3a28eee..6e141cdb98a4 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1646,14 +1646,9 @@ static int btusb_setup_intel(struct hci_dev *hdev) struct sk_buff *skb; const struct firmware *fw; const u8 *fw_ptr; - int disable_patch; + int disable_patch, err; struct intel_version *ver; - const u8 mfg_enable[] = { 0x01, 0x00 }; - const u8 mfg_disable[] = { 0x00, 0x00 }; - const u8 mfg_reset_deactivate[] = { 0x00, 0x01 }; - const u8 mfg_reset_activate[] = { 0x00, 0x02 }; - BT_DBG("%s", hdev->name); /* The controller has a bug with the first HCI command sent to it @@ -1725,22 +1720,16 @@ static int btusb_setup_intel(struct hci_dev *hdev) kfree_skb(skb); - /* This Intel specific command enables the manufacturer mode of the - * controller. - * + /* Enable the manufacturer mode of the controller. * Only while this mode is enabled, the driver can download the * firmware patch data and configuration parameters. */ - skb = __hci_cmd_sync(hdev, 0xfc11, 2, mfg_enable, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - BT_ERR("%s entering Intel manufacturer mode failed (%ld)", - hdev->name, PTR_ERR(skb)); + err = btintel_enter_mfg(hdev); + if (err) { release_firmware(fw); - return PTR_ERR(skb); + return err; } - kfree_skb(skb); - disable_patch = 1; /* The firmware data file consists of list of Intel specific HCI @@ -1780,14 +1769,9 @@ static int btusb_setup_intel(struct hci_dev *hdev) /* Patching completed successfully and disable the manufacturer mode * with reset and activate the downloaded firmware patches. */ - skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_activate), - mfg_reset_activate, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - BT_ERR("%s exiting Intel manufacturer mode failed (%ld)", - hdev->name, PTR_ERR(skb)); - return PTR_ERR(skb); - } - kfree_skb(skb); + err = btintel_exit_mfg(hdev, true, true); + if (err) + return err; BT_INFO("%s: Intel Bluetooth firmware patch completed and activated", hdev->name); @@ -1796,14 +1780,9 @@ static int btusb_setup_intel(struct hci_dev *hdev) exit_mfg_disable: /* Disable the manufacturer mode without reset */ - skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_disable), mfg_disable, - HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - BT_ERR("%s exiting Intel manufacturer mode failed (%ld)", - hdev->name, PTR_ERR(skb)); - return PTR_ERR(skb); - } - kfree_skb(skb); + err = btintel_exit_mfg(hdev, false, false); + if (err) + return err; BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name); @@ -1815,14 +1794,9 @@ exit_mfg_deactivate: /* Patching failed. Disable the manufacturer mode with reset and * deactivate the downloaded firmware patches. */ - skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_deactivate), - mfg_reset_deactivate, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - BT_ERR("%s exiting Intel manufacturer mode failed (%ld)", - hdev->name, PTR_ERR(skb)); - return PTR_ERR(skb); - } - kfree_skb(skb); + err = btintel_exit_mfg(hdev, true, false); + if (err) + return err; BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated", hdev->name); -- cgit v1.2.3-59-g8ed1b From 6c483de1b3c41e939b735df1861759815e125304 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Sun, 6 Dec 2015 16:18:34 +0100 Subject: Bluetooth: btintel: Create common Intel Version Read function The Intel Version Read command is used to retrieve information about hardware and firmware version/revision of Intel Bluetooth controllers. This is an Intel generic command used in USB and UART drivers. Signed-off-by: Loic Poulain Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btintel.c | 25 ++++++++++++++ drivers/bluetooth/btintel.h | 7 ++++ drivers/bluetooth/btusb.c | 80 +++++++++++++------------------------------ drivers/bluetooth/hci_intel.c | 46 ++++++------------------- 4 files changed, 66 insertions(+), 92 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index 54410479f2f5..fce154855718 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -349,6 +349,31 @@ int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug) } EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg); +int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver) +{ + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Reading Intel version information failed (%ld)", + PTR_ERR(skb)); + return PTR_ERR(skb); + } + + if (skb->len != sizeof(*ver)) { + bt_dev_err(hdev, "Intel version event size mismatch"); + kfree_skb(skb); + return -EILSEQ; + } + + memcpy(ver, skb->data, sizeof(*ver)); + + kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(btintel_read_version); + /* ------- REGMAP IBT SUPPORT ------- */ #define IBT_REG_MODE_8BIT 0x00 diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index fa72eaec3461..1e8955aaafed 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -85,6 +85,7 @@ int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen, int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name); int btintel_set_event_mask(struct hci_dev *hdev, bool debug); int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug); +int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver); struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read, u16 opcode_write); @@ -152,6 +153,12 @@ static inline int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug) return -EOPNOTSUPP; } +static inline int btintel_read_version(struct hci_dev *hdev, + struct intel_version *ver) +{ + return -EOPNOTSUPP; +} + static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read, u16 opcode_write) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 6e141cdb98a4..a191e318fab8 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1647,7 +1647,7 @@ static int btusb_setup_intel(struct hci_dev *hdev) const struct firmware *fw; const u8 *fw_ptr; int disable_patch, err; - struct intel_version *ver; + struct intel_version ver; BT_DBG("%s", hdev->name); @@ -1673,35 +1673,22 @@ static int btusb_setup_intel(struct hci_dev *hdev) * The returned information are hardware variant and revision plus * firmware variant, revision and build number. */ - skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - BT_ERR("%s reading Intel fw version command failed (%ld)", - hdev->name, PTR_ERR(skb)); - return PTR_ERR(skb); - } - - if (skb->len != sizeof(*ver)) { - BT_ERR("%s Intel version event length mismatch", hdev->name); - kfree_skb(skb); - return -EIO; - } - - ver = (struct intel_version *)skb->data; + err = btintel_read_version(hdev, &ver); + if (err) + return err; BT_INFO("%s: read Intel version: %02x%02x%02x%02x%02x%02x%02x%02x%02x", - hdev->name, ver->hw_platform, ver->hw_variant, - ver->hw_revision, ver->fw_variant, ver->fw_revision, - ver->fw_build_num, ver->fw_build_ww, ver->fw_build_yy, - ver->fw_patch_num); + hdev->name, ver.hw_platform, ver.hw_variant, ver.hw_revision, + ver.fw_variant, ver.fw_revision, ver.fw_build_num, + ver.fw_build_ww, ver.fw_build_yy, ver.fw_patch_num); /* fw_patch_num indicates the version of patch the device currently * have. If there is no patch data in the device, it is always 0x00. * So, if it is other than 0x00, no need to patch the device again. */ - if (ver->fw_patch_num) { + if (ver.fw_patch_num) { BT_INFO("%s: Intel device is already patched. patch num: %02x", - hdev->name, ver->fw_patch_num); - kfree_skb(skb); + hdev->name, ver.fw_patch_num); goto complete; } @@ -1711,15 +1698,11 @@ static int btusb_setup_intel(struct hci_dev *hdev) * If no patch file is found, allow the device to operate without * a patch. */ - fw = btusb_setup_intel_get_fw(hdev, ver); - if (!fw) { - kfree_skb(skb); + fw = btusb_setup_intel_get_fw(hdev, &ver); + if (!fw) goto complete; - } fw_ptr = fw->data; - kfree_skb(skb); - /* Enable the manufacturer mode of the controller. * Only while this mode is enabled, the driver can download the * firmware patch data and configuration parameters. @@ -1983,7 +1966,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) 0x00, 0x08, 0x04, 0x00 }; struct btusb_data *data = hci_get_drvdata(hdev); struct sk_buff *skb; - struct intel_version *ver; + struct intel_version ver; struct intel_boot_params *params; const struct firmware *fw; const u8 *fw_ptr; @@ -2001,28 +1984,16 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) * is in bootloader mode or if it already has operational firmware * loaded. */ - skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - BT_ERR("%s: Reading Intel version information failed (%ld)", - hdev->name, PTR_ERR(skb)); - return PTR_ERR(skb); - } - - if (skb->len != sizeof(*ver)) { - BT_ERR("%s: Intel version event size mismatch", hdev->name); - kfree_skb(skb); - return -EILSEQ; - } - - ver = (struct intel_version *)skb->data; + err = btintel_read_version(hdev, &ver); + if (err) + return err; /* The hardware platform number has a fixed value of 0x37 and * for now only accept this single value. */ - if (ver->hw_platform != 0x37) { + if (ver.hw_platform != 0x37) { BT_ERR("%s: Unsupported Intel hardware platform (%u)", - hdev->name, ver->hw_platform); - kfree_skb(skb); + hdev->name, ver.hw_platform); return -EINVAL; } @@ -2031,14 +2002,13 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) * put in place to ensure correct forward compatibility options * when newer hardware variants come along. */ - if (ver->hw_variant != 0x0b) { + if (ver.hw_variant != 0x0b) { BT_ERR("%s: Unsupported Intel hardware variant (%u)", - hdev->name, ver->hw_variant); - kfree_skb(skb); + hdev->name, ver.hw_variant); return -EINVAL; } - btintel_version_info(hdev, ver); + btintel_version_info(hdev, &ver); /* The firmware variant determines if the device is in bootloader * mode or is running operational firmware. The value 0x06 identifies @@ -2053,8 +2023,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) * It is not possible to use the Secure Boot Parameters in this * case since that command is only available in bootloader mode. */ - if (ver->fw_variant == 0x23) { - kfree_skb(skb); + if (ver.fw_variant == 0x23) { clear_bit(BTUSB_BOOTLOADER, &data->flags); btintel_check_bdaddr(hdev); return 0; @@ -2063,15 +2032,12 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) /* If the device is not in bootloader mode, then the only possible * choice is to return an error and abort the device initialization. */ - if (ver->fw_variant != 0x06) { + if (ver.fw_variant != 0x06) { BT_ERR("%s: Unsupported Intel firmware variant (%u)", - hdev->name, ver->fw_variant); - kfree_skb(skb); + hdev->name, ver.fw_variant); return -ENODEV; } - kfree_skb(skb); - /* Read the secure boot parameters to identify the operating * details of the bootloader. */ diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index 3c28c4a6f380..3d63ea37bd4c 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -542,7 +542,7 @@ static int intel_setup(struct hci_uart *hu) struct intel_device *idev = NULL; struct hci_dev *hdev = hu->hdev; struct sk_buff *skb; - struct intel_version *ver; + struct intel_version ver; struct intel_boot_params *params; struct list_head *p; const struct firmware *fw; @@ -590,35 +590,16 @@ static int intel_setup(struct hci_uart *hu) * is in bootloader mode or if it already has operational firmware * loaded. */ - skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT); - if (IS_ERR(skb)) { - bt_dev_err(hdev, "Reading Intel version information failed (%ld)", - PTR_ERR(skb)); - return PTR_ERR(skb); - } - - if (skb->len != sizeof(*ver)) { - bt_dev_err(hdev, "Intel version event size mismatch"); - kfree_skb(skb); - return -EILSEQ; - } - - ver = (struct intel_version *)skb->data; - if (ver->status) { - bt_dev_err(hdev, "Intel version command failure (%02x)", - ver->status); - err = -bt_to_errno(ver->status); - kfree_skb(skb); + err = btintel_read_version(hdev, &ver); + if (err) return err; - } /* The hardware platform number has a fixed value of 0x37 and * for now only accept this single value. */ - if (ver->hw_platform != 0x37) { + if (ver.hw_platform != 0x37) { bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)", - ver->hw_platform); - kfree_skb(skb); + ver.hw_platform); return -EINVAL; } @@ -627,14 +608,13 @@ static int intel_setup(struct hci_uart *hu) * put in place to ensure correct forward compatibility options * when newer hardware variants come along. */ - if (ver->hw_variant != 0x0b) { + if (ver.hw_variant != 0x0b) { bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)", - ver->hw_variant); - kfree_skb(skb); + ver.hw_variant); return -EINVAL; } - btintel_version_info(hdev, ver); + btintel_version_info(hdev, &ver); /* The firmware variant determines if the device is in bootloader * mode or is running operational firmware. The value 0x06 identifies @@ -649,8 +629,7 @@ static int intel_setup(struct hci_uart *hu) * It is not possible to use the Secure Boot Parameters in this * case since that command is only available in bootloader mode. */ - if (ver->fw_variant == 0x23) { - kfree_skb(skb); + if (ver.fw_variant == 0x23) { clear_bit(STATE_BOOTLOADER, &intel->flags); btintel_check_bdaddr(hdev); return 0; @@ -659,15 +638,12 @@ static int intel_setup(struct hci_uart *hu) /* If the device is not in bootloader mode, then the only possible * choice is to return an error and abort the device initialization. */ - if (ver->fw_variant != 0x06) { + if (ver.fw_variant != 0x06) { bt_dev_err(hdev, "Unsupported Intel firmware variant (%u)", - ver->fw_variant); - kfree_skb(skb); + ver.fw_variant); return -ENODEV; } - kfree_skb(skb); - /* Read the secure boot parameters to identify the operating * details of the bootloader. */ -- cgit v1.2.3-59-g8ed1b From 87a6b9bd6306e0efd5eedbd68042eee36184dbd7 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 10 Dec 2015 17:03:43 +0200 Subject: Bluetooth: h5: Do not initialize Configuration field Initializing Configuration field in H5 Config message to 0x01 gives wrong impression that the value is used and needed. Later on the whole field is rewritten with h5_cfg_field(). Signed-off-by: Andrei Emeltchenko Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h5.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 2d1c9f6a912c..0879d64b1caf 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -123,7 +123,7 @@ static u8 h5_cfg_field(struct h5 *h5) static void h5_timed_event(unsigned long arg) { const unsigned char sync_req[] = { 0x01, 0x7e }; - unsigned char conf_req[] = { 0x03, 0xfc, 0x01 }; + unsigned char conf_req[3] = { 0x03, 0xfc }; struct hci_uart *hu = (struct hci_uart *)arg; struct h5 *h5 = hu->priv; struct sk_buff *skb; @@ -281,7 +281,7 @@ static void h5_handle_internal_rx(struct hci_uart *hu) struct h5 *h5 = hu->priv; const unsigned char sync_req[] = { 0x01, 0x7e }; const unsigned char sync_rsp[] = { 0x02, 0x7d }; - unsigned char conf_req[] = { 0x03, 0xfc, 0x01 }; + unsigned char conf_req[3] = { 0x03, 0xfc }; const unsigned char conf_rsp[] = { 0x04, 0x7b }; const unsigned char wakeup_req[] = { 0x05, 0xfa }; const unsigned char woken_req[] = { 0x06, 0xf9 }; -- cgit v1.2.3-59-g8ed1b From 4188146566a9f1d57dfce77fd9457f7304b69dfa Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 10 Dec 2015 23:44:34 +0100 Subject: ieee802154-atusb: Delete an unnecessary check before the function call "kfree_skb" The kfree_skb() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Acked-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/atusb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c index 199a94a9c8bc..b1cd865ade2e 100644 --- a/drivers/net/ieee802154/atusb.c +++ b/drivers/net/ieee802154/atusb.c @@ -310,8 +310,7 @@ static void atusb_free_urbs(struct atusb *atusb) urb = usb_get_from_anchor(&atusb->idle_urbs); if (!urb) break; - if (urb->context) - kfree_skb(urb->context); + kfree_skb(urb->context); usb_free_urb(urb); } } -- cgit v1.2.3-59-g8ed1b From 7302b9d90117496049dd4bfa28755f7c2ed55b27 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 11 Dec 2015 11:17:15 +0100 Subject: ieee802154/adf7242: Driver for ADF7242 MAC IEEE802154 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver has been sitting in the linux-zigbee[2] repository for a long time. We updated it from time to time and made it available via our github kernel repository. The Linux MAC802.15.4 support has improved a lot since then. Thanks to all! So it’s finally time to upstream this driver. The ADF7242 requires an add-on firmware for the automatic IEEE 802.15.4 operating modes. The firmware file is currently made available on the ADF7242 wiki page here [1] [1] http://wiki.analog.com/resources/tools-software/linux-drivers/networking-mac802154/adf7242 [2] http://sourceforge.net/p/linux-zigbee/kernel/ci/devel/tree/drivers/ieee802154/adf7242.c Signed-off-by: Michael Hennerich Reviewed-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- .../devicetree/bindings/net/ieee802154/adf7242.txt | 18 + MAINTAINERS | 9 + drivers/net/ieee802154/Kconfig | 11 + drivers/net/ieee802154/Makefile | 1 + drivers/net/ieee802154/adf7242.c | 1285 ++++++++++++++++++++ 5 files changed, 1324 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/ieee802154/adf7242.txt create mode 100644 drivers/net/ieee802154/adf7242.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/net/ieee802154/adf7242.txt b/Documentation/devicetree/bindings/net/ieee802154/adf7242.txt new file mode 100644 index 000000000000..dea5124cdc52 --- /dev/null +++ b/Documentation/devicetree/bindings/net/ieee802154/adf7242.txt @@ -0,0 +1,18 @@ +* ADF7242 IEEE 802.15.4 * + +Required properties: + - compatible: should be "adi,adf7242" + - spi-max-frequency: maximal bus speed (12.5 MHz) + - reg: the chipselect index + - interrupts: the interrupt generated by the device via pin IRQ1. + IRQ_TYPE_LEVEL_HIGH (4) or IRQ_TYPE_EDGE_FALLING (1) + +Example: + + adf7242@0 { + compatible = "adi,adf7242"; + spi-max-frequency = <10000000>; + reg = <0>; + interrupts = <98 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&gpio3>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 8eba565b2b46..4366c53dbb2b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -371,6 +371,15 @@ ADDRESS SPACE LAYOUT RANDOMIZATION (ASLR) M: Jiri Kosina S: Maintained +ADF7242 IEEE 802.15.4 RADIO DRIVER +M: Michael Hennerich +W: https://wiki.analog.com/ADF7242 +W: http://ez.analog.com/community/linux-device-drivers +L: linux-wpan@vger.kernel.org +S: Supported +F: drivers/net/ieee802154/adf7242.c +F: Documentation/devicetree/bindings/net/ieee802154/adf7242.txt + ADM1025 HARDWARE MONITOR DRIVER M: Jean Delvare L: lm-sensors@lm-sensors.org diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig index ce5f1a21e6d7..3057a8df4ce9 100644 --- a/drivers/net/ieee802154/Kconfig +++ b/drivers/net/ieee802154/Kconfig @@ -71,3 +71,14 @@ config IEEE802154_ATUSB This driver can also be built as a module. To do so say M here. The module will be called 'atusb'. + +config IEEE802154_ADF7242 + tristate "ADF7242 transceiver driver" + depends on IEEE802154_DRIVERS && MAC802154 + depends on SPI + ---help--- + Say Y here to enable the ADF7242 SPI 802.15.4 wireless + controller. + + This driver can also be built as a module. To do so, say M here. + the module will be called 'adf7242'. diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile index cf1d2a6db023..3a923d339497 100644 --- a/drivers/net/ieee802154/Makefile +++ b/drivers/net/ieee802154/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o obj-$(CONFIG_IEEE802154_ATUSB) += atusb.o +obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o diff --git a/drivers/net/ieee802154/adf7242.c b/drivers/net/ieee802154/adf7242.c new file mode 100644 index 000000000000..89154c079788 --- /dev/null +++ b/drivers/net/ieee802154/adf7242.c @@ -0,0 +1,1285 @@ +/* + * Analog Devices ADF7242 Low-Power IEEE 802.15.4 Transceiver + * + * Copyright 2009-2015 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + * + * http://www.analog.com/ADF7242 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FIRMWARE "adf7242_firmware.bin" +#define MAX_POLL_LOOPS 200 + +/* All Registers */ + +#define REG_EXT_CTRL 0x100 /* RW External LNA/PA and internal PA control */ +#define REG_TX_FSK_TEST 0x101 /* RW TX FSK test mode configuration */ +#define REG_CCA1 0x105 /* RW RSSI threshold for CCA */ +#define REG_CCA2 0x106 /* RW CCA mode configuration */ +#define REG_BUFFERCFG 0x107 /* RW RX_BUFFER overwrite control */ +#define REG_PKT_CFG 0x108 /* RW FCS evaluation configuration */ +#define REG_DELAYCFG0 0x109 /* RW RC_RX command to SFD or sync word delay */ +#define REG_DELAYCFG1 0x10A /* RW RC_TX command to TX state */ +#define REG_DELAYCFG2 0x10B /* RW Mac delay extension */ +#define REG_SYNC_WORD0 0x10C /* RW sync word bits [7:0] of [23:0] */ +#define REG_SYNC_WORD1 0x10D /* RW sync word bits [15:8] of [23:0] */ +#define REG_SYNC_WORD2 0x10E /* RW sync word bits [23:16] of [23:0] */ +#define REG_SYNC_CONFIG 0x10F /* RW sync word configuration */ +#define REG_RC_CFG 0x13E /* RW RX / TX packet configuration */ +#define REG_RC_VAR44 0x13F /* RW RESERVED */ +#define REG_CH_FREQ0 0x300 /* RW Channel Frequency Settings - Low */ +#define REG_CH_FREQ1 0x301 /* RW Channel Frequency Settings - Middle */ +#define REG_CH_FREQ2 0x302 /* RW Channel Frequency Settings - High */ +#define REG_TX_FD 0x304 /* RW TX Frequency Deviation Register */ +#define REG_DM_CFG0 0x305 /* RW RX Discriminator BW Register */ +#define REG_TX_M 0x306 /* RW TX Mode Register */ +#define REG_RX_M 0x307 /* RW RX Mode Register */ +#define REG_RRB 0x30C /* R RSSI Readback Register */ +#define REG_LRB 0x30D /* R Link Quality Readback Register */ +#define REG_DR0 0x30E /* RW bits [15:8] of [15:0] data rate setting */ +#define REG_DR1 0x30F /* RW bits [7:0] of [15:0] data rate setting */ +#define REG_PRAMPG 0x313 /* RW RESERVED */ +#define REG_TXPB 0x314 /* RW TX Packet Storage Base Address */ +#define REG_RXPB 0x315 /* RW RX Packet Storage Base Address */ +#define REG_TMR_CFG0 0x316 /* RW Wake up Timer Conf Register - High */ +#define REG_TMR_CFG1 0x317 /* RW Wake up Timer Conf Register - Low */ +#define REG_TMR_RLD0 0x318 /* RW Wake up Timer Value Register - High */ +#define REG_TMR_RLD1 0x319 /* RW Wake up Timer Value Register - Low */ +#define REG_TMR_CTRL 0x31A /* RW Wake up Timer Timeout flag */ +#define REG_PD_AUX 0x31E /* RW Battmon enable */ +#define REG_GP_CFG 0x32C /* RW GPIO Configuration */ +#define REG_GP_OUT 0x32D /* RW GPIO Configuration */ +#define REG_GP_IN 0x32E /* R GPIO Configuration */ +#define REG_SYNT 0x335 /* RW bandwidth calibration timers */ +#define REG_CAL_CFG 0x33D /* RW Calibration Settings */ +#define REG_PA_BIAS 0x36E /* RW PA BIAS */ +#define REG_SYNT_CAL 0x371 /* RW Oscillator and Doubler Configuration */ +#define REG_IIRF_CFG 0x389 /* RW BB Filter Decimation Rate */ +#define REG_CDR_CFG 0x38A /* RW CDR kVCO */ +#define REG_DM_CFG1 0x38B /* RW Postdemodulator Filter */ +#define REG_AGCSTAT 0x38E /* R RXBB Ref Osc Calibration Engine Readback */ +#define REG_RXCAL0 0x395 /* RW RX BB filter tuning, LSB */ +#define REG_RXCAL1 0x396 /* RW RX BB filter tuning, MSB */ +#define REG_RXFE_CFG 0x39B /* RW RXBB Ref Osc & RXFE Calibration */ +#define REG_PA_RR 0x3A7 /* RW Set PA ramp rate */ +#define REG_PA_CFG 0x3A8 /* RW PA enable */ +#define REG_EXTPA_CFG 0x3A9 /* RW External PA BIAS DAC */ +#define REG_EXTPA_MSC 0x3AA /* RW PA Bias Mode */ +#define REG_ADC_RBK 0x3AE /* R Readback temp */ +#define REG_AGC_CFG1 0x3B2 /* RW GC Parameters */ +#define REG_AGC_MAX 0x3B4 /* RW Slew rate */ +#define REG_AGC_CFG2 0x3B6 /* RW RSSI Parameters */ +#define REG_AGC_CFG3 0x3B7 /* RW RSSI Parameters */ +#define REG_AGC_CFG4 0x3B8 /* RW RSSI Parameters */ +#define REG_AGC_CFG5 0x3B9 /* RW RSSI & NDEC Parameters */ +#define REG_AGC_CFG6 0x3BA /* RW NDEC Parameters */ +#define REG_OCL_CFG1 0x3C4 /* RW OCL System Parameters */ +#define REG_IRQ1_EN0 0x3C7 /* RW Interrupt Mask set bits for IRQ1 */ +#define REG_IRQ1_EN1 0x3C8 /* RW Interrupt Mask set bits for IRQ1 */ +#define REG_IRQ2_EN0 0x3C9 /* RW Interrupt Mask set bits for IRQ2 */ +#define REG_IRQ2_EN1 0x3CA /* RW Interrupt Mask set bits for IRQ2 */ +#define REG_IRQ1_SRC0 0x3CB /* RW Interrupt Source bits for IRQ */ +#define REG_IRQ1_SRC1 0x3CC /* RW Interrupt Source bits for IRQ */ +#define REG_OCL_BW0 0x3D2 /* RW OCL System Parameters */ +#define REG_OCL_BW1 0x3D3 /* RW OCL System Parameters */ +#define REG_OCL_BW2 0x3D4 /* RW OCL System Parameters */ +#define REG_OCL_BW3 0x3D5 /* RW OCL System Parameters */ +#define REG_OCL_BW4 0x3D6 /* RW OCL System Parameters */ +#define REG_OCL_BWS 0x3D7 /* RW OCL System Parameters */ +#define REG_OCL_CFG13 0x3E0 /* RW OCL System Parameters */ +#define REG_GP_DRV 0x3E3 /* RW I/O pads Configuration and bg trim */ +#define REG_BM_CFG 0x3E6 /* RW Batt. Monitor Threshold Voltage setting */ +#define REG_SFD_15_4 0x3F4 /* RW Option to set non standard SFD */ +#define REG_AFC_CFG 0x3F7 /* RW AFC mode and polarity */ +#define REG_AFC_KI_KP 0x3F8 /* RW AFC ki and kp */ +#define REG_AFC_RANGE 0x3F9 /* RW AFC range */ +#define REG_AFC_READ 0x3FA /* RW Readback frequency error */ + +/* REG_EXTPA_MSC */ +#define PA_PWR(x) (((x) & 0xF) << 4) +#define EXTPA_BIAS_SRC BIT(3) +#define EXTPA_BIAS_MODE(x) (((x) & 0x7) << 0) + +/* REG_PA_CFG */ +#define PA_BRIDGE_DBIAS(x) (((x) & 0x1F) << 0) +#define PA_DBIAS_HIGH_POWER 21 +#define PA_DBIAS_LOW_POWER 13 + +/* REG_PA_BIAS */ +#define PA_BIAS_CTRL(x) (((x) & 0x1F) << 1) +#define REG_PA_BIAS_DFL BIT(0) +#define PA_BIAS_HIGH_POWER 63 +#define PA_BIAS_LOW_POWER 55 + +#define REG_PAN_ID0 0x112 +#define REG_PAN_ID1 0x113 +#define REG_SHORT_ADDR_0 0x114 +#define REG_SHORT_ADDR_1 0x115 +#define REG_IEEE_ADDR_0 0x116 +#define REG_IEEE_ADDR_1 0x117 +#define REG_IEEE_ADDR_2 0x118 +#define REG_IEEE_ADDR_3 0x119 +#define REG_IEEE_ADDR_4 0x11A +#define REG_IEEE_ADDR_5 0x11B +#define REG_IEEE_ADDR_6 0x11C +#define REG_IEEE_ADDR_7 0x11D +#define REG_FFILT_CFG 0x11E +#define REG_AUTO_CFG 0x11F +#define REG_AUTO_TX1 0x120 +#define REG_AUTO_TX2 0x121 +#define REG_AUTO_STATUS 0x122 + +/* REG_FFILT_CFG */ +#define ACCEPT_BEACON_FRAMES BIT(0) +#define ACCEPT_DATA_FRAMES BIT(1) +#define ACCEPT_ACK_FRAMES BIT(2) +#define ACCEPT_MACCMD_FRAMES BIT(3) +#define ACCEPT_RESERVED_FRAMES BIT(4) +#define ACCEPT_ALL_ADDRESS BIT(5) + +/* REG_AUTO_CFG */ +#define AUTO_ACK_FRAMEPEND BIT(0) +#define IS_PANCOORD BIT(1) +#define RX_AUTO_ACK_EN BIT(3) +#define CSMA_CA_RX_TURNAROUND BIT(4) + +/* REG_AUTO_TX1 */ +#define MAX_FRAME_RETRIES(x) ((x) & 0xF) +#define MAX_CCA_RETRIES(x) (((x) & 0x7) << 4) + +/* REG_AUTO_TX2 */ +#define CSMA_MAX_BE(x) ((x) & 0xF) +#define CSMA_MIN_BE(x) (((x) & 0xF) << 4) + +#define CMD_SPI_NOP 0xFF /* No operation. Use for dummy writes */ +#define CMD_SPI_PKT_WR 0x10 /* Write telegram to the Packet RAM + * starting from the TX packet base address + * pointer tx_packet_base + */ +#define CMD_SPI_PKT_RD 0x30 /* Read telegram from the Packet RAM + * starting from RX packet base address + * pointer rxpb.rx_packet_base + */ +#define CMD_SPI_MEM_WR(x) (0x18 + (x >> 8)) /* Write data to MCR or + * Packet RAM sequentially + */ +#define CMD_SPI_MEM_RD(x) (0x38 + (x >> 8)) /* Read data from MCR or + * Packet RAM sequentially + */ +#define CMD_SPI_MEMR_WR(x) (0x08 + (x >> 8)) /* Write data to MCR or Packet + * RAM as random block + */ +#define CMD_SPI_MEMR_RD(x) (0x28 + (x >> 8)) /* Read data from MCR or + * Packet RAM random block + */ +#define CMD_SPI_PRAM_WR 0x1E /* Write data sequentially to current + * PRAM page selected + */ +#define CMD_SPI_PRAM_RD 0x3E /* Read data sequentially from current + * PRAM page selected + */ +#define CMD_RC_SLEEP 0xB1 /* Invoke transition of radio controller + * into SLEEP state + */ +#define CMD_RC_IDLE 0xB2 /* Invoke transition of radio controller + * into IDLE state + */ +#define CMD_RC_PHY_RDY 0xB3 /* Invoke transition of radio controller + * into PHY_RDY state + */ +#define CMD_RC_RX 0xB4 /* Invoke transition of radio controller + * into RX state + */ +#define CMD_RC_TX 0xB5 /* Invoke transition of radio controller + * into TX state + */ +#define CMD_RC_MEAS 0xB6 /* Invoke transition of radio controller + * into MEAS state + */ +#define CMD_RC_CCA 0xB7 /* Invoke Clear channel assessment */ +#define CMD_RC_CSMACA 0xC1 /* initiates CSMA-CA channel access + * sequence and frame transmission + */ +#define CMD_RC_PC_RESET 0xC7 /* Program counter reset */ +#define CMD_RC_RESET 0xC8 /* Resets the ADF7242 and puts it in + * the sleep state + */ +#define CMD_RC_PC_RESET_NO_WAIT (CMD_RC_PC_RESET | BIT(31)) + +/* STATUS */ + +#define STAT_SPI_READY BIT(7) +#define STAT_IRQ_STATUS BIT(6) +#define STAT_RC_READY BIT(5) +#define STAT_CCA_RESULT BIT(4) +#define RC_STATUS_IDLE 1 +#define RC_STATUS_MEAS 2 +#define RC_STATUS_PHY_RDY 3 +#define RC_STATUS_RX 4 +#define RC_STATUS_TX 5 +#define RC_STATUS_MASK 0xF + +/* AUTO_STATUS */ + +#define SUCCESS 0 +#define SUCCESS_DATPEND 1 +#define FAILURE_CSMACA 2 +#define FAILURE_NOACK 3 +#define AUTO_STATUS_MASK 0x3 + +#define PRAM_PAGESIZE 256 + +/* IRQ1 */ + +#define IRQ_CCA_COMPLETE BIT(0) +#define IRQ_SFD_RX BIT(1) +#define IRQ_SFD_TX BIT(2) +#define IRQ_RX_PKT_RCVD BIT(3) +#define IRQ_TX_PKT_SENT BIT(4) +#define IRQ_FRAME_VALID BIT(5) +#define IRQ_ADDRESS_VALID BIT(6) +#define IRQ_CSMA_CA BIT(7) + +#define AUTO_TX_TURNAROUND BIT(3) +#define ADDON_EN BIT(4) + +#define FLAG_XMIT 0 +#define FLAG_START 1 + +#define ADF7242_REPORT_CSMA_CA_STAT 0 /* framework doesn't handle yet */ + +struct adf7242_local { + struct spi_device *spi; + struct completion tx_complete; + struct ieee802154_hw *hw; + struct mutex bmux; /* protect SPI messages */ + struct spi_message stat_msg; + struct spi_transfer stat_xfer; + struct dentry *debugfs_root; + unsigned long flags; + int tx_stat; + bool promiscuous; + s8 rssi; + u8 max_frame_retries; + u8 max_cca_retries; + u8 max_be; + u8 min_be; + + /* DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + + u8 buf[3] ____cacheline_aligned; + u8 buf_reg_tx[3]; + u8 buf_read_tx[4]; + u8 buf_read_rx[4]; + u8 buf_stat_rx; + u8 buf_stat_tx; + u8 buf_cmd; +}; + +static int adf7242_soft_reset(struct adf7242_local *lp, int line); + +static int adf7242_status(struct adf7242_local *lp, u8 *stat) +{ + int status; + + mutex_lock(&lp->bmux); + status = spi_sync(lp->spi, &lp->stat_msg); + *stat = lp->buf_stat_rx; + mutex_unlock(&lp->bmux); + + return status; +} + +static int adf7242_wait_status(struct adf7242_local *lp, unsigned status, + unsigned mask, int line) +{ + int cnt = 0, ret = 0; + u8 stat; + + do { + adf7242_status(lp, &stat); + cnt++; + } while (((stat & mask) != status) && (cnt < MAX_POLL_LOOPS)); + + if (cnt >= MAX_POLL_LOOPS) { + ret = -ETIMEDOUT; + + if (!(stat & STAT_RC_READY)) { + adf7242_soft_reset(lp, line); + adf7242_status(lp, &stat); + + if ((stat & mask) == status) + ret = 0; + } + + if (ret < 0) + dev_warn(&lp->spi->dev, + "%s:line %d Timeout status 0x%x (%d)\n", + __func__, line, stat, cnt); + } + + dev_vdbg(&lp->spi->dev, "%s : loops=%d line %d\n", __func__, cnt, line); + + return ret; +} + +static int adf7242_wait_ready(struct adf7242_local *lp, int line) +{ + return adf7242_wait_status(lp, STAT_RC_READY | STAT_SPI_READY, + STAT_RC_READY | STAT_SPI_READY, line); +} + +static int adf7242_write_fbuf(struct adf7242_local *lp, u8 *data, u8 len) +{ + u8 *buf = lp->buf; + int status; + struct spi_message msg; + struct spi_transfer xfer_head = { + .len = 2, + .tx_buf = buf, + + }; + struct spi_transfer xfer_buf = { + .len = len, + .tx_buf = data, + }; + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head, &msg); + spi_message_add_tail(&xfer_buf, &msg); + + adf7242_wait_ready(lp, __LINE__); + + mutex_lock(&lp->bmux); + buf[0] = CMD_SPI_PKT_WR; + buf[1] = len + 2; + + status = spi_sync(lp->spi, &msg); + mutex_unlock(&lp->bmux); + + return status; +} + +static int adf7242_read_fbuf(struct adf7242_local *lp, + u8 *data, size_t len, bool packet_read) +{ + u8 *buf = lp->buf; + int status; + struct spi_message msg; + struct spi_transfer xfer_head = { + .len = 3, + .tx_buf = buf, + .rx_buf = buf, + }; + struct spi_transfer xfer_buf = { + .len = len, + .rx_buf = data, + }; + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head, &msg); + spi_message_add_tail(&xfer_buf, &msg); + + adf7242_wait_ready(lp, __LINE__); + + mutex_lock(&lp->bmux); + if (packet_read) { + buf[0] = CMD_SPI_PKT_RD; + buf[1] = CMD_SPI_NOP; + buf[2] = 0; /* PHR */ + } else { + buf[0] = CMD_SPI_PRAM_RD; + buf[1] = 0; + buf[2] = CMD_SPI_NOP; + } + + status = spi_sync(lp->spi, &msg); + + mutex_unlock(&lp->bmux); + + return status; +} + +static int adf7242_read_reg(struct adf7242_local *lp, u16 addr, u8 *data) +{ + int status; + struct spi_message msg; + + struct spi_transfer xfer = { + .len = 4, + .tx_buf = lp->buf_read_tx, + .rx_buf = lp->buf_read_rx, + }; + + adf7242_wait_ready(lp, __LINE__); + + mutex_lock(&lp->bmux); + lp->buf_read_tx[0] = CMD_SPI_MEM_RD(addr); + lp->buf_read_tx[1] = addr; + lp->buf_read_tx[2] = CMD_SPI_NOP; + lp->buf_read_tx[3] = CMD_SPI_NOP; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + status = spi_sync(lp->spi, &msg); + if (msg.status) + status = msg.status; + + if (!status) + *data = lp->buf_read_rx[3]; + + mutex_unlock(&lp->bmux); + + dev_vdbg(&lp->spi->dev, "%s : REG 0x%X, VAL 0x%X\n", __func__, + addr, *data); + + return status; +} + +static int adf7242_write_reg(struct adf7242_local *lp, u16 addr, u8 data) +{ + int status; + + adf7242_wait_ready(lp, __LINE__); + + mutex_lock(&lp->bmux); + lp->buf_reg_tx[0] = CMD_SPI_MEM_WR(addr); + lp->buf_reg_tx[1] = addr; + lp->buf_reg_tx[2] = data; + status = spi_write(lp->spi, lp->buf_reg_tx, 3); + mutex_unlock(&lp->bmux); + + dev_vdbg(&lp->spi->dev, "%s : REG 0x%X, VAL 0x%X\n", + __func__, addr, data); + + return status; +} + +static int adf7242_cmd(struct adf7242_local *lp, unsigned cmd) +{ + int status; + + dev_vdbg(&lp->spi->dev, "%s : CMD=0x%X\n", __func__, cmd); + + if (cmd != CMD_RC_PC_RESET_NO_WAIT) + adf7242_wait_ready(lp, __LINE__); + + mutex_lock(&lp->bmux); + lp->buf_cmd = cmd; + status = spi_write(lp->spi, &lp->buf_cmd, 1); + mutex_unlock(&lp->bmux); + + return status; +} + +static int adf7242_upload_firmware(struct adf7242_local *lp, u8 *data, u16 len) +{ + struct spi_message msg; + struct spi_transfer xfer_buf = { }; + int status, i, page = 0; + u8 *buf = lp->buf; + + struct spi_transfer xfer_head = { + .len = 2, + .tx_buf = buf, + }; + + buf[0] = CMD_SPI_PRAM_WR; + buf[1] = 0; + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head, &msg); + spi_message_add_tail(&xfer_buf, &msg); + + for (i = len; i >= 0; i -= PRAM_PAGESIZE) { + adf7242_write_reg(lp, REG_PRAMPG, page); + + xfer_buf.len = (i >= PRAM_PAGESIZE) ? PRAM_PAGESIZE : i; + xfer_buf.tx_buf = &data[page * PRAM_PAGESIZE]; + + mutex_lock(&lp->bmux); + status = spi_sync(lp->spi, &msg); + mutex_unlock(&lp->bmux); + page++; + } + + return status; +} + +static int adf7242_verify_firmware(struct adf7242_local *lp, + const u8 *data, size_t len) +{ +#ifdef DEBUG + int i, j; + unsigned int page; + u8 *buf = kmalloc(PRAM_PAGESIZE, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + for (page = 0, i = len; i >= 0; i -= PRAM_PAGESIZE, page++) { + size_t nb = (i >= PRAM_PAGESIZE) ? PRAM_PAGESIZE : i; + + adf7242_write_reg(lp, REG_PRAMPG, page); + adf7242_read_fbuf(lp, buf, nb, false); + + for (j = 0; j < nb; j++) { + if (buf[j] != data[page * PRAM_PAGESIZE + j]) { + kfree(buf); + return -EIO; + } + } + } + kfree(buf); +#endif + return 0; +} + +static int adf7242_set_txpower(struct ieee802154_hw *hw, int mbm) +{ + struct adf7242_local *lp = hw->priv; + u8 pwr, bias_ctrl, dbias, tmp; + int db = mbm / 100; + + dev_vdbg(&lp->spi->dev, "%s : Power %d dB\n", __func__, db); + + if (db > 5 || db < -26) + return -EINVAL; + + db = DIV_ROUND_CLOSEST(db + 29, 2); + + if (db > 15) { + dbias = PA_DBIAS_HIGH_POWER; + bias_ctrl = PA_BIAS_HIGH_POWER; + } else { + dbias = PA_DBIAS_LOW_POWER; + bias_ctrl = PA_BIAS_LOW_POWER; + } + + pwr = clamp_t(u8, db, 3, 15); + + adf7242_read_reg(lp, REG_PA_CFG, &tmp); + tmp &= ~PA_BRIDGE_DBIAS(~0); + tmp |= PA_BRIDGE_DBIAS(dbias); + adf7242_write_reg(lp, REG_PA_CFG, tmp); + + adf7242_read_reg(lp, REG_PA_BIAS, &tmp); + tmp &= ~PA_BIAS_CTRL(~0); + tmp |= PA_BIAS_CTRL(bias_ctrl); + adf7242_write_reg(lp, REG_PA_BIAS, tmp); + + adf7242_read_reg(lp, REG_EXTPA_MSC, &tmp); + tmp &= ~PA_PWR(~0); + tmp |= PA_PWR(pwr); + + return adf7242_write_reg(lp, REG_EXTPA_MSC, tmp); +} + +static int adf7242_set_csma_params(struct ieee802154_hw *hw, u8 min_be, + u8 max_be, u8 retries) +{ + struct adf7242_local *lp = hw->priv; + int ret; + + dev_vdbg(&lp->spi->dev, "%s : min_be=%d max_be=%d retries=%d\n", + __func__, min_be, max_be, retries); + + if (min_be > max_be || max_be > 8 || retries > 5) + return -EINVAL; + + ret = adf7242_write_reg(lp, REG_AUTO_TX1, + MAX_FRAME_RETRIES(lp->max_frame_retries) | + MAX_CCA_RETRIES(retries)); + if (ret) + return ret; + + lp->max_cca_retries = retries; + lp->max_be = max_be; + lp->min_be = min_be; + + return adf7242_write_reg(lp, REG_AUTO_TX2, CSMA_MAX_BE(max_be) | + CSMA_MIN_BE(min_be)); +} + +static int adf7242_set_frame_retries(struct ieee802154_hw *hw, s8 retries) +{ + struct adf7242_local *lp = hw->priv; + int ret = 0; + + dev_vdbg(&lp->spi->dev, "%s : Retries = %d\n", __func__, retries); + + if (retries < -1 || retries > 15) + return -EINVAL; + + if (retries >= 0) + ret = adf7242_write_reg(lp, REG_AUTO_TX1, + MAX_FRAME_RETRIES(retries) | + MAX_CCA_RETRIES(lp->max_cca_retries)); + + lp->max_frame_retries = retries; + + return ret; +} + +static int adf7242_ed(struct ieee802154_hw *hw, u8 *level) +{ + struct adf7242_local *lp = hw->priv; + + *level = lp->rssi; + + dev_vdbg(&lp->spi->dev, "%s :Exit level=%d\n", + __func__, *level); + + return 0; +} + +static int adf7242_start(struct ieee802154_hw *hw) +{ + struct adf7242_local *lp = hw->priv; + + adf7242_cmd(lp, CMD_RC_PHY_RDY); + adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF); + enable_irq(lp->spi->irq); + set_bit(FLAG_START, &lp->flags); + + return adf7242_cmd(lp, CMD_RC_RX); +} + +static void adf7242_stop(struct ieee802154_hw *hw) +{ + struct adf7242_local *lp = hw->priv; + + adf7242_cmd(lp, CMD_RC_IDLE); + clear_bit(FLAG_START, &lp->flags); + disable_irq(lp->spi->irq); + adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF); +} + +static int adf7242_channel(struct ieee802154_hw *hw, u8 page, u8 channel) +{ + struct adf7242_local *lp = hw->priv; + unsigned long freq; + + dev_dbg(&lp->spi->dev, "%s :Channel=%d\n", __func__, channel); + + might_sleep(); + + WARN_ON(page != 0); + WARN_ON(channel < 11); + WARN_ON(channel > 26); + + freq = (2405 + 5 * (channel - 11)) * 100; + adf7242_cmd(lp, CMD_RC_PHY_RDY); + + adf7242_write_reg(lp, REG_CH_FREQ0, freq); + adf7242_write_reg(lp, REG_CH_FREQ1, freq >> 8); + adf7242_write_reg(lp, REG_CH_FREQ2, freq >> 16); + + return adf7242_cmd(lp, CMD_RC_RX); +} + +static int adf7242_set_hw_addr_filt(struct ieee802154_hw *hw, + struct ieee802154_hw_addr_filt *filt, + unsigned long changed) +{ + struct adf7242_local *lp = hw->priv; + u8 reg; + + dev_dbg(&lp->spi->dev, "%s :Changed=0x%lX\n", __func__, changed); + + might_sleep(); + + if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { + u8 addr[8], i; + + memcpy(addr, &filt->ieee_addr, 8); + + for (i = 0; i < 8; i++) + adf7242_write_reg(lp, REG_IEEE_ADDR_0 + i, addr[i]); + } + + if (changed & IEEE802154_AFILT_SADDR_CHANGED) { + u16 saddr = le16_to_cpu(filt->short_addr); + + adf7242_write_reg(lp, REG_SHORT_ADDR_0, saddr); + adf7242_write_reg(lp, REG_SHORT_ADDR_1, saddr >> 8); + } + + if (changed & IEEE802154_AFILT_PANID_CHANGED) { + u16 pan_id = le16_to_cpu(filt->pan_id); + + adf7242_write_reg(lp, REG_PAN_ID0, pan_id); + adf7242_write_reg(lp, REG_PAN_ID1, pan_id >> 8); + } + + if (changed & IEEE802154_AFILT_PANC_CHANGED) { + adf7242_read_reg(lp, REG_AUTO_CFG, ®); + if (filt->pan_coord) + reg |= IS_PANCOORD; + else + reg &= ~IS_PANCOORD; + adf7242_write_reg(lp, REG_AUTO_CFG, reg); + } + + return 0; +} + +static int adf7242_set_promiscuous_mode(struct ieee802154_hw *hw, bool on) +{ + struct adf7242_local *lp = hw->priv; + + dev_dbg(&lp->spi->dev, "%s : mode %d\n", __func__, on); + + lp->promiscuous = on; + + if (on) { + adf7242_write_reg(lp, REG_AUTO_CFG, 0); + return adf7242_write_reg(lp, REG_FFILT_CFG, + ACCEPT_BEACON_FRAMES | + ACCEPT_DATA_FRAMES | + ACCEPT_MACCMD_FRAMES | + ACCEPT_ALL_ADDRESS | + ACCEPT_ACK_FRAMES | + ACCEPT_RESERVED_FRAMES); + } else { + adf7242_write_reg(lp, REG_FFILT_CFG, + ACCEPT_BEACON_FRAMES | + ACCEPT_DATA_FRAMES | + ACCEPT_MACCMD_FRAMES | + ACCEPT_RESERVED_FRAMES); + + return adf7242_write_reg(lp, REG_AUTO_CFG, RX_AUTO_ACK_EN); + } +} + +static int adf7242_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm) +{ + struct adf7242_local *lp = hw->priv; + s8 level = clamp_t(s8, mbm / 100, S8_MIN, S8_MAX); + + dev_dbg(&lp->spi->dev, "%s : level %d\n", __func__, level); + + return adf7242_write_reg(lp, REG_CCA1, level); +} + +static int adf7242_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) +{ + struct adf7242_local *lp = hw->priv; + int ret; + + set_bit(FLAG_XMIT, &lp->flags); + reinit_completion(&lp->tx_complete); + adf7242_cmd(lp, CMD_RC_PHY_RDY); + + ret = adf7242_write_fbuf(lp, skb->data, skb->len); + if (ret) + goto err; + + ret = adf7242_cmd(lp, CMD_RC_CSMACA); + if (ret) + goto err; + + ret = wait_for_completion_interruptible_timeout(&lp->tx_complete, + HZ / 10); + if (ret < 0) + goto err; + if (ret == 0) { + dev_dbg(&lp->spi->dev, "Timeout waiting for TX interrupt\n"); + ret = -ETIMEDOUT; + goto err; + } + + if (lp->tx_stat != SUCCESS) { + dev_dbg(&lp->spi->dev, + "Error xmit: Retry count exceeded Status=0x%x\n", + lp->tx_stat); + ret = -ECOMM; + } else { + ret = 0; + } + +err: + clear_bit(FLAG_XMIT, &lp->flags); + adf7242_cmd(lp, CMD_RC_RX); + + return ret; +} + +static int adf7242_rx(struct adf7242_local *lp) +{ + struct sk_buff *skb; + size_t len; + int ret; + u8 lqi, len_u8, *data; + + adf7242_read_reg(lp, 0, &len_u8); + + len = len_u8; + + if (!ieee802154_is_valid_psdu_len(len)) { + dev_dbg(&lp->spi->dev, + "corrupted frame received len %d\n", (int)len); + len = IEEE802154_MTU; + } + + skb = dev_alloc_skb(len); + if (!skb) { + adf7242_cmd(lp, CMD_RC_RX); + return -ENOMEM; + } + + data = skb_put(skb, len); + ret = adf7242_read_fbuf(lp, data, len, true); + if (ret < 0) { + kfree_skb(skb); + adf7242_cmd(lp, CMD_RC_RX); + return ret; + } + + lqi = data[len - 2]; + lp->rssi = data[len - 1]; + + adf7242_cmd(lp, CMD_RC_RX); + + skb_trim(skb, len - 2); /* Don't put RSSI/LQI or CRC into the frame */ + + ieee802154_rx_irqsafe(lp->hw, skb, lqi); + + dev_dbg(&lp->spi->dev, "%s: ret=%d len=%d lqi=%d rssi=%d\n", + __func__, ret, (int)len, (int)lqi, lp->rssi); + + return 0; +} + +static struct ieee802154_ops adf7242_ops = { + .owner = THIS_MODULE, + .xmit_sync = adf7242_xmit, + .ed = adf7242_ed, + .set_channel = adf7242_channel, + .set_hw_addr_filt = adf7242_set_hw_addr_filt, + .start = adf7242_start, + .stop = adf7242_stop, + .set_csma_params = adf7242_set_csma_params, + .set_frame_retries = adf7242_set_frame_retries, + .set_txpower = adf7242_set_txpower, + .set_promiscuous_mode = adf7242_set_promiscuous_mode, + .set_cca_ed_level = adf7242_set_cca_ed_level, +}; + +static void adf7242_debug(u8 irq1) +{ +#ifdef DEBUG + u8 stat; + + adf7242_status(lp, &stat); + + dev_dbg(&lp->spi->dev, "%s IRQ1 = %X:\n%s%s%s%s%s%s%s%s\n", + __func__, irq1, + irq1 & IRQ_CCA_COMPLETE ? "IRQ_CCA_COMPLETE\n" : "", + irq1 & IRQ_SFD_RX ? "IRQ_SFD_RX\n" : "", + irq1 & IRQ_SFD_TX ? "IRQ_SFD_TX\n" : "", + irq1 & IRQ_RX_PKT_RCVD ? "IRQ_RX_PKT_RCVD\n" : "", + irq1 & IRQ_TX_PKT_SENT ? "IRQ_TX_PKT_SENT\n" : "", + irq1 & IRQ_CSMA_CA ? "IRQ_CSMA_CA\n" : "", + irq1 & IRQ_FRAME_VALID ? "IRQ_FRAME_VALID\n" : "", + irq1 & IRQ_ADDRESS_VALID ? "IRQ_ADDRESS_VALID\n" : ""); + + dev_dbg(&lp->spi->dev, "%s STATUS = %X:\n%s\n%s%s%s%s%s\n", + __func__, stat, + stat & STAT_RC_READY ? "RC_READY" : "RC_BUSY", + (stat & 0xf) == RC_STATUS_IDLE ? "RC_STATUS_IDLE" : "", + (stat & 0xf) == RC_STATUS_MEAS ? "RC_STATUS_MEAS" : "", + (stat & 0xf) == RC_STATUS_PHY_RDY ? "RC_STATUS_PHY_RDY" : "", + (stat & 0xf) == RC_STATUS_RX ? "RC_STATUS_RX" : "", + (stat & 0xf) == RC_STATUS_TX ? "RC_STATUS_TX" : ""); + } +#endif +} + +static irqreturn_t adf7242_isr(int irq, void *data) +{ + struct adf7242_local *lp = data; + unsigned xmit; + u8 irq1; + + adf7242_wait_status(lp, RC_STATUS_PHY_RDY, RC_STATUS_MASK, __LINE__); + + adf7242_read_reg(lp, REG_IRQ1_SRC1, &irq1); + adf7242_write_reg(lp, REG_IRQ1_SRC1, irq1); + + if (!(irq1 & (IRQ_RX_PKT_RCVD | IRQ_CSMA_CA))) + dev_err(&lp->spi->dev, "%s :ERROR IRQ1 = 0x%X\n", + __func__, irq1); + + adf7242_debug(irq1); + + xmit = test_bit(FLAG_XMIT, &lp->flags); + + if (xmit && (irq1 & IRQ_CSMA_CA)) { + if (ADF7242_REPORT_CSMA_CA_STAT) { + u8 astat; + + adf7242_read_reg(lp, REG_AUTO_STATUS, &astat); + astat &= AUTO_STATUS_MASK; + + dev_dbg(&lp->spi->dev, "AUTO_STATUS = %X:\n%s%s%s%s\n", + astat, + astat == SUCCESS ? "SUCCESS" : "", + astat == + SUCCESS_DATPEND ? "SUCCESS_DATPEND" : "", + astat == FAILURE_CSMACA ? "FAILURE_CSMACA" : "", + astat == FAILURE_NOACK ? "FAILURE_NOACK" : ""); + + /* save CSMA-CA completion status */ + lp->tx_stat = astat; + } else { + lp->tx_stat = SUCCESS; + } + complete(&lp->tx_complete); + } else if (!xmit && (irq1 & IRQ_RX_PKT_RCVD) && + (irq1 & IRQ_FRAME_VALID)) { + adf7242_rx(lp); + } else if (!xmit && test_bit(FLAG_START, &lp->flags)) { + /* Invalid packet received - drop it and restart */ + dev_dbg(&lp->spi->dev, "%s:%d : ERROR IRQ1 = 0x%X\n", + __func__, __LINE__, irq1); + adf7242_cmd(lp, CMD_RC_PHY_RDY); + adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF); + adf7242_cmd(lp, CMD_RC_RX); + } else { + /* This can only be xmit without IRQ, likely a RX packet. + * we get an TX IRQ shortly - do nothing or let the xmit + * timeout handle this + */ + dev_dbg(&lp->spi->dev, "%s:%d : ERROR IRQ1 = 0x%X, xmit %d\n", + __func__, __LINE__, irq1, xmit); + complete(&lp->tx_complete); + } + + return IRQ_HANDLED; +} + +static int adf7242_soft_reset(struct adf7242_local *lp, int line) +{ + dev_warn(&lp->spi->dev, "%s (line %d)\n", __func__, line); + + if (test_bit(FLAG_START, &lp->flags)) + disable_irq_nosync(lp->spi->irq); + + adf7242_cmd(lp, CMD_RC_PC_RESET_NO_WAIT); + usleep_range(200, 250); + adf7242_write_reg(lp, REG_PKT_CFG, ADDON_EN | BIT(2)); + adf7242_cmd(lp, CMD_RC_PHY_RDY); + adf7242_set_promiscuous_mode(lp->hw, lp->promiscuous); + adf7242_set_csma_params(lp->hw, lp->min_be, lp->max_be, + lp->max_cca_retries); + adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF); + + if (test_bit(FLAG_START, &lp->flags)) { + enable_irq(lp->spi->irq); + return adf7242_cmd(lp, CMD_RC_RX); + } + + return 0; +} + +static int adf7242_hw_init(struct adf7242_local *lp) +{ + int ret; + const struct firmware *fw; + + adf7242_cmd(lp, CMD_RC_RESET); + adf7242_cmd(lp, CMD_RC_IDLE); + + /* get ADF7242 addon firmware + * build this driver as module + * and place under /lib/firmware/adf7242_firmware.bin + * or compile firmware into the kernel. + */ + ret = request_firmware(&fw, FIRMWARE, &lp->spi->dev); + if (ret) { + dev_err(&lp->spi->dev, + "request_firmware() failed with %d\n", ret); + return ret; + } + + ret = adf7242_upload_firmware(lp, (u8 *)fw->data, fw->size); + if (ret) { + dev_err(&lp->spi->dev, + "upload firmware failed with %d\n", ret); + return ret; + } + + ret = adf7242_verify_firmware(lp, (u8 *)fw->data, fw->size); + if (ret) { + dev_err(&lp->spi->dev, + "verify firmware failed with %d\n", ret); + return ret; + } + + adf7242_cmd(lp, CMD_RC_PC_RESET); + + release_firmware(fw); + + adf7242_write_reg(lp, REG_FFILT_CFG, + ACCEPT_BEACON_FRAMES | + ACCEPT_DATA_FRAMES | + ACCEPT_MACCMD_FRAMES | + ACCEPT_RESERVED_FRAMES); + + adf7242_write_reg(lp, REG_AUTO_CFG, RX_AUTO_ACK_EN); + + adf7242_write_reg(lp, REG_PKT_CFG, ADDON_EN | BIT(2)); + + adf7242_write_reg(lp, REG_EXTPA_MSC, 0xF1); + adf7242_write_reg(lp, REG_RXFE_CFG, 0x1D); + + adf7242_write_reg(lp, REG_IRQ1_EN0, 0); + adf7242_write_reg(lp, REG_IRQ1_EN1, IRQ_RX_PKT_RCVD | IRQ_CSMA_CA); + + adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF); + adf7242_write_reg(lp, REG_IRQ1_SRC0, 0xFF); + + adf7242_cmd(lp, CMD_RC_IDLE); + + return 0; +} + +static int adf7242_stats_show(struct seq_file *file, void *offset) +{ + struct adf7242_local *lp = spi_get_drvdata(file->private); + u8 stat, irq1; + + adf7242_status(lp, &stat); + adf7242_read_reg(lp, REG_IRQ1_SRC1, &irq1); + + seq_printf(file, "IRQ1 = %X:\n%s%s%s%s%s%s%s%s\n", irq1, + irq1 & IRQ_CCA_COMPLETE ? "IRQ_CCA_COMPLETE\n" : "", + irq1 & IRQ_SFD_RX ? "IRQ_SFD_RX\n" : "", + irq1 & IRQ_SFD_TX ? "IRQ_SFD_TX\n" : "", + irq1 & IRQ_RX_PKT_RCVD ? "IRQ_RX_PKT_RCVD\n" : "", + irq1 & IRQ_TX_PKT_SENT ? "IRQ_TX_PKT_SENT\n" : "", + irq1 & IRQ_CSMA_CA ? "IRQ_CSMA_CA\n" : "", + irq1 & IRQ_FRAME_VALID ? "IRQ_FRAME_VALID\n" : "", + irq1 & IRQ_ADDRESS_VALID ? "IRQ_ADDRESS_VALID\n" : ""); + + seq_printf(file, "STATUS = %X:\n%s\n%s%s%s%s%s\n", stat, + stat & STAT_RC_READY ? "RC_READY" : "RC_BUSY", + (stat & 0xf) == RC_STATUS_IDLE ? "RC_STATUS_IDLE" : "", + (stat & 0xf) == RC_STATUS_MEAS ? "RC_STATUS_MEAS" : "", + (stat & 0xf) == RC_STATUS_PHY_RDY ? "RC_STATUS_PHY_RDY" : "", + (stat & 0xf) == RC_STATUS_RX ? "RC_STATUS_RX" : "", + (stat & 0xf) == RC_STATUS_TX ? "RC_STATUS_TX" : ""); + + seq_printf(file, "RSSI = %d\n", lp->rssi); + + return 0; +} + +static int adf7242_debugfs_init(struct adf7242_local *lp) +{ + char debugfs_dir_name[DNAME_INLINE_LEN + 1] = "adf7242-"; + struct dentry *stats; + + strncat(debugfs_dir_name, dev_name(&lp->spi->dev), DNAME_INLINE_LEN); + + lp->debugfs_root = debugfs_create_dir(debugfs_dir_name, NULL); + if (IS_ERR_OR_NULL(lp->debugfs_root)) + return PTR_ERR_OR_ZERO(lp->debugfs_root); + + stats = debugfs_create_devm_seqfile(&lp->spi->dev, "status", + lp->debugfs_root, + adf7242_stats_show); + return PTR_ERR_OR_ZERO(stats); + + return 0; +} + +static const s32 adf7242_powers[] = { + 500, 400, 300, 200, 100, 0, -100, -200, -300, -400, -500, -600, -700, + -800, -900, -1000, -1100, -1200, -1300, -1400, -1500, -1600, -1700, + -1800, -1900, -2000, -2100, -2200, -2300, -2400, -2500, -2600, +}; + +static const s32 adf7242_ed_levels[] = { + -9000, -8900, -8800, -8700, -8600, -8500, -8400, -8300, -8200, -8100, + -8000, -7900, -7800, -7700, -7600, -7500, -7400, -7300, -7200, -7100, + -7000, -6900, -6800, -6700, -6600, -6500, -6400, -6300, -6200, -6100, + -6000, -5900, -5800, -5700, -5600, -5500, -5400, -5300, -5200, -5100, + -5000, -4900, -4800, -4700, -4600, -4500, -4400, -4300, -4200, -4100, + -4000, -3900, -3800, -3700, -3600, -3500, -3400, -3200, -3100, -3000 +}; + +static int adf7242_probe(struct spi_device *spi) +{ + struct ieee802154_hw *hw; + struct adf7242_local *lp; + int ret, irq_type; + + if (!spi->irq) { + dev_err(&spi->dev, "no IRQ specified\n"); + return -EINVAL; + } + + hw = ieee802154_alloc_hw(sizeof(*lp), &adf7242_ops); + if (!hw) + return -ENOMEM; + + lp = hw->priv; + lp->hw = hw; + lp->spi = spi; + + hw->priv = lp; + hw->parent = &spi->dev; + hw->extra_tx_headroom = 0; + + /* We support only 2.4 Ghz */ + hw->phy->supported.channels[0] = 0x7FFF800; + + hw->flags = IEEE802154_HW_OMIT_CKSUM | + IEEE802154_HW_CSMA_PARAMS | + IEEE802154_HW_FRAME_RETRIES | IEEE802154_HW_AFILT | + IEEE802154_HW_PROMISCUOUS; + + hw->phy->flags = WPAN_PHY_FLAG_TXPOWER | + WPAN_PHY_FLAG_CCA_ED_LEVEL | + WPAN_PHY_FLAG_CCA_MODE; + + hw->phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY); + + hw->phy->supported.cca_ed_levels = adf7242_ed_levels; + hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(adf7242_ed_levels); + + hw->phy->cca.mode = NL802154_CCA_ENERGY; + + hw->phy->supported.tx_powers = adf7242_powers; + hw->phy->supported.tx_powers_size = ARRAY_SIZE(adf7242_powers); + + hw->phy->supported.min_minbe = 0; + hw->phy->supported.max_minbe = 8; + + hw->phy->supported.min_maxbe = 3; + hw->phy->supported.max_maxbe = 8; + + hw->phy->supported.min_frame_retries = 0; + hw->phy->supported.max_frame_retries = 15; + + hw->phy->supported.min_csma_backoffs = 0; + hw->phy->supported.max_csma_backoffs = 5; + + ieee802154_random_extended_addr(&hw->phy->perm_extended_addr); + + mutex_init(&lp->bmux); + init_completion(&lp->tx_complete); + + /* Setup Status Message */ + lp->stat_xfer.len = 1; + lp->stat_xfer.tx_buf = &lp->buf_stat_tx; + lp->stat_xfer.rx_buf = &lp->buf_stat_rx; + lp->buf_stat_tx = CMD_SPI_NOP; + + spi_message_init(&lp->stat_msg); + spi_message_add_tail(&lp->stat_xfer, &lp->stat_msg); + + spi_set_drvdata(spi, lp); + + ret = adf7242_hw_init(lp); + if (ret) + goto err_hw_init; + + irq_type = irq_get_trigger_type(spi->irq); + if (!irq_type) + irq_type = IRQF_TRIGGER_HIGH; + + ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, adf7242_isr, + irq_type | IRQF_ONESHOT, + dev_name(&spi->dev), lp); + if (ret) + goto err_hw_init; + + disable_irq(spi->irq); + + ret = ieee802154_register_hw(lp->hw); + if (ret) + goto err_hw_init; + + dev_set_drvdata(&spi->dev, lp); + + adf7242_debugfs_init(lp); + + dev_info(&spi->dev, "mac802154 IRQ-%d registered\n", spi->irq); + + return ret; + +err_hw_init: + mutex_destroy(&lp->bmux); + ieee802154_free_hw(lp->hw); + + return ret; +} + +static int adf7242_remove(struct spi_device *spi) +{ + struct adf7242_local *lp = spi_get_drvdata(spi); + + if (!IS_ERR_OR_NULL(lp->debugfs_root)) + debugfs_remove_recursive(lp->debugfs_root); + + ieee802154_unregister_hw(lp->hw); + mutex_destroy(&lp->bmux); + ieee802154_free_hw(lp->hw); + + return 0; +} + +static const struct of_device_id adf7242_of_match[] = { + { .compatible = "adi,adf7242", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adf7242_of_match); + +static const struct spi_device_id adf7242_device_id[] = { + { .name = "adf7242", }, + { }, +}; +MODULE_DEVICE_TABLE(spi, adf7242_device_id); + +static struct spi_driver adf7242_driver = { + .id_table = adf7242_device_id, + .driver = { + .of_match_table = of_match_ptr(adf7242_of_match), + .name = "adf7242", + .owner = THIS_MODULE, + }, + .probe = adf7242_probe, + .remove = adf7242_remove, +}; + +module_spi_driver(adf7242_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("ADF7242 IEEE802.15.4 Transceiver Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 133b7326e96031e69fcfb42d6f3040b563ce6464 Mon Sep 17 00:00:00 2001 From: Guy Mishol Date: Wed, 2 Dec 2015 10:28:22 +0200 Subject: wlcore: split wl12xx/wl18xx sg parameters Align to new wl18xx sg parameters. This requires to split both wl12xx/wl18xx enumerators. Signed-off-by: Guy Mishol Acked-by: Eliad Peller Signed-off-by: Kalle Valo --- drivers/net/wireless/ti/wl12xx/conf.h | 233 +++++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wl12xx/main.c | 116 +++++++++-------- drivers/net/wireless/ti/wl18xx/conf.h | 90 +++++++++++++ drivers/net/wireless/ti/wl18xx/main.c | 138 +++++++++++--------- drivers/net/wireless/ti/wlcore/acx.c | 4 +- drivers/net/wireless/ti/wlcore/acx.h | 2 +- drivers/net/wireless/ti/wlcore/conf.h | 237 +--------------------------------- 7 files changed, 463 insertions(+), 357 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/conf.h b/drivers/net/wireless/ti/wl12xx/conf.h index 75e29897a0f5..a606ba9ef041 100644 --- a/drivers/net/wireless/ti/wl12xx/conf.h +++ b/drivers/net/wireless/ti/wl12xx/conf.h @@ -47,4 +47,237 @@ struct wl12xx_priv_conf { struct conf_memory_settings mem_wl127x; }; +enum wl12xx_sg_params { + /* + * Configure the min and max time BT gains the antenna + * in WLAN / BT master basic rate + * + * Range: 0 - 255 (ms) + */ + WL12XX_CONF_SG_ACL_BT_MASTER_MIN_BR = 0, + WL12XX_CONF_SG_ACL_BT_MASTER_MAX_BR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN / BT slave basic rate + * + * Range: 0 - 255 (ms) + */ + WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_BR, + WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_BR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN / BT master EDR + * + * Range: 0 - 255 (ms) + */ + WL12XX_CONF_SG_ACL_BT_MASTER_MIN_EDR, + WL12XX_CONF_SG_ACL_BT_MASTER_MAX_EDR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN / BT slave EDR + * + * Range: 0 - 255 (ms) + */ + WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_EDR, + WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_EDR, + + /* + * The maximum time WLAN can gain the antenna + * in WLAN PSM / BT master/slave BR + * + * Range: 0 - 255 (ms) + */ + WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_BR, + WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_BR, + + /* + * The maximum time WLAN can gain the antenna + * in WLAN PSM / BT master/slave EDR + * + * Range: 0 - 255 (ms) + */ + WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_EDR, + WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_EDR, + + /* TODO: explain these values */ + WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR, + WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR, + WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR, + WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR, + WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR, + WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR, + WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR, + WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR, + + WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR, + WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR, + WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_BR, + WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR, + WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_EDR, + WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR, + + /* + * Compensation percentage of probe requests when scan initiated + * during BT voice/ACL link. + * + * Range: 0 - 255 (%) + */ + WL12XX_CONF_SG_AUTO_SCAN_PROBE_REQ, + + /* + * Compensation percentage of probe requests when active scan initiated + * during BT voice + * + * Range: 0 - 255 (%) + */ + WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3, + + /* + * Compensation percentage of WLAN active scan window if initiated + * during BT A2DP + * + * Range: 0 - 1000 (%) + */ + WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP, + + /* + * Compensation percentage of WLAN passive scan window if initiated + * during BT A2DP BR + * + * Range: 0 - 1000 (%) + */ + WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_BR, + + /* + * Compensation percentage of WLAN passive scan window if initiated + * during BT A2DP EDR + * + * Range: 0 - 1000 (%) + */ + WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_EDR, + + /* + * Compensation percentage of WLAN passive scan window if initiated + * during BT voice + * + * Range: 0 - 1000 (%) + */ + WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_HV3, + + /* TODO: explain these values */ + WL12XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN, + WL12XX_CONF_SG_BCN_HV3_COLL_THR_IN_PASSIVE_SCAN, + WL12XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN, + + /* + * Defines whether the SG will force WLAN host to enter/exit PSM + * + * Range: 1 - SG can force, 0 - host handles PSM + */ + WL12XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO, + + /* + * Defines antenna configuration (single/dual antenna) + * + * Range: 0 - single antenna, 1 - dual antenna + */ + WL12XX_CONF_SG_ANTENNA_CONFIGURATION, + + /* + * The threshold (percent) of max consecutive beacon misses before + * increasing priority of beacon reception. + * + * Range: 0 - 100 (%) + */ + WL12XX_CONF_SG_BEACON_MISS_PERCENT, + + /* + * Protection time of the DHCP procedure. + * + * Range: 0 - 100000 (ms) + */ + WL12XX_CONF_SG_DHCP_TIME, + + /* + * RX guard time before the beginning of a new BT voice frame during + * which no new WLAN trigger frame is transmitted. + * + * Range: 0 - 100000 (us) + */ + WL12XX_CONF_SG_RXT, + + /* + * TX guard time before the beginning of a new BT voice frame during + * which no new WLAN frame is transmitted. + * + * Range: 0 - 100000 (us) + */ + WL12XX_CONF_SG_TXT, + + /* + * Enable adaptive RXT/TXT algorithm. If disabled, the host values + * will be utilized. + * + * Range: 0 - disable, 1 - enable + */ + WL12XX_CONF_SG_ADAPTIVE_RXT_TXT, + + /* TODO: explain this value */ + WL12XX_CONF_SG_GENERAL_USAGE_BIT_MAP, + + /* + * Number of consecutive BT voice frames not interrupted by WLAN + * + * Range: 0 - 100 + */ + WL12XX_CONF_SG_HV3_MAX_SERVED, + + /* + * The used WLAN legacy service period during active BT ACL link + * + * Range: 0 - 255 (ms) + */ + WL12XX_CONF_SG_PS_POLL_TIMEOUT, + + /* + * The used WLAN UPSD service period during active BT ACL link + * + * Range: 0 - 255 (ms) + */ + WL12XX_CONF_SG_UPSD_TIMEOUT, + + WL12XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD, + WL12XX_CONF_SG_STA_RX_WINDOW_AFTER_DTIM, + WL12XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME, + + /* AP params */ + WL12XX_CONF_AP_BEACON_MISS_TX, + WL12XX_CONF_AP_RX_WINDOW_AFTER_BEACON, + WL12XX_CONF_AP_BEACON_WINDOW_INTERVAL, + WL12XX_CONF_AP_CONNECTION_PROTECTION_TIME, + WL12XX_CONF_AP_BT_ACL_VAL_BT_SERVE_TIME, + WL12XX_CONF_AP_BT_ACL_VAL_WL_SERVE_TIME, + + /* CTS Diluting params */ + WL12XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH, + WL12XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER, + + WL12XX_CONF_SG_TEMP_PARAM_1, + WL12XX_CONF_SG_TEMP_PARAM_2, + WL12XX_CONF_SG_TEMP_PARAM_3, + WL12XX_CONF_SG_TEMP_PARAM_4, + WL12XX_CONF_SG_TEMP_PARAM_5, + WL12XX_CONF_SG_TEMP_PARAM_6, + WL12XX_CONF_SG_TEMP_PARAM_7, + WL12XX_CONF_SG_TEMP_PARAM_8, + WL12XX_CONF_SG_TEMP_PARAM_9, + WL12XX_CONF_SG_TEMP_PARAM_10, + + WL12XX_CONF_SG_PARAMS_MAX, + WL12XX_CONF_SG_PARAMS_ALL = 0xff +}; + #endif /* __WL12XX_CONF_H__ */ diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index af0fe2e17151..a0d6cccc56f3 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -39,6 +39,7 @@ #include "scan.h" #include "event.h" #include "debugfs.h" +#include "conf.h" static char *fref_param; static char *tcxo_param; @@ -46,69 +47,69 @@ static char *tcxo_param; static struct wlcore_conf wl12xx_conf = { .sg = { .params = { - [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10, - [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180, - [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10, - [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180, - [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10, - [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80, - [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10, - [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80, - [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8, - [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8, - [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20, - [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20, - [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20, - [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35, - [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16, - [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35, - [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32, - [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50, - [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28, - [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50, - [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10, - [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20, - [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75, - [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15, - [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27, - [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17, + [WL12XX_CONF_SG_ACL_BT_MASTER_MIN_BR] = 10, + [WL12XX_CONF_SG_ACL_BT_MASTER_MAX_BR] = 180, + [WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10, + [WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180, + [WL12XX_CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10, + [WL12XX_CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80, + [WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10, + [WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80, + [WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8, + [WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8, + [WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20, + [WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20, + [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20, + [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35, + [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16, + [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35, + [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32, + [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50, + [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28, + [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50, + [WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10, + [WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20, + [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75, + [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15, + [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27, + [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17, /* active scan params */ - [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, - [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, - [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, + [WL12XX_CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, + [WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, + [WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, /* passive scan params */ - [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800, - [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200, - [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200, + [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_BR] = 800, + [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_EDR] = 200, + [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_HV3] = 200, /* passive scan in dual antenna params */ - [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0, - [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0, - [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0, + [WL12XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0, + [WL12XX_CONF_SG_BCN_HV3_COLL_THR_IN_PASSIVE_SCAN] = 0, + [WL12XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN] = 0, /* general params */ - [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1, - [CONF_SG_ANTENNA_CONFIGURATION] = 0, - [CONF_SG_BEACON_MISS_PERCENT] = 60, - [CONF_SG_DHCP_TIME] = 5000, - [CONF_SG_RXT] = 1200, - [CONF_SG_TXT] = 1000, - [CONF_SG_ADAPTIVE_RXT_TXT] = 1, - [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3, - [CONF_SG_HV3_MAX_SERVED] = 6, - [CONF_SG_PS_POLL_TIMEOUT] = 10, - [CONF_SG_UPSD_TIMEOUT] = 10, - [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2, - [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5, - [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30, + [WL12XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1, + [WL12XX_CONF_SG_ANTENNA_CONFIGURATION] = 0, + [WL12XX_CONF_SG_BEACON_MISS_PERCENT] = 60, + [WL12XX_CONF_SG_DHCP_TIME] = 5000, + [WL12XX_CONF_SG_RXT] = 1200, + [WL12XX_CONF_SG_TXT] = 1000, + [WL12XX_CONF_SG_ADAPTIVE_RXT_TXT] = 1, + [WL12XX_CONF_SG_GENERAL_USAGE_BIT_MAP] = 3, + [WL12XX_CONF_SG_HV3_MAX_SERVED] = 6, + [WL12XX_CONF_SG_PS_POLL_TIMEOUT] = 10, + [WL12XX_CONF_SG_UPSD_TIMEOUT] = 10, + [WL12XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2, + [WL12XX_CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5, + [WL12XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30, /* AP params */ - [CONF_AP_BEACON_MISS_TX] = 3, - [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10, - [CONF_AP_BEACON_WINDOW_INTERVAL] = 2, - [CONF_AP_CONNECTION_PROTECTION_TIME] = 0, - [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25, - [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25, + [WL12XX_CONF_AP_BEACON_MISS_TX] = 3, + [WL12XX_CONF_AP_RX_WINDOW_AFTER_BEACON] = 10, + [WL12XX_CONF_AP_BEACON_WINDOW_INTERVAL] = 2, + [WL12XX_CONF_AP_CONNECTION_PROTECTION_TIME] = 0, + [WL12XX_CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25, + [WL12XX_CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25, /* CTS Diluting params */ - [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0, - [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0, + [WL12XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0, + [WL12XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0, }, .state = CONF_SG_PROTECTIVE, }, @@ -1809,6 +1810,7 @@ static int wl12xx_setup(struct wl1271 *wl) BUILD_BUG_ON(WL12XX_MAX_LINKS > WLCORE_MAX_LINKS); BUILD_BUG_ON(WL12XX_MAX_AP_STATIONS > WL12XX_MAX_LINKS); + BUILD_BUG_ON(WL12XX_CONF_SG_PARAMS_MAX > WLCORE_CONF_SG_PARAMS_MAX); wl->rtable = wl12xx_rtable; wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS; diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index 71f1ec448ba5..7aa880f14ccb 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -139,4 +139,94 @@ struct wl18xx_priv_conf { struct conf_ap_sleep_settings ap_sleep; } __packed; +enum wl18xx_sg_params { + WL18XX_CONF_SG_PARAM_0 = 0, + + /* Configuration Parameters */ + WL18XX_CONF_SG_ANTENNA_CONFIGURATION, + WL18XX_CONF_SG_ZIGBEE_COEX, + WL18XX_CONF_SG_TIME_SYNC, + + WL18XX_CONF_SG_PARAM_4, + WL18XX_CONF_SG_PARAM_5, + WL18XX_CONF_SG_PARAM_6, + WL18XX_CONF_SG_PARAM_7, + WL18XX_CONF_SG_PARAM_8, + WL18XX_CONF_SG_PARAM_9, + WL18XX_CONF_SG_PARAM_10, + WL18XX_CONF_SG_PARAM_11, + WL18XX_CONF_SG_PARAM_12, + WL18XX_CONF_SG_PARAM_13, + WL18XX_CONF_SG_PARAM_14, + WL18XX_CONF_SG_PARAM_15, + WL18XX_CONF_SG_PARAM_16, + WL18XX_CONF_SG_PARAM_17, + WL18XX_CONF_SG_PARAM_18, + WL18XX_CONF_SG_PARAM_19, + WL18XX_CONF_SG_PARAM_20, + WL18XX_CONF_SG_PARAM_21, + WL18XX_CONF_SG_PARAM_22, + WL18XX_CONF_SG_PARAM_23, + WL18XX_CONF_SG_PARAM_24, + WL18XX_CONF_SG_PARAM_25, + + /* Active Scan Parameters */ + WL18XX_CONF_SG_AUTO_SCAN_PROBE_REQ, + WL18XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3, + + WL18XX_CONF_SG_PARAM_28, + + /* Passive Scan Parameters */ + WL18XX_CONF_SG_PARAM_29, + WL18XX_CONF_SG_PARAM_30, + WL18XX_CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3, + + /* Passive Scan in Dual Antenna Parameters */ + WL18XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN, + WL18XX_CONF_SG_BEACON_HV3_COLL_TH_IN_PASSIVE_SCAN, + WL18XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN, + + /* General Parameters */ + WL18XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO, + WL18XX_CONF_SG_PARAM_36, + WL18XX_CONF_SG_BEACON_MISS_PERCENT, + WL18XX_CONF_SG_PARAM_38, + WL18XX_CONF_SG_RXT, + WL18XX_CONF_SG_UNUSED, + WL18XX_CONF_SG_ADAPTIVE_RXT_TXT, + WL18XX_CONF_SG_GENERAL_USAGE_BIT_MAP, + WL18XX_CONF_SG_HV3_MAX_SERVED, + WL18XX_CONF_SG_PARAM_44, + WL18XX_CONF_SG_PARAM_45, + WL18XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD, + WL18XX_CONF_SG_GEMINI_PARAM_47, + WL18XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME, + + /* AP Parameters */ + WL18XX_CONF_SG_AP_BEACON_MISS_TX, + WL18XX_CONF_SG_PARAM_50, + WL18XX_CONF_SG_AP_BEACON_WINDOW_INTERVAL, + WL18XX_CONF_SG_AP_CONNECTION_PROTECTION_TIME, + WL18XX_CONF_SG_PARAM_53, + WL18XX_CONF_SG_PARAM_54, + + /* CTS Diluting Parameters */ + WL18XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH, + WL18XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER, + + WL18XX_CONF_SG_TEMP_PARAM_1, + WL18XX_CONF_SG_TEMP_PARAM_2, + WL18XX_CONF_SG_TEMP_PARAM_3, + WL18XX_CONF_SG_TEMP_PARAM_4, + WL18XX_CONF_SG_TEMP_PARAM_5, + WL18XX_CONF_SG_TEMP_PARAM_6, + WL18XX_CONF_SG_TEMP_PARAM_7, + WL18XX_CONF_SG_TEMP_PARAM_8, + WL18XX_CONF_SG_TEMP_PARAM_9, + WL18XX_CONF_SG_TEMP_PARAM_10, + + WL18XX_CONF_SG_PARAMS_MAX, + WL18XX_CONF_SG_PARAMS_ALL = 0xff +}; + #endif /* __WL18XX_CONF_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 50cce42089a5..7869c3c99e6d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -177,69 +177,80 @@ enum wl18xx_hw_rates { static struct wlcore_conf wl18xx_conf = { .sg = { .params = { - [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10, - [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180, - [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10, - [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180, - [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10, - [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80, - [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10, - [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80, - [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8, - [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8, - [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20, - [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20, - [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20, - [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35, - [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16, - [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35, - [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32, - [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50, - [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28, - [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50, - [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10, - [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20, - [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75, - [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15, - [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27, - [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17, - /* active scan params */ - [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, - [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, - [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, - /* passive scan params */ - [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800, - [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200, - [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200, - /* passive scan in dual antenna params */ - [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0, - [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0, - [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0, - /* general params */ - [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1, - [CONF_SG_ANTENNA_CONFIGURATION] = 0, - [CONF_SG_BEACON_MISS_PERCENT] = 60, - [CONF_SG_DHCP_TIME] = 5000, - [CONF_SG_RXT] = 1200, - [CONF_SG_TXT] = 1000, - [CONF_SG_ADAPTIVE_RXT_TXT] = 1, - [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3, - [CONF_SG_HV3_MAX_SERVED] = 6, - [CONF_SG_PS_POLL_TIMEOUT] = 10, - [CONF_SG_UPSD_TIMEOUT] = 10, - [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2, - [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5, - [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30, - /* AP params */ - [CONF_AP_BEACON_MISS_TX] = 3, - [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10, - [CONF_AP_BEACON_WINDOW_INTERVAL] = 2, - [CONF_AP_CONNECTION_PROTECTION_TIME] = 0, - [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25, - [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25, - /* CTS Diluting params */ - [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0, - [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0, + [WL18XX_CONF_SG_PARAM_0] = 0, + /* Configuartion Parameters */ + [WL18XX_CONF_SG_ANTENNA_CONFIGURATION] = 0, + [WL18XX_CONF_SG_ZIGBEE_COEX] = 0, + [WL18XX_CONF_SG_TIME_SYNC] = 0, + [WL18XX_CONF_SG_PARAM_4] = 0, + [WL18XX_CONF_SG_PARAM_5] = 0, + [WL18XX_CONF_SG_PARAM_6] = 0, + [WL18XX_CONF_SG_PARAM_7] = 0, + [WL18XX_CONF_SG_PARAM_8] = 0, + [WL18XX_CONF_SG_PARAM_9] = 0, + [WL18XX_CONF_SG_PARAM_10] = 0, + [WL18XX_CONF_SG_PARAM_11] = 0, + [WL18XX_CONF_SG_PARAM_12] = 0, + [WL18XX_CONF_SG_PARAM_13] = 0, + [WL18XX_CONF_SG_PARAM_14] = 0, + [WL18XX_CONF_SG_PARAM_15] = 0, + [WL18XX_CONF_SG_PARAM_16] = 0, + [WL18XX_CONF_SG_PARAM_17] = 0, + [WL18XX_CONF_SG_PARAM_18] = 0, + [WL18XX_CONF_SG_PARAM_19] = 0, + [WL18XX_CONF_SG_PARAM_20] = 0, + [WL18XX_CONF_SG_PARAM_21] = 0, + [WL18XX_CONF_SG_PARAM_22] = 0, + [WL18XX_CONF_SG_PARAM_23] = 0, + [WL18XX_CONF_SG_PARAM_24] = 0, + [WL18XX_CONF_SG_PARAM_25] = 0, + /* Active Scan Parameters */ + [WL18XX_CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, + [WL18XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, + [WL18XX_CONF_SG_PARAM_28] = 0, + /* Passive Scan Parameters */ + [WL18XX_CONF_SG_PARAM_29] = 0, + [WL18XX_CONF_SG_PARAM_30] = 0, + [WL18XX_CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200, + /* Passive Scan in Dual Antenna Parameters */ + [WL18XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0, + [WL18XX_CONF_SG_BEACON_HV3_COLL_TH_IN_PASSIVE_SCAN] = 0, + [WL18XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN] = 0, + /* General Parameters */ + [WL18XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1, + [WL18XX_CONF_SG_PARAM_36] = 0, + [WL18XX_CONF_SG_BEACON_MISS_PERCENT] = 60, + [WL18XX_CONF_SG_PARAM_38] = 0, + [WL18XX_CONF_SG_RXT] = 1200, + [WL18XX_CONF_SG_UNUSED] = 0, + [WL18XX_CONF_SG_ADAPTIVE_RXT_TXT] = 1, + [WL18XX_CONF_SG_GENERAL_USAGE_BIT_MAP] = 3, + [WL18XX_CONF_SG_HV3_MAX_SERVED] = 6, + [WL18XX_CONF_SG_PARAM_44] = 0, + [WL18XX_CONF_SG_PARAM_45] = 0, + [WL18XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2, + [WL18XX_CONF_SG_GEMINI_PARAM_47] = 0, + [WL18XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 0, + /* AP Parameters */ + [WL18XX_CONF_SG_AP_BEACON_MISS_TX] = 3, + [WL18XX_CONF_SG_PARAM_50] = 0, + [WL18XX_CONF_SG_AP_BEACON_WINDOW_INTERVAL] = 2, + [WL18XX_CONF_SG_AP_CONNECTION_PROTECTION_TIME] = 30, + [WL18XX_CONF_SG_PARAM_53] = 0, + [WL18XX_CONF_SG_PARAM_54] = 0, + /* CTS Diluting Parameters */ + [WL18XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0, + [WL18XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0, + [WL18XX_CONF_SG_TEMP_PARAM_1] = 0, + [WL18XX_CONF_SG_TEMP_PARAM_2] = 0, + [WL18XX_CONF_SG_TEMP_PARAM_3] = 0, + [WL18XX_CONF_SG_TEMP_PARAM_4] = 0, + [WL18XX_CONF_SG_TEMP_PARAM_5] = 0, + [WL18XX_CONF_SG_TEMP_PARAM_6] = 0, + [WL18XX_CONF_SG_TEMP_PARAM_7] = 0, + [WL18XX_CONF_SG_TEMP_PARAM_8] = 0, + [WL18XX_CONF_SG_TEMP_PARAM_9] = 0, + [WL18XX_CONF_SG_TEMP_PARAM_10] = 0, }, .state = CONF_SG_PROTECTIVE, }, @@ -1895,6 +1906,7 @@ static int wl18xx_setup(struct wl1271 *wl) BUILD_BUG_ON(WL18XX_MAX_LINKS > WLCORE_MAX_LINKS); BUILD_BUG_ON(WL18XX_MAX_AP_STATIONS > WL18XX_MAX_LINKS); + BUILD_BUG_ON(WL18XX_CONF_SG_PARAMS_MAX > WLCORE_CONF_SG_PARAMS_MAX); wl->rtable = wl18xx_rtable; wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS; diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index f28fa3b5029d..26cc23f32241 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -534,9 +534,9 @@ int wl12xx_acx_sg_cfg(struct wl1271 *wl) } /* BT-WLAN coext parameters */ - for (i = 0; i < CONF_SG_PARAMS_MAX; i++) + for (i = 0; i < WLCORE_CONF_SG_PARAMS_MAX; i++) param->params[i] = cpu_to_le32(c->params[i]); - param->param_idx = CONF_SG_PARAMS_ALL; + param->param_idx = WLCORE_CONF_SG_PARAMS_ALL; ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); if (ret < 0) { diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index 954d57ec98f4..0d61fae88dcb 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -300,7 +300,7 @@ struct acx_bt_wlan_coex { struct acx_bt_wlan_coex_param { struct acx_header header; - __le32 params[CONF_SG_PARAMS_MAX]; + __le32 params[WLCORE_CONF_SG_PARAMS_MAX]; u8 param_idx; u8 padding[3]; } __packed; diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 52a9d1b14020..44d898fe0afc 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -110,242 +110,11 @@ enum { CONF_SG_OPPORTUNISTIC }; -enum { - /* - * Configure the min and max time BT gains the antenna - * in WLAN / BT master basic rate - * - * Range: 0 - 255 (ms) - */ - CONF_SG_ACL_BT_MASTER_MIN_BR = 0, - CONF_SG_ACL_BT_MASTER_MAX_BR, - - /* - * Configure the min and max time BT gains the antenna - * in WLAN / BT slave basic rate - * - * Range: 0 - 255 (ms) - */ - CONF_SG_ACL_BT_SLAVE_MIN_BR, - CONF_SG_ACL_BT_SLAVE_MAX_BR, - - /* - * Configure the min and max time BT gains the antenna - * in WLAN / BT master EDR - * - * Range: 0 - 255 (ms) - */ - CONF_SG_ACL_BT_MASTER_MIN_EDR, - CONF_SG_ACL_BT_MASTER_MAX_EDR, - - /* - * Configure the min and max time BT gains the antenna - * in WLAN / BT slave EDR - * - * Range: 0 - 255 (ms) - */ - CONF_SG_ACL_BT_SLAVE_MIN_EDR, - CONF_SG_ACL_BT_SLAVE_MAX_EDR, - - /* - * The maximum time WLAN can gain the antenna - * in WLAN PSM / BT master/slave BR - * - * Range: 0 - 255 (ms) - */ - CONF_SG_ACL_WLAN_PS_MASTER_BR, - CONF_SG_ACL_WLAN_PS_SLAVE_BR, - - /* - * The maximum time WLAN can gain the antenna - * in WLAN PSM / BT master/slave EDR - * - * Range: 0 - 255 (ms) - */ - CONF_SG_ACL_WLAN_PS_MASTER_EDR, - CONF_SG_ACL_WLAN_PS_SLAVE_EDR, - - /* TODO: explain these values */ - CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR, - CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR, - CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR, - CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR, - CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR, - CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR, - CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR, - CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR, - - CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR, - CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR, - CONF_SG_ACL_PASSIVE_SCAN_BT_BR, - CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR, - CONF_SG_ACL_PASSIVE_SCAN_BT_EDR, - CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR, - - /* - * Compensation percentage of probe requests when scan initiated - * during BT voice/ACL link. - * - * Range: 0 - 255 (%) - */ - CONF_SG_AUTO_SCAN_PROBE_REQ, - - /* - * Compensation percentage of probe requests when active scan initiated - * during BT voice - * - * Range: 0 - 255 (%) - */ - CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3, - - /* - * Compensation percentage of WLAN active scan window if initiated - * during BT A2DP - * - * Range: 0 - 1000 (%) - */ - CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP, - - /* - * Compensation percentage of WLAN passive scan window if initiated - * during BT A2DP BR - * - * Range: 0 - 1000 (%) - */ - CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR, - - /* - * Compensation percentage of WLAN passive scan window if initiated - * during BT A2DP EDR - * - * Range: 0 - 1000 (%) - */ - CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR, - - /* - * Compensation percentage of WLAN passive scan window if initiated - * during BT voice - * - * Range: 0 - 1000 (%) - */ - CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3, - - /* TODO: explain these values */ - CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN, - CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN, - CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN, - - /* - * Defines whether the SG will force WLAN host to enter/exit PSM - * - * Range: 1 - SG can force, 0 - host handles PSM - */ - CONF_SG_STA_FORCE_PS_IN_BT_SCO, - - /* - * Defines antenna configuration (single/dual antenna) - * - * Range: 0 - single antenna, 1 - dual antenna - */ - CONF_SG_ANTENNA_CONFIGURATION, - - /* - * The threshold (percent) of max consecutive beacon misses before - * increasing priority of beacon reception. - * - * Range: 0 - 100 (%) - */ - CONF_SG_BEACON_MISS_PERCENT, - - /* - * Protection time of the DHCP procedure. - * - * Range: 0 - 100000 (ms) - */ - CONF_SG_DHCP_TIME, - - /* - * RX guard time before the beginning of a new BT voice frame during - * which no new WLAN trigger frame is transmitted. - * - * Range: 0 - 100000 (us) - */ - CONF_SG_RXT, - - /* - * TX guard time before the beginning of a new BT voice frame during - * which no new WLAN frame is transmitted. - * - * Range: 0 - 100000 (us) - */ - - CONF_SG_TXT, - - /* - * Enable adaptive RXT/TXT algorithm. If disabled, the host values - * will be utilized. - * - * Range: 0 - disable, 1 - enable - */ - CONF_SG_ADAPTIVE_RXT_TXT, - - /* TODO: explain this value */ - CONF_SG_GENERAL_USAGE_BIT_MAP, - - /* - * Number of consecutive BT voice frames not interrupted by WLAN - * - * Range: 0 - 100 - */ - CONF_SG_HV3_MAX_SERVED, - - /* - * The used WLAN legacy service period during active BT ACL link - * - * Range: 0 - 255 (ms) - */ - CONF_SG_PS_POLL_TIMEOUT, - - /* - * The used WLAN UPSD service period during active BT ACL link - * - * Range: 0 - 255 (ms) - */ - CONF_SG_UPSD_TIMEOUT, - - CONF_SG_CONSECUTIVE_CTS_THRESHOLD, - CONF_SG_STA_RX_WINDOW_AFTER_DTIM, - CONF_SG_STA_CONNECTION_PROTECTION_TIME, - - /* AP params */ - CONF_AP_BEACON_MISS_TX, - CONF_AP_RX_WINDOW_AFTER_BEACON, - CONF_AP_BEACON_WINDOW_INTERVAL, - CONF_AP_CONNECTION_PROTECTION_TIME, - CONF_AP_BT_ACL_VAL_BT_SERVE_TIME, - CONF_AP_BT_ACL_VAL_WL_SERVE_TIME, - - /* CTS Diluting params */ - CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH, - CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER, - - CONF_SG_TEMP_PARAM_1, - CONF_SG_TEMP_PARAM_2, - CONF_SG_TEMP_PARAM_3, - CONF_SG_TEMP_PARAM_4, - CONF_SG_TEMP_PARAM_5, - CONF_SG_TEMP_PARAM_6, - CONF_SG_TEMP_PARAM_7, - CONF_SG_TEMP_PARAM_8, - CONF_SG_TEMP_PARAM_9, - CONF_SG_TEMP_PARAM_10, - - CONF_SG_PARAMS_MAX, - CONF_SG_PARAMS_ALL = 0xff -}; +#define WLCORE_CONF_SG_PARAMS_MAX 67 +#define WLCORE_CONF_SG_PARAMS_ALL 0xff struct conf_sg_settings { - u32 params[CONF_SG_PARAMS_MAX]; + u32 params[WLCORE_CONF_SG_PARAMS_MAX]; u8 state; } __packed; -- cgit v1.2.3-59-g8ed1b From b513cac22ad6527bd85042766afa8eacf1adffbe Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 2 Dec 2015 11:45:10 +0000 Subject: brcmfmac: only lock and unlock fws if fws is not null There is a null ptr check for fws to set bcmc_credit_check, however, there a lock and unlock on fws should only performed if fwts is also not null to also avoid a potential null pointer deference. Signed-off-by: Colin Ian King Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index ffdc7f82df4b..e5f5fac9f9b3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -1609,10 +1609,11 @@ static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp, { struct brcmf_fws_info *fws = ifp->drvr->fws; - brcmf_fws_lock(fws); - if (fws) + if (fws) { + brcmf_fws_lock(fws); fws->bcmc_credit_check = true; - brcmf_fws_unlock(fws); + brcmf_fws_unlock(fws); + } return 0; } -- cgit v1.2.3-59-g8ed1b From 09ad44efe722161bbc26317b6c2a43dfd86f023b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Dec 2015 16:15:23 +0300 Subject: cw1200: remove some dead code If the mode is NL80211_IFTYPE_UNSPECIFIED then we return success at the start of the function so this condition is never true. Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/st/cw1200/sta.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/st/cw1200/sta.c b/drivers/net/wireless/st/cw1200/sta.c index 95a7fdb3cc1c..06321c799c90 100644 --- a/drivers/net/wireless/st/cw1200/sta.c +++ b/drivers/net/wireless/st/cw1200/sta.c @@ -873,12 +873,6 @@ int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) else val32 = 0; /* disabled */ - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { - /* device is down, can _not_ set threshold */ - ret = -ENODEV; - goto out; - } - if (priv->rts_threshold == value) goto out; -- cgit v1.2.3-59-g8ed1b From 3f0267f6f76d56285095f88a8908daf8be0fe56b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Dec 2015 16:16:45 +0300 Subject: iwlegacy: cleanup end of il_send_add_sta() This code causes a static checker warning because we check for "if (ret == 0)" but we have already had verified that was true. Clean it up a little. Signed-off-by: Dan Carpenter Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/iwlegacy/common.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c index 887114582583..e49bad261a76 100644 --- a/drivers/net/wireless/intel/iwlegacy/common.c +++ b/drivers/net/wireless/intel/iwlegacy/common.c @@ -1865,14 +1865,14 @@ il_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags) cmd.len = il->ops->build_addsta_hcmd(sta, data); ret = il_send_cmd(il, &cmd); - - if (ret || (flags & CMD_ASYNC)) + if (ret) return ret; + if (flags & CMD_ASYNC) + return 0; + + pkt = (struct il_rx_pkt *)cmd.reply_page; + ret = il_process_add_sta_resp(il, sta, pkt, true); - if (ret == 0) { - pkt = (struct il_rx_pkt *)cmd.reply_page; - ret = il_process_add_sta_resp(il, sta, pkt, true); - } il_free_pages(il, cmd.reply_page); return ret; -- cgit v1.2.3-59-g8ed1b From 97276c10cb45380ba5d5772b616fc33b55b5bf40 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Dec 2015 16:17:15 +0300 Subject: mwifiex: remove an unneeded condition We already know that "wep_key->key_length" is set so there is no need to check again. Also the last curly brace was indented too far. Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index a6c8a4f7bfe9..80f50645b594 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -923,9 +923,8 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, if (encrypt_key->key_disable) { memset(&priv->wep_key[index], 0, sizeof(struct mwifiex_wep_key)); - if (wep_key->key_length) - goto done; - } + goto done; + } if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) enc_key = encrypt_key; -- cgit v1.2.3-59-g8ed1b From 9e4aee43dc2211a976f8aa7df5cf823d7a4ae640 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Dec 2015 16:17:46 +0300 Subject: hostap: fix an error code in prism2_config() The current code returns success if prism2_init_local_data() fails, but we want to return an error code. Also we can remove the bogus ret initializer because it is wrong and never used. Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/hostap/hostap_cs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intersil/hostap/hostap_cs.c b/drivers/net/wireless/intersil/hostap/hostap_cs.c index 50033aa7c7d5..74f63b7bf7b4 100644 --- a/drivers/net/wireless/intersil/hostap/hostap_cs.c +++ b/drivers/net/wireless/intersil/hostap/hostap_cs.c @@ -473,7 +473,7 @@ static int prism2_config(struct pcmcia_device *link) struct net_device *dev; struct hostap_interface *iface; local_info_t *local; - int ret = 1; + int ret; struct hostap_cs_priv *hw_priv; unsigned long flags; @@ -502,8 +502,10 @@ static int prism2_config(struct pcmcia_device *link) /* Need to allocate net_device before requesting IRQ handler */ dev = prism2_init_local_data(&prism2_pccard_funcs, 0, &link->dev); - if (dev == NULL) + if (!dev) { + ret = -ENOMEM; goto failed; + } link->priv = dev; iface = netdev_priv(dev); -- cgit v1.2.3-59-g8ed1b From bd9a212bab3db65baff094fe811ef1b593faca2a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Dec 2015 16:19:49 +0300 Subject: prism54: off by one BUG_ON() test This code was supposed to trigger a BUG() if we truncate the output but it's off by one so it allows one character to be truncated. Really drivers shouldn't call BUG_ON() and especially for something minor like this so I've changed it to a WARN_ON(). Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/prism54/isl_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intersil/prism54/isl_ioctl.c b/drivers/net/wireless/intersil/prism54/isl_ioctl.c index ecbb0546cf3e..48e8a978a832 100644 --- a/drivers/net/wireless/intersil/prism54/isl_ioctl.c +++ b/drivers/net/wireless/intersil/prism54/isl_ioctl.c @@ -2036,7 +2036,7 @@ format_event(islpci_private *priv, char *dest, const char *str, mlme->address, (error ? (mlme->code ? " : REJECTED " : " : ACCEPTED ") : ""), mlme->code); - BUG_ON(n > IW_CUSTOM_MAX); + WARN_ON(n >= IW_CUSTOM_MAX); *length = n; } -- cgit v1.2.3-59-g8ed1b From d5556e87610e45e586b1bdf6653920b543083653 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 4 Dec 2015 06:13:02 -0800 Subject: mwifiex: parse adhoc start/join result Even if ADHOC start or join attempt is failed, these commands are returned with success status by firmware. Actual connection result is provided inside command response. This patch parses the adhoc connection result and resets connection state variables if connection is not successful. Signed-off-by: Amitkumar Karwar Signed-off-by: Cathy Luo Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/fw.h | 11 +++++++++-- drivers/net/wireless/marvell/mwifiex/join.c | 20 +++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index 1e1e81a0a8d4..89938f61717a 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -1092,9 +1092,15 @@ struct host_cmd_ds_802_11_ad_hoc_start { u8 data_rate[HOSTCMD_SUPPORTED_RATES]; } __packed; -struct host_cmd_ds_802_11_ad_hoc_result { +struct host_cmd_ds_802_11_ad_hoc_start_result { u8 pad[3]; u8 bssid[ETH_ALEN]; + u8 pad2[2]; + u8 result; +} __packed; + +struct host_cmd_ds_802_11_ad_hoc_join_result { + u8 result; } __packed; struct adhoc_bss_desc { @@ -2124,7 +2130,8 @@ struct host_cmd_ds_command { struct host_cmd_ds_802_11_associate_rsp associate_rsp; struct host_cmd_ds_802_11_deauthenticate deauth; struct host_cmd_ds_802_11_ad_hoc_start adhoc_start; - struct host_cmd_ds_802_11_ad_hoc_result adhoc_result; + struct host_cmd_ds_802_11_ad_hoc_start_result start_result; + struct host_cmd_ds_802_11_ad_hoc_join_result join_result; struct host_cmd_ds_802_11_ad_hoc_join adhoc_join; struct host_cmd_ds_802_11d_domain_info domain_info; struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp; diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c index 3cda1f956f0b..cc09a81dbf6a 100644 --- a/drivers/net/wireless/marvell/mwifiex/join.c +++ b/drivers/net/wireless/marvell/mwifiex/join.c @@ -1247,20 +1247,26 @@ int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, { int ret = 0; struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_ad_hoc_result *adhoc_result; + struct host_cmd_ds_802_11_ad_hoc_start_result *start_result = + &resp->params.start_result; + struct host_cmd_ds_802_11_ad_hoc_join_result *join_result = + &resp->params.join_result; struct mwifiex_bssdescriptor *bss_desc; - u16 reason_code; + u16 cmd = le16_to_cpu(resp->command); + u8 result; - adhoc_result = &resp->params.adhoc_result; + if (cmd == HostCmd_CMD_802_11_AD_HOC_START) + result = start_result->result; + else + result = join_result->result; bss_desc = priv->attempted_bss_desc; /* Join result code 0 --> SUCCESS */ - reason_code = le16_to_cpu(resp->result); - if (reason_code) { + if (result) { mwifiex_dbg(priv->adapter, ERROR, "ADHOC_RESP: failed\n"); if (priv->media_connected) - mwifiex_reset_connect_state(priv, reason_code); + mwifiex_reset_connect_state(priv, result); memset(&priv->curr_bss_params.bss_descriptor, 0x00, sizeof(struct mwifiex_bssdescriptor)); @@ -1278,7 +1284,7 @@ int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, /* Update the created network descriptor with the new BSSID */ memcpy(bss_desc->mac_address, - adhoc_result->bssid, ETH_ALEN); + start_result->bssid, ETH_ALEN); priv->adhoc_state = ADHOC_STARTED; } else { -- cgit v1.2.3-59-g8ed1b From d2b0c735ebac0f0d9746305b40806693d3092f57 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 4 Dec 2015 06:13:03 -0800 Subject: mwifiex: handle start AP error paths correctly It's been observed that even if firmware returns an error for a configuration command, we go ahead and start AP. This patch changes the command type from async to sync so that threads waits for command response and return failure start AP. Signed-off-by: Amitkumar Karwar Signed-off-by: Cathy Luo Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/ie.c | 2 +- drivers/net/wireless/marvell/mwifiex/uap_cmd.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/ie.c b/drivers/net/wireless/marvell/mwifiex/ie.c index abf52d25b981..c488c3068abc 100644 --- a/drivers/net/wireless/marvell/mwifiex/ie.c +++ b/drivers/net/wireless/marvell/mwifiex/ie.c @@ -140,7 +140,7 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv, if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, HostCmd_ACT_GEN_SET, - UAP_CUSTOM_IE_I, ie_list, false); + UAP_CUSTOM_IE_I, ie_list, true); return 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c index 759a6ada5b0f..e791166d90c4 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c @@ -848,9 +848,9 @@ int mwifiex_config_start_uap(struct mwifiex_private *priv, if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, HostCmd_ACT_GEN_SET, - UAP_BSS_PARAMS_I, bss_cfg, false)) { + UAP_BSS_PARAMS_I, bss_cfg, true)) { mwifiex_dbg(priv->adapter, ERROR, - "Failed to set the SSID\n"); + "Failed to set AP configuration\n"); return -1; } @@ -865,7 +865,7 @@ int mwifiex_config_start_uap(struct mwifiex_private *priv, } if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, - HostCmd_ACT_GEN_SET, 0, NULL, false)) { + HostCmd_ACT_GEN_SET, 0, NULL, true)) { mwifiex_dbg(priv->adapter, ERROR, "Failed to start the BSS\n"); return -1; -- cgit v1.2.3-59-g8ed1b From 658cb59232b1b853385ded3d4ed587b410457787 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 4 Dec 2015 06:13:04 -0800 Subject: mwifiex: set regulatory info from EEPROM Driver gets country information from EEPROM during initialization. We will call regulatory_hint to update current regulatory domain. As by default world regulatory domain is selected by cfg80211, country '00' from EEPROM is ignored. Signed-off-by: Amitkumar Karwar Signed-off-by: Cathy Luo Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 14 +++++++++----- drivers/net/wireless/marvell/mwifiex/cfp.c | 2 +- drivers/net/wireless/marvell/mwifiex/main.h | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 4073116e6e9f..9b570e14c65a 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -3862,11 +3862,15 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) "driver hint alpha2: %2.2s\n", reg_alpha2); regulatory_hint(wiphy, reg_alpha2); } else { - country_code = mwifiex_11d_code_2_region(adapter->region_code); - if (country_code) - mwifiex_dbg(adapter, WARN, - "ignoring F/W country code %2.2s\n", - country_code); + if (adapter->region_code == 0x00) { + mwifiex_dbg(adapter, WARN, "Ignore world regulatory domain\n"); + } else { + country_code = + mwifiex_11d_code_2_region(adapter->region_code); + if (country_code && + regulatory_hint(wiphy, country_code)) + mwifiex_dbg(priv->adapter, ERROR, "regulatory_hint() failed\n"); + } } mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, diff --git a/drivers/net/wireless/marvell/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c index 3ddb8ec676ed..dd3adc52b017 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfp.c +++ b/drivers/net/wireless/marvell/mwifiex/cfp.c @@ -66,7 +66,7 @@ static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c, 0x12, 0x16, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, 0 }; -u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30, +u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x00, 0x10, 0x20, 0x30, 0x32, 0x40, 0x41, 0xff }; static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 3959f1c97f4e..a86e2aebf566 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -84,7 +84,7 @@ enum { #define MWIFIEX_KEY_BUFFER_SIZE 16 #define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10 -#define MWIFIEX_MAX_REGION_CODE 7 +#define MWIFIEX_MAX_REGION_CODE 8 #define DEFAULT_BCN_AVG_FACTOR 8 #define DEFAULT_DATA_AVG_FACTOR 8 -- cgit v1.2.3-59-g8ed1b From 947d315257f9b25b0e24f5706f8184b3b00774d4 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 4 Dec 2015 06:13:05 -0800 Subject: mwifiex: don't follow AP if country code received from EEPROM If device has already received country information from EEPROM, we won't parse AP's country IE and download it to firmware. We will also set regulatory flags to disable beacon hints and ignore country IE. Signed-off-by: Amitkumar Karwar Signed-off-by: Cathy Luo Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 4 ++++ drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 9b570e14c65a..65dd85d4fb14 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -3802,6 +3802,10 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wiphy->cipher_suites = mwifiex_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); + if (adapter->region_code) + wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS | + REGULATORY_COUNTRY_IE_IGNORE; + ether_addr_copy(wiphy->perm_addr, adapter->perm_addr); wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index 80f50645b594..62b35a31a225 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -272,7 +272,8 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, priv->scan_block = false; if (bss) { - mwifiex_process_country_ie(priv, bss); + if (adapter->region_code == 0x00) + mwifiex_process_country_ie(priv, bss); /* Allocate and fill new bss descriptor */ bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), -- cgit v1.2.3-59-g8ed1b From 7cfd829cfe559d6df4c261584a72bb4ea23470d0 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 4 Dec 2015 06:13:07 -0800 Subject: mwifiex: correction in region code to country mapping EU is not a valid country in db.txt file. Hence regulatory_hint returns failure if EEPROM provides region code as 0x30. Let's use FR for 0x30. Signed-off-by: Amitkumar Karwar Signed-off-by: Cathy Luo Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c index dd3adc52b017..beb564fb2dba 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfp.c +++ b/drivers/net/wireless/marvell/mwifiex/cfp.c @@ -168,7 +168,7 @@ struct region_code_mapping { static struct region_code_mapping region_code_mapping_t[] = { { 0x10, "US " }, /* US FCC */ { 0x20, "CA " }, /* IC Canada */ - { 0x30, "EU " }, /* ETSI */ + { 0x30, "FR " }, /* France */ { 0x31, "ES " }, /* Spain */ { 0x32, "FR " }, /* France */ { 0x40, "JP " }, /* Japan */ -- cgit v1.2.3-59-g8ed1b From 631d464d3d5c2e3b38da96e08139270f7b8274a6 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 6 Dec 2015 07:03:26 +0100 Subject: brcm80211: fix compare_const_fl.cocci warnings Move constants to the right of binary operators. Generated by: scripts/coccinelle/misc/compare_const_fl.cocci Signed-off-by: Fengguang Wu Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c index 635ae034c7e5..38bd5890bd53 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c @@ -177,8 +177,8 @@ static bool brcms_c_country_valid(const char *ccode) * only allow ascii alpha uppercase for the first 2 * chars. */ - if (!((0x80 & ccode[0]) == 0 && ccode[0] >= 0x41 && ccode[0] <= 0x5A && - (0x80 & ccode[1]) == 0 && ccode[1] >= 0x41 && ccode[1] <= 0x5A)) + if (!((ccode[0] & 0x80) == 0 && ccode[0] >= 0x41 && ccode[0] <= 0x5A && + (ccode[1] & 0x80) == 0 && ccode[1] >= 0x41 && ccode[1] <= 0x5A)) return false; /* -- cgit v1.2.3-59-g8ed1b From fc30c30554d8a28cccdae846e31294255e392cff Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 6 Dec 2015 22:56:21 +0100 Subject: mwifiex: fix semicolon.cocci warnings Remove unneeded semicolon. Generated by: scripts/coccinelle/misc/semicolon.cocci Signed-off-by: Fengguang Wu Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c index de74a7773fb6..6f7876ec31b7 100644 --- a/drivers/net/wireless/marvell/mwifiex/init.c +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -95,7 +95,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv) priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON | HostCmd_ACT_MAC_ETHERNETII_ENABLE; - priv->beacon_period = 100; /* beacon interval */ ; + priv->beacon_period = 100; /* beacon interval */ priv->attempted_bss_desc = NULL; memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params)); priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL; -- cgit v1.2.3-59-g8ed1b From 6b904f625271b8ded6f06663b7a0c2d41366ea19 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 7 Dec 2015 10:55:33 +0100 Subject: prism54: fix compare_const_fl.cocci warnings Move constants to the right of binary operators. Generated by: scripts/coccinelle/misc/compare_const_fl.cocci Signed-off-by: Fengguang Wu Signed-off-by: Julia Lawall Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/prism54/oid_mgt.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intersil/prism54/oid_mgt.c b/drivers/net/wireless/intersil/prism54/oid_mgt.c index 3a8d2dbcfecd..6528ed5b9b1d 100644 --- a/drivers/net/wireless/intersil/prism54/oid_mgt.c +++ b/drivers/net/wireless/intersil/prism54/oid_mgt.c @@ -424,7 +424,7 @@ mgt_set_request(islpci_private *priv, enum oid_num_t n, int extra, void *data) void *cache, *_data = data; u32 oid; - BUG_ON(OID_NUM_LAST <= n); + BUG_ON(n >= OID_NUM_LAST); BUG_ON(extra > isl_oid[n].range); if (!priv->mib) @@ -485,7 +485,7 @@ mgt_set_varlen(islpci_private *priv, enum oid_num_t n, void *data, int extra_len int dlen; u32 oid; - BUG_ON(OID_NUM_LAST <= n); + BUG_ON(n >= OID_NUM_LAST); dlen = isl_oid[n].size; oid = isl_oid[n].oid; @@ -524,7 +524,7 @@ mgt_get_request(islpci_private *priv, enum oid_num_t n, int extra, void *data, void *cache, *_res = NULL; u32 oid; - BUG_ON(OID_NUM_LAST <= n); + BUG_ON(n >= OID_NUM_LAST); BUG_ON(extra > isl_oid[n].range); res->ptr = NULL; @@ -626,7 +626,7 @@ mgt_commit_list(islpci_private *priv, enum oid_num_t *l, int n) void mgt_set(islpci_private *priv, enum oid_num_t n, void *data) { - BUG_ON(OID_NUM_LAST <= n); + BUG_ON(n >= OID_NUM_LAST); BUG_ON(priv->mib[n] == NULL); memcpy(priv->mib[n], data, isl_oid[n].size); @@ -636,7 +636,7 @@ mgt_set(islpci_private *priv, enum oid_num_t n, void *data) void mgt_get(islpci_private *priv, enum oid_num_t n, void *res) { - BUG_ON(OID_NUM_LAST <= n); + BUG_ON(n >= OID_NUM_LAST); BUG_ON(priv->mib[n] == NULL); BUG_ON(res == NULL); -- cgit v1.2.3-59-g8ed1b From 17bc55864f81dd730d05f09b1641312a7990d636 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Mon, 7 Dec 2015 01:07:31 +0100 Subject: rtlwifi: fix memory leak for USB device Free skb for received frames with a wrong checksum. This can happen pretty rapidly, exhausting all memory. This fixes a memleak (detected with kmemleak). Originally found while using monitor mode, but it also appears during managed mode (once the link is up). Cc: stable@vger.kernel.org Signed-off-by: Peter Wu ACKed-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/usb.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c index 2721cf89fb16..aac1ed3f7bb4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.c +++ b/drivers/net/wireless/realtek/rtlwifi/usb.c @@ -531,6 +531,8 @@ static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw, ieee80211_rx(hw, skb); else dev_kfree_skb_any(skb); + } else { + dev_kfree_skb_any(skb); } } -- cgit v1.2.3-59-g8ed1b From 31ced24d88f1f8cfb420a25ddfc6eeda1a38d471 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 9 Dec 2015 17:42:41 +0100 Subject: iwlegacy: mark il_adjust_beacon_interval as noinline With the new optimized do_div() code, some versions of gcc produce obviously incorrect code that leads to a link error in iwlegacy/common.o: drivers/built-in.o: In function `il_send_rxon_timing': :(.text+0xa6b4d4): undefined reference to `____ilog2_NaN' :(.text+0xa6b4f0): undefined reference to `__aeabi_uldivmod' In a few thousand randconfig builds, I have seen this problem a couple of times in this file, but never anywhere else in the kernel, so we can try to work around this in the only file that shows the behavior, by marking the il_adjust_beacon_interval function as noinline, which convinces gcc to use the unoptimized do_div() all the time. Signed-off-by: Arnd Bergmann Acked-by: Nicolas Pitre Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/iwlegacy/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c index e49bad261a76..eb5cb603bc52 100644 --- a/drivers/net/wireless/intel/iwlegacy/common.c +++ b/drivers/net/wireless/intel/iwlegacy/common.c @@ -3602,7 +3602,7 @@ il_is_ht40_tx_allowed(struct il_priv *il, struct ieee80211_sta_ht_cap *ht_cap) } EXPORT_SYMBOL(il_is_ht40_tx_allowed); -static u16 +static u16 noinline il_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) { u16 new_val; -- cgit v1.2.3-59-g8ed1b From 1678ba8ed3500729f3d457791f9a77f67e7ff7d4 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 10 Dec 2015 13:43:00 +0100 Subject: brcmfmac: Simplify scan timing configuration No need to pass timing configuration in local functions as they are static anyway. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 31 +++++++++------------- .../broadcom/brcm80211/brcmfmac/cfg80211.h | 4 --- 2 files changed, 12 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 771c5819a6e3..4aebb2a9d3aa 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -91,6 +91,10 @@ #define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 #define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20 +#define BRCMF_SCAN_CHANNEL_TIME 40 +#define BRCMF_SCAN_UNASSOC_TIME 40 +#define BRCMF_SCAN_PASSIVE_TIME 120 + #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) @@ -5357,37 +5361,27 @@ roam_setup_done: } static s32 -brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time, - s32 scan_unassoc_time, s32 scan_passive_time) +brcmf_dongle_scantime(struct brcmf_if *ifp) { s32 err = 0; err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, - scan_assoc_time); + BRCMF_SCAN_CHANNEL_TIME); if (err) { - if (err == -EOPNOTSUPP) - brcmf_dbg(INFO, "Scan assoc time is not supported\n"); - else - brcmf_err("Scan assoc time error (%d)\n", err); + brcmf_err("Scan assoc time error (%d)\n", err); goto dongle_scantime_out; } err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, - scan_unassoc_time); + BRCMF_SCAN_UNASSOC_TIME); if (err) { - if (err == -EOPNOTSUPP) - brcmf_dbg(INFO, "Scan unassoc time is not supported\n"); - else - brcmf_err("Scan unassoc time error (%d)\n", err); + brcmf_err("Scan unassoc time error (%d)\n", err); goto dongle_scantime_out; } err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME, - scan_passive_time); + BRCMF_SCAN_PASSIVE_TIME); if (err) { - if (err == -EOPNOTSUPP) - brcmf_dbg(INFO, "Scan passive time is not supported\n"); - else - brcmf_err("Scan passive time error (%d)\n", err); + brcmf_err("Scan passive time error (%d)\n", err); goto dongle_scantime_out; } @@ -6089,8 +6083,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) /* make sure RF is ready for work */ brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); - brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME, - WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME); + brcmf_dongle_scantime(ifp); power_mode = cfg->pwr_save ? PM_FAST : PM_OFF; err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index d492163e4de9..cf7a93d711e5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -29,10 +29,6 @@ #define WL_ROAM_TRIGGER_LEVEL -75 #define WL_ROAM_DELTA 20 -#define WL_SCAN_CHANNEL_TIME 40 -#define WL_SCAN_UNASSOC_TIME 40 -#define WL_SCAN_PASSIVE_TIME 120 - #define WL_ESCAN_BUF_SIZE (1024 * 64) #define WL_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */ -- cgit v1.2.3-59-g8ed1b From 675f5d82a73f4cd3894c60a97dee9c17a676a954 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 10 Dec 2015 13:43:01 +0100 Subject: brcmfmac: Use local storage for ssid iovar Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 18 +++++++++--------- .../wireless/broadcom/brcm80211/brcmfmac/cfg80211.h | 7 ------- 2 files changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 4aebb2a9d3aa..b7b60f52a6cc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1031,11 +1031,11 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, struct brcmf_if *ifp = vif->ifp; struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct cfg80211_ssid *ssids; - struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int; u32 passive_scan; bool escan_req; bool spec_scan; s32 err; + struct brcmf_ssid_le ssid_le; u32 SSID_len; brcmf_dbg(SCAN, "START ESCAN\n"); @@ -1088,13 +1088,13 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, } else { brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n", ssids->ssid, ssids->ssid_len); - memset(&sr->ssid_le, 0, sizeof(sr->ssid_le)); - SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len); - sr->ssid_le.SSID_len = cpu_to_le32(0); + memset(&ssid_le, 0, sizeof(ssid_le)); + SSID_len = min_t(u8, sizeof(ssid_le.SSID), ssids->ssid_len); + ssid_le.SSID_len = cpu_to_le32(0); spec_scan = false; if (SSID_len) { - memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len); - sr->ssid_le.SSID_len = cpu_to_le32(SSID_len); + memcpy(ssid_le.SSID, ssids->ssid, SSID_len); + ssid_le.SSID_len = cpu_to_le32(SSID_len); spec_scan = true; } else brcmf_dbg(SCAN, "Broadcast scan\n"); @@ -1107,12 +1107,12 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, goto scan_out; } brcmf_scan_config_mpc(ifp, 0); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, - &sr->ssid_le, sizeof(sr->ssid_le)); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &ssid_le, + sizeof(ssid_le)); if (err) { if (err == -EBUSY) brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n", - sr->ssid_le.SSID); + ssid_le.SSID); else brcmf_err("WLC_SCAN error (%d)\n", err); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index cf7a93d711e5..e77f1375b86e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -99,11 +99,6 @@ struct brcmf_cfg80211_conf { struct ieee80211_channel channel; }; -/* basic structure of scan request */ -struct brcmf_cfg80211_scan_req { - struct brcmf_ssid_le ssid_le; -}; - /* basic structure of information element */ struct brcmf_cfg80211_ie { u16 offset; @@ -337,7 +332,6 @@ struct brcmf_cfg80211_vif_event { * @scan_request: cfg80211 scan request object. * @usr_sync: mainly for dongle up/down synchronization. * @bss_list: bss_list holding scanned ap information. - * @scan_req_int: internal scan request object. * @bss_info: bss information for cfg80211 layer. * @ie: information element object for internal purpose. * @conn_info: association info. @@ -372,7 +366,6 @@ struct brcmf_cfg80211_info { struct brcmf_btcoex_info *btcoex; struct cfg80211_scan_request *scan_request; struct mutex usr_sync; - struct brcmf_cfg80211_scan_req scan_req_int; struct wl_cfg80211_bss_info *bss_info; struct brcmf_cfg80211_ie ie; struct brcmf_cfg80211_connect_info conn_info; -- cgit v1.2.3-59-g8ed1b From 4235edcdfc30e07dfa15d578a58b8411fb770c05 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 10 Dec 2015 13:43:02 +0100 Subject: brcmfmac: Remove some redundant cfg80211 data Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 1 - drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h | 10 ---------- 2 files changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index b7b60f52a6cc..5432c46fea62 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -5203,7 +5203,6 @@ static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) conf->rts_threshold = (u32)-1; conf->retry_short = (u32)-1; conf->retry_long = (u32)-1; - conf->tx_power = -1; } static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index e77f1375b86e..ec8d1f848db4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -95,14 +95,6 @@ struct brcmf_cfg80211_conf { u32 rts_threshold; u32 retry_short; u32 retry_long; - s32 tx_power; - struct ieee80211_channel channel; -}; - -/* basic structure of information element */ -struct brcmf_cfg80211_ie { - u16 offset; - u8 buf[WL_TLV_INFO_MAX]; }; /* security information with currently associated ap */ @@ -333,7 +325,6 @@ struct brcmf_cfg80211_vif_event { * @usr_sync: mainly for dongle up/down synchronization. * @bss_list: bss_list holding scanned ap information. * @bss_info: bss information for cfg80211 layer. - * @ie: information element object for internal purpose. * @conn_info: association info. * @pmk_list: wpa2 pmk list. * @scan_status: scan activity on the dongle. @@ -367,7 +358,6 @@ struct brcmf_cfg80211_info { struct cfg80211_scan_request *scan_request; struct mutex usr_sync; struct wl_cfg80211_bss_info *bss_info; - struct brcmf_cfg80211_ie ie; struct brcmf_cfg80211_connect_info conn_info; struct brcmf_cfg80211_pmk_list *pmk_list; unsigned long scan_status; -- cgit v1.2.3-59-g8ed1b From 6c404f34f2bdd422e24783287520ebaed036657a Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 10 Dec 2015 13:43:03 +0100 Subject: brcmfmac: Cleanup pmksa cache handling code Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 131 +++++++++------------ .../broadcom/brcm80211/brcmfmac/cfg80211.h | 9 +- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 24 ++++ .../broadcom/brcm80211/include/brcmu_wifi.h | 23 ---- 4 files changed, 82 insertions(+), 105 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 5432c46fea62..564e533215ae 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3223,26 +3223,22 @@ exit: } static __used s32 -brcmf_update_pmklist(struct net_device *ndev, - struct brcmf_cfg80211_pmk_list *pmk_list, s32 err) +brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp) { - int i, j; - u32 pmkid_len; + struct brcmf_pmk_list_le *pmk_list; + int i; + u32 npmk; + s32 err; - pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid); + pmk_list = &cfg->pmk_list; + npmk = le32_to_cpu(pmk_list->npmk); - brcmf_dbg(CONN, "No of elements %d\n", pmkid_len); - for (i = 0; i < pmkid_len; i++) { - brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i, - &pmk_list->pmkids.pmkid[i].BSSID); - for (j = 0; j < WLAN_PMKID_LEN; j++) - brcmf_dbg(CONN, "%02x\n", - pmk_list->pmkids.pmkid[i].PMKID[j]); - } + brcmf_dbg(CONN, "No of elements %d\n", npmk); + for (i = 0; i < npmk; i++) + brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid); - if (!err) - brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info", - (char *)pmk_list, sizeof(*pmk_list)); + err = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list, + sizeof(*pmk_list)); return err; } @@ -3253,34 +3249,37 @@ brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); - struct pmkid_list *pmkids = &cfg->pmk_list->pmkids; - s32 err = 0; - u32 pmkid_len, i; + struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0]; + s32 err; + u32 npmk, i; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) return -EIO; - pmkid_len = le32_to_cpu(pmkids->npmkid); - for (i = 0; i < pmkid_len; i++) - if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN)) + npmk = le32_to_cpu(cfg->pmk_list.npmk); + for (i = 0; i < npmk; i++) + if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN)) break; - if (i < WL_NUM_PMKIDS_MAX) { - memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN); - memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); - if (i == pmkid_len) { - pmkid_len++; - pmkids->npmkid = cpu_to_le32(pmkid_len); + if (i < BRCMF_MAXPMKID) { + memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN); + memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); + if (i == npmk) { + npmk++; + cfg->pmk_list.npmk = cpu_to_le32(npmk); } - } else - err = -EINVAL; + } else { + brcmf_err("Too many PMKSA entries cached %d\n", npmk); + return -EINVAL; + } - brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n", - pmkids->pmkid[pmkid_len].BSSID); - for (i = 0; i < WLAN_PMKID_LEN; i++) - brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]); + brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid); + for (i = 0; i < WLAN_PMKID_LEN; i += 4) + brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i], + pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2], + pmk[npmk].pmkid[i + 3]); - err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); + err = brcmf_update_pmklist(cfg, ifp); brcmf_dbg(TRACE, "Exit\n"); return err; @@ -3288,50 +3287,39 @@ brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, static s32 brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_pmksa *pmksa) + struct cfg80211_pmksa *pmksa) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); - struct pmkid_list pmkid; - s32 err = 0; - u32 pmkid_len, i; + struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0]; + s32 err; + u32 npmk, i; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) return -EIO; - memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN); - memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); + brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", &pmksa->bssid); - brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n", - &pmkid.pmkid[0].BSSID); - for (i = 0; i < WLAN_PMKID_LEN; i++) - brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]); - - pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid); - for (i = 0; i < pmkid_len; i++) - if (!memcmp - (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID, - ETH_ALEN)) + npmk = le32_to_cpu(cfg->pmk_list.npmk); + for (i = 0; i < npmk; i++) + if (!memcmp(&pmksa->bssid, &pmk[i].bssid, ETH_ALEN)) break; - if ((pmkid_len > 0) - && (i < pmkid_len)) { - memset(&cfg->pmk_list->pmkids.pmkid[i], 0, - sizeof(struct pmkid)); - for (; i < (pmkid_len - 1); i++) { - memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID, - &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID, - ETH_ALEN); - memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID, - &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID, + if ((npmk > 0) && (i < npmk)) { + for (; i < (npmk - 1); i++) { + memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN); + memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid, WLAN_PMKID_LEN); } - cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1); - } else - err = -EINVAL; + memset(&pmk[i], 0, sizeof(*pmk)); + cfg->pmk_list.npmk = cpu_to_le32(npmk - 1); + } else { + brcmf_err("Cache entry not found\n"); + return -EINVAL; + } - err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); + err = brcmf_update_pmklist(cfg, ifp); brcmf_dbg(TRACE, "Exit\n"); return err; @@ -3343,14 +3331,14 @@ brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); - s32 err = 0; + s32 err; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) return -EIO; - memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list)); - err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); + memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list)); + err = brcmf_update_pmklist(cfg, ifp); brcmf_dbg(TRACE, "Exit\n"); return err; @@ -5249,8 +5237,6 @@ static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) cfg->escan_ioctl_buf = NULL; kfree(cfg->extra_buf); cfg->extra_buf = NULL; - kfree(cfg->pmk_list); - cfg->pmk_list = NULL; } static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) @@ -5264,9 +5250,6 @@ static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); if (!cfg->extra_buf) goto init_priv_mem_out; - cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL); - if (!cfg->pmk_list) - goto init_priv_mem_out; return 0; @@ -5971,7 +5954,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; - wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; + wiphy->max_num_pmkids = BRCMF_MAXPMKID; err = brcmf_setup_ifmodes(wiphy, ifp); if (err) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index ec8d1f848db4..397d41bd9a1d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -21,7 +21,6 @@ #include #define WL_NUM_SCAN_MAX 10 -#define WL_NUM_PMKIDS_MAX MAXPMKID #define WL_TLV_INFO_MAX 1024 #define WL_BSS_INFO_MAX 2048 #define WL_ASSOC_INFO_MAX 512 /* assoc related fil max buf */ @@ -196,12 +195,6 @@ struct brcmf_cfg80211_assoc_ielen_le { __le32 resp_len; }; -/* wpa2 pmk list */ -struct brcmf_cfg80211_pmk_list { - struct pmkid_list pmkids; - struct pmkid foo[MAXPMKID - 1]; -}; - /* dongle escan state */ enum wl_escan_state { WL_ESCAN_STATE_IDLE, @@ -359,7 +352,7 @@ struct brcmf_cfg80211_info { struct mutex usr_sync; struct wl_cfg80211_bss_info *bss_info; struct brcmf_cfg80211_connect_info conn_info; - struct brcmf_cfg80211_pmk_list *pmk_list; + struct brcmf_pmk_list_le pmk_list; unsigned long scan_status; struct brcmf_pub *pub; u32 channel; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 18483e782bfe..e56eabc16fdd 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -126,6 +126,8 @@ #define BRCMF_TXBF_SU_BFR_CAP BIT(0) #define BRCMF_TXBF_MU_BFR_CAP BIT(1) +#define BRCMF_MAXPMKID 16 /* max # PMKID cache entries */ + /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { BRCMF_JOIN_PREF_RSSI = 1, @@ -646,4 +648,26 @@ struct brcmf_wowl_wakeind_le { __le32 ucode_wakeind; }; +/** + * struct brcmf_pmksa - PMK Security Association + * + * @bssid: The AP's BSSID. + * @pmkid: he PMK material itself. + */ +struct brcmf_pmksa { + u8 bssid[ETH_ALEN]; + u8 pmkid[WLAN_PMKID_LEN]; +}; + +/** + * struct brcmf_pmk_list_le - List of pmksa's. + * + * @npmk: Number of pmksa's. + * @pmk: PMK SA information. + */ +struct brcmf_pmk_list_le { + __le32 npmk; + struct brcmf_pmksa pmk[BRCMF_MAXPMKID]; +}; + #endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h index 76b5d3a86294..3f68dd5ecd11 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h @@ -237,9 +237,6 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec) #define WPA2_AUTH_RESERVED4 0x0400 #define WPA2_AUTH_RESERVED5 0x0800 -/* pmkid */ -#define MAXPMKID 16 - #define DOT11_DEFAULT_RTS_LEN 2347 #define DOT11_DEFAULT_FRAG_LEN 2346 @@ -251,24 +248,4 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec) #define HT_CAP_RX_STBC_NO 0x0 #define HT_CAP_RX_STBC_ONE_STREAM 0x1 -struct pmkid { - u8 BSSID[ETH_ALEN]; - u8 PMKID[WLAN_PMKID_LEN]; -}; - -struct pmkid_list { - __le32 npmkid; - struct pmkid pmkid[1]; -}; - -struct pmkid_cand { - u8 BSSID[ETH_ALEN]; - u8 preauth; -}; - -struct pmkid_cand_list { - u32 npmkid_cand; - struct pmkid_cand pmkid_cand[1]; -}; - #endif /* _BRCMU_WIFI_H_ */ -- cgit v1.2.3-59-g8ed1b From a7b82d474171ff93ae9a6a7b39b0cc1ec376b06d Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 10 Dec 2015 13:43:04 +0100 Subject: brcmfmac: Make TDLS a detectable feature Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 21 ++++++++++++--------- .../wireless/broadcom/brcm80211/brcmfmac/feature.c | 1 + .../wireless/broadcom/brcm80211/brcmfmac/feature.h | 4 +++- 3 files changed, 16 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 564e533215ae..8557566fff54 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -5983,8 +5983,9 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | WIPHY_FLAG_OFFCHAN_TX | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_SUPPORTS_TDLS; + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; if (!brcmf_roamoff) wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; wiphy->mgmt_stypes = brcmf_txrx_stypes; @@ -6376,13 +6377,15 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto wiphy_unreg_out; } - err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); - if (err) { - brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); - wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; - } else { - brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT, - brcmf_notify_tdls_peer_event); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) { + err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); + if (err) { + brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); + wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; + } else { + brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT, + brcmf_notify_tdls_peer_event); + } } /* (re-) activate FWEH event handling */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index ba5249403036..d9d1ca4b93ec 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -138,6 +138,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable"); if (brcmf_feature_disable) { brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index 538175882c8a..13906568bc2d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -25,6 +25,7 @@ * WOWL: Wake-On-WLAN. * P2P: peer-to-peer * RSDB: Real Simultaneous Dual Band + * TDLS: Tunneled Direct Link Setup */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ @@ -32,7 +33,8 @@ BRCMF_FEAT_DEF(PNO) \ BRCMF_FEAT_DEF(WOWL) \ BRCMF_FEAT_DEF(P2P) \ - BRCMF_FEAT_DEF(RSDB) + BRCMF_FEAT_DEF(RSDB) \ + BRCMF_FEAT_DEF(TDLS) /* * Quirks: -- cgit v1.2.3-59-g8ed1b From fdf3456422972016ab802317ba1c2ce5969dd7a6 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 10 Dec 2015 13:43:05 +0100 Subject: brcmfmac: Add support for PCIE 4350 revision 5 device Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index b5a4247604f3..4ebc53c757cb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -46,6 +46,7 @@ enum brcmf_pcie_state { BRCMF_FW_NVRAM_DEF(43602, "brcmfmac43602-pcie.bin", "brcmfmac43602-pcie.txt"); BRCMF_FW_NVRAM_DEF(4350, "brcmfmac4350-pcie.bin", "brcmfmac4350-pcie.txt"); +BRCMF_FW_NVRAM_DEF(4350C, "brcmfmac4350c2-pcie.bin", "brcmfmac4350c2-pcie.txt"); BRCMF_FW_NVRAM_DEF(4356, "brcmfmac4356-pcie.bin", "brcmfmac4356-pcie.txt"); BRCMF_FW_NVRAM_DEF(43570, "brcmfmac43570-pcie.bin", "brcmfmac43570-pcie.txt"); BRCMF_FW_NVRAM_DEF(4358, "brcmfmac4358-pcie.bin", "brcmfmac4358-pcie.txt"); @@ -56,7 +57,8 @@ BRCMF_FW_NVRAM_DEF(4371, "brcmfmac4371-pcie.bin", "brcmfmac4371-pcie.txt"); static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602), - BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0xFFFFFFFF, 4350), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0x000000FF, 4350C), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0xFFFFFF00, 4350), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43567_CHIP_ID, 0xFFFFFFFF, 43570), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43570), -- cgit v1.2.3-59-g8ed1b From 353c46ac9e530f511fba9387e1e63226eb168e63 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 10 Dec 2015 13:43:06 +0100 Subject: brcmfmac: no interface combination check for single interface The interface combinations are intended for use-case in which the driver handles multiple interface concurrently. This means that the combinations do not need to be checked when there is only a single interface active. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 8557566fff54..3ac5cf79cd2b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -396,15 +396,23 @@ static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg, { int iftype_num[NUM_NL80211_IFTYPES]; struct brcmf_cfg80211_vif *pos; + bool check_combos = false; + int ret = 0; memset(&iftype_num[0], 0, sizeof(iftype_num)); list_for_each_entry(pos, &cfg->vif_list, list) - if (pos == vif) + if (pos == vif) { iftype_num[new_type]++; - else + } else { + /* concurrent interfaces so need check combinations */ + check_combos = true; iftype_num[pos->wdev.iftype]++; + } - return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); + if (check_combos) + ret = cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); + + return ret; } static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg, -- cgit v1.2.3-59-g8ed1b From b0a790883ed4d12b2c6c0fd19cda421ba7bb84ca Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 10 Dec 2015 13:43:07 +0100 Subject: brcmfmac: Fix IBSS setup IBSS got broken over time. Disconnect events should not be given for IBSS mode and connect events for IBSS need to have channel information. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 3ac5cf79cd2b..17658b326a6c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1273,17 +1273,17 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason) brcmf_dbg(TRACE, "Enter\n"); - if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) { + if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) { brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n "); err = brcmf_fil_cmd_data_set(vif->ifp, BRCMF_C_DISASSOC, NULL, 0); if (err) { brcmf_err("WLC_DISASSOC failed (%d)\n", err); } - clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state); - cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0, - true, GFP_KERNEL); - + if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) || + (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) + cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0, + true, GFP_KERNEL); } clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); @@ -2697,8 +2697,8 @@ static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg) return err; } -static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, const u8 *bssid) +static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg, + struct net_device *ndev, const u8 *bssid) { struct wiphy *wiphy = cfg_to_wiphy(cfg); struct ieee80211_channel *notify_channel; @@ -2743,6 +2743,7 @@ static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg, band = wiphy->bands[IEEE80211_BAND_5GHZ]; freq = ieee80211_channel_to_frequency(ch.chnum, band->band); + cfg->channel = freq; notify_channel = ieee80211_get_channel(wiphy, freq); notify_capability = le16_to_cpu(bi->capability); @@ -5071,9 +5072,9 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, } else if (brcmf_is_linkup(e)) { brcmf_dbg(CONN, "Linkup\n"); if (brcmf_is_ibssmode(ifp->vif)) { + brcmf_inform_ibss(cfg, ndev, e->addr); chan = ieee80211_get_channel(cfg->wiphy, cfg->channel); memcpy(profile->bssid, e->addr, ETH_ALEN); - wl_inform_ibss(cfg, ndev, e->addr); cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL); clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); -- cgit v1.2.3-59-g8ed1b From a7decc44a002dffe7ee6e361ddc703b8c3035d2c Mon Sep 17 00:00:00 2001 From: Kosuke Tatsukawa Date: Thu, 10 Dec 2015 13:43:08 +0100 Subject: brcmfmac: fix waitqueue_active without memory barrier in brcmfmac driver brcmf_msgbuf_ioctl_resp_wake() seems to be missing a memory barrier which might cause the waker to not notice the waiter and miss sending a wake_up as in the following figure. brcmf_msgbuf_ioctl_resp_wake brcmf_msgbuf_ioctl_resp_wait ------------------------------------------------------------------------ if (waitqueue_active(&msgbuf->ioctl_resp_wait)) /* The CPU might reorder the test for the waitqueue up here, before prior writes complete */ /* wait_event_timeout */ /* __wait_event_timeout */ /* ___wait_event */ prepare_to_wait_event(&wq, &__wait, state); if (msgbuf->ctl_completed) ... msgbuf->ctl_completed = true; schedule_timeout(__ret)) ------------------------------------------------------------------------ There are three other place in drivers/net/wireless/brcm80211/brcmfmac/ which have similar code. The attached patch removes the call to waitqueue_active() leaving just wake_up() behind. This fixes the problem because the call to spin_lock_irqsave() in wake_up() will be an ACQUIRE operation. I found this issue when I was looking through the linux source code for places calling waitqueue_active() before wake_up*(), but without preceding memory barriers, after sending a patch to fix a similar issue in drivers/tty/n_tty.c (Details about the original issue can be found here: https://lkml.org/lkml/2015/9/28/849). Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Kosuke Tatsukawa Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 3 +-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 6 ++---- drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 3 +-- 3 files changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 44e618f9d890..5df91386e13a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -473,8 +473,7 @@ static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf) static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf) { msgbuf->ctl_completed = true; - if (waitqueue_active(&msgbuf->ioctl_resp_wait)) - wake_up(&msgbuf->ioctl_resp_wait); + wake_up(&msgbuf->ioctl_resp_wait); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 9fa3a3a0f12c..ceb2a754308e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -1678,8 +1678,7 @@ static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus) { - if (waitqueue_active(&bus->dcmd_resp_wait)) - wake_up_interruptible(&bus->dcmd_resp_wait); + wake_up_interruptible(&bus->dcmd_resp_wait); return 0; } @@ -2003,8 +2002,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) static void brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus) { - if (waitqueue_active(&bus->ctrl_wait)) - wake_up_interruptible(&bus->ctrl_wait); + wake_up_interruptible(&bus->ctrl_wait); return; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index ccde5599c401..23eaf0f71c43 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -196,8 +196,7 @@ static int brcmf_usb_ioctl_resp_wait(struct brcmf_usbdev_info *devinfo) static void brcmf_usb_ioctl_resp_wake(struct brcmf_usbdev_info *devinfo) { - if (waitqueue_active(&devinfo->ioctl_resp_wait)) - wake_up(&devinfo->ioctl_resp_wait); + wake_up(&devinfo->ioctl_resp_wait); } static void -- cgit v1.2.3-59-g8ed1b From 540313d8f9547b63882d74d28a60cda35971d332 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 10 Dec 2015 13:43:09 +0100 Subject: brcmfmac: add 43242 device id for LG dongle Got a hint on IRC that a 43242 dongle for LG smart TV's works with brcmfmac: > Hello - I recently got a LG AN-WF500 wireless dongle for LG's SmartTVs. > From the information I gathered it uses a Broadcom chipset BCM43242. > The device should have been supported by brcmfmac kernel driver if it used > USB IDs 0a5c:bd1f. > My device however identifies itself as "ID 043e:3101 LG Electronics USA, Inc.". > I then tried adding the USB ID to the driver with > "echo "043e 3101" > /sys/bus/usb/drivers/brcmfmac/new_id" and it just works. > The kernel provides the following information. > [15958.851291] usb 3-1.1.3: new high-speed USB device number 53 using ehci-pci > [15958.946723] usb 3-1.1.3: New USB device found, idVendor=043e, idProduct=3101 > [15958.946728] usb 3-1.1.3: New USB device strings: Mfr=1, Product=4, SerialNumber=3 > [15958.946731] usb 3-1.1.3: Product: Composite Wireless Adapter > [15958.946733] usb 3-1.1.3: Manufacturer: Broadcom > [15958.946735] usb 3-1.1.3: SerialNumber: 28458 Reported-by: Johannes Berg Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 1 + drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 23eaf0f71c43..66c26a92b29c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1452,6 +1452,7 @@ static struct usb_device_id brcmf_usb_devid_table[] = { BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID), BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID), BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID), + { USB_DEVICE(BRCM_USB_VENDOR_ID_LG, BRCM_USB_43242_LG_DEVICE_ID) }, /* special entry for device with firmware loaded and running */ BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID), { /* end: all zeroes */ } diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h index 4092d2789979..699f2c2782ee 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h @@ -21,6 +21,7 @@ #include #define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c +#define BRCM_USB_VENDOR_ID_LG 0x043e #define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM /* Chipcommon Core Chip IDs */ @@ -57,6 +58,7 @@ #define BRCM_USB_43143_DEVICE_ID 0xbd1e #define BRCM_USB_43236_DEVICE_ID 0xbd17 #define BRCM_USB_43242_DEVICE_ID 0xbd1f +#define BRCM_USB_43242_LG_DEVICE_ID 0x3101 #define BRCM_USB_43569_DEVICE_ID 0xbd27 #define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc -- cgit v1.2.3-59-g8ed1b From f3fb75038dd1174764521303a746fe9de67eb0e5 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 10 Dec 2015 13:43:10 +0100 Subject: brcmfmac: Change error print in debug print The pcie suspend and resume routines contain some error prints, which should have been debug prints. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 4ebc53c757cb..3d2d790d3ad6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -1875,7 +1875,7 @@ static int brcmf_pcie_pm_enter_D3(struct device *dev) struct brcmf_pciedev_info *devinfo; struct brcmf_bus *bus; - brcmf_err("Enter\n"); + brcmf_dbg(PCIE, "Enter\n"); bus = dev_get_drvdata(dev); devinfo = bus->bus_priv.pcie->devinfo; @@ -1906,7 +1906,7 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev) struct pci_dev *pdev; int err; - brcmf_err("Enter\n"); + brcmf_dbg(PCIE, "Enter\n"); bus = dev_get_drvdata(dev); devinfo = bus->bus_priv.pcie->devinfo; -- cgit v1.2.3-59-g8ed1b From a41286aee42f1a4a1d5abc7ce6904360a6e3de7e Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 10 Dec 2015 13:43:11 +0100 Subject: brcmfmac: Move scheduled scan related interface layer structs All interface layer related to scheduled scan are moved in fwil_types.h Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.h | 81 ---------------------- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 81 ++++++++++++++++++++++ 2 files changed, 81 insertions(+), 81 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 397d41bd9a1d..c17b6d584fe0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -210,87 +210,6 @@ struct escan_info { struct cfg80211_scan_request *request); }; -/** - * struct brcmf_pno_param_le - PNO scan configuration parameters - * - * @version: PNO parameters version. - * @scan_freq: scan frequency. - * @lost_network_timeout: #sec. to declare discovered network as lost. - * @flags: Bit field to control features of PFN such as sort criteria auto - * enable switch and background scan. - * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort - * criteria. - * @bestn: number of best networks in each scan. - * @mscan: number of scans recorded. - * @repeat: minimum number of scan intervals before scan frequency changes - * in adaptive scan. - * @exp: exponent of 2 for maximum scan interval. - * @slow_freq: slow scan period. - */ -struct brcmf_pno_param_le { - __le32 version; - __le32 scan_freq; - __le32 lost_network_timeout; - __le16 flags; - __le16 rssi_margin; - u8 bestn; - u8 mscan; - u8 repeat; - u8 exp; - __le32 slow_freq; -}; - -/** - * struct brcmf_pno_net_param_le - scan parameters per preferred network. - * - * @ssid: ssid name and its length. - * @flags: bit2: hidden. - * @infra: BSS vs IBSS. - * @auth: Open vs Closed. - * @wpa_auth: WPA type. - * @wsec: wsec value. - */ -struct brcmf_pno_net_param_le { - struct brcmf_ssid_le ssid; - __le32 flags; - __le32 infra; - __le32 auth; - __le32 wpa_auth; - __le32 wsec; -}; - -/** - * struct brcmf_pno_net_info_le - information per found network. - * - * @bssid: BSS network identifier. - * @channel: channel number only. - * @SSID_len: length of ssid. - * @SSID: ssid characters. - * @RSSI: receive signal strength (in dBm). - * @timestamp: age in seconds. - */ -struct brcmf_pno_net_info_le { - u8 bssid[ETH_ALEN]; - u8 channel; - u8 SSID_len; - u8 SSID[32]; - __le16 RSSI; - __le16 timestamp; -}; - -/** - * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event. - * - * @version: PNO version identifier. - * @status: indicates completion status of PNO scan. - * @count: amount of brcmf_pno_net_info_le entries appended. - */ -struct brcmf_pno_scanresults_le { - __le32 version; - __le32 status; - __le32 count; -}; - /** * struct brcmf_cfg80211_vif_event - virtual interface event information. * diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index e56eabc16fdd..94d34ad3a6e6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -670,4 +670,85 @@ struct brcmf_pmk_list_le { struct brcmf_pmksa pmk[BRCMF_MAXPMKID]; }; +/** + * struct brcmf_pno_param_le - PNO scan configuration parameters + * + * @version: PNO parameters version. + * @scan_freq: scan frequency. + * @lost_network_timeout: #sec. to declare discovered network as lost. + * @flags: Bit field to control features of PFN such as sort criteria auto + * enable switch and background scan. + * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort + * criteria. + * @bestn: number of best networks in each scan. + * @mscan: number of scans recorded. + * @repeat: minimum number of scan intervals before scan frequency changes + * in adaptive scan. + * @exp: exponent of 2 for maximum scan interval. + * @slow_freq: slow scan period. + */ +struct brcmf_pno_param_le { + __le32 version; + __le32 scan_freq; + __le32 lost_network_timeout; + __le16 flags; + __le16 rssi_margin; + u8 bestn; + u8 mscan; + u8 repeat; + u8 exp; + __le32 slow_freq; +}; + +/** + * struct brcmf_pno_net_param_le - scan parameters per preferred network. + * + * @ssid: ssid name and its length. + * @flags: bit2: hidden. + * @infra: BSS vs IBSS. + * @auth: Open vs Closed. + * @wpa_auth: WPA type. + * @wsec: wsec value. + */ +struct brcmf_pno_net_param_le { + struct brcmf_ssid_le ssid; + __le32 flags; + __le32 infra; + __le32 auth; + __le32 wpa_auth; + __le32 wsec; +}; + +/** + * struct brcmf_pno_net_info_le - information per found network. + * + * @bssid: BSS network identifier. + * @channel: channel number only. + * @SSID_len: length of ssid. + * @SSID: ssid characters. + * @RSSI: receive signal strength (in dBm). + * @timestamp: age in seconds. + */ +struct brcmf_pno_net_info_le { + u8 bssid[ETH_ALEN]; + u8 channel; + u8 SSID_len; + u8 SSID[32]; + __le16 RSSI; + __le16 timestamp; +}; + +/** + * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event. + * + * @version: PNO version identifier. + * @status: indicates completion status of PNO scan. + * @count: amount of brcmf_pno_net_info_le entries appended. + */ +struct brcmf_pno_scanresults_le { + __le32 version; + __le32 status; + __le32 count; +}; + #endif /* FWIL_TYPES_H_ */ -- cgit v1.2.3-59-g8ed1b From 9b2761cb72dc41e1948c8a5512b4efd384eda130 Mon Sep 17 00:00:00 2001 From: Uri Mashiach Date: Thu, 10 Dec 2015 15:12:56 +0200 Subject: wlcore/wl12xx: spi: fix oops on firmware load The maximum chunks used by the function is (SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE + 1). The original commands array had space for (SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) commands. When the last chunk is used (len > 4 * WSPI_MAX_CHUNK_SIZE), the last command is stored outside the bounds of the commands array. Oops 5 (page fault) is generated during current wl1271 firmware load attempt: root@debian-armhf:~# ifconfig wlan0 up [ 294.312399] Unable to handle kernel paging request at virtual address 00203fc4 [ 294.320173] pgd = de528000 [ 294.323028] [00203fc4] *pgd=00000000 [ 294.326916] Internal error: Oops: 5 [#1] SMP ARM [ 294.331789] Modules linked in: bnep rfcomm bluetooth ipv6 arc4 wl12xx wlcore mac80211 musb_dsps cfg80211 musb_hdrc usbcore usb_common wlcore_spi omap_rng rng_core musb_am335x omap_wdt cpufreq_dt thermal_sys hwmon [ 294.351838] CPU: 0 PID: 1827 Comm: ifconfig Not tainted 4.2.0-00002-g3e9ad27-dirty #78 [ 294.360154] Hardware name: Generic AM33XX (Flattened Device Tree) [ 294.366557] task: dc9d6d40 ti: de550000 task.ti: de550000 [ 294.372236] PC is at __spi_validate+0xa8/0x2ac [ 294.376902] LR is at __spi_sync+0x78/0x210 [ 294.381200] pc : [] lr : [] psr: 60000013 [ 294.381200] sp : de551998 ip : de5519d8 fp : 00200000 [ 294.393242] r10: de551c8c r9 : de5519d8 r8 : de3a9000 [ 294.398730] r7 : de3a9258 r6 : de3a9400 r5 : de551a48 r4 : 00203fbc [ 294.405577] r3 : 00000000 r2 : 00000000 r1 : 00000000 r0 : de3a9000 [ 294.412420] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user [ 294.419918] Control: 10c5387d Table: 9e528019 DAC: 00000015 [ 294.425954] Process ifconfig (pid: 1827, stack limit = 0xde550218) [ 294.432437] Stack: (0xde551998 to 0xde552000) ... [ 294.883613] [] (__spi_validate) from [] (__spi_sync+0x78/0x210) [ 294.891670] [] (__spi_sync) from [] (wl12xx_spi_raw_write+0xfc/0x148 [wlcore_spi]) [ 294.901661] [] (wl12xx_spi_raw_write [wlcore_spi]) from [] (wlcore_boot_upload_firmware+0x1ec/0x458 [wlcore]) [ 294.914038] [] (wlcore_boot_upload_firmware [wlcore]) from [] (wl12xx_boot+0xc10/0xfac [wl12xx]) [ 294.925161] [] (wl12xx_boot [wl12xx]) from [] (wl1271_op_add_interface+0x5b0/0x910 [wlcore]) [ 294.936364] [] (wl1271_op_add_interface [wlcore]) from [] (ieee80211_do_open+0x44c/0xf7c [mac80211]) [ 294.947963] [] (ieee80211_do_open [mac80211]) from [] (__dev_open+0xa8/0x110) [ 294.957307] [] (__dev_open) from [] (__dev_change_flags+0x88/0x148) [ 294.965713] [] (__dev_change_flags) from [] (dev_change_flags+0x18/0x48) [ 294.974576] [] (dev_change_flags) from [] (devinet_ioctl+0x6b4/0x7d0) [ 294.983191] [] (devinet_ioctl) from [] (sock_ioctl+0x1e4/0x2bc) [ 294.991244] [] (sock_ioctl) from [] (do_vfs_ioctl+0x420/0x6b0) [ 294.999208] [] (do_vfs_ioctl) from [] (SyS_ioctl+0x6c/0x7c) [ 295.006880] [] (SyS_ioctl) from [] (ret_fast_syscall+0x0/0x54) [ 295.014835] Code: e1550004 e2444034 0a00007d e5953018 (e5942008) [ 295.021544] ---[ end trace 66ed188198f4e24e ]--- Signed-off-by: Uri Mashiach Acked-by: Igor Grinberg Cc: stable@vger.kernel.org Signed-off-by: Kalle Valo --- drivers/net/wireless/ti/wlcore/spi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 236b41090827..44f059f7f34e 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -73,7 +73,10 @@ */ #define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) -#define WSPI_MAX_NUM_OF_CHUNKS (SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) +/* Maximum number of SPI write chunks */ +#define WSPI_MAX_NUM_OF_CHUNKS \ + ((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1) + struct wl12xx_spi_glue { struct device *dev; @@ -268,9 +271,10 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr, void *buf, size_t len, bool fixed) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); - struct spi_transfer t[2 * (WSPI_MAX_NUM_OF_CHUNKS + 1)]; + /* SPI write buffers - 2 for each chunk */ + struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS]; struct spi_message m; - u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; + u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */ u32 *cmd; u32 chunk_len; int i; -- cgit v1.2.3-59-g8ed1b From 26ca14d42706ae8b6053244d5aa4e2eb3707045a Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Mon, 30 Nov 2015 15:01:47 -0800 Subject: ath6kl: fix tx/rx antenna reporting for 2x2 devices My previous patch incorrectly reported the antenna for 2x2 devices. It should be a mask instead of a numeric count. This patch fixes that. Signed-off-by: Ben Greear Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 81ac8c59f0ec..7f3f94fbf157 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3930,8 +3930,8 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff; - ar->hw.tx_ant = 2; - ar->hw.rx_ant = 2; + ar->hw.tx_ant = 0x3; /* mask, 2 antenna */ + ar->hw.rx_ant = 0x3; } else { ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; -- cgit v1.2.3-59-g8ed1b From efc2b2b50c92c72916608b0e7d74651471fe5dd7 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Mon, 30 Nov 2015 15:01:48 -0800 Subject: ath6kl: add log messages for firmware failure cases. This gives a user a chance to know why a firmware load is failing. Signed-off-by: Ben Greear Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 6ae0734f86e0..da557dc742e6 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -954,8 +954,10 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, name); ret = request_firmware(&fw, filename, ar->dev); - if (ret) + if (ret) { + ath6kl_err("Failed request firmware, rv: %d\n", ret); return ret; + } data = fw->data; len = fw->size; @@ -964,11 +966,15 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1; if (len < magic_len) { + ath6kl_err("Magic length is invalid, len: %zd magic_len: %zd\n", + len, magic_len); ret = -EINVAL; goto out; } if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) { + ath6kl_err("Magic is invalid, magic_len: %zd\n", + magic_len); ret = -EINVAL; goto out; } @@ -987,7 +993,12 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) len -= sizeof(*hdr); data += sizeof(*hdr); + ath6kl_dbg(ATH6KL_DBG_BOOT, "ie-id: %d len: %zd (0x%zx)\n", + ie_id, ie_len, ie_len); + if (len < ie_len) { + ath6kl_err("IE len is invalid, len: %zd ie_len: %zd ie-id: %d\n", + len, ie_len, ie_id); ret = -EINVAL; goto out; } @@ -1008,6 +1019,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL); if (ar->fw_otp == NULL) { + ath6kl_err("fw_otp cannot be allocated\n"); ret = -ENOMEM; goto out; } @@ -1025,6 +1037,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) ar->fw = vmalloc(ie_len); if (ar->fw == NULL) { + ath6kl_err("fw storage cannot be allocated, len: %zd\n", ie_len); ret = -ENOMEM; goto out; } @@ -1039,6 +1052,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL); if (ar->fw_patch == NULL) { + ath6kl_err("fw_patch storage cannot be allocated, len: %zd\n", ie_len); ret = -ENOMEM; goto out; } -- cgit v1.2.3-59-g8ed1b From f0b2c30a20299083665f3f14870ff5ce631d32ef Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Thu, 20 Aug 2015 09:21:38 +0800 Subject: ath9k: fix AR_RX_FILTER for ar9462/ar9565 when rx stopped When rx stopped, AR_RX_FILTER should be cleared, but in ath9k_hw_setrxfilter(), ATH9K_RX_FILTER_CONTROL_WRAPPER will always be set for ar9462/ar9565. Fix this by moving the code in ath9k_hw_setrxfilter() to ath_calcrxfilter(). Signed-off-by: Miaoqing Pan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/hw.c | 3 --- drivers/net/wireless/ath/ath9k/recv.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 2e252af0b26a..257f46ed4a04 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2761,9 +2761,6 @@ void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits) ENABLE_REGWRITE_BUFFER(ah); - if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) - bits |= ATH9K_RX_FILTER_CONTROL_WRAPPER; - REG_WRITE(ah, AR_RX_FILTER, bits); phybits = 0; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 994daf6c6297..32160fca876a 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -424,6 +424,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc) AR_SREV_9561(sc->sc_ah)) rfilt |= ATH9K_RX_FILTER_4ADDRESS; + if (AR_SREV_9462(sc->sc_ah) || AR_SREV_9565(sc->sc_ah)) + rfilt |= ATH9K_RX_FILTER_CONTROL_WRAPPER; + if (ath9k_is_chanctx_enabled() && test_bit(ATH_OP_SCANNING, &common->op_flags)) rfilt |= ATH9K_RX_FILTER_BEACON; -- cgit v1.2.3-59-g8ed1b From ed14dc0af7ccea867b479feb88efdfe43ca2a0f9 Mon Sep 17 00:00:00 2001 From: Miaoqing Pan Date: Fri, 9 Oct 2015 17:06:45 +0800 Subject: ath9k: feeding entropy in kernel from ADC capture This patch is derived from commit 6301566e0b2d ("ath9k: export HW random number generator"), We evaluated the entropy of the ADC data on QCA9531, QCA9561, QCA955x, and AR9340, and it has sufficient quality random data (at least 10 bits and up to 22 bits of min-entropy for a 32-bit value). We conservatively assume the min-entropy is 10 bits out of 32 bits. Thus, ATH9K_RNG_BUF_SIZE is set to 320 (u32) i.e., 1.25 kilobytes of data is inserted to fill up the pool as soon as the entropy counter becomes 896/4096 (set by random.c). Since ADC was not designed to be a dedicated HW RNG, we do not want to bind it to /dev/hwrng framework directly. This patch feeds the entropy directly from the WiFi driver to the input pool. The ADC register output is only used as a seed for the Linux entropy pool. No conditioning is needed, since all the conditioning is performed by the pool itself. Signed-off-by: Miaoqing Pan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/Kconfig | 11 ++++ drivers/net/wireless/ath/ath9k/Makefile | 1 + drivers/net/wireless/ath/ath9k/ath9k.h | 22 +++++++ drivers/net/wireless/ath/ath9k/main.c | 4 ++ drivers/net/wireless/ath/ath9k/rng.c | 107 ++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+) create mode 100644 drivers/net/wireless/ath/ath9k/rng.c (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index fee0cadb0f5e..40fa915d6f35 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -176,3 +176,14 @@ config ATH9K_HTC_DEBUGFS depends on ATH9K_HTC && DEBUG_FS ---help--- Say Y, if you need access to ath9k_htc's statistics. + +config ATH9K_HWRNG + bool "Random number generator support" + depends on ATH9K && (HW_RANDOM = y || HW_RANDOM = ATH9K) + default y + ---help--- + This option incorporates the ADC register output as a source of + randomness into Linux entropy pool (/dev/urandom and /dev/random) + + Say Y, feeds the entropy directly from the WiFi driver to the input + pool. diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index ecda613c2d54..76f9dc37500b 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -15,6 +15,7 @@ ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o ath9k-$(CONFIG_ATH9K_TX99) += tx99.o ath9k-$(CONFIG_ATH9K_WOW) += wow.o +ath9k-$(CONFIG_ATH9K_HWRNG) += rng.o ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index acc21f60f36b..5294595da5a7 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "common.h" #include "debug.h" @@ -1041,6 +1042,11 @@ struct ath_softc { u32 wow_intr_before_sleep; bool force_wow; #endif + +#ifdef CONFIG_ATH9K_HWRNG + u32 rng_last; + struct task_struct *rng_task; +#endif }; /********/ @@ -1063,6 +1069,22 @@ static inline int ath9k_tx99_send(struct ath_softc *sc, } #endif /* CONFIG_ATH9K_TX99 */ +/***************************/ +/* Random Number Generator */ +/***************************/ +#ifdef CONFIG_ATH9K_HWRNG +void ath9k_rng_start(struct ath_softc *sc); +void ath9k_rng_stop(struct ath_softc *sc); +#else +static inline void ath9k_rng_start(struct ath_softc *sc) +{ +} + +static inline void ath9k_rng_stop(struct ath_softc *sc) +{ +} +#endif + static inline void ath_read_cachesize(struct ath_common *common, int *csz) { common->bus_ops->read_cachesize(common, csz); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d184e682e636..c1b33fdcca08 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -739,6 +739,8 @@ static int ath9k_start(struct ieee80211_hw *hw) ath9k_ps_restore(sc); + ath9k_rng_start(sc); + return 0; } @@ -828,6 +830,8 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath9k_deinit_channel_context(sc); + ath9k_rng_stop(sc); + mutex_lock(&sc->mutex); ath_cancel_work(sc); diff --git a/drivers/net/wireless/ath/ath9k/rng.c b/drivers/net/wireless/ath/ath9k/rng.c new file mode 100644 index 000000000000..c9cb2aad7b6f --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/rng.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015 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 +#include + +#include "ath9k.h" +#include "hw.h" +#include "ar9003_phy.h" + +#define ATH9K_RNG_BUF_SIZE 320 +#define ATH9K_RNG_ENTROPY(x) (((x) * 8 * 320) >> 10) /* quality: 320/1024 */ + +static int ath9k_rng_data_read(struct ath_softc *sc, u32 *buf, u32 buf_size) +{ + int i, j; + u32 v1, v2, rng_last = sc->rng_last; + struct ath_hw *ah = sc->sc_ah; + + ath9k_ps_wakeup(sc); + + REG_RMW_FIELD(ah, AR_PHY_TEST, AR_PHY_TEST_BBB_OBS_SEL, 1); + REG_CLR_BIT(ah, AR_PHY_TEST, AR_PHY_TEST_RX_OBS_SEL_BIT5); + REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS, AR_PHY_TEST_CTL_RX_OBS_SEL, 0); + + for (i = 0, j = 0; i < buf_size; i++) { + v1 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff; + v2 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff; + + /* wait for data ready */ + if (v1 && v2 && rng_last != v1 && v1 != v2 && v1 != 0xffff && + v2 != 0xffff) + buf[j++] = (v1 << 16) | v2; + + rng_last = v2; + } + + ath9k_ps_restore(sc); + + sc->rng_last = rng_last; + + return j << 2; +} + +static int ath9k_rng_kthread(void *data) +{ + int bytes_read; + struct ath_softc *sc = data; + u32 *rng_buf; + + rng_buf = kmalloc_array(ATH9K_RNG_BUF_SIZE, sizeof(u32), GFP_KERNEL); + if (!rng_buf) + goto out; + + while (!kthread_should_stop()) { + bytes_read = ath9k_rng_data_read(sc, rng_buf, + ATH9K_RNG_BUF_SIZE); + if (unlikely(!bytes_read)) { + msleep_interruptible(10); + continue; + } + + /* sleep until entropy bits under write_wakeup_threshold */ + add_hwgenerator_randomness((void *)rng_buf, bytes_read, + ATH9K_RNG_ENTROPY(bytes_read)); + } + + kfree(rng_buf); +out: + sc->rng_task = NULL; + + return 0; +} + +void ath9k_rng_start(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + + if (sc->rng_task) + return; + + if (!AR_SREV_9300_20_OR_LATER(ah)) + return; + + sc->rng_task = kthread_run(ath9k_rng_kthread, sc, "ath9k-hwrng"); + if (IS_ERR(sc->rng_task)) + sc->rng_task = NULL; +} + +void ath9k_rng_stop(struct ath_softc *sc) +{ + if (sc->rng_task) + kthread_stop(sc->rng_task); +} -- cgit v1.2.3-59-g8ed1b From ba38a1718b7386bacbaed81167a68b9a673a0a42 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 8 Dec 2015 23:49:31 +0000 Subject: ath9k: fix inconsistent indenting on return statement minor change, indenting is one tab out. Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/xmit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 82fc76f5b2a9..fe795fc5288c 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2915,7 +2915,7 @@ int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, if (skb_headroom(skb) < padsize) { ath_dbg(common, XMIT, "tx99 padding failed\n"); - return -EINVAL; + return -EINVAL; } skb_push(skb, padsize); -- cgit v1.2.3-59-g8ed1b From 899077791403ff7a2d8cfaa87bd1a82d729463e2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 8 Dec 2015 16:32:27 +0100 Subject: netcp: try to reduce type confusion in descriptors The netcp driver produces tons of warnings when CONFIG_LPAE is enabled on ARM: drivers/net/ethernet/ti/netcp_core.c: In function 'netcp_tx_map_skb': drivers/net/ethernet/ti/netcp_core.c:1084:13: warning: passing argument 1 of 'set_words' from incompatible pointer type [-Wincompatible-pointer-types] This is the result of trying to pass a pointer to a dma_addr_t to a function that expects a u32 pointer to copy that into a DMA descriptor. Looking at that code in more detail to fix the warnings, I see multiple related problems: * The conversion functions are not endian-safe, as the DMA descriptors are almost certainly fixed-endian, but the CPU is not. * On 64-bit machines, passing a pointer through a u32 variable is a bug, accessing an indirect pointer as a u32 pointer even more so. * The handling of epib and psdata mixes native-endian and device-endian data. In this patch, I try to sort out the types for most accesses here, adding le32_to_cpu/cpu_to_le32 where appropriate, and passing pointers through two 32-bit words in the descriptor padding, to make it plausible that the driver does the right thing if compiled for big-endian or 64-bit systems. Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/netcp_core.c | 123 ++++++++++++++++++++--------------- include/linux/soc/ti/knav_dma.h | 22 +++---- 2 files changed, 82 insertions(+), 63 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index e5e20e734f21..eb2585e777e1 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -109,69 +109,80 @@ module_param(netcp_debug_level, int, 0); MODULE_PARM_DESC(netcp_debug_level, "Netcp debug level (NETIF_MSG bits) (0=none,...,16=all)"); /* Helper functions - Get/Set */ -static void get_pkt_info(u32 *buff, u32 *buff_len, u32 *ndesc, +static void get_pkt_info(dma_addr_t *buff, u32 *buff_len, dma_addr_t *ndesc, struct knav_dma_desc *desc) { - *buff_len = desc->buff_len; - *buff = desc->buff; - *ndesc = desc->next_desc; + *buff_len = le32_to_cpu(desc->buff_len); + *buff = le32_to_cpu(desc->buff); + *ndesc = le32_to_cpu(desc->next_desc); } -static void get_pad_info(u32 *pad0, u32 *pad1, struct knav_dma_desc *desc) +static void get_pad_info(u32 *pad0, u32 *pad1, u32 *pad2, struct knav_dma_desc *desc) { - *pad0 = desc->pad[0]; - *pad1 = desc->pad[1]; + *pad0 = le32_to_cpu(desc->pad[0]); + *pad1 = le32_to_cpu(desc->pad[1]); + *pad2 = le32_to_cpu(desc->pad[2]); } -static void get_org_pkt_info(u32 *buff, u32 *buff_len, +static void get_pad_ptr(void **padptr, struct knav_dma_desc *desc) +{ + u64 pad64; + + pad64 = le32_to_cpu(desc->pad[0]) + + ((u64)le32_to_cpu(desc->pad[1]) << 32); + *padptr = (void *)(uintptr_t)pad64; +} + +static void get_org_pkt_info(dma_addr_t *buff, u32 *buff_len, struct knav_dma_desc *desc) { - *buff = desc->orig_buff; - *buff_len = desc->orig_len; + *buff = le32_to_cpu(desc->orig_buff); + *buff_len = le32_to_cpu(desc->orig_len); } -static void get_words(u32 *words, int num_words, u32 *desc) +static void get_words(dma_addr_t *words, int num_words, __le32 *desc) { int i; for (i = 0; i < num_words; i++) - words[i] = desc[i]; + words[i] = le32_to_cpu(desc[i]); } -static void set_pkt_info(u32 buff, u32 buff_len, u32 ndesc, +static void set_pkt_info(dma_addr_t buff, u32 buff_len, u32 ndesc, struct knav_dma_desc *desc) { - desc->buff_len = buff_len; - desc->buff = buff; - desc->next_desc = ndesc; + desc->buff_len = cpu_to_le32(buff_len); + desc->buff = cpu_to_le32(buff); + desc->next_desc = cpu_to_le32(ndesc); } static void set_desc_info(u32 desc_info, u32 pkt_info, struct knav_dma_desc *desc) { - desc->desc_info = desc_info; - desc->packet_info = pkt_info; + desc->desc_info = cpu_to_le32(desc_info); + desc->packet_info = cpu_to_le32(pkt_info); } -static void set_pad_info(u32 pad0, u32 pad1, struct knav_dma_desc *desc) +static void set_pad_info(u32 pad0, u32 pad1, u32 pad2, struct knav_dma_desc *desc) { - desc->pad[0] = pad0; - desc->pad[1] = pad1; + desc->pad[0] = cpu_to_le32(pad0); + desc->pad[1] = cpu_to_le32(pad1); + desc->pad[2] = cpu_to_le32(pad1); } -static void set_org_pkt_info(u32 buff, u32 buff_len, +static void set_org_pkt_info(dma_addr_t buff, u32 buff_len, struct knav_dma_desc *desc) { - desc->orig_buff = buff; - desc->orig_len = buff_len; + desc->orig_buff = cpu_to_le32(buff); + desc->orig_len = cpu_to_le32(buff_len); } -static void set_words(u32 *words, int num_words, u32 *desc) +static void set_words(u32 *words, int num_words, __le32 *desc) { int i; for (i = 0; i < num_words; i++) - desc[i] = words[i]; + desc[i] = cpu_to_le32(words[i]); } /* Read the e-fuse value as 32 bit values to be endian independent */ @@ -570,7 +581,7 @@ static void netcp_free_rx_desc_chain(struct netcp_intf *netcp, dma_addr_t dma_desc, dma_buf; unsigned int buf_len, dma_sz = sizeof(*ndesc); void *buf_ptr; - u32 tmp; + u32 pad[2]; get_words(&dma_desc, 1, &desc->next_desc); @@ -580,14 +591,15 @@ static void netcp_free_rx_desc_chain(struct netcp_intf *netcp, dev_err(netcp->ndev_dev, "failed to unmap Rx desc\n"); break; } - get_pkt_info(&dma_buf, &tmp, &dma_desc, ndesc); - get_pad_info((u32 *)&buf_ptr, &tmp, ndesc); + get_pad_ptr(&buf_ptr, ndesc); dma_unmap_page(netcp->dev, dma_buf, PAGE_SIZE, DMA_FROM_DEVICE); __free_page(buf_ptr); knav_pool_desc_put(netcp->rx_pool, desc); } - get_pad_info((u32 *)&buf_ptr, &buf_len, desc); + get_pad_info(&pad[0], &pad[1], &buf_len, desc); + buf_ptr = (void *)(uintptr_t)(pad[0] + ((u64)pad[1] << 32)); + if (buf_ptr) netcp_frag_free(buf_len <= PAGE_SIZE, buf_ptr); knav_pool_desc_put(netcp->rx_pool, desc); @@ -626,7 +638,6 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp) struct netcp_packet p_info; struct sk_buff *skb; void *org_buf_ptr; - u32 tmp; dma_desc = knav_queue_pop(netcp->rx_queue, &dma_sz); if (!dma_desc) @@ -639,7 +650,7 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp) } get_pkt_info(&dma_buff, &buf_len, &dma_desc, desc); - get_pad_info((u32 *)&org_buf_ptr, &org_buf_len, desc); + get_pad_ptr(&org_buf_ptr, desc); if (unlikely(!org_buf_ptr)) { dev_err(netcp->ndev_dev, "NULL bufptr in desc\n"); @@ -664,6 +675,7 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp) /* Fill in the page fragment list */ while (dma_desc) { struct page *page; + void *ptr; ndesc = knav_pool_desc_unmap(netcp->rx_pool, dma_desc, dma_sz); if (unlikely(!ndesc)) { @@ -672,14 +684,15 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp) } get_pkt_info(&dma_buff, &buf_len, &dma_desc, ndesc); - get_pad_info((u32 *)&page, &tmp, ndesc); + get_pad_ptr(ptr, ndesc); + page = ptr; if (likely(dma_buff && buf_len && page)) { dma_unmap_page(netcp->dev, dma_buff, PAGE_SIZE, DMA_FROM_DEVICE); } else { - dev_err(netcp->ndev_dev, "Bad Rx desc dma_buff(%p), len(%d), page(%p)\n", - (void *)dma_buff, buf_len, page); + dev_err(netcp->ndev_dev, "Bad Rx desc dma_buff(%pad), len(%d), page(%p)\n", + &dma_buff, buf_len, page); goto free_desc; } @@ -750,7 +763,6 @@ static void netcp_free_rx_buf(struct netcp_intf *netcp, int fdq) unsigned int buf_len, dma_sz; dma_addr_t dma; void *buf_ptr; - u32 tmp; /* Allocate descriptor */ while ((dma = knav_queue_pop(netcp->rx_fdq[fdq], &dma_sz))) { @@ -761,7 +773,7 @@ static void netcp_free_rx_buf(struct netcp_intf *netcp, int fdq) } get_org_pkt_info(&dma, &buf_len, desc); - get_pad_info((u32 *)&buf_ptr, &tmp, desc); + get_pad_ptr(buf_ptr, desc); if (unlikely(!dma)) { dev_err(netcp->ndev_dev, "NULL orig_buff in desc\n"); @@ -813,7 +825,7 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq) struct page *page; dma_addr_t dma; void *bufptr; - u32 pad[2]; + u32 pad[3]; /* Allocate descriptor */ hwdesc = knav_pool_desc_get(netcp->rx_pool); @@ -830,7 +842,7 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq) SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); bufptr = netdev_alloc_frag(primary_buf_len); - pad[1] = primary_buf_len; + pad[2] = primary_buf_len; if (unlikely(!bufptr)) { dev_warn_ratelimited(netcp->ndev_dev, @@ -842,7 +854,8 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq) if (unlikely(dma_mapping_error(netcp->dev, dma))) goto fail; - pad[0] = (u32)bufptr; + pad[0] = lower_32_bits((uintptr_t)bufptr); + pad[1] = upper_32_bits((uintptr_t)bufptr); } else { /* Allocate a secondary receive queue entry */ @@ -853,8 +866,9 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq) } buf_len = PAGE_SIZE; dma = dma_map_page(netcp->dev, page, 0, buf_len, DMA_TO_DEVICE); - pad[0] = (u32)page; - pad[1] = 0; + pad[0] = lower_32_bits(dma); + pad[1] = upper_32_bits(dma); + pad[2] = 0; } desc_info = KNAV_DMA_DESC_PS_INFO_IN_DESC; @@ -864,7 +878,7 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq) pkt_info |= (netcp->rx_queue_id & KNAV_DMA_DESC_RETQ_MASK) << KNAV_DMA_DESC_RETQ_SHIFT; set_org_pkt_info(dma, buf_len, hwdesc); - set_pad_info(pad[0], pad[1], hwdesc); + set_pad_info(pad[0], pad[1], pad[2], hwdesc); set_desc_info(desc_info, pkt_info, hwdesc); /* Push to FDQs */ @@ -935,8 +949,8 @@ static void netcp_free_tx_desc_chain(struct netcp_intf *netcp, dma_unmap_single(netcp->dev, dma_buf, buf_len, DMA_TO_DEVICE); else - dev_warn(netcp->ndev_dev, "bad Tx desc buf(%p), len(%d)\n", - (void *)dma_buf, buf_len); + dev_warn(netcp->ndev_dev, "bad Tx desc buf(%pad), len(%d)\n", + &dma_buf, buf_len); knav_pool_desc_put(netcp->tx_pool, ndesc); ndesc = NULL; @@ -953,11 +967,11 @@ static int netcp_process_tx_compl_packets(struct netcp_intf *netcp, unsigned int budget) { struct knav_dma_desc *desc; + void *ptr; struct sk_buff *skb; unsigned int dma_sz; dma_addr_t dma; int pkts = 0; - u32 tmp; while (budget--) { dma = knav_queue_pop(netcp->tx_compl_q, &dma_sz); @@ -970,7 +984,8 @@ static int netcp_process_tx_compl_packets(struct netcp_intf *netcp, continue; } - get_pad_info((u32 *)&skb, &tmp, desc); + get_pad_ptr(&ptr, desc); + skb = ptr; netcp_free_tx_desc_chain(netcp, desc, dma_sz); if (!skb) { dev_err(netcp->ndev_dev, "No skb in Tx desc\n"); @@ -1059,6 +1074,7 @@ netcp_tx_map_skb(struct sk_buff *skb, struct netcp_intf *netcp) u32 page_offset = frag->page_offset; u32 buf_len = skb_frag_size(frag); dma_addr_t desc_dma; + u32 desc_dma_32; u32 pkt_info; dma_addr = dma_map_page(dev, page, page_offset, buf_len, @@ -1075,13 +1091,13 @@ netcp_tx_map_skb(struct sk_buff *skb, struct netcp_intf *netcp) goto free_descs; } - desc_dma = knav_pool_desc_virt_to_dma(netcp->tx_pool, - (void *)ndesc); + desc_dma = knav_pool_desc_virt_to_dma(netcp->tx_pool, ndesc); pkt_info = (netcp->tx_compl_qid & KNAV_DMA_DESC_RETQ_MASK) << KNAV_DMA_DESC_RETQ_SHIFT; set_pkt_info(dma_addr, buf_len, 0, ndesc); - set_words(&desc_dma, 1, &pdesc->next_desc); + desc_dma_32 = (u32)desc_dma; + set_words(&desc_dma_32, 1, &pdesc->next_desc); pkt_len += buf_len; if (pdesc != desc) knav_pool_desc_map(netcp->tx_pool, pdesc, @@ -1173,11 +1189,14 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp, } set_words(&tmp, 1, &desc->packet_info); - set_words((u32 *)&skb, 1, &desc->pad[0]); + tmp = lower_32_bits((uintptr_t)&skb); + set_words(&tmp, 1, &desc->pad[0]); + tmp = upper_32_bits((uintptr_t)&skb); + set_words(&tmp, 1, &desc->pad[1]); if (tx_pipe->flags & SWITCH_TO_PORT_IN_TAGINFO) { tmp = tx_pipe->switch_to_port; - set_words((u32 *)&tmp, 1, &desc->tag_info); + set_words(&tmp, 1, &desc->tag_info); } /* submit packet descriptor */ diff --git a/include/linux/soc/ti/knav_dma.h b/include/linux/soc/ti/knav_dma.h index dad035c16d94..343c13ac4f71 100644 --- a/include/linux/soc/ti/knav_dma.h +++ b/include/linux/soc/ti/knav_dma.h @@ -144,17 +144,17 @@ struct knav_dma_cfg { * @psdata: Protocol specific */ struct knav_dma_desc { - u32 desc_info; - u32 tag_info; - u32 packet_info; - u32 buff_len; - u32 buff; - u32 next_desc; - u32 orig_len; - u32 orig_buff; - u32 epib[KNAV_DMA_NUM_EPIB_WORDS]; - u32 psdata[KNAV_DMA_NUM_PS_WORDS]; - u32 pad[4]; + __le32 desc_info; + __le32 tag_info; + __le32 packet_info; + __le32 buff_len; + __le32 buff; + __le32 next_desc; + __le32 orig_len; + __le32 orig_buff; + __le32 epib[KNAV_DMA_NUM_EPIB_WORDS]; + __le32 psdata[KNAV_DMA_NUM_PS_WORDS]; + __le32 pad[4]; } ____cacheline_aligned; #if IS_ENABLED(CONFIG_KEYSTONE_NAVIGATOR_DMA) -- cgit v1.2.3-59-g8ed1b From 9dd2d6c5c9755b160fe0111bcdad9491676feea8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 8 Dec 2015 16:32:59 +0100 Subject: netcp: add more __le32 annotations The handling of epib and psdata remains a bit unclear in the driver, as we access the same fields both as CPU-endian and through DMA from the device. Sparse warns about this: ti/netcp_core.c:1147:21: warning: incorrect type in assignment (different base types) ti/netcp_core.c:1147:21: expected unsigned int [usertype] *[assigned] epib ti/netcp_core.c:1147:21: got restricted __le32 * This uses __le32 types in a few places and uses __force where the code looks fishy. The previous patch should really have produced the correct behavior, but this second patch is needed to shut up the warnings about it. Ideally it would be slightly rewritten to not need those casts, but I don't dare do that without access to the hardware for proper testing. Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/netcp.h | 2 +- drivers/net/ethernet/ti/netcp_core.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ti/netcp.h b/drivers/net/ethernet/ti/netcp.h index bb1bb72121c0..17a26a429b71 100644 --- a/drivers/net/ethernet/ti/netcp.h +++ b/drivers/net/ethernet/ti/netcp.h @@ -113,7 +113,7 @@ struct netcp_intf { #define NETCP_PSDATA_LEN KNAV_DMA_NUM_PS_WORDS struct netcp_packet { struct sk_buff *skb; - u32 *epib; + __le32 *epib; u32 *psdata; unsigned int psdata_len; struct netcp_intf *netcp; diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index eb2585e777e1..92d08eb262c2 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1145,8 +1145,8 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp, p_info.ts_context = NULL; p_info.txtstamp_complete = NULL; p_info.epib = desc->epib; - p_info.psdata = desc->psdata; - memset(p_info.epib, 0, KNAV_DMA_NUM_EPIB_WORDS * sizeof(u32)); + p_info.psdata = (u32 __force *)desc->psdata; + memset(p_info.epib, 0, KNAV_DMA_NUM_EPIB_WORDS * sizeof(__le32)); /* Find out where to inject the packet for transmission */ list_for_each_entry(tx_hook, &netcp->txhook_list_head, list) { @@ -1170,11 +1170,12 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp, /* update descriptor */ if (p_info.psdata_len) { - u32 *psdata = p_info.psdata; + /* psdata points to both native-endian and device-endian data */ + __le32 *psdata = (void __force *)p_info.psdata; memmove(p_info.psdata, p_info.psdata + p_info.psdata_len, p_info.psdata_len); - set_words(psdata, p_info.psdata_len, psdata); + set_words(p_info.psdata, p_info.psdata_len, psdata); tmp |= (p_info.psdata_len & KNAV_DMA_DESC_PSLEN_MASK) << KNAV_DMA_DESC_PSLEN_SHIFT; } -- cgit v1.2.3-59-g8ed1b From 82a06429ae215b39320e925dfae72419865d6029 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 9 Dec 2015 13:33:51 +0300 Subject: mlxsw: spectrum: fix some error handling The "err = " assignment is missing here. Fixes: 0d65fc13042f ('mlxsw: spectrum: Implement LAG port join/leave') Signed-off-by: Dan Carpenter Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 3ec07b9a458d..322ed544348f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2091,7 +2091,7 @@ static int mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port, err = mlxsw_sp_lag_col_port_disable(mlxsw_sp_port, lag_id); if (err) return err; - mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id); + err = mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id); if (err) return err; -- cgit v1.2.3-59-g8ed1b From 515123e286b686d32256342318f20ff932ebac1b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 9 Dec 2015 13:41:29 +0300 Subject: mlxsw: core: remove an unneeded condition We already know "err" is zero so there is no need to check. Signed-off-by: Dan Carpenter Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c index 4dad146b41ae..913106d37bb0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c @@ -169,7 +169,7 @@ static ssize_t mlxsw_hwmon_pwm_store(struct device *dev, dev_err(mlxsw_hwmon->bus_info->dev, "Failed to write PWM\n"); return err; } - return err ? err : len; + return len; } enum mlxsw_hwmon_attr_type { -- cgit v1.2.3-59-g8ed1b From eb72f74f03125a3a80fb98f41e5aba55cad70c8c Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 9 Dec 2015 17:16:35 +0530 Subject: cxgb4: Handle clip return values Add a warn message when clip table overflows. If clip table isn't allocated, return from cxgb4_clip_release() to avoid panic. Disable offload if clip isn't enabled in the hardware. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c | 11 +++++++++++ drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 22 ++++++++++++++++------ drivers/net/ethernet/chelsio/cxgb4/t4_regs.h | 4 ++++ 3 files changed, 31 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c index c308429dd9c7..d288dcf6062f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c +++ b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c @@ -118,6 +118,11 @@ int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6) ret = clip6_get_mbox(dev, (const struct in6_addr *)lip); if (ret) { write_unlock_bh(&ctbl->lock); + dev_err(adap->pdev_dev, + "CLIP FW cmd failed with error %d, " + "Connections using %pI6c wont be " + "offloaded", + ret, ce->addr6.sin6_addr.s6_addr); return ret; } } else { @@ -127,6 +132,9 @@ int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6) } } else { write_unlock_bh(&ctbl->lock); + dev_info(adap->pdev_dev, "CLIP table overflow, " + "Connections using %pI6c wont be offloaded", + (void *)lip); return -ENOMEM; } write_unlock_bh(&ctbl->lock); @@ -146,6 +154,9 @@ void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6) int hash; int ret = -1; + if (!ctbl) + return; + hash = clip_addr_hash(ctbl, addr, v6); read_lock_bh(&ctbl->lock); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 0d147610a06f..edd706e739fb 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -4865,15 +4865,25 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } #if IS_ENABLED(CONFIG_IPV6) - adapter->clipt = t4_init_clip_tbl(adapter->clipt_start, - adapter->clipt_end); - if (!adapter->clipt) { - /* We tolerate a lack of clip_table, giving up - * some functionality + if ((CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) && + (!(t4_read_reg(adapter, LE_DB_CONFIG_A) & ASLIPCOMPEN_F))) { + /* CLIP functionality is not present in hardware, + * hence disable all offload features */ dev_warn(&pdev->dev, - "could not allocate Clip table, continuing\n"); + "CLIP not enabled in hardware, continuing\n"); adapter->params.offload = 0; + } else { + adapter->clipt = t4_init_clip_tbl(adapter->clipt_start, + adapter->clipt_end); + if (!adapter->clipt) { + /* We tolerate a lack of clip_table, giving up + * some functionality + */ + dev_warn(&pdev->dev, + "could not allocate Clip table, continuing\n"); + adapter->params.offload = 0; + } } #endif if (is_offload(adapter) && tid_init(&adapter->tids) < 0) { diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index fc3044c8ac1c..91b52a21a2e7 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -2802,6 +2802,10 @@ #define HASHEN_V(x) ((x) << HASHEN_S) #define HASHEN_F HASHEN_V(1U) +#define ASLIPCOMPEN_S 17 +#define ASLIPCOMPEN_V(x) ((x) << ASLIPCOMPEN_S) +#define ASLIPCOMPEN_F ASLIPCOMPEN_V(1U) + #define REQQPARERR_S 16 #define REQQPARERR_V(x) ((x) << REQQPARERR_S) #define REQQPARERR_F REQQPARERR_V(1U) -- cgit v1.2.3-59-g8ed1b From 90b74c013d1b9ed294447d2287fa4b4b21f0681d Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 9 Dec 2015 18:23:48 +0100 Subject: net: mvneta: Make the default queue related for each port Instead of using the same default queue for all the port. Move it in the port struct. It will allow have a different default queue for each port. Signed-off-by: Gregory CLEMENT Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 528c2544389b..7f807ce9738e 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -356,6 +356,7 @@ struct mvneta_port { struct mvneta_tx_queue *txqs; struct net_device *dev; struct notifier_block cpu_notifier; + int rxq_def; /* Core clock */ struct clk *clk; @@ -819,7 +820,7 @@ static void mvneta_port_up(struct mvneta_port *pp) mvreg_write(pp, MVNETA_TXQ_CMD, q_map); /* Enable all initialized RXQs. */ - mvreg_write(pp, MVNETA_RXQ_CMD, BIT(rxq_def)); + mvreg_write(pp, MVNETA_RXQ_CMD, BIT(pp->rxq_def)); } /* Stop the Ethernet port activity */ @@ -1067,7 +1068,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp) mvreg_write(pp, MVNETA_ACC_MODE, val); /* Update val of portCfg register accordingly with all RxQueue types */ - val = MVNETA_PORT_CONFIG_DEFL_VALUE(rxq_def); + val = MVNETA_PORT_CONFIG_DEFL_VALUE(pp->rxq_def); mvreg_write(pp, MVNETA_PORT_CONFIG, val); val = 0; @@ -2101,19 +2102,19 @@ static void mvneta_set_rx_mode(struct net_device *dev) if (dev->flags & IFF_PROMISC) { /* Accept all: Multicast + Unicast */ mvneta_rx_unicast_promisc_set(pp, 1); - mvneta_set_ucast_table(pp, rxq_def); - mvneta_set_special_mcast_table(pp, rxq_def); - mvneta_set_other_mcast_table(pp, rxq_def); + mvneta_set_ucast_table(pp, pp->rxq_def); + mvneta_set_special_mcast_table(pp, pp->rxq_def); + mvneta_set_other_mcast_table(pp, pp->rxq_def); } else { /* Accept single Unicast */ mvneta_rx_unicast_promisc_set(pp, 0); mvneta_set_ucast_table(pp, -1); - mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def); + mvneta_mac_addr_set(pp, dev->dev_addr, pp->rxq_def); if (dev->flags & IFF_ALLMULTI) { /* Accept all multicast */ - mvneta_set_special_mcast_table(pp, rxq_def); - mvneta_set_other_mcast_table(pp, rxq_def); + mvneta_set_special_mcast_table(pp, pp->rxq_def); + mvneta_set_other_mcast_table(pp, pp->rxq_def); } else { /* Accept only initialized multicast */ mvneta_set_special_mcast_table(pp, -1); @@ -2122,7 +2123,7 @@ static void mvneta_set_rx_mode(struct net_device *dev) if (!netdev_mc_empty(dev)) { netdev_for_each_mc_addr(ha, dev) { mvneta_mcast_addr_set(pp, ha->addr, - rxq_def); + pp->rxq_def); } } } @@ -2205,7 +2206,7 @@ static int mvneta_poll(struct napi_struct *napi, int budget) * RX packets */ cause_rx_tx |= port->cause_rx_tx; - rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]); + rx_done = mvneta_rx(pp, budget, &pp->rxqs[pp->rxq_def]); budget -= rx_done; if (budget > 0) { @@ -2418,17 +2419,17 @@ static void mvneta_cleanup_txqs(struct mvneta_port *pp) /* Cleanup all Rx queues */ static void mvneta_cleanup_rxqs(struct mvneta_port *pp) { - mvneta_rxq_deinit(pp, &pp->rxqs[rxq_def]); + mvneta_rxq_deinit(pp, &pp->rxqs[pp->rxq_def]); } /* Init all Rx queues */ static int mvneta_setup_rxqs(struct mvneta_port *pp) { - int err = mvneta_rxq_init(pp, &pp->rxqs[rxq_def]); + int err = mvneta_rxq_init(pp, &pp->rxqs[pp->rxq_def]); if (err) { netdev_err(pp->dev, "%s: can't create rxq=%d\n", - __func__, rxq_def); + __func__, pp->rxq_def); mvneta_cleanup_rxqs(pp); return err; } @@ -2634,7 +2635,7 @@ static int mvneta_set_mac_addr(struct net_device *dev, void *addr) mvneta_mac_addr_set(pp, dev->dev_addr, -1); /* Set new addr in hw */ - mvneta_mac_addr_set(pp, sockaddr->sa_data, rxq_def); + mvneta_mac_addr_set(pp, sockaddr->sa_data, pp->rxq_def); eth_commit_mac_addr_change(dev, addr); return 0; @@ -2753,7 +2754,7 @@ static void mvneta_percpu_elect(struct mvneta_port *pp) { int online_cpu_idx, cpu, i = 0; - online_cpu_idx = rxq_def % num_online_cpus(); + online_cpu_idx = pp->rxq_def % num_online_cpus(); for_each_online_cpu(cpu) { if (i == online_cpu_idx) @@ -3363,6 +3364,8 @@ static int mvneta_probe(struct platform_device *pdev) strcmp(managed, "in-band-status") == 0); pp->cpu_notifier.notifier_call = mvneta_percpu_notifier; + pp->rxq_def = rxq_def; + pp->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(pp->clk)) { err = PTR_ERR(pp->clk); -- cgit v1.2.3-59-g8ed1b From 2dcf75e2793c53349f0464a4ba0f6253b345668b Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 9 Dec 2015 18:23:49 +0100 Subject: net: mvneta: Associate RX queues with each CPU We enable the percpu interrupt for all the CPU and we just associate a CPU to a few queue at the neta level. The mapping between the CPUs and the queues is static. The queues are associated to the CPU module the number of CPUs. However currently we only use on RX queue for a given Ethernet port. Signed-off-by: Gregory CLEMENT Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 150 ++++++++++++++++++++++++++-------- 1 file changed, 115 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 7f807ce9738e..d6dfaa1173ae 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -110,9 +110,16 @@ #define MVNETA_CPU_MAP(cpu) (0x2540 + ((cpu) << 2)) #define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff #define MVNETA_CPU_TXQ_ACCESS_ALL_MASK 0x0000ff00 +#define MVNETA_CPU_RXQ_ACCESS(rxq) BIT(rxq) #define MVNETA_RXQ_TIME_COAL_REG(q) (0x2580 + ((q) << 2)) -/* Exception Interrupt Port/Queue Cause register */ +/* Exception Interrupt Port/Queue Cause register + * + * Their behavior depend of the mapping done using the PCPX2Q + * registers. For a given CPU if the bit associated to a queue is not + * set, then for the register a read from this CPU will always return + * 0 and a write won't do anything + */ #define MVNETA_INTR_NEW_CAUSE 0x25a0 #define MVNETA_INTR_NEW_MASK 0x25a4 @@ -820,7 +827,13 @@ static void mvneta_port_up(struct mvneta_port *pp) mvreg_write(pp, MVNETA_TXQ_CMD, q_map); /* Enable all initialized RXQs. */ - mvreg_write(pp, MVNETA_RXQ_CMD, BIT(pp->rxq_def)); + for (queue = 0; queue < rxq_number; queue++) { + struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; + + if (rxq->descs != NULL) + q_map |= (1 << queue); + } + mvreg_write(pp, MVNETA_RXQ_CMD, q_map); } /* Stop the Ethernet port activity */ @@ -1026,6 +1039,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp) int cpu; int queue; u32 val; + int max_cpu = num_present_cpus(); /* Clear all Cause registers */ mvreg_write(pp, MVNETA_INTR_NEW_CAUSE, 0); @@ -1041,13 +1055,23 @@ static void mvneta_defaults_set(struct mvneta_port *pp) /* Enable MBUS Retry bit16 */ mvreg_write(pp, MVNETA_MBUS_RETRY, 0x20); - /* Set CPU queue access map - all CPUs have access to all RX - * queues and to all TX queues + /* Set CPU queue access map. CPUs are assigned to the RX + * queues modulo their number and all the TX queues are + * assigned to the CPU associated to the default RX queue. */ - for_each_present_cpu(cpu) - mvreg_write(pp, MVNETA_CPU_MAP(cpu), - (MVNETA_CPU_RXQ_ACCESS_ALL_MASK | - MVNETA_CPU_TXQ_ACCESS_ALL_MASK)); + for_each_present_cpu(cpu) { + int rxq_map = 0, txq_map = 0; + int rxq; + + for (rxq = 0; rxq < rxq_number; rxq++) + if ((rxq % max_cpu) == cpu) + rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq); + + if (cpu == rxq_def) + txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK; + + mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map); + } /* Reset RX and TX DMAs */ mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET); @@ -2174,6 +2198,7 @@ static int mvneta_poll(struct napi_struct *napi, int budget) { int rx_done = 0; u32 cause_rx_tx; + int rx_queue; struct mvneta_port *pp = netdev_priv(napi->dev); struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports); @@ -2205,8 +2230,15 @@ static int mvneta_poll(struct napi_struct *napi, int budget) /* For the case where the last mvneta_poll did not process all * RX packets */ + rx_queue = fls(((cause_rx_tx >> 8) & 0xff)); + cause_rx_tx |= port->cause_rx_tx; - rx_done = mvneta_rx(pp, budget, &pp->rxqs[pp->rxq_def]); + + if (rx_queue) { + rx_queue = rx_queue - 1; + rx_done = mvneta_rx(pp, budget, &pp->rxqs[rx_queue]); + } + budget -= rx_done; if (budget > 0) { @@ -2419,19 +2451,27 @@ static void mvneta_cleanup_txqs(struct mvneta_port *pp) /* Cleanup all Rx queues */ static void mvneta_cleanup_rxqs(struct mvneta_port *pp) { - mvneta_rxq_deinit(pp, &pp->rxqs[pp->rxq_def]); + int queue; + + for (queue = 0; queue < txq_number; queue++) + mvneta_rxq_deinit(pp, &pp->rxqs[queue]); } /* Init all Rx queues */ static int mvneta_setup_rxqs(struct mvneta_port *pp) { - int err = mvneta_rxq_init(pp, &pp->rxqs[pp->rxq_def]); - if (err) { - netdev_err(pp->dev, "%s: can't create rxq=%d\n", - __func__, pp->rxq_def); - mvneta_cleanup_rxqs(pp); - return err; + int queue; + + for (queue = 0; queue < rxq_number; queue++) { + int err = mvneta_rxq_init(pp, &pp->rxqs[queue]); + + if (err) { + netdev_err(pp->dev, "%s: can't create rxq=%d\n", + __func__, queue); + mvneta_cleanup_rxqs(pp); + return err; + } } return 0; @@ -2455,6 +2495,19 @@ static int mvneta_setup_txqs(struct mvneta_port *pp) return 0; } +static void mvneta_percpu_unmask_interrupt(void *arg) +{ + struct mvneta_port *pp = arg; + + /* All the queue are unmasked, but actually only the ones + * maped to this CPU will be unmasked + */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, + MVNETA_RX_INTR_MASK_ALL | + MVNETA_TX_INTR_MASK_ALL | + MVNETA_MISCINTR_INTR_MASK); +} + static void mvneta_start_dev(struct mvneta_port *pp) { unsigned int cpu; @@ -2472,11 +2525,10 @@ static void mvneta_start_dev(struct mvneta_port *pp) napi_enable(&port->napi); } - /* Unmask interrupts */ - mvreg_write(pp, MVNETA_INTR_NEW_MASK, - MVNETA_RX_INTR_MASK(rxq_number) | - MVNETA_TX_INTR_MASK(txq_number) | - MVNETA_MISCINTR_INTR_MASK); + /* Unmask interrupts. It has to be done from each CPU */ + for_each_online_cpu(cpu) + smp_call_function_single(cpu, mvneta_percpu_unmask_interrupt, + pp, true); mvreg_write(pp, MVNETA_INTR_MISC_MASK, MVNETA_CAUSE_PHY_STATUS_CHANGE | MVNETA_CAUSE_LINK_CHANGE | @@ -2752,22 +2804,35 @@ static void mvneta_percpu_disable(void *arg) static void mvneta_percpu_elect(struct mvneta_port *pp) { - int online_cpu_idx, cpu, i = 0; + int online_cpu_idx, max_cpu, cpu, i = 0; online_cpu_idx = pp->rxq_def % num_online_cpus(); + max_cpu = num_present_cpus(); for_each_online_cpu(cpu) { - if (i == online_cpu_idx) - /* Enable per-CPU interrupt on the one CPU we - * just elected + int rxq_map = 0, txq_map = 0; + int rxq; + + for (rxq = 0; rxq < rxq_number; rxq++) + if ((rxq % max_cpu) == cpu) + rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq); + + if (i == online_cpu_idx) { + /* Map the default receive queue and transmit + * queue to the elected CPU */ - smp_call_function_single(cpu, mvneta_percpu_enable, - pp, true); - else - /* Disable per-CPU interrupt on all the other CPU */ - smp_call_function_single(cpu, mvneta_percpu_disable, - pp, true); + rxq_map |= MVNETA_CPU_RXQ_ACCESS(pp->rxq_def); + txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK; + } + mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map); + + /* Update the interrupt mask on each CPU according the + * new mapping + */ + smp_call_function_single(cpu, mvneta_percpu_unmask_interrupt, + pp, true); i++; + } }; @@ -2802,12 +2867,22 @@ static int mvneta_percpu_notifier(struct notifier_block *nfb, mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0); napi_enable(&port->napi); + + /* Enable per-CPU interrupts on the CPU that is + * brought up. + */ + smp_call_function_single(cpu, mvneta_percpu_enable, + pp, true); + /* Enable per-CPU interrupt on the one CPU we care * about. */ mvneta_percpu_elect(pp); - /* Unmask all ethernet port interrupts */ + /* Unmask all ethernet port interrupts, as this + * notifier is called for each CPU then the CPU to + * Queue mapping is applied + */ mvreg_write(pp, MVNETA_INTR_NEW_MASK, MVNETA_RX_INTR_MASK(rxq_number) | MVNETA_TX_INTR_MASK(txq_number) | @@ -2858,7 +2933,7 @@ static int mvneta_percpu_notifier(struct notifier_block *nfb, static int mvneta_open(struct net_device *dev) { struct mvneta_port *pp = netdev_priv(dev); - int ret; + int ret, cpu; pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu); pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) + @@ -2888,8 +2963,13 @@ static int mvneta_open(struct net_device *dev) */ mvneta_percpu_disable(pp); - /* Elect a CPU to handle our RX queue interrupt */ - mvneta_percpu_elect(pp); + /* Enable per-CPU interrupt on all the CPU to handle our RX + * queue interrupts + */ + for_each_online_cpu(cpu) + smp_call_function_single(cpu, mvneta_percpu_enable, + pp, true); + /* Register a CPU notifier to handle the case where our CPU * might be taken offline. -- cgit v1.2.3-59-g8ed1b From 9a401dead08c158d4f4a2fc2ade3cb42656d0ca5 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 9 Dec 2015 18:23:50 +0100 Subject: net: mvneta: Add naive RSS support This patch adds the support for the RSS related ethtool function. Currently it only uses one entry in the indirection table which allows associating an mvneta interface to a given CPU. Signed-off-by: Gregory CLEMENT Tested-by: Marcin Wojtas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 127 +++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index d6dfaa1173ae..6f423e7bf09c 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -261,6 +261,11 @@ #define MVNETA_TX_MTU_MAX 0x3ffff +/* The RSS lookup table actually has 256 entries but we do not use + * them yet + */ +#define MVNETA_RSS_LU_TABLE_SIZE 1 + /* TSO header size */ #define TSO_HEADER_SIZE 128 @@ -382,6 +387,8 @@ struct mvneta_port { unsigned int use_inband_status:1; u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)]; + + u32 indir[MVNETA_RSS_LU_TABLE_SIZE]; }; /* The mvneta_tx_desc and mvneta_rx_desc structures describe the @@ -1067,7 +1074,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp) if ((rxq % max_cpu) == cpu) rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq); - if (cpu == rxq_def) + if (cpu == pp->rxq_def) txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK; mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map); @@ -2508,6 +2515,18 @@ static void mvneta_percpu_unmask_interrupt(void *arg) MVNETA_MISCINTR_INTR_MASK); } +static void mvneta_percpu_mask_interrupt(void *arg) +{ + struct mvneta_port *pp = arg; + + /* All the queue are masked, but actually only the ones + * maped to this CPU will be masked + */ + mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0); + mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0); + mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0); +} + static void mvneta_start_dev(struct mvneta_port *pp) { unsigned int cpu; @@ -3231,6 +3250,106 @@ static int mvneta_ethtool_get_sset_count(struct net_device *dev, int sset) return -EOPNOTSUPP; } +static u32 mvneta_ethtool_get_rxfh_indir_size(struct net_device *dev) +{ + return MVNETA_RSS_LU_TABLE_SIZE; +} + +static int mvneta_ethtool_get_rxnfc(struct net_device *dev, + struct ethtool_rxnfc *info, + u32 *rules __always_unused) +{ + switch (info->cmd) { + case ETHTOOL_GRXRINGS: + info->data = rxq_number; + return 0; + case ETHTOOL_GRXFH: + return -EOPNOTSUPP; + default: + return -EOPNOTSUPP; + } +} + +static int mvneta_config_rss(struct mvneta_port *pp) +{ + int cpu; + u32 val; + + netif_tx_stop_all_queues(pp->dev); + + for_each_online_cpu(cpu) + smp_call_function_single(cpu, mvneta_percpu_mask_interrupt, + pp, true); + + /* We have to synchronise on the napi of each CPU */ + for_each_online_cpu(cpu) { + struct mvneta_pcpu_port *pcpu_port = + per_cpu_ptr(pp->ports, cpu); + + napi_synchronize(&pcpu_port->napi); + napi_disable(&pcpu_port->napi); + } + + pp->rxq_def = pp->indir[0]; + + /* Update unicast mapping */ + mvneta_set_rx_mode(pp->dev); + + /* Update val of portCfg register accordingly with all RxQueue types */ + val = MVNETA_PORT_CONFIG_DEFL_VALUE(pp->rxq_def); + mvreg_write(pp, MVNETA_PORT_CONFIG, val); + + /* Update the elected CPU matching the new rxq_def */ + mvneta_percpu_elect(pp); + + /* We have to synchronise on the napi of each CPU */ + for_each_online_cpu(cpu) { + struct mvneta_pcpu_port *pcpu_port = + per_cpu_ptr(pp->ports, cpu); + + napi_enable(&pcpu_port->napi); + } + + netif_tx_start_all_queues(pp->dev); + + return 0; +} + +static int mvneta_ethtool_set_rxfh(struct net_device *dev, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct mvneta_port *pp = netdev_priv(dev); + /* We require at least one supported parameter to be changed + * and no change in any of the unsupported parameters + */ + if (key || + (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)) + return -EOPNOTSUPP; + + if (!indir) + return 0; + + memcpy(pp->indir, indir, MVNETA_RSS_LU_TABLE_SIZE); + + return mvneta_config_rss(pp); +} + +static int mvneta_ethtool_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, + u8 *hfunc) +{ + struct mvneta_port *pp = netdev_priv(dev); + + if (hfunc) + *hfunc = ETH_RSS_HASH_TOP; + + if (!indir) + return 0; + + memcpy(indir, pp->indir, MVNETA_RSS_LU_TABLE_SIZE); + + return 0; +} + static const struct net_device_ops mvneta_netdev_ops = { .ndo_open = mvneta_open, .ndo_stop = mvneta_stop, @@ -3255,6 +3374,10 @@ const struct ethtool_ops mvneta_eth_tool_ops = { .get_strings = mvneta_ethtool_get_strings, .get_ethtool_stats = mvneta_ethtool_get_stats, .get_sset_count = mvneta_ethtool_get_sset_count, + .get_rxfh_indir_size = mvneta_ethtool_get_rxfh_indir_size, + .get_rxnfc = mvneta_ethtool_get_rxnfc, + .get_rxfh = mvneta_ethtool_get_rxfh, + .set_rxfh = mvneta_ethtool_set_rxfh, }; /* Initialize hw */ @@ -3446,6 +3569,8 @@ static int mvneta_probe(struct platform_device *pdev) pp->rxq_def = rxq_def; + pp->indir[0] = rxq_def; + pp->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(pp->clk)) { err = PTR_ERR(pp->clk); -- cgit v1.2.3-59-g8ed1b From 50bf8cb6fc9c264b49e0a0cad3f83e751591b6ec Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 9 Dec 2015 18:23:51 +0100 Subject: net: mvneta: Configure XPS support With this patch each CPU is associated with its own set of TX queues. It also setup the XPS with an initial configuration which set the affinity matching the hardware configuration. Suggested-by: Arnd Bergmann Signed-off-by: Gregory CLEMENT Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 56 ++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 6f423e7bf09c..15b1f6bbd92d 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -111,6 +111,7 @@ #define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff #define MVNETA_CPU_TXQ_ACCESS_ALL_MASK 0x0000ff00 #define MVNETA_CPU_RXQ_ACCESS(rxq) BIT(rxq) +#define MVNETA_CPU_TXQ_ACCESS(txq) BIT(txq + 8) #define MVNETA_RXQ_TIME_COAL_REG(q) (0x2580 + ((q) << 2)) /* Exception Interrupt Port/Queue Cause register @@ -514,6 +515,9 @@ struct mvneta_tx_queue { /* DMA address of TSO headers */ dma_addr_t tso_hdrs_phys; + + /* Affinity mask for CPUs*/ + cpumask_t affinity_mask; }; struct mvneta_rx_queue { @@ -1062,20 +1066,30 @@ static void mvneta_defaults_set(struct mvneta_port *pp) /* Enable MBUS Retry bit16 */ mvreg_write(pp, MVNETA_MBUS_RETRY, 0x20); - /* Set CPU queue access map. CPUs are assigned to the RX - * queues modulo their number and all the TX queues are - * assigned to the CPU associated to the default RX queue. + /* Set CPU queue access map. CPUs are assigned to the RX and + * TX queues modulo their number. If there is only one TX + * queue then it is assigned to the CPU associated to the + * default RX queue. */ for_each_present_cpu(cpu) { int rxq_map = 0, txq_map = 0; - int rxq; + int rxq, txq; for (rxq = 0; rxq < rxq_number; rxq++) if ((rxq % max_cpu) == cpu) rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq); - if (cpu == pp->rxq_def) - txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK; + for (txq = 0; txq < txq_number; txq++) + if ((txq % max_cpu) == cpu) + txq_map |= MVNETA_CPU_TXQ_ACCESS(txq); + + /* With only one TX queue we configure a special case + * which will allow to get all the irq on a single + * CPU + */ + if (txq_number == 1) + txq_map = (cpu == pp->rxq_def) ? + MVNETA_CPU_TXQ_ACCESS(1) : 0; mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map); } @@ -2362,6 +2376,8 @@ static void mvneta_rxq_deinit(struct mvneta_port *pp, static int mvneta_txq_init(struct mvneta_port *pp, struct mvneta_tx_queue *txq) { + int cpu; + txq->size = pp->tx_ring_size; /* A queue must always have room for at least one skb. @@ -2414,6 +2430,14 @@ static int mvneta_txq_init(struct mvneta_port *pp, } mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal); + /* Setup XPS mapping */ + if (txq_number > 1) + cpu = txq->id % num_present_cpus(); + else + cpu = pp->rxq_def % num_present_cpus(); + cpumask_set_cpu(cpu, &txq->affinity_mask); + netif_set_xps_queue(pp->dev, &txq->affinity_mask, txq->id); + return 0; } @@ -2836,13 +2860,23 @@ static void mvneta_percpu_elect(struct mvneta_port *pp) if ((rxq % max_cpu) == cpu) rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq); - if (i == online_cpu_idx) { - /* Map the default receive queue and transmit - * queue to the elected CPU + if (i == online_cpu_idx) + /* Map the default receive queue queue to the + * elected CPU */ rxq_map |= MVNETA_CPU_RXQ_ACCESS(pp->rxq_def); - txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK; - } + + /* We update the TX queue map only if we have one + * queue. In this case we associate the TX queue to + * the CPU bound to the default RX queue + */ + if (txq_number == 1) + txq_map = (i == online_cpu_idx) ? + MVNETA_CPU_TXQ_ACCESS(1) : 0; + else + txq_map = mvreg_read(pp, MVNETA_CPU_MAP(cpu)) & + MVNETA_CPU_TXQ_ACCESS_ALL_MASK; + mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map); /* Update the interrupt mask on each CPU according the -- cgit v1.2.3-59-g8ed1b From 40fb5f8a60f33133d36afde35a9ad865d35e4423 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Thu, 10 Dec 2015 13:25:19 +0530 Subject: net: thunderx: HW TSO support for pass-2 hardware This adds support for offloading TCP segmentation to HW in pass-2 revision of hardware. Both driver level SW TSO for pass1.x chips and HW TSO for pass-2 chip will co-exist. Modified SQ descriptor structures to reflect pass-2 hw implementation. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nic.h | 6 +++++ drivers/net/ethernet/cavium/thunder/nic_main.c | 11 +++----- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 15 +++++++++-- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 20 +++++++++++---- drivers/net/ethernet/cavium/thunder/q_struct.h | 30 ++++++++++++---------- 5 files changed, 53 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h index 39ca6744a4e6..688828865c48 100644 --- a/drivers/net/ethernet/cavium/thunder/nic.h +++ b/drivers/net/ethernet/cavium/thunder/nic.h @@ -265,6 +265,7 @@ struct nicvf { u8 tns_mode:1; u8 sqs_mode:1; u8 loopback_supported:1; + bool hw_tso; u16 mtu; struct queue_set *qs; #define MAX_SQS_PER_VF_SINGLE_NODE 5 @@ -489,6 +490,11 @@ static inline int nic_get_node_id(struct pci_dev *pdev) return ((addr >> NIC_NODE_ID_SHIFT) & NIC_NODE_ID_MASK); } +static inline bool pass1_silicon(struct pci_dev *pdev) +{ + return pdev->revision < 8; +} + int nicvf_set_real_num_queues(struct net_device *netdev, int tx_queues, int rx_queues); int nicvf_open(struct net_device *netdev); diff --git a/drivers/net/ethernet/cavium/thunder/nic_main.c b/drivers/net/ethernet/cavium/thunder/nic_main.c index 4b7fd63ae57c..9f80de4d5016 100644 --- a/drivers/net/ethernet/cavium/thunder/nic_main.c +++ b/drivers/net/ethernet/cavium/thunder/nic_main.c @@ -55,11 +55,6 @@ struct nicpf { bool irq_allocated[NIC_PF_MSIX_VECTORS]; }; -static inline bool pass1_silicon(struct nicpf *nic) -{ - return nic->pdev->revision < 8; -} - /* Supported devices */ static const struct pci_device_id nic_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_NIC_PF) }, @@ -123,7 +118,7 @@ static void nic_send_msg_to_vf(struct nicpf *nic, int vf, union nic_mbx *mbx) * when PF writes to MBOX(1), in next revisions when * PF writes to MBOX(0) */ - if (pass1_silicon(nic)) { + if (pass1_silicon(nic->pdev)) { /* see the comment for nic_reg_write()/nic_reg_read() * functions above */ @@ -400,7 +395,7 @@ static void nic_config_cpi(struct nicpf *nic, struct cpi_cfg_msg *cfg) padd = cpi % 8; /* 3 bits CS out of 6bits DSCP */ /* Leave RSS_SIZE as '0' to disable RSS */ - if (pass1_silicon(nic)) { + if (pass1_silicon(nic->pdev)) { nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi << 3), (vnic << 24) | (padd << 16) | (rssi_base + rssi)); @@ -470,7 +465,7 @@ static void nic_config_rss(struct nicpf *nic, struct rss_cfg_msg *cfg) } cpi_base = nic->cpi_base[cfg->vf_id]; - if (pass1_silicon(nic)) + if (pass1_silicon(nic->pdev)) idx_addr = NIC_PF_CPI_0_2047_CFG; else idx_addr = NIC_PF_MPI_0_2047_CFG; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index dde8dc720cd3..c24cb2a86a42 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -525,14 +525,22 @@ static void nicvf_snd_pkt_handler(struct net_device *netdev, __func__, cqe_tx->sq_qs, cqe_tx->sq_idx, cqe_tx->sqe_ptr, hdr->subdesc_cnt); - nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1); nicvf_check_cqe_tx_errs(nic, cq, cqe_tx); skb = (struct sk_buff *)sq->skbuff[cqe_tx->sqe_ptr]; - /* For TSO offloaded packets only one head SKB needs to be freed */ + /* For TSO offloaded packets only one SQE will have a valid SKB */ if (skb) { + nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1); prefetch(skb); dev_consume_skb_any(skb); sq->skbuff[cqe_tx->sqe_ptr] = (u64)NULL; + } else { + /* In case of HW TSO, HW sends a CQE for each segment of a TSO + * packet instead of a single CQE for the whole TSO packet + * transmitted. Each of this CQE points to the same SQE, so + * avoid freeing same SQE multiple times. + */ + if (!nic->hw_tso) + nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1); } } @@ -1549,6 +1557,9 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; + if (!pass1_silicon(nic->pdev)) + nic->hw_tso = true; + netdev->netdev_ops = &nicvf_netdev_ops; netdev->watchdog_timeo = NICVF_TX_TIMEOUT; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 1fbd9084333e..b11fc094f769 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -925,7 +925,7 @@ static int nicvf_sq_subdesc_required(struct nicvf *nic, struct sk_buff *skb) { int subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT; - if (skb_shinfo(skb)->gso_size) { + if (skb_shinfo(skb)->gso_size && !nic->hw_tso) { subdesc_cnt = nicvf_tso_count_subdescs(skb); return subdesc_cnt; } @@ -940,7 +940,7 @@ static int nicvf_sq_subdesc_required(struct nicvf *nic, struct sk_buff *skb) * First subdescriptor for every send descriptor. */ static inline void -nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry, +nicvf_sq_add_hdr_subdesc(struct nicvf *nic, struct snd_queue *sq, int qentry, int subdesc_cnt, struct sk_buff *skb, int len) { int proto; @@ -976,6 +976,15 @@ nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry, break; } } + + if (nic->hw_tso && skb_shinfo(skb)->gso_size) { + hdr->tso = 1; + hdr->tso_start = skb_transport_offset(skb) + tcp_hdrlen(skb); + hdr->tso_max_paysize = skb_shinfo(skb)->gso_size; + /* For non-tunneled pkts, point this to L2 ethertype */ + hdr->inner_l3_offset = skb_network_offset(skb) - 2; + nic->drv_stats.tx_tso++; + } } /* SQ GATHER subdescriptor @@ -1045,7 +1054,7 @@ static int nicvf_sq_append_tso(struct nicvf *nic, struct snd_queue *sq, data_left -= size; tso_build_data(skb, &tso, size); } - nicvf_sq_add_hdr_subdesc(sq, hdr_qentry, + nicvf_sq_add_hdr_subdesc(nic, sq, hdr_qentry, seg_subdescs - 1, skb, seg_len); sq->skbuff[hdr_qentry] = (u64)NULL; qentry = nicvf_get_nxt_sqentry(sq, qentry); @@ -1098,11 +1107,12 @@ int nicvf_sq_append_skb(struct nicvf *nic, struct sk_buff *skb) qentry = nicvf_get_sq_desc(sq, subdesc_cnt); /* Check if its a TSO packet */ - if (skb_shinfo(skb)->gso_size) + if (skb_shinfo(skb)->gso_size && !nic->hw_tso) return nicvf_sq_append_tso(nic, sq, sq_num, qentry, skb); /* Add SQ header subdesc */ - nicvf_sq_add_hdr_subdesc(sq, qentry, subdesc_cnt - 1, skb, skb->len); + nicvf_sq_add_hdr_subdesc(nic, sq, qentry, subdesc_cnt - 1, + skb, skb->len); /* Add SQ gather subdescs */ qentry = nicvf_get_nxt_sqentry(sq, qentry); diff --git a/drivers/net/ethernet/cavium/thunder/q_struct.h b/drivers/net/ethernet/cavium/thunder/q_struct.h index 3c1de97b1add..9e6d9876bfd0 100644 --- a/drivers/net/ethernet/cavium/thunder/q_struct.h +++ b/drivers/net/ethernet/cavium/thunder/q_struct.h @@ -545,25 +545,28 @@ struct sq_hdr_subdesc { u64 subdesc_cnt:8; u64 csum_l4:2; u64 csum_l3:1; - u64 rsvd0:5; + u64 csum_inner_l4:2; + u64 csum_inner_l3:1; + u64 rsvd0:2; u64 l4_offset:8; u64 l3_offset:8; u64 rsvd1:4; u64 tot_len:20; /* W0 */ - u64 tso_sdc_cont:8; - u64 tso_sdc_first:8; - u64 tso_l4_offset:8; - u64 tso_flags_last:12; - u64 tso_flags_first:12; - u64 rsvd2:2; + u64 rsvd2:24; + u64 inner_l4_offset:8; + u64 inner_l3_offset:8; + u64 tso_start:8; + u64 rsvd3:2; u64 tso_max_paysize:14; /* W1 */ #elif defined(__LITTLE_ENDIAN_BITFIELD) u64 tot_len:20; u64 rsvd1:4; u64 l3_offset:8; u64 l4_offset:8; - u64 rsvd0:5; + u64 rsvd0:2; + u64 csum_inner_l3:1; + u64 csum_inner_l4:2; u64 csum_l3:1; u64 csum_l4:2; u64 subdesc_cnt:8; @@ -574,12 +577,11 @@ struct sq_hdr_subdesc { u64 subdesc_type:4; /* W0 */ u64 tso_max_paysize:14; - u64 rsvd2:2; - u64 tso_flags_first:12; - u64 tso_flags_last:12; - u64 tso_l4_offset:8; - u64 tso_sdc_first:8; - u64 tso_sdc_cont:8; /* W1 */ + u64 rsvd3:2; + u64 tso_start:8; + u64 inner_l3_offset:8; + u64 inner_l4_offset:8; + u64 rsvd2:24; /* W1 */ #endif }; -- cgit v1.2.3-59-g8ed1b From b9687b48a63a12ea31442f64dc77d41e83d0e478 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Thu, 10 Dec 2015 13:25:20 +0530 Subject: net: thunderx: Enable CQE count threshold interrupt This feature is introduced in pass-2 chip and with this CQ interrupt coalescing will work based on both timer and count. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 2 +- drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index b11fc094f769..d0d1b5490061 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -299,7 +299,7 @@ static int nicvf_init_cmp_queue(struct nicvf *nic, return err; cq->desc = cq->dmem.base; - cq->thresh = CMP_QUEUE_CQE_THRESH; + cq->thresh = pass1_silicon(nic->pdev) ? 0 : CMP_QUEUE_CQE_THRESH; nic->cq_coalesce_usecs = (CMP_QUEUE_TIMER_THRESH * 0.05) - 1; return 0; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index a4f6667fdbd4..c5030a7f213a 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -75,7 +75,7 @@ */ #define CMP_QSIZE CMP_QUEUE_SIZE2 #define CMP_QUEUE_LEN (1ULL << (CMP_QSIZE + 10)) -#define CMP_QUEUE_CQE_THRESH 0 +#define CMP_QUEUE_CQE_THRESH (NAPI_POLL_WEIGHT / 2) #define CMP_QUEUE_TIMER_THRESH 80 /* ~2usec */ #define RBDR_SIZE RBDR_SIZE0 -- cgit v1.2.3-59-g8ed1b From 9bd0a185c2c48226a50e6e5b639edaa9e8ab6c51 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 10 Dec 2015 17:12:37 +0200 Subject: net/mlx5: Fix query E-Switch capabilities E-Switch capabilities should be queried only if E-Switch flow table is supported and not only when vport group manager. Fixes: d6666753c6e8 ("net/mlx5: E-Switch, Introduce HCA cap and E-Switch vport context") Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index 1c9f9a54a873..aa1ab4702385 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -173,7 +173,7 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev) return err; } - if (MLX5_CAP_GEN(dev, vport_group_manager)) { + if (MLX5_CAP_GEN(dev, eswitch_flow_table)) { err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH, HCA_CAP_OPMOD_GET_CUR); if (err) -- cgit v1.2.3-59-g8ed1b From 108805fc196e001053e9e7c76f259977804dc0d6 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 10 Dec 2015 17:12:38 +0200 Subject: net/mlx5e: Assign random MAC address if needed Under SRIOV there might be a case where VFs are loaded without pre-assigned MAC address. In this case, the VF will randomize its own MAC. This will address the case of administrator not assigning MAC to the VF through the PF OS APIs and keep udev happy. Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 5 +++++ drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h | 3 +++ 2 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index d67058afe87e..a20be56df553 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2103,6 +2103,11 @@ static void mlx5e_set_netdev_dev_addr(struct net_device *netdev) struct mlx5e_priv *priv = netdev_priv(netdev); mlx5_query_nic_vport_mac_address(priv->mdev, 0, netdev->dev_addr); + if (is_zero_ether_addr(netdev->dev_addr) && + !MLX5_CAP_GEN(priv->mdev, vport_group_manager)) { + eth_hw_addr_random(netdev); + mlx5_core_info(priv->mdev, "Assigned random MAC address %pM\n", netdev->dev_addr); + } } static void mlx5e_build_netdev(struct net_device *netdev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index bee7da822dfe..ea6a137fd76c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -65,6 +65,9 @@ do { \ (__dev)->priv.name, __func__, __LINE__, current->pid, \ ##__VA_ARGS__) +#define mlx5_core_info(__dev, format, ...) \ + dev_info(&(__dev)->pdev->dev, format, ##__VA_ARGS__) + enum { MLX5_CMD_DATA, /* print command payload only */ MLX5_CMD_TIME, /* print command execution time */ -- cgit v1.2.3-59-g8ed1b From 26a8145390b36cbe97a5bd0b9e97249f21af6aea Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 10 Dec 2015 17:12:39 +0200 Subject: net/mlx5_core: Introduce flow steering firmware commands Introduce new Flow Steering (FS) firmware commands, in-order to support the new flow steering infrastructure. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 +- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 239 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h | 65 ++++++ drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 81 ++++++++ include/linux/mlx5/fs.h | 47 +++++ include/linux/mlx5/mlx5_ifc.h | 32 ++- 6 files changed, 455 insertions(+), 11 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/fs_core.h create mode 100644 include/linux/mlx5/fs.h (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index a0755919ccaf..be10592e0518 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_MLX5_CORE) += mlx5_core.o mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \ - mad.o transobj.o vport.o sriov.o + mad.o transobj.o vport.o sriov.o fs_cmd.o mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o flow_table.o eswitch.o \ en_main.o en_flow_table.o en_ethtool.o en_tx.o en_rx.o \ en_txrx.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c new file mode 100644 index 000000000000..5096f4f336bd --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2015, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include "fs_core.h" +#include "fs_cmd.h" +#include "mlx5_core.h" + +int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, + enum fs_flow_table_type type, unsigned int level, + unsigned int log_size, unsigned int *table_id) +{ + u32 out[MLX5_ST_SZ_DW(create_flow_table_out)]; + u32 in[MLX5_ST_SZ_DW(create_flow_table_in)]; + int err; + + memset(in, 0, sizeof(in)); + + MLX5_SET(create_flow_table_in, in, opcode, + MLX5_CMD_OP_CREATE_FLOW_TABLE); + + MLX5_SET(create_flow_table_in, in, table_type, type); + MLX5_SET(create_flow_table_in, in, level, level); + MLX5_SET(create_flow_table_in, in, log_size, log_size); + + memset(out, 0, sizeof(out)); + err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, + sizeof(out)); + + if (!err) + *table_id = MLX5_GET(create_flow_table_out, out, + table_id); + return err; +} + +int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft) +{ + u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)]; + u32 out[MLX5_ST_SZ_DW(destroy_flow_table_out)]; + + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + + MLX5_SET(destroy_flow_table_in, in, opcode, + MLX5_CMD_OP_DESTROY_FLOW_TABLE); + MLX5_SET(destroy_flow_table_in, in, table_type, ft->type); + MLX5_SET(destroy_flow_table_in, in, table_id, ft->id); + + return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, + sizeof(out)); +} + +int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + u32 *in, + unsigned int *group_id) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + u32 out[MLX5_ST_SZ_DW(create_flow_group_out)]; + int err; + + memset(out, 0, sizeof(out)); + + MLX5_SET(create_flow_group_in, in, opcode, + MLX5_CMD_OP_CREATE_FLOW_GROUP); + MLX5_SET(create_flow_group_in, in, table_type, ft->type); + MLX5_SET(create_flow_group_in, in, table_id, ft->id); + + err = mlx5_cmd_exec_check_status(dev, in, + inlen, out, + sizeof(out)); + if (!err) + *group_id = MLX5_GET(create_flow_group_out, out, + group_id); + + return err; +} + +int mlx5_cmd_destroy_flow_group(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + unsigned int group_id) +{ + u32 out[MLX5_ST_SZ_DW(destroy_flow_group_out)]; + u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)]; + + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + + MLX5_SET(destroy_flow_group_in, in, opcode, + MLX5_CMD_OP_DESTROY_FLOW_GROUP); + MLX5_SET(destroy_flow_group_in, in, table_type, ft->type); + MLX5_SET(destroy_flow_group_in, in, table_id, ft->id); + MLX5_SET(destroy_flow_group_in, in, group_id, group_id); + + return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, + sizeof(out)); +} + +static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev, + int opmod, int modify_mask, + struct mlx5_flow_table *ft, + unsigned group_id, + struct fs_fte *fte) +{ + unsigned int inlen = MLX5_ST_SZ_BYTES(set_fte_in) + + fte->dests_size * MLX5_ST_SZ_BYTES(dest_format_struct); + u32 out[MLX5_ST_SZ_DW(set_fte_out)]; + struct mlx5_flow_rule *dst; + void *in_flow_context; + void *in_match_value; + void *in_dests; + u32 *in; + int err; + + in = mlx5_vzalloc(inlen); + if (!in) { + mlx5_core_warn(dev, "failed to allocate inbox\n"); + return -ENOMEM; + } + + MLX5_SET(set_fte_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY); + MLX5_SET(set_fte_in, in, op_mod, opmod); + MLX5_SET(set_fte_in, in, modify_enable_mask, modify_mask); + MLX5_SET(set_fte_in, in, table_type, ft->type); + MLX5_SET(set_fte_in, in, table_id, ft->id); + MLX5_SET(set_fte_in, in, flow_index, fte->index); + + in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context); + MLX5_SET(flow_context, in_flow_context, group_id, group_id); + MLX5_SET(flow_context, in_flow_context, flow_tag, fte->flow_tag); + MLX5_SET(flow_context, in_flow_context, action, fte->action); + MLX5_SET(flow_context, in_flow_context, destination_list_size, + fte->dests_size); + in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context, + match_value); + memcpy(in_match_value, &fte->val, MLX5_ST_SZ_BYTES(fte_match_param)); + + in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination); + list_for_each_entry(dst, &fte->node.children, node.list) { + unsigned int id; + + MLX5_SET(dest_format_struct, in_dests, destination_type, + dst->dest_attr.type); + if (dst->dest_attr.type == + MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE) + id = dst->dest_attr.ft->id; + else + id = dst->dest_attr.tir_num; + MLX5_SET(dest_format_struct, in_dests, destination_id, id); + in_dests += MLX5_ST_SZ_BYTES(dest_format_struct); + } + memset(out, 0, sizeof(out)); + err = mlx5_cmd_exec_check_status(dev, in, inlen, out, + sizeof(out)); + kvfree(in); + + return err; +} + +int mlx5_cmd_create_fte(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + unsigned group_id, + struct fs_fte *fte) +{ + return mlx5_cmd_set_fte(dev, 0, 0, ft, group_id, fte); +} + +int mlx5_cmd_update_fte(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + unsigned group_id, + struct fs_fte *fte) +{ + int opmod; + int modify_mask; + int atomic_mod_cap = MLX5_CAP_FLOWTABLE(dev, + flow_table_properties_nic_receive. + flow_modify_en); + if (!atomic_mod_cap) + return -ENOTSUPP; + opmod = 1; + modify_mask = 1 << + MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST; + + return mlx5_cmd_set_fte(dev, opmod, modify_mask, ft, group_id, fte); +} + +int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + unsigned int index) +{ + u32 out[MLX5_ST_SZ_DW(delete_fte_out)]; + u32 in[MLX5_ST_SZ_DW(delete_fte_in)]; + int err; + + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + + MLX5_SET(delete_fte_in, in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY); + MLX5_SET(delete_fte_in, in, table_type, ft->type); + MLX5_SET(delete_fte_in, in, table_id, ft->id); + MLX5_SET(delete_fte_in, in, flow_index, index); + + err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, sizeof(out)); + + return err; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h new file mode 100644 index 000000000000..f39304ede186 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MLX5_FS_CMD_ +#define _MLX5_FS_CMD_ + +int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, + enum fs_flow_table_type type, unsigned int level, + unsigned int log_size, unsigned int *table_id); + +int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft); + +int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + u32 *in, unsigned int *group_id); + +int mlx5_cmd_destroy_flow_group(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + unsigned int group_id); + +int mlx5_cmd_create_fte(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + unsigned group_id, + struct fs_fte *fte); + +int mlx5_cmd_update_fte(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + unsigned group_id, + struct fs_fte *fte); + +int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + unsigned int index); + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h new file mode 100644 index 000000000000..e8b34a9b147b --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MLX5_FS_CORE_ +#define _MLX5_FS_CORE_ + +#include + +enum fs_node_type { + FS_TYPE_NAMESPACE, + FS_TYPE_PRIO, + FS_TYPE_FLOW_TABLE, + FS_TYPE_FLOW_GROUP, + FS_TYPE_FLOW_ENTRY, + FS_TYPE_FLOW_DEST +}; + +enum fs_flow_table_type { + FS_FT_NIC_RX = 0x0, +}; + +enum fs_fte_status { + FS_FTE_STATUS_EXISTING = 1UL << 0, +}; + +struct fs_node { + struct list_head list; + struct list_head children; + enum fs_node_type type; +}; + +struct mlx5_flow_rule { + struct fs_node node; + struct mlx5_flow_destination dest_attr; +}; + +struct mlx5_flow_table { + struct fs_node node; + u32 id; + enum fs_flow_table_type type; +}; + +struct fs_fte { + struct fs_node node; + u32 val[MLX5_ST_SZ_DW(fte_match_param)]; + u32 dests_size; + u32 flow_tag; + u32 index; + u32 action; +}; + +#endif diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h new file mode 100644 index 000000000000..34fd8dc0b3e1 --- /dev/null +++ b/include/linux/mlx5/fs.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _MLX5_FS_ +#define _MLX5_FS_ + +#include + +struct mlx5_flow_table; + +struct mlx5_flow_destination { + enum mlx5_flow_destination_type type; + union { + u32 tir_num; + struct mlx5_flow_table *ft; + }; +}; +#endif diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index f5d94495758a..131a2737cfa3 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -256,25 +256,27 @@ struct mlx5_ifc_flow_table_fields_supported_bits { struct mlx5_ifc_flow_table_prop_layout_bits { u8 ft_support[0x1]; - u8 reserved_0[0x1f]; + u8 reserved_0[0x2]; + u8 flow_modify_en[0x1]; + u8 reserved_1[0x1c]; - u8 reserved_1[0x2]; + u8 reserved_2[0x2]; u8 log_max_ft_size[0x6]; - u8 reserved_2[0x10]; + u8 reserved_3[0x10]; u8 max_ft_level[0x8]; - u8 reserved_3[0x20]; + u8 reserved_4[0x20]; - u8 reserved_4[0x18]; + u8 reserved_5[0x18]; u8 log_max_ft_num[0x8]; - u8 reserved_5[0x18]; + u8 reserved_6[0x18]; u8 log_max_destination[0x8]; - u8 reserved_6[0x18]; + u8 reserved_7[0x18]; u8 log_max_flow[0x8]; - u8 reserved_7[0x40]; + u8 reserved_8[0x40]; struct mlx5_ifc_flow_table_fields_supported_bits ft_field_support; @@ -2843,6 +2845,13 @@ struct mlx5_ifc_set_hca_cap_in_bits { union mlx5_ifc_hca_cap_union_bits capability; }; +enum { + MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION = 0x0, + MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_TAG = 0x1, + MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST = 0x2, + MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS = 0x3 +}; + struct mlx5_ifc_set_fte_out_bits { u8 status[0x8]; u8 reserved_0[0x18]; @@ -2867,11 +2876,14 @@ struct mlx5_ifc_set_fte_in_bits { u8 reserved_4[0x8]; u8 table_id[0x18]; - u8 reserved_5[0x40]; + u8 reserved_5[0x18]; + u8 modify_enable_mask[0x8]; + + u8 reserved_6[0x20]; u8 flow_index[0x20]; - u8 reserved_6[0xe0]; + u8 reserved_7[0xe0]; struct mlx5_ifc_flow_context_bits flow_context; }; -- cgit v1.2.3-59-g8ed1b From de8575e014685034728396f9d69169428be9cc3d Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 10 Dec 2015 17:12:40 +0200 Subject: net/mlx5_core: Add flow steering base data structures Introducing the base data structure and its operations that are going to represent ConnectX-4 Flow Steering, this data structure is basically a tree and all Flow steering objects such as (Flow Table/Flow Group/FTE/etc ..) are represented as fs_node(s). fs_node is the base object which describes a basic tree node, with the following extra info: type: describes the runtime type of the node (Object). lock: lock this node sub-tree. ref_count: number of children + current references. remove_func: a generic destructor. fs_node types will be used and explained once the usage is added in the following patches. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 +- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 116 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 6 ++ 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/fs_core.c (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index be10592e0518..7fc5e2388dec 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_MLX5_CORE) += mlx5_core.o mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \ - mad.o transobj.o vport.o sriov.o fs_cmd.o + mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o flow_table.o eswitch.o \ en_main.o en_flow_table.o en_ethtool.o en_tx.o en_rx.o \ en_txrx.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c new file mode 100644 index 000000000000..3c54d7b5a725 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "mlx5_core.h" +#include "fs_core.h" + +static void tree_init_node(struct fs_node *node, + unsigned int refcount, + void (*remove_func)(struct fs_node *)) +{ + atomic_set(&node->refcount, refcount); + INIT_LIST_HEAD(&node->list); + INIT_LIST_HEAD(&node->children); + mutex_init(&node->lock); + node->remove_func = remove_func; +} + +static void tree_add_node(struct fs_node *node, struct fs_node *parent) +{ + if (parent) + atomic_inc(&parent->refcount); + node->parent = parent; + + /* Parent is the root */ + if (!parent) + node->root = node; + else + node->root = parent->root; +} + +static void tree_get_node(struct fs_node *node) +{ + atomic_inc(&node->refcount); +} + +static void nested_lock_ref_node(struct fs_node *node) +{ + if (node) { + mutex_lock_nested(&node->lock, SINGLE_DEPTH_NESTING); + atomic_inc(&node->refcount); + } +} + +static void lock_ref_node(struct fs_node *node) +{ + if (node) { + mutex_lock(&node->lock); + atomic_inc(&node->refcount); + } +} + +static void unlock_ref_node(struct fs_node *node) +{ + if (node) { + atomic_dec(&node->refcount); + mutex_unlock(&node->lock); + } +} + +static void tree_put_node(struct fs_node *node) +{ + struct fs_node *parent_node = node->parent; + + lock_ref_node(parent_node); + if (atomic_dec_and_test(&node->refcount)) { + if (parent_node) + list_del_init(&node->list); + if (node->remove_func) + node->remove_func(node); + kfree(node); + node = NULL; + } + unlock_ref_node(parent_node); + if (!node && parent_node) + tree_put_node(parent_node); +} + +static int tree_remove_node(struct fs_node *node) +{ + if (atomic_read(&node->refcount) > 1) + return -EPERM; + tree_put_node(node); + return 0; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index e8b34a9b147b..ae03ae497cbf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -56,6 +56,12 @@ struct fs_node { struct list_head list; struct list_head children; enum fs_node_type type; + struct fs_node *parent; + struct fs_node *root; + /* lock the node for writing and traversing */ + struct mutex lock; + atomic_t refcount; + void (*remove_func)(struct fs_node *); }; struct mlx5_flow_rule { -- cgit v1.2.3-59-g8ed1b From 5e1626c09c615005e1edf9f971c20a35dc74efaa Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 10 Dec 2015 17:12:41 +0200 Subject: net/mlx5_core: Add flow steering lookup algorithms Introduce the flow steering mlx5_flow_namespace (Namespace) and fs_prio (Flow Steering Priority) tree nodes. Namespaces are used in order to isolate different usages or types of steering (for example, downstream patches will add a different namespaces for the NIC driver and for E-Switch FDB usages). Flow Steering Priorities are objects that describes priorities ranges between different flow objects under the same namespace. Example, entries in priority i are matched before entries in priority i+1. This patch adds the following algorithms: 1) Calculate level: Each flow table has level(the priority between the flow tables). When we initialize the flow steering tree, we assign range of levels to each priority, therefore the level for new flow table is the location within the priority related to the range of the priority. 2) Match between match criteria. This function is used for searching flow group when new flow rule is added. 3) Match between match values. This function is used for searching flow table entry when new flow rule is added. 4) Add essential macros for traversing on a node's children. E.g. traversing on all the flow table of some priority Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 93 +++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 41 ++++++++++ 2 files changed, 134 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 3c54d7b5a725..cac0d156a3d1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -114,3 +114,96 @@ static int tree_remove_node(struct fs_node *node) tree_put_node(node); return 0; } + +static struct fs_prio *find_prio(struct mlx5_flow_namespace *ns, + unsigned int prio) +{ + struct fs_prio *iter_prio; + + fs_for_each_prio(iter_prio, ns) { + if (iter_prio->prio == prio) + return iter_prio; + } + + return NULL; +} + +static unsigned int find_next_free_level(struct fs_prio *prio) +{ + if (!list_empty(&prio->node.children)) { + struct mlx5_flow_table *ft; + + ft = list_last_entry(&prio->node.children, + struct mlx5_flow_table, + node.list); + return ft->level + 1; + } + return prio->start_level; +} + +static bool masked_memcmp(void *mask, void *val1, void *val2, size_t size) +{ + unsigned int i; + + for (i = 0; i < size; i++, mask++, val1++, val2++) + if ((*((u8 *)val1) & (*(u8 *)mask)) != + ((*(u8 *)val2) & (*(u8 *)mask))) + return false; + + return true; +} + +static bool compare_match_value(struct mlx5_flow_group_mask *mask, + void *fte_param1, void *fte_param2) +{ + if (mask->match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) { + void *fte_match1 = MLX5_ADDR_OF(fte_match_param, + fte_param1, outer_headers); + void *fte_match2 = MLX5_ADDR_OF(fte_match_param, + fte_param2, outer_headers); + void *fte_mask = MLX5_ADDR_OF(fte_match_param, + mask->match_criteria, outer_headers); + + if (!masked_memcmp(fte_mask, fte_match1, fte_match2, + MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4))) + return false; + } + + if (mask->match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) { + void *fte_match1 = MLX5_ADDR_OF(fte_match_param, + fte_param1, misc_parameters); + void *fte_match2 = MLX5_ADDR_OF(fte_match_param, + fte_param2, misc_parameters); + void *fte_mask = MLX5_ADDR_OF(fte_match_param, + mask->match_criteria, misc_parameters); + + if (!masked_memcmp(fte_mask, fte_match1, fte_match2, + MLX5_ST_SZ_BYTES(fte_match_set_misc))) + return false; + } + + if (mask->match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS) { + void *fte_match1 = MLX5_ADDR_OF(fte_match_param, + fte_param1, inner_headers); + void *fte_match2 = MLX5_ADDR_OF(fte_match_param, + fte_param2, inner_headers); + void *fte_mask = MLX5_ADDR_OF(fte_match_param, + mask->match_criteria, inner_headers); + + if (!masked_memcmp(fte_mask, fte_match1, fte_match2, + MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4))) + return false; + } + return true; +} + +static bool compare_match_criteria(u8 match_criteria_enable1, + u8 match_criteria_enable2, + void *mask1, void *mask2) +{ + return match_criteria_enable1 == match_criteria_enable2 && + !memcmp(mask1, mask2, MLX5_ST_SZ_BYTES(fte_match_param)); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index ae03ae497cbf..b03371439ccc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -72,6 +72,7 @@ struct mlx5_flow_rule { struct mlx5_flow_table { struct fs_node node; u32 id; + unsigned int level; enum fs_flow_table_type type; }; @@ -84,4 +85,44 @@ struct fs_fte { u32 action; }; +struct fs_prio { + struct fs_node node; + unsigned int max_ft; + unsigned int start_level; + unsigned int prio; +}; + +struct mlx5_flow_namespace { + /* parent == NULL => root ns */ + struct fs_node node; +}; + +struct mlx5_flow_group_mask { + u8 match_criteria_enable; + u32 match_criteria[MLX5_ST_SZ_DW(fte_match_param)]; +}; + +#define fs_get_obj(v, _node) {v = container_of((_node), typeof(*v), node); } + +#define fs_list_for_each_entry(pos, root) \ + list_for_each_entry(pos, root, node.list) + +#define fs_for_each_ns_or_ft_reverse(pos, prio) \ + list_for_each_entry_reverse(pos, &(prio)->node.children, list) + +#define fs_for_each_ns_or_ft(pos, prio) \ + list_for_each_entry(pos, (&(prio)->node.children), list) + +#define fs_for_each_prio(pos, ns) \ + fs_list_for_each_entry(pos, &(ns)->node.children) + +#define fs_for_each_fg(pos, ft) \ + fs_list_for_each_entry(pos, &(ft)->node.children) + +#define fs_for_each_fte(pos, fg) \ + fs_list_for_each_entry(pos, &(fg)->node.children) + +#define fs_for_each_dst(pos, fte) \ + fs_list_for_each_entry(pos, &(fte)->node.children) + #endif -- cgit v1.2.3-59-g8ed1b From 0c56b97503fdf027488897db5709e2557aa1592a Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 10 Dec 2015 17:12:42 +0200 Subject: net/mlx5_core: Introduce flow steering API Introducing the following objects: mlx5_flow_root_namespace: represent the root of specific flow table type tree(e.g NIC receive, FDB, etc..) mlx5_flow_group: define the mask of the flow specification. fs_fte(flow steering flow table entry): defines the value of the flow specification. The following describes the relationships between the tree objects: root_namespace --> priorities -->namespaces --> priorities -->flow-tables --> flow-groups --> flow-entries --> destinations When we create new object(flow table/flow group/flow table entry), we call to the FW command and then we add the related sw object to the tree. When we destroy object, e.g. call to mlx5_destroy_flow_table, we use the tree node destructor for destroying the FW object and remove the node from the tree. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 464 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 23 ++ 2 files changed, 487 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index cac0d156a3d1..1828351102c5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -35,6 +35,12 @@ #include "mlx5_core.h" #include "fs_core.h" +#include "fs_cmd.h" + +static void del_rule(struct fs_node *node); +static void del_flow_table(struct fs_node *node); +static void del_flow_group(struct fs_node *node); +static void del_fte(struct fs_node *node); static void tree_init_node(struct fs_node *node, unsigned int refcount, @@ -207,3 +213,461 @@ static bool compare_match_criteria(u8 match_criteria_enable1, return match_criteria_enable1 == match_criteria_enable2 && !memcmp(mask1, mask2, MLX5_ST_SZ_BYTES(fte_match_param)); } + +static struct mlx5_flow_root_namespace *find_root(struct fs_node *node) +{ + struct fs_node *root; + struct mlx5_flow_namespace *ns; + + root = node->root; + + if (WARN_ON(root->type != FS_TYPE_NAMESPACE)) { + pr_warn("mlx5: flow steering node is not in tree or garbaged\n"); + return NULL; + } + + ns = container_of(root, struct mlx5_flow_namespace, node); + return container_of(ns, struct mlx5_flow_root_namespace, ns); +} + +static inline struct mlx5_core_dev *get_dev(struct fs_node *node) +{ + struct mlx5_flow_root_namespace *root = find_root(node); + + if (root) + return root->dev; + return NULL; +} + +static void del_flow_table(struct fs_node *node) +{ + struct mlx5_flow_table *ft; + struct mlx5_core_dev *dev; + struct fs_prio *prio; + int err; + + fs_get_obj(ft, node); + dev = get_dev(&ft->node); + + err = mlx5_cmd_destroy_flow_table(dev, ft); + if (err) + pr_warn("flow steering can't destroy ft\n"); + fs_get_obj(prio, ft->node.parent); + prio->num_ft--; +} + +static void del_rule(struct fs_node *node) +{ + struct mlx5_flow_rule *rule; + struct mlx5_flow_table *ft; + struct mlx5_flow_group *fg; + struct fs_fte *fte; + u32 *match_value; + struct mlx5_core_dev *dev = get_dev(node); + int match_len = MLX5_ST_SZ_BYTES(fte_match_param); + int err; + + match_value = mlx5_vzalloc(match_len); + if (!match_value) { + pr_warn("failed to allocate inbox\n"); + return; + } + + fs_get_obj(rule, node); + fs_get_obj(fte, rule->node.parent); + fs_get_obj(fg, fte->node.parent); + memcpy(match_value, fte->val, sizeof(fte->val)); + fs_get_obj(ft, fg->node.parent); + list_del(&rule->node.list); + fte->dests_size--; + if (fte->dests_size) { + err = mlx5_cmd_update_fte(dev, ft, + fg->id, fte); + if (err) + pr_warn("%s can't del rule fg id=%d fte_index=%d\n", + __func__, fg->id, fte->index); + } + kvfree(match_value); +} + +static void del_fte(struct fs_node *node) +{ + struct mlx5_flow_table *ft; + struct mlx5_flow_group *fg; + struct mlx5_core_dev *dev; + struct fs_fte *fte; + int err; + + fs_get_obj(fte, node); + fs_get_obj(fg, fte->node.parent); + fs_get_obj(ft, fg->node.parent); + + dev = get_dev(&ft->node); + err = mlx5_cmd_delete_fte(dev, ft, + fte->index); + if (err) + pr_warn("flow steering can't delete fte in index %d of flow group id %d\n", + fte->index, fg->id); + + fte->status = 0; + fg->num_ftes--; +} + +static void del_flow_group(struct fs_node *node) +{ + struct mlx5_flow_group *fg; + struct mlx5_flow_table *ft; + struct mlx5_core_dev *dev; + + fs_get_obj(fg, node); + fs_get_obj(ft, fg->node.parent); + dev = get_dev(&ft->node); + + if (mlx5_cmd_destroy_flow_group(dev, ft, fg->id)) + pr_warn("flow steering can't destroy fg %d of ft %d\n", + fg->id, ft->id); +} + +static struct fs_fte *alloc_fte(u8 action, + u32 flow_tag, + u32 *match_value, + unsigned int index) +{ + struct fs_fte *fte; + + fte = kzalloc(sizeof(*fte), GFP_KERNEL); + if (!fte) + return ERR_PTR(-ENOMEM); + + memcpy(fte->val, match_value, sizeof(fte->val)); + fte->node.type = FS_TYPE_FLOW_ENTRY; + fte->flow_tag = flow_tag; + fte->index = index; + fte->action = action; + + return fte; +} + +static struct mlx5_flow_group *alloc_flow_group(u32 *create_fg_in) +{ + struct mlx5_flow_group *fg; + void *match_criteria = MLX5_ADDR_OF(create_flow_group_in, + create_fg_in, match_criteria); + u8 match_criteria_enable = MLX5_GET(create_flow_group_in, + create_fg_in, + match_criteria_enable); + fg = kzalloc(sizeof(*fg), GFP_KERNEL); + if (!fg) + return ERR_PTR(-ENOMEM); + + fg->mask.match_criteria_enable = match_criteria_enable; + memcpy(&fg->mask.match_criteria, match_criteria, + sizeof(fg->mask.match_criteria)); + fg->node.type = FS_TYPE_FLOW_GROUP; + fg->start_index = MLX5_GET(create_flow_group_in, create_fg_in, + start_flow_index); + fg->max_ftes = MLX5_GET(create_flow_group_in, create_fg_in, + end_flow_index) - fg->start_index + 1; + return fg; +} + +static struct mlx5_flow_table *alloc_flow_table(int level, int max_fte, + enum fs_flow_table_type table_type) +{ + struct mlx5_flow_table *ft; + + ft = kzalloc(sizeof(*ft), GFP_KERNEL); + if (!ft) + return NULL; + + ft->level = level; + ft->node.type = FS_TYPE_FLOW_TABLE; + ft->type = table_type; + ft->max_fte = max_fte; + + return ft; +} + +static struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, + int prio, + int max_fte) +{ + struct mlx5_flow_table *ft; + int err; + int log_table_sz; + struct mlx5_flow_root_namespace *root = + find_root(&ns->node); + struct fs_prio *fs_prio = NULL; + + if (!root) { + pr_err("mlx5: flow steering failed to find root of namespace\n"); + return ERR_PTR(-ENODEV); + } + + fs_prio = find_prio(ns, prio); + if (!fs_prio) + return ERR_PTR(-EINVAL); + + lock_ref_node(&fs_prio->node); + if (fs_prio->num_ft == fs_prio->max_ft) { + err = -ENOSPC; + goto unlock_prio; + } + + ft = alloc_flow_table(find_next_free_level(fs_prio), + roundup_pow_of_two(max_fte), + root->table_type); + if (!ft) { + err = -ENOMEM; + goto unlock_prio; + } + + tree_init_node(&ft->node, 1, del_flow_table); + log_table_sz = ilog2(ft->max_fte); + err = mlx5_cmd_create_flow_table(root->dev, ft->type, ft->level, + log_table_sz, &ft->id); + if (err) + goto free_ft; + + tree_add_node(&ft->node, &fs_prio->node); + list_add_tail(&ft->node.list, &fs_prio->node.children); + fs_prio->num_ft++; + unlock_ref_node(&fs_prio->node); + + return ft; + +free_ft: + kfree(ft); +unlock_prio: + unlock_ref_node(&fs_prio->node); + return ERR_PTR(err); +} + +static struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft, + u32 *fg_in) +{ + struct mlx5_flow_group *fg; + struct mlx5_core_dev *dev = get_dev(&ft->node); + int err; + + if (!dev) + return ERR_PTR(-ENODEV); + + fg = alloc_flow_group(fg_in); + if (IS_ERR(fg)) + return fg; + + lock_ref_node(&ft->node); + err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id); + if (err) { + kfree(fg); + unlock_ref_node(&ft->node); + return ERR_PTR(err); + } + /* Add node to tree */ + tree_init_node(&fg->node, 1, del_flow_group); + tree_add_node(&fg->node, &ft->node); + /* Add node to group list */ + list_add(&fg->node.list, ft->node.children.prev); + unlock_ref_node(&ft->node); + + return fg; +} + +static struct mlx5_flow_rule *alloc_rule(struct mlx5_flow_destination *dest) +{ + struct mlx5_flow_rule *rule; + + rule = kzalloc(sizeof(*rule), GFP_KERNEL); + if (!rule) + return NULL; + + rule->node.type = FS_TYPE_FLOW_DEST; + memcpy(&rule->dest_attr, dest, sizeof(*dest)); + + return rule; +} + +/* fte should not be deleted while calling this function */ +static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte, + struct mlx5_flow_group *fg, + struct mlx5_flow_destination *dest) +{ + struct mlx5_flow_table *ft; + struct mlx5_flow_rule *rule; + int err; + + rule = alloc_rule(dest); + if (!rule) + return ERR_PTR(-ENOMEM); + + fs_get_obj(ft, fg->node.parent); + /* Add dest to dests list- added as first element after the head */ + tree_init_node(&rule->node, 1, del_rule); + list_add_tail(&rule->node.list, &fte->node.children); + fte->dests_size++; + if (fte->dests_size == 1) + err = mlx5_cmd_create_fte(get_dev(&ft->node), + ft, fg->id, fte); + else + err = mlx5_cmd_update_fte(get_dev(&ft->node), + ft, fg->id, fte); + if (err) + goto free_rule; + + fte->status |= FS_FTE_STATUS_EXISTING; + + return rule; + +free_rule: + list_del(&rule->node.list); + kfree(rule); + fte->dests_size--; + return ERR_PTR(err); +} + +/* Assumed fg is locked */ +static unsigned int get_free_fte_index(struct mlx5_flow_group *fg, + struct list_head **prev) +{ + struct fs_fte *fte; + unsigned int start = fg->start_index; + + if (prev) + *prev = &fg->node.children; + + /* assumed list is sorted by index */ + fs_for_each_fte(fte, fg) { + if (fte->index != start) + return start; + start++; + if (prev) + *prev = &fte->node.list; + } + + return start; +} + +/* prev is output, prev->next = new_fte */ +static struct fs_fte *create_fte(struct mlx5_flow_group *fg, + u32 *match_value, + u8 action, + u32 flow_tag, + struct list_head **prev) +{ + struct fs_fte *fte; + int index; + + index = get_free_fte_index(fg, prev); + fte = alloc_fte(action, flow_tag, match_value, index); + if (IS_ERR(fte)) + return fte; + + return fte; +} + +/* Assuming parent fg(flow table) is locked */ +static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, + u32 *match_value, + u8 action, + u32 flow_tag, + struct mlx5_flow_destination *dest) +{ + struct fs_fte *fte; + struct mlx5_flow_rule *rule; + struct mlx5_flow_table *ft; + struct list_head *prev; + + lock_ref_node(&fg->node); + fs_for_each_fte(fte, fg) { + nested_lock_ref_node(&fte->node); + if (compare_match_value(&fg->mask, match_value, &fte->val) && + action == fte->action && flow_tag == fte->flow_tag) { + rule = add_rule_fte(fte, fg, dest); + unlock_ref_node(&fte->node); + if (IS_ERR(rule)) + goto unlock_fg; + else + goto add_rule; + } + unlock_ref_node(&fte->node); + } + fs_get_obj(ft, fg->node.parent); + if (fg->num_ftes >= fg->max_ftes) { + rule = ERR_PTR(-ENOSPC); + goto unlock_fg; + } + + fte = create_fte(fg, match_value, action, flow_tag, &prev); + if (IS_ERR(fte)) { + rule = (void *)fte; + goto unlock_fg; + } + tree_init_node(&fte->node, 0, del_fte); + rule = add_rule_fte(fte, fg, dest); + if (IS_ERR(rule)) { + kfree(fte); + goto unlock_fg; + } + + fg->num_ftes++; + + tree_add_node(&fte->node, &fg->node); + list_add(&fte->node.list, prev); +add_rule: + tree_add_node(&rule->node, &fte->node); +unlock_fg: + unlock_ref_node(&fg->node); + return rule; +} + +static struct mlx5_flow_rule * +mlx5_add_flow_rule(struct mlx5_flow_table *ft, + u8 match_criteria_enable, + u32 *match_criteria, + u32 *match_value, + u32 action, + u32 flow_tag, + struct mlx5_flow_destination *dest) +{ + struct mlx5_flow_group *g; + struct mlx5_flow_rule *rule = ERR_PTR(-EINVAL); + + tree_get_node(&ft->node); + lock_ref_node(&ft->node); + fs_for_each_fg(g, ft) + if (compare_match_criteria(g->mask.match_criteria_enable, + match_criteria_enable, + g->mask.match_criteria, + match_criteria)) { + unlock_ref_node(&ft->node); + rule = add_rule_fg(g, match_value, + action, flow_tag, dest); + goto put; + } + unlock_ref_node(&ft->node); +put: + tree_put_node(&ft->node); + return rule; +} + +static void mlx5_del_flow_rule(struct mlx5_flow_rule *rule) +{ + tree_remove_node(&rule->node); +} + +static int mlx5_destroy_flow_table(struct mlx5_flow_table *ft) +{ + if (tree_remove_node(&ft->node)) + mlx5_core_warn(get_dev(&ft->node), "Flow table %d wasn't destroyed, refcount > 1\n", + ft->id); + + return 0; +} + +static void mlx5_destroy_flow_group(struct mlx5_flow_group *fg) +{ + if (tree_remove_node(&fg->node)) + mlx5_core_warn(get_dev(&fg->node), "Flow group %d wasn't destroyed, refcount > 1\n", + fg->id); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index b03371439ccc..6c27b8ef42b7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -69,13 +69,16 @@ struct mlx5_flow_rule { struct mlx5_flow_destination dest_attr; }; +/* Type of children is mlx5_flow_group */ struct mlx5_flow_table { struct fs_node node; u32 id; + unsigned int max_fte; unsigned int level; enum fs_flow_table_type type; }; +/* Type of children is mlx5_flow_rule */ struct fs_fte { struct fs_node node; u32 val[MLX5_ST_SZ_DW(fte_match_param)]; @@ -83,15 +86,19 @@ struct fs_fte { u32 flow_tag; u32 index; u32 action; + enum fs_fte_status status; }; +/* Type of children is mlx5_flow_table/namespace */ struct fs_prio { struct fs_node node; unsigned int max_ft; unsigned int start_level; unsigned int prio; + unsigned int num_ft; }; +/* Type of children is fs_prio */ struct mlx5_flow_namespace { /* parent == NULL => root ns */ struct fs_node node; @@ -102,6 +109,22 @@ struct mlx5_flow_group_mask { u32 match_criteria[MLX5_ST_SZ_DW(fte_match_param)]; }; +/* Type of children is fs_fte */ +struct mlx5_flow_group { + struct fs_node node; + struct mlx5_flow_group_mask mask; + u32 start_index; + u32 max_ftes; + u32 num_ftes; + u32 id; +}; + +struct mlx5_flow_root_namespace { + struct mlx5_flow_namespace ns; + enum fs_flow_table_type table_type; + struct mlx5_core_dev *dev; +}; + #define fs_get_obj(v, _node) {v = container_of((_node), typeof(*v), node); } #define fs_list_for_each_entry(pos, root) \ -- cgit v1.2.3-59-g8ed1b From 2530236303d9e705db6a28eb9a10c8d79b288b37 Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 10 Dec 2015 17:12:43 +0200 Subject: net/mlx5_core: Flow steering tree initialization Flow steering initialization is based on static tree which illustrates the flow steering tree when the driver is loaded. The initialization considers the max supported flow table level of the device, a minimum of 2 kernel flow tables(vlan and mac) are required to have kernel flow table functionality. The tree structures when the driver is loaded: root_namespace(receive nic) | priority-0 (kernel priority) | namespace(kernel namespace) | priority-0 (flow tables priority) In the following patches, When the EN driver will use the flow steering API, it create two flow tables and their flow groups under priority-0(flow tables priority). Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 374 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 4 + include/linux/mlx5/driver.h | 2 + include/linux/mlx5/fs.h | 8 + 4 files changed, 388 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 1828351102c5..4264e8b34b76 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -37,6 +37,54 @@ #include "fs_core.h" #include "fs_cmd.h" +#define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\ + sizeof(struct init_tree_node)) + +#define INIT_PRIO(min_level_val, max_ft_val,\ + start_level_val, ...) {.type = FS_TYPE_PRIO,\ + .min_ft_level = min_level_val,\ + .start_level = start_level_val,\ + .max_ft = max_ft_val,\ + .children = (struct init_tree_node[]) {__VA_ARGS__},\ + .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \ +} + +#define ADD_PRIO(min_level_val, max_ft_val, start_level_val, ...)\ + INIT_PRIO(min_level_val, max_ft_val, start_level_val,\ + __VA_ARGS__)\ + +#define ADD_FT_PRIO(max_ft_val, start_level_val, ...)\ + INIT_PRIO(0, max_ft_val, start_level_val,\ + __VA_ARGS__)\ + +#define ADD_NS(...) {.type = FS_TYPE_NAMESPACE,\ + .children = (struct init_tree_node[]) {__VA_ARGS__},\ + .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \ +} + +#define KERNEL_START_LEVEL 0 +#define KERNEL_P0_START_LEVEL KERNEL_START_LEVEL +#define KERNEL_MAX_FT 2 +#define KENREL_MIN_LEVEL 2 +static struct init_tree_node { + enum fs_node_type type; + struct init_tree_node *children; + int ar_size; + int min_ft_level; + int prio; + int max_ft; + int start_level; +} root_fs = { + .type = FS_TYPE_NAMESPACE, + .ar_size = 1, + .children = (struct init_tree_node[]) { + ADD_PRIO(KENREL_MIN_LEVEL, KERNEL_MAX_FT, + KERNEL_START_LEVEL, + ADD_NS(ADD_FT_PRIO(KERNEL_MAX_FT, + KERNEL_P0_START_LEVEL))), + } +}; + static void del_rule(struct fs_node *node); static void del_flow_table(struct fs_node *node); static void del_flow_group(struct fs_node *node); @@ -671,3 +719,329 @@ static void mlx5_destroy_flow_group(struct mlx5_flow_group *fg) mlx5_core_warn(get_dev(&fg->node), "Flow group %d wasn't destroyed, refcount > 1\n", fg->id); } + +static struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev, + enum mlx5_flow_namespace_type type) +{ + struct mlx5_flow_root_namespace *root_ns = dev->priv.root_ns; + int prio; + static struct fs_prio *fs_prio; + struct mlx5_flow_namespace *ns; + + if (!root_ns) + return NULL; + + switch (type) { + case MLX5_FLOW_NAMESPACE_KERNEL: + prio = 0; + break; + case MLX5_FLOW_NAMESPACE_FDB: + if (dev->priv.fdb_root_ns) + return &dev->priv.fdb_root_ns->ns; + else + return NULL; + default: + return NULL; + } + + fs_prio = find_prio(&root_ns->ns, prio); + if (!fs_prio) + return NULL; + + ns = list_first_entry(&fs_prio->node.children, + typeof(*ns), + node.list); + + return ns; +} + +static struct fs_prio *fs_create_prio(struct mlx5_flow_namespace *ns, + unsigned prio, int max_ft, + int start_level) +{ + struct fs_prio *fs_prio; + + fs_prio = kzalloc(sizeof(*fs_prio), GFP_KERNEL); + if (!fs_prio) + return ERR_PTR(-ENOMEM); + + fs_prio->node.type = FS_TYPE_PRIO; + tree_init_node(&fs_prio->node, 1, NULL); + tree_add_node(&fs_prio->node, &ns->node); + fs_prio->max_ft = max_ft; + fs_prio->prio = prio; + fs_prio->start_level = start_level; + list_add_tail(&fs_prio->node.list, &ns->node.children); + + return fs_prio; +} + +static struct mlx5_flow_namespace *fs_init_namespace(struct mlx5_flow_namespace + *ns) +{ + ns->node.type = FS_TYPE_NAMESPACE; + + return ns; +} + +static struct mlx5_flow_namespace *fs_create_namespace(struct fs_prio *prio) +{ + struct mlx5_flow_namespace *ns; + + ns = kzalloc(sizeof(*ns), GFP_KERNEL); + if (!ns) + return ERR_PTR(-ENOMEM); + + fs_init_namespace(ns); + tree_init_node(&ns->node, 1, NULL); + tree_add_node(&ns->node, &prio->node); + list_add_tail(&ns->node.list, &prio->node.children); + + return ns; +} + +static int init_root_tree_recursive(int max_ft_level, struct init_tree_node *init_node, + struct fs_node *fs_parent_node, + struct init_tree_node *init_parent_node, + int index) +{ + struct mlx5_flow_namespace *fs_ns; + struct fs_prio *fs_prio; + struct fs_node *base; + int i; + int err; + + if (init_node->type == FS_TYPE_PRIO) { + if (init_node->min_ft_level > max_ft_level) + return -ENOTSUPP; + + fs_get_obj(fs_ns, fs_parent_node); + fs_prio = fs_create_prio(fs_ns, index, init_node->max_ft, + init_node->start_level); + if (IS_ERR(fs_prio)) + return PTR_ERR(fs_prio); + base = &fs_prio->node; + } else if (init_node->type == FS_TYPE_NAMESPACE) { + fs_get_obj(fs_prio, fs_parent_node); + fs_ns = fs_create_namespace(fs_prio); + if (IS_ERR(fs_ns)) + return PTR_ERR(fs_ns); + base = &fs_ns->node; + } else { + return -EINVAL; + } + for (i = 0; i < init_node->ar_size; i++) { + err = init_root_tree_recursive(max_ft_level, + &init_node->children[i], base, + init_node, i); + if (err) + return err; + } + + return 0; +} + +static int init_root_tree(int max_ft_level, struct init_tree_node *init_node, + struct fs_node *fs_parent_node) +{ + int i; + struct mlx5_flow_namespace *fs_ns; + int err; + + fs_get_obj(fs_ns, fs_parent_node); + for (i = 0; i < init_node->ar_size; i++) { + err = init_root_tree_recursive(max_ft_level, + &init_node->children[i], + &fs_ns->node, + init_node, i); + if (err) + return err; + } + return 0; +} + +static struct mlx5_flow_root_namespace *create_root_ns(struct mlx5_core_dev *dev, + enum fs_flow_table_type + table_type) +{ + struct mlx5_flow_root_namespace *root_ns; + struct mlx5_flow_namespace *ns; + + /* create the root namespace */ + root_ns = mlx5_vzalloc(sizeof(*root_ns)); + if (!root_ns) + return NULL; + + root_ns->dev = dev; + root_ns->table_type = table_type; + + ns = &root_ns->ns; + fs_init_namespace(ns); + tree_init_node(&ns->node, 1, NULL); + tree_add_node(&ns->node, NULL); + + return root_ns; +} + +static int init_root_ns(struct mlx5_core_dev *dev) +{ + int max_ft_level = MLX5_CAP_FLOWTABLE(dev, + flow_table_properties_nic_receive. + max_ft_level); + + dev->priv.root_ns = create_root_ns(dev, FS_FT_NIC_RX); + if (IS_ERR_OR_NULL(dev->priv.root_ns)) + goto cleanup; + + if (init_root_tree(max_ft_level, &root_fs, &dev->priv.root_ns->ns.node)) + goto cleanup; + + return 0; + +cleanup: + mlx5_cleanup_fs(dev); + return -ENOMEM; +} + +static void cleanup_single_prio_root_ns(struct mlx5_core_dev *dev, + struct mlx5_flow_root_namespace *root_ns) +{ + struct fs_node *prio; + + if (!root_ns) + return; + + if (!list_empty(&root_ns->ns.node.children)) { + prio = list_first_entry(&root_ns->ns.node.children, + struct fs_node, + list); + if (tree_remove_node(prio)) + mlx5_core_warn(dev, + "Flow steering priority wasn't destroyed, refcount > 1\n"); + } + if (tree_remove_node(&root_ns->ns.node)) + mlx5_core_warn(dev, + "Flow steering namespace wasn't destroyed, refcount > 1\n"); + root_ns = NULL; +} + +static void cleanup_root_ns(struct mlx5_core_dev *dev) +{ + struct mlx5_flow_root_namespace *root_ns = dev->priv.root_ns; + struct fs_prio *iter_prio; + + if (!MLX5_CAP_GEN(dev, nic_flow_table)) + return; + + if (!root_ns) + return; + + /* stage 1 */ + fs_for_each_prio(iter_prio, &root_ns->ns) { + struct fs_node *node; + struct mlx5_flow_namespace *iter_ns; + + fs_for_each_ns_or_ft(node, iter_prio) { + if (node->type == FS_TYPE_FLOW_TABLE) + continue; + fs_get_obj(iter_ns, node); + while (!list_empty(&iter_ns->node.children)) { + struct fs_prio *obj_iter_prio2; + struct fs_node *iter_prio2 = + list_first_entry(&iter_ns->node.children, + struct fs_node, + list); + + fs_get_obj(obj_iter_prio2, iter_prio2); + if (tree_remove_node(iter_prio2)) { + mlx5_core_warn(dev, + "Priority %d wasn't destroyed, refcount > 1\n", + obj_iter_prio2->prio); + return; + } + } + } + } + + /* stage 2 */ + fs_for_each_prio(iter_prio, &root_ns->ns) { + while (!list_empty(&iter_prio->node.children)) { + struct fs_node *iter_ns = + list_first_entry(&iter_prio->node.children, + struct fs_node, + list); + if (tree_remove_node(iter_ns)) { + mlx5_core_warn(dev, + "Namespace wasn't destroyed, refcount > 1\n"); + return; + } + } + } + + /* stage 3 */ + while (!list_empty(&root_ns->ns.node.children)) { + struct fs_prio *obj_prio_node; + struct fs_node *prio_node = + list_first_entry(&root_ns->ns.node.children, + struct fs_node, + list); + + fs_get_obj(obj_prio_node, prio_node); + if (tree_remove_node(prio_node)) { + mlx5_core_warn(dev, + "Priority %d wasn't destroyed, refcount > 1\n", + obj_prio_node->prio); + return; + } + } + + if (tree_remove_node(&root_ns->ns.node)) { + mlx5_core_warn(dev, + "root namespace wasn't destroyed, refcount > 1\n"); + return; + } + + dev->priv.root_ns = NULL; +} + +void mlx5_cleanup_fs(struct mlx5_core_dev *dev) +{ + cleanup_root_ns(dev); + cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns); +} + +static int init_fdb_root_ns(struct mlx5_core_dev *dev) +{ + struct fs_prio *prio; + + dev->priv.fdb_root_ns = create_root_ns(dev, FS_FT_FDB); + if (!dev->priv.fdb_root_ns) + return -ENOMEM; + + /* create 1 prio*/ + prio = fs_create_prio(&dev->priv.fdb_root_ns->ns, 0, 1, 0); + if (IS_ERR(prio)) { + cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns); + return PTR_ERR(prio); + } else { + return 0; + } +} + +int mlx5_init_fs(struct mlx5_core_dev *dev) +{ + int err = 0; + + if (MLX5_CAP_GEN(dev, nic_flow_table)) { + err = init_root_ns(dev); + if (err) + return err; + } + if (MLX5_CAP_GEN(dev, eswitch_flow_table)) { + err = init_fdb_root_ns(dev); + if (err) + cleanup_root_ns(dev); + } + + return err; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 6c27b8ef42b7..4ebb97fd5544 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -46,6 +46,7 @@ enum fs_node_type { enum fs_flow_table_type { FS_FT_NIC_RX = 0x0, + FS_FT_FDB = 0X4, }; enum fs_fte_status { @@ -125,6 +126,9 @@ struct mlx5_flow_root_namespace { struct mlx5_core_dev *dev; }; +int mlx5_init_fs(struct mlx5_core_dev *dev); +void mlx5_cleanup_fs(struct mlx5_core_dev *dev); + #define fs_get_obj(v, _node) {v = container_of((_node), typeof(*v), node); } #define fs_list_for_each_entry(pos, root) \ diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index ac098b6b97bf..2fd7019f69db 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -502,6 +502,8 @@ struct mlx5_priv { struct mlx5_eswitch *eswitch; struct mlx5_core_sriov sriov; unsigned long pci_dev_data; + struct mlx5_flow_root_namespace *root_ns; + struct mlx5_flow_root_namespace *fdb_root_ns; }; enum mlx5_device_state { diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index 34fd8dc0b3e1..16ae5233dc7b 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -35,6 +35,13 @@ #include +#define MLX5_FS_DEFAULT_FLOW_TAG 0x0 + +enum mlx5_flow_namespace_type { + MLX5_FLOW_NAMESPACE_KERNEL, + MLX5_FLOW_NAMESPACE_FDB, +}; + struct mlx5_flow_table; struct mlx5_flow_destination { @@ -42,6 +49,7 @@ struct mlx5_flow_destination { union { u32 tir_num; struct mlx5_flow_table *ft; + u32 vport_num; }; }; #endif -- cgit v1.2.3-59-g8ed1b From 86d722ad2c3bd2f0536b196b7fd67ae2a7e2a492 Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 10 Dec 2015 17:12:44 +0200 Subject: net/mlx5: Use flow steering infrastructure for mlx5_en Expose the new flow steering API and remove the old one. Few changes are required: 1. The Ethernet flow steering follows the existing implementation, but uses the new steering API. The old flow steering implementation is removed. 2. Move the E-switch FDB management to use the new API. 3. When driver is loaded call to mlx5_init_fs which initialize the flow steering tree structure, open namespaces for NIC receive and for E-switch FDB. 4. Call to mlx5_cleanup_fs when the driver is unloaded. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en.h | 23 +- .../ethernet/mellanox/mlx5/core/en_flow_table.c | 824 +++++++++++++-------- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 291 ++------ drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 15 +- .../net/ethernet/mellanox/mlx5/core/flow_table.c | 422 ----------- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 26 +- drivers/net/ethernet/mellanox/mlx5/core/main.c | 9 + include/linux/mlx5/flow_table.h | 63 -- include/linux/mlx5/fs.h | 38 + 11 files changed, 633 insertions(+), 1082 deletions(-) delete mode 100644 drivers/net/ethernet/mellanox/mlx5/core/flow_table.c delete mode 100644 include/linux/mlx5/flow_table.h (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 7fc5e2388dec..11ee062965c5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_MLX5_CORE) += mlx5_core.o mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \ mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o -mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o flow_table.o eswitch.o \ +mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o \ en_main.o en_flow_table.o en_ethtool.o en_tx.o en_rx.o \ en_txrx.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 89313d46952d..f689ce580b44 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -64,6 +64,8 @@ #define MLX5E_UPDATE_STATS_INTERVAL 200 /* msecs */ #define MLX5E_SQ_BF_BUDGET 16 +#define MLX5E_NUM_MAIN_GROUPS 9 + static const char vport_strings[][ETH_GSTRING_LEN] = { /* vport statistics */ "rx_packets", @@ -442,7 +444,7 @@ enum mlx5e_rqt_ix { struct mlx5e_eth_addr_info { u8 addr[ETH_ALEN + 2]; u32 tt_vec; - u32 ft_ix[MLX5E_NUM_TT]; /* flow table index per traffic type */ + struct mlx5_flow_rule *ft_rule[MLX5E_NUM_TT]; }; #define MLX5E_ETH_ADDR_HASH_SIZE (1 << BITS_PER_BYTE) @@ -466,15 +468,22 @@ enum { struct mlx5e_vlan_db { unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; - u32 active_vlans_ft_ix[VLAN_N_VID]; - u32 untagged_rule_ft_ix; - u32 any_vlan_rule_ft_ix; + struct mlx5_flow_rule *active_vlans_rule[VLAN_N_VID]; + struct mlx5_flow_rule *untagged_rule; + struct mlx5_flow_rule *any_vlan_rule; bool filter_disabled; }; struct mlx5e_flow_table { - void *vlan; - void *main; + int num_groups; + struct mlx5_flow_table *t; + struct mlx5_flow_group **g; +}; + +struct mlx5e_flow_tables { + struct mlx5_flow_namespace *ns; + struct mlx5e_flow_table vlan; + struct mlx5e_flow_table main; }; struct mlx5e_priv { @@ -497,7 +506,7 @@ struct mlx5e_priv { u32 rqtn[MLX5E_NUM_RQT]; u32 tirn[MLX5E_NUM_TT]; - struct mlx5e_flow_table ft; + struct mlx5e_flow_tables fts; struct mlx5e_eth_addr_db eth_addr; struct mlx5e_vlan_db vlan; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c b/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c index 5b93c9c6e341..80d81abc4820 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c @@ -34,9 +34,11 @@ #include #include #include -#include +#include #include "en.h" +#define MLX5_SET_CFG(p, f, v) MLX5_SET(create_flow_group_in, p, f, v) + enum { MLX5E_FULLMATCH = 0, MLX5E_ALLMULTI = 1, @@ -103,44 +105,38 @@ static void mlx5e_del_eth_addr_from_hash(struct mlx5e_eth_addr_hash_node *hn) static void mlx5e_del_eth_addr_from_flow_table(struct mlx5e_priv *priv, struct mlx5e_eth_addr_info *ai) { - void *ft = priv->ft.main; - if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP)) - mlx5_del_flow_table_entry(ft, - ai->ft_ix[MLX5E_TT_IPV6_IPSEC_ESP]); + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_IPSEC_ESP]); if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP)) - mlx5_del_flow_table_entry(ft, - ai->ft_ix[MLX5E_TT_IPV4_IPSEC_ESP]); + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_IPSEC_ESP]); if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH)) - mlx5_del_flow_table_entry(ft, - ai->ft_ix[MLX5E_TT_IPV6_IPSEC_AH]); + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_IPSEC_AH]); if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH)) - mlx5_del_flow_table_entry(ft, - ai->ft_ix[MLX5E_TT_IPV4_IPSEC_AH]); + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_IPSEC_AH]); if (ai->tt_vec & BIT(MLX5E_TT_IPV6_TCP)) - mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV6_TCP]); + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_TCP]); if (ai->tt_vec & BIT(MLX5E_TT_IPV4_TCP)) - mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV4_TCP]); + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_TCP]); if (ai->tt_vec & BIT(MLX5E_TT_IPV6_UDP)) - mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV6_UDP]); + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_UDP]); if (ai->tt_vec & BIT(MLX5E_TT_IPV4_UDP)) - mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV4_UDP]); + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_UDP]); if (ai->tt_vec & BIT(MLX5E_TT_IPV6)) - mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV6]); + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6]); if (ai->tt_vec & BIT(MLX5E_TT_IPV4)) - mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV4]); + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4]); if (ai->tt_vec & BIT(MLX5E_TT_ANY)) - mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_ANY]); + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_ANY]); } static int mlx5e_get_eth_addr_type(u8 *addr) @@ -240,44 +236,34 @@ static u32 mlx5e_get_tt_vec(struct mlx5e_eth_addr_info *ai, int type) } static int __mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv, - struct mlx5e_eth_addr_info *ai, int type, - void *flow_context, void *match_criteria) + struct mlx5e_eth_addr_info *ai, + int type, u32 *mc, u32 *mv) { + struct mlx5_flow_destination dest; u8 match_criteria_enable = 0; - void *match_value; - void *dest; - u8 *dmac; - u8 *match_criteria_dmac; - void *ft = priv->ft.main; - u32 *tirn = priv->tirn; - u32 *ft_ix; - u32 tt_vec; - int err; - - match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value); - dmac = MLX5_ADDR_OF(fte_match_param, match_value, - outer_headers.dmac_47_16); - match_criteria_dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, - outer_headers.dmac_47_16); - dest = MLX5_ADDR_OF(flow_context, flow_context, destination); - - MLX5_SET(flow_context, flow_context, action, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST); - MLX5_SET(flow_context, flow_context, destination_list_size, 1); - MLX5_SET(dest_format_struct, dest, destination_type, - MLX5_FLOW_CONTEXT_DEST_TYPE_TIR); + struct mlx5_flow_rule **rule_p; + struct mlx5_flow_table *ft = priv->fts.main.t; + u8 *mc_dmac = MLX5_ADDR_OF(fte_match_param, mc, + outer_headers.dmac_47_16); + u8 *mv_dmac = MLX5_ADDR_OF(fte_match_param, mv, + outer_headers.dmac_47_16); + u32 *tirn = priv->tirn; + u32 tt_vec; + int err = 0; + + dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; switch (type) { case MLX5E_FULLMATCH: match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - memset(match_criteria_dmac, 0xff, ETH_ALEN); - ether_addr_copy(dmac, ai->addr); + eth_broadcast_addr(mc_dmac); + ether_addr_copy(mv_dmac, ai->addr); break; case MLX5E_ALLMULTI: match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - match_criteria_dmac[0] = 0x01; - dmac[0] = 0x01; + mc_dmac[0] = 0x01; + mv_dmac[0] = 0x01; break; case MLX5E_PROMISC: @@ -286,190 +272,165 @@ static int __mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv, tt_vec = mlx5e_get_tt_vec(ai, type); - ft_ix = &ai->ft_ix[MLX5E_TT_ANY]; if (tt_vec & BIT(MLX5E_TT_ANY)) { - MLX5_SET(dest_format_struct, dest, destination_id, - tirn[MLX5E_TT_ANY]); - err = mlx5_add_flow_table_entry(ft, match_criteria_enable, - match_criteria, flow_context, - ft_ix); - if (err) + rule_p = &ai->ft_rule[MLX5E_TT_ANY]; + dest.tir_num = tirn[MLX5E_TT_ANY]; + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_ANY); } match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - MLX5_SET_TO_ONES(fte_match_param, match_criteria, - outer_headers.ethertype); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); - ft_ix = &ai->ft_ix[MLX5E_TT_IPV4]; if (tt_vec & BIT(MLX5E_TT_IPV4)) { - MLX5_SET(fte_match_param, match_value, outer_headers.ethertype, + rule_p = &ai->ft_rule[MLX5E_TT_IPV4]; + dest.tir_num = tirn[MLX5E_TT_IPV4]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, ETH_P_IP); - MLX5_SET(dest_format_struct, dest, destination_id, - tirn[MLX5E_TT_IPV4]); - err = mlx5_add_flow_table_entry(ft, match_criteria_enable, - match_criteria, flow_context, - ft_ix); - if (err) + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV4); } - ft_ix = &ai->ft_ix[MLX5E_TT_IPV6]; if (tt_vec & BIT(MLX5E_TT_IPV6)) { - MLX5_SET(fte_match_param, match_value, outer_headers.ethertype, + rule_p = &ai->ft_rule[MLX5E_TT_IPV6]; + dest.tir_num = tirn[MLX5E_TT_IPV6]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, ETH_P_IPV6); - MLX5_SET(dest_format_struct, dest, destination_id, - tirn[MLX5E_TT_IPV6]); - err = mlx5_add_flow_table_entry(ft, match_criteria_enable, - match_criteria, flow_context, - ft_ix); - if (err) + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV6); } - MLX5_SET_TO_ONES(fte_match_param, match_criteria, - outer_headers.ip_protocol); - MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol, - IPPROTO_UDP); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); + MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_UDP); - ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_UDP]; if (tt_vec & BIT(MLX5E_TT_IPV4_UDP)) { - MLX5_SET(fte_match_param, match_value, outer_headers.ethertype, + rule_p = &ai->ft_rule[MLX5E_TT_IPV4_UDP]; + dest.tir_num = tirn[MLX5E_TT_IPV4_UDP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, ETH_P_IP); - MLX5_SET(dest_format_struct, dest, destination_id, - tirn[MLX5E_TT_IPV4_UDP]); - err = mlx5_add_flow_table_entry(ft, match_criteria_enable, - match_criteria, flow_context, - ft_ix); - if (err) + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV4_UDP); } - ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_UDP]; if (tt_vec & BIT(MLX5E_TT_IPV6_UDP)) { - MLX5_SET(fte_match_param, match_value, outer_headers.ethertype, + rule_p = &ai->ft_rule[MLX5E_TT_IPV6_UDP]; + dest.tir_num = tirn[MLX5E_TT_IPV6_UDP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, ETH_P_IPV6); - MLX5_SET(dest_format_struct, dest, destination_id, - tirn[MLX5E_TT_IPV6_UDP]); - err = mlx5_add_flow_table_entry(ft, match_criteria_enable, - match_criteria, flow_context, - ft_ix); - if (err) + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV6_UDP); } - MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol, - IPPROTO_TCP); + MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_TCP); - ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_TCP]; if (tt_vec & BIT(MLX5E_TT_IPV4_TCP)) { - MLX5_SET(fte_match_param, match_value, outer_headers.ethertype, + rule_p = &ai->ft_rule[MLX5E_TT_IPV4_TCP]; + dest.tir_num = tirn[MLX5E_TT_IPV4_TCP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, ETH_P_IP); - MLX5_SET(dest_format_struct, dest, destination_id, - tirn[MLX5E_TT_IPV4_TCP]); - err = mlx5_add_flow_table_entry(ft, match_criteria_enable, - match_criteria, flow_context, - ft_ix); - if (err) + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV4_TCP); } - ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_TCP]; if (tt_vec & BIT(MLX5E_TT_IPV6_TCP)) { - MLX5_SET(fte_match_param, match_value, outer_headers.ethertype, + rule_p = &ai->ft_rule[MLX5E_TT_IPV6_TCP]; + dest.tir_num = tirn[MLX5E_TT_IPV6_TCP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, ETH_P_IPV6); - MLX5_SET(dest_format_struct, dest, destination_id, - tirn[MLX5E_TT_IPV6_TCP]); - err = mlx5_add_flow_table_entry(ft, match_criteria_enable, - match_criteria, flow_context, - ft_ix); - if (err) + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) goto err_del_ai; ai->tt_vec |= BIT(MLX5E_TT_IPV6_TCP); } - MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol, - IPPROTO_AH); + MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_AH); - ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_IPSEC_AH]; if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH)) { - MLX5_SET(fte_match_param, match_value, outer_headers.ethertype, + rule_p = &ai->ft_rule[MLX5E_TT_IPV4_IPSEC_AH]; + dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_AH]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, ETH_P_IP); - MLX5_SET(dest_format_struct, dest, destination_id, - tirn[MLX5E_TT_IPV4_IPSEC_AH]); - err = mlx5_add_flow_table_entry(ft, match_criteria_enable, - match_criteria, flow_context, - ft_ix); - if (err) + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_AH); } - ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_IPSEC_AH]; if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH)) { - MLX5_SET(fte_match_param, match_value, outer_headers.ethertype, + rule_p = &ai->ft_rule[MLX5E_TT_IPV6_IPSEC_AH]; + dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_AH]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, ETH_P_IPV6); - MLX5_SET(dest_format_struct, dest, destination_id, - tirn[MLX5E_TT_IPV6_IPSEC_AH]); - err = mlx5_add_flow_table_entry(ft, match_criteria_enable, - match_criteria, flow_context, - ft_ix); - if (err) + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_AH); } - MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol, - IPPROTO_ESP); + MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_ESP); - ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_IPSEC_ESP]; if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP)) { - MLX5_SET(fte_match_param, match_value, outer_headers.ethertype, + rule_p = &ai->ft_rule[MLX5E_TT_IPV4_IPSEC_ESP]; + dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_ESP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, ETH_P_IP); - MLX5_SET(dest_format_struct, dest, destination_id, - tirn[MLX5E_TT_IPV4_IPSEC_ESP]); - err = mlx5_add_flow_table_entry(ft, match_criteria_enable, - match_criteria, flow_context, - ft_ix); - if (err) + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_ESP); } - ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_IPSEC_ESP]; if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP)) { - MLX5_SET(fte_match_param, match_value, outer_headers.ethertype, + rule_p = &ai->ft_rule[MLX5E_TT_IPV6_IPSEC_ESP]; + dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_ESP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, ETH_P_IPV6); - MLX5_SET(dest_format_struct, dest, destination_id, - tirn[MLX5E_TT_IPV6_IPSEC_ESP]); - err = mlx5_add_flow_table_entry(ft, match_criteria_enable, - match_criteria, flow_context, - ft_ix); - if (err) + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_ESP); } return 0; err_del_ai: + err = PTR_ERR(*rule_p); + *rule_p = NULL; mlx5e_del_eth_addr_from_flow_table(priv, ai); return err; @@ -478,27 +439,25 @@ err_del_ai: static int mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv, struct mlx5e_eth_addr_info *ai, int type) { - u32 *flow_context; u32 *match_criteria; - int err; + u32 *match_value; + int err = 0; - flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context) + - MLX5_ST_SZ_BYTES(dest_format_struct)); - match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); - if (!flow_context || !match_criteria) { + match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + if (!match_value || !match_criteria) { netdev_err(priv->netdev, "%s: alloc failed\n", __func__); err = -ENOMEM; goto add_eth_addr_rule_out; } - err = __mlx5e_add_eth_addr_rule(priv, ai, type, flow_context, - match_criteria); - if (err) - netdev_err(priv->netdev, "%s: failed\n", __func__); + err = __mlx5e_add_eth_addr_rule(priv, ai, type, match_criteria, + match_value); add_eth_addr_rule_out: kvfree(match_criteria); - kvfree(flow_context); + kvfree(match_value); + return err; } @@ -551,72 +510,77 @@ enum mlx5e_vlan_rule_type { MLX5E_VLAN_RULE_TYPE_MATCH_VID, }; -static int mlx5e_add_vlan_rule(struct mlx5e_priv *priv, - enum mlx5e_vlan_rule_type rule_type, u16 vid) +static int __mlx5e_add_vlan_rule(struct mlx5e_priv *priv, + enum mlx5e_vlan_rule_type rule_type, + u16 vid, u32 *mc, u32 *mv) { + struct mlx5_flow_table *ft = priv->fts.vlan.t; + struct mlx5_flow_destination dest; u8 match_criteria_enable = 0; - u32 *flow_context; - void *match_value; - void *dest; - u32 *match_criteria; - u32 *ft_ix; - int err; + struct mlx5_flow_rule **rule_p; + int err = 0; - flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context) + - MLX5_ST_SZ_BYTES(dest_format_struct)); - match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); - if (!flow_context || !match_criteria) { - netdev_err(priv->netdev, "%s: alloc failed\n", __func__); - err = -ENOMEM; - goto add_vlan_rule_out; - } - match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value); - dest = MLX5_ADDR_OF(flow_context, flow_context, destination); - - MLX5_SET(flow_context, flow_context, action, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST); - MLX5_SET(flow_context, flow_context, destination_list_size, 1); - MLX5_SET(dest_format_struct, dest, destination_type, - MLX5_FLOW_CONTEXT_DEST_TYPE_FLOW_TABLE); - MLX5_SET(dest_format_struct, dest, destination_id, - mlx5_get_flow_table_id(priv->ft.main)); + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = priv->fts.main.t; match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - MLX5_SET_TO_ONES(fte_match_param, match_criteria, - outer_headers.vlan_tag); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag); switch (rule_type) { case MLX5E_VLAN_RULE_TYPE_UNTAGGED: - ft_ix = &priv->vlan.untagged_rule_ft_ix; + rule_p = &priv->vlan.untagged_rule; break; case MLX5E_VLAN_RULE_TYPE_ANY_VID: - ft_ix = &priv->vlan.any_vlan_rule_ft_ix; - MLX5_SET(fte_match_param, match_value, outer_headers.vlan_tag, - 1); + rule_p = &priv->vlan.any_vlan_rule; + MLX5_SET(fte_match_param, mv, outer_headers.vlan_tag, 1); break; default: /* MLX5E_VLAN_RULE_TYPE_MATCH_VID */ - err = mlx5e_vport_context_update_vlans(priv); - if (err) - goto add_vlan_rule_out; - - ft_ix = &priv->vlan.active_vlans_ft_ix[vid]; - MLX5_SET(fte_match_param, match_value, outer_headers.vlan_tag, - 1); - MLX5_SET_TO_ONES(fte_match_param, match_criteria, - outer_headers.first_vid); - MLX5_SET(fte_match_param, match_value, outer_headers.first_vid, - vid); + rule_p = &priv->vlan.active_vlans_rule[vid]; + MLX5_SET(fte_match_param, mv, outer_headers.vlan_tag, 1); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.first_vid); + MLX5_SET(fte_match_param, mv, outer_headers.first_vid, vid); break; } - err = mlx5_add_flow_table_entry(priv->ft.vlan, match_criteria_enable, - match_criteria, flow_context, ft_ix); - if (err) - netdev_err(priv->netdev, "%s: failed\n", __func__); + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, + &dest); + + if (IS_ERR(*rule_p)) { + err = PTR_ERR(*rule_p); + *rule_p = NULL; + netdev_err(priv->netdev, "%s: add rule failed\n", __func__); + } + + return err; +} + +static int mlx5e_add_vlan_rule(struct mlx5e_priv *priv, + enum mlx5e_vlan_rule_type rule_type, u16 vid) +{ + u32 *match_criteria; + u32 *match_value; + int err = 0; + + match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + if (!match_value || !match_criteria) { + netdev_err(priv->netdev, "%s: alloc failed\n", __func__); + err = -ENOMEM; + goto add_vlan_rule_out; + } + + if (rule_type == MLX5E_VLAN_RULE_TYPE_MATCH_VID) + mlx5e_vport_context_update_vlans(priv); + + err = __mlx5e_add_vlan_rule(priv, rule_type, vid, match_criteria, + match_value); add_vlan_rule_out: kvfree(match_criteria); - kvfree(flow_context); + kvfree(match_value); + return err; } @@ -625,16 +589,23 @@ static void mlx5e_del_vlan_rule(struct mlx5e_priv *priv, { switch (rule_type) { case MLX5E_VLAN_RULE_TYPE_UNTAGGED: - mlx5_del_flow_table_entry(priv->ft.vlan, - priv->vlan.untagged_rule_ft_ix); + if (priv->vlan.untagged_rule) { + mlx5_del_flow_rule(priv->vlan.untagged_rule); + priv->vlan.untagged_rule = NULL; + } break; case MLX5E_VLAN_RULE_TYPE_ANY_VID: - mlx5_del_flow_table_entry(priv->ft.vlan, - priv->vlan.any_vlan_rule_ft_ix); + if (priv->vlan.any_vlan_rule) { + mlx5_del_flow_rule(priv->vlan.any_vlan_rule); + priv->vlan.any_vlan_rule = NULL; + } break; case MLX5E_VLAN_RULE_TYPE_MATCH_VID: - mlx5_del_flow_table_entry(priv->ft.vlan, - priv->vlan.active_vlans_ft_ix[vid]); + mlx5e_vport_context_update_vlans(priv); + if (priv->vlan.active_vlans_rule[vid]) { + mlx5_del_flow_rule(priv->vlan.active_vlans_rule[vid]); + priv->vlan.active_vlans_rule[vid] = NULL; + } mlx5e_vport_context_update_vlans(priv); break; } @@ -889,151 +860,358 @@ void mlx5e_set_rx_mode_work(struct work_struct *work) mlx5e_vport_context_update(priv); } +static void mlx5e_destroy_groups(struct mlx5e_flow_table *ft) +{ + int i; + + for (i = ft->num_groups - 1; i >= 0; i--) { + if (!IS_ERR_OR_NULL(ft->g[i])) + mlx5_destroy_flow_group(ft->g[i]); + ft->g[i] = NULL; + } + ft->num_groups = 0; +} + void mlx5e_init_eth_addr(struct mlx5e_priv *priv) { ether_addr_copy(priv->eth_addr.broadcast.addr, priv->netdev->broadcast); } -static int mlx5e_create_main_flow_table(struct mlx5e_priv *priv) +#define MLX5E_MAIN_GROUP0_SIZE BIT(3) +#define MLX5E_MAIN_GROUP1_SIZE BIT(1) +#define MLX5E_MAIN_GROUP2_SIZE BIT(0) +#define MLX5E_MAIN_GROUP3_SIZE BIT(14) +#define MLX5E_MAIN_GROUP4_SIZE BIT(13) +#define MLX5E_MAIN_GROUP5_SIZE BIT(11) +#define MLX5E_MAIN_GROUP6_SIZE BIT(2) +#define MLX5E_MAIN_GROUP7_SIZE BIT(1) +#define MLX5E_MAIN_GROUP8_SIZE BIT(0) +#define MLX5E_MAIN_TABLE_SIZE (MLX5E_MAIN_GROUP0_SIZE +\ + MLX5E_MAIN_GROUP1_SIZE +\ + MLX5E_MAIN_GROUP2_SIZE +\ + MLX5E_MAIN_GROUP3_SIZE +\ + MLX5E_MAIN_GROUP4_SIZE +\ + MLX5E_MAIN_GROUP5_SIZE +\ + MLX5E_MAIN_GROUP6_SIZE +\ + MLX5E_MAIN_GROUP7_SIZE +\ + MLX5E_MAIN_GROUP8_SIZE) + +static int __mlx5e_create_main_groups(struct mlx5e_flow_table *ft, u32 *in, + int inlen) { - struct mlx5_flow_table_group *g; - u8 *dmac; + u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); + u8 *dmac = MLX5_ADDR_OF(create_flow_group_in, in, + match_criteria.outer_headers.dmac_47_16); + int err; + int ix = 0; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP0_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP1_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP2_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); + eth_broadcast_addr(dmac); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP3_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + eth_broadcast_addr(dmac); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP4_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + eth_broadcast_addr(dmac); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP5_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); + dmac[0] = 0x01; + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP6_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + dmac[0] = 0x01; + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP7_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + dmac[0] = 0x01; + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP8_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + return 0; + +err_destroy_groups: + err = PTR_ERR(ft->g[ft->num_groups]); + ft->g[ft->num_groups] = NULL; + mlx5e_destroy_groups(ft); + + return err; +} - g = kcalloc(9, sizeof(*g), GFP_KERNEL); - if (!g) +static int mlx5e_create_main_groups(struct mlx5e_flow_table *ft) +{ + u32 *in; + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + int err; + + in = mlx5_vzalloc(inlen); + if (!in) return -ENOMEM; - g[0].log_sz = 3; - g[0].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria, - outer_headers.ethertype); - MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria, - outer_headers.ip_protocol); - - g[1].log_sz = 1; - g[1].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - MLX5_SET_TO_ONES(fte_match_param, g[1].match_criteria, - outer_headers.ethertype); - - g[2].log_sz = 0; - - g[3].log_sz = 14; - g[3].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - dmac = MLX5_ADDR_OF(fte_match_param, g[3].match_criteria, - outer_headers.dmac_47_16); - memset(dmac, 0xff, ETH_ALEN); - MLX5_SET_TO_ONES(fte_match_param, g[3].match_criteria, - outer_headers.ethertype); - MLX5_SET_TO_ONES(fte_match_param, g[3].match_criteria, - outer_headers.ip_protocol); - - g[4].log_sz = 13; - g[4].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - dmac = MLX5_ADDR_OF(fte_match_param, g[4].match_criteria, - outer_headers.dmac_47_16); - memset(dmac, 0xff, ETH_ALEN); - MLX5_SET_TO_ONES(fte_match_param, g[4].match_criteria, - outer_headers.ethertype); - - g[5].log_sz = 11; - g[5].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - dmac = MLX5_ADDR_OF(fte_match_param, g[5].match_criteria, - outer_headers.dmac_47_16); - memset(dmac, 0xff, ETH_ALEN); - - g[6].log_sz = 2; - g[6].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - dmac = MLX5_ADDR_OF(fte_match_param, g[6].match_criteria, - outer_headers.dmac_47_16); - dmac[0] = 0x01; - MLX5_SET_TO_ONES(fte_match_param, g[6].match_criteria, - outer_headers.ethertype); - MLX5_SET_TO_ONES(fte_match_param, g[6].match_criteria, - outer_headers.ip_protocol); - - g[7].log_sz = 1; - g[7].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - dmac = MLX5_ADDR_OF(fte_match_param, g[7].match_criteria, - outer_headers.dmac_47_16); - dmac[0] = 0x01; - MLX5_SET_TO_ONES(fte_match_param, g[7].match_criteria, - outer_headers.ethertype); + err = __mlx5e_create_main_groups(ft, in, inlen); - g[8].log_sz = 0; - g[8].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - dmac = MLX5_ADDR_OF(fte_match_param, g[8].match_criteria, - outer_headers.dmac_47_16); - dmac[0] = 0x01; - priv->ft.main = mlx5_create_flow_table(priv->mdev, 1, - MLX5_FLOW_TABLE_TYPE_NIC_RCV, - 9, g); - kfree(g); + kvfree(in); + return err; +} - return priv->ft.main ? 0 : -ENOMEM; +static int mlx5e_create_main_flow_table(struct mlx5e_priv *priv) +{ + struct mlx5e_flow_table *ft = &priv->fts.main; + int err; + + ft->num_groups = 0; + ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_MAIN_TABLE_SIZE); + + if (IS_ERR(ft->t)) { + err = PTR_ERR(ft->t); + ft->t = NULL; + return err; + } + ft->g = kcalloc(MLX5E_NUM_MAIN_GROUPS, sizeof(*ft->g), GFP_KERNEL); + if (!ft->g) { + err = -ENOMEM; + goto err_destroy_main_flow_table; + } + + err = mlx5e_create_main_groups(ft); + if (err) + goto err_free_g; + return 0; + +err_free_g: + kfree(ft->g); + +err_destroy_main_flow_table: + mlx5_destroy_flow_table(ft->t); + ft->t = NULL; + + return err; +} + +static void mlx5e_destroy_flow_table(struct mlx5e_flow_table *ft) +{ + mlx5e_destroy_groups(ft); + kfree(ft->g); + mlx5_destroy_flow_table(ft->t); + ft->t = NULL; } static void mlx5e_destroy_main_flow_table(struct mlx5e_priv *priv) { - mlx5_destroy_flow_table(priv->ft.main); + mlx5e_destroy_flow_table(&priv->fts.main); } -static int mlx5e_create_vlan_flow_table(struct mlx5e_priv *priv) +#define MLX5E_NUM_VLAN_GROUPS 2 +#define MLX5E_VLAN_GROUP0_SIZE BIT(12) +#define MLX5E_VLAN_GROUP1_SIZE BIT(1) +#define MLX5E_VLAN_TABLE_SIZE (MLX5E_VLAN_GROUP0_SIZE +\ + MLX5E_VLAN_GROUP1_SIZE) + +static int __mlx5e_create_vlan_groups(struct mlx5e_flow_table *ft, u32 *in, + int inlen) +{ + int err; + int ix = 0; + u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.first_vid); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_VLAN_GROUP0_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_VLAN_GROUP1_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + return 0; + +err_destroy_groups: + err = PTR_ERR(ft->g[ft->num_groups]); + ft->g[ft->num_groups] = NULL; + mlx5e_destroy_groups(ft); + + return err; +} + +static int mlx5e_create_vlan_groups(struct mlx5e_flow_table *ft) { - struct mlx5_flow_table_group *g; + u32 *in; + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + int err; - g = kcalloc(2, sizeof(*g), GFP_KERNEL); - if (!g) + in = mlx5_vzalloc(inlen); + if (!in) return -ENOMEM; - g[0].log_sz = 12; - g[0].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria, - outer_headers.vlan_tag); - MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria, - outer_headers.first_vid); - - /* untagged + any vlan id */ - g[1].log_sz = 1; - g[1].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - MLX5_SET_TO_ONES(fte_match_param, g[1].match_criteria, - outer_headers.vlan_tag); - - priv->ft.vlan = mlx5_create_flow_table(priv->mdev, 0, - MLX5_FLOW_TABLE_TYPE_NIC_RCV, - 2, g); - - kfree(g); - return priv->ft.vlan ? 0 : -ENOMEM; + err = __mlx5e_create_vlan_groups(ft, in, inlen); + + kvfree(in); + return err; +} + +static int mlx5e_create_vlan_flow_table(struct mlx5e_priv *priv) +{ + struct mlx5e_flow_table *ft = &priv->fts.vlan; + int err; + + ft->num_groups = 0; + ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_VLAN_TABLE_SIZE); + + if (IS_ERR(ft->t)) { + err = PTR_ERR(ft->t); + ft->t = NULL; + return err; + } + ft->g = kcalloc(MLX5E_NUM_VLAN_GROUPS, sizeof(*ft->g), GFP_KERNEL); + if (!ft->g) { + err = -ENOMEM; + goto err_destroy_vlan_flow_table; + } + + err = mlx5e_create_vlan_groups(ft); + if (err) + goto err_free_g; + + return 0; + +err_free_g: + kfree(ft->g); + +err_destroy_vlan_flow_table: + mlx5_destroy_flow_table(ft->t); + ft->t = NULL; + + return err; } static void mlx5e_destroy_vlan_flow_table(struct mlx5e_priv *priv) { - mlx5_destroy_flow_table(priv->ft.vlan); + mlx5e_destroy_flow_table(&priv->fts.vlan); } int mlx5e_create_flow_tables(struct mlx5e_priv *priv) { int err; - err = mlx5e_create_main_flow_table(priv); + priv->fts.ns = mlx5_get_flow_namespace(priv->mdev, + MLX5_FLOW_NAMESPACE_KERNEL); + + if (!priv->fts.ns) + return -EINVAL; + + err = mlx5e_create_vlan_flow_table(priv); if (err) return err; - err = mlx5e_create_vlan_flow_table(priv); + err = mlx5e_create_main_flow_table(priv); if (err) - goto err_destroy_main_flow_table; + goto err_destroy_vlan_flow_table; err = mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0); if (err) - goto err_destroy_vlan_flow_table; + goto err_destroy_main_flow_table; return 0; -err_destroy_vlan_flow_table: - mlx5e_destroy_vlan_flow_table(priv); - err_destroy_main_flow_table: mlx5e_destroy_main_flow_table(priv); +err_destroy_vlan_flow_table: + mlx5e_destroy_vlan_flow_table(priv); return err; } @@ -1041,6 +1219,6 @@ err_destroy_main_flow_table: void mlx5e_destroy_flow_tables(struct mlx5e_priv *priv) { mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0); - mlx5e_destroy_vlan_flow_table(priv); mlx5e_destroy_main_flow_table(priv); + mlx5e_destroy_vlan_flow_table(priv); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index a20be56df553..d4601a564699 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -30,7 +30,7 @@ * SOFTWARE. */ -#include +#include #include "en.h" #include "eswitch.h" diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index d8939e597c54..bc3d9f8a75c1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include "mlx5_core.h" #include "eswitch.h" @@ -321,220 +321,6 @@ static void del_l2_table_entry(struct mlx5_core_dev *dev, u32 index) free_l2_table_index(l2_table, index); } -/* E-Switch FDB flow steering */ -struct dest_node { - struct list_head list; - struct mlx5_flow_destination dest; -}; - -static int _mlx5_flow_rule_apply(struct mlx5_flow_rule *fr) -{ - bool was_valid = fr->valid; - struct dest_node *dest_n; - u32 dest_list_size = 0; - void *in_match_value; - u32 *flow_context; - u32 flow_index; - int err; - int i; - - if (list_empty(&fr->dest_list)) { - if (fr->valid) - mlx5_del_flow_table_entry(fr->ft, fr->fi); - fr->valid = false; - return 0; - } - - list_for_each_entry(dest_n, &fr->dest_list, list) - dest_list_size++; - - flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context) + - MLX5_ST_SZ_BYTES(dest_format_struct) * - dest_list_size); - if (!flow_context) - return -ENOMEM; - - MLX5_SET(flow_context, flow_context, flow_tag, fr->flow_tag); - MLX5_SET(flow_context, flow_context, action, fr->action); - MLX5_SET(flow_context, flow_context, destination_list_size, - dest_list_size); - - i = 0; - list_for_each_entry(dest_n, &fr->dest_list, list) { - void *dest_addr = MLX5_ADDR_OF(flow_context, flow_context, - destination[i++]); - - MLX5_SET(dest_format_struct, dest_addr, destination_type, - dest_n->dest.type); - MLX5_SET(dest_format_struct, dest_addr, destination_id, - dest_n->dest.vport_num); - } - - in_match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value); - memcpy(in_match_value, fr->match_value, MLX5_ST_SZ_BYTES(fte_match_param)); - - err = mlx5_add_flow_table_entry(fr->ft, fr->match_criteria_enable, - fr->match_criteria, flow_context, - &flow_index); - if (!err) { - if (was_valid) - mlx5_del_flow_table_entry(fr->ft, fr->fi); - fr->fi = flow_index; - fr->valid = true; - } - kfree(flow_context); - return err; -} - -static int mlx5_flow_rule_add_dest(struct mlx5_flow_rule *fr, - struct mlx5_flow_destination *new_dest) -{ - struct dest_node *dest_n; - int err; - - dest_n = kzalloc(sizeof(*dest_n), GFP_KERNEL); - if (!dest_n) - return -ENOMEM; - - memcpy(&dest_n->dest, new_dest, sizeof(dest_n->dest)); - mutex_lock(&fr->mutex); - list_add(&dest_n->list, &fr->dest_list); - err = _mlx5_flow_rule_apply(fr); - if (err) { - list_del(&dest_n->list); - kfree(dest_n); - } - mutex_unlock(&fr->mutex); - return err; -} - -static int mlx5_flow_rule_del_dest(struct mlx5_flow_rule *fr, - struct mlx5_flow_destination *dest) -{ - struct dest_node *dest_n; - struct dest_node *n; - int err; - - mutex_lock(&fr->mutex); - list_for_each_entry_safe(dest_n, n, &fr->dest_list, list) { - if (dest->vport_num == dest_n->dest.vport_num) - goto found; - } - mutex_unlock(&fr->mutex); - return -ENOENT; - -found: - list_del(&dest_n->list); - err = _mlx5_flow_rule_apply(fr); - mutex_unlock(&fr->mutex); - kfree(dest_n); - - return err; -} - -static struct mlx5_flow_rule *find_fr(struct mlx5_eswitch *esw, - u8 match_criteria_enable, - u32 *match_value) -{ - struct hlist_head *hash = esw->mc_table; - struct esw_mc_addr *esw_mc; - u8 *dmac_v; - - dmac_v = MLX5_ADDR_OF(fte_match_param, match_value, - outer_headers.dmac_47_16); - - /* UNICAST FULL MATCH */ - if (!is_multicast_ether_addr(dmac_v)) - return NULL; - - /* MULTICAST FULL MATCH */ - esw_mc = l2addr_hash_find(hash, dmac_v, struct esw_mc_addr); - - return esw_mc ? esw_mc->uplink_rule : NULL; -} - -static struct mlx5_flow_rule *alloc_fr(void *ft, - u8 match_criteria_enable, - u32 *match_criteria, - u32 *match_value, - u32 action, - u32 flow_tag) -{ - struct mlx5_flow_rule *fr = kzalloc(sizeof(*fr), GFP_KERNEL); - - if (!fr) - return NULL; - - fr->match_criteria = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); - fr->match_value = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); - if (!fr->match_criteria || !fr->match_value) { - kfree(fr->match_criteria); - kfree(fr->match_value); - kfree(fr); - return NULL; - } - - memcpy(fr->match_criteria, match_criteria, MLX5_ST_SZ_BYTES(fte_match_param)); - memcpy(fr->match_value, match_value, MLX5_ST_SZ_BYTES(fte_match_param)); - fr->match_criteria_enable = match_criteria_enable; - fr->flow_tag = flow_tag; - fr->action = action; - - mutex_init(&fr->mutex); - INIT_LIST_HEAD(&fr->dest_list); - atomic_set(&fr->refcount, 0); - fr->ft = ft; - return fr; -} - -static void deref_fr(struct mlx5_flow_rule *fr) -{ - if (!atomic_dec_and_test(&fr->refcount)) - return; - - kfree(fr->match_criteria); - kfree(fr->match_value); - kfree(fr); -} - -static struct mlx5_flow_rule * -mlx5_add_flow_rule(struct mlx5_eswitch *esw, - u8 match_criteria_enable, - u32 *match_criteria, - u32 *match_value, - u32 action, - u32 flow_tag, - struct mlx5_flow_destination *dest) -{ - struct mlx5_flow_rule *fr; - int err; - - fr = find_fr(esw, match_criteria_enable, match_value); - fr = fr ? fr : alloc_fr(esw->fdb_table.fdb, match_criteria_enable, match_criteria, - match_value, action, flow_tag); - if (!fr) - return NULL; - - atomic_inc(&fr->refcount); - - err = mlx5_flow_rule_add_dest(fr, dest); - if (err) { - deref_fr(fr); - return NULL; - } - - return fr; -} - -static void mlx5_del_flow_rule(struct mlx5_flow_rule *fr, u32 vport) -{ - struct mlx5_flow_destination dest; - - dest.vport_num = vport; - mlx5_flow_rule_del_dest(fr, &dest); - deref_fr(fr); -} - /* E-Switch FDB */ static struct mlx5_flow_rule * esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport) @@ -569,7 +355,7 @@ esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport) "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n", dmac_v, dmac_c, vport); flow_rule = - mlx5_add_flow_rule(esw, + mlx5_add_flow_rule(esw->fdb_table.fdb, match_header, match_c, match_v, @@ -589,33 +375,61 @@ out: static int esw_create_fdb_table(struct mlx5_eswitch *esw, int nvports) { + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); struct mlx5_core_dev *dev = esw->dev; - struct mlx5_flow_table_group g; + struct mlx5_flow_namespace *root_ns; struct mlx5_flow_table *fdb; + struct mlx5_flow_group *g; + void *match_criteria; + int table_size; + u32 *flow_group_in; u8 *dmac; + int err = 0; esw_debug(dev, "Create FDB log_max_size(%d)\n", MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)); - memset(&g, 0, sizeof(g)); - /* UC MC Full match rules*/ - g.log_sz = MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size); - g.match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - dmac = MLX5_ADDR_OF(fte_match_param, g.match_criteria, - outer_headers.dmac_47_16); - /* Match criteria mask */ - memset(dmac, 0xff, 6); + root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB); + if (!root_ns) { + esw_warn(dev, "Failed to get FDB flow namespace\n"); + return -ENOMEM; + } - fdb = mlx5_create_flow_table(dev, 0, - MLX5_FLOW_TABLE_TYPE_ESWITCH, - 1, &g); - if (fdb) - esw_debug(dev, "ESW: FDB Table created fdb->id %d\n", mlx5_get_flow_table_id(fdb)); - else - esw_warn(dev, "ESW: Failed to create FDB Table\n"); + flow_group_in = mlx5_vzalloc(inlen); + if (!flow_group_in) + return -ENOMEM; + memset(flow_group_in, 0, inlen); + + table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)); + fdb = mlx5_create_flow_table(root_ns, 0, table_size); + if (IS_ERR_OR_NULL(fdb)) { + err = PTR_ERR(fdb); + esw_warn(dev, "Failed to create FDB Table err %d\n", err); + goto out; + } + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, + MLX5_MATCH_OUTER_HEADERS); + match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria); + dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, outer_headers.dmac_47_16); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 1); + eth_broadcast_addr(dmac); + + g = mlx5_create_flow_group(fdb, flow_group_in); + if (IS_ERR_OR_NULL(g)) { + err = PTR_ERR(g); + esw_warn(dev, "Failed to create flow group err(%d)\n", err); + goto out; + } + + esw->fdb_table.addr_grp = g; esw->fdb_table.fdb = fdb; - return fdb ? 0 : -ENOMEM; +out: + kfree(flow_group_in); + if (err && !IS_ERR_OR_NULL(fdb)) + mlx5_destroy_flow_table(fdb); + return err; } static void esw_destroy_fdb_table(struct mlx5_eswitch *esw) @@ -623,10 +437,11 @@ static void esw_destroy_fdb_table(struct mlx5_eswitch *esw) if (!esw->fdb_table.fdb) return; - esw_debug(esw->dev, "Destroy FDB Table fdb(%d)\n", - mlx5_get_flow_table_id(esw->fdb_table.fdb)); + esw_debug(esw->dev, "Destroy FDB Table\n"); + mlx5_destroy_flow_group(esw->fdb_table.addr_grp); mlx5_destroy_flow_table(esw->fdb_table.fdb); esw->fdb_table.fdb = NULL; + esw->fdb_table.addr_grp = NULL; } /* E-Switch vport UC/MC lists management */ @@ -689,7 +504,7 @@ static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) del_l2_table_entry(esw->dev, esw_uc->table_index); if (vaddr->flow_rule) - mlx5_del_flow_rule(vaddr->flow_rule, vport); + mlx5_del_flow_rule(vaddr->flow_rule); vaddr->flow_rule = NULL; l2addr_hash_del(esw_uc); @@ -750,14 +565,14 @@ static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) esw_mc->uplink_rule); if (vaddr->flow_rule) - mlx5_del_flow_rule(vaddr->flow_rule, vport); + mlx5_del_flow_rule(vaddr->flow_rule); vaddr->flow_rule = NULL; if (--esw_mc->refcnt) return 0; if (esw_mc->uplink_rule) - mlx5_del_flow_rule(esw_mc->uplink_rule, UPLINK_VPORT); + mlx5_del_flow_rule(esw_mc->uplink_rule); l2addr_hash_del(esw_mc); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 02ff3eade026..3416a428f70f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -88,20 +88,6 @@ struct l2addr_node { kfree(ptr); \ }) -struct mlx5_flow_rule { - void *ft; - u32 fi; - u8 match_criteria_enable; - u32 *match_criteria; - u32 *match_value; - u32 action; - u32 flow_tag; - bool valid; - atomic_t refcount; - struct mutex mutex; /* protect flow rule updates */ - struct list_head dest_list; -}; - struct mlx5_vport { struct mlx5_core_dev *dev; int vport; @@ -126,6 +112,7 @@ struct mlx5_l2_table { struct mlx5_eswitch_fdb { void *fdb; + struct mlx5_flow_group *addr_grp; }; struct mlx5_eswitch { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/flow_table.c b/drivers/net/ethernet/mellanox/mlx5/core/flow_table.c deleted file mode 100644 index ca90b9bc3b95..000000000000 --- a/drivers/net/ethernet/mellanox/mlx5/core/flow_table.c +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (c) 2013-2015, Mellanox Technologies, Ltd. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include "mlx5_core.h" - -struct mlx5_ftg { - struct mlx5_flow_table_group g; - u32 id; - u32 start_ix; -}; - -struct mlx5_flow_table { - struct mlx5_core_dev *dev; - u8 level; - u8 type; - u32 id; - struct mutex mutex; /* sync bitmap alloc */ - u16 num_groups; - struct mlx5_ftg *group; - unsigned long *bitmap; - u32 size; -}; - -static int mlx5_set_flow_entry_cmd(struct mlx5_flow_table *ft, u32 group_ix, - u32 flow_index, void *flow_context) -{ - u32 out[MLX5_ST_SZ_DW(set_fte_out)]; - u32 *in; - void *in_flow_context; - int fcdls = - MLX5_GET(flow_context, flow_context, destination_list_size) * - MLX5_ST_SZ_BYTES(dest_format_struct); - int inlen = MLX5_ST_SZ_BYTES(set_fte_in) + fcdls; - int err; - - in = mlx5_vzalloc(inlen); - if (!in) { - mlx5_core_warn(ft->dev, "failed to allocate inbox\n"); - return -ENOMEM; - } - - MLX5_SET(set_fte_in, in, table_type, ft->type); - MLX5_SET(set_fte_in, in, table_id, ft->id); - MLX5_SET(set_fte_in, in, flow_index, flow_index); - MLX5_SET(set_fte_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY); - - in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context); - memcpy(in_flow_context, flow_context, - MLX5_ST_SZ_BYTES(flow_context) + fcdls); - - MLX5_SET(flow_context, in_flow_context, group_id, - ft->group[group_ix].id); - - memset(out, 0, sizeof(out)); - err = mlx5_cmd_exec_check_status(ft->dev, in, inlen, out, - sizeof(out)); - kvfree(in); - - return err; -} - -static void mlx5_del_flow_entry_cmd(struct mlx5_flow_table *ft, u32 flow_index) -{ - u32 in[MLX5_ST_SZ_DW(delete_fte_in)]; - u32 out[MLX5_ST_SZ_DW(delete_fte_out)]; - - memset(in, 0, sizeof(in)); - memset(out, 0, sizeof(out)); - -#define MLX5_SET_DFTEI(p, x, v) MLX5_SET(delete_fte_in, p, x, v) - MLX5_SET_DFTEI(in, table_type, ft->type); - MLX5_SET_DFTEI(in, table_id, ft->id); - MLX5_SET_DFTEI(in, flow_index, flow_index); - MLX5_SET_DFTEI(in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY); - - mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out, sizeof(out)); -} - -static void mlx5_destroy_flow_group_cmd(struct mlx5_flow_table *ft, int i) -{ - u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)]; - u32 out[MLX5_ST_SZ_DW(destroy_flow_group_out)]; - - memset(in, 0, sizeof(in)); - memset(out, 0, sizeof(out)); - -#define MLX5_SET_DFGI(p, x, v) MLX5_SET(destroy_flow_group_in, p, x, v) - MLX5_SET_DFGI(in, table_type, ft->type); - MLX5_SET_DFGI(in, table_id, ft->id); - MLX5_SET_DFGI(in, opcode, MLX5_CMD_OP_DESTROY_FLOW_GROUP); - MLX5_SET_DFGI(in, group_id, ft->group[i].id); - mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out, sizeof(out)); -} - -static int mlx5_create_flow_group_cmd(struct mlx5_flow_table *ft, int i) -{ - u32 out[MLX5_ST_SZ_DW(create_flow_group_out)]; - u32 *in; - void *in_match_criteria; - int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); - struct mlx5_flow_table_group *g = &ft->group[i].g; - u32 start_ix = ft->group[i].start_ix; - u32 end_ix = start_ix + (1 << g->log_sz) - 1; - int err; - - in = mlx5_vzalloc(inlen); - if (!in) { - mlx5_core_warn(ft->dev, "failed to allocate inbox\n"); - return -ENOMEM; - } - in_match_criteria = MLX5_ADDR_OF(create_flow_group_in, in, - match_criteria); - - memset(out, 0, sizeof(out)); - -#define MLX5_SET_CFGI(p, x, v) MLX5_SET(create_flow_group_in, p, x, v) - MLX5_SET_CFGI(in, table_type, ft->type); - MLX5_SET_CFGI(in, table_id, ft->id); - MLX5_SET_CFGI(in, opcode, MLX5_CMD_OP_CREATE_FLOW_GROUP); - MLX5_SET_CFGI(in, start_flow_index, start_ix); - MLX5_SET_CFGI(in, end_flow_index, end_ix); - MLX5_SET_CFGI(in, match_criteria_enable, g->match_criteria_enable); - - memcpy(in_match_criteria, g->match_criteria, - MLX5_ST_SZ_BYTES(fte_match_param)); - - err = mlx5_cmd_exec_check_status(ft->dev, in, inlen, out, - sizeof(out)); - if (!err) - ft->group[i].id = MLX5_GET(create_flow_group_out, out, - group_id); - - kvfree(in); - - return err; -} - -static void mlx5_destroy_flow_table_groups(struct mlx5_flow_table *ft) -{ - int i; - - for (i = 0; i < ft->num_groups; i++) - mlx5_destroy_flow_group_cmd(ft, i); -} - -static int mlx5_create_flow_table_groups(struct mlx5_flow_table *ft) -{ - int err; - int i; - - for (i = 0; i < ft->num_groups; i++) { - err = mlx5_create_flow_group_cmd(ft, i); - if (err) - goto err_destroy_flow_table_groups; - } - - return 0; - -err_destroy_flow_table_groups: - for (i--; i >= 0; i--) - mlx5_destroy_flow_group_cmd(ft, i); - - return err; -} - -static int mlx5_create_flow_table_cmd(struct mlx5_flow_table *ft) -{ - u32 in[MLX5_ST_SZ_DW(create_flow_table_in)]; - u32 out[MLX5_ST_SZ_DW(create_flow_table_out)]; - int err; - - memset(in, 0, sizeof(in)); - - MLX5_SET(create_flow_table_in, in, table_type, ft->type); - MLX5_SET(create_flow_table_in, in, level, ft->level); - MLX5_SET(create_flow_table_in, in, log_size, order_base_2(ft->size)); - - MLX5_SET(create_flow_table_in, in, opcode, - MLX5_CMD_OP_CREATE_FLOW_TABLE); - - memset(out, 0, sizeof(out)); - err = mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out, - sizeof(out)); - if (err) - return err; - - ft->id = MLX5_GET(create_flow_table_out, out, table_id); - - return 0; -} - -static void mlx5_destroy_flow_table_cmd(struct mlx5_flow_table *ft) -{ - u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)]; - u32 out[MLX5_ST_SZ_DW(destroy_flow_table_out)]; - - memset(in, 0, sizeof(in)); - memset(out, 0, sizeof(out)); - -#define MLX5_SET_DFTI(p, x, v) MLX5_SET(destroy_flow_table_in, p, x, v) - MLX5_SET_DFTI(in, table_type, ft->type); - MLX5_SET_DFTI(in, table_id, ft->id); - MLX5_SET_DFTI(in, opcode, MLX5_CMD_OP_DESTROY_FLOW_TABLE); - - mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out, sizeof(out)); -} - -static int mlx5_find_group(struct mlx5_flow_table *ft, u8 match_criteria_enable, - u32 *match_criteria, int *group_ix) -{ - void *mc_outer = MLX5_ADDR_OF(fte_match_param, match_criteria, - outer_headers); - void *mc_misc = MLX5_ADDR_OF(fte_match_param, match_criteria, - misc_parameters); - void *mc_inner = MLX5_ADDR_OF(fte_match_param, match_criteria, - inner_headers); - int mc_outer_sz = MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4); - int mc_misc_sz = MLX5_ST_SZ_BYTES(fte_match_set_misc); - int mc_inner_sz = MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4); - int i; - - for (i = 0; i < ft->num_groups; i++) { - struct mlx5_flow_table_group *g = &ft->group[i].g; - void *gmc_outer = MLX5_ADDR_OF(fte_match_param, - g->match_criteria, - outer_headers); - void *gmc_misc = MLX5_ADDR_OF(fte_match_param, - g->match_criteria, - misc_parameters); - void *gmc_inner = MLX5_ADDR_OF(fte_match_param, - g->match_criteria, - inner_headers); - - if (g->match_criteria_enable != match_criteria_enable) - continue; - - if (match_criteria_enable & MLX5_MATCH_OUTER_HEADERS) - if (memcmp(mc_outer, gmc_outer, mc_outer_sz)) - continue; - - if (match_criteria_enable & MLX5_MATCH_MISC_PARAMETERS) - if (memcmp(mc_misc, gmc_misc, mc_misc_sz)) - continue; - - if (match_criteria_enable & MLX5_MATCH_INNER_HEADERS) - if (memcmp(mc_inner, gmc_inner, mc_inner_sz)) - continue; - - *group_ix = i; - return 0; - } - - return -EINVAL; -} - -static int alloc_flow_index(struct mlx5_flow_table *ft, int group_ix, u32 *ix) -{ - struct mlx5_ftg *g = &ft->group[group_ix]; - int err = 0; - - mutex_lock(&ft->mutex); - - *ix = find_next_zero_bit(ft->bitmap, ft->size, g->start_ix); - if (*ix >= (g->start_ix + (1 << g->g.log_sz))) - err = -ENOSPC; - else - __set_bit(*ix, ft->bitmap); - - mutex_unlock(&ft->mutex); - - return err; -} - -static void mlx5_free_flow_index(struct mlx5_flow_table *ft, u32 ix) -{ - __clear_bit(ix, ft->bitmap); -} - -int mlx5_add_flow_table_entry(void *flow_table, u8 match_criteria_enable, - void *match_criteria, void *flow_context, - u32 *flow_index) -{ - struct mlx5_flow_table *ft = flow_table; - int group_ix; - int err; - - err = mlx5_find_group(ft, match_criteria_enable, match_criteria, - &group_ix); - if (err) { - mlx5_core_warn(ft->dev, "mlx5_find_group failed\n"); - return err; - } - - err = alloc_flow_index(ft, group_ix, flow_index); - if (err) { - mlx5_core_warn(ft->dev, "alloc_flow_index failed\n"); - return err; - } - - return mlx5_set_flow_entry_cmd(ft, group_ix, *flow_index, flow_context); -} -EXPORT_SYMBOL(mlx5_add_flow_table_entry); - -void mlx5_del_flow_table_entry(void *flow_table, u32 flow_index) -{ - struct mlx5_flow_table *ft = flow_table; - - mlx5_del_flow_entry_cmd(ft, flow_index); - mlx5_free_flow_index(ft, flow_index); -} -EXPORT_SYMBOL(mlx5_del_flow_table_entry); - -void *mlx5_create_flow_table(struct mlx5_core_dev *dev, u8 level, u8 table_type, - u16 num_groups, - struct mlx5_flow_table_group *group) -{ - struct mlx5_flow_table *ft; - u32 start_ix = 0; - u32 ft_size = 0; - void *gr; - void *bm; - int err; - int i; - - for (i = 0; i < num_groups; i++) - ft_size += (1 << group[i].log_sz); - - ft = kzalloc(sizeof(*ft), GFP_KERNEL); - gr = kcalloc(num_groups, sizeof(struct mlx5_ftg), GFP_KERNEL); - bm = kcalloc(BITS_TO_LONGS(ft_size), sizeof(uintptr_t), GFP_KERNEL); - if (!ft || !gr || !bm) - goto err_free_ft; - - ft->group = gr; - ft->bitmap = bm; - ft->num_groups = num_groups; - ft->level = level; - ft->type = table_type; - ft->size = ft_size; - ft->dev = dev; - mutex_init(&ft->mutex); - - for (i = 0; i < ft->num_groups; i++) { - memcpy(&ft->group[i].g, &group[i], sizeof(*group)); - ft->group[i].start_ix = start_ix; - start_ix += 1 << group[i].log_sz; - } - - err = mlx5_create_flow_table_cmd(ft); - if (err) - goto err_free_ft; - - err = mlx5_create_flow_table_groups(ft); - if (err) - goto err_destroy_flow_table_cmd; - - return ft; - -err_destroy_flow_table_cmd: - mlx5_destroy_flow_table_cmd(ft); - -err_free_ft: - mlx5_core_warn(dev, "failed to alloc flow table\n"); - kfree(bm); - kfree(gr); - kfree(ft); - - return NULL; -} -EXPORT_SYMBOL(mlx5_create_flow_table); - -void mlx5_destroy_flow_table(void *flow_table) -{ - struct mlx5_flow_table *ft = flow_table; - - mlx5_destroy_flow_table_groups(ft); - mlx5_destroy_flow_table_cmd(ft); - kfree(ft->bitmap); - kfree(ft->group); - kfree(ft); -} -EXPORT_SYMBOL(mlx5_destroy_flow_table); - -u32 mlx5_get_flow_table_id(void *flow_table) -{ - struct mlx5_flow_table *ft = flow_table; - - return ft->id; -} -EXPORT_SYMBOL(mlx5_get_flow_table_id); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 4264e8b34b76..f7d62fe595f6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -436,9 +436,9 @@ static struct mlx5_flow_table *alloc_flow_table(int level, int max_fte, return ft; } -static struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, - int prio, - int max_fte) +struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, + int prio, + int max_fte) { struct mlx5_flow_table *ft; int err; @@ -491,8 +491,8 @@ unlock_prio: return ERR_PTR(err); } -static struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft, - u32 *fg_in) +struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft, + u32 *fg_in) { struct mlx5_flow_group *fg; struct mlx5_core_dev *dev = get_dev(&ft->node); @@ -669,7 +669,7 @@ unlock_fg: return rule; } -static struct mlx5_flow_rule * +struct mlx5_flow_rule * mlx5_add_flow_rule(struct mlx5_flow_table *ft, u8 match_criteria_enable, u32 *match_criteria, @@ -699,12 +699,12 @@ put: return rule; } -static void mlx5_del_flow_rule(struct mlx5_flow_rule *rule) +void mlx5_del_flow_rule(struct mlx5_flow_rule *rule) { tree_remove_node(&rule->node); } -static int mlx5_destroy_flow_table(struct mlx5_flow_table *ft) +int mlx5_destroy_flow_table(struct mlx5_flow_table *ft) { if (tree_remove_node(&ft->node)) mlx5_core_warn(get_dev(&ft->node), "Flow table %d wasn't destroyed, refcount > 1\n", @@ -713,15 +713,15 @@ static int mlx5_destroy_flow_table(struct mlx5_flow_table *ft) return 0; } -static void mlx5_destroy_flow_group(struct mlx5_flow_group *fg) +void mlx5_destroy_flow_group(struct mlx5_flow_group *fg) { if (tree_remove_node(&fg->node)) mlx5_core_warn(get_dev(&fg->node), "Flow group %d wasn't destroyed, refcount > 1\n", fg->id); } -static struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev, - enum mlx5_flow_namespace_type type) +struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev, + enum mlx5_flow_namespace_type type) { struct mlx5_flow_root_namespace *root_ns = dev->priv.root_ns; int prio; @@ -867,7 +867,7 @@ static struct mlx5_flow_root_namespace *create_root_ns(struct mlx5_core_dev *dev struct mlx5_flow_root_namespace *root_ns; struct mlx5_flow_namespace *ns; - /* create the root namespace */ + /* Create the root namespace */ root_ns = mlx5_vzalloc(sizeof(*root_ns)); if (!root_ns) return NULL; @@ -1018,7 +1018,7 @@ static int init_fdb_root_ns(struct mlx5_core_dev *dev) if (!dev->priv.fdb_root_ns) return -ENOMEM; - /* create 1 prio*/ + /* Create single prio */ prio = fs_create_prio(&dev->priv.fdb_root_ns->ns, 0, 1, 0); if (IS_ERR(prio)) { cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index c6de3240f76f..789882b7b711 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -49,6 +49,7 @@ #include #include #include "mlx5_core.h" +#include "fs_core.h" #ifdef CONFIG_MLX5_CORE_EN #include "eswitch.h" #endif @@ -1055,6 +1056,11 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) mlx5_init_srq_table(dev); mlx5_init_mr_table(dev); + err = mlx5_init_fs(dev); + if (err) { + dev_err(&pdev->dev, "Failed to init flow steering\n"); + goto err_fs; + } #ifdef CONFIG_MLX5_CORE_EN err = mlx5_eswitch_init(dev); if (err) { @@ -1093,6 +1099,8 @@ err_sriov: mlx5_eswitch_cleanup(dev->priv.eswitch); #endif err_reg_dev: + mlx5_cleanup_fs(dev); +err_fs: mlx5_cleanup_mr_table(dev); mlx5_cleanup_srq_table(dev); mlx5_cleanup_qp_table(dev); @@ -1165,6 +1173,7 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv) mlx5_eswitch_cleanup(dev->priv.eswitch); #endif + mlx5_cleanup_fs(dev); mlx5_cleanup_mr_table(dev); mlx5_cleanup_srq_table(dev); mlx5_cleanup_qp_table(dev); diff --git a/include/linux/mlx5/flow_table.h b/include/linux/mlx5/flow_table.h deleted file mode 100644 index 0f2a15cf3317..000000000000 --- a/include/linux/mlx5/flow_table.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2013-2015, Mellanox Technologies, Ltd. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef MLX5_FLOW_TABLE_H -#define MLX5_FLOW_TABLE_H - -#include - -struct mlx5_flow_table_group { - u8 log_sz; - u8 match_criteria_enable; - u32 match_criteria[MLX5_ST_SZ_DW(fte_match_param)]; -}; - -struct mlx5_flow_destination { - enum mlx5_flow_destination_type type; - union { - u32 tir_num; - void *ft; - u32 vport_num; - }; -}; - -void *mlx5_create_flow_table(struct mlx5_core_dev *dev, u8 level, u8 table_type, - u16 num_groups, - struct mlx5_flow_table_group *group); -void mlx5_destroy_flow_table(void *flow_table); -int mlx5_add_flow_table_entry(void *flow_table, u8 match_criteria_enable, - void *match_criteria, void *flow_context, - u32 *flow_index); -void mlx5_del_flow_table_entry(void *flow_table, u32 flow_index); -u32 mlx5_get_flow_table_id(void *flow_table); - -#endif /* MLX5_FLOW_TABLE_H */ diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index 16ae5233dc7b..bc7ad019afde 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -33,6 +33,7 @@ #ifndef _MLX5_FS_ #define _MLX5_FS_ +#include #include #define MLX5_FS_DEFAULT_FLOW_TAG 0x0 @@ -43,6 +44,9 @@ enum mlx5_flow_namespace_type { }; struct mlx5_flow_table; +struct mlx5_flow_group; +struct mlx5_flow_rule; +struct mlx5_flow_namespace; struct mlx5_flow_destination { enum mlx5_flow_destination_type type; @@ -52,4 +56,38 @@ struct mlx5_flow_destination { u32 vport_num; }; }; + +struct mlx5_flow_namespace * +mlx5_get_flow_namespace(struct mlx5_core_dev *dev, + enum mlx5_flow_namespace_type type); + +struct mlx5_flow_table * +mlx5_create_flow_table(struct mlx5_flow_namespace *ns, + int prio, + int num_flow_table_entries); +int mlx5_destroy_flow_table(struct mlx5_flow_table *ft); + +/* inbox should be set with the following values: + * start_flow_index + * end_flow_index + * match_criteria_enable + * match_criteria + */ +struct mlx5_flow_group * +mlx5_create_flow_group(struct mlx5_flow_table *ft, u32 *in); +void mlx5_destroy_flow_group(struct mlx5_flow_group *fg); + +/* Single destination per rule. + * Group ID is implied by the match criteria. + */ +struct mlx5_flow_rule * +mlx5_add_flow_rule(struct mlx5_flow_table *ft, + u8 match_criteria_enable, + u32 *match_criteria, + u32 *match_value, + u32 action, + u32 flow_tag, + struct mlx5_flow_destination *dest); +void mlx5_del_flow_rule(struct mlx5_flow_rule *fr); + #endif -- cgit v1.2.3-59-g8ed1b From 7cb21b794baa521a068e5f8640c248b4d1c283ff Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 10 Dec 2015 17:12:45 +0200 Subject: net/mlx5e: Rename en_flow_table.c to en_fs.c Rename en_flow_table.c to en_fs.c in order to be aligned with the new flow steering files. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 +- .../ethernet/mellanox/mlx5/core/en_flow_table.c | 1224 -------------------- drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | 1224 ++++++++++++++++++++ 3 files changed, 1225 insertions(+), 1225 deletions(-) delete mode 100644 drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/en_fs.c (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 11ee062965c5..fe11e967095f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -4,5 +4,5 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \ mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o \ - en_main.o en_flow_table.o en_ethtool.o en_tx.o en_rx.o \ + en_main.o en_fs.o en_ethtool.o en_tx.o en_rx.o \ en_txrx.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c b/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c deleted file mode 100644 index 80d81abc4820..000000000000 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c +++ /dev/null @@ -1,1224 +0,0 @@ -/* - * Copyright (c) 2015, Mellanox Technologies. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include "en.h" - -#define MLX5_SET_CFG(p, f, v) MLX5_SET(create_flow_group_in, p, f, v) - -enum { - MLX5E_FULLMATCH = 0, - MLX5E_ALLMULTI = 1, - MLX5E_PROMISC = 2, -}; - -enum { - MLX5E_UC = 0, - MLX5E_MC_IPV4 = 1, - MLX5E_MC_IPV6 = 2, - MLX5E_MC_OTHER = 3, -}; - -enum { - MLX5E_ACTION_NONE = 0, - MLX5E_ACTION_ADD = 1, - MLX5E_ACTION_DEL = 2, -}; - -struct mlx5e_eth_addr_hash_node { - struct hlist_node hlist; - u8 action; - struct mlx5e_eth_addr_info ai; -}; - -static inline int mlx5e_hash_eth_addr(u8 *addr) -{ - return addr[5]; -} - -static void mlx5e_add_eth_addr_to_hash(struct hlist_head *hash, u8 *addr) -{ - struct mlx5e_eth_addr_hash_node *hn; - int ix = mlx5e_hash_eth_addr(addr); - int found = 0; - - hlist_for_each_entry(hn, &hash[ix], hlist) - if (ether_addr_equal_64bits(hn->ai.addr, addr)) { - found = 1; - break; - } - - if (found) { - hn->action = MLX5E_ACTION_NONE; - return; - } - - hn = kzalloc(sizeof(*hn), GFP_ATOMIC); - if (!hn) - return; - - ether_addr_copy(hn->ai.addr, addr); - hn->action = MLX5E_ACTION_ADD; - - hlist_add_head(&hn->hlist, &hash[ix]); -} - -static void mlx5e_del_eth_addr_from_hash(struct mlx5e_eth_addr_hash_node *hn) -{ - hlist_del(&hn->hlist); - kfree(hn); -} - -static void mlx5e_del_eth_addr_from_flow_table(struct mlx5e_priv *priv, - struct mlx5e_eth_addr_info *ai) -{ - if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP)) - mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_IPSEC_ESP]); - - if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP)) - mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_IPSEC_ESP]); - - if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH)) - mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_IPSEC_AH]); - - if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH)) - mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_IPSEC_AH]); - - if (ai->tt_vec & BIT(MLX5E_TT_IPV6_TCP)) - mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_TCP]); - - if (ai->tt_vec & BIT(MLX5E_TT_IPV4_TCP)) - mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_TCP]); - - if (ai->tt_vec & BIT(MLX5E_TT_IPV6_UDP)) - mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_UDP]); - - if (ai->tt_vec & BIT(MLX5E_TT_IPV4_UDP)) - mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_UDP]); - - if (ai->tt_vec & BIT(MLX5E_TT_IPV6)) - mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6]); - - if (ai->tt_vec & BIT(MLX5E_TT_IPV4)) - mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4]); - - if (ai->tt_vec & BIT(MLX5E_TT_ANY)) - mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_ANY]); -} - -static int mlx5e_get_eth_addr_type(u8 *addr) -{ - if (is_unicast_ether_addr(addr)) - return MLX5E_UC; - - if ((addr[0] == 0x01) && - (addr[1] == 0x00) && - (addr[2] == 0x5e) && - !(addr[3] & 0x80)) - return MLX5E_MC_IPV4; - - if ((addr[0] == 0x33) && - (addr[1] == 0x33)) - return MLX5E_MC_IPV6; - - return MLX5E_MC_OTHER; -} - -static u32 mlx5e_get_tt_vec(struct mlx5e_eth_addr_info *ai, int type) -{ - int eth_addr_type; - u32 ret; - - switch (type) { - case MLX5E_FULLMATCH: - eth_addr_type = mlx5e_get_eth_addr_type(ai->addr); - switch (eth_addr_type) { - case MLX5E_UC: - ret = - BIT(MLX5E_TT_IPV4_TCP) | - BIT(MLX5E_TT_IPV6_TCP) | - BIT(MLX5E_TT_IPV4_UDP) | - BIT(MLX5E_TT_IPV6_UDP) | - BIT(MLX5E_TT_IPV4_IPSEC_AH) | - BIT(MLX5E_TT_IPV6_IPSEC_AH) | - BIT(MLX5E_TT_IPV4_IPSEC_ESP) | - BIT(MLX5E_TT_IPV6_IPSEC_ESP) | - BIT(MLX5E_TT_IPV4) | - BIT(MLX5E_TT_IPV6) | - BIT(MLX5E_TT_ANY) | - 0; - break; - - case MLX5E_MC_IPV4: - ret = - BIT(MLX5E_TT_IPV4_UDP) | - BIT(MLX5E_TT_IPV4) | - 0; - break; - - case MLX5E_MC_IPV6: - ret = - BIT(MLX5E_TT_IPV6_UDP) | - BIT(MLX5E_TT_IPV6) | - 0; - break; - - case MLX5E_MC_OTHER: - ret = - BIT(MLX5E_TT_ANY) | - 0; - break; - } - - break; - - case MLX5E_ALLMULTI: - ret = - BIT(MLX5E_TT_IPV4_UDP) | - BIT(MLX5E_TT_IPV6_UDP) | - BIT(MLX5E_TT_IPV4) | - BIT(MLX5E_TT_IPV6) | - BIT(MLX5E_TT_ANY) | - 0; - break; - - default: /* MLX5E_PROMISC */ - ret = - BIT(MLX5E_TT_IPV4_TCP) | - BIT(MLX5E_TT_IPV6_TCP) | - BIT(MLX5E_TT_IPV4_UDP) | - BIT(MLX5E_TT_IPV6_UDP) | - BIT(MLX5E_TT_IPV4_IPSEC_AH) | - BIT(MLX5E_TT_IPV6_IPSEC_AH) | - BIT(MLX5E_TT_IPV4_IPSEC_ESP) | - BIT(MLX5E_TT_IPV6_IPSEC_ESP) | - BIT(MLX5E_TT_IPV4) | - BIT(MLX5E_TT_IPV6) | - BIT(MLX5E_TT_ANY) | - 0; - break; - } - - return ret; -} - -static int __mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv, - struct mlx5e_eth_addr_info *ai, - int type, u32 *mc, u32 *mv) -{ - struct mlx5_flow_destination dest; - u8 match_criteria_enable = 0; - struct mlx5_flow_rule **rule_p; - struct mlx5_flow_table *ft = priv->fts.main.t; - u8 *mc_dmac = MLX5_ADDR_OF(fte_match_param, mc, - outer_headers.dmac_47_16); - u8 *mv_dmac = MLX5_ADDR_OF(fte_match_param, mv, - outer_headers.dmac_47_16); - u32 *tirn = priv->tirn; - u32 tt_vec; - int err = 0; - - dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; - - switch (type) { - case MLX5E_FULLMATCH: - match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - eth_broadcast_addr(mc_dmac); - ether_addr_copy(mv_dmac, ai->addr); - break; - - case MLX5E_ALLMULTI: - match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - mc_dmac[0] = 0x01; - mv_dmac[0] = 0x01; - break; - - case MLX5E_PROMISC: - break; - } - - tt_vec = mlx5e_get_tt_vec(ai, type); - - if (tt_vec & BIT(MLX5E_TT_ANY)) { - rule_p = &ai->ft_rule[MLX5E_TT_ANY]; - dest.tir_num = tirn[MLX5E_TT_ANY]; - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); - if (IS_ERR_OR_NULL(*rule_p)) - goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_ANY); - } - - match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); - - if (tt_vec & BIT(MLX5E_TT_IPV4)) { - rule_p = &ai->ft_rule[MLX5E_TT_IPV4]; - dest.tir_num = tirn[MLX5E_TT_IPV4]; - MLX5_SET(fte_match_param, mv, outer_headers.ethertype, - ETH_P_IP); - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); - if (IS_ERR_OR_NULL(*rule_p)) - goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV4); - } - - if (tt_vec & BIT(MLX5E_TT_IPV6)) { - rule_p = &ai->ft_rule[MLX5E_TT_IPV6]; - dest.tir_num = tirn[MLX5E_TT_IPV6]; - MLX5_SET(fte_match_param, mv, outer_headers.ethertype, - ETH_P_IPV6); - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); - if (IS_ERR_OR_NULL(*rule_p)) - goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV6); - } - - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); - MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_UDP); - - if (tt_vec & BIT(MLX5E_TT_IPV4_UDP)) { - rule_p = &ai->ft_rule[MLX5E_TT_IPV4_UDP]; - dest.tir_num = tirn[MLX5E_TT_IPV4_UDP]; - MLX5_SET(fte_match_param, mv, outer_headers.ethertype, - ETH_P_IP); - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); - if (IS_ERR_OR_NULL(*rule_p)) - goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV4_UDP); - } - - if (tt_vec & BIT(MLX5E_TT_IPV6_UDP)) { - rule_p = &ai->ft_rule[MLX5E_TT_IPV6_UDP]; - dest.tir_num = tirn[MLX5E_TT_IPV6_UDP]; - MLX5_SET(fte_match_param, mv, outer_headers.ethertype, - ETH_P_IPV6); - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); - if (IS_ERR_OR_NULL(*rule_p)) - goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV6_UDP); - } - - MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_TCP); - - if (tt_vec & BIT(MLX5E_TT_IPV4_TCP)) { - rule_p = &ai->ft_rule[MLX5E_TT_IPV4_TCP]; - dest.tir_num = tirn[MLX5E_TT_IPV4_TCP]; - MLX5_SET(fte_match_param, mv, outer_headers.ethertype, - ETH_P_IP); - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); - if (IS_ERR_OR_NULL(*rule_p)) - goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV4_TCP); - } - - if (tt_vec & BIT(MLX5E_TT_IPV6_TCP)) { - rule_p = &ai->ft_rule[MLX5E_TT_IPV6_TCP]; - dest.tir_num = tirn[MLX5E_TT_IPV6_TCP]; - MLX5_SET(fte_match_param, mv, outer_headers.ethertype, - ETH_P_IPV6); - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); - if (IS_ERR_OR_NULL(*rule_p)) - goto err_del_ai; - - ai->tt_vec |= BIT(MLX5E_TT_IPV6_TCP); - } - - MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_AH); - - if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH)) { - rule_p = &ai->ft_rule[MLX5E_TT_IPV4_IPSEC_AH]; - dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_AH]; - MLX5_SET(fte_match_param, mv, outer_headers.ethertype, - ETH_P_IP); - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); - if (IS_ERR_OR_NULL(*rule_p)) - goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_AH); - } - - if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH)) { - rule_p = &ai->ft_rule[MLX5E_TT_IPV6_IPSEC_AH]; - dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_AH]; - MLX5_SET(fte_match_param, mv, outer_headers.ethertype, - ETH_P_IPV6); - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); - if (IS_ERR_OR_NULL(*rule_p)) - goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_AH); - } - - MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_ESP); - - if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP)) { - rule_p = &ai->ft_rule[MLX5E_TT_IPV4_IPSEC_ESP]; - dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_ESP]; - MLX5_SET(fte_match_param, mv, outer_headers.ethertype, - ETH_P_IP); - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); - if (IS_ERR_OR_NULL(*rule_p)) - goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_ESP); - } - - if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP)) { - rule_p = &ai->ft_rule[MLX5E_TT_IPV6_IPSEC_ESP]; - dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_ESP]; - MLX5_SET(fte_match_param, mv, outer_headers.ethertype, - ETH_P_IPV6); - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, &dest); - if (IS_ERR_OR_NULL(*rule_p)) - goto err_del_ai; - ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_ESP); - } - - return 0; - -err_del_ai: - err = PTR_ERR(*rule_p); - *rule_p = NULL; - mlx5e_del_eth_addr_from_flow_table(priv, ai); - - return err; -} - -static int mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv, - struct mlx5e_eth_addr_info *ai, int type) -{ - u32 *match_criteria; - u32 *match_value; - int err = 0; - - match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); - match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); - if (!match_value || !match_criteria) { - netdev_err(priv->netdev, "%s: alloc failed\n", __func__); - err = -ENOMEM; - goto add_eth_addr_rule_out; - } - - err = __mlx5e_add_eth_addr_rule(priv, ai, type, match_criteria, - match_value); - -add_eth_addr_rule_out: - kvfree(match_criteria); - kvfree(match_value); - - return err; -} - -static int mlx5e_vport_context_update_vlans(struct mlx5e_priv *priv) -{ - struct net_device *ndev = priv->netdev; - int max_list_size; - int list_size; - u16 *vlans; - int vlan; - int err; - int i; - - list_size = 0; - for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID) - list_size++; - - max_list_size = 1 << MLX5_CAP_GEN(priv->mdev, log_max_vlan_list); - - if (list_size > max_list_size) { - netdev_warn(ndev, - "netdev vlans list size (%d) > (%d) max vport list size, some vlans will be dropped\n", - list_size, max_list_size); - list_size = max_list_size; - } - - vlans = kcalloc(list_size, sizeof(*vlans), GFP_KERNEL); - if (!vlans) - return -ENOMEM; - - i = 0; - for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID) { - if (i >= list_size) - break; - vlans[i++] = vlan; - } - - err = mlx5_modify_nic_vport_vlans(priv->mdev, vlans, list_size); - if (err) - netdev_err(ndev, "Failed to modify vport vlans list err(%d)\n", - err); - - kfree(vlans); - return err; -} - -enum mlx5e_vlan_rule_type { - MLX5E_VLAN_RULE_TYPE_UNTAGGED, - MLX5E_VLAN_RULE_TYPE_ANY_VID, - MLX5E_VLAN_RULE_TYPE_MATCH_VID, -}; - -static int __mlx5e_add_vlan_rule(struct mlx5e_priv *priv, - enum mlx5e_vlan_rule_type rule_type, - u16 vid, u32 *mc, u32 *mv) -{ - struct mlx5_flow_table *ft = priv->fts.vlan.t; - struct mlx5_flow_destination dest; - u8 match_criteria_enable = 0; - struct mlx5_flow_rule **rule_p; - int err = 0; - - dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; - dest.ft = priv->fts.main.t; - - match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag); - - switch (rule_type) { - case MLX5E_VLAN_RULE_TYPE_UNTAGGED: - rule_p = &priv->vlan.untagged_rule; - break; - case MLX5E_VLAN_RULE_TYPE_ANY_VID: - rule_p = &priv->vlan.any_vlan_rule; - MLX5_SET(fte_match_param, mv, outer_headers.vlan_tag, 1); - break; - default: /* MLX5E_VLAN_RULE_TYPE_MATCH_VID */ - rule_p = &priv->vlan.active_vlans_rule[vid]; - MLX5_SET(fte_match_param, mv, outer_headers.vlan_tag, 1); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.first_vid); - MLX5_SET(fte_match_param, mv, outer_headers.first_vid, vid); - break; - } - - *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, - MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - MLX5_FS_DEFAULT_FLOW_TAG, - &dest); - - if (IS_ERR(*rule_p)) { - err = PTR_ERR(*rule_p); - *rule_p = NULL; - netdev_err(priv->netdev, "%s: add rule failed\n", __func__); - } - - return err; -} - -static int mlx5e_add_vlan_rule(struct mlx5e_priv *priv, - enum mlx5e_vlan_rule_type rule_type, u16 vid) -{ - u32 *match_criteria; - u32 *match_value; - int err = 0; - - match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); - match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); - if (!match_value || !match_criteria) { - netdev_err(priv->netdev, "%s: alloc failed\n", __func__); - err = -ENOMEM; - goto add_vlan_rule_out; - } - - if (rule_type == MLX5E_VLAN_RULE_TYPE_MATCH_VID) - mlx5e_vport_context_update_vlans(priv); - - err = __mlx5e_add_vlan_rule(priv, rule_type, vid, match_criteria, - match_value); - -add_vlan_rule_out: - kvfree(match_criteria); - kvfree(match_value); - - return err; -} - -static void mlx5e_del_vlan_rule(struct mlx5e_priv *priv, - enum mlx5e_vlan_rule_type rule_type, u16 vid) -{ - switch (rule_type) { - case MLX5E_VLAN_RULE_TYPE_UNTAGGED: - if (priv->vlan.untagged_rule) { - mlx5_del_flow_rule(priv->vlan.untagged_rule); - priv->vlan.untagged_rule = NULL; - } - break; - case MLX5E_VLAN_RULE_TYPE_ANY_VID: - if (priv->vlan.any_vlan_rule) { - mlx5_del_flow_rule(priv->vlan.any_vlan_rule); - priv->vlan.any_vlan_rule = NULL; - } - break; - case MLX5E_VLAN_RULE_TYPE_MATCH_VID: - mlx5e_vport_context_update_vlans(priv); - if (priv->vlan.active_vlans_rule[vid]) { - mlx5_del_flow_rule(priv->vlan.active_vlans_rule[vid]); - priv->vlan.active_vlans_rule[vid] = NULL; - } - mlx5e_vport_context_update_vlans(priv); - break; - } -} - -void mlx5e_enable_vlan_filter(struct mlx5e_priv *priv) -{ - if (!priv->vlan.filter_disabled) - return; - - priv->vlan.filter_disabled = false; - if (priv->netdev->flags & IFF_PROMISC) - return; - mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0); -} - -void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv) -{ - if (priv->vlan.filter_disabled) - return; - - priv->vlan.filter_disabled = true; - if (priv->netdev->flags & IFF_PROMISC) - return; - mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0); -} - -int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto, - u16 vid) -{ - struct mlx5e_priv *priv = netdev_priv(dev); - - set_bit(vid, priv->vlan.active_vlans); - - return mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid); -} - -int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto, - u16 vid) -{ - struct mlx5e_priv *priv = netdev_priv(dev); - - clear_bit(vid, priv->vlan.active_vlans); - - mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid); - - return 0; -} - -#define mlx5e_for_each_hash_node(hn, tmp, hash, i) \ - for (i = 0; i < MLX5E_ETH_ADDR_HASH_SIZE; i++) \ - hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist) - -static void mlx5e_execute_action(struct mlx5e_priv *priv, - struct mlx5e_eth_addr_hash_node *hn) -{ - switch (hn->action) { - case MLX5E_ACTION_ADD: - mlx5e_add_eth_addr_rule(priv, &hn->ai, MLX5E_FULLMATCH); - hn->action = MLX5E_ACTION_NONE; - break; - - case MLX5E_ACTION_DEL: - mlx5e_del_eth_addr_from_flow_table(priv, &hn->ai); - mlx5e_del_eth_addr_from_hash(hn); - break; - } -} - -static void mlx5e_sync_netdev_addr(struct mlx5e_priv *priv) -{ - struct net_device *netdev = priv->netdev; - struct netdev_hw_addr *ha; - - netif_addr_lock_bh(netdev); - - mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc, - priv->netdev->dev_addr); - - netdev_for_each_uc_addr(ha, netdev) - mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc, ha->addr); - - netdev_for_each_mc_addr(ha, netdev) - mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_mc, ha->addr); - - netif_addr_unlock_bh(netdev); -} - -static void mlx5e_fill_addr_array(struct mlx5e_priv *priv, int list_type, - u8 addr_array[][ETH_ALEN], int size) -{ - bool is_uc = (list_type == MLX5_NVPRT_LIST_TYPE_UC); - struct net_device *ndev = priv->netdev; - struct mlx5e_eth_addr_hash_node *hn; - struct hlist_head *addr_list; - struct hlist_node *tmp; - int i = 0; - int hi; - - addr_list = is_uc ? priv->eth_addr.netdev_uc : priv->eth_addr.netdev_mc; - - if (is_uc) /* Make sure our own address is pushed first */ - ether_addr_copy(addr_array[i++], ndev->dev_addr); - else if (priv->eth_addr.broadcast_enabled) - ether_addr_copy(addr_array[i++], ndev->broadcast); - - mlx5e_for_each_hash_node(hn, tmp, addr_list, hi) { - if (ether_addr_equal(ndev->dev_addr, hn->ai.addr)) - continue; - if (i >= size) - break; - ether_addr_copy(addr_array[i++], hn->ai.addr); - } -} - -static void mlx5e_vport_context_update_addr_list(struct mlx5e_priv *priv, - int list_type) -{ - bool is_uc = (list_type == MLX5_NVPRT_LIST_TYPE_UC); - struct mlx5e_eth_addr_hash_node *hn; - u8 (*addr_array)[ETH_ALEN] = NULL; - struct hlist_head *addr_list; - struct hlist_node *tmp; - int max_size; - int size; - int err; - int hi; - - size = is_uc ? 0 : (priv->eth_addr.broadcast_enabled ? 1 : 0); - max_size = is_uc ? - 1 << MLX5_CAP_GEN(priv->mdev, log_max_current_uc_list) : - 1 << MLX5_CAP_GEN(priv->mdev, log_max_current_mc_list); - - addr_list = is_uc ? priv->eth_addr.netdev_uc : priv->eth_addr.netdev_mc; - mlx5e_for_each_hash_node(hn, tmp, addr_list, hi) - size++; - - if (size > max_size) { - netdev_warn(priv->netdev, - "netdev %s list size (%d) > (%d) max vport list size, some addresses will be dropped\n", - is_uc ? "UC" : "MC", size, max_size); - size = max_size; - } - - if (size) { - addr_array = kcalloc(size, ETH_ALEN, GFP_KERNEL); - if (!addr_array) { - err = -ENOMEM; - goto out; - } - mlx5e_fill_addr_array(priv, list_type, addr_array, size); - } - - err = mlx5_modify_nic_vport_mac_list(priv->mdev, list_type, addr_array, size); -out: - if (err) - netdev_err(priv->netdev, - "Failed to modify vport %s list err(%d)\n", - is_uc ? "UC" : "MC", err); - kfree(addr_array); -} - -static void mlx5e_vport_context_update(struct mlx5e_priv *priv) -{ - struct mlx5e_eth_addr_db *ea = &priv->eth_addr; - - mlx5e_vport_context_update_addr_list(priv, MLX5_NVPRT_LIST_TYPE_UC); - mlx5e_vport_context_update_addr_list(priv, MLX5_NVPRT_LIST_TYPE_MC); - mlx5_modify_nic_vport_promisc(priv->mdev, 0, - ea->allmulti_enabled, - ea->promisc_enabled); -} - -static void mlx5e_apply_netdev_addr(struct mlx5e_priv *priv) -{ - struct mlx5e_eth_addr_hash_node *hn; - struct hlist_node *tmp; - int i; - - mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i) - mlx5e_execute_action(priv, hn); - - mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i) - mlx5e_execute_action(priv, hn); -} - -static void mlx5e_handle_netdev_addr(struct mlx5e_priv *priv) -{ - struct mlx5e_eth_addr_hash_node *hn; - struct hlist_node *tmp; - int i; - - mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i) - hn->action = MLX5E_ACTION_DEL; - mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i) - hn->action = MLX5E_ACTION_DEL; - - if (!test_bit(MLX5E_STATE_DESTROYING, &priv->state)) - mlx5e_sync_netdev_addr(priv); - - mlx5e_apply_netdev_addr(priv); -} - -void mlx5e_set_rx_mode_work(struct work_struct *work) -{ - struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv, - set_rx_mode_work); - - struct mlx5e_eth_addr_db *ea = &priv->eth_addr; - struct net_device *ndev = priv->netdev; - - bool rx_mode_enable = !test_bit(MLX5E_STATE_DESTROYING, &priv->state); - bool promisc_enabled = rx_mode_enable && (ndev->flags & IFF_PROMISC); - bool allmulti_enabled = rx_mode_enable && (ndev->flags & IFF_ALLMULTI); - bool broadcast_enabled = rx_mode_enable; - - bool enable_promisc = !ea->promisc_enabled && promisc_enabled; - bool disable_promisc = ea->promisc_enabled && !promisc_enabled; - bool enable_allmulti = !ea->allmulti_enabled && allmulti_enabled; - bool disable_allmulti = ea->allmulti_enabled && !allmulti_enabled; - bool enable_broadcast = !ea->broadcast_enabled && broadcast_enabled; - bool disable_broadcast = ea->broadcast_enabled && !broadcast_enabled; - - if (enable_promisc) { - mlx5e_add_eth_addr_rule(priv, &ea->promisc, MLX5E_PROMISC); - if (!priv->vlan.filter_disabled) - mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, - 0); - } - if (enable_allmulti) - mlx5e_add_eth_addr_rule(priv, &ea->allmulti, MLX5E_ALLMULTI); - if (enable_broadcast) - mlx5e_add_eth_addr_rule(priv, &ea->broadcast, MLX5E_FULLMATCH); - - mlx5e_handle_netdev_addr(priv); - - if (disable_broadcast) - mlx5e_del_eth_addr_from_flow_table(priv, &ea->broadcast); - if (disable_allmulti) - mlx5e_del_eth_addr_from_flow_table(priv, &ea->allmulti); - if (disable_promisc) { - if (!priv->vlan.filter_disabled) - mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, - 0); - mlx5e_del_eth_addr_from_flow_table(priv, &ea->promisc); - } - - ea->promisc_enabled = promisc_enabled; - ea->allmulti_enabled = allmulti_enabled; - ea->broadcast_enabled = broadcast_enabled; - - mlx5e_vport_context_update(priv); -} - -static void mlx5e_destroy_groups(struct mlx5e_flow_table *ft) -{ - int i; - - for (i = ft->num_groups - 1; i >= 0; i--) { - if (!IS_ERR_OR_NULL(ft->g[i])) - mlx5_destroy_flow_group(ft->g[i]); - ft->g[i] = NULL; - } - ft->num_groups = 0; -} - -void mlx5e_init_eth_addr(struct mlx5e_priv *priv) -{ - ether_addr_copy(priv->eth_addr.broadcast.addr, priv->netdev->broadcast); -} - -#define MLX5E_MAIN_GROUP0_SIZE BIT(3) -#define MLX5E_MAIN_GROUP1_SIZE BIT(1) -#define MLX5E_MAIN_GROUP2_SIZE BIT(0) -#define MLX5E_MAIN_GROUP3_SIZE BIT(14) -#define MLX5E_MAIN_GROUP4_SIZE BIT(13) -#define MLX5E_MAIN_GROUP5_SIZE BIT(11) -#define MLX5E_MAIN_GROUP6_SIZE BIT(2) -#define MLX5E_MAIN_GROUP7_SIZE BIT(1) -#define MLX5E_MAIN_GROUP8_SIZE BIT(0) -#define MLX5E_MAIN_TABLE_SIZE (MLX5E_MAIN_GROUP0_SIZE +\ - MLX5E_MAIN_GROUP1_SIZE +\ - MLX5E_MAIN_GROUP2_SIZE +\ - MLX5E_MAIN_GROUP3_SIZE +\ - MLX5E_MAIN_GROUP4_SIZE +\ - MLX5E_MAIN_GROUP5_SIZE +\ - MLX5E_MAIN_GROUP6_SIZE +\ - MLX5E_MAIN_GROUP7_SIZE +\ - MLX5E_MAIN_GROUP8_SIZE) - -static int __mlx5e_create_main_groups(struct mlx5e_flow_table *ft, u32 *in, - int inlen) -{ - u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); - u8 *dmac = MLX5_ADDR_OF(create_flow_group_in, in, - match_criteria.outer_headers.dmac_47_16); - int err; - int ix = 0; - - memset(in, 0, inlen); - MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); - MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_MAIN_GROUP0_SIZE; - MLX5_SET_CFG(in, end_flow_index, ix - 1); - ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); - if (IS_ERR(ft->g[ft->num_groups])) - goto err_destroy_groups; - ft->num_groups++; - - memset(in, 0, inlen); - MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); - MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_MAIN_GROUP1_SIZE; - MLX5_SET_CFG(in, end_flow_index, ix - 1); - ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); - if (IS_ERR(ft->g[ft->num_groups])) - goto err_destroy_groups; - ft->num_groups++; - - memset(in, 0, inlen); - MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_MAIN_GROUP2_SIZE; - MLX5_SET_CFG(in, end_flow_index, ix - 1); - ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); - if (IS_ERR(ft->g[ft->num_groups])) - goto err_destroy_groups; - ft->num_groups++; - - memset(in, 0, inlen); - MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); - eth_broadcast_addr(dmac); - MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_MAIN_GROUP3_SIZE; - MLX5_SET_CFG(in, end_flow_index, ix - 1); - ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); - if (IS_ERR(ft->g[ft->num_groups])) - goto err_destroy_groups; - ft->num_groups++; - - memset(in, 0, inlen); - MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); - eth_broadcast_addr(dmac); - MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_MAIN_GROUP4_SIZE; - MLX5_SET_CFG(in, end_flow_index, ix - 1); - ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); - if (IS_ERR(ft->g[ft->num_groups])) - goto err_destroy_groups; - ft->num_groups++; - - memset(in, 0, inlen); - MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); - eth_broadcast_addr(dmac); - MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_MAIN_GROUP5_SIZE; - MLX5_SET_CFG(in, end_flow_index, ix - 1); - ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); - if (IS_ERR(ft->g[ft->num_groups])) - goto err_destroy_groups; - ft->num_groups++; - - memset(in, 0, inlen); - MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); - dmac[0] = 0x01; - MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_MAIN_GROUP6_SIZE; - MLX5_SET_CFG(in, end_flow_index, ix - 1); - ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); - if (IS_ERR(ft->g[ft->num_groups])) - goto err_destroy_groups; - ft->num_groups++; - - memset(in, 0, inlen); - MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); - dmac[0] = 0x01; - MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_MAIN_GROUP7_SIZE; - MLX5_SET_CFG(in, end_flow_index, ix - 1); - ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); - if (IS_ERR(ft->g[ft->num_groups])) - goto err_destroy_groups; - ft->num_groups++; - - memset(in, 0, inlen); - MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); - dmac[0] = 0x01; - MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_MAIN_GROUP8_SIZE; - MLX5_SET_CFG(in, end_flow_index, ix - 1); - ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); - if (IS_ERR(ft->g[ft->num_groups])) - goto err_destroy_groups; - ft->num_groups++; - - return 0; - -err_destroy_groups: - err = PTR_ERR(ft->g[ft->num_groups]); - ft->g[ft->num_groups] = NULL; - mlx5e_destroy_groups(ft); - - return err; -} - -static int mlx5e_create_main_groups(struct mlx5e_flow_table *ft) -{ - u32 *in; - int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); - int err; - - in = mlx5_vzalloc(inlen); - if (!in) - return -ENOMEM; - - err = __mlx5e_create_main_groups(ft, in, inlen); - - kvfree(in); - return err; -} - -static int mlx5e_create_main_flow_table(struct mlx5e_priv *priv) -{ - struct mlx5e_flow_table *ft = &priv->fts.main; - int err; - - ft->num_groups = 0; - ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_MAIN_TABLE_SIZE); - - if (IS_ERR(ft->t)) { - err = PTR_ERR(ft->t); - ft->t = NULL; - return err; - } - ft->g = kcalloc(MLX5E_NUM_MAIN_GROUPS, sizeof(*ft->g), GFP_KERNEL); - if (!ft->g) { - err = -ENOMEM; - goto err_destroy_main_flow_table; - } - - err = mlx5e_create_main_groups(ft); - if (err) - goto err_free_g; - return 0; - -err_free_g: - kfree(ft->g); - -err_destroy_main_flow_table: - mlx5_destroy_flow_table(ft->t); - ft->t = NULL; - - return err; -} - -static void mlx5e_destroy_flow_table(struct mlx5e_flow_table *ft) -{ - mlx5e_destroy_groups(ft); - kfree(ft->g); - mlx5_destroy_flow_table(ft->t); - ft->t = NULL; -} - -static void mlx5e_destroy_main_flow_table(struct mlx5e_priv *priv) -{ - mlx5e_destroy_flow_table(&priv->fts.main); -} - -#define MLX5E_NUM_VLAN_GROUPS 2 -#define MLX5E_VLAN_GROUP0_SIZE BIT(12) -#define MLX5E_VLAN_GROUP1_SIZE BIT(1) -#define MLX5E_VLAN_TABLE_SIZE (MLX5E_VLAN_GROUP0_SIZE +\ - MLX5E_VLAN_GROUP1_SIZE) - -static int __mlx5e_create_vlan_groups(struct mlx5e_flow_table *ft, u32 *in, - int inlen) -{ - int err; - int ix = 0; - u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); - - memset(in, 0, inlen); - MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.first_vid); - MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_VLAN_GROUP0_SIZE; - MLX5_SET_CFG(in, end_flow_index, ix - 1); - ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); - if (IS_ERR(ft->g[ft->num_groups])) - goto err_destroy_groups; - ft->num_groups++; - - memset(in, 0, inlen); - MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag); - MLX5_SET_CFG(in, start_flow_index, ix); - ix += MLX5E_VLAN_GROUP1_SIZE; - MLX5_SET_CFG(in, end_flow_index, ix - 1); - ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); - if (IS_ERR(ft->g[ft->num_groups])) - goto err_destroy_groups; - ft->num_groups++; - - return 0; - -err_destroy_groups: - err = PTR_ERR(ft->g[ft->num_groups]); - ft->g[ft->num_groups] = NULL; - mlx5e_destroy_groups(ft); - - return err; -} - -static int mlx5e_create_vlan_groups(struct mlx5e_flow_table *ft) -{ - u32 *in; - int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); - int err; - - in = mlx5_vzalloc(inlen); - if (!in) - return -ENOMEM; - - err = __mlx5e_create_vlan_groups(ft, in, inlen); - - kvfree(in); - return err; -} - -static int mlx5e_create_vlan_flow_table(struct mlx5e_priv *priv) -{ - struct mlx5e_flow_table *ft = &priv->fts.vlan; - int err; - - ft->num_groups = 0; - ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_VLAN_TABLE_SIZE); - - if (IS_ERR(ft->t)) { - err = PTR_ERR(ft->t); - ft->t = NULL; - return err; - } - ft->g = kcalloc(MLX5E_NUM_VLAN_GROUPS, sizeof(*ft->g), GFP_KERNEL); - if (!ft->g) { - err = -ENOMEM; - goto err_destroy_vlan_flow_table; - } - - err = mlx5e_create_vlan_groups(ft); - if (err) - goto err_free_g; - - return 0; - -err_free_g: - kfree(ft->g); - -err_destroy_vlan_flow_table: - mlx5_destroy_flow_table(ft->t); - ft->t = NULL; - - return err; -} - -static void mlx5e_destroy_vlan_flow_table(struct mlx5e_priv *priv) -{ - mlx5e_destroy_flow_table(&priv->fts.vlan); -} - -int mlx5e_create_flow_tables(struct mlx5e_priv *priv) -{ - int err; - - priv->fts.ns = mlx5_get_flow_namespace(priv->mdev, - MLX5_FLOW_NAMESPACE_KERNEL); - - if (!priv->fts.ns) - return -EINVAL; - - err = mlx5e_create_vlan_flow_table(priv); - if (err) - return err; - - err = mlx5e_create_main_flow_table(priv); - if (err) - goto err_destroy_vlan_flow_table; - - err = mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0); - if (err) - goto err_destroy_main_flow_table; - - return 0; - -err_destroy_main_flow_table: - mlx5e_destroy_main_flow_table(priv); -err_destroy_vlan_flow_table: - mlx5e_destroy_vlan_flow_table(priv); - - return err; -} - -void mlx5e_destroy_flow_tables(struct mlx5e_priv *priv) -{ - mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0); - mlx5e_destroy_main_flow_table(priv); - mlx5e_destroy_vlan_flow_table(priv); -} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c new file mode 100644 index 000000000000..80d81abc4820 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -0,0 +1,1224 @@ +/* + * Copyright (c) 2015, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "en.h" + +#define MLX5_SET_CFG(p, f, v) MLX5_SET(create_flow_group_in, p, f, v) + +enum { + MLX5E_FULLMATCH = 0, + MLX5E_ALLMULTI = 1, + MLX5E_PROMISC = 2, +}; + +enum { + MLX5E_UC = 0, + MLX5E_MC_IPV4 = 1, + MLX5E_MC_IPV6 = 2, + MLX5E_MC_OTHER = 3, +}; + +enum { + MLX5E_ACTION_NONE = 0, + MLX5E_ACTION_ADD = 1, + MLX5E_ACTION_DEL = 2, +}; + +struct mlx5e_eth_addr_hash_node { + struct hlist_node hlist; + u8 action; + struct mlx5e_eth_addr_info ai; +}; + +static inline int mlx5e_hash_eth_addr(u8 *addr) +{ + return addr[5]; +} + +static void mlx5e_add_eth_addr_to_hash(struct hlist_head *hash, u8 *addr) +{ + struct mlx5e_eth_addr_hash_node *hn; + int ix = mlx5e_hash_eth_addr(addr); + int found = 0; + + hlist_for_each_entry(hn, &hash[ix], hlist) + if (ether_addr_equal_64bits(hn->ai.addr, addr)) { + found = 1; + break; + } + + if (found) { + hn->action = MLX5E_ACTION_NONE; + return; + } + + hn = kzalloc(sizeof(*hn), GFP_ATOMIC); + if (!hn) + return; + + ether_addr_copy(hn->ai.addr, addr); + hn->action = MLX5E_ACTION_ADD; + + hlist_add_head(&hn->hlist, &hash[ix]); +} + +static void mlx5e_del_eth_addr_from_hash(struct mlx5e_eth_addr_hash_node *hn) +{ + hlist_del(&hn->hlist); + kfree(hn); +} + +static void mlx5e_del_eth_addr_from_flow_table(struct mlx5e_priv *priv, + struct mlx5e_eth_addr_info *ai) +{ + if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP)) + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_IPSEC_ESP]); + + if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP)) + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_IPSEC_ESP]); + + if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH)) + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_IPSEC_AH]); + + if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH)) + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_IPSEC_AH]); + + if (ai->tt_vec & BIT(MLX5E_TT_IPV6_TCP)) + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_TCP]); + + if (ai->tt_vec & BIT(MLX5E_TT_IPV4_TCP)) + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_TCP]); + + if (ai->tt_vec & BIT(MLX5E_TT_IPV6_UDP)) + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_UDP]); + + if (ai->tt_vec & BIT(MLX5E_TT_IPV4_UDP)) + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_UDP]); + + if (ai->tt_vec & BIT(MLX5E_TT_IPV6)) + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6]); + + if (ai->tt_vec & BIT(MLX5E_TT_IPV4)) + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4]); + + if (ai->tt_vec & BIT(MLX5E_TT_ANY)) + mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_ANY]); +} + +static int mlx5e_get_eth_addr_type(u8 *addr) +{ + if (is_unicast_ether_addr(addr)) + return MLX5E_UC; + + if ((addr[0] == 0x01) && + (addr[1] == 0x00) && + (addr[2] == 0x5e) && + !(addr[3] & 0x80)) + return MLX5E_MC_IPV4; + + if ((addr[0] == 0x33) && + (addr[1] == 0x33)) + return MLX5E_MC_IPV6; + + return MLX5E_MC_OTHER; +} + +static u32 mlx5e_get_tt_vec(struct mlx5e_eth_addr_info *ai, int type) +{ + int eth_addr_type; + u32 ret; + + switch (type) { + case MLX5E_FULLMATCH: + eth_addr_type = mlx5e_get_eth_addr_type(ai->addr); + switch (eth_addr_type) { + case MLX5E_UC: + ret = + BIT(MLX5E_TT_IPV4_TCP) | + BIT(MLX5E_TT_IPV6_TCP) | + BIT(MLX5E_TT_IPV4_UDP) | + BIT(MLX5E_TT_IPV6_UDP) | + BIT(MLX5E_TT_IPV4_IPSEC_AH) | + BIT(MLX5E_TT_IPV6_IPSEC_AH) | + BIT(MLX5E_TT_IPV4_IPSEC_ESP) | + BIT(MLX5E_TT_IPV6_IPSEC_ESP) | + BIT(MLX5E_TT_IPV4) | + BIT(MLX5E_TT_IPV6) | + BIT(MLX5E_TT_ANY) | + 0; + break; + + case MLX5E_MC_IPV4: + ret = + BIT(MLX5E_TT_IPV4_UDP) | + BIT(MLX5E_TT_IPV4) | + 0; + break; + + case MLX5E_MC_IPV6: + ret = + BIT(MLX5E_TT_IPV6_UDP) | + BIT(MLX5E_TT_IPV6) | + 0; + break; + + case MLX5E_MC_OTHER: + ret = + BIT(MLX5E_TT_ANY) | + 0; + break; + } + + break; + + case MLX5E_ALLMULTI: + ret = + BIT(MLX5E_TT_IPV4_UDP) | + BIT(MLX5E_TT_IPV6_UDP) | + BIT(MLX5E_TT_IPV4) | + BIT(MLX5E_TT_IPV6) | + BIT(MLX5E_TT_ANY) | + 0; + break; + + default: /* MLX5E_PROMISC */ + ret = + BIT(MLX5E_TT_IPV4_TCP) | + BIT(MLX5E_TT_IPV6_TCP) | + BIT(MLX5E_TT_IPV4_UDP) | + BIT(MLX5E_TT_IPV6_UDP) | + BIT(MLX5E_TT_IPV4_IPSEC_AH) | + BIT(MLX5E_TT_IPV6_IPSEC_AH) | + BIT(MLX5E_TT_IPV4_IPSEC_ESP) | + BIT(MLX5E_TT_IPV6_IPSEC_ESP) | + BIT(MLX5E_TT_IPV4) | + BIT(MLX5E_TT_IPV6) | + BIT(MLX5E_TT_ANY) | + 0; + break; + } + + return ret; +} + +static int __mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv, + struct mlx5e_eth_addr_info *ai, + int type, u32 *mc, u32 *mv) +{ + struct mlx5_flow_destination dest; + u8 match_criteria_enable = 0; + struct mlx5_flow_rule **rule_p; + struct mlx5_flow_table *ft = priv->fts.main.t; + u8 *mc_dmac = MLX5_ADDR_OF(fte_match_param, mc, + outer_headers.dmac_47_16); + u8 *mv_dmac = MLX5_ADDR_OF(fte_match_param, mv, + outer_headers.dmac_47_16); + u32 *tirn = priv->tirn; + u32 tt_vec; + int err = 0; + + dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; + + switch (type) { + case MLX5E_FULLMATCH: + match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + eth_broadcast_addr(mc_dmac); + ether_addr_copy(mv_dmac, ai->addr); + break; + + case MLX5E_ALLMULTI: + match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + mc_dmac[0] = 0x01; + mv_dmac[0] = 0x01; + break; + + case MLX5E_PROMISC: + break; + } + + tt_vec = mlx5e_get_tt_vec(ai, type); + + if (tt_vec & BIT(MLX5E_TT_ANY)) { + rule_p = &ai->ft_rule[MLX5E_TT_ANY]; + dest.tir_num = tirn[MLX5E_TT_ANY]; + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + ai->tt_vec |= BIT(MLX5E_TT_ANY); + } + + match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + + if (tt_vec & BIT(MLX5E_TT_IPV4)) { + rule_p = &ai->ft_rule[MLX5E_TT_IPV4]; + dest.tir_num = tirn[MLX5E_TT_IPV4]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, + ETH_P_IP); + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + ai->tt_vec |= BIT(MLX5E_TT_IPV4); + } + + if (tt_vec & BIT(MLX5E_TT_IPV6)) { + rule_p = &ai->ft_rule[MLX5E_TT_IPV6]; + dest.tir_num = tirn[MLX5E_TT_IPV6]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, + ETH_P_IPV6); + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + ai->tt_vec |= BIT(MLX5E_TT_IPV6); + } + + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); + MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_UDP); + + if (tt_vec & BIT(MLX5E_TT_IPV4_UDP)) { + rule_p = &ai->ft_rule[MLX5E_TT_IPV4_UDP]; + dest.tir_num = tirn[MLX5E_TT_IPV4_UDP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, + ETH_P_IP); + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + ai->tt_vec |= BIT(MLX5E_TT_IPV4_UDP); + } + + if (tt_vec & BIT(MLX5E_TT_IPV6_UDP)) { + rule_p = &ai->ft_rule[MLX5E_TT_IPV6_UDP]; + dest.tir_num = tirn[MLX5E_TT_IPV6_UDP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, + ETH_P_IPV6); + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + ai->tt_vec |= BIT(MLX5E_TT_IPV6_UDP); + } + + MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_TCP); + + if (tt_vec & BIT(MLX5E_TT_IPV4_TCP)) { + rule_p = &ai->ft_rule[MLX5E_TT_IPV4_TCP]; + dest.tir_num = tirn[MLX5E_TT_IPV4_TCP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, + ETH_P_IP); + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + ai->tt_vec |= BIT(MLX5E_TT_IPV4_TCP); + } + + if (tt_vec & BIT(MLX5E_TT_IPV6_TCP)) { + rule_p = &ai->ft_rule[MLX5E_TT_IPV6_TCP]; + dest.tir_num = tirn[MLX5E_TT_IPV6_TCP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, + ETH_P_IPV6); + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + + ai->tt_vec |= BIT(MLX5E_TT_IPV6_TCP); + } + + MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_AH); + + if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH)) { + rule_p = &ai->ft_rule[MLX5E_TT_IPV4_IPSEC_AH]; + dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_AH]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, + ETH_P_IP); + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_AH); + } + + if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH)) { + rule_p = &ai->ft_rule[MLX5E_TT_IPV6_IPSEC_AH]; + dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_AH]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, + ETH_P_IPV6); + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_AH); + } + + MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_ESP); + + if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP)) { + rule_p = &ai->ft_rule[MLX5E_TT_IPV4_IPSEC_ESP]; + dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_ESP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, + ETH_P_IP); + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_ESP); + } + + if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP)) { + rule_p = &ai->ft_rule[MLX5E_TT_IPV6_IPSEC_ESP]; + dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_ESP]; + MLX5_SET(fte_match_param, mv, outer_headers.ethertype, + ETH_P_IPV6); + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, &dest); + if (IS_ERR_OR_NULL(*rule_p)) + goto err_del_ai; + ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_ESP); + } + + return 0; + +err_del_ai: + err = PTR_ERR(*rule_p); + *rule_p = NULL; + mlx5e_del_eth_addr_from_flow_table(priv, ai); + + return err; +} + +static int mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv, + struct mlx5e_eth_addr_info *ai, int type) +{ + u32 *match_criteria; + u32 *match_value; + int err = 0; + + match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + if (!match_value || !match_criteria) { + netdev_err(priv->netdev, "%s: alloc failed\n", __func__); + err = -ENOMEM; + goto add_eth_addr_rule_out; + } + + err = __mlx5e_add_eth_addr_rule(priv, ai, type, match_criteria, + match_value); + +add_eth_addr_rule_out: + kvfree(match_criteria); + kvfree(match_value); + + return err; +} + +static int mlx5e_vport_context_update_vlans(struct mlx5e_priv *priv) +{ + struct net_device *ndev = priv->netdev; + int max_list_size; + int list_size; + u16 *vlans; + int vlan; + int err; + int i; + + list_size = 0; + for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID) + list_size++; + + max_list_size = 1 << MLX5_CAP_GEN(priv->mdev, log_max_vlan_list); + + if (list_size > max_list_size) { + netdev_warn(ndev, + "netdev vlans list size (%d) > (%d) max vport list size, some vlans will be dropped\n", + list_size, max_list_size); + list_size = max_list_size; + } + + vlans = kcalloc(list_size, sizeof(*vlans), GFP_KERNEL); + if (!vlans) + return -ENOMEM; + + i = 0; + for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID) { + if (i >= list_size) + break; + vlans[i++] = vlan; + } + + err = mlx5_modify_nic_vport_vlans(priv->mdev, vlans, list_size); + if (err) + netdev_err(ndev, "Failed to modify vport vlans list err(%d)\n", + err); + + kfree(vlans); + return err; +} + +enum mlx5e_vlan_rule_type { + MLX5E_VLAN_RULE_TYPE_UNTAGGED, + MLX5E_VLAN_RULE_TYPE_ANY_VID, + MLX5E_VLAN_RULE_TYPE_MATCH_VID, +}; + +static int __mlx5e_add_vlan_rule(struct mlx5e_priv *priv, + enum mlx5e_vlan_rule_type rule_type, + u16 vid, u32 *mc, u32 *mv) +{ + struct mlx5_flow_table *ft = priv->fts.vlan.t; + struct mlx5_flow_destination dest; + u8 match_criteria_enable = 0; + struct mlx5_flow_rule **rule_p; + int err = 0; + + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = priv->fts.main.t; + + match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag); + + switch (rule_type) { + case MLX5E_VLAN_RULE_TYPE_UNTAGGED: + rule_p = &priv->vlan.untagged_rule; + break; + case MLX5E_VLAN_RULE_TYPE_ANY_VID: + rule_p = &priv->vlan.any_vlan_rule; + MLX5_SET(fte_match_param, mv, outer_headers.vlan_tag, 1); + break; + default: /* MLX5E_VLAN_RULE_TYPE_MATCH_VID */ + rule_p = &priv->vlan.active_vlans_rule[vid]; + MLX5_SET(fte_match_param, mv, outer_headers.vlan_tag, 1); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.first_vid); + MLX5_SET(fte_match_param, mv, outer_headers.first_vid, vid); + break; + } + + *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, + &dest); + + if (IS_ERR(*rule_p)) { + err = PTR_ERR(*rule_p); + *rule_p = NULL; + netdev_err(priv->netdev, "%s: add rule failed\n", __func__); + } + + return err; +} + +static int mlx5e_add_vlan_rule(struct mlx5e_priv *priv, + enum mlx5e_vlan_rule_type rule_type, u16 vid) +{ + u32 *match_criteria; + u32 *match_value; + int err = 0; + + match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param)); + if (!match_value || !match_criteria) { + netdev_err(priv->netdev, "%s: alloc failed\n", __func__); + err = -ENOMEM; + goto add_vlan_rule_out; + } + + if (rule_type == MLX5E_VLAN_RULE_TYPE_MATCH_VID) + mlx5e_vport_context_update_vlans(priv); + + err = __mlx5e_add_vlan_rule(priv, rule_type, vid, match_criteria, + match_value); + +add_vlan_rule_out: + kvfree(match_criteria); + kvfree(match_value); + + return err; +} + +static void mlx5e_del_vlan_rule(struct mlx5e_priv *priv, + enum mlx5e_vlan_rule_type rule_type, u16 vid) +{ + switch (rule_type) { + case MLX5E_VLAN_RULE_TYPE_UNTAGGED: + if (priv->vlan.untagged_rule) { + mlx5_del_flow_rule(priv->vlan.untagged_rule); + priv->vlan.untagged_rule = NULL; + } + break; + case MLX5E_VLAN_RULE_TYPE_ANY_VID: + if (priv->vlan.any_vlan_rule) { + mlx5_del_flow_rule(priv->vlan.any_vlan_rule); + priv->vlan.any_vlan_rule = NULL; + } + break; + case MLX5E_VLAN_RULE_TYPE_MATCH_VID: + mlx5e_vport_context_update_vlans(priv); + if (priv->vlan.active_vlans_rule[vid]) { + mlx5_del_flow_rule(priv->vlan.active_vlans_rule[vid]); + priv->vlan.active_vlans_rule[vid] = NULL; + } + mlx5e_vport_context_update_vlans(priv); + break; + } +} + +void mlx5e_enable_vlan_filter(struct mlx5e_priv *priv) +{ + if (!priv->vlan.filter_disabled) + return; + + priv->vlan.filter_disabled = false; + if (priv->netdev->flags & IFF_PROMISC) + return; + mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0); +} + +void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv) +{ + if (priv->vlan.filter_disabled) + return; + + priv->vlan.filter_disabled = true; + if (priv->netdev->flags & IFF_PROMISC) + return; + mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0); +} + +int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto, + u16 vid) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + + set_bit(vid, priv->vlan.active_vlans); + + return mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid); +} + +int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto, + u16 vid) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + + clear_bit(vid, priv->vlan.active_vlans); + + mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid); + + return 0; +} + +#define mlx5e_for_each_hash_node(hn, tmp, hash, i) \ + for (i = 0; i < MLX5E_ETH_ADDR_HASH_SIZE; i++) \ + hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist) + +static void mlx5e_execute_action(struct mlx5e_priv *priv, + struct mlx5e_eth_addr_hash_node *hn) +{ + switch (hn->action) { + case MLX5E_ACTION_ADD: + mlx5e_add_eth_addr_rule(priv, &hn->ai, MLX5E_FULLMATCH); + hn->action = MLX5E_ACTION_NONE; + break; + + case MLX5E_ACTION_DEL: + mlx5e_del_eth_addr_from_flow_table(priv, &hn->ai); + mlx5e_del_eth_addr_from_hash(hn); + break; + } +} + +static void mlx5e_sync_netdev_addr(struct mlx5e_priv *priv) +{ + struct net_device *netdev = priv->netdev; + struct netdev_hw_addr *ha; + + netif_addr_lock_bh(netdev); + + mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc, + priv->netdev->dev_addr); + + netdev_for_each_uc_addr(ha, netdev) + mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc, ha->addr); + + netdev_for_each_mc_addr(ha, netdev) + mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_mc, ha->addr); + + netif_addr_unlock_bh(netdev); +} + +static void mlx5e_fill_addr_array(struct mlx5e_priv *priv, int list_type, + u8 addr_array[][ETH_ALEN], int size) +{ + bool is_uc = (list_type == MLX5_NVPRT_LIST_TYPE_UC); + struct net_device *ndev = priv->netdev; + struct mlx5e_eth_addr_hash_node *hn; + struct hlist_head *addr_list; + struct hlist_node *tmp; + int i = 0; + int hi; + + addr_list = is_uc ? priv->eth_addr.netdev_uc : priv->eth_addr.netdev_mc; + + if (is_uc) /* Make sure our own address is pushed first */ + ether_addr_copy(addr_array[i++], ndev->dev_addr); + else if (priv->eth_addr.broadcast_enabled) + ether_addr_copy(addr_array[i++], ndev->broadcast); + + mlx5e_for_each_hash_node(hn, tmp, addr_list, hi) { + if (ether_addr_equal(ndev->dev_addr, hn->ai.addr)) + continue; + if (i >= size) + break; + ether_addr_copy(addr_array[i++], hn->ai.addr); + } +} + +static void mlx5e_vport_context_update_addr_list(struct mlx5e_priv *priv, + int list_type) +{ + bool is_uc = (list_type == MLX5_NVPRT_LIST_TYPE_UC); + struct mlx5e_eth_addr_hash_node *hn; + u8 (*addr_array)[ETH_ALEN] = NULL; + struct hlist_head *addr_list; + struct hlist_node *tmp; + int max_size; + int size; + int err; + int hi; + + size = is_uc ? 0 : (priv->eth_addr.broadcast_enabled ? 1 : 0); + max_size = is_uc ? + 1 << MLX5_CAP_GEN(priv->mdev, log_max_current_uc_list) : + 1 << MLX5_CAP_GEN(priv->mdev, log_max_current_mc_list); + + addr_list = is_uc ? priv->eth_addr.netdev_uc : priv->eth_addr.netdev_mc; + mlx5e_for_each_hash_node(hn, tmp, addr_list, hi) + size++; + + if (size > max_size) { + netdev_warn(priv->netdev, + "netdev %s list size (%d) > (%d) max vport list size, some addresses will be dropped\n", + is_uc ? "UC" : "MC", size, max_size); + size = max_size; + } + + if (size) { + addr_array = kcalloc(size, ETH_ALEN, GFP_KERNEL); + if (!addr_array) { + err = -ENOMEM; + goto out; + } + mlx5e_fill_addr_array(priv, list_type, addr_array, size); + } + + err = mlx5_modify_nic_vport_mac_list(priv->mdev, list_type, addr_array, size); +out: + if (err) + netdev_err(priv->netdev, + "Failed to modify vport %s list err(%d)\n", + is_uc ? "UC" : "MC", err); + kfree(addr_array); +} + +static void mlx5e_vport_context_update(struct mlx5e_priv *priv) +{ + struct mlx5e_eth_addr_db *ea = &priv->eth_addr; + + mlx5e_vport_context_update_addr_list(priv, MLX5_NVPRT_LIST_TYPE_UC); + mlx5e_vport_context_update_addr_list(priv, MLX5_NVPRT_LIST_TYPE_MC); + mlx5_modify_nic_vport_promisc(priv->mdev, 0, + ea->allmulti_enabled, + ea->promisc_enabled); +} + +static void mlx5e_apply_netdev_addr(struct mlx5e_priv *priv) +{ + struct mlx5e_eth_addr_hash_node *hn; + struct hlist_node *tmp; + int i; + + mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i) + mlx5e_execute_action(priv, hn); + + mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i) + mlx5e_execute_action(priv, hn); +} + +static void mlx5e_handle_netdev_addr(struct mlx5e_priv *priv) +{ + struct mlx5e_eth_addr_hash_node *hn; + struct hlist_node *tmp; + int i; + + mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i) + hn->action = MLX5E_ACTION_DEL; + mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i) + hn->action = MLX5E_ACTION_DEL; + + if (!test_bit(MLX5E_STATE_DESTROYING, &priv->state)) + mlx5e_sync_netdev_addr(priv); + + mlx5e_apply_netdev_addr(priv); +} + +void mlx5e_set_rx_mode_work(struct work_struct *work) +{ + struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv, + set_rx_mode_work); + + struct mlx5e_eth_addr_db *ea = &priv->eth_addr; + struct net_device *ndev = priv->netdev; + + bool rx_mode_enable = !test_bit(MLX5E_STATE_DESTROYING, &priv->state); + bool promisc_enabled = rx_mode_enable && (ndev->flags & IFF_PROMISC); + bool allmulti_enabled = rx_mode_enable && (ndev->flags & IFF_ALLMULTI); + bool broadcast_enabled = rx_mode_enable; + + bool enable_promisc = !ea->promisc_enabled && promisc_enabled; + bool disable_promisc = ea->promisc_enabled && !promisc_enabled; + bool enable_allmulti = !ea->allmulti_enabled && allmulti_enabled; + bool disable_allmulti = ea->allmulti_enabled && !allmulti_enabled; + bool enable_broadcast = !ea->broadcast_enabled && broadcast_enabled; + bool disable_broadcast = ea->broadcast_enabled && !broadcast_enabled; + + if (enable_promisc) { + mlx5e_add_eth_addr_rule(priv, &ea->promisc, MLX5E_PROMISC); + if (!priv->vlan.filter_disabled) + mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, + 0); + } + if (enable_allmulti) + mlx5e_add_eth_addr_rule(priv, &ea->allmulti, MLX5E_ALLMULTI); + if (enable_broadcast) + mlx5e_add_eth_addr_rule(priv, &ea->broadcast, MLX5E_FULLMATCH); + + mlx5e_handle_netdev_addr(priv); + + if (disable_broadcast) + mlx5e_del_eth_addr_from_flow_table(priv, &ea->broadcast); + if (disable_allmulti) + mlx5e_del_eth_addr_from_flow_table(priv, &ea->allmulti); + if (disable_promisc) { + if (!priv->vlan.filter_disabled) + mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, + 0); + mlx5e_del_eth_addr_from_flow_table(priv, &ea->promisc); + } + + ea->promisc_enabled = promisc_enabled; + ea->allmulti_enabled = allmulti_enabled; + ea->broadcast_enabled = broadcast_enabled; + + mlx5e_vport_context_update(priv); +} + +static void mlx5e_destroy_groups(struct mlx5e_flow_table *ft) +{ + int i; + + for (i = ft->num_groups - 1; i >= 0; i--) { + if (!IS_ERR_OR_NULL(ft->g[i])) + mlx5_destroy_flow_group(ft->g[i]); + ft->g[i] = NULL; + } + ft->num_groups = 0; +} + +void mlx5e_init_eth_addr(struct mlx5e_priv *priv) +{ + ether_addr_copy(priv->eth_addr.broadcast.addr, priv->netdev->broadcast); +} + +#define MLX5E_MAIN_GROUP0_SIZE BIT(3) +#define MLX5E_MAIN_GROUP1_SIZE BIT(1) +#define MLX5E_MAIN_GROUP2_SIZE BIT(0) +#define MLX5E_MAIN_GROUP3_SIZE BIT(14) +#define MLX5E_MAIN_GROUP4_SIZE BIT(13) +#define MLX5E_MAIN_GROUP5_SIZE BIT(11) +#define MLX5E_MAIN_GROUP6_SIZE BIT(2) +#define MLX5E_MAIN_GROUP7_SIZE BIT(1) +#define MLX5E_MAIN_GROUP8_SIZE BIT(0) +#define MLX5E_MAIN_TABLE_SIZE (MLX5E_MAIN_GROUP0_SIZE +\ + MLX5E_MAIN_GROUP1_SIZE +\ + MLX5E_MAIN_GROUP2_SIZE +\ + MLX5E_MAIN_GROUP3_SIZE +\ + MLX5E_MAIN_GROUP4_SIZE +\ + MLX5E_MAIN_GROUP5_SIZE +\ + MLX5E_MAIN_GROUP6_SIZE +\ + MLX5E_MAIN_GROUP7_SIZE +\ + MLX5E_MAIN_GROUP8_SIZE) + +static int __mlx5e_create_main_groups(struct mlx5e_flow_table *ft, u32 *in, + int inlen) +{ + u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); + u8 *dmac = MLX5_ADDR_OF(create_flow_group_in, in, + match_criteria.outer_headers.dmac_47_16); + int err; + int ix = 0; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP0_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP1_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP2_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); + eth_broadcast_addr(dmac); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP3_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + eth_broadcast_addr(dmac); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP4_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + eth_broadcast_addr(dmac); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP5_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); + dmac[0] = 0x01; + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP6_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + dmac[0] = 0x01; + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP7_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + dmac[0] = 0x01; + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_MAIN_GROUP8_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + return 0; + +err_destroy_groups: + err = PTR_ERR(ft->g[ft->num_groups]); + ft->g[ft->num_groups] = NULL; + mlx5e_destroy_groups(ft); + + return err; +} + +static int mlx5e_create_main_groups(struct mlx5e_flow_table *ft) +{ + u32 *in; + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + int err; + + in = mlx5_vzalloc(inlen); + if (!in) + return -ENOMEM; + + err = __mlx5e_create_main_groups(ft, in, inlen); + + kvfree(in); + return err; +} + +static int mlx5e_create_main_flow_table(struct mlx5e_priv *priv) +{ + struct mlx5e_flow_table *ft = &priv->fts.main; + int err; + + ft->num_groups = 0; + ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_MAIN_TABLE_SIZE); + + if (IS_ERR(ft->t)) { + err = PTR_ERR(ft->t); + ft->t = NULL; + return err; + } + ft->g = kcalloc(MLX5E_NUM_MAIN_GROUPS, sizeof(*ft->g), GFP_KERNEL); + if (!ft->g) { + err = -ENOMEM; + goto err_destroy_main_flow_table; + } + + err = mlx5e_create_main_groups(ft); + if (err) + goto err_free_g; + return 0; + +err_free_g: + kfree(ft->g); + +err_destroy_main_flow_table: + mlx5_destroy_flow_table(ft->t); + ft->t = NULL; + + return err; +} + +static void mlx5e_destroy_flow_table(struct mlx5e_flow_table *ft) +{ + mlx5e_destroy_groups(ft); + kfree(ft->g); + mlx5_destroy_flow_table(ft->t); + ft->t = NULL; +} + +static void mlx5e_destroy_main_flow_table(struct mlx5e_priv *priv) +{ + mlx5e_destroy_flow_table(&priv->fts.main); +} + +#define MLX5E_NUM_VLAN_GROUPS 2 +#define MLX5E_VLAN_GROUP0_SIZE BIT(12) +#define MLX5E_VLAN_GROUP1_SIZE BIT(1) +#define MLX5E_VLAN_TABLE_SIZE (MLX5E_VLAN_GROUP0_SIZE +\ + MLX5E_VLAN_GROUP1_SIZE) + +static int __mlx5e_create_vlan_groups(struct mlx5e_flow_table *ft, u32 *in, + int inlen) +{ + int err; + int ix = 0; + u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.first_vid); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_VLAN_GROUP0_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + memset(in, 0, inlen); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_VLAN_GROUP1_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err_destroy_groups; + ft->num_groups++; + + return 0; + +err_destroy_groups: + err = PTR_ERR(ft->g[ft->num_groups]); + ft->g[ft->num_groups] = NULL; + mlx5e_destroy_groups(ft); + + return err; +} + +static int mlx5e_create_vlan_groups(struct mlx5e_flow_table *ft) +{ + u32 *in; + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + int err; + + in = mlx5_vzalloc(inlen); + if (!in) + return -ENOMEM; + + err = __mlx5e_create_vlan_groups(ft, in, inlen); + + kvfree(in); + return err; +} + +static int mlx5e_create_vlan_flow_table(struct mlx5e_priv *priv) +{ + struct mlx5e_flow_table *ft = &priv->fts.vlan; + int err; + + ft->num_groups = 0; + ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_VLAN_TABLE_SIZE); + + if (IS_ERR(ft->t)) { + err = PTR_ERR(ft->t); + ft->t = NULL; + return err; + } + ft->g = kcalloc(MLX5E_NUM_VLAN_GROUPS, sizeof(*ft->g), GFP_KERNEL); + if (!ft->g) { + err = -ENOMEM; + goto err_destroy_vlan_flow_table; + } + + err = mlx5e_create_vlan_groups(ft); + if (err) + goto err_free_g; + + return 0; + +err_free_g: + kfree(ft->g); + +err_destroy_vlan_flow_table: + mlx5_destroy_flow_table(ft->t); + ft->t = NULL; + + return err; +} + +static void mlx5e_destroy_vlan_flow_table(struct mlx5e_priv *priv) +{ + mlx5e_destroy_flow_table(&priv->fts.vlan); +} + +int mlx5e_create_flow_tables(struct mlx5e_priv *priv) +{ + int err; + + priv->fts.ns = mlx5_get_flow_namespace(priv->mdev, + MLX5_FLOW_NAMESPACE_KERNEL); + + if (!priv->fts.ns) + return -EINVAL; + + err = mlx5e_create_vlan_flow_table(priv); + if (err) + return err; + + err = mlx5e_create_main_flow_table(priv); + if (err) + goto err_destroy_vlan_flow_table; + + err = mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0); + if (err) + goto err_destroy_main_flow_table; + + return 0; + +err_destroy_main_flow_table: + mlx5e_destroy_main_flow_table(priv); +err_destroy_vlan_flow_table: + mlx5e_destroy_vlan_flow_table(priv); + + return err; +} + +void mlx5e_destroy_flow_tables(struct mlx5e_priv *priv) +{ + mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0); + mlx5e_destroy_main_flow_table(priv); + mlx5e_destroy_vlan_flow_table(priv); +} -- cgit v1.2.3-59-g8ed1b From acf35a4ec6f75bcb1c20c7aa02586de6220d76c5 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 11 Dec 2015 16:10:39 +0100 Subject: mlxsw: reg: Fix max temperature getting Fix copy & paste error in MTPM unpack helper. Fixes: 85926f877040 ("mlxsw: reg: Add definition of temperature management registers") Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 4e4e4dcf054f..af631df4603a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -2684,7 +2684,7 @@ static inline void mlxsw_reg_mtmp_unpack(char *payload, unsigned int *p_temp, *p_temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp); } if (p_max_temp) { - temp = mlxsw_reg_mtmp_temperature_get(payload); + temp = mlxsw_reg_mtmp_max_temperature_get(payload); *p_max_temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp); } if (sensor_name) -- cgit v1.2.3-59-g8ed1b From b626f2cb75cfc9ab707fce6e60df0d94650a4abf Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 11 Dec 2015 18:26:00 +0100 Subject: mlxsw: core: Fix temperature sensor index during initialization Sensor index should be passed instead of 0. For now, this does not make a difference, since there is so far only one temperature sensor exposed by HW. Fixes: 89309da39 ("mlxsw: core: Implement temperature hwmon interface") Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c index 913106d37bb0..b86db967eab9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c @@ -242,7 +242,7 @@ static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon) } sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl); for (i = 0; i < sensor_count; i++) { - mlxsw_reg_mtmp_pack(mtmp_pl, 0, true, true); + mlxsw_reg_mtmp_pack(mtmp_pl, i, true, true); err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl); if (err) { -- cgit v1.2.3-59-g8ed1b From 530fd82a9fea5bba8e044bdf6fdf2ddc495e3807 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 2 Nov 2015 17:09:29 -0800 Subject: ixgbe: Return error on failure to allocate mac_table Add a check to make certain mac_table was actually allocated and is not NULL. If it is NULL return -ENOMEM and allow the probe routine to fail rather then causing a NULL pointer dereference further down the line. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index ebd4522e7879..c539b1c71201 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -5381,6 +5381,8 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) adapter->mac_table = kzalloc(sizeof(struct ixgbe_mac_addr) * hw->mac.num_rar_entries, GFP_ATOMIC); + if (!adapter->mac_table) + return -ENOMEM; /* Set MAC specific capability flags and exceptions */ switch (hw->mac.type) { -- cgit v1.2.3-59-g8ed1b From 8e8e9a0b7df0194e95bb1d657f9edbdc6363f082 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 2 Nov 2015 17:09:35 -0800 Subject: ixgbe: Fix SR-IOV VLAN pool configuration The code for checking the PF bit in ixgbe_set_vf_vlan_msg was using the wrong offset and as a result it was pulling the VLAN off of the PF even if there were VFs numbered greater than 40 that still had the VLAN enabled. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 31de6cf7adb0..61a054ace56d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -887,10 +887,10 @@ static int ixgbe_set_vf_vlan_msg(struct ixgbe_adapter *adapter, bits = IXGBE_READ_REG(hw, IXGBE_VLVFB(reg_ndx * 2)); bits &= ~(1 << VMDQ_P(0)); bits |= IXGBE_READ_REG(hw, - IXGBE_VLVFB(reg_ndx * 2) + 1); + IXGBE_VLVFB(reg_ndx * 2 + 1)); } else { bits = IXGBE_READ_REG(hw, - IXGBE_VLVFB(reg_ndx * 2) + 1); + IXGBE_VLVFB(reg_ndx * 2 + 1)); bits &= ~(1 << (VMDQ_P(0) - 32)); bits |= IXGBE_READ_REG(hw, IXGBE_VLVFB(reg_ndx * 2)); } -- cgit v1.2.3-59-g8ed1b From c18fbd5f024e47897a120f42d128c04fa708692c Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 2 Nov 2015 17:09:42 -0800 Subject: ixgbe: Simplify definitions for regidx and bit in set_vfta This patch simplifies the logic for setting the VFTA register by removing the number of conditional checks needed. Instead we just use some boolean logic to generate vfta_delta, and if that is set then we xor the vfta by that value and write it back. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 48 ++++++++++--------------- 1 file changed, 19 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index daec6aef5dc8..027c1ad3e8cb 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -3050,13 +3050,9 @@ static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan) s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on) { - s32 regindex; - u32 bitindex; - u32 vfta; + u32 regidx, vfta_delta, vfta; u32 bits; u32 vt; - u32 targetbit; - bool vfta_changed = false; if (vlan > 4095) return IXGBE_ERR_PARAM; @@ -3073,22 +3069,16 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, * bits[11-5]: which register * bits[4-0]: which bit in the register */ - regindex = (vlan >> 5) & 0x7F; - bitindex = vlan & 0x1F; - targetbit = (1 << bitindex); - vfta = IXGBE_READ_REG(hw, IXGBE_VFTA(regindex)); - - if (vlan_on) { - if (!(vfta & targetbit)) { - vfta |= targetbit; - vfta_changed = true; - } - } else { - if ((vfta & targetbit)) { - vfta &= ~targetbit; - vfta_changed = true; - } - } + regidx = vlan / 32; + vfta_delta = 1 << (vlan % 32); + vfta = IXGBE_READ_REG(hw, IXGBE_VFTA(regidx)); + + /* vfta_delta represents the difference between the current value + * of vfta and the value we want in the register. Since the diff + * is an XOR mask we can just update vfta using an XOR. + */ + vfta_delta &= vlan_on ? ~vfta : vfta; + vfta ^= vfta_delta; /* Part 2 * If VT Mode is set @@ -3164,19 +3154,19 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, if (bits) { IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), (IXGBE_VLVF_VIEN | vlan)); - if (!vlan_on) { - /* someone wants to clear the vfta entry - * but some pools/VFs are still using it. - * Ignore it. */ - vfta_changed = false; - } + + /* if someone wants to clear the vfta entry but + * some pools/VFs are still using it. Ignore it. + */ + if (!vlan_on) + vfta_delta = 0; } else { IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0); } } - if (vfta_changed) - IXGBE_WRITE_REG(hw, IXGBE_VFTA(regindex), vfta); + if (vfta_delta) + IXGBE_WRITE_REG(hw, IXGBE_VFTA(regidx), vfta); return 0; } -- cgit v1.2.3-59-g8ed1b From 63d9379a598ed9fbb887b8679623f8a328ee394e Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 2 Nov 2015 17:09:48 -0800 Subject: ixgbe: Reduce VT code indent in set_vfta by introducing jump label In order to clear the way for upcoming work I thought it best to drop the level of indent in the ixgbe_set_vfta_generic function. Most of the code is held in the virtualization specific section. So the easiest approach is to just add a jump label and jump past the bulk of the code if it is not enabled. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 142 ++++++++++++------------ 1 file changed, 70 insertions(+), 72 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 027c1ad3e8cb..f608973ae73e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -3051,8 +3051,8 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on) { u32 regidx, vfta_delta, vfta; + s32 vlvf_index; u32 bits; - u32 vt; if (vlan > 4095) return IXGBE_ERR_PARAM; @@ -3088,83 +3088,81 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, * Or !vlan_on * clear the pool bit and possibly the vind */ - vt = IXGBE_READ_REG(hw, IXGBE_VT_CTL); - if (vt & IXGBE_VT_CTL_VT_ENABLE) { - s32 vlvf_index; - - vlvf_index = ixgbe_find_vlvf_slot(hw, vlan); - if (vlvf_index < 0) - return vlvf_index; - - if (vlan_on) { - /* set the pool bit */ - if (vind < 32) { - bits = IXGBE_READ_REG(hw, - IXGBE_VLVFB(vlvf_index*2)); - bits |= (1 << vind); - IXGBE_WRITE_REG(hw, - IXGBE_VLVFB(vlvf_index*2), - bits); - } else { - bits = IXGBE_READ_REG(hw, - IXGBE_VLVFB((vlvf_index*2)+1)); - bits |= (1 << (vind-32)); - IXGBE_WRITE_REG(hw, - IXGBE_VLVFB((vlvf_index*2)+1), - bits); - } + if (!(IXGBE_READ_REG(hw, IXGBE_VT_CTL) & IXGBE_VT_CTL_VT_ENABLE)) + goto vfta_update; + + vlvf_index = ixgbe_find_vlvf_slot(hw, vlan); + if (vlvf_index < 0) + return vlvf_index; + + if (vlan_on) { + /* set the pool bit */ + if (vind < 32) { + bits = IXGBE_READ_REG(hw, + IXGBE_VLVFB(vlvf_index*2)); + bits |= (1 << vind); + IXGBE_WRITE_REG(hw, + IXGBE_VLVFB(vlvf_index*2), + bits); } else { - /* clear the pool bit */ - if (vind < 32) { - bits = IXGBE_READ_REG(hw, - IXGBE_VLVFB(vlvf_index*2)); - bits &= ~(1 << vind); - IXGBE_WRITE_REG(hw, - IXGBE_VLVFB(vlvf_index*2), - bits); - bits |= IXGBE_READ_REG(hw, - IXGBE_VLVFB((vlvf_index*2)+1)); - } else { - bits = IXGBE_READ_REG(hw, - IXGBE_VLVFB((vlvf_index*2)+1)); - bits &= ~(1 << (vind-32)); - IXGBE_WRITE_REG(hw, - IXGBE_VLVFB((vlvf_index*2)+1), - bits); - bits |= IXGBE_READ_REG(hw, - IXGBE_VLVFB(vlvf_index*2)); - } + bits = IXGBE_READ_REG(hw, + IXGBE_VLVFB((vlvf_index*2)+1)); + bits |= (1 << (vind-32)); + IXGBE_WRITE_REG(hw, + IXGBE_VLVFB((vlvf_index*2)+1), + bits); } - - /* - * If there are still bits set in the VLVFB registers - * for the VLAN ID indicated we need to see if the - * caller is requesting that we clear the VFTA entry bit. - * If the caller has requested that we clear the VFTA - * entry bit but there are still pools/VFs using this VLAN - * ID entry then ignore the request. We're not worried - * about the case where we're turning the VFTA VLAN ID - * entry bit on, only when requested to turn it off as - * there may be multiple pools and/or VFs using the - * VLAN ID entry. In that case we cannot clear the - * VFTA bit until all pools/VFs using that VLAN ID have also - * been cleared. This will be indicated by "bits" being - * zero. - */ - if (bits) { - IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), - (IXGBE_VLVF_VIEN | vlan)); - - /* if someone wants to clear the vfta entry but - * some pools/VFs are still using it. Ignore it. - */ - if (!vlan_on) - vfta_delta = 0; + } else { + /* clear the pool bit */ + if (vind < 32) { + bits = IXGBE_READ_REG(hw, + IXGBE_VLVFB(vlvf_index*2)); + bits &= ~(1 << vind); + IXGBE_WRITE_REG(hw, + IXGBE_VLVFB(vlvf_index*2), + bits); + bits |= IXGBE_READ_REG(hw, + IXGBE_VLVFB((vlvf_index*2)+1)); } else { - IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0); + bits = IXGBE_READ_REG(hw, + IXGBE_VLVFB((vlvf_index*2)+1)); + bits &= ~(1 << (vind-32)); + IXGBE_WRITE_REG(hw, + IXGBE_VLVFB((vlvf_index*2)+1), + bits); + bits |= IXGBE_READ_REG(hw, + IXGBE_VLVFB(vlvf_index*2)); } } + /* If there are still bits set in the VLVFB registers + * for the VLAN ID indicated we need to see if the + * caller is requesting that we clear the VFTA entry bit. + * If the caller has requested that we clear the VFTA + * entry bit but there are still pools/VFs using this VLAN + * ID entry then ignore the request. We're not worried + * about the case where we're turning the VFTA VLAN ID + * entry bit on, only when requested to turn it off as + * there may be multiple pools and/or VFs using the + * VLAN ID entry. In that case we cannot clear the + * VFTA bit until all pools/VFs using that VLAN ID have also + * been cleared. This will be indicated by "bits" being + * zero. + */ + if (bits) { + IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), + (IXGBE_VLVF_VIEN | vlan)); + + /* if someone wants to clear the vfta entry but + * some pools/VFs are still using it. Ignore it. + */ + if (!vlan_on) + vfta_delta = 0; + } else { + IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0); + } + +vfta_update: if (vfta_delta) IXGBE_WRITE_REG(hw, IXGBE_VFTA(regidx), vfta); -- cgit v1.2.3-59-g8ed1b From 5ac736a65ac131e76edb5bbe75f7f9acef7a8a7b Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 2 Nov 2015 17:09:54 -0800 Subject: ixgbe: Simplify configuration of setting VLVF and VLVFB This patch addresses several issues within the VLVF and VLVFB configuration First was the fact that code was overly complicated with multiple conditional paths depending on if we adding or removing and which bit we were going to add or remove. Instead of messing with all that I have simplified it by using (vid / 32) and (1 - vid / 32) to identify our register and the other vlvfb register. Second was the fact that we were likely leaking a few packets into the PF in cases where we were deleting an entry and the VFTA filter for that entry as the ordering was such that we deleted the pool and then the VLAN filter instead of the other way around. I have updated that by adding a check for no bits being set and if that occurs we clear things up in the proper order. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 88 ++++++++++--------------- 1 file changed, 34 insertions(+), 54 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index f608973ae73e..3f5fe60fdc11 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -3050,11 +3050,10 @@ static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan) s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on) { - u32 regidx, vfta_delta, vfta; + u32 regidx, vfta_delta, vfta, bits; s32 vlvf_index; - u32 bits; - if (vlan > 4095) + if ((vlan > 4095) || (vind > 63)) return IXGBE_ERR_PARAM; /* @@ -3095,44 +3094,30 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, if (vlvf_index < 0) return vlvf_index; - if (vlan_on) { - /* set the pool bit */ - if (vind < 32) { - bits = IXGBE_READ_REG(hw, - IXGBE_VLVFB(vlvf_index*2)); - bits |= (1 << vind); - IXGBE_WRITE_REG(hw, - IXGBE_VLVFB(vlvf_index*2), - bits); - } else { - bits = IXGBE_READ_REG(hw, - IXGBE_VLVFB((vlvf_index*2)+1)); - bits |= (1 << (vind-32)); - IXGBE_WRITE_REG(hw, - IXGBE_VLVFB((vlvf_index*2)+1), - bits); - } - } else { - /* clear the pool bit */ - if (vind < 32) { - bits = IXGBE_READ_REG(hw, - IXGBE_VLVFB(vlvf_index*2)); - bits &= ~(1 << vind); - IXGBE_WRITE_REG(hw, - IXGBE_VLVFB(vlvf_index*2), - bits); - bits |= IXGBE_READ_REG(hw, - IXGBE_VLVFB((vlvf_index*2)+1)); - } else { - bits = IXGBE_READ_REG(hw, - IXGBE_VLVFB((vlvf_index*2)+1)); - bits &= ~(1 << (vind-32)); - IXGBE_WRITE_REG(hw, - IXGBE_VLVFB((vlvf_index*2)+1), - bits); - bits |= IXGBE_READ_REG(hw, - IXGBE_VLVFB(vlvf_index*2)); - } + bits = IXGBE_READ_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + vind / 32)); + + /* set the pool bit */ + bits |= 1 << (vind % 32); + if (vlan_on) + goto vlvf_update; + + /* clear the pool bit */ + bits ^= 1 << (vind % 32); + + if (!bits && + !IXGBE_READ_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + 1 - vind / 32))) { + /* Clear VFTA first, then disable VLVF. Otherwise + * we run the risk of stray packets leaking into + * the PF via the default pool + */ + if (vfta_delta) + IXGBE_WRITE_REG(hw, IXGBE_VFTA(regidx), vfta); + + /* disable VLVF and clear remaining bit from pool */ + IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0); + IXGBE_WRITE_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + vind / 32), 0); + + return 0; } /* If there are still bits set in the VLVFB registers @@ -3149,20 +3134,15 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, * been cleared. This will be indicated by "bits" being * zero. */ - if (bits) { - IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), - (IXGBE_VLVF_VIEN | vlan)); + vfta_delta = 0; - /* if someone wants to clear the vfta entry but - * some pools/VFs are still using it. Ignore it. - */ - if (!vlan_on) - vfta_delta = 0; - } else { - IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0); - } +vlvf_update: + /* record pool change and enable VLAN ID if not already enabled */ + IXGBE_WRITE_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + vind / 32), bits); + IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), IXGBE_VLVF_VIEN | vlan); vfta_update: + /* Update VFTA now that we are ready for traffic */ if (vfta_delta) IXGBE_WRITE_REG(hw, IXGBE_VFTA(regidx), vfta); @@ -3184,8 +3164,8 @@ s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw) for (offset = 0; offset < IXGBE_VLVF_ENTRIES; offset++) { IXGBE_WRITE_REG(hw, IXGBE_VLVF(offset), 0); - IXGBE_WRITE_REG(hw, IXGBE_VLVFB(offset*2), 0); - IXGBE_WRITE_REG(hw, IXGBE_VLVFB((offset*2)+1), 0); + IXGBE_WRITE_REG(hw, IXGBE_VLVFB(offset * 2), 0); + IXGBE_WRITE_REG(hw, IXGBE_VLVFB(offset * 2 + 1), 0); } return 0; -- cgit v1.2.3-59-g8ed1b From b6488b662b5011a3640033a266886603892dfed1 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 2 Nov 2015 17:10:01 -0800 Subject: ixgbe: Add support for adding/removing VLAN on PF bypassing the VLVF This patch adds support for bypassing the VLVF entry creation when the PF is adding a new VLAN. The advantage to doing this is that we can then save the VLVF entries for the VFs which must have them in order to function, versus the PF which can fall back on the default pool entry. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c | 3 ++- drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 21 +++++++++++++++------ drivers/net/ethernet/intel/ixgbe/ixgbe_common.h | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 ++-- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 18 +++++++++++++++++- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 2 +- 6 files changed, 38 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c index 8f09d291a043..d8a9fb8a59e2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c @@ -880,11 +880,12 @@ static s32 ixgbe_clear_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq) * @vlan: VLAN id to write to VLAN filter * @vind: VMDq output index that maps queue to VLAN id in VFTA * @vlan_on: boolean flag to turn on/off VLAN in VFTA + * @vlvf_bypass: boolean flag - unused * * Turn on/off specified VLAN in the VLAN filter table. **/ static s32 ixgbe_set_vfta_82598(struct ixgbe_hw *hw, u32 vlan, u32 vind, - bool vlan_on) + bool vlan_on, bool vlvf_bypass) { u32 regindex; u32 bitindex; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 3f5fe60fdc11..5fd860a8d6f7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -2999,16 +2999,21 @@ s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw) * return the VLVF index where this VLAN id should be placed * **/ -static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan) +static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan, bool vlvf_bypass) { + s32 regindex, first_empty_slot; u32 bits = 0; - u32 first_empty_slot = 0; - s32 regindex; /* short cut the special case */ if (vlan == 0) return 0; + /* if vlvf_bypass is set we don't want to use an empty slot, we + * will simply bypass the VLVF if there are no entries present in the + * VLVF that contain our VLAN + */ + first_empty_slot = vlvf_bypass ? IXGBE_ERR_NO_SPACE : 0; + /* * Search for the vlan id in the VLVF entries. Save off the first empty * slot found along the way @@ -3044,11 +3049,12 @@ static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan) * @vlan: VLAN id to write to VLAN filter * @vind: VMDq output index that maps queue to VLAN id in VFVFB * @vlan_on: boolean flag to turn on/off VLAN in VFVF + * @vlvf_bypass: boolean flag indicating updating default pool is okay * * Turn on/off specified VLAN in the VLAN filter table. **/ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, - bool vlan_on) + bool vlan_on, bool vlvf_bypass) { u32 regidx, vfta_delta, vfta, bits; s32 vlvf_index; @@ -3090,9 +3096,12 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind, if (!(IXGBE_READ_REG(hw, IXGBE_VT_CTL) & IXGBE_VT_CTL_VT_ENABLE)) goto vfta_update; - vlvf_index = ixgbe_find_vlvf_slot(hw, vlan); - if (vlvf_index < 0) + vlvf_index = ixgbe_find_vlvf_slot(hw, vlan, vlvf_bypass); + if (vlvf_index < 0) { + if (vlvf_bypass) + goto vfta_update; return vlvf_index; + } bits = IXGBE_READ_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + vind / 32)); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h index a0044e4a8b90..2b9563137fd8 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h @@ -92,7 +92,7 @@ s32 ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq); s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq); s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw); s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, - u32 vind, bool vlan_on); + u32 vind, bool vlan_on, bool vlvf_bypass); s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw); s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index c539b1c71201..dcd99e61af64 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3899,7 +3899,7 @@ static int ixgbe_vlan_rx_add_vid(struct net_device *netdev, struct ixgbe_hw *hw = &adapter->hw; /* add VID to filter table */ - hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), true); + hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), true, true); set_bit(vid, adapter->active_vlans); return 0; @@ -3912,7 +3912,7 @@ static int ixgbe_vlan_rx_kill_vid(struct net_device *netdev, struct ixgbe_hw *hw = &adapter->hw; /* remove VID from filter table */ - hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), false); + hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), false, true); clear_bit(vid, adapter->active_vlans); return 0; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 61a054ace56d..3380f14517dc 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -452,11 +452,27 @@ void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter) static int ixgbe_set_vf_vlan(struct ixgbe_adapter *adapter, int add, int vid, u32 vf) { + struct ixgbe_hw *hw = &adapter->hw; + int err; + /* VLAN 0 is a special case, don't allow it to be removed */ if (!vid && !add) return 0; - return adapter->hw.mac.ops.set_vfta(&adapter->hw, vid, vf, (bool)add); + /* If VLAN overlaps with one the PF is currently monitoring make + * sure that we are able to allocate a VLVF entry. This may be + * redundant but it guarantees PF will maintain visibility to + * the VLAN. + */ + if (add && test_bit(vid, adapter->active_vlans)) { + err = hw->mac.ops.set_vfta(hw, vid, VMDQ_P(0), true, false); + if (err) + return err; + } + + err = hw->mac.ops.set_vfta(hw, vid, vf, !!add, false); + + return err; } static s32 ixgbe_set_vf_lpe(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 1329eddfc9ce..06add27c8b8c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3300,7 +3300,7 @@ struct ixgbe_mac_operations { s32 (*enable_mc)(struct ixgbe_hw *); s32 (*disable_mc)(struct ixgbe_hw *); s32 (*clear_vfta)(struct ixgbe_hw *); - s32 (*set_vfta)(struct ixgbe_hw *, u32, u32, bool); + s32 (*set_vfta)(struct ixgbe_hw *, u32, u32, bool, bool); s32 (*init_uta_tables)(struct ixgbe_hw *); void (*set_mac_anti_spoofing)(struct ixgbe_hw *, bool, int); void (*set_vlan_anti_spoofing)(struct ixgbe_hw *, bool, int); -- cgit v1.2.3-59-g8ed1b From c2bc9ce91c31cc214667b9e1a150cd3000856c1c Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 2 Nov 2015 17:10:07 -0800 Subject: ixgbe: Reorder search to work from the top down instead of bottom up This patch is meant to reduce the complexity of the search function used for finding a VLVF entry associated with a given VLAN ID. The previous code was searching from bottom to top. I reordered it to search from top to bottom. In addition I pulled an AND statement out of the loop and instead replaced it with an OR statement outside the loop. This should help to reduce the overall size and complexity of the function. There was also some formatting I cleaned up in regards to whitespace and such. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 42 +++++++++++-------------- 1 file changed, 19 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 5fd860a8d6f7..73dcc0aec6dc 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -3002,7 +3002,7 @@ s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw) static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan, bool vlvf_bypass) { s32 regindex, first_empty_slot; - u32 bits = 0; + u32 bits; /* short cut the special case */ if (vlan == 0) @@ -3014,33 +3014,29 @@ static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan, bool vlvf_bypass) */ first_empty_slot = vlvf_bypass ? IXGBE_ERR_NO_SPACE : 0; - /* - * Search for the vlan id in the VLVF entries. Save off the first empty - * slot found along the way - */ - for (regindex = 1; regindex < IXGBE_VLVF_ENTRIES; regindex++) { + /* add VLAN enable bit for comparison */ + vlan |= IXGBE_VLVF_VIEN; + + /* Search for the vlan id in the VLVF entries. Save off the first empty + * slot found along the way. + * + * pre-decrement loop covering (IXGBE_VLVF_ENTRIES - 1) .. 1 + */ + for (regindex = IXGBE_VLVF_ENTRIES; --regindex;) { bits = IXGBE_READ_REG(hw, IXGBE_VLVF(regindex)); - if (!bits && !(first_empty_slot)) + if (bits == vlan) + return regindex; + if (!first_empty_slot && !bits) first_empty_slot = regindex; - else if ((bits & 0x0FFF) == vlan) - break; } - /* - * If regindex is less than IXGBE_VLVF_ENTRIES, then we found the vlan - * in the VLVF. Else use the first empty VLVF register for this - * vlan id. - */ - if (regindex >= IXGBE_VLVF_ENTRIES) { - if (first_empty_slot) - regindex = first_empty_slot; - else { - hw_dbg(hw, "No space in VLVF.\n"); - regindex = IXGBE_ERR_NO_SPACE; - } - } + /* If we are here then we didn't find the VLAN. Return first empty + * slot we found during our search, else error. + */ + if (!first_empty_slot) + hw_dbg(hw, "No space in VLVF.\n"); - return regindex; + return first_empty_slot ? : IXGBE_ERR_NO_SPACE; } /** -- cgit v1.2.3-59-g8ed1b From 16369564915a9777217244678ee6160f8f1acac7 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 2 Nov 2015 17:10:13 -0800 Subject: ixgbe: Add support for VLAN promiscuous with SR-IOV This patch adds support for VLAN promiscuous with SR-IOV enabled. The code prior to this patch was only adding the PF to VLANs that the VF had added. As such enabling promiscuous mode would actually not add any additional VLAN filters so visibility was limited. This lead to a number of issues as the bridge and OVS would expect us to accept all VLAN tagged packets when promiscuous mode was enabled, and instead we would filter out most if not all depending on the configuration of the PF. With this patch what we do is set all the bits in the VFTA and all of the VLVF bits associated with the pool belonging to the PF. By doing this the PF is guaranteed to receive all VLAN tagged traffic associated with the RAR filters assigned to the PF. In addition we will clean up those same bits in the event of promiscuous mode being disabled. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 140 +++++++++++++++++++++++--- 2 files changed, 129 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 445b4c9169b6..0269c0ca997c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -664,6 +664,7 @@ struct ixgbe_adapter { #ifdef CONFIG_IXGBE_VXLAN #define IXGBE_FLAG2_VXLAN_REREG_NEEDED BIT(12) #endif +#define IXGBE_FLAG2_VLAN_PROMISC BIT(13) /* Tx fast path data */ int num_tx_queues; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index dcd99e61af64..431c70ca603e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3702,6 +3702,9 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter) /* Map PF MAC address in RAR Entry 0 to first pool following VFs */ hw->mac.ops.set_vmdq(hw, 0, VMDQ_P(0)); + /* clear VLAN promisc flag so VFTA will be updated if necessary */ + adapter->flags2 &= ~IXGBE_FLAG2_VLAN_PROMISC; + /* * Set up VF register offsets for selected VT Mode, * i.e. 32 or 64 VFs for SR-IOV @@ -3990,6 +3993,129 @@ static void ixgbe_vlan_strip_enable(struct ixgbe_adapter *adapter) } } +static void ixgbe_vlan_promisc_enable(struct ixgbe_adapter *adapter) +{ + struct ixgbe_hw *hw = &adapter->hw; + u32 vlnctrl, i; + + switch (hw->mac.type) { + case ixgbe_mac_82599EB: + case ixgbe_mac_X540: + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + default: + if (adapter->flags & IXGBE_FLAG_VMDQ_ENABLED) + break; + /* fall through */ + case ixgbe_mac_82598EB: + /* legacy case, we can just disable VLAN filtering */ + vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL); + vlnctrl &= ~(IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN); + IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl); + return; + } + + /* We are already in VLAN promisc, nothing to do */ + if (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC) + return; + + /* Set flag so we don't redo unnecessary work */ + adapter->flags2 |= IXGBE_FLAG2_VLAN_PROMISC; + + /* Add PF to all active pools */ + for (i = IXGBE_VLVF_ENTRIES; --i;) { + u32 reg_offset = IXGBE_VLVFB(i * 2 + VMDQ_P(0) / 32); + u32 vlvfb = IXGBE_READ_REG(hw, reg_offset); + + vlvfb |= 1 << (VMDQ_P(0) % 32); + IXGBE_WRITE_REG(hw, reg_offset, vlvfb); + } + + /* Set all bits in the VLAN filter table array */ + for (i = hw->mac.vft_size; i--;) + IXGBE_WRITE_REG(hw, IXGBE_VFTA(i), ~0U); +} + +#define VFTA_BLOCK_SIZE 8 +static void ixgbe_scrub_vfta(struct ixgbe_adapter *adapter, u32 vfta_offset) +{ + struct ixgbe_hw *hw = &adapter->hw; + u32 vfta[VFTA_BLOCK_SIZE] = { 0 }; + u32 vid_start = vfta_offset * 32; + u32 vid_end = vid_start + (VFTA_BLOCK_SIZE * 32); + u32 i, vid, word, bits; + + for (i = IXGBE_VLVF_ENTRIES; --i;) { + u32 vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(i)); + + /* pull VLAN ID from VLVF */ + vid = vlvf & VLAN_VID_MASK; + + /* only concern outselves with a certain range */ + if (vid < vid_start || vid >= vid_end) + continue; + + if (vlvf) { + /* record VLAN ID in VFTA */ + vfta[(vid - vid_start) / 32] |= 1 << (vid % 32); + + /* if PF is part of this then continue */ + if (test_bit(vid, adapter->active_vlans)) + continue; + } + + /* remove PF from the pool */ + word = i * 2 + VMDQ_P(0) / 32; + bits = ~(1 << (VMDQ_P(0) % 32)); + bits &= IXGBE_READ_REG(hw, IXGBE_VLVFB(word)); + IXGBE_WRITE_REG(hw, IXGBE_VLVFB(word), bits); + } + + /* extract values from active_vlans and write back to VFTA */ + for (i = VFTA_BLOCK_SIZE; i--;) { + vid = (vfta_offset + i) * 32; + word = vid / BITS_PER_LONG; + bits = vid % BITS_PER_LONG; + + vfta[i] |= adapter->active_vlans[word] >> bits; + + IXGBE_WRITE_REG(hw, IXGBE_VFTA(vfta_offset + i), vfta[i]); + } +} + +static void ixgbe_vlan_promisc_disable(struct ixgbe_adapter *adapter) +{ + struct ixgbe_hw *hw = &adapter->hw; + u32 vlnctrl, i; + + switch (hw->mac.type) { + case ixgbe_mac_82599EB: + case ixgbe_mac_X540: + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + default: + if (adapter->flags & IXGBE_FLAG_VMDQ_ENABLED) + break; + /* fall through */ + case ixgbe_mac_82598EB: + vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL); + vlnctrl &= ~IXGBE_VLNCTRL_CFIEN; + vlnctrl |= IXGBE_VLNCTRL_VFE; + IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl); + return; + } + + /* We are not in VLAN promisc, nothing to do */ + if (!(adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC)) + return; + + /* Set flag so we don't redo unnecessary work */ + adapter->flags2 &= ~IXGBE_FLAG2_VLAN_PROMISC; + + for (i = 0; i < hw->mac.vft_size; i += VFTA_BLOCK_SIZE) + ixgbe_scrub_vfta(adapter, i); +} + static void ixgbe_restore_vlan(struct ixgbe_adapter *adapter) { u16 vid; @@ -4246,12 +4372,10 @@ void ixgbe_set_rx_mode(struct net_device *netdev) struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; u32 fctrl, vmolr = IXGBE_VMOLR_BAM | IXGBE_VMOLR_AUPE; - u32 vlnctrl; int count; /* Check for Promiscuous and All Multicast modes */ fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL); - vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL); /* set all bits that we expect to always be set */ fctrl &= ~IXGBE_FCTRL_SBP; /* disable store-bad-packets */ @@ -4261,25 +4385,18 @@ void ixgbe_set_rx_mode(struct net_device *netdev) /* clear the bits we are changing the status of */ fctrl &= ~(IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE); - vlnctrl &= ~(IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN); if (netdev->flags & IFF_PROMISC) { hw->addr_ctrl.user_set_promisc = true; fctrl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE); vmolr |= IXGBE_VMOLR_MPE; - /* Only disable hardware filter vlans in promiscuous mode - * if SR-IOV and VMDQ are disabled - otherwise ensure - * that hardware VLAN filters remain enabled. - */ - if (adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED | - IXGBE_FLAG_SRIOV_ENABLED)) - vlnctrl |= (IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN); + ixgbe_vlan_promisc_enable(adapter); } else { if (netdev->flags & IFF_ALLMULTI) { fctrl |= IXGBE_FCTRL_MPE; vmolr |= IXGBE_VMOLR_MPE; } - vlnctrl |= IXGBE_VLNCTRL_VFE; hw->addr_ctrl.user_set_promisc = false; + ixgbe_vlan_promisc_disable(adapter); } /* @@ -4323,7 +4440,6 @@ void ixgbe_set_rx_mode(struct net_device *netdev) /* NOTE: VLAN filtering is disabled by setting PROMISC */ } - IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl); IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl); if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) -- cgit v1.2.3-59-g8ed1b From e1d0a2af2b30f5f0cbce2e4dd438d4da2433b226 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 2 Nov 2015 17:10:19 -0800 Subject: ixgbe: Fix VLAN promisc in relation to SR-IOV This patch is a follow-on for enabling VLAN promiscuous and allowing the PF to add VLANs without adding a VLVF entry. What this patch does is go through and free the VLVF registers if they are not needed as the VLAN belongs only to the PF which is the default pool. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 50 +++++++++++++- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 94 ++++++-------------------- 3 files changed, 72 insertions(+), 73 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 0269c0ca997c..f4c9a42dafcf 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -898,6 +898,7 @@ int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter, const u8 *addr, u16 queue); int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter, const u8 *addr, u16 queue); +void ixgbe_update_pf_promisc_vlvf(struct ixgbe_adapter *adapter, u32 vid); void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter); netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *, struct ixgbe_adapter *, struct ixgbe_ring *); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 431c70ca603e..66c64a376719 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3908,6 +3908,50 @@ static int ixgbe_vlan_rx_add_vid(struct net_device *netdev, return 0; } +static int ixgbe_find_vlvf_entry(struct ixgbe_hw *hw, u32 vlan) +{ + u32 vlvf; + int idx; + + /* short cut the special case */ + if (vlan == 0) + return 0; + + /* Search for the vlan id in the VLVF entries */ + for (idx = IXGBE_VLVF_ENTRIES; --idx;) { + vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(idx)); + if ((vlvf & VLAN_VID_MASK) == vlan) + break; + } + + return idx; +} + +void ixgbe_update_pf_promisc_vlvf(struct ixgbe_adapter *adapter, u32 vid) +{ + struct ixgbe_hw *hw = &adapter->hw; + u32 bits, word; + int idx; + + idx = ixgbe_find_vlvf_entry(hw, vid); + if (!idx) + return; + + /* See if any other pools are set for this VLAN filter + * entry other than the PF. + */ + word = idx * 2 + (VMDQ_P(0) / 32); + bits = ~(1 << (VMDQ_P(0)) % 32); + bits &= IXGBE_READ_REG(hw, IXGBE_VLVFB(word)); + + /* Disable the filter so this falls into the default pool. */ + if (!bits && !IXGBE_READ_REG(hw, IXGBE_VLVFB(word ^ 1))) { + if (!(adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC)) + IXGBE_WRITE_REG(hw, IXGBE_VLVFB(word), 0); + IXGBE_WRITE_REG(hw, IXGBE_VLVF(idx), 0); + } +} + static int ixgbe_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid) { @@ -3915,7 +3959,11 @@ static int ixgbe_vlan_rx_kill_vid(struct net_device *netdev, struct ixgbe_hw *hw = &adapter->hw; /* remove VID from filter table */ - hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), false, true); + if (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC) + ixgbe_update_pf_promisc_vlvf(adapter, vid); + else + hw->mac.ops.set_vfta(hw, vid, VMDQ_P(0), false, true); + clear_bit(vid, adapter->active_vlans); return 0; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 3380f14517dc..03d4e5c9d71d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -472,6 +472,17 @@ static int ixgbe_set_vf_vlan(struct ixgbe_adapter *adapter, int add, int vid, err = hw->mac.ops.set_vfta(hw, vid, vf, !!add, false); + if (add && !err) + return err; + + /* If we failed to add the VF VLAN or we are removing the VF VLAN + * we may need to drop the PF pool bit in order to allow us to free + * up the VLVF resources. + */ + if (test_bit(vid, adapter->active_vlans) || + (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC)) + ixgbe_update_pf_promisc_vlvf(adapter, vid); + return err; } @@ -830,40 +841,14 @@ static int ixgbe_set_vf_mac_addr(struct ixgbe_adapter *adapter, return ixgbe_set_vf_mac(adapter, vf, new_mac) < 0; } -static int ixgbe_find_vlvf_entry(struct ixgbe_hw *hw, u32 vlan) -{ - u32 vlvf; - s32 regindex; - - /* short cut the special case */ - if (vlan == 0) - return 0; - - /* Search for the vlan id in the VLVF entries */ - for (regindex = 1; regindex < IXGBE_VLVF_ENTRIES; regindex++) { - vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(regindex)); - if ((vlvf & VLAN_VID_MASK) == vlan) - break; - } - - /* Return a negative value if not found */ - if (regindex >= IXGBE_VLVF_ENTRIES) - regindex = -1; - - return regindex; -} - static int ixgbe_set_vf_vlan_msg(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf) { + u32 add = (msgbuf[0] & IXGBE_VT_MSGINFO_MASK) >> IXGBE_VT_MSGINFO_SHIFT; + u32 vid = (msgbuf[1] & IXGBE_VLVF_VLANID_MASK); + u8 tcs = netdev_get_num_tc(adapter->netdev); struct ixgbe_hw *hw = &adapter->hw; - int add = (msgbuf[0] & IXGBE_VT_MSGINFO_MASK) >> IXGBE_VT_MSGINFO_SHIFT; - int vid = (msgbuf[1] & IXGBE_VLVF_VLANID_MASK); int err; - s32 reg_ndx; - u32 vlvf; - u32 bits; - u8 tcs = netdev_get_num_tc(adapter->netdev); if (adapter->vfinfo[vf].pf_vlan || tcs) { e_warn(drv, @@ -873,54 +858,19 @@ static int ixgbe_set_vf_vlan_msg(struct ixgbe_adapter *adapter, return -1; } + err = ixgbe_set_vf_vlan(adapter, add, vid, vf); + if (err) + return err; + + if (adapter->vfinfo[vf].spoofchk_enabled) + hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf); + if (add) adapter->vfinfo[vf].vlan_count++; else if (adapter->vfinfo[vf].vlan_count) adapter->vfinfo[vf].vlan_count--; - /* in case of promiscuous mode any VLAN filter set for a VF must - * also have the PF pool added to it. - */ - if (add && adapter->netdev->flags & IFF_PROMISC) - err = ixgbe_set_vf_vlan(adapter, add, vid, VMDQ_P(0)); - - err = ixgbe_set_vf_vlan(adapter, add, vid, vf); - if (!err && adapter->vfinfo[vf].spoofchk_enabled) - hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf); - - /* Go through all the checks to see if the VLAN filter should - * be wiped completely. - */ - if (!add && adapter->netdev->flags & IFF_PROMISC) { - reg_ndx = ixgbe_find_vlvf_entry(hw, vid); - if (reg_ndx < 0) - return err; - vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(reg_ndx)); - /* See if any other pools are set for this VLAN filter - * entry other than the PF. - */ - if (VMDQ_P(0) < 32) { - bits = IXGBE_READ_REG(hw, IXGBE_VLVFB(reg_ndx * 2)); - bits &= ~(1 << VMDQ_P(0)); - bits |= IXGBE_READ_REG(hw, - IXGBE_VLVFB(reg_ndx * 2 + 1)); - } else { - bits = IXGBE_READ_REG(hw, - IXGBE_VLVFB(reg_ndx * 2 + 1)); - bits &= ~(1 << (VMDQ_P(0) - 32)); - bits |= IXGBE_READ_REG(hw, IXGBE_VLVFB(reg_ndx * 2)); - } - - /* If the filter was removed then ensure PF pool bit - * is cleared if the PF only added itself to the pool - * because the PF is in promiscuous mode. - */ - if ((vlvf & VLAN_VID_MASK) == vid && - !test_bit(vid, adapter->active_vlans) && !bits) - ixgbe_set_vf_vlan(adapter, add, vid, VMDQ_P(0)); - } - - return err; + return 0; } static int ixgbe_set_vf_macvlan_msg(struct ixgbe_adapter *adapter, -- cgit v1.2.3-59-g8ed1b From 6e982aeae5779a67fc02c5f6873654c49af97e70 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 2 Nov 2015 17:10:26 -0800 Subject: ixgbe: Clear stale pool mappings This patch makes certain that we clear the pool mappings added when we configure default MAC addresses for the interface. Without this we run the risk of leaking an address into pool 0 which really belongs to VF 0 when SR-IOV is enabled. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c | 10 +++++++--- drivers/net/ethernet/intel/ixgbe/ixgbe_common.c | 7 ++++--- drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c | 10 +++++++--- 3 files changed, 18 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index b8bd72589f72..fa8d4f40ac2a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -1083,12 +1083,16 @@ mac_reset_top: /* Add the SAN MAC address to the RAR only if it's a valid address */ if (is_valid_ether_addr(hw->mac.san_addr)) { - hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1, - hw->mac.san_addr, 0, IXGBE_RAH_AV); - /* Save the SAN MAC RAR index */ hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1; + hw->mac.ops.set_rar(hw, hw->mac.san_mac_rar_index, + hw->mac.san_addr, 0, IXGBE_RAH_AV); + + /* clear VMDq pool/queue selection for this RAR */ + hw->mac.ops.clear_vmdq(hw, hw->mac.san_mac_rar_index, + IXGBE_CLEAR_VMDQ_ALL); + /* Reserve the last RAR for the SAN MAC address */ hw->mac.num_rar_entries--; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 73dcc0aec6dc..64045053e874 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -1884,10 +1884,11 @@ s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw) hw_dbg(hw, " New MAC Addr =%pM\n", hw->mac.addr); hw->mac.ops.set_rar(hw, 0, hw->mac.addr, 0, IXGBE_RAH_AV); - - /* clear VMDq pool/queue selection for RAR 0 */ - hw->mac.ops.clear_vmdq(hw, 0, IXGBE_CLEAR_VMDQ_ALL); } + + /* clear VMDq pool/queue selection for RAR 0 */ + hw->mac.ops.clear_vmdq(hw, 0, IXGBE_CLEAR_VMDQ_ALL); + hw->addr_ctrl.overflow_promisc = 0; hw->addr_ctrl.rar_used_count = 1; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index bf8225ceab8e..2358c1b7d586 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -154,12 +154,16 @@ mac_reset_top: /* Add the SAN MAC address to the RAR only if it's a valid address */ if (is_valid_ether_addr(hw->mac.san_addr)) { - hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1, - hw->mac.san_addr, 0, IXGBE_RAH_AV); - /* Save the SAN MAC RAR index */ hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1; + hw->mac.ops.set_rar(hw, hw->mac.san_mac_rar_index, + hw->mac.san_addr, 0, IXGBE_RAH_AV); + + /* clear VMDq pool/queue selection for this RAR */ + hw->mac.ops.clear_vmdq(hw, hw->mac.san_mac_rar_index, + IXGBE_CLEAR_VMDQ_ALL); + /* Reserve the last RAR for the SAN MAC address */ hw->mac.num_rar_entries--; } -- cgit v1.2.3-59-g8ed1b From 4c7f35f679f592804736f9303051257de2c9f021 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 2 Nov 2015 17:10:32 -0800 Subject: ixgbe: Clean stale VLANs when changing port VLAN or resetting This patch guarantees that the VFs do not have access to VLANs that they were not supposed to. What this patch does is add code so that we delete the previous port VLAN after adding a new one, and if we reset the VF we clear all of the filters associated with it. Previously the code was leaving all previous VLANs mapped to the VF and they didn't get deleted unless the VF specifically requested it or if the PF itself was reset. Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 77 ++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 03d4e5c9d71d..eeff3d075bf8 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -455,10 +455,6 @@ static int ixgbe_set_vf_vlan(struct ixgbe_adapter *adapter, int add, int vid, struct ixgbe_hw *hw = &adapter->hw; int err; - /* VLAN 0 is a special case, don't allow it to be removed */ - if (!vid && !add) - return 0; - /* If VLAN overlaps with one the PF is currently monitoring make * sure that we are able to allocate a VLVF entry. This may be * redundant but it guarantees PF will maintain visibility to @@ -589,13 +585,75 @@ static void ixgbe_clear_vmvir(struct ixgbe_adapter *adapter, u32 vf) IXGBE_WRITE_REG(hw, IXGBE_VMVIR(vf), 0); } + +static void ixgbe_clear_vf_vlans(struct ixgbe_adapter *adapter, u32 vf) +{ + struct ixgbe_hw *hw = &adapter->hw; + u32 i; + + /* post increment loop, covers VLVF_ENTRIES - 1 to 0 */ + for (i = IXGBE_VLVF_ENTRIES; i--;) { + u32 word = IXGBE_VLVFB(i * 2 + vf / 32); + u32 bits[2], vlvfb, vid, vfta, vlvf; + u32 mask = 1 << (vf / 32); + + vlvfb = IXGBE_READ_REG(hw, word); + + /* if our bit isn't set we can skip it */ + if (!(vlvfb & mask)) + continue; + + /* clear our bit from vlvfb */ + vlvfb ^= mask; + + /* create 64b mask to chedk to see if we should clear VLVF */ + bits[word % 2] = vlvfb; + bits[(word % 2) ^ 1] = IXGBE_READ_REG(hw, word ^ 1); + + /* if promisc is enabled, PF will be present, leave VFTA */ + if (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC) { + bits[VMDQ_P(0) / 32] &= ~(1 << (VMDQ_P(0) % 32)); + + if (bits[0] || bits[1]) + goto update_vlvfb; + goto update_vlvf; + } + + /* if other pools are present, just remove ourselves */ + if (bits[0] || bits[1]) + goto update_vlvfb; + + /* if we cannot determine VLAN just remove ourselves */ + vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(i)); + if (!vlvf) + goto update_vlvfb; + + vid = vlvf & VLAN_VID_MASK; + mask = 1 << (vid % 32); + + /* clear bit from VFTA */ + vfta = IXGBE_READ_REG(hw, IXGBE_VFTA(vid / 32)); + if (vfta & mask) + IXGBE_WRITE_REG(hw, IXGBE_VFTA(vid / 32), vfta ^ mask); +update_vlvf: + /* clear POOL selection enable */ + IXGBE_WRITE_REG(hw, IXGBE_VLVF(i), 0); +update_vlvfb: + /* clear pool bits */ + IXGBE_WRITE_REG(hw, IXGBE_VLVFB(word), vlvfb); + } +} + static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf) { struct ixgbe_hw *hw = &adapter->hw; struct vf_data_storage *vfinfo = &adapter->vfinfo[vf]; u8 num_tcs = netdev_get_num_tc(adapter->netdev); - /* add PF assigned VLAN or VLAN 0 */ + /* remove VLAN filters beloning to this VF */ + ixgbe_clear_vf_vlans(adapter, vf); + + /* add back PF assigned VLAN or VLAN 0 */ ixgbe_set_vf_vlan(adapter, true, vfinfo->pf_vlan, vf); /* reset offloads to defaults */ @@ -858,6 +916,10 @@ static int ixgbe_set_vf_vlan_msg(struct ixgbe_adapter *adapter, return -1; } + /* VLAN 0 is a special case, don't allow it to be removed */ + if (!vid && !add) + return 0; + err = ixgbe_set_vf_vlan(adapter, add, vid, vf); if (err) return err; @@ -1251,6 +1313,9 @@ static int ixgbe_enable_port_vlan(struct ixgbe_adapter *adapter, int vf, if (err) goto out; + /* Revoke tagless access via VLAN 0 */ + ixgbe_set_vf_vlan(adapter, false, 0, vf); + ixgbe_set_vmvir(adapter, vlan, qos, vf); ixgbe_set_vmolr(hw, vf, false); if (adapter->vfinfo[vf].spoofchk_enabled) @@ -1284,6 +1349,8 @@ static int ixgbe_disable_port_vlan(struct ixgbe_adapter *adapter, int vf) err = ixgbe_set_vf_vlan(adapter, false, adapter->vfinfo[vf].pf_vlan, vf); + /* Restore tagless access via VLAN 0 */ + ixgbe_set_vf_vlan(adapter, true, 0, vf); ixgbe_clear_vmvir(adapter, vf); ixgbe_set_vmolr(hw, vf, true); hw->mac.ops.set_vlan_anti_spoofing(hw, false, vf); -- cgit v1.2.3-59-g8ed1b From d3428001c58dce10af624e889667c7862320390a Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Fri, 6 Nov 2015 16:34:33 -0800 Subject: ixgbe: do not report 2.5 Gbps as supported Some X550 devices can connect at 2.5Gbps during fail-over, but only with certain link partners. Also setting the advertised speed will not work so we do not report it as supported to avoid confusion. Signed-off-by: Emil Tantilov Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index d681273bd39d..1ed4c9add00d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -166,8 +166,6 @@ static int ixgbe_get_settings(struct net_device *netdev, /* set the supported link speeds */ if (supported_link & IXGBE_LINK_SPEED_10GB_FULL) ecmd->supported |= SUPPORTED_10000baseT_Full; - if (supported_link & IXGBE_LINK_SPEED_2_5GB_FULL) - ecmd->supported |= SUPPORTED_2500baseX_Full; if (supported_link & IXGBE_LINK_SPEED_1GB_FULL) ecmd->supported |= SUPPORTED_1000baseT_Full; if (supported_link & IXGBE_LINK_SPEED_100_FULL) @@ -179,8 +177,6 @@ static int ixgbe_get_settings(struct net_device *netdev, ecmd->advertising |= ADVERTISED_100baseT_Full; if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) ecmd->advertising |= ADVERTISED_10000baseT_Full; - if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_2_5GB_FULL) - ecmd->advertising |= ADVERTISED_2500baseX_Full; if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) ecmd->advertising |= ADVERTISED_1000baseT_Full; } else { -- cgit v1.2.3-59-g8ed1b From 75f5cea9ddef6a8f8cedb86c0ac8c696b258824d Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Thu, 19 Nov 2015 11:34:14 -0800 Subject: i40e: chomp the BIT(_ULL) BIT_ULL was used on a u32 or less where it can simply be BIT. This fixes some trivial static analyzer warnings. Chomp, chomp. Tested with objdump of binary before and after, no changes to code. Change-ID: I6245e9abd447192dbde1669c747aeb2878126c7d Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2b1b655a3b52..3373b7506bed 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1531,7 +1531,7 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, if (enabled_tc && (vsi->back->flags & I40E_FLAG_DCB_ENABLED)) { /* Find numtc from enabled TC bitmap */ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - if (enabled_tc & BIT_ULL(i)) /* TC is enabled */ + if (enabled_tc & BIT(i)) /* TC is enabled */ numtc++; } if (!numtc) { @@ -1560,7 +1560,7 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, /* Setup queue offset/count for all TCs for given VSI */ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { /* See if the given TC is enabled for the given VSI */ - if (vsi->tc_config.enabled_tc & BIT_ULL(i)) { + if (vsi->tc_config.enabled_tc & BIT(i)) { /* TC is enabled */ int pow, num_qps; @@ -4433,7 +4433,7 @@ static u8 i40e_get_iscsi_tc_map(struct i40e_pf *pf) if (app.selector == I40E_APP_SEL_TCPIP && app.protocolid == I40E_APP_PROTOID_ISCSI) { tc = dcbcfg->etscfg.prioritytable[app.priority]; - enabled_tc |= BIT_ULL(tc); + enabled_tc |= BIT(tc); break; } } @@ -4517,7 +4517,7 @@ static u8 i40e_pf_get_num_tc(struct i40e_pf *pf) /* At least have TC0 */ enabled_tc = (enabled_tc ? enabled_tc : 0x1); for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - if (enabled_tc & BIT_ULL(i)) + if (enabled_tc & BIT(i)) num_tc++; } return num_tc; @@ -4539,7 +4539,7 @@ static u8 i40e_pf_get_default_tc(struct i40e_pf *pf) /* Find the first enabled TC */ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - if (enabled_tc & BIT_ULL(i)) + if (enabled_tc & BIT(i)) break; } @@ -4699,7 +4699,7 @@ static void i40e_vsi_config_netdev_tc(struct i40e_vsi *vsi, u8 enabled_tc) * will set the numtc for netdev as 2 that will be * referenced by the netdev layer as TC 0 and 1. */ - if (vsi->tc_config.enabled_tc & BIT_ULL(i)) + if (vsi->tc_config.enabled_tc & BIT(i)) netdev_set_tc_queue(netdev, vsi->tc_config.tc_info[i].netdev_tc, vsi->tc_config.tc_info[i].qcount, @@ -4761,7 +4761,7 @@ static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc) /* Enable ETS TCs with equal BW Share for now across all VSIs */ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - if (enabled_tc & BIT_ULL(i)) + if (enabled_tc & BIT(i)) bw_share[i] = 1; } @@ -4835,7 +4835,7 @@ int i40e_veb_config_tc(struct i40e_veb *veb, u8 enabled_tc) /* Enable ETS TCs with equal BW Share for now */ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - if (enabled_tc & BIT_ULL(i)) + if (enabled_tc & BIT(i)) bw_data.tc_bw_share_credits[i] = 1; } @@ -5232,7 +5232,7 @@ static int i40e_setup_tc(struct net_device *netdev, u8 tc) /* Generate TC map for number of tc requested */ for (i = 0; i < tc; i++) - enabled_tc |= BIT_ULL(i); + enabled_tc |= BIT(i); /* Requesting same TC configuration as already enabled */ if (enabled_tc == vsi->tc_config.enabled_tc) @@ -6096,23 +6096,23 @@ static void i40e_reset_subtask(struct i40e_pf *pf) rtnl_lock(); if (test_bit(__I40E_REINIT_REQUESTED, &pf->state)) { - reset_flags |= BIT_ULL(__I40E_REINIT_REQUESTED); + reset_flags |= BIT(__I40E_REINIT_REQUESTED); clear_bit(__I40E_REINIT_REQUESTED, &pf->state); } if (test_bit(__I40E_PF_RESET_REQUESTED, &pf->state)) { - reset_flags |= BIT_ULL(__I40E_PF_RESET_REQUESTED); + reset_flags |= BIT(__I40E_PF_RESET_REQUESTED); clear_bit(__I40E_PF_RESET_REQUESTED, &pf->state); } if (test_bit(__I40E_CORE_RESET_REQUESTED, &pf->state)) { - reset_flags |= BIT_ULL(__I40E_CORE_RESET_REQUESTED); + reset_flags |= BIT(__I40E_CORE_RESET_REQUESTED); clear_bit(__I40E_CORE_RESET_REQUESTED, &pf->state); } if (test_bit(__I40E_GLOBAL_RESET_REQUESTED, &pf->state)) { - reset_flags |= BIT_ULL(__I40E_GLOBAL_RESET_REQUESTED); + reset_flags |= BIT(__I40E_GLOBAL_RESET_REQUESTED); clear_bit(__I40E_GLOBAL_RESET_REQUESTED, &pf->state); } if (test_bit(__I40E_DOWN_REQUESTED, &pf->state)) { - reset_flags |= BIT_ULL(__I40E_DOWN_REQUESTED); + reset_flags |= BIT(__I40E_DOWN_REQUESTED); clear_bit(__I40E_DOWN_REQUESTED, &pf->state); } @@ -10567,7 +10567,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* NVM bit on means WoL disabled for the port */ i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits); - if ((1 << hw->port) & wol_nvm_bits || hw->partition_id != 1) + if (BIT (hw->port) & wol_nvm_bits || hw->partition_id != 1) pf->wol_en = false; else pf->wol_en = true; -- cgit v1.2.3-59-g8ed1b From b36e9ab59b7e3a5b14bf88dc0536e6579db7b54d Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 19 Nov 2015 11:34:16 -0800 Subject: i40e: properly delete VF MAC filters The virtual channel interface was using incorrect semantics to remove MAC addresses, which would leave incorrect filters active when using VLANs. To correct this, add a new function that unconditionally removes MAC addresses from all VLANs, and call this function when the VF requests a MAC filter removal. Change-ID: I69826908ae4f6c847f5bf9b32f11faa760189c74 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 ++ drivers/net/ethernet/intel/i40e/i40e_main.c | 36 ++++++++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 8 +++-- 3 files changed, 44 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index bd6d9c002acc..b7bc014ae00b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -776,6 +776,8 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid); int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid); struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr, bool is_vf, bool is_netdev); +int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr, + bool is_vf, bool is_netdev); bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi); struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr, bool is_vf, bool is_netdev); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 3373b7506bed..36d1c2bb0120 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1258,6 +1258,42 @@ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr, struct i40e_mac_filter, list); } +/** + * i40e_del_mac_all_vlan - Remove a MAC filter from all VLANS + * @vsi: the VSI to be searched + * @macaddr: the mac address to be removed + * @is_vf: true if it is a VF + * @is_netdev: true if it is a netdev + * + * Removes a given MAC address from a VSI, regardless of VLAN + * + * Returns 0 for success, or error + **/ +int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr, + bool is_vf, bool is_netdev) +{ + struct i40e_mac_filter *f = NULL; + int changed = 0; + + WARN(!spin_is_locked(&vsi->mac_filter_list_lock), + "Missing mac_filter_list_lock\n"); + list_for_each_entry(f, &vsi->mac_filter_list, list) { + if ((ether_addr_equal(macaddr, f->macaddr)) && + (is_vf == f->is_vf) && + (is_netdev == f->is_netdev)) { + f->counter--; + f->changed = true; + changed = 1; + } + } + if (changed) { + vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; + vsi->back->flags |= I40E_FLAG_FILTER_SYNC; + return 0; + } + return -ENOENT; +} + /** * i40e_rm_default_mac_filter - Remove the default MAC filter set by NVM * @vsi: the PF Main VSI - inappropriate for any other VSI diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index b3bd81c3e1ce..cbbb2ef4b256 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1683,8 +1683,12 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) spin_lock_bh(&vsi->mac_filter_list_lock); /* delete addresses from the list */ for (i = 0; i < al->num_elements; i++) - i40e_del_filter(vsi, al->list[i].addr, - I40E_VLAN_ANY, true, false); + if (i40e_del_mac_all_vlan(vsi, al->list[i].addr, true, false)) { + ret = I40E_ERR_INVALID_MAC_ADDR; + spin_unlock_bh(&vsi->mac_filter_list_lock); + goto error_param; + } + spin_unlock_bh(&vsi->mac_filter_list_lock); /* program the updated filter list */ -- cgit v1.2.3-59-g8ed1b From b7b713a8eaf325607d37229f024ad0b9f3e7f320 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 19 Nov 2015 11:34:17 -0800 Subject: i40e: don't add zero MAC filter When VFs are created, the MAC address defaults to all zeros, indicating to the VF driver that it should use a random MAC address. However, the PF driver was incorrectly adding this zero MAC to the filter table, along with the VF's randomly generated MAC address. Check for a good address before adding the default filter. While we're at it, make the error message a bit more useful. Change-ID: Ia100947d68140e0f73a19ba755cbffc3e79a8fcf Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index cbbb2ef4b256..aa58a498c239 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -549,12 +549,15 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) i40e_vsi_add_pvid(vsi, vf->port_vlan_id); spin_lock_bh(&vsi->mac_filter_list_lock); - f = i40e_add_filter(vsi, vf->default_lan_addr.addr, - vf->port_vlan_id ? vf->port_vlan_id : -1, - true, false); - if (!f) - dev_info(&pf->pdev->dev, - "Could not allocate VF MAC addr\n"); + if (is_valid_ether_addr(vf->default_lan_addr.addr)) { + f = i40e_add_filter(vsi, vf->default_lan_addr.addr, + vf->port_vlan_id ? vf->port_vlan_id : -1, + true, false); + if (!f) + dev_info(&pf->pdev->dev, + "Could not add MAC filter %pM for VF %d\n", + vf->default_lan_addr.addr, vf->vf_id); + } f = i40e_add_filter(vsi, brdcast, vf->port_vlan_id ? vf->port_vlan_id : -1, true, false); -- cgit v1.2.3-59-g8ed1b From fdb47ae87af537b24977a03bc69cfe1c5c55ca62 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 19 Nov 2015 11:34:18 -0800 Subject: i40evf: check rings before freeing resources If the driver gets unloaded during reset recovery, it's possible that it will attempt to free resources when they're already free. Add a check to make sure that the Tx and Rx rings actually exist before dereferencing them to free resources. Change-ID: I4d2b7e9ede49f634d421a4c5deaa5446bc755eee Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index b4c632f417f6..98091b4ccc53 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2034,6 +2034,9 @@ void i40evf_free_all_tx_resources(struct i40evf_adapter *adapter) { int i; + if (!adapter->tx_rings) + return; + for (i = 0; i < adapter->num_active_queues; i++) if (adapter->tx_rings[i].desc) i40evf_free_tx_resources(&adapter->tx_rings[i]); @@ -2102,6 +2105,9 @@ void i40evf_free_all_rx_resources(struct i40evf_adapter *adapter) { int i; + if (!adapter->rx_rings) + return; + for (i = 0; i < adapter->num_active_queues; i++) if (adapter->rx_rings[i].desc) i40evf_free_rx_resources(&adapter->rx_rings[i]); -- cgit v1.2.3-59-g8ed1b From 6621e4b2518a2ea5e1f3aab041bfbb0e4fc91239 Mon Sep 17 00:00:00 2001 From: Kamil Krawczyk Date: Thu, 19 Nov 2015 11:34:19 -0800 Subject: i40e: use explicit cast from u16 to u8 Current implementation generates compilation warnings. Change-ID: Icceefb50fe62aefaf90a64afb7192e08355a4ec5 Signed-off-by: Kamil Krawczyk Acked-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c index 79ae7beeafe5..daa9204426d4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c +++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c @@ -762,7 +762,7 @@ static void i40e_write_byte(u8 *hmc_bits, /* prepare the bits and mask */ shift_width = ce_info->lsb % 8; - mask = BIT(ce_info->width) - 1; + mask = (u8)(BIT(ce_info->width) - 1); src_byte = *from; src_byte &= mask; -- cgit v1.2.3-59-g8ed1b From 00ada50d8989ed14b160d831d25fe09bf9ea9115 Mon Sep 17 00:00:00 2001 From: Michal Kosiarz Date: Thu, 19 Nov 2015 11:34:20 -0800 Subject: i40e: Opcode and structures required by OEM Post Update AQ command and add new NVM arq message This is a part of implementation which contains data structures and opcode for new AQ command. There's a new ARQ message that gets sent near the end of the NVM update process that the driver should recognize and ignore, rather than printing an Unknown Event error. Change-ID: I04830a5bcae14823e16b9424cc4165e169336c1f Signed-off-by: Michal Kosiarz Acked-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h | 21 +++++++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_main.c | 1 + drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h | 21 +++++++++++++++++++++ 3 files changed, 43 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 61a497935941..b22012a446a6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -227,6 +227,7 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_nvm_update = 0x0703, i40e_aqc_opc_nvm_config_read = 0x0704, i40e_aqc_opc_nvm_config_write = 0x0705, + i40e_aqc_opc_oem_post_update = 0x0720, /* virtualization commands */ i40e_aqc_opc_send_msg_to_pf = 0x0801, @@ -1891,6 +1892,26 @@ struct i40e_aqc_nvm_config_data_immediate_field { I40E_CHECK_STRUCT_LEN(0xc, i40e_aqc_nvm_config_data_immediate_field); +/* OEM Post Update (indirect 0x0720) + * no command data struct used + */ +struct i40e_aqc_nvm_oem_post_update { +#define I40E_AQ_NVM_OEM_POST_UPDATE_EXTERNAL_DATA 0x01 + u8 sel_data; + u8 reserved[7]; +}; + +I40E_CHECK_STRUCT_LEN(0x8, i40e_aqc_nvm_oem_post_update); + +struct i40e_aqc_nvm_oem_post_update_buffer { + u8 str_len; + u8 dev_addr; + __le16 eeprom_addr; + u8 data[36]; +}; + +I40E_CHECK_STRUCT_LEN(0x28, i40e_aqc_nvm_oem_post_update_buffer); + /* Send to PF command (indirect 0x0801) id is only used by PF * Send to VF command (indirect 0x0802) id is only used by PF * Send to Peer PF command (indirect 0x0803) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 36d1c2bb0120..aff401fee2a4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6295,6 +6295,7 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf) break; case i40e_aqc_opc_nvm_erase: case i40e_aqc_opc_nvm_update: + case i40e_aqc_opc_oem_post_update: i40e_debug(&pf->hw, I40E_DEBUG_NVM, "ARQ NVM operation completed\n"); break; default: diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h index 1c76389bd888..f5b2b369dc7c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h @@ -227,6 +227,7 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_nvm_update = 0x0703, i40e_aqc_opc_nvm_config_read = 0x0704, i40e_aqc_opc_nvm_config_write = 0x0705, + i40e_aqc_opc_oem_post_update = 0x0720, /* virtualization commands */ i40e_aqc_opc_send_msg_to_pf = 0x0801, @@ -1888,6 +1889,26 @@ struct i40e_aqc_nvm_config_data_immediate_field { I40E_CHECK_STRUCT_LEN(0xc, i40e_aqc_nvm_config_data_immediate_field); +/* OEM Post Update (indirect 0x0720) + * no command data struct used + */ + struct i40e_aqc_nvm_oem_post_update { +#define I40E_AQ_NVM_OEM_POST_UPDATE_EXTERNAL_DATA 0x01 + u8 sel_data; + u8 reserved[7]; +}; + +I40E_CHECK_STRUCT_LEN(0x8, i40e_aqc_nvm_oem_post_update); + +struct i40e_aqc_nvm_oem_post_update_buffer { + u8 str_len; + u8 dev_addr; + __le16 eeprom_addr; + u8 data[36]; +}; + +I40E_CHECK_STRUCT_LEN(0x28, i40e_aqc_nvm_oem_post_update_buffer); + /* Send to PF command (indirect 0x0801) id is only used by PF * Send to VF command (indirect 0x0802) id is only used by PF * Send to Peer PF command (indirect 0x0803) -- cgit v1.2.3-59-g8ed1b From 75eb73c127beaccf1b2c825f5d583ebca17ab2ba Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 19 Nov 2015 11:34:21 -0800 Subject: i40e: hush little warnings These messages seem big and scary, but they're really not. The driver can fully recover from any of these. The overflow error in particular can happen when enabling a bunch of VFs and the VF driver is not blacklisted. Since these messages are really for debugging purposes, reclassify them as such. Change-ID: I628d0f5e135e7063450ba05393a50b7af23aa6d7 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index aff401fee2a4..35d787eb8f64 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6219,15 +6219,18 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf) val = rd32(&pf->hw, pf->hw.aq.arq.len); oldval = val; if (val & I40E_PF_ARQLEN_ARQVFE_MASK) { - dev_info(&pf->pdev->dev, "ARQ VF Error detected\n"); + if (hw->debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ARQ VF Error detected\n"); val &= ~I40E_PF_ARQLEN_ARQVFE_MASK; } if (val & I40E_PF_ARQLEN_ARQOVFL_MASK) { - dev_info(&pf->pdev->dev, "ARQ Overflow Error detected\n"); + if (hw->debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ARQ Overflow Error detected\n"); val &= ~I40E_PF_ARQLEN_ARQOVFL_MASK; } if (val & I40E_PF_ARQLEN_ARQCRIT_MASK) { - dev_info(&pf->pdev->dev, "ARQ Critical Error detected\n"); + if (hw->debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ARQ Critical Error detected\n"); val &= ~I40E_PF_ARQLEN_ARQCRIT_MASK; } if (oldval != val) @@ -6236,15 +6239,18 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf) val = rd32(&pf->hw, pf->hw.aq.asq.len); oldval = val; if (val & I40E_PF_ATQLEN_ATQVFE_MASK) { - dev_info(&pf->pdev->dev, "ASQ VF Error detected\n"); + if (pf->hw.debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ASQ VF Error detected\n"); val &= ~I40E_PF_ATQLEN_ATQVFE_MASK; } if (val & I40E_PF_ATQLEN_ATQOVFL_MASK) { - dev_info(&pf->pdev->dev, "ASQ Overflow Error detected\n"); + if (pf->hw.debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ASQ Overflow Error detected\n"); val &= ~I40E_PF_ATQLEN_ATQOVFL_MASK; } if (val & I40E_PF_ATQLEN_ATQCRIT_MASK) { - dev_info(&pf->pdev->dev, "ASQ Critical Error detected\n"); + if (pf->hw.debug_mask & I40E_DEBUG_AQ) + dev_info(&pf->pdev->dev, "ASQ Critical Error detected\n"); val &= ~I40E_PF_ATQLEN_ATQCRIT_MASK; } if (oldval != val) -- cgit v1.2.3-59-g8ed1b From b9eacec3e66cd7e3db11882474024d1586d1f832 Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Thu, 19 Nov 2015 11:34:22 -0800 Subject: i40e/i40evf: Add a new offload for RSS PCTYPE V2 for X722 X722 supports Expanded version of TCP, UDP PCTYPES for RSS. Add a Virtchnl offload to support this. Without this patch with X722 devices, driver will set wrong PCTYPES for VF and UDP flows will not fan out. Change-ID: I04fe4988253b7cd108c9179a643c969764efcb76 Signed-off-by: Anjali Singhai Jain Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl.h | 1 + drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h | 1 + drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h index ae879826084b..3226946bf3d4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h @@ -153,6 +153,7 @@ struct i40e_virtchnl_vsi_resource { #define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020 #define I40E_VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000 #define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000 +#define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000 struct i40e_virtchnl_vf_resource { u16 num_vsis; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h index 9f7b279b9d9c..3b9d2037456c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h @@ -153,6 +153,7 @@ struct i40e_virtchnl_vsi_resource { #define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020 #define I40E_VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000 #define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000 +#define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000 struct i40e_virtchnl_vf_resource { u16 num_vsis; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 3c9c008b168b..c1c526283757 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -157,7 +157,9 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter) I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ | I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG | I40E_VIRTCHNL_VF_OFFLOAD_VLAN | - I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR; + I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR | + I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2; + adapter->current_op = I40E_VIRTCHNL_OP_GET_VF_RESOURCES; adapter->aq_required &= ~I40EVF_FLAG_AQ_GET_CONFIG; if (PF_IS_V11(adapter)) -- cgit v1.2.3-59-g8ed1b From f11999987bc0b5559ab56dedc6f4ca32fab5438a Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Thu, 19 Nov 2015 11:34:23 -0800 Subject: i40e: clean whole mac filter list Clean the whole mac filter list when resetting after an intermediate add or delete push to the firmware. The code had evolved from using a list from the stack to a heap allocation, but the memset() didn't follow the change correctly. This now cleans the whole list rather that just part of the first element. Change-ID: I4cd03d5a103b7407dd8556a3a231e800f2d6f2d5 Reported-by: Rasmus Villemoes Signed-off-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 35d787eb8f64..7049342081b4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1916,11 +1916,13 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) /* Now process 'del_list' outside the lock */ if (!list_empty(&tmp_del_list)) { + int del_list_size; + filter_list_len = pf->hw.aq.asq_buf_size / sizeof(struct i40e_aqc_remove_macvlan_element_data); - del_list = kcalloc(filter_list_len, - sizeof(struct i40e_aqc_remove_macvlan_element_data), - GFP_KERNEL); + del_list_size = filter_list_len * + sizeof(struct i40e_aqc_remove_macvlan_element_data); + del_list = kzalloc(del_list_size, GFP_KERNEL); if (!del_list) { i40e_cleanup_add_list(&tmp_add_list); @@ -1955,7 +1957,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) NULL); aq_err = pf->hw.aq.asq_last_status; num_del = 0; - memset(del_list, 0, sizeof(*del_list)); + memset(del_list, 0, del_list_size); if (aq_ret && aq_err != I40E_AQ_RC_ENOENT) { retval = -EIO; @@ -1991,13 +1993,14 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) } if (!list_empty(&tmp_add_list)) { + int add_list_size; /* do all the adds now */ filter_list_len = pf->hw.aq.asq_buf_size / sizeof(struct i40e_aqc_add_macvlan_element_data), - add_list = kcalloc(filter_list_len, - sizeof(struct i40e_aqc_add_macvlan_element_data), - GFP_KERNEL); + add_list_size = filter_list_len * + sizeof(struct i40e_aqc_add_macvlan_element_data); + add_list = kzalloc(add_list_size, GFP_KERNEL); if (!add_list) { /* Purge element from temporary lists */ i40e_cleanup_add_list(&tmp_add_list); @@ -2036,7 +2039,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) if (aq_ret) break; - memset(add_list, 0, sizeof(*add_list)); + memset(add_list, 0, add_list_size); } /* Entries from tmp_add_list were cloned from MAC * filter list, hence clean those cloned entries -- cgit v1.2.3-59-g8ed1b From 69ebe955be358fe8f8c73b3aa7395760f337845f Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 19 Nov 2015 11:34:24 -0800 Subject: i40evf: change version string generation Generate version strings like the PF driver does. This gives us more flexibility to add suffixes to the version string at build time. Change-ID: I0a5ca0783dd8fb849516bfc1e37ea070127847bd Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 98091b4ccc53..cb58bad60833 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -34,7 +34,15 @@ char i40evf_driver_name[] = "i40evf"; static const char i40evf_driver_string[] = "Intel(R) XL710/X710 Virtual Function Network Driver"; -#define DRV_VERSION "1.4.3" +#define DRV_KERN "-k" + +#define DRV_VERSION_MAJOR 1 +#define DRV_VERSION_MINOR 4 +#define DRV_VERSION_BUILD 3 +#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ + __stringify(DRV_VERSION_MINOR) "." \ + __stringify(DRV_VERSION_BUILD) \ + DRV_KERN const char i40evf_driver_version[] = DRV_VERSION; static const char i40evf_copyright[] = "Copyright (c) 2013 - 2015 Intel Corporation."; -- cgit v1.2.3-59-g8ed1b From c9c9f1ba75fa1260a9087ba229559175cd4a2b40 Mon Sep 17 00:00:00 2001 From: Catherine Sullivan Date: Thu, 19 Nov 2015 11:34:25 -0800 Subject: i40e/i40evf: Bump i40e to 1.4.8 and i40evf to 1.4.4 Bump. Change-ID: I2b8976bde070244de144e2ed8990b083de39f332 Signed-off-by: Catherine Sullivan Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7049342081b4..b118deb08ce6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -39,7 +39,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 4 -#define DRV_VERSION_BUILD 7 +#define DRV_VERSION_BUILD 8 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index cb58bad60833..455394cf7f80 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -38,7 +38,7 @@ static const char i40evf_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 4 -#define DRV_VERSION_BUILD 3 +#define DRV_VERSION_BUILD 4 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) \ -- cgit v1.2.3-59-g8ed1b From 99319b8c23f183cdc7a0652ed40f879f82cf8941 Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Tue, 24 Nov 2015 19:13:27 +0200 Subject: iwlwifi: mvm: add an option to start rs from HT/VHT rates Extend the configurable option of setting initial rate to RSSI based. Make the initial rate to be set to VHT/HT SISO or legacy depending on the AP capabilities. Signed-off-by: Gregory Greenman Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 67 ++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 54789bd3b352..feb775a8223a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -2550,6 +2550,8 @@ static const struct rs_init_rate_info rs_optimal_rates_vht_40_80mhz[] = { { S8_MIN, IWL_RATE_MCS_0_INDEX }, }; +#define IWL_RS_LOW_RSSI_THRESHOLD (-76) /* dBm */ + /* Init the optimal rate based on STA caps * This combined with rssi is used to report the last tx rate * to userspace when we haven't transmitted enough frames. @@ -2635,11 +2637,13 @@ static struct rs_rate *rs_get_optimal_rate(struct iwl_mvm *mvm, * of last Rx */ static void rs_get_initial_rate(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, enum ieee80211_band band, struct rs_rate *rate) { int i, nentries; + unsigned long active_rate; s8 best_rssi = S8_MIN; u8 best_ant = ANT_NONE; u8 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); @@ -2680,19 +2684,55 @@ static void rs_get_initial_rate(struct iwl_mvm *mvm, nentries = ARRAY_SIZE(rs_optimal_rates_24ghz_legacy); } - if (IWL_MVM_RS_RSSI_BASED_INIT_RATE) { - for (i = 0; i < nentries; i++) { - int rate_idx = initial_rates[i].rate_idx; - if ((best_rssi >= initial_rates[i].rssi) && - (BIT(rate_idx) & lq_sta->active_legacy_rate)) { - rate->index = rate_idx; - break; - } + if (!IWL_MVM_RS_RSSI_BASED_INIT_RATE) + goto out; + + /* Start from a higher rate if the corresponding debug capability + * is enabled. The rate is chosen according to AP capabilities. + * In case of VHT/HT when the rssi is low fallback to the case of + * legacy rates. + */ + if (sta->vht_cap.vht_supported && + best_rssi > IWL_RS_LOW_RSSI_THRESHOLD) { + if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) { + initial_rates = rs_optimal_rates_vht_40_80mhz; + nentries = ARRAY_SIZE(rs_optimal_rates_vht_40_80mhz); + if (sta->bandwidth >= IEEE80211_STA_RX_BW_80) + rate->bw = RATE_MCS_CHAN_WIDTH_80; + else + rate->bw = RATE_MCS_CHAN_WIDTH_40; + } else if (sta->bandwidth == IEEE80211_STA_RX_BW_20) { + initial_rates = rs_optimal_rates_vht_20mhz; + nentries = ARRAY_SIZE(rs_optimal_rates_vht_20mhz); + rate->bw = RATE_MCS_CHAN_WIDTH_20; + } else { + IWL_ERR(mvm, "Invalid BW %d\n", sta->bandwidth); + goto out; } + active_rate = lq_sta->active_siso_rate; + rate->type = LQ_VHT_SISO; + } else if (sta->ht_cap.ht_supported && + best_rssi > IWL_RS_LOW_RSSI_THRESHOLD) { + initial_rates = rs_optimal_rates_ht; + nentries = ARRAY_SIZE(rs_optimal_rates_ht); + active_rate = lq_sta->active_siso_rate; + rate->type = LQ_HT_SISO; + } else { + active_rate = lq_sta->active_legacy_rate; } - IWL_DEBUG_RATE(mvm, "rate_idx %d ANT %s\n", rate->index, - rs_pretty_ant(rate->ant)); + for (i = 0; i < nentries; i++) { + int rate_idx = initial_rates[i].rate_idx; + + if ((best_rssi >= initial_rates[i].rssi) && + (BIT(rate_idx) & active_rate)) { + rate->index = rate_idx; + break; + } + } + +out: + rs_dump_rate(mvm, rate, "INITIAL"); } /* Save info about RSSI of last Rx */ @@ -2752,14 +2792,11 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, tbl = &(lq_sta->lq_info[active_tbl]); rate = &tbl->rate; - rs_get_initial_rate(mvm, lq_sta, band, rate); + rs_get_initial_rate(mvm, sta, lq_sta, band, rate); rs_init_optimal_rate(mvm, sta, lq_sta); WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B); - if (rate->ant == ANT_A) - tbl->column = RS_COLUMN_LEGACY_ANT_A; - else - tbl->column = RS_COLUMN_LEGACY_ANT_B; + tbl->column = rs_get_column_from_rate(rate); rs_set_expected_tpt_table(lq_sta, tbl); rs_fill_lq_cmd(mvm, sta, lq_sta, rate); -- cgit v1.2.3-59-g8ed1b From cf0cda191074fba464999ef29eb654cdab909e66 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Thu, 24 Sep 2015 10:44:12 +0200 Subject: iwlwifi: mvm: set default new STA as non-aggregated When sending the first ADD_STA HCMD for a STA, the %add_modify field indicates an addition of a STA and not a modification of one. In such a case, all fields of the HCMD are used to initialize the corresponding fields in the FW, regardless of what bits are set in %modify_mask. Set the %tid_disable_tx field to mvm_sta->tid_disable_agg in iwl_mvm_sta_send_to_fw(). If the STA is only updated this will have no effect, but if it is added - it will make sure the STA starts with the correct queues - if any - configured as non-aggregated by default (until told otherwise). Signed-off-by: Liad Kaufman Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 354acbde088e..dc3206a353e1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -106,6 +106,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, .add_modify = update ? 1 : 0, .station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK | STA_FLG_MIMO_EN_MSK), + .tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg), }; int ret; u32 status; @@ -622,6 +623,7 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, color)); cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk); + cmd.tid_disable_tx = cpu_to_le16(0xffff); if (addr) memcpy(cmd.addr, addr, ETH_ALEN); -- cgit v1.2.3-59-g8ed1b From 355346ba3050f42dc33663d7dd6cba055ba31924 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Thu, 26 Nov 2015 11:22:33 +0200 Subject: iwlwifi: mvm: configure scheduled scan according to traffic conditions Change scan configuration (dwell time, suspend time etc.) according to traffic conditions. This is useful for scans that are managed by the FW (e.g. scheduled scan). Signed-off-by: Avraham Stern Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 +++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 10 +++++- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 43 +++++++++++------------ 3 files changed, 34 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 518a1d518ed4..fc13ad93562a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -993,6 +993,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) mvm->vif_count = 0; mvm->rx_ba_sessions = 0; mvm->fw_dbg_conf = FW_DBG_INVALID; + mvm->scan_type = IWL_SCAN_TYPE_NOT_SET; /* keep statistics ticking */ iwl_mvm_accu_radio_stats(mvm); @@ -1868,6 +1869,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, iwl_mvm_bt_coex_vif_change(mvm); iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, IEEE80211_SMPS_AUTOMATIC); + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_UMAC_SCAN)) + iwl_mvm_config_scan(mvm); } else if (changes & BSS_CHANGED_BEACON_INFO) { /* * We received a beacon _after_ association so diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 7dc3af6e06f7..612799a889db 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -475,6 +475,14 @@ enum iwl_scan_status { IWL_MVM_SCAN_MASK = 0xff, }; +enum iwl_mvm_scan_type { + IWL_SCAN_TYPE_NOT_SET, + IWL_SCAN_TYPE_UNASSOC, + IWL_SCAN_TYPE_WILD, + IWL_SCAN_TYPE_MILD, + IWL_SCAN_TYPE_FRAGMENTED, +}; + /** * struct iwl_nvm_section - describes an NVM section in memory. * @@ -643,7 +651,7 @@ struct iwl_mvm { unsigned int scan_status; void *scan_cmd; struct iwl_mcast_filter_cmd *mcast_filter_cmd; - bool scan_fragmented; + enum iwl_mvm_scan_type scan_type; /* max number of simultaneous scans the FW supports */ unsigned int max_scans; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 7cbfb085f60b..4887418b6ad3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -72,13 +72,6 @@ #define IWL_DENSE_EBS_SCAN_RATIO 5 #define IWL_SPARSE_EBS_SCAN_RATIO 1 -enum iwl_mvm_scan_type { - IWL_SCAN_TYPE_UNASSOC, - IWL_SCAN_TYPE_WILD, - IWL_SCAN_TYPE_MILD, - IWL_SCAN_TYPE_FRAGMENTED, -}; - enum iwl_mvm_traffic_load { IWL_MVM_TRAFFIC_LOW, IWL_MVM_TRAFFIC_MEDIUM, @@ -206,9 +199,7 @@ static enum iwl_mvm_traffic_load iwl_mvm_get_traffic_load(struct iwl_mvm *mvm) } static enum -iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mvm_scan_params *params) +iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device) { int global_cnt = 0; enum iwl_mvm_traffic_load load; @@ -224,8 +215,7 @@ iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, load = iwl_mvm_get_traffic_load(mvm); low_latency = iwl_mvm_low_latency(mvm); - if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) && - vif->type != NL80211_IFTYPE_P2P_DEVICE && + if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) && !p2p_device && fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAGMENTED_SCAN)) return IWL_SCAN_TYPE_FRAGMENTED; @@ -917,18 +907,20 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) struct iwl_host_cmd cmd = { .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0), }; + enum iwl_mvm_scan_type type = iwl_mvm_get_scan_type(mvm, false); if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels)) return -ENOBUFS; + if (type == mvm->scan_type) + return 0; + cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels; scan_config = kzalloc(cmd_size, GFP_KERNEL); if (!scan_config) return -ENOMEM; - mvm->scan_fragmented = iwl_mvm_low_latency(mvm); - scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE | SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS | SCAN_CONFIG_FLAG_SET_TX_CHAINS | @@ -938,17 +930,18 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) SCAN_CONFIG_FLAG_SET_MAC_ADDR | SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS| SCAN_CONFIG_N_CHANNELS(num_channels) | - (mvm->scan_fragmented ? + (type == IWL_SCAN_TYPE_FRAGMENTED ? SCAN_CONFIG_FLAG_SET_FRAGMENTED : SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED)); scan_config->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm)); scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm); - scan_config->out_of_channel_time = cpu_to_le32(170); - scan_config->suspend_time = cpu_to_le32(30); - scan_config->dwell_active = 20; - scan_config->dwell_passive = 110; - scan_config->dwell_fragmented = 20; + scan_config->out_of_channel_time = + cpu_to_le32(scan_timing[type].max_out_time); + scan_config->suspend_time = cpu_to_le32(scan_timing[type].suspend_time); + scan_config->dwell_active = scan_timing[type].dwell_active; + scan_config->dwell_passive = scan_timing[type].dwell_passive; + scan_config->dwell_fragmented = scan_timing[type].dwell_fragmented; memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); @@ -972,6 +965,8 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n"); ret = iwl_mvm_send_cmd(mvm, &cmd); + if (!ret) + mvm->scan_type = type; kfree(scan_config); return ret; @@ -1225,7 +1220,9 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, params.scan_plans = &scan_plan; params.n_scan_plans = 1; - params.type = iwl_mvm_get_scan_type(mvm, vif, ¶ms); + params.type = + iwl_mvm_get_scan_type(mvm, + vif->type == NL80211_IFTYPE_P2P_DEVICE); iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); @@ -1307,7 +1304,9 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, params.n_scan_plans = req->n_scan_plans; params.scan_plans = req->scan_plans; - params.type = iwl_mvm_get_scan_type(mvm, vif, ¶ms); + params.type = + iwl_mvm_get_scan_type(mvm, + vif->type == NL80211_IFTYPE_P2P_DEVICE); /* In theory, LMAC scans can handle a 32-bit delay, but since * waiting for over 18 hours to start the scan is a bit silly -- cgit v1.2.3-59-g8ed1b From e8b3f7b6e746e5a850d243e1e11d83d2163e16e4 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 19 Nov 2015 18:37:31 +0200 Subject: iwlwifi: mvm: rs: fix a potential out of bounds access Klocwork pointed these out. There is a theoretical possibility that rate->index might be set to IWL_RATE_INVALID (15). This could trigger an out of bounds access on ht_vht_rates or legacy_rates arrays. Fix it by adding some checks. Signed-off-by: Eyal Shapira Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index feb775a8223a..31b082edd29e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -552,9 +552,10 @@ static char *rs_pretty_rate(const struct rs_rate *rate) }; const char *rate_str; - if (is_type_legacy(rate->type)) + if (is_type_legacy(rate->type) && (rate->index <= IWL_RATE_54M_INDEX)) rate_str = legacy_rates[rate->index]; - else if (is_type_ht(rate->type) || is_type_vht(rate->type)) + else if ((is_type_ht(rate->type) || is_type_vht(rate->type)) && + (rate->index <= IWL_RATE_MCS_9_INDEX)) rate_str = ht_vht_rates[rate->index]; else rate_str = "BAD_RATE"; -- cgit v1.2.3-59-g8ed1b From 2d7cf5497cca77c16145c9ad06ae5aee1e497c2e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 17 Apr 2015 16:39:12 +0200 Subject: iwlwifi: mvm: advertise NETIF_F_SG If the transport supports it, advertise NETIF_F_SG to mac80211 to be able to use frag SKBs. This will already improve performance by allowing software GSO to be used. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index fc13ad93562a..09afb6d55714 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -439,6 +439,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); + if (mvm->trans->max_skb_frags) + hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; + hw->queues = mvm->first_agg_queue; hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | -- cgit v1.2.3-59-g8ed1b From 07abbc5068d00b879d71099ed2e383e7b2ee3116 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 17 Apr 2015 16:39:12 +0200 Subject: iwlwifi: dvm: advertise NETIF_F_SG If the transport supports it, advertise NETIF_F_SG to mac80211 to be able to use frag SKBs. This will already improve performance by allowing software GSO to be used. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index a9d8b51419b4..f4c2529f7b94 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -115,6 +115,9 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); ieee80211_hw_set(hw, WANT_MONITOR_VIF); + if (priv->trans->max_skb_frags) + hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; + hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE; hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT; -- cgit v1.2.3-59-g8ed1b From 95a451c5e2eff812d572ee289a92cc5d38cc7e1c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 9 Dec 2015 13:26:08 +0300 Subject: iwlwifi: mvm: remove an extra tab Smatch prints a static checker warning here: drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c:386 iwl_dump_prph() warn: curly braces intended? Curly braces are NOT intended, the extra tab was added by mistake in commit 1a616dd2f171 ('iwlwifi: dump prph registers in a common place for all transports'). type=cleanup Signed-off-by: Dan Carpenter Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index 9fcabc5ce904..a6985da9c4a8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -383,7 +383,7 @@ static u32 iwl_dump_prph(struct iwl_trans *trans, *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans, reg)); - *data = iwl_fw_error_next_data(*data); + *data = iwl_fw_error_next_data(*data); } iwl_trans_release_nic_access(trans, &flags); -- cgit v1.2.3-59-g8ed1b From 305d236e83b664e0c1e1e29d06f8780fdd311fd4 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 9 Dec 2015 16:33:20 +0200 Subject: iwlwifi: mvm: cleanup roc te on restart cleanup iwl_mvm_restart_cleanup() calls ieee80211_remain_on_channel_expired() on cleanup, but it doesn't clean the actual roc time events, resulting in failure of further ROC attempts. Refactor iwl_mvm_stop_roc() a bit, and add a new function to only cleanup the roc time events (without sending further commands). Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 + .../net/wireless/intel/iwlwifi/mvm/time-event.c | 38 ++++++++++++++-------- .../net/wireless/intel/iwlwifi/mvm/time-event.h | 1 + 3 files changed, 27 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 09afb6d55714..f428f1cb0034 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -967,6 +967,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) mvm->calibrating = false; /* just in case one was running */ + iwl_mvm_cleanup_roc_te(mvm); ieee80211_remain_on_channel_expired(mvm->hw); /* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 87a04c32cb92..924dd6a41626 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -792,11 +792,9 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); } -void iwl_mvm_stop_roc(struct iwl_mvm *mvm) +static struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm) { - struct iwl_mvm_vif *mvmvif = NULL; struct iwl_mvm_time_event_data *te_data; - bool is_p2p = false; lockdep_assert_held(&mvm->mutex); @@ -810,11 +808,8 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm) * request */ list_for_each_entry(te_data, &mvm->time_event_list, list) { - if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { - mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); - is_p2p = true; - goto remove_te; - } + if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) + goto out; } /* There can only be at most one AUX ROC time event, we just use the @@ -823,18 +818,35 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm) te_data = list_first_entry_or_null(&mvm->aux_roc_te_list, struct iwl_mvm_time_event_data, list); +out: + spin_unlock_bh(&mvm->time_event_lock); + return te_data; +} + +void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm) +{ + struct iwl_mvm_time_event_data *te_data; + u32 uid; + + te_data = iwl_mvm_get_roc_te(mvm); if (te_data) - mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); + __iwl_mvm_remove_time_event(mvm, te_data, &uid); +} -remove_te: - spin_unlock_bh(&mvm->time_event_lock); +void iwl_mvm_stop_roc(struct iwl_mvm *mvm) +{ + struct iwl_mvm_vif *mvmvif; + struct iwl_mvm_time_event_data *te_data; - if (!mvmvif) { + te_data = iwl_mvm_get_roc_te(mvm); + if (!te_data) { IWL_WARN(mvm, "No remain on channel event\n"); return; } - if (is_p2p) + mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif); + + if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) iwl_mvm_remove_time_event(mvm, mvmvif, te_data); else iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h index 61d7cd791b6e..99d9a35ad5b1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h @@ -215,6 +215,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, void iwl_mvm_te_clear_data(struct iwl_mvm *mvm, struct iwl_mvm_time_event_data *te_data); +void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm); void iwl_mvm_roc_done_wk(struct work_struct *wk); /** -- cgit v1.2.3-59-g8ed1b From 3f50a69077b5b8086fd16cfa0b71138082126a69 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 15 Nov 2015 15:44:17 +0200 Subject: iwlwifi: mvm: check iwl_mvm_wowlan_config_key_params() return value commit 9a4c830007817e ("iwlwifi: mvm: refactor d3 key update functions") refactored some code into iwl_mvm_wowlan_config_key_params() function, but the return value was never checked, and not all the function flows returned valid values. fix it. Fixes: ac8ef0ce38de ("iwlwifi: mvm: refactor d3 key update functions") Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 9e51843764ca..542de7401a8d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -917,6 +917,7 @@ int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, if (ret) goto out; } + ret = 0; out: kfree(key_data.rsc_tsc); return ret; @@ -946,8 +947,11 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, * that isn't really a problem though. */ mutex_unlock(&mvm->mutex); - iwl_mvm_wowlan_config_key_params(mvm, vif, true, CMD_ASYNC); + ret = iwl_mvm_wowlan_config_key_params(mvm, vif, true, + CMD_ASYNC); mutex_lock(&mvm->mutex); + if (ret) + return ret; } ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0, -- cgit v1.2.3-59-g8ed1b From 0cd58eaab1482ab75800f68cd0033286232827f1 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Nov 2015 13:24:24 +0200 Subject: iwlwifi: pcie: allow the op_mode to block the tx queues In certain flows (see next patches), the op_mode may need to block the Tx queues for a short period. Provide an API for that. The transport is in charge of counting the number of times the queues are blocked since the op_mode may block the queues several times in a row before unblocking them. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 16 +++++++++++++ drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 1 + drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 28 ++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 4 +++- 4 files changed, 48 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 3d089ae171b7..0cd69cd98cd6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -542,6 +542,11 @@ struct iwl_trans_txq_scd_cfg { * @wait_tx_queue_empty: wait until tx queues are empty. May sleep. * @freeze_txq_timer: prevents the timer of the queue from firing until the * queue is set to awake. Must be atomic. + * @block_txq_ptrs: stop updating the write pointers of the Tx queues. Note + * that the transport needs to refcount the calls since this function + * will be called several times with block = true, and then the queues + * need to be unblocked only after the same number of calls with + * block = false. * @write8: write a u8 to a register at offset ofs from the BAR * @write32: write a u32 to a register at offset ofs from the BAR * @read32: read a u32 register at offset ofs from the BAR @@ -600,6 +605,7 @@ struct iwl_trans_ops { int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm); void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs, bool freeze); + void (*block_txq_ptrs)(struct iwl_trans *trans, bool block); void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val); void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val); @@ -1010,6 +1016,16 @@ static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans, trans->ops->freeze_txq_timer(trans, txqs, freeze); } +static inline void iwl_trans_block_txq_ptrs(struct iwl_trans *trans, + bool block) +{ + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + + if (trans->ops->block_txq_ptrs) + trans->ops->block_txq_ptrs(trans, block); +} + static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans, u32 txqs) { diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 44dc09d84d42..31f30a2dd654 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -278,6 +278,7 @@ struct iwl_txq { bool frozen; u8 active; bool ampdu; + bool block; unsigned long wd_timeout; }; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index efef4871478d..316162a45989 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1673,6 +1673,33 @@ next_queue: } } +static void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int i; + + for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { + struct iwl_txq *txq = &trans_pcie->txq[i]; + + if (i == trans_pcie->cmd_queue) + continue; + + spin_lock_bh(&txq->lock); + + if (!block && !(WARN_ON_ONCE(!txq->block))) { + txq->block--; + if (!txq->block) { + iwl_write32(trans, HBUS_TARG_WRPTR, + txq->q.write_ptr | (i << 8)); + } + } else if (block) { + txq->block++; + } + + spin_unlock_bh(&txq->lock); + } +} + #define IWL_FLUSH_WAIT_MS 2000 static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) @@ -2467,6 +2494,7 @@ static const struct iwl_trans_ops trans_ops_pcie = { .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, + .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs, .write8 = iwl_trans_pcie_write8, .write32 = iwl_trans_pcie_write32, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index a8c8a4a7420b..95ba920edb1d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -318,7 +318,9 @@ static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, * trying to tx (during RFKILL, we're not trying to tx). */ IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->q.write_ptr); - iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); + if (!txq->block) + iwl_write32(trans, HBUS_TARG_WRPTR, + txq->q.write_ptr | (txq_id << 8)); } void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans) -- cgit v1.2.3-59-g8ed1b From dcbb4746286abe50300deef1858cfd6dc1b9c198 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Nov 2015 15:17:37 +0200 Subject: iwlwifi: trans: support a callback for ASYNC commands This allows the op_mode to request from the transport to call a callback when an ASYNC commands is completed by the firmware. The same callback will be called for all the commands. Pass the command whose response triggers the callback as a parameter to the callback itself. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h | 11 +++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 7 +++++++ drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 7 +++---- 3 files changed, 21 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h index 2a58d6833224..ffff31c38ecf 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h @@ -123,6 +123,8 @@ struct iwl_cfg; * received on the RSS queue(s). The queue parameter indicates which of the * RSS queues received this frame; it will always be non-zero. * This method must not sleep. + * @async_cb: called when an ASYNC command with CMD_WANT_ASYNC_CALLBACK set + * completes. Must be atomic. * @queue_full: notifies that a HW queue is full. * Must be atomic and called with BH disabled. * @queue_not_full: notifies that a HW queue is not full any more. @@ -155,6 +157,8 @@ struct iwl_op_mode_ops { struct iwl_rx_cmd_buffer *rxb); void (*rx_rss)(struct iwl_op_mode *op_mode, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, unsigned int queue); + void (*async_cb)(struct iwl_op_mode *op_mode, + const struct iwl_device_cmd *cmd); void (*queue_full)(struct iwl_op_mode *op_mode, int queue); void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue); bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state); @@ -203,6 +207,13 @@ static inline void iwl_op_mode_rx_rss(struct iwl_op_mode *op_mode, op_mode->ops->rx_rss(op_mode, napi, rxb, queue); } +static inline void iwl_op_mode_async_cb(struct iwl_op_mode *op_mode, + const struct iwl_device_cmd *cmd) +{ + if (op_mode->ops->async_cb) + op_mode->ops->async_cb(op_mode, cmd); +} + static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode, int queue) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 0cd69cd98cd6..77ae7fa3053e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -248,6 +248,8 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt) * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle. * @CMD_WAKE_UP_TRANS: The command response should wake up the trans * (i.e. mark it as non-idle). + * @CMD_WANT_ASYNC_CALLBACK: the op_mode's async callback function must be + * called after this command completes. Valid only with CMD_ASYNC. * @CMD_TB_BITMAP_POS: Position of the first bit for the TB bitmap. We need to * check that we leave enough room for the TBs bitmap which needs 20 bits. */ @@ -259,6 +261,7 @@ enum CMD_MODE { CMD_SEND_IN_IDLE = BIT(4), CMD_MAKE_TRANS_IDLE = BIT(5), CMD_WAKE_UP_TRANS = BIT(6), + CMD_WANT_ASYNC_CALLBACK = BIT(7), CMD_TB_BITMAP_POS = 11, }; @@ -903,6 +906,10 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans, return -EIO; } + if (WARN_ON((cmd->flags & CMD_WANT_ASYNC_CALLBACK) && + !(cmd->flags & CMD_ASYNC))) + return -EINVAL; + if (!(cmd->flags & CMD_ASYNC)) lock_map_acquire_read(&trans->sync_cmd_lockdep_map); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 95ba920edb1d..fe8241ff8da2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -1593,10 +1593,6 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, /* * iwl_pcie_hcmd_complete - Pull unused buffers off the queue and reclaim them * @rxb: Rx buffer to reclaim - * - * If an Rx buffer has an async callback associated with it the callback - * will be executed. The attached skb (if present) will only be freed - * if the callback returns 1 */ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb) @@ -1640,6 +1636,9 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, meta->source->_rx_page_order = trans_pcie->rx_page_order; } + if (meta->flags & CMD_WANT_ASYNC_CALLBACK) + iwl_op_mode_async_cb(trans->op_mode, cmd); + iwl_pcie_cmdq_reclaim(trans, txq_id, index); if (!(meta->flags & CMD_ASYNC)) { -- cgit v1.2.3-59-g8ed1b From 156f92f2b4711a0e8478c596557a1bd5ca69b02d Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Nov 2015 14:55:18 +0200 Subject: iwlwifi: block the queues when we send ADD_STA for uAPSD We send an ADD_STA to instruct the firmware to release frames despite the peer being in PS. Since the ADD_STA command and the Tx frame that comes immediately afterwards can be reordered by the DMA engine, we need to block the Tx queues until the firmware replies with the ADD_STA response. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 13 +++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 7 ++++++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index b71c85b244a0..4e417fd9beb6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -839,6 +839,18 @@ static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) } } +static void iwl_mvm_async_cb(struct iwl_op_mode *op_mode, + const struct iwl_device_cmd *cmd) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + /* + * For now, we only set the CMD_WANT_ASYNC_CALLBACK for ADD_STA + * commands that need to block the Tx queues. + */ + iwl_trans_block_txq_ptrs(mvm->trans, false); +} + static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); @@ -1414,6 +1426,7 @@ int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) #define IWL_MVM_COMMON_OPS \ /* these could be differentiated */ \ + .async_cb = iwl_mvm_async_cb, \ .queue_full = iwl_mvm_stop_sw_queue, \ .queue_not_full = iwl_mvm_wake_sw_queue, \ .hw_rf_kill = iwl_mvm_set_hw_rfkill_state, \ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index dc3206a353e1..566b65944fb8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1708,7 +1708,12 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD); } - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); + /* block the Tx queues until the FW updated the sleep Tx count */ + iwl_trans_block_txq_ptrs(mvm->trans, true); + + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, + CMD_ASYNC | CMD_WANT_ASYNC_CALLBACK, + sizeof(cmd), &cmd); if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); } -- cgit v1.2.3-59-g8ed1b From 92fe83430b899b786c837e5b716a328220d47ae5 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 1 Dec 2015 13:45:37 +0200 Subject: iwlwifi: uninline iwl_trans_send_cmd This function got too big to be inlined. Uninline it. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 33 +++++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 34 ++------------------------ 2 files changed, 35 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index ccd317b6408f..c3fafbc6b654 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -61,6 +61,7 @@ * *****************************************************************************/ #include +#include "iwl-drv.h" #include "iwl-trans.h" struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, @@ -112,3 +113,35 @@ void iwl_trans_free(struct iwl_trans *trans) kmem_cache_destroy(trans->dev_cmd_pool); kfree(trans); } + +int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) +{ + int ret; + + if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) && + test_bit(STATUS_RFKILL, &trans->status))) + return -ERFKILL; + + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) + return -EIO; + + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) { + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + return -EIO; + } + + if (WARN_ON((cmd->flags & CMD_WANT_ASYNC_CALLBACK) && + !(cmd->flags & CMD_ASYNC))) + return -EINVAL; + + if (!(cmd->flags & CMD_ASYNC)) + lock_map_acquire_read(&trans->sync_cmd_lockdep_map); + + ret = trans->ops->send_cmd(trans, cmd); + + if (!(cmd->flags & CMD_ASYNC)) + lock_map_release(&trans->sync_cmd_lockdep_map); + + return ret; +} +IWL_EXPORT_SYMBOL(iwl_trans_send_cmd); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 77ae7fa3053e..b825d940cc58 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -889,38 +889,6 @@ iwl_trans_dump_data(struct iwl_trans *trans, return trans->ops->dump_data(trans, trigger); } -static inline int iwl_trans_send_cmd(struct iwl_trans *trans, - struct iwl_host_cmd *cmd) -{ - int ret; - - if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) && - test_bit(STATUS_RFKILL, &trans->status))) - return -ERFKILL; - - if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) - return -EIO; - - if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) { - IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); - return -EIO; - } - - if (WARN_ON((cmd->flags & CMD_WANT_ASYNC_CALLBACK) && - !(cmd->flags & CMD_ASYNC))) - return -EINVAL; - - if (!(cmd->flags & CMD_ASYNC)) - lock_map_acquire_read(&trans->sync_cmd_lockdep_map); - - ret = trans->ops->send_cmd(trans, cmd); - - if (!(cmd->flags & CMD_ASYNC)) - lock_map_release(&trans->sync_cmd_lockdep_map); - - return ret; -} - static inline struct iwl_device_cmd * iwl_trans_alloc_tx_cmd(struct iwl_trans *trans) { @@ -933,6 +901,8 @@ iwl_trans_alloc_tx_cmd(struct iwl_trans *trans) (dev_cmd_ptr + trans->dev_cmd_headroom); } +int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); + static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans, struct iwl_device_cmd *dev_cmd) { -- cgit v1.2.3-59-g8ed1b From 39bdb17ebb5bd7a5c8a231d7cac4a4d5ccc58149 Mon Sep 17 00:00:00 2001 From: Sharon Dvir Date: Thu, 15 Oct 2015 18:18:09 +0300 Subject: iwlwifi: update host command messages to new format Host commands now have a group id, express this in printed messages. Signed-off-by: Sharon Dvir Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/agn.h | 9 - drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c | 4 +- drivers/net/wireless/intel/iwlwifi/dvm/lib.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/main.c | 94 ++++++++- drivers/net/wireless/intel/iwlwifi/dvm/rx.c | 87 +------- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 60 +++++- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 34 +++- drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 5 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 222 ++++++++++++--------- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 10 - drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 5 +- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 4 +- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 31 +-- 13 files changed, 345 insertions(+), 222 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h index 991def878881..82c177aae580 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h @@ -473,13 +473,4 @@ do { \ } while (0) #endif /* CONFIG_IWLWIFI_DEBUG */ -extern const char *const iwl_dvm_cmd_strings[REPLY_MAX + 1]; - -static inline const char *iwl_dvm_get_cmd_string(u8 cmd) -{ - const char *s = iwl_dvm_cmd_strings[cmd]; - if (s) - return s; - return "UNKNOWN"; -} #endif /* __iwl_agn_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c index b15e44f8d1bd..a114d3e42828 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c @@ -32,7 +32,9 @@ #include #include #include + #include "iwl-debug.h" +#include "iwl-trans.h" #include "iwl-io.h" #include "dev.h" #include "agn.h" @@ -438,7 +440,7 @@ static ssize_t iwl_dbgfs_rx_handlers_read(struct file *file, if (priv->rx_handlers_stats[cnt] > 0) pos += scnprintf(buf + pos, bufsz - pos, "\tRx handler[%36s]:\t\t %u\n", - iwl_dvm_get_cmd_string(cnt), + iwl_get_cmd_string(priv->trans, (u32)cnt), priv->rx_handlers_stats[cnt]); } diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c index a612fbe66377..bee1c03ee259 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c @@ -1262,7 +1262,7 @@ int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) if (test_bit(STATUS_FW_ERROR, &priv->status)) { IWL_ERR(priv, "Command %s failed: FW Error\n", - iwl_dvm_get_cmd_string(cmd->id)); + iwl_get_cmd_string(priv->trans, cmd->id)); return -EIO; } diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index 41f3aa17475e..9f6f564504ff 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -69,6 +70,93 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search. + * A warning will be triggered on violation. + */ +static const struct iwl_hcmd_names iwl_dvm_cmd_names[] = { + HCMD_NAME(REPLY_ALIVE), + HCMD_NAME(REPLY_ERROR), + HCMD_NAME(REPLY_ECHO), + HCMD_NAME(REPLY_RXON), + HCMD_NAME(REPLY_RXON_ASSOC), + HCMD_NAME(REPLY_QOS_PARAM), + HCMD_NAME(REPLY_RXON_TIMING), + HCMD_NAME(REPLY_ADD_STA), + HCMD_NAME(REPLY_REMOVE_STA), + HCMD_NAME(REPLY_REMOVE_ALL_STA), + HCMD_NAME(REPLY_TX), + HCMD_NAME(REPLY_TXFIFO_FLUSH), + HCMD_NAME(REPLY_WEPKEY), + HCMD_NAME(REPLY_LEDS_CMD), + HCMD_NAME(REPLY_TX_LINK_QUALITY_CMD), + HCMD_NAME(COEX_PRIORITY_TABLE_CMD), + HCMD_NAME(COEX_MEDIUM_NOTIFICATION), + HCMD_NAME(COEX_EVENT_CMD), + HCMD_NAME(TEMPERATURE_NOTIFICATION), + HCMD_NAME(CALIBRATION_CFG_CMD), + HCMD_NAME(CALIBRATION_RES_NOTIFICATION), + HCMD_NAME(CALIBRATION_COMPLETE_NOTIFICATION), + HCMD_NAME(REPLY_QUIET_CMD), + HCMD_NAME(REPLY_CHANNEL_SWITCH), + HCMD_NAME(CHANNEL_SWITCH_NOTIFICATION), + HCMD_NAME(REPLY_SPECTRUM_MEASUREMENT_CMD), + HCMD_NAME(SPECTRUM_MEASURE_NOTIFICATION), + HCMD_NAME(POWER_TABLE_CMD), + HCMD_NAME(PM_SLEEP_NOTIFICATION), + HCMD_NAME(PM_DEBUG_STATISTIC_NOTIFIC), + HCMD_NAME(REPLY_SCAN_CMD), + HCMD_NAME(REPLY_SCAN_ABORT_CMD), + HCMD_NAME(SCAN_START_NOTIFICATION), + HCMD_NAME(SCAN_RESULTS_NOTIFICATION), + HCMD_NAME(SCAN_COMPLETE_NOTIFICATION), + HCMD_NAME(BEACON_NOTIFICATION), + HCMD_NAME(REPLY_TX_BEACON), + HCMD_NAME(WHO_IS_AWAKE_NOTIFICATION), + HCMD_NAME(REPLY_TX_POWER_DBM_CMD), + HCMD_NAME(QUIET_NOTIFICATION), + HCMD_NAME(REPLY_TX_PWR_TABLE_CMD), + HCMD_NAME(REPLY_TX_POWER_DBM_CMD_V1), + HCMD_NAME(TX_ANT_CONFIGURATION_CMD), + HCMD_NAME(MEASURE_ABORT_NOTIFICATION), + HCMD_NAME(REPLY_BT_CONFIG), + HCMD_NAME(REPLY_STATISTICS_CMD), + HCMD_NAME(STATISTICS_NOTIFICATION), + HCMD_NAME(REPLY_CARD_STATE_CMD), + HCMD_NAME(CARD_STATE_NOTIFICATION), + HCMD_NAME(MISSED_BEACONS_NOTIFICATION), + HCMD_NAME(REPLY_CT_KILL_CONFIG_CMD), + HCMD_NAME(SENSITIVITY_CMD), + HCMD_NAME(REPLY_PHY_CALIBRATION_CMD), + HCMD_NAME(REPLY_WIPAN_PARAMS), + HCMD_NAME(REPLY_WIPAN_RXON), + HCMD_NAME(REPLY_WIPAN_RXON_TIMING), + HCMD_NAME(REPLY_WIPAN_RXON_ASSOC), + HCMD_NAME(REPLY_WIPAN_QOS_PARAM), + HCMD_NAME(REPLY_WIPAN_WEPKEY), + HCMD_NAME(REPLY_WIPAN_P2P_CHANNEL_SWITCH), + HCMD_NAME(REPLY_WIPAN_NOA_NOTIFICATION), + HCMD_NAME(REPLY_WIPAN_DEACTIVATION_COMPLETE), + HCMD_NAME(REPLY_RX_PHY_CMD), + HCMD_NAME(REPLY_RX_MPDU_CMD), + HCMD_NAME(REPLY_RX), + HCMD_NAME(REPLY_COMPRESSED_BA), + HCMD_NAME(REPLY_BT_COEX_PRIO_TABLE), + HCMD_NAME(REPLY_BT_COEX_PROT_ENV), + HCMD_NAME(REPLY_BT_COEX_PROFILE_NOTIF), + HCMD_NAME(REPLY_D3_CONFIG), + HCMD_NAME(REPLY_WOWLAN_PATTERNS), + HCMD_NAME(REPLY_WOWLAN_WAKEUP_FILTER), + HCMD_NAME(REPLY_WOWLAN_TSC_RSC_PARAMS), + HCMD_NAME(REPLY_WOWLAN_TKIP_PARAMS), + HCMD_NAME(REPLY_WOWLAN_KEK_KCK_MATERIAL), + HCMD_NAME(REPLY_WOWLAN_GET_STATUS), +}; + +static const struct iwl_hcmd_arr iwl_dvm_groups[] = { + [0x0] = HCMD_ARR(iwl_dvm_cmd_names), +}; + static const struct iwl_op_mode_ops iwl_dvm_ops; void iwl_update_chain_flags(struct iwl_priv *priv) @@ -1244,7 +1332,9 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, trans_cfg.cmd_q_wdg_timeout = IWL_WATCHDOG_DISABLED; - trans_cfg.command_names = iwl_dvm_cmd_strings; + trans_cfg.command_groups = iwl_dvm_groups; + trans_cfg.command_groups_size = ARRAY_SIZE(iwl_dvm_groups); + trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM; WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE < @@ -1265,6 +1355,8 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); + trans->command_groups = trans_cfg.command_groups; + trans->command_groups_size = trans_cfg.command_groups_size; /* At this point both hw and priv are allocated. */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c index 4a45b0b594c7..94b41e4e1dca 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portionhelp of the ieee80211 subsystem header files. @@ -32,91 +33,13 @@ #include #include #include + +#include "iwl-trans.h" #include "iwl-io.h" #include "dev.h" #include "calib.h" #include "agn.h" -#define IWL_CMD_ENTRY(x) [x] = #x - -const char *const iwl_dvm_cmd_strings[REPLY_MAX + 1] = { - IWL_CMD_ENTRY(REPLY_ALIVE), - IWL_CMD_ENTRY(REPLY_ERROR), - IWL_CMD_ENTRY(REPLY_ECHO), - IWL_CMD_ENTRY(REPLY_RXON), - IWL_CMD_ENTRY(REPLY_RXON_ASSOC), - IWL_CMD_ENTRY(REPLY_QOS_PARAM), - IWL_CMD_ENTRY(REPLY_RXON_TIMING), - IWL_CMD_ENTRY(REPLY_ADD_STA), - IWL_CMD_ENTRY(REPLY_REMOVE_STA), - IWL_CMD_ENTRY(REPLY_REMOVE_ALL_STA), - IWL_CMD_ENTRY(REPLY_TXFIFO_FLUSH), - IWL_CMD_ENTRY(REPLY_WEPKEY), - IWL_CMD_ENTRY(REPLY_TX), - IWL_CMD_ENTRY(REPLY_LEDS_CMD), - IWL_CMD_ENTRY(REPLY_TX_LINK_QUALITY_CMD), - IWL_CMD_ENTRY(COEX_PRIORITY_TABLE_CMD), - IWL_CMD_ENTRY(COEX_MEDIUM_NOTIFICATION), - IWL_CMD_ENTRY(COEX_EVENT_CMD), - IWL_CMD_ENTRY(REPLY_QUIET_CMD), - IWL_CMD_ENTRY(REPLY_CHANNEL_SWITCH), - IWL_CMD_ENTRY(CHANNEL_SWITCH_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_SPECTRUM_MEASUREMENT_CMD), - IWL_CMD_ENTRY(SPECTRUM_MEASURE_NOTIFICATION), - IWL_CMD_ENTRY(POWER_TABLE_CMD), - IWL_CMD_ENTRY(PM_SLEEP_NOTIFICATION), - IWL_CMD_ENTRY(PM_DEBUG_STATISTIC_NOTIFIC), - IWL_CMD_ENTRY(REPLY_SCAN_CMD), - IWL_CMD_ENTRY(REPLY_SCAN_ABORT_CMD), - IWL_CMD_ENTRY(SCAN_START_NOTIFICATION), - IWL_CMD_ENTRY(SCAN_RESULTS_NOTIFICATION), - IWL_CMD_ENTRY(SCAN_COMPLETE_NOTIFICATION), - IWL_CMD_ENTRY(BEACON_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_TX_BEACON), - IWL_CMD_ENTRY(WHO_IS_AWAKE_NOTIFICATION), - IWL_CMD_ENTRY(QUIET_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_TX_PWR_TABLE_CMD), - IWL_CMD_ENTRY(MEASURE_ABORT_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_BT_CONFIG), - IWL_CMD_ENTRY(REPLY_STATISTICS_CMD), - IWL_CMD_ENTRY(STATISTICS_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_CARD_STATE_CMD), - IWL_CMD_ENTRY(CARD_STATE_NOTIFICATION), - IWL_CMD_ENTRY(MISSED_BEACONS_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_CT_KILL_CONFIG_CMD), - IWL_CMD_ENTRY(SENSITIVITY_CMD), - IWL_CMD_ENTRY(REPLY_PHY_CALIBRATION_CMD), - IWL_CMD_ENTRY(REPLY_RX_PHY_CMD), - IWL_CMD_ENTRY(REPLY_RX_MPDU_CMD), - IWL_CMD_ENTRY(REPLY_COMPRESSED_BA), - IWL_CMD_ENTRY(CALIBRATION_CFG_CMD), - IWL_CMD_ENTRY(CALIBRATION_RES_NOTIFICATION), - IWL_CMD_ENTRY(CALIBRATION_COMPLETE_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_TX_POWER_DBM_CMD), - IWL_CMD_ENTRY(TEMPERATURE_NOTIFICATION), - IWL_CMD_ENTRY(TX_ANT_CONFIGURATION_CMD), - IWL_CMD_ENTRY(REPLY_BT_COEX_PROFILE_NOTIF), - IWL_CMD_ENTRY(REPLY_BT_COEX_PRIO_TABLE), - IWL_CMD_ENTRY(REPLY_BT_COEX_PROT_ENV), - IWL_CMD_ENTRY(REPLY_WIPAN_PARAMS), - IWL_CMD_ENTRY(REPLY_WIPAN_RXON), - IWL_CMD_ENTRY(REPLY_WIPAN_RXON_TIMING), - IWL_CMD_ENTRY(REPLY_WIPAN_RXON_ASSOC), - IWL_CMD_ENTRY(REPLY_WIPAN_QOS_PARAM), - IWL_CMD_ENTRY(REPLY_WIPAN_WEPKEY), - IWL_CMD_ENTRY(REPLY_WIPAN_P2P_CHANNEL_SWITCH), - IWL_CMD_ENTRY(REPLY_WIPAN_NOA_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_WIPAN_DEACTIVATION_COMPLETE), - IWL_CMD_ENTRY(REPLY_WOWLAN_PATTERNS), - IWL_CMD_ENTRY(REPLY_WOWLAN_WAKEUP_FILTER), - IWL_CMD_ENTRY(REPLY_WOWLAN_TSC_RSC_PARAMS), - IWL_CMD_ENTRY(REPLY_WOWLAN_TKIP_PARAMS), - IWL_CMD_ENTRY(REPLY_WOWLAN_KEK_KCK_MATERIAL), - IWL_CMD_ENTRY(REPLY_WOWLAN_GET_STATUS), - IWL_CMD_ENTRY(REPLY_D3_CONFIG), -}; -#undef IWL_CMD_ENTRY - /****************************************************************************** * * Generic RX handler implementations @@ -1095,7 +1018,9 @@ void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct napi_struct *napi, } else { /* No handling needed */ IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n", - iwl_dvm_get_cmd_string(pkt->hdr.cmd), + iwl_get_cmd_string(priv->trans, + iwl_cmd_id(pkt->hdr.cmd, + 0, 0)), pkt->hdr.cmd); } } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index c3fafbc6b654..6069a9ff53fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -61,8 +61,10 @@ * *****************************************************************************/ #include -#include "iwl-drv.h" +#include + #include "iwl-trans.h" +#include "iwl-drv.h" struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, struct device *dev, @@ -145,3 +147,59 @@ int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) return ret; } IWL_EXPORT_SYMBOL(iwl_trans_send_cmd); + +/* Comparator for struct iwl_hcmd_names. + * Used in the binary search over a list of host commands. + * + * @key: command_id that we're looking for. + * @elt: struct iwl_hcmd_names candidate for match. + * + * @return 0 iff equal. + */ +static int iwl_hcmd_names_cmp(const void *key, const void *elt) +{ + const struct iwl_hcmd_names *name = elt; + u8 cmd1 = *(u8 *)key; + u8 cmd2 = name->cmd_id; + + return (cmd1 - cmd2); +} + +const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id) +{ + u8 grp, cmd; + struct iwl_hcmd_names *ret; + const struct iwl_hcmd_arr *arr; + size_t size = sizeof(struct iwl_hcmd_names); + + grp = iwl_cmd_groupid(id); + cmd = iwl_cmd_opcode(id); + + if (!trans->command_groups || grp >= trans->command_groups_size || + !trans->command_groups[grp].arr) + return "UNKNOWN"; + + arr = &trans->command_groups[grp]; + ret = bsearch(&cmd, arr->arr, arr->size, size, iwl_hcmd_names_cmp); + if (!ret) + return "UNKNOWN"; + return ret->cmd_name; +} +IWL_EXPORT_SYMBOL(iwl_get_cmd_string); + +int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans) +{ + int i, j; + const struct iwl_hcmd_arr *arr; + + for (i = 0; i < trans->command_groups_size; i++) { + arr = &trans->command_groups[i]; + if (!arr->arr) + continue; + for (j = 0; j < arr->size - 1; j++) + if (arr->arr[j].cmd_id > arr->arr[j + 1].cmd_id) + return -1; + } + return 0; +} +IWL_EXPORT_SYMBOL(iwl_cmd_groups_verify_sorted); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index b825d940cc58..f00c9ad9d3c2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -68,6 +68,7 @@ #include #include /* for page_address */ #include +#include #include "iwl-debug.h" #include "iwl-config.h" @@ -442,6 +443,22 @@ iwl_trans_get_rb_size_order(enum iwl_amsdu_size rb_size) } } +struct iwl_hcmd_names { + u8 cmd_id; + const char *const cmd_name; +}; + +#define HCMD_NAME(x) \ + { .cmd_id = x, .cmd_name = #x } + +struct iwl_hcmd_arr { + const struct iwl_hcmd_names *arr; + int size; +}; + +#define HCMD_ARR(x) \ + { .arr = x, .size = ARRAY_SIZE(x) } + /** * struct iwl_trans_config - transport configuration * @@ -461,8 +478,9 @@ iwl_trans_get_rb_size_order(enum iwl_amsdu_size rb_size) * in DWORD (as opposed to bytes) * @scd_set_active: should the transport configure the SCD for HCMD queue * @wide_cmd_header: firmware supports wide host command header - * @command_names: array of command names, must be 256 entries - * (one for each command); for debugging only + * @command_groups: array of command groups, each member is an array of the + * commands in the group; for debugging only + * @command_groups_size: number of command groups, to avoid illegal access * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until * we get the ALIVE from the uCode */ @@ -479,8 +497,9 @@ struct iwl_trans_config { bool bc_table_dword; bool scd_set_active; bool wide_cmd_header; - const char *const *command_names; - + const struct iwl_hcmd_arr *command_groups; + int command_groups_size; + u32 sdio_adma_addr; }; @@ -720,6 +739,9 @@ struct iwl_trans { bool pm_support; bool ltr_enabled; + const struct iwl_hcmd_arr *command_groups; + int command_groups_size; + u8 num_rx_queues; /* The following fields are internal only */ @@ -757,12 +779,16 @@ struct iwl_trans { char trans_specific[0] __aligned(sizeof(void *)); }; +const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id); +int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans); + static inline void iwl_trans_configure(struct iwl_trans *trans, const struct iwl_trans_config *trans_cfg) { trans->op_mode = trans_cfg->op_mode; trans->ops->configure(trans, trans_cfg); + WARN_ON(iwl_cmd_groups_verify_sorted(trans_cfg)); } static inline int _iwl_trans_start_hw(struct iwl_trans *trans, bool low_power) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 9436798977a0..3f7478b54300 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -270,6 +270,9 @@ enum { REPLY_MAX = 0xff, }; +/* Please keep this enum *SORTED* by hex value. + * Needed for binary search, otherwise a warning will be triggered. + */ enum iwl_phy_ops_subcmd_ids { CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, @@ -277,6 +280,8 @@ enum iwl_phy_ops_subcmd_ids { /* command groups */ enum { + LEGACY_GROUP = 0x0, + LONG_GROUP = 0x1, PHY_OPS_GROUP = 0x4, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 4e417fd9beb6..958984cc4d2e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -83,6 +83,8 @@ #include "fw-api-scan.h" #include "time-event.h" #include "fw-dbg.h" +#include "fw-api.h" +#include "fw-api-scan.h" #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" MODULE_DESCRIPTION(DRV_DESCRIPTION); @@ -269,104 +271,127 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { }; #undef RX_HANDLER #undef RX_HANDLER_GRP -#define CMD(x) [x] = #x - -static const char *const iwl_mvm_cmd_strings[REPLY_MAX + 1] = { - CMD(MVM_ALIVE), - CMD(REPLY_ERROR), - CMD(ECHO_CMD), - CMD(INIT_COMPLETE_NOTIF), - CMD(PHY_CONTEXT_CMD), - CMD(MGMT_MCAST_KEY), - CMD(TX_CMD), - CMD(TXPATH_FLUSH), - CMD(SHARED_MEM_CFG), - CMD(MAC_CONTEXT_CMD), - CMD(TIME_EVENT_CMD), - CMD(TIME_EVENT_NOTIFICATION), - CMD(BINDING_CONTEXT_CMD), - CMD(TIME_QUOTA_CMD), - CMD(NON_QOS_TX_COUNTER_CMD), - CMD(DC2DC_CONFIG_CMD), - CMD(NVM_ACCESS_CMD), - CMD(PHY_CONFIGURATION_CMD), - CMD(CALIB_RES_NOTIF_PHY_DB), - CMD(SET_CALIB_DEFAULT_CMD), - CMD(FW_PAGING_BLOCK_CMD), - CMD(ADD_STA_KEY), - CMD(ADD_STA), - CMD(FW_GET_ITEM_CMD), - CMD(REMOVE_STA), - CMD(LQ_CMD), - CMD(SCAN_OFFLOAD_CONFIG_CMD), - CMD(MATCH_FOUND_NOTIFICATION), - CMD(SCAN_OFFLOAD_REQUEST_CMD), - CMD(SCAN_OFFLOAD_ABORT_CMD), - CMD(HOT_SPOT_CMD), - CMD(SCAN_OFFLOAD_COMPLETE), - CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD), - CMD(SCAN_ITERATION_COMPLETE), - CMD(POWER_TABLE_CMD), - CMD(WEP_KEY), - CMD(REPLY_RX_PHY_CMD), - CMD(REPLY_RX_MPDU_CMD), - CMD(FRAME_RELEASE), - CMD(BEACON_NOTIFICATION), - CMD(BEACON_TEMPLATE_CMD), - CMD(STATISTICS_CMD), - CMD(STATISTICS_NOTIFICATION), - CMD(EOSP_NOTIFICATION), - CMD(REDUCE_TX_POWER_CMD), - CMD(TX_ANT_CONFIGURATION_CMD), - CMD(D3_CONFIG_CMD), - CMD(D0I3_END_CMD), - CMD(PROT_OFFLOAD_CONFIG_CMD), - CMD(OFFLOADS_QUERY_CMD), - CMD(REMOTE_WAKE_CONFIG_CMD), - CMD(WOWLAN_PATTERNS), - CMD(WOWLAN_CONFIGURATION), - CMD(WOWLAN_TSC_RSC_PARAM), - CMD(WOWLAN_TKIP_PARAM), - CMD(WOWLAN_KEK_KCK_MATERIAL), - CMD(WOWLAN_GET_STATUSES), - CMD(WOWLAN_TX_POWER_PER_DB), - CMD(SCAN_OFFLOAD_PROFILES_QUERY_CMD), - CMD(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD), - CMD(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD), - CMD(CARD_STATE_NOTIFICATION), - CMD(MISSED_BEACONS_NOTIFICATION), - CMD(BT_COEX_PRIO_TABLE), - CMD(BT_COEX_PROT_ENV), - CMD(BT_PROFILE_NOTIFICATION), - CMD(BT_CONFIG), - CMD(MCAST_FILTER_CMD), - CMD(BCAST_FILTER_CMD), - CMD(REPLY_SF_CFG_CMD), - CMD(REPLY_BEACON_FILTERING_CMD), - CMD(CMD_DTS_MEASUREMENT_TRIGGER), - CMD(DTS_MEASUREMENT_NOTIFICATION), - CMD(REPLY_THERMAL_MNG_BACKOFF), - CMD(MAC_PM_POWER_TABLE), - CMD(LTR_CONFIG), - CMD(BT_COEX_CI), - CMD(BT_COEX_UPDATE_SW_BOOST), - CMD(BT_COEX_UPDATE_CORUN_LUT), - CMD(BT_COEX_UPDATE_REDUCED_TXP), - CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), - CMD(ANTENNA_COUPLING_NOTIFICATION), - CMD(SCD_QUEUE_CFG), - CMD(SCAN_CFG_CMD), - CMD(SCAN_REQ_UMAC), - CMD(SCAN_ABORT_UMAC), - CMD(SCAN_COMPLETE_UMAC), - CMD(TDLS_CHANNEL_SWITCH_CMD), - CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION), - CMD(TDLS_CONFIG_CMD), - CMD(MCC_UPDATE_CMD), - CMD(SCAN_ITERATION_COMPLETE_UMAC), - CMD(LDBG_CONFIG_CMD), + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = { + HCMD_NAME(MVM_ALIVE), + HCMD_NAME(REPLY_ERROR), + HCMD_NAME(ECHO_CMD), + HCMD_NAME(INIT_COMPLETE_NOTIF), + HCMD_NAME(PHY_CONTEXT_CMD), + HCMD_NAME(DBG_CFG), + HCMD_NAME(ANTENNA_COUPLING_NOTIFICATION), + HCMD_NAME(SCAN_CFG_CMD), + HCMD_NAME(SCAN_REQ_UMAC), + HCMD_NAME(SCAN_ABORT_UMAC), + HCMD_NAME(SCAN_COMPLETE_UMAC), + HCMD_NAME(TOF_CMD), + HCMD_NAME(TOF_NOTIFICATION), + HCMD_NAME(ADD_STA_KEY), + HCMD_NAME(ADD_STA), + HCMD_NAME(REMOVE_STA), + HCMD_NAME(FW_GET_ITEM_CMD), + HCMD_NAME(TX_CMD), + HCMD_NAME(SCD_QUEUE_CFG), + HCMD_NAME(TXPATH_FLUSH), + HCMD_NAME(MGMT_MCAST_KEY), + HCMD_NAME(WEP_KEY), + HCMD_NAME(SHARED_MEM_CFG), + HCMD_NAME(TDLS_CHANNEL_SWITCH_CMD), + HCMD_NAME(MAC_CONTEXT_CMD), + HCMD_NAME(TIME_EVENT_CMD), + HCMD_NAME(TIME_EVENT_NOTIFICATION), + HCMD_NAME(BINDING_CONTEXT_CMD), + HCMD_NAME(TIME_QUOTA_CMD), + HCMD_NAME(NON_QOS_TX_COUNTER_CMD), + HCMD_NAME(LQ_CMD), + HCMD_NAME(FW_PAGING_BLOCK_CMD), + HCMD_NAME(SCAN_OFFLOAD_REQUEST_CMD), + HCMD_NAME(SCAN_OFFLOAD_ABORT_CMD), + HCMD_NAME(HOT_SPOT_CMD), + HCMD_NAME(SCAN_OFFLOAD_PROFILES_QUERY_CMD), + HCMD_NAME(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD), + HCMD_NAME(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD), + HCMD_NAME(BT_COEX_UPDATE_SW_BOOST), + HCMD_NAME(BT_COEX_UPDATE_CORUN_LUT), + HCMD_NAME(BT_COEX_UPDATE_REDUCED_TXP), + HCMD_NAME(BT_COEX_CI), + HCMD_NAME(PHY_CONFIGURATION_CMD), + HCMD_NAME(CALIB_RES_NOTIF_PHY_DB), + HCMD_NAME(SCAN_OFFLOAD_COMPLETE), + HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD), + HCMD_NAME(SCAN_OFFLOAD_CONFIG_CMD), + HCMD_NAME(POWER_TABLE_CMD), + HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), + HCMD_NAME(REPLY_THERMAL_MNG_BACKOFF), + HCMD_NAME(DC2DC_CONFIG_CMD), + HCMD_NAME(NVM_ACCESS_CMD), + HCMD_NAME(SET_CALIB_DEFAULT_CMD), + HCMD_NAME(BEACON_NOTIFICATION), + HCMD_NAME(BEACON_TEMPLATE_CMD), + HCMD_NAME(TX_ANT_CONFIGURATION_CMD), + HCMD_NAME(BT_CONFIG), + HCMD_NAME(STATISTICS_CMD), + HCMD_NAME(STATISTICS_NOTIFICATION), + HCMD_NAME(EOSP_NOTIFICATION), + HCMD_NAME(REDUCE_TX_POWER_CMD), + HCMD_NAME(CARD_STATE_CMD), + HCMD_NAME(CARD_STATE_NOTIFICATION), + HCMD_NAME(MISSED_BEACONS_NOTIFICATION), + HCMD_NAME(TDLS_CONFIG_CMD), + HCMD_NAME(MAC_PM_POWER_TABLE), + HCMD_NAME(TDLS_CHANNEL_SWITCH_NOTIFICATION), + HCMD_NAME(MFUART_LOAD_NOTIFICATION), + HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC), + HCMD_NAME(REPLY_RX_PHY_CMD), + HCMD_NAME(REPLY_RX_MPDU_CMD), + HCMD_NAME(BA_NOTIF), + HCMD_NAME(MCC_UPDATE_CMD), + HCMD_NAME(MCC_CHUB_UPDATE_CMD), + HCMD_NAME(MARKER_CMD), + HCMD_NAME(BT_COEX_PRIO_TABLE), + HCMD_NAME(BT_COEX_PROT_ENV), + HCMD_NAME(BT_PROFILE_NOTIFICATION), + HCMD_NAME(BCAST_FILTER_CMD), + HCMD_NAME(MCAST_FILTER_CMD), + HCMD_NAME(REPLY_SF_CFG_CMD), + HCMD_NAME(REPLY_BEACON_FILTERING_CMD), + HCMD_NAME(D3_CONFIG_CMD), + HCMD_NAME(PROT_OFFLOAD_CONFIG_CMD), + HCMD_NAME(OFFLOADS_QUERY_CMD), + HCMD_NAME(REMOTE_WAKE_CONFIG_CMD), + HCMD_NAME(MATCH_FOUND_NOTIFICATION), + HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER), + HCMD_NAME(DTS_MEASUREMENT_NOTIFICATION), + HCMD_NAME(WOWLAN_PATTERNS), + HCMD_NAME(WOWLAN_CONFIGURATION), + HCMD_NAME(WOWLAN_TSC_RSC_PARAM), + HCMD_NAME(WOWLAN_TKIP_PARAM), + HCMD_NAME(WOWLAN_KEK_KCK_MATERIAL), + HCMD_NAME(WOWLAN_GET_STATUSES), + HCMD_NAME(WOWLAN_TX_POWER_PER_DB), + HCMD_NAME(SCAN_ITERATION_COMPLETE), + HCMD_NAME(D0I3_END_CMD), + HCMD_NAME(LTR_CONFIG), + HCMD_NAME(REPLY_DEBUG_CMD), }; -#undef CMD + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mvm_phy_names[] = { + HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE), + HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE), +}; + +static const struct iwl_hcmd_arr iwl_mvm_groups[] = { + [LEGACY_GROUP] = HCMD_ARR(iwl_mvm_legacy_names), + [LONG_GROUP] = HCMD_ARR(iwl_mvm_legacy_names), + [PHY_OPS_GROUP] = HCMD_ARR(iwl_mvm_phy_names), +}; + /* this forward declaration can avoid to export the function */ static void iwl_mvm_async_handlers_wk(struct work_struct *wk); @@ -508,7 +533,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) trans_cfg.bc_table_dword = true; - trans_cfg.command_names = iwl_mvm_cmd_strings; + trans_cfg.command_groups = iwl_mvm_groups; + trans_cfg.command_groups_size = ARRAY_SIZE(iwl_mvm_groups); trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 31f30a2dd654..0aceb9f3e49d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -363,8 +363,6 @@ struct iwl_trans_pcie { bool wide_cmd_header; u32 rx_page_order; - const char *const *command_names; - /*protect hw register */ spinlock_t reg_lock; bool cmd_hold_nic_awake; @@ -527,14 +525,6 @@ static inline u8 get_cmd_index(struct iwl_queue *q, u32 index) return index & (q->n_window - 1); } -static inline const char *get_cmd_string(struct iwl_trans_pcie *trans_pcie, - u8 cmd) -{ - if (!trans_pcie->command_names || !trans_pcie->command_names[cmd]) - return "UNKNOWN"; - return trans_pcie->command_names[cmd]; -} - static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) { return !(iwl_read32(trans, CSR_GP_CNTRL) & diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 9193f0c7f104..ccafbd8cf4b3 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -877,7 +877,10 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x, seq 0x%x)\n", rxcb._offset, - get_cmd_string(trans_pcie, pkt->hdr.cmd), + iwl_get_cmd_string(trans, + iwl_cmd_id(pkt->hdr.cmd, + pkt->hdr.group_id, + 0)), pkt->hdr.cmd, le16_to_cpu(pkt->hdr.sequence)); len = iwl_rx_packet_len(pkt); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 316162a45989..0d927635d0dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1440,10 +1440,12 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, iwl_trans_get_rb_size_order(trans_pcie->rx_buf_size); trans_pcie->wide_cmd_header = trans_cfg->wide_cmd_header; - trans_pcie->command_names = trans_cfg->command_names; trans_pcie->bc_table_dword = trans_cfg->bc_table_dword; trans_pcie->scd_set_active = trans_cfg->scd_set_active; + trans->command_groups = trans_cfg->command_groups; + trans->command_groups_size = trans_cfg->command_groups_size; + /* init ref_count to 1 (should be cleared when ucode is loaded) */ trans_pcie->ref_count = 1; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index fe8241ff8da2..f2277c7dd6b2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -1413,7 +1413,8 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, */ if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE, "Command %s (%#x) is too large (%d bytes)\n", - get_cmd_string(trans_pcie, cmd->id), cmd->id, copy_size)) { + iwl_get_cmd_string(trans, cmd->id), + cmd->id, copy_size)) { idx = -EINVAL; goto free_dup_buf; } @@ -1503,7 +1504,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, IWL_DEBUG_HC(trans, "Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", - get_cmd_string(trans_pcie, out_cmd->hdr.cmd), + iwl_get_cmd_string(trans, cmd->id), group_id, out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); @@ -1599,6 +1600,8 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, { struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); + u8 group_id = iwl_cmd_groupid(pkt->hdr.group_id); + u32 cmd_id; int txq_id = SEQ_TO_QUEUE(sequence); int index = SEQ_TO_INDEX(sequence); int cmd_index; @@ -1624,6 +1627,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, cmd_index = get_cmd_index(&txq->q, index); cmd = txq->entries[cmd_index].cmd; meta = &txq->entries[cmd_index].meta; + cmd_id = iwl_cmd_id(cmd->hdr.cmd, group_id, 0); iwl_pcie_tfd_unmap(trans, meta, &txq->tfds[index]); @@ -1645,11 +1649,11 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, if (!test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status)) { IWL_WARN(trans, "HCMD_ACTIVE already clear for command %s\n", - get_cmd_string(trans_pcie, cmd->hdr.cmd)); + iwl_get_cmd_string(trans, cmd_id)); } clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", - get_cmd_string(trans_pcie, cmd->hdr.cmd)); + iwl_get_cmd_string(trans, cmd_id)); wake_up(&trans_pcie->wait_command_queue); } @@ -1663,7 +1667,6 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; /* An asynchronous command can not expect an SKB to be set. */ @@ -1674,7 +1677,7 @@ static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans, if (ret < 0) { IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", - get_cmd_string(trans_pcie, cmd->id), ret); + iwl_get_cmd_string(trans, cmd->id), ret); return ret; } return 0; @@ -1688,16 +1691,16 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, int ret; IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", - get_cmd_string(trans_pcie, cmd->id)); + iwl_get_cmd_string(trans, cmd->id)); if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status), "Command %s: a command is already active!\n", - get_cmd_string(trans_pcie, cmd->id))) + iwl_get_cmd_string(trans, cmd->id))) return -EIO; IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", - get_cmd_string(trans_pcie, cmd->id)); + iwl_get_cmd_string(trans, cmd->id)); cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd); if (cmd_idx < 0) { @@ -1705,7 +1708,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", - get_cmd_string(trans_pcie, cmd->id), ret); + iwl_get_cmd_string(trans, cmd->id), ret); return ret; } @@ -1718,7 +1721,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, struct iwl_queue *q = &txq->q; IWL_ERR(trans, "Error sending %s: time out after %dms.\n", - get_cmd_string(trans_pcie, cmd->id), + iwl_get_cmd_string(trans, cmd->id), jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n", @@ -1726,7 +1729,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", - get_cmd_string(trans_pcie, cmd->id)); + iwl_get_cmd_string(trans, cmd->id)); ret = -ETIMEDOUT; iwl_force_nmi(trans); @@ -1737,7 +1740,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, if (test_bit(STATUS_FW_ERROR, &trans->status)) { IWL_ERR(trans, "FW error in SYNC CMD %s\n", - get_cmd_string(trans_pcie, cmd->id)); + iwl_get_cmd_string(trans, cmd->id)); dump_stack(); ret = -EIO; goto cancel; @@ -1752,7 +1755,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { IWL_ERR(trans, "Error: Response NULL in '%s'\n", - get_cmd_string(trans_pcie, cmd->id)); + iwl_get_cmd_string(trans, cmd->id)); ret = -EIO; goto cancel; } -- cgit v1.2.3-59-g8ed1b From e09b89069ff9e3877bdbca8e64da05cde88bc3a2 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Thu, 23 Jul 2015 14:54:34 -0700 Subject: e1000: make eeprom read/write scheduler friendly Code was responsible for ~150ms scheduler latencies. Signed-off-by: Joern Engel Signed-off-by: Spencer Baugh Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_hw.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c index b1af0d613caa..2523e301c26c 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_hw.c +++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c @@ -106,7 +106,7 @@ u16 e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] = { 120, 120 }; -static DEFINE_SPINLOCK(e1000_eeprom_lock); +static DEFINE_MUTEX(e1000_eeprom_lock); static DEFINE_SPINLOCK(e1000_phy_lock); /** @@ -3882,9 +3882,9 @@ static s32 e1000_spi_eeprom_ready(struct e1000_hw *hw) s32 e1000_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { s32 ret; - spin_lock(&e1000_eeprom_lock); + mutex_lock(&e1000_eeprom_lock); ret = e1000_do_read_eeprom(hw, offset, words, data); - spin_unlock(&e1000_eeprom_lock); + mutex_unlock(&e1000_eeprom_lock); return ret; } @@ -3968,6 +3968,7 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, */ data[i] = e1000_shift_in_ee_bits(hw, 16); e1000_standby_eeprom(hw); + cond_resched(); } } @@ -4052,9 +4053,9 @@ s32 e1000_update_eeprom_checksum(struct e1000_hw *hw) s32 e1000_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { s32 ret; - spin_lock(&e1000_eeprom_lock); + mutex_lock(&e1000_eeprom_lock); ret = e1000_do_write_eeprom(hw, offset, words, data); - spin_unlock(&e1000_eeprom_lock); + mutex_unlock(&e1000_eeprom_lock); return ret; } @@ -4116,6 +4117,7 @@ static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words, return -E1000_ERR_EEPROM; e1000_standby_eeprom(hw); + cond_resched(); /* Send the WRITE ENABLE command (8 bit opcode ) */ e1000_shift_out_ee_bits(hw, EEPROM_WREN_OPCODE_SPI, @@ -4224,6 +4226,7 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset, /* Recover from write */ e1000_standby_eeprom(hw); + cond_resched(); words_written++; } -- cgit v1.2.3-59-g8ed1b From 18f7ce5412027232890143ccfae23668d0872d27 Mon Sep 17 00:00:00 2001 From: Todd Fujinaka Date: Wed, 2 Sep 2015 16:54:20 -0700 Subject: igb: add 88E1543 initialization code Initialize the 88E1543 PHY. Signed-off-by: Todd Fujinaka Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_82575.c | 7 ++ drivers/net/ethernet/intel/igb/e1000_defines.h | 1 + drivers/net/ethernet/intel/igb/e1000_phy.c | 94 ++++++++++++++++++++++++++ drivers/net/ethernet/intel/igb/e1000_phy.h | 1 + 4 files changed, 103 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index 7a73510e547c..d9664c47fe3a 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -272,6 +272,11 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw) if (ret_val) goto out; } + if (phy->id == M88E1543_E_PHY_ID) { + ret_val = igb_initialize_M88E1543_phy(hw); + if (ret_val) + goto out; + } break; case IGP03E1000_E_PHY_ID: phy->type = e1000_phy_igp_3; @@ -925,6 +930,8 @@ static s32 igb_phy_hw_reset_sgmii_82575(struct e1000_hw *hw) if (phy->id == M88E1512_E_PHY_ID) ret_val = igb_initialize_M88E1512_phy(hw); + if (phy->id == M88E1543_E_PHY_ID) + ret_val = igb_initialize_M88E1543_phy(hw); out: return ret_val; } diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index b1915043bc0c..a61ee9462dd4 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -990,6 +990,7 @@ #define E1000_M88E1543_PAGE_ADDR 0x16 /* Page Offset Register */ #define E1000_M88E1543_EEE_CTRL_1 0x0 #define E1000_M88E1543_EEE_CTRL_1_MS 0x0001 /* EEE Master/Slave */ +#define E1000_M88E1543_FIBER_CTRL 0x0 #define E1000_EEE_ADV_DEV_I354 7 #define E1000_EEE_ADV_ADDR_I354 60 #define E1000_EEE_ADV_100_SUPPORTED (1 << 1) /* 100BaseTx EEE Supported */ diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index 23ec28f43f6d..c0df40f2b295 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -2277,6 +2277,100 @@ out: return ret_val; } +/** + * igb_initialize_M88E1543_phy - Initialize M88E1512 PHY + * @hw: pointer to the HW structure + * + * Initialize Marvell 1543 to work correctly with Avoton. + **/ +s32 igb_initialize_M88E1543_phy(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = 0; + + /* Switch to PHY page 0xFF. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x00FF); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0x214B); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2144); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0x0C28); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2146); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0xB233); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x214D); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0xDC0C); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2159); + if (ret_val) + goto out; + + /* Switch to PHY page 0xFB. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x00FB); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_3, 0x0C0D); + if (ret_val) + goto out; + + /* Switch to PHY page 0x12. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x12); + if (ret_val) + goto out; + + /* Change mode to SGMII-to-Copper */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_MODE, 0x8001); + if (ret_val) + goto out; + + /* Switch to PHY page 1. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x1); + if (ret_val) + goto out; + + /* Change mode to 1000BASE-X/SGMII and autoneg enable */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_FIBER_CTRL, 0x9140); + if (ret_val) + goto out; + + /* Return the PHY to page 0. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0); + if (ret_val) + goto out; + + ret_val = igb_phy_sw_reset(hw); + if (ret_val) { + hw_dbg("Error committing the PHY changes\n"); + return ret_val; + } + + /* msec_delay(1000); */ + usleep_range(1000, 2000); +out: + return ret_val; +} + /** * igb_power_up_phy_copper - Restore copper link in case of PHY power down * @hw: pointer to the HW structure diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h index 24d55edbb0e3..aa1ae61a61d8 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.h +++ b/drivers/net/ethernet/intel/igb/e1000_phy.h @@ -62,6 +62,7 @@ void igb_power_up_phy_copper(struct e1000_hw *hw); void igb_power_down_phy_copper(struct e1000_hw *hw); s32 igb_phy_init_script_igp3(struct e1000_hw *hw); s32 igb_initialize_M88E1512_phy(struct e1000_hw *hw); +s32 igb_initialize_M88E1543_phy(struct e1000_hw *hw); s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data); s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data); -- cgit v1.2.3-59-g8ed1b From 9eab46b7cb8d0b0dcf014bf7b25e0e72b9e4d929 Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Tue, 8 Sep 2015 10:52:44 +0200 Subject: e1000: fix data race between tx_ring->next_to_clean e1000_clean_tx_irq cleans buffers and sets tx_ring->next_to_clean, then e1000_xmit_frame reuses the cleaned buffers. But there are no memory barriers when buffers gets recycled, so the recycled buffers can be corrupted. Use smp_store_release to update tx_ring->next_to_clean and smp_load_acquire to read tx_ring->next_to_clean to properly hand off buffers from e1000_clean_tx_irq to e1000_xmit_frame. The data race was found with KernelThreadSanitizer (KTSAN). Signed-off-by: Dmitry Vyukov Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000.h | 7 +++++-- drivers/net/ethernet/intel/e1000/e1000_main.c | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000/e1000.h b/drivers/net/ethernet/intel/e1000/e1000.h index 69707108d23c..98fe5a2cd6e3 100644 --- a/drivers/net/ethernet/intel/e1000/e1000.h +++ b/drivers/net/ethernet/intel/e1000/e1000.h @@ -213,8 +213,11 @@ struct e1000_rx_ring { }; #define E1000_DESC_UNUSED(R) \ - ((((R)->next_to_clean > (R)->next_to_use) \ - ? 0 : (R)->count) + (R)->next_to_clean - (R)->next_to_use - 1) +({ \ + unsigned int clean = smp_load_acquire(&(R)->next_to_clean); \ + unsigned int use = READ_ONCE((R)->next_to_use); \ + (clean > use ? 0 : (R)->count) + clean - use - 1; \ +}) #define E1000_RX_DESC_EXT(R, i) \ (&(((union e1000_rx_desc_extended *)((R).desc))[i])) diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index fd7be860c201..068023595d84 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -3876,7 +3876,10 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, eop_desc = E1000_TX_DESC(*tx_ring, eop); } - tx_ring->next_to_clean = i; + /* Synchronize with E1000_DESC_UNUSED called from e1000_xmit_frame, + * which will reuse the cleaned buffers. + */ + smp_store_release(&tx_ring->next_to_clean, i); netdev_completed_queue(netdev, pkts_compl, bytes_compl); -- cgit v1.2.3-59-g8ed1b From 73bf8048d7c86a20a59d427e55deb1a778e94df7 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 10 Sep 2015 15:37:50 -0400 Subject: igb: don't unmap NULL hw_addr I've got a startech thunderbolt dock someone loaned me, which among other things, has the following device in it: 08:00.0 Ethernet controller: Intel Corporation I210 Gigabit Network Connection (rev 03) This hotplugs just fine (kernel 4.2.0 plus a patch or two here): [ 863.020315] igb: Intel(R) Gigabit Ethernet Network Driver - version 5.2.18-k [ 863.020316] igb: Copyright (c) 2007-2014 Intel Corporation. [ 863.028657] igb 0000:08:00.0: enabling device (0000 -> 0002) [ 863.062089] igb 0000:08:00.0: added PHC on eth0 [ 863.062090] igb 0000:08:00.0: Intel(R) Gigabit Ethernet Network Connection [ 863.062091] igb 0000:08:00.0: eth0: (PCIe:2.5Gb/s:Width x1) e8:ea:6a:00:1b:2a [ 863.062194] igb 0000:08:00.0: eth0: PBA No: 000200-000 [ 863.062196] igb 0000:08:00.0: Using MSI-X interrupts. 4 rx queue(s), 4 tx queue(s) [ 863.064889] igb 0000:08:00.0 enp8s0: renamed from eth0 But disconnecting it is another story: [ 1002.807932] igb 0000:08:00.0: removed PHC on enp8s0 [ 1002.807944] igb 0000:08:00.0 enp8s0: PCIe link lost, device now detached [ 1003.341141] ------------[ cut here ]------------ [ 1003.341148] WARNING: CPU: 0 PID: 199 at lib/iomap.c:43 bad_io_access+0x38/0x40() [ 1003.341149] Bad IO access at port 0x0 () [ 1003.342767] Modules linked in: snd_usb_audio snd_usbmidi_lib snd_rawmidi igb dca firewire_ohci firewire_core crc_itu_t rfcomm ctr ccm arc4 iwlmvm mac80211 fuse xt_CHECKSUM ipt_MASQUERADE nf_nat_masquerade_ipv4 tun ip6t_rpfilter ip6t_REJECT nf_reject_ipv6 ipt_REJECT nf_reject_ipv4 xt_conntrack ebtable_nat ebtable_broute bridge stp llc ebtable_filter ebtables ip6table_nat nf_conntrack_ipv6 nf_defrag_ipv6 nf_nat_ipv6 ip6table_mangle ip6table_security ip6table_raw ip6table_filter ip6_tables iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack iptable_mangle iptable_security iptable_raw iptable_filter bnep dm_mirror dm_region_hash dm_log dm_mod coretemp x86_pkg_temp_thermal intel_powerclamp kvm_intel snd_hda_codec_hdmi kvm crct10dif_pclmul crc32_pclmul ghash_clmulni_intel drbg [ 1003.342793] ansi_cprng aesni_intel hp_wmi aes_x86_64 iTCO_wdt lrw iTCO_vendor_support ppdev gf128mul sparse_keymap glue_helper ablk_helper cryptd snd_hda_codec_realtek snd_hda_codec_generic microcode snd_hda_intel uvcvideo iwlwifi snd_hda_codec videobuf2_vmalloc videobuf2_memops snd_hda_core videobuf2_core snd_hwdep btusb v4l2_common btrtl snd_seq btbcm btintel videodev cfg80211 snd_seq_device rtsx_pci_ms bluetooth pcspkr input_leds i2c_i801 media parport_pc memstick rfkill sg lpc_ich snd_pcm 8250_fintek parport joydev snd_timer snd soundcore hp_accel ie31200_edac mei_me lis3lv02d edac_core input_polldev mei hp_wireless shpchp tpm_infineon sch_fq_codel nfsd auth_rpcgss nfs_acl lockd grace sunrpc ip_tables autofs4 xfs libcrc32c sd_mod sr_mod cdrom rtsx_pci_sdmmc mmc_core crc32c_intel serio_raw rtsx_pci [ 1003.342822] nouveau ahci libahci mxm_wmi e1000e xhci_pci hwmon ptp drm_kms_helper pps_core xhci_hcd ttm wmi video ipv6 [ 1003.342839] CPU: 0 PID: 199 Comm: kworker/0:2 Not tainted 4.2.0-2.el7_UNSUPPORTED.x86_64 #1 [ 1003.342840] Hardware name: Hewlett-Packard HP ZBook 15 G2/2253, BIOS M70 Ver. 01.07 02/26/2015 [ 1003.342843] Workqueue: pciehp-3 pciehp_power_thread [ 1003.342844] ffffffff81a90655 ffff8804866d3b48 ffffffff8164763a 0000000000000000 [ 1003.342846] ffff8804866d3b98 ffff8804866d3b88 ffffffff8107134a ffff8804866d3b88 [ 1003.342847] ffff880486f46000 ffff88046c8a8000 ffff880486f46840 ffff88046c8a8098 [ 1003.342848] Call Trace: [ 1003.342852] [] dump_stack+0x45/0x57 [ 1003.342855] [] warn_slowpath_common+0x8a/0xc0 [ 1003.342857] [] warn_slowpath_fmt+0x46/0x50 [ 1003.342859] [] ? pci_disable_msix+0x3e/0x50 [ 1003.342860] [] bad_io_access+0x38/0x40 [ 1003.342861] [] pci_iounmap+0x27/0x40 [ 1003.342865] [] igb_remove+0xc7/0x160 [igb] [ 1003.342867] [] pci_device_remove+0x3f/0xc0 [ 1003.342869] [] __device_release_driver+0x96/0x130 [ 1003.342870] [] device_release_driver+0x23/0x30 [ 1003.342871] [] pci_stop_bus_device+0x94/0xa0 [ 1003.342872] [] pci_stop_bus_device+0x3d/0xa0 [ 1003.342873] [] pci_stop_bus_device+0x3d/0xa0 [ 1003.342874] [] pci_stop_and_remove_bus_device+0x16/0x30 [ 1003.342876] [] pciehp_unconfigure_device+0x9b/0x180 [ 1003.342877] [] pciehp_disable_slot+0x43/0xb0 [ 1003.342878] [] pciehp_power_thread+0x8d/0xb0 [ 1003.342885] [] process_one_work+0x152/0x3d0 [ 1003.342886] [] worker_thread+0x11a/0x460 [ 1003.342887] [] ? process_one_work+0x3d0/0x3d0 [ 1003.342890] [] kthread+0xc9/0xe0 [ 1003.342891] [] ? kthread_create_on_node+0x180/0x180 [ 1003.342893] [] ret_from_fork+0x3f/0x70 [ 1003.342894] [] ? kthread_create_on_node+0x180/0x180 [ 1003.342895] ---[ end trace 65a77e06d5aa9358 ]--- Upon looking at the igb driver, I see that igb_rd32() attempted to read from hw_addr and failed, so it set hw->hw_addr to NULL and spit out the message in the log output above, "PCIe link lost, device now detached". Well, now that hw_addr is NULL, the attempt to call pci_iounmap is obviously not going to go well. As suggested by Mark Rustad, do something similar to what ixgbe does, and save a copy of hw_addr as adapter->io_addr, so we can still call pci_iounmap on it on teardown. Additionally, for consistency, make the pci_iomap call assignment directly to io_addr, so map and unmap match. Signed-off-by: Jarod Wilson Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 2 ++ drivers/net/ethernet/intel/igb/igb_main.c | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 1a2f1cc44b28..e3cb93bdb21a 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -389,6 +389,8 @@ struct igb_adapter { u16 link_speed; u16 link_duplex; + u8 __iomem *io_addr; /* Mainly for iounmap use */ + struct work_struct reset_task; struct work_struct watchdog_task; bool fc_autoneg; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index ea7b09887245..061e1026af76 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -2294,9 +2294,11 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE); err = -EIO; - hw->hw_addr = pci_iomap(pdev, 0, 0); - if (!hw->hw_addr) + adapter->io_addr = pci_iomap(pdev, 0, 0); + if (!adapter->io_addr) goto err_ioremap; + /* hw->hw_addr can be altered, we'll use adapter->io_addr for unmap */ + hw->hw_addr = adapter->io_addr; netdev->netdev_ops = &igb_netdev_ops; igb_set_ethtool_ops(netdev); @@ -2656,7 +2658,7 @@ err_sw_init: #ifdef CONFIG_PCI_IOV igb_disable_sriov(pdev); #endif - pci_iounmap(pdev, hw->hw_addr); + pci_iounmap(pdev, adapter->io_addr); err_ioremap: free_netdev(netdev); err_alloc_etherdev: @@ -2823,7 +2825,7 @@ static void igb_remove(struct pci_dev *pdev) igb_clear_interrupt_scheme(adapter); - pci_iounmap(pdev, hw->hw_addr); + pci_iounmap(pdev, adapter->io_addr); if (hw->flash_address) iounmap(hw->flash_address); pci_release_selected_regions(pdev, -- cgit v1.2.3-59-g8ed1b From a48954c88b5a9ec0a8323fff1d47a6affba31d61 Mon Sep 17 00:00:00 2001 From: Janusz Wolak Date: Thu, 17 Sep 2015 23:34:29 +0200 Subject: e1000: Remove checkpatch coding style errors Signed-off-by: Janusz Wolak Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_hw.c | 16 ++-- drivers/net/ethernet/intel/e1000/e1000_main.c | 116 +++++++++++++++----------- 2 files changed, 72 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c index 2523e301c26c..59d1769d4816 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_hw.c +++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c @@ -683,7 +683,7 @@ static s32 e1000_adjust_serdes_amplitude(struct e1000_hw *hw) } ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1, - &eeprom_data); + &eeprom_data); if (ret_val) { return ret_val; } @@ -1652,7 +1652,7 @@ s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) mii_1000t_ctrl_reg = 0; } else { ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, - mii_1000t_ctrl_reg); + mii_1000t_ctrl_reg); if (ret_val) return ret_val; } @@ -2193,8 +2193,7 @@ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw) else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) && (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && - (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) - { + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { hw->fc = E1000_FC_TX_PAUSE; e_dbg ("Flow Control = TX PAUSE frames only.\n"); @@ -2210,8 +2209,7 @@ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw) else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && - (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) - { + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { hw->fc = E1000_FC_RX_PAUSE; e_dbg ("Flow Control = RX PAUSE frames only.\n"); @@ -3449,7 +3447,7 @@ s32 e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info) if (hw->phy_type == e1000_phy_igp) return e1000_phy_igp_get_info(hw, phy_info); else if ((hw->phy_type == e1000_phy_8211) || - (hw->phy_type == e1000_phy_8201)) + (hw->phy_type == e1000_phy_8201)) return E1000_SUCCESS; else return e1000_phy_m88_get_info(hw, phy_info); @@ -3896,7 +3894,7 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, if (hw->mac_type == e1000_ce4100) { GBE_CONFIG_FLASH_READ(GBE_CONFIG_BASE_VIRT, offset, words, - data); + data); return E1000_SUCCESS; } @@ -4067,7 +4065,7 @@ static s32 e1000_do_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, if (hw->mac_type == e1000_ce4100) { GBE_CONFIG_FLASH_WRITE(GBE_CONFIG_BASE_VIRT, offset, words, - data); + data); return E1000_SUCCESS; } diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 068023595d84..12d59a464240 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -99,13 +99,13 @@ int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); void e1000_free_all_tx_resources(struct e1000_adapter *adapter); void e1000_free_all_rx_resources(struct e1000_adapter *adapter); static int e1000_setup_tx_resources(struct e1000_adapter *adapter, - struct e1000_tx_ring *txdr); + struct e1000_tx_ring *txdr); static int e1000_setup_rx_resources(struct e1000_adapter *adapter, - struct e1000_rx_ring *rxdr); + struct e1000_rx_ring *rxdr); static void e1000_free_tx_resources(struct e1000_adapter *adapter, - struct e1000_tx_ring *tx_ring); + struct e1000_tx_ring *tx_ring); static void e1000_free_rx_resources(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring); + struct e1000_rx_ring *rx_ring); void e1000_update_stats(struct e1000_adapter *adapter); static int e1000_init_module(void); @@ -122,16 +122,16 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter); static void e1000_clean_all_tx_rings(struct e1000_adapter *adapter); static void e1000_clean_all_rx_rings(struct e1000_adapter *adapter); static void e1000_clean_tx_ring(struct e1000_adapter *adapter, - struct e1000_tx_ring *tx_ring); + struct e1000_tx_ring *tx_ring); static void e1000_clean_rx_ring(struct e1000_adapter *adapter, - struct e1000_rx_ring *rx_ring); + struct e1000_rx_ring *rx_ring); static void e1000_set_rx_mode(struct net_device *netdev); static void e1000_update_phy_info_task(struct work_struct *work); static void e1000_watchdog(struct work_struct *work); static void e1000_82547_tx_fifo_stall_task(struct work_struct *work); static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev); -static struct net_device_stats * e1000_get_stats(struct net_device *netdev); +static struct net_device_stats *e1000_get_stats(struct net_device *netdev); static int e1000_change_mtu(struct net_device *netdev, int new_mtu); static int e1000_set_mac(struct net_device *netdev, void *p); static irqreturn_t e1000_intr(int irq, void *data); @@ -164,7 +164,7 @@ static void e1000_tx_timeout(struct net_device *dev); static void e1000_reset_task(struct work_struct *work); static void e1000_smartspeed(struct e1000_adapter *adapter); static int e1000_82547_fifo_workaround(struct e1000_adapter *adapter, - struct sk_buff *skb); + struct sk_buff *skb); static bool e1000_vlan_used(struct e1000_adapter *adapter); static void e1000_vlan_mode(struct net_device *netdev, @@ -195,7 +195,7 @@ MODULE_PARM_DESC(copybreak, "Maximum size of packet that is copied to a new buffer on receive"); static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev, - pci_channel_state_t state); + pci_channel_state_t state); static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev); static void e1000_io_resume(struct pci_dev *pdev); @@ -287,7 +287,7 @@ static int e1000_request_irq(struct e1000_adapter *adapter) int err; err = request_irq(adapter->pdev->irq, handler, irq_flags, netdev->name, - netdev); + netdev); if (err) { e_err(probe, "Unable to allocate interrupt Error: %d\n", err); } @@ -636,8 +636,8 @@ void e1000_reset(struct e1000_adapter *adapter) * but don't include ethernet FCS because hardware appends it */ min_tx_space = (hw->max_frame_size + - sizeof(struct e1000_tx_desc) - - ETH_FCS_LEN) * 2; + sizeof(struct e1000_tx_desc) - + ETH_FCS_LEN) * 2; min_tx_space = ALIGN(min_tx_space, 1024); min_tx_space >>= 10; /* software strips receive CRC, so leave room for it */ @@ -943,8 +943,8 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct e1000_adapter *adapter; struct e1000_hw *hw; - static int cards_found = 0; - static int global_quad_port_a = 0; /* global ksp3 port a indication */ + static int cards_found; + static int global_quad_port_a; /* global ksp3 port a indication */ int i, err, pci_using_dac; u16 eeprom_data = 0; u16 tmp = 0; @@ -1046,7 +1046,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (hw->mac_type == e1000_ce4100) { hw->ce4100_gbe_mdio_base_virt = ioremap(pci_resource_start(pdev, BAR_1), - pci_resource_len(pdev, BAR_1)); + pci_resource_len(pdev, BAR_1)); if (!hw->ce4100_gbe_mdio_base_virt) goto err_mdio_ioremap; @@ -1148,7 +1148,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) break; case e1000_82546: case e1000_82546_rev_3: - if (er32(STATUS) & E1000_STATUS_FUNC_1){ + if (er32(STATUS) & E1000_STATUS_FUNC_1) { e1000_read_eeprom(hw, EEPROM_INIT_CONTROL3_PORT_B, 1, &eeprom_data); break; @@ -1334,12 +1334,12 @@ static int e1000_sw_init(struct e1000_adapter *adapter) static int e1000_alloc_queues(struct e1000_adapter *adapter) { adapter->tx_ring = kcalloc(adapter->num_tx_queues, - sizeof(struct e1000_tx_ring), GFP_KERNEL); + sizeof(struct e1000_tx_ring), GFP_KERNEL); if (!adapter->tx_ring) return -ENOMEM; adapter->rx_ring = kcalloc(adapter->num_rx_queues, - sizeof(struct e1000_rx_ring), GFP_KERNEL); + sizeof(struct e1000_rx_ring), GFP_KERNEL); if (!adapter->rx_ring) { kfree(adapter->tx_ring); return -ENOMEM; @@ -1811,20 +1811,20 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter) rctl &= ~E1000_RCTL_SZ_4096; rctl |= E1000_RCTL_BSEX; switch (adapter->rx_buffer_len) { - case E1000_RXBUFFER_2048: - default: - rctl |= E1000_RCTL_SZ_2048; - rctl &= ~E1000_RCTL_BSEX; - break; - case E1000_RXBUFFER_4096: - rctl |= E1000_RCTL_SZ_4096; - break; - case E1000_RXBUFFER_8192: - rctl |= E1000_RCTL_SZ_8192; - break; - case E1000_RXBUFFER_16384: - rctl |= E1000_RCTL_SZ_16384; - break; + case E1000_RXBUFFER_2048: + default: + rctl |= E1000_RCTL_SZ_2048; + rctl &= ~E1000_RCTL_BSEX; + break; + case E1000_RXBUFFER_4096: + rctl |= E1000_RCTL_SZ_4096; + break; + case E1000_RXBUFFER_8192: + rctl |= E1000_RCTL_SZ_8192; + break; + case E1000_RXBUFFER_16384: + rctl |= E1000_RCTL_SZ_16384; + break; } /* This is useful for sniffing bad packets. */ @@ -1861,12 +1861,12 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) if (adapter->netdev->mtu > ETH_DATA_LEN) { rdlen = adapter->rx_ring[0].count * - sizeof(struct e1000_rx_desc); + sizeof(struct e1000_rx_desc); adapter->clean_rx = e1000_clean_jumbo_rx_irq; adapter->alloc_rx_buf = e1000_alloc_jumbo_rx_buffers; } else { rdlen = adapter->rx_ring[0].count * - sizeof(struct e1000_rx_desc); + sizeof(struct e1000_rx_desc); adapter->clean_rx = e1000_clean_rx_irq; adapter->alloc_rx_buf = e1000_alloc_rx_buffers; } @@ -2761,7 +2761,9 @@ static int e1000_tso(struct e1000_adapter *adapter, buffer_info->time_stamp = jiffies; buffer_info->next_to_watch = i; - if (++i == tx_ring->count) i = 0; + if (++i == tx_ring->count) + i = 0; + tx_ring->next_to_use = i; return true; @@ -2816,7 +2818,9 @@ static bool e1000_tx_csum(struct e1000_adapter *adapter, buffer_info->time_stamp = jiffies; buffer_info->next_to_watch = i; - if (unlikely(++i == tx_ring->count)) i = 0; + if (unlikely(++i == tx_ring->count)) + i = 0; + tx_ring->next_to_use = i; return true; @@ -2865,8 +2869,8 @@ static int e1000_tx_map(struct e1000_adapter *adapter, * packet is smaller than 2048 - 16 - 16 (or 2016) bytes */ if (unlikely((hw->bus_type == e1000_bus_type_pcix) && - (size > 2015) && count == 0)) - size = 2015; + (size > 2015) && count == 0)) + size = 2015; /* Workaround for potential 82544 hang in PCI-X. Avoid * terminating buffers within evenly-aligned dwords. @@ -2963,7 +2967,7 @@ dma_error: count--; while (count--) { - if (i==0) + if (i == 0) i += tx_ring->count; i--; buffer_info = &tx_ring->buffer_info[i]; @@ -3013,7 +3017,8 @@ static void e1000_tx_queue(struct e1000_adapter *adapter, tx_desc->lower.data = cpu_to_le32(txd_lower | buffer_info->length); tx_desc->upper.data = cpu_to_le32(txd_upper); - if (unlikely(++i == tx_ring->count)) i = 0; + if (unlikely(++i == tx_ring->count)) + i = 0; } tx_desc->lower.data |= cpu_to_le32(adapter->txd_cmd); @@ -3101,7 +3106,7 @@ static int e1000_maybe_stop_tx(struct net_device *netdev, return __e1000_maybe_stop_tx(netdev, size); } -#define TXD_USE_COUNT(S, X) (((S) >> (X)) + 1 ) +#define TXD_USE_COUNT(S, X) (((S) >> (X)) + 1) static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { @@ -3841,7 +3846,7 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, struct e1000_tx_buffer *buffer_info; unsigned int i, eop; unsigned int count = 0; - unsigned int total_tx_bytes=0, total_tx_packets=0; + unsigned int total_tx_bytes = 0, total_tx_packets = 0; unsigned int bytes_compl = 0, pkts_compl = 0; i = tx_ring->next_to_clean; @@ -3869,7 +3874,8 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, e1000_unmap_and_free_tx_resource(adapter, buffer_info); tx_desc->upper.data = 0; - if (unlikely(++i == tx_ring->count)) i = 0; + if (unlikely(++i == tx_ring->count)) + i = 0; } eop = tx_ring->buffer_info[i].next_to_watch; @@ -3957,9 +3963,11 @@ static void e1000_rx_checksum(struct e1000_adapter *adapter, u32 status_err, skb_checksum_none_assert(skb); /* 82543 or newer only */ - if (unlikely(hw->mac_type < e1000_82543)) return; + if (unlikely(hw->mac_type < e1000_82543)) + return; /* Ignore Checksum bit is set */ - if (unlikely(status & E1000_RXD_STAT_IXSM)) return; + if (unlikely(status & E1000_RXD_STAT_IXSM)) + return; /* TCP/UDP checksum error bit is set */ if (unlikely(errors & E1000_RXD_ERR_TCPE)) { /* let the stack verify checksum errors */ @@ -4139,7 +4147,7 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter, unsigned int i; int cleaned_count = 0; bool cleaned = false; - unsigned int total_rx_bytes=0, total_rx_packets=0; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; i = rx_ring->next_to_clean; rx_desc = E1000_RX_DESC(*rx_ring, i); @@ -4156,7 +4164,9 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter, status = rx_desc->status; - if (++i == rx_ring->count) i = 0; + if (++i == rx_ring->count) + i = 0; + next_rxd = E1000_RX_DESC(*rx_ring, i); prefetch(next_rxd); @@ -4359,7 +4369,7 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, unsigned int i; int cleaned_count = 0; bool cleaned = false; - unsigned int total_rx_bytes=0, total_rx_packets=0; + unsigned int total_rx_bytes = 0, total_rx_packets = 0; i = rx_ring->next_to_clean; rx_desc = E1000_RX_DESC(*rx_ring, i); @@ -4398,7 +4408,9 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter, buffer_info->rxbuf.data = NULL; } - if (++i == rx_ring->count) i = 0; + if (++i == rx_ring->count) + i = 0; + next_rxd = E1000_RX_DESC(*rx_ring, i); prefetch(next_rxd); @@ -4686,9 +4698,11 @@ static void e1000_smartspeed(struct e1000_adapter *adapter) * we assume back-to-back */ e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_status); - if (!(phy_status & SR_1000T_MS_CONFIG_FAULT)) return; + if (!(phy_status & SR_1000T_MS_CONFIG_FAULT)) + return; e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_status); - if (!(phy_status & SR_1000T_MS_CONFIG_FAULT)) return; + if (!(phy_status & SR_1000T_MS_CONFIG_FAULT)) + return; e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_ctrl); if (phy_ctrl & CR_1000T_MS_ENABLE) { phy_ctrl &= ~CR_1000T_MS_ENABLE; -- cgit v1.2.3-59-g8ed1b From 08c991297582114a6e1220f913eec91789c4eac6 Mon Sep 17 00:00:00 2001 From: Todd Fujinaka Date: Fri, 18 Sep 2015 15:43:51 -0700 Subject: igb: use the correct i210 register for EEMNGCTL The i210 has two EEPROM access registers that are located in non-standard offsets: EEARBC and EEMNGCTL. EEARBC was fixed previously and EEMNGCTL should also be corrected. Reported-by: Roman Hodek Signed-off-by: Todd Fujinaka Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_82575.c | 1 + drivers/net/ethernet/intel/igb/e1000_i210.c | 27 +++++++++++++++++++++++++++ drivers/net/ethernet/intel/igb/e1000_i210.h | 1 + drivers/net/ethernet/intel/igb/e1000_regs.h | 1 + 4 files changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index d9664c47fe3a..362911d024b5 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -299,6 +299,7 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw) case I210_I_PHY_ID: phy->type = e1000_phy_i210; phy->ops.check_polarity = igb_check_polarity_m88; + phy->ops.get_cfg_done = igb_get_cfg_done_i210; phy->ops.get_phy_info = igb_get_phy_info_m88; phy->ops.get_cable_length = igb_get_cable_length_m88_gen2; phy->ops.set_d0_lplu_state = igb_set_d0_lplu_state_82580; diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.c b/drivers/net/ethernet/intel/igb/e1000_i210.c index 65d931669f81..29f59c76878a 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.c +++ b/drivers/net/ethernet/intel/igb/e1000_i210.c @@ -900,3 +900,30 @@ s32 igb_pll_workaround_i210(struct e1000_hw *hw) wr32(E1000_MDICNFG, mdicnfg); return ret_val; } + +/** + * igb_get_cfg_done_i210 - Read config done bit + * @hw: pointer to the HW structure + * + * Read the management control register for the config done bit for + * completion status. NOTE: silicon which is EEPROM-less will fail trying + * to read the config done bit, so an error is *ONLY* logged and returns + * 0. If we were to return with error, EEPROM-less silicon + * would not be able to be reset or change link. + **/ +s32 igb_get_cfg_done_i210(struct e1000_hw *hw) +{ + s32 timeout = PHY_CFG_TIMEOUT; + u32 mask = E1000_NVM_CFG_DONE_PORT_0; + + while (timeout) { + if (rd32(E1000_EEMNGCTL_I210) & mask) + break; + usleep_range(1000, 2000); + timeout--; + } + if (!timeout) + hw_dbg("MNG configuration cycle has not completed.\n"); + + return 0; +} diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.h b/drivers/net/ethernet/intel/igb/e1000_i210.h index 3442b6357d01..eaa68a50cb3b 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.h +++ b/drivers/net/ethernet/intel/igb/e1000_i210.h @@ -34,6 +34,7 @@ s32 igb_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data); s32 igb_init_nvm_params_i210(struct e1000_hw *hw); bool igb_get_flash_presence_i210(struct e1000_hw *hw); s32 igb_pll_workaround_i210(struct e1000_hw *hw); +s32 igb_get_cfg_done_i210(struct e1000_hw *hw); #define E1000_STM_OPCODE 0xDB00 #define E1000_EEPROM_FLASH_SIZE_WORD 0x11 diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h index 4af2870e49f8..0fdcd4d1b982 100644 --- a/drivers/net/ethernet/intel/igb/e1000_regs.h +++ b/drivers/net/ethernet/intel/igb/e1000_regs.h @@ -66,6 +66,7 @@ #define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ #define E1000_PBS 0x01008 /* Packet Buffer Size */ #define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_EEMNGCTL_I210 0x12030 /* MNG EEprom Control */ #define E1000_EEARBC_I210 0x12024 /* EEPROM Auto Read Bus Control */ #define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ #define E1000_I2CCMD 0x01028 /* SFPI2C Command Register - RW */ -- cgit v1.2.3-59-g8ed1b From 4e01f3a802b5910b25814e1d0fd05907edffed6f Mon Sep 17 00:00:00 2001 From: Jean Sacren Date: Sat, 19 Sep 2015 05:08:40 -0600 Subject: e1000: clean up the checking logic The checking logic needed some clean-up work, so we rewrite it by checking for break first. With that change in place, we can even move the second check for goto statement outside of the loop. As this is merely a cleanup, no functional change is involved. The questionable 'tmp != 0xFF' is intentionally left alone. Mark Rustad and Alexander Duyck contributed to this patch. CC: Mark Rustad CC: Alex Duyck Signed-off-by: Jean Sacren Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 12d59a464240..a0f3a313e9e7 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -1199,13 +1199,13 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) for (i = 0; i < 32; i++) { hw->phy_addr = i; e1000_read_phy_reg(hw, PHY_ID2, &tmp); - if (tmp == 0 || tmp == 0xFF) { - if (i == 31) - goto err_eeprom; - continue; - } else + + if (tmp != 0 && tmp != 0xFF) break; } + + if (i >= 32) + goto err_eeprom; } /* reset the hardware with the new settings */ -- cgit v1.2.3-59-g8ed1b From b6fad9f9fc5d4deb1cb50173bd729de26570fbbe Mon Sep 17 00:00:00 2001 From: Jean Sacren Date: Sat, 19 Sep 2015 05:08:41 -0600 Subject: e1000: fix a typo in the comment Use 'That' to replace 'The' so that the comment would make sense. Signed-off-by: Jean Sacren Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index a0f3a313e9e7..3fc7bde699ba 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -1263,7 +1263,7 @@ err_pci_reg: * @pdev: PCI device information struct * * e1000_remove is called by the PCI subsystem to alert the driver - * that it should release a PCI device. The could be caused by a + * that it should release a PCI device. That could be caused by a * Hot-Plug event, or because the driver is going to be removed from * memory. **/ -- cgit v1.2.3-59-g8ed1b From 5a5e889c80cef7513a40143ee1e474acccdee13b Mon Sep 17 00:00:00 2001 From: Jean Sacren Date: Sat, 19 Sep 2015 05:08:42 -0600 Subject: e1000e: clean up the local variable The local variable 'ret' doesn't serve much purpose so we might as well clean it up. Signed-off-by: Jean Sacren Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/netdev.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 0a854a47d31a..0003bf61b211 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -7504,14 +7504,11 @@ static struct pci_driver e1000_driver = { **/ static int __init e1000_init_module(void) { - int ret; - pr_info("Intel(R) PRO/1000 Network Driver - %s\n", e1000e_driver_version); pr_info("Copyright(c) 1999 - 2015 Intel Corporation.\n"); - ret = pci_register_driver(&e1000_driver); - return ret; + return pci_register_driver(&e1000_driver); } module_init(e1000_init_module); -- cgit v1.2.3-59-g8ed1b From f03fed668a9193036db0258229af87e9ba46ed5e Mon Sep 17 00:00:00 2001 From: Jean Sacren Date: Sat, 19 Sep 2015 05:08:46 -0600 Subject: e1000: fix kernel-doc argument being missing Due to historical reason, 'phy_data' has never been included in the kernel doc. Fix it so that the requirement could be fulfilled. Signed-off-by: Jean Sacren Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_hw.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c index 59d1769d4816..dbae55e926e7 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_hw.c +++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c @@ -2806,6 +2806,7 @@ static u16 e1000_shift_in_mdi_bits(struct e1000_hw *hw) * e1000_read_phy_reg - read a phy register * @hw: Struct containing variables accessed by shared code * @reg_addr: address of the PHY register to read + * @phy_data: pointer to the value on the PHY register * * Reads the value from a PHY register, if the value is on a specific non zero * page, sets the page first. -- cgit v1.2.3-59-g8ed1b From c619581a7903fadccb900d1d497d2366fdf7da9d Mon Sep 17 00:00:00 2001 From: Jean Sacren Date: Sat, 19 Sep 2015 05:08:47 -0600 Subject: e1000: get rid of duplicate exit path By using goto statement, we can achieve sharing the same exit path so that code duplication could be minimized. Signed-off-by: Jean Sacren Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_hw.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c index dbae55e926e7..9c06456759e9 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_hw.c +++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c @@ -2822,14 +2822,13 @@ s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 *phy_data) (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, (u16) reg_addr); - if (ret_val) { - spin_unlock_irqrestore(&e1000_phy_lock, flags); - return ret_val; - } + if (ret_val) + goto out; } ret_val = e1000_read_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr, phy_data); +out: spin_unlock_irqrestore(&e1000_phy_lock, flags); return ret_val; -- cgit v1.2.3-59-g8ed1b From 13a87c124ec8a717b557cd8bc08693af021c8812 Mon Sep 17 00:00:00 2001 From: Janusz Wolak Date: Mon, 28 Sep 2015 23:40:19 +0200 Subject: e1000: Elementary checkpatch warnings and checks removed Signed-off-by: Janusz Wolak Acked-by: Jesse Brandeburg Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_hw.c | 179 ++++++++++++++-------------- 1 file changed, 89 insertions(+), 90 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c index 9c06456759e9..8172cf08cc33 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_hw.c +++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c @@ -1,5 +1,5 @@ /******************************************************************************* - +* Intel PRO/1000 Linux driver Copyright(c) 1999 - 2006 Intel Corporation. @@ -624,8 +624,8 @@ s32 e1000_init_hw(struct e1000_hw *hw) /* Workaround for PCI-X problem when BIOS sets MMRBC * incorrectly. */ - if (hw->bus_type == e1000_bus_type_pcix - && e1000_pcix_get_mmrbc(hw) > 2048) + if (hw->bus_type == e1000_bus_type_pcix && + e1000_pcix_get_mmrbc(hw) > 2048) e1000_pcix_set_mmrbc(hw, 2048); break; } @@ -684,9 +684,8 @@ static s32 e1000_adjust_serdes_amplitude(struct e1000_hw *hw) ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1, &eeprom_data); - if (ret_val) { + if (ret_val) return ret_val; - } if (eeprom_data != EEPROM_RESERVED_WORD) { /* Adjust SERDES output amplitude only. */ @@ -1074,8 +1073,8 @@ static s32 e1000_copper_link_preconfig(struct e1000_hw *hw) if (hw->mac_type <= e1000_82543 || hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 || - hw->mac_type == e1000_82541_rev_2 - || hw->mac_type == e1000_82547_rev_2) + hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547_rev_2) hw->phy_reset_disable = false; return E1000_SUCCESS; @@ -1881,10 +1880,11 @@ static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw) if (ret_val) return ret_val; - if ((hw->mac_type == e1000_82544 || hw->mac_type == e1000_82543) - && (!hw->autoneg) - && (hw->forced_speed_duplex == e1000_10_full - || hw->forced_speed_duplex == e1000_10_half)) { + if ((hw->mac_type == e1000_82544 || + hw->mac_type == e1000_82543) && + (!hw->autoneg) && + (hw->forced_speed_duplex == e1000_10_full || + hw->forced_speed_duplex == e1000_10_half)) { ret_val = e1000_polarity_reversal_workaround(hw); if (ret_val) return ret_val; @@ -2084,11 +2084,12 @@ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw) * so we had to force link. In this case, we need to force the * configuration of the MAC to match the "fc" parameter. */ - if (((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) - || ((hw->media_type == e1000_media_type_internal_serdes) - && (hw->autoneg_failed)) - || ((hw->media_type == e1000_media_type_copper) - && (!hw->autoneg))) { + if (((hw->media_type == e1000_media_type_fiber) && + (hw->autoneg_failed)) || + ((hw->media_type == e1000_media_type_internal_serdes) && + (hw->autoneg_failed)) || + ((hw->media_type == e1000_media_type_copper) && + (!hw->autoneg))) { ret_val = e1000_force_mac_fc(hw); if (ret_val) { e_dbg("Error forcing flow control settings\n"); @@ -2458,10 +2459,11 @@ s32 e1000_check_for_link(struct e1000_hw *hw) * happen due to the execution of this workaround. */ - if ((hw->mac_type == e1000_82544 - || hw->mac_type == e1000_82543) && (!hw->autoneg) - && (hw->forced_speed_duplex == e1000_10_full - || hw->forced_speed_duplex == e1000_10_half)) { + if ((hw->mac_type == e1000_82544 || + hw->mac_type == e1000_82543) && + (!hw->autoneg) && + (hw->forced_speed_duplex == e1000_10_full || + hw->forced_speed_duplex == e1000_10_half)) { ew32(IMC, 0xffffffff); ret_val = e1000_polarity_reversal_workaround(hw); @@ -2526,8 +2528,10 @@ s32 e1000_check_for_link(struct e1000_hw *hw) */ if (hw->tbi_compatibility_en) { u16 speed, duplex; + ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { e_dbg ("Error getting link speed and duplex\n"); @@ -2626,10 +2630,10 @@ s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex) e1000_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data); if (ret_val) return ret_val; - if ((*speed == SPEED_100 - && !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) - || (*speed == SPEED_10 - && !(phy_data & NWAY_LPAR_10T_FD_CAPS))) + if ((*speed == SPEED_100 && + !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) || + (*speed == SPEED_10 && + !(phy_data & NWAY_LPAR_10T_FD_CAPS))) *duplex = HALF_DUPLEX; } } @@ -2662,9 +2666,9 @@ static s32 e1000_wait_autoneg(struct e1000_hw *hw) ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); if (ret_val) return ret_val; - if (phy_data & MII_SR_AUTONEG_COMPLETE) { + if (phy_data & MII_SR_AUTONEG_COMPLETE) return E1000_SUCCESS; - } + msleep(100); } return E1000_SUCCESS; @@ -2801,7 +2805,6 @@ static u16 e1000_shift_in_mdi_bits(struct e1000_hw *hw) return data; } - /** * e1000_read_phy_reg - read a phy register * @hw: Struct containing variables accessed by shared code @@ -2879,7 +2882,7 @@ static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, e_dbg("MDI Read Error\n"); return -E1000_ERR_PHY; } - *phy_data = (u16) mdic; + *phy_data = (u16)mdic; } else { mdic = ((reg_addr << E1000_MDIC_REG_SHIFT) | (phy_addr << E1000_MDIC_PHY_SHIFT) | @@ -2904,7 +2907,7 @@ static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, e_dbg("MDI Error\n"); return -E1000_ERR_PHY; } - *phy_data = (u16) mdic; + *phy_data = (u16)mdic; } } else { /* We must first send a preamble through the MDIO pin to signal @@ -2958,7 +2961,7 @@ s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 phy_data) if ((hw->phy_type == e1000_phy_igp) && (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, - (u16) reg_addr); + (u16)reg_addr); if (ret_val) { spin_unlock_irqrestore(&e1000_phy_lock, flags); return ret_val; @@ -2991,7 +2994,7 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, * the desired data. */ if (hw->mac_type == e1000_ce4100) { - mdic = (((u32) phy_data) | + mdic = (((u32)phy_data) | (reg_addr << E1000_MDIC_REG_SHIFT) | (phy_addr << E1000_MDIC_PHY_SHIFT) | (INTEL_CE_GBE_MDIC_OP_WRITE) | @@ -3013,7 +3016,7 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, return -E1000_ERR_PHY; } } else { - mdic = (((u32) phy_data) | + mdic = (((u32)phy_data) | (reg_addr << E1000_MDIC_REG_SHIFT) | (phy_addr << E1000_MDIC_PHY_SHIFT) | (E1000_MDIC_OP_WRITE)); @@ -3051,7 +3054,7 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) | (PHY_OP_WRITE << 12) | (PHY_SOF << 14)); mdic <<= 16; - mdic |= (u32) phy_data; + mdic |= (u32)phy_data; e1000_shift_out_mdi_bits(hw, mdic, 32); } @@ -3174,14 +3177,14 @@ static s32 e1000_detect_gig_phy(struct e1000_hw *hw) if (ret_val) return ret_val; - hw->phy_id = (u32) (phy_id_high << 16); + hw->phy_id = (u32)(phy_id_high << 16); udelay(20); ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low); if (ret_val) return ret_val; - hw->phy_id |= (u32) (phy_id_low & PHY_REVISION_MASK); - hw->phy_revision = (u32) phy_id_low & ~PHY_REVISION_MASK; + hw->phy_id |= (u32)(phy_id_low & PHY_REVISION_MASK); + hw->phy_revision = (u32)phy_id_low & ~PHY_REVISION_MASK; switch (hw->mac_type) { case e1000_82543: @@ -3399,7 +3402,6 @@ static s32 e1000_phy_m88_get_info(struct e1000_hw *hw, phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >> SR_1000T_REMOTE_RX_STATUS_SHIFT) ? e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; - } return E1000_SUCCESS; @@ -3609,11 +3611,11 @@ static void e1000_shift_out_ee_bits(struct e1000_hw *hw, u16 data, u16 count) */ mask = 0x01 << (count - 1); eecd = er32(EECD); - if (eeprom->type == e1000_eeprom_microwire) { + if (eeprom->type == e1000_eeprom_microwire) eecd &= ~E1000_EECD_DO; - } else if (eeprom->type == e1000_eeprom_spi) { + else if (eeprom->type == e1000_eeprom_spi) eecd |= E1000_EECD_DO; - } + do { /* A "1" is shifted out to the EEPROM by setting bit "DI" to a * "1", and then raising and then lowering the clock (the SK bit @@ -3849,7 +3851,7 @@ static s32 e1000_spi_eeprom_ready(struct e1000_hw *hw) do { e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI, hw->eeprom.opcode_bits); - spi_stat_reg = (u8) e1000_shift_in_ee_bits(hw, 8); + spi_stat_reg = (u8)e1000_shift_in_ee_bits(hw, 8); if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI)) break; @@ -3880,6 +3882,7 @@ static s32 e1000_spi_eeprom_ready(struct e1000_hw *hw) s32 e1000_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { s32 ret; + mutex_lock(&e1000_eeprom_lock); ret = e1000_do_read_eeprom(hw, offset, words, data); mutex_unlock(&e1000_eeprom_lock); @@ -3901,8 +3904,9 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, /* A check for invalid values: offset too large, too many words, and * not enough words. */ - if ((offset >= eeprom->word_size) - || (words > eeprom->word_size - offset) || (words == 0)) { + if ((offset >= eeprom->word_size) || + (words > eeprom->word_size - offset) || + (words == 0)) { e_dbg("\"words\" parameter out of bounds. Words = %d," "size = %d\n", offset, eeprom->word_size); return -E1000_ERR_EEPROM; @@ -3938,7 +3942,7 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, /* Send the READ command (opcode + addr) */ e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits); - e1000_shift_out_ee_bits(hw, (u16) (offset * 2), + e1000_shift_out_ee_bits(hw, (u16)(offset * 2), eeprom->address_bits); /* Read the data. The address of the eeprom internally @@ -3958,7 +3962,7 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, e1000_shift_out_ee_bits(hw, EEPROM_READ_OPCODE_MICROWIRE, eeprom->opcode_bits); - e1000_shift_out_ee_bits(hw, (u16) (offset + i), + e1000_shift_out_ee_bits(hw, (u16)(offset + i), eeprom->address_bits); /* Read the data. For microwire, each word requires the @@ -4003,7 +4007,7 @@ s32 e1000_validate_eeprom_checksum(struct e1000_hw *hw) return E1000_SUCCESS; #endif - if (checksum == (u16) EEPROM_SUM) + if (checksum == (u16)EEPROM_SUM) return E1000_SUCCESS; else { e_dbg("EEPROM Checksum Invalid\n"); @@ -4030,7 +4034,7 @@ s32 e1000_update_eeprom_checksum(struct e1000_hw *hw) } checksum += eeprom_data; } - checksum = (u16) EEPROM_SUM - checksum; + checksum = (u16)EEPROM_SUM - checksum; if (e1000_write_eeprom(hw, EEPROM_CHECKSUM_REG, 1, &checksum) < 0) { e_dbg("EEPROM Write Error\n"); return -E1000_ERR_EEPROM; @@ -4051,6 +4055,7 @@ s32 e1000_update_eeprom_checksum(struct e1000_hw *hw) s32 e1000_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { s32 ret; + mutex_lock(&e1000_eeprom_lock); ret = e1000_do_write_eeprom(hw, offset, words, data); mutex_unlock(&e1000_eeprom_lock); @@ -4072,8 +4077,9 @@ static s32 e1000_do_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, /* A check for invalid values: offset too large, too many words, and * not enough words. */ - if ((offset >= eeprom->word_size) - || (words > eeprom->word_size - offset) || (words == 0)) { + if ((offset >= eeprom->word_size) || + (words > eeprom->word_size - offset) || + (words == 0)) { e_dbg("\"words\" parameter out of bounds\n"); return -E1000_ERR_EEPROM; } @@ -4132,7 +4138,7 @@ static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words, /* Send the Write command (8-bit opcode + addr) */ e1000_shift_out_ee_bits(hw, write_opcode, eeprom->opcode_bits); - e1000_shift_out_ee_bits(hw, (u16) ((offset + widx) * 2), + e1000_shift_out_ee_bits(hw, (u16)((offset + widx) * 2), eeprom->address_bits); /* Send the data */ @@ -4142,6 +4148,7 @@ static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words, */ while (widx < words) { u16 word_out = data[widx]; + word_out = (word_out >> 8) | (word_out << 8); e1000_shift_out_ee_bits(hw, word_out, 16); widx++; @@ -4183,9 +4190,9 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset, * EEPROM into write/erase mode. */ e1000_shift_out_ee_bits(hw, EEPROM_EWEN_OPCODE_MICROWIRE, - (u16) (eeprom->opcode_bits + 2)); + (u16)(eeprom->opcode_bits + 2)); - e1000_shift_out_ee_bits(hw, 0, (u16) (eeprom->address_bits - 2)); + e1000_shift_out_ee_bits(hw, 0, (u16)(eeprom->address_bits - 2)); /* Prepare the EEPROM */ e1000_standby_eeprom(hw); @@ -4195,7 +4202,7 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset, e1000_shift_out_ee_bits(hw, EEPROM_WRITE_OPCODE_MICROWIRE, eeprom->opcode_bits); - e1000_shift_out_ee_bits(hw, (u16) (offset + words_written), + e1000_shift_out_ee_bits(hw, (u16)(offset + words_written), eeprom->address_bits); /* Send the data */ @@ -4236,9 +4243,9 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset, * EEPROM out of write/erase mode. */ e1000_shift_out_ee_bits(hw, EEPROM_EWDS_OPCODE_MICROWIRE, - (u16) (eeprom->opcode_bits + 2)); + (u16)(eeprom->opcode_bits + 2)); - e1000_shift_out_ee_bits(hw, 0, (u16) (eeprom->address_bits - 2)); + e1000_shift_out_ee_bits(hw, 0, (u16)(eeprom->address_bits - 2)); return E1000_SUCCESS; } @@ -4261,8 +4268,8 @@ s32 e1000_read_mac_addr(struct e1000_hw *hw) e_dbg("EEPROM Read Error\n"); return -E1000_ERR_EEPROM; } - hw->perm_mac_addr[i] = (u8) (eeprom_data & 0x00FF); - hw->perm_mac_addr[i + 1] = (u8) (eeprom_data >> 8); + hw->perm_mac_addr[i] = (u8)(eeprom_data & 0x00FF); + hw->perm_mac_addr[i + 1] = (u8)(eeprom_data >> 8); } switch (hw->mac_type) { @@ -4329,19 +4336,19 @@ u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) */ case 0: /* [47:36] i.e. 0x563 for above example address */ - hash_value = ((mc_addr[4] >> 4) | (((u16) mc_addr[5]) << 4)); + hash_value = ((mc_addr[4] >> 4) | (((u16)mc_addr[5]) << 4)); break; case 1: /* [46:35] i.e. 0xAC6 for above example address */ - hash_value = ((mc_addr[4] >> 3) | (((u16) mc_addr[5]) << 5)); + hash_value = ((mc_addr[4] >> 3) | (((u16)mc_addr[5]) << 5)); break; case 2: /* [45:34] i.e. 0x5D8 for above example address */ - hash_value = ((mc_addr[4] >> 2) | (((u16) mc_addr[5]) << 6)); + hash_value = ((mc_addr[4] >> 2) | (((u16)mc_addr[5]) << 6)); break; case 3: /* [43:32] i.e. 0x634 for above example address */ - hash_value = ((mc_addr[4]) | (((u16) mc_addr[5]) << 8)); + hash_value = ((mc_addr[4]) | (((u16)mc_addr[5]) << 8)); break; } @@ -4362,9 +4369,9 @@ void e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index) /* HW expects these in little endian so we reverse the byte order * from network order (big endian) to little endian */ - rar_low = ((u32) addr[0] | ((u32) addr[1] << 8) | - ((u32) addr[2] << 16) | ((u32) addr[3] << 24)); - rar_high = ((u32) addr[4] | ((u32) addr[5] << 8)); + rar_low = ((u32)addr[0] | ((u32)addr[1] << 8) | + ((u32)addr[2] << 16) | ((u32)addr[3] << 24)); + rar_high = ((u32)addr[4] | ((u32)addr[5] << 8)); /* Disable Rx and flush all Rx frames before enabling RSS to avoid Rx * unit hang. @@ -4538,7 +4545,7 @@ s32 e1000_setup_led(struct e1000_hw *hw) if (ret_val) return ret_val; ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, - (u16) (hw->phy_spd_default & + (u16)(hw->phy_spd_default & ~IGP01E1000_GMII_SPD)); if (ret_val) return ret_val; @@ -4803,7 +4810,7 @@ void e1000_reset_adaptive(struct e1000_hw *hw) void e1000_update_adaptive(struct e1000_hw *hw) { if (hw->adaptive_ifs) { - if ((hw->collision_delta *hw->ifs_ratio) > hw->tx_packet_delta) { + if ((hw->collision_delta * hw->ifs_ratio) > hw->tx_packet_delta) { if (hw->tx_packet_delta > MIN_NUM_XMITS) { hw->in_ifs_mode = true; if (hw->current_ifs_val < hw->ifs_max_val) { @@ -4817,8 +4824,8 @@ void e1000_update_adaptive(struct e1000_hw *hw) } } } else { - if (hw->in_ifs_mode - && (hw->tx_packet_delta <= MIN_NUM_XMITS)) { + if (hw->in_ifs_mode && + (hw->tx_packet_delta <= MIN_NUM_XMITS)) { hw->current_ifs_val = 0; hw->in_ifs_mode = false; ew32(AIT, 0); @@ -4923,7 +4930,6 @@ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length, /* Use old method for Phy older than IGP */ if (hw->phy_type == e1000_phy_m88) { - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); if (ret_val) @@ -4967,7 +4973,6 @@ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length, }; /* Read the AGC registers for all channels */ for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { - ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data); if (ret_val) @@ -4977,8 +4982,8 @@ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length, /* Value bound check. */ if ((cur_agc_value >= - IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) - || (cur_agc_value == 0)) + IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) || + (cur_agc_value == 0)) return -E1000_ERR_PHY; agc_value += cur_agc_value; @@ -5055,7 +5060,6 @@ static s32 e1000_check_polarity(struct e1000_hw *hw, */ if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) == IGP01E1000_PSSR_SPEED_1000MBPS) { - /* Read the GIG initialization PCS register (0x00B4) */ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG, @@ -5176,8 +5180,8 @@ static s32 e1000_1000Mb_check_cable_length(struct e1000_hw *hw) hw->ffe_config_state = e1000_ffe_config_active; ret_val = e1000_write_phy_reg(hw, - IGP01E1000_PHY_DSP_FFE, - IGP01E1000_PHY_DSP_FFE_CM_CP); + IGP01E1000_PHY_DSP_FFE, + IGP01E1000_PHY_DSP_FFE_CM_CP); if (ret_val) return ret_val; break; @@ -5244,7 +5248,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up) msleep(20); ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_FORCE_GIGA); + IGP01E1000_IEEE_FORCE_GIGA); if (ret_val) return ret_val; for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { @@ -5265,7 +5269,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up) } ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_RESTART_AUTONEG); + IGP01E1000_IEEE_RESTART_AUTONEG); if (ret_val) return ret_val; @@ -5300,7 +5304,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up) msleep(20); ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_FORCE_GIGA); + IGP01E1000_IEEE_FORCE_GIGA); if (ret_val) return ret_val; ret_val = @@ -5310,7 +5314,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up) return ret_val; ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_RESTART_AUTONEG); + IGP01E1000_IEEE_RESTART_AUTONEG); if (ret_val) return ret_val; @@ -5347,9 +5351,8 @@ static s32 e1000_set_phy_mode(struct e1000_hw *hw) ret_val = e1000_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, 1, &eeprom_data); - if (ret_val) { + if (ret_val) return ret_val; - } if ((eeprom_data != EEPROM_RESERVED_WORD) && (eeprom_data & EEPROM_PHY_CLASS_A)) { @@ -5396,8 +5399,8 @@ static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active) * from the lowest speeds starting from 10Mbps. The capability is used * for Dx transitions and states */ - if (hw->mac_type == e1000_82541_rev_2 - || hw->mac_type == e1000_82547_rev_2) { + if (hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547_rev_2) { ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data); if (ret_val) @@ -5447,11 +5450,9 @@ static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active) if (ret_val) return ret_val; } - } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) - || (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL) - || (hw->autoneg_advertised == - AUTONEG_ADVERTISE_10_100_ALL)) { - + } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) || + (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL) || + (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) { if (hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) { phy_data |= IGP01E1000_GMII_FLEX_SPD; @@ -5475,7 +5476,6 @@ static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active) phy_data); if (ret_val) return ret_val; - } return E1000_SUCCESS; } @@ -5543,7 +5543,6 @@ static s32 e1000_set_vco_speed(struct e1000_hw *hw) return E1000_SUCCESS; } - /** * e1000_enable_mng_pass_thru - check for bmc pass through * @hw: Struct containing variables accessed by shared code -- cgit v1.2.3-59-g8ed1b From b77ac46bbae862dcb3f51296825c940404c69b0f Mon Sep 17 00:00:00 2001 From: Dmitry Fleytman Date: Tue, 13 Oct 2015 12:48:18 +0300 Subject: e1000e: fix division by zero on jumbo MTUs This patch fixes possible division by zero in receive interrupt handler when working without adaptive interrupt moderation. The adaptive interrupt moderation mechanism is typically disabled on jumbo MTUs. Signed-off-by: Dmitry Fleytman Signed-off-by: Leonid Bloch Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/netdev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 0003bf61b211..772447306c13 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -1959,8 +1959,10 @@ static irqreturn_t e1000_intr_msix_rx(int __always_unused irq, void *data) * previous interrupt. */ if (rx_ring->set_itr) { - writel(1000000000 / (rx_ring->itr_val * 256), - rx_ring->itr_register); + u32 itr = rx_ring->itr_val ? + 1000000000 / (rx_ring->itr_val * 256) : 0; + + writel(itr, rx_ring->itr_register); rx_ring->set_itr = 0; } -- cgit v1.2.3-59-g8ed1b From d17c7868b2f8e329dcee4ecd2f5d16cfc9b26ac8 Mon Sep 17 00:00:00 2001 From: Raanan Avargil Date: Thu, 15 Oct 2015 15:59:49 +0300 Subject: e1000e: Increase timeout of polling bit RSPCIPHY Due to timing changes to the ME firmware in Skylake, this timer needs to be increased to 300ms. Signed-off-by: Raanan Avargil Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/ich8lan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index 91a5a0ae9cd7..64c1f3620033 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -1984,7 +1984,7 @@ static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw) int i = 0; while ((blocked = !(er32(FWSM) & E1000_ICH_FWSM_RSPCIPHY)) && - (i++ < 10)) + (i++ < 30)) usleep_range(10000, 20000); return blocked ? E1000_BLK_PHY_RESET : 0; } -- cgit v1.2.3-59-g8ed1b From be06998f96ecb93938ad2cce46c4289bf7cf45bc Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Mon, 19 Oct 2015 04:23:29 -0600 Subject: igb: fix NULL derefs due to skipped SR-IOV enabling The combined effect of commits 6423fc3416 ("igb: do not re-init SR-IOV during probe") and ceee3450b3 ("igb: make sure SR-IOV init uses the right number of queues") causes VFs no longer getting set up, leading to NULL pointer dereferences due to the adapter's ->vf_data being NULL while ->vfs_allocated_count is non-zero. The first commit not only neglected the side effect of igb_sriov_reinit() that the second commit tried to account for, but also that of setting IGB_FLAG_HAS_MSIX, without which igb_enable_sriov() is effectively a no-op. Calling igb_{,re}set_interrupt_capability() as done here seems to address this, but I'm not sure whether this is better than sinply reverting the other two commits. Signed-off-by: Jan Beulich Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 061e1026af76..fa3b4cbea23b 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -2858,6 +2858,13 @@ static void igb_probe_vfs(struct igb_adapter *adapter) if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211)) return; + /* Of the below we really only want the effect of getting + * IGB_FLAG_HAS_MSIX set (if available), without which + * igb_enable_sriov() has no effect. + */ + igb_set_interrupt_capability(adapter, true); + igb_reset_interrupt_capability(adapter); + pci_sriov_set_totalvfs(pdev, 7); igb_enable_sriov(pdev, max_vfs); -- cgit v1.2.3-59-g8ed1b From 36be0eb62e8292b1dbe6fdc2163196f9f69ca422 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 5 Nov 2015 10:32:31 +0200 Subject: iwlwifi: mvm: close the SP if we send fewer frames than expected in SP When we have holes in the BA window, there might be frames that have been ACKed between the read and the right pointers. This means that these frames won't be scheduled again by the SCD and the firwmare won't see them. This invalidates the number of frames we tell the firmware to send. When we detect this case, tell mac80211 to close the SP and to send an EOSP so that the firmware can be in sync. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 6 +++++- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 6 ++++++ drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 30 ++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 566b65944fb8..78ff21a88a8b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1664,6 +1664,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, */ if (agg) { int remaining = cnt; + int sleep_tx_count; spin_lock_bh(&mvmsta->lock); for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) { @@ -1688,9 +1689,12 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, } remaining -= n_queued; } + sleep_tx_count = cnt - remaining; + if (reason == IEEE80211_FRAME_RELEASE_UAPSD) + mvmsta->sleep_tx_count = sleep_tx_count; spin_unlock_bh(&mvmsta->lock); - cmd.sleep_tx_count = cpu_to_le16(cnt - remaining); + cmd.sleep_tx_count = cpu_to_le16(sleep_tx_count); if (WARN_ON(cnt - remaining == 0)) { ieee80211_sta_eosp(sta); return; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 0631cc0a6d3c..ce1b16ac4c66 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -303,6 +303,11 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) * @tt_tx_protection: is thermal throttling enable Tx protection? * @disable_tx: is tx to this STA disabled? * @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON) + * @sleep_tx_count: the number of frames that we told the firmware to let out + * even when that station is asleep. This is useful in case the queue + * gets empty before all the frames were sent, which can happen when + * we are sending frames from an AMPDU queue and there was a hole in + * the BA window. To be used for UAPSD only. * * When mac80211 creates a station it reserves some space (hw->sta_data_size) * in the structure for use by driver. This structure is placed in that @@ -329,6 +334,7 @@ struct iwl_mvm_sta { bool disable_tx; u8 agg_tids; + u8 sleep_tx_count; }; static inline struct iwl_mvm_sta * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index aaebb5d4462a..cc0cdce84954 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -788,13 +788,43 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, if (tid != IWL_TID_NON_QOS) { struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + bool send_eosp_ndp = false; spin_lock_bh(&mvmsta->lock); tid_data->next_reclaimed = next_reclaimed; IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n", next_reclaimed); iwl_mvm_check_ratid_empty(mvm, sta, tid); + + if (mvmsta->sleep_tx_count) { + mvmsta->sleep_tx_count--; + if (mvmsta->sleep_tx_count && + !iwl_mvm_tid_queued(tid_data)) { + /* + * The number of frames in the queue + * dropped to 0 even if we sent less + * frames than we thought we had on the + * Tx queue. + * This means we had holes in the BA + * window that we just filled, ask + * mac80211 to send EOSP since the + * firmware won't know how to do that. + * Send NDP and the firmware will send + * EOSP notification that will trigger + * a call to ieee80211_sta_eosp(). + */ + send_eosp_ndp = true; + } + } + spin_unlock_bh(&mvmsta->lock); + if (send_eosp_ndp) { + iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, + IEEE80211_FRAME_RELEASE_UAPSD, + 1, tid, false, false); + mvmsta->sleep_tx_count = 0; + ieee80211_send_eosp_nullfunc(sta, tid); + } } if (mvmsta->next_status_eosp) { -- cgit v1.2.3-59-g8ed1b From 7b06a6909555ffb0140733cc4420222604140b27 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 19 Oct 2015 11:52:04 -0400 Subject: igb: improve handling of disconnected adapters Clean up array_rd32 so that it uses igb_rd32 the same as rd32, per the suggestion of Alexander Duyck, and use io_addr in more places, so that we don't have the need to call E1000_REMOVED (which simply looks for a null hw_addr) nearly as much. Signed-off-by: Jarod Wilson Acked-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_regs.h | 3 +-- drivers/net/ethernet/intel/igb/igb_main.c | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h index 0fdcd4d1b982..21d9d02885cb 100644 --- a/drivers/net/ethernet/intel/igb/e1000_regs.h +++ b/drivers/net/ethernet/intel/igb/e1000_regs.h @@ -386,8 +386,7 @@ do { \ #define array_wr32(reg, offset, value) \ wr32((reg) + ((offset) << 2), (value)) -#define array_rd32(reg, offset) \ - (readl(hw->hw_addr + reg + ((offset) << 2))) +#define array_rd32(reg, offset) (igb_rd32(hw, reg + ((offset) << 2))) /* DMA Coalescing registers */ #define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index fa3b4cbea23b..7afde455326d 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -946,7 +946,6 @@ static void igb_configure_msix(struct igb_adapter *adapter) static int igb_request_msix(struct igb_adapter *adapter) { struct net_device *netdev = adapter->netdev; - struct e1000_hw *hw = &adapter->hw; int i, err = 0, vector = 0, free_vector = 0; err = request_irq(adapter->msix_entries[vector].vector, @@ -959,7 +958,7 @@ static int igb_request_msix(struct igb_adapter *adapter) vector++; - q_vector->itr_register = hw->hw_addr + E1000_EITR(vector); + q_vector->itr_register = adapter->io_addr + E1000_EITR(vector); if (q_vector->rx.ring && q_vector->tx.ring) sprintf(q_vector->name, "%s-TxRx-%u", netdev->name, @@ -1230,7 +1229,7 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter, q_vector->tx.work_limit = adapter->tx_work_limit; /* initialize ITR configuration */ - q_vector->itr_register = adapter->hw.hw_addr + E1000_EITR(0); + q_vector->itr_register = adapter->io_addr + E1000_EITR(0); q_vector->itr_val = IGB_START_ITR; /* initialize pointer to rings */ -- cgit v1.2.3-59-g8ed1b From f3ed935de059b83394c3ecf2c64c93b57c8915fe Mon Sep 17 00:00:00 2001 From: Raanan Avargil Date: Tue, 20 Oct 2015 17:13:01 +0300 Subject: e1000e: initial support for i219-LM (3) i219-LM (3) is a LOM that will be available on systems with the Lewisburg Platform Controller Hub (PCH) chipset from Intel. This patch provides the initial support for the device. Signed-off-by: Raanan Avargil Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/hw.h | 1 + drivers/net/ethernet/intel/e1000e/ich8lan.c | 43 +++++++++++++++++++++-------- drivers/net/ethernet/intel/e1000e/netdev.c | 1 + 3 files changed, 34 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h index c9da4654e9ca..b3949d5bef5c 100644 --- a/drivers/net/ethernet/intel/e1000e/hw.h +++ b/drivers/net/ethernet/intel/e1000e/hw.h @@ -91,6 +91,7 @@ struct e1000_hw; #define E1000_DEV_ID_PCH_SPT_I219_V 0x1570 /* SPT PCH */ #define E1000_DEV_ID_PCH_SPT_I219_LM2 0x15B7 /* SPT-H PCH */ #define E1000_DEV_ID_PCH_SPT_I219_V2 0x15B8 /* SPT-H PCH */ +#define E1000_DEV_ID_PCH_LBG_I219_LM3 0x15B9 /* LBG PCH */ #define E1000_REVISION_4 4 diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index 64c1f3620033..a049e30639a1 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -3093,24 +3093,45 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank) struct e1000_nvm_info *nvm = &hw->nvm; u32 bank1_offset = nvm->flash_bank_size * sizeof(u16); u32 act_offset = E1000_ICH_NVM_SIG_WORD * 2 + 1; + u32 nvm_dword = 0; u8 sig_byte = 0; s32 ret_val; switch (hw->mac.type) { - /* In SPT, read from the CTRL_EXT reg instead of - * accessing the sector valid bits from the nvm - */ case e1000_pch_spt: - *bank = er32(CTRL_EXT) - & E1000_CTRL_EXT_NVMVS; - if ((*bank == 0) || (*bank == 1)) { - e_dbg("ERROR: No valid NVM bank present\n"); - return -E1000_ERR_NVM; - } else { - *bank = *bank - 2; + bank1_offset = nvm->flash_bank_size; + act_offset = E1000_ICH_NVM_SIG_WORD; + + /* set bank to 0 in case flash read fails */ + *bank = 0; + + /* Check bank 0 */ + ret_val = e1000_read_flash_dword_ich8lan(hw, act_offset, + &nvm_dword); + if (ret_val) + return ret_val; + sig_byte = (u8)((nvm_dword & 0xFF00) >> 8); + if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) == + E1000_ICH_NVM_SIG_VALUE) { + *bank = 0; return 0; } - break; + + /* Check bank 1 */ + ret_val = e1000_read_flash_dword_ich8lan(hw, act_offset + + bank1_offset, + &nvm_dword); + if (ret_val) + return ret_val; + sig_byte = (u8)((nvm_dword & 0xFF00) >> 8); + if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) == + E1000_ICH_NVM_SIG_VALUE) { + *bank = 1; + return 0; + } + + e_dbg("ERROR: No valid NVM bank present\n"); + return -E1000_ERR_NVM; case e1000_ich8lan: case e1000_ich9lan: eecd = er32(EECD); diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 772447306c13..775e38910681 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -7467,6 +7467,7 @@ static const struct pci_device_id e1000_pci_tbl[] = { { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V), board_pch_spt }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM2), board_pch_spt }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V2), board_pch_spt }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LBG_I219_LM3), board_pch_spt }, { 0, 0, 0, 0, 0, 0, 0 } /* terminate list */ }; -- cgit v1.2.3-59-g8ed1b From 08f0d23d97dcb49ca7ba65a3fc714a5d28a91d0c Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 10 Dec 2015 15:47:11 +0200 Subject: iwlwifi: avoid d0i3 commands when no/init ucode is loaded d0i3 commands are not supported in the init image, so take a reference to ensure we don't enter d0i3 during init image, and additional checks to prevent d0i3 commands when no fw image is loaded. Add a few WARN_ON_ONCE to the d0i3 enter/exit commands to ensure we send d0i3 commands only when the normal ucode is loaded. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 8 ++++++++ 3 files changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 7e2a8149411c..9ce7587e329d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -1316,6 +1316,7 @@ static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file, PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK); PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA); PRINT_MVM_REF(IWL_MVM_REF_FW_DBG_COLLECT); + PRINT_MVM_REF(IWL_MVM_REF_INIT_UCODE); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 612799a889db..3f0db29d5d56 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -294,6 +294,7 @@ enum iwl_mvm_ref_type { IWL_MVM_REF_EXIT_WORK, IWL_MVM_REF_PROTECT_CSA, IWL_MVM_REF_FW_DBG_COLLECT, + IWL_MVM_REF_INIT_UCODE, /* update debugfs.c when changing this */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 958984cc4d2e..31c16a185001 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -603,9 +603,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, goto out_free; mutex_lock(&mvm->mutex); + iwl_mvm_ref(mvm, IWL_MVM_REF_INIT_UCODE); err = iwl_run_init_mvm_ucode(mvm, true); if (!err || !iwlmvm_mod_params.init_dbg) iwl_trans_stop_device(trans); + iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE); mutex_unlock(&mvm->mutex); /* returns 0 if successful, 1 if success but in rfkill */ if (err < 0 && !iwlmvm_mod_params.init_dbg) { @@ -1213,6 +1215,9 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n"); + if (WARN_ON_ONCE(mvm->cur_ucode != IWL_UCODE_REGULAR)) + return -EINVAL; + set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); /* @@ -1420,6 +1425,9 @@ int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm) IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n"); + if (WARN_ON_ONCE(mvm->cur_ucode != IWL_UCODE_REGULAR)) + return -EINVAL; + mutex_lock(&mvm->d0i3_suspend_mutex); if (test_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags)) { IWL_DEBUG_RPM(mvm, "Deferring d0i3 exit until resume\n"); -- cgit v1.2.3-59-g8ed1b From 34672bb390fa73f1e201cc376291a5624dafab64 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 3 Dec 2015 16:10:07 +0200 Subject: iwlwifi: mvm: remove the vif parameter of iwl_mvm_configure_bcast_filter() Remove the vif parameter of iwl_mvm_configure_bcast_filter() as it's not being used. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index f428f1cb0034..7487eb08d156 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1734,8 +1734,8 @@ bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, return true; } -static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) + +static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm) { struct iwl_bcast_filter_cmd cmd; @@ -1749,8 +1749,7 @@ static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, sizeof(cmd), &cmd); } #else -static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm) { return 0; } @@ -1865,7 +1864,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, } iwl_mvm_recalc_multicast(mvm); - iwl_mvm_configure_bcast_filter(mvm, vif); + iwl_mvm_configure_bcast_filter(mvm); /* reset rssi values */ mvmvif->bf_data.ave_beacon_signal = 0; @@ -1916,7 +1915,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, if (changes & BSS_CHANGED_ARP_FILTER) { IWL_DEBUG_MAC80211(mvm, "arp filter changed\n"); - iwl_mvm_configure_bcast_filter(mvm, vif); + iwl_mvm_configure_bcast_filter(mvm); } } -- cgit v1.2.3-59-g8ed1b From b7282643bf46076b0898d9d6dfe2294db4616d15 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Thu, 17 Sep 2015 23:44:14 +0300 Subject: iwlwifi: replace d0i3_mode and wowlan_d0i3 with more generic variables The d0i3_mode variable is used to distinguish between transports that handle d0i3 entry during suspend by themselves (i.e. the slave transports) and those which rely on the op_mode layer to do it. The reason why the former do it by themselves is that they need to transition from d0i3 in runtime_suspend into d0i3 in system-wide suspend and this transition needs to happen before the op_mode's suspend flow is called. The wowlan_d0i3 element is also a bit confusing, because it just reflects the wowlan->any value for the trans to understand. This is a bit unclear in the code and not generic enough for future use. To make it clearer and to generalize the platform power mode settings, introduce two variables to indicate the platform power management modes used by the transport. Additionally, in order not to take too big a step in one patch, treat this new variables semantically in the same way as the old d0i3_mode element, introducing a iwl_mvm_enter_d0i3_on_suspend() function to help with that. This commit also adds the foundation for a new concept where the firmware configuration state (i.e. D0, D3 or D0i3) is abstracted from the platform PM mode we are in (i.e. runtime suspend or system-wide suspend). Signed-off-by: Luca Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 74 +++++++++++++++++++---- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 40 ++++++++---- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 16 +++-- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 13 ++++ drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 7 +-- 5 files changed, 111 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index f00c9ad9d3c2..8caf68bce1ef 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -669,18 +669,61 @@ enum iwl_trans_state { }; /** - * enum iwl_d0i3_mode - d0i3 mode + * DOC: Platform power management * - * @IWL_D0I3_MODE_OFF - d0i3 is disabled - * @IWL_D0I3_MODE_ON_IDLE - enter d0i3 when device is idle - * (e.g. no active references) - * @IWL_D0I3_MODE_ON_SUSPEND - enter d0i3 only on suspend - * (in case of 'any' trigger) + * There are two types of platform power management: system-wide + * (WoWLAN) and runtime. + * + * In system-wide power management the entire platform goes into a low + * power state (e.g. idle or suspend to RAM) at the same time and the + * device is configured as a wakeup source for the entire platform. + * This is usually triggered by userspace activity (e.g. the user + * presses the suspend button or a power management daemon decides to + * put the platform in low power mode). The device's behavior in this + * mode is dictated by the wake-on-WLAN configuration. + * + * In runtime power management, only the devices which are themselves + * idle enter a low power state. This is done at runtime, which means + * that the entire system is still running normally. This mode is + * usually triggered automatically by the device driver and requires + * the ability to enter and exit the low power modes in a very short + * time, so there is not much impact in usability. + * + * The terms used for the device's behavior are as follows: + * + * - D0: the device is fully powered and the host is awake; + * - D3: the device is in low power mode and only reacts to + * specific events (e.g. magic-packet received or scan + * results found); + * - D0I3: the device is in low power mode and reacts to any + * activity (e.g. RX); + * + * These terms reflect the power modes in the firmware and are not to + * be confused with the physical device power state. The NIC can be + * in D0I3 mode even if, for instance, the PCI device is in D3 state. */ -enum iwl_d0i3_mode { - IWL_D0I3_MODE_OFF = 0, - IWL_D0I3_MODE_ON_IDLE, - IWL_D0I3_MODE_ON_SUSPEND, + +/** + * enum iwl_plat_pm_mode - platform power management mode + * + * This enumeration describes the device's platform power management + * behavior when in idle mode (i.e. runtime power management) or when + * in system-wide suspend (i.e WoWLAN). + * + * @IWL_PLAT_PM_MODE_DISABLED: power management is disabled for this + * device. At runtime, this means that nothing happens and the + * device always remains in active. In system-wide suspend mode, + * it means that the all connections will be closed automatically + * by mac80211 before the platform is suspended. + * @IWL_PLAT_PM_MODE_D3: the device goes into D3 mode (i.e. WoWLAN). + * For runtime power management, this mode is not officially + * supported. + * @IWL_PLAT_PM_MODE_D0I3: the device goes into D0I3 mode. + */ +enum iwl_plat_pm_mode { + IWL_PLAT_PM_MODE_DISABLED, + IWL_PLAT_PM_MODE_D3, + IWL_PLAT_PM_MODE_D0I3, }; /** @@ -720,6 +763,12 @@ enum iwl_d0i3_mode { * the opmode. * @paging_download_buf: Buffer used for copying all of the pages before * downloading them to the FW. The buffer is allocated in the opmode + * @system_pm_mode: the system-wide power management mode in use. + * This mode is set dynamically, depending on the WoWLAN values + * configured from the userspace at runtime. + * @runtime_pm_mode: the runtime power management mode in use. This + * mode is set during the initialization phase and is not + * supposed to change during runtime. */ struct iwl_trans { const struct iwl_trans_ops *ops; @@ -770,9 +819,8 @@ struct iwl_trans { struct iwl_fw_paging *paging_db; void *paging_download_buf; - enum iwl_d0i3_mode d0i3_mode; - - bool wowlan_d0i3; + enum iwl_plat_pm_mode system_pm_mode; + enum iwl_plat_pm_mode runtime_pm_mode; /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 542de7401a8d..b1c99921c376 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1183,19 +1183,20 @@ remove_notif: int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_trans *trans = mvm->trans; int ret; /* make sure the d0i3 exit work is not pending */ flush_work(&mvm->d0i3_exit_work); - ret = iwl_trans_suspend(mvm->trans); + ret = iwl_trans_suspend(trans); if (ret) return ret; - mvm->trans->wowlan_d0i3 = wowlan->any; - if (mvm->trans->wowlan_d0i3) { - /* 'any' trigger means d0i3 usage */ - if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { + if (wowlan->any) { + trans->system_pm_mode = IWL_PLAT_PM_MODE_D0I3; + + if (iwl_mvm_enter_d0i3_on_suspend(mvm)) { ret = iwl_mvm_enter_d0i3_sync(mvm); if (ret) @@ -1206,11 +1207,13 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); mutex_unlock(&mvm->d0i3_suspend_mutex); - iwl_trans_d3_suspend(mvm->trans, false); + iwl_trans_d3_suspend(trans, false); return 0; } + trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; + return __iwl_mvm_suspend(hw, wowlan, false); } @@ -1973,8 +1976,9 @@ static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm) { bool exit_now; enum iwl_d3_status d3_status; + struct iwl_trans *trans = mvm->trans; - iwl_trans_d3_resume(mvm->trans, &d3_status, false); + iwl_trans_d3_resume(trans, &d3_status, false); /* * make sure to clear D0I3_DEFER_WAKEUP before @@ -1991,9 +1995,9 @@ static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm) _iwl_mvm_exit_d0i3(mvm); } - iwl_trans_resume(mvm->trans); + iwl_trans_resume(trans); - if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { + if (iwl_mvm_enter_d0i3_on_suspend(mvm)) { int ret = iwl_mvm_exit_d0i3(mvm->hw->priv); if (ret) @@ -2009,12 +2013,16 @@ static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm) int iwl_mvm_resume(struct ieee80211_hw *hw) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; - /* 'any' trigger means d0i3 was used */ - if (hw->wiphy->wowlan_config->any) - return iwl_mvm_resume_d0i3(mvm); + if (mvm->trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) + ret = iwl_mvm_resume_d0i3(mvm); else - return iwl_mvm_resume_d3(mvm); + ret = iwl_mvm_resume_d3(mvm); + + mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; + + return ret; } void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled) @@ -2038,6 +2046,8 @@ static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file) ieee80211_stop_queues(mvm->hw); synchronize_net(); + mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; + /* start pseudo D3 */ rtnl_lock(); err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true); @@ -2092,9 +2102,13 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) int remaining_time = 10; mvm->d3_test_active = false; + rtnl_lock(); __iwl_mvm_resume(mvm, true); rtnl_unlock(); + + mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; + iwl_abort_notification_waits(&mvm->notif_wait); ieee80211_restart_hw(mvm->hw); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 7487eb08d156..3a78a3f1610e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1087,15 +1087,13 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm) static void iwl_mvm_resume_complete(struct iwl_mvm *mvm) { - if (!iwl_mvm_is_d0i3_supported(mvm)) - return; - - if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) - if (!wait_event_timeout(mvm->d0i3_exit_waitq, - !test_bit(IWL_MVM_STATUS_IN_D0I3, - &mvm->status), - HZ)) - WARN_ONCE(1, "D0i3 exit on resume timed out\n"); + if (iwl_mvm_is_d0i3_supported(mvm) && + iwl_mvm_enter_d0i3_on_suspend(mvm)) + WARN_ONCE(!wait_event_timeout(mvm->d0i3_exit_waitq, + !test_bit(IWL_MVM_STATUS_IN_D0I3, + &mvm->status), + HZ), + "D0i3 exit on resume timed out\n"); } static void diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 3f0db29d5d56..3fc7199abc94 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -933,6 +933,19 @@ static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_DQA_SUPPORT); } +static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm) +{ + /* For now we only use this mode to differentiate between + * slave transports, which handle D0i3 entry in suspend by + * themselves in conjunction with runtime PM D0i3. So, this + * function is used to check whether we need to do anything + * when entering suspend or if the transport layer has already + * done it. + */ + return (mvm->trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) && + (mvm->trans->runtime_pm_mode != IWL_PLAT_PM_MODE_D0I3); +} + static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm) { bool nvm_lar = mvm->nvm_data->lar_enabled; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 0d927635d0dc..05d5a5b1e71a 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1213,7 +1213,7 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (trans->wowlan_d0i3) { + if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) { /* Enable persistence mode to avoid reset */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PERSIST_MODE); @@ -1237,7 +1237,7 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - if (!trans->wowlan_d0i3) { + if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D3) { /* * reset TX queues -- some of their registers reset during S3 * so if we don't reset everything here the D3 image would try @@ -1286,7 +1286,7 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, iwl_pcie_set_pwr(trans, false); - if (trans->wowlan_d0i3) { + if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) { iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); } else { @@ -2677,7 +2677,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } trans_pcie->inta_mask = CSR_INI_SET_MASK; - trans->d0i3_mode = IWL_D0I3_MODE_ON_SUSPEND; return trans; -- cgit v1.2.3-59-g8ed1b From f0cf5c98981353563b929801100c30071f0eeefb Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 26 Oct 2015 16:32:02 -0700 Subject: fm10k: use ether_addr_copy to copy MAC address Use the ether_addr_copy function instead of copying byte-by-byte in a for-loop by hand. Reported-by: Bruce Allan Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 8b9b6ba5b92b..bbf0b4aef241 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -266,7 +266,6 @@ static s32 fm10k_read_mac_addr_pf(struct fm10k_hw *hw) { u8 perm_addr[ETH_ALEN]; u32 serial_num; - int i; serial_num = fm10k_read_reg(hw, FM10K_SM_AREA(1)); @@ -288,10 +287,8 @@ static s32 fm10k_read_mac_addr_pf(struct fm10k_hw *hw) perm_addr[4] = (u8)(serial_num >> 8); perm_addr[5] = (u8)(serial_num); - for (i = 0; i < ETH_ALEN; i++) { - hw->mac.perm_addr[i] = perm_addr[i]; - hw->mac.addr[i] = perm_addr[i]; - } + ether_addr_copy(hw->mac.perm_addr, perm_addr); + ether_addr_copy(hw->mac.addr, perm_addr); return 0; } -- cgit v1.2.3-59-g8ed1b From 40423dd2a56ab95c41b69d9dd910199a0acfd5c5 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 26 Oct 2015 16:32:04 -0700 Subject: fm10k: do not use CamelCase Avoid the use of CamelCase for some variable names that previously slipped through review. Reported-by: Bruce Allan Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 12 ++++++------ drivers/net/ethernet/intel/fm10k/fm10k_type.h | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 15d8e10c2504..644b0c182911 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -1411,14 +1411,14 @@ static int fm10k_mbx_request_irq_pf(struct fm10k_intfc *interface) } /* Enable interrupts w/ no moderation for "other" interrupts */ - fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_PCIeFault), other_itr); - fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_SwitchUpDown), other_itr); - fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_SRAM), other_itr); - fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_MaxHoldTime), other_itr); - fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_VFLR), other_itr); + fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_pcie_fault), other_itr); + fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_switch_up_down), other_itr); + fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_sram), other_itr); + fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_max_hold_time), other_itr); + fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_vflr), other_itr); /* Enable interrupts w/ moderation for mailbox */ - fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_Mailbox), mbx_itr); + fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_mailbox), mbx_itr); /* Enable individual interrupt causes */ fm10k_write_reg(hw, FM10K_EIMR, FM10K_EIMR_ENABLE(PCA_FAULT) | diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h index 02727250ce1f..2fd962f97cb9 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h @@ -388,13 +388,13 @@ struct fm10k_hw; #define FM10K_SW_SYSTIME_PULSE(_n) ((_n) + 0x02252) enum fm10k_int_source { - fm10k_int_Mailbox = 0, - fm10k_int_PCIeFault = 1, - fm10k_int_SwitchUpDown = 2, - fm10k_int_SwitchEvent = 3, - fm10k_int_SRAM = 4, - fm10k_int_VFLR = 5, - fm10k_int_MaxHoldTime = 6, + fm10k_int_mailbox = 0, + fm10k_int_pcie_fault = 1, + fm10k_int_switch_up_down = 2, + fm10k_int_switch_event = 3, + fm10k_int_sram = 4, + fm10k_int_vflr = 5, + fm10k_int_max_hold_time = 6, fm10k_int_sources_max_pf }; -- cgit v1.2.3-59-g8ed1b From 4ab0f79bf313b16d4038b94d861ec2184d347c6d Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 26 Oct 2015 16:32:05 -0700 Subject: fm10k: remove unnecessary else block from if statements with return Improve code style by removing the unnecessary else block of an if statement which immediately returns. Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index bbf0b4aef241..76f273ae476b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -1232,8 +1232,8 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results, err = fm10k_iov_select_vid(vf_info, (u16)vid); if (err < 0) return err; - else - vid = err; + + vid = err; /* update VSI info for VF in regards to VLAN table */ err = hw->mac.ops.update_vlan(hw, vid, vf_info->vsi, set); @@ -1258,8 +1258,8 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results, err = fm10k_iov_select_vid(vf_info, vlan); if (err < 0) return err; - else - vlan = (u16)err; + + vlan = (u16)err; /* notify switch of request for new unicast address */ err = hw->mac.ops.update_uc_addr(hw, vf_info->glort, @@ -1284,8 +1284,8 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results, err = fm10k_iov_select_vid(vf_info, vlan); if (err < 0) return err; - else - vlan = (u16)err; + + vlan = (u16)err; /* notify switch of request for new multicast address */ err = hw->mac.ops.update_mc_addr(hw, vf_info->glort, -- cgit v1.2.3-59-g8ed1b From b80b1a51a19d613751d032fe213d7802d945f300 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 26 Oct 2015 16:32:06 -0700 Subject: fm10k: remove namespace pollution of fm10k_iov_msg_data_pf Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 8 -------- drivers/net/ethernet/intel/fm10k/fm10k_pf.h | 1 - 2 files changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 76f273ae476b..5bb6185c6209 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -1413,14 +1413,6 @@ s32 fm10k_iov_msg_lport_state_pf(struct fm10k_hw *hw, u32 **results, return err; } -const struct fm10k_msg_data fm10k_iov_msg_data_pf[] = { - FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test), - FM10K_VF_MSG_MSIX_HANDLER(fm10k_iov_msg_msix_pf), - FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_iov_msg_mac_vlan_pf), - FM10K_VF_MSG_LPORT_STATE_HANDLER(fm10k_iov_msg_lport_state_pf), - FM10K_TLV_MSG_ERROR_HANDLER(fm10k_tlv_msg_error), -}; - /** * fm10k_update_stats_hw_pf - Updates hardware related statistics of PF * @hw: pointer to hardware structure diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h index 40a0dbc62a04..9d77daa777bb 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h @@ -129,7 +129,6 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *, u32 **, struct fm10k_mbx_info *); s32 fm10k_iov_msg_lport_state_pf(struct fm10k_hw *, u32 **, struct fm10k_mbx_info *); -extern const struct fm10k_msg_data fm10k_iov_msg_data_pf[]; extern struct fm10k_info fm10k_pf_info; #endif /* _FM10K_PF_H */ -- cgit v1.2.3-59-g8ed1b From aa502b4a2425a269787fbe2c3a91054d5e648a64 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 2 Nov 2015 12:10:22 -0800 Subject: fm10k: consistently refer to VLANs and VLAN IDs Instead of using lowercase vlan, vid, or VID, always use VLAN or VLAN ID in comments when referring to VLANs. The original driver code was consistent, but recent patches have not been as consistent with this naming scheme. Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 4 ++-- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 8 ++++---- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 4 ++-- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 14 +++++++------- 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index fa26e20445a5..8595a3d7e142 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -138,7 +138,7 @@ struct fm10k_ring { * different for DCB and RSS modes */ u8 qos_pc; /* priority class of queue */ - u16 vid; /* default vlan ID of queue */ + u16 vid; /* default VLAN ID of queue */ u16 count; /* amount of descriptors */ u16 next_to_alloc; @@ -440,7 +440,7 @@ union fm10k_ftag_info { struct { /* dglort and sglort combined into a single 32bit desc read */ __le32 glort; - /* upper 16 bits of vlan are reserved 0 for swpri_type_user */ + /* upper 16 bits of VLAN are reserved 0 for swpri_type_user */ __le32 vlan; } d; struct { diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 79f6b7dd2362..c60b553d7caf 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -632,7 +632,7 @@ static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } - /* locate vlan header */ + /* locate VLAN header */ vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN); /* pull the 2 key pieces of data out of it */ @@ -778,7 +778,7 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) if (!set) clear_bit(vid, interface->active_vlans); - /* disable the default VID on ring if we have an active VLAN */ + /* disable the default VLAN ID on ring if we have an active VLAN */ for (i = 0; i < interface->num_rx_queues; i++) { struct fm10k_ring *rx_ring = interface->rx_ring[i]; u16 rx_vid = rx_ring->vid & (VLAN_N_VID - 1); @@ -789,7 +789,7 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) rx_ring->vid &= ~FM10K_VLAN_CLEAR; } - /* Do not remove default VID related entries from VLAN and MAC tables */ + /* Do not remove default VLAN ID related entries from VLAN and MAC tables */ if (!set && vid == hw->mac.default_vid) return 0; @@ -814,7 +814,7 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) if (err) goto err_out; - /* set vid prior to syncing/unsyncing the VLAN */ + /* set VLAN ID prior to syncing/unsyncing the VLAN */ interface->vid = vid + (set ? VLAN_N_VID : 0); /* Update the unicast and multicast address list to add/drop VLAN */ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 644b0c182911..b9ed94ae8d60 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -715,7 +715,7 @@ static void fm10k_configure_rx_ring(struct fm10k_intfc *interface, /* assign default VLAN to queue */ ring->vid = hw->mac.default_vid; - /* if we have an active VLAN, disable default VID */ + /* if we have an active VLAN, disable default VLAN ID */ if (test_bit(hw->mac.default_vid, interface->active_vlans)) ring->vid |= FM10K_VLAN_CLEAR; @@ -1292,7 +1292,7 @@ static s32 fm10k_update_pvid(struct fm10k_hw *hw, u32 **results, if (!fm10k_glort_valid_pf(hw, glort)) return FM10K_ERR_PARAM; - /* verify VID is valid */ + /* verify VLAN ID is valid */ if (pvid >= FM10K_VLAN_TABLE_VID_MAX) return FM10K_ERR_PARAM; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 5bb6185c6209..ad6381c94713 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -329,7 +329,7 @@ static s32 fm10k_update_xc_addr_pf(struct fm10k_hw *hw, u16 glort, /* clear set bit from VLAN ID */ vid &= ~FM10K_VLAN_CLEAR; - /* if glort or vlan are not valid return error */ + /* if glort or VLAN are not valid return error */ if (!fm10k_glort_valid_pf(hw, glort) || vid >= FM10K_VLAN_TABLE_VID_MAX) return FM10K_ERR_PARAM; @@ -921,7 +921,7 @@ err_out: txqctl |= (vf_idx << FM10K_TXQCTL_TC_SHIFT) | FM10K_TXQCTL_VF | vf_idx; - /* assign VID */ + /* assign VLAN ID */ for (i = 0; i < queues_per_pool; i++) fm10k_write_reg(hw, FM10K_TXQCTL(vf_q_idx + i), txqctl); @@ -1172,12 +1172,12 @@ s32 fm10k_iov_msg_msix_pf(struct fm10k_hw *hw, u32 **results, } /** - * fm10k_iov_select_vid - Select correct default VID + * fm10k_iov_select_vid - Select correct default VLAN ID * @hw: Pointer to hardware structure - * @vid: VID to correct + * @vid: VLAN ID to correct * - * Will report an error if VID is out of range. For VID = 0, it will return - * either the pf_vid or sw_vid depending on which one is set. + * Will report an error if the VLAN ID is out of range. For VID = 0, it will + * return either the pf_vid or sw_vid depending on which one is set. */ static inline s32 fm10k_iov_select_vid(struct fm10k_vf_info *vf_info, u16 vid) { @@ -1707,7 +1707,7 @@ s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *hw, u32 **results, if (!fm10k_glort_valid_pf(hw, glort)) return FM10K_ERR_PARAM; - /* verify VID is valid */ + /* verify VLAN ID is valid */ if (pvid >= FM10K_VLAN_TABLE_VID_MAX) return FM10K_ERR_PARAM; -- cgit v1.2.3-59-g8ed1b From e3b6e95d070cbca5e82279fea99e2dba8e38f960 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 26 Oct 2015 14:38:40 -0700 Subject: fm10k: bump driver version We haven't bumped the driver version in a while despite many fixes being pulled in from the out-of-tree Sourceforge driver. Update the version to match. Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index 1c17b6284daa..df43362f2284 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -28,7 +28,7 @@ #include "fm10k.h" -#define DRV_VERSION "0.15.2-k" +#define DRV_VERSION "0.19.3-k" const char fm10k_driver_version[] = DRV_VERSION; char fm10k_driver_name[] = "fm10k"; static const char fm10k_driver_string[] = -- cgit v1.2.3-59-g8ed1b From 5682366cecd1d40cb63f6a88dbe53349cb97e173 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 27 Oct 2015 12:51:09 -0700 Subject: fm10k: conditionally compile DCB and DebugFS support Rather than wrapping fm10k_dcbnl.c and fm10k_debugfs.c support with #ifdef blocks, just conditionally include the .o files in the Makefile. Also, since we're modifying it, update the copyright year on the Makefile. Signed-off-by: Jacob Keller Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/Makefile | 20 +++++++++++++++----- drivers/net/ethernet/intel/fm10k/fm10k.h | 4 ++++ drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c | 4 ---- drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c | 4 ---- 4 files changed, 19 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/Makefile b/drivers/net/ethernet/intel/fm10k/Makefile index 08859dd220a8..b006ff66d028 100644 --- a/drivers/net/ethernet/intel/fm10k/Makefile +++ b/drivers/net/ethernet/intel/fm10k/Makefile @@ -1,7 +1,7 @@ ################################################################################ # # Intel Ethernet Switch Host Interface Driver -# Copyright(c) 2013 - 2014 Intel Corporation. +# Copyright(c) 2013 - 2015 Intel Corporation. # # This program is free software; you can redistribute it and/or modify it # under the terms and conditions of the GNU General Public License, @@ -27,7 +27,17 @@ obj-$(CONFIG_FM10K) += fm10k.o -fm10k-objs := fm10k_main.o fm10k_common.o fm10k_pci.o \ - fm10k_netdev.o fm10k_ethtool.o fm10k_pf.o fm10k_vf.o \ - fm10k_mbx.o fm10k_iov.o fm10k_tlv.o \ - fm10k_debugfs.o fm10k_ptp.o fm10k_dcbnl.o +fm10k-y := fm10k_main.o \ + fm10k_common.o \ + fm10k_pci.o \ + fm10k_ptp.o \ + fm10k_netdev.o \ + fm10k_ethtool.o \ + fm10k_pf.o \ + fm10k_vf.o \ + fm10k_mbx.o \ + fm10k_iov.o \ + fm10k_tlv.o + +fm10k-$(CONFIG_DEBUG_FS) += fm10k_debugfs.o +fm10k-$(CONFIG_DCB) += fm10k_dcbnl.o diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index 8595a3d7e142..02219d935208 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -557,5 +557,9 @@ int fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr); int fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr); /* DCB */ +#ifdef CONFIG_DCB void fm10k_dcbnl_set_ops(struct net_device *dev); +#else +static inline void fm10k_dcbnl_set_ops(struct net_device *dev) {} +#endif #endif /* _FM10K_H_ */ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c b/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c index 5c7a4d7662d8..2be4361839db 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c @@ -20,7 +20,6 @@ #include "fm10k.h" -#ifdef CONFIG_DCB /** * fm10k_dcbnl_ieee_getets - get the ETS configuration for the device * @dev: netdev interface for the device @@ -155,7 +154,6 @@ static const struct dcbnl_rtnl_ops fm10k_dcbnl_ops = { .setdcbx = fm10k_dcbnl_setdcbx, }; -#endif /* CONFIG_DCB */ /** * fm10k_dcbnl_set_ops - Configures dcbnl ops pointer for netdev * @dev: netdev interface for the device @@ -164,11 +162,9 @@ static const struct dcbnl_rtnl_ops fm10k_dcbnl_ops = { **/ void fm10k_dcbnl_set_ops(struct net_device *dev) { -#ifdef CONFIG_DCB struct fm10k_intfc *interface = netdev_priv(dev); struct fm10k_hw *hw = &interface->hw; if (hw->mac.type == fm10k_mac_pf) dev->dcbnl_ops = &fm10k_dcbnl_ops; -#endif /* CONFIG_DCB */ } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c index 5304bc1fbecd..5d6137faf7d1 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c @@ -18,8 +18,6 @@ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 */ -#ifdef CONFIG_DEBUG_FS - #include "fm10k.h" #include @@ -258,5 +256,3 @@ void fm10k_dbg_exit(void) debugfs_remove_recursive(dbg_root); dbg_root = NULL; } - -#endif /* CONFIG_DEBUG_FS */ -- cgit v1.2.3-59-g8ed1b From 587731e684dcf3522215194a02357d26b9bc7277 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 27 Oct 2015 16:59:12 -0700 Subject: fm10k: Cleanup MSI-X interrupts in case of failure If the q_vector allocation fails we should free the resources associated with the MSI-X vector table. Signed-off-by: Alexander Duyck Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index df43362f2284..d8b780006394 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -1998,8 +1998,10 @@ int fm10k_init_queueing_scheme(struct fm10k_intfc *interface) /* Allocate memory for queues */ err = fm10k_alloc_q_vectors(interface); - if (err) + if (err) { + fm10k_reset_msix_capability(interface); return err; + } /* Map rings to devices, and map devices to physical queues */ fm10k_assign_rings(interface); -- cgit v1.2.3-59-g8ed1b From e00e23bceba48a8f0c94fefe26948404cbd43d0a Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 27 Oct 2015 16:59:18 -0700 Subject: fm10k: Cleanup exception handling for mailbox interrupt This patch addresses two issues. First is the fact that the fm10k_mbx_free_irq was assuming msix_entries was valid and that will not always be the case. As such we need to add a check for if it is NULL. Second is the fact that we weren't freeing the IRQ if the mailbox API returned an error on trying to connect. Signed-off-by: Alexander Duyck Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index b9ed94ae8d60..694c6486b5ee 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -1124,6 +1124,10 @@ void fm10k_mbx_free_irq(struct fm10k_intfc *interface) struct fm10k_hw *hw = &interface->hw; int itr_reg; + /* no mailbox IRQ to free if MSI-X is not enabled */ + if (!interface->msix_entries) + return; + /* disconnect the mailbox */ hw->mbx.ops.disconnect(hw, &hw->mbx); @@ -1446,10 +1450,15 @@ int fm10k_mbx_request_irq(struct fm10k_intfc *interface) err = fm10k_mbx_request_irq_pf(interface); else err = fm10k_mbx_request_irq_vf(interface); + if (err) + return err; /* connect mailbox */ - if (!err) - err = hw->mbx.ops.connect(hw, &hw->mbx); + err = hw->mbx.ops.connect(hw, &hw->mbx); + + /* if the mailbox failed to connect, then free IRQ */ + if (err) + fm10k_mbx_free_irq(interface); return err; } -- cgit v1.2.3-59-g8ed1b From e214d85b4a0c358c5aefa45d72bb00138fbcb6ac Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Wed, 28 Oct 2015 16:04:40 -0700 Subject: fm10k: do not inline fm10k_iov_select_vid() The function declaration does not need to be 'inline'd here. Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index ad6381c94713..750518d00cbe 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -1179,7 +1179,7 @@ s32 fm10k_iov_msg_msix_pf(struct fm10k_hw *hw, u32 **results, * Will report an error if the VLAN ID is out of range. For VID = 0, it will * return either the pf_vid or sw_vid depending on which one is set. */ -static inline s32 fm10k_iov_select_vid(struct fm10k_vf_info *vf_info, u16 vid) +static s32 fm10k_iov_select_vid(struct fm10k_vf_info *vf_info, u16 vid) { if (!vid) return vf_info->pf_vid ? vf_info->pf_vid : vf_info->sw_vid; -- cgit v1.2.3-59-g8ed1b From a4fcad656e1100bdda9b0b752b93a1a276810469 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Wed, 28 Oct 2015 17:19:40 -0700 Subject: fm10k: whitespace cleanups Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 4 ++-- drivers/net/ethernet/intel/fm10k/fm10k_main.c | 4 ++-- drivers/net/ethernet/intel/fm10k/fm10k_mbx.c | 1 + drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 4 ++-- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 5 ++--- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 1 + drivers/net/ethernet/intel/fm10k/fm10k_vf.c | 1 + 7 files changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index 109e2111bdda..3b857f46f612 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -127,7 +127,7 @@ static const struct fm10k_stats fm10k_gstrings_mbx_stats[] = { #define FM10K_MBX_STATS_LEN ARRAY_SIZE(fm10k_gstrings_mbx_stats) #define FM10K_QUEUE_STATS_LEN(_n) \ - ( (_n) * 2 * (sizeof(struct fm10k_queue_stats) / sizeof(u64))) + ((_n) * 2 * (sizeof(struct fm10k_queue_stats) / sizeof(u64))) #define FM10K_STATIC_STATS_LEN (FM10K_GLOBAL_STATS_LEN + \ FM10K_NETDEV_STATS_LEN + \ @@ -322,6 +322,7 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev, if ((interface->flags & FM10K_FLAG_DEBUG_STATS) && iov_data) { for (i = 0; i < iov_data->num_vfs; i++) { struct fm10k_vf_info *vf_info; + vf_info = &iov_data->vf_info[i]; /* skip stats if we don't have a vf info */ @@ -1020,7 +1021,6 @@ static int fm10k_set_priv_flags(struct net_device *netdev, u32 priv_flags) return 0; } - static u32 fm10k_get_reta_size(struct net_device __always_unused *netdev) { return FM10K_RETA_SIZE * FM10K_RETA_ENTRIES_PER_REG; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index d8b780006394..75ff1092b7ee 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -917,7 +917,7 @@ static u8 fm10k_tx_desc_flags(struct sk_buff *skb, u32 tx_flags) /* set timestamping bits */ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && likely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) - desc_flags |= FM10K_TXD_FLAG_TIME; + desc_flags |= FM10K_TXD_FLAG_TIME; /* set checksum offload bits */ desc_flags |= FM10K_SET_FLAG(tx_flags, FM10K_TX_FLAGS_CSUM, @@ -1462,7 +1462,7 @@ static int fm10k_poll(struct napi_struct *napi, int budget) * allow the budget to go below 1 because we'll exit polling */ if (q_vector->rx.count > 1) - per_ring_budget = max(budget/q_vector->rx.count, 1); + per_ring_budget = max(budget / q_vector->rx.count, 1); else per_ring_budget = budget; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c index 2bce47490723..b31bbc28e9e3 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c @@ -2140,6 +2140,7 @@ s32 fm10k_sm_mbx_init(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx, { mbx->mbx_reg = FM10K_GMBX; mbx->mbmem_reg = FM10K_MBMEM_PF(0); + /* start out in closed state */ mbx->state = FM10K_STATE_CLOSED; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index c60b553d7caf..c9c94c41fca8 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -608,7 +608,7 @@ static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev) unsigned int r_idx = skb->queue_mapping; int err; - if ((skb->protocol == htons(ETH_P_8021Q)) && + if ((skb->protocol == htons(ETH_P_8021Q)) && !skb_vlan_tag_present(skb)) { /* FM10K only supports hardware tagging, any tags in frame * are considered 2nd level or "outer" tags @@ -705,7 +705,7 @@ static void fm10k_tx_timeout(struct net_device *netdev) } else { netif_info(interface, drv, netdev, "Fake Tx hang detected with timeout of %d seconds\n", - netdev->watchdog_timeo/HZ); + netdev->watchdog_timeo / HZ); /* fake Tx hang - increase the kernel timeout */ if (netdev->watchdog_timeo < TX_TIMEO_LIMIT) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 694c6486b5ee..84424a86954c 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -919,7 +919,7 @@ void fm10k_netpoll(struct net_device *netdev) #endif #define FM10K_ERR_MSG(type) case (type): error = #type; break static void fm10k_handle_fault(struct fm10k_intfc *interface, int type, - struct fm10k_fault *fault) + struct fm10k_fault *fault) { struct pci_dev *pdev = interface->pdev; struct fm10k_hw *hw = &interface->hw; @@ -1890,8 +1890,7 @@ static void fm10k_slot_warn(struct fm10k_intfc *interface) * The OS initialization, configuring of the interface private structure, * and a hardware reset occur. **/ -static int fm10k_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int fm10k_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *netdev; struct fm10k_intfc *interface; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 750518d00cbe..523fe702b94d 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -414,6 +414,7 @@ static s32 fm10k_update_xcast_mode_pf(struct fm10k_hw *hw, u16 glort, u8 mode) if (mode > FM10K_XCAST_MODE_NONE) return FM10K_ERR_PARAM; + /* if glort is not valid return error */ if (!fm10k_glort_valid_pf(hw, glort)) return FM10K_ERR_PARAM; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c index 2af697df5abc..5445c0fab49f 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c @@ -442,6 +442,7 @@ static s32 fm10k_update_xcast_mode_vf(struct fm10k_hw *hw, u16 glort, u8 mode) if (mode > FM10K_XCAST_MODE_NONE) return FM10K_ERR_PARAM; + /* generate message requesting to change xcast mode */ fm10k_tlv_msg_init(msg, FM10K_VF_MSG_ID_LPORT_STATE); fm10k_tlv_attr_put_u8(msg, FM10K_LPORT_STATE_MSG_XCAST_MODE, mode); -- cgit v1.2.3-59-g8ed1b From 3e515645b198ac60f493822f9193dd8107e1b8d0 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Wed, 28 Oct 2015 17:19:45 -0700 Subject: fm10k: use BIT() macro instead of open-coded bit-shifting Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 2 +- drivers/net/ethernet/intel/fm10k/fm10k_type.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index 02219d935208..849e36d8e10e 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -419,7 +419,7 @@ static inline u16 fm10k_desc_unused(struct fm10k_ring *ring) (&(((union fm10k_rx_desc *)((R)->desc))[i])) #define FM10K_MAX_TXD_PWR 14 -#define FM10K_MAX_DATA_PER_TXD (1 << FM10K_MAX_TXD_PWR) +#define FM10K_MAX_DATA_PER_TXD BIT(FM10K_MAX_TXD_PWR) /* Tx Descriptors needed, worst case */ #define TXD_USE_COUNT(S) DIV_ROUND_UP((S), FM10K_MAX_DATA_PER_TXD) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h index 2fd962f97cb9..098883d2875f 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h @@ -354,7 +354,7 @@ struct fm10k_hw; #define FM10K_VLAN_TABLE_VID_MAX 4096 #define FM10K_VLAN_TABLE_VSI_MAX 64 #define FM10K_VLAN_LENGTH_SHIFT 16 -#define FM10K_VLAN_CLEAR (1 << 15) +#define FM10K_VLAN_CLEAR BIT(15) #define FM10K_VLAN_ALL \ ((FM10K_VLAN_TABLE_VID_MAX - 1) << FM10K_VLAN_LENGTH_SHIFT) -- cgit v1.2.3-59-g8ed1b From bb269e8bb5ab5015574cf44af6233278af629bcb Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Wed, 28 Oct 2015 17:19:51 -0700 Subject: fm10k: cleanup namespace pollution Make functions that should be static. While we're at it, fix the function header comment for fm10k_tlv_attr_nest_stop(), and update the copyright header for fm10k_pf.h, fm10k_tlv.c and fm10k_tlv.h. Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 4 ++-- drivers/net/ethernet/intel/fm10k/fm10k_pf.h | 4 +--- drivers/net/ethernet/intel/fm10k/fm10k_tlv.c | 18 +++++++++--------- drivers/net/ethernet/intel/fm10k/fm10k_tlv.h | 7 +------ 4 files changed, 13 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 523fe702b94d..ac3dc284323c 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -1688,8 +1688,8 @@ const struct fm10k_tlv_attr fm10k_update_pvid_msg_attr[] = { * * This handler configures the default VLAN for the PF **/ -s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *hw, u32 **results, - struct fm10k_mbx_info *mbx) +static s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *hw, u32 **results, + struct fm10k_mbx_info *mbx) { u16 glort, pvid; u32 pvid_update; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h index 9d77daa777bb..a8fc512a2416 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h @@ -1,5 +1,5 @@ /* Intel Ethernet Switch Host Interface Driver - * Copyright(c) 2013 - 2014 Intel Corporation. + * Copyright(c) 2013 - 2015 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -107,8 +107,6 @@ extern const struct fm10k_tlv_attr fm10k_lport_map_msg_attr[]; #define FM10K_PF_MSG_LPORT_MAP_HANDLER(func) \ FM10K_MSG_HANDLER(FM10K_PF_MSG_ID_LPORT_MAP, \ fm10k_lport_map_msg_attr, func) -s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *, u32 **, - struct fm10k_mbx_info *); extern const struct fm10k_tlv_attr fm10k_update_pvid_msg_attr[]; #define FM10K_PF_MSG_UPDATE_PVID_HANDLER(func) \ FM10K_MSG_HANDLER(FM10K_PF_MSG_ID_UPDATE_PVID, \ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c index 9b29d7b0377a..95afb5c0c9c4 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c @@ -1,5 +1,5 @@ /* Intel Ethernet Switch Host Interface Driver - * Copyright(c) 2013 - 2014 Intel Corporation. + * Copyright(c) 2013 - 2015 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -48,8 +48,8 @@ s32 fm10k_tlv_msg_init(u32 *msg, u16 msg_id) * the attribute buffer. It will return success if provided with a valid * pointers. **/ -s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id, - const unsigned char *string) +static s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id, + const unsigned char *string) { u32 attr_data = 0, len = 0; u32 *attr; @@ -98,7 +98,7 @@ s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id, * it in the array pointed by by string. It will return success if provided * with a valid pointers. **/ -s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string) +static s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string) { u32 len; @@ -353,7 +353,7 @@ s32 fm10k_tlv_attr_get_le_struct(u32 *attr, void *le_struct, u32 len) * function will return NULL on failure, and a pointer to the start * of the nested attributes on success. **/ -u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id) +static u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id) { u32 *attr; @@ -370,7 +370,7 @@ u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id) } /** - * fm10k_tlv_attr_nest_start - Start a set of nested attributes + * fm10k_tlv_attr_nest_stop - Stop a set of nested attributes * @msg: Pointer to message block * * This function closes off an existing set of nested attributes. The @@ -378,7 +378,7 @@ u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id) * the case of a nest within the nest this would be the outer nest pointer. * This function will return success provided all pointers are valid. **/ -s32 fm10k_tlv_attr_nest_stop(u32 *msg) +static s32 fm10k_tlv_attr_nest_stop(u32 *msg) { u32 *attr; u32 len; @@ -483,8 +483,8 @@ static s32 fm10k_tlv_attr_validate(u32 *attr, * FM10K_NOT_IMPLEMENTED for any attribute that is outside of the array * and 0 on success. **/ -s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results, - const struct fm10k_tlv_attr *tlv_attr) +static s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results, + const struct fm10k_tlv_attr *tlv_attr) { u32 i, attr_id, offset = 0; s32 err = 0; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h index 7e045e8bf1eb..d5ad359c1d54 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h @@ -1,5 +1,5 @@ /* Intel Ethernet Switch Host Interface Driver - * Copyright(c) 2013 - 2014 Intel Corporation. + * Copyright(c) 2013 - 2015 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -106,8 +106,6 @@ struct fm10k_msg_data { #define FM10K_MSG_HANDLER(id, attr, func) { id, attr, func } s32 fm10k_tlv_msg_init(u32 *, u16); -s32 fm10k_tlv_attr_put_null_string(u32 *, u16, const unsigned char *); -s32 fm10k_tlv_attr_get_null_string(u32 *, unsigned char *); s32 fm10k_tlv_attr_put_mac_vlan(u32 *, u16, const u8 *, u16); s32 fm10k_tlv_attr_get_mac_vlan(u32 *, u8 *, u16 *); s32 fm10k_tlv_attr_put_bool(u32 *, u16); @@ -147,9 +145,6 @@ s32 fm10k_tlv_attr_get_value(u32 *, void *, u32); fm10k_tlv_attr_get_value(attr, ptr, sizeof(s64)) s32 fm10k_tlv_attr_put_le_struct(u32 *, u16, const void *, u32); s32 fm10k_tlv_attr_get_le_struct(u32 *, void *, u32); -u32 *fm10k_tlv_attr_nest_start(u32 *, u16); -s32 fm10k_tlv_attr_nest_stop(u32 *); -s32 fm10k_tlv_attr_parse(u32 *, u32 **, const struct fm10k_tlv_attr *); s32 fm10k_tlv_msg_parse(struct fm10k_hw *, u32 *, struct fm10k_mbx_info *, const struct fm10k_msg_data *); s32 fm10k_tlv_msg_error(struct fm10k_hw *hw, u32 **results, -- cgit v1.2.3-59-g8ed1b From 3d02b3df733af0531789fff3fc999f9ca843b66e Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Wed, 28 Oct 2015 17:19:56 -0700 Subject: fm10k: cleanup overly long lines Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 2 +- drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 12 ++++++---- drivers/net/ethernet/intel/fm10k/fm10k_mbx.c | 2 +- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 4 +++- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 29 +++++++++++++----------- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 7 +++--- 6 files changed, 33 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index 849e36d8e10e..021abe301f2f 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -164,7 +164,7 @@ struct fm10k_ring_container { unsigned int total_packets; /* total packets processed this int */ u16 work_limit; /* total work allowed per interrupt */ u16 itr; /* interrupt throttle rate value */ - u8 itr_scale; /* ITR adjustment scaler based on PCI speed */ + u8 itr_scale; /* ITR adjustment based on PCI speed */ u8 count; /* total number of rings in vector */ }; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index 3b857f46f612..2f6a05b57228 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -259,7 +259,8 @@ static int fm10k_get_sset_count(struct net_device *dev, int sset) stats_len += FM10K_DEBUG_STATS_LEN; if (iov_data) - stats_len += FM10K_MBX_STATS_LEN * iov_data->num_vfs; + stats_len += FM10K_MBX_STATS_LEN * + iov_data->num_vfs; } return stats_len; @@ -298,14 +299,16 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev, if (interface->flags & FM10K_FLAG_DEBUG_STATS) { for (i = 0; i < FM10K_DEBUG_STATS_LEN; i++) { - p = (char *)interface + fm10k_gstrings_debug_stats[i].stat_offset; + p = (char *)interface + + fm10k_gstrings_debug_stats[i].stat_offset; *(data++) = (fm10k_gstrings_debug_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } } for (i = 0; i < FM10K_MBX_STATS_LEN; i++) { - p = (char *)&interface->hw.mbx + fm10k_gstrings_mbx_stats[i].stat_offset; + p = (char *)&interface->hw.mbx + + fm10k_gstrings_mbx_stats[i].stat_offset; *(data++) = (fm10k_gstrings_mbx_stats[i].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } @@ -332,7 +335,8 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev, } for (j = 0; j < FM10K_MBX_STATS_LEN; j++) { - p = (char *)&vf_info->mbx + fm10k_gstrings_mbx_stats[j].stat_offset; + p = (char *)&vf_info->mbx + + fm10k_gstrings_mbx_stats[j].stat_offset; *(data++) = (fm10k_gstrings_mbx_stats[j].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c index b31bbc28e9e3..c7fea47b8909 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c @@ -903,7 +903,7 @@ static void fm10k_mbx_create_disconnect_hdr(struct fm10k_mbx_info *mbx) } /** - * fm10k_mbx_create_fake_disconnect_hdr - Generate a false disconnect mailbox header + * fm10k_mbx_create_fake_disconnect_hdr - Generate a false disconnect mbox hdr * @mbx: pointer to mailbox * * This function creates a fake disconnect header for loading into remote diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index c9c94c41fca8..d9854d39576d 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -789,7 +789,9 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) rx_ring->vid &= ~FM10K_VLAN_CLEAR; } - /* Do not remove default VLAN ID related entries from VLAN and MAC tables */ + /* Do not remove default VLAN ID related entries from VLAN and MAC + * tables + */ if (!set && vid == hw->mac.default_vid) return 0; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 84424a86954c..e820b5f34eeb 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -180,7 +180,8 @@ static void fm10k_reinit(struct fm10k_intfc *interface) err = fm10k_init_queueing_scheme(interface); if (err) { - dev_err(&interface->pdev->dev, "init_queueing_scheme failed: %d\n", err); + dev_err(&interface->pdev->dev, + "init_queueing_scheme failed: %d\n", err); goto reinit_err; } @@ -1866,17 +1867,18 @@ static void fm10k_slot_warn(struct fm10k_intfc *interface) return; } - if (max_gts < expected_gts) { - dev_warn(&interface->pdev->dev, - "This device requires %dGT/s of bandwidth for optimal performance.\n", - expected_gts); - dev_warn(&interface->pdev->dev, - "A %sslot with x%d lanes is suggested.\n", - (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s " : - hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s " : - hw->bus_caps.speed == fm10k_bus_speed_8000 ? "8.0GT/s " : ""), - hw->bus_caps.width); - } + if (max_gts >= expected_gts) + return; + + dev_warn(&interface->pdev->dev, + "This device requires %dGT/s of bandwidth for optimal performance.\n", + expected_gts); + dev_warn(&interface->pdev->dev, + "A %sslot with x%d lanes is suggested.\n", + (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s " : + hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s " : + hw->bus_caps.speed == fm10k_bus_speed_8000 ? "8.0GT/s " : ""), + hw->bus_caps.width); } /** @@ -2294,7 +2296,8 @@ static void fm10k_io_resume(struct pci_dev *pdev) err = fm10k_init_queueing_scheme(interface); if (err) { - dev_err(&interface->pdev->dev, "init_queueing_scheme failed: %d\n", err); + dev_err(&interface->pdev->dev, + "init_queueing_scheme failed: %d\n", err); return; } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index ac3dc284323c..808307e67718 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -1441,9 +1441,10 @@ static void fm10k_update_hw_stats_pf(struct fm10k_hw *hw, xec = fm10k_read_hw_stats_32b(hw, FM10K_STATS_XEC, &stats->xec); vlan_drop = fm10k_read_hw_stats_32b(hw, FM10K_STATS_VLAN_DROP, &stats->vlan_drop); - loopback_drop = fm10k_read_hw_stats_32b(hw, - FM10K_STATS_LOOPBACK_DROP, - &stats->loopback_drop); + loopback_drop = + fm10k_read_hw_stats_32b(hw, + FM10K_STATS_LOOPBACK_DROP, + &stats->loopback_drop); nodesc_drop = fm10k_read_hw_stats_32b(hw, FM10K_STATS_NODESC_DROP, &stats->nodesc_drop); -- cgit v1.2.3-59-g8ed1b From 504b0fdf92bfb28b88b79fe3bf356b840b2e555c Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 29 Oct 2015 13:43:40 -0700 Subject: fm10k: initialize xps at driver load Similar to ixgbe and i40e, initialize XPS on driver load so that we can take advantage of this kernel feature. Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 3 +++ drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index 021abe301f2f..b34bb008b104 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -66,6 +67,7 @@ struct fm10k_l2_accel { enum fm10k_ring_state_t { __FM10K_TX_DETECT_HANG, __FM10K_HANG_CHECK_ARMED, + __FM10K_TX_XPS_INIT_DONE, }; #define check_for_tx_hang(ring) \ @@ -209,6 +211,7 @@ struct fm10k_q_vector { struct fm10k_ring_container rx, tx; struct napi_struct napi; + cpumask_t affinity_mask; char name[IFNAMSIZ + 9]; #ifdef CONFIG_DEBUG_FS diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index e820b5f34eeb..020f6dce4154 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -601,6 +601,13 @@ static void fm10k_configure_tx_ring(struct fm10k_intfc *interface, fm10k_write_reg(hw, FM10K_PFVTCTL(reg_idx), FM10K_PFVTCTL_FTAG_DESC_ENABLE); + /* Initialize XPS */ + if (!test_and_set_bit(__FM10K_TX_XPS_INIT_DONE, &ring->state) && + ring->q_vector) + netif_set_xps_queue(ring->netdev, + &ring->q_vector->affinity_mask, + ring->queue_index); + /* enable queue */ fm10k_write_reg(hw, FM10K_TXDCTL(reg_idx), txdctl); } @@ -1488,8 +1495,10 @@ void fm10k_qv_free_irq(struct fm10k_intfc *interface) if (!q_vector->tx.count && !q_vector->rx.count) continue; - /* disable interrupts */ + /* clear the affinity_mask in the IRQ descriptor */ + irq_set_affinity_hint(entry->vector, NULL); + /* disable interrupts */ writel(FM10K_ITR_MASK_SET, q_vector->itr); free_irq(entry->vector, q_vector); @@ -1547,6 +1556,9 @@ int fm10k_qv_request_irq(struct fm10k_intfc *interface) goto err_out; } + /* assign the mask for this irq */ + irq_set_affinity_hint(entry->vector, &q_vector->affinity_mask); + /* Enable q_vector */ writel(FM10K_ITR_ENABLE, q_vector->itr); @@ -1567,8 +1579,10 @@ err_out: if (!q_vector->tx.count && !q_vector->rx.count) continue; - /* disable interrupts */ + /* clear the affinity_mask in the IRQ descriptor */ + irq_set_affinity_hint(entry->vector, NULL); + /* disable interrupts */ writel(FM10K_ITR_MASK_SET, q_vector->itr); free_irq(entry->vector, q_vector); -- cgit v1.2.3-59-g8ed1b From abe492b4f50c3ae2ebcfaa2f5c16176aebaa1c68 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Thu, 10 Dec 2015 12:37:45 -0800 Subject: geneve: UDP checksum configuration via netlink Add support to enable and disable UDP checksums via netlink. This is similar to how VXLAN and GUE allow this. This includes support for enabling the UDP zero checksum (for both TX and RX). Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- drivers/net/geneve.c | 93 +++++++++++++++++++++++++++++++++----------- include/uapi/linux/if_link.h | 3 ++ 2 files changed, 73 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index de5c30c9f059..0750d7a93878 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -71,8 +71,14 @@ struct geneve_dev { __be16 dst_port; bool collect_md; struct gro_cells gro_cells; + u32 flags; }; +/* Geneve device flags */ +#define GENEVE_F_UDP_CSUM BIT(0) +#define GENEVE_F_UDP_ZERO_CSUM6_TX BIT(1) +#define GENEVE_F_UDP_ZERO_CSUM6_RX BIT(2) + struct geneve_sock { bool collect_md; struct list_head list; @@ -81,6 +87,7 @@ struct geneve_sock { int refcnt; struct udp_offload udp_offloads; struct hlist_head vni_list[VNI_HASH_SIZE]; + u32 flags; }; static inline __u32 geneve_net_vni_hash(u8 vni[3]) @@ -343,7 +350,7 @@ error: } static struct socket *geneve_create_sock(struct net *net, bool ipv6, - __be16 port) + __be16 port, u32 flags) { struct socket *sock; struct udp_port_cfg udp_conf; @@ -354,6 +361,8 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6, if (ipv6) { udp_conf.family = AF_INET6; udp_conf.ipv6_v6only = 1; + udp_conf.use_udp6_rx_checksums = + !(flags & GENEVE_F_UDP_ZERO_CSUM6_RX); } else { udp_conf.family = AF_INET; udp_conf.local_ip.s_addr = htonl(INADDR_ANY); @@ -480,7 +489,7 @@ static int geneve_gro_complete(struct sk_buff *skb, int nhoff, /* Create new listen socket if needed */ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, - bool ipv6) + bool ipv6, u32 flags) { struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_sock *gs; @@ -492,7 +501,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, if (!gs) return ERR_PTR(-ENOMEM); - sock = geneve_create_sock(net, ipv6, port); + sock = geneve_create_sock(net, ipv6, port, flags); if (IS_ERR(sock)) { kfree(gs); return ERR_CAST(sock); @@ -575,12 +584,13 @@ static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6) goto out; } - gs = geneve_socket_create(net, geneve->dst_port, ipv6); + gs = geneve_socket_create(net, geneve->dst_port, ipv6, geneve->flags); if (IS_ERR(gs)) return PTR_ERR(gs); out: gs->collect_md = geneve->collect_md; + gs->flags = geneve->flags; #if IS_ENABLED(CONFIG_IPV6) if (ipv6) geneve->sock6 = gs; @@ -642,11 +652,12 @@ static void geneve_build_header(struct genevehdr *geneveh, static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb, __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt, - bool csum, bool xnet) + u32 flags, bool xnet) { struct genevehdr *gnvh; int min_headroom; int err; + bool udp_sum = !!(flags & GENEVE_F_UDP_CSUM); skb_scrub_packet(skb, xnet); @@ -658,7 +669,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb, goto free_rt; } - skb = udp_tunnel_handle_offloads(skb, csum); + skb = udp_tunnel_handle_offloads(skb, udp_sum); if (IS_ERR(skb)) { err = PTR_ERR(skb); goto free_rt; @@ -678,11 +689,12 @@ free_rt: #if IS_ENABLED(CONFIG_IPV6) static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb, __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt, - bool csum, bool xnet) + u32 flags, bool xnet) { struct genevehdr *gnvh; int min_headroom; int err; + bool udp_sum = !(flags & GENEVE_F_UDP_ZERO_CSUM6_TX); skb_scrub_packet(skb, xnet); @@ -694,7 +706,7 @@ static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb, goto free_dst; } - skb = udp_tunnel_handle_offloads(skb, csum); + skb = udp_tunnel_handle_offloads(skb, udp_sum); if (IS_ERR(skb)) { err = PTR_ERR(skb); goto free_dst; @@ -824,9 +836,9 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct flowi4 fl4; __u8 tos, ttl; __be16 sport; - bool udp_csum; __be16 df; bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); + u32 flags = geneve->flags; if (geneve->collect_md) { if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) { @@ -857,9 +869,13 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (key->tun_flags & TUNNEL_GENEVE_OPT) opts = ip_tunnel_info_opts(info); - udp_csum = !!(key->tun_flags & TUNNEL_CSUM); + if (key->tun_flags & TUNNEL_CSUM) + flags |= GENEVE_F_UDP_CSUM; + else + flags &= ~GENEVE_F_UDP_CSUM; + err = geneve_build_skb(rt, skb, key->tun_flags, vni, - info->options_len, opts, udp_csum, xnet); + info->options_len, opts, flags, xnet); if (unlikely(err)) goto err; @@ -867,9 +883,8 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, ttl = key->ttl; df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; } else { - udp_csum = false; err = geneve_build_skb(rt, skb, 0, geneve->vni, - 0, NULL, udp_csum, xnet); + 0, NULL, flags, xnet); if (unlikely(err)) goto err; @@ -883,7 +898,7 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr, tos, ttl, df, sport, geneve->dst_port, !net_eq(geneve->net, dev_net(geneve->dev)), - !udp_csum); + !(flags & GENEVE_F_UDP_CSUM)); iptunnel_xmit_stats(err, &dev->stats, dev->tstats); return NETDEV_TX_OK; @@ -912,8 +927,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, struct flowi6 fl6; __u8 prio, ttl; __be16 sport; - bool udp_csum; bool xnet = !net_eq(geneve->net, dev_net(geneve->dev)); + u32 flags = geneve->flags; if (geneve->collect_md) { if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) { @@ -942,19 +957,22 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, if (key->tun_flags & TUNNEL_GENEVE_OPT) opts = ip_tunnel_info_opts(info); - udp_csum = !!(key->tun_flags & TUNNEL_CSUM); + if (key->tun_flags & TUNNEL_CSUM) + flags |= GENEVE_F_UDP_CSUM; + else + flags &= ~GENEVE_F_UDP_CSUM; + err = geneve6_build_skb(dst, skb, key->tun_flags, vni, info->options_len, opts, - udp_csum, xnet); + flags, xnet); if (unlikely(err)) goto err; prio = ip_tunnel_ecn_encap(key->tos, iip, skb); ttl = key->ttl; } else { - udp_csum = false; err = geneve6_build_skb(dst, skb, 0, geneve->vni, - 0, NULL, udp_csum, xnet); + 0, NULL, flags, xnet); if (unlikely(err)) goto err; @@ -966,7 +984,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, } err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev, &fl6.saddr, &fl6.daddr, prio, ttl, - sport, geneve->dst_port, !udp_csum); + sport, geneve->dst_port, + !!(flags & GENEVE_F_UDP_ZERO_CSUM6_TX)); iptunnel_xmit_stats(err, &dev->stats, dev->tstats); return NETDEV_TX_OK; @@ -1099,6 +1118,9 @@ static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = { [IFLA_GENEVE_TOS] = { .type = NLA_U8 }, [IFLA_GENEVE_PORT] = { .type = NLA_U16 }, [IFLA_GENEVE_COLLECT_METADATA] = { .type = NLA_FLAG }, + [IFLA_GENEVE_UDP_CSUM] = { .type = NLA_U8 }, + [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 }, + [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 }, }; static int geneve_validate(struct nlattr *tb[], struct nlattr *data[]) @@ -1152,7 +1174,7 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn, static int geneve_configure(struct net *net, struct net_device *dev, union geneve_addr *remote, __u32 vni, __u8 ttl, __u8 tos, __be16 dst_port, - bool metadata) + bool metadata, u32 flags) { struct geneve_net *gn = net_generic(net, geneve_net_id); struct geneve_dev *t, *geneve = netdev_priv(dev); @@ -1183,6 +1205,7 @@ static int geneve_configure(struct net *net, struct net_device *dev, geneve->tos = tos; geneve->dst_port = dst_port; geneve->collect_md = metadata; + geneve->flags = flags; t = geneve_find_dev(gn, dst_port, remote, geneve->vni, &tun_on_same_port, &tun_collect_md); @@ -1213,6 +1236,7 @@ static int geneve_newlink(struct net *net, struct net_device *dev, bool metadata = false; union geneve_addr remote = geneve_remote_unspec; __u32 vni = 0; + u32 flags = 0; if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) return -EINVAL; @@ -1253,8 +1277,20 @@ static int geneve_newlink(struct net *net, struct net_device *dev, if (data[IFLA_GENEVE_COLLECT_METADATA]) metadata = true; + if (data[IFLA_GENEVE_UDP_CSUM] && + nla_get_u8(data[IFLA_GENEVE_UDP_CSUM])) + flags |= GENEVE_F_UDP_CSUM; + + if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX] && + nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) + flags |= GENEVE_F_UDP_ZERO_CSUM6_TX; + + if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX] && + nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) + flags |= GENEVE_F_UDP_ZERO_CSUM6_RX; + return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port, - metadata); + metadata, flags); } static void geneve_dellink(struct net_device *dev, struct list_head *head) @@ -1273,6 +1309,9 @@ static size_t geneve_get_size(const struct net_device *dev) nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TOS */ nla_total_size(sizeof(__be16)) + /* IFLA_GENEVE_PORT */ nla_total_size(0) + /* IFLA_GENEVE_COLLECT_METADATA */ + nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_CSUM */ + nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_TX */ + nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_RX */ 0; } @@ -1309,6 +1348,14 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev) goto nla_put_failure; } + if (nla_put_u8(skb, IFLA_GENEVE_UDP_CSUM, + !!(geneve->flags & GENEVE_F_UDP_CSUM)) || + nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, + !!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_TX)) || + nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, + !!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_RX))) + goto nla_put_failure; + return 0; nla_put_failure: @@ -1342,7 +1389,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name, return dev; err = geneve_configure(net, dev, &geneve_remote_unspec, - 0, 0, 0, htons(dst_port), true); + 0, 0, 0, htons(dst_port), true, 0); if (err) { free_netdev(dev); return ERR_PTR(err); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 5ad57375a99f..2be1dd5a103f 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -462,6 +462,9 @@ enum { IFLA_GENEVE_PORT, /* destination port */ IFLA_GENEVE_COLLECT_METADATA, IFLA_GENEVE_REMOTE6, + IFLA_GENEVE_UDP_CSUM, + IFLA_GENEVE_UDP_ZERO_CSUM6_TX, + IFLA_GENEVE_UDP_ZERO_CSUM6_RX, __IFLA_GENEVE_MAX }; #define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) -- cgit v1.2.3-59-g8ed1b From cf8190e4c26c8ccf87d80793f8f1f7fe150cdf8c Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Thu, 10 Dec 2015 12:19:35 -0800 Subject: hv_netvsc: Fix race condition on Multi-Send Data field In commit 2a04ae8acb14 ("hv_netvsc: remove locking in netvsc_send()"), the locking for MSD (Multi-Send Data) field was removed. This could cause a race condition between RNDIS control messages and data packets processing, because these two types of traffic are not synchronized. This patch fixes this issue by sending control messages out directly without reading MSD field. Signed-off-by: Haiyang Zhang Reviewed-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 02bab9a7c9ff..059fc5231601 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -867,6 +867,14 @@ int netvsc_send(struct hv_device *device, packet->send_buf_index = NETVSC_INVALID_INDEX; packet->cp_partial = false; + /* Send control message directly without accessing msd (Multi-Send + * Data) field which may be changed during data packet processing. + */ + if (!skb) { + cur_send = packet; + goto send_now; + } + msdp = &net_device->msd[q_idx]; /* batch packets in send buffer if possible */ @@ -939,6 +947,7 @@ int netvsc_send(struct hv_device *device, } } +send_now: if (cur_send) ret = netvsc_send_pkt(cur_send, net_device, pb, skb); -- cgit v1.2.3-59-g8ed1b From cb4396edd84ed73081635fb933d19c1410fafaf4 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sat, 12 Dec 2015 18:24:38 +0100 Subject: drivers/net: fix eisa_driver probe section mismatch Some eisa_driver structures used __init probe functions which generates a warning and could crash if function is called after being deleted. Signed-off-by: Fabian Frederick Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/3c509.c | 2 +- drivers/net/ethernet/3com/3c59x.c | 2 +- drivers/net/ethernet/dec/tulip/de4x5.c | 2 +- drivers/net/ethernet/hp/hp100.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index 4547a1b8b958..7677c745fb30 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -562,7 +562,7 @@ static void el3_common_remove (struct net_device *dev) } #ifdef CONFIG_EISA -static int __init el3_eisa_probe (struct device *device) +static int el3_eisa_probe(struct device *device) { short i; int ioaddr, irq, if_port; diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index 2839af00f20c..1c5f3b273e6a 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -907,7 +907,7 @@ static struct eisa_device_id vortex_eisa_ids[] = { }; MODULE_DEVICE_TABLE(eisa, vortex_eisa_ids); -static int __init vortex_eisa_probe(struct device *device) +static int vortex_eisa_probe(struct device *device) { void __iomem *ioaddr; struct eisa_device *edev; diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c index 8966f3159bb2..3acde3b9b767 100644 --- a/drivers/net/ethernet/dec/tulip/de4x5.c +++ b/drivers/net/ethernet/dec/tulip/de4x5.c @@ -1990,7 +1990,7 @@ SetMulticastFilter(struct net_device *dev) static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; -static int __init de4x5_eisa_probe (struct device *gendev) +static int de4x5_eisa_probe(struct device *gendev) { struct eisa_device *edev; u_long iobase; diff --git a/drivers/net/ethernet/hp/hp100.c b/drivers/net/ethernet/hp/hp100.c index ae6e30d39f0f..1d5c3e16d8f4 100644 --- a/drivers/net/ethernet/hp/hp100.c +++ b/drivers/net/ethernet/hp/hp100.c @@ -2843,7 +2843,7 @@ static void cleanup_dev(struct net_device *d) } #ifdef CONFIG_EISA -static int __init hp100_eisa_probe (struct device *gendev) +static int hp100_eisa_probe(struct device *gendev) { struct net_device *dev = alloc_etherdev(sizeof(struct hp100_private)); struct eisa_device *edev = to_eisa_device(gendev); -- cgit v1.2.3-59-g8ed1b From 7c97b72a623cf7f7cfede9caed531b2679556cca Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Thu, 3 Dec 2015 09:50:55 -0800 Subject: ath10k: allow Mesh Point to install peer security key Mesh Point requires peer security key install when running in secured mode since it's a type of peer links, otherwise peer link will be removed due to key install failure. MFP feature set is required to run Mesh in secured mode and QCA988X firmware, 10.2.4.70.14-2 and above, is the only one supporting secured Mesh at this moment. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 8ae5216fd188..a4c5c1de3806 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -250,7 +250,8 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif, lockdep_assert_held(&ar->conf_mutex); if (WARN_ON(arvif->vif->type != NL80211_IFTYPE_AP && - arvif->vif->type != NL80211_IFTYPE_ADHOC)) + arvif->vif->type != NL80211_IFTYPE_ADHOC && + arvif->vif->type != NL80211_IFTYPE_MESH_POINT)) return -EINVAL; spin_lock_bh(&ar->data_lock); -- cgit v1.2.3-59-g8ed1b From 386164d9b36b1f6f1396978110de85c7e186491d Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 27 Oct 2015 16:59:31 -0700 Subject: e1000e: Switch e1000e_up to void, drop code checking for error result The function e1000e_up always returns 0. As such we can convert it to a void and just ignore the results. This allows us to drop some code in a couple spots as we no longer need to worry about non-zero return values. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/e1000.h | 2 +- drivers/net/ethernet/intel/e1000e/netdev.c | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index 0b748d1959d9..1dc293bad87b 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -480,7 +480,7 @@ extern const char e1000e_driver_version[]; void e1000e_check_options(struct e1000_adapter *adapter); void e1000e_set_ethtool_ops(struct net_device *netdev); -int e1000e_up(struct e1000_adapter *adapter); +void e1000e_up(struct e1000_adapter *adapter); void e1000e_down(struct e1000_adapter *adapter, bool reset); void e1000e_reinit_locked(struct e1000_adapter *adapter); void e1000e_reset(struct e1000_adapter *adapter); diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 775e38910681..955c8c76c680 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -4146,7 +4146,7 @@ void e1000e_reset(struct e1000_adapter *adapter) } -int e1000e_up(struct e1000_adapter *adapter) +void e1000e_up(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; @@ -4166,8 +4166,6 @@ int e1000e_up(struct e1000_adapter *adapter) ew32(ICS, E1000_ICS_LSC | E1000_ICR_OTHER); else ew32(ICS, E1000_ICS_LSC); - - return 0; } static void e1000e_flush_descriptors(struct e1000_adapter *adapter) @@ -6633,7 +6631,7 @@ static int e1000e_pm_runtime_resume(struct device *dev) return rc; if (netdev->flags & IFF_UP) - rc = e1000e_up(adapter); + e1000e_up(adapter); return rc; } @@ -6824,13 +6822,8 @@ static void e1000_io_resume(struct pci_dev *pdev) e1000_init_manageability_pt(adapter); - if (netif_running(netdev)) { - if (e1000e_up(adapter)) { - dev_err(&pdev->dev, - "can't bring device back up after reset\n"); - return; - } - } + if (netif_running(netdev)) + e1000e_up(adapter); netif_device_attach(netdev); -- cgit v1.2.3-59-g8ed1b From 2a3cdead8b408351fa1e3079b220fa331480ffbc Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Tue, 3 Nov 2015 12:37:09 -0600 Subject: igb: Remove GS40G specific defines/functions The I210 internal PHY can be accessed just as well with the access functions shared by 82580, I350, and I354 devices. A side effect of relying on the common functions, is that I210 cable length support is folded back into the common case which effectively reverts the following commit: commit 59f301046b276f87483b3afa3201a4273def06a9 Author: Carolyn Wyborny Date: Wed Oct 10 04:42:59 2012 +0000 igb: Update get cable length function for i210/i211 Cc: Carolyn Wyborny Signed-off-by: Aaron Sierra Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_82575.c | 13 ++--- drivers/net/ethernet/intel/igb/e1000_i210.c | 5 +- drivers/net/ethernet/intel/igb/e1000_i210.h | 2 +- drivers/net/ethernet/intel/igb/e1000_phy.c | 82 +--------------------------- drivers/net/ethernet/intel/igb/e1000_phy.h | 15 +---- 5 files changed, 11 insertions(+), 106 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index 362911d024b5..adb33e2a0137 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -45,8 +45,6 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *); static s32 igb_init_hw_82575(struct e1000_hw *); static s32 igb_phy_hw_reset_sgmii_82575(struct e1000_hw *); static s32 igb_read_phy_reg_sgmii_82575(struct e1000_hw *, u32, u16 *); -static s32 igb_read_phy_reg_82580(struct e1000_hw *, u32, u16 *); -static s32 igb_write_phy_reg_82580(struct e1000_hw *, u32, u16); static s32 igb_reset_hw_82575(struct e1000_hw *); static s32 igb_reset_hw_82580(struct e1000_hw *); static s32 igb_set_d0_lplu_state_82575(struct e1000_hw *, bool); @@ -205,13 +203,10 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw) case e1000_82580: case e1000_i350: case e1000_i354: - phy->ops.read_reg = igb_read_phy_reg_82580; - phy->ops.write_reg = igb_write_phy_reg_82580; - break; case e1000_i210: case e1000_i211: - phy->ops.read_reg = igb_read_phy_reg_gs40g; - phy->ops.write_reg = igb_write_phy_reg_gs40g; + phy->ops.read_reg = igb_read_phy_reg_82580; + phy->ops.write_reg = igb_write_phy_reg_82580; break; default: phy->ops.read_reg = igb_read_phy_reg_igp; @@ -2153,7 +2148,7 @@ void igb_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable) * Reads the MDI control register in the PHY at offset and stores the * information read to data. **/ -static s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data) +s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data) { s32 ret_val; @@ -2177,7 +2172,7 @@ out: * * Writes data to MDI control register in the PHY at offset. **/ -static s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data) +s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data) { s32 ret_val; diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.c b/drivers/net/ethernet/intel/igb/e1000_i210.c index 29f59c76878a..8aa798737d4d 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.c +++ b/drivers/net/ethernet/intel/igb/e1000_i210.c @@ -861,10 +861,10 @@ s32 igb_pll_workaround_i210(struct e1000_hw *hw) if (ret_val) nvm_word = E1000_INVM_DEFAULT_AL; tmp_nvm = nvm_word | E1000_INVM_PLL_WO_VAL; + igb_write_phy_reg_82580(hw, I347AT4_PAGE_SELECT, E1000_PHY_PLL_FREQ_PAGE); for (i = 0; i < E1000_MAX_PLL_TRIES; i++) { /* check current state directly from internal PHY */ - igb_read_phy_reg_gs40g(hw, (E1000_PHY_PLL_FREQ_PAGE | - E1000_PHY_PLL_FREQ_REG), &phy_word); + igb_read_phy_reg_82580(hw, E1000_PHY_PLL_FREQ_REG, &phy_word); if ((phy_word & E1000_PHY_PLL_UNCONF) != E1000_PHY_PLL_UNCONF) { ret_val = 0; @@ -896,6 +896,7 @@ s32 igb_pll_workaround_i210(struct e1000_hw *hw) /* restore WUC register */ wr32(E1000_WUC, wuc); } + igb_write_phy_reg_82580(hw, I347AT4_PAGE_SELECT, 0); /* restore MDICNFG setting */ wr32(E1000_MDICNFG, mdicnfg); return ret_val; diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.h b/drivers/net/ethernet/intel/igb/e1000_i210.h index eaa68a50cb3b..b2964a2a60b1 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.h +++ b/drivers/net/ethernet/intel/igb/e1000_i210.h @@ -85,7 +85,7 @@ enum E1000_INVM_STRUCTURE_TYPE { #define E1000_PCI_PMCSR_D3 0x03 #define E1000_MAX_PLL_TRIES 5 #define E1000_PHY_PLL_UNCONF 0xFF -#define E1000_PHY_PLL_FREQ_PAGE 0xFC0000 +#define E1000_PHY_PLL_FREQ_PAGE 0xFC #define E1000_PHY_PLL_FREQ_REG 0x000E #define E1000_INVM_DEFAULT_AL 0x202F #define E1000_INVM_AUTOLOAD 0x0A diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index c0df40f2b295..c906826b6c02 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -1719,30 +1719,10 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw) u16 phy_data, phy_data2, index, default_page, is_cm; switch (hw->phy.id) { - case I210_I_PHY_ID: - /* Get cable length from PHY Cable Diagnostics Control Reg */ - ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) + - (I347AT4_PCDL + phy->addr), - &phy_data); - if (ret_val) - return ret_val; - - /* Check if the unit of cable length is meters or cm */ - ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) + - I347AT4_PCDC, &phy_data2); - if (ret_val) - return ret_val; - - is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT); - - /* Populate the phy structure with cable length in meters */ - phy->min_cable_length = phy_data / (is_cm ? 100 : 1); - phy->max_cable_length = phy_data / (is_cm ? 100 : 1); - phy->cable_length = phy_data / (is_cm ? 100 : 1); - break; case M88E1543_E_PHY_ID: case M88E1512_E_PHY_ID: case I347AT4_E_PHY_ID: + case I210_I_PHY_ID: /* Remember the original page select and set it to 7 */ ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT, &default_page); @@ -2587,66 +2567,6 @@ out: return ret_val; } -/** - * igb_write_phy_reg_gs40g - Write GS40G PHY register - * @hw: pointer to the HW structure - * @offset: lower half is register offset to write to - * upper half is page to use. - * @data: data to write at register offset - * - * Acquires semaphore, if necessary, then writes the data to PHY register - * at the offset. Release any acquired semaphores before exiting. - **/ -s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data) -{ - s32 ret_val; - u16 page = offset >> GS40G_PAGE_SHIFT; - - offset = offset & GS40G_OFFSET_MASK; - ret_val = hw->phy.ops.acquire(hw); - if (ret_val) - return ret_val; - - ret_val = igb_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page); - if (ret_val) - goto release; - ret_val = igb_write_phy_reg_mdic(hw, offset, data); - -release: - hw->phy.ops.release(hw); - return ret_val; -} - -/** - * igb_read_phy_reg_gs40g - Read GS40G PHY register - * @hw: pointer to the HW structure - * @offset: lower half is register offset to read to - * upper half is page to use. - * @data: data to read at register offset - * - * Acquires semaphore, if necessary, then reads the data in the PHY register - * at the offset. Release any acquired semaphores before exiting. - **/ -s32 igb_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data) -{ - s32 ret_val; - u16 page = offset >> GS40G_PAGE_SHIFT; - - offset = offset & GS40G_OFFSET_MASK; - ret_val = hw->phy.ops.acquire(hw); - if (ret_val) - return ret_val; - - ret_val = igb_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page); - if (ret_val) - goto release; - ret_val = igb_read_phy_reg_mdic(hw, offset, data); - -release: - hw->phy.ops.release(hw); - return ret_val; -} - /** * igb_set_master_slave_mode - Setup PHY for Master/slave mode * @hw: pointer to the HW structure diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h index aa1ae61a61d8..969a6ddafa3b 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.h +++ b/drivers/net/ethernet/intel/igb/e1000_phy.h @@ -72,8 +72,8 @@ s32 igb_copper_link_setup_82580(struct e1000_hw *hw); s32 igb_get_phy_info_82580(struct e1000_hw *hw); s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw); s32 igb_get_cable_length_82580(struct e1000_hw *hw); -s32 igb_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data); -s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data); +s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data); +s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data); s32 igb_check_polarity_m88(struct e1000_hw *hw); /* IGP01E1000 Specific Registers */ @@ -144,17 +144,6 @@ s32 igb_check_polarity_m88(struct e1000_hw *hw); #define E1000_CABLE_LENGTH_UNDEFINED 0xFF -/* GS40G - I210 PHY defines */ -#define GS40G_PAGE_SELECT 0x16 -#define GS40G_PAGE_SHIFT 16 -#define GS40G_OFFSET_MASK 0xFFFF -#define GS40G_PAGE_2 0x20000 -#define GS40G_MAC_REG2 0x15 -#define GS40G_MAC_LB 0x4140 -#define GS40G_MAC_SPEED_1G 0X0006 -#define GS40G_COPPER_SPEC 0x0010 -#define GS40G_LINE_LB 0x4000 - /* SFP modules ID memory locations */ #define E1000_SFF_IDENTIFIER_OFFSET 0x00 #define E1000_SFF_IDENTIFIER_SFF 0x02 -- cgit v1.2.3-59-g8ed1b From 6474de5f105c8fc70695661aa68466ac52e05794 Mon Sep 17 00:00:00 2001 From: Kazuya Mizuguchi Date: Tue, 15 Dec 2015 01:24:58 +0900 Subject: ravb: clear RIC1 in init instead of stop AVB-DMAC Receive FIFO Warning interrupt is not enabled, so it is not necessary to disable the interrupt in ravb_close(). On the other hand, this patch disables the interrupt in ravb_dmac_init() to prevent the possibility that the interrupt is issued by the state that a boot loader left. Signed-off-by: Kazuya Mizuguchi Signed-off-by: Yoshihiro Kaneko Acked-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/ravb_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 1cf12264861c..120cc2565d16 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -410,9 +410,11 @@ static int ravb_dmac_init(struct net_device *ndev) /* Timestamp enable */ ravb_write(ndev, TCCR_TFEN, TCCR); - /* Interrupt enable: */ + /* Interrupt init: */ /* Frame receive */ ravb_write(ndev, RIC0_FRE0 | RIC0_FRE1, RIC0); + /* Disable FIFO full warning */ + ravb_write(ndev, 0, RIC1); /* Receive FIFO full error, descriptor empty */ ravb_write(ndev, RIC2_QFE0 | RIC2_QFE1 | RIC2_RFFE, RIC2); /* Frame transmitted, timestamp FIFO updated */ @@ -1478,7 +1480,6 @@ static int ravb_close(struct net_device *ndev) /* Disable interrupts by clearing the interrupt masks. */ ravb_write(ndev, 0, RIC0); - ravb_write(ndev, 0, RIC1); ravb_write(ndev, 0, RIC2); ravb_write(ndev, 0, TIC); -- cgit v1.2.3-59-g8ed1b From e043046a5a29bebe7e9a68b92c4580e2888f8ed3 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 11 Dec 2015 12:27:53 +0100 Subject: s390-ctcm: Delete unnecessary checks before the function call "channel_remove" The channel_remove() function tests whether its argument is NULL and then returns immediately. Thus the test around the calls is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/ctcm_main.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 05c37d6d4afe..c3e22523faf3 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1677,11 +1677,8 @@ static int ctcm_shutdown_device(struct ccwgroup_device *cgdev) ccw_device_set_offline(cgdev->cdev[1]); ccw_device_set_offline(cgdev->cdev[0]); - - if (priv->channel[CTCM_READ]) - channel_remove(priv->channel[CTCM_READ]); - if (priv->channel[CTCM_WRITE]) - channel_remove(priv->channel[CTCM_WRITE]); + channel_remove(priv->channel[CTCM_READ]); + channel_remove(priv->channel[CTCM_WRITE]); priv->channel[CTCM_READ] = priv->channel[CTCM_WRITE] = NULL; return 0; -- cgit v1.2.3-59-g8ed1b From b475e3161ce74a7e2ee72322585ed5f39985c06a Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Fri, 11 Dec 2015 12:27:54 +0100 Subject: qeth use common function qeth_get_setassparms_cmd There have been 2 identical versions of function qeth_get_setassparms_cmd() for layer 2 and layer 3. Remove the layer 3 function qeth_l3_get_setassparms_cmd() and call the common one named qeth_get_setassparms_cmd() located in qeth_core_main.c Signed-off-by: Thomas Richter Signed-off-by: Ursula Braun Reviewed-by: Eugene Crosser Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 4 ++++ drivers/s390/net/qeth_core_main.c | 9 +++++---- drivers/s390/net/qeth_l3_main.c | 37 ++++++++----------------------------- 3 files changed, 17 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 1766a20ebcb1..ec2e014e885c 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -981,6 +981,10 @@ int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, __u16, int (*reply_cb)(struct qeth_card *, struct qeth_reply *, unsigned long), void *); +struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *, + enum qeth_ipa_funcs, + __u16, __u16, + enum qeth_prot_versions); int qeth_start_ipa_tx_checksum(struct qeth_card *); int qeth_set_rx_csum(struct qeth_card *, int); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 31ac53fa5cee..fe1845acf560 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5297,10 +5297,10 @@ static int qeth_setassparms_cb(struct qeth_card *card, return 0; } -static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card, - enum qeth_ipa_funcs ipa_func, - __u16 cmd_code, __u16 len, - enum qeth_prot_versions prot) +struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, + __u16 cmd_code, __u16 len, + enum qeth_prot_versions prot) { struct qeth_cmd_buffer *iob; struct qeth_ipa_cmd *cmd; @@ -5319,6 +5319,7 @@ static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card, return iob; } +EXPORT_SYMBOL_GPL(qeth_get_setassparms_cmd); int qeth_send_setassparms(struct qeth_card *card, struct qeth_cmd_buffer *iob, __u16 len, long data, diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 543960e96b42..e2eb88c0d41d 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1043,28 +1043,6 @@ static int qeth_l3_default_setassparms_cb(struct qeth_card *card, return 0; } -static struct qeth_cmd_buffer *qeth_l3_get_setassparms_cmd( - struct qeth_card *card, enum qeth_ipa_funcs ipa_func, __u16 cmd_code, - __u16 len, enum qeth_prot_versions prot) -{ - struct qeth_cmd_buffer *iob; - struct qeth_ipa_cmd *cmd; - - QETH_CARD_TEXT(card, 4, "getasscm"); - iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot); - - if (iob) { - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - cmd->data.setassparms.hdr.assist_no = ipa_func; - cmd->data.setassparms.hdr.length = 8 + len; - cmd->data.setassparms.hdr.command_code = cmd_code; - cmd->data.setassparms.hdr.return_code = 0; - cmd->data.setassparms.hdr.seq_no = 0; - } - - return iob; -} - #ifdef CONFIG_QETH_IPV6 static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card, enum qeth_ipa_funcs ipa_func, __u16 cmd_code) @@ -1073,7 +1051,7 @@ static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card, struct qeth_cmd_buffer *iob; QETH_CARD_TEXT(card, 4, "simassp6"); - iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code, + iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, 0, QETH_PROT_IPV6); if (!iob) return -ENOMEM; @@ -2344,10 +2322,11 @@ static int qeth_l3_query_arp_cache_info(struct qeth_card *card, QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot); - iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, - IPA_CMD_ASS_ARP_QUERY_INFO, - sizeof(struct qeth_arp_query_data) - sizeof(char), - prot); + iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, + IPA_CMD_ASS_ARP_QUERY_INFO, + sizeof(struct qeth_arp_query_data) + - sizeof(char), + prot); if (!iob) return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); @@ -2439,7 +2418,7 @@ static int qeth_l3_arp_add_entry(struct qeth_card *card, return -EOPNOTSUPP; } - iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, + iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, IPA_CMD_ASS_ARP_ADD_ENTRY, sizeof(struct qeth_arp_cache_entry), QETH_PROT_IPV4); @@ -2480,7 +2459,7 @@ static int qeth_l3_arp_remove_entry(struct qeth_card *card, return -EOPNOTSUPP; } memcpy(buf, entry, 12); - iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, + iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, IPA_CMD_ASS_ARP_REMOVE_ENTRY, 12, QETH_PROT_IPV4); -- cgit v1.2.3-59-g8ed1b From e5ebe63214d44d4dcf43df02edf3613e04d671b9 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 11 Dec 2015 12:27:55 +0100 Subject: qeth: initialize net_device with carrier off /sys/class/net//operstate for an active qeth network interface offen shows "unknown", which translates to "state UNKNOWN in output of "ip link show". It is caused by a missing initialization of the __LINK_STATE_NOCARRIER bit in the net_device state field. This patch adds a netif_carrier_off() invocation when creating the net_device for a qeth device. Signed-off-by: Ursula Braun Acked-by: Hendrik Brueckner Reference-ID: Bugzilla 133209 Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 1 + drivers/s390/net/qeth_l3_main.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 8f1b091e1732..80b1979e8d95 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1126,6 +1126,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) qeth_l2_request_initial_mac(card); SET_NETDEV_DEV(card->dev, &card->gdev->dev); netif_napi_add(card->dev, &card->napi, qeth_l2_poll, QETH_NAPI_WEIGHT); + netif_carrier_off(card->dev); return register_netdev(card->dev); } diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index e2eb88c0d41d..10d9d3eefd20 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -3199,6 +3199,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) SET_NETDEV_DEV(card->dev, &card->gdev->dev); netif_napi_add(card->dev, &card->napi, qeth_l3_poll, QETH_NAPI_WEIGHT); + netif_carrier_off(card->dev); return register_netdev(card->dev); } -- cgit v1.2.3-59-g8ed1b From 577777359e1f47d0cb7dfc23aac0da867a14af00 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 11 Dec 2015 12:27:56 +0100 Subject: qeth: repair SBAL elements calculation When sending skbs, qeth determines the number of qdio SBAL elements required. If qeth sends a fragmented skb, the SBAL element number calculation is wrong, because the fragmented data part is added twice in qeth_l3_tso_elements(). This patch makes sure fragmented data is handled in qeth_elements_for_frags() only, while qeth_l3_tso_elements() starts calculation of qdio SBAL elements just with the linear data part of the skb. Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 10d9d3eefd20..7c8c68c26540 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2797,7 +2797,7 @@ static inline int qeth_l3_tso_elements(struct sk_buff *skb) { unsigned long tcpd = (unsigned long)tcp_hdr(skb) + tcp_hdr(skb)->doff * 4; - int tcpd_len = skb->len - (tcpd - (unsigned long)skb->data); + int tcpd_len = skb_headlen(skb) - (tcpd - (unsigned long)skb->data); int elements = PFN_UP(tcpd + tcpd_len - 1) - PFN_DOWN(tcpd); elements += qeth_get_elements_for_frags(skb); -- cgit v1.2.3-59-g8ed1b From 476933ce9fedaa723f13c2aabd2aa26dfcdceb15 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 11 Dec 2015 12:27:57 +0100 Subject: qeth: get rid of redundant 0-termination 0-termination is redundant, since sprintf has done that. Signed-off-by: Rasmus Villemoes Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index fe1845acf560..787153764120 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -2684,8 +2684,6 @@ void qeth_print_status_message(struct qeth_card *card) sprintf(card->info.mcl_level, "%02x%02x", card->info.mcl_level[2], card->info.mcl_level[3]); - - card->info.mcl_level[QETH_MCL_LENGTH] = 0; break; } /* fallthrough */ -- cgit v1.2.3-59-g8ed1b From 94dbffe16eb187a7e9c34f1f614925fd9460ec33 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Fri, 11 Dec 2015 19:54:49 +0100 Subject: ppp: define "ppp" device type Let PPP devices be identified as such in /sys/class/net//uevent. Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- drivers/net/ppp/ppp_generic.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 9a863c6a6a33..1cd7651c6659 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -1138,9 +1138,15 @@ static const struct net_device_ops ppp_netdev_ops = { .ndo_get_stats64 = ppp_get_stats64, }; +static struct device_type ppp_type = { + .name = "ppp", +}; + static void ppp_setup(struct net_device *dev) { dev->netdev_ops = &ppp_netdev_ops; + SET_NETDEV_DEVTYPE(dev, &ppp_type); + dev->hard_header_len = PPP_HDRLEN; dev->mtu = PPP_MRU; dev->addr_len = 0; -- cgit v1.2.3-59-g8ed1b From 69d9728d00c7f2acc290d08718c185f231b8fc20 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Fri, 11 Dec 2015 19:54:52 +0100 Subject: ppp: declare ppp devices as enumerated interfaces Let user space be aware of the naming scheme used by ppp interfaces (visible in /sys/class/net//name_assign_type). Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- drivers/net/ppp/ppp_generic.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 1cd7651c6659..fc8ad001bc94 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -2726,8 +2726,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit, int ret = -ENOMEM; int i; - dev = alloc_netdev(sizeof(struct ppp), "", NET_NAME_UNKNOWN, - ppp_setup); + dev = alloc_netdev(sizeof(struct ppp), "", NET_NAME_ENUM, ppp_setup); if (!dev) goto out1; -- cgit v1.2.3-59-g8ed1b From 7e074af2273a6e3115ff7c35529081ab52f8a9e9 Mon Sep 17 00:00:00 2001 From: Saurabh Sengar Date: Sat, 12 Dec 2015 00:58:19 +0530 Subject: wan: wanxl: add pci_disable_device in case of error If there is 'no suitable DMA available' error, device should be disabled before returning Signed-off-by: Saurabh Sengar Signed-off-by: David S. Miller --- drivers/net/wan/wanxl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index e73f13857846..a20d688d2595 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -586,6 +586,7 @@ static int wanxl_pci_init_one(struct pci_dev *pdev, if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(28)) || pci_set_dma_mask(pdev, DMA_BIT_MASK(28))) { pr_err("No usable DMA configuration\n"); + pci_disable_device(pdev); return -EIO; } -- cgit v1.2.3-59-g8ed1b From 06b0dd64923b5598a52de4c889a116c49493bf97 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Tue, 3 Nov 2015 12:37:18 -0600 Subject: igb: Don't add PHY address to PCDL address There is no reason to add the PHY address into the PCDL register address. Signed-off-by: Aaron Sierra Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_phy.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index c906826b6c02..8015f3b8e579 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -1734,8 +1734,7 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw) goto out; /* Get cable length from PHY Cable Diagnostics Control Reg */ - ret_val = phy->ops.read_reg(hw, (I347AT4_PCDL + phy->addr), - &phy_data); + ret_val = phy->ops.read_reg(hw, I347AT4_PCDL, &phy_data); if (ret_val) goto out; -- cgit v1.2.3-59-g8ed1b From 3627f8f1d137f6487e51ff1199a54a087a2a6446 Mon Sep 17 00:00:00 2001 From: Joe Schultz Date: Tue, 3 Nov 2015 12:37:24 -0600 Subject: igb: Improve cable length function for I210, etc. Previously, the PHY-specific code to get the cable length for the I210 internal and related PHYs was reporting the cable length of a single pair and reporting it as the min, max, and total cable length. Update it so that all four pairs are checked so the true min, max, and average cable lengths are reported. Signed-off-by: Joe Schultz Signed-off-by: Aaron Sierra Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_defines.h | 5 ++- drivers/net/ethernet/intel/igb/e1000_hw.h | 1 + drivers/net/ethernet/intel/igb/e1000_phy.c | 54 ++++++++++++++++++++++---- 3 files changed, 51 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index a61ee9462dd4..c3c598c347a9 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -927,7 +927,10 @@ /* Intel i347-AT4 Registers */ -#define I347AT4_PCDL 0x10 /* PHY Cable Diagnostics Length */ +#define I347AT4_PCDL0 0x10 /* Pair 0 PHY Cable Diagnostics Length */ +#define I347AT4_PCDL1 0x11 /* Pair 1 PHY Cable Diagnostics Length */ +#define I347AT4_PCDL2 0x12 /* Pair 2 PHY Cable Diagnostics Length */ +#define I347AT4_PCDL3 0x13 /* Pair 3 PHY Cable Diagnostics Length */ #define I347AT4_PCDC 0x15 /* PHY Cable Diagnostics Control */ #define I347AT4_PAGE_SELECT 0x16 diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h index 2003b3756ba2..4034207eb5cc 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -441,6 +441,7 @@ struct e1000_phy_info { u16 cable_length; u16 max_cable_length; u16 min_cable_length; + u16 pair_length[4]; u8 mdix; diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index 8015f3b8e579..5b54254aed4f 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -1717,6 +1717,9 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw) struct e1000_phy_info *phy = &hw->phy; s32 ret_val; u16 phy_data, phy_data2, index, default_page, is_cm; + int len_tot = 0; + u16 len_min; + u16 len_max; switch (hw->phy.id) { case M88E1543_E_PHY_ID: @@ -1733,11 +1736,6 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw) if (ret_val) goto out; - /* Get cable length from PHY Cable Diagnostics Control Reg */ - ret_val = phy->ops.read_reg(hw, I347AT4_PCDL, &phy_data); - if (ret_val) - goto out; - /* Check if the unit of cable length is meters or cm */ ret_val = phy->ops.read_reg(hw, I347AT4_PCDC, &phy_data2); if (ret_val) @@ -1745,10 +1743,50 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw) is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT); + /* Get cable length from Pair 0 length Regs */ + ret_val = phy->ops.read_reg(hw, I347AT4_PCDL0, &phy_data); + if (ret_val) + goto out; + + phy->pair_length[0] = phy_data / (is_cm ? 100 : 1); + len_tot = phy->pair_length[0]; + len_min = phy->pair_length[0]; + len_max = phy->pair_length[0]; + + /* Get cable length from Pair 1 length Regs */ + ret_val = phy->ops.read_reg(hw, I347AT4_PCDL1, &phy_data); + if (ret_val) + goto out; + + phy->pair_length[1] = phy_data / (is_cm ? 100 : 1); + len_tot += phy->pair_length[1]; + len_min = min(len_min, phy->pair_length[1]); + len_max = max(len_max, phy->pair_length[1]); + + /* Get cable length from Pair 2 length Regs */ + ret_val = phy->ops.read_reg(hw, I347AT4_PCDL2, &phy_data); + if (ret_val) + goto out; + + phy->pair_length[2] = phy_data / (is_cm ? 100 : 1); + len_tot += phy->pair_length[2]; + len_min = min(len_min, phy->pair_length[2]); + len_max = max(len_max, phy->pair_length[2]); + + /* Get cable length from Pair 3 length Regs */ + ret_val = phy->ops.read_reg(hw, I347AT4_PCDL3, &phy_data); + if (ret_val) + goto out; + + phy->pair_length[3] = phy_data / (is_cm ? 100 : 1); + len_tot += phy->pair_length[3]; + len_min = min(len_min, phy->pair_length[3]); + len_max = max(len_max, phy->pair_length[3]); + /* Populate the phy structure with cable length in meters */ - phy->min_cable_length = phy_data / (is_cm ? 100 : 1); - phy->max_cable_length = phy_data / (is_cm ? 100 : 1); - phy->cable_length = phy_data / (is_cm ? 100 : 1); + phy->min_cable_length = len_min; + phy->max_cable_length = len_max; + phy->cable_length = len_tot / 4; /* Reset the page selec to its original value */ ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, -- cgit v1.2.3-59-g8ed1b From d602de05934c1d3022b153ff879e81f65df2a7b6 Mon Sep 17 00:00:00 2001 From: Joe Schultz Date: Tue, 3 Nov 2015 12:37:29 -0600 Subject: igb: Explicitly label self-test result indices Previously, the ethtool self-test gstrings/data arrays were accessed via hardcoded indices, which made the code difficult to follow. This patch replaces the hardcoded values with enum-based labels. Signed-off-by: Joe Schultz Signed-off-by: Aaron Sierra Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_ethtool.c | 38 ++++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 2529bc625de4..1d329f1d047b 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -127,10 +127,20 @@ static const struct igb_stats igb_gstrings_net_stats[] = { #define IGB_STATS_LEN \ (IGB_GLOBAL_STATS_LEN + IGB_NETDEV_STATS_LEN + IGB_QUEUE_STATS_LEN) +enum igb_diagnostics_results { + TEST_REG = 0, + TEST_EEP, + TEST_IRQ, + TEST_LOOP, + TEST_LINK +}; + static const char igb_gstrings_test[][ETH_GSTRING_LEN] = { - "Register test (offline)", "Eeprom test (offline)", - "Interrupt test (offline)", "Loopback test (offline)", - "Link test (on/offline)" + [TEST_REG] = "Register test (offline)", + [TEST_EEP] = "Eeprom test (offline)", + [TEST_IRQ] = "Interrupt test (offline)", + [TEST_LOOP] = "Loopback test (offline)", + [TEST_LINK] = "Link test (on/offline)" }; #define IGB_TEST_LEN (sizeof(igb_gstrings_test) / ETH_GSTRING_LEN) @@ -2002,7 +2012,7 @@ static void igb_diag_test(struct net_device *netdev, /* Link test performed before hardware reset so autoneg doesn't * interfere with test result */ - if (igb_link_test(adapter, &data[4])) + if (igb_link_test(adapter, &data[TEST_LINK])) eth_test->flags |= ETH_TEST_FL_FAILED; if (if_running) @@ -2011,21 +2021,21 @@ static void igb_diag_test(struct net_device *netdev, else igb_reset(adapter); - if (igb_reg_test(adapter, &data[0])) + if (igb_reg_test(adapter, &data[TEST_REG])) eth_test->flags |= ETH_TEST_FL_FAILED; igb_reset(adapter); - if (igb_eeprom_test(adapter, &data[1])) + if (igb_eeprom_test(adapter, &data[TEST_EEP])) eth_test->flags |= ETH_TEST_FL_FAILED; igb_reset(adapter); - if (igb_intr_test(adapter, &data[2])) + if (igb_intr_test(adapter, &data[TEST_IRQ])) eth_test->flags |= ETH_TEST_FL_FAILED; igb_reset(adapter); /* power up link for loopback test */ igb_power_up_link(adapter); - if (igb_loopback_test(adapter, &data[3])) + if (igb_loopback_test(adapter, &data[TEST_LOOP])) eth_test->flags |= ETH_TEST_FL_FAILED; /* restore speed, duplex, autoneg settings */ @@ -2045,16 +2055,16 @@ static void igb_diag_test(struct net_device *netdev, dev_info(&adapter->pdev->dev, "online testing starting\n"); /* PHY is powered down when interface is down */ - if (if_running && igb_link_test(adapter, &data[4])) + if (if_running && igb_link_test(adapter, &data[TEST_LINK])) eth_test->flags |= ETH_TEST_FL_FAILED; else - data[4] = 0; + data[TEST_LINK] = 0; /* Online tests aren't run; pass by default */ - data[0] = 0; - data[1] = 0; - data[2] = 0; - data[3] = 0; + data[TEST_REG] = 0; + data[TEST_EEP] = 0; + data[TEST_IRQ] = 0; + data[TEST_LOOP] = 0; clear_bit(__IGB_TESTING, &adapter->state); } -- cgit v1.2.3-59-g8ed1b From 5833e0526820c3216de8ae60661fb307c89c8710 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Fri, 11 Dec 2015 11:34:53 +0100 Subject: net/macb: add support for resetting PHY using GPIO With device tree it is no more possible to reset the PHY at board level. Furthermore, doing in the driver allow to power down the PHY when the network interface is no more used. This reset can't be done at the PHY driver level. The PHY must be able to answer the to the mii bus scan to let the kernel creating a PHY device. The patch introduces a new optional property "phy-reset-gpios" inspired from the one use for the FEC. Signed-off-by: Gregory CLEMENT Acked-by: Rob Herring Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/macb.txt | 3 +++ drivers/net/ethernet/cadence/macb.c | 8 ++++++++ drivers/net/ethernet/cadence/macb.h | 1 + 3 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt index b5d79761ac97..4a7fb6ccf298 100644 --- a/Documentation/devicetree/bindings/net/macb.txt +++ b/Documentation/devicetree/bindings/net/macb.txt @@ -19,6 +19,9 @@ Required properties: Optional elements: 'tx_clk' - clocks: Phandles to input clocks. +Optional properties: +- phy-reset-gpios : Should specify the gpio for phy reset + Examples: macb0: ethernet@fffc4000 { diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 169059c92f80..01236465c298 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -2904,6 +2904,10 @@ static int macb_probe(struct platform_device *pdev) else macb_get_hwaddr(bp); + /* Power up the PHY if there is a GPIO reset */ + bp->reset_gpio = devm_gpiod_get_optional(&bp->pdev->dev, "phy-reset", + GPIOD_OUT_HIGH); + err = of_get_phy_mode(np); if (err < 0) { pdata = dev_get_platdata(&pdev->dev); @@ -2970,6 +2974,10 @@ static int macb_remove(struct platform_device *pdev) mdiobus_unregister(bp->mii_bus); kfree(bp->mii_bus->irq); mdiobus_free(bp->mii_bus); + + /* Shutdown the PHY if there is a GPIO reset */ + gpiod_set_value(bp->reset_gpio, 0); + unregister_netdev(dev); clk_disable_unprepare(bp->tx_clk); clk_disable_unprepare(bp->hclk); diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index d83b0db77821..5c03e811224d 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -829,6 +829,7 @@ struct macb { unsigned int dma_burst_length; phy_interface_t phy_interface; + struct gpio_desc *reset_gpio; /* AT91RM9200 transmit */ struct sk_buff *skb; /* holds skb until xmit interrupt completes */ -- cgit v1.2.3-59-g8ed1b From 4d432f67ff004dc387ba307d418d0eae4fa9dc13 Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Mon, 9 Nov 2015 15:50:18 -0800 Subject: e1000e: Remove unreachable code msi-x interrupts are not shared so there's no need to check if the interrupt was really from this adapter. Signed-off-by: Benjamin Poirier Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/netdev.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 955c8c76c680..26cf1833b86d 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -1907,12 +1907,6 @@ static irqreturn_t e1000_msix_other(int __always_unused irq, void *data) struct e1000_hw *hw = &adapter->hw; u32 icr = er32(ICR); - if (!(icr & E1000_ICR_INT_ASSERTED)) { - if (!test_bit(__E1000_DOWN, &adapter->state)) - ew32(IMS, E1000_IMS_OTHER); - return IRQ_NONE; - } - if (icr & adapter->eiac_mask) ew32(ICS, (icr & adapter->eiac_mask)); -- cgit v1.2.3-59-g8ed1b From 16ecba59bc333d6282ee057fb02339f77a880beb Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Mon, 9 Nov 2015 15:50:19 -0800 Subject: e1000e: Do not read ICR in Other interrupt Removes the ICR read in the other interrupt handler, uses EIAC to autoclear the Other bit from ICR and IMS. This allows us to avoid interference with Rx and Tx interrupts in the Other interrupt handler. The information read from ICR is not needed. IMS is configured such that the only interrupt cause that can trigger the Other interrupt is Link Status Change. Signed-off-by: Benjamin Poirier Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/netdev.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 26cf1833b86d..56bc422dc831 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -1905,24 +1905,15 @@ static irqreturn_t e1000_msix_other(int __always_unused irq, void *data) struct net_device *netdev = data; struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - u32 icr = er32(ICR); - if (icr & adapter->eiac_mask) - ew32(ICS, (icr & adapter->eiac_mask)); + hw->mac.get_link_status = true; - if (icr & E1000_ICR_OTHER) { - if (!(icr & E1000_ICR_LSC)) - goto no_link_interrupt; - hw->mac.get_link_status = true; - /* guard against interrupt when we're going down */ - if (!test_bit(__E1000_DOWN, &adapter->state)) - mod_timer(&adapter->watchdog_timer, jiffies + 1); + /* guard against interrupt when we're going down */ + if (!test_bit(__E1000_DOWN, &adapter->state)) { + mod_timer(&adapter->watchdog_timer, jiffies + 1); + ew32(IMS, E1000_IMS_OTHER); } -no_link_interrupt: - if (!test_bit(__E1000_DOWN, &adapter->state)) - ew32(IMS, E1000_IMS_LSC | E1000_IMS_OTHER); - return IRQ_HANDLED; } @@ -2021,6 +2012,7 @@ static void e1000_configure_msix(struct e1000_adapter *adapter) hw->hw_addr + E1000_EITR_82574(vector)); else writel(1, hw->hw_addr + E1000_EITR_82574(vector)); + adapter->eiac_mask |= E1000_IMS_OTHER; /* Cause Tx interrupts on every write back */ ivar |= (1 << 31); @@ -2249,7 +2241,7 @@ static void e1000_irq_enable(struct e1000_adapter *adapter) if (adapter->msix_entries) { ew32(EIAC_82574, adapter->eiac_mask & E1000_EIAC_MASK_82574); - ew32(IMS, adapter->eiac_mask | E1000_IMS_OTHER | E1000_IMS_LSC); + ew32(IMS, adapter->eiac_mask | E1000_IMS_LSC); } else if ((hw->mac.type == e1000_pch_lpt) || (hw->mac.type == e1000_pch_spt)) { ew32(IMS, IMS_ENABLE_MASK | E1000_IMS_ECCER); -- cgit v1.2.3-59-g8ed1b From a61cfe4ffad7864a07e0c74969ca7ceb77ab2f1f Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Mon, 9 Nov 2015 15:50:20 -0800 Subject: e1000e: Do not write lsc to ics in msi-x mode In msi-x mode, there is no handler for the lsc interrupt so there is no point in writing that to ics now that we always assume Other interrupts are caused by lsc. Reviewed-by: Jasna Hodzic Signed-off-by: Benjamin Poirier Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/defines.h | 3 ++- drivers/net/ethernet/intel/e1000e/netdev.c | 28 +++++++++++++++++----------- 2 files changed, 19 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h index 133d4074dbe4..f7c7804d79e5 100644 --- a/drivers/net/ethernet/intel/e1000e/defines.h +++ b/drivers/net/ethernet/intel/e1000e/defines.h @@ -441,12 +441,13 @@ #define E1000_IMS_RXQ1 E1000_ICR_RXQ1 /* Rx Queue 1 Interrupt */ #define E1000_IMS_TXQ0 E1000_ICR_TXQ0 /* Tx Queue 0 Interrupt */ #define E1000_IMS_TXQ1 E1000_ICR_TXQ1 /* Tx Queue 1 Interrupt */ -#define E1000_IMS_OTHER E1000_ICR_OTHER /* Other Interrupts */ +#define E1000_IMS_OTHER E1000_ICR_OTHER /* Other Interrupt */ /* Interrupt Cause Set */ #define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ #define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* Rx sequence error */ #define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* Rx desc min. threshold */ +#define E1000_ICS_OTHER E1000_ICR_OTHER /* Other Interrupt */ /* Transmit Descriptor Control */ #define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */ diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 56bc422dc831..d59c0bcb59aa 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -4132,10 +4132,24 @@ void e1000e_reset(struct e1000_adapter *adapter) } -void e1000e_up(struct e1000_adapter *adapter) +/** + * e1000e_trigger_lsc - trigger an LSC interrupt + * @adapter: + * + * Fire a link status change interrupt to start the watchdog. + **/ +static void e1000e_trigger_lsc(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; + if (adapter->msix_entries) + ew32(ICS, E1000_ICS_OTHER); + else + ew32(ICS, E1000_ICS_LSC); +} + +void e1000e_up(struct e1000_adapter *adapter) +{ /* hardware has been reset, we need to reload some things */ e1000_configure(adapter); @@ -4147,11 +4161,7 @@ void e1000e_up(struct e1000_adapter *adapter) netif_start_queue(adapter->netdev); - /* fire a link change interrupt to start the watchdog */ - if (adapter->msix_entries) - ew32(ICS, E1000_ICS_LSC | E1000_ICR_OTHER); - else - ew32(ICS, E1000_ICS_LSC); + e1000e_trigger_lsc(adapter); } static void e1000e_flush_descriptors(struct e1000_adapter *adapter) @@ -4576,11 +4586,7 @@ static int e1000_open(struct net_device *netdev) hw->mac.get_link_status = true; pm_runtime_put(&pdev->dev); - /* fire a link status change interrupt to start the watchdog */ - if (adapter->msix_entries) - ew32(ICS, E1000_ICS_LSC | E1000_ICR_OTHER); - else - ew32(ICS, E1000_ICS_LSC); + e1000e_trigger_lsc(adapter); return 0; -- cgit v1.2.3-59-g8ed1b From 0a8047ac68e50e4ccbadcfc6b6b070805b976885 Mon Sep 17 00:00:00 2001 From: Benjamin Poirier Date: Mon, 9 Nov 2015 15:50:21 -0800 Subject: e1000e: Fix msi-x interrupt automask Since the introduction of 82574 support in e1000e, the driver has worked on the assumption that msi-x interrupt generation is automatically disabled after each irq. As it turns out, this is not the case. Currently, rx interrupts can fire multiple times before and during napi processing. This can be a problem for users because frames that arrive in a certain window (after adapter->clean_rx() but before napi_complete_done() has cleared NAPI_STATE_SCHED) generate an interrupt which does not lead to napi_schedule(). These frames sit in the rx queue until another frame arrives (a tcp retransmit for example). While the EIAC and CTRL_EXT registers are properly configured for irq automask, the modification of IAM in e1000_configure_msix() is what prevents automask from working as intended. This patch removes that erroneous write and fixes interrupt rearming for tx interrupts. It also clears IAME from CTRL_EXT. This is not strictly necessary for operation of the driver but it is to avoid disruption from potential programs that access the registers directly, like `ethregs -c`. Reported-by: Frank Steiner Signed-off-by: Benjamin Poirier Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/netdev.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index d59c0bcb59aa..c71ba1bfc1ec 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -1931,6 +1931,9 @@ static irqreturn_t e1000_intr_msix_tx(int __always_unused irq, void *data) /* Ring was not completely cleaned, so fire another interrupt */ ew32(ICS, tx_ring->ims_val); + if (!test_bit(__E1000_DOWN, &adapter->state)) + ew32(IMS, adapter->tx_ring->ims_val); + return IRQ_HANDLED; } @@ -2020,12 +2023,8 @@ static void e1000_configure_msix(struct e1000_adapter *adapter) ew32(IVAR, ivar); /* enable MSI-X PBA support */ - ctrl_ext = er32(CTRL_EXT); - ctrl_ext |= E1000_CTRL_EXT_PBA_CLR; - - /* Auto-Mask Other interrupts upon ICR read */ - ew32(IAM, ~E1000_EIAC_MASK_82574 | E1000_IMS_OTHER); - ctrl_ext |= E1000_CTRL_EXT_EIAME; + ctrl_ext = er32(CTRL_EXT) & ~E1000_CTRL_EXT_IAME; + ctrl_ext |= E1000_CTRL_EXT_PBA_CLR | E1000_CTRL_EXT_EIAME; ew32(CTRL_EXT, ctrl_ext); e1e_flush(); } -- cgit v1.2.3-59-g8ed1b From bd40e9d6d5386d08ae1e54f28444f3e98c23485c Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:36 +0100 Subject: mlxsw: spectrum: Allocate active VLANs only for port netdevs When adding support for bridges between VLAN interfaces, we'll introduce a new entity called a vPort, which is a represntation of the VLAN interface in the hardware. The main difference between a vPort and a physical port is that several FIDs can be bound to the latter, whereas only one (called a vFID) can be bound to the first. Therefore, it makes sense to use the same struct to represent the two, but to only allocate the 'active_vlans' bitmap in case of a physical port. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 10 ++++++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 322ed544348f..6bacdcf5ac76 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1245,6 +1245,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) struct mlxsw_sp_port *mlxsw_sp_port; struct net_device *dev; bool usable; + size_t bytes; int err; dev = alloc_etherdev(sizeof(struct mlxsw_sp_port)); @@ -1258,6 +1259,12 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) mlxsw_sp_port->learning_sync = 1; mlxsw_sp_port->uc_flood = 1; mlxsw_sp_port->pvid = 1; + bytes = DIV_ROUND_UP(VLAN_N_VID, BITS_PER_BYTE); + mlxsw_sp_port->active_vlans = kzalloc(bytes, GFP_KERNEL); + if (!mlxsw_sp_port->active_vlans) { + err = -ENOMEM; + goto err_port_active_vlans_alloc; + } mlxsw_sp_port->pcpu_stats = netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats); @@ -1359,6 +1366,8 @@ err_port_module_check: err_dev_addr_init: free_percpu(mlxsw_sp_port->pcpu_stats); err_alloc_stats: + kfree(mlxsw_sp_port->active_vlans); +err_port_active_vlans_alloc: free_netdev(dev); return err; } @@ -1381,6 +1390,7 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ mlxsw_sp_port_switchdev_fini(mlxsw_sp_port); free_percpu(mlxsw_sp_port->pcpu_stats); + kfree(mlxsw_sp_port->active_vlans); free_netdev(mlxsw_sp_port->dev); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 48be5a63b9b5..608be6e596a1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -103,7 +103,7 @@ struct mlxsw_sp_port { u16 pvid; u16 lag_id; /* 802.1Q bridge VLANs */ - unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; + unsigned long *active_vlans; /* VLAN interfaces */ unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)]; u16 nr_vfids; -- cgit v1.2.3-59-g8ed1b From 7f71eb46a4858499e6e1be796786a6a1a8d685e5 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:37 +0100 Subject: mlxsw: spectrum: Split vFID range in two Up until now we used a 1:1 mapping - based on VID - to map a VLAN interface to a vFID. However, a different scheme is needed in order to support bridges between VLAN interfaces, as all the member interfaces - which can have different VIDs - need to share the same vFID. Solve that by splitting the vFID range in two: 1. Non-bridged VLAN interfaces 2. Bridged VLAN interfaces When a VLAN interface is created, assign it the next available vFID in the first range, unless one already exists for that VID or number of vFIDs in the range was exceeded. When interface is removed, free the vFID, unless other interfaces are mapped to it. To accomplish the above: 1. Store the VID to vFID mapping in a new struct (mlxsw_sp_vfid), which has a global context and holds a reference count. 2. Create a vPort (dummy in case of bridge SELF invocation) on top of of the physical port and hold a reference to the associated vFID. vfid vfid +-------------+ +-------------+ | vfid | | vfid | | vid +---> ... | vid | | nr_vports | | nr_vports | +------+------+ +------+------+ | +-----------------------+-------+ | | vport vport +-------------+ +-------------+ | ... | | ... | | *vfid +---> ... | *vfid +---> ... | ... | | ... | +------+------+ +------+------+ | | port port +-------------+ +-------------+ | ... | | ... | | vports_list | | vports_list | | ... | | ... | +-------------+ +-------------+ swXpY swXpZ Next patches in the series will add the missing infrastructure for the second range and transfer vPorts between the two ranges according to the received notifications. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 294 ++++++++++++++------- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 64 ++++- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 28 +- 3 files changed, 287 insertions(+), 99 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 6bacdcf5ac76..00ffff91e73b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -186,33 +187,6 @@ static int mlxsw_sp_port_oper_status_get(struct mlxsw_sp_port *mlxsw_sp_port, return 0; } -static int mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, u16 vfid) -{ - char sfmr_pl[MLXSW_REG_SFMR_LEN]; - int err; - - mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID, - MLXSW_SP_VFID_BASE + vfid, 0); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); - - if (err) - return err; - - set_bit(vfid, mlxsw_sp->active_vfids); - return 0; -} - -static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, u16 vfid) -{ - char sfmr_pl[MLXSW_REG_SFMR_LEN]; - - clear_bit(vfid, mlxsw_sp->active_vfids); - - mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID, - MLXSW_SP_VFID_BASE + vfid, 0); - mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); -} - static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port, unsigned char *addr) { @@ -549,12 +523,130 @@ static int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) return 0; } +static struct mlxsw_sp_vfid * +mlxsw_sp_vfid_find(const struct mlxsw_sp *mlxsw_sp, u16 vid) +{ + struct mlxsw_sp_vfid *vfid; + + list_for_each_entry(vfid, &mlxsw_sp->port_vfids.list, list) { + if (vfid->vid == vid) + return vfid; + } + + return NULL; +} + +static u16 mlxsw_sp_avail_vfid_get(const struct mlxsw_sp *mlxsw_sp) +{ + return find_first_zero_bit(mlxsw_sp->port_vfids.mapped, + MLXSW_SP_VFID_PORT_MAX); +} + +static int __mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, u16 vfid) +{ + u16 fid = mlxsw_sp_vfid_to_fid(vfid); + char sfmr_pl[MLXSW_REG_SFMR_LEN]; + + mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID, fid, 0); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); +} + +static void __mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, u16 vfid) +{ + u16 fid = mlxsw_sp_vfid_to_fid(vfid); + char sfmr_pl[MLXSW_REG_SFMR_LEN]; + + mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID, fid, 0); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); +} + +static struct mlxsw_sp_vfid *mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, + u16 vid) +{ + struct device *dev = mlxsw_sp->bus_info->dev; + struct mlxsw_sp_vfid *vfid; + u16 n_vfid; + int err; + + n_vfid = mlxsw_sp_avail_vfid_get(mlxsw_sp); + if (n_vfid == MLXSW_SP_VFID_PORT_MAX) { + dev_err(dev, "No available vFIDs\n"); + return ERR_PTR(-ERANGE); + } + + err = __mlxsw_sp_vfid_create(mlxsw_sp, n_vfid); + if (err) { + dev_err(dev, "Failed to create vFID=%d\n", n_vfid); + return ERR_PTR(err); + } + + vfid = kzalloc(sizeof(*vfid), GFP_KERNEL); + if (!vfid) + goto err_allocate_vfid; + + vfid->vfid = n_vfid; + vfid->vid = vid; + + list_add(&vfid->list, &mlxsw_sp->port_vfids.list); + set_bit(n_vfid, mlxsw_sp->port_vfids.mapped); + + return vfid; + +err_allocate_vfid: + __mlxsw_sp_vfid_destroy(mlxsw_sp, n_vfid); + return ERR_PTR(-ENOMEM); +} + +static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_vfid *vfid) +{ + clear_bit(vfid->vfid, mlxsw_sp->port_vfids.mapped); + list_del(&vfid->list); + + __mlxsw_sp_vfid_destroy(mlxsw_sp, vfid->vfid); + + kfree(vfid); +} + +static struct mlxsw_sp_port * +mlxsw_sp_port_vport_create(struct mlxsw_sp_port *mlxsw_sp_port, + struct mlxsw_sp_vfid *vfid) +{ + struct mlxsw_sp_port *mlxsw_sp_vport; + + mlxsw_sp_vport = kzalloc(sizeof(*mlxsw_sp_vport), GFP_KERNEL); + if (!mlxsw_sp_vport) + return NULL; + + /* dev will be set correctly after the VLAN device is linked + * with the real device. In case of bridge SELF invocation, dev + * will remain as is. + */ + mlxsw_sp_vport->dev = mlxsw_sp_port->dev; + mlxsw_sp_vport->mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + mlxsw_sp_vport->local_port = mlxsw_sp_port->local_port; + mlxsw_sp_vport->stp_state = BR_STATE_FORWARDING; + mlxsw_sp_vport->vport.vfid = vfid; + mlxsw_sp_vport->vport.vid = vfid->vid; + + list_add(&mlxsw_sp_vport->vport.list, &mlxsw_sp_port->vports_list); + + return mlxsw_sp_vport; +} + +static void mlxsw_sp_port_vport_destroy(struct mlxsw_sp_port *mlxsw_sp_vport) +{ + list_del(&mlxsw_sp_vport->vport.list); + kfree(mlxsw_sp_vport); +} + int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, u16 vid) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - char *sftr_pl; + struct mlxsw_sp_port *mlxsw_sp_vport; + struct mlxsw_sp_vfid *vfid; int err; /* VLAN 0 is added to HW filter when device goes up, but it is @@ -563,100 +655,104 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, if (!vid) return 0; - if (test_bit(vid, mlxsw_sp_port->active_vfids)) { + if (mlxsw_sp_port_vport_find(mlxsw_sp_port, vid)) { netdev_warn(dev, "VID=%d already configured\n", vid); return 0; } - if (!test_bit(vid, mlxsw_sp->active_vfids)) { - err = mlxsw_sp_vfid_create(mlxsw_sp, vid); - if (err) { - netdev_err(dev, "Failed to create vFID=%d\n", - MLXSW_SP_VFID_BASE + vid); - return err; + vfid = mlxsw_sp_vfid_find(mlxsw_sp, vid); + if (!vfid) { + vfid = mlxsw_sp_vfid_create(mlxsw_sp, vid); + if (IS_ERR(vfid)) { + netdev_err(dev, "Failed to create vFID for VID=%d\n", + vid); + return PTR_ERR(vfid); } + } - sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); - if (!sftr_pl) { - err = -ENOMEM; - goto err_flood_table_alloc; - } - mlxsw_reg_sftr_pack(sftr_pl, 0, vid, - MLXSW_REG_SFGC_TABLE_TYPE_FID, 0, - MLXSW_PORT_CPU_PORT, true); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); - kfree(sftr_pl); + mlxsw_sp_vport = mlxsw_sp_port_vport_create(mlxsw_sp_port, vfid); + if (!mlxsw_sp_vport) { + netdev_err(dev, "Failed to create vPort for VID=%d\n", vid); + err = -ENOMEM; + goto err_port_vport_create; + } + + if (!vfid->nr_vports) { + err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, + true); if (err) { - netdev_err(dev, "Failed to configure flood table\n"); - goto err_flood_table_config; + netdev_err(dev, "Failed to setup flooding for vFID=%d\n", + vfid->vfid); + goto err_vport_flood_set; } } - /* In case we fail in the following steps, we intentionally do not - * destroy the associated vFID. - */ - /* When adding the first VLAN interface on a bridged port we need to * transition all the active 802.1Q bridge VLANs to use explicit * {Port, VID} to FID mappings and set the port's mode to Virtual mode. */ - if (!mlxsw_sp_port->nr_vfids) { + if (list_is_singular(&mlxsw_sp_port->vports_list)) { err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port); if (err) { netdev_err(dev, "Failed to set to Virtual mode\n"); - return err; + goto err_port_vp_mode_trans; } } - err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport, MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, - true, MLXSW_SP_VFID_BASE + vid, vid); + true, + mlxsw_sp_vfid_to_fid(vfid->vfid), + vid); if (err) { netdev_err(dev, "Failed to map {Port, VID=%d} to vFID=%d\n", - vid, MLXSW_SP_VFID_BASE + vid); + vid, vfid->vfid); goto err_port_vid_to_fid_set; } - err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false); + err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false); if (err) { netdev_err(dev, "Failed to disable learning for VID=%d\n", vid); goto err_port_vid_learning_set; } - err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, false); + err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, true, false); if (err) { netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", vid); goto err_port_add_vid; } - err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid, + err = mlxsw_sp_port_stp_state_set(mlxsw_sp_vport, vid, MLXSW_REG_SPMS_STATE_FORWARDING); if (err) { netdev_err(dev, "Failed to set STP state for VID=%d\n", vid); goto err_port_stp_state_set; } - mlxsw_sp_port->nr_vfids++; - set_bit(vid, mlxsw_sp_port->active_vfids); + vfid->nr_vports++; return 0; -err_flood_table_config: -err_flood_table_alloc: - mlxsw_sp_vfid_destroy(mlxsw_sp, vid); - return err; - err_port_stp_state_set: - mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); + mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, false, false); err_port_add_vid: - mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true); + mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true); err_port_vid_learning_set: - mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, + mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport, MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, false, - MLXSW_SP_VFID_BASE + vid, vid); + mlxsw_sp_vfid_to_fid(vfid->vfid), vid); err_port_vid_to_fid_set: - mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); + if (list_is_singular(&mlxsw_sp_port->vports_list)) + mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); +err_port_vp_mode_trans: + if (!vfid->nr_vports) + mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false); +err_vport_flood_set: + mlxsw_sp_port_vport_destroy(mlxsw_sp_vport); +err_port_vport_create: + if (!vfid->nr_vports) + mlxsw_sp_vfid_destroy(mlxsw_sp, vfid); return err; } @@ -664,6 +760,8 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev, __be16 __always_unused proto, u16 vid) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp_port *mlxsw_sp_vport; + struct mlxsw_sp_vfid *vfid; int err; /* VLAN 0 is removed from HW filter when device goes down, but @@ -672,38 +770,42 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev, if (!vid) return 0; - if (!test_bit(vid, mlxsw_sp_port->active_vfids)) { + mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid); + if (!mlxsw_sp_vport) { netdev_warn(dev, "VID=%d does not exist\n", vid); return 0; } - err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid, + vfid = mlxsw_sp_vport->vport.vfid; + + err = mlxsw_sp_port_stp_state_set(mlxsw_sp_vport, vid, MLXSW_REG_SPMS_STATE_DISCARDING); if (err) { netdev_err(dev, "Failed to set STP state for VID=%d\n", vid); return err; } - err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); + err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, false, false); if (err) { netdev_err(dev, "Failed to set VLAN membership for VID=%d\n", vid); return err; } - err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true); + err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true); if (err) { netdev_err(dev, "Failed to enable learning for VID=%d\n", vid); return err; } - err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport, MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, - false, MLXSW_SP_VFID_BASE + vid, + false, + mlxsw_sp_vfid_to_fid(vfid->vfid), vid); if (err) { netdev_err(dev, "Failed to invalidate {Port, VID=%d} to vFID=%d mapping\n", - vid, MLXSW_SP_VFID_BASE + vid); + vid, vfid->vfid); return err; } @@ -711,7 +813,7 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev, * transition all active 802.1Q bridge VLANs to use VID to FID * mappings and set port's mode to VLAN mode. */ - if (mlxsw_sp_port->nr_vfids == 1) { + if (list_is_singular(&mlxsw_sp_port->vports_list)) { err = mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); if (err) { netdev_err(dev, "Failed to set to VLAN mode\n"); @@ -719,8 +821,12 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev, } } - mlxsw_sp_port->nr_vfids--; - clear_bit(vid, mlxsw_sp_port->active_vfids); + vfid->nr_vports--; + mlxsw_sp_port_vport_destroy(mlxsw_sp_vport); + + /* Destroy the vFID if no vPorts are assigned to it anymore. */ + if (!vfid->nr_vports) + mlxsw_sp_vfid_destroy(mlxsw_sp_port->mlxsw_sp, vfid); return 0; } @@ -1265,6 +1371,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) err = -ENOMEM; goto err_port_active_vlans_alloc; } + INIT_LIST_HEAD(&mlxsw_sp_port->vports_list); mlxsw_sp_port->pcpu_stats = netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats); @@ -1372,12 +1479,21 @@ err_port_active_vlans_alloc: return err; } -static void mlxsw_sp_vfids_fini(struct mlxsw_sp *mlxsw_sp) +static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port) { - u16 vfid; + struct net_device *dev = mlxsw_sp_port->dev; + struct mlxsw_sp_port *mlxsw_sp_vport, *tmp; - for_each_set_bit(vfid, mlxsw_sp->active_vfids, VLAN_N_VID) - mlxsw_sp_vfid_destroy(mlxsw_sp, vfid); + list_for_each_entry_safe(mlxsw_sp_vport, tmp, + &mlxsw_sp_port->vports_list, vport.list) { + u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); + + /* vPorts created for VLAN devices should already be gone + * by now, since we unregistered the port netdev. + */ + WARN_ON(is_vlan_dev(mlxsw_sp_vport->dev)); + mlxsw_sp_port_kill_vid(dev, 0, vid); + } } static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) @@ -1386,8 +1502,8 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) if (!mlxsw_sp_port) return; - mlxsw_sp_port_kill_vid(mlxsw_sp_port->dev, 0, 1); unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ + mlxsw_sp_port_vports_fini(mlxsw_sp_port); mlxsw_sp_port_switchdev_fini(mlxsw_sp_port); free_percpu(mlxsw_sp_port->pcpu_stats); kfree(mlxsw_sp_port->active_vlans); @@ -1746,6 +1862,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, mlxsw_sp->core = mlxsw_core; mlxsw_sp->bus_info = mlxsw_bus_info; + INIT_LIST_HEAD(&mlxsw_sp->port_vfids.list); err = mlxsw_sp_base_mac_get(mlxsw_sp); if (err) { @@ -1756,7 +1873,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, err = mlxsw_sp_ports_create(mlxsw_sp); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n"); - goto err_ports_create; + return err; } err = mlxsw_sp_event_register(mlxsw_sp, MLXSW_TRAP_ID_PUDE); @@ -1806,8 +1923,6 @@ err_rx_listener_register: mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); err_event_register: mlxsw_sp_ports_remove(mlxsw_sp); -err_ports_create: - mlxsw_sp_vfids_fini(mlxsw_sp); return err; } @@ -1819,7 +1934,6 @@ static void mlxsw_sp_fini(void *priv) mlxsw_sp_traps_fini(mlxsw_sp); mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); mlxsw_sp_ports_remove(mlxsw_sp); - mlxsw_sp_vfids_fini(mlxsw_sp); } static struct mlxsw_config_profile mlxsw_sp_config_profile = { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 608be6e596a1..c2aa9acc8bf9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -41,11 +41,16 @@ #include #include #include +#include #include #include "core.h" #define MLXSW_SP_VFID_BASE VLAN_N_VID +#define MLXSW_SP_VFID_PORT_MAX 512 /* Non-bridged VLAN interfaces */ +#define MLXSW_SP_VFID_BR_MAX 8192 /* Bridged VLAN interfaces */ +#define MLXSW_SP_VFID_MAX (MLXSW_SP_VFID_PORT_MAX + MLXSW_SP_VFID_BR_MAX) + #define MLXSW_SP_LAG_MAX 64 #define MLXSW_SP_PORT_PER_LAG_MAX 16 @@ -56,8 +61,23 @@ struct mlxsw_sp_upper { unsigned int ref_count; }; +struct mlxsw_sp_vfid { + struct list_head list; + u16 nr_vports; + u16 vfid; /* Starting at 0 */ + u16 vid; +}; + +static inline u16 mlxsw_sp_vfid_to_fid(u16 vfid) +{ + return MLXSW_SP_VFID_BASE + vfid; +} + struct mlxsw_sp { - unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)]; + struct { + struct list_head list; + unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_VFID_PORT_MAX)]; + } port_vfids; unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)]; struct mlxsw_sp_port **ports; struct mlxsw_core *core; @@ -102,11 +122,15 @@ struct mlxsw_sp_port { lagged:1; u16 pvid; u16 lag_id; + struct { + struct list_head list; + struct mlxsw_sp_vfid *vfid; + u16 vid; + } vport; /* 802.1Q bridge VLANs */ unsigned long *active_vlans; /* VLAN interfaces */ - unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)]; - u16 nr_vfids; + struct list_head vports_list; }; static inline struct mlxsw_sp_port * @@ -121,6 +145,38 @@ mlxsw_sp_port_lagged_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id, u8 port_index) return mlxsw_sp_port && mlxsw_sp_port->lagged ? mlxsw_sp_port : NULL; } +static inline bool +mlxsw_sp_port_is_vport(const struct mlxsw_sp_port *mlxsw_sp_port) +{ + return mlxsw_sp_port->vport.vfid; +} + +static inline u16 +mlxsw_sp_vport_vid_get(const struct mlxsw_sp_port *mlxsw_sp_vport) +{ + return mlxsw_sp_vport->vport.vid; +} + +static inline u16 +mlxsw_sp_vport_vfid_get(const struct mlxsw_sp_port *mlxsw_sp_vport) +{ + return mlxsw_sp_vport->vport.vfid->vfid; +} + +static inline struct mlxsw_sp_port * +mlxsw_sp_port_vport_find(const struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) +{ + struct mlxsw_sp_port *mlxsw_sp_vport; + + list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list, + vport.list) { + if (mlxsw_sp_vport_vid_get(mlxsw_sp_vport) == vid) + return mlxsw_sp_vport; + } + + return NULL; +} + enum mlxsw_sp_flood_table { MLXSW_SP_FLOOD_TABLE_UC, MLXSW_SP_FLOOD_TABLE_BM, @@ -143,5 +199,7 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, u16 vid); int mlxsw_sp_port_kill_vid(struct net_device *dev, __be16 __always_unused proto, u16 vid); +int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid, + bool set); #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 406dab2f6b17..67052ea3f9c1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -129,17 +129,25 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, bool only_uc) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + u16 local_port = mlxsw_sp_port->local_port; + enum mlxsw_flood_table_type table_type; u16 range = fid_end - fid_begin + 1; char *sftr_pl; int err; + if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { + table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID; + local_port = MLXSW_PORT_CPU_PORT; + } else { + table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST; + } + sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); if (!sftr_pl) return -ENOMEM; mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid_begin, - MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range, - mlxsw_sp_port->local_port, set); + table_type, range, local_port, set); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); if (err) goto buffer_out; @@ -151,8 +159,7 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, goto buffer_out; mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid_begin, - MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range, - mlxsw_sp_port->local_port, set); + table_type, range, local_port, set); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); buffer_out: @@ -185,6 +192,15 @@ err_port_flood_set: return err; } +int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid, + bool set) +{ + /* In case of vFIDs, index into the flooding table is relative to + * the start of the vFIDs range. + */ + return __mlxsw_sp_port_flood_set(mlxsw_sp_vport, vfid, vfid, set, true); +} + static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, struct switchdev_trans *trans, unsigned long brport_flags) @@ -304,7 +320,7 @@ static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid) { enum mlxsw_reg_svfa_mt mt; - if (mlxsw_sp_port->nr_vfids) + if (!list_empty(&mlxsw_sp_port->vports_list)) mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; else mt = MLXSW_REG_SVFA_MT_VID_TO_FID; @@ -316,7 +332,7 @@ static int mlxsw_sp_port_fid_unmap(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid) { enum mlxsw_reg_svfa_mt mt; - if (!mlxsw_sp_port->nr_vfids) + if (list_empty(&mlxsw_sp_port->vports_list)) return 0; mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; -- cgit v1.2.3-59-g8ed1b From c06a94ef61b920f2c2374f1195db982c835b651a Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:38 +0100 Subject: mlxsw: spectrum: Use appropriate parameter name The __mlxsw_sp_port_flood_set function is now used to configure flooding for both FIDs and vFIDs, so change the parameter name to 'idx' instead of 'fid'. This is also consistent with hardware documentation. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 67052ea3f9c1..a8c35edfa3e6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -125,13 +125,13 @@ static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, } static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, - u16 fid_begin, u16 fid_end, bool set, + u16 idx_begin, u16 idx_end, bool set, bool only_uc) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; u16 local_port = mlxsw_sp_port->local_port; enum mlxsw_flood_table_type table_type; - u16 range = fid_end - fid_begin + 1; + u16 range = idx_end - idx_begin + 1; char *sftr_pl; int err; @@ -146,7 +146,7 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, if (!sftr_pl) return -ENOMEM; - mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid_begin, + mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, idx_begin, table_type, range, local_port, set); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); if (err) @@ -158,7 +158,7 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, if (only_uc) goto buffer_out; - mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid_begin, + mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, idx_begin, table_type, range, local_port, set); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); -- cgit v1.2.3-59-g8ed1b From 19ae61241485981aa4fae5d494923a27e4d00fba Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:39 +0100 Subject: mlxsw: spectrum: Add another flood table for vFIDs We previously used only one flood table for packets classified to vFIDs. However, since we are going to add support for bridges between VLAN interfaces (mapped to vFIDs) we need to add one more flood table. That way we can separate the flooding domain of unknown unicast traffic from all the rest and support flood control (as we do with the 802.1Q bridge). Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 36 ++++++++-------------- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 2 +- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 5 +-- 3 files changed, 16 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 00ffff91e73b..7b1ce97d76fd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -679,7 +679,7 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, if (!vfid->nr_vports) { err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, - true); + true, false); if (err) { netdev_err(dev, "Failed to setup flooding for vFID=%d\n", vfid->vfid); @@ -747,7 +747,8 @@ err_port_vid_to_fid_set: mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); err_port_vp_mode_trans: if (!vfid->nr_vports) - mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false); + mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false, + false); err_vport_flood_set: mlxsw_sp_port_vport_destroy(mlxsw_sp_vport); err_port_vport_create: @@ -1788,16 +1789,15 @@ static int __mlxsw_sp_flood_init(struct mlxsw_core *mlxsw_core, enum mlxsw_sp_flood_table flood_table; char sfgc_pl[MLXSW_REG_SFGC_LEN]; - if (bridge_type == MLXSW_REG_SFGC_BRIDGE_TYPE_VFID) { + if (bridge_type == MLXSW_REG_SFGC_BRIDGE_TYPE_VFID) table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID; - flood_table = 0; - } else { + else table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST; - if (type == MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST) - flood_table = MLXSW_SP_FLOOD_TABLE_UC; - else - flood_table = MLXSW_SP_FLOOD_TABLE_BM; - } + + if (type == MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST) + flood_table = MLXSW_SP_FLOOD_TABLE_UC; + else + flood_table = MLXSW_SP_FLOOD_TABLE_BM; mlxsw_reg_sfgc_pack(sfgc_pl, type, bridge_type, table_type, flood_table); @@ -1808,9 +1808,6 @@ static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp) { int type, err; - /* For non-offloaded netdevs, flood all traffic types to CPU - * port. - */ for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) { if (type == MLXSW_REG_SFGC_TYPE_RESERVED) continue; @@ -1819,15 +1816,6 @@ static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp) MLXSW_REG_SFGC_BRIDGE_TYPE_VFID); if (err) return err; - } - - /* For bridged ports, use one flooding table for unknown unicast - * traffic and a second table for unregistered multicast and - * broadcast. - */ - for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) { - if (type == MLXSW_REG_SFGC_TYPE_RESERVED) - continue; err = __mlxsw_sp_flood_init(mlxsw_sp->core, type, MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID); @@ -1958,8 +1946,8 @@ static struct mlxsw_config_profile mlxsw_sp_config_profile = { .flood_mode = 3, .max_fid_offset_flood_tables = 2, .fid_offset_flood_table_size = VLAN_N_VID - 1, - .max_fid_flood_tables = 1, - .fid_flood_table_size = VLAN_N_VID, + .max_fid_flood_tables = 2, + .fid_flood_table_size = MLXSW_SP_VFID_MAX, .used_max_ib_mc = 1, .max_ib_mc = 0, .used_max_pkey = 1, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index c2aa9acc8bf9..809c32296aa6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -200,6 +200,6 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto, int mlxsw_sp_port_kill_vid(struct net_device *dev, __be16 __always_unused proto, u16 vid); int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid, - bool set); + bool set, bool only_uc); #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index a8c35edfa3e6..af9c73bfb17d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -193,12 +193,13 @@ err_port_flood_set: } int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid, - bool set) + bool set, bool only_uc) { /* In case of vFIDs, index into the flooding table is relative to * the start of the vFIDs range. */ - return __mlxsw_sp_port_flood_set(mlxsw_sp_vport, vfid, vfid, set, true); + return __mlxsw_sp_port_flood_set(mlxsw_sp_vport, vfid, vfid, set, + only_uc); } static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, -- cgit v1.2.3-59-g8ed1b From 9de6a80e061238d0c1765e0b8d71bd22116edd9d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:40 +0100 Subject: mlxsw: spectrum: Use FID instead of VID when accessing FDB In the Spectrum ASIC - unlike SwitchX-2 - FDB access is done by specifying FID as parameter and not VID. Change the relevant variables and parameters names to reflect that. Note that this was OK up until now, since FID was always equal to VID, but with the introduction of VLAN interfaces this is no longer the case. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 12 +++--- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 50 ++++++++++++---------- 2 files changed, 34 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index af631df4603a..be529e41507f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -396,7 +396,7 @@ static inline void mlxsw_reg_sfd_rec_pack(char *payload, int rec_index, static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index, enum mlxsw_reg_sfd_rec_policy policy, - const char *mac, u16 vid, + const char *mac, u16 fid_vid, enum mlxsw_reg_sfd_rec_action action, u8 local_port) { @@ -404,16 +404,16 @@ static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index, MLXSW_REG_SFD_REC_TYPE_UNICAST, policy, mac, action); mlxsw_reg_sfd_uc_sub_port_set(payload, rec_index, 0); - mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, vid); + mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, fid_vid); mlxsw_reg_sfd_uc_system_port_set(payload, rec_index, local_port); } static inline void mlxsw_reg_sfd_uc_unpack(char *payload, int rec_index, - char *mac, u16 *p_vid, + char *mac, u16 *p_fid_vid, u8 *p_local_port) { mlxsw_reg_sfd_rec_mac_memcpy_from(payload, rec_index, mac); - *p_vid = mlxsw_reg_sfd_uc_fid_vid_get(payload, rec_index); + *p_fid_vid = mlxsw_reg_sfd_uc_fid_vid_get(payload, rec_index); *p_local_port = mlxsw_reg_sfd_uc_system_port_get(payload, rec_index); } @@ -448,7 +448,7 @@ MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_lag_id, MLXSW_REG_SFD_BASE_LEN, 0, 10, static inline void mlxsw_reg_sfd_uc_lag_pack(char *payload, int rec_index, enum mlxsw_reg_sfd_rec_policy policy, - const char *mac, u16 vid, + const char *mac, u16 fid_vid, enum mlxsw_reg_sfd_rec_action action, u16 lag_id) { @@ -456,7 +456,7 @@ mlxsw_reg_sfd_uc_lag_pack(char *payload, int rec_index, MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG, policy, mac, action); mlxsw_reg_sfd_uc_lag_sub_port_set(payload, rec_index, 0); - mlxsw_reg_sfd_uc_lag_fid_vid_set(payload, rec_index, vid); + mlxsw_reg_sfd_uc_lag_fid_vid_set(payload, rec_index, fid_vid); mlxsw_reg_sfd_uc_lag_lag_id_set(payload, rec_index, lag_id); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index af9c73bfb17d..97714e7d95b3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -520,7 +520,7 @@ static enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding) } static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port, - const char *mac, u16 vid, bool adding, + const char *mac, u16 fid, bool adding, bool dynamic) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; @@ -533,7 +533,7 @@ static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); mlxsw_reg_sfd_uc_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic), - mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP, + mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP, mlxsw_sp_port->local_port); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); kfree(sfd_pl); @@ -542,7 +542,7 @@ static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id, - const char *mac, u16 vid, bool adding, + const char *mac, u16 fid, bool adding, bool dynamic) { char *sfd_pl; @@ -554,7 +554,7 @@ static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id, mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic), - mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP, + mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP, lag_id); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); kfree(sfd_pl); @@ -567,21 +567,21 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_fdb *fdb, struct switchdev_trans *trans) { - u16 vid = fdb->vid; + u16 fid = fdb->vid; if (switchdev_trans_ph_prepare(trans)) return 0; - if (!vid) - vid = mlxsw_sp_port->pvid; + if (!fid) + fid = mlxsw_sp_port->pvid; if (!mlxsw_sp_port->lagged) return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, - fdb->addr, vid, true, false); + fdb->addr, fid, true, false); else return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_port->lag_id, - fdb->addr, vid, true, false); + fdb->addr, fid, true, false); } static int mlxsw_sp_port_obj_add(struct net_device *dev, @@ -696,14 +696,16 @@ static int mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_fdb *fdb) { + u16 fid = fdb->vid; + if (!mlxsw_sp_port->lagged) return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, - fdb->addr, fdb->vid, + fdb->addr, fid, false, false); else return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_port->lag_id, - fdb->addr, fdb->vid, + fdb->addr, fid, false, false); } @@ -751,7 +753,7 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; char *sfd_pl; char mac[ETH_ALEN]; - u16 vid; + u16 fid; u8 local_port; u16 lag_id; u8 num_rec; @@ -781,12 +783,12 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, for (i = 0; i < num_rec; i++) { switch (mlxsw_reg_sfd_rec_type_get(sfd_pl, i)) { case MLXSW_REG_SFD_REC_TYPE_UNICAST: - mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &vid, + mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &fid, &local_port); if (local_port == mlxsw_sp_port->local_port) { ether_addr_copy(fdb->addr, mac); fdb->ndm_state = NUD_REACHABLE; - fdb->vid = vid; + fdb->vid = fid; err = cb(&fdb->obj); if (err) stored_err = err; @@ -794,12 +796,12 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, break; case MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG: mlxsw_reg_sfd_uc_lag_unpack(sfd_pl, i, - mac, &vid, &lag_id); + mac, &fid, &lag_id); if (mlxsw_sp_port == mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id)) { ether_addr_copy(fdb->addr, mac); fdb->ndm_state = NUD_REACHABLE; - fdb->vid = vid; + fdb->vid = fid; err = cb(&fdb->obj); if (err) stored_err = err; @@ -888,17 +890,17 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_port *mlxsw_sp_port; char mac[ETH_ALEN]; u8 local_port; - u16 vid; + u16 vid, fid; int err; - mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &vid, &local_port); + mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port); mlxsw_sp_port = mlxsw_sp->ports[local_port]; if (!mlxsw_sp_port) { dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n"); return; } - err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, mac, vid, + err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, mac, fid, adding && mlxsw_sp_port->learning, true); if (err) { if (net_ratelimit()) @@ -906,6 +908,8 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, return; } + vid = fid; + mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning, mlxsw_sp_port->learning_sync, adding, mac, vid, mlxsw_sp_port->dev); @@ -918,17 +922,17 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_port *mlxsw_sp_port; char mac[ETH_ALEN]; u16 lag_id; - u16 vid; + u16 vid, fid; int err; - mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &vid, &lag_id); + mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &fid, &lag_id); mlxsw_sp_port = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id); if (!mlxsw_sp_port) { dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Cannot find port representor for LAG\n"); return; } - err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, vid, + err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, adding && mlxsw_sp_port->learning, true); if (err) { @@ -937,6 +941,8 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, return; } + vid = fid; + mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning, mlxsw_sp_port->learning_sync, adding, mac, vid, -- cgit v1.2.3-59-g8ed1b From 54a732018d8e016e899817eda4af517729e0571c Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:41 +0100 Subject: mlxsw: spectrum: Adjust switchdev ops for VLAN devices switchdev ops can now be called for VLAN devices and we need to be prepared for it. Until now they were only called for the port netdev. Use the newly propagated orig_dev passed as part of the switchdev attr/obj and determine whether the original device is a VLAN device. If so, act accordingly, otherwise continue as usual. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Kconfig | 2 +- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 100 ++++++++++++++++++++- 2 files changed, 98 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index ec8caf8fedc6..ce26adcb4988 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -41,7 +41,7 @@ config MLXSW_SWITCHX2 config MLXSW_SPECTRUM tristate "Mellanox Technologies Spectrum support" - depends on MLXSW_CORE && NET_SWITCHDEV + depends on MLXSW_CORE && NET_SWITCHDEV && VLAN_8021Q default m ---help--- This driver supports Mellanox Technologies Spectrum Ethernet diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 97714e7d95b3..49d531873536 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -51,12 +51,33 @@ #include "core.h" #include "reg.h" +static struct mlxsw_sp_port * +mlxsw_sp_port_orig_get(struct net_device *dev, + struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct mlxsw_sp_port *mlxsw_sp_vport; + u16 vid; + + if (!is_vlan_dev(dev)) + return mlxsw_sp_port; + + vid = vlan_dev_vlan_id(dev); + mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid); + WARN_ON(!mlxsw_sp_vport); + + return mlxsw_sp_vport; +} + static int mlxsw_sp_port_attr_get(struct net_device *dev, struct switchdev_attr *attr) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + mlxsw_sp_port = mlxsw_sp_port_orig_get(attr->orig_dev, mlxsw_sp_port); + if (!mlxsw_sp_port) + return -EINVAL; + switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: attr->u.ppid.id_len = sizeof(mlxsw_sp->base_mac); @@ -105,8 +126,14 @@ static int mlxsw_sp_port_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, if (!spms_pl) return -ENOMEM; mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port); - for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) + + if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { + vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port); mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state); + } else { + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) + mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state); + } err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl); kfree(spms_pl); @@ -174,6 +201,13 @@ static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid, last_visited_vid; int err; + if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { + u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port); + + return __mlxsw_sp_port_flood_set(mlxsw_sp_port, vfid, vfid, + set, true); + } + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) { err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, vid, set, true); @@ -261,6 +295,10 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev, struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); int err = 0; + mlxsw_sp_port = mlxsw_sp_port_orig_get(attr->orig_dev, mlxsw_sp_port); + if (!mlxsw_sp_port) + return -EINVAL; + switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_STP_STATE: err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans, @@ -572,6 +610,12 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, if (switchdev_trans_ph_prepare(trans)) return 0; + if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { + u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port); + + fid = mlxsw_sp_vfid_to_fid(vfid); + } + if (!fid) fid = mlxsw_sp_port->pvid; @@ -591,8 +635,15 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev, struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); int err = 0; + mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port); + if (!mlxsw_sp_port) + return -EINVAL; + switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: + if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) + return 0; + err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, SWITCHDEV_OBJ_PORT_VLAN(obj), trans); @@ -698,6 +749,12 @@ mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port, { u16 fid = fdb->vid; + if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { + u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port); + + fid = mlxsw_sp_vfid_to_fid(vfid); + } + if (!mlxsw_sp_port->lagged) return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, fdb->addr, fid, @@ -715,8 +772,15 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev, struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); int err = 0; + mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port); + if (!mlxsw_sp_port) + return -EINVAL; + switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: + if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) + return 0; + err = mlxsw_sp_port_vlans_del(mlxsw_sp_port, SWITCHDEV_OBJ_PORT_VLAN(obj)); break; @@ -751,6 +815,7 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, switchdev_obj_dump_cb_t *cb) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + u16 vport_vid = 0, vport_fid = 0; char *sfd_pl; char mac[ETH_ALEN]; u16 fid; @@ -765,6 +830,14 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, if (!sfd_pl) return -ENOMEM; + if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { + u16 tmp; + + tmp = mlxsw_sp_vport_vfid_get(mlxsw_sp_port); + vport_fid = mlxsw_sp_vfid_to_fid(tmp); + vport_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port); + } + mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0); do { mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT); @@ -786,9 +859,14 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &fid, &local_port); if (local_port == mlxsw_sp_port->local_port) { + if (vport_fid && vport_fid != fid) + continue; + else if (vport_fid) + fdb->vid = vport_vid; + else + fdb->vid = fid; ether_addr_copy(fdb->addr, mac); fdb->ndm_state = NUD_REACHABLE; - fdb->vid = fid; err = cb(&fdb->obj); if (err) stored_err = err; @@ -799,9 +877,14 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, mac, &fid, &lag_id); if (mlxsw_sp_port == mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id)) { + if (vport_fid && vport_fid != fid) + continue; + else if (vport_fid) + fdb->vid = vport_vid; + else + fdb->vid = fid; ether_addr_copy(fdb->addr, mac); fdb->ndm_state = NUD_REACHABLE; - fdb->vid = fid; err = cb(&fdb->obj); if (err) stored_err = err; @@ -823,6 +906,13 @@ static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid; int err = 0; + if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { + vlan->flags = 0; + vlan->vid_begin = mlxsw_sp_vport_vid_get(mlxsw_sp_port); + vlan->vid_end = mlxsw_sp_vport_vid_get(mlxsw_sp_port); + return cb(&vlan->obj); + } + for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) { vlan->flags = 0; if (vid == mlxsw_sp_port->pvid) @@ -843,6 +933,10 @@ static int mlxsw_sp_port_obj_dump(struct net_device *dev, struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); int err = 0; + mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port); + if (!mlxsw_sp_port) + return -EINVAL; + switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: err = mlxsw_sp_port_vlan_dump(mlxsw_sp_port, -- cgit v1.2.3-59-g8ed1b From aac78a44088728f0712eaea74fbb2493e12080dd Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:42 +0100 Subject: mlxsw: spectrum: Adjust FDB notifications for VLAN devices FDB notifications contain the FID and port (or LAG ID) on which the MAC was learned. In the case of the 802.1Q bridge one can easily derive the matching VID - as FID equals VID - and generate the appropriate notification for the software bridge. With VLAN devices this is no longer the case, as these are associated with a vFID. Solve that by converting the FID to a vFID and lookup the matching VLAN device. From that derive the VID and whether learning (and learning sync) should occur. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 25 ++++++++++++++ .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 40 +++++++++++++++++++--- 2 files changed, 61 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 809c32296aa6..33794f222614 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -73,6 +73,16 @@ static inline u16 mlxsw_sp_vfid_to_fid(u16 vfid) return MLXSW_SP_VFID_BASE + vfid; } +static inline u16 mlxsw_sp_fid_to_vfid(u16 fid) +{ + return fid - MLXSW_SP_VFID_BASE; +} + +static inline bool mlxsw_sp_fid_is_vfid(u16 fid) +{ + return fid >= MLXSW_SP_VFID_BASE; +} + struct mlxsw_sp { struct { struct list_head list; @@ -177,6 +187,21 @@ mlxsw_sp_port_vport_find(const struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) return NULL; } +static inline struct mlxsw_sp_port * +mlxsw_sp_port_vport_find_by_vfid(const struct mlxsw_sp_port *mlxsw_sp_port, + u16 vfid) +{ + struct mlxsw_sp_port *mlxsw_sp_vport; + + list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list, + vport.list) { + if (mlxsw_sp_vport_vfid_get(mlxsw_sp_vport) == vfid) + return mlxsw_sp_vport; + } + + return NULL; +} + enum mlxsw_sp_flood_table { MLXSW_SP_FLOOD_TABLE_UC, MLXSW_SP_FLOOD_TABLE_BM, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 49d531873536..e6e5b5e17847 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -994,6 +994,24 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, return; } + if (mlxsw_sp_fid_is_vfid(fid)) { + u16 vfid = mlxsw_sp_fid_to_vfid(fid); + struct mlxsw_sp_port *mlxsw_sp_vport; + + mlxsw_sp_vport = mlxsw_sp_port_vport_find_by_vfid(mlxsw_sp_port, + vfid); + if (!mlxsw_sp_vport) { + netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n"); + return; + } + + vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); + /* Override the physical port with the vPort. */ + mlxsw_sp_port = mlxsw_sp_vport; + } else { + vid = fid; + } + err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, mac, fid, adding && mlxsw_sp_port->learning, true); if (err) { @@ -1002,8 +1020,6 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, return; } - vid = fid; - mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning, mlxsw_sp_port->learning_sync, adding, mac, vid, mlxsw_sp_port->dev); @@ -1026,6 +1042,24 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, return; } + if (mlxsw_sp_fid_is_vfid(fid)) { + u16 vfid = mlxsw_sp_fid_to_vfid(fid); + struct mlxsw_sp_port *mlxsw_sp_vport; + + mlxsw_sp_vport = mlxsw_sp_port_vport_find_by_vfid(mlxsw_sp_port, + vfid); + if (!mlxsw_sp_vport) { + netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n"); + return; + } + + vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); + /* Override the physical port with the vPort. */ + mlxsw_sp_port = mlxsw_sp_vport; + } else { + vid = fid; + } + err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, adding && mlxsw_sp_port->learning, true); @@ -1035,8 +1069,6 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, return; } - vid = fid; - mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning, mlxsw_sp_port->learning_sync, adding, mac, vid, -- cgit v1.2.3-59-g8ed1b From 9589a7b5d7d9172b7849031377e0bd581ee055c2 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:43 +0100 Subject: mlxsw: spectrum: Handle VLAN devices linking / unlinking When a VLAN interface is configured on top of a physical port we should associate the VLAN device with the matching vPort. Likewise, when it's removed, we should revert back to the underlying port netdev. While not a must, this is consistent with port netdevs and also provides a more accurate error printing via netdev_err() and friends. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 54 ++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 7b1ce97d76fd..159b18d60bae 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2259,6 +2259,40 @@ static int mlxsw_sp_port_lag_changed(struct mlxsw_sp_port *mlxsw_sp_port, return mlxsw_sp_port_lag_tx_en_set(mlxsw_sp_port, info->tx_enabled); } +static int mlxsw_sp_port_vlan_link(struct mlxsw_sp_port *mlxsw_sp_port, + struct net_device *vlan_dev) +{ + struct mlxsw_sp_port *mlxsw_sp_vport; + u16 vid = vlan_dev_vlan_id(vlan_dev); + + mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid); + if (!mlxsw_sp_vport) { + WARN_ON(!mlxsw_sp_vport); + return -EINVAL; + } + + mlxsw_sp_vport->dev = vlan_dev; + + return 0; +} + +static int mlxsw_sp_port_vlan_unlink(struct mlxsw_sp_port *mlxsw_sp_port, + struct net_device *vlan_dev) +{ + struct mlxsw_sp_port *mlxsw_sp_vport; + u16 vid = vlan_dev_vlan_id(vlan_dev); + + mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid); + if (!mlxsw_sp_vport) { + WARN_ON(!mlxsw_sp_vport); + return -EINVAL; + } + + mlxsw_sp_vport->dev = mlxsw_sp_port->dev; + + return 0; +} + static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, unsigned long event, void *ptr) { @@ -2288,9 +2322,23 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, break; case NETDEV_CHANGEUPPER: upper_dev = info->upper_dev; - if (!info->master) - break; - if (netif_is_bridge_master(upper_dev)) { + if (is_vlan_dev(upper_dev)) { + if (info->linking) { + err = mlxsw_sp_port_vlan_link(mlxsw_sp_port, + upper_dev); + if (err) { + netdev_err(dev, "Failed to link VLAN device\n"); + return NOTIFY_BAD; + } + } else { + err = mlxsw_sp_port_vlan_unlink(mlxsw_sp_port, + upper_dev); + if (err) { + netdev_err(dev, "Failed to unlink VLAN device\n"); + return NOTIFY_BAD; + } + } + } else if (netif_is_bridge_master(upper_dev)) { if (info->linking) { err = mlxsw_sp_port_bridge_join(mlxsw_sp_port); if (err) -- cgit v1.2.3-59-g8ed1b From 26f0e7fb15de53da4d3b1ac7d389525cccd6421a Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:44 +0100 Subject: mlxsw: spectrum: Add support for VLAN devices bridging All the member VLAN devices in a bridge need to share the same vFID. To achieve that, expand the vFID struct to include the associated bridge device (or lack of) and allow one to lookup a vFID based on a bridge device. When joining a bridge, lookup the relevant vFID or create one if none exists. Next, make the VLAN device use the vFID. Leaving a bridge can either occur because a user removed the VLAN device from a bridge or because the VLAN device was deleted by the user. In the latter case the bridge's teardown sequence is invoked after the hardware vPort is already gone. Therefore, when unlinking the VLAN device from the real device, check if the associated vPort is bridged and act accordingly. The bridge's notification will be ignored in this case. Note that bridging a VLAN interface with an ordinary port netdev is currently not supported, but not forbidden. This will be addressed in a follow-up patchset. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 358 +++++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 11 + .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 10 +- 3 files changed, 378 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 159b18d60bae..e5f888f4e091 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1851,6 +1851,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, mlxsw_sp->core = mlxsw_core; mlxsw_sp->bus_info = mlxsw_bus_info; INIT_LIST_HEAD(&mlxsw_sp->port_vfids.list); + INIT_LIST_HEAD(&mlxsw_sp->br_vfids.list); err = mlxsw_sp_base_mac_get(mlxsw_sp); if (err) { @@ -2259,6 +2260,9 @@ static int mlxsw_sp_port_lag_changed(struct mlxsw_sp_port *mlxsw_sp_port, return mlxsw_sp_port_lag_tx_en_set(mlxsw_sp_port, info->tx_enabled); } +static int mlxsw_sp_vport_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_vport, + struct net_device *br_dev); + static int mlxsw_sp_port_vlan_link(struct mlxsw_sp_port *mlxsw_sp_port, struct net_device *vlan_dev) { @@ -2288,6 +2292,17 @@ static int mlxsw_sp_port_vlan_unlink(struct mlxsw_sp_port *mlxsw_sp_port, return -EINVAL; } + /* When removing a VLAN device while still bridged we should first + * remove it from the bridge, as we receive the bridge's notification + * when the vPort is already gone. + */ + if (mlxsw_sp_vport->bridged) { + struct net_device *br_dev; + + br_dev = mlxsw_sp_vport_br_get(mlxsw_sp_vport); + mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport, br_dev); + } + mlxsw_sp_vport->dev = mlxsw_sp_port->dev; return 0; @@ -2431,6 +2446,346 @@ static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev, return NOTIFY_DONE; } +static struct mlxsw_sp_vfid * +mlxsw_sp_br_vfid_find(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *br_dev) +{ + struct mlxsw_sp_vfid *vfid; + + list_for_each_entry(vfid, &mlxsw_sp->br_vfids.list, list) { + if (vfid->br_dev == br_dev) + return vfid; + } + + return NULL; +} + +static u16 mlxsw_sp_vfid_to_br_vfid(u16 vfid) +{ + return vfid - MLXSW_SP_VFID_PORT_MAX; +} + +static u16 mlxsw_sp_br_vfid_to_vfid(u16 br_vfid) +{ + return MLXSW_SP_VFID_PORT_MAX + br_vfid; +} + +static u16 mlxsw_sp_avail_br_vfid_get(const struct mlxsw_sp *mlxsw_sp) +{ + return find_first_zero_bit(mlxsw_sp->br_vfids.mapped, + MLXSW_SP_VFID_BR_MAX); +} + +static struct mlxsw_sp_vfid *mlxsw_sp_br_vfid_create(struct mlxsw_sp *mlxsw_sp, + struct net_device *br_dev) +{ + struct device *dev = mlxsw_sp->bus_info->dev; + struct mlxsw_sp_vfid *vfid; + u16 n_vfid; + int err; + + n_vfid = mlxsw_sp_br_vfid_to_vfid(mlxsw_sp_avail_br_vfid_get(mlxsw_sp)); + if (n_vfid == MLXSW_SP_VFID_MAX) { + dev_err(dev, "No available vFIDs\n"); + return ERR_PTR(-ERANGE); + } + + err = __mlxsw_sp_vfid_create(mlxsw_sp, n_vfid); + if (err) { + dev_err(dev, "Failed to create vFID=%d\n", n_vfid); + return ERR_PTR(err); + } + + vfid = kzalloc(sizeof(*vfid), GFP_KERNEL); + if (!vfid) + goto err_allocate_vfid; + + vfid->vfid = n_vfid; + vfid->br_dev = br_dev; + + list_add(&vfid->list, &mlxsw_sp->br_vfids.list); + set_bit(mlxsw_sp_vfid_to_br_vfid(n_vfid), mlxsw_sp->br_vfids.mapped); + + return vfid; + +err_allocate_vfid: + __mlxsw_sp_vfid_destroy(mlxsw_sp, n_vfid); + return ERR_PTR(-ENOMEM); +} + +static void mlxsw_sp_br_vfid_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_vfid *vfid) +{ + u16 br_vfid = mlxsw_sp_vfid_to_br_vfid(vfid->vfid); + + clear_bit(br_vfid, mlxsw_sp->br_vfids.mapped); + list_del(&vfid->list); + + __mlxsw_sp_vfid_destroy(mlxsw_sp, vfid->vfid); + + kfree(vfid); +} + +static int mlxsw_sp_vport_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_vport, + struct net_device *br_dev) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; + u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); + struct net_device *dev = mlxsw_sp_vport->dev; + struct mlxsw_sp_vfid *vfid, *new_vfid; + int err; + + vfid = mlxsw_sp_br_vfid_find(mlxsw_sp, br_dev); + if (!vfid) { + WARN_ON(!vfid); + return -EINVAL; + } + + /* We need a vFID to go back to after leaving the bridge's vFID. */ + new_vfid = mlxsw_sp_vfid_find(mlxsw_sp, vid); + if (!new_vfid) { + new_vfid = mlxsw_sp_vfid_create(mlxsw_sp, vid); + if (IS_ERR(new_vfid)) { + netdev_err(dev, "Failed to create vFID for VID=%d\n", + vid); + return PTR_ERR(new_vfid); + } + } + + /* Invalidate existing {Port, VID} to vFID mapping and create a new + * one for the new vFID. + */ + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, + false, + mlxsw_sp_vfid_to_fid(vfid->vfid), + vid); + if (err) { + netdev_err(dev, "Failed to invalidate {Port, VID} to vFID=%d mapping\n", + vfid->vfid); + goto err_port_vid_to_fid_invalidate; + } + + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, + true, + mlxsw_sp_vfid_to_fid(new_vfid->vfid), + vid); + if (err) { + netdev_err(dev, "Failed to map {Port, VID} to vFID=%d\n", + new_vfid->vfid); + goto err_port_vid_to_fid_validate; + } + + err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false); + if (err) { + netdev_err(dev, "Failed to disable learning\n"); + goto err_port_vid_learning_set; + } + + err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false, + false); + if (err) { + netdev_err(dev, "Failed clear to clear flooding\n"); + goto err_vport_flood_set; + } + + /* Switch between the vFIDs and destroy the old one if needed. */ + new_vfid->nr_vports++; + mlxsw_sp_vport->vport.vfid = new_vfid; + vfid->nr_vports--; + if (!vfid->nr_vports) + mlxsw_sp_br_vfid_destroy(mlxsw_sp, vfid); + + mlxsw_sp_vport->learning = 0; + mlxsw_sp_vport->learning_sync = 0; + mlxsw_sp_vport->uc_flood = 0; + mlxsw_sp_vport->bridged = 0; + + return 0; + +err_vport_flood_set: +err_port_vid_learning_set: +err_port_vid_to_fid_validate: +err_port_vid_to_fid_invalidate: + /* Rollback vFID only if new. */ + if (!new_vfid->nr_vports) + mlxsw_sp_vfid_destroy(mlxsw_sp, new_vfid); + return err; +} + +static int mlxsw_sp_vport_bridge_join(struct mlxsw_sp_port *mlxsw_sp_vport, + struct net_device *br_dev) +{ + struct mlxsw_sp_vfid *old_vfid = mlxsw_sp_vport->vport.vfid; + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; + u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); + struct net_device *dev = mlxsw_sp_vport->dev; + struct mlxsw_sp_vfid *vfid; + int err; + + vfid = mlxsw_sp_br_vfid_find(mlxsw_sp, br_dev); + if (!vfid) { + vfid = mlxsw_sp_br_vfid_create(mlxsw_sp, br_dev); + if (IS_ERR(vfid)) { + netdev_err(dev, "Failed to create bridge vFID\n"); + return PTR_ERR(vfid); + } + } + + err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, true, false); + if (err) { + netdev_err(dev, "Failed to setup flooding for vFID=%d\n", + vfid->vfid); + goto err_port_flood_set; + } + + err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true); + if (err) { + netdev_err(dev, "Failed to enable learning\n"); + goto err_port_vid_learning_set; + } + + /* We need to invalidate existing {Port, VID} to vFID mapping and + * create a new one for the bridge's vFID. + */ + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, + false, + mlxsw_sp_vfid_to_fid(old_vfid->vfid), + vid); + if (err) { + netdev_err(dev, "Failed to invalidate {Port, VID} to vFID=%d mapping\n", + old_vfid->vfid); + goto err_port_vid_to_fid_invalidate; + } + + err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, + true, + mlxsw_sp_vfid_to_fid(vfid->vfid), + vid); + if (err) { + netdev_err(dev, "Failed to map {Port, VID} to vFID=%d\n", + vfid->vfid); + goto err_port_vid_to_fid_validate; + } + + /* Switch between the vFIDs and destroy the old one if needed. */ + vfid->nr_vports++; + mlxsw_sp_vport->vport.vfid = vfid; + old_vfid->nr_vports--; + if (!old_vfid->nr_vports) + mlxsw_sp_vfid_destroy(mlxsw_sp, old_vfid); + + mlxsw_sp_vport->learning = 1; + mlxsw_sp_vport->learning_sync = 1; + mlxsw_sp_vport->uc_flood = 1; + mlxsw_sp_vport->bridged = 1; + + return 0; + +err_port_vid_to_fid_validate: + mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport, + MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, false, + mlxsw_sp_vfid_to_fid(old_vfid->vfid), vid); +err_port_vid_to_fid_invalidate: + mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false); +err_port_vid_learning_set: + mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false, false); +err_port_flood_set: + if (!vfid->nr_vports) + mlxsw_sp_br_vfid_destroy(mlxsw_sp, vfid); + return err; +} + +static bool +mlxsw_sp_port_master_bridge_check(const struct mlxsw_sp_port *mlxsw_sp_port, + const struct net_device *br_dev) +{ + struct mlxsw_sp_port *mlxsw_sp_vport; + + list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list, + vport.list) { + if (mlxsw_sp_vport_br_get(mlxsw_sp_vport) == br_dev) + return false; + } + + return true; +} + +static int mlxsw_sp_netdevice_vport_event(struct net_device *dev, + unsigned long event, void *ptr, + u16 vid) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct netdev_notifier_changeupper_info *info = ptr; + struct mlxsw_sp_port *mlxsw_sp_vport; + struct net_device *upper_dev; + int err; + + mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid); + + switch (event) { + case NETDEV_PRECHANGEUPPER: + upper_dev = info->upper_dev; + if (!info->master || !info->linking) + break; + if (!netif_is_bridge_master(upper_dev)) + return NOTIFY_BAD; + /* We can't have multiple VLAN interfaces configured on + * the same port and being members in the same bridge. + */ + if (!mlxsw_sp_port_master_bridge_check(mlxsw_sp_port, + upper_dev)) + return NOTIFY_BAD; + break; + case NETDEV_CHANGEUPPER: + upper_dev = info->upper_dev; + if (!info->master) + break; + if (info->linking) { + if (!mlxsw_sp_vport) { + WARN_ON(!mlxsw_sp_vport); + return NOTIFY_BAD; + } + err = mlxsw_sp_vport_bridge_join(mlxsw_sp_vport, + upper_dev); + if (err) { + netdev_err(dev, "Failed to join bridge\n"); + return NOTIFY_BAD; + } + } else { + /* We ignore bridge's unlinking notifications if vPort + * is gone, since we already left the bridge when the + * VLAN device was unlinked from the real device. + */ + if (!mlxsw_sp_vport) + return NOTIFY_DONE; + err = mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport, + upper_dev); + if (err) { + netdev_err(dev, "Failed to leave bridge\n"); + return NOTIFY_BAD; + } + } + } + + return NOTIFY_DONE; +} + +static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev, + unsigned long event, void *ptr) +{ + struct net_device *real_dev = vlan_dev_real_dev(vlan_dev); + u16 vid = vlan_dev_vlan_id(vlan_dev); + + if (!mlxsw_sp_port_dev_check(real_dev)) + return NOTIFY_DONE; + + return mlxsw_sp_netdevice_vport_event(real_dev, event, ptr, vid); +} + static int mlxsw_sp_netdevice_event(struct notifier_block *unused, unsigned long event, void *ptr) { @@ -2442,6 +2797,9 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused, if (netif_is_lag_master(dev)) return mlxsw_sp_netdevice_lag_event(dev, event, ptr); + if (is_vlan_dev(dev)) + return mlxsw_sp_netdevice_vlan_event(dev, event, ptr); + return NOTIFY_DONE; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 33794f222614..463ed6dcc709 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -65,6 +65,7 @@ struct mlxsw_sp_vfid { struct list_head list; u16 nr_vports; u16 vfid; /* Starting at 0 */ + struct net_device *br_dev; u16 vid; }; @@ -88,6 +89,10 @@ struct mlxsw_sp { struct list_head list; unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_VFID_PORT_MAX)]; } port_vfids; + struct { + struct list_head list; + unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_VFID_BR_MAX)]; + } br_vfids; unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)]; struct mlxsw_sp_port **ports; struct mlxsw_core *core; @@ -161,6 +166,12 @@ mlxsw_sp_port_is_vport(const struct mlxsw_sp_port *mlxsw_sp_port) return mlxsw_sp_port->vport.vfid; } +static inline struct net_device * +mlxsw_sp_vport_br_get(const struct mlxsw_sp_port *mlxsw_sp_vport) +{ + return mlxsw_sp_vport->vport.vfid->br_dev; +} + static inline u16 mlxsw_sp_vport_vid_get(const struct mlxsw_sp_port *mlxsw_sp_vport) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index e6e5b5e17847..a428dfa3876f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -151,6 +151,11 @@ static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port, return mlxsw_sp_port_stp_state_set(mlxsw_sp_port, state); } +static bool mlxsw_sp_vfid_is_vport_br(u16 vfid) +{ + return vfid >= MLXSW_SP_VFID_PORT_MAX; +} + static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 idx_begin, u16 idx_end, bool set, bool only_uc) @@ -164,7 +169,10 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID; - local_port = MLXSW_PORT_CPU_PORT; + if (mlxsw_sp_vfid_is_vport_br(idx_begin)) + local_port = mlxsw_sp_port->local_port; + else + local_port = MLXSW_PORT_CPU_PORT; } else { table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST; } -- cgit v1.2.3-59-g8ed1b From afd7f979b27b6ae263d7d5c1e81ab02b8830eb74 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:45 +0100 Subject: mlxsw: reg: Add lag_vid field to SFD register Unicast LAG records in the Switch Filtering Database (SFD) register have a lag_vid field indicating the VLAN ID in case of vFIDs. This field is no longer reserved since we are going to add support for VLAN devices on top of LAG. Add the lag_vid field to be used by VLAN devies on top of LAG. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 10 +++++++++- drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index be529e41507f..66d851d4dfb4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -438,6 +438,13 @@ MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_sub_port, MLXSW_REG_SFD_BASE_LEN, 16, 8, MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16, MLXSW_REG_SFD_REC_LEN, 0x08, false); +/* reg_sfd_uc_lag_lag_vid + * Indicates VID in case of vFIDs. Reserved for FIDs. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_lag_vid, MLXSW_REG_SFD_BASE_LEN, 16, 12, + MLXSW_REG_SFD_REC_LEN, 0x0C, false); + /* reg_sfd_uc_lag_lag_id * LAG Identifier - pointer into the LAG descriptor table. * Access: RW @@ -449,7 +456,7 @@ static inline void mlxsw_reg_sfd_uc_lag_pack(char *payload, int rec_index, enum mlxsw_reg_sfd_rec_policy policy, const char *mac, u16 fid_vid, - enum mlxsw_reg_sfd_rec_action action, + enum mlxsw_reg_sfd_rec_action action, u16 lag_vid, u16 lag_id) { mlxsw_reg_sfd_rec_pack(payload, rec_index, @@ -457,6 +464,7 @@ mlxsw_reg_sfd_uc_lag_pack(char *payload, int rec_index, policy, mac, action); mlxsw_reg_sfd_uc_lag_sub_port_set(payload, rec_index, 0); mlxsw_reg_sfd_uc_lag_fid_vid_set(payload, rec_index, fid_vid); + mlxsw_reg_sfd_uc_lag_lag_vid_set(payload, rec_index, lag_vid); mlxsw_reg_sfd_uc_lag_lag_id_set(payload, rec_index, lag_id); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index a428dfa3876f..3eed8d7d2397 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -600,7 +600,7 @@ static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id, mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic), - mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP, + mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP, 0, lag_id); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); kfree(sfd_pl); -- cgit v1.2.3-59-g8ed1b From 64771e31ce5a682c597bccae41f7641252b91fbe Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:46 +0100 Subject: mlxsw: spectrum: Enable FDB records for VLAN devices on top of LAG When adding or removing FDB records of VLAN devices on top of LAG we should set the lag_vid parameter to the VLAN ID of the VLAN device. It is reserved otherwise. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 3eed8d7d2397..9476ff9237ae 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -588,8 +588,8 @@ static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id, - const char *mac, u16 fid, bool adding, - bool dynamic) + const char *mac, u16 fid, u16 lag_vid, + bool adding, bool dynamic) { char *sfd_pl; int err; @@ -600,8 +600,8 @@ static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id, mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic), - mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP, 0, - lag_id); + mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP, + lag_vid, lag_id); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); kfree(sfd_pl); @@ -614,6 +614,7 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, struct switchdev_trans *trans) { u16 fid = fdb->vid; + u16 lag_vid = 0; if (switchdev_trans_ph_prepare(trans)) return 0; @@ -622,6 +623,7 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port); fid = mlxsw_sp_vfid_to_fid(vfid); + lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port); } if (!fid) @@ -633,7 +635,8 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, else return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_port->lag_id, - fdb->addr, fid, true, false); + fdb->addr, fid, lag_vid, + true, false); } static int mlxsw_sp_port_obj_add(struct net_device *dev, @@ -756,11 +759,13 @@ mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_fdb *fdb) { u16 fid = fdb->vid; + u16 lag_vid = 0; if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port); fid = mlxsw_sp_vfid_to_fid(vfid); + lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port); } if (!mlxsw_sp_port->lagged) @@ -770,7 +775,7 @@ mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port, else return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_port->lag_id, - fdb->addr, fid, + fdb->addr, fid, lag_vid, false, false); } @@ -1039,6 +1044,7 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_port *mlxsw_sp_port; char mac[ETH_ALEN]; + u16 lag_vid = 0; u16 lag_id; u16 vid, fid; int err; @@ -1062,13 +1068,14 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, } vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); + lag_vid = vid; /* Override the physical port with the vPort. */ mlxsw_sp_port = mlxsw_sp_vport; } else { vid = fid; } - err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, + err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid, adding && mlxsw_sp_port->learning, true); if (err) { -- cgit v1.2.3-59-g8ed1b From 272c447017df1bbd68e46efad8178c3e449b03ed Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Dec 2015 16:03:47 +0100 Subject: mlxsw: spectrum: Add support for VLAN devices on top of LAG When creating a VLAN device on top of LAG, we are basically creating a vPort on top of each of the port netdevs member in the LAG. Therefore, these vPorts should inherit both the LAG status and LAG ID from the underlying port netdevs. In addition, when the VLAN device joins or leaves a bridge each of the underlying vPorts should know about it and act accordingly. This is achieved by propagating the VLAN event down to the lower devices. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 32 +++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index e5f888f4e091..c588c65e91f5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -626,6 +626,8 @@ mlxsw_sp_port_vport_create(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_vport->mlxsw_sp = mlxsw_sp_port->mlxsw_sp; mlxsw_sp_vport->local_port = mlxsw_sp_port->local_port; mlxsw_sp_vport->stp_state = BR_STATE_FORWARDING; + mlxsw_sp_vport->lagged = mlxsw_sp_port->lagged; + mlxsw_sp_vport->lag_id = mlxsw_sp_port->lag_id; mlxsw_sp_vport->vport.vfid = vfid; mlxsw_sp_vport->vport.vid = vfid->vid; @@ -2774,16 +2776,40 @@ static int mlxsw_sp_netdevice_vport_event(struct net_device *dev, return NOTIFY_DONE; } +static int mlxsw_sp_netdevice_lag_vport_event(struct net_device *lag_dev, + unsigned long event, void *ptr, + u16 vid) +{ + struct net_device *dev; + struct list_head *iter; + int ret; + + netdev_for_each_lower_dev(lag_dev, dev, iter) { + if (mlxsw_sp_port_dev_check(dev)) { + ret = mlxsw_sp_netdevice_vport_event(dev, event, ptr, + vid); + if (ret == NOTIFY_BAD) + return ret; + } + } + + return NOTIFY_DONE; +} + static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev, unsigned long event, void *ptr) { struct net_device *real_dev = vlan_dev_real_dev(vlan_dev); u16 vid = vlan_dev_vlan_id(vlan_dev); - if (!mlxsw_sp_port_dev_check(real_dev)) - return NOTIFY_DONE; + if (mlxsw_sp_port_dev_check(real_dev)) + return mlxsw_sp_netdevice_vport_event(real_dev, event, ptr, + vid); + else if (netif_is_lag_master(real_dev)) + return mlxsw_sp_netdevice_lag_vport_event(real_dev, event, ptr, + vid); - return mlxsw_sp_netdevice_vport_event(real_dev, event, ptr, vid); + return NOTIFY_DONE; } static int mlxsw_sp_netdevice_event(struct notifier_block *unused, -- cgit v1.2.3-59-g8ed1b From b4bc88a868edb878827499f0fb049b9f83fc2710 Mon Sep 17 00:00:00 2001 From: Kazuya Mizuguchi Date: Tue, 15 Dec 2015 19:44:13 +0900 Subject: ravb: Add fixed-link support This patch adds support of the fixed PHY. This patch is based on commit 87009814cdbb ("ucc_geth: use the new fixed PHY helpers"). Signed-off-by: Kazuya Mizuguchi Signed-off-by: Yoshihiro Kaneko Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/ravb_main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 120cc2565d16..3448eb0f8a4a 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -882,6 +882,7 @@ static int ravb_phy_init(struct net_device *ndev) struct ravb_private *priv = netdev_priv(ndev); struct phy_device *phydev; struct device_node *pn; + int err; priv->link = 0; priv->speed = 0; @@ -889,6 +890,17 @@ static int ravb_phy_init(struct net_device *ndev) /* Try connecting to PHY */ pn = of_parse_phandle(np, "phy-handle", 0); + if (!pn) { + /* In the case of a fixed PHY, the DT node associated + * to the PHY is the Ethernet MAC DT node. + */ + if (of_phy_is_fixed_link(np)) { + err = of_phy_register_fixed_link(np); + if (err) + return err; + } + pn = of_node_get(np); + } phydev = of_phy_connect(ndev, pn, ravb_adjust_link, 0, priv->phy_interface); if (!phydev) { -- cgit v1.2.3-59-g8ed1b From 53692b1de419c1b59106909c7f6b4dd3dbc768ac Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Mon, 14 Dec 2015 11:19:41 -0800 Subject: sctp: Rename NETIF_F_SCTP_CSUM to NETIF_F_SCTP_CRC The SCTP checksum is really a CRC and is very different from the standards 1's complement checksum that serves as the checksum for IP protocols. This offload interface is also very different. Rename NETIF_F_SCTP_CSUM to NETIF_F_SCTP_CRC to highlight these differences. The term CSUM should be reserved in the stack to refer to the standard 1's complement IP checksum. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- drivers/net/ethernet/intel/igb/igb_main.c | 4 ++-- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 ++-- drivers/net/loopback.c | 2 +- include/linux/netdev_features.h | 4 ++-- net/8021q/vlan_dev.c | 2 +- net/core/ethtool.c | 4 ++-- net/netfilter/ipvs/ip_vs_proto_sctp.c | 2 +- net/sctp/output.c | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index b118deb08ce6..a63d980f478e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8842,7 +8842,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) netdev->features = NETIF_F_SG | NETIF_F_IP_CSUM | - NETIF_F_SCTP_CSUM | + NETIF_F_SCTP_CRC | NETIF_F_HIGHDMA | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 455394cf7f80..4d05ff6f0423 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2321,7 +2321,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter) netdev->features |= NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_IP_CSUM | - NETIF_F_SCTP_CSUM | + NETIF_F_SCTP_CRC | NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6 | diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 7afde455326d..31e5f3942839 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -2379,8 +2379,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } if (hw->mac.type >= e1000_82576) { - netdev->hw_features |= NETIF_F_SCTP_CSUM; - netdev->features |= NETIF_F_SCTP_CSUM; + netdev->hw_features |= NETIF_F_SCTP_CRC; + netdev->features |= NETIF_F_SCTP_CRC; } netdev->priv_flags |= IFF_UNICAST_FLT; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 66c64a376719..9f27001cac1f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -8995,8 +8995,8 @@ skip_sriov: case ixgbe_mac_X540: case ixgbe_mac_X550: case ixgbe_mac_X550EM_x: - netdev->features |= NETIF_F_SCTP_CSUM; - netdev->hw_features |= NETIF_F_SCTP_CSUM | + netdev->features |= NETIF_F_SCTP_CRC; + netdev->hw_features |= NETIF_F_SCTP_CRC | NETIF_F_NTUPLE; break; default: diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index dc7d970bd1c0..a400288cb37b 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -175,7 +175,7 @@ static void loopback_setup(struct net_device *dev) | NETIF_F_UFO | NETIF_F_HW_CSUM | NETIF_F_RXCSUM - | NETIF_F_SCTP_CSUM + | NETIF_F_SCTP_CRC | NETIF_F_HIGHDMA | NETIF_F_LLTX | NETIF_F_NETNS_LOCAL diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index f0d87347df19..6395f8309393 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -52,7 +52,7 @@ enum { NETIF_F_GSO_TUNNEL_REMCSUM_BIT, NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */ - NETIF_F_SCTP_CSUM_BIT, /* SCTP checksum offload */ + NETIF_F_SCTP_CRC_BIT, /* SCTP checksum offload */ NETIF_F_FCOE_MTU_BIT, /* Supports max FCoE MTU, 2158 bytes*/ NETIF_F_NTUPLE_BIT, /* N-tuple filters supported */ NETIF_F_RXHASH_BIT, /* Receive hashing offload */ @@ -103,7 +103,7 @@ enum { #define NETIF_F_NTUPLE __NETIF_F(NTUPLE) #define NETIF_F_RXCSUM __NETIF_F(RXCSUM) #define NETIF_F_RXHASH __NETIF_F(RXHASH) -#define NETIF_F_SCTP_CSUM __NETIF_F(SCTP_CSUM) +#define NETIF_F_SCTP_CRC __NETIF_F(SCTP_CRC) #define NETIF_F_SG __NETIF_F(SG) #define NETIF_F_TSO6 __NETIF_F(TSO6) #define NETIF_F_TSO_ECN __NETIF_F(TSO_ECN) diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 9f4bd137e045..45b74e875381 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -545,7 +545,7 @@ static int vlan_dev_init(struct net_device *dev) dev->hw_features = NETIF_F_ALL_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | - NETIF_F_HIGHDMA | NETIF_F_SCTP_CSUM | + NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC | NETIF_F_ALL_FCOE; dev->features |= real_dev->vlan_features | NETIF_F_LLTX | diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 29edf74846fc..4a0cab85d67d 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -87,7 +87,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation", [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", - [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp", + [NETIF_F_SCTP_CRC_BIT] = "tx-checksum-sctp", [NETIF_F_FCOE_MTU_BIT] = "fcoe-mtu", [NETIF_F_NTUPLE_BIT] = "rx-ntuple-filter", [NETIF_F_RXHASH_BIT] = "rx-hashing", @@ -235,7 +235,7 @@ static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd) switch (eth_cmd) { case ETHTOOL_GTXCSUM: case ETHTOOL_STXCSUM: - return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM; + return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CRC; case ETHTOOL_GRXCSUM: case ETHTOOL_SRXCSUM: return NETIF_F_RXCSUM; diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 010ddeec135f..d952d67f904d 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, /* Only update csum if we really have to */ if (sctph->dest != cp->dport || payload_csum || (skb->ip_summed == CHECKSUM_PARTIAL && - !(skb_dst(skb)->dev->features & NETIF_F_SCTP_CSUM))) { + !(skb_dst(skb)->dev->features & NETIF_F_SCTP_CRC))) { sctph->dest = cp->dport; sctp_nat_csum(skb, sctph, sctphoff); } else if (skb->ip_summed != CHECKSUM_PARTIAL) { diff --git a/net/sctp/output.c b/net/sctp/output.c index abe7c2db2412..9d610eddd19e 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -534,7 +534,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) * by CRC32-C as described in . */ if (!sctp_checksum_disable) { - if (!(dst->dev->features & NETIF_F_SCTP_CSUM) || + if (!(dst->dev->features & NETIF_F_SCTP_CRC) || (dst_xfrm(dst) != NULL) || packet->ipfragok) { sh->checksum = sctp_compute_cksum(nskb, 0); } else { -- cgit v1.2.3-59-g8ed1b From 253aab0597d9e16014888639f801ff89df6949c3 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Mon, 14 Dec 2015 11:19:42 -0800 Subject: fcoe: Use CHECKSUM_PARTIAL to indicate CRC offload When setting up CRC offload set ip_summed to CHECKSUM_PARTIAL instead of CHECKSUM_UNNECESSARY. This is consistent with the definition of CHECKSUM_PARTIAL. The only driver that seems to be advertising NETIF_F_FCOE_CRC is ixgbe. AFICT the driver does not look at ip_summed for FCOE and just assumes that CRC is being offloaded. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- drivers/scsi/fcoe/fcoe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index f4424063b860..0efe7112fc1f 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1625,7 +1625,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) /* crc offload */ if (likely(lport->crc_offload)) { - skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = skb_headroom(skb); skb->csum_offset = skb->len; crc = 0; -- cgit v1.2.3-59-g8ed1b From a188222b6ed29404ac2d4232d35d1fe0e77af370 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Mon, 14 Dec 2015 11:19:43 -0800 Subject: net: Rename NETIF_F_ALL_CSUM to NETIF_F_CSUM_MASK The name NETIF_F_ALL_CSUM is a misnomer. This does not correspond to the set of features for offloading all checksums. This is a mask of the checksum offload related features bits. It is incorrect to set both NETIF_F_HW_CSUM and NETIF_F_IP_CSUM or NETIF_F_IPV6 at the same time for features of a device. This patch: - Changes instances of NETIF_F_ALL_CSUM to NETIF_F_CSUM_MASK (where NETIF_F_ALL_CSUM is being used as a mask). - Changes bonding, sfc/efx, ipvlan, macvlan, vlan, and team drivers to use NEITF_F_HW_CSUM in features list instead of NETIF_F_ALL_CSUM. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 7 +++---- drivers/net/ethernet/emulex/benet/be_main.c | 2 +- drivers/net/ethernet/ibm/ibmveth.c | 5 +++-- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/jme.c | 2 +- drivers/net/ethernet/marvell/sky2.c | 2 +- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 4 ++-- drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c | 2 +- drivers/net/ethernet/sfc/efx.c | 2 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++-- drivers/net/ipvlan/ipvlan_main.c | 2 +- drivers/net/macvlan.c | 2 +- drivers/net/macvtap.c | 2 +- drivers/net/team/team.c | 3 +-- drivers/net/usb/r8152.c | 2 +- drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c | 2 +- include/linux/netdev_features.h | 7 ++++++- include/linux/netdevice.h | 6 +++--- include/net/vxlan.h | 2 +- net/8021q/vlan_dev.c | 2 +- net/core/dev.c | 10 +++++----- net/core/ethtool.c | 2 +- net/ipv4/tcp.c | 4 ++-- 25 files changed, 43 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index fe0e7a6f4d72..cab99fd44c8e 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1067,12 +1067,12 @@ static netdev_features_t bond_fix_features(struct net_device *dev, return features; } -#define BOND_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ +#define BOND_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \ NETIF_F_HIGHDMA | NETIF_F_LRO) -#define BOND_ENC_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | NETIF_F_RXCSUM |\ - NETIF_F_ALL_TSO) +#define BOND_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ + NETIF_F_RXCSUM | NETIF_F_ALL_TSO) static void bond_compute_features(struct bonding *bond) { @@ -4182,7 +4182,6 @@ void bond_setup(struct net_device *bond_dev) NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; - bond_dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM); bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL; bond_dev->features |= bond_dev->hw_features; } diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 4cab8879f5ae..34e324f20d80 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -5289,7 +5289,7 @@ static netdev_features_t be_features_check(struct sk_buff *skb, skb->inner_protocol != htons(ETH_P_TEB) || skb_inner_mac_header(skb) - skb_transport_header(skb) != sizeof(struct udphdr) + sizeof(struct vxlanhdr)) - return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); + return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); return features; } diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 7af870a3c549..6691b5a45b9d 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -763,7 +763,7 @@ static netdev_features_t ibmveth_fix_features(struct net_device *dev, */ if (!(features & NETIF_F_RXCSUM)) - features &= ~NETIF_F_ALL_CSUM; + features &= ~NETIF_F_CSUM_MASK; return features; } @@ -928,7 +928,8 @@ static int ibmveth_set_features(struct net_device *dev, rc1 = ibmveth_set_csum_offload(dev, rx_csum); if (rc1 && !adapter->rx_csum) dev->features = - features & ~(NETIF_F_ALL_CSUM | NETIF_F_RXCSUM); + features & ~(NETIF_F_CSUM_MASK | + NETIF_F_RXCSUM); } if (large_send != adapter->large_send) { diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index d9854d39576d..83ddf362ea77 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1357,7 +1357,7 @@ static netdev_features_t fm10k_features_check(struct sk_buff *skb, if (!skb->encapsulation || fm10k_tx_encap_offload(skb)) return features; - return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); + return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); } static const struct net_device_ops fm10k_netdev_ops = { diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index a63d980f478e..c284e4341c7c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8766,7 +8766,7 @@ static netdev_features_t i40e_features_check(struct sk_buff *skb, if (skb->encapsulation && (skb_inner_mac_header(skb) - skb_transport_header(skb) > I40E_MAX_TUNNEL_HDR_LEN)) - return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); + return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); return features; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 9f27001cac1f..fca35aa90d0f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -8598,7 +8598,7 @@ ixgbe_features_check(struct sk_buff *skb, struct net_device *dev, if (unlikely(skb_inner_mac_header(skb) - skb_transport_header(skb) > IXGBE_MAX_TUNNEL_HDR_LEN)) - return features & ~NETIF_F_ALL_CSUM; + return features & ~NETIF_F_CSUM_MASK; return features; } diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index 060dd3922974..b1de7afd4116 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -2753,7 +2753,7 @@ static netdev_features_t jme_fix_features(struct net_device *netdev, netdev_features_t features) { if (netdev->mtu > 1900) - features &= ~(NETIF_F_ALL_TSO | NETIF_F_ALL_CSUM); + features &= ~(NETIF_F_ALL_TSO | NETIF_F_CSUM_MASK); return features; } diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index 5606a043063e..ec0a22119e09 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -4380,7 +4380,7 @@ static netdev_features_t sky2_fix_features(struct net_device *dev, */ if (dev->mtu > ETH_DATA_LEN && hw->chip_id == CHIP_ID_YUKON_EC_U) { netdev_info(dev, "checksum offload not possible with jumbo frames\n"); - features &= ~(NETIF_F_TSO|NETIF_F_SG|NETIF_F_ALL_CSUM); + features &= ~(NETIF_F_TSO | NETIF_F_SG | NETIF_F_CSUM_MASK); } /* Some hardware requires receive checksum for RSS to work. */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 038ac6b14a60..7060539d276a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2071,7 +2071,7 @@ nfp_net_features_check(struct sk_buff *skb, struct net_device *dev, l4_hdr = ipv6_hdr(skb)->nexthdr; break; default: - return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); + return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); } if (skb->inner_protocol_type != ENCAP_TYPE_ETHER || @@ -2080,7 +2080,7 @@ nfp_net_features_check(struct sk_buff *skb, struct net_device *dev, (l4_hdr == IPPROTO_UDP && (skb_inner_mac_header(skb) - skb_transport_header(skb) != sizeof(struct udphdr) + sizeof(struct vxlanhdr)))) - return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); + return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); return features; } diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c index 08d4be616064..e097e6baaac4 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c @@ -500,7 +500,7 @@ void pch_gbe_check_options(struct pch_gbe_adapter *adapter) val = XsumTX; pch_gbe_validate_option(&val, &opt, adapter); if (!val) - dev->features &= ~NETIF_F_ALL_CSUM; + dev->features &= ~NETIF_F_CSUM_MASK; } { /* Flow Control */ static const struct pch_gbe_option opt = { diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index b405349a570c..1fe13c733c1e 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -3131,7 +3131,7 @@ static int efx_pci_probe(struct pci_dev *pci_dev, if (efx->type->offload_features & NETIF_F_V6_CSUM) net_dev->features |= NETIF_F_TSO6; /* Mask for features that also apply to VLAN devices */ - net_dev->vlan_features |= (NETIF_F_ALL_CSUM | NETIF_F_SG | + net_dev->vlan_features |= (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_ALL_TSO | NETIF_F_RXCSUM); /* All offloads can be toggled */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 3c6549aee11d..0b0fea73a7a7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2402,7 +2402,7 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev, features &= ~NETIF_F_RXCSUM; if (!priv->plat->tx_coe) - features &= ~NETIF_F_ALL_CSUM; + features &= ~NETIF_F_CSUM_MASK; /* Some GMAC devices have a bugged Jumbo frame support that * needs to have the Tx COE disabled for oversized frames @@ -2410,7 +2410,7 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev, * the TX csum insertionin the TDES and not use SF. */ if (priv->plat->bugged_jumbo && (dev->mtu > ETH_DATA_LEN)) - features &= ~NETIF_F_ALL_CSUM; + features &= ~NETIF_F_CSUM_MASK; return features; } diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index a9268db4e349..f94392d07126 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -88,7 +88,7 @@ static struct lock_class_key ipvlan_netdev_xmit_lock_key; static struct lock_class_key ipvlan_netdev_addr_lock_key; #define IPVLAN_FEATURES \ - (NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \ + (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \ NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_GSO_ROBUST | \ NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \ NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 06c8bfeaccd6..ae3b486fb663 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -762,7 +762,7 @@ static struct lock_class_key macvlan_netdev_addr_lock_key; NETIF_F_GSO_ROBUST) #define MACVLAN_FEATURES \ - (NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \ + (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \ NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_LRO | \ NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \ NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 0fc521941c71..d636d051fac8 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -388,7 +388,7 @@ static rx_handler_result_t macvtap_handle_frame(struct sk_buff **pskb) * check, we either support them all or none. */ if (skb->ip_summed == CHECKSUM_PARTIAL && - !(features & NETIF_F_ALL_CSUM) && + !(features & NETIF_F_CSUM_MASK) && skb_checksum_help(skb)) goto drop; skb_queue_tail(&q->sk.sk_receive_queue, skb); diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 059c0f60a2b2..915f60fce186 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -981,7 +981,7 @@ static void team_port_disable(struct team *team, team_lower_state_changed(port); } -#define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ +#define TEAM_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \ NETIF_F_HIGHDMA | NETIF_F_LRO) @@ -2091,7 +2091,6 @@ static void team_setup(struct net_device *dev) NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; - dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM); dev->features |= dev->hw_features; } diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index d9427ca3dba7..34642a9583e0 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1986,7 +1986,7 @@ rtl8152_features_check(struct sk_buff *skb, struct net_device *dev, int offset = skb_transport_offset(skb); if ((mss || skb->ip_summed == CHECKSUM_PARTIAL) && offset > max_offset) - features &= ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); + features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); else if ((skb->len + sizeof(struct tx_desc)) > agg_buf_sz) features &= ~NETIF_F_GSO_MASK; diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c index 679785b0209c..9de4f23910d8 100644 --- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c +++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c @@ -69,7 +69,7 @@ ksocknal_lib_zc_capable(ksock_conn_t *conn) /* ZC if the socket supports scatter/gather and doesn't need software * checksums */ - return ((caps & NETIF_F_SG) != 0 && (caps & NETIF_F_ALL_CSUM) != 0); + return ((caps & NETIF_F_SG) != 0 && (caps & NETIF_F_CSUM_MASK) != 0); } int diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index 6395f8309393..2c4e94ab88da 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -149,7 +149,12 @@ enum { #define NETIF_F_GEN_CSUM NETIF_F_HW_CSUM #define NETIF_F_V4_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IP_CSUM) #define NETIF_F_V6_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM) -#define NETIF_F_ALL_CSUM (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM) + +/* List of IP checksum features. Note that NETIF_HW_CSUM should not be + * set in features when NETIF_F_IP_CSUM or NETIF_F_IPV6_CSUM are set-- + * this would be contradictory + */ +#define NETIF_F_CSUM_MASK (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM) #define NETIF_F_ALL_TSO (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1bb21ff0fa64..a54223a113b1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3763,12 +3763,12 @@ static inline netdev_features_t netdev_intersect_features(netdev_features_t f1, netdev_features_t f2) { if (f1 & NETIF_F_GEN_CSUM) - f1 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM); + f1 |= (NETIF_F_CSUM_MASK & ~NETIF_F_GEN_CSUM); if (f2 & NETIF_F_GEN_CSUM) - f2 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM); + f2 |= (NETIF_F_CSUM_MASK & ~NETIF_F_GEN_CSUM); f1 &= f2; if (f1 & NETIF_F_GEN_CSUM) - f1 &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM); + f1 &= ~(NETIF_F_CSUM_MASK & ~NETIF_F_GEN_CSUM); return f1; } diff --git a/include/net/vxlan.h b/include/net/vxlan.h index c1c899c3a51b..b5a1aec1a167 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -232,7 +232,7 @@ static inline netdev_features_t vxlan_features_check(struct sk_buff *skb, skb->inner_protocol != htons(ETH_P_TEB) || (skb_inner_mac_header(skb) - skb_transport_header(skb) != sizeof(struct udphdr) + sizeof(struct vxlanhdr)))) - return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); + return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); return features; } diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 45b74e875381..ad5e2fd1012c 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -543,7 +543,7 @@ static int vlan_dev_init(struct net_device *dev) (1<<__LINK_STATE_DORMANT))) | (1<<__LINK_STATE_PRESENT); - dev->hw_features = NETIF_F_ALL_CSUM | NETIF_F_SG | + dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC | NETIF_F_ALL_FCOE; diff --git a/net/core/dev.c b/net/core/dev.c index 8f705fcedb94..5a3b5a404642 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2645,7 +2645,7 @@ static netdev_features_t harmonize_features(struct sk_buff *skb, if (skb->ip_summed != CHECKSUM_NONE && !can_checksum_protocol(features, type)) { - features &= ~NETIF_F_ALL_CSUM; + features &= ~NETIF_F_CSUM_MASK; } else if (illegal_highdma(skb->dev, skb)) { features &= ~NETIF_F_SG; } @@ -2792,7 +2792,7 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device else skb_set_transport_header(skb, skb_checksum_start_offset(skb)); - if (!(features & NETIF_F_ALL_CSUM) && + if (!(features & NETIF_F_CSUM_MASK) && skb_checksum_help(skb)) goto out_kfree_skb; } @@ -7572,15 +7572,15 @@ netdev_features_t netdev_increment_features(netdev_features_t all, netdev_features_t one, netdev_features_t mask) { if (mask & NETIF_F_GEN_CSUM) - mask |= NETIF_F_ALL_CSUM; + mask |= NETIF_F_CSUM_MASK; mask |= NETIF_F_VLAN_CHALLENGED; - all |= one & (NETIF_F_ONE_FOR_ALL|NETIF_F_ALL_CSUM) & mask; + all |= one & (NETIF_F_ONE_FOR_ALL | NETIF_F_CSUM_MASK) & mask; all &= one | ~NETIF_F_ALL_FOR_ALL; /* If one device supports hw checksumming, set for all. */ if (all & NETIF_F_GEN_CSUM) - all &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM); + all &= ~(NETIF_F_CSUM_MASK & ~NETIF_F_GEN_CSUM); return all; } diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 4a0cab85d67d..09948a726347 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -235,7 +235,7 @@ static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd) switch (eth_cmd) { case ETHTOOL_GTXCSUM: case ETHTOOL_STXCSUM: - return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CRC; + return NETIF_F_CSUM_MASK | NETIF_F_SCTP_CRC; case ETHTOOL_GRXCSUM: case ETHTOOL_SRXCSUM: return NETIF_F_RXCSUM; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c82cca18c90f..cf7ef7be79f0 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1018,7 +1018,7 @@ int tcp_sendpage(struct sock *sk, struct page *page, int offset, ssize_t res; if (!(sk->sk_route_caps & NETIF_F_SG) || - !(sk->sk_route_caps & NETIF_F_ALL_CSUM)) + !(sk->sk_route_caps & NETIF_F_CSUM_MASK)) return sock_no_sendpage(sk->sk_socket, page, offset, size, flags); @@ -1175,7 +1175,7 @@ new_segment: /* * Check whether we can use HW checksum. */ - if (sk->sk_route_caps & NETIF_F_ALL_CSUM) + if (sk->sk_route_caps & NETIF_F_CSUM_MASK) skb->ip_summed = CHECKSUM_PARTIAL; skb_entail(sk, skb); -- cgit v1.2.3-59-g8ed1b From c8cd0989bd151fda87bbf10887b3df18021284bc Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Mon, 14 Dec 2015 11:19:44 -0800 Subject: net: Eliminate NETIF_F_GEN_CSUM and NETIF_F_V[46]_CSUM These netif flags are unnecessary convolutions. It is more straightforward to just use NETIF_F_HW_CSUM, NETIF_F_IP_CSUM, and NETIF_F_IPV6_CSUM directly. This patch also: - Cleans up can_checksum_protocol - Simplifies netdev_intersect_features Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/efx.c | 2 +- drivers/net/macvlan.c | 2 +- include/linux/if_vlan.h | 2 +- include/linux/netdev_features.h | 9 +++---- include/linux/netdevice.h | 40 ++++++++++++++++++++------------ net/core/dev.c | 12 +++++----- net/core/pktgen.c | 4 ++-- net/ipv4/ip_output.c | 2 +- net/ipv4/netfilter/nf_nat_l3proto_ipv4.c | 3 ++- net/ipv4/udp.c | 3 ++- net/ipv4/udp_offload.c | 5 ++-- net/ipv6/ip6_output.c | 2 +- net/ipv6/netfilter/nf_nat_l3proto_ipv6.c | 3 ++- 13 files changed, 50 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 1fe13c733c1e..6f697438545d 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -3128,7 +3128,7 @@ static int efx_pci_probe(struct pci_dev *pci_dev, net_dev->features |= (efx->type->offload_features | NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_TSO | NETIF_F_RXCSUM); - if (efx->type->offload_features & NETIF_F_V6_CSUM) + if (efx->type->offload_features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM)) net_dev->features |= NETIF_F_TSO6; /* Mask for features that also apply to VLAN devices */ net_dev->vlan_features |= (NETIF_F_HW_CSUM | NETIF_F_SG | diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index ae3b486fb663..6a57a005e0ca 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -758,7 +758,7 @@ static struct lock_class_key macvlan_netdev_xmit_lock_key; static struct lock_class_key macvlan_netdev_addr_lock_key; #define ALWAYS_ON_FEATURES \ - (NETIF_F_SG | NETIF_F_GEN_CSUM | NETIF_F_GSO_SOFTWARE | NETIF_F_LLTX | \ + (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE | NETIF_F_LLTX | \ NETIF_F_GSO_ROBUST) #define MACVLAN_FEATURES \ diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 05f5879821b8..a5f6ce6b578c 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -621,7 +621,7 @@ static inline netdev_features_t vlan_features_check(const struct sk_buff *skb, NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | - NETIF_F_GEN_CSUM | + NETIF_F_HW_CSUM | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX); diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index 2c4e94ab88da..d9654f0eecb3 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -146,15 +146,12 @@ enum { #define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | \ NETIF_F_TSO6 | NETIF_F_UFO) -#define NETIF_F_GEN_CSUM NETIF_F_HW_CSUM -#define NETIF_F_V4_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IP_CSUM) -#define NETIF_F_V6_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM) - -/* List of IP checksum features. Note that NETIF_HW_CSUM should not be +/* List of IP checksum features. Note that NETIF_F_ HW_CSUM should not be * set in features when NETIF_F_IP_CSUM or NETIF_F_IPV6_CSUM are set-- * this would be contradictory */ -#define NETIF_F_CSUM_MASK (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM) +#define NETIF_F_CSUM_MASK (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \ + NETIF_F_HW_CSUM) #define NETIF_F_ALL_TSO (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a54223a113b1..283984b67cd9 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3691,13 +3691,24 @@ __be16 skb_network_protocol(struct sk_buff *skb, int *depth); static inline bool can_checksum_protocol(netdev_features_t features, __be16 protocol) { - return ((features & NETIF_F_GEN_CSUM) || - ((features & NETIF_F_V4_CSUM) && - protocol == htons(ETH_P_IP)) || - ((features & NETIF_F_V6_CSUM) && - protocol == htons(ETH_P_IPV6)) || - ((features & NETIF_F_FCOE_CRC) && - protocol == htons(ETH_P_FCOE))); + if (protocol == htons(ETH_P_FCOE)) + return !!(features & NETIF_F_FCOE_CRC); + + /* Assume this is an IP checksum (not SCTP CRC) */ + + if (features & NETIF_F_HW_CSUM) { + /* Can checksum everything */ + return true; + } + + switch (protocol) { + case htons(ETH_P_IP): + return !!(features & NETIF_F_IP_CSUM); + case htons(ETH_P_IPV6): + return !!(features & NETIF_F_IPV6_CSUM); + default: + return false; + } } #ifdef CONFIG_BUG @@ -3762,15 +3773,14 @@ void linkwatch_run_queue(void); static inline netdev_features_t netdev_intersect_features(netdev_features_t f1, netdev_features_t f2) { - if (f1 & NETIF_F_GEN_CSUM) - f1 |= (NETIF_F_CSUM_MASK & ~NETIF_F_GEN_CSUM); - if (f2 & NETIF_F_GEN_CSUM) - f2 |= (NETIF_F_CSUM_MASK & ~NETIF_F_GEN_CSUM); - f1 &= f2; - if (f1 & NETIF_F_GEN_CSUM) - f1 &= ~(NETIF_F_CSUM_MASK & ~NETIF_F_GEN_CSUM); + if ((f1 ^ f2) & NETIF_F_HW_CSUM) { + if (f1 & NETIF_F_HW_CSUM) + f1 |= (NETIF_F_IP_CSUM|NETIF_F_IP_CSUM); + else + f2 |= (NETIF_F_IP_CSUM|NETIF_F_IP_CSUM); + } - return f1; + return f1 & f2; } static inline netdev_features_t netdev_get_wanted_features( diff --git a/net/core/dev.c b/net/core/dev.c index 5a3b5a404642..45b013f27625 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6467,9 +6467,9 @@ static netdev_features_t netdev_fix_features(struct net_device *dev, /* UFO needs SG and checksumming */ if (features & NETIF_F_UFO) { /* maybe split UFO into V4 and V6? */ - if (!((features & NETIF_F_GEN_CSUM) || - (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM)) - == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) { + if (!(features & NETIF_F_HW_CSUM) && + ((features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) != + (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))) { netdev_dbg(dev, "Dropping NETIF_F_UFO since no checksum offload features.\n"); features &= ~NETIF_F_UFO; @@ -7571,7 +7571,7 @@ static int dev_cpu_callback(struct notifier_block *nfb, netdev_features_t netdev_increment_features(netdev_features_t all, netdev_features_t one, netdev_features_t mask) { - if (mask & NETIF_F_GEN_CSUM) + if (mask & NETIF_F_HW_CSUM) mask |= NETIF_F_CSUM_MASK; mask |= NETIF_F_VLAN_CHALLENGED; @@ -7579,8 +7579,8 @@ netdev_features_t netdev_increment_features(netdev_features_t all, all &= one | ~NETIF_F_ALL_FOR_ALL; /* If one device supports hw checksumming, set for all. */ - if (all & NETIF_F_GEN_CSUM) - all &= ~(NETIF_F_CSUM_MASK & ~NETIF_F_GEN_CSUM); + if (all & NETIF_F_HW_CSUM) + all &= ~(NETIF_F_CSUM_MASK & ~NETIF_F_HW_CSUM); return all; } diff --git a/net/core/pktgen.c b/net/core/pktgen.c index de8d5cc5eb24..2be144498bcf 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2898,7 +2898,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, if (!(pkt_dev->flags & F_UDPCSUM)) { skb->ip_summed = CHECKSUM_NONE; - } else if (odev->features & NETIF_F_V4_CSUM) { + } else if (odev->features & (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM)) { skb->ip_summed = CHECKSUM_PARTIAL; skb->csum = 0; udp4_hwcsum(skb, iph->saddr, iph->daddr); @@ -3032,7 +3032,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, if (!(pkt_dev->flags & F_UDPCSUM)) { skb->ip_summed = CHECKSUM_NONE; - } else if (odev->features & NETIF_F_V6_CSUM) { + } else if (odev->features & (NETIF_F_HW_CSUM | NETIF_F_IPV6_CSUM)) { skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct udphdr, check); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index e0b94cd843d7..568e2bc0d93d 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -911,7 +911,7 @@ static int __ip_append_data(struct sock *sk, */ if (transhdrlen && length + fragheaderlen <= mtu && - rt->dst.dev->features & NETIF_F_V4_CSUM && + rt->dst.dev->features & (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM) && !(flags & MSG_MORE) && !exthdrlen) csummode = CHECKSUM_PARTIAL; diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c index 5075b7ecd26d..61c7cc22ea68 100644 --- a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c @@ -132,7 +132,8 @@ static void nf_nat_ipv4_csum_recalc(struct sk_buff *skb, if (skb->ip_summed != CHECKSUM_PARTIAL) { if (!(rt->rt_flags & RTCF_LOCAL) && - (!skb->dev || skb->dev->features & NETIF_F_V4_CSUM)) { + (!skb->dev || skb->dev->features & + (NETIF_F_IP_CSUM | NETIF_F_HW_CSUM))) { skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) + diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 0c7b0e61b917..8841e984f8bf 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -772,7 +772,8 @@ void udp_set_csum(bool nocheck, struct sk_buff *skb, else if (skb_is_gso(skb)) uh->check = ~udp_v4_check(len, saddr, daddr, 0); else if (skb_dst(skb) && skb_dst(skb)->dev && - (skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) { + (skb_dst(skb)->dev->features & + (NETIF_F_IP_CSUM | NETIF_F_HW_CSUM))) { BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL); diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index f9386160cbee..130042660181 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -60,8 +60,9 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, /* Try to offload checksum if possible */ offload_csum = !!(need_csum && - (skb->dev->features & - (is_ipv6 ? NETIF_F_V6_CSUM : NETIF_F_V4_CSUM))); + ((skb->dev->features & NETIF_F_HW_CSUM) || + (skb->dev->features & (is_ipv6 ? + NETIF_F_IPV6_CSUM : NETIF_F_IP_CSUM)))); /* segment inner packet. */ enc_features = skb->dev->hw_enc_features & features; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index e6a7bd15b9b7..2f748452b4aa 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1322,7 +1322,7 @@ emsgsize: headersize == sizeof(struct ipv6hdr) && length < mtu - headersize && !(flags & MSG_MORE) && - rt->dst.dev->features & NETIF_F_V6_CSUM) + rt->dst.dev->features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM)) csummode = CHECKSUM_PARTIAL; if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_RAW) { diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c index 238e70c3f7b7..6ce309928841 100644 --- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c @@ -136,7 +136,8 @@ static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb, if (skb->ip_summed != CHECKSUM_PARTIAL) { if (!(rt->rt6i_flags & RTF_LOCAL) && - (!skb->dev || skb->dev->features & NETIF_F_V6_CSUM)) { + (!skb->dev || skb->dev->features & + (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))) { skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) + -- cgit v1.2.3-59-g8ed1b From e34d65696d2ef13dc32f2a162556c86c461ed763 Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Mon, 14 Dec 2015 11:31:59 +0800 Subject: stmmac: create of compatible mdio bus for stmmac driver The DSA driver needs to be passed a reference to an mdio bus. Typically the mac is configured to use a fixed link but the mdio bus still needs to be registered so that it con configure the switch. This patch follows the same process as the altera tse ethernet driver for creation of the mdio bus. Acked-by: Rob Herring Signed-off-by: Phil Reid Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 8 +++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 26 +++++++++++++++++++--- .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 2 +- 3 files changed, 32 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index f34fc3c81a75..fd5ddf8c4504 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -47,6 +47,7 @@ Optional properties: - snps,burst_len: The AXI burst lenth value of the AXI BUS MODE register. - tx-fifo-depth: See ethernet.txt file in the same directory - rx-fifo-depth: See ethernet.txt file in the same directory +- mdio: with compatible = "snps,dwmac-mdio", create and register mdio bus. Examples: @@ -65,4 +66,11 @@ Examples: tx-fifo-depth = <16384>; clocks = <&clock>; clock-names = "stmmaceth"; + mdio0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + phy1: ethernet-phy@0 { + }; + }; }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index bba670c42e37..16c85ccd1762 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -29,7 +29,7 @@ #include #include #include - +#include #include #include "stmmac.h" @@ -200,10 +200,29 @@ int stmmac_mdio_register(struct net_device *ndev) struct stmmac_priv *priv = netdev_priv(ndev); struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data; int addr, found; + struct device_node *mdio_node = NULL; + struct device_node *child_node = NULL; if (!mdio_bus_data) return 0; + if (IS_ENABLED(CONFIG_OF)) { + for_each_child_of_node(priv->device->of_node, child_node) { + if (of_device_is_compatible(child_node, + "snps,dwmac-mdio")) { + mdio_node = child_node; + break; + } + } + + if (mdio_node) { + netdev_dbg(ndev, "FOUND MDIO subnode\n"); + } else { + netdev_err(ndev, "NO MDIO subnode\n"); + return 0; + } + } + new_bus = mdiobus_alloc(); if (new_bus == NULL) return -ENOMEM; @@ -231,7 +250,8 @@ int stmmac_mdio_register(struct net_device *ndev) new_bus->irq = irqlist; new_bus->phy_mask = mdio_bus_data->phy_mask; new_bus->parent = priv->device; - err = mdiobus_register(new_bus); + + err = of_mdiobus_register(new_bus, mdio_node); if (err != 0) { pr_err("%s: Cannot register as MDIO bus\n", new_bus->name); goto bus_register_fail; @@ -284,7 +304,7 @@ int stmmac_mdio_register(struct net_device *ndev) } } - if (!found) { + if (!found && !mdio_node) { pr_warn("%s: No PHY found\n", ndev->name); mdiobus_unregister(new_bus); mdiobus_free(new_bus); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index d02691ba3d7f..6a52fa18cbf2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -146,7 +146,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0) dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n"); - if (plat->phy_node || plat->phy_bus_name) + if ((plat->phy_node && !of_phy_is_fixed_link(np)) || plat->phy_bus_name) plat->mdio_bus_data = NULL; else plat->mdio_bus_data = -- cgit v1.2.3-59-g8ed1b From 19d857c9038e5c07db8f8cc02b5ad0cd0098714f Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Mon, 14 Dec 2015 11:32:01 +0800 Subject: stmmac: Fix calculations for ptp counters when clock input = 50Mhz. stmmac_config_sub_second_increment set the sub second increment to 20ns. Driver is configured to use the fine adjustment method where the sub second register is incremented when the acculumator incremented by the addend register wraps overflows. This accumulator is update on every ptp clk cycle. If a ptp clk with a period of greater than 20ns was used the sub second register would not get updated correctly. Instead set the sub sec increment to twice the period of the ptp clk. This result in the addend register being set mid range and overflow the accumlator every 2 clock cycles. Signed-off-by: Phil Reid Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 2 +- drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c | 9 ++++++--- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 19 ++++++++----------- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index f4518bc2cd28..1e19c8fd8b82 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -439,7 +439,7 @@ struct stmmac_ops { /* PTP and HW Timer helpers */ struct stmmac_hwtimestamp { void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data); - void (*config_sub_second_increment) (void __iomem *ioaddr); + u32 (*config_sub_second_increment) (void __iomem *ioaddr, u32 clk_rate); int (*init_systime) (void __iomem *ioaddr, u32 sec, u32 nsec); int (*config_addend) (void __iomem *ioaddr, u32 addend); int (*adjust_systime) (void __iomem *ioaddr, u32 sec, u32 nsec, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c index 76ad214b4036..a77f68918010 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c @@ -33,22 +33,25 @@ static void stmmac_config_hw_tstamping(void __iomem *ioaddr, u32 data) writel(data, ioaddr + PTP_TCR); } -static void stmmac_config_sub_second_increment(void __iomem *ioaddr) +static u32 stmmac_config_sub_second_increment(void __iomem *ioaddr, + u32 ptp_clock) { u32 value = readl(ioaddr + PTP_TCR); unsigned long data; /* Convert the ptp_clock to nano second - * formula = (1/ptp_clock) * 1000000000 + * formula = (2/ptp_clock) * 1000000000 * where, ptp_clock = 50MHz. */ - data = (1000000000ULL / 50000000); + data = (2000000000ULL / ptp_clock); /* 0.465ns accuracy */ if (!(value & PTP_TCR_TSCTRLSSR)) data = (data * 1000) / 465; writel(data, ioaddr + PTP_SSIR); + + return data; } static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 0b0fea73a7a7..6d4c33a7f0b4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -53,6 +53,7 @@ #include "stmmac.h" #include #include +#include "dwmac1000.h" #define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x) @@ -185,7 +186,7 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv) priv->clk_csr = STMMAC_CSR_100_150M; else if ((clk_rate >= CSR_F_150M) && (clk_rate < CSR_F_250M)) priv->clk_csr = STMMAC_CSR_150_250M; - else if ((clk_rate >= CSR_F_250M) && (clk_rate <= CSR_F_300M)) + else if ((clk_rate >= CSR_F_250M) && (clk_rate < CSR_F_300M)) priv->clk_csr = STMMAC_CSR_250_300M; } } @@ -435,6 +436,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) u32 ts_master_en = 0; u32 ts_event_en = 0; u32 value = 0; + u32 sec_inc; if (!(priv->dma_cap.time_stamp || priv->adv_ts)) { netdev_alert(priv->dev, "No support for HW time stamping\n"); @@ -598,24 +600,19 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) tstamp_all | ptp_v2 | ptp_over_ethernet | ptp_over_ipv6_udp | ptp_over_ipv4_udp | ts_event_en | ts_master_en | snap_type_sel); - priv->hw->ptp->config_hw_tstamping(priv->ioaddr, value); /* program Sub Second Increment reg */ - priv->hw->ptp->config_sub_second_increment(priv->ioaddr); + sec_inc = priv->hw->ptp->config_sub_second_increment( + priv->ioaddr, priv->clk_ptp_rate); + temp = div_u64(1000000000ULL, sec_inc); /* calculate default added value: * formula is : * addend = (2^32)/freq_div_ratio; - * where, freq_div_ratio = clk_ptp_ref_i/50MHz - * hence, addend = ((2^32) * 50MHz)/clk_ptp_ref_i; - * NOTE: clk_ptp_ref_i should be >= 50MHz to - * achieve 20ns accuracy. - * - * 2^x * y == (y << x), hence - * 2^32 * 50000000 ==> (50000000 << 32) + * where, freq_div_ratio = 1e9ns/sec_inc */ - temp = (u64) (50000000ULL << 32); + temp = (u64)(temp << 32); priv->default_addend = div_u64(temp, priv->clk_ptp_rate); priv->hw->ptp->config_addend(priv->ioaddr, priv->default_addend); -- cgit v1.2.3-59-g8ed1b From 43569814fa35b2ae68f09780c4ee3d4a182711e9 Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Mon, 14 Dec 2015 11:32:02 +0800 Subject: stmmac: socfpga: Provide dt node to config ptp clk source. Provides an options to use the ptp clock routed from the Altera FPGA fabric. Instead of the defalt eosc1 clock connected to the ARM HPS core. This setting affects all emacs in the core as the ptp clock is common. Acked-by: Rob Herring Signed-off-by: Phil Reid Acked-by: Dinh Nguyen Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/socfpga-dwmac.txt | 2 ++ drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c | 9 +++++++++ 2 files changed, 11 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/net/socfpga-dwmac.txt b/Documentation/devicetree/bindings/net/socfpga-dwmac.txt index 3a9d67951606..72d82d684342 100644 --- a/Documentation/devicetree/bindings/net/socfpga-dwmac.txt +++ b/Documentation/devicetree/bindings/net/socfpga-dwmac.txt @@ -11,6 +11,8 @@ Required properties: designware version numbers documented in stmmac.txt - altr,sysmgr-syscon : Should be the phandle to the system manager node that encompasses the glue register, the register offset, and the register shift. + - altr,f2h_ptp_ref_clk use f2h_ptp_ref_clk instead of default eosc1 clock + for ptp ref clk. This affects all emacs as the clock is common. Optional properties: altr,emac-splitter: Should be the phandle to the emac splitter soft IP node if diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index 401383b252a8..f0d797ab74d8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -32,6 +32,7 @@ #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2 #define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2 #define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003 +#define SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK 0x00000010 #define EMAC_SPLITTER_CTRL_REG 0x0 #define EMAC_SPLITTER_CTRL_SPEED_MASK 0x3 @@ -47,6 +48,7 @@ struct socfpga_dwmac { struct regmap *sys_mgr_base_addr; struct reset_control *stmmac_rst; void __iomem *splitter_base; + bool f2h_ptp_ref_clk; }; static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed) @@ -116,6 +118,8 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device * return -EINVAL; } + dwmac->f2h_ptp_ref_clk = of_property_read_bool(np, "altr,f2h_ptp_ref_clk"); + np_splitter = of_parse_phandle(np, "altr,emac-splitter", 0); if (np_splitter) { if (of_address_to_resource(np_splitter, 0, &res_splitter)) { @@ -171,6 +175,11 @@ static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac) ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift); ctrl |= val << reg_shift; + if (dwmac->f2h_ptp_ref_clk) + ctrl |= SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2); + else + ctrl &= ~(SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2)); + regmap_write(sys_mgr_base_addr, reg_offset, ctrl); return 0; } -- cgit v1.2.3-59-g8ed1b From e6eb8ca9e486a8f73ae237630e89406683b2be75 Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Sun, 30 Aug 2015 17:41:36 +0300 Subject: iwlwifi: expose fw usniffer mode to more utilities Today, in order to configure fw in usniffer mode, the ucode must have the corresponding tlv, which is revealed to the driver while parsing the ucode. Expose the mode of the usniffer to other utilities in the driver (other than the ucode parser) by passing back a pointer to the value. This can be very useful for allowing configuring the fw dbg data using an external configuration file, because this configuration depends on the fw usniffer mode. Signed-off-by: Golan Ben-Ami Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 16756f0eaba8..47a83abc557d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -594,7 +594,8 @@ static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv, static int iwl_parse_tlv_firmware(struct iwl_drv *drv, const struct firmware *ucode_raw, struct iwl_firmware_pieces *pieces, - struct iwl_ucode_capabilities *capa) + struct iwl_ucode_capabilities *capa, + bool *usniffer_images) { struct iwl_tlv_ucode_header *ucode = (void *)ucode_raw->data; struct iwl_ucode_tlv *tlv; @@ -607,7 +608,6 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, char buildstr[25]; u32 build, paging_mem_size; int num_of_cpus; - bool usniffer_images = false; bool usniffer_req = false; bool gscan_capa = false; @@ -980,7 +980,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, break; } case IWL_UCODE_TLV_SEC_RT_USNIFFER: - usniffer_images = true; + *usniffer_images = true; iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR_USNIFFER, tlv_len); @@ -1031,7 +1031,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, } } - if (usniffer_req && !usniffer_images) { + if (usniffer_req && !*usniffer_images) { IWL_ERR(drv, "user selected to work with usniffer but usniffer image isn't available in ucode package\n"); return -EINVAL; @@ -1192,6 +1192,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) u32 api_ver; int i; bool load_module = false; + bool usniffer_images = false; fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH; fw->ucode_capa.standard_phy_calibration_size = @@ -1229,7 +1230,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces); else err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces, - &fw->ucode_capa); + &fw->ucode_capa, &usniffer_images); if (err) goto try_again; -- cgit v1.2.3-59-g8ed1b From c97dab40796c59a4b03c532603e837077718fb81 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 19 Nov 2015 11:53:49 +0200 Subject: iwlwifi: mvm: change protocol offload flows RFC4862 states that "In all cases, a node MUST NOT respond to a Neighbor Solicitation for a tentative address". Currently the driver configures the NS offload and does not wait for address to become permanent, thus violating the RFC. Just removing the address from the address list is not good enough for all cases, since the NS messages are needed for the duplicate address detection and should not be discarded. For d0i3 disable NS offload. Put tentative address in the address list so the NS packet will not be filtered out by ucode. For D3 the platform will not wake from NS packets - so enable NS offload while removing the tentative address from the list. Given that now NS offload might be disabled, and that the ucode uses the IP data for other puroposes (L3 filtering) add two independent flags indicating if IPv4\IPv6 data is valid. Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 6 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h | 4 ++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 + .../net/wireless/intel/iwlwifi/mvm/offloading.c | 74 ++++++++++++++++------ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 3 +- 5 files changed, 69 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index b1c99921c376..8824a894aab0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -104,9 +104,13 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, struct inet6_ifaddr *ifa; int idx = 0; + memset(mvmvif->tentative_addrs, 0, sizeof(mvmvif->tentative_addrs)); + read_lock_bh(&idev->lock); list_for_each_entry(ifa, &idev->addr_list, if_list) { mvmvif->target_ipv6_addrs[idx] = ifa->addr; + if (ifa->flags & IFA_F_TENTATIVE) + __set_bit(idx, mvmvif->tentative_addrs); idx++; if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX) break; @@ -964,7 +968,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, if (ret) return ret; - ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0); + ret = iwl_mvm_send_proto_offload(mvm, vif, false, true, 0); if (ret) return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h index 228684cfdb7e..c36c9563f744 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h @@ -94,10 +94,14 @@ struct iwl_d3_manager_config { * enum iwl_d3_proto_offloads - enabled protocol offloads * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled + * @IWL_D3_PROTO_IPV4_VALID: IPv4 data is valid + * @IWL_D3_PROTO_IPV6_VALID: IPv6 data is valid */ enum iwl_proto_offloads { IWL_D3_PROTO_OFFLOAD_ARP = BIT(0), IWL_D3_PROTO_OFFLOAD_NS = BIT(1), + IWL_D3_PROTO_IPV4_VALID = BIT(2), + IWL_D3_PROTO_IPV6_VALID = BIT(3), }; #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 3fc7199abc94..d8760fa9e322 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -422,6 +422,7 @@ struct iwl_mvm_vif { #if IS_ENABLED(CONFIG_IPV6) /* IPv6 addresses for WoWLAN */ struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; + unsigned long tentative_addrs[BITS_TO_LONGS(IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)]; int num_target_ipv6_addrs; #endif @@ -1299,6 +1300,7 @@ void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool disable_offloading, + bool offload_ns, u32 cmd_flags); /* D0i3 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c index f83e2bc6d0d5..6338d9cf7070 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -64,6 +66,7 @@ *****************************************************************************/ #include #include +#include #include "mvm.h" void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, @@ -86,6 +89,7 @@ void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool disable_offloading, + bool offload_ns, u32 cmd_flags) { union { @@ -106,6 +110,13 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, #if IS_ENABLED(CONFIG_IPV6) struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int i; + /* + * Skip tentative address when ns offload is enabled to avoid + * violating RFC4862. + * Keep tentative address when ns offload is disabled so the NS packets + * will not be filtered out and will wake up the host. + */ + bool skip_tentative = offload_ns; if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { @@ -113,6 +124,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, struct iwl_targ_addr *addrs; int n_nsc, n_addrs; int c; + int num_skipped = 0; if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { nsc = cmd.v3s.ns_config; @@ -126,9 +138,6 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; } - if (mvmvif->num_target_ipv6_addrs) - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - /* * For each address we have (and that will fit) fill a target * address struct and combine for NS offload structs with the @@ -140,6 +149,12 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, struct in6_addr solicited_addr; int j; + if (skip_tentative && + test_bit(i, mvmvif->tentative_addrs)) { + num_skipped++; + continue; + } + addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], &solicited_addr); for (j = 0; j < c; j++) @@ -154,41 +169,64 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); } + if (mvmvif->num_target_ipv6_addrs - num_skipped) + enabled |= IWL_D3_PROTO_IPV6_VALID; + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) - cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i); + cmd.v3s.num_valid_ipv6_addrs = + cpu_to_le32(i - num_skipped); else - cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i); + cmd.v3l.num_valid_ipv6_addrs = + cpu_to_le32(i - num_skipped); } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { - if (mvmvif->num_target_ipv6_addrs) { - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); - } + bool found = false; BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != sizeof(mvmvif->target_ipv6_addrs[0])); for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, - IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) + IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) { + if (skip_tentative && + test_bit(i, mvmvif->tentative_addrs)) + continue; + memcpy(cmd.v2.target_ipv6_addr[i], &mvmvif->target_ipv6_addrs[i], sizeof(cmd.v2.target_ipv6_addr[i])); - } else { - if (mvmvif->num_target_ipv6_addrs) { - enabled |= IWL_D3_PROTO_OFFLOAD_NS; - memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); - } + found = true; + } + if (found) { + enabled |= IWL_D3_PROTO_IPV6_VALID; + memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); + } + } else { + bool found = false; BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != sizeof(mvmvif->target_ipv6_addrs[0])); for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, - IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) + IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) { + if (skip_tentative && + test_bit(i, mvmvif->tentative_addrs)) + continue; + memcpy(cmd.v1.target_ipv6_addr[i], &mvmvif->target_ipv6_addrs[i], sizeof(cmd.v1.target_ipv6_addr[i])); + + found = true; + } + + if (found) { + enabled |= IWL_D3_PROTO_IPV6_VALID; + memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); + } } -#endif + if (offload_ns && (enabled & IWL_D3_PROTO_IPV6_VALID)) + enabled |= IWL_D3_PROTO_OFFLOAD_NS; +#endif if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { common = &cmd.v3s.common; size = sizeof(cmd.v3s); @@ -204,7 +242,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, } if (vif->bss_conf.arp_addr_cnt) { - enabled |= IWL_D3_PROTO_OFFLOAD_ARP; + enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID; common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 31c16a185001..4753ecd1abda 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1153,7 +1153,8 @@ static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac, data->disable_offloading = true; iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags); - iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading, flags); + iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading, + false, flags); /* * on init/association, mvm already configures POWER_TABLE_CMD -- cgit v1.2.3-59-g8ed1b From e70d41b59f30d2f26f505890d9a893e924b359bf Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 27 Nov 2015 17:10:58 +0100 Subject: iwlwifi: dvm: fix compare_const_fl.cocci warnings Move constants to the right of binary operators. Generated by: scripts/coccinelle/misc/compare_const_fl.cocci type=cleanup Signed-off-by: Fengguang Wu Signed-off-by: Julia Lawall Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/calib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/calib.c b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c index 9be636268848..07a4c644fb9b 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/calib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c @@ -311,7 +311,7 @@ static int iwl_sens_energy_cck(struct iwl_priv *priv, /* If previous beacon had too many false alarms, * give it some extra margin by reducing sensitivity again * (but don't go below measured energy of desired Rx) */ - if (IWL_FA_TOO_MANY == data->nrg_prev_state) { + if (data->nrg_prev_state == IWL_FA_TOO_MANY) { IWL_DEBUG_CALIB(priv, "... increasing margin\n"); if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) data->nrg_th_cck -= NRG_MARGIN; -- cgit v1.2.3-59-g8ed1b From 541c9a84cd85203244307d9ebb821102eed82789 Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Wed, 9 Dec 2015 23:36:51 +0100 Subject: ssb: pick SoC invariants code from MIPS BCM47xx arch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is code in ssb fetching "invariants" that is basically a set of board specific data. Every host requires its own implementation of reading function. In ssb we have support for PCI, PCMCIA & SDIO. For some (historical?) reason code reading "invariants" for SoC was placed in arch code and provided by a callback. This is not needed nowadays, so lets move that into ssb. This way we keep all "invariants" functions in a single module making code cleaner. Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Kalle Valo --- arch/mips/bcm47xx/setup.c | 39 +-------------------------------------- drivers/ssb/Kconfig | 2 +- drivers/ssb/host_soc.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/ssb/main.c | 5 ++--- drivers/ssb/ssb_private.h | 3 +++ include/linux/ssb/ssb.h | 10 +++------- 6 files changed, 47 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c index 6d38948f0f1e..c807e32d6d81 100644 --- a/arch/mips/bcm47xx/setup.c +++ b/arch/mips/bcm47xx/setup.c @@ -101,50 +101,13 @@ static void bcm47xx_machine_halt(void) } #ifdef CONFIG_BCM47XX_SSB -static int bcm47xx_get_invariants(struct ssb_bus *bus, - struct ssb_init_invariants *iv) -{ - char buf[20]; - int len, err; - - /* Fill boardinfo structure */ - memset(&iv->boardinfo, 0 , sizeof(struct ssb_boardinfo)); - - len = bcm47xx_nvram_getenv("boardvendor", buf, sizeof(buf)); - if (len > 0) { - err = kstrtou16(strim(buf), 0, &iv->boardinfo.vendor); - if (err) - pr_warn("Couldn't parse nvram board vendor entry with value \"%s\"\n", - buf); - } - if (!iv->boardinfo.vendor) - iv->boardinfo.vendor = SSB_BOARDVENDOR_BCM; - - len = bcm47xx_nvram_getenv("boardtype", buf, sizeof(buf)); - if (len > 0) { - err = kstrtou16(strim(buf), 0, &iv->boardinfo.type); - if (err) - pr_warn("Couldn't parse nvram board type entry with value \"%s\"\n", - buf); - } - - memset(&iv->sprom, 0, sizeof(struct ssb_sprom)); - bcm47xx_fill_sprom(&iv->sprom, NULL, false); - - if (bcm47xx_nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) - iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10); - - return 0; -} - static void __init bcm47xx_register_ssb(void) { int err; char buf[100]; struct ssb_mipscore *mcore; - err = ssb_bus_ssbbus_register(&bcm47xx_bus.ssb, SSB_ENUM_BASE, - bcm47xx_get_invariants); + err = ssb_bus_host_soc_register(&bcm47xx_bus.ssb, SSB_ENUM_BASE); if (err) panic("Failed to initialize SSB bus (err %d)", err); diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index 149214beeda9..0c675861623f 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig @@ -82,7 +82,7 @@ config SSB_SDIOHOST config SSB_HOST_SOC bool "Support for SSB bus on SoC" - depends on SSB + depends on SSB && BCM47XX_NVRAM help Host interface for a SSB directly mapped into memory. This is for some Broadcom SoCs from the BCM47xx and BCM53xx lines. diff --git a/drivers/ssb/host_soc.c b/drivers/ssb/host_soc.c index c809f255af34..d62992dc08b2 100644 --- a/drivers/ssb/host_soc.c +++ b/drivers/ssb/host_soc.c @@ -8,6 +8,7 @@ * Licensed under the GNU/GPL. See COPYING for details. */ +#include #include #include "ssb_private.h" @@ -171,3 +172,39 @@ const struct ssb_bus_ops ssb_host_soc_ops = { .block_write = ssb_host_soc_block_write, #endif }; + +int ssb_host_soc_get_invariants(struct ssb_bus *bus, + struct ssb_init_invariants *iv) +{ + char buf[20]; + int len, err; + + /* Fill boardinfo structure */ + memset(&iv->boardinfo, 0, sizeof(struct ssb_boardinfo)); + + len = bcm47xx_nvram_getenv("boardvendor", buf, sizeof(buf)); + if (len > 0) { + err = kstrtou16(strim(buf), 0, &iv->boardinfo.vendor); + if (err) + pr_warn("Couldn't parse nvram board vendor entry with value \"%s\"\n", + buf); + } + if (!iv->boardinfo.vendor) + iv->boardinfo.vendor = SSB_BOARDVENDOR_BCM; + + len = bcm47xx_nvram_getenv("boardtype", buf, sizeof(buf)); + if (len > 0) { + err = kstrtou16(strim(buf), 0, &iv->boardinfo.type); + if (err) + pr_warn("Couldn't parse nvram board type entry with value \"%s\"\n", + buf); + } + + memset(&iv->sprom, 0, sizeof(struct ssb_sprom)); + ssb_fill_sprom_with_fallback(bus, &iv->sprom); + + if (bcm47xx_nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) + iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10); + + return 0; +} diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 5d1e9a0fc389..cde5ff7529eb 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -762,15 +762,14 @@ EXPORT_SYMBOL(ssb_bus_sdiobus_register); #endif /* CONFIG_SSB_PCMCIAHOST */ #ifdef CONFIG_SSB_HOST_SOC -int ssb_bus_ssbbus_register(struct ssb_bus *bus, unsigned long baseaddr, - ssb_invariants_func_t get_invariants) +int ssb_bus_host_soc_register(struct ssb_bus *bus, unsigned long baseaddr) { int err; bus->bustype = SSB_BUSTYPE_SSB; bus->ops = &ssb_host_soc_ops; - err = ssb_bus_register(bus, get_invariants, baseaddr); + err = ssb_bus_register(bus, ssb_host_soc_get_invariants, baseaddr); if (!err) { ssb_info("Sonics Silicon Backplane found at address 0x%08lX\n", baseaddr); diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index 15bfd5c7d2d7..c2f5d3969c8b 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -163,6 +163,9 @@ static inline int ssb_sdio_init(struct ssb_bus *bus) #ifdef CONFIG_SSB_HOST_SOC extern const struct ssb_bus_ops ssb_host_soc_ops; + +extern int ssb_host_soc_get_invariants(struct ssb_bus *bus, + struct ssb_init_invariants *iv); #endif /* scan.c */ diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index c3d1a525bacc..26a0b3c3ce5f 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -524,13 +524,9 @@ struct ssb_init_invariants { typedef int (*ssb_invariants_func_t)(struct ssb_bus *bus, struct ssb_init_invariants *iv); -/* Register a SSB system bus. get_invariants() is called after the - * basic system devices are initialized. - * The invariants are usually fetched from some NVRAM. - * Put the invariants into the struct pointed to by iv. */ -extern int ssb_bus_ssbbus_register(struct ssb_bus *bus, - unsigned long baseaddr, - ssb_invariants_func_t get_invariants); +/* Register SoC bus. */ +extern int ssb_bus_host_soc_register(struct ssb_bus *bus, + unsigned long baseaddr); #ifdef CONFIG_SSB_PCIHOST extern int ssb_bus_pcibus_register(struct ssb_bus *bus, struct pci_dev *host_pci); -- cgit v1.2.3-59-g8ed1b From a8170d2b9e8d38a1f3fa3b40b6f8cd34a87d5382 Mon Sep 17 00:00:00 2001 From: "Singhai, Anjali" Date: Mon, 14 Dec 2015 12:21:17 -0800 Subject: geneve: Add geneve udp port offload for ethernet devices Add ndo_ops to add/del UDP ports to a device that supports geneve offload. v2: Comment fix. Signed-off-by: Anjali Singhai Jain Signed-off-by: Kiran Patil Signed-off-by: David S. Miller --- drivers/net/geneve.c | 23 +++++++++++++++++++++++ include/linux/netdevice.h | 20 +++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 0750d7a93878..89325e483ecf 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -380,8 +380,11 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6, static void geneve_notify_add_rx_port(struct geneve_sock *gs) { + struct net_device *dev; struct sock *sk = gs->sock->sk; + struct net *net = sock_net(sk); sa_family_t sa_family = sk->sk_family; + __be16 port = inet_sk(sk)->inet_sport; int err; if (sa_family == AF_INET) { @@ -390,6 +393,14 @@ static void geneve_notify_add_rx_port(struct geneve_sock *gs) pr_warn("geneve: udp_add_offload failed with status %d\n", err); } + + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + if (dev->netdev_ops->ndo_add_geneve_port) + dev->netdev_ops->ndo_add_geneve_port(dev, sa_family, + port); + } + rcu_read_unlock(); } static int geneve_hlen(struct genevehdr *gh) @@ -530,8 +541,20 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port, static void geneve_notify_del_rx_port(struct geneve_sock *gs) { + struct net_device *dev; struct sock *sk = gs->sock->sk; + struct net *net = sock_net(sk); sa_family_t sa_family = sk->sk_family; + __be16 port = inet_sk(sk)->inet_sport; + + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + if (dev->netdev_ops->ndo_del_geneve_port) + dev->netdev_ops->ndo_del_geneve_port(dev, sa_family, + port); + } + + rcu_read_unlock(); if (sa_family == AF_INET) udp_del_offload(&gs->udp_offloads); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9fb6395967de..81b26a543a3c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1013,6 +1013,19 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, * a new port starts listening. The operation is protected by the * vxlan_net->sock_lock. * + * void (*ndo_add_geneve_port)(struct net_device *dev, + * sa_family_t sa_family, __be16 port); + * Called by geneve to notify a driver about the UDP port and socket + * address family that geneve is listnening to. It is called only when + * a new port starts listening. The operation is protected by the + * geneve_net->sock_lock. + * + * void (*ndo_del_geneve_port)(struct net_device *dev, + * sa_family_t sa_family, __be16 port); + * Called by geneve to notify the driver about a UDP port and socket + * address family that geneve is not listening to anymore. The operation + * is protected by the geneve_net->sock_lock. + * * void (*ndo_del_vxlan_port)(struct net_device *dev, * sa_family_t sa_family, __be16 port); * Called by vxlan to notify the driver about a UDP port and socket @@ -1217,7 +1230,12 @@ struct net_device_ops { void (*ndo_del_vxlan_port)(struct net_device *dev, sa_family_t sa_family, __be16 port); - + void (*ndo_add_geneve_port)(struct net_device *dev, + sa_family_t sa_family, + __be16 port); + void (*ndo_del_geneve_port)(struct net_device *dev, + sa_family_t sa_family, + __be16 port); void* (*ndo_dfwd_add_station)(struct net_device *pdev, struct net_device *dev); void (*ndo_dfwd_del_station)(struct net_device *pdev, -- cgit v1.2.3-59-g8ed1b From 6a899024058d35dbcac33fbd3c7d70f2a54828e1 Mon Sep 17 00:00:00 2001 From: "Singhai, Anjali" Date: Mon, 14 Dec 2015 12:21:18 -0800 Subject: i40e: geneve tunnel offload support This patch adds driver hooks to implement ndo_ops to add/del udp port in the HW to identify GENEVE tunnels. Signed-off-by: Anjali Singhai Jain Signed-off-by: Kiran Patil Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/i40e/i40e.h | 16 +-- drivers/net/ethernet/intel/i40e/i40e_main.c | 167 ++++++++++++++++++++++------ drivers/net/ethernet/intel/i40e/i40e_txrx.c | 8 +- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 2 +- 4 files changed, 150 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index b7bc014ae00b..c202f9b9386a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -245,6 +245,11 @@ struct i40e_tc_configuration { struct i40e_tc_info tc_info[I40E_MAX_TRAFFIC_CLASS]; }; +struct i40e_udp_port_config { + __be16 index; + u8 type; +}; + /* struct that defines the Ethernet device */ struct i40e_pf { struct pci_dev *pdev; @@ -281,11 +286,9 @@ struct i40e_pf { u32 fd_atr_cnt; u32 fd_tcp_rule; -#ifdef CONFIG_I40E_VXLAN - __be16 vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS]; - u16 pending_vxlan_bitmap; + struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS]; + u16 pending_udp_bitmap; -#endif enum i40e_interrupt_policy int_policy; u16 rx_itr_default; u16 tx_itr_default; @@ -322,9 +325,7 @@ struct i40e_pf { #define I40E_FLAG_FD_ATR_ENABLED BIT_ULL(22) #define I40E_FLAG_PTP BIT_ULL(25) #define I40E_FLAG_MFP_ENABLED BIT_ULL(26) -#ifdef CONFIG_I40E_VXLAN -#define I40E_FLAG_VXLAN_FILTER_SYNC BIT_ULL(27) -#endif +#define I40E_FLAG_UDP_FILTER_SYNC BIT_ULL(27) #define I40E_FLAG_PORT_ID_VALID BIT_ULL(28) #define I40E_FLAG_DCB_CAPABLE BIT_ULL(29) #define I40E_FLAG_RSS_AQ_CAPABLE BIT_ULL(31) @@ -336,6 +337,7 @@ struct i40e_pf { #define I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(38) #define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(39) #define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40) +#define I40E_FLAG_GENEVE_OFFLOAD_CAPABLE BIT_ULL(41) #define I40E_FLAG_NO_PCI_LINK_CHECK BIT_ULL(42) /* tracks features that get auto disabled by errors */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c284e4341c7c..c69b1bb8f2a4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -27,9 +27,12 @@ /* Local includes */ #include "i40e.h" #include "i40e_diag.h" -#ifdef CONFIG_I40E_VXLAN +#if IS_ENABLED(CONFIG_VXLAN) #include #endif +#if IS_ENABLED(CONFIG_GENEVE) +#include +#endif const char i40e_driver_name[] = "i40e"; static const char i40e_driver_string[] = @@ -7036,30 +7039,30 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf) i40e_flush(hw); } -#ifdef CONFIG_I40E_VXLAN /** - * i40e_sync_vxlan_filters_subtask - Sync the VSI filter list with HW + * i40e_sync_udp_filters_subtask - Sync the VSI filter list with HW * @pf: board private structure **/ -static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf) +static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf) { +#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE) struct i40e_hw *hw = &pf->hw; i40e_status ret; __be16 port; int i; - if (!(pf->flags & I40E_FLAG_VXLAN_FILTER_SYNC)) + if (!(pf->flags & I40E_FLAG_UDP_FILTER_SYNC)) return; - pf->flags &= ~I40E_FLAG_VXLAN_FILTER_SYNC; + pf->flags &= ~I40E_FLAG_UDP_FILTER_SYNC; for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { - if (pf->pending_vxlan_bitmap & BIT_ULL(i)) { - pf->pending_vxlan_bitmap &= ~BIT_ULL(i); - port = pf->vxlan_ports[i]; + if (pf->pending_udp_bitmap & BIT_ULL(i)) { + pf->pending_udp_bitmap &= ~BIT_ULL(i); + port = pf->udp_ports[i].index; if (port) ret = i40e_aq_add_udp_tunnel(hw, ntohs(port), - I40E_AQC_TUNNEL_TYPE_VXLAN, + pf->udp_ports[i].type, NULL, NULL); else ret = i40e_aq_del_udp_tunnel(hw, i, NULL); @@ -7072,13 +7075,13 @@ static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf) i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - pf->vxlan_ports[i] = 0; + pf->udp_ports[i].index = 0; } } } +#endif } -#endif /** * i40e_service_task - Run the driver's async subtasks * @work: pointer to work_struct containing our data @@ -7103,8 +7106,8 @@ static void i40e_service_task(struct work_struct *work) i40e_watchdog_subtask(pf); i40e_fdir_reinit_subtask(pf); i40e_sync_filters_subtask(pf); -#ifdef CONFIG_I40E_VXLAN - i40e_sync_vxlan_filters_subtask(pf); +#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE) + i40e_sync_udp_filters_subtask(pf); #endif i40e_clean_adminq_subtask(pf); @@ -8380,7 +8383,8 @@ static int i40e_sw_init(struct i40e_pf *pf) I40E_FLAG_HW_ATR_EVICT_CAPABLE | I40E_FLAG_OUTER_UDP_CSUM_CAPABLE | I40E_FLAG_WB_ON_ITR_CAPABLE | - I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE; + I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE | + I40E_FLAG_GENEVE_OFFLOAD_CAPABLE; } pf->eeprom_version = 0xDEAD; pf->lan_veb = I40E_NO_VEB; @@ -8479,26 +8483,27 @@ static int i40e_set_features(struct net_device *netdev, return 0; } -#ifdef CONFIG_I40E_VXLAN +#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE) /** - * i40e_get_vxlan_port_idx - Lookup a possibly offloaded for Rx UDP port + * i40e_get_udp_port_idx - Lookup a possibly offloaded for Rx UDP port * @pf: board private structure * @port: The UDP port to look up * * Returns the index number or I40E_MAX_PF_UDP_OFFLOAD_PORTS if port not found **/ -static u8 i40e_get_vxlan_port_idx(struct i40e_pf *pf, __be16 port) +static u8 i40e_get_udp_port_idx(struct i40e_pf *pf, __be16 port) { u8 i; for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { - if (pf->vxlan_ports[i] == port) + if (pf->udp_ports[i].index == port) return i; } return i; } +#endif /** * i40e_add_vxlan_port - Get notifications about VXLAN ports that come up * @netdev: This physical port's netdev @@ -8508,6 +8513,7 @@ static u8 i40e_get_vxlan_port_idx(struct i40e_pf *pf, __be16 port) static void i40e_add_vxlan_port(struct net_device *netdev, sa_family_t sa_family, __be16 port) { +#if IS_ENABLED(CONFIG_VXLAN) struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; @@ -8517,7 +8523,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev, if (sa_family == AF_INET6) return; - idx = i40e_get_vxlan_port_idx(pf, port); + idx = i40e_get_udp_port_idx(pf, port); /* Check if port already exists */ if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) { @@ -8527,7 +8533,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev, } /* Now check if there is space to add the new port */ - next_idx = i40e_get_vxlan_port_idx(pf, 0); + next_idx = i40e_get_udp_port_idx(pf, 0); if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) { netdev_info(netdev, "maximum number of vxlan UDP ports reached, not adding port %d\n", @@ -8536,9 +8542,11 @@ static void i40e_add_vxlan_port(struct net_device *netdev, } /* New port: add it and mark its index in the bitmap */ - pf->vxlan_ports[next_idx] = port; - pf->pending_vxlan_bitmap |= BIT_ULL(next_idx); - pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC; + pf->udp_ports[next_idx].index = port; + pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_VXLAN; + pf->pending_udp_bitmap |= BIT_ULL(next_idx); + pf->flags |= I40E_FLAG_UDP_FILTER_SYNC; +#endif } /** @@ -8550,6 +8558,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev, static void i40e_del_vxlan_port(struct net_device *netdev, sa_family_t sa_family, __be16 port) { +#if IS_ENABLED(CONFIG_VXLAN) struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; @@ -8558,23 +8567,108 @@ static void i40e_del_vxlan_port(struct net_device *netdev, if (sa_family == AF_INET6) return; - idx = i40e_get_vxlan_port_idx(pf, port); + idx = i40e_get_udp_port_idx(pf, port); /* Check if port already exists */ if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) { /* if port exists, set it to 0 (mark for deletion) * and make it pending */ - pf->vxlan_ports[idx] = 0; - pf->pending_vxlan_bitmap |= BIT_ULL(idx); - pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC; + pf->udp_ports[idx].index = 0; + pf->pending_udp_bitmap |= BIT_ULL(idx); + pf->flags |= I40E_FLAG_UDP_FILTER_SYNC; } else { netdev_warn(netdev, "vxlan port %d was not found, not deleting\n", ntohs(port)); } +#endif +} + +/** + * i40e_add_geneve_port - Get notifications about GENEVE ports that come up + * @netdev: This physical port's netdev + * @sa_family: Socket Family that GENEVE is notifying us about + * @port: New UDP port number that GENEVE started listening to + **/ +static void i40e_add_geneve_port(struct net_device *netdev, + sa_family_t sa_family, __be16 port) +{ +#if IS_ENABLED(CONFIG_GENEVE) + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + u8 next_idx; + u8 idx; + + if (sa_family == AF_INET6) + return; + + idx = i40e_get_udp_port_idx(pf, port); + + /* Check if port already exists */ + if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) { + netdev_info(netdev, "udp port %d already offloaded\n", + ntohs(port)); + return; + } + + /* Now check if there is space to add the new port */ + next_idx = i40e_get_udp_port_idx(pf, 0); + + if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) { + netdev_info(netdev, "maximum number of UDP ports reached, not adding port %d\n", + ntohs(port)); + return; + } + + /* New port: add it and mark its index in the bitmap */ + pf->udp_ports[next_idx].index = port; + pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_NGE; + pf->pending_udp_bitmap |= BIT_ULL(next_idx); + pf->flags |= I40E_FLAG_UDP_FILTER_SYNC; + + dev_info(&pf->pdev->dev, "adding geneve port %d\n", ntohs(port)); +#endif } +/** + * i40e_del_geneve_port - Get notifications about GENEVE ports that go away + * @netdev: This physical port's netdev + * @sa_family: Socket Family that GENEVE is notifying us about + * @port: UDP port number that GENEVE stopped listening to + **/ +static void i40e_del_geneve_port(struct net_device *netdev, + sa_family_t sa_family, __be16 port) +{ +#if IS_ENABLED(CONFIG_GENEVE) + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + u8 idx; + + if (sa_family == AF_INET6) + return; + + idx = i40e_get_udp_port_idx(pf, port); + + /* Check if port already exists */ + if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) { + /* if port exists, set it to 0 (mark for deletion) + * and make it pending + */ + pf->udp_ports[idx].index = 0; + pf->pending_udp_bitmap |= BIT_ULL(idx); + pf->flags |= I40E_FLAG_UDP_FILTER_SYNC; + + dev_info(&pf->pdev->dev, "deleting geneve port %d\n", + ntohs(port)); + } else { + netdev_warn(netdev, "geneve port %d was not found, not deleting\n", + ntohs(port)); + } #endif +} + static int i40e_get_phys_port_id(struct net_device *netdev, struct netdev_phys_item_id *ppid) { @@ -8752,7 +8846,10 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, nlflags, 0, 0, filter_mask, NULL); } -#define I40E_MAX_TUNNEL_HDR_LEN 80 +/* Hardware supports L4 tunnel length of 128B (=2^7) which includes + * inner mac plus all inner ethertypes. + */ +#define I40E_MAX_TUNNEL_HDR_LEN 128 /** * i40e_features_check - Validate encapsulated packet conforms to limits * @skb: skb buff @@ -8764,7 +8861,7 @@ static netdev_features_t i40e_features_check(struct sk_buff *skb, netdev_features_t features) { if (skb->encapsulation && - (skb_inner_mac_header(skb) - skb_transport_header(skb) > + ((skb_inner_network_header(skb) - skb_transport_header(skb)) > I40E_MAX_TUNNEL_HDR_LEN)) return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); @@ -8799,9 +8896,13 @@ static const struct net_device_ops i40e_netdev_ops = { .ndo_get_vf_config = i40e_ndo_get_vf_config, .ndo_set_vf_link_state = i40e_ndo_set_vf_link_state, .ndo_set_vf_spoofchk = i40e_ndo_set_vf_spoofchk, -#ifdef CONFIG_I40E_VXLAN +#if IS_ENABLED(CONFIG_VXLAN) .ndo_add_vxlan_port = i40e_add_vxlan_port, .ndo_del_vxlan_port = i40e_del_vxlan_port, +#endif +#if IS_ENABLED(CONFIG_GENEVE) + .ndo_add_geneve_port = i40e_add_geneve_port, + .ndo_del_geneve_port = i40e_del_geneve_port, #endif .ndo_get_phys_port_id = i40e_get_phys_port_id, .ndo_fdb_add = i40e_ndo_fdb_add, @@ -8836,6 +8937,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) np->vsi = vsi; netdev->hw_enc_features |= NETIF_F_IP_CSUM | + NETIF_F_RXCSUM | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | NETIF_F_TSO; @@ -10348,6 +10450,9 @@ static void i40e_print_features(struct i40e_pf *pf) i += snprintf(&buf[i], REMAIN(i), " DCB"); #if IS_ENABLED(CONFIG_VXLAN) i += snprintf(&buf[i], REMAIN(i), " VxLAN"); +#endif +#if IS_ENABLED(CONFIG_GENEVE) + i += snprintf(&buf[i], REMAIN(i), " Geneve"); #endif if (pf->flags & I40E_FLAG_PTP) i += snprintf(&buf[i], REMAIN(i), " PTP"); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index b0ae3e695783..e9e9a37ee274 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1380,7 +1380,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT)) return; - /* If VXLAN traffic has an outer UDPv4 checksum we need to check + /* If VXLAN/GENEVE traffic has an outer UDPv4 checksum we need to check * it in the driver, hardware does not do it for us. * Since L3L4P bit was set we assume a valid IHL value (>=5) * so the total length of IPv4 header is IHL*4 bytes @@ -2001,7 +2001,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, if (!(tx_flags & (I40E_TX_FLAGS_IPV4 | I40E_TX_FLAGS_IPV6))) return; - if (!(tx_flags & I40E_TX_FLAGS_VXLAN_TUNNEL)) { + if (!(tx_flags & I40E_TX_FLAGS_UDP_TUNNEL)) { /* snag network header to get L4 type and address */ hdr.network = skb_network_header(skb); @@ -2086,7 +2086,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT; dtype_cmd |= I40E_TXD_FLTR_QW1_CNT_ENA_MASK; - if (!(tx_flags & I40E_TX_FLAGS_VXLAN_TUNNEL)) + if (!(tx_flags & I40E_TX_FLAGS_UDP_TUNNEL)) dtype_cmd |= ((u32)I40E_FD_ATR_STAT_IDX(pf->hw.pf_id) << I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) & @@ -2319,7 +2319,7 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, oudph = udp_hdr(skb); oiph = ip_hdr(skb); l4_tunnel = I40E_TXD_CTX_UDP_TUNNELING; - *tx_flags |= I40E_TX_FLAGS_VXLAN_TUNNEL; + *tx_flags |= I40E_TX_FLAGS_UDP_TUNNEL; break; case IPPROTO_GRE: l4_tunnel = I40E_TXD_CTX_GRE_TUNNELING; diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index dccc1eb576f2..3f081e25e097 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -163,7 +163,7 @@ enum i40e_dyn_idx_t { #define I40E_TX_FLAGS_FSO BIT(7) #define I40E_TX_FLAGS_TSYN BIT(8) #define I40E_TX_FLAGS_FD_SB BIT(9) -#define I40E_TX_FLAGS_VXLAN_TUNNEL BIT(10) +#define I40E_TX_FLAGS_UDP_TUNNEL BIT(10) #define I40E_TX_FLAGS_VLAN_MASK 0xffff0000 #define I40E_TX_FLAGS_VLAN_PRIO_MASK 0xe0000000 #define I40E_TX_FLAGS_VLAN_PRIO_SHIFT 29 -- cgit v1.2.3-59-g8ed1b From c110c311b195c54ce75bc2e85bbebd88d566d86c Mon Sep 17 00:00:00 2001 From: "Singhai, Anjali" Date: Mon, 14 Dec 2015 12:21:19 -0800 Subject: i40e: Kernel dependency update for i40e to support geneve offload Update the Kconfig file with dependency for supporting GENEVE tunnel offloads. Signed-off-by: Anjali Singhai Jain Signed-off-by: Kiran Patil Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/Kconfig | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig index 4163b16489b3..fa593dd3efe1 100644 --- a/drivers/net/ethernet/intel/Kconfig +++ b/drivers/net/ethernet/intel/Kconfig @@ -280,6 +280,16 @@ config I40E_VXLAN Say Y here if you want to use Virtual eXtensible Local Area Network (VXLAN) in the driver. +config I40E_GENEVE + bool "Generic Network Virtualization Encapsulation (GENEVE) Support" + depends on I40E && GENEVE && !(I40E=y && GENEVE=m) + default n + ---help--- + This allows one to create GENEVE virtual interfaces that provide + Layer 2 Networks over Layer 3 Networks. GENEVE is often used + to tunnel virtual network infrastructure in virtualized environments. + Say Y here if you want to use GENEVE in the driver. + config I40E_DCB bool "Data Center Bridging (DCB) Support" default n -- cgit v1.2.3-59-g8ed1b From 05ca4029b25c65c860be5baa9288ec8f7e0a97e6 Mon Sep 17 00:00:00 2001 From: "Singhai, Anjali" Date: Mon, 14 Dec 2015 12:21:20 -0800 Subject: geneve: Add geneve_get_rx_port support This patch adds an op that the drivers can call into to get existing geneve ports. Signed-off-by: Anjali Singhai Jain Signed-off-by: David S. Miller --- drivers/net/geneve.c | 24 ++++++++++++++++++++++++ include/net/geneve.h | 8 ++++++++ 2 files changed, 32 insertions(+) (limited to 'drivers') diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 89325e483ecf..31b19fdf659d 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1109,6 +1109,30 @@ static struct device_type geneve_type = { .name = "geneve", }; +/* Calls the ndo_add_geneve_port of the caller in order to + * supply the listening GENEVE udp ports. Callers are expected + * to implement the ndo_add_geneve_port. + */ +void geneve_get_rx_port(struct net_device *dev) +{ + struct net *net = dev_net(dev); + struct geneve_net *gn = net_generic(net, geneve_net_id); + struct geneve_sock *gs; + sa_family_t sa_family; + struct sock *sk; + __be16 port; + + rcu_read_lock(); + list_for_each_entry_rcu(gs, &gn->sock_list, list) { + sk = gs->sock->sk; + sa_family = sk->sk_family; + port = inet_sk(sk)->inet_sport; + dev->netdev_ops->ndo_add_geneve_port(dev, sa_family, port); + } + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(geneve_get_rx_port); + /* Initialize the device structure. */ static void geneve_setup(struct net_device *dev) { diff --git a/include/net/geneve.h b/include/net/geneve.h index 3106ed6eae0d..e6c23dc765f7 100644 --- a/include/net/geneve.h +++ b/include/net/geneve.h @@ -62,6 +62,14 @@ struct genevehdr { struct geneve_opt options[]; }; +#if IS_ENABLED(CONFIG_GENEVE) +void geneve_get_rx_port(struct net_device *netdev); +#else +static inline void geneve_get_rx_port(struct net_device *netdev) +{ +} +#endif + #ifdef CONFIG_INET struct net_device *geneve_dev_create_fb(struct net *net, const char *name, u8 name_assign_type, u16 dst_port); -- cgit v1.2.3-59-g8ed1b From cd866606c91b1e99517fd866de0049276f011ea7 Mon Sep 17 00:00:00 2001 From: "Singhai, Anjali" Date: Mon, 14 Dec 2015 12:21:21 -0800 Subject: i40e: Call geneve_get_rx_port to get the existing Geneve ports This patch adds a call to geneve_get_rx_port in i40e so that when it comes up it can learn about the existing geneve tunnels. Signed-off-by: Anjali Singhai Jain Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/i40e/i40e_main.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c69b1bb8f2a4..23211e08eecb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5339,6 +5339,9 @@ int i40e_open(struct net_device *netdev) #ifdef CONFIG_I40E_VXLAN vxlan_get_rx_port(netdev); #endif +#ifdef CONFIG_I40E_GENEVE + geneve_get_rx_port(netdev); +#endif return 0; } -- cgit v1.2.3-59-g8ed1b From 1bd4978a88ac2589f3105f599b1d404a312fb7f6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 16 Dec 2015 08:57:37 -0800 Subject: tun: honor IFF_UP in tun_get_user() If a tun interface is turned down, we should not allow packet injection into the kernel. Kernel does not send packets to the tun already. TUNATTACHFILTER can not be used as only tun_net_xmit() is taking care of it. Reported-by: Curt Wohlgemuth Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/tun.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index f0db770e8b2f..88bb8cc3555b 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1095,6 +1095,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, u32 rxhash; ssize_t n; + if (!(tun->dev->flags & IFF_UP)) + return -EIO; + if (!(tun->flags & IFF_NO_PI)) { if (len < sizeof(pi)) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 301c141d6a5ddacb8b2310a87a7e8fe997d17a39 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 16 Dec 2015 19:08:52 +0000 Subject: nfp: clear ring delayed kick counters We need to clear delayed kick counters when we free rings otherwise after ndo_close()/ndo_open() we could kick HW by more entries than actually written to rings. Signed-off-by: Jakub Kicinski Reviewed-by: Rolf Neugebauer Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 7060539d276a..6c5af4cb5bdc 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1363,6 +1363,7 @@ static void nfp_net_tx_ring_free(struct nfp_net_tx_ring *tx_ring) tx_ring->wr_p = 0; tx_ring->rd_p = 0; tx_ring->qcp_rd_p = 0; + tx_ring->wr_ptr_add = 0; tx_ring->txbufs = NULL; tx_ring->txds = NULL; @@ -1437,6 +1438,7 @@ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring) rx_ring->cnt = 0; rx_ring->wr_p = 0; rx_ring->rd_p = 0; + rx_ring->wr_ptr_add = 0; rx_ring->rxbufs = NULL; rx_ring->rxds = NULL; -- cgit v1.2.3-59-g8ed1b From e41e282428ee897b8817049e6ee0b7d5c9caf005 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 17 Dec 2015 13:45:07 +0530 Subject: cxgb4: Use symbolic constant for VLAN priority calculation Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/l2t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index ac27898c6ab0..7234b82eb833 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -66,7 +66,7 @@ struct l2t_data { static inline unsigned int vlan_prio(const struct l2t_entry *e) { - return e->vlan >> 13; + return e->vlan >> VLAN_PRIO_SHIFT; } static inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e) -- cgit v1.2.3-59-g8ed1b From f7502659cec8f2165805e967e113c0894b931181 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 17 Dec 2015 13:45:08 +0530 Subject: cxgb4: Add API to alloc l2t entry; also update existing ones Based on original work by Kumar Sanghvi Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 10 +- drivers/net/ethernet/chelsio/cxgb4/l2t.c | 125 +++++++++++++++++++++--- drivers/net/ethernet/chelsio/cxgb4/l2t.h | 7 +- 3 files changed, 117 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index edd706e739fb..8490c845a815 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -1181,15 +1181,9 @@ static int set_filter_wr(struct adapter *adapter, int fidx) */ if (f->fs.newdmac || f->fs.newvlan) { /* allocate L2T entry for new filter */ - f->l2t = t4_l2t_alloc_switching(adapter->l2t); + f->l2t = t4_l2t_alloc_switching(adapter, f->fs.vlan, + f->fs.eport, f->fs.dmac); if (f->l2t == NULL) { - kfree_skb(skb); - return -EAGAIN; - } - if (t4_l2t_set_switching(adapter, f->l2t, f->fs.vlan, - f->fs.eport, f->fs.dmac)) { - cxgb4_l2t_release(f->l2t); - f->l2t = NULL; kfree_skb(skb); return -ENOMEM; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index 7234b82eb833..d1b3b3a132a4 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -305,9 +305,82 @@ found: return e; } -/* - * Called when an L2T entry has no more users. +static struct l2t_entry *find_or_alloc_l2e(struct l2t_data *d, u16 vlan, + u8 port, u8 *dmac) +{ + struct l2t_entry *end, *e, **p; + struct l2t_entry *first_free = NULL; + + for (e = &d->l2tab[0], end = &d->l2tab[d->l2t_size]; e != end; ++e) { + if (atomic_read(&e->refcnt) == 0) { + if (!first_free) + first_free = e; + } else { + if (e->state == L2T_STATE_SWITCHING) { + if (ether_addr_equal(e->dmac, dmac) && + (e->vlan == vlan) && (e->lport == port)) + goto exists; + } + } + } + + if (first_free) { + e = first_free; + goto found; + } + + return NULL; + +found: + /* The entry we found may be an inactive entry that is + * presently in the hash table. We need to remove it. + */ + if (e->state < L2T_STATE_SWITCHING) + for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next) + if (*p == e) { + *p = e->next; + e->next = NULL; + break; + } + e->state = L2T_STATE_UNUSED; + +exists: + return e; +} + +/* Called when an L2T entry has no more users. The entry is left in the hash + * table since it is likely to be reused but we also bump nfree to indicate + * that the entry can be reallocated for a different neighbor. We also drop + * the existing neighbor reference in case the neighbor is going away and is + * waiting on our reference. + * + * Because entries can be reallocated to other neighbors once their ref count + * drops to 0 we need to take the entry's lock to avoid races with a new + * incarnation. */ +static void _t4_l2e_free(struct l2t_entry *e) +{ + struct l2t_data *d; + + if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */ + if (e->neigh) { + neigh_release(e->neigh); + e->neigh = NULL; + } + while (e->arpq_head) { + struct sk_buff *skb = e->arpq_head; + + e->arpq_head = skb->next; + kfree_skb(skb); + } + e->arpq_tail = NULL; + } + + d = container_of(e, struct l2t_data, l2tab[e->idx]); + atomic_inc(&d->nfree); +} + +/* Locked version of _t4_l2e_free */ static void t4_l2e_free(struct l2t_entry *e) { struct l2t_data *d; @@ -529,33 +602,57 @@ void t4_l2t_update(struct adapter *adap, struct neighbour *neigh) * explicitly freed and while busy they are not on any hash chain, so normal * address resolution updates do not see them. */ -struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *d) +struct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan, + u8 port, u8 *eth_addr) { + struct l2t_data *d = adap->l2t; struct l2t_entry *e; + int ret; write_lock_bh(&d->lock); - e = alloc_l2e(d); + e = find_or_alloc_l2e(d, vlan, port, eth_addr); if (e) { spin_lock(&e->lock); /* avoid race with t4_l2t_free */ - e->state = L2T_STATE_SWITCHING; - atomic_set(&e->refcnt, 1); + if (!atomic_read(&e->refcnt)) { + e->state = L2T_STATE_SWITCHING; + e->vlan = vlan; + e->lport = port; + ether_addr_copy(e->dmac, eth_addr); + atomic_set(&e->refcnt, 1); + ret = write_l2e(adap, e, 0); + if (ret < 0) { + _t4_l2e_free(e); + spin_unlock(&e->lock); + write_unlock_bh(&d->lock); + return NULL; + } + } else { + atomic_inc(&e->refcnt); + } + spin_unlock(&e->lock); } write_unlock_bh(&d->lock); return e; } -/* Sets/updates the contents of a switching L2T entry that has been allocated - * with an earlier call to @t4_l2t_alloc_switching. +/** + * @dev: net_device pointer + * @vlan: VLAN Id + * @port: Associated port + * @dmac: Destination MAC address to add to L2T + * Returns pointer to the allocated l2t entry + * + * Allocates an L2T entry for use by switching rule of a filter */ -int t4_l2t_set_switching(struct adapter *adap, struct l2t_entry *e, u16 vlan, - u8 port, u8 *eth_addr) +struct l2t_entry *cxgb4_l2t_alloc_switching(struct net_device *dev, u16 vlan, + u8 port, u8 *dmac) { - e->vlan = vlan; - e->lport = port; - memcpy(e->dmac, eth_addr, ETH_ALEN); - return write_l2e(adap, e, 0); + struct adapter *adap = netdev2adap(dev); + + return t4_l2t_alloc_switching(adap, vlan, port, dmac); } +EXPORT_SYMBOL(cxgb4_l2t_alloc_switching); struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end) { diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.h b/drivers/net/ethernet/chelsio/cxgb4/l2t.h index b38dc526aad5..17803a871ae2 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.h +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.h @@ -114,10 +114,11 @@ struct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh, unsigned int priority); u64 cxgb4_select_ntuple(struct net_device *dev, const struct l2t_entry *l2t); +struct l2t_entry *cxgb4_l2t_alloc_switching(struct net_device *dev, u16 vlan, + u8 port, u8 *dmac); void t4_l2t_update(struct adapter *adap, struct neighbour *neigh); -struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *d); -int t4_l2t_set_switching(struct adapter *adap, struct l2t_entry *e, u16 vlan, - u8 port, u8 *eth_addr); +struct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan, + u8 port, u8 *dmac); struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end); void do_l2t_write_rpl(struct adapter *p, const struct cpl_l2t_write_rpl *rpl); -- cgit v1.2.3-59-g8ed1b From 9baeb9d7d8f61f7786416e4f001d738750d2aa24 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 17 Dec 2015 13:45:09 +0530 Subject: cxgb4: Use t4_mgmt_tx() API for sending write l2t request ctrl packets. Based on original work by Michael Werner Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/l2t.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index d1b3b3a132a4..81af78ee2d7e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -161,8 +161,7 @@ static int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync) memcpy(e->dmac, e->neigh->ha, sizeof(e->dmac)); memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac)); - set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0); - t4_ofld_send(adap, skb); + t4_mgmt_tx(adap, skb); if (sync && e->state != L2T_STATE_SWITCHING) e->state = L2T_STATE_SYNC_WRITE; -- cgit v1.2.3-59-g8ed1b From 749cb5fe48bb29f55f2cdde4d76183340e11547e Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 17 Dec 2015 13:45:10 +0530 Subject: cxgb4: Replace arpq_head/arpq_tail with SKB double link-list code Based on original work by Michael Werner Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/l2t.c | 61 ++++++++++++-------------------- drivers/net/ethernet/chelsio/cxgb4/l2t.h | 3 +- 2 files changed, 24 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index 81af78ee2d7e..5b0f3ef348e9 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -174,14 +174,10 @@ static int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync) */ static void send_pending(struct adapter *adap, struct l2t_entry *e) { - while (e->arpq_head) { - struct sk_buff *skb = e->arpq_head; + struct sk_buff *skb; - e->arpq_head = skb->next; - skb->next = NULL; + while ((skb = __skb_dequeue(&e->arpq)) != NULL) t4_ofld_send(adap, skb); - } - e->arpq_tail = NULL; } /* @@ -221,12 +217,7 @@ void do_l2t_write_rpl(struct adapter *adap, const struct cpl_l2t_write_rpl *rpl) */ static inline void arpq_enqueue(struct l2t_entry *e, struct sk_buff *skb) { - skb->next = NULL; - if (e->arpq_head) - e->arpq_tail->next = skb; - else - e->arpq_head = skb; - e->arpq_tail = skb; + __skb_queue_tail(&e->arpq, skb); } int cxgb4_l2t_send(struct net_device *dev, struct sk_buff *skb, @@ -258,7 +249,8 @@ again: if (e->state == L2T_STATE_RESOLVING && !neigh_event_send(e->neigh, NULL)) { spin_lock_bh(&e->lock); - if (e->state == L2T_STATE_RESOLVING && e->arpq_head) + if (e->state == L2T_STATE_RESOLVING && + !skb_queue_empty(&e->arpq)) write_l2e(adap, e, 1); spin_unlock_bh(&e->lock); } @@ -360,19 +352,15 @@ exists: static void _t4_l2e_free(struct l2t_entry *e) { struct l2t_data *d; + struct sk_buff *skb; if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */ if (e->neigh) { neigh_release(e->neigh); e->neigh = NULL; } - while (e->arpq_head) { - struct sk_buff *skb = e->arpq_head; - - e->arpq_head = skb->next; + while ((skb = __skb_dequeue(&e->arpq)) != NULL) kfree_skb(skb); - } - e->arpq_tail = NULL; } d = container_of(e, struct l2t_data, l2tab[e->idx]); @@ -383,6 +371,7 @@ static void _t4_l2e_free(struct l2t_entry *e) static void t4_l2e_free(struct l2t_entry *e) { struct l2t_data *d; + struct sk_buff *skb; spin_lock_bh(&e->lock); if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */ @@ -390,13 +379,8 @@ static void t4_l2e_free(struct l2t_entry *e) neigh_release(e->neigh); e->neigh = NULL; } - while (e->arpq_head) { - struct sk_buff *skb = e->arpq_head; - - e->arpq_head = skb->next; + while ((skb = __skb_dequeue(&e->arpq)) != NULL) kfree_skb(skb); - } - e->arpq_tail = NULL; } spin_unlock_bh(&e->lock); @@ -529,18 +513,19 @@ EXPORT_SYMBOL(cxgb4_select_ntuple); * on the arpq head. If a packet specifies a failure handler it is invoked, * otherwise the packet is sent to the device. */ -static void handle_failed_resolution(struct adapter *adap, struct sk_buff *arpq) +static void handle_failed_resolution(struct adapter *adap, struct l2t_entry *e) { - while (arpq) { - struct sk_buff *skb = arpq; + struct sk_buff *skb; + + while ((skb = __skb_dequeue(&e->arpq)) != NULL) { const struct l2t_skb_cb *cb = L2T_SKB_CB(skb); - arpq = skb->next; - skb->next = NULL; + spin_unlock(&e->lock); if (cb->arp_err_handler) cb->arp_err_handler(cb->handle, skb); else t4_ofld_send(adap, skb); + spin_lock(&e->lock); } } @@ -551,7 +536,7 @@ static void handle_failed_resolution(struct adapter *adap, struct sk_buff *arpq) void t4_l2t_update(struct adapter *adap, struct neighbour *neigh) { struct l2t_entry *e; - struct sk_buff *arpq = NULL; + struct sk_buff_head *arpq = NULL; struct l2t_data *d = adap->l2t; int addr_len = neigh->tbl->key_len; u32 *addr = (u32 *) neigh->primary_key; @@ -578,10 +563,9 @@ void t4_l2t_update(struct adapter *adap, struct neighbour *neigh) if (e->state == L2T_STATE_RESOLVING) { if (neigh->nud_state & NUD_FAILED) { - arpq = e->arpq_head; - e->arpq_head = e->arpq_tail = NULL; + arpq = &e->arpq; } else if ((neigh->nud_state & (NUD_CONNECTED | NUD_STALE)) && - e->arpq_head) { + !skb_queue_empty(&e->arpq)) { write_l2e(adap, e, 1); } } else { @@ -591,10 +575,9 @@ void t4_l2t_update(struct adapter *adap, struct neighbour *neigh) write_l2e(adap, e, 0); } - spin_unlock_bh(&e->lock); - if (arpq) - handle_failed_resolution(adap, arpq); + handle_failed_resolution(adap, e); + spin_unlock_bh(&e->lock); } /* Allocate an L2T entry for use by a switching rule. Such need to be @@ -681,6 +664,7 @@ struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end) d->l2tab[i].state = L2T_STATE_UNUSED; spin_lock_init(&d->l2tab[i].lock); atomic_set(&d->l2tab[i].refcnt, 0); + skb_queue_head_init(&d->l2tab[i].arpq); } return d; } @@ -715,7 +699,8 @@ static char l2e_state(const struct l2t_entry *e) case L2T_STATE_VALID: return 'V'; case L2T_STATE_STALE: return 'S'; case L2T_STATE_SYNC_WRITE: return 'W'; - case L2T_STATE_RESOLVING: return e->arpq_head ? 'A' : 'R'; + case L2T_STATE_RESOLVING: + return skb_queue_empty(&e->arpq) ? 'R' : 'A'; case L2T_STATE_SWITCHING: return 'X'; default: return 'U'; diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.h b/drivers/net/ethernet/chelsio/cxgb4/l2t.h index 17803a871ae2..4e2d47ac102b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.h +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.h @@ -76,8 +76,7 @@ struct l2t_entry { struct neighbour *neigh; /* associated neighbour */ struct l2t_entry *first; /* start of hash chain */ struct l2t_entry *next; /* next l2t_entry on chain */ - struct sk_buff *arpq_head; /* queue of packets awaiting resolution */ - struct sk_buff *arpq_tail; + struct sk_buff_head arpq; /* packet queue awaiting resolution */ spinlock_t lock; atomic_t refcnt; /* entry reference count */ u16 hash; /* hash bucket the entry is on */ -- cgit v1.2.3-59-g8ed1b From 270c499f0993a6b3a591eda6a5b96e873b1d84fa Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 17 Dec 2015 10:51:04 +0100 Subject: net/macb: Update device tree binding for resetting PHY using GPIO Instead of being at the MAC level the reset gpio preperty is moved at the PHY child node level. It is still managed by the MAC, but from the point of view of the binding it make more sense to be part of the PHY node. This commit also fixes a build errors if GPIOLIB is not selected. Signed-off-by: Gregory CLEMENT Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/macb.txt | 8 ++++++-- drivers/net/ethernet/cadence/macb.c | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt index 4a7fb6ccf298..38c8e845ac5d 100644 --- a/Documentation/devicetree/bindings/net/macb.txt +++ b/Documentation/devicetree/bindings/net/macb.txt @@ -19,8 +19,8 @@ Required properties: Optional elements: 'tx_clk' - clocks: Phandles to input clocks. -Optional properties: -- phy-reset-gpios : Should specify the gpio for phy reset +Optional properties for PHY child node: +- reset-gpios : Should specify the gpio for phy reset Examples: @@ -32,4 +32,8 @@ Examples: local-mac-address = [3a 0e 03 04 05 06]; clock-names = "pclk", "hclk", "tx_clk"; clocks = <&clkc 30>, <&clkc 30>, <&clkc 13>; + ethernet-phy@1 { + reg = <0x1>; + reset-gpios = <&pioE 6 1>; + }; }; diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 01236465c298..8b45bc9ac29e 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include @@ -2817,6 +2819,7 @@ static int macb_probe(struct platform_device *pdev) = macb_clk_init; int (*init)(struct platform_device *) = macb_init; struct device_node *np = pdev->dev.of_node; + struct device_node *phy_node; const struct macb_config *macb_config = NULL; struct clk *pclk, *hclk, *tx_clk; unsigned int queue_mask, num_queues; @@ -2905,8 +2908,14 @@ static int macb_probe(struct platform_device *pdev) macb_get_hwaddr(bp); /* Power up the PHY if there is a GPIO reset */ - bp->reset_gpio = devm_gpiod_get_optional(&bp->pdev->dev, "phy-reset", - GPIOD_OUT_HIGH); + phy_node = of_get_next_available_child(np, NULL); + if (phy_node) { + int gpio = of_get_named_gpio(phy_node, "reset-gpios", 0); + if (gpio_is_valid(gpio)) + bp->reset_gpio = gpio_to_desc(gpio); + gpiod_set_value(bp->reset_gpio, GPIOD_OUT_HIGH); + } + of_node_put(phy_node); err = of_get_phy_mode(np); if (err < 0) { @@ -2976,7 +2985,7 @@ static int macb_remove(struct platform_device *pdev) mdiobus_free(bp->mii_bus); /* Shutdown the PHY if there is a GPIO reset */ - gpiod_set_value(bp->reset_gpio, 0); + gpiod_set_value(bp->reset_gpio, GPIOD_OUT_LOW); unregister_netdev(dev); clk_disable_unprepare(bp->tx_clk); -- cgit v1.2.3-59-g8ed1b From 34a55d5e858e81a20d33fd9490149d6a1058be0c Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Thu, 17 Dec 2015 12:44:04 +0100 Subject: net: qmi_wwan: ignore bogus CDC Union descriptors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CDC descriptors found on these vendor specific functions should not be considered authoritative. They seem to be ignored by drivers for other systems, and the quality is therefore low. One device (1e0e:9001) has been reported to have such a bogus union descriptor on the QMI function, making it fail probing even if the device id was dynamically added. The report was not complete enough to allow adding a device entry for this modem. But this should at least fix the dynamic id probing problem. Reported-by: Kanerva Topi Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index babc84a3946c..d0b29733c021 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -374,7 +374,10 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) "bogus CDC Union: master=%u, slave=%u\n", cdc_union->bMasterInterface0, cdc_union->bSlaveInterface0); - goto err; + + /* ignore and continue... */ + cdc_union = NULL; + info->data = intf; } } -- cgit v1.2.3-59-g8ed1b From 3268e5cb494d8778a5a67a9fa2b1bdb0243b77ad Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Thu, 17 Dec 2015 16:11:55 +0200 Subject: team: Advertise tunneling offload features When the underlying device supports offloads encapulated traffic, we need to reflect that through the hw_enc_features field of the team net-device. This will cause the xmit path in the core networking stack to provide team with encapsulated GSO frames to offload into the HW etc. Using this over Mellanox ConnectX3-pro (mlx4 driver) card that supports VXLAN offloads we got 36.0 Gbits/sec using eight iperf streams. Signed-off-by: Eran Ben Elisha Signed-off-by: Jack Morgenstein Reviewed-by: Or Gerlitz Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 915f60fce186..2528331de193 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -985,10 +985,14 @@ static void team_port_disable(struct team *team, NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \ NETIF_F_HIGHDMA | NETIF_F_LRO) +#define TEAM_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \ + NETIF_F_RXCSUM | NETIF_F_ALL_TSO) + static void __team_compute_features(struct team *team) { struct team_port *port; u32 vlan_features = TEAM_VLAN_FEATURES & NETIF_F_ALL_FOR_ALL; + netdev_features_t enc_features = TEAM_ENC_FEATURES; unsigned short max_hard_header_len = ETH_HLEN; unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM; @@ -997,6 +1001,11 @@ static void __team_compute_features(struct team *team) vlan_features = netdev_increment_features(vlan_features, port->dev->vlan_features, TEAM_VLAN_FEATURES); + enc_features = + netdev_increment_features(enc_features, + port->dev->hw_enc_features, + TEAM_ENC_FEATURES); + dst_release_flag &= port->dev->priv_flags; if (port->dev->hard_header_len > max_hard_header_len) @@ -1004,6 +1013,7 @@ static void __team_compute_features(struct team *team) } team->dev->vlan_features = vlan_features; + team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL; team->dev->hard_header_len = max_hard_header_len; team->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; @@ -2091,6 +2101,7 @@ static void team_setup(struct net_device *dev) NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; + dev->hw_features |= NETIF_F_GSO_ENCAP_ALL; dev->features |= dev->hw_features; } -- cgit v1.2.3-59-g8ed1b From 4b402d71d304aa627111fb9d746bb0a75c3989b9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 17 Dec 2015 14:18:44 +0000 Subject: nfp: call netif_carrier_off() during init Netdevs default to carrier on, we should call netif_carrier_off() during initialization since we handle carrier state changes in the driver. Signed-off-by: Jakub Kicinski Reviewed-by: Rolf Neugebauer Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 6c5af4cb5bdc..43c618bafdb6 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2417,6 +2417,7 @@ int nfp_net_netdev_init(struct net_device *netdev) ether_setup(netdev); netdev->netdev_ops = &nfp_net_netdev_ops; netdev->watchdog_timeo = msecs_to_jiffies(5 * 1000); + netif_carrier_off(netdev); nfp_net_set_ethtool_ops(netdev); nfp_net_irqs_assign(netdev); -- cgit v1.2.3-59-g8ed1b From b70183db83552cf63cac51406aaf76a2cf5fca73 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 17 Dec 2015 17:51:16 -0800 Subject: asix: silence log message from oversize packet Since it is possible for an external system to send oversize packets at anytime, it is best for driver not to print a message and spam the log (potential external DoS). Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=109471 Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/usb/asix_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index bd9acff1eb7b..0c5c22b84da8 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -118,7 +118,7 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, return 0; } if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { - netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n", + netdev_dbg(dev->net, "asix_rx_fixup() Bad RX Length %d\n", size); return 0; } -- cgit v1.2.3-59-g8ed1b From 958d104e3d40eef5148c402887138f6594ff7e1e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 18 Dec 2015 15:18:08 +0100 Subject: netcp: fix regression in receive processing A cleanup patch I did was unfortunately wrong and introduced multiple serious bugs in the netcp rx processing, as indicated by these correct gcc warnings: drivers/net/ethernet/ti/netcp_core.c:776:14: warning: 'buf_ptr' may be used uninitialized in this function [-Wuninitialized] drivers/net/ethernet/ti/netcp_core.c:687:14: warning: 'ptr' may be used uninitialized in this function [-Wuninitialized] I have checked the patch once more and found that a call to get_pkt_info() accidentally got removed in netcp_free_rx_desc_chain, and netcp_process_one_rx_packet no longer retrieved the correct buffer length. This patch should fix all the known problems, but I did not test on real hardware. Signed-off-by: Arnd Bergmann Fixes: 899077791403 ("netcp: try to reduce type confusion in descriptors") Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/netcp_core.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 92d08eb262c2..c61d66d38634 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -582,6 +582,7 @@ static void netcp_free_rx_desc_chain(struct netcp_intf *netcp, unsigned int buf_len, dma_sz = sizeof(*ndesc); void *buf_ptr; u32 pad[2]; + u32 tmp; get_words(&dma_desc, 1, &desc->next_desc); @@ -591,6 +592,7 @@ static void netcp_free_rx_desc_chain(struct netcp_intf *netcp, dev_err(netcp->ndev_dev, "failed to unmap Rx desc\n"); break; } + get_pkt_info(&dma_buf, &tmp, &dma_desc, ndesc); get_pad_ptr(&buf_ptr, ndesc); dma_unmap_page(netcp->dev, dma_buf, PAGE_SIZE, DMA_FROM_DEVICE); __free_page(buf_ptr); @@ -637,6 +639,7 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp) dma_addr_t dma_desc, dma_buff; struct netcp_packet p_info; struct sk_buff *skb; + u32 pad[2]; void *org_buf_ptr; dma_desc = knav_queue_pop(netcp->rx_queue, &dma_sz); @@ -650,7 +653,8 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp) } get_pkt_info(&dma_buff, &buf_len, &dma_desc, desc); - get_pad_ptr(&org_buf_ptr, desc); + get_pad_info(&pad[0], &pad[1], &org_buf_len, desc); + org_buf_ptr = (void *)(uintptr_t)(pad[0] + ((u64)pad[1] << 32)); if (unlikely(!org_buf_ptr)) { dev_err(netcp->ndev_dev, "NULL bufptr in desc\n"); @@ -684,7 +688,7 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp) } get_pkt_info(&dma_buff, &buf_len, &dma_desc, ndesc); - get_pad_ptr(ptr, ndesc); + get_pad_ptr(&ptr, ndesc); page = ptr; if (likely(dma_buff && buf_len && page)) { @@ -773,7 +777,7 @@ static void netcp_free_rx_buf(struct netcp_intf *netcp, int fdq) } get_org_pkt_info(&dma, &buf_len, desc); - get_pad_ptr(buf_ptr, desc); + get_pad_ptr(&buf_ptr, desc); if (unlikely(!dma)) { dev_err(netcp->ndev_dev, "NULL orig_buff in desc\n"); -- cgit v1.2.3-59-g8ed1b From cb2f827795126979c6266f1c87dae584bd2fbb2d Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 17 Nov 2015 15:39:56 +0200 Subject: iwlwifi: change the Intel Wireless email address ilw@linux.intel.com is not available anymore. linuxwifi@intel.com should be used instead. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/agn.h | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/dev.h | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/led.h | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/main.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/rs.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/rs.h | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/rx.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/rxon.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/scan.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/sta.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/tt.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/tt.h | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/tx.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-7000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-8000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-csr.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-debug.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-debug.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-drv.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-modparams.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/constants.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/power.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/quota.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/rs.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tdls.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tof.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tof.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 2 +- 68 files changed, 69 insertions(+), 69 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h index 82c177aae580..9de277c6c420 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c index a114d3e42828..74c51615244e 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c @@ -22,7 +22,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/dev.h b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h index 0ba3e56d6015..1a7ead753eee 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.h b/drivers/net/wireless/intel/iwlwifi/dvm/led.h index 1c6b2252d0f2..75f74edd018f 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/led.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.h @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index f4c2529f7b94..29ea1c6705b4 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -22,7 +22,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index 9f6f564504ff..94bc66adef2f 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -23,7 +23,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c index cef921c1a623..ee7505537c96 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rs.h b/drivers/net/wireless/intel/iwlwifi/dvm/rs.h index f6bd25cad203..c5fe44584613 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.h @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c index 94b41e4e1dca..52ab1e012e8f 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c @@ -23,7 +23,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c index 85ceceb34fcc..2d47cb24c48b 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c @@ -20,7 +20,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/scan.c b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c index 648159495bbc..81a2ddbe9569 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c @@ -22,7 +22,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #include diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/sta.c b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c index 0fa67d3b7235..8e9768a553e4 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c @@ -22,7 +22,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c index c4736c8834c5..7decfc5677c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c @@ -22,7 +22,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h index 507726534b84..d324e9be9cbf 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h @@ -22,7 +22,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *****************************************************************************/ #ifndef __iwl_tt_setting_h__ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c index bddd19769035..cf0ad5f832e5 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c @@ -22,7 +22,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c index 390ee4d303db..fd9064bf389a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c @@ -27,7 +27,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c index 89a25cffb060..dee4458b408d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 6a4c0c236d10..f99048135fb9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index 543abeaffcf0..163b21bc20cb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.c b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c index 09feff4fa226..b1c3b0d0fcc6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-debug.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h index e8dbb24d993d..110333208450 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h @@ -21,7 +21,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h index eb4b99a1c8cd..22786d7dc00a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h @@ -20,7 +20,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c index 90987d6f348e..1d9dd153ef1c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h index b87acd6a229b..f4d3cd010087 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h @@ -19,7 +19,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 47a83abc557d..7acb49075683 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h index cda746b33db1..f6eacfdbc265 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE @@ -69,7 +69,7 @@ /* for all modules */ #define DRV_NAME "iwlwifi" #define DRV_COPYRIGHT "Copyright(c) 2003- 2015 Intel Corporation" -#define DRV_AUTHOR "" +#define DRV_AUTHOR "" /* radio config bits (actual values from NVM definition) */ #define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index e08319a3ed1b..d2294ad67023 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index 603c8945871b..07ad5c80e6ae 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -22,7 +22,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h index 9baf9ef8bdfb..fd42f63f5e84 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h index ffff31c38ecf..b49eda8150bb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h @@ -27,7 +27,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c index a105455b6a24..4a4dea08751c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h index 9ee18d0d2d01..24103877eab0 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 3ab777f79e4f..9da7dc49549c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 8caf68bce1ef..777fd6c50ba4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index c9ca029c69f4..2e098f8e0f83 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c index 61c07b05fcaa..015045733444 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index 5c21231e195d..1f8f61604416 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 8824a894aab0..8cd2e6786075 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index 7904b41a04c6..9e0d46368cdd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 9ce7587e329d..2acf9ffd5ed4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h index d398a6102805..2a33b694ba10 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h index 9a8a37f7fd19..af7f9dd7954c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -27,7 +27,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h index 3a657e4b60ac..d92712d4345a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 3f7478b54300..995898c5d017 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 05c933313f97..e6e80882d86d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index ee511aae727d..d00a6e9982f2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -27,7 +27,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 3a78a3f1610e..0fb10fd57b3b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index d8760fa9e322..ce9f57969847 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index a9fcb15e9d1a..d8dcb67b7ff9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 4753ecd1abda..ffcebea1e88f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c index e68a475e3071..6e6a56f2153d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index bed9696ee410..87a9f244e151 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -27,7 +27,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c index 509a66d05245..0b762b4f8fad 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 31b082edd29e..7bb6fd0e4391 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -20,7 +20,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h index 81314ad9ebe0..bdb6f2d8d854 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h @@ -20,7 +20,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index a0e957a4018d..145ec68ce6f9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 4887418b6ad3..87cc993f044a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index df1fed7e4c1c..4148ebea5373 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index ce1b16ac4c66..973f2ed0266c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c index fe2fa5650443..18711c5de35a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.c b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c index 4007f1d421dd..a1947d6f3a2c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tof.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h index 9beebc33cb8d..8c3421c9991d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tof.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h @@ -25,7 +25,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index cc0cdce84954..4d13df1b843b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index bbb7f6b27f5e..3a989f5c20db 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -27,7 +27,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 9bdce443628b..4f94be916b9a 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 0aceb9f3e49d..92a35d964178 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -23,7 +23,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 05d5a5b1e71a..a5bf24ed2a19 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -26,7 +26,7 @@ * in the file called COPYING. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index f2277c7dd6b2..2952915e273f 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -23,7 +23,7 @@ * file called LICENSE. * * Contact Information: - * Intel Linux Wireless + * Intel Linux Wireless * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ -- cgit v1.2.3-59-g8ed1b From 41837ca962ecb4ae7d98b00f94a51e737c8384ea Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 21 Oct 2015 09:00:07 +0300 Subject: iwlwifi: pcie: allow to pretend to have Tx CSUM for debug Allow to configure the driver to pretend to have TX CSUM offload support. This will be useful to test the TSO flows that will come in further patches. This configuration is disabled by default. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/constants.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 +++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 3 +++ drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 1 + drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 13 +++++++++++++ 7 files changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 777fd6c50ba4..6534537a2daf 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -478,6 +478,7 @@ struct iwl_hcmd_arr { * in DWORD (as opposed to bytes) * @scd_set_active: should the transport configure the SCD for HCMD queue * @wide_cmd_header: firmware supports wide host command header + * @sw_csum_tx: transport should compute the TCP checksum * @command_groups: array of command groups, each member is an array of the * commands in the group; for debugging only * @command_groups_size: number of command groups, to avoid illegal access @@ -497,6 +498,7 @@ struct iwl_trans_config { bool bc_table_dword; bool scd_set_active; bool wide_cmd_header; + bool sw_csum_tx; const struct iwl_hcmd_arr *command_groups; int command_groups_size; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index 1f8f61604416..b00c03fcd447 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -106,6 +106,7 @@ #define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0 #define IWL_MVM_RS_80_20_FAR_RANGE_TWEAK 1 #define IWL_MVM_TOF_IS_RESPONDER 0 +#define IWL_MVM_SW_TX_CSUM_OFFLOAD 0 #define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 0fb10fd57b3b..0227b29d43c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -667,6 +667,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) if (!iwl_mvm_is_csum_supported(mvm)) hw->netdev_features &= ~NETIF_F_RXCSUM; + if (IWL_MVM_SW_TX_CSUM_OFFLOAD) + hw->netdev_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + ret = ieee80211_register_hw(mvm->hw); if (ret) iwl_mvm_leds_exit(mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index ffcebea1e88f..50b8c01fadbd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -541,6 +541,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.scd_set_active = true; trans_cfg.sdio_adma_addr = fw->sdio_adma_addr; + trans_cfg.sw_csum_tx = IWL_MVM_SW_TX_CSUM_OFFLOAD; /* Set a short watchdog for the command queue */ trans_cfg.cmd_q_wdg_timeout = diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 92a35d964178..3d47dd7576ee 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -307,6 +307,8 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) * @scd_set_active: should the transport configure the SCD for HCMD queue * @wide_cmd_header: true when ucode supports wide command header format + * @sw_csum_tx: if true, then the transport will compute the csum of the TXed + * frame. * @rx_page_order: page order for receive buffer size * @reg_lock: protect hw register access * @mutex: to protect stop_device / start_fw / start_hw @@ -361,6 +363,7 @@ struct iwl_trans_pcie { bool bc_table_dword; bool scd_set_active; bool wide_cmd_header; + bool sw_csum_tx; u32 rx_page_order; /*protect hw register */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index a5bf24ed2a19..97e22fbda516 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1442,6 +1442,7 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, trans_pcie->wide_cmd_header = trans_cfg->wide_cmd_header; trans_pcie->bc_table_dword = trans_cfg->bc_table_dword; trans_pcie->scd_set_active = trans_cfg->scd_set_active; + trans_pcie->sw_csum_tx = trans_cfg->sw_csum_tx; trans->command_groups = trans_cfg->command_groups; trans->command_groups_size = trans_cfg->command_groups_size; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 2952915e273f..6c460a567ff9 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -1823,6 +1823,19 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, "TX on unused queue %d\n", txq_id)) return -EINVAL; + if (unlikely(trans_pcie->sw_csum_tx && + skb->ip_summed == CHECKSUM_PARTIAL)) { + int offs = skb_checksum_start_offset(skb); + int csum_offs = offs + skb->csum_offset; + __wsum csum; + + if (skb_ensure_writable(skb, csum_offs + sizeof(__sum16))) + return -1; + + csum = skb_checksum(skb, offs, skb->len - offs, 0); + *(__sum16 *)(skb->data + csum_offs) = csum_fold(csum); + } + if (skb_is_nonlinear(skb) && skb_shinfo(skb)->nr_frags > IWL_PCIE_MAX_FRAGS && __skb_linearize(skb)) -- cgit v1.2.3-59-g8ed1b From a3713f8bdd9c840c902998fd6133d94031974960 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 14 Oct 2015 14:16:35 +0300 Subject: iwlwifi: mvm: prepare the code towards TSO implementation Differentiate between the cases where the skb is a large send and the other cases. Advertise TSO even if, at this stage, skb_gso_segment will be called and it will do all the work. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 +- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 78 ++++++++++++++++++++++- 2 files changed, 78 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 0227b29d43c8..d239e97ab98a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -668,7 +668,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->netdev_features &= ~NETIF_F_RXCSUM; if (IWL_MVM_SW_TX_CSUM_OFFLOAD) - hw->netdev_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + hw->netdev_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_TSO | NETIF_F_TSO6; ret = ieee80211_register_hw(mvm->hw); if (ret) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 4d13df1b843b..04e921fcc0fb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -64,6 +64,7 @@ *****************************************************************************/ #include #include +#include #include "iwl-trans.h" #include "iwl-eeprom-parse.h" @@ -425,11 +426,39 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) return 0; } +static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb_gso, + struct ieee80211_sta *sta, + struct sk_buff_head *mpdus_skb) +{ + struct sk_buff *tmp, *next; + char cb[sizeof(skb_gso->cb)]; + + memcpy(cb, skb_gso->cb, sizeof(cb)); + next = skb_gso_segment(skb_gso, 0); + if (IS_ERR(next)) + return -EINVAL; + else if (next) + consume_skb(skb_gso); + + while (next) { + tmp = next; + next = tmp->next; + memcpy(tmp->cb, cb, sizeof(tmp->cb)); + + tmp->prev = NULL; + tmp->next = NULL; + + __skb_queue_tail(mpdus_skb, tmp); + } + + return 0; +} + /* * Sets the fields in the Tx cmd that are crypto related */ -int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, - struct ieee80211_sta *sta) +static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, + struct ieee80211_sta *sta) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -525,6 +554,51 @@ drop: return -1; } +int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct sk_buff_head mpdus_skbs; + unsigned int payload_len; + int ret; + + if (WARN_ON_ONCE(!mvmsta)) + return -1; + + if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) + return -1; + + if (!skb_is_gso(skb)) + return iwl_mvm_tx_mpdu(mvm, skb, sta); + + payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - + tcp_hdrlen(skb) + skb->data_len; + + if (payload_len <= skb_shinfo(skb)->gso_size) + return iwl_mvm_tx_mpdu(mvm, skb, sta); + + __skb_queue_head_init(&mpdus_skbs); + + ret = iwl_mvm_tx_tso(mvm, skb, sta, &mpdus_skbs); + if (ret) + return ret; + + if (WARN_ON(skb_queue_empty(&mpdus_skbs))) + return ret; + + while (!skb_queue_empty(&mpdus_skbs)) { + struct sk_buff *skb = __skb_dequeue(&mpdus_skbs); + + ret = iwl_mvm_tx_mpdu(mvm, skb, sta); + if (ret) { + __skb_queue_purge(&mpdus_skbs); + return ret; + } + } + + return 0; +} + static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 tid) { -- cgit v1.2.3-59-g8ed1b From 3a0b2a4225ede9205af12511c9b06ba29d94a08f Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 14 Oct 2015 22:10:50 +0300 Subject: iwlwifi: pcie: re-organize code towards TSO The code that handles the TBs that contain the WiFi payload will be changed for TSO. Move the current code into a separate function. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 113 +++++++++++++++------------ 1 file changed, 64 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 6c460a567ff9..890148c648c7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -1798,6 +1798,66 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) return iwl_pcie_send_hcmd_sync(trans, cmd); } +static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_txq *txq, u8 hdr_len, + struct iwl_cmd_meta *out_meta, + struct iwl_device_cmd *dev_cmd, u16 tb1_len) +{ + struct iwl_queue *q = &txq->q; + u16 tb2_len; + int i; + + /* + * Set up TFD's third entry to point directly to remainder + * of skb's head, if any + */ + tb2_len = skb_headlen(skb) - hdr_len; + + if (tb2_len > 0) { + dma_addr_t tb2_phys = dma_map_single(trans->dev, + skb->data + hdr_len, + tb2_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) { + iwl_pcie_tfd_unmap(trans, out_meta, + &txq->tfds[q->write_ptr]); + return -EINVAL; + } + iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false); + } + + /* set up the remaining entries to point to the data */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + dma_addr_t tb_phys; + int tb_idx; + + if (!skb_frag_size(frag)) + continue; + + tb_phys = skb_frag_dma_map(trans->dev, frag, 0, + skb_frag_size(frag), DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { + iwl_pcie_tfd_unmap(trans, out_meta, + &txq->tfds[q->write_ptr]); + return -EINVAL; + } + tb_idx = iwl_pcie_txq_build_tfd(trans, txq, tb_phys, + skb_frag_size(frag), false); + + out_meta->flags |= BIT(tb_idx + CMD_TB_BITMAP_POS); + } + + trace_iwlwifi_dev_tx(trans->dev, skb, + &txq->tfds[txq->q.write_ptr], + sizeof(struct iwl_tfd), + &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len, + skb->data + hdr_len, tb2_len); + trace_iwlwifi_dev_tx_data(trans->dev, skb, + hdr_len, skb->len - hdr_len); + return 0; +} + int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id) { @@ -1809,12 +1869,11 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_queue *q; dma_addr_t tb0_phys, tb1_phys, scratch_phys; void *tb1_addr; - u16 len, tb1_len, tb2_len; + u16 len, tb1_len; bool wait_write_ptr; __le16 fc; u8 hdr_len; u16 wifi_seq; - int i; txq = &trans_pcie->txq[txq_id]; q = &txq->q; @@ -1910,57 +1969,13 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, goto out_err; iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false); - /* - * Set up TFD's third entry to point directly to remainder - * of skb's head, if any - */ - tb2_len = skb_headlen(skb) - hdr_len; - if (tb2_len > 0) { - dma_addr_t tb2_phys = dma_map_single(trans->dev, - skb->data + hdr_len, - tb2_len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) { - iwl_pcie_tfd_unmap(trans, out_meta, - &txq->tfds[q->write_ptr]); - goto out_err; - } - iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false); - } - - /* set up the remaining entries to point to the data */ - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - dma_addr_t tb_phys; - int tb_idx; - - if (!skb_frag_size(frag)) - continue; - - tb_phys = skb_frag_dma_map(trans->dev, frag, 0, - skb_frag_size(frag), DMA_TO_DEVICE); - - if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { - iwl_pcie_tfd_unmap(trans, out_meta, - &txq->tfds[q->write_ptr]); - goto out_err; - } - tb_idx = iwl_pcie_txq_build_tfd(trans, txq, tb_phys, - skb_frag_size(frag), false); - - out_meta->flags |= BIT(tb_idx + CMD_TB_BITMAP_POS); - } + if (unlikely(iwl_fill_data_tbs(trans, skb, txq, hdr_len, + out_meta, dev_cmd, tb1_len))) + goto out_err; /* Set up entry for this TFD in Tx byte-count array */ iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); - trace_iwlwifi_dev_tx(trans->dev, skb, - &txq->tfds[txq->q.write_ptr], - sizeof(struct iwl_tfd), - &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len, - skb->data + hdr_len, tb2_len); - trace_iwlwifi_dev_tx_data(trans->dev, skb, - hdr_len, skb->len - hdr_len); - wait_write_ptr = ieee80211_has_morefrags(fc); /* start timer if queue currently empty */ -- cgit v1.2.3-59-g8ed1b From 3f73b8cad1d2923bf27caeaba705c4a64b287f1e Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 26 Oct 2015 15:39:22 +0200 Subject: iwlwifi: clear ieee80211_tx_info->driver_data in the op_mode The transport will need to use the info->driver_data pointers. Since the op_mode has this memory hot in cache, clear it there. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/tx.c | 1 + drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 3 ++- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c index cf0ad5f832e5..59e2001c39f8 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c @@ -383,6 +383,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, sta, fc); memset(&info->status, 0, sizeof(info->status)); + memset(info->driver_data, 0, sizeof(info->driver_data)); info->driver_data[0] = ctx; info->driver_data[1] = dev_cmd; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 6534537a2daf..a2683ef2068c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -552,7 +552,8 @@ struct iwl_trans_txq_scd_cfg { * If RFkill is asserted in the middle of a SYNC host command, it must * return -ERFKILL straight away. * May sleep only if CMD_ASYNC is not set - * @tx: send an skb + * @tx: send an skb. The transport relies on the op_mode to zero the + * the ieee80211_tx_info->driver_data. * Must be atomic * @reclaim: free packet until ssn. Returns a list of freed packets. * Must be atomic diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 04e921fcc0fb..8bf48a7d0f4e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -346,8 +346,8 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control); memset(&info->status, 0, sizeof(info->status)); + memset(info->driver_data, 0, sizeof(info->driver_data)); - info->driver_data[0] = NULL; info->driver_data[1] = dev_cmd; return dev_cmd; -- cgit v1.2.3-59-g8ed1b From 806f50c729c2d9f80500014f1d20c0ac74e66082 Mon Sep 17 00:00:00 2001 From: Luka Karinja Date: Sun, 20 Dec 2015 13:36:06 +0100 Subject: Bluetooth: hci_bcm: Add BCM2E65 ACPI ID Add BCM2E65 device in acpi_device_id table used on Asus T100TAF. Signed-off-by: Luka Karinja Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 3eed35e0207c..5c7c696c5838 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -815,6 +815,7 @@ static const struct hci_uart_proto bcm_proto = { #ifdef CONFIG_ACPI static const struct acpi_device_id bcm_acpi_match[] = { { "BCM2E39", 0 }, + { "BCM2E65", 0 }, { "BCM2E67", 0 }, { }, }; -- cgit v1.2.3-59-g8ed1b From c6b5df0daf0ecbb986420ea7342a6bd9c71619a9 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 13 Nov 2015 13:04:41 +0100 Subject: nfc: s3fwrn5: constify s3fwrn5_phy_ops structures The s3fwrn5_phy_ops structure is never modified, so declare it as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Acked-by: Robert Baldyga Signed-off-by: Samuel Ortiz --- drivers/nfc/s3fwrn5/core.c | 2 +- drivers/nfc/s3fwrn5/i2c.c | 2 +- drivers/nfc/s3fwrn5/s3fwrn5.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/s3fwrn5/core.c b/drivers/nfc/s3fwrn5/core.c index 0d866ca295e3..9d9c8d57a042 100644 --- a/drivers/nfc/s3fwrn5/core.c +++ b/drivers/nfc/s3fwrn5/core.c @@ -147,7 +147,7 @@ static struct nci_ops s3fwrn5_nci_ops = { }; int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev, - struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload) + const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload) { struct s3fwrn5_info *info; int ret; diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c index c61d8a308da4..3ed0adf6479b 100644 --- a/drivers/nfc/s3fwrn5/i2c.c +++ b/drivers/nfc/s3fwrn5/i2c.c @@ -125,7 +125,7 @@ static int s3fwrn5_i2c_write(void *phy_id, struct sk_buff *skb) return 0; } -static struct s3fwrn5_phy_ops i2c_phy_ops = { +static const struct s3fwrn5_phy_ops i2c_phy_ops = { .set_wake = s3fwrn5_i2c_set_wake, .set_mode = s3fwrn5_i2c_set_mode, .get_mode = s3fwrn5_i2c_get_mode, diff --git a/drivers/nfc/s3fwrn5/s3fwrn5.h b/drivers/nfc/s3fwrn5/s3fwrn5.h index 89210d4828b8..7d5e516036fb 100644 --- a/drivers/nfc/s3fwrn5/s3fwrn5.h +++ b/drivers/nfc/s3fwrn5/s3fwrn5.h @@ -44,7 +44,7 @@ struct s3fwrn5_info { void *phy_id; struct device *pdev; - struct s3fwrn5_phy_ops *phy_ops; + const struct s3fwrn5_phy_ops *phy_ops; unsigned int max_payload; struct s3fwrn5_fw_info fw_info; @@ -90,7 +90,7 @@ static inline int s3fwrn5_write(struct s3fwrn5_info *info, struct sk_buff *skb) } int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev, - struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload); + const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload); void s3fwrn5_remove(struct nci_dev *ndev); int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb, -- cgit v1.2.3-59-g8ed1b From 076ef440708bc28d821cebb2dbca64e3c917ac73 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Sun, 20 Dec 2015 15:06:18 +0100 Subject: ibmveth: consolidate kmalloc of array, memset 0 to kcalloc This is an API consolidation only. The use of kmalloc + memset to 0 is equivalent to kcalloc in this case as it is allocating an array of elements. Signed-off-by: Nicholas Mc Guire Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmveth.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 6691b5a45b9d..335417b4756b 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -169,7 +169,7 @@ static int ibmveth_alloc_buffer_pool(struct ibmveth_buff_pool *pool) if (!pool->free_map) return -1; - pool->dma_addr = kmalloc(sizeof(dma_addr_t) * pool->size, GFP_KERNEL); + pool->dma_addr = kcalloc(pool->size, sizeof(dma_addr_t), GFP_KERNEL); if (!pool->dma_addr) { kfree(pool->free_map); pool->free_map = NULL; @@ -187,8 +187,6 @@ static int ibmveth_alloc_buffer_pool(struct ibmveth_buff_pool *pool) return -1; } - memset(pool->dma_addr, 0, sizeof(dma_addr_t) * pool->size); - for (i = 0; i < pool->size; ++i) pool->free_map[i] = i; -- cgit v1.2.3-59-g8ed1b From 6eb5e529d7e3bf85b50ab0b5178df905dfc53b06 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 18 Oct 2015 09:31:24 +0300 Subject: iwlwifi: pcie: build an A-MSDU using TSO core When the op_mode sends an skb whose payload is bigger than MSS, PCIe will create an A-MSDU out of it. PCIe assumes that the skb that is coming from the op_mode can fit in one A-MSDU. It is the op_mode's responsibility to make sure that this guarantee holds. Additional headers need to be built for the subframes. The TSO core code takes care of the IP / TCP headers and the driver takes care of the 802.11 subframe headers. These headers are stored on a per-cpu page that is re-used for all the packets handled on that same CPU. Each skb holds a reference to that page and releases the page when it is reclaimed. When the page gets full, it is released and a new one is allocated. Since any SKB that doesn't go through the fast-xmit path of mac80211 will be segmented, we can assume here that the packet is not WEP / TKIP and has a proper SNAP header. Signed-off-by: Emmanuel Grumbach --- .../net/wireless/intel/iwlwifi/iwl-devtrace-data.h | 17 ++ drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 10 +- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 7 + drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 16 ++ drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 283 ++++++++++++++++++++- 5 files changed, 328 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h index bde023316b4a..d80312b46f16 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h @@ -1,6 +1,7 @@ /****************************************************************************** * * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -51,6 +52,22 @@ TRACE_EVENT(iwlwifi_dev_tx_data, TP_printk("[%s] TX frame data", __get_str(dev)) ); +TRACE_EVENT(iwlwifi_dev_tx_tso_chunk, + TP_PROTO(const struct device *dev, + u8 *data_src, size_t data_len), + TP_ARGS(dev, data_src, data_len), + TP_STRUCT__entry( + DEV_ENTRY + + __dynamic_array(u8, data, data_len) + ), + TP_fast_assign( + DEV_ASSIGN; + memcpy(__get_dynamic_array(data), data_src, data_len); + ), + TP_printk("[%s] TX frame data", __get_str(dev)) +); + TRACE_EVENT(iwlwifi_dev_rx_data, TP_PROTO(const struct device *dev, const struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index a2683ef2068c..43a48746d731 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -381,6 +381,11 @@ static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r) #define MAX_NO_RECLAIM_CMDS 6 +/* + * The first entry in driver_data array in ieee80211_tx_info + * that can be used by the transport. + */ +#define IWL_TRANS_FIRST_DRIVER_DATA 2 #define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) /* @@ -553,7 +558,10 @@ struct iwl_trans_txq_scd_cfg { * return -ERFKILL straight away. * May sleep only if CMD_ASYNC is not set * @tx: send an skb. The transport relies on the op_mode to zero the - * the ieee80211_tx_info->driver_data. + * the ieee80211_tx_info->driver_data. If the MPDU is an A-MSDU, all + * the CSUM will be taken care of (TCP CSUM and IP header in case of + * IPv4). If the MPDU is a single MSDU, the op_mode must compute the IP + * header if it is IPv4. * Must be atomic * @reclaim: free packet until ssn. Returns a list of freed packets. * Must be atomic diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 3d47dd7576ee..cc3888e2700d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -289,6 +289,11 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) sizeof(struct iwl_pcie_txq_scratch_buf) * idx; } +struct iwl_tso_hdr_page { + struct page *page; + u8 *pos; +}; + /** * struct iwl_trans_pcie - PCIe transport specific data * @rxq: all the RX queue data @@ -326,6 +331,8 @@ struct iwl_trans_pcie { struct net_device napi_dev; struct napi_struct napi; + struct __percpu iwl_tso_hdr_page *tso_hdr_page; + /* INT ICT Table */ __le32 *ict_tbl; dma_addr_t ict_tbl_dma; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 97e22fbda516..e8041907e7e2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1465,6 +1465,7 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, void iwl_trans_pcie_free(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int i; synchronize_irq(trans_pcie->pci_dev->irq); @@ -1484,6 +1485,15 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) iwl_pcie_free_fw_monitor(trans); + for_each_possible_cpu(i) { + struct iwl_tso_hdr_page *p = + per_cpu_ptr(trans_pcie->tso_hdr_page, i); + + if (p->page) + __free_page(p->page); + } + + free_percpu(trans_pcie->tso_hdr_page); iwl_trans_free(trans); } @@ -2542,6 +2552,11 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, spin_lock_init(&trans_pcie->ref_lock); mutex_init(&trans_pcie->mutex); init_waitqueue_head(&trans_pcie->ucode_write_waitq); + trans_pcie->tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page); + if (!trans_pcie->tso_hdr_page) { + ret = -ENOMEM; + goto out_no_pci; + } ret = pci_enable_device(pdev); if (ret) @@ -2690,6 +2705,7 @@ out_pci_release_regions: out_pci_disable_device: pci_disable_device(pdev); out_no_pci: + free_percpu(trans_pcie->tso_hdr_page); iwl_trans_free(trans); return ERR_PTR(ret); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 890148c648c7..a85ae1002d97 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -28,8 +28,12 @@ * *****************************************************************************/ #include +#include #include #include +#include +#include +#include #include "iwl-debug.h" #include "iwl-csr.h" @@ -578,6 +582,19 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, return 0; } +static void iwl_pcie_free_tso_page(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA]) { + struct page *page = + info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA]; + + __free_page(page); + info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA] = NULL; + } +} + /* * iwl_pcie_txq_unmap - Unmap any remaining DMA mappings and free skb's */ @@ -591,6 +608,15 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) while (q->write_ptr != q->read_ptr) { IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", txq_id, q->read_ptr); + + if (txq_id != trans_pcie->cmd_queue) { + struct sk_buff *skb = txq->entries[q->read_ptr].skb; + + if (WARN_ON_ONCE(!skb)) + continue; + + iwl_pcie_free_tso_page(skb); + } iwl_pcie_txq_free_tfd(trans, txq); q->read_ptr = iwl_queue_inc_wrap(q->read_ptr); } @@ -1008,11 +1034,14 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, for (; q->read_ptr != tfd_num; q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) { + struct sk_buff *skb = txq->entries[txq->q.read_ptr].skb; - if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL)) + if (WARN_ON_ONCE(!skb)) continue; - __skb_queue_tail(skbs, txq->entries[txq->q.read_ptr].skb); + iwl_pcie_free_tso_page(skb); + + __skb_queue_tail(skbs, skb); txq->entries[txq->q.read_ptr].skb = NULL; @@ -1858,6 +1887,245 @@ static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb, return 0; } +#ifdef CONFIG_INET +static struct iwl_tso_hdr_page * +get_page_hdr(struct iwl_trans *trans, size_t len) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tso_hdr_page *p = this_cpu_ptr(trans_pcie->tso_hdr_page); + + if (!p->page) + goto alloc; + + /* enough room on this page */ + if (p->pos + len < (u8 *)page_address(p->page) + PAGE_SIZE) + return p; + + /* We don't have enough room on this page, get a new one. */ + __free_page(p->page); + +alloc: + p->page = alloc_page(GFP_ATOMIC); + if (!p->page) + return NULL; + p->pos = page_address(p->page); + return p; +} + +static void iwl_compute_pseudo_hdr_csum(void *iph, struct tcphdr *tcph, + bool ipv6, unsigned int len) +{ + if (ipv6) { + struct ipv6hdr *iphv6 = iph; + + tcph->check = ~csum_ipv6_magic(&iphv6->saddr, &iphv6->daddr, + len + tcph->doff * 4, + IPPROTO_TCP, 0); + } else { + struct iphdr *iphv4 = iph; + + ip_send_check(iphv4); + tcph->check = ~csum_tcpudp_magic(iphv4->saddr, iphv4->daddr, + len + tcph->doff * 4, + IPPROTO_TCP, 0); + } +} + +static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_txq *txq, u8 hdr_len, + struct iwl_cmd_meta *out_meta, + struct iwl_device_cmd *dev_cmd, u16 tb1_len) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; + struct ieee80211_hdr *hdr = (void *)skb->data; + unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room; + unsigned int mss = skb_shinfo(skb)->gso_size; + struct iwl_queue *q = &txq->q; + u16 length, iv_len, amsdu_pad; + u8 *start_hdr; + struct iwl_tso_hdr_page *hdr_page; + int ret; + struct tso_t tso; + + /* if the packet is protected, then it must be CCMP or GCMP */ + BUILD_BUG_ON(IEEE80211_CCMP_HDR_LEN != IEEE80211_GCMP_HDR_LEN); + iv_len = ieee80211_has_protected(hdr->frame_control) ? + IEEE80211_CCMP_HDR_LEN : 0; + + trace_iwlwifi_dev_tx(trans->dev, skb, + &txq->tfds[txq->q.write_ptr], + sizeof(struct iwl_tfd), + &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len, + NULL, 0); + + ip_hdrlen = skb_transport_header(skb) - skb_network_header(skb); + snap_ip_tcp_hdrlen = 8 + ip_hdrlen + tcp_hdrlen(skb); + total_len = skb->len - snap_ip_tcp_hdrlen - hdr_len - iv_len; + amsdu_pad = 0; + + /* total amount of header we may need for this A-MSDU */ + hdr_room = DIV_ROUND_UP(total_len, mss) * + (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr)) + iv_len; + + /* Our device supports 9 segments at most, it will fit in 1 page */ + hdr_page = get_page_hdr(trans, hdr_room); + if (!hdr_page) + return -ENOMEM; + + get_page(hdr_page->page); + start_hdr = hdr_page->pos; + info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA] = hdr_page->page; + memcpy(hdr_page->pos, skb->data + hdr_len, iv_len); + hdr_page->pos += iv_len; + + /* + * Pull the ieee80211 header + IV to be able to use TSO core, + * we will restore it for the tx_status flow. + */ + skb_pull(skb, hdr_len + iv_len); + + tso_start(skb, &tso); + + while (total_len) { + /* this is the data left for this subframe */ + unsigned int data_left = + min_t(unsigned int, mss, total_len); + struct sk_buff *csum_skb = NULL; + unsigned int hdr_tb_len; + dma_addr_t hdr_tb_phys; + struct tcphdr *tcph; + u8 *iph; + + total_len -= data_left; + + memset(hdr_page->pos, 0, amsdu_pad); + hdr_page->pos += amsdu_pad; + amsdu_pad = (4 - (sizeof(struct ethhdr) + snap_ip_tcp_hdrlen + + data_left)) & 0x3; + ether_addr_copy(hdr_page->pos, ieee80211_get_DA(hdr)); + hdr_page->pos += ETH_ALEN; + ether_addr_copy(hdr_page->pos, ieee80211_get_SA(hdr)); + hdr_page->pos += ETH_ALEN; + + length = snap_ip_tcp_hdrlen + data_left; + *((__be16 *)hdr_page->pos) = cpu_to_be16(length); + hdr_page->pos += sizeof(length); + + /* + * This will copy the SNAP as well which will be considered + * as MAC header. + */ + tso_build_hdr(skb, hdr_page->pos, &tso, data_left, !total_len); + iph = hdr_page->pos + 8; + tcph = (void *)(iph + ip_hdrlen); + + /* For testing on current hardware only */ + if (trans_pcie->sw_csum_tx) { + csum_skb = alloc_skb(data_left + tcp_hdrlen(skb), + GFP_ATOMIC); + if (!csum_skb) { + ret = -ENOMEM; + goto out_unmap; + } + + iwl_compute_pseudo_hdr_csum(iph, tcph, + skb->protocol == + htons(ETH_P_IPV6), + data_left); + + memcpy(skb_put(csum_skb, tcp_hdrlen(skb)), + tcph, tcp_hdrlen(skb)); + skb_set_transport_header(csum_skb, 0); + csum_skb->csum_start = + (unsigned char *)tcp_hdr(csum_skb) - + csum_skb->head; + } + + hdr_page->pos += snap_ip_tcp_hdrlen; + + hdr_tb_len = hdr_page->pos - start_hdr; + hdr_tb_phys = dma_map_single(trans->dev, start_hdr, + hdr_tb_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, hdr_tb_phys))) { + dev_kfree_skb(csum_skb); + ret = -EINVAL; + goto out_unmap; + } + iwl_pcie_txq_build_tfd(trans, txq, hdr_tb_phys, + hdr_tb_len, false); + trace_iwlwifi_dev_tx_tso_chunk(trans->dev, start_hdr, + hdr_tb_len); + + /* prepare the start_hdr for the next subframe */ + start_hdr = hdr_page->pos; + + /* put the payload */ + while (data_left) { + unsigned int size = min_t(unsigned int, tso.size, + data_left); + dma_addr_t tb_phys; + + if (trans_pcie->sw_csum_tx) + memcpy(skb_put(csum_skb, size), tso.data, size); + + tb_phys = dma_map_single(trans->dev, tso.data, + size, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { + dev_kfree_skb(csum_skb); + ret = -EINVAL; + goto out_unmap; + } + + iwl_pcie_txq_build_tfd(trans, txq, tb_phys, + size, false); + trace_iwlwifi_dev_tx_tso_chunk(trans->dev, tso.data, + size); + + data_left -= size; + tso_build_data(skb, &tso, size); + } + + /* For testing on early hardware only */ + if (trans_pcie->sw_csum_tx) { + __wsum csum; + + csum = skb_checksum(csum_skb, + skb_checksum_start_offset(csum_skb), + csum_skb->len - + skb_checksum_start_offset(csum_skb), + 0); + dev_kfree_skb(csum_skb); + dma_sync_single_for_cpu(trans->dev, hdr_tb_phys, + hdr_tb_len, DMA_TO_DEVICE); + tcph->check = csum_fold(csum); + dma_sync_single_for_device(trans->dev, hdr_tb_phys, + hdr_tb_len, DMA_TO_DEVICE); + } + } + + /* re -add the WiFi header and IV */ + skb_push(skb, hdr_len + iv_len); + + return 0; + +out_unmap: + iwl_pcie_tfd_unmap(trans, out_meta, &txq->tfds[q->write_ptr]); + return ret; +} +#else /* CONFIG_INET */ +static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_txq *txq, u8 hdr_len, + struct iwl_cmd_meta *out_meta, + struct iwl_device_cmd *dev_cmd, u16 tb1_len) +{ + /* No A-MSDU without CONFIG_INET */ + WARN_ON(1); + + return -1; +} +#endif /* CONFIG_INET */ + int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id) { @@ -1969,9 +2237,16 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, goto out_err; iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false); - if (unlikely(iwl_fill_data_tbs(trans, skb, txq, hdr_len, - out_meta, dev_cmd, tb1_len))) + if (ieee80211_is_data_qos(fc) && + (*ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_A_MSDU_PRESENT)) { + if (unlikely(iwl_fill_data_tbs_amsdu(trans, skb, txq, hdr_len, + out_meta, dev_cmd, + tb1_len))) + goto out_err; + } else if (unlikely(iwl_fill_data_tbs(trans, skb, txq, hdr_len, + out_meta, dev_cmd, tb1_len))) { goto out_err; + } /* Set up entry for this TFD in Tx byte-count array */ iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); -- cgit v1.2.3-59-g8ed1b From 6ae57b293be8588f9523ae39a5475940772c31d0 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 16 Dec 2015 10:50:03 +0200 Subject: iwlwifi: 9000: increase the number of queues 9000 family devices have 31 queues. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-9000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c index a784bb64ef63..0d2aa1d9a50f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c @@ -83,7 +83,7 @@ static const struct iwl_base_params iwl9000_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_9000, - .num_of_queues = IWLAGN_NUM_QUEUES, + .num_of_queues = 31, .pll_cfg_val = 0, .shadow_ram_support = true, .led_compensation = 57, -- cgit v1.2.3-59-g8ed1b From 9c3deeb51b664db183e00688caf8da2a096d16e5 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 11 Nov 2015 01:06:17 +0200 Subject: iwlwifi: mvm: refactor the way fw_key_table is handled Instead of keeping the fw_key_table bits set when the keys are removed (i.e. in D3 entry or HW_RESTART flows), clear them and set them again only when the keys have been successfully re-added. This makes the bitmask more closely tied to the actual firmware programming. Signed-off-by: Luca Coelho Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 3 +++ drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 10 ++++------ 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 8cd2e6786075..69fd10a2f2a3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -779,6 +779,9 @@ static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm) */ set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + /* the fw is reset, so all the keys are cleared */ + memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); + mvm->ptk_ivlen = 0; mvm->ptk_icvlen = 0; mvm->ptk_ivlen = 0; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index d239e97ab98a..be5703c44c22 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -984,6 +984,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; iwl_mvm_reset_phy_ctxts(mvm); + memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained)); memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained)); memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 4148ebea5373..92edacc298da 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1198,8 +1198,6 @@ static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm) if (max_offs < 0) return STA_KEY_IDX_INVALID; - __set_bit(max_offs, mvm->fw_key_table); - return max_offs; } @@ -1507,10 +1505,8 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, } ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, key_offset, mcast); - if (ret) { - __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table); + if (ret) goto end; - } /* * For WEP, the same key is used for multicast and unicast. Upload it @@ -1523,11 +1519,13 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, key_offset, !mcast); if (ret) { - __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table); __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast); + goto end; } } + __set_bit(key_offset, mvm->fw_key_table); + end: IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", keyconf->cipher, keyconf->keylen, keyconf->keyidx, -- cgit v1.2.3-59-g8ed1b From 70b4c53646e5960fd94f67c033aeca519908c3e9 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 19 Nov 2015 13:12:15 +0200 Subject: iwlwifi: mvm: enable L3 filtering Firmware will support filtering multicast L3 packets. The L3 filtering is configured by the WOWLAN_CONFIG command. All flags should be enabled by default. Older firmware is not affected as it does not look into this field. Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h | 13 +++++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 3 ++- 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 69fd10a2f2a3..3e6b6d626c25 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -804,6 +804,8 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, wowlan_config_cmd->is_11n_connection = ap_sta->ht_cap.ht_supported; + wowlan_config_cmd->flags = ENABLE_L3_FILTERING | + ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING; /* Query the last used seqno and set it */ ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h index c36c9563f744..62b9a0a96700 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -245,6 +246,13 @@ enum iwl_wowlan_wakeup_filters { IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16), }; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */ +enum iwl_wowlan_flags { + IS_11W_ASSOC = BIT(0), + ENABLE_L3_FILTERING = BIT(1), + ENABLE_NBNS_FILTERING = BIT(2), + ENABLE_DHCP_FILTERING = BIT(3), +}; + struct iwl_wowlan_config_cmd { __le32 wakeup_filter; __le16 non_qos_seq; @@ -252,8 +260,9 @@ struct iwl_wowlan_config_cmd { u8 wowlan_ba_teardown_tids; u8 is_11n_connection; u8 offloading_tid; - u8 reserved[3]; -} __packed; /* WOWLAN_CONFIG_API_S_VER_3 */ + u8 flags; + u8 reserved[2]; +} __packed; /* WOWLAN_CONFIG_API_S_VER_4 */ /* * WOWLAN_TSC_RSC_PARAMS diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 50b8c01fadbd..a5e1c8e943a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1186,7 +1186,8 @@ static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); cmd->is_11n_connection = ap_sta->ht_cap.ht_supported; cmd->offloading_tid = iter_data->offloading_tid; - + cmd->flags = ENABLE_L3_FILTERING | ENABLE_NBNS_FILTERING | + ENABLE_DHCP_FILTERING; /* * The d0i3 uCode takes care of the nonqos counters, * so configure only the qos seq ones. -- cgit v1.2.3-59-g8ed1b From c91b865cb14d9dd7b0e411bc4546ac115900970d Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Mon, 30 Nov 2015 14:30:21 +0200 Subject: iwlwifi: mvm: support description for user triggered fw dbg collection Add to the user triggered fw debug collection support for describing the reason of the trigger. This could be useful for identifying a dump by a unique id, passed as a description. Signed-off-by: Golan Ben-Ami Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 2acf9ffd5ed4..90500e2d107b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -1029,7 +1029,8 @@ static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm, if (ret) return ret; - iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, NULL, 0, NULL); + iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, buf, + (count - 1), NULL); iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE); @@ -1451,7 +1452,7 @@ MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10); MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8); -MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 64); MVM_DEBUGFS_WRITE_FILE_OPS(cont_recording, 8); #ifdef CONFIG_IWLWIFI_BCAST_FILTERING -- cgit v1.2.3-59-g8ed1b From c81240707c71f343d96dae3537997682a9eb436d Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 16 Dec 2015 11:26:09 +0200 Subject: iwlwifi: mvm: small update in the firmware API Small change in firmware API, no functional change. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h index af7f9dd7954c..75c79e13269e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -79,6 +79,11 @@ #define IWL_RX_INFO_ENERGY_ANT_B_POS 8 #define IWL_RX_INFO_ENERGY_ANT_C_POS 16 +enum iwl_mac_context_info { + MAC_CONTEXT_INFO_NONE, + MAC_CONTEXT_INFO_GSCAN, +}; + /** * struct iwl_rx_phy_info - phy info * (REPLY_RX_PHY_CMD = 0xc0) @@ -97,6 +102,8 @@ * @frame_time: frame's time on the air, based on byte count and frame rate * calculation * @mac_active_msk: what MACs were active when the frame was received + * @mac_context_info: additional info on the context in which the frame was + * received as defined in &enum iwl_mac_context_info * * Before each Rx, the device sends this data. It contains PHY information * about the reception of the packet. @@ -114,7 +121,8 @@ struct iwl_rx_phy_info { __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT]; __le32 rate_n_flags; __le32 byte_count; - __le16 mac_active_msk; + u8 mac_active_msk; + u8 mac_context_info; __le16 frame_time; } __packed; -- cgit v1.2.3-59-g8ed1b From c3e230b167a9d618c4eb41c0a5ba1851d33d6dbd Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Sun, 22 Nov 2015 16:37:36 +0200 Subject: iwlwifi: mvm: add extended dwell time When doing active scan on crowded channels we are likely to miss probe responses due to collisions. To overcome this issue we use an extended dwell time on channels 1, 6 and 11; this dwell time is set to 100. In case of fragmented scan extended dwell time is the maximum out of channel time - 44 msec. Fragmented active scan will be addressed later. Extended dwell time isn't used in sched scan or p2p find. Signed-off-by: David Spinadel Signed-off-by: Emmanuel Grumbach --- .../net/wireless/intel/iwlwifi/mvm/fw-api-scan.h | 22 +++++++----- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 41 ++++++++++++++++------ 2 files changed, 45 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h index d92712d4345a..f01dab0d0dac 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h @@ -285,6 +285,8 @@ struct iwl_scan_channel_opt { * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented * @IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report * and DS parameter set IEs into probe requests. + * @IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL: use extended dwell time on channels + * 1, 6 and 11. * @IWL_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches */ enum iwl_mvm_lmac_scan_flags { @@ -295,6 +297,7 @@ enum iwl_mvm_lmac_scan_flags { IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4), IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5), IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = BIT(6), + IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL = BIT(7), IWL_MVM_LMAC_SCAN_FLAG_MATCH = BIT(9), }; @@ -322,6 +325,7 @@ enum iwl_scan_priority_ext { * @active-dwell: dwell time for active channels * @passive-dwell: dwell time for passive channels * @fragmented-dwell: dwell time for fragmented passive scan + * @extended_dwell: dwell time for channels 1, 6 and 11 (in certain cases) * @reserved2: for alignment and future use * @rx_chain_selct: PHY_RX_CHAIN_* flags * @scan_flags: &enum iwl_mvm_lmac_scan_flags @@ -346,7 +350,8 @@ struct iwl_scan_req_lmac { u8 active_dwell; u8 passive_dwell; u8 fragmented_dwell; - __le16 reserved2; + u8 extended_dwell; + u8 reserved2; __le16 rx_chain_select; __le32 scan_flags; __le32 max_out_time; @@ -490,7 +495,7 @@ enum iwl_channel_flags { * @dwell_active: default dwell time for active scan * @dwell_passive: default dwell time for passive scan * @dwell_fragmented: default dwell time for fragmented scan - * @reserved: for future use and alignment + * @dwell_extended: default dwell time for channels 1, 6 and 11 * @mac_addr: default mac address to be used in probes * @bcast_sta_id: the index of the station in the fw * @channel_flags: default channel flags - enum iwl_channel_flags @@ -507,7 +512,7 @@ struct iwl_scan_config { u8 dwell_active; u8 dwell_passive; u8 dwell_fragmented; - u8 reserved; + u8 dwell_extended; u8 mac_addr[ETH_ALEN]; u8 bcast_sta_id; u8 channel_flags; @@ -543,7 +548,8 @@ enum iwl_umac_scan_general_flags { IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = BIT(6), IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = BIT(7), IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = BIT(8), - IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9) + IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9), + IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL = BIT(10), }; /** @@ -597,7 +603,7 @@ struct iwl_scan_req_umac_tail { * @uid: scan id, &enum iwl_umac_scan_uid_offsets * @ooc_priority: out of channel priority - &enum iwl_scan_priority * @general_flags: &enum iwl_umac_scan_general_flags - * @reserved1: for future use and alignment + * @extended_dwell: dwell time for channels 1, 6 and 11 * @active_dwell: dwell time for active scan * @passive_dwell: dwell time for passive scan * @fragmented_dwell: dwell time for fragmented passive scan @@ -606,7 +612,7 @@ struct iwl_scan_req_umac_tail { * @scan_priority: scan internal prioritization &enum iwl_scan_priority * @channel_flags: &enum iwl_scan_channel_flags * @n_channels: num of channels in scan request - * @reserved2: for future use and alignment + * @reserved: for future use and alignment * @data: &struct iwl_scan_channel_cfg_umac and * &struct iwl_scan_req_umac_tail */ @@ -616,7 +622,7 @@ struct iwl_scan_req_umac { __le32 ooc_priority; /* SCAN_GENERAL_PARAMS_API_S_VER_1 */ __le32 general_flags; - u8 reserved1; + u8 extended_dwell; u8 active_dwell; u8 passive_dwell; u8 fragmented_dwell; @@ -626,7 +632,7 @@ struct iwl_scan_req_umac { /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */ u8 channel_flags; u8 n_channels; - __le16 reserved2; + __le16 reserved; u8 data[]; } __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 87cc993f044a..bee3201c7116 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -82,6 +82,7 @@ struct iwl_mvm_scan_timing_params { u32 dwell_active; u32 dwell_passive; u32 dwell_fragmented; + u32 dwell_extended; u32 suspend_time; u32 max_out_time; }; @@ -91,6 +92,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, + .dwell_extended = 100, .suspend_time = 0, .max_out_time = 0, }, @@ -98,6 +100,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, + .dwell_extended = 100, .suspend_time = 30, .max_out_time = 120, }, @@ -105,6 +108,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, + .dwell_extended = 100, .suspend_time = 120, .max_out_time = 120, }, @@ -112,6 +116,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, + .dwell_extended = 44, .suspend_time = 95, .max_out_time = 44, }, @@ -716,6 +721,7 @@ static void iwl_mvm_scan_lmac_dwell(struct iwl_mvm *mvm, cmd->active_dwell = scan_timing[params->type].dwell_active; cmd->passive_dwell = scan_timing[params->type].dwell_passive; cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented; + cmd->extended_dwell = scan_timing[params->type].dwell_extended; cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time); cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time); cmd->scan_prio = iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); @@ -749,8 +755,15 @@ static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm, vif->type != NL80211_IFTYPE_P2P_DEVICE); } +static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params) +{ + return params->n_scan_plans == 1 && + params->scan_plans[0].iterations == 1; +} + static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm, - struct iwl_mvm_scan_params *params) + struct iwl_mvm_scan_params *params, + struct ieee80211_vif *vif) { int flags = 0; @@ -776,6 +789,10 @@ static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm, flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE; #endif + if (iwl_mvm_is_regular_scan(params) && + vif->type != NL80211_IFTYPE_P2P_DEVICE) + flags |= IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL; + return flags; } @@ -804,7 +821,8 @@ static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, cmd->delay = cpu_to_le32(params->delay); - cmd->scan_flags = cpu_to_le32(iwl_mvm_scan_lmac_flags(mvm, params)); + cmd->scan_flags = cpu_to_le32(iwl_mvm_scan_lmac_flags(mvm, params, + vif)); cmd->flags = iwl_mvm_scan_rxon_flags(params->channels[0]->band); cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | @@ -942,6 +960,7 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) scan_config->dwell_active = scan_timing[type].dwell_active; scan_config->dwell_passive = scan_timing[type].dwell_passive; scan_config->dwell_fragmented = scan_timing[type].dwell_fragmented; + scan_config->dwell_extended = scan_timing[type].dwell_extended; memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); @@ -983,16 +1002,11 @@ static int iwl_mvm_scan_uid_by_status(struct iwl_mvm *mvm, int status) return -ENOENT; } -static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params) -{ - return params->n_scan_plans == 1 && - params->scan_plans[0].iterations == 1; -} - static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, struct iwl_scan_req_umac *cmd, struct iwl_mvm_scan_params *params) { + cmd->extended_dwell = scan_timing[params->type].dwell_extended; cmd->active_dwell = scan_timing[params->type].dwell_active; cmd->passive_dwell = scan_timing[params->type].dwell_passive; cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented; @@ -1027,7 +1041,8 @@ iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm, } static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm, - struct iwl_mvm_scan_params *params) + struct iwl_mvm_scan_params *params, + struct ieee80211_vif *vif) { int flags = 0; @@ -1055,6 +1070,11 @@ static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm, if (mvm->scan_iter_notif_enabled) flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE; #endif + + if (iwl_mvm_is_regular_scan(params) && + vif->type != NL80211_IFTYPE_P2P_DEVICE) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL; + return flags; } @@ -1085,7 +1105,8 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvm->scan_uid_status[uid] = type; cmd->uid = cpu_to_le32(uid); - cmd->general_flags = cpu_to_le32(iwl_mvm_scan_umac_flags(mvm, params)); + cmd->general_flags = cpu_to_le32(iwl_mvm_scan_umac_flags(mvm, params, + vif)); if (type == IWL_MVM_SCAN_SCHED) cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE); -- cgit v1.2.3-59-g8ed1b From 0e39eb0386841fc71a86ad3feb5ddff553d2888e Mon Sep 17 00:00:00 2001 From: Chaya Rachel Ivgi Date: Thu, 3 Dec 2015 15:51:46 +0200 Subject: iwlwifi: mvm: Add a station in monitor mode Currently when creating a new vif in monitor mode the driver doesn't allocate a specific station. This causes that in the situation that tx traffic is injected, the tx queues are not scheduled, with the result of a TFD queue hang. Fix that by allocating a station and ensuring its tx queues are scheduled. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=104591 Signed-off-by: Chaya Rachel Ivgi Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 16 ++++++++++- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 6 +++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 33 ++++++++++++++++++++--- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 6 +++++ 5 files changed, 58 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index d00a6e9982f2..5e3a7582885b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -855,11 +855,17 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, u32 action) { struct iwl_mac_ctx_cmd cmd = {}; + u32 tfd_queue_msk = 0; + int ret, i; WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); + for (i = 0; i < IEEE80211_NUM_ACS; i++) + if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) + tfd_queue_msk |= BIT(vif->hw_queue[i]); + cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC | MAC_FILTER_IN_CONTROL_AND_MGMT | MAC_FILTER_IN_BEACON | @@ -867,6 +873,12 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, MAC_FILTER_IN_CRC32); ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS); + /* Allocate sniffer station */ + ret = iwl_mvm_allocate_int_sta(mvm, &mvm->snif_sta, tfd_queue_msk, + vif->type); + if (ret) + return ret; + return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); } @@ -1289,8 +1301,10 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvmvif->uploaded = false; - if (vif->type == NL80211_IFTYPE_MONITOR) + if (vif->type == NL80211_IFTYPE_MONITOR) { __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags); + iwl_mvm_dealloc_snif_sta(mvm); + } return 0; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index be5703c44c22..53415dc23ff4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3126,6 +3126,11 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, ret = iwl_mvm_update_quotas(mvm, false, NULL); if (ret) goto out_remove_binding; + + ret = iwl_mvm_add_snif_sta(mvm, vif); + if (ret) + goto out_remove_binding; + } /* Handle binding during CSA */ @@ -3199,6 +3204,7 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; mvmvif->ps_disabled = false; + iwl_mvm_rm_snif_sta(mvm, vif); break; case NL80211_IFTYPE_AP: /* This part is triggered only during CSA */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index ce9f57969847..df68973e4c10 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -677,6 +677,7 @@ struct iwl_mvm { /* Internal station */ struct iwl_mvm_int_sta aux_sta; + struct iwl_mvm_int_sta snif_sta; bool last_ebs_successful; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 92edacc298da..bfa46998dd26 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -581,9 +581,9 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, return ret; } -static int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *sta, - u32 qmask, enum nl80211_iftype iftype) +int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *sta, + u32 qmask, enum nl80211_iftype iftype) { if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype); @@ -673,6 +673,33 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) return ret; } +int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + lockdep_assert_held(&mvm->mutex); + return iwl_mvm_add_int_sta_common(mvm, &mvm->snif_sta, vif->addr, + mvmvif->id, 0); +} + +int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + int ret; + + lockdep_assert_held(&mvm->mutex); + + ret = iwl_mvm_rm_sta_common(mvm, mvm->snif_sta.sta_id); + if (ret) + IWL_WARN(mvm, "Failed sending remove station\n"); + + return ret; +} + +void iwl_mvm_dealloc_snif_sta(struct iwl_mvm *mvm) +{ + iwl_mvm_dealloc_int_sta(mvm, &mvm->snif_sta); +} + void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm) { lockdep_assert_held(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 973f2ed0266c..badf17c7fcca 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -407,7 +407,13 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, + struct iwl_mvm_int_sta *sta, + u32 qmask, enum nl80211_iftype iftype); void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_dealloc_snif_sta(struct iwl_mvm *mvm); void iwl_mvm_sta_drained_wk(struct work_struct *wk); void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, -- cgit v1.2.3-59-g8ed1b From 5f7a18477c6cb36a435554b9744160e76bc9eb69 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 11 Dec 2015 09:36:10 +0100 Subject: iwlwifi: mvm: change iwl_mvm_get_key_sta_id() to return the station The code in iwl_mvm_update_tkip_key() is now pretty much duplicated with the code in iwl_mvm_get_key_sta_id() doing the station ID lookup again after it was already done. Change iwl_mvm_get_key_sta_id() to iwl_mvm_get_key_sta(), returning the mvm_sta pointer, to allow that duplicate code to be removed. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 53 ++++++++++++---------------- 1 file changed, 22 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index bfa46998dd26..2c9675b45350 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1228,17 +1228,14 @@ static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm) return max_offs; } -static u8 iwl_mvm_get_key_sta_id(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) +static struct iwl_mvm_sta *iwl_mvm_get_key_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - if (sta) { - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); - - return mvm_sta->sta_id; - } + if (sta) + return iwl_mvm_sta_from_mac80211(sta); /* * The device expects GTKs for station interfaces to be @@ -1257,12 +1254,12 @@ static u8 iwl_mvm_get_key_sta_id(struct iwl_mvm *mvm, * be the AP ID, and no station was passed by mac80211. */ if (IS_ERR_OR_NULL(sta)) - return IWL_MVM_STATION_COUNT; + return NULL; - return sta_id; + return iwl_mvm_sta_from_mac80211(sta); } - return IWL_MVM_STATION_COUNT; + return NULL; } static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, @@ -1479,6 +1476,7 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, u8 key_offset) { bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); + struct iwl_mvm_sta *mvm_sta; u8 sta_id; int ret; static const u8 __maybe_unused zero_addr[ETH_ALEN] = {0}; @@ -1486,11 +1484,12 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); /* Get the station id from the mvm local station table */ - sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta); - if (sta_id == IWL_MVM_STATION_COUNT) { - IWL_ERR(mvm, "Failed to find station id\n"); + mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta); + if (!mvm_sta) { + IWL_ERR(mvm, "Failed to find station\n"); return -EINVAL; } + sta_id = mvm_sta->sta_id; if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false); @@ -1566,13 +1565,14 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, struct ieee80211_key_conf *keyconf) { bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); - u8 sta_id; + struct iwl_mvm_sta *mvm_sta; + u8 sta_id = IWL_MVM_STATION_COUNT; int ret, i; lockdep_assert_held(&mvm->mutex); - /* Get the station id from the mvm local station table */ - sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta); + /* Get the station from the mvm local station table */ + mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta); IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id); @@ -1593,11 +1593,13 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, } mvm->fw_key_deleted[keyconf->hw_key_idx] = 0; - if (sta_id == IWL_MVM_STATION_COUNT) { + if (!mvm_sta) { IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n"); return 0; } + sta_id = mvm_sta->sta_id; + ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast); if (ret) return ret; @@ -1617,24 +1619,13 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, u16 *phase1key) { struct iwl_mvm_sta *mvm_sta; - u8 sta_id; bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); rcu_read_lock(); - sta_id = iwl_mvm_get_key_sta_id(mvm, vif, sta); - if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT)) + mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta); + if (WARN_ON_ONCE(!mvm_sta)) goto unlock; - - if (!sta) { - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (WARN_ON(IS_ERR_OR_NULL(sta))) { - rcu_read_unlock(); - return; - } - } - - mvm_sta = iwl_mvm_sta_from_mac80211(sta); iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx); -- cgit v1.2.3-59-g8ed1b From 780e87c29e77688a453a657ba14c9b8215dbec1c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 3 Sep 2015 14:56:10 +0200 Subject: iwlwifi: mvm: add 9000 series RX processing Convert the convert the new infrastructure added by previous patches to actually use the new RX descriptor layout. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h | 6 + drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 6 +- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 372 +++++++++++++++++++++ 5 files changed, 385 insertions(+), 4 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile index 2c0d20f2a918..80c2f88386a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile @@ -1,6 +1,6 @@ obj-$(CONFIG_IWLMVM) += iwlmvm.o iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o -iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o +iwlmvm-y += utils.o rx.o rxmq.o tx.o binding.o quota.o sta.o sf.o iwlmvm-y += scan.o time-event.o rs.o iwlmvm-y += power.o coex.o coex_legacy.o iwlmvm-y += tt.o offloading.o tdls.o diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h index 75c79e13269e..0a8d162047d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -287,11 +287,17 @@ enum iwl_rx_mpdu_status { IWL_RX_MPDU_STATUS_KEY_ERROR = BIT(4), IWL_RX_MPDU_STATUS_ICV_OK = BIT(5), IWL_RX_MPDU_STATUS_MIC_OK = BIT(6), + /* TODO - verify this is the correct value */ + IWL_RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), IWL_RX_MPDU_STATUS_SEC_MASK = 0x7 << 8, IWL_RX_MPDU_STATUS_SEC_NONE = 0x0 << 8, IWL_RX_MPDU_STATUS_SEC_WEP = 0x1 << 8, IWL_RX_MPDU_STATUS_SEC_CCM = 0x2 << 8, IWL_RX_MPDU_STATUS_SEC_TKIP = 0x3 << 8, + /* TODO - define IWL_RX_MPDU_STATUS_SEC_EXT_ENC - this is a stub */ + IWL_RX_MPDU_STATUS_SEC_EXT_ENC = 0x4 << 8, + /* TODO - define IWL_RX_MPDU_STATUS_SEC_GCM - this is a stub */ + IWL_RX_MPDU_STATUS_SEC_GCM = 0x5 << 8, IWL_RX_MPDU_STATUS_DECRYPTED = BIT(11), IWL_RX_MPDU_STATUS_WEP_MATCH = BIT(12), IWL_RX_MPDU_STATUS_EXT_IV_MATCH = BIT(13), diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index df68973e4c10..0088106de95b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1135,6 +1135,9 @@ bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, int queue); void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index a5e1c8e943a1..0885b77127f0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -836,9 +836,9 @@ static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode, struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) - iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); + iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, 0); else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) - iwl_mvm_rx_rx_phy_cmd(mvm, rxb); + iwl_mvm_rx_phy_cmd_mq(mvm, rxb); else iwl_mvm_rx_common(mvm, rxb, pkt); } @@ -1489,7 +1489,7 @@ static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode, { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); + iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, queue); } static const struct iwl_op_mode_ops iwl_mvm_ops_mq = { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c new file mode 100644 index 000000000000..c67962e56a75 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -0,0 +1,372 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#include +#include +#include "iwl-trans.h" +#include "mvm.h" +#include "fw-api.h" +#include "fw-dbg.h" + +void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) +{ + mvm->ampdu_ref++; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { + spin_lock(&mvm->drv_stats_lock); + mvm->drv_rx_stats.ampdu_count++; + spin_unlock(&mvm->drv_stats_lock); + } +#endif +} + +static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, + struct napi_struct *napi, + struct sk_buff *skb, + struct ieee80211_hdr *hdr, u16 len, + u32 ampdu_status, u8 crypt_len, + struct iwl_rx_cmd_buffer *rxb) +{ + unsigned int hdrlen, fraglen; + + /* If frame is small enough to fit in skb->head, pull it completely. + * If not, only pull ieee80211_hdr (including crypto if present, and + * an additional 8 bytes for SNAP/ethertype, see below) so that + * splice() or TCP coalesce are more efficient. + * + * Since, in addition, ieee80211_data_to_8023() always pull in at + * least 8 bytes (possibly more for mesh) we can do the same here + * to save the cost of doing it later. That still doesn't pull in + * the actual IP header since the typical case has a SNAP header. + * If the latter changes (there are efforts in the standards group + * to do so) we should revisit this and ieee80211_data_to_8023(). + */ + hdrlen = (len <= skb_tailroom(skb)) ? len : + sizeof(*hdr) + crypt_len + 8; + + memcpy(skb_put(skb, hdrlen), hdr, hdrlen); + fraglen = len - hdrlen; + + if (fraglen) { + int offset = (void *)hdr + hdrlen - + rxb_addr(rxb) + rxb_offset(rxb); + + skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, + fraglen, rxb->truesize); + } + + ieee80211_rx_napi(mvm->hw, skb, napi); +} + +static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, + struct iwl_rx_mpdu_desc *desc, + struct ieee80211_rx_status *rx_status) +{ + int energy_a, energy_b, energy_c, max_energy; + + energy_a = desc->energy_a; + energy_a = energy_a ? -energy_a : S8_MIN; + energy_b = desc->energy_b; + energy_b = energy_b ? -energy_b : S8_MIN; + energy_c = desc->energy_c; + energy_c = energy_c ? -energy_c : S8_MIN; + max_energy = max(energy_a, energy_b); + max_energy = max(max_energy, energy_c); + + IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n", + energy_a, energy_b, energy_c, max_energy); + + rx_status->signal = max_energy; + rx_status->chains = 0; /* TODO: phy info */ + rx_status->chain_signal[0] = energy_a; + rx_status->chain_signal[1] = energy_b; + rx_status->chain_signal[2] = energy_c; +} + +static u32 iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, + struct ieee80211_rx_status *stats, + struct iwl_rx_mpdu_desc *desc, int queue, + u8 *crypt_len) +{ + u16 status = le16_to_cpu(desc->status); + + if (!ieee80211_has_protected(hdr->frame_control) || + (status & IWL_RX_MPDU_STATUS_SEC_MASK) == + IWL_RX_MPDU_STATUS_SEC_NONE) + return 0; + + /* TODO: handle packets encrypted with unknown alg */ + + switch (status & IWL_RX_MPDU_STATUS_SEC_MASK) { + case IWL_RX_MPDU_STATUS_SEC_CCM: + case IWL_RX_MPDU_STATUS_SEC_GCM: + /* alg is CCM: check MIC only */ + if (!(status & IWL_RX_MPDU_STATUS_MIC_OK)) + return -1; + + stats->flag |= RX_FLAG_DECRYPTED; + *crypt_len = IEEE80211_CCMP_HDR_LEN; + return 0; + case IWL_RX_MPDU_STATUS_SEC_TKIP: + /* Don't drop the frame and decrypt it in SW */ + if (!(status & IWL_RX_MPDU_RES_STATUS_TTAK_OK)) + return 0; + + *crypt_len = IEEE80211_TKIP_IV_LEN; + /* fall through if TTAK OK */ + case IWL_RX_MPDU_STATUS_SEC_WEP: + if (!(status & IWL_RX_MPDU_STATUS_ICV_OK)) + return -1; + + stats->flag |= RX_FLAG_DECRYPTED; + if ((status & IWL_RX_MPDU_STATUS_SEC_MASK) == + IWL_RX_MPDU_STATUS_SEC_WEP) + *crypt_len = IEEE80211_WEP_IV_LEN; + return 0; + case IWL_RX_MPDU_STATUS_SEC_EXT_ENC: + if (!(status & IWL_RX_MPDU_STATUS_MIC_OK)) + return -1; + stats->flag |= RX_FLAG_DECRYPTED; + return 0; + default: + IWL_ERR(mvm, "Unhandled alg: 0x%x\n", status); + } + + return 0; +} + +static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, + struct sk_buff *skb, + struct iwl_rx_mpdu_desc *desc) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); + + if (mvmvif->features & NETIF_F_RXCSUM && + desc->l3l4_flags & cpu_to_le16(IWL_RX_L3L4_IP_HDR_CSUM_OK) && + desc->l3l4_flags & cpu_to_le16(IWL_RX_L3L4_TCP_UDP_CSUM_OK)) + skb->ip_summed = CHECKSUM_UNNECESSARY; +} + +void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, int queue) +{ + struct ieee80211_rx_status *rx_status; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_rx_mpdu_desc *desc = (void *)pkt->data; + struct ieee80211_hdr *hdr = (void *)(desc + 1); + u32 len = le16_to_cpu(desc->mpdu_len); + u32 rate_n_flags = le32_to_cpu(desc->rate_n_flags); + struct ieee80211_sta *sta = NULL; + struct sk_buff *skb; + u32 ampdu_status; + u8 crypt_len = 0; + + /* Dont use dev_alloc_skb(), we'll have enough headroom once + * ieee80211_hdr pulled. + */ + skb = alloc_skb(128, GFP_ATOMIC); + if (!skb) { + IWL_ERR(mvm, "alloc_skb failed\n"); + return; + } + + rx_status = IEEE80211_SKB_RXCB(skb); + + if (iwl_mvm_rx_crypto(mvm, hdr, rx_status, desc, queue, &crypt_len)) { + kfree_skb(skb); + return; + } + + /* + * Keep packets with CRC errors (and with overrun) for monitor mode + * (otherwise the firmware discards them) but mark them as bad. + */ + if (!(desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_CRC_OK)) || + !(desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_OVERRUN_OK))) { + IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", + le16_to_cpu(desc->status)); + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + } + + rx_status->mactime = le64_to_cpu(desc->tsf_on_air_rise); + rx_status->device_timestamp = le32_to_cpu(desc->gp2_on_air_rise); + rx_status->band = desc->channel > 14 ? IEEE80211_BAND_5GHZ : + IEEE80211_BAND_2GHZ; + rx_status->freq = ieee80211_channel_to_frequency(desc->channel, + rx_status->band); + iwl_mvm_get_signal_strength(mvm, desc, rx_status); + + rcu_read_lock(); + + if (le16_to_cpu(desc->status) & IWL_RX_MPDU_STATUS_SRC_STA_FOUND) { + u8 id = desc->sta_id_flags & IWL_RX_MPDU_SIF_STA_ID_MASK; + + if (!WARN_ON_ONCE(id >= IWL_MVM_STATION_COUNT)) { + sta = rcu_dereference(mvm->fw_id_to_mac_id[id]); + if (IS_ERR(sta)) + sta = NULL; + } + } else if (!is_multicast_ether_addr(hdr->addr2)) { + /* + * This is fine since we prevent two stations with the same + * address from being added. + */ + sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL); + } + + if (sta) { + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + + /* + * We have tx blocked stations (with CS bit). If we heard + * frames from a blocked station on a new channel we can + * TX to it again. + */ + if (unlikely(mvm->csa_tx_block_bcn_timeout)) + iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); + + rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); + + if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) && + ieee80211_is_beacon(hdr->frame_control)) { + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_low_rssi *rssi_trig; + bool trig_check; + s32 rssi; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, + FW_DBG_TRIGGER_RSSI); + rssi_trig = (void *)trig->data; + rssi = le32_to_cpu(rssi_trig->rssi); + + trig_check = + iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif, + trig); + if (trig_check && rx_status->signal < rssi) + iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); + } + + /* TODO: multi queue TCM */ + + if (ieee80211_is_data(hdr->frame_control)) + iwl_mvm_rx_csum(sta, skb, desc); + } + + rcu_read_unlock(); + + /* + * TODO: PHY info. + * Verify we don't have the information in the MPDU descriptor and + * that it is not needed. + * Make sure for monitor mode that we are on default queue, update + * ampdu_ref and the rest of phy info then + */ + + /* Set up the HT phy flags */ + switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + break; + case RATE_MCS_CHAN_WIDTH_40: + rx_status->flag |= RX_FLAG_40MHZ; + break; + case RATE_MCS_CHAN_WIDTH_80: + rx_status->vht_flag |= RX_VHT_FLAG_80MHZ; + break; + case RATE_MCS_CHAN_WIDTH_160: + rx_status->vht_flag |= RX_VHT_FLAG_160MHZ; + break; + } + if (rate_n_flags & RATE_MCS_SGI_MSK) + rx_status->flag |= RX_FLAG_SHORT_GI; + if (rate_n_flags & RATE_HT_MCS_GF_MSK) + rx_status->flag |= RX_FLAG_HT_GF; + if (rate_n_flags & RATE_MCS_LDPC_MSK) + rx_status->flag |= RX_FLAG_LDPC; + if (rate_n_flags & RATE_MCS_HT_MSK) { + u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> + RATE_MCS_STBC_POS; + rx_status->flag |= RX_FLAG_HT; + rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; + rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; + } else if (rate_n_flags & RATE_MCS_VHT_MSK) { + u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >> + RATE_MCS_STBC_POS; + rx_status->vht_nss = + ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> + RATE_VHT_MCS_NSS_POS) + 1; + rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; + rx_status->flag |= RX_FLAG_VHT; + rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; + if (rate_n_flags & RATE_MCS_BF_MSK) + rx_status->vht_flag |= RX_VHT_FLAG_BF; + } else { + rx_status->rate_idx = + iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, + rx_status->band); + } + + /* TODO: PHY info - update ampdu queue statistics (for debugfs) */ + /* TODO: PHY info - gscan */ + + iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status, + crypt_len, rxb); +} -- cgit v1.2.3-59-g8ed1b From 585a6fccf5b843ded7c37b3090f811484d76c509 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 1 Dec 2015 13:48:18 +0200 Subject: iwlwifi: mvm: infrastructure for frame-release message Incoming hardware will send frame release notifications to the reorder buffer in order to update with the BA session status and up to date NSSN. This patch enables the API. Signed-off-by: Johannes Berg Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 8 +++++++- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 6 ++++++ 4 files changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h index 0a8d162047d9..fb6d341d6f3d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -316,6 +316,8 @@ enum iwl_rx_mpdu_sta_id_flags { IWL_RX_MPDU_SIF_FILTER_STATUS_MASK = 0xc0, }; +#define IWL_RX_REORDER_DATA_INVALID_BAID 0x7f + enum iwl_rx_mpdu_reorder_data { IWL_RX_MPDU_REORDER_NSSN_MASK = 0x00000fff, IWL_RX_MPDU_REORDER_SN_MASK = 0x00fff000, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 0088106de95b..86409c5964cc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1138,6 +1138,8 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, int queue); +void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, int queue); void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 0885b77127f0..2debce3b839b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -822,6 +822,8 @@ static void iwl_mvm_rx(struct iwl_op_mode *op_mode, if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); + else if (pkt->hdr.cmd == FRAME_RELEASE) + iwl_mvm_rx_frame_release(mvm, rxb, 0); else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) iwl_mvm_rx_rx_phy_cmd(mvm, rxb); else @@ -1488,8 +1490,12 @@ static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode, unsigned int queue) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + struct iwl_rx_packet *pkt = rxb_addr(rxb); - iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, queue); + if (unlikely(pkt->hdr.cmd == FRAME_RELEASE)) + iwl_mvm_rx_frame_release(mvm, rxb, queue); + else + iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, queue); } static const struct iwl_op_mode_ops iwl_mvm_ops_mq = { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index c67962e56a75..e2a872deb668 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -370,3 +370,9 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status, crypt_len, rxb); } + +void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, int queue) +{ + /* TODO */ +} -- cgit v1.2.3-59-g8ed1b From e8f0c4d87c391f16756e066323d4edf1fd567213 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 16 Dec 2015 13:42:17 +0200 Subject: iwlwifi: mvm: dump more registers upon error These registers can help to debug PHY issues. Since this adds a significant amount of work to the debug collection phase, dump the periphery registers only if the firmware is stopped. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 29 ++++++++++++++----------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index a6985da9c4a8..29d54ec8ebea 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -349,6 +349,7 @@ static const struct { { .start = 0x00a04560, .end = 0x00a0457c }, { .start = 0x00a04590, .end = 0x00a04598 }, { .start = 0x00a045c0, .end = 0x00a045f4 }, + { .start = 0x00a44000, .end = 0x00a7bf80 }, }; static u32 iwl_dump_prph(struct iwl_trans *trans, @@ -400,7 +401,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) struct iwl_fw_error_dump_trigger_desc *dump_trig; struct iwl_mvm_dump_ptrs *fw_error_dump; u32 sram_len, sram_ofs; - u32 file_len, fifo_data_len = 0; + u32 file_len, fifo_data_len = 0, prph_len = 0; u32 smem_len = mvm->cfg->smem_len; u32 sram2_len = mvm->cfg->dccm2_len; bool monitor_dump_only = false; @@ -460,12 +461,24 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) sizeof(*dump_data) + sizeof(struct iwl_fw_error_dump_fifo); } + + /* Make room for PRPH registers */ + for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { + /* The range includes both boundaries */ + int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - + iwl_prph_dump_addr[i].start + 4; + + prph_len += sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_prph) + + num_bytes_in_chunk; + } } file_len = sizeof(*dump_file) + sizeof(*dump_data) * 2 + sram_len + sizeof(*dump_mem) + fifo_data_len + + prph_len + sizeof(*dump_info); /* Make room for the SMEM, if it exists */ @@ -489,17 +502,6 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) sizeof(*dump_info); } - /* Make room for PRPH registers */ - for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { - /* The range includes both boundaries */ - int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - - iwl_prph_dump_addr[i].start + 4; - - file_len += sizeof(*dump_data) + - sizeof(struct iwl_fw_error_dump_prph) + - num_bytes_in_chunk; - } - /* * In 8000 HW family B-step include the ICCM (which resides separately) */ @@ -625,7 +627,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) } dump_data = iwl_fw_error_next_data(dump_data); - iwl_dump_prph(mvm->trans, &dump_data); + if (prph_len) + iwl_dump_prph(mvm->trans, &dump_data); dump_trans_data: fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans, -- cgit v1.2.3-59-g8ed1b From 8d0e4f8f737d808b9fa296813470f34559a9ac00 Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Thu, 17 Dec 2015 13:44:06 +0200 Subject: iwlwifi: Update PCI IDs for 8000 and 9000 series A new PCI IDs update to the 8000 and 9000 series. type=feature Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 4f94be916b9a..af106513d38e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -471,19 +471,20 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0000, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0000, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x8010, iwl8265_2ac_cfg)}, /* 9000 Series */ {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl5165_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x2010, iwl5165_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x0A10, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x0010, iwl9260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0000, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0000, iwl5165_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x0310, iwl5165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0510, iwl9260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0710, iwl9260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, 0x0210, iwl5165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0510, iwl5165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0710, iwl5165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, 0x0210, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x0410, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x0610, iwl9260_2ac_cfg)}, #endif /* CONFIG_IWLMVM */ -- cgit v1.2.3-59-g8ed1b From 8be30c13ebafdf743ab638a83095715a01bc1071 Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Wed, 16 Dec 2015 00:32:07 +0200 Subject: iwlwifi: mvm: Change number of associated stations when station becomes associated Currently, the number of associated stations gets updated when adding a new station or removing it. This is incorrect as it's possible that a station was inserted before it was associated Fix this by increasing/decreasing ap_assoc_sta_count whenever a station transitions in/out the associated state. Signed-off-by: Ayala Beker Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 14 ++++++++------ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 5 ----- 2 files changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 53415dc23ff4..296b9c5cd1be 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2248,7 +2248,6 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); /* @@ -2264,11 +2263,6 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], ERR_PTR(-ENOENT)); - if (mvm_sta->vif->type == NL80211_IFTYPE_AP) { - mvmvif->ap_assoc_sta_count--; - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - } - mutex_unlock(&mvm->mutex); } @@ -2380,6 +2374,10 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, ret = 0; } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) { + if (vif->type == NL80211_IFTYPE_AP) { + mvmvif->ap_assoc_sta_count++; + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + } ret = iwl_mvm_update_sta(mvm, vif, sta); if (ret == 0) iwl_mvm_rs_rate_init(mvm, sta, @@ -2406,6 +2404,10 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, ret = 0; } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) { + if (vif->type == NL80211_IFTYPE_AP) { + mvmvif->ap_assoc_sta_count--; + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); + } ret = 0; } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_NONE) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 2c9675b45350..b556e33658d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -278,11 +278,6 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, if (sta_id == IWL_MVM_STATION_COUNT) return -ENOSPC; - if (vif->type == NL80211_IFTYPE_AP) { - mvmvif->ap_assoc_sta_count++; - iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); - } - spin_lock_init(&mvm_sta->lock); mvm_sta->sta_id = sta_id; -- cgit v1.2.3-59-g8ed1b From a3f7ba5c8825879cc76110bc8dcadf92a6d5fa8e Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 11 Nov 2015 17:23:59 +0200 Subject: iwlwifi: update key params on d0i3 entrance/exit In order to let the fw do offloading properly, we need to provide various key data (e.g. PN). Configure the params on d0i3 entrance, and update them back on d0i3 exit. Since d3 code is now called in d0i3 which requires runtime pm only, make d3.0 depend on CONFIG_PM (rather than CONFIG_PM_SLEEP), and add required #ifdefs and wrappers where needed, so both CONFIG_PM=n and CONFIG_PM_RUNTIME=n configurations will build correctly. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 84 +++++++++++++++++++++---- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 31 ++++++--- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 64 +++++++++++-------- 4 files changed, 136 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile index 80c2f88386a5..23e7e2937566 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile @@ -7,6 +7,6 @@ iwlmvm-y += tt.o offloading.o tdls.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o iwlmvm-y += tof.o fw-dbg.o -iwlmvm-$(CONFIG_PM_SLEEP) += d3.o +iwlmvm-$(CONFIG_PM) += d3.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 3e6b6d626c25..6ac40727541e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -855,15 +855,38 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, return 0; } +static void +iwl_mvm_iter_d0i3_ap_keys(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + void (*iter)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *data), + void *data) +{ + struct ieee80211_sta *ap_sta; + + rcu_read_lock(); + + ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id]); + if (IS_ERR_OR_NULL(ap_sta)) + goto out; + + ieee80211_iter_keys_rcu(mvm->hw, vif, iter, data); +out: + rcu_read_unlock(); +} + int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool configure_keys, + bool d0i3, u32 cmd_flags) { struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; struct wowlan_key_data key_data = { - .configure_keys = configure_keys, + .configure_keys = !d0i3, .use_rsc_tsc = false, .tkip = &tkip_cmd, .use_tkip = false, @@ -876,15 +899,28 @@ int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, return -ENOMEM; /* - * Note that currently we don't propagate cmd_flags - * to the iterator. In case of key_data.configure_keys, - * all the configured commands are SYNC, and - * iwl_mvm_wowlan_program_keys() will take care of - * locking/unlocking mvm->mutex. + * if we have to configure keys, call ieee80211_iter_keys(), + * as we need non-atomic context in order to take the + * required locks. + * for the d0i3 we can't use ieee80211_iter_keys(), as + * taking (almost) any mutex might result in deadlock. */ - ieee80211_iter_keys(mvm->hw, vif, - iwl_mvm_wowlan_program_keys, - &key_data); + if (!d0i3) { + /* + * Note that currently we don't propagate cmd_flags + * to the iterator. In case of key_data.configure_keys, + * all the configured commands are SYNC, and + * iwl_mvm_wowlan_program_keys() will take care of + * locking/unlocking mvm->mutex. + */ + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_wowlan_program_keys, + &key_data); + } else { + iwl_mvm_iter_d0i3_ap_keys(mvm, vif, + iwl_mvm_wowlan_program_keys, + &key_data); + } if (key_data.error) { ret = -EIO; @@ -909,7 +945,8 @@ int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, goto out; } - if (mvmvif->rekey_data.valid) { + /* configure rekey data only if offloaded rekey is supported (d3) */ + if (mvmvif->rekey_data.valid && !d0i3) { memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck, NL80211_KCK_LEN); @@ -956,7 +993,7 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, * that isn't really a problem though. */ mutex_unlock(&mvm->mutex); - ret = iwl_mvm_wowlan_config_key_params(mvm, vif, true, + ret = iwl_mvm_wowlan_config_key_params(mvm, vif, false, CMD_ASYNC); mutex_lock(&mvm->mutex); if (ret) @@ -1727,6 +1764,29 @@ out_unlock: return false; } +void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_wowlan_status *status) +{ + struct iwl_mvm_d3_gtk_iter_data gtkdata = { + .status = status, + }; + + /* + * rekey handling requires taking locks that can't be taken now. + * however, d0i3 doesn't offload rekey, so we're fine. + */ + if (WARN_ON_ONCE(status->num_of_gtk_rekeys)) + return; + + /* find last GTK that we used initially, if any */ + gtkdata.find_phase = true; + iwl_mvm_iter_d0i3_ap_keys(mvm, vif, iwl_mvm_d3_update_keys, >kdata); + + gtkdata.find_phase = false; + iwl_mvm_iter_d0i3_ap_keys(mvm, vif, iwl_mvm_d3_update_keys, >kdata); +} + struct iwl_mvm_nd_query_results { u32 matched_profiles; struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES]; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 86409c5964cc..287c16250570 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -405,7 +405,7 @@ struct iwl_mvm_vif { */ struct iwl_mvm_phy_ctxt *phy_ctxt; -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM /* WoWLAN GTK rekey data */ struct { u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; @@ -738,7 +738,7 @@ struct iwl_mvm { struct ieee80211_vif *p2p_device_vif; -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM struct wiphy_wowlan_support wowlan; int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; @@ -1278,10 +1278,6 @@ static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm) /* D3 (WoWLAN, NetDetect) */ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); int iwl_mvm_resume(struct ieee80211_hw *hw); -int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool configure_keys, - u32 cmd_flags); void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled); void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1292,10 +1288,31 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int idx); extern const struct file_operations iwl_dbgfs_d3_test_ops; -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM +int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool host_awake, + u32 cmd_flags); +void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_wowlan_status *status); void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif); #else +static inline int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool host_awake, + u32 cmd_flags) +{ + return 0; +} + +static inline void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_wowlan_status *status) +{ +} + static inline void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 2debce3b839b..89ea70deeb84 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1076,6 +1076,7 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode) struct iwl_d0i3_iter_data { struct iwl_mvm *mvm; + struct ieee80211_vif *connected_vif; u8 ap_sta_id; u8 vif_count; u8 offloading_tid; @@ -1167,6 +1168,12 @@ static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac, */ data->ap_sta_id = mvmvif->ap_sta_id; data->vif_count++; + + /* + * no new commands can be sent at this stage, so it's safe + * to save the vif pointer during d0i3 entrance. + */ + data->connected_vif = vif; } static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, @@ -1261,6 +1268,10 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) /* configure wowlan configuration only if needed */ if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) { + iwl_mvm_wowlan_config_key_params(mvm, + d0i3_iter_data.connected_vif, + true, flags); + iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, &d0i3_iter_data); @@ -1290,25 +1301,30 @@ static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac, iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags); } -struct iwl_mvm_wakeup_reason_iter_data { +struct iwl_mvm_d0i3_exit_work_iter_data { struct iwl_mvm *mvm; + struct iwl_wowlan_status *status; u32 wakeup_reasons; }; -static void iwl_mvm_d0i3_wakeup_reason_iter(void *_data, u8 *mac, - struct ieee80211_vif *vif) +static void iwl_mvm_d0i3_exit_work_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) { - struct iwl_mvm_wakeup_reason_iter_data *data = _data; + struct iwl_mvm_d0i3_exit_work_iter_data *data = _data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u32 reasons = data->wakeup_reasons; - if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc && - data->mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) { - if (data->wakeup_reasons & - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH) - iwl_mvm_connection_loss(data->mvm, vif, "D0i3"); - else - ieee80211_beacon_loss(vif); - } + /* consider only the relevant station interface */ + if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc || + data->mvm->d0i3_ap_sta_id != mvmvif->ap_sta_id) + return; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH) + iwl_mvm_connection_loss(data->mvm, vif, "D0i3"); + else if (reasons & IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON) + ieee80211_beacon_loss(vif); + else + iwl_mvm_d0i3_update_keys(data->mvm, vif, data->status); } void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq) @@ -1374,9 +1390,13 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) .id = WOWLAN_GET_STATUSES, .flags = CMD_HIGH_PRIO | CMD_WANT_SKB, }; + struct iwl_mvm_d0i3_exit_work_iter_data iter_data = { + .mvm = mvm, + }; + struct iwl_wowlan_status *status; int ret; - u32 handled_reasons, wakeup_reasons = 0; + u32 wakeup_reasons = 0; __le16 *qos_seq = NULL; mutex_lock(&mvm->mutex); @@ -1393,18 +1413,12 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons); - handled_reasons = IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | - IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; - if (wakeup_reasons & handled_reasons) { - struct iwl_mvm_wakeup_reason_iter_data data = { - .mvm = mvm, - .wakeup_reasons = wakeup_reasons, - }; - - ieee80211_iterate_active_interfaces( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_d0i3_wakeup_reason_iter, &data); - } + iter_data.wakeup_reasons = wakeup_reasons; + iter_data.status = status; + ieee80211_iterate_active_interfaces(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_d0i3_exit_work_iter, + &iter_data); out: iwl_mvm_d0i3_enable_tx(mvm, qos_seq); -- cgit v1.2.3-59-g8ed1b From a440f1aa74da9cb1a77afcfadb12e1d4a78e7e02 Mon Sep 17 00:00:00 2001 From: Saurabh Sengar Date: Mon, 21 Dec 2015 00:29:30 +0530 Subject: NFC: add rx delay sysfs parameter for nfcsim workqueue added the rx delay parameter as a device tunable parameter. Signed-off-by: Saurabh Sengar Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcsim.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c index 26ac9e5fa1ab..93aaca586858 100644 --- a/drivers/nfc/nfcsim.c +++ b/drivers/nfc/nfcsim.c @@ -32,6 +32,8 @@ #define NFCSIM_POLL_TARGET 2 #define NFCSIM_POLL_DUAL (NFCSIM_POLL_INITIATOR | NFCSIM_POLL_TARGET) +#define RX_DEFAULT_DELAY 5 + struct nfcsim { struct nfc_dev *nfc_dev; @@ -51,6 +53,8 @@ struct nfcsim { u8 initiator; + u32 rx_delay; + data_exchange_cb_t cb; void *cb_context; @@ -320,10 +324,9 @@ static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target, * If packet transmission occurs immediately between them, we have a * non-stop flow of several tens of thousands SYMM packets per second * and a burning cpu. - * - * TODO: Add support for a sysfs entry to control this delay. */ - queue_delayed_work(wq, &peer->recv_work, msecs_to_jiffies(5)); + queue_delayed_work(wq, &peer->recv_work, + msecs_to_jiffies(dev->rx_delay)); mutex_unlock(&peer->lock); @@ -461,6 +464,7 @@ static struct nfcsim *nfcsim_init_dev(void) if (rc) goto free_nfc_dev; + dev->rx_delay = RX_DEFAULT_DELAY; return dev; free_nfc_dev: -- cgit v1.2.3-59-g8ed1b From 23ba93403b29f828feb29c06397355213a5af4b5 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 17 Dec 2015 11:55:13 +0200 Subject: iwlwifi: remove unused parameter from grab_nic_access All the callers used silent = false. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/main.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/dvm/tt.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 14 ++++++------- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 7 +++---- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 27 +++++++++++-------------- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 2 +- 7 files changed, 28 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index 94bc66adef2f..f62c2d727ddb 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -429,7 +429,7 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base, ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32)); /* Make sure device is powered up for SRAM reads */ - if (!iwl_trans_grab_nic_access(priv->trans, false, ®_flags)) + if (!iwl_trans_grab_nic_access(priv->trans, ®_flags)) return; /* Set starting address; reads will auto-increment */ @@ -1731,7 +1731,7 @@ static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, ptr = base + EVENT_START_OFFSET + (start_idx * event_size); /* Make sure device is powered up for SRAM reads */ - if (!iwl_trans_grab_nic_access(trans, false, ®_flags)) + if (!iwl_trans_grab_nic_access(trans, ®_flags)) return pos; /* Set starting address; reads will auto-increment */ diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c index 7decfc5677c6..5b73492e7ff7 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c @@ -184,7 +184,7 @@ static void iwl_tt_check_exit_ct_kill(unsigned long data) priv->thermal_throttle.ct_kill_toggle = true; } iwl_read32(priv->trans, CSR_UCODE_DRV_GP1); - if (iwl_trans_grab_nic_access(priv->trans, false, &flags)) + if (iwl_trans_grab_nic_access(priv->trans, &flags)) iwl_trans_release_nic_access(priv->trans, &flags); /* Reschedule the ct_kill timer to occur in diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index 07ad5c80e6ae..32c8f84ae519 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -82,7 +82,7 @@ u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) { u32 value = 0x5a5a5a5a; unsigned long flags; - if (iwl_trans_grab_nic_access(trans, false, &flags)) { + if (iwl_trans_grab_nic_access(trans, &flags)) { value = iwl_read32(trans, reg); iwl_trans_release_nic_access(trans, &flags); } @@ -95,7 +95,7 @@ void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) { unsigned long flags; - if (iwl_trans_grab_nic_access(trans, false, &flags)) { + if (iwl_trans_grab_nic_access(trans, &flags)) { iwl_write32(trans, reg, value); iwl_trans_release_nic_access(trans, &flags); } @@ -138,7 +138,7 @@ u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs) unsigned long flags; u32 val = 0x5a5a5a5a; - if (iwl_trans_grab_nic_access(trans, false, &flags)) { + if (iwl_trans_grab_nic_access(trans, &flags)) { val = iwl_read_prph_no_grab(trans, ofs); iwl_trans_release_nic_access(trans, &flags); } @@ -150,7 +150,7 @@ void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) { unsigned long flags; - if (iwl_trans_grab_nic_access(trans, false, &flags)) { + if (iwl_trans_grab_nic_access(trans, &flags)) { iwl_write_prph_no_grab(trans, ofs, val); iwl_trans_release_nic_access(trans, &flags); } @@ -176,7 +176,7 @@ void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) { unsigned long flags; - if (iwl_trans_grab_nic_access(trans, false, &flags)) { + if (iwl_trans_grab_nic_access(trans, &flags)) { iwl_write_prph_no_grab(trans, ofs, iwl_read_prph_no_grab(trans, ofs) | mask); @@ -190,7 +190,7 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, { unsigned long flags; - if (iwl_trans_grab_nic_access(trans, false, &flags)) { + if (iwl_trans_grab_nic_access(trans, &flags)) { iwl_write_prph_no_grab(trans, ofs, (iwl_read_prph_no_grab(trans, ofs) & mask) | bits); @@ -204,7 +204,7 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) unsigned long flags; u32 val; - if (iwl_trans_grab_nic_access(trans, false, &flags)) { + if (iwl_trans_grab_nic_access(trans, &flags)) { val = iwl_read_prph_no_grab(trans, ofs); iwl_write_prph_no_grab(trans, ofs, (val & ~mask)); iwl_trans_release_nic_access(trans, &flags); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 43a48746d731..290d538a3190 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -652,8 +652,7 @@ struct iwl_trans_ops { void (*configure)(struct iwl_trans *trans, const struct iwl_trans_config *trans_cfg); void (*set_pmi)(struct iwl_trans *trans, bool state); - bool (*grab_nic_access)(struct iwl_trans *trans, bool silent, - unsigned long *flags); + bool (*grab_nic_access)(struct iwl_trans *trans, unsigned long *flags); void (*release_nic_access)(struct iwl_trans *trans, unsigned long *flags); void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask, @@ -1170,9 +1169,9 @@ iwl_trans_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value) trans->ops->set_bits_mask(trans, reg, mask, value); } -#define iwl_trans_grab_nic_access(trans, silent, flags) \ +#define iwl_trans_grab_nic_access(trans, flags) \ __cond_lock(nic_access, \ - likely((trans)->ops->grab_nic_access(trans, silent, flags))) + likely((trans)->ops->grab_nic_access(trans, flags))) static inline void __releases(nic_access) iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index 29d54ec8ebea..f406c76b4302 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -122,7 +122,7 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, unsigned long flags; int i, j; - if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) + if (!iwl_trans_grab_nic_access(mvm->trans, &flags)) return; /* Pull RXF data from all RXFs */ @@ -359,7 +359,7 @@ static u32 iwl_dump_prph(struct iwl_trans *trans, unsigned long flags; u32 prph_len = 0, i; - if (!iwl_trans_grab_nic_access(trans, false, &flags)) + if (!iwl_trans_grab_nic_access(trans, &flags)) return 0; for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index e8041907e7e2..d44e7afad593 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1505,8 +1505,8 @@ static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) clear_bit(STATUS_TPOWER_PMI, &trans->status); } -static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, - unsigned long *flags) +static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, + unsigned long *flags) { int ret; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1547,14 +1547,11 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); if (unlikely(ret < 0)) { iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI); - if (!silent) { - u32 val = iwl_read32(trans, CSR_GP_CNTRL); - WARN_ONCE(1, - "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n", - val); - spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags); - return false; - } + WARN_ONCE(1, + "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n", + iwl_read32(trans, CSR_GP_CNTRL)); + spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags); + return false; } out: @@ -1602,7 +1599,7 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, int offs, ret = 0; u32 *vals = buf; - if (iwl_trans_grab_nic_access(trans, false, &flags)) { + if (iwl_trans_grab_nic_access(trans, &flags)) { iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr); for (offs = 0; offs < dwords; offs++) vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT); @@ -1620,7 +1617,7 @@ static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, int offs, ret = 0; const u32 *vals = buf; - if (iwl_trans_grab_nic_access(trans, false, &flags)) { + if (iwl_trans_grab_nic_access(trans, &flags)) { iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr); for (offs = 0; offs < dwords; offs++) iwl_write32(trans, HBUS_TARG_MEM_WDAT, @@ -2246,7 +2243,7 @@ static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans, __le32 *val; int i; - if (!iwl_trans_grab_nic_access(trans, false, &flags)) + if (!iwl_trans_grab_nic_access(trans, &flags)) return 0; (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FH_REGS); @@ -2273,7 +2270,7 @@ iwl_trans_pci_dump_marbh_monitor(struct iwl_trans *trans, unsigned long flags; u32 i; - if (!iwl_trans_grab_nic_access(trans, false, &flags)) + if (!iwl_trans_grab_nic_access(trans, &flags)) return 0; iwl_write_prph_no_grab(trans, MON_DMARB_RD_CTL_ADDR, 0x1); @@ -2658,7 +2655,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, goto out_pci_disable_msi; } - if (iwl_trans_grab_nic_access(trans, false, &flags)) { + if (iwl_trans_grab_nic_access(trans, &flags)) { u32 hw_step; hw_step = iwl_read_prph_no_grab(trans, WFPM_CTRL_REG); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index a85ae1002d97..5262028b5505 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -770,7 +770,7 @@ static void iwl_pcie_tx_stop_fh(struct iwl_trans *trans) spin_lock(&trans_pcie->irq_lock); - if (!iwl_trans_grab_nic_access(trans, false, &flags)) + if (!iwl_trans_grab_nic_access(trans, &flags)) goto out; /* Stop each Tx DMA channel */ -- cgit v1.2.3-59-g8ed1b From 5d4e8d6463dd7a7fb4f7f0bd1a045036c30b3bed Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 17 Dec 2015 10:41:49 +0200 Subject: iwlwifi: fix printf specifier Smatch warned about a bad specifier being used. Fix that. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c index b395854a94d9..c15f5be85197 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c @@ -454,11 +454,11 @@ static void iwl_eeprom_enhanced_txpower(struct device *dev, TXP_CHECK_AND_PRINT(COMMON_TYPE), txp->flags); IWL_DEBUG_EEPROM(dev, - "\t\t chain_A: 0x%02x chain_B: 0X%02x chain_C: 0X%02x\n", + "\t\t chain_A: %d chain_B: %d chain_C: %d\n", txp->chain_a_max, txp->chain_b_max, txp->chain_c_max); IWL_DEBUG_EEPROM(dev, - "\t\t MIMO2: 0x%02x MIMO3: 0x%02x High 20_on_40: 0x%02x Low 20_on_40: 0x%02x\n", + "\t\t MIMO2: %d MIMO3: %d High 20_on_40: 0x%02x Low 20_on_40: 0x%02x\n", txp->mimo2_max, txp->mimo3_max, ((txp->delta_20_in_40 & 0xf0) >> 4), (txp->delta_20_in_40 & 0x0f)); -- cgit v1.2.3-59-g8ed1b From e5d15cb530082cc13a6c9457eddd6f75b0f4de65 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 17 Dec 2015 13:02:56 +0200 Subject: iwlwifi: bail out in case of bad trans state In case of bad trans state (i.e. fw is not loaded) bail out immediately instead of calling the trans, which might not be fully initialized yet. Also add WARN_ON_ONCE to help debugging where the errorneous call is coming from. Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 290d538a3190..81b7cb71e001 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1001,8 +1001,10 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) return -EIO; - if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) { IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + return -EIO; + } return trans->ops->tx(trans, skb, dev_cmd, queue); } @@ -1010,8 +1012,10 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue, int ssn, struct sk_buff_head *skbs) { - if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) { IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + return; + } trans->ops->reclaim(trans, queue, ssn, skbs); } @@ -1029,8 +1033,10 @@ iwl_trans_txq_enable_cfg(struct iwl_trans *trans, int queue, u16 ssn, { might_sleep(); - if (unlikely((trans->state != IWL_TRANS_FW_ALIVE))) + if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) { IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + return; + } trans->ops->txq_enable(trans, queue, ssn, cfg, queue_wdg_timeout); } @@ -1070,8 +1076,10 @@ static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans, unsigned long txqs, bool freeze) { - if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) { IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + return; + } if (trans->ops->freeze_txq_timer) trans->ops->freeze_txq_timer(trans, txqs, freeze); @@ -1080,8 +1088,10 @@ static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans, static inline void iwl_trans_block_txq_ptrs(struct iwl_trans *trans, bool block) { - if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) { IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + return; + } if (trans->ops->block_txq_ptrs) trans->ops->block_txq_ptrs(trans, block); @@ -1090,8 +1100,10 @@ static inline void iwl_trans_block_txq_ptrs(struct iwl_trans *trans, static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans, u32 txqs) { - if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) { IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + return -EIO; + } return trans->ops->wait_tx_queue_empty(trans, txqs); } -- cgit v1.2.3-59-g8ed1b From 07146e2ea8d878d3bfb8d7d3424350d447e0166f Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Tue, 3 Nov 2015 11:35:02 -0800 Subject: fm10k: don't initialize fm10k_workqueue at global level Cleans up checkpatch GLOBAL_INITIALIZERS error Signed-off-by: Bruce Allan Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_main.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index 75ff1092b7ee..b243c3cbe68f 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -42,7 +42,7 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); /* single workqueue for entire fm10k driver */ -struct workqueue_struct *fm10k_workqueue = NULL; +struct workqueue_struct *fm10k_workqueue; /** * fm10k_init_module - Driver Registration Routine @@ -56,8 +56,7 @@ static int __init fm10k_init_module(void) pr_info("%s\n", fm10k_copyright); /* create driver workqueue */ - if (!fm10k_workqueue) - fm10k_workqueue = create_workqueue("fm10k"); + fm10k_workqueue = create_workqueue("fm10k"); fm10k_dbg_init(); @@ -80,7 +79,6 @@ static void __exit fm10k_exit_module(void) /* destroy driver workqueue */ flush_workqueue(fm10k_workqueue); destroy_workqueue(fm10k_workqueue); - fm10k_workqueue = NULL; } module_exit(fm10k_exit_module); -- cgit v1.2.3-59-g8ed1b From 8c2a029c7eff14510fed04cef2848c6d21ed92dd Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 9 Nov 2015 14:04:08 -0800 Subject: fm10k: correctly pack TLV structures and explain reasoning The TLV format for little endian structures is actually 4 byte aligned copy. To this end, we need to add an additional __aligned(4) marker along with __packed to ensure that these structures are actually 4 byte aligned and packed correctly. Use of just __packed will not work as this will result in 1byte alignment which is incorrect. Add a comment explaining the reasoning behind why these structures need the special treatment. Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pf.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h index a8fc512a2416..337859260b9b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h @@ -74,6 +74,11 @@ enum fm10k_pf_tlv_attr_id_v1 { #define FM10K_MSG_UPDATE_PVID_PVID_SHIFT 16 #define FM10K_MSG_UPDATE_PVID_PVID_SIZE 16 +/* The following data structures are overlayed directly onto TLV mailbox + * messages, and must not break 4 byte alignment. Ensure the structures line + * up correctly as per their TLV definition. + */ + struct fm10k_mac_update { __le32 mac_lower; __le16 mac_upper; @@ -81,26 +86,26 @@ struct fm10k_mac_update { __le16 glort; u8 flags; u8 action; -} __packed; +} __aligned(4) __packed; struct fm10k_global_table_data { __le32 used; __le32 avail; -} __packed; +} __aligned(4) __packed; struct fm10k_swapi_error { __le32 status; struct fm10k_global_table_data mac; struct fm10k_global_table_data nexthop; struct fm10k_global_table_data ffu; -} __packed; +} __aligned(4) __packed; struct fm10k_swapi_1588_timestamp { __le64 egress; __le64 ingress; __le16 dglort; __le16 sglort; -} __packed; +} __aligned(4) __packed; s32 fm10k_msg_lport_map_pf(struct fm10k_hw *, u32 **, struct fm10k_mbx_info *); extern const struct fm10k_tlv_attr fm10k_lport_map_msg_attr[]; -- cgit v1.2.3-59-g8ed1b From 09f8a82b6abbff279f41ac2892707d3f0f32d00c Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 10 Nov 2015 09:40:30 -0800 Subject: fm10k: Cleanup exception handling for changing queues This patch is meant to cleanup the exception handling for the paths where we reset the interrupts and then reconfigure them. In all of these paths we had very different levels of exception handling. I have updated the driver so that all of the paths should result in a similar state if we fail. Specifically the driver will now unload the mailbox interrupt, free the queue vectors and MSI-X, and then detach the interface. In addition for any of the PCIe related resets I have added a check with the hw_ready function to just make sure the registers are in a readable state prior to reopening the interface. Signed-off-by: Alexander Duyck Reviewed-by: Bruce Allan Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 22 ++++++++-- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 53 +++++++++++++++++++------ 2 files changed, 59 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 83ddf362ea77..6fdb78264f9f 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1153,6 +1153,7 @@ static struct rtnl_link_stats64 *fm10k_get_stats64(struct net_device *netdev, int fm10k_setup_tc(struct net_device *dev, u8 tc) { struct fm10k_intfc *interface = netdev_priv(dev); + int err; /* Currently only the PF supports priority classes */ if (tc && (interface->hw.mac.type != fm10k_mac_pf)) @@ -1177,17 +1178,30 @@ int fm10k_setup_tc(struct net_device *dev, u8 tc) netdev_reset_tc(dev); netdev_set_num_tc(dev, tc); - fm10k_init_queueing_scheme(interface); + err = fm10k_init_queueing_scheme(interface); + if (err) + goto err_queueing_scheme; - fm10k_mbx_request_irq(interface); + err = fm10k_mbx_request_irq(interface); + if (err) + goto err_mbx_irq; - if (netif_running(dev)) - fm10k_open(dev); + err = netif_running(dev) ? fm10k_open(dev) : 0; + if (err) + goto err_open; /* flag to indicate SWPRI has yet to be updated */ interface->flags |= FM10K_FLAG_SWPRI_CONFIG; return 0; +err_open: + fm10k_mbx_free_irq(interface); +err_mbx_irq: + fm10k_clear_queueing_scheme(interface); +err_queueing_scheme: + netif_device_detach(dev); + + return err; } static int fm10k_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 020f6dce4154..202468f07ede 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -186,7 +186,13 @@ static void fm10k_reinit(struct fm10k_intfc *interface) } /* reassociate interrupts */ - fm10k_mbx_request_irq(interface); + err = fm10k_mbx_request_irq(interface); + if (err) + goto err_mbx_irq; + + err = fm10k_hw_ready(interface); + if (err) + goto err_open; /* update hardware address for VFs if perm_addr has changed */ if (hw->mac.type == fm10k_mac_vf) { @@ -206,14 +212,23 @@ static void fm10k_reinit(struct fm10k_intfc *interface) /* reset clock */ fm10k_ts_reset(interface); - if (netif_running(netdev)) - fm10k_open(netdev); + err = netif_running(netdev) ? fm10k_open(netdev) : 0; + if (err) + goto err_open; fm10k_iov_resume(interface->pdev); + rtnl_unlock(); + + clear_bit(__FM10K_RESETTING, &interface->state); + + return; +err_open: + fm10k_mbx_free_irq(interface); +err_mbx_irq: + fm10k_clear_queueing_scheme(interface); reinit_err: - if (err) - netif_device_detach(netdev); + netif_device_detach(netdev); rtnl_unlock(); @@ -2131,16 +2146,22 @@ static int fm10k_resume(struct pci_dev *pdev) rtnl_lock(); err = fm10k_init_queueing_scheme(interface); - if (!err) { - fm10k_mbx_request_irq(interface); - if (netif_running(netdev)) - err = fm10k_open(netdev); - } + if (err) + goto err_queueing_scheme; - rtnl_unlock(); + err = fm10k_mbx_request_irq(interface); + if (err) + goto err_mbx_irq; + err = fm10k_hw_ready(interface); if (err) - return err; + goto err_open; + + err = netif_running(netdev) ? fm10k_open(netdev) : 0; + if (err) + goto err_open; + + rtnl_unlock(); /* assume host is not ready, to prevent race with watchdog in case we * actually don't have connection to the switch @@ -2158,6 +2179,14 @@ static int fm10k_resume(struct pci_dev *pdev) netif_device_attach(netdev); return 0; +err_open: + fm10k_mbx_free_irq(interface); +err_mbx_irq: + fm10k_clear_queueing_scheme(interface); +err_queueing_scheme: + rtnl_unlock(); + + return err; } /** -- cgit v1.2.3-59-g8ed1b From 6186ddf06dd270a09ca08cc3c182d4cd58cf0218 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 16 Nov 2015 15:33:34 -0800 Subject: fm10k: use ether_addr_equal instead of memcmp When comparing MAC addresses, use ether_addr_equal instead of memcmp to ETH_ALEN length. Found and replaced using the following sed: sed -e 's/memcmp\x28\(.*\), ETH_ALEN\x29/!ether_addr_equal\x28\1\x29/' Reported-by: Bruce Allan Signed-off-by: Jacob Keller Reviewed-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 2 +- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 2 +- drivers/net/ethernet/intel/fm10k/fm10k_tlv.c | 2 +- drivers/net/ethernet/intel/fm10k/fm10k_vf.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 202468f07ede..9c6ed8855abe 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -1191,7 +1191,7 @@ static s32 fm10k_mbx_mac_addr(struct fm10k_hw *hw, u32 **results, /* MAC was changed so we need reset */ if (is_valid_ether_addr(hw->mac.perm_addr) && - memcmp(hw->mac.perm_addr, hw->mac.addr, ETH_ALEN)) + !ether_addr_equal(hw->mac.perm_addr, hw->mac.addr)) interface->flags |= FM10K_FLAG_RESET_REQUESTED; /* VLAN override was changed, or default VLAN changed */ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 808307e67718..7dd7ca8fa2bb 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -1250,7 +1250,7 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results, /* block attempts to set MAC for a locked device */ if (is_valid_ether_addr(vf_info->mac) && - memcmp(mac, vf_info->mac, ETH_ALEN)) + !ether_addr_equal(mac, vf_info->mac)) return FM10K_ERR_PARAM; set = !(vlan & FM10K_VLAN_CLEAR); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c index 95afb5c0c9c4..ab01bb30752f 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c @@ -755,7 +755,7 @@ parse_nested: err = fm10k_tlv_attr_get_mac_vlan( results[FM10K_TEST_MSG_MAC_ADDR], result_mac, &result_vlan); - if (!err && memcmp(test_mac, result_mac, ETH_ALEN)) + if (!err && !ether_addr_equal(test_mac, result_mac)) err = FM10K_ERR_INVALID_VALUE; if (!err && test_vlan != result_vlan) err = FM10K_ERR_INVALID_VALUE; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c index 5445c0fab49f..f1dc6e818730 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c @@ -298,7 +298,7 @@ static s32 fm10k_update_uc_addr_vf(struct fm10k_hw *hw, u16 glort, /* verify we are not locked down on the MAC address */ if (is_valid_ether_addr(hw->mac.perm_addr) && - memcmp(hw->mac.perm_addr, mac, ETH_ALEN)) + !ether_addr_equal(hw->mac.perm_addr, mac)) return FM10K_ERR_PARAM; /* add bit to notify us if this is a set or clear operation */ -- cgit v1.2.3-59-g8ed1b From 4e458cfb226bf9a0e211895370f06838495b2e97 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Tue, 8 Dec 2015 15:50:34 -0800 Subject: fm10k: address operator not needed when declaring function pointers Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 58 ++++++++++++++--------------- drivers/net/ethernet/intel/fm10k/fm10k_vf.c | 38 +++++++++---------- 2 files changed, 48 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 7dd7ca8fa2bb..606c0f1e49e6 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -1867,38 +1867,38 @@ static const struct fm10k_msg_data fm10k_msg_data_pf[] = { }; static struct fm10k_mac_ops mac_ops_pf = { - .get_bus_info = &fm10k_get_bus_info_generic, - .reset_hw = &fm10k_reset_hw_pf, - .init_hw = &fm10k_init_hw_pf, - .start_hw = &fm10k_start_hw_generic, - .stop_hw = &fm10k_stop_hw_generic, - .update_vlan = &fm10k_update_vlan_pf, - .read_mac_addr = &fm10k_read_mac_addr_pf, - .update_uc_addr = &fm10k_update_uc_addr_pf, - .update_mc_addr = &fm10k_update_mc_addr_pf, - .update_xcast_mode = &fm10k_update_xcast_mode_pf, - .update_int_moderator = &fm10k_update_int_moderator_pf, - .update_lport_state = &fm10k_update_lport_state_pf, - .update_hw_stats = &fm10k_update_hw_stats_pf, - .rebind_hw_stats = &fm10k_rebind_hw_stats_pf, - .configure_dglort_map = &fm10k_configure_dglort_map_pf, - .set_dma_mask = &fm10k_set_dma_mask_pf, - .get_fault = &fm10k_get_fault_pf, - .get_host_state = &fm10k_get_host_state_pf, - .adjust_systime = &fm10k_adjust_systime_pf, - .read_systime = &fm10k_read_systime_pf, + .get_bus_info = fm10k_get_bus_info_generic, + .reset_hw = fm10k_reset_hw_pf, + .init_hw = fm10k_init_hw_pf, + .start_hw = fm10k_start_hw_generic, + .stop_hw = fm10k_stop_hw_generic, + .update_vlan = fm10k_update_vlan_pf, + .read_mac_addr = fm10k_read_mac_addr_pf, + .update_uc_addr = fm10k_update_uc_addr_pf, + .update_mc_addr = fm10k_update_mc_addr_pf, + .update_xcast_mode = fm10k_update_xcast_mode_pf, + .update_int_moderator = fm10k_update_int_moderator_pf, + .update_lport_state = fm10k_update_lport_state_pf, + .update_hw_stats = fm10k_update_hw_stats_pf, + .rebind_hw_stats = fm10k_rebind_hw_stats_pf, + .configure_dglort_map = fm10k_configure_dglort_map_pf, + .set_dma_mask = fm10k_set_dma_mask_pf, + .get_fault = fm10k_get_fault_pf, + .get_host_state = fm10k_get_host_state_pf, + .adjust_systime = fm10k_adjust_systime_pf, + .read_systime = fm10k_read_systime_pf, }; static struct fm10k_iov_ops iov_ops_pf = { - .assign_resources = &fm10k_iov_assign_resources_pf, - .configure_tc = &fm10k_iov_configure_tc_pf, - .assign_int_moderator = &fm10k_iov_assign_int_moderator_pf, + .assign_resources = fm10k_iov_assign_resources_pf, + .configure_tc = fm10k_iov_configure_tc_pf, + .assign_int_moderator = fm10k_iov_assign_int_moderator_pf, .assign_default_mac_vlan = fm10k_iov_assign_default_mac_vlan_pf, - .reset_resources = &fm10k_iov_reset_resources_pf, - .set_lport = &fm10k_iov_set_lport_pf, - .reset_lport = &fm10k_iov_reset_lport_pf, - .update_stats = &fm10k_iov_update_stats_pf, - .report_timestamp = &fm10k_iov_report_timestamp_pf, + .reset_resources = fm10k_iov_reset_resources_pf, + .set_lport = fm10k_iov_set_lport_pf, + .reset_lport = fm10k_iov_reset_lport_pf, + .update_stats = fm10k_iov_update_stats_pf, + .report_timestamp = fm10k_iov_report_timestamp_pf, }; static s32 fm10k_get_invariants_pf(struct fm10k_hw *hw) @@ -1910,7 +1910,7 @@ static s32 fm10k_get_invariants_pf(struct fm10k_hw *hw) struct fm10k_info fm10k_pf_info = { .mac = fm10k_mac_pf, - .get_invariants = &fm10k_get_invariants_pf, + .get_invariants = fm10k_get_invariants_pf, .mac_ops = &mac_ops_pf, .iov_ops = &iov_ops_pf, }; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c index f1dc6e818730..38219f5f51b2 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c @@ -563,24 +563,24 @@ static const struct fm10k_msg_data fm10k_msg_data_vf[] = { }; static struct fm10k_mac_ops mac_ops_vf = { - .get_bus_info = &fm10k_get_bus_info_generic, - .reset_hw = &fm10k_reset_hw_vf, - .init_hw = &fm10k_init_hw_vf, - .start_hw = &fm10k_start_hw_generic, - .stop_hw = &fm10k_stop_hw_vf, - .update_vlan = &fm10k_update_vlan_vf, - .read_mac_addr = &fm10k_read_mac_addr_vf, - .update_uc_addr = &fm10k_update_uc_addr_vf, - .update_mc_addr = &fm10k_update_mc_addr_vf, - .update_xcast_mode = &fm10k_update_xcast_mode_vf, - .update_int_moderator = &fm10k_update_int_moderator_vf, - .update_lport_state = &fm10k_update_lport_state_vf, - .update_hw_stats = &fm10k_update_hw_stats_vf, - .rebind_hw_stats = &fm10k_rebind_hw_stats_vf, - .configure_dglort_map = &fm10k_configure_dglort_map_vf, - .get_host_state = &fm10k_get_host_state_generic, - .adjust_systime = &fm10k_adjust_systime_vf, - .read_systime = &fm10k_read_systime_vf, + .get_bus_info = fm10k_get_bus_info_generic, + .reset_hw = fm10k_reset_hw_vf, + .init_hw = fm10k_init_hw_vf, + .start_hw = fm10k_start_hw_generic, + .stop_hw = fm10k_stop_hw_vf, + .update_vlan = fm10k_update_vlan_vf, + .read_mac_addr = fm10k_read_mac_addr_vf, + .update_uc_addr = fm10k_update_uc_addr_vf, + .update_mc_addr = fm10k_update_mc_addr_vf, + .update_xcast_mode = fm10k_update_xcast_mode_vf, + .update_int_moderator = fm10k_update_int_moderator_vf, + .update_lport_state = fm10k_update_lport_state_vf, + .update_hw_stats = fm10k_update_hw_stats_vf, + .rebind_hw_stats = fm10k_rebind_hw_stats_vf, + .configure_dglort_map = fm10k_configure_dglort_map_vf, + .get_host_state = fm10k_get_host_state_generic, + .adjust_systime = fm10k_adjust_systime_vf, + .read_systime = fm10k_read_systime_vf, }; static s32 fm10k_get_invariants_vf(struct fm10k_hw *hw) @@ -592,6 +592,6 @@ static s32 fm10k_get_invariants_vf(struct fm10k_hw *hw) struct fm10k_info fm10k_vf_info = { .mac = fm10k_mac_vf, - .get_invariants = &fm10k_get_invariants_vf, + .get_invariants = fm10k_get_invariants_vf, .mac_ops = &mac_ops_vf, }; -- cgit v1.2.3-59-g8ed1b From f329ad732b21dc86d477b47300805dd16178f9ca Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Tue, 8 Dec 2015 15:50:39 -0800 Subject: fm10k: constify fm10k_mac_ops, fm10k_iov_ops and fm10k_info structures These structures never change so declare them as const. Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 6 +++--- drivers/net/ethernet/intel/fm10k/fm10k_pf.h | 2 +- drivers/net/ethernet/intel/fm10k/fm10k_type.h | 8 ++++---- drivers/net/ethernet/intel/fm10k/fm10k_vf.c | 4 ++-- drivers/net/ethernet/intel/fm10k/fm10k_vf.h | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 606c0f1e49e6..62ccebc5f728 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -1866,7 +1866,7 @@ static const struct fm10k_msg_data fm10k_msg_data_pf[] = { FM10K_TLV_MSG_ERROR_HANDLER(fm10k_tlv_msg_error), }; -static struct fm10k_mac_ops mac_ops_pf = { +static const struct fm10k_mac_ops mac_ops_pf = { .get_bus_info = fm10k_get_bus_info_generic, .reset_hw = fm10k_reset_hw_pf, .init_hw = fm10k_init_hw_pf, @@ -1889,7 +1889,7 @@ static struct fm10k_mac_ops mac_ops_pf = { .read_systime = fm10k_read_systime_pf, }; -static struct fm10k_iov_ops iov_ops_pf = { +static const struct fm10k_iov_ops iov_ops_pf = { .assign_resources = fm10k_iov_assign_resources_pf, .configure_tc = fm10k_iov_configure_tc_pf, .assign_int_moderator = fm10k_iov_assign_int_moderator_pf, @@ -1908,7 +1908,7 @@ static s32 fm10k_get_invariants_pf(struct fm10k_hw *hw) return fm10k_sm_mbx_init(hw, &hw->mbx, fm10k_msg_data_pf); } -struct fm10k_info fm10k_pf_info = { +const struct fm10k_info fm10k_pf_info = { .mac = fm10k_mac_pf, .get_invariants = fm10k_get_invariants_pf, .mac_ops = &mac_ops_pf, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h index 337859260b9b..b2d96b45ca3c 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h @@ -133,5 +133,5 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *, u32 **, s32 fm10k_iov_msg_lport_state_pf(struct fm10k_hw *, u32 **, struct fm10k_mbx_info *); -extern struct fm10k_info fm10k_pf_info; +extern const struct fm10k_info fm10k_pf_info; #endif /* _FM10K_PF_H */ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h index 098883d2875f..bc27c757d88b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h @@ -660,10 +660,10 @@ enum fm10k_devices { }; struct fm10k_info { - enum fm10k_mac_type mac; - s32 (*get_invariants)(struct fm10k_hw *); - struct fm10k_mac_ops *mac_ops; - struct fm10k_iov_ops *iov_ops; + enum fm10k_mac_type mac; + s32 (*get_invariants)(struct fm10k_hw *); + const struct fm10k_mac_ops *mac_ops; + const struct fm10k_iov_ops *iov_ops; }; struct fm10k_hw { diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c index 38219f5f51b2..91f8d7311f3b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c @@ -562,7 +562,7 @@ static const struct fm10k_msg_data fm10k_msg_data_vf[] = { FM10K_TLV_MSG_ERROR_HANDLER(fm10k_tlv_msg_error), }; -static struct fm10k_mac_ops mac_ops_vf = { +static const struct fm10k_mac_ops mac_ops_vf = { .get_bus_info = fm10k_get_bus_info_generic, .reset_hw = fm10k_reset_hw_vf, .init_hw = fm10k_init_hw_vf, @@ -590,7 +590,7 @@ static s32 fm10k_get_invariants_vf(struct fm10k_hw *hw) return fm10k_pfvf_mbx_init(hw, &hw->mbx, fm10k_msg_data_vf, 0); } -struct fm10k_info fm10k_vf_info = { +const struct fm10k_info fm10k_vf_info = { .mac = fm10k_mac_vf, .get_invariants = fm10k_get_invariants_vf, .mac_ops = &mac_ops_vf, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.h b/drivers/net/ethernet/intel/fm10k/fm10k_vf.h index 06a99d794c99..c4439f1313a0 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.h @@ -74,5 +74,5 @@ extern const struct fm10k_tlv_attr fm10k_1588_msg_attr[]; #define FM10K_VF_MSG_1588_HANDLER(func) \ FM10K_MSG_HANDLER(FM10K_VF_MSG_ID_1588, fm10k_1588_msg_attr, func) -extern struct fm10k_info fm10k_vf_info; +extern const struct fm10k_info fm10k_vf_info; #endif /* _FM10K_VF_H */ -- cgit v1.2.3-59-g8ed1b From e6f244d484793e0ce80101eb4a523ff08ad73172 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Tue, 8 Dec 2015 15:51:04 -0800 Subject: fm10k: remove unused struct element Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_type.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h index bc27c757d88b..854ebb1906bf 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h @@ -550,7 +550,6 @@ struct fm10k_mac_ops { struct fm10k_dglort_cfg *); void (*set_dma_mask)(struct fm10k_hw *, u64); s32 (*get_fault)(struct fm10k_hw *, int, struct fm10k_fault *); - void (*request_lport_map)(struct fm10k_hw *); s32 (*adjust_systime)(struct fm10k_hw *, s32 ppb); u64 (*read_systime)(struct fm10k_hw *); }; -- cgit v1.2.3-59-g8ed1b From f355bb51794af64ef583c259469a778e606d95bb Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Tue, 8 Dec 2015 15:51:11 -0800 Subject: fm10k: use true/false for boolean get_host_state Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 9c6ed8855abe..4eb7a6fa6b0d 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -912,7 +912,7 @@ static irqreturn_t fm10k_msix_mbx_vf(int __always_unused irq, void *data) fm10k_mbx_unlock(interface); } - hw->mac.get_host_state = 1; + hw->mac.get_host_state = true; fm10k_service_event_schedule(interface); return IRQ_HANDLED; @@ -1128,7 +1128,7 @@ static irqreturn_t fm10k_msix_mbx_pf(int __always_unused irq, void *data) } /* we should validate host state after interrupt event */ - hw->mac.get_host_state = 1; + hw->mac.get_host_state = true; /* validate host state, and handle VF mailboxes in the service task */ fm10k_service_event_schedule(interface); @@ -1635,7 +1635,7 @@ void fm10k_up(struct fm10k_intfc *interface) netif_tx_start_all_queues(interface->netdev); /* kick off the service timer now */ - hw->mac.get_host_state = 1; + hw->mac.get_host_state = true; mod_timer(&interface->service_timer, jiffies); } -- cgit v1.2.3-59-g8ed1b From f632fed30f8e0c1b5c9de209f00145f516e7ad37 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Tue, 8 Dec 2015 17:20:44 -0800 Subject: fm10k: cleanup mailbox code comments etc Cleanup a number of issues with function header comments, lower-case acronyms (i.e. FIFO, TLV), duplicate comments and a stubbed-out header comment for fm10k_sm_mbx_init. Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_mbx.c | 50 +++++++++++++++++----------- drivers/net/ethernet/intel/fm10k/fm10k_mbx.h | 4 +-- drivers/net/ethernet/intel/fm10k/fm10k_tlv.h | 4 +-- 3 files changed, 34 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c index c7fea47b8909..98202c3d591c 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c @@ -57,7 +57,7 @@ static u16 fm10k_fifo_unused(struct fm10k_mbx_fifo *fifo) } /** - * fm10k_fifo_empty - Test to verify if fifo is empty + * fm10k_fifo_empty - Test to verify if FIFO is empty * @fifo: pointer to FIFO * * This function returns true if the FIFO is empty, else false @@ -72,7 +72,7 @@ static bool fm10k_fifo_empty(struct fm10k_mbx_fifo *fifo) * @fifo: pointer to FIFO * @offset: offset to add to head * - * This function returns the indices into the fifo based on head + offset + * This function returns the indices into the FIFO based on head + offset **/ static u16 fm10k_fifo_head_offset(struct fm10k_mbx_fifo *fifo, u16 offset) { @@ -84,7 +84,7 @@ static u16 fm10k_fifo_head_offset(struct fm10k_mbx_fifo *fifo, u16 offset) * @fifo: pointer to FIFO * @offset: offset to add to tail * - * This function returns the indices into the fifo based on tail + offset + * This function returns the indices into the FIFO based on tail + offset **/ static u16 fm10k_fifo_tail_offset(struct fm10k_mbx_fifo *fifo, u16 offset) { @@ -160,7 +160,7 @@ static u16 fm10k_mbx_index_len(struct fm10k_mbx_info *mbx, u16 head, u16 tail) /** * fm10k_mbx_tail_add - Determine new tail value with added offset * @mbx: pointer to mailbox - * @offset: length to add to head offset + * @offset: length to add to tail offset * * This function takes the local tail index and recomputes it for * a given length added as an offset. @@ -176,7 +176,7 @@ static u16 fm10k_mbx_tail_add(struct fm10k_mbx_info *mbx, u16 offset) /** * fm10k_mbx_tail_sub - Determine new tail value with subtracted offset * @mbx: pointer to mailbox - * @offset: length to add to head offset + * @offset: length to add to tail offset * * This function takes the local tail index and recomputes it for * a given length added as an offset. @@ -240,7 +240,7 @@ static u16 fm10k_mbx_pushed_tail_len(struct fm10k_mbx_info *mbx) } /** - * fm10k_fifo_write_copy - pulls data off of msg and places it in fifo + * fm10k_fifo_write_copy - pulls data off of msg and places it in FIFO * @fifo: pointer to FIFO * @msg: message array to populate * @tail_offset: additional offset to add to tail pointer @@ -336,6 +336,7 @@ static u16 fm10k_mbx_validate_msg_size(struct fm10k_mbx_info *mbx, u16 len) /** * fm10k_mbx_write_copy - pulls data off of Tx FIFO and places it in mbmem + * @hw: pointer to hardware structure * @mbx: pointer to mailbox * * This function will take a section of the Tx FIFO and copy it into the @@ -711,7 +712,7 @@ static bool fm10k_mbx_tx_complete(struct fm10k_mbx_info *mbx) * @hw: pointer to hardware structure * @mbx: pointer to mailbox * - * This function dequeues messages and hands them off to the tlv parser. + * This function dequeues messages and hands them off to the TLV parser. * It will return the number of messages processed when called. **/ static u16 fm10k_mbx_dequeue_rx(struct fm10k_hw *hw, @@ -924,7 +925,7 @@ static void fm10k_mbx_create_fake_disconnect_hdr(struct fm10k_mbx_info *mbx) } /** - * fm10k_mbx_create_error_msg - Generate a error message + * fm10k_mbx_create_error_msg - Generate an error message * @mbx: pointer to mailbox * @err: local error encountered * @@ -957,7 +958,6 @@ static void fm10k_mbx_create_error_msg(struct fm10k_mbx_info *mbx, s32 err) /** * fm10k_mbx_validate_msg_hdr - Validate common fields in the message header * @mbx: pointer to mailbox - * @msg: message array to read * * This function will parse up the fields in the mailbox header and return * an error if the header contains any of a number of invalid configurations @@ -1021,11 +1021,12 @@ static s32 fm10k_mbx_validate_msg_hdr(struct fm10k_mbx_info *mbx) /** * fm10k_mbx_create_reply - Generate reply based on state and remote head + * @hw: pointer to hardware structure * @mbx: pointer to mailbox * @head: acknowledgement number * * This function will generate an outgoing message based on the current - * mailbox state and the remote fifo head. It will return the length + * mailbox state and the remote FIFO head. It will return the length * of the outgoing message excluding header on success, and a negative value * on error. **/ @@ -1151,8 +1152,8 @@ static void fm10k_mbx_connect_reset(struct fm10k_mbx_info *mbx) /** * fm10k_mbx_process_connect - Process connect header + * @hw: pointer to hardware structure * @mbx: pointer to mailbox - * @msg: message array to process * * This function will read an incoming connect header and reply with the * appropriate message. It will return a value indicating the number of @@ -1198,6 +1199,7 @@ static s32 fm10k_mbx_process_connect(struct fm10k_hw *hw, /** * fm10k_mbx_process_data - Process data header + * @hw: pointer to hardware structure * @mbx: pointer to mailbox * * This function will read an incoming data header and reply with the @@ -1239,6 +1241,7 @@ static s32 fm10k_mbx_process_data(struct fm10k_hw *hw, /** * fm10k_mbx_process_disconnect - Process disconnect header + * @hw: pointer to hardware structure * @mbx: pointer to mailbox * * This function will read an incoming disconnect header and reply with the @@ -1291,6 +1294,7 @@ static s32 fm10k_mbx_process_disconnect(struct fm10k_hw *hw, /** * fm10k_mbx_process_error - Process error header + * @hw: pointer to hardware structure * @mbx: pointer to mailbox * * This function will read an incoming error header and reply with the @@ -1560,7 +1564,7 @@ static s32 fm10k_mbx_register_handlers(struct fm10k_mbx_info *mbx, * @id: ID reference for PF as it supports up to 64 PF/VF mailboxes * * This function initializes the mailbox for use. It will split the - * buffer provided an use that th populate both the Tx and Rx FIFO by + * buffer provided and use that to populate both the Tx and Rx FIFO by * evenly splitting it. In order to allow for easy masking of head/tail * the value reported in size must be a power of 2 and is reported in * DWORDs, not bytes. Any invalid values will cause the mailbox to return @@ -1637,7 +1641,7 @@ s32 fm10k_pfvf_mbx_init(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx, * fm10k_sm_mbx_create_data_hdr - Generate a mailbox header for local FIFO * @mbx: pointer to mailbox * - * This function returns a connection mailbox header + * This function returns a data mailbox header **/ static void fm10k_sm_mbx_create_data_hdr(struct fm10k_mbx_info *mbx) { @@ -1730,8 +1734,6 @@ static s32 fm10k_sm_mbx_connect(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx) fm10k_sm_mbx_create_connect_hdr(mbx, 0); fm10k_mbx_write(hw, mbx); - /* enable interrupt and notify other party of new message */ - return 0; } @@ -1775,7 +1777,7 @@ static void fm10k_sm_mbx_disconnect(struct fm10k_hw *hw, } /** - * fm10k_mbx_validate_fifo_hdr - Validate fields in the remote FIFO header + * fm10k_sm_mbx_validate_fifo_hdr - Validate fields in the remote FIFO header * @mbx: pointer to mailbox * * This function will parse up the fields in the mailbox header and return @@ -1853,7 +1855,7 @@ static void fm10k_sm_mbx_process_error(struct fm10k_mbx_info *mbx) } /** - * fm10k_sm_mbx_create_error_message - Process an error in FIFO hdr + * fm10k_sm_mbx_create_error_msg - Process an error in FIFO header * @mbx: pointer to mailbox * @err: local error encountered * @@ -1883,6 +1885,7 @@ static void fm10k_sm_mbx_create_error_msg(struct fm10k_mbx_info *mbx, s32 err) * fm10k_sm_mbx_receive - Take message from Rx mailbox FIFO and put it in Rx * @hw: pointer to hardware structure * @mbx: pointer to mailbox + * @tail: tail index of message * * This function will dequeue one message from the Rx switch manager mailbox * FIFO and place it in the Rx mailbox FIFO for processing by software. @@ -1922,6 +1925,7 @@ static s32 fm10k_sm_mbx_receive(struct fm10k_hw *hw, * fm10k_sm_mbx_transmit - Take message from Tx and put it in Tx mailbox FIFO * @hw: pointer to hardware structure * @mbx: pointer to mailbox + * @head: head index of message * * This function will dequeue one message from the Tx mailbox FIFO and place * it in the Tx switch manager mailbox FIFO for processing by hardware. @@ -1961,11 +1965,12 @@ static void fm10k_sm_mbx_transmit(struct fm10k_hw *hw, /** * fm10k_sm_mbx_create_reply - Generate reply based on state and remote head + * @hw: pointer to hardware structure * @mbx: pointer to mailbox * @head: acknowledgement number * * This function will generate an outgoing message based on the current - * mailbox state and the remote fifo head. It will return the length + * mailbox state and the remote FIFO head. It will return the length * of the outgoing message excluding header on success, and a negative value * on error. **/ @@ -2077,7 +2082,7 @@ send_reply: } /** - * fm10k_sm_mbx_process - Process mailbox switch mailbox interrupt + * fm10k_sm_mbx_process - Process switch manager mailbox interrupt * @hw: pointer to hardware structure * @mbx: pointer to mailbox * @@ -2133,7 +2138,12 @@ fifo_err: * @mbx: pointer to mailbox * @msg_data: handlers for mailbox events * - * This function for now is used to stub out the PF/SM mailbox + * This function initializes the PF/SM mailbox for use. It will split the + * buffer provided and use that to populate both the Tx and Rx FIFO by + * evenly splitting it. In order to allow for easy masking of head/tail + * the value reported in size must be a power of 2 and is reported in + * DWORDs, not bytes. Any invalid values will cause the mailbox to return + * error. **/ s32 fm10k_sm_mbx_init(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx, const struct fm10k_msg_data *msg_data) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h index c4f18a8f176c..245a0a3dc32e 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h @@ -128,11 +128,11 @@ enum fm10k_mbx_state { * The maximum message size is provided during connect to avoid * jamming the mailbox with messages that do not fit. * Err_no: Error number - Applies only to error headers - * The error number provides a indication of the type of error + * The error number provides an indication of the type of error * experienced. */ -/* macros for retriving and setting header values */ +/* macros for retrieving and setting header values */ #define FM10K_MSG_HDR_MASK(name) \ ((0x1u << FM10K_MSG_##name##_SIZE) - 1) #define FM10K_MSG_HDR_FIELD_SET(value, name) \ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h index d5ad359c1d54..e1845e0a17d8 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h @@ -38,9 +38,9 @@ struct fm10k_msg_data; * mailbox size we will provide a message with the above header and it * will be segmented and transported to the mailbox to the other side where * it is reassembled. It contains the following fields: - * Len: Length of the message in bytes excluding the message header + * Length: Length of the message in bytes excluding the message header * Flags: TBD - * Rule: These will be the message/argument types we pass + * Type/ID: These will be the message/argument types we pass */ /* message data header */ #define FM10K_TLV_ID_SHIFT 0 -- cgit v1.2.3-59-g8ed1b From 0d722ec8bf46cb6547d10e8c5d9b8b6498bc7f97 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Tue, 8 Dec 2015 17:20:49 -0800 Subject: fm10k: IS_ENABLED() is not appropriate for boolean kconfig option Tri-states need 'if IS_ENABLED()', booleans should use 'ifdef'. Signed-off-by: Bruce Allan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 6fdb78264f9f..662569d5b7c0 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -20,7 +20,7 @@ #include "fm10k.h" #include -#if IS_ENABLED(CONFIG_FM10K_VXLAN) +#ifdef CONFIG_FM10K_VXLAN #include #endif /* CONFIG_FM10K_VXLAN */ @@ -556,11 +556,11 @@ int fm10k_open(struct net_device *netdev) if (err) goto err_set_queues; -#if IS_ENABLED(CONFIG_FM10K_VXLAN) +#ifdef CONFIG_FM10K_VXLAN /* update VXLAN port configuration */ vxlan_get_rx_port(netdev); - #endif + fm10k_up(interface); return 0; -- cgit v1.2.3-59-g8ed1b From 17722e245b1eed116268338a0a05970bf797ce06 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Tue, 22 Dec 2015 19:30:18 +0530 Subject: Bluetooth: bcm203x: Remove redundant error message devm_kzalloc prints its own OOM message upon failure. Signed-off-by: Syam Sidhardhan Signed-off-by: Marcel Holtmann --- drivers/bluetooth/bcm203x.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c index 364f82b34d03..5b0ef7bbe8ac 100644 --- a/drivers/bluetooth/bcm203x.c +++ b/drivers/bluetooth/bcm203x.c @@ -178,10 +178,8 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id return -ENODEV; data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); - if (!data) { - BT_ERR("Can't allocate memory for data structure"); + if (!data) return -ENOMEM; - } data->udev = udev; data->state = BCM203X_LOAD_MINIDRV; -- cgit v1.2.3-59-g8ed1b From f71e823b8776b3ce0073087d7b8ec8a7d31fe818 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Tue, 22 Dec 2015 19:30:19 +0530 Subject: Bluetooth: bfusb: Remove redundant error message devm_kzalloc prints its own OOM message upon failure. Signed-off-by: Syam Sidhardhan Signed-off-by: Marcel Holtmann --- drivers/bluetooth/bfusb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 72d8bfabef09..c304102fa673 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -636,10 +636,8 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i /* Initialize control structure and load firmware */ data = devm_kzalloc(&intf->dev, sizeof(struct bfusb_data), GFP_KERNEL); - if (!data) { - BT_ERR("Can't allocate memory for control structure"); + if (!data) goto done; - } data->udev = udev; data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress; -- cgit v1.2.3-59-g8ed1b From 7ddb69222d0fdc0bc83b60dbb21d9864ebf2907e Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Tue, 22 Dec 2015 19:30:20 +0530 Subject: Bluetooth: bfusb: Fix the return error code -ENOMEM is the appropriate error code instead of -EIO. Signed-off-by: Syam Sidhardhan Signed-off-by: Marcel Holtmann --- drivers/bluetooth/bfusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index c304102fa673..3bf4ec60e073 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -637,7 +637,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i /* Initialize control structure and load firmware */ data = devm_kzalloc(&intf->dev, sizeof(struct bfusb_data), GFP_KERNEL); if (!data) - goto done; + return -ENOMEM; data->udev = udev; data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress; -- cgit v1.2.3-59-g8ed1b From e7bc73cbb522c1704aafc428d5d9763c44666e38 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 21 Dec 2015 11:14:21 +0100 Subject: mlxsw: core: Allow to reset temperature history via hwmon interface Add another sysfs hwmon attribute to expose possibility to reset temperature sensors history. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c | 37 +++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c index b86db967eab9..00ccf1c0dcda 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c @@ -49,7 +49,7 @@ struct mlxsw_hwmon_attr { struct device_attribute dev_attr; struct mlxsw_hwmon *hwmon; unsigned int type_index; - char name[16]; + char name[32]; }; struct mlxsw_hwmon { @@ -107,6 +107,32 @@ static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev, return sprintf(buf, "%u\n", temp_max); } +static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct mlxsw_hwmon_attr *mlwsw_hwmon_attr = + container_of(attr, struct mlxsw_hwmon_attr, dev_attr); + struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon; + char mtmp_pl[MLXSW_REG_MTMP_LEN]; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + if (val != 1) + return -EINVAL; + + mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index, true, true); + err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl); + if (err) { + dev_err(mlxsw_hwmon->bus_info->dev, "Failed to reset temp sensor history\n"); + return err; + } + return err ? err : len; +} + static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -175,6 +201,7 @@ static ssize_t mlxsw_hwmon_pwm_store(struct device *dev, enum mlxsw_hwmon_attr_type { MLXSW_HWMON_ATTR_TYPE_TEMP, MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, + MLXSW_HWMON_ATTR_TYPE_TEMP_RST, MLXSW_HWMON_ATTR_TYPE_FAN_RPM, MLXSW_HWMON_ATTR_TYPE_PWM, }; @@ -201,6 +228,12 @@ static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon, snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), "temp%u_highest", num + 1); break; + case MLXSW_HWMON_ATTR_TYPE_TEMP_RST: + mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_temp_rst_store; + mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR; + snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name), + "temp%u_reset_history", num + 1); + break; case MLXSW_HWMON_ATTR_TYPE_FAN_RPM: mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_rpm_show; mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO; @@ -254,6 +287,8 @@ static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon) MLXSW_HWMON_ATTR_TYPE_TEMP, i, i); mlxsw_hwmon_attr_add(mlxsw_hwmon, MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, i, i); + mlxsw_hwmon_attr_add(mlxsw_hwmon, + MLXSW_HWMON_ATTR_TYPE_TEMP_RST, i, i); } return 0; } -- cgit v1.2.3-59-g8ed1b From f4cee3af0dedf3270e6d615d772e3fbfa3200769 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 22 Dec 2015 09:43:07 +0100 Subject: mlxsw: core: Use devm_kzalloc to allocate mlxsw_hwmon structure KASan reported use-after-free for the hwmon structure. So fix this by using devm_kzalloc and let the core take care about freeing the memory during device dettach. Reported-by: Ido Schimmel Fixes: 89309da39 ("mlxsw: core: Implement temperature hwmon interface") Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 2 -- drivers/net/ethernet/mellanox/mlxsw/core.h | 4 ---- drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c | 9 ++------- 3 files changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index af8a48b3b3ad..22379eb8e924 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -855,7 +855,6 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, err_debugfs_init: mlxsw_core->driver->fini(mlxsw_core->driver_priv); err_driver_init: - mlxsw_hwmon_fini(mlxsw_core->hwmon); err_hwmon_init: mlxsw_emad_fini(mlxsw_core); err_emad_init: @@ -878,7 +877,6 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core) mlxsw_core_debugfs_fini(mlxsw_core); mlxsw_core->driver->fini(mlxsw_core->driver_priv); - mlxsw_hwmon_fini(mlxsw_core->hwmon); mlxsw_emad_fini(mlxsw_core); mlxsw_core->bus->fini(mlxsw_core->bus_priv); kfree(mlxsw_core->lag.mapping); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index 4833fb33ce07..a01723600f0a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -239,10 +239,6 @@ static inline int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, return 0; } -static inline void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon) -{ -} - #endif #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c index 00ccf1c0dcda..5b9364f4837d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c @@ -334,7 +334,8 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, struct device *hwmon_dev; int err; - mlxsw_hwmon = kzalloc(sizeof(*mlxsw_hwmon), GFP_KERNEL); + mlxsw_hwmon = devm_kzalloc(mlxsw_bus_info->dev, sizeof(*mlxsw_hwmon), + GFP_KERNEL); if (!mlxsw_hwmon) return -ENOMEM; mlxsw_hwmon->core = mlxsw_core; @@ -367,11 +368,5 @@ int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, err_hwmon_register: err_fans_init: err_temp_init: - kfree(mlxsw_hwmon); return err; } - -void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon) -{ - kfree(mlxsw_hwmon); -} -- cgit v1.2.3-59-g8ed1b From 547fd27241a887c1df020c5f8347e348540f0591 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 11:29:53 +0530 Subject: cxgb4: Warn if device doesn't have enough PCI bandwidth Check if the device get enough bandwidth from the entire PCI chain to satisfy its capabilities. This patch determines the PCIe device's bandwidth capabilities by reading its PCIe Link Capabilities registers and then call the pcie_get_minimum_link function to ensure that the adapter is hooked into a slot which is capable of providing the necessary bandwidth capabilities. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 80 ++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 8490c845a815..8326a7760ffd 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -4532,6 +4532,79 @@ static int init_rss(struct adapter *adap) return 0; } +static int cxgb4_get_pcie_dev_link_caps(struct adapter *adap, + enum pci_bus_speed *speed, + enum pcie_link_width *width) +{ + u32 lnkcap1, lnkcap2; + int err1, err2; + +#define PCIE_MLW_CAP_SHIFT 4 /* start of MLW mask in link capabilities */ + + *speed = PCI_SPEED_UNKNOWN; + *width = PCIE_LNK_WIDTH_UNKNOWN; + + err1 = pcie_capability_read_dword(adap->pdev, PCI_EXP_LNKCAP, + &lnkcap1); + err2 = pcie_capability_read_dword(adap->pdev, PCI_EXP_LNKCAP2, + &lnkcap2); + if (!err2 && lnkcap2) { /* PCIe r3.0-compliant */ + if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) + *speed = PCIE_SPEED_8_0GT; + else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) + *speed = PCIE_SPEED_5_0GT; + else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) + *speed = PCIE_SPEED_2_5GT; + } + if (!err1) { + *width = (lnkcap1 & PCI_EXP_LNKCAP_MLW) >> PCIE_MLW_CAP_SHIFT; + if (!lnkcap2) { /* pre-r3.0 */ + if (lnkcap1 & PCI_EXP_LNKCAP_SLS_5_0GB) + *speed = PCIE_SPEED_5_0GT; + else if (lnkcap1 & PCI_EXP_LNKCAP_SLS_2_5GB) + *speed = PCIE_SPEED_2_5GT; + } + } + + if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN) + return err1 ? err1 : err2 ? err2 : -EINVAL; + return 0; +} + +static void cxgb4_check_pcie_caps(struct adapter *adap) +{ + enum pcie_link_width width, width_cap; + enum pci_bus_speed speed, speed_cap; + +#define PCIE_SPEED_STR(speed) \ + (speed == PCIE_SPEED_8_0GT ? "8.0GT/s" : \ + speed == PCIE_SPEED_5_0GT ? "5.0GT/s" : \ + speed == PCIE_SPEED_2_5GT ? "2.5GT/s" : \ + "Unknown") + + if (cxgb4_get_pcie_dev_link_caps(adap, &speed_cap, &width_cap)) { + dev_warn(adap->pdev_dev, + "Unable to determine PCIe device BW capabilities\n"); + return; + } + + if (pcie_get_minimum_link(adap->pdev, &speed, &width) || + speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) { + dev_warn(adap->pdev_dev, + "Unable to determine PCI Express bandwidth.\n"); + return; + } + + dev_info(adap->pdev_dev, "PCIe link speed is %s, device supports %s\n", + PCIE_SPEED_STR(speed), PCIE_SPEED_STR(speed_cap)); + dev_info(adap->pdev_dev, "PCIe link width is x%d, device supports x%d\n", + width, width_cap); + if (speed < speed_cap || width < width_cap) + dev_info(adap->pdev_dev, + "A slot with more lanes and/or higher speed is " + "suggested for optimal performance.\n"); +} + static void print_port_info(const struct net_device *dev) { char buf[80]; @@ -4559,10 +4632,10 @@ static void print_port_info(const struct net_device *dev) --bufp; sprintf(bufp, "BASE-%s", t4_get_port_type_description(pi->port_type)); - netdev_info(dev, "Chelsio %s rev %d %s %sNIC PCIe x%d%s%s\n", + netdev_info(dev, "Chelsio %s rev %d %s %sNIC %s\n", adap->params.vpd.id, CHELSIO_CHIP_RELEASE(adap->params.chip), buf, - is_offload(adap) ? "R" : "", adap->params.pci.width, spd, + is_offload(adap) ? "R" : "", (adap->flags & USING_MSIX) ? " MSI-X" : (adap->flags & USING_MSI) ? " MSI" : ""); netdev_info(dev, "S/N: %s, P/N: %s\n", @@ -4908,6 +4981,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) else if (msi > 0 && pci_enable_msi(pdev) == 0) adapter->flags |= USING_MSI; + /* check for PCI Express bandwidth capabiltites */ + cxgb4_check_pcie_caps(adapter); + err = init_rss(adapter); if (err) goto out_free_dev; -- cgit v1.2.3-59-g8ed1b From f90ce56187e78f46560a6a31e39ee3209b1a9427 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 11:29:54 +0530 Subject: cxgb4: get naming correct for iscsi queues All the upper level protocols like rdma, iscsi have their own offload rx queues, so instead of using the generic naming convention be specific while naming them. Improves code readability Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 13 +++--- drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 8 ++-- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 53 +++++++++++----------- drivers/net/ethernet/chelsio/cxgb4/sge.c | 2 +- 4 files changed, 38 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index e01e7228f607..3b59bc4038a6 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -398,11 +398,10 @@ struct link_config { enum { MAX_ETH_QSETS = 32, /* # of Ethernet Tx/Rx queue sets */ - MAX_OFLD_QSETS = 16, /* # of offload Tx/Rx queue sets */ + MAX_OFLD_QSETS = 16, /* # of offload Tx, iscsi Rx queue sets */ MAX_CTRL_QUEUES = NCHAN, /* # of control Tx queues */ MAX_RDMA_QUEUES = NCHAN, /* # of streaming RDMA Rx queues */ MAX_RDMA_CIQS = 32, /* # of RDMA concentrator IQs */ - MAX_ISCSI_QUEUES = NCHAN, /* # of streaming iSCSI Rx queues */ }; enum { @@ -420,7 +419,7 @@ enum { INGQ_EXTRAS = 2, /* firmware event queue and */ /* forwarded interrupts */ MAX_INGQ = MAX_ETH_QSETS + MAX_OFLD_QSETS + MAX_RDMA_QUEUES - + MAX_RDMA_CIQS + MAX_ISCSI_QUEUES + INGQ_EXTRAS, + + MAX_RDMA_CIQS + INGQ_EXTRAS, }; struct adapter; @@ -639,7 +638,7 @@ struct sge { struct sge_ctrl_txq ctrlq[MAX_CTRL_QUEUES]; struct sge_eth_rxq ethrxq[MAX_ETH_QSETS]; - struct sge_ofld_rxq ofldrxq[MAX_OFLD_QSETS]; + struct sge_ofld_rxq iscsirxq[MAX_OFLD_QSETS]; struct sge_ofld_rxq rdmarxq[MAX_RDMA_QUEUES]; struct sge_ofld_rxq rdmaciq[MAX_RDMA_CIQS]; struct sge_rspq fw_evtq ____cacheline_aligned_in_smp; @@ -650,10 +649,10 @@ struct sge { u16 max_ethqsets; /* # of available Ethernet queue sets */ u16 ethqsets; /* # of active Ethernet queue sets */ u16 ethtxq_rover; /* Tx queue to clean up next */ - u16 ofldqsets; /* # of active offload queue sets */ + u16 iscsiqsets; /* # of active iSCSI queue sets */ u16 rdmaqs; /* # of available RDMA Rx queues */ u16 rdmaciqs; /* # of available RDMA concentrator IQs */ - u16 ofld_rxq[MAX_OFLD_QSETS]; + u16 iscsi_rxq[MAX_OFLD_QSETS]; u16 rdma_rxq[MAX_RDMA_QUEUES]; u16 rdma_ciq[MAX_RDMA_CIQS]; u16 timer_val[SGE_NTIMERS]; @@ -679,7 +678,7 @@ struct sge { }; #define for_each_ethrxq(sge, i) for (i = 0; i < (sge)->ethqsets; i++) -#define for_each_ofldrxq(sge, i) for (i = 0; i < (sge)->ofldqsets; i++) +#define for_each_iscsirxq(sge, i) for (i = 0; i < (sge)->iscsiqsets; i++) #define for_each_rdmarxq(sge, i) for (i = 0; i < (sge)->rdmaqs; i++) #define for_each_rdmaciq(sge, i) for (i = 0; i < (sge)->rdmaciqs; i++) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 0d579b192350..62a343fc9c1a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -2245,7 +2245,7 @@ static int sge_qinfo_show(struct seq_file *seq, void *v) { struct adapter *adap = seq->private; int eth_entries = DIV_ROUND_UP(adap->sge.ethqsets, 4); - int iscsi_entries = DIV_ROUND_UP(adap->sge.ofldqsets, 4); + int iscsi_entries = DIV_ROUND_UP(adap->sge.iscsiqsets, 4); int rdma_entries = DIV_ROUND_UP(adap->sge.rdmaqs, 4); int ciq_entries = DIV_ROUND_UP(adap->sge.rdmaciqs, 4); int ctrl_entries = DIV_ROUND_UP(MAX_CTRL_QUEUES, 4); @@ -2331,10 +2331,10 @@ do { \ } else if (iscsi_idx < iscsi_entries) { const struct sge_ofld_rxq *rx = - &adap->sge.ofldrxq[iscsi_idx * 4]; + &adap->sge.iscsirxq[iscsi_idx * 4]; const struct sge_ofld_txq *tx = &adap->sge.ofldtxq[iscsi_idx * 4]; - int n = min(4, adap->sge.ofldqsets - 4 * iscsi_idx); + int n = min(4, adap->sge.iscsiqsets - 4 * iscsi_idx); S("QType:", "iSCSI"); T("TxQ ID:", q.cntxt_id); @@ -2454,7 +2454,7 @@ do { \ static int sge_queue_entries(const struct adapter *adap) { return DIV_ROUND_UP(adap->sge.ethqsets, 4) + - DIV_ROUND_UP(adap->sge.ofldqsets, 4) + + DIV_ROUND_UP(adap->sge.iscsiqsets, 4) + DIV_ROUND_UP(adap->sge.rdmaqs, 4) + DIV_ROUND_UP(adap->sge.rdmaciqs, 4) + DIV_ROUND_UP(MAX_CTRL_QUEUES, 4) + 1; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 8326a7760ffd..2642593289d9 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -766,8 +766,8 @@ static void name_msix_vecs(struct adapter *adap) } /* offload queues */ - for_each_ofldrxq(&adap->sge, i) - snprintf(adap->msix_info[msi_idx++].desc, n, "%s-ofld%d", + for_each_iscsirxq(&adap->sge, i) + snprintf(adap->msix_info[msi_idx++].desc, n, "%s-iscsi%d", adap->port[0]->name, i); for_each_rdmarxq(&adap->sge, i) @@ -782,7 +782,7 @@ static void name_msix_vecs(struct adapter *adap) static int request_msix_queue_irqs(struct adapter *adap) { struct sge *s = &adap->sge; - int err, ethqidx, ofldqidx = 0, rdmaqidx = 0, rdmaciqqidx = 0; + int err, ethqidx, iscsiqidx = 0, rdmaqidx = 0, rdmaciqqidx = 0; int msi_index = 2; err = request_irq(adap->msix_info[1].vec, t4_sge_intr_msix, 0, @@ -799,11 +799,11 @@ static int request_msix_queue_irqs(struct adapter *adap) goto unwind; msi_index++; } - for_each_ofldrxq(s, ofldqidx) { + for_each_iscsirxq(s, iscsiqidx) { err = request_irq(adap->msix_info[msi_index].vec, t4_sge_intr_msix, 0, adap->msix_info[msi_index].desc, - &s->ofldrxq[ofldqidx].rspq); + &s->iscsirxq[iscsiqidx].rspq); if (err) goto unwind; msi_index++; @@ -835,9 +835,9 @@ unwind: while (--rdmaqidx >= 0) free_irq(adap->msix_info[--msi_index].vec, &s->rdmarxq[rdmaqidx].rspq); - while (--ofldqidx >= 0) + while (--iscsiqidx >= 0) free_irq(adap->msix_info[--msi_index].vec, - &s->ofldrxq[ofldqidx].rspq); + &s->iscsirxq[iscsiqidx].rspq); while (--ethqidx >= 0) free_irq(adap->msix_info[--msi_index].vec, &s->ethrxq[ethqidx].rspq); @@ -853,8 +853,9 @@ static void free_msix_queue_irqs(struct adapter *adap) free_irq(adap->msix_info[1].vec, &s->fw_evtq); for_each_ethrxq(s, i) free_irq(adap->msix_info[msi_index++].vec, &s->ethrxq[i].rspq); - for_each_ofldrxq(s, i) - free_irq(adap->msix_info[msi_index++].vec, &s->ofldrxq[i].rspq); + for_each_iscsirxq(s, i) + free_irq(adap->msix_info[msi_index++].vec, + &s->iscsirxq[i].rspq); for_each_rdmarxq(s, i) free_irq(adap->msix_info[msi_index++].vec, &s->rdmarxq[i].rspq); for_each_rdmaciq(s, i) @@ -1093,8 +1094,8 @@ freeout: t4_free_sge_resources(adap); } } - j = s->ofldqsets / adap->params.nports; /* ofld queues per channel */ - for_each_ofldrxq(s, i) { + j = s->iscsiqsets / adap->params.nports; /* iscsi queues per channel */ + for_each_iscsirxq(s, i) { err = t4_sge_alloc_ofld_txq(adap, &s->ofldtxq[i], adap->port[i / j], s->fw_evtq.cntxt_id); @@ -1110,7 +1111,7 @@ freeout: t4_free_sge_resources(adap); msi_idx += nq; \ } while (0) - ALLOC_OFLD_RXQS(s->ofldrxq, s->ofldqsets, j, s->ofld_rxq); + ALLOC_OFLD_RXQS(s->iscsirxq, s->iscsiqsets, j, s->iscsi_rxq); ALLOC_OFLD_RXQS(s->rdmarxq, s->rdmaqs, 1, s->rdma_rxq); j = s->rdmaciqs / adap->params.nports; /* rdmaq queues per channel */ ALLOC_OFLD_RXQS(s->rdmaciq, s->rdmaciqs, j, s->rdma_ciq); @@ -2277,7 +2278,7 @@ static void disable_dbs(struct adapter *adap) for_each_ethrxq(&adap->sge, i) disable_txq_db(&adap->sge.ethtxq[i].q); - for_each_ofldrxq(&adap->sge, i) + for_each_iscsirxq(&adap->sge, i) disable_txq_db(&adap->sge.ofldtxq[i].q); for_each_port(adap, i) disable_txq_db(&adap->sge.ctrlq[i].q); @@ -2289,7 +2290,7 @@ static void enable_dbs(struct adapter *adap) for_each_ethrxq(&adap->sge, i) enable_txq_db(adap, &adap->sge.ethtxq[i].q); - for_each_ofldrxq(&adap->sge, i) + for_each_iscsirxq(&adap->sge, i) enable_txq_db(adap, &adap->sge.ofldtxq[i].q); for_each_port(adap, i) enable_txq_db(adap, &adap->sge.ctrlq[i].q); @@ -2359,7 +2360,7 @@ static void recover_all_queues(struct adapter *adap) for_each_ethrxq(&adap->sge, i) sync_txq_pidx(adap, &adap->sge.ethtxq[i].q); - for_each_ofldrxq(&adap->sge, i) + for_each_iscsirxq(&adap->sge, i) sync_txq_pidx(adap, &adap->sge.ofldtxq[i].q); for_each_port(adap, i) sync_txq_pidx(adap, &adap->sge.ctrlq[i].q); @@ -2443,10 +2444,10 @@ static void uld_attach(struct adapter *adap, unsigned int uld) lli.nrxq = adap->sge.rdmaqs; lli.nciq = adap->sge.rdmaciqs; } else if (uld == CXGB4_ULD_ISCSI) { - lli.rxq_ids = adap->sge.ofld_rxq; - lli.nrxq = adap->sge.ofldqsets; + lli.rxq_ids = adap->sge.iscsi_rxq; + lli.nrxq = adap->sge.iscsiqsets; } - lli.ntxq = adap->sge.ofldqsets; + lli.ntxq = adap->sge.iscsiqsets; lli.nchan = adap->params.nports; lli.nports = adap->params.nports; lli.wr_cred = adap->params.ofldq_wr_cred; @@ -4342,11 +4343,11 @@ static void cfg_queues(struct adapter *adap) * capped by the number of available cores. */ if (n10g) { - i = min_t(int, ARRAY_SIZE(s->ofldrxq), + i = min_t(int, ARRAY_SIZE(s->iscsirxq), num_online_cpus()); - s->ofldqsets = roundup(i, adap->params.nports); + s->iscsiqsets = roundup(i, adap->params.nports); } else - s->ofldqsets = adap->params.nports; + s->iscsiqsets = adap->params.nports; /* For RDMA one Rx queue per channel suffices */ s->rdmaqs = adap->params.nports; /* Try and allow at least 1 CIQ per cpu rounding down @@ -4377,8 +4378,8 @@ static void cfg_queues(struct adapter *adap) for (i = 0; i < ARRAY_SIZE(s->ofldtxq); i++) s->ofldtxq[i].q.size = 1024; - for (i = 0; i < ARRAY_SIZE(s->ofldrxq); i++) { - struct sge_ofld_rxq *r = &s->ofldrxq[i]; + for (i = 0; i < ARRAY_SIZE(s->iscsirxq); i++) { + struct sge_ofld_rxq *r = &s->iscsirxq[i]; init_rspq(adap, &r->rspq, 5, 1, 1024, 64); r->rspq.uld = CXGB4_ULD_ISCSI; @@ -4459,7 +4460,7 @@ static int enable_msix(struct adapter *adap) want = s->max_ethqsets + EXTRA_VECS; if (is_offload(adap)) { - want += s->rdmaqs + s->rdmaciqs + s->ofldqsets; + want += s->rdmaqs + s->rdmaciqs + s->iscsiqsets; /* need nchan for each possible ULD */ ofld_need = 3 * nchan; } @@ -4498,13 +4499,13 @@ static int enable_msix(struct adapter *adap) /* leftovers go to OFLD */ i = allocated - EXTRA_VECS - s->max_ethqsets - s->rdmaqs - s->rdmaciqs; - s->ofldqsets = (i / nchan) * nchan; /* round down */ + s->iscsiqsets = (i / nchan) * nchan; /* round down */ } for (i = 0; i < allocated; ++i) adap->msix_info[i].vec = entries[i].vector; dev_info(adap->pdev_dev, "%d MSI-X vectors allocated, " "nic %d iscsi %d rdma cpl %d rdma ciq %d\n", - allocated, s->max_ethqsets, s->ofldqsets, s->rdmaqs, + allocated, s->max_ethqsets, s->iscsiqsets, s->rdmaqs, s->rdmaciqs); kfree(entries); diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 8d35ce317f67..0333435ca86e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -2978,7 +2978,7 @@ void t4_free_sge_resources(struct adapter *adap) } /* clean up RDMA and iSCSI Rx queues */ - t4_free_ofld_rxqs(adap, adap->sge.ofldqsets, adap->sge.ofldrxq); + t4_free_ofld_rxqs(adap, adap->sge.iscsiqsets, adap->sge.iscsirxq); t4_free_ofld_rxqs(adap, adap->sge.rdmaqs, adap->sge.rdmarxq); t4_free_ofld_rxqs(adap, adap->sge.rdmaciqs, adap->sge.rdmaciq); -- cgit v1.2.3-59-g8ed1b From 0ac5b708e362bdbe7c16b67dc3cad41f69aeab3f Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 11:29:55 +0530 Subject: cxgb4: Use the node info to alloc_ring() for RX queues Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/sge.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 0333435ca86e..138be46d199d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -2555,7 +2555,8 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, iq->size = roundup(iq->size, 16); iq->desc = alloc_ring(adap->pdev_dev, iq->size, iq->iqe_len, 0, - &iq->phys_addr, NULL, 0, NUMA_NO_NODE); + &iq->phys_addr, NULL, 0, + dev_to_node(adap->pdev_dev)); if (!iq->desc) return -ENOMEM; @@ -2595,7 +2596,8 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, fl->size = roundup(fl->size, 8); fl->desc = alloc_ring(adap->pdev_dev, fl->size, sizeof(__be64), sizeof(struct rx_sw_desc), &fl->addr, - &fl->sdesc, s->stat_len, NUMA_NO_NODE); + &fl->sdesc, s->stat_len, + dev_to_node(adap->pdev_dev)); if (!fl->desc) goto fl_nomem; -- cgit v1.2.3-59-g8ed1b From 812787b8e4ea1a1c9144fe06ebf9b45371b2f5ce Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 11:29:56 +0530 Subject: cxgb4: Use napi_complete_done() api in napi handler Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/sge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 138be46d199d..5e3ffa73ee13 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -2288,7 +2288,7 @@ static int napi_rx_handler(struct napi_struct *napi, int budget) if (likely(work_done < budget)) { int timer_index; - napi_complete(napi); + napi_complete_done(napi, work_done); timer_index = QINTR_TIMER_IDX_G(q->next_intr_params); if (q->adaptive_rx) { -- cgit v1.2.3-59-g8ed1b From ac28d179b8d9dd444490c078826bf33f735c2285 Mon Sep 17 00:00:00 2001 From: Bert Kenward Date: Wed, 23 Dec 2015 08:56:40 +0000 Subject: sfc: Retry MCDI after NO_EVB_PORT error on a VF After reboot the vswitch configuration from the PF may not be complete before the VF attempts to restore filters. In that case we see NO_EVB_PORT errors from the MC. Retry up to a time limit or until a different result is seen. Signed-off-by: Bert Kenward Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/mcdi.c | 99 ++++++++++++++++++++++++++++++++++------- drivers/net/ethernet/sfc/mcdi.h | 1 + 2 files changed, 85 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index 41fb6b60a3f0..2509ca916880 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -315,6 +315,7 @@ static void efx_mcdi_read_response_header(struct efx_nic *efx) } #endif + mcdi->resprc_raw = 0; if (error && mcdi->resp_data_len == 0) { netif_err(efx, hw, efx->net_dev, "MC rebooted\n"); mcdi->resprc = -EIO; @@ -325,8 +326,8 @@ static void efx_mcdi_read_response_header(struct efx_nic *efx) mcdi->resprc = -EIO; } else if (error) { efx->type->mcdi_read_response(efx, &hdr, mcdi->resp_hdr_len, 4); - mcdi->resprc = - efx_mcdi_errno(EFX_DWORD_FIELD(hdr, EFX_DWORD_0)); + mcdi->resprc_raw = EFX_DWORD_FIELD(hdr, EFX_DWORD_0); + mcdi->resprc = efx_mcdi_errno(mcdi->resprc_raw); } else { mcdi->resprc = 0; } @@ -623,7 +624,8 @@ efx_mcdi_check_supported(struct efx_nic *efx, unsigned int cmd, size_t inlen) static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, efx_dword_t *outbuf, size_t outlen, - size_t *outlen_actual, bool quiet) + size_t *outlen_actual, bool quiet, + int *raw_rc) { struct efx_mcdi_iface *mcdi = efx_mcdi(efx); MCDI_DECLARE_BUF_ERR(errbuf); @@ -669,6 +671,8 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, * acquiring the iface_lock. */ spin_lock_bh(&mcdi->iface_lock); rc = mcdi->resprc; + if (raw_rc) + *raw_rc = mcdi->resprc_raw; hdr_len = mcdi->resp_hdr_len; data_len = mcdi->resp_data_len; err_len = min(sizeof(errbuf), data_len); @@ -708,27 +712,92 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, static int _efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const efx_dword_t *inbuf, size_t inlen, efx_dword_t *outbuf, size_t outlen, - size_t *outlen_actual, bool quiet) + size_t *outlen_actual, bool quiet, int *raw_rc) { int rc; rc = efx_mcdi_rpc_start(efx, cmd, inbuf, inlen); - if (rc) { - if (outlen_actual) - *outlen_actual = 0; + if (rc) return rc; - } + return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen, - outlen_actual, quiet); + outlen_actual, quiet, raw_rc); } +static int _efx_mcdi_rpc_evb_retry(struct efx_nic *efx, unsigned cmd, + const efx_dword_t *inbuf, size_t inlen, + efx_dword_t *outbuf, size_t outlen, + size_t *outlen_actual, bool quiet) +{ + int raw_rc = 0; + int rc; + + rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen, + outbuf, outlen, outlen_actual, true, &raw_rc); + + if ((rc == -EPROTO) && (raw_rc == MC_CMD_ERR_NO_EVB_PORT) && + efx->type->is_vf) { + /* If the EVB port isn't available within a VF this may + * mean the PF is still bringing the switch up. We should + * retry our request shortly. + */ + unsigned long abort_time = jiffies + MCDI_RPC_TIMEOUT; + unsigned int delay_us = 10000; + + netif_dbg(efx, hw, efx->net_dev, + "%s: NO_EVB_PORT; will retry request\n", + __func__); + + do { + usleep_range(delay_us, delay_us + 10000); + rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen, + outbuf, outlen, outlen_actual, + true, &raw_rc); + if (delay_us < 100000) + delay_us <<= 1; + } while ((rc == -EPROTO) && + (raw_rc == MC_CMD_ERR_NO_EVB_PORT) && + time_before(jiffies, abort_time)); + } + + if (rc && !quiet && !(cmd == MC_CMD_REBOOT && rc == -EIO)) + efx_mcdi_display_error(efx, cmd, inlen, + outbuf, outlen, rc); + + return rc; +} + +/** + * efx_mcdi_rpc - Issue an MCDI command and wait for completion + * @efx: NIC through which to issue the command + * @cmd: Command type number + * @inbuf: Command parameters + * @inlen: Length of command parameters, in bytes. Must be a multiple + * of 4 and no greater than %MCDI_CTL_SDU_LEN_MAX_V1. + * @outbuf: Response buffer. May be %NULL if @outlen is 0. + * @outlen: Length of response buffer, in bytes. If the actual + * response is longer than @outlen & ~3, it will be truncated + * to that length. + * @outlen_actual: Pointer through which to return the actual response + * length. May be %NULL if this is not needed. + * + * This function may sleep and therefore must be called in an appropriate + * context. + * + * Return: A negative error code, or zero if successful. The error + * code may come from the MCDI response or may indicate a failure + * to communicate with the MC. In the former case, the response + * will still be copied to @outbuf and *@outlen_actual will be + * set accordingly. In the latter case, *@outlen_actual will be + * set to zero. + */ int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const efx_dword_t *inbuf, size_t inlen, efx_dword_t *outbuf, size_t outlen, size_t *outlen_actual) { - return _efx_mcdi_rpc(efx, cmd, inbuf, inlen, outbuf, outlen, - outlen_actual, false); + return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen, + outlen_actual, false); } /* Normally, on receiving an error code in the MCDI response, @@ -744,8 +813,8 @@ int efx_mcdi_rpc_quiet(struct efx_nic *efx, unsigned cmd, efx_dword_t *outbuf, size_t outlen, size_t *outlen_actual) { - return _efx_mcdi_rpc(efx, cmd, inbuf, inlen, outbuf, outlen, - outlen_actual, true); + return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen, + outlen_actual, true); } int efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd, @@ -866,7 +935,7 @@ int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, size_t *outlen_actual) { return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen, - outlen_actual, false); + outlen_actual, false, NULL); } int efx_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned cmd, size_t inlen, @@ -874,7 +943,7 @@ int efx_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned cmd, size_t inlen, size_t *outlen_actual) { return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen, - outlen_actual, true); + outlen_actual, true, NULL); } void efx_mcdi_display_error(struct efx_nic *efx, unsigned cmd, diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index 025d504c472b..397660d1b3de 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -71,6 +71,7 @@ struct efx_mcdi_iface { unsigned int credits; unsigned int seqno; int resprc; + int resprc_raw; size_t resp_hdr_len; size_t resp_data_len; spinlock_t async_lock; -- cgit v1.2.3-59-g8ed1b From acd43a9097f0f2bb806faeb292b49224ed91fcfb Mon Sep 17 00:00:00 2001 From: Bert Kenward Date: Wed, 23 Dec 2015 08:57:18 +0000 Subject: sfc: Handle MCDI proxy authorisation For unprivileged functions operations can be authorised by an admin function. Extra steps are introduced to the MCDI protocol in this situation - the initial response from the MCDI tells us that the operation has been deferred, and we must retry when told. We then receive an event telling us to retry. Note that this provides only the functionality for the unprivileged functions, not the handling of the administrative side. Signed-off-by: Bert Kenward Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/mcdi.c | 154 +++++++++++++++++++++++++++++++++++++--- drivers/net/ethernet/sfc/mcdi.h | 9 +++ 2 files changed, 155 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index 2509ca916880..d6b8a77f5b22 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -82,6 +82,7 @@ int efx_mcdi_init(struct efx_nic *efx) mcdi->logging_enabled = mcdi_logging_default; #endif init_waitqueue_head(&mcdi->wq); + init_waitqueue_head(&mcdi->proxy_rx_wq); spin_lock_init(&mcdi->iface_lock); mcdi->state = MCDI_STATE_QUIESCENT; mcdi->mode = MCDI_MODE_POLL; @@ -622,10 +623,30 @@ efx_mcdi_check_supported(struct efx_nic *efx, unsigned int cmd, size_t inlen) return 0; } -static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, +static bool efx_mcdi_get_proxy_handle(struct efx_nic *efx, + size_t hdr_len, size_t data_len, + u32 *proxy_handle) +{ + MCDI_DECLARE_BUF_ERR(testbuf); + const size_t buflen = sizeof(testbuf); + + if (!proxy_handle || data_len < buflen) + return false; + + efx->type->mcdi_read_response(efx, testbuf, hdr_len, buflen); + if (MCDI_DWORD(testbuf, ERR_CODE) == MC_CMD_ERR_PROXY_PENDING) { + *proxy_handle = MCDI_DWORD(testbuf, ERR_PROXY_PENDING_HANDLE); + return true; + } + + return false; +} + +static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned int cmd, + size_t inlen, efx_dword_t *outbuf, size_t outlen, size_t *outlen_actual, bool quiet, - int *raw_rc) + u32 *proxy_handle, int *raw_rc) { struct efx_mcdi_iface *mcdi = efx_mcdi(efx); MCDI_DECLARE_BUF_ERR(errbuf); @@ -659,6 +680,9 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, spin_unlock_bh(&mcdi->iface_lock); } + if (proxy_handle) + *proxy_handle = 0; + if (rc != 0) { if (outlen_actual) *outlen_actual = 0; @@ -693,6 +717,12 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, netif_err(efx, hw, efx->net_dev, "MC fatal error %d\n", -rc); efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE); + } else if (proxy_handle && (rc == -EPROTO) && + efx_mcdi_get_proxy_handle(efx, hdr_len, data_len, + proxy_handle)) { + mcdi->proxy_rx_status = 0; + mcdi->proxy_rx_handle = 0; + mcdi->state = MCDI_STATE_PROXY_WAIT; } else if (rc && !quiet) { efx_mcdi_display_error(efx, cmd, inlen, errbuf, err_len, rc); @@ -705,23 +735,119 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, } } - efx_mcdi_release(mcdi); + if (!proxy_handle || !*proxy_handle) + efx_mcdi_release(mcdi); return rc; } -static int _efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, +static void efx_mcdi_proxy_abort(struct efx_mcdi_iface *mcdi) +{ + if (mcdi->state == MCDI_STATE_PROXY_WAIT) { + /* Interrupt the proxy wait. */ + mcdi->proxy_rx_status = -EINTR; + wake_up(&mcdi->proxy_rx_wq); + } +} + +static void efx_mcdi_ev_proxy_response(struct efx_nic *efx, + u32 handle, int status) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + + WARN_ON(mcdi->state != MCDI_STATE_PROXY_WAIT); + + mcdi->proxy_rx_status = efx_mcdi_errno(status); + /* Ensure the status is written before we update the handle, since the + * latter is used to check if we've finished. + */ + wmb(); + mcdi->proxy_rx_handle = handle; + wake_up(&mcdi->proxy_rx_wq); +} + +static int efx_mcdi_proxy_wait(struct efx_nic *efx, u32 handle, bool quiet) +{ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + int rc; + + /* Wait for a proxy event, or timeout. */ + rc = wait_event_timeout(mcdi->proxy_rx_wq, + mcdi->proxy_rx_handle != 0 || + mcdi->proxy_rx_status == -EINTR, + MCDI_RPC_TIMEOUT); + + if (rc <= 0) { + netif_dbg(efx, hw, efx->net_dev, + "MCDI proxy timeout %d\n", handle); + return -ETIMEDOUT; + } else if (mcdi->proxy_rx_handle != handle) { + netif_warn(efx, hw, efx->net_dev, + "MCDI proxy unexpected handle %d (expected %d)\n", + mcdi->proxy_rx_handle, handle); + return -EINVAL; + } + + return mcdi->proxy_rx_status; +} + +static int _efx_mcdi_rpc(struct efx_nic *efx, unsigned int cmd, const efx_dword_t *inbuf, size_t inlen, efx_dword_t *outbuf, size_t outlen, size_t *outlen_actual, bool quiet, int *raw_rc) { + u32 proxy_handle = 0; /* Zero is an invalid proxy handle. */ int rc; + if (inbuf && inlen && (inbuf == outbuf)) { + /* The input buffer can't be aliased with the output. */ + WARN_ON(1); + return -EINVAL; + } + rc = efx_mcdi_rpc_start(efx, cmd, inbuf, inlen); if (rc) return rc; - return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen, - outlen_actual, quiet, raw_rc); + rc = _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen, + outlen_actual, quiet, &proxy_handle, raw_rc); + + if (proxy_handle) { + /* Handle proxy authorisation. This allows approval of MCDI + * operations to be delegated to the admin function, allowing + * fine control over (eg) multicast subscriptions. + */ + struct efx_mcdi_iface *mcdi = efx_mcdi(efx); + + netif_dbg(efx, hw, efx->net_dev, + "MCDI waiting for proxy auth %d\n", + proxy_handle); + rc = efx_mcdi_proxy_wait(efx, proxy_handle, quiet); + + if (rc == 0) { + netif_dbg(efx, hw, efx->net_dev, + "MCDI proxy retry %d\n", proxy_handle); + + /* We now retry the original request. */ + mcdi->state = MCDI_STATE_RUNNING_SYNC; + efx_mcdi_send_request(efx, cmd, inbuf, inlen); + + rc = _efx_mcdi_rpc_finish(efx, cmd, inlen, + outbuf, outlen, outlen_actual, + quiet, NULL, raw_rc); + } else { + netif_printk(efx, hw, + rc == -EPERM ? KERN_DEBUG : KERN_ERR, + efx->net_dev, + "MC command 0x%x failed after proxy auth rc=%d\n", + cmd, rc); + + if (rc == -EINTR || rc == -EIO) + efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE); + efx_mcdi_release(mcdi); + } + } + + return rc; } static int _efx_mcdi_rpc_evb_retry(struct efx_nic *efx, unsigned cmd, @@ -935,7 +1061,7 @@ int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, size_t *outlen_actual) { return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen, - outlen_actual, false, NULL); + outlen_actual, false, NULL, NULL); } int efx_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned cmd, size_t inlen, @@ -943,7 +1069,7 @@ int efx_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned cmd, size_t inlen, size_t *outlen_actual) { return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen, - outlen_actual, true, NULL); + outlen_actual, true, NULL, NULL); } void efx_mcdi_display_error(struct efx_nic *efx, unsigned cmd, @@ -1083,8 +1209,13 @@ static void efx_mcdi_ev_death(struct efx_nic *efx, int rc) * receiving a REBOOT event after posting the MCDI * request. Did the mc reboot before or after the copyout? The * best we can do always is just return failure. + * + * If there is an outstanding proxy response expected it is not going + * to arrive. We should thus abort it. */ spin_lock(&mcdi->iface_lock); + efx_mcdi_proxy_abort(mcdi); + if (efx_mcdi_complete_sync(mcdi)) { if (mcdi->mode == MCDI_MODE_EVENTS) { mcdi->resprc = rc; @@ -1132,6 +1263,8 @@ static void efx_mcdi_ev_bist(struct efx_nic *efx) spin_lock(&mcdi->iface_lock); efx->mc_bist_for_other_fn = true; + efx_mcdi_proxy_abort(mcdi); + if (efx_mcdi_complete_sync(mcdi)) { if (mcdi->mode == MCDI_MODE_EVENTS) { mcdi->resprc = -EIO; @@ -1240,6 +1373,11 @@ void efx_mcdi_process_event(struct efx_channel *channel, EFX_QWORD_VAL(*event)); efx_schedule_reset(efx, RESET_TYPE_DMA_ERROR); break; + case MCDI_EVENT_CODE_PROXY_RESPONSE: + efx_mcdi_ev_proxy_response(efx, + MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_HANDLE), + MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_RC)); + break; default: netif_err(efx, hw, efx->net_dev, "Unknown MCDI event 0x%x\n", code); diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index 397660d1b3de..c9aeb0701c9a 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -17,6 +17,8 @@ * @MCDI_STATE_RUNNING_SYNC: There is a synchronous MCDI request pending. * Only the thread that moved into this state is allowed to move out of it. * @MCDI_STATE_RUNNING_ASYNC: There is an asynchronous MCDI request pending. + * @MCDI_STATE_PROXY_WAIT: An MCDI request has completed with a response that + * indicates we must wait for a proxy try again message. * @MCDI_STATE_COMPLETED: An MCDI request has completed, but the owning thread * has not yet consumed the result. For all other threads, equivalent to * %MCDI_STATE_RUNNING. @@ -25,6 +27,7 @@ enum efx_mcdi_state { MCDI_STATE_QUIESCENT, MCDI_STATE_RUNNING_SYNC, MCDI_STATE_RUNNING_ASYNC, + MCDI_STATE_PROXY_WAIT, MCDI_STATE_COMPLETED, }; @@ -60,6 +63,9 @@ enum efx_mcdi_mode { * @async_timer: Timer for asynchronous request timeout * @logging_buffer: buffer that may be used to build MCDI tracing messages * @logging_enabled: whether to trace MCDI + * @proxy_rx_handle: Most recently received proxy authorisation handle + * @proxy_rx_status: Status of most recent proxy authorisation + * @proxy_rx_wq: Wait queue for updates to proxy_rx_handle */ struct efx_mcdi_iface { struct efx_nic *efx; @@ -81,6 +87,9 @@ struct efx_mcdi_iface { char *logging_buffer; bool logging_enabled; #endif + unsigned int proxy_rx_handle; + int proxy_rx_status; + wait_queue_head_t proxy_rx_wq; }; struct efx_mcdi_mon { -- cgit v1.2.3-59-g8ed1b From e65a510918d72a1606897186386123af001045ab Mon Sep 17 00:00:00 2001 From: Bert Kenward Date: Wed, 23 Dec 2015 08:57:36 +0000 Subject: sfc: Make failed filter removal less noisy There are situations - mostly reset related - where our view of the filter table differs from the hardware. In this case we may try and remove filters that aren't actually installed. This isn't that interesting in most situations, so downgrade the logging. Signed-off-by: Bert Kenward Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 48 ++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 230fabd818ae..b0c6c61216d8 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -3833,13 +3833,12 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx) MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE); MCDI_SET_QWORD(inbuf, FILTER_OP_IN_HANDLE, table->entry[filter_idx].handle); - rc = efx_mcdi_rpc(efx, MC_CMD_FILTER_OP, inbuf, sizeof(inbuf), - NULL, 0, NULL); + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FILTER_OP, inbuf, + sizeof(inbuf), NULL, 0, NULL); if (rc) - netdev_WARN(efx->net_dev, - "filter_idx=%#x handle=%#llx\n", - filter_idx, - table->entry[filter_idx].handle); + netif_info(efx, drv, efx->net_dev, + "%s: filter %04x remove failed\n", + __func__, filter_idx); kfree(spec); } @@ -3848,11 +3847,14 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx) } #define EFX_EF10_FILTER_DO_MARK_OLD(id) \ - if (id != EFX_EF10_FILTER_ID_INVALID) { \ - filter_idx = efx_ef10_filter_get_unsafe_id(efx, id); \ - WARN_ON(!table->entry[filter_idx].spec); \ - table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD; \ - } + if (id != EFX_EF10_FILTER_ID_INVALID) { \ + filter_idx = efx_ef10_filter_get_unsafe_id(efx, id); \ + if (!table->entry[filter_idx].spec) \ + netif_dbg(efx, drv, efx->net_dev, \ + "%s: marked null spec old %04x:%04x\n", \ + __func__, id, filter_idx); \ + table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD;\ + } static void efx_ef10_filter_mark_old(struct efx_nic *efx) { struct efx_ef10_filter_table *table = efx->filter_state; @@ -4070,19 +4072,31 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast, static void efx_ef10_filter_remove_old(struct efx_nic *efx) { struct efx_ef10_filter_table *table = efx->filter_state; - bool remove_failed = false; + int remove_failed = 0; + int remove_noent = 0; + int rc; int i; for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) { if (ACCESS_ONCE(table->entry[i].spec) & EFX_EF10_FILTER_FLAG_AUTO_OLD) { - if (efx_ef10_filter_remove_internal( - efx, 1U << EFX_FILTER_PRI_AUTO, - i, true) < 0) - remove_failed = true; + rc = efx_ef10_filter_remove_internal(efx, + 1U << EFX_FILTER_PRI_AUTO, i, true); + if (rc == -ENOENT) + remove_noent++; + else if (rc) + remove_failed++; } } - WARN_ON(remove_failed); + + if (remove_failed) + netif_info(efx, drv, efx->net_dev, + "%s: failed to remove %d filters\n", + __func__, remove_failed); + if (remove_noent) + netif_info(efx, drv, efx->net_dev, + "%s: failed to remove %d non-existent filters\n", + __func__, remove_noent); } static int efx_ef10_vport_set_mac_address(struct efx_nic *efx) -- cgit v1.2.3-59-g8ed1b From 8c578368e862f6e3055ce5435942e31d72b1cbb2 Mon Sep 17 00:00:00 2001 From: Tomáš PilaÅ™ Date: Wed, 23 Dec 2015 08:57:51 +0000 Subject: sfc: Downgrade EPERM messages from MCDI to debug When running in an unprivileged function we expect some MC commands to fail with permission errors. To avoid log spew downgrade these to debug only. Signed-off-by: Bert Kenward Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/mcdi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index d6b8a77f5b22..d28e7dd8fa3c 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -1082,9 +1082,10 @@ void efx_mcdi_display_error(struct efx_nic *efx, unsigned cmd, code = MCDI_DWORD(outbuf, ERR_CODE); if (outlen >= MC_CMD_ERR_ARG_OFST + 4) err_arg = MCDI_DWORD(outbuf, ERR_ARG); - netif_err(efx, hw, efx->net_dev, - "MC command 0x%x inlen %d failed rc=%d (raw=%d) arg=%d\n", - cmd, (int)inlen, rc, code, err_arg); + netif_printk(efx, hw, rc == -EPERM ? KERN_DEBUG : KERN_ERR, + efx->net_dev, + "MC command 0x%x inlen %zu failed rc=%d (raw=%d) arg=%d\n", + cmd, inlen, rc, code, err_arg); } /* Switch to polled MCDI completions. This can be called in various -- cgit v1.2.3-59-g8ed1b From 09a04204f041caff7717a001a16c376f73be731b Mon Sep 17 00:00:00 2001 From: Bert Kenward Date: Wed, 23 Dec 2015 08:58:15 +0000 Subject: sfc: Downgrade or remove some error messages Depending on configuration the NIC may return errors for unprivileged functions and/or VFs. Where these are expected and handled, reduce the level of any output. Signed-off-by: Bert Kenward Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 20 ++++++++++++++------ drivers/net/ethernet/sfc/efx.c | 7 ++++--- 2 files changed, 18 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index b0c6c61216d8..98d33d462c6c 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -486,10 +486,17 @@ static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n) BUILD_BUG_ON(MC_CMD_ALLOC_PIOBUF_IN_LEN != 0); for (i = 0; i < n; i++) { - rc = efx_mcdi_rpc(efx, MC_CMD_ALLOC_PIOBUF, NULL, 0, - outbuf, sizeof(outbuf), &outlen); - if (rc) + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_ALLOC_PIOBUF, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) { + /* Don't display the MC error if we didn't have space + * for a VF. + */ + if (!(efx_ef10_is_vf(efx) && rc == -ENOSPC)) + efx_mcdi_display_error(efx, MC_CMD_ALLOC_PIOBUF, + 0, outbuf, outlen, rc); break; + } if (outlen < MC_CMD_ALLOC_PIOBUF_OUT_LEN) { rc = -EIO; break; @@ -4028,9 +4035,10 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast, rc = efx_ef10_filter_insert(efx, &spec, true); if (rc < 0) { - netif_warn(efx, drv, efx->net_dev, - "%scast mismatch filter insert failed rc=%d\n", - multicast ? "Multi" : "Uni", rc); + netif_printk(efx, drv, rc == -EPERM ? KERN_DEBUG : KERN_WARNING, + efx->net_dev, + "%scast mismatch filter insert failed rc=%d\n", + multicast ? "Multi" : "Uni", rc); } else if (multicast) { table->mcdef_id = efx_ef10_filter_get_unsafe_id(efx, rc); if (!nic_data->workaround_26807) { diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 6f697438545d..0705ec869487 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -3174,14 +3174,15 @@ static int efx_pci_probe(struct pci_dev *pci_dev, rtnl_lock(); rc = efx_mtd_probe(efx); rtnl_unlock(); - if (rc) + if (rc && rc != -EPERM) netif_warn(efx, probe, efx->net_dev, "failed to create MTDs (%d)\n", rc); rc = pci_enable_pcie_error_reporting(pci_dev); if (rc && rc != -EINVAL) - netif_warn(efx, probe, efx->net_dev, - "pci_enable_pcie_error_reporting failed (%d)\n", rc); + netif_notice(efx, probe, efx->net_dev, + "PCIE error reporting unavailable (%d).\n", + rc); return 0; -- cgit v1.2.3-59-g8ed1b From f5e2ed022dff60d9023d997ed719531129cc1365 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 23 Dec 2015 13:23:17 +0100 Subject: dsa: mv88e6xxx: Add Second back of statistics The 6320 family of switch chips has a second bank for statistics, but is missing three statistics in the port registers. Generalise and extend the code: * adding a field to the statistics table indicating the bank/register set where each statistics is. * add a function indicating if an individual statistics is available on this device * calculate at run time the sset_count. * return strings based on the available statistics of the device * return statistics based on the available statistics of the device * Add support for reading from the second bank. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx.c | 230 +++++++++++++++++++++++--------------------- drivers/net/dsa/mv88e6xxx.h | 8 ++ 2 files changed, 129 insertions(+), 109 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 75e245c4235e..9fe33fc3c2b9 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -617,98 +617,112 @@ static void _mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val) } static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { - { "in_good_octets", 8, 0x00, }, - { "in_bad_octets", 4, 0x02, }, - { "in_unicast", 4, 0x04, }, - { "in_broadcasts", 4, 0x06, }, - { "in_multicasts", 4, 0x07, }, - { "in_pause", 4, 0x16, }, - { "in_undersize", 4, 0x18, }, - { "in_fragments", 4, 0x19, }, - { "in_oversize", 4, 0x1a, }, - { "in_jabber", 4, 0x1b, }, - { "in_rx_error", 4, 0x1c, }, - { "in_fcs_error", 4, 0x1d, }, - { "out_octets", 8, 0x0e, }, - { "out_unicast", 4, 0x10, }, - { "out_broadcasts", 4, 0x13, }, - { "out_multicasts", 4, 0x12, }, - { "out_pause", 4, 0x15, }, - { "excessive", 4, 0x11, }, - { "collisions", 4, 0x1e, }, - { "deferred", 4, 0x05, }, - { "single", 4, 0x14, }, - { "multiple", 4, 0x17, }, - { "out_fcs_error", 4, 0x03, }, - { "late", 4, 0x1f, }, - { "hist_64bytes", 4, 0x08, }, - { "hist_65_127bytes", 4, 0x09, }, - { "hist_128_255bytes", 4, 0x0a, }, - { "hist_256_511bytes", 4, 0x0b, }, - { "hist_512_1023bytes", 4, 0x0c, }, - { "hist_1024_max_bytes", 4, 0x0d, }, - /* Not all devices have the following counters */ - { "sw_in_discards", 4, 0x110, }, - { "sw_in_filtered", 2, 0x112, }, - { "sw_out_filtered", 2, 0x113, }, - + { "in_good_octets", 8, 0x00, BANK0, }, + { "in_bad_octets", 4, 0x02, BANK0, }, + { "in_unicast", 4, 0x04, BANK0, }, + { "in_broadcasts", 4, 0x06, BANK0, }, + { "in_multicasts", 4, 0x07, BANK0, }, + { "in_pause", 4, 0x16, BANK0, }, + { "in_undersize", 4, 0x18, BANK0, }, + { "in_fragments", 4, 0x19, BANK0, }, + { "in_oversize", 4, 0x1a, BANK0, }, + { "in_jabber", 4, 0x1b, BANK0, }, + { "in_rx_error", 4, 0x1c, BANK0, }, + { "in_fcs_error", 4, 0x1d, BANK0, }, + { "out_octets", 8, 0x0e, BANK0, }, + { "out_unicast", 4, 0x10, BANK0, }, + { "out_broadcasts", 4, 0x13, BANK0, }, + { "out_multicasts", 4, 0x12, BANK0, }, + { "out_pause", 4, 0x15, BANK0, }, + { "excessive", 4, 0x11, BANK0, }, + { "collisions", 4, 0x1e, BANK0, }, + { "deferred", 4, 0x05, BANK0, }, + { "single", 4, 0x14, BANK0, }, + { "multiple", 4, 0x17, BANK0, }, + { "out_fcs_error", 4, 0x03, BANK0, }, + { "late", 4, 0x1f, BANK0, }, + { "hist_64bytes", 4, 0x08, BANK0, }, + { "hist_65_127bytes", 4, 0x09, BANK0, }, + { "hist_128_255bytes", 4, 0x0a, BANK0, }, + { "hist_256_511bytes", 4, 0x0b, BANK0, }, + { "hist_512_1023bytes", 4, 0x0c, BANK0, }, + { "hist_1024_max_bytes", 4, 0x0d, BANK0, }, + { "sw_in_discards", 4, 0x10, PORT, }, + { "sw_in_filtered", 2, 0x12, PORT, }, + { "sw_out_filtered", 2, 0x13, PORT, }, + { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, }, + { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, }, }; -static bool have_sw_in_discards(struct dsa_switch *ds) +static bool mv88e6xxx_has_stat(struct dsa_switch *ds, + struct mv88e6xxx_hw_stat *stat) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - - switch (ps->id) { - case PORT_SWITCH_ID_6095: case PORT_SWITCH_ID_6161: - case PORT_SWITCH_ID_6165: case PORT_SWITCH_ID_6171: - case PORT_SWITCH_ID_6172: case PORT_SWITCH_ID_6176: - case PORT_SWITCH_ID_6182: case PORT_SWITCH_ID_6185: - case PORT_SWITCH_ID_6352: + switch (stat->type) { + case BANK0: return true; - default: - return false; - } -} - -static void _mv88e6xxx_get_strings(struct dsa_switch *ds, - int nr_stats, - struct mv88e6xxx_hw_stat *stats, - int port, uint8_t *data) -{ - int i; - - for (i = 0; i < nr_stats; i++) { - memcpy(data + i * ETH_GSTRING_LEN, - stats[i].string, ETH_GSTRING_LEN); + case BANK1: + return mv88e6xxx_6320_family(ds); + case PORT: + return mv88e6xxx_6095_family(ds) || + mv88e6xxx_6185_family(ds) || + mv88e6xxx_6097_family(ds) || + mv88e6xxx_6165_family(ds) || + mv88e6xxx_6351_family(ds) || + mv88e6xxx_6352_family(ds); } + return false; } static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds, - int stat, - struct mv88e6xxx_hw_stat *stats, + struct mv88e6xxx_hw_stat *s, int port) { - struct mv88e6xxx_hw_stat *s = stats + stat; u32 low; u32 high = 0; int ret; u64 value; - if (s->reg >= 0x100) { - ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), - s->reg - 0x100); + switch (s->type) { + case PORT: + ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), s->reg); if (ret < 0) return UINT64_MAX; low = ret; if (s->sizeof_stat == 4) { ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), - s->reg - 0x100 + 1); + s->reg + 1); if (ret < 0) return UINT64_MAX; high = ret; } - } else { + break; + case BANK0: + case BANK1: _mv88e6xxx_stats_read(ds, s->reg, &low); if (s->sizeof_stat == 8) _mv88e6xxx_stats_read(ds, s->reg + 1, &high); @@ -717,61 +731,59 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds, return value; } -static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, - int nr_stats, - struct mv88e6xxx_hw_stat *stats, - int port, uint64_t *data) +void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - int i; - - mutex_lock(&ps->smi_mutex); + struct mv88e6xxx_hw_stat *stat; + int i, j; - ret = _mv88e6xxx_stats_snapshot(ds, port); - if (ret < 0) { - mutex_unlock(&ps->smi_mutex); - return; + for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { + stat = &mv88e6xxx_hw_stats[i]; + if (mv88e6xxx_has_stat(ds, stat)) { + memcpy(data + j * ETH_GSTRING_LEN, stat->string, + ETH_GSTRING_LEN); + j++; + } } - - /* Read each of the counters. */ - for (i = 0; i < nr_stats; i++) - data[i] = _mv88e6xxx_get_ethtool_stat(ds, i, stats, port); - - mutex_unlock(&ps->smi_mutex); -} - -/* All the statistics in the table */ -void -mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data) -{ - if (have_sw_in_discards(ds)) - _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats), - mv88e6xxx_hw_stats, port, data); - else - _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3, - mv88e6xxx_hw_stats, port, data); } int mv88e6xxx_get_sset_count(struct dsa_switch *ds) { - if (have_sw_in_discards(ds)) - return ARRAY_SIZE(mv88e6xxx_hw_stats); - return ARRAY_SIZE(mv88e6xxx_hw_stats) - 3; + struct mv88e6xxx_hw_stat *stat; + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { + stat = &mv88e6xxx_hw_stats[i]; + if (mv88e6xxx_has_stat(ds, stat)) + j++; + } + return j; } void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) { - if (have_sw_in_discards(ds)) - _mv88e6xxx_get_ethtool_stats( - ds, ARRAY_SIZE(mv88e6xxx_hw_stats), - mv88e6xxx_hw_stats, port, data); - else - _mv88e6xxx_get_ethtool_stats( - ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3, - mv88e6xxx_hw_stats, port, data); + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + struct mv88e6xxx_hw_stat *stat; + int ret; + int i, j; + + mutex_lock(&ps->smi_mutex); + + ret = _mv88e6xxx_stats_snapshot(ds, port); + if (ret < 0) { + mutex_unlock(&ps->smi_mutex); + return; + } + for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { + stat = &mv88e6xxx_hw_stats[i]; + if (mv88e6xxx_has_stat(ds, stat)) { + data[j] = _mv88e6xxx_get_ethtool_stat(ds, stat, port); + j++; + } + } + + mutex_unlock(&ps->smi_mutex); } int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 21c8daa03f78..ca08f913d302 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -288,6 +288,7 @@ #define GLOBAL_STATS_OP_HIST_RX ((1 << 10) | GLOBAL_STATS_OP_BUSY) #define GLOBAL_STATS_OP_HIST_TX ((2 << 10) | GLOBAL_STATS_OP_BUSY) #define GLOBAL_STATS_OP_HIST_RX_TX ((3 << 10) | GLOBAL_STATS_OP_BUSY) +#define GLOBAL_STATS_OP_BANK_1 BIT(9) #define GLOBAL_STATS_COUNTER_32 0x1e #define GLOBAL_STATS_COUNTER_01 0x1f @@ -420,10 +421,17 @@ struct mv88e6xxx_priv_state { struct work_struct bridge_work; }; +enum stat_type { + BANK0, + BANK1, + PORT, +}; + struct mv88e6xxx_hw_stat { char string[ETH_GSTRING_LEN]; int sizeof_stat; int reg; + enum stat_type type; }; int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active); -- cgit v1.2.3-59-g8ed1b From 8099c9edfd96d91b1f01b31ca5270ba3bceb0c9b Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 23 Dec 2015 20:42:20 +0800 Subject: bonding: drop unused to_dev macro in bond_sysfs.c to_dev is not used anymore so drop it. Signed-off-by: Geliang Tang Signed-off-by: David S. Miller --- drivers/net/bonding/bond_sysfs.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index f4ae72086215..313dbac207ee 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -42,7 +42,6 @@ #include -#define to_dev(obj) container_of(obj, struct device, kobj) #define to_bond(cd) ((struct bonding *)(netdev_priv(to_net_dev(cd)))) /* "show" function for the bond_masters attribute. -- cgit v1.2.3-59-g8ed1b From e894d720a5c501c4a2a01ae64f732046d1f48b76 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 22:47:11 +0530 Subject: cxgb4: Pass correct argument to t4_link_l1cfg() Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index 2a61a42ab033..7a0b92b2f73c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -686,7 +686,7 @@ static int set_pauseparam(struct net_device *dev, if (epause->tx_pause) lc->requested_fc |= PAUSE_TX; if (netif_running(dev)) - return t4_link_l1cfg(p->adapter, p->adapter->pf, p->tx_chan, + return t4_link_l1cfg(p->adapter, p->adapter->mbox, p->tx_chan, lc); return 0; } -- cgit v1.2.3-59-g8ed1b From 44588560d95c105cc9dfa82180a6f542adea9ac9 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 22:47:12 +0530 Subject: cxgb4: Update pm_stats for T6 adapter family Updated pm_stats code to display input FIFO wait (index 5) and read latency (index 7) counters for T6 adapters Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 1 + drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 30 ++++++++++++++++++++-- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 7 +++-- drivers/net/ethernet/chelsio/cxgb4/t4_hw.h | 1 + 4 files changed, 35 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 3b59bc4038a6..eab57cf29aa1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -301,6 +301,7 @@ struct devlog_params { /* Stores chip specific parameters */ struct arch_specific_params { u8 nchan; + u8 pm_stats_cnt; u16 mps_rplc_size; u16 vfcount; u32 sge_fl_db; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 62a343fc9c1a..c6e92cc0aa09 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -757,8 +757,8 @@ static int pm_stats_show(struct seq_file *seq, void *v) }; int i; - u32 tx_cnt[PM_NSTATS], rx_cnt[PM_NSTATS]; - u64 tx_cyc[PM_NSTATS], rx_cyc[PM_NSTATS]; + u32 tx_cnt[T6_PM_NSTATS], rx_cnt[T6_PM_NSTATS]; + u64 tx_cyc[T6_PM_NSTATS], rx_cyc[T6_PM_NSTATS]; struct adapter *adap = seq->private; t4_pmtx_get_stats(adap, tx_cnt, tx_cyc); @@ -773,6 +773,32 @@ static int pm_stats_show(struct seq_file *seq, void *v) for (i = 0; i < PM_NSTATS - 1; i++) seq_printf(seq, "%-13s %10u %20llu\n", rx_pm_stats[i], rx_cnt[i], rx_cyc[i]); + + if (CHELSIO_CHIP_VERSION(adap->params.chip) > CHELSIO_T5) { + /* In T5 the granularity of the total wait is too fine. + * It is not useful as it reaches the max value too fast. + * Hence display this Input FIFO wait for T6 onwards. + */ + seq_printf(seq, "%13s %10s %20s\n", + " ", "Total wait", "Total Occupancy"); + seq_printf(seq, "Tx FIFO wait %10u %20llu\n", + tx_cnt[i], tx_cyc[i]); + seq_printf(seq, "Rx FIFO wait %10u %20llu\n", + rx_cnt[i], rx_cyc[i]); + + /* Skip index 6 as there is nothing useful ihere */ + i += 2; + + /* At index 7, a new stat for read latency (count, total wait) + * is added. + */ + seq_printf(seq, "%13s %10s %20s\n", + " ", "Reads", "Total wait"); + seq_printf(seq, "Tx latency %10u %20llu\n", + tx_cnt[i], tx_cyc[i]); + seq_printf(seq, "Rx latency %10u %20llu\n", + rx_cnt[i], rx_cyc[i]); + } return 0; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index cf61a5869c6e..5266011e530b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -5254,7 +5254,7 @@ void t4_pmtx_get_stats(struct adapter *adap, u32 cnt[], u64 cycles[]) int i; u32 data[2]; - for (i = 0; i < PM_NSTATS; i++) { + for (i = 0; i < adap->params.arch.pm_stats_cnt; i++) { t4_write_reg(adap, PM_TX_STAT_CONFIG_A, i + 1); cnt[i] = t4_read_reg(adap, PM_TX_STAT_COUNT_A); if (is_t4(adap->params.chip)) { @@ -5281,7 +5281,7 @@ void t4_pmrx_get_stats(struct adapter *adap, u32 cnt[], u64 cycles[]) int i; u32 data[2]; - for (i = 0; i < PM_NSTATS; i++) { + for (i = 0; i < adap->params.arch.pm_stats_cnt; i++) { t4_write_reg(adap, PM_RX_STAT_CONFIG_A, i + 1); cnt[i] = t4_read_reg(adap, PM_RX_STAT_COUNT_A); if (is_t4(adap->params.chip)) { @@ -7060,6 +7060,7 @@ int t4_prep_adapter(struct adapter *adapter) NUM_MPS_CLS_SRAM_L_INSTANCES; adapter->params.arch.mps_rplc_size = 128; adapter->params.arch.nchan = NCHAN; + adapter->params.arch.pm_stats_cnt = PM_NSTATS; adapter->params.arch.vfcount = 128; break; case CHELSIO_T5: @@ -7069,6 +7070,7 @@ int t4_prep_adapter(struct adapter *adapter) NUM_MPS_T5_CLS_SRAM_L_INSTANCES; adapter->params.arch.mps_rplc_size = 128; adapter->params.arch.nchan = NCHAN; + adapter->params.arch.pm_stats_cnt = PM_NSTATS; adapter->params.arch.vfcount = 128; break; case CHELSIO_T6: @@ -7078,6 +7080,7 @@ int t4_prep_adapter(struct adapter *adapter) NUM_MPS_T5_CLS_SRAM_L_INSTANCES; adapter->params.arch.mps_rplc_size = 256; adapter->params.arch.nchan = 2; + adapter->params.arch.pm_stats_cnt = T6_PM_NSTATS; adapter->params.arch.vfcount = 256; break; default: diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h index 13708fde1668..2fc60e83a7a1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h @@ -48,6 +48,7 @@ enum { NMTUS = 16, /* size of MTU table */ NCCTRL_WIN = 32, /* # of congestion control windows */ PM_NSTATS = 5, /* # of PM stats */ + T6_PM_NSTATS = 7, /* # of PM stats in T6 */ MBOX_LEN = 64, /* mailbox size in bytes */ TRACE_LEN = 112, /* length of trace data and mask */ FILTER_OPT_LEN = 36, /* filter tuple width for optional components */ -- cgit v1.2.3-59-g8ed1b From acac596286bcadda964d0e15dbbd8bdebedcaa41 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 22:47:13 +0530 Subject: cxgb4/cxgb4vf: Update Ingress padding boundary values for T6 adapter Ingress padding boundary values got changed for T6. T5: 0=32B 1=64B 2=128B 3=256B 4=512B 5=1024B 6=2048B 7=4096B T6: 0=8B 1=16B 2=32B 3=64B 4=128B 5=128B 6=256B 7=512B Updating the driver to set the correct boundary values in SGE_CONTROL to 32B. Also, need to take care of this fl alignment change when calculating the next packet offset. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 1 + drivers/net/ethernet/chelsio/cxgb4/sge.c | 33 +------------- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 62 +++++++++++++++++++++++++- drivers/net/ethernet/chelsio/cxgb4/t4_values.h | 3 ++ drivers/net/ethernet/chelsio/cxgb4vf/sge.c | 11 ++++- 5 files changed, 76 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index eab57cf29aa1..5c193a5f564e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -1256,6 +1256,7 @@ int t4_phy_fw_ver(struct adapter *adap, int *phy_fw_ver); int t4_fwcache(struct adapter *adap, enum fw_params_param_dev_fwcache op); int t4_fw_upgrade(struct adapter *adap, unsigned int mbox, const u8 *fw_data, unsigned int size, int force); +int t4_fl_pkt_align(struct adapter *adap); unsigned int t4_flash_cfg_addr(struct adapter *adapter); int t4_check_fw_version(struct adapter *adap); int t4_get_fw_version(struct adapter *adapter, u32 *vers); diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 5e3ffa73ee13..099b946f017f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -3173,8 +3173,7 @@ static int t4_sge_init_soft(struct adapter *adap) int t4_sge_init(struct adapter *adap) { struct sge *s = &adap->sge; - u32 sge_control, sge_control2, sge_conm_ctrl; - unsigned int ingpadboundary, ingpackboundary; + u32 sge_control, sge_conm_ctrl; int ret, egress_threshold; /* @@ -3185,35 +3184,7 @@ int t4_sge_init(struct adapter *adap) s->pktshift = PKTSHIFT_G(sge_control); s->stat_len = (sge_control & EGRSTATUSPAGESIZE_F) ? 128 : 64; - /* T4 uses a single control field to specify both the PCIe Padding and - * Packing Boundary. T5 introduced the ability to specify these - * separately. The actual Ingress Packet Data alignment boundary - * within Packed Buffer Mode is the maximum of these two - * specifications. (Note that it makes no real practical sense to - * have the Pading Boudary be larger than the Packing Boundary but you - * could set the chip up that way and, in fact, legacy T4 code would - * end doing this because it would initialize the Padding Boundary and - * leave the Packing Boundary initialized to 0 (16 bytes).) - */ - ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_control) + - INGPADBOUNDARY_SHIFT_X); - if (is_t4(adap->params.chip)) { - s->fl_align = ingpadboundary; - } else { - /* T5 has a different interpretation of one of the PCIe Packing - * Boundary values. - */ - sge_control2 = t4_read_reg(adap, SGE_CONTROL2_A); - ingpackboundary = INGPACKBOUNDARY_G(sge_control2); - if (ingpackboundary == INGPACKBOUNDARY_16B_X) - ingpackboundary = 16; - else - ingpackboundary = 1 << (ingpackboundary + - INGPACKBOUNDARY_SHIFT_X); - - s->fl_align = max(ingpadboundary, ingpackboundary); - } - + s->fl_align = t4_fl_pkt_align(adap); ret = t4_sge_init_soft(adap); if (ret < 0) return ret; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 5266011e530b..42754c06b7d8 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -6096,6 +6096,59 @@ int t4_fw_upgrade(struct adapter *adap, unsigned int mbox, return t4_fw_restart(adap, mbox, reset); } +/** + * t4_fl_pkt_align - return the fl packet alignment + * @adap: the adapter + * + * T4 has a single field to specify the packing and padding boundary. + * T5 onwards has separate fields for this and hence the alignment for + * next packet offset is maximum of these two. + * + */ +int t4_fl_pkt_align(struct adapter *adap) +{ + u32 sge_control, sge_control2; + unsigned int ingpadboundary, ingpackboundary, fl_align, ingpad_shift; + + sge_control = t4_read_reg(adap, SGE_CONTROL_A); + + /* T4 uses a single control field to specify both the PCIe Padding and + * Packing Boundary. T5 introduced the ability to specify these + * separately. The actual Ingress Packet Data alignment boundary + * within Packed Buffer Mode is the maximum of these two + * specifications. (Note that it makes no real practical sense to + * have the Pading Boudary be larger than the Packing Boundary but you + * could set the chip up that way and, in fact, legacy T4 code would + * end doing this because it would initialize the Padding Boundary and + * leave the Packing Boundary initialized to 0 (16 bytes).) + * Padding Boundary values in T6 starts from 8B, + * where as it is 32B for T4 and T5. + */ + if (CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5) + ingpad_shift = INGPADBOUNDARY_SHIFT_X; + else + ingpad_shift = T6_INGPADBOUNDARY_SHIFT_X; + + ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_control) + ingpad_shift); + + fl_align = ingpadboundary; + if (!is_t4(adap->params.chip)) { + /* T5 has a weird interpretation of one of the PCIe Packing + * Boundary values. No idea why ... + */ + sge_control2 = t4_read_reg(adap, SGE_CONTROL2_A); + ingpackboundary = INGPACKBOUNDARY_G(sge_control2); + if (ingpackboundary == INGPACKBOUNDARY_16B_X) + ingpackboundary = 16; + else + ingpackboundary = 1 << (ingpackboundary + + INGPACKBOUNDARY_SHIFT_X); + + fl_align = max(ingpadboundary, ingpackboundary); + } + return fl_align; +} + /** * t4_fixup_host_params - fix up host-dependent parameters * @adap: the adapter @@ -6114,6 +6167,7 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, unsigned int stat_len = cache_line_size > 64 ? 128 : 64; unsigned int fl_align = cache_line_size < 32 ? 32 : cache_line_size; unsigned int fl_align_log = fls(fl_align) - 1; + unsigned int ingpad; t4_write_reg(adap, SGE_HOST_PAGE_SIZE_A, HOSTPAGESIZEPF0_V(sge_hps) | @@ -6161,10 +6215,16 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, fl_align = 64; fl_align_log = 6; } + + if (is_t5(adap->params.chip)) + ingpad = INGPCIEBOUNDARY_32B_X; + else + ingpad = T6_INGPADBOUNDARY_32B_X; + t4_set_reg_field(adap, SGE_CONTROL_A, INGPADBOUNDARY_V(INGPADBOUNDARY_M) | EGRSTATUSPAGESIZE_F, - INGPADBOUNDARY_V(INGPCIEBOUNDARY_32B_X) | + INGPADBOUNDARY_V(ingpad) | EGRSTATUSPAGESIZE_V(stat_len != 64)); t4_set_reg_field(adap, SGE_CONTROL2_A, INGPACKBOUNDARY_V(INGPACKBOUNDARY_M), diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h index 7bdee3bf75ec..a5231fa771db 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h @@ -53,6 +53,9 @@ #define INGPADBOUNDARY_SHIFT_X 5 +#define T6_INGPADBOUNDARY_SHIFT_X 3 +#define T6_INGPADBOUNDARY_32B_X 2 + /* CONTROL2 register */ #define INGPACKBOUNDARY_SHIFT_X 5 #define INGPACKBOUNDARY_16B_X 0 diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index fa3786a9d30e..6528231d8a59 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -2607,7 +2607,7 @@ int t4vf_sge_init(struct adapter *adapter) u32 fl0 = sge_params->sge_fl_buffer_size[0]; u32 fl1 = sge_params->sge_fl_buffer_size[1]; struct sge *s = &adapter->sge; - unsigned int ingpadboundary, ingpackboundary; + unsigned int ingpadboundary, ingpackboundary, ingpad_shift; /* * Start by vetting the basic SGE parameters which have been set up by @@ -2642,9 +2642,16 @@ int t4vf_sge_init(struct adapter *adapter) * could set the chip up that way and, in fact, legacy T4 code would * end doing this because it would initialize the Padding Boundary and * leave the Packing Boundary initialized to 0 (16 bytes).) + * Padding Boundary values in T6 starts from 8B, + * where as it is 32B for T4 and T5. */ + if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) + ingpad_shift = INGPADBOUNDARY_SHIFT_X; + else + ingpad_shift = T6_INGPADBOUNDARY_SHIFT_X; + ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_params->sge_control) + - INGPADBOUNDARY_SHIFT_X); + ingpad_shift); if (is_t4(adapter->params.chip)) { s->fl_align = ingpadboundary; } else { -- cgit v1.2.3-59-g8ed1b From 676d6a753066e9b5ae6c0a6ce8ec8cf9dabc4352 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 22:47:14 +0530 Subject: cxgb4: Update register range and SGE registers for T6 adapter Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 5 +++-- drivers/net/ethernet/chelsio/cxgb4/sge.c | 15 +++++++++++++-- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 16 +++++++++++----- drivers/net/ethernet/chelsio/cxgb4/t4_regs.h | 3 +++ 4 files changed, 30 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 2642593289d9..c0dd533eeafb 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -4855,8 +4855,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* configure SGE_STAT_CFG_A to read WC stats */ if (!is_t4(adapter->params.chip)) - t4_write_reg(adapter, SGE_STAT_CFG_A, - STATSOURCE_T5_V(7) | STATMODE_V(0)); + t4_write_reg(adapter, SGE_STAT_CFG_A, STATSOURCE_T5_V(7) | + (is_t5(adapter->params.chip) ? STATMODE_V(0) : + T6_STATMODE_V(0))); for_each_port(adapter, i) { struct net_device *netdev; diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 099b946f017f..5829c9caa1fa 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -3202,10 +3202,21 @@ int t4_sge_init(struct adapter *adap) * buffers. */ sge_conm_ctrl = t4_read_reg(adap, SGE_CONM_CTRL_A); - if (is_t4(adap->params.chip)) + switch (CHELSIO_CHIP_VERSION(adap->params.chip)) { + case CHELSIO_T4: egress_threshold = EGRTHRESHOLD_G(sge_conm_ctrl); - else + break; + case CHELSIO_T5: egress_threshold = EGRTHRESHOLDPACKING_G(sge_conm_ctrl); + break; + case CHELSIO_T6: + egress_threshold = T6_EGRTHRESHOLDPACKING_G(sge_conm_ctrl); + break; + default: + dev_err(adap->pdev_dev, "Unsupported Chip version %d\n", + CHELSIO_CHIP_VERSION(adap->params.chip)); + return -EINVAL; + } s->fl_starve_thres = 2*egress_threshold + 1; t4_idma_monitor_init(adap, &s->idma_monitor); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 42754c06b7d8..c613bd6a6091 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -1942,8 +1942,12 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x1190, 0x1194, 0x11a0, 0x11a4, 0x11b0, 0x11b4, - 0x11fc, 0x1254, - 0x1280, 0x133c, + 0x11fc, 0x1258, + 0x1280, 0x12d4, + 0x12d9, 0x12d9, + 0x12de, 0x12de, + 0x12e3, 0x12e3, + 0x12e8, 0x133c, 0x1800, 0x18fc, 0x3000, 0x302c, 0x3060, 0x30b0, @@ -1973,7 +1977,7 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x5e50, 0x5e94, 0x5ea0, 0x5eb0, 0x5ec0, 0x5ec0, - 0x5ec8, 0x5ecc, + 0x5ec8, 0x5ed0, 0x6000, 0x6020, 0x6028, 0x6040, 0x6058, 0x609c, @@ -2048,7 +2052,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x19150, 0x19194, 0x1919c, 0x191b0, 0x191d0, 0x191e8, - 0x19238, 0x192b0, + 0x19238, 0x19290, + 0x192a4, 0x192b0, 0x192bc, 0x192bc, 0x19348, 0x1934c, 0x193f8, 0x19418, @@ -2442,7 +2447,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x40280, 0x40280, 0x40304, 0x40304, 0x40330, 0x4033c, - 0x41304, 0x413c8, + 0x41304, 0x413b8, + 0x413c0, 0x413c8, 0x413d0, 0x413dc, 0x413f0, 0x413f0, 0x41400, 0x4140c, diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 91b52a21a2e7..0971cc41c747 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -492,6 +492,9 @@ #define STATSOURCE_T5_V(x) ((x) << STATSOURCE_T5_S) #define STATSOURCE_T5_G(x) (((x) >> STATSOURCE_T5_S) & STATSOURCE_T5_M) +#define T6_STATMODE_S 0 +#define T6_STATMODE_V(x) ((x) << T6_STATMODE_S) + #define SGE_DBFIFO_STATUS2_A 0x1118 #define HP_INT_THRESH_T5_S 10 -- cgit v1.2.3-59-g8ed1b From e9faeab8d48fc8962e4a244a6097048acebde47b Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 22:47:15 +0530 Subject: cxgb4: Update Congestion Channel map for T6 adapter Updating Congestion Channel/Priority Map in Congestion Manager Context for T6. In T6 port 0 is mapped to channel 0 and port 1 is mapped to channel 1. For 2 port T4/T5 adapter, port 0 is mapped to channel 0,1 and port 1 is mapped to channel 2,3 Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index c613bd6a6091..2f8c73630410 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -5316,7 +5316,14 @@ unsigned int t4_get_mps_bg_map(struct adapter *adap, int idx) if (n == 0) return idx == 0 ? 0xf : 0; - if (n == 1) + /* In T6 (which is a 2 port card), + * port 0 is mapped to channel 0 and port 1 is mapped to channel 1. + * For 2 port T4/T5 adapter, + * port 0 is mapped to channel 0 and 1, + * port 1 is mapped to channel 2 and 3. + */ + if ((n == 1) && + (CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5)) return idx < 2 ? (3 << (2 * idx)) : 0; return 1 << idx; } -- cgit v1.2.3-59-g8ed1b From 6df397539cb0200b237e9b7b1665d26d5e01d01a Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 22:47:16 +0530 Subject: cxgb4: Update correct encoding of SGE Ingress DMA States for T6 adapter Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 59 ++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 2f8c73630410..0132b6492611 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -5702,6 +5702,39 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state) "IDMA_FL_SEND_PADDING", "IDMA_FL_SEND_COMPLETION_TO_IMSG", }; + static const char * const t6_decode[] = { + "IDMA_IDLE", + "IDMA_PUSH_MORE_CPL_FIFO", + "IDMA_PUSH_CPL_MSG_HEADER_TO_FIFO", + "IDMA_SGEFLRFLUSH_SEND_PCIEHDR", + "IDMA_PHYSADDR_SEND_PCIEHDR", + "IDMA_PHYSADDR_SEND_PAYLOAD_FIRST", + "IDMA_PHYSADDR_SEND_PAYLOAD", + "IDMA_FL_REQ_DATA_FL", + "IDMA_FL_DROP", + "IDMA_FL_DROP_SEND_INC", + "IDMA_FL_H_REQ_HEADER_FL", + "IDMA_FL_H_SEND_PCIEHDR", + "IDMA_FL_H_PUSH_CPL_FIFO", + "IDMA_FL_H_SEND_CPL", + "IDMA_FL_H_SEND_IP_HDR_FIRST", + "IDMA_FL_H_SEND_IP_HDR", + "IDMA_FL_H_REQ_NEXT_HEADER_FL", + "IDMA_FL_H_SEND_NEXT_PCIEHDR", + "IDMA_FL_H_SEND_IP_HDR_PADDING", + "IDMA_FL_D_SEND_PCIEHDR", + "IDMA_FL_D_SEND_CPL_AND_IP_HDR", + "IDMA_FL_D_REQ_NEXT_DATA_FL", + "IDMA_FL_SEND_PCIEHDR", + "IDMA_FL_PUSH_CPL_FIFO", + "IDMA_FL_SEND_CPL", + "IDMA_FL_SEND_PAYLOAD_FIRST", + "IDMA_FL_SEND_PAYLOAD", + "IDMA_FL_REQ_NEXT_DATA_FL", + "IDMA_FL_SEND_NEXT_PCIEHDR", + "IDMA_FL_SEND_PADDING", + "IDMA_FL_SEND_COMPLETION_TO_IMSG", + }; static const u32 sge_regs[] = { SGE_DEBUG_DATA_LOW_INDEX_2_A, SGE_DEBUG_DATA_LOW_INDEX_3_A, @@ -5710,6 +5743,32 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state) const char **sge_idma_decode; int sge_idma_decode_nstates; int i; + unsigned int chip_version = CHELSIO_CHIP_VERSION(adapter->params.chip); + + /* Select the right set of decode strings to dump depending on the + * adapter chip type. + */ + switch (chip_version) { + case CHELSIO_T4: + sge_idma_decode = (const char **)t4_decode; + sge_idma_decode_nstates = ARRAY_SIZE(t4_decode); + break; + + case CHELSIO_T5: + sge_idma_decode = (const char **)t5_decode; + sge_idma_decode_nstates = ARRAY_SIZE(t5_decode); + break; + + case CHELSIO_T6: + sge_idma_decode = (const char **)t6_decode; + sge_idma_decode_nstates = ARRAY_SIZE(t6_decode); + break; + + default: + dev_err(adapter->pdev_dev, + "Unsupported chip version %d\n", chip_version); + return; + } if (is_t4(adapter->params.chip)) { sge_idma_decode = (const char **)t4_decode; -- cgit v1.2.3-59-g8ed1b From 115b56af88b538147cf241d0f75a370a73009ed9 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 22:47:17 +0530 Subject: cxgb4: Update mps_tcam output to include T6 fields In T6, MPS classification has a 512 deep TCAM to do the match lookup. Each entry has 80x2b sets containing 48 bit MAC address, port number, VLAN Valid/ID, VNI, lookup type (outer or inner packet header). [71:48] bit locations are overloaded for outer vs. inner lookup types. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 100 +++++++++++++++++---- drivers/net/ethernet/chelsio/cxgb4/t4_regs.h | 24 +++++ 2 files changed, 105 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index c6e92cc0aa09..1bab34f923e7 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -1585,25 +1585,35 @@ static int mps_tcam_show(struct seq_file *seq, void *v) { struct adapter *adap = seq->private; unsigned int chip_ver = CHELSIO_CHIP_VERSION(adap->params.chip); - if (v == SEQ_START_TOKEN) { - if (adap->params.arch.mps_rplc_size > 128) + if (chip_ver > CHELSIO_T5) { seq_puts(seq, "Idx Ethernet address Mask " + " VNI Mask IVLAN Vld " + "DIP_Hit Lookup Port " "Vld Ports PF VF " "Replication " " P0 P1 P2 P3 ML\n"); - else - seq_puts(seq, "Idx Ethernet address Mask " - "Vld Ports PF VF Replication" - " P0 P1 P2 P3 ML\n"); + } else { + if (adap->params.arch.mps_rplc_size > 128) + seq_puts(seq, "Idx Ethernet address Mask " + "Vld Ports PF VF " + "Replication " + " P0 P1 P2 P3 ML\n"); + else + seq_puts(seq, "Idx Ethernet address Mask " + "Vld Ports PF VF Replication" + " P0 P1 P2 P3 ML\n"); + } } else { u64 mask; u8 addr[ETH_ALEN]; - bool replicate; + bool replicate, dip_hit = false, vlan_vld = false; unsigned int idx = (uintptr_t)v - 2; u64 tcamy, tcamx, val; - u32 cls_lo, cls_hi, ctl; + u32 cls_lo, cls_hi, ctl, data2, vnix = 0, vniy = 0; u32 rplc[8] = {0}; + u8 lookup_type = 0, port_num = 0; + u16 ivlan = 0; if (chip_ver > CHELSIO_T5) { /* CtlCmdType - 0: Read, 1: Write @@ -1622,6 +1632,22 @@ static int mps_tcam_show(struct seq_file *seq, void *v) val = t4_read_reg(adap, MPS_CLS_TCAM_DATA1_A); tcamy = DMACH_G(val) << 32; tcamy |= t4_read_reg(adap, MPS_CLS_TCAM_DATA0_A); + data2 = t4_read_reg(adap, MPS_CLS_TCAM_DATA2_CTL_A); + lookup_type = DATALKPTYPE_G(data2); + /* 0 - Outer header, 1 - Inner header + * [71:48] bit locations are overloaded for + * outer vs. inner lookup types. + */ + if (lookup_type && (lookup_type != DATALKPTYPE_M)) { + /* Inner header VNI */ + vniy = ((data2 & DATAVIDH2_F) << 23) | + (DATAVIDH1_G(data2) << 16) | VIDL_G(val); + dip_hit = data2 & DATADIPHIT_F; + } else { + vlan_vld = data2 & DATAVIDH2_F; + ivlan = VIDL_G(val); + } + port_num = DATAPORTNUM_G(data2); /* Read tcamx. Change the control param */ ctl |= CTLXYBITSEL_V(1); @@ -1629,6 +1655,12 @@ static int mps_tcam_show(struct seq_file *seq, void *v) val = t4_read_reg(adap, MPS_CLS_TCAM_DATA1_A); tcamx = DMACH_G(val) << 32; tcamx |= t4_read_reg(adap, MPS_CLS_TCAM_DATA0_A); + data2 = t4_read_reg(adap, MPS_CLS_TCAM_DATA2_CTL_A); + if (lookup_type && (lookup_type != DATALKPTYPE_M)) { + /* Inner header VNI mask */ + vnix = ((data2 & DATAVIDH2_F) << 23) | + (DATAVIDH1_G(data2) << 16) | VIDL_G(val); + } } else { tcamy = t4_read_reg64(adap, MPS_CLS_TCAM_Y_L(idx)); tcamx = t4_read_reg64(adap, MPS_CLS_TCAM_X_L(idx)); @@ -1688,17 +1720,47 @@ static int mps_tcam_show(struct seq_file *seq, void *v) } tcamxy2valmask(tcamx, tcamy, addr, &mask); - if (chip_ver > CHELSIO_T5) - seq_printf(seq, "%3u %02x:%02x:%02x:%02x:%02x:%02x " - "%012llx%3c %#x%4u%4d", - idx, addr[0], addr[1], addr[2], addr[3], - addr[4], addr[5], (unsigned long long)mask, - (cls_lo & T6_SRAM_VLD_F) ? 'Y' : 'N', - PORTMAP_G(cls_hi), - T6_PF_G(cls_lo), - (cls_lo & T6_VF_VALID_F) ? - T6_VF_G(cls_lo) : -1); - else + if (chip_ver > CHELSIO_T5) { + /* Inner header lookup */ + if (lookup_type && (lookup_type != DATALKPTYPE_M)) { + seq_printf(seq, + "%3u %02x:%02x:%02x:%02x:%02x:%02x " + "%012llx %06x %06x - - %3c" + " %3c %4x " + "%3c %#x%4u%4d", idx, addr[0], + addr[1], addr[2], addr[3], + addr[4], addr[5], + (unsigned long long)mask, + vniy, vnix, dip_hit ? 'Y' : 'N', + lookup_type ? 'I' : 'O', port_num, + (cls_lo & T6_SRAM_VLD_F) ? 'Y' : 'N', + PORTMAP_G(cls_hi), + T6_PF_G(cls_lo), + (cls_lo & T6_VF_VALID_F) ? + T6_VF_G(cls_lo) : -1); + } else { + seq_printf(seq, + "%3u %02x:%02x:%02x:%02x:%02x:%02x " + "%012llx - - ", + idx, addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5], + (unsigned long long)mask); + + if (vlan_vld) + seq_printf(seq, "%4u Y ", ivlan); + else + seq_puts(seq, " - N "); + + seq_printf(seq, + "- %3c %4x %3c %#x%4u%4d", + lookup_type ? 'I' : 'O', port_num, + (cls_lo & T6_SRAM_VLD_F) ? 'Y' : 'N', + PORTMAP_G(cls_hi), + T6_PF_G(cls_lo), + (cls_lo & T6_VF_VALID_F) ? + T6_VF_G(cls_lo) : -1); + } + } else seq_printf(seq, "%3u %02x:%02x:%02x:%02x:%02x:%02x " "%012llx%3c %#x%4u%4d", idx, addr[0], addr[1], addr[2], addr[3], diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 0971cc41c747..9fea255c7e87 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -2398,6 +2398,30 @@ #define MPS_CLS_TCAM_DATA0_A 0xf000 #define MPS_CLS_TCAM_DATA1_A 0xf004 +#define VIDL_S 16 +#define VIDL_M 0xffffU +#define VIDL_G(x) (((x) >> VIDL_S) & VIDL_M) + +#define DATALKPTYPE_S 10 +#define DATALKPTYPE_M 0x3U +#define DATALKPTYPE_G(x) (((x) >> DATALKPTYPE_S) & DATALKPTYPE_M) + +#define DATAPORTNUM_S 12 +#define DATAPORTNUM_M 0xfU +#define DATAPORTNUM_G(x) (((x) >> DATAPORTNUM_S) & DATAPORTNUM_M) + +#define DATADIPHIT_S 8 +#define DATADIPHIT_V(x) ((x) << DATADIPHIT_S) +#define DATADIPHIT_F DATADIPHIT_V(1U) + +#define DATAVIDH2_S 7 +#define DATAVIDH2_V(x) ((x) << DATAVIDH2_S) +#define DATAVIDH2_F DATAVIDH2_V(1U) + +#define DATAVIDH1_S 0 +#define DATAVIDH1_M 0x7fU +#define DATAVIDH1_G(x) (((x) >> DATAVIDH1_S) & DATAVIDH1_M) + #define USED_S 16 #define USED_M 0x7ffU #define USED_G(x) (((x) >> USED_S) & USED_M) -- cgit v1.2.3-59-g8ed1b From 2216d01432cb1589d1bfbd2722f2ec05cb26ea51 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 22:47:18 +0530 Subject: cxgb4: Update SGE context congestion map change for T6 adapter SGE context congestion map changed from 4 to 8 priority per port in T6 as there are only 2 channels. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4.h | 1 + drivers/net/ethernet/chelsio/cxgb4/sge.c | 7 ++++--- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 9 +++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 5c193a5f564e..ec6e849676c1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -302,6 +302,7 @@ struct devlog_params { struct arch_specific_params { u8 nchan; u8 pm_stats_cnt; + u8 cng_ch_bits_log; /* congestion channel map bits width */ u16 mps_rplc_size; u16 vfcount; u32 sge_fl_db; diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index 5829c9caa1fa..b4eb4680a27c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -2670,8 +2670,9 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, * simple (and hopefully less wrong). */ if (!is_t4(adap->params.chip) && cong >= 0) { - u32 param, val; + u32 param, val, ch_map = 0; int i; + u16 cng_ch_bits_log = adap->params.arch.cng_ch_bits_log; param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) | FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DMAQ_CONM_CTXT) | @@ -2683,9 +2684,9 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, CONMCTXT_CNGTPMODE_V(CONMCTXT_CNGTPMODE_CHANNEL_X); for (i = 0; i < 4; i++) { if (cong & (1 << i)) - val |= - CONMCTXT_CNGCHMAP_V(1 << (i << 2)); + ch_map |= 1 << (i << cng_ch_bits_log); } + val |= CONMCTXT_CNGCHMAP_V(ch_map); } ret = t4_set_params(adap, adap->mbox, adap->pf, 0, 1, ¶m, &val); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 0132b6492611..636b4691f252 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -7194,6 +7194,10 @@ int t4_prep_adapter(struct adapter *adapter) adapter->params.arch.nchan = NCHAN; adapter->params.arch.pm_stats_cnt = PM_NSTATS; adapter->params.arch.vfcount = 128; + /* Congestion map is for 4 channels so that + * MPS can have 4 priority per port. + */ + adapter->params.arch.cng_ch_bits_log = 2; break; case CHELSIO_T5: adapter->params.chip |= CHELSIO_CHIP_CODE(CHELSIO_T5, pl_rev); @@ -7204,6 +7208,7 @@ int t4_prep_adapter(struct adapter *adapter) adapter->params.arch.nchan = NCHAN; adapter->params.arch.pm_stats_cnt = PM_NSTATS; adapter->params.arch.vfcount = 128; + adapter->params.arch.cng_ch_bits_log = 2; break; case CHELSIO_T6: adapter->params.chip |= CHELSIO_CHIP_CODE(CHELSIO_T6, pl_rev); @@ -7214,6 +7219,10 @@ int t4_prep_adapter(struct adapter *adapter) adapter->params.arch.nchan = 2; adapter->params.arch.pm_stats_cnt = T6_PM_NSTATS; adapter->params.arch.vfcount = 256; + /* Congestion map will be for 2 channels so that + * MPS can have 8 priority per port. + */ + adapter->params.arch.cng_ch_bits_log = 3; break; default: dev_err(adapter->pdev_dev, "Device %d is not supported\n", -- cgit v1.2.3-59-g8ed1b From 10aa3b78e9446b6d31262671dc1faed8d950d090 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Wed, 23 Dec 2015 22:47:19 +0530 Subject: cxgb4vf: Update to 128 byte mailbox size for T6 adapter Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h | 1 + drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h index b516b12b1884..f859db3d254c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h @@ -54,6 +54,7 @@ #define T4VF_MPS_BASE_ADDR 0x0100 #define T4VF_PL_BASE_ADDR 0x0200 #define T4VF_MBDATA_BASE_ADDR 0x0240 +#define T6VF_MBDATA_BASE_ADDR 0x0280 #define T4VF_CIM_BASE_ADDR 0x0300 #define T4VF_REGMAP_START 0x0000 diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index 63dd5fdac5b9..b6fa74aafe47 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -120,12 +120,19 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, 1, 1, 3, 5, 10, 10, 20, 50, 100 }; - u32 v; + u32 v, mbox_data; int i, ms, delay_idx; const __be64 *p; - u32 mbox_data = T4VF_MBDATA_BASE_ADDR; u32 mbox_ctl = T4VF_CIM_BASE_ADDR + CIM_VF_EXT_MAILBOX_CTRL; + /* In T6, mailbox size is changed to 128 bytes to avoid + * invalidating the entire prefetch buffer. + */ + if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) + mbox_data = T4VF_MBDATA_BASE_ADDR; + else + mbox_data = T6VF_MBDATA_BASE_ADDR; + /* * Commands must be multiples of 16 bytes in length and may not be * larger than the size of the Mailbox Data register array. -- cgit v1.2.3-59-g8ed1b From a99c683e0cd2e55fa314de40ca5e329b05c6d166 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 24 Dec 2015 16:15:17 +0530 Subject: cxgb4: Get TID calculation right for IPv6 mode CLIP is always enabled and hardware uses 2 TID entries instead of 4 for IPv6 in CLIP mode. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index c0dd533eeafb..bfe50aafb312 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -1506,7 +1506,7 @@ int cxgb4_alloc_stid(struct tid_info *t, int family, void *data) else stid = -1; } else { - stid = bitmap_find_free_region(t->stid_bmap, t->nstids, 2); + stid = bitmap_find_free_region(t->stid_bmap, t->nstids, 1); if (stid < 0) stid = -1; } @@ -1520,7 +1520,7 @@ int cxgb4_alloc_stid(struct tid_info *t, int family, void *data) if (family == PF_INET) t->stids_in_use++; else - t->stids_in_use += 4; + t->stids_in_use += 2; } spin_unlock_bh(&t->stid_lock); return stid; @@ -1571,13 +1571,13 @@ void cxgb4_free_stid(struct tid_info *t, unsigned int stid, int family) if (family == PF_INET) __clear_bit(stid, t->stid_bmap); else - bitmap_release_region(t->stid_bmap, stid, 2); + bitmap_release_region(t->stid_bmap, stid, 1); t->stid_tab[stid].data = NULL; if (stid < t->nstids) { if (family == PF_INET) t->stids_in_use--; else - t->stids_in_use -= 4; + t->stids_in_use -= 2; } else { t->sftids_in_use--; } -- cgit v1.2.3-59-g8ed1b From d7d3e25f40e950bdcec6d94faf9346b7a7d6e4bb Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Thu, 24 Dec 2015 16:24:53 +0530 Subject: cxgb4: Remove deprecated module parameters Remove deprecated module parameters, and mark one parameter as deprecated. Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 54 +------------------------ 1 file changed, 2 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index bfe50aafb312..b8a5fb0c32d4 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -162,19 +162,8 @@ MODULE_FIRMWARE(FW6_FNAME); static uint force_init; module_param(force_init, uint, 0644); -MODULE_PARM_DESC(force_init, "Forcibly become Master PF and initialize adapter"); - -/* - * Normally if the firmware we connect to has Configuration File support, we - * use that and only fall back to the old Driver-based initialization if the - * Configuration File fails for some reason. If force_old_init is set, then - * we'll always use the old Driver-based initialization sequence. - */ -static uint force_old_init; - -module_param(force_old_init, uint, 0644); -MODULE_PARM_DESC(force_old_init, "Force old initialization sequence, deprecated" - " parameter"); +MODULE_PARM_DESC(force_init, "Forcibly become Master PF and initialize adapter," + "deprecated parameter"); static int dflt_msg_enable = DFLT_MSG_ENABLE; @@ -195,23 +184,6 @@ static int msi = 2; module_param(msi, int, 0644); MODULE_PARM_DESC(msi, "whether to use INTx (0), MSI (1) or MSI-X (2)"); -/* - * Queue interrupt hold-off timer values. Queues default to the first of these - * upon creation. - */ -static unsigned int intr_holdoff[SGE_NTIMERS - 1] = { 5, 10, 20, 50, 100 }; - -module_param_array(intr_holdoff, uint, NULL, 0644); -MODULE_PARM_DESC(intr_holdoff, "values for queue interrupt hold-off timers " - "0..4 in microseconds, deprecated parameter"); - -static unsigned int intr_cnt[SGE_NCOUNTERS - 1] = { 4, 8, 16 }; - -module_param_array(intr_cnt, uint, NULL, 0644); -MODULE_PARM_DESC(intr_cnt, - "thresholds 1..3 for queue interrupt packet counters, " - "deprecated parameter"); - /* * Normally we tell the chip to deliver Ingress Packets into our DMA buffers * offset by 2 bytes in order to have the IP headers line up on 4-byte @@ -226,13 +198,7 @@ MODULE_PARM_DESC(intr_cnt, */ static int rx_dma_offset = 2; -static bool vf_acls; - #ifdef CONFIG_PCI_IOV -module_param(vf_acls, bool, 0644); -MODULE_PARM_DESC(vf_acls, "if set enable virtualization L2 ACL enforcement, " - "deprecated parameter"); - /* Configure the number of PCI-E Virtual Function which are to be instantiated * on SR-IOV Capable Physical Functions. */ @@ -253,12 +219,6 @@ module_param(select_queue, int, 0644); MODULE_PARM_DESC(select_queue, "Select between kernel provided method of selecting or driver method of selecting TX queue. Default is kernel method."); -static unsigned int tp_vlan_pri_map = HW_TPL_FR_MT_PR_IV_P_FC; - -module_param(tp_vlan_pri_map, uint, 0644); -MODULE_PARM_DESC(tp_vlan_pri_map, "global compressed filter configuration, " - "deprecated parameter"); - static struct dentry *cxgb4_debugfs_root; static LIST_HEAD(adapter_list); @@ -3141,16 +3101,6 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c) if (ret < 0) return ret; - /* select capabilities we'll be using */ - if (c->niccaps & htons(FW_CAPS_CONFIG_NIC_VM)) { - if (!vf_acls) - c->niccaps ^= htons(FW_CAPS_CONFIG_NIC_VM); - else - c->niccaps = htons(FW_CAPS_CONFIG_NIC_VM); - } else if (vf_acls) { - dev_err(adap->pdev_dev, "virtualization ACLs not supported"); - return ret; - } c->op_to_write = htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) | FW_CMD_REQUEST_F | FW_CMD_WRITE_F); ret = t4_wr_mbox(adap, adap->mbox, c, sizeof(*c), NULL); -- cgit v1.2.3-59-g8ed1b From 039f50629b7f860f36644ed1f34b27da9aa62f43 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Thu, 24 Dec 2015 14:34:54 -0800 Subject: ip_tunnel: Move stats update to iptunnel_xmit() By moving stats update into iptunnel_xmit(), we can simplify iptunnel_xmit() usage. With this change there is no need to call another function (iptunnel_xmit_stats()) to update stats in tunnel xmit code path. Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller --- drivers/net/geneve.c | 17 ++++++++--------- drivers/net/vxlan.c | 9 ++++----- include/net/ip6_tunnel.h | 17 ++++------------- include/net/ip_tunnels.h | 28 +++++++++++++++------------- include/net/udp_tunnel.h | 8 ++++---- net/ipv4/ip_gre.c | 7 +++---- net/ipv4/ip_tunnel.c | 7 ++----- net/ipv4/ip_tunnel_core.c | 9 +++++---- net/ipv4/ip_vti.c | 2 +- net/ipv4/udp_tunnel.c | 11 +++++------ net/ipv6/sit.c | 7 ++----- net/tipc/udp_media.c | 12 +++--------- 12 files changed, 56 insertions(+), 78 deletions(-) (limited to 'drivers') diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index e6e00924f8ef..20dd66423ec8 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -918,12 +918,11 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev, ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); df = 0; } - err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr, - tos, ttl, df, sport, geneve->dst_port, - !net_eq(geneve->net, dev_net(geneve->dev)), - !(flags & GENEVE_F_UDP_CSUM)); + udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr, + tos, ttl, df, sport, geneve->dst_port, + !net_eq(geneve->net, dev_net(geneve->dev)), + !(flags & GENEVE_F_UDP_CSUM)); - iptunnel_xmit_stats(err, &dev->stats, dev->tstats); return NETDEV_TX_OK; tx_error: @@ -1005,10 +1004,10 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev, ttl = 1; ttl = ttl ? : ip6_dst_hoplimit(dst); } - err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev, - &fl6.saddr, &fl6.daddr, prio, ttl, - sport, geneve->dst_port, - !!(flags & GENEVE_F_UDP_ZERO_CSUM6_TX)); + udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev, + &fl6.saddr, &fl6.daddr, prio, ttl, + sport, geneve->dst_port, + !!(flags & GENEVE_F_UDP_ZERO_CSUM6_TX)); return NETDEV_TX_OK; tx_error: diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index ba363cedef80..fecf7b6c732e 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1841,9 +1841,10 @@ static int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *sk skb_set_inner_protocol(skb, htons(ETH_P_TEB)); - return udp_tunnel_xmit_skb(rt, sk, skb, src, dst, tos, - ttl, df, src_port, dst_port, xnet, - !(vxflags & VXLAN_F_UDP_CSUM)); + udp_tunnel_xmit_skb(rt, sk, skb, src, dst, tos, ttl, df, + src_port, dst_port, xnet, + !(vxflags & VXLAN_F_UDP_CSUM)); + return 0; } #if IS_ENABLED(CONFIG_IPV6) @@ -2056,8 +2057,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, skb = NULL; goto rt_tx_error; } - - iptunnel_xmit_stats(err, &dev->stats, dev->tstats); #if IS_ENABLED(CONFIG_IPV6) } else { struct dst_entry *ndst; diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index ff788b665277..ae07e94778d8 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -5,6 +5,7 @@ #include #include #include +#include #define IP6TUNNEL_ERR_TIMEO (30*HZ) @@ -83,22 +84,12 @@ int ip6_tnl_get_iflink(const struct net_device *dev); static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb, struct net_device *dev) { - struct net_device_stats *stats = &dev->stats; int pkt_len, err; pkt_len = skb->len - skb_inner_network_offset(skb); err = ip6_local_out(dev_net(skb_dst(skb)->dev), sk, skb); - - if (net_xmit_eval(err) == 0) { - struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats); - u64_stats_update_begin(&tstats->syncp); - tstats->tx_bytes += pkt_len; - tstats->tx_packets++; - u64_stats_update_end(&tstats->syncp); - put_cpu_ptr(tstats); - } else { - stats->tx_errors++; - stats->tx_aborted_errors++; - } + if (unlikely(net_xmit_eval(err))) + pkt_len = -1; + iptunnel_xmit_stats(dev, pkt_len); } #endif diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 62a750a6a8f8..6db96ea0144f 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -273,32 +273,34 @@ static inline u8 ip_tunnel_ecn_encap(u8 tos, const struct iphdr *iph, } int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto); -int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, - __be32 src, __be32 dst, u8 proto, - u8 tos, u8 ttl, __be16 df, bool xnet); +void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, + __be32 src, __be32 dst, u8 proto, + u8 tos, u8 ttl, __be16 df, bool xnet); struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md, gfp_t flags); struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, bool gre_csum, int gso_type_mask); -static inline void iptunnel_xmit_stats(int err, - struct net_device_stats *err_stats, - struct pcpu_sw_netstats __percpu *stats) +static inline void iptunnel_xmit_stats(struct net_device *dev, int pkt_len) { - if (err > 0) { - struct pcpu_sw_netstats *tstats = get_cpu_ptr(stats); + if (pkt_len > 0) { + struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats); u64_stats_update_begin(&tstats->syncp); - tstats->tx_bytes += err; + tstats->tx_bytes += pkt_len; tstats->tx_packets++; u64_stats_update_end(&tstats->syncp); put_cpu_ptr(tstats); - } else if (err < 0) { - err_stats->tx_errors++; - err_stats->tx_aborted_errors++; } else { - err_stats->tx_dropped++; + struct net_device_stats *err_stats = &dev->stats; + + if (pkt_len < 0) { + err_stats->tx_errors++; + err_stats->tx_aborted_errors++; + } else { + err_stats->tx_dropped++; + } } } diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h index cb2f89f20f5c..cca2ad3082c3 100644 --- a/include/net/udp_tunnel.h +++ b/include/net/udp_tunnel.h @@ -78,10 +78,10 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock, struct udp_tunnel_sock_cfg *sock_cfg); /* Transmit the skb using UDP encapsulation. */ -int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, - __be32 src, __be32 dst, __u8 tos, __u8 ttl, - __be16 df, __be16 src_port, __be16 dst_port, - bool xnet, bool nocheck); +void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, + __be32 src, __be32 dst, __u8 tos, __u8 ttl, + __be16 df, __be16 src_port, __be16 dst_port, + bool xnet, bool nocheck); #if IS_ENABLED(CONFIG_IPV6) int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk, diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 04a48c0159cc..7c51c4e1661f 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -561,10 +561,9 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev) tunnel_id_to_key(tun_info->key.tun_id), 0); df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; - err = iptunnel_xmit(skb->sk, rt, skb, fl.saddr, - key->u.ipv4.dst, IPPROTO_GRE, - key->tos, key->ttl, df, false); - iptunnel_xmit_stats(err, &dev->stats, dev->tstats); + + iptunnel_xmit(skb->sk, rt, skb, fl.saddr, key->u.ipv4.dst, IPPROTO_GRE, + key->tos, key->ttl, df, false); return; err_free_rt: diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 0f6e9ee031c4..c7bd72e9b544 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -656,7 +656,6 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, struct rtable *rt; /* Route to the other host */ unsigned int max_headroom; /* The extra header space needed */ __be32 dst; - int err; bool connected; inner_iph = (const struct iphdr *)skb_inner_network_header(skb); @@ -794,10 +793,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, return; } - err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol, - tos, ttl, df, !net_eq(tunnel->net, dev_net(dev))); - iptunnel_xmit_stats(err, &dev->stats, dev->tstats); - + iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol, tos, ttl, + df, !net_eq(tunnel->net, dev_net(dev))); return; #if IS_ENABLED(CONFIG_IPV6) diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index 1db8418aa62e..eb52ce950c27 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -47,12 +47,13 @@ #include #include -int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, - __be32 src, __be32 dst, __u8 proto, - __u8 tos, __u8 ttl, __be16 df, bool xnet) +void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, + __be32 src, __be32 dst, __u8 proto, + __u8 tos, __u8 ttl, __be16 df, bool xnet) { int pkt_len = skb->len - skb_inner_network_offset(skb); struct net *net = dev_net(rt->dst.dev); + struct net_device *dev = skb->dev; struct iphdr *iph; int err; @@ -81,7 +82,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb, err = ip_local_out(net, sk, skb); if (unlikely(net_xmit_eval(err))) pkt_len = 0; - return pkt_len; + iptunnel_xmit_stats(dev, pkt_len); } EXPORT_SYMBOL_GPL(iptunnel_xmit); diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 02d9c21e2953..5cf10b777b7e 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -199,7 +199,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev, err = dst_output(tunnel->net, skb->sk, skb); if (net_xmit_eval(err) == 0) err = skb->len; - iptunnel_xmit_stats(err, &dev->stats, dev->tstats); + iptunnel_xmit_stats(dev, err); return NETDEV_TX_OK; tx_error_icmp: diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c index aba428626b52..0ec08814f37d 100644 --- a/net/ipv4/udp_tunnel.c +++ b/net/ipv4/udp_tunnel.c @@ -74,10 +74,10 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock, } EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock); -int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, - __be32 src, __be32 dst, __u8 tos, __u8 ttl, - __be16 df, __be16 src_port, __be16 dst_port, - bool xnet, bool nocheck) +void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, + __be32 src, __be32 dst, __u8 tos, __u8 ttl, + __be16 df, __be16 src_port, __be16 dst_port, + bool xnet, bool nocheck) { struct udphdr *uh; @@ -91,8 +91,7 @@ int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, udp_set_csum(nocheck, skb, src, dst, skb->len); - return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP, - tos, ttl, df, xnet); + iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP, tos, ttl, df, xnet); } EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb); diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index dcccae86190f..e794ef66a401 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -820,7 +820,6 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, const struct in6_addr *addr6; int addr_type; u8 ttl; - int err; u8 protocol = IPPROTO_IPV6; int t_hlen = tunnel->hlen + sizeof(struct iphdr); @@ -983,10 +982,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, skb_set_inner_ipproto(skb, IPPROTO_IPV6); - err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, - protocol, tos, ttl, df, - !net_eq(tunnel->net, dev_net(dev))); - iptunnel_xmit_stats(err, &dev->stats, dev->tstats); + iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol, tos, ttl, + df, !net_eq(tunnel->net, dev_net(dev))); return NETDEV_TX_OK; tx_error_icmp: diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 6af78c6276b4..d63a911e7fe2 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -182,15 +182,9 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb, goto tx_error; } ttl = ip4_dst_hoplimit(&rt->dst); - err = udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, - src->ipv4.s_addr, - dst->ipv4.s_addr, 0, ttl, 0, - src->udp_port, dst->udp_port, - false, true); - if (err < 0) { - ip_rt_put(rt); - goto tx_error; - } + udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, src->ipv4.s_addr, + dst->ipv4.s_addr, 0, ttl, 0, src->udp_port, + dst->udp_port, false, true); #if IS_ENABLED(CONFIG_IPV6) } else { struct dst_entry *ndst; -- cgit v1.2.3-59-g8ed1b From 5adae51a64b8b72430fe2682c9656661551f4641 Mon Sep 17 00:00:00 2001 From: Igal Liberman Date: Mon, 21 Dec 2015 02:21:25 +0200 Subject: fsl/fman: Add FMan MURAM support Add Frame Manager Multi-User RAM support. This internal FMan memory block is used by the FMan hardware modules, the management being made through the generic allocator. The FMan Internal memory, for example, is used for allocating transmit and receive FIFOs. Signed-off-by: Igal Liberman Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/Kconfig | 1 + drivers/net/ethernet/freescale/Makefile | 2 + drivers/net/ethernet/freescale/fman/Kconfig | 8 ++ drivers/net/ethernet/freescale/fman/Makefile | 5 + drivers/net/ethernet/freescale/fman/fman_muram.c | 158 +++++++++++++++++++++++ drivers/net/ethernet/freescale/fman/fman_muram.h | 51 ++++++++ 6 files changed, 225 insertions(+) create mode 100644 drivers/net/ethernet/freescale/fman/Kconfig create mode 100644 drivers/net/ethernet/freescale/fman/Makefile create mode 100644 drivers/net/ethernet/freescale/fman/fman_muram.c create mode 100644 drivers/net/ethernet/freescale/fman/fman_muram.h (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index bee32a9d9876..d1ca45fbb164 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -54,6 +54,7 @@ config FEC_MPC52xx_MDIO If compiled as module, it will be called fec_mpc52xx_phy. source "drivers/net/ethernet/freescale/fs_enet/Kconfig" +source "drivers/net/ethernet/freescale/fman/Kconfig" config FSL_PQ_MDIO tristate "Freescale PQ MDIO" diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile index 71debd1c18c9..4097c58d17a7 100644 --- a/drivers/net/ethernet/freescale/Makefile +++ b/drivers/net/ethernet/freescale/Makefile @@ -17,3 +17,5 @@ gianfar_driver-objs := gianfar.o \ gianfar_ethtool.o obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o ucc_geth_driver-objs := ucc_geth.o ucc_geth_ethtool.o + +obj-$(CONFIG_FSL_FMAN) += fman/ diff --git a/drivers/net/ethernet/freescale/fman/Kconfig b/drivers/net/ethernet/freescale/fman/Kconfig new file mode 100644 index 000000000000..66b729692b48 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/Kconfig @@ -0,0 +1,8 @@ +config FSL_FMAN + bool "FMan support" + depends on FSL_SOC || COMPILE_TEST + select GENERIC_ALLOCATOR + default n + help + Freescale Data-Path Acceleration Architecture Frame Manager + (FMan) support diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile new file mode 100644 index 000000000000..fc2e194b8464 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -0,0 +1,5 @@ +subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman + +obj-y += fsl_fman.o + +fsl_fman-objs := fman_muram.o diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.c b/drivers/net/ethernet/freescale/fman/fman_muram.c new file mode 100644 index 000000000000..4eb0e9ac7182 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_muram.c @@ -0,0 +1,158 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fman_muram.h" + +#include +#include +#include + +struct muram_info { + struct gen_pool *pool; + void __iomem *vbase; + size_t size; + phys_addr_t pbase; +}; + +static unsigned long fman_muram_vbase_to_offset(struct muram_info *muram, + unsigned long vaddr) +{ + return vaddr - (unsigned long)muram->vbase; +} + +/** + * fman_muram_init + * @base: Pointer to base of memory mapped FM-MURAM. + * @size: Size of the FM-MURAM partition. + * + * Creates partition in the MURAM. + * The routine returns a pointer to the MURAM partition. + * This pointer must be passed as to all other FM-MURAM function calls. + * No actual initialization or configuration of FM_MURAM hardware is done by + * this routine. + * + * Return: pointer to FM-MURAM object, or NULL for Failure. + */ +struct muram_info *fman_muram_init(phys_addr_t base, size_t size) +{ + struct muram_info *muram; + void __iomem *vaddr; + int ret; + + muram = kzalloc(sizeof(*muram), GFP_KERNEL); + if (!muram) + return NULL; + + muram->pool = gen_pool_create(ilog2(64), -1); + if (!muram->pool) { + pr_err("%s(): MURAM pool create failed\n", __func__); + goto muram_free; + } + + vaddr = ioremap(base, size); + if (!vaddr) { + pr_err("%s(): MURAM ioremap failed\n", __func__); + goto pool_destroy; + } + + ret = gen_pool_add_virt(muram->pool, (unsigned long)vaddr, + base, size, -1); + if (ret < 0) { + pr_err("%s(): MURAM pool add failed\n", __func__); + iounmap(vaddr); + goto pool_destroy; + } + + memset_io(vaddr, 0, (int)size); + + muram->vbase = vaddr; + muram->pbase = base; + return muram; + +pool_destroy: + gen_pool_destroy(muram->pool); +muram_free: + kfree(muram); + return NULL; +} + +/** + * fman_muram_offset_to_vbase + * @muram: FM-MURAM module pointer. + * @offset: the offset of the memory block + * + * Gives the address of the memory region from specific offset + * + * Return: The address of the memory block + */ +unsigned long fman_muram_offset_to_vbase(struct muram_info *muram, + unsigned long offset) +{ + return offset + (unsigned long)muram->vbase; +} + +/** + * fman_muram_alloc + * @muram: FM-MURAM module pointer. + * @size: Size of the memory to be allocated. + * + * Allocate some memory from FM-MURAM partition. + * + * Return: address of the allocated memory; NULL otherwise. + */ +int fman_muram_alloc(struct muram_info *muram, size_t size) +{ + unsigned long vaddr; + + vaddr = gen_pool_alloc(muram->pool, size); + if (!vaddr) + return -ENOMEM; + + memset_io((void __iomem *)vaddr, 0, size); + + return fman_muram_vbase_to_offset(muram, vaddr); +} + +/** + * fman_muram_free_mem + * muram: FM-MURAM module pointer. + * offset: offset of the memory region to be freed. + * size: size of the memory to be freed. + * + * Free an allocated memory from FM-MURAM partition. + */ +void fman_muram_free_mem(struct muram_info *muram, u32 offset, size_t size) +{ + unsigned long addr = fman_muram_offset_to_vbase(muram, offset); + + gen_pool_free(muram->pool, addr, size); +} diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.h b/drivers/net/ethernet/freescale/fman/fman_muram.h new file mode 100644 index 000000000000..dbf0af9e5bb5 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_muram.h @@ -0,0 +1,51 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __FM_MURAM_EXT +#define __FM_MURAM_EXT + +#include + +#define FM_MURAM_INVALID_ALLOCATION -1 + +/* Structure for FM MURAM information */ +struct muram_info; + +struct muram_info *fman_muram_init(phys_addr_t base, size_t size); + +unsigned long fman_muram_offset_to_vbase(struct muram_info *muram, + unsigned long offset); + +int fman_muram_alloc(struct muram_info *muram, size_t size); + +void fman_muram_free_mem(struct muram_info *muram, u32 offset, size_t size); + +#endif /* __FM_MURAM_EXT */ -- cgit v1.2.3-59-g8ed1b From 414fd46e77626f4e1f89ab4be6b876ef91e89dfa Mon Sep 17 00:00:00 2001 From: Igal Liberman Date: Mon, 21 Dec 2015 02:21:26 +0200 Subject: fsl/fman: Add FMan support Add the Data Path Acceleration Architecture Frame Manger Driver. The FMan embeds a series of hardware blocks that implement a group of Ethernet interfaces. This patch adds The FMan configuration, initialization and runtime control routines. The FMan driver supports several hardware versions differentiated by things like: - Different type of MACs - Number of MAC and ports - Available resources - Different hardware errata Signed-off-by: Igal Liberman Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/Makefile | 2 +- drivers/net/ethernet/freescale/fman/fman.c | 2871 ++++++++++++++++++++++++++ drivers/net/ethernet/freescale/fman/fman.h | 325 +++ 3 files changed, 3197 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/freescale/fman/fman.c create mode 100644 drivers/net/ethernet/freescale/fman/fman.h (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile index fc2e194b8464..fb5a7f0e14ed 100644 --- a/drivers/net/ethernet/freescale/fman/Makefile +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -2,4 +2,4 @@ subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman obj-y += fsl_fman.o -fsl_fman-objs := fman_muram.o +fsl_fman-objs := fman_muram.o fman.o diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c new file mode 100644 index 000000000000..623aa1c8ebc6 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -0,0 +1,2871 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fman.h" +#include "fman_muram.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* General defines */ +#define FMAN_LIODN_TBL 64 /* size of LIODN table */ +#define MAX_NUM_OF_MACS 10 +#define FM_NUM_OF_FMAN_CTRL_EVENT_REGS 4 +#define BASE_RX_PORTID 0x08 +#define BASE_TX_PORTID 0x28 + +/* Modules registers offsets */ +#define BMI_OFFSET 0x00080000 +#define QMI_OFFSET 0x00080400 +#define DMA_OFFSET 0x000C2000 +#define FPM_OFFSET 0x000C3000 +#define IMEM_OFFSET 0x000C4000 +#define CGP_OFFSET 0x000DB000 + +/* Exceptions bit map */ +#define EX_DMA_BUS_ERROR 0x80000000 +#define EX_DMA_READ_ECC 0x40000000 +#define EX_DMA_SYSTEM_WRITE_ECC 0x20000000 +#define EX_DMA_FM_WRITE_ECC 0x10000000 +#define EX_FPM_STALL_ON_TASKS 0x08000000 +#define EX_FPM_SINGLE_ECC 0x04000000 +#define EX_FPM_DOUBLE_ECC 0x02000000 +#define EX_QMI_SINGLE_ECC 0x01000000 +#define EX_QMI_DEQ_FROM_UNKNOWN_PORTID 0x00800000 +#define EX_QMI_DOUBLE_ECC 0x00400000 +#define EX_BMI_LIST_RAM_ECC 0x00200000 +#define EX_BMI_STORAGE_PROFILE_ECC 0x00100000 +#define EX_BMI_STATISTICS_RAM_ECC 0x00080000 +#define EX_IRAM_ECC 0x00040000 +#define EX_MURAM_ECC 0x00020000 +#define EX_BMI_DISPATCH_RAM_ECC 0x00010000 +#define EX_DMA_SINGLE_PORT_ECC 0x00008000 + +/* DMA defines */ +/* masks */ +#define DMA_MODE_BER 0x00200000 +#define DMA_MODE_ECC 0x00000020 +#define DMA_MODE_SECURE_PROT 0x00000800 +#define DMA_MODE_AXI_DBG_MASK 0x0F000000 + +#define DMA_TRANSFER_PORTID_MASK 0xFF000000 +#define DMA_TRANSFER_TNUM_MASK 0x00FF0000 +#define DMA_TRANSFER_LIODN_MASK 0x00000FFF + +#define DMA_STATUS_BUS_ERR 0x08000000 +#define DMA_STATUS_READ_ECC 0x04000000 +#define DMA_STATUS_SYSTEM_WRITE_ECC 0x02000000 +#define DMA_STATUS_FM_WRITE_ECC 0x01000000 +#define DMA_STATUS_FM_SPDAT_ECC 0x00080000 + +#define DMA_MODE_CACHE_OR_SHIFT 30 +#define DMA_MODE_AXI_DBG_SHIFT 24 +#define DMA_MODE_CEN_SHIFT 13 +#define DMA_MODE_CEN_MASK 0x00000007 +#define DMA_MODE_DBG_SHIFT 7 +#define DMA_MODE_AID_MODE_SHIFT 4 + +#define DMA_THRESH_COMMQ_SHIFT 24 +#define DMA_THRESH_READ_INT_BUF_SHIFT 16 +#define DMA_THRESH_READ_INT_BUF_MASK 0x0000003f +#define DMA_THRESH_WRITE_INT_BUF_MASK 0x0000003f + +#define DMA_TRANSFER_PORTID_SHIFT 24 +#define DMA_TRANSFER_TNUM_SHIFT 16 + +#define DMA_CAM_SIZEOF_ENTRY 0x40 +#define DMA_CAM_UNITS 8 + +#define DMA_LIODN_SHIFT 16 +#define DMA_LIODN_BASE_MASK 0x00000FFF + +/* FPM defines */ +#define FPM_EV_MASK_DOUBLE_ECC 0x80000000 +#define FPM_EV_MASK_STALL 0x40000000 +#define FPM_EV_MASK_SINGLE_ECC 0x20000000 +#define FPM_EV_MASK_RELEASE_FM 0x00010000 +#define FPM_EV_MASK_DOUBLE_ECC_EN 0x00008000 +#define FPM_EV_MASK_STALL_EN 0x00004000 +#define FPM_EV_MASK_SINGLE_ECC_EN 0x00002000 +#define FPM_EV_MASK_EXTERNAL_HALT 0x00000008 +#define FPM_EV_MASK_ECC_ERR_HALT 0x00000004 + +#define FPM_RAM_MURAM_ECC 0x00008000 +#define FPM_RAM_IRAM_ECC 0x00004000 +#define FPM_IRAM_ECC_ERR_EX_EN 0x00020000 +#define FPM_MURAM_ECC_ERR_EX_EN 0x00040000 +#define FPM_RAM_IRAM_ECC_EN 0x40000000 +#define FPM_RAM_RAMS_ECC_EN 0x80000000 +#define FPM_RAM_RAMS_ECC_EN_SRC_SEL 0x08000000 + +#define FPM_REV1_MAJOR_MASK 0x0000FF00 +#define FPM_REV1_MINOR_MASK 0x000000FF + +#define FPM_DISP_LIMIT_SHIFT 24 + +#define FPM_PRT_FM_CTL1 0x00000001 +#define FPM_PRT_FM_CTL2 0x00000002 +#define FPM_PORT_FM_CTL_PORTID_SHIFT 24 +#define FPM_PRC_ORA_FM_CTL_SEL_SHIFT 16 + +#define FPM_THR1_PRS_SHIFT 24 +#define FPM_THR1_KG_SHIFT 16 +#define FPM_THR1_PLCR_SHIFT 8 +#define FPM_THR1_BMI_SHIFT 0 + +#define FPM_THR2_QMI_ENQ_SHIFT 24 +#define FPM_THR2_QMI_DEQ_SHIFT 0 +#define FPM_THR2_FM_CTL1_SHIFT 16 +#define FPM_THR2_FM_CTL2_SHIFT 8 + +#define FPM_EV_MASK_CAT_ERR_SHIFT 1 +#define FPM_EV_MASK_DMA_ERR_SHIFT 0 + +#define FPM_REV1_MAJOR_SHIFT 8 + +#define FPM_RSTC_FM_RESET 0x80000000 +#define FPM_RSTC_MAC0_RESET 0x40000000 +#define FPM_RSTC_MAC1_RESET 0x20000000 +#define FPM_RSTC_MAC2_RESET 0x10000000 +#define FPM_RSTC_MAC3_RESET 0x08000000 +#define FPM_RSTC_MAC8_RESET 0x04000000 +#define FPM_RSTC_MAC4_RESET 0x02000000 +#define FPM_RSTC_MAC5_RESET 0x01000000 +#define FPM_RSTC_MAC6_RESET 0x00800000 +#define FPM_RSTC_MAC7_RESET 0x00400000 +#define FPM_RSTC_MAC9_RESET 0x00200000 + +#define FPM_TS_INT_SHIFT 16 +#define FPM_TS_CTL_EN 0x80000000 + +/* BMI defines */ +#define BMI_INIT_START 0x80000000 +#define BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC 0x80000000 +#define BMI_ERR_INTR_EN_LIST_RAM_ECC 0x40000000 +#define BMI_ERR_INTR_EN_STATISTICS_RAM_ECC 0x20000000 +#define BMI_ERR_INTR_EN_DISPATCH_RAM_ECC 0x10000000 +#define BMI_NUM_OF_TASKS_MASK 0x3F000000 +#define BMI_NUM_OF_EXTRA_TASKS_MASK 0x000F0000 +#define BMI_NUM_OF_DMAS_MASK 0x00000F00 +#define BMI_NUM_OF_EXTRA_DMAS_MASK 0x0000000F +#define BMI_FIFO_SIZE_MASK 0x000003FF +#define BMI_EXTRA_FIFO_SIZE_MASK 0x03FF0000 +#define BMI_CFG2_DMAS_MASK 0x0000003F +#define BMI_CFG2_TASKS_MASK 0x0000003F + +#define BMI_CFG2_TASKS_SHIFT 16 +#define BMI_CFG2_DMAS_SHIFT 0 +#define BMI_CFG1_FIFO_SIZE_SHIFT 16 +#define BMI_NUM_OF_TASKS_SHIFT 24 +#define BMI_EXTRA_NUM_OF_TASKS_SHIFT 16 +#define BMI_NUM_OF_DMAS_SHIFT 8 +#define BMI_EXTRA_NUM_OF_DMAS_SHIFT 0 + +#define BMI_FIFO_ALIGN 0x100 + +#define BMI_EXTRA_FIFO_SIZE_SHIFT 16 + +/* QMI defines */ +#define QMI_CFG_ENQ_EN 0x80000000 +#define QMI_CFG_DEQ_EN 0x40000000 +#define QMI_CFG_EN_COUNTERS 0x10000000 +#define QMI_CFG_DEQ_MASK 0x0000003F +#define QMI_CFG_ENQ_MASK 0x00003F00 +#define QMI_CFG_ENQ_SHIFT 8 + +#define QMI_ERR_INTR_EN_DOUBLE_ECC 0x80000000 +#define QMI_ERR_INTR_EN_DEQ_FROM_DEF 0x40000000 +#define QMI_INTR_EN_SINGLE_ECC 0x80000000 + +#define QMI_GS_HALT_NOT_BUSY 0x00000002 + +/* IRAM defines */ +#define IRAM_IADD_AIE 0x80000000 +#define IRAM_READY 0x80000000 + +/* Default values */ +#define DEFAULT_CATASTROPHIC_ERR 0 +#define DEFAULT_DMA_ERR 0 +#define DEFAULT_AID_MODE FMAN_DMA_AID_OUT_TNUM +#define DEFAULT_DMA_COMM_Q_LOW 0x2A +#define DEFAULT_DMA_COMM_Q_HIGH 0x3F +#define DEFAULT_CACHE_OVERRIDE 0 +#define DEFAULT_DMA_CAM_NUM_OF_ENTRIES 64 +#define DEFAULT_DMA_DBG_CNT_MODE 0 +#define DEFAULT_DMA_SOS_EMERGENCY 0 +#define DEFAULT_DMA_WATCHDOG 0 +#define DEFAULT_DISP_LIMIT 0 +#define DEFAULT_PRS_DISP_TH 16 +#define DEFAULT_PLCR_DISP_TH 16 +#define DEFAULT_KG_DISP_TH 16 +#define DEFAULT_BMI_DISP_TH 16 +#define DEFAULT_QMI_ENQ_DISP_TH 16 +#define DEFAULT_QMI_DEQ_DISP_TH 16 +#define DEFAULT_FM_CTL1_DISP_TH 16 +#define DEFAULT_FM_CTL2_DISP_TH 16 + +#define DFLT_AXI_DBG_NUM_OF_BEATS 1 + +#define DFLT_DMA_READ_INT_BUF_LOW(dma_thresh_max_buf) \ + ((dma_thresh_max_buf + 1) / 2) +#define DFLT_DMA_READ_INT_BUF_HIGH(dma_thresh_max_buf) \ + ((dma_thresh_max_buf + 1) * 3 / 4) +#define DFLT_DMA_WRITE_INT_BUF_LOW(dma_thresh_max_buf) \ + ((dma_thresh_max_buf + 1) / 2) +#define DFLT_DMA_WRITE_INT_BUF_HIGH(dma_thresh_max_buf)\ + ((dma_thresh_max_buf + 1) * 3 / 4) + +#define DMA_COMM_Q_LOW_FMAN_V3 0x2A +#define DMA_COMM_Q_LOW_FMAN_V2(dma_thresh_max_commq) \ + ((dma_thresh_max_commq + 1) / 2) +#define DFLT_DMA_COMM_Q_LOW(major, dma_thresh_max_commq) \ + ((major == 6) ? DMA_COMM_Q_LOW_FMAN_V3 : \ + DMA_COMM_Q_LOW_FMAN_V2(dma_thresh_max_commq)) + +#define DMA_COMM_Q_HIGH_FMAN_V3 0x3f +#define DMA_COMM_Q_HIGH_FMAN_V2(dma_thresh_max_commq) \ + ((dma_thresh_max_commq + 1) * 3 / 4) +#define DFLT_DMA_COMM_Q_HIGH(major, dma_thresh_max_commq) \ + ((major == 6) ? DMA_COMM_Q_HIGH_FMAN_V3 : \ + DMA_COMM_Q_HIGH_FMAN_V2(dma_thresh_max_commq)) + +#define TOTAL_NUM_OF_TASKS_FMAN_V3L 59 +#define TOTAL_NUM_OF_TASKS_FMAN_V3H 124 +#define DFLT_TOTAL_NUM_OF_TASKS(major, minor, bmi_max_num_of_tasks) \ + ((major == 6) ? ((minor == 1 || minor == 4) ? \ + TOTAL_NUM_OF_TASKS_FMAN_V3L : TOTAL_NUM_OF_TASKS_FMAN_V3H) : \ + bmi_max_num_of_tasks) + +#define DMA_CAM_NUM_OF_ENTRIES_FMAN_V3 64 +#define DMA_CAM_NUM_OF_ENTRIES_FMAN_V2 32 +#define DFLT_DMA_CAM_NUM_OF_ENTRIES(major) \ + (major == 6 ? DMA_CAM_NUM_OF_ENTRIES_FMAN_V3 : \ + DMA_CAM_NUM_OF_ENTRIES_FMAN_V2) + +#define FM_TIMESTAMP_1_USEC_BIT 8 + +/* Defines used for enabling/disabling FMan interrupts */ +#define ERR_INTR_EN_DMA 0x00010000 +#define ERR_INTR_EN_FPM 0x80000000 +#define ERR_INTR_EN_BMI 0x00800000 +#define ERR_INTR_EN_QMI 0x00400000 +#define ERR_INTR_EN_MURAM 0x00040000 +#define ERR_INTR_EN_MAC0 0x00004000 +#define ERR_INTR_EN_MAC1 0x00002000 +#define ERR_INTR_EN_MAC2 0x00001000 +#define ERR_INTR_EN_MAC3 0x00000800 +#define ERR_INTR_EN_MAC4 0x00000400 +#define ERR_INTR_EN_MAC5 0x00000200 +#define ERR_INTR_EN_MAC6 0x00000100 +#define ERR_INTR_EN_MAC7 0x00000080 +#define ERR_INTR_EN_MAC8 0x00008000 +#define ERR_INTR_EN_MAC9 0x00000040 + +#define INTR_EN_QMI 0x40000000 +#define INTR_EN_MAC0 0x00080000 +#define INTR_EN_MAC1 0x00040000 +#define INTR_EN_MAC2 0x00020000 +#define INTR_EN_MAC3 0x00010000 +#define INTR_EN_MAC4 0x00000040 +#define INTR_EN_MAC5 0x00000020 +#define INTR_EN_MAC6 0x00000008 +#define INTR_EN_MAC7 0x00000002 +#define INTR_EN_MAC8 0x00200000 +#define INTR_EN_MAC9 0x00100000 +#define INTR_EN_REV0 0x00008000 +#define INTR_EN_REV1 0x00004000 +#define INTR_EN_REV2 0x00002000 +#define INTR_EN_REV3 0x00001000 +#define INTR_EN_TMR 0x01000000 + +enum fman_dma_aid_mode { + FMAN_DMA_AID_OUT_PORT_ID = 0, /* 4 LSB of PORT_ID */ + FMAN_DMA_AID_OUT_TNUM /* 4 LSB of TNUM */ +}; + +struct fman_iram_regs { + u32 iadd; /* FM IRAM instruction address register */ + u32 idata; /* FM IRAM instruction data register */ + u32 itcfg; /* FM IRAM timing config register */ + u32 iready; /* FM IRAM ready register */ +}; + +struct fman_fpm_regs { + u32 fmfp_tnc; /* FPM TNUM Control 0x00 */ + u32 fmfp_prc; /* FPM Port_ID FmCtl Association 0x04 */ + u32 fmfp_brkc; /* FPM Breakpoint Control 0x08 */ + u32 fmfp_mxd; /* FPM Flush Control 0x0c */ + u32 fmfp_dist1; /* FPM Dispatch Thresholds1 0x10 */ + u32 fmfp_dist2; /* FPM Dispatch Thresholds2 0x14 */ + u32 fm_epi; /* FM Error Pending Interrupts 0x18 */ + u32 fm_rie; /* FM Error Interrupt Enable 0x1c */ + u32 fmfp_fcev[4]; /* FPM FMan-Controller Event 1-4 0x20-0x2f */ + u32 res0030[4]; /* res 0x30 - 0x3f */ + u32 fmfp_cee[4]; /* PM FMan-Controller Event 1-4 0x40-0x4f */ + u32 res0050[4]; /* res 0x50-0x5f */ + u32 fmfp_tsc1; /* FPM TimeStamp Control1 0x60 */ + u32 fmfp_tsc2; /* FPM TimeStamp Control2 0x64 */ + u32 fmfp_tsp; /* FPM Time Stamp 0x68 */ + u32 fmfp_tsf; /* FPM Time Stamp Fraction 0x6c */ + u32 fm_rcr; /* FM Rams Control 0x70 */ + u32 fmfp_extc; /* FPM External Requests Control 0x74 */ + u32 fmfp_ext1; /* FPM External Requests Config1 0x78 */ + u32 fmfp_ext2; /* FPM External Requests Config2 0x7c */ + u32 fmfp_drd[16]; /* FPM Data_Ram Data 0-15 0x80 - 0xbf */ + u32 fmfp_dra; /* FPM Data Ram Access 0xc0 */ + u32 fm_ip_rev_1; /* FM IP Block Revision 1 0xc4 */ + u32 fm_ip_rev_2; /* FM IP Block Revision 2 0xc8 */ + u32 fm_rstc; /* FM Reset Command 0xcc */ + u32 fm_cld; /* FM Classifier Debug 0xd0 */ + u32 fm_npi; /* FM Normal Pending Interrupts 0xd4 */ + u32 fmfp_exte; /* FPM External Requests Enable 0xd8 */ + u32 fmfp_ee; /* FPM Event&Mask 0xdc */ + u32 fmfp_cev[4]; /* FPM CPU Event 1-4 0xe0-0xef */ + u32 res00f0[4]; /* res 0xf0-0xff */ + u32 fmfp_ps[50]; /* FPM Port Status 0x100-0x1c7 */ + u32 res01c8[14]; /* res 0x1c8-0x1ff */ + u32 fmfp_clfabc; /* FPM CLFABC 0x200 */ + u32 fmfp_clfcc; /* FPM CLFCC 0x204 */ + u32 fmfp_clfaval; /* FPM CLFAVAL 0x208 */ + u32 fmfp_clfbval; /* FPM CLFBVAL 0x20c */ + u32 fmfp_clfcval; /* FPM CLFCVAL 0x210 */ + u32 fmfp_clfamsk; /* FPM CLFAMSK 0x214 */ + u32 fmfp_clfbmsk; /* FPM CLFBMSK 0x218 */ + u32 fmfp_clfcmsk; /* FPM CLFCMSK 0x21c */ + u32 fmfp_clfamc; /* FPM CLFAMC 0x220 */ + u32 fmfp_clfbmc; /* FPM CLFBMC 0x224 */ + u32 fmfp_clfcmc; /* FPM CLFCMC 0x228 */ + u32 fmfp_decceh; /* FPM DECCEH 0x22c */ + u32 res0230[116]; /* res 0x230 - 0x3ff */ + u32 fmfp_ts[128]; /* 0x400: FPM Task Status 0x400 - 0x5ff */ + u32 res0600[0x400 - 384]; +}; + +struct fman_bmi_regs { + u32 fmbm_init; /* BMI Initialization 0x00 */ + u32 fmbm_cfg1; /* BMI Configuration 1 0x04 */ + u32 fmbm_cfg2; /* BMI Configuration 2 0x08 */ + u32 res000c[5]; /* 0x0c - 0x1f */ + u32 fmbm_ievr; /* Interrupt Event Register 0x20 */ + u32 fmbm_ier; /* Interrupt Enable Register 0x24 */ + u32 fmbm_ifr; /* Interrupt Force Register 0x28 */ + u32 res002c[5]; /* 0x2c - 0x3f */ + u32 fmbm_arb[8]; /* BMI Arbitration 0x40 - 0x5f */ + u32 res0060[12]; /* 0x60 - 0x8f */ + u32 fmbm_dtc[3]; /* Debug Trap Counter 0x90 - 0x9b */ + u32 res009c; /* 0x9c */ + u32 fmbm_dcv[3][4]; /* Debug Compare val 0xa0-0xcf */ + u32 fmbm_dcm[3][4]; /* Debug Compare Mask 0xd0-0xff */ + u32 fmbm_gde; /* BMI Global Debug Enable 0x100 */ + u32 fmbm_pp[63]; /* BMI Port Parameters 0x104 - 0x1ff */ + u32 res0200; /* 0x200 */ + u32 fmbm_pfs[63]; /* BMI Port FIFO Size 0x204 - 0x2ff */ + u32 res0300; /* 0x300 */ + u32 fmbm_spliodn[63]; /* Port Partition ID 0x304 - 0x3ff */ +}; + +struct fman_qmi_regs { + u32 fmqm_gc; /* General Configuration Register 0x00 */ + u32 res0004; /* 0x04 */ + u32 fmqm_eie; /* Error Interrupt Event Register 0x08 */ + u32 fmqm_eien; /* Error Interrupt Enable Register 0x0c */ + u32 fmqm_eif; /* Error Interrupt Force Register 0x10 */ + u32 fmqm_ie; /* Interrupt Event Register 0x14 */ + u32 fmqm_ien; /* Interrupt Enable Register 0x18 */ + u32 fmqm_if; /* Interrupt Force Register 0x1c */ + u32 fmqm_gs; /* Global Status Register 0x20 */ + u32 fmqm_ts; /* Task Status Register 0x24 */ + u32 fmqm_etfc; /* Enqueue Total Frame Counter 0x28 */ + u32 fmqm_dtfc; /* Dequeue Total Frame Counter 0x2c */ + u32 fmqm_dc0; /* Dequeue Counter 0 0x30 */ + u32 fmqm_dc1; /* Dequeue Counter 1 0x34 */ + u32 fmqm_dc2; /* Dequeue Counter 2 0x38 */ + u32 fmqm_dc3; /* Dequeue Counter 3 0x3c */ + u32 fmqm_dfdc; /* Dequeue FQID from Default Counter 0x40 */ + u32 fmqm_dfcc; /* Dequeue FQID from Context Counter 0x44 */ + u32 fmqm_dffc; /* Dequeue FQID from FD Counter 0x48 */ + u32 fmqm_dcc; /* Dequeue Confirm Counter 0x4c */ + u32 res0050[7]; /* 0x50 - 0x6b */ + u32 fmqm_tapc; /* Tnum Aging Period Control 0x6c */ + u32 fmqm_dmcvc; /* Dequeue MAC Command Valid Counter 0x70 */ + u32 fmqm_difdcc; /* Dequeue Invalid FD Command Counter 0x74 */ + u32 fmqm_da1v; /* Dequeue A1 Valid Counter 0x78 */ + u32 res007c; /* 0x7c */ + u32 fmqm_dtc; /* 0x80 Debug Trap Counter 0x80 */ + u32 fmqm_efddd; /* 0x84 Enqueue Frame desc Dynamic dbg 0x84 */ + u32 res0088[2]; /* 0x88 - 0x8f */ + struct { + u32 fmqm_dtcfg1; /* 0x90 dbg trap cfg 1 Register 0x00 */ + u32 fmqm_dtval1; /* Debug Trap Value 1 Register 0x04 */ + u32 fmqm_dtm1; /* Debug Trap Mask 1 Register 0x08 */ + u32 fmqm_dtc1; /* Debug Trap Counter 1 Register 0x0c */ + u32 fmqm_dtcfg2; /* dbg Trap cfg 2 Register 0x10 */ + u32 fmqm_dtval2; /* Debug Trap Value 2 Register 0x14 */ + u32 fmqm_dtm2; /* Debug Trap Mask 2 Register 0x18 */ + u32 res001c; /* 0x1c */ + } dbg_traps[3]; /* 0x90 - 0xef */ + u8 res00f0[0x400 - 0xf0]; /* 0xf0 - 0x3ff */ +}; + +struct fman_dma_regs { + u32 fmdmsr; /* FM DMA status register 0x00 */ + u32 fmdmmr; /* FM DMA mode register 0x04 */ + u32 fmdmtr; /* FM DMA bus threshold register 0x08 */ + u32 fmdmhy; /* FM DMA bus hysteresis register 0x0c */ + u32 fmdmsetr; /* FM DMA SOS emergency Threshold Register 0x10 */ + u32 fmdmtah; /* FM DMA transfer bus address high reg 0x14 */ + u32 fmdmtal; /* FM DMA transfer bus address low reg 0x18 */ + u32 fmdmtcid; /* FM DMA transfer bus communication ID reg 0x1c */ + u32 fmdmra; /* FM DMA bus internal ram address register 0x20 */ + u32 fmdmrd; /* FM DMA bus internal ram data register 0x24 */ + u32 fmdmwcr; /* FM DMA CAM watchdog counter value 0x28 */ + u32 fmdmebcr; /* FM DMA CAM base in MURAM register 0x2c */ + u32 fmdmccqdr; /* FM DMA CAM and CMD Queue Debug reg 0x30 */ + u32 fmdmccqvr1; /* FM DMA CAM and CMD Queue Value reg #1 0x34 */ + u32 fmdmccqvr2; /* FM DMA CAM and CMD Queue Value reg #2 0x38 */ + u32 fmdmcqvr3; /* FM DMA CMD Queue Value register #3 0x3c */ + u32 fmdmcqvr4; /* FM DMA CMD Queue Value register #4 0x40 */ + u32 fmdmcqvr5; /* FM DMA CMD Queue Value register #5 0x44 */ + u32 fmdmsefrc; /* FM DMA Semaphore Entry Full Reject Cntr 0x48 */ + u32 fmdmsqfrc; /* FM DMA Semaphore Queue Full Reject Cntr 0x4c */ + u32 fmdmssrc; /* FM DMA Semaphore SYNC Reject Counter 0x50 */ + u32 fmdmdcr; /* FM DMA Debug Counter 0x54 */ + u32 fmdmemsr; /* FM DMA Emergency Smoother Register 0x58 */ + u32 res005c; /* 0x5c */ + u32 fmdmplr[FMAN_LIODN_TBL / 2]; /* DMA LIODN regs 0x60-0xdf */ + u32 res00e0[0x400 - 56]; +}; + +/* Structure that holds current FMan state. + * Used for saving run time information. + */ +struct fman_state_struct { + u8 fm_id; + u16 fm_clk_freq; + struct fman_rev_info rev_info; + bool enabled_time_stamp; + u8 count1_micro_bit; + u8 total_num_of_tasks; + u8 accumulated_num_of_tasks; + u32 accumulated_fifo_size; + u8 accumulated_num_of_open_dmas; + u8 accumulated_num_of_deq_tnums; + u32 exceptions; + u32 extra_fifo_pool_size; + u8 extra_tasks_pool_size; + u8 extra_open_dmas_pool_size; + u16 port_mfl[MAX_NUM_OF_MACS]; + u16 mac_mfl[MAX_NUM_OF_MACS]; + + /* SOC specific */ + u32 fm_iram_size; + /* DMA */ + u32 dma_thresh_max_commq; + u32 dma_thresh_max_buf; + u32 max_num_of_open_dmas; + /* QMI */ + u32 qmi_max_num_of_tnums; + u32 qmi_def_tnums_thresh; + /* BMI */ + u32 bmi_max_num_of_tasks; + u32 bmi_max_fifo_size; + /* General */ + u32 fm_port_num_of_cg; + u32 num_of_rx_ports; + u32 total_fifo_size; + + u32 qman_channel_base; + u32 num_of_qman_channels; + + struct resource *res; +}; + +/* Structure that holds FMan initial configuration */ +struct fman_cfg { + u8 disp_limit_tsh; + u8 prs_disp_tsh; + u8 plcr_disp_tsh; + u8 kg_disp_tsh; + u8 bmi_disp_tsh; + u8 qmi_enq_disp_tsh; + u8 qmi_deq_disp_tsh; + u8 fm_ctl1_disp_tsh; + u8 fm_ctl2_disp_tsh; + int dma_cache_override; + enum fman_dma_aid_mode dma_aid_mode; + u32 dma_axi_dbg_num_of_beats; + u32 dma_cam_num_of_entries; + u32 dma_watchdog; + u8 dma_comm_qtsh_asrt_emer; + u32 dma_write_buf_tsh_asrt_emer; + u32 dma_read_buf_tsh_asrt_emer; + u8 dma_comm_qtsh_clr_emer; + u32 dma_write_buf_tsh_clr_emer; + u32 dma_read_buf_tsh_clr_emer; + u32 dma_sos_emergency; + int dma_dbg_cnt_mode; + int catastrophic_err; + int dma_err; + u32 exceptions; + u16 clk_freq; + u32 cam_base_addr; + u32 fifo_base_addr; + u32 total_fifo_size; + u32 total_num_of_tasks; + u32 qmi_def_tnums_thresh; +}; + +/* Structure that holds information received from device tree */ +struct fman_dts_params { + void __iomem *base_addr; /* FMan virtual address */ + struct resource *res; /* FMan memory resource */ + u8 id; /* FMan ID */ + + int err_irq; /* FMan Error IRQ */ + + u16 clk_freq; /* FMan clock freq (In Mhz) */ + + u32 qman_channel_base; /* QMan channels base */ + u32 num_of_qman_channels; /* Number of QMan channels */ + + struct resource muram_res; /* MURAM resource */ +}; + +/** fman_exceptions_cb + * fman - Pointer to FMan + * exception - The exception. + * + * Exceptions user callback routine, will be called upon an exception + * passing the exception identification. + * + * Return: irq status + */ +typedef irqreturn_t (fman_exceptions_cb)(struct fman *fman, + enum fman_exceptions exception); + +/** fman_bus_error_cb + * fman - Pointer to FMan + * port_id - Port id + * addr - Address that caused the error + * tnum - Owner of error + * liodn - Logical IO device number + * + * Bus error user callback routine, will be called upon bus error, + * passing parameters describing the errors and the owner. + * + * Return: IRQ status + */ +typedef irqreturn_t (fman_bus_error_cb)(struct fman *fman, u8 port_id, + u64 addr, u8 tnum, u16 liodn); + +struct fman { + struct device *dev; + void __iomem *base_addr; + struct fman_intr_src intr_mng[FMAN_EV_CNT]; + + struct fman_fpm_regs __iomem *fpm_regs; + struct fman_bmi_regs __iomem *bmi_regs; + struct fman_qmi_regs __iomem *qmi_regs; + struct fman_dma_regs __iomem *dma_regs; + fman_exceptions_cb *exception_cb; + fman_bus_error_cb *bus_error_cb; + /* Spinlock for FMan use */ + spinlock_t spinlock; + struct fman_state_struct *state; + + struct fman_cfg *cfg; + struct muram_info *muram; + /* cam section in muram */ + int cam_offset; + size_t cam_size; + /* Fifo in MURAM */ + int fifo_offset; + size_t fifo_size; + + u32 liodn_base[64]; + u32 liodn_offset[64]; + + struct fman_dts_params dts_params; +}; + +static irqreturn_t fman_exceptions(struct fman *fman, + enum fman_exceptions exception) +{ + dev_dbg(fman->dev, "%s: FMan[%d] exception %d\n", + __func__, fman->state->fm_id, exception); + + return IRQ_HANDLED; +} + +static irqreturn_t fman_bus_error(struct fman *fman, u8 __maybe_unused port_id, + u64 __maybe_unused addr, + u8 __maybe_unused tnum, + u16 __maybe_unused liodn) +{ + dev_dbg(fman->dev, "%s: FMan[%d] bus error: port_id[%d]\n", + __func__, fman->state->fm_id, port_id); + + return IRQ_HANDLED; +} + +static inline irqreturn_t call_mac_isr(struct fman *fman, u8 id) +{ + if (fman->intr_mng[id].isr_cb) { + fman->intr_mng[id].isr_cb(fman->intr_mng[id].src_handle); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static inline u8 hw_port_id_to_sw_port_id(u8 major, u8 hw_port_id) +{ + u8 sw_port_id = 0; + + if (hw_port_id >= BASE_TX_PORTID) + sw_port_id = hw_port_id - BASE_TX_PORTID; + else if (hw_port_id >= BASE_RX_PORTID) + sw_port_id = hw_port_id - BASE_RX_PORTID; + else + sw_port_id = 0; + + return sw_port_id; +} + +static void set_port_order_restoration(struct fman_fpm_regs __iomem *fpm_rg, + u8 port_id) +{ + u32 tmp = 0; + + tmp = port_id << FPM_PORT_FM_CTL_PORTID_SHIFT; + + tmp |= FPM_PRT_FM_CTL2 | FPM_PRT_FM_CTL1; + + /* order restoration */ + if (port_id % 2) + tmp |= FPM_PRT_FM_CTL1 << FPM_PRC_ORA_FM_CTL_SEL_SHIFT; + else + tmp |= FPM_PRT_FM_CTL2 << FPM_PRC_ORA_FM_CTL_SEL_SHIFT; + + iowrite32be(tmp, &fpm_rg->fmfp_prc); +} + +static void set_port_liodn(struct fman *fman, u8 port_id, + u32 liodn_base, u32 liodn_ofst) +{ + u32 tmp; + + /* set LIODN base for this port */ + tmp = ioread32be(&fman->dma_regs->fmdmplr[port_id / 2]); + if (port_id % 2) { + tmp &= ~DMA_LIODN_BASE_MASK; + tmp |= liodn_base; + } else { + tmp &= ~(DMA_LIODN_BASE_MASK << DMA_LIODN_SHIFT); + tmp |= liodn_base << DMA_LIODN_SHIFT; + } + iowrite32be(tmp, &fman->dma_regs->fmdmplr[port_id / 2]); + iowrite32be(liodn_ofst, &fman->bmi_regs->fmbm_spliodn[port_id - 1]); +} + +static void enable_rams_ecc(struct fman_fpm_regs __iomem *fpm_rg) +{ + u32 tmp; + + tmp = ioread32be(&fpm_rg->fm_rcr); + if (tmp & FPM_RAM_RAMS_ECC_EN_SRC_SEL) + iowrite32be(tmp | FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr); + else + iowrite32be(tmp | FPM_RAM_RAMS_ECC_EN | + FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr); +} + +static void disable_rams_ecc(struct fman_fpm_regs __iomem *fpm_rg) +{ + u32 tmp; + + tmp = ioread32be(&fpm_rg->fm_rcr); + if (tmp & FPM_RAM_RAMS_ECC_EN_SRC_SEL) + iowrite32be(tmp & ~FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr); + else + iowrite32be(tmp & ~(FPM_RAM_RAMS_ECC_EN | FPM_RAM_IRAM_ECC_EN), + &fpm_rg->fm_rcr); +} + +static void fman_defconfig(struct fman_cfg *cfg) +{ + memset(cfg, 0, sizeof(struct fman_cfg)); + + cfg->catastrophic_err = DEFAULT_CATASTROPHIC_ERR; + cfg->dma_err = DEFAULT_DMA_ERR; + cfg->dma_aid_mode = DEFAULT_AID_MODE; + cfg->dma_comm_qtsh_clr_emer = DEFAULT_DMA_COMM_Q_LOW; + cfg->dma_comm_qtsh_asrt_emer = DEFAULT_DMA_COMM_Q_HIGH; + cfg->dma_cache_override = DEFAULT_CACHE_OVERRIDE; + cfg->dma_cam_num_of_entries = DEFAULT_DMA_CAM_NUM_OF_ENTRIES; + cfg->dma_dbg_cnt_mode = DEFAULT_DMA_DBG_CNT_MODE; + cfg->dma_sos_emergency = DEFAULT_DMA_SOS_EMERGENCY; + cfg->dma_watchdog = DEFAULT_DMA_WATCHDOG; + cfg->disp_limit_tsh = DEFAULT_DISP_LIMIT; + cfg->prs_disp_tsh = DEFAULT_PRS_DISP_TH; + cfg->plcr_disp_tsh = DEFAULT_PLCR_DISP_TH; + cfg->kg_disp_tsh = DEFAULT_KG_DISP_TH; + cfg->bmi_disp_tsh = DEFAULT_BMI_DISP_TH; + cfg->qmi_enq_disp_tsh = DEFAULT_QMI_ENQ_DISP_TH; + cfg->qmi_deq_disp_tsh = DEFAULT_QMI_DEQ_DISP_TH; + cfg->fm_ctl1_disp_tsh = DEFAULT_FM_CTL1_DISP_TH; + cfg->fm_ctl2_disp_tsh = DEFAULT_FM_CTL2_DISP_TH; +} + +static int dma_init(struct fman *fman) +{ + struct fman_dma_regs __iomem *dma_rg = fman->dma_regs; + struct fman_cfg *cfg = fman->cfg; + u32 tmp_reg; + + /* Init DMA Registers */ + + /* clear status reg events */ + tmp_reg = (DMA_STATUS_BUS_ERR | DMA_STATUS_READ_ECC | + DMA_STATUS_SYSTEM_WRITE_ECC | DMA_STATUS_FM_WRITE_ECC); + iowrite32be(ioread32be(&dma_rg->fmdmsr) | tmp_reg, &dma_rg->fmdmsr); + + /* configure mode register */ + tmp_reg = 0; + tmp_reg |= cfg->dma_cache_override << DMA_MODE_CACHE_OR_SHIFT; + if (cfg->exceptions & EX_DMA_BUS_ERROR) + tmp_reg |= DMA_MODE_BER; + if ((cfg->exceptions & EX_DMA_SYSTEM_WRITE_ECC) | + (cfg->exceptions & EX_DMA_READ_ECC) | + (cfg->exceptions & EX_DMA_FM_WRITE_ECC)) + tmp_reg |= DMA_MODE_ECC; + if (cfg->dma_axi_dbg_num_of_beats) + tmp_reg |= (DMA_MODE_AXI_DBG_MASK & + ((cfg->dma_axi_dbg_num_of_beats - 1) + << DMA_MODE_AXI_DBG_SHIFT)); + + tmp_reg |= (((cfg->dma_cam_num_of_entries / DMA_CAM_UNITS) - 1) & + DMA_MODE_CEN_MASK) << DMA_MODE_CEN_SHIFT; + tmp_reg |= DMA_MODE_SECURE_PROT; + tmp_reg |= cfg->dma_dbg_cnt_mode << DMA_MODE_DBG_SHIFT; + tmp_reg |= cfg->dma_aid_mode << DMA_MODE_AID_MODE_SHIFT; + + iowrite32be(tmp_reg, &dma_rg->fmdmmr); + + /* configure thresholds register */ + tmp_reg = ((u32)cfg->dma_comm_qtsh_asrt_emer << + DMA_THRESH_COMMQ_SHIFT); + tmp_reg |= (cfg->dma_read_buf_tsh_asrt_emer & + DMA_THRESH_READ_INT_BUF_MASK) << DMA_THRESH_READ_INT_BUF_SHIFT; + tmp_reg |= cfg->dma_write_buf_tsh_asrt_emer & + DMA_THRESH_WRITE_INT_BUF_MASK; + + iowrite32be(tmp_reg, &dma_rg->fmdmtr); + + /* configure hysteresis register */ + tmp_reg = ((u32)cfg->dma_comm_qtsh_clr_emer << + DMA_THRESH_COMMQ_SHIFT); + tmp_reg |= (cfg->dma_read_buf_tsh_clr_emer & + DMA_THRESH_READ_INT_BUF_MASK) << DMA_THRESH_READ_INT_BUF_SHIFT; + tmp_reg |= cfg->dma_write_buf_tsh_clr_emer & + DMA_THRESH_WRITE_INT_BUF_MASK; + + iowrite32be(tmp_reg, &dma_rg->fmdmhy); + + /* configure emergency threshold */ + iowrite32be(cfg->dma_sos_emergency, &dma_rg->fmdmsetr); + + /* configure Watchdog */ + iowrite32be((cfg->dma_watchdog * cfg->clk_freq), &dma_rg->fmdmwcr); + + iowrite32be(cfg->cam_base_addr, &dma_rg->fmdmebcr); + + /* Allocate MURAM for CAM */ + fman->cam_size = + (u32)(fman->cfg->dma_cam_num_of_entries * DMA_CAM_SIZEOF_ENTRY); + fman->cam_offset = fman_muram_alloc(fman->muram, fman->cam_size); + if (IS_ERR_VALUE(fman->cam_offset)) { + dev_err(fman->dev, "%s: MURAM alloc for DMA CAM failed\n", + __func__); + return -ENOMEM; + } + + if (fman->state->rev_info.major == 2) { + u32 __iomem *cam_base_addr; + + fman_muram_free_mem(fman->muram, fman->cam_offset, + fman->cam_size); + + fman->cam_size = fman->cfg->dma_cam_num_of_entries * 72 + 128; + fman->cam_offset = fman_muram_alloc(fman->muram, + fman->cam_size); + if (IS_ERR_VALUE(fman->cam_offset)) { + dev_err(fman->dev, "%s: MURAM alloc for DMA CAM failed\n", + __func__); + return -ENOMEM; + } + + if (fman->cfg->dma_cam_num_of_entries % 8 || + fman->cfg->dma_cam_num_of_entries > 32) { + dev_err(fman->dev, "%s: wrong dma_cam_num_of_entries\n", + __func__); + return -EINVAL; + } + + cam_base_addr = (u32 __iomem *) + fman_muram_offset_to_vbase(fman->muram, + fman->cam_offset); + iowrite32be(~((1 << + (32 - fman->cfg->dma_cam_num_of_entries)) - 1), + cam_base_addr); + } + + fman->cfg->cam_base_addr = fman->cam_offset; + + return 0; +} + +static void fpm_init(struct fman_fpm_regs __iomem *fpm_rg, struct fman_cfg *cfg) +{ + u32 tmp_reg; + int i; + + /* Init FPM Registers */ + + tmp_reg = (u32)(cfg->disp_limit_tsh << FPM_DISP_LIMIT_SHIFT); + iowrite32be(tmp_reg, &fpm_rg->fmfp_mxd); + + tmp_reg = (((u32)cfg->prs_disp_tsh << FPM_THR1_PRS_SHIFT) | + ((u32)cfg->kg_disp_tsh << FPM_THR1_KG_SHIFT) | + ((u32)cfg->plcr_disp_tsh << FPM_THR1_PLCR_SHIFT) | + ((u32)cfg->bmi_disp_tsh << FPM_THR1_BMI_SHIFT)); + iowrite32be(tmp_reg, &fpm_rg->fmfp_dist1); + + tmp_reg = + (((u32)cfg->qmi_enq_disp_tsh << FPM_THR2_QMI_ENQ_SHIFT) | + ((u32)cfg->qmi_deq_disp_tsh << FPM_THR2_QMI_DEQ_SHIFT) | + ((u32)cfg->fm_ctl1_disp_tsh << FPM_THR2_FM_CTL1_SHIFT) | + ((u32)cfg->fm_ctl2_disp_tsh << FPM_THR2_FM_CTL2_SHIFT)); + iowrite32be(tmp_reg, &fpm_rg->fmfp_dist2); + + /* define exceptions and error behavior */ + tmp_reg = 0; + /* Clear events */ + tmp_reg |= (FPM_EV_MASK_STALL | FPM_EV_MASK_DOUBLE_ECC | + FPM_EV_MASK_SINGLE_ECC); + /* enable interrupts */ + if (cfg->exceptions & EX_FPM_STALL_ON_TASKS) + tmp_reg |= FPM_EV_MASK_STALL_EN; + if (cfg->exceptions & EX_FPM_SINGLE_ECC) + tmp_reg |= FPM_EV_MASK_SINGLE_ECC_EN; + if (cfg->exceptions & EX_FPM_DOUBLE_ECC) + tmp_reg |= FPM_EV_MASK_DOUBLE_ECC_EN; + tmp_reg |= (cfg->catastrophic_err << FPM_EV_MASK_CAT_ERR_SHIFT); + tmp_reg |= (cfg->dma_err << FPM_EV_MASK_DMA_ERR_SHIFT); + /* FMan is not halted upon external halt activation */ + tmp_reg |= FPM_EV_MASK_EXTERNAL_HALT; + /* Man is not halted upon Unrecoverable ECC error behavior */ + tmp_reg |= FPM_EV_MASK_ECC_ERR_HALT; + iowrite32be(tmp_reg, &fpm_rg->fmfp_ee); + + /* clear all fmCtls event registers */ + for (i = 0; i < FM_NUM_OF_FMAN_CTRL_EVENT_REGS; i++) + iowrite32be(0xFFFFFFFF, &fpm_rg->fmfp_cev[i]); + + /* RAM ECC - enable and clear events */ + /* first we need to clear all parser memory, + * as it is uninitialized and may cause ECC errors + */ + /* event bits */ + tmp_reg = (FPM_RAM_MURAM_ECC | FPM_RAM_IRAM_ECC); + + iowrite32be(tmp_reg, &fpm_rg->fm_rcr); + + tmp_reg = 0; + if (cfg->exceptions & EX_IRAM_ECC) { + tmp_reg |= FPM_IRAM_ECC_ERR_EX_EN; + enable_rams_ecc(fpm_rg); + } + if (cfg->exceptions & EX_MURAM_ECC) { + tmp_reg |= FPM_MURAM_ECC_ERR_EX_EN; + enable_rams_ecc(fpm_rg); + } + iowrite32be(tmp_reg, &fpm_rg->fm_rie); +} + +static void bmi_init(struct fman_bmi_regs __iomem *bmi_rg, + struct fman_cfg *cfg) +{ + u32 tmp_reg; + + /* Init BMI Registers */ + + /* define common resources */ + tmp_reg = cfg->fifo_base_addr; + tmp_reg = tmp_reg / BMI_FIFO_ALIGN; + + tmp_reg |= ((cfg->total_fifo_size / FMAN_BMI_FIFO_UNITS - 1) << + BMI_CFG1_FIFO_SIZE_SHIFT); + iowrite32be(tmp_reg, &bmi_rg->fmbm_cfg1); + + tmp_reg = ((cfg->total_num_of_tasks - 1) & BMI_CFG2_TASKS_MASK) << + BMI_CFG2_TASKS_SHIFT; + /* num of DMA's will be dynamically updated when each port is set */ + iowrite32be(tmp_reg, &bmi_rg->fmbm_cfg2); + + /* define unmaskable exceptions, enable and clear events */ + tmp_reg = 0; + iowrite32be(BMI_ERR_INTR_EN_LIST_RAM_ECC | + BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC | + BMI_ERR_INTR_EN_STATISTICS_RAM_ECC | + BMI_ERR_INTR_EN_DISPATCH_RAM_ECC, &bmi_rg->fmbm_ievr); + + if (cfg->exceptions & EX_BMI_LIST_RAM_ECC) + tmp_reg |= BMI_ERR_INTR_EN_LIST_RAM_ECC; + if (cfg->exceptions & EX_BMI_STORAGE_PROFILE_ECC) + tmp_reg |= BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC; + if (cfg->exceptions & EX_BMI_STATISTICS_RAM_ECC) + tmp_reg |= BMI_ERR_INTR_EN_STATISTICS_RAM_ECC; + if (cfg->exceptions & EX_BMI_DISPATCH_RAM_ECC) + tmp_reg |= BMI_ERR_INTR_EN_DISPATCH_RAM_ECC; + iowrite32be(tmp_reg, &bmi_rg->fmbm_ier); +} + +static void qmi_init(struct fman_qmi_regs __iomem *qmi_rg, + struct fman_cfg *cfg) +{ + u32 tmp_reg; + + /* Init QMI Registers */ + + /* Clear error interrupt events */ + + iowrite32be(QMI_ERR_INTR_EN_DOUBLE_ECC | QMI_ERR_INTR_EN_DEQ_FROM_DEF, + &qmi_rg->fmqm_eie); + tmp_reg = 0; + if (cfg->exceptions & EX_QMI_DEQ_FROM_UNKNOWN_PORTID) + tmp_reg |= QMI_ERR_INTR_EN_DEQ_FROM_DEF; + if (cfg->exceptions & EX_QMI_DOUBLE_ECC) + tmp_reg |= QMI_ERR_INTR_EN_DOUBLE_ECC; + /* enable events */ + iowrite32be(tmp_reg, &qmi_rg->fmqm_eien); + + tmp_reg = 0; + /* Clear interrupt events */ + iowrite32be(QMI_INTR_EN_SINGLE_ECC, &qmi_rg->fmqm_ie); + if (cfg->exceptions & EX_QMI_SINGLE_ECC) + tmp_reg |= QMI_INTR_EN_SINGLE_ECC; + /* enable events */ + iowrite32be(tmp_reg, &qmi_rg->fmqm_ien); +} + +static int enable(struct fman *fman, struct fman_cfg *cfg) +{ + u32 cfg_reg = 0; + + /* Enable all modules */ + + /* clear&enable global counters - calculate reg and save for later, + * because it's the same reg for QMI enable + */ + cfg_reg = QMI_CFG_EN_COUNTERS; + + /* Set enqueue and dequeue thresholds */ + cfg_reg |= (cfg->qmi_def_tnums_thresh << 8) | cfg->qmi_def_tnums_thresh; + + iowrite32be(BMI_INIT_START, &fman->bmi_regs->fmbm_init); + iowrite32be(cfg_reg | QMI_CFG_ENQ_EN | QMI_CFG_DEQ_EN, + &fman->qmi_regs->fmqm_gc); + + return 0; +} + +static int set_exception(struct fman *fman, + enum fman_exceptions exception, bool enable) +{ + u32 tmp; + + switch (exception) { + case FMAN_EX_DMA_BUS_ERROR: + tmp = ioread32be(&fman->dma_regs->fmdmmr); + if (enable) + tmp |= DMA_MODE_BER; + else + tmp &= ~DMA_MODE_BER; + /* disable bus error */ + iowrite32be(tmp, &fman->dma_regs->fmdmmr); + break; + case FMAN_EX_DMA_READ_ECC: + case FMAN_EX_DMA_SYSTEM_WRITE_ECC: + case FMAN_EX_DMA_FM_WRITE_ECC: + tmp = ioread32be(&fman->dma_regs->fmdmmr); + if (enable) + tmp |= DMA_MODE_ECC; + else + tmp &= ~DMA_MODE_ECC; + iowrite32be(tmp, &fman->dma_regs->fmdmmr); + break; + case FMAN_EX_FPM_STALL_ON_TASKS: + tmp = ioread32be(&fman->fpm_regs->fmfp_ee); + if (enable) + tmp |= FPM_EV_MASK_STALL_EN; + else + tmp &= ~FPM_EV_MASK_STALL_EN; + iowrite32be(tmp, &fman->fpm_regs->fmfp_ee); + break; + case FMAN_EX_FPM_SINGLE_ECC: + tmp = ioread32be(&fman->fpm_regs->fmfp_ee); + if (enable) + tmp |= FPM_EV_MASK_SINGLE_ECC_EN; + else + tmp &= ~FPM_EV_MASK_SINGLE_ECC_EN; + iowrite32be(tmp, &fman->fpm_regs->fmfp_ee); + break; + case FMAN_EX_FPM_DOUBLE_ECC: + tmp = ioread32be(&fman->fpm_regs->fmfp_ee); + if (enable) + tmp |= FPM_EV_MASK_DOUBLE_ECC_EN; + else + tmp &= ~FPM_EV_MASK_DOUBLE_ECC_EN; + iowrite32be(tmp, &fman->fpm_regs->fmfp_ee); + break; + case FMAN_EX_QMI_SINGLE_ECC: + tmp = ioread32be(&fman->qmi_regs->fmqm_ien); + if (enable) + tmp |= QMI_INTR_EN_SINGLE_ECC; + else + tmp &= ~QMI_INTR_EN_SINGLE_ECC; + iowrite32be(tmp, &fman->qmi_regs->fmqm_ien); + break; + case FMAN_EX_QMI_DOUBLE_ECC: + tmp = ioread32be(&fman->qmi_regs->fmqm_eien); + if (enable) + tmp |= QMI_ERR_INTR_EN_DOUBLE_ECC; + else + tmp &= ~QMI_ERR_INTR_EN_DOUBLE_ECC; + iowrite32be(tmp, &fman->qmi_regs->fmqm_eien); + break; + case FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID: + tmp = ioread32be(&fman->qmi_regs->fmqm_eien); + if (enable) + tmp |= QMI_ERR_INTR_EN_DEQ_FROM_DEF; + else + tmp &= ~QMI_ERR_INTR_EN_DEQ_FROM_DEF; + iowrite32be(tmp, &fman->qmi_regs->fmqm_eien); + break; + case FMAN_EX_BMI_LIST_RAM_ECC: + tmp = ioread32be(&fman->bmi_regs->fmbm_ier); + if (enable) + tmp |= BMI_ERR_INTR_EN_LIST_RAM_ECC; + else + tmp &= ~BMI_ERR_INTR_EN_LIST_RAM_ECC; + iowrite32be(tmp, &fman->bmi_regs->fmbm_ier); + break; + case FMAN_EX_BMI_STORAGE_PROFILE_ECC: + tmp = ioread32be(&fman->bmi_regs->fmbm_ier); + if (enable) + tmp |= BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC; + else + tmp &= ~BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC; + iowrite32be(tmp, &fman->bmi_regs->fmbm_ier); + break; + case FMAN_EX_BMI_STATISTICS_RAM_ECC: + tmp = ioread32be(&fman->bmi_regs->fmbm_ier); + if (enable) + tmp |= BMI_ERR_INTR_EN_STATISTICS_RAM_ECC; + else + tmp &= ~BMI_ERR_INTR_EN_STATISTICS_RAM_ECC; + iowrite32be(tmp, &fman->bmi_regs->fmbm_ier); + break; + case FMAN_EX_BMI_DISPATCH_RAM_ECC: + tmp = ioread32be(&fman->bmi_regs->fmbm_ier); + if (enable) + tmp |= BMI_ERR_INTR_EN_DISPATCH_RAM_ECC; + else + tmp &= ~BMI_ERR_INTR_EN_DISPATCH_RAM_ECC; + iowrite32be(tmp, &fman->bmi_regs->fmbm_ier); + break; + case FMAN_EX_IRAM_ECC: + tmp = ioread32be(&fman->fpm_regs->fm_rie); + if (enable) { + /* enable ECC if not enabled */ + enable_rams_ecc(fman->fpm_regs); + /* enable ECC interrupts */ + tmp |= FPM_IRAM_ECC_ERR_EX_EN; + } else { + /* ECC mechanism may be disabled, + * depending on driver status + */ + disable_rams_ecc(fman->fpm_regs); + tmp &= ~FPM_IRAM_ECC_ERR_EX_EN; + } + iowrite32be(tmp, &fman->fpm_regs->fm_rie); + break; + case FMAN_EX_MURAM_ECC: + tmp = ioread32be(&fman->fpm_regs->fm_rie); + if (enable) { + /* enable ECC if not enabled */ + enable_rams_ecc(fman->fpm_regs); + /* enable ECC interrupts */ + tmp |= FPM_MURAM_ECC_ERR_EX_EN; + } else { + /* ECC mechanism may be disabled, + * depending on driver status + */ + disable_rams_ecc(fman->fpm_regs); + tmp &= ~FPM_MURAM_ECC_ERR_EX_EN; + } + iowrite32be(tmp, &fman->fpm_regs->fm_rie); + break; + default: + return -EINVAL; + } + return 0; +} + +static void resume(struct fman_fpm_regs __iomem *fpm_rg) +{ + u32 tmp; + + tmp = ioread32be(&fpm_rg->fmfp_ee); + /* clear tmp_reg event bits in order not to clear standing events */ + tmp &= ~(FPM_EV_MASK_DOUBLE_ECC | + FPM_EV_MASK_STALL | FPM_EV_MASK_SINGLE_ECC); + tmp |= FPM_EV_MASK_RELEASE_FM; + + iowrite32be(tmp, &fpm_rg->fmfp_ee); +} + +static int fill_soc_specific_params(struct fman_state_struct *state) +{ + u8 minor = state->rev_info.minor; + /* P4080 - Major 2 + * P2041/P3041/P5020/P5040 - Major 3 + * Tx/Bx - Major 6 + */ + switch (state->rev_info.major) { + case 3: + state->bmi_max_fifo_size = 160 * 1024; + state->fm_iram_size = 64 * 1024; + state->dma_thresh_max_commq = 31; + state->dma_thresh_max_buf = 127; + state->qmi_max_num_of_tnums = 64; + state->qmi_def_tnums_thresh = 48; + state->bmi_max_num_of_tasks = 128; + state->max_num_of_open_dmas = 32; + state->fm_port_num_of_cg = 256; + state->num_of_rx_ports = 6; + state->total_fifo_size = 122 * 1024; + break; + + case 2: + state->bmi_max_fifo_size = 160 * 1024; + state->fm_iram_size = 64 * 1024; + state->dma_thresh_max_commq = 31; + state->dma_thresh_max_buf = 127; + state->qmi_max_num_of_tnums = 64; + state->qmi_def_tnums_thresh = 48; + state->bmi_max_num_of_tasks = 128; + state->max_num_of_open_dmas = 32; + state->fm_port_num_of_cg = 256; + state->num_of_rx_ports = 5; + state->total_fifo_size = 100 * 1024; + break; + + case 6: + state->dma_thresh_max_commq = 83; + state->dma_thresh_max_buf = 127; + state->qmi_max_num_of_tnums = 64; + state->qmi_def_tnums_thresh = 32; + state->fm_port_num_of_cg = 256; + + /* FManV3L */ + if (minor == 1 || minor == 4) { + state->bmi_max_fifo_size = 192 * 1024; + state->bmi_max_num_of_tasks = 64; + state->max_num_of_open_dmas = 32; + state->num_of_rx_ports = 5; + if (minor == 1) + state->fm_iram_size = 32 * 1024; + else + state->fm_iram_size = 64 * 1024; + state->total_fifo_size = 156 * 1024; + } + /* FManV3H */ + else if (minor == 0 || minor == 2 || minor == 3) { + state->bmi_max_fifo_size = 384 * 1024; + state->fm_iram_size = 64 * 1024; + state->bmi_max_num_of_tasks = 128; + state->max_num_of_open_dmas = 84; + state->num_of_rx_ports = 8; + state->total_fifo_size = 295 * 1024; + } else { + pr_err("Unsupported FManv3 version\n"); + return -EINVAL; + } + + break; + default: + pr_err("Unsupported FMan version\n"); + return -EINVAL; + } + + return 0; +} + +static bool is_init_done(struct fman_cfg *cfg) +{ + /* Checks if FMan driver parameters were initialized */ + if (!cfg) + return true; + + return false; +} + +static void free_init_resources(struct fman *fman) +{ + if (fman->cam_offset) + fman_muram_free_mem(fman->muram, fman->cam_offset, + fman->cam_size); + if (fman->fifo_offset) + fman_muram_free_mem(fman->muram, fman->fifo_offset, + fman->fifo_size); +} + +static irqreturn_t bmi_err_event(struct fman *fman) +{ + u32 event, mask, force; + struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs; + irqreturn_t ret = IRQ_NONE; + + event = ioread32be(&bmi_rg->fmbm_ievr); + mask = ioread32be(&bmi_rg->fmbm_ier); + event &= mask; + /* clear the forced events */ + force = ioread32be(&bmi_rg->fmbm_ifr); + if (force & event) + iowrite32be(force & ~event, &bmi_rg->fmbm_ifr); + /* clear the acknowledged events */ + iowrite32be(event, &bmi_rg->fmbm_ievr); + + if (event & BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC) + ret = fman->exception_cb(fman, FMAN_EX_BMI_STORAGE_PROFILE_ECC); + if (event & BMI_ERR_INTR_EN_LIST_RAM_ECC) + ret = fman->exception_cb(fman, FMAN_EX_BMI_LIST_RAM_ECC); + if (event & BMI_ERR_INTR_EN_STATISTICS_RAM_ECC) + ret = fman->exception_cb(fman, FMAN_EX_BMI_STATISTICS_RAM_ECC); + if (event & BMI_ERR_INTR_EN_DISPATCH_RAM_ECC) + ret = fman->exception_cb(fman, FMAN_EX_BMI_DISPATCH_RAM_ECC); + + return ret; +} + +static irqreturn_t qmi_err_event(struct fman *fman) +{ + u32 event, mask, force; + struct fman_qmi_regs __iomem *qmi_rg = fman->qmi_regs; + irqreturn_t ret = IRQ_NONE; + + event = ioread32be(&qmi_rg->fmqm_eie); + mask = ioread32be(&qmi_rg->fmqm_eien); + event &= mask; + + /* clear the forced events */ + force = ioread32be(&qmi_rg->fmqm_eif); + if (force & event) + iowrite32be(force & ~event, &qmi_rg->fmqm_eif); + /* clear the acknowledged events */ + iowrite32be(event, &qmi_rg->fmqm_eie); + + if (event & QMI_ERR_INTR_EN_DOUBLE_ECC) + ret = fman->exception_cb(fman, FMAN_EX_QMI_DOUBLE_ECC); + if (event & QMI_ERR_INTR_EN_DEQ_FROM_DEF) + ret = fman->exception_cb(fman, + FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID); + + return ret; +} + +static irqreturn_t dma_err_event(struct fman *fman) +{ + u32 status, mask, com_id; + u8 tnum, port_id, relative_port_id; + u16 liodn; + struct fman_dma_regs __iomem *dma_rg = fman->dma_regs; + irqreturn_t ret = IRQ_NONE; + + status = ioread32be(&dma_rg->fmdmsr); + mask = ioread32be(&dma_rg->fmdmmr); + + /* clear DMA_STATUS_BUS_ERR if mask has no DMA_MODE_BER */ + if ((mask & DMA_MODE_BER) != DMA_MODE_BER) + status &= ~DMA_STATUS_BUS_ERR; + + /* clear relevant bits if mask has no DMA_MODE_ECC */ + if ((mask & DMA_MODE_ECC) != DMA_MODE_ECC) + status &= ~(DMA_STATUS_FM_SPDAT_ECC | + DMA_STATUS_READ_ECC | + DMA_STATUS_SYSTEM_WRITE_ECC | + DMA_STATUS_FM_WRITE_ECC); + + /* clear set events */ + iowrite32be(status, &dma_rg->fmdmsr); + + if (status & DMA_STATUS_BUS_ERR) { + u64 addr; + + addr = (u64)ioread32be(&dma_rg->fmdmtal); + addr |= ((u64)(ioread32be(&dma_rg->fmdmtah)) << 32); + + com_id = ioread32be(&dma_rg->fmdmtcid); + port_id = (u8)(((com_id & DMA_TRANSFER_PORTID_MASK) >> + DMA_TRANSFER_PORTID_SHIFT)); + relative_port_id = + hw_port_id_to_sw_port_id(fman->state->rev_info.major, port_id); + tnum = (u8)((com_id & DMA_TRANSFER_TNUM_MASK) >> + DMA_TRANSFER_TNUM_SHIFT); + liodn = (u16)(com_id & DMA_TRANSFER_LIODN_MASK); + ret = fman->bus_error_cb(fman, relative_port_id, addr, tnum, + liodn); + } + if (status & DMA_STATUS_FM_SPDAT_ECC) + ret = fman->exception_cb(fman, FMAN_EX_DMA_SINGLE_PORT_ECC); + if (status & DMA_STATUS_READ_ECC) + ret = fman->exception_cb(fman, FMAN_EX_DMA_READ_ECC); + if (status & DMA_STATUS_SYSTEM_WRITE_ECC) + ret = fman->exception_cb(fman, FMAN_EX_DMA_SYSTEM_WRITE_ECC); + if (status & DMA_STATUS_FM_WRITE_ECC) + ret = fman->exception_cb(fman, FMAN_EX_DMA_FM_WRITE_ECC); + + return ret; +} + +static irqreturn_t fpm_err_event(struct fman *fman) +{ + u32 event; + struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs; + irqreturn_t ret = IRQ_NONE; + + event = ioread32be(&fpm_rg->fmfp_ee); + /* clear the all occurred events */ + iowrite32be(event, &fpm_rg->fmfp_ee); + + if ((event & FPM_EV_MASK_DOUBLE_ECC) && + (event & FPM_EV_MASK_DOUBLE_ECC_EN)) + ret = fman->exception_cb(fman, FMAN_EX_FPM_DOUBLE_ECC); + if ((event & FPM_EV_MASK_STALL) && (event & FPM_EV_MASK_STALL_EN)) + ret = fman->exception_cb(fman, FMAN_EX_FPM_STALL_ON_TASKS); + if ((event & FPM_EV_MASK_SINGLE_ECC) && + (event & FPM_EV_MASK_SINGLE_ECC_EN)) + ret = fman->exception_cb(fman, FMAN_EX_FPM_SINGLE_ECC); + + return ret; +} + +static irqreturn_t muram_err_intr(struct fman *fman) +{ + u32 event, mask; + struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs; + irqreturn_t ret = IRQ_NONE; + + event = ioread32be(&fpm_rg->fm_rcr); + mask = ioread32be(&fpm_rg->fm_rie); + + /* clear MURAM event bit (do not clear IRAM event) */ + iowrite32be(event & ~FPM_RAM_IRAM_ECC, &fpm_rg->fm_rcr); + + if ((mask & FPM_MURAM_ECC_ERR_EX_EN) && (event & FPM_RAM_MURAM_ECC)) + ret = fman->exception_cb(fman, FMAN_EX_MURAM_ECC); + + return ret; +} + +static irqreturn_t qmi_event(struct fman *fman) +{ + u32 event, mask, force; + struct fman_qmi_regs __iomem *qmi_rg = fman->qmi_regs; + irqreturn_t ret = IRQ_NONE; + + event = ioread32be(&qmi_rg->fmqm_ie); + mask = ioread32be(&qmi_rg->fmqm_ien); + event &= mask; + /* clear the forced events */ + force = ioread32be(&qmi_rg->fmqm_if); + if (force & event) + iowrite32be(force & ~event, &qmi_rg->fmqm_if); + /* clear the acknowledged events */ + iowrite32be(event, &qmi_rg->fmqm_ie); + + if (event & QMI_INTR_EN_SINGLE_ECC) + ret = fman->exception_cb(fman, FMAN_EX_QMI_SINGLE_ECC); + + return ret; +} + +static void enable_time_stamp(struct fman *fman) +{ + struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs; + u16 fm_clk_freq = fman->state->fm_clk_freq; + u32 tmp, intgr, ts_freq; + u64 frac; + + ts_freq = (u32)(1 << fman->state->count1_micro_bit); + /* configure timestamp so that bit 8 will count 1 microsecond + * Find effective count rate at TIMESTAMP least significant bits: + * Effective_Count_Rate = 1MHz x 2^8 = 256MHz + * Find frequency ratio between effective count rate and the clock: + * Effective_Count_Rate / CLK e.g. for 600 MHz clock: + * 256/600 = 0.4266666... + */ + + intgr = ts_freq / fm_clk_freq; + /* we multiply by 2^16 to keep the fraction of the division + * we do not div back, since we write this value as a fraction + * see spec + */ + + frac = ((ts_freq << 16) - (intgr << 16) * fm_clk_freq) / fm_clk_freq; + /* we check remainder of the division in order to round up if not int */ + if (((ts_freq << 16) - (intgr << 16) * fm_clk_freq) % fm_clk_freq) + frac++; + + tmp = (intgr << FPM_TS_INT_SHIFT) | (u16)frac; + iowrite32be(tmp, &fpm_rg->fmfp_tsc2); + + /* enable timestamp with original clock */ + iowrite32be(FPM_TS_CTL_EN, &fpm_rg->fmfp_tsc1); + fman->state->enabled_time_stamp = true; +} + +static int clear_iram(struct fman *fman) +{ + struct fman_iram_regs __iomem *iram; + int i, count; + + iram = fman->base_addr + IMEM_OFFSET; + + /* Enable the auto-increment */ + iowrite32be(IRAM_IADD_AIE, &iram->iadd); + count = 100; + do { + udelay(1); + } while ((ioread32be(&iram->iadd) != IRAM_IADD_AIE) && --count); + if (count == 0) + return -EBUSY; + + for (i = 0; i < (fman->state->fm_iram_size / 4); i++) + iowrite32be(0xffffffff, &iram->idata); + + iowrite32be(fman->state->fm_iram_size - 4, &iram->iadd); + count = 100; + do { + udelay(1); + } while ((ioread32be(&iram->idata) != 0xffffffff) && --count); + if (count == 0) + return -EBUSY; + + return 0; +} + +static u32 get_exception_flag(enum fman_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FMAN_EX_DMA_BUS_ERROR: + bit_mask = EX_DMA_BUS_ERROR; + break; + case FMAN_EX_DMA_SINGLE_PORT_ECC: + bit_mask = EX_DMA_SINGLE_PORT_ECC; + break; + case FMAN_EX_DMA_READ_ECC: + bit_mask = EX_DMA_READ_ECC; + break; + case FMAN_EX_DMA_SYSTEM_WRITE_ECC: + bit_mask = EX_DMA_SYSTEM_WRITE_ECC; + break; + case FMAN_EX_DMA_FM_WRITE_ECC: + bit_mask = EX_DMA_FM_WRITE_ECC; + break; + case FMAN_EX_FPM_STALL_ON_TASKS: + bit_mask = EX_FPM_STALL_ON_TASKS; + break; + case FMAN_EX_FPM_SINGLE_ECC: + bit_mask = EX_FPM_SINGLE_ECC; + break; + case FMAN_EX_FPM_DOUBLE_ECC: + bit_mask = EX_FPM_DOUBLE_ECC; + break; + case FMAN_EX_QMI_SINGLE_ECC: + bit_mask = EX_QMI_SINGLE_ECC; + break; + case FMAN_EX_QMI_DOUBLE_ECC: + bit_mask = EX_QMI_DOUBLE_ECC; + break; + case FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID: + bit_mask = EX_QMI_DEQ_FROM_UNKNOWN_PORTID; + break; + case FMAN_EX_BMI_LIST_RAM_ECC: + bit_mask = EX_BMI_LIST_RAM_ECC; + break; + case FMAN_EX_BMI_STORAGE_PROFILE_ECC: + bit_mask = EX_BMI_STORAGE_PROFILE_ECC; + break; + case FMAN_EX_BMI_STATISTICS_RAM_ECC: + bit_mask = EX_BMI_STATISTICS_RAM_ECC; + break; + case FMAN_EX_BMI_DISPATCH_RAM_ECC: + bit_mask = EX_BMI_DISPATCH_RAM_ECC; + break; + case FMAN_EX_MURAM_ECC: + bit_mask = EX_MURAM_ECC; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static int get_module_event(enum fman_event_modules module, u8 mod_id, + enum fman_intr_type intr_type) +{ + int event; + + switch (module) { + case FMAN_MOD_MAC: + if (intr_type == FMAN_INTR_TYPE_ERR) + event = FMAN_EV_ERR_MAC0 + mod_id; + else + event = FMAN_EV_MAC0 + mod_id; + break; + case FMAN_MOD_FMAN_CTRL: + if (intr_type == FMAN_INTR_TYPE_ERR) + event = FMAN_EV_CNT; + else + event = (FMAN_EV_FMAN_CTRL_0 + mod_id); + break; + case FMAN_MOD_DUMMY_LAST: + event = FMAN_EV_CNT; + break; + default: + event = FMAN_EV_CNT; + break; + } + + return event; +} + +static int set_size_of_fifo(struct fman *fman, u8 port_id, u32 *size_of_fifo, + u32 *extra_size_of_fifo) +{ + struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs; + u32 fifo = *size_of_fifo; + u32 extra_fifo = *extra_size_of_fifo; + u32 tmp; + + /* if this is the first time a port requires extra_fifo_pool_size, + * the total extra_fifo_pool_size must be initialized to 1 buffer per + * port + */ + if (extra_fifo && !fman->state->extra_fifo_pool_size) + fman->state->extra_fifo_pool_size = + fman->state->num_of_rx_ports * FMAN_BMI_FIFO_UNITS; + + fman->state->extra_fifo_pool_size = + max(fman->state->extra_fifo_pool_size, extra_fifo); + + /* check that there are enough uncommitted fifo size */ + if ((fman->state->accumulated_fifo_size + fifo) > + (fman->state->total_fifo_size - + fman->state->extra_fifo_pool_size)) { + dev_err(fman->dev, "%s: Requested fifo size and extra size exceed total FIFO size.\n", + __func__); + return -EAGAIN; + } + + /* Read, modify and write to HW */ + tmp = (fifo / FMAN_BMI_FIFO_UNITS - 1) | + ((extra_fifo / FMAN_BMI_FIFO_UNITS) << + BMI_EXTRA_FIFO_SIZE_SHIFT); + iowrite32be(tmp, &bmi_rg->fmbm_pfs[port_id - 1]); + + /* update accumulated */ + fman->state->accumulated_fifo_size += fifo; + + return 0; +} + +static int set_num_of_tasks(struct fman *fman, u8 port_id, u8 *num_of_tasks, + u8 *num_of_extra_tasks) +{ + struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs; + u8 tasks = *num_of_tasks; + u8 extra_tasks = *num_of_extra_tasks; + u32 tmp; + + if (extra_tasks) + fman->state->extra_tasks_pool_size = + max(fman->state->extra_tasks_pool_size, extra_tasks); + + /* check that there are enough uncommitted tasks */ + if ((fman->state->accumulated_num_of_tasks + tasks) > + (fman->state->total_num_of_tasks - + fman->state->extra_tasks_pool_size)) { + dev_err(fman->dev, "%s: Requested num_of_tasks and extra tasks pool for fm%d exceed total num_of_tasks.\n", + __func__, fman->state->fm_id); + return -EAGAIN; + } + /* update accumulated */ + fman->state->accumulated_num_of_tasks += tasks; + + /* Write to HW */ + tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]) & + ~(BMI_NUM_OF_TASKS_MASK | BMI_NUM_OF_EXTRA_TASKS_MASK); + tmp |= ((u32)((tasks - 1) << BMI_NUM_OF_TASKS_SHIFT) | + (u32)(extra_tasks << BMI_EXTRA_NUM_OF_TASKS_SHIFT)); + iowrite32be(tmp, &bmi_rg->fmbm_pp[port_id - 1]); + + return 0; +} + +static int set_num_of_open_dmas(struct fman *fman, u8 port_id, + u8 *num_of_open_dmas, + u8 *num_of_extra_open_dmas) +{ + struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs; + u8 open_dmas = *num_of_open_dmas; + u8 extra_open_dmas = *num_of_extra_open_dmas; + u8 total_num_dmas = 0, current_val = 0, current_extra_val = 0; + u32 tmp; + + if (!open_dmas) { + /* Configuration according to values in the HW. + * read the current number of open Dma's + */ + tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]); + current_extra_val = (u8)((tmp & BMI_NUM_OF_EXTRA_DMAS_MASK) >> + BMI_EXTRA_NUM_OF_DMAS_SHIFT); + + tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]); + current_val = (u8)(((tmp & BMI_NUM_OF_DMAS_MASK) >> + BMI_NUM_OF_DMAS_SHIFT) + 1); + + /* This is the first configuration and user did not + * specify value (!open_dmas), reset values will be used + * and we just save these values for resource management + */ + fman->state->extra_open_dmas_pool_size = + (u8)max(fman->state->extra_open_dmas_pool_size, + current_extra_val); + fman->state->accumulated_num_of_open_dmas += current_val; + *num_of_open_dmas = current_val; + *num_of_extra_open_dmas = current_extra_val; + return 0; + } + + if (extra_open_dmas > current_extra_val) + fman->state->extra_open_dmas_pool_size = + (u8)max(fman->state->extra_open_dmas_pool_size, + extra_open_dmas); + + if ((fman->state->rev_info.major < 6) && + (fman->state->accumulated_num_of_open_dmas - current_val + + open_dmas > fman->state->max_num_of_open_dmas)) { + dev_err(fman->dev, "%s: Requested num_of_open_dmas for fm%d exceeds total num_of_open_dmas.\n", + __func__, fman->state->fm_id); + return -EAGAIN; + } else if ((fman->state->rev_info.major >= 6) && + !((fman->state->rev_info.major == 6) && + (fman->state->rev_info.minor == 0)) && + (fman->state->accumulated_num_of_open_dmas - + current_val + open_dmas > + fman->state->dma_thresh_max_commq + 1)) { + dev_err(fman->dev, "%s: Requested num_of_open_dmas for fm%d exceeds DMA Command queue (%d)\n", + __func__, fman->state->fm_id, + fman->state->dma_thresh_max_commq + 1); + return -EAGAIN; + } + + WARN_ON(fman->state->accumulated_num_of_open_dmas < current_val); + /* update acummulated */ + fman->state->accumulated_num_of_open_dmas -= current_val; + fman->state->accumulated_num_of_open_dmas += open_dmas; + + if (fman->state->rev_info.major < 6) + total_num_dmas = + (u8)(fman->state->accumulated_num_of_open_dmas + + fman->state->extra_open_dmas_pool_size); + + /* calculate reg */ + tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]) & + ~(BMI_NUM_OF_DMAS_MASK | BMI_NUM_OF_EXTRA_DMAS_MASK); + tmp |= (u32)(((open_dmas - 1) << BMI_NUM_OF_DMAS_SHIFT) | + (extra_open_dmas << BMI_EXTRA_NUM_OF_DMAS_SHIFT)); + iowrite32be(tmp, &bmi_rg->fmbm_pp[port_id - 1]); + + /* update total num of DMA's with committed number of open DMAS, + * and max uncommitted pool. + */ + if (total_num_dmas) { + tmp = ioread32be(&bmi_rg->fmbm_cfg2) & ~BMI_CFG2_DMAS_MASK; + tmp |= (u32)(total_num_dmas - 1) << BMI_CFG2_DMAS_SHIFT; + iowrite32be(tmp, &bmi_rg->fmbm_cfg2); + } + + return 0; +} + +static int fman_config(struct fman *fman) +{ + void __iomem *base_addr; + int err; + + base_addr = fman->dts_params.base_addr; + + fman->state = kzalloc(sizeof(*fman->state), GFP_KERNEL); + if (!fman->state) + goto err_fm_state; + + /* Allocate the FM driver's parameters structure */ + fman->cfg = kzalloc(sizeof(*fman->cfg), GFP_KERNEL); + if (!fman->cfg) + goto err_fm_drv; + + /* Initialize MURAM block */ + fman->muram = + fman_muram_init(fman->dts_params.muram_res.start, + resource_size(&fman->dts_params.muram_res)); + if (!fman->muram) + goto err_fm_soc_specific; + + /* Initialize FM parameters which will be kept by the driver */ + fman->state->fm_id = fman->dts_params.id; + fman->state->fm_clk_freq = fman->dts_params.clk_freq; + fman->state->qman_channel_base = fman->dts_params.qman_channel_base; + fman->state->num_of_qman_channels = + fman->dts_params.num_of_qman_channels; + fman->state->res = fman->dts_params.res; + fman->exception_cb = fman_exceptions; + fman->bus_error_cb = fman_bus_error; + fman->fpm_regs = base_addr + FPM_OFFSET; + fman->bmi_regs = base_addr + BMI_OFFSET; + fman->qmi_regs = base_addr + QMI_OFFSET; + fman->dma_regs = base_addr + DMA_OFFSET; + fman->base_addr = base_addr; + + spin_lock_init(&fman->spinlock); + fman_defconfig(fman->cfg); + + fman->state->extra_fifo_pool_size = 0; + fman->state->exceptions = (EX_DMA_BUS_ERROR | + EX_DMA_READ_ECC | + EX_DMA_SYSTEM_WRITE_ECC | + EX_DMA_FM_WRITE_ECC | + EX_FPM_STALL_ON_TASKS | + EX_FPM_SINGLE_ECC | + EX_FPM_DOUBLE_ECC | + EX_QMI_DEQ_FROM_UNKNOWN_PORTID | + EX_BMI_LIST_RAM_ECC | + EX_BMI_STORAGE_PROFILE_ECC | + EX_BMI_STATISTICS_RAM_ECC | + EX_MURAM_ECC | + EX_BMI_DISPATCH_RAM_ECC | + EX_QMI_DOUBLE_ECC | + EX_QMI_SINGLE_ECC); + + /* Read FMan revision for future use*/ + fman_get_revision(fman, &fman->state->rev_info); + + err = fill_soc_specific_params(fman->state); + if (err) + goto err_fm_soc_specific; + + /* FM_AID_MODE_NO_TNUM_SW005 Errata workaround */ + if (fman->state->rev_info.major >= 6) + fman->cfg->dma_aid_mode = FMAN_DMA_AID_OUT_PORT_ID; + + fman->cfg->qmi_def_tnums_thresh = fman->state->qmi_def_tnums_thresh; + + fman->state->total_num_of_tasks = + (u8)DFLT_TOTAL_NUM_OF_TASKS(fman->state->rev_info.major, + fman->state->rev_info.minor, + fman->state->bmi_max_num_of_tasks); + + if (fman->state->rev_info.major < 6) { + fman->cfg->dma_comm_qtsh_clr_emer = + (u8)DFLT_DMA_COMM_Q_LOW(fman->state->rev_info.major, + fman->state->dma_thresh_max_commq); + + fman->cfg->dma_comm_qtsh_asrt_emer = + (u8)DFLT_DMA_COMM_Q_HIGH(fman->state->rev_info.major, + fman->state->dma_thresh_max_commq); + + fman->cfg->dma_cam_num_of_entries = + DFLT_DMA_CAM_NUM_OF_ENTRIES(fman->state->rev_info.major); + + fman->cfg->dma_read_buf_tsh_clr_emer = + DFLT_DMA_READ_INT_BUF_LOW(fman->state->dma_thresh_max_buf); + + fman->cfg->dma_read_buf_tsh_asrt_emer = + DFLT_DMA_READ_INT_BUF_HIGH(fman->state->dma_thresh_max_buf); + + fman->cfg->dma_write_buf_tsh_clr_emer = + DFLT_DMA_WRITE_INT_BUF_LOW(fman->state->dma_thresh_max_buf); + + fman->cfg->dma_write_buf_tsh_asrt_emer = + DFLT_DMA_WRITE_INT_BUF_HIGH(fman->state->dma_thresh_max_buf); + + fman->cfg->dma_axi_dbg_num_of_beats = + DFLT_AXI_DBG_NUM_OF_BEATS; + } + + return 0; + +err_fm_soc_specific: + kfree(fman->cfg); +err_fm_drv: + kfree(fman->state); +err_fm_state: + kfree(fman); + return -EINVAL; +} + +static int fman_init(struct fman *fman) +{ + struct fman_cfg *cfg = NULL; + int err = 0, i, count; + + if (is_init_done(fman->cfg)) + return -EINVAL; + + fman->state->count1_micro_bit = FM_TIMESTAMP_1_USEC_BIT; + + cfg = fman->cfg; + + /* clear revision-dependent non existing exception */ + if (fman->state->rev_info.major < 6) + fman->state->exceptions &= ~FMAN_EX_BMI_DISPATCH_RAM_ECC; + + if (fman->state->rev_info.major >= 6) + fman->state->exceptions &= ~FMAN_EX_QMI_SINGLE_ECC; + + /* clear CPG */ + memset_io((void __iomem *)(fman->base_addr + CGP_OFFSET), 0, + fman->state->fm_port_num_of_cg); + + /* Save LIODN info before FMan reset + * Skipping non-existent port 0 (i = 1) + */ + for (i = 1; i < FMAN_LIODN_TBL; i++) { + u32 liodn_base; + + fman->liodn_offset[i] = + ioread32be(&fman->bmi_regs->fmbm_spliodn[i - 1]); + liodn_base = ioread32be(&fman->dma_regs->fmdmplr[i / 2]); + if (i % 2) { + /* FMDM_PLR LSB holds LIODN base for odd ports */ + liodn_base &= DMA_LIODN_BASE_MASK; + } else { + /* FMDM_PLR MSB holds LIODN base for even ports */ + liodn_base >>= DMA_LIODN_SHIFT; + liodn_base &= DMA_LIODN_BASE_MASK; + } + fman->liodn_base[i] = liodn_base; + } + + /* FMan Reset (supported only for FMan V2) */ + if (fman->state->rev_info.major >= 6) { + /* Errata A007273 */ + dev_dbg(fman->dev, "%s: FManV3 reset is not supported!\n", + __func__); + } else { + iowrite32be(FPM_RSTC_FM_RESET, &fman->fpm_regs->fm_rstc); + /* Wait for reset completion */ + count = 100; + do { + udelay(1); + } while (((ioread32be(&fman->fpm_regs->fm_rstc)) & + FPM_RSTC_FM_RESET) && --count); + if (count == 0) + return -EBUSY; + } + + if (ioread32be(&fman->qmi_regs->fmqm_gs) & QMI_GS_HALT_NOT_BUSY) { + resume(fman->fpm_regs); + /* Wait until QMI is not in halt not busy state */ + count = 100; + do { + udelay(1); + } while (((ioread32be(&fman->qmi_regs->fmqm_gs)) & + QMI_GS_HALT_NOT_BUSY) && --count); + if (count == 0) + dev_warn(fman->dev, "%s: QMI is in halt not busy state\n", + __func__); + } + + if (clear_iram(fman) != 0) + return -EINVAL; + + cfg->exceptions = fman->state->exceptions; + + /* Init DMA Registers */ + + err = dma_init(fman); + if (err != 0) { + free_init_resources(fman); + return err; + } + + /* Init FPM Registers */ + fpm_init(fman->fpm_regs, fman->cfg); + + /* define common resources */ + /* allocate MURAM for FIFO according to total size */ + fman->fifo_offset = fman_muram_alloc(fman->muram, + fman->state->total_fifo_size); + if (IS_ERR_VALUE(fman->cam_offset)) { + free_init_resources(fman); + dev_err(fman->dev, "%s: MURAM alloc for BMI FIFO failed\n", + __func__); + return -ENOMEM; + } + + cfg->fifo_base_addr = fman->fifo_offset; + cfg->total_fifo_size = fman->state->total_fifo_size; + cfg->total_num_of_tasks = fman->state->total_num_of_tasks; + cfg->clk_freq = fman->state->fm_clk_freq; + + /* Init BMI Registers */ + bmi_init(fman->bmi_regs, fman->cfg); + + /* Init QMI Registers */ + qmi_init(fman->qmi_regs, fman->cfg); + + err = enable(fman, cfg); + if (err != 0) + return err; + + enable_time_stamp(fman); + + kfree(fman->cfg); + fman->cfg = NULL; + + return 0; +} + +static int fman_set_exception(struct fman *fman, + enum fman_exceptions exception, bool enable) +{ + u32 bit_mask = 0; + + if (!is_init_done(fman->cfg)) + return -EINVAL; + + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + fman->state->exceptions |= bit_mask; + else + fman->state->exceptions &= ~bit_mask; + } else { + dev_err(fman->dev, "%s: Undefined exception (%d)\n", + __func__, exception); + return -EINVAL; + } + + return set_exception(fman, exception, enable); +} + +/** + * fman_register_intr + * @fman: A Pointer to FMan device + * @mod: Calling module + * @mod_id: Module id (if more than 1 exists, '0' if not) + * @intr_type: Interrupt type (error/normal) selection. + * @f_isr: The interrupt service routine. + * @h_src_arg: Argument to be passed to f_isr. + * + * Used to register an event handler to be processed by FMan + * + * Return: 0 on success; Error code otherwise. + */ +void fman_register_intr(struct fman *fman, enum fman_event_modules module, + u8 mod_id, enum fman_intr_type intr_type, + void (*isr_cb)(void *src_arg), void *src_arg) +{ + int event = 0; + + event = get_module_event(module, mod_id, intr_type); + WARN_ON(event >= FMAN_EV_CNT); + + /* register in local FM structure */ + fman->intr_mng[event].isr_cb = isr_cb; + fman->intr_mng[event].src_handle = src_arg; +} + +/** + * fman_unregister_intr + * @fman: A Pointer to FMan device + * @mod: Calling module + * @mod_id: Module id (if more than 1 exists, '0' if not) + * @intr_type: Interrupt type (error/normal) selection. + * + * Used to unregister an event handler to be processed by FMan + * + * Return: 0 on success; Error code otherwise. + */ +void fman_unregister_intr(struct fman *fman, enum fman_event_modules module, + u8 mod_id, enum fman_intr_type intr_type) +{ + int event = 0; + + event = get_module_event(module, mod_id, intr_type); + WARN_ON(event >= FMAN_EV_CNT); + + fman->intr_mng[event].isr_cb = NULL; + fman->intr_mng[event].src_handle = NULL; +} + +/** + * fman_set_port_params + * @fman: A Pointer to FMan device + * @port_params: Port parameters + * + * Used by FMan Port to pass parameters to the FMan + * + * Return: 0 on success; Error code otherwise. + */ +int fman_set_port_params(struct fman *fman, + struct fman_port_init_params *port_params) +{ + int err; + unsigned long flags; + u8 port_id = port_params->port_id, mac_id; + + spin_lock_irqsave(&fman->spinlock, flags); + + err = set_num_of_tasks(fman, port_params->port_id, + &port_params->num_of_tasks, + &port_params->num_of_extra_tasks); + if (err) + goto return_err; + + /* TX Ports */ + if (port_params->port_type != FMAN_PORT_TYPE_RX) { + u32 enq_th, deq_th, reg; + + /* update qmi ENQ/DEQ threshold */ + fman->state->accumulated_num_of_deq_tnums += + port_params->deq_pipeline_depth; + enq_th = (ioread32be(&fman->qmi_regs->fmqm_gc) & + QMI_CFG_ENQ_MASK) >> QMI_CFG_ENQ_SHIFT; + /* if enq_th is too big, we reduce it to the max value + * that is still 0 + */ + if (enq_th >= (fman->state->qmi_max_num_of_tnums - + fman->state->accumulated_num_of_deq_tnums)) { + enq_th = + fman->state->qmi_max_num_of_tnums - + fman->state->accumulated_num_of_deq_tnums - 1; + + reg = ioread32be(&fman->qmi_regs->fmqm_gc); + reg &= ~QMI_CFG_ENQ_MASK; + reg |= (enq_th << QMI_CFG_ENQ_SHIFT); + iowrite32be(reg, &fman->qmi_regs->fmqm_gc); + } + + deq_th = ioread32be(&fman->qmi_regs->fmqm_gc) & + QMI_CFG_DEQ_MASK; + /* if deq_th is too small, we enlarge it to the min + * value that is still 0. + * depTh may not be larger than 63 + * (fman->state->qmi_max_num_of_tnums-1). + */ + if ((deq_th <= fman->state->accumulated_num_of_deq_tnums) && + (deq_th < fman->state->qmi_max_num_of_tnums - 1)) { + deq_th = fman->state->accumulated_num_of_deq_tnums + 1; + reg = ioread32be(&fman->qmi_regs->fmqm_gc); + reg &= ~QMI_CFG_DEQ_MASK; + reg |= deq_th; + iowrite32be(reg, &fman->qmi_regs->fmqm_gc); + } + } + + err = set_size_of_fifo(fman, port_params->port_id, + &port_params->size_of_fifo, + &port_params->extra_size_of_fifo); + if (err) + goto return_err; + + err = set_num_of_open_dmas(fman, port_params->port_id, + &port_params->num_of_open_dmas, + &port_params->num_of_extra_open_dmas); + if (err) + goto return_err; + + set_port_liodn(fman, port_id, fman->liodn_base[port_id], + fman->liodn_offset[port_id]); + + if (fman->state->rev_info.major < 6) + set_port_order_restoration(fman->fpm_regs, port_id); + + mac_id = hw_port_id_to_sw_port_id(fman->state->rev_info.major, port_id); + + if (port_params->max_frame_length >= fman->state->mac_mfl[mac_id]) { + fman->state->port_mfl[mac_id] = port_params->max_frame_length; + } else { + dev_warn(fman->dev, "%s: Port (%d) max_frame_length is smaller than MAC (%d) current MTU\n", + __func__, port_id, mac_id); + err = -EINVAL; + goto return_err; + } + + spin_unlock_irqrestore(&fman->spinlock, flags); + + return 0; + +return_err: + spin_unlock_irqrestore(&fman->spinlock, flags); + return err; +} + +/** + * fman_reset_mac + * @fman: A Pointer to FMan device + * @mac_id: MAC id to be reset + * + * Reset a specific MAC + * + * Return: 0 on success; Error code otherwise. + */ +int fman_reset_mac(struct fman *fman, u8 mac_id) +{ + struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs; + u32 msk, timeout = 100; + + if (fman->state->rev_info.major >= 6) { + dev_err(fman->dev, "%s: FMan MAC reset no available for FMan V3!\n", + __func__); + return -EINVAL; + } + + /* Get the relevant bit mask */ + switch (mac_id) { + case 0: + msk = FPM_RSTC_MAC0_RESET; + break; + case 1: + msk = FPM_RSTC_MAC1_RESET; + break; + case 2: + msk = FPM_RSTC_MAC2_RESET; + break; + case 3: + msk = FPM_RSTC_MAC3_RESET; + break; + case 4: + msk = FPM_RSTC_MAC4_RESET; + break; + case 5: + msk = FPM_RSTC_MAC5_RESET; + break; + case 6: + msk = FPM_RSTC_MAC6_RESET; + break; + case 7: + msk = FPM_RSTC_MAC7_RESET; + break; + case 8: + msk = FPM_RSTC_MAC8_RESET; + break; + case 9: + msk = FPM_RSTC_MAC9_RESET; + break; + default: + dev_warn(fman->dev, "%s: Illegal MAC Id [%d]\n", + __func__, mac_id); + return -EINVAL; + } + + /* reset */ + iowrite32be(msk, &fpm_rg->fm_rstc); + while ((ioread32be(&fpm_rg->fm_rstc) & msk) && --timeout) + udelay(10); + + if (!timeout) + return -EIO; + + return 0; +} + +/** + * fman_set_mac_max_frame + * @fman: A Pointer to FMan device + * @mac_id: MAC id + * @mfl: Maximum frame length + * + * Set maximum frame length of specific MAC in FMan driver + * + * Return: 0 on success; Error code otherwise. + */ +int fman_set_mac_max_frame(struct fman *fman, u8 mac_id, u16 mfl) +{ + /* if port is already initialized, check that MaxFrameLength is smaller + * or equal to the port's max + */ + if ((!fman->state->port_mfl[mac_id]) || + (fman->state->port_mfl[mac_id] && + (mfl <= fman->state->port_mfl[mac_id]))) { + fman->state->mac_mfl[mac_id] = mfl; + } else { + dev_warn(fman->dev, "%s: MAC max_frame_length is larger than Port max_frame_length\n", + __func__); + return -EINVAL; + } + return 0; +} + +/** + * fman_get_clock_freq + * @fman: A Pointer to FMan device + * + * Get FMan clock frequency + * + * Return: FMan clock frequency + */ +u16 fman_get_clock_freq(struct fman *fman) +{ + return fman->state->fm_clk_freq; +} + +/** + * fman_get_bmi_max_fifo_size + * @fman: A Pointer to FMan device + * + * Get FMan maximum FIFO size + * + * Return: FMan Maximum FIFO size + */ +u32 fman_get_bmi_max_fifo_size(struct fman *fman) +{ + return fman->state->bmi_max_fifo_size; +} + +/** + * fman_get_revision + * @fman - Pointer to the FMan module + * @rev_info - A structure of revision information parameters. + * + * Returns the FM revision + * + * Allowed only following fman_init(). + * + * Return: 0 on success; Error code otherwise. + */ +void fman_get_revision(struct fman *fman, struct fman_rev_info *rev_info) +{ + u32 tmp; + + tmp = ioread32be(&fman->fpm_regs->fm_ip_rev_1); + rev_info->major = (u8)((tmp & FPM_REV1_MAJOR_MASK) >> + FPM_REV1_MAJOR_SHIFT); + rev_info->minor = tmp & FPM_REV1_MINOR_MASK; +} + +/** + * fman_get_qman_channel_id + * @fman: A Pointer to FMan device + * @port_id: Port id + * + * Get QMan channel ID associated to the Port id + * + * Return: QMan channel ID + */ +u32 fman_get_qman_channel_id(struct fman *fman, u32 port_id) +{ + int i; + + if (fman->state->rev_info.major >= 6) { + u32 port_ids[] = {0x30, 0x31, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2, 0x3, 0x4, 0x5, 0x7, 0x7}; + for (i = 0; i < fman->state->num_of_qman_channels; i++) { + if (port_ids[i] == port_id) + break; + } + } else { + u32 port_ids[] = {0x30, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x1, + 0x2, 0x3, 0x4, 0x5, 0x7, 0x7}; + for (i = 0; i < fman->state->num_of_qman_channels; i++) { + if (port_ids[i] == port_id) + break; + } + } + + if (i == fman->state->num_of_qman_channels) + return 0; + + return fman->state->qman_channel_base + i; +} + +/** + * fman_get_mem_region + * @fman: A Pointer to FMan device + * + * Get FMan memory region + * + * Return: A structure with FMan memory region information + */ +struct resource *fman_get_mem_region(struct fman *fman) +{ + return fman->state->res; +} + +/* Bootargs defines */ +/* Extra headroom for RX buffers - Default, min and max */ +#define FSL_FM_RX_EXTRA_HEADROOM 64 +#define FSL_FM_RX_EXTRA_HEADROOM_MIN 16 +#define FSL_FM_RX_EXTRA_HEADROOM_MAX 384 + +/* Maximum frame length */ +#define FSL_FM_MAX_FRAME_SIZE 1522 +#define FSL_FM_MAX_POSSIBLE_FRAME_SIZE 9600 +#define FSL_FM_MIN_POSSIBLE_FRAME_SIZE 64 + +/* Extra headroom for Rx buffers. + * FMan is instructed to allocate, on the Rx path, this amount of + * space at the beginning of a data buffer, beside the DPA private + * data area and the IC fields. + * Does not impact Tx buffer layout. + * Configurable from bootargs. 64 by default, it's needed on + * particular forwarding scenarios that add extra headers to the + * forwarded frame. + */ +int fsl_fm_rx_extra_headroom = FSL_FM_RX_EXTRA_HEADROOM; +module_param(fsl_fm_rx_extra_headroom, int, 0); +MODULE_PARM_DESC(fsl_fm_rx_extra_headroom, "Extra headroom for Rx buffers"); + +/* Max frame size, across all interfaces. + * Configurable from bootargs, to avoid allocating oversized (socket) + * buffers when not using jumbo frames. + * Must be large enough to accommodate the network MTU, but small enough + * to avoid wasting skb memory. + * + * Could be overridden once, at boot-time, via the + * fm_set_max_frm() callback. + */ +int fsl_fm_max_frm = FSL_FM_MAX_FRAME_SIZE; +module_param(fsl_fm_max_frm, int, 0); +MODULE_PARM_DESC(fsl_fm_max_frm, "Maximum frame size, across all interfaces"); + +/** + * fman_get_max_frm + * + * Return: Max frame length configured in the FM driver + */ +u16 fman_get_max_frm(void) +{ + static bool fm_check_mfl; + + if (!fm_check_mfl) { + if (fsl_fm_max_frm > FSL_FM_MAX_POSSIBLE_FRAME_SIZE || + fsl_fm_max_frm < FSL_FM_MIN_POSSIBLE_FRAME_SIZE) { + pr_warn("Invalid fsl_fm_max_frm value (%d) in bootargs, valid range is %d-%d. Falling back to the default (%d)\n", + fsl_fm_max_frm, + FSL_FM_MIN_POSSIBLE_FRAME_SIZE, + FSL_FM_MAX_POSSIBLE_FRAME_SIZE, + FSL_FM_MAX_FRAME_SIZE); + fsl_fm_max_frm = FSL_FM_MAX_FRAME_SIZE; + } + fm_check_mfl = true; + } + + return fsl_fm_max_frm; +} +EXPORT_SYMBOL(fman_get_max_frm); + +/** + * fman_get_rx_extra_headroom + * + * Return: Extra headroom size configured in the FM driver + */ +int fman_get_rx_extra_headroom(void) +{ + static bool fm_check_rx_extra_headroom; + + if (!fm_check_rx_extra_headroom) { + if (fsl_fm_rx_extra_headroom > FSL_FM_RX_EXTRA_HEADROOM_MAX || + fsl_fm_rx_extra_headroom < FSL_FM_RX_EXTRA_HEADROOM_MIN) { + pr_warn("Invalid fsl_fm_rx_extra_headroom value (%d) in bootargs, valid range is %d-%d. Falling back to the default (%d)\n", + fsl_fm_rx_extra_headroom, + FSL_FM_RX_EXTRA_HEADROOM_MIN, + FSL_FM_RX_EXTRA_HEADROOM_MAX, + FSL_FM_RX_EXTRA_HEADROOM); + fsl_fm_rx_extra_headroom = FSL_FM_RX_EXTRA_HEADROOM; + } + + fm_check_rx_extra_headroom = true; + fsl_fm_rx_extra_headroom = ALIGN(fsl_fm_rx_extra_headroom, 16); + } + + return fsl_fm_rx_extra_headroom; +} +EXPORT_SYMBOL(fman_get_rx_extra_headroom); + +/** + * fman_bind + * @dev: FMan OF device pointer + * + * Bind to a specific FMan device. + * + * Allowed only after the port was created. + * + * Return: A pointer to the FMan device + */ +struct fman *fman_bind(struct device *fm_dev) +{ + return (struct fman *)(dev_get_drvdata(get_device(fm_dev))); +} + +static irqreturn_t fman_err_irq(int irq, void *handle) +{ + struct fman *fman = (struct fman *)handle; + u32 pending; + struct fman_fpm_regs __iomem *fpm_rg; + irqreturn_t single_ret, ret = IRQ_NONE; + + if (!is_init_done(fman->cfg)) + return IRQ_NONE; + + fpm_rg = fman->fpm_regs; + + /* error interrupts */ + pending = ioread32be(&fpm_rg->fm_epi); + if (!pending) + return IRQ_NONE; + + if (pending & ERR_INTR_EN_BMI) { + single_ret = bmi_err_event(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_QMI) { + single_ret = qmi_err_event(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_FPM) { + single_ret = fpm_err_event(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_DMA) { + single_ret = dma_err_event(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MURAM) { + single_ret = muram_err_intr(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + + /* MAC error interrupts */ + if (pending & ERR_INTR_EN_MAC0) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 0); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC1) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 1); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC2) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 2); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC3) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 3); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC4) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 4); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC5) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 5); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC6) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 6); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC7) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 7); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC8) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 8); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & ERR_INTR_EN_MAC9) { + single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 9); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + + return ret; +} + +static irqreturn_t fman_irq(int irq, void *handle) +{ + struct fman *fman = (struct fman *)handle; + u32 pending; + struct fman_fpm_regs __iomem *fpm_rg; + irqreturn_t single_ret, ret = IRQ_NONE; + + if (!is_init_done(fman->cfg)) + return IRQ_NONE; + + fpm_rg = fman->fpm_regs; + + /* normal interrupts */ + pending = ioread32be(&fpm_rg->fm_npi); + if (!pending) + return IRQ_NONE; + + if (pending & INTR_EN_QMI) { + single_ret = qmi_event(fman); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + + /* MAC interrupts */ + if (pending & INTR_EN_MAC0) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 0); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC1) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 1); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC2) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 2); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC3) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 3); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC4) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 4); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC5) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 5); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC6) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 6); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC7) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 7); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC8) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 8); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + if (pending & INTR_EN_MAC9) { + single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 9); + if (single_ret == IRQ_HANDLED) + ret = IRQ_HANDLED; + } + + return ret; +} + +static const struct of_device_id fman_muram_match[] = { + { + .compatible = "fsl,fman-muram"}, + {} +}; +MODULE_DEVICE_TABLE(of, fman_muram_match); + +static struct fman *read_dts_node(struct platform_device *of_dev) +{ + struct fman *fman; + struct device_node *fm_node, *muram_node; + struct resource *res; + const u32 *u32_prop; + int lenp, err, irq; + struct clk *clk; + u32 clk_rate; + phys_addr_t phys_base_addr; + resource_size_t mem_size; + + fman = kzalloc(sizeof(*fman), GFP_KERNEL); + if (!fman) + return NULL; + + fm_node = of_node_get(of_dev->dev.of_node); + + u32_prop = (const u32 *)of_get_property(fm_node, "cell-index", &lenp); + if (!u32_prop) { + dev_err(&of_dev->dev, "%s: of_get_property(%s, cell-index) failed\n", + __func__, fm_node->full_name); + goto fman_node_put; + } + if (WARN_ON(lenp != sizeof(u32))) + goto fman_node_put; + + fman->dts_params.id = (u8)fdt32_to_cpu(u32_prop[0]); + + /* Get the FM interrupt */ + res = platform_get_resource(of_dev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&of_dev->dev, "%s: Can't get FMan IRQ resource\n", + __func__); + goto fman_node_put; + } + irq = res->start; + + /* Get the FM error interrupt */ + res = platform_get_resource(of_dev, IORESOURCE_IRQ, 1); + if (!res) { + dev_err(&of_dev->dev, "%s: Can't get FMan Error IRQ resource\n", + __func__); + goto fman_node_put; + } + fman->dts_params.err_irq = res->start; + + /* Get the FM address */ + res = platform_get_resource(of_dev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&of_dev->dev, "%s: Can't get FMan memory resouce\n", + __func__); + goto fman_node_put; + } + + phys_base_addr = res->start; + mem_size = resource_size(res); + + clk = of_clk_get(fm_node, 0); + if (IS_ERR(clk)) { + dev_err(&of_dev->dev, "%s: Failed to get FM%d clock structure\n", + __func__, fman->dts_params.id); + goto fman_node_put; + } + + clk_rate = clk_get_rate(clk); + if (!clk_rate) { + dev_err(&of_dev->dev, "%s: Failed to determine FM%d clock rate\n", + __func__, fman->dts_params.id); + goto fman_node_put; + } + /* Rounding to MHz */ + fman->dts_params.clk_freq = DIV_ROUND_UP(clk_rate, 1000000); + + u32_prop = (const u32 *)of_get_property(fm_node, + "fsl,qman-channel-range", + &lenp); + if (!u32_prop) { + dev_err(&of_dev->dev, "%s: of_get_property(%s, fsl,qman-channel-range) failed\n", + __func__, fm_node->full_name); + goto fman_node_put; + } + if (WARN_ON(lenp != sizeof(u32) * 2)) + goto fman_node_put; + fman->dts_params.qman_channel_base = fdt32_to_cpu(u32_prop[0]); + fman->dts_params.num_of_qman_channels = fdt32_to_cpu(u32_prop[1]); + + /* Get the MURAM base address and size */ + muram_node = of_find_matching_node(fm_node, fman_muram_match); + if (!muram_node) { + dev_err(&of_dev->dev, "%s: could not find MURAM node\n", + __func__); + goto fman_node_put; + } + + err = of_address_to_resource(muram_node, 0, + &fman->dts_params.muram_res); + if (err) { + of_node_put(muram_node); + dev_err(&of_dev->dev, "%s: of_address_to_resource() = %d\n", + __func__, err); + goto fman_node_put; + } + + of_node_put(muram_node); + of_node_put(fm_node); + + err = devm_request_irq(&of_dev->dev, irq, fman_irq, 0, "fman", fman); + if (err < 0) { + dev_err(&of_dev->dev, "%s: irq %d allocation failed (error = %d)\n", + __func__, irq, err); + goto fman_free; + } + + if (fman->dts_params.err_irq != 0) { + err = devm_request_irq(&of_dev->dev, fman->dts_params.err_irq, + fman_err_irq, IRQF_SHARED, + "fman-err", fman); + if (err < 0) { + dev_err(&of_dev->dev, "%s: irq %d allocation failed (error = %d)\n", + __func__, fman->dts_params.err_irq, err); + goto fman_free; + } + } + + fman->dts_params.res = + devm_request_mem_region(&of_dev->dev, phys_base_addr, + mem_size, "fman"); + if (!fman->dts_params.res) { + dev_err(&of_dev->dev, "%s: request_mem_region() failed\n", + __func__); + goto fman_free; + } + + fman->dts_params.base_addr = + devm_ioremap(&of_dev->dev, phys_base_addr, mem_size); + if (fman->dts_params.base_addr == 0) { + dev_err(&of_dev->dev, "%s: devm_ioremap() failed\n", __func__); + goto fman_free; + } + + return fman; + +fman_node_put: + of_node_put(fm_node); +fman_free: + kfree(fman); + return NULL; +} + +static int fman_probe(struct platform_device *of_dev) +{ + struct fman *fman; + struct device *dev; + int err; + + dev = &of_dev->dev; + + fman = read_dts_node(of_dev); + if (!fman) + return -EIO; + + err = fman_config(fman); + if (err) { + dev_err(dev, "%s: FMan config failed\n", __func__); + return -EINVAL; + } + + if (fman_init(fman) != 0) { + dev_err(dev, "%s: FMan init failed\n", __func__); + return -EINVAL; + } + + if (fman->dts_params.err_irq == 0) { + fman_set_exception(fman, FMAN_EX_DMA_BUS_ERROR, false); + fman_set_exception(fman, FMAN_EX_DMA_READ_ECC, false); + fman_set_exception(fman, FMAN_EX_DMA_SYSTEM_WRITE_ECC, false); + fman_set_exception(fman, FMAN_EX_DMA_FM_WRITE_ECC, false); + fman_set_exception(fman, FMAN_EX_DMA_SINGLE_PORT_ECC, false); + fman_set_exception(fman, FMAN_EX_FPM_STALL_ON_TASKS, false); + fman_set_exception(fman, FMAN_EX_FPM_SINGLE_ECC, false); + fman_set_exception(fman, FMAN_EX_FPM_DOUBLE_ECC, false); + fman_set_exception(fman, FMAN_EX_QMI_SINGLE_ECC, false); + fman_set_exception(fman, FMAN_EX_QMI_DOUBLE_ECC, false); + fman_set_exception(fman, + FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID, false); + fman_set_exception(fman, FMAN_EX_BMI_LIST_RAM_ECC, false); + fman_set_exception(fman, FMAN_EX_BMI_STORAGE_PROFILE_ECC, + false); + fman_set_exception(fman, FMAN_EX_BMI_STATISTICS_RAM_ECC, false); + fman_set_exception(fman, FMAN_EX_BMI_DISPATCH_RAM_ECC, false); + } + + dev_set_drvdata(dev, fman); + + fman->dev = dev; + + dev_dbg(dev, "FMan%d probed\n", fman->dts_params.id); + + return 0; +} + +static const struct of_device_id fman_match[] = { + { + .compatible = "fsl,fman"}, + {} +}; + +MODULE_DEVICE_TABLE(of, fm_match); + +static struct platform_driver fman_driver = { + .driver = { + .name = "fsl-fman", + .of_match_table = fman_match, + }, + .probe = fman_probe, +}; + +builtin_platform_driver(fman_driver); diff --git a/drivers/net/ethernet/freescale/fman/fman.h b/drivers/net/ethernet/freescale/fman/fman.h new file mode 100644 index 000000000000..57aae8d17d77 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman.h @@ -0,0 +1,325 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __FM_H +#define __FM_H + +#include + +/* FM Frame descriptor macros */ +/* Frame queue Context Override */ +#define FM_FD_CMD_FCO 0x80000000 +#define FM_FD_CMD_RPD 0x40000000 /* Read Prepended Data */ +#define FM_FD_CMD_DTC 0x10000000 /* Do L4 Checksum */ + +/* TX-Port: Unsupported Format */ +#define FM_FD_ERR_UNSUPPORTED_FORMAT 0x04000000 +/* TX Port: Length Error */ +#define FM_FD_ERR_LENGTH 0x02000000 +#define FM_FD_ERR_DMA 0x01000000 /* DMA Data error */ + +/* IPR frame (not error) */ +#define FM_FD_IPR 0x00000001 +/* IPR non-consistent-sp */ +#define FM_FD_ERR_IPR_NCSP (0x00100000 | FM_FD_IPR) +/* IPR error */ +#define FM_FD_ERR_IPR (0x00200000 | FM_FD_IPR) +/* IPR timeout */ +#define FM_FD_ERR_IPR_TO (0x00300000 | FM_FD_IPR) +/* TX Port: Length Error */ +#define FM_FD_ERR_IPRE (FM_FD_ERR_IPR & ~FM_FD_IPR) + +/* Rx FIFO overflow, FCS error, code error, running disparity error + * (SGMII and TBI modes), FIFO parity error. PHY Sequence error, + * PHY error control character detected. + */ +#define FM_FD_ERR_PHYSICAL 0x00080000 +/* Frame too long OR Frame size exceeds max_length_frame */ +#define FM_FD_ERR_SIZE 0x00040000 +/* classification discard */ +#define FM_FD_ERR_CLS_DISCARD 0x00020000 +/* Extract Out of Frame */ +#define FM_FD_ERR_EXTRACTION 0x00008000 +/* No Scheme Selected */ +#define FM_FD_ERR_NO_SCHEME 0x00004000 +/* Keysize Overflow */ +#define FM_FD_ERR_KEYSIZE_OVERFLOW 0x00002000 +/* Frame color is red */ +#define FM_FD_ERR_COLOR_RED 0x00000800 +/* Frame color is yellow */ +#define FM_FD_ERR_COLOR_YELLOW 0x00000400 +/* Parser Time out Exceed */ +#define FM_FD_ERR_PRS_TIMEOUT 0x00000080 +/* Invalid Soft Parser instruction */ +#define FM_FD_ERR_PRS_ILL_INSTRUCT 0x00000040 +/* Header error was identified during parsing */ +#define FM_FD_ERR_PRS_HDR_ERR 0x00000020 +/* Frame parsed beyind 256 first bytes */ +#define FM_FD_ERR_BLOCK_LIMIT_EXCEEDED 0x00000008 + +/* non Frame-Manager error */ +#define FM_FD_RX_STATUS_ERR_NON_FM 0x00400000 + +/* FMan driver defines */ +#define FMAN_BMI_FIFO_UNITS 0x100 +#define OFFSET_UNITS 16 + +/* BMan defines */ +#define BM_MAX_NUM_OF_POOLS 64 /* Buffers pools */ +#define FMAN_PORT_MAX_EXT_POOLS_NUM 8 /* External BM pools per Rx port */ + +struct fman; /* FMan data */ + +/* Enum for defining port types */ +enum fman_port_type { + FMAN_PORT_TYPE_TX = 0, /* TX Port */ + FMAN_PORT_TYPE_RX, /* RX Port */ +}; + +struct fman_rev_info { + u8 major; /* Major revision */ + u8 minor; /* Minor revision */ +}; + +enum fman_exceptions { + FMAN_EX_DMA_BUS_ERROR = 0, /* DMA bus error. */ + FMAN_EX_DMA_READ_ECC, /* Read Buffer ECC error */ + FMAN_EX_DMA_SYSTEM_WRITE_ECC, /* Write Buffer ECC err on sys side */ + FMAN_EX_DMA_FM_WRITE_ECC, /* Write Buffer ECC error on FM side */ + FMAN_EX_DMA_SINGLE_PORT_ECC, /* Single Port ECC error on FM side */ + FMAN_EX_FPM_STALL_ON_TASKS, /* Stall of tasks on FPM */ + FMAN_EX_FPM_SINGLE_ECC, /* Single ECC on FPM. */ + FMAN_EX_FPM_DOUBLE_ECC, /* Double ECC error on FPM ram access */ + FMAN_EX_QMI_SINGLE_ECC, /* Single ECC on QMI. */ + FMAN_EX_QMI_DOUBLE_ECC, /* Double bit ECC occurred on QMI */ + FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID,/* DeQ from unknown port id */ + FMAN_EX_BMI_LIST_RAM_ECC, /* Linked List RAM ECC error */ + FMAN_EX_BMI_STORAGE_PROFILE_ECC,/* storage profile */ + FMAN_EX_BMI_STATISTICS_RAM_ECC,/* Statistics RAM ECC Err Enable */ + FMAN_EX_BMI_DISPATCH_RAM_ECC, /* Dispatch RAM ECC Error Enable */ + FMAN_EX_IRAM_ECC, /* Double bit ECC occurred on IRAM */ + FMAN_EX_MURAM_ECC /* Double bit ECC occurred on MURAM */ +}; + +/* Parse results memory layout */ +struct fman_prs_result { + u8 lpid; /* Logical port id */ + u8 shimr; /* Shim header result */ + u16 l2r; /* Layer 2 result */ + u16 l3r; /* Layer 3 result */ + u8 l4r; /* Layer 4 result */ + u8 cplan; /* Classification plan id */ + u16 nxthdr; /* Next Header */ + u16 cksum; /* Running-sum */ + /* Flags&fragment-offset field of the last IP-header */ + u16 flags_frag_off; + /* Routing type field of a IPV6 routing extension header */ + u8 route_type; + /* Routing Extension Header Present; last bit is IP valid */ + u8 rhp_ip_valid; + u8 shim_off[2]; /* Shim offset */ + u8 ip_pid_off; /* IP PID (last IP-proto) offset */ + u8 eth_off; /* ETH offset */ + u8 llc_snap_off; /* LLC_SNAP offset */ + u8 vlan_off[2]; /* VLAN offset */ + u8 etype_off; /* ETYPE offset */ + u8 pppoe_off; /* PPP offset */ + u8 mpls_off[2]; /* MPLS offset */ + u8 ip_off[2]; /* IP offset */ + u8 gre_off; /* GRE offset */ + u8 l4_off; /* Layer 4 offset */ + u8 nxthdr_off; /* Parser end point */ +}; + +/* A structure for defining buffer prefix area content. */ +struct fman_buffer_prefix_content { + /* Number of bytes to be left at the beginning of the external + * buffer; Note that the private-area will start from the base + * of the buffer address. + */ + u16 priv_data_size; + /* true to pass the parse result to/from the FM; + * User may use FM_PORT_GetBufferPrsResult() in + * order to get the parser-result from a buffer. + */ + bool pass_prs_result; + /* true to pass the timeStamp to/from the FM User */ + bool pass_time_stamp; + /* true to pass the KG hash result to/from the FM User may + * use FM_PORT_GetBufferHashResult() in order to get the + * parser-result from a buffer. + */ + bool pass_hash_result; + /* Add all other Internal-Context information: AD, + * hash-result, key, etc. + */ + u16 data_align; +}; + +/* A structure of information about each of the external + * buffer pools used by a port or storage-profile. + */ +struct fman_ext_pool_params { + u8 id; /* External buffer pool id */ + u16 size; /* External buffer pool buffer size */ +}; + +/* A structure for informing the driver about the external + * buffer pools allocated in the BM and used by a port or a + * storage-profile. + */ +struct fman_ext_pools { + u8 num_of_pools_used; /* Number of pools use by this port */ + struct fman_ext_pool_params ext_buf_pool[FMAN_PORT_MAX_EXT_POOLS_NUM]; + /* Parameters for each port */ +}; + +/* A structure for defining BM pool depletion criteria */ +struct fman_buf_pool_depletion { + /* select mode in which pause frames will be sent after a + * number of pools (all together!) are depleted + */ + bool pools_grp_mode_enable; + /* the number of depleted pools that will invoke pause + * frames transmission. + */ + u8 num_of_pools; + /* For each pool, true if it should be considered for + * depletion (Note - this pool must be used by this port!). + */ + bool pools_to_consider[BM_MAX_NUM_OF_POOLS]; + /* select mode in which pause frames will be sent + * after a single-pool is depleted; + */ + bool single_pool_mode_enable; + /* For each pool, true if it should be considered + * for depletion (Note - this pool must be used by this port!) + */ + bool pools_to_consider_for_single_mode[BM_MAX_NUM_OF_POOLS]; +}; + +/* Enum for inter-module interrupts registration */ +enum fman_event_modules { + FMAN_MOD_MAC = 0, /* MAC event */ + FMAN_MOD_FMAN_CTRL, /* FMAN Controller */ + FMAN_MOD_DUMMY_LAST +}; + +/* Enum for interrupts types */ +enum fman_intr_type { + FMAN_INTR_TYPE_ERR, + FMAN_INTR_TYPE_NORMAL +}; + +/* Enum for inter-module interrupts registration */ +enum fman_inter_module_event { + FMAN_EV_ERR_MAC0 = 0, /* MAC 0 error event */ + FMAN_EV_ERR_MAC1, /* MAC 1 error event */ + FMAN_EV_ERR_MAC2, /* MAC 2 error event */ + FMAN_EV_ERR_MAC3, /* MAC 3 error event */ + FMAN_EV_ERR_MAC4, /* MAC 4 error event */ + FMAN_EV_ERR_MAC5, /* MAC 5 error event */ + FMAN_EV_ERR_MAC6, /* MAC 6 error event */ + FMAN_EV_ERR_MAC7, /* MAC 7 error event */ + FMAN_EV_ERR_MAC8, /* MAC 8 error event */ + FMAN_EV_ERR_MAC9, /* MAC 9 error event */ + FMAN_EV_MAC0, /* MAC 0 event (Magic packet detection) */ + FMAN_EV_MAC1, /* MAC 1 event (Magic packet detection) */ + FMAN_EV_MAC2, /* MAC 2 (Magic packet detection) */ + FMAN_EV_MAC3, /* MAC 3 (Magic packet detection) */ + FMAN_EV_MAC4, /* MAC 4 (Magic packet detection) */ + FMAN_EV_MAC5, /* MAC 5 (Magic packet detection) */ + FMAN_EV_MAC6, /* MAC 6 (Magic packet detection) */ + FMAN_EV_MAC7, /* MAC 7 (Magic packet detection) */ + FMAN_EV_MAC8, /* MAC 8 event (Magic packet detection) */ + FMAN_EV_MAC9, /* MAC 9 event (Magic packet detection) */ + FMAN_EV_FMAN_CTRL_0, /* Fman controller event 0 */ + FMAN_EV_FMAN_CTRL_1, /* Fman controller event 1 */ + FMAN_EV_FMAN_CTRL_2, /* Fman controller event 2 */ + FMAN_EV_FMAN_CTRL_3, /* Fman controller event 3 */ + FMAN_EV_CNT +}; + +struct fman_intr_src { + void (*isr_cb)(void *src_arg); + void *src_handle; +}; + +/* Structure for port-FM communication during fman_port_init. */ +struct fman_port_init_params { + u8 port_id; /* port Id */ + enum fman_port_type port_type; /* Port type */ + u16 port_speed; /* Port speed */ + u16 liodn_offset; /* Port's requested resource */ + u8 num_of_tasks; /* Port's requested resource */ + u8 num_of_extra_tasks; /* Port's requested resource */ + u8 num_of_open_dmas; /* Port's requested resource */ + u8 num_of_extra_open_dmas; /* Port's requested resource */ + u32 size_of_fifo; /* Port's requested resource */ + u32 extra_size_of_fifo; /* Port's requested resource */ + u8 deq_pipeline_depth; /* Port's requested resource */ + u16 max_frame_length; /* Port's max frame length. */ + u16 liodn_base; + /* LIODN base for this port, to be used together with LIODN offset. */ +}; + +void fman_get_revision(struct fman *fman, struct fman_rev_info *rev_info); + +void fman_register_intr(struct fman *fman, enum fman_event_modules mod, + u8 mod_id, enum fman_intr_type intr_type, + void (*f_isr)(void *h_src_arg), void *h_src_arg); + +void fman_unregister_intr(struct fman *fman, enum fman_event_modules mod, + u8 mod_id, enum fman_intr_type intr_type); + +int fman_set_port_params(struct fman *fman, + struct fman_port_init_params *port_params); + +int fman_reset_mac(struct fman *fman, u8 mac_id); + +u16 fman_get_clock_freq(struct fman *fman); + +u32 fman_get_bmi_max_fifo_size(struct fman *fman); + +int fman_set_mac_max_frame(struct fman *fman, u8 mac_id, u16 mfl); + +u32 fman_get_qman_channel_id(struct fman *fman, u32 port_id); + +struct resource *fman_get_mem_region(struct fman *fman); + +u16 fman_get_max_frm(void); + +int fman_get_rx_extra_headroom(void); + +struct fman *fman_bind(struct device *dev); + +#endif /* __FM_H */ -- cgit v1.2.3-59-g8ed1b From 57ba4c9b56d898a955f95cb4091aeab28e902cac Mon Sep 17 00:00:00 2001 From: Igal Liberman Date: Mon, 21 Dec 2015 02:21:27 +0200 Subject: fsl/fman: Add FMan MAC support Add the Data Path Acceleration Architecture Frame Manger MAC support. This patch adds The FMan MAC configuration, initialization and runtime control routines. This patch contains support for these types of MACs: - dTSEC: Three speed Ethernet controller (10/100/1000 Mbps) - tGEC: 10G Ethernet controller (10 Gbps) - mEMAC: Multi-rate Ethernet MAC (10/100/1000/10000 Mbps) Different FMan revisions have different type and number of MACs. Signed-off-by: Igal Liberman Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/Makefile | 3 +- drivers/net/ethernet/freescale/fman/fman_dtsec.c | 1453 ++++++++++++++++++++++ drivers/net/ethernet/freescale/fman/fman_dtsec.h | 59 + drivers/net/ethernet/freescale/fman/fman_mac.h | 278 +++++ drivers/net/ethernet/freescale/fman/fman_memac.c | 1170 +++++++++++++++++ drivers/net/ethernet/freescale/fman/fman_memac.h | 60 + drivers/net/ethernet/freescale/fman/fman_tgec.c | 786 ++++++++++++ drivers/net/ethernet/freescale/fman/fman_tgec.h | 55 + 8 files changed, 3863 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/freescale/fman/fman_dtsec.c create mode 100644 drivers/net/ethernet/freescale/fman/fman_dtsec.h create mode 100644 drivers/net/ethernet/freescale/fman/fman_mac.h create mode 100644 drivers/net/ethernet/freescale/fman/fman_memac.c create mode 100644 drivers/net/ethernet/freescale/fman/fman_memac.h create mode 100644 drivers/net/ethernet/freescale/fman/fman_tgec.c create mode 100644 drivers/net/ethernet/freescale/fman/fman_tgec.h (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile index fb5a7f0e14ed..43360d701e1d 100644 --- a/drivers/net/ethernet/freescale/fman/Makefile +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -1,5 +1,6 @@ subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman -obj-y += fsl_fman.o +obj-y += fsl_fman.o fsl_fman_mac.o fsl_fman-objs := fman_muram.o fman.o +fsl_fman_mac-objs := fman_dtsec.o fman_memac.o fman_tgec.o diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c new file mode 100644 index 000000000000..d78e2ba73ffa --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -0,0 +1,1453 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fman_dtsec.h" +#include "fman.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* TBI register addresses */ +#define MII_TBICON 0x11 + +/* TBICON register bit fields */ +#define TBICON_SOFT_RESET 0x8000 /* Soft reset */ +#define TBICON_DISABLE_RX_DIS 0x2000 /* Disable receive disparity */ +#define TBICON_DISABLE_TX_DIS 0x1000 /* Disable transmit disparity */ +#define TBICON_AN_SENSE 0x0100 /* Auto-negotiation sense enable */ +#define TBICON_CLK_SELECT 0x0020 /* Clock select */ +#define TBICON_MI_MODE 0x0010 /* GMII mode (TBI if not set) */ + +#define TBIANA_SGMII 0x4001 +#define TBIANA_1000X 0x01a0 + +/* Interrupt Mask Register (IMASK) */ +#define DTSEC_IMASK_BREN 0x80000000 +#define DTSEC_IMASK_RXCEN 0x40000000 +#define DTSEC_IMASK_MSROEN 0x04000000 +#define DTSEC_IMASK_GTSCEN 0x02000000 +#define DTSEC_IMASK_BTEN 0x01000000 +#define DTSEC_IMASK_TXCEN 0x00800000 +#define DTSEC_IMASK_TXEEN 0x00400000 +#define DTSEC_IMASK_LCEN 0x00040000 +#define DTSEC_IMASK_CRLEN 0x00020000 +#define DTSEC_IMASK_XFUNEN 0x00010000 +#define DTSEC_IMASK_ABRTEN 0x00008000 +#define DTSEC_IMASK_IFERREN 0x00004000 +#define DTSEC_IMASK_MAGEN 0x00000800 +#define DTSEC_IMASK_MMRDEN 0x00000400 +#define DTSEC_IMASK_MMWREN 0x00000200 +#define DTSEC_IMASK_GRSCEN 0x00000100 +#define DTSEC_IMASK_TDPEEN 0x00000002 +#define DTSEC_IMASK_RDPEEN 0x00000001 + +#define DTSEC_EVENTS_MASK \ + ((u32)(DTSEC_IMASK_BREN | \ + DTSEC_IMASK_RXCEN | \ + DTSEC_IMASK_BTEN | \ + DTSEC_IMASK_TXCEN | \ + DTSEC_IMASK_TXEEN | \ + DTSEC_IMASK_ABRTEN | \ + DTSEC_IMASK_LCEN | \ + DTSEC_IMASK_CRLEN | \ + DTSEC_IMASK_XFUNEN | \ + DTSEC_IMASK_IFERREN | \ + DTSEC_IMASK_MAGEN | \ + DTSEC_IMASK_TDPEEN | \ + DTSEC_IMASK_RDPEEN)) + +/* dtsec timestamp event bits */ +#define TMR_PEMASK_TSREEN 0x00010000 +#define TMR_PEVENT_TSRE 0x00010000 + +/* Group address bit indication */ +#define MAC_GROUP_ADDRESS 0x0000010000000000ULL + +/* Defaults */ +#define DEFAULT_HALFDUP_RETRANSMIT 0xf +#define DEFAULT_HALFDUP_COLL_WINDOW 0x37 +#define DEFAULT_TX_PAUSE_TIME 0xf000 +#define DEFAULT_RX_PREPEND 0 +#define DEFAULT_PREAMBLE_LEN 7 +#define DEFAULT_TX_PAUSE_TIME_EXTD 0 +#define DEFAULT_NON_BACK_TO_BACK_IPG1 0x40 +#define DEFAULT_NON_BACK_TO_BACK_IPG2 0x60 +#define DEFAULT_MIN_IFG_ENFORCEMENT 0x50 +#define DEFAULT_BACK_TO_BACK_IPG 0x60 +#define DEFAULT_MAXIMUM_FRAME 0x600 + +/* register related defines (bits, field offsets..) */ +#define DTSEC_ID2_INT_REDUCED_OFF 0x00010000 + +#define DTSEC_ECNTRL_GMIIM 0x00000040 +#define DTSEC_ECNTRL_TBIM 0x00000020 +#define DTSEC_ECNTRL_SGMIIM 0x00000002 +#define DTSEC_ECNTRL_RPM 0x00000010 +#define DTSEC_ECNTRL_R100M 0x00000008 +#define DTSEC_ECNTRL_QSGMIIM 0x00000001 + +#define DTSEC_TCTRL_GTS 0x00000020 + +#define RCTRL_PAL_MASK 0x001f0000 +#define RCTRL_PAL_SHIFT 16 +#define RCTRL_GHTX 0x00000400 +#define RCTRL_GRS 0x00000020 +#define RCTRL_MPROM 0x00000008 +#define RCTRL_RSF 0x00000004 +#define RCTRL_UPROM 0x00000001 + +#define MACCFG1_SOFT_RESET 0x80000000 +#define MACCFG1_RX_FLOW 0x00000020 +#define MACCFG1_TX_FLOW 0x00000010 +#define MACCFG1_TX_EN 0x00000001 +#define MACCFG1_RX_EN 0x00000004 + +#define MACCFG2_NIBBLE_MODE 0x00000100 +#define MACCFG2_BYTE_MODE 0x00000200 +#define MACCFG2_PAD_CRC_EN 0x00000004 +#define MACCFG2_FULL_DUPLEX 0x00000001 +#define MACCFG2_PREAMBLE_LENGTH_MASK 0x0000f000 +#define MACCFG2_PREAMBLE_LENGTH_SHIFT 12 + +#define IPGIFG_NON_BACK_TO_BACK_IPG_1_SHIFT 24 +#define IPGIFG_NON_BACK_TO_BACK_IPG_2_SHIFT 16 +#define IPGIFG_MIN_IFG_ENFORCEMENT_SHIFT 8 + +#define IPGIFG_NON_BACK_TO_BACK_IPG_1 0x7F000000 +#define IPGIFG_NON_BACK_TO_BACK_IPG_2 0x007F0000 +#define IPGIFG_MIN_IFG_ENFORCEMENT 0x0000FF00 +#define IPGIFG_BACK_TO_BACK_IPG 0x0000007F + +#define HAFDUP_EXCESS_DEFER 0x00010000 +#define HAFDUP_COLLISION_WINDOW 0x000003ff +#define HAFDUP_RETRANSMISSION_MAX_SHIFT 12 +#define HAFDUP_RETRANSMISSION_MAX 0x0000f000 + +#define NUM_OF_HASH_REGS 8 /* Number of hash table registers */ + +#define PTV_PTE_MASK 0xffff0000 +#define PTV_PT_MASK 0x0000ffff +#define PTV_PTE_SHIFT 16 + +#define MAX_PACKET_ALIGNMENT 31 +#define MAX_INTER_PACKET_GAP 0x7f +#define MAX_RETRANSMISSION 0x0f +#define MAX_COLLISION_WINDOW 0x03ff + +/* Hash table size (32 bits*8 regs) */ +#define DTSEC_HASH_TABLE_SIZE 256 +/* Extended Hash table size (32 bits*16 regs) */ +#define EXTENDED_HASH_TABLE_SIZE 512 + +/* dTSEC Memory Map registers */ +struct dtsec_regs { + /* dTSEC General Control and Status Registers */ + u32 tsec_id; /* 0x000 ETSEC_ID register */ + u32 tsec_id2; /* 0x004 ETSEC_ID2 register */ + u32 ievent; /* 0x008 Interrupt event register */ + u32 imask; /* 0x00C Interrupt mask register */ + u32 reserved0010[1]; + u32 ecntrl; /* 0x014 E control register */ + u32 ptv; /* 0x018 Pause time value register */ + u32 tbipa; /* 0x01C TBI PHY address register */ + u32 tmr_ctrl; /* 0x020 Time-stamp Control register */ + u32 tmr_pevent; /* 0x024 Time-stamp event register */ + u32 tmr_pemask; /* 0x028 Timer event mask register */ + u32 reserved002c[5]; + u32 tctrl; /* 0x040 Transmit control register */ + u32 reserved0044[3]; + u32 rctrl; /* 0x050 Receive control register */ + u32 reserved0054[11]; + u32 igaddr[8]; /* 0x080-0x09C Individual/group address */ + u32 gaddr[8]; /* 0x0A0-0x0BC Group address registers 0-7 */ + u32 reserved00c0[16]; + u32 maccfg1; /* 0x100 MAC configuration #1 */ + u32 maccfg2; /* 0x104 MAC configuration #2 */ + u32 ipgifg; /* 0x108 IPG/IFG */ + u32 hafdup; /* 0x10C Half-duplex */ + u32 maxfrm; /* 0x110 Maximum frame */ + u32 reserved0114[10]; + u32 ifstat; /* 0x13C Interface status */ + u32 macstnaddr1; /* 0x140 Station Address,part 1 */ + u32 macstnaddr2; /* 0x144 Station Address,part 2 */ + struct { + u32 exact_match1; /* octets 1-4 */ + u32 exact_match2; /* octets 5-6 */ + } macaddr[15]; /* 0x148-0x1BC mac exact match addresses 1-15 */ + u32 reserved01c0[16]; + u32 tr64; /* 0x200 Tx and Rx 64 byte frame counter */ + u32 tr127; /* 0x204 Tx and Rx 65 to 127 byte frame counter */ + u32 tr255; /* 0x208 Tx and Rx 128 to 255 byte frame counter */ + u32 tr511; /* 0x20C Tx and Rx 256 to 511 byte frame counter */ + u32 tr1k; /* 0x210 Tx and Rx 512 to 1023 byte frame counter */ + u32 trmax; /* 0x214 Tx and Rx 1024 to 1518 byte frame counter */ + u32 trmgv; + /* 0x218 Tx and Rx 1519 to 1522 byte good VLAN frame count */ + u32 rbyt; /* 0x21C receive byte counter */ + u32 rpkt; /* 0x220 receive packet counter */ + u32 rfcs; /* 0x224 receive FCS error counter */ + u32 rmca; /* 0x228 RMCA Rx multicast packet counter */ + u32 rbca; /* 0x22C Rx broadcast packet counter */ + u32 rxcf; /* 0x230 Rx control frame packet counter */ + u32 rxpf; /* 0x234 Rx pause frame packet counter */ + u32 rxuo; /* 0x238 Rx unknown OP code counter */ + u32 raln; /* 0x23C Rx alignment error counter */ + u32 rflr; /* 0x240 Rx frame length error counter */ + u32 rcde; /* 0x244 Rx code error counter */ + u32 rcse; /* 0x248 Rx carrier sense error counter */ + u32 rund; /* 0x24C Rx undersize packet counter */ + u32 rovr; /* 0x250 Rx oversize packet counter */ + u32 rfrg; /* 0x254 Rx fragments counter */ + u32 rjbr; /* 0x258 Rx jabber counter */ + u32 rdrp; /* 0x25C Rx drop */ + u32 tbyt; /* 0x260 Tx byte counter */ + u32 tpkt; /* 0x264 Tx packet counter */ + u32 tmca; /* 0x268 Tx multicast packet counter */ + u32 tbca; /* 0x26C Tx broadcast packet counter */ + u32 txpf; /* 0x270 Tx pause control frame counter */ + u32 tdfr; /* 0x274 Tx deferral packet counter */ + u32 tedf; /* 0x278 Tx excessive deferral packet counter */ + u32 tscl; /* 0x27C Tx single collision packet counter */ + u32 tmcl; /* 0x280 Tx multiple collision packet counter */ + u32 tlcl; /* 0x284 Tx late collision packet counter */ + u32 txcl; /* 0x288 Tx excessive collision packet counter */ + u32 tncl; /* 0x28C Tx total collision counter */ + u32 reserved0290[1]; + u32 tdrp; /* 0x294 Tx drop frame counter */ + u32 tjbr; /* 0x298 Tx jabber frame counter */ + u32 tfcs; /* 0x29C Tx FCS error counter */ + u32 txcf; /* 0x2A0 Tx control frame counter */ + u32 tovr; /* 0x2A4 Tx oversize frame counter */ + u32 tund; /* 0x2A8 Tx undersize frame counter */ + u32 tfrg; /* 0x2AC Tx fragments frame counter */ + u32 car1; /* 0x2B0 carry register one register* */ + u32 car2; /* 0x2B4 carry register two register* */ + u32 cam1; /* 0x2B8 carry register one mask register */ + u32 cam2; /* 0x2BC carry register two mask register */ + u32 reserved02c0[848]; +}; + +/* struct dtsec_cfg - dTSEC configuration + * Transmit half-duplex flow control, under software control for 10/100-Mbps + * half-duplex media. If set, back pressure is applied to media by raising + * carrier. + * halfdup_retransmit: + * Number of retransmission attempts following a collision. + * If this is exceeded dTSEC aborts transmission due to excessive collisions. + * The standard specifies the attempt limit to be 15. + * halfdup_coll_window: + * The number of bytes of the frame during which collisions may occur. + * The default value of 55 corresponds to the frame byte at the end of the + * standard 512-bit slot time window. If collisions are detected after this + * byte, the late collision event is asserted and transmission of current + * frame is aborted. + * tx_pad_crc: + * Pad and append CRC. If set, the MAC pads all ransmitted short frames and + * appends a CRC to every frame regardless of padding requirement. + * tx_pause_time: + * Transmit pause time value. This pause value is used as part of the pause + * frame to be sent when a transmit pause frame is initiated. + * If set to 0 this disables transmission of pause frames. + * preamble_len: + * Length, in bytes, of the preamble field preceding each Ethernet + * start-of-frame delimiter byte. The default value of 0x7 should be used in + * order to guarantee reliable operation with IEEE 802.3 compliant hardware. + * rx_prepend: + * Packet alignment padding length. The specified number of bytes (1-31) + * of zero padding are inserted before the start of each received frame. + * For Ethernet, where optional preamble extraction is enabled, the padding + * appears before the preamble, otherwise the padding precedes the + * layer 2 header. + * + * This structure contains basic dTSEC configuration and must be passed to + * init() function. A default set of configuration values can be + * obtained by calling set_dflts(). + */ +struct dtsec_cfg { + u16 halfdup_retransmit; + u16 halfdup_coll_window; + bool tx_pad_crc; + u16 tx_pause_time; + bool ptp_tsu_en; + bool ptp_exception_en; + u32 preamble_len; + u32 rx_prepend; + u16 tx_pause_time_extd; + u16 maximum_frame; + u32 non_back_to_back_ipg1; + u32 non_back_to_back_ipg2; + u32 min_ifg_enforcement; + u32 back_to_back_ipg; +}; + +struct fman_mac { + /* pointer to dTSEC memory mapped registers */ + struct dtsec_regs __iomem *regs; + /* MAC address of device */ + u64 addr; + /* Ethernet physical interface */ + phy_interface_t phy_if; + u16 max_speed; + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *exception_cb; + fman_mac_exception_cb *event_cb; + /* Number of individual addresses in registers for this station */ + u8 num_of_ind_addr_in_regs; + /* pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + u8 mac_id; + u32 exceptions; + bool ptp_tsu_enabled; + bool en_tsu_err_exeption; + struct dtsec_cfg *dtsec_drv_param; + void *fm; + struct fman_rev_info fm_rev_info; + bool basex_if; + struct phy_device *tbiphy; +}; + +static void set_dflts(struct dtsec_cfg *cfg) +{ + cfg->halfdup_retransmit = DEFAULT_HALFDUP_RETRANSMIT; + cfg->halfdup_coll_window = DEFAULT_HALFDUP_COLL_WINDOW; + cfg->tx_pad_crc = true; + cfg->tx_pause_time = DEFAULT_TX_PAUSE_TIME; + /* PHY address 0 is reserved (DPAA RM) */ + cfg->rx_prepend = DEFAULT_RX_PREPEND; + cfg->ptp_tsu_en = true; + cfg->ptp_exception_en = true; + cfg->preamble_len = DEFAULT_PREAMBLE_LEN; + cfg->tx_pause_time_extd = DEFAULT_TX_PAUSE_TIME_EXTD; + cfg->non_back_to_back_ipg1 = DEFAULT_NON_BACK_TO_BACK_IPG1; + cfg->non_back_to_back_ipg2 = DEFAULT_NON_BACK_TO_BACK_IPG2; + cfg->min_ifg_enforcement = DEFAULT_MIN_IFG_ENFORCEMENT; + cfg->back_to_back_ipg = DEFAULT_BACK_TO_BACK_IPG; + cfg->maximum_frame = DEFAULT_MAXIMUM_FRAME; +} + +static int init(struct dtsec_regs __iomem *regs, struct dtsec_cfg *cfg, + phy_interface_t iface, u16 iface_speed, u8 *macaddr, + u32 exception_mask, u8 tbi_addr) +{ + bool is_rgmii, is_sgmii, is_qsgmii; + int i; + u32 tmp; + + /* Soft reset */ + iowrite32be(MACCFG1_SOFT_RESET, ®s->maccfg1); + iowrite32be(0, ®s->maccfg1); + + /* dtsec_id2 */ + tmp = ioread32be(®s->tsec_id2); + + /* check RGMII support */ + if (iface == PHY_INTERFACE_MODE_RGMII || + iface == PHY_INTERFACE_MODE_RMII) + if (tmp & DTSEC_ID2_INT_REDUCED_OFF) + return -EINVAL; + + if (iface == PHY_INTERFACE_MODE_SGMII || + iface == PHY_INTERFACE_MODE_MII) + if (tmp & DTSEC_ID2_INT_REDUCED_OFF) + return -EINVAL; + + is_rgmii = iface == PHY_INTERFACE_MODE_RGMII; + is_sgmii = iface == PHY_INTERFACE_MODE_SGMII; + is_qsgmii = iface == PHY_INTERFACE_MODE_QSGMII; + + tmp = 0; + if (is_rgmii || iface == PHY_INTERFACE_MODE_GMII) + tmp |= DTSEC_ECNTRL_GMIIM; + if (is_sgmii) + tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM); + if (is_qsgmii) + tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM | + DTSEC_ECNTRL_QSGMIIM); + if (is_rgmii) + tmp |= DTSEC_ECNTRL_RPM; + if (iface_speed == SPEED_100) + tmp |= DTSEC_ECNTRL_R100M; + + iowrite32be(tmp, ®s->ecntrl); + + tmp = 0; + + if (cfg->tx_pause_time) + tmp |= cfg->tx_pause_time; + if (cfg->tx_pause_time_extd) + tmp |= cfg->tx_pause_time_extd << PTV_PTE_SHIFT; + iowrite32be(tmp, ®s->ptv); + + tmp = 0; + tmp |= (cfg->rx_prepend << RCTRL_PAL_SHIFT) & RCTRL_PAL_MASK; + /* Accept short frames */ + tmp |= RCTRL_RSF; + + iowrite32be(tmp, ®s->rctrl); + + /* Assign a Phy Address to the TBI (TBIPA). + * Done also in cases where TBI is not selected to avoid conflict with + * the external PHY's Physical address + */ + iowrite32be(tbi_addr, ®s->tbipa); + + iowrite32be(0, ®s->tmr_ctrl); + + if (cfg->ptp_tsu_en) { + tmp = 0; + tmp |= TMR_PEVENT_TSRE; + iowrite32be(tmp, ®s->tmr_pevent); + + if (cfg->ptp_exception_en) { + tmp = 0; + tmp |= TMR_PEMASK_TSREEN; + iowrite32be(tmp, ®s->tmr_pemask); + } + } + + tmp = 0; + tmp |= MACCFG1_RX_FLOW; + tmp |= MACCFG1_TX_FLOW; + iowrite32be(tmp, ®s->maccfg1); + + tmp = 0; + + if (iface_speed < SPEED_1000) + tmp |= MACCFG2_NIBBLE_MODE; + else if (iface_speed == SPEED_1000) + tmp |= MACCFG2_BYTE_MODE; + + tmp |= (cfg->preamble_len << MACCFG2_PREAMBLE_LENGTH_SHIFT) & + MACCFG2_PREAMBLE_LENGTH_MASK; + if (cfg->tx_pad_crc) + tmp |= MACCFG2_PAD_CRC_EN; + /* Full Duplex */ + tmp |= MACCFG2_FULL_DUPLEX; + iowrite32be(tmp, ®s->maccfg2); + + tmp = (((cfg->non_back_to_back_ipg1 << + IPGIFG_NON_BACK_TO_BACK_IPG_1_SHIFT) + & IPGIFG_NON_BACK_TO_BACK_IPG_1) + | ((cfg->non_back_to_back_ipg2 << + IPGIFG_NON_BACK_TO_BACK_IPG_2_SHIFT) + & IPGIFG_NON_BACK_TO_BACK_IPG_2) + | ((cfg->min_ifg_enforcement << IPGIFG_MIN_IFG_ENFORCEMENT_SHIFT) + & IPGIFG_MIN_IFG_ENFORCEMENT) + | (cfg->back_to_back_ipg & IPGIFG_BACK_TO_BACK_IPG)); + iowrite32be(tmp, ®s->ipgifg); + + tmp = 0; + tmp |= HAFDUP_EXCESS_DEFER; + tmp |= ((cfg->halfdup_retransmit << HAFDUP_RETRANSMISSION_MAX_SHIFT) + & HAFDUP_RETRANSMISSION_MAX); + tmp |= (cfg->halfdup_coll_window & HAFDUP_COLLISION_WINDOW); + + iowrite32be(tmp, ®s->hafdup); + + /* Initialize Maximum frame length */ + iowrite32be(cfg->maximum_frame, ®s->maxfrm); + + iowrite32be(0xffffffff, ®s->cam1); + iowrite32be(0xffffffff, ®s->cam2); + + iowrite32be(exception_mask, ®s->imask); + + iowrite32be(0xffffffff, ®s->ievent); + + tmp = (u32)((macaddr[5] << 24) | + (macaddr[4] << 16) | (macaddr[3] << 8) | macaddr[2]); + iowrite32be(tmp, ®s->macstnaddr1); + + tmp = (u32)((macaddr[1] << 24) | (macaddr[0] << 16)); + iowrite32be(tmp, ®s->macstnaddr2); + + /* HASH */ + for (i = 0; i < NUM_OF_HASH_REGS; i++) { + /* Initialize IADDRx */ + iowrite32be(0, ®s->igaddr[i]); + /* Initialize GADDRx */ + iowrite32be(0, ®s->gaddr[i]); + } + + return 0; +} + +static void set_mac_address(struct dtsec_regs __iomem *regs, u8 *adr) +{ + u32 tmp; + + tmp = (u32)((adr[5] << 24) | + (adr[4] << 16) | (adr[3] << 8) | adr[2]); + iowrite32be(tmp, ®s->macstnaddr1); + + tmp = (u32)((adr[1] << 24) | (adr[0] << 16)); + iowrite32be(tmp, ®s->macstnaddr2); +} + +static void set_bucket(struct dtsec_regs __iomem *regs, int bucket, + bool enable) +{ + int reg_idx = (bucket >> 5) & 0xf; + int bit_idx = bucket & 0x1f; + u32 bit_mask = 0x80000000 >> bit_idx; + u32 __iomem *reg; + + if (reg_idx > 7) + reg = ®s->gaddr[reg_idx - 8]; + else + reg = ®s->igaddr[reg_idx]; + + if (enable) + iowrite32be(ioread32be(reg) | bit_mask, reg); + else + iowrite32be(ioread32be(reg) & (~bit_mask), reg); +} + +static int check_init_parameters(struct fman_mac *dtsec) +{ + if (dtsec->max_speed >= SPEED_10000) { + pr_err("1G MAC driver supports 1G or lower speeds\n"); + return -EINVAL; + } + if (dtsec->addr == 0) { + pr_err("Ethernet MAC Must have a valid MAC Address\n"); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->rx_prepend > + MAX_PACKET_ALIGNMENT) { + pr_err("packetAlignmentPadding can't be > than %d\n", + MAX_PACKET_ALIGNMENT); + return -EINVAL; + } + if (((dtsec->dtsec_drv_param)->non_back_to_back_ipg1 > + MAX_INTER_PACKET_GAP) || + ((dtsec->dtsec_drv_param)->non_back_to_back_ipg2 > + MAX_INTER_PACKET_GAP) || + ((dtsec->dtsec_drv_param)->back_to_back_ipg > + MAX_INTER_PACKET_GAP)) { + pr_err("Inter packet gap can't be greater than %d\n", + MAX_INTER_PACKET_GAP); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->halfdup_retransmit > + MAX_RETRANSMISSION) { + pr_err("maxRetransmission can't be greater than %d\n", + MAX_RETRANSMISSION); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->halfdup_coll_window > + MAX_COLLISION_WINDOW) { + pr_err("collisionWindow can't be greater than %d\n", + MAX_COLLISION_WINDOW); + return -EINVAL; + /* If Auto negotiation process is disabled, need to set up the PHY + * using the MII Management Interface + */ + } + if (!dtsec->exception_cb) { + pr_err("uninitialized exception_cb\n"); + return -EINVAL; + } + if (!dtsec->event_cb) { + pr_err("uninitialized event_cb\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fman_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_1G_BAB_RX: + bit_mask = DTSEC_IMASK_BREN; + break; + case FM_MAC_EX_1G_RX_CTL: + bit_mask = DTSEC_IMASK_RXCEN; + break; + case FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET: + bit_mask = DTSEC_IMASK_GTSCEN; + break; + case FM_MAC_EX_1G_BAB_TX: + bit_mask = DTSEC_IMASK_BTEN; + break; + case FM_MAC_EX_1G_TX_CTL: + bit_mask = DTSEC_IMASK_TXCEN; + break; + case FM_MAC_EX_1G_TX_ERR: + bit_mask = DTSEC_IMASK_TXEEN; + break; + case FM_MAC_EX_1G_LATE_COL: + bit_mask = DTSEC_IMASK_LCEN; + break; + case FM_MAC_EX_1G_COL_RET_LMT: + bit_mask = DTSEC_IMASK_CRLEN; + break; + case FM_MAC_EX_1G_TX_FIFO_UNDRN: + bit_mask = DTSEC_IMASK_XFUNEN; + break; + case FM_MAC_EX_1G_MAG_PCKT: + bit_mask = DTSEC_IMASK_MAGEN; + break; + case FM_MAC_EX_1G_MII_MNG_RD_COMPLET: + bit_mask = DTSEC_IMASK_MMRDEN; + break; + case FM_MAC_EX_1G_MII_MNG_WR_COMPLET: + bit_mask = DTSEC_IMASK_MMWREN; + break; + case FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET: + bit_mask = DTSEC_IMASK_GRSCEN; + break; + case FM_MAC_EX_1G_DATA_ERR: + bit_mask = DTSEC_IMASK_TDPEEN; + break; + case FM_MAC_EX_1G_RX_MIB_CNT_OVFL: + bit_mask = DTSEC_IMASK_MSROEN; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static bool is_init_done(struct dtsec_cfg *dtsec_drv_params) +{ + /* Checks if dTSEC driver parameters were initialized */ + if (!dtsec_drv_params) + return true; + + return false; +} + +static u16 dtsec_get_max_frame_length(struct fman_mac *dtsec) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + + if (is_init_done(dtsec->dtsec_drv_param)) + return 0; + + return (u16)ioread32be(®s->maxfrm); +} + +static void dtsec_isr(void *handle) +{ + struct fman_mac *dtsec = (struct fman_mac *)handle; + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 event; + + /* do not handle MDIO events */ + event = ioread32be(®s->ievent) & + (u32)(~(DTSEC_IMASK_MMRDEN | DTSEC_IMASK_MMWREN)); + + event &= ioread32be(®s->imask); + + iowrite32be(event, ®s->ievent); + + if (event & DTSEC_IMASK_BREN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_RX); + if (event & DTSEC_IMASK_RXCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_RX_CTL); + if (event & DTSEC_IMASK_GTSCEN) + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET); + if (event & DTSEC_IMASK_BTEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_TX); + if (event & DTSEC_IMASK_TXCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_CTL); + if (event & DTSEC_IMASK_TXEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_ERR); + if (event & DTSEC_IMASK_LCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_LATE_COL); + if (event & DTSEC_IMASK_CRLEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_COL_RET_LMT); + if (event & DTSEC_IMASK_XFUNEN) { + /* FM_TX_LOCKUP_ERRATA_DTSEC6 Errata workaround */ + if (dtsec->fm_rev_info.major == 2) { + u32 tpkt1, tmp_reg1, tpkt2, tmp_reg2, i; + /* a. Write 0x00E0_0C00 to DTSEC_ID + * This is a read only register + * b. Read and save the value of TPKT + */ + tpkt1 = ioread32be(®s->tpkt); + + /* c. Read the register at dTSEC address offset 0x32C */ + tmp_reg1 = ioread32be(®s->reserved02c0[27]); + + /* d. Compare bits [9:15] to bits [25:31] of the + * register at address offset 0x32C. + */ + if ((tmp_reg1 & 0x007F0000) != + (tmp_reg1 & 0x0000007F)) { + /* If they are not equal, save the value of + * this register and wait for at least + * MAXFRM*16 ns + */ + usleep_range((u32)(min + (dtsec_get_max_frame_length(dtsec) * + 16 / 1000, 1)), (u32) + (min(dtsec_get_max_frame_length + (dtsec) * 16 / 1000, 1) + 1)); + } + + /* e. Read and save TPKT again and read the register + * at dTSEC address offset 0x32C again + */ + tpkt2 = ioread32be(®s->tpkt); + tmp_reg2 = ioread32be(®s->reserved02c0[27]); + + /* f. Compare the value of TPKT saved in step b to + * value read in step e. Also compare bits [9:15] of + * the register at offset 0x32C saved in step d to the + * value of bits [9:15] saved in step e. If the two + * registers values are unchanged, then the transmit + * portion of the dTSEC controller is locked up and + * the user should proceed to the recover sequence. + */ + if ((tpkt1 == tpkt2) && ((tmp_reg1 & 0x007F0000) == + (tmp_reg2 & 0x007F0000))) { + /* recover sequence */ + + /* a.Write a 1 to RCTRL[GRS] */ + + iowrite32be(ioread32be(®s->rctrl) | + RCTRL_GRS, ®s->rctrl); + + /* b.Wait until IEVENT[GRSC]=1, or at least + * 100 us has elapsed. + */ + for (i = 0; i < 100; i++) { + if (ioread32be(®s->ievent) & + DTSEC_IMASK_GRSCEN) + break; + udelay(1); + } + if (ioread32be(®s->ievent) & + DTSEC_IMASK_GRSCEN) + iowrite32be(DTSEC_IMASK_GRSCEN, + ®s->ievent); + else + pr_debug("Rx lockup due to Tx lockup\n"); + + /* c.Write a 1 to bit n of FM_RSTC + * (offset 0x0CC of FPM) + */ + fman_reset_mac(dtsec->fm, dtsec->mac_id); + + /* d.Wait 4 Tx clocks (32 ns) */ + udelay(1); + + /* e.Write a 0 to bit n of FM_RSTC. */ + /* cleared by FMAN + */ + } + } + + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_FIFO_UNDRN); + } + if (event & DTSEC_IMASK_MAGEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_MAG_PCKT); + if (event & DTSEC_IMASK_GRSCEN) + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET); + if (event & DTSEC_IMASK_TDPEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_DATA_ERR); + if (event & DTSEC_IMASK_RDPEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_1G_RX_DATA_ERR); + + /* masked interrupts */ + WARN_ON(event & DTSEC_IMASK_ABRTEN); + WARN_ON(event & DTSEC_IMASK_IFERREN); +} + +static void dtsec_1588_isr(void *handle) +{ + struct fman_mac *dtsec = (struct fman_mac *)handle; + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 event; + + if (dtsec->ptp_tsu_enabled) { + event = ioread32be(®s->tmr_pevent); + event &= ioread32be(®s->tmr_pemask); + + if (event) { + iowrite32be(event, ®s->tmr_pevent); + WARN_ON(event & TMR_PEVENT_TSRE); + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_1588_TS_RX_ERR); + } + } +} + +static void free_init_resources(struct fman_mac *dtsec) +{ + fman_unregister_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_ERR); + fman_unregister_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_NORMAL); + + /* release the driver's group hash table */ + free_hash_table(dtsec->multicast_addr_hash); + dtsec->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(dtsec->unicast_addr_hash); + dtsec->unicast_addr_hash = NULL; +} + +int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val) +{ + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + dtsec->dtsec_drv_param->maximum_frame = new_val; + + return 0; +} + +int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val) +{ + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + dtsec->dtsec_drv_param->tx_pad_crc = new_val; + + return 0; +} + +int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Enable */ + tmp = ioread32be(®s->maccfg1); + if (mode & COMM_MODE_RX) + tmp |= MACCFG1_RX_EN; + if (mode & COMM_MODE_TX) + tmp |= MACCFG1_TX_EN; + + iowrite32be(tmp, ®s->maccfg1); + + /* Graceful start - clear the graceful receive stop bit */ + if (mode & COMM_MODE_TX) + iowrite32be(ioread32be(®s->tctrl) & ~DTSEC_TCTRL_GTS, + ®s->tctrl); + if (mode & COMM_MODE_RX) + iowrite32be(ioread32be(®s->rctrl) & ~RCTRL_GRS, + ®s->rctrl); + + return 0; +} + +int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Gracefull stop - Assert the graceful transmit stop bit */ + if (mode & COMM_MODE_RX) { + tmp = ioread32be(®s->rctrl) | RCTRL_GRS; + iowrite32be(tmp, ®s->rctrl); + + if (dtsec->fm_rev_info.major == 2) + usleep_range(100, 200); + else + udelay(10); + } + + if (mode & COMM_MODE_TX) { + if (dtsec->fm_rev_info.major == 2) + pr_debug("GTS not supported due to DTSEC_A004 errata.\n"); + else + pr_debug("GTS not supported due to DTSEC_A0014 errata.\n"); + } + + tmp = ioread32be(®s->maccfg1); + if (mode & COMM_MODE_RX) + tmp &= ~MACCFG1_RX_EN; + if (mode & COMM_MODE_TX) + tmp &= ~MACCFG1_TX_EN; + + iowrite32be(tmp, ®s->maccfg1); + + return 0; +} + +int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, + u8 __maybe_unused priority, + u16 pause_time, u16 __maybe_unused thresh_time) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 ptv = 0; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */ + if (dtsec->fm_rev_info.major == 2) + if (pause_time < 0 && pause_time <= 320) { + pr_warn("pause-time: %d illegal.Should be > 320\n", + pause_time); + return -EINVAL; + } + + if (pause_time) { + ptv = ioread32be(®s->ptv); + ptv &= PTV_PTE_MASK; + ptv |= pause_time & PTV_PT_MASK; + iowrite32be(ptv, ®s->ptv); + + /* trigger the transmission of a flow-control pause frame */ + iowrite32be(ioread32be(®s->maccfg1) | MACCFG1_TX_FLOW, + ®s->maccfg1); + } else + iowrite32be(ioread32be(®s->maccfg1) & ~MACCFG1_TX_FLOW, + ®s->maccfg1); + + return 0; +} + +int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->maccfg1); + if (en) + tmp |= MACCFG1_RX_FLOW; + else + tmp &= ~MACCFG1_RX_FLOW; + iowrite32be(tmp, ®s->maccfg1); + + return 0; +} + +int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr) +{ + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Initialize MAC Station Address registers (1 & 2) + * Station address have to be swapped (big endian to little endian + */ + dtsec->addr = ENET_ADDR_TO_UINT64(*enet_addr); + set_mac_address(dtsec->regs, (u8 *)(*enet_addr)); + + return 0; +} + +int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + struct eth_hash_entry *hash_entry; + u64 addr; + s32 bucket; + u32 crc = 0xFFFFFFFF; + bool mcast, ghtx; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false); + mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false); + + /* Cannot handle unicast mac addr when GHTX is on */ + if (ghtx && !mcast) { + pr_err("Could not compute hash bucket\n"); + return -EINVAL; + } + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + + /* considering the 9 highest order bits in crc H[8:0]: + *if ghtx = 0 H[8:6] (highest order 3 bits) identify the hash register + *and H[5:1] (next 5 bits) identify the hash bit + *if ghts = 1 H[8:5] (highest order 4 bits) identify the hash register + *and H[4:0] (next 5 bits) identify the hash bit. + * + *In bucket index output the low 5 bits identify the hash register + *bit, while the higher 4 bits identify the hash register + */ + + if (ghtx) { + bucket = (s32)((crc >> 23) & 0x1ff); + } else { + bucket = (s32)((crc >> 24) & 0xff); + /* if !ghtx and mcast the bit must be set in gaddr instead of + *igaddr. + */ + if (mcast) + bucket += 0x100; + } + + set_bucket(dtsec->regs, bucket, true); + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + if (addr & MAC_GROUP_ADDRESS) + /* Group Address */ + list_add_tail(&hash_entry->node, + &dtsec->multicast_addr_hash->lsts[bucket]); + else + list_add_tail(&hash_entry->node, + &dtsec->unicast_addr_hash->lsts[bucket]); + + return 0; +} + +int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + struct list_head *pos; + struct eth_hash_entry *hash_entry = NULL; + u64 addr; + s32 bucket; + u32 crc = 0xFFFFFFFF; + bool mcast, ghtx; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + ghtx = (bool)((ioread32be(®s->rctrl) & RCTRL_GHTX) ? true : false); + mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false); + + /* Cannot handle unicast mac addr when GHTX is on */ + if (ghtx && !mcast) { + pr_err("Could not compute hash bucket\n"); + return -EINVAL; + } + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + + if (ghtx) { + bucket = (s32)((crc >> 23) & 0x1ff); + } else { + bucket = (s32)((crc >> 24) & 0xff); + /* if !ghtx and mcast the bit must be set + * in gaddr instead of igaddr. + */ + if (mcast) + bucket += 0x100; + } + + if (addr & MAC_GROUP_ADDRESS) { + /* Group Address */ + list_for_each(pos, + &dtsec->multicast_addr_hash->lsts[bucket]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&dtsec->multicast_addr_hash->lsts[bucket])) + set_bucket(dtsec->regs, bucket, false); + } else { + /* Individual Address */ + list_for_each(pos, + &dtsec->unicast_addr_hash->lsts[bucket]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&dtsec->unicast_addr_hash->lsts[bucket])) + set_bucket(dtsec->regs, bucket, false); + } + + /* address does not exist */ + WARN_ON(!hash_entry); + + return 0; +} + +int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Set unicast promiscuous */ + tmp = ioread32be(®s->rctrl); + if (new_val) + tmp |= RCTRL_UPROM; + else + tmp &= ~RCTRL_UPROM; + + iowrite32be(tmp, ®s->rctrl); + + /* Set multicast promiscuous */ + tmp = ioread32be(®s->rctrl); + if (new_val) + tmp |= RCTRL_MPROM; + else + tmp &= ~RCTRL_MPROM; + + iowrite32be(tmp, ®s->rctrl); + + return 0; +} + +int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->maccfg2); + + /* Full Duplex */ + tmp |= MACCFG2_FULL_DUPLEX; + + tmp &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE); + if (speed < SPEED_1000) + tmp |= MACCFG2_NIBBLE_MODE; + else if (speed == SPEED_1000) + tmp |= MACCFG2_BYTE_MODE; + iowrite32be(tmp, ®s->maccfg2); + + tmp = ioread32be(®s->ecntrl); + if (speed == SPEED_100) + tmp |= DTSEC_ECNTRL_R100M; + else + tmp &= ~DTSEC_ECNTRL_R100M; + iowrite32be(tmp, ®s->ecntrl); + + return 0; +} + +int dtsec_restart_autoneg(struct fman_mac *dtsec) +{ + u16 tmp_reg16; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + tmp_reg16 = phy_read(dtsec->tbiphy, MII_BMCR); + + tmp_reg16 &= ~(BMCR_SPEED100 | BMCR_SPEED1000); + tmp_reg16 |= (BMCR_ANENABLE | BMCR_ANRESTART | + BMCR_FULLDPLX | BMCR_SPEED1000); + + phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); + + return 0; +} + +int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + *mac_version = ioread32be(®s->tsec_id); + + return 0; +} + +int dtsec_set_exception(struct fman_mac *dtsec, + enum fman_mac_exceptions exception, bool enable) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 bit_mask = 0; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if (exception != FM_MAC_EX_1G_1588_TS_RX_ERR) { + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + dtsec->exceptions |= bit_mask; + else + dtsec->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + if (enable) + iowrite32be(ioread32be(®s->imask) | bit_mask, + ®s->imask); + else + iowrite32be(ioread32be(®s->imask) & ~bit_mask, + ®s->imask); + } else { + if (!dtsec->ptp_tsu_enabled) { + pr_err("Exception valid for 1588 only\n"); + return -EINVAL; + } + switch (exception) { + case FM_MAC_EX_1G_1588_TS_RX_ERR: + if (enable) { + dtsec->en_tsu_err_exeption = true; + iowrite32be(ioread32be(®s->tmr_pemask) | + TMR_PEMASK_TSREEN, + ®s->tmr_pemask); + } else { + dtsec->en_tsu_err_exeption = false; + iowrite32be(ioread32be(®s->tmr_pemask) & + ~TMR_PEMASK_TSREEN, + ®s->tmr_pemask); + } + break; + default: + pr_err("Undefined exception\n"); + return -EINVAL; + } + } + + return 0; +} + +int dtsec_init(struct fman_mac *dtsec) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + struct dtsec_cfg *dtsec_drv_param; + int err; + u16 max_frm_ln; + enet_addr_t eth_addr; + + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if (DEFAULT_RESET_ON_INIT && + (fman_reset_mac(dtsec->fm, dtsec->mac_id) != 0)) { + pr_err("Can't reset MAC!\n"); + return -EINVAL; + } + + err = check_init_parameters(dtsec); + if (err) + return err; + + dtsec_drv_param = dtsec->dtsec_drv_param; + + MAKE_ENET_ADDR_FROM_UINT64(dtsec->addr, eth_addr); + + err = init(dtsec->regs, dtsec_drv_param, dtsec->phy_if, + dtsec->max_speed, (u8 *)eth_addr, dtsec->exceptions, + dtsec->tbiphy->addr); + if (err) { + free_init_resources(dtsec); + pr_err("DTSEC version doesn't support this i/f mode\n"); + return err; + } + + if (dtsec->phy_if == PHY_INTERFACE_MODE_SGMII) { + u16 tmp_reg16; + + /* Configure the TBI PHY Control Register */ + tmp_reg16 = TBICON_CLK_SELECT | TBICON_SOFT_RESET; + phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16); + + tmp_reg16 = TBICON_CLK_SELECT; + phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16); + + tmp_reg16 = (BMCR_RESET | BMCR_ANENABLE | + BMCR_FULLDPLX | BMCR_SPEED1000); + phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); + + if (dtsec->basex_if) + tmp_reg16 = TBIANA_1000X; + else + tmp_reg16 = TBIANA_SGMII; + phy_write(dtsec->tbiphy, MII_ADVERTISE, tmp_reg16); + + tmp_reg16 = (BMCR_ANENABLE | BMCR_ANRESTART | + BMCR_FULLDPLX | BMCR_SPEED1000); + + phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16); + } + + /* Max Frame Length */ + max_frm_ln = (u16)ioread32be(®s->maxfrm); + err = fman_set_mac_max_frame(dtsec->fm, dtsec->mac_id, max_frm_ln); + if (err) { + pr_err("Setting max frame length failed\n"); + free_init_resources(dtsec); + return -EINVAL; + } + + dtsec->multicast_addr_hash = + alloc_hash_table(EXTENDED_HASH_TABLE_SIZE); + if (!dtsec->multicast_addr_hash) { + free_init_resources(dtsec); + pr_err("MC hash table is failed\n"); + return -ENOMEM; + } + + dtsec->unicast_addr_hash = alloc_hash_table(DTSEC_HASH_TABLE_SIZE); + if (!dtsec->unicast_addr_hash) { + free_init_resources(dtsec); + pr_err("UC hash table is failed\n"); + return -ENOMEM; + } + + /* register err intr handler for dtsec to FPM (err) */ + fman_register_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_ERR, dtsec_isr, dtsec); + /* register 1588 intr handler for TMR to FPM (normal) */ + fman_register_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id, + FMAN_INTR_TYPE_NORMAL, dtsec_1588_isr, dtsec); + + kfree(dtsec_drv_param); + dtsec->dtsec_drv_param = NULL; + + return 0; +} + +int dtsec_free(struct fman_mac *dtsec) +{ + free_init_resources(dtsec); + + kfree(dtsec->dtsec_drv_param); + dtsec->dtsec_drv_param = NULL; + kfree(dtsec); + + return 0; +} + +struct fman_mac *dtsec_config(struct fman_mac_params *params) +{ + struct fman_mac *dtsec; + struct dtsec_cfg *dtsec_drv_param; + void __iomem *base_addr; + + base_addr = params->base_addr; + + /* allocate memory for the UCC GETH data structure. */ + dtsec = kzalloc(sizeof(*dtsec), GFP_KERNEL); + if (!dtsec) + return NULL; + + /* allocate memory for the d_tsec driver parameters data structure. */ + dtsec_drv_param = kzalloc(sizeof(*dtsec_drv_param), GFP_KERNEL); + if (!dtsec_drv_param) + goto err_dtsec; + + /* Plant parameter structure pointer */ + dtsec->dtsec_drv_param = dtsec_drv_param; + + set_dflts(dtsec_drv_param); + + dtsec->regs = base_addr; + dtsec->addr = ENET_ADDR_TO_UINT64(params->addr); + dtsec->max_speed = params->max_speed; + dtsec->phy_if = params->phy_if; + dtsec->mac_id = params->mac_id; + dtsec->exceptions = (DTSEC_IMASK_BREN | + DTSEC_IMASK_RXCEN | + DTSEC_IMASK_BTEN | + DTSEC_IMASK_TXCEN | + DTSEC_IMASK_TXEEN | + DTSEC_IMASK_ABRTEN | + DTSEC_IMASK_LCEN | + DTSEC_IMASK_CRLEN | + DTSEC_IMASK_XFUNEN | + DTSEC_IMASK_IFERREN | + DTSEC_IMASK_MAGEN | + DTSEC_IMASK_TDPEEN | + DTSEC_IMASK_RDPEEN); + dtsec->exception_cb = params->exception_cb; + dtsec->event_cb = params->event_cb; + dtsec->dev_id = params->dev_id; + dtsec->ptp_tsu_enabled = dtsec->dtsec_drv_param->ptp_tsu_en; + dtsec->en_tsu_err_exeption = dtsec->dtsec_drv_param->ptp_exception_en; + + dtsec->fm = params->fm; + dtsec->basex_if = params->basex_if; + + if (!params->internal_phy_node) { + pr_err("TBI PHY node is not available\n"); + goto err_dtsec_drv_param; + } + + dtsec->tbiphy = of_phy_find_device(params->internal_phy_node); + if (!dtsec->tbiphy) { + pr_err("of_phy_find_device (TBI PHY) failed\n"); + put_device(&dtsec->tbiphy->dev); + goto err_dtsec_drv_param; + } + + put_device(&dtsec->tbiphy->dev); + + /* Save FMan revision */ + fman_get_revision(dtsec->fm, &dtsec->fm_rev_info); + + return dtsec; + +err_dtsec_drv_param: + kfree(dtsec_drv_param); +err_dtsec: + kfree(dtsec); + return NULL; +} diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.h b/drivers/net/ethernet/freescale/fman/fman_dtsec.h new file mode 100644 index 000000000000..c4467c072058 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.h @@ -0,0 +1,59 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DTSEC_H +#define __DTSEC_H + +#include "fman_mac.h" + +struct fman_mac *dtsec_config(struct fman_mac_params *params); +int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val); +int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr); +int dtsec_adjust_link(struct fman_mac *dtsec, + u16 speed); +int dtsec_restart_autoneg(struct fman_mac *dtsec); +int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val); +int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val); +int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode); +int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode); +int dtsec_init(struct fman_mac *dtsec); +int dtsec_free(struct fman_mac *dtsec); +int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en); +int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, u8 priority, + u16 pause_time, u16 thresh_time); +int dtsec_set_exception(struct fman_mac *dtsec, + enum fman_mac_exceptions exception, bool enable); +int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr); +int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr); +int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version); + +#endif /* __DTSEC_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_mac.h b/drivers/net/ethernet/freescale/fman/fman_mac.h new file mode 100644 index 000000000000..8ddeedbcef9c --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_mac.h @@ -0,0 +1,278 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* FM MAC ... */ +#ifndef __FM_MAC_H +#define __FM_MAC_H + +#include "fman.h" + +#include +#include +#include + +struct fman_mac; + +/* Ethernet Address */ +typedef u8 enet_addr_t[ETH_ALEN]; + +#define ENET_ADDR_TO_UINT64(_enet_addr) \ + (u64)(((u64)(_enet_addr)[0] << 40) | \ + ((u64)(_enet_addr)[1] << 32) | \ + ((u64)(_enet_addr)[2] << 24) | \ + ((u64)(_enet_addr)[3] << 16) | \ + ((u64)(_enet_addr)[4] << 8) | \ + ((u64)(_enet_addr)[5])) + +#define MAKE_ENET_ADDR_FROM_UINT64(_addr64, _enet_addr) \ + do { \ + int i; \ + for (i = 0; i < ETH_ALEN; i++) \ + (_enet_addr)[i] = \ + (u8)((_addr64) >> ((5 - i) * 8)); \ + } while (0) + +/* defaults */ +#define DEFAULT_RESET_ON_INIT false + +/* PFC defines */ +#define FSL_FM_PAUSE_TIME_ENABLE 0xf000 +#define FSL_FM_PAUSE_TIME_DISABLE 0 +#define FSL_FM_PAUSE_THRESH_DEFAULT 0 + +#define FM_MAC_NO_PFC 0xff + +/* HASH defines */ +#define ETH_HASH_ENTRY_OBJ(ptr) \ + hlist_entry_safe(ptr, struct eth_hash_entry, node) + +/* Enumeration (bit flags) of communication modes (Transmit, + * receive or both). + */ +enum comm_mode { + COMM_MODE_NONE = 0, /* No transmit/receive communication */ + COMM_MODE_RX = 1, /* Only receive communication */ + COMM_MODE_TX = 2, /* Only transmit communication */ + COMM_MODE_RX_AND_TX = 3 /* Both transmit and receive communication */ +}; + +/* FM MAC Exceptions */ +enum fman_mac_exceptions { + FM_MAC_EX_10G_MDIO_SCAN_EVENT = 0 + /* 10GEC MDIO scan event interrupt */ + , FM_MAC_EX_10G_MDIO_CMD_CMPL + /* 10GEC MDIO command completion interrupt */ + , FM_MAC_EX_10G_REM_FAULT + /* 10GEC, mEMAC Remote fault interrupt */ + , FM_MAC_EX_10G_LOC_FAULT + /* 10GEC, mEMAC Local fault interrupt */ + , FM_MAC_EX_10G_TX_ECC_ER + /* 10GEC, mEMAC Transmit frame ECC error interrupt */ + , FM_MAC_EX_10G_TX_FIFO_UNFL + /* 10GEC, mEMAC Transmit FIFO underflow interrupt */ + , FM_MAC_EX_10G_TX_FIFO_OVFL + /* 10GEC, mEMAC Transmit FIFO overflow interrupt */ + , FM_MAC_EX_10G_TX_ER + /* 10GEC Transmit frame error interrupt */ + , FM_MAC_EX_10G_RX_FIFO_OVFL + /* 10GEC, mEMAC Receive FIFO overflow interrupt */ + , FM_MAC_EX_10G_RX_ECC_ER + /* 10GEC, mEMAC Receive frame ECC error interrupt */ + , FM_MAC_EX_10G_RX_JAB_FRM + /* 10GEC Receive jabber frame interrupt */ + , FM_MAC_EX_10G_RX_OVRSZ_FRM + /* 10GEC Receive oversized frame interrupt */ + , FM_MAC_EX_10G_RX_RUNT_FRM + /* 10GEC Receive runt frame interrupt */ + , FM_MAC_EX_10G_RX_FRAG_FRM + /* 10GEC Receive fragment frame interrupt */ + , FM_MAC_EX_10G_RX_LEN_ER + /* 10GEC Receive payload length error interrupt */ + , FM_MAC_EX_10G_RX_CRC_ER + /* 10GEC Receive CRC error interrupt */ + , FM_MAC_EX_10G_RX_ALIGN_ER + /* 10GEC Receive alignment error interrupt */ + , FM_MAC_EX_1G_BAB_RX + /* dTSEC Babbling receive error */ + , FM_MAC_EX_1G_RX_CTL + /* dTSEC Receive control (pause frame) interrupt */ + , FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET + /* dTSEC Graceful transmit stop complete */ + , FM_MAC_EX_1G_BAB_TX + /* dTSEC Babbling transmit error */ + , FM_MAC_EX_1G_TX_CTL + /* dTSEC Transmit control (pause frame) interrupt */ + , FM_MAC_EX_1G_TX_ERR + /* dTSEC Transmit error */ + , FM_MAC_EX_1G_LATE_COL + /* dTSEC Late collision */ + , FM_MAC_EX_1G_COL_RET_LMT + /* dTSEC Collision retry limit */ + , FM_MAC_EX_1G_TX_FIFO_UNDRN + /* dTSEC Transmit FIFO underrun */ + , FM_MAC_EX_1G_MAG_PCKT + /* dTSEC Magic Packet detection */ + , FM_MAC_EX_1G_MII_MNG_RD_COMPLET + /* dTSEC MII management read completion */ + , FM_MAC_EX_1G_MII_MNG_WR_COMPLET + /* dTSEC MII management write completion */ + , FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET + /* dTSEC Graceful receive stop complete */ + , FM_MAC_EX_1G_DATA_ERR + /* dTSEC Internal data error on transmit */ + , FM_MAC_1G_RX_DATA_ERR + /* dTSEC Internal data error on receive */ + , FM_MAC_EX_1G_1588_TS_RX_ERR + /* dTSEC Time-Stamp Receive Error */ + , FM_MAC_EX_1G_RX_MIB_CNT_OVFL + /* dTSEC MIB counter overflow */ + , FM_MAC_EX_TS_FIFO_ECC_ERR + /* mEMAC Time-stamp FIFO ECC error interrupt; + * not supported on T4240/B4860 rev1 chips + */ + , FM_MAC_EX_MAGIC_PACKET_INDICATION = FM_MAC_EX_1G_MAG_PCKT + /* mEMAC Magic Packet Indication Interrupt */ +}; + +struct eth_hash_entry { + u64 addr; /* Ethernet Address */ + struct list_head node; +}; + +typedef void (fman_mac_exception_cb)(void *dev_id, + enum fman_mac_exceptions exceptions); + +/* FMan MAC config input */ +struct fman_mac_params { + /* Base of memory mapped FM MAC registers */ + void __iomem *base_addr; + /* MAC address of device; First octet is sent first */ + enet_addr_t addr; + /* MAC ID; numbering of dTSEC and 1G-mEMAC: + * 0 - FM_MAX_NUM_OF_1G_MACS; + * numbering of 10G-MAC (TGEC) and 10G-mEMAC: + * 0 - FM_MAX_NUM_OF_10G_MACS + */ + u8 mac_id; + /* PHY interface */ + phy_interface_t phy_if; + /* Note that the speed should indicate the maximum rate that + * this MAC should support rather than the actual speed; + */ + u16 max_speed; + /* A handle to the FM object this port related to */ + void *fm; + /* MDIO exceptions interrupt source - not valid for all + * MACs; MUST be set to 'NO_IRQ' for MACs that don't have + * mdio-irq, or for polling + */ + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *event_cb; /* MDIO Events Callback Routine */ + fman_mac_exception_cb *exception_cb;/* Exception Callback Routine */ + /* SGMII/QSGII interface with 1000BaseX auto-negotiation between MAC + * and phy or backplane; Note: 1000BaseX auto-negotiation relates only + * to interface between MAC and phy/backplane, SGMII phy can still + * synchronize with far-end phy at 10Mbps, 100Mbps or 1000Mbps + */ + bool basex_if; + /* Pointer to TBI/PCS PHY node, used for TBI/PCS PHY access */ + struct device_node *internal_phy_node; +}; + +struct eth_hash_t { + u16 size; + struct list_head *lsts; +}; + +static inline struct eth_hash_entry +*dequeue_addr_from_hash_entry(struct list_head *addr_lst) +{ + struct eth_hash_entry *hash_entry = NULL; + + if (!list_empty(addr_lst)) { + hash_entry = ETH_HASH_ENTRY_OBJ(addr_lst->next); + list_del_init(&hash_entry->node); + } + return hash_entry; +} + +static inline void free_hash_table(struct eth_hash_t *hash) +{ + struct eth_hash_entry *hash_entry; + int i = 0; + + if (hash) { + if (hash->lsts) { + for (i = 0; i < hash->size; i++) { + hash_entry = + dequeue_addr_from_hash_entry(&hash->lsts[i]); + while (hash_entry) { + kfree(hash_entry); + hash_entry = + dequeue_addr_from_hash_entry(&hash-> + lsts[i]); + } + } + + kfree(hash->lsts); + } + + kfree(hash); + } +} + +static inline struct eth_hash_t *alloc_hash_table(u16 size) +{ + u32 i; + struct eth_hash_t *hash; + + /* Allocate address hash table */ + hash = kmalloc_array(size, sizeof(struct eth_hash_t *), GFP_KERNEL); + if (!hash) + return NULL; + + hash->size = size; + + hash->lsts = kmalloc_array(hash->size, sizeof(struct list_head), + GFP_KERNEL); + if (!hash->lsts) { + kfree(hash); + return NULL; + } + + for (i = 0; i < hash->size; i++) + INIT_LIST_HEAD(&hash->lsts[i]); + + return hash; +} + +#endif /* __FM_MAC_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c new file mode 100644 index 000000000000..58bb72071c14 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -0,0 +1,1170 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fman_memac.h" +#include "fman.h" + +#include +#include +#include +#include + +/* PCS registers */ +#define MDIO_SGMII_CR 0x00 +#define MDIO_SGMII_DEV_ABIL_SGMII 0x04 +#define MDIO_SGMII_LINK_TMR_L 0x12 +#define MDIO_SGMII_LINK_TMR_H 0x13 +#define MDIO_SGMII_IF_MODE 0x14 + +/* SGMII Control defines */ +#define SGMII_CR_AN_EN 0x1000 +#define SGMII_CR_RESTART_AN 0x0200 +#define SGMII_CR_FD 0x0100 +#define SGMII_CR_SPEED_SEL1_1G 0x0040 +#define SGMII_CR_DEF_VAL (SGMII_CR_AN_EN | SGMII_CR_FD | \ + SGMII_CR_SPEED_SEL1_1G) + +/* SGMII Device Ability for SGMII defines */ +#define MDIO_SGMII_DEV_ABIL_SGMII_MODE 0x4001 +#define MDIO_SGMII_DEV_ABIL_BASEX_MODE 0x01A0 + +/* Link timer define */ +#define LINK_TMR_L 0xa120 +#define LINK_TMR_H 0x0007 +#define LINK_TMR_L_BASEX 0xaf08 +#define LINK_TMR_H_BASEX 0x002f + +/* SGMII IF Mode defines */ +#define IF_MODE_USE_SGMII_AN 0x0002 +#define IF_MODE_SGMII_EN 0x0001 +#define IF_MODE_SGMII_SPEED_100M 0x0004 +#define IF_MODE_SGMII_SPEED_1G 0x0008 +#define IF_MODE_SGMII_DUPLEX_HALF 0x0010 + +/* Num of additional exact match MAC adr regs */ +#define MEMAC_NUM_OF_PADDRS 7 + +/* Control and Configuration Register (COMMAND_CONFIG) */ +#define CMD_CFG_REG_LOWP_RXETY 0x01000000 /* 07 Rx low power indication */ +#define CMD_CFG_TX_LOWP_ENA 0x00800000 /* 08 Tx Low Power Idle Enable */ +#define CMD_CFG_PFC_MODE 0x00080000 /* 12 Enable PFC */ +#define CMD_CFG_NO_LEN_CHK 0x00020000 /* 14 Payload length check disable */ +#define CMD_CFG_SW_RESET 0x00001000 /* 19 S/W Reset, self clearing bit */ +#define CMD_CFG_TX_PAD_EN 0x00000800 /* 20 Enable Tx padding of frames */ +#define CMD_CFG_PAUSE_IGNORE 0x00000100 /* 23 Ignore Pause frame quanta */ +#define CMD_CFG_CRC_FWD 0x00000040 /* 25 Terminate/frwd CRC of frames */ +#define CMD_CFG_PAD_EN 0x00000020 /* 26 Frame padding removal */ +#define CMD_CFG_PROMIS_EN 0x00000010 /* 27 Promiscuous operation enable */ +#define CMD_CFG_RX_EN 0x00000002 /* 30 MAC receive path enable */ +#define CMD_CFG_TX_EN 0x00000001 /* 31 MAC transmit path enable */ + +/* Transmit FIFO Sections Register (TX_FIFO_SECTIONS) */ +#define TX_FIFO_SECTIONS_TX_EMPTY_MASK 0xFFFF0000 +#define TX_FIFO_SECTIONS_TX_AVAIL_MASK 0x0000FFFF +#define TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G 0x00400000 +#define TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G 0x00100000 +#define TX_FIFO_SECTIONS_TX_AVAIL_10G 0x00000019 +#define TX_FIFO_SECTIONS_TX_AVAIL_1G 0x00000020 +#define TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G 0x00000060 + +#define GET_TX_EMPTY_DEFAULT_VALUE(_val) \ +do { \ + _val &= ~TX_FIFO_SECTIONS_TX_EMPTY_MASK; \ + ((_val == TX_FIFO_SECTIONS_TX_AVAIL_10G) ? \ + (_val |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G) :\ + (_val |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G));\ +} while (0) + +/* Interface Mode Register (IF_MODE) */ + +#define IF_MODE_MASK 0x00000003 /* 30-31 Mask on i/f mode bits */ +#define IF_MODE_XGMII 0x00000000 /* 30-31 XGMII (10G) interface */ +#define IF_MODE_GMII 0x00000002 /* 30-31 GMII (1G) interface */ +#define IF_MODE_RGMII 0x00000004 +#define IF_MODE_RGMII_AUTO 0x00008000 +#define IF_MODE_RGMII_1000 0x00004000 /* 10 - 1000Mbps RGMII */ +#define IF_MODE_RGMII_100 0x00000000 /* 00 - 100Mbps RGMII */ +#define IF_MODE_RGMII_10 0x00002000 /* 01 - 10Mbps RGMII */ +#define IF_MODE_RGMII_SP_MASK 0x00006000 /* Setsp mask bits */ +#define IF_MODE_RGMII_FD 0x00001000 /* Full duplex RGMII */ +#define IF_MODE_HD 0x00000040 /* Half duplex operation */ + +/* Hash table Control Register (HASHTABLE_CTRL) */ +#define HASH_CTRL_MCAST_EN 0x00000100 +/* 26-31 Hash table address code */ +#define HASH_CTRL_ADDR_MASK 0x0000003F +/* MAC mcast indication */ +#define GROUP_ADDRESS 0x0000010000000000LL +#define HASH_TABLE_SIZE 64 /* Hash tbl size */ + +/* Interrupt Mask Register (IMASK) */ +#define MEMAC_IMASK_MGI 0x40000000 /* 1 Magic pkt detect indication */ +#define MEMAC_IMASK_TSECC_ER 0x20000000 /* 2 Timestamp FIFO ECC error evnt */ +#define MEMAC_IMASK_TECC_ER 0x02000000 /* 6 Transmit frame ECC error evnt */ +#define MEMAC_IMASK_RECC_ER 0x01000000 /* 7 Receive frame ECC error evnt */ + +#define MEMAC_ALL_ERRS_IMASK \ + ((u32)(MEMAC_IMASK_TSECC_ER | \ + MEMAC_IMASK_TECC_ER | \ + MEMAC_IMASK_RECC_ER | \ + MEMAC_IMASK_MGI)) + +#define MEMAC_IEVNT_PCS 0x80000000 /* PCS (XG). Link sync (G) */ +#define MEMAC_IEVNT_AN 0x40000000 /* Auto-negotiation */ +#define MEMAC_IEVNT_LT 0x20000000 /* Link Training/New page */ +#define MEMAC_IEVNT_MGI 0x00004000 /* Magic pkt detection */ +#define MEMAC_IEVNT_TS_ECC_ER 0x00002000 /* Timestamp FIFO ECC error*/ +#define MEMAC_IEVNT_RX_FIFO_OVFL 0x00001000 /* Rx FIFO overflow */ +#define MEMAC_IEVNT_TX_FIFO_UNFL 0x00000800 /* Tx FIFO underflow */ +#define MEMAC_IEVNT_TX_FIFO_OVFL 0x00000400 /* Tx FIFO overflow */ +#define MEMAC_IEVNT_TX_ECC_ER 0x00000200 /* Tx frame ECC error */ +#define MEMAC_IEVNT_RX_ECC_ER 0x00000100 /* Rx frame ECC error */ +#define MEMAC_IEVNT_LI_FAULT 0x00000080 /* Link Interruption flt */ +#define MEMAC_IEVNT_RX_EMPTY 0x00000040 /* Rx FIFO empty */ +#define MEMAC_IEVNT_TX_EMPTY 0x00000020 /* Tx FIFO empty */ +#define MEMAC_IEVNT_RX_LOWP 0x00000010 /* Low Power Idle */ +#define MEMAC_IEVNT_PHY_LOS 0x00000004 /* Phy loss of signal */ +#define MEMAC_IEVNT_REM_FAULT 0x00000002 /* Remote fault (XGMII) */ +#define MEMAC_IEVNT_LOC_FAULT 0x00000001 /* Local fault (XGMII) */ + +#define DEFAULT_PAUSE_QUANTA 0xf000 +#define DEFAULT_FRAME_LENGTH 0x600 +#define DEFAULT_TX_IPG_LENGTH 12 + +#define CLXY_PAUSE_QUANTA_CLX_PQNT 0x0000FFFF +#define CLXY_PAUSE_QUANTA_CLY_PQNT 0xFFFF0000 +#define CLXY_PAUSE_THRESH_CLX_QTH 0x0000FFFF +#define CLXY_PAUSE_THRESH_CLY_QTH 0xFFFF0000 + +struct mac_addr { + /* Lower 32 bits of 48-bit MAC address */ + u32 mac_addr_l; + /* Upper 16 bits of 48-bit MAC address */ + u32 mac_addr_u; +}; + +/* memory map */ +struct memac_regs { + u32 res0000[2]; /* General Control and Status */ + u32 command_config; /* 0x008 Ctrl and cfg */ + struct mac_addr mac_addr0; /* 0x00C-0x010 MAC_ADDR_0...1 */ + u32 maxfrm; /* 0x014 Max frame length */ + u32 res0018[1]; + u32 rx_fifo_sections; /* Receive FIFO configuration reg */ + u32 tx_fifo_sections; /* Transmit FIFO configuration reg */ + u32 res0024[2]; + u32 hashtable_ctrl; /* 0x02C Hash table control */ + u32 res0030[4]; + u32 ievent; /* 0x040 Interrupt event */ + u32 tx_ipg_length; /* 0x044 Transmitter inter-packet-gap */ + u32 res0048; + u32 imask; /* 0x04C Interrupt mask */ + u32 res0050; + u32 pause_quanta[4]; /* 0x054 Pause quanta */ + u32 pause_thresh[4]; /* 0x064 Pause quanta threshold */ + u32 rx_pause_status; /* 0x074 Receive pause status */ + u32 res0078[2]; + struct mac_addr mac_addr[MEMAC_NUM_OF_PADDRS];/* 0x80-0x0B4 mac padr */ + u32 lpwake_timer; /* 0x0B8 Low Power Wakeup Timer */ + u32 sleep_timer; /* 0x0BC Transmit EEE Low Power Timer */ + u32 res00c0[8]; + u32 statn_config; /* 0x0E0 Statistics configuration */ + u32 res00e4[7]; + /* Rx Statistics Counter */ + u32 reoct_l; + u32 reoct_u; + u32 roct_l; + u32 roct_u; + u32 raln_l; + u32 raln_u; + u32 rxpf_l; + u32 rxpf_u; + u32 rfrm_l; + u32 rfrm_u; + u32 rfcs_l; + u32 rfcs_u; + u32 rvlan_l; + u32 rvlan_u; + u32 rerr_l; + u32 rerr_u; + u32 ruca_l; + u32 ruca_u; + u32 rmca_l; + u32 rmca_u; + u32 rbca_l; + u32 rbca_u; + u32 rdrp_l; + u32 rdrp_u; + u32 rpkt_l; + u32 rpkt_u; + u32 rund_l; + u32 rund_u; + u32 r64_l; + u32 r64_u; + u32 r127_l; + u32 r127_u; + u32 r255_l; + u32 r255_u; + u32 r511_l; + u32 r511_u; + u32 r1023_l; + u32 r1023_u; + u32 r1518_l; + u32 r1518_u; + u32 r1519x_l; + u32 r1519x_u; + u32 rovr_l; + u32 rovr_u; + u32 rjbr_l; + u32 rjbr_u; + u32 rfrg_l; + u32 rfrg_u; + u32 rcnp_l; + u32 rcnp_u; + u32 rdrntp_l; + u32 rdrntp_u; + u32 res01d0[12]; + /* Tx Statistics Counter */ + u32 teoct_l; + u32 teoct_u; + u32 toct_l; + u32 toct_u; + u32 res0210[2]; + u32 txpf_l; + u32 txpf_u; + u32 tfrm_l; + u32 tfrm_u; + u32 tfcs_l; + u32 tfcs_u; + u32 tvlan_l; + u32 tvlan_u; + u32 terr_l; + u32 terr_u; + u32 tuca_l; + u32 tuca_u; + u32 tmca_l; + u32 tmca_u; + u32 tbca_l; + u32 tbca_u; + u32 res0258[2]; + u32 tpkt_l; + u32 tpkt_u; + u32 tund_l; + u32 tund_u; + u32 t64_l; + u32 t64_u; + u32 t127_l; + u32 t127_u; + u32 t255_l; + u32 t255_u; + u32 t511_l; + u32 t511_u; + u32 t1023_l; + u32 t1023_u; + u32 t1518_l; + u32 t1518_u; + u32 t1519x_l; + u32 t1519x_u; + u32 res02a8[6]; + u32 tcnp_l; + u32 tcnp_u; + u32 res02c8[14]; + /* Line Interface Control */ + u32 if_mode; /* 0x300 Interface Mode Control */ + u32 if_status; /* 0x304 Interface Status */ + u32 res0308[14]; + /* HiGig/2 */ + u32 hg_config; /* 0x340 Control and cfg */ + u32 res0344[3]; + u32 hg_pause_quanta; /* 0x350 Pause quanta */ + u32 res0354[3]; + u32 hg_pause_thresh; /* 0x360 Pause quanta threshold */ + u32 res0364[3]; + u32 hgrx_pause_status; /* 0x370 Receive pause status */ + u32 hg_fifos_status; /* 0x374 fifos status */ + u32 rhm; /* 0x378 rx messages counter */ + u32 thm; /* 0x37C tx messages counter */ +}; + +struct memac_cfg { + bool reset_on_init; + bool pause_ignore; + bool promiscuous_mode_enable; + struct fixed_phy_status *fixed_link; + u16 max_frame_length; + u16 pause_quanta; + u32 tx_ipg_length; +}; + +struct fman_mac { + /* Pointer to MAC memory mapped registers */ + struct memac_regs __iomem *regs; + /* MAC address of device */ + u64 addr; + /* Ethernet physical interface */ + phy_interface_t phy_if; + u16 max_speed; + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *exception_cb; + fman_mac_exception_cb *event_cb; + /* Pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* Pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + u8 mac_id; + u32 exceptions; + struct memac_cfg *memac_drv_param; + void *fm; + struct fman_rev_info fm_rev_info; + bool basex_if; + struct phy_device *pcsphy; +}; + +static void add_addr_in_paddr(struct memac_regs __iomem *regs, u8 *adr, + u8 paddr_num) +{ + u32 tmp0, tmp1; + + tmp0 = (u32)(adr[0] | adr[1] << 8 | adr[2] << 16 | adr[3] << 24); + tmp1 = (u32)(adr[4] | adr[5] << 8); + + if (paddr_num == 0) { + iowrite32be(tmp0, ®s->mac_addr0.mac_addr_l); + iowrite32be(tmp1, ®s->mac_addr0.mac_addr_u); + } else { + iowrite32be(tmp0, ®s->mac_addr[paddr_num - 1].mac_addr_l); + iowrite32be(tmp1, ®s->mac_addr[paddr_num - 1].mac_addr_u); + } +} + +static int reset(struct memac_regs __iomem *regs) +{ + u32 tmp; + int count; + + tmp = ioread32be(®s->command_config); + + tmp |= CMD_CFG_SW_RESET; + + iowrite32be(tmp, ®s->command_config); + + count = 100; + do { + udelay(1); + } while ((ioread32be(®s->command_config) & CMD_CFG_SW_RESET) && + --count); + + if (count == 0) + return -EBUSY; + + return 0; +} + +static void set_exception(struct memac_regs __iomem *regs, u32 val, + bool enable) +{ + u32 tmp; + + tmp = ioread32be(®s->imask); + if (enable) + tmp |= val; + else + tmp &= ~val; + + iowrite32be(tmp, ®s->imask); +} + +static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg, + phy_interface_t phy_if, u16 speed, bool slow_10g_if, + u32 exceptions) +{ + u32 tmp; + + /* Config */ + tmp = 0; + if (cfg->promiscuous_mode_enable) + tmp |= CMD_CFG_PROMIS_EN; + if (cfg->pause_ignore) + tmp |= CMD_CFG_PAUSE_IGNORE; + + /* Payload length check disable */ + tmp |= CMD_CFG_NO_LEN_CHK; + /* Enable padding of frames in transmit direction */ + tmp |= CMD_CFG_TX_PAD_EN; + + tmp |= CMD_CFG_CRC_FWD; + + iowrite32be(tmp, ®s->command_config); + + /* Max Frame Length */ + iowrite32be((u32)cfg->max_frame_length, ®s->maxfrm); + + /* Pause Time */ + iowrite32be((u32)cfg->pause_quanta, ®s->pause_quanta[0]); + iowrite32be((u32)0, ®s->pause_thresh[0]); + + /* IF_MODE */ + tmp = 0; + switch (phy_if) { + case PHY_INTERFACE_MODE_XGMII: + tmp |= IF_MODE_XGMII; + break; + default: + tmp |= IF_MODE_GMII; + if (phy_if == PHY_INTERFACE_MODE_RGMII) + tmp |= IF_MODE_RGMII | IF_MODE_RGMII_AUTO; + } + iowrite32be(tmp, ®s->if_mode); + + /* TX_FIFO_SECTIONS */ + tmp = 0; + if (phy_if == PHY_INTERFACE_MODE_XGMII) { + if (slow_10g_if) { + tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G | + TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G); + } else { + tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_10G | + TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G); + } + } else { + tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_1G | + TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G); + } + iowrite32be(tmp, ®s->tx_fifo_sections); + + /* clear all pending events and set-up interrupts */ + iowrite32be(0xffffffff, ®s->ievent); + set_exception(regs, exceptions, true); + + return 0; +} + +static void set_dflts(struct memac_cfg *cfg) +{ + cfg->reset_on_init = false; + cfg->promiscuous_mode_enable = false; + cfg->pause_ignore = false; + cfg->tx_ipg_length = DEFAULT_TX_IPG_LENGTH; + cfg->max_frame_length = DEFAULT_FRAME_LENGTH; + cfg->pause_quanta = DEFAULT_PAUSE_QUANTA; +} + +static u32 get_mac_addr_hash_code(u64 eth_addr) +{ + u64 mask1, mask2; + u32 xor_val = 0; + u8 i, j; + + for (i = 0; i < 6; i++) { + mask1 = eth_addr & (u64)0x01; + eth_addr >>= 1; + + for (j = 0; j < 7; j++) { + mask2 = eth_addr & (u64)0x01; + mask1 ^= mask2; + eth_addr >>= 1; + } + + xor_val |= (mask1 << (5 - i)); + } + + return xor_val; +} + +static void setup_sgmii_internal_phy(struct fman_mac *memac, + struct fixed_phy_status *fixed_link) +{ + u16 tmp_reg16; + + /* SGMII mode */ + tmp_reg16 = IF_MODE_SGMII_EN; + if (!fixed_link) + /* AN enable */ + tmp_reg16 |= IF_MODE_USE_SGMII_AN; + else { + switch (fixed_link->speed) { + case 10: + /* For 10M: IF_MODE[SPEED_10M] = 0 */ + break; + case 100: + tmp_reg16 |= IF_MODE_SGMII_SPEED_100M; + break; + case 1000: /* fallthrough */ + default: + tmp_reg16 |= IF_MODE_SGMII_SPEED_1G; + break; + } + if (!fixed_link->duplex) + tmp_reg16 |= IF_MODE_SGMII_DUPLEX_HALF; + } + phy_write(memac->pcsphy, MDIO_SGMII_IF_MODE, tmp_reg16); + + /* Device ability according to SGMII specification */ + tmp_reg16 = MDIO_SGMII_DEV_ABIL_SGMII_MODE; + phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16); + + /* Adjust link timer for SGMII - + * According to Cisco SGMII specification the timer should be 1.6 ms. + * The link_timer register is configured in units of the clock. + * - When running as 1G SGMII, Serdes clock is 125 MHz, so + * unit = 1 / (125*10^6 Hz) = 8 ns. + * 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2*10^5 = 0x30d40 + * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so + * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. + * 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5*10^5 = 0x7a120. + * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, + * we always set up here a value of 2.5 SGMII. + */ + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H); + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L); + + if (!fixed_link) + /* Restart AN */ + tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN; + else + /* AN disabled */ + tmp_reg16 = SGMII_CR_DEF_VAL & ~SGMII_CR_AN_EN; + phy_write(memac->pcsphy, 0x0, tmp_reg16); +} + +static void setup_sgmii_internal_phy_base_x(struct fman_mac *memac) +{ + u16 tmp_reg16; + + /* AN Device capability */ + tmp_reg16 = MDIO_SGMII_DEV_ABIL_BASEX_MODE; + phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16); + + /* Adjust link timer for SGMII - + * For Serdes 1000BaseX auto-negotiation the timer should be 10 ms. + * The link_timer register is configured in units of the clock. + * - When running as 1G SGMII, Serdes clock is 125 MHz, so + * unit = 1 / (125*10^6 Hz) = 8 ns. + * 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0 + * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so + * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. + * 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08. + * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, + * we always set up here a value of 2.5 SGMII. + */ + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H_BASEX); + phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L_BASEX); + + /* Restart AN */ + tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN; + phy_write(memac->pcsphy, 0x0, tmp_reg16); +} + +static int check_init_parameters(struct fman_mac *memac) +{ + if (memac->addr == 0) { + pr_err("Ethernet MAC must have a valid MAC address\n"); + return -EINVAL; + } + if (!memac->exception_cb) { + pr_err("Uninitialized exception handler\n"); + return -EINVAL; + } + if (!memac->event_cb) { + pr_warn("Uninitialize event handler\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fman_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_10G_TX_ECC_ER: + bit_mask = MEMAC_IMASK_TECC_ER; + break; + case FM_MAC_EX_10G_RX_ECC_ER: + bit_mask = MEMAC_IMASK_RECC_ER; + break; + case FM_MAC_EX_TS_FIFO_ECC_ERR: + bit_mask = MEMAC_IMASK_TSECC_ER; + break; + case FM_MAC_EX_MAGIC_PACKET_INDICATION: + bit_mask = MEMAC_IMASK_MGI; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static void memac_err_exception(void *handle) +{ + struct fman_mac *memac = (struct fman_mac *)handle; + struct memac_regs __iomem *regs = memac->regs; + u32 event, imask; + + event = ioread32be(®s->ievent); + imask = ioread32be(®s->imask); + + /* Imask include both error and notification/event bits. + * Leaving only error bits enabled by imask. + * The imask error bits are shifted by 16 bits offset from + * their corresponding location in the ievent - hence the >> 16 + */ + event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16); + + iowrite32be(event, ®s->ievent); + + if (event & MEMAC_IEVNT_TS_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_TS_FIFO_ECC_ERR); + if (event & MEMAC_IEVNT_TX_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_TX_ECC_ER); + if (event & MEMAC_IEVNT_RX_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_RX_ECC_ER); +} + +static void memac_exception(void *handle) +{ + struct fman_mac *memac = (struct fman_mac *)handle; + struct memac_regs __iomem *regs = memac->regs; + u32 event, imask; + + event = ioread32be(®s->ievent); + imask = ioread32be(®s->imask); + + /* Imask include both error and notification/event bits. + * Leaving only error bits enabled by imask. + * The imask error bits are shifted by 16 bits offset from + * their corresponding location in the ievent - hence the >> 16 + */ + event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16); + + iowrite32be(event, ®s->ievent); + + if (event & MEMAC_IEVNT_MGI) + memac->exception_cb(memac->dev_id, + FM_MAC_EX_MAGIC_PACKET_INDICATION); +} + +static void free_init_resources(struct fman_mac *memac) +{ + fman_unregister_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_ERR); + + fman_unregister_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_NORMAL); + + /* release the driver's group hash table */ + free_hash_table(memac->multicast_addr_hash); + memac->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(memac->unicast_addr_hash); + memac->unicast_addr_hash = NULL; +} + +static bool is_init_done(struct memac_cfg *memac_drv_params) +{ + /* Checks if mEMAC driver parameters were initialized */ + if (!memac_drv_params) + return true; + + return false; +} + +int memac_enable(struct fman_mac *memac, enum comm_mode mode) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp |= CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp |= CMD_CFG_TX_EN; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_disable(struct fman_mac *memac, enum comm_mode mode) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp &= ~CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp &= ~CMD_CFG_TX_EN; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_set_promiscuous(struct fman_mac *memac, bool new_val) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (new_val) + tmp |= CMD_CFG_PROMIS_EN; + else + tmp &= ~CMD_CFG_PROMIS_EN; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_adjust_link(struct fman_mac *memac, u16 speed) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->if_mode); + + /* Set full duplex */ + tmp &= ~IF_MODE_HD; + + if (memac->phy_if == PHY_INTERFACE_MODE_RGMII) { + /* Configure RGMII in manual mode */ + tmp &= ~IF_MODE_RGMII_AUTO; + tmp &= ~IF_MODE_RGMII_SP_MASK; + /* Full duplex */ + tmp |= IF_MODE_RGMII_FD; + + switch (speed) { + case SPEED_1000: + tmp |= IF_MODE_RGMII_1000; + break; + case SPEED_100: + tmp |= IF_MODE_RGMII_100; + break; + case SPEED_10: + tmp |= IF_MODE_RGMII_10; + break; + default: + break; + } + } + + iowrite32be(tmp, ®s->if_mode); + + return 0; +} + +int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val) +{ + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + memac->memac_drv_param->max_frame_length = new_val; + + return 0; +} + +int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable) +{ + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + memac->memac_drv_param->reset_on_init = enable; + + return 0; +} + +int memac_cfg_fixed_link(struct fman_mac *memac, + struct fixed_phy_status *fixed_link) +{ + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + memac->memac_drv_param->fixed_link = fixed_link; + + return 0; +} + +int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, + u16 pause_time, u16 thresh_time) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->tx_fifo_sections); + + GET_TX_EMPTY_DEFAULT_VALUE(tmp); + iowrite32be(tmp, ®s->tx_fifo_sections); + + tmp = ioread32be(®s->command_config); + tmp &= ~CMD_CFG_PFC_MODE; + priority = 0; + + iowrite32be(tmp, ®s->command_config); + + tmp = ioread32be(®s->pause_quanta[priority / 2]); + if (priority % 2) + tmp &= CLXY_PAUSE_QUANTA_CLX_PQNT; + else + tmp &= CLXY_PAUSE_QUANTA_CLY_PQNT; + tmp |= ((u32)pause_time << (16 * (priority % 2))); + iowrite32be(tmp, ®s->pause_quanta[priority / 2]); + + tmp = ioread32be(®s->pause_thresh[priority / 2]); + if (priority % 2) + tmp &= CLXY_PAUSE_THRESH_CLX_QTH; + else + tmp &= CLXY_PAUSE_THRESH_CLY_QTH; + tmp |= ((u32)thresh_time << (16 * (priority % 2))); + iowrite32be(tmp, ®s->pause_thresh[priority / 2]); + + return 0; +} + +int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en) +{ + struct memac_regs __iomem *regs = memac->regs; + u32 tmp; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (en) + tmp &= ~CMD_CFG_PAUSE_IGNORE; + else + tmp |= CMD_CFG_PAUSE_IGNORE; + + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int memac_modify_mac_address(struct fman_mac *memac, enet_addr_t *enet_addr) +{ + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + add_addr_in_paddr(memac->regs, (u8 *)(*enet_addr), 0); + + return 0; +} + +int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr) +{ + struct memac_regs __iomem *regs = memac->regs; + struct eth_hash_entry *hash_entry; + u32 hash; + u64 addr; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + if (!(addr & GROUP_ADDRESS)) { + /* Unicast addresses not supported in hash */ + pr_err("Unicast Address\n"); + return -EINVAL; + } + hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK; + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + list_add_tail(&hash_entry->node, + &memac->multicast_addr_hash->lsts[hash]); + iowrite32be(hash | HASH_CTRL_MCAST_EN, ®s->hashtable_ctrl); + + return 0; +} + +int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr) +{ + struct memac_regs __iomem *regs = memac->regs; + struct eth_hash_entry *hash_entry = NULL; + struct list_head *pos; + u32 hash; + u64 addr; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK; + + list_for_each(pos, &memac->multicast_addr_hash->lsts[hash]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&memac->multicast_addr_hash->lsts[hash])) + iowrite32be(hash & ~HASH_CTRL_MCAST_EN, ®s->hashtable_ctrl); + + return 0; +} + +int memac_set_exception(struct fman_mac *memac, + enum fman_mac_exceptions exception, bool enable) +{ + u32 bit_mask = 0; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + memac->exceptions |= bit_mask; + else + memac->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + set_exception(memac->regs, bit_mask, enable); + + return 0; +} + +int memac_init(struct fman_mac *memac) +{ + struct memac_cfg *memac_drv_param; + u8 i; + enet_addr_t eth_addr; + bool slow_10g_if = false; + struct fixed_phy_status *fixed_link; + int err; + u32 reg32 = 0; + + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + err = check_init_parameters(memac); + if (err) + return err; + + memac_drv_param = memac->memac_drv_param; + + if (memac->fm_rev_info.major == 6 && memac->fm_rev_info.minor == 4) + slow_10g_if = true; + + /* First, reset the MAC if desired. */ + if (memac_drv_param->reset_on_init) { + err = reset(memac->regs); + if (err) { + pr_err("mEMAC reset failed\n"); + return err; + } + } + + /* MAC Address */ + MAKE_ENET_ADDR_FROM_UINT64(memac->addr, eth_addr); + add_addr_in_paddr(memac->regs, (u8 *)eth_addr, 0); + + fixed_link = memac_drv_param->fixed_link; + + init(memac->regs, memac->memac_drv_param, memac->phy_if, + memac->max_speed, slow_10g_if, memac->exceptions); + + /* FM_RX_FIFO_CORRUPT_ERRATA_10GMAC_A006320 errata workaround + * Exists only in FMan 6.0 and 6.3. + */ + if ((memac->fm_rev_info.major == 6) && + ((memac->fm_rev_info.minor == 0) || + (memac->fm_rev_info.minor == 3))) { + /* MAC strips CRC from received frames - this workaround + * should decrease the likelihood of bug appearance + */ + reg32 = ioread32be(&memac->regs->command_config); + reg32 &= ~CMD_CFG_CRC_FWD; + iowrite32be(reg32, &memac->regs->command_config); + } + + if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) { + /* Configure internal SGMII PHY */ + if (memac->basex_if) + setup_sgmii_internal_phy_base_x(memac); + else + setup_sgmii_internal_phy(memac, fixed_link); + } else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) { + /* Configure 4 internal SGMII PHYs */ + for (i = 0; i < 4; i++) { + u8 qsmgii_phy_addr, phy_addr; + /* QSGMII PHY address occupies 3 upper bits of 5-bit + * phy_address; the lower 2 bits are used to extend + * register address space and access each one of 4 + * ports inside QSGMII. + */ + phy_addr = memac->pcsphy->addr; + qsmgii_phy_addr = (u8)((phy_addr << 2) | i); + memac->pcsphy->addr = qsmgii_phy_addr; + if (memac->basex_if) + setup_sgmii_internal_phy_base_x(memac); + else + setup_sgmii_internal_phy(memac, fixed_link); + + memac->pcsphy->addr = phy_addr; + } + } + + /* Max Frame Length */ + err = fman_set_mac_max_frame(memac->fm, memac->mac_id, + memac_drv_param->max_frame_length); + if (err) { + pr_err("settings Mac max frame length is FAILED\n"); + return err; + } + + memac->multicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE); + if (!memac->multicast_addr_hash) { + free_init_resources(memac); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + memac->unicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE); + if (!memac->unicast_addr_hash) { + free_init_resources(memac); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_ERR, memac_err_exception, memac); + + fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id, + FMAN_INTR_TYPE_NORMAL, memac_exception, memac); + + kfree(memac_drv_param); + memac->memac_drv_param = NULL; + + return 0; +} + +int memac_free(struct fman_mac *memac) +{ + free_init_resources(memac); + + kfree(memac->memac_drv_param); + kfree(memac); + + return 0; +} + +struct fman_mac *memac_config(struct fman_mac_params *params) +{ + struct fman_mac *memac; + struct memac_cfg *memac_drv_param; + void __iomem *base_addr; + + base_addr = params->base_addr; + /* allocate memory for the m_emac data structure */ + memac = kzalloc(sizeof(*memac), GFP_KERNEL); + if (!memac) + return NULL; + + /* allocate memory for the m_emac driver parameters data structure */ + memac_drv_param = kzalloc(sizeof(*memac_drv_param), GFP_KERNEL); + if (!memac_drv_param) { + memac_free(memac); + return NULL; + } + + /* Plant parameter structure pointer */ + memac->memac_drv_param = memac_drv_param; + + set_dflts(memac_drv_param); + + memac->addr = ENET_ADDR_TO_UINT64(params->addr); + + memac->regs = base_addr; + memac->max_speed = params->max_speed; + memac->phy_if = params->phy_if; + memac->mac_id = params->mac_id; + memac->exceptions = (MEMAC_IMASK_TSECC_ER | MEMAC_IMASK_TECC_ER | + MEMAC_IMASK_RECC_ER | MEMAC_IMASK_MGI); + memac->exception_cb = params->exception_cb; + memac->event_cb = params->event_cb; + memac->dev_id = params->dev_id; + memac->fm = params->fm; + memac->basex_if = params->basex_if; + + /* Save FMan revision */ + fman_get_revision(memac->fm, &memac->fm_rev_info); + + if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) { + if (!params->internal_phy_node) { + pr_err("PCS PHY node is not available\n"); + memac_free(memac); + return NULL; + } + + memac->pcsphy = of_phy_find_device(params->internal_phy_node); + if (!memac->pcsphy) { + pr_err("of_phy_find_device (PCS PHY) failed\n"); + memac_free(memac); + return NULL; + } + } + + return memac; +} diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.h b/drivers/net/ethernet/freescale/fman/fman_memac.h new file mode 100644 index 000000000000..173d8e0fd716 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_memac.h @@ -0,0 +1,60 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MEMAC_H +#define __MEMAC_H + +#include "fman_mac.h" + +#include + +struct fman_mac *memac_config(struct fman_mac_params *params); +int memac_set_promiscuous(struct fman_mac *memac, bool new_val); +int memac_modify_mac_address(struct fman_mac *memac, enet_addr_t *enet_addr); +int memac_adjust_link(struct fman_mac *memac, u16 speed); +int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val); +int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable); +int memac_cfg_fixed_link(struct fman_mac *memac, + struct fixed_phy_status *fixed_link); +int memac_enable(struct fman_mac *memac, enum comm_mode mode); +int memac_disable(struct fman_mac *memac, enum comm_mode mode); +int memac_init(struct fman_mac *memac); +int memac_free(struct fman_mac *memac); +int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en); +int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority, + u16 pause_time, u16 thresh_time); +int memac_set_exception(struct fman_mac *memac, + enum fman_mac_exceptions exception, bool enable); +int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr); +int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr); + +#endif /* __MEMAC_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.c b/drivers/net/ethernet/freescale/fman/fman_tgec.c new file mode 100644 index 000000000000..efabb04a1ae8 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c @@ -0,0 +1,786 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fman_tgec.h" +#include "fman.h" + +#include +#include +#include +#include + +/* Transmit Inter-Packet Gap Length Register (TX_IPG_LENGTH) */ +#define TGEC_TX_IPG_LENGTH_MASK 0x000003ff + +/* Command and Configuration Register (COMMAND_CONFIG) */ +#define CMD_CFG_NO_LEN_CHK 0x00020000 +#define CMD_CFG_PAUSE_IGNORE 0x00000100 +#define CMF_CFG_CRC_FWD 0x00000040 +#define CMD_CFG_PROMIS_EN 0x00000010 +#define CMD_CFG_RX_EN 0x00000002 +#define CMD_CFG_TX_EN 0x00000001 + +/* Interrupt Mask Register (IMASK) */ +#define TGEC_IMASK_MDIO_SCAN_EVENT 0x00010000 +#define TGEC_IMASK_MDIO_CMD_CMPL 0x00008000 +#define TGEC_IMASK_REM_FAULT 0x00004000 +#define TGEC_IMASK_LOC_FAULT 0x00002000 +#define TGEC_IMASK_TX_ECC_ER 0x00001000 +#define TGEC_IMASK_TX_FIFO_UNFL 0x00000800 +#define TGEC_IMASK_TX_FIFO_OVFL 0x00000400 +#define TGEC_IMASK_TX_ER 0x00000200 +#define TGEC_IMASK_RX_FIFO_OVFL 0x00000100 +#define TGEC_IMASK_RX_ECC_ER 0x00000080 +#define TGEC_IMASK_RX_JAB_FRM 0x00000040 +#define TGEC_IMASK_RX_OVRSZ_FRM 0x00000020 +#define TGEC_IMASK_RX_RUNT_FRM 0x00000010 +#define TGEC_IMASK_RX_FRAG_FRM 0x00000008 +#define TGEC_IMASK_RX_LEN_ER 0x00000004 +#define TGEC_IMASK_RX_CRC_ER 0x00000002 +#define TGEC_IMASK_RX_ALIGN_ER 0x00000001 + +/* Hashtable Control Register (HASHTABLE_CTRL) */ +#define TGEC_HASH_MCAST_SHIFT 23 +#define TGEC_HASH_MCAST_EN 0x00000200 +#define TGEC_HASH_ADR_MSK 0x000001ff + +#define DEFAULT_TX_IPG_LENGTH 12 +#define DEFAULT_MAX_FRAME_LENGTH 0x600 +#define DEFAULT_PAUSE_QUANT 0xf000 + +/* number of pattern match registers (entries) */ +#define TGEC_NUM_OF_PADDRS 1 + +/* Group address bit indication */ +#define GROUP_ADDRESS 0x0000010000000000LL + +/* Hash table size (= 32 bits*8 regs) */ +#define TGEC_HASH_TABLE_SIZE 512 + +/* tGEC memory map */ +struct tgec_regs { + u32 tgec_id; /* 0x000 Controller ID */ + u32 reserved001[1]; /* 0x004 */ + u32 command_config; /* 0x008 Control and configuration */ + u32 mac_addr_0; /* 0x00c Lower 32 bits of the MAC adr */ + u32 mac_addr_1; /* 0x010 Upper 16 bits of the MAC adr */ + u32 maxfrm; /* 0x014 Maximum frame length */ + u32 pause_quant; /* 0x018 Pause quanta */ + u32 rx_fifo_sections; /* 0x01c */ + u32 tx_fifo_sections; /* 0x020 */ + u32 rx_fifo_almost_f_e; /* 0x024 */ + u32 tx_fifo_almost_f_e; /* 0x028 */ + u32 hashtable_ctrl; /* 0x02c Hash table control */ + u32 mdio_cfg_status; /* 0x030 */ + u32 mdio_command; /* 0x034 */ + u32 mdio_data; /* 0x038 */ + u32 mdio_regaddr; /* 0x03c */ + u32 status; /* 0x040 */ + u32 tx_ipg_len; /* 0x044 Transmitter inter-packet-gap */ + u32 mac_addr_2; /* 0x048 Lower 32 bits of 2nd MAC adr */ + u32 mac_addr_3; /* 0x04c Upper 16 bits of 2nd MAC adr */ + u32 rx_fifo_ptr_rd; /* 0x050 */ + u32 rx_fifo_ptr_wr; /* 0x054 */ + u32 tx_fifo_ptr_rd; /* 0x058 */ + u32 tx_fifo_ptr_wr; /* 0x05c */ + u32 imask; /* 0x060 Interrupt mask */ + u32 ievent; /* 0x064 Interrupt event */ + u32 udp_port; /* 0x068 Defines a UDP Port number */ + u32 type_1588v2; /* 0x06c Type field for 1588v2 */ + u32 reserved070[4]; /* 0x070 */ + /* 10Ge Statistics Counter */ + u32 tfrm_u; /* 80 aFramesTransmittedOK */ + u32 tfrm_l; /* 84 aFramesTransmittedOK */ + u32 rfrm_u; /* 88 aFramesReceivedOK */ + u32 rfrm_l; /* 8c aFramesReceivedOK */ + u32 rfcs_u; /* 90 aFrameCheckSequenceErrors */ + u32 rfcs_l; /* 94 aFrameCheckSequenceErrors */ + u32 raln_u; /* 98 aAlignmentErrors */ + u32 raln_l; /* 9c aAlignmentErrors */ + u32 txpf_u; /* A0 aPAUSEMACCtrlFramesTransmitted */ + u32 txpf_l; /* A4 aPAUSEMACCtrlFramesTransmitted */ + u32 rxpf_u; /* A8 aPAUSEMACCtrlFramesReceived */ + u32 rxpf_l; /* Ac aPAUSEMACCtrlFramesReceived */ + u32 rlong_u; /* B0 aFrameTooLongErrors */ + u32 rlong_l; /* B4 aFrameTooLongErrors */ + u32 rflr_u; /* B8 aInRangeLengthErrors */ + u32 rflr_l; /* Bc aInRangeLengthErrors */ + u32 tvlan_u; /* C0 VLANTransmittedOK */ + u32 tvlan_l; /* C4 VLANTransmittedOK */ + u32 rvlan_u; /* C8 VLANReceivedOK */ + u32 rvlan_l; /* Cc VLANReceivedOK */ + u32 toct_u; /* D0 if_out_octets */ + u32 toct_l; /* D4 if_out_octets */ + u32 roct_u; /* D8 if_in_octets */ + u32 roct_l; /* Dc if_in_octets */ + u32 ruca_u; /* E0 if_in_ucast_pkts */ + u32 ruca_l; /* E4 if_in_ucast_pkts */ + u32 rmca_u; /* E8 ifInMulticastPkts */ + u32 rmca_l; /* Ec ifInMulticastPkts */ + u32 rbca_u; /* F0 ifInBroadcastPkts */ + u32 rbca_l; /* F4 ifInBroadcastPkts */ + u32 terr_u; /* F8 if_out_errors */ + u32 terr_l; /* Fc if_out_errors */ + u32 reserved100[2]; /* 100-108 */ + u32 tuca_u; /* 108 if_out_ucast_pkts */ + u32 tuca_l; /* 10c if_out_ucast_pkts */ + u32 tmca_u; /* 110 ifOutMulticastPkts */ + u32 tmca_l; /* 114 ifOutMulticastPkts */ + u32 tbca_u; /* 118 ifOutBroadcastPkts */ + u32 tbca_l; /* 11c ifOutBroadcastPkts */ + u32 rdrp_u; /* 120 etherStatsDropEvents */ + u32 rdrp_l; /* 124 etherStatsDropEvents */ + u32 reoct_u; /* 128 etherStatsOctets */ + u32 reoct_l; /* 12c etherStatsOctets */ + u32 rpkt_u; /* 130 etherStatsPkts */ + u32 rpkt_l; /* 134 etherStatsPkts */ + u32 trund_u; /* 138 etherStatsUndersizePkts */ + u32 trund_l; /* 13c etherStatsUndersizePkts */ + u32 r64_u; /* 140 etherStatsPkts64Octets */ + u32 r64_l; /* 144 etherStatsPkts64Octets */ + u32 r127_u; /* 148 etherStatsPkts65to127Octets */ + u32 r127_l; /* 14c etherStatsPkts65to127Octets */ + u32 r255_u; /* 150 etherStatsPkts128to255Octets */ + u32 r255_l; /* 154 etherStatsPkts128to255Octets */ + u32 r511_u; /* 158 etherStatsPkts256to511Octets */ + u32 r511_l; /* 15c etherStatsPkts256to511Octets */ + u32 r1023_u; /* 160 etherStatsPkts512to1023Octets */ + u32 r1023_l; /* 164 etherStatsPkts512to1023Octets */ + u32 r1518_u; /* 168 etherStatsPkts1024to1518Octets */ + u32 r1518_l; /* 16c etherStatsPkts1024to1518Octets */ + u32 r1519x_u; /* 170 etherStatsPkts1519toX */ + u32 r1519x_l; /* 174 etherStatsPkts1519toX */ + u32 trovr_u; /* 178 etherStatsOversizePkts */ + u32 trovr_l; /* 17c etherStatsOversizePkts */ + u32 trjbr_u; /* 180 etherStatsJabbers */ + u32 trjbr_l; /* 184 etherStatsJabbers */ + u32 trfrg_u; /* 188 etherStatsFragments */ + u32 trfrg_l; /* 18C etherStatsFragments */ + u32 rerr_u; /* 190 if_in_errors */ + u32 rerr_l; /* 194 if_in_errors */ +}; + +struct tgec_cfg { + bool pause_ignore; + bool promiscuous_mode_enable; + u16 max_frame_length; + u16 pause_quant; + u32 tx_ipg_length; +}; + +struct fman_mac { + /* Pointer to the memory mapped registers. */ + struct tgec_regs __iomem *regs; + /* MAC address of device; */ + u64 addr; + u16 max_speed; + void *dev_id; /* device cookie used by the exception cbs */ + fman_mac_exception_cb *exception_cb; + fman_mac_exception_cb *event_cb; + /* pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + u8 mac_id; + u32 exceptions; + struct tgec_cfg *cfg; + void *fm; + struct fman_rev_info fm_rev_info; +}; + +static void set_mac_address(struct tgec_regs __iomem *regs, u8 *adr) +{ + u32 tmp0, tmp1; + + tmp0 = (u32)(adr[0] | adr[1] << 8 | adr[2] << 16 | adr[3] << 24); + tmp1 = (u32)(adr[4] | adr[5] << 8); + iowrite32be(tmp0, ®s->mac_addr_0); + iowrite32be(tmp1, ®s->mac_addr_1); +} + +static void set_dflts(struct tgec_cfg *cfg) +{ + cfg->promiscuous_mode_enable = false; + cfg->pause_ignore = false; + cfg->tx_ipg_length = DEFAULT_TX_IPG_LENGTH; + cfg->max_frame_length = DEFAULT_MAX_FRAME_LENGTH; + cfg->pause_quant = DEFAULT_PAUSE_QUANT; +} + +static int init(struct tgec_regs __iomem *regs, struct tgec_cfg *cfg, + u32 exception_mask) +{ + u32 tmp; + + /* Config */ + tmp = CMF_CFG_CRC_FWD; + if (cfg->promiscuous_mode_enable) + tmp |= CMD_CFG_PROMIS_EN; + if (cfg->pause_ignore) + tmp |= CMD_CFG_PAUSE_IGNORE; + /* Payload length check disable */ + tmp |= CMD_CFG_NO_LEN_CHK; + iowrite32be(tmp, ®s->command_config); + + /* Max Frame Length */ + iowrite32be((u32)cfg->max_frame_length, ®s->maxfrm); + /* Pause Time */ + iowrite32be(cfg->pause_quant, ®s->pause_quant); + + /* clear all pending events and set-up interrupts */ + iowrite32be(0xffffffff, ®s->ievent); + iowrite32be(ioread32be(®s->imask) | exception_mask, ®s->imask); + + return 0; +} + +static int check_init_parameters(struct fman_mac *tgec) +{ + if (tgec->max_speed < SPEED_10000) { + pr_err("10G MAC driver only support 10G speed\n"); + return -EINVAL; + } + if (tgec->addr == 0) { + pr_err("Ethernet 10G MAC Must have valid MAC Address\n"); + return -EINVAL; + } + if (!tgec->exception_cb) { + pr_err("uninitialized exception_cb\n"); + return -EINVAL; + } + if (!tgec->event_cb) { + pr_err("uninitialized event_cb\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fman_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_10G_MDIO_SCAN_EVENT: + bit_mask = TGEC_IMASK_MDIO_SCAN_EVENT; + break; + case FM_MAC_EX_10G_MDIO_CMD_CMPL: + bit_mask = TGEC_IMASK_MDIO_CMD_CMPL; + break; + case FM_MAC_EX_10G_REM_FAULT: + bit_mask = TGEC_IMASK_REM_FAULT; + break; + case FM_MAC_EX_10G_LOC_FAULT: + bit_mask = TGEC_IMASK_LOC_FAULT; + break; + case FM_MAC_EX_10G_TX_ECC_ER: + bit_mask = TGEC_IMASK_TX_ECC_ER; + break; + case FM_MAC_EX_10G_TX_FIFO_UNFL: + bit_mask = TGEC_IMASK_TX_FIFO_UNFL; + break; + case FM_MAC_EX_10G_TX_FIFO_OVFL: + bit_mask = TGEC_IMASK_TX_FIFO_OVFL; + break; + case FM_MAC_EX_10G_TX_ER: + bit_mask = TGEC_IMASK_TX_ER; + break; + case FM_MAC_EX_10G_RX_FIFO_OVFL: + bit_mask = TGEC_IMASK_RX_FIFO_OVFL; + break; + case FM_MAC_EX_10G_RX_ECC_ER: + bit_mask = TGEC_IMASK_RX_ECC_ER; + break; + case FM_MAC_EX_10G_RX_JAB_FRM: + bit_mask = TGEC_IMASK_RX_JAB_FRM; + break; + case FM_MAC_EX_10G_RX_OVRSZ_FRM: + bit_mask = TGEC_IMASK_RX_OVRSZ_FRM; + break; + case FM_MAC_EX_10G_RX_RUNT_FRM: + bit_mask = TGEC_IMASK_RX_RUNT_FRM; + break; + case FM_MAC_EX_10G_RX_FRAG_FRM: + bit_mask = TGEC_IMASK_RX_FRAG_FRM; + break; + case FM_MAC_EX_10G_RX_LEN_ER: + bit_mask = TGEC_IMASK_RX_LEN_ER; + break; + case FM_MAC_EX_10G_RX_CRC_ER: + bit_mask = TGEC_IMASK_RX_CRC_ER; + break; + case FM_MAC_EX_10G_RX_ALIGN_ER: + bit_mask = TGEC_IMASK_RX_ALIGN_ER; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static void tgec_err_exception(void *handle) +{ + struct fman_mac *tgec = (struct fman_mac *)handle; + struct tgec_regs __iomem *regs = tgec->regs; + u32 event; + + /* do not handle MDIO events */ + event = ioread32be(®s->ievent) & + ~(TGEC_IMASK_MDIO_SCAN_EVENT | + TGEC_IMASK_MDIO_CMD_CMPL); + + event &= ioread32be(®s->imask); + + iowrite32be(event, ®s->ievent); + + if (event & TGEC_IMASK_REM_FAULT) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_REM_FAULT); + if (event & TGEC_IMASK_LOC_FAULT) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_LOC_FAULT); + if (event & TGEC_IMASK_TX_ECC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ECC_ER); + if (event & TGEC_IMASK_TX_FIFO_UNFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_UNFL); + if (event & TGEC_IMASK_TX_FIFO_OVFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_OVFL); + if (event & TGEC_IMASK_TX_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ER); + if (event & TGEC_IMASK_RX_FIFO_OVFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FIFO_OVFL); + if (event & TGEC_IMASK_RX_ECC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ECC_ER); + if (event & TGEC_IMASK_RX_JAB_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_JAB_FRM); + if (event & TGEC_IMASK_RX_OVRSZ_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_OVRSZ_FRM); + if (event & TGEC_IMASK_RX_RUNT_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_RUNT_FRM); + if (event & TGEC_IMASK_RX_FRAG_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FRAG_FRM); + if (event & TGEC_IMASK_RX_LEN_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_LEN_ER); + if (event & TGEC_IMASK_RX_CRC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_CRC_ER); + if (event & TGEC_IMASK_RX_ALIGN_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ALIGN_ER); +} + +static void free_init_resources(struct fman_mac *tgec) +{ + fman_unregister_intr(tgec->fm, FMAN_MOD_MAC, tgec->mac_id, + FMAN_INTR_TYPE_ERR); + + /* release the driver's group hash table */ + free_hash_table(tgec->multicast_addr_hash); + tgec->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(tgec->unicast_addr_hash); + tgec->unicast_addr_hash = NULL; +} + +static bool is_init_done(struct tgec_cfg *cfg) +{ + /* Checks if tGEC driver parameters were initialized */ + if (!cfg) + return true; + + return false; +} + +int tgec_enable(struct fman_mac *tgec, enum comm_mode mode) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp |= CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp |= CMD_CFG_TX_EN; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_disable(struct fman_mac *tgec, enum comm_mode mode) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (mode & COMM_MODE_RX) + tmp &= ~CMD_CFG_RX_EN; + if (mode & COMM_MODE_TX) + tmp &= ~CMD_CFG_TX_EN; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (new_val) + tmp |= CMD_CFG_PROMIS_EN; + else + tmp &= ~CMD_CFG_PROMIS_EN; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val) +{ + if (is_init_done(tgec->cfg)) + return -EINVAL; + + tgec->cfg->max_frame_length = new_val; + + return 0; +} + +int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 __maybe_unused priority, + u16 pause_time, u16 __maybe_unused thresh_time) +{ + struct tgec_regs __iomem *regs = tgec->regs; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + iowrite32be((u32)pause_time, ®s->pause_quant); + + return 0; +} + +int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tmp = ioread32be(®s->command_config); + if (!en) + tmp |= CMD_CFG_PAUSE_IGNORE; + else + tmp &= ~CMD_CFG_PAUSE_IGNORE; + iowrite32be(tmp, ®s->command_config); + + return 0; +} + +int tgec_modify_mac_address(struct fman_mac *tgec, enet_addr_t *p_enet_addr) +{ + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + tgec->addr = ENET_ADDR_TO_UINT64(*p_enet_addr); + set_mac_address(tgec->regs, (u8 *)(*p_enet_addr)); + + return 0; +} + +int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr) +{ + struct tgec_regs __iomem *regs = tgec->regs; + struct eth_hash_entry *hash_entry; + u32 crc = 0xFFFFFFFF, hash; + u64 addr; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + if (!(addr & GROUP_ADDRESS)) { + /* Unicast addresses not supported in hash */ + pr_err("Unicast Address\n"); + return -EINVAL; + } + /* CRC calculation */ + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + /* Take 9 MSB bits */ + hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK; + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + list_add_tail(&hash_entry->node, + &tgec->multicast_addr_hash->lsts[hash]); + iowrite32be((hash | TGEC_HASH_MCAST_EN), ®s->hashtable_ctrl); + + return 0; +} + +int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr) +{ + struct tgec_regs __iomem *regs = tgec->regs; + struct eth_hash_entry *hash_entry = NULL; + struct list_head *pos; + u32 crc = 0xFFFFFFFF, hash; + u64 addr; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + addr = ((*(u64 *)eth_addr) >> 16); + + /* CRC calculation */ + crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN); + crc = bitrev32(crc); + /* Take 9 MSB bits */ + hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK; + + list_for_each(pos, &tgec->multicast_addr_hash->lsts[hash]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&tgec->multicast_addr_hash->lsts[hash])) + iowrite32be((hash & ~TGEC_HASH_MCAST_EN), + ®s->hashtable_ctrl); + + return 0; +} + +int tgec_get_version(struct fman_mac *tgec, u32 *mac_version) +{ + struct tgec_regs __iomem *regs = tgec->regs; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + *mac_version = ioread32be(®s->tgec_id); + + return 0; +} + +int tgec_set_exception(struct fman_mac *tgec, + enum fman_mac_exceptions exception, bool enable) +{ + struct tgec_regs __iomem *regs = tgec->regs; + u32 bit_mask = 0; + + if (!is_init_done(tgec->cfg)) + return -EINVAL; + + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + tgec->exceptions |= bit_mask; + else + tgec->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + if (enable) + iowrite32be(ioread32be(®s->imask) | bit_mask, ®s->imask); + else + iowrite32be(ioread32be(®s->imask) & ~bit_mask, ®s->imask); + + return 0; +} + +int tgec_init(struct fman_mac *tgec) +{ + struct tgec_cfg *cfg; + enet_addr_t eth_addr; + int err; + + if (is_init_done(tgec->cfg)) + return -EINVAL; + + if (DEFAULT_RESET_ON_INIT && + (fman_reset_mac(tgec->fm, tgec->mac_id) != 0)) { + pr_err("Can't reset MAC!\n"); + return -EINVAL; + } + + err = check_init_parameters(tgec); + if (err) + return err; + + cfg = tgec->cfg; + + MAKE_ENET_ADDR_FROM_UINT64(tgec->addr, eth_addr); + set_mac_address(tgec->regs, (u8 *)eth_addr); + + /* interrupts */ + /* FM_10G_REM_N_LCL_FLT_EX_10GMAC_ERRATA_SW005 Errata workaround */ + if (tgec->fm_rev_info.major <= 2) + tgec->exceptions &= ~(TGEC_IMASK_REM_FAULT | + TGEC_IMASK_LOC_FAULT); + + err = init(tgec->regs, cfg, tgec->exceptions); + if (err) { + free_init_resources(tgec); + pr_err("TGEC version doesn't support this i/f mode\n"); + return err; + } + + /* Max Frame Length */ + err = fman_set_mac_max_frame(tgec->fm, tgec->mac_id, + cfg->max_frame_length); + if (err) { + pr_err("Setting max frame length FAILED\n"); + free_init_resources(tgec); + return -EINVAL; + } + + /* FM_TX_FIFO_CORRUPTION_ERRATA_10GMAC_A007 Errata workaround */ + if (tgec->fm_rev_info.major == 2) { + struct tgec_regs __iomem *regs = tgec->regs; + u32 tmp; + + /* restore the default tx ipg Length */ + tmp = (ioread32be(®s->tx_ipg_len) & + ~TGEC_TX_IPG_LENGTH_MASK) | 12; + + iowrite32be(tmp, ®s->tx_ipg_len); + } + + tgec->multicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE); + if (!tgec->multicast_addr_hash) { + free_init_resources(tgec); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + tgec->unicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE); + if (!tgec->unicast_addr_hash) { + free_init_resources(tgec); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + fman_register_intr(tgec->fm, FMAN_MOD_MAC, tgec->mac_id, + FMAN_INTR_TYPE_ERR, tgec_err_exception, tgec); + + kfree(cfg); + tgec->cfg = NULL; + + return 0; +} + +int tgec_free(struct fman_mac *tgec) +{ + free_init_resources(tgec); + + if (tgec->cfg) + tgec->cfg = NULL; + + kfree(tgec->cfg); + kfree(tgec); + + return 0; +} + +struct fman_mac *tgec_config(struct fman_mac_params *params) +{ + struct fman_mac *tgec; + struct tgec_cfg *cfg; + void __iomem *base_addr; + + base_addr = params->base_addr; + /* allocate memory for the UCC GETH data structure. */ + tgec = kzalloc(sizeof(*tgec), GFP_KERNEL); + if (!tgec) + return NULL; + + /* allocate memory for the 10G MAC driver parameters data structure. */ + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) { + tgec_free(tgec); + return NULL; + } + + /* Plant parameter structure pointer */ + tgec->cfg = cfg; + + set_dflts(cfg); + + tgec->regs = base_addr; + tgec->addr = ENET_ADDR_TO_UINT64(params->addr); + tgec->max_speed = params->max_speed; + tgec->mac_id = params->mac_id; + tgec->exceptions = (TGEC_IMASK_MDIO_SCAN_EVENT | + TGEC_IMASK_REM_FAULT | + TGEC_IMASK_LOC_FAULT | + TGEC_IMASK_TX_ECC_ER | + TGEC_IMASK_TX_FIFO_UNFL | + TGEC_IMASK_TX_FIFO_OVFL | + TGEC_IMASK_TX_ER | + TGEC_IMASK_RX_FIFO_OVFL | + TGEC_IMASK_RX_ECC_ER | + TGEC_IMASK_RX_JAB_FRM | + TGEC_IMASK_RX_OVRSZ_FRM | + TGEC_IMASK_RX_RUNT_FRM | + TGEC_IMASK_RX_FRAG_FRM | + TGEC_IMASK_RX_CRC_ER | + TGEC_IMASK_RX_ALIGN_ER); + tgec->exception_cb = params->exception_cb; + tgec->event_cb = params->event_cb; + tgec->dev_id = params->dev_id; + tgec->fm = params->fm; + + /* Save FMan revision */ + fman_get_revision(tgec->fm, &tgec->fm_rev_info); + + return tgec; +} diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.h b/drivers/net/ethernet/freescale/fman/fman_tgec.h new file mode 100644 index 000000000000..514bba9f47ce --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_tgec.h @@ -0,0 +1,55 @@ +/* + * Copyright 2008-2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __TGEC_H +#define __TGEC_H + +#include "fman_mac.h" + +struct fman_mac *tgec_config(struct fman_mac_params *params); +int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val); +int tgec_modify_mac_address(struct fman_mac *tgec, enet_addr_t *enet_addr); +int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val); +int tgec_enable(struct fman_mac *tgec, enum comm_mode mode); +int tgec_disable(struct fman_mac *tgec, enum comm_mode mode); +int tgec_init(struct fman_mac *tgec); +int tgec_free(struct fman_mac *tgec); +int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en); +int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 priority, + u16 pause_time, u16 thresh_time); +int tgec_set_exception(struct fman_mac *tgec, + enum fman_mac_exceptions exception, bool enable); +int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr); +int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr); +int tgec_get_version(struct fman_mac *tgec, u32 *mac_version); + +#endif /* __TGEC_H */ -- cgit v1.2.3-59-g8ed1b From e81edc38eea52a7d707f3cebd121da2c40afc7f8 Mon Sep 17 00:00:00 2001 From: Igal Liberman Date: Mon, 21 Dec 2015 02:21:28 +0200 Subject: fsl/fman: Add FMan SP support The Storage Profiles contain parameters that are used by the FMan for frame reception and transmission. Signed-off-by: Igal Liberman Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/Makefile | 2 +- drivers/net/ethernet/freescale/fman/fman_sp.c | 166 ++++++++++++++++++++++++++ drivers/net/ethernet/freescale/fman/fman_sp.h | 103 ++++++++++++++++ 3 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/freescale/fman/fman_sp.c create mode 100644 drivers/net/ethernet/freescale/fman/fman_sp.h (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile index 43360d701e1d..5141532bbeb3 100644 --- a/drivers/net/ethernet/freescale/fman/Makefile +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -2,5 +2,5 @@ subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman obj-y += fsl_fman.o fsl_fman_mac.o -fsl_fman-objs := fman_muram.o fman.o +fsl_fman-objs := fman_muram.o fman.o fman_sp.o fsl_fman_mac-objs := fman_dtsec.o fman_memac.o fman_tgec.o diff --git a/drivers/net/ethernet/freescale/fman/fman_sp.c b/drivers/net/ethernet/freescale/fman/fman_sp.c new file mode 100644 index 000000000000..f9e7aa385cba --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_sp.c @@ -0,0 +1,166 @@ +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fman_sp.h" +#include "fman.h" + +void fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(struct fman_ext_pools + *fm_ext_pools, + u8 *ordered_array, + u16 *sizes_array) +{ + u16 buf_size = 0; + int i = 0, j = 0, k = 0; + + /* First we copy the external buffers pools information + * to an ordered local array + */ + for (i = 0; i < fm_ext_pools->num_of_pools_used; i++) { + /* get pool size */ + buf_size = fm_ext_pools->ext_buf_pool[i].size; + + /* keep sizes in an array according to poolId + * for direct access + */ + sizes_array[fm_ext_pools->ext_buf_pool[i].id] = buf_size; + + /* save poolId in an ordered array according to size */ + for (j = 0; j <= i; j++) { + /* this is the next free place in the array */ + if (j == i) + ordered_array[i] = + fm_ext_pools->ext_buf_pool[i].id; + else { + /* find the right place for this poolId */ + if (buf_size < sizes_array[ordered_array[j]]) { + /* move the pool_ids one place ahead + * to make room for this poolId + */ + for (k = i; k > j; k--) + ordered_array[k] = + ordered_array[k - 1]; + + /* now k==j, this is the place for + * the new size + */ + ordered_array[k] = + fm_ext_pools->ext_buf_pool[i].id; + break; + } + } + } + } +} + +int fman_sp_build_buffer_struct(struct fman_sp_int_context_data_copy * + int_context_data_copy, + struct fman_buffer_prefix_content * + buffer_prefix_content, + struct fman_sp_buf_margins *buf_margins, + struct fman_sp_buffer_offsets *buffer_offsets, + u8 *internal_buf_offset) +{ + u32 tmp; + + /* Align start of internal context data to 16 byte */ + int_context_data_copy->ext_buf_offset = (u16) + ((buffer_prefix_content->priv_data_size & (OFFSET_UNITS - 1)) ? + ((buffer_prefix_content->priv_data_size + OFFSET_UNITS) & + ~(u16)(OFFSET_UNITS - 1)) : + buffer_prefix_content->priv_data_size); + + /* Translate margin and int_context params to FM parameters */ + /* Initialize with illegal value. Later we'll set legal values. */ + buffer_offsets->prs_result_offset = (u32)ILLEGAL_BASE; + buffer_offsets->time_stamp_offset = (u32)ILLEGAL_BASE; + buffer_offsets->hash_result_offset = (u32)ILLEGAL_BASE; + + /* Internally the driver supports 4 options + * 1. prsResult/timestamp/hashResult selection (in fact 8 options, + * but for simplicity we'll + * relate to it as 1). + * 2. All IC context (from AD) not including debug. + */ + + /* This case covers the options under 1 */ + /* Copy size must be in 16-byte granularity. */ + int_context_data_copy->size = + (u16)((buffer_prefix_content->pass_prs_result ? 32 : 0) + + ((buffer_prefix_content->pass_time_stamp || + buffer_prefix_content->pass_hash_result) ? 16 : 0)); + + /* Align start of internal context data to 16 byte */ + int_context_data_copy->int_context_offset = + (u8)(buffer_prefix_content->pass_prs_result ? 32 : + ((buffer_prefix_content->pass_time_stamp || + buffer_prefix_content->pass_hash_result) ? 64 : 0)); + + if (buffer_prefix_content->pass_prs_result) + buffer_offsets->prs_result_offset = + int_context_data_copy->ext_buf_offset; + if (buffer_prefix_content->pass_time_stamp) + buffer_offsets->time_stamp_offset = + buffer_prefix_content->pass_prs_result ? + (int_context_data_copy->ext_buf_offset + + sizeof(struct fman_prs_result)) : + int_context_data_copy->ext_buf_offset; + if (buffer_prefix_content->pass_hash_result) + /* If PR is not requested, whether TS is + * requested or not, IC will be copied from TS + */ + buffer_offsets->hash_result_offset = + buffer_prefix_content->pass_prs_result ? + (int_context_data_copy->ext_buf_offset + + sizeof(struct fman_prs_result) + 8) : + int_context_data_copy->ext_buf_offset + 8; + + if (int_context_data_copy->size) + buf_margins->start_margins = + (u16)(int_context_data_copy->ext_buf_offset + + int_context_data_copy->size); + else + /* No Internal Context passing, STartMargin is + * immediately after private_info + */ + buf_margins->start_margins = + buffer_prefix_content->priv_data_size; + + /* align data start */ + tmp = (u32)(buf_margins->start_margins % + buffer_prefix_content->data_align); + if (tmp) + buf_margins->start_margins += + (buffer_prefix_content->data_align - tmp); + buffer_offsets->data_offset = buf_margins->start_margins; + + return 0; +} diff --git a/drivers/net/ethernet/freescale/fman/fman_sp.h b/drivers/net/ethernet/freescale/fman/fman_sp.h new file mode 100644 index 000000000000..820b7f63088f --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_sp.h @@ -0,0 +1,103 @@ +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __FM_SP_H +#define __FM_SP_H + +#include "fman.h" +#include + +#define ILLEGAL_BASE (~0) + +/* defaults */ +#define DFLT_FM_SP_BUFFER_PREFIX_CONTEXT_DATA_ALIGN 64 + +/* Registers bit fields */ +#define FMAN_SP_EXT_BUF_POOL_EN_COUNTER 0x40000000 +#define FMAN_SP_EXT_BUF_POOL_VALID 0x80000000 +#define FMAN_SP_EXT_BUF_POOL_BACKUP 0x20000000 +#define FMAN_SP_DMA_ATTR_WRITE_OPTIMIZE 0x00100000 +#define FMAN_SP_SG_DISABLE 0x80000000 + +/* shifts */ +#define FMAN_SP_EXT_BUF_MARG_START_SHIFT 16 +#define FMAN_SP_DMA_ATTR_SWP_SHIFT 30 +#define FMAN_SP_IC_TO_EXT_SHIFT 16 +#define FMAN_SP_IC_FROM_INT_SHIFT 8 + +/* structure for defining internal context copying */ +struct fman_sp_int_context_data_copy { + /* < Offset in External buffer to which internal + * context is copied to (Rx) or taken from (Tx, Op). + */ + u16 ext_buf_offset; + /* Offset within internal context to copy from + * (Rx) or to copy to (Tx, Op). + */ + u8 int_context_offset; + /* Internal offset size to be copied */ + u16 size; +}; + +/* struct for defining external buffer margins */ +struct fman_sp_buf_margins { + /* Number of bytes to be left at the beginning + * of the external buffer (must be divisible by 16) + */ + u16 start_margins; + /* number of bytes to be left at the end + * of the external buffer(must be divisible by 16) + */ + u16 end_margins; +}; + +struct fman_sp_buffer_offsets { + u32 data_offset; + u32 prs_result_offset; + u32 time_stamp_offset; + u32 hash_result_offset; +}; + +int fman_sp_build_buffer_struct(struct fman_sp_int_context_data_copy + *int_context_data_copy, + struct fman_buffer_prefix_content + *buffer_prefix_content, + struct fman_sp_buf_margins *buf_margins, + struct fman_sp_buffer_offsets + *buffer_offsets, + u8 *internal_buf_offset); + +void fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(struct fman_ext_pools + *fm_ext_pools, + u8 *ordered_array, + u16 *sizes_array); + +#endif /* __FM_SP_H */ -- cgit v1.2.3-59-g8ed1b From 18a6c85fcc78ddb63b0646a302e5ec8d6a9e29d6 Mon Sep 17 00:00:00 2001 From: Igal Liberman Date: Mon, 21 Dec 2015 02:21:29 +0200 Subject: fsl/fman: Add FMan Port Support Add the Data Path Acceleration Architecture Frame Manger Port Driver. The FMan driver uses a module called "Port" to represent the physical TX and RX ports. Each FMan version has different number of physical ports. This patch adds The FMan Port configuration, initialization and runtime control routines for both TX and RX. Signed-off-by: Igal Liberman Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/Makefile | 2 +- drivers/net/ethernet/freescale/fman/fman_port.c | 1778 +++++++++++++++++++++++ drivers/net/ethernet/freescale/fman/fman_port.h | 151 ++ 3 files changed, 1930 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/freescale/fman/fman_port.c create mode 100644 drivers/net/ethernet/freescale/fman/fman_port.h (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile index 5141532bbeb3..2eb0b9b5c76b 100644 --- a/drivers/net/ethernet/freescale/fman/Makefile +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -2,5 +2,5 @@ subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman obj-y += fsl_fman.o fsl_fman_mac.o -fsl_fman-objs := fman_muram.o fman.o fman_sp.o +fsl_fman-objs := fman_muram.o fman.o fman_sp.o fman_port.o fsl_fman_mac-objs := fman_dtsec.o fman_memac.o fman_tgec.o diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c new file mode 100644 index 000000000000..70c198d072dc --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_port.c @@ -0,0 +1,1778 @@ +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fman_port.h" +#include "fman.h" +#include "fman_sp.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Queue ID */ +#define DFLT_FQ_ID 0x00FFFFFF + +/* General defines */ +#define PORT_BMI_FIFO_UNITS 0x100 + +#define MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) \ + min((u32)bmi_max_fifo_size, (u32)1024 * FMAN_BMI_FIFO_UNITS) + +#define PORT_CG_MAP_NUM 8 +#define PORT_PRS_RESULT_WORDS_NUM 8 +#define PORT_IC_OFFSET_UNITS 0x10 + +#define MIN_EXT_BUF_SIZE 64 + +#define BMI_PORT_REGS_OFFSET 0 +#define QMI_PORT_REGS_OFFSET 0x400 + +/* Default values */ +#define DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN \ + DFLT_FM_SP_BUFFER_PREFIX_CONTEXT_DATA_ALIGN + +#define DFLT_PORT_CUT_BYTES_FROM_END 4 + +#define DFLT_PORT_ERRORS_TO_DISCARD FM_PORT_FRM_ERR_CLS_DISCARD +#define DFLT_PORT_MAX_FRAME_LENGTH 9600 + +#define DFLT_PORT_RX_FIFO_PRI_ELEVATION_LEV(bmi_max_fifo_size) \ + MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) + +#define DFLT_PORT_RX_FIFO_THRESHOLD(major, bmi_max_fifo_size) \ + (major == 6 ? \ + MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) : \ + (MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) * 3 / 4)) \ + +#define DFLT_PORT_EXTRA_NUM_OF_FIFO_BUFS 0 + +/* QMI defines */ +#define QMI_DEQ_CFG_SUBPORTAL_MASK 0x1f + +#define QMI_PORT_CFG_EN 0x80000000 +#define QMI_PORT_STATUS_DEQ_FD_BSY 0x20000000 + +#define QMI_DEQ_CFG_PRI 0x80000000 +#define QMI_DEQ_CFG_TYPE1 0x10000000 +#define QMI_DEQ_CFG_TYPE2 0x20000000 +#define QMI_DEQ_CFG_TYPE3 0x30000000 +#define QMI_DEQ_CFG_PREFETCH_PARTIAL 0x01000000 +#define QMI_DEQ_CFG_PREFETCH_FULL 0x03000000 +#define QMI_DEQ_CFG_SP_MASK 0xf +#define QMI_DEQ_CFG_SP_SHIFT 20 + +#define QMI_BYTE_COUNT_LEVEL_CONTROL(_type) \ + (_type == FMAN_PORT_TYPE_TX ? 0x1400 : 0x400) + +/* BMI defins */ +#define BMI_EBD_EN 0x80000000 + +#define BMI_PORT_CFG_EN 0x80000000 + +#define BMI_PORT_STATUS_BSY 0x80000000 + +#define BMI_DMA_ATTR_SWP_SHIFT FMAN_SP_DMA_ATTR_SWP_SHIFT +#define BMI_DMA_ATTR_WRITE_OPTIMIZE FMAN_SP_DMA_ATTR_WRITE_OPTIMIZE + +#define BMI_RX_FIFO_PRI_ELEVATION_SHIFT 16 +#define BMI_RX_FIFO_THRESHOLD_ETHE 0x80000000 + +#define BMI_FRAME_END_CS_IGNORE_SHIFT 24 +#define BMI_FRAME_END_CS_IGNORE_MASK 0x0000001f + +#define BMI_RX_FRAME_END_CUT_SHIFT 16 +#define BMI_RX_FRAME_END_CUT_MASK 0x0000001f + +#define BMI_IC_TO_EXT_SHIFT FMAN_SP_IC_TO_EXT_SHIFT +#define BMI_IC_TO_EXT_MASK 0x0000001f +#define BMI_IC_FROM_INT_SHIFT FMAN_SP_IC_FROM_INT_SHIFT +#define BMI_IC_FROM_INT_MASK 0x0000000f +#define BMI_IC_SIZE_MASK 0x0000001f + +#define BMI_INT_BUF_MARG_SHIFT 28 +#define BMI_INT_BUF_MARG_MASK 0x0000000f +#define BMI_EXT_BUF_MARG_START_SHIFT FMAN_SP_EXT_BUF_MARG_START_SHIFT +#define BMI_EXT_BUF_MARG_START_MASK 0x000001ff +#define BMI_EXT_BUF_MARG_END_MASK 0x000001ff + +#define BMI_CMD_MR_LEAC 0x00200000 +#define BMI_CMD_MR_SLEAC 0x00100000 +#define BMI_CMD_MR_MA 0x00080000 +#define BMI_CMD_MR_DEAS 0x00040000 +#define BMI_CMD_RX_MR_DEF (BMI_CMD_MR_LEAC | \ + BMI_CMD_MR_SLEAC | \ + BMI_CMD_MR_MA | \ + BMI_CMD_MR_DEAS) +#define BMI_CMD_TX_MR_DEF 0 + +#define BMI_CMD_ATTR_ORDER 0x80000000 +#define BMI_CMD_ATTR_SYNC 0x02000000 +#define BMI_CMD_ATTR_COLOR_SHIFT 26 + +#define BMI_FIFO_PIPELINE_DEPTH_SHIFT 12 +#define BMI_FIFO_PIPELINE_DEPTH_MASK 0x0000000f +#define BMI_NEXT_ENG_FD_BITS_SHIFT 24 + +#define BMI_EXT_BUF_POOL_VALID FMAN_SP_EXT_BUF_POOL_VALID +#define BMI_EXT_BUF_POOL_EN_COUNTER FMAN_SP_EXT_BUF_POOL_EN_COUNTER +#define BMI_EXT_BUF_POOL_BACKUP FMAN_SP_EXT_BUF_POOL_BACKUP +#define BMI_EXT_BUF_POOL_ID_SHIFT 16 +#define BMI_EXT_BUF_POOL_ID_MASK 0x003F0000 +#define BMI_POOL_DEP_NUM_OF_POOLS_SHIFT 16 + +#define BMI_TX_FIFO_MIN_FILL_SHIFT 16 + +#define BMI_PRIORITY_ELEVATION_LEVEL ((0x3FF + 1) * PORT_BMI_FIFO_UNITS) +#define BMI_FIFO_THRESHOLD ((0x3FF + 1) * PORT_BMI_FIFO_UNITS) + +#define BMI_DEQUEUE_PIPELINE_DEPTH(_type, _speed) \ + ((_type == FMAN_PORT_TYPE_TX && _speed == 10000) ? 4 : 1) + +#define RX_ERRS_TO_ENQ \ + (FM_PORT_FRM_ERR_DMA | \ + FM_PORT_FRM_ERR_PHYSICAL | \ + FM_PORT_FRM_ERR_SIZE | \ + FM_PORT_FRM_ERR_EXTRACTION | \ + FM_PORT_FRM_ERR_NO_SCHEME | \ + FM_PORT_FRM_ERR_PRS_TIMEOUT | \ + FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT | \ + FM_PORT_FRM_ERR_BLOCK_LIMIT_EXCEEDED | \ + FM_PORT_FRM_ERR_PRS_HDR_ERR | \ + FM_PORT_FRM_ERR_KEYSIZE_OVERFLOW | \ + FM_PORT_FRM_ERR_IPRE) + +/* NIA defines */ +#define NIA_ORDER_RESTOR 0x00800000 +#define NIA_ENG_BMI 0x00500000 +#define NIA_ENG_QMI_ENQ 0x00540000 +#define NIA_ENG_QMI_DEQ 0x00580000 + +#define NIA_BMI_AC_ENQ_FRAME 0x00000002 +#define NIA_BMI_AC_TX_RELEASE 0x000002C0 +#define NIA_BMI_AC_RELEASE 0x000000C0 +#define NIA_BMI_AC_TX 0x00000274 +#define NIA_BMI_AC_FETCH_ALL_FRAME 0x0000020c + +/* Port IDs */ +#define TX_10G_PORT_BASE 0x30 +#define RX_10G_PORT_BASE 0x10 + +/* BMI Rx port register map */ +struct fman_port_rx_bmi_regs { + u32 fmbm_rcfg; /* Rx Configuration */ + u32 fmbm_rst; /* Rx Status */ + u32 fmbm_rda; /* Rx DMA attributes */ + u32 fmbm_rfp; /* Rx FIFO Parameters */ + u32 fmbm_rfed; /* Rx Frame End Data */ + u32 fmbm_ricp; /* Rx Internal Context Parameters */ + u32 fmbm_rim; /* Rx Internal Buffer Margins */ + u32 fmbm_rebm; /* Rx External Buffer Margins */ + u32 fmbm_rfne; /* Rx Frame Next Engine */ + u32 fmbm_rfca; /* Rx Frame Command Attributes. */ + u32 fmbm_rfpne; /* Rx Frame Parser Next Engine */ + u32 fmbm_rpso; /* Rx Parse Start Offset */ + u32 fmbm_rpp; /* Rx Policer Profile */ + u32 fmbm_rccb; /* Rx Coarse Classification Base */ + u32 fmbm_reth; /* Rx Excessive Threshold */ + u32 reserved003c[1]; /* (0x03C 0x03F) */ + u32 fmbm_rprai[PORT_PRS_RESULT_WORDS_NUM]; + /* Rx Parse Results Array Init */ + u32 fmbm_rfqid; /* Rx Frame Queue ID */ + u32 fmbm_refqid; /* Rx Error Frame Queue ID */ + u32 fmbm_rfsdm; /* Rx Frame Status Discard Mask */ + u32 fmbm_rfsem; /* Rx Frame Status Error Mask */ + u32 fmbm_rfene; /* Rx Frame Enqueue Next Engine */ + u32 reserved0074[0x2]; /* (0x074-0x07C) */ + u32 fmbm_rcmne; /* Rx Frame Continuous Mode Next Engine */ + u32 reserved0080[0x20]; /* (0x080 0x0FF) */ + u32 fmbm_ebmpi[FMAN_PORT_MAX_EXT_POOLS_NUM]; + /* Buffer Manager pool Information- */ + u32 fmbm_acnt[FMAN_PORT_MAX_EXT_POOLS_NUM]; /* Allocate Counter- */ + u32 reserved0130[8]; /* 0x130/0x140 - 0x15F reserved - */ + u32 fmbm_rcgm[PORT_CG_MAP_NUM]; /* Congestion Group Map */ + u32 fmbm_mpd; /* BM Pool Depletion */ + u32 reserved0184[0x1F]; /* (0x184 0x1FF) */ + u32 fmbm_rstc; /* Rx Statistics Counters */ + u32 fmbm_rfrc; /* Rx Frame Counter */ + u32 fmbm_rfbc; /* Rx Bad Frames Counter */ + u32 fmbm_rlfc; /* Rx Large Frames Counter */ + u32 fmbm_rffc; /* Rx Filter Frames Counter */ + u32 fmbm_rfdc; /* Rx Frame Discard Counter */ + u32 fmbm_rfldec; /* Rx Frames List DMA Error Counter */ + u32 fmbm_rodc; /* Rx Out of Buffers Discard nntr */ + u32 fmbm_rbdc; /* Rx Buffers Deallocate Counter */ + u32 fmbm_rpec; /* RX Prepare to enqueue Counte */ + u32 reserved0224[0x16]; /* (0x224 0x27F) */ + u32 fmbm_rpc; /* Rx Performance Counters */ + u32 fmbm_rpcp; /* Rx Performance Count Parameters */ + u32 fmbm_rccn; /* Rx Cycle Counter */ + u32 fmbm_rtuc; /* Rx Tasks Utilization Counter */ + u32 fmbm_rrquc; /* Rx Receive Queue Utilization cntr */ + u32 fmbm_rduc; /* Rx DMA Utilization Counter */ + u32 fmbm_rfuc; /* Rx FIFO Utilization Counter */ + u32 fmbm_rpac; /* Rx Pause Activation Counter */ + u32 reserved02a0[0x18]; /* (0x2A0 0x2FF) */ + u32 fmbm_rdcfg[0x3]; /* Rx Debug Configuration */ + u32 fmbm_rgpr; /* Rx General Purpose Register */ + u32 reserved0310[0x3a]; +}; + +/* BMI Tx port register map */ +struct fman_port_tx_bmi_regs { + u32 fmbm_tcfg; /* Tx Configuration */ + u32 fmbm_tst; /* Tx Status */ + u32 fmbm_tda; /* Tx DMA attributes */ + u32 fmbm_tfp; /* Tx FIFO Parameters */ + u32 fmbm_tfed; /* Tx Frame End Data */ + u32 fmbm_ticp; /* Tx Internal Context Parameters */ + u32 fmbm_tfdne; /* Tx Frame Dequeue Next Engine. */ + u32 fmbm_tfca; /* Tx Frame Command attribute. */ + u32 fmbm_tcfqid; /* Tx Confirmation Frame Queue ID. */ + u32 fmbm_tefqid; /* Tx Frame Error Queue ID */ + u32 fmbm_tfene; /* Tx Frame Enqueue Next Engine */ + u32 fmbm_trlmts; /* Tx Rate Limiter Scale */ + u32 fmbm_trlmt; /* Tx Rate Limiter */ + u32 reserved0034[0x0e]; /* (0x034-0x6c) */ + u32 fmbm_tccb; /* Tx Coarse Classification base */ + u32 fmbm_tfne; /* Tx Frame Next Engine */ + u32 fmbm_tpfcm[0x02]; + /* Tx Priority based Flow Control (PFC) Mapping */ + u32 fmbm_tcmne; /* Tx Frame Continuous Mode Next Engine */ + u32 reserved0080[0x60]; /* (0x080-0x200) */ + u32 fmbm_tstc; /* Tx Statistics Counters */ + u32 fmbm_tfrc; /* Tx Frame Counter */ + u32 fmbm_tfdc; /* Tx Frames Discard Counter */ + u32 fmbm_tfledc; /* Tx Frame len error discard cntr */ + u32 fmbm_tfufdc; /* Tx Frame unsprt frmt discard cntr */ + u32 fmbm_tbdc; /* Tx Buffers Deallocate Counter */ + u32 reserved0218[0x1A]; /* (0x218-0x280) */ + u32 fmbm_tpc; /* Tx Performance Counters */ + u32 fmbm_tpcp; /* Tx Performance Count Parameters */ + u32 fmbm_tccn; /* Tx Cycle Counter */ + u32 fmbm_ttuc; /* Tx Tasks Utilization Counter */ + u32 fmbm_ttcquc; /* Tx Transmit conf Q util Counter */ + u32 fmbm_tduc; /* Tx DMA Utilization Counter */ + u32 fmbm_tfuc; /* Tx FIFO Utilization Counter */ + u32 reserved029c[16]; /* (0x29C-0x2FF) */ + u32 fmbm_tdcfg[0x3]; /* Tx Debug Configuration */ + u32 fmbm_tgpr; /* Tx General Purpose Register */ + u32 reserved0310[0x3a]; /* (0x310-0x3FF) */ +}; + +/* BMI port register map */ +union fman_port_bmi_regs { + struct fman_port_rx_bmi_regs rx; + struct fman_port_tx_bmi_regs tx; +}; + +/* QMI port register map */ +struct fman_port_qmi_regs { + u32 fmqm_pnc; /* PortID n Configuration Register */ + u32 fmqm_pns; /* PortID n Status Register */ + u32 fmqm_pnts; /* PortID n Task Status Register */ + u32 reserved00c[4]; /* 0xn00C - 0xn01B */ + u32 fmqm_pnen; /* PortID n Enqueue NIA Register */ + u32 fmqm_pnetfc; /* PortID n Enq Total Frame Counter */ + u32 reserved024[2]; /* 0xn024 - 0x02B */ + u32 fmqm_pndn; /* PortID n Dequeue NIA Register */ + u32 fmqm_pndc; /* PortID n Dequeue Config Register */ + u32 fmqm_pndtfc; /* PortID n Dequeue tot Frame cntr */ + u32 fmqm_pndfdc; /* PortID n Dequeue FQID Dflt Cntr */ + u32 fmqm_pndcc; /* PortID n Dequeue Confirm Counter */ +}; + +/* QMI dequeue prefetch modes */ +enum fman_port_deq_prefetch { + FMAN_PORT_DEQ_NO_PREFETCH, /* No prefetch mode */ + FMAN_PORT_DEQ_PART_PREFETCH, /* Partial prefetch mode */ + FMAN_PORT_DEQ_FULL_PREFETCH /* Full prefetch mode */ +}; + +/* A structure for defining FM port resources */ +struct fman_port_rsrc { + u32 num; /* Committed required resource */ + u32 extra; /* Extra (not committed) required resource */ +}; + +enum fman_port_dma_swap { + FMAN_PORT_DMA_NO_SWAP, /* No swap, transfer data as is */ + FMAN_PORT_DMA_SWAP_LE, + /* The transferred data should be swapped in PPC Little Endian mode */ + FMAN_PORT_DMA_SWAP_BE + /* The transferred data should be swapped in Big Endian mode */ +}; + +/* Default port color */ +enum fman_port_color { + FMAN_PORT_COLOR_GREEN, /* Default port color is green */ + FMAN_PORT_COLOR_YELLOW, /* Default port color is yellow */ + FMAN_PORT_COLOR_RED, /* Default port color is red */ + FMAN_PORT_COLOR_OVERRIDE /* Ignore color */ +}; + +/* QMI dequeue from the SP channel - types */ +enum fman_port_deq_type { + FMAN_PORT_DEQ_BY_PRI, + /* Priority precedence and Intra-Class scheduling */ + FMAN_PORT_DEQ_ACTIVE_FQ, + /* Active FQ precedence and Intra-Class scheduling */ + FMAN_PORT_DEQ_ACTIVE_FQ_NO_ICS + /* Active FQ precedence and override Intra-Class scheduling */ +}; + +/* External buffer pools configuration */ +struct fman_port_bpools { + u8 count; /* Num of pools to set up */ + bool counters_enable; /* Enable allocate counters */ + u8 grp_bp_depleted_num; + /* Number of depleted pools - if reached the BMI indicates + * the MAC to send a pause frame + */ + struct { + u8 bpid; /* BM pool ID */ + u16 size; + /* Pool's size - must be in ascending order */ + bool is_backup; + /* If this is a backup pool */ + bool grp_bp_depleted; + /* Consider this buffer in multiple pools depletion criteria */ + bool single_bp_depleted; + /* Consider this buffer in single pool depletion criteria */ + } bpool[FMAN_PORT_MAX_EXT_POOLS_NUM]; +}; + +struct fman_port_cfg { + u32 dflt_fqid; + u32 err_fqid; + u8 deq_sp; + bool deq_high_priority; + enum fman_port_deq_type deq_type; + enum fman_port_deq_prefetch deq_prefetch_option; + u16 deq_byte_cnt; + u8 cheksum_last_bytes_ignore; + u8 rx_cut_end_bytes; + struct fman_buf_pool_depletion buf_pool_depletion; + struct fman_ext_pools ext_buf_pools; + u32 tx_fifo_min_level; + u32 tx_fifo_low_comf_level; + u32 rx_pri_elevation; + u32 rx_fifo_thr; + struct fman_sp_buf_margins buf_margins; + u32 int_buf_start_margin; + struct fman_sp_int_context_data_copy int_context; + u32 discard_mask; + u32 err_mask; + struct fman_buffer_prefix_content buffer_prefix_content; + bool dont_release_buf; + + u8 rx_fd_bits; + u32 tx_fifo_deq_pipeline_depth; + bool errata_A006320; + bool excessive_threshold_register; + bool fmbm_tfne_has_features; + + enum fman_port_dma_swap dma_swap_data; + enum fman_port_color color; +}; + +struct fman_port_rx_pools_params { + u8 num_of_pools; + u16 second_largest_buf_size; + u16 largest_buf_size; +}; + +struct fman_port_dts_params { + void __iomem *base_addr; /* FMan port virtual memory */ + enum fman_port_type type; /* Port type */ + u16 speed; /* Port speed */ + u8 id; /* HW Port Id */ + u32 qman_channel_id; /* QMan channel id (non RX only) */ + struct fman *fman; /* FMan Handle */ +}; + +struct fman_port { + void *fm; + struct device *dev; + struct fman_rev_info rev_info; + u8 port_id; + enum fman_port_type port_type; + u16 port_speed; + + union fman_port_bmi_regs __iomem *bmi_regs; + struct fman_port_qmi_regs __iomem *qmi_regs; + + struct fman_sp_buffer_offsets buffer_offsets; + + u8 internal_buf_offset; + struct fman_ext_pools ext_buf_pools; + + u16 max_frame_length; + struct fman_port_rsrc open_dmas; + struct fman_port_rsrc tasks; + struct fman_port_rsrc fifo_bufs; + struct fman_port_rx_pools_params rx_pools_params; + + struct fman_port_cfg *cfg; + struct fman_port_dts_params dts_params; + + u8 ext_pools_num; + u32 max_port_fifo_size; + u32 max_num_of_ext_pools; + u32 max_num_of_sub_portals; + u32 bm_max_num_of_pools; +}; + +static int init_bmi_rx(struct fman_port *port) +{ + struct fman_port_rx_bmi_regs __iomem *regs = &port->bmi_regs->rx; + struct fman_port_cfg *cfg = port->cfg; + u32 tmp; + + /* DMA attributes */ + tmp = (u32)cfg->dma_swap_data << BMI_DMA_ATTR_SWP_SHIFT; + /* Enable write optimization */ + tmp |= BMI_DMA_ATTR_WRITE_OPTIMIZE; + iowrite32be(tmp, ®s->fmbm_rda); + + /* Rx FIFO parameters */ + tmp = (cfg->rx_pri_elevation / PORT_BMI_FIFO_UNITS - 1) << + BMI_RX_FIFO_PRI_ELEVATION_SHIFT; + tmp |= cfg->rx_fifo_thr / PORT_BMI_FIFO_UNITS - 1; + iowrite32be(tmp, ®s->fmbm_rfp); + + if (cfg->excessive_threshold_register) + /* always allow access to the extra resources */ + iowrite32be(BMI_RX_FIFO_THRESHOLD_ETHE, ®s->fmbm_reth); + + /* Frame end data */ + tmp = (cfg->cheksum_last_bytes_ignore & BMI_FRAME_END_CS_IGNORE_MASK) << + BMI_FRAME_END_CS_IGNORE_SHIFT; + tmp |= (cfg->rx_cut_end_bytes & BMI_RX_FRAME_END_CUT_MASK) << + BMI_RX_FRAME_END_CUT_SHIFT; + if (cfg->errata_A006320) + tmp &= 0xffe0ffff; + iowrite32be(tmp, ®s->fmbm_rfed); + + /* Internal context parameters */ + tmp = ((cfg->int_context.ext_buf_offset / PORT_IC_OFFSET_UNITS) & + BMI_IC_TO_EXT_MASK) << BMI_IC_TO_EXT_SHIFT; + tmp |= ((cfg->int_context.int_context_offset / PORT_IC_OFFSET_UNITS) & + BMI_IC_FROM_INT_MASK) << BMI_IC_FROM_INT_SHIFT; + tmp |= (cfg->int_context.size / PORT_IC_OFFSET_UNITS) & + BMI_IC_SIZE_MASK; + iowrite32be(tmp, ®s->fmbm_ricp); + + /* Internal buffer offset */ + tmp = ((cfg->int_buf_start_margin / PORT_IC_OFFSET_UNITS) & + BMI_INT_BUF_MARG_MASK) << BMI_INT_BUF_MARG_SHIFT; + iowrite32be(tmp, ®s->fmbm_rim); + + /* External buffer margins */ + tmp = (cfg->buf_margins.start_margins & BMI_EXT_BUF_MARG_START_MASK) << + BMI_EXT_BUF_MARG_START_SHIFT; + tmp |= cfg->buf_margins.end_margins & BMI_EXT_BUF_MARG_END_MASK; + iowrite32be(tmp, ®s->fmbm_rebm); + + /* Frame attributes */ + tmp = BMI_CMD_RX_MR_DEF; + tmp |= BMI_CMD_ATTR_ORDER; + tmp |= (u32)cfg->color << BMI_CMD_ATTR_COLOR_SHIFT; + /* Synchronization request */ + tmp |= BMI_CMD_ATTR_SYNC; + + iowrite32be(tmp, ®s->fmbm_rfca); + + /* NIA */ + tmp = (u32)cfg->rx_fd_bits << BMI_NEXT_ENG_FD_BITS_SHIFT; + + tmp |= NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME; + iowrite32be(tmp, ®s->fmbm_rfne); + + /* Enqueue NIA */ + iowrite32be(NIA_ENG_QMI_ENQ | NIA_ORDER_RESTOR, ®s->fmbm_rfene); + + /* Default/error queues */ + iowrite32be((cfg->dflt_fqid & DFLT_FQ_ID), ®s->fmbm_rfqid); + iowrite32be((cfg->err_fqid & DFLT_FQ_ID), ®s->fmbm_refqid); + + /* Discard/error masks */ + iowrite32be(cfg->discard_mask, ®s->fmbm_rfsdm); + iowrite32be(cfg->err_mask, ®s->fmbm_rfsem); + + return 0; +} + +static int init_bmi_tx(struct fman_port *port) +{ + struct fman_port_tx_bmi_regs __iomem *regs = &port->bmi_regs->tx; + struct fman_port_cfg *cfg = port->cfg; + u32 tmp; + + /* Tx Configuration register */ + tmp = 0; + iowrite32be(tmp, ®s->fmbm_tcfg); + + /* DMA attributes */ + tmp = (u32)cfg->dma_swap_data << BMI_DMA_ATTR_SWP_SHIFT; + iowrite32be(tmp, ®s->fmbm_tda); + + /* Tx FIFO parameters */ + tmp = (cfg->tx_fifo_min_level / PORT_BMI_FIFO_UNITS) << + BMI_TX_FIFO_MIN_FILL_SHIFT; + tmp |= ((cfg->tx_fifo_deq_pipeline_depth - 1) & + BMI_FIFO_PIPELINE_DEPTH_MASK) << BMI_FIFO_PIPELINE_DEPTH_SHIFT; + tmp |= (cfg->tx_fifo_low_comf_level / PORT_BMI_FIFO_UNITS) - 1; + iowrite32be(tmp, ®s->fmbm_tfp); + + /* Frame end data */ + tmp = (cfg->cheksum_last_bytes_ignore & BMI_FRAME_END_CS_IGNORE_MASK) << + BMI_FRAME_END_CS_IGNORE_SHIFT; + iowrite32be(tmp, ®s->fmbm_tfed); + + /* Internal context parameters */ + tmp = ((cfg->int_context.ext_buf_offset / PORT_IC_OFFSET_UNITS) & + BMI_IC_TO_EXT_MASK) << BMI_IC_TO_EXT_SHIFT; + tmp |= ((cfg->int_context.int_context_offset / PORT_IC_OFFSET_UNITS) & + BMI_IC_FROM_INT_MASK) << BMI_IC_FROM_INT_SHIFT; + tmp |= (cfg->int_context.size / PORT_IC_OFFSET_UNITS) & + BMI_IC_SIZE_MASK; + iowrite32be(tmp, ®s->fmbm_ticp); + + /* Frame attributes */ + tmp = BMI_CMD_TX_MR_DEF; + tmp |= BMI_CMD_ATTR_ORDER; + tmp |= (u32)cfg->color << BMI_CMD_ATTR_COLOR_SHIFT; + iowrite32be(tmp, ®s->fmbm_tfca); + + /* Dequeue NIA + enqueue NIA */ + iowrite32be(NIA_ENG_QMI_DEQ, ®s->fmbm_tfdne); + iowrite32be(NIA_ENG_QMI_ENQ | NIA_ORDER_RESTOR, ®s->fmbm_tfene); + if (cfg->fmbm_tfne_has_features) + iowrite32be(!cfg->dflt_fqid ? + BMI_EBD_EN | NIA_BMI_AC_FETCH_ALL_FRAME : + NIA_BMI_AC_FETCH_ALL_FRAME, ®s->fmbm_tfne); + if (!cfg->dflt_fqid && cfg->dont_release_buf) { + iowrite32be(DFLT_FQ_ID, ®s->fmbm_tcfqid); + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE, + ®s->fmbm_tfene); + if (cfg->fmbm_tfne_has_features) + iowrite32be(ioread32be(®s->fmbm_tfne) & ~BMI_EBD_EN, + ®s->fmbm_tfne); + } + + /* Confirmation/error queues */ + if (cfg->dflt_fqid || !cfg->dont_release_buf) + iowrite32be(cfg->dflt_fqid & DFLT_FQ_ID, ®s->fmbm_tcfqid); + iowrite32be((cfg->err_fqid & DFLT_FQ_ID), ®s->fmbm_tefqid); + + return 0; +} + +static int init_qmi(struct fman_port *port) +{ + struct fman_port_qmi_regs __iomem *regs = port->qmi_regs; + struct fman_port_cfg *cfg = port->cfg; + u32 tmp; + + /* Rx port configuration */ + if (port->port_type == FMAN_PORT_TYPE_RX) { + /* Enqueue NIA */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_RELEASE, ®s->fmqm_pnen); + return 0; + } + + /* Continue with Tx port configuration */ + if (port->port_type == FMAN_PORT_TYPE_TX) { + /* Enqueue NIA */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE, + ®s->fmqm_pnen); + /* Dequeue NIA */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX, ®s->fmqm_pndn); + } + + /* Dequeue Configuration register */ + tmp = 0; + if (cfg->deq_high_priority) + tmp |= QMI_DEQ_CFG_PRI; + + switch (cfg->deq_type) { + case FMAN_PORT_DEQ_BY_PRI: + tmp |= QMI_DEQ_CFG_TYPE1; + break; + case FMAN_PORT_DEQ_ACTIVE_FQ: + tmp |= QMI_DEQ_CFG_TYPE2; + break; + case FMAN_PORT_DEQ_ACTIVE_FQ_NO_ICS: + tmp |= QMI_DEQ_CFG_TYPE3; + break; + default: + return -EINVAL; + } + + switch (cfg->deq_prefetch_option) { + case FMAN_PORT_DEQ_NO_PREFETCH: + break; + case FMAN_PORT_DEQ_PART_PREFETCH: + tmp |= QMI_DEQ_CFG_PREFETCH_PARTIAL; + break; + case FMAN_PORT_DEQ_FULL_PREFETCH: + tmp |= QMI_DEQ_CFG_PREFETCH_FULL; + break; + default: + return -EINVAL; + } + + tmp |= (cfg->deq_sp & QMI_DEQ_CFG_SP_MASK) << QMI_DEQ_CFG_SP_SHIFT; + tmp |= cfg->deq_byte_cnt; + iowrite32be(tmp, ®s->fmqm_pndc); + + return 0; +} + +static int init(struct fman_port *port) +{ + int err; + + /* Init BMI registers */ + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + err = init_bmi_rx(port); + break; + case FMAN_PORT_TYPE_TX: + err = init_bmi_tx(port); + break; + default: + return -EINVAL; + } + + if (err) + return err; + + /* Init QMI registers */ + err = init_qmi(port); + return err; + + return 0; +} + +static int set_bpools(const struct fman_port *port, + const struct fman_port_bpools *bp) +{ + u32 __iomem *bp_reg, *bp_depl_reg; + u32 tmp; + u8 i, max_bp_num; + bool grp_depl_used = false, rx_port; + + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + max_bp_num = port->ext_pools_num; + rx_port = true; + bp_reg = port->bmi_regs->rx.fmbm_ebmpi; + bp_depl_reg = &port->bmi_regs->rx.fmbm_mpd; + break; + default: + return -EINVAL; + } + + if (rx_port) { + /* Check buffers are provided in ascending order */ + for (i = 0; (i < (bp->count - 1) && + (i < FMAN_PORT_MAX_EXT_POOLS_NUM - 1)); i++) { + if (bp->bpool[i].size > bp->bpool[i + 1].size) + return -EINVAL; + } + } + + /* Set up external buffers pools */ + for (i = 0; i < bp->count; i++) { + tmp = BMI_EXT_BUF_POOL_VALID; + tmp |= ((u32)bp->bpool[i].bpid << + BMI_EXT_BUF_POOL_ID_SHIFT) & BMI_EXT_BUF_POOL_ID_MASK; + + if (rx_port) { + if (bp->counters_enable) + tmp |= BMI_EXT_BUF_POOL_EN_COUNTER; + + if (bp->bpool[i].is_backup) + tmp |= BMI_EXT_BUF_POOL_BACKUP; + + tmp |= (u32)bp->bpool[i].size; + } + + iowrite32be(tmp, &bp_reg[i]); + } + + /* Clear unused pools */ + for (i = bp->count; i < max_bp_num; i++) + iowrite32be(0, &bp_reg[i]); + + /* Pools depletion */ + tmp = 0; + for (i = 0; i < FMAN_PORT_MAX_EXT_POOLS_NUM; i++) { + if (bp->bpool[i].grp_bp_depleted) { + grp_depl_used = true; + tmp |= 0x80000000 >> i; + } + + if (bp->bpool[i].single_bp_depleted) + tmp |= 0x80 >> i; + } + + if (grp_depl_used) + tmp |= ((u32)bp->grp_bp_depleted_num - 1) << + BMI_POOL_DEP_NUM_OF_POOLS_SHIFT; + + iowrite32be(tmp, bp_depl_reg); + return 0; +} + +static bool is_init_done(struct fman_port_cfg *cfg) +{ + /* Checks if FMan port driver parameters were initialized */ + if (!cfg) + return true; + + return false; +} + +static int verify_size_of_fifo(struct fman_port *port) +{ + u32 min_fifo_size_required = 0, opt_fifo_size_for_b2b = 0; + + /* TX Ports */ + if (port->port_type == FMAN_PORT_TYPE_TX) { + min_fifo_size_required = (u32) + (roundup(port->max_frame_length, + FMAN_BMI_FIFO_UNITS) + (3 * FMAN_BMI_FIFO_UNITS)); + + min_fifo_size_required += + port->cfg->tx_fifo_deq_pipeline_depth * + FMAN_BMI_FIFO_UNITS; + + opt_fifo_size_for_b2b = min_fifo_size_required; + + /* Add some margin for back-to-back capability to improve + * performance, allows the hardware to pipeline new frame dma + * while the previous frame not yet transmitted. + */ + if (port->port_speed == 10000) + opt_fifo_size_for_b2b += 3 * FMAN_BMI_FIFO_UNITS; + else + opt_fifo_size_for_b2b += 2 * FMAN_BMI_FIFO_UNITS; + } + + /* RX Ports */ + else if (port->port_type == FMAN_PORT_TYPE_RX) { + if (port->rev_info.major >= 6) + min_fifo_size_required = (u32) + (roundup(port->max_frame_length, + FMAN_BMI_FIFO_UNITS) + + (5 * FMAN_BMI_FIFO_UNITS)); + /* 4 according to spec + 1 for FOF>0 */ + else + min_fifo_size_required = (u32) + (roundup(min(port->max_frame_length, + port->rx_pools_params.largest_buf_size), + FMAN_BMI_FIFO_UNITS) + + (7 * FMAN_BMI_FIFO_UNITS)); + + opt_fifo_size_for_b2b = min_fifo_size_required; + + /* Add some margin for back-to-back capability to improve + * performance,allows the hardware to pipeline new frame dma + * while the previous frame not yet transmitted. + */ + if (port->port_speed == 10000) + opt_fifo_size_for_b2b += 8 * FMAN_BMI_FIFO_UNITS; + else + opt_fifo_size_for_b2b += 3 * FMAN_BMI_FIFO_UNITS; + } + + WARN_ON(min_fifo_size_required <= 0); + WARN_ON(opt_fifo_size_for_b2b < min_fifo_size_required); + + /* Verify the size */ + if (port->fifo_bufs.num < min_fifo_size_required) + dev_dbg(port->dev, "%s: FIFO size should be enlarged to %d bytes\n", + __func__, min_fifo_size_required); + else if (port->fifo_bufs.num < opt_fifo_size_for_b2b) + dev_dbg(port->dev, "%s: For b2b processing,FIFO may be enlarged to %d bytes\n", + __func__, opt_fifo_size_for_b2b); + + return 0; +} + +static int set_ext_buffer_pools(struct fman_port *port) +{ + struct fman_ext_pools *ext_buf_pools = &port->cfg->ext_buf_pools; + struct fman_buf_pool_depletion *buf_pool_depletion = + &port->cfg->buf_pool_depletion; + u8 ordered_array[FMAN_PORT_MAX_EXT_POOLS_NUM]; + u16 sizes_array[BM_MAX_NUM_OF_POOLS]; + int i = 0, j = 0, err; + struct fman_port_bpools bpools; + + memset(&ordered_array, 0, sizeof(u8) * FMAN_PORT_MAX_EXT_POOLS_NUM); + memset(&sizes_array, 0, sizeof(u16) * BM_MAX_NUM_OF_POOLS); + memcpy(&port->ext_buf_pools, ext_buf_pools, + sizeof(struct fman_ext_pools)); + + fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(ext_buf_pools, + ordered_array, + sizes_array); + + memset(&bpools, 0, sizeof(struct fman_port_bpools)); + bpools.count = ext_buf_pools->num_of_pools_used; + bpools.counters_enable = true; + for (i = 0; i < ext_buf_pools->num_of_pools_used; i++) { + bpools.bpool[i].bpid = ordered_array[i]; + bpools.bpool[i].size = sizes_array[ordered_array[i]]; + } + + /* save pools parameters for later use */ + port->rx_pools_params.num_of_pools = ext_buf_pools->num_of_pools_used; + port->rx_pools_params.largest_buf_size = + sizes_array[ordered_array[ext_buf_pools->num_of_pools_used - 1]]; + port->rx_pools_params.second_largest_buf_size = + sizes_array[ordered_array[ext_buf_pools->num_of_pools_used - 2]]; + + /* FMBM_RMPD reg. - pool depletion */ + if (buf_pool_depletion->pools_grp_mode_enable) { + bpools.grp_bp_depleted_num = buf_pool_depletion->num_of_pools; + for (i = 0; i < port->bm_max_num_of_pools; i++) { + if (buf_pool_depletion->pools_to_consider[i]) { + for (j = 0; j < ext_buf_pools-> + num_of_pools_used; j++) { + if (i == ordered_array[j]) { + bpools.bpool[j]. + grp_bp_depleted = true; + break; + } + } + } + } + } + + if (buf_pool_depletion->single_pool_mode_enable) { + for (i = 0; i < port->bm_max_num_of_pools; i++) { + if (buf_pool_depletion-> + pools_to_consider_for_single_mode[i]) { + for (j = 0; j < ext_buf_pools-> + num_of_pools_used; j++) { + if (i == ordered_array[j]) { + bpools.bpool[j]. + single_bp_depleted = true; + break; + } + } + } + } + } + + err = set_bpools(port, &bpools); + if (err != 0) { + dev_err(port->dev, "%s: set_bpools() failed\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int init_low_level_driver(struct fman_port *port) +{ + struct fman_port_cfg *cfg = port->cfg; + u32 tmp_val; + + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + cfg->err_mask = (RX_ERRS_TO_ENQ & ~cfg->discard_mask); + break; + default: + break; + } + + tmp_val = (u32)((port->internal_buf_offset % OFFSET_UNITS) ? + (port->internal_buf_offset / OFFSET_UNITS + 1) : + (port->internal_buf_offset / OFFSET_UNITS)); + port->internal_buf_offset = (u8)(tmp_val * OFFSET_UNITS); + port->cfg->int_buf_start_margin = port->internal_buf_offset; + + if (init(port) != 0) { + dev_err(port->dev, "%s: fman port initialization failed\n", + __func__); + return -ENODEV; + } + + /* The code bellow is a trick so the FM will not release the buffer + * to BM nor will try to enqueue the frame to QM + */ + if (port->port_type == FMAN_PORT_TYPE_TX) { + if (!cfg->dflt_fqid && cfg->dont_release_buf) { + /* override fmbm_tcfqid 0 with a false non-0 value. + * This will force FM to act according to tfene. + * Otherwise, if fmbm_tcfqid is 0 the FM will release + * buffers to BM regardless of fmbm_tfene + */ + iowrite32be(0xFFFFFF, &port->bmi_regs->tx.fmbm_tcfqid); + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE, + &port->bmi_regs->tx.fmbm_tfene); + } + } + + return 0; +} + +static int fill_soc_specific_params(struct fman_port *port) +{ + u32 bmi_max_fifo_size; + + bmi_max_fifo_size = fman_get_bmi_max_fifo_size(port->fm); + port->max_port_fifo_size = MAX_PORT_FIFO_SIZE(bmi_max_fifo_size); + port->bm_max_num_of_pools = 64; + + /* P4080 - Major 2 + * P2041/P3041/P5020/P5040 - Major 3 + * Tx/Bx - Major 6 + */ + switch (port->rev_info.major) { + case 2: + case 3: + port->max_num_of_ext_pools = 4; + port->max_num_of_sub_portals = 12; + break; + + case 6: + port->max_num_of_ext_pools = 8; + port->max_num_of_sub_portals = 16; + break; + + default: + dev_err(port->dev, "%s: Unsupported FMan version\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int get_dflt_fifo_deq_pipeline_depth(u8 major, enum fman_port_type type, + u16 speed) +{ + switch (type) { + case FMAN_PORT_TYPE_RX: + case FMAN_PORT_TYPE_TX: + switch (speed) { + case 10000: + return 4; + case 1000: + if (major >= 6) + return 2; + else + return 1; + default: + return 0; + } + default: + return 0; + } +} + +static int get_dflt_num_of_tasks(u8 major, enum fman_port_type type, + u16 speed) +{ + switch (type) { + case FMAN_PORT_TYPE_RX: + case FMAN_PORT_TYPE_TX: + switch (speed) { + case 10000: + return 16; + case 1000: + if (major >= 6) + return 4; + else + return 3; + default: + return 0; + } + default: + return 0; + } +} + +static int get_dflt_extra_num_of_tasks(u8 major, enum fman_port_type type, + u16 speed) +{ + switch (type) { + case FMAN_PORT_TYPE_RX: + /* FMan V3 */ + if (major >= 6) + return 0; + + /* FMan V2 */ + if (speed == 10000) + return 8; + else + return 2; + case FMAN_PORT_TYPE_TX: + default: + return 0; + } +} + +static int get_dflt_num_of_open_dmas(u8 major, enum fman_port_type type, + u16 speed) +{ + int val; + + if (major >= 6) { + switch (type) { + case FMAN_PORT_TYPE_TX: + if (speed == 10000) + val = 12; + else + val = 3; + break; + case FMAN_PORT_TYPE_RX: + if (speed == 10000) + val = 8; + else + val = 2; + break; + default: + return 0; + } + } else { + switch (type) { + case FMAN_PORT_TYPE_TX: + case FMAN_PORT_TYPE_RX: + if (speed == 10000) + val = 8; + else + val = 1; + break; + default: + val = 0; + } + } + + return val; +} + +static int get_dflt_extra_num_of_open_dmas(u8 major, enum fman_port_type type, + u16 speed) +{ + /* FMan V3 */ + if (major >= 6) + return 0; + + /* FMan V2 */ + switch (type) { + case FMAN_PORT_TYPE_RX: + case FMAN_PORT_TYPE_TX: + if (speed == 10000) + return 8; + else + return 1; + default: + return 0; + } +} + +static int get_dflt_num_of_fifo_bufs(u8 major, enum fman_port_type type, + u16 speed) +{ + int val; + + if (major >= 6) { + switch (type) { + case FMAN_PORT_TYPE_TX: + if (speed == 10000) + val = 64; + else + val = 50; + break; + case FMAN_PORT_TYPE_RX: + if (speed == 10000) + val = 96; + else + val = 50; + break; + default: + val = 0; + } + } else { + switch (type) { + case FMAN_PORT_TYPE_TX: + if (speed == 10000) + val = 48; + else + val = 44; + break; + case FMAN_PORT_TYPE_RX: + if (speed == 10000) + val = 48; + else + val = 45; + break; + default: + val = 0; + } + } + + return val; +} + +static void set_dflt_cfg(struct fman_port *port, + struct fman_port_params *port_params) +{ + struct fman_port_cfg *cfg = port->cfg; + + cfg->dma_swap_data = FMAN_PORT_DMA_NO_SWAP; + cfg->color = FMAN_PORT_COLOR_GREEN; + cfg->rx_cut_end_bytes = DFLT_PORT_CUT_BYTES_FROM_END; + cfg->rx_pri_elevation = BMI_PRIORITY_ELEVATION_LEVEL; + cfg->rx_fifo_thr = BMI_FIFO_THRESHOLD; + cfg->tx_fifo_low_comf_level = (5 * 1024); + cfg->deq_type = FMAN_PORT_DEQ_BY_PRI; + cfg->deq_prefetch_option = FMAN_PORT_DEQ_FULL_PREFETCH; + cfg->tx_fifo_deq_pipeline_depth = + BMI_DEQUEUE_PIPELINE_DEPTH(port->port_type, port->port_speed); + cfg->deq_byte_cnt = QMI_BYTE_COUNT_LEVEL_CONTROL(port->port_type); + + cfg->rx_pri_elevation = + DFLT_PORT_RX_FIFO_PRI_ELEVATION_LEV(port->max_port_fifo_size); + port->cfg->rx_fifo_thr = + DFLT_PORT_RX_FIFO_THRESHOLD(port->rev_info.major, + port->max_port_fifo_size); + + if ((port->rev_info.major == 6) && + ((port->rev_info.minor == 0) || (port->rev_info.minor == 3))) + cfg->errata_A006320 = true; + + /* Excessive Threshold register - exists for pre-FMv3 chips only */ + if (port->rev_info.major < 6) + cfg->excessive_threshold_register = true; + else + cfg->fmbm_tfne_has_features = true; + + cfg->buffer_prefix_content.data_align = + DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN; +} + +static void set_rx_dflt_cfg(struct fman_port *port, + struct fman_port_params *port_params) +{ + port->cfg->discard_mask = DFLT_PORT_ERRORS_TO_DISCARD; + + memcpy(&port->cfg->ext_buf_pools, + &port_params->specific_params.rx_params.ext_buf_pools, + sizeof(struct fman_ext_pools)); + port->cfg->err_fqid = + port_params->specific_params.rx_params.err_fqid; + port->cfg->dflt_fqid = + port_params->specific_params.rx_params.dflt_fqid; +} + +static void set_tx_dflt_cfg(struct fman_port *port, + struct fman_port_params *port_params, + struct fman_port_dts_params *dts_params) +{ + port->cfg->tx_fifo_deq_pipeline_depth = + get_dflt_fifo_deq_pipeline_depth(port->rev_info.major, + port->port_type, + port->port_speed); + port->cfg->err_fqid = + port_params->specific_params.non_rx_params.err_fqid; + port->cfg->deq_sp = + (u8)(dts_params->qman_channel_id & QMI_DEQ_CFG_SUBPORTAL_MASK); + port->cfg->dflt_fqid = + port_params->specific_params.non_rx_params.dflt_fqid; + port->cfg->deq_high_priority = true; +} + +/** + * fman_port_config + * @port: Pointer to the port structure + * @params: Pointer to data structure of parameters + * + * Creates a descriptor for the FM PORT module. + * The routine returns a pointer to the FM PORT object. + * This descriptor must be passed as first parameter to all other FM PORT + * function calls. + * No actual initialization or configuration of FM hardware is done by this + * routine. + * + * Return: 0 on success; Error code otherwise. + */ +int fman_port_config(struct fman_port *port, struct fman_port_params *params) +{ + void __iomem *base_addr = port->dts_params.base_addr; + int err; + + /* Allocate the FM driver's parameters structure */ + port->cfg = kzalloc(sizeof(*port->cfg), GFP_KERNEL); + if (!port->cfg) + goto err_params; + + /* Initialize FM port parameters which will be kept by the driver */ + port->port_type = port->dts_params.type; + port->port_speed = port->dts_params.speed; + port->port_id = port->dts_params.id; + port->fm = port->dts_params.fman; + port->ext_pools_num = (u8)8; + + /* get FM revision */ + fman_get_revision(port->fm, &port->rev_info); + + err = fill_soc_specific_params(port); + if (err) + goto err_port_cfg; + + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + set_rx_dflt_cfg(port, params); + case FMAN_PORT_TYPE_TX: + set_tx_dflt_cfg(port, params, &port->dts_params); + default: + set_dflt_cfg(port, params); + } + + /* Continue with other parameters */ + /* set memory map pointers */ + port->bmi_regs = base_addr + BMI_PORT_REGS_OFFSET; + port->qmi_regs = base_addr + QMI_PORT_REGS_OFFSET; + + port->max_frame_length = DFLT_PORT_MAX_FRAME_LENGTH; + /* resource distribution. */ + + port->fifo_bufs.num = + get_dflt_num_of_fifo_bufs(port->rev_info.major, port->port_type, + port->port_speed) * FMAN_BMI_FIFO_UNITS; + port->fifo_bufs.extra = + DFLT_PORT_EXTRA_NUM_OF_FIFO_BUFS * FMAN_BMI_FIFO_UNITS; + + port->open_dmas.num = + get_dflt_num_of_open_dmas(port->rev_info.major, + port->port_type, port->port_speed); + port->open_dmas.extra = + get_dflt_extra_num_of_open_dmas(port->rev_info.major, + port->port_type, port->port_speed); + port->tasks.num = + get_dflt_num_of_tasks(port->rev_info.major, + port->port_type, port->port_speed); + port->tasks.extra = + get_dflt_extra_num_of_tasks(port->rev_info.major, + port->port_type, port->port_speed); + + /* FM_HEAVY_TRAFFIC_SEQUENCER_HANG_ERRATA_FMAN_A006981 errata + * workaround + */ + if ((port->rev_info.major == 6) && (port->rev_info.minor == 0) && + (((port->port_type == FMAN_PORT_TYPE_TX) && + (port->port_speed == 1000)))) { + port->open_dmas.num = 16; + port->open_dmas.extra = 0; + } + + if (port->rev_info.major >= 6 && + port->port_type == FMAN_PORT_TYPE_TX && + port->port_speed == 1000) { + /* FM_WRONG_RESET_VALUES_ERRATA_FMAN_A005127 Errata + * workaround + */ + if (port->rev_info.major >= 6) { + u32 reg; + + reg = 0x00001013; + iowrite32be(reg, &port->bmi_regs->tx.fmbm_tfp); + } + } + + return 0; + +err_port_cfg: + kfree(port->cfg); +err_params: + kfree(port); + return -EINVAL; +} +EXPORT_SYMBOL(fman_port_config); + +/** + * fman_port_init + * port: A pointer to a FM Port module. + * Initializes the FM PORT module by defining the software structure and + * configuring the hardware registers. + * + * Return: 0 on success; Error code otherwise. + */ +int fman_port_init(struct fman_port *port) +{ + struct fman_port_cfg *cfg; + int err; + struct fman_port_init_params params; + + if (is_init_done(port->cfg)) + return -EINVAL; + + err = fman_sp_build_buffer_struct(&port->cfg->int_context, + &port->cfg->buffer_prefix_content, + &port->cfg->buf_margins, + &port->buffer_offsets, + &port->internal_buf_offset); + if (err) + return err; + + cfg = port->cfg; + + if (port->port_type == FMAN_PORT_TYPE_RX) { + /* Call the external Buffer routine which also checks fifo + * size and updates it if necessary + */ + /* define external buffer pools and pool depletion */ + err = set_ext_buffer_pools(port); + if (err) + return err; + /* check if the largest external buffer pool is large enough */ + if (cfg->buf_margins.start_margins + MIN_EXT_BUF_SIZE + + cfg->buf_margins.end_margins > + port->rx_pools_params.largest_buf_size) { + dev_err(port->dev, "%s: buf_margins.start_margins (%d) + minimum buf size (64) + buf_margins.end_margins (%d) is larger than maximum external buffer size (%d)\n", + __func__, cfg->buf_margins.start_margins, + cfg->buf_margins.end_margins, + port->rx_pools_params.largest_buf_size); + return -EINVAL; + } + } + + /* Call FM module routine for communicating parameters */ + memset(¶ms, 0, sizeof(params)); + params.port_id = port->port_id; + params.port_type = port->port_type; + params.port_speed = port->port_speed; + params.num_of_tasks = (u8)port->tasks.num; + params.num_of_extra_tasks = (u8)port->tasks.extra; + params.num_of_open_dmas = (u8)port->open_dmas.num; + params.num_of_extra_open_dmas = (u8)port->open_dmas.extra; + + if (port->fifo_bufs.num) { + err = verify_size_of_fifo(port); + if (err) + return err; + } + params.size_of_fifo = port->fifo_bufs.num; + params.extra_size_of_fifo = port->fifo_bufs.extra; + params.deq_pipeline_depth = port->cfg->tx_fifo_deq_pipeline_depth; + params.max_frame_length = port->max_frame_length; + + err = fman_set_port_params(port->fm, ¶ms); + if (err) + return err; + + err = init_low_level_driver(port); + if (err) + return err; + + kfree(port->cfg); + port->cfg = NULL; + + return 0; +} +EXPORT_SYMBOL(fman_port_init); + +/** + * fman_port_cfg_buf_prefix_content + * @port A pointer to a FM Port module. + * @buffer_prefix_content A structure of parameters describing + * the structure of the buffer. + * Out parameter: + * Start margin - offset of data from + * start of external buffer. + * Defines the structure, size and content of the application buffer. + * The prefix, in Tx ports, if 'pass_prs_result', the application should set + * a value to their offsets in the prefix of the FM will save the first + * 'priv_data_size', than, depending on 'pass_prs_result' and + * 'pass_time_stamp', copy parse result and timeStamp, and the packet itself + * (in this order), to the application buffer, and to offset. + * Calling this routine changes the buffer margins definitions in the internal + * driver data base from its default configuration: + * Data size: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PRIV_DATA_SIZE] + * Pass Parser result: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PASS_PRS_RESULT]. + * Pass timestamp: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PASS_TIME_STAMP]. + * May be used for all ports + * + * Allowed only following fman_port_config() and before fman_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fman_port_cfg_buf_prefix_content(struct fman_port *port, + struct fman_buffer_prefix_content * + buffer_prefix_content) +{ + if (is_init_done(port->cfg)) + return -EINVAL; + + memcpy(&port->cfg->buffer_prefix_content, + buffer_prefix_content, + sizeof(struct fman_buffer_prefix_content)); + /* if data_align was not initialized by user, + * we return to driver's default + */ + if (!port->cfg->buffer_prefix_content.data_align) + port->cfg->buffer_prefix_content.data_align = + DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN; + + return 0; +} +EXPORT_SYMBOL(fman_port_cfg_buf_prefix_content); + +/** + * fman_port_disable + * port: A pointer to a FM Port module. + * + * Gracefully disable an FM port. The port will not start new tasks after all + * tasks associated with the port are terminated. + * + * This is a blocking routine, it returns after port is gracefully stopped, + * i.e. the port will not except new frames, but it will finish all frames + * or tasks which were already began. + * Allowed only following fman_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fman_port_disable(struct fman_port *port) +{ + u32 __iomem *bmi_cfg_reg, *bmi_status_reg, tmp; + bool rx_port, failure = false; + int count; + + if (!is_init_done(port->cfg)) + return -EINVAL; + + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + bmi_cfg_reg = &port->bmi_regs->rx.fmbm_rcfg; + bmi_status_reg = &port->bmi_regs->rx.fmbm_rst; + rx_port = true; + break; + case FMAN_PORT_TYPE_TX: + bmi_cfg_reg = &port->bmi_regs->tx.fmbm_tcfg; + bmi_status_reg = &port->bmi_regs->tx.fmbm_tst; + rx_port = false; + break; + default: + return -EINVAL; + } + + /* Disable QMI */ + if (!rx_port) { + tmp = ioread32be(&port->qmi_regs->fmqm_pnc) & ~QMI_PORT_CFG_EN; + iowrite32be(tmp, &port->qmi_regs->fmqm_pnc); + + /* Wait for QMI to finish FD handling */ + count = 100; + do { + udelay(10); + tmp = ioread32be(&port->qmi_regs->fmqm_pns); + } while ((tmp & QMI_PORT_STATUS_DEQ_FD_BSY) && --count); + + if (count == 0) { + /* Timeout */ + failure = true; + } + } + + /* Disable BMI */ + tmp = ioread32be(bmi_cfg_reg) & ~BMI_PORT_CFG_EN; + iowrite32be(tmp, bmi_cfg_reg); + + /* Wait for graceful stop end */ + count = 500; + do { + udelay(10); + tmp = ioread32be(bmi_status_reg); + } while ((tmp & BMI_PORT_STATUS_BSY) && --count); + + if (count == 0) { + /* Timeout */ + failure = true; + } + + if (failure) + dev_dbg(port->dev, "%s: FMan Port[%d]: BMI or QMI is Busy. Port forced down\n", + __func__, port->port_id); + + return 0; +} +EXPORT_SYMBOL(fman_port_disable); + +/** + * fman_port_enable + * port: A pointer to a FM Port module. + * + * A runtime routine provided to allow disable/enable of port. + * + * Allowed only following fman_port_init(). + * + * Return: 0 on success; Error code otherwise. + */ +int fman_port_enable(struct fman_port *port) +{ + u32 __iomem *bmi_cfg_reg, tmp; + bool rx_port; + + if (!is_init_done(port->cfg)) + return -EINVAL; + + switch (port->port_type) { + case FMAN_PORT_TYPE_RX: + bmi_cfg_reg = &port->bmi_regs->rx.fmbm_rcfg; + rx_port = true; + break; + case FMAN_PORT_TYPE_TX: + bmi_cfg_reg = &port->bmi_regs->tx.fmbm_tcfg; + rx_port = false; + break; + default: + return -EINVAL; + } + + /* Enable QMI */ + if (!rx_port) { + tmp = ioread32be(&port->qmi_regs->fmqm_pnc) | QMI_PORT_CFG_EN; + iowrite32be(tmp, &port->qmi_regs->fmqm_pnc); + } + + /* Enable BMI */ + tmp = ioread32be(bmi_cfg_reg) | BMI_PORT_CFG_EN; + iowrite32be(tmp, bmi_cfg_reg); + + return 0; +} +EXPORT_SYMBOL(fman_port_enable); + +/** + * fman_port_bind + * dev: FMan Port OF device pointer + * + * Bind to a specific FMan Port. + * + * Allowed only after the port was created. + * + * Return: A pointer to the FMan port device. + */ +struct fman_port *fman_port_bind(struct device *dev) +{ + return (struct fman_port *)(dev_get_drvdata(get_device(dev))); +} +EXPORT_SYMBOL(fman_port_bind); + +/** + * fman_port_get_qman_channel_id + * port: Pointer to the FMan port devuce + * + * Get the QMan channel ID for the specific port + * + * Return: QMan channel ID + */ +u32 fman_port_get_qman_channel_id(struct fman_port *port) +{ + return port->dts_params.qman_channel_id; +} +EXPORT_SYMBOL(fman_port_get_qman_channel_id); + +static int fman_port_probe(struct platform_device *of_dev) +{ + struct fman_port *port; + struct fman *fman; + struct device_node *fm_node, *port_node; + struct resource res; + struct resource *dev_res; + const u32 *u32_prop; + int err = 0, lenp; + enum fman_port_type port_type; + u16 port_speed; + u8 port_id; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->dev = &of_dev->dev; + + port_node = of_node_get(of_dev->dev.of_node); + + /* Get the FM node */ + fm_node = of_get_parent(port_node); + if (!fm_node) { + dev_err(port->dev, "%s: of_get_parent() failed\n", __func__); + err = -ENODEV; + goto return_err; + } + + fman = dev_get_drvdata(&of_find_device_by_node(fm_node)->dev); + of_node_put(fm_node); + if (!fman) { + err = -EINVAL; + goto return_err; + } + + u32_prop = (const u32 *)of_get_property(port_node, "cell-index", &lenp); + if (!u32_prop) { + dev_err(port->dev, "%s: of_get_property(%s, cell-index) failed\n", + __func__, port_node->full_name); + err = -EINVAL; + goto return_err; + } + if (WARN_ON(lenp != sizeof(u32))) { + err = -EINVAL; + goto return_err; + } + port_id = (u8)fdt32_to_cpu(u32_prop[0]); + + port->dts_params.id = port_id; + + if (of_device_is_compatible(port_node, "fsl,fman-v3-port-tx")) { + port_type = FMAN_PORT_TYPE_TX; + port_speed = 1000; + u32_prop = (const u32 *)of_get_property(port_node, + "fsl,fman-10g-port", + &lenp); + if (u32_prop) + port_speed = 10000; + + } else if (of_device_is_compatible(port_node, "fsl,fman-v2-port-tx")) { + if (port_id >= TX_10G_PORT_BASE) + port_speed = 10000; + else + port_speed = 1000; + port_type = FMAN_PORT_TYPE_TX; + + } else if (of_device_is_compatible(port_node, "fsl,fman-v3-port-rx")) { + port_type = FMAN_PORT_TYPE_RX; + port_speed = 1000; + u32_prop = (const u32 *)of_get_property(port_node, + "fsl,fman-10g-port", &lenp); + if (u32_prop) + port_speed = 10000; + + } else if (of_device_is_compatible(port_node, "fsl,fman-v2-port-rx")) { + if (port_id >= RX_10G_PORT_BASE) + port_speed = 10000; + else + port_speed = 1000; + port_type = FMAN_PORT_TYPE_RX; + + } else { + dev_err(port->dev, "%s: Illegal port type\n", __func__); + err = -EINVAL; + goto return_err; + } + + port->dts_params.type = port_type; + port->dts_params.speed = port_speed; + + if (port_type == FMAN_PORT_TYPE_TX) { + u32 qman_channel_id; + + qman_channel_id = fman_get_qman_channel_id(fman, port_id); + if (qman_channel_id == 0) { + dev_err(port->dev, "%s: incorrect qman-channel-id\n", + __func__); + err = -EINVAL; + goto return_err; + } + port->dts_params.qman_channel_id = qman_channel_id; + } + + err = of_address_to_resource(port_node, 0, &res); + if (err < 0) { + dev_err(port->dev, "%s: of_address_to_resource() failed\n", + __func__); + err = -ENOMEM; + goto return_err; + } + + port->dts_params.fman = fman; + + of_node_put(port_node); + + dev_res = __devm_request_region(port->dev, &res, res.start, + resource_size(&res), "fman-port"); + if (!dev_res) { + dev_err(port->dev, "%s: __devm_request_region() failed\n", + __func__); + err = -EINVAL; + goto free_port; + } + + port->dts_params.base_addr = devm_ioremap(port->dev, res.start, + resource_size(&res)); + if (port->dts_params.base_addr == 0) + dev_err(port->dev, "%s: devm_ioremap() failed\n", __func__); + + dev_set_drvdata(&of_dev->dev, port); + + return 0; + +return_err: + of_node_put(port_node); +free_port: + kfree(port); + return err; +} + +static const struct of_device_id fman_port_match[] = { + {.compatible = "fsl,fman-v3-port-rx"}, + {.compatible = "fsl,fman-v2-port-rx"}, + {.compatible = "fsl,fman-v3-port-tx"}, + {.compatible = "fsl,fman-v2-port-tx"}, + {} +}; + +MODULE_DEVICE_TABLE(of, fman_port_match); + +static struct platform_driver fman_port_driver = { + .driver = { + .name = "fsl-fman-port", + .of_match_table = fman_port_match, + }, + .probe = fman_port_probe, +}; + +builtin_platform_driver(fman_port_driver); diff --git a/drivers/net/ethernet/freescale/fman/fman_port.h b/drivers/net/ethernet/freescale/fman/fman_port.h new file mode 100644 index 000000000000..8ba901737048 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_port.h @@ -0,0 +1,151 @@ +/* + * Copyright 2008 - 2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __FMAN_PORT_H +#define __FMAN_PORT_H + +#include "fman.h" + +/* FM Port API + * The FM uses a general module called "port" to represent a Tx port (MAC), + * an Rx port (MAC). + * The number of ports in an FM varies between SOCs. + * The SW driver manages these ports as sub-modules of the FM,i.e. after an + * FM is initialized, its ports may be initialized and operated upon. + * The port is initialized aware of its type, but other functions on a port + * may be indifferent to its type. When necessary, the driver verifies + * coherence and returns error if applicable. + * On initialization, user specifies the port type and it's index (relative + * to the port's type) - always starting at 0. + */ + +/* FM Frame error */ +/* Frame Descriptor errors */ +/* Not for Rx-Port! Unsupported Format */ +#define FM_PORT_FRM_ERR_UNSUPPORTED_FORMAT FM_FD_ERR_UNSUPPORTED_FORMAT +/* Not for Rx-Port! Length Error */ +#define FM_PORT_FRM_ERR_LENGTH FM_FD_ERR_LENGTH +/* DMA Data error */ +#define FM_PORT_FRM_ERR_DMA FM_FD_ERR_DMA +/* non Frame-Manager error; probably come from SEC that was chained to FM */ +#define FM_PORT_FRM_ERR_NON_FM FM_FD_RX_STATUS_ERR_NON_FM + /* IPR error */ +#define FM_PORT_FRM_ERR_IPRE (FM_FD_ERR_IPR & ~FM_FD_IPR) +/* IPR non-consistent-sp */ +#define FM_PORT_FRM_ERR_IPR_NCSP (FM_FD_ERR_IPR_NCSP & \ + ~FM_FD_IPR) + +/* Rx FIFO overflow, FCS error, code error, running disparity + * error (SGMII and TBI modes), FIFO parity error. + * PHY Sequence error, PHY error control character detected. + */ +#define FM_PORT_FRM_ERR_PHYSICAL FM_FD_ERR_PHYSICAL +/* Frame too long OR Frame size exceeds max_length_frame */ +#define FM_PORT_FRM_ERR_SIZE FM_FD_ERR_SIZE +/* indicates a classifier "drop" operation */ +#define FM_PORT_FRM_ERR_CLS_DISCARD FM_FD_ERR_CLS_DISCARD +/* Extract Out of Frame */ +#define FM_PORT_FRM_ERR_EXTRACTION FM_FD_ERR_EXTRACTION +/* No Scheme Selected */ +#define FM_PORT_FRM_ERR_NO_SCHEME FM_FD_ERR_NO_SCHEME +/* Keysize Overflow */ +#define FM_PORT_FRM_ERR_KEYSIZE_OVERFLOW FM_FD_ERR_KEYSIZE_OVERFLOW +/* Frame color is red */ +#define FM_PORT_FRM_ERR_COLOR_RED FM_FD_ERR_COLOR_RED +/* Frame color is yellow */ +#define FM_PORT_FRM_ERR_COLOR_YELLOW FM_FD_ERR_COLOR_YELLOW +/* Parser Time out Exceed */ +#define FM_PORT_FRM_ERR_PRS_TIMEOUT FM_FD_ERR_PRS_TIMEOUT +/* Invalid Soft Parser instruction */ +#define FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT FM_FD_ERR_PRS_ILL_INSTRUCT +/* Header error was identified during parsing */ +#define FM_PORT_FRM_ERR_PRS_HDR_ERR FM_FD_ERR_PRS_HDR_ERR +/* Frame parsed beyind 256 first bytes */ +#define FM_PORT_FRM_ERR_BLOCK_LIMIT_EXCEEDED FM_FD_ERR_BLOCK_LIMIT_EXCEEDED +/* FPM Frame Processing Timeout Exceeded */ +#define FM_PORT_FRM_ERR_PROCESS_TIMEOUT 0x00000001 + +struct fman_port; + +/* A structure for additional Rx port parameters */ +struct fman_port_rx_params { + u32 err_fqid; /* Error Queue Id. */ + u32 dflt_fqid; /* Default Queue Id. */ + /* Which external buffer pools are used + * (up to FMAN_PORT_MAX_EXT_POOLS_NUM), and their sizes. + */ + struct fman_ext_pools ext_buf_pools; +}; + +/* A structure for additional non-Rx port parameters */ +struct fman_port_non_rx_params { + /* Error Queue Id. */ + u32 err_fqid; + /* For Tx - Default Confirmation queue, 0 means no Tx confirmation + * for processed frames. For OP port - default Rx queue. + */ + u32 dflt_fqid; +}; + +/* A union for additional parameters depending on port type */ +union fman_port_specific_params { + /* Rx port parameters structure */ + struct fman_port_rx_params rx_params; + /* Non-Rx port parameters structure */ + struct fman_port_non_rx_params non_rx_params; +}; + +/* A structure representing FM initialization parameters */ +struct fman_port_params { + /* Virtual Address of memory mapped FM Port registers. */ + void *fm; + union fman_port_specific_params specific_params; + /* Additional parameters depending on port type. */ +}; + +int fman_port_config(struct fman_port *port, struct fman_port_params *params); + +int fman_port_init(struct fman_port *port); + +int fman_port_cfg_buf_prefix_content(struct fman_port *port, + struct fman_buffer_prefix_content + *buffer_prefix_content); + +int fman_port_disable(struct fman_port *port); + +int fman_port_enable(struct fman_port *port); + +u32 fman_port_get_qman_channel_id(struct fman_port *port); + +struct fman_port *fman_port_bind(struct device *dev); + +#endif /* __FMAN_PORT_H */ -- cgit v1.2.3-59-g8ed1b From 3933961682a30ae7d405cda344c040a129fea422 Mon Sep 17 00:00:00 2001 From: Igal Liberman Date: Mon, 21 Dec 2015 02:21:30 +0200 Subject: fsl/fman: Add FMan MAC driver This patch adds the Ethernet MAC driver supporting the three different types of MACs: dTSEC, tGEC and mEMAC. Signed-off-by: Igal Liberman Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/Makefile | 3 +- drivers/net/ethernet/freescale/fman/mac.c | 978 +++++++++++++++++++++++++++ drivers/net/ethernet/freescale/fman/mac.h | 97 +++ 3 files changed, 1077 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/freescale/fman/mac.c create mode 100644 drivers/net/ethernet/freescale/fman/mac.h (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile index 2eb0b9b5c76b..51fd2e6c1b84 100644 --- a/drivers/net/ethernet/freescale/fman/Makefile +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -1,6 +1,7 @@ subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman -obj-y += fsl_fman.o fsl_fman_mac.o +obj-y += fsl_fman.o fsl_fman_mac.o fsl_mac.o fsl_fman-objs := fman_muram.o fman.o fman_sp.o fman_port.o fsl_fman_mac-objs := fman_dtsec.o fman_memac.o fman_tgec.o +fsl_mac-objs += mac.o diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c new file mode 100644 index 000000000000..743a393ba657 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/mac.c @@ -0,0 +1,978 @@ +/* Copyright 2008-2015 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mac.h" +#include "fman_mac.h" +#include "fman_dtsec.h" +#include "fman_tgec.h" +#include "fman_memac.h" + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("FSL FMan MAC API based driver"); + +struct mac_priv_s { + struct device *dev; + void __iomem *vaddr; + u8 cell_index; + phy_interface_t phy_if; + struct fman *fman; + struct device_node *phy_node; + struct device_node *internal_phy_node; + /* List of multicast addresses */ + struct list_head mc_addr_list; + struct platform_device *eth_dev; + struct fixed_phy_status *fixed_link; + u16 speed; + u16 max_speed; + + int (*enable)(struct fman_mac *mac_dev, enum comm_mode mode); + int (*disable)(struct fman_mac *mac_dev, enum comm_mode mode); +}; + +struct mac_address { + u8 addr[ETH_ALEN]; + struct list_head list; +}; + +static void mac_exception(void *handle, enum fman_mac_exceptions ex) +{ + struct mac_device *mac_dev; + struct mac_priv_s *priv; + + mac_dev = handle; + priv = mac_dev->priv; + + if (ex == FM_MAC_EX_10G_RX_FIFO_OVFL) { + /* don't flag RX FIFO after the first */ + mac_dev->set_exception(mac_dev->fman_mac, + FM_MAC_EX_10G_RX_FIFO_OVFL, false); + dev_err(priv->dev, "10G MAC got RX FIFO Error = %x\n", ex); + } + + dev_dbg(priv->dev, "%s:%s() -> %d\n", KBUILD_BASENAME ".c", + __func__, ex); +} + +static void set_fman_mac_params(struct mac_device *mac_dev, + struct fman_mac_params *params) +{ + struct mac_priv_s *priv = mac_dev->priv; + + params->base_addr = (typeof(params->base_addr)) + devm_ioremap(priv->dev, mac_dev->res->start, + resource_size(mac_dev->res)); + memcpy(¶ms->addr, mac_dev->addr, sizeof(mac_dev->addr)); + params->max_speed = priv->max_speed; + params->phy_if = priv->phy_if; + params->basex_if = false; + params->mac_id = priv->cell_index; + params->fm = (void *)priv->fman; + params->exception_cb = mac_exception; + params->event_cb = mac_exception; + params->dev_id = mac_dev; + params->internal_phy_node = priv->internal_phy_node; +} + +static int tgec_initialization(struct mac_device *mac_dev) +{ + int err; + struct mac_priv_s *priv; + struct fman_mac_params params; + u32 version; + + priv = mac_dev->priv; + + set_fman_mac_params(mac_dev, ¶ms); + + mac_dev->fman_mac = tgec_config(¶ms); + if (!mac_dev->fman_mac) { + err = -EINVAL; + goto _return; + } + + err = tgec_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm()); + if (err < 0) + goto _return_fm_mac_free; + + err = tgec_init(mac_dev->fman_mac); + if (err < 0) + goto _return_fm_mac_free; + + /* For 10G MAC, disable Tx ECC exception */ + err = mac_dev->set_exception(mac_dev->fman_mac, + FM_MAC_EX_10G_TX_ECC_ER, false); + if (err < 0) + goto _return_fm_mac_free; + + err = tgec_get_version(mac_dev->fman_mac, &version); + if (err < 0) + goto _return_fm_mac_free; + + dev_info(priv->dev, "FMan XGEC version: 0x%08x\n", version); + + goto _return; + +_return_fm_mac_free: + tgec_free(mac_dev->fman_mac); + +_return: + return err; +} + +static int dtsec_initialization(struct mac_device *mac_dev) +{ + int err; + struct mac_priv_s *priv; + struct fman_mac_params params; + u32 version; + + priv = mac_dev->priv; + + set_fman_mac_params(mac_dev, ¶ms); + + mac_dev->fman_mac = dtsec_config(¶ms); + if (!mac_dev->fman_mac) { + err = -EINVAL; + goto _return; + } + + err = dtsec_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm()); + if (err < 0) + goto _return_fm_mac_free; + + err = dtsec_cfg_pad_and_crc(mac_dev->fman_mac, true); + if (err < 0) + goto _return_fm_mac_free; + + err = dtsec_init(mac_dev->fman_mac); + if (err < 0) + goto _return_fm_mac_free; + + /* For 1G MAC, disable by default the MIB counters overflow interrupt */ + err = mac_dev->set_exception(mac_dev->fman_mac, + FM_MAC_EX_1G_RX_MIB_CNT_OVFL, false); + if (err < 0) + goto _return_fm_mac_free; + + err = dtsec_get_version(mac_dev->fman_mac, &version); + if (err < 0) + goto _return_fm_mac_free; + + dev_info(priv->dev, "FMan dTSEC version: 0x%08x\n", version); + + goto _return; + +_return_fm_mac_free: + dtsec_free(mac_dev->fman_mac); + +_return: + return err; +} + +static int memac_initialization(struct mac_device *mac_dev) +{ + int err; + struct mac_priv_s *priv; + struct fman_mac_params params; + + priv = mac_dev->priv; + + set_fman_mac_params(mac_dev, ¶ms); + + if (priv->max_speed == SPEED_10000) + params.phy_if = PHY_INTERFACE_MODE_XGMII; + + mac_dev->fman_mac = memac_config(¶ms); + if (!mac_dev->fman_mac) { + err = -EINVAL; + goto _return; + } + + err = memac_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm()); + if (err < 0) + goto _return_fm_mac_free; + + err = memac_cfg_reset_on_init(mac_dev->fman_mac, true); + if (err < 0) + goto _return_fm_mac_free; + + err = memac_cfg_fixed_link(mac_dev->fman_mac, priv->fixed_link); + if (err < 0) + goto _return_fm_mac_free; + + err = memac_init(mac_dev->fman_mac); + if (err < 0) + goto _return_fm_mac_free; + + dev_info(priv->dev, "FMan MEMAC\n"); + + goto _return; + +_return_fm_mac_free: + memac_free(mac_dev->fman_mac); + +_return: + return err; +} + +static int start(struct mac_device *mac_dev) +{ + int err; + struct phy_device *phy_dev = mac_dev->phy_dev; + struct mac_priv_s *priv = mac_dev->priv; + + err = priv->enable(mac_dev->fman_mac, COMM_MODE_RX_AND_TX); + if (!err && phy_dev) + phy_start(phy_dev); + + return err; +} + +static int stop(struct mac_device *mac_dev) +{ + struct mac_priv_s *priv = mac_dev->priv; + + if (mac_dev->phy_dev) + phy_stop(mac_dev->phy_dev); + + return priv->disable(mac_dev->fman_mac, COMM_MODE_RX_AND_TX); +} + +static int set_multi(struct net_device *net_dev, struct mac_device *mac_dev) +{ + struct mac_priv_s *priv; + struct mac_address *old_addr, *tmp; + struct netdev_hw_addr *ha; + int err; + enet_addr_t *addr; + + priv = mac_dev->priv; + + /* Clear previous address list */ + list_for_each_entry_safe(old_addr, tmp, &priv->mc_addr_list, list) { + addr = (enet_addr_t *)old_addr->addr; + err = mac_dev->remove_hash_mac_addr(mac_dev->fman_mac, addr); + if (err < 0) + return err; + + list_del(&old_addr->list); + kfree(old_addr); + } + + /* Add all the addresses from the new list */ + netdev_for_each_mc_addr(ha, net_dev) { + addr = (enet_addr_t *)ha->addr; + err = mac_dev->add_hash_mac_addr(mac_dev->fman_mac, addr); + if (err < 0) + return err; + + tmp = kmalloc(sizeof(*tmp), GFP_ATOMIC); + if (!tmp) + return -ENOMEM; + + ether_addr_copy(tmp->addr, ha->addr); + list_add(&tmp->list, &priv->mc_addr_list); + } + return 0; +} + +/** + * fman_set_mac_active_pause + * @mac_dev: A pointer to the MAC device + * @rx: Pause frame setting for RX + * @tx: Pause frame setting for TX + * + * Set the MAC RX/TX PAUSE frames settings + * + * Avoid redundant calls to FMD, if the MAC driver already contains the desired + * active PAUSE settings. Otherwise, the new active settings should be reflected + * in FMan. + * + * Return: 0 on success; Error code otherwise. + */ +int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx) +{ + struct fman_mac *fman_mac = mac_dev->fman_mac; + int err = 0; + + if (rx != mac_dev->rx_pause_active) { + err = mac_dev->set_rx_pause(fman_mac, rx); + if (likely(err == 0)) + mac_dev->rx_pause_active = rx; + } + + if (tx != mac_dev->tx_pause_active) { + u16 pause_time = (tx ? FSL_FM_PAUSE_TIME_ENABLE : + FSL_FM_PAUSE_TIME_DISABLE); + + err = mac_dev->set_tx_pause(fman_mac, 0, pause_time, 0); + + if (likely(err == 0)) + mac_dev->tx_pause_active = tx; + } + + return err; +} +EXPORT_SYMBOL(fman_set_mac_active_pause); + +/** + * fman_get_pause_cfg + * @mac_dev: A pointer to the MAC device + * @rx: Return value for RX setting + * @tx: Return value for TX setting + * + * Determine the MAC RX/TX PAUSE frames settings based on PHY + * autonegotiation or values set by eththool. + * + * Return: Pointer to FMan device. + */ +void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause, + bool *tx_pause) +{ + struct phy_device *phy_dev = mac_dev->phy_dev; + u16 lcl_adv, rmt_adv; + u8 flowctrl; + + *rx_pause = *tx_pause = false; + + if (!phy_dev->duplex) + return; + + /* If PAUSE autonegotiation is disabled, the TX/RX PAUSE settings + * are those set by ethtool. + */ + if (!mac_dev->autoneg_pause) { + *rx_pause = mac_dev->rx_pause_req; + *tx_pause = mac_dev->tx_pause_req; + return; + } + + /* Else if PAUSE autonegotiation is enabled, the TX/RX PAUSE + * settings depend on the result of the link negotiation. + */ + + /* get local capabilities */ + lcl_adv = 0; + if (phy_dev->advertising & ADVERTISED_Pause) + lcl_adv |= ADVERTISE_PAUSE_CAP; + if (phy_dev->advertising & ADVERTISED_Asym_Pause) + lcl_adv |= ADVERTISE_PAUSE_ASYM; + + /* get link partner capabilities */ + rmt_adv = 0; + if (phy_dev->pause) + rmt_adv |= LPA_PAUSE_CAP; + if (phy_dev->asym_pause) + rmt_adv |= LPA_PAUSE_ASYM; + + /* Calculate TX/RX settings based on local and peer advertised + * symmetric/asymmetric PAUSE capabilities. + */ + flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); + if (flowctrl & FLOW_CTRL_RX) + *rx_pause = true; + if (flowctrl & FLOW_CTRL_TX) + *tx_pause = true; +} +EXPORT_SYMBOL(fman_get_pause_cfg); + +static void adjust_link_void(struct net_device *net_dev) +{ +} + +static void adjust_link_dtsec(struct net_device *net_dev) +{ + struct device *dev = net_dev->dev.parent; + struct dpaa_eth_data *eth_data = dev->platform_data; + struct mac_device *mac_dev = eth_data->mac_dev; + struct phy_device *phy_dev = mac_dev->phy_dev; + struct fman_mac *fman_mac; + bool rx_pause, tx_pause; + int err; + + fman_mac = mac_dev->fman_mac; + if (!phy_dev->link) { + dtsec_restart_autoneg(fman_mac); + + return; + } + + dtsec_adjust_link(fman_mac, phy_dev->speed); + fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); + err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); + if (err < 0) + netdev_err(net_dev, "fman_set_mac_active_pause() = %d\n", err); +} + +static void adjust_link_memac(struct net_device *net_dev) +{ + struct device *dev = net_dev->dev.parent; + struct dpaa_eth_data *eth_data = dev->platform_data; + struct mac_device *mac_dev = eth_data->mac_dev; + struct phy_device *phy_dev = mac_dev->phy_dev; + struct fman_mac *fman_mac; + bool rx_pause, tx_pause; + int err; + + fman_mac = mac_dev->fman_mac; + memac_adjust_link(fman_mac, phy_dev->speed); + + fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause); + err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause); + if (err < 0) + netdev_err(net_dev, "fman_set_mac_active_pause() = %d\n", err); +} + +/* Initializes driver's PHY state, and attaches to the PHY. + * Returns 0 on success. + */ +static int init_phy(struct net_device *net_dev, + struct mac_device *mac_dev, + void (*adj_lnk)(struct net_device *)) +{ + struct phy_device *phy_dev; + struct mac_priv_s *priv = mac_dev->priv; + + phy_dev = of_phy_connect(net_dev, priv->phy_node, adj_lnk, 0, + priv->phy_if); + if (!phy_dev) { + netdev_err(net_dev, "Could not connect to PHY\n"); + return -ENODEV; + } + + /* Remove any features not supported by the controller */ + phy_dev->supported &= mac_dev->if_support; + /* Enable the symmetric and asymmetric PAUSE frame advertisements, + * as most of the PHY drivers do not enable them by default. + */ + phy_dev->supported |= (SUPPORTED_Pause | SUPPORTED_Asym_Pause); + phy_dev->advertising = phy_dev->supported; + + mac_dev->phy_dev = phy_dev; + + return 0; +} + +static int dtsec_init_phy(struct net_device *net_dev, + struct mac_device *mac_dev) +{ + return init_phy(net_dev, mac_dev, &adjust_link_dtsec); +} + +static int tgec_init_phy(struct net_device *net_dev, + struct mac_device *mac_dev) +{ + return init_phy(net_dev, mac_dev, adjust_link_void); +} + +static int memac_init_phy(struct net_device *net_dev, + struct mac_device *mac_dev) +{ + return init_phy(net_dev, mac_dev, &adjust_link_memac); +} + +static void setup_dtsec(struct mac_device *mac_dev) +{ + mac_dev->init_phy = dtsec_init_phy; + mac_dev->init = dtsec_initialization; + mac_dev->set_promisc = dtsec_set_promiscuous; + mac_dev->change_addr = dtsec_modify_mac_address; + mac_dev->add_hash_mac_addr = dtsec_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = dtsec_del_hash_mac_address; + mac_dev->set_tx_pause = dtsec_set_tx_pause_frames; + mac_dev->set_rx_pause = dtsec_accept_rx_pause_frames; + mac_dev->set_exception = dtsec_set_exception; + mac_dev->set_multi = set_multi; + mac_dev->start = start; + mac_dev->stop = stop; + + mac_dev->priv->enable = dtsec_enable; + mac_dev->priv->disable = dtsec_disable; +} + +static void setup_tgec(struct mac_device *mac_dev) +{ + mac_dev->init_phy = tgec_init_phy; + mac_dev->init = tgec_initialization; + mac_dev->set_promisc = tgec_set_promiscuous; + mac_dev->change_addr = tgec_modify_mac_address; + mac_dev->add_hash_mac_addr = tgec_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = tgec_del_hash_mac_address; + mac_dev->set_tx_pause = tgec_set_tx_pause_frames; + mac_dev->set_rx_pause = tgec_accept_rx_pause_frames; + mac_dev->set_exception = tgec_set_exception; + mac_dev->set_multi = set_multi; + mac_dev->start = start; + mac_dev->stop = stop; + + mac_dev->priv->enable = tgec_enable; + mac_dev->priv->disable = tgec_disable; +} + +static void setup_memac(struct mac_device *mac_dev) +{ + mac_dev->init_phy = memac_init_phy; + mac_dev->init = memac_initialization; + mac_dev->set_promisc = memac_set_promiscuous; + mac_dev->change_addr = memac_modify_mac_address; + mac_dev->add_hash_mac_addr = memac_add_hash_mac_address; + mac_dev->remove_hash_mac_addr = memac_del_hash_mac_address; + mac_dev->set_tx_pause = memac_set_tx_pause_frames; + mac_dev->set_rx_pause = memac_accept_rx_pause_frames; + mac_dev->set_exception = memac_set_exception; + mac_dev->set_multi = set_multi; + mac_dev->start = start; + mac_dev->stop = stop; + + mac_dev->priv->enable = memac_enable; + mac_dev->priv->disable = memac_disable; +} + +#define DTSEC_SUPPORTED \ + (SUPPORTED_10baseT_Half \ + | SUPPORTED_10baseT_Full \ + | SUPPORTED_100baseT_Half \ + | SUPPORTED_100baseT_Full \ + | SUPPORTED_Autoneg \ + | SUPPORTED_Pause \ + | SUPPORTED_Asym_Pause \ + | SUPPORTED_MII) + +static DEFINE_MUTEX(eth_lock); + +static const char phy_str[][11] = { + [PHY_INTERFACE_MODE_MII] = "mii", + [PHY_INTERFACE_MODE_GMII] = "gmii", + [PHY_INTERFACE_MODE_SGMII] = "sgmii", + [PHY_INTERFACE_MODE_TBI] = "tbi", + [PHY_INTERFACE_MODE_RMII] = "rmii", + [PHY_INTERFACE_MODE_RGMII] = "rgmii", + [PHY_INTERFACE_MODE_RGMII_ID] = "rgmii-id", + [PHY_INTERFACE_MODE_RGMII_RXID] = "rgmii-rxid", + [PHY_INTERFACE_MODE_RGMII_TXID] = "rgmii-txid", + [PHY_INTERFACE_MODE_RTBI] = "rtbi", + [PHY_INTERFACE_MODE_XGMII] = "xgmii" +}; + +static phy_interface_t __pure __attribute__((nonnull)) str2phy(const char *str) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(phy_str); i++) + if (strcmp(str, phy_str[i]) == 0) + return (phy_interface_t)i; + + return PHY_INTERFACE_MODE_MII; +} + +static const u16 phy2speed[] = { + [PHY_INTERFACE_MODE_MII] = SPEED_100, + [PHY_INTERFACE_MODE_GMII] = SPEED_1000, + [PHY_INTERFACE_MODE_SGMII] = SPEED_1000, + [PHY_INTERFACE_MODE_TBI] = SPEED_1000, + [PHY_INTERFACE_MODE_RMII] = SPEED_100, + [PHY_INTERFACE_MODE_RGMII] = SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_ID] = SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_RXID] = SPEED_1000, + [PHY_INTERFACE_MODE_RGMII_TXID] = SPEED_1000, + [PHY_INTERFACE_MODE_RTBI] = SPEED_1000, + [PHY_INTERFACE_MODE_XGMII] = SPEED_10000 +}; + +static struct platform_device *dpaa_eth_add_device(int fman_id, + struct mac_device *mac_dev, + struct device_node *node) +{ + struct platform_device *pdev; + struct dpaa_eth_data data; + struct mac_priv_s *priv; + static int dpaa_eth_dev_cnt; + int ret; + + priv = mac_dev->priv; + + data.mac_dev = mac_dev; + data.mac_hw_id = priv->cell_index; + data.fman_hw_id = fman_id; + data.mac_node = node; + + mutex_lock(ð_lock); + + pdev = platform_device_alloc("dpaa-ethernet", dpaa_eth_dev_cnt); + if (!pdev) { + ret = -ENOMEM; + goto no_mem; + } + + ret = platform_device_add_data(pdev, &data, sizeof(data)); + if (ret) + goto err; + + ret = platform_device_add(pdev); + if (ret) + goto err; + + dpaa_eth_dev_cnt++; + mutex_unlock(ð_lock); + + return pdev; + +err: + platform_device_put(pdev); +no_mem: + mutex_unlock(ð_lock); + + return ERR_PTR(ret); +} + +static const struct of_device_id mac_match[] = { + { .compatible = "fsl,fman-dtsec" }, + { .compatible = "fsl,fman-xgec" }, + { .compatible = "fsl,fman-memac" }, + {} +}; +MODULE_DEVICE_TABLE(of, mac_match); + +static int mac_probe(struct platform_device *_of_dev) +{ + int err, i, lenp, nph; + struct device *dev; + struct device_node *mac_node, *dev_node; + struct mac_device *mac_dev; + struct platform_device *of_dev; + struct resource res; + struct mac_priv_s *priv; + const u8 *mac_addr; + const char *char_prop; + const u32 *u32_prop; + u8 fman_id; + + dev = &_of_dev->dev; + mac_node = dev->of_node; + + mac_dev = devm_kzalloc(dev, sizeof(*mac_dev), GFP_KERNEL); + if (!mac_dev) { + err = -ENOMEM; + dev_err(dev, "devm_kzalloc() = %d\n", err); + goto _return; + } + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + err = -ENOMEM; + goto _return; + } + + /* Save private information */ + mac_dev->priv = priv; + priv->dev = dev; + + if (of_device_is_compatible(mac_node, "fsl,fman-dtsec")) { + setup_dtsec(mac_dev); + priv->internal_phy_node = of_parse_phandle(mac_node, + "tbi-handle", 0); + } else if (of_device_is_compatible(mac_node, "fsl,fman-xgec")) { + setup_tgec(mac_dev); + } else if (of_device_is_compatible(mac_node, "fsl,fman-memac")) { + setup_memac(mac_dev); + priv->internal_phy_node = of_parse_phandle(mac_node, + "pcsphy-handle", 0); + } else { + dev_err(dev, "MAC node (%s) contains unsupported MAC\n", + mac_node->full_name); + err = -EINVAL; + goto _return; + } + + /* Register mac_dev */ + dev_set_drvdata(dev, mac_dev); + + INIT_LIST_HEAD(&priv->mc_addr_list); + + /* Get the FM node */ + dev_node = of_get_parent(mac_node); + if (!dev_node) { + dev_err(dev, "of_get_parent(%s) failed\n", + mac_node->full_name); + err = -EINVAL; + goto _return_dev_set_drvdata; + } + + of_dev = of_find_device_by_node(dev_node); + if (!of_dev) { + dev_err(dev, "of_find_device_by_node(%s) failed\n", + dev_node->full_name); + err = -EINVAL; + goto _return_of_node_put; + } + + /* Get the FMan cell-index */ + u32_prop = of_get_property(dev_node, "cell-index", &lenp); + if (!u32_prop) { + dev_err(dev, "of_get_property(%s, cell-index) failed\n", + dev_node->full_name); + err = -EINVAL; + goto _return_of_node_put; + } + WARN_ON(lenp != sizeof(u32)); + /* cell-index 0 => FMan id 1 */ + fman_id = (u8)(fdt32_to_cpu(u32_prop[0]) + 1); + + priv->fman = fman_bind(&of_dev->dev); + if (!priv->fman) { + dev_err(dev, "fman_bind(%s) failed\n", dev_node->full_name); + err = -ENODEV; + goto _return_of_node_put; + } + + of_node_put(dev_node); + + /* Get the address of the memory mapped registers */ + err = of_address_to_resource(mac_node, 0, &res); + if (err < 0) { + dev_err(dev, "of_address_to_resource(%s) = %d\n", + mac_node->full_name, err); + goto _return_dev_set_drvdata; + } + + mac_dev->res = __devm_request_region(dev, + fman_get_mem_region(priv->fman), + res.start, res.end + 1 - res.start, + "mac"); + if (!mac_dev->res) { + dev_err(dev, "__devm_request_mem_region(mac) failed\n"); + err = -EBUSY; + goto _return_dev_set_drvdata; + } + + priv->vaddr = devm_ioremap(dev, mac_dev->res->start, + mac_dev->res->end + 1 - mac_dev->res->start); + if (!priv->vaddr) { + dev_err(dev, "devm_ioremap() failed\n"); + err = -EIO; + goto _return_dev_set_drvdata; + } + + if (!of_device_is_available(mac_node)) { + devm_iounmap(dev, priv->vaddr); + __devm_release_region(dev, fman_get_mem_region(priv->fman), + res.start, res.end + 1 - res.start); + devm_kfree(dev, mac_dev); + dev_set_drvdata(dev, NULL); + return -ENODEV; + } + + /* Get the cell-index */ + u32_prop = of_get_property(mac_node, "cell-index", &lenp); + if (!u32_prop) { + dev_err(dev, "of_get_property(%s, cell-index) failed\n", + mac_node->full_name); + err = -EINVAL; + goto _return_dev_set_drvdata; + } + WARN_ON(lenp != sizeof(u32)); + priv->cell_index = (u8)fdt32_to_cpu(u32_prop[0]); + + /* Get the MAC address */ + mac_addr = of_get_mac_address(mac_node); + if (!mac_addr) { + dev_err(dev, "of_get_mac_address(%s) failed\n", + mac_node->full_name); + err = -EINVAL; + goto _return_dev_set_drvdata; + } + memcpy(mac_dev->addr, mac_addr, sizeof(mac_dev->addr)); + + /* Get the port handles */ + nph = of_count_phandle_with_args(mac_node, "fsl,fman-ports", NULL); + if (unlikely(nph < 0)) { + dev_err(dev, "of_count_phandle_with_args(%s, fsl,fman-ports) failed\n", + mac_node->full_name); + err = nph; + goto _return_dev_set_drvdata; + } + + if (nph != ARRAY_SIZE(mac_dev->port)) { + dev_err(dev, "Not supported number of fman-ports handles of mac node %s from device tree\n", + mac_node->full_name); + err = -EINVAL; + goto _return_dev_set_drvdata; + } + + for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) { + /* Find the port node */ + dev_node = of_parse_phandle(mac_node, "fsl,fman-ports", i); + if (!dev_node) { + dev_err(dev, "of_parse_phandle(%s, fsl,fman-ports) failed\n", + mac_node->full_name); + err = -EINVAL; + goto _return_of_node_put; + } + + of_dev = of_find_device_by_node(dev_node); + if (!of_dev) { + dev_err(dev, "of_find_device_by_node(%s) failed\n", + dev_node->full_name); + err = -EINVAL; + goto _return_of_node_put; + } + + mac_dev->port[i] = fman_port_bind(&of_dev->dev); + if (!mac_dev->port[i]) { + dev_err(dev, "dev_get_drvdata(%s) failed\n", + dev_node->full_name); + err = -EINVAL; + goto _return_of_node_put; + } + of_node_put(dev_node); + } + + /* Get the PHY connection type */ + char_prop = (const char *)of_get_property(mac_node, + "phy-connection-type", NULL); + if (!char_prop) { + dev_warn(dev, + "of_get_property(%s, phy-connection-type) failed. Defaulting to MII\n", + mac_node->full_name); + priv->phy_if = PHY_INTERFACE_MODE_MII; + } else { + priv->phy_if = str2phy(char_prop); + } + + priv->speed = phy2speed[priv->phy_if]; + priv->max_speed = priv->speed; + mac_dev->if_support = DTSEC_SUPPORTED; + /* We don't support half-duplex in SGMII mode */ + if (priv->phy_if == PHY_INTERFACE_MODE_SGMII) + mac_dev->if_support &= ~(SUPPORTED_10baseT_Half | + SUPPORTED_100baseT_Half); + + /* Gigabit support (no half-duplex) */ + if (priv->max_speed == 1000) + mac_dev->if_support |= SUPPORTED_1000baseT_Full; + + /* The 10G interface only supports one mode */ + if (priv->phy_if == PHY_INTERFACE_MODE_XGMII) + mac_dev->if_support = SUPPORTED_10000baseT_Full; + + /* Get the rest of the PHY information */ + priv->phy_node = of_parse_phandle(mac_node, "phy-handle", 0); + if (!priv->phy_node && of_phy_is_fixed_link(mac_node)) { + struct phy_device *phy; + + err = of_phy_register_fixed_link(mac_node); + if (err) + goto _return_dev_set_drvdata; + + priv->fixed_link = kzalloc(sizeof(*priv->fixed_link), + GFP_KERNEL); + if (!priv->fixed_link) + goto _return_dev_set_drvdata; + + priv->phy_node = of_node_get(mac_node); + phy = of_phy_find_device(priv->phy_node); + if (!phy) + goto _return_dev_set_drvdata; + + priv->fixed_link->link = phy->link; + priv->fixed_link->speed = phy->speed; + priv->fixed_link->duplex = phy->duplex; + priv->fixed_link->pause = phy->pause; + priv->fixed_link->asym_pause = phy->asym_pause; + } + + err = mac_dev->init(mac_dev); + if (err < 0) { + dev_err(dev, "mac_dev->init() = %d\n", err); + of_node_put(priv->phy_node); + goto _return_dev_set_drvdata; + } + + /* pause frame autonegotiation enabled */ + mac_dev->autoneg_pause = true; + + /* By intializing the values to false, force FMD to enable PAUSE frames + * on RX and TX + */ + mac_dev->rx_pause_req = true; + mac_dev->tx_pause_req = true; + mac_dev->rx_pause_active = false; + mac_dev->tx_pause_active = false; + err = fman_set_mac_active_pause(mac_dev, true, true); + if (err < 0) + dev_err(dev, "fman_set_mac_active_pause() = %d\n", err); + + dev_info(dev, "FMan MAC address: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n", + mac_dev->addr[0], mac_dev->addr[1], mac_dev->addr[2], + mac_dev->addr[3], mac_dev->addr[4], mac_dev->addr[5]); + + priv->eth_dev = dpaa_eth_add_device(fman_id, mac_dev, mac_node); + if (IS_ERR(priv->eth_dev)) { + dev_err(dev, "failed to add Ethernet platform device for MAC %d\n", + priv->cell_index); + priv->eth_dev = NULL; + } + + goto _return; + +_return_of_node_put: + of_node_put(dev_node); +_return_dev_set_drvdata: + kfree(priv->fixed_link); + kfree(priv); + dev_set_drvdata(dev, NULL); +_return: + return err; +} + +static struct platform_driver mac_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = mac_match, + }, + .probe = mac_probe, +}; + +builtin_platform_driver(mac_driver); diff --git a/drivers/net/ethernet/freescale/fman/mac.h b/drivers/net/ethernet/freescale/fman/mac.h new file mode 100644 index 000000000000..0211cc9a46d6 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/mac.h @@ -0,0 +1,97 @@ +/* Copyright 2008-2015 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MAC_H +#define __MAC_H + +#include +#include +#include +#include + +#include "fman_port.h" +#include "fman.h" +#include "fman_mac.h" + +struct fman_mac; +struct mac_priv_s; + +struct mac_device { + struct resource *res; + u8 addr[ETH_ALEN]; + struct fman_port *port[2]; + u32 if_support; + struct phy_device *phy_dev; + + bool autoneg_pause; + bool rx_pause_req; + bool tx_pause_req; + bool rx_pause_active; + bool tx_pause_active; + bool promisc; + + int (*init_phy)(struct net_device *net_dev, struct mac_device *mac_dev); + int (*init)(struct mac_device *mac_dev); + int (*start)(struct mac_device *mac_dev); + int (*stop)(struct mac_device *mac_dev); + int (*set_promisc)(struct fman_mac *mac_dev, bool enable); + int (*change_addr)(struct fman_mac *mac_dev, enet_addr_t *enet_addr); + int (*set_multi)(struct net_device *net_dev, + struct mac_device *mac_dev); + int (*set_rx_pause)(struct fman_mac *mac_dev, bool en); + int (*set_tx_pause)(struct fman_mac *mac_dev, u8 priority, + u16 pause_time, u16 thresh_time); + int (*set_exception)(struct fman_mac *mac_dev, + enum fman_mac_exceptions exception, bool enable); + int (*add_hash_mac_addr)(struct fman_mac *mac_dev, + enet_addr_t *eth_addr); + int (*remove_hash_mac_addr)(struct fman_mac *mac_dev, + enet_addr_t *eth_addr); + + struct fman_mac *fman_mac; + struct mac_priv_s *priv; +}; + +struct dpaa_eth_data { + struct device_node *mac_node; + struct mac_device *mac_dev; + int mac_hw_id; + int fman_hw_id; +}; + +extern const char *mac_driver_description; + +int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx); + +void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause, + bool *tx_pause); + +#endif /* __MAC_H */ -- cgit v1.2.3-59-g8ed1b From 032c5e82847a2214c3196a90f0aeba0ce252de58 Mon Sep 17 00:00:00 2001 From: Thomas Falcon Date: Mon, 21 Dec 2015 11:26:06 -0600 Subject: Driver for IBM System i/p VNIC protocol This is a new device driver for a high performance SR-IOV assisted virtual network for IBM System p and IBM System i systems. The SR-IOV VF will be attached to the VIOS partition and mapped to the Linux client via the hypervisor's VNIC protocol that this driver implements. This driver is able to perform basic tx and rx, new features and improvements will be added as they are being developed and tested. Signed-off-by: Thomas Falcon Signed-off-by: John Allen Signed-off-by: David S. Miller --- MAINTAINERS | 7 + arch/powerpc/include/asm/hvcall.h | 20 + drivers/net/ethernet/ibm/Kconfig | 10 + drivers/net/ethernet/ibm/Makefile | 1 + drivers/net/ethernet/ibm/ibmvnic.c | 3585 ++++++++++++++++++++++++++++++++++++ drivers/net/ethernet/ibm/ibmvnic.h | 1046 +++++++++++ 6 files changed, 4669 insertions(+) create mode 100644 drivers/net/ethernet/ibm/ibmvnic.c create mode 100644 drivers/net/ethernet/ibm/ibmvnic.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index c6b78b053939..2eed9bc338db 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5292,6 +5292,13 @@ L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/ibm/ibmveth.* +IBM Power SRIOV Virtual NIC Device Driver +M: Thomas Falcon +M: John Allen +L: netdev@vger.kernel.org +S: Supported +F: drivers/net/ethernet/ibm/ibmvnic.* + IBM Power Virtual SCSI Device Drivers M: Tyrel Datwyler L: linux-scsi@vger.kernel.org diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h index 85bc8c0d257b..e3b54dd4f730 100644 --- a/arch/powerpc/include/asm/hvcall.h +++ b/arch/powerpc/include/asm/hvcall.h @@ -258,11 +258,16 @@ #define H_DEL_CONN 0x288 #define H_JOIN 0x298 #define H_VASI_STATE 0x2A4 +#define H_VIOCTL 0x2A8 #define H_ENABLE_CRQ 0x2B0 #define H_GET_EM_PARMS 0x2B8 #define H_SET_MPP 0x2D0 #define H_GET_MPP 0x2D4 +#define H_REG_SUB_CRQ 0x2DC #define H_HOME_NODE_ASSOCIATIVITY 0x2EC +#define H_FREE_SUB_CRQ 0x2E0 +#define H_SEND_SUB_CRQ 0x2E4 +#define H_SEND_SUB_CRQ_INDIRECT 0x2E8 #define H_BEST_ENERGY 0x2F4 #define H_XIRR_X 0x2FC #define H_RANDOM 0x300 @@ -271,6 +276,21 @@ #define H_SET_MODE 0x31C #define MAX_HCALL_OPCODE H_SET_MODE +/* H_VIOCTL functions */ +#define H_GET_VIOA_DUMP_SIZE 0x01 +#define H_GET_VIOA_DUMP 0x02 +#define H_GET_ILLAN_NUM_VLAN_IDS 0x03 +#define H_GET_ILLAN_VLAN_ID_LIST 0x04 +#define H_GET_ILLAN_SWITCH_ID 0x05 +#define H_DISABLE_MIGRATION 0x06 +#define H_ENABLE_MIGRATION 0x07 +#define H_GET_PARTNER_INFO 0x08 +#define H_GET_PARTNER_WWPN_LIST 0x09 +#define H_DISABLE_ALL_VIO_INTS 0x0A +#define H_DISABLE_VIO_INTERRUPT 0x0B +#define H_ENABLE_VIO_INTERRUPT 0x0C + + /* Platform specific hcalls, used by KVM */ #define H_RTAS 0xf000 diff --git a/drivers/net/ethernet/ibm/Kconfig b/drivers/net/ethernet/ibm/Kconfig index 99c1cebd002d..37dceabf8861 100644 --- a/drivers/net/ethernet/ibm/Kconfig +++ b/drivers/net/ethernet/ibm/Kconfig @@ -37,4 +37,14 @@ config EHEA To compile the driver as a module, choose M here. The module will be called ehea. +config IBMVNIC + tristate "IBM Virtual NIC support" + depends on PPC_PSERIES + ---help--- + This driver supports Virtual NIC adapters on IBM i and IBM System p + systems. + + To compile this driver as a module, choose M here. The module will + be called ibmvnic. + endif # NET_VENDOR_IBM diff --git a/drivers/net/ethernet/ibm/Makefile b/drivers/net/ethernet/ibm/Makefile index 2f04e71a5926..447865c8b632 100644 --- a/drivers/net/ethernet/ibm/Makefile +++ b/drivers/net/ethernet/ibm/Makefile @@ -3,5 +3,6 @@ # obj-$(CONFIG_IBMVETH) += ibmveth.o +obj-$(CONFIG_IBMVNIC) += ibmvnic.o obj-$(CONFIG_IBM_EMAC) += emac/ obj-$(CONFIG_EHEA) += ehea/ diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c new file mode 100644 index 000000000000..7d6570843723 --- /dev/null +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -0,0 +1,3585 @@ +/**************************************************************************/ +/* */ +/* IBM System i and System p Virtual NIC Device Driver */ +/* Copyright (C) 2014 IBM Corp. */ +/* Santiago Leon (santi_leon@yahoo.com) */ +/* Thomas Falcon (tlfalcon@linux.vnet.ibm.com) */ +/* John Allen (jallen@linux.vnet.ibm.com) */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program. */ +/* */ +/* This module contains the implementation of a virtual ethernet device */ +/* for use with IBM i/p Series LPAR Linux. It utilizes the logical LAN */ +/* option of the RS/6000 Platform Architecture to interface with virtual */ +/* ethernet NICs that are presented to the partition by the hypervisor. */ +/* */ +/* Messages are passed between the VNIC driver and the VNIC server using */ +/* Command/Response Queues (CRQs) and sub CRQs (sCRQs). CRQs are used to */ +/* issue and receive commands that initiate communication with the server */ +/* on driver initialization. Sub CRQs (sCRQs) are similar to CRQs, but */ +/* are used by the driver to notify the server that a packet is */ +/* ready for transmission or that a buffer has been added to receive a */ +/* packet. Subsequently, sCRQs are used by the server to notify the */ +/* driver that a packet transmission has been completed or that a packet */ +/* has been received and placed in a waiting buffer. */ +/* */ +/* In lieu of a more conventional "on-the-fly" DMA mapping strategy in */ +/* which skbs are DMA mapped and immediately unmapped when the transmit */ +/* or receive has been completed, the VNIC driver is required to use */ +/* "long term mapping". This entails that large, continuous DMA mapped */ +/* buffers are allocated on driver initialization and these buffers are */ +/* then continuously reused to pass skbs to and from the VNIC server. */ +/* */ +/**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ibmvnic.h" + +static const char ibmvnic_driver_name[] = "ibmvnic"; +static const char ibmvnic_driver_string[] = "IBM System i/p Virtual NIC Driver"; + +MODULE_AUTHOR("Santiago Leon "); +MODULE_DESCRIPTION("IBM System i/p Virtual NIC Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(IBMVNIC_DRIVER_VERSION); + +static int ibmvnic_version = IBMVNIC_INITIAL_VERSION; +static int ibmvnic_remove(struct vio_dev *); +static void release_sub_crqs(struct ibmvnic_adapter *); +static int ibmvnic_reset_crq(struct ibmvnic_adapter *); +static int ibmvnic_send_crq_init(struct ibmvnic_adapter *); +static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *); +static int ibmvnic_send_crq(struct ibmvnic_adapter *, union ibmvnic_crq *); +static int send_subcrq(struct ibmvnic_adapter *adapter, u64 remote_handle, + union sub_crq *sub_crq); +static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance); +static int enable_scrq_irq(struct ibmvnic_adapter *, + struct ibmvnic_sub_crq_queue *); +static int disable_scrq_irq(struct ibmvnic_adapter *, + struct ibmvnic_sub_crq_queue *); +static int pending_scrq(struct ibmvnic_adapter *, + struct ibmvnic_sub_crq_queue *); +static union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *, + struct ibmvnic_sub_crq_queue *); +static int ibmvnic_poll(struct napi_struct *napi, int data); +static void send_map_query(struct ibmvnic_adapter *adapter); +static void send_request_map(struct ibmvnic_adapter *, dma_addr_t, __be32, u8); +static void send_request_unmap(struct ibmvnic_adapter *, u8); + +struct ibmvnic_stat { + char name[ETH_GSTRING_LEN]; + int offset; +}; + +#define IBMVNIC_STAT_OFF(stat) (offsetof(struct ibmvnic_adapter, stats) + \ + offsetof(struct ibmvnic_statistics, stat)) +#define IBMVNIC_GET_STAT(a, off) (*((u64 *)(((unsigned long)(a)) + off))) + +static const struct ibmvnic_stat ibmvnic_stats[] = { + {"rx_packets", IBMVNIC_STAT_OFF(rx_packets)}, + {"rx_bytes", IBMVNIC_STAT_OFF(rx_bytes)}, + {"tx_packets", IBMVNIC_STAT_OFF(tx_packets)}, + {"tx_bytes", IBMVNIC_STAT_OFF(tx_bytes)}, + {"ucast_tx_packets", IBMVNIC_STAT_OFF(ucast_tx_packets)}, + {"ucast_rx_packets", IBMVNIC_STAT_OFF(ucast_rx_packets)}, + {"mcast_tx_packets", IBMVNIC_STAT_OFF(mcast_tx_packets)}, + {"mcast_rx_packets", IBMVNIC_STAT_OFF(mcast_rx_packets)}, + {"bcast_tx_packets", IBMVNIC_STAT_OFF(bcast_tx_packets)}, + {"bcast_rx_packets", IBMVNIC_STAT_OFF(bcast_rx_packets)}, + {"align_errors", IBMVNIC_STAT_OFF(align_errors)}, + {"fcs_errors", IBMVNIC_STAT_OFF(fcs_errors)}, + {"single_collision_frames", IBMVNIC_STAT_OFF(single_collision_frames)}, + {"multi_collision_frames", IBMVNIC_STAT_OFF(multi_collision_frames)}, + {"sqe_test_errors", IBMVNIC_STAT_OFF(sqe_test_errors)}, + {"deferred_tx", IBMVNIC_STAT_OFF(deferred_tx)}, + {"late_collisions", IBMVNIC_STAT_OFF(late_collisions)}, + {"excess_collisions", IBMVNIC_STAT_OFF(excess_collisions)}, + {"internal_mac_tx_errors", IBMVNIC_STAT_OFF(internal_mac_tx_errors)}, + {"carrier_sense", IBMVNIC_STAT_OFF(carrier_sense)}, + {"too_long_frames", IBMVNIC_STAT_OFF(too_long_frames)}, + {"internal_mac_rx_errors", IBMVNIC_STAT_OFF(internal_mac_rx_errors)}, +}; + +static long h_reg_sub_crq(unsigned long unit_address, unsigned long token, + unsigned long length, unsigned long *number, + unsigned long *irq) +{ + unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; + long rc; + + rc = plpar_hcall(H_REG_SUB_CRQ, retbuf, unit_address, token, length); + *number = retbuf[0]; + *irq = retbuf[1]; + + return rc; +} + +/* net_device_ops functions */ + +static void init_rx_pool(struct ibmvnic_adapter *adapter, + struct ibmvnic_rx_pool *rx_pool, int num, int index, + int buff_size, int active) +{ + netdev_dbg(adapter->netdev, + "Initializing rx_pool %d, %d buffs, %d bytes each\n", + index, num, buff_size); + rx_pool->size = num; + rx_pool->index = index; + rx_pool->buff_size = buff_size; + rx_pool->active = active; +} + +static int alloc_long_term_buff(struct ibmvnic_adapter *adapter, + struct ibmvnic_long_term_buff *ltb, int size) +{ + struct device *dev = &adapter->vdev->dev; + + ltb->size = size; + ltb->buff = dma_alloc_coherent(dev, ltb->size, <b->addr, + GFP_KERNEL); + + if (!ltb->buff) { + dev_err(dev, "Couldn't alloc long term buffer\n"); + return -ENOMEM; + } + ltb->map_id = adapter->map_id; + adapter->map_id++; + send_request_map(adapter, ltb->addr, + ltb->size, ltb->map_id); + init_completion(&adapter->fw_done); + wait_for_completion(&adapter->fw_done); + return 0; +} + +static void free_long_term_buff(struct ibmvnic_adapter *adapter, + struct ibmvnic_long_term_buff *ltb) +{ + struct device *dev = &adapter->vdev->dev; + + dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr); + send_request_unmap(adapter, ltb->map_id); +} + +static int alloc_rx_pool(struct ibmvnic_adapter *adapter, + struct ibmvnic_rx_pool *pool) +{ + struct device *dev = &adapter->vdev->dev; + int i; + + pool->free_map = kcalloc(pool->size, sizeof(int), GFP_KERNEL); + if (!pool->free_map) + return -ENOMEM; + + pool->rx_buff = kcalloc(pool->size, sizeof(struct ibmvnic_rx_buff), + GFP_KERNEL); + + if (!pool->rx_buff) { + dev_err(dev, "Couldn't alloc rx buffers\n"); + kfree(pool->free_map); + return -ENOMEM; + } + + if (alloc_long_term_buff(adapter, &pool->long_term_buff, + pool->size * pool->buff_size)) { + kfree(pool->free_map); + kfree(pool->rx_buff); + return -ENOMEM; + } + + for (i = 0; i < pool->size; ++i) + pool->free_map[i] = i; + + atomic_set(&pool->available, 0); + pool->next_alloc = 0; + pool->next_free = 0; + + return 0; +} + +static void replenish_rx_pool(struct ibmvnic_adapter *adapter, + struct ibmvnic_rx_pool *pool) +{ + int count = pool->size - atomic_read(&pool->available); + struct device *dev = &adapter->vdev->dev; + int buffers_added = 0; + unsigned long lpar_rc; + union sub_crq sub_crq; + struct sk_buff *skb; + unsigned int offset; + dma_addr_t dma_addr; + unsigned char *dst; + u64 *handle_array; + int shift = 0; + int index; + int i; + + handle_array = (u64 *)((u8 *)(adapter->login_rsp_buf) + + be32_to_cpu(adapter->login_rsp_buf-> + off_rxadd_subcrqs)); + + for (i = 0; i < count; ++i) { + skb = alloc_skb(pool->buff_size, GFP_ATOMIC); + if (!skb) { + dev_err(dev, "Couldn't replenish rx buff\n"); + adapter->replenish_no_mem++; + break; + } + + index = pool->free_map[pool->next_free]; + + if (pool->rx_buff[index].skb) + dev_err(dev, "Inconsistent free_map!\n"); + + /* Copy the skb to the long term mapped DMA buffer */ + offset = index * pool->buff_size; + dst = pool->long_term_buff.buff + offset; + memset(dst, 0, pool->buff_size); + dma_addr = pool->long_term_buff.addr + offset; + pool->rx_buff[index].data = dst; + + pool->free_map[pool->next_free] = IBMVNIC_INVALID_MAP; + pool->rx_buff[index].dma = dma_addr; + pool->rx_buff[index].skb = skb; + pool->rx_buff[index].pool_index = pool->index; + pool->rx_buff[index].size = pool->buff_size; + + memset(&sub_crq, 0, sizeof(sub_crq)); + sub_crq.rx_add.first = IBMVNIC_CRQ_CMD; + sub_crq.rx_add.correlator = + cpu_to_be64((u64)&pool->rx_buff[index]); + sub_crq.rx_add.ioba = cpu_to_be32(dma_addr); + sub_crq.rx_add.map_id = pool->long_term_buff.map_id; + + /* The length field of the sCRQ is defined to be 24 bits so the + * buffer size needs to be left shifted by a byte before it is + * converted to big endian to prevent the last byte from being + * truncated. + */ +#ifdef __LITTLE_ENDIAN__ + shift = 8; +#endif + sub_crq.rx_add.len = cpu_to_be32(pool->buff_size << shift); + + lpar_rc = send_subcrq(adapter, handle_array[pool->index], + &sub_crq); + if (lpar_rc != H_SUCCESS) + goto failure; + + buffers_added++; + adapter->replenish_add_buff_success++; + pool->next_free = (pool->next_free + 1) % pool->size; + } + atomic_add(buffers_added, &pool->available); + return; + +failure: + dev_info(dev, "replenish pools failure\n"); + pool->free_map[pool->next_free] = index; + pool->rx_buff[index].skb = NULL; + if (!dma_mapping_error(dev, dma_addr)) + dma_unmap_single(dev, dma_addr, pool->buff_size, + DMA_FROM_DEVICE); + + dev_kfree_skb_any(skb); + adapter->replenish_add_buff_failure++; + atomic_add(buffers_added, &pool->available); +} + +static void replenish_pools(struct ibmvnic_adapter *adapter) +{ + int i; + + if (adapter->migrated) + return; + + adapter->replenish_task_cycles++; + for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); + i++) { + if (adapter->rx_pool[i].active) + replenish_rx_pool(adapter, &adapter->rx_pool[i]); + } +} + +static void free_rx_pool(struct ibmvnic_adapter *adapter, + struct ibmvnic_rx_pool *pool) +{ + int i; + + kfree(pool->free_map); + pool->free_map = NULL; + + if (!pool->rx_buff) + return; + + for (i = 0; i < pool->size; i++) { + if (pool->rx_buff[i].skb) { + dev_kfree_skb_any(pool->rx_buff[i].skb); + pool->rx_buff[i].skb = NULL; + } + } + kfree(pool->rx_buff); + pool->rx_buff = NULL; +} + +static int ibmvnic_open(struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_tx_pool *tx_pool; + union ibmvnic_crq crq; + int rxadd_subcrqs; + u64 *size_array; + int tx_subcrqs; + int i, j; + + rxadd_subcrqs = + be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); + tx_subcrqs = + be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); + size_array = (u64 *)((u8 *)(adapter->login_rsp_buf) + + be32_to_cpu(adapter->login_rsp_buf-> + off_rxadd_buff_size)); + adapter->map_id = 1; + adapter->napi = kcalloc(adapter->req_rx_queues, + sizeof(struct napi_struct), GFP_KERNEL); + if (!adapter->napi) + goto alloc_napi_failed; + for (i = 0; i < adapter->req_rx_queues; i++) { + netif_napi_add(netdev, &adapter->napi[i], ibmvnic_poll, + NAPI_POLL_WEIGHT); + napi_enable(&adapter->napi[i]); + } + adapter->rx_pool = + kcalloc(rxadd_subcrqs, sizeof(struct ibmvnic_rx_pool), GFP_KERNEL); + + if (!adapter->rx_pool) + goto rx_pool_arr_alloc_failed; + send_map_query(adapter); + for (i = 0; i < rxadd_subcrqs; i++) { + init_rx_pool(adapter, &adapter->rx_pool[i], + IBMVNIC_BUFFS_PER_POOL, i, + be64_to_cpu(size_array[i]), 1); + if (alloc_rx_pool(adapter, &adapter->rx_pool[i])) { + dev_err(dev, "Couldn't alloc rx pool\n"); + goto rx_pool_alloc_failed; + } + } + adapter->tx_pool = + kcalloc(tx_subcrqs, sizeof(struct ibmvnic_tx_pool), GFP_KERNEL); + + if (!adapter->tx_pool) + goto tx_pool_arr_alloc_failed; + for (i = 0; i < tx_subcrqs; i++) { + tx_pool = &adapter->tx_pool[i]; + tx_pool->tx_buff = + kcalloc(adapter->max_tx_entries_per_subcrq, + sizeof(struct ibmvnic_tx_buff), GFP_KERNEL); + if (!tx_pool->tx_buff) + goto tx_pool_alloc_failed; + + if (alloc_long_term_buff(adapter, &tx_pool->long_term_buff, + adapter->max_tx_entries_per_subcrq * + adapter->req_mtu)) + goto tx_ltb_alloc_failed; + + tx_pool->free_map = + kcalloc(adapter->max_tx_entries_per_subcrq, + sizeof(int), GFP_KERNEL); + if (!tx_pool->free_map) + goto tx_fm_alloc_failed; + + for (j = 0; j < adapter->max_tx_entries_per_subcrq; j++) + tx_pool->free_map[j] = j; + + tx_pool->consumer_index = 0; + tx_pool->producer_index = 0; + } + adapter->bounce_buffer_size = + (netdev->mtu + ETH_HLEN - 1) / PAGE_SIZE + 1; + adapter->bounce_buffer = kmalloc(adapter->bounce_buffer_size, + GFP_KERNEL); + if (!adapter->bounce_buffer) + goto bounce_alloc_failed; + + adapter->bounce_buffer_dma = dma_map_single(dev, adapter->bounce_buffer, + adapter->bounce_buffer_size, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, adapter->bounce_buffer_dma)) { + dev_err(dev, "Couldn't map tx bounce buffer\n"); + goto bounce_map_failed; + } + replenish_pools(adapter); + + /* We're ready to receive frames, enable the sub-crq interrupts and + * set the logical link state to up + */ + for (i = 0; i < adapter->req_rx_queues; i++) + enable_scrq_irq(adapter, adapter->rx_scrq[i]); + + for (i = 0; i < adapter->req_tx_queues; i++) + enable_scrq_irq(adapter, adapter->tx_scrq[i]); + + memset(&crq, 0, sizeof(crq)); + crq.logical_link_state.first = IBMVNIC_CRQ_CMD; + crq.logical_link_state.cmd = LOGICAL_LINK_STATE; + crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_UP; + ibmvnic_send_crq(adapter, &crq); + + netif_start_queue(netdev); + return 0; + +bounce_map_failed: + kfree(adapter->bounce_buffer); +bounce_alloc_failed: + i = tx_subcrqs - 1; + kfree(adapter->tx_pool[i].free_map); +tx_fm_alloc_failed: + free_long_term_buff(adapter, &adapter->tx_pool[i].long_term_buff); +tx_ltb_alloc_failed: + kfree(adapter->tx_pool[i].tx_buff); +tx_pool_alloc_failed: + for (j = 0; j < i; j++) { + kfree(adapter->tx_pool[j].tx_buff); + free_long_term_buff(adapter, + &adapter->tx_pool[j].long_term_buff); + kfree(adapter->tx_pool[j].free_map); + } + kfree(adapter->tx_pool); + adapter->tx_pool = NULL; +tx_pool_arr_alloc_failed: + i = rxadd_subcrqs; +rx_pool_alloc_failed: + for (j = 0; j < i; j++) { + free_rx_pool(adapter, &adapter->rx_pool[j]); + free_long_term_buff(adapter, + &adapter->rx_pool[j].long_term_buff); + } + kfree(adapter->rx_pool); + adapter->rx_pool = NULL; +rx_pool_arr_alloc_failed: + for (i = 0; i < adapter->req_rx_queues; i++) + napi_enable(&adapter->napi[i]); +alloc_napi_failed: + return -ENOMEM; +} + +static int ibmvnic_close(struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + struct device *dev = &adapter->vdev->dev; + union ibmvnic_crq crq; + int i; + + adapter->closing = true; + + for (i = 0; i < adapter->req_rx_queues; i++) + napi_disable(&adapter->napi[i]); + + netif_stop_queue(netdev); + + if (adapter->bounce_buffer) { + if (!dma_mapping_error(dev, adapter->bounce_buffer_dma)) { + dma_unmap_single(&adapter->vdev->dev, + adapter->bounce_buffer_dma, + adapter->bounce_buffer_size, + DMA_BIDIRECTIONAL); + adapter->bounce_buffer_dma = DMA_ERROR_CODE; + } + kfree(adapter->bounce_buffer); + adapter->bounce_buffer = NULL; + } + + memset(&crq, 0, sizeof(crq)); + crq.logical_link_state.first = IBMVNIC_CRQ_CMD; + crq.logical_link_state.cmd = LOGICAL_LINK_STATE; + crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_DN; + ibmvnic_send_crq(adapter, &crq); + + for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); + i++) { + kfree(adapter->tx_pool[i].tx_buff); + free_long_term_buff(adapter, + &adapter->tx_pool[i].long_term_buff); + kfree(adapter->tx_pool[i].free_map); + } + kfree(adapter->tx_pool); + adapter->tx_pool = NULL; + + for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); + i++) { + free_rx_pool(adapter, &adapter->rx_pool[i]); + free_long_term_buff(adapter, + &adapter->rx_pool[i].long_term_buff); + } + kfree(adapter->rx_pool); + adapter->rx_pool = NULL; + + adapter->closing = false; + + return 0; +} + +static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + int queue_num = skb_get_queue_mapping(skb); + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_tx_buff *tx_buff = NULL; + struct ibmvnic_tx_pool *tx_pool; + unsigned int tx_send_failed = 0; + unsigned int tx_map_failed = 0; + unsigned int tx_dropped = 0; + unsigned int tx_packets = 0; + unsigned int tx_bytes = 0; + dma_addr_t data_dma_addr; + struct netdev_queue *txq; + bool used_bounce = false; + unsigned long lpar_rc; + union sub_crq tx_crq; + unsigned int offset; + unsigned char *dst; + u64 *handle_array; + int index = 0; + int ret = 0; + + tx_pool = &adapter->tx_pool[queue_num]; + txq = netdev_get_tx_queue(netdev, skb_get_queue_mapping(skb)); + handle_array = (u64 *)((u8 *)(adapter->login_rsp_buf) + + be32_to_cpu(adapter->login_rsp_buf-> + off_txsubm_subcrqs)); + if (adapter->migrated) { + tx_send_failed++; + tx_dropped++; + ret = NETDEV_TX_BUSY; + goto out; + } + + index = tx_pool->free_map[tx_pool->consumer_index]; + offset = index * adapter->req_mtu; + dst = tx_pool->long_term_buff.buff + offset; + memset(dst, 0, adapter->req_mtu); + skb_copy_from_linear_data(skb, dst, skb->len); + data_dma_addr = tx_pool->long_term_buff.addr + offset; + + tx_pool->consumer_index = + (tx_pool->consumer_index + 1) % + adapter->max_tx_entries_per_subcrq; + + tx_buff = &tx_pool->tx_buff[index]; + tx_buff->skb = skb; + tx_buff->data_dma[0] = data_dma_addr; + tx_buff->data_len[0] = skb->len; + tx_buff->index = index; + tx_buff->pool_index = queue_num; + tx_buff->last_frag = true; + tx_buff->used_bounce = used_bounce; + + memset(&tx_crq, 0, sizeof(tx_crq)); + tx_crq.v1.first = IBMVNIC_CRQ_CMD; + tx_crq.v1.type = IBMVNIC_TX_DESC; + tx_crq.v1.n_crq_elem = 1; + tx_crq.v1.n_sge = 1; + tx_crq.v1.flags1 = IBMVNIC_TX_COMP_NEEDED; + tx_crq.v1.correlator = cpu_to_be32(index); + tx_crq.v1.dma_reg = cpu_to_be16(tx_pool->long_term_buff.map_id); + tx_crq.v1.sge_len = cpu_to_be32(skb->len); + tx_crq.v1.ioba = cpu_to_be64(data_dma_addr); + + if (adapter->vlan_header_insertion) { + tx_crq.v1.flags2 |= IBMVNIC_TX_VLAN_INSERT; + tx_crq.v1.vlan_id = cpu_to_be16(skb->vlan_tci); + } + + if (skb->protocol == htons(ETH_P_IP)) { + if (ip_hdr(skb)->version == 4) + tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_IPV4; + else if (ip_hdr(skb)->version == 6) + tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_IPV6; + + if (ip_hdr(skb)->protocol == IPPROTO_TCP) + tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_TCP; + else if (ip_hdr(skb)->protocol != IPPROTO_TCP) + tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_UDP; + } + + if (skb->ip_summed == CHECKSUM_PARTIAL) + tx_crq.v1.flags1 |= IBMVNIC_TX_CHKSUM_OFFLOAD; + + lpar_rc = send_subcrq(adapter, handle_array[0], &tx_crq); + + if (lpar_rc != H_SUCCESS) { + dev_err(dev, "tx failed with code %ld\n", lpar_rc); + + if (tx_pool->consumer_index == 0) + tx_pool->consumer_index = + adapter->max_tx_entries_per_subcrq - 1; + else + tx_pool->consumer_index--; + + tx_send_failed++; + tx_dropped++; + ret = NETDEV_TX_BUSY; + goto out; + } + tx_packets++; + tx_bytes += skb->len; + txq->trans_start = jiffies; + ret = NETDEV_TX_OK; + +out: + netdev->stats.tx_dropped += tx_dropped; + netdev->stats.tx_bytes += tx_bytes; + netdev->stats.tx_packets += tx_packets; + adapter->tx_send_failed += tx_send_failed; + adapter->tx_map_failed += tx_map_failed; + + return ret; +} + +static void ibmvnic_set_multi(struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + struct netdev_hw_addr *ha; + union ibmvnic_crq crq; + + memset(&crq, 0, sizeof(crq)); + crq.request_capability.first = IBMVNIC_CRQ_CMD; + crq.request_capability.cmd = REQUEST_CAPABILITY; + + if (netdev->flags & IFF_PROMISC) { + if (!adapter->promisc_supported) + return; + } else { + if (netdev->flags & IFF_ALLMULTI) { + /* Accept all multicast */ + memset(&crq, 0, sizeof(crq)); + crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD; + crq.multicast_ctrl.cmd = MULTICAST_CTRL; + crq.multicast_ctrl.flags = IBMVNIC_ENABLE_ALL; + ibmvnic_send_crq(adapter, &crq); + } else if (netdev_mc_empty(netdev)) { + /* Reject all multicast */ + memset(&crq, 0, sizeof(crq)); + crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD; + crq.multicast_ctrl.cmd = MULTICAST_CTRL; + crq.multicast_ctrl.flags = IBMVNIC_DISABLE_ALL; + ibmvnic_send_crq(adapter, &crq); + } else { + /* Accept one or more multicast(s) */ + netdev_for_each_mc_addr(ha, netdev) { + memset(&crq, 0, sizeof(crq)); + crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD; + crq.multicast_ctrl.cmd = MULTICAST_CTRL; + crq.multicast_ctrl.flags = IBMVNIC_ENABLE_MC; + ether_addr_copy(&crq.multicast_ctrl.mac_addr[0], + ha->addr); + ibmvnic_send_crq(adapter, &crq); + } + } + } +} + +static int ibmvnic_set_mac(struct net_device *netdev, void *p) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + struct sockaddr *addr = p; + union ibmvnic_crq crq; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memset(&crq, 0, sizeof(crq)); + crq.change_mac_addr.first = IBMVNIC_CRQ_CMD; + crq.change_mac_addr.cmd = CHANGE_MAC_ADDR; + ether_addr_copy(&crq.change_mac_addr.mac_addr[0], addr->sa_data); + ibmvnic_send_crq(adapter, &crq); + /* netdev->dev_addr is changed in handle_change_mac_rsp function */ + return 0; +} + +static int ibmvnic_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + + if (new_mtu > adapter->req_mtu || new_mtu < adapter->min_mtu) + return -EINVAL; + + netdev->mtu = new_mtu; + return 0; +} + +static void ibmvnic_tx_timeout(struct net_device *dev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(dev); + int rc; + + /* Adapter timed out, resetting it */ + release_sub_crqs(adapter); + rc = ibmvnic_reset_crq(adapter); + if (rc) + dev_err(&adapter->vdev->dev, "Adapter timeout, reset failed\n"); + else + ibmvnic_send_crq_init(adapter); +} + +static void remove_buff_from_pool(struct ibmvnic_adapter *adapter, + struct ibmvnic_rx_buff *rx_buff) +{ + struct ibmvnic_rx_pool *pool = &adapter->rx_pool[rx_buff->pool_index]; + + rx_buff->skb = NULL; + + pool->free_map[pool->next_alloc] = (int)(rx_buff - pool->rx_buff); + pool->next_alloc = (pool->next_alloc + 1) % pool->size; + + atomic_dec(&pool->available); +} + +static int ibmvnic_poll(struct napi_struct *napi, int budget) +{ + struct net_device *netdev = napi->dev; + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + int scrq_num = (int)(napi - adapter->napi); + int frames_processed = 0; +restart_poll: + while (frames_processed < budget) { + struct sk_buff *skb; + struct ibmvnic_rx_buff *rx_buff; + union sub_crq *next; + u32 length; + u16 offset; + u8 flags = 0; + + if (!pending_scrq(adapter, adapter->rx_scrq[scrq_num])) + break; + next = ibmvnic_next_scrq(adapter, adapter->rx_scrq[scrq_num]); + rx_buff = + (struct ibmvnic_rx_buff *)be64_to_cpu(next-> + rx_comp.correlator); + /* do error checking */ + if (next->rx_comp.rc) { + netdev_err(netdev, "rx error %x\n", next->rx_comp.rc); + /* free the entry */ + next->rx_comp.first = 0; + remove_buff_from_pool(adapter, rx_buff); + break; + } + + length = be32_to_cpu(next->rx_comp.len); + offset = be16_to_cpu(next->rx_comp.off_frame_data); + flags = next->rx_comp.flags; + skb = rx_buff->skb; + skb_copy_to_linear_data(skb, rx_buff->data + offset, + length); + skb->vlan_tci = be16_to_cpu(next->rx_comp.vlan_tci); + /* free the entry */ + next->rx_comp.first = 0; + remove_buff_from_pool(adapter, rx_buff); + + skb_put(skb, length); + skb->protocol = eth_type_trans(skb, netdev); + + if (flags & IBMVNIC_IP_CHKSUM_GOOD && + flags & IBMVNIC_TCP_UDP_CHKSUM_GOOD) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + + length = skb->len; + napi_gro_receive(napi, skb); /* send it up */ + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += length; + frames_processed++; + } + replenish_pools(adapter); + + if (frames_processed < budget) { + enable_scrq_irq(adapter, adapter->rx_scrq[scrq_num]); + napi_complete(napi); + if (pending_scrq(adapter, adapter->rx_scrq[scrq_num]) && + napi_reschedule(napi)) { + disable_scrq_irq(adapter, adapter->rx_scrq[scrq_num]); + goto restart_poll; + } + } + return frames_processed; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void ibmvnic_netpoll_controller(struct net_device *dev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(dev); + int i; + + replenish_pools(netdev_priv(dev)); + for (i = 0; i < adapter->req_rx_queues; i++) + ibmvnic_interrupt_rx(adapter->rx_scrq[i]->irq, + adapter->rx_scrq[i]); +} +#endif + +static const struct net_device_ops ibmvnic_netdev_ops = { + .ndo_open = ibmvnic_open, + .ndo_stop = ibmvnic_close, + .ndo_start_xmit = ibmvnic_xmit, + .ndo_set_rx_mode = ibmvnic_set_multi, + .ndo_set_mac_address = ibmvnic_set_mac, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = ibmvnic_change_mtu, + .ndo_tx_timeout = ibmvnic_tx_timeout, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = ibmvnic_netpoll_controller, +#endif +}; + +/* ethtool functions */ + +static int ibmvnic_get_settings(struct net_device *netdev, + struct ethtool_cmd *cmd) +{ + cmd->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | + SUPPORTED_FIBRE); + cmd->advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | + ADVERTISED_FIBRE); + ethtool_cmd_speed_set(cmd, SPEED_1000); + cmd->duplex = DUPLEX_FULL; + cmd->port = PORT_FIBRE; + cmd->phy_address = 0; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = AUTONEG_ENABLE; + cmd->maxtxpkt = 0; + cmd->maxrxpkt = 1; + return 0; +} + +static void ibmvnic_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, ibmvnic_driver_name, sizeof(info->driver)); + strlcpy(info->version, IBMVNIC_DRIVER_VERSION, sizeof(info->version)); +} + +static u32 ibmvnic_get_msglevel(struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + + return adapter->msg_enable; +} + +static void ibmvnic_set_msglevel(struct net_device *netdev, u32 data) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + + adapter->msg_enable = data; +} + +static u32 ibmvnic_get_link(struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + + /* Don't need to send a query because we request a logical link up at + * init and then we wait for link state indications + */ + return adapter->logical_link_state; +} + +static void ibmvnic_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + ring->rx_max_pending = 0; + ring->tx_max_pending = 0; + ring->rx_mini_max_pending = 0; + ring->rx_jumbo_max_pending = 0; + ring->rx_pending = 0; + ring->tx_pending = 0; + ring->rx_mini_pending = 0; + ring->rx_jumbo_pending = 0; +} + +static void ibmvnic_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + int i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++, data += ETH_GSTRING_LEN) + memcpy(data, ibmvnic_stats[i].name, ETH_GSTRING_LEN); +} + +static int ibmvnic_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(ibmvnic_stats); + default: + return -EOPNOTSUPP; + } +} + +static void ibmvnic_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct ibmvnic_adapter *adapter = netdev_priv(dev); + union ibmvnic_crq crq; + int i; + + memset(&crq, 0, sizeof(crq)); + crq.request_statistics.first = IBMVNIC_CRQ_CMD; + crq.request_statistics.cmd = REQUEST_STATISTICS; + crq.request_statistics.ioba = cpu_to_be32(adapter->stats_token); + crq.request_statistics.len = + cpu_to_be32(sizeof(struct ibmvnic_statistics)); + ibmvnic_send_crq(adapter, &crq); + + /* Wait for data to be written */ + init_completion(&adapter->stats_done); + wait_for_completion(&adapter->stats_done); + + for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++) + data[i] = IBMVNIC_GET_STAT(adapter, ibmvnic_stats[i].offset); +} + +static const struct ethtool_ops ibmvnic_ethtool_ops = { + .get_settings = ibmvnic_get_settings, + .get_drvinfo = ibmvnic_get_drvinfo, + .get_msglevel = ibmvnic_get_msglevel, + .set_msglevel = ibmvnic_set_msglevel, + .get_link = ibmvnic_get_link, + .get_ringparam = ibmvnic_get_ringparam, + .get_strings = ibmvnic_get_strings, + .get_sset_count = ibmvnic_get_sset_count, + .get_ethtool_stats = ibmvnic_get_ethtool_stats, +}; + +/* Routines for managing CRQs/sCRQs */ + +static void release_sub_crq_queue(struct ibmvnic_adapter *adapter, + struct ibmvnic_sub_crq_queue *scrq) +{ + struct device *dev = &adapter->vdev->dev; + long rc; + + netdev_dbg(adapter->netdev, "Releasing sub-CRQ\n"); + + /* Close the sub-crqs */ + do { + rc = plpar_hcall_norets(H_FREE_SUB_CRQ, + adapter->vdev->unit_address, + scrq->crq_num); + } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE, + DMA_BIDIRECTIONAL); + free_pages((unsigned long)scrq->msgs, 2); + kfree(scrq); +} + +static struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter + *adapter) +{ + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_sub_crq_queue *scrq; + int rc; + + scrq = kmalloc(sizeof(*scrq), GFP_ATOMIC); + if (!scrq) + return NULL; + + scrq->msgs = (union sub_crq *)__get_free_pages(GFP_KERNEL, 2); + memset(scrq->msgs, 0, 4 * PAGE_SIZE); + if (!scrq->msgs) { + dev_warn(dev, "Couldn't allocate crq queue messages page\n"); + goto zero_page_failed; + } + + scrq->msg_token = dma_map_single(dev, scrq->msgs, 4 * PAGE_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dev, scrq->msg_token)) { + dev_warn(dev, "Couldn't map crq queue messages page\n"); + goto map_failed; + } + + rc = h_reg_sub_crq(adapter->vdev->unit_address, scrq->msg_token, + 4 * PAGE_SIZE, &scrq->crq_num, &scrq->hw_irq); + + if (rc == H_RESOURCE) + rc = ibmvnic_reset_crq(adapter); + + if (rc == H_CLOSED) { + dev_warn(dev, "Partner adapter not ready, waiting.\n"); + } else if (rc) { + dev_warn(dev, "Error %d registering sub-crq\n", rc); + goto reg_failed; + } + + scrq->irq = irq_create_mapping(NULL, scrq->hw_irq); + if (scrq->irq == NO_IRQ) { + dev_err(dev, "Error mapping irq\n"); + goto map_irq_failed; + } + + scrq->adapter = adapter; + scrq->size = 4 * PAGE_SIZE / sizeof(*scrq->msgs); + scrq->cur = 0; + scrq->rx_skb_top = NULL; + spin_lock_init(&scrq->lock); + + netdev_dbg(adapter->netdev, + "sub-crq initialized, num %lx, hw_irq=%lx, irq=%x\n", + scrq->crq_num, scrq->hw_irq, scrq->irq); + + return scrq; + +map_irq_failed: + do { + rc = plpar_hcall_norets(H_FREE_SUB_CRQ, + adapter->vdev->unit_address, + scrq->crq_num); + } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); +reg_failed: + dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE, + DMA_BIDIRECTIONAL); +map_failed: + free_pages((unsigned long)scrq->msgs, 2); +zero_page_failed: + kfree(scrq); + + return NULL; +} + +static void release_sub_crqs(struct ibmvnic_adapter *adapter) +{ + int i; + + if (adapter->tx_scrq) { + for (i = 0; i < adapter->req_tx_queues; i++) + if (adapter->tx_scrq[i]) { + free_irq(adapter->tx_scrq[i]->irq, + adapter->tx_scrq[i]); + release_sub_crq_queue(adapter, + adapter->tx_scrq[i]); + } + adapter->tx_scrq = NULL; + } + + if (adapter->rx_scrq) { + for (i = 0; i < adapter->req_rx_queues; i++) + if (adapter->rx_scrq[i]) { + free_irq(adapter->rx_scrq[i]->irq, + adapter->rx_scrq[i]); + release_sub_crq_queue(adapter, + adapter->rx_scrq[i]); + } + adapter->rx_scrq = NULL; + } + + adapter->requested_caps = 0; +} + +static int disable_scrq_irq(struct ibmvnic_adapter *adapter, + struct ibmvnic_sub_crq_queue *scrq) +{ + struct device *dev = &adapter->vdev->dev; + unsigned long rc; + + rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address, + H_DISABLE_VIO_INTERRUPT, scrq->hw_irq, 0, 0); + if (rc) + dev_err(dev, "Couldn't disable scrq irq 0x%lx. rc=%ld\n", + scrq->hw_irq, rc); + return rc; +} + +static int enable_scrq_irq(struct ibmvnic_adapter *adapter, + struct ibmvnic_sub_crq_queue *scrq) +{ + struct device *dev = &adapter->vdev->dev; + unsigned long rc; + + if (scrq->hw_irq > 0x100000000ULL) { + dev_err(dev, "bad hw_irq = %lx\n", scrq->hw_irq); + return 1; + } + + rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address, + H_ENABLE_VIO_INTERRUPT, scrq->hw_irq, 0, 0); + if (rc) + dev_err(dev, "Couldn't enable scrq irq 0x%lx. rc=%ld\n", + scrq->hw_irq, rc); + return rc; +} + +static int ibmvnic_complete_tx(struct ibmvnic_adapter *adapter, + struct ibmvnic_sub_crq_queue *scrq) +{ + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_tx_buff *txbuff; + union sub_crq *next; + int index; + int i, j; + +restart_loop: + while (pending_scrq(adapter, scrq)) { + unsigned int pool = scrq->pool_index; + + next = ibmvnic_next_scrq(adapter, scrq); + for (i = 0; i < next->tx_comp.num_comps; i++) { + if (next->tx_comp.rcs[i]) { + dev_err(dev, "tx error %x\n", + next->tx_comp.rcs[i]); + continue; + } + index = be32_to_cpu(next->tx_comp.correlators[i]); + txbuff = &adapter->tx_pool[pool].tx_buff[index]; + + for (j = 0; j < IBMVNIC_MAX_FRAGS_PER_CRQ; j++) { + if (!txbuff->data_dma[j]) + continue; + + txbuff->data_dma[j] = 0; + txbuff->used_bounce = false; + } + + if (txbuff->last_frag) + dev_kfree_skb_any(txbuff->skb); + + adapter->tx_pool[pool].free_map[adapter->tx_pool[pool]. + producer_index] = index; + adapter->tx_pool[pool].producer_index = + (adapter->tx_pool[pool].producer_index + 1) % + adapter->max_tx_entries_per_subcrq; + } + /* remove tx_comp scrq*/ + next->tx_comp.first = 0; + } + + enable_scrq_irq(adapter, scrq); + + if (pending_scrq(adapter, scrq)) { + disable_scrq_irq(adapter, scrq); + goto restart_loop; + } + + return 0; +} + +static irqreturn_t ibmvnic_interrupt_tx(int irq, void *instance) +{ + struct ibmvnic_sub_crq_queue *scrq = instance; + struct ibmvnic_adapter *adapter = scrq->adapter; + + disable_scrq_irq(adapter, scrq); + ibmvnic_complete_tx(adapter, scrq); + + return IRQ_HANDLED; +} + +static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance) +{ + struct ibmvnic_sub_crq_queue *scrq = instance; + struct ibmvnic_adapter *adapter = scrq->adapter; + + if (napi_schedule_prep(&adapter->napi[scrq->scrq_num])) { + disable_scrq_irq(adapter, scrq); + __napi_schedule(&adapter->napi[scrq->scrq_num]); + } + + return IRQ_HANDLED; +} + +static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry) +{ + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_sub_crq_queue **allqueues; + int registered_queues = 0; + union ibmvnic_crq crq; + int total_queues; + int more = 0; + int i, j; + int rc; + + if (!retry) { + /* Sub-CRQ entries are 32 byte long */ + int entries_page = 4 * PAGE_SIZE / (sizeof(u64) * 4); + + if (adapter->min_tx_entries_per_subcrq > entries_page || + adapter->min_rx_add_entries_per_subcrq > entries_page) { + dev_err(dev, "Fatal, invalid entries per sub-crq\n"); + goto allqueues_failed; + } + + /* Get the minimum between the queried max and the entries + * that fit in our PAGE_SIZE + */ + adapter->req_tx_entries_per_subcrq = + adapter->max_tx_entries_per_subcrq > entries_page ? + entries_page : adapter->max_tx_entries_per_subcrq; + adapter->req_rx_add_entries_per_subcrq = + adapter->max_rx_add_entries_per_subcrq > entries_page ? + entries_page : adapter->max_rx_add_entries_per_subcrq; + + /* Choosing the maximum number of queues supported by firmware*/ + adapter->req_tx_queues = adapter->min_tx_queues; + adapter->req_rx_queues = adapter->min_rx_queues; + adapter->req_rx_add_queues = adapter->min_rx_add_queues; + + adapter->req_mtu = adapter->max_mtu; + } + + total_queues = adapter->req_tx_queues + adapter->req_rx_queues; + + allqueues = kcalloc(total_queues, sizeof(*allqueues), GFP_ATOMIC); + if (!allqueues) + goto allqueues_failed; + + for (i = 0; i < total_queues; i++) { + allqueues[i] = init_sub_crq_queue(adapter); + if (!allqueues[i]) { + dev_warn(dev, "Couldn't allocate all sub-crqs\n"); + break; + } + registered_queues++; + } + + /* Make sure we were able to register the minimum number of queues */ + if (registered_queues < + adapter->min_tx_queues + adapter->min_rx_queues) { + dev_err(dev, "Fatal: Couldn't init min number of sub-crqs\n"); + goto tx_failed; + } + + /* Distribute the failed allocated queues*/ + for (i = 0; i < total_queues - registered_queues + more ; i++) { + netdev_dbg(adapter->netdev, "Reducing number of queues\n"); + switch (i % 3) { + case 0: + if (adapter->req_rx_queues > adapter->min_rx_queues) + adapter->req_rx_queues--; + else + more++; + break; + case 1: + if (adapter->req_tx_queues > adapter->min_tx_queues) + adapter->req_tx_queues--; + else + more++; + break; + } + } + + adapter->tx_scrq = kcalloc(adapter->req_tx_queues, + sizeof(*adapter->tx_scrq), GFP_ATOMIC); + if (!adapter->tx_scrq) + goto tx_failed; + + for (i = 0; i < adapter->req_tx_queues; i++) { + adapter->tx_scrq[i] = allqueues[i]; + adapter->tx_scrq[i]->pool_index = i; + rc = request_irq(adapter->tx_scrq[i]->irq, ibmvnic_interrupt_tx, + 0, "ibmvnic_tx", adapter->tx_scrq[i]); + if (rc) { + dev_err(dev, "Couldn't register tx irq 0x%x. rc=%d\n", + adapter->tx_scrq[i]->irq, rc); + goto req_tx_irq_failed; + } + } + + adapter->rx_scrq = kcalloc(adapter->req_rx_queues, + sizeof(*adapter->rx_scrq), GFP_ATOMIC); + if (!adapter->rx_scrq) + goto rx_failed; + + for (i = 0; i < adapter->req_rx_queues; i++) { + adapter->rx_scrq[i] = allqueues[i + adapter->req_tx_queues]; + adapter->rx_scrq[i]->scrq_num = i; + rc = request_irq(adapter->rx_scrq[i]->irq, ibmvnic_interrupt_rx, + 0, "ibmvnic_rx", adapter->rx_scrq[i]); + if (rc) { + dev_err(dev, "Couldn't register rx irq 0x%x. rc=%d\n", + adapter->rx_scrq[i]->irq, rc); + goto req_rx_irq_failed; + } + } + + memset(&crq, 0, sizeof(crq)); + crq.request_capability.first = IBMVNIC_CRQ_CMD; + crq.request_capability.cmd = REQUEST_CAPABILITY; + + crq.request_capability.capability = cpu_to_be16(REQ_TX_QUEUES); + crq.request_capability.number = cpu_to_be32(adapter->req_tx_queues); + ibmvnic_send_crq(adapter, &crq); + + crq.request_capability.capability = cpu_to_be16(REQ_RX_QUEUES); + crq.request_capability.number = cpu_to_be32(adapter->req_rx_queues); + ibmvnic_send_crq(adapter, &crq); + + crq.request_capability.capability = cpu_to_be16(REQ_RX_ADD_QUEUES); + crq.request_capability.number = cpu_to_be32(adapter->req_rx_add_queues); + ibmvnic_send_crq(adapter, &crq); + + crq.request_capability.capability = + cpu_to_be16(REQ_TX_ENTRIES_PER_SUBCRQ); + crq.request_capability.number = + cpu_to_be32(adapter->req_tx_entries_per_subcrq); + ibmvnic_send_crq(adapter, &crq); + + crq.request_capability.capability = + cpu_to_be16(REQ_RX_ADD_ENTRIES_PER_SUBCRQ); + crq.request_capability.number = + cpu_to_be32(adapter->req_rx_add_entries_per_subcrq); + ibmvnic_send_crq(adapter, &crq); + + crq.request_capability.capability = cpu_to_be16(REQ_MTU); + crq.request_capability.number = cpu_to_be32(adapter->req_mtu); + ibmvnic_send_crq(adapter, &crq); + + if (adapter->netdev->flags & IFF_PROMISC) { + if (adapter->promisc_supported) { + crq.request_capability.capability = + cpu_to_be16(PROMISC_REQUESTED); + crq.request_capability.number = cpu_to_be32(1); + ibmvnic_send_crq(adapter, &crq); + } + } else { + crq.request_capability.capability = + cpu_to_be16(PROMISC_REQUESTED); + crq.request_capability.number = cpu_to_be32(0); + ibmvnic_send_crq(adapter, &crq); + } + + kfree(allqueues); + + return; + +req_rx_irq_failed: + for (j = 0; j < i; j++) + free_irq(adapter->rx_scrq[j]->irq, adapter->rx_scrq[j]); + i = adapter->req_tx_queues; +req_tx_irq_failed: + for (j = 0; j < i; j++) + free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]); + kfree(adapter->rx_scrq); + adapter->rx_scrq = NULL; +rx_failed: + kfree(adapter->tx_scrq); + adapter->tx_scrq = NULL; +tx_failed: + for (i = 0; i < registered_queues; i++) + release_sub_crq_queue(adapter, allqueues[i]); + kfree(allqueues); +allqueues_failed: + ibmvnic_remove(adapter->vdev); +} + +static int pending_scrq(struct ibmvnic_adapter *adapter, + struct ibmvnic_sub_crq_queue *scrq) +{ + union sub_crq *entry = &scrq->msgs[scrq->cur]; + + if (entry->generic.first & IBMVNIC_CRQ_CMD_RSP || adapter->closing) + return 1; + else + return 0; +} + +static union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *adapter, + struct ibmvnic_sub_crq_queue *scrq) +{ + union sub_crq *entry; + unsigned long flags; + + spin_lock_irqsave(&scrq->lock, flags); + entry = &scrq->msgs[scrq->cur]; + if (entry->generic.first & IBMVNIC_CRQ_CMD_RSP) { + if (++scrq->cur == scrq->size) + scrq->cur = 0; + } else { + entry = NULL; + } + spin_unlock_irqrestore(&scrq->lock, flags); + + return entry; +} + +static union ibmvnic_crq *ibmvnic_next_crq(struct ibmvnic_adapter *adapter) +{ + struct ibmvnic_crq_queue *queue = &adapter->crq; + union ibmvnic_crq *crq; + + crq = &queue->msgs[queue->cur]; + if (crq->generic.first & IBMVNIC_CRQ_CMD_RSP) { + if (++queue->cur == queue->size) + queue->cur = 0; + } else { + crq = NULL; + } + + return crq; +} + +static int send_subcrq(struct ibmvnic_adapter *adapter, u64 remote_handle, + union sub_crq *sub_crq) +{ + unsigned int ua = adapter->vdev->unit_address; + struct device *dev = &adapter->vdev->dev; + u64 *u64_crq = (u64 *)sub_crq; + int rc; + + netdev_dbg(adapter->netdev, + "Sending sCRQ %016lx: %016lx %016lx %016lx %016lx\n", + (unsigned long int)cpu_to_be64(remote_handle), + (unsigned long int)cpu_to_be64(u64_crq[0]), + (unsigned long int)cpu_to_be64(u64_crq[1]), + (unsigned long int)cpu_to_be64(u64_crq[2]), + (unsigned long int)cpu_to_be64(u64_crq[3])); + + /* Make sure the hypervisor sees the complete request */ + mb(); + + rc = plpar_hcall_norets(H_SEND_SUB_CRQ, ua, + cpu_to_be64(remote_handle), + cpu_to_be64(u64_crq[0]), + cpu_to_be64(u64_crq[1]), + cpu_to_be64(u64_crq[2]), + cpu_to_be64(u64_crq[3])); + + if (rc) { + if (rc == H_CLOSED) + dev_warn(dev, "CRQ Queue closed\n"); + dev_err(dev, "Send error (rc=%d)\n", rc); + } + + return rc; +} + +static int ibmvnic_send_crq(struct ibmvnic_adapter *adapter, + union ibmvnic_crq *crq) +{ + unsigned int ua = adapter->vdev->unit_address; + struct device *dev = &adapter->vdev->dev; + u64 *u64_crq = (u64 *)crq; + int rc; + + netdev_dbg(adapter->netdev, "Sending CRQ: %016lx %016lx\n", + (unsigned long int)cpu_to_be64(u64_crq[0]), + (unsigned long int)cpu_to_be64(u64_crq[1])); + + /* Make sure the hypervisor sees the complete request */ + mb(); + + rc = plpar_hcall_norets(H_SEND_CRQ, ua, + cpu_to_be64(u64_crq[0]), + cpu_to_be64(u64_crq[1])); + + if (rc) { + if (rc == H_CLOSED) + dev_warn(dev, "CRQ Queue closed\n"); + dev_warn(dev, "Send error (rc=%d)\n", rc); + } + + return rc; +} + +static int ibmvnic_send_crq_init(struct ibmvnic_adapter *adapter) +{ + union ibmvnic_crq crq; + + memset(&crq, 0, sizeof(crq)); + crq.generic.first = IBMVNIC_CRQ_INIT_CMD; + crq.generic.cmd = IBMVNIC_CRQ_INIT; + netdev_dbg(adapter->netdev, "Sending CRQ init\n"); + + return ibmvnic_send_crq(adapter, &crq); +} + +static int ibmvnic_send_crq_init_complete(struct ibmvnic_adapter *adapter) +{ + union ibmvnic_crq crq; + + memset(&crq, 0, sizeof(crq)); + crq.generic.first = IBMVNIC_CRQ_INIT_CMD; + crq.generic.cmd = IBMVNIC_CRQ_INIT_COMPLETE; + netdev_dbg(adapter->netdev, "Sending CRQ init complete\n"); + + return ibmvnic_send_crq(adapter, &crq); +} + +static int send_version_xchg(struct ibmvnic_adapter *adapter) +{ + union ibmvnic_crq crq; + + memset(&crq, 0, sizeof(crq)); + crq.version_exchange.first = IBMVNIC_CRQ_CMD; + crq.version_exchange.cmd = VERSION_EXCHANGE; + crq.version_exchange.version = cpu_to_be16(ibmvnic_version); + + return ibmvnic_send_crq(adapter, &crq); +} + +static void send_login(struct ibmvnic_adapter *adapter) +{ + struct ibmvnic_login_rsp_buffer *login_rsp_buffer; + struct ibmvnic_login_buffer *login_buffer; + struct ibmvnic_inflight_cmd *inflight_cmd; + struct device *dev = &adapter->vdev->dev; + dma_addr_t rsp_buffer_token; + dma_addr_t buffer_token; + size_t rsp_buffer_size; + union ibmvnic_crq crq; + unsigned long flags; + size_t buffer_size; + __be64 *tx_list_p; + __be64 *rx_list_p; + int i; + + buffer_size = + sizeof(struct ibmvnic_login_buffer) + + sizeof(u64) * (adapter->req_tx_queues + adapter->req_rx_queues); + + login_buffer = kmalloc(buffer_size, GFP_ATOMIC); + if (!login_buffer) + goto buf_alloc_failed; + + buffer_token = dma_map_single(dev, login_buffer, buffer_size, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, buffer_token)) { + dev_err(dev, "Couldn't map login buffer\n"); + goto buf_map_failed; + } + + rsp_buffer_size = + sizeof(struct ibmvnic_login_rsp_buffer) + + sizeof(u64) * (adapter->req_tx_queues + + adapter->req_rx_queues * + adapter->req_rx_add_queues + adapter-> + req_rx_add_queues) + + sizeof(u8) * (IBMVNIC_TX_DESC_VERSIONS); + + login_rsp_buffer = kmalloc(rsp_buffer_size, GFP_ATOMIC); + if (!login_rsp_buffer) + goto buf_rsp_alloc_failed; + + rsp_buffer_token = dma_map_single(dev, login_rsp_buffer, + rsp_buffer_size, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, rsp_buffer_token)) { + dev_err(dev, "Couldn't map login rsp buffer\n"); + goto buf_rsp_map_failed; + } + inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC); + if (!inflight_cmd) { + dev_err(dev, "Couldn't allocate inflight_cmd\n"); + goto inflight_alloc_failed; + } + adapter->login_buf = login_buffer; + adapter->login_buf_token = buffer_token; + adapter->login_buf_sz = buffer_size; + adapter->login_rsp_buf = login_rsp_buffer; + adapter->login_rsp_buf_token = rsp_buffer_token; + adapter->login_rsp_buf_sz = rsp_buffer_size; + + login_buffer->len = cpu_to_be32(buffer_size); + login_buffer->version = cpu_to_be32(INITIAL_VERSION_LB); + login_buffer->num_txcomp_subcrqs = cpu_to_be32(adapter->req_tx_queues); + login_buffer->off_txcomp_subcrqs = + cpu_to_be32(sizeof(struct ibmvnic_login_buffer)); + login_buffer->num_rxcomp_subcrqs = cpu_to_be32(adapter->req_rx_queues); + login_buffer->off_rxcomp_subcrqs = + cpu_to_be32(sizeof(struct ibmvnic_login_buffer) + + sizeof(u64) * adapter->req_tx_queues); + login_buffer->login_rsp_ioba = cpu_to_be32(rsp_buffer_token); + login_buffer->login_rsp_len = cpu_to_be32(rsp_buffer_size); + + tx_list_p = (__be64 *)((char *)login_buffer + + sizeof(struct ibmvnic_login_buffer)); + rx_list_p = (__be64 *)((char *)login_buffer + + sizeof(struct ibmvnic_login_buffer) + + sizeof(u64) * adapter->req_tx_queues); + + for (i = 0; i < adapter->req_tx_queues; i++) { + if (adapter->tx_scrq[i]) { + tx_list_p[i] = cpu_to_be64(adapter->tx_scrq[i]-> + crq_num); + } + } + + for (i = 0; i < adapter->req_rx_queues; i++) { + if (adapter->rx_scrq[i]) { + rx_list_p[i] = cpu_to_be64(adapter->rx_scrq[i]-> + crq_num); + } + } + + netdev_dbg(adapter->netdev, "Login Buffer:\n"); + for (i = 0; i < (adapter->login_buf_sz - 1) / 8 + 1; i++) { + netdev_dbg(adapter->netdev, "%016lx\n", + ((unsigned long int *)(adapter->login_buf))[i]); + } + + memset(&crq, 0, sizeof(crq)); + crq.login.first = IBMVNIC_CRQ_CMD; + crq.login.cmd = LOGIN; + crq.login.ioba = cpu_to_be32(buffer_token); + crq.login.len = cpu_to_be32(buffer_size); + + memcpy(&inflight_cmd->crq, &crq, sizeof(crq)); + + spin_lock_irqsave(&adapter->inflight_lock, flags); + list_add_tail(&inflight_cmd->list, &adapter->inflight); + spin_unlock_irqrestore(&adapter->inflight_lock, flags); + + ibmvnic_send_crq(adapter, &crq); + + return; + +inflight_alloc_failed: + dma_unmap_single(dev, rsp_buffer_token, rsp_buffer_size, + DMA_FROM_DEVICE); +buf_rsp_map_failed: + kfree(login_rsp_buffer); +buf_rsp_alloc_failed: + dma_unmap_single(dev, buffer_token, buffer_size, DMA_TO_DEVICE); +buf_map_failed: + kfree(login_buffer); +buf_alloc_failed: + return; +} + +static void send_request_map(struct ibmvnic_adapter *adapter, dma_addr_t addr, + u32 len, u8 map_id) +{ + union ibmvnic_crq crq; + + memset(&crq, 0, sizeof(crq)); + crq.request_map.first = IBMVNIC_CRQ_CMD; + crq.request_map.cmd = REQUEST_MAP; + crq.request_map.map_id = map_id; + crq.request_map.ioba = cpu_to_be32(addr); + crq.request_map.len = cpu_to_be32(len); + ibmvnic_send_crq(adapter, &crq); +} + +static void send_request_unmap(struct ibmvnic_adapter *adapter, u8 map_id) +{ + union ibmvnic_crq crq; + + memset(&crq, 0, sizeof(crq)); + crq.request_unmap.first = IBMVNIC_CRQ_CMD; + crq.request_unmap.cmd = REQUEST_UNMAP; + crq.request_unmap.map_id = map_id; + ibmvnic_send_crq(adapter, &crq); +} + +static void send_map_query(struct ibmvnic_adapter *adapter) +{ + union ibmvnic_crq crq; + + memset(&crq, 0, sizeof(crq)); + crq.query_map.first = IBMVNIC_CRQ_CMD; + crq.query_map.cmd = QUERY_MAP; + ibmvnic_send_crq(adapter, &crq); +} + +/* Send a series of CRQs requesting various capabilities of the VNIC server */ +static void send_cap_queries(struct ibmvnic_adapter *adapter) +{ + union ibmvnic_crq crq; + + atomic_set(&adapter->running_cap_queries, 0); + memset(&crq, 0, sizeof(crq)); + crq.query_capability.first = IBMVNIC_CRQ_CMD; + crq.query_capability.cmd = QUERY_CAPABILITY; + + crq.query_capability.capability = cpu_to_be16(MIN_TX_QUEUES); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(MIN_RX_QUEUES); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(MIN_RX_ADD_QUEUES); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(MAX_TX_QUEUES); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(MAX_RX_QUEUES); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(MAX_RX_ADD_QUEUES); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = + cpu_to_be16(MIN_TX_ENTRIES_PER_SUBCRQ); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = + cpu_to_be16(MIN_RX_ADD_ENTRIES_PER_SUBCRQ); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = + cpu_to_be16(MAX_TX_ENTRIES_PER_SUBCRQ); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = + cpu_to_be16(MAX_RX_ADD_ENTRIES_PER_SUBCRQ); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(TCP_IP_OFFLOAD); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(PROMISC_SUPPORTED); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(MIN_MTU); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(MAX_MTU); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(MAX_MULTICAST_FILTERS); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(VLAN_HEADER_INSERTION); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(MAX_TX_SG_ENTRIES); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(RX_SG_SUPPORTED); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(OPT_TX_COMP_SUB_QUEUES); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(OPT_RX_COMP_QUEUES); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = + cpu_to_be16(OPT_RX_BUFADD_Q_PER_RX_COMP_Q); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = + cpu_to_be16(OPT_TX_ENTRIES_PER_SUBCRQ); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = + cpu_to_be16(OPT_RXBA_ENTRIES_PER_SUBCRQ); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); + + crq.query_capability.capability = cpu_to_be16(TX_RX_DESC_REQ); + atomic_inc(&adapter->running_cap_queries); + ibmvnic_send_crq(adapter, &crq); +} + +static void handle_query_ip_offload_rsp(struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_query_ip_offload_buffer *buf = &adapter->ip_offload_buf; + union ibmvnic_crq crq; + int i; + + dma_unmap_single(dev, adapter->ip_offload_tok, + sizeof(adapter->ip_offload_buf), DMA_FROM_DEVICE); + + netdev_dbg(adapter->netdev, "Query IP Offload Buffer:\n"); + for (i = 0; i < (sizeof(adapter->ip_offload_buf) - 1) / 8 + 1; i++) + netdev_dbg(adapter->netdev, "%016lx\n", + ((unsigned long int *)(buf))[i]); + + netdev_dbg(adapter->netdev, "ipv4_chksum = %d\n", buf->ipv4_chksum); + netdev_dbg(adapter->netdev, "ipv6_chksum = %d\n", buf->ipv6_chksum); + netdev_dbg(adapter->netdev, "tcp_ipv4_chksum = %d\n", + buf->tcp_ipv4_chksum); + netdev_dbg(adapter->netdev, "tcp_ipv6_chksum = %d\n", + buf->tcp_ipv6_chksum); + netdev_dbg(adapter->netdev, "udp_ipv4_chksum = %d\n", + buf->udp_ipv4_chksum); + netdev_dbg(adapter->netdev, "udp_ipv6_chksum = %d\n", + buf->udp_ipv6_chksum); + netdev_dbg(adapter->netdev, "large_tx_ipv4 = %d\n", + buf->large_tx_ipv4); + netdev_dbg(adapter->netdev, "large_tx_ipv6 = %d\n", + buf->large_tx_ipv6); + netdev_dbg(adapter->netdev, "large_rx_ipv4 = %d\n", + buf->large_rx_ipv4); + netdev_dbg(adapter->netdev, "large_rx_ipv6 = %d\n", + buf->large_rx_ipv6); + netdev_dbg(adapter->netdev, "max_ipv4_hdr_sz = %d\n", + buf->max_ipv4_header_size); + netdev_dbg(adapter->netdev, "max_ipv6_hdr_sz = %d\n", + buf->max_ipv6_header_size); + netdev_dbg(adapter->netdev, "max_tcp_hdr_size = %d\n", + buf->max_tcp_header_size); + netdev_dbg(adapter->netdev, "max_udp_hdr_size = %d\n", + buf->max_udp_header_size); + netdev_dbg(adapter->netdev, "max_large_tx_size = %d\n", + buf->max_large_tx_size); + netdev_dbg(adapter->netdev, "max_large_rx_size = %d\n", + buf->max_large_rx_size); + netdev_dbg(adapter->netdev, "ipv6_ext_hdr = %d\n", + buf->ipv6_extension_header); + netdev_dbg(adapter->netdev, "tcp_pseudosum_req = %d\n", + buf->tcp_pseudosum_req); + netdev_dbg(adapter->netdev, "num_ipv6_ext_hd = %d\n", + buf->num_ipv6_ext_headers); + netdev_dbg(adapter->netdev, "off_ipv6_ext_hd = %d\n", + buf->off_ipv6_ext_headers); + + adapter->ip_offload_ctrl_tok = + dma_map_single(dev, &adapter->ip_offload_ctrl, + sizeof(adapter->ip_offload_ctrl), DMA_TO_DEVICE); + + if (dma_mapping_error(dev, adapter->ip_offload_ctrl_tok)) { + dev_err(dev, "Couldn't map ip offload control buffer\n"); + return; + } + + adapter->ip_offload_ctrl.version = cpu_to_be32(INITIAL_VERSION_IOB); + adapter->ip_offload_ctrl.tcp_ipv4_chksum = buf->tcp_ipv4_chksum; + adapter->ip_offload_ctrl.udp_ipv4_chksum = buf->udp_ipv4_chksum; + adapter->ip_offload_ctrl.tcp_ipv6_chksum = buf->tcp_ipv6_chksum; + adapter->ip_offload_ctrl.udp_ipv6_chksum = buf->udp_ipv6_chksum; + + /* large_tx/rx disabled for now, additional features needed */ + adapter->ip_offload_ctrl.large_tx_ipv4 = 0; + adapter->ip_offload_ctrl.large_tx_ipv6 = 0; + adapter->ip_offload_ctrl.large_rx_ipv4 = 0; + adapter->ip_offload_ctrl.large_rx_ipv6 = 0; + + adapter->netdev->features = NETIF_F_GSO; + + if (buf->tcp_ipv4_chksum || buf->udp_ipv4_chksum) + adapter->netdev->features |= NETIF_F_IP_CSUM; + + if (buf->tcp_ipv6_chksum || buf->udp_ipv6_chksum) + adapter->netdev->features |= NETIF_F_IPV6_CSUM; + + memset(&crq, 0, sizeof(crq)); + crq.control_ip_offload.first = IBMVNIC_CRQ_CMD; + crq.control_ip_offload.cmd = CONTROL_IP_OFFLOAD; + crq.control_ip_offload.len = + cpu_to_be32(sizeof(adapter->ip_offload_ctrl)); + crq.control_ip_offload.ioba = cpu_to_be32(adapter->ip_offload_ctrl_tok); + ibmvnic_send_crq(adapter, &crq); +} + +static void handle_error_info_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_error_buff *error_buff; + unsigned long flags; + bool found = false; + int i; + + if (!crq->request_error_rsp.rc.code) { + dev_info(dev, "Request Error Rsp returned with rc=%x\n", + crq->request_error_rsp.rc.code); + return; + } + + spin_lock_irqsave(&adapter->error_list_lock, flags); + list_for_each_entry(error_buff, &adapter->errors, list) + if (error_buff->error_id == crq->request_error_rsp.error_id) { + found = true; + list_del(&error_buff->list); + break; + } + spin_unlock_irqrestore(&adapter->error_list_lock, flags); + + if (!found) { + dev_err(dev, "Couldn't find error id %x\n", + crq->request_error_rsp.error_id); + return; + } + + dev_err(dev, "Detailed info for error id %x:", + crq->request_error_rsp.error_id); + + for (i = 0; i < error_buff->len; i++) { + pr_cont("%02x", (int)error_buff->buff[i]); + if (i % 8 == 7) + pr_cont(" "); + } + pr_cont("\n"); + + dma_unmap_single(dev, error_buff->dma, error_buff->len, + DMA_FROM_DEVICE); + kfree(error_buff->buff); + kfree(error_buff); +} + +static void handle_dump_size_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + int len = be32_to_cpu(crq->request_dump_size_rsp.len); + struct ibmvnic_inflight_cmd *inflight_cmd; + struct device *dev = &adapter->vdev->dev; + union ibmvnic_crq newcrq; + unsigned long flags; + + /* allocate and map buffer */ + adapter->dump_data = kmalloc(len, GFP_KERNEL); + if (!adapter->dump_data) { + complete(&adapter->fw_done); + return; + } + + adapter->dump_data_token = dma_map_single(dev, adapter->dump_data, len, + DMA_FROM_DEVICE); + + if (dma_mapping_error(dev, adapter->dump_data_token)) { + if (!firmware_has_feature(FW_FEATURE_CMO)) + dev_err(dev, "Couldn't map dump data\n"); + kfree(adapter->dump_data); + complete(&adapter->fw_done); + return; + } + + inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC); + if (!inflight_cmd) { + dma_unmap_single(dev, adapter->dump_data_token, len, + DMA_FROM_DEVICE); + kfree(adapter->dump_data); + complete(&adapter->fw_done); + return; + } + + memset(&newcrq, 0, sizeof(newcrq)); + newcrq.request_dump.first = IBMVNIC_CRQ_CMD; + newcrq.request_dump.cmd = REQUEST_DUMP; + newcrq.request_dump.ioba = cpu_to_be32(adapter->dump_data_token); + newcrq.request_dump.len = cpu_to_be32(adapter->dump_data_size); + + memcpy(&inflight_cmd->crq, &newcrq, sizeof(newcrq)); + + spin_lock_irqsave(&adapter->inflight_lock, flags); + list_add_tail(&inflight_cmd->list, &adapter->inflight); + spin_unlock_irqrestore(&adapter->inflight_lock, flags); + + ibmvnic_send_crq(adapter, &newcrq); +} + +static void handle_error_indication(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + int detail_len = be32_to_cpu(crq->error_indication.detail_error_sz); + struct ibmvnic_inflight_cmd *inflight_cmd; + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_error_buff *error_buff; + union ibmvnic_crq new_crq; + unsigned long flags; + + dev_err(dev, "Firmware reports %serror id %x, cause %d\n", + crq->error_indication. + flags & IBMVNIC_FATAL_ERROR ? "FATAL " : "", + crq->error_indication.error_id, + crq->error_indication.error_cause); + + error_buff = kmalloc(sizeof(*error_buff), GFP_ATOMIC); + if (!error_buff) + return; + + error_buff->buff = kmalloc(detail_len, GFP_ATOMIC); + if (!error_buff->buff) { + kfree(error_buff); + return; + } + + error_buff->dma = dma_map_single(dev, error_buff->buff, detail_len, + DMA_FROM_DEVICE); + if (dma_mapping_error(dev, error_buff->dma)) { + if (!firmware_has_feature(FW_FEATURE_CMO)) + dev_err(dev, "Couldn't map error buffer\n"); + kfree(error_buff->buff); + kfree(error_buff); + return; + } + + inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC); + if (!inflight_cmd) { + dma_unmap_single(dev, error_buff->dma, detail_len, + DMA_FROM_DEVICE); + kfree(error_buff->buff); + kfree(error_buff); + return; + } + + error_buff->len = detail_len; + error_buff->error_id = crq->error_indication.error_id; + + spin_lock_irqsave(&adapter->error_list_lock, flags); + list_add_tail(&error_buff->list, &adapter->errors); + spin_unlock_irqrestore(&adapter->error_list_lock, flags); + + memset(&new_crq, 0, sizeof(new_crq)); + new_crq.request_error_info.first = IBMVNIC_CRQ_CMD; + new_crq.request_error_info.cmd = REQUEST_ERROR_INFO; + new_crq.request_error_info.ioba = cpu_to_be32(error_buff->dma); + new_crq.request_error_info.len = cpu_to_be32(detail_len); + new_crq.request_error_info.error_id = crq->error_indication.error_id; + + memcpy(&inflight_cmd->crq, &crq, sizeof(crq)); + + spin_lock_irqsave(&adapter->inflight_lock, flags); + list_add_tail(&inflight_cmd->list, &adapter->inflight); + spin_unlock_irqrestore(&adapter->inflight_lock, flags); + + ibmvnic_send_crq(adapter, &new_crq); +} + +static void handle_change_mac_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct device *dev = &adapter->vdev->dev; + long rc; + + rc = crq->change_mac_addr_rsp.rc.code; + if (rc) { + dev_err(dev, "Error %ld in CHANGE_MAC_ADDR_RSP\n", rc); + return; + } + memcpy(netdev->dev_addr, &crq->change_mac_addr_rsp.mac_addr[0], + ETH_ALEN); +} + +static void handle_request_cap_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + u64 *req_value; + char *name; + + switch (be16_to_cpu(crq->request_capability_rsp.capability)) { + case REQ_TX_QUEUES: + req_value = &adapter->req_tx_queues; + name = "tx"; + break; + case REQ_RX_QUEUES: + req_value = &adapter->req_rx_queues; + name = "rx"; + break; + case REQ_RX_ADD_QUEUES: + req_value = &adapter->req_rx_add_queues; + name = "rx_add"; + break; + case REQ_TX_ENTRIES_PER_SUBCRQ: + req_value = &adapter->req_tx_entries_per_subcrq; + name = "tx_entries_per_subcrq"; + break; + case REQ_RX_ADD_ENTRIES_PER_SUBCRQ: + req_value = &adapter->req_rx_add_entries_per_subcrq; + name = "rx_add_entries_per_subcrq"; + break; + case REQ_MTU: + req_value = &adapter->req_mtu; + name = "mtu"; + break; + case PROMISC_REQUESTED: + req_value = &adapter->promisc; + name = "promisc"; + break; + default: + dev_err(dev, "Got invalid cap request rsp %d\n", + crq->request_capability.capability); + return; + } + + switch (crq->request_capability_rsp.rc.code) { + case SUCCESS: + break; + case PARTIALSUCCESS: + dev_info(dev, "req=%lld, rsp=%ld in %s queue, retrying.\n", + *req_value, + (long int)be32_to_cpu(crq->request_capability_rsp. + number), name); + release_sub_crqs(adapter); + *req_value = be32_to_cpu(crq->request_capability_rsp.number); + complete(&adapter->init_done); + return; + default: + dev_err(dev, "Error %d in request cap rsp\n", + crq->request_capability_rsp.rc.code); + return; + } + + /* Done receiving requested capabilities, query IP offload support */ + if (++adapter->requested_caps == 7) { + union ibmvnic_crq newcrq; + int buf_sz = sizeof(struct ibmvnic_query_ip_offload_buffer); + struct ibmvnic_query_ip_offload_buffer *ip_offload_buf = + &adapter->ip_offload_buf; + + adapter->ip_offload_tok = dma_map_single(dev, ip_offload_buf, + buf_sz, + DMA_FROM_DEVICE); + + if (dma_mapping_error(dev, adapter->ip_offload_tok)) { + if (!firmware_has_feature(FW_FEATURE_CMO)) + dev_err(dev, "Couldn't map offload buffer\n"); + return; + } + + memset(&newcrq, 0, sizeof(newcrq)); + newcrq.query_ip_offload.first = IBMVNIC_CRQ_CMD; + newcrq.query_ip_offload.cmd = QUERY_IP_OFFLOAD; + newcrq.query_ip_offload.len = cpu_to_be32(buf_sz); + newcrq.query_ip_offload.ioba = + cpu_to_be32(adapter->ip_offload_tok); + + ibmvnic_send_crq(adapter, &newcrq); + } +} + +static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq, + struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_login_rsp_buffer *login_rsp = adapter->login_rsp_buf; + struct ibmvnic_login_buffer *login = adapter->login_buf; + union ibmvnic_crq crq; + int i; + + dma_unmap_single(dev, adapter->login_buf_token, adapter->login_buf_sz, + DMA_BIDIRECTIONAL); + dma_unmap_single(dev, adapter->login_rsp_buf_token, + adapter->login_rsp_buf_sz, DMA_BIDIRECTIONAL); + + netdev_dbg(adapter->netdev, "Login Response Buffer:\n"); + for (i = 0; i < (adapter->login_rsp_buf_sz - 1) / 8 + 1; i++) { + netdev_dbg(adapter->netdev, "%016lx\n", + ((unsigned long int *)(adapter->login_rsp_buf))[i]); + } + + /* Sanity checks */ + if (login->num_txcomp_subcrqs != login_rsp->num_txsubm_subcrqs || + (be32_to_cpu(login->num_rxcomp_subcrqs) * + adapter->req_rx_add_queues != + be32_to_cpu(login_rsp->num_rxadd_subcrqs))) { + dev_err(dev, "FATAL: Inconsistent login and login rsp\n"); + ibmvnic_remove(adapter->vdev); + return -EIO; + } + complete(&adapter->init_done); + + memset(&crq, 0, sizeof(crq)); + crq.request_ras_comp_num.first = IBMVNIC_CRQ_CMD; + crq.request_ras_comp_num.cmd = REQUEST_RAS_COMP_NUM; + ibmvnic_send_crq(adapter, &crq); + + return 0; +} + +static void handle_request_map_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + u8 map_id = crq->request_map_rsp.map_id; + int tx_subcrqs; + int rx_subcrqs; + long rc; + int i; + + tx_subcrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); + rx_subcrqs = be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); + + rc = crq->request_map_rsp.rc.code; + if (rc) { + dev_err(dev, "Error %ld in REQUEST_MAP_RSP\n", rc); + adapter->map_id--; + /* need to find and zero tx/rx_pool map_id */ + for (i = 0; i < tx_subcrqs; i++) { + if (adapter->tx_pool[i].long_term_buff.map_id == map_id) + adapter->tx_pool[i].long_term_buff.map_id = 0; + } + for (i = 0; i < rx_subcrqs; i++) { + if (adapter->rx_pool[i].long_term_buff.map_id == map_id) + adapter->rx_pool[i].long_term_buff.map_id = 0; + } + } + complete(&adapter->fw_done); +} + +static void handle_request_unmap_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + long rc; + + rc = crq->request_unmap_rsp.rc.code; + if (rc) + dev_err(dev, "Error %ld in REQUEST_UNMAP_RSP\n", rc); +} + +static void handle_query_map_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct device *dev = &adapter->vdev->dev; + long rc; + + rc = crq->query_map_rsp.rc.code; + if (rc) { + dev_err(dev, "Error %ld in QUERY_MAP_RSP\n", rc); + return; + } + netdev_dbg(netdev, "page_size = %d\ntot_pages = %d\nfree_pages = %d\n", + crq->query_map_rsp.page_size, crq->query_map_rsp.tot_pages, + crq->query_map_rsp.free_pages); +} + +static void handle_query_cap_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct device *dev = &adapter->vdev->dev; + long rc; + + atomic_dec(&adapter->running_cap_queries); + netdev_dbg(netdev, "Outstanding queries: %d\n", + atomic_read(&adapter->running_cap_queries)); + rc = crq->query_capability.rc.code; + if (rc) { + dev_err(dev, "Error %ld in QUERY_CAP_RSP\n", rc); + goto out; + } + + switch (be16_to_cpu(crq->query_capability.capability)) { + case MIN_TX_QUEUES: + adapter->min_tx_queues = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "min_tx_queues = %lld\n", + adapter->min_tx_queues); + break; + case MIN_RX_QUEUES: + adapter->min_rx_queues = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "min_rx_queues = %lld\n", + adapter->min_rx_queues); + break; + case MIN_RX_ADD_QUEUES: + adapter->min_rx_add_queues = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "min_rx_add_queues = %lld\n", + adapter->min_rx_add_queues); + break; + case MAX_TX_QUEUES: + adapter->max_tx_queues = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "max_tx_queues = %lld\n", + adapter->max_tx_queues); + break; + case MAX_RX_QUEUES: + adapter->max_rx_queues = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "max_rx_queues = %lld\n", + adapter->max_rx_queues); + break; + case MAX_RX_ADD_QUEUES: + adapter->max_rx_add_queues = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "max_rx_add_queues = %lld\n", + adapter->max_rx_add_queues); + break; + case MIN_TX_ENTRIES_PER_SUBCRQ: + adapter->min_tx_entries_per_subcrq = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "min_tx_entries_per_subcrq = %lld\n", + adapter->min_tx_entries_per_subcrq); + break; + case MIN_RX_ADD_ENTRIES_PER_SUBCRQ: + adapter->min_rx_add_entries_per_subcrq = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "min_rx_add_entrs_per_subcrq = %lld\n", + adapter->min_rx_add_entries_per_subcrq); + break; + case MAX_TX_ENTRIES_PER_SUBCRQ: + adapter->max_tx_entries_per_subcrq = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "max_tx_entries_per_subcrq = %lld\n", + adapter->max_tx_entries_per_subcrq); + break; + case MAX_RX_ADD_ENTRIES_PER_SUBCRQ: + adapter->max_rx_add_entries_per_subcrq = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "max_rx_add_entrs_per_subcrq = %lld\n", + adapter->max_rx_add_entries_per_subcrq); + break; + case TCP_IP_OFFLOAD: + adapter->tcp_ip_offload = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "tcp_ip_offload = %lld\n", + adapter->tcp_ip_offload); + break; + case PROMISC_SUPPORTED: + adapter->promisc_supported = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "promisc_supported = %lld\n", + adapter->promisc_supported); + break; + case MIN_MTU: + adapter->min_mtu = be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "min_mtu = %lld\n", adapter->min_mtu); + break; + case MAX_MTU: + adapter->max_mtu = be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "max_mtu = %lld\n", adapter->max_mtu); + break; + case MAX_MULTICAST_FILTERS: + adapter->max_multicast_filters = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "max_multicast_filters = %lld\n", + adapter->max_multicast_filters); + break; + case VLAN_HEADER_INSERTION: + adapter->vlan_header_insertion = + be32_to_cpu(crq->query_capability.number); + if (adapter->vlan_header_insertion) + netdev->features |= NETIF_F_HW_VLAN_STAG_TX; + netdev_dbg(netdev, "vlan_header_insertion = %lld\n", + adapter->vlan_header_insertion); + break; + case MAX_TX_SG_ENTRIES: + adapter->max_tx_sg_entries = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "max_tx_sg_entries = %lld\n", + adapter->max_tx_sg_entries); + break; + case RX_SG_SUPPORTED: + adapter->rx_sg_supported = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "rx_sg_supported = %lld\n", + adapter->rx_sg_supported); + break; + case OPT_TX_COMP_SUB_QUEUES: + adapter->opt_tx_comp_sub_queues = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "opt_tx_comp_sub_queues = %lld\n", + adapter->opt_tx_comp_sub_queues); + break; + case OPT_RX_COMP_QUEUES: + adapter->opt_rx_comp_queues = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "opt_rx_comp_queues = %lld\n", + adapter->opt_rx_comp_queues); + break; + case OPT_RX_BUFADD_Q_PER_RX_COMP_Q: + adapter->opt_rx_bufadd_q_per_rx_comp_q = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "opt_rx_bufadd_q_per_rx_comp_q = %lld\n", + adapter->opt_rx_bufadd_q_per_rx_comp_q); + break; + case OPT_TX_ENTRIES_PER_SUBCRQ: + adapter->opt_tx_entries_per_subcrq = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "opt_tx_entries_per_subcrq = %lld\n", + adapter->opt_tx_entries_per_subcrq); + break; + case OPT_RXBA_ENTRIES_PER_SUBCRQ: + adapter->opt_rxba_entries_per_subcrq = + be32_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "opt_rxba_entries_per_subcrq = %lld\n", + adapter->opt_rxba_entries_per_subcrq); + break; + case TX_RX_DESC_REQ: + adapter->tx_rx_desc_req = crq->query_capability.number; + netdev_dbg(netdev, "tx_rx_desc_req = %llx\n", + adapter->tx_rx_desc_req); + break; + + default: + netdev_err(netdev, "Got invalid cap rsp %d\n", + crq->query_capability.capability); + } + +out: + if (atomic_read(&adapter->running_cap_queries) == 0) + complete(&adapter->init_done); + /* We're done querying the capabilities, initialize sub-crqs */ +} + +static void handle_control_ras_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + u8 correlator = crq->control_ras_rsp.correlator; + struct device *dev = &adapter->vdev->dev; + bool found = false; + int i; + + if (crq->control_ras_rsp.rc.code) { + dev_warn(dev, "Control ras failed rc=%d\n", + crq->control_ras_rsp.rc.code); + return; + } + + for (i = 0; i < adapter->ras_comp_num; i++) { + if (adapter->ras_comps[i].correlator == correlator) { + found = true; + break; + } + } + + if (!found) { + dev_warn(dev, "Correlator not found on control_ras_rsp\n"); + return; + } + + switch (crq->control_ras_rsp.op) { + case IBMVNIC_TRACE_LEVEL: + adapter->ras_comps[i].trace_level = crq->control_ras.level; + break; + case IBMVNIC_ERROR_LEVEL: + adapter->ras_comps[i].error_check_level = + crq->control_ras.level; + break; + case IBMVNIC_TRACE_PAUSE: + adapter->ras_comp_int[i].paused = 1; + break; + case IBMVNIC_TRACE_RESUME: + adapter->ras_comp_int[i].paused = 0; + break; + case IBMVNIC_TRACE_ON: + adapter->ras_comps[i].trace_on = 1; + break; + case IBMVNIC_TRACE_OFF: + adapter->ras_comps[i].trace_on = 0; + break; + case IBMVNIC_CHG_TRACE_BUFF_SZ: + /* trace_buff_sz is 3 bytes, stuff it into an int */ + ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[0] = 0; + ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[1] = + crq->control_ras_rsp.trace_buff_sz[0]; + ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[2] = + crq->control_ras_rsp.trace_buff_sz[1]; + ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[3] = + crq->control_ras_rsp.trace_buff_sz[2]; + break; + default: + dev_err(dev, "invalid op %d on control_ras_rsp", + crq->control_ras_rsp.op); + } +} + +static int ibmvnic_fw_comp_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t trace_read(struct file *file, char __user *user_buf, size_t len, + loff_t *ppos) +{ + struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; + struct ibmvnic_adapter *adapter = ras_comp_int->adapter; + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_fw_trace_entry *trace; + int num = ras_comp_int->num; + union ibmvnic_crq crq; + dma_addr_t trace_tok; + + if (*ppos >= be32_to_cpu(adapter->ras_comps[num].trace_buff_size)) + return 0; + + trace = + dma_alloc_coherent(dev, + be32_to_cpu(adapter->ras_comps[num]. + trace_buff_size), &trace_tok, + GFP_KERNEL); + if (!trace) { + dev_err(dev, "Couldn't alloc trace buffer\n"); + return 0; + } + + memset(&crq, 0, sizeof(crq)); + crq.collect_fw_trace.first = IBMVNIC_CRQ_CMD; + crq.collect_fw_trace.cmd = COLLECT_FW_TRACE; + crq.collect_fw_trace.correlator = adapter->ras_comps[num].correlator; + crq.collect_fw_trace.ioba = cpu_to_be32(trace_tok); + crq.collect_fw_trace.len = adapter->ras_comps[num].trace_buff_size; + ibmvnic_send_crq(adapter, &crq); + + init_completion(&adapter->fw_done); + wait_for_completion(&adapter->fw_done); + + if (*ppos + len > be32_to_cpu(adapter->ras_comps[num].trace_buff_size)) + len = + be32_to_cpu(adapter->ras_comps[num].trace_buff_size) - + *ppos; + + copy_to_user(user_buf, &((u8 *)trace)[*ppos], len); + + dma_free_coherent(dev, + be32_to_cpu(adapter->ras_comps[num].trace_buff_size), + trace, trace_tok); + *ppos += len; + return len; +} + +static const struct file_operations trace_ops = { + .owner = THIS_MODULE, + .open = ibmvnic_fw_comp_open, + .read = trace_read, +}; + +static ssize_t paused_read(struct file *file, char __user *user_buf, size_t len, + loff_t *ppos) +{ + struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; + struct ibmvnic_adapter *adapter = ras_comp_int->adapter; + int num = ras_comp_int->num; + char buff[5]; /* 1 or 0 plus \n and \0 */ + int size; + + size = sprintf(buff, "%d\n", adapter->ras_comp_int[num].paused); + + if (*ppos >= size) + return 0; + + copy_to_user(user_buf, buff, size); + *ppos += size; + return size; +} + +static ssize_t paused_write(struct file *file, const char __user *user_buf, + size_t len, loff_t *ppos) +{ + struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; + struct ibmvnic_adapter *adapter = ras_comp_int->adapter; + int num = ras_comp_int->num; + union ibmvnic_crq crq; + unsigned long val; + char buff[9]; /* decimal max int plus \n and \0 */ + + copy_from_user(buff, user_buf, sizeof(buff)); + val = kstrtoul(buff, 10, NULL); + + adapter->ras_comp_int[num].paused = val ? 1 : 0; + + memset(&crq, 0, sizeof(crq)); + crq.control_ras.first = IBMVNIC_CRQ_CMD; + crq.control_ras.cmd = CONTROL_RAS; + crq.control_ras.correlator = adapter->ras_comps[num].correlator; + crq.control_ras.op = val ? IBMVNIC_TRACE_PAUSE : IBMVNIC_TRACE_RESUME; + ibmvnic_send_crq(adapter, &crq); + + return len; +} + +static const struct file_operations paused_ops = { + .owner = THIS_MODULE, + .open = ibmvnic_fw_comp_open, + .read = paused_read, + .write = paused_write, +}; + +static ssize_t tracing_read(struct file *file, char __user *user_buf, + size_t len, loff_t *ppos) +{ + struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; + struct ibmvnic_adapter *adapter = ras_comp_int->adapter; + int num = ras_comp_int->num; + char buff[5]; /* 1 or 0 plus \n and \0 */ + int size; + + size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_on); + + if (*ppos >= size) + return 0; + + copy_to_user(user_buf, buff, size); + *ppos += size; + return size; +} + +static ssize_t tracing_write(struct file *file, const char __user *user_buf, + size_t len, loff_t *ppos) +{ + struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; + struct ibmvnic_adapter *adapter = ras_comp_int->adapter; + int num = ras_comp_int->num; + union ibmvnic_crq crq; + unsigned long val; + char buff[9]; /* decimal max int plus \n and \0 */ + + copy_from_user(buff, user_buf, sizeof(buff)); + val = kstrtoul(buff, 10, NULL); + + memset(&crq, 0, sizeof(crq)); + crq.control_ras.first = IBMVNIC_CRQ_CMD; + crq.control_ras.cmd = CONTROL_RAS; + crq.control_ras.correlator = adapter->ras_comps[num].correlator; + crq.control_ras.op = val ? IBMVNIC_TRACE_ON : IBMVNIC_TRACE_OFF; + + return len; +} + +static const struct file_operations tracing_ops = { + .owner = THIS_MODULE, + .open = ibmvnic_fw_comp_open, + .read = tracing_read, + .write = tracing_write, +}; + +static ssize_t error_level_read(struct file *file, char __user *user_buf, + size_t len, loff_t *ppos) +{ + struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; + struct ibmvnic_adapter *adapter = ras_comp_int->adapter; + int num = ras_comp_int->num; + char buff[5]; /* decimal max char plus \n and \0 */ + int size; + + size = sprintf(buff, "%d\n", adapter->ras_comps[num].error_check_level); + + if (*ppos >= size) + return 0; + + copy_to_user(user_buf, buff, size); + *ppos += size; + return size; +} + +static ssize_t error_level_write(struct file *file, const char __user *user_buf, + size_t len, loff_t *ppos) +{ + struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; + struct ibmvnic_adapter *adapter = ras_comp_int->adapter; + int num = ras_comp_int->num; + union ibmvnic_crq crq; + unsigned long val; + char buff[9]; /* decimal max int plus \n and \0 */ + + copy_from_user(buff, user_buf, sizeof(buff)); + val = kstrtoul(buff, 10, NULL); + + if (val > 9) + val = 9; + + memset(&crq, 0, sizeof(crq)); + crq.control_ras.first = IBMVNIC_CRQ_CMD; + crq.control_ras.cmd = CONTROL_RAS; + crq.control_ras.correlator = adapter->ras_comps[num].correlator; + crq.control_ras.op = IBMVNIC_ERROR_LEVEL; + crq.control_ras.level = val; + ibmvnic_send_crq(adapter, &crq); + + return len; +} + +static const struct file_operations error_level_ops = { + .owner = THIS_MODULE, + .open = ibmvnic_fw_comp_open, + .read = error_level_read, + .write = error_level_write, +}; + +static ssize_t trace_level_read(struct file *file, char __user *user_buf, + size_t len, loff_t *ppos) +{ + struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; + struct ibmvnic_adapter *adapter = ras_comp_int->adapter; + int num = ras_comp_int->num; + char buff[5]; /* decimal max char plus \n and \0 */ + int size; + + size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_level); + if (*ppos >= size) + return 0; + + copy_to_user(user_buf, buff, size); + *ppos += size; + return size; +} + +static ssize_t trace_level_write(struct file *file, const char __user *user_buf, + size_t len, loff_t *ppos) +{ + struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; + struct ibmvnic_adapter *adapter = ras_comp_int->adapter; + union ibmvnic_crq crq; + unsigned long val; + char buff[9]; /* decimal max int plus \n and \0 */ + + copy_from_user(buff, user_buf, sizeof(buff)); + val = kstrtoul(buff, 10, NULL); + if (val > 9) + val = 9; + + memset(&crq, 0, sizeof(crq)); + crq.control_ras.first = IBMVNIC_CRQ_CMD; + crq.control_ras.cmd = CONTROL_RAS; + crq.control_ras.correlator = + adapter->ras_comps[ras_comp_int->num].correlator; + crq.control_ras.op = IBMVNIC_TRACE_LEVEL; + crq.control_ras.level = val; + ibmvnic_send_crq(adapter, &crq); + + return len; +} + +static const struct file_operations trace_level_ops = { + .owner = THIS_MODULE, + .open = ibmvnic_fw_comp_open, + .read = trace_level_read, + .write = trace_level_write, +}; + +static ssize_t trace_buff_size_read(struct file *file, char __user *user_buf, + size_t len, loff_t *ppos) +{ + struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; + struct ibmvnic_adapter *adapter = ras_comp_int->adapter; + int num = ras_comp_int->num; + char buff[9]; /* decimal max int plus \n and \0 */ + int size; + + size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_buff_size); + if (*ppos >= size) + return 0; + + copy_to_user(user_buf, buff, size); + *ppos += size; + return size; +} + +static ssize_t trace_buff_size_write(struct file *file, + const char __user *user_buf, size_t len, + loff_t *ppos) +{ + struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; + struct ibmvnic_adapter *adapter = ras_comp_int->adapter; + union ibmvnic_crq crq; + unsigned long val; + char buff[9]; /* decimal max int plus \n and \0 */ + + copy_from_user(buff, user_buf, sizeof(buff)); + val = kstrtoul(buff, 10, NULL); + + memset(&crq, 0, sizeof(crq)); + crq.control_ras.first = IBMVNIC_CRQ_CMD; + crq.control_ras.cmd = CONTROL_RAS; + crq.control_ras.correlator = + adapter->ras_comps[ras_comp_int->num].correlator; + crq.control_ras.op = IBMVNIC_CHG_TRACE_BUFF_SZ; + /* trace_buff_sz is 3 bytes, stuff an int into it */ + crq.control_ras.trace_buff_sz[0] = ((u8 *)(&val))[5]; + crq.control_ras.trace_buff_sz[1] = ((u8 *)(&val))[6]; + crq.control_ras.trace_buff_sz[2] = ((u8 *)(&val))[7]; + ibmvnic_send_crq(adapter, &crq); + + return len; +} + +static const struct file_operations trace_size_ops = { + .owner = THIS_MODULE, + .open = ibmvnic_fw_comp_open, + .read = trace_buff_size_read, + .write = trace_buff_size_write, +}; + +static void handle_request_ras_comps_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + struct dentry *dir_ent; + struct dentry *ent; + int i; + + debugfs_remove_recursive(adapter->ras_comps_ent); + + adapter->ras_comps_ent = debugfs_create_dir("ras_comps", + adapter->debugfs_dir); + if (!adapter->ras_comps_ent || IS_ERR(adapter->ras_comps_ent)) { + dev_info(dev, "debugfs create ras_comps dir failed\n"); + return; + } + + for (i = 0; i < adapter->ras_comp_num; i++) { + dir_ent = debugfs_create_dir(adapter->ras_comps[i].name, + adapter->ras_comps_ent); + if (!dir_ent || IS_ERR(dir_ent)) { + dev_info(dev, "debugfs create %s dir failed\n", + adapter->ras_comps[i].name); + continue; + } + + adapter->ras_comp_int[i].adapter = adapter; + adapter->ras_comp_int[i].num = i; + adapter->ras_comp_int[i].desc_blob.data = + &adapter->ras_comps[i].description; + adapter->ras_comp_int[i].desc_blob.size = + sizeof(adapter->ras_comps[i].description); + + /* Don't need to remember the dentry's because the debugfs dir + * gets removed recursively + */ + ent = debugfs_create_blob("description", S_IRUGO, dir_ent, + &adapter->ras_comp_int[i].desc_blob); + ent = debugfs_create_file("trace_buf_size", S_IRUGO | S_IWUSR, + dir_ent, &adapter->ras_comp_int[i], + &trace_size_ops); + ent = debugfs_create_file("trace_level", + S_IRUGO | + (adapter->ras_comps[i].trace_level != + 0xFF ? S_IWUSR : 0), + dir_ent, &adapter->ras_comp_int[i], + &trace_level_ops); + ent = debugfs_create_file("error_level", + S_IRUGO | + (adapter-> + ras_comps[i].error_check_level != + 0xFF ? S_IWUSR : 0), + dir_ent, &adapter->ras_comp_int[i], + &trace_level_ops); + ent = debugfs_create_file("tracing", S_IRUGO | S_IWUSR, + dir_ent, &adapter->ras_comp_int[i], + &tracing_ops); + ent = debugfs_create_file("paused", S_IRUGO | S_IWUSR, + dir_ent, &adapter->ras_comp_int[i], + &paused_ops); + ent = debugfs_create_file("trace", S_IRUGO, dir_ent, + &adapter->ras_comp_int[i], + &trace_ops); + } +} + +static void handle_request_ras_comp_num_rsp(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + int len = adapter->ras_comp_num * sizeof(struct ibmvnic_fw_component); + struct device *dev = &adapter->vdev->dev; + union ibmvnic_crq newcrq; + + adapter->ras_comps = dma_alloc_coherent(dev, len, + &adapter->ras_comps_tok, + GFP_KERNEL); + if (!adapter->ras_comps) { + if (!firmware_has_feature(FW_FEATURE_CMO)) + dev_err(dev, "Couldn't alloc fw comps buffer\n"); + return; + } + + adapter->ras_comp_int = kmalloc(adapter->ras_comp_num * + sizeof(struct ibmvnic_fw_comp_internal), + GFP_KERNEL); + if (!adapter->ras_comp_int) + dma_free_coherent(dev, len, adapter->ras_comps, + adapter->ras_comps_tok); + + memset(&newcrq, 0, sizeof(newcrq)); + newcrq.request_ras_comps.first = IBMVNIC_CRQ_CMD; + newcrq.request_ras_comps.cmd = REQUEST_RAS_COMPS; + newcrq.request_ras_comps.ioba = cpu_to_be32(adapter->ras_comps_tok); + newcrq.request_ras_comps.len = cpu_to_be32(len); + ibmvnic_send_crq(adapter, &newcrq); +} + +static void ibmvnic_free_inflight(struct ibmvnic_adapter *adapter) +{ + struct ibmvnic_inflight_cmd *inflight_cmd; + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_error_buff *error_buff; + unsigned long flags; + unsigned long flags2; + + spin_lock_irqsave(&adapter->inflight_lock, flags); + list_for_each_entry(inflight_cmd, &adapter->inflight, list) { + switch (inflight_cmd->crq.generic.cmd) { + case LOGIN: + dma_unmap_single(dev, adapter->login_buf_token, + adapter->login_buf_sz, + DMA_BIDIRECTIONAL); + dma_unmap_single(dev, adapter->login_rsp_buf_token, + adapter->login_rsp_buf_sz, + DMA_BIDIRECTIONAL); + kfree(adapter->login_rsp_buf); + kfree(adapter->login_buf); + break; + case REQUEST_DUMP: + complete(&adapter->fw_done); + break; + case REQUEST_ERROR_INFO: + spin_lock_irqsave(&adapter->error_list_lock, flags2); + list_for_each_entry(error_buff, &adapter->errors, + list) { + dma_unmap_single(dev, error_buff->dma, + error_buff->len, + DMA_FROM_DEVICE); + kfree(error_buff->buff); + list_del(&error_buff->list); + kfree(error_buff); + } + spin_unlock_irqrestore(&adapter->error_list_lock, + flags2); + break; + } + list_del(&inflight_cmd->list); + kfree(inflight_cmd); + } + spin_unlock_irqrestore(&adapter->inflight_lock, flags); +} + +static void ibmvnic_handle_crq(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + struct ibmvnic_generic_crq *gen_crq = &crq->generic; + struct net_device *netdev = adapter->netdev; + struct device *dev = &adapter->vdev->dev; + long rc; + + netdev_dbg(netdev, "Handling CRQ: %016lx %016lx\n", + ((unsigned long int *)crq)[0], + ((unsigned long int *)crq)[1]); + switch (gen_crq->first) { + case IBMVNIC_CRQ_INIT_RSP: + switch (gen_crq->cmd) { + case IBMVNIC_CRQ_INIT: + dev_info(dev, "Partner initialized\n"); + /* Send back a response */ + rc = ibmvnic_send_crq_init_complete(adapter); + if (rc == 0) + send_version_xchg(adapter); + else + dev_err(dev, "Can't send initrsp rc=%ld\n", rc); + break; + case IBMVNIC_CRQ_INIT_COMPLETE: + dev_info(dev, "Partner initialization complete\n"); + send_version_xchg(adapter); + break; + default: + dev_err(dev, "Unknown crq cmd: %d\n", gen_crq->cmd); + } + return; + case IBMVNIC_CRQ_XPORT_EVENT: + if (gen_crq->cmd == IBMVNIC_PARTITION_MIGRATED) { + dev_info(dev, "Re-enabling adapter\n"); + adapter->migrated = true; + ibmvnic_free_inflight(adapter); + release_sub_crqs(adapter); + rc = ibmvnic_reenable_crq_queue(adapter); + if (rc) + dev_err(dev, "Error after enable rc=%ld\n", rc); + adapter->migrated = false; + rc = ibmvnic_send_crq_init(adapter); + if (rc) + dev_err(dev, "Error sending init rc=%ld\n", rc); + } else { + /* The adapter lost the connection */ + dev_err(dev, "Virtual Adapter failed (rc=%d)\n", + gen_crq->cmd); + ibmvnic_free_inflight(adapter); + release_sub_crqs(adapter); + } + return; + case IBMVNIC_CRQ_CMD_RSP: + break; + default: + dev_err(dev, "Got an invalid msg type 0x%02x\n", + gen_crq->first); + return; + } + + switch (gen_crq->cmd) { + case VERSION_EXCHANGE_RSP: + rc = crq->version_exchange_rsp.rc.code; + if (rc) { + dev_err(dev, "Error %ld in VERSION_EXCHG_RSP\n", rc); + break; + } + dev_info(dev, "Partner protocol version is %d\n", + crq->version_exchange_rsp.version); + if (be16_to_cpu(crq->version_exchange_rsp.version) < + ibmvnic_version) + ibmvnic_version = + be16_to_cpu(crq->version_exchange_rsp.version); + send_cap_queries(adapter); + break; + case QUERY_CAPABILITY_RSP: + handle_query_cap_rsp(crq, adapter); + break; + case QUERY_MAP_RSP: + handle_query_map_rsp(crq, adapter); + break; + case REQUEST_MAP_RSP: + handle_request_map_rsp(crq, adapter); + break; + case REQUEST_UNMAP_RSP: + handle_request_unmap_rsp(crq, adapter); + break; + case REQUEST_CAPABILITY_RSP: + handle_request_cap_rsp(crq, adapter); + break; + case LOGIN_RSP: + netdev_dbg(netdev, "Got Login Response\n"); + handle_login_rsp(crq, adapter); + break; + case LOGICAL_LINK_STATE_RSP: + netdev_dbg(netdev, "Got Logical Link State Response\n"); + adapter->logical_link_state = + crq->logical_link_state_rsp.link_state; + break; + case LINK_STATE_INDICATION: + netdev_dbg(netdev, "Got Logical Link State Indication\n"); + adapter->phys_link_state = + crq->link_state_indication.phys_link_state; + adapter->logical_link_state = + crq->link_state_indication.logical_link_state; + break; + case CHANGE_MAC_ADDR_RSP: + netdev_dbg(netdev, "Got MAC address change Response\n"); + handle_change_mac_rsp(crq, adapter); + break; + case ERROR_INDICATION: + netdev_dbg(netdev, "Got Error Indication\n"); + handle_error_indication(crq, adapter); + break; + case REQUEST_ERROR_RSP: + netdev_dbg(netdev, "Got Error Detail Response\n"); + handle_error_info_rsp(crq, adapter); + break; + case REQUEST_STATISTICS_RSP: + netdev_dbg(netdev, "Got Statistics Response\n"); + complete(&adapter->stats_done); + break; + case REQUEST_DUMP_SIZE_RSP: + netdev_dbg(netdev, "Got Request Dump Size Response\n"); + handle_dump_size_rsp(crq, adapter); + break; + case REQUEST_DUMP_RSP: + netdev_dbg(netdev, "Got Request Dump Response\n"); + complete(&adapter->fw_done); + break; + case QUERY_IP_OFFLOAD_RSP: + netdev_dbg(netdev, "Got Query IP offload Response\n"); + handle_query_ip_offload_rsp(adapter); + break; + case MULTICAST_CTRL_RSP: + netdev_dbg(netdev, "Got multicast control Response\n"); + break; + case CONTROL_IP_OFFLOAD_RSP: + netdev_dbg(netdev, "Got Control IP offload Response\n"); + dma_unmap_single(dev, adapter->ip_offload_ctrl_tok, + sizeof(adapter->ip_offload_ctrl), + DMA_TO_DEVICE); + /* We're done with the queries, perform the login */ + send_login(adapter); + break; + case REQUEST_RAS_COMP_NUM_RSP: + netdev_dbg(netdev, "Got Request RAS Comp Num Response\n"); + if (crq->request_ras_comp_num_rsp.rc.code == 10) { + netdev_dbg(netdev, "Request RAS Comp Num not supported\n"); + break; + } + adapter->ras_comp_num = + be32_to_cpu(crq->request_ras_comp_num_rsp.num_components); + handle_request_ras_comp_num_rsp(crq, adapter); + break; + case REQUEST_RAS_COMPS_RSP: + netdev_dbg(netdev, "Got Request RAS Comps Response\n"); + handle_request_ras_comps_rsp(crq, adapter); + break; + case CONTROL_RAS_RSP: + netdev_dbg(netdev, "Got Control RAS Response\n"); + handle_control_ras_rsp(crq, adapter); + break; + case COLLECT_FW_TRACE_RSP: + netdev_dbg(netdev, "Got Collect firmware trace Response\n"); + complete(&adapter->fw_done); + break; + default: + netdev_err(netdev, "Got an invalid cmd type 0x%02x\n", + gen_crq->cmd); + } +} + +static irqreturn_t ibmvnic_interrupt(int irq, void *instance) +{ + struct ibmvnic_adapter *adapter = instance; + struct ibmvnic_crq_queue *queue = &adapter->crq; + struct vio_dev *vdev = adapter->vdev; + union ibmvnic_crq *crq; + unsigned long flags; + bool done = false; + + spin_lock_irqsave(&queue->lock, flags); + vio_disable_interrupts(vdev); + while (!done) { + /* Pull all the valid messages off the CRQ */ + while ((crq = ibmvnic_next_crq(adapter)) != NULL) { + ibmvnic_handle_crq(crq, adapter); + crq->generic.first = 0; + } + vio_enable_interrupts(vdev); + crq = ibmvnic_next_crq(adapter); + if (crq) { + vio_disable_interrupts(vdev); + ibmvnic_handle_crq(crq, adapter); + crq->generic.first = 0; + } else { + done = true; + } + } + spin_unlock_irqrestore(&queue->lock, flags); + return IRQ_HANDLED; +} + +static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *adapter) +{ + struct vio_dev *vdev = adapter->vdev; + int rc; + + do { + rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); + } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + if (rc) + dev_err(&vdev->dev, "Error enabling adapter (rc=%d)\n", rc); + + return rc; +} + +static int ibmvnic_reset_crq(struct ibmvnic_adapter *adapter) +{ + struct ibmvnic_crq_queue *crq = &adapter->crq; + struct device *dev = &adapter->vdev->dev; + struct vio_dev *vdev = adapter->vdev; + int rc; + + /* Close the CRQ */ + do { + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + /* Clean out the queue */ + memset(crq->msgs, 0, PAGE_SIZE); + crq->cur = 0; + + /* And re-open it again */ + rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, + crq->msg_token, PAGE_SIZE); + + if (rc == H_CLOSED) + /* Adapter is good, but other end is not ready */ + dev_warn(dev, "Partner adapter not ready\n"); + else if (rc != 0) + dev_warn(dev, "Couldn't register crq (rc=%d)\n", rc); + + return rc; +} + +static void ibmvnic_release_crq_queue(struct ibmvnic_adapter *adapter) +{ + struct ibmvnic_crq_queue *crq = &adapter->crq; + struct vio_dev *vdev = adapter->vdev; + long rc; + + netdev_dbg(adapter->netdev, "Releasing CRQ\n"); + free_irq(vdev->irq, adapter); + do { + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + dma_unmap_single(&vdev->dev, crq->msg_token, PAGE_SIZE, + DMA_BIDIRECTIONAL); + free_page((unsigned long)crq->msgs); +} + +static int ibmvnic_init_crq_queue(struct ibmvnic_adapter *adapter) +{ + struct ibmvnic_crq_queue *crq = &adapter->crq; + struct device *dev = &adapter->vdev->dev; + struct vio_dev *vdev = adapter->vdev; + int rc, retrc = -ENOMEM; + + crq->msgs = (union ibmvnic_crq *)get_zeroed_page(GFP_KERNEL); + /* Should we allocate more than one page? */ + + if (!crq->msgs) + return -ENOMEM; + + crq->size = PAGE_SIZE / sizeof(*crq->msgs); + crq->msg_token = dma_map_single(dev, crq->msgs, PAGE_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dev, crq->msg_token)) + goto map_failed; + + rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, + crq->msg_token, PAGE_SIZE); + + if (rc == H_RESOURCE) + /* maybe kexecing and resource is busy. try a reset */ + rc = ibmvnic_reset_crq(adapter); + retrc = rc; + + if (rc == H_CLOSED) { + dev_warn(dev, "Partner adapter not ready\n"); + } else if (rc) { + dev_warn(dev, "Error %d opening adapter\n", rc); + goto reg_crq_failed; + } + + retrc = 0; + + netdev_dbg(adapter->netdev, "registering irq 0x%x\n", vdev->irq); + rc = request_irq(vdev->irq, ibmvnic_interrupt, 0, IBMVNIC_NAME, + adapter); + if (rc) { + dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", + vdev->irq, rc); + goto req_irq_failed; + } + + rc = vio_enable_interrupts(vdev); + if (rc) { + dev_err(dev, "Error %d enabling interrupts\n", rc); + goto req_irq_failed; + } + + crq->cur = 0; + spin_lock_init(&crq->lock); + + return retrc; + +req_irq_failed: + do { + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); +reg_crq_failed: + dma_unmap_single(dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL); +map_failed: + free_page((unsigned long)crq->msgs); + return retrc; +} + +/* debugfs for dump */ +static int ibmvnic_dump_show(struct seq_file *seq, void *v) +{ + struct net_device *netdev = seq->private; + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + struct device *dev = &adapter->vdev->dev; + union ibmvnic_crq crq; + + memset(&crq, 0, sizeof(crq)); + crq.request_dump_size.first = IBMVNIC_CRQ_CMD; + crq.request_dump_size.cmd = REQUEST_DUMP_SIZE; + ibmvnic_send_crq(adapter, &crq); + + init_completion(&adapter->fw_done); + wait_for_completion(&adapter->fw_done); + + seq_write(seq, adapter->dump_data, adapter->dump_data_size); + + dma_unmap_single(dev, adapter->dump_data_token, adapter->dump_data_size, + DMA_BIDIRECTIONAL); + + kfree(adapter->dump_data); + + return 0; +} + +static int ibmvnic_dump_open(struct inode *inode, struct file *file) +{ + return single_open(file, ibmvnic_dump_show, inode->i_private); +} + +static const struct file_operations ibmvnic_dump_ops = { + .owner = THIS_MODULE, + .open = ibmvnic_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) +{ + struct ibmvnic_adapter *adapter; + struct net_device *netdev; + unsigned char *mac_addr_p; + struct dentry *ent; + char buf[16]; /* debugfs name buf */ + int rc; + + dev_dbg(&dev->dev, "entering ibmvnic_probe for UA 0x%x\n", + dev->unit_address); + + mac_addr_p = (unsigned char *)vio_get_attribute(dev, + VETH_MAC_ADDR, NULL); + if (!mac_addr_p) { + dev_err(&dev->dev, + "(%s:%3.3d) ERROR: Can't find MAC_ADDR attribute\n", + __FILE__, __LINE__); + return 0; + } + + netdev = alloc_etherdev_mq(sizeof(struct ibmvnic_adapter), + IBMVNIC_MAX_TX_QUEUES); + if (!netdev) + return -ENOMEM; + + adapter = netdev_priv(netdev); + dev_set_drvdata(&dev->dev, netdev); + adapter->vdev = dev; + adapter->netdev = netdev; + + ether_addr_copy(adapter->mac_addr, mac_addr_p); + ether_addr_copy(netdev->dev_addr, adapter->mac_addr); + netdev->irq = dev->irq; + netdev->netdev_ops = &ibmvnic_netdev_ops; + netdev->ethtool_ops = &ibmvnic_ethtool_ops; + SET_NETDEV_DEV(netdev, &dev->dev); + + spin_lock_init(&adapter->stats_lock); + + rc = ibmvnic_init_crq_queue(adapter); + if (rc) { + dev_err(&dev->dev, "Couldn't initialize crq. rc=%d\n", rc); + goto free_netdev; + } + + INIT_LIST_HEAD(&adapter->errors); + INIT_LIST_HEAD(&adapter->inflight); + spin_lock_init(&adapter->error_list_lock); + spin_lock_init(&adapter->inflight_lock); + + adapter->stats_token = dma_map_single(&dev->dev, &adapter->stats, + sizeof(struct ibmvnic_statistics), + DMA_FROM_DEVICE); + if (dma_mapping_error(&dev->dev, adapter->stats_token)) { + if (!firmware_has_feature(FW_FEATURE_CMO)) + dev_err(&dev->dev, "Couldn't map stats buffer\n"); + goto free_crq; + } + + snprintf(buf, sizeof(buf), "ibmvnic_%x", dev->unit_address); + ent = debugfs_create_dir(buf, NULL); + if (!ent || IS_ERR(ent)) { + dev_info(&dev->dev, "debugfs create directory failed\n"); + adapter->debugfs_dir = NULL; + } else { + adapter->debugfs_dir = ent; + ent = debugfs_create_file("dump", S_IRUGO, adapter->debugfs_dir, + netdev, &ibmvnic_dump_ops); + if (!ent || IS_ERR(ent)) { + dev_info(&dev->dev, + "debugfs create dump file failed\n"); + adapter->debugfs_dump = NULL; + } else { + adapter->debugfs_dump = ent; + } + } + ibmvnic_send_crq_init(adapter); + + init_completion(&adapter->init_done); + wait_for_completion(&adapter->init_done); + + /* needed to pull init_sub_crqs outside of an interrupt context + * because it creates IRQ mappings for the subCRQ queues, causing + * a kernel warning + */ + init_sub_crqs(adapter, 0); + + reinit_completion(&adapter->init_done); + wait_for_completion(&adapter->init_done); + + /* if init_sub_crqs is partially successful, retry */ + while (!adapter->tx_scrq || !adapter->rx_scrq) { + init_sub_crqs(adapter, 1); + + reinit_completion(&adapter->init_done); + wait_for_completion(&adapter->init_done); + } + + netdev->real_num_tx_queues = adapter->req_tx_queues; + + rc = register_netdev(netdev); + if (rc) { + dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc); + goto free_debugfs; + } + dev_info(&dev->dev, "ibmvnic registered\n"); + + return 0; + +free_debugfs: + if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir)) + debugfs_remove_recursive(adapter->debugfs_dir); +free_crq: + ibmvnic_release_crq_queue(adapter); +free_netdev: + free_netdev(netdev); + return rc; +} + +static int ibmvnic_remove(struct vio_dev *dev) +{ + struct net_device *netdev = dev_get_drvdata(&dev->dev); + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + + unregister_netdev(netdev); + + release_sub_crqs(adapter); + + ibmvnic_release_crq_queue(adapter); + + if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir)) + debugfs_remove_recursive(adapter->debugfs_dir); + + if (adapter->ras_comps) + dma_free_coherent(&dev->dev, + adapter->ras_comp_num * + sizeof(struct ibmvnic_fw_component), + adapter->ras_comps, adapter->ras_comps_tok); + + kfree(adapter->ras_comp_int); + + free_netdev(netdev); + dev_set_drvdata(&dev->dev, NULL); + + return 0; +} + +static unsigned long ibmvnic_get_desired_dma(struct vio_dev *vdev) +{ + struct net_device *netdev = dev_get_drvdata(&vdev->dev); + struct ibmvnic_adapter *adapter; + struct iommu_table *tbl; + unsigned long ret = 0; + int i; + + tbl = get_iommu_table_base(&vdev->dev); + + /* netdev inits at probe time along with the structures we need below*/ + if (!netdev) + return IOMMU_PAGE_ALIGN(IBMVNIC_IO_ENTITLEMENT_DEFAULT, tbl); + + adapter = netdev_priv(netdev); + + ret += PAGE_SIZE; /* the crq message queue */ + ret += adapter->bounce_buffer_size; + ret += IOMMU_PAGE_ALIGN(sizeof(struct ibmvnic_statistics), tbl); + + for (i = 0; i < adapter->req_tx_queues + adapter->req_rx_queues; i++) + ret += 4 * PAGE_SIZE; /* the scrq message queue */ + + for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); + i++) + ret += adapter->rx_pool[i].size * + IOMMU_PAGE_ALIGN(adapter->rx_pool[i].buff_size, tbl); + + return ret; +} + +static int ibmvnic_resume(struct device *dev) +{ + struct net_device *netdev = dev_get_drvdata(dev); + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + int i; + + /* kick the interrupt handlers just in case we lost an interrupt */ + for (i = 0; i < adapter->req_rx_queues; i++) + ibmvnic_interrupt_rx(adapter->rx_scrq[i]->irq, + adapter->rx_scrq[i]); + + return 0; +} + +static struct vio_device_id ibmvnic_device_table[] = { + {"network", "IBM,vnic"}, + {"", "" } +}; +MODULE_DEVICE_TABLE(vio, ibmvnic_device_table); + +static const struct dev_pm_ops ibmvnic_pm_ops = { + .resume = ibmvnic_resume +}; + +static struct vio_driver ibmvnic_driver = { + .id_table = ibmvnic_device_table, + .probe = ibmvnic_probe, + .remove = ibmvnic_remove, + .get_desired_dma = ibmvnic_get_desired_dma, + .name = ibmvnic_driver_name, + .pm = &ibmvnic_pm_ops, +}; + +/* module functions */ +static int __init ibmvnic_module_init(void) +{ + pr_info("%s: %s %s\n", ibmvnic_driver_name, ibmvnic_driver_string, + IBMVNIC_DRIVER_VERSION); + + return vio_register_driver(&ibmvnic_driver); +} + +static void __exit ibmvnic_module_exit(void) +{ + vio_unregister_driver(&ibmvnic_driver); +} + +module_init(ibmvnic_module_init); +module_exit(ibmvnic_module_exit); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h new file mode 100644 index 000000000000..1242925ad34c --- /dev/null +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -0,0 +1,1046 @@ +/**************************************************************************/ +/* */ +/* IBM System i and System p Virtual NIC Device Driver */ +/* Copyright (C) 2014 IBM Corp. */ +/* Santiago Leon (santi_leon@yahoo.com) */ +/* Thomas Falcon (tlfalcon@linux.vnet.ibm.com) */ +/* John Allen (jallen@linux.vnet.ibm.com) */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program. */ +/* */ +/* This module contains the implementation of a virtual ethernet device */ +/* for use with IBM i/pSeries LPAR Linux. It utilizes the logical LAN */ +/* option of the RS/6000 Platform Architecture to interface with virtual */ +/* ethernet NICs that are presented to the partition by the hypervisor. */ +/* */ +/**************************************************************************/ + +#define IBMVNIC_NAME "ibmvnic" +#define IBMVNIC_DRIVER_VERSION "1.0" +#define IBMVNIC_INVALID_MAP -1 +#define IBMVNIC_STATS_TIMEOUT 1 +/* basic structures plus 100 2k buffers */ +#define IBMVNIC_IO_ENTITLEMENT_DEFAULT 610305 + +/* Initial module_parameters */ +#define IBMVNIC_RX_WEIGHT 16 +/* when changing this, update IBMVNIC_IO_ENTITLEMENT_DEFAULT */ +#define IBMVNIC_BUFFS_PER_POOL 100 +#define IBMVNIC_MAX_TX_QUEUES 5 + +struct ibmvnic_login_buffer { + __be32 len; + __be32 version; +#define INITIAL_VERSION_LB 1 + __be32 num_txcomp_subcrqs; + __be32 off_txcomp_subcrqs; + __be32 num_rxcomp_subcrqs; + __be32 off_rxcomp_subcrqs; + __be32 login_rsp_ioba; + __be32 login_rsp_len; +} __packed __aligned(8); + +struct ibmvnic_login_rsp_buffer { + __be32 len; + __be32 version; +#define INITIAL_VERSION_LRB 1 + __be32 num_txsubm_subcrqs; + __be32 off_txsubm_subcrqs; + __be32 num_rxadd_subcrqs; + __be32 off_rxadd_subcrqs; + __be32 off_rxadd_buff_size; + __be32 num_supp_tx_desc; + __be32 off_supp_tx_desc; +} __packed __aligned(8); + +struct ibmvnic_query_ip_offload_buffer { + __be32 len; + __be32 version; +#define INITIAL_VERSION_IOB 1 + u8 ipv4_chksum; + u8 ipv6_chksum; + u8 tcp_ipv4_chksum; + u8 tcp_ipv6_chksum; + u8 udp_ipv4_chksum; + u8 udp_ipv6_chksum; + u8 large_tx_ipv4; + u8 large_tx_ipv6; + u8 large_rx_ipv4; + u8 large_rx_ipv6; + u8 reserved1[14]; + __be16 max_ipv4_header_size; + __be16 max_ipv6_header_size; + __be16 max_tcp_header_size; + __be16 max_udp_header_size; + __be32 max_large_tx_size; + __be32 max_large_rx_size; + u8 reserved2[16]; + u8 ipv6_extension_header; +#define IPV6_EH_NOT_SUPPORTED 0x00 +#define IPV6_EH_SUPPORTED_LIM 0x01 +#define IPV6_EH_SUPPORTED 0xFF + u8 tcp_pseudosum_req; +#define TCP_PS_NOT_REQUIRED 0x00 +#define TCP_PS_REQUIRED 0x01 + u8 reserved3[30]; + __be16 num_ipv6_ext_headers; + __be32 off_ipv6_ext_headers; + u8 reserved4[154]; +} __packed __aligned(8); + +struct ibmvnic_control_ip_offload_buffer { + __be32 len; + __be32 version; +#define INITIAL_VERSION_IOB 1 + u8 ipv4_chksum; + u8 ipv6_chksum; + u8 tcp_ipv4_chksum; + u8 tcp_ipv6_chksum; + u8 udp_ipv4_chksum; + u8 udp_ipv6_chksum; + u8 large_tx_ipv4; + u8 large_tx_ipv6; + u8 bad_packet_rx; + u8 large_rx_ipv4; + u8 large_rx_ipv6; + u8 reserved4[111]; +} __packed __aligned(8); + +struct ibmvnic_fw_component { + u8 name[48]; + __be32 trace_buff_size; + u8 correlator; + u8 trace_level; + u8 parent_correlator; + u8 error_check_level; + u8 trace_on; + u8 reserved[7]; + u8 description[192]; +} __packed __aligned(8); + +struct ibmvnic_fw_trace_entry { + __be32 trace_id; + u8 num_valid_data; + u8 reserved[3]; + __be64 pmc_registers; + __be64 timebase; + __be64 trace_data[5]; +} __packed __aligned(8); + +struct ibmvnic_statistics { + __be32 version; + __be32 promiscuous; + __be64 rx_packets; + __be64 rx_bytes; + __be64 tx_packets; + __be64 tx_bytes; + __be64 ucast_tx_packets; + __be64 ucast_rx_packets; + __be64 mcast_tx_packets; + __be64 mcast_rx_packets; + __be64 bcast_tx_packets; + __be64 bcast_rx_packets; + __be64 align_errors; + __be64 fcs_errors; + __be64 single_collision_frames; + __be64 multi_collision_frames; + __be64 sqe_test_errors; + __be64 deferred_tx; + __be64 late_collisions; + __be64 excess_collisions; + __be64 internal_mac_tx_errors; + __be64 carrier_sense; + __be64 too_long_frames; + __be64 internal_mac_rx_errors; + u8 reserved[72]; +} __packed __aligned(8); + +struct ibmvnic_acl_buffer { + __be32 len; + __be32 version; +#define INITIAL_VERSION_IOB 1 + u8 mac_acls_restrict; + u8 vlan_acls_restrict; + u8 reserved1[22]; + __be32 num_mac_addrs; + __be32 offset_mac_addrs; + __be32 num_vlan_ids; + __be32 offset_vlan_ids; + u8 reserved2[80]; +} __packed __aligned(8); + +/* descriptors have been changed, how should this be defined? 1? 4? */ + +#define IBMVNIC_TX_DESC_VERSIONS 3 + +/* is this still needed? */ +struct ibmvnic_tx_comp_desc { + u8 first; + u8 num_comps; + __be16 rcs[5]; + __be32 correlators[5]; +} __packed __aligned(8); + +/* some flags that included in v0 descriptor, which is gone + * only used for IBMVNIC_TCP_CHKSUM and IBMVNIC_UDP_CHKSUM + * and only in some offload_flags variable that doesn't seem + * to be used anywhere, can probably be removed? + */ + +#define IBMVNIC_TCP_CHKSUM 0x20 +#define IBMVNIC_UDP_CHKSUM 0x08 + +#define IBMVNIC_MAX_FRAGS_PER_CRQ 3 + +struct ibmvnic_tx_desc { + u8 first; + u8 type; + +#define IBMVNIC_TX_DESC 0x10 + u8 n_crq_elem; + u8 n_sge; + u8 flags1; +#define IBMVNIC_TX_COMP_NEEDED 0x80 +#define IBMVNIC_TX_CHKSUM_OFFLOAD 0x40 +#define IBMVNIC_TX_LSO 0x20 +#define IBMVNIC_TX_PROT_TCP 0x10 +#define IBMVNIC_TX_PROT_UDP 0x08 +#define IBMVNIC_TX_PROT_IPV4 0x04 +#define IBMVNIC_TX_PROT_IPV6 0x02 +#define IBMVNIC_TX_VLAN_PRESENT 0x01 + u8 flags2; +#define IBMVNIC_TX_VLAN_INSERT 0x80 + __be16 mss; + u8 reserved[4]; + __be32 correlator; + __be16 vlan_id; + __be16 dma_reg; + __be32 sge_len; + __be64 ioba; +} __packed __aligned(8); + +struct ibmvnic_hdr_desc { + u8 first; + u8 type; +#define IBMVNIC_HDR_DESC 0x11 + u8 len; + u8 l2_len; + __be16 l3_len; + u8 l4_len; + u8 flag; + u8 data[24]; +} __packed __aligned(8); + +struct ibmvnic_hdr_ext_desc { + u8 first; + u8 type; +#define IBMVNIC_HDR_EXT_DESC 0x12 + u8 len; + u8 data[29]; +} __packed __aligned(8); + +struct ibmvnic_sge_desc { + u8 first; + u8 type; +#define IBMVNIC_SGE_DESC 0x30 + __be16 sge1_dma_reg; + __be32 sge1_len; + __be64 sge1_ioba; + __be16 reserved; + __be16 sge2_dma_reg; + __be32 sge2_len; + __be64 sge2_ioba; +} __packed __aligned(8); + +struct ibmvnic_rx_comp_desc { + u8 first; + u8 flags; +#define IBMVNIC_IP_CHKSUM_GOOD 0x80 +#define IBMVNIC_TCP_UDP_CHKSUM_GOOD 0x40 +#define IBMVNIC_END_FRAME 0x20 +#define IBMVNIC_EXACT_MC 0x10 +#define IBMVNIC_VLAN_STRIPPED 0x08 + __be16 off_frame_data; + __be32 len; + __be64 correlator; + __be16 vlan_tci; + __be16 rc; + u8 reserved[12]; +} __packed __aligned(8); + +struct ibmvnic_generic_scrq { + u8 first; + u8 reserved[31]; +} __packed __aligned(8); + +struct ibmvnic_rx_buff_add_desc { + u8 first; + u8 reserved[7]; + __be64 correlator; + __be32 ioba; + u8 map_id; + __be32 len:24; + u8 reserved2[8]; +} __packed __aligned(8); + +struct ibmvnic_rc { + u8 code; /* one of enum ibmvnic_rc_codes */ + u8 detailed_data[3]; +} __packed __aligned(4); + +struct ibmvnic_generic_crq { + u8 first; + u8 cmd; + u8 params[10]; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_version_exchange { + u8 first; + u8 cmd; + __be16 version; +#define IBMVNIC_INITIAL_VERSION 1 + u8 reserved[8]; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_capability { + u8 first; + u8 cmd; + __be16 capability; /* one of ibmvnic_capabilities */ + struct ibmvnic_rc rc; + __be32 number; /*FIX: should be __be64, but I'm getting the least + * significant word first + */ +} __packed __aligned(8); + +struct ibmvnic_login { + u8 first; + u8 cmd; + u8 reserved[6]; + __be32 ioba; + __be32 len; +} __packed __aligned(8); + +struct ibmvnic_phys_parms { + u8 first; + u8 cmd; + u8 flags1; +#define IBMVNIC_EXTERNAL_LOOPBACK 0x80 +#define IBMVNIC_INTERNAL_LOOPBACK 0x40 +#define IBMVNIC_PROMISC 0x20 +#define IBMVNIC_PHYS_LINK_ACTIVE 0x10 +#define IBMVNIC_AUTONEG_DUPLEX 0x08 +#define IBMVNIC_FULL_DUPLEX 0x04 +#define IBMVNIC_HALF_DUPLEX 0x02 +#define IBMVNIC_CAN_CHG_PHYS_PARMS 0x01 + u8 flags2; +#define IBMVNIC_LOGICAL_LNK_ACTIVE 0x80 + __be32 speed; +#define IBMVNIC_AUTONEG 0x80 +#define IBMVNIC_10MBPS 0x40 +#define IBMVNIC_100MBPS 0x20 +#define IBMVNIC_1GBPS 0x10 +#define IBMVNIC_10GBPS 0x08 + __be32 mtu; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_logical_link_state { + u8 first; + u8 cmd; + u8 link_state; +#define IBMVNIC_LOGICAL_LNK_DN 0x00 +#define IBMVNIC_LOGICAL_LNK_UP 0x01 +#define IBMVNIC_LOGICAL_LNK_QUERY 0xff + u8 reserved[9]; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_query_ip_offload { + u8 first; + u8 cmd; + u8 reserved[2]; + __be32 len; + __be32 ioba; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_control_ip_offload { + u8 first; + u8 cmd; + u8 reserved[2]; + __be32 ioba; + __be32 len; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_request_dump_size { + u8 first; + u8 cmd; + u8 reserved[6]; + __be32 len; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_request_dump { + u8 first; + u8 cmd; + u8 reserved1[2]; + __be32 ioba; + __be32 len; + u8 reserved2[4]; +} __packed __aligned(8); + +struct ibmvnic_request_dump_rsp { + u8 first; + u8 cmd; + u8 reserved[6]; + __be32 dumped_len; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_request_ras_comp_num { + u8 first; + u8 cmd; + u8 reserved1[2]; + __be32 num_components; + u8 reserved2[4]; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_request_ras_comps { + u8 first; + u8 cmd; + u8 reserved[2]; + __be32 ioba; + __be32 len; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_control_ras { + u8 first; + u8 cmd; + u8 correlator; + u8 level; + u8 op; +#define IBMVNIC_TRACE_LEVEL 1 +#define IBMVNIC_ERROR_LEVEL 2 +#define IBMVNIC_TRACE_PAUSE 3 +#define IBMVNIC_TRACE_RESUME 4 +#define IBMVNIC_TRACE_ON 5 +#define IBMVNIC_TRACE_OFF 6 +#define IBMVNIC_CHG_TRACE_BUFF_SZ 7 + u8 trace_buff_sz[3]; + u8 reserved[4]; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_collect_fw_trace { + u8 first; + u8 cmd; + u8 correlator; + u8 reserved; + __be32 ioba; + __be32 len; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_request_statistics { + u8 first; + u8 cmd; + u8 flags; +#define IBMVNIC_PHYSICAL_PORT 0x80 + u8 reserved1; + __be32 ioba; + __be32 len; + u8 reserved[4]; +} __packed __aligned(8); + +struct ibmvnic_request_debug_stats { + u8 first; + u8 cmd; + u8 reserved[2]; + __be32 ioba; + __be32 len; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_error_indication { + u8 first; + u8 cmd; + u8 flags; +#define IBMVNIC_FATAL_ERROR 0x80 + u8 reserved1; + __be32 error_id; + __be32 detail_error_sz; + __be16 error_cause; + u8 reserved2[2]; +} __packed __aligned(8); + +struct ibmvnic_request_error_info { + u8 first; + u8 cmd; + u8 reserved[2]; + __be32 ioba; + __be32 len; + __be32 error_id; +} __packed __aligned(8); + +struct ibmvnic_request_error_rsp { + u8 first; + u8 cmd; + u8 reserved[2]; + __be32 error_id; + __be32 len; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_link_state_indication { + u8 first; + u8 cmd; + u8 reserved1[2]; + u8 phys_link_state; + u8 logical_link_state; + u8 reserved2[10]; +} __packed __aligned(8); + +struct ibmvnic_change_mac_addr { + u8 first; + u8 cmd; + u8 mac_addr[6]; + struct ibmvnic_rc rc; + u8 reserved[4]; +} __packed __aligned(8); + +struct ibmvnic_multicast_ctrl { + u8 first; + u8 cmd; + u8 mac_addr[6]; + u8 flags; +#define IBMVNIC_ENABLE_MC 0x80 +#define IBMVNIC_DISABLE_MC 0x40 +#define IBMVNIC_ENABLE_ALL 0x20 +#define IBMVNIC_DISABLE_ALL 0x10 + u8 reserved1; + __be16 reserved2; /* was num_enabled_mc_addr; */ + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_get_vpd_size_rsp { + u8 first; + u8 cmd; + u8 reserved[2]; + __be64 len; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_get_vpd { + u8 first; + u8 cmd; + u8 reserved1[2]; + __be32 ioba; + __be32 len; + u8 reserved[4]; +} __packed __aligned(8); + +struct ibmvnic_acl_change_indication { + u8 first; + u8 cmd; + __be16 change_type; +#define IBMVNIC_MAC_ACL 0 +#define IBMVNIC_VLAN_ACL 1 + u8 reserved[12]; +} __packed __aligned(8); + +struct ibmvnic_acl_query { + u8 first; + u8 cmd; + u8 reserved1[2]; + __be32 ioba; + __be32 len; + u8 reserved2[4]; +} __packed __aligned(8); + +struct ibmvnic_tune { + u8 first; + u8 cmd; + u8 reserved1[2]; + __be32 ioba; + __be32 len; + u8 reserved2[4]; +} __packed __aligned(8); + +struct ibmvnic_request_map { + u8 first; + u8 cmd; + u8 reserved1; + u8 map_id; + __be32 ioba; + __be32 len; + u8 reserved2[4]; +} __packed __aligned(8); + +struct ibmvnic_request_map_rsp { + u8 first; + u8 cmd; + u8 reserved1; + u8 map_id; + u8 reserved2[4]; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_request_unmap { + u8 first; + u8 cmd; + u8 reserved1; + u8 map_id; + u8 reserved2[12]; +} __packed __aligned(8); + +struct ibmvnic_request_unmap_rsp { + u8 first; + u8 cmd; + u8 reserved1; + u8 map_id; + u8 reserved2[8]; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +struct ibmvnic_query_map { + u8 first; + u8 cmd; + u8 reserved[14]; +} __packed __aligned(8); + +struct ibmvnic_query_map_rsp { + u8 first; + u8 cmd; + u8 reserved; + u8 page_size; + __be32 tot_pages; + __be32 free_pages; + struct ibmvnic_rc rc; +} __packed __aligned(8); + +union ibmvnic_crq { + struct ibmvnic_generic_crq generic; + struct ibmvnic_version_exchange version_exchange; + struct ibmvnic_version_exchange version_exchange_rsp; + struct ibmvnic_capability query_capability; + struct ibmvnic_capability query_capability_rsp; + struct ibmvnic_capability request_capability; + struct ibmvnic_capability request_capability_rsp; + struct ibmvnic_login login; + struct ibmvnic_generic_crq login_rsp; + struct ibmvnic_phys_parms query_phys_parms; + struct ibmvnic_phys_parms query_phys_parms_rsp; + struct ibmvnic_phys_parms query_phys_capabilities; + struct ibmvnic_phys_parms query_phys_capabilities_rsp; + struct ibmvnic_phys_parms set_phys_parms; + struct ibmvnic_phys_parms set_phys_parms_rsp; + struct ibmvnic_logical_link_state logical_link_state; + struct ibmvnic_logical_link_state logical_link_state_rsp; + struct ibmvnic_query_ip_offload query_ip_offload; + struct ibmvnic_query_ip_offload query_ip_offload_rsp; + struct ibmvnic_control_ip_offload control_ip_offload; + struct ibmvnic_control_ip_offload control_ip_offload_rsp; + struct ibmvnic_request_dump_size request_dump_size; + struct ibmvnic_request_dump_size request_dump_size_rsp; + struct ibmvnic_request_dump request_dump; + struct ibmvnic_request_dump_rsp request_dump_rsp; + struct ibmvnic_request_ras_comp_num request_ras_comp_num; + struct ibmvnic_request_ras_comp_num request_ras_comp_num_rsp; + struct ibmvnic_request_ras_comps request_ras_comps; + struct ibmvnic_request_ras_comps request_ras_comps_rsp; + struct ibmvnic_control_ras control_ras; + struct ibmvnic_control_ras control_ras_rsp; + struct ibmvnic_collect_fw_trace collect_fw_trace; + struct ibmvnic_collect_fw_trace collect_fw_trace_rsp; + struct ibmvnic_request_statistics request_statistics; + struct ibmvnic_generic_crq request_statistics_rsp; + struct ibmvnic_request_debug_stats request_debug_stats; + struct ibmvnic_request_debug_stats request_debug_stats_rsp; + struct ibmvnic_error_indication error_indication; + struct ibmvnic_request_error_info request_error_info; + struct ibmvnic_request_error_rsp request_error_rsp; + struct ibmvnic_link_state_indication link_state_indication; + struct ibmvnic_change_mac_addr change_mac_addr; + struct ibmvnic_change_mac_addr change_mac_addr_rsp; + struct ibmvnic_multicast_ctrl multicast_ctrl; + struct ibmvnic_multicast_ctrl multicast_ctrl_rsp; + struct ibmvnic_generic_crq get_vpd_size; + struct ibmvnic_get_vpd_size_rsp get_vpd_size_rsp; + struct ibmvnic_get_vpd get_vpd; + struct ibmvnic_generic_crq get_vpd_rsp; + struct ibmvnic_acl_change_indication acl_change_indication; + struct ibmvnic_acl_query acl_query; + struct ibmvnic_generic_crq acl_query_rsp; + struct ibmvnic_tune tune; + struct ibmvnic_generic_crq tune_rsp; + struct ibmvnic_request_map request_map; + struct ibmvnic_request_map_rsp request_map_rsp; + struct ibmvnic_request_unmap request_unmap; + struct ibmvnic_request_unmap_rsp request_unmap_rsp; + struct ibmvnic_query_map query_map; + struct ibmvnic_query_map_rsp query_map_rsp; +}; + +enum ibmvnic_rc_codes { + SUCCESS = 0, + PARTIALSUCCESS = 1, + PERMISSION = 2, + NOMEMORY = 3, + PARAMETER = 4, + UNKNOWNCOMMAND = 5, + ABORTED = 6, + INVALIDSTATE = 7, + INVALIDIOBA = 8, + INVALIDLENGTH = 9, + UNSUPPORTEDOPTION = 10, +}; + +enum ibmvnic_capabilities { + MIN_TX_QUEUES = 1, + MIN_RX_QUEUES = 2, + MIN_RX_ADD_QUEUES = 3, + MAX_TX_QUEUES = 4, + MAX_RX_QUEUES = 5, + MAX_RX_ADD_QUEUES = 6, + REQ_TX_QUEUES = 7, + REQ_RX_QUEUES = 8, + REQ_RX_ADD_QUEUES = 9, + MIN_TX_ENTRIES_PER_SUBCRQ = 10, + MIN_RX_ADD_ENTRIES_PER_SUBCRQ = 11, + MAX_TX_ENTRIES_PER_SUBCRQ = 12, + MAX_RX_ADD_ENTRIES_PER_SUBCRQ = 13, + REQ_TX_ENTRIES_PER_SUBCRQ = 14, + REQ_RX_ADD_ENTRIES_PER_SUBCRQ = 15, + TCP_IP_OFFLOAD = 16, + PROMISC_REQUESTED = 17, + PROMISC_SUPPORTED = 18, + MIN_MTU = 19, + MAX_MTU = 20, + REQ_MTU = 21, + MAX_MULTICAST_FILTERS = 22, + VLAN_HEADER_INSERTION = 23, + MAX_TX_SG_ENTRIES = 25, + RX_SG_SUPPORTED = 26, + RX_SG_REQUESTED = 27, + OPT_TX_COMP_SUB_QUEUES = 28, + OPT_RX_COMP_QUEUES = 29, + OPT_RX_BUFADD_Q_PER_RX_COMP_Q = 30, + OPT_TX_ENTRIES_PER_SUBCRQ = 31, + OPT_RXBA_ENTRIES_PER_SUBCRQ = 32, + TX_RX_DESC_REQ = 33, +}; + +enum ibmvnic_error_cause { + ADAPTER_PROBLEM = 0, + BUS_PROBLEM = 1, + FW_PROBLEM = 2, + DD_PROBLEM = 3, + EEH_RECOVERY = 4, + FW_UPDATED = 5, + LOW_MEMORY = 6, +}; + +enum ibmvnic_commands { + VERSION_EXCHANGE = 0x01, + VERSION_EXCHANGE_RSP = 0x81, + QUERY_CAPABILITY = 0x02, + QUERY_CAPABILITY_RSP = 0x82, + REQUEST_CAPABILITY = 0x03, + REQUEST_CAPABILITY_RSP = 0x83, + LOGIN = 0x04, + LOGIN_RSP = 0x84, + QUERY_PHYS_PARMS = 0x05, + QUERY_PHYS_PARMS_RSP = 0x85, + QUERY_PHYS_CAPABILITIES = 0x06, + QUERY_PHYS_CAPABILITIES_RSP = 0x86, + SET_PHYS_PARMS = 0x07, + SET_PHYS_PARMS_RSP = 0x87, + ERROR_INDICATION = 0x08, + REQUEST_ERROR_INFO = 0x09, + REQUEST_ERROR_RSP = 0x89, + REQUEST_DUMP_SIZE = 0x0A, + REQUEST_DUMP_SIZE_RSP = 0x8A, + REQUEST_DUMP = 0x0B, + REQUEST_DUMP_RSP = 0x8B, + LOGICAL_LINK_STATE = 0x0C, + LOGICAL_LINK_STATE_RSP = 0x8C, + REQUEST_STATISTICS = 0x0D, + REQUEST_STATISTICS_RSP = 0x8D, + REQUEST_RAS_COMP_NUM = 0x0E, + REQUEST_RAS_COMP_NUM_RSP = 0x8E, + REQUEST_RAS_COMPS = 0x0F, + REQUEST_RAS_COMPS_RSP = 0x8F, + CONTROL_RAS = 0x10, + CONTROL_RAS_RSP = 0x90, + COLLECT_FW_TRACE = 0x11, + COLLECT_FW_TRACE_RSP = 0x91, + LINK_STATE_INDICATION = 0x12, + CHANGE_MAC_ADDR = 0x13, + CHANGE_MAC_ADDR_RSP = 0x93, + MULTICAST_CTRL = 0x14, + MULTICAST_CTRL_RSP = 0x94, + GET_VPD_SIZE = 0x15, + GET_VPD_SIZE_RSP = 0x95, + GET_VPD = 0x16, + GET_VPD_RSP = 0x96, + TUNE = 0x17, + TUNE_RSP = 0x97, + QUERY_IP_OFFLOAD = 0x18, + QUERY_IP_OFFLOAD_RSP = 0x98, + CONTROL_IP_OFFLOAD = 0x19, + CONTROL_IP_OFFLOAD_RSP = 0x99, + ACL_CHANGE_INDICATION = 0x1A, + ACL_QUERY = 0x1B, + ACL_QUERY_RSP = 0x9B, + REQUEST_DEBUG_STATS = 0x1C, + REQUEST_DEBUG_STATS_RSP = 0x9C, + QUERY_MAP = 0x1D, + QUERY_MAP_RSP = 0x9D, + REQUEST_MAP = 0x1E, + REQUEST_MAP_RSP = 0x9E, + REQUEST_UNMAP = 0x1F, + REQUEST_UNMAP_RSP = 0x9F, + VLAN_CTRL = 0x20, + VLAN_CTRL_RSP = 0xA0, +}; + +enum ibmvnic_crq_type { + IBMVNIC_CRQ_CMD = 0x80, + IBMVNIC_CRQ_CMD_RSP = 0x80, + IBMVNIC_CRQ_INIT_CMD = 0xC0, + IBMVNIC_CRQ_INIT_RSP = 0xC0, + IBMVNIC_CRQ_XPORT_EVENT = 0xFF, +}; + +enum ibmvfc_crq_format { + IBMVNIC_CRQ_INIT = 0x01, + IBMVNIC_CRQ_INIT_COMPLETE = 0x02, + IBMVNIC_PARTITION_MIGRATED = 0x06, +}; + +struct ibmvnic_crq_queue { + union ibmvnic_crq *msgs; + int size, cur; + dma_addr_t msg_token; + spinlock_t lock; +}; + +union sub_crq { + struct ibmvnic_generic_scrq generic; + struct ibmvnic_tx_comp_desc tx_comp; + struct ibmvnic_tx_desc v1; + struct ibmvnic_hdr_desc hdr; + struct ibmvnic_hdr_ext_desc hdr_ext; + struct ibmvnic_sge_desc sge; + struct ibmvnic_rx_comp_desc rx_comp; + struct ibmvnic_rx_buff_add_desc rx_add; +}; + +struct ibmvnic_sub_crq_queue { + union sub_crq *msgs; + int size, cur; + dma_addr_t msg_token; + unsigned long crq_num; + unsigned long hw_irq; + unsigned int irq; + unsigned int pool_index; + int scrq_num; + spinlock_t lock; + struct sk_buff *rx_skb_top; + struct ibmvnic_adapter *adapter; +}; + +struct ibmvnic_long_term_buff { + unsigned char *buff; + dma_addr_t addr; + u64 size; + u8 map_id; +}; + +struct ibmvnic_tx_buff { + struct sk_buff *skb; + dma_addr_t data_dma[IBMVNIC_MAX_FRAGS_PER_CRQ]; + unsigned int data_len[IBMVNIC_MAX_FRAGS_PER_CRQ]; + int index; + int pool_index; + bool last_frag; + bool used_bounce; +}; + +struct ibmvnic_tx_pool { + struct ibmvnic_tx_buff *tx_buff; + int *free_map; + int consumer_index; + int producer_index; + wait_queue_head_t ibmvnic_tx_comp_q; + struct task_struct *work_thread; + struct ibmvnic_long_term_buff long_term_buff; +}; + +struct ibmvnic_rx_buff { + struct sk_buff *skb; + dma_addr_t dma; + unsigned char *data; + int size; + int pool_index; +}; + +struct ibmvnic_rx_pool { + struct ibmvnic_rx_buff *rx_buff; + int size; + int index; + int buff_size; + atomic_t available; + int *free_map; + int next_free; + int next_alloc; + int active; + struct ibmvnic_long_term_buff long_term_buff; +}; + +struct ibmvnic_error_buff { + char *buff; + dma_addr_t dma; + int len; + struct list_head list; + __be32 error_id; +}; + +struct ibmvnic_fw_comp_internal { + struct ibmvnic_adapter *adapter; + int num; + struct debugfs_blob_wrapper desc_blob; + int paused; +}; + +struct ibmvnic_inflight_cmd { + union ibmvnic_crq crq; + struct list_head list; +}; + +struct ibmvnic_adapter { + struct vio_dev *vdev; + struct net_device *netdev; + struct ibmvnic_crq_queue crq; + u8 mac_addr[ETH_ALEN]; + struct ibmvnic_query_ip_offload_buffer ip_offload_buf; + dma_addr_t ip_offload_tok; + struct ibmvnic_control_ip_offload_buffer ip_offload_ctrl; + dma_addr_t ip_offload_ctrl_tok; + bool migrated; + u32 msg_enable; + void *bounce_buffer; + int bounce_buffer_size; + dma_addr_t bounce_buffer_dma; + + /* Statistics */ + struct net_device_stats net_stats; + struct ibmvnic_statistics stats; + dma_addr_t stats_token; + struct completion stats_done; + spinlock_t stats_lock; + int replenish_no_mem; + int replenish_add_buff_success; + int replenish_add_buff_failure; + int replenish_task_cycles; + int tx_send_failed; + int tx_map_failed; + + int phys_link_state; + int logical_link_state; + + /* login data */ + struct ibmvnic_login_buffer *login_buf; + dma_addr_t login_buf_token; + int login_buf_sz; + + struct ibmvnic_login_rsp_buffer *login_rsp_buf; + dma_addr_t login_rsp_buf_token; + int login_rsp_buf_sz; + + atomic_t running_cap_queries; + + struct ibmvnic_sub_crq_queue **tx_scrq; + struct ibmvnic_sub_crq_queue **rx_scrq; + int requested_caps; + + /* rx structs */ + struct napi_struct *napi; + struct ibmvnic_rx_pool *rx_pool; + u64 promisc; + + struct ibmvnic_tx_pool *tx_pool; + bool closing; + struct completion init_done; + + struct list_head errors; + spinlock_t error_list_lock; + + /* debugfs */ + struct dentry *debugfs_dir; + struct dentry *debugfs_dump; + struct completion fw_done; + char *dump_data; + dma_addr_t dump_data_token; + int dump_data_size; + int ras_comp_num; + struct ibmvnic_fw_component *ras_comps; + struct ibmvnic_fw_comp_internal *ras_comp_int; + dma_addr_t ras_comps_tok; + struct dentry *ras_comps_ent; + + /* in-flight commands that allocate and/or map memory*/ + struct list_head inflight; + spinlock_t inflight_lock; + + /* partner capabilities */ + u64 min_tx_queues; + u64 min_rx_queues; + u64 min_rx_add_queues; + u64 max_tx_queues; + u64 max_rx_queues; + u64 max_rx_add_queues; + u64 req_tx_queues; + u64 req_rx_queues; + u64 req_rx_add_queues; + u64 min_tx_entries_per_subcrq; + u64 min_rx_add_entries_per_subcrq; + u64 max_tx_entries_per_subcrq; + u64 max_rx_add_entries_per_subcrq; + u64 req_tx_entries_per_subcrq; + u64 req_rx_add_entries_per_subcrq; + u64 tcp_ip_offload; + u64 promisc_requested; + u64 promisc_supported; + u64 min_mtu; + u64 max_mtu; + u64 req_mtu; + u64 max_multicast_filters; + u64 vlan_header_insertion; + u64 max_tx_sg_entries; + u64 rx_sg_supported; + u64 rx_sg_requested; + u64 opt_tx_comp_sub_queues; + u64 opt_rx_comp_queues; + u64 opt_rx_bufadd_q_per_rx_comp_q; + u64 opt_tx_entries_per_subcrq; + u64 opt_rxba_entries_per_subcrq; + __be64 tx_rx_desc_req; + u8 map_id; +}; -- cgit v1.2.3-59-g8ed1b From e2e2788e4e96af1383a411335c5686a4b8c82496 Mon Sep 17 00:00:00 2001 From: Chun-Hao Lin Date: Thu, 24 Dec 2015 21:15:26 +0800 Subject: r8169:Fix typo in setting RTL8168H PHY parameter In function "rtl8168h_2_hw_phy_config", there is a typo in setting RTL8168H PHY parameter. Signed-off-by: Chunhao Lin Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 79ef799f88ab..11cc32b1755e 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -3947,7 +3947,7 @@ static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp) data = (ioffset_p3<<12)|(ioffset_p2<<8)|(ioffset_p1<<4)|(ioffset_p0); if ((ioffset_p3 != 0x0f) || (ioffset_p2 != 0x0f) || - (ioffset_p1 != 0x0f) || (ioffset_p0 == 0x0f)) { + (ioffset_p1 != 0x0f) || (ioffset_p0 != 0x0f)) { rtl_writephy(tp, 0x1f, 0x0bcf); rtl_writephy(tp, 0x16, data); rtl_writephy(tp, 0x1f, 0x0000); -- cgit v1.2.3-59-g8ed1b From 584933334b7a81f09637f28cbee759a1e24428db Mon Sep 17 00:00:00 2001 From: Chun-Hao Lin Date: Thu, 24 Dec 2015 21:15:27 +0800 Subject: r8169:Update the way of reading RTL8168H PHY register "rg_saw_cnt" The vlaue of RTL8168H PHY register "rg_saw_cnt" only valid from bit0 to bit13. When read this register, add bitwise-anding its value with 0x3fff. Signed-off-by: Chunhao Lin Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 11cc32b1755e..58365bcf2370 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -6136,7 +6136,7 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp) rtl_pcie_state_l2l3_enable(tp, false); rtl_writephy(tp, 0x1f, 0x0c42); - rg_saw_cnt = rtl_readphy(tp, 0x13); + rg_saw_cnt = (rtl_readphy(tp, 0x13) & 0x3fff); rtl_writephy(tp, 0x1f, 0x0000); if (rg_saw_cnt > 0) { u16 sw_cnt_1ms_ini; -- cgit v1.2.3-59-g8ed1b From f45688280e19b61b5c15a17ebd8c3d9b60fa1252 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 27 Dec 2015 22:01:29 +0100 Subject: qlcnic: constify qlcnic_dcb_ops structures The qlcnic_dcb_ops structures are never modified, so declare them as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c | 4 ++-- drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c index a72bcddf160a..4b76c69fe86d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c @@ -167,7 +167,7 @@ struct qlcnic_dcb_cfg { u32 version; }; -static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = { +static const struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = { .init_dcbnl_ops = __qlcnic_init_dcbnl_ops, .free = __qlcnic_dcb_free, .attach = __qlcnic_dcb_attach, @@ -180,7 +180,7 @@ static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = { .aen_handler = qlcnic_83xx_dcb_aen_handler, }; -static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = { +static const struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = { .init_dcbnl_ops = __qlcnic_init_dcbnl_ops, .free = __qlcnic_dcb_free, .attach = __qlcnic_dcb_attach, diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h index 3cf4a10fbe1e..9777e5713525 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h @@ -37,7 +37,7 @@ struct qlcnic_dcb { struct qlcnic_adapter *adapter; struct delayed_work aen_work; struct workqueue_struct *wq; - struct qlcnic_dcb_ops *ops; + const struct qlcnic_dcb_ops *ops; struct qlcnic_dcb_cfg *cfg; unsigned long state; }; -- cgit v1.2.3-59-g8ed1b From be58a0da1672391b246880450e990fe36d7ba24d Mon Sep 17 00:00:00 2001 From: Jeffrey Huang Date: Sun, 27 Dec 2015 18:19:18 -0500 Subject: bnxt_en: support hwrm_func_drv_unrgtr command During remove_one, the driver should issue hwrm_func_drv_unrgtr command to inform firmware that this function has been unloaded. This is to let firmware keep track of driver present/absent state when driver is gracefully unloaded. A keep alive timer is needed later to keep track of driver state during abnormal shutdown. Signed-off-by: Jeffrey Huang Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 443ef92f9803..8d401e836057 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -2710,6 +2710,14 @@ static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp) return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); } +static int bnxt_hwrm_func_drv_unrgtr(struct bnxt *bp) +{ + struct hwrm_func_drv_unrgtr_input req = {0}; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_DRV_UNRGTR, -1, -1); + return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); +} + static int bnxt_hwrm_tunnel_dst_port_free(struct bnxt *bp, u8 tunnel_type) { u32 rc = 0; @@ -5549,6 +5557,7 @@ static void bnxt_remove_one(struct pci_dev *pdev) cancel_work_sync(&bp->sp_task); bp->sp_event = 0; + bnxt_hwrm_func_drv_unrgtr(bp); bnxt_free_hwrm_resources(bp); pci_iounmap(pdev, bp->bar2); pci_iounmap(pdev, bp->bar1); -- cgit v1.2.3-59-g8ed1b From edd0c2cc2b73ff21f356d6cbd3b5bf83e692ea9d Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sun, 27 Dec 2015 18:19:19 -0500 Subject: bnxt_en: Optimize ring alloc and ring free functions. Remove the unnecessary "if" statement before the "for" statement: if (x) { for (i = 0; i < x; i++) ... } Also, change the ring free function to return void as it only returns 0. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 201 +++++++++++++----------------- 1 file changed, 87 insertions(+), 114 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 8d401e836057..e340fb31ed78 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1969,11 +1969,12 @@ static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr) rxr->rx_prod = prod; ring->fw_ring_id = INVALID_HW_RING_ID; + ring = &rxr->rx_agg_ring_struct; + ring->fw_ring_id = INVALID_HW_RING_ID; + if (!(bp->flags & BNXT_FLAG_AGG_RINGS)) return 0; - ring = &rxr->rx_agg_ring_struct; - type = ((u32)PAGE_SIZE << RX_BD_LEN_SHIFT) | RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP; @@ -1989,7 +1990,6 @@ static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr) prod = NEXT_RX_AGG(prod); } rxr->rx_agg_prod = prod; - ring->fw_ring_id = INVALID_HW_RING_ID; if (bp->flags & BNXT_FLAG_TPA) { if (rxr->rx_tpa) { @@ -3301,54 +3301,45 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp) { int i, rc = 0; - if (bp->cp_nr_rings) { - for (i = 0; i < bp->cp_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; - struct bnxt_ring_struct *ring = &cpr->cp_ring_struct; + for (i = 0; i < bp->cp_nr_rings; i++) { + struct bnxt_napi *bnapi = bp->bnapi[i]; + struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; + struct bnxt_ring_struct *ring = &cpr->cp_ring_struct; - rc = hwrm_ring_alloc_send_msg(bp, ring, - HWRM_RING_ALLOC_CMPL, i, - INVALID_STATS_CTX_ID); - if (rc) - goto err_out; - cpr->cp_doorbell = bp->bar1 + i * 0x80; - BNXT_CP_DB(cpr->cp_doorbell, cpr->cp_raw_cons); - bp->grp_info[i].cp_fw_ring_id = ring->fw_ring_id; - } + rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_CMPL, i, + INVALID_STATS_CTX_ID); + if (rc) + goto err_out; + cpr->cp_doorbell = bp->bar1 + i * 0x80; + BNXT_CP_DB(cpr->cp_doorbell, cpr->cp_raw_cons); + bp->grp_info[i].cp_fw_ring_id = ring->fw_ring_id; } - if (bp->tx_nr_rings) { - for (i = 0; i < bp->tx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_tx_ring_info *txr = &bnapi->tx_ring; - struct bnxt_ring_struct *ring = &txr->tx_ring_struct; - u16 fw_stats_ctx = bp->grp_info[i].fw_stats_ctx; + for (i = 0; i < bp->tx_nr_rings; i++) { + struct bnxt_napi *bnapi = bp->bnapi[i]; + struct bnxt_tx_ring_info *txr = &bnapi->tx_ring; + struct bnxt_ring_struct *ring = &txr->tx_ring_struct; + u16 fw_stats_ctx = bp->grp_info[i].fw_stats_ctx; - rc = hwrm_ring_alloc_send_msg(bp, ring, - HWRM_RING_ALLOC_TX, i, - fw_stats_ctx); - if (rc) - goto err_out; - txr->tx_doorbell = bp->bar1 + i * 0x80; - } + rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_TX, i, + fw_stats_ctx); + if (rc) + goto err_out; + txr->tx_doorbell = bp->bar1 + i * 0x80; } - if (bp->rx_nr_rings) { - for (i = 0; i < bp->rx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; - struct bnxt_ring_struct *ring = &rxr->rx_ring_struct; + for (i = 0; i < bp->rx_nr_rings; i++) { + struct bnxt_napi *bnapi = bp->bnapi[i]; + struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_ring_struct *ring = &rxr->rx_ring_struct; - rc = hwrm_ring_alloc_send_msg(bp, ring, - HWRM_RING_ALLOC_RX, i, - INVALID_STATS_CTX_ID); - if (rc) - goto err_out; - rxr->rx_doorbell = bp->bar1 + i * 0x80; - writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell); - bp->grp_info[i].rx_fw_ring_id = ring->fw_ring_id; - } + rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_RX, i, + INVALID_STATS_CTX_ID); + if (rc) + goto err_out; + rxr->rx_doorbell = bp->bar1 + i * 0x80; + writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell); + bp->grp_info[i].rx_fw_ring_id = ring->fw_ring_id; } if (bp->flags & BNXT_FLAG_AGG_RINGS) { @@ -3416,91 +3407,73 @@ static int hwrm_ring_free_send_msg(struct bnxt *bp, return 0; } -static int bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path) +static void bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path) { - int i, rc = 0; + int i; if (!bp->bnapi) - return 0; + return; - if (bp->tx_nr_rings) { - for (i = 0; i < bp->tx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_tx_ring_info *txr = &bnapi->tx_ring; - struct bnxt_ring_struct *ring = &txr->tx_ring_struct; - u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; - - if (ring->fw_ring_id != INVALID_HW_RING_ID) { - hwrm_ring_free_send_msg( - bp, ring, - RING_FREE_REQ_RING_TYPE_TX, - close_path ? cmpl_ring_id : - INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; - } + for (i = 0; i < bp->tx_nr_rings; i++) { + struct bnxt_napi *bnapi = bp->bnapi[i]; + struct bnxt_tx_ring_info *txr = &bnapi->tx_ring; + struct bnxt_ring_struct *ring = &txr->tx_ring_struct; + u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; + + if (ring->fw_ring_id != INVALID_HW_RING_ID) { + hwrm_ring_free_send_msg(bp, ring, + RING_FREE_REQ_RING_TYPE_TX, + close_path ? cmpl_ring_id : + INVALID_HW_RING_ID); + ring->fw_ring_id = INVALID_HW_RING_ID; } } - if (bp->rx_nr_rings) { - for (i = 0; i < bp->rx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; - struct bnxt_ring_struct *ring = &rxr->rx_ring_struct; - u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; - - if (ring->fw_ring_id != INVALID_HW_RING_ID) { - hwrm_ring_free_send_msg( - bp, ring, - RING_FREE_REQ_RING_TYPE_RX, - close_path ? cmpl_ring_id : - INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; - bp->grp_info[i].rx_fw_ring_id = - INVALID_HW_RING_ID; - } + for (i = 0; i < bp->rx_nr_rings; i++) { + struct bnxt_napi *bnapi = bp->bnapi[i]; + struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_ring_struct *ring = &rxr->rx_ring_struct; + u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; + + if (ring->fw_ring_id != INVALID_HW_RING_ID) { + hwrm_ring_free_send_msg(bp, ring, + RING_FREE_REQ_RING_TYPE_RX, + close_path ? cmpl_ring_id : + INVALID_HW_RING_ID); + ring->fw_ring_id = INVALID_HW_RING_ID; + bp->grp_info[i].rx_fw_ring_id = INVALID_HW_RING_ID; } } - if (bp->rx_agg_nr_pages) { - for (i = 0; i < bp->rx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; - struct bnxt_ring_struct *ring = - &rxr->rx_agg_ring_struct; - u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; - - if (ring->fw_ring_id != INVALID_HW_RING_ID) { - hwrm_ring_free_send_msg( - bp, ring, - RING_FREE_REQ_RING_TYPE_RX, - close_path ? cmpl_ring_id : - INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; - bp->grp_info[i].agg_fw_ring_id = - INVALID_HW_RING_ID; - } + for (i = 0; i < bp->rx_nr_rings; i++) { + struct bnxt_napi *bnapi = bp->bnapi[i]; + struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_ring_struct *ring = &rxr->rx_agg_ring_struct; + u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; + + if (ring->fw_ring_id != INVALID_HW_RING_ID) { + hwrm_ring_free_send_msg(bp, ring, + RING_FREE_REQ_RING_TYPE_RX, + close_path ? cmpl_ring_id : + INVALID_HW_RING_ID); + ring->fw_ring_id = INVALID_HW_RING_ID; + bp->grp_info[i].agg_fw_ring_id = INVALID_HW_RING_ID; } } - if (bp->cp_nr_rings) { - for (i = 0; i < bp->cp_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; - struct bnxt_ring_struct *ring = &cpr->cp_ring_struct; - - if (ring->fw_ring_id != INVALID_HW_RING_ID) { - hwrm_ring_free_send_msg( - bp, ring, - RING_FREE_REQ_RING_TYPE_CMPL, - INVALID_HW_RING_ID); - ring->fw_ring_id = INVALID_HW_RING_ID; - bp->grp_info[i].cp_fw_ring_id = - INVALID_HW_RING_ID; - } + for (i = 0; i < bp->cp_nr_rings; i++) { + struct bnxt_napi *bnapi = bp->bnapi[i]; + struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; + struct bnxt_ring_struct *ring = &cpr->cp_ring_struct; + + if (ring->fw_ring_id != INVALID_HW_RING_ID) { + hwrm_ring_free_send_msg(bp, ring, + RING_FREE_REQ_RING_TYPE_CMPL, + INVALID_HW_RING_ID); + ring->fw_ring_id = INVALID_HW_RING_ID; + bp->grp_info[i].cp_fw_ring_id = INVALID_HW_RING_ID; } } - - return rc; } int bnxt_hwrm_set_coal(struct bnxt *bp) -- cgit v1.2.3-59-g8ed1b From 2731d70fa9cbb62e45743171bf979784fb36778c Mon Sep 17 00:00:00 2001 From: Rob Swindell Date: Sun, 27 Dec 2015 18:19:20 -0500 Subject: bnxt_en: Add support for upgrading APE/NC-SI firmware via Ethtool FLASHDEV NC-SI firmware of type apeFW (10) is now supported. Signed-off-by: Rob Swindell Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 45bd628eaf3a..ea94450ab91e 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -818,6 +818,9 @@ static int bnxt_flash_firmware(struct net_device *dev, case BNX_DIR_TYPE_BOOTCODE_2: code_type = CODE_BOOT; break; + case BNX_DIR_TYPE_APE_FW: + code_type = CODE_MCTP_PASSTHRU; + break; default: netdev_err(dev, "Unsupported directory entry type: %u\n", dir_type); -- cgit v1.2.3-59-g8ed1b From 665e350ddbfde88c5c18142dfd7b8c64556bc964 Mon Sep 17 00:00:00 2001 From: Satish Baddipadige Date: Sun, 27 Dec 2015 18:19:21 -0500 Subject: bnxt_en: Increment checksum error counter only if NETIF_F_RXCSUM is set. rx_l4_csum_error is now incremented only when offload is enabled Signed-off-by: Satish Baddipadige Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index e340fb31ed78..3ce3bd337bb3 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1187,8 +1187,10 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, skb->csum_level = RX_CMP_ENCAP(rxcmp1); } } else { - if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS) - cpr->rx_l4_csum_errors++; + if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS) { + if (dev->features & NETIF_F_RXCSUM) + cpr->rx_l4_csum_errors++; + } } skb_record_rx_queue(skb, bnapi->index); -- cgit v1.2.3-59-g8ed1b From 45019a180358c3cf290c3f3dc953c44f978d5527 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sun, 27 Dec 2015 18:19:22 -0500 Subject: bnxt_en: Allocate rx_cpu_rmap only if Accelerated RFS is enabled. Also, no need to check for bp->rx_nr_rings as it is always >= 1. If the allocation fails, it is not a fatal error and we can still proceed. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 3ce3bd337bb3..06ce100f3c54 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4009,10 +4009,8 @@ static int bnxt_set_real_num_queues(struct bnxt *bp) return rc; #ifdef CONFIG_RFS_ACCEL - if (bp->rx_nr_rings) + if (bp->flags & BNXT_FLAG_RFS) dev->rx_cpu_rmap = alloc_irq_cpu_rmap(bp->rx_nr_rings); - if (!dev->rx_cpu_rmap) - rc = -ENOMEM; #endif return rc; -- cgit v1.2.3-59-g8ed1b From 27e241896f2e21c96200df711659117923dec8a2 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sun, 27 Dec 2015 18:19:23 -0500 Subject: bnxt_en: Don't treat single segment rx frames as GRO frames. If hardware completes single segment rx frames, don't bother setting up all the GRO related fields. Pass the SKB up as a normal frame. Reviewed-by: vasundhara volam Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 06ce100f3c54..082c1e95edc7 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -856,8 +856,13 @@ static inline struct sk_buff *bnxt_gro_skb(struct bnxt_tpa_info *tpa_info, struct tcphdr *th; int payload_off, tcp_opt_len = 0; int len, nw_off; + u16 segs; - NAPI_GRO_CB(skb)->count = TPA_END_TPA_SEGS(tpa_end); + segs = TPA_END_TPA_SEGS(tpa_end); + if (segs == 1) + return skb; + + NAPI_GRO_CB(skb)->count = segs; skb_shinfo(skb)->gso_size = le32_to_cpu(tpa_end1->rx_tpa_end_cmp_seg_len); skb_shinfo(skb)->gso_type = tpa_info->gso_type; -- cgit v1.2.3-59-g8ed1b From 2bcfa6f6e7cf867e4aa623f84caea4bc413d38c9 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sun, 27 Dec 2015 18:19:24 -0500 Subject: bnxt_en: Check hardware resources before enabling NTUPLE. The hardware resources required to enable NTUPLE varies depending on how many rx channels are configured. We need to make sure we have the resources before we enable NTUPLE. Add bnxt_rfs_capable() to do the checking. In addition, we need to do the same checking in ndo_fix_features(). As the rx channels are changed using ethtool -L, we call netdev_update_features() to make the necessary adjustment for NTUPLE. Calling netdev_update_features() in netif_running() state but before calling bnxt_open_nic() would be a problem. To make this work, bnxt_set_features() has to be modified to test for BNXT_STATE_OPEN for the true hardware state instead of checking netif_running(). Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 41 ++++++++++++++++++----- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 2 ++ 2 files changed, 35 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 082c1e95edc7..8827b7bf4bce 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4916,9 +4916,32 @@ skip_uc: return rc; } +static bool bnxt_rfs_capable(struct bnxt *bp) +{ +#ifdef CONFIG_RFS_ACCEL + struct bnxt_pf_info *pf = &bp->pf; + int vnics; + + if (BNXT_VF(bp) || !(bp->flags & BNXT_FLAG_MSIX_CAP)) + return false; + + vnics = 1 + bp->rx_nr_rings; + if (vnics > pf->max_rsscos_ctxs || vnics > pf->max_vnics) + return false; + + return true; +#else + return false; +#endif +} + static netdev_features_t bnxt_fix_features(struct net_device *dev, netdev_features_t features) { + struct bnxt *bp = netdev_priv(dev); + + if (!bnxt_rfs_capable(bp)) + features &= ~NETIF_F_NTUPLE; return features; } @@ -4959,7 +4982,7 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features) bp->flags = flags; - if (!netif_running(dev)) { + if (!test_bit(BNXT_STATE_OPEN, &bp->state)) { if (update_tpa) bnxt_set_ring_params(bp); return rc; @@ -5639,11 +5662,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (bnxt_vf_pciid(ent->driver_data)) bp->flags |= BNXT_FLAG_VF; - if (pdev->msix_cap) { + if (pdev->msix_cap) bp->flags |= BNXT_FLAG_MSIX_CAP; - if (BNXT_PF(bp)) - bp->flags |= BNXT_FLAG_RFS; - } rc = bnxt_init_board(pdev, dev); if (rc < 0) @@ -5662,9 +5682,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) NETIF_F_RXHASH | NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_GRO; - if (bp->flags & BNXT_FLAG_RFS) - dev->hw_features |= NETIF_F_NTUPLE; - dev->hw_enc_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | @@ -5723,6 +5740,14 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) bp->cp_nr_rings = max_t(int, bp->rx_nr_rings, bp->tx_nr_rings); bp->num_stat_ctxs = bp->cp_nr_rings; + if (BNXT_PF(bp)) { + dev->hw_features |= NETIF_F_NTUPLE; + if (bnxt_rfs_capable(bp)) { + bp->flags |= BNXT_FLAG_RFS; + dev->features |= NETIF_F_NTUPLE; + } + } + if (dev->hw_features & NETIF_F_HW_VLAN_CTAG_RX) bp->flags |= BNXT_FLAG_STRIP_VLAN; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index ea94450ab91e..a39511f5be75 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -266,6 +266,8 @@ static int bnxt_set_channels(struct net_device *dev, bp->cp_nr_rings = max_t(int, bp->tx_nr_rings, bp->rx_nr_rings); bp->num_stat_ctxs = bp->cp_nr_rings; + /* After changing number of rx channels, update NTUPLE feature. */ + netdev_update_features(dev); if (netif_running(dev)) { rc = bnxt_open_nic(bp, true, false); if ((!rc) && BNXT_PF(bp)) { -- cgit v1.2.3-59-g8ed1b From 92268c328a8dae4635b3deaca52a8ed329642219 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sun, 27 Dec 2015 18:19:25 -0500 Subject: bnxt_en: Cleanup bnxt_hwrm_func_cfg(). 1. Use local variable pf for repeated access to this pointer. 2. The 2nd argument num_vfs was unnecessarily declared as pointer to int. This function doesn't change num_vfs so change the argument to int. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c | 33 ++++++++++++------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index ea044bbcd384..95b93162a1de 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -363,7 +363,7 @@ static int bnxt_hwrm_func_buf_rgtr(struct bnxt *bp) } /* only call by PF to reserve resources for VF */ -static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs) +static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs) { u32 rc = 0, mtu, i; u16 vf_tx_rings, vf_rx_rings, vf_cp_rings, vf_stat_ctx, vf_vnics; @@ -378,18 +378,17 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs) * be removed once new HWRM provides HW ring groups capability in * hwrm_func_qcap. */ - vf_cp_rings = min_t(u16, bp->pf.max_cp_rings, bp->pf.max_stat_ctxs); - vf_cp_rings = (vf_cp_rings - bp->cp_nr_rings) / *num_vfs; + vf_cp_rings = min_t(u16, pf->max_cp_rings, pf->max_stat_ctxs); + vf_cp_rings = (vf_cp_rings - bp->cp_nr_rings) / num_vfs; /* TODO: restore this logic below once the WA above is removed */ - /* vf_cp_rings = (bp->pf.max_cp_rings - bp->cp_nr_rings) / *num_vfs; */ - vf_stat_ctx = (bp->pf.max_stat_ctxs - bp->num_stat_ctxs) / *num_vfs; + /* vf_cp_rings = (pf->max_cp_rings - bp->cp_nr_rings) / num_vfs; */ + vf_stat_ctx = (pf->max_stat_ctxs - bp->num_stat_ctxs) / num_vfs; if (bp->flags & BNXT_FLAG_AGG_RINGS) - vf_rx_rings = (bp->pf.max_rx_rings - bp->rx_nr_rings * 2) / - *num_vfs; + vf_rx_rings = (pf->max_rx_rings - bp->rx_nr_rings * 2) / + num_vfs; else - vf_rx_rings = (bp->pf.max_rx_rings - bp->rx_nr_rings) / - *num_vfs; - vf_tx_rings = (bp->pf.max_tx_rings - bp->tx_nr_rings) / *num_vfs; + vf_rx_rings = (pf->max_rx_rings - bp->rx_nr_rings) / num_vfs; + vf_tx_rings = (pf->max_tx_rings - bp->tx_nr_rings) / num_vfs; req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_MTU | FUNC_CFG_REQ_ENABLES_MRU | @@ -417,22 +416,22 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs) req.num_stat_ctxs = cpu_to_le16(vf_stat_ctx); mutex_lock(&bp->hwrm_cmd_lock); - for (i = 0; i < *num_vfs; i++) { + for (i = 0; i < num_vfs; i++) { req.vf_id = cpu_to_le16(pf->first_vf_id + i); rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); if (rc) break; - bp->pf.active_vfs = i + 1; - bp->pf.vf[i].fw_fid = le16_to_cpu(req.vf_id); + pf->active_vfs = i + 1; + pf->vf[i].fw_fid = le16_to_cpu(req.vf_id); } mutex_unlock(&bp->hwrm_cmd_lock); if (!rc) { - bp->pf.max_pf_tx_rings = bp->tx_nr_rings; + pf->max_pf_tx_rings = bp->tx_nr_rings; if (bp->flags & BNXT_FLAG_AGG_RINGS) - bp->pf.max_pf_rx_rings = bp->rx_nr_rings * 2; + pf->max_pf_rx_rings = bp->rx_nr_rings * 2; else - bp->pf.max_pf_rx_rings = bp->rx_nr_rings; + pf->max_pf_rx_rings = bp->rx_nr_rings; } return rc; } @@ -492,7 +491,7 @@ static int bnxt_sriov_enable(struct bnxt *bp, int *num_vfs) goto err_out1; /* Reserve resources for VFs */ - rc = bnxt_hwrm_func_cfg(bp, num_vfs); + rc = bnxt_hwrm_func_cfg(bp, *num_vfs); if (rc) goto err_out2; -- cgit v1.2.3-59-g8ed1b From 4a21b49b34c01137a67bf0fe185c5d0fff747e4d Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sun, 27 Dec 2015 18:19:26 -0500 Subject: bnxt_en: Improve VF resource accounting. We need to keep track of all resources, such as rx rings, tx rings, cmpl rings, rss contexts, stats contexts, vnics, after we have divided them for the VFs. Otherwise, subsequent ring changes on the PF may not work correctly. We adjust all max resources in struct bnxt_pf_info after they have been assigned to the VFs. There is no need to keep the separate max_pf_tx_rings and max_pf_rx_rings. When SR-IOV is disabled, we call bnxt_hwrm_func_qcaps() to restore the max resources for the PF. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 +++----- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 3 +-- drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c | 15 ++++++++------- 3 files changed, 12 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 8827b7bf4bce..22c26448fcaf 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -3592,7 +3592,7 @@ static int bnxt_hwrm_stat_ctx_alloc(struct bnxt *bp) return 0; } -static int bnxt_hwrm_func_qcaps(struct bnxt *bp) +int bnxt_hwrm_func_qcaps(struct bnxt *bp) { int rc = 0; struct hwrm_func_qcaps_input req = {0}; @@ -3616,9 +3616,7 @@ static int bnxt_hwrm_func_qcaps(struct bnxt *bp) pf->max_rsscos_ctxs = le16_to_cpu(resp->max_rsscos_ctx); pf->max_cp_rings = le16_to_cpu(resp->max_cmpl_rings); pf->max_tx_rings = le16_to_cpu(resp->max_tx_rings); - pf->max_pf_tx_rings = pf->max_tx_rings; pf->max_rx_rings = le16_to_cpu(resp->max_rx_rings); - pf->max_pf_rx_rings = pf->max_rx_rings; pf->max_l2_ctxs = le16_to_cpu(resp->max_l2_ctxs); pf->max_vnics = le16_to_cpu(resp->max_vnics); pf->max_stat_ctxs = le16_to_cpu(resp->max_stat_ctx); @@ -5623,8 +5621,8 @@ void bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx) int max_rings = 0; if (BNXT_PF(bp)) { - *max_tx = bp->pf.max_pf_tx_rings; - *max_rx = bp->pf.max_pf_rx_rings; + *max_tx = bp->pf.max_tx_rings; + *max_rx = bp->pf.max_rx_rings; max_rings = min_t(int, bp->pf.max_irqs, bp->pf.max_cp_rings); max_rings = min_t(int, max_rings, bp->pf.max_stat_ctxs); } else { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index f199f4cc8ffe..8abaecefefd9 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -722,9 +722,7 @@ struct bnxt_pf_info { u16 max_rsscos_ctxs; u16 max_cp_rings; u16 max_tx_rings; /* HW assigned max tx rings for this PF */ - u16 max_pf_tx_rings; /* runtime max tx rings owned by PF */ u16 max_rx_rings; /* HW assigned max rx rings for this PF */ - u16 max_pf_rx_rings; /* runtime max rx rings owned by PF */ u16 max_irqs; u16 max_l2_ctxs; u16 max_vnics; @@ -1084,6 +1082,7 @@ void bnxt_hwrm_cmd_hdr_init(struct bnxt *, void *, u16, u16, u16); int _hwrm_send_message(struct bnxt *, void *, u32, int); int hwrm_send_message(struct bnxt *, void *, u32, int); int bnxt_hwrm_set_coal(struct bnxt *); +int bnxt_hwrm_func_qcaps(struct bnxt *); int bnxt_hwrm_set_pause(struct bnxt *); int bnxt_hwrm_set_link_setting(struct bnxt *, bool); int bnxt_open_nic(struct bnxt *, bool, bool); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index 95b93162a1de..44673e64c652 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -427,11 +427,12 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs) } mutex_unlock(&bp->hwrm_cmd_lock); if (!rc) { - pf->max_pf_tx_rings = bp->tx_nr_rings; - if (bp->flags & BNXT_FLAG_AGG_RINGS) - pf->max_pf_rx_rings = bp->rx_nr_rings * 2; - else - pf->max_pf_rx_rings = bp->rx_nr_rings; + pf->max_tx_rings -= vf_tx_rings * num_vfs; + pf->max_rx_rings -= vf_rx_rings * num_vfs; + pf->max_cp_rings -= vf_cp_rings * num_vfs; + pf->max_rsscos_ctxs -= num_vfs; + pf->max_stat_ctxs -= vf_stat_ctx * num_vfs; + pf->max_vnics -= vf_vnics * num_vfs; } return rc; } @@ -535,8 +536,8 @@ void bnxt_sriov_disable(struct bnxt *bp) bnxt_free_vf_resources(bp); bp->pf.active_vfs = 0; - bp->pf.max_pf_rx_rings = bp->pf.max_rx_rings; - bp->pf.max_pf_tx_rings = bp->pf.max_tx_rings; + /* Reclaim all resources for the PF. */ + bnxt_hwrm_func_qcaps(bp); } int bnxt_sriov_configure(struct pci_dev *pdev, int num_vfs) -- cgit v1.2.3-59-g8ed1b From b72d4a68c443e29cb59e15a1a9b2c2f4bf802831 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sun, 27 Dec 2015 18:19:27 -0500 Subject: bnxt_en: Keep track of the ring group resource. Newer firmware will return the ring group resource when we call hwrm_func_qcaps(). To be compatible with older firmware, use the number of tx rings as the number of ring groups if the older firmware returns 0. When determining how many rx rings we can support, take the ring group resource in account as well in _bnxt_get_max_rings(). Divide and assign the ring groups to VFs. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 11 ++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 ++ drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c | 7 ++++++- 3 files changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 22c26448fcaf..5737e0d6e7fb 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -3617,6 +3617,9 @@ int bnxt_hwrm_func_qcaps(struct bnxt *bp) pf->max_cp_rings = le16_to_cpu(resp->max_cmpl_rings); pf->max_tx_rings = le16_to_cpu(resp->max_tx_rings); pf->max_rx_rings = le16_to_cpu(resp->max_rx_rings); + pf->max_hw_ring_grps = le32_to_cpu(resp->max_hw_ring_grps); + if (!pf->max_hw_ring_grps) + pf->max_hw_ring_grps = pf->max_tx_rings; pf->max_l2_ctxs = le16_to_cpu(resp->max_l2_ctxs); pf->max_vnics = le16_to_cpu(resp->max_vnics); pf->max_stat_ctxs = le16_to_cpu(resp->max_stat_ctx); @@ -3644,6 +3647,9 @@ int bnxt_hwrm_func_qcaps(struct bnxt *bp) vf->max_cp_rings = le16_to_cpu(resp->max_cmpl_rings); vf->max_tx_rings = le16_to_cpu(resp->max_tx_rings); vf->max_rx_rings = le16_to_cpu(resp->max_rx_rings); + vf->max_hw_ring_grps = le32_to_cpu(resp->max_hw_ring_grps); + if (!vf->max_hw_ring_grps) + vf->max_hw_ring_grps = vf->max_tx_rings; vf->max_l2_ctxs = le16_to_cpu(resp->max_l2_ctxs); vf->max_vnics = le16_to_cpu(resp->max_vnics); vf->max_stat_ctxs = le16_to_cpu(resp->max_stat_ctx); @@ -5618,25 +5624,28 @@ static int bnxt_get_max_irq(struct pci_dev *pdev) void bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx) { - int max_rings = 0; + int max_rings = 0, max_ring_grps = 0; if (BNXT_PF(bp)) { *max_tx = bp->pf.max_tx_rings; *max_rx = bp->pf.max_rx_rings; max_rings = min_t(int, bp->pf.max_irqs, bp->pf.max_cp_rings); max_rings = min_t(int, max_rings, bp->pf.max_stat_ctxs); + max_ring_grps = bp->pf.max_hw_ring_grps; } else { #ifdef CONFIG_BNXT_SRIOV *max_tx = bp->vf.max_tx_rings; *max_rx = bp->vf.max_rx_rings; max_rings = min_t(int, bp->vf.max_irqs, bp->vf.max_cp_rings); max_rings = min_t(int, max_rings, bp->vf.max_stat_ctxs); + max_ring_grps = bp->vf.max_hw_ring_grps; #endif } if (bp->flags & BNXT_FLAG_AGG_RINGS) *max_rx >>= 1; *max_rx = min_t(int, *max_rx, max_rings); + *max_rx = min_t(int, *max_rx, max_ring_grps); *max_tx = min_t(int, *max_tx, max_rings); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 8abaecefefd9..82414378e429 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -695,6 +695,7 @@ struct bnxt_vf_info { u16 max_cp_rings; u16 max_tx_rings; u16 max_rx_rings; + u16 max_hw_ring_grps; u16 max_l2_ctxs; u16 max_irqs; u16 max_vnics; @@ -723,6 +724,7 @@ struct bnxt_pf_info { u16 max_cp_rings; u16 max_tx_rings; /* HW assigned max tx rings for this PF */ u16 max_rx_rings; /* HW assigned max rx rings for this PF */ + u16 max_hw_ring_grps; u16 max_irqs; u16 max_l2_ctxs; u16 max_vnics; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index 44673e64c652..79b48e490de8 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -367,6 +367,7 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs) { u32 rc = 0, mtu, i; u16 vf_tx_rings, vf_rx_rings, vf_cp_rings, vf_stat_ctx, vf_vnics; + u16 vf_ring_grps; struct hwrm_func_cfg_input req = {0}; struct bnxt_pf_info *pf = &bp->pf; @@ -388,6 +389,7 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs) num_vfs; else vf_rx_rings = (pf->max_rx_rings - bp->rx_nr_rings) / num_vfs; + vf_ring_grps = (bp->pf.max_hw_ring_grps - bp->rx_nr_rings) / num_vfs; vf_tx_rings = (pf->max_tx_rings - bp->tx_nr_rings) / num_vfs; req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_MTU | @@ -398,7 +400,8 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs) FUNC_CFG_REQ_ENABLES_NUM_TX_RINGS | FUNC_CFG_REQ_ENABLES_NUM_RX_RINGS | FUNC_CFG_REQ_ENABLES_NUM_L2_CTXS | - FUNC_CFG_REQ_ENABLES_NUM_VNICS); + FUNC_CFG_REQ_ENABLES_NUM_VNICS | + FUNC_CFG_REQ_ENABLES_NUM_HW_RING_GRPS); mtu = bp->dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; req.mru = cpu_to_le16(mtu); @@ -408,6 +411,7 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs) req.num_cmpl_rings = cpu_to_le16(vf_cp_rings); req.num_tx_rings = cpu_to_le16(vf_tx_rings); req.num_rx_rings = cpu_to_le16(vf_rx_rings); + req.num_hw_ring_grps = cpu_to_le16(vf_ring_grps); req.num_l2_ctxs = cpu_to_le16(4); vf_vnics = 1; @@ -429,6 +433,7 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs) if (!rc) { pf->max_tx_rings -= vf_tx_rings * num_vfs; pf->max_rx_rings -= vf_rx_rings * num_vfs; + pf->max_hw_ring_grps -= vf_ring_grps * num_vfs; pf->max_cp_rings -= vf_cp_rings * num_vfs; pf->max_rsscos_ctxs -= num_vfs; pf->max_stat_ctxs -= vf_stat_ctx * num_vfs; -- cgit v1.2.3-59-g8ed1b From c193554ecd050e63753aa0ec99c188800843bca2 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sun, 27 Dec 2015 18:19:28 -0500 Subject: bnxt_en: Update to Firmware interface spec 1.0.0. This interface will be forward compatible with future changes. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 33 +- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 8 +- drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h | 865 +++++++++--------------- drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c | 15 +- 4 files changed, 349 insertions(+), 572 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 5737e0d6e7fb..52dfaab0ff4a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -2787,7 +2787,7 @@ static int bnxt_hwrm_cfa_l2_set_rx_mask(struct bnxt *bp, u16 vnic_id) struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id]; bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_L2_SET_RX_MASK, -1, -1); - req.dflt_vnic_id = cpu_to_le32(vnic->fw_vnic_id); + req.vnic_id = cpu_to_le32(vnic->fw_vnic_id); req.num_mc_entries = cpu_to_le32(vnic->mc_list_count); req.mc_tbl_addr = cpu_to_le64(vnic->mc_list_mapping); @@ -2820,7 +2820,7 @@ static int bnxt_hwrm_cfa_ntuple_filter_free(struct bnxt *bp, CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT_MASK | \ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT | \ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK | \ - CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID) + CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_ID) static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp, struct bnxt_ntuple_filter *fltr) @@ -2839,7 +2839,7 @@ static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp, req.ethertype = htons(ETH_P_IP); memcpy(req.src_macaddr, fltr->src_mac_addr, ETH_ALEN); - req.ipaddr_type = 4; + req.ip_addr_type = CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4; req.ip_protocol = keys->basic.ip_proto; req.src_ipaddr[0] = keys->addrs.v4addrs.src; @@ -2852,7 +2852,7 @@ static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp, req.dst_port = keys->ports.dst; req.dst_port_mask = cpu_to_be16(0xffff); - req.dst_vnic_id = cpu_to_le16(vnic->fw_vnic_id); + req.dst_id = cpu_to_le16(vnic->fw_vnic_id); mutex_lock(&bp->hwrm_cmd_lock); rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); if (!rc) @@ -2872,10 +2872,10 @@ static int bnxt_hwrm_set_vnic_filter(struct bnxt *bp, u16 vnic_id, u16 idx, bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_L2_FILTER_ALLOC, -1, -1); req.flags = cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX | CFA_L2_FILTER_ALLOC_REQ_FLAGS_OUTERMOST); - req.dst_vnic_id = cpu_to_le16(bp->vnic_info[vnic_id].fw_vnic_id); + req.dst_id = cpu_to_le16(bp->vnic_info[vnic_id].fw_vnic_id); req.enables = cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR | - CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID | + CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_ID | CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR_MASK); memcpy(req.l2_addr, mac_addr, ETH_ALEN); req.l2_addr_mask[0] = 0xff; @@ -2945,7 +2945,8 @@ static int bnxt_hwrm_vnic_set_tpa(struct bnxt *bp, u16 vnic_id, u32 tpa_flags) req.enables = cpu_to_le32(VNIC_TPA_CFG_REQ_ENABLES_MAX_AGG_SEGS | - VNIC_TPA_CFG_REQ_ENABLES_MAX_AGGS); + VNIC_TPA_CFG_REQ_ENABLES_MAX_AGGS | + VNIC_TPA_CFG_REQ_ENABLES_MIN_AGG_LEN); /* Number of segs are log2 units, and first packet is not * included as part of this units. @@ -2963,6 +2964,8 @@ static int bnxt_hwrm_vnic_set_tpa(struct bnxt *bp, u16 vnic_id, u32 tpa_flags) segs = ilog2(nsegs); req.max_agg_segs = cpu_to_le16(segs); req.max_aggs = cpu_to_le16(VNIC_TPA_CFG_REQ_MAX_AGGS_MAX); + + req.min_agg_len = cpu_to_le32(512); } req.vnic_id = cpu_to_le16(vnic->fw_vnic_id); @@ -3726,14 +3729,11 @@ static int bnxt_hwrm_ver_get(struct bnxt *bp) memcpy(&bp->ver_resp, resp, sizeof(struct hwrm_ver_get_output)); - if (req.hwrm_intf_maj != resp->hwrm_intf_maj || - req.hwrm_intf_min != resp->hwrm_intf_min || - req.hwrm_intf_upd != resp->hwrm_intf_upd) { - netdev_warn(bp->dev, "HWRM interface %d.%d.%d does not match driver interface %d.%d.%d.\n", + if (resp->hwrm_intf_maj < 1) { + netdev_warn(bp->dev, "HWRM interface %d.%d.%d is older than 1.0.0.\n", resp->hwrm_intf_maj, resp->hwrm_intf_min, - resp->hwrm_intf_upd, req.hwrm_intf_maj, - req.hwrm_intf_min, req.hwrm_intf_upd); - netdev_warn(bp->dev, "Please update driver or firmware with matching interface versions.\n"); + resp->hwrm_intf_upd); + netdev_warn(bp->dev, "Please update firmware with HWRM interface 1.0.0 or newer.\n"); } snprintf(bp->fw_ver_str, BC_HWRM_STR_LEN, "bc %d.%d.%d rm %d.%d.%d", resp->hwrm_fw_maj, resp->hwrm_fw_min, resp->hwrm_fw_bld, @@ -3936,8 +3936,7 @@ static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) } bp->vnic_info[0].uc_filter_count = 1; - bp->vnic_info[0].rx_mask = CFA_L2_SET_RX_MASK_REQ_MASK_UNICAST | - CFA_L2_SET_RX_MASK_REQ_MASK_BCAST; + bp->vnic_info[0].rx_mask = CFA_L2_SET_RX_MASK_REQ_MASK_BCAST; if ((bp->dev->flags & IFF_PROMISC) && BNXT_PF(bp)) bp->vnic_info[0].rx_mask |= @@ -4343,7 +4342,7 @@ static int bnxt_update_link(struct bnxt *bp, bool chng_link_state) link_info->auto_mode = resp->auto_mode; link_info->auto_pause_setting = resp->auto_pause; link_info->force_pause_setting = resp->force_pause; - link_info->duplex_setting = resp->duplex_setting; + link_info->duplex_setting = resp->duplex; if (link_info->phy_link_status == BNXT_LINK_LINK) link_info->link_speed = le16_to_cpu(resp->link_speed); else diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 82414378e429..78d639d259d1 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -11,11 +11,11 @@ #define BNXT_H #define DRV_MODULE_NAME "bnxt_en" -#define DRV_MODULE_VERSION "0.1.24" +#define DRV_MODULE_VERSION "1.0.0" -#define DRV_VER_MAJ 0 -#define DRV_VER_MIN 1 -#define DRV_VER_UPD 24 +#define DRV_VER_MAJ 1 +#define DRV_VER_MIN 0 +#define DRV_VER_UPD 0 struct tx_bd { __le32 tx_bd_len_flags_type; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index 70fc8253c07f..4badbedcb421 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -103,19 +103,22 @@ struct hwrm_async_event_cmpl { #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CHANGE (0x2UL << 0) #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_DCB_CONFIG_CHANGE (0x3UL << 0) #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED (0x4UL << 0) + #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_NOT_ALLOWED (0x5UL << 0) #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_UNLOAD (0x10UL << 0) #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_LOAD (0x11UL << 0) #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD (0x20UL << 0) - #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_LOAD (0x20UL << 0) + #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_LOAD (0x21UL << 0) #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_FLR (0x30UL << 0) #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_MAC_ADDR_CHANGE (0x31UL << 0) + #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_VF_COMM_STATUS_CHANGE (0x32UL << 0) #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR (0xffUL << 0) __le32 event_data2; u8 opaque_v; #define HWRM_ASYNC_EVENT_CMPL_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; }; @@ -132,9 +135,16 @@ struct hwrm_async_event_cmpl_link_status_change { #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; - #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_UP 0x1UL + #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE 0x1UL + #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_DOWN (0x0UL << 0) + #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_UP (0x1UL << 0) + #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_MASK 0xeUL + #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_SFT 1 + #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffff0UL + #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_ID_SFT 4 }; /* HWRM Asynchronous Event Completion Record for link MTU change (16 bytes) */ @@ -150,7 +160,8 @@ struct hwrm_async_event_cmpl_link_mtu_change { #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_MASK 0xffffUL #define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_SFT 0 @@ -169,7 +180,8 @@ struct hwrm_async_event_cmpl_link_speed_change { #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_FORCE 0x1UL #define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_MASK 0xfffeUL @@ -200,7 +212,8 @@ struct hwrm_async_event_cmpl_dcb_config_change { #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffffUL #define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_SFT 0 @@ -219,7 +232,8 @@ struct hwrm_async_event_cmpl_port_conn_not_allowed { #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_MASK 0xffffUL #define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_SFT 0 @@ -238,7 +252,8 @@ struct hwrm_async_event_cmpl_func_drvr_unload { #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0 @@ -257,7 +272,8 @@ struct hwrm_async_event_cmpl_func_drvr_load { #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL #define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0 @@ -276,10 +292,13 @@ struct hwrm_async_event_cmpl_pf_drvr_unload { #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0 + #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_MASK 0x70000UL + #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_SFT 16 }; /* HWRM Asynchronous Event Completion Record for PF Driver load (16 bytes) */ @@ -289,16 +308,19 @@ struct hwrm_async_event_cmpl_pf_drvr_load { #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_SFT 0 #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0) __le16 event_id; - #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_ID_PF_DRVR_LOAD (0x20UL << 0) + #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_ID_PF_DRVR_LOAD (0x21UL << 0) __le32 event_data2; u8 opaque_v; #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0 + #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_PORT_MASK 0x70000UL + #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_PORT_SFT 16 }; /* HWRM Asynchronous Event Completion Record for VF FLR (16 bytes) */ @@ -314,7 +336,8 @@ struct hwrm_async_event_cmpl_vf_flr { #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_MASK 0xffffUL #define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_SFT 0 @@ -333,7 +356,8 @@ struct hwrm_async_event_cmpl_vf_mac_addr_change { #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_MASK 0xffffUL #define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_SFT 0 @@ -357,18 +381,20 @@ struct hwrm_async_event_cmpl_hwrm_error { #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_V 0x1UL #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_MASK 0xfeUL #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_SFT 1 - u8 unused_1[3]; + u8 timestamp_lo; + __le16 timestamp_hi; __le32 event_data1; #define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA1_TIMESTAMP 0x1UL }; -/* HW Resource Manager Specification 0.7.8 */ -#define HWRM_VERSION_MAJOR 0 -#define HWRM_VERSION_MINOR 7 -#define HWRM_VERSION_UPDATE 8 +/* HW Resource Manager Specification 1.0.0 */ +#define HWRM_VERSION_MAJOR 1 +#define HWRM_VERSION_MINOR 0 +#define HWRM_VERSION_UPDATE 0 -#define HWRM_VERSION_STR "0.7.8" -/* Following is the signature for HWRM message field that indicates not +#define HWRM_VERSION_STR "1.0.0" +/* + * Following is the signature for HWRM message field that indicates not * applicable (All F's). Need to cast it the size of the field if needed. */ #define HWRM_NA_SIGNATURE ((__le32)(-1)) @@ -398,7 +424,9 @@ struct output { struct cmd_nums { __le16 req_type; #define HWRM_VER_GET (0x0UL) - #define HWRM_FUNC_DISABLE (0x10UL) + #define HWRM_FUNC_BUF_UNRGTR (0xeUL) + #define HWRM_FUNC_VF_CFG (0xfUL) + #define RESERVED1 (0x10UL) #define HWRM_FUNC_RESET (0x11UL) #define HWRM_FUNC_GETFID (0x12UL) #define HWRM_FUNC_VF_ALLOC (0x13UL) @@ -414,10 +442,9 @@ struct cmd_nums { #define HWRM_FUNC_DRV_RGTR (0x1dUL) #define HWRM_FUNC_DRV_QVER (0x1eUL) #define HWRM_FUNC_BUF_RGTR (0x1fUL) - #define HWRM_FUNC_VF_CFG (0x20UL) #define HWRM_PORT_PHY_CFG (0x20UL) #define HWRM_PORT_MAC_CFG (0x21UL) - #define HWRM_PORT_ENABLE (0x22UL) + #define RESERVED2 (0x22UL) #define HWRM_PORT_QSTATS (0x23UL) #define HWRM_PORT_LPBK_QSTATS (0x24UL) #define HWRM_PORT_CLR_STATS (0x25UL) @@ -455,13 +482,11 @@ struct cmd_nums { #define HWRM_RING_GRP_FREE (0x61UL) #define HWRM_VNIC_RSS_COS_LB_CTX_ALLOC (0x70UL) #define HWRM_VNIC_RSS_COS_LB_CTX_FREE (0x71UL) - #define HWRM_ARB_GRP_ALLOC (0x80UL) - #define HWRM_ARB_GRP_CFG (0x81UL) #define HWRM_CFA_L2_FILTER_ALLOC (0x90UL) #define HWRM_CFA_L2_FILTER_FREE (0x91UL) #define HWRM_CFA_L2_FILTER_CFG (0x92UL) #define HWRM_CFA_L2_SET_RX_MASK (0x93UL) - #define HWRM_CFA_L2_SET_BCASTMCAST_MIRRORING (0x94UL) + #define RESERVED3 (0x94UL) #define HWRM_CFA_TUNNEL_FILTER_ALLOC (0x95UL) #define HWRM_CFA_TUNNEL_FILTER_FREE (0x96UL) #define HWRM_CFA_ENCAP_RECORD_ALLOC (0x97UL) @@ -469,6 +494,9 @@ struct cmd_nums { #define HWRM_CFA_NTUPLE_FILTER_ALLOC (0x99UL) #define HWRM_CFA_NTUPLE_FILTER_FREE (0x9aUL) #define HWRM_CFA_NTUPLE_FILTER_CFG (0x9bUL) + #define HWRM_CFA_EM_FLOW_ALLOC (0x9cUL) + #define HWRM_CFA_EM_FLOW_FREE (0x9dUL) + #define HWRM_CFA_EM_FLOW_CFG (0x9eUL) #define HWRM_TUNNEL_DST_PORT_QUERY (0xa0UL) #define HWRM_TUNNEL_DST_PORT_ALLOC (0xa1UL) #define HWRM_TUNNEL_DST_PORT_FREE (0xa2UL) @@ -483,8 +511,6 @@ struct cmd_nums { #define HWRM_FWD_RESP (0xd2UL) #define HWRM_FWD_ASYNC_EVENT_CMPL (0xd3UL) #define HWRM_TEMP_MONITOR_QUERY (0xe0UL) - #define HWRM_MGMT_L2_FILTER_ALLOC (0x100UL) - #define HWRM_MGMT_L2_FILTER_FREE (0x101UL) #define HWRM_DBG_READ_DIRECT (0xff10UL) #define HWRM_DBG_READ_INDIRECT (0xff11UL) #define HWRM_DBG_WRITE_DIRECT (0xff12UL) @@ -505,7 +531,6 @@ struct cmd_nums { __le16 unused_0[3]; }; -/* Return Codes (8 bytes) */ struct ret_codes { __le16 error_code; #define HWRM_ERR_CODE_SUCCESS (0x0UL) @@ -529,7 +554,7 @@ struct hwrm_err_output { __le16 resp_len; __le32 opaque_0; __le16 opaque_1; - u8 opaque_2; + u8 cmd_err; u8 valid; }; @@ -686,65 +711,38 @@ struct hwrm_ver_get_output { u8 hwrm_fw_min; u8 hwrm_fw_bld; u8 hwrm_fw_rsvd; - u8 ape_fw_maj; - u8 ape_fw_min; - u8 ape_fw_bld; - u8 ape_fw_rsvd; - u8 kong_fw_maj; - u8 kong_fw_min; - u8 kong_fw_bld; - u8 kong_fw_rsvd; - u8 tang_fw_maj; - u8 tang_fw_min; - u8 tang_fw_bld; - u8 tang_fw_rsvd; - u8 bono_fw_maj; - u8 bono_fw_min; - u8 bono_fw_bld; - u8 bono_fw_rsvd; + u8 mgmt_fw_maj; + u8 mgmt_fw_min; + u8 mgmt_fw_bld; + u8 mgmt_fw_rsvd; + u8 netctrl_fw_maj; + u8 netctrl_fw_min; + u8 netctrl_fw_bld; + u8 netctrl_fw_rsvd; + __le32 reserved1; + u8 roce_fw_maj; + u8 roce_fw_min; + u8 roce_fw_bld; + u8 roce_fw_rsvd; char hwrm_fw_name[16]; - char ape_fw_name[16]; - char kong_fw_name[16]; - char tang_fw_name[16]; - char bono_fw_name[16]; + char mgmt_fw_name[16]; + char netctrl_fw_name[16]; + __le32 reserved2[4]; + char roce_fw_name[16]; __le16 chip_num; u8 chip_rev; u8 chip_metal; u8 chip_bond_id; - u8 unused_0; + u8 chip_platform_type; + #define VER_GET_RESP_CHIP_PLATFORM_TYPE_ASIC (0x0UL << 0) + #define VER_GET_RESP_CHIP_PLATFORM_TYPE_FPGA (0x1UL << 0) + #define VER_GET_RESP_CHIP_PLATFORM_TYPE_PALLADIUM (0x2UL << 0) __le16 max_req_win_len; __le16 max_resp_len; __le16 def_req_timeout; + u8 unused_0; u8 unused_1; u8 unused_2; - u8 unused_3; - u8 valid; -}; - -/* hwrm_func_disable */ -/* Input (24 bytes) */ -struct hwrm_func_disable_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - __le32 enables; - #define FUNC_DISABLE_REQ_ENABLES_VF_ID_VALID 0x1UL - __le16 vf_id; - __le16 unused_0; -}; - -/* Output (16 bytes) */ -struct hwrm_func_disable_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - __le32 unused_0; - u8 unused_1; - u8 unused_2; - u8 unused_3; u8 valid; }; @@ -759,7 +757,12 @@ struct hwrm_func_reset_input { __le32 enables; #define FUNC_RESET_REQ_ENABLES_VF_ID_VALID 0x1UL __le16 vf_id; - __le16 unused_0; + u8 func_reset_level; + #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETALL (0x0UL << 0) + #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETME (0x1UL << 0) + #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETCHILDREN (0x2UL << 0) + #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETVF (0x3UL << 0) + u8 unused_0; }; /* Output (16 bytes) */ @@ -861,7 +864,7 @@ struct hwrm_func_vf_free_output { }; /* hwrm_func_vf_cfg */ -/* Input (24 bytes) */ +/* Input (32 bytes) */ struct hwrm_func_vf_cfg_input { __le16 req_type; __le16 cmpl_ring; @@ -871,8 +874,11 @@ struct hwrm_func_vf_cfg_input { __le32 enables; #define FUNC_VF_CFG_REQ_ENABLES_MTU 0x1UL #define FUNC_VF_CFG_REQ_ENABLES_GUEST_VLAN 0x2UL + #define FUNC_VF_CFG_REQ_ENABLES_ASYNC_EVENT_CR 0x4UL __le16 mtu; __le16 guest_vlan; + __le16 async_event_cr; + __le16 unused_0[3]; }; /* Output (16 bytes) */ @@ -944,7 +950,7 @@ struct hwrm_func_cfg_input { __le16 seq_id; __le16 target_id; __le64 resp_addr; - __le16 vf_id; + __le16 fid; u8 unused_0; u8 unused_1; __le32 flags; @@ -1000,10 +1006,6 @@ struct hwrm_func_cfg_input { #define FUNC_CFG_REQ_VLAN_ANTISPOOF_MODE_INSERT_IF_VLANDNE (0x2UL << 0) #define FUNC_CFG_REQ_VLAN_ANTISPOOF_MODE_INSERT_OR_OVERRIDE_VLAN (0x3UL << 0) u8 allowed_vlan_pris; - #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_NOCHECK (0x0UL << 0) - #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_VALIDATE_VLAN (0x1UL << 0) - #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_INSERT_IF_VLANDNE (0x2UL << 0) - #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_INSERT_OR_OVERRIDE_VLAN (0x3UL << 0) u8 evb_mode; #define FUNC_CFG_REQ_EVB_MODE_NO_EVB (0x0UL << 0) #define FUNC_CFG_REQ_EVB_MODE_VEB (0x1UL << 0) @@ -1166,6 +1168,15 @@ struct hwrm_func_drv_rgtr_input { #define FUNC_DRV_RGTR_REQ_ENABLES_VF_REQ_FWD 0x8UL #define FUNC_DRV_RGTR_REQ_ENABLES_ASYNC_EVENT_FWD 0x10UL __le16 os_type; + #define FUNC_DRV_RGTR_REQ_OS_TYPE_UNKNOWN (0x0UL << 0) + #define FUNC_DRV_RGTR_REQ_OS_TYPE_OTHER (0x1UL << 0) + #define FUNC_DRV_RGTR_REQ_OS_TYPE_MSDOS (0xeUL << 0) + #define FUNC_DRV_RGTR_REQ_OS_TYPE_SOLARIS (0x1dUL << 0) + #define FUNC_DRV_RGTR_REQ_OS_TYPE_LINUX (0x24UL << 0) + #define FUNC_DRV_RGTR_REQ_OS_TYPE_FREEBSD (0x2aUL << 0) + #define FUNC_DRV_RGTR_REQ_OS_TYPE_ESXI (0x68UL << 0) + #define FUNC_DRV_RGTR_REQ_OS_TYPE_WIN864 (0x73UL << 0) + #define FUNC_DRV_RGTR_REQ_OS_TYPE_WIN2012R2 (0x74UL << 0) u8 ver_maj; u8 ver_min; u8 ver_upd; @@ -1276,9 +1287,7 @@ struct hwrm_func_drv_qver_input { __le16 seq_id; __le16 target_id; __le64 resp_addr; - __le32 enables; - #define FUNC_DRV_QVER_REQ_ENABLES_OS_TYPE_VALID 0x1UL - #define FUNC_DRV_QVER_REQ_ENABLES_VER_VALID 0x2UL + __le32 reserved; __le16 fid; __le16 unused_0; }; @@ -1290,6 +1299,15 @@ struct hwrm_func_drv_qver_output { __le16 seq_id; __le16 resp_len; __le16 os_type; + #define FUNC_DRV_QVER_RESP_OS_TYPE_UNKNOWN (0x0UL << 0) + #define FUNC_DRV_QVER_RESP_OS_TYPE_OTHER (0x1UL << 0) + #define FUNC_DRV_QVER_RESP_OS_TYPE_MSDOS (0xeUL << 0) + #define FUNC_DRV_QVER_RESP_OS_TYPE_SOLARIS (0x1dUL << 0) + #define FUNC_DRV_QVER_RESP_OS_TYPE_LINUX (0x24UL << 0) + #define FUNC_DRV_QVER_RESP_OS_TYPE_FREEBSD (0x2aUL << 0) + #define FUNC_DRV_QVER_RESP_OS_TYPE_ESXI (0x68UL << 0) + #define FUNC_DRV_QVER_RESP_OS_TYPE_WIN864 (0x73UL << 0) + #define FUNC_DRV_QVER_RESP_OS_TYPE_WIN2012R2 (0x74UL << 0) u8 ver_maj; u8 ver_min; u8 ver_upd; @@ -1498,9 +1516,7 @@ struct hwrm_port_phy_qcfg_output { u8 force_pause; #define PORT_PHY_QCFG_RESP_FORCE_PAUSE_TX 0x1UL #define PORT_PHY_QCFG_RESP_FORCE_PAUSE_RX 0x2UL - u8 duplex_setting; - #define PORT_PHY_QCFG_RESP_DUPLEX_SETTING_HALF (0x0UL << 0) - #define PORT_PHY_QCFG_RESP_DUPLEX_SETTING_FULL (0x1UL << 0) + u8 reserved1; __le32 preemphasis; u8 phy_maj; u8 phy_min; @@ -1601,33 +1617,6 @@ struct hwrm_port_mac_cfg_output { u8 valid; }; -/* hwrm_port_enable */ -/* Input (24 bytes) */ -struct hwrm_port_enable_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - __le32 flags; - #define PORT_ENABLE_REQ_FLAGS_FORWARD_TRAFFIC 0x1UL - __le16 port_id; - __le16 unused_0; -}; - -/* Output (16 bytes) */ -struct hwrm_port_enable_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - __le32 unused_0; - u8 unused_1; - u8 unused_2; - u8 unused_3; - u8 valid; -}; - /* hwrm_port_qstats */ /* Input (40 bytes) */ struct hwrm_port_qstats_input { @@ -1651,10 +1640,11 @@ struct hwrm_port_qstats_output { __le16 req_type; __le16 seq_id; __le16 resp_len; - __le32 unused_0; + __le16 tx_stat_size; + __le16 rx_stat_size; + u8 unused_0; u8 unused_1; u8 unused_2; - u8 unused_3; u8 valid; }; @@ -1668,7 +1658,7 @@ struct hwrm_port_lpbk_qstats_input { __le64 resp_addr; }; -/* Output (64 bytes) */ +/* Output (96 bytes) */ struct hwrm_port_lpbk_qstats_output { __le16 error_code; __le16 req_type; @@ -1680,6 +1670,10 @@ struct hwrm_port_lpbk_qstats_output { __le64 lpbk_ucast_bytes; __le64 lpbk_mcast_bytes; __le64 lpbk_bcast_bytes; + __le64 tx_stat_discard; + __le64 tx_stat_error; + __le64 rx_stat_discard; + __le64 rx_stat_error; __le32 unused_0; u8 unused_1; u8 unused_2; @@ -1884,12 +1878,11 @@ struct hwrm_queue_buffers_cfg_input { __le32 enables; #define QUEUE_BUFFERS_CFG_REQ_ENABLES_RESERVED 0x1UL #define QUEUE_BUFFERS_CFG_REQ_ENABLES_SHARED 0x2UL - #define QUEUE_BUFFERS_CFG_REQ_ENABLES_GROUP 0x4UL - #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XOFF 0x8UL - #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XON 0x10UL - #define QUEUE_BUFFERS_CFG_REQ_ENABLES_FULL 0x20UL - #define QUEUE_BUFFERS_CFG_REQ_ENABLES_NOTFULL 0x40UL - #define QUEUE_BUFFERS_CFG_REQ_ENABLES_MAX 0x80UL + #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XOFF 0x4UL + #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XON 0x8UL + #define QUEUE_BUFFERS_CFG_REQ_ENABLES_FULL 0x10UL + #define QUEUE_BUFFERS_CFG_REQ_ENABLES_NOTFULL 0x20UL + #define QUEUE_BUFFERS_CFG_REQ_ENABLES_MAX 0x40UL __le32 queue_id; __le32 reserved; __le32 shared; @@ -1921,15 +1914,15 @@ struct hwrm_queue_pfcenable_cfg_input { __le16 seq_id; __le16 target_id; __le64 resp_addr; - __le32 enables; - #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI0_PFC_ENABLED 0x1UL - #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI1_PFC_ENABLED 0x2UL - #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI2_PFC_ENABLED 0x4UL - #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI3_PFC_ENABLED 0x8UL - #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI4_PFC_ENABLED 0x10UL - #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI5_PFC_ENABLED 0x20UL - #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI6_PFC_ENABLED 0x40UL - #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI7_PFC_ENABLED 0x80UL + __le32 flags; + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI0_PFC_ENABLED 0x1UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI1_PFC_ENABLED 0x2UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI2_PFC_ENABLED 0x4UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI3_PFC_ENABLED 0x8UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI4_PFC_ENABLED 0x10UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI5_PFC_ENABLED 0x20UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI6_PFC_ENABLED 0x40UL + #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI7_PFC_ENABLED 0x80UL __le16 port_id; __le16 unused_0; }; @@ -1962,14 +1955,14 @@ struct hwrm_queue_pri2cos_cfg_input { #define QUEUE_PRI2COS_CFG_REQ_FLAGS_IVLAN 0x2UL __le32 enables; u8 port_id; - u8 pri0_cos; - u8 pri1_cos; - u8 pri2_cos; - u8 pri3_cos; - u8 pri4_cos; - u8 pri5_cos; - u8 pri6_cos; - u8 pri7_cos; + u8 pri0_cos_queue_id; + u8 pri1_cos_queue_id; + u8 pri2_cos_queue_id; + u8 pri3_cos_queue_id; + u8 pri4_cos_queue_id; + u8 pri5_cos_queue_id; + u8 pri6_cos_queue_id; + u8 pri7_cos_queue_id; u8 unused_0[7]; }; @@ -2164,6 +2157,7 @@ struct hwrm_vnic_cfg_input { __le32 flags; #define VNIC_CFG_REQ_FLAGS_DEFAULT 0x1UL #define VNIC_CFG_REQ_FLAGS_VLAN_STRIP_MODE 0x2UL + #define VNIC_CFG_REQ_FLAGS_BD_STALL_MODE 0x4UL __le32 enables; #define VNIC_CFG_REQ_ENABLES_DFLT_RING_GRP 0x1UL #define VNIC_CFG_REQ_ENABLES_RSS_RULE 0x2UL @@ -2380,18 +2374,16 @@ struct hwrm_ring_alloc_input { __le16 target_id; __le64 resp_addr; __le32 enables; - #define RING_ALLOC_REQ_ENABLES_ARB_GRP_ID_VALID 0x1UL - #define RING_ALLOC_REQ_ENABLES_INPUT_NUM_VALID 0x2UL - #define RING_ALLOC_REQ_ENABLES_WEIGHT_VALID 0x4UL + #define RING_ALLOC_REQ_ENABLES_RESERVED1 0x1UL + #define RING_ALLOC_REQ_ENABLES_RESERVED2 0x2UL + #define RING_ALLOC_REQ_ENABLES_RESERVED3 0x4UL #define RING_ALLOC_REQ_ENABLES_STAT_CTX_ID_VALID 0x8UL - #define RING_ALLOC_REQ_ENABLES_MIN_BW_VALID 0x10UL + #define RING_ALLOC_REQ_ENABLES_RESERVED4 0x10UL #define RING_ALLOC_REQ_ENABLES_MAX_BW_VALID 0x20UL u8 ring_type; #define RING_ALLOC_REQ_RING_TYPE_CMPL (0x0UL << 0) #define RING_ALLOC_REQ_RING_TYPE_TX (0x1UL << 0) #define RING_ALLOC_REQ_RING_TYPE_RX (0x2UL << 0) - #define RING_ALLOC_REQ_RING_TYPE_STATUS (0x3UL << 0) - #define RING_ALLOC_REQ_RING_TYPE_CMD (0x4UL << 0) u8 unused_0; __le16 unused_1; __le64 page_tbl_addr; @@ -2406,17 +2398,17 @@ struct hwrm_ring_alloc_input { __le16 queue_id; u8 unused_4; u8 unused_5; - __le32 arb_grp_id; - __le16 input_number; + __le32 reserved1; + __le16 reserved2; u8 unused_6; u8 unused_7; - __le32 weight; + __le32 reserved3; __le32 stat_ctx_id; - __le32 min_bw; + __le32 reserved4; __le32 max_bw; u8 int_mode; #define RING_ALLOC_REQ_INT_MODE_LEGACY (0x0UL << 0) - #define RING_ALLOC_REQ_INT_MODE_MSI (0x1UL << 0) + #define RING_ALLOC_REQ_INT_MODE_RSVD (0x1UL << 0) #define RING_ALLOC_REQ_INT_MODE_MSIX (0x2UL << 0) #define RING_ALLOC_REQ_INT_MODE_POLL (0x3UL << 0) u8 unused_8[3]; @@ -2448,8 +2440,6 @@ struct hwrm_ring_free_input { #define RING_FREE_REQ_RING_TYPE_CMPL (0x0UL << 0) #define RING_FREE_REQ_RING_TYPE_TX (0x1UL << 0) #define RING_FREE_REQ_RING_TYPE_RX (0x2UL << 0) - #define RING_FREE_REQ_RING_TYPE_STATUS (0x3UL << 0) - #define RING_FREE_REQ_RING_TYPE_CMD (0x4UL << 0) u8 unused_0; __le16 ring_id; __le32 unused_1; @@ -2550,8 +2540,6 @@ struct hwrm_ring_reset_input { #define RING_RESET_REQ_RING_TYPE_CMPL (0x0UL << 0) #define RING_RESET_REQ_RING_TYPE_TX (0x1UL << 0) #define RING_RESET_REQ_RING_TYPE_RX (0x2UL << 0) - #define RING_RESET_REQ_RING_TYPE_STATUS (0x3UL << 0) - #define RING_RESET_REQ_RING_TYPE_CMD (0x4UL << 0) u8 unused_0; __le16 ring_id; __le32 unused_1; @@ -2622,61 +2610,6 @@ struct hwrm_ring_grp_free_output { u8 valid; }; -/* hwrm_arb_grp_alloc */ -/* Input (24 bytes) */ -struct hwrm_arb_grp_alloc_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - __le16 input_number; - __le16 unused_0[3]; -}; - -/* Output (16 bytes) */ -struct hwrm_arb_grp_alloc_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - __le16 arb_grp_id; - u8 unused_0; - u8 unused_1; - u8 unused_2; - u8 unused_3; - u8 unused_4; - u8 valid; -}; - -/* hwrm_arb_grp_cfg */ -/* Input (32 bytes) */ -struct hwrm_arb_grp_cfg_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - __le32 arb_grp_id; - __le16 input_number; - __le16 tx_ring; - __le32 weight; - __le32 unused_0; -}; - -/* Output (16 bytes) */ -struct hwrm_arb_grp_cfg_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - __le32 unused_0; - u8 unused_1; - u8 unused_2; - u8 unused_3; - u8 valid; -}; - /* hwrm_cfa_l2_filter_alloc */ /* Input (96 bytes) */ struct hwrm_cfa_l2_filter_alloc_input { @@ -2708,7 +2641,7 @@ struct hwrm_cfa_l2_filter_alloc_input { #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_SRC_TYPE 0x1000UL #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_SRC_ID 0x2000UL #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE 0x4000UL - #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID 0x8000UL + #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_ID 0x8000UL #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x10000UL u8 l2_addr[6]; u8 unused_0; @@ -2751,7 +2684,7 @@ struct hwrm_cfa_l2_filter_alloc_input { #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0) #define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0) u8 unused_7; - __le16 dst_vnic_id; + __le16 dst_id; __le16 mirror_vnic_id; u8 pri_hint; #define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_NO_PREFER (0x0UL << 0) @@ -2816,10 +2749,11 @@ struct hwrm_cfa_l2_filter_cfg_input { #define CFA_L2_FILTER_CFG_REQ_FLAGS_PATH_RX (0x1UL << 0) #define CFA_L2_FILTER_CFG_REQ_FLAGS_DROP 0x2UL __le32 enables; - #define CFA_L2_FILTER_CFG_REQ_ENABLES_DST_VNIC_ID_VALID 0x1UL + #define CFA_L2_FILTER_CFG_REQ_ENABLES_DST_ID 0x1UL + #define CFA_L2_FILTER_CFG_REQ_ENABLES_NEW_MIRROR_VNIC_ID 0x2UL __le64 l2_filter_id; - __le32 dst_vnic_id; - __le32 unused_0; + __le32 dst_id; + __le32 new_mirror_vnic_id; }; /* Output (16 bytes) */ @@ -2843,9 +2777,9 @@ struct hwrm_cfa_l2_set_rx_mask_input { __le16 seq_id; __le16 target_id; __le64 resp_addr; - __le32 dflt_vnic_id; + __le32 vnic_id; __le32 mask; - #define CFA_L2_SET_RX_MASK_REQ_MASK_UNICAST 0x1UL + #define CFA_L2_SET_RX_MASK_REQ_MASK_RESERVED 0x1UL #define CFA_L2_SET_RX_MASK_REQ_MASK_MCAST 0x2UL #define CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST 0x4UL #define CFA_L2_SET_RX_MASK_REQ_MASK_BCAST 0x8UL @@ -2869,46 +2803,6 @@ struct hwrm_cfa_l2_set_rx_mask_output { u8 valid; }; -/* hwrm_cfa_l2_set_bcastmcast_mirroring */ -/* Input (32 bytes) */ -struct hwrm_cfa_l2_set_bcastmcast_mirroring_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - __le32 dflt_vnic_id; - __le32 mirroring_flags; - #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_BCAST_MIRRORING 0x1UL - #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_MCAST_MIRRORING 0x2UL - #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_BCAST_SRC_KNOCKOUT 0x4UL - #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_MCAST_SRC_KNOCKOUT 0x8UL - #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_VLAN_ID_VALID 0x10UL - __le16 vlan_id; - u8 bcast_domain; - #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_PFONLY (0x0UL << 0) - #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_ALLPFS (0x1UL << 0) - #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_ALLPFSVFS (0x2UL << 0) - u8 mcast_domain; - #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_PFONLY (0x0UL << 0) - #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_ALLPFS (0x1UL << 0) - #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_ALLPFSVFS (0x2UL << 0) - __le32 unused_0; -}; - -/* Output (16 bytes) */ -struct hwrm_cfa_l2_set_bcastmcast_mirroring_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - __le32 unused_0; - u8 unused_1; - u8 unused_2; - u8 unused_3; - u8 valid; -}; - /* hwrm_cfa_tunnel_filter_alloc */ /* Input (88 bytes) */ struct hwrm_cfa_tunnel_filter_alloc_input { @@ -3017,17 +2911,16 @@ struct hwrm_cfa_encap_record_alloc_input { __le32 encap_data[16]; }; -/* Output (24 bytes) */ +/* Output (16 bytes) */ struct hwrm_cfa_encap_record_alloc_output { __le16 error_code; __le16 req_type; __le16 seq_id; __le16 resp_len; - __le64 encap_record_id; - __le32 unused_0; + __le32 encap_record_id; + u8 unused_0; u8 unused_1; u8 unused_2; - u8 unused_3; u8 valid; }; @@ -3039,7 +2932,8 @@ struct hwrm_cfa_encap_record_free_input { __le16 seq_id; __le16 target_id; __le64 resp_addr; - __le64 encap_record_id; + __le32 encap_record_id; + __le32 unused_0; }; /* Output (16 bytes) */ @@ -3083,14 +2977,21 @@ struct hwrm_cfa_ntuple_filter_alloc_input { #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK 0x2000UL #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_PRI_HINT 0x4000UL #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_NTUPLE_FILTER_ID 0x8000UL - #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID 0x10000UL + #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_ID 0x10000UL #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x20000UL + #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_MACADDR 0x40000UL __le64 l2_filter_id; u8 src_macaddr[6]; __be16 ethertype; - u8 ipaddr_type; + u8 ip_addr_type; + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_UNKNOWN (0x0UL << 0) + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4 (0x4UL << 0) + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6 (0x6UL << 0) u8 ip_protocol; - __le16 dst_vnic_id; + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UNKNOWN (0x0UL << 0) + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP (0x6UL << 0) + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_TCP (0x11UL << 0) + __le16 dst_id; __le16 mirror_vnic_id; u8 tunnel_type; #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0) @@ -3104,6 +3005,11 @@ struct hwrm_cfa_ntuple_filter_alloc_input { #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0) #define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0) u8 pri_hint; + #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_NO_PREFER (0x0UL << 0) + #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_ABOVE (0x1UL << 0) + #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_BELOW (0x2UL << 0) + #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_HIGHEST (0x3UL << 0) + #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_LOWEST (0x4UL << 0) __be32 src_ipaddr[4]; __be32 src_ipaddr_mask[4]; __be32 dst_ipaddr[4]; @@ -3162,11 +3068,11 @@ struct hwrm_cfa_ntuple_filter_cfg_input { __le16 target_id; __le64 resp_addr; __le32 enables; - #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_DST_VNIC_ID_VALID 0x1UL - #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_MIRROR_VNIC_ID_VALID 0x2UL + #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_DST_ID 0x1UL + #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_MIRROR_VNIC_ID 0x2UL __le32 unused_0; __le64 ntuple_filter_id; - __le32 new_dst_vnic_id; + __le32 new_dst_id; __le32 new_mirror_vnic_id; }; @@ -3192,16 +3098,8 @@ struct hwrm_tunnel_dst_port_query_input { __le16 target_id; __le64 resp_addr; u8 tunnel_type; - #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0) #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN (0x1UL << 0) - #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_NVGRE (0x2UL << 0) - #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_L2GRE (0x3UL << 0) - #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_IPIP (0x4UL << 0) #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_GENEVE (0x5UL << 0) - #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_MPLS (0x6UL << 0) - #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_STT (0x7UL << 0) - #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0) - #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0) u8 unused_0[7]; }; @@ -3228,16 +3126,8 @@ struct hwrm_tunnel_dst_port_alloc_input { __le16 target_id; __le64 resp_addr; u8 tunnel_type; - #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0) #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN (0x1UL << 0) - #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_NVGRE (0x2UL << 0) - #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_L2GRE (0x3UL << 0) - #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_IPIP (0x4UL << 0) #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_GENEVE (0x5UL << 0) - #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_MPLS (0x6UL << 0) - #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_STT (0x7UL << 0) - #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0) - #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0) u8 unused_0; __be16 tunnel_dst_port_val; __le32 unused_1; @@ -3267,16 +3157,8 @@ struct hwrm_tunnel_dst_port_free_input { __le16 target_id; __le64 resp_addr; u8 tunnel_type; - #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0) #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN (0x1UL << 0) - #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_NVGRE (0x2UL << 0) - #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_L2GRE (0x3UL << 0) - #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_IPIP (0x4UL << 0) #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE (0x5UL << 0) - #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_MPLS (0x6UL << 0) - #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_STT (0x7UL << 0) - #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0) - #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0) u8 unused_0; __le16 tunnel_dst_port_id; __le32 unused_1; @@ -3416,68 +3298,145 @@ struct hwrm_stat_ctx_clr_stats_output { u8 valid; }; -/* hwrm_mgmt_l2_filter_alloc */ -/* Input (56 bytes) */ -struct hwrm_mgmt_l2_filter_alloc_input { +/* hwrm_fw_reset */ +/* Input (24 bytes) */ +struct hwrm_fw_reset_input { __le16 req_type; __le16 cmpl_ring; __le16 seq_id; __le16 target_id; __le64 resp_addr; - __le32 flags; - #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH 0x1UL - #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH_TX (0x0UL << 0) - #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX (0x1UL << 0) - __le32 enables; - #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDRESS 0x1UL - #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_OVLAN 0x2UL - #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_IVLAN 0x4UL - #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_ACTION_ID 0x8UL - u8 l2_address[6]; + u8 embedded_proc_type; + #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT (0x0UL << 0) + #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT (0x1UL << 0) + #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL (0x2UL << 0) + #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE (0x3UL << 0) + #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_RSVD (0x4UL << 0) + u8 selfrst_status; + #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0) + #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0) + #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0) + __le16 unused_0[3]; +}; + +/* Output (16 bytes) */ +struct hwrm_fw_reset_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 selfrst_status; + #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0) + #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0) + #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0) u8 unused_0; + __le16 unused_1; + u8 unused_2; + u8 unused_3; + u8 unused_4; + u8 valid; +}; + +/* hwrm_exec_fwd_resp */ +/* Input (128 bytes) */ +struct hwrm_exec_fwd_resp_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 encap_request[26]; + __le16 encap_resp_target_id; + __le16 unused_0[3]; +}; + +/* Output (16 bytes) */ +struct hwrm_exec_fwd_resp_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 unused_0; u8 unused_1; - u8 l2_address_mask[6]; - __le16 ovlan; - __le16 ovlan_mask; - __le16 ivlan; - __le16 ivlan_mask; u8 unused_2; u8 unused_3; - __le32 action_id; - u8 action_bypass; - #define MGMT_L2_FILTER_ALLOC_REQ_ACTION_BYPASS 0x1UL - u8 unused_5[3]; + u8 valid; +}; + +/* hwrm_reject_fwd_resp */ +/* Input (128 bytes) */ +struct hwrm_reject_fwd_resp_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 encap_request[26]; + __le16 encap_resp_target_id; + __le16 unused_0[3]; }; /* Output (16 bytes) */ -struct hwrm_mgmt_l2_filter_alloc_output { +struct hwrm_reject_fwd_resp_output { __le16 error_code; __le16 req_type; __le16 seq_id; __le16 resp_len; - __le16 mgmt_l2_filter_id; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + +/* hwrm_fwd_resp */ +/* Input (40 bytes) */ +struct hwrm_fwd_resp_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 encap_resp_target_id; + __le16 encap_resp_cmpl_ring; + __le16 encap_resp_len; u8 unused_0; u8 unused_1; + __le64 encap_resp_addr; + __le32 encap_resp[24]; +}; + +/* Output (16 bytes) */ +struct hwrm_fwd_resp_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 unused_0; + u8 unused_1; u8 unused_2; u8 unused_3; - u8 unused_4; u8 valid; }; -/* hwrm_mgmt_l2_filter_free */ -/* Input (24 bytes) */ -struct hwrm_mgmt_l2_filter_free_input { +/* hwrm_fwd_async_event_cmpl */ +/* Input (32 bytes) */ +struct hwrm_fwd_async_event_cmpl_input { __le16 req_type; __le16 cmpl_ring; __le16 seq_id; __le16 target_id; __le64 resp_addr; - __le16 mgmt_l2_filter_id; - __le16 unused_0[3]; + __le16 encap_async_event_target_id; + u8 unused_0; + u8 unused_1; + u8 unused_2[3]; + u8 unused_3; + __le32 encap_async_event_cmpl[4]; }; /* Output (16 bytes) */ -struct hwrm_mgmt_l2_filter_free_output { +struct hwrm_fwd_async_event_cmpl_output { __le16 error_code; __le16 req_type; __le16 seq_id; @@ -3489,6 +3448,31 @@ struct hwrm_mgmt_l2_filter_free_output { u8 valid; }; +/* hwrm_temp_monitor_query */ +/* Input (16 bytes) */ +struct hwrm_temp_monitor_query_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; +}; + +/* Output (16 bytes) */ +struct hwrm_temp_monitor_query_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 temp; + u8 unused_0; + __le16 unused_1; + u8 unused_2; + u8 unused_3; + u8 unused_4; + u8 valid; +}; + /* hwrm_nvm_raw_write_blk */ /* Input (32 bytes) */ struct hwrm_nvm_raw_write_blk_input { @@ -3621,7 +3605,7 @@ struct hwrm_nvm_get_dir_info_output { }; /* hwrm_nvm_write */ -/* Input (40 bytes) */ +/* Input (48 bytes) */ struct hwrm_nvm_write_input { __le16 req_type; __le16 cmpl_ring; @@ -3637,6 +3621,8 @@ struct hwrm_nvm_write_input { __le16 option; __le16 flags; #define NVM_WRITE_REQ_FLAGS_KEEP_ORIG_ACTIVE_IMG 0x1UL + __le32 dir_item_length; + __le32 unused_0; }; /* Output (16 bytes) */ @@ -3645,10 +3631,9 @@ struct hwrm_nvm_write_output { __le16 req_type; __le16 seq_id; __le16 resp_len; - __le32 unused_0; - u8 unused_1; - u8 unused_2; - u8 unused_3; + __le32 dir_item_length; + __le16 dir_idx; + u8 unused_0; u8 valid; }; @@ -3833,214 +3818,4 @@ struct hwrm_nvm_verify_update_output { u8 valid; }; -/* hwrm_exec_fwd_resp */ -/* Input (120 bytes) */ -struct hwrm_exec_fwd_resp_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - __le32 encap_request[24]; - __le16 encap_resp_target_id; - __le16 unused_0[3]; -}; - -/* Output (16 bytes) */ -struct hwrm_exec_fwd_resp_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - __le32 unused_0; - u8 unused_1; - u8 unused_2; - u8 unused_3; - u8 valid; -}; - -/* hwrm_reject_fwd_resp */ -/* Input (120 bytes) */ -struct hwrm_reject_fwd_resp_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - __le32 encap_request[24]; - __le16 encap_resp_target_id; - __le16 unused_0[3]; -}; - -/* Output (16 bytes) */ -struct hwrm_reject_fwd_resp_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - __le32 unused_0; - u8 unused_1; - u8 unused_2; - u8 unused_3; - u8 valid; -}; - -/* hwrm_fwd_resp */ -/* Input (40 bytes) */ -struct hwrm_fwd_resp_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - __le16 encap_resp_target_id; - __le16 encap_resp_cmpl_ring; - __le16 encap_resp_len; - u8 unused_0; - u8 unused_1; - __le64 encap_resp_addr; - __le32 encap_resp[24]; -}; - -/* Output (16 bytes) */ -struct hwrm_fwd_resp_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - __le32 unused_0; - u8 unused_1; - u8 unused_2; - u8 unused_3; - u8 valid; -}; - -/* hwrm_fwd_async_event_cmpl */ -/* Input (32 bytes) */ -struct hwrm_fwd_async_event_cmpl_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - __le16 encap_async_event_target_id; - u8 unused_0; - u8 unused_1; - u8 unused_2[3]; - u8 unused_3; - __le32 encap_async_event_cmpl[4]; -}; - -/* Output (16 bytes) */ -struct hwrm_fwd_async_event_cmpl_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - __le32 unused_0; - u8 unused_1; - u8 unused_2; - u8 unused_3; - u8 valid; -}; - -/* hwrm_fw_reset */ -/* Input (24 bytes) */ -struct hwrm_fw_reset_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - u8 embedded_proc_type; - #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIMP (0x0UL << 0) - #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_APE (0x1UL << 0) - #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_KONG (0x2UL << 0) - #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_BONO (0x3UL << 0) - #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_TANG (0x4UL << 0) - u8 selfrst_status; - #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0) - #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0) - #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0) - __le16 unused_0[3]; -}; - -/* Output (16 bytes) */ -struct hwrm_fw_reset_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - u8 selfrst_status; - #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0) - #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0) - #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0) - u8 unused_0; - __le16 unused_1; - u8 unused_2; - u8 unused_3; - u8 unused_4; - u8 valid; -}; - -/* hwrm_fw_qstatus */ -/* Input (24 bytes) */ -struct hwrm_fw_qstatus_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; - u8 embedded_proc_type; - #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_CHIMP (0x0UL << 0) - #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_APE (0x1UL << 0) - #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_KONG (0x2UL << 0) - #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_BONO (0x3UL << 0) - #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_TANG (0x4UL << 0) - u8 unused_0[7]; -}; - -/* Output (16 bytes) */ -struct hwrm_fw_qstatus_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - u8 selfrst_status; - #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0) - #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0) - #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0) - u8 unused_0; - __le16 unused_1; - u8 unused_2; - u8 unused_3; - u8 unused_4; - u8 valid; -}; - -/* hwrm_temp_monitor_query */ -/* Input (16 bytes) */ -struct hwrm_temp_monitor_query_input { - __le16 req_type; - __le16 cmpl_ring; - __le16 seq_id; - __le16 target_id; - __le64 resp_addr; -}; - -/* Output (16 bytes) */ -struct hwrm_temp_monitor_query_output { - __le16 error_code; - __le16 req_type; - __le16 seq_id; - __le16 resp_len; - u8 temp; - u8 unused_0; - __le16 unused_1; - u8 unused_2; - u8 unused_3; - u8 unused_4; - u8 valid; -}; - #endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index 79b48e490de8..c1cc83d7e38c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -64,7 +64,7 @@ int bnxt_set_vf_spoofchk(struct net_device *dev, int vf_id, bool setting) * the spoof check should also include vlan anti-spoofing */ bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1); - req.vf_id = cpu_to_le16(vf->fw_fid); + req.fid = cpu_to_le16(vf->fw_fid); req.flags = cpu_to_le32(func_flags); rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); if (!rc) { @@ -128,7 +128,7 @@ int bnxt_set_vf_mac(struct net_device *dev, int vf_id, u8 *mac) memcpy(vf->mac_addr, mac, ETH_ALEN); bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1); - req.vf_id = cpu_to_le16(vf->fw_fid); + req.fid = cpu_to_le16(vf->fw_fid); req.flags = cpu_to_le32(vf->func_flags); req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_DFLT_MAC_ADDR); memcpy(req.dflt_mac_addr, mac, ETH_ALEN); @@ -159,7 +159,7 @@ int bnxt_set_vf_vlan(struct net_device *dev, int vf_id, u16 vlan_id, u8 qos) return 0; bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1); - req.vf_id = cpu_to_le16(vf->fw_fid); + req.fid = cpu_to_le16(vf->fw_fid); req.flags = cpu_to_le32(vf->func_flags); req.dflt_vlan = cpu_to_le16(vlan_tag); req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_DFLT_VLAN); @@ -198,7 +198,7 @@ int bnxt_set_vf_bw(struct net_device *dev, int vf_id, int min_tx_rate, if (min_tx_rate == vf->min_tx_rate && max_tx_rate == vf->max_tx_rate) return 0; bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1); - req.vf_id = cpu_to_le16(vf->fw_fid); + req.fid = cpu_to_le16(vf->fw_fid); req.flags = cpu_to_le32(vf->func_flags); req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_MAX_BW); req.max_bw = cpu_to_le32(max_tx_rate); @@ -421,13 +421,13 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs) mutex_lock(&bp->hwrm_cmd_lock); for (i = 0; i < num_vfs; i++) { - req.vf_id = cpu_to_le16(pf->first_vf_id + i); + req.fid = cpu_to_le16(pf->first_vf_id + i); rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); if (rc) break; pf->active_vfs = i + 1; - pf->vf[i].fw_fid = le16_to_cpu(req.vf_id); + pf->vf[i].fw_fid = le16_to_cpu(req.fid); } mutex_unlock(&bp->hwrm_cmd_lock); if (!rc) { @@ -600,6 +600,7 @@ static int bnxt_hwrm_fwd_resp(struct bnxt *bp, struct bnxt_vf_info *vf, /* Set the new target id */ req.target_id = cpu_to_le16(vf->fw_fid); + req.encap_resp_target_id = cpu_to_le16(vf->fw_fid); req.encap_resp_len = cpu_to_le16(msg_size); req.encap_resp_addr = encap_resp_addr; req.encap_resp_cmpl_ring = encap_resp_cpr; @@ -634,6 +635,7 @@ static int bnxt_hwrm_fwd_err_resp(struct bnxt *bp, struct bnxt_vf_info *vf, bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_REJECT_FWD_RESP, -1, -1); /* Set the new target id */ req.target_id = cpu_to_le16(vf->fw_fid); + req.encap_resp_target_id = cpu_to_le16(vf->fw_fid); memcpy(req.encap_request, vf->hwrm_cmd_req_addr, msg_size); mutex_lock(&bp->hwrm_cmd_lock); @@ -665,6 +667,7 @@ static int bnxt_hwrm_exec_fwd_resp(struct bnxt *bp, struct bnxt_vf_info *vf, bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_EXEC_FWD_RESP, -1, -1); /* Set the new target id */ req.target_id = cpu_to_le16(vf->fw_fid); + req.encap_resp_target_id = cpu_to_le16(vf->fw_fid); memcpy(req.encap_request, vf->hwrm_cmd_req_addr, msg_size); mutex_lock(&bp->hwrm_cmd_lock); -- cgit v1.2.3-59-g8ed1b From fbc9a5237a767cada312cb07877d0992b1e34242 Mon Sep 17 00:00:00 2001 From: David Christensen Date: Sun, 27 Dec 2015 18:19:29 -0500 Subject: bnxt_en: Add BCM57301 & BCM57402 devices. Added the PCI IDs for the BCM57301 and BCM57402 controllers. Signed-off-by: David Christensen Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 52dfaab0ff4a..319653a3e333 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -72,8 +72,10 @@ MODULE_VERSION(DRV_MODULE_VERSION); #define BNXT_TX_PUSH_THRESH 92 enum board_idx { + BCM57301, BCM57302, BCM57304, + BCM57402, BCM57404, BCM57406, BCM57304_VF, @@ -84,17 +86,21 @@ enum board_idx { static const struct { char *name; } board_info[] = { - { "Broadcom BCM57302 NetXtreme-C Single-port 10Gb/25Gb/40Gb/50Gb Ethernet" }, + { "Broadcom BCM57301 NetXtreme-C Single-port 10Gb Ethernet" }, + { "Broadcom BCM57302 NetXtreme-C Dual-port 10Gb/25Gb Ethernet" }, { "Broadcom BCM57304 NetXtreme-C Dual-port 10Gb/25Gb/40Gb/50Gb Ethernet" }, + { "Broadcom BCM57402 NetXtreme-E Dual-port 10Gb Ethernet" }, { "Broadcom BCM57404 NetXtreme-E Dual-port 10Gb/25Gb Ethernet" }, - { "Broadcom BCM57406 NetXtreme-E Dual-port 10Gb Ethernet" }, + { "Broadcom BCM57406 NetXtreme-E Dual-port 10GBase-T Ethernet" }, { "Broadcom BCM57304 NetXtreme-C Ethernet Virtual Function" }, { "Broadcom BCM57404 NetXtreme-E Ethernet Virtual Function" }, }; static const struct pci_device_id bnxt_pci_tbl[] = { + { PCI_VDEVICE(BROADCOM, 0x16c8), .driver_data = BCM57301 }, { PCI_VDEVICE(BROADCOM, 0x16c9), .driver_data = BCM57302 }, { PCI_VDEVICE(BROADCOM, 0x16ca), .driver_data = BCM57304 }, + { PCI_VDEVICE(BROADCOM, 0x16d0), .driver_data = BCM57402 }, { PCI_VDEVICE(BROADCOM, 0x16d1), .driver_data = BCM57404 }, { PCI_VDEVICE(BROADCOM, 0x16d2), .driver_data = BCM57406 }, #ifdef CONFIG_BNXT_SRIOV -- cgit v1.2.3-59-g8ed1b From cab47333f0f75b685bce1facecb73bf3632e1360 Mon Sep 17 00:00:00 2001 From: Shikha Singh Date: Tue, 22 Dec 2015 00:03:30 +0100 Subject: NFC: Add STMicroelectronics ST95HF driver This driver supports STMicroelectronics NFC Transceiver "ST95HF", in in initiator role to read/write ISO14443 Type 4A, ISO14443 Type 4B and ISO15693 Type5 tags. The ST95HF datasheet is available here: http://www.st.com/web/en/resource/technical/document/datasheet/DM00102056.pdf Signed-off-by: Shikha Singh Signed-off-by: Samuel Ortiz --- drivers/nfc/Kconfig | 1 + drivers/nfc/Makefile | 1 + drivers/nfc/st95hf/Kconfig | 10 + drivers/nfc/st95hf/Makefile | 6 + drivers/nfc/st95hf/core.c | 1273 +++++++++++++++++++++++++++++++++++++++++++ drivers/nfc/st95hf/spi.c | 167 ++++++ drivers/nfc/st95hf/spi.h | 64 +++ 7 files changed, 1522 insertions(+) create mode 100644 drivers/nfc/st95hf/Kconfig create mode 100644 drivers/nfc/st95hf/Makefile create mode 100644 drivers/nfc/st95hf/core.c create mode 100644 drivers/nfc/st95hf/spi.c create mode 100644 drivers/nfc/st95hf/spi.h (limited to 'drivers') diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 0d6003dee3af..7437c9dfd8fc 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -76,4 +76,5 @@ source "drivers/nfc/st21nfca/Kconfig" source "drivers/nfc/st-nci/Kconfig" source "drivers/nfc/nxp-nci/Kconfig" source "drivers/nfc/s3fwrn5/Kconfig" +source "drivers/nfc/st95hf/Kconfig" endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index e3621416a48e..0a99e67daa10 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ obj-$(CONFIG_NFC_ST_NCI) += st-nci/ obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/ obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5/ +obj-$(CONFIG_NFC_ST95HF) += st95hf/ diff --git a/drivers/nfc/st95hf/Kconfig b/drivers/nfc/st95hf/Kconfig new file mode 100644 index 000000000000..224f266fdcb6 --- /dev/null +++ b/drivers/nfc/st95hf/Kconfig @@ -0,0 +1,10 @@ +config NFC_ST95HF + tristate "ST95HF NFC Transceiver driver" + depends on SPI && NFC_DIGITAL + help + This enables the ST NFC driver for ST95HF NFC transceiver. + This makes use of SPI framework to communicate with transceiver + and registered with NFC digital core to support Linux NFC framework. + + Say Y here to compile support for ST NFC transceiver ST95HF + linux driver into the kernel or say M to compile it as module. diff --git a/drivers/nfc/st95hf/Makefile b/drivers/nfc/st95hf/Makefile new file mode 100644 index 000000000000..00760b38ab7e --- /dev/null +++ b/drivers/nfc/st95hf/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for STMicroelectronics NFC transceiver ST95HF +# + +obj-$(CONFIG_NFC_ST95HF) += st95hf.o +st95hf-objs := spi.o core.o diff --git a/drivers/nfc/st95hf/core.c b/drivers/nfc/st95hf/core.c new file mode 100644 index 000000000000..454efc6ae3f4 --- /dev/null +++ b/drivers/nfc/st95hf/core.c @@ -0,0 +1,1273 @@ +/* + * -------------------------------------------------------------------- + * Driver for ST NFC Transceiver ST95HF + * -------------------------------------------------------------------- + * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi.h" + +/* supported protocols */ +#define ST95HF_SUPPORTED_PROT (NFC_PROTO_ISO14443_MASK | \ + NFC_PROTO_ISO14443_B_MASK | \ + NFC_PROTO_ISO15693_MASK) +/* driver capabilities */ +#define ST95HF_CAPABILITIES NFC_DIGITAL_DRV_CAPS_IN_CRC + +/* Command Send Interface */ +/* ST95HF_COMMAND_SEND CMD Ids */ +#define ECHO_CMD 0x55 +#define WRITE_REGISTER_CMD 0x9 +#define PROTOCOL_SELECT_CMD 0x2 +#define SEND_RECEIVE_CMD 0x4 + +/* Select protocol codes */ +#define ISO15693_PROTOCOL_CODE 0x1 +#define ISO14443A_PROTOCOL_CODE 0x2 +#define ISO14443B_PROTOCOL_CODE 0x3 + +/* + * head room len is 3 + * 1 byte for control byte + * 1 byte for cmd + * 1 byte for size + */ +#define ST95HF_HEADROOM_LEN 3 + +/* + * tailroom is 1 for ISO14443A + * and 0 for ISO14443B/ISO15693, + * hence the max value 1 should be + * taken. + */ +#define ST95HF_TAILROOM_LEN 1 + +/* Command Response interface */ +#define MAX_RESPONSE_BUFFER_SIZE 280 +#define ECHORESPONSE 0x55 +#define ST95HF_ERR_MASK 0xF +#define ST95HF_TIMEOUT_ERROR 0x87 +#define ST95HF_NFCA_CRC_ERR_MASK 0x20 +#define ST95HF_NFCB_CRC_ERR_MASK 0x01 + +/* ST95HF transmission flag values */ +#define TRFLAG_NFCA_SHORT_FRAME 0x07 +#define TRFLAG_NFCA_STD_FRAME 0x08 +#define TRFLAG_NFCA_STD_FRAME_CRC 0x28 + +/* Misc defs */ +#define HIGH 1 +#define LOW 0 +#define ISO14443A_RATS_REQ 0xE0 +#define RATS_TB1_PRESENT_MASK 0x20 +#define RATS_TA1_PRESENT_MASK 0x10 +#define TB1_FWI_MASK 0xF0 +#define WTX_REQ_FROM_TAG 0xF2 + +#define MAX_CMD_LEN 0x7 + +#define MAX_CMD_PARAMS 4 +struct cmd { + int cmd_len; + unsigned char cmd_id; + unsigned char no_cmd_params; + unsigned char cmd_params[MAX_CMD_PARAMS]; + enum req_type req; +}; + +struct param_list { + int param_offset; + int new_param_val; +}; + +/* + * List of top-level cmds to be used internally by the driver. + * All these commands are build on top of ST95HF basic commands + * such as SEND_RECEIVE_CMD, PROTOCOL_SELECT_CMD, etc. + * These top level cmds are used internally while implementing various ops of + * digital layer/driver probe or extending the digital framework layer for + * features that are not yet implemented there, for example, WTX cmd handling. + */ +enum st95hf_cmd_list { + CMD_ECHO, + CMD_ISO14443A_CONFIG, + CMD_ISO14443A_DEMOGAIN, + CMD_ISO14443B_DEMOGAIN, + CMD_ISO14443A_PROTOCOL_SELECT, + CMD_ISO14443B_PROTOCOL_SELECT, + CMD_WTX_RESPONSE, + CMD_FIELD_OFF, + CMD_ISO15693_PROTOCOL_SELECT, +}; + +static const struct cmd cmd_array[] = { + [CMD_ECHO] = { + .cmd_len = 0x2, + .cmd_id = ECHO_CMD, + .no_cmd_params = 0, + .req = SYNC, + }, + [CMD_ISO14443A_CONFIG] = { + .cmd_len = 0x7, + .cmd_id = WRITE_REGISTER_CMD, + .no_cmd_params = 0x4, + .cmd_params = {0x3A, 0x00, 0x5A, 0x04}, + .req = SYNC, + }, + [CMD_ISO14443A_DEMOGAIN] = { + .cmd_len = 0x7, + .cmd_id = WRITE_REGISTER_CMD, + .no_cmd_params = 0x4, + .cmd_params = {0x68, 0x01, 0x01, 0xDF}, + .req = SYNC, + }, + [CMD_ISO14443B_DEMOGAIN] = { + .cmd_len = 0x7, + .cmd_id = WRITE_REGISTER_CMD, + .no_cmd_params = 0x4, + .cmd_params = {0x68, 0x01, 0x01, 0x51}, + .req = SYNC, + }, + [CMD_ISO14443A_PROTOCOL_SELECT] = { + .cmd_len = 0x7, + .cmd_id = PROTOCOL_SELECT_CMD, + .no_cmd_params = 0x4, + .cmd_params = {ISO14443A_PROTOCOL_CODE, 0x00, 0x01, 0xA0}, + .req = SYNC, + }, + [CMD_ISO14443B_PROTOCOL_SELECT] = { + .cmd_len = 0x7, + .cmd_id = PROTOCOL_SELECT_CMD, + .no_cmd_params = 0x4, + .cmd_params = {ISO14443B_PROTOCOL_CODE, 0x01, 0x03, 0xFF}, + .req = SYNC, + }, + [CMD_WTX_RESPONSE] = { + .cmd_len = 0x6, + .cmd_id = SEND_RECEIVE_CMD, + .no_cmd_params = 0x3, + .cmd_params = {0xF2, 0x00, TRFLAG_NFCA_STD_FRAME_CRC}, + .req = ASYNC, + }, + [CMD_FIELD_OFF] = { + .cmd_len = 0x5, + .cmd_id = PROTOCOL_SELECT_CMD, + .no_cmd_params = 0x2, + .cmd_params = {0x0, 0x0}, + .req = SYNC, + }, + [CMD_ISO15693_PROTOCOL_SELECT] = { + .cmd_len = 0x5, + .cmd_id = PROTOCOL_SELECT_CMD, + .no_cmd_params = 0x2, + .cmd_params = {ISO15693_PROTOCOL_CODE, 0x0D}, + .req = SYNC, + }, +}; + +/* st95_digital_cmd_complete_arg stores client context */ +struct st95_digital_cmd_complete_arg { + struct sk_buff *skb_resp; + nfc_digital_cmd_complete_t complete_cb; + void *cb_usrarg; + bool rats; +}; + +/* + * structure containing ST95HF driver specific data. + * @spicontext: structure containing information required + * for spi communication between st95hf and host. + * @ddev: nfc digital device object. + * @nfcdev: nfc device object. + * @enable_gpio: gpio used to enable st95hf transceiver. + * @complete_cb_arg: structure to store various context information + * that is passed from nfc requesting thread to the threaded ISR. + * @st95hf_supply: regulator "consumer" for NFC device. + * @sendrcv_trflag: last byte of frame send by sendrecv command + * of st95hf. This byte contains transmission flag info. + * @exchange_lock: semaphore used for signaling the st95hf_remove + * function that the last outstanding async nfc request is finished. + * @rm_lock: mutex for ensuring safe access of nfc digital object + * from threaded ISR. Usage of this mutex avoids any race between + * deletion of the object from st95hf_remove() and its access from + * the threaded ISR. + * @nfcdev_free: flag to have the state of nfc device object. + * [alive | died] + * @current_protocol: current nfc protocol. + * @current_rf_tech: current rf technology. + * @fwi: frame waiting index, received in reply of RATS according to + * digital protocol. + */ +struct st95hf_context { + struct st95hf_spi_context spicontext; + struct nfc_digital_dev *ddev; + struct nfc_dev *nfcdev; + unsigned int enable_gpio; + struct st95_digital_cmd_complete_arg complete_cb_arg; + struct regulator *st95hf_supply; + unsigned char sendrcv_trflag; + struct semaphore exchange_lock; + struct mutex rm_lock; + bool nfcdev_free; + u8 current_protocol; + u8 current_rf_tech; + int fwi; +}; + +/* + * st95hf_send_recv_cmd() is for sending commands to ST95HF + * that are described in the cmd_array[]. It can optionally + * receive the response if the cmd request is of type + * SYNC. For that to happen caller must pass true to recv_res. + * For ASYNC request, recv_res is ignored and the + * function will never try to receive the response on behalf + * of the caller. + */ +static int st95hf_send_recv_cmd(struct st95hf_context *st95context, + enum st95hf_cmd_list cmd, + int no_modif, + struct param_list *list_array, + bool recv_res) +{ + unsigned char spi_cmd_buffer[MAX_CMD_LEN]; + int i, ret; + struct device *dev = &st95context->spicontext.spidev->dev; + + if (cmd_array[cmd].cmd_len > MAX_CMD_LEN) + return -EINVAL; + if (cmd_array[cmd].no_cmd_params < no_modif) + return -EINVAL; + if (no_modif && !list_array) + return -EINVAL; + + spi_cmd_buffer[0] = ST95HF_COMMAND_SEND; + spi_cmd_buffer[1] = cmd_array[cmd].cmd_id; + spi_cmd_buffer[2] = cmd_array[cmd].no_cmd_params; + + memcpy(&spi_cmd_buffer[3], cmd_array[cmd].cmd_params, + spi_cmd_buffer[2]); + + for (i = 0; i < no_modif; i++) { + if (list_array[i].param_offset >= cmd_array[cmd].no_cmd_params) + return -EINVAL; + spi_cmd_buffer[3 + list_array[i].param_offset] = + list_array[i].new_param_val; + } + + ret = st95hf_spi_send(&st95context->spicontext, + spi_cmd_buffer, + cmd_array[cmd].cmd_len, + cmd_array[cmd].req); + if (ret) { + dev_err(dev, "st95hf_spi_send failed with error %d\n", ret); + return ret; + } + + if (cmd_array[cmd].req == SYNC && recv_res) { + unsigned char st95hf_response_arr[2]; + + ret = st95hf_spi_recv_response(&st95context->spicontext, + st95hf_response_arr); + if (ret < 0) { + dev_err(dev, "spi error from st95hf_spi_recv_response(), err = 0x%x\n", + ret); + return ret; + } + + if (st95hf_response_arr[0]) { + dev_err(dev, "st95hf error from st95hf_spi_recv_response(), err = 0x%x\n", + st95hf_response_arr[0]); + return -EIO; + } + } + + return 0; +} + +static int st95hf_echo_command(struct st95hf_context *st95context) +{ + int result = 0; + unsigned char echo_response; + + result = st95hf_send_recv_cmd(st95context, CMD_ECHO, 0, NULL, false); + if (result) + return result; + + /* If control reached here, response can be taken */ + result = st95hf_spi_recv_echo_res(&st95context->spicontext, + &echo_response); + if (result) { + dev_err(&st95context->spicontext.spidev->dev, + "err: echo response receieve error = 0x%x\n", result); + return result; + } + + if (echo_response == ECHORESPONSE) + return 0; + + dev_err(&st95context->spicontext.spidev->dev, "err: echo res is 0x%x\n", + echo_response); + + return -EIO; +} + +static int secondary_configuration_type4a(struct st95hf_context *stcontext) +{ + int result = 0; + struct device *dev = &stcontext->nfcdev->dev; + + /* 14443A config setting after select protocol */ + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO14443A_CONFIG, + 0, + NULL, + true); + if (result) { + dev_err(dev, "type a config cmd, err = 0x%x\n", result); + return result; + } + + /* 14443A demo gain setting */ + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO14443A_DEMOGAIN, + 0, + NULL, + true); + if (result) + dev_err(dev, "type a demogain cmd, err = 0x%x\n", result); + + return result; +} + +static int secondary_configuration_type4b(struct st95hf_context *stcontext) +{ + int result = 0; + struct device *dev = &stcontext->nfcdev->dev; + + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO14443B_DEMOGAIN, + 0, + NULL, + true); + if (result) + dev_err(dev, "type b demogain cmd, err = 0x%x\n", result); + + return result; +} + +static int st95hf_select_protocol(struct st95hf_context *stcontext, int type) +{ + int result = 0; + struct device *dev; + + dev = &stcontext->nfcdev->dev; + + switch (type) { + case NFC_DIGITAL_RF_TECH_106A: + stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106A; + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO14443A_PROTOCOL_SELECT, + 0, + NULL, + true); + if (result) { + dev_err(dev, "protocol sel, err = 0x%x\n", + result); + return result; + } + + /* secondary config. for 14443Type 4A after protocol select */ + result = secondary_configuration_type4a(stcontext); + if (result) { + dev_err(dev, "type a secondary config, err = 0x%x\n", + result); + return result; + } + break; + case NFC_DIGITAL_RF_TECH_106B: + stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106B; + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO14443B_PROTOCOL_SELECT, + 0, + NULL, + true); + if (result) { + dev_err(dev, "protocol sel send, err = 0x%x\n", + result); + return result; + } + + /* + * delay of 5-6 ms is required after select protocol + * command in case of ISO14443 Type B + */ + usleep_range(50000, 60000); + + /* secondary config. for 14443Type 4B after protocol select */ + result = secondary_configuration_type4b(stcontext); + if (result) { + dev_err(dev, "type b secondary config, err = 0x%x\n", + result); + return result; + } + break; + case NFC_DIGITAL_RF_TECH_ISO15693: + stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_ISO15693; + result = st95hf_send_recv_cmd(stcontext, + CMD_ISO15693_PROTOCOL_SELECT, + 0, + NULL, + true); + if (result) { + dev_err(dev, "protocol sel send, err = 0x%x\n", + result); + return result; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static void st95hf_send_st95enable_negativepulse(struct st95hf_context *st95con) +{ + /* First make irq_in pin high */ + gpio_set_value(st95con->enable_gpio, HIGH); + + /* wait for 1 milisecond */ + usleep_range(1000, 2000); + + /* Make irq_in pin low */ + gpio_set_value(st95con->enable_gpio, LOW); + + /* wait for minimum interrupt pulse to make st95 active */ + usleep_range(1000, 2000); + + /* At end make it high */ + gpio_set_value(st95con->enable_gpio, HIGH); +} + +/* + * Send a reset sequence over SPI bus (Reset command + wait 3ms + + * negative pulse on st95hf enable gpio + */ +static int st95hf_send_spi_reset_sequence(struct st95hf_context *st95context) +{ + int result = 0; + unsigned char reset_cmd = ST95HF_COMMAND_RESET; + + result = st95hf_spi_send(&st95context->spicontext, + &reset_cmd, + ST95HF_RESET_CMD_LEN, + ASYNC); + if (result) { + dev_err(&st95context->spicontext.spidev->dev, + "spi reset sequence cmd error = %d", result); + return result; + } + + /* wait for 3 milisecond to complete the controller reset process */ + usleep_range(3000, 4000); + + /* send negative pulse to make st95hf active */ + st95hf_send_st95enable_negativepulse(st95context); + + /* wait for 10 milisecond : HFO setup time */ + usleep_range(10000, 20000); + + return result; +} + +static int st95hf_por_sequence(struct st95hf_context *st95context) +{ + int nth_attempt = 1; + int result; + + st95hf_send_st95enable_negativepulse(st95context); + + usleep_range(5000, 6000); + do { + /* send an ECHO command and checks ST95HF response */ + result = st95hf_echo_command(st95context); + + dev_dbg(&st95context->spicontext.spidev->dev, + "response from echo function = 0x%x, attempt = %d\n", + result, nth_attempt); + + if (!result) + return 0; + + /* send an pulse on IRQ in case of the chip is on sleep state */ + if (nth_attempt == 2) + st95hf_send_st95enable_negativepulse(st95context); + else + st95hf_send_spi_reset_sequence(st95context); + + /* delay of 50 milisecond */ + usleep_range(50000, 51000); + } while (nth_attempt++ < 3); + + return -ETIMEDOUT; +} + +static int iso14443_config_fdt(struct st95hf_context *st95context, int wtxm) +{ + int result = 0; + struct device *dev = &st95context->spicontext.spidev->dev; + struct nfc_digital_dev *nfcddev = st95context->ddev; + unsigned char pp_typeb; + struct param_list new_params[2]; + + pp_typeb = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[2]; + + if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 && + st95context->fwi < 4) + st95context->fwi = 4; + + new_params[0].param_offset = 2; + if (nfcddev->curr_protocol == NFC_PROTO_ISO14443) + new_params[0].new_param_val = st95context->fwi; + else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B) + new_params[0].new_param_val = pp_typeb; + + new_params[1].param_offset = 3; + new_params[1].new_param_val = wtxm; + + switch (nfcddev->curr_protocol) { + case NFC_PROTO_ISO14443: + result = st95hf_send_recv_cmd(st95context, + CMD_ISO14443A_PROTOCOL_SELECT, + 2, + new_params, + true); + if (result) { + dev_err(dev, "WTX type a sel proto, err = 0x%x\n", + result); + return result; + } + + /* secondary config. for 14443Type 4A after protocol select */ + result = secondary_configuration_type4a(st95context); + if (result) { + dev_err(dev, "WTX type a second. config, err = 0x%x\n", + result); + return result; + } + break; + case NFC_PROTO_ISO14443_B: + result = st95hf_send_recv_cmd(st95context, + CMD_ISO14443B_PROTOCOL_SELECT, + 2, + new_params, + true); + if (result) { + dev_err(dev, "WTX type b sel proto, err = 0x%x\n", + result); + return result; + } + + /* secondary config. for 14443Type 4B after protocol select */ + result = secondary_configuration_type4b(st95context); + if (result) { + dev_err(dev, "WTX type b second. config, err = 0x%x\n", + result); + return result; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int st95hf_handle_wtx(struct st95hf_context *stcontext, + bool new_wtx, + int wtx_val) +{ + int result = 0; + unsigned char val_mm = 0; + struct param_list new_params[1]; + struct nfc_digital_dev *nfcddev = stcontext->ddev; + struct device *dev = &stcontext->nfcdev->dev; + + if (new_wtx) { + result = iso14443_config_fdt(stcontext, wtx_val & 0x3f); + if (result) { + dev_err(dev, "Config. setting error on WTX req, err = 0x%x\n", + result); + return result; + } + + /* Send response of wtx with ASYNC as no response expected */ + new_params[0].param_offset = 1; + new_params[0].new_param_val = wtx_val; + + result = st95hf_send_recv_cmd(stcontext, + CMD_WTX_RESPONSE, + 1, + new_params, + false); + if (result) + dev_err(dev, "WTX response send, err = 0x%x\n", result); + return result; + } + + /* if no new wtx, cofigure with default values */ + if (nfcddev->curr_protocol == NFC_PROTO_ISO14443) + val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3]; + else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B) + val_mm = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[3]; + + result = iso14443_config_fdt(stcontext, val_mm); + if (result) + dev_err(dev, "Default config. setting error after WTX processing, err = 0x%x\n", + result); + + return result; +} + +static int st95hf_error_handling(struct st95hf_context *stcontext, + struct sk_buff *skb_resp, + int res_len) +{ + int result = 0; + unsigned char error_byte; + struct device *dev = &stcontext->nfcdev->dev; + + /* First check ST95HF specific error */ + if (skb_resp->data[0] & ST95HF_ERR_MASK) { + if (skb_resp->data[0] == ST95HF_TIMEOUT_ERROR) + result = -ETIMEDOUT; + else + result = -EIO; + return result; + } + + /* Check for CRC err only if CRC is present in the tag response */ + switch (stcontext->current_rf_tech) { + case NFC_DIGITAL_RF_TECH_106A: + if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC) { + error_byte = skb_resp->data[res_len - 3]; + if (error_byte & ST95HF_NFCA_CRC_ERR_MASK) { + /* CRC error occurred */ + dev_err(dev, "CRC error, byte received = 0x%x\n", + error_byte); + result = -EIO; + } + } + break; + case NFC_DIGITAL_RF_TECH_106B: + case NFC_DIGITAL_RF_TECH_ISO15693: + error_byte = skb_resp->data[res_len - 1]; + if (error_byte & ST95HF_NFCB_CRC_ERR_MASK) { + /* CRC error occurred */ + dev_err(dev, "CRC error, byte received = 0x%x\n", + error_byte); + result = -EIO; + } + break; + } + + return result; +} + +static int st95hf_response_handler(struct st95hf_context *stcontext, + struct sk_buff *skb_resp, + int res_len) +{ + int result = 0; + int skb_len; + unsigned char val_mm; + struct nfc_digital_dev *nfcddev = stcontext->ddev; + struct device *dev = &stcontext->nfcdev->dev; + struct st95_digital_cmd_complete_arg *cb_arg; + + cb_arg = &stcontext->complete_cb_arg; + + /* Process the response */ + skb_put(skb_resp, res_len); + + /* Remove st95 header */ + skb_pull(skb_resp, 2); + + skb_len = skb_resp->len; + + /* check if it is case of RATS request reply & FWI is present */ + if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 && cb_arg->rats && + (skb_resp->data[1] & RATS_TB1_PRESENT_MASK)) { + if (skb_resp->data[1] & RATS_TA1_PRESENT_MASK) + stcontext->fwi = + (skb_resp->data[3] & TB1_FWI_MASK) >> 4; + else + stcontext->fwi = + (skb_resp->data[2] & TB1_FWI_MASK) >> 4; + + val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3]; + + result = iso14443_config_fdt(stcontext, val_mm); + if (result) { + dev_err(dev, "error in config_fdt to handle fwi of ATS, error=%d\n", + result); + return result; + } + } + cb_arg->rats = false; + + /* Remove CRC bytes only if received frames data has an eod (CRC) */ + switch (stcontext->current_rf_tech) { + case NFC_DIGITAL_RF_TECH_106A: + if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC) + skb_trim(skb_resp, (skb_len - 5)); + else + skb_trim(skb_resp, (skb_len - 3)); + break; + case NFC_DIGITAL_RF_TECH_106B: + case NFC_DIGITAL_RF_TECH_ISO15693: + skb_trim(skb_resp, (skb_len - 3)); + break; + } + + return result; +} + +static irqreturn_t irq_handler(int irq, void *st95hfcontext) +{ + struct st95hf_context *stcontext = + (struct st95hf_context *)st95hfcontext; + + if (stcontext->spicontext.req_issync) { + complete(&stcontext->spicontext.done); + stcontext->spicontext.req_issync = false; + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t irq_thread_handler(int irq, void *st95hfcontext) +{ + int result = 0; + int res_len; + static bool wtx; + struct device *dev; + struct device *spidevice; + struct nfc_digital_dev *nfcddev; + struct sk_buff *skb_resp; + struct st95hf_context *stcontext = + (struct st95hf_context *)st95hfcontext; + struct st95_digital_cmd_complete_arg *cb_arg; + + spidevice = &stcontext->spicontext.spidev->dev; + + /* + * check semaphore, if not down() already, then we don't + * know in which context the ISR is called and surely it + * will be a bug. Note that down() of the semaphore is done + * in the corresponding st95hf_in_send_cmd() and then + * only this ISR should be called. ISR will up() the + * semaphore before leaving. Hence when the ISR is called + * the correct behaviour is down_trylock() should always + * return 1 (indicating semaphore cant be taken and hence no + * change in semaphore count). + * If not, then we up() the semaphore and crash on + * a BUG() ! + */ + if (!down_trylock(&stcontext->exchange_lock)) { + up(&stcontext->exchange_lock); + WARN(1, "unknown context in ST95HF ISR"); + return IRQ_NONE; + } + + cb_arg = &stcontext->complete_cb_arg; + skb_resp = cb_arg->skb_resp; + + mutex_lock(&stcontext->rm_lock); + res_len = st95hf_spi_recv_response(&stcontext->spicontext, + skb_resp->data); + if (res_len < 0) { + dev_err(spidevice, "TISR spi response err = 0x%x\n", res_len); + result = res_len; + goto end; + } + + /* if stcontext->nfcdev_free is true, it means remove already ran */ + if (stcontext->nfcdev_free) { + result = -ENODEV; + goto end; + } + + dev = &stcontext->nfcdev->dev; + nfcddev = stcontext->ddev; + if (skb_resp->data[2] == WTX_REQ_FROM_TAG) { + /* Request for new FWT from tag */ + result = st95hf_handle_wtx(stcontext, true, skb_resp->data[3]); + if (result) + goto end; + + wtx = true; + mutex_unlock(&stcontext->rm_lock); + return IRQ_HANDLED; + } + + result = st95hf_error_handling(stcontext, skb_resp, res_len); + if (result) + goto end; + + result = st95hf_response_handler(stcontext, skb_resp, res_len); + if (result) + goto end; + + /* + * If select protocol is done on wtx req. do select protocol + * again with default values + */ + if (wtx) { + wtx = false; + result = st95hf_handle_wtx(stcontext, false, 0); + if (result) + goto end; + } + + /* call digital layer callback */ + cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp); + + /* up the semaphore before returning */ + up(&stcontext->exchange_lock); + mutex_unlock(&stcontext->rm_lock); + + return IRQ_HANDLED; + +end: + kfree_skb(skb_resp); + wtx = false; + cb_arg->rats = false; + skb_resp = ERR_PTR(result); + /* call of callback with error */ + cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp); + /* up the semaphore before returning */ + up(&stcontext->exchange_lock); + mutex_unlock(&stcontext->rm_lock); + return IRQ_HANDLED; +} + +/* NFC ops functions definition */ +static int st95hf_in_configure_hw(struct nfc_digital_dev *ddev, + int type, + int param) +{ + struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); + + if (type == NFC_DIGITAL_CONFIG_RF_TECH) + return st95hf_select_protocol(stcontext, param); + + if (type == NFC_DIGITAL_CONFIG_FRAMING) { + switch (param) { + case NFC_DIGITAL_FRAMING_NFCA_SHORT: + stcontext->sendrcv_trflag = TRFLAG_NFCA_SHORT_FRAME; + break; + case NFC_DIGITAL_FRAMING_NFCA_STANDARD: + stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME; + break; + case NFC_DIGITAL_FRAMING_NFCA_T4T: + case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP: + case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A: + stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME_CRC; + break; + case NFC_DIGITAL_FRAMING_NFCB: + case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY: + case NFC_DIGITAL_FRAMING_ISO15693_T5T: + break; + } + } + + return 0; +} + +static int rf_off(struct st95hf_context *stcontext) +{ + int rc; + struct device *dev; + + dev = &stcontext->nfcdev->dev; + + rc = st95hf_send_recv_cmd(stcontext, CMD_FIELD_OFF, 0, NULL, true); + if (rc) + dev_err(dev, "protocol sel send field off, err = 0x%x\n", rc); + + return rc; +} + +static int st95hf_in_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, + u16 timeout, + nfc_digital_cmd_complete_t cb, + void *arg) +{ + struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); + int rc; + struct sk_buff *skb_resp; + int len_data_to_tag = 0; + + skb_resp = nfc_alloc_recv_skb(MAX_RESPONSE_BUFFER_SIZE, GFP_KERNEL); + if (!skb_resp) { + rc = -ENOMEM; + goto error; + } + + switch (stcontext->current_rf_tech) { + case NFC_DIGITAL_RF_TECH_106A: + len_data_to_tag = skb->len + 1; + *skb_put(skb, 1) = stcontext->sendrcv_trflag; + break; + case NFC_DIGITAL_RF_TECH_106B: + case NFC_DIGITAL_RF_TECH_ISO15693: + len_data_to_tag = skb->len; + break; + default: + rc = -EINVAL; + goto free_skb_resp; + } + + skb_push(skb, 3); + skb->data[0] = ST95HF_COMMAND_SEND; + skb->data[1] = SEND_RECEIVE_CMD; + skb->data[2] = len_data_to_tag; + + stcontext->complete_cb_arg.skb_resp = skb_resp; + stcontext->complete_cb_arg.cb_usrarg = arg; + stcontext->complete_cb_arg.complete_cb = cb; + + if ((skb->data[3] == ISO14443A_RATS_REQ) && + ddev->curr_protocol == NFC_PROTO_ISO14443) + stcontext->complete_cb_arg.rats = true; + + /* + * down the semaphore to indicate to remove func that an + * ISR is pending, note that it will not block here in any case. + * If found blocked, it is a BUG! + */ + rc = down_killable(&stcontext->exchange_lock); + if (rc) { + WARN(1, "Semaphore is not found up in st95hf_in_send_cmd\n"); + return rc; + } + + rc = st95hf_spi_send(&stcontext->spicontext, skb->data, + skb->len, + ASYNC); + if (rc) { + dev_err(&stcontext->nfcdev->dev, + "Error %d trying to perform data_exchange", rc); + /* up the semaphore since ISR will never come in this case */ + up(&stcontext->exchange_lock); + goto free_skb_resp; + } + + kfree_skb(skb); + + return rc; + +free_skb_resp: + kfree_skb(skb_resp); +error: + return rc; +} + +/* p2p will be supported in a later release ! */ +static int st95hf_tg_configure_hw(struct nfc_digital_dev *ddev, + int type, + int param) +{ + return 0; +} + +static int st95hf_tg_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, + u16 timeout, + nfc_digital_cmd_complete_t cb, + void *arg) +{ + return 0; +} + +static int st95hf_tg_listen(struct nfc_digital_dev *ddev, + u16 timeout, + nfc_digital_cmd_complete_t cb, + void *arg) +{ + return 0; +} + +static int st95hf_tg_get_rf_tech(struct nfc_digital_dev *ddev, u8 *rf_tech) +{ + return 0; +} + +static int st95hf_switch_rf(struct nfc_digital_dev *ddev, bool on) +{ + u8 rf_tech; + struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev); + + rf_tech = ddev->curr_rf_tech; + + if (on) + /* switch on RF field */ + return st95hf_select_protocol(stcontext, rf_tech); + + /* switch OFF RF field */ + return rf_off(stcontext); +} + +/* TODO st95hf_abort_cmd */ +static void st95hf_abort_cmd(struct nfc_digital_dev *ddev) +{ +} + +static struct nfc_digital_ops st95hf_nfc_digital_ops = { + .in_configure_hw = st95hf_in_configure_hw, + .in_send_cmd = st95hf_in_send_cmd, + + .tg_listen = st95hf_tg_listen, + .tg_configure_hw = st95hf_tg_configure_hw, + .tg_send_cmd = st95hf_tg_send_cmd, + .tg_get_rf_tech = st95hf_tg_get_rf_tech, + + .switch_rf = st95hf_switch_rf, + .abort_cmd = st95hf_abort_cmd, +}; + +static const struct spi_device_id st95hf_id[] = { + { "st95hf", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, st95hf_id); + +static int st95hf_probe(struct spi_device *nfc_spi_dev) +{ + int ret; + + struct st95hf_context *st95context; + struct st95hf_spi_context *spicontext; + + nfc_info(&nfc_spi_dev->dev, "ST95HF driver probe called.\n"); + + st95context = devm_kzalloc(&nfc_spi_dev->dev, + sizeof(struct st95hf_context), + GFP_KERNEL); + if (!st95context) + return -ENOMEM; + + spicontext = &st95context->spicontext; + + spicontext->spidev = nfc_spi_dev; + + st95context->fwi = + cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[2]; + + if (device_property_present(&nfc_spi_dev->dev, "st95hfvin")) { + st95context->st95hf_supply = + devm_regulator_get(&nfc_spi_dev->dev, + "st95hfvin"); + if (IS_ERR(st95context->st95hf_supply)) { + dev_err(&nfc_spi_dev->dev, "failed to acquire regulator\n"); + return PTR_ERR(st95context->st95hf_supply); + } + + ret = regulator_enable(st95context->st95hf_supply); + if (ret) { + dev_err(&nfc_spi_dev->dev, "failed to enable regulator\n"); + return ret; + } + } + + init_completion(&spicontext->done); + mutex_init(&spicontext->spi_lock); + + /* + * Store spicontext in spi device object for using it in + * remove function + */ + dev_set_drvdata(&nfc_spi_dev->dev, spicontext); + + st95context->enable_gpio = + of_get_named_gpio(nfc_spi_dev->dev.of_node, + "enable-gpio", + 0); + if (!gpio_is_valid(st95context->enable_gpio)) { + dev_err(&nfc_spi_dev->dev, "No valid enable gpio\n"); + ret = st95context->enable_gpio; + goto err_disable_regulator; + } + + ret = devm_gpio_request_one(&nfc_spi_dev->dev, st95context->enable_gpio, + GPIOF_DIR_OUT | GPIOF_INIT_HIGH, + "enable_gpio"); + if (ret) + goto err_disable_regulator; + + if (nfc_spi_dev->irq > 0) { + if (devm_request_threaded_irq(&nfc_spi_dev->dev, + nfc_spi_dev->irq, + irq_handler, + irq_thread_handler, + IRQF_TRIGGER_FALLING, + "st95hf", + (void *)st95context) < 0) { + dev_err(&nfc_spi_dev->dev, "err: irq request for st95hf is failed\n"); + ret = -EINVAL; + goto err_disable_regulator; + } + } else { + dev_err(&nfc_spi_dev->dev, "not a valid IRQ associated with ST95HF\n"); + ret = -EINVAL; + goto err_disable_regulator; + } + + /* + * First reset SPI to handle warm reset of the system. + * It will put the ST95HF device in Power ON state + * which make the state of device identical to state + * at the time of cold reset of the system. + */ + ret = st95hf_send_spi_reset_sequence(st95context); + if (ret) { + dev_err(&nfc_spi_dev->dev, "err: spi_reset_sequence failed\n"); + goto err_disable_regulator; + } + + /* call PowerOnReset sequence of ST95hf to activate it */ + ret = st95hf_por_sequence(st95context); + if (ret) { + dev_err(&nfc_spi_dev->dev, "err: por seq failed for st95hf\n"); + goto err_disable_regulator; + } + + /* create NFC dev object and register with NFC Subsystem */ + st95context->ddev = nfc_digital_allocate_device(&st95hf_nfc_digital_ops, + ST95HF_SUPPORTED_PROT, + ST95HF_CAPABILITIES, + ST95HF_HEADROOM_LEN, + ST95HF_TAILROOM_LEN); + if (!st95context->ddev) { + ret = -ENOMEM; + goto err_disable_regulator; + } + + st95context->nfcdev = st95context->ddev->nfc_dev; + nfc_digital_set_parent_dev(st95context->ddev, &nfc_spi_dev->dev); + + ret = nfc_digital_register_device(st95context->ddev); + if (ret) { + dev_err(&st95context->nfcdev->dev, "st95hf registration failed\n"); + goto err_free_digital_device; + } + + /* store st95context in nfc device object */ + nfc_digital_set_drvdata(st95context->ddev, st95context); + + sema_init(&st95context->exchange_lock, 1); + mutex_init(&st95context->rm_lock); + + return ret; + +err_free_digital_device: + nfc_digital_free_device(st95context->ddev); +err_disable_regulator: + if (st95context->st95hf_supply) + regulator_disable(st95context->st95hf_supply); + + return ret; +} + +static int st95hf_remove(struct spi_device *nfc_spi_dev) +{ + int result = 0; + unsigned char reset_cmd = ST95HF_COMMAND_RESET; + struct st95hf_spi_context *spictx = dev_get_drvdata(&nfc_spi_dev->dev); + + struct st95hf_context *stcontext = container_of(spictx, + struct st95hf_context, + spicontext); + + mutex_lock(&stcontext->rm_lock); + + nfc_digital_unregister_device(stcontext->ddev); + nfc_digital_free_device(stcontext->ddev); + stcontext->nfcdev_free = true; + + mutex_unlock(&stcontext->rm_lock); + + /* if last in_send_cmd's ISR is pending, wait for it to finish */ + result = down_killable(&stcontext->exchange_lock); + if (result == -EINTR) + dev_err(&spictx->spidev->dev, "sleep for semaphore interrupted by signal\n"); + + /* next reset the ST95HF controller */ + result = st95hf_spi_send(&stcontext->spicontext, + &reset_cmd, + ST95HF_RESET_CMD_LEN, + ASYNC); + if (result) { + dev_err(&spictx->spidev->dev, + "ST95HF reset failed in remove() err = %d\n", result); + return result; + } + + /* wait for 3 ms to complete the controller reset process */ + usleep_range(3000, 4000); + + /* disable regulator */ + if (stcontext->st95hf_supply) + regulator_disable(stcontext->st95hf_supply); + + return result; +} + +/* Register as SPI protocol driver */ +static struct spi_driver st95hf_driver = { + .driver = { + .name = "st95hf", + .owner = THIS_MODULE, + }, + .id_table = st95hf_id, + .probe = st95hf_probe, + .remove = st95hf_remove, +}; + +module_spi_driver(st95hf_driver); + +MODULE_AUTHOR("Shikha Singh "); +MODULE_DESCRIPTION("ST NFC Transceiver ST95HF driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nfc/st95hf/spi.c b/drivers/nfc/st95hf/spi.c new file mode 100644 index 000000000000..e2d3bbcc8c34 --- /dev/null +++ b/drivers/nfc/st95hf/spi.c @@ -0,0 +1,167 @@ +/* + * ---------------------------------------------------------------------------- + * drivers/nfc/st95hf/spi.c function definitions for SPI communication + * ---------------------------------------------------------------------------- + * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "spi.h" + +/* Function to send user provided buffer to ST95HF through SPI */ +int st95hf_spi_send(struct st95hf_spi_context *spicontext, + unsigned char *buffertx, + int datalen, + enum req_type reqtype) +{ + struct spi_message m; + int result = 0; + struct spi_device *spidev = spicontext->spidev; + struct spi_transfer tx_transfer = { + .tx_buf = buffertx, + .len = datalen, + }; + + mutex_lock(&spicontext->spi_lock); + + if (reqtype == SYNC) { + spicontext->req_issync = true; + reinit_completion(&spicontext->done); + } else { + spicontext->req_issync = false; + } + + spi_message_init(&m); + spi_message_add_tail(&tx_transfer, &m); + + result = spi_sync(spidev, &m); + if (result) { + dev_err(&spidev->dev, "error: sending cmd to st95hf using SPI = %d\n", + result); + mutex_unlock(&spicontext->spi_lock); + return result; + } + + /* return for asynchronous or no-wait case */ + if (reqtype == ASYNC) { + mutex_unlock(&spicontext->spi_lock); + return 0; + } + + result = wait_for_completion_timeout(&spicontext->done, + msecs_to_jiffies(1000)); + /* check for timeout or success */ + if (!result) { + dev_err(&spidev->dev, "error: response not ready timeout\n"); + result = -ETIMEDOUT; + } else { + result = 0; + } + + mutex_unlock(&spicontext->spi_lock); + + return result; +} +EXPORT_SYMBOL_GPL(st95hf_spi_send); + +/* Function to Receive command Response */ +int st95hf_spi_recv_response(struct st95hf_spi_context *spicontext, + unsigned char *receivebuff) +{ + int len = 0; + struct spi_transfer tx_takedata; + struct spi_message m; + struct spi_device *spidev = spicontext->spidev; + unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE; + struct spi_transfer t[2] = { + {.tx_buf = &readdata_cmd, .len = 1,}, + {.rx_buf = receivebuff, .len = 2, .cs_change = 1,}, + }; + + int ret = 0; + + memset(&tx_takedata, 0x0, sizeof(struct spi_transfer)); + + mutex_lock(&spicontext->spi_lock); + + /* First spi transfer to know the length of valid data */ + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + ret = spi_sync(spidev, &m); + if (ret) { + dev_err(&spidev->dev, "spi_recv_resp, data length error = %d\n", + ret); + mutex_unlock(&spicontext->spi_lock); + return ret; + } + + /* As 2 bytes are already read */ + len = 2; + + /* Support of long frame */ + if (receivebuff[0] & 0x60) + len += (((receivebuff[0] & 0x60) >> 5) << 8) | receivebuff[1]; + else + len += receivebuff[1]; + + /* Now make a transfer to read only relevant bytes */ + tx_takedata.rx_buf = &receivebuff[2]; + tx_takedata.len = len - 2; + + spi_message_init(&m); + spi_message_add_tail(&tx_takedata, &m); + + ret = spi_sync(spidev, &m); + + mutex_unlock(&spicontext->spi_lock); + if (ret) { + dev_err(&spidev->dev, "spi_recv_resp, data read error = %d\n", + ret); + return ret; + } + + return len; +} +EXPORT_SYMBOL_GPL(st95hf_spi_recv_response); + +int st95hf_spi_recv_echo_res(struct st95hf_spi_context *spicontext, + unsigned char *receivebuff) +{ + unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE; + struct spi_transfer t[2] = { + {.tx_buf = &readdata_cmd, .len = 1,}, + {.rx_buf = receivebuff, .len = 1,}, + }; + struct spi_message m; + struct spi_device *spidev = spicontext->spidev; + int ret = 0; + + mutex_lock(&spicontext->spi_lock); + + spi_message_init(&m); + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + ret = spi_sync(spidev, &m); + + mutex_unlock(&spicontext->spi_lock); + + if (ret) + dev_err(&spidev->dev, "recv_echo_res, data read error = %d\n", + ret); + + return ret; +} +EXPORT_SYMBOL_GPL(st95hf_spi_recv_echo_res); diff --git a/drivers/nfc/st95hf/spi.h b/drivers/nfc/st95hf/spi.h new file mode 100644 index 000000000000..552d220747cd --- /dev/null +++ b/drivers/nfc/st95hf/spi.h @@ -0,0 +1,64 @@ +/* + * --------------------------------------------------------------------------- + * drivers/nfc/st95hf/spi.h functions declarations for SPI communication + * --------------------------------------------------------------------------- + * Copyright (C) 2015 STMicroelectronics – All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef __LINUX_ST95HF_SPI_H +#define __LINUX_ST95HF_SPI_H + +#include + +/* Basic ST95HF SPI CMDs */ +#define ST95HF_COMMAND_SEND 0x0 +#define ST95HF_COMMAND_RESET 0x1 +#define ST95HF_COMMAND_RECEIVE 0x2 + +#define ST95HF_RESET_CMD_LEN 0x1 + +/* + * structure to contain st95hf spi communication specific information. + * @req_issync: true for synchronous calls. + * @spidev: st95hf spi device object. + * @done: completion structure to wait for st95hf response + * for synchronous calls. + * @spi_lock: mutex to allow only one spi transfer at a time. + */ +struct st95hf_spi_context { + bool req_issync; + struct spi_device *spidev; + struct completion done; + struct mutex spi_lock; +}; + +/* flag to differentiate synchronous & asynchronous spi request */ +enum req_type { + SYNC, + ASYNC, +}; + +int st95hf_spi_send(struct st95hf_spi_context *spicontext, + unsigned char *buffertx, + int datalen, + enum req_type reqtype); + +int st95hf_spi_recv_response(struct st95hf_spi_context *spicontext, + unsigned char *receivebuff); + +int st95hf_spi_recv_echo_res(struct st95hf_spi_context *spicontext, + unsigned char *receivebuff); + +#endif -- cgit v1.2.3-59-g8ed1b From 6b52d994b2763c87d67b0968fa746824f8855783 Mon Sep 17 00:00:00 2001 From: Shikha Singh Date: Tue, 22 Dec 2015 04:53:37 -0500 Subject: NFC: st95hf: Fix build error This fixes a build error on the mn10300 architecture: drivers/nfc/st95hf/core.c:765:20: error: conflicting types for 'irq_handler' static irqreturn_t irq_handler(int irq, void *st95hfcontext) ^ In file included from arch/mn10300/include/asm/reset-regs.h:16:0, from arch/mn10300/include/asm/irq.h:18, from include/linux/irq.h:26, from arch/mn10300/include/asm/hardirq.h:16, from include/linux/hardirq.h:8, from include/linux/interrupt.h:12, from drivers/nfc/st95hf/core.c:23: arch/mn10300/include/asm/exceptions.h:107:24: note: previous declaration of 'irq_handler' was here extern asmlinkage void irq_handler(void); Signed-off-by: Shikha Singh Signed-off-by: Samuel Ortiz --- drivers/nfc/st95hf/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/st95hf/core.c b/drivers/nfc/st95hf/core.c index 454efc6ae3f4..c2840e412962 100644 --- a/drivers/nfc/st95hf/core.c +++ b/drivers/nfc/st95hf/core.c @@ -762,7 +762,7 @@ static int st95hf_response_handler(struct st95hf_context *stcontext, return result; } -static irqreturn_t irq_handler(int irq, void *st95hfcontext) +static irqreturn_t st95hf_irq_handler(int irq, void *st95hfcontext) { struct st95hf_context *stcontext = (struct st95hf_context *)st95hfcontext; @@ -776,7 +776,7 @@ static irqreturn_t irq_handler(int irq, void *st95hfcontext) return IRQ_WAKE_THREAD; } -static irqreturn_t irq_thread_handler(int irq, void *st95hfcontext) +static irqreturn_t st95hf_irq_thread_handler(int irq, void *st95hfcontext) { int result = 0; int res_len; @@ -1140,8 +1140,8 @@ static int st95hf_probe(struct spi_device *nfc_spi_dev) if (nfc_spi_dev->irq > 0) { if (devm_request_threaded_irq(&nfc_spi_dev->dev, nfc_spi_dev->irq, - irq_handler, - irq_thread_handler, + st95hf_irq_handler, + st95hf_irq_thread_handler, IRQF_TRIGGER_FALLING, "st95hf", (void *)st95context) < 0) { -- cgit v1.2.3-59-g8ed1b From 88e2ce014fd3472825f70050aec6e8426667ae90 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 23 Dec 2015 00:18:42 +0800 Subject: NFC: trf7970a: use to_spi_device Use to_spi_device() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Samuel Ortiz --- drivers/nfc/trf7970a.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index f857feb2b573..10842b7051b3 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -2139,7 +2139,7 @@ static int trf7970a_remove(struct spi_device *spi) #ifdef CONFIG_PM_SLEEP static int trf7970a_suspend(struct device *dev) { - struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct spi_device *spi = to_spi_device(dev); struct trf7970a *trf = spi_get_drvdata(spi); dev_dbg(dev, "Suspend\n"); @@ -2155,7 +2155,7 @@ static int trf7970a_suspend(struct device *dev) static int trf7970a_resume(struct device *dev) { - struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct spi_device *spi = to_spi_device(dev); struct trf7970a *trf = spi_get_drvdata(spi); int ret; @@ -2174,7 +2174,7 @@ static int trf7970a_resume(struct device *dev) #ifdef CONFIG_PM static int trf7970a_pm_runtime_suspend(struct device *dev) { - struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct spi_device *spi = to_spi_device(dev); struct trf7970a *trf = spi_get_drvdata(spi); int ret; @@ -2191,7 +2191,7 @@ static int trf7970a_pm_runtime_suspend(struct device *dev) static int trf7970a_pm_runtime_resume(struct device *dev) { - struct spi_device *spi = container_of(dev, struct spi_device, dev); + struct spi_device *spi = to_spi_device(dev); struct trf7970a *trf = spi_get_drvdata(spi); int ret; -- cgit v1.2.3-59-g8ed1b From 53eb5252bb71c77b46ed953a2bb16627804dd29d Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:00 +0100 Subject: nfc: st-nci: Remove useless #include "ndlc.h" Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st-nci/ndlc.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nfc/st-nci/ndlc.c b/drivers/nfc/st-nci/ndlc.c index 0884b11001ef..50880d747b02 100644 --- a/drivers/nfc/st-nci/ndlc.c +++ b/drivers/nfc/st-nci/ndlc.c @@ -20,7 +20,6 @@ #include #include "st-nci.h" -#include "ndlc.h" #define NDLC_TIMER_T1 100 #define NDLC_TIMER_T1_WAIT 400 -- cgit v1.2.3-59-g8ed1b From 1faa65b0cf29dd23a8641805003bd58c5b3ab16c Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:01 +0100 Subject: nfc: st-nci: Remove unneeded CONFIG_OF switches DT headers already define NOOP routines when CONFIG_OF is not defined. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st-nci/i2c.c | 9 --------- drivers/nfc/st-nci/spi.c | 9 --------- 2 files changed, 18 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c index 15e3ce2d274c..a4b49e054ea2 100644 --- a/drivers/nfc/st-nci/i2c.c +++ b/drivers/nfc/st-nci/i2c.c @@ -210,7 +210,6 @@ static struct nfc_phy_ops i2c_phy_ops = { .disable = st_nci_i2c_disable, }; -#ifdef CONFIG_OF static int st_nci_i2c_of_request_resources(struct i2c_client *client) { struct st_nci_i2c_phy *phy = i2c_get_clientdata(client); @@ -248,12 +247,6 @@ static int st_nci_i2c_of_request_resources(struct i2c_client *client) return 0; } -#else -static int st_nci_i2c_of_request_resources(struct i2c_client *client) -{ - return -ENODEV; -} -#endif static int st_nci_i2c_request_resources(struct i2c_client *client) { @@ -358,7 +351,6 @@ static int st_nci_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_OF static const struct of_device_id of_st_nci_i2c_match[] = { { .compatible = "st,st21nfcb-i2c", }, { .compatible = "st,st21nfcb_i2c", }, @@ -366,7 +358,6 @@ static const struct of_device_id of_st_nci_i2c_match[] = { {} }; MODULE_DEVICE_TABLE(of, of_st_nci_i2c_match); -#endif static struct i2c_driver st_nci_i2c_driver = { .driver = { diff --git a/drivers/nfc/st-nci/spi.c b/drivers/nfc/st-nci/spi.c index d6519bb9dba5..0ab40cb335ad 100644 --- a/drivers/nfc/st-nci/spi.c +++ b/drivers/nfc/st-nci/spi.c @@ -225,7 +225,6 @@ static struct nfc_phy_ops spi_phy_ops = { .disable = st_nci_spi_disable, }; -#ifdef CONFIG_OF static int st_nci_spi_of_request_resources(struct spi_device *dev) { struct st_nci_spi_phy *phy = spi_get_drvdata(dev); @@ -263,12 +262,6 @@ static int st_nci_spi_of_request_resources(struct spi_device *dev) return 0; } -#else -static int st_nci_spi_of_request_resources(struct spi_device *dev) -{ - return -ENODEV; -} -#endif static int st_nci_spi_request_resources(struct spi_device *dev) { @@ -374,13 +367,11 @@ static int st_nci_spi_remove(struct spi_device *dev) return 0; } -#ifdef CONFIG_OF static const struct of_device_id of_st_nci_spi_match[] = { { .compatible = "st,st21nfcb-spi", }, {} }; MODULE_DEVICE_TABLE(of, of_st_nci_spi_match); -#endif static struct spi_driver st_nci_spi_driver = { .driver = { -- cgit v1.2.3-59-g8ed1b From 9135177fb6cda166c2f4b1f5441a2cefedcbf034 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:02 +0100 Subject: nfc: st21nfca: Remove unneeded CONFIG_OF switches DT headers already define NOOP routines when CONFIG_OF is not defined. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index a98da33e680a..bb0409e05167 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -509,7 +509,6 @@ static struct nfc_phy_ops i2c_phy_ops = { .disable = st21nfca_hci_i2c_disable, }; -#ifdef CONFIG_OF static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) { struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); @@ -547,12 +546,6 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) return 0; } -#else -static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) -{ - return -ENODEV; -} -#endif static int st21nfca_hci_i2c_request_resources(struct i2c_client *client) { @@ -670,14 +663,12 @@ static int st21nfca_hci_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_OF static const struct of_device_id of_st21nfca_i2c_match[] = { { .compatible = "st,st21nfca-i2c", }, { .compatible = "st,st21nfca_i2c", }, {} }; MODULE_DEVICE_TABLE(of, of_st21nfca_i2c_match); -#endif static struct i2c_driver st21nfca_hci_i2c_driver = { .driver = { -- cgit v1.2.3-59-g8ed1b From da5afe06d565ac03c27c6d173f5eaeb473ca1080 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:03 +0100 Subject: nfc: nxp-nci: Remove #ifdef CONFIG_OF All of_* APIs are safe if CONFIG_OF is not define. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/nxp-nci/i2c.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index ec359fc10b48..c71adc3254df 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -264,8 +264,6 @@ exit_irq_none: return IRQ_NONE; } -#ifdef CONFIG_OF - static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) { struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client); @@ -304,15 +302,6 @@ static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) return 0; } -#else - -static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) -{ - return -ENODEV; -} - -#endif - static int nxp_nci_i2c_acpi_config(struct nxp_nci_i2c_phy *phy) { struct i2c_client *client = phy->i2c_dev; -- cgit v1.2.3-59-g8ed1b From a21512d1b61c6aeeb05d74910078bc19059da87b Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:04 +0100 Subject: nfc: pn544: Remove #ifdef CONFIG_OF All of_* APIs are safe if CONFIG_OF is not define. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/i2c.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index fa75c53f3fa5..534c79fee1b7 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -938,8 +938,6 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) return 0; } -#ifdef CONFIG_OF - static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) { struct pn544_i2c_phy *phy = i2c_get_clientdata(client); @@ -1015,15 +1013,6 @@ err_dt: return ret; } -#else - -static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) -{ - return -ENODEV; -} - -#endif - static int pn544_hci_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { -- cgit v1.2.3-59-g8ed1b From 3252897f7a326a480e662c3b38a554fc35c6ad31 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:05 +0100 Subject: nfc: st-nci: Group device table together Group device table at the same place in order to make the code easier to read and parse. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st-nci/i2c.c | 11 ++++++----- drivers/nfc/st-nci/spi.c | 12 ++++++------ 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c index a4b49e054ea2..e0a512e4f0e6 100644 --- a/drivers/nfc/st-nci/i2c.c +++ b/drivers/nfc/st-nci/i2c.c @@ -40,11 +40,6 @@ #define ST_NCI_I2C_DRIVER_NAME "st_nci_i2c" -static struct i2c_device_id st_nci_i2c_id_table[] = { - {ST_NCI_DRIVER_NAME, 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table); struct st_nci_i2c_phy { struct i2c_client *i2c_dev; @@ -351,6 +346,12 @@ static int st_nci_i2c_remove(struct i2c_client *client) return 0; } +static struct i2c_device_id st_nci_i2c_id_table[] = { + {ST_NCI_DRIVER_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table); + static const struct of_device_id of_st_nci_i2c_match[] = { { .compatible = "st,st21nfcb-i2c", }, { .compatible = "st,st21nfcb_i2c", }, diff --git a/drivers/nfc/st-nci/spi.c b/drivers/nfc/st-nci/spi.c index 0ab40cb335ad..c378dd0c798c 100644 --- a/drivers/nfc/st-nci/spi.c +++ b/drivers/nfc/st-nci/spi.c @@ -41,12 +41,6 @@ #define ST_NCI_SPI_DRIVER_NAME "st_nci_spi" -static struct spi_device_id st_nci_spi_id_table[] = { - {ST_NCI_SPI_DRIVER_NAME, 0}, - {} -}; -MODULE_DEVICE_TABLE(spi, st_nci_spi_id_table); - struct st_nci_spi_phy { struct spi_device *spi_dev; struct llt_ndlc *ndlc; @@ -367,6 +361,12 @@ static int st_nci_spi_remove(struct spi_device *dev) return 0; } +static struct spi_device_id st_nci_spi_id_table[] = { + {ST_NCI_SPI_DRIVER_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, st_nci_spi_id_table); + static const struct of_device_id of_st_nci_spi_match[] = { { .compatible = "st,st21nfcb-spi", }, {} -- cgit v1.2.3-59-g8ed1b From 39db2d214898baa55db39a54f8644ffc0535fe4a Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:06 +0100 Subject: nfc: st21nfca: Group device table together Group device table at the same place in order to make the code easier to read and parse. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index bb0409e05167..0e6f6a87718c 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -60,13 +60,6 @@ #define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c" -static struct i2c_device_id st21nfca_hci_i2c_id_table[] = { - {ST21NFCA_HCI_DRIVER_NAME, 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table); - struct st21nfca_i2c_phy { struct i2c_client *i2c_dev; struct nfc_hci_dev *hdev; @@ -663,6 +656,12 @@ static int st21nfca_hci_i2c_remove(struct i2c_client *client) return 0; } +static struct i2c_device_id st21nfca_hci_i2c_id_table[] = { + {ST21NFCA_HCI_DRIVER_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table); + static const struct of_device_id of_st21nfca_i2c_match[] = { { .compatible = "st,st21nfca-i2c", }, { .compatible = "st,st21nfca_i2c", }, -- cgit v1.2.3-59-g8ed1b From 4c62c208ab777b03b7539f31adb2f2da3b3d0b31 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:07 +0100 Subject: nfc: st-nci: Add macro for gpio name Add macro definition for each gpio string for an easier code maintenance. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st-nci/i2c.c | 6 ++++-- drivers/nfc/st-nci/spi.c | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c index e0a512e4f0e6..0e6e3b22e24a 100644 --- a/drivers/nfc/st-nci/i2c.c +++ b/drivers/nfc/st-nci/i2c.c @@ -40,6 +40,7 @@ #define ST_NCI_I2C_DRIVER_NAME "st_nci_i2c" +#define ST_NCI_GPIO_NAME_RESET "clf_reset" struct st_nci_i2c_phy { struct i2c_client *i2c_dev; @@ -226,7 +227,7 @@ static int st_nci_i2c_of_request_resources(struct i2c_client *client) /* GPIO request and configuration */ r = devm_gpio_request_one(&client->dev, gpio, - GPIOF_OUT_INIT_HIGH, "clf_reset"); + GPIOF_OUT_INIT_HIGH, ST_NCI_GPIO_NAME_RESET); if (r) { nfc_err(&client->dev, "Failed to request reset pin\n"); return r; @@ -260,7 +261,8 @@ static int st_nci_i2c_request_resources(struct i2c_client *client) phy->irq_polarity = pdata->irq_polarity; r = devm_gpio_request_one(&client->dev, - phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset"); + phy->gpio_reset, GPIOF_OUT_INIT_HIGH, + ST_NCI_GPIO_NAME_RESET); if (r) { pr_err("%s : reset gpio_request failed\n", __FILE__); return r; diff --git a/drivers/nfc/st-nci/spi.c b/drivers/nfc/st-nci/spi.c index c378dd0c798c..7c3684b84cb8 100644 --- a/drivers/nfc/st-nci/spi.c +++ b/drivers/nfc/st-nci/spi.c @@ -41,6 +41,8 @@ #define ST_NCI_SPI_DRIVER_NAME "st_nci_spi" +#define ST_NCI_GPIO_NAME_RESET "clf_reset" + struct st_nci_spi_phy { struct spi_device *spi_dev; struct llt_ndlc *ndlc; @@ -240,7 +242,7 @@ static int st_nci_spi_of_request_resources(struct spi_device *dev) /* GPIO request and configuration */ r = devm_gpio_request_one(&dev->dev, gpio, - GPIOF_OUT_INIT_HIGH, "clf_reset"); + GPIOF_OUT_INIT_HIGH, ST_NCI_GPIO_NAME_RESET); if (r) { nfc_err(&dev->dev, "Failed to request reset pin\n"); return r; @@ -274,7 +276,8 @@ static int st_nci_spi_request_resources(struct spi_device *dev) phy->irq_polarity = pdata->irq_polarity; r = devm_gpio_request_one(&dev->dev, - phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset"); + phy->gpio_reset, GPIOF_OUT_INIT_HIGH, + ST_NCI_GPIO_NAME_RESET); if (r) { pr_err("%s : reset gpio_request failed\n", __FILE__); return r; -- cgit v1.2.3-59-g8ed1b From 04e99b71fe6e96476884b0e6d50bfe421dee9960 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:08 +0100 Subject: nfc: st21nfca: Add macro for gpio name Add macro definition for each gpio string for an easier code maintenance. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index 0e6f6a87718c..831e20d4da65 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -60,6 +60,8 @@ #define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c" +#define ST21NFCA_GPIO_NAME_EN "clf_enable" + struct st21nfca_i2c_phy { struct i2c_client *i2c_dev; struct nfc_hci_dev *hdev; @@ -522,7 +524,7 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) /* GPIO request and configuration */ r = devm_gpio_request_one(&client->dev, gpio, GPIOF_OUT_INIT_HIGH, - "clf_enable"); + ST21NFCA_GPIO_NAME_EN); if (r) { nfc_err(&client->dev, "Failed to request enable pin\n"); return r; @@ -558,7 +560,8 @@ static int st21nfca_hci_i2c_request_resources(struct i2c_client *client) if (phy->gpio_ena > 0) { r = devm_gpio_request_one(&client->dev, phy->gpio_ena, - GPIOF_OUT_INIT_HIGH, "clf_enable"); + GPIOF_OUT_INIT_HIGH, + ST21NFCA_GPIO_NAME_EN); if (r) { pr_err("%s : ena gpio_request failed\n", __FILE__); return r; -- cgit v1.2.3-59-g8ed1b From ed6a2f3fb770acc18bba8c9bc86f09e95d7fc641 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:09 +0100 Subject: nfc: st-nci: Add support for acpi probing for i2c device. Add support for acpi probing. SMO2101 is used for st21nfcb SMO2102 is used for st21nfcc It has been tested with the following acpi node on Minnowboard: Note: Remove uicc-present or ese-present Package if one them is not supported. Device (NFC1) { Name (_ADR, Zero) // _ADR: Address Name (_HID, "SMO2101") // _HID: Hardware ID Name (_CID, "SMO2101") // _CID: Compatible ID Name (_DDN, "SMO NFC") // _DDN: DOS Device Name Name (_UID, One) // _UID: Unique ID Name (_DSD, Package (0x02) { /* Device Properties for _DSD */ ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package (0x02) { Package (0x02) { "uicc-present", 1 }, Package (0x02) { "ese-present", 1 } } }) Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings { Name (SBUF, ResourceTemplate () { I2cSerialBus (0x0008, ControllerInitiated, 400000, AddressingMode7Bit, "\\_SB.I2C7", 0x00, ResourceConsumer, ,) GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullNone, 0x0000, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0001 } GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0002, } }) Return (SBUF) /* \_SB_.I2C7.NFC1._CRS.SBUF */ } Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x0F) } } Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st-nci/i2c.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'drivers') diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c index 0e6e3b22e24a..3e384159b288 100644 --- a/drivers/nfc/st-nci/i2c.c +++ b/drivers/nfc/st-nci/i2c.c @@ -20,8 +20,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -206,6 +208,43 @@ static struct nfc_phy_ops i2c_phy_ops = { .disable = st_nci_i2c_disable, }; +static int st_nci_i2c_acpi_request_resources(struct i2c_client *client) +{ + struct st_nci_i2c_phy *phy = i2c_get_clientdata(client); + const struct acpi_device_id *id; + struct gpio_desc *gpiod_reset; + struct device *dev; + + if (!client) + return -EINVAL; + + dev = &client->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* Get RESET GPIO from ACPI */ + gpiod_reset = devm_gpiod_get_index(dev, ST_NCI_GPIO_NAME_RESET, 1, + GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_reset)) { + nfc_err(dev, "Unable to get RESET GPIO\n"); + return -ENODEV; + } + + phy->gpio_reset = desc_to_gpio(gpiod_reset); + + phy->irq_polarity = irq_get_trigger_type(client->irq); + + phy->se_status.is_ese_present = + device_property_present(dev, "ese-present"); + phy->se_status.is_uicc_present = + device_property_present(dev, "uicc-present"); + + return 0; +} + static int st_nci_i2c_of_request_resources(struct i2c_client *client) { struct st_nci_i2c_phy *phy = i2c_get_clientdata(client); @@ -312,6 +351,12 @@ static int st_nci_i2c_probe(struct i2c_client *client, "Cannot get platform resources\n"); return r; } + } else if (ACPI_HANDLE(&client->dev)) { + r = st_nci_i2c_acpi_request_resources(client); + if (r) { + nfc_err(&client->dev, "Cannot get ACPI data\n"); + return r; + } } else { nfc_err(&client->dev, "st_nci platform resources not available\n"); @@ -354,6 +399,13 @@ static struct i2c_device_id st_nci_i2c_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table); +static const struct acpi_device_id st_nci_i2c_acpi_match[] = { + {"SMO2101"}, + {"SMO2102"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, st_nci_i2c_acpi_match); + static const struct of_device_id of_st_nci_i2c_match[] = { { .compatible = "st,st21nfcb-i2c", }, { .compatible = "st,st21nfcb_i2c", }, @@ -367,6 +419,7 @@ static struct i2c_driver st_nci_i2c_driver = { .owner = THIS_MODULE, .name = ST_NCI_I2C_DRIVER_NAME, .of_match_table = of_match_ptr(of_st_nci_i2c_match), + .acpi_match_table = ACPI_PTR(st_nci_i2c_acpi_match), }, .probe = st_nci_i2c_probe, .id_table = st_nci_i2c_id_table, -- cgit v1.2.3-59-g8ed1b From 60cd6d89319f7f3854bad5f1bad570d9f135b03c Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:10 +0100 Subject: nfc: st-nci: Add support for acpi probing for spi device. Add support for acpi probing. SMO2101 is used for st21nfcb It has been tested with the following acpi node on Minnowboard: Note: Remove uicc-present or ese-present Package if one of them is not supported. Device (NFC1) { Name (_ADR, Zero) // _ADR: Address Name (_HID, "SMO2101") // _HID: Hardware ID Name (_CID, "SMO2101") // _CID: Compatible ID Name (_DDN, "SMO NFC") // _DDN: DOS Device Name Name (_UID, One) // _UID: Unique ID Name (_DSD, Package (0x02) { /* Device Properties for _DSD */ ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package (0x02) { Package (0x02) { "uicc-present", 1 }, Package (0x02) { "ese-present", 1 } } }) Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings { Name (SBUF, ResourceTemplate () { SpiSerialBus (0, PolarityLow, FourWireMode, 8, ControllerInitiated, 4000000, ClockPolarityLow, ClockPhaseFirst, "\\_SB.SPI1", 0x00, ResourceConsumer, ,) GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullNone, 0x0000, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0001 } GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0002, } }) Return (SBUF) /* \_SB_.SPI1.NFC1._CRS.SBUF */ } Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x0F) } } Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st-nci/spi.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'drivers') diff --git a/drivers/nfc/st-nci/spi.c b/drivers/nfc/st-nci/spi.c index 7c3684b84cb8..df02847e5f86 100644 --- a/drivers/nfc/st-nci/spi.c +++ b/drivers/nfc/st-nci/spi.c @@ -20,8 +20,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -221,6 +223,43 @@ static struct nfc_phy_ops spi_phy_ops = { .disable = st_nci_spi_disable, }; +static int st_nci_spi_acpi_request_resources(struct spi_device *spi_dev) +{ + struct st_nci_spi_phy *phy = spi_get_drvdata(spi_dev); + const struct acpi_device_id *id; + struct gpio_desc *gpiod_reset; + struct device *dev; + + if (!spi_dev) + return -EINVAL; + + dev = &spi_dev->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* Get RESET GPIO from ACPI */ + gpiod_reset = devm_gpiod_get_index(dev, ST_NCI_GPIO_NAME_RESET, 1, + GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_reset)) { + nfc_err(dev, "Unable to get RESET GPIO\n"); + return -ENODEV; + } + + phy->gpio_reset = desc_to_gpio(gpiod_reset); + + phy->irq_polarity = irq_get_trigger_type(spi_dev->irq); + + phy->se_status.is_ese_present = + device_property_present(dev, "ese-present"); + phy->se_status.is_uicc_present = + device_property_present(dev, "uicc-present"); + + return 0; +} + static int st_nci_spi_of_request_resources(struct spi_device *dev) { struct st_nci_spi_phy *phy = spi_get_drvdata(dev); @@ -328,6 +367,12 @@ static int st_nci_spi_probe(struct spi_device *dev) "Cannot get platform resources\n"); return r; } + } else if (ACPI_HANDLE(&dev->dev)) { + r = st_nci_spi_acpi_request_resources(dev); + if (r) { + nfc_err(&dev->dev, "Cannot get ACPI data\n"); + return r; + } } else { nfc_err(&dev->dev, "st_nci platform resources not available\n"); @@ -370,6 +415,12 @@ static struct spi_device_id st_nci_spi_id_table[] = { }; MODULE_DEVICE_TABLE(spi, st_nci_spi_id_table); +static const struct acpi_device_id st_nci_spi_acpi_match[] = { + {"SMO2101", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, st_nci_spi_acpi_match); + static const struct of_device_id of_st_nci_spi_match[] = { { .compatible = "st,st21nfcb-spi", }, {} @@ -380,6 +431,7 @@ static struct spi_driver st_nci_spi_driver = { .driver = { .name = ST_NCI_SPI_DRIVER_NAME, .of_match_table = of_match_ptr(of_st_nci_spi_match), + .acpi_match_table = ACPI_PTR(st_nci_spi_acpi_match), }, .probe = st_nci_spi_probe, .id_table = st_nci_spi_id_table, -- cgit v1.2.3-59-g8ed1b From dfa8070d7f641e1fb1b5e5fe643faddfa4869e07 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:11 +0100 Subject: nfc: st21nfca: Add support for acpi probing for i2c device. Add support for acpi probing. SMO2100 is used for st21nfca It has been tested with the following acpi node on Minnowboard Max: Note: Remove uicc-present or ese-present Package if one them is not supported. Device (NFC1) { Name (_ADR, Zero) // _ADR: Address Name (_HID, "SMO2100") // _HID: Hardware ID Name (_CID, "SMO2100") // _CID: Compatible ID Name (_DDN, "SMO NFC") // _DDN: DOS Device Name Name (_UID, One) // _UID: Unique ID Name (_DSD, Package (0x02) { /* Device Properties for _DSD */ ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package (0x02) { Package (0x02) { "uicc-present", 1 }, Package (0x02) { "ese-present", 1 } } }) Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings { Name (SBUF, ResourceTemplate () { I2cSerialBus (0x0001, ControllerInitiated, 400000, AddressingMode7Bit, "\\_SB.I2C7", 0x00, ResourceConsumer, ,) GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullNone, 0x0000, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0001 } GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0002, } }) Return (SBUF) /* \_SB_.I2C7.NFC1._CRS.SBUF */ } Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x0F) } } Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 51 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index 831e20d4da65..ae1653444549 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -21,8 +21,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -205,7 +207,6 @@ static int st21nfca_hci_i2c_write(void *phy_id, struct sk_buff *skb) I2C_DUMP_SKB("st21nfca_hci_i2c_write", skb); - if (phy->hard_fault != 0) return phy->hard_fault; @@ -504,6 +505,41 @@ static struct nfc_phy_ops i2c_phy_ops = { .disable = st21nfca_hci_i2c_disable, }; +static int st21nfca_hci_i2c_acpi_request_resources(struct i2c_client *client) +{ + struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); + const struct acpi_device_id *id; + struct gpio_desc *gpiod_ena; + struct device *dev; + + if (!client) + return -EINVAL; + + dev = &client->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* Get EN GPIO from ACPI */ + gpiod_ena = devm_gpiod_get_index(dev, ST21NFCA_GPIO_NAME_EN, 1, + GPIOD_OUT_LOW); + if (!IS_ERR(gpiod_ena)) + phy->gpio_ena = desc_to_gpio(gpiod_ena); + + phy->gpio_ena = desc_to_gpio(gpiod_ena); + + phy->irq_polarity = irq_get_trigger_type(client->irq); + + phy->se_status.is_ese_present = + device_property_present(dev, "ese-present"); + phy->se_status.is_uicc_present = + device_property_present(dev, "uicc-present"); + + return 0; +} + static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) { struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); @@ -617,6 +653,12 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client, nfc_err(&client->dev, "Cannot get platform resources\n"); return r; } + } else if (ACPI_HANDLE(&client->dev)) { + r = st21nfca_hci_i2c_acpi_request_resources(client); + if (r) { + nfc_err(&client->dev, "Cannot get ACPI data\n"); + return r; + } } else { nfc_err(&client->dev, "st21nfca platform resources not available\n"); return -ENODEV; @@ -665,6 +707,12 @@ static struct i2c_device_id st21nfca_hci_i2c_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table); +static const struct acpi_device_id st21nfca_hci_i2c_acpi_match[] = { + {"SMO2100", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, st21nfca_hci_i2c_acpi_match); + static const struct of_device_id of_st21nfca_i2c_match[] = { { .compatible = "st,st21nfca-i2c", }, { .compatible = "st,st21nfca_i2c", }, @@ -677,6 +725,7 @@ static struct i2c_driver st21nfca_hci_i2c_driver = { .owner = THIS_MODULE, .name = ST21NFCA_HCI_I2C_DRIVER_NAME, .of_match_table = of_match_ptr(of_st21nfca_i2c_match), + .acpi_match_table = ACPI_PTR(st21nfca_hci_i2c_acpi_match), }, .probe = st21nfca_hci_i2c_probe, .id_table = st21nfca_hci_i2c_id_table, -- cgit v1.2.3-59-g8ed1b From ba2c231cbcbd5fdd6335b0839f7bf4a08f208e4e Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:12 +0100 Subject: nfc: st-nci: Code cleanup A few code cleanups, mostly empty lines removal. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st-nci/i2c.c | 1 - drivers/nfc/st-nci/se.c | 1 - drivers/nfc/st-nci/spi.c | 3 +-- 3 files changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c index 3e384159b288..8a56b5c6e4c4 100644 --- a/drivers/nfc/st-nci/i2c.c +++ b/drivers/nfc/st-nci/i2c.c @@ -425,7 +425,6 @@ static struct i2c_driver st_nci_i2c_driver = { .id_table = st_nci_i2c_id_table, .remove = st_nci_i2c_remove, }; - module_i2c_driver(st_nci_i2c_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c index dbab722a0654..aa692dabee76 100644 --- a/drivers/nfc/st-nci/se.c +++ b/drivers/nfc/st-nci/se.c @@ -392,7 +392,6 @@ void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe, } EXPORT_SYMBOL_GPL(st_nci_hci_event_received); - void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, struct sk_buff *skb) { diff --git a/drivers/nfc/st-nci/spi.c b/drivers/nfc/st-nci/spi.c index df02847e5f86..821dfa950fa8 100644 --- a/drivers/nfc/st-nci/spi.c +++ b/drivers/nfc/st-nci/spi.c @@ -36,7 +36,7 @@ /* ndlc header */ #define ST_NCI_FRAME_HEADROOM 1 -#define ST_NCI_FRAME_TAILROOM 0 +#define ST_NCI_FRAME_TAILROOM 0 #define ST_NCI_SPI_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */ #define ST_NCI_SPI_MAX_SIZE 250 /* req 4.2.1 */ @@ -437,7 +437,6 @@ static struct spi_driver st_nci_spi_driver = { .id_table = st_nci_spi_id_table, .remove = st_nci_spi_remove, }; - module_spi_driver(st_nci_spi_driver); MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From a6e57ec6d96914d874b8809e0e1432a4dff23e45 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:13 +0100 Subject: nfc: st21nfca: Code cleanup A few code cleanups, mostly empty lines removal. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 1 - drivers/nfc/st21nfca/se.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index ae1653444549..75ab2ef3253b 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -731,7 +731,6 @@ static struct i2c_driver st21nfca_hci_i2c_driver = { .id_table = st21nfca_hci_i2c_id_table, .remove = st21nfca_hci_i2c_remove, }; - module_i2c_driver(st21nfca_hci_i2c_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c index c79d99b24c96..d5172e46dbaf 100644 --- a/drivers/nfc/st21nfca/se.c +++ b/drivers/nfc/st21nfca/se.c @@ -312,7 +312,7 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, switch (event) { case ST21NFCA_EVT_CONNECTIVITY: - break; + break; case ST21NFCA_EVT_TRANSACTION: /* * According to specification etsi 102 622 @@ -342,7 +342,7 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, transaction->aid_len + 4, transaction->params_len); r = nfc_se_transaction(hdev->ndev, host, transaction); - break; + break; default: nfc_err(&hdev->ndev->dev, "Unexpected event on connectivity gate\n"); return 1; -- cgit v1.2.3-59-g8ed1b From 4940d1c35535c32e6d6e950d91efa485eab41d76 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:14 +0100 Subject: nfc: st21nfca: Remove useless pr_info in st21nfca_hci_i2c_disable Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index 75ab2ef3253b..1f44a151d206 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -164,7 +164,6 @@ static void st21nfca_hci_i2c_disable(void *phy_id) { struct st21nfca_i2c_phy *phy = phy_id; - pr_info("\n"); gpio_set_value(phy->gpio_ena, 0); phy->powered = 0; -- cgit v1.2.3-59-g8ed1b From 9ba04ebf82ba26d848a74e0e1c72ddf09d5a1265 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:16 +0100 Subject: NFC: st-nci: Auto-select core module The core st-nci module is useless without either the I2C or the SPI access module. So hide NFC_ST_NCI and select it automatically if either NFC_ST_NCI_I2C or NFC_ST_NCI_SPI is selected. This avoids presenting NFC_ST_NCI when neither NFC_ST_NCI_I2C nor NFC_ST_NCI_SPI can be selected. Cc: Jean Delvare Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st-nci/Kconfig | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/st-nci/Kconfig b/drivers/nfc/st-nci/Kconfig index e7c6db9c5860..dc9b777d78f6 100644 --- a/drivers/nfc/st-nci/Kconfig +++ b/drivers/nfc/st-nci/Kconfig @@ -1,19 +1,14 @@ config NFC_ST_NCI - tristate "STMicroelectronics ST NCI NFC driver" - depends on NFC_NCI - default n + tristate ---help--- STMicroelectronics NFC NCI chips core driver. It implements the chipset NCI logic and hooks into the NFC kernel APIs. Physical layers will register against it. - To compile this driver as a module, choose m here. The module will - be called st-nci. - Say N if unsure. - config NFC_ST_NCI_I2C - tristate "NFC ST NCI i2c support" - depends on NFC_ST_NCI && I2C + tristate "STMicroelectronics ST NCI NFC driver (I2C)" + depends on NFC_NCI && I2C + select NFC_ST_NCI ---help--- This module adds support for an I2C interface to the STMicroelectronics NFC NCI chips familly. @@ -23,8 +18,9 @@ config NFC_ST_NCI_I2C Say N if unsure. config NFC_ST_NCI_SPI - tristate "NFC ST NCI spi support" - depends on NFC_ST_NCI && SPI + tristate "STMicroelectronics ST NCI NFC driver (SPI)" + depends on NFC_NCI && SPI + select NFC_ST_NCI ---help--- This module adds support for an SPI interface to the STMicroelectronics NFC NCI chips familly. -- cgit v1.2.3-59-g8ed1b From dfa4089b3a3f3ac8bea847f968c92b89fbbf107c Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:17 +0100 Subject: NFC: st21nfca: Auto-select core module The core st21nca module is useless without the I2C access module. So hide NFC_ST21NFCA and select it automatically if either NFC_ST21NFCA_I2C is selected. This avoids presenting NFC_ST21NFCA when NFC_ST21NFCA_I2C can't be selected. Cc: Jean Delvare Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/Kconfig | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/st21nfca/Kconfig b/drivers/nfc/st21nfca/Kconfig index ee459f066ade..cc3bd5658901 100644 --- a/drivers/nfc/st21nfca/Kconfig +++ b/drivers/nfc/st21nfca/Kconfig @@ -1,20 +1,15 @@ config NFC_ST21NFCA - tristate "STMicroelectronics ST21NFCA NFC driver" - depends on NFC_HCI + tristate select CRC_CCITT - default n ---help--- STMicroelectronics ST21NFCA core driver. It implements the chipset HCI logic and hooks into the NFC kernel APIs. Physical layers will register against it. - To compile this driver as a module, choose m here. The module will - be called st21nfca. - Say N if unsure. - config NFC_ST21NFCA_I2C - tristate "NFC ST21NFCA i2c support" - depends on NFC_ST21NFCA && I2C && NFC_SHDLC + tristate "STMicroelectronics ST21NFCA NFC driver (I2C)" + depends on NFC_HCI && I2C && NFC_SHDLC + select NFC_ST21NFCA ---help--- This module adds support for the STMicroelectronics st21nfca i2c interface. Select this if your platform is using the i2c bus. -- cgit v1.2.3-59-g8ed1b From 25960c2176112f39e8862e692e07cad918c06707 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:19 +0100 Subject: nfc: st-nci: Add support for HCI event connectivity Add support for connectivity event Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st-nci/se.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c index aa692dabee76..a53e5df803eb 100644 --- a/drivers/nfc/st-nci/se.c +++ b/drivers/nfc/st-nci/se.c @@ -331,7 +331,7 @@ static int st_nci_hci_connectivity_event_received(struct nci_dev *ndev, switch (event) { case ST_NCI_EVT_CONNECTIVITY: - + r = nfc_se_connectivity(ndev->nfc_dev, host); break; case ST_NCI_EVT_TRANSACTION: /* According to specification etsi 102 622 -- cgit v1.2.3-59-g8ed1b From 72c54c42b2943985505966ece29f2d45b0330f26 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:20 +0100 Subject: nfc: st21nfca: Add support for HCI event connectivity Add support for connectivity event Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/se.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c index d5172e46dbaf..bd56a16e4007 100644 --- a/drivers/nfc/st21nfca/se.c +++ b/drivers/nfc/st21nfca/se.c @@ -312,6 +312,7 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, switch (event) { case ST21NFCA_EVT_CONNECTIVITY: + r = nfc_se_connectivity(hdev->ndev, host); break; case ST21NFCA_EVT_TRANSACTION: /* -- cgit v1.2.3-59-g8ed1b From 0b0a264df5d3854c3e6411c28616d2148c897bad Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:22 +0100 Subject: nfc: fdp: Move i2c client irq checking It is cleaner to check if the i2c_client irq is not configured properly before allocating any data. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/fdp/i2c.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c index 532db28145c7..5e797d5c38ed 100644 --- a/drivers/nfc/fdp/i2c.c +++ b/drivers/nfc/fdp/i2c.c @@ -298,6 +298,12 @@ static int fdp_nci_i2c_probe(struct i2c_client *client, return -ENODEV; } + /* Checking if we have an irq */ + if (client->irq <= 0) { + nfc_err(dev, "IRQ not present\n"); + return -ENODEV; + } + phy = devm_kzalloc(dev, sizeof(struct fdp_i2c_phy), GFP_KERNEL); if (!phy) @@ -307,12 +313,6 @@ static int fdp_nci_i2c_probe(struct i2c_client *client, phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD; i2c_set_clientdata(client, phy); - /* Checking if we have an irq */ - if (client->irq <= 0) { - dev_err(dev, "IRQ not present\n"); - return -ENODEV; - } - r = request_threaded_irq(client->irq, NULL, fdp_nci_i2c_irq_thread_fn, IRQF_TRIGGER_RISING | IRQF_ONESHOT, FDP_I2C_DRIVER_NAME, phy); -- cgit v1.2.3-59-g8ed1b From 3897de6a6eb74ff8f25c2021431bedd4ba138685 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:23 +0100 Subject: nfc: microread: Remove useless irq field In microread_i2c_phy, irq field is never used. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/microread/i2c.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c index daf352597ef8..918e8f2eac47 100644 --- a/drivers/nfc/microread/i2c.c +++ b/drivers/nfc/microread/i2c.c @@ -50,8 +50,6 @@ struct microread_i2c_phy { struct i2c_client *i2c_dev; struct nfc_hci_dev *hdev; - int irq; - int hard_fault; /* * < 0 if hardware error occured (e.g. i2c err) * and prevents normal operation. -- cgit v1.2.3-59-g8ed1b From be103b714e4e6ad7316b0cb9b9d473ddedaf4ad6 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:25 +0100 Subject: nfc: nxp-nci: Remove i2c client gpio irq configuration gpio irq is already configured by the core i2c layers when reaching the probe function. Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/nxp-nci/i2c.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index c71adc3254df..11520f472f98 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -52,7 +52,6 @@ struct nxp_nci_i2c_phy { unsigned int gpio_en; unsigned int gpio_fw; - unsigned int gpio_irq; int hard_fault; /* * < 0 if hardware error occurred (e.g. i2c err) @@ -292,39 +291,24 @@ static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) } phy->gpio_fw = r; - r = irq_of_parse_and_map(pp, 0); - if (r < 0) { - nfc_err(&client->dev, "Unable to get irq, error: %d\n", r); - return r; - } - client->irq = r; - return 0; } static int nxp_nci_i2c_acpi_config(struct nxp_nci_i2c_phy *phy) { struct i2c_client *client = phy->i2c_dev; - struct gpio_desc *gpiod_en, *gpiod_fw, *gpiod_irq; + struct gpio_desc *gpiod_en, *gpiod_fw; gpiod_en = devm_gpiod_get_index(&client->dev, NULL, 2, GPIOD_OUT_LOW); gpiod_fw = devm_gpiod_get_index(&client->dev, NULL, 1, GPIOD_OUT_LOW); - gpiod_irq = devm_gpiod_get_index(&client->dev, NULL, 0, GPIOD_IN); - if (IS_ERR(gpiod_en) || IS_ERR(gpiod_fw) || IS_ERR(gpiod_irq)) { + if (IS_ERR(gpiod_en) || IS_ERR(gpiod_fw)) { nfc_err(&client->dev, "No GPIOs\n"); return -EINVAL; } - client->irq = gpiod_to_irq(gpiod_irq); - if (client->irq < 0) { - nfc_err(&client->dev, "No IRQ\n"); - return -EINVAL; - } - phy->gpio_en = desc_to_gpio(gpiod_en); phy->gpio_fw = desc_to_gpio(gpiod_fw); - phy->gpio_irq = desc_to_gpio(gpiod_irq); return 0; } @@ -363,7 +347,6 @@ static int nxp_nci_i2c_probe(struct i2c_client *client, } else if (pdata) { phy->gpio_en = pdata->gpio_en; phy->gpio_fw = pdata->gpio_fw; - client->irq = pdata->irq; } else if (ACPI_HANDLE(&client->dev)) { r = nxp_nci_i2c_acpi_config(phy); if (r < 0) -- cgit v1.2.3-59-g8ed1b From 97b69788971de8af994ba570d56c455e79eaf35e Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Dec 2015 23:45:26 +0100 Subject: nfc: pn544: Remove i2c client gpio irq configuration gpio irq is already configured by the core i2c layers when reaching the probe function Signed-off-by: Christophe Ricard Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/i2c.c | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index 534c79fee1b7..76c318444304 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -166,7 +166,6 @@ struct pn544_i2c_phy { struct nfc_hci_dev *hdev; unsigned int gpio_en; - unsigned int gpio_irq; unsigned int gpio_fw; unsigned int en_polarity; @@ -879,9 +878,8 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) { struct pn544_i2c_phy *phy = i2c_get_clientdata(client); const struct acpi_device_id *id; - struct gpio_desc *gpiod_en, *gpiod_irq, *gpiod_fw; + struct gpio_desc *gpiod_en, *gpiod_fw; struct device *dev; - int ret; if (!client) return -EINVAL; @@ -914,27 +912,6 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) phy->gpio_fw = desc_to_gpio(gpiod_fw); - /* Get IRQ GPIO */ - gpiod_irq = devm_gpiod_get_index(dev, PN544_GPIO_NAME_IRQ, 0, - GPIOD_IN); - if (IS_ERR(gpiod_irq)) { - nfc_err(dev, "Unable to get IRQ GPIO\n"); - return -ENODEV; - } - - phy->gpio_irq = desc_to_gpio(gpiod_irq); - - /* Map the pin to an IRQ */ - ret = gpiod_to_irq(gpiod_irq); - if (ret < 0) { - nfc_err(dev, "Fail pin IRQ mapping\n"); - return ret; - } - - nfc_info(dev, "GPIO resource, no:%d irq:%d\n", - desc_to_gpio(gpiod_irq), ret); - client->irq = ret; - return 0; } @@ -994,15 +971,6 @@ static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) goto err_gpio_fw; } - /* IRQ */ - ret = irq_of_parse_and_map(pp, 0); - if (ret < 0) { - nfc_err(&client->dev, - "Unable to get irq, error: %d\n", ret); - goto err_gpio_fw; - } - client->irq = ret; - return 0; err_gpio_fw: @@ -1065,7 +1033,6 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET); - phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ); /* Using ACPI */ } else if (ACPI_HANDLE(&client->dev)) { r = pn544_hci_i2c_acpi_request_resources(client); -- cgit v1.2.3-59-g8ed1b From bd0ed561356db935d0b5a68e2be32d976e6963c4 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sun, 27 Dec 2015 18:45:57 +0800 Subject: atm: solos-pci: use to_pci_dev() Use to_pci_dev() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: David S. Miller --- drivers/atm/solos-pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 0c2b4ba06813..6ac2b2b1e8de 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -525,7 +525,7 @@ struct geos_gpio_attr { static ssize_t geos_gpio_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct pci_dev *pdev = to_pci_dev(dev); struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr); struct solos_card *card = pci_get_drvdata(pdev); uint32_t data32; @@ -551,7 +551,7 @@ static ssize_t geos_gpio_store(struct device *dev, struct device_attribute *attr static ssize_t geos_gpio_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct pci_dev *pdev = to_pci_dev(dev); struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr); struct solos_card *card = pci_get_drvdata(pdev); uint32_t data32; @@ -565,7 +565,7 @@ static ssize_t geos_gpio_show(struct device *dev, struct device_attribute *attr, static ssize_t hardware_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct pci_dev *pdev = to_pci_dev(dev); struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr); struct solos_card *card = pci_get_drvdata(pdev); uint32_t data32; -- cgit v1.2.3-59-g8ed1b From da0bcb4e36411c1e9e327359c36507e2e04964a1 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sun, 27 Dec 2015 21:15:44 +0800 Subject: net: hns: use to_platform_device() Use to_platform_device() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c index 8c30cec8850a..d2263c72bd8a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -499,8 +499,7 @@ void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common) int base_irq_idx = hns_rcb_get_base_irq_idx(rcb_common); struct device_node *np = rcb_common->dsaf_dev->dev->of_node; struct platform_device *pdev = - container_of(rcb_common->dsaf_dev->dev, - struct platform_device, dev); + to_platform_device(rcb_common->dsaf_dev->dev); bool is_ver1 = AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver); for (i = 0; i < ring_num; i++) { -- cgit v1.2.3-59-g8ed1b From d0f71afffa1c3d5a36a4a278f1dbbd2643176dc3 Mon Sep 17 00:00:00 2001 From: William Dauchy Date: Fri, 30 Oct 2015 18:16:30 +0100 Subject: ixgbevf: Fix handling of NAPI budget when multiple queues are enabled per vector This is the same patch as for ixgbe but applied differently according to busy polling. See commit 5d6002b7b822c74 ("ixgbe: Fix handling of NAPI budget when multiple queues are enabled per vector") Signed-off-by: William Dauchy Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index f098952d4fb4..478c0f1f12bb 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1016,6 +1016,8 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget) ixgbevf_for_each_ring(ring, q_vector->tx) clean_complete &= ixgbevf_clean_tx_irq(q_vector, ring); + if (budget <= 0) + return budget; #ifdef CONFIG_NET_RX_BUSY_POLL if (!ixgbevf_qv_lock_napi(q_vector)) return budget; -- cgit v1.2.3-59-g8ed1b From 9ad3d6f7eb300d464bfce2c80e7b1594f5e5eff9 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Wed, 4 Nov 2015 16:02:21 -0800 Subject: ixgbevf: minor cleanups for ixgbevf_set_itr() adapter->rx_itr_setting is not a mask so check it with == instead of & do not default to 12K interrupts in ixgbevf_set_itr() There should be no functional effect from these changes. Signed-off-by: Emil Tantilov Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 478c0f1f12bb..3558f019b631 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1047,7 +1047,7 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget) return budget; /* all work done, exit the polling mode */ napi_complete_done(napi, work_done); - if (adapter->rx_itr_setting & 1) + if (adapter->rx_itr_setting == 1) ixgbevf_set_itr(q_vector); if (!test_bit(__IXGBEVF_DOWN, &adapter->state) && !test_bit(__IXGBEVF_REMOVING, &adapter->state)) @@ -1250,9 +1250,10 @@ static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector) new_itr = IXGBE_20K_ITR; break; case bulk_latency: - default: new_itr = IXGBE_12K_ITR; break; + default: + break; } if (new_itr != q_vector->itr) { -- cgit v1.2.3-59-g8ed1b From af56b4d865bf40e031df9118b0663ebf406ff121 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Mon, 9 Nov 2015 15:07:12 -0800 Subject: ixgbe: add support for QSFP PHY types in ixgbe_get_settings() Add missing QSFP PHY types to allow for more accurate reporting of port settings. Signed-off-by: Emil Tantilov Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 1ed4c9add00d..e10d197b6a04 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -225,6 +225,10 @@ static int ixgbe_get_settings(struct net_device *netdev, case ixgbe_phy_sfp_avago: case ixgbe_phy_sfp_intel: case ixgbe_phy_sfp_unknown: + case ixgbe_phy_qsfp_passive_unknown: + case ixgbe_phy_qsfp_active_unknown: + case ixgbe_phy_qsfp_intel: + case ixgbe_phy_qsfp_unknown: /* SFP+ devices, further checking needed */ switch (adapter->hw.phy.sfp_type) { case ixgbe_sfp_type_da_cu: -- cgit v1.2.3-59-g8ed1b From 695b816d1aeb09505f499ec7cc5e90657c8c11ac Mon Sep 17 00:00:00 2001 From: Veola Nazareth Date: Wed, 11 Nov 2015 16:22:59 -0700 Subject: ixgbe: report correct media type for KR, KX and KX4 interfaces Ethtool reports backplane type interfaces as 1000/10000baseT link modes. This has been corrected to report the media as KR, KX or KX4 based on the backplane interface present. Signed-off-by: Veola Nazareth Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 55 ++++++++++++++++++------ 1 file changed, 42 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index e10d197b6a04..2448eba2eecd 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -151,6 +151,34 @@ static const char ixgbe_gstrings_test[][ETH_GSTRING_LEN] = { }; #define IXGBE_TEST_LEN sizeof(ixgbe_gstrings_test) / ETH_GSTRING_LEN +/* currently supported speeds for 10G */ +#define ADVRTSD_MSK_10G (SUPPORTED_10000baseT_Full | \ + SUPPORTED_10000baseKX4_Full | \ + SUPPORTED_10000baseKR_Full) + +#define ixgbe_isbackplane(type) ((type) == ixgbe_media_type_backplane) + +static u32 ixgbe_get_supported_10gtypes(struct ixgbe_hw *hw) +{ + if (!ixgbe_isbackplane(hw->phy.media_type)) + return SUPPORTED_10000baseT_Full; + + switch (hw->device_id) { + case IXGBE_DEV_ID_82598: + case IXGBE_DEV_ID_82599_KX4: + case IXGBE_DEV_ID_82599_KX4_MEZZ: + case IXGBE_DEV_ID_X550EM_X_KX4: + return SUPPORTED_10000baseKX4_Full; + case IXGBE_DEV_ID_82598_BX: + case IXGBE_DEV_ID_82599_KR: + case IXGBE_DEV_ID_X550EM_X_KR: + return SUPPORTED_10000baseKR_Full; + default: + return SUPPORTED_10000baseKX4_Full | + SUPPORTED_10000baseKR_Full; + } +} + static int ixgbe_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { @@ -165,29 +193,30 @@ static int ixgbe_get_settings(struct net_device *netdev, /* set the supported link speeds */ if (supported_link & IXGBE_LINK_SPEED_10GB_FULL) - ecmd->supported |= SUPPORTED_10000baseT_Full; + ecmd->supported |= ixgbe_get_supported_10gtypes(hw); if (supported_link & IXGBE_LINK_SPEED_1GB_FULL) ecmd->supported |= SUPPORTED_1000baseT_Full; if (supported_link & IXGBE_LINK_SPEED_100_FULL) - ecmd->supported |= SUPPORTED_100baseT_Full; + ecmd->supported |= ixgbe_isbackplane(hw->phy.media_type) ? + SUPPORTED_1000baseKX_Full : + SUPPORTED_1000baseT_Full; + /* default advertised speed if phy.autoneg_advertised isn't set */ + ecmd->advertising = ecmd->supported; /* set the advertised speeds */ if (hw->phy.autoneg_advertised) { + ecmd->advertising = 0; if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL) ecmd->advertising |= ADVERTISED_100baseT_Full; if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) - ecmd->advertising |= ADVERTISED_10000baseT_Full; - if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) - ecmd->advertising |= ADVERTISED_1000baseT_Full; + ecmd->advertising |= ecmd->supported & ADVRTSD_MSK_10G; + if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) { + if (ecmd->supported & SUPPORTED_1000baseKX_Full) + ecmd->advertising |= ADVERTISED_1000baseKX_Full; + else + ecmd->advertising |= ADVERTISED_1000baseT_Full; + } } else { - /* default modes in case phy.autoneg_advertised isn't set */ - if (supported_link & IXGBE_LINK_SPEED_10GB_FULL) - ecmd->advertising |= ADVERTISED_10000baseT_Full; - if (supported_link & IXGBE_LINK_SPEED_1GB_FULL) - ecmd->advertising |= ADVERTISED_1000baseT_Full; - if (supported_link & IXGBE_LINK_SPEED_100_FULL) - ecmd->advertising |= ADVERTISED_100baseT_Full; - if (hw->phy.multispeed_fiber && !autoneg) { if (supported_link & IXGBE_LINK_SPEED_10GB_FULL) ecmd->advertising = ADVERTISED_10000baseT_Full; -- cgit v1.2.3-59-g8ed1b From fb8ad4a592c627783dc18cc147c7f4de55cf318d Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Wed, 18 Nov 2015 15:37:04 -0800 Subject: ixgbe: Clean up redundancy in hw_enc_features Clean up minor redundancy in the setting of hw_enc_features that makes it appears that X550 uniquely has more encapsulation features than other devices. The driver only supports one more feature, so make it look that way. No longer set NETIF_F_SG since that is set by the register_netdev call. Thanks to Alex Duyck for noticing this slight confusion. Reported-by: Alexander Duyck Signed-off-by: Mark Rustad Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index c5c0fb4ddf9f..ea9537d0e63a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9015,8 +9015,7 @@ skip_sriov: netdev->vlan_features |= NETIF_F_IPV6_CSUM; netdev->vlan_features |= NETIF_F_SG; - netdev->hw_enc_features |= NETIF_F_SG | NETIF_F_IP_CSUM | - NETIF_F_IPV6_CSUM; + netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; netdev->priv_flags |= IFF_UNICAST_FLT; netdev->priv_flags |= IFF_SUPP_NOFCS; @@ -9025,9 +9024,7 @@ skip_sriov: switch (adapter->hw.mac.type) { case ixgbe_mac_X550: case ixgbe_mac_X550EM_x: - netdev->hw_enc_features |= NETIF_F_RXCSUM | - NETIF_F_IP_CSUM | - NETIF_F_IPV6_CSUM; + netdev->hw_enc_features |= NETIF_F_RXCSUM; break; default: break; -- cgit v1.2.3-59-g8ed1b From e9ee3238f8a480bbca58e51d02a93628d7c1f265 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Fri, 20 Nov 2015 13:02:16 -0800 Subject: ixgbe: fix RSS limit for X550 X550 allows for up to 64 RSS queues, but the driver can have max of 63 (-1 MSIX vector for link). On systems with >= 64 CPUs the driver will set the redirection table for all 64 queues which will result in packets being dropped. Signed-off-by: Emil Tantilov Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index f4c9a42dafcf..4b9156cd8b93 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -317,7 +317,7 @@ enum ixgbe_ring_f_enum { }; #define IXGBE_MAX_RSS_INDICES 16 -#define IXGBE_MAX_RSS_INDICES_X550 64 +#define IXGBE_MAX_RSS_INDICES_X550 63 #define IXGBE_MAX_VMDQ_INDICES 64 #define IXGBE_MAX_FDIR_INDICES 63 /* based on q_vector limit */ #define IXGBE_MAX_FCOE_INDICES 8 -- cgit v1.2.3-59-g8ed1b From 3ca2b2506ec9a3b1615930a6810d30ec9aba10a1 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Fri, 20 Nov 2015 13:12:17 -0800 Subject: ixgbe: Correct X550EM_x revision check The X550EM_x revision check needs to check a value, not just a bit. Use a mask and check the value. Also remove the redundant check inside the ixgbe_enter_lplu_t_x550em, because it can only be called when both the mac type and revision check pass. Signed-off-by: Mark Rustad Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 06add27c8b8c..5f53cc6c609a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3520,7 +3520,7 @@ struct ixgbe_info { #define IXGBE_FUSES0_GROUP(_i) (0x11158 + ((_i) * 4)) #define IXGBE_FUSES0_300MHZ BIT(5) -#define IXGBE_FUSES0_REV1 BIT(6) +#define IXGBE_FUSES0_REV_MASK (3 << 6) #define IXGBE_KRM_PORT_CAR_GEN_CTRL(P) ((P) ? 0x8010 : 0x4010) #define IXGBE_KRM_LINK_CTRL_1(P) ((P) ? 0x820C : 0x420C) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index f4ef0d1a5dbe..87aca3f7c3de 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1857,10 +1857,6 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw) u32 save_autoneg; bool link_up; - /* SW LPLU not required on later HW revisions. */ - if (IXGBE_FUSES0_REV1 & IXGBE_READ_REG(hw, IXGBE_FUSES0_GROUP(0))) - return 0; - /* If blocked by MNG FW, then don't restart AN */ if (ixgbe_check_reset_blocked(hw)) return 0; @@ -2000,8 +1996,9 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) ixgbe_setup_internal_phy_t_x550em; /* setup SW LPLU only for first revision */ - if (!(IXGBE_FUSES0_REV1 & IXGBE_READ_REG(hw, - IXGBE_FUSES0_GROUP(0)))) + if (hw->mac.type == ixgbe_mac_X550EM_x && + !(IXGBE_READ_REG(hw, IXGBE_FUSES0_GROUP(0)) & + IXGBE_FUSES0_REV_MASK)) phy->ops.enter_lplu = ixgbe_enter_lplu_t_x550em; phy->ops.handle_lasi = ixgbe_handle_lasi_ext_t_x550em; -- cgit v1.2.3-59-g8ed1b From ab3a3b7b0cf88021376d565c526aa27b1e105148 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 23 Dec 2015 09:00:35 -0800 Subject: ixgbe: Fix bugs in ixgbe_clear_vf_vlans() When I had rewritten the code for ixgbe_clear_vf_vlans() it looks like I had transitioned back and forth between using word as an offset and using word as a register offset. As a result I honestly don't see how the code was working before other than the fact that resetting the VLANs on the VF like didn't do much to clear them. Another issue found is that the mask was using a divide instead of a modulus. As a result the mask bit was incorrectly being set to either bit 0 or 1 based on the value of the VF being tested. As a result the wrong VFs were having their VLANs cleared if they were enabled. I have updated the code so that word represents the offset in the array. This way we can use the modulus and xor operations and they will make sense instead of being performed on a 4 byte aligned value. I replaced the statement "(word % 2) ^ 1" with "~word % 2" in order to reduce the line length as the line exceeded 80 characters with the register name inserted. The two should be equivalent so the change should be safe. Reported-by: Emil Tantilov Signed-off-by: Alexander Duyck Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index eeff3d075bf8..8025a3f93598 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -593,11 +593,11 @@ static void ixgbe_clear_vf_vlans(struct ixgbe_adapter *adapter, u32 vf) /* post increment loop, covers VLVF_ENTRIES - 1 to 0 */ for (i = IXGBE_VLVF_ENTRIES; i--;) { - u32 word = IXGBE_VLVFB(i * 2 + vf / 32); u32 bits[2], vlvfb, vid, vfta, vlvf; - u32 mask = 1 << (vf / 32); + u32 word = i * 2 + vf / 32; + u32 mask = 1 << (vf % 32); - vlvfb = IXGBE_READ_REG(hw, word); + vlvfb = IXGBE_READ_REG(hw, IXGBE_VLVFB(word)); /* if our bit isn't set we can skip it */ if (!(vlvfb & mask)) @@ -608,7 +608,7 @@ static void ixgbe_clear_vf_vlans(struct ixgbe_adapter *adapter, u32 vf) /* create 64b mask to chedk to see if we should clear VLVF */ bits[word % 2] = vlvfb; - bits[(word % 2) ^ 1] = IXGBE_READ_REG(hw, word ^ 1); + bits[~word % 2] = IXGBE_READ_REG(hw, IXGBE_VLVFB(word ^ 1)); /* if promisc is enabled, PF will be present, leave VFTA */ if (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC) { -- cgit v1.2.3-59-g8ed1b From eb5d912e156409a951460578de898270e1c89617 Mon Sep 17 00:00:00 2001 From: Shengzhen Li Date: Mon, 14 Dec 2015 04:15:02 -0800 Subject: mwifiex: change ap and station interface limits ap/station interface limit has been changed to allow creating maximum 3 interfaces. Signed-off-by: Shengzhen Li Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 65dd85d4fb14..47d8afd2ad34 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -26,12 +26,10 @@ module_param(reg_alpha2, charp, 0); static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { { - .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | + .max = 3, .types = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT), - }, - { - .max = 1, .types = BIT(NL80211_IFTYPE_AP), + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_AP), }, }; -- cgit v1.2.3-59-g8ed1b From 5b13d3e1f9260878c06763a13129bc94e51b4c9c Mon Sep 17 00:00:00 2001 From: Shengzhen Li Date: Mon, 14 Dec 2015 04:15:03 -0800 Subject: mwifiex: multiple bss support This patch fixes issues observed while starting 3 different bss simultaneously, eg, 2 AP + 1 STA or 3 AP Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 20 ++++++++---- drivers/net/wireless/marvell/mwifiex/decl.h | 6 ++-- drivers/net/wireless/marvell/mwifiex/main.h | 42 ++++++++++++++++++++----- 3 files changed, 51 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 47d8afd2ad34..ab0ba6a601b4 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -825,18 +825,26 @@ mwifiex_init_new_priv_params(struct mwifiex_private *priv, switch (type) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: + priv->bss_num = mwifiex_get_unused_bss_num(adapter, + MWIFIEX_BSS_TYPE_STA); priv->bss_role = MWIFIEX_BSS_ROLE_STA; priv->bss_type = MWIFIEX_BSS_TYPE_STA; break; case NL80211_IFTYPE_P2P_CLIENT: + priv->bss_num = mwifiex_get_unused_bss_num(adapter, + MWIFIEX_BSS_TYPE_P2P); priv->bss_role = MWIFIEX_BSS_ROLE_STA; priv->bss_type = MWIFIEX_BSS_TYPE_P2P; break; case NL80211_IFTYPE_P2P_GO: + priv->bss_num = mwifiex_get_unused_bss_num(adapter, + MWIFIEX_BSS_TYPE_P2P); priv->bss_role = MWIFIEX_BSS_ROLE_UAP; priv->bss_type = MWIFIEX_BSS_TYPE_P2P; break; case NL80211_IFTYPE_AP: + priv->bss_num = mwifiex_get_unused_bss_num(adapter, + MWIFIEX_BSS_TYPE_UAP); priv->bss_type = MWIFIEX_BSS_TYPE_UAP; priv->bss_role = MWIFIEX_BSS_ROLE_UAP; break; @@ -2606,7 +2614,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, return ERR_PTR(-EINVAL); } - priv = mwifiex_get_unused_priv(adapter); + priv = mwifiex_get_unused_priv_by_bss_type( + adapter, MWIFIEX_BSS_TYPE_STA); if (!priv) { mwifiex_dbg(adapter, ERROR, "could not get free private struct\n"); @@ -2625,7 +2634,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; priv->bss_priority = 0; priv->bss_role = MWIFIEX_BSS_ROLE_STA; - priv->bss_num = adapter->curr_iface_comb.sta_intf; break; case NL80211_IFTYPE_AP: @@ -2636,7 +2644,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, return ERR_PTR(-EINVAL); } - priv = mwifiex_get_unused_priv(adapter); + priv = mwifiex_get_unused_priv_by_bss_type( + adapter, MWIFIEX_BSS_TYPE_UAP); if (!priv) { mwifiex_dbg(adapter, ERROR, "could not get free private struct\n"); @@ -2651,7 +2660,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, priv->bss_priority = 0; priv->bss_role = MWIFIEX_BSS_ROLE_UAP; priv->bss_started = 0; - priv->bss_num = adapter->curr_iface_comb.uap_intf; priv->bss_mode = type; break; @@ -2663,7 +2671,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, return ERR_PTR(-EINVAL); } - priv = mwifiex_get_unused_priv(adapter); + priv = mwifiex_get_unused_priv_by_bss_type( + adapter, MWIFIEX_BSS_TYPE_P2P); if (!priv) { mwifiex_dbg(adapter, ERROR, "could not get free private struct\n"); @@ -2687,7 +2696,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, priv->bss_priority = MWIFIEX_BSS_ROLE_STA; priv->bss_role = MWIFIEX_BSS_ROLE_STA; priv->bss_started = 0; - priv->bss_num = adapter->curr_iface_comb.p2p_intf; if (mwifiex_cfg80211_init_p2p_client(priv)) { memset(&priv->wdev, 0, sizeof(priv->wdev)); diff --git a/drivers/net/wireless/marvell/mwifiex/decl.h b/drivers/net/wireless/marvell/mwifiex/decl.h index 098e1f14dc9a..d9c15cd36f12 100644 --- a/drivers/net/wireless/marvell/mwifiex/decl.h +++ b/drivers/net/wireless/marvell/mwifiex/decl.h @@ -111,9 +111,9 @@ /* Rate index for OFDM 0 */ #define MWIFIEX_RATE_INDEX_OFDM0 4 -#define MWIFIEX_MAX_STA_NUM 1 -#define MWIFIEX_MAX_UAP_NUM 1 -#define MWIFIEX_MAX_P2P_NUM 1 +#define MWIFIEX_MAX_STA_NUM 3 +#define MWIFIEX_MAX_UAP_NUM 3 +#define MWIFIEX_MAX_P2P_NUM 3 #define MWIFIEX_A_BAND_START_FREQ 5000 diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index a86e2aebf566..10e614e7060a 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1272,21 +1272,47 @@ mwifiex_get_priv(struct mwifiex_adapter *adapter, return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); } +/* + * This function checks available bss_num when adding new interface or + * changing interface type. + */ +static inline u8 +mwifiex_get_unused_bss_num(struct mwifiex_adapter *adapter, u8 bss_type) +{ + u8 i, j; + int index[MWIFIEX_MAX_BSS_NUM]; + + memset(index, 0, sizeof(index)); + for (i = 0; i < adapter->priv_num; i++) + if (adapter->priv[i]) { + if (adapter->priv[i]->bss_type == bss_type && + !(adapter->priv[i]->bss_mode == + NL80211_IFTYPE_UNSPECIFIED)) { + index[adapter->priv[i]->bss_num] = 1; + } + } + for (j = 0; j < MWIFIEX_MAX_BSS_NUM; j++) + if (!index[j]) + return j; + return -1; +} + /* * This function returns the first available unused private structure pointer. */ static inline struct mwifiex_private * -mwifiex_get_unused_priv(struct mwifiex_adapter *adapter) +mwifiex_get_unused_priv_by_bss_type(struct mwifiex_adapter *adapter, + u8 bss_type) { - int i; + u8 i; - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - if (adapter->priv[i]->bss_mode == - NL80211_IFTYPE_UNSPECIFIED) - break; + for (i = 0; i < adapter->priv_num; i++) + if (adapter->priv[i]->bss_mode == + NL80211_IFTYPE_UNSPECIFIED) { + adapter->priv[i]->bss_num = + mwifiex_get_unused_bss_num(adapter, bss_type); + break; } - } return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); } -- cgit v1.2.3-59-g8ed1b From 5eab677799743b75037efcfcb8c660dffa33f244 Mon Sep 17 00:00:00 2001 From: Zhaoyang Liu Date: Mon, 14 Dec 2015 04:15:04 -0800 Subject: mwifiex: advertise SMS4 cipher suite This is needed to support WAPI functionality. Signed-off-by: Zhaoyang Liu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index ab0ba6a601b4..3349c2ae3b76 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -1539,6 +1539,7 @@ static const u32 mwifiex_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_SMS4, WLAN_CIPHER_SUITE_AES_CMAC, }; -- cgit v1.2.3-59-g8ed1b From c8ac6a8ee8e70c128d3d562704846c8cba91c61d Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Mon, 14 Dec 2015 04:15:05 -0800 Subject: mwifiex: fix bug for wildcard-prefix wowlan pattern Wildcard prefix bytes are ignored while downloading packet pattern to firmware. As packet offset is not adjusted accordingly firmware end up matching the pattern at wrong offset. The packet offset is corrected in this patch. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 3349c2ae3b76..0aab9354ab5b 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -2921,6 +2921,12 @@ mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq, dont_care_byte = true; } + /* wildcard bytes record as the offset + * before the valid byte + */ + if (!valid_byte_cnt && !dont_care_byte) + pat->pkt_offset++; + if (valid_byte_cnt > max_byte_seq) return false; } -- cgit v1.2.3-59-g8ed1b From b0922ffa706be2aa556f19fa6aa0fa983ec19bfa Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Mon, 14 Dec 2015 04:15:06 -0800 Subject: mwifiex: increase supported wowlan pattern length Maximum supported wowlan pattern length has been increased from 20 to 40. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/fw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index 89938f61717a..ced7af2be29a 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -537,7 +537,7 @@ enum P2P_MODES { #define EVENT_GET_BSS_TYPE(event_cause) \ (((event_cause) >> 24) & 0x00ff) -#define MWIFIEX_MAX_PATTERN_LEN 20 +#define MWIFIEX_MAX_PATTERN_LEN 40 #define MWIFIEX_MAX_OFFSET_LEN 100 #define STACK_NBYTES 100 #define TYPE_DNUM 1 -- cgit v1.2.3-59-g8ed1b From abffd274a243c5e5e72a39148c9dd36b26f7c6b7 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Mon, 14 Dec 2015 04:15:07 -0800 Subject: mwifiex: abort cac in del_station() handler When hostapd is killed with Ctrl+C before cac get completed, stop_ap handler will not be called, thus priv->wdev.cac_started flag remains set. Hostapd restart attempt will be failed in this case with device busy error. This patch aborts cac in del_station handler to handle this corner case. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 0aab9354ab5b..f7c26d6bca28 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -1708,6 +1708,11 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, u8 deauth_mac[ETH_ALEN]; unsigned long flags; + if (!priv->bss_started && priv->wdev.cac_started) { + mwifiex_dbg(priv->adapter, INFO, "%s: abort CAC!\n", __func__); + mwifiex_abort_cac(priv); + } + if (list_empty(&priv->sta_list) || !priv->bss_started) return 0; -- cgit v1.2.3-59-g8ed1b From bd642acf3213c2856221b4f3e2fd84056578b4fd Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 14 Dec 2015 04:15:08 -0800 Subject: mwifiex: suppress "Rx of mgmt packet failed" message Block ACK action frames are dropped in driver. This error is expected in this case. Let's lower the priority of this message. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sta_rx.c | 2 +- drivers/net/wireless/marvell/mwifiex/uap_txrx.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/sta_rx.c b/drivers/net/wireless/marvell/mwifiex/sta_rx.c index d4d4cb1ce95b..00fcbda09349 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_rx.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_rx.c @@ -215,7 +215,7 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, if (rx_pkt_type == PKT_TYPE_MGMT) { ret = mwifiex_process_mgmt_packet(priv, skb); if (ret) - mwifiex_dbg(adapter, ERROR, "Rx of mgmt packet failed"); + mwifiex_dbg(adapter, DATA, "Rx of mgmt packet failed"); dev_kfree_skb_any(skb); return ret; } diff --git a/drivers/net/wireless/marvell/mwifiex/uap_txrx.c b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c index 74d5d7238633..52f7981a8afc 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c @@ -310,8 +310,7 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, if (rx_pkt_type == PKT_TYPE_MGMT) { ret = mwifiex_process_mgmt_packet(priv, skb); if (ret) - mwifiex_dbg(adapter, ERROR, - "Rx of mgmt packet failed"); + mwifiex_dbg(adapter, DATA, "Rx of mgmt packet failed"); dev_kfree_skb_any(skb); return ret; } -- cgit v1.2.3-59-g8ed1b From 47f336d7e0df3ef04f5bae0f5f37cdb0f09996e1 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 14 Dec 2015 04:15:09 -0800 Subject: mwifiex: remove redundant timestamp assignment During AMSDU aggregation, we are already using timestamp value of a first packet being aggregated. This patch removes redundant ktime_get_real() call. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/11n_aggr.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c index aa498e0d2204..1efef3b8273d 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c @@ -203,8 +203,6 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, skb_aggr->priority = skb_src->priority; skb_aggr->tstamp = skb_src->tstamp; - skb_aggr->tstamp = ktime_get_real(); - do { /* Check if AMSDU can accommodate this MSDU */ if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN)) -- cgit v1.2.3-59-g8ed1b From 76ae3e26ea4341cd18705b6f78d1dbf10bb9de35 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 14 Dec 2015 04:15:10 -0800 Subject: mwifiex: add debugfs file for testing reset of card This provides an option for user to power cycle the card. It will be used to change the firmware without actually rebooting the system. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/debugfs.c | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c index 9824d8dd2b44..5e5562909d9f 100644 --- a/drivers/net/wireless/marvell/mwifiex/debugfs.c +++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c @@ -906,6 +906,34 @@ mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf, return count; } +static ssize_t +mwifiex_reset_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = file->private_data; + struct mwifiex_adapter *adapter = priv->adapter; + char cmd; + bool result; + + if (copy_from_user(&cmd, ubuf, sizeof(cmd))) + return -EFAULT; + + if (strtobool(&cmd, &result)) + return -EINVAL; + + if (!result) + return -EINVAL; + + if (adapter->if_ops.card_reset) { + dev_info(adapter->dev, "Resetting per request\n"); + adapter->hw_status = MWIFIEX_HW_STATUS_RESET; + mwifiex_cancel_all_pending_cmd(adapter); + adapter->if_ops.card_reset(adapter); + } + + return count; +} + #define MWIFIEX_DFS_ADD_FILE(name) do { \ if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \ priv, &mwifiex_dfs_##name##_fops)) \ @@ -943,6 +971,7 @@ MWIFIEX_DFS_FILE_OPS(hscfg); MWIFIEX_DFS_FILE_OPS(histogram); MWIFIEX_DFS_FILE_OPS(debug_mask); MWIFIEX_DFS_FILE_OPS(timeshare_coex); +MWIFIEX_DFS_FILE_WRITE_OPS(reset); /* * This function creates the debug FS directory structure and the files. @@ -970,6 +999,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv) MWIFIEX_DFS_ADD_FILE(histogram); MWIFIEX_DFS_ADD_FILE(debug_mask); MWIFIEX_DFS_ADD_FILE(timeshare_coex); + MWIFIEX_DFS_ADD_FILE(reset); } /* -- cgit v1.2.3-59-g8ed1b From 776f742040ca5eb6242c60f29ac73d5752a5b621 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 14 Dec 2015 04:15:11 -0800 Subject: mwifiex: fix AMPDU not setup on TDLS link problem Sometimes AP sends TDLS setup response as AMSDU packet. As driver doesn't parse it and update peer station's 11n capability in this case, AMPDU doesn't get setup. This patch calls mwifiex_process_tdls_action_frame() in AMSDU Rx path to fix the problem. Signed-off-by: Amitkumar Karwar Signed-off-by: Cathy Luo Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c index b3970a8c9e48..09578c6cde59 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c @@ -48,7 +48,17 @@ static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, priv->wdev.iftype, 0, false); while (!skb_queue_empty(&list)) { + struct rx_packet_hdr *rx_hdr; + rx_skb = __skb_dequeue(&list); + rx_hdr = (struct rx_packet_hdr *)rx_skb->data; + if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { + mwifiex_process_tdls_action_frame(priv, + (u8 *)rx_hdr, + skb->len); + } + ret = mwifiex_recv_packet(priv, rx_skb); if (ret == -1) mwifiex_dbg(priv->adapter, ERROR, -- cgit v1.2.3-59-g8ed1b From 7f3f1245ffcde03e46b012119c12d8262e2d297a Mon Sep 17 00:00:00 2001 From: chunfan chen Date: Mon, 14 Dec 2015 04:15:12 -0800 Subject: mwifiex: fix wake on disconnect feature Default gpio and gap is downloaded to firmware while configuring host sleep for wake on disconnect. We may have gpio and gap modified by user. This patch fixes the problem. Signed-off-by: chunfan chen Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index f7c26d6bca28..e7adef72c05f 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -3159,8 +3159,8 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, memset(&hs_cfg, 0, sizeof(hs_cfg)); hs_cfg.is_invoke_hostcmd = false; hs_cfg.conditions = HS_CFG_COND_MAC_EVENT; - hs_cfg.gpio = HS_CFG_GPIO_DEF; - hs_cfg.gap = HS_CFG_GAP_DEF; + hs_cfg.gpio = adapter->hs_cfg.gpio; + hs_cfg.gap = adapter->hs_cfg.gap; ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD, &hs_cfg); if (ret) { -- cgit v1.2.3-59-g8ed1b From 610d0af8e8d227e5ecf17568817205a69a206a8f Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 14 Dec 2015 04:15:13 -0800 Subject: mwifiex: update region_code_index array This array contains list of supported region codes. It is changed to make it aligned with region code to country mapping table in driver. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfp.c | 2 +- drivers/net/wireless/marvell/mwifiex/main.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c index beb564fb2dba..09fae27140f7 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfp.c +++ b/drivers/net/wireless/marvell/mwifiex/cfp.c @@ -67,7 +67,7 @@ static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c, 0x60, 0x6c, 0 }; u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x00, 0x10, 0x20, 0x30, - 0x32, 0x40, 0x41, 0xff }; + 0x31, 0x32, 0x40, 0x41, 0x50 }; static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 10e614e7060a..0fa1d8e82798 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -84,7 +84,7 @@ enum { #define MWIFIEX_KEY_BUFFER_SIZE 16 #define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10 -#define MWIFIEX_MAX_REGION_CODE 8 +#define MWIFIEX_MAX_REGION_CODE 9 #define DEFAULT_BCN_AVG_FACTOR 8 #define DEFAULT_DATA_AVG_FACTOR 8 -- cgit v1.2.3-59-g8ed1b From 19b0a71017aa6d14a32541b8917f1ddf00bad0cd Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 14 Dec 2015 04:15:14 -0800 Subject: mwifiex: use world for unidentified region code It's better to use world if region code from EEPROM is unidentied instead of forcing it to FCC Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cmdevt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c index 45ae38e32621..cb25aa7e90db 100644 --- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -1637,9 +1637,9 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, if (adapter->region_code == region_code_index[i]) break; - /* If it's unidentified region code, use the default (USA) */ + /* If it's unidentified region code, use the default (world) */ if (i >= MWIFIEX_MAX_REGION_CODE) { - adapter->region_code = 0x10; + adapter->region_code = 0x00; mwifiex_dbg(adapter, WARN, "cmd: unknown region code, use default (USA)\n"); } -- cgit v1.2.3-59-g8ed1b From 84a38fb3167e6cc25c4acfcbb79aa0d800542fd7 Mon Sep 17 00:00:00 2001 From: chunfan chen Date: Mon, 14 Dec 2015 04:15:15 -0800 Subject: mwifiex: fix WPA connection problem Device fails to connect to some AP's configured in WPA security mode. Currently IE buffer parsing logic in driver expects WPA IE to be present at the beginning of IE buffer. Otherwise connection is failed with 'incompatible network setting' error. This patch fixes the problem by improving IE buffer parsing logic. Signed-off-by: chunfan chen Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 75 +++++++++++++++++------- 1 file changed, 53 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index 62b35a31a225..439e73fc9c02 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -1293,6 +1293,8 @@ mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, struct ieee_types_vendor_header *pvendor_ie; const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; + u16 unparsed_len = ie_len; + int find_wpa_ie = 0; /* If the passed length is zero, reset the buffer */ if (!ie_len) { @@ -1304,40 +1306,69 @@ mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, return -1; } pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; - /* Test to see if it is a WPA IE, if not, then it is a gen IE */ - if (((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && - (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) || - (pvendor_ie->element_id == WLAN_EID_RSN)) { - /* IE is a WPA/WPA2 IE so call set_wpa function */ - ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len); - priv->wps.session_enable = false; + while (pvendor_ie) { + if (pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) { + /* Test to see if it is a WPA IE, if not, then it is a + * gen IE + */ + if (!memcmp(pvendor_ie->oui, wpa_oui, + sizeof(wpa_oui))) { + find_wpa_ie = 1; + break; + } - return ret; - } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { + /* Test to see if it is a WPS IE, if so, enable + * wps session flag + */ + if (!memcmp(pvendor_ie->oui, wps_oui, + sizeof(wps_oui))) { + priv->wps.session_enable = true; + mwifiex_dbg(priv->adapter, MSG, + "info: WPS Session Enabled.\n"); + ret = mwifiex_set_wps_ie(priv, + (u8 *)pvendor_ie, + unparsed_len); + } + } + + if (pvendor_ie->element_id == WLAN_EID_RSN) { + find_wpa_ie = 1; + break; + } + + if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { /* IE is a WAPI IE so call set_wapi function */ - ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len); + ret = mwifiex_set_wapi_ie(priv, (u8 *)pvendor_ie, + unparsed_len); + return ret; + } + + unparsed_len -= (pvendor_ie->len + + sizeof(struct ieee_types_header)); + + if (unparsed_len <= sizeof(struct ieee_types_header)) + pvendor_ie = NULL; + else + pvendor_ie = (struct ieee_types_vendor_header *) + (((u8 *)pvendor_ie) + pvendor_ie->len + + sizeof(struct ieee_types_header)); + } + if (find_wpa_ie) { + /* IE is a WPA/WPA2 IE so call set_wpa function */ + ret = mwifiex_set_wpa_ie_helper(priv, (u8 *)pvendor_ie, + unparsed_len); + priv->wps.session_enable = false; return ret; } + /* * Verify that the passed length is not larger than the * available space remaining in the buffer */ if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) { - /* Test to see if it is a WPS IE, if so, enable - * wps session flag - */ - pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; - if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && - (!memcmp(pvendor_ie->oui, wps_oui, sizeof(wps_oui)))) { - priv->wps.session_enable = true; - mwifiex_dbg(priv->adapter, INFO, - "info: WPS Session Enabled.\n"); - ret = mwifiex_set_wps_ie(priv, ie_data_ptr, ie_len); - } - /* Append the passed data to the end of the genIeBuffer */ memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr, -- cgit v1.2.3-59-g8ed1b From 7079604ddb83f428359feace3aeaf8a9f435be4a Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 14 Dec 2015 16:34:31 -0600 Subject: rtlwifi: rtl8723be: Fix module parameter initialization This driver has a number of errors in the module initialization. These include the following: Parameter msi_support is stored in two places - one is removed. Paramters sw_crypto and disable_watchdog were never stored in the final locations, nor were they initialized properly. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c index d091f1d5f91e..a78eaeda0008 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c @@ -93,7 +93,6 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw) struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); rtl8723be_bt_reg_init(hw); - rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer(); rtlpriv->dm.dm_initialgain_enable = 1; @@ -151,6 +150,10 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; + rtlpriv->cfg->mod_params->sw_crypto = + rtlpriv->cfg->mod_params->sw_crypto; + rtlpriv->cfg->mod_params->disable_watchdog = + rtlpriv->cfg->mod_params->disable_watchdog; if (rtlpriv->cfg->mod_params->disable_watchdog) pr_info("watchdog disabled\n"); rtlpriv->psc.reg_fwctrl_lps = 3; @@ -267,6 +270,9 @@ static struct rtl_mod_params rtl8723be_mod_params = { .inactiveps = true, .swctrl_lps = false, .fwctrl_lps = true, + .msi_support = false, + .disable_watchdog = false, + .debug = DBG_EMERG, }; static struct rtl_hal_cfg rtl8723be_hal_cfg = { -- cgit v1.2.3-59-g8ed1b From 793b09994211fbe0b5cd5f7b6dd8cc9b6256c3c6 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 14 Dec 2015 16:34:32 -0600 Subject: rtlwifi: rtl8723ae: Fix initialization of module parameters This driver has some errors in the handling of module parameters. These include missing initialization for parameters msi_support and disable_watchdog. In addition, neither of these parameters nor sw_crypto are transferred into the locations used by the driver. A final fix is adding parameter msi to the module named and description macros. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c index 3859b3e3d158..ff49a8c0ff61 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c @@ -150,6 +150,11 @@ int rtl8723e_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; + rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; + rtlpriv->cfg->mod_params->sw_crypto = + rtlpriv->cfg->mod_params->sw_crypto; + rtlpriv->cfg->mod_params->disable_watchdog = + rtlpriv->cfg->mod_params->disable_watchdog; if (rtlpriv->cfg->mod_params->disable_watchdog) pr_info("watchdog disabled\n"); rtlpriv->psc.reg_fwctrl_lps = 3; @@ -267,6 +272,8 @@ static struct rtl_mod_params rtl8723e_mod_params = { .swctrl_lps = false, .fwctrl_lps = true, .debug = DBG_EMERG, + .msi_support = false, + .disable_watchdog = false, }; static struct rtl_hal_cfg rtl8723e_hal_cfg = { @@ -383,12 +390,14 @@ module_param_named(debug, rtl8723e_mod_params.debug, int, 0444); module_param_named(ips, rtl8723e_mod_params.inactiveps, bool, 0444); module_param_named(swlps, rtl8723e_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl8723e_mod_params.fwctrl_lps, bool, 0444); +module_param_named(msi, rtl8723e_mod_params.msi_support, bool, 0444); module_param_named(disable_watchdog, rtl8723e_mod_params.disable_watchdog, bool, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); +MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n"); -- cgit v1.2.3-59-g8ed1b From 78bae1de422a7f6f2b4b61f6a5c379e3d7f96f44 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 14 Dec 2015 16:34:33 -0600 Subject: rtlwifi: rtl8821ae: Fix errors in parameter initialization This driver failed to copy parameters sw_crypto and disable_watchdog into the locations actually used by the driver. In addition, msi_support was initialized three times and one of them used the wrong variable. The initialization of parameter int_clear was moved so that it is near that of the rest of the parameters. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c index 142bdff4ed60..4159f9b14db6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c @@ -95,8 +95,6 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); rtl8821ae_bt_reg_init(hw); - rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; - rtlpci->int_clear = rtlpriv->cfg->mod_params->int_clear; rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer(); rtlpriv->dm.dm_initialgain_enable = 1; @@ -168,12 +166,15 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; - rtlpci->msi_support = rtlpriv->cfg->mod_params->int_clear; + rtlpci->int_clear = rtlpriv->cfg->mod_params->int_clear; + rtlpriv->cfg->mod_params->sw_crypto = + rtlpriv->cfg->mod_params->sw_crypto; + rtlpriv->cfg->mod_params->disable_watchdog = + rtlpriv->cfg->mod_params->disable_watchdog; if (rtlpriv->cfg->mod_params->disable_watchdog) pr_info("watchdog disabled\n"); rtlpriv->psc.reg_fwctrl_lps = 3; rtlpriv->psc.reg_max_lps_awakeintvl = 5; - rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; /* for ASPM, you can close aspm through * set const_support_pciaspm = 0 -- cgit v1.2.3-59-g8ed1b From 06f34572c6110e2e2d5e653a957f1d74db9e3f2b Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 14 Dec 2015 16:34:34 -0600 Subject: rtlwifi: rtl8188ee: Fix module parameter initialization In this driver, parameters disable_watchdog and sw_crypto are never copied into the locations used in the main code. While modifying the parameter handling, the copying of parameter msi_support is moved to be with the rest. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c index 11344121c55e..47e32cb0ec1a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c @@ -88,8 +88,6 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw) u8 tid; rtl8188ee_bt_reg_init(hw); - rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; - rtlpriv->dm.dm_initialgain_enable = 1; rtlpriv->dm.dm_flag = 0; rtlpriv->dm.disable_framebursting = 0; @@ -138,6 +136,11 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; + rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; + rtlpriv->cfg->mod_params->sw_crypto = + rtlpriv->cfg->mod_params->sw_crypto; + rtlpriv->cfg->mod_params->disable_watchdog = + rtlpriv->cfg->mod_params->disable_watchdog; if (rtlpriv->cfg->mod_params->disable_watchdog) pr_info("watchdog disabled\n"); if (!rtlpriv->psc.inactiveps) -- cgit v1.2.3-59-g8ed1b From d4d60b4caaa5926e1b243070770968f05656107a Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 14 Dec 2015 16:34:35 -0600 Subject: rtlwifi: rtl8192de: Fix incorrect module parameter descriptions Two of the module parameters are listed with incorrect default values. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c index b19d0398215f..c6e09a19de1a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c @@ -376,8 +376,8 @@ module_param_named(swlps, rtl92de_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl92de_mod_params.fwctrl_lps, bool, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); -MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); -MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); +MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 1)\n"); +MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 0)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); -- cgit v1.2.3-59-g8ed1b From 7503efbd82c15c4070adffff1344e5169d3634b4 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 14 Dec 2015 16:34:36 -0600 Subject: rtlwifi: rtl8192se: Fix module parameter initialization Two of the module parameter descriptions show incorrect default values. In addition the value for software encryption is not transferred to the locations used by the driver. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c index e1fd27c888bf..31baca41ac2f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c @@ -187,6 +187,8 @@ static int rtl92s_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; + rtlpriv->cfg->mod_params->sw_crypto = + rtlpriv->cfg->mod_params->sw_crypto; if (!rtlpriv->psc.inactiveps) pr_info("Power Save off (module option)\n"); if (!rtlpriv->psc.fwctrl_lps) @@ -425,8 +427,8 @@ module_param_named(swlps, rtl92se_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl92se_mod_params.fwctrl_lps, bool, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); -MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); -MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); +MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 1)\n"); +MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 0)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); -- cgit v1.2.3-59-g8ed1b From b24f19f16b9e43f54218c07609b783ea8625406a Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 14 Dec 2015 16:34:37 -0600 Subject: rtlwifi: rtl8192ce: Fix handling of module parameters The module parameter for software encryption was never transferred to the location used by the driver. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c index de6cb6c3a48c..4780bdc63b2b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c @@ -139,6 +139,8 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; + rtlpriv->cfg->mod_params->sw_crypto = + rtlpriv->cfg->mod_params->sw_crypto; if (!rtlpriv->psc.inactiveps) pr_info("rtl8192ce: Power Save off (module option)\n"); if (!rtlpriv->psc.fwctrl_lps) -- cgit v1.2.3-59-g8ed1b From b68d0ae7e58624c33f2eddab471fee55db27dbf9 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 14 Dec 2015 16:34:38 -0600 Subject: rtlwifi: rtl8192cu: Add missing parameter setup This driver fails to copy the module parameter for software encryption to the locations used by the main code. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c index fd4a5353d216..7c6f7f0d18c6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c @@ -65,6 +65,8 @@ static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->dm.disable_framebursting = false; rtlpriv->dm.thermalvalue = 0; rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug; + rtlpriv->cfg->mod_params->sw_crypto = + rtlpriv->cfg->mod_params->sw_crypto; /* for firmware buf */ rtlpriv->rtlhal.pfirmware = vzalloc(0x4000); -- cgit v1.2.3-59-g8ed1b From 1d8f5c1385402552f29143a4793875393a360038 Mon Sep 17 00:00:00 2001 From: chunfan chen Date: Wed, 16 Dec 2015 04:21:42 -0800 Subject: mwifiex: fix potential integer truncation At some places, ie length is truncated from u16 to u8 while storing it to driver's internal variable. This patch fixes the problem. Signed-off-by: chunfan chen Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.h | 6 +++--- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 0fa1d8e82798..2f7f478ce04b 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -564,14 +564,14 @@ struct mwifiex_private { struct mwifiex_wep_key wep_key[NUM_WEP_KEYS]; u16 wep_key_curr_index; u8 wpa_ie[256]; - u8 wpa_ie_len; + u16 wpa_ie_len; u8 wpa_is_gtk_set; struct host_cmd_ds_802_11_key_material aes_key; struct host_cmd_ds_802_11_key_material_v2 aes_key_v2; u8 wapi_ie[256]; - u8 wapi_ie_len; + u16 wapi_ie_len; u8 *wps_ie; - u8 wps_ie_len; + u16 wps_ie_len; u8 wmm_required; u8 wmm_enabled; u8 wmm_qosinfo; diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index 439e73fc9c02..6a4fc5d183cf 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -759,7 +759,7 @@ static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, return -1; } memcpy(priv->wpa_ie, ie_data_ptr, ie_len); - priv->wpa_ie_len = (u8) ie_len; + priv->wpa_ie_len = ie_len; mwifiex_dbg(priv->adapter, CMD, "cmd: Set Wpa_ie_len=%d IE=%#x\n", priv->wpa_ie_len, priv->wpa_ie[0]); -- cgit v1.2.3-59-g8ed1b From ce0c58d998410fb91c63a70e749e98bb0e67eb67 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 16 Dec 2015 04:21:43 -0800 Subject: mwifiex: fix PCIe register information for 8997 chipset This patch corrects some information in mwifiex_pcie_card_reg() structure for 8997 chipset Fixes: 6d85ef00d9dfe ("mwifiex: add support for 8997 chipset") Signed-off-by: Amitkumar Karwar Signed-off-by: Shengzhen Li Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h index 48e549c3b285..7db46eeb6bcf 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie.h @@ -210,17 +210,17 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = { .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, .tx_rdptr = 0xC1A4, - .tx_wrptr = 0xC1A8, - .rx_rdptr = 0xC1A8, + .tx_wrptr = 0xC174, + .rx_rdptr = 0xC174, .rx_wrptr = 0xC1A4, .evt_rdptr = PCIE_SCRATCH_10_REG, .evt_wrptr = PCIE_SCRATCH_11_REG, .drv_rdy = PCIE_SCRATCH_12_REG, .tx_start_ptr = 16, .tx_mask = 0x0FFF0000, - .tx_wrap_mask = 0x01FF0000, + .tx_wrap_mask = 0x1FFF0000, .rx_mask = 0x00000FFF, - .rx_wrap_mask = 0x000001FF, + .rx_wrap_mask = 0x00001FFF, .tx_rollover_ind = BIT(28), .rx_rollover_ind = BIT(12), .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND, -- cgit v1.2.3-59-g8ed1b From f3b35f28096895f2438c10ef719ef67d2951a8c9 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 16 Dec 2015 04:21:44 -0800 Subject: mwifiex: add missing check for PCIe8997 chipset This patch ensures mwifiex_pcie_txbd_empty() does take care of 8997 chipset. Fixes: 6d85ef00d9dfe ("mwifiex: add support for 8997 chipset") Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h index 7db46eeb6bcf..347ba45f1f2a 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie.h @@ -342,6 +342,7 @@ mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr) return 1; break; case PCIE_DEVICE_ID_MARVELL_88W8897: + case PCIE_DEVICE_ID_MARVELL_88W8997: if (((card->txbd_wrptr & reg->tx_mask) == (rdptr & reg->tx_mask)) && ((card->txbd_wrptr & reg->tx_rollover_ind) == -- cgit v1.2.3-59-g8ed1b From 7be0f5b5e169a566be9400481c21de32f147ccbe Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Wed, 16 Dec 2015 04:21:45 -0800 Subject: mwifiex: enable MSI interrupt support in pcie Newer pcie devices (8897 onwards) support MSI. This patch enables it. Signed-off-by: Avinash Patil Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 33 ++++++++++++++++++++++------- drivers/net/wireless/marvell/mwifiex/pcie.h | 1 + 2 files changed, 26 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 21192b6f9c64..9703848ba9f8 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -2599,6 +2599,30 @@ static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter) kfree(card); } +static int mwifiex_pcie_request_irq(struct mwifiex_adapter *adapter) +{ + int ret; + struct pcie_service_card *card = adapter->card; + struct pci_dev *pdev = card->dev; + + if (pci_enable_msi(pdev) != 0) + pci_disable_msi(pdev); + else + card->msi_enable = 1; + + mwifiex_dbg(adapter, INFO, "msi_enable = %d\n", card->msi_enable); + + ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED, + "MRVL_PCIE", pdev); + if (ret) { + pr_err("request_irq failed: ret=%d\n", ret); + adapter->card = NULL; + return -1; + } + + return 0; +} + /* * This function registers the PCIE device. * @@ -2606,21 +2630,14 @@ static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter) */ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) { - int ret; struct pcie_service_card *card = adapter->card; struct pci_dev *pdev = card->dev; /* save adapter pointer in card */ card->adapter = adapter; - ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED, - "MRVL_PCIE", pdev); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "request_irq failed: ret=%d\n", ret); - adapter->card = NULL; + if (mwifiex_pcie_request_irq(adapter)) return -1; - } adapter->dev = &pdev->dev; adapter->tx_buf_size = card->pcie.tx_buf_size; diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h index 347ba45f1f2a..6fc28737b576 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie.h @@ -326,6 +326,7 @@ struct pcie_service_card { dma_addr_t sleep_cookie_pbase; void __iomem *pci_mmap; void __iomem *pci_mmap1; + int msi_enable; }; static inline int -- cgit v1.2.3-59-g8ed1b From 97d14b623c481126416f98dea9d40f650d518ee7 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Sat, 19 Dec 2015 17:39:26 +0100 Subject: rsi: consolidate kmalloc/memset 0 calls to kzalloc This is an API consolidation only. The use of kmalloc + memset to 0 is equivalent to kzalloc. Signed-off-by: Nicholas Mc Guire Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 8d110fd9eba1..0391406f09d3 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1023,7 +1023,7 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) return -ENOMEM; } - selected_rates = kmalloc(2 * RSI_TBL_SZ, GFP_KERNEL); + selected_rates = kzalloc(2 * RSI_TBL_SZ, GFP_KERNEL); if (!selected_rates) { rsi_dbg(ERR_ZONE, "%s: Failed in allocation of mem\n", __func__); @@ -1032,7 +1032,6 @@ static int rsi_send_auto_rate_request(struct rsi_common *common) } memset(skb->data, 0, sizeof(struct rsi_auto_rate)); - memset(selected_rates, 0, 2 * RSI_TBL_SZ); auto_rate = (struct rsi_auto_rate *)skb->data; -- cgit v1.2.3-59-g8ed1b From d47762633fd2b2471868266d55acb6f3a5b4052b Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Sat, 19 Dec 2015 17:39:45 +0100 Subject: rsi: bool tests do not need comparison This is an API consolidation only. Bool initializations should use true and false thus bool tests don't need an explicit comparison. Signed-off-by: Nicholas Mc Guire Signed-off-by: Kalle Valo --- drivers/net/wireless/rsi/rsi_91x_mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c index 0391406f09d3..e43b59d5b53b 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -1226,7 +1226,7 @@ int rsi_send_block_unblock_frame(struct rsi_common *common, bool block_event) mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12); mgmt_frame->desc_word[1] = cpu_to_le16(BLOCK_HW_QUEUE); - if (block_event == true) { + if (block_event) { rsi_dbg(INFO_ZONE, "blocking the data qs\n"); mgmt_frame->desc_word[4] = cpu_to_le16(0xf); } else { -- cgit v1.2.3-59-g8ed1b From f99551a2d39dc26ea03dc6761be11ac913eb2d57 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 21 Dec 2015 17:05:08 -0600 Subject: rtlwifi: rtl_pci: Fix kernel panic In commit 38506ecefab9 (rtlwifi: rtl_pci: Start modification for new drivers), a bug was introduced that causes a NULL pointer dereference. As this bug only affects the infrequently used RTL8192EE and only under low-memory conditions, it has taken a long time for the bug to show up. The bug was reported on the linux-wireless mailing list and also at https://bugs.launchpad.net/ubuntu/+source/ubuntu-release-upgrader/ as bug #1527603 (kernel crashes due to rtl8192ee driver on ubuntu 15.10). Fixes: 38506ecefab9 ("rtlwifi: rtl_pci: Start modification for new drivers") Signed-off-by: Larry Finger Cc: Stable Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/pci.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index f46c9d7f6528..7f471bff435c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -801,7 +801,9 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) hw_queue); if (rx_remained_cnt == 0) return; - + buffer_desc = &rtlpci->rx_ring[rxring_idx].buffer_desc[ + rtlpci->rx_ring[rxring_idx].idx]; + pdesc = (struct rtl_rx_desc *)skb->data; } else { /* rx descriptor */ pdesc = &rtlpci->rx_ring[rxring_idx].desc[ rtlpci->rx_ring[rxring_idx].idx]; @@ -824,13 +826,6 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) new_skb = dev_alloc_skb(rtlpci->rxbuffersize); if (unlikely(!new_skb)) goto no_new; - if (rtlpriv->use_new_trx_flow) { - buffer_desc = - &rtlpci->rx_ring[rxring_idx].buffer_desc - [rtlpci->rx_ring[rxring_idx].idx]; - /*means rx wifi info*/ - pdesc = (struct rtl_rx_desc *)skb->data; - } memset(&rx_status , 0 , sizeof(rx_status)); rtlpriv->cfg->ops->query_rx_desc(hw, &stats, &rx_status, (u8 *)pdesc, skb); -- cgit v1.2.3-59-g8ed1b From d9d426af6bfb4b7f407191395fffbfe42b1ea129 Mon Sep 17 00:00:00 2001 From: Suresh Reddy Date: Wed, 30 Dec 2015 01:28:56 -0500 Subject: be2net: fix VF link state transition from disabled to auto The VF link state setting transition from "disable" to "auto" does not work due to a bug in SET_LOGICAL_LINK_CONFIG_V1 cmd in FW. This issue could not be fixed in FW due to some backward compatibility issues it causes with some released drivers. The issue has been fixed by introducing a new version (v2) of the cmd from 10.6 FW onwards. In v2, to set the VF link state to auto, both PLINK_ENABLE and PLINK_TRACK bits have to be set to 1. The VF link state setting feature now works on Lancer chips too from FW ver 10.6.315.0 onwards. Signed-off-by: Suresh Reddy Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_cmds.c | 35 +++++++++++++++++++++-------- drivers/net/ethernet/emulex/benet/be_cmds.h | 3 ++- 2 files changed, 28 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 1795c935ff02..8083eca60808 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -4260,16 +4260,13 @@ err: return status; } -int be_cmd_set_logical_link_config(struct be_adapter *adapter, - int link_state, u8 domain) +int __be_cmd_set_logical_link_config(struct be_adapter *adapter, + int link_state, int version, u8 domain) { struct be_mcc_wrb *wrb; struct be_cmd_req_set_ll_link *req; int status; - if (BEx_chip(adapter) || lancer_chip(adapter)) - return -EOPNOTSUPP; - spin_lock_bh(&adapter->mcc_lock); wrb = wrb_from_mccq(adapter); @@ -4284,14 +4281,15 @@ int be_cmd_set_logical_link_config(struct be_adapter *adapter, OPCODE_COMMON_SET_LOGICAL_LINK_CONFIG, sizeof(*req), wrb, NULL); - req->hdr.version = 1; + req->hdr.version = version; req->hdr.domain = domain; - if (link_state == IFLA_VF_LINK_STATE_ENABLE) - req->link_config |= 1; + if (link_state == IFLA_VF_LINK_STATE_ENABLE || + link_state == IFLA_VF_LINK_STATE_AUTO) + req->link_config |= PLINK_ENABLE; if (link_state == IFLA_VF_LINK_STATE_AUTO) - req->link_config |= 1 << PLINK_TRACK_SHIFT; + req->link_config |= PLINK_TRACK; status = be_mcc_notify_wait(adapter); err: @@ -4299,6 +4297,25 @@ err: return status; } +int be_cmd_set_logical_link_config(struct be_adapter *adapter, + int link_state, u8 domain) +{ + int status; + + if (BEx_chip(adapter)) + return -EOPNOTSUPP; + + status = __be_cmd_set_logical_link_config(adapter, link_state, + 2, domain); + + /* Version 2 of the command will not be recognized by older FW. + * On such a failure issue version 1 of the command. + */ + if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST) + status = __be_cmd_set_logical_link_config(adapter, link_state, + 1, domain); + return status; +} int be_roce_mcc_cmd(void *netdev_handle, void *wrb_payload, int wrb_payload_size, u16 *cmd_status, u16 *ext_status) { diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 91155ea74f34..9690c3aa5d47 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -2246,7 +2246,8 @@ struct be_cmd_resp_get_iface_list { }; /*************** Set logical link ********************/ -#define PLINK_TRACK_SHIFT 8 +#define PLINK_ENABLE BIT(0) +#define PLINK_TRACK BIT(8) struct be_cmd_req_set_ll_link { struct be_cmd_req_hdr hdr; u32 link_config; /* Bit 0: UP_DOWN, Bit 9: PLINK */ -- cgit v1.2.3-59-g8ed1b From ac0f5fba632023a39be26ac42afd5cfc1f2cc0b4 Mon Sep 17 00:00:00 2001 From: Suresh Reddy Date: Wed, 30 Dec 2015 01:28:57 -0500 Subject: be2net: avoid configuring VEPA mode on BE3 BE3 chip doesn't support VEPA mode. Signed-off-by: Suresh Reddy Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 34e324f20d80..2f76cbeab1e4 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -5108,6 +5108,9 @@ static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, return -EINVAL; mode = nla_get_u16(attr); + if (BE3_chip(adapter) && mode == BRIDGE_MODE_VEPA) + return -EOPNOTSUPP; + if (mode != BRIDGE_MODE_VEPA && mode != BRIDGE_MODE_VEB) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 8836ff48b92296bd70b2aa86883a245eaca7001a Mon Sep 17 00:00:00 2001 From: Suresh Reddy Date: Wed, 30 Dec 2015 01:28:58 -0500 Subject: be2net: cleanup FW flash image related macro defines Many constant definitions relating to the FW-image layout (such as section offset values) were defined in decimal format rather than hexa-decimal. This makes this part of the code un-readable. Also some defines related to BE2 are labeld "g2" and defines related to BE3 are labeled "g3". This patch cleans up all of this to make this code more readable. Signed-off-by: Suresh Reddy Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_cmds.h | 141 ++++++++++++++++------------ drivers/net/ethernet/emulex/benet/be_main.c | 78 +++++++-------- 2 files changed, 118 insertions(+), 101 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 9690c3aa5d47..8c6b606f0e90 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -1207,68 +1207,85 @@ struct be_cmd_resp_get_beacon_state { /* Flashrom related descriptors */ #define MAX_FLASH_COMP 32 -#define OPTYPE_ISCSI_ACTIVE 0 -#define OPTYPE_REDBOOT 1 -#define OPTYPE_BIOS 2 -#define OPTYPE_PXE_BIOS 3 -#define OPTYPE_OFFSET_SPECIFIED 7 -#define OPTYPE_FCOE_BIOS 8 -#define OPTYPE_ISCSI_BACKUP 9 -#define OPTYPE_FCOE_FW_ACTIVE 10 -#define OPTYPE_FCOE_FW_BACKUP 11 -#define OPTYPE_NCSI_FW 13 -#define OPTYPE_REDBOOT_DIR 18 -#define OPTYPE_REDBOOT_CONFIG 19 -#define OPTYPE_SH_PHY_FW 21 -#define OPTYPE_FLASHISM_JUMPVECTOR 22 -#define OPTYPE_UFI_DIR 23 -#define OPTYPE_PHY_FW 99 - -#define FLASH_BIOS_IMAGE_MAX_SIZE_g2 262144 /* Max OPTION ROM image sz */ -#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g2 262144 /* Max Redboot image sz */ -#define FLASH_IMAGE_MAX_SIZE_g2 1310720 /* Max firmware image size */ - -#define FLASH_NCSI_IMAGE_MAX_SIZE_g3 262144 -#define FLASH_PHY_FW_IMAGE_MAX_SIZE_g3 262144 -#define FLASH_BIOS_IMAGE_MAX_SIZE_g3 524288 /* Max OPTION ROM image sz */ -#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g3 1048576 /* Max Redboot image sz */ -#define FLASH_IMAGE_MAX_SIZE_g3 2097152 /* Max firmware image size */ - -/* Offsets for components on Flash. */ -#define FLASH_REDBOOT_START_g2 0 -#define FLASH_FCoE_BIOS_START_g2 524288 -#define FLASH_iSCSI_PRIMARY_IMAGE_START_g2 1048576 -#define FLASH_iSCSI_BACKUP_IMAGE_START_g2 2359296 -#define FLASH_FCoE_PRIMARY_IMAGE_START_g2 3670016 -#define FLASH_FCoE_BACKUP_IMAGE_START_g2 4980736 -#define FLASH_iSCSI_BIOS_START_g2 7340032 -#define FLASH_PXE_BIOS_START_g2 7864320 - -#define FLASH_REDBOOT_START_g3 262144 -#define FLASH_PHY_FW_START_g3 1310720 -#define FLASH_iSCSI_PRIMARY_IMAGE_START_g3 2097152 -#define FLASH_iSCSI_BACKUP_IMAGE_START_g3 4194304 -#define FLASH_FCoE_PRIMARY_IMAGE_START_g3 6291456 -#define FLASH_FCoE_BACKUP_IMAGE_START_g3 8388608 -#define FLASH_iSCSI_BIOS_START_g3 12582912 -#define FLASH_PXE_BIOS_START_g3 13107200 -#define FLASH_FCoE_BIOS_START_g3 13631488 -#define FLASH_NCSI_START_g3 15990784 - -#define IMAGE_NCSI 16 -#define IMAGE_OPTION_ROM_PXE 32 -#define IMAGE_OPTION_ROM_FCoE 33 -#define IMAGE_OPTION_ROM_ISCSI 34 -#define IMAGE_FLASHISM_JUMPVECTOR 48 -#define IMAGE_FIRMWARE_iSCSI 160 -#define IMAGE_FIRMWARE_FCoE 162 -#define IMAGE_FIRMWARE_BACKUP_iSCSI 176 -#define IMAGE_FIRMWARE_BACKUP_FCoE 178 -#define IMAGE_FIRMWARE_PHY 192 -#define IMAGE_REDBOOT_DIR 208 -#define IMAGE_REDBOOT_CONFIG 209 -#define IMAGE_UFI_DIR 210 -#define IMAGE_BOOT_CODE 224 +/* Optypes of each component in the UFI */ +enum { + OPTYPE_ISCSI_ACTIVE = 0, + OPTYPE_REDBOOT = 1, + OPTYPE_BIOS = 2, + OPTYPE_PXE_BIOS = 3, + OPTYPE_OFFSET_SPECIFIED = 7, + OPTYPE_FCOE_BIOS = 8, + OPTYPE_ISCSI_BACKUP = 9, + OPTYPE_FCOE_FW_ACTIVE = 10, + OPTYPE_FCOE_FW_BACKUP = 11, + OPTYPE_NCSI_FW = 13, + OPTYPE_REDBOOT_DIR = 18, + OPTYPE_REDBOOT_CONFIG = 19, + OPTYPE_SH_PHY_FW = 21, + OPTYPE_FLASHISM_JUMPVECTOR = 22, + OPTYPE_UFI_DIR = 23, + OPTYPE_PHY_FW = 99 +}; + +/* Maximum sizes of components in BE2 FW UFI */ +enum { + BE2_BIOS_COMP_MAX_SIZE = 0x40000, + BE2_REDBOOT_COMP_MAX_SIZE = 0x40000, + BE2_COMP_MAX_SIZE = 0x140000 +}; + +/* Maximum sizes of components in BE3 FW UFI */ +enum { + BE3_NCSI_COMP_MAX_SIZE = 0x40000, + BE3_PHY_FW_COMP_MAX_SIZE = 0x40000, + BE3_BIOS_COMP_MAX_SIZE = 0x80000, + BE3_REDBOOT_COMP_MAX_SIZE = 0x100000, + BE3_COMP_MAX_SIZE = 0x200000 +}; + +/* Offsets for components in BE2 FW UFI */ +enum { + BE2_REDBOOT_START = 0x8000, + BE2_FCOE_BIOS_START = 0x80000, + BE2_ISCSI_PRIMARY_IMAGE_START = 0x100000, + BE2_ISCSI_BACKUP_IMAGE_START = 0x240000, + BE2_FCOE_PRIMARY_IMAGE_START = 0x380000, + BE2_FCOE_BACKUP_IMAGE_START = 0x4c0000, + BE2_ISCSI_BIOS_START = 0x700000, + BE2_PXE_BIOS_START = 0x780000 +}; + +/* Offsets for components in BE3 FW UFI */ +enum { + BE3_REDBOOT_START = 0x40000, + BE3_PHY_FW_START = 0x140000, + BE3_ISCSI_PRIMARY_IMAGE_START = 0x200000, + BE3_ISCSI_BACKUP_IMAGE_START = 0x400000, + BE3_FCOE_PRIMARY_IMAGE_START = 0x600000, + BE3_FCOE_BACKUP_IMAGE_START = 0x800000, + BE3_ISCSI_BIOS_START = 0xc00000, + BE3_PXE_BIOS_START = 0xc80000, + BE3_FCOE_BIOS_START = 0xd00000, + BE3_NCSI_START = 0xf40000 +}; + +/* Component entry types */ +enum { + IMAGE_NCSI = 0x10, + IMAGE_OPTION_ROM_PXE = 0x20, + IMAGE_OPTION_ROM_FCOE = 0x21, + IMAGE_OPTION_ROM_ISCSI = 0x22, + IMAGE_FLASHISM_JUMPVECTOR = 0x30, + IMAGE_FIRMWARE_ISCSI = 0xa0, + IMAGE_FIRMWARE_FCOE = 0xa2, + IMAGE_FIRMWARE_BACKUP_ISCSI = 0xb0, + IMAGE_FIRMWARE_BACKUP_FCOE = 0xb2, + IMAGE_FIRMWARE_PHY = 0xc0, + IMAGE_REDBOOT_DIR = 0xd0, + IMAGE_REDBOOT_CONFIG = 0xd1, + IMAGE_UFI_DIR = 0xd2, + IMAGE_BOOT_CODE = 0xe2 +}; struct controller_id { u32 vendor; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 2f76cbeab1e4..0bb6e5a29f8d 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -4616,45 +4616,45 @@ static int be_flash_BEx(struct be_adapter *adapter, const u8 *p; struct flash_comp gen3_flash_types[] = { - { FLASH_iSCSI_PRIMARY_IMAGE_START_g3, OPTYPE_ISCSI_ACTIVE, - FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_iSCSI}, - { FLASH_REDBOOT_START_g3, OPTYPE_REDBOOT, - FLASH_REDBOOT_IMAGE_MAX_SIZE_g3, IMAGE_BOOT_CODE}, - { FLASH_iSCSI_BIOS_START_g3, OPTYPE_BIOS, - FLASH_BIOS_IMAGE_MAX_SIZE_g3, IMAGE_OPTION_ROM_ISCSI}, - { FLASH_PXE_BIOS_START_g3, OPTYPE_PXE_BIOS, - FLASH_BIOS_IMAGE_MAX_SIZE_g3, IMAGE_OPTION_ROM_PXE}, - { FLASH_FCoE_BIOS_START_g3, OPTYPE_FCOE_BIOS, - FLASH_BIOS_IMAGE_MAX_SIZE_g3, IMAGE_OPTION_ROM_FCoE}, - { FLASH_iSCSI_BACKUP_IMAGE_START_g3, OPTYPE_ISCSI_BACKUP, - FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_BACKUP_iSCSI}, - { FLASH_FCoE_PRIMARY_IMAGE_START_g3, OPTYPE_FCOE_FW_ACTIVE, - FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_FCoE}, - { FLASH_FCoE_BACKUP_IMAGE_START_g3, OPTYPE_FCOE_FW_BACKUP, - FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_BACKUP_FCoE}, - { FLASH_NCSI_START_g3, OPTYPE_NCSI_FW, - FLASH_NCSI_IMAGE_MAX_SIZE_g3, IMAGE_NCSI}, - { FLASH_PHY_FW_START_g3, OPTYPE_PHY_FW, - FLASH_PHY_FW_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_PHY} + { BE3_ISCSI_PRIMARY_IMAGE_START, OPTYPE_ISCSI_ACTIVE, + BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_ISCSI}, + { BE3_REDBOOT_START, OPTYPE_REDBOOT, + BE3_REDBOOT_COMP_MAX_SIZE, IMAGE_BOOT_CODE}, + { BE3_ISCSI_BIOS_START, OPTYPE_BIOS, + BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_ISCSI}, + { BE3_PXE_BIOS_START, OPTYPE_PXE_BIOS, + BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_PXE}, + { BE3_FCOE_BIOS_START, OPTYPE_FCOE_BIOS, + BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_FCOE}, + { BE3_ISCSI_BACKUP_IMAGE_START, OPTYPE_ISCSI_BACKUP, + BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_ISCSI}, + { BE3_FCOE_PRIMARY_IMAGE_START, OPTYPE_FCOE_FW_ACTIVE, + BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_FCOE}, + { BE3_FCOE_BACKUP_IMAGE_START, OPTYPE_FCOE_FW_BACKUP, + BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_FCOE}, + { BE3_NCSI_START, OPTYPE_NCSI_FW, + BE3_NCSI_COMP_MAX_SIZE, IMAGE_NCSI}, + { BE3_PHY_FW_START, OPTYPE_PHY_FW, + BE3_PHY_FW_COMP_MAX_SIZE, IMAGE_FIRMWARE_PHY} }; struct flash_comp gen2_flash_types[] = { - { FLASH_iSCSI_PRIMARY_IMAGE_START_g2, OPTYPE_ISCSI_ACTIVE, - FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_iSCSI}, - { FLASH_REDBOOT_START_g2, OPTYPE_REDBOOT, - FLASH_REDBOOT_IMAGE_MAX_SIZE_g2, IMAGE_BOOT_CODE}, - { FLASH_iSCSI_BIOS_START_g2, OPTYPE_BIOS, - FLASH_BIOS_IMAGE_MAX_SIZE_g2, IMAGE_OPTION_ROM_ISCSI}, - { FLASH_PXE_BIOS_START_g2, OPTYPE_PXE_BIOS, - FLASH_BIOS_IMAGE_MAX_SIZE_g2, IMAGE_OPTION_ROM_PXE}, - { FLASH_FCoE_BIOS_START_g2, OPTYPE_FCOE_BIOS, - FLASH_BIOS_IMAGE_MAX_SIZE_g2, IMAGE_OPTION_ROM_FCoE}, - { FLASH_iSCSI_BACKUP_IMAGE_START_g2, OPTYPE_ISCSI_BACKUP, - FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_BACKUP_iSCSI}, - { FLASH_FCoE_PRIMARY_IMAGE_START_g2, OPTYPE_FCOE_FW_ACTIVE, - FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_FCoE}, - { FLASH_FCoE_BACKUP_IMAGE_START_g2, OPTYPE_FCOE_FW_BACKUP, - FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_BACKUP_FCoE} + { BE2_ISCSI_PRIMARY_IMAGE_START, OPTYPE_ISCSI_ACTIVE, + BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_ISCSI}, + { BE2_REDBOOT_START, OPTYPE_REDBOOT, + BE2_REDBOOT_COMP_MAX_SIZE, IMAGE_BOOT_CODE}, + { BE2_ISCSI_BIOS_START, OPTYPE_BIOS, + BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_ISCSI}, + { BE2_PXE_BIOS_START, OPTYPE_PXE_BIOS, + BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_PXE}, + { BE2_FCOE_BIOS_START, OPTYPE_FCOE_BIOS, + BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_FCOE}, + { BE2_ISCSI_BACKUP_IMAGE_START, OPTYPE_ISCSI_BACKUP, + BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_ISCSI}, + { BE2_FCOE_PRIMARY_IMAGE_START, OPTYPE_FCOE_FW_ACTIVE, + BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_FCOE}, + { BE2_FCOE_BACKUP_IMAGE_START, OPTYPE_FCOE_FW_BACKUP, + BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_FCOE} }; if (BE3_chip(adapter)) { @@ -4729,7 +4729,7 @@ static u16 be_get_img_optype(struct flash_section_entry fsec_entry) return img_optype; switch (img_type) { - case IMAGE_FIRMWARE_iSCSI: + case IMAGE_FIRMWARE_ISCSI: img_optype = OPTYPE_ISCSI_ACTIVE; break; case IMAGE_BOOT_CODE: @@ -4741,10 +4741,10 @@ static u16 be_get_img_optype(struct flash_section_entry fsec_entry) case IMAGE_OPTION_ROM_PXE: img_optype = OPTYPE_PXE_BIOS; break; - case IMAGE_OPTION_ROM_FCoE: + case IMAGE_OPTION_ROM_FCOE: img_optype = OPTYPE_FCOE_BIOS; break; - case IMAGE_FIRMWARE_BACKUP_iSCSI: + case IMAGE_FIRMWARE_BACKUP_ISCSI: img_optype = OPTYPE_ISCSI_BACKUP; break; case IMAGE_NCSI: -- cgit v1.2.3-59-g8ed1b From a23113b5f6db9804da185bab4137dfad5dab9dc8 Mon Sep 17 00:00:00 2001 From: Suresh Reddy Date: Wed, 30 Dec 2015 01:28:59 -0500 Subject: be2net: move FW flash cmd code to be_cmds.c All code relating to FW cmds is in be_cmds.[ch] excepting FW flash cmd related code. This patch moves these routines from be_main.c to be_cmds.c Signed-off-by: Suresh Reddy Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_cmds.c | 583 +++++++++++++++++++++++++++- drivers/net/ethernet/emulex/benet/be_cmds.h | 15 +- drivers/net/ethernet/emulex/benet/be_main.c | 564 --------------------------- 3 files changed, 578 insertions(+), 584 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 8083eca60808..da3b39873470 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -2291,10 +2291,11 @@ err: return status; } -int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd, - u32 data_size, u32 data_offset, - const char *obj_name, u32 *data_written, - u8 *change_status, u8 *addn_status) +static int lancer_cmd_write_object(struct be_adapter *adapter, + struct be_dma_mem *cmd, u32 data_size, + u32 data_offset, const char *obj_name, + u32 *data_written, u8 *change_status, + u8 *addn_status) { struct be_mcc_wrb *wrb; struct lancer_cmd_req_write_object *req; @@ -2410,7 +2411,8 @@ int be_cmd_query_sfp_info(struct be_adapter *adapter) return status; } -int lancer_cmd_delete_object(struct be_adapter *adapter, const char *obj_name) +static int lancer_cmd_delete_object(struct be_adapter *adapter, + const char *obj_name) { struct lancer_cmd_req_delete_object *req; struct be_mcc_wrb *wrb; @@ -2485,9 +2487,9 @@ err_unlock: return status; } -int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd, - u32 flash_type, u32 flash_opcode, u32 img_offset, - u32 buf_size) +static int be_cmd_write_flashrom(struct be_adapter *adapter, + struct be_dma_mem *cmd, u32 flash_type, + u32 flash_opcode, u32 img_offset, u32 buf_size) { struct be_mcc_wrb *wrb; struct be_cmd_write_flashrom *req; @@ -2533,8 +2535,8 @@ err_unlock: return status; } -int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc, - u16 img_optype, u32 img_offset, u32 crc_offset) +static int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc, + u16 img_optype, u32 img_offset, u32 crc_offset) { struct be_cmd_read_flash_crc *req; struct be_mcc_wrb *wrb; @@ -2571,6 +2573,567 @@ err: return status; } +static char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "}; + +static bool phy_flashing_required(struct be_adapter *adapter) +{ + return (adapter->phy.phy_type == PHY_TYPE_TN_8022 && + adapter->phy.interface_type == PHY_TYPE_BASET_10GB); +} + +static bool is_comp_in_ufi(struct be_adapter *adapter, + struct flash_section_info *fsec, int type) +{ + int i = 0, img_type = 0; + struct flash_section_info_g2 *fsec_g2 = NULL; + + if (BE2_chip(adapter)) + fsec_g2 = (struct flash_section_info_g2 *)fsec; + + for (i = 0; i < MAX_FLASH_COMP; i++) { + if (fsec_g2) + img_type = le32_to_cpu(fsec_g2->fsec_entry[i].type); + else + img_type = le32_to_cpu(fsec->fsec_entry[i].type); + + if (img_type == type) + return true; + } + return false; +} + +static struct flash_section_info *get_fsec_info(struct be_adapter *adapter, + int header_size, + const struct firmware *fw) +{ + struct flash_section_info *fsec = NULL; + const u8 *p = fw->data; + + p += header_size; + while (p < (fw->data + fw->size)) { + fsec = (struct flash_section_info *)p; + if (!memcmp(flash_cookie, fsec->cookie, sizeof(flash_cookie))) + return fsec; + p += 32; + } + return NULL; +} + +static int be_check_flash_crc(struct be_adapter *adapter, const u8 *p, + u32 img_offset, u32 img_size, int hdr_size, + u16 img_optype, bool *crc_match) +{ + u32 crc_offset; + int status; + u8 crc[4]; + + status = be_cmd_get_flash_crc(adapter, crc, img_optype, img_offset, + img_size - 4); + if (status) + return status; + + crc_offset = hdr_size + img_offset + img_size - 4; + + /* Skip flashing, if crc of flashed region matches */ + if (!memcmp(crc, p + crc_offset, 4)) + *crc_match = true; + else + *crc_match = false; + + return status; +} + +static int be_flash(struct be_adapter *adapter, const u8 *img, + struct be_dma_mem *flash_cmd, int optype, int img_size, + u32 img_offset) +{ + u32 flash_op, num_bytes, total_bytes = img_size, bytes_sent = 0; + struct be_cmd_write_flashrom *req = flash_cmd->va; + int status; + + while (total_bytes) { + num_bytes = min_t(u32, 32 * 1024, total_bytes); + + total_bytes -= num_bytes; + + if (!total_bytes) { + if (optype == OPTYPE_PHY_FW) + flash_op = FLASHROM_OPER_PHY_FLASH; + else + flash_op = FLASHROM_OPER_FLASH; + } else { + if (optype == OPTYPE_PHY_FW) + flash_op = FLASHROM_OPER_PHY_SAVE; + else + flash_op = FLASHROM_OPER_SAVE; + } + + memcpy(req->data_buf, img, num_bytes); + img += num_bytes; + status = be_cmd_write_flashrom(adapter, flash_cmd, optype, + flash_op, img_offset + + bytes_sent, num_bytes); + if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST && + optype == OPTYPE_PHY_FW) + break; + else if (status) + return status; + + bytes_sent += num_bytes; + } + return 0; +} + +/* For BE2, BE3 and BE3-R */ +static int be_flash_BEx(struct be_adapter *adapter, + const struct firmware *fw, + struct be_dma_mem *flash_cmd, int num_of_images) +{ + int img_hdrs_size = (num_of_images * sizeof(struct image_hdr)); + struct device *dev = &adapter->pdev->dev; + struct flash_section_info *fsec = NULL; + int status, i, filehdr_size, num_comp; + const struct flash_comp *pflashcomp; + bool crc_match; + const u8 *p; + + struct flash_comp gen3_flash_types[] = { + { BE3_ISCSI_PRIMARY_IMAGE_START, OPTYPE_ISCSI_ACTIVE, + BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_ISCSI}, + { BE3_REDBOOT_START, OPTYPE_REDBOOT, + BE3_REDBOOT_COMP_MAX_SIZE, IMAGE_BOOT_CODE}, + { BE3_ISCSI_BIOS_START, OPTYPE_BIOS, + BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_ISCSI}, + { BE3_PXE_BIOS_START, OPTYPE_PXE_BIOS, + BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_PXE}, + { BE3_FCOE_BIOS_START, OPTYPE_FCOE_BIOS, + BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_FCOE}, + { BE3_ISCSI_BACKUP_IMAGE_START, OPTYPE_ISCSI_BACKUP, + BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_ISCSI}, + { BE3_FCOE_PRIMARY_IMAGE_START, OPTYPE_FCOE_FW_ACTIVE, + BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_FCOE}, + { BE3_FCOE_BACKUP_IMAGE_START, OPTYPE_FCOE_FW_BACKUP, + BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_FCOE}, + { BE3_NCSI_START, OPTYPE_NCSI_FW, + BE3_NCSI_COMP_MAX_SIZE, IMAGE_NCSI}, + { BE3_PHY_FW_START, OPTYPE_PHY_FW, + BE3_PHY_FW_COMP_MAX_SIZE, IMAGE_FIRMWARE_PHY} + }; + + struct flash_comp gen2_flash_types[] = { + { BE2_ISCSI_PRIMARY_IMAGE_START, OPTYPE_ISCSI_ACTIVE, + BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_ISCSI}, + { BE2_REDBOOT_START, OPTYPE_REDBOOT, + BE2_REDBOOT_COMP_MAX_SIZE, IMAGE_BOOT_CODE}, + { BE2_ISCSI_BIOS_START, OPTYPE_BIOS, + BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_ISCSI}, + { BE2_PXE_BIOS_START, OPTYPE_PXE_BIOS, + BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_PXE}, + { BE2_FCOE_BIOS_START, OPTYPE_FCOE_BIOS, + BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_FCOE}, + { BE2_ISCSI_BACKUP_IMAGE_START, OPTYPE_ISCSI_BACKUP, + BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_ISCSI}, + { BE2_FCOE_PRIMARY_IMAGE_START, OPTYPE_FCOE_FW_ACTIVE, + BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_FCOE}, + { BE2_FCOE_BACKUP_IMAGE_START, OPTYPE_FCOE_FW_BACKUP, + BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_FCOE} + }; + + if (BE3_chip(adapter)) { + pflashcomp = gen3_flash_types; + filehdr_size = sizeof(struct flash_file_hdr_g3); + num_comp = ARRAY_SIZE(gen3_flash_types); + } else { + pflashcomp = gen2_flash_types; + filehdr_size = sizeof(struct flash_file_hdr_g2); + num_comp = ARRAY_SIZE(gen2_flash_types); + img_hdrs_size = 0; + } + + /* Get flash section info*/ + fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw); + if (!fsec) { + dev_err(dev, "Invalid Cookie. FW image may be corrupted\n"); + return -1; + } + for (i = 0; i < num_comp; i++) { + if (!is_comp_in_ufi(adapter, fsec, pflashcomp[i].img_type)) + continue; + + if ((pflashcomp[i].optype == OPTYPE_NCSI_FW) && + memcmp(adapter->fw_ver, "3.102.148.0", 11) < 0) + continue; + + if (pflashcomp[i].optype == OPTYPE_PHY_FW && + !phy_flashing_required(adapter)) + continue; + + if (pflashcomp[i].optype == OPTYPE_REDBOOT) { + status = be_check_flash_crc(adapter, fw->data, + pflashcomp[i].offset, + pflashcomp[i].size, + filehdr_size + + img_hdrs_size, + OPTYPE_REDBOOT, &crc_match); + if (status) { + dev_err(dev, + "Could not get CRC for 0x%x region\n", + pflashcomp[i].optype); + continue; + } + + if (crc_match) + continue; + } + + p = fw->data + filehdr_size + pflashcomp[i].offset + + img_hdrs_size; + if (p + pflashcomp[i].size > fw->data + fw->size) + return -1; + + status = be_flash(adapter, p, flash_cmd, pflashcomp[i].optype, + pflashcomp[i].size, 0); + if (status) { + dev_err(dev, "Flashing section type 0x%x failed\n", + pflashcomp[i].img_type); + return status; + } + } + return 0; +} + +static u16 be_get_img_optype(struct flash_section_entry fsec_entry) +{ + u32 img_type = le32_to_cpu(fsec_entry.type); + u16 img_optype = le16_to_cpu(fsec_entry.optype); + + if (img_optype != 0xFFFF) + return img_optype; + + switch (img_type) { + case IMAGE_FIRMWARE_ISCSI: + img_optype = OPTYPE_ISCSI_ACTIVE; + break; + case IMAGE_BOOT_CODE: + img_optype = OPTYPE_REDBOOT; + break; + case IMAGE_OPTION_ROM_ISCSI: + img_optype = OPTYPE_BIOS; + break; + case IMAGE_OPTION_ROM_PXE: + img_optype = OPTYPE_PXE_BIOS; + break; + case IMAGE_OPTION_ROM_FCOE: + img_optype = OPTYPE_FCOE_BIOS; + break; + case IMAGE_FIRMWARE_BACKUP_ISCSI: + img_optype = OPTYPE_ISCSI_BACKUP; + break; + case IMAGE_NCSI: + img_optype = OPTYPE_NCSI_FW; + break; + case IMAGE_FLASHISM_JUMPVECTOR: + img_optype = OPTYPE_FLASHISM_JUMPVECTOR; + break; + case IMAGE_FIRMWARE_PHY: + img_optype = OPTYPE_SH_PHY_FW; + break; + case IMAGE_REDBOOT_DIR: + img_optype = OPTYPE_REDBOOT_DIR; + break; + case IMAGE_REDBOOT_CONFIG: + img_optype = OPTYPE_REDBOOT_CONFIG; + break; + case IMAGE_UFI_DIR: + img_optype = OPTYPE_UFI_DIR; + break; + default: + break; + } + + return img_optype; +} + +static int be_flash_skyhawk(struct be_adapter *adapter, + const struct firmware *fw, + struct be_dma_mem *flash_cmd, int num_of_images) +{ + int img_hdrs_size = num_of_images * sizeof(struct image_hdr); + bool crc_match, old_fw_img, flash_offset_support = true; + struct device *dev = &adapter->pdev->dev; + struct flash_section_info *fsec = NULL; + u32 img_offset, img_size, img_type; + u16 img_optype, flash_optype; + int status, i, filehdr_size; + const u8 *p; + + filehdr_size = sizeof(struct flash_file_hdr_g3); + fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw); + if (!fsec) { + dev_err(dev, "Invalid Cookie. FW image may be corrupted\n"); + return -EINVAL; + } + +retry_flash: + for (i = 0; i < le32_to_cpu(fsec->fsec_hdr.num_images); i++) { + img_offset = le32_to_cpu(fsec->fsec_entry[i].offset); + img_size = le32_to_cpu(fsec->fsec_entry[i].pad_size); + img_type = le32_to_cpu(fsec->fsec_entry[i].type); + img_optype = be_get_img_optype(fsec->fsec_entry[i]); + old_fw_img = fsec->fsec_entry[i].optype == 0xFFFF; + + if (img_optype == 0xFFFF) + continue; + + if (flash_offset_support) + flash_optype = OPTYPE_OFFSET_SPECIFIED; + else + flash_optype = img_optype; + + /* Don't bother verifying CRC if an old FW image is being + * flashed + */ + if (old_fw_img) + goto flash; + + status = be_check_flash_crc(adapter, fw->data, img_offset, + img_size, filehdr_size + + img_hdrs_size, flash_optype, + &crc_match); + if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST || + base_status(status) == MCC_STATUS_ILLEGAL_FIELD) { + /* The current FW image on the card does not support + * OFFSET based flashing. Retry using older mechanism + * of OPTYPE based flashing + */ + if (flash_optype == OPTYPE_OFFSET_SPECIFIED) { + flash_offset_support = false; + goto retry_flash; + } + + /* The current FW image on the card does not recognize + * the new FLASH op_type. The FW download is partially + * complete. Reboot the server now to enable FW image + * to recognize the new FLASH op_type. To complete the + * remaining process, download the same FW again after + * the reboot. + */ + dev_err(dev, "Flash incomplete. Reset the server\n"); + dev_err(dev, "Download FW image again after reset\n"); + return -EAGAIN; + } else if (status) { + dev_err(dev, "Could not get CRC for 0x%x region\n", + img_optype); + return -EFAULT; + } + + if (crc_match) + continue; + +flash: + p = fw->data + filehdr_size + img_offset + img_hdrs_size; + if (p + img_size > fw->data + fw->size) + return -1; + + status = be_flash(adapter, p, flash_cmd, flash_optype, img_size, + img_offset); + + /* The current FW image on the card does not support OFFSET + * based flashing. Retry using older mechanism of OPTYPE based + * flashing + */ + if (base_status(status) == MCC_STATUS_ILLEGAL_FIELD && + flash_optype == OPTYPE_OFFSET_SPECIFIED) { + flash_offset_support = false; + goto retry_flash; + } + + /* For old FW images ignore ILLEGAL_FIELD error or errors on + * UFI_DIR region + */ + if (old_fw_img && + (base_status(status) == MCC_STATUS_ILLEGAL_FIELD || + (img_optype == OPTYPE_UFI_DIR && + base_status(status) == MCC_STATUS_FAILED))) { + continue; + } else if (status) { + dev_err(dev, "Flashing section type 0x%x failed\n", + img_type); + return -EFAULT; + } + } + return 0; +} + +int lancer_fw_download(struct be_adapter *adapter, + const struct firmware *fw) +{ + struct device *dev = &adapter->pdev->dev; + struct be_dma_mem flash_cmd; + const u8 *data_ptr = NULL; + u8 *dest_image_ptr = NULL; + size_t image_size = 0; + u32 chunk_size = 0; + u32 data_written = 0; + u32 offset = 0; + int status = 0; + u8 add_status = 0; + u8 change_status; + + if (!IS_ALIGNED(fw->size, sizeof(u32))) { + dev_err(dev, "FW image size should be multiple of 4\n"); + return -EINVAL; + } + + flash_cmd.size = sizeof(struct lancer_cmd_req_write_object) + + LANCER_FW_DOWNLOAD_CHUNK; + flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size, + &flash_cmd.dma, GFP_KERNEL); + if (!flash_cmd.va) + return -ENOMEM; + + dest_image_ptr = flash_cmd.va + + sizeof(struct lancer_cmd_req_write_object); + image_size = fw->size; + data_ptr = fw->data; + + while (image_size) { + chunk_size = min_t(u32, image_size, LANCER_FW_DOWNLOAD_CHUNK); + + /* Copy the image chunk content. */ + memcpy(dest_image_ptr, data_ptr, chunk_size); + + status = lancer_cmd_write_object(adapter, &flash_cmd, + chunk_size, offset, + LANCER_FW_DOWNLOAD_LOCATION, + &data_written, &change_status, + &add_status); + if (status) + break; + + offset += data_written; + data_ptr += data_written; + image_size -= data_written; + } + + if (!status) { + /* Commit the FW written */ + status = lancer_cmd_write_object(adapter, &flash_cmd, + 0, offset, + LANCER_FW_DOWNLOAD_LOCATION, + &data_written, &change_status, + &add_status); + } + + dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma); + if (status) { + dev_err(dev, "Firmware load error\n"); + return be_cmd_status(status); + } + + dev_info(dev, "Firmware flashed successfully\n"); + + if (change_status == LANCER_FW_RESET_NEEDED) { + dev_info(dev, "Resetting adapter to activate new FW\n"); + status = lancer_physdev_ctrl(adapter, + PHYSDEV_CONTROL_FW_RESET_MASK); + if (status) { + dev_err(dev, "Adapter busy, could not reset FW\n"); + dev_err(dev, "Reboot server to activate new FW\n"); + } + } else if (change_status != LANCER_NO_RESET_NEEDED) { + dev_info(dev, "Reboot server to activate new FW\n"); + } + + return 0; +} + +/* Check if the flash image file is compatible with the adapter that + * is being flashed. + */ +static bool be_check_ufi_compatibility(struct be_adapter *adapter, + struct flash_file_hdr_g3 *fhdr) +{ + if (!fhdr) { + dev_err(&adapter->pdev->dev, "Invalid FW UFI file"); + return false; + } + + /* First letter of the build version is used to identify + * which chip this image file is meant for. + */ + switch (fhdr->build[0]) { + case BLD_STR_UFI_TYPE_SH: + if (!skyhawk_chip(adapter)) + return false; + break; + case BLD_STR_UFI_TYPE_BE3: + if (!BE3_chip(adapter)) + return false; + break; + case BLD_STR_UFI_TYPE_BE2: + if (!BE2_chip(adapter)) + return false; + break; + default: + return false; + } + + /* In BE3 FW images the "asic_type_rev" field doesn't track the + * asic_rev of the chips it is compatible with. + * When asic_type_rev is 0 the image is compatible only with + * pre-BE3-R chips (asic_rev < 0x10) + */ + if (BEx_chip(adapter) && fhdr->asic_type_rev == 0) + return adapter->asic_rev < 0x10; + else + return (fhdr->asic_type_rev >= adapter->asic_rev); +} + +int be_fw_download(struct be_adapter *adapter, const struct firmware *fw) +{ + struct device *dev = &adapter->pdev->dev; + struct flash_file_hdr_g3 *fhdr3; + struct image_hdr *img_hdr_ptr; + int status = 0, i, num_imgs; + struct be_dma_mem flash_cmd; + + fhdr3 = (struct flash_file_hdr_g3 *)fw->data; + if (!be_check_ufi_compatibility(adapter, fhdr3)) { + dev_err(dev, "Flash image is not compatible with adapter\n"); + return -EINVAL; + } + + flash_cmd.size = sizeof(struct be_cmd_write_flashrom); + flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size, &flash_cmd.dma, + GFP_KERNEL); + if (!flash_cmd.va) + return -ENOMEM; + + num_imgs = le32_to_cpu(fhdr3->num_imgs); + for (i = 0; i < num_imgs; i++) { + img_hdr_ptr = (struct image_hdr *)(fw->data + + (sizeof(struct flash_file_hdr_g3) + + i * sizeof(struct image_hdr))); + if (!BE2_chip(adapter) && + le32_to_cpu(img_hdr_ptr->imageid) != 1) + continue; + + if (skyhawk_chip(adapter)) + status = be_flash_skyhawk(adapter, fw, &flash_cmd, + num_imgs); + else + status = be_flash_BEx(adapter, fw, &flash_cmd, + num_imgs); + } + + dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma); + if (!status) + dev_info(dev, "Firmware flashed successfully\n"); + + return status; +} + int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac, struct be_dma_mem *nonemb_cmd) { diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 8c6b606f0e90..4b0ca994d6c3 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -1411,6 +1411,9 @@ struct be_cmd_read_flash_crc { } __packed; /**************** Lancer Firmware Flash ************/ +#define LANCER_FW_DOWNLOAD_CHUNK (32 * 1024) +#define LANCER_FW_DOWNLOAD_LOCATION "/prg" + struct amap_lancer_write_obj_context { u8 write_length[24]; u8 reserved1[7]; @@ -2339,19 +2342,11 @@ int be_cmd_read_port_transceiver_data(struct be_adapter *adapter, u8 page_num, u8 *data); int be_cmd_query_cable_type(struct be_adapter *adapter); int be_cmd_query_sfp_info(struct be_adapter *adapter); -int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd, - u32 flash_oper, u32 flash_opcode, u32 img_offset, - u32 buf_size); -int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd, - u32 data_size, u32 data_offset, - const char *obj_name, u32 *data_written, - u8 *change_status, u8 *addn_status); int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd, u32 data_size, u32 data_offset, const char *obj_name, u32 *data_read, u32 *eof, u8 *addn_status); -int lancer_cmd_delete_object(struct be_adapter *adapter, const char *obj_name); -int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc, - u16 img_optype, u32 img_offset, u32 crc_offset); +int lancer_fw_download(struct be_adapter *adapter, const struct firmware *fw); +int be_fw_download(struct be_adapter *adapter, const struct firmware *fw); int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac, struct be_dma_mem *nonemb_cmd); int be_cmd_fw_init(struct be_adapter *adapter); diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 0bb6e5a29f8d..646b02136b0e 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -4490,570 +4490,6 @@ static void be_netpoll(struct net_device *netdev) } #endif -static char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "}; - -static bool phy_flashing_required(struct be_adapter *adapter) -{ - return (adapter->phy.phy_type == PHY_TYPE_TN_8022 && - adapter->phy.interface_type == PHY_TYPE_BASET_10GB); -} - -static bool is_comp_in_ufi(struct be_adapter *adapter, - struct flash_section_info *fsec, int type) -{ - int i = 0, img_type = 0; - struct flash_section_info_g2 *fsec_g2 = NULL; - - if (BE2_chip(adapter)) - fsec_g2 = (struct flash_section_info_g2 *)fsec; - - for (i = 0; i < MAX_FLASH_COMP; i++) { - if (fsec_g2) - img_type = le32_to_cpu(fsec_g2->fsec_entry[i].type); - else - img_type = le32_to_cpu(fsec->fsec_entry[i].type); - - if (img_type == type) - return true; - } - return false; - -} - -static struct flash_section_info *get_fsec_info(struct be_adapter *adapter, - int header_size, - const struct firmware *fw) -{ - struct flash_section_info *fsec = NULL; - const u8 *p = fw->data; - - p += header_size; - while (p < (fw->data + fw->size)) { - fsec = (struct flash_section_info *)p; - if (!memcmp(flash_cookie, fsec->cookie, sizeof(flash_cookie))) - return fsec; - p += 32; - } - return NULL; -} - -static int be_check_flash_crc(struct be_adapter *adapter, const u8 *p, - u32 img_offset, u32 img_size, int hdr_size, - u16 img_optype, bool *crc_match) -{ - u32 crc_offset; - int status; - u8 crc[4]; - - status = be_cmd_get_flash_crc(adapter, crc, img_optype, img_offset, - img_size - 4); - if (status) - return status; - - crc_offset = hdr_size + img_offset + img_size - 4; - - /* Skip flashing, if crc of flashed region matches */ - if (!memcmp(crc, p + crc_offset, 4)) - *crc_match = true; - else - *crc_match = false; - - return status; -} - -static int be_flash(struct be_adapter *adapter, const u8 *img, - struct be_dma_mem *flash_cmd, int optype, int img_size, - u32 img_offset) -{ - u32 flash_op, num_bytes, total_bytes = img_size, bytes_sent = 0; - struct be_cmd_write_flashrom *req = flash_cmd->va; - int status; - - while (total_bytes) { - num_bytes = min_t(u32, 32*1024, total_bytes); - - total_bytes -= num_bytes; - - if (!total_bytes) { - if (optype == OPTYPE_PHY_FW) - flash_op = FLASHROM_OPER_PHY_FLASH; - else - flash_op = FLASHROM_OPER_FLASH; - } else { - if (optype == OPTYPE_PHY_FW) - flash_op = FLASHROM_OPER_PHY_SAVE; - else - flash_op = FLASHROM_OPER_SAVE; - } - - memcpy(req->data_buf, img, num_bytes); - img += num_bytes; - status = be_cmd_write_flashrom(adapter, flash_cmd, optype, - flash_op, img_offset + - bytes_sent, num_bytes); - if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST && - optype == OPTYPE_PHY_FW) - break; - else if (status) - return status; - - bytes_sent += num_bytes; - } - return 0; -} - -/* For BE2, BE3 and BE3-R */ -static int be_flash_BEx(struct be_adapter *adapter, - const struct firmware *fw, - struct be_dma_mem *flash_cmd, int num_of_images) -{ - int img_hdrs_size = (num_of_images * sizeof(struct image_hdr)); - struct device *dev = &adapter->pdev->dev; - struct flash_section_info *fsec = NULL; - int status, i, filehdr_size, num_comp; - const struct flash_comp *pflashcomp; - bool crc_match; - const u8 *p; - - struct flash_comp gen3_flash_types[] = { - { BE3_ISCSI_PRIMARY_IMAGE_START, OPTYPE_ISCSI_ACTIVE, - BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_ISCSI}, - { BE3_REDBOOT_START, OPTYPE_REDBOOT, - BE3_REDBOOT_COMP_MAX_SIZE, IMAGE_BOOT_CODE}, - { BE3_ISCSI_BIOS_START, OPTYPE_BIOS, - BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_ISCSI}, - { BE3_PXE_BIOS_START, OPTYPE_PXE_BIOS, - BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_PXE}, - { BE3_FCOE_BIOS_START, OPTYPE_FCOE_BIOS, - BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_FCOE}, - { BE3_ISCSI_BACKUP_IMAGE_START, OPTYPE_ISCSI_BACKUP, - BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_ISCSI}, - { BE3_FCOE_PRIMARY_IMAGE_START, OPTYPE_FCOE_FW_ACTIVE, - BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_FCOE}, - { BE3_FCOE_BACKUP_IMAGE_START, OPTYPE_FCOE_FW_BACKUP, - BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_FCOE}, - { BE3_NCSI_START, OPTYPE_NCSI_FW, - BE3_NCSI_COMP_MAX_SIZE, IMAGE_NCSI}, - { BE3_PHY_FW_START, OPTYPE_PHY_FW, - BE3_PHY_FW_COMP_MAX_SIZE, IMAGE_FIRMWARE_PHY} - }; - - struct flash_comp gen2_flash_types[] = { - { BE2_ISCSI_PRIMARY_IMAGE_START, OPTYPE_ISCSI_ACTIVE, - BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_ISCSI}, - { BE2_REDBOOT_START, OPTYPE_REDBOOT, - BE2_REDBOOT_COMP_MAX_SIZE, IMAGE_BOOT_CODE}, - { BE2_ISCSI_BIOS_START, OPTYPE_BIOS, - BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_ISCSI}, - { BE2_PXE_BIOS_START, OPTYPE_PXE_BIOS, - BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_PXE}, - { BE2_FCOE_BIOS_START, OPTYPE_FCOE_BIOS, - BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_FCOE}, - { BE2_ISCSI_BACKUP_IMAGE_START, OPTYPE_ISCSI_BACKUP, - BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_ISCSI}, - { BE2_FCOE_PRIMARY_IMAGE_START, OPTYPE_FCOE_FW_ACTIVE, - BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_FCOE}, - { BE2_FCOE_BACKUP_IMAGE_START, OPTYPE_FCOE_FW_BACKUP, - BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_FCOE} - }; - - if (BE3_chip(adapter)) { - pflashcomp = gen3_flash_types; - filehdr_size = sizeof(struct flash_file_hdr_g3); - num_comp = ARRAY_SIZE(gen3_flash_types); - } else { - pflashcomp = gen2_flash_types; - filehdr_size = sizeof(struct flash_file_hdr_g2); - num_comp = ARRAY_SIZE(gen2_flash_types); - img_hdrs_size = 0; - } - - /* Get flash section info*/ - fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw); - if (!fsec) { - dev_err(dev, "Invalid Cookie. FW image may be corrupted\n"); - return -1; - } - for (i = 0; i < num_comp; i++) { - if (!is_comp_in_ufi(adapter, fsec, pflashcomp[i].img_type)) - continue; - - if ((pflashcomp[i].optype == OPTYPE_NCSI_FW) && - memcmp(adapter->fw_ver, "3.102.148.0", 11) < 0) - continue; - - if (pflashcomp[i].optype == OPTYPE_PHY_FW && - !phy_flashing_required(adapter)) - continue; - - if (pflashcomp[i].optype == OPTYPE_REDBOOT) { - status = be_check_flash_crc(adapter, fw->data, - pflashcomp[i].offset, - pflashcomp[i].size, - filehdr_size + - img_hdrs_size, - OPTYPE_REDBOOT, &crc_match); - if (status) { - dev_err(dev, - "Could not get CRC for 0x%x region\n", - pflashcomp[i].optype); - continue; - } - - if (crc_match) - continue; - } - - p = fw->data + filehdr_size + pflashcomp[i].offset + - img_hdrs_size; - if (p + pflashcomp[i].size > fw->data + fw->size) - return -1; - - status = be_flash(adapter, p, flash_cmd, pflashcomp[i].optype, - pflashcomp[i].size, 0); - if (status) { - dev_err(dev, "Flashing section type 0x%x failed\n", - pflashcomp[i].img_type); - return status; - } - } - return 0; -} - -static u16 be_get_img_optype(struct flash_section_entry fsec_entry) -{ - u32 img_type = le32_to_cpu(fsec_entry.type); - u16 img_optype = le16_to_cpu(fsec_entry.optype); - - if (img_optype != 0xFFFF) - return img_optype; - - switch (img_type) { - case IMAGE_FIRMWARE_ISCSI: - img_optype = OPTYPE_ISCSI_ACTIVE; - break; - case IMAGE_BOOT_CODE: - img_optype = OPTYPE_REDBOOT; - break; - case IMAGE_OPTION_ROM_ISCSI: - img_optype = OPTYPE_BIOS; - break; - case IMAGE_OPTION_ROM_PXE: - img_optype = OPTYPE_PXE_BIOS; - break; - case IMAGE_OPTION_ROM_FCOE: - img_optype = OPTYPE_FCOE_BIOS; - break; - case IMAGE_FIRMWARE_BACKUP_ISCSI: - img_optype = OPTYPE_ISCSI_BACKUP; - break; - case IMAGE_NCSI: - img_optype = OPTYPE_NCSI_FW; - break; - case IMAGE_FLASHISM_JUMPVECTOR: - img_optype = OPTYPE_FLASHISM_JUMPVECTOR; - break; - case IMAGE_FIRMWARE_PHY: - img_optype = OPTYPE_SH_PHY_FW; - break; - case IMAGE_REDBOOT_DIR: - img_optype = OPTYPE_REDBOOT_DIR; - break; - case IMAGE_REDBOOT_CONFIG: - img_optype = OPTYPE_REDBOOT_CONFIG; - break; - case IMAGE_UFI_DIR: - img_optype = OPTYPE_UFI_DIR; - break; - default: - break; - } - - return img_optype; -} - -static int be_flash_skyhawk(struct be_adapter *adapter, - const struct firmware *fw, - struct be_dma_mem *flash_cmd, int num_of_images) -{ - int img_hdrs_size = num_of_images * sizeof(struct image_hdr); - bool crc_match, old_fw_img, flash_offset_support = true; - struct device *dev = &adapter->pdev->dev; - struct flash_section_info *fsec = NULL; - u32 img_offset, img_size, img_type; - u16 img_optype, flash_optype; - int status, i, filehdr_size; - const u8 *p; - - filehdr_size = sizeof(struct flash_file_hdr_g3); - fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw); - if (!fsec) { - dev_err(dev, "Invalid Cookie. FW image may be corrupted\n"); - return -EINVAL; - } - -retry_flash: - for (i = 0; i < le32_to_cpu(fsec->fsec_hdr.num_images); i++) { - img_offset = le32_to_cpu(fsec->fsec_entry[i].offset); - img_size = le32_to_cpu(fsec->fsec_entry[i].pad_size); - img_type = le32_to_cpu(fsec->fsec_entry[i].type); - img_optype = be_get_img_optype(fsec->fsec_entry[i]); - old_fw_img = fsec->fsec_entry[i].optype == 0xFFFF; - - if (img_optype == 0xFFFF) - continue; - - if (flash_offset_support) - flash_optype = OPTYPE_OFFSET_SPECIFIED; - else - flash_optype = img_optype; - - /* Don't bother verifying CRC if an old FW image is being - * flashed - */ - if (old_fw_img) - goto flash; - - status = be_check_flash_crc(adapter, fw->data, img_offset, - img_size, filehdr_size + - img_hdrs_size, flash_optype, - &crc_match); - if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST || - base_status(status) == MCC_STATUS_ILLEGAL_FIELD) { - /* The current FW image on the card does not support - * OFFSET based flashing. Retry using older mechanism - * of OPTYPE based flashing - */ - if (flash_optype == OPTYPE_OFFSET_SPECIFIED) { - flash_offset_support = false; - goto retry_flash; - } - - /* The current FW image on the card does not recognize - * the new FLASH op_type. The FW download is partially - * complete. Reboot the server now to enable FW image - * to recognize the new FLASH op_type. To complete the - * remaining process, download the same FW again after - * the reboot. - */ - dev_err(dev, "Flash incomplete. Reset the server\n"); - dev_err(dev, "Download FW image again after reset\n"); - return -EAGAIN; - } else if (status) { - dev_err(dev, "Could not get CRC for 0x%x region\n", - img_optype); - return -EFAULT; - } - - if (crc_match) - continue; - -flash: - p = fw->data + filehdr_size + img_offset + img_hdrs_size; - if (p + img_size > fw->data + fw->size) - return -1; - - status = be_flash(adapter, p, flash_cmd, flash_optype, img_size, - img_offset); - - /* The current FW image on the card does not support OFFSET - * based flashing. Retry using older mechanism of OPTYPE based - * flashing - */ - if (base_status(status) == MCC_STATUS_ILLEGAL_FIELD && - flash_optype == OPTYPE_OFFSET_SPECIFIED) { - flash_offset_support = false; - goto retry_flash; - } - - /* For old FW images ignore ILLEGAL_FIELD error or errors on - * UFI_DIR region - */ - if (old_fw_img && - (base_status(status) == MCC_STATUS_ILLEGAL_FIELD || - (img_optype == OPTYPE_UFI_DIR && - base_status(status) == MCC_STATUS_FAILED))) { - continue; - } else if (status) { - dev_err(dev, "Flashing section type 0x%x failed\n", - img_type); - return -EFAULT; - } - } - return 0; -} - -static int lancer_fw_download(struct be_adapter *adapter, - const struct firmware *fw) -{ -#define LANCER_FW_DOWNLOAD_CHUNK (32 * 1024) -#define LANCER_FW_DOWNLOAD_LOCATION "/prg" - struct device *dev = &adapter->pdev->dev; - struct be_dma_mem flash_cmd; - const u8 *data_ptr = NULL; - u8 *dest_image_ptr = NULL; - size_t image_size = 0; - u32 chunk_size = 0; - u32 data_written = 0; - u32 offset = 0; - int status = 0; - u8 add_status = 0; - u8 change_status; - - if (!IS_ALIGNED(fw->size, sizeof(u32))) { - dev_err(dev, "FW image size should be multiple of 4\n"); - return -EINVAL; - } - - flash_cmd.size = sizeof(struct lancer_cmd_req_write_object) - + LANCER_FW_DOWNLOAD_CHUNK; - flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size, - &flash_cmd.dma, GFP_KERNEL); - if (!flash_cmd.va) - return -ENOMEM; - - dest_image_ptr = flash_cmd.va + - sizeof(struct lancer_cmd_req_write_object); - image_size = fw->size; - data_ptr = fw->data; - - while (image_size) { - chunk_size = min_t(u32, image_size, LANCER_FW_DOWNLOAD_CHUNK); - - /* Copy the image chunk content. */ - memcpy(dest_image_ptr, data_ptr, chunk_size); - - status = lancer_cmd_write_object(adapter, &flash_cmd, - chunk_size, offset, - LANCER_FW_DOWNLOAD_LOCATION, - &data_written, &change_status, - &add_status); - if (status) - break; - - offset += data_written; - data_ptr += data_written; - image_size -= data_written; - } - - if (!status) { - /* Commit the FW written */ - status = lancer_cmd_write_object(adapter, &flash_cmd, - 0, offset, - LANCER_FW_DOWNLOAD_LOCATION, - &data_written, &change_status, - &add_status); - } - - dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma); - if (status) { - dev_err(dev, "Firmware load error\n"); - return be_cmd_status(status); - } - - dev_info(dev, "Firmware flashed successfully\n"); - - if (change_status == LANCER_FW_RESET_NEEDED) { - dev_info(dev, "Resetting adapter to activate new FW\n"); - status = lancer_physdev_ctrl(adapter, - PHYSDEV_CONTROL_FW_RESET_MASK); - if (status) { - dev_err(dev, "Adapter busy, could not reset FW\n"); - dev_err(dev, "Reboot server to activate new FW\n"); - } - } else if (change_status != LANCER_NO_RESET_NEEDED) { - dev_info(dev, "Reboot server to activate new FW\n"); - } - - return 0; -} - -/* Check if the flash image file is compatible with the adapter that - * is being flashed. - */ -static bool be_check_ufi_compatibility(struct be_adapter *adapter, - struct flash_file_hdr_g3 *fhdr) -{ - if (!fhdr) { - dev_err(&adapter->pdev->dev, "Invalid FW UFI file"); - return false; - } - - /* First letter of the build version is used to identify - * which chip this image file is meant for. - */ - switch (fhdr->build[0]) { - case BLD_STR_UFI_TYPE_SH: - if (!skyhawk_chip(adapter)) - return false; - break; - case BLD_STR_UFI_TYPE_BE3: - if (!BE3_chip(adapter)) - return false; - break; - case BLD_STR_UFI_TYPE_BE2: - if (!BE2_chip(adapter)) - return false; - break; - default: - return false; - } - - /* In BE3 FW images the "asic_type_rev" field doesn't track the - * asic_rev of the chips it is compatible with. - * When asic_type_rev is 0 the image is compatible only with - * pre-BE3-R chips (asic_rev < 0x10) - */ - if (BEx_chip(adapter) && fhdr->asic_type_rev == 0) - return adapter->asic_rev < 0x10; - else - return (fhdr->asic_type_rev >= adapter->asic_rev); -} - -static int be_fw_download(struct be_adapter *adapter, const struct firmware* fw) -{ - struct device *dev = &adapter->pdev->dev; - struct flash_file_hdr_g3 *fhdr3; - struct image_hdr *img_hdr_ptr; - int status = 0, i, num_imgs; - struct be_dma_mem flash_cmd; - - fhdr3 = (struct flash_file_hdr_g3 *)fw->data; - if (!be_check_ufi_compatibility(adapter, fhdr3)) { - dev_err(dev, "Flash image is not compatible with adapter\n"); - return -EINVAL; - } - - flash_cmd.size = sizeof(struct be_cmd_write_flashrom); - flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size, &flash_cmd.dma, - GFP_KERNEL); - if (!flash_cmd.va) - return -ENOMEM; - - num_imgs = le32_to_cpu(fhdr3->num_imgs); - for (i = 0; i < num_imgs; i++) { - img_hdr_ptr = (struct image_hdr *)(fw->data + - (sizeof(struct flash_file_hdr_g3) + - i * sizeof(struct image_hdr))); - if (!BE2_chip(adapter) && - le32_to_cpu(img_hdr_ptr->imageid) != 1) - continue; - - if (skyhawk_chip(adapter)) - status = be_flash_skyhawk(adapter, fw, &flash_cmd, - num_imgs); - else - status = be_flash_BEx(adapter, fw, &flash_cmd, - num_imgs); - } - - dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma); - if (!status) - dev_info(dev, "Firmware flashed successfully\n"); - - return status; -} - int be_load_fw(struct be_adapter *adapter, u8 *fw_file) { const struct firmware *fw; -- cgit v1.2.3-59-g8ed1b From 6b525782820f49079a0848f72d7a379f0c101cf8 Mon Sep 17 00:00:00 2001 From: Suresh Reddy Date: Wed, 30 Dec 2015 01:29:00 -0500 Subject: be2net: log digital signature errors while flashing FW image (based on a jumper setting on the adapter.) In this mode, the FW image when flashed is authenticated with a digital signature. This patch logs appropriate error messages and return a status to ethtool when errors relating to FW image authentication occur. Signed-off-by: Suresh Reddy Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_cmds.c | 14 +++++++++++++- drivers/net/ethernet/emulex/benet/be_cmds.h | 4 +++- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index da3b39873470..451f9eaa27a2 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -2959,7 +2959,19 @@ flash: } else if (status) { dev_err(dev, "Flashing section type 0x%x failed\n", img_type); - return -EFAULT; + + switch (addl_status(status)) { + case MCC_ADDL_STATUS_MISSING_SIGNATURE: + dev_err(dev, + "Digital signature missing in FW\n"); + return -EINVAL; + case MCC_ADDL_STATUS_INVALID_SIGNATURE: + dev_err(dev, + "Invalid digital signature in FW\n"); + return -EINVAL; + default: + return -EFAULT; + } } } return 0; diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 4b0ca994d6c3..16415cad1298 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -66,7 +66,9 @@ enum mcc_addl_status { MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES = 0x16, MCC_ADDL_STATUS_FLASH_IMAGE_CRC_MISMATCH = 0x4d, MCC_ADDL_STATUS_TOO_MANY_INTERFACES = 0x4a, - MCC_ADDL_STATUS_INSUFFICIENT_VLANS = 0xab + MCC_ADDL_STATUS_INSUFFICIENT_VLANS = 0xab, + MCC_ADDL_STATUS_INVALID_SIGNATURE = 0x56, + MCC_ADDL_STATUS_MISSING_SIGNATURE = 0x57 }; #define CQE_BASE_STATUS_MASK 0xFFFF -- cgit v1.2.3-59-g8ed1b From fdf81bfb7aab307146cdc33c1d741db324572ca3 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Wed, 30 Dec 2015 01:29:01 -0500 Subject: be2net: remove a line of code that has no effect This patch removes a line of code that changes adapter->recommended_prio value followed by yet another assignment. Also, the variable is used to store the vlan priority value that is already shifted to the PCP bits position in the vlan tag format. Hence, the name of this variable is changed to recommended_prio_bits. Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be.h | 2 +- drivers/net/ethernet/emulex/benet/be_cmds.c | 3 +-- drivers/net/ethernet/emulex/benet/be_main.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index d463563e1f70..93e5eab20e02 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -521,7 +521,7 @@ struct be_adapter { struct be_drv_stats drv_stats; struct be_aic_obj aic_obj[MAX_EVT_QS]; u8 vlan_prio_bmap; /* Available Priority BitMap */ - u16 recommended_prio; /* Recommended Priority */ + u16 recommended_prio_bits;/* Recommended Priority bits in vlan tag */ struct be_dma_mem rx_filter; /* Cmd DMA mem for rx-filter */ struct be_dma_mem stats_cmd; diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 451f9eaa27a2..7c0e7ff4679f 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -308,8 +308,7 @@ static void be_async_grp5_cos_priority_process(struct be_adapter *adapter, if (evt->valid) { adapter->vlan_prio_bmap = evt->available_priority_bmap; - adapter->recommended_prio &= ~VLAN_PRIO_MASK; - adapter->recommended_prio = + adapter->recommended_prio_bits = evt->reco_default_priority << VLAN_PRIO_SHIFT; } } diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 646b02136b0e..c9f9d4b7e6af 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -729,7 +729,7 @@ static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter, /* If vlan priority provided by OS is NOT in available bmap */ if (!(adapter->vlan_prio_bmap & (1 << vlan_prio))) vlan_tag = (vlan_tag & ~VLAN_PRIO_MASK) | - adapter->recommended_prio; + adapter->recommended_prio_bits; return vlan_tag; } -- cgit v1.2.3-59-g8ed1b From 04e888de2d72b587f81a18e42cb9af2c020ef63b Mon Sep 17 00:00:00 2001 From: Venkat Duvvuru Date: Wed, 30 Dec 2015 01:29:02 -0500 Subject: be2net: remove unused error variables eeh_error, fw_timeout, hw_error variables in the be_adapter structure are not used anymore. An earlier patch that introduced adapter->err_flags to store this information missed removing these variables. Signed-off-by: Venkat Duvvuru Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 93e5eab20e02..0af32f15007c 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -547,10 +547,6 @@ struct be_adapter { u32 beacon_state; /* for set_phys_id */ - bool eeh_error; - bool fw_timeout; - bool hw_error; - u32 port_num; char port_name; u8 mc_type; -- cgit v1.2.3-59-g8ed1b From 980df249bdab2adc8522d2178bfffceba79c5e6b Mon Sep 17 00:00:00 2001 From: Suresh Reddy Date: Wed, 30 Dec 2015 01:29:03 -0500 Subject: be2net: fix port-res desc query of GET_PROFILE_CONFIG FW cmd Commit 72ef3a88fa8e ("be2net: set pci_func_num while issuing GET_PROFILE_CONFIG cmd") passed a specific pf_num while issuing a GET_PROFILE_CONFIG cmd as FW returns descriptors for all functions when pf_num is zero. But, when pf_num is set to a non-zero value, FW does not return the Port resource descriptor. This patch fixes this by setting pf_num to 0 while issuing the query cmd and adds code to pick the correct NIC resource descriptor from the list of descriptors returned by FW. Fixes: 72ef3a88fa8e ("be2net: set pci_func_num while issuing GET_PROFILE_CONFIG cmd") Signed-off-by: Suresh Reddy Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be.h | 4 +- drivers/net/ethernet/emulex/benet/be_cmds.c | 85 ++++++++++++++++------------- drivers/net/ethernet/emulex/benet/be_cmds.h | 11 +--- drivers/net/ethernet/emulex/benet/be_main.c | 16 ++++-- 4 files changed, 63 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 0af32f15007c..66988f4312f9 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -570,6 +570,8 @@ struct be_adapter { struct be_resources pool_res; /* resources available for the port */ struct be_resources res; /* resources available for the func */ u16 num_vfs; /* Number of VFs provisioned by PF */ + u8 pf_num; /* Numbering used by FW, starts at 0 */ + u8 vf_num; /* Numbering used by FW, starts at 1 */ u8 virtfn; struct be_vf_cfg *vf_cfg; bool be3_native; @@ -587,8 +589,6 @@ struct be_adapter { u32 msg_enable; int be_get_temp_freq; struct be_hwmon hwmon_info; - u8 pf_number; - u8 pci_func_num; struct rss_info rss_info; /* Filters for packets that need to be sent to BMC */ u32 bmc_filt_mask; diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 7c0e7ff4679f..b92ee06555c5 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -3466,7 +3466,6 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter) if (!status) { attribs = attribs_cmd.va + sizeof(struct be_cmd_resp_hdr); adapter->hba_port_num = attribs->hba_attribs.phy_port; - adapter->pci_func_num = attribs->pci_func_num; serial_num = attribs->hba_attribs.controller_serial_number; for (i = 0; i < CNTL_SERIAL_NUM_WORDS; i++) adapter->serial_num[i] = le32_to_cpu(serial_num[i]) & @@ -4149,14 +4148,16 @@ int be_cmd_query_port_name(struct be_adapter *adapter) return status; } -/* Descriptor type */ -enum { - FUNC_DESC = 1, - VFT_DESC = 2 -}; - +/* When more than 1 NIC descriptor is present in the descriptor list, + * the caller must specify the pf_num to obtain the NIC descriptor + * corresponding to its pci function. + * get_vft must be true when the caller wants the VF-template desc of the + * PF-pool. + * The pf_num should be set to PF_NUM_IGNORE when the caller knows + * that only it's NIC descriptor is present in the descriptor list. + */ static struct be_nic_res_desc *be_get_nic_desc(u8 *buf, u32 desc_count, - int desc_type) + bool get_vft, u8 pf_num) { struct be_res_desc_hdr *hdr = (struct be_res_desc_hdr *)buf; struct be_nic_res_desc *nic; @@ -4166,40 +4167,42 @@ static struct be_nic_res_desc *be_get_nic_desc(u8 *buf, u32 desc_count, if (hdr->desc_type == NIC_RESOURCE_DESC_TYPE_V0 || hdr->desc_type == NIC_RESOURCE_DESC_TYPE_V1) { nic = (struct be_nic_res_desc *)hdr; - if (desc_type == FUNC_DESC || - (desc_type == VFT_DESC && - nic->flags & (1 << VFT_SHIFT))) + + if ((pf_num == PF_NUM_IGNORE || + nic->pf_num == pf_num) && + (!get_vft || nic->flags & BIT(VFT_SHIFT))) return nic; } - hdr->desc_len = hdr->desc_len ? : RESOURCE_DESC_SIZE_V0; hdr = (void *)hdr + hdr->desc_len; } return NULL; } -static struct be_nic_res_desc *be_get_vft_desc(u8 *buf, u32 desc_count) +static struct be_nic_res_desc *be_get_vft_desc(u8 *buf, u32 desc_count, + u8 pf_num) { - return be_get_nic_desc(buf, desc_count, VFT_DESC); + return be_get_nic_desc(buf, desc_count, true, pf_num); } -static struct be_nic_res_desc *be_get_func_nic_desc(u8 *buf, u32 desc_count) +static struct be_nic_res_desc *be_get_func_nic_desc(u8 *buf, u32 desc_count, + u8 pf_num) { - return be_get_nic_desc(buf, desc_count, FUNC_DESC); + return be_get_nic_desc(buf, desc_count, false, pf_num); } -static struct be_pcie_res_desc *be_get_pcie_desc(u8 devfn, u8 *buf, - u32 desc_count) +static struct be_pcie_res_desc *be_get_pcie_desc(u8 *buf, u32 desc_count, + u8 pf_num) { struct be_res_desc_hdr *hdr = (struct be_res_desc_hdr *)buf; struct be_pcie_res_desc *pcie; int i; for (i = 0; i < desc_count; i++) { - if ((hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V0 || - hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V1)) { - pcie = (struct be_pcie_res_desc *)hdr; - if (pcie->pf_num == devfn) + if (hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V0 || + hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V1) { + pcie = (struct be_pcie_res_desc *)hdr; + if (pcie->pf_num == pf_num) return pcie; } @@ -4284,13 +4287,23 @@ int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res) u32 desc_count = le32_to_cpu(resp->desc_count); struct be_nic_res_desc *desc; - desc = be_get_func_nic_desc(resp->func_param, desc_count); + /* GET_FUNC_CONFIG returns resource descriptors of the + * current function only. So, pf_num should be set to + * PF_NUM_IGNORE. + */ + desc = be_get_func_nic_desc(resp->func_param, desc_count, + PF_NUM_IGNORE); if (!desc) { status = -EINVAL; goto err; } - adapter->pf_number = desc->pf_num; - be_copy_nic_desc(res, desc); + + /* Store pf_num & vf_num for later use in GET_PROFILE_CONFIG */ + adapter->pf_num = desc->pf_num; + adapter->vf_num = desc->vf_num; + + if (res) + be_copy_nic_desc(res, desc); } err: mutex_unlock(&adapter->mbox_lock); @@ -4300,10 +4313,7 @@ err: return status; } -/* Will use MBOX only if MCCQ has not been created - * non-zero domain => a PF is querying this on behalf of a VF - * zero domain => a PF or a VF is querying this for itself - */ +/* Will use MBOX only if MCCQ has not been created */ int be_cmd_get_profile_config(struct be_adapter *adapter, struct be_resources *res, u8 query, u8 domain) { @@ -4333,12 +4343,7 @@ int be_cmd_get_profile_config(struct be_adapter *adapter, if (!lancer_chip(adapter)) req->hdr.version = 1; req->type = ACTIVE_PROFILE_TYPE; - /* When a function is querying profile information relating to - * itself hdr.pf_number must be set to it's pci_func_num + 1 - */ req->hdr.domain = domain; - if (domain == 0) - req->hdr.pf_num = adapter->pci_func_num + 1; /* When QUERY_MODIFIABLE_FIELDS_TYPE bit is set, cmd returns the * descriptors with all bits set to "1" for the fields which can be @@ -4354,8 +4359,8 @@ int be_cmd_get_profile_config(struct be_adapter *adapter, resp = cmd.va; desc_count = le16_to_cpu(resp->desc_count); - pcie = be_get_pcie_desc(adapter->pdev->devfn, resp->func_param, - desc_count); + pcie = be_get_pcie_desc(resp->func_param, desc_count, + adapter->pf_num); if (pcie) res->max_vfs = le16_to_cpu(pcie->num_vfs); @@ -4363,11 +4368,13 @@ int be_cmd_get_profile_config(struct be_adapter *adapter, if (port) adapter->mc_type = port->mc_type; - nic = be_get_func_nic_desc(resp->func_param, desc_count); + nic = be_get_func_nic_desc(resp->func_param, desc_count, + adapter->pf_num); if (nic) be_copy_nic_desc(res, nic); - vf_res = be_get_vft_desc(resp->func_param, desc_count); + vf_res = be_get_vft_desc(resp->func_param, desc_count, + adapter->pf_num); if (vf_res) res->vf_if_cap_flags = vf_res->cap_flags; err: @@ -4457,7 +4464,7 @@ int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate, u16 link_speed, return be_cmd_set_qos(adapter, max_rate / 10, domain); be_reset_nic_desc(&nic_desc); - nic_desc.pf_num = adapter->pf_number; + nic_desc.pf_num = adapter->pf_num; nic_desc.vf_num = domain; nic_desc.bw_min = 0; if (lancer_chip(adapter)) { diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 16415cad1298..50981700cdb0 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -291,9 +291,7 @@ struct be_cmd_req_hdr { u32 timeout; /* dword 1 */ u32 request_length; /* dword 2 */ u8 version; /* dword 3 */ - u8 rsvd1; /* dword 3 */ - u8 pf_num; /* dword 3 */ - u8 rsvd2; /* dword 3 */ + u8 rsvd[3]; /* dword 3 */ }; #define RESP_HDR_INFO_OPCODE_SHIFT 0 /* bits 0 - 7 */ @@ -1676,11 +1674,7 @@ struct mgmt_hba_attribs { struct mgmt_controller_attrib { struct mgmt_hba_attribs hba_attribs; - u32 rsvd0[2]; - u16 rsvd1; - u8 pci_func_num; - u8 rsvd2; - u32 rsvd3[7]; + u32 rsvd0[10]; } __packed; struct be_cmd_req_cntl_attribs { @@ -2105,6 +2099,7 @@ struct be_port_res_desc { #define NV_TYPE_VXLAN 3 #define SOCVID_SHIFT 2 /* Strip outer vlan */ #define RCVID_SHIFT 4 /* Report vlan */ +#define PF_NUM_IGNORE 255 u8 nv_flags; u8 rsvd2; __le16 nv_port; /* vxlan/gre port */ diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index c9f9d4b7e6af..7039870a4e43 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -4204,6 +4204,10 @@ static int be_get_config(struct be_adapter *adapter) int status, level; u16 profile_id; + status = be_cmd_get_cntl_attributes(adapter); + if (status) + return status; + status = be_cmd_query_fw_cfg(adapter); if (status) return status; @@ -4402,10 +4406,14 @@ static int be_setup(struct be_adapter *adapter) if (!lancer_chip(adapter)) be_cmd_req_native_mode(adapter); - /* Need to invoke this cmd first to get the PCI Function Number */ - status = be_cmd_get_cntl_attributes(adapter); - if (status) - return status; + /* invoke this cmd first to get pf_num and vf_num which are needed + * for issuing profile related cmds + */ + if (!BEx_chip(adapter)) { + status = be_cmd_get_func_config(adapter, NULL); + if (status) + return status; + } if (!BE2_chip(adapter) && be_physfn(adapter)) be_alloc_sriov_res(adapter); -- cgit v1.2.3-59-g8ed1b From fd7ff6f06a4eda894e309bf21c6f11fc86f08841 Mon Sep 17 00:00:00 2001 From: Venkat Duvvuru Date: Wed, 30 Dec 2015 01:29:04 -0500 Subject: be2net: support ethtool get-dump option This patch adds support for ethtool's --get-dump option in be2net, to retrieve FW dump. In the past when this option was not yet available, this feature was supported via the --register-dump option as a workaround. This patch removes support for FW-dump via --register-dump option as it is now available via --get-dump option. Even though the "ethtool --register-dump" cmd which used to work earlier, will now fail with ENOTSUPP error, we feel it is not an issue as this is used only for diagnostics purpose. Signed-off-by: Venkat Duvvuru Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be.h | 1 + drivers/net/ethernet/emulex/benet/be_cmds.c | 38 ++++-------- drivers/net/ethernet/emulex/benet/be_cmds.h | 4 +- drivers/net/ethernet/emulex/benet/be_ethtool.c | 82 ++++++++++++++++---------- drivers/net/ethernet/emulex/benet/be_main.c | 3 + 5 files changed, 71 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 66988f4312f9..8b8212fda632 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -592,6 +592,7 @@ struct be_adapter { struct rss_info rss_info; /* Filters for packets that need to be sent to BMC */ u32 bmc_filt_mask; + u32 fat_dump_len; u16 serial_num[CNTL_SERIAL_NUM_WORDS]; }; diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index b92ee06555c5..b63d8ad2e115 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -1712,49 +1712,40 @@ err: } /* Uses synchronous mcc */ -int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size) +int be_cmd_get_fat_dump_len(struct be_adapter *adapter, u32 *dump_size) { - struct be_mcc_wrb *wrb; + struct be_mcc_wrb wrb = {0}; struct be_cmd_req_get_fat *req; int status; - spin_lock_bh(&adapter->mcc_lock); - - wrb = wrb_from_mccq(adapter); - if (!wrb) { - status = -EBUSY; - goto err; - } - req = embedded_payload(wrb); + req = embedded_payload(&wrb); be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, - OPCODE_COMMON_MANAGE_FAT, sizeof(*req), wrb, - NULL); + OPCODE_COMMON_MANAGE_FAT, sizeof(*req), + &wrb, NULL); req->fat_operation = cpu_to_le32(QUERY_FAT); - status = be_mcc_notify_wait(adapter); + status = be_cmd_notify_wait(adapter, &wrb); if (!status) { - struct be_cmd_resp_get_fat *resp = embedded_payload(wrb); + struct be_cmd_resp_get_fat *resp = embedded_payload(&wrb); - if (log_size && resp->log_size) - *log_size = le32_to_cpu(resp->log_size) - + if (dump_size && resp->log_size) + *dump_size = le32_to_cpu(resp->log_size) - sizeof(u32); } -err: - spin_unlock_bh(&adapter->mcc_lock); return status; } -int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf) +int be_cmd_get_fat_dump(struct be_adapter *adapter, u32 buf_len, void *buf) { struct be_dma_mem get_fat_cmd; struct be_mcc_wrb *wrb; struct be_cmd_req_get_fat *req; u32 offset = 0, total_size, buf_size, log_offset = sizeof(u32), payload_len; - int status = 0; + int status; if (buf_len == 0) - return -EIO; + return 0; total_size = buf_len; @@ -1762,11 +1753,8 @@ int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf) get_fat_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, get_fat_cmd.size, &get_fat_cmd.dma, GFP_ATOMIC); - if (!get_fat_cmd.va) { - dev_err(&adapter->pdev->dev, - "Memory allocation failure while reading FAT data\n"); + if (!get_fat_cmd.va) return -ENOMEM; - } spin_lock_bh(&adapter->mcc_lock); diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 50981700cdb0..241819b36ca7 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -2365,9 +2365,9 @@ int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate, void be_detect_error(struct be_adapter *adapter); int be_cmd_get_die_temperature(struct be_adapter *adapter); int be_cmd_get_cntl_attributes(struct be_adapter *adapter); +int be_cmd_get_fat_dump_len(struct be_adapter *adapter, u32 *dump_size); +int be_cmd_get_fat_dump(struct be_adapter *adapter, u32 buf_len, void *buf); int be_cmd_req_native_mode(struct be_adapter *adapter); -int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size); -int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf); int be_cmd_get_fn_privileges(struct be_adapter *adapter, u32 *privilege, u32 domain); int be_cmd_set_fn_privileges(struct be_adapter *adapter, u32 privileges, diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index d2a5baf019ab..a19ac441336f 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -250,6 +250,19 @@ static u32 lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name) return data_read; } +static int be_get_dump_len(struct be_adapter *adapter) +{ + u32 dump_size = 0; + + if (lancer_chip(adapter)) + dump_size = lancer_cmd_get_file_len(adapter, + LANCER_FW_DUMP_FILE); + else + dump_size = adapter->fat_dump_len; + + return dump_size; +} + static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name, u32 buf_len, void *buf) { @@ -291,37 +304,18 @@ static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name, return status; } -static int be_get_reg_len(struct net_device *netdev) +static int be_read_dump_data(struct be_adapter *adapter, u32 dump_len, + void *buf) { - struct be_adapter *adapter = netdev_priv(netdev); - u32 log_size = 0; - - if (!check_privilege(adapter, MAX_PRIVILEGES)) - return 0; - - if (be_physfn(adapter)) { - if (lancer_chip(adapter)) - log_size = lancer_cmd_get_file_len(adapter, - LANCER_FW_DUMP_FILE); - else - be_cmd_get_reg_len(adapter, &log_size); - } - return log_size; -} + int status = 0; -static void -be_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *buf) -{ - struct be_adapter *adapter = netdev_priv(netdev); + if (lancer_chip(adapter)) + status = lancer_cmd_read_file(adapter, LANCER_FW_DUMP_FILE, + dump_len, buf); + else + status = be_cmd_get_fat_dump(adapter, dump_len, buf); - if (be_physfn(adapter)) { - memset(buf, 0, regs->len); - if (lancer_chip(adapter)) - lancer_cmd_read_file(adapter, LANCER_FW_DUMP_FILE, - regs->len, buf); - else - be_cmd_get_regs(adapter, regs->len, buf); - } + return status; } static int be_get_coalesce(struct net_device *netdev, @@ -914,6 +908,34 @@ static int be_do_flash(struct net_device *netdev, struct ethtool_flash *efl) return be_load_fw(adapter, efl->data); } +static int +be_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) +{ + struct be_adapter *adapter = netdev_priv(netdev); + + if (!check_privilege(adapter, MAX_PRIVILEGES)) + return -EOPNOTSUPP; + + dump->len = be_get_dump_len(adapter); + dump->version = 1; + dump->flag = 0x1; /* FW dump is enabled */ + return 0; +} + +static int +be_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, + void *buf) +{ + struct be_adapter *adapter = netdev_priv(netdev); + int status; + + if (!check_privilege(adapter, MAX_PRIVILEGES)) + return -EOPNOTSUPP; + + status = be_read_dump_data(adapter, dump->len, buf); + return be_cmd_status(status); +} + static int be_get_eeprom_len(struct net_device *netdev) { struct be_adapter *adapter = netdev_priv(netdev); @@ -1311,8 +1333,6 @@ const struct ethtool_ops be_ethtool_ops = { .set_msglevel = be_set_msg_level, .get_sset_count = be_get_sset_count, .get_ethtool_stats = be_get_ethtool_stats, - .get_regs_len = be_get_reg_len, - .get_regs = be_get_regs, .flash_device = be_do_flash, .self_test = be_self_test, .get_rxnfc = be_get_rxnfc, @@ -1321,6 +1341,8 @@ const struct ethtool_ops be_ethtool_ops = { .get_rxfh_key_size = be_get_rxfh_key_size, .get_rxfh = be_get_rxfh, .set_rxfh = be_set_rxfh, + .get_dump_flag = be_get_dump_flag, + .get_dump_data = be_get_dump_data, .get_channels = be_get_channels, .set_channels = be_set_channels, .get_module_info = be_get_module_info, diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 7039870a4e43..00059afad81c 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -4212,6 +4212,9 @@ static int be_get_config(struct be_adapter *adapter) if (status) return status; + if (!lancer_chip(adapter) && be_physfn(adapter)) + be_cmd_get_fat_dump_len(adapter, &adapter->fat_dump_len); + if (BEx_chip(adapter)) { level = be_cmd_get_fw_log_level(adapter); adapter->msg_enable = -- cgit v1.2.3-59-g8ed1b From ab07ead53327ba574b84547a9c403fabab9e5a64 Mon Sep 17 00:00:00 2001 From: Suresh Reddy Date: Wed, 30 Dec 2015 01:29:05 -0500 Subject: be2net: bump up the driver version to 11.0.0.0 Signed-off-by: Suresh Reddy Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 8b8212fda632..22bf7afe5311 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -37,7 +37,7 @@ #include "be_hw.h" #include "be_roce.h" -#define DRV_VER "10.6.0.3" +#define DRV_VER "11.0.0.0" #define DRV_NAME "be2net" #define BE_NAME "Emulex BladeEngine2" #define BE3_NAME "Emulex BladeEngine3" -- cgit v1.2.3-59-g8ed1b From d2fa47d9dd5c5f82d78c4503cf33989f67e8aa64 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 30 Dec 2015 16:28:26 +0100 Subject: phy: marvell: Add ethtool statistics counters The PHY counters receiver errors and errors while idle. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/marvell.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) (limited to 'drivers') diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 0240552b50f3..50b5eac75854 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -137,6 +137,22 @@ MODULE_DESCRIPTION("Marvell PHY driver"); MODULE_AUTHOR("Andy Fleming"); MODULE_LICENSE("GPL"); +struct marvell_hw_stat { + const char *string; + u8 page; + u8 reg; + u8 bits; +}; + +static struct marvell_hw_stat marvell_hw_stats[] = { + { "phy_receive_errors", 0, 21, 16}, + { "phy_idle_errors", 0, 10, 8 }, +}; + +struct marvell_priv { + u64 stats[ARRAY_SIZE(marvell_hw_stats)]; +}; + static int marvell_ack_interrupt(struct phy_device *phydev) { int err; @@ -986,12 +1002,80 @@ static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *w return 0; } +static int marvell_get_sset_count(struct phy_device *phydev) +{ + return ARRAY_SIZE(marvell_hw_stats); +} + +static void marvell_get_strings(struct phy_device *phydev, u8 *data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) { + memcpy(data + i * ETH_GSTRING_LEN, + marvell_hw_stats[i].string, ETH_GSTRING_LEN); + } +} + +#ifndef UINT64_MAX +#define UINT64_MAX (u64)(~((u64)0)) +#endif +static u64 marvell_get_stat(struct phy_device *phydev, int i) +{ + struct marvell_hw_stat stat = marvell_hw_stats[i]; + struct marvell_priv *priv = phydev->priv; + int err, oldpage; + u64 val; + + oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); + err = phy_write(phydev, MII_MARVELL_PHY_PAGE, + stat.page); + if (err < 0) + return UINT64_MAX; + + val = phy_read(phydev, stat.reg); + if (val < 0) { + val = UINT64_MAX; + } else { + val = val & ((1 << stat.bits) - 1); + priv->stats[i] += val; + val = priv->stats[i]; + } + + phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); + + return val; +} + +static void marvell_get_stats(struct phy_device *phydev, + struct ethtool_stats *stats, u64 *data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) + data[i] = marvell_get_stat(phydev, i); +} + +static int marvell_probe(struct phy_device *phydev) +{ + struct marvell_priv *priv; + + priv = devm_kzalloc(&phydev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + + return 0; +} + static struct phy_driver marvell_drivers[] = { { .phy_id = MARVELL_PHY_ID_88E1101, .phy_id_mask = MARVELL_PHY_ID_MASK, .name = "Marvell 88E1101", .features = PHY_GBIT_FEATURES, + .probe = marvell_probe, .flags = PHY_HAS_INTERRUPT, .config_aneg = &marvell_config_aneg, .read_status = &genphy_read_status, @@ -999,6 +1083,9 @@ static struct phy_driver marvell_drivers[] = { .config_intr = &marvell_config_intr, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, { @@ -1007,6 +1094,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1112", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_init = &m88e1111_config_init, .config_aneg = &marvell_config_aneg, .read_status = &genphy_read_status, @@ -1014,6 +1102,9 @@ static struct phy_driver marvell_drivers[] = { .config_intr = &marvell_config_intr, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, { @@ -1022,6 +1113,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1111", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_init = &m88e1111_config_init, .config_aneg = &marvell_config_aneg, .read_status = &marvell_read_status, @@ -1029,6 +1121,9 @@ static struct phy_driver marvell_drivers[] = { .config_intr = &marvell_config_intr, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, { @@ -1037,6 +1132,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1118", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_init = &m88e1118_config_init, .config_aneg = &m88e1118_config_aneg, .read_status = &genphy_read_status, @@ -1044,6 +1140,9 @@ static struct phy_driver marvell_drivers[] = { .config_intr = &marvell_config_intr, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = {.owner = THIS_MODULE,}, }, { @@ -1052,6 +1151,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1121R", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_aneg = &m88e1121_config_aneg, .read_status = &marvell_read_status, .ack_interrupt = &marvell_ack_interrupt, @@ -1059,6 +1159,9 @@ static struct phy_driver marvell_drivers[] = { .did_interrupt = &m88e1121_did_interrupt, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, { @@ -1067,6 +1170,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1318S", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_aneg = &m88e1318_config_aneg, .read_status = &marvell_read_status, .ack_interrupt = &marvell_ack_interrupt, @@ -1076,6 +1180,9 @@ static struct phy_driver marvell_drivers[] = { .set_wol = &m88e1318_set_wol, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, { @@ -1084,6 +1191,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1145", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_init = &m88e1145_config_init, .config_aneg = &marvell_config_aneg, .read_status = &genphy_read_status, @@ -1091,6 +1199,9 @@ static struct phy_driver marvell_drivers[] = { .config_intr = &marvell_config_intr, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, { @@ -1099,6 +1210,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1149R", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_init = &m88e1149_config_init, .config_aneg = &m88e1118_config_aneg, .read_status = &genphy_read_status, @@ -1106,6 +1218,9 @@ static struct phy_driver marvell_drivers[] = { .config_intr = &marvell_config_intr, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, { @@ -1114,6 +1229,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1240", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_init = &m88e1111_config_init, .config_aneg = &marvell_config_aneg, .read_status = &genphy_read_status, @@ -1121,6 +1237,9 @@ static struct phy_driver marvell_drivers[] = { .config_intr = &marvell_config_intr, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, { @@ -1129,6 +1248,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1116R", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_init = &m88e1116r_config_init, .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, @@ -1136,6 +1256,9 @@ static struct phy_driver marvell_drivers[] = { .config_intr = &marvell_config_intr, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, { @@ -1144,6 +1267,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1510", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_aneg = &m88e1510_config_aneg, .read_status = &marvell_read_status, .ack_interrupt = &marvell_ack_interrupt, @@ -1151,6 +1275,9 @@ static struct phy_driver marvell_drivers[] = { .did_interrupt = &m88e1121_did_interrupt, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, { @@ -1159,6 +1286,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1540", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_aneg = &m88e1510_config_aneg, .read_status = &marvell_read_status, .ack_interrupt = &marvell_ack_interrupt, @@ -1166,6 +1294,9 @@ static struct phy_driver marvell_drivers[] = { .did_interrupt = &m88e1121_did_interrupt, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, { @@ -1174,6 +1305,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E3016", .features = PHY_BASIC_FEATURES, .flags = PHY_HAS_INTERRUPT, + .probe = marvell_probe, .config_aneg = &genphy_config_aneg, .config_init = &m88e3016_config_init, .aneg_done = &marvell_aneg_done, @@ -1183,6 +1315,9 @@ static struct phy_driver marvell_drivers[] = { .did_interrupt = &m88e1121_did_interrupt, .resume = &genphy_resume, .suspend = &genphy_suspend, + .get_sset_count = marvell_get_sset_count, + .get_strings = marvell_get_strings, + .get_stats = marvell_get_stats, .driver = { .owner = THIS_MODULE }, }, }; -- cgit v1.2.3-59-g8ed1b From 2b2427d06426a99bf5f57f28b6c2477e78577a5e Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 30 Dec 2015 16:28:27 +0100 Subject: phy: micrel: Add ethtool statistics counters The PHY counters receiver errors and errors while idle. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/micrel.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) (limited to 'drivers') diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index e13ad6cdcc22..1a6048a8c29d 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -73,6 +73,17 @@ #define PS_TO_REG 200 +struct kszphy_hw_stat { + const char *string; + u8 reg; + u8 bits; +}; + +static struct kszphy_hw_stat kszphy_hw_stats[] = { + { "phy_receive_errors", 21, 16}, + { "phy_idle_errors", 10, 8 }, +}; + struct kszphy_type { u32 led_mode_reg; u16 interrupt_level_mask; @@ -86,6 +97,7 @@ struct kszphy_priv { int led_mode; bool rmii_ref_clk_sel; bool rmii_ref_clk_sel_val; + u64 stats[ARRAY_SIZE(kszphy_hw_stats)]; }; static const struct kszphy_type ksz8021_type = { @@ -569,6 +581,51 @@ ksz9021_wr_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum, { } +static int kszphy_get_sset_count(struct phy_device *phydev) +{ + return ARRAY_SIZE(kszphy_hw_stats); +} + +static void kszphy_get_strings(struct phy_device *phydev, u8 *data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) { + memcpy(data + i * ETH_GSTRING_LEN, + kszphy_hw_stats[i].string, ETH_GSTRING_LEN); + } +} + +#ifndef UINT64_MAX +#define UINT64_MAX (u64)(~((u64)0)) +#endif +static u64 kszphy_get_stat(struct phy_device *phydev, int i) +{ + struct kszphy_hw_stat stat = kszphy_hw_stats[i]; + struct kszphy_priv *priv = phydev->priv; + u64 val; + + val = phy_read(phydev, stat.reg); + if (val < 0) { + val = UINT64_MAX; + } else { + val = val & ((1 << stat.bits) - 1); + priv->stats[i] += val; + val = priv->stats[i]; + } + + return val; +} + +static void kszphy_get_stats(struct phy_device *phydev, + struct ethtool_stats *stats, u64 *data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) + data[i] = kszphy_get_stat(phydev, i); +} + static int kszphy_probe(struct phy_device *phydev) { const struct kszphy_type *type = phydev->drv->driver_data; @@ -642,6 +699,9 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, @@ -659,6 +719,9 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, @@ -676,6 +739,9 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, @@ -693,6 +759,9 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, @@ -710,6 +779,9 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, @@ -727,6 +799,9 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, @@ -743,6 +818,9 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, @@ -759,6 +837,9 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, @@ -773,6 +854,9 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, @@ -788,6 +872,9 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .read_mmd_indirect = ksz9021_rd_mmd_phyreg, @@ -805,6 +892,9 @@ static struct phy_driver ksphy_driver[] = { .read_status = ksz9031_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE, }, @@ -817,6 +907,9 @@ static struct phy_driver ksphy_driver[] = { .config_init = kszphy_config_init, .config_aneg = ksz8873mll_config_aneg, .read_status = ksz8873mll_read_status, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE, }, @@ -829,6 +922,9 @@ static struct phy_driver ksphy_driver[] = { .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, + .get_sset_count = kszphy_get_sset_count, + .get_strings = kszphy_get_strings, + .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE, }, -- cgit v1.2.3-59-g8ed1b From 0510931ef5e89d67f1c87c792219733972069269 Mon Sep 17 00:00:00 2001 From: RafaÅ‚ MiÅ‚ecki Date: Thu, 17 Dec 2015 20:53:54 +0100 Subject: bcma: use module_init for the main part of bus initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far we were using fs_initcall. It was (and still is) needed because struct bus_type has to be registered early. However main bus initialization has to happen later as it requires SPROM which depends on NVRAM which depends on mtd. Solve it by using fs_initcall only for bus_register call and module_init for the rest. It affects bcma only when built-in obviously. This was tested with BCM4706 and BCM5357C0 (BCM47XX), BCM4708A0 (ARCH_BCM_5301X) and BCM43225 (PCIe card with bcma as module). Signed-off-by: RafaÅ‚ MiÅ‚ecki Signed-off-by: Kalle Valo --- drivers/bcma/main.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 59d8d0d14824..c466f752b067 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -668,11 +668,36 @@ static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env) core->id.rev, core->id.class); } -static int __init bcma_modinit(void) +static unsigned int bcma_bus_registered; + +/* + * If built-in, bus has to be registered early, before any driver calls + * bcma_driver_register. + * Otherwise registering driver would trigger BUG in driver_register. + */ +static int __init bcma_init_bus_register(void) { int err; + if (bcma_bus_registered) + return 0; + err = bus_register(&bcma_bus_type); + if (!err) + bcma_bus_registered = 1; + + return err; +} +#ifndef MODULE +fs_initcall(bcma_init_bus_register); +#endif + +/* Main initialization has to be done with SPI/mtd/NAND/SPROM available */ +static int __init bcma_modinit(void) +{ + int err; + + err = bcma_init_bus_register(); if (err) return err; @@ -691,7 +716,7 @@ static int __init bcma_modinit(void) return err; } -fs_initcall(bcma_modinit); +module_init(bcma_modinit); static void __exit bcma_modexit(void) { -- cgit v1.2.3-59-g8ed1b From 3719c17e1816695f415dd3b4ddcb679f7dc617c8 Mon Sep 17 00:00:00 2001 From: Shahar Patury Date: Tue, 22 Dec 2015 14:30:06 +0200 Subject: wlcore/wl18xx: fw logger over sdio Enable the FW Logger to work over the SDIO interface in addition to over UART interface. In the new design we use fw internal memory instead of packet ram that was used in older (wl12xx) design. This change reduces the impact on TP and stability. A new event was added to notify fw logger is ready for reading. Dynamic configuration to debugfs was added as well. Signed-off-by: Shahar Patury Signed-off-by: Guy Mishol Signed-off-by: Kalle Valo --- drivers/net/wireless/ti/wl18xx/event.c | 2 + drivers/net/wireless/ti/wl18xx/event.h | 1 + drivers/net/wireless/ti/wl18xx/main.c | 9 +-- drivers/net/wireless/ti/wlcore/cmd.h | 1 - drivers/net/wireless/ti/wlcore/debugfs.c | 60 ++++++++++++++++++++ drivers/net/wireless/ti/wlcore/event.c | 82 +++++++++++++++++++++++++++ drivers/net/wireless/ti/wlcore/event.h | 9 +++ drivers/net/wireless/ti/wlcore/io.c | 11 ++-- drivers/net/wireless/ti/wlcore/io.h | 4 +- drivers/net/wireless/ti/wlcore/main.c | 96 ++++---------------------------- drivers/net/wireless/ti/wlcore/rx.c | 1 - drivers/net/wireless/ti/wlcore/sysfs.c | 26 --------- drivers/net/wireless/ti/wlcore/wlcore.h | 3 - 13 files changed, 177 insertions(+), 128 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c index 09c7e098f460..719907a0a2c2 100644 --- a/drivers/net/wireless/ti/wl18xx/event.c +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -205,6 +205,8 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl) mbox->sc_ssid, mbox->sc_pwd_len, mbox->sc_pwd); + if (vector & FW_LOGGER_INDICATION) + wlcore_event_fw_logger(wl); return 0; } diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h index f3d4f13379cb..070de1274694 100644 --- a/drivers/net/wireless/ti/wl18xx/event.h +++ b/drivers/net/wireless/ti/wl18xx/event.h @@ -41,6 +41,7 @@ enum { SMART_CONFIG_SYNC_EVENT_ID = BIT(22), SMART_CONFIG_DECODE_EVENT_ID = BIT(23), TIME_SYNC_EVENT_ID = BIT(24), + FW_LOGGER_INDICATION = BIT(25), }; enum wl18xx_radar_types { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 7869c3c99e6d..1bf26cc7374e 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -472,7 +472,7 @@ static struct wlcore_conf wl18xx_conf = { }, .fwlog = { .mode = WL12XX_FWLOG_CONTINUOUS, - .mem_blocks = 2, + .mem_blocks = 0, .severity = 0, .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED, .output = WL12XX_FWLOG_OUTPUT_DBG_PINS, @@ -595,7 +595,7 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { .mem = { .start = 0x00A00000, .size = 0x00012000 }, .reg = { .start = 0x00807000, .size = 0x00005000 }, .mem2 = { .start = 0x00800000, .size = 0x0000B000 }, - .mem3 = { .start = 0x00000000, .size = 0x00000000 }, + .mem3 = { .start = 0x00401594, .size = 0x00001020 }, }, [PART_DOWN] = { .mem = { .start = 0x00000000, .size = 0x00014000 }, @@ -613,7 +613,7 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { .mem = { .start = 0x00800000, .size = 0x000050FC }, .reg = { .start = 0x00B00404, .size = 0x00001000 }, .mem2 = { .start = 0x00C00000, .size = 0x00000400 }, - .mem3 = { .start = 0x00000000, .size = 0x00000000 }, + .mem3 = { .start = 0x00401594, .size = 0x00001020 }, }, [PART_PHY_INIT] = { .mem = { .start = WL18XX_PHY_INIT_MEM_ADDR, @@ -1040,7 +1040,8 @@ static int wl18xx_boot(struct wl1271 *wl) DFS_CHANNELS_CONFIG_COMPLETE_EVENT | SMART_CONFIG_SYNC_EVENT_ID | SMART_CONFIG_DECODE_EVENT_ID | - TIME_SYNC_EVENT_ID; + TIME_SYNC_EVENT_ID | + FW_LOGGER_INDICATION; wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID; diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 8dc46c0a489a..e28e2f2303ce 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -626,7 +626,6 @@ struct wl12xx_cmd_remove_peer { */ enum wl12xx_fwlogger_log_mode { WL12XX_FWLOG_CONTINUOUS, - WL12XX_FWLOG_ON_DEMAND }; /* Include/exclude timestamps from the log messages */ diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index eb43f94a1597..8367f9edfb11 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -1234,6 +1234,65 @@ static const struct file_operations dev_mem_ops = { .llseek = dev_mem_seek, }; +static ssize_t fw_logger_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + + return wl1271_format_buffer(user_buf, count, + ppos, "%d\n", + wl->conf.fwlog.output); +} + +static ssize_t fw_logger_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + unsigned long value; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 0, &value); + if (ret < 0) { + wl1271_warning("illegal value in fw_logger"); + return -EINVAL; + } + + if ((value > 2) || (value == 0)) { + wl1271_warning("fw_logger value must be 1-UART 2-SDIO"); + return -ERANGE; + } + + if (wl->conf.fwlog.output == 0) { + wl1271_warning("iligal opperation - fw logger disabled by default, please change mode via wlconf"); + return -EINVAL; + } + + mutex_lock(&wl->mutex); + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) { + count = ret; + goto out; + } + + wl->conf.fwlog.output = value; + + ret = wl12xx_cmd_config_fwlog(wl); + + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations fw_logger_ops = { + .open = simple_open, + .read = fw_logger_read, + .write = fw_logger_write, + .llseek = default_llseek, +}; + static int wl1271_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { @@ -1260,6 +1319,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(irq_timeout, rootdir); DEBUGFS_ADD(fw_stats_raw, rootdir); DEBUGFS_ADD(sleep_auth, rootdir); + DEBUGFS_ADD(fw_logger, rootdir); streaming = debugfs_create_dir("rx_streaming", rootdir); if (!streaming || IS_ERR(streaming)) diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index c42e78955e7b..c96405498bf4 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -28,6 +28,88 @@ #include "ps.h" #include "scan.h" #include "wl12xx_80211.h" +#include "hw_ops.h" + +#define WL18XX_LOGGER_SDIO_BUFF_MAX (0x1020) +#define WL18XX_DATA_RAM_BASE_ADDRESS (0x20000000) +#define WL18XX_LOGGER_SDIO_BUFF_ADDR (0x40159c) +#define WL18XX_LOGGER_BUFF_OFFSET (sizeof(struct fw_logger_information)) +#define WL18XX_LOGGER_READ_POINT_OFFSET (12) + +int wlcore_event_fw_logger(struct wl1271 *wl) +{ + u32 ret; + struct fw_logger_information fw_log; + u8 *buffer; + u32 internal_fw_addrbase = WL18XX_DATA_RAM_BASE_ADDRESS; + u32 addr = WL18XX_LOGGER_SDIO_BUFF_ADDR; + u32 end_buff_addr = WL18XX_LOGGER_SDIO_BUFF_ADDR + + WL18XX_LOGGER_BUFF_OFFSET; + u32 available_len; + u32 actual_len; + u32 clear_addr; + size_t len; + u32 start_loc; + + buffer = kzalloc(WL18XX_LOGGER_SDIO_BUFF_MAX, GFP_KERNEL); + if (!buffer) { + wl1271_error("Fail to allocate fw logger memory"); + fw_log.actual_buff_size = cpu_to_le32(0); + goto out; + } + + ret = wlcore_read(wl, addr, buffer, WL18XX_LOGGER_SDIO_BUFF_MAX, + false); + if (ret < 0) { + wl1271_error("Fail to read logger buffer, error_id = %d", + ret); + fw_log.actual_buff_size = cpu_to_le32(0); + goto free_out; + } + + memcpy(&fw_log, buffer, sizeof(fw_log)); + + if (le32_to_cpu(fw_log.actual_buff_size) == 0) + goto free_out; + + actual_len = le32_to_cpu(fw_log.actual_buff_size); + start_loc = (le32_to_cpu(fw_log.buff_read_ptr) - + internal_fw_addrbase) - addr; + end_buff_addr += le32_to_cpu(fw_log.max_buff_size); + available_len = end_buff_addr - + (le32_to_cpu(fw_log.buff_read_ptr) - + internal_fw_addrbase); + actual_len = min(actual_len, available_len); + len = actual_len; + + wl12xx_copy_fwlog(wl, &buffer[start_loc], len); + clear_addr = addr + start_loc + le32_to_cpu(fw_log.actual_buff_size) + + internal_fw_addrbase; + + len = le32_to_cpu(fw_log.actual_buff_size) - len; + if (len) { + wl12xx_copy_fwlog(wl, + &buffer[WL18XX_LOGGER_BUFF_OFFSET], + len); + clear_addr = addr + WL18XX_LOGGER_BUFF_OFFSET + len + + internal_fw_addrbase; + } + + /* double check that clear address and write pointer are the same */ + if (clear_addr != le32_to_cpu(fw_log.buff_write_ptr)) { + wl1271_error("Calculate of clear addr Clear = %x, write = %x", + clear_addr, le32_to_cpu(fw_log.buff_write_ptr)); + } + + /* indicate FW about Clear buffer */ + ret = wlcore_write32(wl, addr + WL18XX_LOGGER_READ_POINT_OFFSET, + fw_log.buff_write_ptr); +free_out: + kfree(buffer); +out: + return le32_to_cpu(fw_log.actual_buff_size); +} +EXPORT_SYMBOL_GPL(wlcore_event_fw_logger); void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr) { diff --git a/drivers/net/wireless/ti/wlcore/event.h b/drivers/net/wireless/ti/wlcore/event.h index acc7a59d3828..75e8e98da2fe 100644 --- a/drivers/net/wireless/ti/wlcore/event.h +++ b/drivers/net/wireless/ti/wlcore/event.h @@ -64,6 +64,14 @@ enum { #define NUM_OF_RSSI_SNR_TRIGGERS 8 +struct fw_logger_information { + __le32 max_buff_size; + __le32 actual_buff_size; + __le32 num_trace_drop; + __le32 buff_read_ptr; + __le32 buff_write_ptr; +} __packed; + struct wl1271; int wl1271_event_unmask(struct wl1271 *wl); @@ -84,4 +92,5 @@ void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap); void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap); void wlcore_event_roc_complete(struct wl1271 *wl); void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr); +int wlcore_event_fw_logger(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/ti/wlcore/io.c b/drivers/net/wireless/ti/wlcore/io.c index 68e74eefd296..9ac118e727e9 100644 --- a/drivers/net/wireless/ti/wlcore/io.c +++ b/drivers/net/wireless/ti/wlcore/io.c @@ -175,12 +175,13 @@ int wlcore_set_partition(struct wl1271 *wl, if (ret < 0) goto out; - /* - * We don't need the size of the last partition, as it is - * automatically calculated based on the total memory size and - * the sizes of the previous partitions. - */ ret = wlcore_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); + if (ret < 0) + goto out; + + ret = wlcore_raw_write32(wl, HW_PART3_SIZE_ADDR, p->mem3.size); + if (ret < 0) + goto out; out: return ret; diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index 0305729d0986..020c7b8f640c 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -36,8 +36,8 @@ #define HW_PART1_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 12) #define HW_PART2_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 16) #define HW_PART2_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 20) -#define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 24) - +#define HW_PART3_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 24) +#define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 28) #define HW_ACCESS_REGISTER_SIZE 4 #define HW_ACCESS_PRAM_MAX_RANGE 0x3c000 diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ec7f6af3fab2..d1109c4f0f0d 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1,4 +1,3 @@ - /* * This file is part of wlcore * @@ -303,25 +302,11 @@ out: static void wlcore_adjust_conf(struct wl1271 *wl) { - /* Adjust settings according to optional module parameters */ - - /* Firmware Logger params */ - if (fwlog_mem_blocks != -1) { - if (fwlog_mem_blocks >= CONF_FWLOG_MIN_MEM_BLOCKS && - fwlog_mem_blocks <= CONF_FWLOG_MAX_MEM_BLOCKS) { - wl->conf.fwlog.mem_blocks = fwlog_mem_blocks; - } else { - wl1271_error( - "Illegal fwlog_mem_blocks=%d using default %d", - fwlog_mem_blocks, wl->conf.fwlog.mem_blocks); - } - } if (fwlog_param) { if (!strcmp(fwlog_param, "continuous")) { wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS; - } else if (!strcmp(fwlog_param, "ondemand")) { - wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND; + wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_HOST; } else if (!strcmp(fwlog_param, "dbgpins")) { wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS; wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS; @@ -825,91 +810,32 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen) static void wl12xx_read_fwlog_panic(struct wl1271 *wl) { - struct wlcore_partition_set part, old_part; - u32 addr; - u32 offset; - u32 end_of_log; - u8 *block; - int ret; + u32 end_of_log = 0; - if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) || - (wl->conf.fwlog.mem_blocks == 0)) + if (wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) return; wl1271_info("Reading FW panic log"); - block = kmalloc(wl->fw_mem_block_size, GFP_KERNEL); - if (!block) - return; - /* * Make sure the chip is awake and the logger isn't active. * Do not send a stop fwlog command if the fw is hanged or if * dbgpins are used (due to some fw bug). */ if (wl1271_ps_elp_wakeup(wl)) - goto out; + return; if (!wl->watchdog_recovery && wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS) wl12xx_cmd_stop_fwlog(wl); - /* Read the first memory block address */ - ret = wlcore_fw_status(wl, wl->fw_status); - if (ret < 0) - goto out; - - addr = wl->fw_status->log_start_addr; - if (!addr) - goto out; - - if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) { - offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor); - end_of_log = wl->fwlog_end; - } else { - offset = sizeof(addr); - end_of_log = addr; - } - - old_part = wl->curr_part; - memset(&part, 0, sizeof(part)); - /* Traverse the memory blocks linked list */ do { - part.mem.start = wlcore_hw_convert_hwaddr(wl, addr); - part.mem.size = PAGE_SIZE; - - ret = wlcore_set_partition(wl, &part); - if (ret < 0) { - wl1271_error("%s: set_partition start=0x%X size=%d", - __func__, part.mem.start, part.mem.size); - goto out; + end_of_log = wlcore_event_fw_logger(wl); + if (end_of_log == 0) { + msleep(100); + end_of_log = wlcore_event_fw_logger(wl); } - - memset(block, 0, wl->fw_mem_block_size); - ret = wlcore_read_hwaddr(wl, addr, block, - wl->fw_mem_block_size, false); - - if (ret < 0) - goto out; - - /* - * Memory blocks are linked to one another. The first 4 bytes - * of each memory block hold the hardware address of the next - * one. The last memory block points to the first one in - * on demand mode and is equal to 0x2000000 in continuous mode. - */ - addr = le32_to_cpup((__le32 *)block); - - if (!wl12xx_copy_fwlog(wl, block + offset, - wl->fw_mem_block_size - offset)) - break; - } while (addr && (addr != end_of_log)); - - wake_up_interruptible(&wl->fwlog_waitq); - -out: - kfree(block); - wlcore_set_partition(wl, &old_part); + } while (end_of_log != 0); } static void wlcore_save_freed_pkts(struct wl1271 *wl, struct wl12xx_vif *wlvif, @@ -6291,7 +6217,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, wl->active_sta_count = 0; wl->active_link_count = 0; wl->fwlog_size = 0; - init_waitqueue_head(&wl->fwlog_waitq); /* The system link is always allocated */ __set_bit(WL12XX_SYSTEM_HLID, wl->links_map); @@ -6377,7 +6302,6 @@ int wlcore_free_hw(struct wl1271 *wl) /* Unblock any fwlog readers */ mutex_lock(&wl->mutex); wl->fwlog_size = -1; - wake_up_interruptible_all(&wl->fwlog_waitq); mutex_unlock(&wl->mutex); wlcore_sysfs_free(wl); @@ -6584,7 +6508,7 @@ MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); module_param_named(fwlog, fwlog_param, charp, 0); MODULE_PARM_DESC(fwlog, - "FW logger options: continuous, ondemand, dbgpins or disable"); + "FW logger options: continuous, dbgpins or disable"); module_param(fwlog_mem_blocks, int, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks"); diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index 5b2927391d1c..34e7e938ede4 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -149,7 +149,6 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, if (desc->packet_class == WL12XX_RX_CLASS_LOGGER) { size_t len = length - sizeof(*desc); wl12xx_copy_fwlog(wl, data + sizeof(*desc), len); - wake_up_interruptible(&wl->fwlog_waitq); return 0; } diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c index 24dd288d6809..a9218e5b0efc 100644 --- a/drivers/net/wireless/ti/wlcore/sysfs.c +++ b/drivers/net/wireless/ti/wlcore/sysfs.c @@ -119,32 +119,6 @@ static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, if (ret < 0) return -ERESTARTSYS; - /* Let only one thread read the log at a time, blocking others */ - while (wl->fwlog_size == 0) { - DEFINE_WAIT(wait); - - prepare_to_wait_exclusive(&wl->fwlog_waitq, - &wait, - TASK_INTERRUPTIBLE); - - if (wl->fwlog_size != 0) { - finish_wait(&wl->fwlog_waitq, &wait); - break; - } - - mutex_unlock(&wl->mutex); - - schedule(); - finish_wait(&wl->fwlog_waitq, &wait); - - if (signal_pending(current)) - return -ERESTARTSYS; - - ret = mutex_lock_interruptible(&wl->mutex); - if (ret < 0) - return -ERESTARTSYS; - } - /* Check if the fwlog is still valid */ if (wl->fwlog_size < 0) { mutex_unlock(&wl->mutex); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 906be6aa4eb6..dda01b118c26 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -310,9 +310,6 @@ struct wl1271 { /* FW memory block size */ u32 fw_mem_block_size; - /* Sysfs FW log entry readers wait queue */ - wait_queue_head_t fwlog_waitq; - /* Hardware recovery work */ struct work_struct recovery_work; bool watchdog_recovery; -- cgit v1.2.3-59-g8ed1b From e47301b06d5a65678690f04c2248fd181db1e59a Mon Sep 17 00:00:00 2001 From: Uri Mashiach Date: Thu, 24 Dec 2015 16:05:00 +0200 Subject: wlcore/wl12xx: spi: fix NULL pointer dereference (Oops) Fix the below Oops when trying to modprobe wlcore_spi. The oops occurs because the wl1271_power_{off,on}() function doesn't check the power() function pointer. [ 23.401447] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 23.409954] pgd = c0004000 [ 23.412922] [00000000] *pgd=00000000 [ 23.416693] Internal error: Oops: 80000007 [#1] SMP ARM [ 23.422168] Modules linked in: wl12xx wlcore mac80211 cfg80211 musb_dsps musb_hdrc usbcore usb_common snd_soc_simple_card evdev joydev omap_rng wlcore_spi snd_soc_tlv320aic23_i2c rng_core snd_soc_tlv320aic23 c_can_platform c_can can_dev snd_soc_davinci_mcasp snd_soc_edma snd_soc_omap omap_wdt musb_am335x cpufreq_dt thermal_sys hwmon [ 23.453253] CPU: 0 PID: 36 Comm: kworker/0:2 Not tainted 4.2.0-00002-g951efee-dirty #233 [ 23.461720] Hardware name: Generic AM33XX (Flattened Device Tree) [ 23.468123] Workqueue: events request_firmware_work_func [ 23.473690] task: de32efc0 ti: de4ee000 task.ti: de4ee000 [ 23.479341] PC is at 0x0 [ 23.482112] LR is at wl12xx_set_power_on+0x28/0x124 [wlcore] [ 23.488074] pc : [<00000000>] lr : [] psr: 60000013 [ 23.488074] sp : de4efe50 ip : 00000002 fp : 00000000 [ 23.500162] r10: de7cdd00 r9 : dc848800 r8 : bf27af00 [ 23.505663] r7 : bf27a1a8 r6 : dcbd8a80 r5 : dce0e2e0 r4 : dce0d2e0 [ 23.512536] r3 : 00000000 r2 : 00000000 r1 : 00000001 r0 : dc848810 [ 23.519412] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel [ 23.527109] Control: 10c5387d Table: 9cb78019 DAC: 00000015 [ 23.533160] Process kworker/0:2 (pid: 36, stack limit = 0xde4ee218) [ 23.539760] Stack: (0xde4efe50 to 0xde4f0000) [...] [ 23.665030] [] (wl12xx_set_power_on [wlcore]) from [] (wlcore_nvs_cb+0x118/0xa4c [wlcore]) [ 23.675604] [] (wlcore_nvs_cb [wlcore]) from [] (request_firmware_work_func+0x30/0x58) [ 23.685784] [] (request_firmware_work_func) from [] (process_one_work+0x1b4/0x4b4) [ 23.695591] [] (process_one_work) from [] (worker_thread+0x3c/0x4a4) [ 23.704124] [] (worker_thread) from [] (kthread+0xd4/0xf0) [ 23.711747] [] (kthread) from [] (ret_from_fork+0x14/0x3c) [ 23.719357] Code: bad PC value [ 23.722760] ---[ end trace 981be8510db9b3a9 ]--- Prevent oops by validationg power() pointer value before calling the function. Signed-off-by: Uri Mashiach Cc: stable@vger.kernel.org Acked-by: Igor Grinberg Signed-off-by: Kalle Valo --- drivers/net/wireless/ti/wlcore/io.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index 020c7b8f640c..6c257b54f415 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -207,19 +207,23 @@ static inline int __must_check wlcore_write_reg(struct wl1271 *wl, int reg, static inline void wl1271_power_off(struct wl1271 *wl) { - int ret; + int ret = 0; if (!test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags)) return; - ret = wl->if_ops->power(wl->dev, false); + if (wl->if_ops->power) + ret = wl->if_ops->power(wl->dev, false); if (!ret) clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); } static inline int wl1271_power_on(struct wl1271 *wl) { - int ret = wl->if_ops->power(wl->dev, true); + int ret = 0; + + if (wl->if_ops->power) + ret = wl->if_ops->power(wl->dev, true); if (ret == 0) set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); -- cgit v1.2.3-59-g8ed1b From 49f2a47d913184b97ef84b2e4fd438dbee341487 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 26 Dec 2015 01:42:41 +0300 Subject: prism54: fix checks for dma mapping errors prism54 checks for dma mapping errors by comparison returned address with zero, while pci_dma_mapping_error() should be used. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/prism54/islpci_dev.c | 4 +++- drivers/net/wireless/intersil/prism54/islpci_eth.c | 5 +++-- drivers/net/wireless/intersil/prism54/islpci_mgt.c | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intersil/prism54/islpci_dev.c b/drivers/net/wireless/intersil/prism54/islpci_dev.c index 931cf440ff18..84a42012aeae 100644 --- a/drivers/net/wireless/intersil/prism54/islpci_dev.c +++ b/drivers/net/wireless/intersil/prism54/islpci_dev.c @@ -707,7 +707,9 @@ islpci_alloc_memory(islpci_private *priv) pci_map_single(priv->pdev, (void *) skb->data, MAX_FRAGMENT_SIZE_RX + 2, PCI_DMA_FROMDEVICE); - if (!priv->pci_map_rx_address[counter]) { + if (pci_dma_mapping_error(priv->pdev, + priv->pci_map_rx_address[counter])) { + priv->pci_map_rx_address[counter] = 0; /* error mapping the buffer to device accessible memory address */ printk(KERN_ERR "failed to map skb DMA'able\n"); diff --git a/drivers/net/wireless/intersil/prism54/islpci_eth.c b/drivers/net/wireless/intersil/prism54/islpci_eth.c index 674658f2e6ef..d83f6332019e 100644 --- a/drivers/net/wireless/intersil/prism54/islpci_eth.c +++ b/drivers/net/wireless/intersil/prism54/islpci_eth.c @@ -190,7 +190,7 @@ islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev) pci_map_address = pci_map_single(priv->pdev, (void *) skb->data, skb->len, PCI_DMA_TODEVICE); - if (unlikely(pci_map_address == 0)) { + if (pci_dma_mapping_error(priv->pdev, pci_map_address)) { printk(KERN_WARNING "%s: cannot map buffer to PCI\n", ndev->name); goto drop_free; @@ -448,7 +448,8 @@ islpci_eth_receive(islpci_private *priv) pci_map_single(priv->pdev, (void *) skb->data, MAX_FRAGMENT_SIZE_RX + 2, PCI_DMA_FROMDEVICE); - if (unlikely(!priv->pci_map_rx_address[index])) { + if (pci_dma_mapping_error(priv->pdev, + priv->pci_map_rx_address[index])) { /* error mapping the buffer to device accessible memory address */ DEBUG(SHOW_ERROR_MESSAGES, "Error mapping DMA address\n"); diff --git a/drivers/net/wireless/intersil/prism54/islpci_mgt.c b/drivers/net/wireless/intersil/prism54/islpci_mgt.c index 0de14dfa68cc..53d7a1705e8e 100644 --- a/drivers/net/wireless/intersil/prism54/islpci_mgt.c +++ b/drivers/net/wireless/intersil/prism54/islpci_mgt.c @@ -130,7 +130,7 @@ islpci_mgmt_rx_fill(struct net_device *ndev) buf->pci_addr = pci_map_single(priv->pdev, buf->mem, MGMT_FRAME_SIZE, PCI_DMA_FROMDEVICE); - if (!buf->pci_addr) { + if (pci_dma_mapping_error(priv->pdev, buf->pci_addr)) { printk(KERN_WARNING "Failed to make memory DMA'able.\n"); return -ENOMEM; @@ -217,7 +217,7 @@ islpci_mgt_transmit(struct net_device *ndev, int operation, unsigned long oid, err = -ENOMEM; buf.pci_addr = pci_map_single(priv->pdev, buf.mem, frag_len, PCI_DMA_TODEVICE); - if (!buf.pci_addr) { + if (pci_dma_mapping_error(priv->pdev, buf.pci_addr)) { printk(KERN_WARNING "%s: cannot map PCI memory for mgmt\n", ndev->name); goto error_free; -- cgit v1.2.3-59-g8ed1b From aa538aca9fb4a197464f5926c7b5d7050e9144b1 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 16 Dec 2015 16:52:19 +0200 Subject: ath10k: wake up device before accessing registers commit 1aaf8efba0ae ("ath10k: disable PCI PS for QCA988X and QCA99X0") partially reverts pci soc powersave support added by commit 77258d409ce4 ("ath10k: enable pci soc powersaving"). While reverting the change, pci wake up function is called after accessing pci registers instead of prior to access. The assumption is that chip is woken up before accessing its registers.Though this change does not fix any known issues, this might help to avoid unknown or low power platform specific issues. Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 14fd73ec1c96..d77ba4c09e78 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -3071,9 +3071,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_sleep; } - ath10k_pci_ce_deinit(ar); - ath10k_pci_irq_disable(ar); - if (ar_pci->pci_ps == 0) { ret = ath10k_pci_force_wake(ar); if (ret) { @@ -3082,6 +3079,9 @@ static int ath10k_pci_probe(struct pci_dev *pdev, } } + ath10k_pci_ce_deinit(ar); + ath10k_pci_irq_disable(ar); + ret = ath10k_pci_init_irq(ar); if (ret) { ath10k_err(ar, "failed to init irqs: %d\n", ret); -- cgit v1.2.3-59-g8ed1b From d9d6a5ae2171576aa8f4a9669d1d02e99d830b77 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 16 Dec 2015 16:52:19 +0200 Subject: ath10k: reduce indentation by moving powersave check within function For devices that does not support PCI power save, force wake up API is used. So move powersave check inside force wakeup to reduce one level indentation. Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index d77ba4c09e78..ee925c618535 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -487,6 +487,9 @@ static int ath10k_pci_force_wake(struct ath10k *ar) unsigned long flags; int ret = 0; + if (ar_pci->pci_ps) + return ret; + spin_lock_irqsave(&ar_pci->ps_lock, flags); if (!ar_pci->ps_awake) { @@ -2480,12 +2483,10 @@ static int ath10k_pci_hif_resume(struct ath10k *ar) u32 val; int ret = 0; - if (ar_pci->pci_ps == 0) { - ret = ath10k_pci_force_wake(ar); - if (ret) { - ath10k_err(ar, "failed to wake up target: %d\n", ret); - return ret; - } + ret = ath10k_pci_force_wake(ar); + if (ret) { + ath10k_err(ar, "failed to wake up target: %d\n", ret); + return ret; } /* Suspend/Resume resets the PCI configuration space, so we have to @@ -2592,13 +2593,10 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; - if (ar_pci->pci_ps == 0) { - ret = ath10k_pci_force_wake(ar); - if (ret) { - ath10k_warn(ar, "failed to wake device up on irq: %d\n", - ret); - return IRQ_NONE; - } + ret = ath10k_pci_force_wake(ar); + if (ret) { + ath10k_warn(ar, "failed to wake device up on irq: %d\n", ret); + return IRQ_NONE; } if (ar_pci->num_msi_intrs == 0) { @@ -3071,12 +3069,10 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_sleep; } - if (ar_pci->pci_ps == 0) { - ret = ath10k_pci_force_wake(ar); - if (ret) { - ath10k_warn(ar, "failed to wake up device : %d\n", ret); - goto err_free_pipes; - } + ret = ath10k_pci_force_wake(ar); + if (ret) { + ath10k_warn(ar, "failed to wake up device : %d\n", ret); + goto err_free_pipes; } ath10k_pci_ce_deinit(ar); -- cgit v1.2.3-59-g8ed1b From 5c352bf0880655e3a782f55a86fb172542c19a00 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Mon, 14 Dec 2015 11:06:01 +0100 Subject: ath9k_htc: fix handling return value of ath9k_hw_calibrate The function can return negative values in case of error. Its result should be then tested for such case. The problem has been detected using proposed semantic patch scripts/coccinelle/tests/assign_signed_to_unsigned.cocci [1]. [1]: http://permalink.gmane.org/gmane.linux.kernel/2046107 Signed-off-by: Andrzej Hajda Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index a680a970b7f7..fe1fd1a5ae15 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -834,7 +834,7 @@ void ath9k_htc_ani_work(struct work_struct *work) if (longcal || shortcal) common->ani.caldone = ath9k_hw_calibrate(ah, ah->curchan, - ah->rxchainmask, longcal); + ah->rxchainmask, longcal) > 0; ath9k_htc_ps_restore(priv); } -- cgit v1.2.3-59-g8ed1b From f94c48d6c315a580b60b062c0360561b61faa311 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 16 Dec 2015 14:10:00 +0300 Subject: wil6210: fix a warning message condition "iter" is -1 at the end of the loop and not zero. It means we don't print a warning message. Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 09b4daebab9d..b39f0bfc591e 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -987,7 +987,7 @@ int __wil_down(struct wil6210_priv *wil) } mutex_lock(&wil->mutex); - if (!iter) + if (iter < 0) wil_err(wil, "timeout waiting for idle FW/HW\n"); wil_reset(wil, false); -- cgit v1.2.3-59-g8ed1b From 59869ebfe7a73771399b897e592a8618fa0e43d7 Mon Sep 17 00:00:00 2001 From: Brad Campbell Date: Wed, 23 Dec 2015 21:19:41 -0500 Subject: ieee802154: cc2520: Check CRC & add promiscuous This patch adds checking the "CRC_OK" bit at the end of packets coming from the CC2520 radio. It also adds support for putting the radio in promiscuous mode (in which packets are not dropped if the CRC fails). In promiscuous mode the AUTOCRC flag is cleared so that the driver can pass the received CRC to the monitors. The radio now defaults to frame filtering (checking that the destination and PANID in the incoming packet matches the local node). This matches the other 15.4 radios and is what a user would expect to be the default. Other changes: 1. Adds LQI calculation 2. Makes #defines for relevant bit fields in CC2520 registers Signed-off-by: Brad Campbell Acked-by: Varka Bhadram Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/cc2520.c | 145 +++++++++++++++++++++++++++++++++------- 1 file changed, 122 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index e65b60591317..d50add705a79 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -189,6 +191,18 @@ #define CC2520_RXFIFOCNT 0x3E #define CC2520_TXFIFOCNT 0x3F +/* CC2520_FRMFILT0 */ +#define FRMFILT0_FRAME_FILTER_EN BIT(0) +#define FRMFILT0_PAN_COORDINATOR BIT(1) + +/* CC2520_FRMCTRL0 */ +#define FRMCTRL0_AUTOACK BIT(5) +#define FRMCTRL0_AUTOCRC BIT(6) + +/* CC2520_FRMCTRL1 */ +#define FRMCTRL1_SET_RXENMASK_ON_TX BIT(0) +#define FRMCTRL1_IGNORE_TX_UNDERF BIT(1) + /* Driver private information */ struct cc2520_private { struct spi_device *spi; /* SPI device structure */ @@ -201,6 +215,7 @@ struct cc2520_private { struct work_struct fifop_irqwork;/* Workqueue for FIFOP */ spinlock_t lock; /* Lock for is_tx*/ struct completion tx_complete; /* Work completion for Tx */ + bool promiscuous; /* Flag for promiscuous mode */ }; /* Generic Functions */ @@ -367,14 +382,14 @@ cc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data) } static int -cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len) +cc2520_write_txfifo(struct cc2520_private *priv, u8 pkt_len, u8 *data, u8 len) { int status; /* length byte must include FCS even * if it is calculated in the hardware */ - int len_byte = len + 2; + int len_byte = pkt_len; struct spi_message msg; @@ -414,7 +429,7 @@ cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len) } static int -cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len, u8 *lqi) +cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len) { int status; struct spi_message msg; @@ -470,12 +485,25 @@ cc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb) unsigned long flags; int rc; u8 status = 0; + u8 pkt_len; + + /* In promiscuous mode we disable AUTOCRC so we can get the raw CRC + * values on RX. This means we need to manually add the CRC on TX. + */ + if (priv->promiscuous) { + u16 crc = crc_ccitt(0, skb->data, skb->len); + + put_unaligned_le16(crc, skb_put(skb, 2)); + pkt_len = skb->len; + } else { + pkt_len = skb->len + 2; + } rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); if (rc) goto err_tx; - rc = cc2520_write_txfifo(priv, skb->data, skb->len); + rc = cc2520_write_txfifo(priv, pkt_len, skb->data, skb->len); if (rc) goto err_tx; @@ -518,22 +546,62 @@ static int cc2520_rx(struct cc2520_private *priv) u8 len = 0, lqi = 0, bytes = 1; struct sk_buff *skb; - cc2520_read_rxfifo(priv, &len, bytes, &lqi); + /* Read single length byte from the radio. */ + cc2520_read_rxfifo(priv, &len, bytes); - if (len < 2 || len > IEEE802154_MTU) - return -EINVAL; + if (!ieee802154_is_valid_psdu_len(len)) { + /* Corrupted frame received, clear frame buffer by + * reading entire buffer. + */ + dev_dbg(&priv->spi->dev, "corrupted frame received\n"); + len = IEEE802154_MTU; + } skb = dev_alloc_skb(len); if (!skb) return -ENOMEM; - if (cc2520_read_rxfifo(priv, skb_put(skb, len), len, &lqi)) { + if (cc2520_read_rxfifo(priv, skb_put(skb, len), len)) { dev_dbg(&priv->spi->dev, "frame reception failed\n"); kfree_skb(skb); return -EINVAL; } - skb_trim(skb, skb->len - 2); + /* In promiscuous mode, we configure the radio to include the + * CRC (AUTOCRC==0) and we pass on the packet unconditionally. If not + * in promiscuous mode, we check the CRC here, but leave the + * RSSI/LQI/CRC_OK bytes as they will get removed in the mac layer. + */ + if (!priv->promiscuous) { + bool crc_ok; + + /* Check if the CRC is valid. With AUTOCRC set, the most + * significant bit of the last byte returned from the CC2520 + * is CRC_OK flag. See section 20.3.4 of the datasheet. + */ + crc_ok = skb->data[len - 1] & BIT(7); + + /* If we failed CRC drop the packet in the driver layer. */ + if (!crc_ok) { + dev_dbg(&priv->spi->dev, "CRC check failed\n"); + kfree_skb(skb); + return -EINVAL; + } + + /* To calculate LQI, the lower 7 bits of the last byte (the + * correlation value provided by the radio) must be scaled to + * the range 0-255. According to section 20.6, the correlation + * value ranges from 50-110. Ideally this would be calibrated + * per hardware design, but we use roughly the datasheet values + * to get close enough while avoiding floating point. + */ + lqi = skb->data[len - 1] & 0x7f; + if (lqi < 50) + lqi = 50; + else if (lqi > 113) + lqi = 113; + lqi = (lqi - 50) * 4; + } ieee802154_rx_irqsafe(priv->hw, skb, lqi); @@ -619,14 +687,19 @@ cc2520_filter(struct ieee802154_hw *hw, } if (changed & IEEE802154_AFILT_PANC_CHANGED) { + u8 frmfilt0; + dev_vdbg(&priv->spi->dev, "cc2520_filter called for panc change\n"); + + cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0); + if (filt->pan_coord) - ret = cc2520_write_register(priv, CC2520_FRMFILT0, - 0x02); + frmfilt0 |= FRMFILT0_PAN_COORDINATOR; else - ret = cc2520_write_register(priv, CC2520_FRMFILT0, - 0x00); + frmfilt0 &= ~FRMFILT0_PAN_COORDINATOR; + + ret = cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0); } return ret; @@ -723,6 +796,30 @@ cc2520_set_txpower(struct ieee802154_hw *hw, s32 mbm) return cc2520_cc2591_set_tx_power(priv, mbm); } +static int +cc2520_set_promiscuous_mode(struct ieee802154_hw *hw, bool on) +{ + struct cc2520_private *priv = hw->priv; + u8 frmfilt0; + + dev_dbg(&priv->spi->dev, "%s : mode %d\n", __func__, on); + + priv->promiscuous = on; + + cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0); + + if (on) { + /* Disable automatic ACK, automatic CRC, and frame filtering. */ + cc2520_write_register(priv, CC2520_FRMCTRL0, 0); + frmfilt0 &= ~FRMFILT0_FRAME_FILTER_EN; + } else { + cc2520_write_register(priv, CC2520_FRMCTRL0, FRMCTRL0_AUTOACK | + FRMCTRL0_AUTOCRC); + frmfilt0 |= FRMFILT0_FRAME_FILTER_EN; + } + return cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0); +} + static const struct ieee802154_ops cc2520_ops = { .owner = THIS_MODULE, .start = cc2520_start, @@ -732,6 +829,7 @@ static const struct ieee802154_ops cc2520_ops = { .set_channel = cc2520_set_channel, .set_hw_addr_filt = cc2520_filter, .set_txpower = cc2520_set_txpower, + .set_promiscuous_mode = cc2520_set_promiscuous_mode, }; static int cc2520_register(struct cc2520_private *priv) @@ -749,7 +847,8 @@ static int cc2520_register(struct cc2520_private *priv) /* We do support only 2.4 Ghz */ priv->hw->phy->supported.channels[0] = 0x7FFF800; - priv->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT; + priv->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT | + IEEE802154_HW_PROMISCUOUS; priv->hw->phy->flags = WPAN_PHY_FLAG_TXPOWER; @@ -919,6 +1018,11 @@ static int cc2520_hw_init(struct cc2520_private *priv) } /* Registers default value: section 28.1 in Datasheet */ + + /* Set the CCA threshold to -50 dBm. This seems to have been copied + * from the TinyOS CC2520 driver and is much higher than the -84 dBm + * threshold suggested in the datasheet. + */ ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A); if (ret) goto err_ret; @@ -955,15 +1059,10 @@ static int cc2520_hw_init(struct cc2520_private *priv) if (ret) goto err_ret; - ret = cc2520_write_register(priv, CC2520_FRMCTRL0, 0x60); - if (ret) - goto err_ret; - - ret = cc2520_write_register(priv, CC2520_FRMCTRL1, 0x03); - if (ret) - goto err_ret; - - ret = cc2520_write_register(priv, CC2520_FRMFILT0, 0x00); + /* Configure registers correctly for this driver. */ + ret = cc2520_write_register(priv, CC2520_FRMCTRL1, + FRMCTRL1_SET_RXENMASK_ON_TX | + FRMCTRL1_IGNORE_TX_UNDERF); if (ret) goto err_ret; -- cgit v1.2.3-59-g8ed1b From d3d20725407955d0bb107939f23535d2e7dadbee Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Jan 2016 14:23:21 +0200 Subject: Bluetooth: hci_bcm: move all Broadcom ACPI IDs to BCM HCI driver The IDs should all be for Broadcom BCM43241 module, and hci_bcm is now the proper driver for them. This removes one of two different ways of handling PM with the module. Cc: Johannes Berg Signed-off-by: Heikki Krogerus Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcm.c | 4 ++++ net/rfkill/rfkill-gpio.c | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 5c7c696c5838..abf13a754689 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -814,7 +814,11 @@ static const struct hci_uart_proto bcm_proto = { #ifdef CONFIG_ACPI static const struct acpi_device_id bcm_acpi_match[] = { + { "BCM2E1A", 0 }, { "BCM2E39", 0 }, + { "BCM2E3D", 0 }, + { "BCM2E40", 0 }, + { "BCM2E64", 0 }, { "BCM2E65", 0 }, { "BCM2E67", 0 }, { }, diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index 93127220cb54..4b1e3f35f06c 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -163,10 +163,6 @@ static int rfkill_gpio_remove(struct platform_device *pdev) #ifdef CONFIG_ACPI static const struct acpi_device_id rfkill_acpi_match[] = { - { "BCM2E1A", RFKILL_TYPE_BLUETOOTH }, - { "BCM2E3D", RFKILL_TYPE_BLUETOOTH }, - { "BCM2E40", RFKILL_TYPE_BLUETOOTH }, - { "BCM2E64", RFKILL_TYPE_BLUETOOTH }, { "BCM4752", RFKILL_TYPE_GPS }, { "LNV4752", RFKILL_TYPE_GPS }, { }, -- cgit v1.2.3-59-g8ed1b From adbdeae5cece1d888a1542edfe9e1c6f8884fef8 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 4 Jan 2016 14:23:22 +0200 Subject: Bluetooth: hci_bcm: new ACPI IDs These are used at least by Acer with BCM43241. Signed-off-by: Heikki Krogerus Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index abf13a754689..5f3de181e744 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -816,11 +816,14 @@ static const struct hci_uart_proto bcm_proto = { static const struct acpi_device_id bcm_acpi_match[] = { { "BCM2E1A", 0 }, { "BCM2E39", 0 }, + { "BCM2E3A", 0 }, { "BCM2E3D", 0 }, + { "BCM2E3F", 0 }, { "BCM2E40", 0 }, { "BCM2E64", 0 }, { "BCM2E65", 0 }, { "BCM2E67", 0 }, + { "BCM2E7B", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, bcm_acpi_match); -- cgit v1.2.3-59-g8ed1b From 86f7ac77d4035e22ec7e58dcdb96327e2ecc3a9b Mon Sep 17 00:00:00 2001 From: Chin-Ran Lo Date: Tue, 29 Dec 2015 04:26:33 -0800 Subject: Bluetooth: btmrvl: fix hung task warning dump It's been observed that when bluetooth driver fails to activate the firmware, below hung task warning dump is displayed after 120 seconds. [ 36.461022] Bluetooth: vendor=0x2df, device=0x912e, class=255, fn=2 [ 56.512128] Bluetooth: FW failed to be active in time! [ 56.517264] Bluetooth: Downloading firmware failed! [ 240.252176] INFO: task kworker/3:2:129 blocked for more than 120 seconds. [ 240.258931] Not tainted 3.18.0 #254 [ 240.262972] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 240.270751] kworker/3:2 D ffffffc000205760 0 129 2 0x00000000 [ 240.277825] Workqueue: events request_firmware_work_func [ 240.283134] Call trace: [ 240.285581] [] __switch_to+0x80/0x8c [ 240.290693] [] __schedule+0x540/0x7b8 [ 240.295921] [] schedule+0x78/0x84 [ 240.300764] [] __mmc_claim_host+0xe8/0x1c8 [ 240.306395] [] sdio_claim_host+0x74/0x84 [ 240.311840] [] 0xffffffbffc163d08 [ 240.316685] [] 0xffffffbffc165104 [ 240.321524] [] mwifiex_dnld_fw+0x98/0x110 [mwifiex] [ 240.327918] [] mwifiex_remove_card+0x2c4/0x5fc [mwifiex] [ 240.334741] [] request_firmware_work_func+0x44/0x80 [ 240.341127] [] process_one_work+0x2ec/0x50c [ 240.346831] [] worker_thread+0x350/0x470 [ 240.352272] [] kthread+0xf0/0xfc [ 240.357019] 2 locks held by kworker/3:2/129: [ 240.361248] #0: ("events"){.+.+.+}, at: [] process_one_work+0x1f8/0x50c [ 240.369562] #1: ((&fw_work->work)){+.+.+.}, at: [] process_one_work+0x1f8/0x50c [ 240.378589] task PC stack pid father [ 240.384501] kworker/1:1 D ffffffc000205760 0 40 2 0x00000000 [ 240.391524] Workqueue: events mtk_atomic_work [ 240.395884] Call trace: [ 240.398317] [] __switch_to+0x80/0x8c [ 240.403448] [] lock_acquire+0x128/0x164 [ 240.408821] kworker/3:2 D ffffffc000205760 0 129 2 0x00000000 [ 240.415867] Workqueue: events request_firmware_work_func [ 240.421138] Call trace: [ 240.423589] [] __switch_to+0x80/0x8c [ 240.428688] [] __schedule+0x540/0x7b8 [ 240.433886] [] schedule+0x78/0x84 [ 240.438732] [] __mmc_claim_host+0xe8/0x1c8 [ 240.444361] [] sdio_claim_host+0x74/0x84 [ 240.449801] [] 0xffffffbffc163d08 [ 240.454649] [] 0xffffffbffc165104 [ 240.459486] [] mwifiex_dnld_fw+0x98/0x110 [mwifiex] [ 240.465882] [] mwifiex_remove_card+0x2c4/0x5fc [mwifiex] [ 240.472705] [] request_firmware_work_func+0x44/0x80 [ 240.479090] [] process_one_work+0x2ec/0x50c [ 240.484794] [] worker_thread+0x350/0x470 [ 240.490231] [] kthread+0xf0/0xfc This patch adds missing sdio_release_host() call so that wlan driver thread can claim sdio host. Fixes: 4863e4cc31d647e1 ("Bluetooth: btmrvl: release sdio bus after firmware is up") Signed-off-by: Chin-Ran Lo Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_sdio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index d3a4acdf98c9..73a1c2779969 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -1112,7 +1112,8 @@ static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card) */ if (btmrvl_sdio_verify_fw_download(card, pollnum)) { BT_ERR("FW failed to be active in time!"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto done; } sdio_release_host(card->func); -- cgit v1.2.3-59-g8ed1b From 9f5545905fbcc069f6fa8030b866e967ec6a5c73 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sat, 2 Jan 2016 23:44:58 -0500 Subject: bnxt_en: Refactor bnxt_dbg_dump_states(). By adding 3 separate functions to dump the different ring states. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 50 ++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 319653a3e333..3f053de32f3a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5015,31 +5015,47 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features) return rc; } +static void bnxt_dump_tx_sw_state(struct bnxt_napi *bnapi) +{ + struct bnxt_tx_ring_info *txr = &bnapi->tx_ring; + int i = bnapi->index; + + netdev_info(bnapi->bp->dev, "[%d]: tx{fw_ring: %d prod: %x cons: %x}\n", + i, txr->tx_ring_struct.fw_ring_id, txr->tx_prod, + txr->tx_cons); +} + +static void bnxt_dump_rx_sw_state(struct bnxt_napi *bnapi) +{ + struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + int i = bnapi->index; + + netdev_info(bnapi->bp->dev, "[%d]: rx{fw_ring: %d prod: %x} rx_agg{fw_ring: %d agg_prod: %x sw_agg_prod: %x}\n", + i, rxr->rx_ring_struct.fw_ring_id, rxr->rx_prod, + rxr->rx_agg_ring_struct.fw_ring_id, rxr->rx_agg_prod, + rxr->rx_sw_agg_prod); +} + +static void bnxt_dump_cp_sw_state(struct bnxt_napi *bnapi) +{ + struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; + int i = bnapi->index; + + netdev_info(bnapi->bp->dev, "[%d]: cp{fw_ring: %d raw_cons: %x}\n", + i, cpr->cp_ring_struct.fw_ring_id, cpr->cp_raw_cons); +} + static void bnxt_dbg_dump_states(struct bnxt *bp) { int i; struct bnxt_napi *bnapi; - struct bnxt_tx_ring_info *txr; - struct bnxt_rx_ring_info *rxr; - struct bnxt_cp_ring_info *cpr; for (i = 0; i < bp->cp_nr_rings; i++) { bnapi = bp->bnapi[i]; - txr = &bnapi->tx_ring; - rxr = &bnapi->rx_ring; - cpr = &bnapi->cp_ring; if (netif_msg_drv(bp)) { - netdev_info(bp->dev, "[%d]: tx{fw_ring: %d prod: %x cons: %x}\n", - i, txr->tx_ring_struct.fw_ring_id, - txr->tx_prod, txr->tx_cons); - netdev_info(bp->dev, "[%d]: rx{fw_ring: %d prod: %x} rx_agg{fw_ring: %d agg_prod: %x sw_agg_prod: %x}\n", - i, rxr->rx_ring_struct.fw_ring_id, - rxr->rx_prod, - rxr->rx_agg_ring_struct.fw_ring_id, - rxr->rx_agg_prod, rxr->rx_sw_agg_prod); - netdev_info(bp->dev, "[%d]: cp{fw_ring: %d raw_cons: %x}\n", - i, cpr->cp_ring_struct.fw_ring_id, - cpr->cp_raw_cons); + bnxt_dump_tx_sw_state(bnapi); + bnxt_dump_rx_sw_state(bnapi); + bnxt_dump_cp_sw_state(bnapi); } } } -- cgit v1.2.3-59-g8ed1b From b6ab4b01f53b5f9e17dbd4f91c95fa5049fa2101 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sat, 2 Jan 2016 23:44:59 -0500 Subject: bnxt_en: Separate bnxt_{rx|tx}_ring_info structs from bnxt_napi struct. Currently, an rx and a tx ring are always paired with a completion ring. We want to restructure it so that it is possible to have a dedicated completion ring for tx or rx only. The bnxt hardware uses a completion ring for rx and tx events. The driver has to process the completion ring entries sequentially for the rx and tx events. Using a dedicated completion ring for rx only or tx only has these benefits: 1. A burst of rx packets can cause delay in processing tx events if the completion ring is shared. If tx queue is stopped by BQL, this can cause delay in re-starting the tx queue. 2. A completion ring is sized according to the rx and tx ring size rounded up to the nearest power of 2. When the completion ring is shared, it is sized by adding the rx and tx ring sizes and then rounded to the next power of 2, often with a lot of wasted space. 3. Using dedicated completion ring, we can adjust the tx and rx coalescing parameters independently for rx and tx. The first step is to separate the rx and tx ring structures from the bnxt_napi struct. In this patch, an rx ring and a tx ring will point to the same bnxt_napi struct to share the same completion ring. No change in ring assignment and mapping yet. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 147 +++++++++++++----------------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 9 +- 2 files changed, 70 insertions(+), 86 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 3f053de32f3a..8635e0323dbd 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -179,7 +179,6 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) u32 len, free_size, vlan_tag_flags, cfa_action, flags; u16 prod, last_frag; struct pci_dev *pdev = bp->pdev; - struct bnxt_napi *bnapi; struct bnxt_tx_ring_info *txr; struct bnxt_sw_tx_bd *tx_buf; @@ -189,8 +188,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } - bnapi = bp->bnapi[i]; - txr = &bnapi->tx_ring; + txr = &bp->tx_ring[i]; txq = netdev_get_tx_queue(dev, i); prod = txr->tx_prod; @@ -423,7 +421,7 @@ tx_dma_error: static void bnxt_tx_int(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts) { - struct bnxt_tx_ring_info *txr = &bnapi->tx_ring; + struct bnxt_tx_ring_info *txr = bnapi->tx_ring; int index = bnapi->index; struct netdev_queue *txq = netdev_get_tx_queue(bp->dev, index); u16 cons = txr->tx_cons; @@ -602,7 +600,7 @@ static void bnxt_reuse_rx_agg_bufs(struct bnxt_napi *bnapi, u16 cp_cons, { struct bnxt *bp = bnapi->bp; struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; u16 prod = rxr->rx_agg_prod; u16 sw_prod = rxr->rx_sw_agg_prod; u32 i; @@ -681,7 +679,7 @@ static struct sk_buff *bnxt_rx_pages(struct bnxt *bp, struct bnxt_napi *bnapi, { struct pci_dev *pdev = bp->pdev; struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; u16 prod = rxr->rx_agg_prod; u32 i; @@ -940,7 +938,7 @@ static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp, bool *agg_event) { struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; u8 agg_id = TPA_END_AGG_ID(tpa_end); u8 *data, agg_bufs; u16 cp_cons = RING_CMP(*raw_cons); @@ -1056,7 +1054,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, bool *agg_event) { struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; struct net_device *dev = bp->dev; struct rx_cmp *rxcmp; struct rx_cmp_ext *rxcmp1; @@ -1390,7 +1388,7 @@ static int bnxt_poll_work(struct bnxt *bp, struct bnxt_napi *bnapi, int budget) bnxt_tx_int(bp, bnapi, tx_pkts); if (rx_event) { - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell); writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell); @@ -1459,19 +1457,14 @@ static void bnxt_free_tx_skbs(struct bnxt *bp) int i, max_idx; struct pci_dev *pdev = bp->pdev; - if (!bp->bnapi) + if (!bp->tx_ring) return; max_idx = bp->tx_nr_pages * TX_DESC_CNT; for (i = 0; i < bp->tx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_tx_ring_info *txr; + struct bnxt_tx_ring_info *txr = &bp->tx_ring[i]; int j; - if (!bnapi) - continue; - - txr = &bnapi->tx_ring; for (j = 0; j < max_idx;) { struct bnxt_sw_tx_bd *tx_buf = &txr->tx_buf_ring[j]; struct sk_buff *skb = tx_buf->skb; @@ -1517,21 +1510,15 @@ static void bnxt_free_rx_skbs(struct bnxt *bp) int i, max_idx, max_agg_idx; struct pci_dev *pdev = bp->pdev; - if (!bp->bnapi) + if (!bp->rx_ring) return; max_idx = bp->rx_nr_pages * RX_DESC_CNT; max_agg_idx = bp->rx_agg_nr_pages * RX_DESC_CNT; for (i = 0; i < bp->rx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_rx_ring_info *rxr; + struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; int j; - if (!bnapi) - continue; - - rxr = &bnapi->rx_ring; - if (rxr->rx_tpa) { for (j = 0; j < MAX_TPA; j++) { struct bnxt_tpa_info *tpa_info = @@ -1659,19 +1646,13 @@ static void bnxt_free_rx_rings(struct bnxt *bp) { int i; - if (!bp->bnapi) + if (!bp->rx_ring) return; for (i = 0; i < bp->rx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_rx_ring_info *rxr; + struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; struct bnxt_ring_struct *ring; - if (!bnapi) - continue; - - rxr = &bnapi->rx_ring; - kfree(rxr->rx_tpa); rxr->rx_tpa = NULL; @@ -1690,6 +1671,9 @@ static int bnxt_alloc_rx_rings(struct bnxt *bp) { int i, rc, agg_rings = 0, tpa_rings = 0; + if (!bp->rx_ring) + return -ENOMEM; + if (bp->flags & BNXT_FLAG_AGG_RINGS) agg_rings = 1; @@ -1697,14 +1681,9 @@ static int bnxt_alloc_rx_rings(struct bnxt *bp) tpa_rings = 1; for (i = 0; i < bp->rx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_rx_ring_info *rxr; + struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; struct bnxt_ring_struct *ring; - if (!bnapi) - continue; - - rxr = &bnapi->rx_ring; ring = &rxr->rx_ring_struct; rc = bnxt_alloc_ring(bp, ring); @@ -1742,19 +1721,13 @@ static void bnxt_free_tx_rings(struct bnxt *bp) int i; struct pci_dev *pdev = bp->pdev; - if (!bp->bnapi) + if (!bp->tx_ring) return; for (i = 0; i < bp->tx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_tx_ring_info *txr; + struct bnxt_tx_ring_info *txr = &bp->tx_ring[i]; struct bnxt_ring_struct *ring; - if (!bnapi) - continue; - - txr = &bnapi->tx_ring; - if (txr->tx_push) { dma_free_coherent(&pdev->dev, bp->tx_push_size, txr->tx_push, txr->tx_push_mapping); @@ -1788,14 +1761,9 @@ static int bnxt_alloc_tx_rings(struct bnxt *bp) } for (i = 0, j = 0; i < bp->tx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_tx_ring_info *txr; + struct bnxt_tx_ring_info *txr = &bp->tx_ring[i]; struct bnxt_ring_struct *ring; - if (!bnapi) - continue; - - txr = &bnapi->tx_ring; ring = &txr->tx_ring_struct; rc = bnxt_alloc_ring(bp, ring); @@ -1898,7 +1866,7 @@ static void bnxt_init_ring_struct(struct bnxt *bp) ring->dma_arr = cpr->cp_desc_mapping; ring->vmem_size = 0; - rxr = &bnapi->rx_ring; + rxr = bnapi->rx_ring; ring = &rxr->rx_ring_struct; ring->nr_pages = bp->rx_nr_pages; ring->page_size = HW_RXBD_RING_SIZE; @@ -1915,7 +1883,7 @@ static void bnxt_init_ring_struct(struct bnxt *bp) ring->vmem_size = SW_RXBD_AGG_RING_SIZE * bp->rx_agg_nr_pages; ring->vmem = (void **)&rxr->rx_agg_ring; - txr = &bnapi->tx_ring; + txr = bnapi->tx_ring; ring = &txr->tx_ring_struct; ring->nr_pages = bp->tx_nr_pages; ring->page_size = HW_RXBD_RING_SIZE; @@ -1951,22 +1919,18 @@ static void bnxt_init_rxbd_pages(struct bnxt_ring_struct *ring, u32 type) static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr) { struct net_device *dev = bp->dev; - struct bnxt_napi *bnapi = bp->bnapi[ring_nr]; struct bnxt_rx_ring_info *rxr; struct bnxt_ring_struct *ring; u32 prod, type; int i; - if (!bnapi) - return -EINVAL; - type = (bp->rx_buf_use_size << RX_BD_LEN_SHIFT) | RX_BD_TYPE_RX_PACKET_BD | RX_BD_FLAGS_EOP; if (NET_IP_ALIGN == 2) type |= RX_BD_FLAGS_SOP; - rxr = &bnapi->rx_ring; + rxr = &bp->rx_ring[ring_nr]; ring = &rxr->rx_ring_struct; bnxt_init_rxbd_pages(ring, type); @@ -2048,8 +2012,7 @@ static int bnxt_init_tx_rings(struct bnxt *bp) MAX_SKB_FRAGS + 1); for (i = 0; i < bp->tx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_tx_ring_info *txr = &bnapi->tx_ring; + struct bnxt_tx_ring_info *txr = &bp->tx_ring[i]; struct bnxt_ring_struct *ring = &txr->tx_ring_struct; ring->fw_ring_id = INVALID_HW_RING_ID; @@ -2436,11 +2399,11 @@ static void bnxt_clear_ring_indices(struct bnxt *bp) cpr = &bnapi->cp_ring; cpr->cp_raw_cons = 0; - txr = &bnapi->tx_ring; + txr = bnapi->tx_ring; txr->tx_prod = 0; txr->tx_cons = 0; - rxr = &bnapi->rx_ring; + rxr = bnapi->rx_ring; rxr->rx_prod = 0; rxr->rx_agg_prod = 0; rxr->rx_sw_agg_prod = 0; @@ -2509,6 +2472,10 @@ static void bnxt_free_mem(struct bnxt *bp, bool irq_re_init) bnxt_free_stats(bp); bnxt_free_ring_grps(bp); bnxt_free_vnics(bp); + kfree(bp->tx_ring); + bp->tx_ring = NULL; + kfree(bp->rx_ring); + bp->rx_ring = NULL; kfree(bp->bnapi); bp->bnapi = NULL; } else { @@ -2540,6 +2507,28 @@ static int bnxt_alloc_mem(struct bnxt *bp, bool irq_re_init) bp->bnapi[i]->bp = bp; } + bp->rx_ring = kcalloc(bp->rx_nr_rings, + sizeof(struct bnxt_rx_ring_info), + GFP_KERNEL); + if (!bp->rx_ring) + return -ENOMEM; + + for (i = 0; i < bp->rx_nr_rings; i++) { + bp->rx_ring[i].bnapi = bp->bnapi[i]; + bp->bnapi[i]->rx_ring = &bp->rx_ring[i]; + } + + bp->tx_ring = kcalloc(bp->tx_nr_rings, + sizeof(struct bnxt_tx_ring_info), + GFP_KERNEL); + if (!bp->tx_ring) + return -ENOMEM; + + for (i = 0; i < bp->tx_nr_rings; i++) { + bp->tx_ring[i].bnapi = bp->bnapi[i]; + bp->bnapi[i]->tx_ring = &bp->tx_ring[i]; + } + rc = bnxt_alloc_stats(bp); if (rc) goto alloc_mem_err; @@ -3332,8 +3321,7 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp) } for (i = 0; i < bp->tx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_tx_ring_info *txr = &bnapi->tx_ring; + struct bnxt_tx_ring_info *txr = &bp->tx_ring[i]; struct bnxt_ring_struct *ring = &txr->tx_ring_struct; u16 fw_stats_ctx = bp->grp_info[i].fw_stats_ctx; @@ -3345,8 +3333,7 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp) } for (i = 0; i < bp->rx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; struct bnxt_ring_struct *ring = &rxr->rx_ring_struct; rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_RX, i, @@ -3360,8 +3347,7 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp) if (bp->flags & BNXT_FLAG_AGG_RINGS) { for (i = 0; i < bp->rx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; struct bnxt_ring_struct *ring = &rxr->rx_agg_ring_struct; @@ -3431,8 +3417,7 @@ static void bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path) return; for (i = 0; i < bp->tx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_tx_ring_info *txr = &bnapi->tx_ring; + struct bnxt_tx_ring_info *txr = &bp->tx_ring[i]; struct bnxt_ring_struct *ring = &txr->tx_ring_struct; u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; @@ -3446,8 +3431,7 @@ static void bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path) } for (i = 0; i < bp->rx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; struct bnxt_ring_struct *ring = &rxr->rx_ring_struct; u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; @@ -3462,8 +3446,7 @@ static void bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path) } for (i = 0; i < bp->rx_nr_rings; i++) { - struct bnxt_napi *bnapi = bp->bnapi[i]; - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; struct bnxt_ring_struct *ring = &rxr->rx_agg_ring_struct; u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; @@ -4258,14 +4241,12 @@ static void bnxt_enable_napi(struct bnxt *bp) static void bnxt_tx_disable(struct bnxt *bp) { int i; - struct bnxt_napi *bnapi; struct bnxt_tx_ring_info *txr; struct netdev_queue *txq; - if (bp->bnapi) { + if (bp->tx_ring) { for (i = 0; i < bp->tx_nr_rings; i++) { - bnapi = bp->bnapi[i]; - txr = &bnapi->tx_ring; + txr = &bp->tx_ring[i]; txq = netdev_get_tx_queue(bp->dev, i); __netif_tx_lock(txq, smp_processor_id()); txr->dev_state = BNXT_DEV_STATE_CLOSING; @@ -4280,13 +4261,11 @@ static void bnxt_tx_disable(struct bnxt *bp) static void bnxt_tx_enable(struct bnxt *bp) { int i; - struct bnxt_napi *bnapi; struct bnxt_tx_ring_info *txr; struct netdev_queue *txq; for (i = 0; i < bp->tx_nr_rings; i++) { - bnapi = bp->bnapi[i]; - txr = &bnapi->tx_ring; + txr = &bp->tx_ring[i]; txq = netdev_get_tx_queue(bp->dev, i); txr->dev_state = 0; } @@ -5017,7 +4996,7 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features) static void bnxt_dump_tx_sw_state(struct bnxt_napi *bnapi) { - struct bnxt_tx_ring_info *txr = &bnapi->tx_ring; + struct bnxt_tx_ring_info *txr = bnapi->tx_ring; int i = bnapi->index; netdev_info(bnapi->bp->dev, "[%d]: tx{fw_ring: %d prod: %x cons: %x}\n", @@ -5027,7 +5006,7 @@ static void bnxt_dump_tx_sw_state(struct bnxt_napi *bnapi) static void bnxt_dump_rx_sw_state(struct bnxt_napi *bnapi) { - struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring; + struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; int i = bnapi->index; netdev_info(bnapi->bp->dev, "[%d]: rx{fw_ring: %d prod: %x} rx_agg{fw_ring: %d agg_prod: %x sw_agg_prod: %x}\n", diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 78d639d259d1..ea2fd927442b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -528,6 +528,7 @@ struct tx_push_bd { }; struct bnxt_tx_ring_info { + struct bnxt_napi *bnapi; u16 tx_prod; u16 tx_cons; void __iomem *tx_doorbell; @@ -558,6 +559,7 @@ struct bnxt_tpa_info { }; struct bnxt_rx_ring_info { + struct bnxt_napi *bnapi; u16 rx_prod; u16 rx_agg_prod; u16 rx_sw_agg_prod; @@ -604,8 +606,8 @@ struct bnxt_napi { int index; struct bnxt_cp_ring_info cp_ring; - struct bnxt_rx_ring_info rx_ring; - struct bnxt_tx_ring_info tx_ring; + struct bnxt_rx_ring_info *rx_ring; + struct bnxt_tx_ring_info *tx_ring; #ifdef CONFIG_NET_RX_BUSY_POLL atomic_t poll_state; @@ -884,6 +886,9 @@ struct bnxt { struct bnxt_napi **bnapi; + struct bnxt_rx_ring_info *rx_ring; + struct bnxt_tx_ring_info *tx_ring; + u32 rx_buf_size; u32 rx_buf_use_size; /* useable size */ u32 rx_ring_size; -- cgit v1.2.3-59-g8ed1b From 3b2b7d9db74adb95aa0bd029298a741333eb847e Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sat, 2 Jan 2016 23:45:00 -0500 Subject: bnxt_en: Check for NULL rx or tx ring. Each bnxt_napi structure may no longer be having both an rx ring and a tx ring. Check for a valid ring before using it. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 8635e0323dbd..11478c912b1d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1867,6 +1867,9 @@ static void bnxt_init_ring_struct(struct bnxt *bp) ring->vmem_size = 0; rxr = bnapi->rx_ring; + if (!rxr) + goto skip_rx; + ring = &rxr->rx_ring_struct; ring->nr_pages = bp->rx_nr_pages; ring->page_size = HW_RXBD_RING_SIZE; @@ -1883,7 +1886,11 @@ static void bnxt_init_ring_struct(struct bnxt *bp) ring->vmem_size = SW_RXBD_AGG_RING_SIZE * bp->rx_agg_nr_pages; ring->vmem = (void **)&rxr->rx_agg_ring; +skip_rx: txr = bnapi->tx_ring; + if (!txr) + continue; + ring = &txr->tx_ring_struct; ring->nr_pages = bp->tx_nr_pages; ring->page_size = HW_RXBD_RING_SIZE; @@ -2400,13 +2407,17 @@ static void bnxt_clear_ring_indices(struct bnxt *bp) cpr->cp_raw_cons = 0; txr = bnapi->tx_ring; - txr->tx_prod = 0; - txr->tx_cons = 0; + if (txr) { + txr->tx_prod = 0; + txr->tx_cons = 0; + } rxr = bnapi->rx_ring; - rxr->rx_prod = 0; - rxr->rx_agg_prod = 0; - rxr->rx_sw_agg_prod = 0; + if (rxr) { + rxr->rx_prod = 0; + rxr->rx_agg_prod = 0; + rxr->rx_sw_agg_prod = 0; + } } } @@ -4999,6 +5010,9 @@ static void bnxt_dump_tx_sw_state(struct bnxt_napi *bnapi) struct bnxt_tx_ring_info *txr = bnapi->tx_ring; int i = bnapi->index; + if (!txr) + return; + netdev_info(bnapi->bp->dev, "[%d]: tx{fw_ring: %d prod: %x cons: %x}\n", i, txr->tx_ring_struct.fw_ring_id, txr->tx_prod, txr->tx_cons); @@ -5009,6 +5023,9 @@ static void bnxt_dump_rx_sw_state(struct bnxt_napi *bnapi) struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; int i = bnapi->index; + if (!rxr) + return; + netdev_info(bnapi->bp->dev, "[%d]: rx{fw_ring: %d prod: %x} rx_agg{fw_ring: %d agg_prod: %x sw_agg_prod: %x}\n", i, rxr->rx_ring_struct.fw_ring_id, rxr->rx_prod, rxr->rx_agg_ring_struct.fw_ring_id, rxr->rx_agg_prod, -- cgit v1.2.3-59-g8ed1b From b81a90d3028af92da61a61e2efd231a585180044 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sat, 2 Jan 2016 23:45:01 -0500 Subject: bnxt_en: Re-structure ring indexing and mapping. In order to support dedicated or shared completion rings, the ring indexing and mapping are re-structured as below: 1. bp->grp_info[] array index is 1:1 with bp->bnapi[] array index and completion ring index. 2. rx rings 0 to n will be mapped to completion rings 0 to n. 3. If tx and rx rings share completion rings, then tx rings 0 to m will be mapped to completion rings 0 to m. 4. If tx and rx rings use dedicated completion rings, then tx rings 0 to m will be mapped to completion rings n + 1 to n + m. 5. Each tx or rx ring will use the corresponding completion ring index for doorbell mapping and MSIX mapping. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 91 ++++++++++++++++++------------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 + 2 files changed, 55 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 11478c912b1d..8a54dabdcc7d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -422,7 +422,7 @@ tx_dma_error: static void bnxt_tx_int(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts) { struct bnxt_tx_ring_info *txr = bnapi->tx_ring; - int index = bnapi->index; + int index = txr - &bp->tx_ring[0]; struct netdev_queue *txq = netdev_get_tx_queue(bp->dev, index); u16 cons = txr->tx_cons; struct pci_dev *pdev = bp->pdev; @@ -3082,7 +3082,7 @@ static int bnxt_hwrm_vnic_ctx_alloc(struct bnxt *bp, u16 vnic_id) static int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id) { - int grp_idx = 0; + unsigned int ring = 0, grp_idx; struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id]; struct hwrm_vnic_cfg_input req = {0}; @@ -3093,10 +3093,11 @@ static int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id) req.rss_rule = cpu_to_le16(vnic->fw_rss_cos_lb_ctx); req.cos_rule = cpu_to_le16(0xffff); if (vnic->flags & BNXT_VNIC_RSS_FLAG) - grp_idx = 0; + ring = 0; else if (vnic->flags & BNXT_VNIC_RFS_FLAG) - grp_idx = vnic_id - 1; + ring = vnic_id - 1; + grp_idx = bp->rx_ring[ring].bnapi->index; req.vnic_id = cpu_to_le16(vnic->fw_vnic_id); req.dflt_ring_grp = cpu_to_le16(bp->grp_info[grp_idx].fw_grp_id); @@ -3137,22 +3138,25 @@ static void bnxt_hwrm_vnic_free(struct bnxt *bp) bnxt_hwrm_vnic_free_one(bp, i); } -static int bnxt_hwrm_vnic_alloc(struct bnxt *bp, u16 vnic_id, u16 start_grp_id, - u16 end_grp_id) +static int bnxt_hwrm_vnic_alloc(struct bnxt *bp, u16 vnic_id, + unsigned int start_rx_ring_idx, + unsigned int nr_rings) { - u32 rc = 0, i, j; + int rc = 0; + unsigned int i, j, grp_idx, end_idx = start_rx_ring_idx + nr_rings; struct hwrm_vnic_alloc_input req = {0}; struct hwrm_vnic_alloc_output *resp = bp->hwrm_cmd_resp_addr; /* map ring groups to this vnic */ - for (i = start_grp_id, j = 0; i < end_grp_id; i++, j++) { - if (bp->grp_info[i].fw_grp_id == INVALID_HW_RING_ID) { + for (i = start_rx_ring_idx, j = 0; i < end_idx; i++, j++) { + grp_idx = bp->rx_ring[i].bnapi->index; + if (bp->grp_info[grp_idx].fw_grp_id == INVALID_HW_RING_ID) { netdev_err(bp->dev, "Not enough ring groups avail:%x req:%x\n", - j, (end_grp_id - start_grp_id)); + j, nr_rings); break; } bp->vnic_info[vnic_id].fw_grp_ids[j] = - bp->grp_info[i].fw_grp_id; + bp->grp_info[grp_idx].fw_grp_id; } bp->vnic_info[vnic_id].fw_rss_cos_lb_ctx = INVALID_HW_RING_ID; @@ -3179,20 +3183,22 @@ static int bnxt_hwrm_ring_grp_alloc(struct bnxt *bp) struct hwrm_ring_grp_alloc_input req = {0}; struct hwrm_ring_grp_alloc_output *resp = bp->hwrm_cmd_resp_addr; + unsigned int grp_idx = bp->rx_ring[i].bnapi->index; bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_RING_GRP_ALLOC, -1, -1); - req.cr = cpu_to_le16(bp->grp_info[i].cp_fw_ring_id); - req.rr = cpu_to_le16(bp->grp_info[i].rx_fw_ring_id); - req.ar = cpu_to_le16(bp->grp_info[i].agg_fw_ring_id); - req.sc = cpu_to_le16(bp->grp_info[i].fw_stats_ctx); + req.cr = cpu_to_le16(bp->grp_info[grp_idx].cp_fw_ring_id); + req.rr = cpu_to_le16(bp->grp_info[grp_idx].rx_fw_ring_id); + req.ar = cpu_to_le16(bp->grp_info[grp_idx].agg_fw_ring_id); + req.sc = cpu_to_le16(bp->grp_info[grp_idx].fw_stats_ctx); rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); if (rc) break; - bp->grp_info[i].fw_grp_id = le32_to_cpu(resp->ring_group_id); + bp->grp_info[grp_idx].fw_grp_id = + le32_to_cpu(resp->ring_group_id); } mutex_unlock(&bp->hwrm_cmd_lock); return rc; @@ -3334,26 +3340,28 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp) for (i = 0; i < bp->tx_nr_rings; i++) { struct bnxt_tx_ring_info *txr = &bp->tx_ring[i]; struct bnxt_ring_struct *ring = &txr->tx_ring_struct; - u16 fw_stats_ctx = bp->grp_info[i].fw_stats_ctx; + u32 map_idx = txr->bnapi->index; + u16 fw_stats_ctx = bp->grp_info[map_idx].fw_stats_ctx; - rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_TX, i, - fw_stats_ctx); + rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_TX, + map_idx, fw_stats_ctx); if (rc) goto err_out; - txr->tx_doorbell = bp->bar1 + i * 0x80; + txr->tx_doorbell = bp->bar1 + map_idx * 0x80; } for (i = 0; i < bp->rx_nr_rings; i++) { struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; struct bnxt_ring_struct *ring = &rxr->rx_ring_struct; + u32 map_idx = rxr->bnapi->index; - rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_RX, i, - INVALID_STATS_CTX_ID); + rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_RX, + map_idx, INVALID_STATS_CTX_ID); if (rc) goto err_out; - rxr->rx_doorbell = bp->bar1 + i * 0x80; + rxr->rx_doorbell = bp->bar1 + map_idx * 0x80; writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell); - bp->grp_info[i].rx_fw_ring_id = ring->fw_ring_id; + bp->grp_info[map_idx].rx_fw_ring_id = ring->fw_ring_id; } if (bp->flags & BNXT_FLAG_AGG_RINGS) { @@ -3361,19 +3369,20 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp) struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; struct bnxt_ring_struct *ring = &rxr->rx_agg_ring_struct; + u32 grp_idx = rxr->bnapi->index; + u32 map_idx = grp_idx + bp->rx_nr_rings; rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_AGG, - bp->rx_nr_rings + i, + map_idx, INVALID_STATS_CTX_ID); if (rc) goto err_out; - rxr->rx_agg_doorbell = - bp->bar1 + (bp->rx_nr_rings + i) * 0x80; + rxr->rx_agg_doorbell = bp->bar1 + map_idx * 0x80; writel(DB_KEY_RX | rxr->rx_agg_prod, rxr->rx_agg_doorbell); - bp->grp_info[i].agg_fw_ring_id = ring->fw_ring_id; + bp->grp_info[grp_idx].agg_fw_ring_id = ring->fw_ring_id; } } err_out: @@ -3430,7 +3439,8 @@ static void bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path) for (i = 0; i < bp->tx_nr_rings; i++) { struct bnxt_tx_ring_info *txr = &bp->tx_ring[i]; struct bnxt_ring_struct *ring = &txr->tx_ring_struct; - u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; + u32 grp_idx = txr->bnapi->index; + u32 cmpl_ring_id = bp->grp_info[grp_idx].cp_fw_ring_id; if (ring->fw_ring_id != INVALID_HW_RING_ID) { hwrm_ring_free_send_msg(bp, ring, @@ -3444,7 +3454,8 @@ static void bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path) for (i = 0; i < bp->rx_nr_rings; i++) { struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; struct bnxt_ring_struct *ring = &rxr->rx_ring_struct; - u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; + u32 grp_idx = rxr->bnapi->index; + u32 cmpl_ring_id = bp->grp_info[grp_idx].cp_fw_ring_id; if (ring->fw_ring_id != INVALID_HW_RING_ID) { hwrm_ring_free_send_msg(bp, ring, @@ -3452,14 +3463,16 @@ static void bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path) close_path ? cmpl_ring_id : INVALID_HW_RING_ID); ring->fw_ring_id = INVALID_HW_RING_ID; - bp->grp_info[i].rx_fw_ring_id = INVALID_HW_RING_ID; + bp->grp_info[grp_idx].rx_fw_ring_id = + INVALID_HW_RING_ID; } } for (i = 0; i < bp->rx_nr_rings; i++) { struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i]; struct bnxt_ring_struct *ring = &rxr->rx_agg_ring_struct; - u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id; + u32 grp_idx = rxr->bnapi->index; + u32 cmpl_ring_id = bp->grp_info[grp_idx].cp_fw_ring_id; if (ring->fw_ring_id != INVALID_HW_RING_ID) { hwrm_ring_free_send_msg(bp, ring, @@ -3467,7 +3480,8 @@ static void bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path) close_path ? cmpl_ring_id : INVALID_HW_RING_ID); ring->fw_ring_id = INVALID_HW_RING_ID; - bp->grp_info[i].agg_fw_ring_id = INVALID_HW_RING_ID; + bp->grp_info[grp_idx].agg_fw_ring_id = + INVALID_HW_RING_ID; } } @@ -3859,7 +3873,7 @@ static int bnxt_alloc_rfs_vnics(struct bnxt *bp) break; bp->vnic_info[vnic_id].flags |= BNXT_VNIC_RFS_FLAG; - rc = bnxt_hwrm_vnic_alloc(bp, vnic_id, ring_id, ring_id + 1); + rc = bnxt_hwrm_vnic_alloc(bp, vnic_id, ring_id, 1); if (rc) { netdev_err(bp->dev, "hwrm vnic %d alloc failure rc: %x\n", vnic_id, rc); @@ -4165,7 +4179,7 @@ static void bnxt_free_irq(struct bnxt *bp) static int bnxt_request_irq(struct bnxt *bp) { - int i, rc = 0; + int i, j, rc = 0; unsigned long flags = 0; #ifdef CONFIG_RFS_ACCEL struct cpu_rmap *rmap = bp->dev->rx_cpu_rmap; @@ -4174,14 +4188,15 @@ static int bnxt_request_irq(struct bnxt *bp) if (!(bp->flags & BNXT_FLAG_USING_MSIX)) flags = IRQF_SHARED; - for (i = 0; i < bp->cp_nr_rings; i++) { + for (i = 0, j = 0; i < bp->cp_nr_rings; i++) { struct bnxt_irq *irq = &bp->irq_tbl[i]; #ifdef CONFIG_RFS_ACCEL - if (rmap && (i < bp->rx_nr_rings)) { + if (rmap && bp->bnapi[i]->rx_ring) { rc = irq_cpu_rmap_add(rmap, irq->vector); if (rc) netdev_warn(bp->dev, "failed adding irq rmap for ring %d\n", - i); + j); + j++; } #endif rc = request_irq(irq->vector, irq->handler, flags, irq->name, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index ea2fd927442b..ec3edcc3d2b6 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -918,6 +918,8 @@ struct bnxt { int cp_nr_rings; int num_stat_ctxs; + + /* grp_info indexed by completion ring index */ struct bnxt_ring_grp_info *grp_info; struct bnxt_vnic_info *vnic_info; int nr_vnics; -- cgit v1.2.3-59-g8ed1b From 6e6c5a57fbe1c77c2c55e266f87a83429adc3de7 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sat, 2 Jan 2016 23:45:02 -0500 Subject: bnxt_en: Modify bnxt_get_max_rings() to support shared or non shared rings. Add logic to calculate how many shared or non shared rings can be supported. Default is to use shared rings. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 96 +++++++++++++++++------ drivers/net/ethernet/broadcom/bnxt/bnxt.h | 4 +- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 4 +- 3 files changed, 79 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 8a54dabdcc7d..4f7b2316bb52 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4038,6 +4038,30 @@ static int bnxt_set_real_num_queues(struct bnxt *bp) return rc; } +static int bnxt_trim_rings(struct bnxt *bp, int *rx, int *tx, int max, + bool shared) +{ + int _rx = *rx, _tx = *tx; + + if (shared) { + *rx = min_t(int, _rx, max); + *tx = min_t(int, _tx, max); + } else { + if (max < 2) + return -ENOMEM; + + while (_rx + _tx > max) { + if (_rx > _tx && _rx > 1) + _rx--; + else if (_tx > 1) + _tx--; + } + *rx = _rx; + *tx = _tx; + } + return 0; +} + static int bnxt_setup_msix(struct bnxt *bp) { struct msix_entry *msix_ent; @@ -4068,8 +4092,11 @@ static int bnxt_setup_msix(struct bnxt *bp) int tcs; /* Trim rings based upon num of vectors allocated */ - bp->rx_nr_rings = min_t(int, total_vecs, bp->rx_nr_rings); - bp->tx_nr_rings = min_t(int, total_vecs, bp->tx_nr_rings); + rc = bnxt_trim_rings(bp, &bp->rx_nr_rings, &bp->tx_nr_rings, + total_vecs, true); + if (rc) + goto msix_setup_exit; + bp->tx_nr_rings_per_tc = bp->tx_nr_rings; tcs = netdev_get_num_tc(dev); if (tcs > 1) { @@ -5337,10 +5364,10 @@ static int bnxt_setup_tc(struct net_device *dev, u8 tc) return 0; if (tc) { - int max_rx_rings, max_tx_rings; + int max_rx_rings, max_tx_rings, rc; - bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings); - if (bp->tx_nr_rings_per_tc * tc > max_tx_rings) + rc = bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true); + if (rc || bp->tx_nr_rings_per_tc * tc > max_tx_rings) return -ENOMEM; } @@ -5654,31 +5681,62 @@ static int bnxt_get_max_irq(struct pci_dev *pdev) return (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1; } -void bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx) +static void _bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx, + int *max_cp) { - int max_rings = 0, max_ring_grps = 0; + int max_ring_grps = 0; if (BNXT_PF(bp)) { *max_tx = bp->pf.max_tx_rings; *max_rx = bp->pf.max_rx_rings; - max_rings = min_t(int, bp->pf.max_irqs, bp->pf.max_cp_rings); - max_rings = min_t(int, max_rings, bp->pf.max_stat_ctxs); + *max_cp = min_t(int, bp->pf.max_irqs, bp->pf.max_cp_rings); + *max_cp = min_t(int, *max_cp, bp->pf.max_stat_ctxs); max_ring_grps = bp->pf.max_hw_ring_grps; } else { #ifdef CONFIG_BNXT_SRIOV *max_tx = bp->vf.max_tx_rings; *max_rx = bp->vf.max_rx_rings; - max_rings = min_t(int, bp->vf.max_irqs, bp->vf.max_cp_rings); - max_rings = min_t(int, max_rings, bp->vf.max_stat_ctxs); + *max_cp = min_t(int, bp->vf.max_irqs, bp->vf.max_cp_rings); + *max_cp = min_t(int, *max_cp, bp->vf.max_stat_ctxs); max_ring_grps = bp->vf.max_hw_ring_grps; #endif } if (bp->flags & BNXT_FLAG_AGG_RINGS) *max_rx >>= 1; - - *max_rx = min_t(int, *max_rx, max_rings); *max_rx = min_t(int, *max_rx, max_ring_grps); - *max_tx = min_t(int, *max_tx, max_rings); +} + +int bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx, bool shared) +{ + int rx, tx, cp; + + _bnxt_get_max_rings(bp, &rx, &tx, &cp); + if (!rx || !tx || !cp) + return -ENOMEM; + + *max_rx = rx; + *max_tx = tx; + return bnxt_trim_rings(bp, max_rx, max_tx, cp, shared); +} + +static int bnxt_set_dflt_rings(struct bnxt *bp) +{ + int dflt_rings, max_rx_rings, max_tx_rings, rc; + bool sh = true; + + if (sh) + bp->flags |= BNXT_FLAG_SHARED_RINGS; + dflt_rings = netif_get_num_default_rss_queues(); + rc = bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, sh); + if (rc) + return rc; + bp->rx_nr_rings = min_t(int, dflt_rings, max_rx_rings); + bp->tx_nr_rings_per_tc = min_t(int, dflt_rings, max_tx_rings); + bp->tx_nr_rings = bp->tx_nr_rings_per_tc; + bp->cp_nr_rings = sh ? max_t(int, bp->tx_nr_rings, bp->rx_nr_rings) : + bp->tx_nr_rings + bp->rx_nr_rings; + bp->num_stat_ctxs = bp->cp_nr_rings; + return rc; } static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -5686,7 +5744,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) static int version_printed; struct net_device *dev; struct bnxt *bp; - int rc, max_rx_rings, max_tx_rings, max_irqs, dflt_rings; + int rc, max_irqs; if (version_printed++ == 0) pr_info("%s", version); @@ -5765,19 +5823,13 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) bnxt_set_tpa_flags(bp); bnxt_set_ring_params(bp); - dflt_rings = netif_get_num_default_rss_queues(); if (BNXT_PF(bp)) bp->pf.max_irqs = max_irqs; #if defined(CONFIG_BNXT_SRIOV) else bp->vf.max_irqs = max_irqs; #endif - bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings); - bp->rx_nr_rings = min_t(int, dflt_rings, max_rx_rings); - bp->tx_nr_rings_per_tc = min_t(int, dflt_rings, max_tx_rings); - bp->tx_nr_rings = bp->tx_nr_rings_per_tc; - bp->cp_nr_rings = max_t(int, bp->rx_nr_rings, bp->tx_nr_rings); - bp->num_stat_ctxs = bp->cp_nr_rings; + bnxt_set_dflt_rings(bp); if (BNXT_PF(bp)) { dev->hw_features |= NETIF_F_NTUPLE; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index ec3edcc3d2b6..8af3ca8efcef 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -877,6 +877,8 @@ struct bnxt { #define BNXT_FLAG_USING_MSIX 0x40 #define BNXT_FLAG_MSIX_CAP 0x80 #define BNXT_FLAG_RFS 0x100 + #define BNXT_FLAG_SHARED_RINGS 0x200 + #define BNXT_FLAG_ALL_CONFIG_FEATS (BNXT_FLAG_TPA | \ BNXT_FLAG_RFS | \ BNXT_FLAG_STRIP_VLAN) @@ -1096,5 +1098,5 @@ int bnxt_hwrm_set_pause(struct bnxt *); int bnxt_hwrm_set_link_setting(struct bnxt *, bool); int bnxt_open_nic(struct bnxt *, bool, bool); int bnxt_close_nic(struct bnxt *, bool, bool); -void bnxt_get_max_rings(struct bnxt *, int *, int *); +int bnxt_get_max_rings(struct bnxt *, int *, int *, bool); #endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index a39511f5be75..8ad1b6ca7211 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -211,7 +211,7 @@ static void bnxt_get_channels(struct net_device *dev, struct bnxt *bp = netdev_priv(dev); int max_rx_rings, max_tx_rings, tcs; - bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings); + bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true); tcs = netdev_get_num_tc(dev); if (tcs > 1) max_tx_rings /= tcs; @@ -235,7 +235,7 @@ static int bnxt_set_channels(struct net_device *dev, !channel->rx_count || !channel->tx_count) return -EINVAL; - bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings); + bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true); tcs = netdev_get_num_tc(dev); if (tcs > 1) max_tx_rings /= tcs; -- cgit v1.2.3-59-g8ed1b From 01657bcd078b924e4599a83acd402ea6f85a1e45 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sat, 2 Jan 2016 23:45:03 -0500 Subject: bnxt_en: Modify init sequence to support shared or non shared rings. Modify ring memory allocation and MSIX setup to support shared or non shared rings and do the proper mapping. Default is still to use shared rings. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 42 +++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 4f7b2316bb52..f9569493034b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -2496,7 +2496,7 @@ static void bnxt_free_mem(struct bnxt *bp, bool irq_re_init) static int bnxt_alloc_mem(struct bnxt *bp, bool irq_re_init) { - int i, rc, size, arr_size; + int i, j, rc, size, arr_size; void *bnapi; if (irq_re_init) { @@ -2535,9 +2535,14 @@ static int bnxt_alloc_mem(struct bnxt *bp, bool irq_re_init) if (!bp->tx_ring) return -ENOMEM; - for (i = 0; i < bp->tx_nr_rings; i++) { - bp->tx_ring[i].bnapi = bp->bnapi[i]; - bp->bnapi[i]->tx_ring = &bp->tx_ring[i]; + if (bp->flags & BNXT_FLAG_SHARED_RINGS) + j = 0; + else + j = bp->rx_nr_rings; + + for (i = 0; i < bp->tx_nr_rings; i++, j++) { + bp->tx_ring[i].bnapi = bp->bnapi[j]; + bp->bnapi[j]->tx_ring = &bp->tx_ring[i]; } rc = bnxt_alloc_stats(bp); @@ -4066,7 +4071,7 @@ static int bnxt_setup_msix(struct bnxt *bp) { struct msix_entry *msix_ent; struct net_device *dev = bp->dev; - int i, total_vecs, rc = 0; + int i, total_vecs, rc = 0, min = 1; const int len = sizeof(bp->irq_tbl[0].name); bp->flags &= ~BNXT_FLAG_USING_MSIX; @@ -4081,7 +4086,10 @@ static int bnxt_setup_msix(struct bnxt *bp) msix_ent[i].vector = 0; } - total_vecs = pci_enable_msix_range(bp->pdev, msix_ent, 1, total_vecs); + if (!(bp->flags & BNXT_FLAG_SHARED_RINGS)) + min = 2; + + total_vecs = pci_enable_msix_range(bp->pdev, msix_ent, min, total_vecs); if (total_vecs < 0) { rc = -ENODEV; goto msix_setup_exit; @@ -4093,7 +4101,7 @@ static int bnxt_setup_msix(struct bnxt *bp) /* Trim rings based upon num of vectors allocated */ rc = bnxt_trim_rings(bp, &bp->rx_nr_rings, &bp->tx_nr_rings, - total_vecs, true); + total_vecs, min == 1); if (rc) goto msix_setup_exit; @@ -4115,12 +4123,21 @@ static int bnxt_setup_msix(struct bnxt *bp) } } } - bp->cp_nr_rings = max_t(int, bp->rx_nr_rings, bp->tx_nr_rings); + bp->cp_nr_rings = total_vecs; for (i = 0; i < bp->cp_nr_rings; i++) { + char *attr; + bp->irq_tbl[i].vector = msix_ent[i].vector; + if (bp->flags & BNXT_FLAG_SHARED_RINGS) + attr = "TxRx"; + else if (i < bp->rx_nr_rings) + attr = "rx"; + else + attr = "tx"; + snprintf(bp->irq_tbl[i].name, len, - "%s-%s-%d", dev->name, "TxRx", i); + "%s-%s-%d", dev->name, attr, i); bp->irq_tbl[i].handler = bnxt_msix; } rc = bnxt_set_real_num_queues(bp); @@ -4158,6 +4175,7 @@ static int bnxt_setup_inta(struct bnxt *bp) bp->tx_nr_rings = 1; bp->cp_nr_rings = 1; bp->tx_nr_rings_per_tc = bp->tx_nr_rings; + bp->flags |= BNXT_FLAG_SHARED_RINGS; bp->irq_tbl[0].vector = bp->pdev->irq; snprintf(bp->irq_tbl[0].name, len, "%s-%s-%d", bp->dev->name, "TxRx", 0); @@ -5365,8 +5383,12 @@ static int bnxt_setup_tc(struct net_device *dev, u8 tc) if (tc) { int max_rx_rings, max_tx_rings, rc; + bool sh = false; + + if (bp->flags & BNXT_FLAG_SHARED_RINGS) + sh = true; - rc = bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true); + rc = bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, sh); if (rc || bp->tx_nr_rings_per_tc * tc > max_tx_rings) return -ENOMEM; } -- cgit v1.2.3-59-g8ed1b From 068c9ec62906b626a30526638fd36189b80b6464 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sat, 2 Jan 2016 23:45:04 -0500 Subject: bnxt_en: Modify ethtool -l|-L to support combined or rx/tx rings. The driver can support either all combined or all rx/tx rings. The default is combined, but the user can now select rx/tx rings. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 57 ++++++++++++++++++----- 1 file changed, 45 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 8ad1b6ca7211..4f62c9fa96d5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -212,6 +212,9 @@ static void bnxt_get_channels(struct net_device *dev, int max_rx_rings, max_tx_rings, tcs; bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true); + channel->max_combined = max_rx_rings; + + bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, false); tcs = netdev_get_num_tc(dev); if (tcs > 1) max_tx_rings /= tcs; @@ -219,9 +222,12 @@ static void bnxt_get_channels(struct net_device *dev, channel->max_rx = max_rx_rings; channel->max_tx = max_tx_rings; channel->max_other = 0; - channel->max_combined = 0; - channel->rx_count = bp->rx_nr_rings; - channel->tx_count = bp->tx_nr_rings_per_tc; + if (bp->flags & BNXT_FLAG_SHARED_RINGS) { + channel->combined_count = bp->rx_nr_rings; + } else { + channel->rx_count = bp->rx_nr_rings; + channel->tx_count = bp->tx_nr_rings_per_tc; + } } static int bnxt_set_channels(struct net_device *dev, @@ -230,19 +236,35 @@ static int bnxt_set_channels(struct net_device *dev, struct bnxt *bp = netdev_priv(dev); int max_rx_rings, max_tx_rings, tcs; u32 rc = 0; + bool sh = false; - if (channel->other_count || channel->combined_count || - !channel->rx_count || !channel->tx_count) + if (channel->other_count) return -EINVAL; - bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true); + if (!channel->combined_count && + (!channel->rx_count || !channel->tx_count)) + return -EINVAL; + + if (channel->combined_count && + (channel->rx_count || channel->tx_count)) + return -EINVAL; + + if (channel->combined_count) + sh = true; + + bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, sh); + tcs = netdev_get_num_tc(dev); if (tcs > 1) max_tx_rings /= tcs; - if (channel->rx_count > max_rx_rings || - channel->tx_count > max_tx_rings) - return -EINVAL; + if (sh && (channel->combined_count > max_rx_rings || + channel->combined_count > max_tx_rings)) + return -ENOMEM; + + if (!sh && (channel->rx_count > max_rx_rings || + channel->tx_count > max_tx_rings)) + return -ENOMEM; if (netif_running(dev)) { if (BNXT_PF(bp)) { @@ -258,12 +280,23 @@ static int bnxt_set_channels(struct net_device *dev, } } - bp->rx_nr_rings = channel->rx_count; - bp->tx_nr_rings_per_tc = channel->tx_count; + if (sh) { + bp->flags |= BNXT_FLAG_SHARED_RINGS; + bp->rx_nr_rings = channel->combined_count; + bp->tx_nr_rings_per_tc = channel->combined_count; + } else { + bp->flags &= ~BNXT_FLAG_SHARED_RINGS; + bp->rx_nr_rings = channel->rx_count; + bp->tx_nr_rings_per_tc = channel->tx_count; + } + bp->tx_nr_rings = bp->tx_nr_rings_per_tc; if (tcs > 1) bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tcs; - bp->cp_nr_rings = max_t(int, bp->tx_nr_rings, bp->rx_nr_rings); + + bp->cp_nr_rings = sh ? max_t(int, bp->tx_nr_rings, bp->rx_nr_rings) : + bp->tx_nr_rings + bp->rx_nr_rings; + bp->num_stat_ctxs = bp->cp_nr_rings; /* After changing number of rx channels, update NTUPLE feature. */ -- cgit v1.2.3-59-g8ed1b From 6e898bfd6e9e5cd0f7f8275ad4dc41a3acf7c75a Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Fri, 1 Jan 2016 23:48:57 +0800 Subject: tilepro: use to_delayed_work Use to_delayed_work() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: David S. Miller --- drivers/net/ethernet/tile/tilepro.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c index 6f0a4495c7f3..298e059d0498 100644 --- a/drivers/net/ethernet/tile/tilepro.c +++ b/drivers/net/ethernet/tile/tilepro.c @@ -1349,8 +1349,7 @@ static int tile_net_open_inner(struct net_device *dev) */ static void tile_net_open_retry(struct work_struct *w) { - struct delayed_work *dw = - container_of(w, struct delayed_work, work); + struct delayed_work *dw = to_delayed_work(w); struct tile_net_priv *priv = container_of(dw, struct tile_net_priv, retry_work); -- cgit v1.2.3-59-g8ed1b From 888cc8c20cf265fcd1302f6c5d6be07628ba66c7 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 28 Dec 2015 02:07:08 +0300 Subject: sh_eth: remove EDMAC_BIG_ENDIAN Commit 71557a37adb5 ("[netdrvr] sh_eth: Add SH7619 support") added support for the big-endian EDMAC descriptors. However, it was never used and never worked right until the recent driver fixes. I think we now can just remove this support, it was only burdening the driver from the start. It should be easy to do without disturbing the SH platform code, at least for now... Signed-off-by: Sergei Shtylyov Acked-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 18 ++---------------- drivers/net/ethernet/renesas/sh_eth.h | 1 - include/linux/sh_eth.h | 2 +- 3 files changed, 3 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 8ca040113a09..2c7dd8a68d84 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -971,24 +971,12 @@ static void sh_eth_set_receive_align(struct sk_buff *skb) /* CPU <-> EDMAC endian convert */ static inline __u32 cpu_to_edmac(struct sh_eth_private *mdp, u32 x) { - switch (mdp->edmac_endian) { - case EDMAC_LITTLE_ENDIAN: - return cpu_to_le32(x); - case EDMAC_BIG_ENDIAN: - return cpu_to_be32(x); - } - return x; + return cpu_to_le32(x); } static inline __u32 edmac_to_cpu(struct sh_eth_private *mdp, u32 x) { - switch (mdp->edmac_endian) { - case EDMAC_LITTLE_ENDIAN: - return le32_to_cpu(x); - case EDMAC_BIG_ENDIAN: - return be32_to_cpu(x); - } - return x; + return le32_to_cpu(x); } /* Program the hardware MAC address from dev->dev_addr. */ @@ -3097,8 +3085,6 @@ static int sh_eth_drv_probe(struct platform_device *pdev) /* get PHY ID */ mdp->phy_id = pd->phy; mdp->phy_interface = pd->phy_interface; - /* EDMAC endian */ - mdp->edmac_endian = pd->edmac_endian; mdp->no_ether_link = pd->no_ether_link; mdp->ether_link_active_low = pd->ether_link_active_low; diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 72fcfc924589..8fa4ef3a7fdd 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -513,7 +513,6 @@ struct sh_eth_private { u32 cur_rx, dirty_rx; /* Producer/consumer ring indices */ u32 cur_tx, dirty_tx; u32 rx_buf_sz; /* Based on MTU+slack. */ - int edmac_endian; struct napi_struct napi; bool irq_enabled; /* MII transceiver section. */ diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h index 8c9131db2b25..f2e27e078362 100644 --- a/include/linux/sh_eth.h +++ b/include/linux/sh_eth.h @@ -4,7 +4,7 @@ #include #include -enum {EDMAC_LITTLE_ENDIAN, EDMAC_BIG_ENDIAN}; +enum {EDMAC_LITTLE_ENDIAN}; struct sh_eth_plat_data { int phy; -- cgit v1.2.3-59-g8ed1b From 7cf724770b78c1d4f24e23747ac03233a88af440 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 28 Dec 2015 02:10:47 +0300 Subject: sh_eth: get rid of {cpu|edmac}_to_{edmac|cpu}() Now that {cpu|edmac}_to_{edmac|cpu}() functions boiled down to the mere {cpu|le32}_to_{le32|cpu}() calls, there's no need for these functions anymore, so just get rid of them. Signed-off-by: Sergei Shtylyov Acked-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 72 ++++++++++++++--------------------- 1 file changed, 29 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 2c7dd8a68d84..baa81535a8fc 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -967,18 +967,6 @@ static void sh_eth_set_receive_align(struct sk_buff *skb) skb_reserve(skb, SH_ETH_RX_ALIGN - reserve); } - -/* CPU <-> EDMAC endian convert */ -static inline __u32 cpu_to_edmac(struct sh_eth_private *mdp, u32 x) -{ - return cpu_to_le32(x); -} - -static inline __u32 edmac_to_cpu(struct sh_eth_private *mdp, u32 x) -{ - return le32_to_cpu(x); -} - /* Program the hardware MAC address from dev->dev_addr. */ static void update_mac_address(struct net_device *ndev) { @@ -1152,7 +1140,7 @@ static void sh_eth_ring_format(struct net_device *ndev) rxdesc = &mdp->rx_ring[i]; /* The size of the buffer is a multiple of 32 bytes. */ buf_len = ALIGN(mdp->rx_buf_sz, 32); - rxdesc->len = cpu_to_edmac(mdp, buf_len << 16); + rxdesc->len = cpu_to_le32(buf_len << 16); dma_addr = dma_map_single(&ndev->dev, skb->data, buf_len, DMA_FROM_DEVICE); if (dma_mapping_error(&ndev->dev, dma_addr)) { @@ -1160,8 +1148,8 @@ static void sh_eth_ring_format(struct net_device *ndev) break; } mdp->rx_skbuff[i] = skb; - rxdesc->addr = cpu_to_edmac(mdp, dma_addr); - rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP); + rxdesc->addr = cpu_to_le32(dma_addr); + rxdesc->status = cpu_to_le32(RD_RACT | RD_RFP); /* Rx descriptor address set */ if (i == 0) { @@ -1175,7 +1163,7 @@ static void sh_eth_ring_format(struct net_device *ndev) mdp->dirty_rx = (u32) (i - mdp->num_rx_ring); /* Mark the last entry as wrapping the ring. */ - rxdesc->status |= cpu_to_edmac(mdp, RD_RDLE); + rxdesc->status |= cpu_to_le32(RD_RDLE); memset(mdp->tx_ring, 0, tx_ringsize); @@ -1183,8 +1171,8 @@ static void sh_eth_ring_format(struct net_device *ndev) for (i = 0; i < mdp->num_tx_ring; i++) { mdp->tx_skbuff[i] = NULL; txdesc = &mdp->tx_ring[i]; - txdesc->status = cpu_to_edmac(mdp, TD_TFP); - txdesc->len = cpu_to_edmac(mdp, 0); + txdesc->status = cpu_to_le32(TD_TFP); + txdesc->len = cpu_to_le32(0); if (i == 0) { /* Tx descriptor address set */ sh_eth_write(ndev, mdp->tx_desc_dma, TDLAR); @@ -1194,7 +1182,7 @@ static void sh_eth_ring_format(struct net_device *ndev) } } - txdesc->status |= cpu_to_edmac(mdp, TD_TDLE); + txdesc->status |= cpu_to_le32(TD_TDLE); } /* Get skb and descriptor buffer */ @@ -1350,7 +1338,7 @@ static void sh_eth_dev_exit(struct net_device *ndev) * packet boundary if it's currently running */ for (i = 0; i < mdp->num_tx_ring; i++) - mdp->tx_ring[i].status &= ~cpu_to_edmac(mdp, TD_TACT); + mdp->tx_ring[i].status &= ~cpu_to_le32(TD_TACT); /* Disable TX FIFO egress to MAC */ sh_eth_rcv_snd_disable(ndev); @@ -1382,29 +1370,28 @@ static int sh_eth_txfree(struct net_device *ndev) for (; mdp->cur_tx - mdp->dirty_tx > 0; mdp->dirty_tx++) { entry = mdp->dirty_tx % mdp->num_tx_ring; txdesc = &mdp->tx_ring[entry]; - if (txdesc->status & cpu_to_edmac(mdp, TD_TACT)) + if (txdesc->status & cpu_to_le32(TD_TACT)) break; /* TACT bit must be checked before all the following reads */ dma_rmb(); netif_info(mdp, tx_done, ndev, "tx entry %d status 0x%08x\n", - entry, edmac_to_cpu(mdp, txdesc->status)); + entry, le32_to_cpu(txdesc->status)); /* Free the original skb. */ if (mdp->tx_skbuff[entry]) { - dma_unmap_single(&ndev->dev, - edmac_to_cpu(mdp, txdesc->addr), - edmac_to_cpu(mdp, txdesc->len) >> 16, + dma_unmap_single(&ndev->dev, le32_to_cpu(txdesc->addr), + le32_to_cpu(txdesc->len) >> 16, DMA_TO_DEVICE); dev_kfree_skb_irq(mdp->tx_skbuff[entry]); mdp->tx_skbuff[entry] = NULL; free_num++; } - txdesc->status = cpu_to_edmac(mdp, TD_TFP); + txdesc->status = cpu_to_le32(TD_TFP); if (entry >= mdp->num_tx_ring - 1) - txdesc->status |= cpu_to_edmac(mdp, TD_TDLE); + txdesc->status |= cpu_to_le32(TD_TDLE); ndev->stats.tx_packets++; - ndev->stats.tx_bytes += edmac_to_cpu(mdp, txdesc->len) >> 16; + ndev->stats.tx_bytes += le32_to_cpu(txdesc->len) >> 16; } return free_num; } @@ -1428,11 +1415,11 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) boguscnt = min(boguscnt, *quota); limit = boguscnt; rxdesc = &mdp->rx_ring[entry]; - while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) { + while (!(rxdesc->status & cpu_to_le32(RD_RACT))) { /* RACT bit must be checked before all the following reads */ dma_rmb(); - desc_status = edmac_to_cpu(mdp, rxdesc->status); - pkt_len = edmac_to_cpu(mdp, rxdesc->len) & RD_RFL; + desc_status = le32_to_cpu(rxdesc->status); + pkt_len = le32_to_cpu(rxdesc->len) & RD_RFL; if (--boguscnt < 0) break; @@ -1470,7 +1457,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) if (desc_status & RD_RFS10) ndev->stats.rx_over_errors++; } else if (skb) { - dma_addr = edmac_to_cpu(mdp, rxdesc->addr); + dma_addr = le32_to_cpu(rxdesc->addr); if (!mdp->cd->hw_swap) sh_eth_soft_swap( phys_to_virt(ALIGN(dma_addr, 4)), @@ -1499,7 +1486,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) rxdesc = &mdp->rx_ring[entry]; /* The size of the buffer is 32 byte boundary. */ buf_len = ALIGN(mdp->rx_buf_sz, 32); - rxdesc->len = cpu_to_edmac(mdp, buf_len << 16); + rxdesc->len = cpu_to_le32(buf_len << 16); if (mdp->rx_skbuff[entry] == NULL) { skb = netdev_alloc_skb(ndev, skbuff_size); @@ -1515,15 +1502,14 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) mdp->rx_skbuff[entry] = skb; skb_checksum_none_assert(skb); - rxdesc->addr = cpu_to_edmac(mdp, dma_addr); + rxdesc->addr = cpu_to_le32(dma_addr); } dma_wmb(); /* RACT bit must be set after all the above writes */ if (entry >= mdp->num_rx_ring - 1) rxdesc->status |= - cpu_to_edmac(mdp, RD_RACT | RD_RFP | RD_RDLE); + cpu_to_le32(RD_RACT | RD_RFP | RD_RDLE); else - rxdesc->status |= - cpu_to_edmac(mdp, RD_RACT | RD_RFP); + rxdesc->status |= cpu_to_le32(RD_RACT | RD_RFP); } /* Restart Rx engine if stopped. */ @@ -2323,8 +2309,8 @@ static void sh_eth_tx_timeout(struct net_device *ndev) /* Free all the skbuffs in the Rx queue. */ for (i = 0; i < mdp->num_rx_ring; i++) { rxdesc = &mdp->rx_ring[i]; - rxdesc->status = cpu_to_edmac(mdp, 0); - rxdesc->addr = cpu_to_edmac(mdp, 0xBADF00D0); + rxdesc->status = cpu_to_le32(0); + rxdesc->addr = cpu_to_le32(0xBADF00D0); dev_kfree_skb(mdp->rx_skbuff[i]); mdp->rx_skbuff[i] = NULL; } @@ -2372,14 +2358,14 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) kfree_skb(skb); return NETDEV_TX_OK; } - txdesc->addr = cpu_to_edmac(mdp, dma_addr); - txdesc->len = cpu_to_edmac(mdp, skb->len << 16); + txdesc->addr = cpu_to_le32(dma_addr); + txdesc->len = cpu_to_le32(skb->len << 16); dma_wmb(); /* TACT bit must be set after all the above writes */ if (entry >= mdp->num_tx_ring - 1) - txdesc->status |= cpu_to_edmac(mdp, TD_TACT | TD_TDLE); + txdesc->status |= cpu_to_le32(TD_TACT | TD_TDLE); else - txdesc->status |= cpu_to_edmac(mdp, TD_TACT); + txdesc->status |= cpu_to_le32(TD_TACT); mdp->cur_tx++; -- cgit v1.2.3-59-g8ed1b From 69f3dc379bef56f9fe3b815cd41a7ae34e2c9047 Mon Sep 17 00:00:00 2001 From: Chun-Hao Lin Date: Tue, 29 Dec 2015 22:13:37 +0800 Subject: r8169:Fix typo in setting RTL8168EP and RTL8168H D3cold PFM mode The register for setting D3code PFM mode is MISC_1, not DLLPR. Signed-off-by: Chunhao Lin Reviewed-by: Francois Romieu Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 58365bcf2370..0decc1b7bafd 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -6127,7 +6127,7 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp) RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07); RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN); - RTL_W8(DLLPR, RTL_R8(MISC_1) & ~PFM_D3COLD_EN); + RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN); RTL_W8(DLLPR, RTL_R8(DLLPR) & ~TX_10M_PS_EN); @@ -6252,7 +6252,7 @@ static void rtl_hw_start_8168ep_2(struct rtl8169_private *tp) rtl_hw_start_8168ep(tp); RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN); - RTL_W8(DLLPR, RTL_R8(MISC_1) & ~PFM_D3COLD_EN); + RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN); } static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp) @@ -6274,7 +6274,7 @@ static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp) rtl_hw_start_8168ep(tp); RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN); - RTL_W8(DLLPR, RTL_R8(MISC_1) & ~PFM_D3COLD_EN); + RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN); data = r8168_mac_ocp_read(tp, 0xd3e2); data &= 0xf000; -- cgit v1.2.3-59-g8ed1b From c832c35f5f27f38b65d7d4d55906a3365ba6f33a Mon Sep 17 00:00:00 2001 From: Chun-Hao Lin Date: Tue, 29 Dec 2015 22:13:38 +0800 Subject: r8169:Fix typo in setting RTL8168H PHY PFM mode. The PHY PFM register is in PHY page 0x0a44 register 0x11, not 0x14. Signed-off-by: Chunhao Lin Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 0decc1b7bafd..629f5e5ab576 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -3894,7 +3894,7 @@ static void rtl8168h_1_hw_phy_config(struct rtl8169_private *tp) /* disable phy pfm mode */ rtl_writephy(tp, 0x1f, 0x0a44); - rtl_w0w1_phy(tp, 0x14, 0x0000, 0x0080); + rtl_w0w1_phy(tp, 0x11, 0x0000, 0x0080); rtl_writephy(tp, 0x1f, 0x0000); /* Check ALDPS bit, disable it if enabled */ @@ -3967,7 +3967,7 @@ static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp) /* disable phy pfm mode */ rtl_writephy(tp, 0x1f, 0x0a44); - rtl_w0w1_phy(tp, 0x14, 0x0000, 0x0080); + rtl_w0w1_phy(tp, 0x11, 0x0000, 0x0080); rtl_writephy(tp, 0x1f, 0x0000); /* Check ALDPS bit, disable it if enabled */ -- cgit v1.2.3-59-g8ed1b From 1016a4a1f4018c9a1db411b38f46bd00b96f4e83 Mon Sep 17 00:00:00 2001 From: Chun-Hao Lin Date: Tue, 29 Dec 2015 22:13:39 +0800 Subject: r8169:Correct the way of setting RTL8168DP ephy The original way is wrong, it always writes ephy reg 0x03. Signed-off-by: Chunhao Lin Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 629f5e5ab576..17d5571d0432 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -5818,11 +5818,10 @@ static void rtl_hw_start_8168d_4(struct rtl8169_private *tp) void __iomem *ioaddr = tp->mmio_addr; struct pci_dev *pdev = tp->pci_dev; static const struct ephy_info e_info_8168d_4[] = { - { 0x0b, ~0, 0x48 }, - { 0x19, 0x20, 0x50 }, - { 0x0c, ~0, 0x20 } + { 0x0b, 0x0000, 0x0048 }, + { 0x19, 0x0020, 0x0050 }, + { 0x0c, 0x0100, 0x0020 } }; - int i; rtl_csi_access_enable_1(tp); @@ -5830,13 +5829,7 @@ static void rtl_hw_start_8168d_4(struct rtl8169_private *tp) RTL_W8(MaxTxPacketSize, TxPacketMax); - for (i = 0; i < ARRAY_SIZE(e_info_8168d_4); i++) { - const struct ephy_info *e = e_info_8168d_4 + i; - u16 w; - - w = rtl_ephy_read(tp, e->offset); - rtl_ephy_write(tp, 0x03, (w & e->mask) | e->bits); - } + rtl_ephy_init(tp, e_info_8168d_4, ARRAY_SIZE(e_info_8168d_4)); rtl_enable_clock_request(pdev); } -- cgit v1.2.3-59-g8ed1b From 46678612e13027b0ea901664504f3bf347756125 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 1 Jan 2016 14:55:24 +0100 Subject: fsl/fman: allow modular build ARM allmodconfig fails because of the addition of the FMAN driver: drivers/built-in.o: In function `dtsec_restart_autoneg': binder.c:(.text+0x173328): undefined reference to `mdiobus_read' binder.c:(.text+0x173348): undefined reference to `mdiobus_write' drivers/built-in.o: In function `dtsec_config': binder.c:(.text+0x173d24): undefined reference to `of_phy_find_device' drivers/built-in.o: In function `init_phy': binder.c:(.text+0x1763b0): undefined reference to `of_phy_connect' drivers/built-in.o: In function `stop': binder.c:(.text+0x176014): undefined reference to `phy_stop' drivers/built-in.o: In function `start': binder.c:(.text+0x176078): undefined reference to `phy_start' The reason is that the driver uses PHYLIB, but that is a loadable module here, and fman itself is built-in. This patch makes it possible to configure fman as a module as well so we don't change the status of PHYLIB in an allmodconfig kernel, and it adds a 'select PHYLIB' statement to ensure that phylib is always built-in when fman is. The driver uses "builtin_platform_driver(fman_driver);", which means it cannot be unloaded, but it's still possible to have it as a loadable module that gets loaded once and never removed. Signed-off-by: Arnd Bergmann Fixes: 5adae51a64b8 ("fsl/fman: Add FMan MURAM support") Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fman/Kconfig b/drivers/net/ethernet/freescale/fman/Kconfig index 66b729692b48..79b7c84b7869 100644 --- a/drivers/net/ethernet/freescale/fman/Kconfig +++ b/drivers/net/ethernet/freescale/fman/Kconfig @@ -1,7 +1,8 @@ config FSL_FMAN - bool "FMan support" + tristate "FMan support" depends on FSL_SOC || COMPILE_TEST select GENERIC_ALLOCATOR + select PHYLIB default n help Freescale Data-Path Acceleration Architecture Frame Manager -- cgit v1.2.3-59-g8ed1b From 46f85a9215d7352d886626e29c3fc3095bc5bec7 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 3 Jan 2016 14:09:37 +0100 Subject: chelsio: constify cphy_ops structures The cphy_ops structures are never modified, so declare them as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb/cphy.h | 2 +- drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c | 2 +- drivers/net/ethernet/chelsio/cxgb/mv88x201x.c | 2 +- drivers/net/ethernet/chelsio/cxgb/my3126.c | 2 +- drivers/net/ethernet/chelsio/cxgb3/ael1002.c | 12 ++++++------ drivers/net/ethernet/chelsio/cxgb3/aq100x.c | 2 +- drivers/net/ethernet/chelsio/cxgb3/common.h | 2 +- drivers/net/ethernet/chelsio/cxgb3/vsc8211.c | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb/cphy.h b/drivers/net/ethernet/chelsio/cxgb/cphy.h index a4d2a4c08d3f..bf43da6c6a63 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cphy.h +++ b/drivers/net/ethernet/chelsio/cxgb/cphy.h @@ -137,7 +137,7 @@ static inline int simple_mdio_write(struct cphy *cphy, int reg, /* Convenience initializer */ static inline void cphy_init(struct cphy *phy, struct net_device *dev, - int phy_addr, struct cphy_ops *phy_ops, + int phy_addr, const struct cphy_ops *phy_ops, const struct mdio_ops *mdio_ops) { struct adapter *adapter = netdev_priv(dev); diff --git a/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c b/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c index 71018a4fdf15..76ce6e538326 100644 --- a/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c +++ b/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c @@ -337,7 +337,7 @@ static void mv88e1xxx_destroy(struct cphy *cphy) kfree(cphy); } -static struct cphy_ops mv88e1xxx_ops = { +static const struct cphy_ops mv88e1xxx_ops = { .destroy = mv88e1xxx_destroy, .reset = mv88e1xxx_reset, .interrupt_enable = mv88e1xxx_interrupt_enable, diff --git a/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c b/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c index d0cf611551a1..7ddb301bcba0 100644 --- a/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c +++ b/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c @@ -195,7 +195,7 @@ static void mv88x201x_destroy(struct cphy *cphy) kfree(cphy); } -static struct cphy_ops mv88x201x_ops = { +static const struct cphy_ops mv88x201x_ops = { .destroy = mv88x201x_destroy, .reset = mv88x201x_reset, .interrupt_enable = mv88x201x_interrupt_enable, diff --git a/drivers/net/ethernet/chelsio/cxgb/my3126.c b/drivers/net/ethernet/chelsio/cxgb/my3126.c index a683fd3bb624..d546f46c8ef7 100644 --- a/drivers/net/ethernet/chelsio/cxgb/my3126.c +++ b/drivers/net/ethernet/chelsio/cxgb/my3126.c @@ -154,7 +154,7 @@ static void my3126_destroy(struct cphy *cphy) kfree(cphy); } -static struct cphy_ops my3126_ops = { +static const struct cphy_ops my3126_ops = { .destroy = my3126_destroy, .reset = my3126_reset, .interrupt_enable = my3126_interrupt_enable, diff --git a/drivers/net/ethernet/chelsio/cxgb3/ael1002.c b/drivers/net/ethernet/chelsio/cxgb3/ael1002.c index 2028da95afa1..dadf11e3dddb 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/ael1002.c +++ b/drivers/net/ethernet/chelsio/cxgb3/ael1002.c @@ -198,7 +198,7 @@ static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed, return 0; } -static struct cphy_ops ael1002_ops = { +static const struct cphy_ops ael1002_ops = { .reset = ael1002_reset, .intr_enable = ael1002_intr_noop, .intr_disable = ael1002_intr_noop, @@ -224,7 +224,7 @@ static int ael1006_reset(struct cphy *phy, int wait) return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait); } -static struct cphy_ops ael1006_ops = { +static const struct cphy_ops ael1006_ops = { .reset = ael1006_reset, .intr_enable = t3_phy_lasi_intr_enable, .intr_disable = t3_phy_lasi_intr_disable, @@ -495,7 +495,7 @@ static int ael2005_intr_handler(struct cphy *phy) return ret ? ret : cphy_cause_link_change; } -static struct cphy_ops ael2005_ops = { +static const struct cphy_ops ael2005_ops = { .reset = ael2005_reset, .intr_enable = ael2005_intr_enable, .intr_disable = ael2005_intr_disable, @@ -801,7 +801,7 @@ static int ael2020_intr_handler(struct cphy *phy) return ret ? ret : cphy_cause_link_change; } -static struct cphy_ops ael2020_ops = { +static const struct cphy_ops ael2020_ops = { .reset = ael2020_reset, .intr_enable = ael2020_intr_enable, .intr_disable = ael2020_intr_disable, @@ -856,7 +856,7 @@ static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed, return 0; } -static struct cphy_ops qt2045_ops = { +static const struct cphy_ops qt2045_ops = { .reset = ael1006_reset, .intr_enable = t3_phy_lasi_intr_enable, .intr_disable = t3_phy_lasi_intr_disable, @@ -921,7 +921,7 @@ static int xaui_direct_power_down(struct cphy *phy, int enable) return 0; } -static struct cphy_ops xaui_direct_ops = { +static const struct cphy_ops xaui_direct_ops = { .reset = xaui_direct_reset, .intr_enable = ael1002_intr_noop, .intr_disable = ael1002_intr_noop, diff --git a/drivers/net/ethernet/chelsio/cxgb3/aq100x.c b/drivers/net/ethernet/chelsio/cxgb3/aq100x.c index 341b7ef1508f..6af5d200e44f 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/aq100x.c +++ b/drivers/net/ethernet/chelsio/cxgb3/aq100x.c @@ -247,7 +247,7 @@ static int aq100x_get_link_status(struct cphy *phy, int *link_ok, return 0; } -static struct cphy_ops aq100x_ops = { +static const struct cphy_ops aq100x_ops = { .reset = aq100x_reset, .intr_enable = aq100x_intr_enable, .intr_disable = aq100x_intr_disable, diff --git a/drivers/net/ethernet/chelsio/cxgb3/common.h b/drivers/net/ethernet/chelsio/cxgb3/common.h index 442480982d3f..1bd7d89666c4 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/common.h +++ b/drivers/net/ethernet/chelsio/cxgb3/common.h @@ -575,7 +575,7 @@ static inline int t3_mdio_write(struct cphy *phy, int mmd, int reg, /* Convenience initializer */ static inline void cphy_init(struct cphy *phy, struct adapter *adapter, - int phy_addr, struct cphy_ops *phy_ops, + int phy_addr, const struct cphy_ops *phy_ops, const struct mdio_ops *mdio_ops, unsigned int caps, const char *desc) { diff --git a/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c b/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c index 4f9a1c2724f4..8638ad42bf60 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c +++ b/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c @@ -336,7 +336,7 @@ static int vsc8211_intr_handler(struct cphy *cphy) return cphy_cause; } -static struct cphy_ops vsc8211_ops = { +static const struct cphy_ops vsc8211_ops = { .reset = vsc8211_reset, .intr_enable = vsc8211_intr_enable, .intr_disable = vsc8211_intr_disable, @@ -350,7 +350,7 @@ static struct cphy_ops vsc8211_ops = { .power_down = vsc8211_power_down, }; -static struct cphy_ops vsc8211_fiber_ops = { +static const struct cphy_ops vsc8211_fiber_ops = { .reset = vsc8211_reset, .intr_enable = vsc8211_intr_enable, .intr_disable = vsc8211_intr_disable, -- cgit v1.2.3-59-g8ed1b From 7b31abe70b29edb3005a6466f51b7a10f8413a01 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 4 Jan 2016 10:42:23 +0100 Subject: mlxsw: spectrum: Initialize PVID only once We set PVID to 1 in mlxsw_sp_port_vlan_init(), so we can remove this statement. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index c588c65e91f5..6cc1125030df 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1367,7 +1367,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) mlxsw_sp_port->learning = 1; mlxsw_sp_port->learning_sync = 1; mlxsw_sp_port->uc_flood = 1; - mlxsw_sp_port->pvid = 1; bytes = DIV_ROUND_UP(VLAN_N_VID, BITS_PER_BYTE); mlxsw_sp_port->active_vlans = kzalloc(bytes, GFP_KERNEL); if (!mlxsw_sp_port->active_vlans) { -- cgit v1.2.3-59-g8ed1b From 78124078c404f3ab790d98d651f059f62d361659 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 4 Jan 2016 10:42:24 +0100 Subject: mlxsw: spectrum: Return NOTIFY_BAD on bridge failure It is possible for us to fail when joining or leaving a bridge, so let the user know about that by returning NOTIFY_BAD, as already done for LAG join/leave and 802.1D bridges. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 6cc1125030df..eb719f3343d0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2357,16 +2357,20 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, } else if (netif_is_bridge_master(upper_dev)) { if (info->linking) { err = mlxsw_sp_port_bridge_join(mlxsw_sp_port); - if (err) + if (err) { netdev_err(dev, "Failed to join bridge\n"); + return NOTIFY_BAD; + } mlxsw_sp_master_bridge_inc(mlxsw_sp, upper_dev); mlxsw_sp_port->bridged = 1; } else { err = mlxsw_sp_port_bridge_leave(mlxsw_sp_port); - if (err) - netdev_err(dev, "Failed to leave bridge\n"); mlxsw_sp_port->bridged = 0; mlxsw_sp_master_bridge_dec(mlxsw_sp, upper_dev); + if (err) { + netdev_err(dev, "Failed to leave bridge\n"); + return NOTIFY_BAD; + } } } else if (netif_is_lag_master(upper_dev)) { if (info->linking) { -- cgit v1.2.3-59-g8ed1b From 5a8f45258eb2ff9da1ee68aad96b1189df8af50e Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 4 Jan 2016 10:42:25 +0100 Subject: mlxsw: spectrum: Set bridge status in appropriate functions Set the bridge status of physical ports in the appropriate functions, to be consistent with LAG join/leave and vPorts joining/leaving bridge. Also, remove the error messages in these two functions, as we already emit errors in both the single functions they call. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index eb719f3343d0..ed2eb7b74850 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1989,8 +1989,8 @@ static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port) * own VLANs. */ err = mlxsw_sp_port_kill_vid(dev, 0, 1); - if (err) - netdev_err(dev, "Failed to remove VID 1\n"); + if (!err) + mlxsw_sp_port->bridged = 1; return err; } @@ -1998,16 +1998,13 @@ static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port) static int mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port) { struct net_device *dev = mlxsw_sp_port->dev; - int err; + + mlxsw_sp_port->bridged = 0; /* Add implicit VLAN interface in the device, so that untagged * packets will be classified to the default vFID. */ - err = mlxsw_sp_port_add_vid(dev, 0, 1); - if (err) - netdev_err(dev, "Failed to add VID 1\n"); - - return err; + return mlxsw_sp_port_add_vid(dev, 0, 1); } static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp, @@ -2362,10 +2359,8 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, return NOTIFY_BAD; } mlxsw_sp_master_bridge_inc(mlxsw_sp, upper_dev); - mlxsw_sp_port->bridged = 1; } else { err = mlxsw_sp_port_bridge_leave(mlxsw_sp_port); - mlxsw_sp_port->bridged = 0; mlxsw_sp_master_bridge_dec(mlxsw_sp, upper_dev); if (err) { netdev_err(dev, "Failed to leave bridge\n"); -- cgit v1.2.3-59-g8ed1b From 6c72a3d0d3e2b3055a04aa6124c2faa3420a30b1 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 4 Jan 2016 10:42:26 +0100 Subject: mlxsw: spectrum: Change bridge port attributes only when bridged Bridge port attributes are offloaded to hardware when invoked with SELF flag set, but it really makes no sense to reflect them when port is not bridged. Allow a user to change these attribute only when port is bridged and initialize them correctly when joining or leaving a bridge. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 17 +++++++++++------ .../net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 3 +++ 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index ed2eb7b74850..74ff0110e899 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1364,9 +1364,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) mlxsw_sp_port->dev = dev; mlxsw_sp_port->mlxsw_sp = mlxsw_sp; mlxsw_sp_port->local_port = local_port; - mlxsw_sp_port->learning = 1; - mlxsw_sp_port->learning_sync = 1; - mlxsw_sp_port->uc_flood = 1; bytes = DIV_ROUND_UP(VLAN_N_VID, BITS_PER_BYTE); mlxsw_sp_port->active_vlans = kzalloc(bytes, GFP_KERNEL); if (!mlxsw_sp_port->active_vlans) { @@ -1989,16 +1986,24 @@ static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port) * own VLANs. */ err = mlxsw_sp_port_kill_vid(dev, 0, 1); - if (!err) - mlxsw_sp_port->bridged = 1; + if (err) + return err; - return err; + mlxsw_sp_port->learning = 1; + mlxsw_sp_port->learning_sync = 1; + mlxsw_sp_port->uc_flood = 1; + mlxsw_sp_port->bridged = 1; + + return 0; } static int mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port) { struct net_device *dev = mlxsw_sp_port->dev; + mlxsw_sp_port->learning = 0; + mlxsw_sp_port->learning_sync = 0; + mlxsw_sp_port->uc_flood = 0; mlxsw_sp_port->bridged = 0; /* Add implicit VLAN interface in the device, so that untagged diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 9476ff9237ae..62159547ebf9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -252,6 +252,9 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, bool set; int err; + if (!mlxsw_sp_port->bridged) + return -EINVAL; + if (switchdev_trans_ph_prepare(trans)) return 0; -- cgit v1.2.3-59-g8ed1b From 34802a42b3528b0e18ea4517c8b23e1214a09332 Mon Sep 17 00:00:00 2001 From: Achiad Shochat Date: Tue, 29 Dec 2015 14:58:29 +0200 Subject: net/mlx5e: Do not modify the TX SKB If the SKB is cloned, or has an elevated users count, someone else can be looking at it at the same time. Signed-off-by: Achiad Shochat Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 5 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 5 +- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 73 +++++++++++++---------- 3 files changed, 49 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index f689ce580b44..ae3f0e326c79 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -328,14 +328,12 @@ struct mlx5e_rq { struct mlx5e_priv *priv; } ____cacheline_aligned_in_smp; -struct mlx5e_tx_skb_cb { +struct mlx5e_tx_wqe_info { u32 num_bytes; u8 num_wqebbs; u8 num_dma; }; -#define MLX5E_TX_SKB_CB(__skb) ((struct mlx5e_tx_skb_cb *)__skb->cb) - enum mlx5e_dma_map_type { MLX5E_DMA_MAP_SINGLE, MLX5E_DMA_MAP_PAGE @@ -371,6 +369,7 @@ struct mlx5e_sq { /* pointers to per packet info: write@xmit, read@completion */ struct sk_buff **skb; struct mlx5e_sq_dma *dma_fifo; + struct mlx5e_tx_wqe_info *wqe_info; /* read only */ struct mlx5_wq_cyc wq; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index d4601a564699..96775a29a440 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -507,6 +507,7 @@ static void mlx5e_close_rq(struct mlx5e_rq *rq) static void mlx5e_free_sq_db(struct mlx5e_sq *sq) { + kfree(sq->wqe_info); kfree(sq->dma_fifo); kfree(sq->skb); } @@ -519,8 +520,10 @@ static int mlx5e_alloc_sq_db(struct mlx5e_sq *sq, int numa) sq->skb = kzalloc_node(wq_sz * sizeof(*sq->skb), GFP_KERNEL, numa); sq->dma_fifo = kzalloc_node(df_sz * sizeof(*sq->dma_fifo), GFP_KERNEL, numa); + sq->wqe_info = kzalloc_node(wq_sz * sizeof(*sq->wqe_info), GFP_KERNEL, + numa); - if (!sq->skb || !sq->dma_fifo) { + if (!sq->skb || !sq->dma_fifo || !sq->wqe_info) { mlx5e_free_sq_db(sq); return -ENOMEM; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 1341b1d3c421..aa037eb1a0b2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -92,11 +92,11 @@ static inline struct mlx5e_sq_dma *mlx5e_dma_get(struct mlx5e_sq *sq, u32 i) return &sq->dma_fifo[i & sq->dma_fifo_mask]; } -static void mlx5e_dma_unmap_wqe_err(struct mlx5e_sq *sq, struct sk_buff *skb) +static void mlx5e_dma_unmap_wqe_err(struct mlx5e_sq *sq, u8 num_dma) { int i; - for (i = 0; i < MLX5E_TX_SKB_CB(skb)->num_dma; i++) { + for (i = 0; i < num_dma; i++) { struct mlx5e_sq_dma *last_pushed_dma = mlx5e_dma_get(sq, --sq->dma_fifo_pc); @@ -139,19 +139,28 @@ static inline u16 mlx5e_get_inline_hdr_size(struct mlx5e_sq *sq, return MLX5E_MIN_INLINE; } -static inline void mlx5e_insert_vlan(void *start, struct sk_buff *skb, u16 ihs) +static inline void mlx5e_tx_skb_pull_inline(unsigned char **skb_data, + unsigned int *skb_len, + unsigned int len) +{ + *skb_len -= len; + *skb_data += len; +} + +static inline void mlx5e_insert_vlan(void *start, struct sk_buff *skb, u16 ihs, + unsigned char **skb_data, + unsigned int *skb_len) { struct vlan_ethhdr *vhdr = (struct vlan_ethhdr *)start; int cpy1_sz = 2 * ETH_ALEN; int cpy2_sz = ihs - cpy1_sz; - skb_copy_from_linear_data(skb, vhdr, cpy1_sz); - skb_pull_inline(skb, cpy1_sz); + memcpy(vhdr, *skb_data, cpy1_sz); + mlx5e_tx_skb_pull_inline(skb_data, skb_len, cpy1_sz); vhdr->h_vlan_proto = skb->vlan_proto; vhdr->h_vlan_TCI = cpu_to_be16(skb_vlan_tag_get(skb)); - skb_copy_from_linear_data(skb, &vhdr->h_vlan_encapsulated_proto, - cpy2_sz); - skb_pull_inline(skb, cpy2_sz); + memcpy(&vhdr->h_vlan_encapsulated_proto, *skb_data, cpy2_sz); + mlx5e_tx_skb_pull_inline(skb_data, skb_len, cpy2_sz); } static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) @@ -160,11 +169,14 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) u16 pi = sq->pc & wq->sz_m1; struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); + struct mlx5e_tx_wqe_info *wi = &sq->wqe_info[pi]; struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; struct mlx5_wqe_eth_seg *eseg = &wqe->eth; struct mlx5_wqe_data_seg *dseg; + unsigned char *skb_data = skb->data; + unsigned int skb_len = skb->len; u8 opcode = MLX5_OPCODE_SEND; dma_addr_t dma_addr = 0; bool bf = false; @@ -192,8 +204,8 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) opcode = MLX5_OPCODE_LSO; ihs = skb_transport_offset(skb) + tcp_hdrlen(skb); payload_len = skb->len - ihs; - MLX5E_TX_SKB_CB(skb)->num_bytes = skb->len + - (skb_shinfo(skb)->gso_segs - 1) * ihs; + wi->num_bytes = skb->len + + (skb_shinfo(skb)->gso_segs - 1) * ihs; sq->stats.tso_packets++; sq->stats.tso_bytes += payload_len; } else { @@ -201,16 +213,16 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) !skb->xmit_more && !skb_shinfo(skb)->nr_frags; ihs = mlx5e_get_inline_hdr_size(sq, skb, bf); - MLX5E_TX_SKB_CB(skb)->num_bytes = max_t(unsigned int, skb->len, - ETH_ZLEN); + wi->num_bytes = max_t(unsigned int, skb->len, ETH_ZLEN); } if (skb_vlan_tag_present(skb)) { - mlx5e_insert_vlan(eseg->inline_hdr_start, skb, ihs); + mlx5e_insert_vlan(eseg->inline_hdr_start, skb, ihs, &skb_data, + &skb_len); ihs += VLAN_HLEN; } else { - skb_copy_from_linear_data(skb, eseg->inline_hdr_start, ihs); - skb_pull_inline(skb, ihs); + memcpy(eseg->inline_hdr_start, skb_data, ihs); + mlx5e_tx_skb_pull_inline(&skb_data, &skb_len, ihs); } eseg->inline_hdr_sz = cpu_to_be16(ihs); @@ -220,11 +232,11 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) MLX5_SEND_WQE_DS); dseg = (struct mlx5_wqe_data_seg *)cseg + ds_cnt; - MLX5E_TX_SKB_CB(skb)->num_dma = 0; + wi->num_dma = 0; - headlen = skb_headlen(skb); + headlen = skb_len - skb->data_len; if (headlen) { - dma_addr = dma_map_single(sq->pdev, skb->data, headlen, + dma_addr = dma_map_single(sq->pdev, skb_data, headlen, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sq->pdev, dma_addr))) goto dma_unmap_wqe_err; @@ -234,7 +246,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) dseg->byte_count = cpu_to_be32(headlen); mlx5e_dma_push(sq, dma_addr, headlen, MLX5E_DMA_MAP_SINGLE); - MLX5E_TX_SKB_CB(skb)->num_dma++; + wi->num_dma++; dseg++; } @@ -253,23 +265,22 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) dseg->byte_count = cpu_to_be32(fsz); mlx5e_dma_push(sq, dma_addr, fsz, MLX5E_DMA_MAP_PAGE); - MLX5E_TX_SKB_CB(skb)->num_dma++; + wi->num_dma++; dseg++; } - ds_cnt += MLX5E_TX_SKB_CB(skb)->num_dma; + ds_cnt += wi->num_dma; cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | opcode); cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); sq->skb[pi] = skb; - MLX5E_TX_SKB_CB(skb)->num_wqebbs = DIV_ROUND_UP(ds_cnt, - MLX5_SEND_WQEBB_NUM_DS); - sq->pc += MLX5E_TX_SKB_CB(skb)->num_wqebbs; + wi->num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS); + sq->pc += wi->num_wqebbs; - netdev_tx_sent_queue(sq->txq, MLX5E_TX_SKB_CB(skb)->num_bytes); + netdev_tx_sent_queue(sq->txq, wi->num_bytes); if (unlikely(!mlx5e_sq_has_room_for(sq, MLX5E_SQ_STOP_ROOM))) { netif_tx_stop_queue(sq->txq); @@ -280,7 +291,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) int bf_sz = 0; if (bf && sq->uar_bf_map) - bf_sz = MLX5E_TX_SKB_CB(skb)->num_wqebbs << 3; + bf_sz = wi->num_wqebbs << 3; cseg->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE; mlx5e_tx_notify_hw(sq, wqe, bf_sz); @@ -297,7 +308,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) dma_unmap_wqe_err: sq->stats.dropped++; - mlx5e_dma_unmap_wqe_err(sq, skb); + mlx5e_dma_unmap_wqe_err(sq, wi->num_dma); dev_kfree_skb_any(skb); @@ -352,6 +363,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq) wqe_counter = be16_to_cpu(cqe->wqe_counter); do { + struct mlx5e_tx_wqe_info *wi; struct sk_buff *skb; u16 ci; int j; @@ -360,6 +372,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq) ci = sqcc & sq->wq.sz_m1; skb = sq->skb[ci]; + wi = &sq->wqe_info[ci]; if (unlikely(!skb)) { /* nop */ sq->stats.nop++; @@ -367,7 +380,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq) continue; } - for (j = 0; j < MLX5E_TX_SKB_CB(skb)->num_dma; j++) { + for (j = 0; j < wi->num_dma; j++) { struct mlx5e_sq_dma *dma = mlx5e_dma_get(sq, dma_fifo_cc++); @@ -375,8 +388,8 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq) } npkts++; - nbytes += MLX5E_TX_SKB_CB(skb)->num_bytes; - sqcc += MLX5E_TX_SKB_CB(skb)->num_wqebbs; + nbytes += wi->num_bytes; + sqcc += wi->num_wqebbs; dev_kfree_skb(skb); } while (!last_wqe); } -- cgit v1.2.3-59-g8ed1b From b0844444590e18704644f707ea88bff1b976b0e7 Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Tue, 29 Dec 2015 14:58:30 +0200 Subject: net/mlx5_core: Introduce access function to read internal timer A preparation step which adds support for reading the hardware internal timer and the hardware timestamping from the CQE. In addition, advertize device_frequency_khz HCA capability. Signed-off-by: Eran Ben Elisha Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 13 +++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h | 1 + include/linux/mlx5/device.h | 20 +++++++++++++++++--- include/linux/mlx5/mlx5_ifc.h | 6 +++--- 4 files changed, 34 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 789882b7b711..67676cf0d507 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -504,6 +504,19 @@ int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id) return mlx5_cmd_status_to_err_v2(out); } +cycle_t mlx5_read_internal_timer(struct mlx5_core_dev *dev) +{ + u32 timer_h, timer_h1, timer_l; + + timer_h = ioread32be(&dev->iseg->internal_timer_h); + timer_l = ioread32be(&dev->iseg->internal_timer_l); + timer_h1 = ioread32be(&dev->iseg->internal_timer_h); + if (timer_h != timer_h1) /* wrap around */ + timer_l = ioread32be(&dev->iseg->internal_timer_l); + + return (cycle_t)timer_l | (cycle_t)timer_h1 << 32; +} + static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i) { struct mlx5_priv *priv = &mdev->priv; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index ea6a137fd76c..0336847ec9a1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -98,6 +98,7 @@ int mlx5_core_sriov_configure(struct pci_dev *dev, int num_vfs); int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id); int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id); int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev); +cycle_t mlx5_read_internal_timer(struct mlx5_core_dev *dev); void mlx5e_init(void); void mlx5e_cleanup(void); diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 7d3a85faefb7..df2f79ef3cac 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -443,9 +443,12 @@ struct mlx5_init_seg { __be32 rsvd1[120]; __be32 initializing; struct health_buffer health; - __be32 rsvd2[884]; + __be32 rsvd2[880]; + __be32 internal_timer_h; + __be32 internal_timer_l; + __be32 rsrv3[2]; __be32 health_counter; - __be32 rsvd3[1019]; + __be32 rsvd4[1019]; __be64 ieee1588_clk; __be32 ieee1588_clk_type; __be32 clr_intx; @@ -601,7 +604,8 @@ struct mlx5_cqe64 { __be32 imm_inval_pkey; u8 rsvd40[4]; __be32 byte_cnt; - __be64 timestamp; + __be32 timestamp_h; + __be32 timestamp_l; __be32 sop_drop_qpn; __be16 wqe_counter; u8 signature; @@ -623,6 +627,16 @@ static inline int cqe_has_vlan(struct mlx5_cqe64 *cqe) return !!(cqe->l4_hdr_type_etc & 0x1); } +static inline u64 get_cqe_ts(struct mlx5_cqe64 *cqe) +{ + u32 hi, lo; + + hi = be32_to_cpu(cqe->timestamp_h); + lo = be32_to_cpu(cqe->timestamp_l); + + return (u64)lo | ((u64)hi << 32); +} + enum { CQE_L4_HDR_TYPE_NONE = 0x0, CQE_L4_HDR_TYPE_TCP_NO_ACK = 0x1, diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 131a2737cfa3..1780a85a8797 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -829,9 +829,9 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 reserved_66[0x8]; u8 log_uar_page_sz[0x10]; - u8 reserved_67[0xe0]; - - u8 reserved_68[0x1f]; + u8 reserved_67[0x40]; + u8 device_frequency_khz[0x20]; + u8 reserved_68[0x5f]; u8 cqe_zip[0x1]; u8 cqe_zip_timeout[0x10]; -- cgit v1.2.3-59-g8ed1b From ef9814deafd0c83a358d49e3709c3e16cc352118 Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Tue, 29 Dec 2015 14:58:31 +0200 Subject: net/mlx5e: Add HW timestamping (TS) support Add support for enable/disable HW timestamping for incoming and/or outgoing packets. To enable/disable HW timestamping appropriate ioctl should be used. Currently HWTSTAMP_FILTER_ALL/NONE and HWTSAMP_TX_ON/OFF only are supported. Make all relevant changes in RX/TX flows to consider TS request and plant HW timestamps into relevant structures. Add internal clock for converting hardware timestamp to nanoseconds. In addition, add a service task to catch internal clock overflow, to make sure timestamping is accurate. Signed-off-by: Eran Ben Elisha Signed-off-by: Saeed Mahameed Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en.h | 23 +++ drivers/net/ethernet/mellanox/mlx5/core/en_clock.c | 187 +++++++++++++++++++++ .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 29 ++++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 19 ++- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 9 + drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 12 ++ 7 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/en_clock.c (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index fe11e967095f..01c0256effb8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -5,4 +5,4 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o \ en_main.o en_fs.o en_ethtool.o en_tx.o en_rx.o \ - en_txrx.o + en_txrx.o en_clock.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index ae3f0e326c79..477e24884012 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -32,6 +32,8 @@ #include #include +#include +#include #include #include #include @@ -284,6 +286,17 @@ struct mlx5e_params { u32 indirection_rqt[MLX5E_INDIR_RQT_SIZE]; }; +struct mlx5e_tstamp { + rwlock_t lock; + struct cyclecounter cycles; + struct timecounter clock; + struct hwtstamp_config hwtstamp_config; + u32 nominal_c_mult; + unsigned long overflow_period; + struct delayed_work overflow_work; + struct mlx5_core_dev *mdev; +}; + enum { MLX5E_RQ_STATE_POST_WQES_ENABLE, }; @@ -315,6 +328,7 @@ struct mlx5e_rq { struct device *pdev; struct net_device *netdev; + struct mlx5e_tstamp *tstamp; struct mlx5e_rq_stats stats; struct mlx5e_cq cq; @@ -382,6 +396,7 @@ struct mlx5e_sq { u16 max_inline; u16 edge; struct device *pdev; + struct mlx5e_tstamp *tstamp; __be32 mkey_be; unsigned long state; @@ -518,6 +533,7 @@ struct mlx5e_priv { struct mlx5_core_dev *mdev; struct net_device *netdev; struct mlx5e_stats stats; + struct mlx5e_tstamp tstamp; }; #define MLX5E_NET_IP_ALIGN 2 @@ -584,6 +600,13 @@ void mlx5e_destroy_flow_tables(struct mlx5e_priv *priv); void mlx5e_init_eth_addr(struct mlx5e_priv *priv); void mlx5e_set_rx_mode_work(struct work_struct *work); +void mlx5e_fill_hwstamp(struct mlx5e_tstamp *clock, u64 timestamp, + struct skb_shared_hwtstamps *hwts); +void mlx5e_timestamp_init(struct mlx5e_priv *priv); +void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv); +int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr); +int mlx5e_hwstamp_get(struct net_device *dev, struct ifreq *ifr); + int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto, u16 vid); int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c new file mode 100644 index 000000000000..49a82385a6c0 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2015, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "en.h" + +enum { + MLX5E_CYCLES_SHIFT = 23 +}; + +void mlx5e_fill_hwstamp(struct mlx5e_tstamp *tstamp, u64 timestamp, + struct skb_shared_hwtstamps *hwts) +{ + u64 nsec; + + read_lock(&tstamp->lock); + nsec = timecounter_cyc2time(&tstamp->clock, timestamp); + read_unlock(&tstamp->lock); + + hwts->hwtstamp = ns_to_ktime(nsec); +} + +static cycle_t mlx5e_read_internal_timer(const struct cyclecounter *cc) +{ + struct mlx5e_tstamp *tstamp = container_of(cc, struct mlx5e_tstamp, + cycles); + + return mlx5_read_internal_timer(tstamp->mdev) & cc->mask; +} + +static void mlx5e_timestamp_overflow(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct mlx5e_tstamp *tstamp = container_of(dwork, struct mlx5e_tstamp, + overflow_work); + + write_lock(&tstamp->lock); + timecounter_read(&tstamp->clock); + write_unlock(&tstamp->lock); + schedule_delayed_work(&tstamp->overflow_work, tstamp->overflow_period); +} + +int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct hwtstamp_config config; + + if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) + return -EOPNOTSUPP; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + /* TX HW timestamp */ + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + case HWTSTAMP_TX_ON: + break; + default: + return -ERANGE; + } + + /* RX HW timestamp */ + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_SOME: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + config.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + return -ERANGE; + } + + memcpy(&priv->tstamp.hwtstamp_config, &config, sizeof(config)); + + return copy_to_user(ifr->ifr_data, &config, + sizeof(config)) ? -EFAULT : 0; +} + +int mlx5e_hwstamp_get(struct net_device *dev, struct ifreq *ifr) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct hwtstamp_config *cfg = &priv->tstamp.hwtstamp_config; + + if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) + return -EOPNOTSUPP; + + return copy_to_user(ifr->ifr_data, cfg, sizeof(*cfg)) ? -EFAULT : 0; +} + +static void mlx5e_timestamp_init_config(struct mlx5e_tstamp *tstamp) +{ + tstamp->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF; + tstamp->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; +} + +void mlx5e_timestamp_init(struct mlx5e_priv *priv) +{ + struct mlx5e_tstamp *tstamp = &priv->tstamp; + u64 ns; + u64 frac = 0; + u32 dev_freq; + + mlx5e_timestamp_init_config(tstamp); + dev_freq = MLX5_CAP_GEN(priv->mdev, device_frequency_khz); + if (!dev_freq) { + mlx5_core_warn(priv->mdev, "invalid device_frequency_khz, aborting HW clock init\n"); + return; + } + rwlock_init(&tstamp->lock); + tstamp->cycles.read = mlx5e_read_internal_timer; + tstamp->cycles.shift = MLX5E_CYCLES_SHIFT; + tstamp->cycles.mult = clocksource_khz2mult(dev_freq, + tstamp->cycles.shift); + tstamp->nominal_c_mult = tstamp->cycles.mult; + tstamp->cycles.mask = CLOCKSOURCE_MASK(41); + tstamp->mdev = priv->mdev; + + timecounter_init(&tstamp->clock, &tstamp->cycles, + ktime_to_ns(ktime_get_real())); + + /* Calculate period in seconds to call the overflow watchdog - to make + * sure counter is checked at least once every wrap around. + */ + ns = cyclecounter_cyc2ns(&tstamp->cycles, tstamp->cycles.mask, + frac, &frac); + do_div(ns, NSEC_PER_SEC / 2 / HZ); + tstamp->overflow_period = ns; + + INIT_DELAYED_WORK(&tstamp->overflow_work, mlx5e_timestamp_overflow); + if (tstamp->overflow_period) + schedule_delayed_work(&tstamp->overflow_work, 0); + else + mlx5_core_warn(priv->mdev, "invalid overflow period, overflow_work is not scheduled\n"); +} + +void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv) +{ + struct mlx5e_tstamp *tstamp = &priv->tstamp; + + if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) + return; + + cancel_delayed_work_sync(&tstamp->overflow_work); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 2e022e900939..a8a90f3c5807 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -855,6 +855,34 @@ static int mlx5e_set_pauseparam(struct net_device *netdev, return err; } +static int mlx5e_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *info) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + int ret; + + ret = ethtool_op_get_ts_info(dev, info); + if (ret) + return ret; + + info->phc_index = -1; + + if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) + return 0; + + info->so_timestamping |= SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + info->tx_types = (BIT(1) << HWTSTAMP_TX_OFF) | + (BIT(1) << HWTSTAMP_TX_ON); + + info->rx_filters = (BIT(1) << HWTSTAMP_FILTER_NONE) | + (BIT(1) << HWTSTAMP_FILTER_ALL); + + return 0; +} + const struct ethtool_ops mlx5e_ethtool_ops = { .get_drvinfo = mlx5e_get_drvinfo, .get_link = ethtool_op_get_link, @@ -878,4 +906,5 @@ const struct ethtool_ops mlx5e_ethtool_ops = { .set_tunable = mlx5e_set_tunable, .get_pauseparam = mlx5e_get_pauseparam, .set_pauseparam = mlx5e_set_pauseparam, + .get_ts_info = mlx5e_get_ts_info, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 96775a29a440..5c74a734f158 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -351,6 +351,7 @@ static int mlx5e_create_rq(struct mlx5e_channel *c, rq->pdev = c->pdev; rq->netdev = c->netdev; + rq->tstamp = &priv->tstamp; rq->channel = c; rq->ix = c->ix; rq->priv = c->priv; @@ -571,6 +572,7 @@ static int mlx5e_create_sq(struct mlx5e_channel *c, sq->txq = netdev_get_tx_queue(priv->netdev, txq_ix); sq->pdev = c->pdev; + sq->tstamp = &priv->tstamp; sq->mkey_be = c->mkey_be; sq->channel = c; sq->tc = tc; @@ -1430,6 +1432,7 @@ int mlx5e_open_locked(struct net_device *netdev) mlx5e_update_carrier(priv); mlx5e_redirect_rqts(priv); + mlx5e_timestamp_init(priv); schedule_delayed_work(&priv->update_stats_work, 0); @@ -1466,6 +1469,7 @@ int mlx5e_close_locked(struct net_device *netdev) clear_bit(MLX5E_STATE_OPENED, &priv->state); + mlx5e_timestamp_cleanup(priv); mlx5e_redirect_rqts(priv); netif_carrier_off(priv->netdev); mlx5e_close_channels(priv); @@ -1935,6 +1939,18 @@ static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu) return err; } +static int mlx5e_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + switch (cmd) { + case SIOCSHWTSTAMP: + return mlx5e_hwstamp_set(dev, ifr); + case SIOCGHWTSTAMP: + return mlx5e_hwstamp_get(dev, ifr); + default: + return -EOPNOTSUPP; + } +} + static int mlx5e_set_vf_mac(struct net_device *dev, int vf, u8 *mac) { struct mlx5e_priv *priv = netdev_priv(dev); @@ -2018,7 +2034,8 @@ static struct net_device_ops mlx5e_netdev_ops = { .ndo_vlan_rx_add_vid = mlx5e_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = mlx5e_vlan_rx_kill_vid, .ndo_set_features = mlx5e_set_features, - .ndo_change_mtu = mlx5e_change_mtu + .ndo_change_mtu = mlx5e_change_mtu, + .ndo_do_ioctl = mlx5e_ioctl, }; static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 7c8c4088d1be..dd959d929aad 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -36,6 +36,11 @@ #include #include "en.h" +static inline bool mlx5e_rx_hw_stamp(struct mlx5e_tstamp *tstamp) +{ + return tstamp->hwtstamp_config.rx_filter == HWTSTAMP_FILTER_ALL; +} + static inline int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) { @@ -190,6 +195,7 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe, { struct net_device *netdev = rq->netdev; u32 cqe_bcnt = be32_to_cpu(cqe->byte_cnt); + struct mlx5e_tstamp *tstamp = rq->tstamp; int lro_num_seg; skb_put(skb, cqe_bcnt); @@ -202,6 +208,9 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe, rq->stats.lro_bytes += cqe_bcnt; } + if (unlikely(mlx5e_rx_hw_stamp(tstamp))) + mlx5e_fill_hwstamp(tstamp, get_cqe_ts(cqe), skb_hwtstamps(skb)); + mlx5e_handle_csum(netdev, cqe, rq, skb); skb->protocol = eth_type_trans(skb, netdev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index aa037eb1a0b2..2c3fba0fff54 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -282,6 +282,9 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) netdev_tx_sent_queue(sq->txq, wi->num_bytes); + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + if (unlikely(!mlx5e_sq_has_room_for(sq, MLX5E_SQ_STOP_ROOM))) { netif_tx_stop_queue(sq->txq); sq->stats.stopped++; @@ -380,6 +383,15 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq) continue; } + if (unlikely(skb_shinfo(skb)->tx_flags & + SKBTX_HW_TSTAMP)) { + struct skb_shared_hwtstamps hwts = {}; + + mlx5e_fill_hwstamp(sq->tstamp, + get_cqe_ts(cqe), &hwts); + skb_tstamp_tx(skb, &hwts); + } + for (j = 0; j < wi->num_dma; j++) { struct mlx5e_sq_dma *dma = mlx5e_dma_get(sq, dma_fifo_cc++); -- cgit v1.2.3-59-g8ed1b From 3d8c38af149309feb2541b995b3a45df170d6da3 Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Tue, 29 Dec 2015 14:58:32 +0200 Subject: net/mlx5e: Add PTP Hardware Clock (PHC) support Add a PHC support to the mlx5_en driver. Use reader/writer spinlocks to protect the timecounter since every packet received needs to call timecounter_cycle2time() when timestamping is enabled. This can become a performance bottleneck with RSS and multiple receive queues if normal spinlocks are used. The driver has been tested with both Documentation/ptp/testptp and the linuxptp project (http://linuxptp.sourceforge.net/) on a Mellanox ConnectX-4 card. Signed-off-by: Eran Ben Elisha Cc: Richard Cochran Signed-off-by: Saeed Mahameed Acked-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/Kconfig | 1 + drivers/net/ethernet/mellanox/mlx5/core/en.h | 3 + drivers/net/ethernet/mellanox/mlx5/core/en_clock.c | 100 +++++++++++++++++++++ .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 3 +- 4 files changed, 106 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig index 158c88c69ef9..c503ea05e742 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig +++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig @@ -13,6 +13,7 @@ config MLX5_CORE config MLX5_CORE_EN bool "Mellanox Technologies ConnectX-4 Ethernet support" depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE + select PTP_1588_CLOCK default n ---help--- Ethernet support in Mellanox Technologies ConnectX-4 NIC. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 477e24884012..9ea49a893323 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -295,6 +296,8 @@ struct mlx5e_tstamp { unsigned long overflow_period; struct delayed_work overflow_work; struct mlx5_core_dev *mdev; + struct ptp_clock *ptp; + struct ptp_clock_info ptp_info; }; enum { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c index 49a82385a6c0..be6543570b2b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c @@ -130,6 +130,89 @@ int mlx5e_hwstamp_get(struct net_device *dev, struct ifreq *ifr) return copy_to_user(ifr->ifr_data, cfg, sizeof(*cfg)) ? -EFAULT : 0; } +static int mlx5e_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, + ptp_info); + u64 ns = timespec64_to_ns(ts); + + write_lock(&tstamp->lock); + timecounter_init(&tstamp->clock, &tstamp->cycles, ns); + write_unlock(&tstamp->lock); + + return 0; +} + +static int mlx5e_ptp_gettime(struct ptp_clock_info *ptp, + struct timespec64 *ts) +{ + struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, + ptp_info); + u64 ns; + + write_lock(&tstamp->lock); + ns = timecounter_read(&tstamp->clock); + write_unlock(&tstamp->lock); + + *ts = ns_to_timespec64(ns); + + return 0; +} + +static int mlx5e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, + ptp_info); + + write_lock(&tstamp->lock); + timecounter_adjtime(&tstamp->clock, delta); + write_unlock(&tstamp->lock); + + return 0; +} + +static int mlx5e_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) +{ + u64 adj; + u32 diff; + int neg_adj = 0; + struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, + ptp_info); + + if (delta < 0) { + neg_adj = 1; + delta = -delta; + } + + adj = tstamp->nominal_c_mult; + adj *= delta; + diff = div_u64(adj, 1000000000ULL); + + write_lock(&tstamp->lock); + timecounter_read(&tstamp->clock); + tstamp->cycles.mult = neg_adj ? tstamp->nominal_c_mult - diff : + tstamp->nominal_c_mult + diff; + write_unlock(&tstamp->lock); + + return 0; +} + +static const struct ptp_clock_info mlx5e_ptp_clock_info = { + .owner = THIS_MODULE, + .max_adj = 100000000, + .n_alarm = 0, + .n_ext_ts = 0, + .n_per_out = 0, + .n_pins = 0, + .pps = 0, + .adjfreq = mlx5e_ptp_adjfreq, + .adjtime = mlx5e_ptp_adjtime, + .gettime64 = mlx5e_ptp_gettime, + .settime64 = mlx5e_ptp_settime, + .enable = NULL, +}; + static void mlx5e_timestamp_init_config(struct mlx5e_tstamp *tstamp) { tstamp->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF; @@ -174,6 +257,18 @@ void mlx5e_timestamp_init(struct mlx5e_priv *priv) schedule_delayed_work(&tstamp->overflow_work, 0); else mlx5_core_warn(priv->mdev, "invalid overflow period, overflow_work is not scheduled\n"); + + /* Configure the PHC */ + tstamp->ptp_info = mlx5e_ptp_clock_info; + snprintf(tstamp->ptp_info.name, 16, "mlx5 ptp"); + + tstamp->ptp = ptp_clock_register(&tstamp->ptp_info, + &priv->mdev->pdev->dev); + if (IS_ERR_OR_NULL(tstamp->ptp)) { + mlx5_core_warn(priv->mdev, "ptp_clock_register failed %ld\n", + PTR_ERR(tstamp->ptp)); + tstamp->ptp = NULL; + } } void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv) @@ -183,5 +278,10 @@ void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv) if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) return; + if (priv->tstamp.ptp) { + ptp_clock_unregister(priv->tstamp.ptp); + priv->tstamp.ptp = NULL; + } + cancel_delayed_work_sync(&tstamp->overflow_work); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index a8a90f3c5807..65624ac65b4c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -865,7 +865,8 @@ static int mlx5e_get_ts_info(struct net_device *dev, if (ret) return ret; - info->phc_index = -1; + info->phc_index = priv->tstamp.ptp ? + ptp_clock_index(priv->tstamp.ptp) : -1; if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) return 0; -- cgit v1.2.3-59-g8ed1b From be78a690f50f38882220ceecda403478d477122e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 1 Jan 2016 23:27:57 +0100 Subject: net: hns: avoid uninitialized variable warning: gcc fails to see that the use of the 'last_offset' variable in hns_nic_reuse_page() is used correctly and issues a bogus warning: drivers/net/ethernet/hisilicon/hns/hns_enet.c: In function 'hns_nic_reuse_page': drivers/net/ethernet/hisilicon/hns/hns_enet.c:541:6: warning: 'last_offset' may be used uninitialized in this function [-Wmaybe-uninitialized] This simplifies the function to make it more obvious what is going on to both readers and compilers, which makes the warning go away. Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_enet.c | 47 +++++++++++++-------------- 1 file changed, 22 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 5a81dafd725e..0e30846a24f8 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -499,50 +499,47 @@ static void hns_nic_reuse_page(struct sk_buff *skb, int i, struct hnae_desc *desc; int truesize, size; int last_offset; + bool twobufs; + + twobufs = ((PAGE_SIZE < 8192) && hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048); desc = &ring->desc[ring->next_to_clean]; size = le16_to_cpu(desc->rx.size); -#if (PAGE_SIZE < 8192) - if (hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048) { + if (twobufs) { truesize = hnae_buf_size(ring); } else { truesize = ALIGN(size, L1_CACHE_BYTES); last_offset = hnae_page_size(ring) - hnae_buf_size(ring); } -#else - truesize = ALIGN(size, L1_CACHE_BYTES); - last_offset = hnae_page_size(ring) - hnae_buf_size(ring); -#endif - skb_add_rx_frag(skb, i, desc_cb->priv, desc_cb->page_offset + pull_len, size - pull_len, truesize - pull_len); /* avoid re-using remote pages,flag default unreuse */ - if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) { -#if (PAGE_SIZE < 8192) - if (hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048) { - /* if we are only owner of page we can reuse it */ - if (likely(page_count(desc_cb->priv) == 1)) { - /* flip page offset to other buffer */ - desc_cb->page_offset ^= truesize; - - desc_cb->reuse_flag = 1; - /* bump ref count on page before it is given*/ - get_page(desc_cb->priv); - } - return; - } -#endif - /* move offset up to the next cache line */ - desc_cb->page_offset += truesize; + if (unlikely(page_to_nid(desc_cb->priv) != numa_node_id())) + return; + + if (twobufs) { + /* if we are only owner of page we can reuse it */ + if (likely(page_count(desc_cb->priv) == 1)) { + /* flip page offset to other buffer */ + desc_cb->page_offset ^= truesize; - if (desc_cb->page_offset <= last_offset) { desc_cb->reuse_flag = 1; /* bump ref count on page before it is given*/ get_page(desc_cb->priv); } + return; + } + + /* move offset up to the next cache line */ + desc_cb->page_offset += truesize; + + if (desc_cb->page_offset <= last_offset) { + desc_cb->reuse_flag = 1; + /* bump ref count on page before it is given*/ + get_page(desc_cb->priv); } } -- cgit v1.2.3-59-g8ed1b From f0138e2596f2994129451ee320ff8692cb7bc86b Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 5 Jan 2016 11:36:40 +0100 Subject: mlxsw: pci: Adjust value of CPU egress traffic class During initialization, when creating the send descriptor queues (SDQs), we specify the CPU egress traffic class of each SDQ. The maximum number of classes of this type is different in the two ASICs supported by this PCI driver. New firmware versions check this value is set correctly, which causes errors on the Spectrum ASIC, as its max exposed egress traffic class is lower than 7. Solve this by setting this field to 3, which is an acceptable value for both ASICs. Note that we currently do not expose the QoS capabilities of the ASICs, so setting this to an hardcoded value is OK for now. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index d2102e572b1d..c071077aafbd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -384,7 +384,7 @@ static int mlxsw_pci_sdq_init(struct mlxsw_pci *mlxsw_pci, char *mbox, /* Set CQ of same number of this SDQ. */ mlxsw_cmd_mbox_sw2hw_dq_cq_set(mbox, q->num); - mlxsw_cmd_mbox_sw2hw_dq_sdq_tclass_set(mbox, 7); + mlxsw_cmd_mbox_sw2hw_dq_sdq_tclass_set(mbox, 3); mlxsw_cmd_mbox_sw2hw_dq_log2_dq_sz_set(mbox, 3); /* 8 pages */ for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) { dma_addr_t mapaddr = __mlxsw_pci_queue_page_get(q, i); -- cgit v1.2.3-59-g8ed1b From d716892f0827dd29ae2488ab3005dfc84ff8fed0 Mon Sep 17 00:00:00 2001 From: Chin-Ran Lo Date: Wed, 6 Jan 2016 06:34:37 -0800 Subject: Bluetooth: btmrvl: max out host sleep parameter 'gap' For gpio=0xff (wake up host through SDIO interface) case, gap=0xff means no delay (same as gap=0) for incoming data packet to be sent to host after host sleep is activated. Change it to the maximum delay to reduce the chance that RX interrupt could be delivered while host controller suspends. Signed-off-by: Chin-Ran Lo Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index f2b38c880b11..b2a567bb1b46 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -543,7 +543,7 @@ static int btmrvl_setup(struct hci_dev *hdev) if (ret) return ret; - priv->btmrvl_dev.gpio_gap = 0xffff; + priv->btmrvl_dev.gpio_gap = 0xfffe; btmrvl_check_device_tree(priv); -- cgit v1.2.3-59-g8ed1b From 8cf60cf238ce1bea38593321e6ea8561fc32e38d Mon Sep 17 00:00:00 2001 From: Chin-Ran Lo Date: Wed, 6 Jan 2016 06:34:38 -0800 Subject: Bluetooth: btmrvl: don't send data to firmware while processing suspend Usually when driver sends data to firmware it receives TX_DONE (DN_LD_HOST_INT_STATUS) interrupt from firmware right away. It's also observed that some times the fireware could delay sending DN_LD_HOST_INT_STATUS interrupt. If driver sends data to firmware during suspend processing and the TX_DONE interrupt is delayed, it may come back at wrong time when SDIO host driver is in the middle of suspending. Block any data from stack while suspending. Also skip sending data that are already in driver tx_queue. Don't purge the skb queue on suspend to avoid intermittent music after system resumes from S3. Signed-off-by: Chin-Ran Lo Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_drv.h | 1 + drivers/bluetooth/btmrvl_main.c | 11 +++++++++-- drivers/bluetooth/btmrvl_sdio.c | 3 ++- 3 files changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index 27a9aac25583..05904732e6f1 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -89,6 +89,7 @@ struct btmrvl_adapter { wait_queue_head_t event_hs_wait_q; u8 cmd_complete; bool is_suspended; + bool is_suspending; }; struct btmrvl_private { diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index b2a567bb1b46..f25a825a693f 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -436,6 +436,11 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("type=%d, len=%d", hci_skb_pkt_type(skb), skb->len); + if (priv->adapter->is_suspending || priv->adapter->is_suspended) { + BT_ERR("%s: Device is suspending or suspended", __func__); + return -EBUSY; + } + switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; @@ -452,7 +457,8 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb) skb_queue_tail(&priv->adapter->tx_queue, skb); - wake_up_interruptible(&priv->main_thread.wait_q); + if (!priv->adapter->is_suspended) + wake_up_interruptible(&priv->main_thread.wait_q); return 0; } @@ -643,7 +649,8 @@ static int btmrvl_service_main_thread(void *data) if (adapter->ps_state == PS_SLEEP) continue; - if (!priv->btmrvl_dev.tx_dnld_rdy) + if (!priv->btmrvl_dev.tx_dnld_rdy || + priv->adapter->is_suspended) continue; skb = skb_dequeue(&adapter->tx_queue); diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 73a1c2779969..6ed8acfcfa9c 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -1545,10 +1545,10 @@ static int btmrvl_sdio_suspend(struct device *dev) } priv = card->priv; + priv->adapter->is_suspending = true; hcidev = priv->btmrvl_dev.hcidev; BT_DBG("%s: SDIO suspend", hcidev->name); hci_suspend_dev(hcidev); - skb_queue_purge(&priv->adapter->tx_queue); if (priv->adapter->hs_state != HS_ACTIVATED) { if (btmrvl_enable_hs(priv)) { @@ -1557,6 +1557,7 @@ static int btmrvl_sdio_suspend(struct device *dev) } } + priv->adapter->is_suspending = false; priv->adapter->is_suspended = true; /* We will keep the power when hs enabled successfully */ -- cgit v1.2.3-59-g8ed1b From 29edf44f858a3f923c16f9f5b1aa790e44b8feb9 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Wed, 6 Jan 2016 13:01:08 +0100 Subject: mlxsw: Fixing vlans init range Initialize VLANs 0..4095 (Remove init for VID 4096). Signed-off-by: Elad Raz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 62159547ebf9..dcf77c9d7649 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1201,7 +1201,8 @@ int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port) * with VID 1. */ mlxsw_sp_port->pvid = 1; - err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID, true); + err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID - 1, + true); if (err) { netdev_err(dev, "Unable to init VLANs\n"); return err; -- cgit v1.2.3-59-g8ed1b From e4a13055077af25d310f61f2b52a9bdfaba45eb6 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Wed, 6 Jan 2016 13:01:09 +0100 Subject: mlxsw: Renaming local variable names for consistency Signed-off-by: Elad Raz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index dcf77c9d7649..ad1bf7f62d79 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -545,15 +545,15 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_vlan *vlan, struct switchdev_trans *trans) { - bool untagged_flag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; - bool pvid_flag = vlan->flags & BRIDGE_VLAN_INFO_PVID; + bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; if (switchdev_trans_ph_prepare(trans)) return 0; return __mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan->vid_begin, vlan->vid_end, - untagged_flag, pvid_flag); + flag_untagged, flag_pvid); } static enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic) -- cgit v1.2.3-59-g8ed1b From 26a4ea0f454c6714aeb2b1e295c1f8d76de94013 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Wed, 6 Jan 2016 13:01:10 +0100 Subject: mlxsw: Disable vlan_filtering for non .1D bridge When a port is bridged, the bridge must be vlan aware bridge (.1Q) or the bridging should be on top of VLAN interfaces (.1D bridge). Signed-off-by: Elad Raz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index ad1bf7f62d79..d6242cf29aa9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -299,6 +299,22 @@ static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port, return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time); } +static int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, + struct switchdev_trans *trans, + struct net_device *orig_dev, + bool vlan_enabled) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + + /* SWITCHDEV_TRANS_PREPARE phase */ + if ((!vlan_enabled) && (mlxsw_sp->master_bridge.dev == orig_dev)) { + netdev_err(mlxsw_sp_port->dev, "Bridge must be vlan-aware\n"); + return -EINVAL; + } + + return 0; +} + static int mlxsw_sp_port_attr_set(struct net_device *dev, const struct switchdev_attr *attr, struct switchdev_trans *trans) @@ -323,6 +339,11 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev, err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans, attr->u.ageing_time); break; + case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: + err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, trans, + attr->orig_dev, + attr->u.vlan_filtering); + break; default: err = -EOPNOTSUPP; break; -- cgit v1.2.3-59-g8ed1b From fc1273afb257663de034260f5b5fbbd8d79d6308 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Wed, 6 Jan 2016 13:01:11 +0100 Subject: mlxsw: Remember untagged VLANs When a vlan is been configured, remeber the untagged mode of the vlan. When displaying the list of configured VLANs, show the untagged attribute. Signed-off-by: Elad Raz Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 8 ++++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 1 + drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 9 ++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 74ff0110e899..b6f365060dd7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1370,6 +1370,11 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port) err = -ENOMEM; goto err_port_active_vlans_alloc; } + mlxsw_sp_port->untagged_vlans = kzalloc(bytes, GFP_KERNEL); + if (!mlxsw_sp_port->untagged_vlans) { + err = -ENOMEM; + goto err_port_untagged_vlans_alloc; + } INIT_LIST_HEAD(&mlxsw_sp_port->vports_list); mlxsw_sp_port->pcpu_stats = @@ -1472,6 +1477,8 @@ err_port_module_check: err_dev_addr_init: free_percpu(mlxsw_sp_port->pcpu_stats); err_alloc_stats: + kfree(mlxsw_sp_port->untagged_vlans); +err_port_untagged_vlans_alloc: kfree(mlxsw_sp_port->active_vlans); err_port_active_vlans_alloc: free_netdev(dev); @@ -1505,6 +1512,7 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port) mlxsw_sp_port_vports_fini(mlxsw_sp_port); mlxsw_sp_port_switchdev_fini(mlxsw_sp_port); free_percpu(mlxsw_sp_port->pcpu_stats); + kfree(mlxsw_sp_port->untagged_vlans); kfree(mlxsw_sp_port->active_vlans); free_netdev(mlxsw_sp_port->dev); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 463ed6dcc709..7601789dd522 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -144,6 +144,7 @@ struct mlxsw_sp_port { } vport; /* 802.1Q bridge VLANs */ unsigned long *active_vlans; + unsigned long *untagged_vlans; /* VLAN interfaces */ struct list_head vports_list; }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index d6242cf29aa9..614ef57ceefa 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -526,8 +526,13 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, } /* Changing activity bits only if HW operation succeded */ - for (vid = vid_begin; vid <= vid_end; vid++) + for (vid = vid_begin; vid <= vid_end; vid++) { set_bit(vid, mlxsw_sp_port->active_vlans); + if (flag_untagged) + set_bit(vid, mlxsw_sp_port->untagged_vlans); + else + clear_bit(vid, mlxsw_sp_port->untagged_vlans); + } /* STP state change must be done after we set active VLANs */ err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, @@ -954,6 +959,8 @@ static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port, vlan->flags = 0; if (vid == mlxsw_sp_port->pvid) vlan->flags |= BRIDGE_VLAN_INFO_PVID; + if (test_bit(vid, mlxsw_sp_port->untagged_vlans)) + vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED; vlan->vid_begin = vid; vlan->vid_end = vid; err = cb(&vlan->obj); -- cgit v1.2.3-59-g8ed1b From c406700cdf882b89cb036117414fcd8b0cc2656d Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Wed, 6 Jan 2016 09:36:37 -0500 Subject: ethernet/atheros/alx: sanitize buffer sizing and padding This is based on the work done by Przemek Rudy in bug 70761 at bugzilla.kernel.org, but with some work done to disentagle and clarify things a bit. Similar to Przemek's work and other drivers, we're adding a padding of 16 here, but we're also disentangling mtu size calculations from max buffer size calculations a bit, and adding ETH_HLEN to the value written into ALX_MTU. Hopefully, with a bit more consistency and clarity, things behave better here. Sadly, I can only test in my alx-driven E2200, which worked just fine before this patch. In comment #58 of bug 70761, Eugene A. Shatokhin reports that this patch does help considerably for a ROSA Linux user of his with an AR8162 network adapter when patched into a 4.1.x-based kernel, with several days of normal operation where wired network previously wasn't usable without setting MTU to 9000 as a work-around. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=70761 CC: "Eugene A. Shatokhin" CC: Przemek Rudy CC: Jay Cliburn CC: Chris Snook CC: netdev@vger.kernel.org Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/hw.c | 10 +++++----- drivers/net/ethernet/atheros/alx/hw.h | 9 ++++++--- drivers/net/ethernet/atheros/alx/main.c | 7 +++---- 3 files changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/atheros/alx/hw.c b/drivers/net/ethernet/atheros/alx/hw.c index 7712f068f6d4..1fe35e453d43 100644 --- a/drivers/net/ethernet/atheros/alx/hw.c +++ b/drivers/net/ethernet/atheros/alx/hw.c @@ -958,13 +958,13 @@ void alx_configure_basic(struct alx_hw *hw) alx_write_mem32(hw, ALX_TINT_TPD_THRSHLD, hw->ith_tpd); alx_write_mem32(hw, ALX_TINT_TIMER, hw->imt); - raw_mtu = hw->mtu + ETH_HLEN; - alx_write_mem32(hw, ALX_MTU, raw_mtu + 8); - if (raw_mtu > ALX_MTU_JUMBO_TH) + raw_mtu = ALX_RAW_MTU(hw->mtu); + alx_write_mem32(hw, ALX_MTU, raw_mtu); + if (raw_mtu > (ALX_MTU_JUMBO_TH + ETH_FCS_LEN + VLAN_HLEN)) hw->rx_ctrl &= ~ALX_MAC_CTRL_FAST_PAUSE; - if ((raw_mtu + 8) < ALX_TXQ1_JUMBO_TSO_TH) - val = (raw_mtu + 8 + 7) >> 3; + if (raw_mtu < ALX_TXQ1_JUMBO_TSO_TH) + val = (raw_mtu + 7) >> 3; else val = ALX_TXQ1_JUMBO_TSO_TH >> 3; alx_write_mem32(hw, ALX_TXQ1, val | ALX_TXQ1_ERRLGPKT_DROP_EN); diff --git a/drivers/net/ethernet/atheros/alx/hw.h b/drivers/net/ethernet/atheros/alx/hw.h index 15548802d6f8..f289c05f5cb4 100644 --- a/drivers/net/ethernet/atheros/alx/hw.h +++ b/drivers/net/ethernet/atheros/alx/hw.h @@ -37,6 +37,7 @@ #include #include #include +#include #include "reg.h" /* Transmit Packet Descriptor, contains 4 32-bit words. @@ -343,12 +344,14 @@ struct alx_rrd { ALX_RSS_HASH_TYPE_IPV4_TCP | \ ALX_RSS_HASH_TYPE_IPV6 | \ ALX_RSS_HASH_TYPE_IPV6_TCP) -#define ALX_DEF_RXBUF_SIZE 1536 +#define ALX_FRAME_PAD 16 +#define ALX_RAW_MTU(_mtu) (_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN) +#define ALX_MAX_FRAME_LEN(_mtu) (ALIGN((ALX_RAW_MTU(_mtu) + ALX_FRAME_PAD), 8)) +#define ALX_DEF_RXBUF_SIZE ALX_MAX_FRAME_LEN(1500) #define ALX_MAX_JUMBO_PKT_SIZE (9*1024) #define ALX_MAX_TSO_PKT_SIZE (7*1024) #define ALX_MAX_FRAME_SIZE ALX_MAX_JUMBO_PKT_SIZE -#define ALX_MIN_FRAME_SIZE 68 -#define ALX_RAW_MTU(_mtu) (_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN) +#define ALX_MIN_FRAME_SIZE (ETH_ZLEN + ETH_FCS_LEN + VLAN_HLEN) #define ALX_MAX_RX_QUEUES 8 #define ALX_MAX_TX_QUEUES 4 diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index d3763bc2c561..55b118e876fd 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -704,7 +704,7 @@ static int alx_init_sw(struct alx_priv *alx) hw->smb_timer = 400; hw->mtu = alx->dev->mtu; - alx->rxbuf_size = ALIGN(ALX_RAW_MTU(hw->mtu), 8); + alx->rxbuf_size = ALX_MAX_FRAME_LEN(hw->mtu); alx->tx_ringsz = 256; alx->rx_ringsz = 512; hw->imt = 200; @@ -805,7 +805,7 @@ static void alx_reinit(struct alx_priv *alx) static int alx_change_mtu(struct net_device *netdev, int mtu) { struct alx_priv *alx = netdev_priv(netdev); - int max_frame = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + int max_frame = ALX_MAX_FRAME_LEN(mtu); if ((max_frame < ALX_MIN_FRAME_SIZE) || (max_frame > ALX_MAX_FRAME_SIZE)) @@ -816,8 +816,7 @@ static int alx_change_mtu(struct net_device *netdev, int mtu) netdev->mtu = mtu; alx->hw.mtu = mtu; - alx->rxbuf_size = mtu > ALX_DEF_RXBUF_SIZE ? - ALIGN(max_frame, 8) : ALX_DEF_RXBUF_SIZE; + alx->rxbuf_size = max(max_frame, ALX_DEF_RXBUF_SIZE); netdev_update_features(netdev); if (netif_running(netdev)) alx_reinit(alx); -- cgit v1.2.3-59-g8ed1b From 719255d0e29463ddba2d01a72c1e74092793103a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 6 Jan 2016 12:56:30 +0300 Subject: mlxsw: core: remove an unnecessary condition We checked "err" on the lines before so we know it's zero here. These cause a static checker warning because checking known things can indicate a bug. Maybe there is a missing assignment or we are checking the wrong variable. Signed-off-by: Dan Carpenter Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c index 5b9364f4837d..1ac8bf187168 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c @@ -130,7 +130,7 @@ static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev, dev_err(mlxsw_hwmon->bus_info->dev, "Failed to reset temp sensor history\n"); return err; } - return err ? err : len; + return len; } static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev, -- cgit v1.2.3-59-g8ed1b From e06a03bdf8781d6a93a13d5bdf33d058ca498a86 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 6 Jan 2016 12:58:09 +0300 Subject: fsl/fman: fix the pause_time test pause_time is unsigned so it can't be less than zero. The bug means that we allow invalid pause-times. Fixes: 57ba4c9b56d8 ('fsl/fman: Add FMan MAC support') Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/fman_dtsec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c index d78e2ba73ffa..587f9b40cfaa 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -934,7 +934,7 @@ int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */ if (dtsec->fm_rev_info.major == 2) - if (pause_time < 0 && pause_time <= 320) { + if (pause_time <= 320) { pr_warn("pause-time: %d illegal.Should be > 320\n", pause_time); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 9e02d8caaf90077bc47e054e4c3a41736245a9f8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 6 Jan 2016 12:59:10 +0300 Subject: fsl/fman: double free on probe failure "priv" is allocated with devm_kzalloc() so freeing it here with kfree() will lead to a double free. Fixes: 3933961682a3 ('fsl/fman: Add FMan MAC driver') Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/mac.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c index 743a393ba657..e33d9d24c1db 100644 --- a/drivers/net/ethernet/freescale/fman/mac.c +++ b/drivers/net/ethernet/freescale/fman/mac.c @@ -961,7 +961,6 @@ _return_of_node_put: of_node_put(dev_node); _return_dev_set_drvdata: kfree(priv->fixed_link); - kfree(priv); dev_set_drvdata(dev, NULL); _return: return err; -- cgit v1.2.3-59-g8ed1b From 9ec855cc9e17fe111e1f868027a75ac7ae849997 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 30 Dec 2015 12:20:49 +0100 Subject: iwlegacy: 4965-mac: constify il_sensitivity_ranges structure The il_sensitivity_ranges is never modified, so declare it as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/iwlegacy/4965-mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 6656215a13a9..fd38aa0763e4 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -6416,7 +6416,7 @@ il4965_hw_detect(struct il_priv *il) D_INFO("HW Revision ID = 0x%X\n", il->rev_id); } -static struct il_sensitivity_ranges il4965_sensitivity = { +static const struct il_sensitivity_ranges il4965_sensitivity = { .min_nrg_cck = 97, .max_nrg_cck = 0, /* not used, set to 0 */ -- cgit v1.2.3-59-g8ed1b From 50f85e220f6f51798429a3cc74e7c76d27513bef Mon Sep 17 00:00:00 2001 From: Insu Yun Date: Wed, 30 Dec 2015 11:01:44 -0500 Subject: mwifiex: correctly handling kzalloc Since kzalloc can be failed in memory pressure, it needs to be handled, otherwise NULL dereference could be happened Signed-off-by: Insu Yun Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sdio.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 78a8474e1a3d..a8af72d02c44 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -2053,8 +2053,19 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) /* Allocate skb pointer buffers */ card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) * card->mp_agg_pkt_limit, GFP_KERNEL); + if (!card->mpa_rx.skb_arr) { + kfree(card->mp_regs); + return -ENOMEM; + } + card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) * card->mp_agg_pkt_limit, GFP_KERNEL); + if (!card->mpa_rx.len_arr) { + kfree(card->mp_regs); + kfree(card->mpa_rx.skb_arr); + return -ENOMEM; + } + ret = mwifiex_alloc_sdio_mpa_buffers(adapter, card->mp_tx_agg_buf_size, card->mp_rx_agg_buf_size); -- cgit v1.2.3-59-g8ed1b From fdb1e28e05c9d1d6d9c3226e5443ad12e2032e00 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 31 Dec 2015 06:24:12 -0800 Subject: mwifiex: fix missing debug messages Some critical messages are missed until "adapter->dev" gets initialized in mwifiex_register_dev(). We will use pr_* print message instead of mwifiex_dbg at those places to resolve the problem. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 9703848ba9f8..eb0b386f1a33 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -2473,50 +2473,44 @@ static int mwifiex_pcie_init(struct mwifiex_adapter *adapter) pci_set_master(pdev); - mwifiex_dbg(adapter, INFO, - "try set_consistent_dma_mask(32)\n"); + pr_notice("try set_consistent_dma_mask(32)\n"); ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { - mwifiex_dbg(adapter, ERROR, - "set_dma_mask(32) failed\n"); + pr_err("set_dma_mask(32) failed\n"); goto err_set_dma_mask; } ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { - mwifiex_dbg(adapter, ERROR, - "set_consistent_dma_mask(64) failed\n"); + pr_err("set_consistent_dma_mask(64) failed\n"); goto err_set_dma_mask; } ret = pci_request_region(pdev, 0, DRV_NAME); if (ret) { - mwifiex_dbg(adapter, ERROR, - "req_reg(0) error\n"); + pr_err("req_reg(0) error\n"); goto err_req_region0; } card->pci_mmap = pci_iomap(pdev, 0, 0); if (!card->pci_mmap) { - mwifiex_dbg(adapter, ERROR, "iomap(0) error\n"); + pr_err("iomap(0) error\n"); ret = -EIO; goto err_iomap0; } ret = pci_request_region(pdev, 2, DRV_NAME); if (ret) { - mwifiex_dbg(adapter, ERROR, "req_reg(2) error\n"); + pr_err("req_reg(2) error\n"); goto err_req_region2; } card->pci_mmap1 = pci_iomap(pdev, 2, 0); if (!card->pci_mmap1) { - mwifiex_dbg(adapter, ERROR, - "iomap(2) error\n"); + pr_err("iomap(2) error\n"); ret = -EIO; goto err_iomap2; } - mwifiex_dbg(adapter, INFO, - "PCI memory map Virt0: %p PCI memory map Virt2: %p\n", - card->pci_mmap, card->pci_mmap1); + pr_notice("PCI memory map Virt0: %p PCI memory map Virt2: %p\n", + card->pci_mmap, card->pci_mmap1); card->cmdrsp_buf = NULL; ret = mwifiex_pcie_create_txbd_ring(adapter); @@ -2635,11 +2629,11 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) /* save adapter pointer in card */ card->adapter = adapter; + adapter->dev = &pdev->dev; if (mwifiex_pcie_request_irq(adapter)) return -1; - adapter->dev = &pdev->dev; adapter->tx_buf_size = card->pcie.tx_buf_size; adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); -- cgit v1.2.3-59-g8ed1b From 91442431c3f90d69e73f776847058d1ab2efdad1 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 31 Dec 2015 06:24:14 -0800 Subject: mwifiex: increase priority for critical message This patch increase the priority for some critical messages. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 6 +++--- drivers/net/wireless/marvell/mwifiex/sdio.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index eb0b386f1a33..6d0dc40e20e5 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -2129,14 +2129,14 @@ static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) struct mwifiex_adapter *adapter; if (!pdev) { - pr_debug("info: %s: pdev is NULL\n", (u8 *)pdev); + pr_err("info: %s: pdev is NULL\n", __func__); goto exit; } card = pci_get_drvdata(pdev); if (!card || !card->adapter) { - pr_debug("info: %s: card=%p adapter=%p\n", __func__, card, - card ? card->adapter : NULL); + pr_err("info: %s: card=%p adapter=%p\n", __func__, card, + card ? card->adapter : NULL); goto exit; } adapter = card->adapter; diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index a8af72d02c44..4c8cae682c89 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -796,8 +796,8 @@ mwifiex_sdio_interrupt(struct sdio_func *func) card = sdio_get_drvdata(func); if (!card || !card->adapter) { - pr_debug("int: func=%p card=%p adapter=%p\n", - func, card, card ? card->adapter : NULL); + pr_err("int: func=%p card=%p adapter=%p\n", + func, card, card ? card->adapter : NULL); return; } adapter = card->adapter; -- cgit v1.2.3-59-g8ed1b From ee548d4b1036aaba290a4eec68ba11590ad9cc73 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 31 Dec 2015 06:24:15 -0800 Subject: mwifiex: reduce cloned skb queue size Driver supports Tx status ack feature. When hostapd/ wpa_supplicant asks for ack status of an EAPOL/ACTION frame, driver maintains a cloned skb for the packet until TX_STATUS event is received from firmware. Cloned skb queue gets flushed when connection is terminated or driver is unloaded. Let's reduce the queue size to avoid unnecessarily keeping memory allocated when environment is busy. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 969ca1e1f3e9..79c16de8743e 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -763,7 +763,7 @@ mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, spin_lock_irqsave(&priv->ack_status_lock, flags); id = idr_alloc(&priv->ack_status_frames, orig_skb, - 1, 0xff, GFP_ATOMIC); + 1, 0x10, GFP_ATOMIC); spin_unlock_irqrestore(&priv->ack_status_lock, flags); if (id >= 0) { -- cgit v1.2.3-59-g8ed1b From f7b7caa488eb209f1c63cc98104407c62753e406 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 2 Jan 2016 02:12:38 +0300 Subject: ipw2x00: add checks for dma mapping errors ipw2100_alloc_skb() and ipw2100_tx_send_data() do not check if mapping dma memory succeed. The patch adds the checks and failure handling. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/ipw2x00/ipw2100.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index 36818c7f30b9..f93a7f71c047 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -2311,8 +2311,10 @@ static int ipw2100_alloc_skb(struct ipw2100_priv *priv, packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data, sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE); - /* NOTE: pci_map_single does not return an error code, and 0 is a valid - * dma_addr */ + if (pci_dma_mapping_error(priv->pci_dev, packet->dma_addr)) { + dev_kfree_skb(packet->skb); + return -ENOMEM; + } return 0; } @@ -3183,6 +3185,11 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv) LIBIPW_3ADDR_LEN, tbd->buf_length, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(priv->pci_dev, + tbd->host_addr)) { + IPW_DEBUG_TX("dma mapping error\n"); + break; + } IPW_DEBUG_TX("data frag tbd TX%d P=%08x L=%d\n", txq->next, tbd->host_addr, -- cgit v1.2.3-59-g8ed1b From 44129ed04b2b01c3ce9421bd3f530bd6d558e8cd Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Sat, 2 Jan 2016 09:41:36 +0100 Subject: brcmfmac: add arp offload ip address table configuration support Obtain ipv4 address through inetaddr notification for ARP offload host ip table configuration. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/core.c | 108 +++++++++++++++++++++ .../wireless/broadcom/brcm80211/brcmfmac/core.h | 2 + 2 files changed, 110 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 3a39192e3f14..4c8f7bf4227c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -620,6 +621,8 @@ static int brcmf_netdev_stop(struct net_device *ndev) brcmf_cfg80211_down(ndev); + brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear", NULL, 0); + brcmf_net_setcarrier(ifp, false); return 0; @@ -940,6 +943,98 @@ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr) return available ? bsscfgidx : -ENOMEM; } +#ifdef CONFIG_INET +#define ARPOL_MAX_ENTRIES 8 +static int brcmf_inetaddr_changed(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub, + inetaddr_notifier); + struct in_ifaddr *ifa = data; + struct net_device *ndev = ifa->ifa_dev->dev; + struct brcmf_if *ifp; + int idx, i, ret; + u32 val; + __be32 addr_table[ARPOL_MAX_ENTRIES] = {0}; + + /* Find out if the notification is meant for us */ + for (idx = 0; idx < BRCMF_MAX_IFS; idx++) { + ifp = drvr->iflist[idx]; + if (ifp && ifp->ndev == ndev) + break; + if (idx == BRCMF_MAX_IFS - 1) + return NOTIFY_DONE; + } + + /* check if arp offload is supported */ + ret = brcmf_fil_iovar_int_get(ifp, "arpoe", &val); + if (ret) + return NOTIFY_OK; + + /* old version only support primary index */ + ret = brcmf_fil_iovar_int_get(ifp, "arp_version", &val); + if (ret) + val = 1; + if (val == 1) + ifp = drvr->iflist[0]; + + /* retrieve the table from firmware */ + ret = brcmf_fil_iovar_data_get(ifp, "arp_hostip", addr_table, + sizeof(addr_table)); + if (ret) { + brcmf_err("fail to get arp ip table err:%d\n", ret); + return NOTIFY_OK; + } + + for (i = 0; i < ARPOL_MAX_ENTRIES; i++) + if (ifa->ifa_address == addr_table[i]) + break; + + switch (action) { + case NETDEV_UP: + if (i == ARPOL_MAX_ENTRIES) { + brcmf_dbg(TRACE, "add %pI4 to arp table\n", + &ifa->ifa_address); + /* set it directly */ + ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip", + &ifa->ifa_address, sizeof(ifa->ifa_address)); + if (ret) + brcmf_err("add arp ip err %d\n", ret); + } + break; + case NETDEV_DOWN: + if (i < ARPOL_MAX_ENTRIES) { + addr_table[i] = 0; + brcmf_dbg(TRACE, "remove %pI4 from arp table\n", + &ifa->ifa_address); + /* clear the table in firmware */ + ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear", + NULL, 0); + if (ret) { + brcmf_err("fail to clear arp ip table err:%d\n", + ret); + return NOTIFY_OK; + } + for (i = 0; i < ARPOL_MAX_ENTRIES; i++) { + if (addr_table[i] != 0) { + brcmf_fil_iovar_data_set(ifp, + "arp_hostip", &addr_table[i], + sizeof(addr_table[i])); + if (ret) + brcmf_err("add arp ip err %d\n", + ret); + } + } + } + break; + default: + break; + } + + return NOTIFY_OK; +} +#endif + int brcmf_attach(struct device *dev) { struct brcmf_pub *drvr = NULL; @@ -1068,6 +1163,15 @@ int brcmf_bus_start(struct device *dev) if (p2p_ifp) ret = brcmf_net_p2p_attach(p2p_ifp); } + + if (ret) + goto fail; + +#ifdef CONFIG_INET + drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed; + ret = register_inetaddr_notifier(&drvr->inetaddr_notifier); +#endif + fail: if (ret < 0) { brcmf_err("failed: %d\n", ret); @@ -1133,6 +1237,10 @@ void brcmf_detach(struct device *dev) if (drvr == NULL) return; +#ifdef CONFIG_INET + unregister_inetaddr_notifier(&drvr->inetaddr_notifier); +#endif + /* stop firmware event handling */ brcmf_fweh_detach(drvr); if (drvr->config) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 77d8239d9536..6018af72bab1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -141,6 +141,8 @@ struct brcmf_pub { #ifdef DEBUG struct dentry *dbgfs_dir; #endif + + struct notifier_block inetaddr_notifier; }; /* forward declarations */ -- cgit v1.2.3-59-g8ed1b From 3f5893d1b30a92f3fac1587750b2402c40d66651 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 2 Jan 2016 09:41:37 +0100 Subject: brcmfmac: Add get_station support for IBSS When get_station is requested for IBSS then an error will be printed and no information will be returned. This patch adds IBSS specific get station information function. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 51 ++++++++++++++++++++++ .../wireless/broadcom/brcm80211/brcmfmac/fwil.h | 1 + .../broadcom/brcm80211/brcmfmac/fwil_types.h | 17 ++++++++ 3 files changed, 69 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 17658b326a6c..6b9339b83abc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -2428,6 +2428,54 @@ static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si) si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; } +static s32 +brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp, + struct station_info *sinfo) +{ + struct brcmf_scb_val_le scbval; + struct brcmf_pktcnt_le pktcnt; + s32 err; + u32 rate; + u32 rssi; + + /* Get the current tx rate */ + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate); + if (err < 0) { + brcmf_err("BRCMF_C_GET_RATE error (%d)\n", err); + return err; + } + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); + sinfo->txrate.legacy = rate * 5; + + memset(&scbval, 0, sizeof(scbval)); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval, + sizeof(scbval)); + if (err) { + brcmf_err("BRCMF_C_GET_RSSI error (%d)\n", err); + return err; + } + rssi = le32_to_cpu(scbval.val); + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); + sinfo->signal = rssi; + + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt, + sizeof(pktcnt)); + if (err) { + brcmf_err("BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err); + return err; + } + sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) | + BIT(NL80211_STA_INFO_RX_DROP_MISC) | + BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_TX_FAILED); + sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt); + sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt); + sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt); + sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt); + + return 0; +} + static s32 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, const u8 *mac, struct station_info *sinfo) @@ -2445,6 +2493,9 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; + if (brcmf_is_ibssmode(ifp->vif)) + return brcmf_cfg80211_get_station_ibss(ifp, sinfo); + memset(&sta_info_le, 0, sizeof(sta_info_le)); memcpy(&sta_info_le, mac, ETH_ALEN); err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info", diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h index b20fc0f82a48..6b72df17744e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h @@ -70,6 +70,7 @@ #define BRCMF_C_SET_WSEC 134 #define BRCMF_C_GET_PHY_NOISE 135 #define BRCMF_C_GET_BSS_INFO 136 +#define BRCMF_C_GET_GET_PKTCNTS 137 #define BRCMF_C_GET_BANDLIST 140 #define BRCMF_C_SET_SCB_TIMEOUT 158 #define BRCMF_C_GET_ASSOCLIST 159 diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 94d34ad3a6e6..0b1e46d67e84 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -751,4 +751,21 @@ struct brcmf_pno_scanresults_le { __le32 count; }; +/** + * struct brcmf_pktcnt_le - packet counters. + * + * @rx_good_pkt: packets (MSDUs & MMPDUs) received from this station + * @rx_bad_pkt: failed rx packets + * @tx_good_pkt: packets (MSDUs & MMPDUs) transmitted to this station + * @tx_bad_pkt: failed tx packets + * @rx_ocast_good_pkt: unicast packets destined for others + */ +struct brcmf_pktcnt_le { + __le32 rx_good_pkt; + __le32 rx_bad_pkt; + __le32 tx_good_pkt; + __le32 tx_bad_pkt; + __le32 rx_ocast_good_pkt; +}; + #endif /* FWIL_TYPES_H_ */ -- cgit v1.2.3-59-g8ed1b From 48ed16e86b282309afa1b911271cb39fbcfb9f06 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 2 Jan 2016 09:41:38 +0100 Subject: brcmfmac: Add support for scheduled scan mac randomization Scheduled scan be requested to use mac randomization. This patch checks the flags and enables the randomization if desired. 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: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 54 +++++++++++++++++++--- .../wireless/broadcom/brcm80211/brcmfmac/feature.c | 10 ++++ .../wireless/broadcom/brcm80211/brcmfmac/feature.h | 4 +- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 17 +++++++ 4 files changed, 77 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 6b9339b83abc..05843b783d7f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3544,9 +3544,14 @@ static int brcmf_dev_pno_clean(struct net_device *ndev) return ret; } -static int brcmf_dev_pno_config(struct net_device *ndev) +static int brcmf_dev_pno_config(struct brcmf_if *ifp, + struct cfg80211_sched_scan_request *request) { struct brcmf_pno_param_le pfn_param; + struct brcmf_pno_macaddr_le pfn_mac; + s32 err; + u8 *mac_mask; + int i; memset(&pfn_param, 0, sizeof(pfn_param)); pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); @@ -3559,8 +3564,37 @@ static int brcmf_dev_pno_config(struct net_device *ndev) /* set up pno scan fr */ pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); - return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set", - &pfn_param, sizeof(pfn_param)); + err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, + sizeof(pfn_param)); + if (err) { + brcmf_err("pfn_set failed, err=%d\n", err); + return err; + } + + /* Find out if mac randomization should be turned on */ + if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) + return 0; + + pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; + pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; + + memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); + mac_mask = request->mac_addr_mask; + for (i = 0; i < ETH_ALEN; i++) { + pfn_mac.mac[i] &= mac_mask[i]; + pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); + } + /* Clear multi bit */ + pfn_mac.mac[0] &= 0xFE; + /* Set locally administered */ + pfn_mac.mac[0] |= 0x02; + + err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, + sizeof(pfn_mac)); + if (err) + brcmf_err("pfn_macaddr failed, err=%d\n", err); + + return err; } static int @@ -3614,11 +3648,8 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, } /* configure pno */ - ret = brcmf_dev_pno_config(ndev); - if (ret < 0) { - brcmf_err("PNO setup failed!! ret=%d\n", ret); + if (brcmf_dev_pno_config(ifp, request)) return -EINVAL; - } /* configure each match set */ for (i = 0; i < request->n_match_sets; i++) { @@ -6455,6 +6486,15 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto wiphy_unreg_out; } + /* Fill in some of the advertised nl80211 supported features */ + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) { + wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR; +#ifdef CONFIG_PM + if (wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT) + wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR; +#endif + } + return cfg; wiphy_unreg_out: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index d9d1ca4b93ec..e7ac8a294bb7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -18,10 +18,12 @@ #include #include +#include #include "core.h" #include "bus.h" #include "debug.h" #include "fwil.h" +#include "fwil_types.h" #include "feature.h" @@ -129,6 +131,8 @@ static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp, void brcmf_feat_attach(struct brcmf_pub *drvr) { struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); + struct brcmf_pno_macaddr_le pfn_mac; + s32 err; brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); @@ -140,6 +144,12 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable"); + pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; + err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac, + sizeof(pfn_mac)); + if (!err) + ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC); + if (brcmf_feature_disable) { brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", ifp->drvr->feat_flags, brcmf_feature_disable); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index 13906568bc2d..2e2479d41337 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -26,6 +26,7 @@ * P2P: peer-to-peer * RSDB: Real Simultaneous Dual Band * TDLS: Tunneled Direct Link Setup + * SCAN_RANDOM_MAC: Random MAC during (net detect) scheduled scan. */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ @@ -34,7 +35,8 @@ BRCMF_FEAT_DEF(WOWL) \ BRCMF_FEAT_DEF(P2P) \ BRCMF_FEAT_DEF(RSDB) \ - BRCMF_FEAT_DEF(TDLS) + BRCMF_FEAT_DEF(TDLS) \ + BRCMF_FEAT_DEF(SCAN_RANDOM_MAC) /* * Quirks: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 0b1e46d67e84..bf2df49d7098 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -128,6 +128,10 @@ #define BRCMF_MAXPMKID 16 /* max # PMKID cache entries */ +#define BRCMF_PFN_MACADDR_CFG_VER 1 +#define BRCMF_PFN_MAC_OUI_ONLY BIT(0) +#define BRCMF_PFN_SET_MAC_UNASSOC BIT(1) + /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { BRCMF_JOIN_PREF_RSSI = 1, @@ -751,6 +755,19 @@ struct brcmf_pno_scanresults_le { __le32 count; }; +/** + * struct brcmf_pno_macaddr_le - to configure PNO macaddr randomization. + * + * @version: PNO version identifier. + * @flags: Flags defining how mac addrss should be used. + * @mac: MAC address. + */ +struct brcmf_pno_macaddr_le { + u8 version; + u8 flags; + u8 mac[ETH_ALEN]; +}; + /** * struct brcmf_pktcnt_le - packet counters. * -- cgit v1.2.3-59-g8ed1b From ec64241c9fd2873979cf9c05eeaaa4cabe12032a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 2 Jan 2016 09:41:39 +0100 Subject: brcmfmac: obtain feature info using 'cap' firmware command Several features in the driver directly map to a firmware feature listed in response of the 'cap' firmware command. For those features this response will be examined instead of attempting individual firmware commands. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Reviewed-by: Mathy Vanhoef Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/feature.c | 53 +++++++++++++--------- 1 file changed, 31 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index e7ac8a294bb7..d41f343f4838 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -42,6 +42,17 @@ static const char *brcmf_feat_names[] = { }; #undef BRCMF_FEAT_DEF +struct brcmf_feat_fwcap { + enum brcmf_feat_id feature; + const char * const fwcap_id; +}; + +static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = { + { BRCMF_FEAT_MBSS, "mbss" }, + { BRCMF_FEAT_MCHAN, "mchan" }, + { BRCMF_FEAT_P2P, "p2p" }, +}; + #ifdef DEBUG /* * expand quirk list to array of quirk strings. @@ -106,25 +117,22 @@ static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp, } } -/** - * brcmf_feat_iovar_int_set() - determine feature through iovar set. - * - * @ifp: interface to query. - * @id: feature id. - * @name: iovar name. - */ -static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp, - enum brcmf_feat_id id, char *name, u32 val) +static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp) { - int err; - - err = brcmf_fil_iovar_int_set(ifp, name, val); - if (err == 0) { - brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); - ifp->drvr->feat_flags |= BIT(id); - } else { - brcmf_dbg(TRACE, "%s feature check failed: %d\n", - brcmf_feat_names[id], err); + char caps[256]; + enum brcmf_feat_id id; + int i; + + brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps)); + brcmf_dbg(INFO, "[ %s]\n", caps); + + for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) { + if (strnstr(caps, brcmf_fwcap_map[i].fwcap_id, sizeof(caps))) { + id = brcmf_fwcap_map[i].feature; + brcmf_dbg(INFO, "enabling feature: %s\n", + brcmf_feat_names[id]); + ifp->drvr->feat_flags |= BIT(id); + } } } @@ -134,13 +142,14 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) struct brcmf_pno_macaddr_le pfn_mac; s32 err; - brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); + brcmf_feat_firmware_capabilities(ifp); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); - if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) - brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); - brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p"); + /* MBSS does not work for 43362 */ + if (drvr->bus_if->chip == BRCM_CC_43362_CHIP_ID) + ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable"); -- cgit v1.2.3-59-g8ed1b From 6a98d64a27de16f88b97a493577a5fb9d18ec000 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 2 Jan 2016 09:41:40 +0100 Subject: brcmfmac: Fix warn trace on module unload while in ibss mode When the driver is being unloaded a situation can occur where the wirelesss core (cfg80211) wants to remove the ibss, but the state of brcmfmac has already been set to down. When an error is returned in that situation then that will result in a stack trace on removal of the wiphy object. This is avoided by returning 0 when device is down on a leave_ibss call. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 05843b783d7f..dc14dd483779 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1448,8 +1448,13 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) struct brcmf_if *ifp = netdev_priv(ndev); brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; + if (!check_vif_up(ifp->vif)) { + /* When driver is being unloaded, it can end up here. If an + * error is returned then later on a debug trace in the wireless + * core module will be printed. To avoid this 0 is returned. + */ + return 0; + } brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING); -- cgit v1.2.3-59-g8ed1b From 7d34b05605676736c6695ccdcec547055d07468f Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 2 Jan 2016 09:41:41 +0100 Subject: brcmfmac: Move all module parameters to one place Module parameters are defined in several files. Move them in one place and make them device specific or global. This makes it easier to override device specific settings by external data like platform data in the future. 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: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 12 ++--- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 13 ++--- .../wireless/broadcom/brcm80211/brcmfmac/common.c | 63 ++++++++++++++++++++++ .../wireless/broadcom/brcm80211/brcmfmac/common.h | 43 +++++++++++++++ .../wireless/broadcom/brcm80211/brcmfmac/core.c | 22 ++++---- .../wireless/broadcom/brcm80211/brcmfmac/core.h | 5 +- .../wireless/broadcom/brcm80211/brcmfmac/feature.c | 13 ++--- .../broadcom/brcm80211/brcmfmac/firmware.c | 16 +++--- .../broadcom/brcm80211/brcmfmac/fwsignal.c | 9 ++-- 9 files changed, 144 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index 410a6645d316..53637399bb99 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -47,6 +47,8 @@ #include "debug.h" #include "sdio.h" #include "of.h" +#include "core.h" +#include "common.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 @@ -57,7 +59,6 @@ /* Maximum milliseconds to wait for F2 to come up */ #define SDIO_WAIT_F2RDY 3000 -#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ #define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */ struct brcmf_sdiod_freezer { @@ -68,10 +69,6 @@ struct brcmf_sdiod_freezer { struct completion resumed; }; -static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; -module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); -MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); - static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id) { struct brcmf_bus *bus_if = dev_get_drvdata(dev_id); @@ -890,7 +887,8 @@ static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) if (!sdiodev->sg_support) return; - nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, brcmf_sdiod_txglomsz); + nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, + sdiodev->bus_if->drvr->settings->sdiod_txglomsz); nents += (nents >> 4) + 1; WARN_ON(nents > sdiodev->max_segment_count); @@ -902,7 +900,7 @@ static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) sdiodev->sg_support = false; } - sdiodev->txglomsz = brcmf_sdiod_txglomsz; + sdiodev->txglomsz = sdiodev->bus_if->drvr->settings->sdiod_txglomsz; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index dc14dd483779..6a7759fcbd86 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -236,10 +236,6 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; -static int brcmf_roamoff; -module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); -MODULE_PARM_DESC(roamoff, "do not use internal roaming engine"); - static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, struct cfg80211_chan_def *ch) @@ -5395,7 +5391,7 @@ static s32 brcmf_dongle_roam(struct brcmf_if *ifp) __le32 roam_delta[2]; /* Configure beacon timeout value based upon roaming setting */ - if (brcmf_roamoff) + if (ifp->drvr->settings->roamoff) bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF; else bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON; @@ -5409,8 +5405,9 @@ static s32 brcmf_dongle_roam(struct brcmf_if *ifp) * roaming. */ brcmf_dbg(INFO, "Internal Roaming = %s\n", - brcmf_roamoff ? "Off" : "On"); - err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff)); + ifp->drvr->settings->roamoff ? "Off" : "On"); + err = brcmf_fil_iovar_int_set(ifp, "roam_off", + ifp->drvr->settings->roamoff); if (err) { brcmf_err("roam_off error (%d)\n", err); goto roam_setup_done; @@ -6082,7 +6079,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; - if (!brcmf_roamoff) + if (!ifp->drvr->settings->roamoff) wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 474de118d0b4..bb9e2b3f5012 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -35,6 +35,40 @@ const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /* boost value for RSSI_DELTA in preferred join selection */ #define BRCMF_JOIN_PREF_RSSI_BOOST 8 +#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ + +static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; +module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); +MODULE_PARM_DESC(txglomsz, "Maximum tx packet chain size [SDIO]"); + +/* Debug level configuration. See debug.h for bits, sysfs modifiable */ +int brcmf_msg_level; +module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(debug, "Level of debug output"); + +static int brcmf_p2p_enable; +module_param_named(p2pon, brcmf_p2p_enable, int, 0); +MODULE_PARM_DESC(p2pon, "Enable legacy p2p management functionality"); + +static int brcmf_feature_disable; +module_param_named(feature_disable, brcmf_feature_disable, int, 0); +MODULE_PARM_DESC(feature_disable, "Disable features"); + +static char brcmf_firmware_path[BRCMF_FW_ALTPATH_LEN]; +module_param_string(alternative_fw_path, brcmf_firmware_path, + BRCMF_FW_ALTPATH_LEN, S_IRUSR); +MODULE_PARM_DESC(alternative_fw_path, "Alternative firmware path"); + +static int brcmf_fcmode; +module_param_named(fcmode, brcmf_fcmode, int, 0); +MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control"); + +static int brcmf_roamoff; +module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); +MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine"); + +struct brcmf_mp_global_t brcmf_mp_global; + int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) { s8 eventmask[BRCMF_EVENTING_MASK_LEN]; @@ -178,3 +212,32 @@ void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) va_end(args); } #endif + +void brcmf_mp_attach(void) +{ + strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path, + BRCMF_FW_ALTPATH_LEN); +} + +int brcmf_mp_device_attach(struct brcmf_pub *drvr) +{ + drvr->settings = kzalloc(sizeof(*drvr->settings), GFP_ATOMIC); + if (!drvr->settings) { + brcmf_err("Failed to alloca storage space for settings\n"); + return -ENOMEM; + } + + drvr->settings->sdiod_txglomsz = brcmf_sdiod_txglomsz; + drvr->settings->p2p_enable = !!brcmf_p2p_enable; + drvr->settings->feature_disable = brcmf_feature_disable; + drvr->settings->fcmode = brcmf_fcmode; + drvr->settings->roamoff = !!brcmf_roamoff; + + return 0; +} + +void brcmf_mp_device_detach(struct brcmf_pub *drvr) +{ + kfree(drvr->settings); +} + diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h index 21c7488b4732..abe3764669a6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h @@ -17,6 +17,49 @@ extern const u8 ALLFFMAC[ETH_ALEN]; +#define BRCMF_FW_ALTPATH_LEN 256 + +/* Definitions for the module global and device specific settings are defined + * here. Two structs are used for them. brcmf_mp_global_t and brcmf_mp_device. + * The mp_global is instantiated once in a global struct and gets initialized + * by the common_attach function which should be called before any other + * (module) initiliazation takes place. The device specific settings is part + * of the drvr struct and should be initialized on every brcmf_attach. + */ + +/** + * struct brcmf_mp_global_t - Global module paramaters. + * + * @firmware_path: Alternative firmware path. + */ +struct brcmf_mp_global_t { + char firmware_path[BRCMF_FW_ALTPATH_LEN]; +}; + +extern struct brcmf_mp_global_t brcmf_mp_global; + +/** + * struct brcmf_mp_device - Device module paramaters. + * + * @sdiod_txglomsz: SDIO txglom size. + * @joinboost_5g_rssi: 5g rssi booost for preferred join selection. + * @p2p_enable: Legacy P2P0 enable (old wpa_supplicant). + * @feature_disable: Feature_disable bitmask. + * @fcmode: FWS flow control. + * @roamoff: Firmware roaming off? + */ +struct brcmf_mp_device { + int sdiod_txglomsz; + int joinboost_5g_rssi; + bool p2p_enable; + int feature_disable; + int fcmode; + bool roamoff; +}; + +void brcmf_mp_attach(void); +int brcmf_mp_device_attach(struct brcmf_pub *drvr); +void brcmf_mp_device_detach(struct brcmf_pub *drvr); /* Sets dongle media info (drv_version, mac address). */ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 4c8f7bf4227c..3fa7bc5ce4b7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -57,16 +57,6 @@ MODULE_LICENSE("Dual BSD/GPL"); #define BRCMF_BSSIDX_INVALID -1 -/* Error bits */ -int brcmf_msg_level; -module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); -MODULE_PARM_DESC(debug, "level of debug output"); - -/* P2P0 enable */ -static int brcmf_p2p_enable; -module_param_named(p2pon, brcmf_p2p_enable, int, 0); -MODULE_PARM_DESC(p2pon, "enable legacy p2p management functionality"); - char *brcmf_ifname(struct brcmf_if *ifp) { if (!ifp) @@ -827,7 +817,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx, } } - if (!brcmf_p2p_enable && is_p2pdev) { + if (!drvr->settings->p2p_enable && is_p2pdev) { /* this is P2P_DEVICE interface */ brcmf_dbg(INFO, "allocate non-netdev interface\n"); ifp = kzalloc(sizeof(*ifp), GFP_KERNEL); @@ -1058,6 +1048,10 @@ int brcmf_attach(struct device *dev) drvr->bus_if = dev_get_drvdata(dev); drvr->bus_if->drvr = drvr; + /* Initialize device specific settings */ + if (brcmf_mp_device_attach(drvr)) + goto fail; + /* attach debug facilities */ brcmf_debug_attach(drvr); @@ -1150,7 +1144,7 @@ int brcmf_bus_start(struct device *dev) brcmf_fws_add_interface(ifp); drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev, - brcmf_p2p_enable); + drvr->settings->p2p_enable); if (drvr->config == NULL) { ret = -ENOMEM; goto fail; @@ -1158,7 +1152,7 @@ int brcmf_bus_start(struct device *dev) ret = brcmf_net_attach(ifp, false); - if ((!ret) && (brcmf_p2p_enable)) { + if ((!ret) && (drvr->settings->p2p_enable)) { p2p_ifp = drvr->iflist[1]; if (p2p_ifp) ret = brcmf_net_p2p_attach(p2p_ifp); @@ -1260,6 +1254,8 @@ void brcmf_detach(struct device *dev) brcmf_proto_detach(drvr); + brcmf_mp_device_detach(drvr); + brcmf_debug_detach(drvr); bus_if->drvr = NULL; kfree(drvr); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 6018af72bab1..8f39435f976f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -69,8 +69,8 @@ struct brcmf_ampdu_rx_reorder { /* Forward decls for struct brcmf_pub (see below) */ struct brcmf_proto; /* device communication protocol info */ -struct brcmf_cfg80211_dev; /* cfg80211 device info */ -struct brcmf_fws_info; /* firmware signalling info */ +struct brcmf_fws_info; /* firmware signalling info */ +struct brcmf_mp_device; /* module paramateres, device specific */ /* * struct brcmf_rev_info @@ -143,6 +143,7 @@ struct brcmf_pub { #endif struct notifier_block inetaddr_notifier; + struct brcmf_mp_device *settings; }; /* forward declarations */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index d41f343f4838..1ffa95f1b8d2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -25,13 +25,9 @@ #include "fwil.h" #include "fwil_types.h" #include "feature.h" +#include "common.h" -/* Module param feature_disable (global for all devices) */ -static int brcmf_feature_disable; -module_param_named(feature_disable, brcmf_feature_disable, int, 0); -MODULE_PARM_DESC(feature_disable, "Disable features"); - /* * expand feature list to array of feature strings. */ @@ -159,10 +155,11 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) if (!err) ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC); - if (brcmf_feature_disable) { + if (drvr->settings->feature_disable) { brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", - ifp->drvr->feat_flags, brcmf_feature_disable); - ifp->drvr->feat_flags &= ~brcmf_feature_disable; + ifp->drvr->feat_flags, + drvr->settings->feature_disable); + ifp->drvr->feat_flags &= ~drvr->settings->feature_disable; } /* set chip related quirks */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index 1e4d5f663036..1365c12b78fc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -23,15 +23,13 @@ #include "debug.h" #include "firmware.h" +#include "core.h" +#include "common.h" #define BRCMF_FW_MAX_NVRAM_SIZE 64000 #define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */ #define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */ -static char brcmf_firmware_path[BRCMF_FW_NAME_LEN]; -module_param_string(alternative_fw_path, brcmf_firmware_path, - BRCMF_FW_NAME_LEN, 0440); - enum nvram_parser_state { IDLE, KEY, @@ -559,13 +557,15 @@ int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev, } /* check if firmware path is provided by module parameter */ - if (brcmf_firmware_path[0] != '\0') { - strlcpy(fw_name, brcmf_firmware_path, BRCMF_FW_NAME_LEN); + if (brcmf_mp_global.firmware_path[0] != '\0') { + strlcpy(fw_name, brcmf_mp_global.firmware_path, + BRCMF_FW_NAME_LEN); if ((nvram_name) && (mapping_table[i].nvram)) - strlcpy(nvram_name, brcmf_firmware_path, + strlcpy(nvram_name, brcmf_mp_global.firmware_path, BRCMF_FW_NAME_LEN); - end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; + end = brcmf_mp_global.firmware_path[ + strlen(brcmf_mp_global.firmware_path) - 1]; if (end != '/') { strlcat(fw_name, "/", BRCMF_FW_NAME_LEN); if ((nvram_name) && (mapping_table[i].nvram)) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index e5f5fac9f9b3..f82c9ab5480b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -36,6 +36,7 @@ #include "p2p.h" #include "cfg80211.h" #include "proto.h" +#include "common.h" /** * DOC: Firmware Signalling @@ -521,10 +522,6 @@ static const int brcmf_fws_prio2fifo[] = { BRCMF_FWS_FIFO_AC_VO }; -static int fcmode; -module_param(fcmode, int, S_IRUSR); -MODULE_PARM_DESC(fcmode, "mode of firmware signalled flow control"); - #define BRCMF_FWS_TLV_DEF(name, id, len) \ case BRCMF_FWS_TYPE_ ## name: \ return len; @@ -2134,10 +2131,10 @@ int brcmf_fws_init(struct brcmf_pub *drvr) /* set linkage back */ fws->drvr = drvr; - fws->fcmode = fcmode; + fws->fcmode = drvr->settings->fcmode; if ((drvr->bus_if->always_use_fws_queue == false) && - (fcmode == BRCMF_FWS_FCMODE_NONE)) { + (fws->fcmode == BRCMF_FWS_FCMODE_NONE)) { fws->avoid_queueing = true; brcmf_dbg(INFO, "FWS queueing will be avoided\n"); return 0; -- cgit v1.2.3-59-g8ed1b From 8ba83d4daab9b71dd27da3765f34d1ffa1fee2ec Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 2 Jan 2016 09:41:42 +0100 Subject: brcmfmac: introduce module parameter to force successful probe The module parameter can be used to ensure the probe succeeds thus claiming the device and allowing post-mortem debugging in case of firmware crash. It is only available when select CONFIG_BRCMDBG. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | 11 ++++++++++- drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h | 13 +++++++++++++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index bb9e2b3f5012..4265b50faa98 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -67,6 +67,13 @@ static int brcmf_roamoff; module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine"); +#ifdef DEBUG +/* always succeed brcmf_bus_start() */ +static int brcmf_ignore_probe_fail; +module_param_named(ignore_probe_fail, brcmf_ignore_probe_fail, int, 0); +MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging"); +#endif + struct brcmf_mp_global_t brcmf_mp_global; int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) @@ -232,7 +239,9 @@ int brcmf_mp_device_attach(struct brcmf_pub *drvr) drvr->settings->feature_disable = brcmf_feature_disable; drvr->settings->fcmode = brcmf_fcmode; drvr->settings->roamoff = !!brcmf_roamoff; - +#ifdef DEBUG + drvr->settings->ignore_probe_fail = !!brcmf_ignore_probe_fail; +#endif return 0; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h index abe3764669a6..3b0a63b98e99 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h @@ -55,11 +55,24 @@ struct brcmf_mp_device { int feature_disable; int fcmode; bool roamoff; + bool ignore_probe_fail; }; void brcmf_mp_attach(void); int brcmf_mp_device_attach(struct brcmf_pub *drvr); void brcmf_mp_device_detach(struct brcmf_pub *drvr); +#ifdef DEBUG +static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr) +{ + return drvr->settings->ignore_probe_fail; +} +#else +static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr) +{ + return false; +} +#endif + /* Sets dongle media info (drv_version, mac address). */ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 3fa7bc5ce4b7..7c75b1acdf00 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -1183,6 +1183,8 @@ fail: brcmf_net_detach(p2p_ifp->ndev); drvr->iflist[0] = NULL; drvr->iflist[1] = NULL; + if (brcmf_ignoring_probe_fail(drvr)) + ret = 0; return ret; } return 0; -- cgit v1.2.3-59-g8ed1b From 7683fe016c010a20b5f2d88b7d8dad198506f5f7 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Mon, 4 Jan 2016 15:55:38 +0800 Subject: rt2x00pci: Disable memory-write-invalidate when the driver exits The driver calls pci_set_mwi to enable memory-write-invalidate when it is initialized, but does not call pci_clear_mwi when it is removed. Many other drivers calls pci_clear_mwi when pci_set_mwi is called, such as r8169, 8139cp and e1000. This patch adds pci_clear_mwi in error handling and removal procedure, which can fix the problem. Signed-off-by: Jia-Ju Bai Acked-by: Helmut Schaa Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2x00pci.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c index d93db4b0371b..eb6dbcd4fddf 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c @@ -149,6 +149,7 @@ exit_free_device: ieee80211_free_hw(hw); exit_release_regions: + pci_clear_mwi(pci_dev); pci_release_regions(pci_dev); exit_disable_device: @@ -173,6 +174,7 @@ void rt2x00pci_remove(struct pci_dev *pci_dev) /* * Free the PCI device data. */ + pci_clear_mwi(pci_dev); pci_disable_device(pci_dev); pci_release_regions(pci_dev); } -- cgit v1.2.3-59-g8ed1b From e33a99e227e430a788467e5a85dc29f6df16b983 Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Thu, 31 Dec 2015 15:26:20 +0200 Subject: ath10k: set SM power save disabled to default value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use SMPS disabled as default because FW does not indicate any support of SMPS. This change will help STAs out that don’t support SMPS from sticking on 1SS, since they don’t have method to change it back to multiple chains. This change also should not affect power consumption of STAs supporting SMPS, because they are capable to switch the mode to dynamic or static either at the end of frame sequence or by using SMPS action frame. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index b4bdeb07a012..6146a293601a 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3860,7 +3860,8 @@ static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar) ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; - ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT; + ht_cap.cap |= + WLAN_HT_CAP_SM_PS_DISABLED << IEEE80211_HT_CAP_SM_PS_SHIFT; if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI) ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; -- cgit v1.2.3-59-g8ed1b From dea16eddb4753129dbcd8dc8d1a58ff0cc4ea38c Mon Sep 17 00:00:00 2001 From: Hamad Kadmany Date: Wed, 16 Dec 2015 17:51:45 +0200 Subject: wil6210: fix kernel OOPS when stopping interface during Rx traffic When network interface is stopping, some resources may be already released by the network stack, and Rx frames cause kernel OOPS (observed one is in netfilter code) Proper solution is to drop packets pending in reorder buffer. Signed-off-by: Hamad Kadmany Signed-off-by: Vladimir Kondratiev Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/rx_reorder.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index e3d1be82f314..32031e7a11d5 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -261,9 +261,19 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, struct wil_tid_ampdu_rx *r) { + int i; + if (!r) return; - wil_release_reorder_frames(wil, r, r->head_seq_num + r->buf_size); + + /* Do not pass remaining frames to the network stack - it may be + * not expecting to get any more Rx. Rx from here may lead to + * kernel OOPS since some per-socket accounting info was already + * released. + */ + for (i = 0; i < r->buf_size; i++) + kfree_skb(r->reorder_buf[i]); + kfree(r->reorder_buf); kfree(r->reorder_time); kfree(r); -- cgit v1.2.3-59-g8ed1b From ea3ade75db690dc47c78a77d71dfd7c2df3bb15d Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 16 Dec 2015 17:51:46 +0200 Subject: wil6210: support for platform specific crash recovery Added a simple interface for platform to perform crash recovery. When firmware crashes, wil driver can notify the platform which can trigger a crash recovery process. During the process the platform can request a ram dump from the wil driver as well as control when firmware recovery will start. This interface allows the platform to implement a more advanced crash recovery, for example to reset dependent subsystems in proper order, or to provide its own notifications during the recovery process. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/interrupt.c | 8 +++-- drivers/net/wireless/ath/wil6210/pcie_bus.c | 30 ++++++++++++++++-- drivers/net/wireless/ath/wil6210/wil6210.h | 1 + drivers/net/wireless/ath/wil6210/wil_crash_dump.c | 3 +- drivers/net/wireless/ath/wil6210/wil_platform.c | 3 +- drivers/net/wireless/ath/wil6210/wil_platform.h | 38 +++++++++++++++++++++-- 6 files changed, 73 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 50c136e843c4..4f2ffa5c6e17 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -394,9 +394,13 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) wil_fw_core_dump(wil); wil_notify_fw_error(wil); isr &= ~ISR_MISC_FW_ERROR; - wil_fw_error_recovery(wil); + if (wil->platform_ops.notify_crash) { + wil_err(wil, "notify platform driver about FW crash"); + wil->platform_ops.notify_crash(wil->platform_handle); + } else { + wil_fw_error_recovery(wil); + } } - if (isr & ISR_MISC_MBOX_EVT) { wil_dbg_irq(wil, "MBOX event\n"); wmi_recv_cmd(wil); diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 1a3142c332e1..e36f2a0c8cb6 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 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 @@ -125,11 +125,37 @@ static int wil_if_pcie_disable(struct wil6210_priv *wil) return 0; } +static int wil_platform_rop_ramdump(void *wil_handle, void *buf, uint32_t size) +{ + struct wil6210_priv *wil = wil_handle; + + if (!wil) + return -EINVAL; + + return wil_fw_copy_crash_dump(wil, buf, size); +} + +static int wil_platform_rop_fw_recovery(void *wil_handle) +{ + struct wil6210_priv *wil = wil_handle; + + if (!wil) + return -EINVAL; + + wil_fw_error_recovery(wil); + + return 0; +} + static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct wil6210_priv *wil; struct device *dev = &pdev->dev; int rc; + const struct wil_platform_rops rops = { + .ramdump = wil_platform_rop_ramdump, + .fw_recovery = wil_platform_rop_fw_recovery, + }; /* check HW */ dev_info(&pdev->dev, WIL_NAME @@ -154,7 +180,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* rollback to if_free */ wil->platform_handle = - wil_platform_init(&pdev->dev, &wil->platform_ops); + wil_platform_init(&pdev->dev, &wil->platform_ops, &rops, wil); if (!wil->platform_handle) { rc = -ENODEV; wil_err(wil, "wil_platform_init failed\n"); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index ade5f3b8274b..235e205ce2bc 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -828,6 +828,7 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime); int wil_suspend(struct wil6210_priv *wil, bool is_runtime); int wil_resume(struct wil6210_priv *wil, bool is_runtime); +int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size); void wil_fw_core_dump(struct wil6210_priv *wil); #endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c index 7e70934990ae..b57d280946e0 100644 --- a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c +++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c @@ -51,8 +51,7 @@ static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil, return 0; } -static int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, - u32 size) +int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size) { int i; const struct fw_map *map; diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.c b/drivers/net/wireless/ath/wil6210/wil_platform.c index 2e831bf20117..4eed05bddb60 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform.c +++ b/drivers/net/wireless/ath/wil6210/wil_platform.c @@ -33,7 +33,8 @@ void wil_platform_modexit(void) * It returns a handle which is used with the rest of the API * */ -void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops) +void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops, + const struct wil_platform_rops *rops, void *wil_handle) { void *handle = ops; /* to return some non-NULL for 'void' impl. */ diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h index d7fa19b7886d..9a949d910343 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform.h +++ b/drivers/net/wireless/ath/wil6210/wil_platform.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Qualcomm Atheros, Inc. + * Copyright (c) 2014-2015 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 @@ -20,16 +20,48 @@ struct device; /** - * struct wil_platform_ops - wil platform module callbacks + * struct wil_platform_ops - wil platform module calls from this + * driver to platform driver */ struct wil_platform_ops { int (*bus_request)(void *handle, uint32_t kbps /* KBytes/Sec */); int (*suspend)(void *handle); int (*resume)(void *handle); void (*uninit)(void *handle); + int (*notify_crash)(void *handle); }; -void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops); +/** + * struct wil_platform_rops - wil platform module callbacks from + * platform driver to this driver + * @ramdump: store a ramdump from the wil firmware. The platform + * driver may add additional data to the ramdump to + * generate the final crash dump. + * @fw_recovery: start a firmware recovery process. Called as + * part of a crash recovery process which may include other + * related platform subsystems. + */ +struct wil_platform_rops { + int (*ramdump)(void *wil_handle, void *buf, uint32_t size); + int (*fw_recovery)(void *wil_handle); +}; + +/** + * wil_platform_init - initialize the platform driver + * + * @dev - pointer to the wil6210 device + * @ops - structure with platform driver operations. Platform + * driver will fill this structure with function pointers. + * @rops - structure with callbacks from platform driver to + * this driver. The platform driver copies the structure to + * its own storage. Can be NULL if this driver does not + * support crash recovery. + * @wil_handle - context for this driver that will be passed + * when platform driver invokes one of the callbacks in + * rops. May be NULL if rops is NULL. + */ +void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops, + const struct wil_platform_rops *rops, void *wil_handle); int __init wil_platform_modinit(void); void wil_platform_modexit(void); -- cgit v1.2.3-59-g8ed1b From 50e81e2f0b0a9fdb9e5bd515270680c7c281ce1f Mon Sep 17 00:00:00 2001 From: Pawel Kulakowski Date: Fri, 18 Dec 2015 10:48:57 +0100 Subject: ath9k: Enable support for cloned SKBS Ath9k driver does not modify tx skbs, so SUPPORTS_CLONED_SKBS flag can be set. Enabling this flag significant reduce number of copy operation during TCP Tx. This is especially noticeable on platforms with slower CPU (lower CPU usage brings profits in better TCP Tx troughput results). Tested on MIPS with 560 MHz clock Without CLONED_SKBS flag: TCP Tx 145 Mb/s (iperf result) __copy_user_common consumes 12.9% of CPU (result from perf tool) 0% CPU Idle With CLONED_SKBS flag: TCP Tx 170 Mb/s (iperf result) __copy_user_common consumes 1.8% of CPU (result from perf tool) 12% CPU Idle Signed-off-by: Pawel Kulakowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/init.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 2e2b92ba96b8..ab7a1ac37849 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -828,6 +828,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) ieee80211_hw_set(hw, RX_INCLUDES_FCS); ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); + ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); if (ath9k_ps_enable) ieee80211_hw_set(hw, SUPPORTS_PS); -- cgit v1.2.3-59-g8ed1b From 19f2ce3f1114c5d59e5e4457ccdd15464070dc5f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 19 Dec 2015 13:59:19 +0300 Subject: ath9k: fix ath9k_hw_nvram_check_version() There is a type bug so it always returns success. Fixes: 6fa658fd5ab2 ('ath9k: Simplify and fix eeprom endianness swapping') Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index f8c5065e5f5f..a7afdeee698c 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -206,7 +206,7 @@ bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev) ath_err(common, "Bad EEPROM VER 0x%04x or REV 0x%04x\n", ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah)); - return -EINVAL; + return false; } return true; -- cgit v1.2.3-59-g8ed1b From 2ec7752fd9b9fe5924973390b784584e7481f52c Mon Sep 17 00:00:00 2001 From: Fengwei Yin Date: Sun, 20 Dec 2015 21:20:40 +0800 Subject: wcn36xx: handle rx skb allocation failure to avoid system crash Lawrence reported that git clone could make system crash on a Qualcomm ARM soc based device (DragonBoard, 1G memory without swap) running 64bit Debian. It's turned out the crash is related with rx skb allocation failure. git could consume more than 600MB anonymous memory. And system is in extremely memory shortage case. But driver didn't handle the rx allocation failure case. This patch doesn't submit skb to upper layer if rx skb allocation fails. Instead, it reuse the old skb for rx DMA again. It's more like drop the packets if system is in memory shortage case. With this change, git clone is OOMed instead of system crash. Reported-by: King, Lawrence Signed-off-by: Fengwei Yin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/dxe.c | 43 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index f8dfa05b290a..473381f483bf 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -474,36 +474,37 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, struct wcn36xx_dxe_desc *dxe = ctl->desc; dma_addr_t dma_addr; struct sk_buff *skb; + int ret = 0, int_mask; + u32 value; + + if (ch->ch_type == WCN36XX_DXE_CH_RX_L) { + value = WCN36XX_DXE_CTRL_RX_L; + int_mask = WCN36XX_DXE_INT_CH1_MASK; + } else { + value = WCN36XX_DXE_CTRL_RX_H; + int_mask = WCN36XX_DXE_INT_CH3_MASK; + } while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) { skb = ctl->skb; dma_addr = dxe->dst_addr_l; - wcn36xx_dxe_fill_skb(wcn->dev, ctl); - - switch (ch->ch_type) { - case WCN36XX_DXE_CH_RX_L: - dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; - wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, - WCN36XX_DXE_INT_CH1_MASK); - break; - case WCN36XX_DXE_CH_RX_H: - dxe->ctrl = WCN36XX_DXE_CTRL_RX_H; - wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, - WCN36XX_DXE_INT_CH3_MASK); - break; - default: - wcn36xx_warn("Unknown channel\n"); - } - - dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE, - DMA_FROM_DEVICE); - wcn36xx_rx_skb(wcn, skb); + ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl); + if (0 == ret) { + /* new skb allocation ok. Use the new one and queue + * the old one to network system. + */ + dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE, + DMA_FROM_DEVICE); + wcn36xx_rx_skb(wcn, skb); + } /* else keep old skb not submitted and use it for rx DMA */ + + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, int_mask); + dxe->ctrl = value; ctl = ctl->next; dxe = ctl->desc; } ch->head_blk_ctl = ctl; - return 0; } -- cgit v1.2.3-59-g8ed1b From 9d5db23eecdb001ac26610d7a87ccdea566feee9 Mon Sep 17 00:00:00 2001 From: Fengwei Yin Date: Sun, 20 Dec 2015 21:20:41 +0800 Subject: wcn36xx: split DMA mask register writing. Per comments from Bjorn Andersson , split DMA mask register writing as seperate patch in case we need bi-sect in the furture. Signed-off-by: Fengwei Yin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/dxe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index 473381f483bf..8643801f31b6 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -498,11 +498,11 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, wcn36xx_rx_skb(wcn, skb); } /* else keep old skb not submitted and use it for rx DMA */ - wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, int_mask); dxe->ctrl = value; ctl = ctl->next; dxe = ctl->desc; } + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, int_mask); ch->head_blk_ctl = ctl; return 0; -- cgit v1.2.3-59-g8ed1b From a1cdb1c59c8c203de2731fc6910598ed19c97e41 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 20 Dec 2015 08:45:40 +0200 Subject: iwlwifi: dvm: fix WoWLAN My commit below introduced a mutex in the transport to prevent concurrent operations. To do so, it added a flag (is_down) to make sure the transport is in the right state. This uncoverred an bug that didn't cause any harm until now: iwldvm calls stop_device and then starts the firmware without calling start_hw in between. While this flow is fine from the device configuration point of view (register, etc...), it is now forbidden by the new is_down flag. This led to this error to appear: iwlwifi 0000:05:00.0: Can't start_fw since the HW hasn't been started and the suspend would fail. This fixes: https://bugzilla.kernel.org/show_bug.cgi?id=109591 CC: [4.3+] Reported-by: Bogdan Bogush Fixes=fa9f3281cbb1 ("iwlwifi: pcie: lock start_hw / start_fw / stop_device") Signed-off-by: Emmanuel Grumbach Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/lib.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c index bee1c03ee259..4841be2aa499 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c @@ -1154,6 +1154,9 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) priv->ucode_loaded = false; iwl_trans_stop_device(priv->trans); + ret = iwl_trans_start_hw(priv->trans); + if (ret) + goto out; priv->wowlan = true; -- cgit v1.2.3-59-g8ed1b From 006bda75d81fd27a583a3b310e9444fea2aa6ef2 Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Thu, 17 Dec 2015 14:17:00 +0200 Subject: iwlwifi: update and fix 7265 series PCI IDs Update and fix some 7265 PCI IDs entries. CC: [3.13+] Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index af106513d38e..6261a68cae90 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -388,6 +388,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_n_cfg)}, {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5C10, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)}, @@ -405,10 +406,10 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x095A, 0x900A, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x9210, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x9310, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7265_2n_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7265_2n_cfg)}, -- cgit v1.2.3-59-g8ed1b From 1e3c3c3529a7d0455b38f74d287d296cb9c786aa Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 13 Dec 2015 09:35:30 +0200 Subject: iwlwifi: mvm: let the firmware choose the antenna for beacons The firmware knows better what antenna to choose. Old firmware still need the setting, so use a flag to know if the driver should choose the antenna or if the firmware can do it iself. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 3 +++ drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index d2294ad67023..9f24f990b705 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -309,6 +309,8 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT + * @IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what + * antenna the beacon should be transmitted * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -336,6 +338,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64, IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67, + IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = (__force iwl_ucode_tlv_capa_t)71, NUM_IWL_UCODE_TLV_CAPA #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 5e3a7582885b..448b9c927b51 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1012,9 +1012,12 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, TX_CMD_FLG_BT_PRIO_POS; beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags); - mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), - mvm->mgmt_last_antenna_idx); + if (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) { + mvm->mgmt_last_antenna_idx = + iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), + mvm->mgmt_last_antenna_idx); + } beacon_cmd.tx.rate_n_flags = cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << -- cgit v1.2.3-59-g8ed1b From 6fa52430f0b3a45a31fb706084288884532e857c Mon Sep 17 00:00:00 2001 From: Matti Gottlieb Date: Mon, 4 Jan 2016 13:38:41 +0200 Subject: iwlwifi: mvm: change mcc update API New functionality for testing that is not relevant for this driver has been added. This required an API change. Add new cmd & response versions for the MCC update cmd & response. Add new TLV indicating that the FW is using the new API. Signed-off-by: Matti Gottlieb Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 4 ++ drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 60 ++++++++++++++++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 52 +++++++++++++++----- 3 files changed, 100 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index 9f24f990b705..8d37e330f153 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -7,6 +7,7 @@ * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -311,6 +313,7 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT * @IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what * antenna the beacon should be transmitted + * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2: support LAR API V2 * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -339,6 +342,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67, IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = (__force iwl_ucode_tlv_capa_t)71, + IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2 = (__force iwl_ucode_tlv_capa_t)73, NUM_IWL_UCODE_TLV_CAPA #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 995898c5d017..82049bb139c2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1451,6 +1453,22 @@ struct iwl_sf_cfg_cmd { * Location Aware Regulatory (LAR) API - MCC updates ***********************************/ +/** + * struct iwl_mcc_update_cmd_v1 - Request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: the source from where we got the MCC, see iwl_mcc_source + * @reserved: reserved for alignment + */ +struct iwl_mcc_update_cmd_v1 { + __le16 mcc; + u8 source_id; + u8 reserved; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_1 */ + /** * struct iwl_mcc_update_cmd - Request the device to update geographic * regulatory profile according to the given MCC (Mobile Country Code). @@ -1460,12 +1478,39 @@ struct iwl_sf_cfg_cmd { * @mcc: given mobile country code * @source_id: the source from where we got the MCC, see iwl_mcc_source * @reserved: reserved for alignment + * @key: integrity key for MCC API OEM testing + * @reserved2: reserved */ struct iwl_mcc_update_cmd { __le16 mcc; u8 source_id; u8 reserved; -} __packed; /* LAR_UPDATE_MCC_CMD_API_S */ + __le32 key; + __le32 reserved2[5]; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_2 */ + +/** + * iwl_mcc_update_resp_v1 - response to MCC_UPDATE_CMD. + * Contains the new channel control profile map, if changed, and the new MCC + * (mobile country code). + * The new MCC may be different than what was requested in MCC_UPDATE_CMD. + * @status: see &enum iwl_mcc_update_status + * @mcc: the new applied MCC + * @cap: capabilities for all channels which matches the MCC + * @source_id: the MCC source, see iwl_mcc_source + * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 + * channels, depending on platform) + * @channels: channel control data map, DWORD for each channel. Only the first + * 16bits are used. + */ +struct iwl_mcc_update_resp_v1 { + __le32 status; + __le16 mcc; + u8 cap; + u8 source_id; + __le32 n_channels; + __le32 channels[0]; +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_1 */ /** * iwl_mcc_update_resp - response to MCC_UPDATE_CMD. @@ -1476,6 +1521,8 @@ struct iwl_mcc_update_cmd { * @mcc: the new applied MCC * @cap: capabilities for all channels which matches the MCC * @source_id: the MCC source, see iwl_mcc_source + * @time: time elapsed from the MCC test start (in 30 seconds TU) + * @reserved: reserved. * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 * channels, depending on platform) * @channels: channel control data map, DWORD for each channel. Only the first @@ -1486,9 +1533,11 @@ struct iwl_mcc_update_resp { __le16 mcc; u8 cap; u8 source_id; + __le16 time; + __le16 reserved; __le32 n_channels; __le32 channels[0]; -} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S */ +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_2 */ /** * struct iwl_mcc_chub_notif - chub notifies of mcc change @@ -1518,6 +1567,9 @@ enum iwl_mcc_update_status { MCC_RESP_NVM_DISABLED, MCC_RESP_ILLEGAL, MCC_RESP_LOW_PRIORITY, + MCC_RESP_TEST_MODE_ACTIVE, + MCC_RESP_TEST_MODE_NOT_ACTIVE, + MCC_RESP_TEST_MODE_DENIAL_OF_SERVICE, }; enum iwl_mcc_source { @@ -1530,7 +1582,9 @@ enum iwl_mcc_source { MCC_SOURCE_RESERVED = 6, MCC_SOURCE_DEFAULT = 7, MCC_SOURCE_UNINITIALIZED = 8, - MCC_SOURCE_GET_CURRENT = 0x10 + MCC_SOURCE_MCC_API = 9, + MCC_SOURCE_GET_CURRENT = 0x10, + MCC_SOURCE_GETTING_MCC_TEST_MODE = 0x11, }; /* DTS measurements */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index d8dcb67b7ff9..e4fe8a66a3a0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -670,6 +672,7 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, .source_id = (u8)src_id, }; struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL; + struct iwl_mcc_update_resp_v1 *mcc_resp_v1 = NULL; struct iwl_rx_packet *pkt; struct iwl_host_cmd cmd = { .id = MCC_UPDATE_CMD, @@ -681,11 +684,15 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, u32 status; int resp_len, n_channels; u16 mcc; + bool resp_v2 = fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2); if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) return ERR_PTR(-EOPNOTSUPP); cmd.len[0] = sizeof(struct iwl_mcc_update_cmd); + if (!resp_v2) + cmd.len[0] = sizeof(struct iwl_mcc_update_cmd_v1); IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n", alpha2[0], alpha2[1], src_id); @@ -697,31 +704,50 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, pkt = cmd.resp_pkt; /* Extract MCC response */ - mcc_resp = (void *)pkt->data; - status = le32_to_cpu(mcc_resp->status); + if (resp_v2) { + mcc_resp = (void *)pkt->data; + n_channels = __le32_to_cpu(mcc_resp->n_channels); + } else { + mcc_resp_v1 = (void *)pkt->data; + n_channels = __le32_to_cpu(mcc_resp_v1->n_channels); + } - mcc = le16_to_cpu(mcc_resp->mcc); + resp_len = sizeof(struct iwl_mcc_update_resp) + n_channels * + sizeof(__le32); + + resp_cp = kzalloc(resp_len, GFP_KERNEL); + if (!resp_cp) { + ret = -ENOMEM; + goto exit; + } + + if (resp_v2) { + memcpy(resp_cp, mcc_resp, resp_len); + } else { + resp_cp->status = mcc_resp_v1->status; + resp_cp->mcc = mcc_resp_v1->mcc; + resp_cp->cap = mcc_resp_v1->cap; + resp_cp->source_id = mcc_resp_v1->source_id; + resp_cp->n_channels = mcc_resp_v1->n_channels; + memcpy(resp_cp->channels, mcc_resp_v1->channels, + n_channels * sizeof(__le32)); + } + + status = le32_to_cpu(resp_cp->status); + + mcc = le16_to_cpu(resp_cp->mcc); /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ if (mcc == 0) { mcc = 0x3030; /* "00" - world */ - mcc_resp->mcc = cpu_to_le16(mcc); + resp_cp->mcc = cpu_to_le16(mcc); } - n_channels = __le32_to_cpu(mcc_resp->n_channels); IWL_DEBUG_LAR(mvm, "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n", status, mcc, mcc >> 8, mcc & 0xff, !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels); - resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32); - resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL); - if (!resp_cp) { - ret = -ENOMEM; - goto exit; - } - - ret = 0; exit: iwl_free_resp(&cmd); if (ret) -- cgit v1.2.3-59-g8ed1b From 4ca87a5f58454711cc8cc550a7844efebd9bc001 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 3 Jan 2016 22:23:40 +0200 Subject: iwlwifi: mvm: reset mvm->scan_type when firmware is started If we don't reset the scan type when the firmware is started, we will think the firmware is still configured after the interface has been brought down. When we will bring it up again, we will not configure the scan type in firmware and it will crash with the following assert: 0x0000100A | ADVANCED_SYSASSERT Fixes: 355346ba3050 ("iwlwifi: mvm: configure scheduled scan according to traffic conditions") Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index e6e80882d86d..4ed5180c547b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -943,6 +943,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) } if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { + mvm->scan_type = IWL_SCAN_TYPE_NOT_SET; ret = iwl_mvm_config_scan(mvm); if (ret) goto error; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 296b9c5cd1be..2d65040269d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1002,7 +1002,6 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) mvm->vif_count = 0; mvm->rx_ba_sessions = 0; mvm->fw_dbg_conf = FW_DBG_INVALID; - mvm->scan_type = IWL_SCAN_TYPE_NOT_SET; /* keep statistics ticking */ iwl_mvm_accu_radio_stats(mvm); -- cgit v1.2.3-59-g8ed1b From ed0450cef00d2c76bcb8778721df947ba7ff4147 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 4 Jan 2016 10:19:17 +0200 Subject: iwlwifi: set max firmware version of 7265 to 17 Just like 7260, 7265 will not have firmware releases newer than iwlwifi-7265-17.ucode. 7265D is still supported in latest firmware releases. Fixes: 628a2918afe4 ("iwlwifi: separate firmware version for 7260 devices") Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-7000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c index fd9064bf389a..d15117cc1fa4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c @@ -72,7 +72,7 @@ /* Highest firmware API version supported */ #define IWL7260_UCODE_API_MAX 17 -#define IWL7265_UCODE_API_MAX 19 +#define IWL7265_UCODE_API_MAX 17 #define IWL7265D_UCODE_API_MAX 19 /* Oldest version we won't warn about */ -- cgit v1.2.3-59-g8ed1b From f370f5cffe7ae8b92026c8d6cb2738b4d3aeed9c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 5 Jan 2016 16:30:04 +0200 Subject: iwlwifi: mvm: bump max API to 20 7265D and up are now able to handle -20.ucode. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-7000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-8000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-9000.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c index d15117cc1fa4..e60cf141ed79 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c @@ -73,7 +73,7 @@ /* Highest firmware API version supported */ #define IWL7260_UCODE_API_MAX 17 #define IWL7265_UCODE_API_MAX 17 -#define IWL7265D_UCODE_API_MAX 19 +#define IWL7265D_UCODE_API_MAX 20 /* Oldest version we won't warn about */ #define IWL7260_UCODE_API_OK 13 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c index dee4458b408d..c84a0299d43e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c @@ -69,7 +69,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL8000_UCODE_API_MAX 19 +#define IWL8000_UCODE_API_MAX 20 /* Oldest version we won't warn about */ #define IWL8000_UCODE_API_OK 13 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c index 0d2aa1d9a50f..ecbf4822cd69 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c @@ -55,7 +55,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL9000_UCODE_API_MAX 16 +#define IWL9000_UCODE_API_MAX 20 /* Oldest version we won't warn about */ #define IWL9000_UCODE_API_OK 13 -- cgit v1.2.3-59-g8ed1b From 488c28e110e18466c99ffb1e2342498b42d3344e Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Wed, 25 Nov 2015 11:17:41 +0200 Subject: iwlwifi: nvm: fix loading default NVM file Fix loading the default NVM file, in the case where the requested NVM file isn't found in the file system. Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index e4fe8a66a3a0..7a3da2da6fd0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -642,7 +642,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) else mvm->nvm_file_name = nvm_file_C; - if (ret == -EFAULT && mvm->nvm_file_name) { + if ((ret == -EFAULT || ret == -ENOENT) && + mvm->nvm_file_name) { /* in case nvm file was failed try again */ ret = iwl_mvm_read_external_nvm(mvm); if (ret) -- cgit v1.2.3-59-g8ed1b From ca95ff3a9f9edea919a53a297a4cba178b6cdf5f Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Sun, 3 Jan 2016 17:08:32 +0200 Subject: iwlwifi: mvm: fix extended dwell time FW adds 10 msec for every dwell time in low band, so we need to set 10 msec less. Don't use extended dwell time when fragmented scan is needed because FW adds 3 msec per probe and it can easily exceed max out of channel time. Fixes: c3e230b167a9 ("iwlwifi: mvm: add extended dwell time") Signed-off-by: David Spinadel Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index bee3201c7116..9a15642f80dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -92,7 +92,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, - .dwell_extended = 100, + .dwell_extended = 90, .suspend_time = 0, .max_out_time = 0, }, @@ -100,7 +100,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, - .dwell_extended = 100, + .dwell_extended = 90, .suspend_time = 30, .max_out_time = 120, }, @@ -108,7 +108,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, - .dwell_extended = 100, + .dwell_extended = 90, .suspend_time = 120, .max_out_time = 120, }, @@ -116,7 +116,6 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, - .dwell_extended = 44, .suspend_time = 95, .max_out_time = 44, }, @@ -790,7 +789,8 @@ static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm, #endif if (iwl_mvm_is_regular_scan(params) && - vif->type != NL80211_IFTYPE_P2P_DEVICE) + vif->type != NL80211_IFTYPE_P2P_DEVICE && + params->type != IWL_SCAN_TYPE_FRAGMENTED) flags |= IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL; return flags; @@ -1072,7 +1072,8 @@ static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm, #endif if (iwl_mvm_is_regular_scan(params) && - vif->type != NL80211_IFTYPE_P2P_DEVICE) + vif->type != NL80211_IFTYPE_P2P_DEVICE && + params->type != IWL_SCAN_TYPE_FRAGMENTED) flags |= IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL; return flags; -- cgit v1.2.3-59-g8ed1b From 976f15a8ef53144731f7e431c9498aff9392c9cb Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 28 Dec 2015 15:22:28 +0200 Subject: iwlwifi: mvm: dump the radio registers when the firmware crashes Dumping the content of the radio registers greatly helps to debug PHY issues, which can lead to TFD queue hang. Signed-off-by: Emmanuel Grumbach --- .../net/wireless/intel/iwlwifi/iwl-fw-error-dump.h | 2 ++ drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 6 ++++ drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 40 ++++++++++++++++++++-- 3 files changed, 46 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h index f08a1319fc04..a5aaf6853704 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h @@ -88,6 +88,7 @@ * &struct iwl_fw_error_dump_rb * @IWL_FW_ERROR_PAGING: UMAC's image memory segments which were * paged to the DRAM. + * @IWL_FW_ERROR_DUMP_RADIO_REG: Dump the radio registers. */ enum iwl_fw_error_dump_type { /* 0 is deprecated */ @@ -103,6 +104,7 @@ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_ERROR_INFO = 10, IWL_FW_ERROR_DUMP_RB = 11, IWL_FW_ERROR_DUMP_PAGING = 12, + IWL_FW_ERROR_DUMP_RADIO_REG = 13, IWL_FW_ERROR_DUMP_MAX, }; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 9da7dc49549c..5bde23a472b4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -345,6 +345,12 @@ enum secure_load_status_reg { #define TXF_READ_MODIFY_DATA (0xa00448) #define TXF_READ_MODIFY_ADDR (0xa0044c) +/* Radio registers access */ +#define RSP_RADIO_CMD (0xa02804) +#define RSP_RADIO_RDDAT (0xa02814) +#define RADIO_RSP_ADDR_POS (6) +#define RADIO_RSP_RD_CMD (3) + /* FW monitor */ #define MON_BUFF_SAMPLE_CTL (0xa03c00) #define MON_BUFF_BASE_ADDR (0xa03c3c) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index f406c76b4302..59450f177c43 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -113,6 +113,35 @@ static void iwl_mvm_free_coredump(const void *data) kfree(fw_error_dump); } +#define RADIO_REG_MAX_READ 0x2ad +static void iwl_mvm_read_radio_reg(struct iwl_mvm *mvm, + struct iwl_fw_error_dump_data **dump_data) +{ + u8 *pos = (void *)(*dump_data)->data; + unsigned long flags; + int i; + + if (!iwl_trans_grab_nic_access(mvm->trans, &flags)) + return; + + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG); + (*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ); + + for (i = 0; i < RADIO_REG_MAX_READ; i++) { + u32 rd_cmd = RADIO_RSP_RD_CMD; + + rd_cmd |= i << RADIO_RSP_ADDR_POS; + iwl_write_prph_no_grab(mvm->trans, RSP_RADIO_CMD, rd_cmd); + *pos = (u8)iwl_read_prph_no_grab(mvm->trans, RSP_RADIO_RDDAT); + + pos++; + } + + *dump_data = iwl_fw_error_next_data(*dump_data); + + iwl_trans_release_nic_access(mvm->trans, &flags); +} + static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, struct iwl_fw_error_dump_data **dump_data) { @@ -401,7 +430,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) struct iwl_fw_error_dump_trigger_desc *dump_trig; struct iwl_mvm_dump_ptrs *fw_error_dump; u32 sram_len, sram_ofs; - u32 file_len, fifo_data_len = 0, prph_len = 0; + u32 file_len, fifo_data_len = 0, prph_len = 0, radio_len = 0; u32 smem_len = mvm->cfg->smem_len; u32 sram2_len = mvm->cfg->dccm2_len; bool monitor_dump_only = false; @@ -472,6 +501,9 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) sizeof(struct iwl_fw_error_dump_prph) + num_bytes_in_chunk; } + + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) + radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ; } file_len = sizeof(*dump_file) + @@ -479,6 +511,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) sram_len + sizeof(*dump_mem) + fifo_data_len + prph_len + + radio_len + sizeof(*dump_info); /* Make room for the SMEM, if it exists */ @@ -543,8 +576,11 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) dump_data = iwl_fw_error_next_data(dump_data); /* We only dump the FIFOs if the FW is in error state */ - if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) + if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { iwl_mvm_dump_fifos(mvm, &dump_data); + if (radio_len) + iwl_mvm_read_radio_reg(mvm, &dump_data); + } if (mvm->fw_dump_desc) { dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); -- cgit v1.2.3-59-g8ed1b From a977a1507ce133201ac2f11b3fbf102a73a77244 Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Wed, 25 Nov 2015 11:44:57 +0200 Subject: iwlwifi: mvm: add a non-trigger window to fw dbg triggers Allow the user to configure a non-trigger session - a window between triggers in which the driver won't collect fw debug data. This can be useful when the frequent collection of fw data has an impact on the performance, such as debugging tx flows. Signed-off-by: Golan Ben-Ami Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 5 ++++- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h | 24 ++++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 +++ 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index 8d37e330f153..84f8aeb926c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -557,6 +557,8 @@ enum iwl_fw_dbg_trigger_vif_type { * @start_conf_id: if mode is %IWL_FW_DBG_TRIGGER_START, this defines what * configuration should be applied when the triggers kicks in. * @occurrences: number of occurrences. 0 means the trigger will never fire. + * @trig_dis_ms: the time, in milliseconds, after an occurrence of this + * trigger in which another occurrence should be ignored. */ struct iwl_fw_dbg_trigger_tlv { __le32 id; @@ -566,7 +568,8 @@ struct iwl_fw_dbg_trigger_tlv { u8 mode; u8 start_conf_id; __le16 occurrences; - __le32 reserved[2]; + __le16 trig_dis_ms; + __le16 reserved[3]; u8 data[0]; } __packed; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h index 461acdf497dc..08148b258bc5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h @@ -117,6 +117,24 @@ iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm, (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids)))); } +static inline bool +iwl_fw_dbg_no_trig_window(struct iwl_mvm *mvm, + struct iwl_fw_dbg_trigger_tlv *trig) +{ + unsigned long wind_jiff = + msecs_to_jiffies(le16_to_cpu(trig->trig_dis_ms)); + u32 id = le32_to_cpu(trig->id); + + /* If this is the first event checked, jump to update start ts */ + if (mvm->fw_dbg_non_collect_ts_start[id] && + (time_after(mvm->fw_dbg_non_collect_ts_start[id] + wind_jiff, + jiffies))) + return true; + + mvm->fw_dbg_non_collect_ts_start[id] = jiffies; + return false; +} + static inline bool iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -125,6 +143,12 @@ iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif)) return false; + if (iwl_fw_dbg_no_trig_window(mvm, trig)) { + IWL_WARN(mvm, "Trigger %d occurred while no-collect window.\n", + trig->id); + return false; + } + return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 287c16250570..7517dc13eb86 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -658,6 +658,9 @@ struct iwl_mvm { /* max number of simultaneous scans the FW supports */ unsigned int max_scans; + /* ts of the beginning of a non-collect fw dbg data period */ + unsigned long fw_dbg_non_collect_ts_start[FW_DBG_TRIGGER_MAX - 1]; + /* UMAC scan tracking */ u32 scan_uid_status[IWL_MVM_MAX_UMAC_SCANS]; -- cgit v1.2.3-59-g8ed1b From 909ddf0b812ecd5ff3a9d5a164134a3714101fdb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 21 Sep 2015 14:09:17 +0200 Subject: iwlwifi: mvm: support A-MSDU in A-MPDU Since A-MPDU deaggregation is done in hardware, and A-MSDU deaggregation is done in software, there's no reason not to support A-MSDU in A-MPDU; set the flag to support it. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 2d65040269d7..8539dfe9998e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -438,6 +438,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ieee80211_hw_set(hw, CHANCTX_STA_CSA); ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); + ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); if (mvm->trans->max_skb_frags) hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; -- cgit v1.2.3-59-g8ed1b From b6c7d7209dc1f128fb5b9e07ee0bb2f808f86bab Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 24 Dec 2015 08:48:46 +0200 Subject: iwlwifi: mvm: remove useless WARN_ON and rely on cfg80211's combination We advertise one STATION vif only, so this just can't happen. Remove this useless WARN_ON. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/power.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index 87a9f244e151..9de159f1ef2d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -613,8 +613,6 @@ static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac, break; case NL80211_IFTYPE_STATION: - /* only a single MAC of the same type */ - WARN_ON(power_iterator->bss_vif); power_iterator->bss_vif = vif; if (mvmvif->phy_ctxt) if (mvmvif->phy_ctxt->id < MAX_PHYS) -- cgit v1.2.3-59-g8ed1b From 30433d3b9850140ceb801c5cc2013c7b408c33de Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 24 Sep 2015 18:14:55 +0200 Subject: iwlwifi: mvm: prevent multiple stations with the same address As the device (and parts of the driver) cannot deal with having the same MAC address for two stations (on two virtual interfaces), add some explicit code to prevent this case. Note that in practice this cannot happen since the device doesn't support operating with two AP/GO interfaces at the same time either, and other scenarios for this are, while not impossible, not going to happen in practice. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 8539dfe9998e..40b5c5378bd2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -439,6 +439,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); + ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR); if (mvm->trans->max_skb_frags) hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; -- cgit v1.2.3-59-g8ed1b From bd6f5bd70012569dc626f50f3272b9f516cf20f8 Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Sun, 20 Dec 2015 09:27:50 +0200 Subject: iwlwifi: mvm: don't ask beacons when P2P GO vif and no assoc sta The commit below called iwl_mvm_mac_ctxt_changed() to handle a case that the vif is a P2P GO. However iwl_mvm_mac_ctxt_cmd_go() ignores the number of associated stations and asks the FW to pass beacons anyways. Fix this by checking ap_assoc_sta_count parameter, in iwl_mvm_mac_ctxt_cmd_go() as well, and ask the FW to pass beacons only when there's at least one associated station. Signed-off-by: Ayala Beker Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 44 ++++++++++------------- 1 file changed, 19 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 448b9c927b51..bf1e5eb5dbdb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -717,6 +717,8 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cpu_to_le32(vif->bss_conf.use_short_slot ? MAC_FLG_SHORT_SLOT : 0); + cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { u8 txf = iwl_mvm_ac_to_tx_fifo[i]; @@ -730,11 +732,26 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cmd->ac[txf].fifos_mask = BIT(txf); } - /* in AP mode, the MCAST FIFO takes the EDCA params from VO */ - if (vif->type == NL80211_IFTYPE_AP) + if (vif->type == NL80211_IFTYPE_AP) { + /* in AP mode, the MCAST FIFO takes the EDCA params from VO */ cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |= BIT(IWL_MVM_TX_FIFO_MCAST); + /* + * in AP mode, pass probe requests and beacons from other APs + * (needed for ht protection); when there're no any associated + * station don't ask FW to pass beacons to prevent unnecessary + * wake-ups. + */ + cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); + if (mvmvif->ap_assoc_sta_count) { + cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); + IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); + } else { + IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); + } + } + if (vif->bss_conf.qos) cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); @@ -748,8 +765,6 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); if (ht_enabled) iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd); - - cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP); } static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, @@ -1156,7 +1171,6 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 action) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mac_ctx_cmd cmd = {}; WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p); @@ -1164,19 +1178,6 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, /* Fill the common data for all mac context types */ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - /* - * pass probe requests and beacons from other APs (needed - * for ht protection); when there're no any associated station - * don't ask FW to pass beacons to prevent unnecessary wake-ups. - */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); - if (mvmvif->ap_assoc_sta_count) { - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); - IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); - } else { - IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); - } - /* Fill the data specific for ap mode */ iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap, action == FW_CTXT_ACTION_ADD); @@ -1196,13 +1197,6 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm, /* Fill the common data for all mac context types */ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - /* - * pass probe requests and beacons from other APs (needed - * for ht protection) - */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST | - MAC_FILTER_IN_BEACON); - /* Fill the data specific for GO mode */ iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap, action == FW_CTXT_ACTION_ADD); -- cgit v1.2.3-59-g8ed1b From f5e28eac1a890e5a950cacfa3a4942a6d69462e6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 6 Dec 2015 14:58:08 +0200 Subject: iwlwifi: mvm: check PN for CCMP/GCMP in the driver As we're working on multi-queue RX, we want to parallelise checking the PN in order to avoid having to serialise the RX processing. It may seem that doing parallel PN checking is insecure, but it turns out to be OK because queue assignment is done based on the data in the frame (IP/TCP) and thus cannot be manipulated by an attacker, since the data is encrypted and must first have been decrypted successfully. There are some corner cases, in particular when the peer starts using fragmentation which redirects the packet to the default queue. However this redirection is remembered (for the STA, per TID) and thus cannot be exploited by an attacker either. Leave checking on the default queue (queue 0) to mac80211, since we get fragmented packets there and those are subject to stricter checks during reassembly. Signed-off-by: Johannes Berg Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 114 ++++++++++++++++++---- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 46 +++++++++ drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 106 +++++++++++++++++--- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 12 +++ 4 files changed, 245 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 6ac40727541e..2cd9052899c4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -137,6 +137,28 @@ static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out) out[i] = cpu_to_le16(p1k[i]); } +static const u8 *iwl_mvm_find_max_pn(struct ieee80211_key_conf *key, + struct iwl_mvm_key_pn *ptk_pn, + struct ieee80211_key_seq *seq, + int tid, int queues) +{ + const u8 *ret = seq->ccmp.pn; + int i; + + /* get the PN from mac80211, used on the default queue */ + ieee80211_get_key_rx_seq(key, tid, seq); + + /* and use the internal data for the other queues */ + for (i = 1; i < queues; i++) { + const u8 *tmp = ptk_pn->q[i].pn[tid]; + + if (memcmp(ret, tmp, IEEE80211_CCMP_PN_LEN) <= 0) + ret = tmp; + } + + return ret; +} + struct wowlan_key_data { struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc; struct iwl_wowlan_tkip_params_cmd *tkip; @@ -294,18 +316,42 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, /* * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 for checking the IV in the frames. + * mac80211/our RX code use TID 0 for checking the PN. */ - for (i = 0; i < IWL_NUM_RSC; i++) { - u8 *pn = seq.ccmp.pn; + if (sta && iwl_mvm_has_new_rx_api(mvm)) { + struct iwl_mvm_sta *mvmsta; + struct iwl_mvm_key_pn *ptk_pn; + const u8 *pn; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + ptk_pn = rcu_dereference_protected( + mvmsta->ptk_pn[key->keyidx], + lockdep_is_held(&mvm->mutex)); + if (WARN_ON(!ptk_pn)) + break; - ieee80211_get_key_rx_seq(key, i, &seq); - aes_sc[i].pn = cpu_to_le64((u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + pn = iwl_mvm_find_max_pn(key, ptk_pn, &seq, i, + mvm->trans->num_rx_queues); + aes_sc[i].pn = cpu_to_le64((u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } + } else { + for (i = 0; i < IWL_NUM_RSC; i++) { + u8 *pn = seq.ccmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); + aes_sc[i].pn = cpu_to_le64((u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } } data->use_rsc_tsc = true; break; @@ -1426,18 +1472,42 @@ static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc, seq->tkip.iv16 = le16_to_cpu(sc->iv16); } -static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs, +static void iwl_mvm_set_aes_rx_seq(struct iwl_mvm *mvm, struct aes_sc *scs, + struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { int tid; BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); - for (tid = 0; tid < IWL_NUM_RSC; tid++) { - struct ieee80211_key_seq seq = {}; + if (sta && iwl_mvm_has_new_rx_api(mvm)) { + struct iwl_mvm_sta *mvmsta; + struct iwl_mvm_key_pn *ptk_pn; - iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); - ieee80211_set_key_rx_seq(key, tid, &seq); + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + ptk_pn = rcu_dereference_protected(mvmsta->ptk_pn[key->keyidx], + lockdep_is_held(&mvm->mutex)); + if (WARN_ON(!ptk_pn)) + return; + + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + struct ieee80211_key_seq seq = {}; + int i; + + iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + for (i = 1; i < mvm->trans->num_rx_queues; i++) + memcpy(ptk_pn->q[i].pn[tid], + seq.ccmp.pn, IEEE80211_CCMP_PN_LEN); + } + } else { + for (tid = 0; tid < IWL_NUM_RSC; tid++) { + struct ieee80211_key_seq seq = {}; + + iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + } } } @@ -1456,14 +1526,15 @@ static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs, } } -static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key, +static void iwl_mvm_set_key_rx_seq(struct iwl_mvm *mvm, + struct ieee80211_key_conf *key, struct iwl_wowlan_status *status) { union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc; switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: - iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key); + iwl_mvm_set_aes_rx_seq(mvm, rsc->aes.multicast_rsc, NULL, key); break; case WLAN_CIPHER_SUITE_TKIP: iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key); @@ -1474,6 +1545,7 @@ static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key, } struct iwl_mvm_d3_gtk_iter_data { + struct iwl_mvm *mvm; struct iwl_wowlan_status *status; void *last_gtk; u32 cipher; @@ -1522,7 +1594,8 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw, switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: - iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key); + iwl_mvm_set_aes_rx_seq(data->mvm, sc->aes.unicast_rsc, + sta, key); atomic64_set(&key->tx_pn, le64_to_cpu(sc->aes.tsc.pn)); break; case WLAN_CIPHER_SUITE_TKIP: @@ -1545,7 +1618,7 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw, if (data->status->num_of_gtk_rekeys) ieee80211_remove_key(key); else if (data->last_gtk == key) - iwl_mvm_set_key_rx_seq(key, data->status); + iwl_mvm_set_key_rx_seq(data->mvm, key, data->status); } static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, @@ -1554,6 +1627,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_d3_gtk_iter_data gtkdata = { + .mvm = mvm, .status = status, }; u32 disconnection_reasons = @@ -1615,7 +1689,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, key = ieee80211_gtk_rekey_add(vif, &conf.conf); if (IS_ERR(key)) return false; - iwl_mvm_set_key_rx_seq(key, status); + iwl_mvm_set_key_rx_seq(mvm, key, status); } if (status->num_of_gtk_rekeys) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 40b5c5378bd2..d70a1716f3e0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2568,6 +2568,9 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, struct ieee80211_key_conf *key) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_sta *mvmsta; + struct iwl_mvm_key_pn *ptk_pn; + int keyidx = key->keyidx; int ret; u8 key_offset; @@ -2635,6 +2638,36 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, break; } + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + sta && iwl_mvm_has_new_rx_api(mvm) && + key->flags & IEEE80211_KEY_FLAG_PAIRWISE && + (key->cipher == WLAN_CIPHER_SUITE_CCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP)) { + struct ieee80211_key_seq seq; + int tid, q; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + WARN_ON(rcu_access_pointer(mvmsta->ptk_pn[keyidx])); + ptk_pn = kzalloc(sizeof(*ptk_pn) + + mvm->trans->num_rx_queues * + sizeof(ptk_pn->q[0]), + GFP_KERNEL); + if (!ptk_pn) { + ret = -ENOMEM; + break; + } + + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + ieee80211_get_key_rx_seq(key, tid, &seq); + for (q = 0; q < mvm->trans->num_rx_queues; q++) + memcpy(ptk_pn->q[q].pn[tid], + seq.ccmp.pn, + IEEE80211_CCMP_PN_LEN); + } + + rcu_assign_pointer(mvmsta->ptk_pn[keyidx], ptk_pn); + } + /* in HW restart reuse the index, otherwise request a new one */ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) key_offset = key->hw_key_idx; @@ -2660,6 +2693,19 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, break; } + if (sta && iwl_mvm_has_new_rx_api(mvm) && + key->flags & IEEE80211_KEY_FLAG_PAIRWISE && + (key->cipher == WLAN_CIPHER_SUITE_CCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP)) { + mvmsta = iwl_mvm_sta_from_mac80211(sta); + ptk_pn = rcu_dereference_protected( + mvmsta->ptk_pn[keyidx], + lockdep_is_held(&mvm->mutex)); + RCU_INIT_POINTER(mvmsta->ptk_pn[keyidx], NULL); + if (ptk_pn) + kfree_rcu(ptk_pn, rcu_head); + } + IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n"); ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key); break; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index e2a872deb668..0c073e02fd4c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -78,12 +78,83 @@ void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) #endif } -static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, - struct napi_struct *napi, - struct sk_buff *skb, - struct ieee80211_hdr *hdr, u16 len, - u32 ampdu_status, u8 crypt_len, - struct iwl_rx_cmd_buffer *rxb) +static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb, + int queue, struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb); + struct iwl_mvm_key_pn *ptk_pn; + u8 tid, keyidx; + u8 pn[IEEE80211_CCMP_PN_LEN]; + u8 *extiv; + + /* do PN checking */ + + /* multicast and non-data only arrives on default queue */ + if (!ieee80211_is_data(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) + return 0; + + /* do not check PN for open AP */ + if (!(stats->flag & RX_FLAG_DECRYPTED)) + return 0; + + /* + * avoid checking for default queue - we don't want to replicate + * all the logic that's necessary for checking the PN on fragmented + * frames, leave that to mac80211 + */ + if (queue == 0) + return 0; + + /* if we are here - this for sure is either CCMP or GCMP */ + if (IS_ERR_OR_NULL(sta)) { + IWL_ERR(mvm, + "expected hw-decrypted unicast frame for station\n"); + return -1; + } + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); + keyidx = extiv[3] >> 6; + + ptk_pn = rcu_dereference(mvmsta->ptk_pn[keyidx]); + if (!ptk_pn) + return -1; + + if (ieee80211_is_data_qos(hdr->frame_control)) + tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + else + tid = 0; + + /* we don't use HCCA/802.11 QoS TSPECs, so drop such frames */ + if (tid >= IWL_MAX_TID_COUNT) + return -1; + + /* load pn */ + pn[0] = extiv[7]; + pn[1] = extiv[6]; + pn[2] = extiv[5]; + pn[3] = extiv[4]; + pn[4] = extiv[1]; + pn[5] = extiv[0]; + + if (memcmp(pn, ptk_pn->q[queue].pn[tid], + IEEE80211_CCMP_PN_LEN) <= 0) + return -1; + + memcpy(ptk_pn->q[queue].pn[tid], pn, IEEE80211_CCMP_PN_LEN); + stats->flag |= RX_FLAG_PN_VALIDATED; + + return 0; +} + +/* iwl_mvm_create_skb Adds the rxb to a new skb */ +static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr, + u16 len, u8 crypt_len, + struct iwl_rx_cmd_buffer *rxb) { unsigned int hdrlen, fraglen; @@ -112,8 +183,18 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, fraglen, rxb->truesize); } +} - ieee80211_rx_napi(mvm->hw, skb, napi); +/* iwl_mvm_pass_packet_to_mac80211 - passes the packet for mac80211 */ +static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, + struct napi_struct *napi, + struct sk_buff *skb, int queue, + struct ieee80211_sta *sta) +{ + if (iwl_mvm_check_pn(mvm, skb, queue, sta)) + kfree_skb(skb); + else + ieee80211_rx_napi(mvm->hw, skb, napi); } static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, @@ -141,7 +222,7 @@ static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, rx_status->chain_signal[2] = energy_c; } -static u32 iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, +static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, struct ieee80211_rx_status *stats, struct iwl_rx_mpdu_desc *desc, int queue, u8 *crypt_len) @@ -158,6 +239,7 @@ static u32 iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, switch (status & IWL_RX_MPDU_STATUS_SEC_MASK) { case IWL_RX_MPDU_STATUS_SEC_CCM: case IWL_RX_MPDU_STATUS_SEC_GCM: + BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != IEEE80211_GCMP_PN_LEN); /* alg is CCM: check MIC only */ if (!(status & IWL_RX_MPDU_STATUS_MIC_OK)) return -1; @@ -217,7 +299,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, u32 rate_n_flags = le32_to_cpu(desc->rate_n_flags); struct ieee80211_sta *sta = NULL; struct sk_buff *skb; - u32 ampdu_status; u8 crypt_len = 0; /* Dont use dev_alloc_skb(), we'll have enough headroom once @@ -311,8 +392,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, iwl_mvm_rx_csum(sta, skb, desc); } - rcu_read_unlock(); - /* * TODO: PHY info. * Verify we don't have the information in the MPDU descriptor and @@ -367,8 +446,9 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, /* TODO: PHY info - update ampdu queue statistics (for debugfs) */ /* TODO: PHY info - gscan */ - iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status, - crypt_len, rxb); + iwl_mvm_create_skb(skb, hdr, len, crypt_len, rxb); + iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta); + rcu_read_unlock(); } void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index badf17c7fcca..39fdf5224e81 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -284,6 +286,13 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) tid_data->next_reclaimed); } +struct iwl_mvm_key_pn { + struct rcu_head rcu_head; + struct { + u8 pn[IWL_MAX_TID_COUNT][IEEE80211_CCMP_PN_LEN]; + } ____cacheline_aligned_in_smp q[]; +}; + /** * struct iwl_mvm_sta - representation of a station in the driver * @sta_id: the index of the station in the fw (will be replaced by id_n_color) @@ -308,6 +317,7 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) * gets empty before all the frames were sent, which can happen when * we are sending frames from an AMPDU queue and there was a hole in * the BA window. To be used for UAPSD only. + * @ptk_pn: per-queue PTK PN data structures * * When mac80211 creates a station it reserves some space (hw->sta_data_size) * in the structure for use by driver. This structure is placed in that @@ -328,6 +338,8 @@ struct iwl_mvm_sta { struct iwl_lq_sta lq_sta; struct ieee80211_vif *vif; + struct iwl_mvm_key_pn __rcu *ptk_pn[4]; + /* Temporary, until the new TLC will control the Tx protection */ s8 tx_protection; bool tt_tx_protection; -- cgit v1.2.3-59-g8ed1b From 0eb1c968f3c1519505d20b62f782b4f4daf4f525 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 31 Dec 2015 15:19:32 +0200 Subject: iwlwifi: mvm: initialize gtkdata->mvm correctly gtkdata->mvm wasn't set in iwl_mvm_d0i3_update_keys, resulting in kernel panic in some flows (when mvm is actually used...) Fixes: a3f7ba5c8825 ("iwlwifi: update key params on d0i3 entrance/exit") Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 2cd9052899c4..d3e21d95cece 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1843,6 +1843,7 @@ void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm, struct iwl_wowlan_status *status) { struct iwl_mvm_d3_gtk_iter_data gtkdata = { + .mvm = mvm, .status = status, }; -- cgit v1.2.3-59-g8ed1b From be720d3fc049e14323f2ba637c053fe39d3e0157 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Jan 2016 16:16:31 +0100 Subject: iwlwifi: mvm: check minimum temperature notification length This notification will be extended with extra data, so just check that it has a minimum length, not the exact length; we might later add handling for the extra fields added and have more code to handle both versions. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index 473975cb34af..fb76004eede4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -120,7 +120,7 @@ static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm, int len = iwl_rx_packet_payload_len(pkt); int temp; - if (WARN_ON_ONCE(len != sizeof(*notif))) { + if (WARN_ON_ONCE(len < sizeof(*notif))) { IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n"); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From a80c7a696610c537fd6e61489be9da0a995fc880 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 5 Jan 2016 09:14:08 +0200 Subject: iwlwifi: mvm: constify the parameters of a few functions in fw-dbg.c The debug functions of fw-dbg.c don't really need to modify the trigger and the description they receive as a parameter. Constify the pointers. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 8 ++++---- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h | 6 +++--- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 6 +++--- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 81b7cb71e001..82fb3a97a46d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -663,7 +663,7 @@ struct iwl_trans_ops { void (*resume)(struct iwl_trans *trans); struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans, - struct iwl_fw_dbg_trigger_tlv + const struct iwl_fw_dbg_trigger_tlv *trigger); }; @@ -966,7 +966,7 @@ static inline void iwl_trans_resume(struct iwl_trans *trans) static inline struct iwl_trans_dump_data * iwl_trans_dump_data(struct iwl_trans *trans, - struct iwl_fw_dbg_trigger_tlv *trigger) + const struct iwl_fw_dbg_trigger_tlv *trigger) { if (!trans->ops->dump_data) return NULL; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index 59450f177c43..b3bc2128d81b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -681,15 +681,15 @@ dump_trans_data: clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); } -struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { +const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { .trig_desc = { .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), }, }; int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - struct iwl_mvm_dump_desc *desc, - struct iwl_fw_dbg_trigger_tlv *trigger) + const struct iwl_mvm_dump_desc *desc, + const struct iwl_fw_dbg_trigger_tlv *trigger) { unsigned int delay = 0; @@ -715,7 +715,7 @@ int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, const char *str, size_t len, - struct iwl_fw_dbg_trigger_tlv *trigger) + const struct iwl_fw_dbg_trigger_tlv *trigger) { struct iwl_mvm_dump_desc *desc; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h index 08148b258bc5..f7dff7612c9c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h @@ -72,11 +72,11 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm); int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - struct iwl_mvm_dump_desc *desc, - struct iwl_fw_dbg_trigger_tlv *trigger); + const struct iwl_mvm_dump_desc *desc, + const struct iwl_fw_dbg_trigger_tlv *trigger); int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, const char *str, size_t len, - struct iwl_fw_dbg_trigger_tlv *trigger); + const struct iwl_fw_dbg_trigger_tlv *trigger); int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, struct iwl_fw_dbg_trigger_tlv *trigger, const char *fmt, ...) __printf(3, 4); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 7517dc13eb86..5f3ac8cccf49 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -157,7 +157,7 @@ struct iwl_mvm_dump_desc { struct iwl_fw_error_dump_trigger_desc trig_desc; }; -extern struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert; +extern const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert; struct iwl_mvm_phy_ctxt { u16 id; @@ -732,8 +732,8 @@ struct iwl_mvm { s8 restart_fw; u8 fw_dbg_conf; struct delayed_work fw_dump_wk; - struct iwl_mvm_dump_desc *fw_dump_desc; - struct iwl_fw_dbg_trigger_tlv *fw_dump_trig; + const struct iwl_mvm_dump_desc *fw_dump_desc; + const struct iwl_fw_dbg_trigger_tlv *fw_dump_trig; #ifdef CONFIG_IWLWIFI_LEDS struct led_classdev led; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index d44e7afad593..3a4310917978 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2364,7 +2364,7 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, static struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans, - struct iwl_fw_dbg_trigger_tlv *trigger) + const struct iwl_fw_dbg_trigger_tlv *trigger) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_fw_error_dump_data *data; -- cgit v1.2.3-59-g8ed1b From 9fb7807ce7f21cbab2b13db0183f7f71a75add5e Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 5 Jan 2016 09:35:21 +0200 Subject: iwlwifi: mvm: fix memory leaks in error paths upon fw error dump When iwl_mvm_fw_error_dump fails, it needs to clear the state in mvm, which includes: * clear IWL_MVM_STATUS_DUMPING_FW_LOG * set mvm->fw_dump_trig to NULL * free the description While at it, remove a NULL check in iwl_mvm_free_fw_dump_desc since kfree is NULL safe. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index b3bc2128d81b..0813f8184e10 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -270,8 +270,7 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) { - if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert || - !mvm->fw_dump_desc) + if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert) return; kfree(mvm->fw_dump_desc); @@ -441,7 +440,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) /* there's no point in fw dump if the bus is dead */ if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { IWL_ERR(mvm, "Skip fw error dump since bus is dead\n"); - return; + goto out; } if (mvm->fw_dump_trig && @@ -450,7 +449,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); if (!fw_error_dump) - return; + goto out; /* SRAM - include stack CCM if driver knows the values for it */ if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { @@ -550,8 +549,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) dump_file = vzalloc(file_len); if (!dump_file) { kfree(fw_error_dump); - iwl_mvm_free_fw_dump_desc(mvm); - return; + goto out; } fw_error_dump->op_mode_ptr = dump_file; @@ -590,8 +588,6 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc, sizeof(*dump_trig) + mvm->fw_dump_desc->len); - /* now we can free this copy */ - iwl_mvm_free_fw_dump_desc(mvm); dump_data = iwl_fw_error_next_data(dump_data); } @@ -677,6 +673,8 @@ dump_trans_data: dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); +out: + iwl_mvm_free_fw_dump_desc(mvm); mvm->fw_dump_trig = NULL; clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); } -- cgit v1.2.3-59-g8ed1b From 62d7476d958ce06d7a10b02bdb30006870286fe2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 5 Jan 2016 15:25:43 +0200 Subject: iwlwifi: pcie: properly configure the debug buffer size for 8000 8000 device family has a new debug engine that needs to be configured differently than 7000's. The debug engine's DMA works in chunks of memory and the size of the buffer really means the start of the last chunk. Since one chunk is 256-byte long, we should configure the device to write to buffer_size - 256. This fixes a situation were the device would write to memory it is not allowed to access. CC: [4.1+] Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 3a4310917978..d60a467a983c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -7,6 +7,7 @@ * * Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -924,9 +926,16 @@ monitor: if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) { iwl_write_prph(trans, le32_to_cpu(dest->base_reg), trans_pcie->fw_mon_phys >> dest->base_shift); - iwl_write_prph(trans, le32_to_cpu(dest->end_reg), - (trans_pcie->fw_mon_phys + - trans_pcie->fw_mon_size) >> dest->end_shift); + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + iwl_write_prph(trans, le32_to_cpu(dest->end_reg), + (trans_pcie->fw_mon_phys + + trans_pcie->fw_mon_size - 256) >> + dest->end_shift); + else + iwl_write_prph(trans, le32_to_cpu(dest->end_reg), + (trans_pcie->fw_mon_phys + + trans_pcie->fw_mon_size) >> + dest->end_shift); } } -- cgit v1.2.3-59-g8ed1b From cd690e48f567642e1c1a2b67e592010e6456082a Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:08 +0100 Subject: phy: Use phy_read() instead of mdiobus_read() Since we have a phydev, make use of it and the phy_read() function. This will help with later refactoring. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 104 ++++++++--------------- 1 file changed, 36 insertions(+), 68 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 3b234176dd36..4eddbeb19307 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -77,18 +77,16 @@ static void hns_get_mdix_mode(struct net_device *net_dev, return; } - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG, - HNS_PHY_PAGE_MDIX); + phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_MDIX); - retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSC_REG); + retval = phy_read(phy_dev, HNS_PHY_CSC_REG); mdix_ctrl = hnae_get_field(retval, PHY_MDIX_CTRL_M, PHY_MDIX_CTRL_S); - retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSS_REG); + retval = phy_read(phy_dev, HNS_PHY_CSS_REG); mdix = hnae_get_bit(retval, PHY_MDIX_STATUS_B); is_resolved = hnae_get_bit(retval, PHY_SPEED_DUP_RESOLVE_B); - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG, - HNS_PHY_PAGE_COPPER); + phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER); switch (mdix_ctrl) { case 0x0: @@ -253,53 +251,36 @@ static int hns_nic_config_phy_loopback(struct phy_device *phy_dev, u8 en) if (en) { /* speed : 1000M */ - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - HNS_PHY_PAGE_REG, 2); - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 21, 0x1046); + phy_write(phy_dev, HNS_PHY_PAGE_REG, 2); + phy_write(phy_dev, 21, 0x1046); /* Force Master */ - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 9, 0x1F00); + phy_write(phy_dev, 9, 0x1F00); /* Soft-reset */ - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 0, 0x9140); + phy_write(phy_dev, 0, 0x9140); /* If autoneg disabled,two soft-reset operations */ - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 0, 0x9140); - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 22, 0xFA); + phy_write(phy_dev, 0, 0x9140); + phy_write(phy_dev, 22, 0xFA); /* Default is 0x0400 */ - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 1, 0x418); + phy_write(phy_dev, 1, 0x418); /* Force 1000M Link, Default is 0x0200 */ - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 7, 0x20C); - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 22, 0); + phy_write(phy_dev, 7, 0x20C); + phy_write(phy_dev, 22, 0); /* Enable MAC loop-back */ - val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr, - COPPER_CONTROL_REG); + val = phy_read(phy_dev, COPPER_CONTROL_REG); val |= PHY_LOOP_BACK; - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - COPPER_CONTROL_REG, val); + phy_write(phy_dev, COPPER_CONTROL_REG, val); } else { - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 22, 0xFA); - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 1, 0x400); - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 7, 0x200); - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - 22, 0); - - val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr, - COPPER_CONTROL_REG); + phy_write(phy_dev, 22, 0xFA); + phy_write(phy_dev, 1, 0x400); + phy_write(phy_dev, 7, 0x200); + phy_write(phy_dev, 22, 0); + + val = phy_read(phy_dev, COPPER_CONTROL_REG); val &= ~PHY_LOOP_BACK; - (void)mdiobus_write(phy_dev->bus, phy_dev->addr, - COPPER_CONTROL_REG, val); + phy_write(phy_dev, COPPER_CONTROL_REG, val); } return 0; } @@ -1018,16 +999,9 @@ int hns_phy_led_set(struct net_device *netdev, int value) struct hns_nic_priv *priv = netdev_priv(netdev); struct phy_device *phy_dev = priv->phy; - if (!phy_dev->bus) { - netdev_err(netdev, "phy_dev->bus is null!\n"); - return -EINVAL; - } - retval = mdiobus_write(phy_dev->bus, phy_dev->addr, - HNS_PHY_PAGE_REG, HNS_PHY_PAGE_LED); - retval = mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_LED_FC_REG, - value); - retval = mdiobus_write(phy_dev->bus, phy_dev->addr, - HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER); + retval = phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_LED); + retval = phy_write(phy_dev, HNS_LED_FC_REG, value); + retval = phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER); if (retval) { netdev_err(netdev, "mdiobus_write fail !\n"); return retval; @@ -1052,19 +1026,15 @@ int hns_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state) if (phy_dev) switch (state) { case ETHTOOL_ID_ACTIVE: - ret = mdiobus_write(phy_dev->bus, phy_dev->addr, - HNS_PHY_PAGE_REG, - HNS_PHY_PAGE_LED); + ret = phy_write(phy_dev, HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_LED); if (ret) return ret; - priv->phy_led_val = (u16)mdiobus_read(phy_dev->bus, - phy_dev->addr, - HNS_LED_FC_REG); + priv->phy_led_val = phy_read(phy_dev, HNS_LED_FC_REG); - ret = mdiobus_write(phy_dev->bus, phy_dev->addr, - HNS_PHY_PAGE_REG, - HNS_PHY_PAGE_COPPER); + ret = phy_write(phy_dev, HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_COPPER); if (ret) return ret; return 2; @@ -1079,20 +1049,18 @@ int hns_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state) return ret; break; case ETHTOOL_ID_INACTIVE: - ret = mdiobus_write(phy_dev->bus, phy_dev->addr, - HNS_PHY_PAGE_REG, - HNS_PHY_PAGE_LED); + ret = phy_write(phy_dev, HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_LED); if (ret) return ret; - ret = mdiobus_write(phy_dev->bus, phy_dev->addr, - HNS_LED_FC_REG, priv->phy_led_val); + ret = phy_write(phy_dev, HNS_LED_FC_REG, + priv->phy_led_val); if (ret) return ret; - ret = mdiobus_write(phy_dev->bus, phy_dev->addr, - HNS_PHY_PAGE_REG, - HNS_PHY_PAGE_COPPER); + ret = phy_write(phy_dev, HNS_PHY_PAGE_REG, + HNS_PHY_PAGE_COPPER); if (ret) return ret; break; -- cgit v1.2.3-59-g8ed1b From 72ba48be3ec8e70937ad97d4420ef7144617c64b Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:09 +0100 Subject: phy: Add phydev_err() and phydev_dbg() macros In preparation for moving some of the phy_device structure members, add macros for printing errors and debug information. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/at803x.c | 4 ++-- drivers/net/phy/bcm87xx.c | 5 +++-- drivers/net/phy/micrel.c | 16 +++++++++------- drivers/net/phy/phy.c | 5 +++-- include/linux/phy.h | 6 ++++++ 5 files changed, 23 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 2d020a3ec0b5..62361f8af375 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -281,8 +281,8 @@ static void at803x_link_change_notify(struct phy_device *phydev) at803x_context_restore(phydev, &context); - dev_dbg(&phydev->dev, "%s(): phy was reset\n", - __func__); + phydev_dbg(phydev, "%s(): phy was reset\n", + __func__); priv->phy_reset = true; } } else { diff --git a/drivers/net/phy/bcm87xx.c b/drivers/net/phy/bcm87xx.c index 1eca20452f03..71b491c7bf96 100644 --- a/drivers/net/phy/bcm87xx.c +++ b/drivers/net/phy/bcm87xx.c @@ -163,8 +163,9 @@ static int bcm87xx_did_interrupt(struct phy_device *phydev) reg = phy_read(phydev, BCM87XX_LASI_STATUS); if (reg < 0) { - dev_err(&phydev->dev, - "Error: Read of BCM87XX_LASI_STATUS failed: %d\n", reg); + phydev_err(phydev, + "Error: Read of BCM87XX_LASI_STATUS failed: %d\n", + reg); return 0; } return (reg & 1) != 0; diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 1a6048a8c29d..bf72365e90bc 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -224,7 +224,7 @@ static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val) rc = phy_write(phydev, reg, temp); out: if (rc < 0) - dev_err(&phydev->dev, "failed to set led mode\n"); + phydev_err(phydev, "failed to set led mode\n"); return rc; } @@ -243,7 +243,7 @@ static int kszphy_broadcast_disable(struct phy_device *phydev) ret = phy_write(phydev, MII_KSZPHY_OMSO, ret | KSZPHY_OMSO_B_CAST_OFF); out: if (ret) - dev_err(&phydev->dev, "failed to disable broadcast address\n"); + phydev_err(phydev, "failed to disable broadcast address\n"); return ret; } @@ -263,7 +263,7 @@ static int kszphy_nand_tree_disable(struct phy_device *phydev) ret & ~KSZPHY_OMSO_NAND_TREE_ON); out: if (ret) - dev_err(&phydev->dev, "failed to disable NAND tree mode\n"); + phydev_err(phydev, "failed to disable NAND tree mode\n"); return ret; } @@ -288,7 +288,8 @@ static int kszphy_config_init(struct phy_device *phydev) if (priv->rmii_ref_clk_sel) { ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val); if (ret) { - dev_err(&phydev->dev, "failed to set rmii reference clock\n"); + phydev_err(phydev, + "failed to set rmii reference clock\n"); return ret; } } @@ -649,8 +650,8 @@ static int kszphy_probe(struct phy_device *phydev) priv->led_mode = -1; if (priv->led_mode > 3) { - dev_err(&phydev->dev, "invalid led mode: 0x%02x\n", - priv->led_mode); + phydev_err(phydev, "invalid led mode: 0x%02x\n", + priv->led_mode); priv->led_mode = -1; } } else { @@ -672,7 +673,8 @@ static int kszphy_probe(struct phy_device *phydev) } else if (rate > 49500000 && rate < 50500000) { priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz; } else { - dev_err(&phydev->dev, "Clock rate out of range: %ld\n", rate); + phydev_err(phydev, "Clock rate out of range: %ld\n", + rate); return -EINVAL; } } diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 47cd306dbb3c..9771941cf0ee 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -995,8 +995,9 @@ void phy_state_machine(struct work_struct *work) if (err < 0) phy_error(phydev); - dev_dbg(&phydev->dev, "PHY state change %s -> %s\n", - phy_state_to_str(old_state), phy_state_to_str(phydev->state)); + phydev_dbg(phydev, "PHY state change %s -> %s\n", + phy_state_to_str(old_state), + phy_state_to_str(phydev->state)); queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, PHY_STATE_TIME * HZ); diff --git a/include/linux/phy.h b/include/linux/phy.h index 8ca161a37e8a..dbcf9fdd960c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -777,6 +777,12 @@ static inline int phy_read_status(struct phy_device *phydev) return phydev->drv->read_status(phydev); } +#define phydev_err(_phydev, format, args...) \ + dev_err(&_phydev->dev, format, ##args) + +#define phydev_dbg(_phydev, format, args...) \ + dev_dbg(&_phydev->dev, format, ##args) + int genphy_config_init(struct phy_device *phydev); int genphy_setup_forced(struct phy_device *phydev); int genphy_restart_aneg(struct phy_device *phydev); -- cgit v1.2.3-59-g8ed1b From 84eff6d194df442bee62c129f2f47efb0dbd0468 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:10 +0100 Subject: phy: add phydev_name() wrapper Add a phydev_name() function, to help with moving some structure members from phy_device. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/8390/ax88796.c | 2 +- drivers/net/ethernet/adi/bfin_mac.c | 4 ++-- drivers/net/ethernet/agere/et131x.c | 4 ++-- drivers/net/ethernet/amd/au1000_eth.c | 4 ++-- drivers/net/ethernet/broadcom/b44.c | 2 +- drivers/net/ethernet/broadcom/tg3.c | 4 ++-- drivers/net/ethernet/cadence/macb.c | 2 +- drivers/net/ethernet/dnet.c | 6 +++--- drivers/net/ethernet/faraday/ftgmac100.c | 2 +- drivers/net/ethernet/lantiq_etop.c | 4 ++-- drivers/net/ethernet/nxp/lpc_eth.c | 4 ++-- drivers/net/ethernet/rdc/r6040.c | 4 ++-- drivers/net/ethernet/renesas/ravb_main.c | 2 +- drivers/net/ethernet/renesas/sh_eth.c | 2 +- drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c | 2 +- drivers/net/ethernet/smsc/smsc911x.c | 2 +- drivers/net/ethernet/smsc/smsc9420.c | 4 ++-- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 2 +- drivers/net/ethernet/ti/davinci_emac.c | 2 +- drivers/net/ethernet/ti/davinci_mdio.c | 2 +- drivers/net/ethernet/ti/netcp_ethss.c | 4 ++-- drivers/net/ethernet/toshiba/tc35815.c | 4 ++-- drivers/net/phy/bcm7xxx.c | 2 +- drivers/net/phy/phy_device.c | 2 +- drivers/staging/netlogic/xlr_net.c | 6 +++--- include/linux/phy.h | 5 +++++ 26 files changed, 44 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index 0443654f0339..90b540a4a561 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -372,7 +372,7 @@ static int ax_mii_probe(struct net_device *dev) ax->phy_dev = phy_dev; netdev_info(dev, "PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - phy_dev->drv->name, dev_name(&phy_dev->dev), phy_dev->irq); + phy_dev->drv->name, phydev_name(phy_dev), phy_dev->irq); return 0; } diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index e0e95a15cab0..5f8a5182b8dc 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -419,7 +419,7 @@ static int mii_probe(struct net_device *dev, int phy_mode) return -EINVAL; } - phydev = phy_connect(dev, dev_name(&phydev->dev), + phydev = phy_connect(dev, phydev_name(phydev), &bfin_mac_adjust_link, phy_mode); if (IS_ERR(phydev)) { @@ -446,7 +446,7 @@ static int mii_probe(struct net_device *dev, int phy_mode) pr_info("attached PHY driver [%s] " "(mii_bus:phy_addr=%s, irq=%d, mdc_clk=%dHz(mdc_div=%d)@sclk=%dMHz)\n", - phydev->drv->name, dev_name(&phydev->dev), phydev->irq, + phydev->drv->name, phydev_name(phydev), phydev->irq, MDC_CLK, mdc_div, sclk/1000000); return 0; diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c index e0f3d197e7f2..80b706f0fc97 100644 --- a/drivers/net/ethernet/agere/et131x.c +++ b/drivers/net/ethernet/agere/et131x.c @@ -3265,7 +3265,7 @@ static int et131x_mii_probe(struct net_device *netdev) return -ENODEV; } - phydev = phy_connect(netdev, dev_name(&phydev->dev), + phydev = phy_connect(netdev, phydev_name(phydev), &et131x_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { @@ -3291,7 +3291,7 @@ static int et131x_mii_probe(struct net_device *netdev) dev_info(&adapter->pdev->dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n", - phydev->drv->name, dev_name(&phydev->dev)); + phydev->drv->name, phydev_name(phydev)); return 0; } diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index 5330bcb8a944..8a8d6f2a0f6f 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -558,7 +558,7 @@ static int au1000_mii_probe(struct net_device *dev) /* now we are supposed to have a proper phydev, to attach to... */ BUG_ON(phydev->attached_dev); - phydev = phy_connect(dev, dev_name(&phydev->dev), + phydev = phy_connect(dev, phydev_name(phydev), &au1000_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { @@ -585,7 +585,7 @@ static int au1000_mii_probe(struct net_device *dev) netdev_info(dev, "attached PHY driver [%s] " "(mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, dev_name(&phydev->dev), phydev->irq); + phydev->drv->name, phydev_name(phydev), phydev->irq); return 0; } diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index a3b1c07ae0af..928a2210e788 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -2316,7 +2316,7 @@ static int b44_register_phy_one(struct b44 *bp) bp->phy_addr = phydev->addr; dev_info(sdev->dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n", - phydev->drv->name, dev_name(&phydev->dev)); + phydev->drv->name, phydev_name(phydev)); return 0; diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 79789d8e52da..69d84d67f09a 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -2096,7 +2096,7 @@ static int tg3_phy_init(struct tg3 *tp) phydev = tp->mdio_bus->phy_map[tp->phy_addr]; /* Attach the MAC to the PHY. */ - phydev = phy_connect(tp->dev, dev_name(&phydev->dev), + phydev = phy_connect(tp->dev, phydev_name(phydev), tg3_adjust_link, phydev->interface); if (IS_ERR(phydev)) { dev_err(&tp->pdev->dev, "Could not attach to PHY\n"); @@ -17903,7 +17903,7 @@ static int tg3_init_one(struct pci_dev *pdev, phydev = tp->mdio_bus->phy_map[tp->phy_addr]; netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n", - phydev->drv->name, dev_name(&phydev->dev)); + phydev->drv->name, phydev_name(phydev)); } else { char *ethtype; diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 8b45bc9ac29e..001d60c5521c 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -2951,7 +2951,7 @@ static int macb_probe(struct platform_device *pdev) phydev = bp->phy_dev; netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, dev_name(&phydev->dev), phydev->irq); + phydev->drv->name, phydev_name(phydev), phydev->irq); return 0; diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c index 13d00a38a5bd..136b6010f704 100644 --- a/drivers/net/ethernet/dnet.c +++ b/drivers/net/ethernet/dnet.c @@ -274,11 +274,11 @@ static int dnet_mii_probe(struct net_device *dev) /* attach the mac to the phy */ if (bp->capabilities & DNET_HAS_RMII) { - phydev = phy_connect(dev, dev_name(&phydev->dev), + phydev = phy_connect(dev, phydev_name(phydev), &dnet_handle_link_change, PHY_INTERFACE_MODE_RMII); } else { - phydev = phy_connect(dev, dev_name(&phydev->dev), + phydev = phy_connect(dev, phydev_name(phydev), &dnet_handle_link_change, PHY_INTERFACE_MODE_MII); } @@ -894,7 +894,7 @@ static int dnet_probe(struct platform_device *pdev) phydev = bp->phy_dev; dev_info(&pdev->dev, "attached PHY driver [%s] " "(mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, dev_name(&phydev->dev), phydev->irq); + phydev->drv->name, phydev_name(phydev), phydev->irq); return 0; diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 6d0c5d5eea6d..c2e2ac6a0313 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -854,7 +854,7 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv) return -ENODEV; } - phydev = phy_connect(netdev, dev_name(&phydev->dev), + phydev = phy_connect(netdev, phydev_name(phydev), &ftgmac100_adjust_link, PHY_INTERFACE_MODE_GMII); if (IS_ERR(phydev)) { diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index 581928c068f2..274a3cec84c2 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -390,7 +390,7 @@ ltq_etop_mdio_probe(struct net_device *dev) return -ENODEV; } - phydev = phy_connect(dev, dev_name(&phydev->dev), + phydev = phy_connect(dev, phydev_name(phydev), <q_etop_mdio_link, priv->pldata->mii_mode); if (IS_ERR(phydev)) { @@ -410,7 +410,7 @@ ltq_etop_mdio_probe(struct net_device *dev) priv->phydev = phydev; pr_info("%s: attached PHY [%s] (phy_addr=%s, irq=%d)\n", dev->name, phydev->drv->name, - dev_name(&phydev->dev), phydev->irq); + phydev_name(phydev), phydev->irq); return 0; } diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 057665180f13..5801aa197697 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -797,7 +797,7 @@ static int lpc_mii_probe(struct net_device *ndev) netdev_info(ndev, "using MII interface\n"); else netdev_info(ndev, "using RMII interface\n"); - phydev = phy_connect(ndev, dev_name(&phydev->dev), + phydev = phy_connect(ndev, phydev_name(phydev), &lpc_handle_link_change, lpc_phy_interface_mode(&pldat->pdev->dev)); @@ -818,7 +818,7 @@ static int lpc_mii_probe(struct net_device *ndev) netdev_info(ndev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, dev_name(&phydev->dev), phydev->irq); + phydev->drv->name, phydev_name(phydev), phydev->irq); return 0; } diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c index 9a37247cf4b8..86a0887811c7 100644 --- a/drivers/net/ethernet/rdc/r6040.c +++ b/drivers/net/ethernet/rdc/r6040.c @@ -1039,7 +1039,7 @@ static int r6040_mii_probe(struct net_device *dev) return -ENODEV; } - phydev = phy_connect(dev, dev_name(&phydev->dev), &r6040_adjust_link, + phydev = phy_connect(dev, phydev_name(phydev), &r6040_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { @@ -1063,7 +1063,7 @@ static int r6040_mii_probe(struct net_device *dev) dev_info(&lp->pdev->dev, "attached PHY driver [%s] " "(mii_bus:phy_addr=%s)\n", - phydev->drv->name, dev_name(&phydev->dev)); + phydev->drv->name, phydev_name(phydev)); return 0; } diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 0e1ebb39ab46..2f6c974e4a6d 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -928,7 +928,7 @@ static int ravb_phy_init(struct net_device *ndev) phydev->supported &= ~PHY_10BT_FEATURES; netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n", - phydev->addr, phydev->irq, phydev->drv->name); + phydev->addr, phydev->irq, phydev_name(phydev)); priv->phydev = phydev; diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index baa81535a8fc..e14d28474b70 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1827,7 +1827,7 @@ static int sh_eth_phy_init(struct net_device *ndev) } netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n", - phydev->addr, phydev->irq, phydev->drv->name); + phydev->addr, phydev->irq, phydev_name(phydev)); mdp->phydev = phydev; diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c index 43ccb4a6de15..5b13b8c11bef 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c @@ -216,7 +216,7 @@ int sxgbe_mdio_register(struct net_device *ndev) } netdev_info(ndev, "PHY ID %08x at %d IRQ %s (%s)%s\n", phy->phy_id, phy_addr, irq_str, - dev_name(&phy->dev), act ? " active" : ""); + phydev_name(phy), act ? " active" : ""); phy_found = true; } } diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 219a99b7a631..067346d3209d 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -1033,7 +1033,7 @@ static int smsc911x_mii_probe(struct net_device *dev) netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, dev_name(&phydev->dev), phydev->irq); + phydev->drv->name, phydev_name(phydev), phydev->irq); /* mask with MAC supported features */ phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause | diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index 4a90cdae5444..a02ed6b63064 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -1167,7 +1167,7 @@ static int smsc9420_mii_probe(struct net_device *dev) netif_info(pd, probe, pd->dev, "PHY addr %d, phy_id 0x%08X\n", phydev->addr, phydev->phy_id); - phydev = phy_connect(dev, dev_name(&phydev->dev), + phydev = phy_connect(dev, phydev_name(phydev), smsc9420_phy_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { @@ -1176,7 +1176,7 @@ static int smsc9420_mii_probe(struct net_device *dev) } netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, dev_name(&phydev->dev), phydev->irq); + phydev->drv->name, phydev_name(phydev), phydev->irq); /* mask with MAC supported features */ phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause | diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 16c85ccd1762..05ba84118f37 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -298,7 +298,7 @@ int stmmac_mdio_register(struct net_device *ndev) } pr_info("%s: PHY ID %08x at %d IRQ %s (%s)%s\n", ndev->name, phydev->phy_id, addr, - irq_str, dev_name(&phydev->dev), + irq_str, phydev_name(phydev), act ? " active" : ""); found = 1; } diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 33bd3b902304..5a40b0256327 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1646,7 +1646,7 @@ static int emac_dev_open(struct net_device *ndev) dev_info(emac_dev, "attached PHY driver [%s] " "(mii_bus:phy_addr=%s, id=%x)\n", - priv->phydev->drv->name, dev_name(&priv->phydev->dev), + priv->phydev->drv->name, phydev_name(priv->phydev), priv->phydev->phy_id); } diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index c00084d689f3..88e8e6055b9f 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -396,7 +396,7 @@ static int davinci_mdio_probe(struct platform_device *pdev) phy = data->bus->phy_map[addr]; if (phy) { dev_info(dev, "phy[%d]: device %s, driver %s\n", - phy->addr, dev_name(&phy->dev), + phy->addr, phydev_name(phy), phy->drv ? phy->drv->name : "unknown"); } } diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c index 4e70e7586a09..d543298d6750 100644 --- a/drivers/net/ethernet/ti/netcp_ethss.c +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -2178,7 +2178,7 @@ static int gbe_slave_open(struct gbe_intf *gbe_intf) return -ENODEV; } dev_dbg(priv->dev, "phy found: id is: 0x%s\n", - dev_name(&slave->phy->dev)); + phydev_name(slave->phy)); phy_start(slave->phy); phy_read_status(slave->phy); } @@ -2681,7 +2681,7 @@ static void init_secondary_ports(struct gbe_priv *gbe_dev, slave->phy = NULL; } else { dev_dbg(dev, "phy found: id is: 0x%s\n", - dev_name(&slave->phy->dev)); + phydev_name(slave->phy)); phy_start(slave->phy); phy_read_status(slave->phy); } diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index 45ac38d29ed8..8df6072ac78d 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -631,7 +631,7 @@ static int tc_mii_probe(struct net_device *dev) } /* attach the mac to the phy */ - phydev = phy_connect(dev, dev_name(&phydev->dev), + phydev = phy_connect(dev, phydev_name(phydev), &tc_handle_link_change, lp->chiptype == TC35815_TX4939 ? PHY_INTERFACE_MODE_RMII : PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { @@ -640,7 +640,7 @@ static int tc_mii_probe(struct net_device *dev) } printk(KERN_INFO "%s: attached PHY driver [%s] " "(mii_bus:phy_addr=%s, id=%x)\n", - dev->name, phydev->drv->name, dev_name(&phydev->dev), + dev->name, phydev->drv->name, phydev_name(phydev), phydev->phy_id); /* mask with MAC supported features */ diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index d4083c381cd1..9f4e6eb886af 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -170,7 +170,7 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev) int ret = 0; pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n", - dev_name(&phydev->dev), phydev->drv->name, rev, patch); + phydev_name(phydev), phydev->drv->name, rev, patch); /* Dummy read to a register to workaround an issue upon reset where the * internal inverter may not allow the first MDIO transaction to pass diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 0bfbabad4431..0f179709a289 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -114,7 +114,7 @@ EXPORT_SYMBOL(phy_register_fixup_for_id); */ static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup) { - if (strcmp(fixup->bus_id, dev_name(&phydev->dev)) != 0) + if (strcmp(fixup->bus_id, phydev_name(phydev)) != 0) if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0) return 0; diff --git a/drivers/staging/netlogic/xlr_net.c b/drivers/staging/netlogic/xlr_net.c index 8ae01753b011..b939c4b5f229 100644 --- a/drivers/staging/netlogic/xlr_net.c +++ b/drivers/staging/netlogic/xlr_net.c @@ -838,8 +838,8 @@ static int xlr_mii_probe(struct xlr_net_priv *priv) } /* Attach MAC to PHY */ - phydev = phy_connect(priv->ndev, dev_name(&phydev->dev), - &xlr_gmac_link_adjust, priv->nd->phy_interface); + phydev = phy_connect(priv->ndev, phydev_name(phydev), + &xlr_gmac_link_adjust, priv->nd->phy_interface); if (IS_ERR(phydev)) { pr_err("could not attach PHY\n"); @@ -855,7 +855,7 @@ static int xlr_mii_probe(struct xlr_net_priv *priv) phydev->advertising = phydev->supported; pr_info("attached PHY driver [%s] (mii_bus:phy_addr=%s\n", - phydev->drv->name, dev_name(&phydev->dev)); + phydev->drv->name, phydev_name(phydev)); return 0; } diff --git a/include/linux/phy.h b/include/linux/phy.h index dbcf9fdd960c..5f5cc3424b9e 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -783,6 +783,11 @@ static inline int phy_read_status(struct phy_device *phydev) #define phydev_dbg(_phydev, format, args...) \ dev_dbg(&_phydev->dev, format, ##args) +static inline const char *phydev_name(const struct phy_device *phydev) +{ + return dev_name(&phydev->dev); +} + int genphy_config_init(struct phy_device *phydev); int genphy_setup_forced(struct phy_device *phydev); int genphy_restart_aneg(struct phy_device *phydev); -- cgit v1.2.3-59-g8ed1b From 04521bf840482af30a6ed54835cefadcecdd56da Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:11 +0100 Subject: net: dnet: Use phy_find_first() helper Replace the open coded search for the first phy with a call to the existing helper function. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/dnet.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c index 136b6010f704..0ec367521354 100644 --- a/drivers/net/ethernet/dnet.c +++ b/drivers/net/ethernet/dnet.c @@ -255,15 +255,9 @@ static int dnet_mii_probe(struct net_device *dev) { struct dnet *bp = netdev_priv(dev); struct phy_device *phydev = NULL; - int phy_addr; /* find the first phy */ - for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { - if (bp->mii_bus->phy_map[phy_addr]) { - phydev = bp->mii_bus->phy_map[phy_addr]; - break; - } - } + phydev = phy_find_first(bp->mii_bus); if (!phydev) { printk(KERN_ERR "%s: no PHY found\n", dev->name); -- cgit v1.2.3-59-g8ed1b From 053e7e169229adebbc27fc176c5369398e9f5eba Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:12 +0100 Subject: phy: phy_{read|write}_mmd_indirect: get addr from phydev The address of the device can be determined from the phydev structure, rather than passing it as a parameter. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm-phy-lib.c | 8 ++++---- drivers/net/phy/dp83867.c | 6 +++--- drivers/net/phy/microchip.c | 5 ++--- drivers/net/phy/phy.c | 36 ++++++++++++++---------------------- include/linux/phy.h | 7 ++----- 5 files changed, 25 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index ddb377e53633..df0416db0b88 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -184,25 +184,25 @@ int bcm_phy_enable_eee(struct phy_device *phydev) /* Enable EEE at PHY level */ val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, - MDIO_MMD_AN, phydev->addr); + MDIO_MMD_AN); if (val < 0) return val; val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X; phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, - MDIO_MMD_AN, phydev->addr, (u32)val); + MDIO_MMD_AN, (u32)val); /* Advertise EEE */ val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, - MDIO_MMD_AN, phydev->addr); + MDIO_MMD_AN); if (val < 0) return val; val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, - MDIO_MMD_AN, phydev->addr, (u32)val); + MDIO_MMD_AN, (u32)val); return 0; } diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 4ebf601073d9..e4c0b0c0af02 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -160,7 +160,7 @@ static int dp83867_config_init(struct phy_device *phydev) if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) && (phydev->interface <= PHY_INTERFACE_MODE_RGMII_RXID)) { val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, phydev->addr); + DP83867_DEVADDR); if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN); @@ -172,13 +172,13 @@ static int dp83867_config_init(struct phy_device *phydev) val |= DP83867_RGMII_RX_CLK_DELAY_EN; phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, phydev->addr, val); + DP83867_DEVADDR, val); delay = (dp83867->rx_id_delay | (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT)); phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL, - DP83867_DEVADDR, phydev->addr, delay); + DP83867_DEVADDR, delay); } return 0; diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c index c0a20ebd083b..99df5bc47424 100644 --- a/drivers/net/phy/microchip.c +++ b/drivers/net/phy/microchip.c @@ -78,10 +78,9 @@ static int lan88xx_probe(struct phy_device *phydev) priv->wolopts = 0; /* these values can be used to identify internal PHY */ - priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID, - 3, phydev->addr); + priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID, 3); priv->chip_rev = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_REV, - 3, phydev->addr); + 3); phydev->priv = priv; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 9771941cf0ee..56c8dd8c0c85 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1029,7 +1029,6 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, * @phydev: The PHY device bus * @prtad: MMD Address * @devad: MMD DEVAD - * @addr: PHY address on the MII bus * * Description: it reads data from the MMD registers (clause 22 to access to * clause 45) of the specified phy address. @@ -1039,10 +1038,10 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, * 3) Write reg 13 // MMD Data Command for MMD DEVAD * 3) Read reg 14 // Read MMD data */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, int addr) +int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) { struct phy_driver *phydrv = phydev->drv; + int addr = phydev->addr; int value = -1; if (!phydrv->read_mmd_indirect) { @@ -1066,7 +1065,6 @@ EXPORT_SYMBOL(phy_read_mmd_indirect); * @phydev: The PHY device * @prtad: MMD Address * @devad: MMD DEVAD - * @addr: PHY address on the MII bus * @data: data to write in the MMD register * * Description: Write data from the MMD registers of the specified @@ -1078,9 +1076,10 @@ EXPORT_SYMBOL(phy_read_mmd_indirect); * 3) Write reg 14 // Write MMD data */ void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, int addr, u32 data) + int devad, u32 data) { struct phy_driver *phydrv = phydev->drv; + int addr = phydev->addr; if (!phydrv->write_mmd_indirect) { struct mii_bus *bus = phydev->bus; @@ -1130,7 +1129,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) /* First check if the EEE ability is supported */ eee_cap = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, - MDIO_MMD_PCS, phydev->addr); + MDIO_MMD_PCS); if (eee_cap <= 0) goto eee_exit_err; @@ -1142,12 +1141,12 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) * the EEE advertising registers. */ eee_lp = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, - MDIO_MMD_AN, phydev->addr); + MDIO_MMD_AN); if (eee_lp <= 0) goto eee_exit_err; eee_adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, - MDIO_MMD_AN, phydev->addr); + MDIO_MMD_AN); if (eee_adv <= 0) goto eee_exit_err; @@ -1161,15 +1160,13 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) * clock while it is signaling LPI. */ int val = phy_read_mmd_indirect(phydev, MDIO_CTRL1, - MDIO_MMD_PCS, - phydev->addr); + MDIO_MMD_PCS); if (val < 0) return val; val |= MDIO_PCS_CTRL1_CLKSTOP_EN; phy_write_mmd_indirect(phydev, MDIO_CTRL1, - MDIO_MMD_PCS, phydev->addr, - val); + MDIO_MMD_PCS, val); } return 0; /* EEE supported */ @@ -1188,8 +1185,7 @@ EXPORT_SYMBOL(phy_init_eee); */ int phy_get_eee_err(struct phy_device *phydev) { - return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR, - MDIO_MMD_PCS, phydev->addr); + return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR, MDIO_MMD_PCS); } EXPORT_SYMBOL(phy_get_eee_err); @@ -1206,22 +1202,19 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) int val; /* Get Supported EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, - MDIO_MMD_PCS, phydev->addr); + val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, MDIO_MMD_PCS); if (val < 0) return val; data->supported = mmd_eee_cap_to_ethtool_sup_t(val); /* Get advertisement EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, - MDIO_MMD_AN, phydev->addr); + val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN); if (val < 0) return val; data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); /* Get LP advertisement EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, - MDIO_MMD_AN, phydev->addr); + val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, MDIO_MMD_AN); if (val < 0) return val; data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); @@ -1241,8 +1234,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) { int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised); - phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, - phydev->addr, val); + phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, val); return 0; } diff --git a/include/linux/phy.h b/include/linux/phy.h index 5f5cc3424b9e..08198ce98773 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -629,14 +629,12 @@ static inline int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) * phy_read_mmd_indirect - reads data from the MMD registers * @phydev: The PHY device bus * @prtad: MMD Address - * @devad: MMD DEVAD * @addr: PHY address on the MII bus * * Description: it reads data from the MMD registers (clause 22 to access to * clause 45) of the specified phy address. */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, int addr); +int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad); /** * phy_read - Convenience function for reading a given PHY register @@ -735,14 +733,13 @@ static inline int phy_write_mmd(struct phy_device *phydev, int devad, * @phydev: The PHY device * @prtad: MMD Address * @devad: MMD DEVAD - * @addr: PHY address on the MII bus * @data: data to write in the MMD register * * Description: Write data from the MMD registers of the specified * phy address. */ void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, int addr, u32 data); + int devad, u32 data); struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, bool is_c45, -- cgit v1.2.3-59-g8ed1b From 2220943a21e26d97d7fd8f83c004b947326b469d Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:13 +0100 Subject: phy: Centralise print about attached phy Many Ethernet drivers contain the same netdev_info() print statement about the attached phy. Move it into the phy device code. Additionally add a varargs function which can be used to append additional information. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/adi/bfin_mac.c | 6 ++---- drivers/net/ethernet/agere/et131x.c | 4 +--- drivers/net/ethernet/amd/au1000_eth.c | 4 +--- drivers/net/ethernet/broadcom/b44.c | 3 +-- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 3 +-- drivers/net/ethernet/broadcom/sb1250-mac.c | 7 +++---- drivers/net/ethernet/broadcom/tg3.c | 10 +++------- drivers/net/ethernet/cadence/macb.c | 3 +-- drivers/net/ethernet/dnet.c | 4 +--- drivers/net/ethernet/freescale/fec_main.c | 4 +--- drivers/net/ethernet/lantiq_etop.c | 4 +--- drivers/net/ethernet/nxp/lpc_eth.c | 5 ++--- drivers/net/ethernet/rdc/r6040.c | 4 +--- drivers/net/ethernet/renesas/ravb_main.c | 3 +-- drivers/net/ethernet/renesas/sh_eth.c | 3 +-- drivers/net/ethernet/smsc/smsc911x.c | 4 +--- drivers/net/ethernet/smsc/smsc9420.c | 9 ++------- drivers/net/ethernet/synopsys/dwc_eth_qos.c | 12 +----------- drivers/net/ethernet/ti/cpsw.c | 4 ++-- drivers/net/ethernet/ti/davinci_emac.c | 5 +---- drivers/net/ethernet/toshiba/tc35815.c | 6 ++---- drivers/net/phy/phy_device.c | 27 +++++++++++++++++++++++++++ drivers/staging/netlogic/xlr_net.c | 3 +-- include/linux/phy.h | 4 ++++ net/dsa/slave.c | 5 ++--- 25 files changed, 64 insertions(+), 82 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index 5f8a5182b8dc..ed5c78cb7239 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -444,10 +444,8 @@ static int mii_probe(struct net_device *dev, int phy_mode) lp->old_duplex = -1; lp->phydev = phydev; - pr_info("attached PHY driver [%s] " - "(mii_bus:phy_addr=%s, irq=%d, mdc_clk=%dHz(mdc_div=%d)@sclk=%dMHz)\n", - phydev->drv->name, phydev_name(phydev), phydev->irq, - MDC_CLK, mdc_div, sclk/1000000); + phy_attached_print(phydev, "mdc_clk=%dHz(mdc_div=%d)@sclk=%dMHz)\n", + MDC_CLK, mdc_div, sclk / 1000000); return 0; } diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c index 80b706f0fc97..825da3af806a 100644 --- a/drivers/net/ethernet/agere/et131x.c +++ b/drivers/net/ethernet/agere/et131x.c @@ -3289,9 +3289,7 @@ static int et131x_mii_probe(struct net_device *netdev) phydev->autoneg = AUTONEG_ENABLE; adapter->phydev = phydev; - dev_info(&adapter->pdev->dev, - "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n", - phydev->drv->name, phydev_name(phydev)); + phy_attached_info(phydev); return 0; } diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index 8a8d6f2a0f6f..114618d357d5 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -583,9 +583,7 @@ static int au1000_mii_probe(struct net_device *dev) aup->old_duplex = -1; aup->phy_dev = phydev; - netdev_info(dev, "attached PHY driver [%s] " - "(mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, phydev_name(phydev), phydev->irq); + phy_attached_info(phydev); return 0; } diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 928a2210e788..e7d9308d6760 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -2315,8 +2315,7 @@ static int b44_register_phy_one(struct b44 *bp) bp->old_link = 0; bp->phy_addr = phydev->addr; - dev_info(sdev->dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n", - phydev->drv->name, phydev_name(phydev)); + phy_attached_info(phydev); return 0; diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index a54bafad3538..55f31faa09e6 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -908,8 +908,7 @@ static int bcm_enet_open(struct net_device *dev) else phydev->advertising &= ~SUPPORTED_Pause; - dev_info(kdev, "attached PHY at address %d [%s]\n", - phydev->addr, phydev->drv->name); + phy_attached_info(phydev); priv->old_link = 0; priv->old_duplex = -1; diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index f557a2aaec23..2470c6084c67 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -2388,11 +2388,10 @@ static int sbmac_mii_probe(struct net_device *dev) SUPPORTED_MII | SUPPORTED_Pause | SUPPORTED_Asym_Pause; - phy_dev->advertising = phy_dev->supported; - pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - dev->name, phy_dev->drv->name, - dev_name(&phy_dev->dev), phy_dev->irq); + phy_attached_info(phydev); + + phy_dev->advertising = phy_dev->supported; sc->phy_dev = phy_dev; diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 69d84d67f09a..07c067590caa 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -2128,6 +2128,8 @@ static int tg3_phy_init(struct tg3 *tp) phydev->advertising = phydev->supported; + phy_attached_info(phydev); + return 0; } @@ -17898,13 +17900,7 @@ static int tg3_init_one(struct pci_dev *pdev, tg3_bus_string(tp, str), dev->dev_addr); - if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) { - struct phy_device *phydev; - phydev = tp->mdio_bus->phy_map[tp->phy_addr]; - netdev_info(dev, - "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n", - phydev->drv->name, phydev_name(phydev)); - } else { + if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) { char *ethtype; if (tp->phy_flags & TG3_PHYFLG_10_100_ONLY) diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 001d60c5521c..98df33b7a395 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -2950,8 +2950,7 @@ static int macb_probe(struct platform_device *pdev) dev->base_addr, dev->irq, dev->dev_addr); phydev = bp->phy_dev; - netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, phydev_name(phydev), phydev->irq); + phy_attached_info(phydev); return 0; diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c index 0ec367521354..6557460cf028 100644 --- a/drivers/net/ethernet/dnet.c +++ b/drivers/net/ethernet/dnet.c @@ -886,9 +886,7 @@ static int dnet_probe(struct platform_device *pdev) (bp->capabilities & DNET_HAS_GIGABIT) ? "" : "no ", (bp->capabilities & DNET_HAS_DMA) ? "" : "no "); phydev = bp->phy_dev; - dev_info(&pdev->dev, "attached PHY driver [%s] " - "(mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, phydev_name(phydev), phydev->irq); + phy_attached_info(phydev); return 0; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index d2328fc5da57..ceabe21b3b2c 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1972,9 +1972,7 @@ static int fec_enet_mii_probe(struct net_device *ndev) fep->link = 0; fep->full_duplex = 0; - netdev_info(ndev, "Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev), - fep->phy_dev->irq); + phy_attached_info(phy_dev); return 0; } diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index 274a3cec84c2..86238a5eaddf 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -408,9 +408,7 @@ ltq_etop_mdio_probe(struct net_device *dev) phydev->advertising = phydev->supported; priv->phydev = phydev; - pr_info("%s: attached PHY [%s] (phy_addr=%s, irq=%d)\n", - dev->name, phydev->drv->name, - phydev_name(phydev), phydev->irq); + phy_attached_info(phydev); return 0; } diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 5801aa197697..024bc3675573 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -816,9 +816,8 @@ static int lpc_mii_probe(struct net_device *ndev) pldat->duplex = -1; pldat->phy_dev = phydev; - netdev_info(ndev, - "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, phydev_name(phydev), phydev->irq); + phy_attached_info(phydev); + return 0; } diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c index 86a0887811c7..174dea787caf 100644 --- a/drivers/net/ethernet/rdc/r6040.c +++ b/drivers/net/ethernet/rdc/r6040.c @@ -1061,9 +1061,7 @@ static int r6040_mii_probe(struct net_device *dev) lp->old_link = 0; lp->old_duplex = -1; - dev_info(&lp->pdev->dev, "attached PHY driver [%s] " - "(mii_bus:phy_addr=%s)\n", - phydev->drv->name, phydev_name(phydev)); + phy_attached_info(phydev); return 0; } diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 2f6c974e4a6d..9e20f37a3b6f 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -927,8 +927,7 @@ static int ravb_phy_init(struct net_device *ndev) /* 10BASE is not supported */ phydev->supported &= ~PHY_10BT_FEATURES; - netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n", - phydev->addr, phydev->irq, phydev_name(phydev)); + phy_attached_info(phydev); priv->phydev = phydev; diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index e14d28474b70..94581be64d65 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1826,8 +1826,7 @@ static int sh_eth_phy_init(struct net_device *ndev) return PTR_ERR(phydev); } - netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n", - phydev->addr, phydev->irq, phydev_name(phydev)); + phy_attached_info(phydev); mdp->phydev = phydev; diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 067346d3209d..139b99b04099 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -1031,9 +1031,7 @@ static int smsc911x_mii_probe(struct net_device *dev) return ret; } - netdev_info(dev, - "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, phydev_name(phydev), phydev->irq); + phy_attached_info(phydev); /* mask with MAC supported features */ phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause | diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index a02ed6b63064..fa8893a804f7 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -1163,10 +1163,6 @@ static int smsc9420_mii_probe(struct net_device *dev) return -ENODEV; } - phydev = pd->mii_bus->phy_map[1]; - netif_info(pd, probe, pd->dev, "PHY addr %d, phy_id 0x%08X\n", - phydev->addr, phydev->phy_id); - phydev = phy_connect(dev, phydev_name(phydev), smsc9420_phy_adjust_link, PHY_INTERFACE_MODE_MII); @@ -1175,14 +1171,13 @@ static int smsc9420_mii_probe(struct net_device *dev) return PTR_ERR(phydev); } - netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - phydev->drv->name, phydev_name(phydev), phydev->irq); - /* mask with MAC supported features */ phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause); phydev->advertising = phydev->supported; + phy_attached_info(phydev); + pd->phy_dev = phydev; pd->last_duplex = -1; pd->last_carrier = -1; diff --git a/drivers/net/ethernet/synopsys/dwc_eth_qos.c b/drivers/net/ethernet/synopsys/dwc_eth_qos.c index 9066d7a8483c..b25ee370254a 100644 --- a/drivers/net/ethernet/synopsys/dwc_eth_qos.c +++ b/drivers/net/ethernet/synopsys/dwc_eth_qos.c @@ -972,9 +972,7 @@ static int dwceqos_mii_probe(struct net_device *ndev) } if (netif_msg_probe(lp)) - netdev_dbg(lp->ndev, - "phydev %p, phydev->phy_id 0xa%x, phydev->addr 0x%x\n", - phydev, phydev->phy_id, phydev->addr); + phy_attached_info(phydev); phydev->supported &= PHY_GBIT_FEATURES; @@ -983,14 +981,6 @@ static int dwceqos_mii_probe(struct net_device *ndev) lp->duplex = DUPLEX_UNKNOWN; lp->phy_dev = phydev; - if (netif_msg_probe(lp)) { - netdev_dbg(lp->ndev, "phy_addr 0x%x, phy_id 0x%08x\n", - lp->phy_dev->addr, lp->phy_dev->phy_id); - - netdev_dbg(lp->ndev, "attach [%s] phy driver\n", - lp->phy_dev->drv->name); - } - return 0; } diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 34ce7dce8c9d..49544c0fa6a7 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1159,8 +1159,8 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) slave->data->phy_id, slave->slave_num); slave->phy = NULL; } else { - dev_info(priv->dev, "phy found : id is : 0x%x\n", - slave->phy->phy_id); + phy_attached_info(slave->phy); + phy_start(slave->phy); /* Configure GMII_SEL register */ diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 5a40b0256327..5d9abedd6b75 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1644,10 +1644,7 @@ static int emac_dev_open(struct net_device *ndev) priv->speed = 0; priv->duplex = ~0; - dev_info(emac_dev, "attached PHY driver [%s] " - "(mii_bus:phy_addr=%s, id=%x)\n", - priv->phydev->drv->name, phydev_name(priv->phydev), - priv->phydev->phy_id); + phy_attached_info(priv->phydev); } if (!priv->phydev) { diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index 8df6072ac78d..8fd5e0ba718c 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -638,10 +638,8 @@ static int tc_mii_probe(struct net_device *dev) printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); return PTR_ERR(phydev); } - printk(KERN_INFO "%s: attached PHY driver [%s] " - "(mii_bus:phy_addr=%s, id=%x)\n", - dev->name, phydev->drv->name, phydev_name(phydev), - phydev->phy_id); + + phy_attached_info(phydev); /* mask with MAC supported features */ phydev->supported &= PHY_BASIC_FEATURES; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 0f179709a289..68fe5738daef 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -607,6 +607,33 @@ int phy_init_hw(struct phy_device *phydev) } EXPORT_SYMBOL(phy_init_hw); +void phy_attached_info(struct phy_device *phydev) +{ + phy_attached_print(phydev, NULL); +} +EXPORT_SYMBOL(phy_attached_info); + +#define ATTACHED_FMT "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)" +void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) +{ + if (!fmt) { + dev_info(&phydev->dev, ATTACHED_FMT "\n", + phydev->drv->name, phydev_name(phydev), + phydev->irq); + } else { + va_list ap; + + dev_info(&phydev->dev, ATTACHED_FMT, + phydev->drv->name, phydev_name(phydev), + phydev->irq); + + va_start(ap, fmt); + vprintk(fmt, ap); + va_end(ap); + } +} +EXPORT_SYMBOL(phy_attached_print); + /** * phy_attach_direct - attach a network device to a given PHY device pointer * @dev: network device to attach diff --git a/drivers/staging/netlogic/xlr_net.c b/drivers/staging/netlogic/xlr_net.c index b939c4b5f229..cbc25b7e70a2 100644 --- a/drivers/staging/netlogic/xlr_net.c +++ b/drivers/staging/netlogic/xlr_net.c @@ -854,8 +854,7 @@ static int xlr_mii_probe(struct xlr_net_priv *priv) | ADVERTISED_MII); phydev->advertising = phydev->supported; - pr_info("attached PHY driver [%s] (mii_bus:phy_addr=%s\n", - phydev->drv->name, phydev_name(phydev)); + phy_attached_info(phydev); return 0; } diff --git a/include/linux/phy.h b/include/linux/phy.h index 08198ce98773..ecbf6382ba29 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -16,6 +16,7 @@ #ifndef __PHY_H #define __PHY_H +#include #include #include #include @@ -785,6 +786,9 @@ static inline const char *phydev_name(const struct phy_device *phydev) return dev_name(&phydev->dev); } +void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) + __printf(2, 3); +void phy_attached_info(struct phy_device *phydev); int genphy_config_init(struct phy_device *phydev); int genphy_setup_forced(struct phy_device *phydev); int genphy_restart_aneg(struct phy_device *phydev); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 1e9e9424a33d..5f45e68b52dc 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1080,11 +1080,10 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p, netdev_err(slave_dev, "failed to connect to port %d: %d\n", p->port, ret); return ret; } - } else { - netdev_info(slave_dev, "attached PHY at address %d [%s]\n", - p->phy->addr, p->phy->drv->name); } + phy_attached_info(p->phy); + return 0; } -- cgit v1.2.3-59-g8ed1b From 35d2aeac9810ca717a72a4ff0d8a20d349e73e55 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:14 +0100 Subject: phy: mdio-octeon: Use devm_mdiobus_alloc_size() Rather than use devm_kzalloc(), use the mdio helper function. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio-octeon.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/mdio-octeon.c b/drivers/net/phy/mdio-octeon.c index fcf4e4df7cc8..0d5da1312dd3 100644 --- a/drivers/net/phy/mdio-octeon.c +++ b/drivers/net/phy/mdio-octeon.c @@ -268,12 +268,13 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id, static int octeon_mdiobus_probe(struct platform_device *pdev) { struct octeon_mdiobus *bus; + struct mii_bus *mii_bus; struct resource *res_mem; union cvmx_smix_en smi_en; int err = -ENOENT; - bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); - if (!bus) + mii_bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*bus)); + if (!mii_bus) return -ENOMEM; res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -282,6 +283,8 @@ static int octeon_mdiobus_probe(struct platform_device *pdev) return -ENXIO; } + bus = mii_bus->priv; + bus->mii_bus = mii_bus; bus->mdio_phys = res_mem->start; bus->regsize = resource_size(res_mem); @@ -298,10 +301,6 @@ static int octeon_mdiobus_probe(struct platform_device *pdev) return -ENOMEM; } - bus->mii_bus = mdiobus_alloc(); - if (!bus->mii_bus) - goto fail; - smi_en.u64 = 0; smi_en.s.en = 1; oct_mdio_writeq(smi_en.u64, bus->register_base + SMI_EN); @@ -326,7 +325,6 @@ static int octeon_mdiobus_probe(struct platform_device *pdev) return 0; fail_register: mdiobus_free(bus->mii_bus); -fail: smi_en.u64 = 0; oct_mdio_writeq(smi_en.u64, bus->register_base + SMI_EN); return err; -- cgit v1.2.3-59-g8ed1b From e7f4dc3536a40097f95103ddf98dd55b3a980f5b Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:15 +0100 Subject: mdio: Move allocation of interrupts into core Have mdio_alloc() create the array of interrupt numbers, and initialize it to POLLING. This is what most MDIO drivers want, so allowing code to be removed from the drivers. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- arch/powerpc/platforms/82xx/ep8248e.c | 10 +--------- arch/powerpc/platforms/pasemi/gpio_mdio.c | 3 --- drivers/net/ethernet/8390/ax88796.c | 15 ++------------- drivers/net/ethernet/adi/bfin_mac.c | 11 +---------- drivers/net/ethernet/aeroflex/greth.c | 5 ----- drivers/net/ethernet/aeroflex/greth.h | 1 - drivers/net/ethernet/agere/et131x.c | 13 +------------ drivers/net/ethernet/altera/altera_tse_main.c | 13 +------------ drivers/net/ethernet/amd/au1000_eth.c | 7 ------- drivers/net/ethernet/broadcom/b44.c | 14 +------------- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 9 --------- drivers/net/ethernet/broadcom/bgmac.c | 15 ++------------- drivers/net/ethernet/broadcom/genet/bcmmii.c | 10 ---------- drivers/net/ethernet/broadcom/sb1250-mac.c | 4 ---- drivers/net/ethernet/broadcom/tg3.c | 4 ---- drivers/net/ethernet/broadcom/tg3.h | 1 - drivers/net/ethernet/cadence/macb.c | 14 +------------- drivers/net/ethernet/dnet.c | 12 +----------- drivers/net/ethernet/ethoc.c | 14 +------------- drivers/net/ethernet/faraday/ftgmac100.c | 6 ------ drivers/net/ethernet/freescale/fec_main.c | 16 ++-------------- drivers/net/ethernet/freescale/fec_mpc52xx_phy.c | 4 ---- drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c | 10 +--------- drivers/net/ethernet/freescale/fs_enet/mii-fec.c | 10 +--------- drivers/net/ethernet/freescale/fsl_pq_mdio.c | 2 -- drivers/net/ethernet/hisilicon/hns_mdio.c | 5 ----- drivers/net/ethernet/lantiq_etop.c | 14 +------------- drivers/net/ethernet/marvell/mvmdio.c | 10 +--------- drivers/net/ethernet/nxp/lpc_eth.c | 13 +------------ drivers/net/ethernet/rdc/r6040.c | 14 +------------- drivers/net/ethernet/renesas/sh_eth.c | 12 +----------- drivers/net/ethernet/smsc/smsc911x.c | 6 ++---- drivers/net/ethernet/smsc/smsc9420.c | 6 +----- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 14 ++++---------- drivers/net/ethernet/synopsys/dwc_eth_qos.c | 15 ++------------- drivers/net/ethernet/ti/cpmac.c | 3 --- drivers/net/ethernet/toshiba/tc35815.c | 14 +------------- drivers/net/ethernet/xilinx/ll_temac.h | 1 - drivers/net/ethernet/xilinx/ll_temac_mdio.c | 2 -- drivers/net/ethernet/xilinx/xilinx_axienet.h | 2 -- drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c | 2 -- drivers/net/ethernet/xilinx/xilinx_emaclite.c | 4 ---- drivers/net/phy/fixed_phy.c | 4 +--- drivers/net/phy/mdio-bcm-unimac.c | 11 +---------- drivers/net/phy/mdio-gpio.c | 2 +- drivers/net/phy/mdio-moxart.c | 7 ------- drivers/net/phy/mdio-mux.c | 3 +-- drivers/net/phy/mdio-octeon.c | 2 -- drivers/net/phy/mdio-sun4i.c | 12 +----------- drivers/net/phy/mdio_bus.c | 5 +++++ drivers/net/usb/ax88172a.c | 14 ++------------ drivers/net/usb/lan78xx.c | 11 +---------- drivers/of/of_mdio.c | 7 +------ include/linux/phy.h | 6 +++--- 54 files changed, 48 insertions(+), 396 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/platforms/82xx/ep8248e.c b/arch/powerpc/platforms/82xx/ep8248e.c index a0cb8bd41958..6781bda117be 100644 --- a/arch/powerpc/platforms/82xx/ep8248e.c +++ b/arch/powerpc/platforms/82xx/ep8248e.c @@ -131,23 +131,15 @@ static int ep8248e_mdio_probe(struct platform_device *ofdev) if (!bus) return -ENOMEM; - bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (bus->irq == NULL) { - ret = -ENOMEM; - goto err_free_bus; - } - bus->name = "ep8248e-mdio-bitbang"; bus->parent = &ofdev->dev; snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start); ret = of_mdiobus_register(bus, ofdev->dev.of_node); if (ret) - goto err_free_irq; + goto err_free_bus; return 0; -err_free_irq: - kfree(bus->irq); err_free_bus: free_mdio_bitbang(bus); return ret; diff --git a/arch/powerpc/platforms/pasemi/gpio_mdio.c b/arch/powerpc/platforms/pasemi/gpio_mdio.c index ae3f47b25b18..ddf635000c6b 100644 --- a/arch/powerpc/platforms/pasemi/gpio_mdio.c +++ b/arch/powerpc/platforms/pasemi/gpio_mdio.c @@ -41,7 +41,6 @@ static void __iomem *gpio_regs; struct gpio_priv { int mdc_pin; int mdio_pin; - int mdio_irqs[PHY_MAX_ADDR]; }; #define MDC_PIN(bus) (((struct gpio_priv *)bus->priv)->mdc_pin) @@ -245,8 +244,6 @@ static int gpio_mdio_probe(struct platform_device *ofdev) snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop); new_bus->priv = priv; - new_bus->irq = priv->mdio_irqs; - prop = of_get_property(np, "mdc-pin", NULL); priv->mdc_pin = *prop; diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index 90b540a4a561..c89b9aeeceb6 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -627,7 +627,7 @@ static int ax_mii_init(struct net_device *dev) struct platform_device *pdev = to_platform_device(dev->dev.parent); struct ei_device *ei_local = netdev_priv(dev); struct ax_device *ax = to_ax_dev(dev); - int err, i; + int err; ax->bb_ctrl.ops = &bb_ops; ax->addr_memr = ei_local->mem + AX_MEMR; @@ -642,23 +642,12 @@ static int ax_mii_init(struct net_device *dev) snprintf(ax->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", pdev->name, pdev->id); - ax->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!ax->mii_bus->irq) { - err = -ENOMEM; - goto out_free_mdio_bitbang; - } - - for (i = 0; i < PHY_MAX_ADDR; i++) - ax->mii_bus->irq[i] = PHY_POLL; - err = mdiobus_register(ax->mii_bus); if (err) - goto out_free_irq; + goto out_free_mdio_bitbang; return 0; - out_free_irq: - kfree(ax->mii_bus->irq); out_free_mdio_bitbang: free_mdio_bitbang(ax->mii_bus); out: diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index ed5c78cb7239..62862744c870 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -1838,12 +1838,6 @@ static int bfin_mii_bus_probe(struct platform_device *pdev) snprintf(miibus->id, MII_BUS_ID_SIZE, "%s-%x", pdev->name, pdev->id); - miibus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); - if (!miibus->irq) - goto out_err_irq_alloc; - - for (i = rc; i < PHY_MAX_ADDR; ++i) - miibus->irq[i] = PHY_POLL; rc = clamp(mii_bus_pd->phydev_number, 0, PHY_MAX_ADDR); if (rc != mii_bus_pd->phydev_number) @@ -1862,14 +1856,12 @@ static int bfin_mii_bus_probe(struct platform_device *pdev) rc = mdiobus_register(miibus); if (rc) { dev_err(&pdev->dev, "Cannot register MDIO bus!\n"); - goto out_err_mdiobus_register; + goto out_err_alloc; } platform_set_drvdata(pdev, miibus); return 0; -out_err_mdiobus_register: - kfree(miibus->irq); out_err_irq_alloc: mdiobus_free(miibus); out_err_alloc: @@ -1885,7 +1877,6 @@ static int bfin_mii_bus_remove(struct platform_device *pdev) dev_get_platdata(&pdev->dev); mdiobus_unregister(miibus); - kfree(miibus->irq); mdiobus_free(miibus); peripheral_free_list(mii_bus_pd->mac_peripherals); diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index 20bf55dbd76f..b873531c5575 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1337,11 +1337,6 @@ static int greth_mdio_init(struct greth_private *greth) greth->mdio->write = greth_mdio_write; greth->mdio->priv = greth; - greth->mdio->irq = greth->mdio_irqs; - - for (phy = 0; phy < PHY_MAX_ADDR; phy++) - greth->mdio->irq[phy] = PHY_POLL; - ret = mdiobus_register(greth->mdio); if (ret) { goto error; diff --git a/drivers/net/ethernet/aeroflex/greth.h b/drivers/net/ethernet/aeroflex/greth.h index ae16ac94daf8..92dd918e4a83 100644 --- a/drivers/net/ethernet/aeroflex/greth.h +++ b/drivers/net/ethernet/aeroflex/greth.h @@ -125,7 +125,6 @@ struct greth_private { struct phy_device *phy; struct mii_bus *mdio; - int mdio_irqs[PHY_MAX_ADDR]; unsigned int link; unsigned int speed; unsigned int duplex; diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c index 825da3af806a..f29d45eea1d9 100644 --- a/drivers/net/ethernet/agere/et131x.c +++ b/drivers/net/ethernet/agere/et131x.c @@ -3325,7 +3325,6 @@ static void et131x_pci_remove(struct pci_dev *pdev) netif_napi_del(&adapter->napi); phy_disconnect(adapter->phydev); mdiobus_unregister(adapter->mii_bus); - kfree(adapter->mii_bus->irq); mdiobus_free(adapter->mii_bus); et131x_adapter_memory_free(adapter); @@ -3944,7 +3943,6 @@ static int et131x_pci_setup(struct pci_dev *pdev, struct net_device *netdev; struct et131x_adapter *adapter; int rc; - int ii; rc = pci_enable_device(pdev); if (rc < 0) { @@ -4034,18 +4032,11 @@ static int et131x_pci_setup(struct pci_dev *pdev, adapter->mii_bus->priv = netdev; adapter->mii_bus->read = et131x_mdio_read; adapter->mii_bus->write = et131x_mdio_write; - adapter->mii_bus->irq = kmalloc_array(PHY_MAX_ADDR, sizeof(int), - GFP_KERNEL); - if (!adapter->mii_bus->irq) - goto err_mdio_free; - - for (ii = 0; ii < PHY_MAX_ADDR; ii++) - adapter->mii_bus->irq[ii] = PHY_POLL; rc = mdiobus_register(adapter->mii_bus); if (rc < 0) { dev_err(&pdev->dev, "failed to register MII bus\n"); - goto err_mdio_free_irq; + goto err_mdio_free; } rc = et131x_mii_probe(netdev); @@ -4085,8 +4076,6 @@ err_phy_disconnect: phy_disconnect(adapter->phydev); err_mdio_unregister: mdiobus_unregister(adapter->mii_bus); -err_mdio_free_irq: - kfree(adapter->mii_bus->irq); err_mdio_free: mdiobus_free(adapter->mii_bus); err_mem_free: diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index fe644823ceaf..10d51e8aefe0 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -131,7 +131,6 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id) { struct altera_tse_private *priv = netdev_priv(dev); int ret; - int i; struct device_node *mdio_node = NULL; struct mii_bus *mdio = NULL; struct device_node *child_node = NULL; @@ -161,14 +160,6 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id) mdio->write = &altera_tse_mdio_write; snprintf(mdio->id, MII_BUS_ID_SIZE, "%s-%u", mdio->name, id); - mdio->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); - if (mdio->irq == NULL) { - ret = -ENOMEM; - goto out_free_mdio; - } - for (i = 0; i < PHY_MAX_ADDR; i++) - mdio->irq[i] = PHY_POLL; - mdio->priv = dev; mdio->parent = priv->device; @@ -176,7 +167,7 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id) if (ret != 0) { netdev_err(dev, "Cannot register MDIO bus %s\n", mdio->id); - goto out_free_mdio_irq; + goto out_free_mdio; } if (netif_msg_drv(priv)) @@ -184,8 +175,6 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id) priv->mdio = mdio; return 0; -out_free_mdio_irq: - kfree(mdio->irq); out_free_mdio: mdiobus_free(mdio); mdio = NULL; diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index 114618d357d5..982b581d3484 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -1291,14 +1291,7 @@ static int au1000_probe(struct platform_device *pdev) aup->mii_bus->name = "au1000_eth_mii"; snprintf(aup->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", pdev->name, aup->mac_id); - aup->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); - if (aup->mii_bus->irq == NULL) { - err = -ENOMEM; - goto err_out; - } - for (i = 0; i < PHY_MAX_ADDR; ++i) - aup->mii_bus->irq[i] = PHY_POLL; /* if known, set corresponding PHY IRQs */ if (aup->phy_static_config) if (aup->phy_irq && aup->phy_busid == aup->mac_id) diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index e7d9308d6760..4d08bc02c7a8 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -2263,21 +2263,13 @@ static int b44_register_phy_one(struct b44 *bp) mii_bus->parent = sdev->dev; mii_bus->phy_mask = ~(1 << bp->phy_addr); snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%x", instance); - mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!mii_bus->irq) { - dev_err(sdev->dev, "mii_bus irq allocation failed\n"); - err = -ENOMEM; - goto err_out_mdiobus; - } - - memset(mii_bus->irq, PHY_POLL, sizeof(int) * PHY_MAX_ADDR); bp->mii_bus = mii_bus; err = mdiobus_register(mii_bus); if (err) { dev_err(sdev->dev, "failed to register MII bus\n"); - goto err_out_mdiobus_irq; + goto err_out_mdiobus; } if (!bp->mii_bus->phy_map[bp->phy_addr] && @@ -2322,9 +2314,6 @@ static int b44_register_phy_one(struct b44 *bp) err_out_mdiobus_unregister: mdiobus_unregister(mii_bus); -err_out_mdiobus_irq: - kfree(mii_bus->irq); - err_out_mdiobus: mdiobus_free(mii_bus); @@ -2338,7 +2327,6 @@ static void b44_unregister_phy_one(struct b44 *bp) phy_disconnect(bp->phydev); mdiobus_unregister(mii_bus); - kfree(mii_bus->irq); mdiobus_free(mii_bus); } diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 55f31faa09e6..87c6b5bdd616 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1848,17 +1848,8 @@ static int bcm_enet_probe(struct platform_device *pdev) * if a slave is not present on hw */ bus->phy_mask = ~(1 << priv->phy_id); - bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR, - GFP_KERNEL); - if (!bus->irq) { - ret = -ENOMEM; - goto out_free_mdio; - } - if (priv->has_phy_interrupt) bus->irq[priv->phy_id] = priv->phy_interrupt; - else - bus->irq[priv->phy_id] = PHY_POLL; ret = mdiobus_register(bus); if (ret) { diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 28f7610b03fe..c7798d360512 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1471,7 +1471,7 @@ static int bgmac_mii_register(struct bgmac *bgmac) struct mii_bus *mii_bus; struct phy_device *phy_dev; char bus_id[MII_BUS_ID_SIZE + 3]; - int i, err = 0; + int err = 0; if (ci->id == BCMA_CHIP_ID_BCM4707 || ci->id == BCMA_CHIP_ID_BCM53018) @@ -1490,18 +1490,10 @@ static int bgmac_mii_register(struct bgmac *bgmac) mii_bus->parent = &bgmac->core->dev; mii_bus->phy_mask = ~(1 << bgmac->phyaddr); - mii_bus->irq = kmalloc_array(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); - if (!mii_bus->irq) { - err = -ENOMEM; - goto err_free_bus; - } - for (i = 0; i < PHY_MAX_ADDR; i++) - mii_bus->irq[i] = PHY_POLL; - err = mdiobus_register(mii_bus); if (err) { bgmac_err(bgmac, "Registration of mii bus failed\n"); - goto err_free_irq; + goto err_free_bus; } bgmac->mii_bus = mii_bus; @@ -1522,8 +1514,6 @@ static int bgmac_mii_register(struct bgmac *bgmac) err_unregister_bus: mdiobus_unregister(mii_bus); -err_free_irq: - kfree(mii_bus->irq); err_free_bus: mdiobus_free(mii_bus); return err; @@ -1534,7 +1524,6 @@ static void bgmac_mii_unregister(struct bgmac *bgmac) struct mii_bus *mii_bus = bgmac->mii_bus; mdiobus_unregister(mii_bus); - kfree(mii_bus->irq); mdiobus_free(mii_bus); } diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 8bdfe53754ba..4523acd8c1c2 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -402,8 +402,6 @@ int bcmgenet_mii_probe(struct net_device *dev) */ if (priv->internal_phy) priv->mii_bus->irq[phydev->addr] = PHY_IGNORE_INTERRUPT; - else - priv->mii_bus->irq[phydev->addr] = PHY_POLL; return 0; } @@ -477,12 +475,6 @@ static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv) snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", priv->pdev->name, priv->pdev->id); - bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); - if (!bus->irq) { - mdiobus_free(priv->mii_bus); - return -ENOMEM; - } - return 0; } @@ -648,7 +640,6 @@ int bcmgenet_mii_init(struct net_device *dev) out: of_node_put(priv->phy_dn); mdiobus_unregister(priv->mii_bus); - kfree(priv->mii_bus->irq); mdiobus_free(priv->mii_bus); return ret; } @@ -659,6 +650,5 @@ void bcmgenet_mii_exit(struct net_device *dev) of_node_put(priv->phy_dn); mdiobus_unregister(priv->mii_bus); - kfree(priv->mii_bus->irq); mdiobus_free(priv->mii_bus); } diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index 2470c6084c67..68a363708d27 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -238,7 +238,6 @@ struct sbmac_softc { struct napi_struct napi; struct phy_device *phy_dev; /* the associated PHY device */ struct mii_bus *mii_bus; /* the MII bus */ - int phy_irq[PHY_MAX_ADDR]; spinlock_t sbm_lock; /* spin lock */ int sbm_devflags; /* current device flags */ @@ -2250,9 +2249,6 @@ static int sbmac_init(struct platform_device *pldev, long long base) sc->mii_bus->priv = sc; sc->mii_bus->read = sbmac_mii_read; sc->mii_bus->write = sbmac_mii_write; - sc->mii_bus->irq = sc->phy_irq; - for (i = 0; i < PHY_MAX_ADDR; ++i) - sc->mii_bus->irq[i] = SBMAC_PHY_INT; sc->mii_bus->parent = &pldev->dev; /* diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 07c067590caa..04e7d0d0e5b1 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -1538,10 +1538,6 @@ static int tg3_mdio_init(struct tg3 *tp) tp->mdio_bus->read = &tg3_mdio_read; tp->mdio_bus->write = &tg3_mdio_write; tp->mdio_bus->phy_mask = ~(1 << tp->phy_addr); - tp->mdio_bus->irq = &tp->mdio_irq[0]; - - for (i = 0; i < PHY_MAX_ADDR; i++) - tp->mdio_bus->irq[i] = PHY_POLL; /* The bus registration will look for all the PHYs on the mdio bus. * Unfortunately, it does not ensure the PHY is powered up before diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h index 31c9f8295953..3b5e98ecba00 100644 --- a/drivers/net/ethernet/broadcom/tg3.h +++ b/drivers/net/ethernet/broadcom/tg3.h @@ -3254,7 +3254,6 @@ struct tg3 { int pcie_readrq; struct mii_bus *mdio_bus; - int mdio_irq[PHY_MAX_ADDR]; int old_link; u8 phy_addr; diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 98df33b7a395..eb1397484eef 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -441,12 +441,6 @@ static int macb_mii_init(struct macb *bp) bp->mii_bus->parent = &bp->dev->dev; pdata = dev_get_platdata(&bp->pdev->dev); - bp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); - if (!bp->mii_bus->irq) { - err = -ENOMEM; - goto err_out_free_mdiobus; - } - dev_set_drvdata(&bp->dev->dev, bp->mii_bus); np = bp->pdev->dev.of_node; @@ -471,9 +465,6 @@ static int macb_mii_init(struct macb *bp) goto err_out_unregister_bus; } } else { - for (i = 0; i < PHY_MAX_ADDR; i++) - bp->mii_bus->irq[i] = PHY_POLL; - if (pdata) bp->mii_bus->phy_mask = pdata->phy_mask; @@ -481,7 +472,7 @@ static int macb_mii_init(struct macb *bp) } if (err) - goto err_out_free_mdio_irq; + goto err_out_free_mdiobus; err = macb_mii_probe(bp->dev); if (err) @@ -491,8 +482,6 @@ static int macb_mii_init(struct macb *bp) err_out_unregister_bus: mdiobus_unregister(bp->mii_bus); -err_out_free_mdio_irq: - kfree(bp->mii_bus->irq); err_out_free_mdiobus: mdiobus_free(bp->mii_bus); err_out: @@ -2980,7 +2969,6 @@ static int macb_remove(struct platform_device *pdev) if (bp->phy_dev) phy_disconnect(bp->phy_dev); mdiobus_unregister(bp->mii_bus); - kfree(bp->mii_bus->irq); mdiobus_free(bp->mii_bus); /* Shutdown the PHY if there is a GPIO reset */ diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c index 6557460cf028..b69a9eacc531 100644 --- a/drivers/net/ethernet/dnet.c +++ b/drivers/net/ethernet/dnet.c @@ -302,7 +302,7 @@ static int dnet_mii_probe(struct net_device *dev) static int dnet_mii_init(struct dnet *bp) { - int err, i; + int err; bp->mii_bus = mdiobus_alloc(); if (bp->mii_bus == NULL) @@ -317,16 +317,6 @@ static int dnet_mii_init(struct dnet *bp) bp->mii_bus->priv = bp; - bp->mii_bus->irq = devm_kmalloc(&bp->pdev->dev, - sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!bp->mii_bus->irq) { - err = -ENOMEM; - goto err_out; - } - - for (i = 0; i < PHY_MAX_ADDR; i++) - bp->mii_bus->irq[i] = PHY_POLL; - if (mdiobus_register(bp->mii_bus)) { err = -ENXIO; goto err_out; diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index ff665493ca97..c028b299ab3f 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -1015,7 +1015,6 @@ static int ethoc_probe(struct platform_device *pdev) struct resource *mmio = NULL; struct resource *mem = NULL; struct ethoc *priv = NULL; - unsigned int phy; int num_bd; int ret = 0; bool random_mac = false; @@ -1206,19 +1205,10 @@ static int ethoc_probe(struct platform_device *pdev) priv->mdio->write = ethoc_mdio_write; priv->mdio->priv = priv; - priv->mdio->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!priv->mdio->irq) { - ret = -ENOMEM; - goto free_mdio; - } - - for (phy = 0; phy < PHY_MAX_ADDR; phy++) - priv->mdio->irq[phy] = PHY_POLL; - ret = mdiobus_register(priv->mdio); if (ret) { dev_err(&netdev->dev, "failed to register MDIO bus\n"); - goto free_mdio; + goto free; } ret = ethoc_mdio_probe(netdev); @@ -1250,8 +1240,6 @@ error2: netif_napi_del(&priv->napi); error: mdiobus_unregister(priv->mdio); -free_mdio: - kfree(priv->mdio->irq); mdiobus_free(priv->mdio); free: if (priv->clk) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index c2e2ac6a0313..8f3f2cf0dcbf 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -71,7 +71,6 @@ struct ftgmac100 { struct napi_struct napi; struct mii_bus *mii_bus; - int phy_irq[PHY_MAX_ADDR]; struct phy_device *phydev; int old_speed; }; @@ -1188,7 +1187,6 @@ static int ftgmac100_probe(struct platform_device *pdev) struct net_device *netdev; struct ftgmac100 *priv; int err; - int i; if (!pdev) return -ENODEV; @@ -1257,10 +1255,6 @@ static int ftgmac100_probe(struct platform_device *pdev) priv->mii_bus->priv = netdev; priv->mii_bus->read = ftgmac100_mdiobus_read; priv->mii_bus->write = ftgmac100_mdiobus_write; - priv->mii_bus->irq = priv->phy_irq; - - for (i = 0; i < PHY_MAX_ADDR; i++) - priv->mii_bus->irq[i] = PHY_POLL; err = mdiobus_register(priv->mii_bus); if (err) { diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index ceabe21b3b2c..da255fb4f1d5 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1983,7 +1983,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct fec_enet_private *fep = netdev_priv(ndev); struct device_node *node; - int err = -ENXIO, i; + int err = -ENXIO; u32 mii_speed, holdtime; /* @@ -2065,15 +2065,6 @@ static int fec_enet_mii_init(struct platform_device *pdev) fep->mii_bus->priv = fep; fep->mii_bus->parent = &pdev->dev; - fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!fep->mii_bus->irq) { - err = -ENOMEM; - goto err_out_free_mdiobus; - } - - for (i = 0; i < PHY_MAX_ADDR; i++) - fep->mii_bus->irq[i] = PHY_POLL; - node = of_get_child_by_name(pdev->dev.of_node, "mdio"); if (node) { err = of_mdiobus_register(fep->mii_bus, node); @@ -2083,7 +2074,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) } if (err) - goto err_out_free_mdio_irq; + goto err_out_free_mdiobus; mii_cnt++; @@ -2093,8 +2084,6 @@ static int fec_enet_mii_init(struct platform_device *pdev) return 0; -err_out_free_mdio_irq: - kfree(fep->mii_bus->irq); err_out_free_mdiobus: mdiobus_free(fep->mii_bus); err_out: @@ -2105,7 +2094,6 @@ static void fec_enet_mii_remove(struct fec_enet_private *fep) { if (--mii_cnt == 0) { mdiobus_unregister(fep->mii_bus); - kfree(fep->mii_bus->irq); mdiobus_free(fep->mii_bus); } } diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c index 1e647beaf989..b5497e308302 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c @@ -22,7 +22,6 @@ struct mpc52xx_fec_mdio_priv { struct mpc52xx_fec __iomem *regs; - int mdio_irqs[PHY_MAX_ADDR]; }; static int mpc52xx_fec_mdio_transfer(struct mii_bus *bus, int phy_id, @@ -83,9 +82,6 @@ static int mpc52xx_fec_mdio_probe(struct platform_device *of) bus->read = mpc52xx_fec_mdio_read; bus->write = mpc52xx_fec_mdio_write; - /* setup irqs */ - bus->irq = priv->mdio_irqs; - /* setup registers */ err = of_address_to_resource(np, 0, &res); if (err) diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c index 68a428de0bc0..1f015edcca22 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c +++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c @@ -172,23 +172,16 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev) goto out_free_bus; new_bus->phy_mask = ~0; - new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!new_bus->irq) { - ret = -ENOMEM; - goto out_unmap_regs; - } new_bus->parent = &ofdev->dev; platform_set_drvdata(ofdev, new_bus); ret = of_mdiobus_register(new_bus, ofdev->dev.of_node); if (ret) - goto out_free_irqs; + goto out_unmap_regs; return 0; -out_free_irqs: - kfree(new_bus->irq); out_unmap_regs: iounmap(bitbang->dir); out_free_bus: @@ -205,7 +198,6 @@ static int fs_enet_mdio_remove(struct platform_device *ofdev) struct bb_info *bitbang = bus->priv; mdiobus_unregister(bus); - kfree(bus->irq); free_mdio_bitbang(bus); iounmap(bitbang->dir); kfree(bitbang); diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c index 2be383e6d258..a89267b94352 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c @@ -166,23 +166,16 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev) clrsetbits_be32(&fec->fecp->fec_mii_speed, 0x7E, fec->mii_speed); new_bus->phy_mask = ~0; - new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!new_bus->irq) { - ret = -ENOMEM; - goto out_unmap_regs; - } new_bus->parent = &ofdev->dev; platform_set_drvdata(ofdev, new_bus); ret = of_mdiobus_register(new_bus, ofdev->dev.of_node); if (ret) - goto out_free_irqs; + goto out_unmap_regs; return 0; -out_free_irqs: - kfree(new_bus->irq); out_unmap_regs: iounmap(fec->fecp); out_res: @@ -200,7 +193,6 @@ static int fs_enet_mdio_remove(struct platform_device *ofdev) struct fec_info *fec = bus->priv; mdiobus_unregister(bus); - kfree(bus->irq); iounmap(fec->fecp); kfree(fec); mdiobus_free(bus); diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c index 40071dad1c57..622005abf859 100644 --- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c +++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c @@ -69,7 +69,6 @@ struct fsl_pq_mdio { struct fsl_pq_mdio_priv { void __iomem *map; struct fsl_pq_mii __iomem *regs; - int irqs[PHY_MAX_ADDR]; }; /* @@ -401,7 +400,6 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev) new_bus->read = &fsl_pq_mdio_read; new_bus->write = &fsl_pq_mdio_write; new_bus->reset = &fsl_pq_mdio_reset; - new_bus->irq = priv->irqs; err = of_address_to_resource(np, 0, &res); if (err < 0) { diff --git a/drivers/net/ethernet/hisilicon/hns_mdio.c b/drivers/net/ethernet/hisilicon/hns_mdio.c index 37491c85bc42..58c96c412fe8 100644 --- a/drivers/net/ethernet/hisilicon/hns_mdio.c +++ b/drivers/net/ethernet/hisilicon/hns_mdio.c @@ -463,11 +463,6 @@ static int hns_mdio_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "no syscon hisilicon,peri-c-subctrl\n"); mdio_dev->subctrl_vbase = NULL; } - new_bus->irq = devm_kcalloc(&pdev->dev, PHY_MAX_ADDR, - sizeof(int), GFP_KERNEL); - if (!new_bus->irq) - return -ENOMEM; - new_bus->parent = &pdev->dev; platform_set_drvdata(pdev, new_bus); diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index 86238a5eaddf..fb61f7f96bb4 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -433,18 +433,9 @@ ltq_etop_mdio_init(struct net_device *dev) priv->mii_bus->name = "ltq_mii"; snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", priv->pdev->name, priv->pdev->id); - priv->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!priv->mii_bus->irq) { - err = -ENOMEM; - goto err_out_free_mdiobus; - } - - for (i = 0; i < PHY_MAX_ADDR; ++i) - priv->mii_bus->irq[i] = PHY_POLL; - if (mdiobus_register(priv->mii_bus)) { err = -ENXIO; - goto err_out_free_mdio_irq; + goto err_out_free_mdiobus; } if (ltq_etop_mdio_probe(dev)) { @@ -455,8 +446,6 @@ ltq_etop_mdio_init(struct net_device *dev) err_out_unregister_bus: mdiobus_unregister(priv->mii_bus); -err_out_free_mdio_irq: - kfree(priv->mii_bus->irq); err_out_free_mdiobus: mdiobus_free(priv->mii_bus); err_out: @@ -470,7 +459,6 @@ ltq_etop_mdio_cleanup(struct net_device *dev) phy_disconnect(priv->phydev); mdiobus_unregister(priv->mii_bus); - kfree(priv->mii_bus->irq); mdiobus_free(priv->mii_bus); } diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index fc2fb25343f4..8982c882af1b 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -187,7 +187,7 @@ static int orion_mdio_probe(struct platform_device *pdev) struct resource *r; struct mii_bus *bus; struct orion_mdio_dev *dev; - int i, ret; + int ret; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { @@ -207,14 +207,6 @@ static int orion_mdio_probe(struct platform_device *pdev) dev_name(&pdev->dev)); bus->parent = &pdev->dev; - bus->irq = devm_kmalloc_array(&pdev->dev, PHY_MAX_ADDR, sizeof(int), - GFP_KERNEL); - if (!bus->irq) - return -ENOMEM; - - for (i = 0; i < PHY_MAX_ADDR; i++) - bus->irq[i] = PHY_POLL; - dev = bus->priv; dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); if (!dev->regs) { diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 024bc3675573..a9ce37f9bb2e 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -850,19 +850,10 @@ static int lpc_mii_init(struct netdata_local *pldat) pldat->mii_bus->priv = pldat; pldat->mii_bus->parent = &pldat->pdev->dev; - pldat->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!pldat->mii_bus->irq) { - err = -ENOMEM; - goto err_out_1; - } - - for (i = 0; i < PHY_MAX_ADDR; i++) - pldat->mii_bus->irq[i] = PHY_POLL; - platform_set_drvdata(pldat->pdev, pldat->mii_bus); if (mdiobus_register(pldat->mii_bus)) - goto err_out_free_mdio_irq; + goto err_out_unregister_bus; if (lpc_mii_probe(pldat->ndev) != 0) goto err_out_unregister_bus; @@ -871,8 +862,6 @@ static int lpc_mii_init(struct netdata_local *pldat) err_out_unregister_bus: mdiobus_unregister(pldat->mii_bus); -err_out_free_mdio_irq: - kfree(pldat->mii_bus->irq); err_out_1: mdiobus_free(pldat->mii_bus); err_out: diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c index 174dea787caf..6b541e57c96a 100644 --- a/drivers/net/ethernet/rdc/r6040.c +++ b/drivers/net/ethernet/rdc/r6040.c @@ -1075,7 +1075,6 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) static int card_idx = -1; int bar = 0; u16 *adrp; - int i; pr_info("%s\n", version); @@ -1187,19 +1186,11 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) lp->mii_bus->name = "r6040_eth_mii"; snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", dev_name(&pdev->dev), card_idx); - lp->mii_bus->irq = kmalloc_array(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); - if (!lp->mii_bus->irq) { - err = -ENOMEM; - goto err_out_mdio; - } - - for (i = 0; i < PHY_MAX_ADDR; i++) - lp->mii_bus->irq[i] = PHY_POLL; err = mdiobus_register(lp->mii_bus); if (err) { dev_err(&pdev->dev, "failed to register MII bus\n"); - goto err_out_mdio_irq; + goto err_out_mdio; } err = r6040_mii_probe(dev); @@ -1218,8 +1209,6 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) err_out_mdio_unregister: mdiobus_unregister(lp->mii_bus); -err_out_mdio_irq: - kfree(lp->mii_bus->irq); err_out_mdio: mdiobus_free(lp->mii_bus); err_out_unmap: @@ -1242,7 +1231,6 @@ static void r6040_remove_one(struct pci_dev *pdev) unregister_netdev(dev); mdiobus_unregister(lp->mii_bus); - kfree(lp->mii_bus->irq); mdiobus_free(lp->mii_bus); netif_napi_del(&lp->napi); pci_iounmap(pdev, lp->base); diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 94581be64d65..c5ec57ce3ddc 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2859,7 +2859,7 @@ static int sh_mdio_release(struct sh_eth_private *mdp) static int sh_mdio_init(struct sh_eth_private *mdp, struct sh_eth_plat_data *pd) { - int ret, i; + int ret; struct bb_info *bitbang; struct platform_device *pdev = mdp->pdev; struct device *dev = &mdp->pdev->dev; @@ -2885,20 +2885,10 @@ static int sh_mdio_init(struct sh_eth_private *mdp, snprintf(mdp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", pdev->name, pdev->id); - /* PHY IRQ */ - mdp->mii_bus->irq = devm_kmalloc_array(dev, PHY_MAX_ADDR, sizeof(int), - GFP_KERNEL); - if (!mdp->mii_bus->irq) { - ret = -ENOMEM; - goto out_free_bus; - } - /* register MDIO bus */ if (dev->of_node) { ret = of_mdiobus_register(mdp->mii_bus, dev->of_node); } else { - for (i = 0; i < PHY_MAX_ADDR; i++) - mdp->mii_bus->irq[i] = PHY_POLL; if (pd->phy_irq > 0) mdp->mii_bus->irq[pd->phy] = pd->phy_irq; diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 139b99b04099..c74e78dd989a 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -1059,7 +1059,7 @@ static int smsc911x_mii_init(struct platform_device *pdev, struct net_device *dev) { struct smsc911x_data *pdata = netdev_priv(dev); - int err = -ENXIO, i; + int err = -ENXIO; pdata->mii_bus = mdiobus_alloc(); if (!pdata->mii_bus) { @@ -1073,9 +1073,7 @@ static int smsc911x_mii_init(struct platform_device *pdev, pdata->mii_bus->priv = pdata; pdata->mii_bus->read = smsc911x_mii_read; pdata->mii_bus->write = smsc911x_mii_write; - pdata->mii_bus->irq = pdata->phy_irq; - for (i = 0; i < PHY_MAX_ADDR; ++i) - pdata->mii_bus->irq[i] = PHY_POLL; + memcpy(pdata->mii_bus->irq, pdata->phy_irq, sizeof(pdata->mii_bus)); pdata->mii_bus->parent = &pdev->dev; diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index fa8893a804f7..59bf4c353d50 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -78,7 +78,6 @@ struct smsc9420_pdata { struct phy_device *phy_dev; struct mii_bus *mii_bus; - int phy_irq[PHY_MAX_ADDR]; int last_duplex; int last_carrier; }; @@ -1188,7 +1187,7 @@ static int smsc9420_mii_probe(struct net_device *dev) static int smsc9420_mii_init(struct net_device *dev) { struct smsc9420_pdata *pd = netdev_priv(dev); - int err = -ENXIO, i; + int err = -ENXIO; pd->mii_bus = mdiobus_alloc(); if (!pd->mii_bus) { @@ -1201,9 +1200,6 @@ static int smsc9420_mii_init(struct net_device *dev) pd->mii_bus->priv = pd; pd->mii_bus->read = smsc9420_mii_read; pd->mii_bus->write = smsc9420_mii_write; - pd->mii_bus->irq = pd->phy_irq; - for (i = 0; i < PHY_MAX_ADDR; ++i) - pd->mii_bus->irq[i] = PHY_POLL; /* Mask all PHYs except ID 1 (internal) */ pd->mii_bus->phy_mask = ~(1 << 1); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 05ba84118f37..f0990eb9460f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -196,7 +196,6 @@ int stmmac_mdio_register(struct net_device *ndev) { int err = 0; struct mii_bus *new_bus; - int *irqlist; struct stmmac_priv *priv = netdev_priv(ndev); struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data; int addr, found; @@ -227,13 +226,8 @@ int stmmac_mdio_register(struct net_device *ndev) if (new_bus == NULL) return -ENOMEM; - if (mdio_bus_data->irqs) { - irqlist = mdio_bus_data->irqs; - } else { - for (addr = 0; addr < PHY_MAX_ADDR; addr++) - priv->mii_irq[addr] = PHY_POLL; - irqlist = priv->mii_irq; - } + if (mdio_bus_data->irqs) + memcpy(new_bus->irq, mdio_bus_data, sizeof(new_bus->irq)); #ifdef CONFIG_OF if (priv->device->of_node) @@ -247,7 +241,6 @@ int stmmac_mdio_register(struct net_device *ndev) snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x", new_bus->name, priv->plat->bus_id); new_bus->priv = ndev; - new_bus->irq = irqlist; new_bus->phy_mask = mdio_bus_data->phy_mask; new_bus->parent = priv->device; @@ -271,7 +264,8 @@ int stmmac_mdio_register(struct net_device *ndev) */ if ((mdio_bus_data->irqs == NULL) && (mdio_bus_data->probed_phy_irq > 0)) { - irqlist[addr] = mdio_bus_data->probed_phy_irq; + new_bus->irq[addr] = + mdio_bus_data->probed_phy_irq; phydev->irq = mdio_bus_data->probed_phy_irq; } diff --git a/drivers/net/ethernet/synopsys/dwc_eth_qos.c b/drivers/net/ethernet/synopsys/dwc_eth_qos.c index b25ee370254a..6dbb681912f2 100644 --- a/drivers/net/ethernet/synopsys/dwc_eth_qos.c +++ b/drivers/net/ethernet/synopsys/dwc_eth_qos.c @@ -1220,7 +1220,7 @@ static void dwceqos_enable_mmc_interrupt(struct net_local *lp) static int dwceqos_mii_init(struct net_local *lp) { - int ret = -ENXIO, i; + int ret = -ENXIO; struct resource res; struct device_node *mdionode; @@ -1241,24 +1241,14 @@ static int dwceqos_mii_init(struct net_local *lp) lp->mii_bus->priv = lp; lp->mii_bus->parent = &lp->ndev->dev; - lp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!lp->mii_bus->irq) { - ret = -ENOMEM; - goto err_out_free_mdiobus; - } - - for (i = 0; i < PHY_MAX_ADDR; i++) - lp->mii_bus->irq[i] = PHY_POLL; of_address_to_resource(lp->pdev->dev.of_node, 0, &res); snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%.8llx", (unsigned long long)res.start); if (of_mdiobus_register(lp->mii_bus, mdionode)) - goto err_out_free_mdio_irq; + goto err_out_free_mdiobus; return 0; -err_out_free_mdio_irq: - kfree(lp->mii_bus->irq); err_out_free_mdiobus: mdiobus_free(lp->mii_bus); err_out: @@ -2977,7 +2967,6 @@ static int dwceqos_remove(struct platform_device *pdev) if (lp->phy_dev) phy_disconnect(lp->phy_dev); mdiobus_unregister(lp->mii_bus); - kfree(lp->mii_bus->irq); mdiobus_free(lp->mii_bus); unregister_netdev(ndev); diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index 77d26fe286c0..0b483301767d 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -316,8 +316,6 @@ static int cpmac_mdio_reset(struct mii_bus *bus) return 0; } -static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, }; - static struct mii_bus *cpmac_mii; static void cpmac_set_multicast_list(struct net_device *dev) @@ -1226,7 +1224,6 @@ int cpmac_init(void) cpmac_mii->read = cpmac_mdio_read; cpmac_mii->write = cpmac_mdio_write; cpmac_mii->reset = cpmac_mdio_reset; - cpmac_mii->irq = mii_irqs; cpmac_mii->priv = ioremap(AR7_REGS_MDIO, 256); diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index 8fd5e0ba718c..fed5e3dfbc8f 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -682,18 +682,9 @@ static int tc_mii_init(struct net_device *dev) (lp->pci_dev->bus->number << 8) | lp->pci_dev->devfn); lp->mii_bus->priv = dev; lp->mii_bus->parent = &lp->pci_dev->dev; - lp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!lp->mii_bus->irq) { - err = -ENOMEM; - goto err_out_free_mii_bus; - } - - for (i = 0; i < PHY_MAX_ADDR; i++) - lp->mii_bus->irq[i] = PHY_POLL; - err = mdiobus_register(lp->mii_bus); if (err) - goto err_out_free_mdio_irq; + goto err_out_free_mii_bus; err = tc_mii_probe(dev); if (err) goto err_out_unregister_bus; @@ -701,8 +692,6 @@ static int tc_mii_init(struct net_device *dev) err_out_unregister_bus: mdiobus_unregister(lp->mii_bus); -err_out_free_mdio_irq: - kfree(lp->mii_bus->irq); err_out_free_mii_bus: mdiobus_free(lp->mii_bus); err_out: @@ -880,7 +869,6 @@ static void tc35815_remove_one(struct pci_dev *pdev) phy_disconnect(lp->phy_dev); mdiobus_unregister(lp->mii_bus); - kfree(lp->mii_bus->irq); mdiobus_free(lp->mii_bus); unregister_netdev(dev); free_netdev(dev); diff --git a/drivers/net/ethernet/xilinx/ll_temac.h b/drivers/net/ethernet/xilinx/ll_temac.h index 522abe2ff25a..902457e43628 100644 --- a/drivers/net/ethernet/xilinx/ll_temac.h +++ b/drivers/net/ethernet/xilinx/ll_temac.h @@ -337,7 +337,6 @@ struct temac_local { /* MDIO bus data */ struct mii_bus *mii_bus; /* MII bus reference */ - int mdio_irqs[PHY_MAX_ADDR]; /* IRQs table for MDIO bus */ /* IO registers, dma functions and IRQs */ void __iomem *regs; diff --git a/drivers/net/ethernet/xilinx/ll_temac_mdio.c b/drivers/net/ethernet/xilinx/ll_temac_mdio.c index 415de1eaf641..7714aff78b7d 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_mdio.c +++ b/drivers/net/ethernet/xilinx/ll_temac_mdio.c @@ -92,7 +92,6 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np) bus->read = temac_mdio_read; bus->write = temac_mdio_write; bus->parent = lp->dev; - bus->irq = lp->mdio_irqs; /* preallocated IRQ table */ lp->mii_bus = bus; @@ -114,7 +113,6 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np) void temac_mdio_teardown(struct temac_local *lp) { mdiobus_unregister(lp->mii_bus); - kfree(lp->mii_bus->irq); mdiobus_free(lp->mii_bus); lp->mii_bus = NULL; } diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index 7cb9abac95c8..9ead4e269409 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -385,7 +385,6 @@ struct axidma_bd { * @phy_dev: Pointer to PHY device structure attached to the axienet_local * @phy_node: Pointer to device node structure * @mii_bus: Pointer to MII bus structure - * @mdio_irqs: IRQs table for MDIO bus required in mii_bus structure * @regs: Base address for the axienet_local device address space * @dma_regs: Base address for the axidma device address space * @dma_err_tasklet: Tasklet structure to process Axi DMA errors @@ -426,7 +425,6 @@ struct axienet_local { /* MDIO bus data */ struct mii_bus *mii_bus; /* MII bus reference */ - int mdio_irqs[PHY_MAX_ADDR]; /* IRQs table for MDIO bus */ /* IO registers, dma functions and IRQs */ void __iomem *regs; diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index 507bbb0355c2..63307ea97846 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -212,7 +212,6 @@ issue: bus->read = axienet_mdio_read; bus->write = axienet_mdio_write; bus->parent = lp->dev; - bus->irq = lp->mdio_irqs; /* preallocated IRQ table */ lp->mii_bus = bus; ret = of_mdiobus_register(bus, np1); @@ -232,7 +231,6 @@ issue: void axienet_mdio_teardown(struct axienet_local *lp) { mdiobus_unregister(lp->mii_bus); - kfree(lp->mii_bus->irq); mdiobus_free(lp->mii_bus); lp->mii_bus = NULL; } diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index cf468c87ce57..d1a0167dff84 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -114,7 +114,6 @@ * @phy_dev: pointer to the PHY device * @phy_node: pointer to the PHY device node * @mii_bus: pointer to the MII bus - * @mdio_irqs: IRQs table for MDIO bus * @last_link: last link status * @has_mdio: indicates whether MDIO is included in the HW */ @@ -135,7 +134,6 @@ struct net_local { struct device_node *phy_node; struct mii_bus *mii_bus; - int mdio_irqs[PHY_MAX_ADDR]; int last_link; bool has_mdio; @@ -852,7 +850,6 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) bus->read = xemaclite_mdio_read; bus->write = xemaclite_mdio_write; bus->parent = dev; - bus->irq = lp->mdio_irqs; /* preallocated IRQ table */ lp->mii_bus = bus; @@ -1196,7 +1193,6 @@ static int xemaclite_of_remove(struct platform_device *of_dev) /* Un-register the mii_bus, if configured */ if (lp->has_mdio) { mdiobus_unregister(lp->mii_bus); - kfree(lp->mii_bus->irq); mdiobus_free(lp->mii_bus); lp->mii_bus = NULL; } diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index e23bf5b90e17..0a1cde6803b0 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -27,7 +27,6 @@ #define MII_REGS_NUM 29 struct fixed_mdio_bus { - int irqs[PHY_MAX_ADDR]; struct mii_bus *mii_bus; struct list_head phys; }; @@ -256,7 +255,7 @@ int fixed_phy_add(unsigned int irq, int phy_addr, memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); - fmb->irqs[phy_addr] = irq; + fmb->mii_bus->irq[phy_addr] = irq; fp->addr = phy_addr; fp->status = *status; @@ -395,7 +394,6 @@ static int __init fixed_mdio_bus_init(void) fmb->mii_bus->parent = &pdev->dev; fmb->mii_bus->read = &fixed_mdio_read; fmb->mii_bus->write = &fixed_mdio_write; - fmb->mii_bus->irq = fmb->irqs; ret = mdiobus_register(fmb->mii_bus); if (ret) diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index 4bde5e728fe0..8c73b2e771dd 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -200,16 +200,10 @@ static int unimac_mdio_probe(struct platform_device *pdev) bus->reset = unimac_mdio_reset; snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); - bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); - if (!bus->irq) { - ret = -ENOMEM; - goto out_mdio_free; - } - ret = of_mdiobus_register(bus, np); if (ret) { dev_err(&pdev->dev, "MDIO bus registration failed\n"); - goto out_mdio_irq; + goto out_mdio_free; } platform_set_drvdata(pdev, priv); @@ -218,8 +212,6 @@ static int unimac_mdio_probe(struct platform_device *pdev) return 0; -out_mdio_irq: - kfree(bus->irq); out_mdio_free: mdiobus_free(bus); return ret; @@ -230,7 +222,6 @@ static int unimac_mdio_remove(struct platform_device *pdev) struct unimac_mdio_priv *priv = platform_get_drvdata(pdev); mdiobus_unregister(priv->mii_bus); - kfree(priv->mii_bus->irq); mdiobus_free(priv->mii_bus); return 0; diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 95f51d7267b3..27ab63064f95 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -159,7 +159,7 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, new_bus->phy_mask = pdata->phy_mask; new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask; - new_bus->irq = pdata->irqs; + memcpy(new_bus->irq, pdata->irqs, sizeof(new_bus->irq)); new_bus->parent = dev; if (new_bus->phy_mask == ~0) diff --git a/drivers/net/phy/mdio-moxart.c b/drivers/net/phy/mdio-moxart.c index f1fc51f655d9..5bb56d126693 100644 --- a/drivers/net/phy/mdio-moxart.c +++ b/drivers/net/phy/mdio-moxart.c @@ -130,13 +130,6 @@ static int moxart_mdio_probe(struct platform_device *pdev) snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d-mii", pdev->name, pdev->id); bus->parent = &pdev->dev; - bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR, - GFP_KERNEL); - if (!bus->irq) { - ret = -ENOMEM; - goto err_out_free_mdiobus; - } - /* Setting PHY_IGNORE_INTERRUPT here even if it has no effect, * of_mdiobus_register() sets these PHY_POLL. * Ideally, the interrupt from MAC controller could be used to diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index 7f8e7662e28c..308ade0eb1b6 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -34,7 +34,6 @@ struct mdio_mux_child_bus { struct mdio_mux_parent_bus *parent; struct mdio_mux_child_bus *next; int bus_number; - int phy_irq[PHY_MAX_ADDR]; }; /* @@ -157,7 +156,7 @@ int mdio_mux_init(struct device *dev, break; } cb->mii_bus->priv = cb; - cb->mii_bus->irq = cb->phy_irq; + cb->mii_bus->name = "mdio_mux"; snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%x.%x", pb->parent_id, v); diff --git a/drivers/net/phy/mdio-octeon.c b/drivers/net/phy/mdio-octeon.c index 0d5da1312dd3..47d4f2f263d1 100644 --- a/drivers/net/phy/mdio-octeon.c +++ b/drivers/net/phy/mdio-octeon.c @@ -113,7 +113,6 @@ struct octeon_mdiobus { resource_size_t mdio_phys; resource_size_t regsize; enum octeon_mdiobus_mode mode; - int phy_irq[PHY_MAX_ADDR]; }; #ifdef CONFIG_CAVIUM_OCTEON_SOC @@ -306,7 +305,6 @@ static int octeon_mdiobus_probe(struct platform_device *pdev) oct_mdio_writeq(smi_en.u64, bus->register_base + SMI_EN); bus->mii_bus->priv = bus; - bus->mii_bus->irq = bus->phy_irq; bus->mii_bus->name = "mdio-octeon"; snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", bus->register_base); bus->mii_bus->parent = &pdev->dev; diff --git a/drivers/net/phy/mdio-sun4i.c b/drivers/net/phy/mdio-sun4i.c index 15bc7f9ea224..f70522c35163 100644 --- a/drivers/net/phy/mdio-sun4i.c +++ b/drivers/net/phy/mdio-sun4i.c @@ -96,7 +96,7 @@ static int sun4i_mdio_probe(struct platform_device *pdev) struct mii_bus *bus; struct sun4i_mdio_data *data; struct resource *res; - int ret, i; + int ret; bus = mdiobus_alloc_size(sizeof(*data)); if (!bus) @@ -108,16 +108,6 @@ static int sun4i_mdio_probe(struct platform_device *pdev) snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev)); bus->parent = &pdev->dev; - bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR, - GFP_KERNEL); - if (!bus->irq) { - ret = -ENOMEM; - goto err_out_free_mdiobus; - } - - for (i = 0; i < PHY_MAX_ADDR; i++) - bus->irq[i] = PHY_POLL; - data = bus->priv; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); data->membase = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 88cb4592b6fb..05381d0f559c 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -51,6 +51,7 @@ struct mii_bus *mdiobus_alloc_size(size_t size) struct mii_bus *bus; size_t aligned_size = ALIGN(sizeof(*bus), NETDEV_ALIGN); size_t alloc_size; + int i; /* If we alloc extra space, it should be aligned */ if (size) @@ -65,6 +66,10 @@ struct mii_bus *mdiobus_alloc_size(size_t size) bus->priv = (void *)bus + aligned_size; } + /* Initialise the interrupts to polling */ + for (i = 0; i < PHY_MAX_ADDR; i++) + bus->irq[i] = PHY_POLL; + return bus; } EXPORT_SYMBOL(mdiobus_alloc_size); diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c index 5f18fcb8dcc7..224e7d82de6d 100644 --- a/drivers/net/usb/ax88172a.c +++ b/drivers/net/usb/ax88172a.c @@ -98,7 +98,7 @@ static void ax88172a_status(struct usbnet *dev, struct urb *urb) static int ax88172a_init_mdio(struct usbnet *dev) { struct ax88172a_private *priv = dev->driver_priv; - int ret, i; + int ret; priv->mdio = mdiobus_alloc(); if (!priv->mdio) { @@ -114,25 +114,15 @@ static int ax88172a_init_mdio(struct usbnet *dev) snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d", dev->udev->bus->busnum, dev->udev->devnum); - priv->mdio->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!priv->mdio->irq) { - ret = -ENOMEM; - goto mfree; - } - for (i = 0; i < PHY_MAX_ADDR; i++) - priv->mdio->irq[i] = PHY_POLL; - ret = mdiobus_register(priv->mdio); if (ret) { netdev_err(dev->net, "Could not register MDIO bus\n"); - goto ifree; + goto mfree; } netdev_info(dev->net, "registered mdio bus %s\n", priv->mdio->id); return 0; -ifree: - kfree(priv->mdio->irq); mfree: mdiobus_free(priv->mdio); return ret; diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 226668ead0d8..1662b7b144a8 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1458,12 +1458,6 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev) snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d", dev->udev->bus->busnum, dev->udev->devnum); - dev->mdiobus->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); - if (!dev->mdiobus->irq) { - ret = -ENOMEM; - goto exit1; - } - /* handle our own interrupt */ for (i = 0; i < PHY_MAX_ADDR; i++) dev->mdiobus->irq[i] = PHY_IGNORE_INTERRUPT; @@ -1479,13 +1473,11 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev) ret = mdiobus_register(dev->mdiobus); if (ret) { netdev_err(dev->net, "can't register MDIO bus\n"); - goto exit2; + goto exit1; } netdev_dbg(dev->net, "registered mdiobus bus %s\n", dev->mdiobus->id); return 0; -exit2: - kfree(dev->mdiobus->irq); exit1: mdiobus_free(dev->mdiobus); return ret; @@ -1494,7 +1486,6 @@ exit1: static void lan78xx_remove_mdio(struct lan78xx_net *dev) { mdiobus_unregister(dev->mdiobus); - kfree(dev->mdiobus->irq); mdiobus_free(dev->mdiobus); } diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index a87a868fed64..2f88ff4654da 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -127,17 +127,12 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) struct device_node *child; const __be32 *paddr; bool scanphys = false; - int addr, rc, i; + int addr, rc; /* Mask out all PHYs from auto probing. Instead the PHYs listed in * the device tree are populated after the bus has been registered */ mdio->phy_mask = ~0; - /* Clear all the IRQ properties */ - if (mdio->irq) - for (i=0; iirq[i] = PHY_POLL; - mdio->dev.of_node = np; /* Register the MDIO bus */ diff --git a/include/linux/phy.h b/include/linux/phy.h index ecbf6382ba29..a5473c9e19de 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -189,10 +189,10 @@ struct mii_bus { u32 phy_ignore_ta_mask; /* - * Pointer to an array of interrupts, each PHY's - * interrupt at the index matching its address + * An array of interrupts, each PHY's interrupt at the index + * matching its address */ - int *irq; + int irq[PHY_MAX_ADDR]; }; #define to_mii_bus(d) container_of(d, struct mii_bus, dev) -- cgit v1.2.3-59-g8ed1b From e5a03bfd873c29eb786655ef2e95e53ed242b404 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:16 +0100 Subject: phy: Add an mdio_device structure Not all devices attached to an MDIO bus are phys. So add an mdio_device structure to represent the generic parts of an mdio device, and place this structure into the phy_device. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/agere/et131x.c | 30 ++++++------ drivers/net/ethernet/altera/altera_tse_main.c | 2 +- drivers/net/ethernet/broadcom/b44.c | 2 +- drivers/net/ethernet/broadcom/genet/bcmmii.c | 2 +- drivers/net/ethernet/broadcom/sb1250-mac.c | 4 +- drivers/net/ethernet/freescale/fman/fman_dtsec.c | 6 +-- drivers/net/ethernet/freescale/fman/fman_memac.c | 6 +-- drivers/net/ethernet/freescale/fs_enet/mac-fec.c | 2 +- drivers/net/ethernet/freescale/gianfar.c | 4 +- drivers/net/ethernet/freescale/ucc_geth.c | 4 +- drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 2 +- drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +- drivers/net/ethernet/marvell/mvneta.c | 2 +- drivers/net/ethernet/smsc/smsc911x.c | 11 +++-- drivers/net/ethernet/smsc/smsc9420.c | 3 +- drivers/net/ethernet/ti/cpsw.c | 3 +- drivers/net/ethernet/ti/davinci_mdio.c | 2 +- drivers/net/ethernet/xilinx/xilinx_emaclite.c | 2 +- drivers/net/phy/at803x.c | 2 +- drivers/net/phy/bcm87xx.c | 4 +- drivers/net/phy/dp83640.c | 22 +++++---- drivers/net/phy/dp83867.c | 4 +- drivers/net/phy/fixed_phy.c | 10 ++-- drivers/net/phy/icplus.c | 18 +++---- drivers/net/phy/marvell.c | 7 +-- drivers/net/phy/mdio_bus.c | 12 ++--- drivers/net/phy/micrel.c | 12 ++--- drivers/net/phy/microchip.c | 4 +- drivers/net/phy/phy.c | 25 +++++----- drivers/net/phy/phy_device.c | 62 ++++++++++++------------ drivers/net/phy/smsc.c | 2 +- drivers/of/of_mdio.c | 6 +-- include/linux/mdio.h | 9 ++++ include/linux/phy.h | 26 +++++----- net/dsa/dsa.c | 2 +- 35 files changed, 165 insertions(+), 151 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c index f29d45eea1d9..3f3bcbea15bd 100644 --- a/drivers/net/ethernet/agere/et131x.c +++ b/drivers/net/ethernet/agere/et131x.c @@ -1235,7 +1235,7 @@ static int et131x_mii_read(struct et131x_adapter *adapter, u8 reg, u16 *value) if (!phydev) return -EIO; - return et131x_phy_mii_read(adapter, phydev->addr, reg, value); + return et131x_phy_mii_read(adapter, phydev->mdio.addr, reg, value); } static int et131x_mii_write(struct et131x_adapter *adapter, u8 addr, u8 reg, @@ -1462,7 +1462,7 @@ static void et1310_phy_power_switch(struct et131x_adapter *adapter, bool down) data &= ~BMCR_PDOWN; if (down) data |= BMCR_PDOWN; - et131x_mii_write(adapter, phydev->addr, MII_BMCR, data); + et131x_mii_write(adapter, phydev->mdio.addr, MII_BMCR, data); } /* et131x_xcvr_init - Init the phy if we are setting it into force mode */ @@ -1490,7 +1490,7 @@ static void et131x_xcvr_init(struct et131x_adapter *adapter) else lcr2 |= (LED_VAL_LINKON << LED_TXRX_SHIFT); - et131x_mii_write(adapter, phydev->addr, PHY_LED_2, lcr2); + et131x_mii_write(adapter, phydev->mdio.addr, PHY_LED_2, lcr2); } } @@ -3192,14 +3192,14 @@ static void et131x_adjust_link(struct net_device *netdev) et131x_mii_read(adapter, PHY_MPHY_CONTROL_REG, ®ister18); - et131x_mii_write(adapter, phydev->addr, + et131x_mii_write(adapter, phydev->mdio.addr, PHY_MPHY_CONTROL_REG, register18 | 0x4); - et131x_mii_write(adapter, phydev->addr, PHY_INDEX_REG, - register18 | 0x8402); - et131x_mii_write(adapter, phydev->addr, PHY_DATA_REG, - register18 | 511); - et131x_mii_write(adapter, phydev->addr, + et131x_mii_write(adapter, phydev->mdio.addr, + PHY_INDEX_REG, register18 | 0x8402); + et131x_mii_write(adapter, phydev->mdio.addr, + PHY_DATA_REG, register18 | 511); + et131x_mii_write(adapter, phydev->mdio.addr, PHY_MPHY_CONTROL_REG, register18); } @@ -3212,8 +3212,8 @@ static void et131x_adjust_link(struct net_device *netdev) et131x_mii_read(adapter, PHY_CONFIG, ®); reg &= ~ET_PHY_CONFIG_TX_FIFO_DEPTH; reg |= ET_PHY_CONFIG_FIFO_DEPTH_32; - et131x_mii_write(adapter, phydev->addr, PHY_CONFIG, - reg); + et131x_mii_write(adapter, phydev->mdio.addr, + PHY_CONFIG, reg); } et131x_set_rx_dma_timer(adapter); @@ -3226,14 +3226,14 @@ static void et131x_adjust_link(struct net_device *netdev) et131x_mii_read(adapter, PHY_MPHY_CONTROL_REG, ®ister18); - et131x_mii_write(adapter, phydev->addr, + et131x_mii_write(adapter, phydev->mdio.addr, PHY_MPHY_CONTROL_REG, register18 | 0x4); - et131x_mii_write(adapter, phydev->addr, + et131x_mii_write(adapter, phydev->mdio.addr, PHY_INDEX_REG, register18 | 0x8402); - et131x_mii_write(adapter, phydev->addr, + et131x_mii_write(adapter, phydev->mdio.addr, PHY_DATA_REG, register18 | 511); - et131x_mii_write(adapter, phydev->addr, + et131x_mii_write(adapter, phydev->mdio.addr, PHY_MPHY_CONTROL_REG, register18); } diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index 10d51e8aefe0..17472851674f 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -844,7 +844,7 @@ static int init_phy(struct net_device *dev) } netdev_dbg(dev, "attached to PHY %d UID 0x%08x Link = %d\n", - phydev->addr, phydev->phy_id, phydev->link); + phydev->mdio.addr, phydev->phy_id, phydev->link); priv->phydev = phydev; return 0; diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 4d08bc02c7a8..843a4a5864fc 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -2305,7 +2305,7 @@ static int b44_register_phy_one(struct b44 *bp) bp->phydev = phydev; bp->old_link = 0; - bp->phy_addr = phydev->addr; + bp->phy_addr = phydev->mdio.addr; phy_attached_info(phydev); diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 4523acd8c1c2..633b59db813a 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -401,7 +401,7 @@ int bcmgenet_mii_probe(struct net_device *dev) * Ethernet MAC ISRs */ if (priv->internal_phy) - priv->mii_bus->irq[phydev->addr] = PHY_IGNORE_INTERRUPT; + priv->mii_bus->irq[phydev->mdio.addr] = PHY_IGNORE_INTERRUPT; return 0; } diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index 68a363708d27..768c18da510c 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -2366,8 +2366,8 @@ static int sbmac_mii_probe(struct net_device *dev) return -ENXIO; } - phy_dev = phy_connect(dev, dev_name(&phy_dev->dev), &sbmac_mii_poll, - PHY_INTERFACE_MODE_GMII); + phy_dev = phy_connect(dev, dev_name(&phy_dev->mdio.dev), + &sbmac_mii_poll, PHY_INTERFACE_MODE_GMII); if (IS_ERR(phy_dev)) { printk(KERN_ERR "%s: could not attach to PHY\n", dev->name); return PTR_ERR(phy_dev); diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c index 587f9b40cfaa..6b1261c0b1c2 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -1295,7 +1295,7 @@ int dtsec_init(struct fman_mac *dtsec) err = init(dtsec->regs, dtsec_drv_param, dtsec->phy_if, dtsec->max_speed, (u8 *)eth_addr, dtsec->exceptions, - dtsec->tbiphy->addr); + dtsec->tbiphy->mdio.addr); if (err) { free_init_resources(dtsec); pr_err("DTSEC version doesn't support this i/f mode\n"); @@ -1434,11 +1434,11 @@ struct fman_mac *dtsec_config(struct fman_mac_params *params) dtsec->tbiphy = of_phy_find_device(params->internal_phy_node); if (!dtsec->tbiphy) { pr_err("of_phy_find_device (TBI PHY) failed\n"); - put_device(&dtsec->tbiphy->dev); + put_device(&dtsec->tbiphy->mdio.dev); goto err_dtsec_drv_param; } - put_device(&dtsec->tbiphy->dev); + put_device(&dtsec->tbiphy->mdio.dev); /* Save FMan revision */ fman_get_revision(dtsec->fm, &dtsec->fm_rev_info); diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c index 58bb72071c14..45e98fd8b79e 100644 --- a/drivers/net/ethernet/freescale/fman/fman_memac.c +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -1054,15 +1054,15 @@ int memac_init(struct fman_mac *memac) * register address space and access each one of 4 * ports inside QSGMII. */ - phy_addr = memac->pcsphy->addr; + phy_addr = memac->pcsphy->mdio.addr; qsmgii_phy_addr = (u8)((phy_addr << 2) | i); - memac->pcsphy->addr = qsmgii_phy_addr; + memac->pcsphy->mdio.addr = qsmgii_phy_addr; if (memac->basex_if) setup_sgmii_internal_phy_base_x(memac); else setup_sgmii_internal_phy(memac, fixed_link); - memac->pcsphy->addr = phy_addr; + memac->pcsphy->mdio.addr = phy_addr; } } diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c index 016743e355de..c158d409f6af 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c @@ -254,7 +254,7 @@ static void restart(struct net_device *dev) int r; u32 addrhi, addrlo; - struct mii_bus* mii = fep->phydev->bus; + struct mii_bus *mii = fep->phydev->mdio.bus; struct fec_info* fec_inf = mii->priv; r = whack_reset(fep->fec.fecp); diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 4e394f75261e..2aa7b401cc3b 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -1834,7 +1834,7 @@ static void gfar_configure_serdes(struct net_device *dev) * several seconds for it to come back. */ if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) { - put_device(&tbiphy->dev); + put_device(&tbiphy->mdio.dev); return; } @@ -1849,7 +1849,7 @@ static void gfar_configure_serdes(struct net_device *dev) BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000); - put_device(&tbiphy->dev); + put_device(&tbiphy->mdio.dev); } static int __gfar_is_rx_idle(struct gfar_private *priv) diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 650f7888e32b..0e7f24ec3239 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -1385,7 +1385,7 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth) value &= ~0x1000; /* Turn off autonegotiation */ phy_write(tbiphy, ENET_TBI_MII_CR, value); - put_device(&tbiphy->dev); + put_device(&tbiphy->mdio.dev); } init_check_frame_length_mode(ug_info->lengthCheckRx, &ug_regs->maccfg2); @@ -1705,7 +1705,7 @@ static void uec_configure_serdes(struct net_device *dev) * several seconds for it to come back. */ if (phy_read(tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS) { - put_device(&tbiphy->dev); + put_device(&tbiphy->mdio.dev); return; } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 4eddbeb19307..3df22840fcd1 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -71,7 +71,7 @@ static void hns_get_mdix_mode(struct net_device *net_dev, struct hns_nic_priv *priv = netdev_priv(net_dev); struct phy_device *phy_dev = priv->phy; - if (!phy_dev || !phy_dev->bus) { + if (!phy_dev || !phy_dev->mdio.bus) { cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; cmd->eth_tp_mdix = ETH_TP_MDI_INVALID; return; diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 4eba2ed53052..a0c03834a2f7 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -3133,7 +3133,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev) if (!mp->phy) err = -ENODEV; else - phy_addr_set(mp, mp->phy->addr); + phy_addr_set(mp, mp->phy->mdio.addr); } else if (pd->phy_addr != MV643XX_ETH_PHY_NONE) { mp->phy = phy_scan(mp, pd->phy_addr); diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 15b1f6bbd92d..fabc8df40392 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3714,7 +3714,7 @@ static int mvneta_probe(struct platform_device *pdev) mvneta_fixed_link_update(pp, phy); - put_device(&phy->dev); + put_device(&phy->mdio.dev); } return 0; diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index c74e78dd989a..8af25563f627 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -864,8 +864,8 @@ static int smsc911x_phy_loopbacktest(struct net_device *dev) for (i = 0; i < 10; i++) { /* Set PHY to 10/FD, no ANEG, and loopback mode */ - smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR, - BMCR_LOOPBACK | BMCR_FULLDPLX); + smsc911x_mii_write(phy_dev->mdio.bus, phy_dev->mdio.addr, + MII_BMCR, BMCR_LOOPBACK | BMCR_FULLDPLX); /* Enable MAC tx/rx, FD */ spin_lock_irqsave(&pdata->mac_lock, flags); @@ -893,7 +893,7 @@ static int smsc911x_phy_loopbacktest(struct net_device *dev) spin_unlock_irqrestore(&pdata->mac_lock, flags); /* Cancel PHY loopback mode */ - smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR, 0); + smsc911x_mii_write(phy_dev->mdio.bus, phy_dev->mdio.addr, MII_BMCR, 0); smsc911x_reg_write(pdata, TX_CFG, 0); smsc911x_reg_write(pdata, RX_CFG, 0); @@ -1021,7 +1021,7 @@ static int smsc911x_mii_probe(struct net_device *dev) } SMSC_TRACE(pdata, probe, "PHY: addr %d, phy_id 0x%08X", - phydev->addr, phydev->phy_id); + phydev->mdio.addr, phydev->phy_id); ret = phy_connect_direct(dev, phydev, &smsc911x_phy_adjust_link, pdata->config.phy_interface); @@ -1988,7 +1988,8 @@ smsc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs, } for (i = 0; i <= 31; i++) - data[j++] = smsc911x_mii_read(phy_dev->bus, phy_dev->addr, i); + data[j++] = smsc911x_mii_read(phy_dev->mdio.bus, + phy_dev->mdio.addr, i); } static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata) diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index 59bf4c353d50..53355c323f54 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -315,7 +315,8 @@ smsc9420_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs, return; for (i = 0; i <= 31; i++) - data[j++] = smsc9420_mii_read(phy_dev->bus, phy_dev->addr, i); + data[j++] = smsc9420_mii_read(phy_dev->mdio.bus, + phy_dev->mdio.addr, i); } static void smsc9420_eeprom_enable_access(struct smsc9420_pdata *pd) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 49544c0fa6a7..42fdfd4d9d4f 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -2050,7 +2050,8 @@ static int cpsw_probe_dt(struct cpsw_priv *priv, if (!phy_dev) return -ENODEV; snprintf(slave_data->phy_id, sizeof(slave_data->phy_id), - PHY_ID_FMT, phy_dev->bus->id, phy_dev->addr); + PHY_ID_FMT, phy_dev->mdio.bus->id, + phy_dev->mdio.addr); } else if (parp) { u32 phyid; struct device_node *mdio_node; diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index 88e8e6055b9f..78299c1592c1 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -396,7 +396,7 @@ static int davinci_mdio_probe(struct platform_device *pdev) phy = data->bus->phy_map[addr]; if (phy) { dev_info(dev, "phy[%d]: device %s, driver %s\n", - phy->addr, phydev_name(phy), + phy->mdio.addr, phydev_name(phy), phy->drv ? phy->drv->name : "unknown"); } } diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index d1a0167dff84..e324b3092380 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -827,7 +827,7 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) dev_info(dev, "MDIO of the phy is not registered yet\n"); else - put_device(&phydev->dev); + put_device(&phydev->mdio.dev); return 0; } diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 62361f8af375..b76ac09a554f 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -190,7 +190,7 @@ static int at803x_resume(struct phy_device *phydev) static int at803x_probe(struct phy_device *phydev) { - struct device *dev = &phydev->dev; + struct device *dev = &phydev->mdio.dev; struct at803x_priv *priv; struct gpio_desc *gpiod_reset; diff --git a/drivers/net/phy/bcm87xx.c b/drivers/net/phy/bcm87xx.c index 71b491c7bf96..e536e30d1643 100644 --- a/drivers/net/phy/bcm87xx.c +++ b/drivers/net/phy/bcm87xx.c @@ -40,10 +40,10 @@ static int bcm87xx_of_reg_init(struct phy_device *phydev) const __be32 *paddr_end; int len, ret; - if (!phydev->dev.of_node) + if (!phydev->mdio.dev.of_node) return 0; - paddr = of_get_property(phydev->dev.of_node, + paddr = of_get_property(phydev->mdio.dev.of_node, "broadcom,c45-reg-init", &len); if (!paddr) return 0; diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index 47b711739ba9..39da6fc6a85e 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -220,9 +220,10 @@ static void rx_timestamp_work(struct work_struct *work); #define BROADCAST_ADDR 31 -static inline int broadcast_write(struct mii_bus *bus, u32 regnum, u16 val) +static inline int broadcast_write(struct phy_device *phydev, u32 regnum, + u16 val) { - return mdiobus_write(bus, BROADCAST_ADDR, regnum, val); + return mdiobus_write(phydev->mdio.bus, BROADCAST_ADDR, regnum, val); } /* Caller must hold extreg_lock. */ @@ -232,7 +233,7 @@ static int ext_read(struct phy_device *phydev, int page, u32 regnum) int val; if (dp83640->clock->page != page) { - broadcast_write(phydev->bus, PAGESEL, page); + broadcast_write(phydev, PAGESEL, page); dp83640->clock->page = page; } val = phy_read(phydev, regnum); @@ -247,11 +248,11 @@ static void ext_write(int broadcast, struct phy_device *phydev, struct dp83640_private *dp83640 = phydev->priv; if (dp83640->clock->page != page) { - broadcast_write(phydev->bus, PAGESEL, page); + broadcast_write(phydev, PAGESEL, page); dp83640->clock->page = page; } if (broadcast) - broadcast_write(phydev->bus, regnum, val); + broadcast_write(phydev, regnum, val); else phy_write(phydev, regnum, val); } @@ -1039,7 +1040,7 @@ static int choose_this_phy(struct dp83640_clock *clock, if (chosen_phy == -1 && !clock->chosen) return 1; - if (chosen_phy == phydev->addr) + if (chosen_phy == phydev->mdio.addr) return 1; return 0; @@ -1103,10 +1104,10 @@ static int dp83640_probe(struct phy_device *phydev) struct dp83640_private *dp83640; int err = -ENOMEM, i; - if (phydev->addr == BROADCAST_ADDR) + if (phydev->mdio.addr == BROADCAST_ADDR) return 0; - clock = dp83640_clock_get_bus(phydev->bus); + clock = dp83640_clock_get_bus(phydev->mdio.bus); if (!clock) goto no_clock; @@ -1132,7 +1133,8 @@ static int dp83640_probe(struct phy_device *phydev) if (choose_this_phy(clock, phydev)) { clock->chosen = dp83640; - clock->ptp_clock = ptp_clock_register(&clock->caps, &phydev->dev); + clock->ptp_clock = ptp_clock_register(&clock->caps, + &phydev->mdio.dev); if (IS_ERR(clock->ptp_clock)) { err = PTR_ERR(clock->ptp_clock); goto no_register; @@ -1158,7 +1160,7 @@ static void dp83640_remove(struct phy_device *phydev) struct list_head *this, *next; struct dp83640_private *tmp, *dp83640 = phydev->priv; - if (phydev->addr == BROADCAST_ADDR) + if (phydev->mdio.addr == BROADCAST_ADDR) return; enable_status_frames(phydev, false); diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index e4c0b0c0af02..74e4521bd2d3 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -103,7 +103,7 @@ static int dp83867_config_intr(struct phy_device *phydev) static int dp83867_of_init(struct phy_device *phydev) { struct dp83867_private *dp83867 = phydev->priv; - struct device *dev = &phydev->dev; + struct device *dev = &phydev->mdio.dev; struct device_node *of_node = dev->of_node; int ret; @@ -137,7 +137,7 @@ static int dp83867_config_init(struct phy_device *phydev) u16 val, delay; if (!phydev->priv) { - dp83867 = devm_kzalloc(&phydev->dev, sizeof(*dp83867), + dp83867 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83867), GFP_KERNEL); if (!dp83867) return -ENOMEM; diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index 0a1cde6803b0..ab9c473d75ea 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -197,11 +197,11 @@ int fixed_phy_set_link_update(struct phy_device *phydev, struct fixed_mdio_bus *fmb = &platform_fmb; struct fixed_phy *fp; - if (!phydev || !phydev->bus) + if (!phydev || !phydev->mdio.bus) return -EINVAL; list_for_each_entry(fp, &fmb->phys, node) { - if (fp->addr == phydev->addr) { + if (fp->addr == phydev->mdio.addr) { fp->link_update = link_update; fp->phydev = phydev; return 0; @@ -219,11 +219,11 @@ int fixed_phy_update_state(struct phy_device *phydev, struct fixed_mdio_bus *fmb = &platform_fmb; struct fixed_phy *fp; - if (!phydev || phydev->bus != fmb->mii_bus) + if (!phydev || phydev->mdio.bus != fmb->mii_bus) return -EINVAL; list_for_each_entry(fp, &fmb->phys, node) { - if (fp->addr == phydev->addr) { + if (fp->addr == phydev->mdio.addr) { #define _UPD(x) if (changed->x) \ fp->status.x = status->x _UPD(link); @@ -344,7 +344,7 @@ struct phy_device *fixed_phy_register(unsigned int irq, } of_node_get(np); - phy->dev.of_node = np; + phy->mdio.dev.of_node = np; phy->is_pseudo_fixed_link = true; switch (status->speed) { diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c index 0dbc445a5fa0..c12170d07b62 100644 --- a/drivers/net/phy/icplus.c +++ b/drivers/net/phy/icplus.c @@ -53,43 +53,43 @@ static int ip175c_config_init(struct phy_device *phydev) if (full_reset_performed == 0) { /* master reset */ - err = mdiobus_write(phydev->bus, 30, 0, 0x175c); + err = mdiobus_write(phydev->mdio.bus, 30, 0, 0x175c); if (err < 0) return err; /* ensure no bus delays overlap reset period */ - err = mdiobus_read(phydev->bus, 30, 0); + err = mdiobus_read(phydev->mdio.bus, 30, 0); /* data sheet specifies reset period is 2 msec */ mdelay(2); /* enable IP175C mode */ - err = mdiobus_write(phydev->bus, 29, 31, 0x175c); + err = mdiobus_write(phydev->mdio.bus, 29, 31, 0x175c); if (err < 0) return err; /* Set MII0 speed and duplex (in PHY mode) */ - err = mdiobus_write(phydev->bus, 29, 22, 0x420); + err = mdiobus_write(phydev->mdio.bus, 29, 22, 0x420); if (err < 0) return err; /* reset switch ports */ for (i = 0; i < 5; i++) { - err = mdiobus_write(phydev->bus, i, + err = mdiobus_write(phydev->mdio.bus, i, MII_BMCR, BMCR_RESET); if (err < 0) return err; } for (i = 0; i < 5; i++) - err = mdiobus_read(phydev->bus, i, MII_BMCR); + err = mdiobus_read(phydev->mdio.bus, i, MII_BMCR); mdelay(2); full_reset_performed = 1; } - if (phydev->addr != 4) { + if (phydev->mdio.addr != 4) { phydev->state = PHY_RUNNING; phydev->speed = SPEED_100; phydev->duplex = DUPLEX_FULL; @@ -184,7 +184,7 @@ static int ip101a_g_config_init(struct phy_device *phydev) static int ip175c_read_status(struct phy_device *phydev) { - if (phydev->addr == 4) /* WAN port */ + if (phydev->mdio.addr == 4) /* WAN port */ genphy_read_status(phydev); else /* Don't need to read status for switch ports */ @@ -195,7 +195,7 @@ static int ip175c_read_status(struct phy_device *phydev) static int ip175c_config_aneg(struct phy_device *phydev) { - if (phydev->addr == 4) /* WAN port */ + if (phydev->mdio.addr == 4) /* WAN port */ genphy_config_aneg(phydev); return 0; diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 50b5eac75854..f96c93c9819a 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -300,10 +300,11 @@ static int marvell_of_reg_init(struct phy_device *phydev) const __be32 *paddr; int len, i, saved_page, current_page, page_changed, ret; - if (!phydev->dev.of_node) + if (!phydev->mdio.dev.of_node) return 0; - paddr = of_get_property(phydev->dev.of_node, "marvell,reg-init", &len); + paddr = of_get_property(phydev->mdio.dev.of_node, + "marvell,reg-init", &len); if (!paddr || len < (4 * sizeof(*paddr))) return 0; @@ -1060,7 +1061,7 @@ static int marvell_probe(struct phy_device *phydev) { struct marvell_priv *priv; - priv = devm_kzalloc(&phydev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 05381d0f559c..e5b1ccde835b 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -200,16 +200,16 @@ EXPORT_SYMBOL(of_mdio_find_bus); * the phy. This allows auto-probed pyh devices to be supplied with information * passed in via DT. */ -static void of_mdiobus_link_phydev(struct mii_bus *mdio, +static void of_mdiobus_link_phydev(struct mii_bus *bus, struct phy_device *phydev) { - struct device *dev = &phydev->dev; + struct device *dev = &phydev->mdio.dev; struct device_node *child; - if (dev->of_node || !mdio->dev.of_node) + if (dev->of_node || !bus->dev.of_node) return; - for_each_available_child_of_node(mdio->dev.of_node, child) { + for_each_available_child_of_node(bus->dev.of_node, child) { int addr; int ret; @@ -227,7 +227,7 @@ static void of_mdiobus_link_phydev(struct mii_bus *mdio, continue; } - if (addr == phydev->addr) { + if (addr == phydev->mdio.addr) { dev->of_node = child; return; } @@ -522,7 +522,7 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv) static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) { - struct device_driver *drv = phydev->dev.driver; + struct device_driver *drv = phydev->mdio.dev.driver; struct phy_driver *phydrv = to_phy_driver(drv); struct net_device *netdev = phydev->attached_dev; diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index bf72365e90bc..b51505be1fa9 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -350,7 +350,7 @@ static int ksz9021_load_values_from_of(struct phy_device *phydev, static int ksz9021_config_init(struct phy_device *phydev) { - const struct device *dev = &phydev->dev; + const struct device *dev = &phydev->mdio.dev; const struct device_node *of_node = dev->of_node; const struct device *dev_walker; @@ -358,7 +358,7 @@ static int ksz9021_config_init(struct phy_device *phydev) * properties in the MAC node. Walk up the tree of devices to * find a device with an OF node. */ - dev_walker = &phydev->dev; + dev_walker = &phydev->mdio.dev; do { of_node = dev_walker->of_node; dev_walker = dev_walker->parent; @@ -471,7 +471,7 @@ static int ksz9031_center_flp_timing(struct phy_device *phydev) static int ksz9031_config_init(struct phy_device *phydev) { - const struct device *dev = &phydev->dev; + const struct device *dev = &phydev->mdio.dev; const struct device_node *of_node = dev->of_node; static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"}; static const char *rx_data_skews[4] = { @@ -630,12 +630,12 @@ static void kszphy_get_stats(struct phy_device *phydev, static int kszphy_probe(struct phy_device *phydev) { const struct kszphy_type *type = phydev->drv->driver_data; - const struct device_node *np = phydev->dev.of_node; + const struct device_node *np = phydev->mdio.dev.of_node; struct kszphy_priv *priv; struct clk *clk; int ret; - priv = devm_kzalloc(&phydev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -658,7 +658,7 @@ static int kszphy_probe(struct phy_device *phydev) priv->led_mode = -1; } - clk = devm_clk_get(&phydev->dev, "rmii-ref"); + clk = devm_clk_get(&phydev->mdio.dev, "rmii-ref"); /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */ if (!IS_ERR_OR_NULL(clk)) { unsigned long rate = clk_get_rate(clk); diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c index 99df5bc47424..5e34b49be0b3 100644 --- a/drivers/net/phy/microchip.c +++ b/drivers/net/phy/microchip.c @@ -68,7 +68,7 @@ int lan88xx_suspend(struct phy_device *phydev) static int lan88xx_probe(struct phy_device *phydev) { - struct device *dev = &phydev->dev; + struct device *dev = &phydev->mdio.dev; struct lan88xx_priv *priv; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -89,7 +89,7 @@ static int lan88xx_probe(struct phy_device *phydev) static void lan88xx_remove(struct phy_device *phydev) { - struct device *dev = &phydev->dev; + struct device *dev = &phydev->mdio.dev; struct lan88xx_priv *priv = phydev->priv; if (priv) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 56c8dd8c0c85..8763bb20988a 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -319,7 +319,7 @@ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd) { u32 speed = ethtool_cmd_speed(cmd); - if (cmd->phy_address != phydev->addr) + if (cmd->phy_address != phydev->mdio.addr) return -EINVAL; /* We make sure that we don't pass unsupported values in to the PHY */ @@ -375,7 +375,7 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd) cmd->port = PORT_BNC; else cmd->port = PORT_MII; - cmd->phy_address = phydev->addr; + cmd->phy_address = phydev->mdio.addr; cmd->transceiver = phy_is_internal(phydev) ? XCVR_INTERNAL : XCVR_EXTERNAL; cmd->autoneg = phydev->autoneg; @@ -403,16 +403,17 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) switch (cmd) { case SIOCGMIIPHY: - mii_data->phy_id = phydev->addr; + mii_data->phy_id = phydev->mdio.addr; /* fall through */ case SIOCGMIIREG: - mii_data->val_out = mdiobus_read(phydev->bus, mii_data->phy_id, + mii_data->val_out = mdiobus_read(phydev->mdio.bus, + mii_data->phy_id, mii_data->reg_num); return 0; case SIOCSMIIREG: - if (mii_data->phy_id == phydev->addr) { + if (mii_data->phy_id == phydev->mdio.addr) { switch (mii_data->reg_num) { case MII_BMCR: if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) { @@ -445,10 +446,10 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) } } - mdiobus_write(phydev->bus, mii_data->phy_id, + mdiobus_write(phydev->mdio.bus, mii_data->phy_id, mii_data->reg_num, val); - if (mii_data->phy_id == phydev->addr && + if (mii_data->phy_id == phydev->mdio.addr && mii_data->reg_num == MII_BMCR && val & BMCR_RESET) return phy_init_hw(phydev); @@ -643,7 +644,7 @@ int phy_start_interrupts(struct phy_device *phydev) if (request_irq(phydev->irq, phy_interrupt, 0, "phy_interrupt", phydev) < 0) { pr_warn("%s: Can't get IRQ %d (PHY)\n", - phydev->bus->name, phydev->irq); + phydev->mdio.bus->name, phydev->irq); phydev->irq = PHY_POLL; return 0; } @@ -1041,11 +1042,11 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) { struct phy_driver *phydrv = phydev->drv; - int addr = phydev->addr; + int addr = phydev->mdio.addr; int value = -1; if (!phydrv->read_mmd_indirect) { - struct mii_bus *bus = phydev->bus; + struct mii_bus *bus = phydev->mdio.bus; mutex_lock(&bus->mdio_lock); mmd_phy_indirect(bus, prtad, devad, addr); @@ -1079,10 +1080,10 @@ void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, int devad, u32 data) { struct phy_driver *phydrv = phydev->drv; - int addr = phydev->addr; + int addr = phydev->mdio.addr; if (!phydrv->write_mmd_indirect) { - struct mii_bus *bus = phydev->bus; + struct mii_bus *bus = phydev->mdio.bus; mutex_lock(&bus->mdio_lock); mmd_phy_indirect(bus, prtad, devad, addr); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 68fe5738daef..01e5d52dc37c 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -43,7 +43,7 @@ MODULE_LICENSE("GPL"); void phy_device_free(struct phy_device *phydev) { - put_device(&phydev->dev); + put_device(&phydev->mdio.dev); } EXPORT_SYMBOL(phy_device_free); @@ -65,7 +65,7 @@ static DEFINE_MUTEX(phy_fixup_lock); /** * phy_register_fixup - creates a new phy_fixup and adds it to the list - * @bus_id: A string which matches phydev->dev.bus_id (or PHY_ANY_ID) + * @bus_id: A string which matches phydev->mdio.dev.bus_id (or PHY_ANY_ID) * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY) * It can also be PHY_ANY_UID * @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before @@ -153,13 +153,19 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, struct phy_c45_device_ids *c45_ids) { struct phy_device *dev; + struct mdio_device *mdiodev; /* We allocate the device, and initialize the default values */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return ERR_PTR(-ENOMEM); - dev->dev.release = phy_device_release; + mdiodev = &dev->mdio; + mdiodev->dev.release = phy_device_release; + mdiodev->dev.parent = &bus->dev; + mdiodev->dev.bus = &mdio_bus_type; + mdiodev->bus = bus; + mdiodev->addr = addr; dev->speed = 0; dev->duplex = -1; @@ -171,15 +177,11 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, dev->autoneg = AUTONEG_ENABLE; dev->is_c45 = is_c45; - dev->addr = addr; dev->phy_id = phy_id; if (c45_ids) dev->c45_ids = *c45_ids; - dev->bus = bus; - dev->dev.parent = &bus->dev; - dev->dev.bus = &mdio_bus_type; dev->irq = bus->irq ? bus->irq[addr] : PHY_POLL; - dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr); + dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr); dev->state = PHY_DOWN; @@ -199,7 +201,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, */ request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id)); - device_initialize(&dev->dev); + device_initialize(&mdiodev->dev); return dev; } @@ -382,27 +384,27 @@ int phy_device_register(struct phy_device *phydev) int err; /* Don't register a phy if one is already registered at this address */ - if (phydev->bus->phy_map[phydev->addr]) + if (phydev->mdio.bus->phy_map[phydev->mdio.addr]) return -EINVAL; - phydev->bus->phy_map[phydev->addr] = phydev; + phydev->mdio.bus->phy_map[phydev->mdio.addr] = phydev; /* Run all of the fixups for this PHY */ err = phy_scan_fixups(phydev); if (err) { - pr_err("PHY %d failed to initialize\n", phydev->addr); + pr_err("PHY %d failed to initialize\n", phydev->mdio.addr); goto out; } - err = device_add(&phydev->dev); + err = device_add(&phydev->mdio.dev); if (err) { - pr_err("PHY %d failed to add\n", phydev->addr); + pr_err("PHY %d failed to add\n", phydev->mdio.addr); goto out; } return 0; out: - phydev->bus->phy_map[phydev->addr] = NULL; + phydev->mdio.bus->phy_map[phydev->mdio.addr] = NULL; return err; } EXPORT_SYMBOL(phy_device_register); @@ -417,10 +419,10 @@ EXPORT_SYMBOL(phy_device_register); */ void phy_device_remove(struct phy_device *phydev) { - struct mii_bus *bus = phydev->bus; - int addr = phydev->addr; + struct mii_bus *bus = phydev->mdio.bus; + int addr = phydev->mdio.addr; - device_del(&phydev->dev); + device_del(&phydev->mdio.dev); bus->phy_map[addr] = NULL; } EXPORT_SYMBOL(phy_device_remove); @@ -617,13 +619,13 @@ EXPORT_SYMBOL(phy_attached_info); void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) { if (!fmt) { - dev_info(&phydev->dev, ATTACHED_FMT "\n", + dev_info(&phydev->mdio.dev, ATTACHED_FMT "\n", phydev->drv->name, phydev_name(phydev), phydev->irq); } else { va_list ap; - dev_info(&phydev->dev, ATTACHED_FMT, + dev_info(&phydev->mdio.dev, ATTACHED_FMT, phydev->drv->name, phydev_name(phydev), phydev->irq); @@ -652,8 +654,8 @@ EXPORT_SYMBOL(phy_attached_print); int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, u32 flags, phy_interface_t interface) { - struct mii_bus *bus = phydev->bus; - struct device *d = &phydev->dev; + struct mii_bus *bus = phydev->mdio.bus; + struct device *d = &phydev->mdio.dev; int err; if (!try_module_get(bus->owner)) { @@ -771,8 +773,8 @@ void phy_detach(struct phy_device *phydev) * real driver could be loaded */ for (i = 0; i < ARRAY_SIZE(genphy_driver); i++) { - if (phydev->dev.driver == &genphy_driver[i].driver) { - device_release_driver(&phydev->dev); + if (phydev->mdio.dev.driver == &genphy_driver[i].driver) { + device_release_driver(&phydev->mdio.dev); break; } } @@ -781,16 +783,16 @@ void phy_detach(struct phy_device *phydev) * The phydev might go away on the put_device() below, so avoid * a use-after-free bug by reading the underlying bus first. */ - bus = phydev->bus; + bus = phydev->mdio.bus; - put_device(&phydev->dev); + put_device(&phydev->mdio.dev); module_put(bus->owner); } EXPORT_SYMBOL(phy_detach); int phy_suspend(struct phy_device *phydev) { - struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); + struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; int ret = 0; @@ -813,7 +815,7 @@ EXPORT_SYMBOL(phy_suspend); int phy_resume(struct phy_device *phydev) { - struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); + struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver); int ret = 0; if (phydrv->resume) @@ -1330,7 +1332,7 @@ EXPORT_SYMBOL(phy_set_max_speed); static void of_set_phy_supported(struct phy_device *phydev) { - struct device_node *node = phydev->dev.of_node; + struct device_node *node = phydev->mdio.dev.of_node; u32 max_speed; if (!IS_ENABLED(CONFIG_OF_MDIO)) @@ -1354,7 +1356,7 @@ static void of_set_phy_supported(struct phy_device *phydev) static int phy_probe(struct device *dev) { struct phy_device *phydev = to_phy_device(dev); - struct device_driver *drv = phydev->dev.driver; + struct device_driver *drv = phydev->mdio.dev.driver; struct phy_driver *phydrv = to_phy_driver(drv); int err = 0; diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index dc2da8770918..18c981b95910 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -44,7 +44,7 @@ static int smsc_phy_ack_interrupt(struct phy_device *phydev) static int smsc_phy_config_init(struct phy_device *phydev) { int __maybe_unused len; - struct device *dev __maybe_unused = &phydev->dev; + struct device *dev __maybe_unused = &phydev->mdio.dev; struct device_node *of_node __maybe_unused = dev->of_node; int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); int enable_energy = 1; diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 2f88ff4654da..bc9d76329435 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -75,7 +75,7 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi /* Associate the OF node with the device structure so it * can be looked up later */ of_node_get(child); - phy->dev.of_node = child; + phy->mdio.dev.of_node = child; /* All data is now stored in the phy struct; * register it */ @@ -233,7 +233,7 @@ struct phy_device *of_phy_connect(struct net_device *dev, ret = phy_connect_direct(dev, phy, hndlr, iface); /* refcount is held by phy_connect_direct() on success */ - put_device(&phy->dev); + put_device(&phy->mdio.dev); return ret ? NULL : phy; } @@ -263,7 +263,7 @@ struct phy_device *of_phy_attach(struct net_device *dev, ret = phy_attach_direct(dev, phy, flags, iface); /* refcount is held by phy_attach_direct() on success */ - put_device(&phy->dev); + put_device(&phy->mdio.dev); return ret ? NULL : phy; } diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 0d073c23c10d..94f9f1491cde 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -13,6 +13,15 @@ struct mii_bus; +struct mdio_device { + struct device dev; + + struct mii_bus *bus; + /* Bus address of the MDIO device (0-31) */ + int addr; +}; +#define to_mdio_device(d) container_of(d, struct mdio_device, dev) + static inline bool mdio_phy_id_is_c45(int phy_id) { return (phy_id & MDIO_PHY_ID_C45) && !(phy_id & ~MDIO_PHY_ID_C45_MASK); diff --git a/include/linux/phy.h b/include/linux/phy.h index a5473c9e19de..239a0c2bc49d 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -358,14 +358,12 @@ struct phy_c45_device_ids { * handling, as well as handling shifts in PHY hardware state */ struct phy_device { + struct mdio_device mdio; + /* Information about the PHY type */ /* And management functions */ struct phy_driver *drv; - struct mii_bus *bus; - - struct device dev; - u32 phy_id; struct phy_c45_device_ids c45_ids; @@ -381,9 +379,6 @@ struct phy_device { phy_interface_t interface; - /* Bus address of the PHY (0-31) */ - int addr; - /* * forced speed & duplex (no autoneg) * partner speed & duplex & pause (autoneg) @@ -432,7 +427,8 @@ struct phy_device { void (*adjust_link)(struct net_device *dev); }; -#define to_phy_device(d) container_of(d, struct phy_device, dev) +#define to_phy_device(d) container_of(to_mdio_device(d), \ + struct phy_device, mdio) /* struct phy_driver: Driver structure for a particular PHY type * @@ -622,7 +618,7 @@ static inline int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) if (!phydev->is_c45) return -EOPNOTSUPP; - return mdiobus_read(phydev->bus, phydev->addr, + return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff)); } @@ -648,7 +644,7 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad); */ static inline int phy_read(struct phy_device *phydev, u32 regnum) { - return mdiobus_read(phydev->bus, phydev->addr, regnum); + return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, regnum); } /** @@ -663,7 +659,7 @@ static inline int phy_read(struct phy_device *phydev, u32 regnum) */ static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val) { - return mdiobus_write(phydev->bus, phydev->addr, regnum, val); + return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val); } /** @@ -726,7 +722,7 @@ static inline int phy_write_mmd(struct phy_device *phydev, int devad, regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff); - return mdiobus_write(phydev->bus, phydev->addr, regnum, val); + return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val); } /** @@ -776,14 +772,14 @@ static inline int phy_read_status(struct phy_device *phydev) } #define phydev_err(_phydev, format, args...) \ - dev_err(&_phydev->dev, format, ##args) + dev_err(&_phydev->mdio.dev, format, ##args) #define phydev_dbg(_phydev, format, args...) \ - dev_dbg(&_phydev->dev, format, ##args) + dev_dbg(&_phydev->mdio.dev, format, ##args); static inline const char *phydev_name(const struct phy_device *phydev) { - return dev_name(&phydev->dev); + return dev_name(&phydev->mdio.dev); } void phy_attached_print(struct phy_device *phydev, const char *fmt, ...) diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 208d1b257194..fa4daba8db55 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -439,7 +439,7 @@ static void dsa_switch_destroy(struct dsa_switch *ds) if (of_phy_is_fixed_link(port_dn)) { phydev = of_phy_find_device(port_dn); if (phydev) { - int addr = phydev->addr; + int addr = phydev->mdio.addr; phy_device_free(phydev); of_node_put(port_dn); -- cgit v1.2.3-59-g8ed1b From 801a8ef54e8b21eb6699aaa88681259dafb1d1b5 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:17 +0100 Subject: of: phy: Only register a phy device for phys We will soon support devices other than phys on the mdio bus. Look at a child's compatibility string to determine if it is a phy, before registering a phy device. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/of/of_mdio.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index bc9d76329435..c0292051392e 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -114,6 +114,35 @@ int of_mdio_parse_addr(struct device *dev, const struct device_node *np) } EXPORT_SYMBOL(of_mdio_parse_addr); +/* + * Return true if the child node is for a phy. It must either: + * o Compatible string of "ethernet-phy-idX.X" + * o Compatible string of "ethernet-phy-ieee802.3-c45" + * o Compatible string of "ethernet-phy-ieee802.3-c22" + * o No compatibility string + * + * A device which is not a phy is expected to have a compatible string + * indicating what sort of device it is. + */ +static bool of_mdiobus_child_is_phy(struct device_node *child) +{ + u32 phy_id; + + if (of_get_phy_id(child, &phy_id) != -EINVAL) + return true; + + if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c45")) + return true; + + if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c22")) + return true; + + if (!of_find_property(child, "compatible", NULL)) + return true; + + return false; +} + /** * of_mdiobus_register - Register mii_bus and create PHYs from the device tree * @mdio: pointer to mii_bus structure @@ -140,7 +169,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) if (rc) return rc; - /* Loop over the child nodes and register a phy_device for each one */ + /* Loop over the child nodes and register a phy_device for each phy */ for_each_available_child_of_node(np, child) { addr = of_mdio_parse_addr(&mdio->dev, child); if (addr < 0) { @@ -148,9 +177,8 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) continue; } - rc = of_mdiobus_register_phy(mdio, child, addr); - if (rc) - continue; + if (of_mdiobus_child_is_phy(child)) + of_mdiobus_register_phy(mdio, child, addr); } if (!scanphys) @@ -172,9 +200,8 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) dev_info(&mdio->dev, "scan phy %s at address %i\n", child->name, addr); - rc = of_mdiobus_register_phy(mdio, child, addr); - if (rc) - continue; + if (of_mdiobus_child_is_phy(child)) + of_mdiobus_register_phy(mdio, child, addr); } } -- cgit v1.2.3-59-g8ed1b From 7f854420fbfe9d49afe2ffb1df052cfe8e215541 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:18 +0100 Subject: phy: Add API for {un}registering an mdio device to a bus. Rather than have drivers directly manipulate the mii_bus structure, provide and API for registering and unregistering devices on an MDIO bus, and performing lookups. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/au1000_eth.c | 9 +++-- drivers/net/ethernet/broadcom/b44.c | 2 +- drivers/net/ethernet/broadcom/genet/bcmmii.c | 2 +- drivers/net/ethernet/broadcom/tg3.c | 30 +++++++-------- drivers/net/ethernet/ethoc.c | 4 +- drivers/net/ethernet/faraday/ftgmac100.c | 2 +- drivers/net/ethernet/freescale/fec_main.c | 7 +--- drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c | 2 +- drivers/net/ethernet/smsc/smsc9420.c | 3 +- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 2 +- drivers/net/ethernet/ti/davinci_mdio.c | 2 +- drivers/net/phy/mdio_bus.c | 46 ++++++++++++++++++++++- drivers/net/phy/phy_device.c | 21 +++++------ drivers/of/of_mdio.c | 2 +- include/linux/mdio.h | 8 ++++ include/linux/phy.h | 2 +- net/dsa/slave.c | 3 +- 17 files changed, 98 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index 982b581d3484..c8640418fc37 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -502,7 +502,7 @@ static int au1000_mii_probe(struct net_device *dev) BUG_ON(aup->mac_id < 0 || aup->mac_id > 1); if (aup->phy_addr) - phydev = aup->mii_bus->phy_map[aup->phy_addr]; + phydev = mdiobus_get_phy(aup->mii_bus, aup->phy_addr); else netdev_info(dev, "using PHY-less setup\n"); return 0; @@ -512,8 +512,8 @@ static int au1000_mii_probe(struct net_device *dev) * on the current MAC's MII bus */ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) - if (aup->mii_bus->phy_map[phy_addr]) { - phydev = aup->mii_bus->phy_map[phy_addr]; + if (mdiobus_get_phy(aup->mii_bus, aup->phy_addr)) { + phydev = mdiobus_get_phy(aup->mii_bus, aup->phy_addr); if (!aup->phy_search_highest_addr) /* break out with first one found */ break; @@ -531,7 +531,8 @@ static int au1000_mii_probe(struct net_device *dev) */ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { struct phy_device *const tmp_phydev = - aup->mii_bus->phy_map[phy_addr]; + mdiobus_get_phy(aup->mii_bus, + phy_addr); if (aup->mac_id == 1) break; diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 843a4a5864fc..74f0a37c4eb6 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -2272,7 +2272,7 @@ static int b44_register_phy_one(struct b44 *bp) goto err_out_mdiobus; } - if (!bp->mii_bus->phy_map[bp->phy_addr] && + if (!mdiobus_is_registered_device(bp->mii_bus, bp->phy_addr) && (sprom->boardflags_lo & (B44_BOARDFLAG_ROBO | B44_BOARDFLAG_ADM))) { dev_info(sdev->dev, diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 633b59db813a..0d775964b060 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -573,7 +573,7 @@ static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv) } if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR) - phydev = mdio->phy_map[pd->phy_address]; + phydev = mdiobus_get_phy(mdio, pd->phy_address); else phydev = phy_find_first(mdio); diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 04e7d0d0e5b1..9293675df7ba 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -1406,7 +1406,7 @@ static void tg3_mdio_config_5785(struct tg3 *tp) u32 val; struct phy_device *phydev; - phydev = tp->mdio_bus->phy_map[tp->phy_addr]; + phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr); switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) { case PHY_ID_BCM50610: case PHY_ID_BCM50610M: @@ -1554,7 +1554,7 @@ static int tg3_mdio_init(struct tg3 *tp) return i; } - phydev = tp->mdio_bus->phy_map[tp->phy_addr]; + phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr); if (!phydev || !phydev->drv) { dev_warn(&tp->pdev->dev, "No PHY devices\n"); @@ -1964,7 +1964,7 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv) u32 old_tx_mode = tp->tx_mode; if (tg3_flag(tp, USE_PHYLIB)) - autoneg = tp->mdio_bus->phy_map[tp->phy_addr]->autoneg; + autoneg = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr)->autoneg; else autoneg = tp->link_config.autoneg; @@ -2000,7 +2000,7 @@ static void tg3_adjust_link(struct net_device *dev) u8 oldflowctrl, linkmesg = 0; u32 mac_mode, lcl_adv, rmt_adv; struct tg3 *tp = netdev_priv(dev); - struct phy_device *phydev = tp->mdio_bus->phy_map[tp->phy_addr]; + struct phy_device *phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr); spin_lock_bh(&tp->lock); @@ -2089,7 +2089,7 @@ static int tg3_phy_init(struct tg3 *tp) /* Bring the PHY back to a known state. */ tg3_bmcr_reset(tp); - phydev = tp->mdio_bus->phy_map[tp->phy_addr]; + phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr); /* Attach the MAC to the PHY. */ phydev = phy_connect(tp->dev, phydev_name(phydev), @@ -2116,7 +2116,7 @@ static int tg3_phy_init(struct tg3 *tp) SUPPORTED_Asym_Pause); break; default: - phy_disconnect(tp->mdio_bus->phy_map[tp->phy_addr]); + phy_disconnect(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr)); return -EINVAL; } @@ -2136,7 +2136,7 @@ static void tg3_phy_start(struct tg3 *tp) if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return; - phydev = tp->mdio_bus->phy_map[tp->phy_addr]; + phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr); if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) { tp->phy_flags &= ~TG3_PHYFLG_IS_LOW_POWER; @@ -2156,13 +2156,13 @@ static void tg3_phy_stop(struct tg3 *tp) if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return; - phy_stop(tp->mdio_bus->phy_map[tp->phy_addr]); + phy_stop(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr)); } static void tg3_phy_fini(struct tg3 *tp) { if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) { - phy_disconnect(tp->mdio_bus->phy_map[tp->phy_addr]); + phy_disconnect(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr)); tp->phy_flags &= ~TG3_PHYFLG_IS_CONNECTED; } } @@ -4046,7 +4046,7 @@ static int tg3_power_down_prepare(struct tg3 *tp) struct phy_device *phydev; u32 phyid, advertising; - phydev = tp->mdio_bus->phy_map[tp->phy_addr]; + phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr); tp->phy_flags |= TG3_PHYFLG_IS_LOW_POWER; @@ -12074,7 +12074,7 @@ static int tg3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) struct phy_device *phydev; if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return -EAGAIN; - phydev = tp->mdio_bus->phy_map[tp->phy_addr]; + phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr); return phy_ethtool_gset(phydev, cmd); } @@ -12141,7 +12141,7 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) struct phy_device *phydev; if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return -EAGAIN; - phydev = tp->mdio_bus->phy_map[tp->phy_addr]; + phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr); return phy_ethtool_sset(phydev, cmd); } @@ -12296,7 +12296,7 @@ static int tg3_nway_reset(struct net_device *dev) if (tg3_flag(tp, USE_PHYLIB)) { if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return -EAGAIN; - r = phy_start_aneg(tp->mdio_bus->phy_map[tp->phy_addr]); + r = phy_start_aneg(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr)); } else { u32 bmcr; @@ -12414,7 +12414,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam u32 newadv; struct phy_device *phydev; - phydev = tp->mdio_bus->phy_map[tp->phy_addr]; + phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr); if (!(phydev->supported & SUPPORTED_Pause) || (!(phydev->supported & SUPPORTED_Asym_Pause) && @@ -13924,7 +13924,7 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) struct phy_device *phydev; if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return -EAGAIN; - phydev = tp->mdio_bus->phy_map[tp->phy_addr]; + phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr); return phy_mii_ioctl(phydev, ifr, cmd); } diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index c028b299ab3f..62fa136554ac 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -678,7 +678,7 @@ static int ethoc_mdio_probe(struct net_device *dev) int err; if (priv->phy_id != -1) - phy = priv->mdio->phy_map[priv->phy_id]; + phy = mdiobus_get_phy(priv->mdio, priv->phy_id); else phy = phy_find_first(priv->mdio); @@ -766,7 +766,7 @@ static int ethoc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (mdio->phy_id >= PHY_MAX_ADDR) return -ERANGE; - phy = priv->mdio->phy_map[mdio->phy_id]; + phy = mdiobus_get_phy(priv->mdio, mdio->phy_id); if (!phy) return -ENODEV; } else { diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 8f3f2cf0dcbf..bb116ad646f6 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -839,7 +839,7 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv) /* search for connect PHY device */ for (i = 0; i < PHY_MAX_ADDR; i++) { - struct phy_device *tmp = priv->mii_bus->phy_map[i]; + struct phy_device *tmp = mdiobus_get_phy(priv->mii_bus, i); if (tmp) { phydev = tmp; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index da255fb4f1d5..502da6f48f95 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -1926,11 +1927,7 @@ static int fec_enet_mii_probe(struct net_device *ndev) } else { /* check for attached phy */ for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { - if ((fep->mii_bus->phy_mask & (1 << phy_id))) - continue; - if (fep->mii_bus->phy_map[phy_id] == NULL) - continue; - if (fep->mii_bus->phy_map[phy_id]->phy_id == 0) + if (!mdiobus_is_registered_device(fep->mii_bus, phy_id)) continue; if (dev_id--) continue; diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c index 5b13b8c11bef..467ff7033606 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c @@ -180,7 +180,7 @@ int sxgbe_mdio_register(struct net_device *ndev) } for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { - struct phy_device *phy = mdio_bus->phy_map[phy_addr]; + struct phy_device *phy = mdiobus_get_phy(mdio_bus, phy_addr); if (phy) { char irq_num[4]; diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index 53355c323f54..8594b9e8b28b 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -1158,7 +1158,8 @@ static int smsc9420_mii_probe(struct net_device *dev) BUG_ON(pd->phy_dev); /* Device only supports internal PHY at address 1 */ - if (!pd->mii_bus->phy_map[1]) { + phydev = mdiobus_get_phy(pd->mii_bus, 1); + if (!phydev) { netdev_err(dev, "no PHY found at address 1\n"); return -ENODEV; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index f0990eb9460f..bff28595b427 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -252,7 +252,7 @@ int stmmac_mdio_register(struct net_device *ndev) found = 0; for (addr = 0; addr < PHY_MAX_ADDR; addr++) { - struct phy_device *phydev = new_bus->phy_map[addr]; + struct phy_device *phydev = mdiobus_get_phy(new_bus, addr); if (phydev) { int act = 0; char irq_num[4]; diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index 78299c1592c1..4e7c9b9b042a 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -393,7 +393,7 @@ static int davinci_mdio_probe(struct platform_device *pdev) /* scan and dump the bus */ for (addr = 0; addr < PHY_MAX_ADDR; addr++) { - phy = data->bus->phy_map[addr]; + phy = mdiobus_get_phy(data->bus, addr); if (phy) { dev_info(dev, "phy[%d]: device %s, driver %s\n", phy->mdio.addr, phydev_name(phy), diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index e5b1ccde835b..f28f89e109ba 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -38,6 +38,48 @@ #include +int mdiobus_register_device(struct mdio_device *mdiodev) +{ + if (mdiodev->bus->mdio_map[mdiodev->addr]) + return -EBUSY; + + mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev; + + return 0; +} +EXPORT_SYMBOL(mdiobus_register_device); + +int mdiobus_unregister_device(struct mdio_device *mdiodev) +{ + if (mdiodev->bus->mdio_map[mdiodev->addr] != mdiodev) + return -EINVAL; + + mdiodev->bus->mdio_map[mdiodev->addr] = NULL; + + return 0; +} +EXPORT_SYMBOL(mdiobus_unregister_device); + +struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr) +{ + struct mdio_device *mdiodev = bus->mdio_map[addr]; + + if (!mdiodev) + return NULL; + + if (!(mdiodev->flags & MDIO_DEVICE_FLAG_PHY)) + return NULL; + + return container_of(mdiodev, struct phy_device, mdio); +} +EXPORT_SYMBOL(mdiobus_get_phy); + +bool mdiobus_is_registered_device(struct mii_bus *bus, int addr) +{ + return bus->mdio_map[addr]; +} +EXPORT_SYMBOL(mdiobus_is_registered_device); + /** * mdiobus_alloc_size - allocate a mii_bus structure * @size: extra amount of memory to allocate for private storage. @@ -299,7 +341,7 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) error: while (--i >= 0) { - struct phy_device *phydev = bus->phy_map[i]; + struct phy_device *phydev = mdiobus_get_phy(bus, i); if (phydev) { phy_device_remove(phydev); phy_device_free(phydev); @@ -318,7 +360,7 @@ void mdiobus_unregister(struct mii_bus *bus) bus->state = MDIOBUS_UNREGISTERED; for (i = 0; i < PHY_MAX_ADDR; i++) { - struct phy_device *phydev = bus->phy_map[i]; + struct phy_device *phydev = mdiobus_get_phy(bus, i); if (phydev) { phy_device_remove(phydev); phy_device_free(phydev); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 01e5d52dc37c..e0d5dbb96700 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -166,6 +166,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, mdiodev->dev.bus = &mdio_bus_type; mdiodev->bus = bus; mdiodev->addr = addr; + mdiodev->flags = MDIO_DEVICE_FLAG_PHY; dev->speed = 0; dev->duplex = -1; @@ -383,10 +384,9 @@ int phy_device_register(struct phy_device *phydev) { int err; - /* Don't register a phy if one is already registered at this address */ - if (phydev->mdio.bus->phy_map[phydev->mdio.addr]) - return -EINVAL; - phydev->mdio.bus->phy_map[phydev->mdio.addr] = phydev; + err = mdiobus_register_device(&phydev->mdio); + if (err) + return err; /* Run all of the fixups for this PHY */ err = phy_scan_fixups(phydev); @@ -404,7 +404,7 @@ int phy_device_register(struct phy_device *phydev) return 0; out: - phydev->mdio.bus->phy_map[phydev->mdio.addr] = NULL; + mdiobus_unregister_device(&phydev->mdio); return err; } EXPORT_SYMBOL(phy_device_register); @@ -419,11 +419,8 @@ EXPORT_SYMBOL(phy_device_register); */ void phy_device_remove(struct phy_device *phydev) { - struct mii_bus *bus = phydev->mdio.bus; - int addr = phydev->mdio.addr; - device_del(&phydev->mdio.dev); - bus->phy_map[addr] = NULL; + mdiobus_unregister_device(&phydev->mdio); } EXPORT_SYMBOL(phy_device_remove); @@ -433,11 +430,13 @@ EXPORT_SYMBOL(phy_device_remove); */ struct phy_device *phy_find_first(struct mii_bus *bus) { + struct phy_device *phydev; int addr; for (addr = 0; addr < PHY_MAX_ADDR; addr++) { - if (bus->phy_map[addr]) - return bus->phy_map[addr]; + phydev = mdiobus_get_phy(bus, addr); + if (phydev) + return phydev; } return NULL; } diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index c0292051392e..6febe2df76f9 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -193,7 +193,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) for (addr = 0; addr < PHY_MAX_ADDR; addr++) { /* skip already registered PHYs */ - if (mdio->phy_map[addr]) + if (mdiobus_is_registered_device(mdio, addr)) continue; /* be noisy to encourage people to set reg property */ diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 94f9f1491cde..8cd9579e18ea 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -19,9 +19,12 @@ struct mdio_device { struct mii_bus *bus; /* Bus address of the MDIO device (0-31) */ int addr; + int flags; }; #define to_mdio_device(d) container_of(d, struct mdio_device, dev) +#define MDIO_DEVICE_FLAG_PHY 1 + static inline bool mdio_phy_id_is_c45(int phy_id) { return (phy_id & MDIO_PHY_ID_C45) && !(phy_id & ~MDIO_PHY_ID_C45_MASK); @@ -188,4 +191,9 @@ int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum); int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val); +int mdiobus_register_device(struct mdio_device *mdiodev); +int mdiobus_unregister_device(struct mdio_device *mdiodev); +bool mdiobus_is_registered_device(struct mii_bus *bus, int addr); +struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr); + #endif /* __LINUX_MDIO_H__ */ diff --git a/include/linux/phy.h b/include/linux/phy.h index 239a0c2bc49d..2d7beef20825 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -180,7 +180,7 @@ struct mii_bus { struct device dev; /* list of all PHYs on bus */ - struct phy_device *phy_map[PHY_MAX_ADDR]; + struct mdio_device *mdio_map[PHY_MAX_ADDR]; /* PHY addresses to be ignored when probing */ u32 phy_mask; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 5f45e68b52dc..2771713714f1 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -997,7 +998,7 @@ static int dsa_slave_phy_connect(struct dsa_slave_priv *p, { struct dsa_switch *ds = p->parent; - p->phy = ds->slave_mii_bus->phy_map[addr]; + p->phy = mdiobus_get_phy(ds->slave_mii_bus, addr); if (!p->phy) { netdev_err(slave_dev, "no phy at %d\n", addr); return -ENODEV; -- cgit v1.2.3-59-g8ed1b From 5cf11beef2c4333b511ff9cef5285320616d938b Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:19 +0100 Subject: phy_device: Move phy attributes into phy_device The mdio_bus exports three attributes: - PHY ID is the unique 32-bits identifier for a MDIO device implementing standard MII registers MII_PHYSID1/2, which is not guaranteed to be the case for non-standard compliant devices (e.g: Ethernet switches) - PHY interface describes the data-path of the PHY/MDIO device, which is not strictly a PHY thing, but is required and needed for PHY devices to function, a MDIO device could be a control device exclusively - PHY has fixups describes what the PHY driver may have done, so completely PHY specific These are all phy attributes, not generic mdio attributes. So move the attributes into the phy device code. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 42 ------------------------------------------ drivers/net/phy/phy_device.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index f28f89e109ba..e6dddb086265 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -670,52 +670,10 @@ static const struct dev_pm_ops mdio_bus_pm_ops = { #endif /* CONFIG_PM */ -static ssize_t -phy_id_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct phy_device *phydev = to_phy_device(dev); - - return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id); -} -static DEVICE_ATTR_RO(phy_id); - -static ssize_t -phy_interface_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct phy_device *phydev = to_phy_device(dev); - const char *mode = NULL; - - if (phy_is_internal(phydev)) - mode = "internal"; - else - mode = phy_modes(phydev->interface); - - return sprintf(buf, "%s\n", mode); -} -static DEVICE_ATTR_RO(phy_interface); - -static ssize_t -phy_has_fixups_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct phy_device *phydev = to_phy_device(dev); - - return sprintf(buf, "%d\n", phydev->has_fixups); -} -static DEVICE_ATTR_RO(phy_has_fixups); - -static struct attribute *mdio_dev_attrs[] = { - &dev_attr_phy_id.attr, - &dev_attr_phy_interface.attr, - &dev_attr_phy_has_fixups.attr, - NULL, -}; -ATTRIBUTE_GROUPS(mdio_dev); - struct bus_type mdio_bus_type = { .name = "mdio_bus", .match = mdio_bus_match, .pm = MDIO_BUS_PM_OPS, - .dev_groups = mdio_dev_groups, }; EXPORT_SYMBOL(mdio_bus_type); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index e0d5dbb96700..7a5222daff93 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -376,6 +376,48 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) } EXPORT_SYMBOL(get_phy_device); +static ssize_t +phy_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct phy_device *phydev = to_phy_device(dev); + + return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id); +} +static DEVICE_ATTR_RO(phy_id); + +static ssize_t +phy_interface_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct phy_device *phydev = to_phy_device(dev); + const char *mode = NULL; + + if (phy_is_internal(phydev)) + mode = "internal"; + else + mode = phy_modes(phydev->interface); + + return sprintf(buf, "%s\n", mode); +} +static DEVICE_ATTR_RO(phy_interface); + +static ssize_t +phy_has_fixups_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct phy_device *phydev = to_phy_device(dev); + + return sprintf(buf, "%d\n", phydev->has_fixups); +} +static DEVICE_ATTR_RO(phy_has_fixups); + +static struct attribute *phy_dev_attrs[] = { + &dev_attr_phy_id.attr, + &dev_attr_phy_interface.attr, + &dev_attr_phy_has_fixups.attr, + NULL, +}; +ATTRIBUTE_GROUPS(phy_dev); + /** * phy_device_register - Register the phy device on the MDIO bus * @phydev: phy_device structure to be added to the MDIO bus @@ -395,6 +437,8 @@ int phy_device_register(struct phy_device *phydev) goto out; } + phydev->mdio.dev.groups = phy_dev_groups; + err = device_add(&phydev->mdio.dev); if (err) { pr_err("PHY %d failed to add\n", phydev->mdio.addr); -- cgit v1.2.3-59-g8ed1b From bc87922ff59d364a33e9bce0febdef21a7fbd2af Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:21 +0100 Subject: phy: Move PHY PM operations into phy_device The MDIO PM operations are really PHY device PM operations. So move them into phy_device. This will be needed when we support devices on the mdio bus which are not PHYs. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 83 ++++---------------------------- drivers/net/phy/phy_device.c | 110 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mdio.h | 2 +- 3 files changed, 121 insertions(+), 74 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index e6dddb086265..65ff8199bd09 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -561,95 +561,32 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv) } #ifdef CONFIG_PM - -static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) -{ - struct device_driver *drv = phydev->mdio.dev.driver; - struct phy_driver *phydrv = to_phy_driver(drv); - struct net_device *netdev = phydev->attached_dev; - - if (!drv || !phydrv->suspend) - return false; - - /* PHY not attached? May suspend if the PHY has not already been - * suspended as part of a prior call to phy_disconnect() -> - * phy_detach() -> phy_suspend() because the parent netdev might be the - * MDIO bus driver and clock gated at this point. - */ - if (!netdev) - return !phydev->suspended; - - /* Don't suspend PHY if the attched netdev parent may wakeup. - * The parent may point to a PCI device, as in tg3 driver. - */ - if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent)) - return false; - - /* Also don't suspend PHY if the netdev itself may wakeup. This - * is the case for devices w/o underlaying pwr. mgmt. aware bus, - * e.g. SoC devices. - */ - if (device_may_wakeup(&netdev->dev)) - return false; - - return true; -} - static int mdio_bus_suspend(struct device *dev) { - struct phy_device *phydev = to_phy_device(dev); + struct mdio_device *mdio = to_mdio_device(dev); - /* We must stop the state machine manually, otherwise it stops out of - * control, possibly with the phydev->lock held. Upon resume, netdev - * may call phy routines that try to grab the same lock, and that may - * lead to a deadlock. - */ - if (phydev->attached_dev && phydev->adjust_link) - phy_stop_machine(phydev); - - if (!mdio_bus_phy_may_suspend(phydev)) - return 0; + if (mdio->pm_ops && mdio->pm_ops->suspend) + return mdio->pm_ops->suspend(dev); - return phy_suspend(phydev); + return 0; } static int mdio_bus_resume(struct device *dev) { - struct phy_device *phydev = to_phy_device(dev); - int ret; - - if (!mdio_bus_phy_may_suspend(phydev)) - goto no_resume; + struct mdio_device *mdio = to_mdio_device(dev); - ret = phy_resume(phydev); - if (ret < 0) - return ret; - -no_resume: - if (phydev->attached_dev && phydev->adjust_link) - phy_start_machine(phydev); + if (mdio->pm_ops && mdio->pm_ops->resume) + return mdio->pm_ops->resume(dev); return 0; } static int mdio_bus_restore(struct device *dev) { - struct phy_device *phydev = to_phy_device(dev); - struct net_device *netdev = phydev->attached_dev; - int ret; - - if (!netdev) - return 0; - - ret = phy_init_hw(phydev); - if (ret < 0) - return ret; - - /* The PHY needs to renegotiate. */ - phydev->link = 0; - phydev->state = PHY_UP; + struct mdio_device *mdio = to_mdio_device(dev); - phy_start_machine(phydev); + if (mdio->pm_ops && mdio->pm_ops->restore) + return mdio->pm_ops->restore(dev); return 0; } diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 7a5222daff93..eb0b0ed32662 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -63,6 +63,115 @@ static struct phy_driver genphy_driver[GENPHY_DRV_MAX]; static LIST_HEAD(phy_fixup_list); static DEFINE_MUTEX(phy_fixup_lock); +#ifdef CONFIG_PM +static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) +{ + struct device_driver *drv = phydev->mdio.dev.driver; + struct phy_driver *phydrv = to_phy_driver(drv); + struct net_device *netdev = phydev->attached_dev; + + if (!drv || !phydrv->suspend) + return false; + + /* PHY not attached? May suspend if the PHY has not already been + * suspended as part of a prior call to phy_disconnect() -> + * phy_detach() -> phy_suspend() because the parent netdev might be the + * MDIO bus driver and clock gated at this point. + */ + if (!netdev) + return !phydev->suspended; + + /* Don't suspend PHY if the attached netdev parent may wakeup. + * The parent may point to a PCI device, as in tg3 driver. + */ + if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent)) + return false; + + /* Also don't suspend PHY if the netdev itself may wakeup. This + * is the case for devices w/o underlaying pwr. mgmt. aware bus, + * e.g. SoC devices. + */ + if (device_may_wakeup(&netdev->dev)) + return false; + + return true; +} + +static int mdio_bus_phy_suspend(struct device *dev) +{ + struct phy_device *phydev = to_phy_device(dev); + + /* We must stop the state machine manually, otherwise it stops out of + * control, possibly with the phydev->lock held. Upon resume, netdev + * may call phy routines that try to grab the same lock, and that may + * lead to a deadlock. + */ + if (phydev->attached_dev && phydev->adjust_link) + phy_stop_machine(phydev); + + if (!mdio_bus_phy_may_suspend(phydev)) + return 0; + + return phy_suspend(phydev); +} + +static int mdio_bus_phy_resume(struct device *dev) +{ + struct phy_device *phydev = to_phy_device(dev); + int ret; + + if (!mdio_bus_phy_may_suspend(phydev)) + goto no_resume; + + ret = phy_resume(phydev); + if (ret < 0) + return ret; + +no_resume: + if (phydev->attached_dev && phydev->adjust_link) + phy_start_machine(phydev); + + return 0; +} + +static int mdio_bus_phy_restore(struct device *dev) +{ + struct phy_device *phydev = to_phy_device(dev); + struct net_device *netdev = phydev->attached_dev; + int ret; + + if (!netdev) + return 0; + + ret = phy_init_hw(phydev); + if (ret < 0) + return ret; + + /* The PHY needs to renegotiate. */ + phydev->link = 0; + phydev->state = PHY_UP; + + phy_start_machine(phydev); + + return 0; +} + +static const struct dev_pm_ops mdio_bus_phy_pm_ops = { + .suspend = mdio_bus_phy_suspend, + .resume = mdio_bus_phy_resume, + .freeze = mdio_bus_phy_suspend, + .thaw = mdio_bus_phy_resume, + .restore = mdio_bus_phy_restore, +}; + +#define MDIO_BUS_PHY_PM_OPS (&mdio_bus_phy_pm_ops) + +#else + +#define MDIO_BUS_PHY_PM_OPS NULL + +#endif /* CONFIG_PM */ + /** * phy_register_fixup - creates a new phy_fixup and adds it to the list * @bus_id: A string which matches phydev->mdio.dev.bus_id (or PHY_ANY_ID) @@ -165,6 +274,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, mdiodev->dev.parent = &bus->dev; mdiodev->dev.bus = &mdio_bus_type; mdiodev->bus = bus; + mdiodev->pm_ops = MDIO_BUS_PHY_PM_OPS; mdiodev->addr = addr; mdiodev->flags = MDIO_DEVICE_FLAG_PHY; diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 8cd9579e18ea..9f844d372ed5 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -15,7 +15,7 @@ struct mii_bus; struct mdio_device { struct device dev; - + const struct dev_pm_ops *pm_ops; struct mii_bus *bus; /* Bus address of the MDIO device (0-31) */ int addr; -- cgit v1.2.3-59-g8ed1b From be01da72b1b832b89fbdf59ae6f1b60e53ca2987 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:22 +0100 Subject: phy: Centralize setting driver module owner Rather than have each driver set the driver owner field, do it once in the core code. This will also help with later changes, when the device structure will move. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/amd.c | 1 - drivers/net/phy/aquantia.c | 4 ---- drivers/net/phy/at803x.c | 9 --------- drivers/net/phy/bcm63xx.c | 2 -- drivers/net/phy/bcm7xxx.c | 6 ------ drivers/net/phy/bcm87xx.c | 2 -- drivers/net/phy/broadcom.c | 12 ------------ drivers/net/phy/cicada.c | 2 -- drivers/net/phy/davicom.c | 4 ---- drivers/net/phy/dp83640.c | 3 +-- drivers/net/phy/dp83848.c | 2 -- drivers/net/phy/dp83867.c | 2 -- drivers/net/phy/et1011c.c | 1 - drivers/net/phy/icplus.c | 3 --- drivers/net/phy/lxt.c | 4 ---- drivers/net/phy/marvell.c | 13 ------------- drivers/net/phy/micrel.c | 13 ------------- drivers/net/phy/microchip.c | 2 -- drivers/net/phy/national.c | 1 - drivers/net/phy/phy_device.c | 13 +++++++------ drivers/net/phy/qsemi.c | 1 - drivers/net/phy/realtek.c | 5 ----- drivers/net/phy/smsc.c | 10 ---------- drivers/net/phy/ste10Xp.c | 2 -- drivers/net/phy/teranetics.c | 1 - drivers/net/phy/vitesse.c | 8 -------- include/linux/phy.h | 7 ++++--- 27 files changed, 12 insertions(+), 121 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/amd.c b/drivers/net/phy/amd.c index 65a488f82eb8..18141c022b13 100644 --- a/drivers/net/phy/amd.c +++ b/drivers/net/phy/amd.c @@ -72,7 +72,6 @@ static struct phy_driver am79c_driver[] = { { .read_status = genphy_read_status, .ack_interrupt = am79c_ack_interrupt, .config_intr = am79c_config_intr, - .driver = { .owner = THIS_MODULE,}, } }; module_phy_driver(am79c_driver); diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c index f1936b7a7af6..09b0b0aa8d68 100644 --- a/drivers/net/phy/aquantia.c +++ b/drivers/net/phy/aquantia.c @@ -128,7 +128,6 @@ static struct phy_driver aquantia_driver[] = { .config_intr = aquantia_config_intr, .ack_interrupt = aquantia_ack_interrupt, .read_status = aquantia_read_status, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_AQ2104, @@ -141,7 +140,6 @@ static struct phy_driver aquantia_driver[] = { .config_intr = aquantia_config_intr, .ack_interrupt = aquantia_ack_interrupt, .read_status = aquantia_read_status, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_AQR105, @@ -154,7 +152,6 @@ static struct phy_driver aquantia_driver[] = { .config_intr = aquantia_config_intr, .ack_interrupt = aquantia_ack_interrupt, .read_status = aquantia_read_status, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_AQR405, @@ -167,7 +164,6 @@ static struct phy_driver aquantia_driver[] = { .config_intr = aquantia_config_intr, .ack_interrupt = aquantia_ack_interrupt, .read_status = aquantia_read_status, - .driver = { .owner = THIS_MODULE,}, }, }; diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index b76ac09a554f..8a8f6fb2880d 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -310,9 +310,6 @@ static struct phy_driver at803x_driver[] = { .read_status = genphy_read_status, .ack_interrupt = at803x_ack_interrupt, .config_intr = at803x_config_intr, - .driver = { - .owner = THIS_MODULE, - }, }, { /* ATHEROS 8030 */ .phy_id = ATH8030_PHY_ID, @@ -331,9 +328,6 @@ static struct phy_driver at803x_driver[] = { .read_status = genphy_read_status, .ack_interrupt = at803x_ack_interrupt, .config_intr = at803x_config_intr, - .driver = { - .owner = THIS_MODULE, - }, }, { /* ATHEROS 8031 */ .phy_id = ATH8031_PHY_ID, @@ -352,9 +346,6 @@ static struct phy_driver at803x_driver[] = { .read_status = genphy_read_status, .ack_interrupt = &at803x_ack_interrupt, .config_intr = &at803x_config_intr, - .driver = { - .owner = THIS_MODULE, - }, } }; module_phy_driver(at803x_driver); diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c index 86b28052bf06..e741bf614c4e 100644 --- a/drivers/net/phy/bcm63xx.c +++ b/drivers/net/phy/bcm63xx.c @@ -56,7 +56,6 @@ static struct phy_driver bcm63xx_driver[] = { .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, }, { /* same phy as above, with just a different OUI */ .phy_id = 0x002bdc00, @@ -69,7 +68,6 @@ static struct phy_driver bcm63xx_driver[] = { .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, } }; module_phy_driver(bcm63xx_driver); diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 9f4e6eb886af..bf241a3ec5e5 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -324,7 +324,6 @@ static int bcm7xxx_dummy_config_init(struct phy_device *phydev) .config_aneg = genphy_config_aneg, \ .read_status = genphy_read_status, \ .resume = bcm7xxx_28nm_resume, \ - .driver = { .owner = THIS_MODULE }, \ } static struct phy_driver bcm7xxx_driver[] = { @@ -346,7 +345,6 @@ static struct phy_driver bcm7xxx_driver[] = { .read_status = genphy_read_status, .suspend = bcm7xxx_suspend, .resume = bcm7xxx_config_init, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM7429, .phy_id_mask = 0xfffffff0, @@ -359,7 +357,6 @@ static struct phy_driver bcm7xxx_driver[] = { .read_status = genphy_read_status, .suspend = bcm7xxx_suspend, .resume = bcm7xxx_config_init, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM7435, .phy_id_mask = 0xfffffff0, @@ -372,7 +369,6 @@ static struct phy_driver bcm7xxx_driver[] = { .read_status = genphy_read_status, .suspend = bcm7xxx_suspend, .resume = bcm7xxx_config_init, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_BCM_OUI_4, .phy_id_mask = 0xffff0000, @@ -385,7 +381,6 @@ static struct phy_driver bcm7xxx_driver[] = { .read_status = genphy_read_status, .suspend = bcm7xxx_suspend, .resume = bcm7xxx_config_init, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_BCM_OUI_5, .phy_id_mask = 0xffffff00, @@ -398,7 +393,6 @@ static struct phy_driver bcm7xxx_driver[] = { .read_status = genphy_read_status, .suspend = bcm7xxx_suspend, .resume = bcm7xxx_config_init, - .driver = { .owner = THIS_MODULE }, } }; static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { diff --git a/drivers/net/phy/bcm87xx.c b/drivers/net/phy/bcm87xx.c index e536e30d1643..f7ebdcff53e4 100644 --- a/drivers/net/phy/bcm87xx.c +++ b/drivers/net/phy/bcm87xx.c @@ -201,7 +201,6 @@ static struct phy_driver bcm87xx_driver[] = { .config_intr = bcm87xx_config_intr, .did_interrupt = bcm87xx_did_interrupt, .match_phy_device = bcm8706_match_phy_device, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM8727, .phy_id_mask = 0xffffffff, @@ -214,7 +213,6 @@ static struct phy_driver bcm87xx_driver[] = { .config_intr = bcm87xx_config_intr, .did_interrupt = bcm87xx_did_interrupt, .match_phy_device = bcm8727_match_phy_device, - .driver = { .owner = THIS_MODULE }, } }; module_phy_driver(bcm87xx_driver); diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 3ce5d9514623..870327efccf7 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -460,7 +460,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5421, .phy_id_mask = 0xfffffff0, @@ -473,7 +472,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5461, .phy_id_mask = 0xfffffff0, @@ -486,7 +484,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM54616S, .phy_id_mask = 0xfffffff0, @@ -499,7 +496,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5464, .phy_id_mask = 0xfffffff0, @@ -512,7 +508,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5481, .phy_id_mask = 0xfffffff0, @@ -525,7 +520,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5482, .phy_id_mask = 0xfffffff0, @@ -538,7 +532,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = bcm5482_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM50610, .phy_id_mask = 0xfffffff0, @@ -551,7 +544,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM50610M, .phy_id_mask = 0xfffffff0, @@ -564,7 +556,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM57780, .phy_id_mask = 0xfffffff0, @@ -577,7 +568,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = genphy_read_status, .ack_interrupt = bcm_phy_ack_intr, .config_intr = bcm_phy_config_intr, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCMAC131, .phy_id_mask = 0xfffffff0, @@ -590,7 +580,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = genphy_read_status, .ack_interrupt = brcm_fet_ack_interrupt, .config_intr = brcm_fet_config_intr, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = PHY_ID_BCM5241, .phy_id_mask = 0xfffffff0, @@ -603,7 +592,6 @@ static struct phy_driver broadcom_drivers[] = { .read_status = genphy_read_status, .ack_interrupt = brcm_fet_ack_interrupt, .config_intr = brcm_fet_config_intr, - .driver = { .owner = THIS_MODULE }, } }; module_phy_driver(broadcom_drivers); diff --git a/drivers/net/phy/cicada.c b/drivers/net/phy/cicada.c index 27f5464899d4..d339c1afea77 100644 --- a/drivers/net/phy/cicada.c +++ b/drivers/net/phy/cicada.c @@ -114,7 +114,6 @@ static struct phy_driver cis820x_driver[] = { .read_status = &genphy_read_status, .ack_interrupt = &cis820x_ack_interrupt, .config_intr = &cis820x_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x000fc440, .name = "Cicada Cis8204", @@ -126,7 +125,6 @@ static struct phy_driver cis820x_driver[] = { .read_status = &genphy_read_status, .ack_interrupt = &cis820x_ack_interrupt, .config_intr = &cis820x_config_intr, - .driver = { .owner = THIS_MODULE,}, } }; module_phy_driver(cis820x_driver); diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c index 2a328703b4ae..36e3e2033eca 100644 --- a/drivers/net/phy/davicom.c +++ b/drivers/net/phy/davicom.c @@ -156,7 +156,6 @@ static struct phy_driver dm91xx_driver[] = { .read_status = genphy_read_status, .ack_interrupt = dm9161_ack_interrupt, .config_intr = dm9161_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x0181b8b0, .name = "Davicom DM9161B/C", @@ -168,7 +167,6 @@ static struct phy_driver dm91xx_driver[] = { .read_status = genphy_read_status, .ack_interrupt = dm9161_ack_interrupt, .config_intr = dm9161_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x0181b8a0, .name = "Davicom DM9161A", @@ -180,7 +178,6 @@ static struct phy_driver dm91xx_driver[] = { .read_status = genphy_read_status, .ack_interrupt = dm9161_ack_interrupt, .config_intr = dm9161_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x00181b80, .name = "Davicom DM9131", @@ -191,7 +188,6 @@ static struct phy_driver dm91xx_driver[] = { .read_status = genphy_read_status, .ack_interrupt = dm9161_ack_interrupt, .config_intr = dm9161_config_intr, - .driver = { .owner = THIS_MODULE,}, } }; module_phy_driver(dm91xx_driver); diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index 39da6fc6a85e..180f69952779 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -1492,12 +1492,11 @@ static struct phy_driver dp83640_driver = { .hwtstamp = dp83640_hwtstamp, .rxtstamp = dp83640_rxtstamp, .txtstamp = dp83640_txtstamp, - .driver = {.owner = THIS_MODULE,} }; static int __init dp83640_init(void) { - return phy_driver_register(&dp83640_driver); + return phy_driver_register(&dp83640_driver, THIS_MODULE); } static void __exit dp83640_exit(void) diff --git a/drivers/net/phy/dp83848.c b/drivers/net/phy/dp83848.c index 5ce9bef54468..5e14e629c597 100644 --- a/drivers/net/phy/dp83848.c +++ b/drivers/net/phy/dp83848.c @@ -88,8 +88,6 @@ static struct phy_driver dp83848_driver[] = { /* IRQ related */ .ack_interrupt = dp83848_ack_interrupt, .config_intr = dp83848_config_intr, - - .driver = { .owner = THIS_MODULE, }, }, }; module_phy_driver(dp83848_driver); diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 74e4521bd2d3..2afa61b51d41 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -214,8 +214,6 @@ static struct phy_driver dp83867_driver[] = { .read_status = genphy_read_status, .suspend = genphy_suspend, .resume = genphy_resume, - - .driver = {.owner = THIS_MODULE,} }, }; module_phy_driver(dp83867_driver); diff --git a/drivers/net/phy/et1011c.c b/drivers/net/phy/et1011c.c index a907743816a8..a9a4edfa23c8 100644 --- a/drivers/net/phy/et1011c.c +++ b/drivers/net/phy/et1011c.c @@ -95,7 +95,6 @@ static struct phy_driver et1011c_driver[] = { { .flags = PHY_POLL, .config_aneg = et1011c_config_aneg, .read_status = et1011c_read_status, - .driver = { .owner = THIS_MODULE,}, } }; module_phy_driver(et1011c_driver); diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c index c12170d07b62..e5f251b91578 100644 --- a/drivers/net/phy/icplus.c +++ b/drivers/net/phy/icplus.c @@ -221,7 +221,6 @@ static struct phy_driver icplus_driver[] = { .read_status = &ip175c_read_status, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x02430d90, .name = "ICPlus IP1001", @@ -233,7 +232,6 @@ static struct phy_driver icplus_driver[] = { .read_status = &genphy_read_status, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x02430c54, .name = "ICPlus IP101A/G", @@ -247,7 +245,6 @@ static struct phy_driver icplus_driver[] = { .read_status = &genphy_read_status, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, } }; module_phy_driver(icplus_driver); diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c index a3a5a703635b..f6078376ef50 100644 --- a/drivers/net/phy/lxt.c +++ b/drivers/net/phy/lxt.c @@ -278,7 +278,6 @@ static struct phy_driver lxt97x_driver[] = { .read_status = genphy_read_status, .ack_interrupt = lxt970_ack_interrupt, .config_intr = lxt970_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x001378e0, .name = "LXT971", @@ -289,7 +288,6 @@ static struct phy_driver lxt97x_driver[] = { .read_status = genphy_read_status, .ack_interrupt = lxt971_ack_interrupt, .config_intr = lxt971_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x00137a10, .name = "LXT973-A2", @@ -299,7 +297,6 @@ static struct phy_driver lxt97x_driver[] = { .probe = lxt973_probe, .config_aneg = lxt973_config_aneg, .read_status = lxt973a2_read_status, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x00137a10, .name = "LXT973", @@ -309,7 +306,6 @@ static struct phy_driver lxt97x_driver[] = { .probe = lxt973_probe, .config_aneg = lxt973_config_aneg, .read_status = genphy_read_status, - .driver = { .owner = THIS_MODULE,}, } }; module_phy_driver(lxt97x_driver); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index f96c93c9819a..e3eb96443c97 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1087,7 +1087,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = MARVELL_PHY_ID_88E1112, @@ -1106,7 +1105,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = MARVELL_PHY_ID_88E1111, @@ -1125,7 +1123,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = MARVELL_PHY_ID_88E1118, @@ -1144,7 +1141,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = {.owner = THIS_MODULE,}, }, { .phy_id = MARVELL_PHY_ID_88E1121R, @@ -1163,7 +1159,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = MARVELL_PHY_ID_88E1318S, @@ -1184,7 +1179,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = MARVELL_PHY_ID_88E1145, @@ -1203,7 +1197,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = MARVELL_PHY_ID_88E1149R, @@ -1222,7 +1215,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = MARVELL_PHY_ID_88E1240, @@ -1241,7 +1233,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = MARVELL_PHY_ID_88E1116R, @@ -1260,7 +1251,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = MARVELL_PHY_ID_88E1510, @@ -1279,7 +1269,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = MARVELL_PHY_ID_88E1540, @@ -1298,7 +1287,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, { .phy_id = MARVELL_PHY_ID_88E3016, @@ -1319,7 +1307,6 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, - .driver = { .owner = THIS_MODULE }, }, }; diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index b51505be1fa9..0dbc6496b6f6 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -706,7 +706,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8021, .phy_id_mask = 0x00ffffff, @@ -726,7 +725,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8031, .phy_id_mask = 0x00ffffff, @@ -746,7 +744,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8041, .phy_id_mask = 0x00fffff0, @@ -766,7 +763,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8041RNLI, .phy_id_mask = 0x00fffff0, @@ -786,7 +782,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8051, .phy_id_mask = 0x00fffff0, @@ -806,7 +801,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8001, .name = "Micrel KSZ8001 or KS8721", @@ -825,7 +819,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8081, .name = "Micrel KSZ8081 or KSZ8091", @@ -844,7 +837,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8061, .name = "Micrel KSZ8061", @@ -861,7 +853,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ9021, .phy_id_mask = 0x000ffffe, @@ -881,7 +872,6 @@ static struct phy_driver ksphy_driver[] = { .resume = genphy_resume, .read_mmd_indirect = ksz9021_rd_mmd_phyreg, .write_mmd_indirect = ksz9021_wr_mmd_phyreg, - .driver = { .owner = THIS_MODULE, }, }, { .phy_id = PHY_ID_KSZ9031, .phy_id_mask = 0x00fffff0, @@ -899,7 +889,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE, }, }, { .phy_id = PHY_ID_KSZ8873MLL, .phy_id_mask = 0x00fffff0, @@ -914,7 +903,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE, }, }, { .phy_id = PHY_ID_KSZ886X, .phy_id_mask = 0x00fffff0, @@ -929,7 +917,6 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE, }, } }; module_phy_driver(ksphy_driver); diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c index 5e34b49be0b3..15f820648f82 100644 --- a/drivers/net/phy/microchip.c +++ b/drivers/net/phy/microchip.c @@ -129,8 +129,6 @@ static struct phy_driver microchip_phy_driver[] = { .suspend = lan88xx_suspend, .resume = genphy_resume, .set_wol = lan88xx_set_wol, - - .driver = { .owner = THIS_MODULE, } } }; module_phy_driver(microchip_phy_driver); diff --git a/drivers/net/phy/national.c b/drivers/net/phy/national.c index 0a7b9c7f09a2..2a1b490bc587 100644 --- a/drivers/net/phy/national.c +++ b/drivers/net/phy/national.c @@ -140,7 +140,6 @@ static struct phy_driver dp83865_driver[] = { { .read_status = genphy_read_status, .ack_interrupt = ns_ack_interrupt, .config_intr = ns_config_intr, - .driver = {.owner = THIS_MODULE,} } }; module_phy_driver(dp83865_driver); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index eb0b0ed32662..a1b833cd4183 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1564,8 +1564,9 @@ static int phy_remove(struct device *dev) /** * phy_driver_register - register a phy_driver with the PHY layer * @new_driver: new phy_driver to register + * @owner: module owning this PHY */ -int phy_driver_register(struct phy_driver *new_driver) +int phy_driver_register(struct phy_driver *new_driver, struct module *owner) { int retval; @@ -1573,6 +1574,7 @@ int phy_driver_register(struct phy_driver *new_driver) new_driver->driver.bus = &mdio_bus_type; new_driver->driver.probe = phy_probe; new_driver->driver.remove = phy_remove; + new_driver->driver.owner = owner; retval = driver_register(&new_driver->driver); if (retval) { @@ -1588,12 +1590,13 @@ int phy_driver_register(struct phy_driver *new_driver) } EXPORT_SYMBOL(phy_driver_register); -int phy_drivers_register(struct phy_driver *new_driver, int n) +int phy_drivers_register(struct phy_driver *new_driver, int n, + struct module *owner) { int i, ret = 0; for (i = 0; i < n; i++) { - ret = phy_driver_register(new_driver + i); + ret = phy_driver_register(new_driver + i, owner); if (ret) { while (i-- > 0) phy_driver_unregister(new_driver + i); @@ -1634,7 +1637,6 @@ static struct phy_driver genphy_driver[] = { .read_status = genphy_read_status, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE, }, }, { .phy_id = 0xffffffff, .phy_id_mask = 0xffffffff, @@ -1646,7 +1648,6 @@ static struct phy_driver genphy_driver[] = { .read_status = gen10g_read_status, .suspend = gen10g_suspend, .resume = gen10g_resume, - .driver = {.owner = THIS_MODULE, }, } }; static int __init phy_init(void) @@ -1658,7 +1659,7 @@ static int __init phy_init(void) return rc; rc = phy_drivers_register(genphy_driver, - ARRAY_SIZE(genphy_driver)); + ARRAY_SIZE(genphy_driver), THIS_MODULE); if (rc) mdio_bus_exit(); diff --git a/drivers/net/phy/qsemi.c b/drivers/net/phy/qsemi.c index be4c6f7c3645..d470db89e8dd 100644 --- a/drivers/net/phy/qsemi.c +++ b/drivers/net/phy/qsemi.c @@ -122,7 +122,6 @@ static struct phy_driver qs6612_driver[] = { { .read_status = genphy_read_status, .ack_interrupt = qs6612_ack_interrupt, .config_intr = qs6612_config_intr, - .driver = { .owner = THIS_MODULE,}, } }; module_phy_driver(qs6612_driver); diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 43ab691362d4..aadd6e9f54ad 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -124,7 +124,6 @@ static struct phy_driver realtek_drvs[] = { .flags = PHY_HAS_INTERRUPT, .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x001cc912, .name = "RTL8211B Gigabit Ethernet", @@ -135,7 +134,6 @@ static struct phy_driver realtek_drvs[] = { .read_status = &genphy_read_status, .ack_interrupt = &rtl821x_ack_interrupt, .config_intr = &rtl8211b_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x001cc914, .name = "RTL8211DN Gigabit Ethernet", @@ -148,7 +146,6 @@ static struct phy_driver realtek_drvs[] = { .config_intr = rtl8211e_config_intr, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x001cc915, .name = "RTL8211E Gigabit Ethernet", @@ -161,7 +158,6 @@ static struct phy_driver realtek_drvs[] = { .config_intr = &rtl8211e_config_intr, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = 0x001cc916, .name = "RTL8211F Gigabit Ethernet", @@ -175,7 +171,6 @@ static struct phy_driver realtek_drvs[] = { .config_intr = &rtl8211f_config_intr, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = { .owner = THIS_MODULE }, }, }; diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 18c981b95910..ddb06135c21d 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -171,8 +171,6 @@ static struct phy_driver smsc_phy_driver[] = { .suspend = genphy_suspend, .resume = genphy_resume, - - .driver = { .owner = THIS_MODULE, } }, { .phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */ .phy_id_mask = 0xfffffff0, @@ -194,8 +192,6 @@ static struct phy_driver smsc_phy_driver[] = { .suspend = genphy_suspend, .resume = genphy_resume, - - .driver = { .owner = THIS_MODULE, } }, { .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ .phy_id_mask = 0xfffffff0, @@ -217,8 +213,6 @@ static struct phy_driver smsc_phy_driver[] = { .suspend = genphy_suspend, .resume = genphy_resume, - - .driver = { .owner = THIS_MODULE, } }, { .phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */ .phy_id_mask = 0xfffffff0, @@ -239,8 +233,6 @@ static struct phy_driver smsc_phy_driver[] = { .suspend = genphy_suspend, .resume = genphy_resume, - - .driver = { .owner = THIS_MODULE, } }, { .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ .phy_id_mask = 0xfffffff0, @@ -262,8 +254,6 @@ static struct phy_driver smsc_phy_driver[] = { .suspend = genphy_suspend, .resume = genphy_resume, - - .driver = { .owner = THIS_MODULE, } } }; module_phy_driver(smsc_phy_driver); diff --git a/drivers/net/phy/ste10Xp.c b/drivers/net/phy/ste10Xp.c index 3fc199b773e6..d00cfb64529e 100644 --- a/drivers/net/phy/ste10Xp.c +++ b/drivers/net/phy/ste10Xp.c @@ -95,7 +95,6 @@ static struct phy_driver ste10xp_pdriver[] = { .config_intr = ste10Xp_config_intr, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = {.owner = THIS_MODULE,} }, { .phy_id = STE100P_PHY_ID, .phy_id_mask = 0xffffffff, @@ -109,7 +108,6 @@ static struct phy_driver ste10xp_pdriver[] = { .config_intr = ste10Xp_config_intr, .suspend = genphy_suspend, .resume = genphy_resume, - .driver = {.owner = THIS_MODULE,} } }; module_phy_driver(ste10xp_pdriver); diff --git a/drivers/net/phy/teranetics.c b/drivers/net/phy/teranetics.c index 07463fcca212..fb2cef764e9a 100644 --- a/drivers/net/phy/teranetics.c +++ b/drivers/net/phy/teranetics.c @@ -108,7 +108,6 @@ static struct phy_driver teranetics_driver[] = { .config_aneg = teranetics_config_aneg, .read_status = teranetics_read_status, .match_phy_device = teranetics_match_phy_device, - .driver = { .owner = THIS_MODULE,}, }, }; diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c index dd295dbaa074..2e37eb337d48 100644 --- a/drivers/net/phy/vitesse.c +++ b/drivers/net/phy/vitesse.c @@ -236,7 +236,6 @@ static struct phy_driver vsc82xx_driver[] = { .read_status = &genphy_read_status, .ack_interrupt = &vsc824x_ack_interrupt, .config_intr = &vsc82xx_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_VSC8244, .name = "Vitesse VSC8244", @@ -248,7 +247,6 @@ static struct phy_driver vsc82xx_driver[] = { .read_status = &genphy_read_status, .ack_interrupt = &vsc824x_ack_interrupt, .config_intr = &vsc82xx_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_VSC8514, .name = "Vitesse VSC8514", @@ -260,7 +258,6 @@ static struct phy_driver vsc82xx_driver[] = { .read_status = &genphy_read_status, .ack_interrupt = &vsc824x_ack_interrupt, .config_intr = &vsc82xx_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_VSC8574, .name = "Vitesse VSC8574", @@ -272,7 +269,6 @@ static struct phy_driver vsc82xx_driver[] = { .read_status = &genphy_read_status, .ack_interrupt = &vsc824x_ack_interrupt, .config_intr = &vsc82xx_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_VSC8601, .name = "Vitesse VSC8601", @@ -284,7 +280,6 @@ static struct phy_driver vsc82xx_driver[] = { .read_status = &genphy_read_status, .ack_interrupt = &vsc824x_ack_interrupt, .config_intr = &vsc82xx_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_VSC8662, .name = "Vitesse VSC8662", @@ -296,7 +291,6 @@ static struct phy_driver vsc82xx_driver[] = { .read_status = &genphy_read_status, .ack_interrupt = &vsc824x_ack_interrupt, .config_intr = &vsc82xx_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { /* Vitesse 8221 */ .phy_id = PHY_ID_VSC8221, @@ -309,7 +303,6 @@ static struct phy_driver vsc82xx_driver[] = { .read_status = &genphy_read_status, .ack_interrupt = &vsc824x_ack_interrupt, .config_intr = &vsc82xx_config_intr, - .driver = { .owner = THIS_MODULE,}, }, { /* Vitesse 8211 */ .phy_id = PHY_ID_VSC8211, @@ -322,7 +315,6 @@ static struct phy_driver vsc82xx_driver[] = { .read_status = &genphy_read_status, .ack_interrupt = &vsc824x_ack_interrupt, .config_intr = &vsc82xx_config_intr, - .driver = { .owner = THIS_MODULE,}, } }; module_phy_driver(vsc82xx_driver); diff --git a/include/linux/phy.h b/include/linux/phy.h index 2d7beef20825..49e4418822b3 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -797,8 +797,9 @@ int genphy_resume(struct phy_device *phydev); int genphy_soft_reset(struct phy_device *phydev); void phy_driver_unregister(struct phy_driver *drv); void phy_drivers_unregister(struct phy_driver *drv, int n); -int phy_driver_register(struct phy_driver *new_driver); -int phy_drivers_register(struct phy_driver *new_driver, int n); +int phy_driver_register(struct phy_driver *new_driver, struct module *owner); +int phy_drivers_register(struct phy_driver *new_driver, int n, + struct module *owner); void phy_state_machine(struct work_struct *work); void phy_change(struct work_struct *work); void phy_mac_interrupt(struct phy_device *phydev, int new_link); @@ -843,7 +844,7 @@ extern struct bus_type mdio_bus_type; #define phy_module_driver(__phy_drivers, __count) \ static int __init phy_module_init(void) \ { \ - return phy_drivers_register(__phy_drivers, __count); \ + return phy_drivers_register(__phy_drivers, __count, THIS_MODULE); \ } \ module_init(phy_module_init); \ static void __exit phy_module_exit(void) \ -- cgit v1.2.3-59-g8ed1b From e76a4957c5ee5cf69cea89d450c29c536e77ce9e Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:23 +0100 Subject: phy: Move phy specific bus match into phy_device Matching a driver to a device has both generic parts, and parts which are specific to PHY devices. Move the PHY specific parts into phy_device. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 38 ++++++++++++-------------------------- drivers/net/phy/phy_device.c | 28 ++++++++++++++++++++++++++++ include/linux/mdio.h | 1 + 3 files changed, 41 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 65ff8199bd09..bd523b2c6331 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -523,41 +523,27 @@ int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) EXPORT_SYMBOL(mdiobus_write); /** - * mdio_bus_match - determine if given PHY driver supports the given PHY device - * @dev: target PHY device - * @drv: given PHY driver + * mdio_bus_match - determine if given MDIO driver supports the given + * MDIO device + * @dev: target MDIO device + * @drv: given MDIO driver * - * Description: Given a PHY device, and a PHY driver, return 1 if - * the driver supports the device. Otherwise, return 0. + * Description: Given a MDIO device, and a MDIO driver, return 1 if + * the driver supports the device. Otherwise, return 0. This may + * require calling the devices own match function, since different classes + * of MDIO devices have different match criteria. */ static int mdio_bus_match(struct device *dev, struct device_driver *drv) { - struct phy_device *phydev = to_phy_device(dev); - struct phy_driver *phydrv = to_phy_driver(drv); - const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); - int i; + struct mdio_device *mdio = to_mdio_device(dev); if (of_driver_match_device(dev, drv)) return 1; - if (phydrv->match_phy_device) - return phydrv->match_phy_device(phydev); + if (mdio->bus_match) + return mdio->bus_match(dev, drv); - if (phydev->is_c45) { - for (i = 1; i < num_ids; i++) { - if (!(phydev->c45_ids.devices_in_package & (1 << i))) - continue; - - if ((phydrv->phy_id & phydrv->phy_id_mask) == - (phydev->c45_ids.device_ids[i] & - phydrv->phy_id_mask)) - return 1; - } - return 0; - } else { - return (phydrv->phy_id & phydrv->phy_id_mask) == - (phydev->phy_id & phydrv->phy_id_mask); - } + return 0; } #ifdef CONFIG_PM diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index a1b833cd4183..78628428ee28 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -257,6 +257,33 @@ static int phy_scan_fixups(struct phy_device *phydev) return 0; } +static int phy_bus_match(struct device *dev, struct device_driver *drv) +{ + struct phy_device *phydev = to_phy_device(dev); + struct phy_driver *phydrv = to_phy_driver(drv); + const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); + int i; + + if (phydrv->match_phy_device) + return phydrv->match_phy_device(phydev); + + if (phydev->is_c45) { + for (i = 1; i < num_ids; i++) { + if (!(phydev->c45_ids.devices_in_package & (1 << i))) + continue; + + if ((phydrv->phy_id & phydrv->phy_id_mask) == + (phydev->c45_ids.device_ids[i] & + phydrv->phy_id_mask)) + return 1; + } + return 0; + } else { + return (phydrv->phy_id & phydrv->phy_id_mask) == + (phydev->phy_id & phydrv->phy_id_mask); + } +} + struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, bool is_c45, struct phy_c45_device_ids *c45_ids) @@ -275,6 +302,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, mdiodev->dev.bus = &mdio_bus_type; mdiodev->bus = bus; mdiodev->pm_ops = MDIO_BUS_PHY_PM_OPS; + mdiodev->bus_match = phy_bus_match; mdiodev->addr = addr; mdiodev->flags = MDIO_DEVICE_FLAG_PHY; diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 9f844d372ed5..0690359e55a5 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -17,6 +17,7 @@ struct mdio_device { struct device dev; const struct dev_pm_ops *pm_ops; struct mii_bus *bus; + int (*bus_match)(struct device *dev, struct device_driver *drv); /* Bus address of the MDIO device (0-31) */ int addr; int flags; -- cgit v1.2.3-59-g8ed1b From f03bc4ae552f30faa9bbee2d72bd1ee34667011a Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:24 +0100 Subject: mdio_bus: Generalise of_mdiobus_link_phydev() This function should work with any sort of MDIO device which can be probed on the bus, not just PHY devices. So generalise it. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index bd523b2c6331..0d369ad19d17 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -237,15 +237,16 @@ struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np) } EXPORT_SYMBOL(of_mdio_find_bus); -/* Walk the list of subnodes of a mdio bus and look for a node that matches the - * phy's address with its 'reg' property. If found, set the of_node pointer for - * the phy. This allows auto-probed pyh devices to be supplied with information - * passed in via DT. +/* Walk the list of subnodes of a mdio bus and look for a node that + * matches the mdio device's address with its 'reg' property. If + * found, set the of_node pointer for the mdio device. This allows + * auto-probed phy devices to be supplied with information passed in + * via DT. */ -static void of_mdiobus_link_phydev(struct mii_bus *bus, - struct phy_device *phydev) +static void of_mdiobus_link_mdiodev(struct mii_bus *bus, + struct mdio_device *mdiodev) { - struct device *dev = &phydev->mdio.dev; + struct device *dev = &mdiodev->dev; struct device_node *child; if (dev->of_node || !bus->dev.of_node) @@ -257,27 +258,27 @@ static void of_mdiobus_link_phydev(struct mii_bus *bus, ret = of_property_read_u32(child, "reg", &addr); if (ret < 0) { - dev_err(dev, "%s has invalid PHY address\n", + dev_err(dev, "%s has invalid MDIO address\n", child->full_name); continue; } - /* A PHY must have a reg property in the range [0-31] */ + /* A MDIO device must have a reg property in the range [0-31] */ if (addr >= PHY_MAX_ADDR) { - dev_err(dev, "%s PHY address %i is too large\n", + dev_err(dev, "%s MDIO address %i is too large\n", child->full_name, addr); continue; } - if (addr == phydev->mdio.addr) { + if (addr == mdiodev->addr) { dev->of_node = child; return; } } } #else /* !IS_ENABLED(CONFIG_OF_MDIO) */ -static inline void of_mdiobus_link_phydev(struct mii_bus *mdio, - struct phy_device *phydev) +static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio, + struct mdio_device *mdiodev) { } #endif @@ -406,7 +407,7 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) * For DT, see if the auto-probed phy has a correspoding child * in the bus node, and set the of_node pointer in this case. */ - of_mdiobus_link_phydev(bus, phydev); + of_mdiobus_link_mdiodev(bus, &phydev->mdio); err = phy_device_register(phydev); if (err) { -- cgit v1.2.3-59-g8ed1b From f89df3f381f1e1247ddccdf178f203c06fddf5ce Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:25 +0100 Subject: mdio_bus: Add comment to mdiobus_scan() and __mdiobus_register() Make it clear that mdiobus_scan () will only find devices which have a vendor/product ID in registers 2 and 3. These are typically PHY devices. Other sort of MDIO devices, such as switches, are not expected to be found during the scan. Similarly, __mdiobus_register(), which calls mdiobus_scan() will only find PHY devices, and other sorts of MDIO devices are expected to be instantiated from device tree. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 0d369ad19d17..6f9ca51446db 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -291,7 +291,9 @@ static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio, * Description: Called by a bus driver to bring up all the PHYs * on a given bus, and attach them to the bus. Drivers should use * mdiobus_register() rather than __mdiobus_register() unless they - * need to pass a specific owner module. + * need to pass a specific owner module. MDIO devices which are not + * PHYs will not be brought up by this function. They are expected to + * to be explicitly listed in DT and instantiated by of_mdiobus_register(). * * Returns 0 on success or < 0 on error. */ @@ -394,6 +396,18 @@ void mdiobus_free(struct mii_bus *bus) } EXPORT_SYMBOL(mdiobus_free); +/** + * mdiobus_scan - scan a bus for MDIO devices. + * @bus: mii_bus to scan + * @addr: address on bus to scan + * + * This function scans the MDIO bus, looking for devices which can be + * identified using a vendor/product ID in registers 2 and 3. Not all + * MDIO devices have such registers, but PHY devices typically + * do. Hence this function assumes anything found is a PHY, or can be + * treated as a PHY. Other MDIO devices, such as switches, will + * probably not be found during the scan. + */ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) { struct phy_device *phydev; -- cgit v1.2.3-59-g8ed1b From a9049e0c513c4521dbfaa302af8ed08b3366b41f Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:26 +0100 Subject: mdio: Add support for mdio drivers. Not all devices on an MDIO bus are PHYs. Meaning not all MDIO drivers are PHY drivers. Add support for generic MDIO drivers. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/Makefile | 2 +- drivers/net/phy/mdio_bus.c | 13 +++- drivers/net/phy/mdio_device.c | 169 ++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/phy_device.c | 27 ++++--- drivers/of/of_mdio.c | 33 +++++++++ include/linux/mdio.h | 50 +++++++++++++ include/linux/phy.h | 9 ++- 7 files changed, 286 insertions(+), 17 deletions(-) create mode 100644 drivers/net/phy/mdio_device.c (limited to 'drivers') diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index f31a4e25cf15..680e88f9915a 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,6 +1,6 @@ # Makefile for Linux PHY drivers -libphy-objs := phy.o phy_device.o mdio_bus.o +libphy-objs := phy.o phy_device.o mdio_bus.o mdio_device.o obj-$(CONFIG_PHYLIB) += libphy.o obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 6f9ca51446db..0573cfd2116f 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -357,16 +357,25 @@ EXPORT_SYMBOL(__mdiobus_register); void mdiobus_unregister(struct mii_bus *bus) { + struct mdio_device *mdiodev; + struct phy_device *phydev; int i; BUG_ON(bus->state != MDIOBUS_REGISTERED); bus->state = MDIOBUS_UNREGISTERED; for (i = 0; i < PHY_MAX_ADDR; i++) { - struct phy_device *phydev = mdiobus_get_phy(bus, i); - if (phydev) { + mdiodev = bus->mdio_map[i]; + if (!mdiodev) + continue; + + if (!(mdiodev->flags & MDIO_DEVICE_FLAG_PHY)) { + phydev = container_of(mdiodev, struct phy_device, mdio); phy_device_remove(phydev); phy_device_free(phydev); + } else { + mdio_device_remove(mdiodev); + mdio_device_free(mdiodev); } } device_del(&bus->dev); diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c new file mode 100644 index 000000000000..64e3777c85b4 --- /dev/null +++ b/drivers/net/phy/mdio_device.c @@ -0,0 +1,169 @@ +/* Framework for MDIO devices, other than PHYs. + * + * Copyright (c) 2016 Andrew Lunn + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void mdio_device_free(struct mdio_device *mdiodev) +{ + put_device(&mdiodev->dev); +} +EXPORT_SYMBOL(mdio_device_free); + +static void mdio_device_release(struct device *dev) +{ + kfree(to_mdio_device(dev)); +} + +struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr) +{ + struct mdio_device *mdiodev; + + /* We allocate the device, and initialize the default values */ + mdiodev = kzalloc(sizeof(*mdiodev), GFP_KERNEL); + if (!mdiodev) + return ERR_PTR(-ENOMEM); + + mdiodev->dev.release = mdio_device_release; + mdiodev->dev.parent = &bus->dev; + mdiodev->dev.bus = &mdio_bus_type; + mdiodev->bus = bus; + mdiodev->addr = addr; + + dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr); + + device_initialize(&mdiodev->dev); + + return mdiodev; +} +EXPORT_SYMBOL(mdio_device_create); + +/** + * mdio_device_register - Register the mdio device on the MDIO bus + * @mdiodev: mdio_device structure to be added to the MDIO bus + */ +int mdio_device_register(struct mdio_device *mdiodev) +{ + int err; + + dev_info(&mdiodev->dev, "mdio_device_register\n"); + + err = mdiobus_register_device(mdiodev); + if (err) + return err; + + err = device_add(&mdiodev->dev); + if (err) { + pr_err("MDIO %d failed to add\n", mdiodev->addr); + goto out; + } + + return 0; + + out: + mdiobus_unregister_device(mdiodev); + return err; +} +EXPORT_SYMBOL(mdio_device_register); + +/** + * mdio_device_remove - Remove a previously registered mdio device from the + * MDIO bus + * @mdiodev: mdio_device structure to remove + * + * This doesn't free the mdio_device itself, it merely reverses the effects + * of mdio_device_register(). Use mdio_device_free() to free the device + * after calling this function. + */ +void mdio_device_remove(struct mdio_device *mdiodev) +{ + device_del(&mdiodev->dev); + mdiobus_unregister_device(mdiodev); +} +EXPORT_SYMBOL(mdio_device_remove); + +/** + * mdio_probe - probe an MDIO device + * @dev: device to probe + * + * Description: Take care of setting up the mdio_device structure + * and calling the driver to probe the device. + */ +static int mdio_probe(struct device *dev) +{ + struct mdio_device *mdiodev = to_mdio_device(dev); + struct device_driver *drv = mdiodev->dev.driver; + struct mdio_driver *mdiodrv = to_mdio_driver(drv); + int err = 0; + + if (mdiodrv->probe) + err = mdiodrv->probe(mdiodev); + + return err; +} + +static int mdio_remove(struct device *dev) +{ + struct mdio_device *mdiodev = to_mdio_device(dev); + struct device_driver *drv = mdiodev->dev.driver; + struct mdio_driver *mdiodrv = to_mdio_driver(drv); + + if (mdiodrv->remove) + mdiodrv->remove(mdiodev); + + return 0; +} + +/** + * mdio_driver_register - register an mdio_driver with the MDIO layer + * @new_driver: new mdio_driver to register + */ +int mdio_driver_register(struct mdio_driver *drv) +{ + struct mdio_driver_common *mdiodrv = &drv->mdiodrv; + int retval; + + pr_info("mdio_driver_register: %s\n", mdiodrv->driver.name); + + mdiodrv->driver.bus = &mdio_bus_type; + mdiodrv->driver.probe = mdio_probe; + mdiodrv->driver.remove = mdio_remove; + + retval = driver_register(&mdiodrv->driver); + if (retval) { + pr_err("%s: Error %d in registering driver\n", + mdiodrv->driver.name, retval); + + return retval; + } + + return 0; +} +EXPORT_SYMBOL(mdio_driver_register); + +void mdio_driver_unregister(struct mdio_driver *drv) +{ + struct mdio_driver_common *mdiodrv = &drv->mdiodrv; + + driver_unregister(&mdiodrv->driver); +} +EXPORT_SYMBOL(mdio_driver_unregister); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 78628428ee28..af6cb6556cf9 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -264,6 +264,9 @@ static int phy_bus_match(struct device *dev, struct device_driver *drv) const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); int i; + if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY)) + return 0; + if (phydrv->match_phy_device) return phydrv->match_phy_device(phydev); @@ -851,9 +854,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, */ if (!d->driver) { if (phydev->is_c45) - d->driver = &genphy_driver[GENPHY_DRV_10G].driver; + d->driver = + &genphy_driver[GENPHY_DRV_10G].mdiodrv.driver; else - d->driver = &genphy_driver[GENPHY_DRV_1G].driver; + d->driver = + &genphy_driver[GENPHY_DRV_1G].mdiodrv.driver; err = d->driver->probe(d); if (err >= 0) @@ -954,7 +959,8 @@ void phy_detach(struct phy_device *phydev) * real driver could be loaded */ for (i = 0; i < ARRAY_SIZE(genphy_driver); i++) { - if (phydev->mdio.dev.driver == &genphy_driver[i].driver) { + if (phydev->mdio.dev.driver == + &genphy_driver[i].mdiodrv.driver) { device_release_driver(&phydev->mdio.dev); break; } @@ -1598,13 +1604,14 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner) { int retval; - new_driver->driver.name = new_driver->name; - new_driver->driver.bus = &mdio_bus_type; - new_driver->driver.probe = phy_probe; - new_driver->driver.remove = phy_remove; - new_driver->driver.owner = owner; + new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY; + new_driver->mdiodrv.driver.name = new_driver->name; + new_driver->mdiodrv.driver.bus = &mdio_bus_type; + new_driver->mdiodrv.driver.probe = phy_probe; + new_driver->mdiodrv.driver.remove = phy_remove; + new_driver->mdiodrv.driver.owner = owner; - retval = driver_register(&new_driver->driver); + retval = driver_register(&new_driver->mdiodrv.driver); if (retval) { pr_err("%s: Error %d in registering driver\n", new_driver->name, retval); @@ -1637,7 +1644,7 @@ EXPORT_SYMBOL(phy_drivers_register); void phy_driver_unregister(struct phy_driver *drv) { - driver_unregister(&drv->driver); + driver_unregister(&drv->mdiodrv.driver); } EXPORT_SYMBOL(phy_driver_unregister); diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 6febe2df76f9..c0a8f84d92db 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -92,6 +92,37 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi return 0; } +static int of_mdiobus_register_device(struct mii_bus *mdio, + struct device_node *child, + u32 addr) +{ + struct mdio_device *mdiodev; + int rc; + + mdiodev = mdio_device_create(mdio, addr); + if (!mdiodev || IS_ERR(mdiodev)) + return 1; + + /* Associate the OF node with the device structure so it + * can be looked up later. + */ + of_node_get(child); + mdiodev->dev.of_node = child; + + /* All data is now stored in the mdiodev struct; register it. */ + rc = mdio_device_register(mdiodev); + if (rc) { + mdio_device_free(mdiodev); + of_node_put(child); + return 1; + } + + dev_dbg(&mdio->dev, "registered mdio device %s at address %i\n", + child->name, addr); + + return 0; +} + int of_mdio_parse_addr(struct device *dev, const struct device_node *np) { u32 addr; @@ -179,6 +210,8 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) if (of_mdiobus_child_is_phy(child)) of_mdiobus_register_phy(mdio, child, addr); + else + of_mdiobus_register_device(mdio, child, addr); } if (!scanphys) diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 0690359e55a5..75f7fad0af4f 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -15,6 +15,7 @@ struct mii_bus; struct mdio_device { struct device dev; + const struct dev_pm_ops *pm_ops; struct mii_bus *bus; int (*bus_match)(struct device *dev, struct device_driver *drv); @@ -24,7 +25,37 @@ struct mdio_device { }; #define to_mdio_device(d) container_of(d, struct mdio_device, dev) +/* struct mdio_driver_common: Common to all MDIO drivers */ +struct mdio_driver_common { + struct device_driver driver; + int flags; +}; #define MDIO_DEVICE_FLAG_PHY 1 +#define to_mdio_common_driver(d) \ + container_of(d, struct mdio_driver_common, driver) + +/* struct mdio_driver: Generic MDIO driver */ +struct mdio_driver { + struct mdio_driver_common mdiodrv; + + /* + * Called during discovery. Used to set + * up device-specific structures, if any + */ + int (*probe)(struct mdio_device *mdiodev); + + /* Clears up any memory if needed */ + void (*remove)(struct mdio_device *mdiodev); +}; +#define to_mdio_driver(d) \ + container_of(to_mdio_common_driver(d), struct mdio_driver, mdiodrv) + +void mdio_device_free(struct mdio_device *mdiodev); +struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr); +int mdio_device_register(struct mdio_device *mdiodev); +void mdio_device_remove(struct mdio_device *mdiodev); +int mdio_driver_register(struct mdio_driver *drv); +void mdio_driver_unregister(struct mdio_driver *drv); static inline bool mdio_phy_id_is_c45(int phy_id) { @@ -197,4 +228,23 @@ int mdiobus_unregister_device(struct mdio_device *mdiodev); bool mdiobus_is_registered_device(struct mii_bus *bus, int addr); struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr); +/** + * module_mdio_driver() - Helper macro for registering mdio drivers + * + * Helper macro for MDIO drivers which do not do anything special in module + * init/exit. Each module may only use this macro once, and calling it + * replaces module_init() and module_exit(). + */ +#define mdio_module_driver(_mdio_driver) \ +static int __init mdio_module_init(void) \ +{ \ + return mdio_driver_register(&_mdio_driver); \ +} \ +module_init(mdio_module_init); \ +static void __exit mdio_module_exit(void) \ +{ \ + mdio_driver_unregister(&_mdio_driver); \ +} \ +module_exit(mdio_module_exit) + #endif /* __LINUX_MDIO_H__ */ diff --git a/include/linux/phy.h b/include/linux/phy.h index 49e4418822b3..d6f3641e7933 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -60,6 +60,7 @@ #define PHY_HAS_INTERRUPT 0x00000001 #define PHY_HAS_MAGICANEG 0x00000002 #define PHY_IS_INTERNAL 0x00000004 +#define MDIO_DEVICE_IS_PHY 0x80000000 /* Interface Mode definitions */ typedef enum { @@ -432,6 +433,7 @@ struct phy_device { /* struct phy_driver: Driver structure for a particular PHY type * + * driver_data: static driver data * phy_id: The result of reading the UID registers of this PHY * type, and ANDing them with the phy_id_mask. This driver * only works for PHYs with IDs which match this field @@ -441,7 +443,6 @@ struct phy_device { * by this PHY * flags: A bitfield defining certain other features this PHY * supports (like interrupts) - * driver_data: static driver data * * The drivers must implement config_aneg and read_status. All * other functions are optional. Note that none of these @@ -452,6 +453,7 @@ struct phy_device { * supported in the driver). */ struct phy_driver { + struct mdio_driver_common mdiodrv; u32 phy_id; char *name; unsigned int phy_id_mask; @@ -587,10 +589,9 @@ struct phy_driver { void (*get_strings)(struct phy_device *dev, u8 *data); void (*get_stats)(struct phy_device *dev, struct ethtool_stats *stats, u64 *data); - - struct device_driver driver; }; -#define to_phy_driver(d) container_of(d, struct phy_driver, driver) +#define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ + struct phy_driver, mdiodrv) #define PHY_ANY_ID "MATCH ANY PHY" #define PHY_ANY_UID 0xffffffff -- cgit v1.2.3-59-g8ed1b From 711fdba37a3dd7ee487e28767f9f0e67144cbf80 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Jan 2016 20:11:27 +0100 Subject: mdio: Abstract device_remove() and device_free() Make device_free and device_remove operations in the mdio device structure, so the core code does not need to differentiate between phy devices and generic mdio devices. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 23 +++++++++-------------- drivers/net/phy/mdio_device.c | 2 ++ drivers/net/phy/phy_device.c | 18 ++++++++++++++++++ include/linux/mdio.h | 4 ++++ 4 files changed, 33 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 0573cfd2116f..0be7b3d65f0f 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -299,6 +299,7 @@ static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio, */ int __mdiobus_register(struct mii_bus *bus, struct module *owner) { + struct mdio_device *mdiodev; int i, err; if (NULL == bus || NULL == bus->name || @@ -344,11 +345,12 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) error: while (--i >= 0) { - struct phy_device *phydev = mdiobus_get_phy(bus, i); - if (phydev) { - phy_device_remove(phydev); - phy_device_free(phydev); - } + mdiodev = bus->mdio_map[i]; + if (!mdiodev) + continue; + + mdiodev->device_remove(mdiodev); + mdiodev->device_free(mdiodev); } device_del(&bus->dev); return err; @@ -358,7 +360,6 @@ EXPORT_SYMBOL(__mdiobus_register); void mdiobus_unregister(struct mii_bus *bus) { struct mdio_device *mdiodev; - struct phy_device *phydev; int i; BUG_ON(bus->state != MDIOBUS_REGISTERED); @@ -369,14 +370,8 @@ void mdiobus_unregister(struct mii_bus *bus) if (!mdiodev) continue; - if (!(mdiodev->flags & MDIO_DEVICE_FLAG_PHY)) { - phydev = container_of(mdiodev, struct phy_device, mdio); - phy_device_remove(phydev); - phy_device_free(phydev); - } else { - mdio_device_remove(mdiodev); - mdio_device_free(mdiodev); - } + mdiodev->device_remove(mdiodev); + mdiodev->device_free(mdiodev); } device_del(&bus->dev); } diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c index 64e3777c85b4..9c88e6749b9a 100644 --- a/drivers/net/phy/mdio_device.c +++ b/drivers/net/phy/mdio_device.c @@ -46,6 +46,8 @@ struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr) mdiodev->dev.release = mdio_device_release; mdiodev->dev.parent = &bus->dev; mdiodev->dev.bus = &mdio_bus_type; + mdiodev->device_free = mdio_device_free; + mdiodev->device_remove = mdio_device_remove; mdiodev->bus = bus; mdiodev->addr = addr; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index af6cb6556cf9..319300627c0b 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -47,11 +47,27 @@ void phy_device_free(struct phy_device *phydev) } EXPORT_SYMBOL(phy_device_free); +static void phy_mdio_device_free(struct mdio_device *mdiodev) +{ + struct phy_device *phydev; + + phydev = container_of(mdiodev, struct phy_device, mdio); + phy_device_free(phydev); +} + static void phy_device_release(struct device *dev) { kfree(to_phy_device(dev)); } +static void phy_mdio_device_remove(struct mdio_device *mdiodev) +{ + struct phy_device *phydev; + + phydev = container_of(mdiodev, struct phy_device, mdio); + phy_device_remove(phydev); +} + enum genphy_driver { GENPHY_DRV_1G, GENPHY_DRV_10G, @@ -308,6 +324,8 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, mdiodev->bus_match = phy_bus_match; mdiodev->addr = addr; mdiodev->flags = MDIO_DEVICE_FLAG_PHY; + mdiodev->device_free = phy_mdio_device_free; + mdiodev->device_remove = phy_mdio_device_remove; dev->speed = 0; dev->duplex = -1; diff --git a/include/linux/mdio.h b/include/linux/mdio.h index 75f7fad0af4f..5bfd99d1a40a 100644 --- a/include/linux/mdio.h +++ b/include/linux/mdio.h @@ -18,7 +18,11 @@ struct mdio_device { const struct dev_pm_ops *pm_ops; struct mii_bus *bus; + int (*bus_match)(struct device *dev, struct device_driver *drv); + void (*device_free)(struct mdio_device *mdiodev); + void (*device_remove)(struct mdio_device *mdiodev); + /* Bus address of the MDIO device (0-31) */ int addr; int flags; -- cgit v1.2.3-59-g8ed1b From ce721a70219720f4c574a7e97f912c71a3510369 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 5 Jan 2016 14:39:16 +0100 Subject: net: ethernet: cadence-macb: Add disabled usrio caps On some platforms, the macb integration does not use the USRIO register to configure the (R)MII port and clocks. When the register is not implemented and the MACB error signal is connected to the bus error, reading or writing to the USRIO register can trigger some Imprecise External Aborts on ARM platforms. Signed-off-by: Neil Armstrong Acked-by: Nicolas Ferre Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb.c | 27 +++++++++++++++------------ drivers/net/ethernet/cadence/macb.h | 1 + 2 files changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index eb1397484eef..8e2b9b6e50e7 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -2113,7 +2113,8 @@ static void macb_get_regs(struct net_device *dev, struct ethtool_regs *regs, regs_buff[10] = macb_tx_dma(&bp->queues[0], tail); regs_buff[11] = macb_tx_dma(&bp->queues[0], head); - regs_buff[12] = macb_or_gem_readl(bp, USRIO); + if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) + regs_buff[12] = macb_or_gem_readl(bp, USRIO); if (macb_is_gem(bp)) { regs_buff[13] = gem_readl(bp, DMACFG); } @@ -2392,19 +2393,21 @@ static int macb_init(struct platform_device *pdev) dev->hw_features &= ~NETIF_F_SG; dev->features = dev->hw_features; - val = 0; - if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII) - val = GEM_BIT(RGMII); - else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII && - (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII)) - val = MACB_BIT(RMII); - else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII)) - val = MACB_BIT(MII); + if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) { + val = 0; + if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII) + val = GEM_BIT(RGMII); + else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII && + (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII)) + val = MACB_BIT(RMII); + else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII)) + val = MACB_BIT(MII); - if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN) - val |= MACB_BIT(CLKEN); + if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN) + val |= MACB_BIT(CLKEN); - macb_or_gem_writel(bp, USRIO, val); + macb_or_gem_writel(bp, USRIO, val); + } /* Set MII management clock divider */ val = macb_mdc_clk_div(bp); diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 5c03e811224d..0d4ecfcd60b7 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -400,6 +400,7 @@ #define MACB_CAPS_USRIO_HAS_CLKEN 0x00000002 #define MACB_CAPS_USRIO_DEFAULT_IS_MII 0x00000004 #define MACB_CAPS_NO_GIGABIT_HALF 0x00000008 +#define MACB_CAPS_USRIO_DISABLED 0x00000010 #define MACB_CAPS_FIFO_MODE 0x10000000 #define MACB_CAPS_GIGABIT_MODE_AVAILABLE 0x20000000 #define MACB_CAPS_SG_DISABLED 0x40000000 -- cgit v1.2.3-59-g8ed1b From e611b5b851e60b028807644fb12911bba0eb601c Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 5 Jan 2016 14:39:17 +0100 Subject: net: macb: Add NP4 macb config using USRIO_DISABLED Declare a new NP4 SoC variant having USRIO_DISABLED as capability bit. Signed-off-by: Neil Armstrong Acked-by: Nicolas Ferre Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 8e2b9b6e50e7..c56347536f6b 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -2770,6 +2770,11 @@ static const struct macb_config emac_config = { .init = at91ether_init, }; +static const struct macb_config np4_config = { + .caps = MACB_CAPS_USRIO_DISABLED, + .clk_init = macb_clk_init, + .init = macb_init, +}; static const struct macb_config zynqmp_config = { .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO, @@ -2790,6 +2795,7 @@ static const struct of_device_id macb_dt_ids[] = { { .compatible = "cdns,at32ap7000-macb" }, { .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config }, { .compatible = "cdns,macb" }, + { .compatible = "cdns,np4-macb", .data = &np4_config }, { .compatible = "cdns,pc302-gem", .data = &pc302gem_config }, { .compatible = "cdns,gem", .data = &pc302gem_config }, { .compatible = "atmel,sama5d2-gem", .data = &sama5d2_config }, -- cgit v1.2.3-59-g8ed1b From 9fb6066d0de0353622ee572e2b0e979bd62bb657 Mon Sep 17 00:00:00 2001 From: "Woojung.Huh@microchip.com" Date: Tue, 5 Jan 2016 17:29:59 +0000 Subject: net: lan78xx: Fix to write to OTP(One Time Programmable) per magic number. This patch fixes a bug writing to EEPROM in lan78xx_ethtool_set_eeprom() when asked to write to OTP. Signed-off-by: Woojung Huh Signed-off-by: David S. Miller --- drivers/net/usb/lan78xx.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 226668ead0d8..d54f536bab03 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -603,6 +603,59 @@ static int lan78xx_read_raw_otp(struct lan78xx_net *dev, u32 offset, return 0; } +static int lan78xx_write_raw_otp(struct lan78xx_net *dev, u32 offset, + u32 length, u8 *data) +{ + int i; + int ret; + u32 buf; + unsigned long timeout; + + ret = lan78xx_read_reg(dev, OTP_PWR_DN, &buf); + + if (buf & OTP_PWR_DN_PWRDN_N_) { + /* clear it and wait to be cleared */ + ret = lan78xx_write_reg(dev, OTP_PWR_DN, 0); + + timeout = jiffies + HZ; + do { + udelay(1); + ret = lan78xx_read_reg(dev, OTP_PWR_DN, &buf); + if (time_after(jiffies, timeout)) { + netdev_warn(dev->net, + "timeout on OTP_PWR_DN completion"); + return -EIO; + } + } while (buf & OTP_PWR_DN_PWRDN_N_); + } + + /* set to BYTE program mode */ + ret = lan78xx_write_reg(dev, OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_); + + for (i = 0; i < length; i++) { + ret = lan78xx_write_reg(dev, OTP_ADDR1, + ((offset + i) >> 8) & OTP_ADDR1_15_11); + ret = lan78xx_write_reg(dev, OTP_ADDR2, + ((offset + i) & OTP_ADDR2_10_3)); + ret = lan78xx_write_reg(dev, OTP_PRGM_DATA, data[i]); + ret = lan78xx_write_reg(dev, OTP_TST_CMD, OTP_TST_CMD_PRGVRFY_); + ret = lan78xx_write_reg(dev, OTP_CMD_GO, OTP_CMD_GO_GO_); + + timeout = jiffies + HZ; + do { + udelay(1); + ret = lan78xx_read_reg(dev, OTP_STATUS, &buf); + if (time_after(jiffies, timeout)) { + netdev_warn(dev->net, + "Timeout on OTP_STATUS completion"); + return -EIO; + } + } while (buf & OTP_STATUS_BUSY_); + } + + return 0; +} + static int lan78xx_read_otp(struct lan78xx_net *dev, u32 offset, u32 length, u8 *data) { @@ -969,7 +1022,7 @@ static int lan78xx_ethtool_set_eeprom(struct net_device *netdev, (ee->offset == 0) && (ee->len == 512) && (data[0] == OTP_INDICATOR_1)) - return lan78xx_write_raw_eeprom(dev, ee->offset, ee->len, data); + return lan78xx_write_raw_otp(dev, ee->offset, ee->len, data); return -EINVAL; } -- cgit v1.2.3-59-g8ed1b From 3021ad9a4f009265e6063e617fb91306980af16c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 5 Jan 2016 11:05:45 +0100 Subject: brcmfmac: Add wowl net detect support With wowl net detect it becomes possible to scan for specific ssids and wakeup once found. 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: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 142 +++++++++++++++++++-- .../broadcom/brcm80211/brcmfmac/cfg80211.h | 27 +++- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 2 + 3 files changed, 155 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 6a7759fcbd86..fd54ad141965 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -95,6 +95,8 @@ #define BRCMF_SCAN_UNASSOC_TIME 40 #define BRCMF_SCAN_PASSIVE_TIME 120 +#define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000) + #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) @@ -236,6 +238,17 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; +/* Function prototype forward declarations */ +static int +brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_sched_scan_request *request); +static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *ndev); +static s32 +brcmf_notify_sched_scan_results(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data); + static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, struct cfg80211_chan_def *ch) @@ -3116,26 +3129,71 @@ static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], return ret; } +static s32 +brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e, + void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_pno_scanresults_le *pfn_result; + struct brcmf_pno_net_info_le *netinfo; + + brcmf_dbg(SCAN, "Enter\n"); + + pfn_result = (struct brcmf_pno_scanresults_le *)data; + + if (e->event_code == BRCMF_E_PFN_NET_LOST) { + brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n"); + return 0; + } + + if (le32_to_cpu(pfn_result->count) < 1) { + brcmf_err("Invalid result count, expected 1 (%d)\n", + le32_to_cpu(pfn_result->count)); + return -EINVAL; + } + + data += sizeof(struct brcmf_pno_scanresults_le); + netinfo = (struct brcmf_pno_net_info_le *)data; + memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len); + cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len; + cfg->wowl.nd->n_channels = 1; + cfg->wowl.nd->channels[0] = + ieee80211_channel_to_frequency(netinfo->channel, + netinfo->channel <= CH_MAX_2G_CHANNEL ? + NL80211_BAND_2GHZ : NL80211_BAND_5GHZ); + cfg->wowl.nd_info->n_matches = 1; + cfg->wowl.nd_info->matches[0] = cfg->wowl.nd; + + /* Inform (the resume task) that the net detect information was recvd */ + cfg->wowl.nd_data_completed = true; + wake_up(&cfg->wowl.nd_data_wait); + + return 0; +} + #ifdef CONFIG_PM static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) { + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_wowl_wakeind_le wake_ind_le; struct cfg80211_wowlan_wakeup wakeup_data; struct cfg80211_wowlan_wakeup *wakeup; u32 wakeind; s32 err; + int timeout; err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le, sizeof(wake_ind_le)); - if (!err) { + if (err) { brcmf_err("Get wowl_wakeind failed, err = %d\n", err); return; } wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind); if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | - BRCMF_WOWL_RETR | BRCMF_WOWL_NET)) { + BRCMF_WOWL_RETR | BRCMF_WOWL_NET | + BRCMF_WOWL_PFN_FOUND)) { wakeup = &wakeup_data; memset(&wakeup_data, 0, sizeof(wakeup_data)); wakeup_data.pattern_idx = -1; @@ -3163,6 +3221,16 @@ static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) */ wakeup_data.pattern_idx = 0; } + if (wakeind & BRCMF_WOWL_PFN_FOUND) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n"); + timeout = wait_event_timeout(cfg->wowl.nd_data_wait, + cfg->wowl.nd_data_completed, + BRCMF_ND_INFO_TIMEOUT); + if (!timeout) + brcmf_err("No result for wowl net detect\n"); + else + wakeup_data.net_detect = cfg->wowl.nd_info; + } } else { wakeup = NULL; } @@ -3185,14 +3253,21 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) brcmf_dbg(TRACE, "Enter\n"); - if (cfg->wowl_enabled) { + if (cfg->wowl.active) { brcmf_report_wowl_wakeind(wiphy, ifp); brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); brcmf_configure_arp_offload(ifp, true); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, - cfg->pre_wowl_pmmode); - cfg->wowl_enabled = false; + cfg->wowl.pre_pmmode); + cfg->wowl.active = false; + if (cfg->wowl.nd_enabled) { + brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev); + brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND); + brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, + brcmf_notify_sched_scan_results); + cfg->wowl.nd_enabled = false; + } } return 0; } @@ -3207,7 +3282,7 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, brcmf_dbg(TRACE, "Suspend, wowl config.\n"); brcmf_configure_arp_offload(ifp, false); - brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode); + brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); wowl_config = 0; @@ -3225,11 +3300,26 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, wowl->patterns[i].pkt_offset); } } + if (wowl->nd_config) { + brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev, + wowl->nd_config); + wowl_config |= BRCMF_WOWL_PFN_FOUND; + + cfg->wowl.nd_data_completed = false; + cfg->wowl.nd_enabled = true; + /* Now reroute the event for PFN to the wowl function. */ + brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND); + brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, + brcmf_wowl_nd_results); + } + if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) + wowl_config |= BRCMF_WOWL_UNASSOC; + brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear", strlen("clear")); brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); brcmf_bus_wowl_config(cfg->pub->bus_if, true); - cfg->wowl_enabled = true; + cfg->wowl.active = true; } static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, @@ -3248,6 +3338,10 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, if (!check_vif_up(ifp->vif)) goto exit; + /* Stop scheduled scan */ + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) + brcmf_cfg80211_sched_scan_stop(wiphy, ndev); + /* end any scanning */ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_abort_scanning(cfg); @@ -5329,6 +5423,10 @@ static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) cfg->escan_ioctl_buf = NULL; kfree(cfg->extra_buf); cfg->extra_buf = NULL; + kfree(cfg->wowl.nd); + cfg->wowl.nd = NULL; + kfree(cfg->wowl.nd_info); + cfg->wowl.nd_info = NULL; } static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) @@ -5342,6 +5440,14 @@ static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); if (!cfg->extra_buf) goto init_priv_mem_out; + cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL); + if (!cfg->wowl.nd) + goto init_priv_mem_out; + cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) + + sizeof(struct cfg80211_wowlan_nd_match *), + GFP_KERNEL); + if (!cfg->wowl.nd_info) + goto init_priv_mem_out; return 0; @@ -6018,7 +6124,7 @@ static void brcmf_wiphy_pno_params(struct wiphy *wiphy) } #ifdef CONFIG_PM -static const struct wiphy_wowlan_support brcmf_wowlan_support = { +static struct wiphy_wowlan_support brcmf_wowlan_support = { .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, .n_patterns = BRCMF_WOWL_MAXPATTERNS, .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE, @@ -6027,10 +6133,23 @@ static const struct wiphy_wowlan_support brcmf_wowlan_support = { }; #endif -static void brcmf_wiphy_wowl_params(struct wiphy *wiphy) +static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp) { #ifdef CONFIG_PM - /* wowl settings */ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + s32 err; + u32 wowl_cap; + + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { + err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap); + if (!err) { + if (wowl_cap & BRCMF_WOWL_PFN_FOUND) { + brcmf_wowlan_support.flags |= + WIPHY_WOWLAN_NET_DETECT; + init_waitqueue_head(&cfg->wowl.nd_data_wait); + } + } + } wiphy->wowlan = &brcmf_wowlan_support; #endif } @@ -6091,8 +6210,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) - brcmf_wiphy_wowl_params(wiphy); - + brcmf_wiphy_wowl_params(wiphy, ifp); err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, sizeof(bandlist)); if (err) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index c17b6d584fe0..69af708b43f5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -226,6 +226,27 @@ struct brcmf_cfg80211_vif_event { struct brcmf_cfg80211_vif *vif; }; +/** + * struct brcmf_cfg80211_wowl - wowl related information. + * + * @active: set on suspend, cleared on resume. + * @pre_pmmode: firmware PM mode at entering suspend. + * @nd: net dectect data. + * @nd_info: helper struct to pass to cfg80211. + * @nd_data_wait: wait queue to sync net detect data. + * @nd_data_completed: completion for net detect data. + * @nd_enabled: net detect enabled. + */ +struct brcmf_cfg80211_wowl { + bool active; + u32 pre_pmmode; + struct cfg80211_wowlan_nd_match *nd; + struct cfg80211_wowlan_nd_info *nd_info; + wait_queue_head_t nd_data_wait; + bool nd_data_completed; + bool nd_enabled; +}; + /** * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface * @@ -259,8 +280,7 @@ struct brcmf_cfg80211_vif_event { * @vif_list: linked list of vif instances. * @vif_cnt: number of vif instances. * @vif_event: vif event signalling. - * @wowl_enabled; set during suspend, is wowl used. - * @pre_wowl_pmmode: intermediate storage of pm mode during wowl. + * @wowl: wowl related information. */ struct brcmf_cfg80211_info { struct wiphy *wiphy; @@ -292,9 +312,8 @@ struct brcmf_cfg80211_info { struct brcmf_cfg80211_vif_event vif_event; struct completion vif_disabled; struct brcmu_d11inf d11inf; - bool wowl_enabled; - u32 pre_wowl_pmmode; struct brcmf_assoclist_le assoclist; + struct brcmf_cfg80211_wowl wowl; }; /** diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index bf2df49d7098..1afc2ad83b6c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -110,6 +110,8 @@ #define BRCMF_WOWL_UNASSOC (1 << 24) /* Wakeup if received matched secured pattern: */ #define BRCMF_WOWL_SECURE (1 << 25) +/* Wakeup on finding preferred network */ +#define BRCMF_WOWL_PFN_FOUND (1 << 26) /* Link Down indication in WoWL mode: */ #define BRCMF_WOWL_LINKDOWN (1 << 31) -- cgit v1.2.3-59-g8ed1b From 5419f7f17d5b70a9b65ec849da69bb5f7253b863 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 5 Jan 2016 11:05:46 +0100 Subject: brcmfmac: Reshuffle functions to avoid forward declarations Function prototype forward declarations are to be avoided. This patch shuffles some of the functions so the forward declarations can be removed. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 1090 ++++++++++---------- 1 file changed, 537 insertions(+), 553 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index fd54ad141965..c9657be3d4c3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -238,18 +238,6 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; -/* Function prototype forward declarations */ -static int -brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, - struct net_device *ndev, - struct cfg80211_sched_scan_request *request); -static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, - struct net_device *ndev); -static s32 -brcmf_notify_sched_scan_results(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data); - - static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, struct cfg80211_chan_def *ch) { @@ -3081,183 +3069,473 @@ static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) brcmf_cfg80211_escan_timeout_worker); } -static __always_inline void brcmf_delay(u32 ms) -{ - if (ms < 1000 / HZ) { - cond_resched(); - mdelay(ms); - } else { - msleep(ms); - } -} - -static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], - u8 *pattern, u32 patternsize, u8 *mask, - u32 packet_offset) -{ - struct brcmf_fil_wowl_pattern_le *filter; - u32 masksize; - u32 patternoffset; - u8 *buf; - u32 bufsize; - s32 ret; - - masksize = (patternsize + 7) / 8; - patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize; - - bufsize = sizeof(*filter) + patternsize + masksize; - buf = kzalloc(bufsize, GFP_KERNEL); - if (!buf) - return -ENOMEM; - filter = (struct brcmf_fil_wowl_pattern_le *)buf; - - memcpy(filter->cmd, cmd, 4); - filter->masksize = cpu_to_le32(masksize); - filter->offset = cpu_to_le32(packet_offset); - filter->patternoffset = cpu_to_le32(patternoffset); - filter->patternsize = cpu_to_le32(patternsize); - filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP); - - if ((mask) && (masksize)) - memcpy(buf + sizeof(*filter), mask, masksize); - if ((pattern) && (patternsize)) - memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize); - - ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize); - - kfree(buf); - return ret; -} - +/* PFN result doesn't have all the info which are required by the supplicant + * (For e.g IEs) Do a target Escan so that sched scan results are reported + * via wl_inform_single_bss in the required format. Escan does require the + * scan request in the form of cfg80211_scan_request. For timebeing, create + * cfg80211_scan_request one out of the received PNO event. + */ static s32 -brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e, - void *data) +brcmf_notify_sched_scan_results(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) { struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_pno_net_info_le *netinfo, *netinfo_start; + struct cfg80211_scan_request *request = NULL; + struct cfg80211_ssid *ssid = NULL; + struct ieee80211_channel *channel = NULL; + struct wiphy *wiphy = cfg_to_wiphy(cfg); + int err = 0; + int channel_req = 0; + int band = 0; struct brcmf_pno_scanresults_le *pfn_result; - struct brcmf_pno_net_info_le *netinfo; + u32 result_count; + u32 status; brcmf_dbg(SCAN, "Enter\n"); - pfn_result = (struct brcmf_pno_scanresults_le *)data; - if (e->event_code == BRCMF_E_PFN_NET_LOST) { - brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n"); + brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n"); return 0; } - if (le32_to_cpu(pfn_result->count) < 1) { - brcmf_err("Invalid result count, expected 1 (%d)\n", - le32_to_cpu(pfn_result->count)); - return -EINVAL; - } + pfn_result = (struct brcmf_pno_scanresults_le *)data; + result_count = le32_to_cpu(pfn_result->count); + status = le32_to_cpu(pfn_result->status); - data += sizeof(struct brcmf_pno_scanresults_le); - netinfo = (struct brcmf_pno_net_info_le *)data; - memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len); - cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len; - cfg->wowl.nd->n_channels = 1; - cfg->wowl.nd->channels[0] = - ieee80211_channel_to_frequency(netinfo->channel, - netinfo->channel <= CH_MAX_2G_CHANNEL ? - NL80211_BAND_2GHZ : NL80211_BAND_5GHZ); - cfg->wowl.nd_info->n_matches = 1; - cfg->wowl.nd_info->matches[0] = cfg->wowl.nd; + /* PFN event is limited to fit 512 bytes so we may get + * multiple NET_FOUND events. For now place a warning here. + */ + WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); + brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count); + if (result_count > 0) { + int i; - /* Inform (the resume task) that the net detect information was recvd */ - cfg->wowl.nd_data_completed = true; - wake_up(&cfg->wowl.nd_data_wait); + request = kzalloc(sizeof(*request), GFP_KERNEL); + ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL); + channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL); + if (!request || !ssid || !channel) { + err = -ENOMEM; + goto out_err; + } - return 0; -} + request->wiphy = wiphy; + data += sizeof(struct brcmf_pno_scanresults_le); + netinfo_start = (struct brcmf_pno_net_info_le *)data; -#ifdef CONFIG_PM + for (i = 0; i < result_count; i++) { + netinfo = &netinfo_start[i]; + if (!netinfo) { + brcmf_err("Invalid netinfo ptr. index: %d\n", + i); + err = -EINVAL; + goto out_err; + } -static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_wowl_wakeind_le wake_ind_le; - struct cfg80211_wowlan_wakeup wakeup_data; - struct cfg80211_wowlan_wakeup *wakeup; - u32 wakeind; - s32 err; - int timeout; + brcmf_dbg(SCAN, "SSID:%s Channel:%d\n", + netinfo->SSID, netinfo->channel); + memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); + ssid[i].ssid_len = netinfo->SSID_len; + request->n_ssids++; - err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le, - sizeof(wake_ind_le)); - if (err) { - brcmf_err("Get wowl_wakeind failed, err = %d\n", err); - return; - } + channel_req = netinfo->channel; + if (channel_req <= CH_MAX_2G_CHANNEL) + band = NL80211_BAND_2GHZ; + else + band = NL80211_BAND_5GHZ; + channel[i].center_freq = + ieee80211_channel_to_frequency(channel_req, + band); + channel[i].band = band; + channel[i].flags |= IEEE80211_CHAN_NO_HT40; + request->channels[i] = &channel[i]; + request->n_channels++; + } - wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind); - if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | - BRCMF_WOWL_RETR | BRCMF_WOWL_NET | - BRCMF_WOWL_PFN_FOUND)) { - wakeup = &wakeup_data; - memset(&wakeup_data, 0, sizeof(wakeup_data)); - wakeup_data.pattern_idx = -1; + /* assign parsed ssid array */ + if (request->n_ssids) + request->ssids = &ssid[0]; - if (wakeind & BRCMF_WOWL_MAGIC) { - brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n"); - wakeup_data.magic_pkt = true; - } - if (wakeind & BRCMF_WOWL_DIS) { - brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n"); - wakeup_data.disconnect = true; - } - if (wakeind & BRCMF_WOWL_BCN) { - brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n"); - wakeup_data.disconnect = true; - } - if (wakeind & BRCMF_WOWL_RETR) { - brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n"); - wakeup_data.disconnect = true; - } - if (wakeind & BRCMF_WOWL_NET) { - brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n"); - /* For now always map to pattern 0, no API to get - * correct information available at the moment. - */ - wakeup_data.pattern_idx = 0; + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + /* Abort any on-going scan */ + brcmf_abort_scanning(cfg); } - if (wakeind & BRCMF_WOWL_PFN_FOUND) { - brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n"); - timeout = wait_event_timeout(cfg->wowl.nd_data_wait, - cfg->wowl.nd_data_completed, - BRCMF_ND_INFO_TIMEOUT); - if (!timeout) - brcmf_err("No result for wowl net detect\n"); - else - wakeup_data.net_detect = cfg->wowl.nd_info; + + 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); + goto out_err; } + cfg->sched_escan = true; + cfg->scan_request = request; } else { - wakeup = NULL; + brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); + goto out_err; } - cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL); -} -#else + kfree(ssid); + kfree(channel); + kfree(request); + return 0; -static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) -{ +out_err: + kfree(ssid); + kfree(channel); + kfree(request); + cfg80211_sched_scan_stopped(wiphy); + return err; } -#endif /* CONFIG_PM */ - -static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) +static int brcmf_dev_pno_clean(struct net_device *ndev) { - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(ndev); + int ret; - brcmf_dbg(TRACE, "Enter\n"); + /* Disable pfn */ + ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0); + if (ret == 0) { + /* clear pfn */ + ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear", + NULL, 0); + } + if (ret < 0) + brcmf_err("failed code %d\n", ret); - if (cfg->wowl.active) { - brcmf_report_wowl_wakeind(wiphy, ifp); - brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); - brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); - brcmf_configure_arp_offload(ifp, true); + return ret; +} + +static int brcmf_dev_pno_config(struct brcmf_if *ifp, + struct cfg80211_sched_scan_request *request) +{ + struct brcmf_pno_param_le pfn_param; + struct brcmf_pno_macaddr_le pfn_mac; + s32 err; + u8 *mac_mask; + int i; + + memset(&pfn_param, 0, sizeof(pfn_param)); + pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); + + /* set extra pno params */ + pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat = BRCMF_PNO_REPEAT; + pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; + + /* set up pno scan fr */ + pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); + + err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, + sizeof(pfn_param)); + if (err) { + brcmf_err("pfn_set failed, err=%d\n", err); + return err; + } + + /* Find out if mac randomization should be turned on */ + if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) + return 0; + + pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; + pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; + + memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); + mac_mask = request->mac_addr_mask; + for (i = 0; i < ETH_ALEN; i++) { + pfn_mac.mac[i] &= mac_mask[i]; + pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); + } + /* Clear multi bit */ + pfn_mac.mac[0] &= 0xFE; + /* Set locally administered */ + pfn_mac.mac[0] |= 0x02; + + err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, + sizeof(pfn_mac)); + if (err) + brcmf_err("pfn_macaddr failed, err=%d\n", err); + + return err; +} + +static int +brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_sched_scan_request *request) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_pno_net_param_le pfn; + int i; + int ret = 0; + + brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", + request->n_match_sets, request->n_ssids); + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); + return -EAGAIN; + } + if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { + brcmf_err("Scanning suppressed: status (%lu)\n", + cfg->scan_status); + return -EAGAIN; + } + + if (!request->n_ssids || !request->n_match_sets) { + brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", + request->n_ssids); + return -EINVAL; + } + + if (request->n_ssids > 0) { + for (i = 0; i < request->n_ssids; i++) { + /* Active scan req for ssids */ + brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n", + request->ssids[i].ssid); + + /* match_set ssids is a supert set of n_ssid list, + * so we need not add these set separately. + */ + } + } + + if (request->n_match_sets > 0) { + /* clean up everything */ + ret = brcmf_dev_pno_clean(ndev); + if (ret < 0) { + brcmf_err("failed error=%d\n", ret); + return ret; + } + + /* configure pno */ + if (brcmf_dev_pno_config(ifp, request)) + return -EINVAL; + + /* configure each match set */ + for (i = 0; i < request->n_match_sets; i++) { + struct cfg80211_ssid *ssid; + u32 ssid_len; + + ssid = &request->match_sets[i].ssid; + ssid_len = ssid->ssid_len; + + if (!ssid_len) { + brcmf_err("skip broadcast ssid\n"); + continue; + } + pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); + pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); + pfn.wsec = cpu_to_le32(0); + pfn.infra = cpu_to_le32(1); + pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); + pfn.ssid.SSID_len = cpu_to_le32(ssid_len); + memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len); + ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, + sizeof(pfn)); + brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", + ret == 0 ? "set" : "failed", ssid->ssid); + } + /* Enable the PNO */ + if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) { + brcmf_err("PNO enable failed!! ret=%d\n", ret); + return -EINVAL; + } + } else { + return -EINVAL; + } + + return 0; +} + +static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *ndev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + + brcmf_dbg(SCAN, "enter\n"); + brcmf_dev_pno_clean(ndev); + if (cfg->sched_escan) + brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true); + return 0; +} + +static __always_inline void brcmf_delay(u32 ms) +{ + if (ms < 1000 / HZ) { + cond_resched(); + mdelay(ms); + } else { + msleep(ms); + } +} + +static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], + u8 *pattern, u32 patternsize, u8 *mask, + u32 packet_offset) +{ + struct brcmf_fil_wowl_pattern_le *filter; + u32 masksize; + u32 patternoffset; + u8 *buf; + u32 bufsize; + s32 ret; + + masksize = (patternsize + 7) / 8; + patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize; + + bufsize = sizeof(*filter) + patternsize + masksize; + buf = kzalloc(bufsize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + filter = (struct brcmf_fil_wowl_pattern_le *)buf; + + memcpy(filter->cmd, cmd, 4); + filter->masksize = cpu_to_le32(masksize); + filter->offset = cpu_to_le32(packet_offset); + filter->patternoffset = cpu_to_le32(patternoffset); + filter->patternsize = cpu_to_le32(patternsize); + filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP); + + if ((mask) && (masksize)) + memcpy(buf + sizeof(*filter), mask, masksize); + if ((pattern) && (patternsize)) + memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize); + + ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize); + + kfree(buf); + return ret; +} + +static s32 +brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e, + void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_pno_scanresults_le *pfn_result; + struct brcmf_pno_net_info_le *netinfo; + + brcmf_dbg(SCAN, "Enter\n"); + + pfn_result = (struct brcmf_pno_scanresults_le *)data; + + if (e->event_code == BRCMF_E_PFN_NET_LOST) { + brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n"); + return 0; + } + + if (le32_to_cpu(pfn_result->count) < 1) { + brcmf_err("Invalid result count, expected 1 (%d)\n", + le32_to_cpu(pfn_result->count)); + return -EINVAL; + } + + data += sizeof(struct brcmf_pno_scanresults_le); + netinfo = (struct brcmf_pno_net_info_le *)data; + memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len); + cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len; + cfg->wowl.nd->n_channels = 1; + cfg->wowl.nd->channels[0] = + ieee80211_channel_to_frequency(netinfo->channel, + netinfo->channel <= CH_MAX_2G_CHANNEL ? + NL80211_BAND_2GHZ : NL80211_BAND_5GHZ); + cfg->wowl.nd_info->n_matches = 1; + cfg->wowl.nd_info->matches[0] = cfg->wowl.nd; + + /* Inform (the resume task) that the net detect information was recvd */ + cfg->wowl.nd_data_completed = true; + wake_up(&cfg->wowl.nd_data_wait); + + return 0; +} + +#ifdef CONFIG_PM + +static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_wowl_wakeind_le wake_ind_le; + struct cfg80211_wowlan_wakeup wakeup_data; + struct cfg80211_wowlan_wakeup *wakeup; + u32 wakeind; + s32 err; + int timeout; + + err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le, + sizeof(wake_ind_le)); + if (err) { + brcmf_err("Get wowl_wakeind failed, err = %d\n", err); + return; + } + + wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind); + if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | + BRCMF_WOWL_RETR | BRCMF_WOWL_NET | + BRCMF_WOWL_PFN_FOUND)) { + wakeup = &wakeup_data; + memset(&wakeup_data, 0, sizeof(wakeup_data)); + wakeup_data.pattern_idx = -1; + + if (wakeind & BRCMF_WOWL_MAGIC) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n"); + wakeup_data.magic_pkt = true; + } + if (wakeind & BRCMF_WOWL_DIS) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n"); + wakeup_data.disconnect = true; + } + if (wakeind & BRCMF_WOWL_BCN) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n"); + wakeup_data.disconnect = true; + } + if (wakeind & BRCMF_WOWL_RETR) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n"); + wakeup_data.disconnect = true; + } + if (wakeind & BRCMF_WOWL_NET) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n"); + /* For now always map to pattern 0, no API to get + * correct information available at the moment. + */ + wakeup_data.pattern_idx = 0; + } + if (wakeind & BRCMF_WOWL_PFN_FOUND) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n"); + timeout = wait_event_timeout(cfg->wowl.nd_data_wait, + cfg->wowl.nd_data_completed, + BRCMF_ND_INFO_TIMEOUT); + if (!timeout) + brcmf_err("No result for wowl net detect\n"); + else + wakeup_data.net_detect = cfg->wowl.nd_info; + } + } else { + wakeup = NULL; + } + cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL); +} + +#else + +static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) +{ +} + +#endif /* CONFIG_PM */ + +static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter\n"); + + if (cfg->wowl.active) { + brcmf_report_wowl_wakeind(wiphy, ifp); + brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); + brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); + brcmf_configure_arp_offload(ifp, true); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, cfg->wowl.pre_pmmode); cfg->wowl.active = false; @@ -3358,440 +3636,146 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED); /* Make sure WPA_Supplicant receives all the event * generated due to DISASSOC call to the fw to keep - * the state fw and WPA_Supplicant state consistent - */ - brcmf_delay(500); - } - /* Configure MPC */ - brcmf_set_mpc(ifp, 1); - - } else { - /* Configure WOWL paramaters */ - brcmf_configure_wowl(cfg, ifp, wowl); - } - -exit: - brcmf_dbg(TRACE, "Exit\n"); - /* clear any scanning activity */ - cfg->scan_status = 0; - return 0; -} - -static __used s32 -brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp) -{ - struct brcmf_pmk_list_le *pmk_list; - int i; - u32 npmk; - s32 err; - - pmk_list = &cfg->pmk_list; - npmk = le32_to_cpu(pmk_list->npmk); - - brcmf_dbg(CONN, "No of elements %d\n", npmk); - for (i = 0; i < npmk; i++) - brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid); - - err = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list, - sizeof(*pmk_list)); - - return err; -} - -static s32 -brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_pmksa *pmksa) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0]; - s32 err; - u32 npmk, i; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - npmk = le32_to_cpu(cfg->pmk_list.npmk); - for (i = 0; i < npmk; i++) - if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN)) - break; - if (i < BRCMF_MAXPMKID) { - memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN); - memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); - if (i == npmk) { - npmk++; - cfg->pmk_list.npmk = cpu_to_le32(npmk); - } - } else { - brcmf_err("Too many PMKSA entries cached %d\n", npmk); - return -EINVAL; - } - - brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid); - for (i = 0; i < WLAN_PMKID_LEN; i += 4) - brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i], - pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2], - pmk[npmk].pmkid[i + 3]); - - err = brcmf_update_pmklist(cfg, ifp); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_pmksa *pmksa) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0]; - s32 err; - u32 npmk, i; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", &pmksa->bssid); - - npmk = le32_to_cpu(cfg->pmk_list.npmk); - for (i = 0; i < npmk; i++) - if (!memcmp(&pmksa->bssid, &pmk[i].bssid, ETH_ALEN)) - break; - - if ((npmk > 0) && (i < npmk)) { - for (; i < (npmk - 1); i++) { - memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN); - memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid, - WLAN_PMKID_LEN); - } - memset(&pmk[i], 0, sizeof(*pmk)); - cfg->pmk_list.npmk = cpu_to_le32(npmk - 1); - } else { - brcmf_err("Cache entry not found\n"); - return -EINVAL; - } - - err = brcmf_update_pmklist(cfg, ifp); - - brcmf_dbg(TRACE, "Exit\n"); - return err; - -} - -static s32 -brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list)); - err = brcmf_update_pmklist(cfg, ifp); - - brcmf_dbg(TRACE, "Exit\n"); - return err; - -} - -/* - * PFN result doesn't have all the info which are - * required by the supplicant - * (For e.g IEs) Do a target Escan so that sched scan results are reported - * via wl_inform_single_bss in the required format. Escan does require the - * scan request in the form of cfg80211_scan_request. For timebeing, create - * cfg80211_scan_request one out of the received PNO event. - */ -static s32 -brcmf_notify_sched_scan_results(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct brcmf_pno_net_info_le *netinfo, *netinfo_start; - struct cfg80211_scan_request *request = NULL; - struct cfg80211_ssid *ssid = NULL; - struct ieee80211_channel *channel = NULL; - struct wiphy *wiphy = cfg_to_wiphy(cfg); - int err = 0; - int channel_req = 0; - int band = 0; - struct brcmf_pno_scanresults_le *pfn_result; - u32 result_count; - u32 status; - - brcmf_dbg(SCAN, "Enter\n"); - - if (e->event_code == BRCMF_E_PFN_NET_LOST) { - brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n"); - return 0; - } - - pfn_result = (struct brcmf_pno_scanresults_le *)data; - result_count = le32_to_cpu(pfn_result->count); - status = le32_to_cpu(pfn_result->status); - - /* - * PFN event is limited to fit 512 bytes so we may get - * multiple NET_FOUND events. For now place a warning here. - */ - WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); - brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count); - if (result_count > 0) { - int i; - - request = kzalloc(sizeof(*request), GFP_KERNEL); - ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL); - channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL); - if (!request || !ssid || !channel) { - err = -ENOMEM; - goto out_err; - } - - request->wiphy = wiphy; - data += sizeof(struct brcmf_pno_scanresults_le); - netinfo_start = (struct brcmf_pno_net_info_le *)data; - - for (i = 0; i < result_count; i++) { - netinfo = &netinfo_start[i]; - if (!netinfo) { - brcmf_err("Invalid netinfo ptr. index: %d\n", - i); - err = -EINVAL; - goto out_err; - } - - brcmf_dbg(SCAN, "SSID:%s Channel:%d\n", - netinfo->SSID, netinfo->channel); - memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); - ssid[i].ssid_len = netinfo->SSID_len; - request->n_ssids++; - - channel_req = netinfo->channel; - if (channel_req <= CH_MAX_2G_CHANNEL) - band = NL80211_BAND_2GHZ; - else - band = NL80211_BAND_5GHZ; - channel[i].center_freq = - ieee80211_channel_to_frequency(channel_req, - band); - channel[i].band = band; - channel[i].flags |= IEEE80211_CHAN_NO_HT40; - request->channels[i] = &channel[i]; - request->n_channels++; - } - - /* assign parsed ssid array */ - if (request->n_ssids) - request->ssids = &ssid[0]; - - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - /* Abort any on-going scan */ - brcmf_abort_scanning(cfg); + * the state fw and WPA_Supplicant state consistent + */ + brcmf_delay(500); } + /* Configure MPC */ + brcmf_set_mpc(ifp, 1); - 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); - goto out_err; - } - cfg->sched_escan = true; - cfg->scan_request = request; } else { - brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); - goto out_err; + /* Configure WOWL paramaters */ + brcmf_configure_wowl(cfg, ifp, wowl); } - kfree(ssid); - kfree(channel); - kfree(request); +exit: + brcmf_dbg(TRACE, "Exit\n"); + /* clear any scanning activity */ + cfg->scan_status = 0; return 0; - -out_err: - kfree(ssid); - kfree(channel); - kfree(request); - cfg80211_sched_scan_stopped(wiphy); - return err; } -static int brcmf_dev_pno_clean(struct net_device *ndev) -{ - int ret; - - /* Disable pfn */ - ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0); - if (ret == 0) { - /* clear pfn */ - ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear", - NULL, 0); - } - if (ret < 0) - brcmf_err("failed code %d\n", ret); - - return ret; -} - -static int brcmf_dev_pno_config(struct brcmf_if *ifp, - struct cfg80211_sched_scan_request *request) +static __used s32 +brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp) { - struct brcmf_pno_param_le pfn_param; - struct brcmf_pno_macaddr_le pfn_mac; - s32 err; - u8 *mac_mask; + struct brcmf_pmk_list_le *pmk_list; int i; + u32 npmk; + s32 err; - memset(&pfn_param, 0, sizeof(pfn_param)); - pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); - - /* set extra pno params */ - pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); - pfn_param.repeat = BRCMF_PNO_REPEAT; - pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; - - /* set up pno scan fr */ - pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); - - err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, - sizeof(pfn_param)); - if (err) { - brcmf_err("pfn_set failed, err=%d\n", err); - return err; - } - - /* Find out if mac randomization should be turned on */ - if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) - return 0; - - pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; - pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; + pmk_list = &cfg->pmk_list; + npmk = le32_to_cpu(pmk_list->npmk); - memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); - mac_mask = request->mac_addr_mask; - for (i = 0; i < ETH_ALEN; i++) { - pfn_mac.mac[i] &= mac_mask[i]; - pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); - } - /* Clear multi bit */ - pfn_mac.mac[0] &= 0xFE; - /* Set locally administered */ - pfn_mac.mac[0] |= 0x02; + brcmf_dbg(CONN, "No of elements %d\n", npmk); + for (i = 0; i < npmk; i++) + brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid); - err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, - sizeof(pfn_mac)); - if (err) - brcmf_err("pfn_macaddr failed, err=%d\n", err); + err = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list, + sizeof(*pmk_list)); return err; } -static int -brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, - struct net_device *ndev, - struct cfg80211_sched_scan_request *request) +static s32 +brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_pmksa *pmksa) { + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct brcmf_pno_net_param_le pfn; - int i; - int ret = 0; + struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0]; + s32 err; + u32 npmk, i; - brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", - request->n_match_sets, request->n_ssids); - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { - brcmf_err("Scanning suppressed: status (%lu)\n", - cfg->scan_status); - return -EAGAIN; - } + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; - if (!request->n_ssids || !request->n_match_sets) { - brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", - request->n_ssids); + npmk = le32_to_cpu(cfg->pmk_list.npmk); + for (i = 0; i < npmk; i++) + if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN)) + break; + if (i < BRCMF_MAXPMKID) { + memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN); + memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); + if (i == npmk) { + npmk++; + cfg->pmk_list.npmk = cpu_to_le32(npmk); + } + } else { + brcmf_err("Too many PMKSA entries cached %d\n", npmk); return -EINVAL; } - if (request->n_ssids > 0) { - for (i = 0; i < request->n_ssids; i++) { - /* Active scan req for ssids */ - brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n", - request->ssids[i].ssid); + brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid); + for (i = 0; i < WLAN_PMKID_LEN; i += 4) + brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i], + pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2], + pmk[npmk].pmkid[i + 3]); - /* - * match_set ssids is a supert set of n_ssid list, - * so we need not add these set seperately. - */ - } - } + err = brcmf_update_pmklist(cfg, ifp); - if (request->n_match_sets > 0) { - /* clean up everything */ - ret = brcmf_dev_pno_clean(ndev); - if (ret < 0) { - brcmf_err("failed error=%d\n", ret); - return ret; - } + brcmf_dbg(TRACE, "Exit\n"); + return err; +} - /* configure pno */ - if (brcmf_dev_pno_config(ifp, request)) - return -EINVAL; +static s32 +brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_pmksa *pmksa) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0]; + s32 err; + u32 npmk, i; - /* configure each match set */ - for (i = 0; i < request->n_match_sets; i++) { - struct cfg80211_ssid *ssid; - u32 ssid_len; + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; - ssid = &request->match_sets[i].ssid; - ssid_len = ssid->ssid_len; + brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", &pmksa->bssid); - if (!ssid_len) { - brcmf_err("skip broadcast ssid\n"); - continue; - } - pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); - pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); - pfn.wsec = cpu_to_le32(0); - pfn.infra = cpu_to_le32(1); - pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); - pfn.ssid.SSID_len = cpu_to_le32(ssid_len); - memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len); - ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, - sizeof(pfn)); - brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", - ret == 0 ? "set" : "failed", ssid->ssid); - } - /* Enable the PNO */ - if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) { - brcmf_err("PNO enable failed!! ret=%d\n", ret); - return -EINVAL; + npmk = le32_to_cpu(cfg->pmk_list.npmk); + for (i = 0; i < npmk; i++) + if (!memcmp(&pmksa->bssid, &pmk[i].bssid, ETH_ALEN)) + break; + + if ((npmk > 0) && (i < npmk)) { + for (; i < (npmk - 1); i++) { + memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN); + memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid, + WLAN_PMKID_LEN); } + memset(&pmk[i], 0, sizeof(*pmk)); + cfg->pmk_list.npmk = cpu_to_le32(npmk - 1); } else { + brcmf_err("Cache entry not found\n"); return -EINVAL; } - return 0; + err = brcmf_update_pmklist(cfg, ifp); + + brcmf_dbg(TRACE, "Exit\n"); + return err; + } -static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, - struct net_device *ndev) +static s32 +brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list)); + err = brcmf_update_pmklist(cfg, ifp); + + brcmf_dbg(TRACE, "Exit\n"); + return err; - brcmf_dbg(SCAN, "enter\n"); - brcmf_dev_pno_clean(ndev); - if (cfg->sched_escan) - brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true); - return 0; } static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp) -- cgit v1.2.3-59-g8ed1b From 4011fc49969019eb5e20824285831d873e885f77 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 5 Jan 2016 11:05:47 +0100 Subject: brcmfmac: change brcmf_sdio_wd_timer() prototype The function brcmf_sdio_wd_timer() has wdtick parameter. However, it is only called with two values and as such the parameter is replaced with boolean value indicating the timer should be active or not. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/sdio.c | 33 ++++++++-------------- .../wireless/broadcom/brcm80211/brcmfmac/sdio.h | 2 +- 2 files changed, 13 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index ceb2a754308e..2f020201945c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -503,8 +503,7 @@ struct brcmf_sdio { struct timer_list timer; struct completion watchdog_wait; struct task_struct *watchdog_tsk; - bool wd_timer_valid; - uint save_ms; + bool wd_active; struct workqueue_struct *brcmf_wq; struct work_struct datawork; @@ -961,7 +960,7 @@ end: brcmf_sdio_clkctl(bus, CLK_NONE, pendok); } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_wd_timer(bus, true); } bus->sleeping = sleep; brcmf_dbg(SDIO, "new state %s\n", @@ -3576,7 +3575,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) if (bus->idlecount > bus->idletime) { brcmf_dbg(SDIO, "idle\n"); sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_wd_timer(bus, 0); + brcmf_sdio_wd_timer(bus, false); bus->idlecount = 0; brcmf_sdio_bus_sleep(bus, true, false); sdio_release_host(bus->sdiodev->func[1]); @@ -3908,7 +3907,7 @@ brcmf_sdio_watchdog(unsigned long data) if (bus->watchdog_tsk) { complete(&bus->watchdog_wait); /* Reschedule the watchdog */ - if (bus->wd_timer_valid) + if (bus->wd_active) mod_timer(&bus->timer, jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); } @@ -3950,7 +3949,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, /* Start the watchdog timer */ bus->sdcnt.tickcnt = 0; - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_wd_timer(bus, true); sdio_claim_host(sdiodev->func[1]); @@ -4195,7 +4194,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) if (bus->ci) { if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) { sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_wd_timer(bus, 0); + brcmf_sdio_wd_timer(bus, false); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Leave the device in state where it is * 'passive'. This is done by resetting all @@ -4217,13 +4216,12 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Disconnected\n"); } -void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active) { /* Totally stop the timer */ - if (!wdtick && bus->wd_timer_valid) { + if (!active && bus->wd_active) { del_timer_sync(&bus->timer); - bus->wd_timer_valid = false; - bus->save_ms = wdtick; + bus->wd_active = false; return; } @@ -4231,27 +4229,20 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) if (bus->sdiodev->state != BRCMF_SDIOD_DATA) return; - if (wdtick) { - if (bus->save_ms != BRCMF_WD_POLL_MS) { - if (bus->wd_timer_valid) - /* Stop timer and restart at new value */ - del_timer_sync(&bus->timer); - + if (active) { + if (!bus->wd_active) { /* Create timer again when watchdog period is dynamically changed or in the first instance */ bus->timer.expires = jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS); add_timer(&bus->timer); - + bus->wd_active = true; } else { /* Re arm the timer, at last watchdog period */ mod_timer(&bus->timer, jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); } - - bus->wd_timer_valid = true; - bus->save_ms = wdtick; } } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index d86ecf26f31a..ff47ceedb1f4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -369,7 +369,7 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); void brcmf_sdio_remove(struct brcmf_sdio *bus); void brcmf_sdio_isr(struct brcmf_sdio *bus); -void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active); void brcmf_sdio_wowl_config(struct device *dev, bool enabled); int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep); void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus); -- cgit v1.2.3-59-g8ed1b From 63ce3d5db09324b56b739bfde5704799b0d6fd89 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 5 Jan 2016 11:05:48 +0100 Subject: brcmfmac: use msecs_to_jiffies() in macro definitions Instead to having macro definition for millisecond timeout have the definition directly in jiffies. This makes the unit of the value immediately clear and may result in code that is bit more compact. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 2 +- .../wireless/broadcom/brcm80211/brcmfmac/cfg80211.h | 2 ++ .../net/wireless/broadcom/brcm80211/brcmfmac/core.c | 4 ++-- .../net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 4 ++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 18 +++++++++--------- .../net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 7 +++---- .../net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 18 ++++++++---------- .../net/wireless/broadcom/brcm80211/brcmfmac/sdio.h | 4 ++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 5 ++--- 9 files changed, 31 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index c9657be3d4c3..84788ea9e39e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -568,7 +568,7 @@ struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, /* wait for firmware event */ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, - msecs_to_jiffies(1500)); + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(cfg, NULL); if (!err) { brcmf_err("timeout occurred\n"); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 69af708b43f5..40efb539ac26 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -75,6 +75,8 @@ #define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON 2 #define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF 4 +#define BRCMF_VIF_EVENT_TIMEOUT msecs_to_jiffies(1500) + /** * enum brcmf_scan_status - scan engine status * diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 7c75b1acdf00..ed9998b69709 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -40,7 +40,7 @@ MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); MODULE_LICENSE("Dual BSD/GPL"); -#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */ +#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(50) /* AMPDU rx reordering definitions */ #define BRCMF_RXREORDER_FLOWID_OFFSET 0 @@ -1282,7 +1282,7 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp) err = wait_event_timeout(ifp->pend_8021x_wait, !brcmf_get_pend_8021x_cnt(ifp), - msecs_to_jiffies(MAX_WAIT_FOR_8021X_TX)); + MAX_WAIT_FOR_8021X_TX); WARN_ON(!err); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 5df91386e13a..c2bdb91746cf 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -34,7 +34,7 @@ #include "tracepoint.h" -#define MSGBUF_IOCTL_RESP_TIMEOUT 2000 +#define MSGBUF_IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) #define MSGBUF_TYPE_GEN_STATUS 0x1 #define MSGBUF_TYPE_RING_STATUS 0x2 @@ -466,7 +466,7 @@ static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf) { return wait_event_timeout(msgbuf->ioctl_resp_wait, msgbuf->ctl_completed, - msecs_to_jiffies(MSGBUF_IOCTL_RESP_TIMEOUT)); + MSGBUF_IOCTL_RESP_TIMEOUT); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index b23dcbcd505e..821b6494f9d1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -71,10 +71,10 @@ #define P2P_AF_MED_DWELL_TIME 400 #define P2P_AF_LONG_DWELL_TIME 1000 #define P2P_AF_TX_MAX_RETRY 1 -#define P2P_AF_MAX_WAIT_TIME 2000 +#define P2P_AF_MAX_WAIT_TIME msecs_to_jiffies(2000) #define P2P_INVALID_CHANNEL -1 #define P2P_CHANNEL_SYNC_RETRY 5 -#define P2P_AF_FRM_SCAN_MAX_WAIT 1500 +#define P2P_AF_FRM_SCAN_MAX_WAIT msecs_to_jiffies(1500) #define P2P_DEFAULT_SLEEP_TIME_VSDB 200 /* WiFi P2P Public Action Frame OUI Subtypes */ @@ -102,6 +102,7 @@ #define P2PSD_ACTION_ID_GAS_CREQ 0x0c /* GAS Comback Request AF */ #define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comback Response AF */ +#define BRCMF_P2P_DISABLE_TIMEOUT msecs_to_jiffies(500) /** * struct brcmf_p2p_disc_st_le - set discovery state in firmware. * @@ -1514,7 +1515,7 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, p2p->af_tx_sent_jiffies = jiffies; timeout = wait_for_completion_timeout(&p2p->send_af_done, - msecs_to_jiffies(P2P_AF_MAX_WAIT_TIME)); + P2P_AF_MAX_WAIT_TIME); if (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status)) { brcmf_dbg(TRACE, "TX action frame operation is success\n"); @@ -1988,7 +1989,7 @@ int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, return err; } err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_CHANGE, - msecs_to_jiffies(1500)); + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(cfg, NULL); if (!err) { brcmf_err("No BRCMF_E_IF_CHANGE event received\n"); @@ -2090,7 +2091,7 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, /* wait for firmware event */ err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD, - msecs_to_jiffies(1500)); + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); brcmf_fweh_p2pdev_setup(pri_ifp, false); if (!err) { @@ -2180,7 +2181,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, /* wait for firmware event */ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, - msecs_to_jiffies(1500)); + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(cfg, NULL); if (!err) { brcmf_err("timeout occurred\n"); @@ -2230,7 +2231,6 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); struct brcmf_p2p_info *p2p = &cfg->p2p; struct brcmf_cfg80211_vif *vif; - unsigned long jiffie_timeout = msecs_to_jiffies(1500); bool wait_for_disable = false; int err; @@ -2263,7 +2263,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) if (wait_for_disable) wait_for_completion_timeout(&cfg->vif_disabled, - msecs_to_jiffies(500)); + BRCMF_P2P_DISABLE_TIMEOUT); err = 0; if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) { @@ -2273,7 +2273,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) if (!err) { /* wait for firmware event */ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL, - jiffie_timeout); + BRCMF_VIF_EVENT_TIMEOUT); if (!err) err = -EIO; else diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 3d2d790d3ad6..0480b70e3eb8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -191,7 +191,7 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008 #define BRCMF_H2D_HOST_D0_INFORM 0x00000010 -#define BRCMF_PCIE_MBDATA_TIMEOUT 2000 +#define BRCMF_PCIE_MBDATA_TIMEOUT msecs_to_jiffies(2000) #define BRCMF_PCIE_CFGREG_STATUS_CMD 0x4 #define BRCMF_PCIE_CFGREG_PM_CSR 0x4C @@ -1885,9 +1885,8 @@ static int brcmf_pcie_pm_enter_D3(struct device *dev) devinfo->mbdata_completed = false; brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D3_INFORM); - wait_event_timeout(devinfo->mbdata_resp_wait, - devinfo->mbdata_completed, - msecs_to_jiffies(BRCMF_PCIE_MBDATA_TIMEOUT)); + wait_event_timeout(devinfo->mbdata_resp_wait, devinfo->mbdata_completed, + BRCMF_PCIE_MBDATA_TIMEOUT); if (!devinfo->mbdata_completed) { brcmf_err("Timeout on response for entering D3 substate\n"); return -EIO; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 2f020201945c..dd6614332836 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -45,8 +45,8 @@ #include "chip.h" #include "firmware.h" -#define DCMD_RESP_TIMEOUT 2000 /* In milli second */ -#define CTL_DONE_TIMEOUT 2000 /* In milli second */ +#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2000) +#define CTL_DONE_TIMEOUT msecs_to_jiffies(2000) #ifdef DEBUG @@ -1657,7 +1657,7 @@ static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, bool *pending) { DECLARE_WAITQUEUE(wait, current); - int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT); + int timeout = DCMD_RESP_TIMEOUT; /* Wait until control frame is available */ add_wait_queue(&bus->dcmd_resp_wait, &wait); @@ -2842,7 +2842,7 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) brcmf_sdio_trigger_dpc(bus); wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, - msecs_to_jiffies(CTL_DONE_TIMEOUT)); + CTL_DONE_TIMEOUT); ret = 0; if (bus->ctrl_frame_stat) { sdio_claim_host(bus->sdiodev->func[1]); @@ -3552,7 +3552,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) /* Poll for console output periodically */ if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() && bus->console_interval != 0) { - bus->console.count += BRCMF_WD_POLL_MS; + bus->console.count += jiffies_to_msecs(BRCMF_WD_POLL); if (bus->console.count >= bus->console_interval) { bus->console.count -= bus->console_interval; sdio_claim_host(bus->sdiodev->func[1]); @@ -3909,7 +3909,7 @@ brcmf_sdio_watchdog(unsigned long data) /* Reschedule the watchdog */ if (bus->wd_active) mod_timer(&bus->timer, - jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); + jiffies + BRCMF_WD_POLL); } } @@ -4234,14 +4234,12 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active) /* Create timer again when watchdog period is dynamically changed or in the first instance */ - bus->timer.expires = - jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS); + bus->timer.expires = jiffies + BRCMF_WD_POLL; add_timer(&bus->timer); bus->wd_active = true; } else { /* Re arm the timer, at last watchdog period */ - mod_timer(&bus->timer, - jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); + mod_timer(&bus->timer, jiffies + BRCMF_WD_POLL); } } } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index ff47ceedb1f4..5ec7a6d87672 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -152,8 +152,8 @@ /* Packet alignment for most efficient SDIO (can change based on platform) */ #define BRCMF_SDALIGN (1 << 6) -/* watchdog polling interval in ms */ -#define BRCMF_WD_POLL_MS 10 +/* watchdog polling interval */ +#define BRCMF_WD_POLL msecs_to_jiffies(10) /** * enum brcmf_sdiod_state - the state of the bus. diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 66c26a92b29c..c72b7b352a77 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -29,7 +29,7 @@ #include "usb.h" -#define IOCTL_RESP_TIMEOUT 2000 +#define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) #define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */ #define BRCMF_USB_RESET_GETVER_LOOP_CNT 10 @@ -190,8 +190,7 @@ static struct brcmf_usbdev_info *brcmf_usb_get_businfo(struct device *dev) static int brcmf_usb_ioctl_resp_wait(struct brcmf_usbdev_info *devinfo) { return wait_event_timeout(devinfo->ioctl_resp_wait, - devinfo->ctl_completed, - msecs_to_jiffies(IOCTL_RESP_TIMEOUT)); + devinfo->ctl_completed, IOCTL_RESP_TIMEOUT); } static void brcmf_usb_ioctl_resp_wake(struct brcmf_usbdev_info *devinfo) -- cgit v1.2.3-59-g8ed1b From 5ce96c0808ac8ad82f70b156ceb77ec2008c82e3 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 5 Jan 2016 11:05:49 +0100 Subject: brcmfmac: use jiffies for timeout in btcoex The btcoex uses a timeout which was in milliseconds and got converted to jiffies upon using timer api. Instead, convert it to jiffies and treat it as such further. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c index 4e33f96b3dd1..14a70d4b4e86 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c @@ -29,7 +29,7 @@ #include "cfg80211.h" /* T1 start SCO/eSCO priority suppression */ -#define BRCMF_BTCOEX_OPPR_WIN_TIME 2000 +#define BRCMF_BTCOEX_OPPR_WIN_TIME msecs_to_jiffies(2000) /* BT registers values during DHCP */ #define BRCMF_BT_DHCP_REG50 0x8022 @@ -314,8 +314,7 @@ static void brcmf_btcoex_handler(struct work_struct *work) } else { btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME; mod_timer(&btci->timer, - jiffies + - msecs_to_jiffies(BRCMF_BTCOEX_OPPR_WIN_TIME)); + jiffies + BRCMF_BTCOEX_OPPR_WIN_TIME); } btci->timer_on = true; break; @@ -328,12 +327,11 @@ static void brcmf_btcoex_handler(struct work_struct *work) /* DHCP is not over yet, start lowering BT priority */ brcmf_dbg(INFO, "DHCP T1:%d expired\n", - BRCMF_BTCOEX_OPPR_WIN_TIME); + jiffies_to_msecs(BRCMF_BTCOEX_OPPR_WIN_TIME)); brcmf_btcoex_boost_wifi(btci, true); btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT; - mod_timer(&btci->timer, - jiffies + msecs_to_jiffies(btci->timeout)); + mod_timer(&btci->timer, jiffies + btci->timeout); btci->timer_on = true; break; @@ -477,7 +475,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, return -EBUSY; /* Start BT timer only for SCO connection */ if (brcmf_btcoex_is_sco_active(ifp)) { - btci->timeout = duration; + btci->timeout = msecs_to_jiffies(duration); btci->vif = vif; brcmf_btcoex_dhcp_start(btci); } -- cgit v1.2.3-59-g8ed1b From 42e0ed0d454c6ad7be67f2c18828391ecfdc9a62 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 5 Jan 2016 11:05:50 +0100 Subject: brcmfmac: Do not handle link downs for ibss. Sometimes on module reload and reconnect to ibss a deauth from other station can be received. This is treated as a link down but for ibss this is wrong. It will close the interface and no data is possible. Ignore the firmware generated link down events in ibss mode, as ibss is always teared down by cfg80211. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 84788ea9e39e..7b01e4ddb315 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1454,6 +1454,7 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) } brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING); + brcmf_net_setcarrier(ifp, false); brcmf_dbg(TRACE, "Exit\n"); @@ -5248,12 +5249,13 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, brcmf_dbg(CONN, "Linkdown\n"); if (!brcmf_is_ibssmode(ifp->vif)) { brcmf_bss_connect_done(cfg, ndev, e, false); + brcmf_link_down(ifp->vif, + brcmf_map_fw_linkdown_reason(e)); + brcmf_init_prof(ndev_to_prof(ndev)); + if (ndev != cfg_to_ndev(cfg)) + complete(&cfg->vif_disabled); + brcmf_net_setcarrier(ifp, false); } - brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e)); - brcmf_init_prof(ndev_to_prof(ndev)); - if (ndev != cfg_to_ndev(cfg)) - complete(&cfg->vif_disabled); - brcmf_net_setcarrier(ifp, false); } else if (brcmf_is_nonetwork(cfg, e)) { if (brcmf_is_ibssmode(ifp->vif)) clear_bit(BRCMF_VIF_STATUS_CONNECTING, -- cgit v1.2.3-59-g8ed1b From 2d166c304065c57925278a69309f15a6a65eff97 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Tue, 22 Dec 2015 15:34:42 -0800 Subject: i40e: change log messages and error returns When VFs are being reset, there is a brief window of time when they cannot be configured because they don't have a VSI to configure. If a script is quick, it can fall through that window. To avoid defenestration, log a useful error message and return -EAGAIN. Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 27 ++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index aa58a498c239..b8deb50bb9f4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2078,9 +2078,9 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) vf = &(pf->vf[vf_id]); vsi = pf->vsi[vf->lan_vsi_idx]; if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { - dev_err(&pf->pdev->dev, - "Uninitialized VF %d\n", vf_id); - ret = -EINVAL; + dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", + vf_id); + ret = -EAGAIN; goto error_param; } @@ -2162,8 +2162,9 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, vf = &(pf->vf[vf_id]); vsi = pf->vsi[vf->lan_vsi_idx]; if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { - dev_err(&pf->pdev->dev, "Uninitialized VF %d\n", vf_id); - ret = -EINVAL; + dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", + vf_id); + ret = -EAGAIN; goto error_pvid; } @@ -2282,8 +2283,9 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, vf = &(pf->vf[vf_id]); vsi = pf->vsi[vf->lan_vsi_idx]; if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { - dev_err(&pf->pdev->dev, "Uninitialized VF %d.\n", vf_id); - ret = -EINVAL; + dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", + vf_id); + ret = -EAGAIN; goto error; } @@ -2356,8 +2358,9 @@ int i40e_ndo_get_vf_config(struct net_device *netdev, /* first vsi is always the LAN vsi */ vsi = pf->vsi[vf->lan_vsi_idx]; if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { - dev_err(&pf->pdev->dev, "Uninitialized VF %d\n", vf_id); - ret = -EINVAL; + dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", + vf_id); + ret = -EAGAIN; goto error_param; } @@ -2472,6 +2475,12 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable) } vf = &(pf->vf[vf_id]); + if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { + dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", + vf_id); + ret = -EAGAIN; + goto out; + } if (enable == vf->spoofchk) goto out; -- cgit v1.2.3-59-g8ed1b From efd8e39acc51cafe7dbc656ba63eea9034238ee7 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Tue, 22 Dec 2015 15:34:43 -0800 Subject: i40e: allow zero MAC address for VFs Allow the user to specify a zero MAC address for VFs. This removes the existing MAC address and allows the VF to use a random address. Libvirt does this normally when removing a VF from a VM. Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index b8deb50bb9f4..63e62f9aec6e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2084,9 +2084,9 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) goto error_param; } - if (!is_valid_ether_addr(mac)) { + if (is_multicast_ether_addr(mac)) { dev_err(&pf->pdev->dev, - "Invalid VF ethernet address\n"); + "Invalid Ethernet address %pM for VF %d\n", mac, vf_id); ret = -EINVAL; goto error_param; } @@ -2097,9 +2097,10 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) spin_lock_bh(&vsi->mac_filter_list_lock); /* delete the temporary mac address */ - i40e_del_filter(vsi, vf->default_lan_addr.addr, - vf->port_vlan_id ? vf->port_vlan_id : -1, - true, false); + if (!is_zero_ether_addr(vf->default_lan_addr.addr)) + i40e_del_filter(vsi, vf->default_lan_addr.addr, + vf->port_vlan_id ? vf->port_vlan_id : -1, + true, false); /* Delete all the filters for this VSI - we're going to kill it * anyway. -- cgit v1.2.3-59-g8ed1b From b499ffb0a22c6cd3762b44fd6489f7384f40437d Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Mon, 7 Dec 2015 15:06:34 -0500 Subject: i40e: Look up MAC address in Open Firmware or IDPROM This is the i40e equivalent of commit c762dff24c06 ("ixgbe: Look up MAC address in Open Firmware or IDPROM"). As with that fix, attempt to look up the MAC address in Open Firmware on systems that support it, and use IDPROM on SPARC if no OF address is found. In the case of the i40e there is an assumption that the default mac address has already been set up as the primary mac filter on probe, so if this filter is obtained from the Open Firmware or IDPROM, an explicit write is needed via i40e_aq_mac_address_write() and i40e_aq_add_macvlan() invocation. The I40E_FLAG_PF_MAC flag in the platform-private i40e_pf structure tracks whether a platform-specific mac address was found, in which case calls to i40e_aq_mac_address_write() and i40e_aq_add_macvlan() will be triggered. Reviewed-by: Martin K. Petersen Signed-off-by: Sowmini Varadhan Acked-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_main.c | 90 +++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index c202f9b9386a..68f2204ec6f3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -339,6 +339,7 @@ struct i40e_pf { #define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40) #define I40E_FLAG_GENEVE_OFFLOAD_CAPABLE BIT_ULL(41) #define I40E_FLAG_NO_PCI_LINK_CHECK BIT_ULL(42) +#define I40E_FLAG_PF_MAC BIT_ULL(50) /* tracks features that get auto disabled by errors */ u64 auto_disable_flags; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1598fb31477a..291480650d11 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -24,6 +24,15 @@ * ******************************************************************************/ +#include +#include +#include + +#ifdef CONFIG_SPARC +#include +#include +#endif + /* Local includes */ #include "i40e.h" #include "i40e_diag.h" @@ -9521,6 +9530,44 @@ err_vsi: return NULL; } +/** + * i40e_macaddr_init - explicitly write the mac address filters. + * + * @vsi: pointer to the vsi. + * @macaddr: the MAC address + * + * This is needed when the macaddr has been obtained by other + * means than the default, e.g., from Open Firmware or IDPROM. + * Returns 0 on success, negative on failure + **/ +static int i40e_macaddr_init(struct i40e_vsi *vsi, u8 *macaddr) +{ + int ret; + struct i40e_aqc_add_macvlan_element_data element; + + ret = i40e_aq_mac_address_write(&vsi->back->hw, + I40E_AQC_WRITE_TYPE_LAA_WOL, + macaddr, NULL); + if (ret) { + dev_info(&vsi->back->pdev->dev, + "Addr change for VSI failed: %d\n", ret); + return -EADDRNOTAVAIL; + } + + memset(&element, 0, sizeof(element)); + ether_addr_copy(element.mac_addr, macaddr); + element.flags = cpu_to_le16(I40E_AQC_MACVLAN_ADD_PERFECT_MATCH); + ret = i40e_aq_add_macvlan(&vsi->back->hw, vsi->seid, &element, 1, NULL); + if (ret) { + dev_info(&vsi->back->pdev->dev, + "add filter failed err %s aq_err %s\n", + i40e_stat_str(&vsi->back->hw, ret), + i40e_aq_str(&vsi->back->hw, + vsi->back->hw.aq.asq_last_status)); + } + return ret; +} + /** * i40e_vsi_setup - Set up a VSI by a given type * @pf: board private structure @@ -9645,6 +9692,17 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, switch (vsi->type) { /* setup the netdev if needed */ case I40E_VSI_MAIN: + /* Apply relevant filters if a platform-specific mac + * address was selected. + */ + if (!!(pf->flags & I40E_FLAG_PF_MAC)) { + ret = i40e_macaddr_init(vsi, pf->hw.mac.addr); + if (ret) { + dev_warn(&pf->pdev->dev, + "could not set up macaddr; err %d\n", + ret); + } + } case I40E_VSI_VMDQ2: case I40E_VSI_FCOE: ret = i40e_config_netdev(vsi); @@ -10473,6 +10531,36 @@ static void i40e_print_features(struct i40e_pf *pf) WARN_ON(i > INFO_STRING_LEN); } +/** + * i40e_get_platform_mac_addr - get platform-specific MAC address + * + * @pdev: PCI device information struct + * @pf: board private structure + * + * Look up the MAC address in Open Firmware on systems that support it, + * and use IDPROM on SPARC if no OF address is found. On return, the + * I40E_FLAG_PF_MAC will be wset in pf->flags if a platform-specific value + * has been selected. + **/ +static void i40e_get_platform_mac_addr(struct pci_dev *pdev, struct i40e_pf *pf) +{ + struct device_node *dp = pci_device_to_OF_node(pdev); + const unsigned char *addr; + u8 *mac_addr = pf->hw.mac.addr; + + pf->flags &= ~I40E_FLAG_PF_MAC; + addr = of_get_mac_address(dp); + if (addr) { + ether_addr_copy(mac_addr, addr); + pf->flags |= I40E_FLAG_PF_MAC; +#ifdef CONFIG_SPARC + } else { + ether_addr_copy(mac_addr, idprom->id_ethaddr); + pf->flags |= I40E_FLAG_PF_MAC; +#endif /* CONFIG_SPARC */ + } +} + /** * i40e_probe - Device initialization routine * @pdev: PCI device information struct @@ -10683,6 +10771,8 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } i40e_get_mac_addr(hw, hw->mac.addr); + /* allow a platform config to override the HW addr */ + i40e_get_platform_mac_addr(pdev, pf); if (!is_valid_ether_addr(hw->mac.addr)) { dev_info(&pdev->dev, "invalid MAC address %pM\n", hw->mac.addr); err = -EIO; -- cgit v1.2.3-59-g8ed1b From 857942fd1aa15edf7356a4a4bad5369c8e70a633 Mon Sep 17 00:00:00 2001 From: Anjali Singhai Jain Date: Wed, 9 Dec 2015 15:50:21 -0800 Subject: i40e: Fix Rx hash reported to the stack by our driver If the driver calls skb_set_hash even with a zero hash, that indicates to the stack that the hash calculation is offloaded in hardware. So the Stack doesn't do a SW hash which is required for load balancing if the user decides to turn of rx-hashing on our device. This patch fixes the path so that we do not call skb_set_hash if the feature is disabled. Change-ID: Ic4debfa4ff91b5a72e447348a75768ed7a2d3e1b Signed-off-by: Anjali Singhai Jain Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 54 ++++++++++++++------------- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 54 ++++++++++++++------------- 2 files changed, 58 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index e9e9a37ee274..720516b0e8ee 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1422,31 +1422,12 @@ checksum_fail: } /** - * i40e_rx_hash - returns the hash value from the Rx descriptor - * @ring: descriptor ring - * @rx_desc: specific descriptor - **/ -static inline u32 i40e_rx_hash(struct i40e_ring *ring, - union i40e_rx_desc *rx_desc) -{ - const __le64 rss_mask = - cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH << - I40E_RX_DESC_STATUS_FLTSTAT_SHIFT); - - if ((ring->netdev->features & NETIF_F_RXHASH) && - (rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask) - return le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss); - else - return 0; -} - -/** - * i40e_ptype_to_hash - get a hash type + * i40e_ptype_to_htype - get a hash type * @ptype: the ptype value from the descriptor * * Returns a hash type to be used by skb_set_hash **/ -static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype) +static inline enum pkt_hash_types i40e_ptype_to_htype(u8 ptype) { struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(ptype); @@ -1463,6 +1444,30 @@ static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype) return PKT_HASH_TYPE_L2; } +/** + * i40e_rx_hash - set the hash value in the skb + * @ring: descriptor ring + * @rx_desc: specific descriptor + **/ +static inline void i40e_rx_hash(struct i40e_ring *ring, + union i40e_rx_desc *rx_desc, + struct sk_buff *skb, + u8 rx_ptype) +{ + u32 hash; + const __le64 rss_mask = + cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH << + I40E_RX_DESC_STATUS_FLTSTAT_SHIFT); + + if (ring->netdev->features & NETIF_F_RXHASH) + return; + + if ((rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask) { + hash = le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss); + skb_set_hash(skb, hash, i40e_ptype_to_htype(rx_ptype)); + } +} + /** * i40e_clean_rx_irq_ps - Reclaim resources after receive; packet split * @rx_ring: rx ring to clean @@ -1612,8 +1617,8 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) continue; } - skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc), - i40e_ptype_to_hash(rx_ptype)); + i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); + if (unlikely(rx_status & I40E_RXD_QW1_STATUS_TSYNVALID_MASK)) { i40e_ptp_rx_hwtstamp(vsi->back, skb, (rx_status & I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >> @@ -1741,8 +1746,7 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) continue; } - skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc), - i40e_ptype_to_hash(rx_ptype)); + i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); if (unlikely(rx_status & I40E_RXD_QW1_STATUS_TSYNVALID_MASK)) { i40e_ptp_rx_hwtstamp(vsi->back, skb, (rx_status & I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >> diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 4ca40651a228..7a00657dacda 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -886,31 +886,12 @@ checksum_fail: } /** - * i40e_rx_hash - returns the hash value from the Rx descriptor - * @ring: descriptor ring - * @rx_desc: specific descriptor - **/ -static inline u32 i40e_rx_hash(struct i40e_ring *ring, - union i40e_rx_desc *rx_desc) -{ - const __le64 rss_mask = - cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH << - I40E_RX_DESC_STATUS_FLTSTAT_SHIFT); - - if ((ring->netdev->features & NETIF_F_RXHASH) && - (rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask) - return le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss); - else - return 0; -} - -/** - * i40e_ptype_to_hash - get a hash type + * i40e_ptype_to_htype - get a hash type * @ptype: the ptype value from the descriptor * * Returns a hash type to be used by skb_set_hash **/ -static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype) +static inline enum pkt_hash_types i40e_ptype_to_htype(u8 ptype) { struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(ptype); @@ -927,6 +908,30 @@ static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype) return PKT_HASH_TYPE_L2; } +/** + * i40e_rx_hash - set the hash value in the skb + * @ring: descriptor ring + * @rx_desc: specific descriptor + **/ +static inline void i40e_rx_hash(struct i40e_ring *ring, + union i40e_rx_desc *rx_desc, + struct sk_buff *skb, + u8 rx_ptype) +{ + u32 hash; + const __le64 rss_mask = + cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH << + I40E_RX_DESC_STATUS_FLTSTAT_SHIFT); + + if (ring->netdev->features & NETIF_F_RXHASH) + return; + + if ((rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask) { + hash = le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss); + skb_set_hash(skb, hash, i40e_ptype_to_htype(rx_ptype)); + } +} + /** * i40e_clean_rx_irq_ps - Reclaim resources after receive; packet split * @rx_ring: rx ring to clean @@ -1068,8 +1073,8 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget) continue; } - skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc), - i40e_ptype_to_hash(rx_ptype)); + i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); + /* probably a little skewed due to removing CRC */ total_rx_bytes += skb->len; total_rx_packets++; @@ -1185,8 +1190,7 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) continue; } - skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc), - i40e_ptype_to_hash(rx_ptype)); + i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); /* probably a little skewed due to removing CRC */ total_rx_bytes += skb->len; total_rx_packets++; -- cgit v1.2.3-59-g8ed1b From 56028154caafab34063324916095618b2cd30c73 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Wed, 9 Dec 2015 15:50:22 -0800 Subject: i40e: remove forever unused ID Somehow an ID that has never been productized is in the code. There are no plans to use it, so just get rid of it. Change-ID: I59117d48ea9ee0360b0fe33833ac8092f8a24b4c Signed-off-by: Jesse Brandeburg Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 1 - drivers/net/ethernet/intel/i40e/i40e_devids.h | 1 - drivers/net/ethernet/intel/i40e/i40e_main.c | 1 - drivers/net/ethernet/intel/i40evf/i40e_common.c | 1 - drivers/net/ethernet/intel/i40evf/i40e_devids.h | 1 - 5 files changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 2d74c6e4d7b6..6a034ddac36a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -44,7 +44,6 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw) switch (hw->device_id) { case I40E_DEV_ID_SFP_XL710: case I40E_DEV_ID_QEMU: - case I40E_DEV_ID_KX_A: case I40E_DEV_ID_KX_B: case I40E_DEV_ID_KX_C: case I40E_DEV_ID_QSFP_A: diff --git a/drivers/net/ethernet/intel/i40e/i40e_devids.h b/drivers/net/ethernet/intel/i40e/i40e_devids.h index c601ca4a610c..448ef4c17efb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_devids.h +++ b/drivers/net/ethernet/intel/i40e/i40e_devids.h @@ -30,7 +30,6 @@ /* Device IDs */ #define I40E_DEV_ID_SFP_XL710 0x1572 #define I40E_DEV_ID_QEMU 0x1574 -#define I40E_DEV_ID_KX_A 0x157F #define I40E_DEV_ID_KX_B 0x1580 #define I40E_DEV_ID_KX_C 0x1581 #define I40E_DEV_ID_QSFP_A 0x1583 diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 291480650d11..bb4612c159fd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -82,7 +82,6 @@ static int i40e_veb_get_bw_info(struct i40e_veb *veb); static const struct pci_device_id i40e_pci_tbl[] = { {PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_XL710), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_QEMU), 0}, - {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_A), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_B), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_C), 0}, {PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_A), 0}, diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index 72b1942a94aa..938783e0baac 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -44,7 +44,6 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw) switch (hw->device_id) { case I40E_DEV_ID_SFP_XL710: case I40E_DEV_ID_QEMU: - case I40E_DEV_ID_KX_A: case I40E_DEV_ID_KX_B: case I40E_DEV_ID_KX_C: case I40E_DEV_ID_QSFP_A: diff --git a/drivers/net/ethernet/intel/i40evf/i40e_devids.h b/drivers/net/ethernet/intel/i40evf/i40e_devids.h index e6a39c9862e8..ca8b58c3d1f5 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_devids.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_devids.h @@ -30,7 +30,6 @@ /* Device IDs */ #define I40E_DEV_ID_SFP_XL710 0x1572 #define I40E_DEV_ID_QEMU 0x1574 -#define I40E_DEV_ID_KX_A 0x157F #define I40E_DEV_ID_KX_B 0x1580 #define I40E_DEV_ID_KX_C 0x1581 #define I40E_DEV_ID_QSFP_A 0x1583 -- cgit v1.2.3-59-g8ed1b From 3efcb86e2da69989827066c231edb30ec10de932 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Mon, 23 Nov 2015 10:30:51 -0800 Subject: ixgbe: Fill at least min credits to a TC credit refills Currently credit_refill and credit_max could be zero for a TC and that is causing Tx hang for CEE mode configuration, so to fix that have at min credit assigned to a TC and that is as what IEEE mode already does. Change-ID: If652c133093a21e530f4e9eab09097976f57fb12 Signed-off-by: Vasu Dev Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c index a507a6fe3624..02c7333a9c83 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c @@ -139,6 +139,11 @@ s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *hw, /* Calculate credit refill ratio using multiplier */ credit_refill = min(link_percentage * min_multiplier, MAX_CREDIT_REFILL); + + /* Refill at least minimum credit */ + if (credit_refill < min_credit) + credit_refill = min_credit; + p->data_credits_refill = (u16)credit_refill; /* Calculate maximum credit for the TC */ @@ -149,7 +154,7 @@ s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *hw, * of a TC is too small, the maximum credit may not be * enough to send out a jumbo frame in data plane arbitration. */ - if (credit_max && (credit_max < min_credit)) + if (credit_max < min_credit) credit_max = min_credit; if (direction == DCB_TX_CONFIG) { -- cgit v1.2.3-59-g8ed1b From f10166aba2def9bc6443290231c60f7e2f70129b Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Mon, 23 Nov 2015 10:31:01 -0800 Subject: ixgbe: use correct FCoE DDP max check Use fcoe_ddp_xid from netdev as this is correctly set for different device IDs to avoid DDP skip error on X550 as "xid=0x20b out-of-range" Signed-off-by: Vasu Dev Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c index 5f988703e1b7..598293790ae6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c @@ -77,7 +77,7 @@ int ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid) if (!netdev) return 0; - if (xid >= IXGBE_FCOE_DDP_MAX) + if (xid >= netdev->fcoe_ddp_xid) return 0; adapter = netdev_priv(netdev); @@ -177,7 +177,7 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid, return 0; adapter = netdev_priv(netdev); - if (xid >= IXGBE_FCOE_DDP_MAX) { + if (xid >= netdev->fcoe_ddp_xid) { e_warn(drv, "xid=0x%x out-of-range\n", xid); return 0; } -- cgit v1.2.3-59-g8ed1b From cb78cf12d6e90f57f6e7d090867ef19b6a189dde Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Mon, 23 Nov 2015 10:31:25 -0800 Subject: ixgbe: fix broken PFC with X550 PFC is configuration is skipped for X550 devices due to a incorrect device id check, fixing that to include X550 PFC configuration. Signed-off-by: Vasu Dev Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c index 23277ab153b6..b5cc989a3d23 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c @@ -223,13 +223,13 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc) reg |= IXGBE_MFLCN_DPF; /* - * X540 supports per TC Rx priority flow control. So - * clear all TCs and only enable those that should be + * X540 & X550 supports per TC Rx priority flow control. + * So clear all TCs and only enable those that should be * enabled. */ reg &= ~(IXGBE_MFLCN_RPFCE_MASK | IXGBE_MFLCN_RFCE); - if (hw->mac.type == ixgbe_mac_X540) + if (hw->mac.type >= ixgbe_mac_X540) reg |= pfc_en << IXGBE_MFLCN_RPFCE_SHIFT; if (pfc_en) -- cgit v1.2.3-59-g8ed1b From 0e4d422f5f7249324ac8d1b8e12772e530787a66 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Thu, 3 Dec 2015 15:20:06 -0800 Subject: ixgbe: do not call check_link for ethtool in ixgbe_get_settings() In ixgbe_get_settings() the link status and speed of the interface are determined based on a read from the LINKS register via the call to mac.ops.check.link(). This can cause issues where external drivers may end up with unknown speed when calling ethtool_get_setings(). Instead of calling the mac.ops.check_link() we can report the speed from the adapter structure which is populated by the driver. Signed-off-by: Emil Tantilov Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 2448eba2eecd..bea96b3bc90c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -185,9 +185,7 @@ static int ixgbe_get_settings(struct net_device *netdev, struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; ixgbe_link_speed supported_link; - u32 link_speed = 0; bool autoneg = false; - bool link_up; hw->mac.ops.get_link_capabilities(hw, &supported_link, &autoneg); @@ -313,9 +311,8 @@ static int ixgbe_get_settings(struct net_device *netdev, break; } - hw->mac.ops.check_link(hw, &link_speed, &link_up, false); - if (link_up) { - switch (link_speed) { + if (netif_carrier_ok(netdev)) { + switch (adapter->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: ethtool_cmd_speed_set(ecmd, SPEED_10000); break; -- cgit v1.2.3-59-g8ed1b From d469251bfd06d15289c9dd5dd60b8ebf65785b03 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Fri, 4 Dec 2015 11:26:43 -0800 Subject: ixgbe: Correct handling of any outer UDP checksum setting If an outer UDP checksum is set, pass the skb up with CHECKSUM_NONE so that the stack will check the checksum. Do not increment an error counter, because we don't know that there is an actual error. Signed-off-by: Mark Rustad Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index ea9537d0e63a..a12f93dd8602 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1483,7 +1483,7 @@ static inline void ixgbe_rx_checksum(struct ixgbe_ring *ring, return; if (ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_ERR_OUTERIPER)) { - ring->rx_stats.csum_err++; + skb->ip_summed = CHECKSUM_NONE; return; } /* If we checked the outer header let the stack know */ -- cgit v1.2.3-59-g8ed1b From b262a9a772eae649159fd2480992713a2dd2b3d3 Mon Sep 17 00:00:00 2001 From: Usha Ketineni Date: Tue, 8 Dec 2015 04:01:18 -0800 Subject: ixgbe: Fix to get FDMI HBA attributes information with X550 Check whether the FCOE support is enabled for the devices to get the FDMI HBA attributes information instead of checking each device id. Also, add Model string information for X550. Signed-off-by: Usha Ketineni Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c index 598293790ae6..df1647fa892a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c @@ -996,8 +996,7 @@ int ixgbe_fcoe_get_hbainfo(struct net_device *netdev, return -EINVAL; /* Don't return information on unsupported devices */ - if (hw->mac.type != ixgbe_mac_82599EB && - hw->mac.type != ixgbe_mac_X540) + if (!(adapter->flags & IXGBE_FLAG_FCOE_ENABLED)) return -EINVAL; /* Manufacturer */ @@ -1043,6 +1042,10 @@ int ixgbe_fcoe_get_hbainfo(struct net_device *netdev, snprintf(info->model, sizeof(info->model), "Intel 82599"); + } else if (hw->mac.type == ixgbe_mac_X550) { + snprintf(info->model, + sizeof(info->model), + "Intel X550"); } else { snprintf(info->model, sizeof(info->model), -- cgit v1.2.3-59-g8ed1b From 8b75451be1fc05b6ee3f9d0eaea0006d60caff89 Mon Sep 17 00:00:00 2001 From: Neerav Parikh Date: Tue, 8 Dec 2015 22:13:58 -0800 Subject: ixgbe: Fix MDD events generated when FCoE+SRIOV are enabled When FCoE is enabled with SR-IOV on the X550 NIC the hardware generates MDD events. This patch fixes these by setting the expected values in the Tx context descriptors for FCoE/FIP frames and adding a flush after writing the RDLEN register. Signed-off-by: Neerav Parikh Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c | 5 ++++- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 3 +++ drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c index df1647fa892a..2a653ec954f5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c @@ -517,6 +517,7 @@ int ixgbe_fso(struct ixgbe_ring *tx_ring, u32 vlan_macip_lens; u32 fcoe_sof_eof = 0; u32 mss_l4len_idx; + u32 type_tucmd = IXGBE_ADVTXT_TUCMD_FCOE; u8 sof, eof; if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_type != SKB_GSO_FCOE)) { @@ -593,6 +594,8 @@ int ixgbe_fso(struct ixgbe_ring *tx_ring, skb_shinfo(skb)->gso_size); first->bytecount += (first->gso_segs - 1) * *hdr_len; first->tx_flags |= IXGBE_TX_FLAGS_TSO; + /* Hardware expects L4T to be RSV for FCoE TSO */ + type_tucmd |= IXGBE_ADVTXD_TUCMD_L4T_RSV; } /* set flag indicating FCOE to ixgbe_tx_map call */ @@ -610,7 +613,7 @@ int ixgbe_fso(struct ixgbe_ring *tx_ring, /* write context desc */ ixgbe_tx_ctxtdesc(tx_ring, vlan_macip_lens, fcoe_sof_eof, - IXGBE_ADVTXT_TUCMD_FCOE, mss_l4len_idx); + type_tucmd, mss_l4len_idx); return 0; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index a12f93dd8602..328d7a828e0b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3619,6 +3619,9 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_RDBAH(reg_idx), (rdba >> 32)); IXGBE_WRITE_REG(hw, IXGBE_RDLEN(reg_idx), ring->count * sizeof(union ixgbe_adv_rx_desc)); + /* Force flushing of IXGBE_RDLEN to prevent MDD */ + IXGBE_WRITE_FLUSH(hw); + IXGBE_WRITE_REG(hw, IXGBE_RDH(reg_idx), 0); IXGBE_WRITE_REG(hw, IXGBE_RDT(reg_idx), 0); ring->tail = adapter->io_addr + IXGBE_RDT(reg_idx); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 5f53cc6c609a..bf7367a08716 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -2780,6 +2780,7 @@ struct ixgbe_adv_tx_context_desc { #define IXGBE_ADVTXD_TUCMD_L4T_UDP 0x00000000 /* L4 Packet TYPE of UDP */ #define IXGBE_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet TYPE of TCP */ #define IXGBE_ADVTXD_TUCMD_L4T_SCTP 0x00001000 /* L4 Packet TYPE of SCTP */ +#define IXGBE_ADVTXD_TUCMD_L4T_RSV 0x00001800 /* RSV L4 Packet TYPE */ #define IXGBE_ADVTXD_TUCMD_MKRREQ 0x00002000 /*Req requires Markers and CRC*/ #define IXGBE_ADVTXD_POPTS_IPSEC 0x00000400 /* IPSec offload request */ #define IXGBE_ADVTXD_TUCMD_IPSEC_TYPE_ESP 0x00002000 /* IPSec Type ESP */ -- cgit v1.2.3-59-g8ed1b From e19dcdeb3527e996a96ea49d86cccce768b1079a Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Wed, 9 Dec 2015 14:55:37 -0800 Subject: ixgbe: Make ATR recognize IPv6 extended headers Right now ATR is not handling IPv6 extended headers, so ATR is not being performed on such packets. Fix that by skipping extended headers when they are present. This also fixes a problem where the ATR code was not checking that the inner protocol was actually TCP before setting up the signature rules. Since the protocol check is intimately involved with the extended header processing as well, this all gets fixed together. Signed-off-by: Mark Rustad Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 47 +++++++++++++++++++++------ 1 file changed, 37 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 328d7a828e0b..c4003a88bbf6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -7573,7 +7573,9 @@ static void ixgbe_atr(struct ixgbe_ring *ring, /* snag network header to get L4 type and address */ skb = first->skb; hdr.network = skb_network_header(skb); - if (skb->encapsulation) { + if (!skb->encapsulation) { + th = tcp_hdr(skb); + } else { #ifdef CONFIG_IXGBE_VXLAN struct ixgbe_adapter *adapter = q_vector->adapter; @@ -7592,14 +7594,34 @@ static void ixgbe_atr(struct ixgbe_ring *ring, #else return; #endif /* CONFIG_IXGBE_VXLAN */ - } else { - /* Currently only IPv4/IPv6 with TCP is supported */ - if ((first->protocol != htons(ETH_P_IPV6) || - hdr.ipv6->nexthdr != IPPROTO_TCP) && - (first->protocol != htons(ETH_P_IP) || - hdr.ipv4->protocol != IPPROTO_TCP)) + } + + /* Currently only IPv4/IPv6 with TCP is supported */ + switch (hdr.ipv4->version) { + case IPVERSION: + if (hdr.ipv4->protocol != IPPROTO_TCP) return; - th = tcp_hdr(skb); + break; + case 6: + if (likely((unsigned char *)th - hdr.network == + sizeof(struct ipv6hdr))) { + if (hdr.ipv6->nexthdr != IPPROTO_TCP) + return; + } else { + __be16 frag_off; + u8 l4_hdr; + + ipv6_skip_exthdr(skb, hdr.network - skb->data + + sizeof(struct ipv6hdr), + &l4_hdr, &frag_off); + if (unlikely(frag_off)) + return; + if (l4_hdr != IPPROTO_TCP) + return; + } + break; + default: + return; } /* skip this packet since it is invalid or the socket is closing */ @@ -7634,10 +7656,12 @@ static void ixgbe_atr(struct ixgbe_ring *ring, common.port.src ^= th->dest ^ first->protocol; common.port.dst ^= th->source; - if (first->protocol == htons(ETH_P_IP)) { + switch (hdr.ipv4->version) { + case IPVERSION: input.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4; common.ip ^= hdr.ipv4->saddr ^ hdr.ipv4->daddr; - } else { + break; + case 6: input.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_TCPV6; common.ip ^= hdr.ipv6->saddr.s6_addr32[0] ^ hdr.ipv6->saddr.s6_addr32[1] ^ @@ -7647,6 +7671,9 @@ static void ixgbe_atr(struct ixgbe_ring *ring, hdr.ipv6->daddr.s6_addr32[1] ^ hdr.ipv6->daddr.s6_addr32[2] ^ hdr.ipv6->daddr.s6_addr32[3]; + break; + default: + break; } #ifdef CONFIG_IXGBE_VXLAN -- cgit v1.2.3-59-g8ed1b From 85f37d17b3f19cf9cde3fd7b90c7a30c97c04023 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 8 Jan 2016 13:58:50 +0300 Subject: isdn: act200: fix MODULE_PARM_DESC() typo There is no "membase", it was "act_port" that was intended. Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/isdn/act2000/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c index c3a1b061838d..68073d0da0e3 100644 --- a/drivers/isdn/act2000/module.c +++ b/drivers/isdn/act2000/module.c @@ -37,7 +37,7 @@ MODULE_DESCRIPTION("ISDN4Linux: Driver for IBM Active 2000 ISDN card"); MODULE_AUTHOR("Fritz Elfert"); MODULE_LICENSE("GPL"); MODULE_PARM_DESC(act_bus, "BusType of first card, 1=ISA, 2=MCA, 3=PCMCIA, currently only ISA"); -MODULE_PARM_DESC(membase, "Base port address of first card"); +MODULE_PARM_DESC(act_port, "Base port address of first card"); MODULE_PARM_DESC(act_irq, "IRQ of first card"); MODULE_PARM_DESC(act_id, "ID-String of first card"); module_param(act_bus, int, 0); -- cgit v1.2.3-59-g8ed1b From 6757e6f5f44de32bc8cd0bb2dc800580380792db Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 8 Jan 2016 14:04:04 +0300 Subject: irda: toim3232-sir: delete some dead code I noticed this code because it has a typo in the module_param(), the "trs" at the end of "toim3232fliptrs" should be "rts". But it's dead code so instead of fixing it, I just deleted it. Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/irda/toim3232-sir.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/irda/toim3232-sir.c b/drivers/net/irda/toim3232-sir.c index 6d2f55959c49..b977d6d33e74 100644 --- a/drivers/net/irda/toim3232-sir.c +++ b/drivers/net/irda/toim3232-sir.c @@ -130,16 +130,6 @@ static int toim3232delay = 150; /* default is 150 ms */ module_param(toim3232delay, int, 0); MODULE_PARM_DESC(toim3232delay, "toim3232 dongle write complete delay"); -#if 0 -static int toim3232flipdtr = 0; /* default is DTR high to reset */ -module_param(toim3232flipdtr, int, 0); -MODULE_PARM_DESC(toim3232flipdtr, "toim3232 dongle invert DTR (Reset)"); - -static int toim3232fliprts = 0; /* default is RTS high for baud change */ -module_param(toim3232fliptrs, int, 0); -MODULE_PARM_DESC(toim3232fliprts, "toim3232 dongle invert RTS (BR/D)"); -#endif - static int toim3232_open(struct sir_dev *); static int toim3232_close(struct sir_dev *); static int toim3232_change_speed(struct sir_dev *, unsigned); -- cgit v1.2.3-59-g8ed1b From b4c19f71252e3b6b8c6478fd712c592f00b11438 Mon Sep 17 00:00:00 2001 From: Roosen Henri Date: Thu, 7 Jan 2016 09:31:15 +0100 Subject: phy: micrel: Fix finding PHY properties in MAC node for KSZ9031. Commit 651df2183543 ("phy: micrel: Fix finding PHY properties in MAC node.") only fixes finding PHY properties in MAC node for KSZ9021. This commit applies the same fix for KSZ9031. Fixes: 8b63ec1837fa ("phylib: Make PHYs children of their MDIO bus, not the bus' parent.") Acked-by: Andrew Lunn Signed-off-by: Henri Roosen Signed-off-by: David S. Miller --- drivers/net/phy/micrel.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index e13ad6cdcc22..7a5679982c03 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -470,9 +470,17 @@ static int ksz9031_config_init(struct phy_device *phydev) "txd2-skew-ps", "txd3-skew-ps" }; static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"}; + const struct device *dev_walker; - if (!of_node && dev->parent->of_node) - of_node = dev->parent->of_node; + /* The Micrel driver has a deprecated option to place phy OF + * properties in the MAC node. Walk up the tree of devices to + * find a device with an OF node. + */ + dev_walker = &phydev->dev; + do { + of_node = dev_walker->of_node; + dev_walker = dev_walker->parent; + } while (!of_node && dev_walker); if (of_node) { ksz9031_of_load_skew_values(phydev, of_node, -- cgit v1.2.3-59-g8ed1b From 5ee3c60c8d3b88cab6496c9b7d49a01576dd9cf9 Mon Sep 17 00:00:00 2001 From: hayeswang Date: Thu, 7 Jan 2016 17:12:17 +0800 Subject: r8152: fix the wake event When the autosuspend is enabled and occurs before system suspend, we should wake the device before running system syspend. Then, we could change the wake event for system suspend. Otherwise, the device would resume the system when receiving any packet. Signed-off-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 2fb637ad594a..ef9aab6712a3 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -25,12 +25,13 @@ #include #include #include +#include /* Information for net-next */ #define NETNEXT_VERSION "08" /* Information for net */ -#define NET_VERSION "2" +#define NET_VERSION "3" #define DRIVER_VERSION "v1." NETNEXT_VERSION "." NET_VERSION #define DRIVER_AUTHOR "Realtek linux nic maintainers " @@ -604,6 +605,9 @@ struct r8152 { struct delayed_work schedule; struct mii_if_info mii; struct mutex control; /* use for hw setting */ +#ifdef CONFIG_PM_SLEEP + struct notifier_block pm_notifier; +#endif struct rtl_ops { void (*init)(struct r8152 *); @@ -3048,6 +3052,33 @@ out1: usb_autopm_put_interface(tp->intf); } +#ifdef CONFIG_PM_SLEEP +static int rtl_notifier(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct r8152 *tp = container_of(nb, struct r8152, pm_notifier); + + switch (action) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + usb_autopm_get_interface(tp->intf); + break; + + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + usb_autopm_put_interface(tp->intf); + break; + + case PM_POST_RESTORE: + case PM_RESTORE_PREPARE: + default: + break; + } + + return NOTIFY_DONE; +} +#endif + static int rtl8152_open(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); @@ -3090,6 +3121,10 @@ static int rtl8152_open(struct net_device *netdev) mutex_unlock(&tp->control); usb_autopm_put_interface(tp->intf); +#ifdef CONFIG_PM_SLEEP + tp->pm_notifier.notifier_call = rtl_notifier; + register_pm_notifier(&tp->pm_notifier); +#endif out: return res; @@ -3100,6 +3135,9 @@ static int rtl8152_close(struct net_device *netdev) struct r8152 *tp = netdev_priv(netdev); int res = 0; +#ifdef CONFIG_PM_SLEEP + unregister_pm_notifier(&tp->pm_notifier); +#endif napi_disable(&tp->napi); clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); -- cgit v1.2.3-59-g8ed1b From 216a8349d3a0dd1bc2afbcc821e374c8f929bd62 Mon Sep 17 00:00:00 2001 From: hayeswang Date: Thu, 7 Jan 2016 17:51:11 +0800 Subject: r8152: use test_and_clear_bit Replace test_bit() followed by clear_bit() with test_and_clear_bit(). Signed-off-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 975e9171cf9a..4f0bb67a7590 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1938,7 +1938,6 @@ static void _rtl8152_set_rx_mode(struct net_device *netdev) __le32 tmp[2]; u32 ocp_data; - clear_bit(RTL8152_SET_RX_MODE, &tp->flags); netif_stop_queue(netdev); ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); ocp_data &= ~RCR_ACPT_ALL; @@ -2424,8 +2423,6 @@ static void rtl_phy_reset(struct r8152 *tp) u16 data; int i; - clear_bit(PHY_RESET, &tp->flags); - data = r8152_mdio_read(tp, MII_BMCR); /* don't reset again before the previous one complete */ @@ -2884,10 +2881,9 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex) r8152_mdio_write(tp, MII_ADVERTISE, anar); r8152_mdio_write(tp, MII_BMCR, bmcr); - if (test_bit(PHY_RESET, &tp->flags)) { + if (test_and_clear_bit(PHY_RESET, &tp->flags)) { int i; - clear_bit(PHY_RESET, &tp->flags); for (i = 0; i < 50; i++) { msleep(20); if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0) @@ -2896,7 +2892,6 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex) } out: - return ret; } @@ -2983,7 +2978,6 @@ static void set_carrier(struct r8152 *tp) struct net_device *netdev = tp->netdev; u8 speed; - clear_bit(RTL8152_LINK_CHG, &tp->flags); speed = rtl8152_get_speed(tp); if (speed & LINK_STATUS) { @@ -3026,20 +3020,18 @@ static void rtl_work_func_t(struct work_struct *work) goto out1; } - if (test_bit(RTL8152_LINK_CHG, &tp->flags)) + if (test_and_clear_bit(RTL8152_LINK_CHG, &tp->flags)) set_carrier(tp); - if (test_bit(RTL8152_SET_RX_MODE, &tp->flags)) + if (test_and_clear_bit(RTL8152_SET_RX_MODE, &tp->flags)) _rtl8152_set_rx_mode(tp->netdev); /* don't schedule napi before linking */ - if (test_bit(SCHEDULE_NAPI, &tp->flags) && - netif_carrier_ok(tp->netdev)) { - clear_bit(SCHEDULE_NAPI, &tp->flags); + if (test_and_clear_bit(SCHEDULE_NAPI, &tp->flags) && + netif_carrier_ok(tp->netdev)) napi_schedule(&tp->napi); - } - if (test_bit(PHY_RESET, &tp->flags)) + if (test_and_clear_bit(PHY_RESET, &tp->flags)) rtl_phy_reset(tp); mutex_unlock(&tp->control); -- cgit v1.2.3-59-g8ed1b From cda9fb01dc3cafd718b2865b447e869bf6624ddd Mon Sep 17 00:00:00 2001 From: hayeswang Date: Thu, 7 Jan 2016 17:51:12 +0800 Subject: r8152: adjust ALDPS function Replace disable_aldps() and enable_aldps() with aldps_en(). Signed-off-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 72 +++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 4f0bb67a7590..230c73c0727a 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -2452,23 +2452,23 @@ static void r8153_teredo_off(struct r8152 *tp) ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TEREDO_TIMER, 0); } -static void r8152b_disable_aldps(struct r8152 *tp) +static void r8152_aldps_en(struct r8152 *tp, bool enable) { - ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE); - msleep(20); -} - -static inline void r8152b_enable_aldps(struct r8152 *tp) -{ - ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS | - LINKENA | DIS_SDSAVE); + if (enable) { + ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS | + LINKENA | DIS_SDSAVE); + } else { + ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | + DIS_SDSAVE); + msleep(20); + } } static void rtl8152_disable(struct r8152 *tp) { - r8152b_disable_aldps(tp); + r8152_aldps_en(tp, false); rtl_disable(tp); - r8152b_enable_aldps(tp); + r8152_aldps_en(tp, true); } static void r8152b_hw_phy_cfg(struct r8152 *tp) @@ -2780,30 +2780,26 @@ static void r8153_enter_oob(struct r8152 *tp) ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); } -static void r8153_disable_aldps(struct r8152 *tp) +static void r8153_aldps_en(struct r8152 *tp, bool enable) { u16 data; data = ocp_reg_read(tp, OCP_POWER_CFG); - data &= ~EN_ALDPS; - ocp_reg_write(tp, OCP_POWER_CFG, data); - msleep(20); -} - -static void r8153_enable_aldps(struct r8152 *tp) -{ - u16 data; - - data = ocp_reg_read(tp, OCP_POWER_CFG); - data |= EN_ALDPS; - ocp_reg_write(tp, OCP_POWER_CFG, data); + if (enable) { + data |= EN_ALDPS; + ocp_reg_write(tp, OCP_POWER_CFG, data); + } else { + data &= ~EN_ALDPS; + ocp_reg_write(tp, OCP_POWER_CFG, data); + msleep(20); + } } static void rtl8153_disable(struct r8152 *tp) { - r8153_disable_aldps(tp); + r8153_aldps_en(tp, false); rtl_disable(tp); - r8153_enable_aldps(tp); + r8153_aldps_en(tp, true); usb_enable_lpm(tp->udev); } @@ -2900,9 +2896,9 @@ static void rtl8152_up(struct r8152 *tp) if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; - r8152b_disable_aldps(tp); + r8152_aldps_en(tp, false); r8152b_exit_oob(tp); - r8152b_enable_aldps(tp); + r8152_aldps_en(tp, true); } static void rtl8152_down(struct r8152 *tp) @@ -2913,9 +2909,9 @@ static void rtl8152_down(struct r8152 *tp) } r8152_power_cut_en(tp, false); - r8152b_disable_aldps(tp); + r8152_aldps_en(tp, false); r8152b_enter_oob(tp); - r8152b_enable_aldps(tp); + r8152_aldps_en(tp, true); } static void rtl8153_up(struct r8152 *tp) @@ -2924,9 +2920,9 @@ static void rtl8153_up(struct r8152 *tp) return; r8153_u1u2en(tp, false); - r8153_disable_aldps(tp); + r8153_aldps_en(tp, false); r8153_first_init(tp); - r8153_enable_aldps(tp); + r8153_aldps_en(tp, true); r8153_u2p3en(tp, true); r8153_u1u2en(tp, true); usb_enable_lpm(tp->udev); @@ -2942,9 +2938,9 @@ static void rtl8153_down(struct r8152 *tp) r8153_u1u2en(tp, false); r8153_u2p3en(tp, false); r8153_power_cut_en(tp, false); - r8153_disable_aldps(tp); + r8153_aldps_en(tp, false); r8153_enter_oob(tp); - r8153_enable_aldps(tp); + r8153_aldps_en(tp, true); } static bool rtl8152_in_nway(struct r8152 *tp) @@ -3230,7 +3226,7 @@ static void r8152b_init(struct r8152 *tp) if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; - r8152b_disable_aldps(tp); + r8152_aldps_en(tp, false); if (tp->version == RTL_VER_01) { ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); @@ -3252,7 +3248,7 @@ static void r8152b_init(struct r8152 *tp) ocp_write_word(tp, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data); r8152b_enable_eee(tp); - r8152b_enable_aldps(tp); + r8152_aldps_en(tp, true); r8152b_enable_fc(tp); rtl_tally_reset(tp); @@ -3270,7 +3266,7 @@ static void r8153_init(struct r8152 *tp) if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; - r8153_disable_aldps(tp); + r8153_aldps_en(tp, false); r8153_u1u2en(tp, false); for (i = 0; i < 500; i++) { @@ -3359,7 +3355,7 @@ static void r8153_init(struct r8152 *tp) EEE_SPDWN_EN); r8153_enable_eee(tp); - r8153_enable_aldps(tp); + r8153_aldps_en(tp, true); r8152b_enable_fc(tp); rtl_tally_reset(tp); r8153_u2p3en(tp, true); -- cgit v1.2.3-59-g8ed1b From 29c6dd591bbd592472247441de9fa694acdabae8 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 7 Jan 2016 11:01:00 +0100 Subject: cdc-acm: fix NULL pointer reference The union descriptor must be checked. Its usage was conditional before the parser was introduced. This is important, because many RNDIS device, which also use the common parser, have bogus extra descriptors. Signed-off-by: Oliver Neukum Tested-by: Vasily Galkin Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ether.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 3da70bf9936a..7cba2c3759df 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -160,6 +160,12 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) info->u = header.usb_cdc_union_desc; info->header = header.usb_cdc_header_desc; info->ether = header.usb_cdc_ether_desc; + if (!info->u) { + if (rndis) + goto skip; + else /* in that case a quirk is mandatory */ + goto bad_desc; + } /* we need a master/control interface (what we're * probed with) and a slave/data interface; union * descriptors sort this all out. @@ -256,7 +262,7 @@ skip: goto bad_desc; } - } else if (!info->header || !info->u || (!rndis && !info->ether)) { + } else if (!info->header || (!rndis && !info->ether)) { dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n", info->header ? "" : "header ", info->u ? "" : "union ", -- cgit v1.2.3-59-g8ed1b From 824b1a97400338e47ba7f878a42cd89d80cd5b81 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Thu, 7 Jan 2016 15:42:37 +0530 Subject: net: plip: use new parport device model Modify plip driver to use the new parallel port device model. Signed-off-by: Sudip Mukherjee Signed-off-by: David S. Miller --- drivers/net/plip/plip.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c index 040b8978d6ca..9c4b41a4df7d 100644 --- a/drivers/net/plip/plip.c +++ b/drivers/net/plip/plip.c @@ -1249,6 +1249,7 @@ static void plip_attach (struct parport *port) struct net_device *dev; struct net_local *nl; char name[IFNAMSIZ]; + struct pardev_cb plip_cb; if ((parport[0] == -1 && (!timid || !port->devices)) || plip_searchfor(parport, port->number)) { @@ -1273,9 +1274,15 @@ static void plip_attach (struct parport *port) nl = netdev_priv(dev); nl->dev = dev; - nl->pardev = parport_register_device(port, dev->name, plip_preempt, - plip_wakeup, plip_interrupt, - 0, dev); + + memset(&plip_cb, 0, sizeof(plip_cb)); + plip_cb.private = dev; + plip_cb.preempt = plip_preempt; + plip_cb.wakeup = plip_wakeup; + plip_cb.irq_func = plip_interrupt; + + nl->pardev = parport_register_dev_model(port, dev->name, + &plip_cb, unit); if (!nl->pardev) { printk(KERN_ERR "%s: parport_register failed\n", name); @@ -1315,10 +1322,23 @@ static void plip_detach (struct parport *port) /* Nothing to do */ } +static int plip_probe(struct pardevice *par_dev) +{ + struct device_driver *drv = par_dev->dev.driver; + int len = strlen(drv->name); + + if (strncmp(par_dev->name, drv->name, len)) + return -ENODEV; + + return 0; +} + static struct parport_driver plip_driver = { - .name = "plip", - .attach = plip_attach, - .detach = plip_detach + .name = "plip", + .probe = plip_probe, + .match_port = plip_attach, + .detach = plip_detach, + .devmodel = true, }; static void __exit plip_cleanup_module (void) @@ -1326,8 +1346,6 @@ static void __exit plip_cleanup_module (void) struct net_device *dev; int i; - parport_unregister_driver (&plip_driver); - for (i=0; i < PLIP_MAX; i++) { if ((dev = dev_plip[i])) { struct net_local *nl = netdev_priv(dev); @@ -1339,6 +1357,8 @@ static void __exit plip_cleanup_module (void) dev_plip[i] = NULL; } } + + parport_unregister_driver(&plip_driver); } #ifndef MODULE -- cgit v1.2.3-59-g8ed1b From 07b9b37c227cb8d88d478b4a9c5634fee514ede1 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 7 Jan 2016 11:26:53 +0100 Subject: vxlan: fix test which detect duplicate vxlan iface When a vxlan interface is created, the driver checks that there is not another vxlan interface with the same properties. To do this, it checks the existing vxlan udp socket. Since commit 1c51a9159dde, the creation of the vxlan socket is done only when the interface is set up, thus it breaks that test. Example: $ ip l a vxlan10 type vxlan id 10 group 239.0.0.10 dev eth0 dstport 0 $ ip l a vxlan11 type vxlan id 10 group 239.0.0.10 dev eth0 dstport 0 $ ip -br l | grep vxlan vxlan10 DOWN f2:55:1c:6a:fb:00 vxlan11 DOWN 7a:cb:b9:38:59:0d Instead of checking sockets, let's loop over the vxlan iface list. Fixes: 1c51a9159dde ("vxlan: fix race caused by dropping rtnl_unlock") Reported-by: Thomas Faivre Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index ba363cedef80..405a7b6cca25 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2751,7 +2751,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, struct vxlan_config *conf) { struct vxlan_net *vn = net_generic(src_net, vxlan_net_id); - struct vxlan_dev *vxlan = netdev_priv(dev); + struct vxlan_dev *vxlan = netdev_priv(dev), *tmp; struct vxlan_rdst *dst = &vxlan->default_dst; unsigned short needed_headroom = ETH_HLEN; int err; @@ -2817,9 +2817,15 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, if (!vxlan->cfg.age_interval) vxlan->cfg.age_interval = FDB_AGE_DEFAULT; - if (vxlan_find_vni(src_net, conf->vni, use_ipv6 ? AF_INET6 : AF_INET, - vxlan->cfg.dst_port, vxlan->flags)) + list_for_each_entry(tmp, &vn->vxlan_list, next) { + if (tmp->cfg.vni == conf->vni && + (tmp->default_dst.remote_ip.sa.sa_family == AF_INET6 || + tmp->cfg.saddr.sa.sa_family == AF_INET6) == use_ipv6 && + tmp->cfg.dst_port == vxlan->cfg.dst_port && + (tmp->flags & VXLAN_F_RCV_FLAGS) == + (vxlan->flags & VXLAN_F_RCV_FLAGS)) return -EEXIST; + } dev->ethtool_ops = &vxlan_ethtool_ops; -- cgit v1.2.3-59-g8ed1b From 2fa9d45e16089b499909d807025f0d66b2442d57 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 7 Jan 2016 11:50:29 +0100 Subject: mlxsw: spectrum: pass local_port to mlxsw_sp_port_fdb_uc_op Do not pass struct mlxsw_sp_port to mlxsw_sp_port_fdb_uc_op and rather just pass local_port. This is needed in case this is called from SFN process function and mlxsw_sp_port is not existent for particular local_port. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 614ef57ceefa..5150e9e2b718 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -594,11 +594,10 @@ static enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding) MLXSW_REG_SFD_OP_WRITE_REMOVE; } -static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port, +static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port, const char *mac, u16 fid, bool adding, bool dynamic) { - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; char *sfd_pl; int err; @@ -609,7 +608,7 @@ static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); mlxsw_reg_sfd_uc_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic), mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP, - mlxsw_sp_port->local_port); + local_port); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); kfree(sfd_pl); @@ -659,7 +658,8 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, fid = mlxsw_sp_port->pvid; if (!mlxsw_sp_port->lagged) - return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, + return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port->mlxsw_sp, + mlxsw_sp_port->local_port, fdb->addr, fid, true, false); else return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp, @@ -798,7 +798,8 @@ mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port, } if (!mlxsw_sp_port->lagged) - return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, + return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port->mlxsw_sp, + mlxsw_sp_port->local_port, fdb->addr, fid, false, false); else @@ -1056,7 +1057,7 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, vid = fid; } - err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port, mac, fid, + err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid, adding && mlxsw_sp_port->learning, true); if (err) { if (net_ratelimit()) -- cgit v1.2.3-59-g8ed1b From 12f1501e7511958b3a7244e24f290803d0a3c41f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 7 Jan 2016 11:50:30 +0100 Subject: mlxsw: spectrum: remove FDB entry in case we get unknown object notification It may happen that we get notification for FDB entry for object (port, lag, vport), which does not exist. Currently we ignore that, which only causes this being re-sent in next notification. The entry will never disappear. So get rid of it by simply removing it using SFD register. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 38 +++++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 5150e9e2b718..d64559e4aff7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1030,13 +1030,14 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, char mac[ETH_ALEN]; u8 local_port; u16 vid, fid; + bool do_notification = true; int err; mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port); mlxsw_sp_port = mlxsw_sp->ports[local_port]; if (!mlxsw_sp_port) { dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n"); - return; + goto just_remove; } if (mlxsw_sp_fid_is_vfid(fid)) { @@ -1047,9 +1048,8 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, vfid); if (!mlxsw_sp_vport) { netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n"); - return; + goto just_remove; } - vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); /* Override the physical port with the vPort. */ mlxsw_sp_port = mlxsw_sp_vport; @@ -1057,17 +1057,28 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, vid = fid; } + adding = adding && mlxsw_sp_port->learning; + +do_fdb_op: err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid, - adding && mlxsw_sp_port->learning, true); + adding, true); if (err) { if (net_ratelimit()) netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n"); return; } + if (!do_notification) + return; mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning, mlxsw_sp_port->learning_sync, adding, mac, vid, mlxsw_sp_port->dev); + return; + +just_remove: + adding = false; + do_notification = false; + goto do_fdb_op; } static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, @@ -1079,13 +1090,14 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, u16 lag_vid = 0; u16 lag_id; u16 vid, fid; + bool do_notification = true; int err; mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &fid, &lag_id); mlxsw_sp_port = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id); if (!mlxsw_sp_port) { dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Cannot find port representor for LAG\n"); - return; + goto just_remove; } if (mlxsw_sp_fid_is_vfid(fid)) { @@ -1096,7 +1108,7 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, vfid); if (!mlxsw_sp_vport) { netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n"); - return; + goto just_remove; } vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport); @@ -1107,19 +1119,29 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp, vid = fid; } + adding = adding && mlxsw_sp_port->learning; + +do_fdb_op: err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid, - adding && mlxsw_sp_port->learning, - true); + adding, true); if (err) { if (net_ratelimit()) netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n"); return; } + if (!do_notification) + return; mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning, mlxsw_sp_port->learning_sync, adding, mac, vid, mlxsw_sp_lag_get(mlxsw_sp, lag_id)->dev); + return; + +just_remove: + adding = false; + do_notification = false; + goto do_fdb_op; } static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp, -- cgit v1.2.3-59-g8ed1b From 5230b25f06da95032320d95e859eb95e463af12c Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Sun, 10 Jan 2016 21:06:24 +0100 Subject: mlxsw: reg: Add definition of multicast record for SFD register Multicast-related records have specific format in SFD register. Signed-off-by: Elad Raz Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 49 +++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 66d851d4dfb4..9b7945757c5d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -287,6 +287,7 @@ MLXSW_ITEM32_INDEXED(reg, sfd, rec_swid, MLXSW_REG_SFD_BASE_LEN, 24, 8, enum mlxsw_reg_sfd_rec_type { MLXSW_REG_SFD_REC_TYPE_UNICAST = 0x0, MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG = 0x1, + MLXSW_REG_SFD_REC_TYPE_MULTICAST = 0x2, }; /* reg_sfd_rec_type @@ -379,7 +380,6 @@ MLXSW_ITEM32_INDEXED(reg, sfd, uc_system_port, MLXSW_REG_SFD_BASE_LEN, 0, 16, static inline void mlxsw_reg_sfd_rec_pack(char *payload, int rec_index, enum mlxsw_reg_sfd_rec_type rec_type, - enum mlxsw_reg_sfd_rec_policy policy, const char *mac, enum mlxsw_reg_sfd_rec_action action) { @@ -389,7 +389,6 @@ static inline void mlxsw_reg_sfd_rec_pack(char *payload, int rec_index, mlxsw_reg_sfd_num_rec_set(payload, rec_index + 1); mlxsw_reg_sfd_rec_swid_set(payload, rec_index, 0); mlxsw_reg_sfd_rec_type_set(payload, rec_index, rec_type); - mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy); mlxsw_reg_sfd_rec_mac_memcpy_to(payload, rec_index, mac); mlxsw_reg_sfd_rec_action_set(payload, rec_index, action); } @@ -401,8 +400,8 @@ static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index, u8 local_port) { mlxsw_reg_sfd_rec_pack(payload, rec_index, - MLXSW_REG_SFD_REC_TYPE_UNICAST, - policy, mac, action); + MLXSW_REG_SFD_REC_TYPE_UNICAST, mac, action); + mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy); mlxsw_reg_sfd_uc_sub_port_set(payload, rec_index, 0); mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, fid_vid); mlxsw_reg_sfd_uc_system_port_set(payload, rec_index, local_port); @@ -461,7 +460,8 @@ mlxsw_reg_sfd_uc_lag_pack(char *payload, int rec_index, { mlxsw_reg_sfd_rec_pack(payload, rec_index, MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG, - policy, mac, action); + mac, action); + mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy); mlxsw_reg_sfd_uc_lag_sub_port_set(payload, rec_index, 0); mlxsw_reg_sfd_uc_lag_fid_vid_set(payload, rec_index, fid_vid); mlxsw_reg_sfd_uc_lag_lag_vid_set(payload, rec_index, lag_vid); @@ -477,6 +477,45 @@ static inline void mlxsw_reg_sfd_uc_lag_unpack(char *payload, int rec_index, *p_lag_id = mlxsw_reg_sfd_uc_lag_lag_id_get(payload, rec_index); } +/* reg_sfd_mc_pgi + * + * Multicast port group index - index into the port group table. + * Value 0x1FFF indicates the pgi should point to the MID entry. + * For Spectrum this value must be set to 0x1FFF + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, mc_pgi, MLXSW_REG_SFD_BASE_LEN, 16, 13, + MLXSW_REG_SFD_REC_LEN, 0x08, false); + +/* reg_sfd_mc_fid_vid + * + * Filtering ID or VLAN ID + * Access: Index + */ +MLXSW_ITEM32_INDEXED(reg, sfd, mc_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16, + MLXSW_REG_SFD_REC_LEN, 0x08, false); + +/* reg_sfd_mc_mid + * + * Multicast identifier - global identifier that represents the multicast + * group across all devices. + * Access: RW + */ +MLXSW_ITEM32_INDEXED(reg, sfd, mc_mid, MLXSW_REG_SFD_BASE_LEN, 0, 16, + MLXSW_REG_SFD_REC_LEN, 0x0C, false); + +static inline void +mlxsw_reg_sfd_mc_pack(char *payload, int rec_index, + const char *mac, u16 fid_vid, + enum mlxsw_reg_sfd_rec_action action, u16 mid) +{ + mlxsw_reg_sfd_rec_pack(payload, rec_index, + MLXSW_REG_SFD_REC_TYPE_MULTICAST, mac, action); + mlxsw_reg_sfd_mc_pgi_set(payload, rec_index, 0x1FFF); + mlxsw_reg_sfd_mc_fid_vid_set(payload, rec_index, fid_vid); + mlxsw_reg_sfd_mc_mid_set(payload, rec_index, mid); +} + /* SFN - Switch FDB Notification Register * ------------------------------------------- * The switch provides notifications on newly learned FDB entries and -- cgit v1.2.3-59-g8ed1b From fabe5483226e9c804837374dadcb472325950e72 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Sun, 10 Jan 2016 21:06:25 +0100 Subject: mlxsw: reg: Adding SMID register Adding back SMID register definition and packing. For each MC group a new SMID entry will be generated. Signed-off-by: Elad Raz Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 51 +++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 9b7945757c5d..0c5237264e3e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -99,6 +99,55 @@ static const struct mlxsw_reg_info mlxsw_reg_spad = { */ MLXSW_ITEM_BUF(reg, spad, base_mac, 0x02, 6); +/* SMID - Switch Multicast ID + * -------------------------- + * The MID record maps from a MID (Multicast ID), which is a unique identifier + * of the multicast group within the stacking domain, into a list of local + * ports into which the packet is replicated. + */ +#define MLXSW_REG_SMID_ID 0x2007 +#define MLXSW_REG_SMID_LEN 0x240 + +static const struct mlxsw_reg_info mlxsw_reg_smid = { + .id = MLXSW_REG_SMID_ID, + .len = MLXSW_REG_SMID_LEN, +}; + +/* reg_smid_swid + * Switch partition ID. + * Access: Index + */ +MLXSW_ITEM32(reg, smid, swid, 0x00, 24, 8); + +/* reg_smid_mid + * Multicast identifier - global identifier that represents the multicast group + * across all devices. + * Access: Index + */ +MLXSW_ITEM32(reg, smid, mid, 0x00, 0, 16); + +/* reg_smid_port + * Local port memebership (1 bit per port). + * Access: RW + */ +MLXSW_ITEM_BIT_ARRAY(reg, smid, port, 0x20, 0x20, 1); + +/* reg_smid_port_mask + * Local port mask (1 bit per port). + * Access: W + */ +MLXSW_ITEM_BIT_ARRAY(reg, smid, port_mask, 0x220, 0x20, 1); + +static inline void mlxsw_reg_smid_pack(char *payload, u16 mid, + u8 port, bool set) +{ + MLXSW_REG_ZERO(smid, payload); + mlxsw_reg_smid_swid_set(payload, 0); + mlxsw_reg_smid_mid_set(payload, mid); + mlxsw_reg_smid_port_set(payload, port, set); + mlxsw_reg_smid_port_mask_set(payload, port, 1); +} + /* SSPR - Switch System Port Record Register * ----------------------------------------- * Configures the system port to local port mapping. @@ -3052,6 +3101,8 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id) return "SGCR"; case MLXSW_REG_SPAD_ID: return "SPAD"; + case MLXSW_REG_SMID_ID: + return "SMID"; case MLXSW_REG_SSPR_ID: return "SSPR"; case MLXSW_REG_SFDAT_ID: -- cgit v1.2.3-59-g8ed1b From 53ae628316b8db6659bc8cef07698f624608c448 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Sun, 10 Jan 2016 21:06:26 +0100 Subject: mlxsw: Changing the maximum number of multicast group to a define Signed-off-by: Elad Raz Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index b6f365060dd7..d0e98b8a8832 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1939,7 +1939,7 @@ static struct mlxsw_config_profile mlxsw_sp_config_profile = { .used_max_port_per_lag = 1, .max_port_per_lag = MLXSW_SP_PORT_PER_LAG_MAX, .used_max_mid = 1, - .max_mid = 7000, + .max_mid = MLXSW_SP_MID_MAX, .used_max_pgt = 1, .max_pgt = 0, .used_max_system_port = 1, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 7601789dd522..d8ef097faaa4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -54,6 +54,8 @@ #define MLXSW_SP_LAG_MAX 64 #define MLXSW_SP_PORT_PER_LAG_MAX 16 +#define MLXSW_SP_MID_MAX 7000 + struct mlxsw_sp_port; struct mlxsw_sp_upper { -- cgit v1.2.3-59-g8ed1b From e4b6f6931c0840b6a9505415bd02bd0c8520e4c9 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Sun, 10 Jan 2016 21:06:27 +0100 Subject: mlxsw: Adding VID to FID translatation Adding a generic function that translate VID to FID. Signed-off-by: Elad Raz Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 30 ++++++++++++++-------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index d64559e4aff7..f45c8e468457 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -51,6 +51,23 @@ #include "core.h" #include "reg.h" +static u16 mlxsw_sp_port_vid_to_fid_get(struct mlxsw_sp_port *mlxsw_sp_port, + u16 vid) +{ + u16 fid = vid; + + if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { + u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port); + + fid = mlxsw_sp_vfid_to_fid(vfid); + } + + if (!fid) + fid = mlxsw_sp_port->pvid; + + return fid; +} + static struct mlxsw_sp_port * mlxsw_sp_port_orig_get(struct net_device *dev, struct mlxsw_sp_port *mlxsw_sp_port) @@ -641,22 +658,16 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_fdb *fdb, struct switchdev_trans *trans) { - u16 fid = fdb->vid; + u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, fdb->vid); u16 lag_vid = 0; if (switchdev_trans_ph_prepare(trans)) return 0; if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { - u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port); - - fid = mlxsw_sp_vfid_to_fid(vfid); lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port); } - if (!fid) - fid = mlxsw_sp_port->pvid; - if (!mlxsw_sp_port->lagged) return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port->mlxsw_sp, mlxsw_sp_port->local_port, @@ -787,13 +798,10 @@ static int mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_fdb *fdb) { - u16 fid = fdb->vid; + u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, fdb->vid); u16 lag_vid = 0; if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) { - u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port); - - fid = mlxsw_sp_vfid_to_fid(vfid); lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port); } -- cgit v1.2.3-59-g8ed1b From 3a49b4fde2a1384101bc80d66e41271bb37a66e7 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Sun, 10 Jan 2016 21:06:28 +0100 Subject: mlxsw: Adding layer 2 multicast support Add SWITCHDEV_OBJ_ID_PORT_MDB switchdev ops support. On first MDB insertion creates a new multicast group (MID) and add members port to the MID. Also add new MDB entry for the flooding-domain (fid-vid) and link the MDB entry to the newly constructed MC group. Signed-off-by: Elad Raz Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 1 + drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 13 ++ .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 176 +++++++++++++++++++++ 3 files changed, 190 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index d0e98b8a8832..ce6845d534a8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1858,6 +1858,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core, mlxsw_sp->bus_info = mlxsw_bus_info; INIT_LIST_HEAD(&mlxsw_sp->port_vfids.list); INIT_LIST_HEAD(&mlxsw_sp->br_vfids.list); + INIT_LIST_HEAD(&mlxsw_sp->br_mids.list); err = mlxsw_sp_base_mac_get(mlxsw_sp); if (err) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index d8ef097faaa4..199f91a62962 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -44,6 +44,7 @@ #include #include +#include "port.h" #include "core.h" #define MLXSW_SP_VFID_BASE VLAN_N_VID @@ -71,6 +72,14 @@ struct mlxsw_sp_vfid { u16 vid; }; +struct mlxsw_sp_mid { + struct list_head list; + unsigned char addr[ETH_ALEN]; + u16 vid; + u16 mid; + unsigned int ref_count; +}; + static inline u16 mlxsw_sp_vfid_to_fid(u16 vfid) { return MLXSW_SP_VFID_BASE + vfid; @@ -95,6 +104,10 @@ struct mlxsw_sp { struct list_head list; unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_VFID_BR_MAX)]; } br_vfids; + struct { + struct list_head list; + unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_MID_MAX)]; + } br_mids; unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)]; struct mlxsw_sp_port **ports; struct mlxsw_core *core; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index f45c8e468457..4cdc18e72222 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -679,6 +679,143 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port, true, false); } +static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr, + u16 fid, u16 mid, bool adding) +{ + char *sfd_pl; + int err; + + sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); + if (!sfd_pl) + return -ENOMEM; + + mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); + mlxsw_reg_sfd_mc_pack(sfd_pl, 0, addr, fid, + MLXSW_REG_SFD_REC_ACTION_NOP, mid); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); + kfree(sfd_pl); + return err; +} + +static int mlxsw_sp_port_smid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mid, + bool add, bool clear_all_ports) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char *smid_pl; + int err, i; + + smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL); + if (!smid_pl) + return -ENOMEM; + + mlxsw_reg_smid_pack(smid_pl, mid, mlxsw_sp_port->local_port, add); + if (clear_all_ports) { + for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) + if (mlxsw_sp->ports[i]) + mlxsw_reg_smid_port_mask_set(smid_pl, i, 1); + } + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl); + kfree(smid_pl); + return err; +} + +static struct mlxsw_sp_mid *__mlxsw_sp_mc_get(struct mlxsw_sp *mlxsw_sp, + const unsigned char *addr, + u16 vid) +{ + struct mlxsw_sp_mid *mid; + + list_for_each_entry(mid, &mlxsw_sp->br_mids.list, list) { + if (ether_addr_equal(mid->addr, addr) && mid->vid == vid) + return mid; + } + return NULL; +} + +static struct mlxsw_sp_mid *__mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp, + const unsigned char *addr, + u16 vid) +{ + struct mlxsw_sp_mid *mid; + u16 mid_idx; + + mid_idx = find_first_zero_bit(mlxsw_sp->br_mids.mapped, + MLXSW_SP_MID_MAX); + if (mid_idx == MLXSW_SP_MID_MAX) + return NULL; + + mid = kzalloc(sizeof(*mid), GFP_KERNEL); + if (!mid) + return NULL; + + set_bit(mid_idx, mlxsw_sp->br_mids.mapped); + ether_addr_copy(mid->addr, addr); + mid->vid = vid; + mid->mid = mid_idx; + mid->ref_count = 0; + list_add_tail(&mid->list, &mlxsw_sp->br_mids.list); + + return mid; +} + +static int __mlxsw_sp_mc_dec_ref(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_mid *mid) +{ + if (--mid->ref_count == 0) { + list_del(&mid->list); + clear_bit(mid->mid, mlxsw_sp->br_mids.mapped); + kfree(mid); + return 1; + } + return 0; +} + +static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, + const struct switchdev_obj_port_mdb *mdb, + struct switchdev_trans *trans) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + struct net_device *dev = mlxsw_sp_port->dev; + struct mlxsw_sp_mid *mid; + u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, mdb->vid); + int err = 0; + + if (switchdev_trans_ph_prepare(trans)) + return 0; + + mid = __mlxsw_sp_mc_get(mlxsw_sp, mdb->addr, mdb->vid); + if (!mid) { + mid = __mlxsw_sp_mc_alloc(mlxsw_sp, mdb->addr, mdb->vid); + if (!mid) { + netdev_err(dev, "Unable to allocate MC group\n"); + return -ENOMEM; + } + } + mid->ref_count++; + + err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, true, + mid->ref_count == 1); + if (err) { + netdev_err(dev, "Unable to set SMID\n"); + goto err_out; + } + + if (mid->ref_count == 1) { + err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb->addr, fid, mid->mid, + true); + if (err) { + netdev_err(dev, "Unable to set MC SFD\n"); + goto err_out; + } + } + + return 0; + +err_out: + __mlxsw_sp_mc_dec_ref(mlxsw_sp, mid); + return err; +} + static int mlxsw_sp_port_obj_add(struct net_device *dev, const struct switchdev_obj *obj, struct switchdev_trans *trans) @@ -704,6 +841,11 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev, SWITCHDEV_OBJ_PORT_FDB(obj), trans); break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + err = mlxsw_sp_port_mdb_add(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_MDB(obj), + trans); + break; default: err = -EOPNOTSUPP; break; @@ -817,6 +959,37 @@ mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port, false, false); } +static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + struct net_device *dev = mlxsw_sp_port->dev; + struct mlxsw_sp_mid *mid; + u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, mdb->vid); + u16 mid_idx; + int err = 0; + + mid = __mlxsw_sp_mc_get(mlxsw_sp, mdb->addr, mdb->vid); + if (!mid) { + netdev_err(dev, "Unable to remove port from MC DB\n"); + return -EINVAL; + } + + err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, false, false); + if (err) + netdev_err(dev, "Unable to remove port from SMID\n"); + + mid_idx = mid->mid; + if (__mlxsw_sp_mc_dec_ref(mlxsw_sp, mid)) { + err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb->addr, fid, mid_idx, + false); + if (err) + netdev_err(dev, "Unable to remove MC SFD\n"); + } + + return err; +} + static int mlxsw_sp_port_obj_del(struct net_device *dev, const struct switchdev_obj *obj) { @@ -839,6 +1012,9 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev, err = mlxsw_sp_port_fdb_static_del(mlxsw_sp_port, SWITCHDEV_OBJ_PORT_FDB(obj)); break; + case SWITCHDEV_OBJ_ID_PORT_MDB: + err = mlxsw_sp_port_mdb_del(mlxsw_sp_port, + SWITCHDEV_OBJ_PORT_MDB(obj)); default: err = -EOPNOTSUPP; break; -- cgit v1.2.3-59-g8ed1b From 787d7ac308ff2279e4b2ea393ad4d990de486ef2 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Thu, 7 Jan 2016 14:28:39 +0100 Subject: udp: restrict offloads to one namespace udp tunnel offloads tend to aggregate datagrams based on inner headers. gro engine gets notified by tunnel implementations about possible offloads. The match is solely based on the port number. Imagine a tunnel bound to port 53, the offloading will look into all DNS packets and tries to aggregate them based on the inner data found within. This could lead to data corruption and malformed DNS packets. While this patch minimizes the problem and helps an administrator to find the issue by querying ip tunnel/fou, a better way would be to match on the specific destination ip address so if a user space socket is bound to the same address it will conflict. Cc: Tom Herbert Cc: Eric Dumazet Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- drivers/net/geneve.c | 2 +- drivers/net/vxlan.c | 2 +- include/net/protocol.h | 2 +- net/ipv4/fou.c | 2 +- net/ipv4/udp_offload.c | 10 +++++++--- 5 files changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 58efdec12f30..db96b0cbb8ba 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -376,7 +376,7 @@ static void geneve_notify_add_rx_port(struct geneve_sock *gs) int err; if (sa_family == AF_INET) { - err = udp_add_offload(&gs->udp_offloads); + err = udp_add_offload(sock_net(sk), &gs->udp_offloads); if (err) pr_warn("geneve: udp_add_offload failed with status %d\n", err); diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 405a7b6cca25..e1e147f2d6ce 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -621,7 +621,7 @@ static void vxlan_notify_add_rx_port(struct vxlan_sock *vs) int err; if (sa_family == AF_INET) { - err = udp_add_offload(&vs->udp_offloads); + err = udp_add_offload(net, &vs->udp_offloads); if (err) pr_warn("vxlan: udp_add_offload failed with status %d\n", err); } diff --git a/include/net/protocol.h b/include/net/protocol.h index d6fcc1fcdb5b..da689f5432de 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -107,7 +107,7 @@ int inet_del_offload(const struct net_offload *prot, unsigned char num); void inet_register_protosw(struct inet_protosw *p); void inet_unregister_protosw(struct inet_protosw *p); -int udp_add_offload(struct udp_offload *prot); +int udp_add_offload(struct net *net, struct udp_offload *prot); void udp_del_offload(struct udp_offload *prot); #if IS_ENABLED(CONFIG_IPV6) diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index bd903fe0f750..976f0dcf6991 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c @@ -498,7 +498,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, sk->sk_allocation = GFP_ATOMIC; if (cfg->udp_config.family == AF_INET) { - err = udp_add_offload(&fou->udp_offloads); + err = udp_add_offload(net, &fou->udp_offloads); if (err) goto error; } diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index f9386160cbee..5d396b96ae8b 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -21,6 +21,7 @@ static struct udp_offload_priv __rcu *udp_offload_base __read_mostly; struct udp_offload_priv { struct udp_offload *offload; + possible_net_t net; struct rcu_head rcu; struct udp_offload_priv __rcu *next; }; @@ -241,13 +242,14 @@ out: return segs; } -int udp_add_offload(struct udp_offload *uo) +int udp_add_offload(struct net *net, struct udp_offload *uo) { struct udp_offload_priv *new_offload = kzalloc(sizeof(*new_offload), GFP_ATOMIC); if (!new_offload) return -ENOMEM; + write_pnet(&new_offload->net, net); new_offload->offload = uo; spin_lock(&udp_offload_lock); @@ -311,7 +313,8 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb, rcu_read_lock(); uo_priv = rcu_dereference(udp_offload_base); for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) { - if (uo_priv->offload->port == uh->dest && + if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) && + uo_priv->offload->port == uh->dest && uo_priv->offload->callbacks.gro_receive) goto unflush; } @@ -389,7 +392,8 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff) uo_priv = rcu_dereference(udp_offload_base); for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) { - if (uo_priv->offload->port == uh->dest && + if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) && + uo_priv->offload->port == uh->dest && uo_priv->offload->callbacks.gro_complete) break; } -- cgit v1.2.3-59-g8ed1b From 18715b261541f35ccede9b8686ee3ebaac697d38 Mon Sep 17 00:00:00 2001 From: Kristian Evensen Date: Thu, 7 Jan 2016 16:41:33 +0100 Subject: net: qmi_wwan: Add SIMCom 7230E MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SIMCom 7230E is a QMI LTE module with support for most "normal" bands. Manual testing has showed that only interface five works. Cc: Bjørn Mork Signed-off-by: Kristian Evensen Acked-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 5fccc5a8153f..772b9d093f75 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -743,6 +743,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x413c, 0x81b1, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ {QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */ + {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ -- cgit v1.2.3-59-g8ed1b From 6c672c9bf44e718ccc8bb137032a57070980bb4e Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Thu, 7 Jan 2016 21:13:28 +0100 Subject: stmmac: Don't exit mdio registration when mdio subnode is not found in the DTS Originally, most of the platforms using this driver did not define an mdio subnode in the devicetree. Commit e34d65 ("stmmac: create of compatible mdio bus for stmmac driver") introduced a backward compatibily issue by using of_mdiobus_register explicitly with an mdio subnode. This patch fixes the issue by calling the function mdiobus_register, when mdio subnode is not found. The driver is now compatible with both modes. Fixes: e34d65696d2e ("stmmac: create of compatible mdio bus for stmmac driver") Signed-off-by: Romain Perier Tested-by: Phil Reid Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index bff28595b427..0faf16336035 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -217,8 +217,7 @@ int stmmac_mdio_register(struct net_device *ndev) if (mdio_node) { netdev_dbg(ndev, "FOUND MDIO subnode\n"); } else { - netdev_err(ndev, "NO MDIO subnode\n"); - return 0; + netdev_warn(ndev, "No MDIO subnode found\n"); } } @@ -244,7 +243,10 @@ int stmmac_mdio_register(struct net_device *ndev) new_bus->phy_mask = mdio_bus_data->phy_mask; new_bus->parent = priv->device; - err = of_mdiobus_register(new_bus, mdio_node); + if (mdio_node) + err = of_mdiobus_register(new_bus, mdio_node); + else + err = mdiobus_register(new_bus); if (err != 0) { pr_err("%s: Cannot register as MDIO bus\n", new_bus->name); goto bus_register_fail; -- cgit v1.2.3-59-g8ed1b From 2a4fc4ea29fbe4c59cf67d73cca309d9fe7e1fa5 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 7 Jan 2016 21:55:51 +0100 Subject: net: lantiq_etop.c: Use helper to find first phy Make use of the helper to find the first phy device. This also fixes the compile breakage. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/lantiq_etop.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index fb61f7f96bb4..b630ef1e9646 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -375,15 +375,9 @@ static int ltq_etop_mdio_probe(struct net_device *dev) { struct ltq_etop_priv *priv = netdev_priv(dev); - struct phy_device *phydev = NULL; - int phy_addr; + struct phy_device *phydev; - for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { - if (priv->mii_bus->phy_map[phy_addr]) { - phydev = priv->mii_bus->phy_map[phy_addr]; - break; - } - } + phydev = phy_find_first(priv->mii_bus); if (!phydev) { netdev_err(dev, "no PHY found\n"); -- cgit v1.2.3-59-g8ed1b From 0c129bf756f869dc386005c59c2f815305dccc86 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 7 Jan 2016 21:55:52 +0100 Subject: net: ethernet-rgmii.c: Fix breakage from moving phdev bus The mdio device patches moved the bus member in phy_device into a substructure. This driver got missed. Fix it. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/staging/octeon/ethernet-rgmii.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c index 613344b886e1..1055ee14b66a 100644 --- a/drivers/staging/octeon/ethernet-rgmii.c +++ b/drivers/staging/octeon/ethernet-rgmii.c @@ -78,7 +78,7 @@ static void cvm_oct_rgmii_poll(struct net_device *dev) */ spin_lock_irqsave(&global_register_lock, flags); } else { - mutex_lock(&priv->phydev->bus->mdio_lock); + mutex_lock(&priv->phydev->mdio.bus->mdio_lock); } link_info = cvmx_helper_link_get(priv->port); @@ -113,7 +113,7 @@ static void cvm_oct_rgmii_poll(struct net_device *dev) if (use_global_register_lock) spin_unlock_irqrestore(&global_register_lock, flags); else - mutex_unlock(&priv->phydev->bus->mdio_lock); + mutex_unlock(&priv->phydev->mdio.bus->mdio_lock); return; } @@ -132,7 +132,7 @@ static void cvm_oct_rgmii_poll(struct net_device *dev) if (use_global_register_lock) spin_unlock_irqrestore(&global_register_lock, flags); else - mutex_unlock(&priv->phydev->bus->mdio_lock); + mutex_unlock(&priv->phydev->mdio.bus->mdio_lock); if (priv->phydev == NULL) { /* Tell core. */ -- cgit v1.2.3-59-g8ed1b From ee64f08ea9832b8127aeb808411e350bc4463399 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 10 Jan 2016 12:03:16 -0800 Subject: net: ethernet: broadcom: Fix build errors Commit 7f854420fbfe ("phy: Add API for {un}registering an mdio device to a bus") introduces an API to access mii_bus structures, but missed to update the sb1250 driver. This results in the following build error. drivers/net/ethernet/broadcom/sb1250-mac.c: In function 'sbmac_mii_probe': drivers/net/ethernet/broadcom/sb1250-mac.c:2360:24: error: 'struct mii_bus' has no member named 'phy_map' Use phy_find_first() instead of open coding it. Commit 2220943a21e2 ("phy: Centralise print about attached phy") introduces the following build error. drivers/net/ethernet/broadcom/sb1250-mac.c: In function 'sbmac_mii_probe': drivers/net/ethernet/broadcom/sb1250-mac.c:2383:20: error: 'phydev' undeclared Fixes: 7f854420fbfe ("phy: Add API for {un}registering an mdio device to a bus") Fixes: 2220943a21e2 ("phy: Centralise print about attached phy") Cc: Andrew Lunn Signed-off-by: Guenter Roeck Acked-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/sb1250-mac.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index 768c18da510c..eacc559679bf 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -2354,13 +2354,8 @@ static int sbmac_mii_probe(struct net_device *dev) { struct sbmac_softc *sc = netdev_priv(dev); struct phy_device *phy_dev; - int i; - for (i = 0; i < PHY_MAX_ADDR; i++) { - phy_dev = sc->mii_bus->phy_map[i]; - if (phy_dev) - break; - } + phy_dev = phy_find_first(sc->mii_bus); if (!phy_dev) { printk(KERN_ERR "%s: no PHY found\n", dev->name); return -ENXIO; @@ -2385,7 +2380,7 @@ static int sbmac_mii_probe(struct net_device *dev) SUPPORTED_Pause | SUPPORTED_Asym_Pause; - phy_attached_info(phydev); + phy_attached_info(phy_dev); phy_dev->advertising = phy_dev->supported; -- cgit v1.2.3-59-g8ed1b From e574f39816f0227e3f43806952c8bc551d61770a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 10 Jan 2016 12:04:32 -0800 Subject: net: ethernet: faraday: Use phy_find_first() instead of open coding it Use phy_find_first() to find the first phy device instead of open coding it. Cc: Andrew Lunn Signed-off-by: Guenter Roeck Acked-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index bb116ad646f6..84384e1585a5 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -834,20 +834,9 @@ static void ftgmac100_adjust_link(struct net_device *netdev) static int ftgmac100_mii_probe(struct ftgmac100 *priv) { struct net_device *netdev = priv->netdev; - struct phy_device *phydev = NULL; - int i; - - /* search for connect PHY device */ - for (i = 0; i < PHY_MAX_ADDR; i++) { - struct phy_device *tmp = mdiobus_get_phy(priv->mii_bus, i); - - if (tmp) { - phydev = tmp; - break; - } - } + struct phy_device *phydev; - /* now we are supposed to have a proper phydev, to attach to... */ + phydev = phy_find_first(priv->mii_bus); if (!phydev) { netdev_info(netdev, "%s: no PHY found\n", netdev->name); return -ENODEV; -- cgit v1.2.3-59-g8ed1b From 3fe01e2406ead2bdc4ae436254c66b5341c1e5c7 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 11 Jan 2016 00:57:43 +0100 Subject: staging: netlogic: Fix build error due to missed API change Fix a number of build errors due to moving the phy_map and centralizing interrupt allocation. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/staging/netlogic/xlr_net.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/netlogic/xlr_net.c b/drivers/staging/netlogic/xlr_net.c index cbc25b7e70a2..0b4e819f5164 100644 --- a/drivers/staging/netlogic/xlr_net.c +++ b/drivers/staging/netlogic/xlr_net.c @@ -165,13 +165,18 @@ static void xlr_net_fmn_handler(int bkt, int src_stnid, int size, } } +static struct phy_device *xlr_get_phydev(struct xlr_net_priv *priv) +{ + return mdiobus_get_phy(priv->mii_bus, priv->phy_addr); +} + /* * Ethtool operation */ static int xlr_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) { struct xlr_net_priv *priv = netdev_priv(ndev); - struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr]; + struct phy_device *phydev = xlr_get_phydev(priv); if (!phydev) return -ENODEV; @@ -181,7 +186,7 @@ static int xlr_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) static int xlr_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd) { struct xlr_net_priv *priv = netdev_priv(ndev); - struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr]; + struct phy_device *phydev = xlr_get_phydev(priv); if (!phydev) return -ENODEV; @@ -218,7 +223,7 @@ static int xlr_net_open(struct net_device *ndev) { u32 err; struct xlr_net_priv *priv = netdev_priv(ndev); - struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr]; + struct phy_device *phydev = xlr_get_phydev(priv); /* schedule a link state check */ phy_start(phydev); @@ -239,7 +244,7 @@ static int xlr_net_open(struct net_device *ndev) static int xlr_net_stop(struct net_device *ndev) { struct xlr_net_priv *priv = netdev_priv(ndev); - struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr]; + struct phy_device *phydev = xlr_get_phydev(priv); phy_stop(phydev); netif_tx_stop_all_queues(ndev); @@ -268,7 +273,7 @@ static void __maybe_unused xlr_wakeup_queue(unsigned long dev) { struct net_device *ndev = (struct net_device *) dev; struct xlr_net_priv *priv = netdev_priv(ndev); - struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr]; + struct phy_device *phydev = xlr_get_phydev(priv); if (phydev->link) netif_tx_wake_queue(netdev_get_tx_queue(ndev, priv->wakeup_q)); @@ -771,7 +776,7 @@ static void xlr_sgmii_init(struct xlr_net_priv *priv) void xlr_set_gmac_speed(struct xlr_net_priv *priv) { - struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr]; + struct phy_device *phydev = xlr_get_phydev(priv); int speed; if (phydev->interface == PHY_INTERFACE_MODE_SGMII) @@ -813,7 +818,7 @@ void xlr_set_gmac_speed(struct xlr_net_priv *priv) static void xlr_gmac_link_adjust(struct net_device *ndev) { struct xlr_net_priv *priv = netdev_priv(ndev); - struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr]; + struct phy_device *phydev = xlr_get_phydev(priv); u32 intreg; intreg = xlr_nae_rdreg(priv->base_addr, R_INTREG); @@ -830,7 +835,7 @@ static void xlr_gmac_link_adjust(struct net_device *ndev) static int xlr_mii_probe(struct xlr_net_priv *priv) { - struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr]; + struct phy_device *phydev = xlr_get_phydev(priv); if (!phydev) { pr_err("no PHY found on phy_addr %d\n", priv->phy_addr); @@ -876,14 +881,6 @@ static int xlr_setup_mdio(struct xlr_net_priv *priv, priv->mii_bus->read = xlr_mii_read; priv->mii_bus->write = xlr_mii_write; priv->mii_bus->parent = &pdev->dev; - priv->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); - if (priv->mii_bus->irq == NULL) { - pr_err("irq alloc failed\n"); - mdiobus_free(priv->mii_bus); - return -ENOMEM; - } - - priv->mii_bus->irq[priv->phy_addr] = priv->ndev->irq; /* Scan only the enabled address */ priv->mii_bus->phy_mask = ~(1 << priv->phy_addr); -- cgit v1.2.3-59-g8ed1b From ede5599753802b5733f671e5f14403ae1eadce5a Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 11 Jan 2016 01:13:21 +0100 Subject: ethernet: amd: au1000: Remove pointless warning The warning about being able to read any MDIO device, not just the attached ethernet devices PHY applies to all MDIO drivers. So remove it. This also removes a reference to a member in phy_device which has moved. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/au1000_eth.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index c8640418fc37..d3977d032b48 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -344,9 +344,6 @@ static void au1000_mdio_write(struct net_device *dev, int phy_addr, static int au1000_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) { - /* WARNING: bus->phy_map[phy_addr].attached_dev == dev does - * _NOT_ hold (e.g. when PHY is accessed through other MAC's MII bus) - */ struct net_device *const dev = bus->priv; /* make sure the MAC associated with this -- cgit v1.2.3-59-g8ed1b From d79979a103f7820d3107cdc04096e87b37f90008 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Thu, 7 Jan 2016 19:56:57 -0500 Subject: bnxt_en: Zero pad firmware messages to 128 bytes. For future compatibility, zero pad all messages that the driver sends to the firmware to 128 bytes. If these messages are extended in the future with new byte enables, zero padding these messages now will guarantee future compatibility. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index f9569493034b..287bfb79ea2d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -2614,6 +2614,9 @@ int _hwrm_send_message(struct bnxt *bp, void *msg, u32 msg_len, int timeout) /* Write request msg to hwrm channel */ __iowrite32_copy(bp->bar0, data, msg_len / 4); + for (i = msg_len; i < HWRM_MAX_REQ_LEN; i += 4) + writel(0, bp->bar0 + i); + /* currently supports only one outstanding message */ if (intr_process) bp->hwrm_intr_seq_id = le32_to_cpu(req->target_id_seq_id) & -- cgit v1.2.3-59-g8ed1b From d2d6318cb996f39112ba24ff23abe67578a611bc Mon Sep 17 00:00:00 2001 From: Rob Swindell Date: Thu, 7 Jan 2016 19:56:58 -0500 Subject: bnxt_en: Reset embedded processor after applying firmware upgrade Use HWRM_FW_RESET command to request a self-reset of the embedded processor(s) after successfully applying a firmware update. For boot processor, the self-reset is currently deferred until the next PCIe reset. Signed-off-by: Rob Swindell Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 46 +++++++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 4f62c9fa96d5..922b898e7a32 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -837,6 +837,45 @@ static int bnxt_flash_nvram(struct net_device *dev, return rc; } +static int bnxt_firmware_reset(struct net_device *dev, + u16 dir_type) +{ + struct bnxt *bp = netdev_priv(dev); + struct hwrm_fw_reset_input req = {0}; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FW_RESET, -1, -1); + + /* TODO: Support ASAP ChiMP self-reset (e.g. upon PF driver unload) */ + /* TODO: Address self-reset of APE/KONG/BONO/TANG or ungraceful reset */ + /* (e.g. when firmware isn't already running) */ + switch (dir_type) { + case BNX_DIR_TYPE_CHIMP_PATCH: + case BNX_DIR_TYPE_BOOTCODE: + case BNX_DIR_TYPE_BOOTCODE_2: + req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT; + /* Self-reset ChiMP upon next PCIe reset: */ + req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST; + break; + case BNX_DIR_TYPE_APE_FW: + case BNX_DIR_TYPE_APE_PATCH: + req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT; + break; + case BNX_DIR_TYPE_KONG_FW: + case BNX_DIR_TYPE_KONG_PATCH: + req.embedded_proc_type = + FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL; + break; + case BNX_DIR_TYPE_BONO_FW: + case BNX_DIR_TYPE_BONO_PATCH: + req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE; + break; + default: + return -EINVAL; + } + + return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); +} + static int bnxt_flash_firmware(struct net_device *dev, u16 dir_type, const u8 *fw_data, @@ -894,10 +933,9 @@ static int bnxt_flash_firmware(struct net_device *dev, /* TODO: Validate digital signature (RSA-encrypted SHA-256 hash) here */ rc = bnxt_flash_nvram(dev, dir_type, BNX_DIR_ORDINAL_FIRST, 0, 0, fw_data, fw_size); - if (rc == 0) { /* Firmware update successful */ - /* TODO: Notify processor it needs to reset itself - */ - } + if (rc == 0) /* Firmware update successful */ + rc = bnxt_firmware_reset(dev, dir_type); + return rc; } -- cgit v1.2.3-59-g8ed1b From c9bca2fe3ca6d6dac2a27eb8619955648369efed Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Fri, 8 Jan 2016 09:35:00 +0800 Subject: net: ethernet: arc: Probe emac after set RMII clock After enter arc_emac_probe, emac will get_phy_id, phy_poll_reset and other connecting PHY via mdiobus_read, so we need to set correct ref clock rate for emac before probe emac. Signed-off-by: Xing Zheng Signed-off-by: David S. Miller --- drivers/net/ethernet/arc/emac_rockchip.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c index c31c7407b753..36e9eb1ee44c 100644 --- a/drivers/net/ethernet/arc/emac_rockchip.c +++ b/drivers/net/ethernet/arc/emac_rockchip.c @@ -164,10 +164,6 @@ static int emac_rockchip_probe(struct platform_device *pdev) } } - err = arc_emac_probe(ndev, interface); - if (err) - goto out_regulator_disable; - /* write-enable bits */ data = GRF_MODE_ENABLE_BIT | GRF_SPEED_ENABLE_BIT; @@ -184,6 +180,13 @@ static int emac_rockchip_probe(struct platform_device *pdev) err = clk_set_rate(priv->refclk, 50000000); if (err) dev_err(dev, "failed to change reference clock rate (%d)\n", err); + + err = arc_emac_probe(ndev, interface); + if (err) { + dev_err(dev, "failed to probe arc emac (%d)\n", err); + goto out_regulator_disable; + } + return 0; out_regulator_disable: -- cgit v1.2.3-59-g8ed1b From f4c9d3ee0334fd9c6c8f2c3a8bd7d0841880e127 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Fri, 8 Jan 2016 09:35:01 +0800 Subject: net: ethernet: arc: Keep emac compatibility for more Rockchip SoCs On the RK3066/RK3188, there was fixed GRF offset configuration to set emac and fixed DIV2 mac TX/RX clock. So, we need to easily set and fit to other SoCs (RK3036) which maybe have different GRF offset, and need adjust mac TX/RX clock. Signed-off-by: Xing Zheng Signed-off-by: David S. Miller --- drivers/net/ethernet/arc/emac_rockchip.c | 69 +++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c index 36e9eb1ee44c..e951c8fab778 100644 --- a/drivers/net/ethernet/arc/emac_rockchip.c +++ b/drivers/net/ethernet/arc/emac_rockchip.c @@ -25,17 +25,13 @@ #include "emac.h" #define DRV_NAME "rockchip_emac" -#define DRV_VERSION "1.0" - -#define GRF_MODE_MII (1UL << 0) -#define GRF_MODE_RMII (0UL << 0) -#define GRF_SPEED_10M (0UL << 1) -#define GRF_SPEED_100M (1UL << 1) -#define GRF_SPEED_ENABLE_BIT (1UL << 17) -#define GRF_MODE_ENABLE_BIT (1UL << 16) +#define DRV_VERSION "1.1" struct emac_rockchip_soc_data { - int grf_offset; + unsigned int grf_offset; + unsigned int grf_mode_offset; + unsigned int grf_speed_offset; + bool need_div_macclk; }; struct rockchip_priv_data { @@ -44,23 +40,22 @@ struct rockchip_priv_data { const struct emac_rockchip_soc_data *soc_data; struct regulator *regulator; struct clk *refclk; + struct clk *macclk; }; static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed) { struct rockchip_priv_data *emac = priv; + u32 speed_offset = emac->soc_data->grf_speed_offset; u32 data; int err = 0; - /* write-enable bits */ - data = GRF_SPEED_ENABLE_BIT; - switch(speed) { case 10: - data |= GRF_SPEED_10M; + data = (1 << (speed_offset + 16)) | (0 << speed_offset); break; case 100: - data |= GRF_SPEED_100M; + data = (1 << (speed_offset + 16)) | (1 << speed_offset); break; default: pr_err("speed %u not supported\n", speed); @@ -72,14 +67,19 @@ static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed) pr_err("unable to apply speed %u to grf (%d)\n", speed, err); } -static const struct emac_rockchip_soc_data emac_rockchip_dt_data[] = { - { .grf_offset = 0x154 }, /* rk3066 */ - { .grf_offset = 0x0a4 }, /* rk3188 */ +static const struct emac_rockchip_soc_data emac_rk3066_emac_data = { + .grf_offset = 0x154, .grf_mode_offset = 0, + .grf_speed_offset = 1, .need_div_macclk = 0, +}; + +static const struct emac_rockchip_soc_data emac_rk3188_emac_data = { + .grf_offset = 0x0a4, .grf_mode_offset = 0, + .grf_speed_offset = 1, .need_div_macclk = 0, }; static const struct of_device_id emac_rockchip_dt_ids[] = { - { .compatible = "rockchip,rk3066-emac", .data = &emac_rockchip_dt_data[0] }, - { .compatible = "rockchip,rk3188-emac", .data = &emac_rockchip_dt_data[1] }, + { .compatible = "rockchip,rk3066-emac", .data = &emac_rk3066_emac_data }, + { .compatible = "rockchip,rk3188-emac", .data = &emac_rk3188_emac_data }, { /* Sentinel */ } }; @@ -164,11 +164,12 @@ static int emac_rockchip_probe(struct platform_device *pdev) } } - /* write-enable bits */ - data = GRF_MODE_ENABLE_BIT | GRF_SPEED_ENABLE_BIT; - - data |= GRF_SPEED_100M; - data |= GRF_MODE_RMII; + /* Set speed 100M */ + data = (1 << (priv->soc_data->grf_speed_offset + 16)) | + (1 << priv->soc_data->grf_speed_offset); + /* Set RMII mode */ + data |= (1 << (priv->soc_data->grf_mode_offset + 16)) | + (0 << priv->soc_data->grf_mode_offset); err = regmap_write(priv->grf, priv->soc_data->grf_offset, data); if (err) { @@ -181,6 +182,26 @@ static int emac_rockchip_probe(struct platform_device *pdev) if (err) dev_err(dev, "failed to change reference clock rate (%d)\n", err); + if (priv->soc_data->need_div_macclk) { + priv->macclk = devm_clk_get(dev, "macclk"); + if (IS_ERR(priv->macclk)) { + dev_err(dev, "failed to retrieve mac clock (%ld)\n", PTR_ERR(priv->macclk)); + err = PTR_ERR(priv->macclk); + goto out_regulator_disable; + } + + err = clk_prepare_enable(priv->macclk); + if (err) { + dev_err(dev, "failed to enable mac clock (%d)\n", err); + goto out_regulator_disable; + } + + /* RMII TX/RX needs always a rate of 25MHz */ + err = clk_set_rate(priv->macclk, 25000000); + if (err) + dev_err(dev, "failed to change mac clock rate (%d)\n", err); + } + err = arc_emac_probe(ndev, interface); if (err) { dev_err(dev, "failed to probe arc emac (%d)\n", err); -- cgit v1.2.3-59-g8ed1b From af72261f33ee4958bb53e299746014f44e1134c4 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Fri, 8 Jan 2016 09:35:02 +0800 Subject: net: ethernet: arc: Add support emac for RK3036 The RK3036's GRFs offset are different with RK3066/RK3188, and need to set mac TX/RX clock before probe emac. Signed-off-by: Xing Zheng Signed-off-by: David S. Miller --- drivers/net/ethernet/arc/Kconfig | 4 ++-- drivers/net/ethernet/arc/emac_rockchip.c | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/arc/Kconfig b/drivers/net/ethernet/arc/Kconfig index 52a6b16f57d2..689045186064 100644 --- a/drivers/net/ethernet/arc/Kconfig +++ b/drivers/net/ethernet/arc/Kconfig @@ -34,9 +34,9 @@ config EMAC_ROCKCHIP select ARC_EMAC_CORE depends on OF_IRQ && OF_NET && REGULATOR && HAS_DMA ---help--- - Support for Rockchip RK3066/RK3188 EMAC ethernet controllers. + Support for Rockchip RK3036/RK3066/RK3188 EMAC ethernet controllers. This selects Rockchip SoC glue layer support for the - emac device driver. This driver is used for RK3066/RK3188 + emac device driver. This driver is used for RK3036/RK3066/RK3188 EMAC ethernet controller. endif # NET_VENDOR_ARC diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c index e951c8fab778..85e821ccfcd2 100644 --- a/drivers/net/ethernet/arc/emac_rockchip.c +++ b/drivers/net/ethernet/arc/emac_rockchip.c @@ -67,6 +67,11 @@ static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed) pr_err("unable to apply speed %u to grf (%d)\n", speed, err); } +static const struct emac_rockchip_soc_data emac_rk3036_emac_data = { + .grf_offset = 0x140, .grf_mode_offset = 8, + .grf_speed_offset = 9, .need_div_macclk = 1, +}; + static const struct emac_rockchip_soc_data emac_rk3066_emac_data = { .grf_offset = 0x154, .grf_mode_offset = 0, .grf_speed_offset = 1, .need_div_macclk = 0, @@ -78,6 +83,7 @@ static const struct emac_rockchip_soc_data emac_rk3188_emac_data = { }; static const struct of_device_id emac_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3036-emac", .data = &emac_rk3036_emac_data }, { .compatible = "rockchip,rk3066-emac", .data = &emac_rk3066_emac_data }, { .compatible = "rockchip,rk3188-emac", .data = &emac_rk3188_emac_data }, { /* Sentinel */ } @@ -110,7 +116,7 @@ static int emac_rockchip_probe(struct platform_device *pdev) interface = of_get_phy_mode(dev->of_node); - /* RK3066 and RK3188 SoCs only support RMII */ + /* RK3036/RK3066/RK3188 SoCs only support RMII */ if (interface != PHY_INTERFACE_MODE_RMII) { dev_err(dev, "unsupported phy interface mode %d\n", interface); err = -ENOTSUPP; -- cgit v1.2.3-59-g8ed1b From 89e7a154e01cffa325874e90cbb124057416de02 Mon Sep 17 00:00:00 2001 From: Hariprasad Shenai Date: Fri, 8 Jan 2016 12:21:25 +0530 Subject: cxgb4: Fixes static checker warning in mps_tcam_show() The commit 115b56af88b5 ("cxgb4: Update mps_tcam output to include T6 fields") from Dec 23, 2015, leads to the following static checker warning: drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c:1735 mps_tcam_show() warn: we tested 'lookup_type' before and it was 'true' Fixing it. Reported-by: Dan Carpenter Signed-off-by: Hariprasad Shenai Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 1bab34f923e7..e6a4072b494b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -1726,13 +1726,13 @@ static int mps_tcam_show(struct seq_file *seq, void *v) seq_printf(seq, "%3u %02x:%02x:%02x:%02x:%02x:%02x " "%012llx %06x %06x - - %3c" - " %3c %4x " + " 'I' %4x " "%3c %#x%4u%4d", idx, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], (unsigned long long)mask, vniy, vnix, dip_hit ? 'Y' : 'N', - lookup_type ? 'I' : 'O', port_num, + port_num, (cls_lo & T6_SRAM_VLD_F) ? 'Y' : 'N', PORTMAP_G(cls_hi), T6_PF_G(cls_lo), -- cgit v1.2.3-59-g8ed1b From 053842a8bffed47fab386954e2e9d0a00e13ae2c Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 8 Jan 2016 16:58:14 +0530 Subject: phy: fix blackfin build failure The build of blackfin defconfig is failing with the error: error: 'struct mii_bus' has no member named 'phy_map' A new API mdiobus_get_phy() was introduced and phy_map was removed but it was not changed here. Fixes: 7f854420fbfe ("phy: Add API for {un}registering an mdio device to a bus.") Cc: Andrew Lunn Signed-off-by: Sudip Mukherjee Acked-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/adi/bfin_mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index 62862744c870..6f3432ae6703 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -398,7 +398,8 @@ static int mii_probe(struct net_device *dev, int phy_mode) /* search for connected PHY device */ for (i = 0; i < PHY_MAX_ADDR; ++i) { - struct phy_device *const tmp_phydev = lp->mii_bus->phy_map[i]; + struct phy_device *const tmp_phydev = + mdiobus_get_phy(lp->mii_bus, i); if (!tmp_phydev) continue; /* no PHY here... */ -- cgit v1.2.3-59-g8ed1b From fdffd2e8c7b361a831dc023c22bb8f4685c241ac Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Fri, 8 Jan 2016 16:58:15 +0530 Subject: bfin_mac: fix error path While building blackfin defconfig we were getting a build warning: warning: label 'out_err_irq_alloc' defined but not used. Commit e7f4dc3536a4 ("mdio: Move allocation of interrupts into core") removed the label out_err_mdiobus_register but then mistakenly jumped to out_err_alloc. But it was actually supposed to jump to out_err_irq_alloc. Fixes: e7f4dc3536a4 ("mdio: Move allocation of interrupts into core") Cc: Andrew Lunn Signed-off-by: Sudip Mukherjee Signed-off-by: David S. Miller --- drivers/net/ethernet/adi/bfin_mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index 6f3432ae6703..5ddfd0ed61d4 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -1857,7 +1857,7 @@ static int bfin_mii_bus_probe(struct platform_device *pdev) rc = mdiobus_register(miibus); if (rc) { dev_err(&pdev->dev, "Cannot register MDIO bus!\n"); - goto out_err_alloc; + goto out_err_irq_alloc; } platform_set_drvdata(pdev, miibus); -- cgit v1.2.3-59-g8ed1b From 541b8e291f29e480fb9c6400ce796c2eaefe9229 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 8 Jan 2016 10:19:16 -0200 Subject: net: lpc_eth: Remove unused variables Commit e7f4dc3536a400 ("mdio: Move allocation of interrupts into core") introduced the following build warnings: drivers/net/ethernet/nxp/lpc_eth.c: In function 'lpc_mii_init': drivers/net/ethernet/nxp/lpc_eth.c:865:1: warning: label 'err_out_1' defined but not used [-Wunused-label] drivers/net/ethernet/nxp/lpc_eth.c:826:20: warning: unused variable 'i' [-Wunused-variable] Remove the unused variables to fix them. Reported-by: Olof's autobuilder Signed-off-by: Fabio Estevam Acked-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/nxp/lpc_eth.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index a9ce37f9bb2e..b1ce7aaa8f8b 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -823,7 +823,7 @@ static int lpc_mii_probe(struct net_device *ndev) static int lpc_mii_init(struct netdata_local *pldat) { - int err = -ENXIO, i; + int err = -ENXIO; pldat->mii_bus = mdiobus_alloc(); if (!pldat->mii_bus) { @@ -862,7 +862,6 @@ static int lpc_mii_init(struct netdata_local *pldat) err_out_unregister_bus: mdiobus_unregister(pldat->mii_bus); -err_out_1: mdiobus_free(pldat->mii_bus); err_out: return err; -- cgit v1.2.3-59-g8ed1b From 26706d43b82b881bd10ce1428516e4a411748ddb Mon Sep 17 00:00:00 2001 From: Joshua Henderson Date: Sat, 9 Jan 2016 04:54:21 -0700 Subject: net: phy: Add support for SMSC LAN8740 PHY LAN8740 has a different phy_id than LAN8710/LAN8720. Signed-off-by: Joshua Henderson Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index ddb06135c21d..e485f2653c82 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -252,6 +252,27 @@ static struct phy_driver smsc_phy_driver[] = { .ack_interrupt = smsc_phy_ack_interrupt, .config_intr = smsc_phy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, +}, { + .phy_id = 0x0007c110, + .phy_id_mask = 0xfffffff0, + .name = "SMSC LAN8740", + + .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause + | SUPPORTED_Asym_Pause), + .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, + + /* basic functions */ + .config_aneg = genphy_config_aneg, + .read_status = lan87xx_read_status, + .config_init = smsc_phy_config_init, + .soft_reset = smsc_phy_reset, + + /* IRQ related */ + .ack_interrupt = smsc_phy_ack_interrupt, + .config_intr = smsc_phy_config_intr, + .suspend = genphy_suspend, .resume = genphy_resume, } }; @@ -268,6 +289,7 @@ static struct mdio_device_id __maybe_unused smsc_tbl[] = { { 0x0007c0c0, 0xfffffff0 }, { 0x0007c0d0, 0xfffffff0 }, { 0x0007c0f0, 0xfffffff0 }, + { 0x0007c110, 0xfffffff0 }, { } }; -- cgit v1.2.3-59-g8ed1b From a05876b30cae6af79a5f0ff2cf5b42248aa0528f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 10 Jan 2016 07:10:44 -0800 Subject: net: tc35815: Fix build error due to missed API change Commit 7f854420fbfe ("phy: Add API for {un}registering an mdio device to a bus") introduces an API to access mii_bus structures, but missed to update the tc35815 driver. This results in the following error message. drivers/net/ethernet/toshiba/tc35815.c: In function 'tc_mii_probe': drivers/net/ethernet/toshiba/tc35815.c:617:18: error: 'struct mii_bus' has no member named 'phy_map' drivers/net/ethernet/toshiba/tc35815.c:623:24: error: 'struct mii_bus' has no member named 'phy_map' Instead of looping over the list of phy addresses to find a phy chip, use phy_find_first(). While the intent of the original code was to return an error if more than one phy was specified, this code path was never executed because the loop aborted after finding the first phy. The original code is therefore semantically identical to phy_find_first(), thus it is simpler and more straightforward to use phy_find_first() directly. Fixes: 7f854420fbfe ("phy: Add API for {un}registering an mdio device to a bus") Cc: Andrew Lunn Signed-off-by: Guenter Roeck Acked-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/toshiba/tc35815.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index fed5e3dfbc8f..71efe0092bec 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -608,23 +608,10 @@ static void tc_handle_link_change(struct net_device *dev) static int tc_mii_probe(struct net_device *dev) { struct tc35815_local *lp = netdev_priv(dev); - struct phy_device *phydev = NULL; - int phy_addr; + struct phy_device *phydev; u32 dropmask; - /* find the first phy */ - for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { - if (lp->mii_bus->phy_map[phy_addr]) { - if (phydev) { - printk(KERN_ERR "%s: multiple PHYs found\n", - dev->name); - return -EINVAL; - } - phydev = lp->mii_bus->phy_map[phy_addr]; - break; - } - } - + phydev = phy_find_first(lp->mii_bus); if (!phydev) { printk(KERN_ERR "%s: no PHY found\n", dev->name); return -ENODEV; -- cgit v1.2.3-59-g8ed1b From e253e8fbab01059c25c6d091e4888c68a26691f5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 10 Jan 2016 07:10:45 -0800 Subject: net: tc35815: Drop unused variable Commit e7f4dc3536a4 ("mdio: Move allocation of interrupts into core") removes some code from tc_mii_init(), but does not remove a now unused variable. This results in the following build warning. drivers/net/ethernet/toshiba/tc35815.c: In function 'tc_mii_init': drivers/net/ethernet/toshiba/tc35815.c:670:6: warning: unused variable 'i' Fixes: e7f4dc3536a4 ("mdio: Move allocation of interrupts into core") Cc: Andrew Lunn Acked-by: Andrew Lunn Signed-off-by: Guenter Roeck Signed-off-by: David S. Miller --- drivers/net/ethernet/toshiba/tc35815.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index 71efe0092bec..54874783476a 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -654,7 +654,6 @@ static int tc_mii_init(struct net_device *dev) { struct tc35815_local *lp = netdev_priv(dev); int err; - int i; lp->mii_bus = mdiobus_alloc(); if (lp->mii_bus == NULL) { -- cgit v1.2.3-59-g8ed1b From 3c6396d6309b446fe28c8c6b49fbb4fa22754650 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 9 Jan 2016 13:19:39 -0800 Subject: net: ti: cpmac: Fix build error due to missed API change Commit 7f854420fbfe ("phy: Add API for {un}registering an mdio device to a bus") introduces an API to access mii_bus structures, but missed to update the TI cpamc driver. This results in the following error message. drivers/net/ethernet/ti/cpmac.c: In function 'cpmac_probe': drivers/net/ethernet/ti/cpmac.c:1119:18: error: 'struct mii_bus' has no member named 'phy_map' Fixes: 7f854420fbfe ("phy: Add API for {un}registering an mdio device to a bus") Cc: Andrew Lunn Signed-off-by: Guenter Roeck Acked-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c index 0b483301767d..7eef45e6d70a 100644 --- a/drivers/net/ethernet/ti/cpmac.c +++ b/drivers/net/ethernet/ti/cpmac.c @@ -1116,7 +1116,7 @@ static int cpmac_probe(struct platform_device *pdev) for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) { if (!(pdata->phy_mask & (1 << phy_id))) continue; - if (!cpmac_mii->phy_map[phy_id]) + if (!mdiobus_get_phy(cpmac_mii, phy_id)) continue; strncpy(mdio_bus_id, cpmac_mii->id, MII_BUS_ID_SIZE); break; -- cgit v1.2.3-59-g8ed1b From 713d40248bde50fba40c5bf1b5dde6d5df02cfd0 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 10 Jan 2016 20:43:49 -0800 Subject: net: bfin_mac: Use phy_find_first() instead of open-coding it Use phy_find_first() to find the first phy device instead of open-coding it. Cc: Andrew Lunn Signed-off-by: Guenter Roeck Signed-off-by: David S. Miller --- drivers/net/ethernet/adi/bfin_mac.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index 5ddfd0ed61d4..74139cb7f849 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -380,9 +380,8 @@ static void bfin_mac_adjust_link(struct net_device *dev) static int mii_probe(struct net_device *dev, int phy_mode) { struct bfin_mac_local *lp = netdev_priv(dev); - struct phy_device *phydev = NULL; + struct phy_device *phydev; unsigned short sysctl; - int i; u32 sclk, mdc_div; /* Enable PHY output early */ @@ -396,19 +395,7 @@ static int mii_probe(struct net_device *dev, int phy_mode) sysctl = (sysctl & ~MDCDIV) | SET_MDCDIV(mdc_div); bfin_write_EMAC_SYSCTL(sysctl); - /* search for connected PHY device */ - for (i = 0; i < PHY_MAX_ADDR; ++i) { - struct phy_device *const tmp_phydev = - mdiobus_get_phy(lp->mii_bus, i); - - if (!tmp_phydev) - continue; /* no PHY here... */ - - phydev = tmp_phydev; - break; /* found it */ - } - - /* now we are supposed to have a proper phydev, to attach to... */ + phydev = phy_find_first(lp->mii_bus); if (!phydev) { netdev_err(dev, "no phy device found\n"); return -ENODEV; -- cgit v1.2.3-59-g8ed1b From 366ce60315292a579b8ceae2777102e1954a2024 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sun, 10 Jan 2016 09:32:16 +0100 Subject: mlxsw: spectrum: Add FDB lock to prevent session interleaving Dumping the FDB (invoked with a process context) or handling FDB notifications (polled periodicly in delayed work) might each entail multiple EMAD transcations due to the number of entries. While we only allow one EMAD transaction at a time, there is nothing stopping the dump and notification processing sessions from interleaving. However, this is forbidden by the hardware, so we need to make sure only one of these sessions can run at a time. Solve this by adding a mutex ('fdb_lock'), as both kernel threads can sleep while waiting for the response EMAD. Fixes: 56ade8fe3f ("mlxsw: spectrum: Add initial support for Spectrum ASIC") Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 1 + drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 4365c8bccc6d..69281ca534b1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -63,6 +63,7 @@ struct mlxsw_sp { } fdb_notify; #define MLXSW_SP_DEFAULT_AGEING_TIME 300 u32 ageing_time; + struct mutex fdb_lock; /* Make sure FDB sessions are atomic. */ struct { struct net_device *dev; unsigned int ref_count; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 617fb22b5d81..80e266063aee 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -650,6 +650,7 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, if (!sfd_pl) return -ENOMEM; + mutex_lock(&mlxsw_sp_port->mlxsw_sp->fdb_lock); mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0); do { mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT); @@ -684,6 +685,7 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port, } while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT); out: + mutex_unlock(&mlxsw_sp_port->mlxsw_sp->fdb_lock); kfree(sfd_pl); return stored_err ? stored_err : err; } @@ -812,6 +814,7 @@ static void mlxsw_sp_fdb_notify_work(struct work_struct *work) mlxsw_sp = container_of(work, struct mlxsw_sp, fdb_notify.dw.work); + mutex_lock(&mlxsw_sp->fdb_lock); do { mlxsw_reg_sfn_pack(sfn_pl); err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl); @@ -824,6 +827,7 @@ static void mlxsw_sp_fdb_notify_work(struct work_struct *work) mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i); } while (num_rec); + mutex_unlock(&mlxsw_sp->fdb_lock); kfree(sfn_pl); mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp); @@ -838,6 +842,7 @@ static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp) dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n"); return err; } + mutex_init(&mlxsw_sp->fdb_lock); INIT_DELAYED_WORK(&mlxsw_sp->fdb_notify.dw, mlxsw_sp_fdb_notify_work); mlxsw_sp->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL; mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp); -- cgit v1.2.3-59-g8ed1b From c8086f6d88f02aa8c87a71dca75fe5392d7679f7 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 8 Jan 2016 20:35:36 -0500 Subject: bonding: make mii_status sysfs node consistent The spew in /proc/net/bonding/bond0 uses netif_carrier_ok() to determine mii_status, while /sys/class/net/bond0/bonding/mii_status looks at curr_active_slave, which doesn't actually seem to be set sometimes when the bond actually is up. A mode 4 bond configured via ifcfg-foo files on a Red Hat Enterprise Linux system, after boot, comes up clean and functional, but the sysfs node shows mii_status of down, while proc shows up. A simple enough fix here seems to be to use the same method for determining up or down in both places, and I'd opt for the one that seems to match reality. CC: Jay Vosburgh CC: Veaceslav Falico CC: Andy Gospodarek CC: netdev@vger.kernel.org Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/bonding/bond_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 313dbac207ee..e23c3ed737de 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -480,7 +480,7 @@ static ssize_t bonding_show_mii_status(struct device *d, char *buf) { struct bonding *bond = to_bond(d); - bool active = !!rcu_access_pointer(bond->curr_active_slave); + bool active = netif_carrier_ok(bond->dev); return sprintf(buf, "%s\n", active ? "up" : "down"); } -- cgit v1.2.3-59-g8ed1b From 1c1fa821199c707a193efd3bc93c4ac9357bafa0 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 11 Jan 2016 00:27:38 +0300 Subject: ravb: stop reading ECMR in ravb_emac_init() The code in ravb_emac_init() twiddling the ECMR bits always looked a bit strange to me: if one intends to respect 'priv->duplex', why save old value of the ECMR.DM bit? As all the other bits are zeroed anyway, we don't really need to read ECMR before writing to it. Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/ravb_main.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 467d41698fd5..fef4fa120d8a 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -338,16 +338,13 @@ error: static void ravb_emac_init(struct net_device *ndev) { struct ravb_private *priv = netdev_priv(ndev); - u32 ecmr; /* Receive frame limit set register */ ravb_write(ndev, ndev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, RFLR); /* PAUSE prohibition */ - ecmr = ravb_read(ndev, ECMR); - ecmr &= ECMR_DM; - ecmr |= ECMR_ZPF | (priv->duplex ? ECMR_DM : 0) | ECMR_TE | ECMR_RE; - ravb_write(ndev, ecmr, ECMR); + ravb_write(ndev, ECMR_ZPF | (priv->duplex ? ECMR_DM : 0) | + ECMR_TE | ECMR_RE, ECMR); ravb_set_rate(ndev); -- cgit v1.2.3-59-g8ed1b From bffa731f8f44e0444afc56875a10fd07e9a08c6c Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 11 Jan 2016 00:28:14 +0300 Subject: sh_eth: stop reading ECMR in sh_eth_dev_init() The code in sh_eth_dev_init() twiddling the ECMR bits always looked a bit strange to me: if one intends to respect 'mdp->duplex', why save old value of the ECMR.DM bit? As all the other bits are zeroed anyway, we don't really need to read ECMR before writing to it. Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 6a8fc0f341ff..9c1e290ca4ee 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1289,7 +1289,6 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start) { int ret = 0; struct sh_eth_private *mdp = netdev_priv(ndev); - u32 val; /* Soft Reset */ ret = sh_eth_reset(ndev); @@ -1342,10 +1341,8 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start) } /* PAUSE Prohibition */ - val = (sh_eth_read(ndev, ECMR) & ECMR_DM) | - ECMR_ZPF | (mdp->duplex ? ECMR_DM : 0) | ECMR_TE | ECMR_RE; - - sh_eth_write(ndev, val, ECMR); + sh_eth_write(ndev, ECMR_ZPF | (mdp->duplex ? ECMR_DM : 0) | + ECMR_TE | ECMR_RE, ECMR); if (mdp->cd->set_rate) mdp->cd->set_rate(ndev); -- cgit v1.2.3-59-g8ed1b From f0d22d1874730530a2ac304fd0888cb8a6864527 Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Mon, 11 Jan 2016 10:25:57 +0200 Subject: net/mlx5_core: Introduce flow steering autogrouped flow table When user add rule to autogrouped flow table, we search for flow group with the same match criteria, if we don't find such group then we create new flow group with the required match criteria and insert the rule to this group. We divide the flow table into required_groups + 1, in order to reserve a part of the flow table for rules which don't match any existing group. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 166 +++++++++++++++++++--- drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 5 + include/linux/mlx5/fs.h | 6 + 3 files changed, 158 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index f7d62fe595f6..7d24bbba58ba 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -85,6 +85,12 @@ static struct init_tree_node { } }; +enum fs_i_mutex_lock_class { + FS_MUTEX_GRANDPARENT, + FS_MUTEX_PARENT, + FS_MUTEX_CHILD +}; + static void del_rule(struct fs_node *node); static void del_flow_table(struct fs_node *node); static void del_flow_group(struct fs_node *node); @@ -119,10 +125,11 @@ static void tree_get_node(struct fs_node *node) atomic_inc(&node->refcount); } -static void nested_lock_ref_node(struct fs_node *node) +static void nested_lock_ref_node(struct fs_node *node, + enum fs_i_mutex_lock_class class) { if (node) { - mutex_lock_nested(&node->lock, SINGLE_DEPTH_NESTING); + mutex_lock_nested(&node->lock, class); atomic_inc(&node->refcount); } } @@ -481,9 +488,7 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, list_add_tail(&ft->node.list, &fs_prio->node.children); fs_prio->num_ft++; unlock_ref_node(&fs_prio->node); - return ft; - free_ft: kfree(ft); unlock_prio: @@ -491,8 +496,32 @@ unlock_prio: return ERR_PTR(err); } -struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft, - u32 *fg_in) +struct mlx5_flow_table *mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns, + int prio, + int num_flow_table_entries, + int max_num_groups) +{ + struct mlx5_flow_table *ft; + + if (max_num_groups > num_flow_table_entries) + return ERR_PTR(-EINVAL); + + ft = mlx5_create_flow_table(ns, prio, num_flow_table_entries); + if (IS_ERR(ft)) + return ft; + + ft->autogroup.active = true; + ft->autogroup.required_groups = max_num_groups; + + return ft; +} + +/* Flow table should be locked */ +static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table *ft, + u32 *fg_in, + struct list_head + *prev_fg, + bool is_auto_fg) { struct mlx5_flow_group *fg; struct mlx5_core_dev *dev = get_dev(&ft->node); @@ -505,18 +534,33 @@ struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft, if (IS_ERR(fg)) return fg; - lock_ref_node(&ft->node); err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id); if (err) { kfree(fg); - unlock_ref_node(&ft->node); return ERR_PTR(err); } + + if (ft->autogroup.active) + ft->autogroup.num_groups++; /* Add node to tree */ - tree_init_node(&fg->node, 1, del_flow_group); + tree_init_node(&fg->node, !is_auto_fg, del_flow_group); tree_add_node(&fg->node, &ft->node); /* Add node to group list */ list_add(&fg->node.list, ft->node.children.prev); + + return fg; +} + +struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft, + u32 *fg_in) +{ + struct mlx5_flow_group *fg; + + if (ft->autogroup.active) + return ERR_PTR(-EPERM); + + lock_ref_node(&ft->node); + fg = create_flow_group_common(ft, fg_in, &ft->node.children, false); unlock_ref_node(&ft->node); return fg; @@ -614,7 +658,63 @@ static struct fs_fte *create_fte(struct mlx5_flow_group *fg, return fte; } -/* Assuming parent fg(flow table) is locked */ +static struct mlx5_flow_group *create_autogroup(struct mlx5_flow_table *ft, + u8 match_criteria_enable, + u32 *match_criteria) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct list_head *prev = &ft->node.children; + unsigned int candidate_index = 0; + struct mlx5_flow_group *fg; + void *match_criteria_addr; + unsigned int group_size = 0; + u32 *in; + + if (!ft->autogroup.active) + return ERR_PTR(-ENOENT); + + in = mlx5_vzalloc(inlen); + if (!in) + return ERR_PTR(-ENOMEM); + + if (ft->autogroup.num_groups < ft->autogroup.required_groups) + /* We save place for flow groups in addition to max types */ + group_size = ft->max_fte / (ft->autogroup.required_groups + 1); + + /* ft->max_fte == ft->autogroup.max_types */ + if (group_size == 0) + group_size = 1; + + /* sorted by start_index */ + fs_for_each_fg(fg, ft) { + if (candidate_index + group_size > fg->start_index) + candidate_index = fg->start_index + fg->max_ftes; + else + break; + prev = &fg->node.list; + } + + if (candidate_index + group_size > ft->max_fte) { + fg = ERR_PTR(-ENOSPC); + goto out; + } + + MLX5_SET(create_flow_group_in, in, match_criteria_enable, + match_criteria_enable); + MLX5_SET(create_flow_group_in, in, start_flow_index, candidate_index); + MLX5_SET(create_flow_group_in, in, end_flow_index, candidate_index + + group_size - 1); + match_criteria_addr = MLX5_ADDR_OF(create_flow_group_in, + in, match_criteria); + memcpy(match_criteria_addr, match_criteria, + MLX5_ST_SZ_BYTES(fte_match_param)); + + fg = create_flow_group_common(ft, in, prev, true); +out: + kvfree(in); + return fg; +} + static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, u32 *match_value, u8 action, @@ -626,9 +726,9 @@ static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg, struct mlx5_flow_table *ft; struct list_head *prev; - lock_ref_node(&fg->node); + nested_lock_ref_node(&fg->node, FS_MUTEX_PARENT); fs_for_each_fte(fte, fg) { - nested_lock_ref_node(&fte->node); + nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD); if (compare_match_value(&fg->mask, match_value, &fte->val) && action == fte->action && flow_tag == fte->flow_tag) { rule = add_rule_fte(fte, fg, dest); @@ -669,6 +769,33 @@ unlock_fg: return rule; } +static struct mlx5_flow_rule *add_rule_to_auto_fg(struct mlx5_flow_table *ft, + u8 match_criteria_enable, + u32 *match_criteria, + u32 *match_value, + u8 action, + u32 flow_tag, + struct mlx5_flow_destination *dest) +{ + struct mlx5_flow_rule *rule; + struct mlx5_flow_group *g; + + g = create_autogroup(ft, match_criteria_enable, match_criteria); + if (IS_ERR(g)) + return (void *)g; + + rule = add_rule_fg(g, match_value, + action, flow_tag, dest); + if (IS_ERR(rule)) { + /* Remove assumes refcount > 0 and autogroup creates a group + * with a refcount = 0. + */ + tree_get_node(&g->node); + tree_remove_node(&g->node); + } + return rule; +} + struct mlx5_flow_rule * mlx5_add_flow_rule(struct mlx5_flow_table *ft, u8 match_criteria_enable, @@ -679,23 +806,24 @@ mlx5_add_flow_rule(struct mlx5_flow_table *ft, struct mlx5_flow_destination *dest) { struct mlx5_flow_group *g; - struct mlx5_flow_rule *rule = ERR_PTR(-EINVAL); + struct mlx5_flow_rule *rule; - tree_get_node(&ft->node); - lock_ref_node(&ft->node); + nested_lock_ref_node(&ft->node, FS_MUTEX_GRANDPARENT); fs_for_each_fg(g, ft) if (compare_match_criteria(g->mask.match_criteria_enable, match_criteria_enable, g->mask.match_criteria, match_criteria)) { - unlock_ref_node(&ft->node); rule = add_rule_fg(g, match_value, action, flow_tag, dest); - goto put; + if (!IS_ERR(rule) || PTR_ERR(rule) != -ENOSPC) + goto unlock; } + + rule = add_rule_to_auto_fg(ft, match_criteria_enable, match_criteria, + match_value, action, flow_tag, dest); +unlock: unlock_ref_node(&ft->node); -put: - tree_put_node(&ft->node); return rule; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 4ebb97fd5544..0f98257c0d31 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -77,6 +77,11 @@ struct mlx5_flow_table { unsigned int max_fte; unsigned int level; enum fs_flow_table_type type; + struct { + bool active; + unsigned int required_groups; + unsigned int num_groups; + } autogroup; }; /* Type of children is mlx5_flow_rule */ diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index bc7ad019afde..06ac6e8fccfa 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -61,6 +61,12 @@ struct mlx5_flow_namespace * mlx5_get_flow_namespace(struct mlx5_core_dev *dev, enum mlx5_flow_namespace_type type); +struct mlx5_flow_table * +mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns, + int prio, + int num_flow_table_entries, + int max_num_groups); + struct mlx5_flow_table * mlx5_create_flow_table(struct mlx5_flow_namespace *ns, int prio, -- cgit v1.2.3-59-g8ed1b From fdb6896fd97eb9d3efbd9f2e6ec1c0c5854bf542 Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Mon, 11 Jan 2016 10:25:58 +0200 Subject: net/mlx5_core: Add utilities to find next and prev flow-tables Add two utility functions for find next and prev flow table. Find next flow table function gets priority and return the first flow table of the next priority in the tree. Find prev flow table return the last flow table of the previous priority in the tree. These utility functions are used for chaining flow table from different priorities. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 67 +++++++++++++++++++++++ 1 file changed, 67 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 7d24bbba58ba..c5a96e6abe0d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -443,6 +443,73 @@ static struct mlx5_flow_table *alloc_flow_table(int level, int max_fte, return ft; } +/* If reverse is false, then we search for the first flow table in the + * root sub-tree from start(closest from right), else we search for the + * last flow table in the root sub-tree till start(closest from left). + */ +static struct mlx5_flow_table *find_closest_ft_recursive(struct fs_node *root, + struct list_head *start, + bool reverse) +{ +#define list_advance_entry(pos, reverse) \ + ((reverse) ? list_prev_entry(pos, list) : list_next_entry(pos, list)) + +#define list_for_each_advance_continue(pos, head, reverse) \ + for (pos = list_advance_entry(pos, reverse); \ + &pos->list != (head); \ + pos = list_advance_entry(pos, reverse)) + + struct fs_node *iter = list_entry(start, struct fs_node, list); + struct mlx5_flow_table *ft = NULL; + + if (!root) + return NULL; + + list_for_each_advance_continue(iter, &root->children, reverse) { + if (iter->type == FS_TYPE_FLOW_TABLE) { + fs_get_obj(ft, iter); + return ft; + } + ft = find_closest_ft_recursive(iter, &iter->children, reverse); + if (ft) + return ft; + } + + return ft; +} + +/* If reverse if false then return the first flow table in next priority of + * prio in the tree, else return the last flow table in the previous priority + * of prio in the tree. + */ +static struct mlx5_flow_table *find_closest_ft(struct fs_prio *prio, bool reverse) +{ + struct mlx5_flow_table *ft = NULL; + struct fs_node *curr_node; + struct fs_node *parent; + + parent = prio->node.parent; + curr_node = &prio->node; + while (!ft && parent) { + ft = find_closest_ft_recursive(parent, &curr_node->list, reverse); + curr_node = parent; + parent = curr_node->parent; + } + return ft; +} + +/* Assuming all the tree is locked by mutex chain lock */ +static struct mlx5_flow_table *find_next_chained_ft(struct fs_prio *prio) +{ + return find_closest_ft(prio, false); +} + +/* Assuming all the tree is locked by mutex chain lock */ +static struct mlx5_flow_table *find_prev_chained_ft(struct fs_prio *prio) +{ + return find_closest_ft(prio, true); +} + struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, int prio, int max_fte) -- cgit v1.2.3-59-g8ed1b From 2cc43b494a6c30ec0e554ea91ce763c97069e8cc Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Mon, 11 Jan 2016 10:25:59 +0200 Subject: net/mlx5_core: Managing root flow table The root Flow Table for each Flow Table Type is defined, by default, as the Flow Table with level 0. In order not to use an empty flow tables and introduce new hops, but still preserve space for flow-tables that have a priority greater(lower number) than the current flow table, we introduce this new set root flow table command. This command tells the HW to start matching packets from the assigned root flow table. This command is used when we create new flow table with level lower than the current lowest flow table or it is the first flow table. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 18 +++++ drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h | 2 + drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 97 ++++++++++++++++++++--- drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 6 ++ include/linux/mlx5/mlx5_ifc.h | 31 +++++++- 5 files changed, 144 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 5096f4f336bd..d8b1195fba3d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -38,6 +38,24 @@ #include "fs_cmd.h" #include "mlx5_core.h" +int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft) +{ + u32 in[MLX5_ST_SZ_DW(set_flow_table_root_in)]; + u32 out[MLX5_ST_SZ_DW(set_flow_table_root_out)]; + + memset(in, 0, sizeof(in)); + + MLX5_SET(set_flow_table_root_in, in, opcode, + MLX5_CMD_OP_SET_FLOW_TABLE_ROOT); + MLX5_SET(set_flow_table_root_in, in, table_type, ft->type); + MLX5_SET(set_flow_table_root_in, in, table_id, ft->id); + + memset(out, 0, sizeof(out)); + return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, + sizeof(out)); +} + int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, enum fs_flow_table_type type, unsigned int level, unsigned int log_size, unsigned int *table_id) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index f39304ede186..70d18ec145c2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -62,4 +62,6 @@ int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft, unsigned int index); +int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft); #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index c5a96e6abe0d..64bdb54041d1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -510,6 +510,29 @@ static struct mlx5_flow_table *find_prev_chained_ft(struct fs_prio *prio) return find_closest_ft(prio, true); } +static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio + *prio) +{ + struct mlx5_flow_root_namespace *root = find_root(&prio->node); + int min_level = INT_MAX; + int err; + + if (root->root_ft) + min_level = root->root_ft->level; + + if (ft->level >= min_level) + return 0; + + err = mlx5_cmd_update_root_ft(root->dev, ft); + if (err) + mlx5_core_warn(root->dev, "Update root flow table of id=%u failed\n", + ft->id); + else + root->root_ft = ft; + + return err; +} + struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, int prio, int max_fte) @@ -526,14 +549,15 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, return ERR_PTR(-ENODEV); } + mutex_lock(&root->chain_lock); fs_prio = find_prio(ns, prio); - if (!fs_prio) - return ERR_PTR(-EINVAL); - - lock_ref_node(&fs_prio->node); + if (!fs_prio) { + err = -EINVAL; + goto unlock_root; + } if (fs_prio->num_ft == fs_prio->max_ft) { err = -ENOSPC; - goto unlock_prio; + goto unlock_root; } ft = alloc_flow_table(find_next_free_level(fs_prio), @@ -541,7 +565,7 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, root->table_type); if (!ft) { err = -ENOMEM; - goto unlock_prio; + goto unlock_root; } tree_init_node(&ft->node, 1, del_flow_table); @@ -551,15 +575,25 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, if (err) goto free_ft; + if (MLX5_CAP_FLOWTABLE(root->dev, + flow_table_properties_nic_receive.modify_root)) { + err = update_root_ft_create(ft, fs_prio); + if (err) + goto destroy_ft; + } + lock_ref_node(&fs_prio->node); tree_add_node(&ft->node, &fs_prio->node); list_add_tail(&ft->node.list, &fs_prio->node.children); fs_prio->num_ft++; unlock_ref_node(&fs_prio->node); + mutex_unlock(&root->chain_lock); return ft; +destroy_ft: + mlx5_cmd_destroy_flow_table(root->dev, ft); free_ft: kfree(ft); -unlock_prio: - unlock_ref_node(&fs_prio->node); +unlock_root: + mutex_unlock(&root->chain_lock); return ERR_PTR(err); } @@ -899,13 +933,57 @@ void mlx5_del_flow_rule(struct mlx5_flow_rule *rule) tree_remove_node(&rule->node); } +/* Assuming prio->node.children(flow tables) is sorted by level */ +static struct mlx5_flow_table *find_next_ft(struct mlx5_flow_table *ft) +{ + struct fs_prio *prio; + + fs_get_obj(prio, ft->node.parent); + + if (!list_is_last(&ft->node.list, &prio->node.children)) + return list_next_entry(ft, node.list); + return find_next_chained_ft(prio); +} + +static int update_root_ft_destroy(struct mlx5_flow_table *ft) +{ + struct mlx5_flow_root_namespace *root = find_root(&ft->node); + struct mlx5_flow_table *new_root_ft = NULL; + + if (root->root_ft != ft) + return 0; + + new_root_ft = find_next_ft(ft); + if (new_root_ft) { + int err = mlx5_cmd_update_root_ft(root->dev, new_root_ft); + + if (err) { + mlx5_core_warn(root->dev, "Update root flow table of id=%u failed\n", + ft->id); + return err; + } + root->root_ft = new_root_ft; + } + return 0; +} + int mlx5_destroy_flow_table(struct mlx5_flow_table *ft) { + struct mlx5_flow_root_namespace *root = find_root(&ft->node); + int err = 0; + + mutex_lock(&root->chain_lock); + err = update_root_ft_destroy(ft); + if (err) { + mutex_unlock(&root->chain_lock); + return err; + } if (tree_remove_node(&ft->node)) mlx5_core_warn(get_dev(&ft->node), "Flow table %d wasn't destroyed, refcount > 1\n", ft->id); + mutex_unlock(&root->chain_lock); - return 0; + return err; } void mlx5_destroy_flow_group(struct mlx5_flow_group *fg) @@ -1072,6 +1150,7 @@ static struct mlx5_flow_root_namespace *create_root_ns(struct mlx5_core_dev *dev ns = &root_ns->ns; fs_init_namespace(ns); + mutex_init(&root_ns->chain_lock); tree_init_node(&ns->node, 1, NULL); tree_add_node(&ns->node, NULL); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 0f98257c0d31..1a2e08bad529 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -129,6 +129,9 @@ struct mlx5_flow_root_namespace { struct mlx5_flow_namespace ns; enum fs_flow_table_type table_type; struct mlx5_core_dev *dev; + struct mlx5_flow_table *root_ft; + /* Should be held when chaining flow tables */ + struct mutex chain_lock; }; int mlx5_init_fs(struct mlx5_core_dev *dev); @@ -148,6 +151,9 @@ void mlx5_cleanup_fs(struct mlx5_core_dev *dev); #define fs_for_each_prio(pos, ns) \ fs_list_for_each_entry(pos, &(ns)->node.children) +#define fs_for_each_ft(pos, prio) \ + fs_list_for_each_entry(pos, &(prio)->node.children) + #define fs_for_each_fg(pos, ft) \ fs_list_for_each_entry(pos, &(ft)->node.children) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 1780a85a8797..323e713c44ba 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -185,6 +185,7 @@ enum { MLX5_CMD_OP_MODIFY_RQT = 0x917, MLX5_CMD_OP_DESTROY_RQT = 0x918, MLX5_CMD_OP_QUERY_RQT = 0x919, + MLX5_CMD_OP_SET_FLOW_TABLE_ROOT = 0x92f, MLX5_CMD_OP_CREATE_FLOW_TABLE = 0x930, MLX5_CMD_OP_DESTROY_FLOW_TABLE = 0x931, MLX5_CMD_OP_QUERY_FLOW_TABLE = 0x932, @@ -258,7 +259,8 @@ struct mlx5_ifc_flow_table_prop_layout_bits { u8 ft_support[0x1]; u8 reserved_0[0x2]; u8 flow_modify_en[0x1]; - u8 reserved_1[0x1c]; + u8 modify_root[0x1]; + u8 reserved_1[0x1b]; u8 reserved_2[0x2]; u8 log_max_ft_size[0x6]; @@ -6946,4 +6948,31 @@ union mlx5_ifc_uplink_pci_interface_document_bits { u8 reserved_0[0x20060]; }; +struct mlx5_ifc_set_flow_table_root_out_bits { + u8 status[0x8]; + u8 reserved_0[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_1[0x40]; +}; + +struct mlx5_ifc_set_flow_table_root_in_bits { + u8 opcode[0x10]; + u8 reserved_0[0x10]; + + u8 reserved_1[0x10]; + u8 op_mod[0x10]; + + u8 reserved_2[0x40]; + + u8 table_type[0x8]; + u8 reserved_3[0x18]; + + u8 reserved_4[0x8]; + u8 table_id[0x18]; + + u8 reserved_5[0x140]; +}; + #endif /* MLX5_IFC_H */ -- cgit v1.2.3-59-g8ed1b From 34a40e689393a6b13673ab395a9a4d063d249fe9 Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Mon, 11 Jan 2016 10:26:00 +0200 Subject: net/mlx5_core: Introduce modify flow table command Introduce the modify flow table command. This command is used when we want to change the next flow table of an existing flow table. The next flow table is defined as the table we search (in order to find a match), if we couldn't find a match in any of the flow table entries in the current flow table. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 27 ++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h | 4 ++ include/linux/mlx5/mlx5_ifc.h | 56 ++++++++++++++++++++++-- 3 files changed, 83 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index d8b1195fba3d..2b5562553f2d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -101,6 +101,33 @@ int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev, sizeof(out)); } +int mlx5_cmd_modify_flow_table(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + struct mlx5_flow_table *next_ft) +{ + u32 in[MLX5_ST_SZ_DW(modify_flow_table_in)]; + u32 out[MLX5_ST_SZ_DW(modify_flow_table_out)]; + + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + + MLX5_SET(modify_flow_table_in, in, opcode, + MLX5_CMD_OP_MODIFY_FLOW_TABLE); + MLX5_SET(modify_flow_table_in, in, table_type, ft->type); + MLX5_SET(modify_flow_table_in, in, table_id, ft->id); + MLX5_SET(modify_flow_table_in, in, modify_field_select, + MLX5_MODIFY_FLOW_TABLE_MISS_TABLE_ID); + if (next_ft) { + MLX5_SET(modify_flow_table_in, in, table_miss_mode, 1); + MLX5_SET(modify_flow_table_in, in, table_miss_id, next_ft->id); + } else { + MLX5_SET(modify_flow_table_in, in, table_miss_mode, 0); + } + + return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, + sizeof(out)); +} + int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft, u32 *in, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index 70d18ec145c2..1ae9b685c783 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -40,6 +40,10 @@ int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft); +int mlx5_cmd_modify_flow_table(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + struct mlx5_flow_table *next_ft); + int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft, u32 *in, unsigned int *group_id); diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 323e713c44ba..7f166955d4c9 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -194,7 +194,8 @@ enum { MLX5_CMD_OP_QUERY_FLOW_GROUP = 0x935, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY = 0x936, MLX5_CMD_OP_QUERY_FLOW_TABLE_ENTRY = 0x937, - MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY = 0x938 + MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY = 0x938, + MLX5_CMD_OP_MODIFY_FLOW_TABLE = 0x93c }; struct mlx5_ifc_flow_table_fields_supported_bits { @@ -260,7 +261,9 @@ struct mlx5_ifc_flow_table_prop_layout_bits { u8 reserved_0[0x2]; u8 flow_modify_en[0x1]; u8 modify_root[0x1]; - u8 reserved_1[0x1b]; + u8 identified_miss_table_mode[0x1]; + u8 flow_table_modify[0x1]; + u8 reserved_1[0x19]; u8 reserved_2[0x2]; u8 log_max_ft_size[0x6]; @@ -5669,12 +5672,16 @@ struct mlx5_ifc_create_flow_table_in_bits { u8 reserved_4[0x20]; - u8 reserved_5[0x8]; + u8 reserved_5[0x4]; + u8 table_miss_mode[0x4]; u8 level[0x8]; u8 reserved_6[0x8]; u8 log_size[0x8]; - u8 reserved_7[0x120]; + u8 reserved_7[0x8]; + u8 table_miss_id[0x18]; + + u8 reserved_8[0x100]; }; struct mlx5_ifc_create_flow_group_out_bits { @@ -6975,4 +6982,45 @@ struct mlx5_ifc_set_flow_table_root_in_bits { u8 reserved_5[0x140]; }; +enum { + MLX5_MODIFY_FLOW_TABLE_MISS_TABLE_ID = 0x1, +}; + +struct mlx5_ifc_modify_flow_table_out_bits { + u8 status[0x8]; + u8 reserved_0[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_1[0x40]; +}; + +struct mlx5_ifc_modify_flow_table_in_bits { + u8 opcode[0x10]; + u8 reserved_0[0x10]; + + u8 reserved_1[0x10]; + u8 op_mod[0x10]; + + u8 reserved_2[0x20]; + + u8 reserved_3[0x10]; + u8 modify_field_select[0x10]; + + u8 table_type[0x8]; + u8 reserved_4[0x18]; + + u8 reserved_5[0x8]; + u8 table_id[0x18]; + + u8 reserved_6[0x4]; + u8 table_miss_mode[0x4]; + u8 reserved_7[0x18]; + + u8 reserved_8[0x8]; + u8 table_miss_id[0x18]; + + u8 reserved_9[0x100]; +}; + #endif /* MLX5_IFC_H */ -- cgit v1.2.3-59-g8ed1b From f90edfd279f35a5c62003eeee107f03f3b1544bc Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Mon, 11 Jan 2016 10:26:01 +0200 Subject: net/mlx5_core: Connect flow tables Flow tables from different priorities should be chained together. When a packet arrives we search for a match in the by-pass flow tables (first we search for a match in priority 0 and if we don't find a match we move to the next priority). If we can't find a match in any of the bypass flow-tables, we continue searching in the flow-tables of the next priority, which are the kernel's flow tables. Setting the miss flow table in a new flow table to be the next one in the list is performed via create flow table API. If we want to change an existing flow table, for example in order to point from an existing flow table to the new next-in-list flow table, we use the modify flow table API. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 7 +- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h | 3 +- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 104 ++++++++++++++++++++-- 3 files changed, 104 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 2b5562553f2d..a9894d2e8e26 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -58,7 +58,8 @@ int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, enum fs_flow_table_type type, unsigned int level, - unsigned int log_size, unsigned int *table_id) + unsigned int log_size, struct mlx5_flow_table + *next_ft, unsigned int *table_id) { u32 out[MLX5_ST_SZ_DW(create_flow_table_out)]; u32 in[MLX5_ST_SZ_DW(create_flow_table_in)]; @@ -69,6 +70,10 @@ int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, MLX5_SET(create_flow_table_in, in, opcode, MLX5_CMD_OP_CREATE_FLOW_TABLE); + if (next_ft) { + MLX5_SET(create_flow_table_in, in, table_miss_mode, 1); + MLX5_SET(create_flow_table_in, in, table_miss_id, next_ft->id); + } MLX5_SET(create_flow_table_in, in, table_type, type); MLX5_SET(create_flow_table_in, in, level, level); MLX5_SET(create_flow_table_in, in, log_size, log_size); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index 1ae9b685c783..9814d4784803 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -35,7 +35,8 @@ int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, enum fs_flow_table_type type, unsigned int level, - unsigned int log_size, unsigned int *table_id); + unsigned int log_size, struct mlx5_flow_table + *next_ft, unsigned int *table_id); int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 64bdb54041d1..c6f864d1ad1a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -510,6 +510,48 @@ static struct mlx5_flow_table *find_prev_chained_ft(struct fs_prio *prio) return find_closest_ft(prio, true); } +static int connect_fts_in_prio(struct mlx5_core_dev *dev, + struct fs_prio *prio, + struct mlx5_flow_table *ft) +{ + struct mlx5_flow_table *iter; + int i = 0; + int err; + + fs_for_each_ft(iter, prio) { + i++; + err = mlx5_cmd_modify_flow_table(dev, + iter, + ft); + if (err) { + mlx5_core_warn(dev, "Failed to modify flow table %d\n", + iter->id); + /* The driver is out of sync with the FW */ + if (i > 1) + WARN_ON(true); + return err; + } + } + return 0; +} + +/* Connect flow tables from previous priority of prio to ft */ +static int connect_prev_fts(struct mlx5_core_dev *dev, + struct mlx5_flow_table *ft, + struct fs_prio *prio) +{ + struct mlx5_flow_table *prev_ft; + + prev_ft = find_prev_chained_ft(prio); + if (prev_ft) { + struct fs_prio *prev_prio; + + fs_get_obj(prev_prio, prev_ft->node.parent); + return connect_fts_in_prio(dev, prev_prio, ft); + } + return 0; +} + static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio *prio) { @@ -533,10 +575,30 @@ static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio return err; } +static int connect_flow_table(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft, + struct fs_prio *prio) +{ + int err = 0; + + /* Connect_prev_fts and update_root_ft_create are mutually exclusive */ + + if (list_empty(&prio->node.children)) { + err = connect_prev_fts(dev, ft, prio); + if (err) + return err; + } + + if (MLX5_CAP_FLOWTABLE(dev, + flow_table_properties_nic_receive.modify_root)) + err = update_root_ft_create(ft, prio); + return err; +} + struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, int prio, int max_fte) { + struct mlx5_flow_table *next_ft = NULL; struct mlx5_flow_table *ft; int err; int log_table_sz; @@ -570,17 +632,15 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, tree_init_node(&ft->node, 1, del_flow_table); log_table_sz = ilog2(ft->max_fte); + next_ft = find_next_chained_ft(fs_prio); err = mlx5_cmd_create_flow_table(root->dev, ft->type, ft->level, - log_table_sz, &ft->id); + log_table_sz, next_ft, &ft->id); if (err) goto free_ft; - if (MLX5_CAP_FLOWTABLE(root->dev, - flow_table_properties_nic_receive.modify_root)) { - err = update_root_ft_create(ft, fs_prio); - if (err) - goto destroy_ft; - } + err = connect_flow_table(root->dev, ft, fs_prio); + if (err) + goto destroy_ft; lock_ref_node(&fs_prio->node); tree_add_node(&ft->node, &fs_prio->node); list_add_tail(&ft->node.list, &fs_prio->node.children); @@ -967,13 +1027,41 @@ static int update_root_ft_destroy(struct mlx5_flow_table *ft) return 0; } +/* Connect flow table from previous priority to + * the next flow table. + */ +static int disconnect_flow_table(struct mlx5_flow_table *ft) +{ + struct mlx5_core_dev *dev = get_dev(&ft->node); + struct mlx5_flow_table *next_ft; + struct fs_prio *prio; + int err = 0; + + err = update_root_ft_destroy(ft); + if (err) + return err; + + fs_get_obj(prio, ft->node.parent); + if (!(list_first_entry(&prio->node.children, + struct mlx5_flow_table, + node.list) == ft)) + return 0; + + next_ft = find_next_chained_ft(prio); + err = connect_prev_fts(dev, next_ft, prio); + if (err) + mlx5_core_warn(dev, "Failed to disconnect flow table %d\n", + ft->id); + return err; +} + int mlx5_destroy_flow_table(struct mlx5_flow_table *ft) { struct mlx5_flow_root_namespace *root = find_root(&ft->node); int err = 0; mutex_lock(&root->chain_lock); - err = update_root_ft_destroy(ft); + err = disconnect_flow_table(ft); if (err) { mutex_unlock(&root->chain_lock); return err; -- cgit v1.2.3-59-g8ed1b From 655227edae4ca42e702db3b7ca490d098090e8fb Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Mon, 11 Jan 2016 10:26:02 +0200 Subject: net/mlx5_core: Set priority attributes Each priority has two attributes: 1. max_ft - maximum allowed flow tables under this priority. 2. start_level - start level range of the flow tables in the priority. These attributes are set by traversing the tree nodes by DFS and set start level and max flow tables to each priority. Start level depends on the max flow tables of the prior priorities in the tree. The leaves of the trees have max_ft set in them. Each node accumulates the max_ft of its children and set it accordingly. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 71 +++++++++++++++++------ drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 3 + 2 files changed, 55 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index c6f864d1ad1a..e1282e8f4ee1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -41,20 +41,19 @@ sizeof(struct init_tree_node)) #define INIT_PRIO(min_level_val, max_ft_val,\ - start_level_val, ...) {.type = FS_TYPE_PRIO,\ + ...) {.type = FS_TYPE_PRIO,\ .min_ft_level = min_level_val,\ - .start_level = start_level_val,\ .max_ft = max_ft_val,\ .children = (struct init_tree_node[]) {__VA_ARGS__},\ .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \ } -#define ADD_PRIO(min_level_val, max_ft_val, start_level_val, ...)\ - INIT_PRIO(min_level_val, max_ft_val, start_level_val,\ +#define ADD_PRIO(min_level_val, max_ft_val, ...)\ + INIT_PRIO(min_level_val, max_ft_val,\ __VA_ARGS__)\ -#define ADD_FT_PRIO(max_ft_val, start_level_val, ...)\ - INIT_PRIO(0, max_ft_val, start_level_val,\ +#define ADD_FT_PRIO(max_ft_val, ...)\ + INIT_PRIO(0, max_ft_val,\ __VA_ARGS__)\ #define ADD_NS(...) {.type = FS_TYPE_NAMESPACE,\ @@ -62,8 +61,6 @@ .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \ } -#define KERNEL_START_LEVEL 0 -#define KERNEL_P0_START_LEVEL KERNEL_START_LEVEL #define KERNEL_MAX_FT 2 #define KENREL_MIN_LEVEL 2 static struct init_tree_node { @@ -73,15 +70,12 @@ static struct init_tree_node { int min_ft_level; int prio; int max_ft; - int start_level; } root_fs = { .type = FS_TYPE_NAMESPACE, .ar_size = 1, .children = (struct init_tree_node[]) { - ADD_PRIO(KENREL_MIN_LEVEL, KERNEL_MAX_FT, - KERNEL_START_LEVEL, - ADD_NS(ADD_FT_PRIO(KERNEL_MAX_FT, - KERNEL_P0_START_LEVEL))), + ADD_PRIO(KENREL_MIN_LEVEL, 0, + ADD_NS(ADD_FT_PRIO(KERNEL_MAX_FT))), } }; @@ -1117,8 +1111,7 @@ struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev, } static struct fs_prio *fs_create_prio(struct mlx5_flow_namespace *ns, - unsigned prio, int max_ft, - int start_level) + unsigned prio, int max_ft) { struct fs_prio *fs_prio; @@ -1131,7 +1124,6 @@ static struct fs_prio *fs_create_prio(struct mlx5_flow_namespace *ns, tree_add_node(&fs_prio->node, &ns->node); fs_prio->max_ft = max_ft; fs_prio->prio = prio; - fs_prio->start_level = start_level; list_add_tail(&fs_prio->node.list, &ns->node.children); return fs_prio; @@ -1177,8 +1169,7 @@ static int init_root_tree_recursive(int max_ft_level, struct init_tree_node *ini return -ENOTSUPP; fs_get_obj(fs_ns, fs_parent_node); - fs_prio = fs_create_prio(fs_ns, index, init_node->max_ft, - init_node->start_level); + fs_prio = fs_create_prio(fs_ns, index, init_node->max_ft); if (IS_ERR(fs_prio)) return PTR_ERR(fs_prio); base = &fs_prio->node; @@ -1245,6 +1236,46 @@ static struct mlx5_flow_root_namespace *create_root_ns(struct mlx5_core_dev *dev return root_ns; } +static void set_prio_attrs_in_prio(struct fs_prio *prio, int acc_level); + +static int set_prio_attrs_in_ns(struct mlx5_flow_namespace *ns, int acc_level) +{ + struct fs_prio *prio; + + fs_for_each_prio(prio, ns) { + /* This updates prio start_level and max_ft */ + set_prio_attrs_in_prio(prio, acc_level); + acc_level += prio->max_ft; + } + return acc_level; +} + +static void set_prio_attrs_in_prio(struct fs_prio *prio, int acc_level) +{ + struct mlx5_flow_namespace *ns; + int acc_level_ns = acc_level; + + prio->start_level = acc_level; + fs_for_each_ns(ns, prio) + /* This updates start_level and max_ft of ns's priority descendants */ + acc_level_ns = set_prio_attrs_in_ns(ns, acc_level); + if (!prio->max_ft) + prio->max_ft = acc_level_ns - prio->start_level; + WARN_ON(prio->max_ft < acc_level_ns - prio->start_level); +} + +static void set_prio_attrs(struct mlx5_flow_root_namespace *root_ns) +{ + struct mlx5_flow_namespace *ns = &root_ns->ns; + struct fs_prio *prio; + int start_level = 0; + + fs_for_each_prio(prio, ns) { + set_prio_attrs_in_prio(prio, start_level); + start_level += prio->max_ft; + } +} + static int init_root_ns(struct mlx5_core_dev *dev) { int max_ft_level = MLX5_CAP_FLOWTABLE(dev, @@ -1258,6 +1289,8 @@ static int init_root_ns(struct mlx5_core_dev *dev) if (init_root_tree(max_ft_level, &root_fs, &dev->priv.root_ns->ns.node)) goto cleanup; + set_prio_attrs(dev->priv.root_ns); + return 0; cleanup: @@ -1381,7 +1414,7 @@ static int init_fdb_root_ns(struct mlx5_core_dev *dev) return -ENOMEM; /* Create single prio */ - prio = fs_create_prio(&dev->priv.fdb_root_ns->ns, 0, 1, 0); + prio = fs_create_prio(&dev->priv.fdb_root_ns->ns, 0, 1); if (IS_ERR(prio)) { cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns); return PTR_ERR(prio); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 1a2e08bad529..00245fd7e4bc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -151,6 +151,9 @@ void mlx5_cleanup_fs(struct mlx5_core_dev *dev); #define fs_for_each_prio(pos, ns) \ fs_list_for_each_entry(pos, &(ns)->node.children) +#define fs_for_each_ns(pos, prio) \ + fs_list_for_each_entry(pos, &(prio)->node.children) + #define fs_for_each_ft(pos, prio) \ fs_list_for_each_entry(pos, &(prio)->node.children) -- cgit v1.2.3-59-g8ed1b From 8d40d162c014dc3be316c5950b382d608aa2c8de Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Mon, 11 Jan 2016 10:26:03 +0200 Subject: net/mlx5_core: Initialize namespaces only when supported by device Before we create the sub tree of a steering namespaces(kernel, bypass, leftovers) we check that the device has the required capabilities in order to create this subtree. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 70 ++++++++++++++++------- 1 file changed, 49 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index e1282e8f4ee1..96e287a326ae 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -40,20 +40,17 @@ #define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\ sizeof(struct init_tree_node)) -#define INIT_PRIO(min_level_val, max_ft_val,\ - ...) {.type = FS_TYPE_PRIO,\ +#define ADD_PRIO(min_level_val, max_ft_val, caps_val,\ + ...) {.type = FS_TYPE_PRIO,\ .min_ft_level = min_level_val,\ .max_ft = max_ft_val,\ + .caps = caps_val,\ .children = (struct init_tree_node[]) {__VA_ARGS__},\ .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \ } -#define ADD_PRIO(min_level_val, max_ft_val, ...)\ - INIT_PRIO(min_level_val, max_ft_val,\ - __VA_ARGS__)\ - #define ADD_FT_PRIO(max_ft_val, ...)\ - INIT_PRIO(0, max_ft_val,\ + ADD_PRIO(0, max_ft_val, {},\ __VA_ARGS__)\ #define ADD_NS(...) {.type = FS_TYPE_NAMESPACE,\ @@ -61,12 +58,26 @@ .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \ } +#define INIT_CAPS_ARRAY_SIZE(...) (sizeof((long[]){__VA_ARGS__}) /\ + sizeof(long)) + +#define FS_CAP(cap) (__mlx5_bit_off(flow_table_nic_cap, cap)) + +#define FS_REQUIRED_CAPS(...) {.arr_sz = INIT_CAPS_ARRAY_SIZE(__VA_ARGS__), \ + .caps = (long[]) {__VA_ARGS__} } + #define KERNEL_MAX_FT 2 #define KENREL_MIN_LEVEL 2 + +struct node_caps { + size_t arr_sz; + long *caps; +}; static struct init_tree_node { enum fs_node_type type; struct init_tree_node *children; int ar_size; + struct node_caps caps; int min_ft_level; int prio; int max_ft; @@ -74,7 +85,7 @@ static struct init_tree_node { .type = FS_TYPE_NAMESPACE, .ar_size = 1, .children = (struct init_tree_node[]) { - ADD_PRIO(KENREL_MIN_LEVEL, 0, + ADD_PRIO(KENREL_MIN_LEVEL, 0, {}, ADD_NS(ADD_FT_PRIO(KERNEL_MAX_FT))), } }; @@ -1153,11 +1164,31 @@ static struct mlx5_flow_namespace *fs_create_namespace(struct fs_prio *prio) return ns; } -static int init_root_tree_recursive(int max_ft_level, struct init_tree_node *init_node, +#define FLOW_TABLE_BIT_SZ 1 +#define GET_FLOW_TABLE_CAP(dev, offset) \ + ((be32_to_cpu(*((__be32 *)(dev->hca_caps_cur[MLX5_CAP_FLOW_TABLE]) + \ + offset / 32)) >> \ + (32 - FLOW_TABLE_BIT_SZ - (offset & 0x1f))) & FLOW_TABLE_BIT_SZ) +static bool has_required_caps(struct mlx5_core_dev *dev, struct node_caps *caps) +{ + int i; + + for (i = 0; i < caps->arr_sz; i++) { + if (!GET_FLOW_TABLE_CAP(dev, caps->caps[i])) + return false; + } + return true; +} + +static int init_root_tree_recursive(struct mlx5_core_dev *dev, + struct init_tree_node *init_node, struct fs_node *fs_parent_node, struct init_tree_node *init_parent_node, int index) { + int max_ft_level = MLX5_CAP_FLOWTABLE(dev, + flow_table_properties_nic_receive. + max_ft_level); struct mlx5_flow_namespace *fs_ns; struct fs_prio *fs_prio; struct fs_node *base; @@ -1165,8 +1196,9 @@ static int init_root_tree_recursive(int max_ft_level, struct init_tree_node *ini int err; if (init_node->type == FS_TYPE_PRIO) { - if (init_node->min_ft_level > max_ft_level) - return -ENOTSUPP; + if ((init_node->min_ft_level > max_ft_level) || + !has_required_caps(dev, &init_node->caps)) + return 0; fs_get_obj(fs_ns, fs_parent_node); fs_prio = fs_create_prio(fs_ns, index, init_node->max_ft); @@ -1183,9 +1215,8 @@ static int init_root_tree_recursive(int max_ft_level, struct init_tree_node *ini return -EINVAL; } for (i = 0; i < init_node->ar_size; i++) { - err = init_root_tree_recursive(max_ft_level, - &init_node->children[i], base, - init_node, i); + err = init_root_tree_recursive(dev, &init_node->children[i], + base, init_node, i); if (err) return err; } @@ -1193,7 +1224,8 @@ static int init_root_tree_recursive(int max_ft_level, struct init_tree_node *ini return 0; } -static int init_root_tree(int max_ft_level, struct init_tree_node *init_node, +static int init_root_tree(struct mlx5_core_dev *dev, + struct init_tree_node *init_node, struct fs_node *fs_parent_node) { int i; @@ -1202,8 +1234,7 @@ static int init_root_tree(int max_ft_level, struct init_tree_node *init_node, fs_get_obj(fs_ns, fs_parent_node); for (i = 0; i < init_node->ar_size; i++) { - err = init_root_tree_recursive(max_ft_level, - &init_node->children[i], + err = init_root_tree_recursive(dev, &init_node->children[i], &fs_ns->node, init_node, i); if (err) @@ -1278,15 +1309,12 @@ static void set_prio_attrs(struct mlx5_flow_root_namespace *root_ns) static int init_root_ns(struct mlx5_core_dev *dev) { - int max_ft_level = MLX5_CAP_FLOWTABLE(dev, - flow_table_properties_nic_receive. - max_ft_level); dev->priv.root_ns = create_root_ns(dev, FS_FT_NIC_RX); if (IS_ERR_OR_NULL(dev->priv.root_ns)) goto cleanup; - if (init_root_tree(max_ft_level, &root_fs, &dev->priv.root_ns->ns.node)) + if (init_root_tree(dev, &root_fs, &dev->priv.root_ns->ns.node)) goto cleanup; set_prio_attrs(dev->priv.root_ns); -- cgit v1.2.3-59-g8ed1b From 4cbdd30ed5c8bc5cf40813b025b4fb57b376a592 Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Mon, 11 Jan 2016 10:26:04 +0200 Subject: net/mlx5_core: Enable flow steering support for the IB driver When the driver is loaded, we create flow steering namespace for kernel bypass with nine priorities and another namespace for leftovers(in order to catch packets that weren't matched). Verbs applications will use these priorities. we found nine as a number that balances the requirements from the user and retains performance. The bypass namespace is used by verbs applications that want to bypass the kernel networking stack. The leftovers namespace is used by verbs applications and the sniffer in order to catch packets that weren't handled by any preceding rules. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 55 +++++++++++++++++++---- include/linux/mlx5/device.h | 2 + include/linux/mlx5/fs.h | 2 + 3 files changed, 51 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 96e287a326ae..757725bf48a8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -40,18 +40,19 @@ #define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\ sizeof(struct init_tree_node)) -#define ADD_PRIO(min_level_val, max_ft_val, caps_val,\ +#define ADD_PRIO(num_prios_val, min_level_val, max_ft_val, caps_val,\ ...) {.type = FS_TYPE_PRIO,\ .min_ft_level = min_level_val,\ .max_ft = max_ft_val,\ + .num_leaf_prios = num_prios_val,\ .caps = caps_val,\ .children = (struct init_tree_node[]) {__VA_ARGS__},\ .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \ } -#define ADD_FT_PRIO(max_ft_val, ...)\ - ADD_PRIO(0, max_ft_val, {},\ - __VA_ARGS__)\ +#define ADD_MULTIPLE_PRIO(num_prios_val, max_ft_val, ...)\ + ADD_PRIO(num_prios_val, 0, max_ft_val, {},\ + __VA_ARGS__)\ #define ADD_NS(...) {.type = FS_TYPE_NAMESPACE,\ .children = (struct init_tree_node[]) {__VA_ARGS__},\ @@ -66,7 +67,14 @@ #define FS_REQUIRED_CAPS(...) {.arr_sz = INIT_CAPS_ARRAY_SIZE(__VA_ARGS__), \ .caps = (long[]) {__VA_ARGS__} } +#define LEFTOVERS_MAX_FT 1 +#define LEFTOVERS_NUM_PRIOS 1 +#define BY_PASS_PRIO_MAX_FT 1 +#define BY_PASS_MIN_LEVEL (KENREL_MIN_LEVEL + MLX5_BY_PASS_NUM_PRIOS +\ + LEFTOVERS_MAX_FT) + #define KERNEL_MAX_FT 2 +#define KERNEL_NUM_PRIOS 1 #define KENREL_MIN_LEVEL 2 struct node_caps { @@ -79,14 +87,27 @@ static struct init_tree_node { int ar_size; struct node_caps caps; int min_ft_level; + int num_leaf_prios; int prio; int max_ft; } root_fs = { .type = FS_TYPE_NAMESPACE, - .ar_size = 1, + .ar_size = 3, .children = (struct init_tree_node[]) { - ADD_PRIO(KENREL_MIN_LEVEL, 0, {}, - ADD_NS(ADD_FT_PRIO(KERNEL_MAX_FT))), + ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0, + FS_REQUIRED_CAPS(FS_CAP(flow_table_properties_nic_receive.flow_modify_en), + FS_CAP(flow_table_properties_nic_receive.modify_root), + FS_CAP(flow_table_properties_nic_receive.identified_miss_table_mode), + FS_CAP(flow_table_properties_nic_receive.flow_table_modify)), + ADD_NS(ADD_MULTIPLE_PRIO(MLX5_BY_PASS_NUM_PRIOS, BY_PASS_PRIO_MAX_FT))), + ADD_PRIO(0, KENREL_MIN_LEVEL, 0, {}, + ADD_NS(ADD_MULTIPLE_PRIO(KERNEL_NUM_PRIOS, KERNEL_MAX_FT))), + ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0, + FS_REQUIRED_CAPS(FS_CAP(flow_table_properties_nic_receive.flow_modify_en), + FS_CAP(flow_table_properties_nic_receive.modify_root), + FS_CAP(flow_table_properties_nic_receive.identified_miss_table_mode), + FS_CAP(flow_table_properties_nic_receive.flow_table_modify)), + ADD_NS(ADD_MULTIPLE_PRIO(LEFTOVERS_NUM_PRIOS, LEFTOVERS_MAX_FT))), } }; @@ -1098,8 +1119,10 @@ struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev, return NULL; switch (type) { + case MLX5_FLOW_NAMESPACE_BYPASS: case MLX5_FLOW_NAMESPACE_KERNEL: - prio = 0; + case MLX5_FLOW_NAMESPACE_LEFTOVERS: + prio = type; break; case MLX5_FLOW_NAMESPACE_FDB: if (dev->priv.fdb_root_ns) @@ -1164,6 +1187,20 @@ static struct mlx5_flow_namespace *fs_create_namespace(struct fs_prio *prio) return ns; } +static int create_leaf_prios(struct mlx5_flow_namespace *ns, struct init_tree_node + *prio_metadata) +{ + struct fs_prio *fs_prio; + int i; + + for (i = 0; i < prio_metadata->num_leaf_prios; i++) { + fs_prio = fs_create_prio(ns, i, prio_metadata->max_ft); + if (IS_ERR(fs_prio)) + return PTR_ERR(fs_prio); + } + return 0; +} + #define FLOW_TABLE_BIT_SZ 1 #define GET_FLOW_TABLE_CAP(dev, offset) \ ((be32_to_cpu(*((__be32 *)(dev->hca_caps_cur[MLX5_CAP_FLOW_TABLE]) + \ @@ -1201,6 +1238,8 @@ static int init_root_tree_recursive(struct mlx5_core_dev *dev, return 0; fs_get_obj(fs_ns, fs_parent_node); + if (init_node->num_leaf_prios) + return create_leaf_prios(fs_ns, init_node); fs_prio = fs_create_prio(fs_ns, index, init_node->max_ft); if (IS_ERR(fs_prio)) return PTR_ERR(fs_prio); diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index df2f79ef3cac..7be845e30689 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -1258,4 +1258,6 @@ static inline u16 mlx5_to_sw_pkey_sz(int pkey_sz) return MLX5_MIN_PKEY_TABLE_SIZE << pkey_sz; } +#define MLX5_BY_PASS_NUM_PRIOS 9 + #endif /* MLX5_DEVICE_H */ diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index 06ac6e8fccfa..a94341271e3f 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -39,7 +39,9 @@ #define MLX5_FS_DEFAULT_FLOW_TAG 0x0 enum mlx5_flow_namespace_type { + MLX5_FLOW_NAMESPACE_BYPASS, MLX5_FLOW_NAMESPACE_KERNEL, + MLX5_FLOW_NAMESPACE_LEFTOVERS, MLX5_FLOW_NAMESPACE_FDB, }; -- cgit v1.2.3-59-g8ed1b From b217ea25afd8910e7ec0abb556bcb312d397c8a6 Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Mon, 11 Jan 2016 10:26:06 +0200 Subject: net/mlx5_core: Export flow steering API Add exports to flow steering API for mlx5_ib usage. The following functions are exported: 1. mlx5_create_auto_grouped_flow_table - used to create flow table with auto flow grouping management (create and destroy flow groups). In auto-grouped flow tables, we create groups automatically if needed (if we don't find an existing flow group with same match criteria when we add new rule). 2. mlx5_destroy_flow_table - used to destroy a flow table. 3. mlx5_add_flow_rule - used to add flow rule into a flow table. 4. mlx5_del_flow_rule - used to delete flow rule from its flow table. 5. mlx5_get_flow_namespace - used to get a handle to the required namespace sub-tree. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 757725bf48a8..6f68dba8d7ed 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -702,6 +702,7 @@ struct mlx5_flow_table *mlx5_create_auto_grouped_flow_table(struct mlx5_flow_nam return ft; } +EXPORT_SYMBOL(mlx5_create_auto_grouped_flow_table); /* Flow table should be locked */ static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table *ft, @@ -1013,11 +1014,13 @@ unlock: unlock_ref_node(&ft->node); return rule; } +EXPORT_SYMBOL(mlx5_add_flow_rule); void mlx5_del_flow_rule(struct mlx5_flow_rule *rule) { tree_remove_node(&rule->node); } +EXPORT_SYMBOL(mlx5_del_flow_rule); /* Assuming prio->node.children(flow tables) is sorted by level */ static struct mlx5_flow_table *find_next_ft(struct mlx5_flow_table *ft) @@ -1099,6 +1102,7 @@ int mlx5_destroy_flow_table(struct mlx5_flow_table *ft) return err; } +EXPORT_SYMBOL(mlx5_destroy_flow_table); void mlx5_destroy_flow_group(struct mlx5_flow_group *fg) { @@ -1143,6 +1147,7 @@ struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev, return ns; } +EXPORT_SYMBOL(mlx5_get_flow_namespace); static struct fs_prio *fs_create_prio(struct mlx5_flow_namespace *ns, unsigned prio, int max_ft) -- cgit v1.2.3-59-g8ed1b From 038d2ef87572757861a177b19f9d489def2c48b8 Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Mon, 11 Jan 2016 10:26:07 +0200 Subject: IB/mlx5: Add flow steering support Adding flow steering support by creating a flow-table per priority (if rules exist in the priority). mlx5_ib uses autogrouping and thus only creates the required destinations. Also includes adding of these flow steering utilities 1. Parsing verbs flow attributes hardware steering specs. 2. Check if flow is multicast - this is required in order to decide to which flow table will we add the steering rule. 3. Set outer headers in flow match criteria to zeros. Signed-off-by: Maor Gottlieb Signed-off-by: Moni Shoua Signed-off-by: Matan Barak Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx5/main.c | 463 +++++++++++++++++++++++++++++++++++ drivers/infiniband/hw/mlx5/mlx5_ib.h | 45 +++- include/linux/mlx5/fs.h | 10 + 3 files changed, 517 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 7e97cb55a6bf..b0ec175cc6ba 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -43,6 +43,9 @@ #include #include #include +#include +#include +#include #include "user.h" #include "mlx5_ib.h" @@ -835,6 +838,457 @@ static int mlx5_ib_dealloc_pd(struct ib_pd *pd) return 0; } +static bool outer_header_zero(u32 *match_criteria) +{ + int size = MLX5_ST_SZ_BYTES(fte_match_param); + char *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_criteria, + outer_headers); + + return outer_headers_c[0] == 0 && !memcmp(outer_headers_c, + outer_headers_c + 1, + size - 1); +} + +static int parse_flow_attr(u32 *match_c, u32 *match_v, + union ib_flow_spec *ib_spec) +{ + void *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_c, + outer_headers); + void *outer_headers_v = MLX5_ADDR_OF(fte_match_param, match_v, + outer_headers); + switch (ib_spec->type) { + case IB_FLOW_SPEC_ETH: + if (ib_spec->size != sizeof(ib_spec->eth)) + return -EINVAL; + + ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c, + dmac_47_16), + ib_spec->eth.mask.dst_mac); + ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v, + dmac_47_16), + ib_spec->eth.val.dst_mac); + + if (ib_spec->eth.mask.vlan_tag) { + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, + vlan_tag, 1); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, + vlan_tag, 1); + + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, + first_vid, ntohs(ib_spec->eth.mask.vlan_tag)); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, + first_vid, ntohs(ib_spec->eth.val.vlan_tag)); + + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, + first_cfi, + ntohs(ib_spec->eth.mask.vlan_tag) >> 12); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, + first_cfi, + ntohs(ib_spec->eth.val.vlan_tag) >> 12); + + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, + first_prio, + ntohs(ib_spec->eth.mask.vlan_tag) >> 13); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, + first_prio, + ntohs(ib_spec->eth.val.vlan_tag) >> 13); + } + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, + ethertype, ntohs(ib_spec->eth.mask.ether_type)); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, + ethertype, ntohs(ib_spec->eth.val.ether_type)); + break; + case IB_FLOW_SPEC_IPV4: + if (ib_spec->size != sizeof(ib_spec->ipv4)) + return -EINVAL; + + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, + ethertype, 0xffff); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, + ethertype, ETH_P_IP); + + memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c, + src_ipv4_src_ipv6.ipv4_layout.ipv4), + &ib_spec->ipv4.mask.src_ip, + sizeof(ib_spec->ipv4.mask.src_ip)); + memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v, + src_ipv4_src_ipv6.ipv4_layout.ipv4), + &ib_spec->ipv4.val.src_ip, + sizeof(ib_spec->ipv4.val.src_ip)); + memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c, + dst_ipv4_dst_ipv6.ipv4_layout.ipv4), + &ib_spec->ipv4.mask.dst_ip, + sizeof(ib_spec->ipv4.mask.dst_ip)); + memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v, + dst_ipv4_dst_ipv6.ipv4_layout.ipv4), + &ib_spec->ipv4.val.dst_ip, + sizeof(ib_spec->ipv4.val.dst_ip)); + break; + case IB_FLOW_SPEC_TCP: + if (ib_spec->size != sizeof(ib_spec->tcp_udp)) + return -EINVAL; + + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol, + 0xff); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, ip_protocol, + IPPROTO_TCP); + + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, tcp_sport, + ntohs(ib_spec->tcp_udp.mask.src_port)); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, tcp_sport, + ntohs(ib_spec->tcp_udp.val.src_port)); + + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, tcp_dport, + ntohs(ib_spec->tcp_udp.mask.dst_port)); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, tcp_dport, + ntohs(ib_spec->tcp_udp.val.dst_port)); + break; + case IB_FLOW_SPEC_UDP: + if (ib_spec->size != sizeof(ib_spec->tcp_udp)) + return -EINVAL; + + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol, + 0xff); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, ip_protocol, + IPPROTO_UDP); + + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, udp_sport, + ntohs(ib_spec->tcp_udp.mask.src_port)); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, udp_sport, + ntohs(ib_spec->tcp_udp.val.src_port)); + + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, udp_dport, + ntohs(ib_spec->tcp_udp.mask.dst_port)); + MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, udp_dport, + ntohs(ib_spec->tcp_udp.val.dst_port)); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* If a flow could catch both multicast and unicast packets, + * it won't fall into the multicast flow steering table and this rule + * could steal other multicast packets. + */ +static bool flow_is_multicast_only(struct ib_flow_attr *ib_attr) +{ + struct ib_flow_spec_eth *eth_spec; + + if (ib_attr->type != IB_FLOW_ATTR_NORMAL || + ib_attr->size < sizeof(struct ib_flow_attr) + + sizeof(struct ib_flow_spec_eth) || + ib_attr->num_of_specs < 1) + return false; + + eth_spec = (struct ib_flow_spec_eth *)(ib_attr + 1); + if (eth_spec->type != IB_FLOW_SPEC_ETH || + eth_spec->size != sizeof(*eth_spec)) + return false; + + return is_multicast_ether_addr(eth_spec->mask.dst_mac) && + is_multicast_ether_addr(eth_spec->val.dst_mac); +} + +static bool is_valid_attr(struct ib_flow_attr *flow_attr) +{ + union ib_flow_spec *ib_spec = (union ib_flow_spec *)(flow_attr + 1); + bool has_ipv4_spec = false; + bool eth_type_ipv4 = true; + unsigned int spec_index; + + /* Validate that ethertype is correct */ + for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) { + if (ib_spec->type == IB_FLOW_SPEC_ETH && + ib_spec->eth.mask.ether_type) { + if (!((ib_spec->eth.mask.ether_type == htons(0xffff)) && + ib_spec->eth.val.ether_type == htons(ETH_P_IP))) + eth_type_ipv4 = false; + } else if (ib_spec->type == IB_FLOW_SPEC_IPV4) { + has_ipv4_spec = true; + } + ib_spec = (void *)ib_spec + ib_spec->size; + } + return !has_ipv4_spec || eth_type_ipv4; +} + +static void put_flow_table(struct mlx5_ib_dev *dev, + struct mlx5_ib_flow_prio *prio, bool ft_added) +{ + prio->refcount -= !!ft_added; + if (!prio->refcount) { + mlx5_destroy_flow_table(prio->flow_table); + prio->flow_table = NULL; + } +} + +static int mlx5_ib_destroy_flow(struct ib_flow *flow_id) +{ + struct mlx5_ib_dev *dev = to_mdev(flow_id->qp->device); + struct mlx5_ib_flow_handler *handler = container_of(flow_id, + struct mlx5_ib_flow_handler, + ibflow); + struct mlx5_ib_flow_handler *iter, *tmp; + + mutex_lock(&dev->flow_db.lock); + + list_for_each_entry_safe(iter, tmp, &handler->list, list) { + mlx5_del_flow_rule(iter->rule); + list_del(&iter->list); + kfree(iter); + } + + mlx5_del_flow_rule(handler->rule); + put_flow_table(dev, &dev->flow_db.prios[handler->prio], true); + mutex_unlock(&dev->flow_db.lock); + + kfree(handler); + + return 0; +} + +#define MLX5_FS_MAX_TYPES 10 +#define MLX5_FS_MAX_ENTRIES 32000UL +static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev, + struct ib_flow_attr *flow_attr) +{ + struct mlx5_flow_namespace *ns = NULL; + struct mlx5_ib_flow_prio *prio; + struct mlx5_flow_table *ft; + int num_entries; + int num_groups; + int priority; + int err = 0; + + if (flow_attr->type == IB_FLOW_ATTR_NORMAL) { + if (flow_is_multicast_only(flow_attr)) + priority = MLX5_IB_FLOW_MCAST_PRIO; + else + priority = flow_attr->priority; + ns = mlx5_get_flow_namespace(dev->mdev, + MLX5_FLOW_NAMESPACE_BYPASS); + num_entries = MLX5_FS_MAX_ENTRIES; + num_groups = MLX5_FS_MAX_TYPES; + prio = &dev->flow_db.prios[priority]; + } else if (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT || + flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT) { + ns = mlx5_get_flow_namespace(dev->mdev, + MLX5_FLOW_NAMESPACE_LEFTOVERS); + build_leftovers_ft_param(&priority, + &num_entries, + &num_groups); + prio = &dev->flow_db.prios[MLX5_IB_FLOW_LEFTOVERS_PRIO]; + } + + if (!ns) + return ERR_PTR(-ENOTSUPP); + + ft = prio->flow_table; + if (!ft) { + ft = mlx5_create_auto_grouped_flow_table(ns, priority, + num_entries, + num_groups); + + if (!IS_ERR(ft)) { + prio->refcount = 0; + prio->flow_table = ft; + } else { + err = PTR_ERR(ft); + } + } + + return err ? ERR_PTR(err) : prio; +} + +static struct mlx5_ib_flow_handler *create_flow_rule(struct mlx5_ib_dev *dev, + struct mlx5_ib_flow_prio *ft_prio, + struct ib_flow_attr *flow_attr, + struct mlx5_flow_destination *dst) +{ + struct mlx5_flow_table *ft = ft_prio->flow_table; + struct mlx5_ib_flow_handler *handler; + void *ib_flow = flow_attr + 1; + u8 match_criteria_enable = 0; + unsigned int spec_index; + u32 *match_c; + u32 *match_v; + int err = 0; + + if (!is_valid_attr(flow_attr)) + return ERR_PTR(-EINVAL); + + match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); + match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); + handler = kzalloc(sizeof(*handler), GFP_KERNEL); + if (!handler || !match_c || !match_v) { + err = -ENOMEM; + goto free; + } + + INIT_LIST_HEAD(&handler->list); + + for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) { + err = parse_flow_attr(match_c, match_v, ib_flow); + if (err < 0) + goto free; + + ib_flow += ((union ib_flow_spec *)ib_flow)->size; + } + + /* Outer header support only */ + match_criteria_enable = (!outer_header_zero(match_c)) << 0; + handler->rule = mlx5_add_flow_rule(ft, match_criteria_enable, + match_c, match_v, + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, + MLX5_FS_DEFAULT_FLOW_TAG, + dst); + + if (IS_ERR(handler->rule)) { + err = PTR_ERR(handler->rule); + goto free; + } + + handler->prio = ft_prio - dev->flow_db.prios; + + ft_prio->flow_table = ft; +free: + if (err) + kfree(handler); + kfree(match_c); + kfree(match_v); + return err ? ERR_PTR(err) : handler; +} + +enum { + LEFTOVERS_MC, + LEFTOVERS_UC, +}; + +static struct mlx5_ib_flow_handler *create_leftovers_rule(struct mlx5_ib_dev *dev, + struct mlx5_ib_flow_prio *ft_prio, + struct ib_flow_attr *flow_attr, + struct mlx5_flow_destination *dst) +{ + struct mlx5_ib_flow_handler *handler_ucast = NULL; + struct mlx5_ib_flow_handler *handler = NULL; + + static struct { + struct ib_flow_attr flow_attr; + struct ib_flow_spec_eth eth_flow; + } leftovers_specs[] = { + [LEFTOVERS_MC] = { + .flow_attr = { + .num_of_specs = 1, + .size = sizeof(leftovers_specs[0]) + }, + .eth_flow = { + .type = IB_FLOW_SPEC_ETH, + .size = sizeof(struct ib_flow_spec_eth), + .mask = {.dst_mac = {0x1} }, + .val = {.dst_mac = {0x1} } + } + }, + [LEFTOVERS_UC] = { + .flow_attr = { + .num_of_specs = 1, + .size = sizeof(leftovers_specs[0]) + }, + .eth_flow = { + .type = IB_FLOW_SPEC_ETH, + .size = sizeof(struct ib_flow_spec_eth), + .mask = {.dst_mac = {0x1} }, + .val = {.dst_mac = {} } + } + } + }; + + handler = create_flow_rule(dev, ft_prio, + &leftovers_specs[LEFTOVERS_MC].flow_attr, + dst); + if (!IS_ERR(handler) && + flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT) { + handler_ucast = create_flow_rule(dev, ft_prio, + &leftovers_specs[LEFTOVERS_UC].flow_attr, + dst); + if (IS_ERR(handler_ucast)) { + kfree(handler); + handler = handler_ucast; + } else { + list_add(&handler_ucast->list, &handler->list); + } + } + + return handler; +} + +static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp, + struct ib_flow_attr *flow_attr, + int domain) +{ + struct mlx5_ib_dev *dev = to_mdev(qp->device); + struct mlx5_ib_flow_handler *handler = NULL; + struct mlx5_flow_destination *dst = NULL; + struct mlx5_ib_flow_prio *ft_prio; + int err; + + if (flow_attr->priority > MLX5_IB_FLOW_LAST_PRIO) + return ERR_PTR(-ENOSPC); + + if (domain != IB_FLOW_DOMAIN_USER || + flow_attr->port > MLX5_CAP_GEN(dev->mdev, num_ports) || + flow_attr->flags) + return ERR_PTR(-EINVAL); + + dst = kzalloc(sizeof(*dst), GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + + mutex_lock(&dev->flow_db.lock); + + ft_prio = get_flow_table(dev, flow_attr); + if (IS_ERR(ft_prio)) { + err = PTR_ERR(ft_prio); + goto unlock; + } + + dst->type = MLX5_FLOW_DESTINATION_TYPE_TIR; + dst->tir_num = to_mqp(qp)->raw_packet_qp.rq.tirn; + + if (flow_attr->type == IB_FLOW_ATTR_NORMAL) { + handler = create_flow_rule(dev, ft_prio, flow_attr, + dst); + } else if (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT || + flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT) { + handler = create_leftovers_rule(dev, ft_prio, flow_attr, + dst); + } else { + err = -EINVAL; + goto destroy_ft; + } + + if (IS_ERR(handler)) { + err = PTR_ERR(handler); + handler = NULL; + goto destroy_ft; + } + + ft_prio->refcount++; + mutex_unlock(&dev->flow_db.lock); + kfree(dst); + + return &handler->ibflow; + +destroy_ft: + put_flow_table(dev, ft_prio, false); +unlock: + mutex_unlock(&dev->flow_db.lock); + kfree(dst); + kfree(handler); + return ERR_PTR(err); +} + static int mlx5_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) { struct mlx5_ib_dev *dev = to_mdev(ibqp->device); @@ -1439,10 +1893,19 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) (1ull << IB_USER_VERBS_CMD_CLOSE_XRCD); } + if (mlx5_ib_port_link_layer(&dev->ib_dev) == + IB_LINK_LAYER_ETHERNET) { + dev->ib_dev.create_flow = mlx5_ib_create_flow; + dev->ib_dev.destroy_flow = mlx5_ib_destroy_flow; + dev->ib_dev.uverbs_ex_cmd_mask |= + (1ull << IB_USER_VERBS_EX_CMD_CREATE_FLOW) | + (1ull << IB_USER_VERBS_EX_CMD_DESTROY_FLOW); + } err = init_node_data(dev); if (err) goto err_dealloc; + mutex_init(&dev->flow_db.lock); mutex_init(&dev->cap_mask_mutex); err = create_dev_resources(&dev->devr); diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 633347260b79..1474cccd1e0f 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -105,6 +105,36 @@ struct mlx5_ib_pd { u32 pdn; }; +#define MLX5_IB_FLOW_MCAST_PRIO (MLX5_BY_PASS_NUM_PRIOS - 1) +#define MLX5_IB_FLOW_LAST_PRIO (MLX5_IB_FLOW_MCAST_PRIO - 1) +#if (MLX5_IB_FLOW_LAST_PRIO <= 0) +#error "Invalid number of bypass priorities" +#endif +#define MLX5_IB_FLOW_LEFTOVERS_PRIO (MLX5_IB_FLOW_MCAST_PRIO + 1) + +#define MLX5_IB_NUM_FLOW_FT (MLX5_IB_FLOW_LEFTOVERS_PRIO + 1) +struct mlx5_ib_flow_prio { + struct mlx5_flow_table *flow_table; + unsigned int refcount; +}; + +struct mlx5_ib_flow_handler { + struct list_head list; + struct ib_flow ibflow; + unsigned int prio; + struct mlx5_flow_rule *rule; +}; + +struct mlx5_ib_flow_db { + struct mlx5_ib_flow_prio prios[MLX5_IB_NUM_FLOW_FT]; + /* Protect flow steering bypass flow tables + * when add/del flow rules. + * only single add/removal of flow steering rule could be done + * simultaneously. + */ + struct mutex lock; +}; + /* Use macros here so that don't have to duplicate * enum ib_send_flags and enum ib_qp_type for low-level driver */ @@ -171,9 +201,21 @@ struct mlx5_ib_pfault { struct mlx5_pagefault mpfault; }; +struct mlx5_ib_rq { + u32 tirn; +}; + +struct mlx5_ib_raw_packet_qp { + struct mlx5_ib_rq rq; +}; + struct mlx5_ib_qp { struct ib_qp ibqp; - struct mlx5_core_qp mqp; + union { + struct mlx5_core_qp mqp; + struct mlx5_ib_raw_packet_qp raw_packet_qp; + }; + struct mlx5_buf buf; struct mlx5_db db; @@ -431,6 +473,7 @@ struct mlx5_ib_dev { */ struct srcu_struct mr_srcu; #endif + struct mlx5_ib_flow_db flow_db; }; static inline struct mlx5_ib_cq *to_mibcq(struct mlx5_core_cq *mcq) diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index a94341271e3f..8230caa3fb6e 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -38,6 +38,16 @@ #define MLX5_FS_DEFAULT_FLOW_TAG 0x0 +#define LEFTOVERS_RULE_NUM 2 +static inline void build_leftovers_ft_param(int *priority, + int *n_ent, + int *n_grp) +{ + *priority = 0; /* Priority of leftovers_prio-0 */ + *n_ent = LEFTOVERS_RULE_NUM; + *n_grp = LEFTOVERS_RULE_NUM; +} + enum mlx5_flow_namespace_type { MLX5_FLOW_NAMESPACE_BYPASS, MLX5_FLOW_NAMESPACE_KERNEL, -- cgit v1.2.3-59-g8ed1b From 03d84a5f83a67e692af00a3d3901e7820e3e84d5 Mon Sep 17 00:00:00 2001 From: Karl Heiss Date: Mon, 11 Jan 2016 08:28:43 -0500 Subject: bonding: Prevent IPv6 link local address on enslaved devices Commit 1f718f0f4f97 ("bonding: populate neighbour's private on enslave") undoes the fix provided by commit c2edacf80e15 ("bonding / ipv6: no addrconf for slaves separately from master") by effectively setting the slave flag after the slave has been opened. If the slave comes up quickly enough, it will go through the IPv6 addrconf before the slave flag has been set and will get a link local IPv6 address. In order to ensure that addrconf knows to ignore the slave devices on state change, set IFF_SLAVE before dev_open() during bonding enslavement. Fixes: 1f718f0f4f97 ("bonding: populate neighbour's private on enslave") Signed-off-by: Karl Heiss Signed-off-by: Jay Vosburgh Reviewed-by: Jarod Wilson Signed-off-by: Andy Gospodarek Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 9e0f8a7ef8b1..f1692e418fe4 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1207,7 +1207,6 @@ static int bond_master_upper_dev_link(struct net_device *bond_dev, err = netdev_master_upper_dev_link_private(slave_dev, bond_dev, slave); if (err) return err; - slave_dev->flags |= IFF_SLAVE; rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL); return 0; } @@ -1465,6 +1464,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } } + /* set slave flag before open to prevent IPv6 addrconf */ + slave_dev->flags |= IFF_SLAVE; + /* open the slave since the application closed it */ res = dev_open(slave_dev); if (res) { @@ -1725,6 +1727,7 @@ err_close: dev_close(slave_dev); err_restore_mac: + slave_dev->flags &= ~IFF_SLAVE; if (!bond->params.fail_over_mac || BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { /* XXX TODO - fom follow mode needs to change master's -- cgit v1.2.3-59-g8ed1b From 5e431650f06a9d857fe1d61d92236fad678f7880 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Tue, 12 Jan 2016 03:24:30 +0100 Subject: net: freescale: ucc_geth: Fix build error from phy_device API change dev has moved inside the new mdio structure. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/ucc_geth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 0e7f24ec3239..cbddbe2d0429 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -1716,7 +1716,7 @@ static void uec_configure_serdes(struct net_device *dev) phy_write(tbiphy, ENET_TBI_MII_CR, TBICR_SETTINGS); - put_device(&tbiphy->dev); + put_device(&tbiphy->mdio.dev); } /* Configure the PHY for dev. -- cgit v1.2.3-59-g8ed1b From e47ad65b1bcc5ede7fa2ae01619047585348563e Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Tue, 12 Jan 2016 03:24:31 +0100 Subject: net: freescale: mac-fec: Fix build error from phy_device API change dev has moved inside the new mdio structure. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fs_enet/mac-fec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c index c158d409f6af..bade2f8f9b5c 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c @@ -363,7 +363,7 @@ static void stop(struct net_device *dev) const struct fs_platform_info *fpi = fep->fpi; struct fec __iomem *fecp = fep->fec.fecp; - struct fec_info* feci= fep->phydev->bus->priv; + struct fec_info *feci = fep->phydev->mdio.bus->priv; int i; -- cgit v1.2.3-59-g8ed1b From db9107b4972bbcb81ee6bc12f365dbe8684b137c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 Jan 2016 12:34:36 +0300 Subject: mdio_bus: NULL dereference on allocation error If bus = kzalloc() fails then we end up dereferencing bus when we do "bus->irq[i] = PHY_POLL;". The code is a little simpler if we reverse the NULL check and return directly on failure. Fixes: e7f4dc3536a4 ('mdio: Move allocation of interrupts into core') Signed-off-by: Dan Carpenter Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 0be7b3d65f0f..0cba64f1ecf4 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -102,11 +102,12 @@ struct mii_bus *mdiobus_alloc_size(size_t size) alloc_size = sizeof(*bus); bus = kzalloc(alloc_size, GFP_KERNEL); - if (bus) { - bus->state = MDIOBUS_ALLOCATED; - if (size) - bus->priv = (void *)bus + aligned_size; - } + if (!bus) + return NULL; + + bus->state = MDIOBUS_ALLOCATED; + if (size) + bus->priv = (void *)bus + aligned_size; /* Initialise the interrupts to polling */ for (i = 0; i < PHY_MAX_ADDR; i++) -- cgit v1.2.3-59-g8ed1b From 1bc16addc0e92222958010537557b18e25a278c0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 Jan 2016 12:35:34 +0300 Subject: mdio: remove an unneed condition It used to be that mdio->irq was a pointer but after e7f4dc3536a4 ('mdio: Move allocation of interrupts into core') it's an array inside the mdio struct so it can never be NULL. Reviewed-by: Andrew Lunn Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/of/of_mdio.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index c0a8f84d92db..86829f8064a6 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -62,11 +62,9 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi rc = irq_of_parse_and_map(child, 0); if (rc > 0) { phy->irq = rc; - if (mdio->irq) - mdio->irq[addr] = rc; + mdio->irq[addr] = rc; } else { - if (mdio->irq) - phy->irq = mdio->irq[addr]; + phy->irq = mdio->irq[addr]; } if (of_property_read_bool(child, "broken-turn-around")) -- cgit v1.2.3-59-g8ed1b From 47b356e499f26d330ab6ae4019e8c05f994be62d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 Jan 2016 12:36:21 +0300 Subject: phy: remove an unneeded condition It used to be that bus->irq was a pointer but after e7f4dc3536a4 ('mdio: Move allocation of interrupts into core') it's an array inside the mdio struct, so it can never be NULL. Let's remove the check. Reviewed-by: Andrew Lunn Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 319300627c0b..903737adfc01 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -340,7 +340,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, dev->phy_id = phy_id; if (c45_ids) dev->c45_ids = *c45_ids; - dev->irq = bus->irq ? bus->irq[addr] : PHY_POLL; + dev->irq = bus->irq[addr]; dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr); dev->state = PHY_DOWN; -- cgit v1.2.3-59-g8ed1b From d461873272169a3fc3a8d155d7b1c92e9d97b419 Mon Sep 17 00:00:00 2001 From: Lars Persson Date: Tue, 12 Jan 2016 15:28:13 +0100 Subject: dwc_eth_qos: Fix dma address for multi-fragment skbs The offset inside the fragment was not used for the dma address and silent data corruption resulted because TSO makes the checksum match. Fixes: 077742dac2c7 ("dwc_eth_qos: Add support for Synopsys DWC Ethernet QoS") Signed-off-by: Lars Persson Signed-off-by: David S. Miller --- drivers/net/ethernet/synopsys/dwc_eth_qos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/synopsys/dwc_eth_qos.c b/drivers/net/ethernet/synopsys/dwc_eth_qos.c index 6dbb681912f2..70814b7386b3 100644 --- a/drivers/net/ethernet/synopsys/dwc_eth_qos.c +++ b/drivers/net/ethernet/synopsys/dwc_eth_qos.c @@ -2087,7 +2087,7 @@ static int dwceqos_tx_frags(struct sk_buff *skb, struct net_local *lp, dd = &lp->tx_descs[lp->tx_next]; /* Set DMA Descriptor fields */ - dd->des0 = dma_handle; + dd->des0 = dma_handle + consumed_size; dd->des1 = 0; dd->des2 = dma_size; -- cgit v1.2.3-59-g8ed1b From 415b6f19e87e350b13585591859d4fdf50772229 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 12 Jan 2016 16:05:08 +0100 Subject: net: bnxt: always return values from _bnxt_get_max_rings Newly added code in the bnxt driver uses a couple of variables that are never initialized when CONFIG_BNXT_SRIOV is not set, and gcc correctly warns about that: In file included from include/linux/list.h:8:0, from include/linux/module.h:9, from drivers/net/ethernet/broadcom/bnxt/bnxt.c:10: drivers/net/ethernet/broadcom/bnxt/bnxt.c: In function 'bnxt_get_max_rings': include/linux/kernel.h:794:26: warning: 'cp' may be used uninitialized in this function [-Wmaybe-uninitialized] include/linux/kernel.h:794:26: warning: 'tx' may be used uninitialized in this function [-Wmaybe-uninitialized] drivers/net/ethernet/broadcom/bnxt/bnxt.c:5730:11: warning: 'rx' may be used uninitialized in this function [-Wmaybe-uninitialized] drivers/net/ethernet/broadcom/bnxt/bnxt.c:5736:6: note: 'rx' was declared here This changes the condition so that we fall back to using the PF data if VF is not available, and always initialize the variables to something useful. Signed-off-by: Arnd Bergmann Fixes: 6e6c5a57fbe1 ("bnxt_en: Modify bnxt_get_max_rings() to support shared or non shared rings.") Acked-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 287bfb79ea2d..df835f5e46d8 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5711,21 +5711,23 @@ static void _bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx, { int max_ring_grps = 0; - if (BNXT_PF(bp)) { - *max_tx = bp->pf.max_tx_rings; - *max_rx = bp->pf.max_rx_rings; - *max_cp = min_t(int, bp->pf.max_irqs, bp->pf.max_cp_rings); - *max_cp = min_t(int, *max_cp, bp->pf.max_stat_ctxs); - max_ring_grps = bp->pf.max_hw_ring_grps; - } else { #ifdef CONFIG_BNXT_SRIOV + if (!BNXT_PF(bp)) { *max_tx = bp->vf.max_tx_rings; *max_rx = bp->vf.max_rx_rings; *max_cp = min_t(int, bp->vf.max_irqs, bp->vf.max_cp_rings); *max_cp = min_t(int, *max_cp, bp->vf.max_stat_ctxs); max_ring_grps = bp->vf.max_hw_ring_grps; + } else #endif + { + *max_tx = bp->pf.max_tx_rings; + *max_rx = bp->pf.max_rx_rings; + *max_cp = min_t(int, bp->pf.max_irqs, bp->pf.max_cp_rings); + *max_cp = min_t(int, *max_cp, bp->pf.max_stat_ctxs); + max_ring_grps = bp->pf.max_hw_ring_grps; } + if (bp->flags & BNXT_FLAG_AGG_RINGS) *max_rx >>= 1; *max_rx = min_t(int, *max_rx, max_ring_grps); -- cgit v1.2.3-59-g8ed1b